BFD: command line interface
[vpp.git] / src / vnet / bfd / bfd_udp.c
1 /*
2  * Copyright (c) 2011-2016 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #include <vppinfra/types.h>
16 #include <vlibmemory/api.h>
17 #include <vlib/vlib.h>
18 #include <vlib/buffer.h>
19 #include <vnet/ip/format.h>
20 #include <vnet/ethernet/packet.h>
21 #include <vnet/udp/udp_packet.h>
22 #include <vnet/udp/udp.h>
23 #include <vnet/ip/lookup.h>
24 #include <vnet/ip/icmp46_packet.h>
25 #include <vnet/ip/ip4.h>
26 #include <vnet/ip/ip6.h>
27 #include <vnet/ip/ip6_packet.h>
28 #include <vnet/adj/adj.h>
29 #include <vnet/adj/adj_nbr.h>
30 #include <vnet/dpo/receive_dpo.h>
31 #include <vnet/fib/fib_entry.h>
32 #include <vnet/fib/fib_table.h>
33 #include <vnet/bfd/bfd_debug.h>
34 #include <vnet/bfd/bfd_udp.h>
35 #include <vnet/bfd/bfd_main.h>
36 #include <vnet/bfd/bfd_api.h>
37
38 typedef struct
39 {
40   bfd_main_t *bfd_main;
41   /* hashmap - bfd session index by bfd key - used for CLI/API lookup, where
42    * discriminator is unknown */
43   mhash_t bfd_session_idx_by_bfd_key;
44   /* convenience variable */
45   vnet_main_t *vnet_main;
46   /* flag indicating whether echo_source_sw_if_index holds a valid value */
47   int echo_source_is_set;
48   /* loopback interface used to get echo source ip */
49   u32 echo_source_sw_if_index;
50 } bfd_udp_main_t;
51
52 static vlib_node_registration_t bfd_udp4_input_node;
53 static vlib_node_registration_t bfd_udp6_input_node;
54 static vlib_node_registration_t bfd_udp_echo4_input_node;
55 static vlib_node_registration_t bfd_udp_echo6_input_node;
56
57 bfd_udp_main_t bfd_udp_main;
58
59 vnet_api_error_t
60 bfd_udp_set_echo_source (u32 sw_if_index)
61 {
62   vnet_sw_interface_t *sw_if =
63     vnet_get_sw_interface_safe (bfd_udp_main.vnet_main, sw_if_index);
64   if (sw_if)
65     {
66       bfd_udp_main.echo_source_sw_if_index = sw_if_index;
67       bfd_udp_main.echo_source_is_set = 1;
68       return 0;
69     }
70   return VNET_API_ERROR_BFD_ENOENT;
71 }
72
73 vnet_api_error_t
74 bfd_udp_del_echo_source (u32 sw_if_index)
75 {
76   bfd_udp_main.echo_source_sw_if_index = ~0;
77   bfd_udp_main.echo_source_is_set = 0;
78   return 0;
79 }
80
81 int
82 bfd_udp_is_echo_available (bfd_transport_e transport)
83 {
84   if (!bfd_udp_main.echo_source_is_set)
85     {
86       BFD_DBG ("UDP echo source not set - echo not available");
87       return 0;
88     }
89   /*
90    * for the echo to work, we need a loopback interface with at least one
91    * address with netmask length at most 31 (ip4) or 127 (ip6) so that we can
92    * pick an unused address from that subnet
93    */
94   vnet_sw_interface_t *sw_if =
95     vnet_get_sw_interface_safe (bfd_udp_main.vnet_main,
96                                 bfd_udp_main.echo_source_sw_if_index);
97   if (sw_if && sw_if->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP)
98     {
99       if (BFD_TRANSPORT_UDP4 == transport)
100         {
101           ip4_main_t *im = &ip4_main;
102           ip_interface_address_t *ia = NULL;
103           /* *INDENT-OFF* */
104           foreach_ip_interface_address (&im->lookup_main, ia,
105                                         bfd_udp_main.echo_source_sw_if_index,
106                                         0 /* honor unnumbered */, ({
107                                           if (ia->address_length <= 31)
108                                             {
109                                               return 1;
110                                             }
111                                         }));
112           /* *INDENT-ON* */
113         }
114       else if (BFD_TRANSPORT_UDP6 == transport)
115         {
116           ip6_main_t *im = &ip6_main;
117           ip_interface_address_t *ia = NULL;
118           /* *INDENT-OFF* */
119           foreach_ip_interface_address (&im->lookup_main, ia,
120                                         bfd_udp_main.echo_source_sw_if_index,
121                                         0 /* honor unnumbered */, ({
122                                           if (ia->address_length <= 127)
123                                             {
124                                               return 1;
125                                             }
126                                         }));
127           /* *INDENT-ON* */
128         }
129     }
130   BFD_DBG ("No usable IP address for UDP echo - echo not available");
131   return 0;
132 }
133
134 static u16
135 bfd_udp_bs_idx_to_sport (u32 bs_idx)
136 {
137   /* The source port MUST be in the range 49152 through 65535. The same UDP
138    * source port number MUST be used for all BFD Control packets associated
139    * with a particular session.  The source port number SHOULD be unique among
140    * all BFD sessions on the system. If more than 16384 BFD sessions are
141    * simultaneously active, UDP source port numbers MAY be reused on
142    * multiple sessions, but the number of distinct uses of the same UDP
143    * source port number SHOULD be minimized.
144    */
145   return 49152 + bs_idx % (65535 - 49152 + 1);
146 }
147
148 int
149 bfd_udp_get_echo_src_ip4 (ip4_address_t * addr)
150 {
151   if (!bfd_udp_main.echo_source_is_set)
152     {
153       BFD_ERR ("cannot find ip4 address, echo source not set");
154       return 0;
155     }
156   ip_interface_address_t *ia = NULL;
157   ip4_main_t *im = &ip4_main;
158
159   /* *INDENT-OFF* */
160   foreach_ip_interface_address (
161       &im->lookup_main, ia, bfd_udp_main.echo_source_sw_if_index,
162       0 /* honor unnumbered */, ({
163         ip4_address_t *x =
164             ip_interface_address_get_address (&im->lookup_main, ia);
165         if (ia->address_length <= 31)
166           {
167             addr->as_u32 = clib_host_to_net_u32 (x->as_u32);
168             /*
169              * flip the last bit to get a different address, might be network,
170              * we don't care ...
171              */
172             addr->as_u32 ^= 1;
173             addr->as_u32 = clib_net_to_host_u32 (addr->as_u32);
174             return 1;
175           }
176       }));
177   /* *INDENT-ON* */
178   BFD_ERR ("cannot find ip4 address, no usable address found");
179   return 0;
180 }
181
182 int
183 bfd_udp_get_echo_src_ip6 (ip6_address_t * addr)
184 {
185   if (!bfd_udp_main.echo_source_is_set)
186     {
187       BFD_ERR ("cannot find ip6 address, echo source not set");
188       return 0;
189     }
190   ip_interface_address_t *ia = NULL;
191   ip6_main_t *im = &ip6_main;
192
193   /* *INDENT-OFF* */
194   foreach_ip_interface_address (
195       &im->lookup_main, ia, bfd_udp_main.echo_source_sw_if_index,
196       0 /* honor unnumbered */, ({
197         ip6_address_t *x =
198             ip_interface_address_get_address (&im->lookup_main, ia);
199         if (ia->address_length <= 127)
200           {
201             *addr = *x;
202             addr->as_u8[15] ^= 1; /* flip the last bit of the address */
203             return 1;
204           }
205       }));
206   /* *INDENT-ON* */
207   BFD_ERR ("cannot find ip6 address, no usable address found");
208   return 0;
209 }
210
211 void
212 bfd_udp_get_echo_source (int *is_set, u32 * sw_if_index, int *have_usable_ip4,
213                          ip4_address_t * ip4, int *have_usable_ip6,
214                          ip6_address_t * ip6)
215 {
216   if (bfd_udp_main.echo_source_is_set)
217     {
218       *is_set = 1;
219       *sw_if_index = bfd_udp_main.echo_source_sw_if_index;
220       *have_usable_ip4 = bfd_udp_get_echo_src_ip4 (ip4);
221       *have_usable_ip6 = bfd_udp_get_echo_src_ip6 (ip6);
222     }
223   else
224     {
225       *is_set = 0;
226     }
227 }
228
229 int
230 bfd_add_udp4_transport (vlib_main_t * vm, vlib_buffer_t * b,
231                         const bfd_session_t * bs, int is_echo)
232 {
233   const bfd_udp_session_t *bus = &bs->udp;
234   const bfd_udp_key_t *key = &bus->key;
235
236   b->flags |= VNET_BUFFER_LOCALLY_ORIGINATED;
237   vnet_buffer (b)->ip.adj_index[VLIB_RX] = bus->adj_index;
238   vnet_buffer (b)->ip.adj_index[VLIB_TX] = bus->adj_index;
239   typedef struct
240   {
241     ip4_header_t ip4;
242     udp_header_t udp;
243   } ip4_udp_headers;
244   ip4_udp_headers *headers = NULL;
245   vlib_buffer_advance (b, -sizeof (*headers));
246   headers = vlib_buffer_get_current (b);
247   memset (headers, 0, sizeof (*headers));
248   headers->ip4.ip_version_and_header_length = 0x45;
249   headers->ip4.ttl = 255;
250   headers->ip4.protocol = IP_PROTOCOL_UDP;
251   headers->udp.src_port =
252     clib_host_to_net_u16 (bfd_udp_bs_idx_to_sport (bs->bs_idx));
253   if (is_echo)
254     {
255       int rv;
256       if (!(rv = bfd_udp_get_echo_src_ip4 (&headers->ip4.src_address)))
257         {
258           return rv;
259         }
260       headers->ip4.dst_address.as_u32 = key->local_addr.ip4.as_u32;
261       headers->udp.dst_port = clib_host_to_net_u16 (UDP_DST_PORT_bfd_echo4);
262     }
263   else
264     {
265       headers->ip4.src_address.as_u32 = key->local_addr.ip4.as_u32;
266       headers->ip4.dst_address.as_u32 = key->peer_addr.ip4.as_u32;
267       headers->udp.dst_port = clib_host_to_net_u16 (UDP_DST_PORT_bfd4);
268     }
269
270   /* fix ip length, checksum and udp length */
271   const u16 ip_length = vlib_buffer_length_in_chain (vm, b);
272
273   headers->ip4.length = clib_host_to_net_u16 (ip_length);
274   headers->ip4.checksum = ip4_header_checksum (&headers->ip4);
275
276   const u16 udp_length = ip_length - (sizeof (headers->ip4));
277   headers->udp.length = clib_host_to_net_u16 (udp_length);
278   return 1;
279 }
280
281 int
282 bfd_add_udp6_transport (vlib_main_t * vm, vlib_buffer_t * b,
283                         const bfd_session_t * bs, int is_echo)
284 {
285   const bfd_udp_session_t *bus = &bs->udp;
286   const bfd_udp_key_t *key = &bus->key;
287
288   b->flags |= VNET_BUFFER_LOCALLY_ORIGINATED;
289   vnet_buffer (b)->ip.adj_index[VLIB_RX] = bus->adj_index;
290   vnet_buffer (b)->ip.adj_index[VLIB_TX] = bus->adj_index;
291   typedef struct
292   {
293     ip6_header_t ip6;
294     udp_header_t udp;
295   } ip6_udp_headers;
296   ip6_udp_headers *headers = NULL;
297   vlib_buffer_advance (b, -sizeof (*headers));
298   headers = vlib_buffer_get_current (b);
299   memset (headers, 0, sizeof (*headers));
300   headers->ip6.ip_version_traffic_class_and_flow_label =
301     clib_host_to_net_u32 (0x6 << 28);
302   headers->ip6.hop_limit = 255;
303   headers->ip6.protocol = IP_PROTOCOL_UDP;
304   headers->udp.src_port =
305     clib_host_to_net_u16 (bfd_udp_bs_idx_to_sport (bs->bs_idx));
306   if (is_echo)
307     {
308       int rv;
309       if (!(rv = bfd_udp_get_echo_src_ip6 (&headers->ip6.src_address)))
310         {
311           return rv;
312         }
313       clib_memcpy (&headers->ip6.dst_address, &key->local_addr.ip6,
314                    sizeof (headers->ip6.dst_address));
315
316       headers->udp.dst_port = clib_host_to_net_u16 (UDP_DST_PORT_bfd_echo6);
317     }
318   else
319     {
320       clib_memcpy (&headers->ip6.src_address, &key->local_addr.ip6,
321                    sizeof (headers->ip6.src_address));
322       clib_memcpy (&headers->ip6.dst_address, &key->peer_addr.ip6,
323                    sizeof (headers->ip6.dst_address));
324       headers->udp.dst_port = clib_host_to_net_u16 (UDP_DST_PORT_bfd6);
325     }
326
327   /* fix ip payload length and udp length */
328   const u16 udp_length =
329     vlib_buffer_length_in_chain (vm, b) - (sizeof (headers->ip6));
330   headers->udp.length = clib_host_to_net_u16 (udp_length);
331   headers->ip6.payload_length = headers->udp.length;
332
333   /* IPv6 UDP checksum is mandatory */
334   int bogus = 0;
335   headers->udp.checksum =
336     ip6_tcp_udp_icmp_compute_checksum (vm, b, &headers->ip6, &bogus);
337   ASSERT (bogus == 0);
338   if (headers->udp.checksum == 0)
339     {
340       headers->udp.checksum = 0xffff;
341     }
342   return 1;
343 }
344
345 static bfd_session_t *
346 bfd_lookup_session (bfd_udp_main_t * bum, const bfd_udp_key_t * key)
347 {
348   uword *p = mhash_get (&bum->bfd_session_idx_by_bfd_key, key);
349   if (p)
350     {
351       return bfd_find_session_by_idx (bum->bfd_main, *p);
352     }
353   return 0;
354 }
355
356 static void
357 bfd_udp_key_init (bfd_udp_key_t * key, u32 sw_if_index,
358                   const ip46_address_t * local_addr,
359                   const ip46_address_t * peer_addr)
360 {
361   memset (key, 0, sizeof (*key));
362   key->sw_if_index = sw_if_index;
363   key->local_addr.as_u64[0] = local_addr->as_u64[0];
364   key->local_addr.as_u64[1] = local_addr->as_u64[1];
365   key->peer_addr.as_u64[0] = peer_addr->as_u64[0];
366   key->peer_addr.as_u64[1] = peer_addr->as_u64[1];
367 }
368
369 static vnet_api_error_t
370 bfd_udp_add_session_internal (bfd_udp_main_t * bum, u32 sw_if_index,
371                               u32 desired_min_tx_usec,
372                               u32 required_min_rx_usec, u8 detect_mult,
373                               const ip46_address_t * local_addr,
374                               const ip46_address_t * peer_addr,
375                               bfd_session_t ** bs_out)
376 {
377   /* get a pool entry and if we end up not needing it, give it back */
378   bfd_transport_e t = BFD_TRANSPORT_UDP4;
379   if (!ip46_address_is_ip4 (local_addr))
380     {
381       t = BFD_TRANSPORT_UDP6;
382     }
383   bfd_session_t *bs = bfd_get_session (bum->bfd_main, t);
384   if (!bs)
385     {
386       bfd_put_session (bum->bfd_main, bs);
387       return VNET_API_ERROR_BFD_EAGAIN;
388     }
389   bfd_udp_session_t *bus = &bs->udp;
390   memset (bus, 0, sizeof (*bus));
391   bfd_udp_key_t *key = &bus->key;
392   bfd_udp_key_init (key, sw_if_index, local_addr, peer_addr);
393   const bfd_session_t *tmp = bfd_lookup_session (bum, key);
394   if (tmp)
395     {
396       clib_warning ("duplicate bfd-udp session, existing bs_idx=%d",
397                     tmp->bs_idx);
398       bfd_put_session (bum->bfd_main, bs);
399       return VNET_API_ERROR_BFD_EEXIST;
400     }
401   mhash_set (&bum->bfd_session_idx_by_bfd_key, key, bs->bs_idx, NULL);
402   BFD_DBG ("session created, bs_idx=%u, sw_if_index=%d, local=%U, peer=%U",
403            bs->bs_idx, key->sw_if_index, format_ip46_address,
404            &key->local_addr, IP46_TYPE_ANY, format_ip46_address,
405            &key->peer_addr, IP46_TYPE_ANY);
406   if (BFD_TRANSPORT_UDP4 == t)
407     {
408       bus->adj_index = adj_nbr_add_or_lock (FIB_PROTOCOL_IP4, VNET_LINK_IP4,
409                                             &key->peer_addr,
410                                             key->sw_if_index);
411       BFD_DBG ("adj_nbr_add_or_lock(FIB_PROTOCOL_IP4, VNET_LINK_IP4, %U, %d) "
412                "returns %d", format_ip46_address, &key->peer_addr,
413                IP46_TYPE_ANY, key->sw_if_index, bus->adj_index);
414
415       fib_prefix_t fib_prefix;
416       memset (&fib_prefix, 0, sizeof (fib_prefix));
417       fib_prefix.fp_len = 0;
418       fib_prefix.fp_proto = FIB_PROTOCOL_IP4;
419       fib_prefix.fp_addr = key->local_addr;
420       u32 fib_index = fib_table_find (FIB_PROTOCOL_IP4, 0);     /* FIXME table id 0? */
421       dpo_id_t dpo = DPO_INVALID;
422       dpo_proto_t dproto;
423       dproto = fib_proto_to_dpo (fib_prefix.fp_proto);
424       receive_dpo_add_or_lock (dproto, ~0, NULL, &dpo);
425       fib_table_entry_special_dpo_update (fib_index, &fib_prefix,
426                                           FIB_SOURCE_API,
427                                           FIB_ENTRY_FLAG_LOCAL, &dpo);
428       dpo_reset (&dpo);
429     }
430   else
431     {
432       bus->adj_index = adj_nbr_add_or_lock (FIB_PROTOCOL_IP6, VNET_LINK_IP6,
433                                             &key->peer_addr,
434                                             key->sw_if_index);
435       BFD_DBG ("adj_nbr_add_or_lock(FIB_PROTOCOL_IP6, VNET_LINK_IP6, %U, %d) "
436                "returns %d", format_ip46_address, &key->peer_addr,
437                IP46_TYPE_ANY, key->sw_if_index, bus->adj_index);
438     }
439   *bs_out = bs;
440   return bfd_session_set_params (bum->bfd_main, bs, desired_min_tx_usec,
441                                  required_min_rx_usec, detect_mult);
442 }
443
444 static vnet_api_error_t
445 bfd_udp_validate_api_input (u32 sw_if_index,
446                             const ip46_address_t * local_addr,
447                             const ip46_address_t * peer_addr)
448 {
449   vnet_sw_interface_t *sw_if =
450     vnet_get_sw_interface_safe (bfd_udp_main.vnet_main, sw_if_index);
451   u8 local_ip_valid = 0;
452   ip_interface_address_t *ia = NULL;
453   if (!sw_if)
454     {
455       clib_warning ("got NULL sw_if");
456       return VNET_API_ERROR_INVALID_SW_IF_INDEX;
457     }
458   if (ip46_address_is_ip4 (local_addr))
459     {
460       if (!ip46_address_is_ip4 (peer_addr))
461         {
462           clib_warning ("IP family mismatch");
463           return VNET_API_ERROR_INVALID_ARGUMENT;
464         }
465       ip4_main_t *im = &ip4_main;
466
467       /* *INDENT-OFF* */
468       foreach_ip_interface_address (
469           &im->lookup_main, ia, sw_if_index, 0 /* honor unnumbered */, ({
470             ip4_address_t *x =
471                 ip_interface_address_get_address (&im->lookup_main, ia);
472             if (x->as_u32 == local_addr->ip4.as_u32)
473               {
474                 /* valid address for this interface */
475                 local_ip_valid = 1;
476                 break;
477               }
478           }));
479       /* *INDENT-ON* */
480     }
481   else
482     {
483       if (ip46_address_is_ip4 (peer_addr))
484         {
485           clib_warning ("IP family mismatch");
486           return VNET_API_ERROR_INVALID_ARGUMENT;
487         }
488       ip6_main_t *im = &ip6_main;
489       /* *INDENT-OFF* */
490       foreach_ip_interface_address (
491           &im->lookup_main, ia, sw_if_index, 0 /* honor unnumbered */, ({
492             ip6_address_t *x =
493                 ip_interface_address_get_address (&im->lookup_main, ia);
494             if (local_addr->ip6.as_u64[0] == x->as_u64[0] &&
495                 local_addr->ip6.as_u64[1] == x->as_u64[1])
496               {
497                 /* valid address for this interface */
498                 local_ip_valid = 1;
499                 break;
500               }
501           }));
502       /* *INDENT-ON* */
503     }
504
505   if (!local_ip_valid)
506     {
507       clib_warning ("address not found on interface");
508       return VNET_API_ERROR_ADDRESS_NOT_FOUND_FOR_INTERFACE;
509     }
510
511   return 0;
512 }
513
514 static vnet_api_error_t
515 bfd_udp_find_session_by_api_input (u32 sw_if_index,
516                                    const ip46_address_t * local_addr,
517                                    const ip46_address_t * peer_addr,
518                                    bfd_session_t ** bs_out)
519 {
520   vnet_api_error_t rv =
521     bfd_udp_validate_api_input (sw_if_index, local_addr, peer_addr);
522   if (!rv)
523     {
524       bfd_udp_main_t *bum = &bfd_udp_main;
525       bfd_udp_key_t key;
526       bfd_udp_key_init (&key, sw_if_index, local_addr, peer_addr);
527       bfd_session_t *bs = bfd_lookup_session (bum, &key);
528       if (bs)
529         {
530           *bs_out = bs;
531         }
532       else
533         {
534           clib_warning
535             ("BFD session not found (sw_if_index=%u, local=%U, peer=%U",
536              sw_if_index, format_ip46_address, local_addr, IP46_TYPE_ANY,
537              format_ip46_address, peer_addr, IP46_TYPE_ANY);
538           return VNET_API_ERROR_BFD_ENOENT;
539         }
540     }
541   return rv;
542 }
543
544 static vnet_api_error_t
545 bfd_api_verify_common (u32 sw_if_index, u32 desired_min_tx_usec,
546                        u32 required_min_rx_usec, u8 detect_mult,
547                        const ip46_address_t * local_addr,
548                        const ip46_address_t * peer_addr)
549 {
550   vnet_api_error_t rv =
551     bfd_udp_validate_api_input (sw_if_index, local_addr, peer_addr);
552   if (rv)
553     {
554       return rv;
555     }
556   if (detect_mult < 1)
557     {
558       clib_warning ("detect_mult < 1");
559       return VNET_API_ERROR_INVALID_ARGUMENT;
560     }
561   if (desired_min_tx_usec < 1)
562     {
563       clib_warning ("desired_min_tx_usec < 1");
564       return VNET_API_ERROR_INVALID_ARGUMENT;
565     }
566   return 0;
567 }
568
569 static void
570 bfd_udp_del_session_internal (bfd_session_t * bs)
571 {
572   bfd_udp_main_t *bum = &bfd_udp_main;
573   BFD_DBG ("free bfd-udp session, bs_idx=%d", bs->bs_idx);
574   mhash_unset (&bum->bfd_session_idx_by_bfd_key, &bs->udp.key, NULL);
575   adj_unlock (bs->udp.adj_index);
576   bfd_put_session (bum->bfd_main, bs);
577 }
578
579 vnet_api_error_t
580 bfd_udp_add_session (u32 sw_if_index, const ip46_address_t * local_addr,
581                      const ip46_address_t * peer_addr,
582                      u32 desired_min_tx_usec, u32 required_min_rx_usec,
583                      u8 detect_mult, u8 is_authenticated, u32 conf_key_id,
584                      u8 bfd_key_id)
585 {
586   vnet_api_error_t rv =
587     bfd_api_verify_common (sw_if_index, desired_min_tx_usec,
588                            required_min_rx_usec, detect_mult,
589                            local_addr, peer_addr);
590   bfd_session_t *bs = NULL;
591   if (!rv)
592     {
593       rv =
594         bfd_udp_add_session_internal (&bfd_udp_main, sw_if_index,
595                                       desired_min_tx_usec,
596                                       required_min_rx_usec, detect_mult,
597                                       local_addr, peer_addr, &bs);
598     }
599   if (!rv && is_authenticated)
600     {
601 #if WITH_LIBSSL > 0
602       rv = bfd_auth_activate (bs, conf_key_id, bfd_key_id,
603                               0 /* is not delayed */ );
604 #else
605       clib_warning ("SSL missing, cannot add authenticated BFD session");
606       rv = VNET_API_ERROR_BFD_NOTSUPP;
607 #endif
608       if (rv)
609         {
610           bfd_udp_del_session_internal (bs);
611         }
612     }
613   if (!rv)
614     {
615       bfd_session_start (bfd_udp_main.bfd_main, bs);
616     }
617
618   return rv;
619 }
620
621 vnet_api_error_t
622 bfd_udp_mod_session (u32 sw_if_index,
623                      const ip46_address_t * local_addr,
624                      const ip46_address_t * peer_addr,
625                      u32 desired_min_tx_usec,
626                      u32 required_min_rx_usec, u8 detect_mult)
627 {
628   bfd_session_t *bs = NULL;
629   vnet_api_error_t rv =
630     bfd_udp_find_session_by_api_input (sw_if_index, local_addr, peer_addr,
631                                        &bs);
632   if (rv)
633     {
634       return rv;
635     }
636
637   return bfd_session_set_params (bfd_udp_main.bfd_main, bs,
638                                  desired_min_tx_usec, required_min_rx_usec,
639                                  detect_mult);
640 }
641
642 vnet_api_error_t
643 bfd_udp_del_session (u32 sw_if_index,
644                      const ip46_address_t * local_addr,
645                      const ip46_address_t * peer_addr)
646 {
647   bfd_session_t *bs = NULL;
648   vnet_api_error_t rv =
649     bfd_udp_find_session_by_api_input (sw_if_index, local_addr, peer_addr,
650                                        &bs);
651   if (rv)
652     {
653       return rv;
654     }
655   bfd_udp_del_session_internal (bs);
656   return 0;
657 }
658
659 vnet_api_error_t
660 bfd_udp_session_set_flags (u32 sw_if_index,
661                            const ip46_address_t * local_addr,
662                            const ip46_address_t * peer_addr, u8 admin_up_down)
663 {
664   bfd_session_t *bs = NULL;
665   vnet_api_error_t rv =
666     bfd_udp_find_session_by_api_input (sw_if_index, local_addr, peer_addr,
667                                        &bs);
668   if (rv)
669     {
670       return rv;
671     }
672   bfd_session_set_flags (bs, admin_up_down);
673   return 0;
674 }
675
676 vnet_api_error_t
677 bfd_udp_auth_activate (u32 sw_if_index,
678                        const ip46_address_t * local_addr,
679                        const ip46_address_t * peer_addr,
680                        u32 conf_key_id, u8 key_id, u8 is_delayed)
681 {
682 #if WITH_LIBSSL > 0
683   bfd_session_t *bs = NULL;
684   vnet_api_error_t rv =
685     bfd_udp_find_session_by_api_input (sw_if_index, local_addr, peer_addr,
686                                        &bs);
687   if (rv)
688     {
689       return rv;
690     }
691   return bfd_auth_activate (bs, conf_key_id, key_id, is_delayed);
692 #else
693   clib_warning ("SSL missing, cannot activate BFD authentication");
694   return VNET_API_ERROR_BFD_NOTSUPP;
695 #endif
696 }
697
698 vnet_api_error_t
699 bfd_udp_auth_deactivate (u32 sw_if_index,
700                          const ip46_address_t * local_addr,
701                          const ip46_address_t * peer_addr, u8 is_delayed)
702 {
703   bfd_session_t *bs = NULL;
704   vnet_api_error_t rv =
705     bfd_udp_find_session_by_api_input (sw_if_index, local_addr, peer_addr,
706                                        &bs);
707   if (rv)
708     {
709       return rv;
710     }
711   return bfd_auth_deactivate (bs, is_delayed);
712 }
713
714 typedef enum
715 {
716   BFD_UDP_INPUT_NEXT_NORMAL,
717   BFD_UDP_INPUT_NEXT_REPLY,
718   BFD_UDP_INPUT_N_NEXT,
719 } bfd_udp_input_next_t;
720
721 /* Packet counters - BFD control frames */
722 #define foreach_bfd_udp_error(F)           \
723   F (NONE, "good bfd packets (processed)") \
724   F (BAD, "invalid bfd packets")
725
726 #define F(sym, string) static char BFD_UDP_ERR_##sym##_STR[] = string;
727 foreach_bfd_udp_error (F);
728 #undef F
729
730 static char *bfd_udp_error_strings[] = {
731 #define F(sym, string) BFD_UDP_ERR_##sym##_STR,
732   foreach_bfd_udp_error (F)
733 #undef F
734 };
735
736 typedef enum
737 {
738 #define F(sym, str) BFD_UDP_ERROR_##sym,
739   foreach_bfd_udp_error (F)
740 #undef F
741     BFD_UDP_N_ERROR,
742 } bfd_udp_error_t;
743
744 /* Packet counters - BFD ECHO packets */
745 #define foreach_bfd_udp_echo_error(F)           \
746   F (NONE, "good bfd echo packets (processed)") \
747   F (BAD, "invalid bfd echo packets")
748
749 #define F(sym, string) static char BFD_UDP_ECHO_ERR_##sym##_STR[] = string;
750 foreach_bfd_udp_echo_error (F);
751 #undef F
752
753 static char *bfd_udp_echo_error_strings[] = {
754 #define F(sym, string) BFD_UDP_ECHO_ERR_##sym##_STR,
755   foreach_bfd_udp_echo_error (F)
756 #undef F
757 };
758
759 typedef enum
760 {
761 #define F(sym, str) BFD_UDP_ECHO_ERROR_##sym,
762   foreach_bfd_udp_echo_error (F)
763 #undef F
764     BFD_UDP_ECHO_N_ERROR,
765 } bfd_udp_echo_error_t;
766
767 static void
768 bfd_udp4_find_headers (vlib_buffer_t * b, ip4_header_t ** ip4,
769                        udp_header_t ** udp)
770 {
771   /* sanity check first */
772   const i32 start = vnet_buffer (b)->ip.start_of_ip_header;
773   if (start < 0 && start < sizeof (b->pre_data))
774     {
775       BFD_ERR ("Start of ip header is before pre_data, ignoring");
776       *ip4 = NULL;
777       *udp = NULL;
778       return;
779     }
780   *ip4 = (ip4_header_t *) (b->data + start);
781   if ((u8 *) * ip4 > (u8 *) vlib_buffer_get_current (b))
782     {
783       BFD_ERR ("Start of ip header is beyond current data, ignoring");
784       *ip4 = NULL;
785       *udp = NULL;
786       return;
787     }
788   *udp = (udp_header_t *) ((*ip4) + 1);
789 }
790
791 static bfd_udp_error_t
792 bfd_udp4_verify_transport (const ip4_header_t * ip4,
793                            const udp_header_t * udp, const bfd_session_t * bs)
794 {
795   const bfd_udp_session_t *bus = &bs->udp;
796   const bfd_udp_key_t *key = &bus->key;
797   if (ip4->src_address.as_u32 != key->peer_addr.ip4.as_u32)
798     {
799       BFD_ERR ("IPv4 src addr mismatch, got %U, expected %U",
800                format_ip4_address, ip4->src_address.as_u8, format_ip4_address,
801                key->peer_addr.ip4.as_u8);
802       return BFD_UDP_ERROR_BAD;
803     }
804   if (ip4->dst_address.as_u32 != key->local_addr.ip4.as_u32)
805     {
806       BFD_ERR ("IPv4 dst addr mismatch, got %U, expected %U",
807                format_ip4_address, ip4->dst_address.as_u8, format_ip4_address,
808                key->local_addr.ip4.as_u8);
809       return BFD_UDP_ERROR_BAD;
810     }
811   const u8 expected_ttl = 255;
812   if (ip4->ttl != expected_ttl)
813     {
814       BFD_ERR ("IPv4 unexpected TTL value %u, expected %u", ip4->ttl,
815                expected_ttl);
816       return BFD_UDP_ERROR_BAD;
817     }
818   if (clib_net_to_host_u16 (udp->src_port) < 49152)
819     {
820       BFD_ERR ("Invalid UDP src port %u, out of range <49152,65535>",
821                udp->src_port);
822     }
823   return BFD_UDP_ERROR_NONE;
824 }
825
826 typedef struct
827 {
828   u32 bs_idx;
829   bfd_pkt_t pkt;
830 } bfd_rpc_update_t;
831
832 static void
833 bfd_rpc_update_session_cb (const bfd_rpc_update_t * a)
834 {
835   bfd_consume_pkt (bfd_udp_main.bfd_main, &a->pkt, a->bs_idx);
836 }
837
838 static void
839 bfd_rpc_update_session (u32 bs_idx, const bfd_pkt_t * pkt)
840 {
841   /* packet length was already verified to be correct by the caller */
842   const u32 data_size = sizeof (bfd_rpc_update_t) -
843     STRUCT_SIZE_OF (bfd_rpc_update_t, pkt) + pkt->head.length;
844   u8 data[data_size];
845   bfd_rpc_update_t *update = (bfd_rpc_update_t *) data;
846   update->bs_idx = bs_idx;
847   clib_memcpy (&update->pkt, pkt, pkt->head.length);
848   vl_api_rpc_call_main_thread (bfd_rpc_update_session_cb, data, data_size);
849 }
850
851 static bfd_udp_error_t
852 bfd_udp4_scan (vlib_main_t * vm, vlib_node_runtime_t * rt,
853                vlib_buffer_t * b, bfd_session_t ** bs_out)
854 {
855   const bfd_pkt_t *pkt = vlib_buffer_get_current (b);
856   if (sizeof (*pkt) > b->current_length)
857     {
858       BFD_ERR
859         ("Payload size %d too small to hold bfd packet of minimum size %d",
860          b->current_length, sizeof (*pkt));
861       return BFD_UDP_ERROR_BAD;
862     }
863   ip4_header_t *ip4;
864   udp_header_t *udp;
865   bfd_udp4_find_headers (b, &ip4, &udp);
866   if (!ip4 || !udp)
867     {
868       BFD_ERR ("Couldn't find ip4 or udp header");
869       return BFD_UDP_ERROR_BAD;
870     }
871   const u32 udp_payload_length = udp->length - sizeof (*udp);
872   if (pkt->head.length > udp_payload_length)
873     {
874       BFD_ERR
875         ("BFD packet length is larger than udp payload length (%u > %u)",
876          pkt->head.length, udp_payload_length);
877       return BFD_UDP_ERROR_BAD;
878     }
879   if (!bfd_verify_pkt_common (pkt))
880     {
881       return BFD_UDP_ERROR_BAD;
882     }
883   bfd_session_t *bs = NULL;
884   if (pkt->your_disc)
885     {
886       BFD_DBG ("Looking up BFD session using discriminator %u",
887                pkt->your_disc);
888       bs = bfd_find_session_by_disc (bfd_udp_main.bfd_main, pkt->your_disc);
889     }
890   else
891     {
892       bfd_udp_key_t key;
893       memset (&key, 0, sizeof (key));
894       key.sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
895       key.local_addr.ip4.as_u32 = ip4->dst_address.as_u32;
896       key.peer_addr.ip4.as_u32 = ip4->src_address.as_u32;
897       BFD_DBG ("Looking up BFD session using key (sw_if_index=%u, local=%U, "
898                "peer=%U)",
899                key.sw_if_index, format_ip4_address, key.local_addr.ip4.as_u8,
900                format_ip4_address, key.peer_addr.ip4.as_u8);
901       bs = bfd_lookup_session (&bfd_udp_main, &key);
902     }
903   if (!bs)
904     {
905       BFD_ERR ("BFD session lookup failed - no session matches BFD pkt");
906       return BFD_UDP_ERROR_BAD;
907     }
908   BFD_DBG ("BFD session found, bs_idx=%u", bs->bs_idx);
909   if (!bfd_verify_pkt_auth (pkt, b->current_length, bs))
910     {
911       BFD_ERR ("Packet verification failed, dropping packet");
912       return BFD_UDP_ERROR_BAD;
913     }
914   bfd_udp_error_t err;
915   if (BFD_UDP_ERROR_NONE != (err = bfd_udp4_verify_transport (ip4, udp, bs)))
916     {
917       return err;
918     }
919   bfd_rpc_update_session (bs->bs_idx, pkt);
920   *bs_out = bs;
921   return BFD_UDP_ERROR_NONE;
922 }
923
924 static void
925 bfd_udp6_find_headers (vlib_buffer_t * b, ip6_header_t ** ip6,
926                        udp_header_t ** udp)
927 {
928   /* sanity check first */
929   const i32 start = vnet_buffer (b)->ip.start_of_ip_header;
930   if (start < 0 && start < sizeof (b->pre_data))
931     {
932       BFD_ERR ("Start of ip header is before pre_data, ignoring");
933       *ip6 = NULL;
934       *udp = NULL;
935       return;
936     }
937   *ip6 = (ip6_header_t *) (b->data + start);
938   if ((u8 *) * ip6 > (u8 *) vlib_buffer_get_current (b))
939     {
940       BFD_ERR ("Start of ip header is beyond current data, ignoring");
941       *ip6 = NULL;
942       *udp = NULL;
943       return;
944     }
945   if ((*ip6)->protocol != IP_PROTOCOL_UDP)
946     {
947       BFD_ERR ("Unexpected protocol in IPv6 header '%u', expected '%u' (== "
948                "IP_PROTOCOL_UDP)", (*ip6)->protocol, IP_PROTOCOL_UDP);
949       *ip6 = NULL;
950       *udp = NULL;
951       return;
952     }
953   *udp = (udp_header_t *) ((*ip6) + 1);
954 }
955
956 static bfd_udp_error_t
957 bfd_udp6_verify_transport (const ip6_header_t * ip6,
958                            const udp_header_t * udp, const bfd_session_t * bs)
959 {
960   const bfd_udp_session_t *bus = &bs->udp;
961   const bfd_udp_key_t *key = &bus->key;
962   if (ip6->src_address.as_u64[0] != key->peer_addr.ip6.as_u64[0] &&
963       ip6->src_address.as_u64[1] != key->peer_addr.ip6.as_u64[1])
964     {
965       BFD_ERR ("IP src addr mismatch, got %U, expected %U",
966                format_ip6_address, ip6, format_ip6_address,
967                &key->peer_addr.ip6);
968       return BFD_UDP_ERROR_BAD;
969     }
970   if (ip6->dst_address.as_u64[0] != key->local_addr.ip6.as_u64[0] &&
971       ip6->dst_address.as_u64[1] != key->local_addr.ip6.as_u64[1])
972     {
973       BFD_ERR ("IP dst addr mismatch, got %U, expected %U",
974                format_ip6_address, ip6, format_ip6_address,
975                &key->local_addr.ip6);
976       return BFD_UDP_ERROR_BAD;
977     }
978   const u8 expected_hop_limit = 255;
979   if (ip6->hop_limit != expected_hop_limit)
980     {
981       BFD_ERR ("IPv6 unexpected hop-limit value %u, expected %u",
982                ip6->hop_limit, expected_hop_limit);
983       return BFD_UDP_ERROR_BAD;
984     }
985   if (clib_net_to_host_u16 (udp->src_port) < 49152)
986     {
987       BFD_ERR ("Invalid UDP src port %u, out of range <49152,65535>",
988                udp->src_port);
989     }
990   return BFD_UDP_ERROR_NONE;
991 }
992
993 static bfd_udp_error_t
994 bfd_udp6_scan (vlib_main_t * vm, vlib_node_runtime_t * rt,
995                vlib_buffer_t * b, bfd_session_t ** bs_out)
996 {
997   const bfd_pkt_t *pkt = vlib_buffer_get_current (b);
998   if (sizeof (*pkt) > b->current_length)
999     {
1000       BFD_ERR
1001         ("Payload size %d too small to hold bfd packet of minimum size %d",
1002          b->current_length, sizeof (*pkt));
1003       return BFD_UDP_ERROR_BAD;
1004     }
1005   ip6_header_t *ip6;
1006   udp_header_t *udp;
1007   bfd_udp6_find_headers (b, &ip6, &udp);
1008   if (!ip6 || !udp)
1009     {
1010       BFD_ERR ("Couldn't find ip6 or udp header");
1011       return BFD_UDP_ERROR_BAD;
1012     }
1013   const u32 udp_payload_length = udp->length - sizeof (*udp);
1014   if (pkt->head.length > udp_payload_length)
1015     {
1016       BFD_ERR
1017         ("BFD packet length is larger than udp payload length (%u > %u)",
1018          pkt->head.length, udp_payload_length);
1019       return BFD_UDP_ERROR_BAD;
1020     }
1021   if (!bfd_verify_pkt_common (pkt))
1022     {
1023       return BFD_UDP_ERROR_BAD;
1024     }
1025   bfd_session_t *bs = NULL;
1026   if (pkt->your_disc)
1027     {
1028       BFD_DBG ("Looking up BFD session using discriminator %u",
1029                pkt->your_disc);
1030       bs = bfd_find_session_by_disc (bfd_udp_main.bfd_main, pkt->your_disc);
1031     }
1032   else
1033     {
1034       bfd_udp_key_t key;
1035       memset (&key, 0, sizeof (key));
1036       key.sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
1037       key.local_addr.ip6.as_u64[0] = ip6->dst_address.as_u64[0];
1038       key.local_addr.ip6.as_u64[1] = ip6->dst_address.as_u64[1];
1039       key.peer_addr.ip6.as_u64[0] = ip6->src_address.as_u64[0];
1040       key.peer_addr.ip6.as_u64[1] = ip6->src_address.as_u64[1];
1041       BFD_DBG ("Looking up BFD session using key (sw_if_index=%u, local=%U, "
1042                "peer=%U)",
1043                key.sw_if_index, format_ip6_address, &key.local_addr,
1044                format_ip6_address, &key.peer_addr);
1045       bs = bfd_lookup_session (&bfd_udp_main, &key);
1046     }
1047   if (!bs)
1048     {
1049       BFD_ERR ("BFD session lookup failed - no session matches BFD pkt");
1050       return BFD_UDP_ERROR_BAD;
1051     }
1052   BFD_DBG ("BFD session found, bs_idx=%u", bs->bs_idx);
1053   if (!bfd_verify_pkt_auth (pkt, b->current_length, bs))
1054     {
1055       BFD_ERR ("Packet verification failed, dropping packet");
1056       return BFD_UDP_ERROR_BAD;
1057     }
1058   bfd_udp_error_t err;
1059   if (BFD_UDP_ERROR_NONE != (err = bfd_udp6_verify_transport (ip6, udp, bs)))
1060     {
1061       return err;
1062     }
1063   bfd_rpc_update_session (bs->bs_idx, pkt);
1064   *bs_out = bs;
1065   return BFD_UDP_ERROR_NONE;
1066 }
1067
1068 /*
1069  * Process a frame of bfd packets
1070  * Expect 1 packet / frame
1071  */
1072 static uword
1073 bfd_udp_input (vlib_main_t * vm, vlib_node_runtime_t * rt,
1074                vlib_frame_t * f, int is_ipv6)
1075 {
1076   u32 n_left_from, *from;
1077   bfd_input_trace_t *t0;
1078
1079   from = vlib_frame_vector_args (f);    /* array of buffer indices */
1080   n_left_from = f->n_vectors;   /* number of buffer indices */
1081
1082   while (n_left_from > 0)
1083     {
1084       u32 bi0;
1085       vlib_buffer_t *b0;
1086       u32 next0, error0;
1087
1088       bi0 = from[0];
1089       b0 = vlib_get_buffer (vm, bi0);
1090
1091       bfd_session_t *bs = NULL;
1092
1093       /* If this pkt is traced, snapshot the data */
1094       if (b0->flags & VLIB_BUFFER_IS_TRACED)
1095         {
1096           int len;
1097           t0 = vlib_add_trace (vm, rt, b0, sizeof (*t0));
1098           len = (b0->current_length < sizeof (t0->data)) ? b0->current_length
1099             : sizeof (t0->data);
1100           t0->len = len;
1101           clib_memcpy (t0->data, vlib_buffer_get_current (b0), len);
1102         }
1103
1104       /* scan this bfd pkt. error0 is the counter index to bmp */
1105       if (is_ipv6)
1106         {
1107           error0 = bfd_udp6_scan (vm, rt, b0, &bs);
1108         }
1109       else
1110         {
1111           error0 = bfd_udp4_scan (vm, rt, b0, &bs);
1112         }
1113       b0->error = rt->errors[error0];
1114
1115       next0 = BFD_UDP_INPUT_NEXT_NORMAL;
1116       if (BFD_UDP_ERROR_NONE == error0)
1117         {
1118           /*
1119            *  if everything went fine, check for poll bit, if present, re-use
1120            *  the buffer and based on (now updated) session parameters, send
1121            *  the final packet back
1122            */
1123           const bfd_pkt_t *pkt = vlib_buffer_get_current (b0);
1124           if (bfd_pkt_get_poll (pkt))
1125             {
1126               bfd_init_final_control_frame (vm, b0, bfd_udp_main.bfd_main,
1127                                             bs);
1128               if (is_ipv6)
1129                 {
1130                   vlib_node_increment_counter (vm, bfd_udp6_input_node.index,
1131                                                b0->error, 1);
1132                 }
1133               else
1134                 {
1135                   vlib_node_increment_counter (vm, bfd_udp4_input_node.index,
1136                                                b0->error, 1);
1137                 }
1138               next0 = BFD_UDP_INPUT_NEXT_REPLY;
1139             }
1140         }
1141       vlib_set_next_frame_buffer (vm, rt, next0, bi0);
1142
1143       from += 1;
1144       n_left_from -= 1;
1145     }
1146
1147   return f->n_vectors;
1148 }
1149
1150 static uword
1151 bfd_udp4_input (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f)
1152 {
1153   return bfd_udp_input (vm, rt, f, 0);
1154 }
1155
1156 /*
1157  * bfd input graph node declaration
1158  */
1159 /* *INDENT-OFF* */
1160 VLIB_REGISTER_NODE (bfd_udp4_input_node, static) = {
1161   .function = bfd_udp4_input,
1162   .name = "bfd-udp4-input",
1163   .vector_size = sizeof (u32),
1164   .type = VLIB_NODE_TYPE_INTERNAL,
1165
1166   .n_errors = BFD_UDP_N_ERROR,
1167   .error_strings = bfd_udp_error_strings,
1168
1169   .format_trace = bfd_input_format_trace,
1170
1171   .n_next_nodes = BFD_UDP_INPUT_N_NEXT,
1172   .next_nodes =
1173       {
1174               [BFD_UDP_INPUT_NEXT_NORMAL] = "error-drop",
1175               [BFD_UDP_INPUT_NEXT_REPLY] = "ip4-lookup",
1176       },
1177 };
1178 /* *INDENT-ON* */
1179
1180 static uword
1181 bfd_udp6_input (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f)
1182 {
1183   return bfd_udp_input (vm, rt, f, 1);
1184 }
1185
1186 /* *INDENT-OFF* */
1187 VLIB_REGISTER_NODE (bfd_udp6_input_node, static) = {
1188   .function = bfd_udp6_input,
1189   .name = "bfd-udp6-input",
1190   .vector_size = sizeof (u32),
1191   .type = VLIB_NODE_TYPE_INTERNAL,
1192
1193   .n_errors = BFD_UDP_N_ERROR,
1194   .error_strings = bfd_udp_error_strings,
1195
1196   .format_trace = bfd_input_format_trace,
1197
1198   .n_next_nodes = BFD_UDP_INPUT_N_NEXT,
1199   .next_nodes =
1200       {
1201               [BFD_UDP_INPUT_NEXT_NORMAL] = "error-drop",
1202               [BFD_UDP_INPUT_NEXT_REPLY] = "ip6-lookup",
1203       },
1204 };
1205 /* *INDENT-ON* */
1206
1207 /*
1208  * Process a frame of bfd echo packets
1209  * Expect 1 packet / frame
1210  */
1211 static uword
1212 bfd_udp_echo_input (vlib_main_t * vm, vlib_node_runtime_t * rt,
1213                     vlib_frame_t * f, int is_ipv6)
1214 {
1215   u32 n_left_from, *from;
1216   bfd_input_trace_t *t0;
1217
1218   from = vlib_frame_vector_args (f);    /* array of buffer indices */
1219   n_left_from = f->n_vectors;   /* number of buffer indices */
1220
1221   while (n_left_from > 0)
1222     {
1223       u32 bi0;
1224       vlib_buffer_t *b0;
1225       u32 next0;
1226
1227       bi0 = from[0];
1228       b0 = vlib_get_buffer (vm, bi0);
1229
1230       /* If this pkt is traced, snapshot the data */
1231       if (b0->flags & VLIB_BUFFER_IS_TRACED)
1232         {
1233           int len;
1234           t0 = vlib_add_trace (vm, rt, b0, sizeof (*t0));
1235           len = (b0->current_length < sizeof (t0->data)) ? b0->current_length
1236             : sizeof (t0->data);
1237           t0->len = len;
1238           clib_memcpy (t0->data, vlib_buffer_get_current (b0), len);
1239         }
1240
1241       if (bfd_consume_echo_pkt (bfd_udp_main.bfd_main, b0))
1242         {
1243           b0->error = rt->errors[BFD_UDP_ERROR_NONE];
1244           next0 = BFD_UDP_INPUT_NEXT_NORMAL;
1245         }
1246       else
1247         {
1248           /* loop back the packet */
1249           b0->error = rt->errors[BFD_UDP_ERROR_NONE];
1250           if (is_ipv6)
1251             {
1252               vlib_node_increment_counter (vm, bfd_udp_echo6_input_node.index,
1253                                            b0->error, 1);
1254             }
1255           else
1256             {
1257               vlib_node_increment_counter (vm, bfd_udp_echo4_input_node.index,
1258                                            b0->error, 1);
1259             }
1260           next0 = BFD_UDP_INPUT_NEXT_REPLY;
1261         }
1262
1263       vlib_set_next_frame_buffer (vm, rt, next0, bi0);
1264
1265       from += 1;
1266       n_left_from -= 1;
1267     }
1268
1269   return f->n_vectors;
1270 }
1271
1272 static uword
1273 bfd_udp_echo4_input (vlib_main_t * vm, vlib_node_runtime_t * rt,
1274                      vlib_frame_t * f)
1275 {
1276   return bfd_udp_echo_input (vm, rt, f, 0);
1277 }
1278
1279 u8 *
1280 bfd_echo_input_format_trace (u8 * s, va_list * args)
1281 {
1282   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
1283   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
1284   const bfd_udp_echo_input_trace_t *t =
1285     va_arg (*args, bfd_udp_echo_input_trace_t *);
1286   if (t->len > STRUCT_SIZE_OF (bfd_pkt_t, head))
1287     {
1288       s = format (s, "BFD ECHO:\n");
1289       s = format (s, "    data: %U", format_hexdump, t->data, t->len);
1290     }
1291
1292   return s;
1293 }
1294
1295 /*
1296  * bfd input graph node declaration
1297  */
1298 /* *INDENT-OFF* */
1299 VLIB_REGISTER_NODE (bfd_udp_echo4_input_node, static) = {
1300   .function = bfd_udp_echo4_input,
1301   .name = "bfd-udp-echo4-input",
1302   .vector_size = sizeof (u32),
1303   .type = VLIB_NODE_TYPE_INTERNAL,
1304
1305   .n_errors = BFD_UDP_ECHO_N_ERROR,
1306   .error_strings = bfd_udp_error_strings,
1307
1308   .format_trace = bfd_echo_input_format_trace,
1309
1310   .n_next_nodes = BFD_UDP_INPUT_N_NEXT,
1311   .next_nodes =
1312       {
1313               [BFD_UDP_INPUT_NEXT_NORMAL] = "error-drop",
1314               [BFD_UDP_INPUT_NEXT_REPLY] = "ip4-lookup",
1315       },
1316 };
1317 /* *INDENT-ON* */
1318
1319 static uword
1320 bfd_udp_echo6_input (vlib_main_t * vm, vlib_node_runtime_t * rt,
1321                      vlib_frame_t * f)
1322 {
1323   return bfd_udp_echo_input (vm, rt, f, 1);
1324 }
1325
1326 /* *INDENT-OFF* */
1327 VLIB_REGISTER_NODE (bfd_udp_echo6_input_node, static) = {
1328   .function = bfd_udp_echo6_input,
1329   .name = "bfd-udp-echo6-input",
1330   .vector_size = sizeof (u32),
1331   .type = VLIB_NODE_TYPE_INTERNAL,
1332
1333   .n_errors = BFD_UDP_ECHO_N_ERROR,
1334   .error_strings = bfd_udp_echo_error_strings,
1335
1336   .format_trace = bfd_echo_input_format_trace,
1337
1338   .n_next_nodes = BFD_UDP_INPUT_N_NEXT,
1339   .next_nodes =
1340       {
1341               [BFD_UDP_INPUT_NEXT_NORMAL] = "error-drop",
1342               [BFD_UDP_INPUT_NEXT_REPLY] = "ip6-lookup",
1343       },
1344 };
1345
1346 /* *INDENT-ON* */
1347
1348 static clib_error_t *
1349 bfd_sw_interface_up_down (vnet_main_t * vnm, u32 sw_if_index, u32 flags)
1350 {
1351   // vnet_hw_interface_t *hi = vnet_get_sup_hw_interface (vnm, sw_if_index);
1352   if (!(flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP))
1353     {
1354       /* TODO */
1355     }
1356   return 0;
1357 }
1358
1359 VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION (bfd_sw_interface_up_down);
1360
1361 static clib_error_t *
1362 bfd_hw_interface_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
1363 {
1364   if (flags & VNET_HW_INTERFACE_FLAG_LINK_UP)
1365     {
1366       /* TODO */
1367     }
1368   return 0;
1369 }
1370
1371 VNET_HW_INTERFACE_LINK_UP_DOWN_FUNCTION (bfd_hw_interface_up_down);
1372
1373 /*
1374  * setup function
1375  */
1376 static clib_error_t *
1377 bfd_udp_init (vlib_main_t * vm)
1378 {
1379   mhash_init (&bfd_udp_main.bfd_session_idx_by_bfd_key, sizeof (uword),
1380               sizeof (bfd_udp_key_t));
1381   bfd_udp_main.bfd_main = &bfd_main;
1382   bfd_udp_main.vnet_main = vnet_get_main ();
1383   udp_register_dst_port (vm, UDP_DST_PORT_bfd4, bfd_udp4_input_node.index, 1);
1384   udp_register_dst_port (vm, UDP_DST_PORT_bfd6, bfd_udp6_input_node.index, 0);
1385   udp_register_dst_port (vm, UDP_DST_PORT_bfd_echo4,
1386                          bfd_udp_echo4_input_node.index, 1);
1387   udp_register_dst_port (vm, UDP_DST_PORT_bfd_echo6,
1388                          bfd_udp_echo6_input_node.index, 0);
1389   return 0;
1390 }
1391
1392 VLIB_INIT_FUNCTION (bfd_udp_init);
1393
1394 /*
1395  * fd.io coding-style-patch-verification: ON
1396  *
1397  * Local Variables:
1398  * eval: (c-set-style "gnu")
1399  * End:
1400  */