1 /*********************************************************************
2 * Copyright 2017 Cumulus Networks, Inc. All rights reserved.
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the Free
6 * Software Foundation; either version 2 of the License, or (at your option)
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * You should have received a copy of the GNU General Public License along
15 * with this program; see the file COPYING; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 * bfd_packet.c: implements the BFD protocol packet handling.
22 * Shrijeet Mukherjee [shm@cumulusnetworks.com]
23 * Kanna Rajagopal [kanna@cumulusnetworks.com]
24 * Radhika Mahankali [Radhika@cumulusnetworks.com]
30 #include <linux/if_packet.h>
31 #endif /* BFD_LINUX */
33 #include <netinet/if_ether.h>
34 #include <netinet/udp.h>
36 #include "lib/sockopt.h"
37 #include "lib/checksum.h"
38 #include "lib/network.h"
45 static int ptm_bfd_process_echo_pkt(struct bfd_vrf_global
*bvrf
, int s
);
46 int _ptm_bfd_send(struct bfd_session
*bs
, uint16_t *port
, const void *data
,
49 static void bfd_sd_reschedule(struct bfd_vrf_global
*bvrf
, int sd
);
50 ssize_t
bfd_recv_ipv4(int sd
, uint8_t *msgbuf
, size_t msgbuflen
, uint8_t *ttl
,
51 ifindex_t
*ifindex
, struct sockaddr_any
*local
,
52 struct sockaddr_any
*peer
);
53 ssize_t
bfd_recv_ipv6(int sd
, uint8_t *msgbuf
, size_t msgbuflen
, uint8_t *ttl
,
54 ifindex_t
*ifindex
, struct sockaddr_any
*local
,
55 struct sockaddr_any
*peer
);
56 int bp_udp_send(int sd
, uint8_t ttl
, uint8_t *data
, size_t datalen
,
57 struct sockaddr
*to
, socklen_t tolen
);
58 int bp_bfd_echo_in(struct bfd_vrf_global
*bvrf
, int sd
, uint8_t *ttl
,
59 uint32_t *my_discr
, uint64_t *my_rtt
);
61 ssize_t
bfd_recv_ipv4_fp(int sd
, uint8_t *msgbuf
, size_t msgbuflen
,
62 uint8_t *ttl
, ifindex_t
*ifindex
,
63 struct sockaddr_any
*local
, struct sockaddr_any
*peer
);
64 void bfd_peer_mac_set(int sd
, struct bfd_session
*bfd
,
65 struct sockaddr_any
*peer
, struct interface
*ifp
);
66 int bp_udp_send_fp(int sd
, uint8_t *data
, size_t datalen
,
67 struct bfd_session
*bfd
);
68 ssize_t
bfd_recv_fp_echo(int sd
, uint8_t *msgbuf
, size_t msgbuflen
,
69 uint8_t *ttl
, ifindex_t
*ifindex
,
70 struct sockaddr_any
*local
, struct sockaddr_any
*peer
);
73 /* socket related prototypes */
74 static void bp_set_ipopts(int sd
);
75 static void bp_bind_ip(int sd
, uint16_t port
);
76 static void bp_set_ipv6opts(int sd
);
77 static void bp_bind_ipv6(int sd
, uint16_t port
);
83 int _ptm_bfd_send(struct bfd_session
*bs
, uint16_t *port
, const void *data
,
87 struct sockaddr_in sin
;
88 struct sockaddr_in6 sin6
;
93 if (CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_IPV6
)) {
94 memset(&sin6
, 0, sizeof(sin6
));
95 sin6
.sin6_family
= AF_INET6
;
96 memcpy(&sin6
.sin6_addr
, &bs
->key
.peer
, sizeof(sin6
.sin6_addr
));
97 if (bs
->ifp
&& IN6_IS_ADDR_LINKLOCAL(&sin6
.sin6_addr
))
98 sin6
.sin6_scope_id
= bs
->ifp
->ifindex
;
102 : (CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_MH
))
103 ? htons(BFD_DEF_MHOP_DEST_PORT
)
104 : htons(BFD_DEFDESTPORT
);
107 sa
= (struct sockaddr
*)&sin6
;
110 memset(&sin
, 0, sizeof(sin
));
111 sin
.sin_family
= AF_INET
;
112 memcpy(&sin
.sin_addr
, &bs
->key
.peer
, sizeof(sin
.sin_addr
));
115 : (CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_MH
))
116 ? htons(BFD_DEF_MHOP_DEST_PORT
)
117 : htons(BFD_DEFDESTPORT
);
120 sa
= (struct sockaddr
*)&sin
;
124 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
126 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
127 rv
= sendto(sd
, data
, datalen
, 0, sa
, slen
);
129 if (bglobal
.debug_network
)
130 zlog_debug("packet-send: send failure: %s",
134 if (rv
< (ssize_t
)datalen
) {
135 if (bglobal
.debug_network
)
136 zlog_debug("packet-send: send partial: %s",
145 * Compute the UDP checksum.
147 * Checksum is not set in the packet, just computed.
150 * Packet, fully filled out except for checksum field.
156 * IP address that pkt will be transmitted from and too.
159 * Checksum in network byte order.
161 static uint16_t bfd_pkt_checksum(struct udphdr
*pkt
, size_t pktsize
,
162 struct in6_addr
*ip
, sa_family_t family
)
168 if (family
== AF_INET6
) {
169 struct ipv6_ph ph
= {};
171 memcpy(&ph
.src
, ip
, sizeof(ph
.src
));
172 memcpy(&ph
.dst
, ip
, sizeof(ph
.dst
));
173 ph
.ulpl
= htons(pktsize
);
174 ph
.next_hdr
= IPPROTO_UDP
;
175 chksum
= in_cksum_with_ph6(&ph
, pkt
, pktsize
);
177 struct ipv4_ph ph
= {};
179 memcpy(&ph
.src
, ip
, sizeof(ph
.src
));
180 memcpy(&ph
.dst
, ip
, sizeof(ph
.dst
));
181 ph
.proto
= IPPROTO_UDP
;
182 ph
.len
= htons(pktsize
);
183 chksum
= in_cksum_with_ph4(&ph
, pkt
, pktsize
);
190 * This routine creates the entire ECHO packet so that it will be looped
191 * in the forwarding plane of the peer router instead of going up the
192 * stack in BFD to be looped. If we haven't learned the peers MAC yet
195 * echo packet with src/dst IP equal to local IP
196 * dest MAC as peer's MAC
198 * currently support ipv4
200 void ptm_bfd_echo_fp_snd(struct bfd_session
*bfd
)
203 struct bfd_vrf_global
*bvrf
= bfd_vrf_look_by_session(bfd
);
208 struct bfd_echo_pkt
*beph
;
209 static char sendbuff
[100];
210 struct timeval time_sent
;
214 if (!CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_MAC_SET
))
216 if (!CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_ECHO_ACTIVE
))
217 SET_FLAG(bfd
->flags
, BFD_SESS_FLAG_ECHO_ACTIVE
);
219 memset(sendbuff
, 0, sizeof(sendbuff
));
222 eth
= (struct ethhdr
*)(sendbuff
);
223 memcpy(eth
->h_source
, bfd
->ifp
->hw_addr
, sizeof(eth
->h_source
));
224 memcpy(eth
->h_dest
, bfd
->peer_hw_addr
, sizeof(eth
->h_dest
));
226 total_len
+= sizeof(struct ethhdr
);
229 eth
->h_proto
= htons(ETH_P_IP
);
232 iph
= (struct iphdr
*)(sendbuff
+ sizeof(struct ethhdr
));
234 iph
->ihl
= sizeof(struct ip
) >> 2;
235 iph
->version
= IPVERSION
;
236 iph
->tos
= IPTOS_PREC_INTERNETCONTROL
;
237 iph
->id
= (uint16_t)frr_weak_random();
238 iph
->ttl
= BFD_TTL_VAL
;
239 iph
->protocol
= IPPROTO_UDP
;
240 memcpy(&iph
->saddr
, &bfd
->local_address
.sa_sin
.sin_addr
,
241 sizeof(bfd
->local_address
.sa_sin
.sin_addr
));
242 memcpy(&iph
->daddr
, &bfd
->local_address
.sa_sin
.sin_addr
,
243 sizeof(bfd
->local_address
.sa_sin
.sin_addr
));
244 total_len
+= sizeof(struct iphdr
);
247 uh
= (struct udphdr
*)(sendbuff
+ sizeof(struct iphdr
) +
248 sizeof(struct ethhdr
));
249 uh
->source
= htons(BFD_DEF_ECHO_PORT
);
250 uh
->dest
= htons(BFD_DEF_ECHO_PORT
);
252 total_len
+= sizeof(struct udphdr
);
255 beph
= (struct bfd_echo_pkt
*)(sendbuff
+ sizeof(struct udphdr
) +
256 sizeof(struct iphdr
) +
257 sizeof(struct ethhdr
));
259 beph
->ver
= BFD_ECHO_VERSION
;
260 beph
->len
= BFD_ECHO_PKT_LEN
;
261 beph
->my_discr
= htonl(bfd
->discrs
.my_discr
);
263 /* RTT calculation: add starting time in packet */
264 monotime(&time_sent
);
265 beph
->time_sent_sec
= htobe64(time_sent
.tv_sec
);
266 beph
->time_sent_usec
= htobe64(time_sent
.tv_usec
);
268 total_len
+= sizeof(struct bfd_echo_pkt
);
270 htons(total_len
- sizeof(struct iphdr
) - sizeof(struct ethhdr
));
271 uh
->check
= bfd_pkt_checksum(
272 uh
, (total_len
- sizeof(struct iphdr
) - sizeof(struct ethhdr
)),
273 (struct in6_addr
*)&iph
->saddr
, AF_INET
);
275 iph
->tot_len
= htons(total_len
- sizeof(struct ethhdr
));
276 iph
->check
= in_cksum((const void *)iph
, sizeof(struct iphdr
));
278 if (bp_udp_send_fp(sd
, (uint8_t *)&sendbuff
, total_len
, bfd
) == -1)
281 bfd
->stats
.tx_echo_pkt
++;
285 void ptm_bfd_echo_snd(struct bfd_session
*bfd
)
290 struct bfd_echo_pkt bep
;
291 struct sockaddr_in sin
;
292 struct sockaddr_in6 sin6
;
293 struct bfd_vrf_global
*bvrf
= bfd_vrf_look_by_session(bfd
);
297 if (!CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_ECHO_ACTIVE
))
298 SET_FLAG(bfd
->flags
, BFD_SESS_FLAG_ECHO_ACTIVE
);
300 memset(&bep
, 0, sizeof(bep
));
301 bep
.ver
= BFD_ECHO_VERSION
;
302 bep
.len
= BFD_ECHO_PKT_LEN
;
303 bep
.my_discr
= htonl(bfd
->discrs
.my_discr
);
305 if (CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_IPV6
)) {
306 if (bvrf
->bg_echov6
== -1)
308 sd
= bvrf
->bg_echov6
;
309 memset(&sin6
, 0, sizeof(sin6
));
310 sin6
.sin6_family
= AF_INET6
;
311 memcpy(&sin6
.sin6_addr
, &bfd
->key
.peer
, sizeof(sin6
.sin6_addr
));
312 if (bfd
->ifp
&& IN6_IS_ADDR_LINKLOCAL(&sin6
.sin6_addr
))
313 sin6
.sin6_scope_id
= bfd
->ifp
->ifindex
;
315 sin6
.sin6_port
= htons(BFD_DEF_ECHO_PORT
);
316 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
317 sin6
.sin6_len
= sizeof(sin6
);
318 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
320 sa
= (struct sockaddr
*)&sin6
;
321 salen
= sizeof(sin6
);
324 memset(&sin
, 0, sizeof(sin
));
325 sin
.sin_family
= AF_INET
;
326 memcpy(&sin
.sin_addr
, &bfd
->key
.peer
, sizeof(sin
.sin_addr
));
327 sin
.sin_port
= htons(BFD_DEF_ECHO_PORT
);
328 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
329 sin
.sin_len
= sizeof(sin
);
330 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
332 sa
= (struct sockaddr
*)&sin
;
335 if (bp_udp_send(sd
, BFD_TTL_VAL
, (uint8_t *)&bep
, sizeof(bep
), sa
,
340 bfd
->stats
.tx_echo_pkt
++;
343 static int ptm_bfd_process_echo_pkt(struct bfd_vrf_global
*bvrf
, int s
)
345 struct bfd_session
*bfd
;
346 uint32_t my_discr
= 0;
350 /* Receive and parse echo packet. */
351 if (bp_bfd_echo_in(bvrf
, s
, &ttl
, &my_discr
, &my_rtt
) == -1)
354 /* Your discriminator not zero - use it to find session */
355 bfd
= bfd_id_lookup(my_discr
);
357 if (bglobal
.debug_network
)
358 zlog_debug("echo-packet: no matching session (id:%u)",
363 if (!CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_ECHO_ACTIVE
)) {
364 if (bglobal
.debug_network
)
365 zlog_debug("echo-packet: echo disabled [%s] (id:%u)",
366 bs_to_string(bfd
), my_discr
);
370 /* RTT Calculation: add current RTT to samples */
372 bfd
->rtt
[bfd
->rtt_index
] = my_rtt
;
374 if (bfd
->rtt_index
>= BFD_RTT_SAMPLE
)
376 if (bfd
->rtt_valid
< BFD_RTT_SAMPLE
)
380 bfd
->stats
.rx_echo_pkt
++;
382 /* Compute detect time */
383 bfd
->echo_detect_TO
= bfd
->remote_detect_mult
* bfd
->echo_xmt_TO
;
385 /* Update echo receive timeout. */
386 if (bfd
->echo_detect_TO
> 0)
387 bfd_echo_recvtimer_update(bfd
);
392 void ptm_bfd_snd(struct bfd_session
*bfd
, int fbit
)
394 struct bfd_pkt cp
= {};
396 /* Set fields according to section 6.5.7 */
397 cp
.diag
= bfd
->local_diag
;
398 BFD_SETVER(cp
.diag
, BFD_VERSION
);
400 BFD_SETSTATE(cp
.flags
, bfd
->ses_state
);
402 if (CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_CBIT
))
403 BFD_SETCBIT(cp
.flags
, BFD_CBIT
);
405 BFD_SETDEMANDBIT(cp
.flags
, BFD_DEF_DEMAND
);
408 * Polling and Final can't be set at the same time.
410 * RFC 5880, Section 6.5.
412 BFD_SETFBIT(cp
.flags
, fbit
);
414 BFD_SETPBIT(cp
.flags
, bfd
->polling
);
416 cp
.detect_mult
= bfd
->detect_mult
;
417 cp
.len
= BFD_PKT_LEN
;
418 cp
.discrs
.my_discr
= htonl(bfd
->discrs
.my_discr
);
419 cp
.discrs
.remote_discr
= htonl(bfd
->discrs
.remote_discr
);
421 cp
.timers
.desired_min_tx
=
422 htonl(bfd
->timers
.desired_min_tx
);
423 cp
.timers
.required_min_rx
=
424 htonl(bfd
->timers
.required_min_rx
);
427 * We can only announce current setting on poll, this
428 * avoids timing mismatch with our peer and give it
429 * the oportunity to learn. See `bs_final_handler` for
432 cp
.timers
.desired_min_tx
=
433 htonl(bfd
->cur_timers
.desired_min_tx
);
434 cp
.timers
.required_min_rx
=
435 htonl(bfd
->cur_timers
.required_min_rx
);
437 cp
.timers
.required_min_echo
= htonl(bfd
->timers
.required_min_echo_rx
);
439 if (_ptm_bfd_send(bfd
, NULL
, &cp
, BFD_PKT_LEN
) != 0)
442 bfd
->stats
.tx_ctrl_pkt
++;
447 * receive the ipv4 echo packet that was loopback in the peers forwarding plane
449 ssize_t
bfd_recv_ipv4_fp(int sd
, uint8_t *msgbuf
, size_t msgbuflen
,
450 uint8_t *ttl
, ifindex_t
*ifindex
,
451 struct sockaddr_any
*local
, struct sockaddr_any
*peer
)
454 struct sockaddr_ll msgaddr
;
455 struct msghdr msghdr
;
457 uint16_t recv_checksum
;
462 /* Prepare the recvmsg params. */
463 iov
[0].iov_base
= msgbuf
;
464 iov
[0].iov_len
= msgbuflen
;
466 memset(&msghdr
, 0, sizeof(msghdr
));
467 msghdr
.msg_name
= &msgaddr
;
468 msghdr
.msg_namelen
= sizeof(msgaddr
);
469 msghdr
.msg_iov
= iov
;
470 msghdr
.msg_iovlen
= 1;
472 mlen
= recvmsg(sd
, &msghdr
, MSG_DONTWAIT
);
474 if (errno
!= EAGAIN
|| errno
!= EWOULDBLOCK
|| errno
!= EINTR
)
475 zlog_err("%s: recv failed: %s", __func__
,
481 ip
= (struct iphdr
*)(msgbuf
+ sizeof(struct ethhdr
));
483 /* verify ip checksum */
484 recv_checksum
= ip
->check
;
486 checksum
= in_cksum((const void *)ip
, sizeof(struct iphdr
));
487 if (recv_checksum
!= checksum
) {
488 if (bglobal
.debug_network
)
490 "%s: invalid iphdr checksum expected 0x%x rcvd 0x%x",
491 __func__
, checksum
, recv_checksum
);
497 /* Echo should be looped in peer's forwarding plane, but it also
498 * comes up to BFD so silently drop it
500 if (ip
->daddr
== ip
->saddr
)
503 if (bglobal
.debug_network
)
504 zlog_debug("%s: invalid TTL: %u", __func__
, *ttl
);
508 local
->sa_sin
.sin_family
= AF_INET
;
509 memcpy(&local
->sa_sin
.sin_addr
, &ip
->saddr
, sizeof(ip
->saddr
));
510 peer
->sa_sin
.sin_family
= AF_INET
;
511 memcpy(&peer
->sa_sin
.sin_addr
, &ip
->daddr
, sizeof(ip
->daddr
));
513 *ifindex
= msgaddr
.sll_ifindex
;
515 /* verify udp checksum */
516 uh
= (struct udphdr
*)(msgbuf
+ sizeof(struct iphdr
) +
517 sizeof(struct ethhdr
));
518 recv_checksum
= uh
->check
;
520 checksum
= bfd_pkt_checksum(uh
, ntohs(uh
->len
),
521 (struct in6_addr
*)&ip
->saddr
, AF_INET
);
522 if (recv_checksum
!= checksum
) {
523 if (bglobal
.debug_network
)
525 "%s: invalid udphdr checksum expected 0x%x rcvd 0x%x",
526 __func__
, checksum
, recv_checksum
);
533 ssize_t
bfd_recv_ipv4(int sd
, uint8_t *msgbuf
, size_t msgbuflen
, uint8_t *ttl
,
534 ifindex_t
*ifindex
, struct sockaddr_any
*local
,
535 struct sockaddr_any
*peer
)
539 struct sockaddr_in msgaddr
;
540 struct msghdr msghdr
;
542 uint8_t cmsgbuf
[255];
544 /* Prepare the recvmsg params. */
545 iov
[0].iov_base
= msgbuf
;
546 iov
[0].iov_len
= msgbuflen
;
548 memset(&msghdr
, 0, sizeof(msghdr
));
549 msghdr
.msg_name
= &msgaddr
;
550 msghdr
.msg_namelen
= sizeof(msgaddr
);
551 msghdr
.msg_iov
= iov
;
552 msghdr
.msg_iovlen
= 1;
553 msghdr
.msg_control
= cmsgbuf
;
554 msghdr
.msg_controllen
= sizeof(cmsgbuf
);
556 mlen
= recvmsg(sd
, &msghdr
, MSG_DONTWAIT
);
559 zlog_err("ipv4-recv: recv failed: %s", strerror(errno
));
564 /* Get source address */
565 peer
->sa_sin
= *((struct sockaddr_in
*)(msghdr
.msg_name
));
567 /* Get and check TTL */
568 for (cm
= CMSG_FIRSTHDR(&msghdr
); cm
!= NULL
;
569 cm
= CMSG_NXTHDR(&msghdr
, cm
)) {
570 if (cm
->cmsg_level
!= IPPROTO_IP
)
573 switch (cm
->cmsg_type
) {
578 memcpy(&ttlval
, CMSG_DATA(cm
), sizeof(ttlval
));
580 if (bglobal
.debug_network
)
581 zlog_debug("ipv4-recv: invalid TTL: %u",
590 struct in_pktinfo
*pi
=
591 (struct in_pktinfo
*)CMSG_DATA(cm
);
596 local
->sa_sin
.sin_family
= AF_INET
;
597 local
->sa_sin
.sin_addr
= pi
->ipi_addr
;
598 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
599 local
->sa_sin
.sin_len
= sizeof(local
->sa_sin
);
600 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
602 *ifindex
= pi
->ipi_ifindex
;
605 #endif /* BFD_LINUX */
608 memcpy(ttl
, CMSG_DATA(cm
), sizeof(*ttl
));
612 case IP_RECVDSTADDR
: {
615 memcpy(&ia
, CMSG_DATA(cm
), sizeof(ia
));
616 local
->sa_sin
.sin_family
= AF_INET
;
617 local
->sa_sin
.sin_addr
= ia
;
618 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
619 local
->sa_sin
.sin_len
= sizeof(local
->sa_sin
);
620 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
627 * On *BSDs we expect to land here when skipping
628 * the IP_RECVIF header. It will be handled by
629 * getsockopt_ifindex() below.
636 /* OS agnostic way of getting interface name. */
637 if (*ifindex
== IFINDEX_INTERNAL
)
638 *ifindex
= getsockopt_ifindex(AF_INET
, &msghdr
);
643 ssize_t
bfd_recv_ipv6(int sd
, uint8_t *msgbuf
, size_t msgbuflen
, uint8_t *ttl
,
644 ifindex_t
*ifindex
, struct sockaddr_any
*local
,
645 struct sockaddr_any
*peer
)
648 struct in6_pktinfo
*pi6
= NULL
;
651 struct sockaddr_in6 msgaddr6
;
652 struct msghdr msghdr6
;
654 uint8_t cmsgbuf6
[255];
656 /* Prepare the recvmsg params. */
657 iov
[0].iov_base
= msgbuf
;
658 iov
[0].iov_len
= msgbuflen
;
660 memset(&msghdr6
, 0, sizeof(msghdr6
));
661 msghdr6
.msg_name
= &msgaddr6
;
662 msghdr6
.msg_namelen
= sizeof(msgaddr6
);
663 msghdr6
.msg_iov
= iov
;
664 msghdr6
.msg_iovlen
= 1;
665 msghdr6
.msg_control
= cmsgbuf6
;
666 msghdr6
.msg_controllen
= sizeof(cmsgbuf6
);
668 mlen
= recvmsg(sd
, &msghdr6
, MSG_DONTWAIT
);
671 zlog_err("ipv6-recv: recv failed: %s", strerror(errno
));
676 /* Get source address */
677 peer
->sa_sin6
= *((struct sockaddr_in6
*)(msghdr6
.msg_name
));
679 /* Get and check TTL */
680 for (cm
= CMSG_FIRSTHDR(&msghdr6
); cm
!= NULL
;
681 cm
= CMSG_NXTHDR(&msghdr6
, cm
)) {
682 if (cm
->cmsg_level
!= IPPROTO_IPV6
)
685 if (cm
->cmsg_type
== IPV6_HOPLIMIT
) {
686 memcpy(&ttlval
, CMSG_DATA(cm
), sizeof(ttlval
));
688 if (bglobal
.debug_network
)
689 zlog_debug("ipv6-recv: invalid TTL: %u",
695 } else if (cm
->cmsg_type
== IPV6_PKTINFO
) {
696 pi6
= (struct in6_pktinfo
*)CMSG_DATA(cm
);
698 local
->sa_sin6
.sin6_family
= AF_INET6
;
699 local
->sa_sin6
.sin6_addr
= pi6
->ipi6_addr
;
700 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
701 local
->sa_sin6
.sin6_len
= sizeof(local
->sa_sin6
);
702 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
704 *ifindex
= pi6
->ipi6_ifindex
;
706 /* Set scope ID for link local addresses. */
707 if (IN6_IS_ADDR_LINKLOCAL(
708 &peer
->sa_sin6
.sin6_addr
))
709 peer
->sa_sin6
.sin6_scope_id
= *ifindex
;
710 if (IN6_IS_ADDR_LINKLOCAL(
711 &local
->sa_sin6
.sin6_addr
))
712 local
->sa_sin6
.sin6_scope_id
= *ifindex
;
720 static void bfd_sd_reschedule(struct bfd_vrf_global
*bvrf
, int sd
)
722 if (sd
== bvrf
->bg_shop
) {
723 THREAD_OFF(bvrf
->bg_ev
[0]);
724 thread_add_read(master
, bfd_recv_cb
, bvrf
, bvrf
->bg_shop
,
726 } else if (sd
== bvrf
->bg_mhop
) {
727 THREAD_OFF(bvrf
->bg_ev
[1]);
728 thread_add_read(master
, bfd_recv_cb
, bvrf
, bvrf
->bg_mhop
,
730 } else if (sd
== bvrf
->bg_shop6
) {
731 THREAD_OFF(bvrf
->bg_ev
[2]);
732 thread_add_read(master
, bfd_recv_cb
, bvrf
, bvrf
->bg_shop6
,
734 } else if (sd
== bvrf
->bg_mhop6
) {
735 THREAD_OFF(bvrf
->bg_ev
[3]);
736 thread_add_read(master
, bfd_recv_cb
, bvrf
, bvrf
->bg_mhop6
,
738 } else if (sd
== bvrf
->bg_echo
) {
739 THREAD_OFF(bvrf
->bg_ev
[4]);
740 thread_add_read(master
, bfd_recv_cb
, bvrf
, bvrf
->bg_echo
,
742 } else if (sd
== bvrf
->bg_echov6
) {
743 THREAD_OFF(bvrf
->bg_ev
[5]);
744 thread_add_read(master
, bfd_recv_cb
, bvrf
, bvrf
->bg_echov6
,
749 static void cp_debug(bool mhop
, struct sockaddr_any
*peer
,
750 struct sockaddr_any
*local
, ifindex_t ifindex
,
751 vrf_id_t vrfid
, const char *fmt
, ...)
753 char buf
[512], peerstr
[128], localstr
[128], portstr
[64], vrfstr
[64];
756 /* Don't to any processing if debug is disabled. */
757 if (bglobal
.debug_network
== false)
760 if (peer
->sa_sin
.sin_family
)
761 snprintf(peerstr
, sizeof(peerstr
), " peer:%s", satostr(peer
));
765 if (local
->sa_sin
.sin_family
)
766 snprintf(localstr
, sizeof(localstr
), " local:%s",
771 if (ifindex
!= IFINDEX_INTERNAL
)
772 snprintf(portstr
, sizeof(portstr
), " port:%u", ifindex
);
776 if (vrfid
!= VRF_DEFAULT
)
777 snprintf(vrfstr
, sizeof(vrfstr
), " vrf:%u", vrfid
);
782 vsnprintf(buf
, sizeof(buf
), fmt
, vl
);
785 zlog_debug("control-packet: %s [mhop:%s%s%s%s%s]", buf
,
786 mhop
? "yes" : "no", peerstr
, localstr
, portstr
, vrfstr
);
789 void bfd_recv_cb(struct thread
*t
)
791 int sd
= THREAD_FD(t
);
792 struct bfd_session
*bfd
;
798 ifindex_t ifindex
= IFINDEX_INTERNAL
;
799 struct sockaddr_any local
, peer
;
800 uint8_t msgbuf
[1516];
801 struct interface
*ifp
= NULL
;
802 struct bfd_vrf_global
*bvrf
= THREAD_ARG(t
);
804 /* Schedule next read. */
805 bfd_sd_reschedule(bvrf
, sd
);
807 /* Handle echo packets. */
808 if (sd
== bvrf
->bg_echo
|| sd
== bvrf
->bg_echov6
) {
809 ptm_bfd_process_echo_pkt(bvrf
, sd
);
813 /* Sanitize input/output. */
814 memset(&local
, 0, sizeof(local
));
815 memset(&peer
, 0, sizeof(peer
));
817 /* Handle control packets. */
819 if (sd
== bvrf
->bg_shop
|| sd
== bvrf
->bg_mhop
) {
820 is_mhop
= sd
== bvrf
->bg_mhop
;
821 mlen
= bfd_recv_ipv4(sd
, msgbuf
, sizeof(msgbuf
), &ttl
, &ifindex
,
823 } else if (sd
== bvrf
->bg_shop6
|| sd
== bvrf
->bg_mhop6
) {
824 is_mhop
= sd
== bvrf
->bg_mhop6
;
825 mlen
= bfd_recv_ipv6(sd
, msgbuf
, sizeof(msgbuf
), &ttl
, &ifindex
,
830 * With netns backend, we have a separate socket in each VRF. It means
831 * that bvrf here is correct and we believe the bvrf->vrf->vrf_id.
832 * With VRF-lite backend, we have a single socket in the default VRF.
833 * It means that we can't believe the bvrf->vrf->vrf_id. But in
834 * VRF-lite, the ifindex is globally unique, so we can retrieve the
835 * correct vrf_id from the interface.
837 vrfid
= bvrf
->vrf
->vrf_id
;
839 ifp
= if_lookup_by_index(ifindex
, vrfid
);
841 vrfid
= ifp
->vrf
->vrf_id
;
844 /* Implement RFC 5880 6.8.6 */
845 if (mlen
< BFD_PKT_LEN
) {
846 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
,
847 "too small (%ld bytes)", mlen
);
851 /* Validate single hop packet TTL. */
852 if ((!is_mhop
) && (ttl
!= BFD_TTL_VAL
)) {
853 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
,
854 "invalid TTL: %d expected %d", ttl
, BFD_TTL_VAL
);
859 * Parse the control header for inconsistencies:
861 * - Bad multiplier configuration;
863 * - Invalid discriminator;
865 cp
= (struct bfd_pkt
*)(msgbuf
);
866 if (BFD_GETVER(cp
->diag
) != BFD_VERSION
) {
867 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
,
868 "bad version %d", BFD_GETVER(cp
->diag
));
872 if (cp
->detect_mult
== 0) {
873 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
,
874 "detect multiplier set to zero");
878 if ((cp
->len
< BFD_PKT_LEN
) || (cp
->len
> mlen
)) {
879 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
, "too small");
883 if (cp
->discrs
.my_discr
== 0) {
884 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
,
885 "'my discriminator' is zero");
889 /* Find the session that this packet belongs. */
890 bfd
= ptm_bfd_sess_find(cp
, &peer
, &local
, ifp
, vrfid
, is_mhop
);
892 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
,
897 /* Ensure that existing good sessions are not overridden. */
898 if (!cp
->discrs
.remote_discr
&& bfd
->ses_state
!= PTM_BFD_DOWN
&&
899 bfd
->ses_state
!= PTM_BFD_ADM_DOWN
) {
900 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
,
901 "'remote discriminator' is zero, not overridden");
906 * Multi hop: validate packet TTL.
907 * Single hop: set local address that received the packet.
908 * set peers mac address for echo packets
911 if (ttl
< bfd
->mh_ttl
) {
912 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
,
913 "exceeded max hop count (expected %d, got %d)",
919 if (bfd
->local_address
.sa_sin
.sin_family
== AF_UNSPEC
)
920 bfd
->local_address
= local
;
923 bfd_peer_mac_set(sd
, bfd
, &peer
, ifp
);
927 bfd
->stats
.rx_ctrl_pkt
++;
930 * If no interface was detected, save the interface where the
933 if (!is_mhop
&& bfd
->ifp
== NULL
)
936 /* Log remote discriminator changes. */
937 if ((bfd
->discrs
.remote_discr
!= 0)
938 && (bfd
->discrs
.remote_discr
!= ntohl(cp
->discrs
.my_discr
)))
939 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
,
940 "remote discriminator mismatch (expected %u, got %u)",
941 bfd
->discrs
.remote_discr
, ntohl(cp
->discrs
.my_discr
));
943 bfd
->discrs
.remote_discr
= ntohl(cp
->discrs
.my_discr
);
945 /* Save remote diagnostics before state switch. */
946 bfd
->remote_diag
= cp
->diag
& BFD_DIAGMASK
;
948 /* Update remote timers settings. */
949 bfd
->remote_timers
.desired_min_tx
= ntohl(cp
->timers
.desired_min_tx
);
950 bfd
->remote_timers
.required_min_rx
= ntohl(cp
->timers
.required_min_rx
);
951 bfd
->remote_timers
.required_min_echo
=
952 ntohl(cp
->timers
.required_min_echo
);
953 bfd
->remote_detect_mult
= cp
->detect_mult
;
955 if (BFD_GETCBIT(cp
->flags
))
956 bfd
->remote_cbit
= 1;
958 bfd
->remote_cbit
= 0;
960 /* State switch from section 6.2. */
961 bs_state_handler(bfd
, BFD_GETSTATE(cp
->flags
));
963 /* RFC 5880, Section 6.5: handle POLL/FINAL negotiation sequence. */
964 if (bfd
->polling
&& BFD_GETFBIT(cp
->flags
)) {
965 /* Disable polling. */
968 /* Handle poll finalization. */
969 bs_final_handler(bfd
);
973 * Detection timeout calculation:
974 * The minimum detection timeout is the remote detection
975 * multipler (number of packets to be missed) times the agreed
976 * transmission interval.
978 * RFC 5880, Section 6.8.4.
980 if (bfd
->cur_timers
.required_min_rx
> bfd
->remote_timers
.desired_min_tx
)
981 bfd
->detect_TO
= bfd
->remote_detect_mult
982 * bfd
->cur_timers
.required_min_rx
;
984 bfd
->detect_TO
= bfd
->remote_detect_mult
985 * bfd
->remote_timers
.desired_min_tx
;
987 /* Apply new receive timer immediately. */
988 bfd_recvtimer_update(bfd
);
990 /* Handle echo timers changes. */
991 bs_echo_timer_handler(bfd
);
994 * We've received a packet with the POLL bit set, we must send
995 * a control packet back with the FINAL bit set.
997 * RFC 5880, Section 6.5.
999 if (BFD_GETPBIT(cp
->flags
)) {
1000 /* We are finalizing a poll negotiation. */
1001 bs_final_handler(bfd
);
1003 /* Send the control packet with the final bit immediately. */
1004 ptm_bfd_snd(bfd
, 1);
1009 * bp_bfd_echo_in: proccesses an BFD echo packet. On TTL == BFD_TTL_VAL
1010 * the packet is looped back or returns the my discriminator ID along
1013 * Returns -1 on error or loopback or 0 on success.
1015 int bp_bfd_echo_in(struct bfd_vrf_global
*bvrf
, int sd
, uint8_t *ttl
,
1016 uint32_t *my_discr
, uint64_t *my_rtt
)
1018 struct bfd_echo_pkt
*bep
;
1020 struct sockaddr_any local
, peer
;
1021 ifindex_t ifindex
= IFINDEX_INTERNAL
;
1022 vrf_id_t vrfid
= VRF_DEFAULT
;
1023 uint8_t msgbuf
[1516];
1024 size_t bfd_offset
= 0;
1026 if (sd
== bvrf
->bg_echo
) {
1028 rlen
= bfd_recv_ipv4_fp(sd
, msgbuf
, sizeof(msgbuf
), ttl
,
1029 &ifindex
, &local
, &peer
);
1031 /* silently drop echo packet that is looped in fastpath but
1032 * still comes up to BFD
1036 bfd_offset
= sizeof(struct udphdr
) + sizeof(struct iphdr
) +
1037 sizeof(struct ethhdr
);
1039 rlen
= bfd_recv_ipv4(sd
, msgbuf
, sizeof(msgbuf
), ttl
, &ifindex
,
1044 rlen
= bfd_recv_ipv6(sd
, msgbuf
, sizeof(msgbuf
), ttl
, &ifindex
,
1049 /* Short packet, better not risk reading it. */
1050 if (rlen
< (ssize_t
)sizeof(*bep
)) {
1051 cp_debug(false, &peer
, &local
, ifindex
, vrfid
,
1052 "small echo packet");
1056 /* Test for loopback for ipv6, ipv4 is looped in forwarding plane */
1057 if ((*ttl
== BFD_TTL_VAL
) && (sd
== bvrf
->bg_echov6
)) {
1058 bp_udp_send(sd
, *ttl
- 1, msgbuf
, rlen
,
1059 (struct sockaddr
*)&peer
,
1060 (sd
== bvrf
->bg_echo
) ? sizeof(peer
.sa_sin
)
1061 : sizeof(peer
.sa_sin6
));
1065 /* Read my discriminator from BFD Echo packet. */
1066 bep
= (struct bfd_echo_pkt
*)(msgbuf
+ bfd_offset
);
1067 *my_discr
= ntohl(bep
->my_discr
);
1068 if (*my_discr
== 0) {
1069 cp_debug(false, &peer
, &local
, ifindex
, vrfid
,
1070 "invalid echo packet discriminator (zero)");
1075 /* RTT Calculation: determine RTT time of IPv4 echo pkt */
1076 if (sd
== bvrf
->bg_echo
) {
1077 struct timeval time_sent
= {0, 0};
1079 time_sent
.tv_sec
= be64toh(bep
->time_sent_sec
);
1080 time_sent
.tv_usec
= be64toh(bep
->time_sent_usec
);
1081 *my_rtt
= monotime_since(&time_sent
, NULL
);
1090 * send a bfd packet with src/dst same IP so that the peer will receive
1091 * the packet and forward it back to sender in the forwarding plane
1093 int bp_udp_send_fp(int sd
, uint8_t *data
, size_t datalen
,
1094 struct bfd_session
*bfd
)
1097 struct msghdr msg
= {0};
1098 struct iovec iov
[1];
1099 uint8_t msgctl
[255];
1100 struct sockaddr_ll sadr_ll
= {0};
1102 sadr_ll
.sll_ifindex
= bfd
->ifp
->ifindex
;
1103 sadr_ll
.sll_halen
= ETH_ALEN
;
1104 memcpy(sadr_ll
.sll_addr
, bfd
->peer_hw_addr
, sizeof(bfd
->peer_hw_addr
));
1105 sadr_ll
.sll_protocol
= htons(ETH_P_IP
);
1107 /* Prepare message data. */
1108 iov
[0].iov_base
= data
;
1109 iov
[0].iov_len
= datalen
;
1111 memset(msgctl
, 0, sizeof(msgctl
));
1112 msg
.msg_name
= &sadr_ll
;
1113 msg
.msg_namelen
= sizeof(sadr_ll
);
1117 /* Send echo to peer */
1118 wlen
= sendmsg(sd
, &msg
, 0);
1121 if (bglobal
.debug_network
)
1122 zlog_debug("udp-send: loopback failure: (%d) %s", errno
,
1125 } else if (wlen
< (ssize_t
)datalen
) {
1126 if (bglobal
.debug_network
)
1127 zlog_debug("udp-send: partial send: %zd expected %zu",
1136 int bp_udp_send(int sd
, uint8_t ttl
, uint8_t *data
, size_t datalen
,
1137 struct sockaddr
*to
, socklen_t tolen
)
1139 struct cmsghdr
*cmsg
;
1142 bool is_ipv6
= to
->sa_family
== AF_INET6
;
1144 struct iovec iov
[1];
1145 uint8_t msgctl
[255];
1147 /* Prepare message data. */
1148 iov
[0].iov_base
= data
;
1149 iov
[0].iov_len
= datalen
;
1151 memset(&msg
, 0, sizeof(msg
));
1152 memset(msgctl
, 0, sizeof(msgctl
));
1154 msg
.msg_namelen
= tolen
;
1158 /* Prepare the packet TTL information. */
1160 /* Use ancillary data. */
1161 msg
.msg_control
= msgctl
;
1162 msg
.msg_controllen
= CMSG_LEN(sizeof(ttlval
));
1164 /* Configure the ancillary data. */
1165 cmsg
= CMSG_FIRSTHDR(&msg
);
1166 cmsg
->cmsg_len
= CMSG_LEN(sizeof(ttlval
));
1168 cmsg
->cmsg_level
= IPPROTO_IPV6
;
1169 cmsg
->cmsg_type
= IPV6_HOPLIMIT
;
1172 cmsg
->cmsg_level
= IPPROTO_IP
;
1173 cmsg
->cmsg_type
= IP_TTL
;
1175 /* FreeBSD does not support TTL in ancillary data. */
1176 msg
.msg_control
= NULL
;
1177 msg
.msg_controllen
= 0;
1179 bp_set_ttl(sd
, ttl
);
1180 #endif /* BFD_BSD */
1182 memcpy(CMSG_DATA(cmsg
), &ttlval
, sizeof(ttlval
));
1185 /* Send echo back. */
1186 wlen
= sendmsg(sd
, &msg
, 0);
1188 if (bglobal
.debug_network
)
1189 zlog_debug("udp-send: loopback failure: (%d) %s", errno
,
1192 } else if (wlen
< (ssize_t
)datalen
) {
1193 if (bglobal
.debug_network
)
1194 zlog_debug("udp-send: partial send: %zd expected %zu",
1211 int bp_set_ttl(int sd
, uint8_t value
)
1215 if (setsockopt(sd
, IPPROTO_IP
, IP_TTL
, &ttl
, sizeof(ttl
)) == -1) {
1216 zlog_warn("set-ttl: setsockopt(IP_TTL, %d): %s", value
,
1224 int bp_set_tos(int sd
, uint8_t value
)
1228 if (setsockopt(sd
, IPPROTO_IP
, IP_TOS
, &tos
, sizeof(tos
)) == -1) {
1229 zlog_warn("set-tos: setsockopt(IP_TOS, %d): %s", value
,
1237 static void bp_set_ipopts(int sd
)
1239 int rcvttl
= BFD_RCV_TTL_VAL
;
1241 if (bp_set_ttl(sd
, BFD_TTL_VAL
) != 0)
1242 zlog_fatal("set-ipopts: TTL configuration failed");
1244 if (setsockopt(sd
, IPPROTO_IP
, IP_RECVTTL
, &rcvttl
, sizeof(rcvttl
))
1246 zlog_fatal("set-ipopts: setsockopt(IP_RECVTTL, %d): %s", rcvttl
,
1250 int pktinfo
= BFD_PKT_INFO_VAL
;
1252 /* Figure out address and interface to do the peer matching. */
1253 if (setsockopt(sd
, IPPROTO_IP
, IP_PKTINFO
, &pktinfo
, sizeof(pktinfo
))
1255 zlog_fatal("set-ipopts: setsockopt(IP_PKTINFO, %d): %s",
1256 pktinfo
, strerror(errno
));
1257 #endif /* BFD_LINUX */
1261 /* Find out our address for peer matching. */
1262 if (setsockopt(sd
, IPPROTO_IP
, IP_RECVDSTADDR
, &yes
, sizeof(yes
)) == -1)
1263 zlog_fatal("set-ipopts: setsockopt(IP_RECVDSTADDR, %d): %s",
1264 yes
, strerror(errno
));
1266 /* Find out interface where the packet came in. */
1267 if (setsockopt_ifindex(AF_INET
, sd
, yes
) == -1)
1268 zlog_fatal("set-ipopts: setsockopt_ipv4_ifindex(%d): %s", yes
,
1270 #endif /* BFD_BSD */
1273 static void bp_bind_ip(int sd
, uint16_t port
)
1275 struct sockaddr_in sin
;
1277 memset(&sin
, 0, sizeof(sin
));
1278 sin
.sin_family
= AF_INET
;
1279 sin
.sin_addr
.s_addr
= htonl(INADDR_ANY
);
1280 sin
.sin_port
= htons(port
);
1281 if (bind(sd
, (struct sockaddr
*)&sin
, sizeof(sin
)) == -1)
1282 zlog_fatal("bind-ip: bind: %s", strerror(errno
));
1285 int bp_udp_shop(const struct vrf
*vrf
)
1289 frr_with_privs(&bglobal
.bfdd_privs
) {
1290 sd
= vrf_socket(AF_INET
, SOCK_DGRAM
, PF_UNSPEC
, vrf
->vrf_id
,
1294 zlog_fatal("udp-shop: socket: %s", strerror(errno
));
1297 bp_bind_ip(sd
, BFD_DEFDESTPORT
);
1301 int bp_udp_mhop(const struct vrf
*vrf
)
1305 frr_with_privs(&bglobal
.bfdd_privs
) {
1306 sd
= vrf_socket(AF_INET
, SOCK_DGRAM
, PF_UNSPEC
, vrf
->vrf_id
,
1310 zlog_fatal("udp-mhop: socket: %s", strerror(errno
));
1313 bp_bind_ip(sd
, BFD_DEF_MHOP_DEST_PORT
);
1318 int bp_peer_socket(const struct bfd_session
*bs
)
1321 struct sockaddr_in sin
;
1322 static int srcPort
= BFD_SRCPORTINIT
;
1323 const char *device_to_bind
= NULL
;
1325 if (bs
->key
.ifname
[0])
1326 device_to_bind
= (const char *)bs
->key
.ifname
;
1327 else if ((!vrf_is_backend_netns() && bs
->vrf
->vrf_id
!= VRF_DEFAULT
)
1328 || ((CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_MH
)
1329 && bs
->key
.vrfname
[0])))
1330 device_to_bind
= (const char *)bs
->key
.vrfname
;
1332 frr_with_privs(&bglobal
.bfdd_privs
) {
1333 sd
= vrf_socket(AF_INET
, SOCK_DGRAM
, PF_UNSPEC
,
1334 bs
->vrf
->vrf_id
, device_to_bind
);
1337 zlog_err("ipv4-new: failed to create socket: %s",
1342 /* Set TTL to 255 for all transmitted packets */
1343 if (bp_set_ttl(sd
, BFD_TTL_VAL
) != 0) {
1348 /* Set TOS to CS6 for all transmitted packets */
1349 if (bp_set_tos(sd
, BFD_TOS_VAL
) != 0) {
1354 /* Find an available source port in the proper range */
1355 memset(&sin
, 0, sizeof(sin
));
1356 sin
.sin_family
= AF_INET
;
1357 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1358 sin
.sin_len
= sizeof(sin
);
1359 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1360 memcpy(&sin
.sin_addr
, &bs
->key
.local
, sizeof(sin
.sin_addr
));
1361 if (CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_MH
) == 0)
1362 sin
.sin_addr
.s_addr
= INADDR_ANY
;
1366 if ((++pcount
) > (BFD_SRCPORTMAX
- BFD_SRCPORTINIT
)) {
1367 /* Searched all ports, none available */
1368 zlog_err("ipv4-new: failed to bind port: %s",
1373 if (srcPort
>= BFD_SRCPORTMAX
)
1374 srcPort
= BFD_SRCPORTINIT
;
1375 sin
.sin_port
= htons(srcPort
++);
1376 } while (bind(sd
, (struct sockaddr
*)&sin
, sizeof(sin
)) < 0);
1386 int bp_peer_socketv6(const struct bfd_session
*bs
)
1389 struct sockaddr_in6 sin6
;
1390 static int srcPort
= BFD_SRCPORTINIT
;
1391 const char *device_to_bind
= NULL
;
1393 if (bs
->key
.ifname
[0])
1394 device_to_bind
= (const char *)bs
->key
.ifname
;
1395 else if ((!vrf_is_backend_netns() && bs
->vrf
->vrf_id
!= VRF_DEFAULT
)
1396 || ((CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_MH
)
1397 && bs
->key
.vrfname
[0])))
1398 device_to_bind
= (const char *)bs
->key
.vrfname
;
1400 frr_with_privs(&bglobal
.bfdd_privs
) {
1401 sd
= vrf_socket(AF_INET6
, SOCK_DGRAM
, PF_UNSPEC
,
1402 bs
->vrf
->vrf_id
, device_to_bind
);
1405 zlog_err("ipv6-new: failed to create socket: %s",
1410 /* Set TTL to 255 for all transmitted packets */
1411 if (bp_set_ttlv6(sd
, BFD_TTL_VAL
) != 0) {
1416 /* Set TOS to CS6 for all transmitted packets */
1417 if (bp_set_tosv6(sd
, BFD_TOS_VAL
) != 0) {
1422 /* Find an available source port in the proper range */
1423 memset(&sin6
, 0, sizeof(sin6
));
1424 sin6
.sin6_family
= AF_INET6
;
1425 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1426 sin6
.sin6_len
= sizeof(sin6
);
1427 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1428 memcpy(&sin6
.sin6_addr
, &bs
->key
.local
, sizeof(sin6
.sin6_addr
));
1429 if (bs
->ifp
&& IN6_IS_ADDR_LINKLOCAL(&sin6
.sin6_addr
))
1430 sin6
.sin6_scope_id
= bs
->ifp
->ifindex
;
1434 if ((++pcount
) > (BFD_SRCPORTMAX
- BFD_SRCPORTINIT
)) {
1435 /* Searched all ports, none available */
1436 zlog_err("ipv6-new: failed to bind port: %s",
1441 if (srcPort
>= BFD_SRCPORTMAX
)
1442 srcPort
= BFD_SRCPORTINIT
;
1443 sin6
.sin6_port
= htons(srcPort
++);
1444 } while (bind(sd
, (struct sockaddr
*)&sin6
, sizeof(sin6
)) < 0);
1449 int bp_set_ttlv6(int sd
, uint8_t value
)
1453 if (setsockopt(sd
, IPPROTO_IPV6
, IPV6_UNICAST_HOPS
, &ttl
, sizeof(ttl
))
1455 zlog_warn("set-ttlv6: setsockopt(IPV6_UNICAST_HOPS, %d): %s",
1456 value
, strerror(errno
));
1463 int bp_set_tosv6(int sd
, uint8_t value
)
1467 if (setsockopt(sd
, IPPROTO_IPV6
, IPV6_TCLASS
, &tos
, sizeof(tos
))
1469 zlog_warn("set-tosv6: setsockopt(IPV6_TCLASS, %d): %s", value
,
1477 static void bp_set_ipv6opts(int sd
)
1479 int ipv6_pktinfo
= BFD_IPV6_PKT_INFO_VAL
;
1480 int ipv6_only
= BFD_IPV6_ONLY_VAL
;
1482 if (bp_set_ttlv6(sd
, BFD_TTL_VAL
) == -1)
1484 "set-ipv6opts: setsockopt(IPV6_UNICAST_HOPS, %d): %s",
1485 BFD_TTL_VAL
, strerror(errno
));
1487 if (setsockopt_ipv6_hoplimit(sd
, BFD_RCV_TTL_VAL
) == -1)
1488 zlog_fatal("set-ipv6opts: setsockopt(IPV6_HOPLIMIT, %d): %s",
1489 BFD_RCV_TTL_VAL
, strerror(errno
));
1491 if (setsockopt_ipv6_pktinfo(sd
, ipv6_pktinfo
) == -1)
1492 zlog_fatal("set-ipv6opts: setsockopt(IPV6_PKTINFO, %d): %s",
1493 ipv6_pktinfo
, strerror(errno
));
1495 if (setsockopt(sd
, IPPROTO_IPV6
, IPV6_V6ONLY
, &ipv6_only
,
1498 zlog_fatal("set-ipv6opts: setsockopt(IPV6_V6ONLY, %d): %s",
1499 ipv6_only
, strerror(errno
));
1502 static void bp_bind_ipv6(int sd
, uint16_t port
)
1504 struct sockaddr_in6 sin6
;
1506 memset(&sin6
, 0, sizeof(sin6
));
1507 sin6
.sin6_family
= AF_INET6
;
1508 sin6
.sin6_addr
= in6addr_any
;
1509 sin6
.sin6_port
= htons(port
);
1510 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1511 sin6
.sin6_len
= sizeof(sin6
);
1512 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1513 if (bind(sd
, (struct sockaddr
*)&sin6
, sizeof(sin6
)) == -1)
1514 zlog_fatal("bind-ipv6: bind: %s", strerror(errno
));
1517 int bp_udp6_shop(const struct vrf
*vrf
)
1521 frr_with_privs(&bglobal
.bfdd_privs
) {
1522 sd
= vrf_socket(AF_INET6
, SOCK_DGRAM
, PF_UNSPEC
, vrf
->vrf_id
,
1526 if (errno
!= EAFNOSUPPORT
)
1527 zlog_fatal("udp6-shop: socket: %s", strerror(errno
));
1529 zlog_warn("udp6-shop: V6 is not supported, continuing");
1534 bp_set_ipv6opts(sd
);
1535 bp_bind_ipv6(sd
, BFD_DEFDESTPORT
);
1540 int bp_udp6_mhop(const struct vrf
*vrf
)
1544 frr_with_privs(&bglobal
.bfdd_privs
) {
1545 sd
= vrf_socket(AF_INET6
, SOCK_DGRAM
, PF_UNSPEC
, vrf
->vrf_id
,
1549 if (errno
!= EAFNOSUPPORT
)
1550 zlog_fatal("udp6-mhop: socket: %s", strerror(errno
));
1552 zlog_warn("udp6-mhop: V6 is not supported, continuing");
1557 bp_set_ipv6opts(sd
);
1558 bp_bind_ipv6(sd
, BFD_DEF_MHOP_DEST_PORT
);
1564 /* tcpdump -dd udp dst port 3785 */
1565 struct sock_filter my_filterudp
[] = {
1566 {0x28, 0, 0, 0x0000000c}, {0x15, 0, 8, 0x00000800},
1567 {0x30, 0, 0, 0x00000017}, {0x15, 0, 6, 0x00000011},
1568 {0x28, 0, 0, 0x00000014}, {0x45, 4, 0, 0x00001fff},
1569 {0xb1, 0, 0, 0x0000000e}, {0x48, 0, 0, 0x00000010},
1570 {0x15, 0, 1, 0x00000ec9}, {0x6, 0, 0, 0x00040000},
1571 {0x6, 0, 0, 0x00000000},
1574 #define MY_FILTER_LENGTH 11
1576 int bp_echo_socket(const struct vrf
*vrf
)
1580 frr_with_privs (&bglobal
.bfdd_privs
) {
1581 s
= vrf_socket(AF_PACKET
, SOCK_RAW
, ETH_P_IP
, vrf
->vrf_id
,
1586 zlog_fatal("echo-socket: socket: %s", strerror(errno
));
1588 struct sock_fprog pf
;
1589 struct sockaddr_ll sll
= {0};
1591 /* adjust filter for socket to only receive ECHO packets */
1592 pf
.filter
= my_filterudp
;
1593 pf
.len
= MY_FILTER_LENGTH
;
1594 if (setsockopt(s
, SOL_SOCKET
, SO_ATTACH_FILTER
, &pf
, sizeof(pf
)) ==
1596 zlog_warn("%s: setsockopt(SO_ATTACH_FILTER): %s", __func__
,
1602 memset(&sll
, 0, sizeof(sll
));
1603 sll
.sll_family
= AF_PACKET
;
1604 sll
.sll_protocol
= htons(ETH_P_IP
);
1605 sll
.sll_ifindex
= 0;
1606 if (bind(s
, (struct sockaddr
*)&sll
, sizeof(sll
)) < 0) {
1607 zlog_warn("Failed to bind echo socket: %s",
1608 safe_strerror(errno
));
1616 int bp_echo_socket(const struct vrf
*vrf
)
1620 frr_with_privs(&bglobal
.bfdd_privs
) {
1621 s
= vrf_socket(AF_INET
, SOCK_DGRAM
, 0, vrf
->vrf_id
, vrf
->name
);
1624 zlog_fatal("echo-socket: socket: %s", strerror(errno
));
1627 bp_bind_ip(s
, BFD_DEF_ECHO_PORT
);
1633 int bp_echov6_socket(const struct vrf
*vrf
)
1637 frr_with_privs(&bglobal
.bfdd_privs
) {
1638 s
= vrf_socket(AF_INET6
, SOCK_DGRAM
, 0, vrf
->vrf_id
, vrf
->name
);
1641 if (errno
!= EAFNOSUPPORT
)
1642 zlog_fatal("echov6-socket: socket: %s",
1645 zlog_warn("echov6-socket: V6 is not supported, continuing");
1651 bp_bind_ipv6(s
, BFD_DEF_ECHO_PORT
);
1657 /* get peer's mac address to be used with Echo packets when they are looped in
1658 * peers forwarding plane
1660 void bfd_peer_mac_set(int sd
, struct bfd_session
*bfd
,
1661 struct sockaddr_any
*peer
, struct interface
*ifp
)
1663 struct arpreq arpreq_
;
1665 if (CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_MAC_SET
))
1668 if (peer
->sa_sin
.sin_family
== AF_INET
) {
1670 struct sockaddr_in
*addr
=
1671 (struct sockaddr_in
*)&arpreq_
.arp_pa
;
1673 memset(&arpreq_
, 0, sizeof(struct arpreq
));
1674 addr
->sin_family
= AF_INET
;
1675 memcpy(&addr
->sin_addr
.s_addr
, &peer
->sa_sin
.sin_addr
,
1676 sizeof(addr
->sin_addr
));
1677 strlcpy(arpreq_
.arp_dev
, ifp
->name
, sizeof(arpreq_
.arp_dev
));
1679 if (ioctl(sd
, SIOCGARP
, &arpreq_
) < 0) {
1680 zlog_warn("BFD: getting peer's mac failed error %s",
1682 UNSET_FLAG(bfd
->flags
, BFD_SESS_FLAG_MAC_SET
);
1683 memset(bfd
->peer_hw_addr
, 0, sizeof(bfd
->peer_hw_addr
));
1686 memcpy(bfd
->peer_hw_addr
, arpreq_
.arp_ha
.sa_data
,
1687 sizeof(bfd
->peer_hw_addr
));
1688 SET_FLAG(bfd
->flags
, BFD_SESS_FLAG_MAC_SET
);