e21b887c47e3b82532d41da4060e9aca6e9e4941
[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       clib_net_to_host_u16 (udp->src_port) > 65535)
642     {
643       BFD_ERR ("Invalid UDP src port %u, out of range <49152,65535>",
644                udp->src_port);
645     }
646   return BFD_UDP_ERROR_NONE;
647 }
648
649 typedef struct
650 {
651   u32 bs_idx;
652   bfd_pkt_t pkt;
653 } bfd_rpc_update_t;
654
655 static void
656 bfd_rpc_update_session_cb (const bfd_rpc_update_t * a)
657 {
658   bfd_consume_pkt (bfd_udp_main.bfd_main, &a->pkt, a->bs_idx);
659 }
660
661 static void
662 bfd_rpc_update_session (u32 bs_idx, const bfd_pkt_t * pkt)
663 {
664   /* packet length was already verified to be correct by the caller */
665   const u32 data_size = sizeof (bfd_rpc_update_t) -
666     STRUCT_SIZE_OF (bfd_rpc_update_t, pkt) + pkt->head.length;
667   u8 data[data_size];
668   bfd_rpc_update_t *update = (bfd_rpc_update_t *) data;
669   update->bs_idx = bs_idx;
670   clib_memcpy (&update->pkt, pkt, pkt->head.length);
671   vl_api_rpc_call_main_thread (bfd_rpc_update_session_cb, data, data_size);
672 }
673
674 static bfd_udp_error_t
675 bfd_udp4_scan (vlib_main_t * vm, vlib_node_runtime_t * rt,
676                vlib_buffer_t * b, bfd_session_t ** bs_out)
677 {
678   const bfd_pkt_t *pkt = vlib_buffer_get_current (b);
679   if (sizeof (*pkt) > b->current_length)
680     {
681       BFD_ERR
682         ("Payload size %d too small to hold bfd packet of minimum size %d",
683          b->current_length, sizeof (*pkt));
684       return BFD_UDP_ERROR_BAD;
685     }
686   const ip4_header_t *ip4;
687   const udp_header_t *udp;
688   bfd_udp4_find_headers (b, &ip4, &udp);
689   if (!ip4 || !udp)
690     {
691       BFD_ERR ("Couldn't find ip4 or udp header");
692       return BFD_UDP_ERROR_BAD;
693     }
694   const u32 udp_payload_length = udp->length - sizeof (*udp);
695   if (pkt->head.length > udp_payload_length)
696     {
697       BFD_ERR
698         ("BFD packet length is larger than udp payload length (%u > %u)",
699          pkt->head.length, udp_payload_length);
700       return BFD_UDP_ERROR_BAD;
701     }
702   if (!bfd_verify_pkt_common (pkt))
703     {
704       return BFD_UDP_ERROR_BAD;
705     }
706   bfd_session_t *bs = NULL;
707   if (pkt->your_disc)
708     {
709       BFD_DBG ("Looking up BFD session using discriminator %u",
710                pkt->your_disc);
711       bs = bfd_find_session_by_disc (bfd_udp_main.bfd_main, pkt->your_disc);
712     }
713   else
714     {
715       bfd_udp_key_t key;
716       memset (&key, 0, sizeof (key));
717       key.sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
718       key.local_addr.ip4.as_u32 = ip4->dst_address.as_u32;
719       key.peer_addr.ip4.as_u32 = ip4->src_address.as_u32;
720       BFD_DBG ("Looking up BFD session using key (sw_if_index=%u, local=%U, "
721                "peer=%U)",
722                key.sw_if_index, format_ip4_address, key.local_addr.ip4.as_u8,
723                format_ip4_address, key.peer_addr.ip4.as_u8);
724       bs = bfd_lookup_session (&bfd_udp_main, &key);
725     }
726   if (!bs)
727     {
728       BFD_ERR ("BFD session lookup failed - no session matches BFD pkt");
729       return BFD_UDP_ERROR_BAD;
730     }
731   BFD_DBG ("BFD session found, bs_idx=%u", bs->bs_idx);
732   if (!bfd_verify_pkt_auth (pkt, b->current_length, bs))
733     {
734       BFD_ERR ("Packet verification failed, dropping packet");
735       return BFD_UDP_ERROR_BAD;
736     }
737   bfd_udp_error_t err;
738   if (BFD_UDP_ERROR_NONE != (err = bfd_udp4_verify_transport (ip4, udp, bs)))
739     {
740       return err;
741     }
742   bfd_rpc_update_session (bs->bs_idx, pkt);
743   *bs_out = bs;
744   return BFD_UDP_ERROR_NONE;
745 }
746
747 static void
748 bfd_udp6_find_headers (vlib_buffer_t * b, const ip6_header_t ** ip6,
749                        const udp_header_t ** udp)
750 {
751   /* sanity check first */
752   const i32 start = vnet_buffer (b)->ip.start_of_ip_header;
753   if (start < 0 && start < sizeof (b->pre_data))
754     {
755       BFD_ERR ("Start of ip header is before pre_data, ignoring");
756       *ip6 = NULL;
757       *udp = NULL;
758       return;
759     }
760   *ip6 = (ip6_header_t *) (b->data + start);
761   if ((u8 *) * ip6 > (u8 *) vlib_buffer_get_current (b))
762     {
763       BFD_ERR ("Start of ip header is beyond current data, ignoring");
764       *ip6 = NULL;
765       *udp = NULL;
766       return;
767     }
768   if ((*ip6)->protocol != IP_PROTOCOL_UDP)
769     {
770       BFD_ERR ("Unexpected protocol in IPv6 header '%u', expected '%u' (== "
771                "IP_PROTOCOL_UDP)", (*ip6)->protocol, IP_PROTOCOL_UDP);
772       *ip6 = NULL;
773       *udp = NULL;
774       return;
775     }
776   *udp = (udp_header_t *) ((*ip6) + 1);
777 }
778
779 static bfd_udp_error_t
780 bfd_udp6_verify_transport (const ip6_header_t * ip6,
781                            const udp_header_t * udp, const bfd_session_t * bs)
782 {
783   const bfd_udp_session_t *bus = &bs->udp;
784   const bfd_udp_key_t *key = &bus->key;
785   if (ip6->src_address.as_u64[0] != key->peer_addr.ip6.as_u64[0] &&
786       ip6->src_address.as_u64[1] != key->peer_addr.ip6.as_u64[1])
787     {
788       BFD_ERR ("IP src addr mismatch, got %U, expected %U",
789                format_ip6_address, ip6, format_ip6_address,
790                &key->peer_addr.ip6);
791       return BFD_UDP_ERROR_BAD;
792     }
793   if (ip6->dst_address.as_u64[0] != key->local_addr.ip6.as_u64[0] &&
794       ip6->dst_address.as_u64[1] != key->local_addr.ip6.as_u64[1])
795     {
796       BFD_ERR ("IP dst addr mismatch, got %U, expected %U",
797                format_ip6_address, ip6, format_ip6_address,
798                &key->local_addr.ip6);
799       return BFD_UDP_ERROR_BAD;
800     }
801   const u8 expected_hop_limit = 255;
802   if (ip6->hop_limit != expected_hop_limit)
803     {
804       BFD_ERR ("IPv6 unexpected hop-limit value %u, expected %u",
805                ip6->hop_limit, expected_hop_limit);
806       return BFD_UDP_ERROR_BAD;
807     }
808   if (clib_net_to_host_u16 (udp->src_port) < 49152 ||
809       clib_net_to_host_u16 (udp->src_port) > 65535)
810     {
811       BFD_ERR ("Invalid UDP src port %u, out of range <49152,65535>",
812                udp->src_port);
813     }
814   return BFD_UDP_ERROR_NONE;
815 }
816
817 static bfd_udp_error_t
818 bfd_udp6_scan (vlib_main_t * vm, vlib_node_runtime_t * rt,
819                vlib_buffer_t * b, bfd_session_t ** bs_out)
820 {
821   const bfd_pkt_t *pkt = vlib_buffer_get_current (b);
822   if (sizeof (*pkt) > b->current_length)
823     {
824       BFD_ERR
825         ("Payload size %d too small to hold bfd packet of minimum size %d",
826          b->current_length, sizeof (*pkt));
827       return BFD_UDP_ERROR_BAD;
828     }
829   const ip6_header_t *ip6;
830   const udp_header_t *udp;
831   bfd_udp6_find_headers (b, &ip6, &udp);
832   if (!ip6 || !udp)
833     {
834       BFD_ERR ("Couldn't find ip6 or udp header");
835       return BFD_UDP_ERROR_BAD;
836     }
837   const u32 udp_payload_length = udp->length - sizeof (*udp);
838   if (pkt->head.length > udp_payload_length)
839     {
840       BFD_ERR
841         ("BFD packet length is larger than udp payload length (%u > %u)",
842          pkt->head.length, udp_payload_length);
843       return BFD_UDP_ERROR_BAD;
844     }
845   if (!bfd_verify_pkt_common (pkt))
846     {
847       return BFD_UDP_ERROR_BAD;
848     }
849   bfd_session_t *bs = NULL;
850   if (pkt->your_disc)
851     {
852       BFD_DBG ("Looking up BFD session using discriminator %u",
853                pkt->your_disc);
854       bs = bfd_find_session_by_disc (bfd_udp_main.bfd_main, pkt->your_disc);
855     }
856   else
857     {
858       bfd_udp_key_t key;
859       memset (&key, 0, sizeof (key));
860       key.sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
861       key.local_addr.ip6.as_u64[0] = ip6->dst_address.as_u64[0];
862       key.local_addr.ip6.as_u64[1] = ip6->dst_address.as_u64[1];
863       key.peer_addr.ip6.as_u64[0] = ip6->src_address.as_u64[0];
864       key.peer_addr.ip6.as_u64[1] = ip6->src_address.as_u64[1];
865       BFD_DBG ("Looking up BFD session using key (sw_if_index=%u, local=%U, "
866                "peer=%U)",
867                key.sw_if_index, format_ip6_address, &key.local_addr,
868                format_ip6_address, &key.peer_addr);
869       bs = bfd_lookup_session (&bfd_udp_main, &key);
870     }
871   if (!bs)
872     {
873       BFD_ERR ("BFD session lookup failed - no session matches BFD pkt");
874       return BFD_UDP_ERROR_BAD;
875     }
876   BFD_DBG ("BFD session found, bs_idx=%u", bs->bs_idx);
877   if (!bfd_verify_pkt_auth (pkt, b->current_length, bs))
878     {
879       BFD_ERR ("Packet verification failed, dropping packet");
880       return BFD_UDP_ERROR_BAD;
881     }
882   bfd_udp_error_t err;
883   if (BFD_UDP_ERROR_NONE != (err = bfd_udp6_verify_transport (ip6, udp, bs)))
884     {
885       return err;
886     }
887   bfd_rpc_update_session (bs->bs_idx, pkt);
888   *bs_out = bs;
889   return BFD_UDP_ERROR_NONE;
890 }
891
892 /*
893  * Process a frame of bfd packets
894  * Expect 1 packet / frame
895  */
896 static uword
897 bfd_udp_input (vlib_main_t * vm, vlib_node_runtime_t * rt,
898                vlib_frame_t * f, int is_ipv6)
899 {
900   u32 n_left_from, *from;
901   bfd_input_trace_t *t0;
902
903   from = vlib_frame_vector_args (f);    /* array of buffer indices */
904   n_left_from = f->n_vectors;   /* number of buffer indices */
905
906   while (n_left_from > 0)
907     {
908       u32 bi0;
909       vlib_buffer_t *b0;
910       u32 next0, error0;
911
912       bi0 = from[0];
913       b0 = vlib_get_buffer (vm, bi0);
914
915       bfd_session_t *bs = NULL;
916
917       /* If this pkt is traced, snapshot the data */
918       if (b0->flags & VLIB_BUFFER_IS_TRACED)
919         {
920           int len;
921           t0 = vlib_add_trace (vm, rt, b0, sizeof (*t0));
922           len = (b0->current_length < sizeof (t0->data)) ? b0->current_length
923             : sizeof (t0->data);
924           t0->len = len;
925           clib_memcpy (t0->data, vlib_buffer_get_current (b0), len);
926         }
927
928       /* scan this bfd pkt. error0 is the counter index to bmp */
929       if (is_ipv6)
930         {
931           error0 = bfd_udp6_scan (vm, rt, b0, &bs);
932         }
933       else
934         {
935           error0 = bfd_udp4_scan (vm, rt, b0, &bs);
936         }
937       b0->error = rt->errors[error0];
938
939       next0 = BFD_UDP_INPUT_NEXT_NORMAL;
940       if (BFD_UDP_ERROR_NONE == error0)
941         {
942           /*
943            *  if everything went fine, check for poll bit, if present, re-use
944            *  the buffer and based on (now updated) session parameters, send
945            *  the final packet back
946            */
947           const bfd_pkt_t *pkt = vlib_buffer_get_current (b0);
948           if (bfd_pkt_get_poll (pkt))
949             {
950               bfd_init_final_control_frame (vm, b0, bs);
951               if (is_ipv6)
952                 {
953                   vlib_node_increment_counter (vm, bfd_udp6_input_node.index,
954                                                b0->error, 1);
955                 }
956               else
957                 {
958                   vlib_node_increment_counter (vm, bfd_udp4_input_node.index,
959                                                b0->error, 1);
960                 }
961               next0 = BFD_UDP_INPUT_NEXT_REPLY;
962             }
963         }
964       vlib_set_next_frame_buffer (vm, rt, next0, bi0);
965
966       from += 1;
967       n_left_from -= 1;
968     }
969
970   return f->n_vectors;
971 }
972
973 static uword
974 bfd_udp4_input (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f)
975 {
976   return bfd_udp_input (vm, rt, f, 0);
977 }
978
979 /*
980  * bfd input graph node declaration
981  */
982 /* *INDENT-OFF* */
983 VLIB_REGISTER_NODE (bfd_udp4_input_node, static) = {
984   .function = bfd_udp4_input,
985   .name = "bfd-udp4-input",
986   .vector_size = sizeof (u32),
987   .type = VLIB_NODE_TYPE_INTERNAL,
988
989   .n_errors = BFD_UDP_N_ERROR,
990   .error_strings = bfd_udp_error_strings,
991
992   .format_trace = bfd_input_format_trace,
993
994   .n_next_nodes = BFD_UDP_INPUT_N_NEXT,
995   .next_nodes =
996       {
997               [BFD_UDP_INPUT_NEXT_NORMAL] = "error-drop",
998               [BFD_UDP_INPUT_NEXT_REPLY] = "ip4-lookup",
999       },
1000 };
1001 /* *INDENT-ON* */
1002
1003 static uword
1004 bfd_udp6_input (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f)
1005 {
1006   return bfd_udp_input (vm, rt, f, 1);
1007 }
1008
1009 /* *INDENT-OFF* */
1010 VLIB_REGISTER_NODE (bfd_udp6_input_node, static) = {
1011   .function = bfd_udp6_input,
1012   .name = "bfd-udp6-input",
1013   .vector_size = sizeof (u32),
1014   .type = VLIB_NODE_TYPE_INTERNAL,
1015
1016   .n_errors = BFD_UDP_N_ERROR,
1017   .error_strings = bfd_udp_error_strings,
1018
1019   .format_trace = bfd_input_format_trace,
1020
1021   .n_next_nodes = BFD_UDP_INPUT_N_NEXT,
1022   .next_nodes =
1023       {
1024               [BFD_UDP_INPUT_NEXT_NORMAL] = "error-drop",
1025               [BFD_UDP_INPUT_NEXT_REPLY] = "ip6-lookup",
1026       },
1027 };
1028 /* *INDENT-ON* */
1029
1030 static clib_error_t *
1031 bfd_sw_interface_up_down (vnet_main_t * vnm, u32 sw_if_index, u32 flags)
1032 {
1033   // vnet_hw_interface_t *hi = vnet_get_sup_hw_interface (vnm, sw_if_index);
1034   if (!(flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP))
1035     {
1036       /* TODO */
1037     }
1038   return 0;
1039 }
1040
1041 VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION (bfd_sw_interface_up_down);
1042
1043 static clib_error_t *
1044 bfd_hw_interface_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
1045 {
1046   if (flags & VNET_HW_INTERFACE_FLAG_LINK_UP)
1047     {
1048       /* TODO */
1049     }
1050   return 0;
1051 }
1052
1053 VNET_HW_INTERFACE_LINK_UP_DOWN_FUNCTION (bfd_hw_interface_up_down);
1054
1055 /*
1056  * setup function
1057  */
1058 static clib_error_t *
1059 bfd_udp_init (vlib_main_t * vm)
1060 {
1061   mhash_init (&bfd_udp_main.bfd_session_idx_by_bfd_key, sizeof (uword),
1062               sizeof (bfd_udp_key_t));
1063   bfd_udp_main.bfd_main = &bfd_main;
1064   udp_register_dst_port (vm, UDP_DST_PORT_bfd4, bfd_udp4_input_node.index, 1);
1065   udp_register_dst_port (vm, UDP_DST_PORT_bfd6, bfd_udp6_input_node.index, 0);
1066   return 0;
1067 }
1068
1069 VLIB_INIT_FUNCTION (bfd_udp_init);
1070
1071 /*
1072  * fd.io coding-style-patch-verification: ON
1073  *
1074  * Local Variables:
1075  * eval: (c-set-style "gnu")
1076  * End:
1077  */