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