ipsec: memory leak fixup
[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         {
276           clib_error_free (err);
277           return VNET_API_ERROR_SYSCALL_ERROR_1;
278         }
279     }
280
281   return 0;
282 }
283
284 u32
285 ipsec_get_sa_index_by_sa_id (u32 sa_id)
286 {
287   ipsec_main_t *im = &ipsec_main;
288   uword *p = hash_get (im->sa_index_by_sa_id, sa_id);
289   if (!p)
290     return ~0;
291
292   return p[0];
293 }
294
295 void
296 ipsec_sa_walk (ipsec_sa_walk_cb_t cb, void *ctx)
297 {
298   ipsec_main_t *im = &ipsec_main;
299   ipsec_sa_t *sa;
300
301   /* *INDENT-OFF* */
302   pool_foreach (sa, im->sad,
303   ({
304     if (WALK_CONTINUE != cb(sa, ctx))
305       break;
306   }));
307   /* *INDENT-ON* */
308 }
309
310 /**
311  * Function definition to get a FIB node from its index
312  */
313 static fib_node_t *
314 ipsec_sa_fib_node_get (fib_node_index_t index)
315 {
316   ipsec_main_t *im;
317   ipsec_sa_t *sa;
318
319   im = &ipsec_main;
320   sa = pool_elt_at_index (im->sad, index);
321
322   return (&sa->node);
323 }
324
325 /**
326  * Function definition to inform the FIB node that its last lock has gone.
327  */
328 static void
329 ipsec_sa_last_lock_gone (fib_node_t * node)
330 {
331   /*
332    * The ipsec SA is a root of the graph. As such
333    * it never has children and thus is never locked.
334    */
335   ASSERT (0);
336 }
337
338 static ipsec_sa_t *
339 ipsec_sa_from_fib_node (fib_node_t * node)
340 {
341   ASSERT (FIB_NODE_TYPE_IPSEC_SA == node->fn_type);
342   return ((ipsec_sa_t *) (((char *) node) -
343                           STRUCT_OFFSET_OF (ipsec_sa_t, node)));
344
345 }
346
347 /**
348  * Function definition to backwalk a FIB node
349  */
350 static fib_node_back_walk_rc_t
351 ipsec_sa_back_walk (fib_node_t * node, fib_node_back_walk_ctx_t * ctx)
352 {
353   ipsec_sa_stack (ipsec_sa_from_fib_node (node));
354
355   return (FIB_NODE_BACK_WALK_CONTINUE);
356 }
357
358 /*
359  * Virtual function table registered by MPLS GRE tunnels
360  * for participation in the FIB object graph.
361  */
362 const static fib_node_vft_t ipsec_sa_vft = {
363   .fnv_get = ipsec_sa_fib_node_get,
364   .fnv_last_lock = ipsec_sa_last_lock_gone,
365   .fnv_back_walk = ipsec_sa_back_walk,
366 };
367
368 /* force inclusion from application's main.c */
369 clib_error_t *
370 ipsec_sa_interface_init (vlib_main_t * vm)
371 {
372   fib_node_register_type (FIB_NODE_TYPE_IPSEC_SA, &ipsec_sa_vft);
373
374   return 0;
375 }
376
377 VLIB_INIT_FUNCTION (ipsec_sa_interface_init);
378
379 /*
380  * fd.io coding-style-patch-verification: ON
381  *
382  * Local Variables:
383  * eval: (c-set-style "gnu")
384  * End:
385  */