BFD: SHA1 authentication
[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 = NULL;
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   ip6_udp_headers *headers = NULL;
86   vlib_buffer_advance (b, -sizeof (*headers));
87   headers = vlib_buffer_get_current (b);
88   memset (headers, 0, sizeof (*headers));
89   headers->ip6.ip_version_traffic_class_and_flow_label =
90     clib_host_to_net_u32 (0x6 << 28);
91   headers->ip6.hop_limit = 255;
92   headers->ip6.protocol = IP_PROTOCOL_UDP;
93   clib_memcpy (&headers->ip6.src_address, &key->local_addr.ip6,
94                sizeof (headers->ip6.src_address));
95   clib_memcpy (&headers->ip6.dst_address, &key->peer_addr.ip6,
96                sizeof (headers->ip6.dst_address));
97
98   headers->udp.src_port = clib_host_to_net_u16 (50000); /* FIXME */
99   headers->udp.dst_port = clib_host_to_net_u16 (UDP_DST_PORT_bfd6);
100
101   /* fix ip payload length and udp length */
102   const u16 udp_length =
103     vlib_buffer_length_in_chain (vm, b) - (sizeof (headers->ip6));
104   headers->udp.length = clib_host_to_net_u16 (udp_length);
105   headers->ip6.payload_length = headers->udp.length;
106
107   /* IPv6 UDP checksum is mandatory */
108   int bogus = 0;
109   headers->udp.checksum =
110     ip6_tcp_udp_icmp_compute_checksum (vm, b, &headers->ip6, &bogus);
111   ASSERT (bogus == 0);
112   if (headers->udp.checksum == 0)
113     {
114       headers->udp.checksum = 0xffff;
115     }
116 }
117
118 static bfd_session_t *
119 bfd_lookup_session (bfd_udp_main_t * bum, const bfd_udp_key_t * key)
120 {
121   uword *p = mhash_get (&bum->bfd_session_idx_by_bfd_key, key);
122   if (p)
123     {
124       return bfd_find_session_by_idx (bum->bfd_main, *p);
125     }
126   return 0;
127 }
128
129 static void
130 bfd_udp_key_init (bfd_udp_key_t * key, u32 sw_if_index,
131                   const ip46_address_t * local_addr,
132                   const ip46_address_t * peer_addr)
133 {
134   memset (key, 0, sizeof (*key));
135   key->sw_if_index = sw_if_index;
136   key->local_addr.as_u64[0] = local_addr->as_u64[0];
137   key->local_addr.as_u64[1] = local_addr->as_u64[1];
138   key->peer_addr.as_u64[0] = peer_addr->as_u64[0];
139   key->peer_addr.as_u64[1] = peer_addr->as_u64[1];
140 }
141
142 static vnet_api_error_t
143 bfd_udp_add_session_internal (bfd_udp_main_t * bum, u32 sw_if_index,
144                               u32 desired_min_tx_us, u32 required_min_rx_us,
145                               u8 detect_mult,
146                               const ip46_address_t * local_addr,
147                               const ip46_address_t * peer_addr,
148                               bfd_session_t ** bs_out)
149 {
150   /* get a pool entry and if we end up not needing it, give it back */
151   bfd_transport_t t = BFD_TRANSPORT_UDP4;
152   if (!ip46_address_is_ip4 (local_addr))
153     {
154       t = BFD_TRANSPORT_UDP6;
155     }
156   bfd_session_t *bs = bfd_get_session (bum->bfd_main, t);
157   bfd_udp_session_t *bus = &bs->udp;
158   memset (bus, 0, sizeof (*bus));
159   bfd_udp_key_t *key = &bus->key;
160   bfd_udp_key_init (key, sw_if_index, local_addr, peer_addr);
161   const bfd_session_t *tmp = bfd_lookup_session (bum, key);
162   if (tmp)
163     {
164       clib_warning ("duplicate bfd-udp session, existing bs_idx=%d",
165                     tmp->bs_idx);
166       bfd_put_session (bum->bfd_main, bs);
167       return VNET_API_ERROR_BFD_EEXIST;
168     }
169   mhash_set (&bum->bfd_session_idx_by_bfd_key, key, bs->bs_idx, NULL);
170   BFD_DBG ("session created, bs_idx=%u, sw_if_index=%d, local=%U, peer=%U",
171            bs->bs_idx, key->sw_if_index, format_ip46_address,
172            &key->local_addr, IP46_TYPE_ANY, format_ip46_address,
173            &key->peer_addr, IP46_TYPE_ANY);
174   if (BFD_TRANSPORT_UDP4 == t)
175     {
176       bus->adj_index = adj_nbr_add_or_lock (FIB_PROTOCOL_IP4, VNET_LINK_IP4,
177                                             &key->peer_addr,
178                                             key->sw_if_index);
179       BFD_DBG ("adj_nbr_add_or_lock(FIB_PROTOCOL_IP4, VNET_LINK_IP4, %U, %d) "
180                "returns %d", format_ip46_address, &key->peer_addr,
181                IP46_TYPE_ANY, key->sw_if_index, bus->adj_index);
182     }
183   else
184     {
185       bus->adj_index = adj_nbr_add_or_lock (FIB_PROTOCOL_IP6, VNET_LINK_IP6,
186                                             &key->peer_addr,
187                                             key->sw_if_index);
188       BFD_DBG ("adj_nbr_add_or_lock(FIB_PROTOCOL_IP6, VNET_LINK_IP6, %U, %d) "
189                "returns %d", format_ip46_address, &key->peer_addr,
190                IP46_TYPE_ANY, key->sw_if_index, bus->adj_index);
191     }
192   bs->config_desired_min_tx_us = desired_min_tx_us;
193   bs->required_min_rx_us = required_min_rx_us;
194   bs->required_min_echo_rx_us = required_min_rx_us;     /* FIXME */
195   bs->local_detect_mult = detect_mult;
196   *bs_out = bs;
197   return 0;
198 }
199
200 static vnet_api_error_t
201 bfd_udp_validate_api_input (u32 sw_if_index,
202                             const ip46_address_t * local_addr,
203                             const ip46_address_t * peer_addr)
204 {
205   vnet_sw_interface_t *sw_if =
206     vnet_get_sw_interface (vnet_get_main (), sw_if_index);
207   u8 local_ip_valid = 0;
208   ip_interface_address_t *ia = NULL;
209   if (!sw_if)
210     {
211       clib_warning ("got NULL sw_if");
212       return VNET_API_ERROR_INVALID_SW_IF_INDEX;
213     }
214   if (ip46_address_is_ip4 (local_addr))
215     {
216       if (!ip46_address_is_ip4 (peer_addr))
217         {
218           clib_warning ("IP family mismatch");
219           return VNET_API_ERROR_INVALID_ARGUMENT;
220         }
221       ip4_main_t *im = &ip4_main;
222
223       /* *INDENT-OFF* */
224       foreach_ip_interface_address (
225           &im->lookup_main, ia, sw_if_index, 0 /* honor unnumbered */, ({
226             ip4_address_t *x =
227                 ip_interface_address_get_address (&im->lookup_main, ia);
228             if (x->as_u32 == local_addr->ip4.as_u32)
229               {
230                 /* valid address for this interface */
231                 local_ip_valid = 1;
232                 break;
233               }
234           }));
235       /* *INDENT-ON* */
236     }
237   else
238     {
239       if (ip46_address_is_ip4 (peer_addr))
240         {
241           clib_warning ("IP family mismatch");
242           return VNET_API_ERROR_INVALID_ARGUMENT;
243         }
244       ip6_main_t *im = &ip6_main;
245       /* *INDENT-OFF* */
246       foreach_ip_interface_address (
247           &im->lookup_main, ia, sw_if_index, 0 /* honor unnumbered */, ({
248             ip6_address_t *x =
249                 ip_interface_address_get_address (&im->lookup_main, ia);
250             if (local_addr->ip6.as_u64[0] == x->as_u64[0] &&
251                 local_addr->ip6.as_u64[1] == x->as_u64[1])
252               {
253                 /* valid address for this interface */
254                 local_ip_valid = 1;
255                 break;
256               }
257           }));
258       /* *INDENT-ON* */
259     }
260
261   if (!local_ip_valid)
262     {
263       clib_warning ("address not found on interface");
264       return VNET_API_ERROR_ADDRESS_NOT_FOUND_FOR_INTERFACE;
265     }
266
267   return 0;
268 }
269
270 static vnet_api_error_t
271 bfd_udp_find_session_by_api_input (u32 sw_if_index,
272                                    const ip46_address_t * local_addr,
273                                    const ip46_address_t * peer_addr,
274                                    bfd_session_t ** bs_out)
275 {
276   vnet_api_error_t rv =
277     bfd_udp_validate_api_input (sw_if_index, local_addr, peer_addr);
278   if (!rv)
279     {
280       bfd_udp_main_t *bum = &bfd_udp_main;
281       bfd_udp_key_t key;
282       bfd_udp_key_init (&key, sw_if_index, local_addr, peer_addr);
283       bfd_session_t *bs = bfd_lookup_session (bum, &key);
284       if (bs)
285         {
286           *bs_out = bs;
287         }
288       else
289         {
290           clib_warning
291             ("BFD session not found (sw_if_index=%u, local=%U, peer=%U",
292              sw_if_index, format_ip46_address, local_addr, IP46_TYPE_ANY,
293              format_ip46_address, peer_addr, IP46_TYPE_ANY);
294           return VNET_API_ERROR_BFD_ENOENT;
295         }
296     }
297   return rv;
298 }
299
300 static vnet_api_error_t
301 bfd_api_verify_common (u32 sw_if_index, u32 desired_min_tx_us,
302                        u32 required_min_rx_us, u8 detect_mult,
303                        const ip46_address_t * local_addr,
304                        const ip46_address_t * peer_addr)
305 {
306   vnet_api_error_t rv =
307     bfd_udp_validate_api_input (sw_if_index, local_addr, peer_addr);
308   if (rv)
309     {
310       return rv;
311     }
312   if (detect_mult < 1)
313     {
314       clib_warning ("detect_mult < 1");
315       return VNET_API_ERROR_INVALID_ARGUMENT;
316     }
317   if (desired_min_tx_us < 1)
318     {
319       clib_warning ("desired_min_tx_us < 1");
320       return VNET_API_ERROR_INVALID_ARGUMENT;
321     }
322   return 0;
323 }
324
325 static void
326 bfd_udp_del_session_internal (bfd_session_t * bs)
327 {
328   bfd_udp_main_t *bum = &bfd_udp_main;
329   BFD_DBG ("free bfd-udp session, bs_idx=%d", bs->bs_idx);
330   mhash_unset (&bum->bfd_session_idx_by_bfd_key, &bs->udp.key, NULL);
331   adj_unlock (bs->udp.adj_index);
332   bfd_put_session (bum->bfd_main, bs);
333 }
334
335 vnet_api_error_t
336 bfd_udp_add_session (u32 sw_if_index, const ip46_address_t * local_addr,
337                      const ip46_address_t * peer_addr, u32 desired_min_tx_us,
338                      u32 required_min_rx_us, u8 detect_mult,
339                      u8 is_authenticated, u32 conf_key_id, u8 bfd_key_id)
340 {
341   vnet_api_error_t rv = bfd_api_verify_common (sw_if_index, desired_min_tx_us,
342                                                required_min_rx_us,
343                                                detect_mult,
344                                                local_addr, peer_addr);
345   bfd_session_t *bs = NULL;
346   if (!rv)
347     {
348       rv =
349         bfd_udp_add_session_internal (&bfd_udp_main, sw_if_index,
350                                       desired_min_tx_us, required_min_rx_us,
351                                       detect_mult, local_addr, peer_addr,
352                                       &bs);
353     }
354   if (!rv && is_authenticated)
355     {
356 #if WITH_LIBSSL > 0
357       rv = bfd_auth_activate (bs, conf_key_id, bfd_key_id,
358                               0 /* is not delayed */ );
359 #else
360       clib_warning ("SSL missing, cannot add authenticated BFD session");
361       rv = VNET_API_ERROR_BFD_NOTSUPP;
362 #endif
363       if (rv)
364         {
365           bfd_udp_del_session_internal (bs);
366         }
367     }
368   if (!rv)
369     {
370       bfd_session_start (bfd_udp_main.bfd_main, bs);
371     }
372
373   return rv;
374 }
375
376 vnet_api_error_t
377 bfd_udp_del_session (u32 sw_if_index,
378                      const ip46_address_t * local_addr,
379                      const ip46_address_t * peer_addr)
380 {
381   bfd_session_t *bs = NULL;
382   vnet_api_error_t rv =
383     bfd_udp_find_session_by_api_input (sw_if_index, local_addr, peer_addr,
384                                        &bs);
385   if (rv)
386     {
387       return rv;
388     }
389   bfd_udp_del_session_internal (bs);
390   return 0;
391 }
392
393 vnet_api_error_t
394 bfd_udp_session_set_flags (u32 sw_if_index,
395                            const ip46_address_t * local_addr,
396                            const ip46_address_t * peer_addr, u8 admin_up_down)
397 {
398   bfd_session_t *bs = NULL;
399   vnet_api_error_t rv =
400     bfd_udp_find_session_by_api_input (sw_if_index, local_addr, peer_addr,
401                                        &bs);
402   if (rv)
403     {
404       return rv;
405     }
406   bfd_session_set_flags (bs, admin_up_down);
407   return 0;
408 }
409
410 vnet_api_error_t
411 bfd_auth_set_key (u32 conf_key_id, u8 auth_type, u8 key_len,
412                   const u8 * key_data)
413 {
414 #if WITH_LIBSSL > 0
415   bfd_auth_key_t *auth_key = NULL;
416   if (!key_len || key_len > bfd_max_len_for_auth_type (auth_type))
417     {
418       clib_warning ("Invalid authentication key length for auth_type=%d:%s "
419                     "(key_len=%u, must be "
420                     "non-zero, expected max=%u)",
421                     auth_type, bfd_auth_type_str (auth_type), key_len,
422                     (u32) bfd_max_len_for_auth_type (auth_type));
423       return VNET_API_ERROR_INVALID_VALUE;
424     }
425   if (!bfd_auth_type_supported (auth_type))
426     {
427       clib_warning ("Unsupported auth type=%d:%s", auth_type,
428                     bfd_auth_type_str (auth_type));
429       return VNET_API_ERROR_BFD_NOTSUPP;
430     }
431   bfd_main_t *bm = bfd_udp_main.bfd_main;
432   uword *key_idx_p = hash_get (bm->auth_key_by_conf_key_id, conf_key_id);
433   if (key_idx_p)
434     {
435       /* modifying existing key - must not be used */
436       const uword key_idx = *key_idx_p;
437       auth_key = pool_elt_at_index (bm->auth_keys, key_idx);
438       if (auth_key->use_count > 0)
439         {
440           clib_warning ("Authentication key with conf ID %u in use by %u BFD "
441                         "sessions - cannot modify",
442                         conf_key_id, auth_key->use_count);
443           return VNET_API_ERROR_BFD_EINUSE;
444         }
445     }
446   else
447     {
448       /* adding new key */
449       pool_get (bm->auth_keys, auth_key);
450       auth_key->conf_key_id = conf_key_id;
451       hash_set (bm->auth_key_by_conf_key_id, conf_key_id,
452                 auth_key - bm->auth_keys);
453     }
454   auth_key->auth_type = auth_type;
455   memset (auth_key->key, 0, sizeof (auth_key->key));
456   clib_memcpy (auth_key->key, key_data, key_len);
457   return 0;
458 #else
459   clib_warning ("SSL missing, cannot manipulate authentication keys");
460   return VNET_API_ERROR_BFD_NOTSUPP;
461 #endif
462 }
463
464 vnet_api_error_t
465 bfd_auth_del_key (u32 conf_key_id)
466 {
467 #if WITH_LIBSSL > 0
468   bfd_auth_key_t *auth_key = NULL;
469   bfd_main_t *bm = bfd_udp_main.bfd_main;
470   uword *key_idx_p = hash_get (bm->auth_key_by_conf_key_id, conf_key_id);
471   if (key_idx_p)
472     {
473       /* deleting existing key - must not be used */
474       const uword key_idx = *key_idx_p;
475       auth_key = pool_elt_at_index (bm->auth_keys, key_idx);
476       if (auth_key->use_count > 0)
477         {
478           clib_warning ("Authentication key with conf ID %u in use by %u BFD "
479                         "sessions - cannot delete",
480                         conf_key_id, auth_key->use_count);
481           return VNET_API_ERROR_BFD_EINUSE;
482         }
483       hash_unset (bm->auth_key_by_conf_key_id, conf_key_id);
484       memset (auth_key, 0, sizeof (*auth_key));
485       pool_put (bm->auth_keys, auth_key);
486     }
487   else
488     {
489       /* no such key */
490       clib_warning ("Authentication key with conf ID %u does not exist",
491                     conf_key_id);
492       return VNET_API_ERROR_BFD_ENOENT;
493     }
494   return 0;
495 #else
496   clib_warning ("SSL missing, cannot manipulate authentication keys");
497   return VNET_API_ERROR_BFD_NOTSUPP;
498 #endif
499 }
500
501 vnet_api_error_t
502 bfd_udp_auth_activate (u32 sw_if_index,
503                        const ip46_address_t * local_addr,
504                        const ip46_address_t * peer_addr,
505                        u32 conf_key_id, u8 key_id, u8 is_delayed)
506 {
507 #if WITH_LIBSSL > 0
508   bfd_session_t *bs = NULL;
509   vnet_api_error_t rv =
510     bfd_udp_find_session_by_api_input (sw_if_index, local_addr, peer_addr,
511                                        &bs);
512   if (rv)
513     {
514       return rv;
515     }
516   return bfd_auth_activate (bs, conf_key_id, key_id, is_delayed);
517 #else
518   clib_warning ("SSL missing, cannot activate BFD authentication");
519   return VNET_API_ERROR_BFD_NOTSUPP;
520 #endif
521 }
522
523 vnet_api_error_t
524 bfd_udp_auth_deactivate (u32 sw_if_index,
525                          const ip46_address_t * local_addr,
526                          const ip46_address_t * peer_addr, u8 is_delayed)
527 {
528   bfd_session_t *bs = NULL;
529   vnet_api_error_t rv =
530     bfd_udp_find_session_by_api_input (sw_if_index, local_addr, peer_addr,
531                                        &bs);
532   if (rv)
533     {
534       return rv;
535     }
536   return bfd_auth_deactivate (bs, is_delayed);
537 }
538
539 typedef enum
540 {
541   BFD_UDP_INPUT_NEXT_NORMAL,
542   BFD_UDP_INPUT_NEXT_REPLY,
543   BFD_UDP_INPUT_N_NEXT,
544 } bfd_udp_input_next_t;
545
546 /* Packet counters */
547 #define foreach_bfd_udp_error(F)           \
548   F (NONE, "good bfd packets (processed)") \
549   F (BAD, "invalid bfd packets")           \
550   F (DISABLED, "bfd packets received on disabled interfaces")
551
552 #define F(sym, string) static char BFD_UDP_ERR_##sym##_STR[] = string;
553 foreach_bfd_udp_error (F);
554 #undef F
555
556 static char *bfd_udp_error_strings[] = {
557 #define F(sym, string) BFD_UDP_ERR_##sym##_STR,
558   foreach_bfd_udp_error (F)
559 #undef F
560 };
561
562 typedef enum
563 {
564 #define F(sym, str) BFD_UDP_ERROR_##sym,
565   foreach_bfd_udp_error (F)
566 #undef F
567     BFD_UDP_N_ERROR,
568 } bfd_udp_error_t;
569
570 static void
571 bfd_udp4_find_headers (vlib_buffer_t * b, const ip4_header_t ** ip4,
572                        const udp_header_t ** udp)
573 {
574   /* sanity check first */
575   const i32 start = vnet_buffer (b)->ip.start_of_ip_header;
576   if (start < 0 && start < sizeof (b->pre_data))
577     {
578       BFD_ERR ("Start of ip header is before pre_data, ignoring");
579       *ip4 = NULL;
580       *udp = NULL;
581       return;
582     }
583   *ip4 = (ip4_header_t *) (b->data + start);
584   if ((u8 *) * ip4 > (u8 *) vlib_buffer_get_current (b))
585     {
586       BFD_ERR ("Start of ip header is beyond current data, ignoring");
587       *ip4 = NULL;
588       *udp = NULL;
589       return;
590     }
591   *udp = (udp_header_t *) ((*ip4) + 1);
592 }
593
594 static bfd_udp_error_t
595 bfd_udp4_verify_transport (const ip4_header_t * ip4,
596                            const udp_header_t * udp, const bfd_session_t * bs)
597 {
598   const bfd_udp_session_t *bus = &bs->udp;
599   const bfd_udp_key_t *key = &bus->key;
600   if (ip4->src_address.as_u32 != key->peer_addr.ip4.as_u32)
601     {
602       BFD_ERR ("IPv4 src addr mismatch, got %U, expected %U",
603                format_ip4_address, ip4->src_address.as_u8, format_ip4_address,
604                key->peer_addr.ip4.as_u8);
605       return BFD_UDP_ERROR_BAD;
606     }
607   if (ip4->dst_address.as_u32 != key->local_addr.ip4.as_u32)
608     {
609       BFD_ERR ("IPv4 dst addr mismatch, got %U, expected %U",
610                format_ip4_address, ip4->dst_address.as_u8, format_ip4_address,
611                key->local_addr.ip4.as_u8);
612       return BFD_UDP_ERROR_BAD;
613     }
614   const u8 expected_ttl = 255;
615   if (ip4->ttl != expected_ttl)
616     {
617       BFD_ERR ("IPv4 unexpected TTL value %u, expected %u", ip4->ttl,
618                expected_ttl);
619       return BFD_UDP_ERROR_BAD;
620     }
621   if (clib_net_to_host_u16 (udp->src_port) < 49152 ||
622       clib_net_to_host_u16 (udp->src_port) > 65535)
623     {
624       BFD_ERR ("Invalid UDP src port %u, out of range <49152,65535>",
625                udp->src_port);
626     }
627   return BFD_UDP_ERROR_NONE;
628 }
629
630 typedef struct
631 {
632   u32 bs_idx;
633   bfd_pkt_t pkt;
634 } bfd_rpc_update_t;
635
636 static void
637 bfd_rpc_update_session_cb (const bfd_rpc_update_t * a)
638 {
639   bfd_consume_pkt (bfd_udp_main.bfd_main, &a->pkt, a->bs_idx);
640 }
641
642 static void
643 bfd_rpc_update_session (u32 bs_idx, const bfd_pkt_t * pkt)
644 {
645   /* packet length was already verified to be correct by the caller */
646   const u32 data_size = sizeof (bfd_rpc_update_t) -
647     STRUCT_SIZE_OF (bfd_rpc_update_t, pkt) + pkt->head.length;
648   u8 data[data_size];
649   bfd_rpc_update_t *update = (bfd_rpc_update_t *) data;
650   update->bs_idx = bs_idx;
651   clib_memcpy (&update->pkt, pkt, pkt->head.length);
652   vl_api_rpc_call_main_thread (bfd_rpc_update_session_cb, data, data_size);
653 }
654
655 static bfd_udp_error_t
656 bfd_udp4_scan (vlib_main_t * vm, vlib_node_runtime_t * rt,
657                vlib_buffer_t * b, bfd_session_t ** bs_out)
658 {
659   const bfd_pkt_t *pkt = vlib_buffer_get_current (b);
660   if (sizeof (*pkt) > b->current_length)
661     {
662       BFD_ERR
663         ("Payload size %d too small to hold bfd packet of minimum size %d",
664          b->current_length, sizeof (*pkt));
665       return BFD_UDP_ERROR_BAD;
666     }
667   const ip4_header_t *ip4;
668   const udp_header_t *udp;
669   bfd_udp4_find_headers (b, &ip4, &udp);
670   if (!ip4 || !udp)
671     {
672       BFD_ERR ("Couldn't find ip4 or udp header");
673       return BFD_UDP_ERROR_BAD;
674     }
675   const u32 udp_payload_length = udp->length - sizeof (*udp);
676   if (pkt->head.length > udp_payload_length)
677     {
678       BFD_ERR
679         ("BFD packet length is larger than udp payload length (%u > %u)",
680          pkt->head.length, udp_payload_length);
681       return BFD_UDP_ERROR_BAD;
682     }
683   if (!bfd_verify_pkt_common (pkt))
684     {
685       return BFD_UDP_ERROR_BAD;
686     }
687   bfd_session_t *bs = NULL;
688   if (pkt->your_disc)
689     {
690       BFD_DBG ("Looking up BFD session using discriminator %u",
691                pkt->your_disc);
692       bs = bfd_find_session_by_disc (bfd_udp_main.bfd_main, pkt->your_disc);
693     }
694   else
695     {
696       bfd_udp_key_t key;
697       memset (&key, 0, sizeof (key));
698       key.sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
699       key.local_addr.ip4.as_u32 = ip4->dst_address.as_u32;
700       key.peer_addr.ip4.as_u32 = ip4->src_address.as_u32;
701       BFD_DBG ("Looking up BFD session using key (sw_if_index=%u, local=%U, "
702                "peer=%U)",
703                key.sw_if_index, format_ip4_address, key.local_addr.ip4.as_u8,
704                format_ip4_address, key.peer_addr.ip4.as_u8);
705       bs = bfd_lookup_session (&bfd_udp_main, &key);
706     }
707   if (!bs)
708     {
709       BFD_ERR ("BFD session lookup failed - no session matches BFD pkt");
710       return BFD_UDP_ERROR_BAD;
711     }
712   BFD_DBG ("BFD session found, bs_idx=%u", bs->bs_idx);
713   if (!bfd_verify_pkt_auth (pkt, b->current_length, bs))
714     {
715       BFD_ERR ("Packet verification failed, dropping packet");
716       return BFD_UDP_ERROR_BAD;
717     }
718   bfd_udp_error_t err;
719   if (BFD_UDP_ERROR_NONE != (err = bfd_udp4_verify_transport (ip4, udp, bs)))
720     {
721       return err;
722     }
723   bfd_rpc_update_session (bs->bs_idx, pkt);
724   *bs_out = bs;
725   return BFD_UDP_ERROR_NONE;
726 }
727
728 static void
729 bfd_udp6_find_headers (vlib_buffer_t * b, const ip6_header_t ** ip6,
730                        const udp_header_t ** udp)
731 {
732   /* sanity check first */
733   const i32 start = vnet_buffer (b)->ip.start_of_ip_header;
734   if (start < 0 && start < sizeof (b->pre_data))
735     {
736       BFD_ERR ("Start of ip header is before pre_data, ignoring");
737       *ip6 = NULL;
738       *udp = NULL;
739       return;
740     }
741   *ip6 = (ip6_header_t *) (b->data + start);
742   if ((u8 *) * ip6 > (u8 *) vlib_buffer_get_current (b))
743     {
744       BFD_ERR ("Start of ip header is beyond current data, ignoring");
745       *ip6 = NULL;
746       *udp = NULL;
747       return;
748     }
749   if ((*ip6)->protocol != IP_PROTOCOL_UDP)
750     {
751       BFD_ERR ("Unexpected protocol in IPv6 header '%u', expected '%u' (== "
752                "IP_PROTOCOL_UDP)", (*ip6)->protocol, IP_PROTOCOL_UDP);
753       *ip6 = NULL;
754       *udp = NULL;
755       return;
756     }
757   *udp = (udp_header_t *) ((*ip6) + 1);
758 }
759
760 static bfd_udp_error_t
761 bfd_udp6_verify_transport (const ip6_header_t * ip6,
762                            const udp_header_t * udp, const bfd_session_t * bs)
763 {
764   const bfd_udp_session_t *bus = &bs->udp;
765   const bfd_udp_key_t *key = &bus->key;
766   if (ip6->src_address.as_u64[0] != key->peer_addr.ip6.as_u64[0] &&
767       ip6->src_address.as_u64[1] != key->peer_addr.ip6.as_u64[1])
768     {
769       BFD_ERR ("IP src addr mismatch, got %U, expected %U",
770                format_ip6_address, ip6, format_ip6_address,
771                &key->peer_addr.ip6);
772       return BFD_UDP_ERROR_BAD;
773     }
774   if (ip6->dst_address.as_u64[0] != key->local_addr.ip6.as_u64[0] &&
775       ip6->dst_address.as_u64[1] != key->local_addr.ip6.as_u64[1])
776     {
777       BFD_ERR ("IP dst addr mismatch, got %U, expected %U",
778                format_ip6_address, ip6, format_ip6_address,
779                &key->local_addr.ip6);
780       return BFD_UDP_ERROR_BAD;
781     }
782   const u8 expected_hop_limit = 255;
783   if (ip6->hop_limit != expected_hop_limit)
784     {
785       BFD_ERR ("IPv6 unexpected hop-limit value %u, expected %u",
786                ip6->hop_limit, expected_hop_limit);
787       return BFD_UDP_ERROR_BAD;
788     }
789   if (clib_net_to_host_u16 (udp->src_port) < 49152 ||
790       clib_net_to_host_u16 (udp->src_port) > 65535)
791     {
792       BFD_ERR ("Invalid UDP src port %u, out of range <49152,65535>",
793                udp->src_port);
794     }
795   return BFD_UDP_ERROR_NONE;
796 }
797
798 static bfd_udp_error_t
799 bfd_udp6_scan (vlib_main_t * vm, vlib_node_runtime_t * rt,
800                vlib_buffer_t * b, bfd_session_t ** bs_out)
801 {
802   const bfd_pkt_t *pkt = vlib_buffer_get_current (b);
803   if (sizeof (*pkt) > b->current_length)
804     {
805       BFD_ERR
806         ("Payload size %d too small to hold bfd packet of minimum size %d",
807          b->current_length, sizeof (*pkt));
808       return BFD_UDP_ERROR_BAD;
809     }
810   const ip6_header_t *ip6;
811   const udp_header_t *udp;
812   bfd_udp6_find_headers (b, &ip6, &udp);
813   if (!ip6 || !udp)
814     {
815       BFD_ERR ("Couldn't find ip6 or udp header");
816       return BFD_UDP_ERROR_BAD;
817     }
818   const u32 udp_payload_length = udp->length - sizeof (*udp);
819   if (pkt->head.length > udp_payload_length)
820     {
821       BFD_ERR
822         ("BFD packet length is larger than udp payload length (%u > %u)",
823          pkt->head.length, udp_payload_length);
824       return BFD_UDP_ERROR_BAD;
825     }
826   if (!bfd_verify_pkt_common (pkt))
827     {
828       return BFD_UDP_ERROR_BAD;
829     }
830   bfd_session_t *bs = NULL;
831   if (pkt->your_disc)
832     {
833       BFD_DBG ("Looking up BFD session using discriminator %u",
834                pkt->your_disc);
835       bs = bfd_find_session_by_disc (bfd_udp_main.bfd_main, pkt->your_disc);
836     }
837   else
838     {
839       bfd_udp_key_t key;
840       memset (&key, 0, sizeof (key));
841       key.sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
842       key.local_addr.ip6.as_u64[0] = ip6->dst_address.as_u64[0];
843       key.local_addr.ip6.as_u64[1] = ip6->dst_address.as_u64[1];
844       key.peer_addr.ip6.as_u64[0] = ip6->src_address.as_u64[0];
845       key.peer_addr.ip6.as_u64[1] = ip6->src_address.as_u64[1];
846       BFD_DBG ("Looking up BFD session using key (sw_if_index=%u, local=%U, "
847                "peer=%U)",
848                key.sw_if_index, format_ip6_address, &key.local_addr,
849                format_ip6_address, &key.peer_addr);
850       bs = bfd_lookup_session (&bfd_udp_main, &key);
851     }
852   if (!bs)
853     {
854       BFD_ERR ("BFD session lookup failed - no session matches BFD pkt");
855       return BFD_UDP_ERROR_BAD;
856     }
857   BFD_DBG ("BFD session found, bs_idx=%u", bs->bs_idx);
858   if (!bfd_verify_pkt_auth (pkt, b->current_length, bs))
859     {
860       BFD_ERR ("Packet verification failed, dropping packet");
861       return BFD_UDP_ERROR_BAD;
862     }
863   bfd_udp_error_t err;
864   if (BFD_UDP_ERROR_NONE != (err = bfd_udp6_verify_transport (ip6, udp, bs)))
865     {
866       return err;
867     }
868   bfd_rpc_update_session (bs->bs_idx, pkt);
869   *bs_out = bs;
870   return BFD_UDP_ERROR_NONE;
871 }
872
873 /*
874  * Process a frame of bfd packets
875  * Expect 1 packet / frame
876  */
877 static uword
878 bfd_udp_input (vlib_main_t * vm, vlib_node_runtime_t * rt,
879                vlib_frame_t * f, int is_ipv6)
880 {
881   u32 n_left_from, *from;
882   bfd_input_trace_t *t0;
883
884   from = vlib_frame_vector_args (f);    /* array of buffer indices */
885   n_left_from = f->n_vectors;   /* number of buffer indices */
886
887   while (n_left_from > 0)
888     {
889       u32 bi0;
890       vlib_buffer_t *b0;
891       u32 next0, error0;
892
893       bi0 = from[0];
894       b0 = vlib_get_buffer (vm, bi0);
895
896       bfd_session_t *bs = NULL;
897
898       /* If this pkt is traced, snapshot the data */
899       if (b0->flags & VLIB_BUFFER_IS_TRACED)
900         {
901           int len;
902           t0 = vlib_add_trace (vm, rt, b0, sizeof (*t0));
903           len = (b0->current_length < sizeof (t0->data)) ? b0->current_length
904             : sizeof (t0->data);
905           t0->len = len;
906           clib_memcpy (t0->data, vlib_buffer_get_current (b0), len);
907         }
908
909       /* scan this bfd pkt. error0 is the counter index to bmp */
910       if (is_ipv6)
911         {
912           error0 = bfd_udp6_scan (vm, rt, b0, &bs);
913         }
914       else
915         {
916           error0 = bfd_udp4_scan (vm, rt, b0, &bs);
917         }
918       b0->error = rt->errors[error0];
919
920       next0 = BFD_UDP_INPUT_NEXT_NORMAL;
921       if (BFD_UDP_ERROR_NONE == error0)
922         {
923           /*
924            *  if everything went fine, check for poll bit, if present, re-use
925            *  the buffer and based on (now updated) session parameters, send
926            *  the final packet back
927            */
928           const bfd_pkt_t *pkt = vlib_buffer_get_current (b0);
929           if (bfd_pkt_get_poll (pkt))
930             {
931               bfd_init_final_control_frame (vm, b0, bs);
932               if (is_ipv6)
933                 {
934                   vlib_node_increment_counter (vm, bfd_udp6_input_node.index,
935                                                b0->error, 1);
936                 }
937               else
938                 {
939                   vlib_node_increment_counter (vm, bfd_udp4_input_node.index,
940                                                b0->error, 1);
941                 }
942               next0 = BFD_UDP_INPUT_NEXT_REPLY;
943             }
944         }
945       vlib_set_next_frame_buffer (vm, rt, next0, bi0);
946
947       from += 1;
948       n_left_from -= 1;
949     }
950
951   return f->n_vectors;
952 }
953
954 static uword
955 bfd_udp4_input (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f)
956 {
957   return bfd_udp_input (vm, rt, f, 0);
958 }
959
960 /*
961  * bfd input graph node declaration
962  */
963 /* *INDENT-OFF* */
964 VLIB_REGISTER_NODE (bfd_udp4_input_node, static) = {
965   .function = bfd_udp4_input,
966   .name = "bfd-udp4-input",
967   .vector_size = sizeof (u32),
968   .type = VLIB_NODE_TYPE_INTERNAL,
969
970   .n_errors = BFD_UDP_N_ERROR,
971   .error_strings = bfd_udp_error_strings,
972
973   .format_trace = bfd_input_format_trace,
974
975   .n_next_nodes = BFD_UDP_INPUT_N_NEXT,
976   .next_nodes =
977       {
978               [BFD_UDP_INPUT_NEXT_NORMAL] = "error-drop",
979               [BFD_UDP_INPUT_NEXT_REPLY] = "ip4-lookup",
980       },
981 };
982 /* *INDENT-ON* */
983
984 static uword
985 bfd_udp6_input (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f)
986 {
987   return bfd_udp_input (vm, rt, f, 1);
988 }
989
990 /* *INDENT-OFF* */
991 VLIB_REGISTER_NODE (bfd_udp6_input_node, static) = {
992   .function = bfd_udp6_input,
993   .name = "bfd-udp6-input",
994   .vector_size = sizeof (u32),
995   .type = VLIB_NODE_TYPE_INTERNAL,
996
997   .n_errors = BFD_UDP_N_ERROR,
998   .error_strings = bfd_udp_error_strings,
999
1000   .format_trace = bfd_input_format_trace,
1001
1002   .n_next_nodes = BFD_UDP_INPUT_N_NEXT,
1003   .next_nodes =
1004       {
1005               [BFD_UDP_INPUT_NEXT_NORMAL] = "error-drop",
1006               [BFD_UDP_INPUT_NEXT_REPLY] = "ip6-lookup",
1007       },
1008 };
1009 /* *INDENT-ON* */
1010
1011 static clib_error_t *
1012 bfd_sw_interface_up_down (vnet_main_t * vnm, u32 sw_if_index, u32 flags)
1013 {
1014   // vnet_hw_interface_t *hi = vnet_get_sup_hw_interface (vnm, sw_if_index);
1015   if (!(flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP))
1016     {
1017       /* TODO */
1018     }
1019   return 0;
1020 }
1021
1022 VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION (bfd_sw_interface_up_down);
1023
1024 static clib_error_t *
1025 bfd_hw_interface_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
1026 {
1027   if (flags & VNET_HW_INTERFACE_FLAG_LINK_UP)
1028     {
1029       /* TODO */
1030     }
1031   return 0;
1032 }
1033
1034 VNET_HW_INTERFACE_LINK_UP_DOWN_FUNCTION (bfd_hw_interface_up_down);
1035
1036 /*
1037  * setup function
1038  */
1039 static clib_error_t *
1040 bfd_udp_init (vlib_main_t * vm)
1041 {
1042   mhash_init (&bfd_udp_main.bfd_session_idx_by_bfd_key, sizeof (uword),
1043               sizeof (bfd_udp_key_t));
1044   bfd_udp_main.bfd_main = &bfd_main;
1045   udp_register_dst_port (vm, UDP_DST_PORT_bfd4, bfd_udp4_input_node.index, 1);
1046   udp_register_dst_port (vm, UDP_DST_PORT_bfd6, bfd_udp6_input_node.index, 0);
1047   return 0;
1048 }
1049
1050 VLIB_INIT_FUNCTION (bfd_udp_init);
1051
1052 /*
1053  * fd.io coding-style-patch-verification: ON
1054  *
1055  * Local Variables:
1056  * eval: (c-set-style "gnu")
1057  * End:
1058  */