1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*********************************************************************
3 * Copyright 2017 Cumulus Networks, Inc. All rights reserved.
5 * bfd_packet.c: implements the BFD protocol packet handling.
9 * Shrijeet Mukherjee [shm@cumulusnetworks.com]
10 * Kanna Rajagopal [kanna@cumulusnetworks.com]
11 * Radhika Mahankali [Radhika@cumulusnetworks.com]
17 #include <linux/if_packet.h>
18 #endif /* BFD_LINUX */
20 #include <netinet/if_ether.h>
21 #include <netinet/udp.h>
23 #include "lib/sockopt.h"
24 #include "lib/checksum.h"
25 #include "lib/network.h"
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
,
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
);
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
);
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
);
70 int _ptm_bfd_send(struct bfd_session
*bs
, uint16_t *port
, const void *data
,
74 struct sockaddr_in sin
;
75 struct sockaddr_in6 sin6
;
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
;
89 : (CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_MH
))
90 ? htons(BFD_DEF_MHOP_DEST_PORT
)
91 : htons(BFD_DEFDESTPORT
);
94 sa
= (struct sockaddr
*)&sin6
;
97 memset(&sin
, 0, sizeof(sin
));
98 sin
.sin_family
= AF_INET
;
99 memcpy(&sin
.sin_addr
, &bs
->key
.peer
, sizeof(sin
.sin_addr
));
102 : (CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_MH
))
103 ? htons(BFD_DEF_MHOP_DEST_PORT
)
104 : htons(BFD_DEFDESTPORT
);
107 sa
= (struct sockaddr
*)&sin
;
111 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
113 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
114 rv
= sendto(sd
, data
, datalen
, 0, sa
, slen
);
116 if (bglobal
.debug_network
)
117 zlog_debug("packet-send: send failure: %s",
121 if (rv
< (ssize_t
)datalen
) {
122 if (bglobal
.debug_network
)
123 zlog_debug("packet-send: send partial: %s",
132 * Compute the UDP checksum.
134 * Checksum is not set in the packet, just computed.
137 * Packet, fully filled out except for checksum field.
143 * IP address that pkt will be transmitted from and too.
146 * Checksum in network byte order.
148 static uint16_t bfd_pkt_checksum(struct udphdr
*pkt
, size_t pktsize
,
149 struct in6_addr
*ip
, sa_family_t family
)
155 if (family
== AF_INET6
) {
156 struct ipv6_ph ph
= {};
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
);
164 struct ipv4_ph ph
= {};
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
);
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
182 * echo packet with src/dst IP equal to local IP
183 * dest MAC as peer's MAC
185 * currently support ipv4
187 void ptm_bfd_echo_fp_snd(struct bfd_session
*bfd
)
190 struct bfd_vrf_global
*bvrf
= bfd_vrf_look_by_session(bfd
);
195 struct bfd_echo_pkt
*beph
;
196 static char sendbuff
[100];
197 struct timeval time_sent
;
201 if (!CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_MAC_SET
))
203 if (!CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_ECHO_ACTIVE
))
204 SET_FLAG(bfd
->flags
, BFD_SESS_FLAG_ECHO_ACTIVE
);
206 memset(sendbuff
, 0, sizeof(sendbuff
));
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
));
213 total_len
+= sizeof(struct ethhdr
);
216 eth
->h_proto
= htons(ETH_P_IP
);
219 iph
= (struct iphdr
*)(sendbuff
+ sizeof(struct ethhdr
));
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
);
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
);
239 total_len
+= sizeof(struct udphdr
);
242 beph
= (struct bfd_echo_pkt
*)(sendbuff
+ sizeof(struct udphdr
) +
243 sizeof(struct iphdr
) +
244 sizeof(struct ethhdr
));
246 beph
->ver
= BFD_ECHO_VERSION
;
247 beph
->len
= BFD_ECHO_PKT_LEN
;
248 beph
->my_discr
= htonl(bfd
->discrs
.my_discr
);
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
);
255 total_len
+= sizeof(struct bfd_echo_pkt
);
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
);
262 iph
->tot_len
= htons(total_len
- sizeof(struct ethhdr
));
263 iph
->check
= in_cksum((const void *)iph
, sizeof(struct iphdr
));
265 if (bp_udp_send_fp(sd
, (uint8_t *)&sendbuff
, total_len
, bfd
) == -1)
268 bfd
->stats
.tx_echo_pkt
++;
272 void ptm_bfd_echo_snd(struct bfd_session
*bfd
)
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
);
284 if (!CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_ECHO_ACTIVE
))
285 SET_FLAG(bfd
->flags
, BFD_SESS_FLAG_ECHO_ACTIVE
);
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
);
292 if (CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_IPV6
)) {
293 if (bvrf
->bg_echov6
== -1)
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
;
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 */
307 sa
= (struct sockaddr
*)&sin6
;
308 salen
= sizeof(sin6
);
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 */
319 sa
= (struct sockaddr
*)&sin
;
322 if (bp_udp_send(sd
, BFD_TTL_VAL
, (uint8_t *)&bep
, sizeof(bep
), sa
,
327 bfd
->stats
.tx_echo_pkt
++;
330 static int ptm_bfd_process_echo_pkt(struct bfd_vrf_global
*bvrf
, int s
)
332 struct bfd_session
*bfd
;
333 uint32_t my_discr
= 0;
337 /* Receive and parse echo packet. */
338 if (bp_bfd_echo_in(bvrf
, s
, &ttl
, &my_discr
, &my_rtt
) == -1)
341 /* Your discriminator not zero - use it to find session */
342 bfd
= bfd_id_lookup(my_discr
);
344 if (bglobal
.debug_network
)
345 zlog_debug("echo-packet: no matching session (id:%u)",
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
);
357 /* RTT Calculation: add current RTT to samples */
359 bfd
->rtt
[bfd
->rtt_index
] = my_rtt
;
361 if (bfd
->rtt_index
>= BFD_RTT_SAMPLE
)
363 if (bfd
->rtt_valid
< BFD_RTT_SAMPLE
)
367 bfd
->stats
.rx_echo_pkt
++;
369 /* Compute detect time */
370 bfd
->echo_detect_TO
= bfd
->remote_detect_mult
* bfd
->echo_xmt_TO
;
372 /* Update echo receive timeout. */
373 if (bfd
->echo_detect_TO
> 0)
374 bfd_echo_recvtimer_update(bfd
);
379 void ptm_bfd_snd(struct bfd_session
*bfd
, int fbit
)
381 struct bfd_pkt cp
= {};
383 /* Set fields according to section 6.5.7 */
384 cp
.diag
= bfd
->local_diag
;
385 BFD_SETVER(cp
.diag
, BFD_VERSION
);
387 BFD_SETSTATE(cp
.flags
, bfd
->ses_state
);
389 if (CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_CBIT
))
390 BFD_SETCBIT(cp
.flags
, BFD_CBIT
);
392 BFD_SETDEMANDBIT(cp
.flags
, BFD_DEF_DEMAND
);
395 * Polling and Final can't be set at the same time.
397 * RFC 5880, Section 6.5.
399 BFD_SETFBIT(cp
.flags
, fbit
);
401 BFD_SETPBIT(cp
.flags
, bfd
->polling
);
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
);
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
);
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
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
);
424 cp
.timers
.required_min_echo
= htonl(bfd
->timers
.required_min_echo_rx
);
426 if (_ptm_bfd_send(bfd
, NULL
, &cp
, BFD_PKT_LEN
) != 0)
429 bfd
->stats
.tx_ctrl_pkt
++;
434 * receive the ipv4 echo packet that was loopback in the peers forwarding plane
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
)
441 struct sockaddr_ll msgaddr
;
442 struct msghdr msghdr
;
444 uint16_t recv_checksum
;
449 /* Prepare the recvmsg params. */
450 iov
[0].iov_base
= msgbuf
;
451 iov
[0].iov_len
= msgbuflen
;
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;
459 mlen
= recvmsg(sd
, &msghdr
, MSG_DONTWAIT
);
461 if (errno
!= EAGAIN
|| errno
!= EWOULDBLOCK
|| errno
!= EINTR
)
462 zlog_err("%s: recv failed: %s", __func__
,
468 ip
= (struct iphdr
*)(msgbuf
+ sizeof(struct ethhdr
));
470 /* verify ip checksum */
471 recv_checksum
= ip
->check
;
473 checksum
= in_cksum((const void *)ip
, sizeof(struct iphdr
));
474 if (recv_checksum
!= checksum
) {
475 if (bglobal
.debug_network
)
477 "%s: invalid iphdr checksum expected 0x%x rcvd 0x%x",
478 __func__
, checksum
, recv_checksum
);
484 /* Echo should be looped in peer's forwarding plane, but it also
485 * comes up to BFD so silently drop it
487 if (ip
->daddr
== ip
->saddr
)
490 if (bglobal
.debug_network
)
491 zlog_debug("%s: invalid TTL: %u", __func__
, *ttl
);
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
));
500 *ifindex
= msgaddr
.sll_ifindex
;
502 /* verify udp checksum */
503 uh
= (struct udphdr
*)(msgbuf
+ sizeof(struct iphdr
) +
504 sizeof(struct ethhdr
));
505 recv_checksum
= uh
->check
;
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
)
512 "%s: invalid udphdr checksum expected 0x%x rcvd 0x%x",
513 __func__
, checksum
, recv_checksum
);
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
)
526 struct sockaddr_in msgaddr
;
527 struct msghdr msghdr
;
529 uint8_t cmsgbuf
[255];
531 /* Prepare the recvmsg params. */
532 iov
[0].iov_base
= msgbuf
;
533 iov
[0].iov_len
= msgbuflen
;
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
);
543 mlen
= recvmsg(sd
, &msghdr
, MSG_DONTWAIT
);
546 zlog_err("ipv4-recv: recv failed: %s", strerror(errno
));
551 /* Get source address */
552 peer
->sa_sin
= *((struct sockaddr_in
*)(msghdr
.msg_name
));
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
)
560 switch (cm
->cmsg_type
) {
565 memcpy(&ttlval
, CMSG_DATA(cm
), sizeof(ttlval
));
567 if (bglobal
.debug_network
)
568 zlog_debug("%s: invalid TTL: %u",
577 struct in_pktinfo
*pi
=
578 (struct in_pktinfo
*)CMSG_DATA(cm
);
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 */
589 *ifindex
= pi
->ipi_ifindex
;
592 #endif /* BFD_LINUX */
595 memcpy(ttl
, CMSG_DATA(cm
), sizeof(*ttl
));
599 case IP_RECVDSTADDR
: {
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 */
614 * On *BSDs we expect to land here when skipping
615 * the IP_RECVIF header. It will be handled by
616 * getsockopt_ifindex() below.
623 /* OS agnostic way of getting interface name. */
624 if (*ifindex
== IFINDEX_INTERNAL
)
625 *ifindex
= getsockopt_ifindex(AF_INET
, &msghdr
);
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
)
635 struct in6_pktinfo
*pi6
= NULL
;
638 struct sockaddr_in6 msgaddr6
;
639 struct msghdr msghdr6
;
641 uint8_t cmsgbuf6
[255];
643 /* Prepare the recvmsg params. */
644 iov
[0].iov_base
= msgbuf
;
645 iov
[0].iov_len
= msgbuflen
;
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
);
655 mlen
= recvmsg(sd
, &msghdr6
, MSG_DONTWAIT
);
658 zlog_err("ipv6-recv: recv failed: %s", strerror(errno
));
663 /* Get source address */
664 peer
->sa_sin6
= *((struct sockaddr_in6
*)(msghdr6
.msg_name
));
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
)
672 if (cm
->cmsg_type
== IPV6_HOPLIMIT
) {
673 memcpy(&ttlval
, CMSG_DATA(cm
), sizeof(ttlval
));
675 if (bglobal
.debug_network
)
676 zlog_debug("%s: invalid TTL: %u",
682 } else if (cm
->cmsg_type
== IPV6_PKTINFO
) {
683 pi6
= (struct in6_pktinfo
*)CMSG_DATA(cm
);
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 */
691 *ifindex
= pi6
->ipi6_ifindex
;
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
;
707 static void bfd_sd_reschedule(struct bfd_vrf_global
*bvrf
, int sd
)
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
,
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
,
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
,
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
,
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
,
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
,
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
, ...)
741 char buf
[512], peerstr
[128], localstr
[128], portstr
[64], vrfstr
[64];
744 /* Don't to any processing if debug is disabled. */
745 if (bglobal
.debug_network
== false)
748 if (peer
->sa_sin
.sin_family
)
749 snprintf(peerstr
, sizeof(peerstr
), " peer:%s", satostr(peer
));
753 if (local
->sa_sin
.sin_family
)
754 snprintf(localstr
, sizeof(localstr
), " local:%s",
759 if (ifindex
!= IFINDEX_INTERNAL
)
760 snprintf(portstr
, sizeof(portstr
), " port:%u", ifindex
);
764 if (vrfid
!= VRF_DEFAULT
)
765 snprintf(vrfstr
, sizeof(vrfstr
), " vrf:%u", vrfid
);
770 vsnprintf(buf
, sizeof(buf
), fmt
, vl
);
773 zlog_debug("control-packet: %s [mhop:%s%s%s%s%s]", buf
,
774 mhop
? "yes" : "no", peerstr
, localstr
, portstr
, vrfstr
);
777 void bfd_recv_cb(struct thread
*t
)
779 int sd
= THREAD_FD(t
);
780 struct bfd_session
*bfd
;
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
);
792 /* Schedule next read. */
793 bfd_sd_reschedule(bvrf
, sd
);
795 /* Handle echo packets. */
796 if (sd
== bvrf
->bg_echo
|| sd
== bvrf
->bg_echov6
) {
797 ptm_bfd_process_echo_pkt(bvrf
, sd
);
801 /* Sanitize input/output. */
802 memset(&local
, 0, sizeof(local
));
803 memset(&peer
, 0, sizeof(peer
));
805 /* Handle control packets. */
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
,
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
,
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.
825 vrfid
= bvrf
->vrf
->vrf_id
;
827 ifp
= if_lookup_by_index(ifindex
, vrfid
);
829 vrfid
= ifp
->vrf
->vrf_id
;
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
);
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
);
847 * Parse the control header for inconsistencies:
849 * - Bad multiplier configuration;
851 * - Invalid discriminator;
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
));
860 if (cp
->detect_mult
== 0) {
861 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
,
862 "detect multiplier set to zero");
866 if ((cp
->len
< BFD_PKT_LEN
) || (cp
->len
> mlen
)) {
867 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
, "too small");
871 if (cp
->discrs
.my_discr
== 0) {
872 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
,
873 "'my discriminator' is zero");
877 /* Find the session that this packet belongs. */
878 bfd
= ptm_bfd_sess_find(cp
, &peer
, &local
, ifp
, vrfid
, is_mhop
);
880 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
,
885 * We may have a situation where received packet is on wrong vrf
887 if (bfd
&& bfd
->vrf
&& bfd
->vrf
!= bvrf
->vrf
) {
888 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
,
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");
902 * Multi hop: validate packet TTL.
903 * Single hop: set local address that received the packet.
904 * set peers mac address for echo packets
907 if (ttl
< bfd
->mh_ttl
) {
908 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
,
909 "exceeded max hop count (expected %d, got %d)",
915 if (bfd
->local_address
.sa_sin
.sin_family
== AF_UNSPEC
)
916 bfd
->local_address
= local
;
919 bfd_peer_mac_set(sd
, bfd
, &peer
, ifp
);
923 bfd
->stats
.rx_ctrl_pkt
++;
926 * If no interface was detected, save the interface where the
929 if (!is_mhop
&& bfd
->ifp
== NULL
)
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
));
939 bfd
->discrs
.remote_discr
= ntohl(cp
->discrs
.my_discr
);
941 /* Save remote diagnostics before state switch. */
942 bfd
->remote_diag
= cp
->diag
& BFD_DIAGMASK
;
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
;
951 if (BFD_GETCBIT(cp
->flags
))
952 bfd
->remote_cbit
= 1;
954 bfd
->remote_cbit
= 0;
956 /* State switch from section 6.2. */
957 bs_state_handler(bfd
, BFD_GETSTATE(cp
->flags
));
959 /* RFC 5880, Section 6.5: handle POLL/FINAL negotiation sequence. */
960 if (bfd
->polling
&& BFD_GETFBIT(cp
->flags
)) {
961 /* Disable polling. */
964 /* Handle poll finalization. */
965 bs_final_handler(bfd
);
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.
974 * RFC 5880, Section 6.8.4.
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
;
980 bfd
->detect_TO
= bfd
->remote_detect_mult
981 * bfd
->remote_timers
.desired_min_tx
;
983 /* Apply new receive timer immediately. */
984 bfd_recvtimer_update(bfd
);
986 /* Handle echo timers changes. */
987 bs_echo_timer_handler(bfd
);
990 * We've received a packet with the POLL bit set, we must send
991 * a control packet back with the FINAL bit set.
993 * RFC 5880, Section 6.5.
995 if (BFD_GETPBIT(cp
->flags
)) {
996 /* We are finalizing a poll negotiation. */
997 bs_final_handler(bfd
);
999 /* Send the control packet with the final bit immediately. */
1000 ptm_bfd_snd(bfd
, 1);
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
1009 * Returns -1 on error or loopback or 0 on success.
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
)
1014 struct bfd_echo_pkt
*bep
;
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;
1022 if (sd
== bvrf
->bg_echo
) {
1024 rlen
= bfd_recv_ipv4_fp(sd
, msgbuf
, sizeof(msgbuf
), ttl
,
1025 &ifindex
, &local
, &peer
);
1027 /* silently drop echo packet that is looped in fastpath but
1028 * still comes up to BFD
1032 bfd_offset
= sizeof(struct udphdr
) + sizeof(struct iphdr
) +
1033 sizeof(struct ethhdr
);
1035 rlen
= bfd_recv_ipv4(sd
, msgbuf
, sizeof(msgbuf
), ttl
, &ifindex
,
1040 rlen
= bfd_recv_ipv6(sd
, msgbuf
, sizeof(msgbuf
), ttl
, &ifindex
,
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");
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
));
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)");
1071 /* RTT Calculation: determine RTT time of IPv4 echo pkt */
1072 if (sd
== bvrf
->bg_echo
) {
1073 struct timeval time_sent
= {0, 0};
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
);
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
1089 int bp_udp_send_fp(int sd
, uint8_t *data
, size_t datalen
,
1090 struct bfd_session
*bfd
)
1093 struct msghdr msg
= {0};
1094 struct iovec iov
[1];
1095 uint8_t msgctl
[255];
1096 struct sockaddr_ll sadr_ll
= {0};
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
);
1103 /* Prepare message data. */
1104 iov
[0].iov_base
= data
;
1105 iov
[0].iov_len
= datalen
;
1107 memset(msgctl
, 0, sizeof(msgctl
));
1108 msg
.msg_name
= &sadr_ll
;
1109 msg
.msg_namelen
= sizeof(sadr_ll
);
1113 /* Send echo to peer */
1114 wlen
= sendmsg(sd
, &msg
, 0);
1117 if (bglobal
.debug_network
)
1118 zlog_debug("%s: loopback failure: (%d) %s", __func__
,
1119 errno
, strerror(errno
));
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
);
1132 int bp_udp_send(int sd
, uint8_t ttl
, uint8_t *data
, size_t datalen
,
1133 struct sockaddr
*to
, socklen_t tolen
)
1135 struct cmsghdr
*cmsg
;
1138 bool is_ipv6
= to
->sa_family
== AF_INET6
;
1140 struct iovec iov
[1];
1141 uint8_t msgctl
[255];
1143 /* Prepare message data. */
1144 iov
[0].iov_base
= data
;
1145 iov
[0].iov_len
= datalen
;
1147 memset(&msg
, 0, sizeof(msg
));
1148 memset(msgctl
, 0, sizeof(msgctl
));
1150 msg
.msg_namelen
= tolen
;
1154 /* Prepare the packet TTL information. */
1156 /* Use ancillary data. */
1157 msg
.msg_control
= msgctl
;
1158 msg
.msg_controllen
= CMSG_LEN(sizeof(ttlval
));
1160 /* Configure the ancillary data. */
1161 cmsg
= CMSG_FIRSTHDR(&msg
);
1162 cmsg
->cmsg_len
= CMSG_LEN(sizeof(ttlval
));
1164 cmsg
->cmsg_level
= IPPROTO_IPV6
;
1165 cmsg
->cmsg_type
= IPV6_HOPLIMIT
;
1168 cmsg
->cmsg_level
= IPPROTO_IP
;
1169 cmsg
->cmsg_type
= IP_TTL
;
1171 /* FreeBSD does not support TTL in ancillary data. */
1172 msg
.msg_control
= NULL
;
1173 msg
.msg_controllen
= 0;
1175 bp_set_ttl(sd
, ttl
);
1176 #endif /* BFD_BSD */
1178 memcpy(CMSG_DATA(cmsg
), &ttlval
, sizeof(ttlval
));
1181 /* Send echo back. */
1182 wlen
= sendmsg(sd
, &msg
, 0);
1184 if (bglobal
.debug_network
)
1185 zlog_debug("%s: loopback failure: (%d) %s", __func__
,
1186 errno
, strerror(errno
));
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
);
1207 int bp_set_ttl(int sd
, uint8_t value
)
1211 if (setsockopt(sd
, IPPROTO_IP
, IP_TTL
, &ttl
, sizeof(ttl
)) == -1) {
1212 zlog_warn("%s: setsockopt(IP_TTL, %d): %s", __func__
, value
,
1220 int bp_set_tos(int sd
, uint8_t value
)
1224 if (setsockopt(sd
, IPPROTO_IP
, IP_TOS
, &tos
, sizeof(tos
)) == -1) {
1225 zlog_warn("%s: setsockopt(IP_TOS, %d): %s", __func__
, value
,
1233 static bool bp_set_reuse_addr(int sd
)
1237 if (setsockopt(sd
, SOL_SOCKET
, SO_REUSEADDR
, &one
, sizeof(one
)) == -1) {
1238 zlog_warn("%s: setsockopt(SO_REUSEADDR, %d): %s", __func__
, one
,
1245 static bool bp_set_reuse_port(int sd
)
1249 if (setsockopt(sd
, SOL_SOCKET
, SO_REUSEPORT
, &one
, sizeof(one
)) == -1) {
1250 zlog_warn("%s: setsockopt(SO_REUSEPORT, %d): %s", __func__
, one
,
1258 static void bp_set_ipopts(int sd
)
1260 int rcvttl
= BFD_RCV_TTL_VAL
;
1262 if (!bp_set_reuse_addr(sd
))
1263 zlog_fatal("set-reuse-addr: failed");
1265 if (!bp_set_reuse_port(sd
))
1266 zlog_fatal("set-reuse-port: failed");
1268 if (bp_set_ttl(sd
, BFD_TTL_VAL
) != 0)
1269 zlog_fatal("set-ipopts: TTL configuration failed");
1271 if (setsockopt(sd
, IPPROTO_IP
, IP_RECVTTL
, &rcvttl
, sizeof(rcvttl
))
1273 zlog_fatal("set-ipopts: setsockopt(IP_RECVTTL, %d): %s", rcvttl
,
1277 int pktinfo
= BFD_PKT_INFO_VAL
;
1279 /* Figure out address and interface to do the peer matching. */
1280 if (setsockopt(sd
, IPPROTO_IP
, IP_PKTINFO
, &pktinfo
, sizeof(pktinfo
))
1282 zlog_fatal("set-ipopts: setsockopt(IP_PKTINFO, %d): %s",
1283 pktinfo
, strerror(errno
));
1284 #endif /* BFD_LINUX */
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
));
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
,
1297 #endif /* BFD_BSD */
1300 static void bp_bind_ip(int sd
, uint16_t port
)
1302 struct sockaddr_in sin
;
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
));
1312 int bp_udp_shop(const struct vrf
*vrf
)
1316 frr_with_privs(&bglobal
.bfdd_privs
) {
1317 sd
= vrf_socket(AF_INET
, SOCK_DGRAM
, PF_UNSPEC
, vrf
->vrf_id
,
1321 zlog_fatal("udp-shop: socket: %s", strerror(errno
));
1324 bp_bind_ip(sd
, BFD_DEFDESTPORT
);
1328 int bp_udp_mhop(const struct vrf
*vrf
)
1332 frr_with_privs(&bglobal
.bfdd_privs
) {
1333 sd
= vrf_socket(AF_INET
, SOCK_DGRAM
, PF_UNSPEC
, vrf
->vrf_id
,
1337 zlog_fatal("udp-mhop: socket: %s", strerror(errno
));
1340 bp_bind_ip(sd
, BFD_DEF_MHOP_DEST_PORT
);
1345 int bp_peer_socket(const struct bfd_session
*bs
)
1348 struct sockaddr_in sin
;
1349 static int srcPort
= BFD_SRCPORTINIT
;
1350 const char *device_to_bind
= NULL
;
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
;
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
);
1364 zlog_err("ipv4-new: failed to create socket: %s",
1369 /* Set TTL to 255 for all transmitted packets */
1370 if (bp_set_ttl(sd
, BFD_TTL_VAL
) != 0) {
1375 /* Set TOS to CS6 for all transmitted packets */
1376 if (bp_set_tos(sd
, BFD_TOS_VAL
) != 0) {
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
));
1391 if ((++pcount
) > (BFD_SRCPORTMAX
- BFD_SRCPORTINIT
)) {
1392 /* Searched all ports, none available */
1393 zlog_err("ipv4-new: failed to bind port: %s",
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);
1411 int bp_peer_socketv6(const struct bfd_session
*bs
)
1414 struct sockaddr_in6 sin6
;
1415 static int srcPort
= BFD_SRCPORTINIT
;
1416 const char *device_to_bind
= NULL
;
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
;
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
);
1430 zlog_err("ipv6-new: failed to create socket: %s",
1435 /* Set TTL to 255 for all transmitted packets */
1436 if (bp_set_ttlv6(sd
, BFD_TTL_VAL
) != 0) {
1441 /* Set TOS to CS6 for all transmitted packets */
1442 if (bp_set_tosv6(sd
, BFD_TOS_VAL
) != 0) {
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
;
1459 if ((++pcount
) > (BFD_SRCPORTMAX
- BFD_SRCPORTINIT
)) {
1460 /* Searched all ports, none available */
1461 zlog_err("ipv6-new: failed to bind port: %s",
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);
1474 int bp_set_ttlv6(int sd
, uint8_t value
)
1478 if (setsockopt(sd
, IPPROTO_IPV6
, IPV6_UNICAST_HOPS
, &ttl
, sizeof(ttl
))
1480 zlog_warn("set-ttlv6: setsockopt(IPV6_UNICAST_HOPS, %d): %s",
1481 value
, strerror(errno
));
1488 int bp_set_tosv6(int sd
, uint8_t value
)
1492 if (setsockopt(sd
, IPPROTO_IPV6
, IPV6_TCLASS
, &tos
, sizeof(tos
))
1494 zlog_warn("set-tosv6: setsockopt(IPV6_TCLASS, %d): %s", value
,
1502 static void bp_set_ipv6opts(int sd
)
1504 int ipv6_pktinfo
= BFD_IPV6_PKT_INFO_VAL
;
1505 int ipv6_only
= BFD_IPV6_ONLY_VAL
;
1507 if (!bp_set_reuse_addr(sd
))
1508 zlog_fatal("set-reuse-addr: failed");
1510 if (!bp_set_reuse_port(sd
))
1511 zlog_fatal("set-reuse-port: failed");
1513 if (bp_set_ttlv6(sd
, BFD_TTL_VAL
) == -1)
1515 "set-ipv6opts: setsockopt(IPV6_UNICAST_HOPS, %d): %s",
1516 BFD_TTL_VAL
, strerror(errno
));
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
));
1522 if (setsockopt_ipv6_pktinfo(sd
, ipv6_pktinfo
) == -1)
1523 zlog_fatal("set-ipv6opts: setsockopt(IPV6_PKTINFO, %d): %s",
1524 ipv6_pktinfo
, strerror(errno
));
1526 if (setsockopt(sd
, IPPROTO_IPV6
, IPV6_V6ONLY
, &ipv6_only
,
1529 zlog_fatal("set-ipv6opts: setsockopt(IPV6_V6ONLY, %d): %s",
1530 ipv6_only
, strerror(errno
));
1533 static void bp_bind_ipv6(int sd
, uint16_t port
)
1535 struct sockaddr_in6 sin6
;
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
));
1548 int bp_udp6_shop(const struct vrf
*vrf
)
1552 frr_with_privs(&bglobal
.bfdd_privs
) {
1553 sd
= vrf_socket(AF_INET6
, SOCK_DGRAM
, PF_UNSPEC
, vrf
->vrf_id
,
1557 if (errno
!= EAFNOSUPPORT
)
1558 zlog_fatal("udp6-shop: socket: %s", strerror(errno
));
1560 zlog_warn("udp6-shop: V6 is not supported, continuing");
1565 bp_set_ipv6opts(sd
);
1566 bp_bind_ipv6(sd
, BFD_DEFDESTPORT
);
1571 int bp_udp6_mhop(const struct vrf
*vrf
)
1575 frr_with_privs(&bglobal
.bfdd_privs
) {
1576 sd
= vrf_socket(AF_INET6
, SOCK_DGRAM
, PF_UNSPEC
, vrf
->vrf_id
,
1580 if (errno
!= EAFNOSUPPORT
)
1581 zlog_fatal("udp6-mhop: socket: %s", strerror(errno
));
1583 zlog_warn("udp6-mhop: V6 is not supported, continuing");
1588 bp_set_ipv6opts(sd
);
1589 bp_bind_ipv6(sd
, BFD_DEF_MHOP_DEST_PORT
);
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},
1605 #define MY_FILTER_LENGTH 11
1607 int bp_echo_socket(const struct vrf
*vrf
)
1611 frr_with_privs (&bglobal
.bfdd_privs
) {
1612 s
= vrf_socket(AF_PACKET
, SOCK_RAW
, ETH_P_IP
, vrf
->vrf_id
,
1617 zlog_fatal("echo-socket: socket: %s", strerror(errno
));
1619 struct sock_fprog pf
;
1620 struct sockaddr_ll sll
= {0};
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
)) ==
1627 zlog_warn("%s: setsockopt(SO_ATTACH_FILTER): %s", __func__
,
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
));
1647 int bp_echo_socket(const struct vrf
*vrf
)
1651 frr_with_privs(&bglobal
.bfdd_privs
) {
1652 s
= vrf_socket(AF_INET
, SOCK_DGRAM
, 0, vrf
->vrf_id
, vrf
->name
);
1655 zlog_fatal("echo-socket: socket: %s", strerror(errno
));
1658 bp_bind_ip(s
, BFD_DEF_ECHO_PORT
);
1664 int bp_echov6_socket(const struct vrf
*vrf
)
1668 frr_with_privs(&bglobal
.bfdd_privs
) {
1669 s
= vrf_socket(AF_INET6
, SOCK_DGRAM
, 0, vrf
->vrf_id
, vrf
->name
);
1672 if (errno
!= EAFNOSUPPORT
)
1673 zlog_fatal("echov6-socket: socket: %s",
1676 zlog_warn("echov6-socket: V6 is not supported, continuing");
1682 bp_bind_ipv6(s
, BFD_DEF_ECHO_PORT
);
1688 /* get peer's mac address to be used with Echo packets when they are looped in
1689 * peers forwarding plane
1691 void bfd_peer_mac_set(int sd
, struct bfd_session
*bfd
,
1692 struct sockaddr_any
*peer
, struct interface
*ifp
)
1694 struct arpreq arpreq_
;
1696 if (CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_MAC_SET
))
1698 if (ifp
->flags
& IFF_NOARP
)
1701 if (peer
->sa_sin
.sin_family
== AF_INET
) {
1703 struct sockaddr_in
*addr
=
1704 (struct sockaddr_in
*)&arpreq_
.arp_pa
;
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
));
1712 if (ioctl(sd
, SIOCGARP
, &arpreq_
) < 0) {
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
));
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
);