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 to.
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 if (bglobal
.debug_network
)
485 zlog_debug("%s: invalid TTL: %u", __func__
, *ttl
);
489 local
->sa_sin
.sin_family
= AF_INET
;
490 memcpy(&local
->sa_sin
.sin_addr
, &ip
->saddr
, sizeof(ip
->saddr
));
491 peer
->sa_sin
.sin_family
= AF_INET
;
492 memcpy(&peer
->sa_sin
.sin_addr
, &ip
->daddr
, sizeof(ip
->daddr
));
494 *ifindex
= msgaddr
.sll_ifindex
;
496 /* verify udp checksum */
497 uh
= (struct udphdr
*)(msgbuf
+ sizeof(struct iphdr
) +
498 sizeof(struct ethhdr
));
499 recv_checksum
= uh
->check
;
501 checksum
= bfd_pkt_checksum(uh
, ntohs(uh
->len
),
502 (struct in6_addr
*)&ip
->saddr
, AF_INET
);
503 if (recv_checksum
!= checksum
) {
504 if (bglobal
.debug_network
)
506 "%s: invalid udphdr checksum expected 0x%x rcvd 0x%x",
507 __func__
, checksum
, recv_checksum
);
514 ssize_t
bfd_recv_ipv4(int sd
, uint8_t *msgbuf
, size_t msgbuflen
, uint8_t *ttl
,
515 ifindex_t
*ifindex
, struct sockaddr_any
*local
,
516 struct sockaddr_any
*peer
)
520 struct sockaddr_in msgaddr
;
521 struct msghdr msghdr
;
523 uint8_t cmsgbuf
[255];
525 /* Prepare the recvmsg params. */
526 iov
[0].iov_base
= msgbuf
;
527 iov
[0].iov_len
= msgbuflen
;
529 memset(&msghdr
, 0, sizeof(msghdr
));
530 msghdr
.msg_name
= &msgaddr
;
531 msghdr
.msg_namelen
= sizeof(msgaddr
);
532 msghdr
.msg_iov
= iov
;
533 msghdr
.msg_iovlen
= 1;
534 msghdr
.msg_control
= cmsgbuf
;
535 msghdr
.msg_controllen
= sizeof(cmsgbuf
);
537 mlen
= recvmsg(sd
, &msghdr
, MSG_DONTWAIT
);
540 zlog_err("ipv4-recv: recv failed: %s", strerror(errno
));
545 /* Get source address */
546 peer
->sa_sin
= *((struct sockaddr_in
*)(msghdr
.msg_name
));
548 /* Get and check TTL */
549 for (cm
= CMSG_FIRSTHDR(&msghdr
); cm
!= NULL
;
550 cm
= CMSG_NXTHDR(&msghdr
, cm
)) {
551 if (cm
->cmsg_level
!= IPPROTO_IP
)
554 switch (cm
->cmsg_type
) {
559 memcpy(&ttlval
, CMSG_DATA(cm
), sizeof(ttlval
));
561 if (bglobal
.debug_network
)
562 zlog_debug("%s: invalid TTL: %u",
571 struct in_pktinfo
*pi
=
572 (struct in_pktinfo
*)CMSG_DATA(cm
);
577 local
->sa_sin
.sin_family
= AF_INET
;
578 local
->sa_sin
.sin_addr
= pi
->ipi_addr
;
579 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
580 local
->sa_sin
.sin_len
= sizeof(local
->sa_sin
);
581 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
583 *ifindex
= pi
->ipi_ifindex
;
586 #endif /* BFD_LINUX */
589 memcpy(ttl
, CMSG_DATA(cm
), sizeof(*ttl
));
593 case IP_RECVDSTADDR
: {
596 memcpy(&ia
, CMSG_DATA(cm
), sizeof(ia
));
597 local
->sa_sin
.sin_family
= AF_INET
;
598 local
->sa_sin
.sin_addr
= ia
;
599 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
600 local
->sa_sin
.sin_len
= sizeof(local
->sa_sin
);
601 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
608 * On *BSDs we expect to land here when skipping
609 * the IP_RECVIF header. It will be handled by
610 * getsockopt_ifindex() below.
617 /* OS agnostic way of getting interface name. */
618 if (*ifindex
== IFINDEX_INTERNAL
)
619 *ifindex
= getsockopt_ifindex(AF_INET
, &msghdr
);
624 ssize_t
bfd_recv_ipv6(int sd
, uint8_t *msgbuf
, size_t msgbuflen
, uint8_t *ttl
,
625 ifindex_t
*ifindex
, struct sockaddr_any
*local
,
626 struct sockaddr_any
*peer
)
629 struct in6_pktinfo
*pi6
= NULL
;
632 struct sockaddr_in6 msgaddr6
;
633 struct msghdr msghdr6
;
635 uint8_t cmsgbuf6
[255];
637 /* Prepare the recvmsg params. */
638 iov
[0].iov_base
= msgbuf
;
639 iov
[0].iov_len
= msgbuflen
;
641 memset(&msghdr6
, 0, sizeof(msghdr6
));
642 msghdr6
.msg_name
= &msgaddr6
;
643 msghdr6
.msg_namelen
= sizeof(msgaddr6
);
644 msghdr6
.msg_iov
= iov
;
645 msghdr6
.msg_iovlen
= 1;
646 msghdr6
.msg_control
= cmsgbuf6
;
647 msghdr6
.msg_controllen
= sizeof(cmsgbuf6
);
649 mlen
= recvmsg(sd
, &msghdr6
, MSG_DONTWAIT
);
652 zlog_err("ipv6-recv: recv failed: %s", strerror(errno
));
657 /* Get source address */
658 peer
->sa_sin6
= *((struct sockaddr_in6
*)(msghdr6
.msg_name
));
660 /* Get and check TTL */
661 for (cm
= CMSG_FIRSTHDR(&msghdr6
); cm
!= NULL
;
662 cm
= CMSG_NXTHDR(&msghdr6
, cm
)) {
663 if (cm
->cmsg_level
!= IPPROTO_IPV6
)
666 if (cm
->cmsg_type
== IPV6_HOPLIMIT
) {
667 memcpy(&ttlval
, CMSG_DATA(cm
), sizeof(ttlval
));
669 if (bglobal
.debug_network
)
670 zlog_debug("%s: invalid TTL: %u",
676 } else if (cm
->cmsg_type
== IPV6_PKTINFO
) {
677 pi6
= (struct in6_pktinfo
*)CMSG_DATA(cm
);
679 local
->sa_sin6
.sin6_family
= AF_INET6
;
680 local
->sa_sin6
.sin6_addr
= pi6
->ipi6_addr
;
681 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
682 local
->sa_sin6
.sin6_len
= sizeof(local
->sa_sin6
);
683 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
685 *ifindex
= pi6
->ipi6_ifindex
;
687 /* Set scope ID for link local addresses. */
688 if (IN6_IS_ADDR_LINKLOCAL(
689 &peer
->sa_sin6
.sin6_addr
))
690 peer
->sa_sin6
.sin6_scope_id
= *ifindex
;
691 if (IN6_IS_ADDR_LINKLOCAL(
692 &local
->sa_sin6
.sin6_addr
))
693 local
->sa_sin6
.sin6_scope_id
= *ifindex
;
701 static void bfd_sd_reschedule(struct bfd_vrf_global
*bvrf
, int sd
)
703 if (sd
== bvrf
->bg_shop
) {
704 EVENT_OFF(bvrf
->bg_ev
[0]);
705 event_add_read(master
, bfd_recv_cb
, bvrf
, bvrf
->bg_shop
,
707 } else if (sd
== bvrf
->bg_mhop
) {
708 EVENT_OFF(bvrf
->bg_ev
[1]);
709 event_add_read(master
, bfd_recv_cb
, bvrf
, bvrf
->bg_mhop
,
711 } else if (sd
== bvrf
->bg_shop6
) {
712 EVENT_OFF(bvrf
->bg_ev
[2]);
713 event_add_read(master
, bfd_recv_cb
, bvrf
, bvrf
->bg_shop6
,
715 } else if (sd
== bvrf
->bg_mhop6
) {
716 EVENT_OFF(bvrf
->bg_ev
[3]);
717 event_add_read(master
, bfd_recv_cb
, bvrf
, bvrf
->bg_mhop6
,
719 } else if (sd
== bvrf
->bg_echo
) {
720 EVENT_OFF(bvrf
->bg_ev
[4]);
721 event_add_read(master
, bfd_recv_cb
, bvrf
, bvrf
->bg_echo
,
723 } else if (sd
== bvrf
->bg_echov6
) {
724 EVENT_OFF(bvrf
->bg_ev
[5]);
725 event_add_read(master
, bfd_recv_cb
, bvrf
, bvrf
->bg_echov6
,
731 static void cp_debug(bool mhop
, struct sockaddr_any
*peer
,
732 struct sockaddr_any
*local
, ifindex_t ifindex
,
733 vrf_id_t vrfid
, const char *fmt
, ...)
735 char buf
[512], peerstr
[128], localstr
[128], portstr
[64], vrfstr
[64];
738 /* Don't to any processing if debug is disabled. */
739 if (bglobal
.debug_network
== false)
742 if (peer
->sa_sin
.sin_family
)
743 snprintf(peerstr
, sizeof(peerstr
), " peer:%s", satostr(peer
));
747 if (local
->sa_sin
.sin_family
)
748 snprintf(localstr
, sizeof(localstr
), " local:%s",
753 if (ifindex
!= IFINDEX_INTERNAL
)
754 snprintf(portstr
, sizeof(portstr
), " port:%u", ifindex
);
758 if (vrfid
!= VRF_DEFAULT
)
759 snprintf(vrfstr
, sizeof(vrfstr
), " vrf:%u", vrfid
);
764 vsnprintf(buf
, sizeof(buf
), fmt
, vl
);
767 zlog_debug("control-packet: %s [mhop:%s%s%s%s%s]", buf
,
768 mhop
? "yes" : "no", peerstr
, localstr
, portstr
, vrfstr
);
771 void bfd_recv_cb(struct event
*t
)
773 int sd
= EVENT_FD(t
);
774 struct bfd_session
*bfd
;
780 ifindex_t ifindex
= IFINDEX_INTERNAL
;
781 struct sockaddr_any local
, peer
;
782 uint8_t msgbuf
[1516];
783 struct interface
*ifp
= NULL
;
784 struct bfd_vrf_global
*bvrf
= EVENT_ARG(t
);
786 /* Schedule next read. */
787 bfd_sd_reschedule(bvrf
, sd
);
789 /* Handle echo packets. */
790 if (sd
== bvrf
->bg_echo
|| sd
== bvrf
->bg_echov6
) {
791 ptm_bfd_process_echo_pkt(bvrf
, sd
);
795 /* Sanitize input/output. */
796 memset(&local
, 0, sizeof(local
));
797 memset(&peer
, 0, sizeof(peer
));
799 /* Handle control packets. */
801 if (sd
== bvrf
->bg_shop
|| sd
== bvrf
->bg_mhop
) {
802 is_mhop
= sd
== bvrf
->bg_mhop
;
803 mlen
= bfd_recv_ipv4(sd
, msgbuf
, sizeof(msgbuf
), &ttl
, &ifindex
,
805 } else if (sd
== bvrf
->bg_shop6
|| sd
== bvrf
->bg_mhop6
) {
806 is_mhop
= sd
== bvrf
->bg_mhop6
;
807 mlen
= bfd_recv_ipv6(sd
, msgbuf
, sizeof(msgbuf
), &ttl
, &ifindex
,
812 * With netns backend, we have a separate socket in each VRF. It means
813 * that bvrf here is correct and we believe the bvrf->vrf->vrf_id.
814 * With VRF-lite backend, we have a single socket in the default VRF.
815 * It means that we can't believe the bvrf->vrf->vrf_id. But in
816 * VRF-lite, the ifindex is globally unique, so we can retrieve the
817 * correct vrf_id from the interface.
819 vrfid
= bvrf
->vrf
->vrf_id
;
821 ifp
= if_lookup_by_index(ifindex
, vrfid
);
823 vrfid
= ifp
->vrf
->vrf_id
;
826 /* Implement RFC 5880 6.8.6 */
827 if (mlen
< BFD_PKT_LEN
) {
828 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
,
829 "too small (%zd bytes)", mlen
);
833 /* Validate single hop packet TTL. */
834 if ((!is_mhop
) && (ttl
!= BFD_TTL_VAL
)) {
835 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
,
836 "invalid TTL: %d expected %d", ttl
, BFD_TTL_VAL
);
841 * Parse the control header for inconsistencies:
843 * - Bad multiplier configuration;
845 * - Invalid discriminator;
847 cp
= (struct bfd_pkt
*)(msgbuf
);
848 if (BFD_GETVER(cp
->diag
) != BFD_VERSION
) {
849 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
,
850 "bad version %d", BFD_GETVER(cp
->diag
));
854 if (cp
->detect_mult
== 0) {
855 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
,
856 "detect multiplier set to zero");
860 if ((cp
->len
< BFD_PKT_LEN
) || (cp
->len
> mlen
)) {
861 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
, "too small");
865 if (cp
->discrs
.my_discr
== 0) {
866 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
,
867 "'my discriminator' is zero");
871 /* Find the session that this packet belongs. */
872 bfd
= ptm_bfd_sess_find(cp
, &peer
, &local
, ifp
, vrfid
, is_mhop
);
874 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
,
879 * We may have a situation where received packet is on wrong vrf
881 if (bfd
&& bfd
->vrf
&& bfd
->vrf
->vrf_id
!= vrfid
) {
882 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
,
887 /* Ensure that existing good sessions are not overridden. */
888 if (!cp
->discrs
.remote_discr
&& bfd
->ses_state
!= PTM_BFD_DOWN
&&
889 bfd
->ses_state
!= PTM_BFD_ADM_DOWN
) {
890 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
,
891 "'remote discriminator' is zero, not overridden");
896 * Multi hop: validate packet TTL.
897 * Single hop: set local address that received the packet.
898 * set peers mac address for echo packets
901 if (ttl
< bfd
->mh_ttl
) {
902 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
,
903 "exceeded max hop count (expected %d, got %d)",
909 if (bfd
->local_address
.sa_sin
.sin_family
== AF_UNSPEC
)
910 bfd
->local_address
= local
;
913 bfd_peer_mac_set(sd
, bfd
, &peer
, ifp
);
917 bfd
->stats
.rx_ctrl_pkt
++;
920 * If no interface was detected, save the interface where the
923 if (!is_mhop
&& bfd
->ifp
== NULL
)
926 /* Log remote discriminator changes. */
927 if ((bfd
->discrs
.remote_discr
!= 0)
928 && (bfd
->discrs
.remote_discr
!= ntohl(cp
->discrs
.my_discr
)))
929 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
,
930 "remote discriminator mismatch (expected %u, got %u)",
931 bfd
->discrs
.remote_discr
, ntohl(cp
->discrs
.my_discr
));
933 bfd
->discrs
.remote_discr
= ntohl(cp
->discrs
.my_discr
);
935 /* Save remote diagnostics before state switch. */
936 bfd
->remote_diag
= cp
->diag
& BFD_DIAGMASK
;
938 /* Update remote timers settings. */
939 bfd
->remote_timers
.desired_min_tx
= ntohl(cp
->timers
.desired_min_tx
);
940 bfd
->remote_timers
.required_min_rx
= ntohl(cp
->timers
.required_min_rx
);
941 bfd
->remote_timers
.required_min_echo
=
942 ntohl(cp
->timers
.required_min_echo
);
943 bfd
->remote_detect_mult
= cp
->detect_mult
;
945 if (BFD_GETCBIT(cp
->flags
))
946 bfd
->remote_cbit
= 1;
948 bfd
->remote_cbit
= 0;
950 /* State switch from section 6.2. */
951 bs_state_handler(bfd
, BFD_GETSTATE(cp
->flags
));
953 /* RFC 5880, Section 6.5: handle POLL/FINAL negotiation sequence. */
954 if (bfd
->polling
&& BFD_GETFBIT(cp
->flags
)) {
955 /* Disable polling. */
958 /* Handle poll finalization. */
959 bs_final_handler(bfd
);
963 * Detection timeout calculation:
964 * The minimum detection timeout is the remote detection
965 * multipler (number of packets to be missed) times the agreed
966 * transmission interval.
968 * RFC 5880, Section 6.8.4.
970 if (bfd
->cur_timers
.required_min_rx
> bfd
->remote_timers
.desired_min_tx
)
971 bfd
->detect_TO
= bfd
->remote_detect_mult
972 * bfd
->cur_timers
.required_min_rx
;
974 bfd
->detect_TO
= bfd
->remote_detect_mult
975 * bfd
->remote_timers
.desired_min_tx
;
977 /* Apply new receive timer immediately. */
978 bfd_recvtimer_update(bfd
);
980 /* Handle echo timers changes. */
981 bs_echo_timer_handler(bfd
);
984 * We've received a packet with the POLL bit set, we must send
985 * a control packet back with the FINAL bit set.
987 * RFC 5880, Section 6.5.
989 if (BFD_GETPBIT(cp
->flags
)) {
990 /* We are finalizing a poll negotiation. */
991 bs_final_handler(bfd
);
993 /* Send the control packet with the final bit immediately. */
999 * bp_bfd_echo_in: proccesses an BFD echo packet. On TTL == BFD_TTL_VAL
1000 * the packet is looped back or returns the my discriminator ID along
1003 * Returns -1 on error or loopback or 0 on success.
1005 int bp_bfd_echo_in(struct bfd_vrf_global
*bvrf
, int sd
, uint8_t *ttl
,
1006 uint32_t *my_discr
, uint64_t *my_rtt
)
1008 struct bfd_echo_pkt
*bep
;
1010 struct sockaddr_any local
, peer
;
1011 ifindex_t ifindex
= IFINDEX_INTERNAL
;
1012 vrf_id_t vrfid
= VRF_DEFAULT
;
1013 uint8_t msgbuf
[1516];
1014 size_t bfd_offset
= 0;
1016 if (sd
== bvrf
->bg_echo
) {
1018 rlen
= bfd_recv_ipv4_fp(sd
, msgbuf
, sizeof(msgbuf
), ttl
,
1019 &ifindex
, &local
, &peer
);
1021 /* silently drop echo packet that is looped in fastpath but
1022 * still comes up to BFD
1026 bfd_offset
= sizeof(struct udphdr
) + sizeof(struct iphdr
) +
1027 sizeof(struct ethhdr
);
1029 rlen
= bfd_recv_ipv4(sd
, msgbuf
, sizeof(msgbuf
), ttl
, &ifindex
,
1034 rlen
= bfd_recv_ipv6(sd
, msgbuf
, sizeof(msgbuf
), ttl
, &ifindex
,
1039 /* Short packet, better not risk reading it. */
1040 if (rlen
< (ssize_t
)sizeof(*bep
)) {
1041 cp_debug(false, &peer
, &local
, ifindex
, vrfid
,
1042 "small echo packet");
1046 /* Test for loopback for ipv6, ipv4 is looped in forwarding plane */
1047 if ((*ttl
== BFD_TTL_VAL
) && (sd
== bvrf
->bg_echov6
)) {
1048 bp_udp_send(sd
, *ttl
- 1, msgbuf
, rlen
,
1049 (struct sockaddr
*)&peer
,
1050 (sd
== bvrf
->bg_echo
) ? sizeof(peer
.sa_sin
)
1051 : sizeof(peer
.sa_sin6
));
1055 /* Read my discriminator from BFD Echo packet. */
1056 bep
= (struct bfd_echo_pkt
*)(msgbuf
+ bfd_offset
);
1057 *my_discr
= ntohl(bep
->my_discr
);
1058 if (*my_discr
== 0) {
1059 cp_debug(false, &peer
, &local
, ifindex
, vrfid
,
1060 "invalid echo packet discriminator (zero)");
1065 /* RTT Calculation: determine RTT time of IPv4 echo pkt */
1066 if (sd
== bvrf
->bg_echo
) {
1067 struct timeval time_sent
= {0, 0};
1069 time_sent
.tv_sec
= be64toh(bep
->time_sent_sec
);
1070 time_sent
.tv_usec
= be64toh(bep
->time_sent_usec
);
1071 *my_rtt
= monotime_since(&time_sent
, NULL
);
1080 * send a bfd packet with src/dst same IP so that the peer will receive
1081 * the packet and forward it back to sender in the forwarding plane
1083 int bp_udp_send_fp(int sd
, uint8_t *data
, size_t datalen
,
1084 struct bfd_session
*bfd
)
1087 struct msghdr msg
= {0};
1088 struct iovec iov
[1];
1089 uint8_t msgctl
[255];
1090 struct sockaddr_ll sadr_ll
= {0};
1092 sadr_ll
.sll_ifindex
= bfd
->ifp
->ifindex
;
1093 sadr_ll
.sll_halen
= ETH_ALEN
;
1094 memcpy(sadr_ll
.sll_addr
, bfd
->peer_hw_addr
, sizeof(bfd
->peer_hw_addr
));
1095 sadr_ll
.sll_protocol
= htons(ETH_P_IP
);
1097 /* Prepare message data. */
1098 iov
[0].iov_base
= data
;
1099 iov
[0].iov_len
= datalen
;
1101 memset(msgctl
, 0, sizeof(msgctl
));
1102 msg
.msg_name
= &sadr_ll
;
1103 msg
.msg_namelen
= sizeof(sadr_ll
);
1107 /* Send echo to peer */
1108 wlen
= sendmsg(sd
, &msg
, 0);
1111 if (bglobal
.debug_network
)
1112 zlog_debug("%s: loopback failure: (%d) %s", __func__
,
1113 errno
, strerror(errno
));
1115 } else if (wlen
< (ssize_t
)datalen
) {
1116 if (bglobal
.debug_network
)
1117 zlog_debug("%s: partial send: %zd expected %zu",
1118 __func__
, wlen
, datalen
);
1126 int bp_udp_send(int sd
, uint8_t ttl
, uint8_t *data
, size_t datalen
,
1127 struct sockaddr
*to
, socklen_t tolen
)
1129 struct cmsghdr
*cmsg
;
1132 bool is_ipv6
= to
->sa_family
== AF_INET6
;
1134 struct iovec iov
[1];
1135 uint8_t msgctl
[255];
1137 /* Prepare message data. */
1138 iov
[0].iov_base
= data
;
1139 iov
[0].iov_len
= datalen
;
1141 memset(&msg
, 0, sizeof(msg
));
1142 memset(msgctl
, 0, sizeof(msgctl
));
1144 msg
.msg_namelen
= tolen
;
1148 /* Prepare the packet TTL information. */
1150 /* Use ancillary data. */
1151 msg
.msg_control
= msgctl
;
1152 msg
.msg_controllen
= CMSG_LEN(sizeof(ttlval
));
1154 /* Configure the ancillary data. */
1155 cmsg
= CMSG_FIRSTHDR(&msg
);
1156 cmsg
->cmsg_len
= CMSG_LEN(sizeof(ttlval
));
1158 cmsg
->cmsg_level
= IPPROTO_IPV6
;
1159 cmsg
->cmsg_type
= IPV6_HOPLIMIT
;
1162 cmsg
->cmsg_level
= IPPROTO_IP
;
1163 cmsg
->cmsg_type
= IP_TTL
;
1165 /* FreeBSD does not support TTL in ancillary data. */
1166 msg
.msg_control
= NULL
;
1167 msg
.msg_controllen
= 0;
1169 bp_set_ttl(sd
, ttl
);
1170 #endif /* BFD_BSD */
1172 memcpy(CMSG_DATA(cmsg
), &ttlval
, sizeof(ttlval
));
1175 /* Send echo back. */
1176 wlen
= sendmsg(sd
, &msg
, 0);
1178 if (bglobal
.debug_network
)
1179 zlog_debug("%s: loopback failure: (%d) %s", __func__
,
1180 errno
, strerror(errno
));
1182 } else if (wlen
< (ssize_t
)datalen
) {
1183 if (bglobal
.debug_network
)
1184 zlog_debug("%s: partial send: %zd expected %zu",
1185 __func__
, wlen
, datalen
);
1201 int bp_set_ttl(int sd
, uint8_t value
)
1205 if (setsockopt(sd
, IPPROTO_IP
, IP_TTL
, &ttl
, sizeof(ttl
)) == -1) {
1206 zlog_warn("%s: setsockopt(IP_TTL, %d): %s", __func__
, value
,
1214 int bp_set_tos(int sd
, uint8_t value
)
1218 if (setsockopt(sd
, IPPROTO_IP
, IP_TOS
, &tos
, sizeof(tos
)) == -1) {
1219 zlog_warn("%s: setsockopt(IP_TOS, %d): %s", __func__
, value
,
1227 static bool bp_set_reuse_addr(int sd
)
1231 if (setsockopt(sd
, SOL_SOCKET
, SO_REUSEADDR
, &one
, sizeof(one
)) == -1) {
1232 zlog_warn("%s: setsockopt(SO_REUSEADDR, %d): %s", __func__
, one
,
1239 static bool bp_set_reuse_port(int sd
)
1243 if (setsockopt(sd
, SOL_SOCKET
, SO_REUSEPORT
, &one
, sizeof(one
)) == -1) {
1244 zlog_warn("%s: setsockopt(SO_REUSEPORT, %d): %s", __func__
, one
,
1252 static void bp_set_ipopts(int sd
)
1254 int rcvttl
= BFD_RCV_TTL_VAL
;
1256 if (!bp_set_reuse_addr(sd
))
1257 zlog_fatal("set-reuse-addr: failed");
1259 if (!bp_set_reuse_port(sd
))
1260 zlog_fatal("set-reuse-port: failed");
1262 if (bp_set_ttl(sd
, BFD_TTL_VAL
) != 0)
1263 zlog_fatal("set-ipopts: TTL configuration failed");
1265 if (setsockopt(sd
, IPPROTO_IP
, IP_RECVTTL
, &rcvttl
, sizeof(rcvttl
))
1267 zlog_fatal("set-ipopts: setsockopt(IP_RECVTTL, %d): %s", rcvttl
,
1271 int pktinfo
= BFD_PKT_INFO_VAL
;
1273 /* Figure out address and interface to do the peer matching. */
1274 if (setsockopt(sd
, IPPROTO_IP
, IP_PKTINFO
, &pktinfo
, sizeof(pktinfo
))
1276 zlog_fatal("set-ipopts: setsockopt(IP_PKTINFO, %d): %s",
1277 pktinfo
, strerror(errno
));
1278 #endif /* BFD_LINUX */
1282 /* Find out our address for peer matching. */
1283 if (setsockopt(sd
, IPPROTO_IP
, IP_RECVDSTADDR
, &yes
, sizeof(yes
)) == -1)
1284 zlog_fatal("set-ipopts: setsockopt(IP_RECVDSTADDR, %d): %s",
1285 yes
, strerror(errno
));
1287 /* Find out interface where the packet came in. */
1288 if (setsockopt_ifindex(AF_INET
, sd
, yes
) == -1)
1289 zlog_fatal("set-ipopts: setsockopt_ipv4_ifindex(%d): %s", yes
,
1291 #endif /* BFD_BSD */
1294 static void bp_bind_ip(int sd
, uint16_t port
)
1296 struct sockaddr_in sin
;
1298 memset(&sin
, 0, sizeof(sin
));
1299 sin
.sin_family
= AF_INET
;
1300 sin
.sin_addr
.s_addr
= htonl(INADDR_ANY
);
1301 sin
.sin_port
= htons(port
);
1302 if (bind(sd
, (struct sockaddr
*)&sin
, sizeof(sin
)) == -1)
1303 zlog_fatal("bind-ip: bind: %s", strerror(errno
));
1306 int bp_udp_shop(const struct vrf
*vrf
)
1310 frr_with_privs(&bglobal
.bfdd_privs
) {
1311 sd
= vrf_socket(AF_INET
, SOCK_DGRAM
, PF_UNSPEC
, vrf
->vrf_id
,
1315 zlog_fatal("udp-shop: socket: %s", strerror(errno
));
1318 bp_bind_ip(sd
, BFD_DEFDESTPORT
);
1322 int bp_udp_mhop(const struct vrf
*vrf
)
1326 frr_with_privs(&bglobal
.bfdd_privs
) {
1327 sd
= vrf_socket(AF_INET
, SOCK_DGRAM
, PF_UNSPEC
, vrf
->vrf_id
,
1331 zlog_fatal("udp-mhop: socket: %s", strerror(errno
));
1334 bp_bind_ip(sd
, BFD_DEF_MHOP_DEST_PORT
);
1339 int bp_peer_socket(const struct bfd_session
*bs
)
1342 struct sockaddr_in sin
;
1343 static int srcPort
= BFD_SRCPORTINIT
;
1344 const char *device_to_bind
= NULL
;
1346 if (bs
->key
.ifname
[0])
1347 device_to_bind
= (const char *)bs
->key
.ifname
;
1348 else if ((!vrf_is_backend_netns() && bs
->vrf
->vrf_id
!= VRF_DEFAULT
)
1349 || ((CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_MH
)
1350 && bs
->key
.vrfname
[0])))
1351 device_to_bind
= (const char *)bs
->key
.vrfname
;
1353 frr_with_privs(&bglobal
.bfdd_privs
) {
1354 sd
= vrf_socket(AF_INET
, SOCK_DGRAM
, PF_UNSPEC
,
1355 bs
->vrf
->vrf_id
, device_to_bind
);
1358 zlog_err("ipv4-new: failed to create socket: %s",
1363 /* Set TTL to 255 for all transmitted packets */
1364 if (bp_set_ttl(sd
, BFD_TTL_VAL
) != 0) {
1369 /* Set TOS to CS6 for all transmitted packets */
1370 if (bp_set_tos(sd
, BFD_TOS_VAL
) != 0) {
1375 /* Find an available source port in the proper range */
1376 memset(&sin
, 0, sizeof(sin
));
1377 sin
.sin_family
= AF_INET
;
1378 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1379 sin
.sin_len
= sizeof(sin
);
1380 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1381 memcpy(&sin
.sin_addr
, &bs
->key
.local
, sizeof(sin
.sin_addr
));
1385 if ((++pcount
) > (BFD_SRCPORTMAX
- BFD_SRCPORTINIT
)) {
1386 /* Searched all ports, none available */
1387 zlog_err("ipv4-new: failed to bind port: %s",
1392 if (srcPort
>= BFD_SRCPORTMAX
)
1393 srcPort
= BFD_SRCPORTINIT
;
1394 sin
.sin_port
= htons(srcPort
++);
1395 } while (bind(sd
, (struct sockaddr
*)&sin
, sizeof(sin
)) < 0);
1405 int bp_peer_socketv6(const struct bfd_session
*bs
)
1408 struct sockaddr_in6 sin6
;
1409 static int srcPort
= BFD_SRCPORTINIT
;
1410 const char *device_to_bind
= NULL
;
1412 if (bs
->key
.ifname
[0])
1413 device_to_bind
= (const char *)bs
->key
.ifname
;
1414 else if ((!vrf_is_backend_netns() && bs
->vrf
->vrf_id
!= VRF_DEFAULT
)
1415 || ((CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_MH
)
1416 && bs
->key
.vrfname
[0])))
1417 device_to_bind
= (const char *)bs
->key
.vrfname
;
1419 frr_with_privs(&bglobal
.bfdd_privs
) {
1420 sd
= vrf_socket(AF_INET6
, SOCK_DGRAM
, PF_UNSPEC
,
1421 bs
->vrf
->vrf_id
, device_to_bind
);
1424 zlog_err("ipv6-new: failed to create socket: %s",
1429 /* Set TTL to 255 for all transmitted packets */
1430 if (bp_set_ttlv6(sd
, BFD_TTL_VAL
) != 0) {
1435 /* Set TOS to CS6 for all transmitted packets */
1436 if (bp_set_tosv6(sd
, BFD_TOS_VAL
) != 0) {
1441 /* Find an available source port in the proper range */
1442 memset(&sin6
, 0, sizeof(sin6
));
1443 sin6
.sin6_family
= AF_INET6
;
1444 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1445 sin6
.sin6_len
= sizeof(sin6
);
1446 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1447 memcpy(&sin6
.sin6_addr
, &bs
->key
.local
, sizeof(sin6
.sin6_addr
));
1448 if (bs
->ifp
&& IN6_IS_ADDR_LINKLOCAL(&sin6
.sin6_addr
))
1449 sin6
.sin6_scope_id
= bs
->ifp
->ifindex
;
1453 if ((++pcount
) > (BFD_SRCPORTMAX
- BFD_SRCPORTINIT
)) {
1454 /* Searched all ports, none available */
1455 zlog_err("ipv6-new: failed to bind port: %s",
1460 if (srcPort
>= BFD_SRCPORTMAX
)
1461 srcPort
= BFD_SRCPORTINIT
;
1462 sin6
.sin6_port
= htons(srcPort
++);
1463 } while (bind(sd
, (struct sockaddr
*)&sin6
, sizeof(sin6
)) < 0);
1468 int bp_set_ttlv6(int sd
, uint8_t value
)
1472 if (setsockopt(sd
, IPPROTO_IPV6
, IPV6_UNICAST_HOPS
, &ttl
, sizeof(ttl
))
1474 zlog_warn("set-ttlv6: setsockopt(IPV6_UNICAST_HOPS, %d): %s",
1475 value
, strerror(errno
));
1482 int bp_set_tosv6(int sd
, uint8_t value
)
1486 if (setsockopt(sd
, IPPROTO_IPV6
, IPV6_TCLASS
, &tos
, sizeof(tos
))
1488 zlog_warn("set-tosv6: setsockopt(IPV6_TCLASS, %d): %s", value
,
1496 static void bp_set_ipv6opts(int sd
)
1498 int ipv6_pktinfo
= BFD_IPV6_PKT_INFO_VAL
;
1499 int ipv6_only
= BFD_IPV6_ONLY_VAL
;
1501 if (!bp_set_reuse_addr(sd
))
1502 zlog_fatal("set-reuse-addr: failed");
1504 if (!bp_set_reuse_port(sd
))
1505 zlog_fatal("set-reuse-port: failed");
1507 if (bp_set_ttlv6(sd
, BFD_TTL_VAL
) == -1)
1509 "set-ipv6opts: setsockopt(IPV6_UNICAST_HOPS, %d): %s",
1510 BFD_TTL_VAL
, strerror(errno
));
1512 if (setsockopt_ipv6_hoplimit(sd
, BFD_RCV_TTL_VAL
) == -1)
1513 zlog_fatal("set-ipv6opts: setsockopt(IPV6_HOPLIMIT, %d): %s",
1514 BFD_RCV_TTL_VAL
, strerror(errno
));
1516 if (setsockopt_ipv6_pktinfo(sd
, ipv6_pktinfo
) == -1)
1517 zlog_fatal("set-ipv6opts: setsockopt(IPV6_PKTINFO, %d): %s",
1518 ipv6_pktinfo
, strerror(errno
));
1520 if (setsockopt(sd
, IPPROTO_IPV6
, IPV6_V6ONLY
, &ipv6_only
,
1523 zlog_fatal("set-ipv6opts: setsockopt(IPV6_V6ONLY, %d): %s",
1524 ipv6_only
, strerror(errno
));
1527 static void bp_bind_ipv6(int sd
, uint16_t port
)
1529 struct sockaddr_in6 sin6
;
1531 memset(&sin6
, 0, sizeof(sin6
));
1532 sin6
.sin6_family
= AF_INET6
;
1533 sin6
.sin6_addr
= in6addr_any
;
1534 sin6
.sin6_port
= htons(port
);
1535 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1536 sin6
.sin6_len
= sizeof(sin6
);
1537 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1538 if (bind(sd
, (struct sockaddr
*)&sin6
, sizeof(sin6
)) == -1)
1539 zlog_fatal("bind-ipv6: bind: %s", strerror(errno
));
1542 int bp_udp6_shop(const struct vrf
*vrf
)
1546 frr_with_privs(&bglobal
.bfdd_privs
) {
1547 sd
= vrf_socket(AF_INET6
, SOCK_DGRAM
, PF_UNSPEC
, vrf
->vrf_id
,
1551 if (errno
!= EAFNOSUPPORT
)
1552 zlog_fatal("udp6-shop: socket: %s", strerror(errno
));
1554 zlog_warn("udp6-shop: V6 is not supported, continuing");
1559 bp_set_ipv6opts(sd
);
1560 bp_bind_ipv6(sd
, BFD_DEFDESTPORT
);
1565 int bp_udp6_mhop(const struct vrf
*vrf
)
1569 frr_with_privs(&bglobal
.bfdd_privs
) {
1570 sd
= vrf_socket(AF_INET6
, SOCK_DGRAM
, PF_UNSPEC
, vrf
->vrf_id
,
1574 if (errno
!= EAFNOSUPPORT
)
1575 zlog_fatal("udp6-mhop: socket: %s", strerror(errno
));
1577 zlog_warn("udp6-mhop: V6 is not supported, continuing");
1582 bp_set_ipv6opts(sd
);
1583 bp_bind_ipv6(sd
, BFD_DEF_MHOP_DEST_PORT
);
1589 /* tcpdump -dd udp dst port 3785 */
1590 struct sock_filter my_filterudp
[] = {
1591 {0x28, 0, 0, 0x0000000c}, {0x15, 0, 8, 0x00000800},
1592 {0x30, 0, 0, 0x00000017}, {0x15, 0, 6, 0x00000011},
1593 {0x28, 0, 0, 0x00000014}, {0x45, 4, 0, 0x00001fff},
1594 {0xb1, 0, 0, 0x0000000e}, {0x48, 0, 0, 0x00000010},
1595 {0x15, 0, 1, 0x00000ec9}, {0x6, 0, 0, 0x00040000},
1596 {0x6, 0, 0, 0x00000000},
1599 #define MY_FILTER_LENGTH 11
1601 int bp_echo_socket(const struct vrf
*vrf
)
1605 frr_with_privs (&bglobal
.bfdd_privs
) {
1606 s
= vrf_socket(AF_PACKET
, SOCK_RAW
, ETH_P_IP
, vrf
->vrf_id
,
1611 zlog_fatal("echo-socket: socket: %s", strerror(errno
));
1613 struct sock_fprog pf
;
1614 struct sockaddr_ll sll
= {0};
1616 /* adjust filter for socket to only receive ECHO packets */
1617 pf
.filter
= my_filterudp
;
1618 pf
.len
= MY_FILTER_LENGTH
;
1619 if (setsockopt(s
, SOL_SOCKET
, SO_ATTACH_FILTER
, &pf
, sizeof(pf
)) ==
1621 zlog_warn("%s: setsockopt(SO_ATTACH_FILTER): %s", __func__
,
1627 memset(&sll
, 0, sizeof(sll
));
1628 sll
.sll_family
= AF_PACKET
;
1629 sll
.sll_protocol
= htons(ETH_P_IP
);
1630 sll
.sll_ifindex
= 0;
1631 if (bind(s
, (struct sockaddr
*)&sll
, sizeof(sll
)) < 0) {
1632 zlog_warn("Failed to bind echo socket: %s",
1633 safe_strerror(errno
));
1641 int bp_echo_socket(const struct vrf
*vrf
)
1645 frr_with_privs(&bglobal
.bfdd_privs
) {
1646 s
= vrf_socket(AF_INET
, SOCK_DGRAM
, 0, vrf
->vrf_id
, vrf
->name
);
1649 zlog_fatal("echo-socket: socket: %s", strerror(errno
));
1652 bp_bind_ip(s
, BFD_DEF_ECHO_PORT
);
1658 int bp_echov6_socket(const struct vrf
*vrf
)
1662 frr_with_privs(&bglobal
.bfdd_privs
) {
1663 s
= vrf_socket(AF_INET6
, SOCK_DGRAM
, 0, vrf
->vrf_id
, vrf
->name
);
1666 if (errno
!= EAFNOSUPPORT
)
1667 zlog_fatal("echov6-socket: socket: %s",
1670 zlog_warn("echov6-socket: V6 is not supported, continuing");
1676 bp_bind_ipv6(s
, BFD_DEF_ECHO_PORT
);
1682 /* get peer's mac address to be used with Echo packets when they are looped in
1683 * peers forwarding plane
1685 void bfd_peer_mac_set(int sd
, struct bfd_session
*bfd
,
1686 struct sockaddr_any
*peer
, struct interface
*ifp
)
1688 struct arpreq arpreq_
;
1690 if (CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_MAC_SET
))
1692 if (ifp
->flags
& IFF_NOARP
)
1695 if (peer
->sa_sin
.sin_family
== AF_INET
) {
1697 struct sockaddr_in
*addr
=
1698 (struct sockaddr_in
*)&arpreq_
.arp_pa
;
1700 memset(&arpreq_
, 0, sizeof(struct arpreq
));
1701 addr
->sin_family
= AF_INET
;
1702 memcpy(&addr
->sin_addr
.s_addr
, &peer
->sa_sin
.sin_addr
,
1703 sizeof(addr
->sin_addr
));
1704 strlcpy(arpreq_
.arp_dev
, ifp
->name
, sizeof(arpreq_
.arp_dev
));
1706 if (ioctl(sd
, SIOCGARP
, &arpreq_
) < 0) {
1708 "BFD: getting peer's mac on %s failed error %s",
1709 ifp
->name
, strerror(errno
));
1710 UNSET_FLAG(bfd
->flags
, BFD_SESS_FLAG_MAC_SET
);
1711 memset(bfd
->peer_hw_addr
, 0, sizeof(bfd
->peer_hw_addr
));
1714 memcpy(bfd
->peer_hw_addr
, arpreq_
.arp_ha
.sa_data
,
1715 sizeof(bfd
->peer_hw_addr
));
1716 SET_FLAG(bfd
->flags
, BFD_SESS_FLAG_MAC_SET
);