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