bfd: add per session counters
[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 <vpp/stats/stat_segment.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_stat_segment_lock ();
89   stat_segment_set_state_counter (entry, value);
90   vlib_stat_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   bfd_udp_key_t *key = &bus->key;
531   bfd_udp_key_init (key, sw_if_index, local_addr, peer_addr);
532   const bfd_session_t *tmp = bfd_lookup_session (bum, key);
533   if (tmp)
534     {
535       vlib_log_err (bum->log_class,
536                     "duplicate bfd-udp session, existing bs_idx=%d",
537                     tmp->bs_idx);
538       bfd_put_session (bum->bfd_main, bs);
539       return VNET_API_ERROR_BFD_EEXIST;
540     }
541   mhash_set (&bum->bfd_session_idx_by_bfd_key, key, bs->bs_idx, NULL);
542   BFD_DBG ("session created, bs_idx=%u, sw_if_index=%d, local=%U, peer=%U",
543            bs->bs_idx, key->sw_if_index, format_ip46_address,
544            &key->local_addr, IP46_TYPE_ANY, format_ip46_address,
545            &key->peer_addr, IP46_TYPE_ANY);
546   vlib_log_info (bum->log_class, "create BFD session: %U",
547                  format_bfd_session, bs);
548   if (BFD_TRANSPORT_UDP4 == t)
549     {
550       bus->adj_index = adj_nbr_add_or_lock (FIB_PROTOCOL_IP4, VNET_LINK_IP4,
551                                             &key->peer_addr,
552                                             key->sw_if_index);
553       BFD_DBG ("adj_nbr_add_or_lock(FIB_PROTOCOL_IP4, VNET_LINK_IP4, %U, %d) "
554                "returns %d", format_ip46_address, &key->peer_addr,
555                IP46_TYPE_ANY, key->sw_if_index, bus->adj_index);
556       ++bum->udp4_sessions_count;
557       bfd_udp_update_stat_segment_entry (
558         bum->udp4_sessions_count_stat_seg_entry, bum->udp4_sessions_count);
559       if (1 == bum->udp4_sessions_count)
560         {
561           udp_register_dst_port (vm, UDP_DST_PORT_bfd4,
562                                  bfd_udp4_input_node.index, 1);
563           udp_register_dst_port (vm, UDP_DST_PORT_bfd_echo4,
564                                  bfd_udp_echo4_input_node.index, 1);
565         }
566     }
567   else
568     {
569       bus->adj_index = adj_nbr_add_or_lock (FIB_PROTOCOL_IP6, VNET_LINK_IP6,
570                                             &key->peer_addr,
571                                             key->sw_if_index);
572       BFD_DBG ("adj_nbr_add_or_lock(FIB_PROTOCOL_IP6, VNET_LINK_IP6, %U, %d) "
573                "returns %d", format_ip46_address, &key->peer_addr,
574                IP46_TYPE_ANY, key->sw_if_index, bus->adj_index);
575       ++bum->udp6_sessions_count;
576       bfd_udp_update_stat_segment_entry (
577         bum->udp6_sessions_count_stat_seg_entry, bum->udp6_sessions_count);
578       if (1 == bum->udp6_sessions_count)
579         {
580           udp_register_dst_port (vm, UDP_DST_PORT_bfd6,
581                                  bfd_udp6_input_node.index, 0);
582           udp_register_dst_port (vm, UDP_DST_PORT_bfd_echo6,
583                                  bfd_udp_echo6_input_node.index, 0);
584         }
585     }
586   *bs_out = bs;
587   return bfd_session_set_params (bum->bfd_main, bs, desired_min_tx_usec,
588                                  required_min_rx_usec, detect_mult);
589 }
590
591 static vnet_api_error_t
592 bfd_udp_validate_api_input (u32 sw_if_index,
593                             const ip46_address_t * local_addr,
594                             const ip46_address_t * peer_addr)
595 {
596   bfd_udp_main_t *bum = &bfd_udp_main;
597   vnet_sw_interface_t *sw_if =
598     vnet_get_sw_interface_or_null (bfd_udp_main.vnet_main, sw_if_index);
599   u8 local_ip_valid = 0;
600   ip_interface_address_t *ia = NULL;
601   if (!sw_if)
602     {
603       vlib_log_err (bum->log_class,
604                     "got NULL sw_if when getting interface by index %u",
605                     sw_if_index);
606       return VNET_API_ERROR_INVALID_SW_IF_INDEX;
607     }
608   if (ip46_address_is_ip4 (local_addr))
609     {
610       if (!ip46_address_is_ip4 (peer_addr))
611         {
612           vlib_log_err (bum->log_class,
613                         "IP family mismatch (local is ipv4, peer is ipv6)");
614           return VNET_API_ERROR_INVALID_ARGUMENT;
615         }
616       ip4_main_t *im = &ip4_main;
617
618       /* *INDENT-OFF* */
619       foreach_ip_interface_address (
620           &im->lookup_main, ia, sw_if_index, 0 /* honor unnumbered */, ({
621             ip4_address_t *x =
622                 ip_interface_address_get_address (&im->lookup_main, ia);
623             if (x->as_u32 == local_addr->ip4.as_u32)
624               {
625                 /* valid address for this interface */
626                 local_ip_valid = 1;
627                 break;
628               }
629           }));
630       /* *INDENT-ON* */
631     }
632   else
633     {
634       if (ip46_address_is_ip4 (peer_addr))
635         {
636           vlib_log_err (bum->log_class,
637                         "IP family mismatch (local is ipv6, peer is ipv4)");
638           return VNET_API_ERROR_INVALID_ARGUMENT;
639         }
640
641       if (ip6_address_is_link_local_unicast (&local_addr->ip6))
642         {
643           const ip6_address_t *ll_addr;
644           ll_addr = ip6_get_link_local_address (sw_if_index);
645           if (ll_addr && ip6_address_is_equal (ll_addr, &local_addr->ip6))
646             {
647               /* valid address for this interface */
648               local_ip_valid = 1;
649             }
650         }
651       else
652         {
653           ip6_main_t *im = &ip6_main;
654           /* *INDENT-OFF* */
655           foreach_ip_interface_address (
656               &im->lookup_main, ia, sw_if_index, 0 /* honor unnumbered */, ({
657                 ip6_address_t *x =
658                     ip_interface_address_get_address (&im->lookup_main, ia);
659                 if (local_addr->ip6.as_u64[0] == x->as_u64[0] &&
660                     local_addr->ip6.as_u64[1] == x->as_u64[1])
661                   {
662                     /* valid address for this interface */
663                     local_ip_valid = 1;
664                     break;
665                   }
666               }));
667           /* *INDENT-ON* */
668         }
669     }
670
671   if (!local_ip_valid)
672     {
673       vlib_log_err (bum->log_class,
674                     "local address %U not found on interface with index %u",
675                     format_ip46_address, local_addr, IP46_TYPE_ANY,
676                     sw_if_index);
677       return VNET_API_ERROR_ADDRESS_NOT_FOUND_FOR_INTERFACE;
678     }
679
680   return 0;
681 }
682
683 static vnet_api_error_t
684 bfd_udp_find_session_by_api_input (u32 sw_if_index,
685                                    const ip46_address_t * local_addr,
686                                    const ip46_address_t * peer_addr,
687                                    bfd_session_t ** bs_out)
688 {
689   vnet_api_error_t rv =
690     bfd_udp_validate_api_input (sw_if_index, local_addr, peer_addr);
691   if (!rv)
692     {
693       bfd_udp_main_t *bum = &bfd_udp_main;
694       bfd_udp_key_t key;
695       bfd_udp_key_init (&key, sw_if_index, local_addr, peer_addr);
696       bfd_session_t *bs = bfd_lookup_session (bum, &key);
697       if (bs)
698         {
699           *bs_out = bs;
700         }
701       else
702         {
703           vlib_log_err (bum->log_class,
704                         "BFD session not found, sw_if_index=%u, local=%U, peer=%U",
705                         sw_if_index, format_ip46_address, local_addr,
706                         IP46_TYPE_ANY, format_ip46_address, peer_addr,
707                         IP46_TYPE_ANY);
708           return VNET_API_ERROR_BFD_ENOENT;
709         }
710     }
711   return rv;
712 }
713
714 static vnet_api_error_t
715 bfd_api_verify_common (u32 sw_if_index, u32 desired_min_tx_usec,
716                        u8 detect_mult, const ip46_address_t *local_addr,
717                        const ip46_address_t *peer_addr)
718 {
719   bfd_udp_main_t *bum = &bfd_udp_main;
720   vnet_api_error_t rv =
721     bfd_udp_validate_api_input (sw_if_index, local_addr, peer_addr);
722   if (rv)
723     {
724       return rv;
725     }
726   if (detect_mult < 1)
727     {
728       vlib_log_err (bum->log_class, "detect_mult < 1");
729       return VNET_API_ERROR_INVALID_ARGUMENT;
730     }
731   if (desired_min_tx_usec < 1)
732     {
733       vlib_log_err (bum->log_class, "desired_min_tx_usec < 1");
734       return VNET_API_ERROR_INVALID_ARGUMENT;
735     }
736   return 0;
737 }
738
739 static void
740 bfd_udp_del_session_internal (vlib_main_t * vm, bfd_session_t * bs)
741 {
742   bfd_udp_main_t *bum = &bfd_udp_main;
743   BFD_DBG ("free bfd-udp session, bs_idx=%d", bs->bs_idx);
744   mhash_unset (&bum->bfd_session_idx_by_bfd_key, &bs->udp.key, NULL);
745   adj_unlock (bs->udp.adj_index);
746   switch (bs->transport)
747     {
748     case BFD_TRANSPORT_UDP4:
749       --bum->udp4_sessions_count;
750       bfd_udp_update_stat_segment_entry (
751         bum->udp4_sessions_count_stat_seg_entry, bum->udp4_sessions_count);
752       if (!bum->udp4_sessions_count)
753         {
754           udp_unregister_dst_port (vm, UDP_DST_PORT_bfd4, 1);
755           udp_unregister_dst_port (vm, UDP_DST_PORT_bfd_echo4, 1);
756         }
757       break;
758     case BFD_TRANSPORT_UDP6:
759       --bum->udp6_sessions_count;
760       bfd_udp_update_stat_segment_entry (
761         bum->udp6_sessions_count_stat_seg_entry, bum->udp6_sessions_count);
762       if (!bum->udp6_sessions_count)
763         {
764           udp_unregister_dst_port (vm, UDP_DST_PORT_bfd6, 0);
765           udp_unregister_dst_port (vm, UDP_DST_PORT_bfd_echo6, 0);
766         }
767       break;
768     }
769   bfd_put_session (bum->bfd_main, bs);
770 }
771
772 static vnet_api_error_t
773 bfd_udp_add_and_start_session (u32 sw_if_index,
774                                const ip46_address_t *local_addr,
775                                const ip46_address_t *peer_addr,
776                                u32 desired_min_tx_usec,
777                                u32 required_min_rx_usec, u8 detect_mult,
778                                u8 is_authenticated, u32 conf_key_id,
779                                u8 bfd_key_id)
780 {
781   bfd_session_t *bs = NULL;
782   vnet_api_error_t rv;
783
784   rv = bfd_udp_add_session_internal (
785     vlib_get_main (), &bfd_udp_main, sw_if_index, desired_min_tx_usec,
786     required_min_rx_usec, detect_mult, local_addr, peer_addr, &bs);
787
788   if (!rv && is_authenticated)
789     {
790       rv = bfd_auth_activate (bs, conf_key_id, bfd_key_id,
791                               0 /* is not delayed */);
792       if (rv)
793         {
794           bfd_udp_del_session_internal (vlib_get_main (), bs);
795         }
796     }
797   if (!rv)
798     {
799       bfd_session_start (bfd_udp_main.bfd_main, bs);
800     }
801
802   return rv;
803 }
804
805 vnet_api_error_t
806 bfd_udp_add_session (u32 sw_if_index, const ip46_address_t * local_addr,
807                      const ip46_address_t * peer_addr,
808                      u32 desired_min_tx_usec, u32 required_min_rx_usec,
809                      u8 detect_mult, u8 is_authenticated, u32 conf_key_id,
810                      u8 bfd_key_id)
811 {
812   bfd_main_t *bm = &bfd_main;
813   bfd_lock (bm);
814
815   vnet_api_error_t rv = bfd_api_verify_common (
816     sw_if_index, desired_min_tx_usec, detect_mult, local_addr, peer_addr);
817
818   if (!rv)
819     rv = bfd_udp_add_and_start_session (
820       sw_if_index, local_addr, peer_addr, desired_min_tx_usec,
821       required_min_rx_usec, detect_mult, is_authenticated, conf_key_id,
822       bfd_key_id);
823
824   bfd_unlock (bm);
825   return rv;
826 }
827
828 vnet_api_error_t
829 bfd_udp_upd_session (u32 sw_if_index, const ip46_address_t *local_addr,
830                      const ip46_address_t *peer_addr, u32 desired_min_tx_usec,
831                      u32 required_min_rx_usec, u8 detect_mult,
832                      u8 is_authenticated, u32 conf_key_id, u8 bfd_key_id)
833 {
834   bfd_main_t *bm = &bfd_main;
835   bfd_lock (bm);
836
837   vnet_api_error_t rv = bfd_api_verify_common (
838     sw_if_index, desired_min_tx_usec, detect_mult, local_addr, peer_addr);
839   if (!rv)
840     {
841       bfd_session_t *bs = NULL;
842
843       rv = bfd_udp_find_session_by_api_input (sw_if_index, local_addr,
844                                               peer_addr, &bs);
845       if (VNET_API_ERROR_BFD_ENOENT == rv)
846         rv = bfd_udp_add_and_start_session (
847           sw_if_index, local_addr, peer_addr, desired_min_tx_usec,
848           required_min_rx_usec, detect_mult, is_authenticated, conf_key_id,
849           bfd_key_id);
850       else
851         rv = bfd_session_set_params (bfd_udp_main.bfd_main, bs,
852                                      desired_min_tx_usec, required_min_rx_usec,
853                                      detect_mult);
854     }
855
856   bfd_unlock (bm);
857   return rv;
858 }
859
860 vnet_api_error_t
861 bfd_udp_mod_session (u32 sw_if_index, const ip46_address_t *local_addr,
862                      const ip46_address_t *peer_addr, u32 desired_min_tx_usec,
863                      u32 required_min_rx_usec, u8 detect_mult)
864 {
865   bfd_session_t *bs = NULL;
866   bfd_main_t *bm = &bfd_main;
867   vnet_api_error_t error;
868   bfd_lock (bm);
869   vnet_api_error_t rv =
870     bfd_udp_find_session_by_api_input (sw_if_index, local_addr, peer_addr,
871                                        &bs);
872   if (rv)
873     {
874       bfd_unlock (bm);
875       return rv;
876     }
877
878   error = bfd_session_set_params (bfd_udp_main.bfd_main, bs,
879                                   desired_min_tx_usec, required_min_rx_usec,
880                                   detect_mult);
881   bfd_unlock (bm);
882   return error;
883 }
884
885 vnet_api_error_t
886 bfd_udp_del_session (u32 sw_if_index,
887                      const ip46_address_t * local_addr,
888                      const ip46_address_t * peer_addr)
889 {
890   bfd_session_t *bs = NULL;
891   bfd_main_t *bm = &bfd_main;
892   bfd_lock (bm);
893   vnet_api_error_t rv =
894     bfd_udp_find_session_by_api_input (sw_if_index, local_addr, peer_addr,
895                                        &bs);
896   if (rv)
897     {
898       bfd_unlock (bm);
899       return rv;
900     }
901   bfd_udp_del_session_internal (vlib_get_main (), bs);
902   bfd_unlock (bm);
903   return 0;
904 }
905
906 vnet_api_error_t
907 bfd_udp_session_set_flags (vlib_main_t * vm, u32 sw_if_index,
908                            const ip46_address_t * local_addr,
909                            const ip46_address_t * peer_addr, u8 admin_up_down)
910 {
911   bfd_session_t *bs = NULL;
912   bfd_main_t *bm = &bfd_main;
913   bfd_lock (bm);
914   vnet_api_error_t rv =
915     bfd_udp_find_session_by_api_input (sw_if_index, local_addr, peer_addr,
916                                        &bs);
917   if (rv)
918     {
919       bfd_unlock (bm);
920       return rv;
921     }
922   bfd_session_set_flags (vm, bs, admin_up_down);
923   bfd_unlock (bm);
924   return 0;
925 }
926
927 vnet_api_error_t
928 bfd_udp_auth_activate (u32 sw_if_index,
929                        const ip46_address_t * local_addr,
930                        const ip46_address_t * peer_addr,
931                        u32 conf_key_id, u8 key_id, u8 is_delayed)
932 {
933   bfd_main_t *bm = &bfd_main;
934   bfd_lock (bm);
935   vnet_api_error_t error;
936
937   bfd_session_t *bs = NULL;
938   vnet_api_error_t rv =
939     bfd_udp_find_session_by_api_input (sw_if_index, local_addr, peer_addr,
940                                        &bs);
941   if (rv)
942     {
943       bfd_unlock (bm);
944       return rv;
945     }
946   error = bfd_auth_activate (bs, conf_key_id, key_id, is_delayed);
947   bfd_unlock (bm);
948   return error;
949 }
950
951 vnet_api_error_t
952 bfd_udp_auth_deactivate (u32 sw_if_index,
953                          const ip46_address_t * local_addr,
954                          const ip46_address_t * peer_addr, u8 is_delayed)
955 {
956   bfd_main_t *bm = &bfd_main;
957   vnet_api_error_t error;
958   bfd_lock (bm);
959   bfd_session_t *bs = NULL;
960   vnet_api_error_t rv =
961     bfd_udp_find_session_by_api_input (sw_if_index, local_addr, peer_addr,
962                                        &bs);
963   if (rv)
964     {
965       bfd_unlock (bm);
966       return rv;
967     }
968   error = bfd_auth_deactivate (bs, is_delayed);
969   bfd_unlock (bm);
970   return error;
971 }
972
973 typedef enum
974 {
975   BFD_UDP_INPUT_NEXT_NORMAL,
976   BFD_UDP_INPUT_NEXT_REPLY_ARP,
977   BFD_UDP_INPUT_NEXT_REPLY_REWRITE,
978   BFD_UDP_INPUT_NEXT_REPLY_MIDCHAIN,
979   BFD_UDP_INPUT_N_NEXT,
980 } bfd_udp_input_next_t;
981
982 /* Packet counters - BFD control frames */
983 #define foreach_bfd_udp_error(F)           \
984   F (NONE, "good bfd packets (processed)") \
985   F (BAD, "invalid bfd packets")
986
987 #define F(sym, string) static char BFD_UDP_ERR_##sym##_STR[] = string;
988 foreach_bfd_udp_error (F);
989 #undef F
990
991 static char *bfd_udp_error_strings[] = {
992 #define F(sym, string) BFD_UDP_ERR_##sym##_STR,
993   foreach_bfd_udp_error (F)
994 #undef F
995 };
996
997 typedef enum
998 {
999 #define F(sym, str) BFD_UDP_ERROR_##sym,
1000   foreach_bfd_udp_error (F)
1001 #undef F
1002     BFD_UDP_N_ERROR,
1003 } bfd_udp_error_t;
1004
1005 typedef enum
1006 {
1007   BFD_UDP_ECHO_INPUT_NEXT_NORMAL,
1008   BFD_UDP_ECHO_INPUT_NEXT_REPLY_ARP,
1009   BFD_UDP_ECHO_INPUT_NEXT_REPLY_REWRITE,
1010   BFD_UDP_ECHO_INPUT_N_NEXT,
1011 } bfd_udp_echo_input_next_t;
1012
1013 /* Packet counters - BFD ECHO packets */
1014 #define foreach_bfd_udp_echo_error(F)           \
1015   F (NONE, "good bfd echo packets (processed)") \
1016   F (BAD, "invalid bfd echo packets")
1017
1018 #define F(sym, string) static char BFD_UDP_ECHO_ERR_##sym##_STR[] = string;
1019 foreach_bfd_udp_echo_error (F);
1020 #undef F
1021
1022 static char *bfd_udp_echo_error_strings[] = {
1023 #define F(sym, string) BFD_UDP_ECHO_ERR_##sym##_STR,
1024   foreach_bfd_udp_echo_error (F)
1025 #undef F
1026 };
1027
1028 typedef enum
1029 {
1030 #define F(sym, str) BFD_UDP_ECHO_ERROR_##sym,
1031   foreach_bfd_udp_echo_error (F)
1032 #undef F
1033     BFD_UDP_ECHO_N_ERROR,
1034 } bfd_udp_echo_error_t;
1035
1036 static void
1037 bfd_udp4_find_headers (vlib_buffer_t * b, ip4_header_t ** ip4,
1038                        udp_header_t ** udp)
1039 {
1040   /* sanity check first */
1041   const i32 start = vnet_buffer (b)->l3_hdr_offset;
1042   if (start < -(signed) sizeof (b->pre_data))
1043     {
1044       BFD_ERR ("Start of ip header is before pre_data, ignoring");
1045       *ip4 = NULL;
1046       *udp = NULL;
1047       return;
1048     }
1049   *ip4 = (ip4_header_t *) (b->data + start);
1050   if ((u8 *) * ip4 > (u8 *) vlib_buffer_get_current (b))
1051     {
1052       BFD_ERR ("Start of ip header is beyond current data, ignoring");
1053       *ip4 = NULL;
1054       *udp = NULL;
1055       return;
1056     }
1057   *udp = (udp_header_t *) ((*ip4) + 1);
1058 }
1059
1060 static bfd_udp_error_t
1061 bfd_udp4_verify_transport (const ip4_header_t * ip4,
1062                            const udp_header_t * udp, const bfd_session_t * bs)
1063 {
1064   const bfd_udp_session_t *bus = &bs->udp;
1065   const bfd_udp_key_t *key = &bus->key;
1066   if (ip4->src_address.as_u32 != key->peer_addr.ip4.as_u32)
1067     {
1068       BFD_ERR ("IPv4 src addr mismatch, got %U, expected %U",
1069                format_ip4_address, ip4->src_address.as_u8, format_ip4_address,
1070                key->peer_addr.ip4.as_u8);
1071       return BFD_UDP_ERROR_BAD;
1072     }
1073   if (ip4->dst_address.as_u32 != key->local_addr.ip4.as_u32)
1074     {
1075       BFD_ERR ("IPv4 dst addr mismatch, got %U, expected %U",
1076                format_ip4_address, ip4->dst_address.as_u8, format_ip4_address,
1077                key->local_addr.ip4.as_u8);
1078       return BFD_UDP_ERROR_BAD;
1079     }
1080   const u8 expected_ttl = 255;
1081   if (ip4->ttl != expected_ttl)
1082     {
1083       BFD_ERR ("IPv4 unexpected TTL value %u, expected %u", ip4->ttl,
1084                expected_ttl);
1085       return BFD_UDP_ERROR_BAD;
1086     }
1087   if (clib_net_to_host_u16 (udp->src_port) < 49152)
1088     {
1089       BFD_ERR ("Invalid UDP src port %u, out of range <49152,65535>",
1090                udp->src_port);
1091     }
1092   return BFD_UDP_ERROR_NONE;
1093 }
1094
1095 typedef struct
1096 {
1097   u32 bs_idx;
1098   bfd_pkt_t pkt;
1099 } bfd_rpc_update_t;
1100
1101 static void
1102 bfd_rpc_update_session (vlib_main_t * vm, u32 bs_idx, const bfd_pkt_t * pkt)
1103 {
1104   bfd_main_t *bm = &bfd_main;
1105   bfd_lock (bm);
1106   bfd_consume_pkt (vm, bm, pkt, bs_idx);
1107   bfd_unlock (bm);
1108 }
1109
1110 static bfd_udp_error_t
1111 bfd_udp4_scan (vlib_main_t *vm, vlib_buffer_t *b, bfd_session_t **bs_out)
1112 {
1113   const bfd_pkt_t *pkt = vlib_buffer_get_current (b);
1114   if (sizeof (*pkt) > b->current_length)
1115     {
1116       BFD_ERR
1117         ("Payload size %d too small to hold bfd packet of minimum size %d",
1118          b->current_length, sizeof (*pkt));
1119       return BFD_UDP_ERROR_BAD;
1120     }
1121   ip4_header_t *ip4;
1122   udp_header_t *udp;
1123   bfd_udp4_find_headers (b, &ip4, &udp);
1124   if (!ip4 || !udp)
1125     {
1126       BFD_ERR ("Couldn't find ip4 or udp header");
1127       return BFD_UDP_ERROR_BAD;
1128     }
1129   const u32 udp_payload_length = udp->length - sizeof (*udp);
1130   if (pkt->head.length > udp_payload_length)
1131     {
1132       BFD_ERR
1133         ("BFD packet length is larger than udp payload length (%u > %u)",
1134          pkt->head.length, udp_payload_length);
1135       return BFD_UDP_ERROR_BAD;
1136     }
1137   if (!bfd_verify_pkt_common (pkt))
1138     {
1139       return BFD_UDP_ERROR_BAD;
1140     }
1141   bfd_session_t *bs = NULL;
1142   if (pkt->your_disc)
1143     {
1144       BFD_DBG ("Looking up BFD session using discriminator %u",
1145                pkt->your_disc);
1146       bs = bfd_find_session_by_disc (bfd_udp_main.bfd_main, pkt->your_disc);
1147     }
1148   else
1149     {
1150       bfd_udp_key_t key;
1151       clib_memset (&key, 0, sizeof (key));
1152       key.sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
1153       key.local_addr.ip4.as_u32 = ip4->dst_address.as_u32;
1154       key.peer_addr.ip4.as_u32 = ip4->src_address.as_u32;
1155       BFD_DBG ("Looking up BFD session using key (sw_if_index=%u, local=%U, "
1156                "peer=%U)",
1157                key.sw_if_index, format_ip4_address, key.local_addr.ip4.as_u8,
1158                format_ip4_address, key.peer_addr.ip4.as_u8);
1159       bs = bfd_lookup_session (&bfd_udp_main, &key);
1160     }
1161   if (!bs)
1162     {
1163       BFD_ERR ("BFD session lookup failed - no session matches BFD pkt");
1164       return BFD_UDP_ERROR_BAD;
1165     }
1166   BFD_DBG ("BFD session found, bs_idx=%u", bs->bs_idx);
1167   if (!bfd_verify_pkt_auth (vm, pkt, b->current_length, bs))
1168     {
1169       BFD_ERR ("Packet verification failed, dropping packet");
1170       return BFD_UDP_ERROR_BAD;
1171     }
1172   bfd_udp_error_t err;
1173   if (BFD_UDP_ERROR_NONE != (err = bfd_udp4_verify_transport (ip4, udp, bs)))
1174     {
1175       return err;
1176     }
1177   bfd_rpc_update_session (vm, bs->bs_idx, pkt);
1178   *bs_out = bs;
1179   return BFD_UDP_ERROR_NONE;
1180 }
1181
1182 static void
1183 bfd_udp6_find_headers (vlib_buffer_t * b, ip6_header_t ** ip6,
1184                        udp_header_t ** udp)
1185 {
1186   /* sanity check first */
1187   const i32 start = vnet_buffer (b)->l3_hdr_offset;
1188   if (start < -(signed) sizeof (b->pre_data))
1189     {
1190       BFD_ERR ("Start of ip header is before pre_data, ignoring");
1191       *ip6 = NULL;
1192       *udp = NULL;
1193       return;
1194     }
1195   *ip6 = (ip6_header_t *) (b->data + start);
1196   if ((u8 *) * ip6 > (u8 *) vlib_buffer_get_current (b))
1197     {
1198       BFD_ERR ("Start of ip header is beyond current data, ignoring");
1199       *ip6 = NULL;
1200       *udp = NULL;
1201       return;
1202     }
1203   if ((*ip6)->protocol != IP_PROTOCOL_UDP)
1204     {
1205       BFD_ERR ("Unexpected protocol in IPv6 header '%u', expected '%u' (== "
1206                "IP_PROTOCOL_UDP)", (*ip6)->protocol, IP_PROTOCOL_UDP);
1207       *ip6 = NULL;
1208       *udp = NULL;
1209       return;
1210     }
1211   *udp = (udp_header_t *) ((*ip6) + 1);
1212 }
1213
1214 static bfd_udp_error_t
1215 bfd_udp6_verify_transport (const ip6_header_t * ip6,
1216                            const udp_header_t * udp, const bfd_session_t * bs)
1217 {
1218   const bfd_udp_session_t *bus = &bs->udp;
1219   const bfd_udp_key_t *key = &bus->key;
1220   if (ip6->src_address.as_u64[0] != key->peer_addr.ip6.as_u64[0] &&
1221       ip6->src_address.as_u64[1] != key->peer_addr.ip6.as_u64[1])
1222     {
1223       BFD_ERR ("IP src addr mismatch, got %U, expected %U",
1224                format_ip6_address, ip6, format_ip6_address,
1225                &key->peer_addr.ip6);
1226       return BFD_UDP_ERROR_BAD;
1227     }
1228   if (ip6->dst_address.as_u64[0] != key->local_addr.ip6.as_u64[0] &&
1229       ip6->dst_address.as_u64[1] != key->local_addr.ip6.as_u64[1])
1230     {
1231       BFD_ERR ("IP dst addr mismatch, got %U, expected %U",
1232                format_ip6_address, ip6, format_ip6_address,
1233                &key->local_addr.ip6);
1234       return BFD_UDP_ERROR_BAD;
1235     }
1236   const u8 expected_hop_limit = 255;
1237   if (ip6->hop_limit != expected_hop_limit)
1238     {
1239       BFD_ERR ("IPv6 unexpected hop-limit value %u, expected %u",
1240                ip6->hop_limit, expected_hop_limit);
1241       return BFD_UDP_ERROR_BAD;
1242     }
1243   if (clib_net_to_host_u16 (udp->src_port) < 49152)
1244     {
1245       BFD_ERR ("Invalid UDP src port %u, out of range <49152,65535>",
1246                udp->src_port);
1247     }
1248   return BFD_UDP_ERROR_NONE;
1249 }
1250
1251 static bfd_udp_error_t
1252 bfd_udp6_scan (vlib_main_t *vm, vlib_buffer_t *b, bfd_session_t **bs_out)
1253 {
1254   const bfd_pkt_t *pkt = vlib_buffer_get_current (b);
1255   if (sizeof (*pkt) > b->current_length)
1256     {
1257       BFD_ERR
1258         ("Payload size %d too small to hold bfd packet of minimum size %d",
1259          b->current_length, sizeof (*pkt));
1260       return BFD_UDP_ERROR_BAD;
1261     }
1262   ip6_header_t *ip6;
1263   udp_header_t *udp;
1264   bfd_udp6_find_headers (b, &ip6, &udp);
1265   if (!ip6 || !udp)
1266     {
1267       BFD_ERR ("Couldn't find ip6 or udp header");
1268       return BFD_UDP_ERROR_BAD;
1269     }
1270   const u32 udp_payload_length = udp->length - sizeof (*udp);
1271   if (pkt->head.length > udp_payload_length)
1272     {
1273       BFD_ERR
1274         ("BFD packet length is larger than udp payload length (%u > %u)",
1275          pkt->head.length, udp_payload_length);
1276       return BFD_UDP_ERROR_BAD;
1277     }
1278   if (!bfd_verify_pkt_common (pkt))
1279     {
1280       return BFD_UDP_ERROR_BAD;
1281     }
1282   bfd_session_t *bs = NULL;
1283   if (pkt->your_disc)
1284     {
1285       BFD_DBG ("Looking up BFD session using discriminator %u",
1286                pkt->your_disc);
1287       bs = bfd_find_session_by_disc (bfd_udp_main.bfd_main, pkt->your_disc);
1288     }
1289   else
1290     {
1291       bfd_udp_key_t key;
1292       clib_memset (&key, 0, sizeof (key));
1293       key.sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
1294       key.local_addr.ip6.as_u64[0] = ip6->dst_address.as_u64[0];
1295       key.local_addr.ip6.as_u64[1] = ip6->dst_address.as_u64[1];
1296       key.peer_addr.ip6.as_u64[0] = ip6->src_address.as_u64[0];
1297       key.peer_addr.ip6.as_u64[1] = ip6->src_address.as_u64[1];
1298       BFD_DBG ("Looking up BFD session using key (sw_if_index=%u, local=%U, "
1299                "peer=%U)",
1300                key.sw_if_index, format_ip6_address, &key.local_addr,
1301                format_ip6_address, &key.peer_addr);
1302       bs = bfd_lookup_session (&bfd_udp_main, &key);
1303     }
1304   if (!bs)
1305     {
1306       BFD_ERR ("BFD session lookup failed - no session matches BFD pkt");
1307       return BFD_UDP_ERROR_BAD;
1308     }
1309   BFD_DBG ("BFD session found, bs_idx=%u", bs->bs_idx);
1310   if (!bfd_verify_pkt_auth (vm, pkt, b->current_length, bs))
1311     {
1312       BFD_ERR ("Packet verification failed, dropping packet");
1313       return BFD_UDP_ERROR_BAD;
1314     }
1315   bfd_udp_error_t err;
1316   if (BFD_UDP_ERROR_NONE != (err = bfd_udp6_verify_transport (ip6, udp, bs)))
1317     {
1318       return err;
1319     }
1320   bfd_rpc_update_session (vm, bs->bs_idx, pkt);
1321   *bs_out = bs;
1322   return BFD_UDP_ERROR_NONE;
1323 }
1324
1325 /*
1326  * Process a frame of bfd packets
1327  * Expect 1 packet / frame
1328  */
1329 static uword
1330 bfd_udp_input (vlib_main_t * vm, vlib_node_runtime_t * rt,
1331                vlib_frame_t * f, int is_ipv6)
1332 {
1333   u32 n_left_from, *from;
1334   bfd_input_trace_t *t0;
1335   bfd_main_t *bm = &bfd_main;
1336
1337   from = vlib_frame_vector_args (f);    /* array of buffer indices */
1338   n_left_from = f->n_vectors;   /* number of buffer indices */
1339
1340   while (n_left_from > 0)
1341     {
1342       u32 bi0;
1343       vlib_buffer_t *b0;
1344       u32 next0, error0;
1345
1346       bi0 = from[0];
1347       b0 = vlib_get_buffer (vm, bi0);
1348
1349       bfd_session_t *bs = NULL;
1350
1351       /* If this pkt is traced, snapshot the data */
1352       if (b0->flags & VLIB_BUFFER_IS_TRACED)
1353         {
1354           u64 len;
1355           t0 = vlib_add_trace (vm, rt, b0, sizeof (*t0));
1356           len = (b0->current_length < sizeof (t0->data)) ? b0->current_length
1357             : sizeof (t0->data);
1358           t0->len = len;
1359           clib_memcpy_fast (t0->data, vlib_buffer_get_current (b0), len);
1360         }
1361
1362       /* scan this bfd pkt. error0 is the counter index to bmp */
1363       bfd_lock (bm);
1364       if (is_ipv6)
1365         {
1366           error0 = bfd_udp6_scan (vm, b0, &bs);
1367         }
1368       else
1369         {
1370           error0 = bfd_udp4_scan (vm, b0, &bs);
1371         }
1372       b0->error = rt->errors[error0];
1373
1374       next0 = BFD_UDP_INPUT_NEXT_NORMAL;
1375       if (BFD_UDP_ERROR_NONE == error0)
1376         {
1377           vlib_increment_combined_counter (
1378             &bm->rx_counter, vm->thread_index, bs->bs_idx, 1,
1379             vlib_buffer_length_in_chain (vm, b0));
1380           /*
1381            *  if everything went fine, check for poll bit, if present, re-use
1382            *  the buffer and based on (now updated) session parameters, send
1383            *  the final packet back
1384            */
1385           const bfd_pkt_t *pkt = vlib_buffer_get_current (b0);
1386           if (bfd_pkt_get_poll (pkt))
1387             {
1388               b0->current_data = 0;
1389               b0->current_length = 0;
1390               bfd_init_final_control_frame (vm, b0, bs);
1391               if (is_ipv6)
1392                 {
1393                   vlib_node_increment_counter (vm, bfd_udp6_input_node.index,
1394                                                b0->error, 1);
1395                 }
1396               else
1397                 {
1398                   vlib_node_increment_counter (vm, bfd_udp4_input_node.index,
1399                                                b0->error, 1);
1400                 }
1401               const bfd_udp_session_t *bus = &bs->udp;
1402               ip_adjacency_t *adj = adj_get (bus->adj_index);
1403               switch (adj->lookup_next_index)
1404                 {
1405                 case IP_LOOKUP_NEXT_ARP:
1406                   next0 = BFD_UDP_INPUT_NEXT_REPLY_ARP;
1407                   break;
1408                 case IP_LOOKUP_NEXT_REWRITE:
1409                   next0 = BFD_UDP_INPUT_NEXT_REPLY_REWRITE;
1410                   break;
1411                 case IP_LOOKUP_NEXT_MIDCHAIN:
1412                   next0 = BFD_UDP_INPUT_NEXT_REPLY_MIDCHAIN;
1413                   break;
1414                 default:
1415                   /* drop */
1416                   break;
1417                 }
1418             }
1419         }
1420       bfd_unlock (bm);
1421       vlib_set_next_frame_buffer (vm, rt, next0, bi0);
1422
1423       from += 1;
1424       n_left_from -= 1;
1425     }
1426
1427   return f->n_vectors;
1428 }
1429
1430 static uword
1431 bfd_udp4_input (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f)
1432 {
1433   return bfd_udp_input (vm, rt, f, 0);
1434 }
1435
1436 /*
1437  * bfd input graph node declaration
1438  */
1439 /* *INDENT-OFF* */
1440 VLIB_REGISTER_NODE (bfd_udp4_input_node, static) = {
1441   .function = bfd_udp4_input,
1442   .name = "bfd-udp4-input",
1443   .vector_size = sizeof (u32),
1444   .type = VLIB_NODE_TYPE_INTERNAL,
1445
1446   .n_errors = BFD_UDP_N_ERROR,
1447   .error_strings = bfd_udp_error_strings,
1448
1449   .format_trace = bfd_input_format_trace,
1450
1451   .n_next_nodes = BFD_UDP_INPUT_N_NEXT,
1452   .next_nodes =
1453       {
1454               [BFD_UDP_INPUT_NEXT_NORMAL] = "error-drop",
1455               [BFD_UDP_INPUT_NEXT_REPLY_ARP] = "ip4-arp",
1456               [BFD_UDP_INPUT_NEXT_REPLY_REWRITE] = "ip4-lookup",
1457               [BFD_UDP_INPUT_NEXT_REPLY_MIDCHAIN] = "ip4-midchain",
1458       },
1459 };
1460 /* *INDENT-ON* */
1461
1462 static uword
1463 bfd_udp6_input (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f)
1464 {
1465   return bfd_udp_input (vm, rt, f, 1);
1466 }
1467
1468 /* *INDENT-OFF* */
1469 VLIB_REGISTER_NODE (bfd_udp6_input_node, static) = {
1470   .function = bfd_udp6_input,
1471   .name = "bfd-udp6-input",
1472   .vector_size = sizeof (u32),
1473   .type = VLIB_NODE_TYPE_INTERNAL,
1474
1475   .n_errors = BFD_UDP_N_ERROR,
1476   .error_strings = bfd_udp_error_strings,
1477
1478   .format_trace = bfd_input_format_trace,
1479
1480   .n_next_nodes = BFD_UDP_INPUT_N_NEXT,
1481   .next_nodes =
1482       {
1483               [BFD_UDP_INPUT_NEXT_NORMAL] = "error-drop",
1484               [BFD_UDP_INPUT_NEXT_REPLY_ARP] = "ip6-discover-neighbor",
1485               [BFD_UDP_INPUT_NEXT_REPLY_REWRITE] = "ip6-lookup",
1486               [BFD_UDP_INPUT_NEXT_REPLY_MIDCHAIN] = "ip6-midchain",
1487       },
1488 };
1489 /* *INDENT-ON* */
1490
1491 /*
1492  * Process a frame of bfd echo packets
1493  * Expect 1 packet / frame
1494  */
1495 static uword
1496 bfd_udp_echo_input (vlib_main_t * vm, vlib_node_runtime_t * rt,
1497                     vlib_frame_t * f, int is_ipv6)
1498 {
1499   u32 n_left_from, *from;
1500   bfd_input_trace_t *t0;
1501   bfd_main_t *bm = &bfd_main;
1502
1503   from = vlib_frame_vector_args (f);    /* array of buffer indices */
1504   n_left_from = f->n_vectors;   /* number of buffer indices */
1505
1506   while (n_left_from > 0)
1507     {
1508       u32 bi0;
1509       vlib_buffer_t *b0;
1510       u32 next0;
1511
1512       bi0 = from[0];
1513       b0 = vlib_get_buffer (vm, bi0);
1514
1515       /* If this pkt is traced, snapshot the data */
1516       if (b0->flags & VLIB_BUFFER_IS_TRACED)
1517         {
1518           u64 len;
1519           t0 = vlib_add_trace (vm, rt, b0, sizeof (*t0));
1520           len = (b0->current_length < sizeof (t0->data)) ? b0->current_length
1521             : sizeof (t0->data);
1522           t0->len = len;
1523           clib_memcpy_fast (t0->data, vlib_buffer_get_current (b0), len);
1524         }
1525
1526       bfd_session_t *bs = NULL;
1527       bfd_lock (bm);
1528       if ((bs = bfd_consume_echo_pkt (vm, bfd_udp_main.bfd_main, b0)))
1529         {
1530           b0->error = rt->errors[BFD_UDP_ERROR_NONE];
1531           next0 = BFD_UDP_ECHO_INPUT_NEXT_NORMAL;
1532         }
1533       else
1534         {
1535           /* loop back the packet */
1536           b0->error = rt->errors[BFD_UDP_ERROR_NONE];
1537           if (is_ipv6)
1538             {
1539               vlib_node_increment_counter (vm, bfd_udp_echo6_input_node.index,
1540                                            b0->error, 1);
1541             }
1542           else
1543             {
1544               vlib_node_increment_counter (vm, bfd_udp_echo4_input_node.index,
1545                                            b0->error, 1);
1546             }
1547           next0 = BFD_UDP_ECHO_INPUT_NEXT_REPLY_REWRITE;
1548         }
1549
1550       bfd_unlock (bm);
1551
1552       if (bs)
1553         {
1554           vlib_increment_combined_counter (
1555             &bm->rx_echo_counter, vm->thread_index, bs->bs_idx, 1,
1556             vlib_buffer_length_in_chain (vm, b0));
1557         }
1558
1559       vlib_set_next_frame_buffer (vm, rt, next0, bi0);
1560
1561       from += 1;
1562       n_left_from -= 1;
1563     }
1564
1565   return f->n_vectors;
1566 }
1567
1568 static uword
1569 bfd_udp_echo4_input (vlib_main_t * vm, vlib_node_runtime_t * rt,
1570                      vlib_frame_t * f)
1571 {
1572   return bfd_udp_echo_input (vm, rt, f, 0);
1573 }
1574
1575 u8 *
1576 bfd_echo_input_format_trace (u8 * s, va_list * args)
1577 {
1578   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
1579   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
1580   const bfd_udp_echo_input_trace_t *t =
1581     va_arg (*args, bfd_udp_echo_input_trace_t *);
1582   if (t->len > STRUCT_SIZE_OF (bfd_pkt_t, head))
1583     {
1584       s = format (s, "BFD ECHO:\n");
1585       s = format (s, "    data: %U", format_hexdump, t->data, t->len);
1586     }
1587
1588   return s;
1589 }
1590
1591 /*
1592  * bfd input graph node declaration
1593  */
1594 /* *INDENT-OFF* */
1595 VLIB_REGISTER_NODE (bfd_udp_echo4_input_node, static) = {
1596   .function = bfd_udp_echo4_input,
1597   .name = "bfd-udp-echo4-input",
1598   .vector_size = sizeof (u32),
1599   .type = VLIB_NODE_TYPE_INTERNAL,
1600
1601   .n_errors = BFD_UDP_ECHO_N_ERROR,
1602   .error_strings = bfd_udp_error_strings,
1603
1604   .format_trace = bfd_echo_input_format_trace,
1605
1606   .n_next_nodes = BFD_UDP_ECHO_INPUT_N_NEXT,
1607   .next_nodes =
1608       {
1609               [BFD_UDP_ECHO_INPUT_NEXT_NORMAL] = "error-drop",
1610               [BFD_UDP_ECHO_INPUT_NEXT_REPLY_ARP] = "ip4-arp",
1611               [BFD_UDP_ECHO_INPUT_NEXT_REPLY_REWRITE] = "ip4-lookup",
1612       },
1613 };
1614 /* *INDENT-ON* */
1615
1616 static uword
1617 bfd_udp_echo6_input (vlib_main_t * vm, vlib_node_runtime_t * rt,
1618                      vlib_frame_t * f)
1619 {
1620   return bfd_udp_echo_input (vm, rt, f, 1);
1621 }
1622
1623 /* *INDENT-OFF* */
1624 VLIB_REGISTER_NODE (bfd_udp_echo6_input_node, static) = {
1625   .function = bfd_udp_echo6_input,
1626   .name = "bfd-udp-echo6-input",
1627   .vector_size = sizeof (u32),
1628   .type = VLIB_NODE_TYPE_INTERNAL,
1629
1630   .n_errors = BFD_UDP_ECHO_N_ERROR,
1631   .error_strings = bfd_udp_echo_error_strings,
1632
1633   .format_trace = bfd_echo_input_format_trace,
1634
1635   .n_next_nodes = BFD_UDP_ECHO_INPUT_N_NEXT,
1636   .next_nodes =
1637       {
1638               [BFD_UDP_ECHO_INPUT_NEXT_NORMAL] = "error-drop",
1639               [BFD_UDP_ECHO_INPUT_NEXT_REPLY_ARP] = "ip6-discover-neighbor",
1640               [BFD_UDP_ECHO_INPUT_NEXT_REPLY_REWRITE] = "ip6-lookup",
1641       },
1642 };
1643
1644 /* *INDENT-ON* */
1645
1646 static clib_error_t *
1647 bfd_udp_sw_if_add_del (CLIB_UNUSED (vnet_main_t *vnm), u32 sw_if_index,
1648                        u32 is_create)
1649 {
1650   u32 *to_be_freed = NULL;
1651   bfd_udp_main_t *bum = &bfd_udp_main;
1652   BFD_DBG ("sw_if_add_del called, sw_if_index=%u, is_create=%u", sw_if_index,
1653            is_create);
1654   if (!is_create)
1655     {
1656       bfd_session_t *bs;
1657       pool_foreach (bs, bum->bfd_main->sessions)
1658         {
1659           if (bs->transport != BFD_TRANSPORT_UDP4 &&
1660               bs->transport != BFD_TRANSPORT_UDP6)
1661             {
1662               continue;
1663             }
1664           if (bs->udp.key.sw_if_index != sw_if_index)
1665             {
1666               continue;
1667             }
1668           vec_add1 (to_be_freed, bs->bs_idx);
1669         }
1670     }
1671   u32 *bs_idx;
1672   vec_foreach (bs_idx, to_be_freed)
1673     {
1674       bfd_session_t *bs = pool_elt_at_index (bum->bfd_main->sessions, *bs_idx);
1675       vlib_log_notice (bum->log_class,
1676                        "removal of sw_if_index=%u forces removal of bfd "
1677                        "session with bs_idx=%u",
1678                        sw_if_index, bs->bs_idx);
1679       bfd_session_set_flags (vlib_get_main (), bs, 0);
1680       bfd_udp_del_session_internal (vlib_get_main (), bs);
1681     }
1682   return 0;
1683 }
1684
1685 VNET_SW_INTERFACE_ADD_DEL_FUNCTION (bfd_udp_sw_if_add_del);
1686
1687 clib_error_t *
1688 bfd_udp_stats_init (bfd_udp_main_t *bum)
1689 {
1690   const char *name4 = "/bfd/udp4/sessions";
1691   bum->udp4_sessions_count_stat_seg_entry =
1692     stat_segment_new_entry ((u8 *) name4, STAT_DIR_TYPE_SCALAR_INDEX);
1693
1694   stat_segment_set_state_counter (bum->udp4_sessions_count_stat_seg_entry, 0);
1695   if (~0 == bum->udp4_sessions_count_stat_seg_entry)
1696     {
1697       return clib_error_return (
1698         0, "Could not create stat segment entry for %s", name4);
1699     }
1700   const char *name6 = "/bfd/udp6/sessions";
1701   bum->udp6_sessions_count_stat_seg_entry =
1702     stat_segment_new_entry ((u8 *) name6, STAT_DIR_TYPE_SCALAR_INDEX);
1703
1704   if (~0 == bum->udp6_sessions_count_stat_seg_entry)
1705     {
1706       return clib_error_return (
1707         0, "Could not create stat segment entry for %s", name6);
1708     }
1709
1710   return 0;
1711 }
1712
1713 /*
1714  * setup function
1715  */
1716 static clib_error_t *
1717 bfd_udp_init (vlib_main_t * vm)
1718 {
1719   bfd_udp_main.udp4_sessions_count = 0;
1720   bfd_udp_main.udp6_sessions_count = 0;
1721   mhash_init (&bfd_udp_main.bfd_session_idx_by_bfd_key, sizeof (uword),
1722               sizeof (bfd_udp_key_t));
1723   bfd_udp_main.bfd_main = &bfd_main;
1724   bfd_udp_main.vnet_main = vnet_get_main ();
1725   vlib_node_t *node = vlib_get_node_by_name (vm, (u8 *) "ip4-arp");
1726   ASSERT (node);
1727   bfd_udp_main.ip4_arp_idx = node->index;
1728   node = vlib_get_node_by_name (vm, (u8 *) "ip6-discover-neighbor");
1729   ASSERT (node);
1730   bfd_udp_main.ip6_ndp_idx = node->index;
1731   node = vlib_get_node_by_name (vm, (u8 *) "ip4-rewrite");
1732   ASSERT (node);
1733   bfd_udp_main.ip4_rewrite_idx = node->index;
1734   node = vlib_get_node_by_name (vm, (u8 *) "ip6-rewrite");
1735   ASSERT (node);
1736   bfd_udp_main.ip6_rewrite_idx = node->index;
1737   node = vlib_get_node_by_name (vm, (u8 *) "ip4-midchain");
1738   ASSERT (node);
1739   bfd_udp_main.ip4_midchain_idx = node->index;
1740   node = vlib_get_node_by_name (vm, (u8 *) "ip6-midchain");
1741   ASSERT (node);
1742   bfd_udp_main.ip6_midchain_idx = node->index;
1743
1744   bfd_udp_stats_init (&bfd_udp_main);
1745
1746   bfd_udp_main.log_class = vlib_log_register_class ("bfd", "udp");
1747   vlib_log_debug (bfd_udp_main.log_class, "initialized");
1748   return 0;
1749 }
1750
1751 VLIB_INIT_FUNCTION (bfd_udp_init);
1752
1753 /*
1754  * fd.io coding-style-patch-verification: ON
1755  *
1756  * Local Variables:
1757  * eval: (c-set-style "gnu")
1758  * End:
1759  */