]> git.proxmox.com Git - mirror_frr.git/blob - bfdd/bfd_packet.c
*: auto-convert to SPDX License IDs
[mirror_frr.git] / bfdd / bfd_packet.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*********************************************************************
3 * Copyright 2017 Cumulus Networks, Inc. All rights reserved.
4 *
5 * bfd_packet.c: implements the BFD protocol packet handling.
6 *
7 * Authors
8 * -------
9 * Shrijeet Mukherjee [shm@cumulusnetworks.com]
10 * Kanna Rajagopal [kanna@cumulusnetworks.com]
11 * Radhika Mahankali [Radhika@cumulusnetworks.com]
12 */
13
14 #include <zebra.h>
15
16 #ifdef BFD_LINUX
17 #include <linux/if_packet.h>
18 #endif /* BFD_LINUX */
19
20 #include <netinet/if_ether.h>
21 #include <netinet/udp.h>
22
23 #include "lib/sockopt.h"
24 #include "lib/checksum.h"
25 #include "lib/network.h"
26
27 #include "bfd.h"
28
29 /*
30 * Prototypes
31 */
32 static int ptm_bfd_process_echo_pkt(struct bfd_vrf_global *bvrf, int s);
33 int _ptm_bfd_send(struct bfd_session *bs, uint16_t *port, const void *data,
34 size_t datalen);
35
36 static void bfd_sd_reschedule(struct bfd_vrf_global *bvrf, int sd);
37 ssize_t bfd_recv_ipv4(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl,
38 ifindex_t *ifindex, struct sockaddr_any *local,
39 struct sockaddr_any *peer);
40 ssize_t bfd_recv_ipv6(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl,
41 ifindex_t *ifindex, struct sockaddr_any *local,
42 struct sockaddr_any *peer);
43 int bp_udp_send(int sd, uint8_t ttl, uint8_t *data, size_t datalen,
44 struct sockaddr *to, socklen_t tolen);
45 int bp_bfd_echo_in(struct bfd_vrf_global *bvrf, int sd, uint8_t *ttl,
46 uint32_t *my_discr, uint64_t *my_rtt);
47 #ifdef BFD_LINUX
48 ssize_t bfd_recv_ipv4_fp(int sd, uint8_t *msgbuf, size_t msgbuflen,
49 uint8_t *ttl, ifindex_t *ifindex,
50 struct sockaddr_any *local, struct sockaddr_any *peer);
51 void bfd_peer_mac_set(int sd, struct bfd_session *bfd,
52 struct sockaddr_any *peer, struct interface *ifp);
53 int bp_udp_send_fp(int sd, uint8_t *data, size_t datalen,
54 struct bfd_session *bfd);
55 ssize_t bfd_recv_fp_echo(int sd, uint8_t *msgbuf, size_t msgbuflen,
56 uint8_t *ttl, ifindex_t *ifindex,
57 struct sockaddr_any *local, struct sockaddr_any *peer);
58 #endif
59
60 /* socket related prototypes */
61 static void bp_set_ipopts(int sd);
62 static void bp_bind_ip(int sd, uint16_t port);
63 static void bp_set_ipv6opts(int sd);
64 static void bp_bind_ipv6(int sd, uint16_t port);
65
66
67 /*
68 * Functions
69 */
70 int _ptm_bfd_send(struct bfd_session *bs, uint16_t *port, const void *data,
71 size_t datalen)
72 {
73 struct sockaddr *sa;
74 struct sockaddr_in sin;
75 struct sockaddr_in6 sin6;
76 socklen_t slen;
77 ssize_t rv;
78 int sd = -1;
79
80 if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_IPV6)) {
81 memset(&sin6, 0, sizeof(sin6));
82 sin6.sin6_family = AF_INET6;
83 memcpy(&sin6.sin6_addr, &bs->key.peer, sizeof(sin6.sin6_addr));
84 if (bs->ifp && IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr))
85 sin6.sin6_scope_id = bs->ifp->ifindex;
86
87 sin6.sin6_port =
88 (port) ? *port
89 : (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH))
90 ? htons(BFD_DEF_MHOP_DEST_PORT)
91 : htons(BFD_DEFDESTPORT);
92
93 sd = bs->sock;
94 sa = (struct sockaddr *)&sin6;
95 slen = sizeof(sin6);
96 } else {
97 memset(&sin, 0, sizeof(sin));
98 sin.sin_family = AF_INET;
99 memcpy(&sin.sin_addr, &bs->key.peer, sizeof(sin.sin_addr));
100 sin.sin_port =
101 (port) ? *port
102 : (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH))
103 ? htons(BFD_DEF_MHOP_DEST_PORT)
104 : htons(BFD_DEFDESTPORT);
105
106 sd = bs->sock;
107 sa = (struct sockaddr *)&sin;
108 slen = sizeof(sin);
109 }
110
111 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
112 sa->sa_len = slen;
113 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
114 rv = sendto(sd, data, datalen, 0, sa, slen);
115 if (rv <= 0) {
116 if (bglobal.debug_network)
117 zlog_debug("packet-send: send failure: %s",
118 strerror(errno));
119 return -1;
120 }
121 if (rv < (ssize_t)datalen) {
122 if (bglobal.debug_network)
123 zlog_debug("packet-send: send partial: %s",
124 strerror(errno));
125 }
126
127 return 0;
128 }
129
130 #ifdef BFD_LINUX
131 /*
132 * Compute the UDP checksum.
133 *
134 * Checksum is not set in the packet, just computed.
135 *
136 * pkt
137 * Packet, fully filled out except for checksum field.
138 *
139 * pktsize
140 * sizeof(*pkt)
141 *
142 * ip
143 * IP address that pkt will be transmitted from and too.
144 *
145 * Returns:
146 * Checksum in network byte order.
147 */
148 static uint16_t bfd_pkt_checksum(struct udphdr *pkt, size_t pktsize,
149 struct in6_addr *ip, sa_family_t family)
150 {
151 uint16_t chksum;
152
153 pkt->check = 0;
154
155 if (family == AF_INET6) {
156 struct ipv6_ph ph = {};
157
158 memcpy(&ph.src, ip, sizeof(ph.src));
159 memcpy(&ph.dst, ip, sizeof(ph.dst));
160 ph.ulpl = htons(pktsize);
161 ph.next_hdr = IPPROTO_UDP;
162 chksum = in_cksum_with_ph6(&ph, pkt, pktsize);
163 } else {
164 struct ipv4_ph ph = {};
165
166 memcpy(&ph.src, ip, sizeof(ph.src));
167 memcpy(&ph.dst, ip, sizeof(ph.dst));
168 ph.proto = IPPROTO_UDP;
169 ph.len = htons(pktsize);
170 chksum = in_cksum_with_ph4(&ph, pkt, pktsize);
171 }
172
173 return chksum;
174 }
175
176 /*
177 * This routine creates the entire ECHO packet so that it will be looped
178 * in the forwarding plane of the peer router instead of going up the
179 * stack in BFD to be looped. If we haven't learned the peers MAC yet
180 * no echo is sent.
181 *
182 * echo packet with src/dst IP equal to local IP
183 * dest MAC as peer's MAC
184 *
185 * currently support ipv4
186 */
187 void ptm_bfd_echo_fp_snd(struct bfd_session *bfd)
188 {
189 int sd;
190 struct bfd_vrf_global *bvrf = bfd_vrf_look_by_session(bfd);
191 int total_len = 0;
192 struct ethhdr *eth;
193 struct udphdr *uh;
194 struct iphdr *iph;
195 struct bfd_echo_pkt *beph;
196 static char sendbuff[100];
197 struct timeval time_sent;
198
199 if (!bvrf)
200 return;
201 if (!CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_MAC_SET))
202 return;
203 if (!CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE))
204 SET_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE);
205
206 memset(sendbuff, 0, sizeof(sendbuff));
207
208 /* add eth hdr */
209 eth = (struct ethhdr *)(sendbuff);
210 memcpy(eth->h_source, bfd->ifp->hw_addr, sizeof(eth->h_source));
211 memcpy(eth->h_dest, bfd->peer_hw_addr, sizeof(eth->h_dest));
212
213 total_len += sizeof(struct ethhdr);
214
215 sd = bvrf->bg_echo;
216 eth->h_proto = htons(ETH_P_IP);
217
218 /* add ip hdr */
219 iph = (struct iphdr *)(sendbuff + sizeof(struct ethhdr));
220
221 iph->ihl = sizeof(struct ip) >> 2;
222 iph->version = IPVERSION;
223 iph->tos = IPTOS_PREC_INTERNETCONTROL;
224 iph->id = (uint16_t)frr_weak_random();
225 iph->ttl = BFD_TTL_VAL;
226 iph->protocol = IPPROTO_UDP;
227 memcpy(&iph->saddr, &bfd->local_address.sa_sin.sin_addr,
228 sizeof(bfd->local_address.sa_sin.sin_addr));
229 memcpy(&iph->daddr, &bfd->local_address.sa_sin.sin_addr,
230 sizeof(bfd->local_address.sa_sin.sin_addr));
231 total_len += sizeof(struct iphdr);
232
233 /* add udp hdr */
234 uh = (struct udphdr *)(sendbuff + sizeof(struct iphdr) +
235 sizeof(struct ethhdr));
236 uh->source = htons(BFD_DEF_ECHO_PORT);
237 uh->dest = htons(BFD_DEF_ECHO_PORT);
238
239 total_len += sizeof(struct udphdr);
240
241 /* add bfd echo */
242 beph = (struct bfd_echo_pkt *)(sendbuff + sizeof(struct udphdr) +
243 sizeof(struct iphdr) +
244 sizeof(struct ethhdr));
245
246 beph->ver = BFD_ECHO_VERSION;
247 beph->len = BFD_ECHO_PKT_LEN;
248 beph->my_discr = htonl(bfd->discrs.my_discr);
249
250 /* RTT calculation: add starting time in packet */
251 monotime(&time_sent);
252 beph->time_sent_sec = htobe64(time_sent.tv_sec);
253 beph->time_sent_usec = htobe64(time_sent.tv_usec);
254
255 total_len += sizeof(struct bfd_echo_pkt);
256 uh->len =
257 htons(total_len - sizeof(struct iphdr) - sizeof(struct ethhdr));
258 uh->check = bfd_pkt_checksum(
259 uh, (total_len - sizeof(struct iphdr) - sizeof(struct ethhdr)),
260 (struct in6_addr *)&iph->saddr, AF_INET);
261
262 iph->tot_len = htons(total_len - sizeof(struct ethhdr));
263 iph->check = in_cksum((const void *)iph, sizeof(struct iphdr));
264
265 if (bp_udp_send_fp(sd, (uint8_t *)&sendbuff, total_len, bfd) == -1)
266 return;
267
268 bfd->stats.tx_echo_pkt++;
269 }
270 #endif
271
272 void ptm_bfd_echo_snd(struct bfd_session *bfd)
273 {
274 struct sockaddr *sa;
275 socklen_t salen;
276 int sd;
277 struct bfd_echo_pkt bep;
278 struct sockaddr_in sin;
279 struct sockaddr_in6 sin6;
280 struct bfd_vrf_global *bvrf = bfd_vrf_look_by_session(bfd);
281
282 if (!bvrf)
283 return;
284 if (!CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE))
285 SET_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE);
286
287 memset(&bep, 0, sizeof(bep));
288 bep.ver = BFD_ECHO_VERSION;
289 bep.len = BFD_ECHO_PKT_LEN;
290 bep.my_discr = htonl(bfd->discrs.my_discr);
291
292 if (CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_IPV6)) {
293 if (bvrf->bg_echov6 == -1)
294 return;
295 sd = bvrf->bg_echov6;
296 memset(&sin6, 0, sizeof(sin6));
297 sin6.sin6_family = AF_INET6;
298 memcpy(&sin6.sin6_addr, &bfd->key.peer, sizeof(sin6.sin6_addr));
299 if (bfd->ifp && IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr))
300 sin6.sin6_scope_id = bfd->ifp->ifindex;
301
302 sin6.sin6_port = htons(BFD_DEF_ECHO_PORT);
303 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
304 sin6.sin6_len = sizeof(sin6);
305 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
306
307 sa = (struct sockaddr *)&sin6;
308 salen = sizeof(sin6);
309 } else {
310 sd = bvrf->bg_echo;
311 memset(&sin, 0, sizeof(sin));
312 sin.sin_family = AF_INET;
313 memcpy(&sin.sin_addr, &bfd->key.peer, sizeof(sin.sin_addr));
314 sin.sin_port = htons(BFD_DEF_ECHO_PORT);
315 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
316 sin.sin_len = sizeof(sin);
317 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
318
319 sa = (struct sockaddr *)&sin;
320 salen = sizeof(sin);
321 }
322 if (bp_udp_send(sd, BFD_TTL_VAL, (uint8_t *)&bep, sizeof(bep), sa,
323 salen)
324 == -1)
325 return;
326
327 bfd->stats.tx_echo_pkt++;
328 }
329
330 static int ptm_bfd_process_echo_pkt(struct bfd_vrf_global *bvrf, int s)
331 {
332 struct bfd_session *bfd;
333 uint32_t my_discr = 0;
334 uint64_t my_rtt = 0;
335 uint8_t ttl = 0;
336
337 /* Receive and parse echo packet. */
338 if (bp_bfd_echo_in(bvrf, s, &ttl, &my_discr, &my_rtt) == -1)
339 return 0;
340
341 /* Your discriminator not zero - use it to find session */
342 bfd = bfd_id_lookup(my_discr);
343 if (bfd == NULL) {
344 if (bglobal.debug_network)
345 zlog_debug("echo-packet: no matching session (id:%u)",
346 my_discr);
347 return -1;
348 }
349
350 if (!CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE)) {
351 if (bglobal.debug_network)
352 zlog_debug("echo-packet: echo disabled [%s] (id:%u)",
353 bs_to_string(bfd), my_discr);
354 return -1;
355 }
356
357 /* RTT Calculation: add current RTT to samples */
358 if (my_rtt != 0) {
359 bfd->rtt[bfd->rtt_index] = my_rtt;
360 bfd->rtt_index++;
361 if (bfd->rtt_index >= BFD_RTT_SAMPLE)
362 bfd->rtt_index = 0;
363 if (bfd->rtt_valid < BFD_RTT_SAMPLE)
364 bfd->rtt_valid++;
365 }
366
367 bfd->stats.rx_echo_pkt++;
368
369 /* Compute detect time */
370 bfd->echo_detect_TO = bfd->remote_detect_mult * bfd->echo_xmt_TO;
371
372 /* Update echo receive timeout. */
373 if (bfd->echo_detect_TO > 0)
374 bfd_echo_recvtimer_update(bfd);
375
376 return 0;
377 }
378
379 void ptm_bfd_snd(struct bfd_session *bfd, int fbit)
380 {
381 struct bfd_pkt cp = {};
382
383 /* Set fields according to section 6.5.7 */
384 cp.diag = bfd->local_diag;
385 BFD_SETVER(cp.diag, BFD_VERSION);
386 cp.flags = 0;
387 BFD_SETSTATE(cp.flags, bfd->ses_state);
388
389 if (CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_CBIT))
390 BFD_SETCBIT(cp.flags, BFD_CBIT);
391
392 BFD_SETDEMANDBIT(cp.flags, BFD_DEF_DEMAND);
393
394 /*
395 * Polling and Final can't be set at the same time.
396 *
397 * RFC 5880, Section 6.5.
398 */
399 BFD_SETFBIT(cp.flags, fbit);
400 if (fbit == 0)
401 BFD_SETPBIT(cp.flags, bfd->polling);
402
403 cp.detect_mult = bfd->detect_mult;
404 cp.len = BFD_PKT_LEN;
405 cp.discrs.my_discr = htonl(bfd->discrs.my_discr);
406 cp.discrs.remote_discr = htonl(bfd->discrs.remote_discr);
407 if (bfd->polling) {
408 cp.timers.desired_min_tx =
409 htonl(bfd->timers.desired_min_tx);
410 cp.timers.required_min_rx =
411 htonl(bfd->timers.required_min_rx);
412 } else {
413 /*
414 * We can only announce current setting on poll, this
415 * avoids timing mismatch with our peer and give it
416 * the oportunity to learn. See `bs_final_handler` for
417 * more information.
418 */
419 cp.timers.desired_min_tx =
420 htonl(bfd->cur_timers.desired_min_tx);
421 cp.timers.required_min_rx =
422 htonl(bfd->cur_timers.required_min_rx);
423 }
424 cp.timers.required_min_echo = htonl(bfd->timers.required_min_echo_rx);
425
426 if (_ptm_bfd_send(bfd, NULL, &cp, BFD_PKT_LEN) != 0)
427 return;
428
429 bfd->stats.tx_ctrl_pkt++;
430 }
431
432 #ifdef BFD_LINUX
433 /*
434 * receive the ipv4 echo packet that was loopback in the peers forwarding plane
435 */
436 ssize_t bfd_recv_ipv4_fp(int sd, uint8_t *msgbuf, size_t msgbuflen,
437 uint8_t *ttl, ifindex_t *ifindex,
438 struct sockaddr_any *local, struct sockaddr_any *peer)
439 {
440 ssize_t mlen;
441 struct sockaddr_ll msgaddr;
442 struct msghdr msghdr;
443 struct iovec iov[1];
444 uint16_t recv_checksum;
445 uint16_t checksum;
446 struct iphdr *ip;
447 struct udphdr *uh;
448
449 /* Prepare the recvmsg params. */
450 iov[0].iov_base = msgbuf;
451 iov[0].iov_len = msgbuflen;
452
453 memset(&msghdr, 0, sizeof(msghdr));
454 msghdr.msg_name = &msgaddr;
455 msghdr.msg_namelen = sizeof(msgaddr);
456 msghdr.msg_iov = iov;
457 msghdr.msg_iovlen = 1;
458
459 mlen = recvmsg(sd, &msghdr, MSG_DONTWAIT);
460 if (mlen == -1) {
461 if (errno != EAGAIN || errno != EWOULDBLOCK || errno != EINTR)
462 zlog_err("%s: recv failed: %s", __func__,
463 strerror(errno));
464
465 return -1;
466 }
467
468 ip = (struct iphdr *)(msgbuf + sizeof(struct ethhdr));
469
470 /* verify ip checksum */
471 recv_checksum = ip->check;
472 ip->check = 0;
473 checksum = in_cksum((const void *)ip, sizeof(struct iphdr));
474 if (recv_checksum != checksum) {
475 if (bglobal.debug_network)
476 zlog_debug(
477 "%s: invalid iphdr checksum expected 0x%x rcvd 0x%x",
478 __func__, checksum, recv_checksum);
479 return -1;
480 }
481
482 *ttl = ip->ttl;
483 if (*ttl != 254) {
484 /* Echo should be looped in peer's forwarding plane, but it also
485 * comes up to BFD so silently drop it
486 */
487 if (ip->daddr == ip->saddr)
488 return -1;
489
490 if (bglobal.debug_network)
491 zlog_debug("%s: invalid TTL: %u", __func__, *ttl);
492 return -1;
493 }
494
495 local->sa_sin.sin_family = AF_INET;
496 memcpy(&local->sa_sin.sin_addr, &ip->saddr, sizeof(ip->saddr));
497 peer->sa_sin.sin_family = AF_INET;
498 memcpy(&peer->sa_sin.sin_addr, &ip->daddr, sizeof(ip->daddr));
499
500 *ifindex = msgaddr.sll_ifindex;
501
502 /* verify udp checksum */
503 uh = (struct udphdr *)(msgbuf + sizeof(struct iphdr) +
504 sizeof(struct ethhdr));
505 recv_checksum = uh->check;
506 uh->check = 0;
507 checksum = bfd_pkt_checksum(uh, ntohs(uh->len),
508 (struct in6_addr *)&ip->saddr, AF_INET);
509 if (recv_checksum != checksum) {
510 if (bglobal.debug_network)
511 zlog_debug(
512 "%s: invalid udphdr checksum expected 0x%x rcvd 0x%x",
513 __func__, checksum, recv_checksum);
514 return -1;
515 }
516 return mlen;
517 }
518 #endif
519
520 ssize_t bfd_recv_ipv4(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl,
521 ifindex_t *ifindex, struct sockaddr_any *local,
522 struct sockaddr_any *peer)
523 {
524 struct cmsghdr *cm;
525 ssize_t mlen;
526 struct sockaddr_in msgaddr;
527 struct msghdr msghdr;
528 struct iovec iov[1];
529 uint8_t cmsgbuf[255];
530
531 /* Prepare the recvmsg params. */
532 iov[0].iov_base = msgbuf;
533 iov[0].iov_len = msgbuflen;
534
535 memset(&msghdr, 0, sizeof(msghdr));
536 msghdr.msg_name = &msgaddr;
537 msghdr.msg_namelen = sizeof(msgaddr);
538 msghdr.msg_iov = iov;
539 msghdr.msg_iovlen = 1;
540 msghdr.msg_control = cmsgbuf;
541 msghdr.msg_controllen = sizeof(cmsgbuf);
542
543 mlen = recvmsg(sd, &msghdr, MSG_DONTWAIT);
544 if (mlen == -1) {
545 if (errno != EAGAIN)
546 zlog_err("ipv4-recv: recv failed: %s", strerror(errno));
547
548 return -1;
549 }
550
551 /* Get source address */
552 peer->sa_sin = *((struct sockaddr_in *)(msghdr.msg_name));
553
554 /* Get and check TTL */
555 for (cm = CMSG_FIRSTHDR(&msghdr); cm != NULL;
556 cm = CMSG_NXTHDR(&msghdr, cm)) {
557 if (cm->cmsg_level != IPPROTO_IP)
558 continue;
559
560 switch (cm->cmsg_type) {
561 #ifdef BFD_LINUX
562 case IP_TTL: {
563 uint32_t ttlval;
564
565 memcpy(&ttlval, CMSG_DATA(cm), sizeof(ttlval));
566 if (ttlval > 255) {
567 if (bglobal.debug_network)
568 zlog_debug("%s: invalid TTL: %u",
569 __func__, ttlval);
570 return -1;
571 }
572 *ttl = ttlval;
573 break;
574 }
575
576 case IP_PKTINFO: {
577 struct in_pktinfo *pi =
578 (struct in_pktinfo *)CMSG_DATA(cm);
579
580 if (pi == NULL)
581 break;
582
583 local->sa_sin.sin_family = AF_INET;
584 local->sa_sin.sin_addr = pi->ipi_addr;
585 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
586 local->sa_sin.sin_len = sizeof(local->sa_sin);
587 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
588
589 *ifindex = pi->ipi_ifindex;
590 break;
591 }
592 #endif /* BFD_LINUX */
593 #ifdef BFD_BSD
594 case IP_RECVTTL: {
595 memcpy(ttl, CMSG_DATA(cm), sizeof(*ttl));
596 break;
597 }
598
599 case IP_RECVDSTADDR: {
600 struct in_addr ia;
601
602 memcpy(&ia, CMSG_DATA(cm), sizeof(ia));
603 local->sa_sin.sin_family = AF_INET;
604 local->sa_sin.sin_addr = ia;
605 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
606 local->sa_sin.sin_len = sizeof(local->sa_sin);
607 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
608 break;
609 }
610 #endif /* BFD_BSD */
611
612 default:
613 /*
614 * On *BSDs we expect to land here when skipping
615 * the IP_RECVIF header. It will be handled by
616 * getsockopt_ifindex() below.
617 */
618 /* NOTHING */
619 break;
620 }
621 }
622
623 /* OS agnostic way of getting interface name. */
624 if (*ifindex == IFINDEX_INTERNAL)
625 *ifindex = getsockopt_ifindex(AF_INET, &msghdr);
626
627 return mlen;
628 }
629
630 ssize_t bfd_recv_ipv6(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl,
631 ifindex_t *ifindex, struct sockaddr_any *local,
632 struct sockaddr_any *peer)
633 {
634 struct cmsghdr *cm;
635 struct in6_pktinfo *pi6 = NULL;
636 ssize_t mlen;
637 uint32_t ttlval;
638 struct sockaddr_in6 msgaddr6;
639 struct msghdr msghdr6;
640 struct iovec iov[1];
641 uint8_t cmsgbuf6[255];
642
643 /* Prepare the recvmsg params. */
644 iov[0].iov_base = msgbuf;
645 iov[0].iov_len = msgbuflen;
646
647 memset(&msghdr6, 0, sizeof(msghdr6));
648 msghdr6.msg_name = &msgaddr6;
649 msghdr6.msg_namelen = sizeof(msgaddr6);
650 msghdr6.msg_iov = iov;
651 msghdr6.msg_iovlen = 1;
652 msghdr6.msg_control = cmsgbuf6;
653 msghdr6.msg_controllen = sizeof(cmsgbuf6);
654
655 mlen = recvmsg(sd, &msghdr6, MSG_DONTWAIT);
656 if (mlen == -1) {
657 if (errno != EAGAIN)
658 zlog_err("ipv6-recv: recv failed: %s", strerror(errno));
659
660 return -1;
661 }
662
663 /* Get source address */
664 peer->sa_sin6 = *((struct sockaddr_in6 *)(msghdr6.msg_name));
665
666 /* Get and check TTL */
667 for (cm = CMSG_FIRSTHDR(&msghdr6); cm != NULL;
668 cm = CMSG_NXTHDR(&msghdr6, cm)) {
669 if (cm->cmsg_level != IPPROTO_IPV6)
670 continue;
671
672 if (cm->cmsg_type == IPV6_HOPLIMIT) {
673 memcpy(&ttlval, CMSG_DATA(cm), sizeof(ttlval));
674 if (ttlval > 255) {
675 if (bglobal.debug_network)
676 zlog_debug("%s: invalid TTL: %u",
677 __func__, ttlval);
678 return -1;
679 }
680
681 *ttl = ttlval;
682 } else if (cm->cmsg_type == IPV6_PKTINFO) {
683 pi6 = (struct in6_pktinfo *)CMSG_DATA(cm);
684 if (pi6) {
685 local->sa_sin6.sin6_family = AF_INET6;
686 local->sa_sin6.sin6_addr = pi6->ipi6_addr;
687 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
688 local->sa_sin6.sin6_len = sizeof(local->sa_sin6);
689 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
690
691 *ifindex = pi6->ipi6_ifindex;
692
693 /* Set scope ID for link local addresses. */
694 if (IN6_IS_ADDR_LINKLOCAL(
695 &peer->sa_sin6.sin6_addr))
696 peer->sa_sin6.sin6_scope_id = *ifindex;
697 if (IN6_IS_ADDR_LINKLOCAL(
698 &local->sa_sin6.sin6_addr))
699 local->sa_sin6.sin6_scope_id = *ifindex;
700 }
701 }
702 }
703
704 return mlen;
705 }
706
707 static void bfd_sd_reschedule(struct bfd_vrf_global *bvrf, int sd)
708 {
709 if (sd == bvrf->bg_shop) {
710 THREAD_OFF(bvrf->bg_ev[0]);
711 thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_shop,
712 &bvrf->bg_ev[0]);
713 } else if (sd == bvrf->bg_mhop) {
714 THREAD_OFF(bvrf->bg_ev[1]);
715 thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_mhop,
716 &bvrf->bg_ev[1]);
717 } else if (sd == bvrf->bg_shop6) {
718 THREAD_OFF(bvrf->bg_ev[2]);
719 thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_shop6,
720 &bvrf->bg_ev[2]);
721 } else if (sd == bvrf->bg_mhop6) {
722 THREAD_OFF(bvrf->bg_ev[3]);
723 thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_mhop6,
724 &bvrf->bg_ev[3]);
725 } else if (sd == bvrf->bg_echo) {
726 THREAD_OFF(bvrf->bg_ev[4]);
727 thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_echo,
728 &bvrf->bg_ev[4]);
729 } else if (sd == bvrf->bg_echov6) {
730 THREAD_OFF(bvrf->bg_ev[5]);
731 thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_echov6,
732 &bvrf->bg_ev[5]);
733 }
734 }
735
736 PRINTFRR(6, 7)
737 static void cp_debug(bool mhop, struct sockaddr_any *peer,
738 struct sockaddr_any *local, ifindex_t ifindex,
739 vrf_id_t vrfid, const char *fmt, ...)
740 {
741 char buf[512], peerstr[128], localstr[128], portstr[64], vrfstr[64];
742 va_list vl;
743
744 /* Don't to any processing if debug is disabled. */
745 if (bglobal.debug_network == false)
746 return;
747
748 if (peer->sa_sin.sin_family)
749 snprintf(peerstr, sizeof(peerstr), " peer:%s", satostr(peer));
750 else
751 peerstr[0] = 0;
752
753 if (local->sa_sin.sin_family)
754 snprintf(localstr, sizeof(localstr), " local:%s",
755 satostr(local));
756 else
757 localstr[0] = 0;
758
759 if (ifindex != IFINDEX_INTERNAL)
760 snprintf(portstr, sizeof(portstr), " port:%u", ifindex);
761 else
762 portstr[0] = 0;
763
764 if (vrfid != VRF_DEFAULT)
765 snprintf(vrfstr, sizeof(vrfstr), " vrf:%u", vrfid);
766 else
767 vrfstr[0] = 0;
768
769 va_start(vl, fmt);
770 vsnprintf(buf, sizeof(buf), fmt, vl);
771 va_end(vl);
772
773 zlog_debug("control-packet: %s [mhop:%s%s%s%s%s]", buf,
774 mhop ? "yes" : "no", peerstr, localstr, portstr, vrfstr);
775 }
776
777 void bfd_recv_cb(struct thread *t)
778 {
779 int sd = THREAD_FD(t);
780 struct bfd_session *bfd;
781 struct bfd_pkt *cp;
782 bool is_mhop;
783 ssize_t mlen = 0;
784 uint8_t ttl = 0;
785 vrf_id_t vrfid;
786 ifindex_t ifindex = IFINDEX_INTERNAL;
787 struct sockaddr_any local, peer;
788 uint8_t msgbuf[1516];
789 struct interface *ifp = NULL;
790 struct bfd_vrf_global *bvrf = THREAD_ARG(t);
791
792 /* Schedule next read. */
793 bfd_sd_reschedule(bvrf, sd);
794
795 /* Handle echo packets. */
796 if (sd == bvrf->bg_echo || sd == bvrf->bg_echov6) {
797 ptm_bfd_process_echo_pkt(bvrf, sd);
798 return;
799 }
800
801 /* Sanitize input/output. */
802 memset(&local, 0, sizeof(local));
803 memset(&peer, 0, sizeof(peer));
804
805 /* Handle control packets. */
806 is_mhop = false;
807 if (sd == bvrf->bg_shop || sd == bvrf->bg_mhop) {
808 is_mhop = sd == bvrf->bg_mhop;
809 mlen = bfd_recv_ipv4(sd, msgbuf, sizeof(msgbuf), &ttl, &ifindex,
810 &local, &peer);
811 } else if (sd == bvrf->bg_shop6 || sd == bvrf->bg_mhop6) {
812 is_mhop = sd == bvrf->bg_mhop6;
813 mlen = bfd_recv_ipv6(sd, msgbuf, sizeof(msgbuf), &ttl, &ifindex,
814 &local, &peer);
815 }
816
817 /*
818 * With netns backend, we have a separate socket in each VRF. It means
819 * that bvrf here is correct and we believe the bvrf->vrf->vrf_id.
820 * With VRF-lite backend, we have a single socket in the default VRF.
821 * It means that we can't believe the bvrf->vrf->vrf_id. But in
822 * VRF-lite, the ifindex is globally unique, so we can retrieve the
823 * correct vrf_id from the interface.
824 */
825 vrfid = bvrf->vrf->vrf_id;
826 if (ifindex) {
827 ifp = if_lookup_by_index(ifindex, vrfid);
828 if (ifp)
829 vrfid = ifp->vrf->vrf_id;
830 }
831
832 /* Implement RFC 5880 6.8.6 */
833 if (mlen < BFD_PKT_LEN) {
834 cp_debug(is_mhop, &peer, &local, ifindex, vrfid,
835 "too small (%zd bytes)", mlen);
836 return;
837 }
838
839 /* Validate single hop packet TTL. */
840 if ((!is_mhop) && (ttl != BFD_TTL_VAL)) {
841 cp_debug(is_mhop, &peer, &local, ifindex, vrfid,
842 "invalid TTL: %d expected %d", ttl, BFD_TTL_VAL);
843 return;
844 }
845
846 /*
847 * Parse the control header for inconsistencies:
848 * - Invalid version;
849 * - Bad multiplier configuration;
850 * - Short packets;
851 * - Invalid discriminator;
852 */
853 cp = (struct bfd_pkt *)(msgbuf);
854 if (BFD_GETVER(cp->diag) != BFD_VERSION) {
855 cp_debug(is_mhop, &peer, &local, ifindex, vrfid,
856 "bad version %d", BFD_GETVER(cp->diag));
857 return;
858 }
859
860 if (cp->detect_mult == 0) {
861 cp_debug(is_mhop, &peer, &local, ifindex, vrfid,
862 "detect multiplier set to zero");
863 return;
864 }
865
866 if ((cp->len < BFD_PKT_LEN) || (cp->len > mlen)) {
867 cp_debug(is_mhop, &peer, &local, ifindex, vrfid, "too small");
868 return;
869 }
870
871 if (cp->discrs.my_discr == 0) {
872 cp_debug(is_mhop, &peer, &local, ifindex, vrfid,
873 "'my discriminator' is zero");
874 return;
875 }
876
877 /* Find the session that this packet belongs. */
878 bfd = ptm_bfd_sess_find(cp, &peer, &local, ifp, vrfid, is_mhop);
879 if (bfd == NULL) {
880 cp_debug(is_mhop, &peer, &local, ifindex, vrfid,
881 "no session found");
882 return;
883 }
884 /*
885 * We may have a situation where received packet is on wrong vrf
886 */
887 if (bfd && bfd->vrf && bfd->vrf != bvrf->vrf) {
888 cp_debug(is_mhop, &peer, &local, ifindex, vrfid,
889 "wrong vrfid.");
890 return;
891 }
892
893 /* Ensure that existing good sessions are not overridden. */
894 if (!cp->discrs.remote_discr && bfd->ses_state != PTM_BFD_DOWN &&
895 bfd->ses_state != PTM_BFD_ADM_DOWN) {
896 cp_debug(is_mhop, &peer, &local, ifindex, vrfid,
897 "'remote discriminator' is zero, not overridden");
898 return;
899 }
900
901 /*
902 * Multi hop: validate packet TTL.
903 * Single hop: set local address that received the packet.
904 * set peers mac address for echo packets
905 */
906 if (is_mhop) {
907 if (ttl < bfd->mh_ttl) {
908 cp_debug(is_mhop, &peer, &local, ifindex, vrfid,
909 "exceeded max hop count (expected %d, got %d)",
910 bfd->mh_ttl, ttl);
911 return;
912 }
913 } else {
914
915 if (bfd->local_address.sa_sin.sin_family == AF_UNSPEC)
916 bfd->local_address = local;
917 #ifdef BFD_LINUX
918 if (ifp)
919 bfd_peer_mac_set(sd, bfd, &peer, ifp);
920 #endif
921 }
922
923 bfd->stats.rx_ctrl_pkt++;
924
925 /*
926 * If no interface was detected, save the interface where the
927 * packet came in.
928 */
929 if (!is_mhop && bfd->ifp == NULL)
930 bfd->ifp = ifp;
931
932 /* Log remote discriminator changes. */
933 if ((bfd->discrs.remote_discr != 0)
934 && (bfd->discrs.remote_discr != ntohl(cp->discrs.my_discr)))
935 cp_debug(is_mhop, &peer, &local, ifindex, vrfid,
936 "remote discriminator mismatch (expected %u, got %u)",
937 bfd->discrs.remote_discr, ntohl(cp->discrs.my_discr));
938
939 bfd->discrs.remote_discr = ntohl(cp->discrs.my_discr);
940
941 /* Save remote diagnostics before state switch. */
942 bfd->remote_diag = cp->diag & BFD_DIAGMASK;
943
944 /* Update remote timers settings. */
945 bfd->remote_timers.desired_min_tx = ntohl(cp->timers.desired_min_tx);
946 bfd->remote_timers.required_min_rx = ntohl(cp->timers.required_min_rx);
947 bfd->remote_timers.required_min_echo =
948 ntohl(cp->timers.required_min_echo);
949 bfd->remote_detect_mult = cp->detect_mult;
950
951 if (BFD_GETCBIT(cp->flags))
952 bfd->remote_cbit = 1;
953 else
954 bfd->remote_cbit = 0;
955
956 /* State switch from section 6.2. */
957 bs_state_handler(bfd, BFD_GETSTATE(cp->flags));
958
959 /* RFC 5880, Section 6.5: handle POLL/FINAL negotiation sequence. */
960 if (bfd->polling && BFD_GETFBIT(cp->flags)) {
961 /* Disable polling. */
962 bfd->polling = 0;
963
964 /* Handle poll finalization. */
965 bs_final_handler(bfd);
966 }
967
968 /*
969 * Detection timeout calculation:
970 * The minimum detection timeout is the remote detection
971 * multipler (number of packets to be missed) times the agreed
972 * transmission interval.
973 *
974 * RFC 5880, Section 6.8.4.
975 */
976 if (bfd->cur_timers.required_min_rx > bfd->remote_timers.desired_min_tx)
977 bfd->detect_TO = bfd->remote_detect_mult
978 * bfd->cur_timers.required_min_rx;
979 else
980 bfd->detect_TO = bfd->remote_detect_mult
981 * bfd->remote_timers.desired_min_tx;
982
983 /* Apply new receive timer immediately. */
984 bfd_recvtimer_update(bfd);
985
986 /* Handle echo timers changes. */
987 bs_echo_timer_handler(bfd);
988
989 /*
990 * We've received a packet with the POLL bit set, we must send
991 * a control packet back with the FINAL bit set.
992 *
993 * RFC 5880, Section 6.5.
994 */
995 if (BFD_GETPBIT(cp->flags)) {
996 /* We are finalizing a poll negotiation. */
997 bs_final_handler(bfd);
998
999 /* Send the control packet with the final bit immediately. */
1000 ptm_bfd_snd(bfd, 1);
1001 }
1002 }
1003
1004 /*
1005 * bp_bfd_echo_in: proccesses an BFD echo packet. On TTL == BFD_TTL_VAL
1006 * the packet is looped back or returns the my discriminator ID along
1007 * with the TTL.
1008 *
1009 * Returns -1 on error or loopback or 0 on success.
1010 */
1011 int bp_bfd_echo_in(struct bfd_vrf_global *bvrf, int sd, uint8_t *ttl,
1012 uint32_t *my_discr, uint64_t *my_rtt)
1013 {
1014 struct bfd_echo_pkt *bep;
1015 ssize_t rlen;
1016 struct sockaddr_any local, peer;
1017 ifindex_t ifindex = IFINDEX_INTERNAL;
1018 vrf_id_t vrfid = VRF_DEFAULT;
1019 uint8_t msgbuf[1516];
1020 size_t bfd_offset = 0;
1021
1022 if (sd == bvrf->bg_echo) {
1023 #ifdef BFD_LINUX
1024 rlen = bfd_recv_ipv4_fp(sd, msgbuf, sizeof(msgbuf), ttl,
1025 &ifindex, &local, &peer);
1026
1027 /* silently drop echo packet that is looped in fastpath but
1028 * still comes up to BFD
1029 */
1030 if (rlen == -1)
1031 return -1;
1032 bfd_offset = sizeof(struct udphdr) + sizeof(struct iphdr) +
1033 sizeof(struct ethhdr);
1034 #else
1035 rlen = bfd_recv_ipv4(sd, msgbuf, sizeof(msgbuf), ttl, &ifindex,
1036 &local, &peer);
1037 bfd_offset = 0;
1038 #endif
1039 } else {
1040 rlen = bfd_recv_ipv6(sd, msgbuf, sizeof(msgbuf), ttl, &ifindex,
1041 &local, &peer);
1042 bfd_offset = 0;
1043 }
1044
1045 /* Short packet, better not risk reading it. */
1046 if (rlen < (ssize_t)sizeof(*bep)) {
1047 cp_debug(false, &peer, &local, ifindex, vrfid,
1048 "small echo packet");
1049 return -1;
1050 }
1051
1052 /* Test for loopback for ipv6, ipv4 is looped in forwarding plane */
1053 if ((*ttl == BFD_TTL_VAL) && (sd == bvrf->bg_echov6)) {
1054 bp_udp_send(sd, *ttl - 1, msgbuf, rlen,
1055 (struct sockaddr *)&peer,
1056 (sd == bvrf->bg_echo) ? sizeof(peer.sa_sin)
1057 : sizeof(peer.sa_sin6));
1058 return -1;
1059 }
1060
1061 /* Read my discriminator from BFD Echo packet. */
1062 bep = (struct bfd_echo_pkt *)(msgbuf + bfd_offset);
1063 *my_discr = ntohl(bep->my_discr);
1064 if (*my_discr == 0) {
1065 cp_debug(false, &peer, &local, ifindex, vrfid,
1066 "invalid echo packet discriminator (zero)");
1067 return -1;
1068 }
1069
1070 #ifdef BFD_LINUX
1071 /* RTT Calculation: determine RTT time of IPv4 echo pkt */
1072 if (sd == bvrf->bg_echo) {
1073 struct timeval time_sent = {0, 0};
1074
1075 time_sent.tv_sec = be64toh(bep->time_sent_sec);
1076 time_sent.tv_usec = be64toh(bep->time_sent_usec);
1077 *my_rtt = monotime_since(&time_sent, NULL);
1078 }
1079 #endif
1080
1081 return 0;
1082 }
1083
1084 #ifdef BFD_LINUX
1085 /*
1086 * send a bfd packet with src/dst same IP so that the peer will receive
1087 * the packet and forward it back to sender in the forwarding plane
1088 */
1089 int bp_udp_send_fp(int sd, uint8_t *data, size_t datalen,
1090 struct bfd_session *bfd)
1091 {
1092 ssize_t wlen;
1093 struct msghdr msg = {0};
1094 struct iovec iov[1];
1095 uint8_t msgctl[255];
1096 struct sockaddr_ll sadr_ll = {0};
1097
1098 sadr_ll.sll_ifindex = bfd->ifp->ifindex;
1099 sadr_ll.sll_halen = ETH_ALEN;
1100 memcpy(sadr_ll.sll_addr, bfd->peer_hw_addr, sizeof(bfd->peer_hw_addr));
1101 sadr_ll.sll_protocol = htons(ETH_P_IP);
1102
1103 /* Prepare message data. */
1104 iov[0].iov_base = data;
1105 iov[0].iov_len = datalen;
1106
1107 memset(msgctl, 0, sizeof(msgctl));
1108 msg.msg_name = &sadr_ll;
1109 msg.msg_namelen = sizeof(sadr_ll);
1110 msg.msg_iov = iov;
1111 msg.msg_iovlen = 1;
1112
1113 /* Send echo to peer */
1114 wlen = sendmsg(sd, &msg, 0);
1115
1116 if (wlen <= 0) {
1117 if (bglobal.debug_network)
1118 zlog_debug("%s: loopback failure: (%d) %s", __func__,
1119 errno, strerror(errno));
1120 return -1;
1121 } else if (wlen < (ssize_t)datalen) {
1122 if (bglobal.debug_network)
1123 zlog_debug("%s: partial send: %zd expected %zu",
1124 __func__, wlen, datalen);
1125 return -1;
1126 }
1127
1128 return 0;
1129 }
1130 #endif
1131
1132 int bp_udp_send(int sd, uint8_t ttl, uint8_t *data, size_t datalen,
1133 struct sockaddr *to, socklen_t tolen)
1134 {
1135 struct cmsghdr *cmsg;
1136 ssize_t wlen;
1137 int ttlval = ttl;
1138 bool is_ipv6 = to->sa_family == AF_INET6;
1139 struct msghdr msg;
1140 struct iovec iov[1];
1141 uint8_t msgctl[255];
1142
1143 /* Prepare message data. */
1144 iov[0].iov_base = data;
1145 iov[0].iov_len = datalen;
1146
1147 memset(&msg, 0, sizeof(msg));
1148 memset(msgctl, 0, sizeof(msgctl));
1149 msg.msg_name = to;
1150 msg.msg_namelen = tolen;
1151 msg.msg_iov = iov;
1152 msg.msg_iovlen = 1;
1153
1154 /* Prepare the packet TTL information. */
1155 if (ttl > 0) {
1156 /* Use ancillary data. */
1157 msg.msg_control = msgctl;
1158 msg.msg_controllen = CMSG_LEN(sizeof(ttlval));
1159
1160 /* Configure the ancillary data. */
1161 cmsg = CMSG_FIRSTHDR(&msg);
1162 cmsg->cmsg_len = CMSG_LEN(sizeof(ttlval));
1163 if (is_ipv6) {
1164 cmsg->cmsg_level = IPPROTO_IPV6;
1165 cmsg->cmsg_type = IPV6_HOPLIMIT;
1166 } else {
1167 #ifdef BFD_LINUX
1168 cmsg->cmsg_level = IPPROTO_IP;
1169 cmsg->cmsg_type = IP_TTL;
1170 #else
1171 /* FreeBSD does not support TTL in ancillary data. */
1172 msg.msg_control = NULL;
1173 msg.msg_controllen = 0;
1174
1175 bp_set_ttl(sd, ttl);
1176 #endif /* BFD_BSD */
1177 }
1178 memcpy(CMSG_DATA(cmsg), &ttlval, sizeof(ttlval));
1179 }
1180
1181 /* Send echo back. */
1182 wlen = sendmsg(sd, &msg, 0);
1183 if (wlen <= 0) {
1184 if (bglobal.debug_network)
1185 zlog_debug("%s: loopback failure: (%d) %s", __func__,
1186 errno, strerror(errno));
1187 return -1;
1188 } else if (wlen < (ssize_t)datalen) {
1189 if (bglobal.debug_network)
1190 zlog_debug("%s: partial send: %zd expected %zu",
1191 __func__, wlen, datalen);
1192 return -1;
1193 }
1194
1195 return 0;
1196 }
1197
1198
1199 /*
1200 * Sockets creation.
1201 */
1202
1203
1204 /*
1205 * IPv4 sockets
1206 */
1207 int bp_set_ttl(int sd, uint8_t value)
1208 {
1209 int ttl = value;
1210
1211 if (setsockopt(sd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)) == -1) {
1212 zlog_warn("%s: setsockopt(IP_TTL, %d): %s", __func__, value,
1213 strerror(errno));
1214 return -1;
1215 }
1216
1217 return 0;
1218 }
1219
1220 int bp_set_tos(int sd, uint8_t value)
1221 {
1222 int tos = value;
1223
1224 if (setsockopt(sd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) == -1) {
1225 zlog_warn("%s: setsockopt(IP_TOS, %d): %s", __func__, value,
1226 strerror(errno));
1227 return -1;
1228 }
1229
1230 return 0;
1231 }
1232
1233 static bool bp_set_reuse_addr(int sd)
1234 {
1235 int one = 1;
1236
1237 if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) == -1) {
1238 zlog_warn("%s: setsockopt(SO_REUSEADDR, %d): %s", __func__, one,
1239 strerror(errno));
1240 return false;
1241 }
1242 return true;
1243 }
1244
1245 static bool bp_set_reuse_port(int sd)
1246 {
1247 int one = 1;
1248
1249 if (setsockopt(sd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)) == -1) {
1250 zlog_warn("%s: setsockopt(SO_REUSEPORT, %d): %s", __func__, one,
1251 strerror(errno));
1252 return false;
1253 }
1254 return true;
1255 }
1256
1257
1258 static void bp_set_ipopts(int sd)
1259 {
1260 int rcvttl = BFD_RCV_TTL_VAL;
1261
1262 if (!bp_set_reuse_addr(sd))
1263 zlog_fatal("set-reuse-addr: failed");
1264
1265 if (!bp_set_reuse_port(sd))
1266 zlog_fatal("set-reuse-port: failed");
1267
1268 if (bp_set_ttl(sd, BFD_TTL_VAL) != 0)
1269 zlog_fatal("set-ipopts: TTL configuration failed");
1270
1271 if (setsockopt(sd, IPPROTO_IP, IP_RECVTTL, &rcvttl, sizeof(rcvttl))
1272 == -1)
1273 zlog_fatal("set-ipopts: setsockopt(IP_RECVTTL, %d): %s", rcvttl,
1274 strerror(errno));
1275
1276 #ifdef BFD_LINUX
1277 int pktinfo = BFD_PKT_INFO_VAL;
1278
1279 /* Figure out address and interface to do the peer matching. */
1280 if (setsockopt(sd, IPPROTO_IP, IP_PKTINFO, &pktinfo, sizeof(pktinfo))
1281 == -1)
1282 zlog_fatal("set-ipopts: setsockopt(IP_PKTINFO, %d): %s",
1283 pktinfo, strerror(errno));
1284 #endif /* BFD_LINUX */
1285 #ifdef BFD_BSD
1286 int yes = 1;
1287
1288 /* Find out our address for peer matching. */
1289 if (setsockopt(sd, IPPROTO_IP, IP_RECVDSTADDR, &yes, sizeof(yes)) == -1)
1290 zlog_fatal("set-ipopts: setsockopt(IP_RECVDSTADDR, %d): %s",
1291 yes, strerror(errno));
1292
1293 /* Find out interface where the packet came in. */
1294 if (setsockopt_ifindex(AF_INET, sd, yes) == -1)
1295 zlog_fatal("set-ipopts: setsockopt_ipv4_ifindex(%d): %s", yes,
1296 strerror(errno));
1297 #endif /* BFD_BSD */
1298 }
1299
1300 static void bp_bind_ip(int sd, uint16_t port)
1301 {
1302 struct sockaddr_in sin;
1303
1304 memset(&sin, 0, sizeof(sin));
1305 sin.sin_family = AF_INET;
1306 sin.sin_addr.s_addr = htonl(INADDR_ANY);
1307 sin.sin_port = htons(port);
1308 if (bind(sd, (struct sockaddr *)&sin, sizeof(sin)) == -1)
1309 zlog_fatal("bind-ip: bind: %s", strerror(errno));
1310 }
1311
1312 int bp_udp_shop(const struct vrf *vrf)
1313 {
1314 int sd;
1315
1316 frr_with_privs(&bglobal.bfdd_privs) {
1317 sd = vrf_socket(AF_INET, SOCK_DGRAM, PF_UNSPEC, vrf->vrf_id,
1318 vrf->name);
1319 }
1320 if (sd == -1)
1321 zlog_fatal("udp-shop: socket: %s", strerror(errno));
1322
1323 bp_set_ipopts(sd);
1324 bp_bind_ip(sd, BFD_DEFDESTPORT);
1325 return sd;
1326 }
1327
1328 int bp_udp_mhop(const struct vrf *vrf)
1329 {
1330 int sd;
1331
1332 frr_with_privs(&bglobal.bfdd_privs) {
1333 sd = vrf_socket(AF_INET, SOCK_DGRAM, PF_UNSPEC, vrf->vrf_id,
1334 vrf->name);
1335 }
1336 if (sd == -1)
1337 zlog_fatal("udp-mhop: socket: %s", strerror(errno));
1338
1339 bp_set_ipopts(sd);
1340 bp_bind_ip(sd, BFD_DEF_MHOP_DEST_PORT);
1341
1342 return sd;
1343 }
1344
1345 int bp_peer_socket(const struct bfd_session *bs)
1346 {
1347 int sd, pcount;
1348 struct sockaddr_in sin;
1349 static int srcPort = BFD_SRCPORTINIT;
1350 const char *device_to_bind = NULL;
1351
1352 if (bs->key.ifname[0])
1353 device_to_bind = (const char *)bs->key.ifname;
1354 else if ((!vrf_is_backend_netns() && bs->vrf->vrf_id != VRF_DEFAULT)
1355 || ((CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)
1356 && bs->key.vrfname[0])))
1357 device_to_bind = (const char *)bs->key.vrfname;
1358
1359 frr_with_privs(&bglobal.bfdd_privs) {
1360 sd = vrf_socket(AF_INET, SOCK_DGRAM, PF_UNSPEC,
1361 bs->vrf->vrf_id, device_to_bind);
1362 }
1363 if (sd == -1) {
1364 zlog_err("ipv4-new: failed to create socket: %s",
1365 strerror(errno));
1366 return -1;
1367 }
1368
1369 /* Set TTL to 255 for all transmitted packets */
1370 if (bp_set_ttl(sd, BFD_TTL_VAL) != 0) {
1371 close(sd);
1372 return -1;
1373 }
1374
1375 /* Set TOS to CS6 for all transmitted packets */
1376 if (bp_set_tos(sd, BFD_TOS_VAL) != 0) {
1377 close(sd);
1378 return -1;
1379 }
1380
1381 /* Find an available source port in the proper range */
1382 memset(&sin, 0, sizeof(sin));
1383 sin.sin_family = AF_INET;
1384 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1385 sin.sin_len = sizeof(sin);
1386 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1387 memcpy(&sin.sin_addr, &bs->key.local, sizeof(sin.sin_addr));
1388
1389 pcount = 0;
1390 do {
1391 if ((++pcount) > (BFD_SRCPORTMAX - BFD_SRCPORTINIT)) {
1392 /* Searched all ports, none available */
1393 zlog_err("ipv4-new: failed to bind port: %s",
1394 strerror(errno));
1395 close(sd);
1396 return -1;
1397 }
1398 if (srcPort >= BFD_SRCPORTMAX)
1399 srcPort = BFD_SRCPORTINIT;
1400 sin.sin_port = htons(srcPort++);
1401 } while (bind(sd, (struct sockaddr *)&sin, sizeof(sin)) < 0);
1402
1403 return sd;
1404 }
1405
1406
1407 /*
1408 * IPv6 sockets
1409 */
1410
1411 int bp_peer_socketv6(const struct bfd_session *bs)
1412 {
1413 int sd, pcount;
1414 struct sockaddr_in6 sin6;
1415 static int srcPort = BFD_SRCPORTINIT;
1416 const char *device_to_bind = NULL;
1417
1418 if (bs->key.ifname[0])
1419 device_to_bind = (const char *)bs->key.ifname;
1420 else if ((!vrf_is_backend_netns() && bs->vrf->vrf_id != VRF_DEFAULT)
1421 || ((CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)
1422 && bs->key.vrfname[0])))
1423 device_to_bind = (const char *)bs->key.vrfname;
1424
1425 frr_with_privs(&bglobal.bfdd_privs) {
1426 sd = vrf_socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC,
1427 bs->vrf->vrf_id, device_to_bind);
1428 }
1429 if (sd == -1) {
1430 zlog_err("ipv6-new: failed to create socket: %s",
1431 strerror(errno));
1432 return -1;
1433 }
1434
1435 /* Set TTL to 255 for all transmitted packets */
1436 if (bp_set_ttlv6(sd, BFD_TTL_VAL) != 0) {
1437 close(sd);
1438 return -1;
1439 }
1440
1441 /* Set TOS to CS6 for all transmitted packets */
1442 if (bp_set_tosv6(sd, BFD_TOS_VAL) != 0) {
1443 close(sd);
1444 return -1;
1445 }
1446
1447 /* Find an available source port in the proper range */
1448 memset(&sin6, 0, sizeof(sin6));
1449 sin6.sin6_family = AF_INET6;
1450 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1451 sin6.sin6_len = sizeof(sin6);
1452 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1453 memcpy(&sin6.sin6_addr, &bs->key.local, sizeof(sin6.sin6_addr));
1454 if (bs->ifp && IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr))
1455 sin6.sin6_scope_id = bs->ifp->ifindex;
1456
1457 pcount = 0;
1458 do {
1459 if ((++pcount) > (BFD_SRCPORTMAX - BFD_SRCPORTINIT)) {
1460 /* Searched all ports, none available */
1461 zlog_err("ipv6-new: failed to bind port: %s",
1462 strerror(errno));
1463 close(sd);
1464 return -1;
1465 }
1466 if (srcPort >= BFD_SRCPORTMAX)
1467 srcPort = BFD_SRCPORTINIT;
1468 sin6.sin6_port = htons(srcPort++);
1469 } while (bind(sd, (struct sockaddr *)&sin6, sizeof(sin6)) < 0);
1470
1471 return sd;
1472 }
1473
1474 int bp_set_ttlv6(int sd, uint8_t value)
1475 {
1476 int ttl = value;
1477
1478 if (setsockopt(sd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl))
1479 == -1) {
1480 zlog_warn("set-ttlv6: setsockopt(IPV6_UNICAST_HOPS, %d): %s",
1481 value, strerror(errno));
1482 return -1;
1483 }
1484
1485 return 0;
1486 }
1487
1488 int bp_set_tosv6(int sd, uint8_t value)
1489 {
1490 int tos = value;
1491
1492 if (setsockopt(sd, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos))
1493 == -1) {
1494 zlog_warn("set-tosv6: setsockopt(IPV6_TCLASS, %d): %s", value,
1495 strerror(errno));
1496 return -1;
1497 }
1498
1499 return 0;
1500 }
1501
1502 static void bp_set_ipv6opts(int sd)
1503 {
1504 int ipv6_pktinfo = BFD_IPV6_PKT_INFO_VAL;
1505 int ipv6_only = BFD_IPV6_ONLY_VAL;
1506
1507 if (!bp_set_reuse_addr(sd))
1508 zlog_fatal("set-reuse-addr: failed");
1509
1510 if (!bp_set_reuse_port(sd))
1511 zlog_fatal("set-reuse-port: failed");
1512
1513 if (bp_set_ttlv6(sd, BFD_TTL_VAL) == -1)
1514 zlog_fatal(
1515 "set-ipv6opts: setsockopt(IPV6_UNICAST_HOPS, %d): %s",
1516 BFD_TTL_VAL, strerror(errno));
1517
1518 if (setsockopt_ipv6_hoplimit(sd, BFD_RCV_TTL_VAL) == -1)
1519 zlog_fatal("set-ipv6opts: setsockopt(IPV6_HOPLIMIT, %d): %s",
1520 BFD_RCV_TTL_VAL, strerror(errno));
1521
1522 if (setsockopt_ipv6_pktinfo(sd, ipv6_pktinfo) == -1)
1523 zlog_fatal("set-ipv6opts: setsockopt(IPV6_PKTINFO, %d): %s",
1524 ipv6_pktinfo, strerror(errno));
1525
1526 if (setsockopt(sd, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6_only,
1527 sizeof(ipv6_only))
1528 == -1)
1529 zlog_fatal("set-ipv6opts: setsockopt(IPV6_V6ONLY, %d): %s",
1530 ipv6_only, strerror(errno));
1531 }
1532
1533 static void bp_bind_ipv6(int sd, uint16_t port)
1534 {
1535 struct sockaddr_in6 sin6;
1536
1537 memset(&sin6, 0, sizeof(sin6));
1538 sin6.sin6_family = AF_INET6;
1539 sin6.sin6_addr = in6addr_any;
1540 sin6.sin6_port = htons(port);
1541 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1542 sin6.sin6_len = sizeof(sin6);
1543 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1544 if (bind(sd, (struct sockaddr *)&sin6, sizeof(sin6)) == -1)
1545 zlog_fatal("bind-ipv6: bind: %s", strerror(errno));
1546 }
1547
1548 int bp_udp6_shop(const struct vrf *vrf)
1549 {
1550 int sd;
1551
1552 frr_with_privs(&bglobal.bfdd_privs) {
1553 sd = vrf_socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC, vrf->vrf_id,
1554 vrf->name);
1555 }
1556 if (sd == -1) {
1557 if (errno != EAFNOSUPPORT)
1558 zlog_fatal("udp6-shop: socket: %s", strerror(errno));
1559 else
1560 zlog_warn("udp6-shop: V6 is not supported, continuing");
1561
1562 return -1;
1563 }
1564
1565 bp_set_ipv6opts(sd);
1566 bp_bind_ipv6(sd, BFD_DEFDESTPORT);
1567
1568 return sd;
1569 }
1570
1571 int bp_udp6_mhop(const struct vrf *vrf)
1572 {
1573 int sd;
1574
1575 frr_with_privs(&bglobal.bfdd_privs) {
1576 sd = vrf_socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC, vrf->vrf_id,
1577 vrf->name);
1578 }
1579 if (sd == -1) {
1580 if (errno != EAFNOSUPPORT)
1581 zlog_fatal("udp6-mhop: socket: %s", strerror(errno));
1582 else
1583 zlog_warn("udp6-mhop: V6 is not supported, continuing");
1584
1585 return -1;
1586 }
1587
1588 bp_set_ipv6opts(sd);
1589 bp_bind_ipv6(sd, BFD_DEF_MHOP_DEST_PORT);
1590
1591 return sd;
1592 }
1593
1594 #ifdef BFD_LINUX
1595 /* tcpdump -dd udp dst port 3785 */
1596 struct sock_filter my_filterudp[] = {
1597 {0x28, 0, 0, 0x0000000c}, {0x15, 0, 8, 0x00000800},
1598 {0x30, 0, 0, 0x00000017}, {0x15, 0, 6, 0x00000011},
1599 {0x28, 0, 0, 0x00000014}, {0x45, 4, 0, 0x00001fff},
1600 {0xb1, 0, 0, 0x0000000e}, {0x48, 0, 0, 0x00000010},
1601 {0x15, 0, 1, 0x00000ec9}, {0x6, 0, 0, 0x00040000},
1602 {0x6, 0, 0, 0x00000000},
1603 };
1604
1605 #define MY_FILTER_LENGTH 11
1606
1607 int bp_echo_socket(const struct vrf *vrf)
1608 {
1609 int s;
1610
1611 frr_with_privs (&bglobal.bfdd_privs) {
1612 s = vrf_socket(AF_PACKET, SOCK_RAW, ETH_P_IP, vrf->vrf_id,
1613 vrf->name);
1614 }
1615
1616 if (s == -1)
1617 zlog_fatal("echo-socket: socket: %s", strerror(errno));
1618
1619 struct sock_fprog pf;
1620 struct sockaddr_ll sll = {0};
1621
1622 /* adjust filter for socket to only receive ECHO packets */
1623 pf.filter = my_filterudp;
1624 pf.len = MY_FILTER_LENGTH;
1625 if (setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &pf, sizeof(pf)) ==
1626 -1) {
1627 zlog_warn("%s: setsockopt(SO_ATTACH_FILTER): %s", __func__,
1628 strerror(errno));
1629 close(s);
1630 return -1;
1631 }
1632
1633 memset(&sll, 0, sizeof(sll));
1634 sll.sll_family = AF_PACKET;
1635 sll.sll_protocol = htons(ETH_P_IP);
1636 sll.sll_ifindex = 0;
1637 if (bind(s, (struct sockaddr *)&sll, sizeof(sll)) < 0) {
1638 zlog_warn("Failed to bind echo socket: %s",
1639 safe_strerror(errno));
1640 close(s);
1641 return -1;
1642 }
1643
1644 return s;
1645 }
1646 #else
1647 int bp_echo_socket(const struct vrf *vrf)
1648 {
1649 int s;
1650
1651 frr_with_privs(&bglobal.bfdd_privs) {
1652 s = vrf_socket(AF_INET, SOCK_DGRAM, 0, vrf->vrf_id, vrf->name);
1653 }
1654 if (s == -1)
1655 zlog_fatal("echo-socket: socket: %s", strerror(errno));
1656
1657 bp_set_ipopts(s);
1658 bp_bind_ip(s, BFD_DEF_ECHO_PORT);
1659
1660 return s;
1661 }
1662 #endif
1663
1664 int bp_echov6_socket(const struct vrf *vrf)
1665 {
1666 int s;
1667
1668 frr_with_privs(&bglobal.bfdd_privs) {
1669 s = vrf_socket(AF_INET6, SOCK_DGRAM, 0, vrf->vrf_id, vrf->name);
1670 }
1671 if (s == -1) {
1672 if (errno != EAFNOSUPPORT)
1673 zlog_fatal("echov6-socket: socket: %s",
1674 strerror(errno));
1675 else
1676 zlog_warn("echov6-socket: V6 is not supported, continuing");
1677
1678 return -1;
1679 }
1680
1681 bp_set_ipv6opts(s);
1682 bp_bind_ipv6(s, BFD_DEF_ECHO_PORT);
1683
1684 return s;
1685 }
1686
1687 #ifdef BFD_LINUX
1688 /* get peer's mac address to be used with Echo packets when they are looped in
1689 * peers forwarding plane
1690 */
1691 void bfd_peer_mac_set(int sd, struct bfd_session *bfd,
1692 struct sockaddr_any *peer, struct interface *ifp)
1693 {
1694 struct arpreq arpreq_;
1695
1696 if (CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_MAC_SET))
1697 return;
1698 if (ifp->flags & IFF_NOARP)
1699 return;
1700
1701 if (peer->sa_sin.sin_family == AF_INET) {
1702 /* IPV4 */
1703 struct sockaddr_in *addr =
1704 (struct sockaddr_in *)&arpreq_.arp_pa;
1705
1706 memset(&arpreq_, 0, sizeof(struct arpreq));
1707 addr->sin_family = AF_INET;
1708 memcpy(&addr->sin_addr.s_addr, &peer->sa_sin.sin_addr,
1709 sizeof(addr->sin_addr));
1710 strlcpy(arpreq_.arp_dev, ifp->name, sizeof(arpreq_.arp_dev));
1711
1712 if (ioctl(sd, SIOCGARP, &arpreq_) < 0) {
1713 zlog_warn(
1714 "BFD: getting peer's mac on %s failed error %s",
1715 ifp->name, strerror(errno));
1716 UNSET_FLAG(bfd->flags, BFD_SESS_FLAG_MAC_SET);
1717 memset(bfd->peer_hw_addr, 0, sizeof(bfd->peer_hw_addr));
1718
1719 } else {
1720 memcpy(bfd->peer_hw_addr, arpreq_.arp_ha.sa_data,
1721 sizeof(bfd->peer_hw_addr));
1722 SET_FLAG(bfd->flags, BFD_SESS_FLAG_MAC_SET);
1723 }
1724 }
1725 }
1726 #endif