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