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