36ecdf1dbc9a9c989e67e461b34ef4b7046dff7d
[vpp.git] / src / vnet / bfd / bfd_udp.c
1 /*
2  * Copyright (c) 2011-2016 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  * @file
17  * @brief BFD UDP transport layer implementation
18  */
19 #include <vppinfra/types.h>
20 #include <vlibmemory/api.h>
21 #include <vlib/vlib.h>
22 #include <vlib/buffer.h>
23 #include <vnet/ip/format.h>
24 #include <vnet/ethernet/packet.h>
25 #include <vnet/udp/udp_local.h>
26 #include <vnet/udp/udp_packet.h>
27 #include <vnet/ip/lookup.h>
28 #include <vnet/ip/icmp46_packet.h>
29 #include <vnet/ip/ip4.h>
30 #include <vnet/ip/ip6.h>
31 #include <vnet/ip/ip6_packet.h>
32 #include <vnet/ip/ip6_link.h>
33 #include <vnet/adj/adj.h>
34 #include <vnet/adj/adj_nbr.h>
35 #include <vnet/dpo/receive_dpo.h>
36 #include <vnet/fib/fib_entry.h>
37 #include <vnet/fib/fib_table.h>
38 #include <vlib/stats/stats.h>
39 #include <vnet/bfd/bfd_debug.h>
40 #include <vnet/bfd/bfd_udp.h>
41 #include <vnet/bfd/bfd_main.h>
42 #include <vnet/bfd/bfd_api.h>
43 #include <vnet/bfd/bfd.api_enum.h>
44
45 typedef struct
46 {
47   bfd_main_t *bfd_main;
48   /* hashmap - bfd session index by bfd key - used for CLI/API lookup, where
49    * discriminator is unknown */
50   mhash_t bfd_session_idx_by_bfd_key;
51   /* convenience variable */
52   vnet_main_t *vnet_main;
53   /* flag indicating whether echo_source_sw_if_index holds a valid value */
54   int echo_source_is_set;
55   /* loopback interface used to get echo source ip */
56   u32 echo_source_sw_if_index;
57   /* node index of "ip4-arp" node */
58   u32 ip4_arp_idx;
59   /* node index of "ip6-discover-neighbor" node */
60   u32 ip6_ndp_idx;
61   /* node index of "ip4-rewrite" node */
62   u32 ip4_rewrite_idx;
63   /* node index of "ip6-rewrite" node */
64   u32 ip6_rewrite_idx;
65   /* node index of "ip4-midchain" node */
66   u32 ip4_midchain_idx;
67   /* node index of "ip6-midchain" node */
68   u32 ip6_midchain_idx;
69   /* log class */
70   vlib_log_class_t log_class;
71   /* number of active udp4 sessions */
72   u32 udp4_sessions_count;
73   u32 udp4_sessions_count_stat_seg_entry;
74   /* number of active udp6 sessions */
75   u32 udp6_sessions_count;
76   u32 udp6_sessions_count_stat_seg_entry;
77 } bfd_udp_main_t;
78
79 static vlib_node_registration_t bfd_udp4_input_node;
80 static vlib_node_registration_t bfd_udp6_input_node;
81 static vlib_node_registration_t bfd_udp_echo4_input_node;
82 static vlib_node_registration_t bfd_udp_echo6_input_node;
83
84 bfd_udp_main_t bfd_udp_main;
85
86 void
87 bfd_udp_update_stat_segment_entry (u32 entry, u64 value)
88 {
89   vlib_stats_segment_lock ();
90   vlib_stats_set_gauge (entry, value);
91   vlib_stats_segment_unlock ();
92 }
93
94 vnet_api_error_t
95 bfd_udp_set_echo_source (u32 sw_if_index)
96 {
97   vnet_sw_interface_t *sw_if =
98     vnet_get_sw_interface_or_null (bfd_udp_main.vnet_main, sw_if_index);
99   if (sw_if)
100     {
101       bfd_udp_main.echo_source_sw_if_index = sw_if_index;
102       bfd_udp_main.echo_source_is_set = 1;
103       return 0;
104     }
105   return VNET_API_ERROR_BFD_ENOENT;
106 }
107
108 vnet_api_error_t
109 bfd_udp_del_echo_source ()
110 {
111   bfd_udp_main.echo_source_sw_if_index = ~0;
112   bfd_udp_main.echo_source_is_set = 0;
113   return 0;
114 }
115
116 int
117 bfd_udp_is_echo_available (bfd_transport_e transport)
118 {
119   if (!bfd_udp_main.echo_source_is_set)
120     {
121       BFD_DBG ("UDP echo source not set - echo not available");
122       return 0;
123     }
124   /*
125    * for the echo to work, we need a loopback interface with at least one
126    * address with netmask length at most 31 (ip4) or 127 (ip6) so that we can
127    * pick an unused address from that subnet
128    */
129   vnet_sw_interface_t *sw_if =
130     vnet_get_sw_interface_or_null (bfd_udp_main.vnet_main,
131                                    bfd_udp_main.echo_source_sw_if_index);
132   if (sw_if && sw_if->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP)
133     {
134       if (BFD_TRANSPORT_UDP4 == transport)
135         {
136           ip4_main_t *im = &ip4_main;
137           ip_interface_address_t *ia = NULL;
138           /* *INDENT-OFF* */
139           foreach_ip_interface_address (&im->lookup_main, ia,
140                                         bfd_udp_main.echo_source_sw_if_index,
141                                         0 /* honor unnumbered */, ({
142                                           if (ia->address_length <= 31)
143                                             {
144                                               return 1;
145                                             }
146                                         }));
147           /* *INDENT-ON* */
148         }
149       else if (BFD_TRANSPORT_UDP6 == transport)
150         {
151           ip6_main_t *im = &ip6_main;
152           ip_interface_address_t *ia = NULL;
153           /* *INDENT-OFF* */
154           foreach_ip_interface_address (&im->lookup_main, ia,
155                                         bfd_udp_main.echo_source_sw_if_index,
156                                         0 /* honor unnumbered */, ({
157                                           if (ia->address_length <= 127)
158                                             {
159                                               return 1;
160                                             }
161                                         }));
162           /* *INDENT-ON* */
163         }
164     }
165   BFD_DBG ("No usable IP address for UDP echo - echo not available");
166   return 0;
167 }
168
169 static u16
170 bfd_udp_bs_idx_to_sport (u32 bs_idx)
171 {
172   /* The source port MUST be in the range 49152 through 65535. The same UDP
173    * source port number MUST be used for all BFD Control packets associated
174    * with a particular session.  The source port number SHOULD be unique among
175    * all BFD sessions on the system. If more than 16384 BFD sessions are
176    * simultaneously active, UDP source port numbers MAY be reused on
177    * multiple sessions, but the number of distinct uses of the same UDP
178    * source port number SHOULD be minimized.
179    */
180   return 49152 + bs_idx % (65535 - 49152 + 1);
181 }
182
183 int
184 bfd_udp_get_echo_src_ip4 (ip4_address_t * addr)
185 {
186   if (!bfd_udp_main.echo_source_is_set)
187     {
188       BFD_ERR ("cannot find ip4 address, echo source not set");
189       return 0;
190     }
191   ip_interface_address_t *ia = NULL;
192   ip4_main_t *im = &ip4_main;
193
194   /* *INDENT-OFF* */
195   foreach_ip_interface_address (
196       &im->lookup_main, ia, bfd_udp_main.echo_source_sw_if_index,
197       0 /* honor unnumbered */, ({
198         ip4_address_t *x =
199             ip_interface_address_get_address (&im->lookup_main, ia);
200         if (ia->address_length <= 31)
201           {
202             addr->as_u32 = clib_host_to_net_u32 (x->as_u32);
203             /*
204              * flip the last bit to get a different address, might be network,
205              * we don't care ...
206              */
207             addr->as_u32 ^= 1;
208             addr->as_u32 = clib_net_to_host_u32 (addr->as_u32);
209             return 1;
210           }
211       }));
212   /* *INDENT-ON* */
213   BFD_ERR ("cannot find ip4 address, no usable address found");
214   return 0;
215 }
216
217 int
218 bfd_udp_get_echo_src_ip6 (ip6_address_t * addr)
219 {
220   if (!bfd_udp_main.echo_source_is_set)
221     {
222       BFD_ERR ("cannot find ip6 address, echo source not set");
223       return 0;
224     }
225   ip_interface_address_t *ia = NULL;
226   ip6_main_t *im = &ip6_main;
227
228   /* *INDENT-OFF* */
229   foreach_ip_interface_address (
230       &im->lookup_main, ia, bfd_udp_main.echo_source_sw_if_index,
231       0 /* honor unnumbered */, ({
232         ip6_address_t *x =
233             ip_interface_address_get_address (&im->lookup_main, ia);
234         if (ia->address_length <= 127)
235           {
236             *addr = *x;
237             addr->as_u8[15] ^= 1; /* flip the last bit of the address */
238             return 1;
239           }
240       }));
241   /* *INDENT-ON* */
242   BFD_ERR ("cannot find ip6 address, no usable address found");
243   return 0;
244 }
245
246 void
247 bfd_udp_get_echo_source (int *is_set, u32 * sw_if_index,
248                          int *have_usable_ip4, ip4_address_t * ip4,
249                          int *have_usable_ip6, ip6_address_t * ip6)
250 {
251   if (bfd_udp_main.echo_source_is_set)
252     {
253       *is_set = 1;
254       *sw_if_index = bfd_udp_main.echo_source_sw_if_index;
255       *have_usable_ip4 = bfd_udp_get_echo_src_ip4 (ip4);
256       *have_usable_ip6 = bfd_udp_get_echo_src_ip6 (ip6);
257     }
258   else
259     {
260       *is_set = 0;
261     }
262 }
263
264 int
265 bfd_add_udp4_transport (vlib_main_t * vm, u32 bi, const bfd_session_t * bs,
266                         int is_echo)
267 {
268   const bfd_udp_session_t *bus = &bs->udp;
269   const bfd_udp_key_t *key = &bus->key;
270   vlib_buffer_t *b = vlib_get_buffer (vm, bi);
271
272   b->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
273   vnet_buffer (b)->ip.adj_index[VLIB_RX] = bus->adj_index;
274   vnet_buffer (b)->ip.adj_index[VLIB_TX] = bus->adj_index;
275   vnet_buffer (b)->sw_if_index[VLIB_RX] = 0;
276   vnet_buffer (b)->sw_if_index[VLIB_TX] = ~0;
277   typedef struct
278   {
279     ip4_header_t ip4;
280     udp_header_t udp;
281   } ip4_udp_headers;
282   ip4_udp_headers *headers = NULL;
283   vlib_buffer_advance (b, -sizeof (*headers));
284   headers = vlib_buffer_get_current (b);
285   clib_memset (headers, 0, sizeof (*headers));
286   headers->ip4.ip_version_and_header_length = 0x45;
287   headers->ip4.ttl = 255;
288   headers->ip4.protocol = IP_PROTOCOL_UDP;
289   headers->udp.src_port =
290     clib_host_to_net_u16 (bfd_udp_bs_idx_to_sport (bs->bs_idx));
291   if (is_echo)
292     {
293       int rv;
294       if (!(rv = bfd_udp_get_echo_src_ip4 (&headers->ip4.src_address)))
295         {
296           return rv;
297         }
298       headers->ip4.dst_address.as_u32 = key->local_addr.ip4.as_u32;
299       headers->udp.dst_port = clib_host_to_net_u16 (UDP_DST_PORT_bfd_echo4);
300     }
301   else
302     {
303       headers->ip4.src_address.as_u32 = key->local_addr.ip4.as_u32;
304       headers->ip4.dst_address.as_u32 = key->peer_addr.ip4.as_u32;
305       headers->udp.dst_port = clib_host_to_net_u16 (UDP_DST_PORT_bfd4);
306     }
307
308   /* fix ip length, checksum and udp length */
309   const u16 ip_length = vlib_buffer_length_in_chain (vm, b);
310
311   headers->ip4.length = clib_host_to_net_u16 (ip_length);
312   headers->ip4.checksum = ip4_header_checksum (&headers->ip4);
313
314   const u16 udp_length = ip_length - (sizeof (headers->ip4));
315   headers->udp.length = clib_host_to_net_u16 (udp_length);
316   return 1;
317 }
318
319 int
320 bfd_add_udp6_transport (vlib_main_t * vm, u32 bi, const bfd_session_t * bs,
321                         int is_echo)
322 {
323   const bfd_udp_session_t *bus = &bs->udp;
324   const bfd_udp_key_t *key = &bus->key;
325   vlib_buffer_t *b = vlib_get_buffer (vm, bi);
326
327   b->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
328   vnet_buffer (b)->ip.adj_index[VLIB_RX] = bus->adj_index;
329   vnet_buffer (b)->ip.adj_index[VLIB_TX] = bus->adj_index;
330   vnet_buffer (b)->sw_if_index[VLIB_RX] = 0;
331   vnet_buffer (b)->sw_if_index[VLIB_TX] = 0;
332   typedef struct
333   {
334     ip6_header_t ip6;
335     udp_header_t udp;
336   } ip6_udp_headers;
337   ip6_udp_headers *headers = NULL;
338   vlib_buffer_advance (b, -sizeof (*headers));
339   headers = vlib_buffer_get_current (b);
340   clib_memset (headers, 0, sizeof (*headers));
341   headers->ip6.ip_version_traffic_class_and_flow_label =
342     clib_host_to_net_u32 (0x6 << 28);
343   headers->ip6.hop_limit = 255;
344   headers->ip6.protocol = IP_PROTOCOL_UDP;
345   headers->udp.src_port =
346     clib_host_to_net_u16 (bfd_udp_bs_idx_to_sport (bs->bs_idx));
347   if (is_echo)
348     {
349       int rv;
350       if (!(rv = bfd_udp_get_echo_src_ip6 (&headers->ip6.src_address)))
351         {
352           return rv;
353         }
354       clib_memcpy_fast (&headers->ip6.dst_address, &key->local_addr.ip6,
355                         sizeof (headers->ip6.dst_address));
356
357       headers->udp.dst_port = clib_host_to_net_u16 (UDP_DST_PORT_bfd_echo6);
358     }
359   else
360     {
361       clib_memcpy_fast (&headers->ip6.src_address, &key->local_addr.ip6,
362                         sizeof (headers->ip6.src_address));
363       clib_memcpy_fast (&headers->ip6.dst_address, &key->peer_addr.ip6,
364                         sizeof (headers->ip6.dst_address));
365       headers->udp.dst_port = clib_host_to_net_u16 (UDP_DST_PORT_bfd6);
366     }
367
368   /* fix ip payload length and udp length */
369   const u16 udp_length =
370     vlib_buffer_length_in_chain (vm, b) - (sizeof (headers->ip6));
371   headers->udp.length = clib_host_to_net_u16 (udp_length);
372   headers->ip6.payload_length = headers->udp.length;
373
374   /* IPv6 UDP checksum is mandatory */
375   int bogus = 0;
376   headers->udp.checksum =
377     ip6_tcp_udp_icmp_compute_checksum (vm, b, &headers->ip6, &bogus);
378   ASSERT (bogus == 0);
379   if (headers->udp.checksum == 0)
380     {
381       headers->udp.checksum = 0xffff;
382     }
383   return 1;
384 }
385
386 static void
387 bfd_create_frame_to_next_node (vlib_main_t *vm, bfd_main_t *bm,
388                                const bfd_session_t *bs, u32 bi, u32 next_node,
389                                vlib_combined_counter_main_t *tx_counter)
390 {
391   vlib_frame_t *f = vlib_get_frame_to_node (vm, next_node);
392   u32 *to_next = vlib_frame_vector_args (f);
393   to_next[0] = bi;
394   f->n_vectors = 1;
395   vlib_put_frame_to_node (vm, next_node, f);
396   vlib_buffer_t *b = vlib_get_buffer (vm, bi);
397   vlib_increment_combined_counter (tx_counter, vm->thread_index, bs->bs_idx, 1,
398                                    vlib_buffer_length_in_chain (vm, b));
399 }
400
401 int
402 bfd_udp_calc_next_node (const struct bfd_session_s *bs, u32 * next_node)
403 {
404   vnet_main_t *vnm = vnet_get_main ();
405   const bfd_udp_session_t *bus = &bs->udp;
406   ip_adjacency_t *adj = adj_get (bus->adj_index);
407
408   /* don't try to send the buffer if the interface is not up */
409   if (!vnet_sw_interface_is_up (vnm, bus->key.sw_if_index))
410     return 0;
411
412   switch (adj->lookup_next_index)
413     {
414     case IP_LOOKUP_NEXT_ARP:
415       switch (bs->transport)
416         {
417         case BFD_TRANSPORT_UDP4:
418           *next_node = bfd_udp_main.ip4_arp_idx;
419           return 1;
420         case BFD_TRANSPORT_UDP6:
421           *next_node = bfd_udp_main.ip6_ndp_idx;
422           return 1;
423         }
424       break;
425     case IP_LOOKUP_NEXT_REWRITE:
426       switch (bs->transport)
427         {
428         case BFD_TRANSPORT_UDP4:
429           *next_node = bfd_udp_main.ip4_rewrite_idx;
430           return 1;
431         case BFD_TRANSPORT_UDP6:
432           *next_node = bfd_udp_main.ip6_rewrite_idx;
433           return 1;
434         }
435       break;
436     case IP_LOOKUP_NEXT_MIDCHAIN:
437       switch (bs->transport)
438         {
439         case BFD_TRANSPORT_UDP4:
440           *next_node = bfd_udp_main.ip4_midchain_idx;
441           return 1;
442         case BFD_TRANSPORT_UDP6:
443           *next_node = bfd_udp_main.ip6_midchain_idx;
444           return 1;
445         }
446       break;
447     default:
448       /* drop */
449       break;
450     }
451   return 0;
452 }
453
454 int
455 bfd_transport_udp4 (vlib_main_t *vm, u32 bi, const struct bfd_session_s *bs,
456                     int is_echo)
457 {
458   u32 next_node;
459   int rv = bfd_udp_calc_next_node (bs, &next_node);
460   bfd_main_t *bm = bfd_udp_main.bfd_main;
461   if (rv)
462     {
463       bfd_create_frame_to_next_node (vm, bm, bs, bi, next_node,
464                                      is_echo ? &bm->tx_echo_counter :
465                                                &bm->tx_counter);
466     }
467   return rv;
468 }
469
470 int
471 bfd_transport_udp6 (vlib_main_t *vm, u32 bi, const struct bfd_session_s *bs,
472                     int is_echo)
473 {
474   u32 next_node;
475   int rv = bfd_udp_calc_next_node (bs, &next_node);
476   bfd_main_t *bm = bfd_udp_main.bfd_main;
477   if (rv)
478     {
479       bfd_create_frame_to_next_node (
480         vm, bfd_udp_main.bfd_main, bs, bi, next_node,
481         is_echo ? &bm->tx_echo_counter : &bm->tx_counter);
482     }
483   return 1;
484 }
485
486 static bfd_session_t *
487 bfd_lookup_session (bfd_udp_main_t * bum, const bfd_udp_key_t * key)
488 {
489   uword *p = mhash_get (&bum->bfd_session_idx_by_bfd_key, key);
490   if (p)
491     {
492       return bfd_find_session_by_idx (bum->bfd_main, *p);
493     }
494   return 0;
495 }
496
497 static void
498 bfd_udp_key_init (bfd_udp_key_t * key, u32 sw_if_index,
499                   const ip46_address_t * local_addr,
500                   const ip46_address_t * peer_addr)
501 {
502   clib_memset (key, 0, sizeof (*key));
503   key->sw_if_index = sw_if_index;
504   key->local_addr.as_u64[0] = local_addr->as_u64[0];
505   key->local_addr.as_u64[1] = local_addr->as_u64[1];
506   key->peer_addr.as_u64[0] = peer_addr->as_u64[0];
507   key->peer_addr.as_u64[1] = peer_addr->as_u64[1];
508 }
509
510 static vnet_api_error_t
511 bfd_udp_add_session_internal (vlib_main_t * vm, bfd_udp_main_t * bum,
512                               u32 sw_if_index, u32 desired_min_tx_usec,
513                               u32 required_min_rx_usec, u8 detect_mult,
514                               const ip46_address_t * local_addr,
515                               const ip46_address_t * peer_addr,
516                               bfd_session_t ** bs_out)
517 {
518   /* get a pool entry and if we end up not needing it, give it back */
519   bfd_transport_e t = BFD_TRANSPORT_UDP4;
520   if (!ip46_address_is_ip4 (local_addr))
521     {
522       t = BFD_TRANSPORT_UDP6;
523     }
524   bfd_session_t *bs = bfd_get_session (bum->bfd_main, t);
525   if (!bs)
526     {
527       return VNET_API_ERROR_BFD_EAGAIN;
528     }
529   bfd_udp_session_t *bus = &bs->udp;
530   clib_memset (bus, 0, sizeof (*bus));
531   bus->adj_index = ADJ_INDEX_INVALID;
532   bfd_udp_key_t *key = &bus->key;
533   bfd_udp_key_init (key, sw_if_index, local_addr, peer_addr);
534   const bfd_session_t *tmp = bfd_lookup_session (bum, key);
535   if (tmp)
536     {
537       vlib_log_err (bum->log_class,
538                     "duplicate bfd-udp session, existing bs_idx=%d",
539                     tmp->bs_idx);
540       bfd_put_session (bum->bfd_main, bs);
541       return VNET_API_ERROR_BFD_EEXIST;
542     }
543   mhash_set (&bum->bfd_session_idx_by_bfd_key, key, bs->bs_idx, NULL);
544   BFD_DBG ("session created, bs_idx=%u, sw_if_index=%d, local=%U, peer=%U",
545            bs->bs_idx, key->sw_if_index, format_ip46_address,
546            &key->local_addr, IP46_TYPE_ANY, format_ip46_address,
547            &key->peer_addr, IP46_TYPE_ANY);
548   vlib_log_info (bum->log_class, "create BFD session: %U",
549                  format_bfd_session, bs);
550   const ip46_address_t *peer =
551     (vnet_sw_interface_is_p2p (vnet_get_main (), key->sw_if_index) ?
552        &zero_addr :
553        &key->peer_addr);
554   if (BFD_TRANSPORT_UDP4 == t)
555     {
556       bus->adj_index = adj_nbr_add_or_lock (FIB_PROTOCOL_IP4, VNET_LINK_IP4,
557                                             peer, key->sw_if_index);
558       BFD_DBG ("adj_nbr_add_or_lock(FIB_PROTOCOL_IP4, VNET_LINK_IP4, %U, %d) "
559                "returns %d",
560                format_ip46_address, peer, IP46_TYPE_ANY, key->sw_if_index,
561                bus->adj_index);
562       ++bum->udp4_sessions_count;
563       bfd_udp_update_stat_segment_entry (
564         bum->udp4_sessions_count_stat_seg_entry, bum->udp4_sessions_count);
565       if (1 == bum->udp4_sessions_count)
566         {
567           udp_register_dst_port (vm, UDP_DST_PORT_bfd4,
568                                  bfd_udp4_input_node.index, 1);
569           udp_register_dst_port (vm, UDP_DST_PORT_bfd_echo4,
570                                  bfd_udp_echo4_input_node.index, 1);
571         }
572     }
573   else
574     {
575       bus->adj_index = adj_nbr_add_or_lock (FIB_PROTOCOL_IP6, VNET_LINK_IP6,
576                                             peer, key->sw_if_index);
577       BFD_DBG ("adj_nbr_add_or_lock(FIB_PROTOCOL_IP6, VNET_LINK_IP6, %U, %d) "
578                "returns %d",
579                format_ip46_address, peer, IP46_TYPE_ANY, key->sw_if_index,
580                bus->adj_index);
581       ++bum->udp6_sessions_count;
582       bfd_udp_update_stat_segment_entry (
583         bum->udp6_sessions_count_stat_seg_entry, bum->udp6_sessions_count);
584       if (1 == bum->udp6_sessions_count)
585         {
586           udp_register_dst_port (vm, UDP_DST_PORT_bfd6,
587                                  bfd_udp6_input_node.index, 0);
588           udp_register_dst_port (vm, UDP_DST_PORT_bfd_echo6,
589                                  bfd_udp_echo6_input_node.index, 0);
590         }
591     }
592   *bs_out = bs;
593   return bfd_session_set_params (bum->bfd_main, bs, desired_min_tx_usec,
594                                  required_min_rx_usec, detect_mult);
595 }
596
597 static vnet_api_error_t
598 bfd_udp_validate_api_input (u32 sw_if_index,
599                             const ip46_address_t * local_addr,
600                             const ip46_address_t * peer_addr)
601 {
602   bfd_udp_main_t *bum = &bfd_udp_main;
603   vnet_sw_interface_t *sw_if =
604     vnet_get_sw_interface_or_null (bfd_udp_main.vnet_main, sw_if_index);
605   if (!sw_if)
606     {
607       vlib_log_err (bum->log_class,
608                     "got NULL sw_if when getting interface by index %u",
609                     sw_if_index);
610       return VNET_API_ERROR_INVALID_SW_IF_INDEX;
611     }
612   if (ip46_address_is_ip4 (local_addr))
613     {
614       if (!ip46_address_is_ip4 (peer_addr))
615         {
616           vlib_log_err (bum->log_class,
617                         "IP family mismatch (local is ipv4, peer is ipv6)");
618           return VNET_API_ERROR_INVALID_ARGUMENT;
619         }
620     }
621   else
622     {
623       if (ip46_address_is_ip4 (peer_addr))
624         {
625           vlib_log_err (bum->log_class,
626                         "IP family mismatch (local is ipv6, peer is ipv4)");
627           return VNET_API_ERROR_INVALID_ARGUMENT;
628         }
629     }
630
631   return 0;
632 }
633
634 static vnet_api_error_t
635 bfd_udp_find_session_by_api_input (u32 sw_if_index,
636                                    const ip46_address_t * local_addr,
637                                    const ip46_address_t * peer_addr,
638                                    bfd_session_t ** bs_out)
639 {
640   vnet_api_error_t rv =
641     bfd_udp_validate_api_input (sw_if_index, local_addr, peer_addr);
642   if (!rv)
643     {
644       bfd_udp_main_t *bum = &bfd_udp_main;
645       bfd_udp_key_t key;
646       bfd_udp_key_init (&key, sw_if_index, local_addr, peer_addr);
647       bfd_session_t *bs = bfd_lookup_session (bum, &key);
648       if (bs)
649         {
650           *bs_out = bs;
651         }
652       else
653         {
654           vlib_log_err (bum->log_class,
655                         "BFD session not found, sw_if_index=%u, local=%U, peer=%U",
656                         sw_if_index, format_ip46_address, local_addr,
657                         IP46_TYPE_ANY, format_ip46_address, peer_addr,
658                         IP46_TYPE_ANY);
659           return VNET_API_ERROR_BFD_ENOENT;
660         }
661     }
662   return rv;
663 }
664
665 static vnet_api_error_t
666 bfd_api_verify_common (u32 sw_if_index, u32 desired_min_tx_usec,
667                        u8 detect_mult, const ip46_address_t *local_addr,
668                        const ip46_address_t *peer_addr)
669 {
670   bfd_udp_main_t *bum = &bfd_udp_main;
671   vnet_api_error_t rv =
672     bfd_udp_validate_api_input (sw_if_index, local_addr, peer_addr);
673   if (rv)
674     {
675       return rv;
676     }
677   if (detect_mult < 1)
678     {
679       vlib_log_err (bum->log_class, "detect_mult < 1");
680       return VNET_API_ERROR_INVALID_ARGUMENT;
681     }
682   if (desired_min_tx_usec < 1)
683     {
684       vlib_log_err (bum->log_class, "desired_min_tx_usec < 1");
685       return VNET_API_ERROR_INVALID_ARGUMENT;
686     }
687   return 0;
688 }
689
690 static void
691 bfd_udp_del_session_internal (vlib_main_t * vm, bfd_session_t * bs)
692 {
693   bfd_udp_main_t *bum = &bfd_udp_main;
694   BFD_DBG ("free bfd-udp session, bs_idx=%d", bs->bs_idx);
695   bfd_session_stop (bum->bfd_main, bs);
696   mhash_unset (&bum->bfd_session_idx_by_bfd_key, &bs->udp.key, NULL);
697   adj_unlock (bs->udp.adj_index);
698   switch (bs->transport)
699     {
700     case BFD_TRANSPORT_UDP4:
701       --bum->udp4_sessions_count;
702       bfd_udp_update_stat_segment_entry (
703         bum->udp4_sessions_count_stat_seg_entry, bum->udp4_sessions_count);
704       if (!bum->udp4_sessions_count)
705         {
706           udp_unregister_dst_port (vm, UDP_DST_PORT_bfd4, 1);
707           udp_unregister_dst_port (vm, UDP_DST_PORT_bfd_echo4, 1);
708         }
709       break;
710     case BFD_TRANSPORT_UDP6:
711       --bum->udp6_sessions_count;
712       bfd_udp_update_stat_segment_entry (
713         bum->udp6_sessions_count_stat_seg_entry, bum->udp6_sessions_count);
714       if (!bum->udp6_sessions_count)
715         {
716           udp_unregister_dst_port (vm, UDP_DST_PORT_bfd6, 0);
717           udp_unregister_dst_port (vm, UDP_DST_PORT_bfd_echo6, 0);
718         }
719       break;
720     }
721   bfd_put_session (bum->bfd_main, bs);
722 }
723
724 static vnet_api_error_t
725 bfd_udp_add_and_start_session (u32 sw_if_index,
726                                const ip46_address_t *local_addr,
727                                const ip46_address_t *peer_addr,
728                                u32 desired_min_tx_usec,
729                                u32 required_min_rx_usec, u8 detect_mult,
730                                u8 is_authenticated, u32 conf_key_id,
731                                u8 bfd_key_id)
732 {
733   bfd_session_t *bs = NULL;
734   vnet_api_error_t rv;
735
736   rv = bfd_udp_add_session_internal (
737     vlib_get_main (), &bfd_udp_main, sw_if_index, desired_min_tx_usec,
738     required_min_rx_usec, detect_mult, local_addr, peer_addr, &bs);
739
740   if (!rv && is_authenticated)
741     {
742       rv = bfd_auth_activate (bs, conf_key_id, bfd_key_id,
743                               0 /* is not delayed */);
744       if (rv)
745         {
746           bfd_udp_del_session_internal (vlib_get_main (), bs);
747         }
748     }
749   if (!rv)
750     {
751       bfd_session_start (bfd_udp_main.bfd_main, bs);
752     }
753
754   return rv;
755 }
756
757 vnet_api_error_t
758 bfd_udp_add_session (u32 sw_if_index, const ip46_address_t * local_addr,
759                      const ip46_address_t * peer_addr,
760                      u32 desired_min_tx_usec, u32 required_min_rx_usec,
761                      u8 detect_mult, u8 is_authenticated, u32 conf_key_id,
762                      u8 bfd_key_id)
763 {
764   bfd_main_t *bm = &bfd_main;
765   bfd_lock (bm);
766
767   vnet_api_error_t rv = bfd_api_verify_common (
768     sw_if_index, desired_min_tx_usec, detect_mult, local_addr, peer_addr);
769
770   if (!rv)
771     rv = bfd_udp_add_and_start_session (
772       sw_if_index, local_addr, peer_addr, desired_min_tx_usec,
773       required_min_rx_usec, detect_mult, is_authenticated, conf_key_id,
774       bfd_key_id);
775
776   bfd_unlock (bm);
777   return rv;
778 }
779
780 vnet_api_error_t
781 bfd_udp_upd_session (u32 sw_if_index, const ip46_address_t *local_addr,
782                      const ip46_address_t *peer_addr, u32 desired_min_tx_usec,
783                      u32 required_min_rx_usec, u8 detect_mult,
784                      u8 is_authenticated, u32 conf_key_id, u8 bfd_key_id)
785 {
786   bfd_main_t *bm = &bfd_main;
787   bfd_lock (bm);
788
789   vnet_api_error_t rv = bfd_api_verify_common (
790     sw_if_index, desired_min_tx_usec, detect_mult, local_addr, peer_addr);
791   if (!rv)
792     {
793       bfd_session_t *bs = NULL;
794
795       rv = bfd_udp_find_session_by_api_input (sw_if_index, local_addr,
796                                               peer_addr, &bs);
797       if (VNET_API_ERROR_BFD_ENOENT == rv)
798         rv = bfd_udp_add_and_start_session (
799           sw_if_index, local_addr, peer_addr, desired_min_tx_usec,
800           required_min_rx_usec, detect_mult, is_authenticated, conf_key_id,
801           bfd_key_id);
802       else
803         rv = bfd_session_set_params (bfd_udp_main.bfd_main, bs,
804                                      desired_min_tx_usec, required_min_rx_usec,
805                                      detect_mult);
806     }
807
808   bfd_unlock (bm);
809   return rv;
810 }
811
812 vnet_api_error_t
813 bfd_udp_mod_session (u32 sw_if_index, const ip46_address_t *local_addr,
814                      const ip46_address_t *peer_addr, u32 desired_min_tx_usec,
815                      u32 required_min_rx_usec, u8 detect_mult)
816 {
817   bfd_session_t *bs = NULL;
818   bfd_main_t *bm = &bfd_main;
819   vnet_api_error_t error;
820   bfd_lock (bm);
821   vnet_api_error_t rv =
822     bfd_udp_find_session_by_api_input (sw_if_index, local_addr, peer_addr,
823                                        &bs);
824   if (rv)
825     {
826       bfd_unlock (bm);
827       return rv;
828     }
829
830   error = bfd_session_set_params (bfd_udp_main.bfd_main, bs,
831                                   desired_min_tx_usec, required_min_rx_usec,
832                                   detect_mult);
833   bfd_unlock (bm);
834   return error;
835 }
836
837 vnet_api_error_t
838 bfd_udp_del_session (u32 sw_if_index,
839                      const ip46_address_t * local_addr,
840                      const ip46_address_t * peer_addr)
841 {
842   bfd_session_t *bs = NULL;
843   bfd_main_t *bm = &bfd_main;
844   bfd_lock (bm);
845   vnet_api_error_t rv =
846     bfd_udp_find_session_by_api_input (sw_if_index, local_addr, peer_addr,
847                                        &bs);
848   if (rv)
849     {
850       bfd_unlock (bm);
851       return rv;
852     }
853   bfd_udp_del_session_internal (vlib_get_main (), bs);
854   bfd_unlock (bm);
855   return 0;
856 }
857
858 vnet_api_error_t
859 bfd_udp_session_set_flags (vlib_main_t * vm, u32 sw_if_index,
860                            const ip46_address_t * local_addr,
861                            const ip46_address_t * peer_addr, u8 admin_up_down)
862 {
863   bfd_session_t *bs = NULL;
864   bfd_main_t *bm = &bfd_main;
865   bfd_lock (bm);
866   vnet_api_error_t rv =
867     bfd_udp_find_session_by_api_input (sw_if_index, local_addr, peer_addr,
868                                        &bs);
869   if (rv)
870     {
871       bfd_unlock (bm);
872       return rv;
873     }
874   bfd_session_set_flags (vm, bs, admin_up_down);
875   bfd_unlock (bm);
876   return 0;
877 }
878
879 vnet_api_error_t
880 bfd_udp_auth_activate (u32 sw_if_index,
881                        const ip46_address_t * local_addr,
882                        const ip46_address_t * peer_addr,
883                        u32 conf_key_id, u8 key_id, u8 is_delayed)
884 {
885   bfd_main_t *bm = &bfd_main;
886   bfd_lock (bm);
887   vnet_api_error_t error;
888
889   bfd_session_t *bs = NULL;
890   vnet_api_error_t rv =
891     bfd_udp_find_session_by_api_input (sw_if_index, local_addr, peer_addr,
892                                        &bs);
893   if (rv)
894     {
895       bfd_unlock (bm);
896       return rv;
897     }
898   error = bfd_auth_activate (bs, conf_key_id, key_id, is_delayed);
899   bfd_unlock (bm);
900   return error;
901 }
902
903 vnet_api_error_t
904 bfd_udp_auth_deactivate (u32 sw_if_index,
905                          const ip46_address_t * local_addr,
906                          const ip46_address_t * peer_addr, u8 is_delayed)
907 {
908   bfd_main_t *bm = &bfd_main;
909   vnet_api_error_t error;
910   bfd_lock (bm);
911   bfd_session_t *bs = NULL;
912   vnet_api_error_t rv =
913     bfd_udp_find_session_by_api_input (sw_if_index, local_addr, peer_addr,
914                                        &bs);
915   if (rv)
916     {
917       bfd_unlock (bm);
918       return rv;
919     }
920   error = bfd_auth_deactivate (bs, is_delayed);
921   bfd_unlock (bm);
922   return error;
923 }
924
925 typedef enum
926 {
927   BFD_UDP_INPUT_NEXT_NORMAL,
928   BFD_UDP_INPUT_NEXT_REPLY_ARP,
929   BFD_UDP_INPUT_NEXT_REPLY_REWRITE,
930   BFD_UDP_INPUT_NEXT_REPLY_MIDCHAIN,
931   BFD_UDP_INPUT_N_NEXT,
932 } bfd_udp_input_next_t;
933
934 typedef enum
935 {
936   BFD_UDP_ECHO_INPUT_NEXT_NORMAL,
937   BFD_UDP_ECHO_INPUT_NEXT_REPLY_ARP,
938   BFD_UDP_ECHO_INPUT_NEXT_REPLY_REWRITE,
939   BFD_UDP_ECHO_INPUT_N_NEXT,
940 } bfd_udp_echo_input_next_t;
941
942 static_always_inline vl_counter_bfd_udp_enum_t
943 bfd_error_to_udp (bfd_error_t e)
944 {
945   /* The UDP error is a super set of the proto independent errors */
946   return ((vl_counter_bfd_udp_enum_t) e);
947 }
948
949 static void
950 bfd_udp4_find_headers (vlib_buffer_t * b, ip4_header_t ** ip4,
951                        udp_header_t ** udp)
952 {
953   /* sanity check first */
954   const i32 start = vnet_buffer (b)->l3_hdr_offset;
955   if (start < -(signed) sizeof (b->pre_data))
956     {
957       BFD_ERR ("Start of ip header is before pre_data, ignoring");
958       *ip4 = NULL;
959       *udp = NULL;
960       return;
961     }
962   *ip4 = (ip4_header_t *) (b->data + start);
963   if ((u8 *) * ip4 > (u8 *) vlib_buffer_get_current (b))
964     {
965       BFD_ERR ("Start of ip header is beyond current data, ignoring");
966       *ip4 = NULL;
967       *udp = NULL;
968       return;
969     }
970   *udp = (udp_header_t *) ((*ip4) + 1);
971 }
972
973 static vl_counter_bfd_udp_enum_t
974 bfd_udp4_verify_transport (const ip4_header_t *ip4, const udp_header_t *udp,
975                            const bfd_session_t *bs)
976 {
977   const bfd_udp_session_t *bus = &bs->udp;
978   const bfd_udp_key_t *key = &bus->key;
979   if (ip4->src_address.as_u32 != key->peer_addr.ip4.as_u32)
980     {
981       BFD_ERR ("IPv4 src addr mismatch, got %U, expected %U",
982                format_ip4_address, ip4->src_address.as_u8, format_ip4_address,
983                key->peer_addr.ip4.as_u8);
984       return BFD_UDP_ERROR_SRC_MISMATCH;
985     }
986   if (ip4->dst_address.as_u32 != key->local_addr.ip4.as_u32)
987     {
988       BFD_ERR ("IPv4 dst addr mismatch, got %U, expected %U",
989                format_ip4_address, ip4->dst_address.as_u8, format_ip4_address,
990                key->local_addr.ip4.as_u8);
991       return BFD_UDP_ERROR_DST_MISMATCH;
992     }
993   const u8 expected_ttl = 255;
994   if (ip4->ttl != expected_ttl)
995     {
996       BFD_ERR ("IPv4 unexpected TTL value %u, expected %u", ip4->ttl,
997                expected_ttl);
998       return BFD_UDP_ERROR_TTL;
999     }
1000   if (clib_net_to_host_u16 (udp->src_port) < 49152)
1001     {
1002       BFD_ERR ("Invalid UDP src port %u, out of range <49152,65535>",
1003                udp->src_port);
1004     }
1005   return BFD_UDP_ERROR_NONE;
1006 }
1007
1008 typedef struct
1009 {
1010   u32 bs_idx;
1011   bfd_pkt_t pkt;
1012 } bfd_rpc_update_t;
1013
1014 static bfd_error_t
1015 bfd_rpc_update_session (vlib_main_t *vm, u32 bs_idx, const bfd_pkt_t *pkt)
1016 {
1017   bfd_main_t *bm = &bfd_main;
1018   bfd_error_t err;
1019   bfd_lock (bm);
1020   err = bfd_consume_pkt (vm, bm, pkt, bs_idx);
1021   bfd_unlock (bm);
1022
1023   return err;
1024 }
1025
1026 static vl_counter_bfd_udp_enum_t
1027 bfd_udp4_scan (vlib_main_t *vm, vlib_buffer_t *b, bfd_session_t **bs_out)
1028 {
1029   const bfd_pkt_t *pkt = vlib_buffer_get_current (b);
1030   if (sizeof (*pkt) > b->current_length)
1031     {
1032       BFD_ERR
1033         ("Payload size %d too small to hold bfd packet of minimum size %d",
1034          b->current_length, sizeof (*pkt));
1035       return BFD_UDP_ERROR_BAD;
1036     }
1037   ip4_header_t *ip4;
1038   udp_header_t *udp;
1039   bfd_udp4_find_headers (b, &ip4, &udp);
1040   if (!ip4 || !udp)
1041     {
1042       BFD_ERR ("Couldn't find ip4 or udp header");
1043       return BFD_UDP_ERROR_BAD;
1044     }
1045   const u32 udp_payload_length = udp->length - sizeof (*udp);
1046   if (pkt->head.length > udp_payload_length)
1047     {
1048       BFD_ERR
1049         ("BFD packet length is larger than udp payload length (%u > %u)",
1050          pkt->head.length, udp_payload_length);
1051       return BFD_UDP_ERROR_LENGTH;
1052     }
1053   vl_counter_bfd_udp_enum_t err;
1054   if (BFD_UDP_ERROR_NONE !=
1055       (err = bfd_error_to_udp (bfd_verify_pkt_common (pkt))))
1056     {
1057       return err;
1058     }
1059   bfd_session_t *bs = NULL;
1060   if (pkt->your_disc)
1061     {
1062       BFD_DBG ("Looking up BFD session using discriminator %u",
1063                pkt->your_disc);
1064       bs = bfd_find_session_by_disc (bfd_udp_main.bfd_main, pkt->your_disc);
1065     }
1066   else
1067     {
1068       bfd_udp_key_t key;
1069       clib_memset (&key, 0, sizeof (key));
1070       key.sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
1071       key.local_addr.ip4.as_u32 = ip4->dst_address.as_u32;
1072       key.peer_addr.ip4.as_u32 = ip4->src_address.as_u32;
1073       BFD_DBG ("Looking up BFD session using key (sw_if_index=%u, local=%U, "
1074                "peer=%U)",
1075                key.sw_if_index, format_ip4_address, key.local_addr.ip4.as_u8,
1076                format_ip4_address, key.peer_addr.ip4.as_u8);
1077       bs = bfd_lookup_session (&bfd_udp_main, &key);
1078     }
1079   if (!bs)
1080     {
1081       BFD_ERR ("BFD session lookup failed - no session matches BFD pkt");
1082       return BFD_UDP_ERROR_NO_SESSION;
1083     }
1084   BFD_DBG ("BFD session found, bs_idx=%u", bs->bs_idx);
1085   if (!bfd_verify_pkt_auth (vm, pkt, b->current_length, bs))
1086     {
1087       BFD_ERR ("Packet verification failed, dropping packet");
1088       return BFD_UDP_ERROR_FAILED_VERIFICATION;
1089     }
1090   if (BFD_UDP_ERROR_NONE != (err = bfd_udp4_verify_transport (ip4, udp, bs)))
1091     {
1092       return err;
1093     }
1094   err = bfd_error_to_udp (bfd_rpc_update_session (vm, bs->bs_idx, pkt));
1095   *bs_out = bs;
1096   return err;
1097 }
1098
1099 static void
1100 bfd_udp6_find_headers (vlib_buffer_t * b, ip6_header_t ** ip6,
1101                        udp_header_t ** udp)
1102 {
1103   /* sanity check first */
1104   const i32 start = vnet_buffer (b)->l3_hdr_offset;
1105   if (start < -(signed) sizeof (b->pre_data))
1106     {
1107       BFD_ERR ("Start of ip header is before pre_data, ignoring");
1108       *ip6 = NULL;
1109       *udp = NULL;
1110       return;
1111     }
1112   *ip6 = (ip6_header_t *) (b->data + start);
1113   if ((u8 *) * ip6 > (u8 *) vlib_buffer_get_current (b))
1114     {
1115       BFD_ERR ("Start of ip header is beyond current data, ignoring");
1116       *ip6 = NULL;
1117       *udp = NULL;
1118       return;
1119     }
1120   if ((*ip6)->protocol != IP_PROTOCOL_UDP)
1121     {
1122       BFD_ERR ("Unexpected protocol in IPv6 header '%u', expected '%u' (== "
1123                "IP_PROTOCOL_UDP)", (*ip6)->protocol, IP_PROTOCOL_UDP);
1124       *ip6 = NULL;
1125       *udp = NULL;
1126       return;
1127     }
1128   *udp = (udp_header_t *) ((*ip6) + 1);
1129 }
1130
1131 static vl_counter_bfd_udp_enum_t
1132 bfd_udp6_verify_transport (const ip6_header_t *ip6, const udp_header_t *udp,
1133                            const bfd_session_t *bs)
1134 {
1135   const bfd_udp_session_t *bus = &bs->udp;
1136   const bfd_udp_key_t *key = &bus->key;
1137   if (ip6->src_address.as_u64[0] != key->peer_addr.ip6.as_u64[0] &&
1138       ip6->src_address.as_u64[1] != key->peer_addr.ip6.as_u64[1])
1139     {
1140       BFD_ERR ("IP src addr mismatch, got %U, expected %U",
1141                format_ip6_address, ip6, format_ip6_address,
1142                &key->peer_addr.ip6);
1143       return BFD_UDP_ERROR_SRC_MISMATCH;
1144     }
1145   if (ip6->dst_address.as_u64[0] != key->local_addr.ip6.as_u64[0] &&
1146       ip6->dst_address.as_u64[1] != key->local_addr.ip6.as_u64[1])
1147     {
1148       BFD_ERR ("IP dst addr mismatch, got %U, expected %U",
1149                format_ip6_address, ip6, format_ip6_address,
1150                &key->local_addr.ip6);
1151       return BFD_UDP_ERROR_DST_MISMATCH;
1152     }
1153   const u8 expected_hop_limit = 255;
1154   if (ip6->hop_limit != expected_hop_limit)
1155     {
1156       BFD_ERR ("IPv6 unexpected hop-limit value %u, expected %u",
1157                ip6->hop_limit, expected_hop_limit);
1158       return BFD_UDP_ERROR_TTL;
1159     }
1160   if (clib_net_to_host_u16 (udp->src_port) < 49152)
1161     {
1162       BFD_ERR ("Invalid UDP src port %u, out of range <49152,65535>",
1163                udp->src_port);
1164     }
1165   return BFD_UDP_ERROR_NONE;
1166 }
1167
1168 static vl_counter_bfd_udp_enum_t
1169 bfd_udp6_scan (vlib_main_t *vm, vlib_buffer_t *b, bfd_session_t **bs_out)
1170 {
1171   const bfd_pkt_t *pkt = vlib_buffer_get_current (b);
1172   if (sizeof (*pkt) > b->current_length)
1173     {
1174       BFD_ERR
1175         ("Payload size %d too small to hold bfd packet of minimum size %d",
1176          b->current_length, sizeof (*pkt));
1177       return BFD_UDP_ERROR_BAD;
1178     }
1179   ip6_header_t *ip6;
1180   udp_header_t *udp;
1181   bfd_udp6_find_headers (b, &ip6, &udp);
1182   if (!ip6 || !udp)
1183     {
1184       BFD_ERR ("Couldn't find ip6 or udp header");
1185       return BFD_UDP_ERROR_BAD;
1186     }
1187   const u32 udp_payload_length = udp->length - sizeof (*udp);
1188   if (pkt->head.length > udp_payload_length)
1189     {
1190       BFD_ERR
1191         ("BFD packet length is larger than udp payload length (%u > %u)",
1192          pkt->head.length, udp_payload_length);
1193       return BFD_UDP_ERROR_BAD;
1194     }
1195   vl_counter_bfd_udp_enum_t err;
1196   if (BFD_UDP_ERROR_NONE !=
1197       (err = bfd_error_to_udp (bfd_verify_pkt_common (pkt))))
1198     {
1199       return err;
1200     }
1201   bfd_session_t *bs = NULL;
1202   if (pkt->your_disc)
1203     {
1204       BFD_DBG ("Looking up BFD session using discriminator %u",
1205                pkt->your_disc);
1206       bs = bfd_find_session_by_disc (bfd_udp_main.bfd_main, pkt->your_disc);
1207     }
1208   else
1209     {
1210       bfd_udp_key_t key;
1211       clib_memset (&key, 0, sizeof (key));
1212       key.sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
1213       key.local_addr.ip6.as_u64[0] = ip6->dst_address.as_u64[0];
1214       key.local_addr.ip6.as_u64[1] = ip6->dst_address.as_u64[1];
1215       key.peer_addr.ip6.as_u64[0] = ip6->src_address.as_u64[0];
1216       key.peer_addr.ip6.as_u64[1] = ip6->src_address.as_u64[1];
1217       BFD_DBG ("Looking up BFD session using key (sw_if_index=%u, local=%U, "
1218                "peer=%U)",
1219                key.sw_if_index, format_ip6_address, &key.local_addr,
1220                format_ip6_address, &key.peer_addr);
1221       bs = bfd_lookup_session (&bfd_udp_main, &key);
1222     }
1223   if (!bs)
1224     {
1225       BFD_ERR ("BFD session lookup failed - no session matches BFD pkt");
1226       return BFD_UDP_ERROR_NO_SESSION;
1227     }
1228   BFD_DBG ("BFD session found, bs_idx=%u", bs->bs_idx);
1229   if (!bfd_verify_pkt_auth (vm, pkt, b->current_length, bs))
1230     {
1231       BFD_ERR ("Packet verification failed, dropping packet");
1232       return BFD_UDP_ERROR_FAILED_VERIFICATION;
1233     }
1234   if (BFD_UDP_ERROR_NONE != (err = bfd_udp6_verify_transport (ip6, udp, bs)))
1235     {
1236       return err;
1237     }
1238   err = bfd_error_to_udp (bfd_rpc_update_session (vm, bs->bs_idx, pkt));
1239   *bs_out = bs;
1240   return err;
1241 }
1242
1243 /*
1244  * Process a frame of bfd packets
1245  * Expect 1 packet / frame
1246  */
1247 static uword
1248 bfd_udp_input (vlib_main_t * vm, vlib_node_runtime_t * rt,
1249                vlib_frame_t * f, int is_ipv6)
1250 {
1251   u32 n_left_from, *from;
1252   bfd_input_trace_t *t0;
1253   bfd_main_t *bm = &bfd_main;
1254
1255   from = vlib_frame_vector_args (f);    /* array of buffer indices */
1256   n_left_from = f->n_vectors;   /* number of buffer indices */
1257
1258   while (n_left_from > 0)
1259     {
1260       u32 bi0;
1261       vlib_buffer_t *b0;
1262       u32 next0, error0;
1263
1264       bi0 = from[0];
1265       b0 = vlib_get_buffer (vm, bi0);
1266
1267       bfd_session_t *bs = NULL;
1268
1269       /* If this pkt is traced, snapshot the data */
1270       if (b0->flags & VLIB_BUFFER_IS_TRACED)
1271         {
1272           u64 len;
1273           t0 = vlib_add_trace (vm, rt, b0, sizeof (*t0));
1274           len = (b0->current_length < sizeof (t0->data)) ? b0->current_length
1275             : sizeof (t0->data);
1276           t0->len = len;
1277           clib_memcpy_fast (t0->data, vlib_buffer_get_current (b0), len);
1278         }
1279
1280       /* scan this bfd pkt. error0 is the counter index to bmp */
1281       bfd_lock (bm);
1282       if (is_ipv6)
1283         {
1284           error0 = bfd_udp6_scan (vm, b0, &bs);
1285         }
1286       else
1287         {
1288           error0 = bfd_udp4_scan (vm, b0, &bs);
1289         }
1290       b0->error = rt->errors[error0];
1291
1292       next0 = BFD_UDP_INPUT_NEXT_NORMAL;
1293       if (BFD_UDP_ERROR_NONE == error0)
1294         {
1295           vlib_increment_combined_counter (
1296             &bm->rx_counter, vm->thread_index, bs->bs_idx, 1,
1297             vlib_buffer_length_in_chain (vm, b0));
1298           /*
1299            *  if everything went fine, check for poll bit, if present, re-use
1300            *  the buffer and based on (now updated) session parameters, send
1301            *  the final packet back
1302            */
1303           const bfd_pkt_t *pkt = vlib_buffer_get_current (b0);
1304           if (bfd_pkt_get_poll (pkt))
1305             {
1306               b0->current_data = 0;
1307               b0->current_length = 0;
1308               bfd_init_final_control_frame (vm, b0, bs);
1309               if (is_ipv6)
1310                 {
1311                   vlib_node_increment_counter (vm, bfd_udp6_input_node.index,
1312                                                error0, 1);
1313                 }
1314               else
1315                 {
1316                   vlib_node_increment_counter (vm, bfd_udp4_input_node.index,
1317                                                error0, 1);
1318                 }
1319               const bfd_udp_session_t *bus = &bs->udp;
1320               ip_adjacency_t *adj = adj_get (bus->adj_index);
1321               switch (adj->lookup_next_index)
1322                 {
1323                 case IP_LOOKUP_NEXT_ARP:
1324                   next0 = BFD_UDP_INPUT_NEXT_REPLY_ARP;
1325                   break;
1326                 case IP_LOOKUP_NEXT_REWRITE:
1327                   next0 = BFD_UDP_INPUT_NEXT_REPLY_REWRITE;
1328                   break;
1329                 case IP_LOOKUP_NEXT_MIDCHAIN:
1330                   next0 = BFD_UDP_INPUT_NEXT_REPLY_MIDCHAIN;
1331                   break;
1332                 default:
1333                   /* drop */
1334                   break;
1335                 }
1336             }
1337         }
1338       bfd_unlock (bm);
1339       vlib_set_next_frame_buffer (vm, rt, next0, bi0);
1340
1341       from += 1;
1342       n_left_from -= 1;
1343     }
1344
1345   return f->n_vectors;
1346 }
1347
1348 static uword
1349 bfd_udp4_input (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f)
1350 {
1351   return bfd_udp_input (vm, rt, f, 0);
1352 }
1353
1354 /*
1355  * bfd input graph node declaration
1356  */
1357 /* *INDENT-OFF* */
1358 VLIB_REGISTER_NODE (bfd_udp4_input_node, static) = {
1359   .function = bfd_udp4_input,
1360   .name = "bfd-udp4-input",
1361   .vector_size = sizeof (u32),
1362   .type = VLIB_NODE_TYPE_INTERNAL,
1363
1364   .n_errors = BFD_UDP_N_ERROR,
1365   .error_counters = bfd_udp_error_counters,
1366
1367   .format_trace = bfd_input_format_trace,
1368
1369   .n_next_nodes = BFD_UDP_INPUT_N_NEXT,
1370   .next_nodes =
1371       {
1372               [BFD_UDP_INPUT_NEXT_NORMAL] = "error-drop",
1373               [BFD_UDP_INPUT_NEXT_REPLY_ARP] = "ip4-arp",
1374               [BFD_UDP_INPUT_NEXT_REPLY_REWRITE] = "ip4-lookup",
1375               [BFD_UDP_INPUT_NEXT_REPLY_MIDCHAIN] = "ip4-midchain",
1376       },
1377 };
1378 /* *INDENT-ON* */
1379
1380 static uword
1381 bfd_udp6_input (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f)
1382 {
1383   return bfd_udp_input (vm, rt, f, 1);
1384 }
1385
1386 /* *INDENT-OFF* */
1387 VLIB_REGISTER_NODE (bfd_udp6_input_node, static) = {
1388   .function = bfd_udp6_input,
1389   .name = "bfd-udp6-input",
1390   .vector_size = sizeof (u32),
1391   .type = VLIB_NODE_TYPE_INTERNAL,
1392
1393   .n_errors = BFD_UDP_N_ERROR,
1394   .error_counters = bfd_udp_error_counters,
1395
1396   .format_trace = bfd_input_format_trace,
1397
1398   .n_next_nodes = BFD_UDP_INPUT_N_NEXT,
1399   .next_nodes =
1400       {
1401               [BFD_UDP_INPUT_NEXT_NORMAL] = "error-drop",
1402               [BFD_UDP_INPUT_NEXT_REPLY_ARP] = "ip6-discover-neighbor",
1403               [BFD_UDP_INPUT_NEXT_REPLY_REWRITE] = "ip6-lookup",
1404               [BFD_UDP_INPUT_NEXT_REPLY_MIDCHAIN] = "ip6-midchain",
1405       },
1406 };
1407 /* *INDENT-ON* */
1408
1409 /*
1410  * Process a frame of bfd echo packets
1411  * Expect 1 packet / frame
1412  */
1413 static uword
1414 bfd_udp_echo_input (vlib_main_t * vm, vlib_node_runtime_t * rt,
1415                     vlib_frame_t * f, int is_ipv6)
1416 {
1417   u32 n_left_from, *from;
1418   bfd_input_trace_t *t0;
1419   bfd_main_t *bm = &bfd_main;
1420
1421   from = vlib_frame_vector_args (f);    /* array of buffer indices */
1422   n_left_from = f->n_vectors;   /* number of buffer indices */
1423
1424   while (n_left_from > 0)
1425     {
1426       u32 bi0;
1427       vlib_buffer_t *b0;
1428       u32 next0;
1429
1430       bi0 = from[0];
1431       b0 = vlib_get_buffer (vm, bi0);
1432
1433       /* If this pkt is traced, snapshot the data */
1434       if (b0->flags & VLIB_BUFFER_IS_TRACED)
1435         {
1436           u64 len;
1437           t0 = vlib_add_trace (vm, rt, b0, sizeof (*t0));
1438           len = (b0->current_length < sizeof (t0->data)) ? b0->current_length
1439             : sizeof (t0->data);
1440           t0->len = len;
1441           clib_memcpy_fast (t0->data, vlib_buffer_get_current (b0), len);
1442         }
1443
1444       bfd_session_t *bs = NULL;
1445       bfd_lock (bm);
1446       if ((bs = bfd_consume_echo_pkt (vm, bfd_udp_main.bfd_main, b0)))
1447         {
1448           b0->error = rt->errors[BFD_UDP_ERROR_NONE];
1449           next0 = BFD_UDP_ECHO_INPUT_NEXT_NORMAL;
1450         }
1451       else
1452         {
1453           /* loop back the packet */
1454           b0->error = rt->errors[BFD_UDP_ERROR_NONE];
1455           if (is_ipv6)
1456             {
1457               vlib_node_increment_counter (vm, bfd_udp_echo6_input_node.index,
1458                                            BFD_UDP_ERROR_NONE, 1);
1459             }
1460           else
1461             {
1462               vlib_node_increment_counter (vm, bfd_udp_echo4_input_node.index,
1463                                            BFD_UDP_ERROR_NONE, 1);
1464             }
1465           next0 = BFD_UDP_ECHO_INPUT_NEXT_REPLY_REWRITE;
1466         }
1467
1468       bfd_unlock (bm);
1469
1470       if (bs)
1471         {
1472           vlib_increment_combined_counter (
1473             &bm->rx_echo_counter, vm->thread_index, bs->bs_idx, 1,
1474             vlib_buffer_length_in_chain (vm, b0));
1475         }
1476
1477       vlib_set_next_frame_buffer (vm, rt, next0, bi0);
1478
1479       from += 1;
1480       n_left_from -= 1;
1481     }
1482
1483   return f->n_vectors;
1484 }
1485
1486 static uword
1487 bfd_udp_echo4_input (vlib_main_t * vm, vlib_node_runtime_t * rt,
1488                      vlib_frame_t * f)
1489 {
1490   return bfd_udp_echo_input (vm, rt, f, 0);
1491 }
1492
1493 u8 *
1494 bfd_echo_input_format_trace (u8 * s, va_list * args)
1495 {
1496   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
1497   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
1498   const bfd_udp_echo_input_trace_t *t =
1499     va_arg (*args, bfd_udp_echo_input_trace_t *);
1500   if (t->len > STRUCT_SIZE_OF (bfd_pkt_t, head))
1501     {
1502       s = format (s, "BFD ECHO:\n");
1503       s = format (s, "    data: %U", format_hexdump, t->data, t->len);
1504     }
1505
1506   return s;
1507 }
1508
1509 /*
1510  * bfd input graph node declaration
1511  */
1512 /* *INDENT-OFF* */
1513 VLIB_REGISTER_NODE (bfd_udp_echo4_input_node, static) = {
1514   .function = bfd_udp_echo4_input,
1515   .name = "bfd-udp-echo4-input",
1516   .vector_size = sizeof (u32),
1517   .type = VLIB_NODE_TYPE_INTERNAL,
1518
1519   .n_errors = BFD_UDP_N_ERROR,
1520   .error_counters = bfd_udp_error_counters,
1521
1522   .format_trace = bfd_echo_input_format_trace,
1523
1524   .n_next_nodes = BFD_UDP_ECHO_INPUT_N_NEXT,
1525   .next_nodes =
1526       {
1527               [BFD_UDP_ECHO_INPUT_NEXT_NORMAL] = "error-drop",
1528               [BFD_UDP_ECHO_INPUT_NEXT_REPLY_ARP] = "ip4-arp",
1529               [BFD_UDP_ECHO_INPUT_NEXT_REPLY_REWRITE] = "ip4-lookup",
1530       },
1531 };
1532 /* *INDENT-ON* */
1533
1534 static uword
1535 bfd_udp_echo6_input (vlib_main_t * vm, vlib_node_runtime_t * rt,
1536                      vlib_frame_t * f)
1537 {
1538   return bfd_udp_echo_input (vm, rt, f, 1);
1539 }
1540
1541 /* *INDENT-OFF* */
1542 VLIB_REGISTER_NODE (bfd_udp_echo6_input_node, static) = {
1543   .function = bfd_udp_echo6_input,
1544   .name = "bfd-udp-echo6-input",
1545   .vector_size = sizeof (u32),
1546   .type = VLIB_NODE_TYPE_INTERNAL,
1547
1548   .n_errors = BFD_UDP_N_ERROR,
1549   .error_counters = bfd_udp_error_counters,
1550
1551   .format_trace = bfd_echo_input_format_trace,
1552
1553   .n_next_nodes = BFD_UDP_ECHO_INPUT_N_NEXT,
1554   .next_nodes =
1555       {
1556               [BFD_UDP_ECHO_INPUT_NEXT_NORMAL] = "error-drop",
1557               [BFD_UDP_ECHO_INPUT_NEXT_REPLY_ARP] = "ip6-discover-neighbor",
1558               [BFD_UDP_ECHO_INPUT_NEXT_REPLY_REWRITE] = "ip6-lookup",
1559       },
1560 };
1561
1562 /* *INDENT-ON* */
1563
1564 static clib_error_t *
1565 bfd_udp_sw_if_add_del (CLIB_UNUSED (vnet_main_t *vnm), u32 sw_if_index,
1566                        u32 is_create)
1567 {
1568   u32 *to_be_freed = NULL;
1569   bfd_udp_main_t *bum = &bfd_udp_main;
1570   BFD_DBG ("sw_if_add_del called, sw_if_index=%u, is_create=%u", sw_if_index,
1571            is_create);
1572   if (!is_create)
1573     {
1574       bfd_session_t *bs;
1575       pool_foreach (bs, bum->bfd_main->sessions)
1576         {
1577           if (bs->transport != BFD_TRANSPORT_UDP4 &&
1578               bs->transport != BFD_TRANSPORT_UDP6)
1579             {
1580               continue;
1581             }
1582           if (bs->udp.key.sw_if_index != sw_if_index)
1583             {
1584               continue;
1585             }
1586           vec_add1 (to_be_freed, bs->bs_idx);
1587         }
1588     }
1589   u32 *bs_idx;
1590   vec_foreach (bs_idx, to_be_freed)
1591     {
1592       bfd_session_t *bs = pool_elt_at_index (bum->bfd_main->sessions, *bs_idx);
1593       vlib_log_notice (bum->log_class,
1594                        "removal of sw_if_index=%u forces removal of bfd "
1595                        "session with bs_idx=%u",
1596                        sw_if_index, bs->bs_idx);
1597       bfd_session_set_flags (vlib_get_main (), bs, 0);
1598       bfd_udp_del_session_internal (vlib_get_main (), bs);
1599     }
1600   return 0;
1601 }
1602
1603 VNET_SW_INTERFACE_ADD_DEL_FUNCTION (bfd_udp_sw_if_add_del);
1604
1605 clib_error_t *
1606 bfd_udp_stats_init (bfd_udp_main_t *bum)
1607 {
1608   const char *name4 = "/bfd/udp4/sessions";
1609   bum->udp4_sessions_count_stat_seg_entry = vlib_stats_add_gauge ("%s", name4);
1610
1611   vlib_stats_set_gauge (bum->udp4_sessions_count_stat_seg_entry, 0);
1612   if (~0 == bum->udp4_sessions_count_stat_seg_entry)
1613     {
1614       return clib_error_return (
1615         0, "Could not create stat segment entry for %s", name4);
1616     }
1617   const char *name6 = "/bfd/udp6/sessions";
1618   bum->udp6_sessions_count_stat_seg_entry = vlib_stats_add_gauge ("%s", name6);
1619
1620   vlib_stats_set_gauge (bum->udp6_sessions_count_stat_seg_entry, 0);
1621   if (~0 == bum->udp6_sessions_count_stat_seg_entry)
1622     {
1623       return clib_error_return (
1624         0, "Could not create stat segment entry for %s", name6);
1625     }
1626
1627   return 0;
1628 }
1629
1630 /*
1631  * setup function
1632  */
1633 static clib_error_t *
1634 bfd_udp_init (vlib_main_t * vm)
1635 {
1636   bfd_udp_main.udp4_sessions_count = 0;
1637   bfd_udp_main.udp6_sessions_count = 0;
1638   mhash_init (&bfd_udp_main.bfd_session_idx_by_bfd_key, sizeof (uword),
1639               sizeof (bfd_udp_key_t));
1640   bfd_udp_main.bfd_main = &bfd_main;
1641   bfd_udp_main.vnet_main = vnet_get_main ();
1642   vlib_node_t *node = vlib_get_node_by_name (vm, (u8 *) "ip4-arp");
1643   ASSERT (node);
1644   bfd_udp_main.ip4_arp_idx = node->index;
1645   node = vlib_get_node_by_name (vm, (u8 *) "ip6-discover-neighbor");
1646   ASSERT (node);
1647   bfd_udp_main.ip6_ndp_idx = node->index;
1648   node = vlib_get_node_by_name (vm, (u8 *) "ip4-rewrite");
1649   ASSERT (node);
1650   bfd_udp_main.ip4_rewrite_idx = node->index;
1651   node = vlib_get_node_by_name (vm, (u8 *) "ip6-rewrite");
1652   ASSERT (node);
1653   bfd_udp_main.ip6_rewrite_idx = node->index;
1654   node = vlib_get_node_by_name (vm, (u8 *) "ip4-midchain");
1655   ASSERT (node);
1656   bfd_udp_main.ip4_midchain_idx = node->index;
1657   node = vlib_get_node_by_name (vm, (u8 *) "ip6-midchain");
1658   ASSERT (node);
1659   bfd_udp_main.ip6_midchain_idx = node->index;
1660
1661   bfd_udp_stats_init (&bfd_udp_main);
1662
1663   bfd_udp_main.log_class = vlib_log_register_class ("bfd", "udp");
1664   vlib_log_debug (bfd_udp_main.log_class, "initialized");
1665   return 0;
1666 }
1667
1668 VLIB_INIT_FUNCTION (bfd_udp_init);
1669
1670 /*
1671  * fd.io coding-style-patch-verification: ON
1672  *
1673  * Local Variables:
1674  * eval: (c-set-style "gnu")
1675  * End:
1676  */