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