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