]> git.proxmox.com Git - mirror_frr.git/blob - bfdd/bfd_packet.c
bfdd: remove global variables and improve logs
[mirror_frr.git] / bfdd / bfd_packet.c
1 /*********************************************************************
2 * Copyright 2017 Cumulus Networks, Inc. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the Free
6 * Software Foundation; either version 2 of the License, or (at your option)
7 * any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; see the file COPYING; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 *
18 * bfd_packet.c: implements the BFD protocol packet handling.
19 *
20 * Authors
21 * -------
22 * Shrijeet Mukherjee [shm@cumulusnetworks.com]
23 * Kanna Rajagopal [kanna@cumulusnetworks.com]
24 * Radhika Mahankali [Radhika@cumulusnetworks.com]
25 */
26
27 #include <zebra.h>
28
29 #ifdef BFD_LINUX
30 #include <linux/if_packet.h>
31 #endif /* BFD_LINUX */
32
33 #include <netinet/if_ether.h>
34 #include <netinet/udp.h>
35
36 #include "lib/sockopt.h"
37
38 #include "bfd.h"
39
40 /*
41 * Definitions
42 */
43
44 /* iov for BFD control frames */
45 #define CMSG_HDR_LEN sizeof(struct cmsghdr)
46 #define CMSG_TTL_LEN (CMSG_HDR_LEN + sizeof(uint32_t))
47 #define CMSG_IN_PKT_INFO_LEN (CMSG_HDR_LEN + sizeof(struct in_pktinfo) + 4)
48 #define CMSG_IN6_PKT_INFO_LEN \
49 (CMSG_HDR_LEN + sizeof(struct in6_addr) + sizeof(int) + 4)
50
51 struct bfd_raw_echo_pkt {
52 #ifdef BFD_LINUX
53 struct iphdr ip;
54 #endif /* BFD_LINUX */
55 #ifdef BFD_BSD
56 struct ip ip;
57 #endif /* BFD_BSD */
58 struct udphdr udp;
59 struct bfd_echo_pkt data;
60 };
61
62 #if 0 /* TODO: VxLAN support. */
63 struct bfd_raw_ctrl_pkt {
64 struct iphdr ip;
65 struct udphdr udp;
66 struct bfd_pkt data;
67 };
68 #endif
69
70 struct vxlan_hdr {
71 uint32_t flags;
72 uint32_t vnid;
73 };
74
75 #define IP_ECHO_PKT_LEN (IP_HDR_LEN + UDP_HDR_LEN + BFD_ECHO_PKT_LEN)
76 #define UDP_ECHO_PKT_LEN (UDP_HDR_LEN + BFD_ECHO_PKT_LEN)
77 #define IP_CTRL_PKT_LEN (IP_HDR_LEN + UDP_HDR_LEN + BFD_PKT_LEN)
78 #define UDP_CTRL_PKT_LEN (UDP_HDR_LEN + BFD_PKT_LEN)
79
80 static uint8_t msgbuf[BFD_PKT_LEN];
81
82 /*
83 * Prototypes
84 */
85 static uint16_t ptm_bfd_gen_IP_ID(struct bfd_session *bfd);
86 static void ptm_bfd_echo_pkt_create(struct bfd_session *bfd);
87 static int ptm_bfd_echo_loopback(uint8_t *pkt, int pkt_len, struct sockaddr *ss,
88 socklen_t sslen);
89 static void ptm_bfd_vxlan_pkt_snd(struct bfd_session *bfd, int fbit);
90 static int ptm_bfd_process_echo_pkt(int s);
91 static bool
92 ptm_bfd_validate_vxlan_pkt(struct bfd_session *bfd,
93 struct bfd_session_vxlan_info *vxlan_info);
94
95 static void bfd_sd_reschedule(int sd);
96 static ssize_t bfd_recv_ipv4(int sd, bool is_mhop, char *port, size_t portlen,
97 char *vrfname, size_t vrfnamelen,
98 struct sockaddr_any *local,
99 struct sockaddr_any *peer);
100 static ssize_t bfd_recv_ipv6(int sd, bool is_mhop, char *port, size_t portlen,
101 char *vrfname, size_t vrfnamelen,
102 struct sockaddr_any *local,
103 struct sockaddr_any *peer);
104
105 /* socket related prototypes */
106 static void bp_set_ipopts(int sd);
107 static void bp_bind_ip(int sd, uint16_t port);
108 static void bp_set_ipv6opts(int sd);
109 static void bp_bind_ipv6(int sd, uint16_t port);
110
111
112 /*
113 * Functions
114 */
115 uint16_t checksum(uint16_t *buf, int len)
116 {
117 int nbytes = len;
118 int sum = 0;
119 uint16_t csum = 0;
120 int size = sizeof(uint16_t);
121
122 while (nbytes > 1) {
123 sum += *buf++;
124 nbytes -= size;
125 }
126
127 if (nbytes == 1) {
128 *(uint8_t *)(&csum) = *(uint8_t *)buf;
129 sum += csum;
130 }
131
132 sum = (sum >> 16) + (sum & 0xFFFF);
133 sum += (sum >> 16);
134 csum = ~sum;
135 return csum;
136 }
137
138 static uint16_t ptm_bfd_gen_IP_ID(struct bfd_session *bfd)
139 {
140 return (++bfd->ip_id);
141 }
142
143 static int _ptm_bfd_send(struct bfd_session *bs, bool use_layer2,
144 uint16_t *port, const void *data, size_t datalen)
145 {
146 struct sockaddr *sa;
147 struct sockaddr_in sin;
148 struct sockaddr_in6 sin6;
149 #ifdef BFD_LINUX
150 struct sockaddr_ll dll;
151 #endif /* BFD_LINUX */
152 socklen_t slen;
153 ssize_t rv;
154 int sd = -1;
155
156 if (use_layer2) {
157 #ifdef BFD_LINUX
158 memset(&dll, 0, sizeof(dll));
159 dll.sll_family = AF_PACKET;
160 dll.sll_protocol = htons(ETH_P_IP);
161 memcpy(dll.sll_addr, bs->peer_mac, ETHERNET_ADDRESS_LENGTH);
162 dll.sll_halen = htons(ETHERNET_ADDRESS_LENGTH);
163 dll.sll_ifindex = bs->ifindex;
164
165 sd = bglobal.bg_echo;
166 sa = (struct sockaddr *)&dll;
167 slen = sizeof(dll);
168 #else
169 /*
170 * TODO: implement layer 2 send for *BSDs. This is
171 * needed for VxLAN.
172 */
173 log_warning("packet-send: not implemented");
174 return -1;
175 #endif
176 } else if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_IPV6)) {
177 memset(&sin6, 0, sizeof(sin6));
178 sin6.sin6_family = AF_INET6;
179 sin6.sin6_addr = bs->shop.peer.sa_sin6.sin6_addr;
180 sin6.sin6_port =
181 (port) ? *port
182 : (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH))
183 ? htons(BFD_DEF_MHOP_DEST_PORT)
184 : htons(BFD_DEFDESTPORT);
185
186 sd = bs->sock;
187 sa = (struct sockaddr *)&sin6;
188 slen = sizeof(sin6);
189 } else {
190 memset(&sin, 0, sizeof(sin));
191 sin.sin_family = AF_INET;
192 sin.sin_addr = bs->shop.peer.sa_sin.sin_addr;
193 sin.sin_port =
194 (port) ? *port
195 : (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH))
196 ? htons(BFD_DEF_MHOP_DEST_PORT)
197 : htons(BFD_DEFDESTPORT);
198
199 sd = bs->sock;
200 sa = (struct sockaddr *)&sin;
201 slen = sizeof(sin);
202 }
203
204 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
205 sa->sa_len = slen;
206 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
207 rv = sendto(sd, data, datalen, 0, sa, slen);
208 if (rv <= 0) {
209 log_debug("packet-send: send failure: %s", strerror(errno));
210 return -1;
211 }
212 if (rv < (ssize_t)datalen)
213 log_debug("packet-send: send partial", strerror(errno));
214
215 return 0;
216 }
217
218 static void ptm_bfd_echo_pkt_create(struct bfd_session *bfd)
219 {
220 struct bfd_raw_echo_pkt ep;
221 uint8_t *pkt = bfd->echo_pkt;
222
223 memset(&ep, 0, sizeof(ep));
224 memset(bfd->echo_pkt, 0, sizeof(bfd->echo_pkt));
225
226 /* Construct ethernet header information */
227 memcpy(pkt, bfd->peer_mac, ETHERNET_ADDRESS_LENGTH);
228 pkt = pkt + ETHERNET_ADDRESS_LENGTH;
229 memcpy(pkt, bfd->local_mac, ETHERNET_ADDRESS_LENGTH);
230 pkt = pkt + ETHERNET_ADDRESS_LENGTH;
231 #ifdef BFD_LINUX
232 pkt[0] = ETH_P_IP / 256;
233 pkt[1] = ETH_P_IP % 256;
234 #endif /* BFD_LINUX */
235 #ifdef BFD_BSD
236 pkt[0] = ETHERTYPE_IP / 256;
237 pkt[1] = ETHERTYPE_IP % 256;
238 #endif /* BFD_BSD */
239 pkt += 2;
240
241 /* Construct IP header information */
242 #ifdef BFD_LINUX
243 ep.ip.version = 4;
244 ep.ip.ihl = 5;
245 ep.ip.tos = 0;
246 ep.ip.tot_len = htons(IP_ECHO_PKT_LEN);
247 ep.ip.id = htons(ptm_bfd_gen_IP_ID(bfd));
248 ep.ip.frag_off = 0;
249 ep.ip.ttl = BFD_TTL_VAL;
250 ep.ip.protocol = IPPROTO_UDP;
251 ep.ip.saddr = bfd->local_ip.sa_sin.sin_addr.s_addr;
252 ep.ip.daddr = bfd->shop.peer.sa_sin.sin_addr.s_addr;
253 ep.ip.check = checksum((uint16_t *)&ep.ip, IP_HDR_LEN);
254 #endif /* BFD_LINUX */
255 #ifdef BFD_BSD
256 ep.ip.ip_v = 4;
257 ep.ip.ip_hl = 5;
258 ep.ip.ip_tos = 0;
259 ep.ip.ip_len = htons(IP_ECHO_PKT_LEN);
260 ep.ip.ip_id = htons(ptm_bfd_gen_IP_ID(bfd));
261 ep.ip.ip_off = 0;
262 ep.ip.ip_ttl = BFD_TTL_VAL;
263 ep.ip.ip_p = IPPROTO_UDP;
264 ep.ip.ip_src = bfd->local_ip.sa_sin.sin_addr;
265 ep.ip.ip_dst = bfd->shop.peer.sa_sin.sin_addr;
266 ep.ip.ip_sum = checksum((uint16_t *)&ep.ip, IP_HDR_LEN);
267 #endif /* BFD_BSD */
268
269 /* Construct UDP header information */
270 #ifdef BFD_LINUX
271 ep.udp.source = htons(BFD_DEF_ECHO_PORT);
272 ep.udp.dest = htons(BFD_DEF_ECHO_PORT);
273 ep.udp.len = htons(UDP_ECHO_PKT_LEN);
274 #endif /* BFD_LINUX */
275 #ifdef BFD_BSD
276 ep.udp.uh_sport = htons(BFD_DEF_ECHO_PORT);
277 ep.udp.uh_dport = htons(BFD_DEF_ECHO_PORT);
278 ep.udp.uh_ulen = htons(UDP_ECHO_PKT_LEN);
279 #endif /* BFD_BSD */
280
281 /* Construct Echo packet information */
282 ep.data.ver = BFD_ECHO_VERSION;
283 ep.data.len = BFD_ECHO_PKT_LEN;
284 ep.data.my_discr = htonl(bfd->discrs.my_discr);
285 #ifdef BFD_LINUX
286 ep.udp.check =
287 #endif /* BFD_LINUX */
288 #ifdef BFD_BSD
289 ep.udp.uh_sum =
290 #endif /* BFD_BSD */
291 udp4_checksum(&ep.ip, (uint8_t *)&ep.udp,
292 UDP_ECHO_PKT_LEN);
293
294 memcpy(pkt, &ep, sizeof(ep));
295 }
296
297 void ptm_bfd_echo_snd(struct bfd_session *bfd)
298 {
299 struct bfd_raw_echo_pkt *ep;
300 bool use_layer2 = false;
301 const void *pkt;
302 size_t pktlen;
303 uint16_t port = htons(BFD_DEF_ECHO_PORT);
304
305 if (!BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE)) {
306 ptm_bfd_echo_pkt_create(bfd);
307 BFD_SET_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE);
308 } else {
309 /* just update the checksum and ip Id */
310 ep = (struct bfd_raw_echo_pkt *)(bfd->echo_pkt + ETH_HDR_LEN);
311 #ifdef BFD_LINUX
312 ep->ip.id = htons(ptm_bfd_gen_IP_ID(bfd));
313 ep->ip.check = 0;
314 ep->ip.check = checksum((uint16_t *)&ep->ip, IP_HDR_LEN);
315 #endif /* BFD_LINUX */
316 #ifdef BFD_BSD
317 ep->ip.ip_id = htons(ptm_bfd_gen_IP_ID(bfd));
318 ep->ip.ip_sum = 0;
319 ep->ip.ip_sum = checksum((uint16_t *)&ep->ip, IP_HDR_LEN);
320 #endif /* BFD_BSD */
321 }
322
323 if (use_layer2) {
324 pkt = bfd->echo_pkt;
325 pktlen = BFD_ECHO_PKT_TOT_LEN;
326 } else {
327 pkt = &bfd->echo_pkt[ETH_HDR_LEN + IP_HDR_LEN + UDP_HDR_LEN];
328 pktlen = BFD_ECHO_PKT_TOT_LEN
329 - (ETH_HDR_LEN + IP_HDR_LEN + UDP_HDR_LEN);
330 }
331
332 if (_ptm_bfd_send(bfd, use_layer2, &port, pkt, pktlen) != 0) {
333 log_debug("echo-packet: send failure: %s", strerror(errno));
334 return;
335 }
336
337 bfd->stats.tx_echo_pkt++;
338 }
339
340 static int ptm_bfd_echo_loopback(uint8_t *pkt, int pkt_len, struct sockaddr *ss,
341 socklen_t sslen)
342 {
343 #ifdef BFD_LINUX
344 struct bfd_raw_echo_pkt *ep =
345 (struct bfd_raw_echo_pkt *)(pkt + ETH_HDR_LEN);
346 uint8_t temp_mac[ETHERNET_ADDRESS_LENGTH];
347 uint32_t temp_ip;
348 struct ethhdr *eth = (struct ethhdr *)pkt;
349
350 /* swap the mac addresses */
351 memcpy(temp_mac, eth->h_source, ETHERNET_ADDRESS_LENGTH);
352 memcpy(eth->h_source, eth->h_dest, ETHERNET_ADDRESS_LENGTH);
353 memcpy(eth->h_dest, temp_mac, ETHERNET_ADDRESS_LENGTH);
354
355 /* swap ip addresses */
356 temp_ip = ep->ip.saddr;
357 ep->ip.saddr = ep->ip.daddr;
358 ep->ip.daddr = temp_ip;
359
360 ep->ip.ttl = ep->ip.ttl - 1;
361 ep->ip.check = 0;
362 ep->ip.check = checksum((uint16_t *)ep, IP_HDR_LEN);
363 #endif /* BFD_LINUX */
364 #ifdef BFD_BSD_FILTER
365 struct bfd_raw_echo_pkt_t *ep =
366 (struct bfd_raw_echo_pkt *)(pkt + ETH_HDR_LEN);
367 uint8_t temp_mac[ETHERNET_ADDRESS_LENGTH];
368 struct in_addr temp_ip;
369 struct ether_header *ether = (struct ether_header *)pkt;
370
371 /*
372 * TODO: this is not yet implemented and requires BPF code for
373 * OmniOS, NetBSD and FreeBSD9.
374 */
375
376 /* swap the mac addresses */
377 memcpy(temp_mac, ether->ether_shost, ETHERNET_ADDRESS_LENGTH);
378 memcpy(ether->ether_shost, ether->ether_dhost, ETHERNET_ADDRESS_LENGTH);
379 memcpy(ether->ether_dhost, temp_mac, ETHERNET_ADDRESS_LENGTH);
380
381 /* swap ip addresses */
382 temp_ip = ep->ip.ip_src;
383 ep->ip.ip_src = ep->ip.ip_dst;
384 ep->ip.ip_dst = temp_ip;
385
386 ep->ip.ip_ttl = ep->ip.ip_ttl - 1;
387 ep->ip.ip_sum = 0;
388 ep->ip.ip_sum = checksum((uint16_t *)ep, IP_HDR_LEN);
389 #endif /* BFD_BSD_FILTER */
390
391 if (sendto(bglobal.bg_echo, pkt, pkt_len, 0, ss, sslen) < 0) {
392 log_debug("echo-loopback: send failure: %s", strerror(errno));
393 return -1;
394 }
395
396 return 0;
397 }
398
399 static void ptm_bfd_vxlan_pkt_snd(struct bfd_session *bfd
400 __attribute__((__unused__)),
401 int fbit __attribute__((__unused__)))
402 {
403 #if 0 /* TODO: VxLAN support. */
404 struct bfd_raw_ctrl_pkt cp;
405 uint8_t vxlan_pkt[BFD_VXLAN_PKT_TOT_LEN];
406 uint8_t *pkt = vxlan_pkt;
407 struct sockaddr_in sin;
408 struct vxlan_hdr *vhdr;
409
410 memset(vxlan_pkt, 0, sizeof(vxlan_pkt));
411 memset(&cp, 0, sizeof(cp));
412
413 /* Construct VxLAN header information */
414 vhdr = (struct vxlan_hdr *)pkt;
415 vhdr->flags = htonl(0x08000000);
416 vhdr->vnid = htonl(bfd->vxlan_info.vnid << 8);
417 pkt += VXLAN_HDR_LEN;
418
419 /* Construct ethernet header information */
420 memcpy(pkt, bfd->vxlan_info.peer_dst_mac, ETHERNET_ADDRESS_LENGTH);
421 pkt = pkt + ETHERNET_ADDRESS_LENGTH;
422 memcpy(pkt, bfd->vxlan_info.local_dst_mac, ETHERNET_ADDRESS_LENGTH);
423 pkt = pkt + ETHERNET_ADDRESS_LENGTH;
424 pkt[0] = ETH_P_IP / 256;
425 pkt[1] = ETH_P_IP % 256;
426 pkt += 2;
427
428 /* Construct IP header information */
429 cp.ip.version = 4;
430 cp.ip.ihl = 5;
431 cp.ip.tos = 0;
432 cp.ip.tot_len = htons(IP_CTRL_PKT_LEN);
433 cp.ip.id = ptm_bfd_gen_IP_ID(bfd);
434 cp.ip.frag_off = 0;
435 cp.ip.ttl = BFD_TTL_VAL;
436 cp.ip.protocol = IPPROTO_UDP;
437 cp.ip.daddr = bfd->vxlan_info.peer_dst_ip.s_addr;
438 cp.ip.saddr = bfd->vxlan_info.local_dst_ip.s_addr;
439 cp.ip.check = checksum((uint16_t *)&cp.ip, IP_HDR_LEN);
440
441 /* Construct UDP header information */
442 cp.udp.source = htons(BFD_DEFDESTPORT);
443 cp.udp.dest = htons(BFD_DEFDESTPORT);
444 cp.udp.len = htons(UDP_CTRL_PKT_LEN);
445
446 /* Construct BFD control packet information */
447 cp.data.diag = bfd->local_diag;
448 BFD_SETVER(cp.data.diag, BFD_VERSION);
449 BFD_SETSTATE(cp.data.flags, bfd->ses_state);
450 BFD_SETDEMANDBIT(cp.data.flags, BFD_DEF_DEMAND);
451 BFD_SETPBIT(cp.data.flags, bfd->polling);
452 BFD_SETFBIT(cp.data.flags, fbit);
453 cp.data.detect_mult = bfd->detect_mult;
454 cp.data.len = BFD_PKT_LEN;
455 cp.data.discrs.my_discr = htonl(bfd->discrs.my_discr);
456 cp.data.discrs.remote_discr = htonl(bfd->discrs.remote_discr);
457 cp.data.timers.desired_min_tx = htonl(bfd->timers.desired_min_tx);
458 cp.data.timers.required_min_rx = htonl(bfd->timers.required_min_rx);
459 cp.data.timers.required_min_echo = htonl(bfd->timers.required_min_echo);
460
461 cp.udp.check =
462 udp4_checksum(&cp.ip, (uint8_t *)&cp.udp, UDP_CTRL_PKT_LEN);
463
464 memcpy(pkt, &cp, sizeof(cp));
465 sin.sin_family = AF_INET;
466 sin.sin_addr = bfd->shop.peer.sa_sin.sin_addr;
467 sin.sin_port = htons(4789);
468
469 if (sendto(bfd->sock, vxlan_pkt, BFD_VXLAN_PKT_TOT_LEN, 0,
470 (struct sockaddr *)&sin, sizeof(struct sockaddr_in))
471 < 0) {
472 ERRLOG("Error sending vxlan bfd pkt: %s", strerror(errno));
473 } else {
474 bfd->stats.tx_ctrl_pkt++;
475 }
476 #endif
477 }
478
479 static int ptm_bfd_process_echo_pkt(int s)
480 {
481 uint32_t my_discr = 0;
482 struct sockaddr_storage ss;
483 socklen_t sslen = sizeof(ss);
484 uint8_t rx_pkt[BFD_RX_BUF_LEN];
485 ssize_t pkt_len = sizeof(rx_pkt);
486 struct bfd_session *bfd;
487 #ifdef BFD_LINUX
488 struct bfd_raw_echo_pkt *ep;
489
490 /*
491 * valgrind: memset() ss so valgrind doesn't complain about
492 * uninitialized memory.
493 */
494 memset(&ss, 0, sizeof(ss));
495 pkt_len = recvfrom(s, rx_pkt, sizeof(rx_pkt), MSG_DONTWAIT,
496 (struct sockaddr *)&ss, &sslen);
497 if (pkt_len <= 0) {
498 if (errno != EAGAIN)
499 log_error("echo-packet: read failure: %s",
500 strerror(errno));
501
502 return -1;
503 }
504
505 /* Check if we have at least the basic headers to send back. */
506 if (pkt_len < BFD_ECHO_PKT_TOT_LEN) {
507 log_debug("echo-packet: too short (got %ld, expected %d)",
508 pkt_len, BFD_ECHO_PKT_TOT_LEN);
509 return -1;
510 }
511
512 ep = (struct bfd_raw_echo_pkt *)(rx_pkt + ETH_HDR_LEN);
513 /* if TTL = 255, assume that the received echo packet has
514 * to be looped back
515 */
516 if (ep->ip.ttl == BFD_TTL_VAL)
517 return ptm_bfd_echo_loopback(rx_pkt, pkt_len,
518 (struct sockaddr *)&ss,
519 sizeof(struct sockaddr_ll));
520
521 my_discr = ntohl(ep->data.my_discr);
522 if (ep->data.my_discr == 0) {
523 log_debug("echo-packet: 'my discriminator' is zero");
524 return -1;
525 }
526 #endif /* BFD_LINUX */
527 #ifdef BFD_BSD
528 int rv;
529 uint8_t ttl;
530
531 /*
532 * bsd_echo_sock_read() already treats invalid TTL values and
533 * zeroed discriminators.
534 */
535 rv = bsd_echo_sock_read(s, rx_pkt, &pkt_len, &ss, &sslen, &ttl,
536 &my_discr);
537 if (rv == -1)
538 return -1;
539
540 if (ttl == BFD_TTL_VAL)
541 return ptm_bfd_echo_loopback(rx_pkt, pkt_len,
542 (struct sockaddr *)&ss, sslen);
543 #endif /* BFD_BSD */
544
545 /* Your discriminator not zero - use it to find session */
546 bfd = bfd_id_lookup(my_discr);
547 if (bfd == NULL) {
548 log_debug("echo-packet: no matching session (id:%u)", my_discr);
549 return -1;
550 }
551
552 if (!BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE)) {
553 log_debug("echo-packet: echo disabled [%s]", my_discr,
554 bs_to_string(bfd));
555 return -1;
556 }
557
558 bfd->stats.rx_echo_pkt++;
559
560 /* Compute detect time */
561 bfd->echo_detect_TO = bfd->remote_detect_mult * bfd->echo_xmt_TO;
562
563 /* Update echo receive timeout. */
564 bfd_echo_recvtimer_update(bfd);
565
566 return 0;
567 }
568
569 void ptm_bfd_snd(struct bfd_session *bfd, int fbit)
570 {
571 struct bfd_pkt cp;
572
573 /* if the BFD session is for VxLAN tunnel, then construct and
574 * send bfd raw packet
575 */
576 if (BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_VXLAN)) {
577 ptm_bfd_vxlan_pkt_snd(bfd, fbit);
578 return;
579 }
580
581 /* Set fields according to section 6.5.7 */
582 cp.diag = bfd->local_diag;
583 BFD_SETVER(cp.diag, BFD_VERSION);
584 cp.flags = 0;
585 BFD_SETSTATE(cp.flags, bfd->ses_state);
586 BFD_SETDEMANDBIT(cp.flags, BFD_DEF_DEMAND);
587 BFD_SETPBIT(cp.flags, bfd->polling);
588 BFD_SETFBIT(cp.flags, fbit);
589 cp.detect_mult = bfd->detect_mult;
590 cp.len = BFD_PKT_LEN;
591 cp.discrs.my_discr = htonl(bfd->discrs.my_discr);
592 cp.discrs.remote_discr = htonl(bfd->discrs.remote_discr);
593 if (bfd->polling) {
594 cp.timers.desired_min_tx =
595 htonl(bfd->new_timers.desired_min_tx);
596 cp.timers.required_min_rx =
597 htonl(bfd->new_timers.required_min_rx);
598 } else {
599 cp.timers.desired_min_tx = htonl(bfd->timers.desired_min_tx);
600 cp.timers.required_min_rx = htonl(bfd->timers.required_min_rx);
601 }
602 cp.timers.required_min_echo = htonl(bfd->timers.required_min_echo);
603
604 if (_ptm_bfd_send(bfd, false, NULL, &cp, BFD_PKT_LEN) != 0)
605 return;
606
607 bfd->stats.tx_ctrl_pkt++;
608 }
609
610 #if 0 /* TODO VxLAN Support */
611 static struct bfd_pkt *
612 ptm_bfd_process_vxlan_pkt(int s, ptm_sockevent_e se, void *udata, int *ifindex,
613 struct sockaddr_in *sin,
614 struct bfd_session_vxlan_info_t *vxlan_info,
615 uint8_t *rx_pkt, int *mlen)
616 {
617 struct sockaddr_ll sll;
618 uint32_t from_len = sizeof(struct sockaddr_ll);
619 struct bfd_raw_ctrl_pkt *cp;
620 uint8_t *pkt = rx_pkt;
621 struct iphdr *iph;
622 struct ethhdr *inner_ethh;
623
624 *mlen = recvfrom(s, rx_pkt, BFD_RX_BUF_LEN, MSG_DONTWAIT,
625 (struct sockaddr *)&sll, &from_len);
626
627 if (*mlen < 0) {
628 if (errno != EAGAIN)
629 ERRLOG("Error receiving from BFD Vxlan socket %d: %m",
630 s);
631 return NULL;
632 }
633
634 iph = (struct iphdr *)(pkt + ETH_HDR_LEN);
635 pkt = pkt + ETH_HDR_LEN + IP_HDR_LEN + UDP_HDR_LEN;
636 vxlan_info->vnid = ntohl(*((int *)(pkt + 4)));
637 vxlan_info->vnid = vxlan_info->vnid >> 8;
638
639 pkt = pkt + VXLAN_HDR_LEN;
640 inner_ethh = (struct ethhdr *)pkt;
641
642 cp = (struct bfd_raw_ctrl_pkt *)(pkt + ETH_HDR_LEN);
643
644 /* Discard the non BFD packets */
645 if (ntohs(cp->udp.dest) != BFD_DEFDESTPORT)
646 return NULL;
647
648 *ifindex = sll.sll_ifindex;
649 sin->sin_addr.s_addr = iph->saddr;
650 sin->sin_port = ntohs(cp->udp.dest);
651
652 vxlan_info->local_dst_ip.s_addr = cp->ip.daddr;
653 memcpy(vxlan_info->local_dst_mac, inner_ethh->h_dest,
654 ETHERNET_ADDRESS_LENGTH);
655
656 return &cp->data;
657 }
658 #endif /* VxLAN */
659
660 static bool
661 ptm_bfd_validate_vxlan_pkt(struct bfd_session *bfd,
662 struct bfd_session_vxlan_info *vxlan_info)
663 {
664 if (bfd->vxlan_info.check_tnl_key && (vxlan_info->vnid != 0)) {
665 log_error("vxlan-packet: vnid not zero: %d", vxlan_info->vnid);
666 return false;
667 }
668
669 if (bfd->vxlan_info.local_dst_ip.s_addr
670 != vxlan_info->local_dst_ip.s_addr) {
671 log_error("vxlan-packet: wrong inner destination",
672 inet_ntoa(vxlan_info->local_dst_ip));
673 return false;
674 }
675
676 if (memcmp(bfd->vxlan_info.local_dst_mac, vxlan_info->local_dst_mac,
677 ETHERNET_ADDRESS_LENGTH)) {
678 log_error(
679 "vxlan-packet: wrong inner mac: %02x:%02x:%02x:%02x:%02x:%02x",
680 vxlan_info->local_dst_mac[0],
681 vxlan_info->local_dst_mac[1],
682 vxlan_info->local_dst_mac[2],
683 vxlan_info->local_dst_mac[3],
684 vxlan_info->local_dst_mac[4],
685 vxlan_info->local_dst_mac[5]);
686 return false;
687 }
688
689 return true;
690 }
691
692 static ssize_t bfd_recv_ipv4(int sd, bool is_mhop, char *port, size_t portlen,
693 char *vrfname, size_t vrfnamelen,
694 struct sockaddr_any *local,
695 struct sockaddr_any *peer)
696 {
697 struct cmsghdr *cm;
698 int ifindex;
699 ssize_t mlen;
700 struct sockaddr_in msgaddr;
701 struct msghdr msghdr;
702 struct iovec iov[1];
703 uint8_t cmsgbuf[255];
704
705 /* Prepare the recvmsg params. */
706 iov[0].iov_base = msgbuf;
707 iov[0].iov_len = sizeof(msgbuf);
708
709 memset(&msghdr, 0, sizeof(msghdr));
710 msghdr.msg_name = &msgaddr;
711 msghdr.msg_namelen = sizeof(msgaddr);
712 msghdr.msg_iov = iov;
713 msghdr.msg_iovlen = 1;
714 msghdr.msg_control = cmsgbuf;
715 msghdr.msg_controllen = sizeof(cmsgbuf);
716
717 mlen = recvmsg(sd, &msghdr, MSG_DONTWAIT);
718 if (mlen == -1) {
719 if (errno != EAGAIN)
720 log_error("ipv4-recv: recv failed: %s",
721 strerror(errno));
722
723 return -1;
724 }
725
726 /* Get source address */
727 peer->sa_sin = *((struct sockaddr_in *)(msghdr.msg_name));
728
729 /* Get and check TTL */
730 for (cm = CMSG_FIRSTHDR(&msghdr); cm != NULL;
731 cm = CMSG_NXTHDR(&msghdr, cm)) {
732 if (cm->cmsg_level != IPPROTO_IP)
733 continue;
734
735 switch (cm->cmsg_type) {
736 #ifdef BFD_LINUX
737 case IP_TTL: {
738 uint32_t ttl;
739
740 memcpy(&ttl, CMSG_DATA(cm), sizeof(ttl));
741 if ((is_mhop == false) && (ttl != BFD_TTL_VAL)) {
742 log_debug(
743 "ipv4-recv: invalid TTL from %s (expected %d, got %d flags %d)",
744 satostr(peer), ttl, BFD_TTL_VAL,
745 msghdr.msg_flags);
746 return -1;
747 }
748 break;
749 }
750
751 case IP_PKTINFO: {
752 struct in_pktinfo *pi =
753 (struct in_pktinfo *)CMSG_DATA(cm);
754
755 if (pi == NULL)
756 break;
757
758 local->sa_sin.sin_family = AF_INET;
759 local->sa_sin.sin_addr = pi->ipi_addr;
760 fetch_portname_from_ifindex(pi->ipi_ifindex, port,
761 portlen);
762 break;
763 }
764 #endif /* BFD_LINUX */
765 #ifdef BFD_BSD
766 case IP_RECVTTL: {
767 uint8_t ttl;
768
769 memcpy(&ttl, CMSG_DATA(cm), sizeof(ttl));
770 if ((is_mhop == false) && (ttl != BFD_TTL_VAL)) {
771 log_debug(
772 "ipv4-recv: invalid TTL from %s (expected %d, got %d flags %d)",
773 satostr(peer), ttl, BFD_TTL_VAL,
774 msghdr.msg_flags);
775 return -1;
776 }
777 break;
778 }
779
780 case IP_RECVDSTADDR: {
781 struct in_addr ia;
782
783 memcpy(&ia, CMSG_DATA(cm), sizeof(ia));
784 local->sa_sin.sin_family = AF_INET;
785 local->sa_sin.sin_addr = ia;
786 break;
787 }
788 #endif /* BFD_BSD */
789
790 default:
791 /*
792 * On *BSDs we expect to land here when skipping
793 * the IP_RECVIF header. It will be handled by
794 * getsockopt_ifindex() below.
795 */
796 /* NOTHING */
797 break;
798 }
799 }
800
801 /* OS agnostic way of getting interface name. */
802 if (port[0] == 0) {
803 ifindex = getsockopt_ifindex(AF_INET, &msghdr);
804 if (ifindex > 0)
805 fetch_portname_from_ifindex(ifindex, port, portlen);
806 }
807
808 return mlen;
809 }
810
811 ssize_t bfd_recv_ipv6(int sd, bool is_mhop, char *port, size_t portlen,
812 char *vrfname, size_t vrfnamelen,
813 struct sockaddr_any *local, struct sockaddr_any *peer)
814 {
815 struct cmsghdr *cm;
816 struct in6_pktinfo *pi6 = NULL;
817 int ifindex = 0;
818 ssize_t mlen;
819 uint32_t ttlval;
820 struct sockaddr_in6 msgaddr6;
821 struct msghdr msghdr6;
822 struct iovec iov[1];
823 uint8_t cmsgbuf6[255];
824
825 /* Prepare the recvmsg params. */
826 iov[0].iov_base = msgbuf;
827 iov[0].iov_len = sizeof(msgbuf);
828
829 memset(&msghdr6, 0, sizeof(msghdr6));
830 msghdr6.msg_name = &msgaddr6;
831 msghdr6.msg_namelen = sizeof(msgaddr6);
832 msghdr6.msg_iov = iov;
833 msghdr6.msg_iovlen = 1;
834 msghdr6.msg_control = cmsgbuf6;
835 msghdr6.msg_controllen = sizeof(cmsgbuf6);
836
837 mlen = recvmsg(sd, &msghdr6, MSG_DONTWAIT);
838 if (mlen == -1) {
839 if (errno != EAGAIN)
840 log_error("ipv6-recv: recv failed: %s",
841 strerror(errno));
842
843 return -1;
844 }
845
846 /* Get source address */
847 peer->sa_sin6 = *((struct sockaddr_in6 *)(msghdr6.msg_name));
848
849 /* Get and check TTL */
850 for (cm = CMSG_FIRSTHDR(&msghdr6); cm != NULL;
851 cm = CMSG_NXTHDR(&msghdr6, cm)) {
852 if (cm->cmsg_level != IPPROTO_IPV6)
853 continue;
854
855 if (cm->cmsg_type == IPV6_HOPLIMIT) {
856 memcpy(&ttlval, CMSG_DATA(cm), sizeof(ttlval));
857 if ((is_mhop == false) && (ttlval != BFD_TTL_VAL)) {
858 log_debug(
859 "ipv6-recv: invalid TTL from %s (expected %d, got %d flags %d)",
860 satostr(peer), ttlval, BFD_TTL_VAL,
861 msghdr6.msg_flags);
862 return -1;
863 }
864 } else if (cm->cmsg_type == IPV6_PKTINFO) {
865 pi6 = (struct in6_pktinfo *)CMSG_DATA(cm);
866 if (pi6) {
867 local->sa_sin.sin_family = AF_INET6;
868 local->sa_sin6.sin6_addr = pi6->ipi6_addr;
869 fetch_portname_from_ifindex(pi6->ipi6_ifindex,
870 port, portlen);
871 ifindex = pi6->ipi6_ifindex;
872 }
873 }
874 }
875
876 /* Set scope ID for link local addresses. */
877 if (IN6_IS_ADDR_LINKLOCAL(&peer->sa_sin6.sin6_addr))
878 peer->sa_sin6.sin6_scope_id = ifindex;
879 if (IN6_IS_ADDR_LINKLOCAL(&local->sa_sin6.sin6_addr))
880 local->sa_sin6.sin6_scope_id = ifindex;
881
882 return mlen;
883 }
884
885 static void bfd_sd_reschedule(int sd)
886 {
887 if (sd == bglobal.bg_shop) {
888 bglobal.bg_ev[0] = NULL;
889 thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_shop,
890 &bglobal.bg_ev[0]);
891 } else if (sd == bglobal.bg_mhop) {
892 bglobal.bg_ev[1] = NULL;
893 thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_mhop,
894 &bglobal.bg_ev[1]);
895 } else if (sd == bglobal.bg_shop6) {
896 bglobal.bg_ev[2] = NULL;
897 thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_shop6,
898 &bglobal.bg_ev[2]);
899 } else if (sd == bglobal.bg_mhop6) {
900 bglobal.bg_ev[3] = NULL;
901 thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_mhop6,
902 &bglobal.bg_ev[3]);
903 } else if (sd == bglobal.bg_echo) {
904 bglobal.bg_ev[4] = NULL;
905 thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_echo,
906 &bglobal.bg_ev[4]);
907 } else if (sd == bglobal.bg_vxlan) {
908 bglobal.bg_ev[5] = NULL;
909 thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_vxlan,
910 &bglobal.bg_ev[5]);
911 }
912 }
913
914 static void cp_debug(bool mhop, struct sockaddr_any *peer,
915 struct sockaddr_any *local, const char *port,
916 const char *vrf, const char *fmt, ...)
917 {
918 char buf[512], peerstr[128], localstr[128], portstr[64], vrfstr[64];
919 va_list vl;
920
921 if (peer->sa_sin.sin_family)
922 snprintf(peerstr, sizeof(peerstr), " peer:%s", satostr(peer));
923 else
924 peerstr[0] = 0;
925
926 if (local->sa_sin.sin_family)
927 snprintf(localstr, sizeof(localstr), " local:%s",
928 satostr(local));
929 else
930 localstr[0] = 0;
931
932 if (port[0])
933 snprintf(portstr, sizeof(portstr), " port:%s", port);
934 else
935 portstr[0] = 0;
936
937 if (vrf[0])
938 snprintf(vrfstr, sizeof(vrfstr), " vrf:%s", port);
939 else
940 vrfstr[0] = 0;
941
942 va_start(vl, fmt);
943 vsnprintf(buf, sizeof(buf), fmt, vl);
944 va_end(vl);
945
946 log_debug("control-packet: %s [mhop:%s%s%s%s%s]", buf,
947 mhop ? "yes" : "no", peerstr, localstr, portstr, vrfstr);
948 }
949
950 int bfd_recv_cb(struct thread *t)
951 {
952 int sd = THREAD_FD(t);
953 struct bfd_session *bfd;
954 struct bfd_pkt *cp;
955 bool is_mhop, is_vxlan;
956 ssize_t mlen = 0;
957 uint32_t oldEchoXmt_TO, oldXmtTime;
958 struct sockaddr_any local, peer;
959 char port[MAXNAMELEN + 1], vrfname[MAXNAMELEN + 1];
960 struct bfd_session_vxlan_info vxlan_info;
961
962 /* Schedule next read. */
963 bfd_sd_reschedule(sd);
964
965 /* Handle echo packets. */
966 if (sd == bglobal.bg_echo) {
967 ptm_bfd_process_echo_pkt(sd);
968 return 0;
969 }
970
971 /* Sanitize input/output. */
972 memset(port, 0, sizeof(port));
973 memset(vrfname, 0, sizeof(vrfname));
974 memset(&local, 0, sizeof(local));
975 memset(&peer, 0, sizeof(peer));
976
977 /* Handle control packets. */
978 is_mhop = is_vxlan = false;
979 if (sd == bglobal.bg_shop || sd == bglobal.bg_mhop) {
980 is_mhop = sd == bglobal.bg_mhop;
981 mlen = bfd_recv_ipv4(sd, is_mhop, port, sizeof(port), vrfname,
982 sizeof(vrfname), &local, &peer);
983 } else if (sd == bglobal.bg_shop6 || sd == bglobal.bg_mhop6) {
984 is_mhop = sd == bglobal.bg_mhop6;
985 mlen = bfd_recv_ipv6(sd, is_mhop, port, sizeof(port), vrfname,
986 sizeof(vrfname), &local, &peer);
987 }
988 #if 0 /* TODO vxlan handling */
989 cp = ptm_bfd_process_vxlan_pkt(s, se, udata, &local_ifindex,
990 &sin, &vxlan_info, rx_pkt, &mlen);
991 if (!cp)
992 return -1;
993
994 is_vxlan = true;
995 /* keep in network-byte order */
996 peer.ip4_addr.s_addr = sin.sin_addr.s_addr;
997 peer.family = AF_INET;
998 strcpy(peer_addr, inet_ntoa(sin.sin_addr));
999 #endif
1000
1001 /* Implement RFC 5880 6.8.6 */
1002 if (mlen < BFD_PKT_LEN) {
1003 cp_debug(is_mhop, &peer, &local, port, vrfname,
1004 "too small (%ld bytes)", mlen);
1005 return 0;
1006 }
1007
1008 /*
1009 * Parse the control header for inconsistencies:
1010 * - Invalid version;
1011 * - Bad multiplier configuration;
1012 * - Short packets;
1013 * - Invalid discriminator;
1014 */
1015 cp = (struct bfd_pkt *)(msgbuf);
1016 if (BFD_GETVER(cp->diag) != BFD_VERSION) {
1017 cp_debug(is_mhop, &peer, &local, port, vrfname,
1018 "bad version %d", BFD_GETVER(cp->diag));
1019 return 0;
1020 }
1021
1022 if (cp->detect_mult == 0) {
1023 cp_debug(is_mhop, &peer, &local, port, vrfname,
1024 "detect multiplier set to zero");
1025 return 0;
1026 }
1027
1028 if ((cp->len < BFD_PKT_LEN) || (cp->len > mlen)) {
1029 cp_debug(is_mhop, &peer, &local, port, vrfname, "too small");
1030 return 0;
1031 }
1032
1033 if (cp->discrs.my_discr == 0) {
1034 cp_debug(is_mhop, &peer, &local, port, vrfname,
1035 "'my discriminator' is zero");
1036 return 0;
1037 }
1038
1039 /* Find the session that this packet belongs. */
1040 bfd = ptm_bfd_sess_find(cp, port, &peer, &local, vrfname, is_mhop);
1041 if (bfd == NULL) {
1042 cp_debug(is_mhop, &peer, &local, port, vrfname,
1043 "no session found");
1044 return 0;
1045 }
1046
1047 /* Handle VxLAN cases. */
1048 if (is_vxlan && !ptm_bfd_validate_vxlan_pkt(bfd, &vxlan_info))
1049 return 0;
1050
1051 bfd->stats.rx_ctrl_pkt++;
1052
1053 /*
1054 * Multi hop: validate packet TTL.
1055 * Single hop: set local address that received the packet.
1056 */
1057 if (is_mhop) {
1058 if ((BFD_TTL_VAL - bfd->mh_ttl) > BFD_TTL_VAL) {
1059 cp_debug(is_mhop, &peer, &local, port, vrfname,
1060 "exceeded max hop count (expected %d, got %d)",
1061 bfd->mh_ttl, BFD_TTL_VAL);
1062 return 0;
1063 }
1064 } else if (bfd->local_ip.sa_sin.sin_family == AF_UNSPEC) {
1065 bfd->local_ip = local;
1066 }
1067
1068 /*
1069 * If no interface was detected, save the interface where the
1070 * packet came in.
1071 */
1072 if (bfd->ifindex == 0)
1073 bfd->ifindex = ptm_bfd_fetch_ifindex(port);
1074
1075 /* Log remote discriminator changes. */
1076 if ((bfd->discrs.remote_discr != 0)
1077 && (bfd->discrs.remote_discr != ntohl(cp->discrs.my_discr)))
1078 cp_debug(is_mhop, &peer, &local, port, vrfname,
1079 "remote discriminator mismatch (expected %d, got %d)",
1080 bfd->discrs.remote_discr, ntohl(cp->discrs.my_discr));
1081
1082 bfd->discrs.remote_discr = ntohl(cp->discrs.my_discr);
1083
1084 /* If received the Final bit, the new values should take effect */
1085 if (bfd->polling && BFD_GETFBIT(cp->flags)) {
1086 bfd->timers.desired_min_tx = bfd->new_timers.desired_min_tx;
1087 bfd->timers.required_min_rx = bfd->new_timers.required_min_rx;
1088 bfd->new_timers.desired_min_tx = 0;
1089 bfd->new_timers.required_min_rx = 0;
1090 bfd->polling = 0;
1091 }
1092
1093 if (!bfd->demand_mode) {
1094 /* Compute detect time */
1095 bfd->detect_TO = cp->detect_mult
1096 * ((bfd->timers.required_min_rx
1097 > ntohl(cp->timers.desired_min_tx))
1098 ? bfd->timers.required_min_rx
1099 : ntohl(cp->timers.desired_min_tx));
1100 bfd->remote_detect_mult = cp->detect_mult;
1101 } else
1102 cp_debug(is_mhop, &peer, &local, port, vrfname,
1103 "unsupported demand mode");
1104
1105 /* Save remote diagnostics before state switch. */
1106 bfd->remote_diag = cp->diag & BFD_DIAGMASK;
1107
1108 /* State switch from section 6.8.6 */
1109 if (BFD_GETSTATE(cp->flags) == PTM_BFD_ADM_DOWN) {
1110 if (bfd->ses_state != PTM_BFD_DOWN)
1111 ptm_bfd_ses_dn(bfd, BFD_DIAGNEIGHDOWN);
1112 } else {
1113 switch (bfd->ses_state) {
1114 case (PTM_BFD_DOWN):
1115 if (BFD_GETSTATE(cp->flags) == PTM_BFD_INIT)
1116 ptm_bfd_ses_up(bfd);
1117 else if (BFD_GETSTATE(cp->flags) == PTM_BFD_DOWN)
1118 bfd->ses_state = PTM_BFD_INIT;
1119 break;
1120 case (PTM_BFD_INIT):
1121 if (BFD_GETSTATE(cp->flags) == PTM_BFD_INIT
1122 || BFD_GETSTATE(cp->flags) == PTM_BFD_UP)
1123 ptm_bfd_ses_up(bfd);
1124 break;
1125 case (PTM_BFD_UP):
1126 if (BFD_GETSTATE(cp->flags) == PTM_BFD_DOWN)
1127 ptm_bfd_ses_dn(bfd, BFD_DIAGNEIGHDOWN);
1128 break;
1129 }
1130 }
1131
1132 /*
1133 * Handle echo packet status:
1134 * - Start echo packets if configured and permitted
1135 * (required_min_echo > 0);
1136 * - Stop echo packets if not allowed (required_min_echo == 0);
1137 * - Recalculate echo packet interval;
1138 */
1139 if (BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO)) {
1140 if (BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE)) {
1141 if (!ntohl(cp->timers.required_min_echo)) {
1142 ptm_bfd_echo_stop(bfd, 1);
1143 } else {
1144 oldEchoXmt_TO = bfd->echo_xmt_TO;
1145 bfd->echo_xmt_TO =
1146 bfd->timers.required_min_echo;
1147 if (ntohl(cp->timers.required_min_echo)
1148 > bfd->echo_xmt_TO)
1149 bfd->echo_xmt_TO = ntohl(
1150 cp->timers.required_min_echo);
1151 if (oldEchoXmt_TO != bfd->echo_xmt_TO)
1152 ptm_bfd_echo_start(bfd);
1153 }
1154 } else if (ntohl(cp->timers.required_min_echo)) {
1155 bfd->echo_xmt_TO = bfd->timers.required_min_echo;
1156 if (ntohl(cp->timers.required_min_echo)
1157 > bfd->echo_xmt_TO)
1158 bfd->echo_xmt_TO =
1159 ntohl(cp->timers.required_min_echo);
1160 ptm_bfd_echo_start(bfd);
1161 }
1162 }
1163
1164 if (BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE)) {
1165 bfd->echo_xmt_TO = bfd->timers.required_min_echo;
1166 if (ntohl(cp->timers.required_min_echo) > bfd->echo_xmt_TO)
1167 bfd->echo_xmt_TO = ntohl(cp->timers.required_min_echo);
1168 }
1169
1170 /* Calculate new transmit time */
1171 oldXmtTime = bfd->xmt_TO;
1172 bfd->xmt_TO =
1173 (bfd->timers.desired_min_tx > ntohl(cp->timers.required_min_rx))
1174 ? bfd->timers.desired_min_tx
1175 : ntohl(cp->timers.required_min_rx);
1176
1177 /* If transmit time has changed, and too much time until next xmt,
1178 * restart
1179 */
1180 if (BFD_GETPBIT(cp->flags)) {
1181 ptm_bfd_xmt_TO(bfd, 1);
1182 } else if (oldXmtTime != bfd->xmt_TO) {
1183 /* XXX add some skid to this as well */
1184 ptm_bfd_start_xmt_timer(bfd, false);
1185 }
1186
1187 /* Restart detection timer (packet received) */
1188 if (!bfd->demand_mode)
1189 bfd_recvtimer_update(bfd);
1190
1191 /*
1192 * Save the timers and state sent by the remote end
1193 * for debugging and statistics.
1194 */
1195 if (BFD_GETFBIT(cp->flags)) {
1196 bfd->remote_timers.desired_min_tx =
1197 ntohl(cp->timers.desired_min_tx);
1198 bfd->remote_timers.required_min_rx =
1199 ntohl(cp->timers.required_min_rx);
1200 bfd->remote_timers.required_min_echo =
1201 ntohl(cp->timers.required_min_echo);
1202
1203 control_notify_config(BCM_NOTIFY_CONFIG_UPDATE, bfd);
1204 }
1205
1206 return 0;
1207 }
1208
1209
1210 /*
1211 * Sockets creation.
1212 */
1213
1214
1215 /*
1216 * IPv4 sockets
1217 */
1218 int bp_set_ttl(int sd, uint8_t value)
1219 {
1220 int ttl = value;
1221
1222 if (setsockopt(sd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)) == -1) {
1223 log_warning("set-ttl: setsockopt(IP_TTL, %d): %s", value,
1224 strerror(errno));
1225 return -1;
1226 }
1227
1228 return 0;
1229 }
1230
1231 int bp_set_tos(int sd, uint8_t value)
1232 {
1233 int tos = value;
1234
1235 if (setsockopt(sd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) == -1) {
1236 log_warning("set-tos: setsockopt(IP_TOS, %d): %s", value,
1237 strerror(errno));
1238 return -1;
1239 }
1240
1241 return 0;
1242 }
1243
1244 static void bp_set_ipopts(int sd)
1245 {
1246 int rcvttl = BFD_RCV_TTL_VAL;
1247
1248 if (bp_set_ttl(sd, BFD_TTL_VAL) != 0)
1249 log_fatal("set-ipopts: TTL configuration failed");
1250
1251 if (setsockopt(sd, IPPROTO_IP, IP_RECVTTL, &rcvttl, sizeof(rcvttl))
1252 == -1)
1253 log_fatal("set-ipopts: setsockopt(IP_RECVTTL, %d): %s", rcvttl,
1254 strerror(errno));
1255
1256 #ifdef BFD_LINUX
1257 int pktinfo = BFD_PKT_INFO_VAL;
1258
1259 /* Figure out address and interface to do the peer matching. */
1260 if (setsockopt(sd, IPPROTO_IP, IP_PKTINFO, &pktinfo, sizeof(pktinfo))
1261 == -1)
1262 log_fatal("set-ipopts: setsockopt(IP_PKTINFO, %d): %s", pktinfo,
1263 strerror(errno));
1264 #endif /* BFD_LINUX */
1265 #ifdef BFD_BSD
1266 int yes = 1;
1267
1268 /* Find out our address for peer matching. */
1269 if (setsockopt(sd, IPPROTO_IP, IP_RECVDSTADDR, &yes, sizeof(yes)) == -1)
1270 log_fatal("set-ipopts: setsockopt(IP_RECVDSTADDR, %d): %s", yes,
1271 strerror(errno));
1272
1273 /* Find out interface where the packet came in. */
1274 if (setsockopt_ifindex(AF_INET, sd, yes) == -1)
1275 log_fatal("set-ipopts: setsockopt_ipv4_ifindex(%d): %s", yes,
1276 strerror(errno));
1277 #endif /* BFD_BSD */
1278 }
1279
1280 static void bp_bind_ip(int sd, uint16_t port)
1281 {
1282 struct sockaddr_in sin;
1283
1284 memset(&sin, 0, sizeof(sin));
1285 sin.sin_family = AF_INET;
1286 sin.sin_addr.s_addr = htonl(INADDR_ANY);
1287 sin.sin_port = htons(port);
1288 if (bind(sd, (struct sockaddr *)&sin, sizeof(sin)) == -1)
1289 log_fatal("bind-ip: bind: %s", strerror(errno));
1290 }
1291
1292 int bp_udp_shop(void)
1293 {
1294 int sd;
1295
1296 sd = socket(AF_INET, SOCK_DGRAM, PF_UNSPEC);
1297 if (sd == -1)
1298 log_fatal("udp-shop: socket: %s", strerror(errno));
1299
1300 bp_set_ipopts(sd);
1301 bp_bind_ip(sd, BFD_DEFDESTPORT);
1302
1303 return sd;
1304 }
1305
1306 int bp_udp_mhop(void)
1307 {
1308 int sd;
1309
1310 sd = socket(AF_INET, SOCK_DGRAM, PF_UNSPEC);
1311 if (sd == -1)
1312 log_fatal("udp-mhop: socket: %s", strerror(errno));
1313
1314 bp_set_ipopts(sd);
1315 bp_bind_ip(sd, BFD_DEF_MHOP_DEST_PORT);
1316
1317 return sd;
1318 }
1319
1320 int bp_peer_socket(struct bfd_peer_cfg *bpc)
1321 {
1322 int sd, pcount;
1323 struct sockaddr_in sin;
1324 static int srcPort = BFD_SRCPORTINIT;
1325
1326 sd = socket(AF_INET, SOCK_DGRAM, PF_UNSPEC);
1327 if (sd == -1) {
1328 log_error("ipv4-new: failed to create socket: %s",
1329 strerror(errno));
1330 return -1;
1331 }
1332
1333 if (!bpc->bpc_has_vxlan) {
1334 /* Set TTL to 255 for all transmitted packets */
1335 if (bp_set_ttl(sd, BFD_TTL_VAL) != 0) {
1336 close(sd);
1337 return -1;
1338 }
1339 }
1340
1341 /* Set TOS to CS6 for all transmitted packets */
1342 if (bp_set_tos(sd, BFD_TOS_VAL) != 0) {
1343 close(sd);
1344 return -1;
1345 }
1346
1347 /* dont bind-to-device incase of vxlan */
1348 if (!bpc->bpc_has_vxlan && bpc->bpc_has_localif) {
1349 if (bp_bind_dev(sd, bpc->bpc_localif) != 0) {
1350 close(sd);
1351 return -1;
1352 }
1353 } else if (bpc->bpc_mhop && bpc->bpc_has_vrfname) {
1354 if (bp_bind_dev(sd, bpc->bpc_vrfname) != 0) {
1355 close(sd);
1356 return -1;
1357 }
1358 }
1359
1360 /* Find an available source port in the proper range */
1361 memset(&sin, 0, sizeof(sin));
1362 sin = bpc->bpc_local.sa_sin;
1363 sin.sin_family = AF_INET;
1364 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1365 sin.sin_len = sizeof(sin);
1366 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1367 if (bpc->bpc_mhop || bpc->bpc_has_vxlan)
1368 sin.sin_addr = bpc->bpc_local.sa_sin.sin_addr;
1369 else
1370 sin.sin_addr.s_addr = INADDR_ANY;
1371
1372 pcount = 0;
1373 do {
1374 if ((++pcount) > (BFD_SRCPORTMAX - BFD_SRCPORTINIT)) {
1375 /* Searched all ports, none available */
1376 log_error("ipv4-new: failed to bind port: %s",
1377 strerror(errno));
1378 close(sd);
1379 return -1;
1380 }
1381 if (srcPort >= BFD_SRCPORTMAX)
1382 srcPort = BFD_SRCPORTINIT;
1383 sin.sin_port = htons(srcPort++);
1384 } while (bind(sd, (struct sockaddr *)&sin, sizeof(sin)) < 0);
1385
1386 return sd;
1387 }
1388
1389
1390 /*
1391 * IPv6 sockets
1392 */
1393
1394 int bp_peer_socketv6(struct bfd_peer_cfg *bpc)
1395 {
1396 int sd, pcount, ifindex;
1397 struct sockaddr_in6 sin6;
1398 static int srcPort = BFD_SRCPORTINIT;
1399
1400 sd = socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC);
1401 if (sd == -1) {
1402 log_error("ipv6-new: failed to create socket: %s",
1403 strerror(errno));
1404 return -1;
1405 }
1406
1407 if (!bpc->bpc_has_vxlan) {
1408 /* Set TTL to 255 for all transmitted packets */
1409 if (bp_set_ttlv6(sd, BFD_TTL_VAL) != 0) {
1410 close(sd);
1411 return -1;
1412 }
1413 }
1414
1415 /* Set TOS to CS6 for all transmitted packets */
1416 if (bp_set_tosv6(sd, BFD_TOS_VAL) != 0) {
1417 close(sd);
1418 return -1;
1419 }
1420
1421 /* Find an available source port in the proper range */
1422 memset(&sin6, 0, sizeof(sin6));
1423 sin6.sin6_family = AF_INET6;
1424 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1425 sin6.sin6_len = sizeof(sin6);
1426 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1427 sin6 = bpc->bpc_local.sa_sin6;
1428 ifindex = ptm_bfd_fetch_ifindex(bpc->bpc_localif);
1429 if (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr))
1430 sin6.sin6_scope_id = ifindex;
1431
1432 if (bpc->bpc_has_localif) {
1433 if (bp_bind_dev(sd, bpc->bpc_localif) != 0) {
1434 close(sd);
1435 return -1;
1436 }
1437 } else if (bpc->bpc_mhop && bpc->bpc_has_vrfname) {
1438 if (bp_bind_dev(sd, bpc->bpc_vrfname) != 0) {
1439 close(sd);
1440 return -1;
1441 }
1442 }
1443
1444 pcount = 0;
1445 do {
1446 if ((++pcount) > (BFD_SRCPORTMAX - BFD_SRCPORTINIT)) {
1447 /* Searched all ports, none available */
1448 log_error("ipv6-new: failed to bind port: %s",
1449 strerror(errno));
1450 close(sd);
1451 return -1;
1452 }
1453 if (srcPort >= BFD_SRCPORTMAX)
1454 srcPort = BFD_SRCPORTINIT;
1455 sin6.sin6_port = htons(srcPort++);
1456 } while (bind(sd, (struct sockaddr *)&sin6, sizeof(sin6)) < 0);
1457
1458 return sd;
1459 }
1460
1461 int bp_set_ttlv6(int sd, uint8_t value)
1462 {
1463 int ttl = value;
1464
1465 if (setsockopt(sd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl))
1466 == -1) {
1467 log_warning("set-ttlv6: setsockopt(IPV6_UNICAST_HOPS, %d): %s",
1468 value, strerror(errno));
1469 return -1;
1470 }
1471
1472 return 0;
1473 }
1474
1475 int bp_set_tosv6(int sd, uint8_t value)
1476 {
1477 int tos = value;
1478
1479 if (setsockopt(sd, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos))
1480 == -1) {
1481 log_warning("set-tosv6: setsockopt(IPV6_TCLASS, %d): %s", value,
1482 strerror(errno));
1483 return -1;
1484 }
1485
1486 return 0;
1487 }
1488
1489 static void bp_set_ipv6opts(int sd)
1490 {
1491 int ipv6_pktinfo = BFD_IPV6_PKT_INFO_VAL;
1492 int ipv6_only = BFD_IPV6_ONLY_VAL;
1493
1494 if (bp_set_ttlv6(sd, BFD_TTL_VAL) == -1)
1495 log_fatal("set-ipv6opts: setsockopt(IPV6_UNICAST_HOPS, %d): %s",
1496 BFD_TTL_VAL, strerror(errno));
1497
1498 if (setsockopt_ipv6_hoplimit(sd, BFD_RCV_TTL_VAL) == -1)
1499 log_fatal("set-ipv6opts: setsockopt(IPV6_HOPLIMIT, %d): %s",
1500 BFD_RCV_TTL_VAL, strerror(errno));
1501
1502 if (setsockopt_ipv6_pktinfo(sd, ipv6_pktinfo) == -1)
1503 log_fatal("set-ipv6opts: setsockopt(IPV6_PKTINFO, %d): %s",
1504 ipv6_pktinfo, strerror(errno));
1505
1506 if (setsockopt(sd, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6_only,
1507 sizeof(ipv6_only))
1508 == -1)
1509 log_fatal("set-ipv6opts: setsockopt(IPV6_V6ONLY, %d): %s",
1510 ipv6_only, strerror(errno));
1511 }
1512
1513 static void bp_bind_ipv6(int sd, uint16_t port)
1514 {
1515 struct sockaddr_in6 sin6;
1516
1517 memset(&sin6, 0, sizeof(sin6));
1518 sin6.sin6_family = AF_INET6;
1519 sin6.sin6_addr = in6addr_any;
1520 sin6.sin6_port = htons(port);
1521 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1522 sin6.sin6_len = sizeof(sin6);
1523 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1524 if (bind(sd, (struct sockaddr *)&sin6, sizeof(sin6)) == -1)
1525 log_fatal("bind-ipv6: bind: %s", strerror(errno));
1526 }
1527
1528 int bp_udp6_shop(void)
1529 {
1530 int sd;
1531
1532 sd = socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC);
1533 if (sd == -1)
1534 log_fatal("udp6-shop: socket: %s", strerror(errno));
1535
1536 bp_set_ipv6opts(sd);
1537 bp_bind_ipv6(sd, BFD_DEFDESTPORT);
1538
1539 return sd;
1540 }
1541
1542 int bp_udp6_mhop(void)
1543 {
1544 int sd;
1545
1546 sd = socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC);
1547 if (sd == -1)
1548 log_fatal("udp6-mhop: socket: %s", strerror(errno));
1549
1550 bp_set_ipv6opts(sd);
1551 bp_bind_ipv6(sd, BFD_DEF_MHOP_DEST_PORT);
1552
1553 return sd;
1554 }