bfd: remove source IP check from session add
[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 (NONE, "good bfd packets (processed)") \
936   F (BAD, "invalid bfd packets")
937
938 #define F(sym, string) static char BFD_UDP_ERR_##sym##_STR[] = string;
939 foreach_bfd_udp_error (F);
940 #undef F
941
942 static char *bfd_udp_error_strings[] = {
943 #define F(sym, string) BFD_UDP_ERR_##sym##_STR,
944   foreach_bfd_udp_error (F)
945 #undef F
946 };
947
948 typedef enum
949 {
950 #define F(sym, str) BFD_UDP_ERROR_##sym,
951   foreach_bfd_udp_error (F)
952 #undef F
953     BFD_UDP_N_ERROR,
954 } bfd_udp_error_t;
955
956 typedef enum
957 {
958   BFD_UDP_ECHO_INPUT_NEXT_NORMAL,
959   BFD_UDP_ECHO_INPUT_NEXT_REPLY_ARP,
960   BFD_UDP_ECHO_INPUT_NEXT_REPLY_REWRITE,
961   BFD_UDP_ECHO_INPUT_N_NEXT,
962 } bfd_udp_echo_input_next_t;
963
964 /* Packet counters - BFD ECHO packets */
965 #define foreach_bfd_udp_echo_error(F)           \
966   F (NONE, "good bfd echo packets (processed)") \
967   F (BAD, "invalid bfd echo packets")
968
969 #define F(sym, string) static char BFD_UDP_ECHO_ERR_##sym##_STR[] = string;
970 foreach_bfd_udp_echo_error (F);
971 #undef F
972
973 static char *bfd_udp_echo_error_strings[] = {
974 #define F(sym, string) BFD_UDP_ECHO_ERR_##sym##_STR,
975   foreach_bfd_udp_echo_error (F)
976 #undef F
977 };
978
979 typedef enum
980 {
981 #define F(sym, str) BFD_UDP_ECHO_ERROR_##sym,
982   foreach_bfd_udp_echo_error (F)
983 #undef F
984     BFD_UDP_ECHO_N_ERROR,
985 } bfd_udp_echo_error_t;
986
987 static void
988 bfd_udp4_find_headers (vlib_buffer_t * b, ip4_header_t ** ip4,
989                        udp_header_t ** udp)
990 {
991   /* sanity check first */
992   const i32 start = vnet_buffer (b)->l3_hdr_offset;
993   if (start < -(signed) sizeof (b->pre_data))
994     {
995       BFD_ERR ("Start of ip header is before pre_data, ignoring");
996       *ip4 = NULL;
997       *udp = NULL;
998       return;
999     }
1000   *ip4 = (ip4_header_t *) (b->data + start);
1001   if ((u8 *) * ip4 > (u8 *) vlib_buffer_get_current (b))
1002     {
1003       BFD_ERR ("Start of ip header is beyond current data, ignoring");
1004       *ip4 = NULL;
1005       *udp = NULL;
1006       return;
1007     }
1008   *udp = (udp_header_t *) ((*ip4) + 1);
1009 }
1010
1011 static bfd_udp_error_t
1012 bfd_udp4_verify_transport (const ip4_header_t * ip4,
1013                            const udp_header_t * udp, const bfd_session_t * bs)
1014 {
1015   const bfd_udp_session_t *bus = &bs->udp;
1016   const bfd_udp_key_t *key = &bus->key;
1017   if (ip4->src_address.as_u32 != key->peer_addr.ip4.as_u32)
1018     {
1019       BFD_ERR ("IPv4 src addr mismatch, got %U, expected %U",
1020                format_ip4_address, ip4->src_address.as_u8, format_ip4_address,
1021                key->peer_addr.ip4.as_u8);
1022       return BFD_UDP_ERROR_BAD;
1023     }
1024   if (ip4->dst_address.as_u32 != key->local_addr.ip4.as_u32)
1025     {
1026       BFD_ERR ("IPv4 dst addr mismatch, got %U, expected %U",
1027                format_ip4_address, ip4->dst_address.as_u8, format_ip4_address,
1028                key->local_addr.ip4.as_u8);
1029       return BFD_UDP_ERROR_BAD;
1030     }
1031   const u8 expected_ttl = 255;
1032   if (ip4->ttl != expected_ttl)
1033     {
1034       BFD_ERR ("IPv4 unexpected TTL value %u, expected %u", ip4->ttl,
1035                expected_ttl);
1036       return BFD_UDP_ERROR_BAD;
1037     }
1038   if (clib_net_to_host_u16 (udp->src_port) < 49152)
1039     {
1040       BFD_ERR ("Invalid UDP src port %u, out of range <49152,65535>",
1041                udp->src_port);
1042     }
1043   return BFD_UDP_ERROR_NONE;
1044 }
1045
1046 typedef struct
1047 {
1048   u32 bs_idx;
1049   bfd_pkt_t pkt;
1050 } bfd_rpc_update_t;
1051
1052 static void
1053 bfd_rpc_update_session (vlib_main_t * vm, u32 bs_idx, const bfd_pkt_t * pkt)
1054 {
1055   bfd_main_t *bm = &bfd_main;
1056   bfd_lock (bm);
1057   bfd_consume_pkt (vm, bm, pkt, bs_idx);
1058   bfd_unlock (bm);
1059 }
1060
1061 static bfd_udp_error_t
1062 bfd_udp4_scan (vlib_main_t *vm, vlib_buffer_t *b, bfd_session_t **bs_out)
1063 {
1064   const bfd_pkt_t *pkt = vlib_buffer_get_current (b);
1065   if (sizeof (*pkt) > b->current_length)
1066     {
1067       BFD_ERR
1068         ("Payload size %d too small to hold bfd packet of minimum size %d",
1069          b->current_length, sizeof (*pkt));
1070       return BFD_UDP_ERROR_BAD;
1071     }
1072   ip4_header_t *ip4;
1073   udp_header_t *udp;
1074   bfd_udp4_find_headers (b, &ip4, &udp);
1075   if (!ip4 || !udp)
1076     {
1077       BFD_ERR ("Couldn't find ip4 or udp header");
1078       return BFD_UDP_ERROR_BAD;
1079     }
1080   const u32 udp_payload_length = udp->length - sizeof (*udp);
1081   if (pkt->head.length > udp_payload_length)
1082     {
1083       BFD_ERR
1084         ("BFD packet length is larger than udp payload length (%u > %u)",
1085          pkt->head.length, udp_payload_length);
1086       return BFD_UDP_ERROR_BAD;
1087     }
1088   if (!bfd_verify_pkt_common (pkt))
1089     {
1090       return BFD_UDP_ERROR_BAD;
1091     }
1092   bfd_session_t *bs = NULL;
1093   if (pkt->your_disc)
1094     {
1095       BFD_DBG ("Looking up BFD session using discriminator %u",
1096                pkt->your_disc);
1097       bs = bfd_find_session_by_disc (bfd_udp_main.bfd_main, pkt->your_disc);
1098     }
1099   else
1100     {
1101       bfd_udp_key_t key;
1102       clib_memset (&key, 0, sizeof (key));
1103       key.sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
1104       key.local_addr.ip4.as_u32 = ip4->dst_address.as_u32;
1105       key.peer_addr.ip4.as_u32 = ip4->src_address.as_u32;
1106       BFD_DBG ("Looking up BFD session using key (sw_if_index=%u, local=%U, "
1107                "peer=%U)",
1108                key.sw_if_index, format_ip4_address, key.local_addr.ip4.as_u8,
1109                format_ip4_address, key.peer_addr.ip4.as_u8);
1110       bs = bfd_lookup_session (&bfd_udp_main, &key);
1111     }
1112   if (!bs)
1113     {
1114       BFD_ERR ("BFD session lookup failed - no session matches BFD pkt");
1115       return BFD_UDP_ERROR_BAD;
1116     }
1117   BFD_DBG ("BFD session found, bs_idx=%u", bs->bs_idx);
1118   if (!bfd_verify_pkt_auth (vm, pkt, b->current_length, bs))
1119     {
1120       BFD_ERR ("Packet verification failed, dropping packet");
1121       return BFD_UDP_ERROR_BAD;
1122     }
1123   bfd_udp_error_t err;
1124   if (BFD_UDP_ERROR_NONE != (err = bfd_udp4_verify_transport (ip4, udp, bs)))
1125     {
1126       return err;
1127     }
1128   bfd_rpc_update_session (vm, bs->bs_idx, pkt);
1129   *bs_out = bs;
1130   return BFD_UDP_ERROR_NONE;
1131 }
1132
1133 static void
1134 bfd_udp6_find_headers (vlib_buffer_t * b, ip6_header_t ** ip6,
1135                        udp_header_t ** udp)
1136 {
1137   /* sanity check first */
1138   const i32 start = vnet_buffer (b)->l3_hdr_offset;
1139   if (start < -(signed) sizeof (b->pre_data))
1140     {
1141       BFD_ERR ("Start of ip header is before pre_data, ignoring");
1142       *ip6 = NULL;
1143       *udp = NULL;
1144       return;
1145     }
1146   *ip6 = (ip6_header_t *) (b->data + start);
1147   if ((u8 *) * ip6 > (u8 *) vlib_buffer_get_current (b))
1148     {
1149       BFD_ERR ("Start of ip header is beyond current data, ignoring");
1150       *ip6 = NULL;
1151       *udp = NULL;
1152       return;
1153     }
1154   if ((*ip6)->protocol != IP_PROTOCOL_UDP)
1155     {
1156       BFD_ERR ("Unexpected protocol in IPv6 header '%u', expected '%u' (== "
1157                "IP_PROTOCOL_UDP)", (*ip6)->protocol, IP_PROTOCOL_UDP);
1158       *ip6 = NULL;
1159       *udp = NULL;
1160       return;
1161     }
1162   *udp = (udp_header_t *) ((*ip6) + 1);
1163 }
1164
1165 static bfd_udp_error_t
1166 bfd_udp6_verify_transport (const ip6_header_t * ip6,
1167                            const udp_header_t * udp, const bfd_session_t * bs)
1168 {
1169   const bfd_udp_session_t *bus = &bs->udp;
1170   const bfd_udp_key_t *key = &bus->key;
1171   if (ip6->src_address.as_u64[0] != key->peer_addr.ip6.as_u64[0] &&
1172       ip6->src_address.as_u64[1] != key->peer_addr.ip6.as_u64[1])
1173     {
1174       BFD_ERR ("IP src addr mismatch, got %U, expected %U",
1175                format_ip6_address, ip6, format_ip6_address,
1176                &key->peer_addr.ip6);
1177       return BFD_UDP_ERROR_BAD;
1178     }
1179   if (ip6->dst_address.as_u64[0] != key->local_addr.ip6.as_u64[0] &&
1180       ip6->dst_address.as_u64[1] != key->local_addr.ip6.as_u64[1])
1181     {
1182       BFD_ERR ("IP dst addr mismatch, got %U, expected %U",
1183                format_ip6_address, ip6, format_ip6_address,
1184                &key->local_addr.ip6);
1185       return BFD_UDP_ERROR_BAD;
1186     }
1187   const u8 expected_hop_limit = 255;
1188   if (ip6->hop_limit != expected_hop_limit)
1189     {
1190       BFD_ERR ("IPv6 unexpected hop-limit value %u, expected %u",
1191                ip6->hop_limit, expected_hop_limit);
1192       return BFD_UDP_ERROR_BAD;
1193     }
1194   if (clib_net_to_host_u16 (udp->src_port) < 49152)
1195     {
1196       BFD_ERR ("Invalid UDP src port %u, out of range <49152,65535>",
1197                udp->src_port);
1198     }
1199   return BFD_UDP_ERROR_NONE;
1200 }
1201
1202 static bfd_udp_error_t
1203 bfd_udp6_scan (vlib_main_t *vm, vlib_buffer_t *b, bfd_session_t **bs_out)
1204 {
1205   const bfd_pkt_t *pkt = vlib_buffer_get_current (b);
1206   if (sizeof (*pkt) > b->current_length)
1207     {
1208       BFD_ERR
1209         ("Payload size %d too small to hold bfd packet of minimum size %d",
1210          b->current_length, sizeof (*pkt));
1211       return BFD_UDP_ERROR_BAD;
1212     }
1213   ip6_header_t *ip6;
1214   udp_header_t *udp;
1215   bfd_udp6_find_headers (b, &ip6, &udp);
1216   if (!ip6 || !udp)
1217     {
1218       BFD_ERR ("Couldn't find ip6 or udp header");
1219       return BFD_UDP_ERROR_BAD;
1220     }
1221   const u32 udp_payload_length = udp->length - sizeof (*udp);
1222   if (pkt->head.length > udp_payload_length)
1223     {
1224       BFD_ERR
1225         ("BFD packet length is larger than udp payload length (%u > %u)",
1226          pkt->head.length, udp_payload_length);
1227       return BFD_UDP_ERROR_BAD;
1228     }
1229   if (!bfd_verify_pkt_common (pkt))
1230     {
1231       return BFD_UDP_ERROR_BAD;
1232     }
1233   bfd_session_t *bs = NULL;
1234   if (pkt->your_disc)
1235     {
1236       BFD_DBG ("Looking up BFD session using discriminator %u",
1237                pkt->your_disc);
1238       bs = bfd_find_session_by_disc (bfd_udp_main.bfd_main, pkt->your_disc);
1239     }
1240   else
1241     {
1242       bfd_udp_key_t key;
1243       clib_memset (&key, 0, sizeof (key));
1244       key.sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
1245       key.local_addr.ip6.as_u64[0] = ip6->dst_address.as_u64[0];
1246       key.local_addr.ip6.as_u64[1] = ip6->dst_address.as_u64[1];
1247       key.peer_addr.ip6.as_u64[0] = ip6->src_address.as_u64[0];
1248       key.peer_addr.ip6.as_u64[1] = ip6->src_address.as_u64[1];
1249       BFD_DBG ("Looking up BFD session using key (sw_if_index=%u, local=%U, "
1250                "peer=%U)",
1251                key.sw_if_index, format_ip6_address, &key.local_addr,
1252                format_ip6_address, &key.peer_addr);
1253       bs = bfd_lookup_session (&bfd_udp_main, &key);
1254     }
1255   if (!bs)
1256     {
1257       BFD_ERR ("BFD session lookup failed - no session matches BFD pkt");
1258       return BFD_UDP_ERROR_BAD;
1259     }
1260   BFD_DBG ("BFD session found, bs_idx=%u", bs->bs_idx);
1261   if (!bfd_verify_pkt_auth (vm, pkt, b->current_length, bs))
1262     {
1263       BFD_ERR ("Packet verification failed, dropping packet");
1264       return BFD_UDP_ERROR_BAD;
1265     }
1266   bfd_udp_error_t err;
1267   if (BFD_UDP_ERROR_NONE != (err = bfd_udp6_verify_transport (ip6, udp, bs)))
1268     {
1269       return err;
1270     }
1271   bfd_rpc_update_session (vm, bs->bs_idx, pkt);
1272   *bs_out = bs;
1273   return BFD_UDP_ERROR_NONE;
1274 }
1275
1276 /*
1277  * Process a frame of bfd packets
1278  * Expect 1 packet / frame
1279  */
1280 static uword
1281 bfd_udp_input (vlib_main_t * vm, vlib_node_runtime_t * rt,
1282                vlib_frame_t * f, int is_ipv6)
1283 {
1284   u32 n_left_from, *from;
1285   bfd_input_trace_t *t0;
1286   bfd_main_t *bm = &bfd_main;
1287
1288   from = vlib_frame_vector_args (f);    /* array of buffer indices */
1289   n_left_from = f->n_vectors;   /* number of buffer indices */
1290
1291   while (n_left_from > 0)
1292     {
1293       u32 bi0;
1294       vlib_buffer_t *b0;
1295       u32 next0, error0;
1296
1297       bi0 = from[0];
1298       b0 = vlib_get_buffer (vm, bi0);
1299
1300       bfd_session_t *bs = NULL;
1301
1302       /* If this pkt is traced, snapshot the data */
1303       if (b0->flags & VLIB_BUFFER_IS_TRACED)
1304         {
1305           u64 len;
1306           t0 = vlib_add_trace (vm, rt, b0, sizeof (*t0));
1307           len = (b0->current_length < sizeof (t0->data)) ? b0->current_length
1308             : sizeof (t0->data);
1309           t0->len = len;
1310           clib_memcpy_fast (t0->data, vlib_buffer_get_current (b0), len);
1311         }
1312
1313       /* scan this bfd pkt. error0 is the counter index to bmp */
1314       bfd_lock (bm);
1315       if (is_ipv6)
1316         {
1317           error0 = bfd_udp6_scan (vm, b0, &bs);
1318         }
1319       else
1320         {
1321           error0 = bfd_udp4_scan (vm, b0, &bs);
1322         }
1323       b0->error = rt->errors[error0];
1324
1325       next0 = BFD_UDP_INPUT_NEXT_NORMAL;
1326       if (BFD_UDP_ERROR_NONE == error0)
1327         {
1328           vlib_increment_combined_counter (
1329             &bm->rx_counter, vm->thread_index, bs->bs_idx, 1,
1330             vlib_buffer_length_in_chain (vm, b0));
1331           /*
1332            *  if everything went fine, check for poll bit, if present, re-use
1333            *  the buffer and based on (now updated) session parameters, send
1334            *  the final packet back
1335            */
1336           const bfd_pkt_t *pkt = vlib_buffer_get_current (b0);
1337           if (bfd_pkt_get_poll (pkt))
1338             {
1339               b0->current_data = 0;
1340               b0->current_length = 0;
1341               bfd_init_final_control_frame (vm, b0, bs);
1342               if (is_ipv6)
1343                 {
1344                   vlib_node_increment_counter (vm, bfd_udp6_input_node.index,
1345                                                b0->error, 1);
1346                 }
1347               else
1348                 {
1349                   vlib_node_increment_counter (vm, bfd_udp4_input_node.index,
1350                                                b0->error, 1);
1351                 }
1352               const bfd_udp_session_t *bus = &bs->udp;
1353               ip_adjacency_t *adj = adj_get (bus->adj_index);
1354               switch (adj->lookup_next_index)
1355                 {
1356                 case IP_LOOKUP_NEXT_ARP:
1357                   next0 = BFD_UDP_INPUT_NEXT_REPLY_ARP;
1358                   break;
1359                 case IP_LOOKUP_NEXT_REWRITE:
1360                   next0 = BFD_UDP_INPUT_NEXT_REPLY_REWRITE;
1361                   break;
1362                 case IP_LOOKUP_NEXT_MIDCHAIN:
1363                   next0 = BFD_UDP_INPUT_NEXT_REPLY_MIDCHAIN;
1364                   break;
1365                 default:
1366                   /* drop */
1367                   break;
1368                 }
1369             }
1370         }
1371       bfd_unlock (bm);
1372       vlib_set_next_frame_buffer (vm, rt, next0, bi0);
1373
1374       from += 1;
1375       n_left_from -= 1;
1376     }
1377
1378   return f->n_vectors;
1379 }
1380
1381 static uword
1382 bfd_udp4_input (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f)
1383 {
1384   return bfd_udp_input (vm, rt, f, 0);
1385 }
1386
1387 /*
1388  * bfd input graph node declaration
1389  */
1390 /* *INDENT-OFF* */
1391 VLIB_REGISTER_NODE (bfd_udp4_input_node, static) = {
1392   .function = bfd_udp4_input,
1393   .name = "bfd-udp4-input",
1394   .vector_size = sizeof (u32),
1395   .type = VLIB_NODE_TYPE_INTERNAL,
1396
1397   .n_errors = BFD_UDP_N_ERROR,
1398   .error_strings = bfd_udp_error_strings,
1399
1400   .format_trace = bfd_input_format_trace,
1401
1402   .n_next_nodes = BFD_UDP_INPUT_N_NEXT,
1403   .next_nodes =
1404       {
1405               [BFD_UDP_INPUT_NEXT_NORMAL] = "error-drop",
1406               [BFD_UDP_INPUT_NEXT_REPLY_ARP] = "ip4-arp",
1407               [BFD_UDP_INPUT_NEXT_REPLY_REWRITE] = "ip4-lookup",
1408               [BFD_UDP_INPUT_NEXT_REPLY_MIDCHAIN] = "ip4-midchain",
1409       },
1410 };
1411 /* *INDENT-ON* */
1412
1413 static uword
1414 bfd_udp6_input (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f)
1415 {
1416   return bfd_udp_input (vm, rt, f, 1);
1417 }
1418
1419 /* *INDENT-OFF* */
1420 VLIB_REGISTER_NODE (bfd_udp6_input_node, static) = {
1421   .function = bfd_udp6_input,
1422   .name = "bfd-udp6-input",
1423   .vector_size = sizeof (u32),
1424   .type = VLIB_NODE_TYPE_INTERNAL,
1425
1426   .n_errors = BFD_UDP_N_ERROR,
1427   .error_strings = bfd_udp_error_strings,
1428
1429   .format_trace = bfd_input_format_trace,
1430
1431   .n_next_nodes = BFD_UDP_INPUT_N_NEXT,
1432   .next_nodes =
1433       {
1434               [BFD_UDP_INPUT_NEXT_NORMAL] = "error-drop",
1435               [BFD_UDP_INPUT_NEXT_REPLY_ARP] = "ip6-discover-neighbor",
1436               [BFD_UDP_INPUT_NEXT_REPLY_REWRITE] = "ip6-lookup",
1437               [BFD_UDP_INPUT_NEXT_REPLY_MIDCHAIN] = "ip6-midchain",
1438       },
1439 };
1440 /* *INDENT-ON* */
1441
1442 /*
1443  * Process a frame of bfd echo packets
1444  * Expect 1 packet / frame
1445  */
1446 static uword
1447 bfd_udp_echo_input (vlib_main_t * vm, vlib_node_runtime_t * rt,
1448                     vlib_frame_t * f, int is_ipv6)
1449 {
1450   u32 n_left_from, *from;
1451   bfd_input_trace_t *t0;
1452   bfd_main_t *bm = &bfd_main;
1453
1454   from = vlib_frame_vector_args (f);    /* array of buffer indices */
1455   n_left_from = f->n_vectors;   /* number of buffer indices */
1456
1457   while (n_left_from > 0)
1458     {
1459       u32 bi0;
1460       vlib_buffer_t *b0;
1461       u32 next0;
1462
1463       bi0 = from[0];
1464       b0 = vlib_get_buffer (vm, bi0);
1465
1466       /* If this pkt is traced, snapshot the data */
1467       if (b0->flags & VLIB_BUFFER_IS_TRACED)
1468         {
1469           u64 len;
1470           t0 = vlib_add_trace (vm, rt, b0, sizeof (*t0));
1471           len = (b0->current_length < sizeof (t0->data)) ? b0->current_length
1472             : sizeof (t0->data);
1473           t0->len = len;
1474           clib_memcpy_fast (t0->data, vlib_buffer_get_current (b0), len);
1475         }
1476
1477       bfd_session_t *bs = NULL;
1478       bfd_lock (bm);
1479       if ((bs = bfd_consume_echo_pkt (vm, bfd_udp_main.bfd_main, b0)))
1480         {
1481           b0->error = rt->errors[BFD_UDP_ERROR_NONE];
1482           next0 = BFD_UDP_ECHO_INPUT_NEXT_NORMAL;
1483         }
1484       else
1485         {
1486           /* loop back the packet */
1487           b0->error = rt->errors[BFD_UDP_ERROR_NONE];
1488           if (is_ipv6)
1489             {
1490               vlib_node_increment_counter (vm, bfd_udp_echo6_input_node.index,
1491                                            b0->error, 1);
1492             }
1493           else
1494             {
1495               vlib_node_increment_counter (vm, bfd_udp_echo4_input_node.index,
1496                                            b0->error, 1);
1497             }
1498           next0 = BFD_UDP_ECHO_INPUT_NEXT_REPLY_REWRITE;
1499         }
1500
1501       bfd_unlock (bm);
1502
1503       if (bs)
1504         {
1505           vlib_increment_combined_counter (
1506             &bm->rx_echo_counter, vm->thread_index, bs->bs_idx, 1,
1507             vlib_buffer_length_in_chain (vm, b0));
1508         }
1509
1510       vlib_set_next_frame_buffer (vm, rt, next0, bi0);
1511
1512       from += 1;
1513       n_left_from -= 1;
1514     }
1515
1516   return f->n_vectors;
1517 }
1518
1519 static uword
1520 bfd_udp_echo4_input (vlib_main_t * vm, vlib_node_runtime_t * rt,
1521                      vlib_frame_t * f)
1522 {
1523   return bfd_udp_echo_input (vm, rt, f, 0);
1524 }
1525
1526 u8 *
1527 bfd_echo_input_format_trace (u8 * s, va_list * args)
1528 {
1529   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
1530   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
1531   const bfd_udp_echo_input_trace_t *t =
1532     va_arg (*args, bfd_udp_echo_input_trace_t *);
1533   if (t->len > STRUCT_SIZE_OF (bfd_pkt_t, head))
1534     {
1535       s = format (s, "BFD ECHO:\n");
1536       s = format (s, "    data: %U", format_hexdump, t->data, t->len);
1537     }
1538
1539   return s;
1540 }
1541
1542 /*
1543  * bfd input graph node declaration
1544  */
1545 /* *INDENT-OFF* */
1546 VLIB_REGISTER_NODE (bfd_udp_echo4_input_node, static) = {
1547   .function = bfd_udp_echo4_input,
1548   .name = "bfd-udp-echo4-input",
1549   .vector_size = sizeof (u32),
1550   .type = VLIB_NODE_TYPE_INTERNAL,
1551
1552   .n_errors = BFD_UDP_ECHO_N_ERROR,
1553   .error_strings = bfd_udp_error_strings,
1554
1555   .format_trace = bfd_echo_input_format_trace,
1556
1557   .n_next_nodes = BFD_UDP_ECHO_INPUT_N_NEXT,
1558   .next_nodes =
1559       {
1560               [BFD_UDP_ECHO_INPUT_NEXT_NORMAL] = "error-drop",
1561               [BFD_UDP_ECHO_INPUT_NEXT_REPLY_ARP] = "ip4-arp",
1562               [BFD_UDP_ECHO_INPUT_NEXT_REPLY_REWRITE] = "ip4-lookup",
1563       },
1564 };
1565 /* *INDENT-ON* */
1566
1567 static uword
1568 bfd_udp_echo6_input (vlib_main_t * vm, vlib_node_runtime_t * rt,
1569                      vlib_frame_t * f)
1570 {
1571   return bfd_udp_echo_input (vm, rt, f, 1);
1572 }
1573
1574 /* *INDENT-OFF* */
1575 VLIB_REGISTER_NODE (bfd_udp_echo6_input_node, static) = {
1576   .function = bfd_udp_echo6_input,
1577   .name = "bfd-udp-echo6-input",
1578   .vector_size = sizeof (u32),
1579   .type = VLIB_NODE_TYPE_INTERNAL,
1580
1581   .n_errors = BFD_UDP_ECHO_N_ERROR,
1582   .error_strings = bfd_udp_echo_error_strings,
1583
1584   .format_trace = bfd_echo_input_format_trace,
1585
1586   .n_next_nodes = BFD_UDP_ECHO_INPUT_N_NEXT,
1587   .next_nodes =
1588       {
1589               [BFD_UDP_ECHO_INPUT_NEXT_NORMAL] = "error-drop",
1590               [BFD_UDP_ECHO_INPUT_NEXT_REPLY_ARP] = "ip6-discover-neighbor",
1591               [BFD_UDP_ECHO_INPUT_NEXT_REPLY_REWRITE] = "ip6-lookup",
1592       },
1593 };
1594
1595 /* *INDENT-ON* */
1596
1597 static clib_error_t *
1598 bfd_udp_sw_if_add_del (CLIB_UNUSED (vnet_main_t *vnm), u32 sw_if_index,
1599                        u32 is_create)
1600 {
1601   u32 *to_be_freed = NULL;
1602   bfd_udp_main_t *bum = &bfd_udp_main;
1603   BFD_DBG ("sw_if_add_del called, sw_if_index=%u, is_create=%u", sw_if_index,
1604            is_create);
1605   if (!is_create)
1606     {
1607       bfd_session_t *bs;
1608       pool_foreach (bs, bum->bfd_main->sessions)
1609         {
1610           if (bs->transport != BFD_TRANSPORT_UDP4 &&
1611               bs->transport != BFD_TRANSPORT_UDP6)
1612             {
1613               continue;
1614             }
1615           if (bs->udp.key.sw_if_index != sw_if_index)
1616             {
1617               continue;
1618             }
1619           vec_add1 (to_be_freed, bs->bs_idx);
1620         }
1621     }
1622   u32 *bs_idx;
1623   vec_foreach (bs_idx, to_be_freed)
1624     {
1625       bfd_session_t *bs = pool_elt_at_index (bum->bfd_main->sessions, *bs_idx);
1626       vlib_log_notice (bum->log_class,
1627                        "removal of sw_if_index=%u forces removal of bfd "
1628                        "session with bs_idx=%u",
1629                        sw_if_index, bs->bs_idx);
1630       bfd_session_set_flags (vlib_get_main (), bs, 0);
1631       bfd_udp_del_session_internal (vlib_get_main (), bs);
1632     }
1633   return 0;
1634 }
1635
1636 VNET_SW_INTERFACE_ADD_DEL_FUNCTION (bfd_udp_sw_if_add_del);
1637
1638 clib_error_t *
1639 bfd_udp_stats_init (bfd_udp_main_t *bum)
1640 {
1641   const char *name4 = "/bfd/udp4/sessions";
1642   bum->udp4_sessions_count_stat_seg_entry = vlib_stats_add_gauge ("%s", name4);
1643
1644   vlib_stats_set_gauge (bum->udp4_sessions_count_stat_seg_entry, 0);
1645   if (~0 == bum->udp4_sessions_count_stat_seg_entry)
1646     {
1647       return clib_error_return (
1648         0, "Could not create stat segment entry for %s", name4);
1649     }
1650   const char *name6 = "/bfd/udp6/sessions";
1651   bum->udp6_sessions_count_stat_seg_entry = vlib_stats_add_gauge ("%s", name6);
1652
1653   vlib_stats_set_gauge (bum->udp6_sessions_count_stat_seg_entry, 0);
1654   if (~0 == bum->udp6_sessions_count_stat_seg_entry)
1655     {
1656       return clib_error_return (
1657         0, "Could not create stat segment entry for %s", name6);
1658     }
1659
1660   return 0;
1661 }
1662
1663 /*
1664  * setup function
1665  */
1666 static clib_error_t *
1667 bfd_udp_init (vlib_main_t * vm)
1668 {
1669   bfd_udp_main.udp4_sessions_count = 0;
1670   bfd_udp_main.udp6_sessions_count = 0;
1671   mhash_init (&bfd_udp_main.bfd_session_idx_by_bfd_key, sizeof (uword),
1672               sizeof (bfd_udp_key_t));
1673   bfd_udp_main.bfd_main = &bfd_main;
1674   bfd_udp_main.vnet_main = vnet_get_main ();
1675   vlib_node_t *node = vlib_get_node_by_name (vm, (u8 *) "ip4-arp");
1676   ASSERT (node);
1677   bfd_udp_main.ip4_arp_idx = node->index;
1678   node = vlib_get_node_by_name (vm, (u8 *) "ip6-discover-neighbor");
1679   ASSERT (node);
1680   bfd_udp_main.ip6_ndp_idx = node->index;
1681   node = vlib_get_node_by_name (vm, (u8 *) "ip4-rewrite");
1682   ASSERT (node);
1683   bfd_udp_main.ip4_rewrite_idx = node->index;
1684   node = vlib_get_node_by_name (vm, (u8 *) "ip6-rewrite");
1685   ASSERT (node);
1686   bfd_udp_main.ip6_rewrite_idx = node->index;
1687   node = vlib_get_node_by_name (vm, (u8 *) "ip4-midchain");
1688   ASSERT (node);
1689   bfd_udp_main.ip4_midchain_idx = node->index;
1690   node = vlib_get_node_by_name (vm, (u8 *) "ip6-midchain");
1691   ASSERT (node);
1692   bfd_udp_main.ip6_midchain_idx = node->index;
1693
1694   bfd_udp_stats_init (&bfd_udp_main);
1695
1696   bfd_udp_main.log_class = vlib_log_register_class ("bfd", "udp");
1697   vlib_log_debug (bfd_udp_main.log_class, "initialized");
1698   return 0;
1699 }
1700
1701 VLIB_INIT_FUNCTION (bfd_udp_init);
1702
1703 /*
1704  * fd.io coding-style-patch-verification: ON
1705  *
1706  * Local Variables:
1707  * eval: (c-set-style "gnu")
1708  * End:
1709  */