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