BFD: IPv6 support
[vpp.git] / src / vnet / bfd / bfd_udp.c
1 #include <vppinfra/types.h>
2 #include <vlibmemory/api.h>
3 #include <vlib/vlib.h>
4 #include <vlib/buffer.h>
5 #include <vnet/ip/format.h>
6 #include <vnet/ethernet/packet.h>
7 #include <vnet/ip/udp_packet.h>
8 #include <vnet/ip/lookup.h>
9 #include <vnet/ip/icmp46_packet.h>
10 #include <vnet/ip/ip4.h>
11 #include <vnet/ip/ip6.h>
12 #include <vnet/ip/udp.h>
13 #include <vnet/ip/ip6_packet.h>
14 #include <vnet/adj/adj.h>
15 #include <vnet/adj/adj_nbr.h>
16 #include <vnet/bfd/bfd_debug.h>
17 #include <vnet/bfd/bfd_udp.h>
18 #include <vnet/bfd/bfd_main.h>
19 #include <vnet/bfd/bfd_api.h>
20
21 typedef struct
22 {
23   bfd_main_t *bfd_main;
24   /* hashmap - bfd session index by bfd key - used for CLI/API lookup, where
25    * discriminator is unknown */
26   mhash_t bfd_session_idx_by_bfd_key;
27 } bfd_udp_main_t;
28
29 static vlib_node_registration_t bfd_udp4_input_node;
30 static vlib_node_registration_t bfd_udp6_input_node;
31
32 bfd_udp_main_t bfd_udp_main;
33
34 void bfd_add_udp4_transport (vlib_main_t *vm, vlib_buffer_t *b,
35                              bfd_udp_session_t *bus)
36 {
37   udp_header_t *udp;
38   const bfd_udp_key_t *key = &bus->key;
39
40   b->flags |= VNET_BUFFER_LOCALLY_ORIGINATED;
41   vnet_buffer (b)->ip.adj_index[VLIB_RX] = bus->adj_index;
42   vnet_buffer (b)->ip.adj_index[VLIB_TX] = bus->adj_index;
43   ip4_header_t *ip4;
44   const size_t headers_size = sizeof (*ip4) + sizeof (*udp);
45   vlib_buffer_advance (b, -headers_size);
46   ip4 = vlib_buffer_get_current (b);
47   udp = (udp_header_t *)(ip4 + 1);
48   memset (ip4, 0, headers_size);
49   ip4->ip_version_and_header_length = 0x45;
50   ip4->ttl = 255;
51   ip4->protocol = IP_PROTOCOL_UDP;
52   ip4->src_address.as_u32 = key->local_addr.ip4.as_u32;
53   ip4->dst_address.as_u32 = key->peer_addr.ip4.as_u32;
54
55   udp->src_port = clib_host_to_net_u16 (50000); /* FIXME */
56   udp->dst_port = clib_host_to_net_u16 (UDP_DST_PORT_bfd4);
57
58   /* fix ip length, checksum and udp length */
59   const u16 ip_length = vlib_buffer_length_in_chain (vm, b);
60
61   ip4->length = clib_host_to_net_u16 (ip_length);
62   ip4->checksum = ip4_header_checksum (ip4);
63
64   const u16 udp_length = ip_length - (sizeof (*ip4));
65   udp->length = clib_host_to_net_u16 (udp_length);
66 }
67
68 void bfd_add_udp6_transport (vlib_main_t *vm, vlib_buffer_t *b,
69                              bfd_udp_session_t *bus)
70 {
71   udp_header_t *udp;
72   const bfd_udp_key_t *key = &bus->key;
73
74   b->flags |= VNET_BUFFER_LOCALLY_ORIGINATED;
75   vnet_buffer (b)->ip.adj_index[VLIB_RX] = bus->adj_index;
76   vnet_buffer (b)->ip.adj_index[VLIB_TX] = bus->adj_index;
77   ip6_header_t *ip6;
78   const size_t headers_size = sizeof (*ip6) + sizeof (*udp);
79   vlib_buffer_advance (b, -headers_size);
80   ip6 = vlib_buffer_get_current (b);
81   udp = (udp_header_t *)(ip6 + 1);
82   memset (ip6, 0, headers_size);
83   ip6->ip_version_traffic_class_and_flow_label =
84       clib_host_to_net_u32 (0x6 << 28);
85   ip6->hop_limit = 255;
86   ip6->protocol = IP_PROTOCOL_UDP;
87   clib_memcpy (&ip6->src_address, &key->local_addr.ip6,
88                sizeof (ip6->src_address));
89   clib_memcpy (&ip6->dst_address, &key->peer_addr.ip6,
90                sizeof (ip6->dst_address));
91
92   udp->src_port = clib_host_to_net_u16 (50000); /* FIXME */
93   udp->dst_port = clib_host_to_net_u16 (UDP_DST_PORT_bfd6);
94
95   /* fix ip payload length and udp length */
96   const u16 udp_length = vlib_buffer_length_in_chain (vm, b) - (sizeof (*ip6));
97   udp->length = clib_host_to_net_u16 (udp_length);
98   ip6->payload_length = udp->length;
99
100   /* IPv6 UDP checksum is mandatory */
101   int bogus = 0;
102   udp->checksum = ip6_tcp_udp_icmp_compute_checksum (vm, b, ip6, &bogus);
103   ASSERT (bogus == 0);
104   if (udp->checksum == 0)
105     {
106       udp->checksum = 0xffff;
107     }
108 }
109
110 static bfd_session_t *bfd_lookup_session (bfd_udp_main_t *bum,
111                                           const bfd_udp_key_t *key)
112 {
113   uword *p = mhash_get (&bum->bfd_session_idx_by_bfd_key, key);
114   if (p)
115     {
116       return bfd_find_session_by_idx (bum->bfd_main, *p);
117     }
118   return 0;
119 }
120
121 static vnet_api_error_t
122 bfd_udp_add_session_internal (bfd_udp_main_t *bum, u32 sw_if_index,
123                               u32 desired_min_tx_us, u32 required_min_rx_us,
124                               u8 detect_mult, const ip46_address_t *local_addr,
125                               const ip46_address_t *peer_addr, u32 *bs_index)
126 {
127   vnet_sw_interface_t *sw_if =
128       vnet_get_sw_interface (vnet_get_main (), sw_if_index);
129   /* get a pool entry and if we end up not needing it, give it back */
130   bfd_transport_t t = BFD_TRANSPORT_UDP4;
131   if (!ip46_address_is_ip4 (local_addr))
132     {
133       t = BFD_TRANSPORT_UDP6;
134     }
135   bfd_session_t *bs = bfd_get_session (bum->bfd_main, t);
136   bfd_udp_session_t *bus = &bs->udp;
137   memset (bus, 0, sizeof (*bus));
138   bfd_udp_key_t *key = &bus->key;
139   key->sw_if_index = sw_if->sw_if_index;
140   key->local_addr.as_u64[0] = local_addr->as_u64[0];
141   key->local_addr.as_u64[1] = local_addr->as_u64[1];
142   key->peer_addr.as_u64[0] = peer_addr->as_u64[0];
143   key->peer_addr.as_u64[1] = peer_addr->as_u64[1];
144   const bfd_session_t *tmp = bfd_lookup_session (bum, key);
145   if (tmp)
146     {
147       BFD_ERR ("duplicate bfd-udp session, existing bs_idx=%d", tmp->bs_idx);
148       bfd_put_session (bum->bfd_main, bs);
149       return VNET_API_ERROR_BFD_EEXIST;
150     }
151   key->sw_if_index = sw_if->sw_if_index;
152   mhash_set (&bum->bfd_session_idx_by_bfd_key, key, bs->bs_idx, NULL);
153   BFD_DBG ("session created, bs_idx=%u, sw_if_index=%d, local=%U, peer=%U",
154            bs->bs_idx, key->sw_if_index, format_ip46_address, &key->local_addr,
155            IP46_TYPE_ANY, format_ip46_address, &key->peer_addr, IP46_TYPE_ANY);
156   if (BFD_TRANSPORT_UDP4 == t)
157     {
158       bus->adj_index = adj_nbr_add_or_lock (FIB_PROTOCOL_IP4, VNET_LINK_IP4,
159                                             &key->peer_addr, key->sw_if_index);
160       BFD_DBG ("adj_nbr_add_or_lock(FIB_PROTOCOL_IP4, VNET_LINK_IP4, %U, %d) "
161                "returns %d",
162                format_ip46_address, &key->peer_addr, IP46_TYPE_ANY,
163                key->sw_if_index, bus->adj_index);
164     }
165   else
166     {
167       bus->adj_index = adj_nbr_add_or_lock (FIB_PROTOCOL_IP6, VNET_LINK_IP6,
168                                             &key->peer_addr, key->sw_if_index);
169       BFD_DBG ("adj_nbr_add_or_lock(FIB_PROTOCOL_IP6, VNET_LINK_IP6, %U, %d) "
170                "returns %d",
171                format_ip46_address, &key->peer_addr, IP46_TYPE_ANY,
172                key->sw_if_index, bus->adj_index);
173     }
174   bs->config_desired_min_tx_us = desired_min_tx_us;
175   bs->required_min_rx_us = required_min_rx_us;
176   bs->required_min_echo_rx_us = required_min_rx_us; /* FIXME */
177   bs->local_detect_mult = detect_mult;
178   bfd_session_start (bum->bfd_main, bs);
179   *bs_index = bs->bs_idx;
180   return 0;
181 }
182
183 static vnet_api_error_t
184 bfd_udp_validate_api_input (u32 sw_if_index, const ip46_address_t *local_addr,
185                             const ip46_address_t *peer_addr)
186 {
187   vnet_sw_interface_t *sw_if =
188       vnet_get_sw_interface (vnet_get_main (), sw_if_index);
189   u8 local_ip_valid = 0;
190   ip_interface_address_t *ia = NULL;
191   if (!sw_if)
192     {
193       BFD_ERR ("got NULL sw_if");
194       return VNET_API_ERROR_INVALID_SW_IF_INDEX;
195     }
196   if (ip46_address_is_ip4 (local_addr))
197     {
198       if (!ip46_address_is_ip4 (peer_addr))
199         {
200           BFD_ERR ("IP family mismatch");
201           return VNET_API_ERROR_INVALID_ARGUMENT;
202         }
203       ip4_main_t *im = &ip4_main;
204
205       /* *INDENT-OFF* */
206       foreach_ip_interface_address (
207           &im->lookup_main, ia, sw_if_index, 0 /* honor unnumbered */, ({
208             ip4_address_t *x =
209                 ip_interface_address_get_address (&im->lookup_main, ia);
210             if (x->as_u32 == local_addr->ip4.as_u32)
211               {
212                 /* valid address for this interface */
213                 local_ip_valid = 1;
214                 break;
215               }
216           }));
217       /* *INDENT-ON* */
218     }
219   else
220     {
221       if (ip46_address_is_ip4 (peer_addr))
222         {
223           BFD_ERR ("IP family mismatch");
224           return VNET_API_ERROR_INVALID_ARGUMENT;
225         }
226       ip6_main_t *im = &ip6_main;
227       /* *INDENT-OFF* */
228       foreach_ip_interface_address (
229           &im->lookup_main, ia, sw_if_index, 0 /* honor unnumbered */, ({
230             ip6_address_t *x =
231                 ip_interface_address_get_address (&im->lookup_main, ia);
232             if (local_addr->ip6.as_u64[0] == x->as_u64[0] &&
233                 local_addr->ip6.as_u64[1] == x->as_u64[1])
234               {
235                 /* valid address for this interface */
236                 local_ip_valid = 1;
237                 break;
238               }
239           }));
240       /* *INDENT-ON* */
241     }
242
243   if (!local_ip_valid)
244     {
245       BFD_ERR ("address not found on interface");
246       return VNET_API_ERROR_ADDRESS_NOT_FOUND_FOR_INTERFACE;
247     }
248
249   return 0;
250 }
251
252 vnet_api_error_t bfd_udp_add_session (u32 sw_if_index, u32 desired_min_tx_us,
253                                       u32 required_min_rx_us, u8 detect_mult,
254                                       const ip46_address_t *local_addr,
255                                       const ip46_address_t *peer_addr,
256                                       u32 *bs_index)
257 {
258   vnet_api_error_t rv =
259       bfd_udp_validate_api_input (sw_if_index, local_addr, peer_addr);
260   if (rv)
261     {
262       return rv;
263     }
264   if (detect_mult < 1)
265     {
266       BFD_ERR ("detect_mult < 1");
267       return VNET_API_ERROR_INVALID_ARGUMENT;
268     }
269   if (desired_min_tx_us < 1)
270     {
271       BFD_ERR ("desired_min_tx_us < 1");
272       return VNET_API_ERROR_INVALID_ARGUMENT;
273     }
274   return bfd_udp_add_session_internal (
275       &bfd_udp_main, sw_if_index, desired_min_tx_us, required_min_rx_us,
276       detect_mult, local_addr, peer_addr, bs_index);
277 }
278
279 vnet_api_error_t bfd_udp_del_session (u32 sw_if_index,
280                                       const ip46_address_t *local_addr,
281                                       const ip46_address_t *peer_addr)
282 {
283   vnet_api_error_t rv =
284       bfd_udp_validate_api_input (sw_if_index, local_addr, peer_addr);
285   if (rv)
286     {
287       return rv;
288     }
289   bfd_udp_main_t *bum = &bfd_udp_main;
290   vnet_sw_interface_t *sw_if =
291       vnet_get_sw_interface (vnet_get_main (), sw_if_index);
292   bfd_udp_key_t key;
293   memset (&key, 0, sizeof (key));
294   key.sw_if_index = sw_if->sw_if_index;
295   key.local_addr.as_u64[0] = local_addr->as_u64[0];
296   key.local_addr.as_u64[1] = local_addr->as_u64[1];
297   key.peer_addr.as_u64[0] = peer_addr->as_u64[0];
298   key.peer_addr.as_u64[1] = peer_addr->as_u64[1];
299   bfd_session_t *tmp = bfd_lookup_session (bum, &key);
300   if (tmp)
301     {
302       BFD_DBG ("free bfd-udp session, bs_idx=%d", tmp->bs_idx);
303       mhash_unset (&bum->bfd_session_idx_by_bfd_key, &key, NULL);
304       adj_unlock (tmp->udp.adj_index);
305       bfd_put_session (bum->bfd_main, tmp);
306     }
307   else
308     {
309       BFD_ERR ("no such session");
310       return VNET_API_ERROR_BFD_NOENT;
311     }
312   return 0;
313 }
314
315 typedef enum {
316   BFD_UDP_INPUT_NEXT_NORMAL,
317   BFD_UDP_INPUT_NEXT_REPLY,
318   BFD_UDP_INPUT_N_NEXT,
319 } bfd_udp_input_next_t;
320
321 /* Packet counters */
322 #define foreach_bfd_udp_error(F)           \
323   F (NONE, "good bfd packets (processed)") \
324   F (BAD, "invalid bfd packets")           \
325   F (DISABLED, "bfd packets received on disabled interfaces")
326
327 #define F(sym, string) static char BFD_UDP_ERR_##sym##_STR[] = string;
328 foreach_bfd_udp_error (F);
329 #undef F
330
331 static char *bfd_udp_error_strings[] = {
332 #define F(sym, string) BFD_UDP_ERR_##sym##_STR,
333   foreach_bfd_udp_error (F)
334 #undef F
335 };
336
337 typedef enum {
338 #define F(sym, str) BFD_UDP_ERROR_##sym,
339   foreach_bfd_udp_error (F)
340 #undef F
341       BFD_UDP_N_ERROR,
342 } bfd_udp_error_t;
343
344 static void bfd_udp4_find_headers (vlib_buffer_t *b, const ip4_header_t **ip4,
345                                    const udp_header_t **udp)
346 {
347   /* sanity check first */
348   const i32 start = vnet_buffer (b)->ip.start_of_ip_header;
349   if (start < 0 && start < sizeof (b->pre_data))
350     {
351       BFD_ERR ("Start of ip header is before pre_data, ignoring");
352       *ip4 = NULL;
353       *udp = NULL;
354       return;
355     }
356   *ip4 = (ip4_header_t *)(b->data + start);
357   if ((u8 *)*ip4 > (u8 *)vlib_buffer_get_current (b))
358     {
359       BFD_ERR ("Start of ip header is beyond current data, ignoring");
360       *ip4 = NULL;
361       *udp = NULL;
362       return;
363     }
364   *udp = (udp_header_t *)((*ip4) + 1);
365 }
366
367 static bfd_udp_error_t bfd_udp4_verify_transport (const ip4_header_t *ip4,
368                                                   const udp_header_t *udp,
369                                                   const bfd_session_t *bs)
370 {
371   const bfd_udp_session_t *bus = &bs->udp;
372   const bfd_udp_key_t *key = &bus->key;
373   if (ip4->src_address.as_u32 != key->peer_addr.ip4.as_u32)
374     {
375       BFD_ERR ("IPv4 src addr mismatch, got %U, expected %U",
376                format_ip4_address, ip4->src_address.as_u8, format_ip4_address,
377                key->peer_addr.ip4.as_u8);
378       return BFD_UDP_ERROR_BAD;
379     }
380   if (ip4->dst_address.as_u32 != key->local_addr.ip4.as_u32)
381     {
382       BFD_ERR ("IPv4 dst addr mismatch, got %U, expected %U",
383                format_ip4_address, ip4->dst_address.as_u8, format_ip4_address,
384                key->local_addr.ip4.as_u8);
385       return BFD_UDP_ERROR_BAD;
386     }
387   const u8 expected_ttl = 255;
388   if (ip4->ttl != expected_ttl)
389     {
390       BFD_ERR ("IPv4 unexpected TTL value %u, expected %u", ip4->ttl,
391                expected_ttl);
392       return BFD_UDP_ERROR_BAD;
393     }
394   if (clib_net_to_host_u16 (udp->src_port) < 49152 ||
395       clib_net_to_host_u16 (udp->src_port) > 65535)
396     {
397       BFD_ERR ("Invalid UDP src port %u, out of range <49152,65535>",
398                udp->src_port);
399     }
400   return BFD_UDP_ERROR_NONE;
401 }
402
403 typedef struct
404 {
405   u32 bs_idx;
406   bfd_pkt_t pkt;
407 } bfd_rpc_update_t;
408
409 static void bfd_rpc_update_session_cb (const bfd_rpc_update_t *a)
410 {
411   bfd_consume_pkt (bfd_udp_main.bfd_main, &a->pkt, a->bs_idx);
412 }
413
414 static void bfd_rpc_update_session (u32 bs_idx, const bfd_pkt_t *pkt)
415 {
416   /* packet length was already verified to be correct by the caller */
417   const u32 data_size = sizeof (bfd_rpc_update_t) -
418                         STRUCT_SIZE_OF (bfd_rpc_update_t, pkt) +
419                         pkt->head.length;
420   u8 data[data_size];
421   bfd_rpc_update_t *update = (bfd_rpc_update_t *)data;
422   update->bs_idx = bs_idx;
423   clib_memcpy (&update->pkt, pkt, pkt->head.length);
424   vl_api_rpc_call_main_thread (bfd_rpc_update_session_cb, data, data_size);
425 }
426
427 static bfd_udp_error_t bfd_udp4_scan (vlib_main_t *vm, vlib_node_runtime_t *rt,
428                                       vlib_buffer_t *b, bfd_session_t **bs_out)
429 {
430   const bfd_pkt_t *pkt = vlib_buffer_get_current (b);
431   if (sizeof (*pkt) > b->current_length)
432     {
433       BFD_ERR (
434           "Payload size %d too small to hold bfd packet of minimum size %d",
435           b->current_length, sizeof (*pkt));
436       return BFD_UDP_ERROR_BAD;
437     }
438   const ip4_header_t *ip4;
439   const udp_header_t *udp;
440   bfd_udp4_find_headers (b, &ip4, &udp);
441   if (!ip4 || !udp)
442     {
443       BFD_ERR ("Couldn't find ip4 or udp header");
444       return BFD_UDP_ERROR_BAD;
445     }
446   if (!bfd_verify_pkt_common (pkt))
447     {
448       return BFD_UDP_ERROR_BAD;
449     }
450   bfd_session_t *bs = NULL;
451   if (pkt->your_disc)
452     {
453       BFD_DBG ("Looking up BFD session using discriminator %u",
454                pkt->your_disc);
455       bs = bfd_find_session_by_disc (bfd_udp_main.bfd_main, pkt->your_disc);
456     }
457   else
458     {
459       bfd_udp_key_t key;
460       memset (&key, 0, sizeof (key));
461       key.sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
462       key.local_addr.ip4.as_u32 = ip4->dst_address.as_u32;
463       key.peer_addr.ip4.as_u32 = ip4->src_address.as_u32;
464       BFD_DBG ("Looking up BFD session using key (sw_if_index=%u, local=%U, "
465                "peer=%U)",
466                key.sw_if_index, format_ip4_address, key.local_addr.ip4.as_u8,
467                format_ip4_address, key.peer_addr.ip4.as_u8);
468       bs = bfd_lookup_session (&bfd_udp_main, &key);
469     }
470   if (!bs)
471     {
472       BFD_ERR ("BFD session lookup failed - no session matches BFD pkt");
473       return BFD_UDP_ERROR_BAD;
474     }
475   BFD_DBG ("BFD session found, bs_idx=%u", bs->bs_idx);
476   if (!bfd_verify_pkt_session (pkt, b->current_length, bs))
477     {
478       return BFD_UDP_ERROR_BAD;
479     }
480   bfd_udp_error_t err;
481   if (BFD_UDP_ERROR_NONE != (err = bfd_udp4_verify_transport (ip4, udp, bs)))
482     {
483       return err;
484     }
485   bfd_rpc_update_session (bs->bs_idx, pkt);
486   *bs_out = bs;
487   return BFD_UDP_ERROR_NONE;
488 }
489
490 static void bfd_udp6_find_headers (vlib_buffer_t *b, const ip6_header_t **ip6,
491                                    const udp_header_t **udp)
492 {
493   /* sanity check first */
494   const i32 start = vnet_buffer (b)->ip.start_of_ip_header;
495   if (start < 0 && start < sizeof (b->pre_data))
496     {
497       BFD_ERR ("Start of ip header is before pre_data, ignoring");
498       *ip6 = NULL;
499       *udp = NULL;
500       return;
501     }
502   *ip6 = (ip6_header_t *)(b->data + start);
503   if ((u8 *)*ip6 > (u8 *)vlib_buffer_get_current (b))
504     {
505       BFD_ERR ("Start of ip header is beyond current data, ignoring");
506       *ip6 = NULL;
507       *udp = NULL;
508       return;
509     }
510   *udp = (udp_header_t *)((*ip6) + 1);
511 }
512
513 static bfd_udp_error_t bfd_udp6_verify_transport (const ip6_header_t *ip6,
514                                                   const udp_header_t *udp,
515                                                   const bfd_session_t *bs)
516 {
517   const bfd_udp_session_t *bus = &bs->udp;
518   const bfd_udp_key_t *key = &bus->key;
519   if (ip6->src_address.as_u64[0] != key->peer_addr.ip6.as_u64[0] &&
520       ip6->src_address.as_u64[1] != key->peer_addr.ip6.as_u64[1])
521     {
522       BFD_ERR ("IP src addr mismatch, got %U, expected %U", format_ip6_address,
523                ip6, format_ip6_address, &key->peer_addr.ip6);
524       return BFD_UDP_ERROR_BAD;
525     }
526   if (ip6->dst_address.as_u64[0] != key->local_addr.ip6.as_u64[0] &&
527       ip6->dst_address.as_u64[1] != key->local_addr.ip6.as_u64[1])
528     {
529       BFD_ERR ("IP dst addr mismatch, got %U, expected %U", format_ip6_address,
530                ip6, format_ip6_address, &key->local_addr.ip6);
531       return BFD_UDP_ERROR_BAD;
532     }
533   const u8 expected_hop_limit = 255;
534   if (ip6->hop_limit != expected_hop_limit)
535     {
536       BFD_ERR ("IPv6 unexpected hop-limit value %u, expected %u",
537                ip6->hop_limit, expected_hop_limit);
538       return BFD_UDP_ERROR_BAD;
539     }
540   if (clib_net_to_host_u16 (udp->src_port) < 49152 ||
541       clib_net_to_host_u16 (udp->src_port) > 65535)
542     {
543       BFD_ERR ("Invalid UDP src port %u, out of range <49152,65535>",
544                udp->src_port);
545     }
546   return BFD_UDP_ERROR_NONE;
547 }
548
549 static bfd_udp_error_t bfd_udp6_scan (vlib_main_t *vm, vlib_node_runtime_t *rt,
550                                       vlib_buffer_t *b, bfd_session_t **bs_out)
551 {
552   const bfd_pkt_t *pkt = vlib_buffer_get_current (b);
553   if (sizeof (*pkt) > b->current_length)
554     {
555       BFD_ERR (
556           "Payload size %d too small to hold bfd packet of minimum size %d",
557           b->current_length, sizeof (*pkt));
558       return BFD_UDP_ERROR_BAD;
559     }
560   const ip6_header_t *ip6;
561   const udp_header_t *udp;
562   bfd_udp6_find_headers (b, &ip6, &udp);
563   if (!ip6 || !udp)
564     {
565       BFD_ERR ("Couldn't find ip6 or udp header");
566       return BFD_UDP_ERROR_BAD;
567     }
568   if (!bfd_verify_pkt_common (pkt))
569     {
570       return BFD_UDP_ERROR_BAD;
571     }
572   bfd_session_t *bs = NULL;
573   if (pkt->your_disc)
574     {
575       BFD_DBG ("Looking up BFD session using discriminator %u",
576                pkt->your_disc);
577       bs = bfd_find_session_by_disc (bfd_udp_main.bfd_main, pkt->your_disc);
578     }
579   else
580     {
581       bfd_udp_key_t key;
582       memset (&key, 0, sizeof (key));
583       key.sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
584       key.local_addr.ip6.as_u64[0] = ip6->dst_address.as_u64[0];
585       key.local_addr.ip6.as_u64[1] = ip6->dst_address.as_u64[1];
586       key.peer_addr.ip6.as_u64[0] = ip6->src_address.as_u64[0];
587       key.peer_addr.ip6.as_u64[1] = ip6->src_address.as_u64[1];
588       BFD_DBG ("Looking up BFD session using key (sw_if_index=%u, local=%U, "
589                "peer=%U)",
590                key.sw_if_index, format_ip6_address, &key.local_addr,
591                format_ip6_address, &key.peer_addr);
592       bs = bfd_lookup_session (&bfd_udp_main, &key);
593     }
594   if (!bs)
595     {
596       BFD_ERR ("BFD session lookup failed - no session matches BFD pkt");
597       return BFD_UDP_ERROR_BAD;
598     }
599   BFD_DBG ("BFD session found, bs_idx=%u", bs->bs_idx);
600   if (!bfd_verify_pkt_session (pkt, b->current_length, bs))
601     {
602       return BFD_UDP_ERROR_BAD;
603     }
604   bfd_udp_error_t err;
605   if (BFD_UDP_ERROR_NONE != (err = bfd_udp6_verify_transport (ip6, udp, bs)))
606     {
607       return err;
608     }
609   bfd_rpc_update_session (bs->bs_idx, pkt);
610   *bs_out = bs;
611   return BFD_UDP_ERROR_NONE;
612 }
613
614 /*
615  * Process a frame of bfd packets
616  * Expect 1 packet / frame
617  */
618 static uword bfd_udp_input (vlib_main_t *vm, vlib_node_runtime_t *rt,
619                             vlib_frame_t *f, int is_ipv6)
620 {
621   u32 n_left_from, *from;
622   bfd_input_trace_t *t0;
623
624   from = vlib_frame_vector_args (f); /* array of buffer indices */
625   n_left_from = f->n_vectors;        /* number of buffer indices */
626
627   while (n_left_from > 0)
628     {
629       u32 bi0;
630       vlib_buffer_t *b0;
631       u32 next0, error0;
632
633       bi0 = from[0];
634       b0 = vlib_get_buffer (vm, bi0);
635
636       bfd_session_t *bs = NULL;
637
638       /* If this pkt is traced, snapshot the data */
639       if (b0->flags & VLIB_BUFFER_IS_TRACED)
640         {
641           int len;
642           t0 = vlib_add_trace (vm, rt, b0, sizeof (*t0));
643           len = (b0->current_length < sizeof (t0->data)) ? b0->current_length
644                                                          : sizeof (t0->data);
645           t0->len = len;
646           clib_memcpy (t0->data, vlib_buffer_get_current (b0), len);
647         }
648
649       /* scan this bfd pkt. error0 is the counter index to bmp */
650       if (is_ipv6)
651         {
652           error0 = bfd_udp6_scan (vm, rt, b0, &bs);
653         }
654       else
655         {
656           error0 = bfd_udp4_scan (vm, rt, b0, &bs);
657         }
658       b0->error = rt->errors[error0];
659
660       next0 = BFD_UDP_INPUT_NEXT_NORMAL;
661       if (BFD_UDP_ERROR_NONE == error0)
662         {
663           /* if everything went fine, check for poll bit, if present, re-use
664              the buffer and based on (now updated) session parameters, send the
665              final packet back */
666           const bfd_pkt_t *pkt = vlib_buffer_get_current (b0);
667           if (bfd_pkt_get_poll (pkt))
668             {
669               bfd_send_final (vm, b0, bs);
670               if (is_ipv6)
671                 {
672                   vlib_node_increment_counter (vm, bfd_udp6_input_node.index,
673                                                b0->error, 1);
674                 }
675               else
676                 {
677                   vlib_node_increment_counter (vm, bfd_udp4_input_node.index,
678                                                b0->error, 1);
679                 }
680               next0 = BFD_UDP_INPUT_NEXT_REPLY;
681             }
682         }
683       vlib_set_next_frame_buffer (vm, rt, next0, bi0);
684
685       from += 1;
686       n_left_from -= 1;
687     }
688
689   return f->n_vectors;
690 }
691
692 static uword bfd_udp4_input (vlib_main_t *vm, vlib_node_runtime_t *rt,
693                              vlib_frame_t *f)
694 {
695   return bfd_udp_input (vm, rt, f, 0);
696 }
697
698 /*
699  * bfd input graph node declaration
700  */
701 /* *INDENT-OFF* */
702 VLIB_REGISTER_NODE (bfd_udp4_input_node, static) = {
703   .function = bfd_udp4_input,
704   .name = "bfd-udp4-input",
705   .vector_size = sizeof (u32),
706   .type = VLIB_NODE_TYPE_INTERNAL,
707
708   .n_errors = BFD_UDP_N_ERROR,
709   .error_strings = bfd_udp_error_strings,
710
711   .format_trace = bfd_input_format_trace,
712
713   .n_next_nodes = BFD_UDP_INPUT_N_NEXT,
714   .next_nodes =
715       {
716               [BFD_UDP_INPUT_NEXT_NORMAL] = "error-drop",
717               [BFD_UDP_INPUT_NEXT_REPLY] = "ip4-lookup",
718       },
719 };
720 /* *INDENT-ON* */
721
722 static uword bfd_udp6_input (vlib_main_t *vm, vlib_node_runtime_t *rt,
723                              vlib_frame_t *f)
724 {
725   return bfd_udp_input (vm, rt, f, 1);
726 }
727
728 /* *INDENT-OFF* */
729 VLIB_REGISTER_NODE (bfd_udp6_input_node, static) = {
730   .function = bfd_udp6_input,
731   .name = "bfd-udp6-input",
732   .vector_size = sizeof (u32),
733   .type = VLIB_NODE_TYPE_INTERNAL,
734
735   .n_errors = BFD_UDP_N_ERROR,
736   .error_strings = bfd_udp_error_strings,
737
738   .format_trace = bfd_input_format_trace,
739
740   .n_next_nodes = BFD_UDP_INPUT_N_NEXT,
741   .next_nodes =
742       {
743               [BFD_UDP_INPUT_NEXT_NORMAL] = "error-drop",
744               [BFD_UDP_INPUT_NEXT_REPLY] = "ip6-lookup",
745       },
746 };
747 /* *INDENT-ON* */
748
749 static clib_error_t *bfd_sw_interface_up_down (vnet_main_t *vnm,
750                                                u32 sw_if_index, u32 flags)
751 {
752   // vnet_hw_interface_t *hi = vnet_get_sup_hw_interface (vnm, sw_if_index);
753   if (!(flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP))
754     {
755       /* TODO */
756     }
757   return 0;
758 }
759
760 VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION (bfd_sw_interface_up_down);
761
762 static clib_error_t *bfd_hw_interface_up_down (vnet_main_t *vnm,
763                                                u32 hw_if_index, u32 flags)
764 {
765   if (flags & VNET_HW_INTERFACE_FLAG_LINK_UP)
766     {
767       /* TODO */
768     }
769   return 0;
770 }
771
772 VNET_HW_INTERFACE_LINK_UP_DOWN_FUNCTION (bfd_hw_interface_up_down);
773
774 /*
775  * setup function
776  */
777 static clib_error_t *bfd_udp_init (vlib_main_t *vm)
778 {
779   mhash_init (&bfd_udp_main.bfd_session_idx_by_bfd_key, sizeof (uword),
780               sizeof (bfd_udp_key_t));
781   bfd_udp_main.bfd_main = &bfd_main;
782   udp_register_dst_port (vm, UDP_DST_PORT_bfd4, bfd_udp4_input_node.index, 1);
783   udp_register_dst_port (vm, UDP_DST_PORT_bfd6, bfd_udp6_input_node.index, 0);
784   return 0;
785 }
786
787 VLIB_INIT_FUNCTION (bfd_udp_init);