IPSEC: restack SAs on backend change
[vpp.git] / src / vnet / ipsec / ipsec_sa.c
1 /*
2  * Copyright (c) 2015 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <vnet/ipsec/ipsec.h>
17 #include <vnet/fib/fib_table.h>
18
19 static clib_error_t *
20 ipsec_call_add_del_callbacks (ipsec_main_t * im, ipsec_sa_t * sa,
21                               u32 sa_index, int is_add)
22 {
23   ipsec_ah_backend_t *ab;
24   ipsec_esp_backend_t *eb;
25   switch (sa->protocol)
26     {
27     case IPSEC_PROTOCOL_AH:
28       ab = pool_elt_at_index (im->ah_backends, im->ah_current_backend);
29       if (ab->add_del_sa_sess_cb)
30         return ab->add_del_sa_sess_cb (sa_index, is_add);
31       break;
32     case IPSEC_PROTOCOL_ESP:
33       eb = pool_elt_at_index (im->esp_backends, im->esp_current_backend);
34       if (eb->add_del_sa_sess_cb)
35         return eb->add_del_sa_sess_cb (sa_index, is_add);
36       break;
37     }
38   return 0;
39 }
40
41 void
42 ipsec_mk_key (ipsec_key_t * key, const u8 * data, u8 len)
43 {
44   memset (key, 0, sizeof (*key));
45
46   if (len > sizeof (key->data))
47     key->len = sizeof (key->data);
48   else
49     key->len = len;
50
51   memcpy (key->data, data, key->len);
52 }
53
54 /**
55  * 'stack' (resolve the recursion for) the SA tunnel destination
56  */
57 void
58 ipsec_sa_stack (ipsec_sa_t * sa)
59 {
60   ipsec_main_t *im = &ipsec_main;
61   fib_forward_chain_type_t fct;
62   dpo_id_t tmp = DPO_INVALID;
63
64   fct = fib_forw_chain_type_from_fib_proto ((sa->is_tunnel_ip6 ?
65                                              FIB_PROTOCOL_IP6 :
66                                              FIB_PROTOCOL_IP4));
67
68   fib_entry_contribute_forwarding (sa->fib_entry_index, fct, &tmp);
69
70   dpo_stack_from_node ((sa->is_tunnel_ip6 ?
71                         im->ah6_encrypt_node_index :
72                         im->ah4_encrypt_node_index),
73                        &sa->dpo[IPSEC_PROTOCOL_AH], &tmp);
74   dpo_stack_from_node ((sa->is_tunnel_ip6 ?
75                         im->esp6_encrypt_node_index :
76                         im->esp4_encrypt_node_index),
77                        &sa->dpo[IPSEC_PROTOCOL_ESP], &tmp);
78   dpo_reset (&tmp);
79 }
80
81 int
82 ipsec_sa_add (u32 id,
83               u32 spi,
84               ipsec_protocol_t proto,
85               ipsec_crypto_alg_t crypto_alg,
86               const ipsec_key_t * ck,
87               ipsec_integ_alg_t integ_alg,
88               const ipsec_key_t * ik,
89               ipsec_sa_flags_t flags,
90               u32 tx_table_id,
91               const ip46_address_t * tun_src,
92               const ip46_address_t * tun_dst, u32 * sa_out_index)
93 {
94   ipsec_main_t *im = &ipsec_main;
95   clib_error_t *err;
96   ipsec_sa_t *sa;
97   u32 sa_index;
98   uword *p;
99
100   p = hash_get (im->sa_index_by_sa_id, id);
101   if (p)
102     return VNET_API_ERROR_ENTRY_ALREADY_EXISTS;
103
104   pool_get_zero (im->sad, sa);
105
106   fib_node_init (&sa->node, FIB_NODE_TYPE_IPSEC_SA);
107   sa_index = sa - im->sad;
108
109   sa->id = id;
110   sa->spi = spi;
111   sa->protocol = proto;
112   sa->crypto_alg = crypto_alg;
113   clib_memcpy (&sa->crypto_key, ck, sizeof (sa->crypto_key));
114   sa->integ_alg = integ_alg;
115   clib_memcpy (&sa->integ_key, ik, sizeof (sa->integ_key));
116   ip46_address_copy (&sa->tunnel_src_addr, tun_src);
117   ip46_address_copy (&sa->tunnel_dst_addr, tun_dst);
118
119   if (flags & IPSEC_SA_FLAG_USE_EXTENDED_SEQ_NUM)
120     sa->use_esn = 1;
121   if (flags & IPSEC_SA_FLAG_USE_ANTI_REPLAY)
122     sa->use_anti_replay = 1;
123   if (flags & IPSEC_SA_FLAG_IS_TUNNEL)
124     sa->is_tunnel = 1;
125   if (flags & IPSEC_SA_FLAG_IS_TUNNEL_V6)
126     sa->is_tunnel_ip6 = 1;
127   if (flags & IPSEC_SA_FLAG_UDP_ENCAP)
128     sa->udp_encap = 1;
129
130   err = ipsec_check_support_cb (im, sa);
131   if (err)
132     {
133       clib_warning ("%s", err->what);
134       pool_put (im->sad, sa);
135       return VNET_API_ERROR_UNIMPLEMENTED;
136     }
137
138   err = ipsec_call_add_del_callbacks (im, sa, sa_index, 1);
139   if (err)
140     {
141       pool_put (im->sad, sa);
142       return VNET_API_ERROR_SYSCALL_ERROR_1;
143     }
144
145   if (sa->is_tunnel)
146     {
147       fib_protocol_t fproto = (sa->is_tunnel_ip6 ?
148                                FIB_PROTOCOL_IP6 : FIB_PROTOCOL_IP4);
149       fib_prefix_t pfx = {
150         .fp_addr = sa->tunnel_dst_addr,
151         .fp_len = (sa->is_tunnel_ip6 ? 128 : 32),
152         .fp_proto = fproto,
153       };
154       sa->tx_fib_index = fib_table_find (fproto, tx_table_id);
155       if (sa->tx_fib_index == ~((u32) 0))
156         {
157           pool_put (im->sad, sa);
158           return VNET_API_ERROR_NO_SUCH_FIB;
159         }
160
161       sa->fib_entry_index = fib_table_entry_special_add (sa->tx_fib_index,
162                                                          &pfx,
163                                                          FIB_SOURCE_RR,
164                                                          FIB_ENTRY_FLAG_NONE);
165       sa->sibling = fib_entry_child_add (sa->fib_entry_index,
166                                          FIB_NODE_TYPE_IPSEC_SA, sa_index);
167       ipsec_sa_stack (sa);
168     }
169   hash_set (im->sa_index_by_sa_id, sa->id, sa_index);
170
171   if (sa_out_index)
172     *sa_out_index = sa_index;
173
174   return (0);
175 }
176
177 u32
178 ipsec_sa_del (u32 id)
179 {
180   ipsec_main_t *im = &ipsec_main;
181   ipsec_sa_t *sa = 0;
182   uword *p;
183   u32 sa_index;
184   clib_error_t *err;
185
186   p = hash_get (im->sa_index_by_sa_id, id);
187
188   if (!p)
189     return VNET_API_ERROR_NO_SUCH_ENTRY;
190
191   sa_index = p[0];
192   sa = pool_elt_at_index (im->sad, sa_index);
193   if (ipsec_is_sa_used (sa_index))
194     {
195       clib_warning ("sa_id %u used in policy", sa->id);
196       /* sa used in policy */
197       return VNET_API_ERROR_SYSCALL_ERROR_1;
198     }
199   hash_unset (im->sa_index_by_sa_id, sa->id);
200   err = ipsec_call_add_del_callbacks (im, sa, sa_index, 0);
201   if (err)
202     return VNET_API_ERROR_SYSCALL_ERROR_1;
203   if (sa->is_tunnel)
204     {
205       fib_entry_child_remove (sa->fib_entry_index, sa->sibling);
206       fib_table_entry_special_remove
207         (sa->tx_fib_index,
208          fib_entry_get_prefix (sa->fib_entry_index), FIB_SOURCE_RR);
209       dpo_reset (&sa->dpo[IPSEC_PROTOCOL_AH]);
210       dpo_reset (&sa->dpo[IPSEC_PROTOCOL_ESP]);
211     }
212   pool_put (im->sad, sa);
213   return 0;
214 }
215
216 u8
217 ipsec_is_sa_used (u32 sa_index)
218 {
219   ipsec_main_t *im = &ipsec_main;
220   ipsec_tunnel_if_t *t;
221   ipsec_policy_t *p;
222
223   /* *INDENT-OFF* */
224   pool_foreach(p, im->policies, ({
225      if (p->policy == IPSEC_POLICY_ACTION_PROTECT)
226        {
227          if (p->sa_index == sa_index)
228            return 1;
229        }
230   }));
231
232   pool_foreach(t, im->tunnel_interfaces, ({
233     if (t->input_sa_index == sa_index)
234       return 1;
235     if (t->output_sa_index == sa_index)
236       return 1;
237   }));
238   /* *INDENT-ON* */
239
240   return 0;
241 }
242
243 int
244 ipsec_set_sa_key (u32 id, const ipsec_key_t * ck, const ipsec_key_t * ik)
245 {
246   ipsec_main_t *im = &ipsec_main;
247   uword *p;
248   u32 sa_index;
249   ipsec_sa_t *sa = 0;
250   clib_error_t *err;
251
252   p = hash_get (im->sa_index_by_sa_id, id);
253   if (!p)
254     return VNET_API_ERROR_SYSCALL_ERROR_1;      /* no such sa-id */
255
256   sa_index = p[0];
257   sa = pool_elt_at_index (im->sad, sa_index);
258
259   /* new crypto key */
260   if (ck)
261     {
262       clib_memcpy (&sa->crypto_key, ck, sizeof (sa->crypto_key));
263     }
264
265   /* new integ key */
266   if (ik)
267     {
268       clib_memcpy (&sa->integ_key, 0, sizeof (sa->integ_key));
269     }
270
271   if (ck || ik)
272     {
273       err = ipsec_call_add_del_callbacks (im, sa, sa_index, 0);
274       if (err)
275         return VNET_API_ERROR_SYSCALL_ERROR_1;
276     }
277
278   return 0;
279 }
280
281 u32
282 ipsec_get_sa_index_by_sa_id (u32 sa_id)
283 {
284   ipsec_main_t *im = &ipsec_main;
285   uword *p = hash_get (im->sa_index_by_sa_id, sa_id);
286   if (!p)
287     return ~0;
288
289   return p[0];
290 }
291
292 void
293 ipsec_sa_walk (ipsec_sa_walk_cb_t cb, void *ctx)
294 {
295   ipsec_main_t *im = &ipsec_main;
296   ipsec_sa_t *sa;
297
298   /* *INDENT-OFF* */
299   pool_foreach (sa, im->sad,
300   ({
301     if (WALK_CONTINUE != cb(sa, ctx))
302       break;
303   }));
304   /* *INDENT-ON* */
305 }
306
307 /**
308  * Function definition to get a FIB node from its index
309  */
310 static fib_node_t *
311 ipsec_sa_fib_node_get (fib_node_index_t index)
312 {
313   ipsec_main_t *im;
314   ipsec_sa_t *sa;
315
316   im = &ipsec_main;
317   sa = pool_elt_at_index (im->sad, index);
318
319   return (&sa->node);
320 }
321
322 /**
323  * Function definition to inform the FIB node that its last lock has gone.
324  */
325 static void
326 ipsec_sa_last_lock_gone (fib_node_t * node)
327 {
328   /*
329    * The ipsec SA is a root of the graph. As such
330    * it never has children and thus is never locked.
331    */
332   ASSERT (0);
333 }
334
335 static ipsec_sa_t *
336 ipsec_sa_from_fib_node (fib_node_t * node)
337 {
338   ASSERT (FIB_NODE_TYPE_IPSEC_SA == node->fn_type);
339   return ((ipsec_sa_t *) (((char *) node) -
340                           STRUCT_OFFSET_OF (ipsec_sa_t, node)));
341
342 }
343
344 /**
345  * Function definition to backwalk a FIB node
346  */
347 static fib_node_back_walk_rc_t
348 ipsec_sa_back_walk (fib_node_t * node, fib_node_back_walk_ctx_t * ctx)
349 {
350   ipsec_sa_stack (ipsec_sa_from_fib_node (node));
351
352   return (FIB_NODE_BACK_WALK_CONTINUE);
353 }
354
355 /*
356  * Virtual function table registered by MPLS GRE tunnels
357  * for participation in the FIB object graph.
358  */
359 const static fib_node_vft_t ipsec_sa_vft = {
360   .fnv_get = ipsec_sa_fib_node_get,
361   .fnv_last_lock = ipsec_sa_last_lock_gone,
362   .fnv_back_walk = ipsec_sa_back_walk,
363 };
364
365 /* force inclusion from application's main.c */
366 clib_error_t *
367 ipsec_sa_interface_init (vlib_main_t * vm)
368 {
369   fib_node_register_type (FIB_NODE_TYPE_IPSEC_SA, &ipsec_sa_vft);
370
371   return 0;
372 }
373
374 VLIB_INIT_FUNCTION (ipsec_sa_interface_init);
375
376 /*
377  * fd.io coding-style-patch-verification: ON
378  *
379  * Local Variables:
380  * eval: (c-set-style "gnu")
381  * End:
382  */