vcl: allow more rx events on peek
[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   else if (IPSEC_CRYPTO_ALG_IS_NULL_GMAC (crypto_alg))
140     {
141       sa->integ_icv_size = im->crypto_algs[crypto_alg].icv_size;
142       ipsec_sa_set_IS_CTR (sa);
143       ipsec_sa_set_IS_AEAD (sa);
144       ipsec_sa_set_IS_NULL_GMAC (sa);
145     }
146 }
147
148 void
149 ipsec_sa_set_integ_alg (ipsec_sa_t * sa, ipsec_integ_alg_t integ_alg)
150 {
151   ipsec_main_t *im = &ipsec_main;
152   sa->integ_alg = integ_alg;
153   sa->integ_icv_size = im->integ_algs[integ_alg].icv_size;
154   sa->integ_sync_op_id = im->integ_algs[integ_alg].op_id;
155   sa->integ_calg = im->integ_algs[integ_alg].alg;
156   ASSERT (sa->integ_icv_size <= ESP_MAX_ICV_SIZE);
157 }
158
159 void
160 ipsec_sa_set_async_op_ids (ipsec_sa_t * sa)
161 {
162   if (ipsec_sa_is_set_USE_ESN (sa))
163     {
164 #define _(n, s, k)                                                            \
165   if (sa->crypto_sync_enc_op_id == VNET_CRYPTO_OP_##n##_ENC)                  \
166     sa->crypto_async_enc_op_id = VNET_CRYPTO_OP_##n##_TAG16_AAD12_ENC;        \
167   if (sa->crypto_sync_dec_op_id == VNET_CRYPTO_OP_##n##_DEC)                  \
168     sa->crypto_async_dec_op_id = VNET_CRYPTO_OP_##n##_TAG16_AAD12_DEC;
169       foreach_crypto_aead_alg
170 #undef _
171     }
172   else
173     {
174 #define _(n, s, k)                                                            \
175   if (sa->crypto_sync_enc_op_id == VNET_CRYPTO_OP_##n##_ENC)                  \
176     sa->crypto_async_enc_op_id = VNET_CRYPTO_OP_##n##_TAG16_AAD8_ENC;         \
177   if (sa->crypto_sync_dec_op_id == VNET_CRYPTO_OP_##n##_DEC)                  \
178     sa->crypto_async_dec_op_id = VNET_CRYPTO_OP_##n##_TAG16_AAD8_DEC;
179       foreach_crypto_aead_alg
180 #undef _
181     }
182
183 #define _(c, h, s, k, d)                                                      \
184   if (sa->crypto_sync_enc_op_id == VNET_CRYPTO_OP_##c##_ENC &&                \
185       sa->integ_sync_op_id == VNET_CRYPTO_OP_##h##_HMAC)                      \
186     sa->crypto_async_enc_op_id = VNET_CRYPTO_OP_##c##_##h##_TAG##d##_ENC;     \
187   if (sa->crypto_sync_dec_op_id == VNET_CRYPTO_OP_##c##_DEC &&                \
188       sa->integ_sync_op_id == VNET_CRYPTO_OP_##h##_HMAC)                      \
189     sa->crypto_async_dec_op_id = VNET_CRYPTO_OP_##c##_##h##_TAG##d##_DEC;
190   foreach_crypto_link_async_alg
191 #undef _
192 }
193
194 int
195 ipsec_sa_update (u32 id, u16 src_port, u16 dst_port, const tunnel_t *tun,
196                  bool is_tun)
197 {
198   ipsec_main_t *im = &ipsec_main;
199   ipsec_sa_t *sa;
200   u32 sa_index;
201   uword *p;
202   int rv;
203
204   p = hash_get (im->sa_index_by_sa_id, id);
205   if (!p)
206     return VNET_API_ERROR_NO_SUCH_ENTRY;
207
208   sa = ipsec_sa_get (p[0]);
209   sa_index = sa - ipsec_sa_pool;
210
211   if (is_tun && ipsec_sa_is_set_IS_TUNNEL (sa) &&
212       (ip_address_cmp (&tun->t_src, &sa->tunnel.t_src) != 0 ||
213        ip_address_cmp (&tun->t_dst, &sa->tunnel.t_dst) != 0))
214     {
215       /* if the source IP is updated for an inbound SA under a tunnel protect,
216        we need to update the tun_protect DB with the new src IP */
217       if (ipsec_sa_is_set_IS_INBOUND (sa) &&
218           ip_address_cmp (&tun->t_src, &sa->tunnel.t_src) != 0 &&
219           !ip46_address_is_zero (&tun->t_src.ip))
220         {
221           if (ip46_address_is_ip4 (&sa->tunnel.t_src.ip))
222             {
223               ipsec4_tunnel_kv_t old_key, new_key;
224               clib_bihash_kv_8_16_t res,
225                 *bkey = (clib_bihash_kv_8_16_t *) &old_key;
226
227               ipsec4_tunnel_mk_key (&old_key, &sa->tunnel.t_src.ip.ip4,
228                                     clib_host_to_net_u32 (sa->spi));
229               ipsec4_tunnel_mk_key (&new_key, &tun->t_src.ip.ip4,
230                                     clib_host_to_net_u32 (sa->spi));
231
232               if (!clib_bihash_search_8_16 (&im->tun4_protect_by_key, bkey,
233                                             &res))
234                 {
235                   clib_bihash_add_del_8_16 (&im->tun4_protect_by_key, &res, 0);
236                   res.key = new_key.key;
237                   clib_bihash_add_del_8_16 (&im->tun4_protect_by_key, &res, 1);
238                 }
239             }
240           else
241             {
242               ipsec6_tunnel_kv_t old_key = {
243           .key = {
244             .remote_ip =  sa->tunnel.t_src.ip.ip6,
245             .spi = clib_host_to_net_u32 (sa->spi),
246           },
247         }, new_key = {
248           .key = {
249             .remote_ip = tun->t_src.ip.ip6,
250             .spi = clib_host_to_net_u32 (sa->spi),
251           }};
252               clib_bihash_kv_24_16_t res,
253                 *bkey = (clib_bihash_kv_24_16_t *) &old_key;
254
255               if (!clib_bihash_search_24_16 (&im->tun6_protect_by_key, bkey,
256                                              &res))
257                 {
258                   clib_bihash_add_del_24_16 (&im->tun6_protect_by_key, &res,
259                                              0);
260                   clib_memcpy (&res.key, &new_key.key, 3);
261                   clib_bihash_add_del_24_16 (&im->tun6_protect_by_key, &res,
262                                              1);
263                 }
264             }
265         }
266       tunnel_unresolve (&sa->tunnel);
267       tunnel_copy (tun, &sa->tunnel);
268       if (!ipsec_sa_is_set_IS_INBOUND (sa))
269         {
270           dpo_reset (&sa->dpo);
271
272           sa->tunnel_flags = sa->tunnel.t_encap_decap_flags;
273
274           rv = tunnel_resolve (&sa->tunnel, FIB_NODE_TYPE_IPSEC_SA, sa_index);
275
276           if (rv)
277             {
278               hash_unset (im->sa_index_by_sa_id, sa->id);
279               pool_put (ipsec_sa_pool, sa);
280               return rv;
281             }
282           ipsec_sa_stack (sa);
283           /* generate header templates */
284           if (ipsec_sa_is_set_IS_TUNNEL_V6 (sa))
285             {
286               tunnel_build_v6_hdr (&sa->tunnel,
287                                    (ipsec_sa_is_set_UDP_ENCAP (sa) ?
288                                             IP_PROTOCOL_UDP :
289                                             IP_PROTOCOL_IPSEC_ESP),
290                                    &sa->ip6_hdr);
291             }
292           else
293             {
294               tunnel_build_v4_hdr (&sa->tunnel,
295                                    (ipsec_sa_is_set_UDP_ENCAP (sa) ?
296                                             IP_PROTOCOL_UDP :
297                                             IP_PROTOCOL_IPSEC_ESP),
298                                    &sa->ip4_hdr);
299             }
300         }
301     }
302
303   if (ipsec_sa_is_set_UDP_ENCAP (sa))
304     {
305       if (dst_port != IPSEC_UDP_PORT_NONE &&
306           dst_port != clib_net_to_host_u16 (sa->udp_hdr.dst_port))
307         {
308           if (ipsec_sa_is_set_IS_INBOUND (sa))
309             {
310               ipsec_unregister_udp_port (
311                 clib_net_to_host_u16 (sa->udp_hdr.dst_port),
312                 !ipsec_sa_is_set_IS_TUNNEL_V6 (sa));
313               ipsec_register_udp_port (dst_port,
314                                        !ipsec_sa_is_set_IS_TUNNEL_V6 (sa));
315             }
316           sa->udp_hdr.dst_port = clib_host_to_net_u16 (dst_port);
317         }
318       if (src_port != IPSEC_UDP_PORT_NONE &&
319           src_port != clib_net_to_host_u16 (sa->udp_hdr.src_port))
320         sa->udp_hdr.src_port = clib_host_to_net_u16 (src_port);
321     }
322   return (0);
323 }
324
325 int
326 ipsec_sa_add_and_lock (u32 id, u32 spi, ipsec_protocol_t proto,
327                        ipsec_crypto_alg_t crypto_alg, const ipsec_key_t *ck,
328                        ipsec_integ_alg_t integ_alg, const ipsec_key_t *ik,
329                        ipsec_sa_flags_t flags, u32 salt, u16 src_port,
330                        u16 dst_port, u32 anti_replay_window_size,
331                        const tunnel_t *tun, u32 *sa_out_index)
332 {
333   vlib_main_t *vm = vlib_get_main ();
334   ipsec_main_t *im = &ipsec_main;
335   clib_error_t *err;
336   ipsec_sa_t *sa;
337   u32 sa_index;
338   u64 rand[2];
339   uword *p;
340   int rv;
341
342   p = hash_get (im->sa_index_by_sa_id, id);
343   if (p)
344     return VNET_API_ERROR_ENTRY_ALREADY_EXISTS;
345
346   if (getrandom (rand, sizeof (rand), 0) != sizeof (rand))
347     return VNET_API_ERROR_INIT_FAILED;
348
349   pool_get_aligned_zero (ipsec_sa_pool, sa, CLIB_CACHE_LINE_BYTES);
350
351   clib_pcg64i_srandom_r (&sa->iv_prng, rand[0], rand[1]);
352
353   fib_node_init (&sa->node, FIB_NODE_TYPE_IPSEC_SA);
354   fib_node_lock (&sa->node);
355   sa_index = sa - ipsec_sa_pool;
356
357   vlib_validate_combined_counter (&ipsec_sa_counters, sa_index);
358   vlib_zero_combined_counter (&ipsec_sa_counters, sa_index);
359   for (int i = 0; i < IPSEC_SA_N_ERRORS; i++)
360     {
361       vlib_validate_simple_counter (&ipsec_sa_err_counters[i], sa_index);
362       vlib_zero_simple_counter (&ipsec_sa_err_counters[i], sa_index);
363     }
364
365   tunnel_copy (tun, &sa->tunnel);
366   sa->id = id;
367   sa->spi = spi;
368   sa->stat_index = sa_index;
369   sa->protocol = proto;
370   sa->flags = flags;
371   sa->salt = salt;
372   sa->thread_index = (vlib_num_workers ()) ? ~0 : 0;
373   if (integ_alg != IPSEC_INTEG_ALG_NONE)
374     {
375       ipsec_sa_set_integ_alg (sa, integ_alg);
376       clib_memcpy (&sa->integ_key, ik, sizeof (sa->integ_key));
377     }
378   ipsec_sa_set_crypto_alg (sa, crypto_alg);
379   ipsec_sa_set_async_op_ids (sa);
380
381   if (ipsec_sa_is_set_USE_ANTI_REPLAY (sa) && anti_replay_window_size > 64)
382     ipsec_sa_set_ANTI_REPLAY_HUGE (sa);
383
384   clib_memcpy (&sa->crypto_key, ck, sizeof (sa->crypto_key));
385
386   sa->crypto_sync_key_index = vnet_crypto_key_add (
387     vm, im->crypto_algs[crypto_alg].alg, (u8 *) ck->data, ck->len);
388   if (~0 == sa->crypto_sync_key_index)
389     {
390       pool_put (ipsec_sa_pool, sa);
391       return VNET_API_ERROR_KEY_LENGTH;
392     }
393
394   if (integ_alg != IPSEC_INTEG_ALG_NONE)
395     {
396       sa->integ_sync_key_index = vnet_crypto_key_add (
397         vm, im->integ_algs[integ_alg].alg, (u8 *) ik->data, ik->len);
398       if (~0 == sa->integ_sync_key_index)
399         {
400           pool_put (ipsec_sa_pool, sa);
401           return VNET_API_ERROR_KEY_LENGTH;
402         }
403     }
404
405   if (sa->crypto_async_enc_op_id && !ipsec_sa_is_set_IS_AEAD (sa))
406     sa->crypto_async_key_index =
407       vnet_crypto_key_add_linked (vm, sa->crypto_sync_key_index,
408                                   sa->integ_sync_key_index); // AES-CBC & HMAC
409   else
410     sa->crypto_async_key_index = sa->crypto_sync_key_index;
411
412   if (im->async_mode)
413     {
414       ipsec_sa_set_async_mode (sa, 1);
415     }
416   else if (ipsec_sa_is_set_IS_ASYNC (sa))
417     {
418       ipsec_sa_set_async_mode (sa, 1 /* is_enabled */);
419     }
420   else
421     {
422       ipsec_sa_set_async_mode (sa, 0 /* is_enabled */);
423     }
424
425   err = ipsec_check_support_cb (im, sa);
426   if (err)
427     {
428       clib_warning ("%v", err->what);
429       pool_put (ipsec_sa_pool, sa);
430       return VNET_API_ERROR_UNIMPLEMENTED;
431     }
432
433   err = ipsec_call_add_del_callbacks (im, sa, sa_index, 1);
434   if (err)
435     {
436       pool_put (ipsec_sa_pool, sa);
437       return VNET_API_ERROR_SYSCALL_ERROR_1;
438     }
439
440   if (ipsec_sa_is_set_IS_TUNNEL (sa) &&
441       AF_IP6 == ip_addr_version (&tun->t_src))
442     ipsec_sa_set_IS_TUNNEL_V6 (sa);
443
444   if (ipsec_sa_is_set_IS_TUNNEL (sa) && !ipsec_sa_is_set_IS_INBOUND (sa))
445     {
446       sa->tunnel_flags = sa->tunnel.t_encap_decap_flags;
447
448       rv = tunnel_resolve (&sa->tunnel, FIB_NODE_TYPE_IPSEC_SA, sa_index);
449
450       if (rv)
451         {
452           pool_put (ipsec_sa_pool, sa);
453           return rv;
454         }
455       ipsec_sa_stack (sa);
456
457       /* generate header templates */
458       if (ipsec_sa_is_set_IS_TUNNEL_V6 (sa))
459         {
460           tunnel_build_v6_hdr (&sa->tunnel,
461                                (ipsec_sa_is_set_UDP_ENCAP (sa) ?
462                                   IP_PROTOCOL_UDP :
463                                   IP_PROTOCOL_IPSEC_ESP),
464                                &sa->ip6_hdr);
465         }
466       else
467         {
468           tunnel_build_v4_hdr (&sa->tunnel,
469                                (ipsec_sa_is_set_UDP_ENCAP (sa) ?
470                                   IP_PROTOCOL_UDP :
471                                   IP_PROTOCOL_IPSEC_ESP),
472                                &sa->ip4_hdr);
473         }
474     }
475
476   if (ipsec_sa_is_set_UDP_ENCAP (sa))
477     {
478       if (dst_port == IPSEC_UDP_PORT_NONE)
479         sa->udp_hdr.dst_port = clib_host_to_net_u16 (UDP_DST_PORT_ipsec);
480       else
481         sa->udp_hdr.dst_port = clib_host_to_net_u16 (dst_port);
482
483       if (src_port == IPSEC_UDP_PORT_NONE)
484         sa->udp_hdr.src_port = clib_host_to_net_u16 (UDP_DST_PORT_ipsec);
485       else
486         sa->udp_hdr.src_port = clib_host_to_net_u16 (src_port);
487
488       if (ipsec_sa_is_set_IS_INBOUND (sa))
489         ipsec_register_udp_port (clib_host_to_net_u16 (sa->udp_hdr.dst_port),
490                                  !ipsec_sa_is_set_IS_TUNNEL_V6 (sa));
491     }
492
493   /* window size rounded up to next power of 2 */
494   if (ipsec_sa_is_set_ANTI_REPLAY_HUGE (sa))
495     {
496       anti_replay_window_size = 1 << max_log2 (anti_replay_window_size);
497       sa->replay_window_huge =
498         clib_bitmap_set_region (0, 0, 1, anti_replay_window_size);
499     }
500   else
501     {
502       sa->replay_window = ~0;
503     }
504
505   hash_set (im->sa_index_by_sa_id, sa->id, sa_index);
506
507   if (sa_out_index)
508     *sa_out_index = sa_index;
509
510   return (0);
511 }
512
513 static void
514 ipsec_sa_del (ipsec_sa_t * sa)
515 {
516   vlib_main_t *vm = vlib_get_main ();
517   ipsec_main_t *im = &ipsec_main;
518   u32 sa_index;
519
520   sa_index = sa - ipsec_sa_pool;
521   hash_unset (im->sa_index_by_sa_id, sa->id);
522   tunnel_unresolve (&sa->tunnel);
523
524   /* no recovery possible when deleting an SA */
525   (void) ipsec_call_add_del_callbacks (im, sa, sa_index, 0);
526
527   if (ipsec_sa_is_set_IS_ASYNC (sa))
528     {
529       if (!ipsec_sa_is_set_IS_AEAD (sa))
530         vnet_crypto_key_del (vm, sa->crypto_async_key_index);
531     }
532
533   if (ipsec_sa_is_set_UDP_ENCAP (sa) && ipsec_sa_is_set_IS_INBOUND (sa))
534     ipsec_unregister_udp_port (clib_net_to_host_u16 (sa->udp_hdr.dst_port),
535                                !ipsec_sa_is_set_IS_TUNNEL_V6 (sa));
536
537   if (ipsec_sa_is_set_IS_TUNNEL (sa) && !ipsec_sa_is_set_IS_INBOUND (sa))
538     dpo_reset (&sa->dpo);
539   vnet_crypto_key_del (vm, sa->crypto_sync_key_index);
540   if (sa->integ_alg != IPSEC_INTEG_ALG_NONE)
541     vnet_crypto_key_del (vm, sa->integ_sync_key_index);
542   if (ipsec_sa_is_set_ANTI_REPLAY_HUGE (sa))
543     clib_bitmap_free (sa->replay_window_huge);
544   pool_put (ipsec_sa_pool, sa);
545 }
546
547 int
548 ipsec_sa_bind (u32 id, u32 worker, bool bind)
549 {
550   ipsec_main_t *im = &ipsec_main;
551   uword *p;
552   ipsec_sa_t *sa;
553
554   p = hash_get (im->sa_index_by_sa_id, id);
555   if (!p)
556     return VNET_API_ERROR_INVALID_VALUE;
557
558   sa = ipsec_sa_get (p[0]);
559
560   if (!bind)
561     {
562       sa->thread_index = ~0;
563       return 0;
564     }
565
566   if (worker >= vlib_num_workers ())
567     return VNET_API_ERROR_INVALID_WORKER;
568
569   sa->thread_index = vlib_get_worker_thread_index (worker);
570   return 0;
571 }
572
573 void
574 ipsec_sa_unlock (index_t sai)
575 {
576   ipsec_sa_t *sa;
577
578   if (INDEX_INVALID == sai)
579     return;
580
581   sa = ipsec_sa_get (sai);
582
583   fib_node_unlock (&sa->node);
584 }
585
586 void
587 ipsec_sa_lock (index_t sai)
588 {
589   ipsec_sa_t *sa;
590
591   if (INDEX_INVALID == sai)
592     return;
593
594   sa = ipsec_sa_get (sai);
595
596   fib_node_lock (&sa->node);
597 }
598
599 index_t
600 ipsec_sa_find_and_lock (u32 id)
601 {
602   ipsec_main_t *im = &ipsec_main;
603   ipsec_sa_t *sa;
604   uword *p;
605
606   p = hash_get (im->sa_index_by_sa_id, id);
607
608   if (!p)
609     return INDEX_INVALID;
610
611   sa = ipsec_sa_get (p[0]);
612
613   fib_node_lock (&sa->node);
614
615   return (p[0]);
616 }
617
618 int
619 ipsec_sa_unlock_id (u32 id)
620 {
621   ipsec_main_t *im = &ipsec_main;
622   uword *p;
623
624   p = hash_get (im->sa_index_by_sa_id, id);
625
626   if (!p)
627     return VNET_API_ERROR_NO_SUCH_ENTRY;
628
629   ipsec_sa_unlock (p[0]);
630
631   return (0);
632 }
633
634 void
635 ipsec_sa_clear (index_t sai)
636 {
637   vlib_zero_combined_counter (&ipsec_sa_counters, sai);
638   for (int i = 0; i < IPSEC_SA_N_ERRORS; i++)
639     vlib_zero_simple_counter (&ipsec_sa_err_counters[i], sai);
640 }
641
642 void
643 ipsec_sa_walk (ipsec_sa_walk_cb_t cb, void *ctx)
644 {
645   ipsec_sa_t *sa;
646
647   pool_foreach (sa, ipsec_sa_pool)
648     {
649       if (WALK_CONTINUE != cb (sa, ctx))
650         break;
651     }
652 }
653
654 /**
655  * Function definition to get a FIB node from its index
656  */
657 static fib_node_t *
658 ipsec_sa_fib_node_get (fib_node_index_t index)
659 {
660   ipsec_sa_t *sa;
661
662   sa = ipsec_sa_get (index);
663
664   return (&sa->node);
665 }
666
667 static ipsec_sa_t *
668 ipsec_sa_from_fib_node (fib_node_t *node)
669 {
670   ASSERT (FIB_NODE_TYPE_IPSEC_SA == node->fn_type);
671   return (
672     (ipsec_sa_t *) (((char *) node) - STRUCT_OFFSET_OF (ipsec_sa_t, node)));
673 }
674
675 /**
676  * Function definition to inform the FIB node that its last lock has gone.
677  */
678 static void
679 ipsec_sa_last_lock_gone (fib_node_t *node)
680 {
681   /*
682    * The ipsec SA is a root of the graph. As such
683    * it never has children and thus is never locked.
684    */
685   ipsec_sa_del (ipsec_sa_from_fib_node (node));
686 }
687
688 /**
689  * Function definition to backwalk a FIB node
690  */
691 static fib_node_back_walk_rc_t
692 ipsec_sa_back_walk (fib_node_t *node, fib_node_back_walk_ctx_t *ctx)
693 {
694   ipsec_sa_stack (ipsec_sa_from_fib_node (node));
695
696   return (FIB_NODE_BACK_WALK_CONTINUE);
697 }
698
699 /*
700  * Virtual function table registered by SAs
701  * for participation in the FIB object graph.
702  */
703 const static fib_node_vft_t ipsec_sa_vft = {
704   .fnv_get = ipsec_sa_fib_node_get,
705   .fnv_last_lock = ipsec_sa_last_lock_gone,
706   .fnv_back_walk = ipsec_sa_back_walk,
707 };
708
709 /* Init per-SA error counters and node type */
710 clib_error_t *
711 ipsec_sa_init (vlib_main_t *vm)
712 {
713   fib_node_register_type (FIB_NODE_TYPE_IPSEC_SA, &ipsec_sa_vft);
714
715 #define _(index, val, err, desc)                                              \
716   ipsec_sa_err_counters[index].name =                                         \
717     (char *) format (0, "SA-" #err "%c", 0);                                  \
718   ipsec_sa_err_counters[index].stat_segment_name =                            \
719     (char *) format (0, "/net/ipsec/sa/err/" #err "%c", 0);                   \
720   ipsec_sa_err_counters[index].counters = 0;
721   foreach_ipsec_sa_err
722 #undef _
723     return 0;
724 }
725
726 VLIB_INIT_FUNCTION (ipsec_sa_init);
727
728 /*
729  * fd.io coding-style-patch-verification: ON
730  *
731  * Local Variables:
732  * eval: (c-set-style "gnu")
733  * End:
734  */