ipsec: manually binding an SA to a worker
[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 <sys/random.h>
17 #include <vnet/ipsec/ipsec.h>
18 #include <vnet/ipsec/esp.h>
19 #include <vnet/udp/udp_local.h>
20 #include <vnet/fib/fib_table.h>
21 #include <vnet/fib/fib_entry_track.h>
22 #include <vnet/ipsec/ipsec_tun.h>
23 #include <vnet/ipsec/ipsec.api_enum.h>
24
25 /**
26  * @brief
27  * SA packet & bytes counters
28  */
29 vlib_combined_counter_main_t ipsec_sa_counters = {
30   .name = "SA",
31   .stat_segment_name = "/net/ipsec/sa",
32 };
33 /* Per-SA error counters */
34 vlib_simple_counter_main_t ipsec_sa_err_counters[IPSEC_SA_N_ERRORS];
35
36 ipsec_sa_t *ipsec_sa_pool;
37
38 static clib_error_t *
39 ipsec_call_add_del_callbacks (ipsec_main_t * im, ipsec_sa_t * sa,
40                               u32 sa_index, int is_add)
41 {
42   ipsec_ah_backend_t *ab;
43   ipsec_esp_backend_t *eb;
44   switch (sa->protocol)
45     {
46     case IPSEC_PROTOCOL_AH:
47       ab = pool_elt_at_index (im->ah_backends, im->ah_current_backend);
48       if (ab->add_del_sa_sess_cb)
49         return ab->add_del_sa_sess_cb (sa_index, is_add);
50       break;
51     case IPSEC_PROTOCOL_ESP:
52       eb = pool_elt_at_index (im->esp_backends, im->esp_current_backend);
53       if (eb->add_del_sa_sess_cb)
54         return eb->add_del_sa_sess_cb (sa_index, is_add);
55       break;
56     }
57   return 0;
58 }
59
60 void
61 ipsec_mk_key (ipsec_key_t * key, const u8 * data, u8 len)
62 {
63   memset (key, 0, sizeof (*key));
64
65   if (len > sizeof (key->data))
66     key->len = sizeof (key->data);
67   else
68     key->len = len;
69
70   memcpy (key->data, data, key->len);
71 }
72
73 /**
74  * 'stack' (resolve the recursion for) the SA tunnel destination
75  */
76 static void
77 ipsec_sa_stack (ipsec_sa_t * sa)
78 {
79   ipsec_main_t *im = &ipsec_main;
80   dpo_id_t tmp = DPO_INVALID;
81
82   tunnel_contribute_forwarding (&sa->tunnel, &tmp);
83
84   if (IPSEC_PROTOCOL_AH == sa->protocol)
85     dpo_stack_from_node ((ipsec_sa_is_set_IS_TUNNEL_V6 (sa) ?
86                           im->ah6_encrypt_node_index :
87                           im->ah4_encrypt_node_index), &sa->dpo, &tmp);
88   else
89     dpo_stack_from_node ((ipsec_sa_is_set_IS_TUNNEL_V6 (sa) ?
90                           im->esp6_encrypt_node_index :
91                           im->esp4_encrypt_node_index), &sa->dpo, &tmp);
92   dpo_reset (&tmp);
93 }
94
95 void
96 ipsec_sa_set_async_mode (ipsec_sa_t *sa, int is_enabled)
97 {
98   if (is_enabled)
99     {
100       sa->crypto_key_index = sa->crypto_async_key_index;
101       sa->crypto_enc_op_id = sa->crypto_async_enc_op_id;
102       sa->crypto_dec_op_id = sa->crypto_async_dec_op_id;
103       sa->integ_key_index = ~0;
104       sa->integ_op_id = ~0;
105     }
106   else
107     {
108       sa->crypto_key_index = sa->crypto_sync_key_index;
109       sa->crypto_enc_op_id = sa->crypto_sync_enc_op_id;
110       sa->crypto_dec_op_id = sa->crypto_sync_dec_op_id;
111       sa->integ_key_index = sa->integ_sync_key_index;
112       sa->integ_op_id = sa->integ_sync_op_id;
113     }
114 }
115
116 void
117 ipsec_sa_set_crypto_alg (ipsec_sa_t * sa, ipsec_crypto_alg_t crypto_alg)
118 {
119   ipsec_main_t *im = &ipsec_main;
120   sa->crypto_alg = crypto_alg;
121   sa->crypto_iv_size = im->crypto_algs[crypto_alg].iv_size;
122   sa->esp_block_align = clib_max (4, im->crypto_algs[crypto_alg].block_align);
123   sa->crypto_sync_enc_op_id = im->crypto_algs[crypto_alg].enc_op_id;
124   sa->crypto_sync_dec_op_id = im->crypto_algs[crypto_alg].dec_op_id;
125   sa->crypto_calg = im->crypto_algs[crypto_alg].alg;
126   ASSERT (sa->crypto_iv_size <= ESP_MAX_IV_SIZE);
127   ASSERT (sa->esp_block_align <= ESP_MAX_BLOCK_SIZE);
128   if (IPSEC_CRYPTO_ALG_IS_GCM (crypto_alg) ||
129       IPSEC_CRYPTO_ALG_CTR_AEAD_OTHERS (crypto_alg))
130     {
131       sa->integ_icv_size = im->crypto_algs[crypto_alg].icv_size;
132       ipsec_sa_set_IS_CTR (sa);
133       ipsec_sa_set_IS_AEAD (sa);
134     }
135   else if (IPSEC_CRYPTO_ALG_IS_CTR (crypto_alg))
136     {
137       ipsec_sa_set_IS_CTR (sa);
138     }
139 }
140
141 void
142 ipsec_sa_set_integ_alg (ipsec_sa_t * sa, ipsec_integ_alg_t integ_alg)
143 {
144   ipsec_main_t *im = &ipsec_main;
145   sa->integ_alg = integ_alg;
146   sa->integ_icv_size = im->integ_algs[integ_alg].icv_size;
147   sa->integ_sync_op_id = im->integ_algs[integ_alg].op_id;
148   sa->integ_calg = im->integ_algs[integ_alg].alg;
149   ASSERT (sa->integ_icv_size <= ESP_MAX_ICV_SIZE);
150 }
151
152 void
153 ipsec_sa_set_async_op_ids (ipsec_sa_t * sa)
154 {
155   /* *INDENT-OFF* */
156   if (ipsec_sa_is_set_USE_ESN (sa))
157     {
158 #define _(n, s, k)                                                            \
159   if (sa->crypto_sync_enc_op_id == VNET_CRYPTO_OP_##n##_ENC)                  \
160     sa->crypto_async_enc_op_id = VNET_CRYPTO_OP_##n##_TAG16_AAD12_ENC;        \
161   if (sa->crypto_sync_dec_op_id == VNET_CRYPTO_OP_##n##_DEC)                  \
162     sa->crypto_async_dec_op_id = VNET_CRYPTO_OP_##n##_TAG16_AAD12_DEC;
163       foreach_crypto_aead_alg
164 #undef _
165     }
166   else
167     {
168 #define _(n, s, k)                                                            \
169   if (sa->crypto_sync_enc_op_id == VNET_CRYPTO_OP_##n##_ENC)                  \
170     sa->crypto_async_enc_op_id = VNET_CRYPTO_OP_##n##_TAG16_AAD8_ENC;         \
171   if (sa->crypto_sync_dec_op_id == VNET_CRYPTO_OP_##n##_DEC)                  \
172     sa->crypto_async_dec_op_id = VNET_CRYPTO_OP_##n##_TAG16_AAD8_DEC;
173       foreach_crypto_aead_alg
174 #undef _
175     }
176
177 #define _(c, h, s, k, d)                                                      \
178   if (sa->crypto_sync_enc_op_id == VNET_CRYPTO_OP_##c##_ENC &&                \
179       sa->integ_sync_op_id == VNET_CRYPTO_OP_##h##_HMAC)                      \
180     sa->crypto_async_enc_op_id = VNET_CRYPTO_OP_##c##_##h##_TAG##d##_ENC;     \
181   if (sa->crypto_sync_dec_op_id == VNET_CRYPTO_OP_##c##_DEC &&                \
182       sa->integ_sync_op_id == VNET_CRYPTO_OP_##h##_HMAC)                      \
183     sa->crypto_async_dec_op_id = VNET_CRYPTO_OP_##c##_##h##_TAG##d##_DEC;
184   foreach_crypto_link_async_alg
185 #undef _
186   /* *INDENT-ON* */
187 }
188
189 int
190 ipsec_sa_update (u32 id, u16 src_port, u16 dst_port, const tunnel_t *tun,
191                  bool is_tun)
192 {
193   ipsec_main_t *im = &ipsec_main;
194   ipsec_sa_t *sa;
195   u32 sa_index;
196   uword *p;
197   int rv;
198
199   p = hash_get (im->sa_index_by_sa_id, id);
200   if (!p)
201     return VNET_API_ERROR_NO_SUCH_ENTRY;
202
203   sa = ipsec_sa_get (p[0]);
204   sa_index = sa - ipsec_sa_pool;
205
206   if (is_tun && ipsec_sa_is_set_IS_TUNNEL (sa) &&
207       (ip_address_cmp (&tun->t_src, &sa->tunnel.t_src) != 0 ||
208        ip_address_cmp (&tun->t_dst, &sa->tunnel.t_dst) != 0))
209     {
210       /* if the source IP is updated for an inbound SA under a tunnel protect,
211        we need to update the tun_protect DB with the new src IP */
212       if (ipsec_sa_is_set_IS_INBOUND (sa) &&
213           ip_address_cmp (&tun->t_src, &sa->tunnel.t_src) != 0 &&
214           !ip46_address_is_zero (&tun->t_src.ip))
215         {
216           if (ip46_address_is_ip4 (&sa->tunnel.t_src.ip))
217             {
218               ipsec4_tunnel_kv_t old_key, new_key;
219               clib_bihash_kv_8_16_t res,
220                 *bkey = (clib_bihash_kv_8_16_t *) &old_key;
221
222               ipsec4_tunnel_mk_key (&old_key, &sa->tunnel.t_src.ip.ip4,
223                                     clib_host_to_net_u32 (sa->spi));
224               ipsec4_tunnel_mk_key (&new_key, &tun->t_src.ip.ip4,
225                                     clib_host_to_net_u32 (sa->spi));
226
227               if (!clib_bihash_search_8_16 (&im->tun4_protect_by_key, bkey,
228                                             &res))
229                 {
230                   clib_bihash_add_del_8_16 (&im->tun4_protect_by_key, &res, 0);
231                   res.key = new_key.key;
232                   clib_bihash_add_del_8_16 (&im->tun4_protect_by_key, &res, 1);
233                 }
234             }
235           else
236             {
237               ipsec6_tunnel_kv_t old_key = {
238           .key = {
239             .remote_ip =  sa->tunnel.t_src.ip.ip6,
240             .spi = clib_host_to_net_u32 (sa->spi),
241           },
242         }, new_key = {
243           .key = {
244             .remote_ip = tun->t_src.ip.ip6,
245             .spi = clib_host_to_net_u32 (sa->spi),
246           }};
247               clib_bihash_kv_24_16_t res,
248                 *bkey = (clib_bihash_kv_24_16_t *) &old_key;
249
250               if (!clib_bihash_search_24_16 (&im->tun6_protect_by_key, bkey,
251                                              &res))
252                 {
253                   clib_bihash_add_del_24_16 (&im->tun6_protect_by_key, &res,
254                                              0);
255                   clib_memcpy (&res.key, &new_key.key, 3);
256                   clib_bihash_add_del_24_16 (&im->tun6_protect_by_key, &res,
257                                              1);
258                 }
259             }
260         }
261       tunnel_unresolve (&sa->tunnel);
262       tunnel_copy (tun, &sa->tunnel);
263       if (!ipsec_sa_is_set_IS_INBOUND (sa))
264         {
265           dpo_reset (&sa->dpo);
266
267           sa->tunnel_flags = sa->tunnel.t_encap_decap_flags;
268
269           rv = tunnel_resolve (&sa->tunnel, FIB_NODE_TYPE_IPSEC_SA, sa_index);
270
271           if (rv)
272             {
273               hash_unset (im->sa_index_by_sa_id, sa->id);
274               pool_put (ipsec_sa_pool, sa);
275               return rv;
276             }
277           ipsec_sa_stack (sa);
278           /* generate header templates */
279           if (ipsec_sa_is_set_IS_TUNNEL_V6 (sa))
280             {
281               tunnel_build_v6_hdr (&sa->tunnel,
282                                    (ipsec_sa_is_set_UDP_ENCAP (sa) ?
283                                             IP_PROTOCOL_UDP :
284                                             IP_PROTOCOL_IPSEC_ESP),
285                                    &sa->ip6_hdr);
286             }
287           else
288             {
289               tunnel_build_v4_hdr (&sa->tunnel,
290                                    (ipsec_sa_is_set_UDP_ENCAP (sa) ?
291                                             IP_PROTOCOL_UDP :
292                                             IP_PROTOCOL_IPSEC_ESP),
293                                    &sa->ip4_hdr);
294             }
295         }
296     }
297
298   if (ipsec_sa_is_set_UDP_ENCAP (sa))
299     {
300       if (dst_port != IPSEC_UDP_PORT_NONE &&
301           dst_port != clib_net_to_host_u16 (sa->udp_hdr.dst_port))
302         {
303           if (ipsec_sa_is_set_IS_INBOUND (sa))
304             {
305               ipsec_unregister_udp_port (
306                 clib_net_to_host_u16 (sa->udp_hdr.dst_port),
307                 !ipsec_sa_is_set_IS_TUNNEL_V6 (sa));
308               ipsec_register_udp_port (dst_port,
309                                        !ipsec_sa_is_set_IS_TUNNEL_V6 (sa));
310             }
311           sa->udp_hdr.dst_port = clib_host_to_net_u16 (dst_port);
312         }
313       if (src_port != IPSEC_UDP_PORT_NONE &&
314           src_port != clib_net_to_host_u16 (sa->udp_hdr.src_port))
315         sa->udp_hdr.src_port = clib_host_to_net_u16 (src_port);
316     }
317   return (0);
318 }
319
320 int
321 ipsec_sa_add_and_lock (u32 id, u32 spi, ipsec_protocol_t proto,
322                        ipsec_crypto_alg_t crypto_alg, const ipsec_key_t *ck,
323                        ipsec_integ_alg_t integ_alg, const ipsec_key_t *ik,
324                        ipsec_sa_flags_t flags, u32 salt, u16 src_port,
325                        u16 dst_port, const tunnel_t *tun, u32 *sa_out_index)
326 {
327   vlib_main_t *vm = vlib_get_main ();
328   ipsec_main_t *im = &ipsec_main;
329   clib_error_t *err;
330   ipsec_sa_t *sa;
331   u32 sa_index;
332   u64 rand[2];
333   uword *p;
334   int rv;
335
336   p = hash_get (im->sa_index_by_sa_id, id);
337   if (p)
338     return VNET_API_ERROR_ENTRY_ALREADY_EXISTS;
339
340   if (getrandom (rand, sizeof (rand), 0) != sizeof (rand))
341     return VNET_API_ERROR_INIT_FAILED;
342
343   pool_get_aligned_zero (ipsec_sa_pool, sa, CLIB_CACHE_LINE_BYTES);
344
345   clib_pcg64i_srandom_r (&sa->iv_prng, rand[0], rand[1]);
346
347   fib_node_init (&sa->node, FIB_NODE_TYPE_IPSEC_SA);
348   fib_node_lock (&sa->node);
349   sa_index = sa - ipsec_sa_pool;
350
351   vlib_validate_combined_counter (&ipsec_sa_counters, sa_index);
352   vlib_zero_combined_counter (&ipsec_sa_counters, sa_index);
353   for (int i = 0; i < IPSEC_SA_N_ERRORS; i++)
354     {
355       vlib_validate_simple_counter (&ipsec_sa_err_counters[i], sa_index);
356       vlib_zero_simple_counter (&ipsec_sa_err_counters[i], sa_index);
357     }
358
359   tunnel_copy (tun, &sa->tunnel);
360   sa->id = id;
361   sa->spi = spi;
362   sa->stat_index = sa_index;
363   sa->protocol = proto;
364   sa->flags = flags;
365   sa->salt = salt;
366   sa->thread_index = (vlib_num_workers ()) ? ~0 : 0;
367   if (integ_alg != IPSEC_INTEG_ALG_NONE)
368     {
369       ipsec_sa_set_integ_alg (sa, integ_alg);
370       clib_memcpy (&sa->integ_key, ik, sizeof (sa->integ_key));
371     }
372   ipsec_sa_set_crypto_alg (sa, crypto_alg);
373   ipsec_sa_set_async_op_ids (sa);
374
375   clib_memcpy (&sa->crypto_key, ck, sizeof (sa->crypto_key));
376
377   sa->crypto_sync_key_index = vnet_crypto_key_add (
378     vm, im->crypto_algs[crypto_alg].alg, (u8 *) ck->data, ck->len);
379   if (~0 == sa->crypto_sync_key_index)
380     {
381       pool_put (ipsec_sa_pool, sa);
382       return VNET_API_ERROR_KEY_LENGTH;
383     }
384
385   if (integ_alg != IPSEC_INTEG_ALG_NONE)
386     {
387       sa->integ_sync_key_index = vnet_crypto_key_add (
388         vm, im->integ_algs[integ_alg].alg, (u8 *) ik->data, ik->len);
389       if (~0 == sa->integ_sync_key_index)
390         {
391           pool_put (ipsec_sa_pool, sa);
392           return VNET_API_ERROR_KEY_LENGTH;
393         }
394     }
395
396   if (sa->crypto_async_enc_op_id && !ipsec_sa_is_set_IS_AEAD (sa))
397     sa->crypto_async_key_index =
398       vnet_crypto_key_add_linked (vm, sa->crypto_sync_key_index,
399                                   sa->integ_sync_key_index); // AES-CBC & HMAC
400   else
401     sa->crypto_async_key_index = sa->crypto_sync_key_index;
402
403   if (im->async_mode)
404     {
405       ipsec_sa_set_async_mode (sa, 1);
406     }
407   else if (ipsec_sa_is_set_IS_ASYNC (sa))
408     {
409       ipsec_sa_set_async_mode (sa, 1 /* is_enabled */);
410     }
411   else
412     {
413       ipsec_sa_set_async_mode (sa, 0 /* is_enabled */);
414     }
415
416   err = ipsec_check_support_cb (im, sa);
417   if (err)
418     {
419       clib_warning ("%s", err->what);
420       pool_put (ipsec_sa_pool, sa);
421       return VNET_API_ERROR_UNIMPLEMENTED;
422     }
423
424   err = ipsec_call_add_del_callbacks (im, sa, sa_index, 1);
425   if (err)
426     {
427       pool_put (ipsec_sa_pool, sa);
428       return VNET_API_ERROR_SYSCALL_ERROR_1;
429     }
430
431   if (ipsec_sa_is_set_IS_TUNNEL (sa) &&
432       AF_IP6 == ip_addr_version (&tun->t_src))
433     ipsec_sa_set_IS_TUNNEL_V6 (sa);
434
435   if (ipsec_sa_is_set_IS_TUNNEL (sa) && !ipsec_sa_is_set_IS_INBOUND (sa))
436     {
437       sa->tunnel_flags = sa->tunnel.t_encap_decap_flags;
438
439       rv = tunnel_resolve (&sa->tunnel, FIB_NODE_TYPE_IPSEC_SA, sa_index);
440
441       if (rv)
442         {
443           pool_put (ipsec_sa_pool, sa);
444           return rv;
445         }
446       ipsec_sa_stack (sa);
447
448       /* generate header templates */
449       if (ipsec_sa_is_set_IS_TUNNEL_V6 (sa))
450         {
451           tunnel_build_v6_hdr (&sa->tunnel,
452                                (ipsec_sa_is_set_UDP_ENCAP (sa) ?
453                                   IP_PROTOCOL_UDP :
454                                   IP_PROTOCOL_IPSEC_ESP),
455                                &sa->ip6_hdr);
456         }
457       else
458         {
459           tunnel_build_v4_hdr (&sa->tunnel,
460                                (ipsec_sa_is_set_UDP_ENCAP (sa) ?
461                                   IP_PROTOCOL_UDP :
462                                   IP_PROTOCOL_IPSEC_ESP),
463                                &sa->ip4_hdr);
464         }
465     }
466
467   if (ipsec_sa_is_set_UDP_ENCAP (sa))
468     {
469       if (dst_port == IPSEC_UDP_PORT_NONE)
470         sa->udp_hdr.dst_port = clib_host_to_net_u16 (UDP_DST_PORT_ipsec);
471       else
472         sa->udp_hdr.dst_port = clib_host_to_net_u16 (dst_port);
473
474       if (src_port == IPSEC_UDP_PORT_NONE)
475         sa->udp_hdr.src_port = clib_host_to_net_u16 (UDP_DST_PORT_ipsec);
476       else
477         sa->udp_hdr.src_port = clib_host_to_net_u16 (src_port);
478
479       if (ipsec_sa_is_set_IS_INBOUND (sa))
480         ipsec_register_udp_port (clib_host_to_net_u16 (sa->udp_hdr.dst_port),
481                                  !ipsec_sa_is_set_IS_TUNNEL_V6 (sa));
482     }
483
484   hash_set (im->sa_index_by_sa_id, sa->id, sa_index);
485
486   if (sa_out_index)
487     *sa_out_index = sa_index;
488
489   return (0);
490 }
491
492 static void
493 ipsec_sa_del (ipsec_sa_t * sa)
494 {
495   vlib_main_t *vm = vlib_get_main ();
496   ipsec_main_t *im = &ipsec_main;
497   u32 sa_index;
498
499   sa_index = sa - ipsec_sa_pool;
500   hash_unset (im->sa_index_by_sa_id, sa->id);
501   tunnel_unresolve (&sa->tunnel);
502
503   /* no recovery possible when deleting an SA */
504   (void) ipsec_call_add_del_callbacks (im, sa, sa_index, 0);
505
506   if (ipsec_sa_is_set_IS_ASYNC (sa))
507     {
508       if (!ipsec_sa_is_set_IS_AEAD (sa))
509         vnet_crypto_key_del (vm, sa->crypto_async_key_index);
510     }
511
512   if (ipsec_sa_is_set_UDP_ENCAP (sa) && ipsec_sa_is_set_IS_INBOUND (sa))
513     ipsec_unregister_udp_port (clib_net_to_host_u16 (sa->udp_hdr.dst_port),
514                                !ipsec_sa_is_set_IS_TUNNEL_V6 (sa));
515
516   if (ipsec_sa_is_set_IS_TUNNEL (sa) && !ipsec_sa_is_set_IS_INBOUND (sa))
517     dpo_reset (&sa->dpo);
518   vnet_crypto_key_del (vm, sa->crypto_sync_key_index);
519   if (sa->integ_alg != IPSEC_INTEG_ALG_NONE)
520     vnet_crypto_key_del (vm, sa->integ_sync_key_index);
521   pool_put (ipsec_sa_pool, sa);
522 }
523
524 int
525 ipsec_sa_bind (u32 id, u32 worker, bool bind)
526 {
527   ipsec_main_t *im = &ipsec_main;
528   uword *p;
529   ipsec_sa_t *sa;
530
531   p = hash_get (im->sa_index_by_sa_id, id);
532   if (!p)
533     return VNET_API_ERROR_INVALID_VALUE;
534
535   sa = ipsec_sa_get (p[0]);
536
537   if (!bind)
538     {
539       sa->thread_index = ~0;
540       return 0;
541     }
542
543   if (worker >= vlib_num_workers ())
544     return VNET_API_ERROR_INVALID_WORKER;
545
546   sa->thread_index = vlib_get_worker_thread_index (worker);
547   return 0;
548 }
549
550 void
551 ipsec_sa_unlock (index_t sai)
552 {
553   ipsec_sa_t *sa;
554
555   if (INDEX_INVALID == sai)
556     return;
557
558   sa = ipsec_sa_get (sai);
559
560   fib_node_unlock (&sa->node);
561 }
562
563 void
564 ipsec_sa_lock (index_t sai)
565 {
566   ipsec_sa_t *sa;
567
568   if (INDEX_INVALID == sai)
569     return;
570
571   sa = ipsec_sa_get (sai);
572
573   fib_node_lock (&sa->node);
574 }
575
576 index_t
577 ipsec_sa_find_and_lock (u32 id)
578 {
579   ipsec_main_t *im = &ipsec_main;
580   ipsec_sa_t *sa;
581   uword *p;
582
583   p = hash_get (im->sa_index_by_sa_id, id);
584
585   if (!p)
586     return INDEX_INVALID;
587
588   sa = ipsec_sa_get (p[0]);
589
590   fib_node_lock (&sa->node);
591
592   return (p[0]);
593 }
594
595 int
596 ipsec_sa_unlock_id (u32 id)
597 {
598   ipsec_main_t *im = &ipsec_main;
599   uword *p;
600
601   p = hash_get (im->sa_index_by_sa_id, id);
602
603   if (!p)
604     return VNET_API_ERROR_NO_SUCH_ENTRY;
605
606   ipsec_sa_unlock (p[0]);
607
608   return (0);
609 }
610
611 void
612 ipsec_sa_clear (index_t sai)
613 {
614   vlib_zero_combined_counter (&ipsec_sa_counters, sai);
615   for (int i = 0; i < IPSEC_SA_N_ERRORS; i++)
616     vlib_zero_simple_counter (&ipsec_sa_err_counters[i], sai);
617 }
618
619 void
620 ipsec_sa_walk (ipsec_sa_walk_cb_t cb, void *ctx)
621 {
622   ipsec_sa_t *sa;
623
624   /* *INDENT-OFF* */
625   pool_foreach (sa, ipsec_sa_pool)
626     {
627       if (WALK_CONTINUE != cb (sa, ctx))
628         break;
629     }
630   /* *INDENT-ON* */
631 }
632
633 /**
634  * Function definition to get a FIB node from its index
635  */
636 static fib_node_t *
637 ipsec_sa_fib_node_get (fib_node_index_t index)
638 {
639   ipsec_sa_t *sa;
640
641   sa = ipsec_sa_get (index);
642
643   return (&sa->node);
644 }
645
646 static ipsec_sa_t *
647 ipsec_sa_from_fib_node (fib_node_t *node)
648 {
649   ASSERT (FIB_NODE_TYPE_IPSEC_SA == node->fn_type);
650   return (
651     (ipsec_sa_t *) (((char *) node) - STRUCT_OFFSET_OF (ipsec_sa_t, node)));
652 }
653
654 /**
655  * Function definition to inform the FIB node that its last lock has gone.
656  */
657 static void
658 ipsec_sa_last_lock_gone (fib_node_t *node)
659 {
660   /*
661    * The ipsec SA is a root of the graph. As such
662    * it never has children and thus is never locked.
663    */
664   ipsec_sa_del (ipsec_sa_from_fib_node (node));
665 }
666
667 /**
668  * Function definition to backwalk a FIB node
669  */
670 static fib_node_back_walk_rc_t
671 ipsec_sa_back_walk (fib_node_t *node, fib_node_back_walk_ctx_t *ctx)
672 {
673   ipsec_sa_stack (ipsec_sa_from_fib_node (node));
674
675   return (FIB_NODE_BACK_WALK_CONTINUE);
676 }
677
678 /*
679  * Virtual function table registered by SAs
680  * for participation in the FIB object graph.
681  */
682 const static fib_node_vft_t ipsec_sa_vft = {
683   .fnv_get = ipsec_sa_fib_node_get,
684   .fnv_last_lock = ipsec_sa_last_lock_gone,
685   .fnv_back_walk = ipsec_sa_back_walk,
686 };
687
688 /* Init per-SA error counters and node type */
689 clib_error_t *
690 ipsec_sa_init (vlib_main_t *vm)
691 {
692   fib_node_register_type (FIB_NODE_TYPE_IPSEC_SA, &ipsec_sa_vft);
693
694 #define _(index, val, err, desc)                                              \
695   ipsec_sa_err_counters[index].name =                                         \
696     (char *) format (0, "SA-" #err "%c", 0);                                  \
697   ipsec_sa_err_counters[index].stat_segment_name =                            \
698     (char *) format (0, "/net/ipsec/sa/err/" #err "%c", 0);                   \
699   ipsec_sa_err_counters[index].counters = 0;
700   foreach_ipsec_sa_err
701 #undef _
702     return 0;
703 }
704
705 VLIB_INIT_FUNCTION (ipsec_sa_init);
706
707 /*
708  * fd.io coding-style-patch-verification: ON
709  *
710  * Local Variables:
711  * eval: (c-set-style "gnu")
712  * End:
713  */