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
,
59 uint8_t *ttl
, uint32_t *my_discr
);
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];
213 if (!CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_MAC_SET
))
215 if (!CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_ECHO_ACTIVE
))
216 SET_FLAG(bfd
->flags
, BFD_SESS_FLAG_ECHO_ACTIVE
);
218 memset(sendbuff
, 0, sizeof(sendbuff
));
221 eth
= (struct ethhdr
*)(sendbuff
);
222 memcpy(eth
->h_source
, bfd
->ifp
->hw_addr
, sizeof(eth
->h_source
));
223 memcpy(eth
->h_dest
, bfd
->peer_hw_addr
, sizeof(eth
->h_dest
));
225 total_len
+= sizeof(struct ethhdr
);
228 eth
->h_proto
= htons(ETH_P_IP
);
231 iph
= (struct iphdr
*)(sendbuff
+ sizeof(struct ethhdr
));
233 iph
->ihl
= sizeof(struct ip
) >> 2;
234 iph
->version
= IPVERSION
;
235 iph
->tos
= IPTOS_PREC_INTERNETCONTROL
;
236 iph
->id
= (uint16_t)frr_weak_random();
237 iph
->ttl
= BFD_TTL_VAL
;
238 iph
->protocol
= IPPROTO_UDP
;
239 memcpy(&iph
->saddr
, &bfd
->local_address
.sa_sin
.sin_addr
,
240 sizeof(bfd
->local_address
.sa_sin
.sin_addr
));
241 memcpy(&iph
->daddr
, &bfd
->local_address
.sa_sin
.sin_addr
,
242 sizeof(bfd
->local_address
.sa_sin
.sin_addr
));
243 total_len
+= sizeof(struct iphdr
);
246 uh
= (struct udphdr
*)(sendbuff
+ sizeof(struct iphdr
) +
247 sizeof(struct ethhdr
));
248 uh
->source
= htons(BFD_DEF_ECHO_PORT
);
249 uh
->dest
= htons(BFD_DEF_ECHO_PORT
);
251 total_len
+= sizeof(struct udphdr
);
254 beph
= (struct bfd_echo_pkt
*)(sendbuff
+ sizeof(struct udphdr
) +
255 sizeof(struct iphdr
) +
256 sizeof(struct ethhdr
));
258 beph
->ver
= BFD_ECHO_VERSION
;
259 beph
->len
= BFD_ECHO_PKT_LEN
;
260 beph
->my_discr
= htonl(bfd
->discrs
.my_discr
);
262 total_len
+= sizeof(struct bfd_echo_pkt
);
264 htons(total_len
- sizeof(struct iphdr
) - sizeof(struct ethhdr
));
265 uh
->check
= bfd_pkt_checksum(
266 uh
, (total_len
- sizeof(struct iphdr
) - sizeof(struct ethhdr
)),
267 (struct in6_addr
*)&iph
->saddr
, AF_INET
);
269 iph
->tot_len
= htons(total_len
- sizeof(struct ethhdr
));
270 iph
->check
= in_cksum((const void *)iph
, sizeof(struct iphdr
));
272 if (bp_udp_send_fp(sd
, (uint8_t *)&sendbuff
, total_len
, bfd
) == -1)
275 bfd
->stats
.tx_echo_pkt
++;
279 void ptm_bfd_echo_snd(struct bfd_session
*bfd
)
284 struct bfd_echo_pkt bep
;
285 struct sockaddr_in sin
;
286 struct sockaddr_in6 sin6
;
287 struct bfd_vrf_global
*bvrf
= bfd_vrf_look_by_session(bfd
);
291 if (!CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_ECHO_ACTIVE
))
292 SET_FLAG(bfd
->flags
, BFD_SESS_FLAG_ECHO_ACTIVE
);
294 memset(&bep
, 0, sizeof(bep
));
295 bep
.ver
= BFD_ECHO_VERSION
;
296 bep
.len
= BFD_ECHO_PKT_LEN
;
297 bep
.my_discr
= htonl(bfd
->discrs
.my_discr
);
299 if (CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_IPV6
)) {
300 if (bvrf
->bg_echov6
== -1)
302 sd
= bvrf
->bg_echov6
;
303 memset(&sin6
, 0, sizeof(sin6
));
304 sin6
.sin6_family
= AF_INET6
;
305 memcpy(&sin6
.sin6_addr
, &bfd
->key
.peer
, sizeof(sin6
.sin6_addr
));
306 if (bfd
->ifp
&& IN6_IS_ADDR_LINKLOCAL(&sin6
.sin6_addr
))
307 sin6
.sin6_scope_id
= bfd
->ifp
->ifindex
;
309 sin6
.sin6_port
= htons(BFD_DEF_ECHO_PORT
);
310 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
311 sin6
.sin6_len
= sizeof(sin6
);
312 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
314 sa
= (struct sockaddr
*)&sin6
;
315 salen
= sizeof(sin6
);
318 memset(&sin
, 0, sizeof(sin
));
319 sin
.sin_family
= AF_INET
;
320 memcpy(&sin
.sin_addr
, &bfd
->key
.peer
, sizeof(sin
.sin_addr
));
321 sin
.sin_port
= htons(BFD_DEF_ECHO_PORT
);
322 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
323 sin
.sin_len
= sizeof(sin
);
324 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
326 sa
= (struct sockaddr
*)&sin
;
329 if (bp_udp_send(sd
, BFD_TTL_VAL
, (uint8_t *)&bep
, sizeof(bep
), sa
,
334 bfd
->stats
.tx_echo_pkt
++;
337 static int ptm_bfd_process_echo_pkt(struct bfd_vrf_global
*bvrf
, int s
)
339 struct bfd_session
*bfd
;
340 uint32_t my_discr
= 0;
343 /* Receive and parse echo packet. */
344 if (bp_bfd_echo_in(bvrf
, s
, &ttl
, &my_discr
) == -1)
347 /* Your discriminator not zero - use it to find session */
348 bfd
= bfd_id_lookup(my_discr
);
350 if (bglobal
.debug_network
)
351 zlog_debug("echo-packet: no matching session (id:%u)",
356 if (!CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_ECHO_ACTIVE
)) {
357 if (bglobal
.debug_network
)
358 zlog_debug("echo-packet: echo disabled [%s] (id:%u)",
359 bs_to_string(bfd
), my_discr
);
363 bfd
->stats
.rx_echo_pkt
++;
365 /* Compute detect time */
366 bfd
->echo_detect_TO
= bfd
->remote_detect_mult
* bfd
->echo_xmt_TO
;
368 /* Update echo receive timeout. */
369 if (bfd
->echo_detect_TO
> 0)
370 bfd_echo_recvtimer_update(bfd
);
375 void ptm_bfd_snd(struct bfd_session
*bfd
, int fbit
)
377 struct bfd_pkt cp
= {};
379 /* Set fields according to section 6.5.7 */
380 cp
.diag
= bfd
->local_diag
;
381 BFD_SETVER(cp
.diag
, BFD_VERSION
);
383 BFD_SETSTATE(cp
.flags
, bfd
->ses_state
);
385 if (CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_CBIT
))
386 BFD_SETCBIT(cp
.flags
, BFD_CBIT
);
388 BFD_SETDEMANDBIT(cp
.flags
, BFD_DEF_DEMAND
);
391 * Polling and Final can't be set at the same time.
393 * RFC 5880, Section 6.5.
395 BFD_SETFBIT(cp
.flags
, fbit
);
397 BFD_SETPBIT(cp
.flags
, bfd
->polling
);
399 cp
.detect_mult
= bfd
->detect_mult
;
400 cp
.len
= BFD_PKT_LEN
;
401 cp
.discrs
.my_discr
= htonl(bfd
->discrs
.my_discr
);
402 cp
.discrs
.remote_discr
= htonl(bfd
->discrs
.remote_discr
);
404 cp
.timers
.desired_min_tx
=
405 htonl(bfd
->timers
.desired_min_tx
);
406 cp
.timers
.required_min_rx
=
407 htonl(bfd
->timers
.required_min_rx
);
410 * We can only announce current setting on poll, this
411 * avoids timing mismatch with our peer and give it
412 * the oportunity to learn. See `bs_final_handler` for
415 cp
.timers
.desired_min_tx
=
416 htonl(bfd
->cur_timers
.desired_min_tx
);
417 cp
.timers
.required_min_rx
=
418 htonl(bfd
->cur_timers
.required_min_rx
);
420 cp
.timers
.required_min_echo
= htonl(bfd
->timers
.required_min_echo_rx
);
422 if (_ptm_bfd_send(bfd
, NULL
, &cp
, BFD_PKT_LEN
) != 0)
425 bfd
->stats
.tx_ctrl_pkt
++;
430 * receive the ipv4 echo packet that was loopback in the peers forwarding plane
432 ssize_t
bfd_recv_ipv4_fp(int sd
, uint8_t *msgbuf
, size_t msgbuflen
,
433 uint8_t *ttl
, ifindex_t
*ifindex
,
434 struct sockaddr_any
*local
, struct sockaddr_any
*peer
)
437 struct sockaddr_ll msgaddr
;
438 struct msghdr msghdr
;
440 uint16_t recv_checksum
;
445 /* Prepare the recvmsg params. */
446 iov
[0].iov_base
= msgbuf
;
447 iov
[0].iov_len
= msgbuflen
;
449 memset(&msghdr
, 0, sizeof(msghdr
));
450 msghdr
.msg_name
= &msgaddr
;
451 msghdr
.msg_namelen
= sizeof(msgaddr
);
452 msghdr
.msg_iov
= iov
;
453 msghdr
.msg_iovlen
= 1;
455 mlen
= recvmsg(sd
, &msghdr
, MSG_DONTWAIT
);
457 if (errno
!= EAGAIN
|| errno
!= EWOULDBLOCK
|| errno
!= EINTR
)
458 zlog_err("%s: recv failed: %s", __func__
,
464 ip
= (struct iphdr
*)(msgbuf
+ sizeof(struct ethhdr
));
466 /* verify ip checksum */
467 recv_checksum
= ip
->check
;
469 checksum
= in_cksum((const void *)ip
, sizeof(struct iphdr
));
470 if (recv_checksum
!= checksum
) {
471 if (bglobal
.debug_network
)
473 "%s: invalid iphdr checksum expected 0x%x rcvd 0x%x",
474 __func__
, checksum
, recv_checksum
);
480 /* Echo should be looped in peer's forwarding plane, but it also
481 * comes up to BFD so silently drop it
483 if (ip
->daddr
== ip
->saddr
)
486 if (bglobal
.debug_network
)
487 zlog_debug("%s: invalid TTL: %u", __func__
, *ttl
);
491 local
->sa_sin
.sin_family
= AF_INET
;
492 memcpy(&local
->sa_sin
.sin_addr
, &ip
->saddr
, sizeof(ip
->saddr
));
493 peer
->sa_sin
.sin_family
= AF_INET
;
494 memcpy(&peer
->sa_sin
.sin_addr
, &ip
->daddr
, sizeof(ip
->daddr
));
496 *ifindex
= msgaddr
.sll_ifindex
;
498 /* verify udp checksum */
499 uh
= (struct udphdr
*)(msgbuf
+ sizeof(struct iphdr
) +
500 sizeof(struct ethhdr
));
501 recv_checksum
= uh
->check
;
503 checksum
= bfd_pkt_checksum(uh
, ntohs(uh
->len
),
504 (struct in6_addr
*)&ip
->saddr
, AF_INET
);
505 if (recv_checksum
!= checksum
) {
506 if (bglobal
.debug_network
)
508 "%s: invalid udphdr checksum expected 0x%x rcvd 0x%x",
509 __func__
, checksum
, recv_checksum
);
516 ssize_t
bfd_recv_ipv4(int sd
, uint8_t *msgbuf
, size_t msgbuflen
, uint8_t *ttl
,
517 ifindex_t
*ifindex
, struct sockaddr_any
*local
,
518 struct sockaddr_any
*peer
)
522 struct sockaddr_in msgaddr
;
523 struct msghdr msghdr
;
525 uint8_t cmsgbuf
[255];
527 /* Prepare the recvmsg params. */
528 iov
[0].iov_base
= msgbuf
;
529 iov
[0].iov_len
= msgbuflen
;
531 memset(&msghdr
, 0, sizeof(msghdr
));
532 msghdr
.msg_name
= &msgaddr
;
533 msghdr
.msg_namelen
= sizeof(msgaddr
);
534 msghdr
.msg_iov
= iov
;
535 msghdr
.msg_iovlen
= 1;
536 msghdr
.msg_control
= cmsgbuf
;
537 msghdr
.msg_controllen
= sizeof(cmsgbuf
);
539 mlen
= recvmsg(sd
, &msghdr
, MSG_DONTWAIT
);
542 zlog_err("ipv4-recv: recv failed: %s", strerror(errno
));
547 /* Get source address */
548 peer
->sa_sin
= *((struct sockaddr_in
*)(msghdr
.msg_name
));
550 /* Get and check TTL */
551 for (cm
= CMSG_FIRSTHDR(&msghdr
); cm
!= NULL
;
552 cm
= CMSG_NXTHDR(&msghdr
, cm
)) {
553 if (cm
->cmsg_level
!= IPPROTO_IP
)
556 switch (cm
->cmsg_type
) {
561 memcpy(&ttlval
, CMSG_DATA(cm
), sizeof(ttlval
));
563 if (bglobal
.debug_network
)
564 zlog_debug("ipv4-recv: invalid TTL: %u",
573 struct in_pktinfo
*pi
=
574 (struct in_pktinfo
*)CMSG_DATA(cm
);
579 local
->sa_sin
.sin_family
= AF_INET
;
580 local
->sa_sin
.sin_addr
= pi
->ipi_addr
;
581 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
582 local
->sa_sin
.sin_len
= sizeof(local
->sa_sin
);
583 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
585 *ifindex
= pi
->ipi_ifindex
;
588 #endif /* BFD_LINUX */
591 memcpy(ttl
, CMSG_DATA(cm
), sizeof(*ttl
));
595 case IP_RECVDSTADDR
: {
598 memcpy(&ia
, CMSG_DATA(cm
), sizeof(ia
));
599 local
->sa_sin
.sin_family
= AF_INET
;
600 local
->sa_sin
.sin_addr
= ia
;
601 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
602 local
->sa_sin
.sin_len
= sizeof(local
->sa_sin
);
603 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
610 * On *BSDs we expect to land here when skipping
611 * the IP_RECVIF header. It will be handled by
612 * getsockopt_ifindex() below.
619 /* OS agnostic way of getting interface name. */
620 if (*ifindex
== IFINDEX_INTERNAL
)
621 *ifindex
= getsockopt_ifindex(AF_INET
, &msghdr
);
626 ssize_t
bfd_recv_ipv6(int sd
, uint8_t *msgbuf
, size_t msgbuflen
, uint8_t *ttl
,
627 ifindex_t
*ifindex
, struct sockaddr_any
*local
,
628 struct sockaddr_any
*peer
)
631 struct in6_pktinfo
*pi6
= NULL
;
634 struct sockaddr_in6 msgaddr6
;
635 struct msghdr msghdr6
;
637 uint8_t cmsgbuf6
[255];
639 /* Prepare the recvmsg params. */
640 iov
[0].iov_base
= msgbuf
;
641 iov
[0].iov_len
= msgbuflen
;
643 memset(&msghdr6
, 0, sizeof(msghdr6
));
644 msghdr6
.msg_name
= &msgaddr6
;
645 msghdr6
.msg_namelen
= sizeof(msgaddr6
);
646 msghdr6
.msg_iov
= iov
;
647 msghdr6
.msg_iovlen
= 1;
648 msghdr6
.msg_control
= cmsgbuf6
;
649 msghdr6
.msg_controllen
= sizeof(cmsgbuf6
);
651 mlen
= recvmsg(sd
, &msghdr6
, MSG_DONTWAIT
);
654 zlog_err("ipv6-recv: recv failed: %s", strerror(errno
));
659 /* Get source address */
660 peer
->sa_sin6
= *((struct sockaddr_in6
*)(msghdr6
.msg_name
));
662 /* Get and check TTL */
663 for (cm
= CMSG_FIRSTHDR(&msghdr6
); cm
!= NULL
;
664 cm
= CMSG_NXTHDR(&msghdr6
, cm
)) {
665 if (cm
->cmsg_level
!= IPPROTO_IPV6
)
668 if (cm
->cmsg_type
== IPV6_HOPLIMIT
) {
669 memcpy(&ttlval
, CMSG_DATA(cm
), sizeof(ttlval
));
671 if (bglobal
.debug_network
)
672 zlog_debug("ipv6-recv: invalid TTL: %u",
678 } else if (cm
->cmsg_type
== IPV6_PKTINFO
) {
679 pi6
= (struct in6_pktinfo
*)CMSG_DATA(cm
);
681 local
->sa_sin6
.sin6_family
= AF_INET6
;
682 local
->sa_sin6
.sin6_addr
= pi6
->ipi6_addr
;
683 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
684 local
->sa_sin6
.sin6_len
= sizeof(local
->sa_sin6
);
685 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
687 *ifindex
= pi6
->ipi6_ifindex
;
689 /* Set scope ID for link local addresses. */
690 if (IN6_IS_ADDR_LINKLOCAL(
691 &peer
->sa_sin6
.sin6_addr
))
692 peer
->sa_sin6
.sin6_scope_id
= *ifindex
;
693 if (IN6_IS_ADDR_LINKLOCAL(
694 &local
->sa_sin6
.sin6_addr
))
695 local
->sa_sin6
.sin6_scope_id
= *ifindex
;
703 static void bfd_sd_reschedule(struct bfd_vrf_global
*bvrf
, int sd
)
705 if (sd
== bvrf
->bg_shop
) {
706 THREAD_OFF(bvrf
->bg_ev
[0]);
707 thread_add_read(master
, bfd_recv_cb
, bvrf
, bvrf
->bg_shop
,
709 } else if (sd
== bvrf
->bg_mhop
) {
710 THREAD_OFF(bvrf
->bg_ev
[1]);
711 thread_add_read(master
, bfd_recv_cb
, bvrf
, bvrf
->bg_mhop
,
713 } else if (sd
== bvrf
->bg_shop6
) {
714 THREAD_OFF(bvrf
->bg_ev
[2]);
715 thread_add_read(master
, bfd_recv_cb
, bvrf
, bvrf
->bg_shop6
,
717 } else if (sd
== bvrf
->bg_mhop6
) {
718 THREAD_OFF(bvrf
->bg_ev
[3]);
719 thread_add_read(master
, bfd_recv_cb
, bvrf
, bvrf
->bg_mhop6
,
721 } else if (sd
== bvrf
->bg_echo
) {
722 THREAD_OFF(bvrf
->bg_ev
[4]);
723 thread_add_read(master
, bfd_recv_cb
, bvrf
, bvrf
->bg_echo
,
725 } else if (sd
== bvrf
->bg_echov6
) {
726 THREAD_OFF(bvrf
->bg_ev
[5]);
727 thread_add_read(master
, bfd_recv_cb
, bvrf
, bvrf
->bg_echov6
,
732 static void cp_debug(bool mhop
, struct sockaddr_any
*peer
,
733 struct sockaddr_any
*local
, ifindex_t ifindex
,
734 vrf_id_t vrfid
, const char *fmt
, ...)
736 char buf
[512], peerstr
[128], localstr
[128], portstr
[64], vrfstr
[64];
739 /* Don't to any processing if debug is disabled. */
740 if (bglobal
.debug_network
== false)
743 if (peer
->sa_sin
.sin_family
)
744 snprintf(peerstr
, sizeof(peerstr
), " peer:%s", satostr(peer
));
748 if (local
->sa_sin
.sin_family
)
749 snprintf(localstr
, sizeof(localstr
), " local:%s",
754 if (ifindex
!= IFINDEX_INTERNAL
)
755 snprintf(portstr
, sizeof(portstr
), " port:%u", ifindex
);
759 if (vrfid
!= VRF_DEFAULT
)
760 snprintf(vrfstr
, sizeof(vrfstr
), " vrf:%u", vrfid
);
765 vsnprintf(buf
, sizeof(buf
), fmt
, vl
);
768 zlog_debug("control-packet: %s [mhop:%s%s%s%s%s]", buf
,
769 mhop
? "yes" : "no", peerstr
, localstr
, portstr
, vrfstr
);
772 void bfd_recv_cb(struct thread
*t
)
774 int sd
= THREAD_FD(t
);
775 struct bfd_session
*bfd
;
781 ifindex_t ifindex
= IFINDEX_INTERNAL
;
782 struct sockaddr_any local
, peer
;
783 uint8_t msgbuf
[1516];
784 struct interface
*ifp
= NULL
;
785 struct bfd_vrf_global
*bvrf
= THREAD_ARG(t
);
787 /* Schedule next read. */
788 bfd_sd_reschedule(bvrf
, sd
);
790 /* Handle echo packets. */
791 if (sd
== bvrf
->bg_echo
|| sd
== bvrf
->bg_echov6
) {
792 ptm_bfd_process_echo_pkt(bvrf
, sd
);
796 /* Sanitize input/output. */
797 memset(&local
, 0, sizeof(local
));
798 memset(&peer
, 0, sizeof(peer
));
800 /* Handle control packets. */
802 if (sd
== bvrf
->bg_shop
|| sd
== bvrf
->bg_mhop
) {
803 is_mhop
= sd
== bvrf
->bg_mhop
;
804 mlen
= bfd_recv_ipv4(sd
, msgbuf
, sizeof(msgbuf
), &ttl
, &ifindex
,
806 } else if (sd
== bvrf
->bg_shop6
|| sd
== bvrf
->bg_mhop6
) {
807 is_mhop
= sd
== bvrf
->bg_mhop6
;
808 mlen
= bfd_recv_ipv6(sd
, msgbuf
, sizeof(msgbuf
), &ttl
, &ifindex
,
813 * With netns backend, we have a separate socket in each VRF. It means
814 * that bvrf here is correct and we believe the bvrf->vrf->vrf_id.
815 * With VRF-lite backend, we have a single socket in the default VRF.
816 * It means that we can't believe the bvrf->vrf->vrf_id. But in
817 * VRF-lite, the ifindex is globally unique, so we can retrieve the
818 * correct vrf_id from the interface.
820 vrfid
= bvrf
->vrf
->vrf_id
;
822 ifp
= if_lookup_by_index(ifindex
, vrfid
);
824 vrfid
= ifp
->vrf
->vrf_id
;
827 /* Implement RFC 5880 6.8.6 */
828 if (mlen
< BFD_PKT_LEN
) {
829 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
,
830 "too small (%ld bytes)", mlen
);
834 /* Validate single hop packet TTL. */
835 if ((!is_mhop
) && (ttl
!= BFD_TTL_VAL
)) {
836 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
,
837 "invalid TTL: %d expected %d", ttl
, BFD_TTL_VAL
);
842 * Parse the control header for inconsistencies:
844 * - Bad multiplier configuration;
846 * - Invalid discriminator;
848 cp
= (struct bfd_pkt
*)(msgbuf
);
849 if (BFD_GETVER(cp
->diag
) != BFD_VERSION
) {
850 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
,
851 "bad version %d", BFD_GETVER(cp
->diag
));
855 if (cp
->detect_mult
== 0) {
856 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
,
857 "detect multiplier set to zero");
861 if ((cp
->len
< BFD_PKT_LEN
) || (cp
->len
> mlen
)) {
862 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
, "too small");
866 if (cp
->discrs
.my_discr
== 0) {
867 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
,
868 "'my discriminator' is zero");
872 /* Find the session that this packet belongs. */
873 bfd
= ptm_bfd_sess_find(cp
, &peer
, &local
, ifp
, vrfid
, is_mhop
);
875 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
,
880 /* Ensure that existing good sessions are not overridden. */
881 if (!cp
->discrs
.remote_discr
&& bfd
->ses_state
!= PTM_BFD_DOWN
&&
882 bfd
->ses_state
!= PTM_BFD_ADM_DOWN
) {
883 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
,
884 "'remote discriminator' is zero, not overridden");
889 * Multi hop: validate packet TTL.
890 * Single hop: set local address that received the packet.
891 * set peers mac address for echo packets
894 if (ttl
< bfd
->mh_ttl
) {
895 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
,
896 "exceeded max hop count (expected %d, got %d)",
902 if (bfd
->local_address
.sa_sin
.sin_family
== AF_UNSPEC
)
903 bfd
->local_address
= local
;
906 bfd_peer_mac_set(sd
, bfd
, &peer
, ifp
);
910 bfd
->stats
.rx_ctrl_pkt
++;
913 * If no interface was detected, save the interface where the
916 if (!is_mhop
&& bfd
->ifp
== NULL
)
919 /* Log remote discriminator changes. */
920 if ((bfd
->discrs
.remote_discr
!= 0)
921 && (bfd
->discrs
.remote_discr
!= ntohl(cp
->discrs
.my_discr
)))
922 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
,
923 "remote discriminator mismatch (expected %u, got %u)",
924 bfd
->discrs
.remote_discr
, ntohl(cp
->discrs
.my_discr
));
926 bfd
->discrs
.remote_discr
= ntohl(cp
->discrs
.my_discr
);
928 /* Save remote diagnostics before state switch. */
929 bfd
->remote_diag
= cp
->diag
& BFD_DIAGMASK
;
931 /* Update remote timers settings. */
932 bfd
->remote_timers
.desired_min_tx
= ntohl(cp
->timers
.desired_min_tx
);
933 bfd
->remote_timers
.required_min_rx
= ntohl(cp
->timers
.required_min_rx
);
934 bfd
->remote_timers
.required_min_echo
=
935 ntohl(cp
->timers
.required_min_echo
);
936 bfd
->remote_detect_mult
= cp
->detect_mult
;
938 if (BFD_GETCBIT(cp
->flags
))
939 bfd
->remote_cbit
= 1;
941 bfd
->remote_cbit
= 0;
943 /* State switch from section 6.2. */
944 bs_state_handler(bfd
, BFD_GETSTATE(cp
->flags
));
946 /* RFC 5880, Section 6.5: handle POLL/FINAL negotiation sequence. */
947 if (bfd
->polling
&& BFD_GETFBIT(cp
->flags
)) {
948 /* Disable polling. */
951 /* Handle poll finalization. */
952 bs_final_handler(bfd
);
956 * Detection timeout calculation:
957 * The minimum detection timeout is the remote detection
958 * multipler (number of packets to be missed) times the agreed
959 * transmission interval.
961 * RFC 5880, Section 6.8.4.
963 if (bfd
->cur_timers
.required_min_rx
> bfd
->remote_timers
.desired_min_tx
)
964 bfd
->detect_TO
= bfd
->remote_detect_mult
965 * bfd
->cur_timers
.required_min_rx
;
967 bfd
->detect_TO
= bfd
->remote_detect_mult
968 * bfd
->remote_timers
.desired_min_tx
;
970 /* Apply new receive timer immediately. */
971 bfd_recvtimer_update(bfd
);
973 /* Handle echo timers changes. */
974 bs_echo_timer_handler(bfd
);
977 * We've received a packet with the POLL bit set, we must send
978 * a control packet back with the FINAL bit set.
980 * RFC 5880, Section 6.5.
982 if (BFD_GETPBIT(cp
->flags
)) {
983 /* We are finalizing a poll negotiation. */
984 bs_final_handler(bfd
);
986 /* Send the control packet with the final bit immediately. */
992 * bp_bfd_echo_in: proccesses an BFD echo packet. On TTL == BFD_TTL_VAL
993 * the packet is looped back or returns the my discriminator ID along
996 * Returns -1 on error or loopback or 0 on success.
998 int bp_bfd_echo_in(struct bfd_vrf_global
*bvrf
, int sd
,
999 uint8_t *ttl
, uint32_t *my_discr
)
1001 struct bfd_echo_pkt
*bep
;
1003 struct sockaddr_any local
, peer
;
1004 ifindex_t ifindex
= IFINDEX_INTERNAL
;
1005 vrf_id_t vrfid
= VRF_DEFAULT
;
1006 uint8_t msgbuf
[1516];
1007 size_t bfd_offset
= 0;
1009 if (sd
== bvrf
->bg_echo
) {
1011 rlen
= bfd_recv_ipv4_fp(sd
, msgbuf
, sizeof(msgbuf
), ttl
,
1012 &ifindex
, &local
, &peer
);
1014 /* silently drop echo packet that is looped in fastpath but
1015 * still comes up to BFD
1019 bfd_offset
= sizeof(struct udphdr
) + sizeof(struct iphdr
) +
1020 sizeof(struct ethhdr
);
1022 rlen
= bfd_recv_ipv4(sd
, msgbuf
, sizeof(msgbuf
), ttl
, &ifindex
,
1027 rlen
= bfd_recv_ipv6(sd
, msgbuf
, sizeof(msgbuf
), ttl
, &ifindex
,
1032 /* Short packet, better not risk reading it. */
1033 if (rlen
< (ssize_t
)sizeof(*bep
)) {
1034 cp_debug(false, &peer
, &local
, ifindex
, vrfid
,
1035 "small echo packet");
1039 /* Test for loopback for ipv6, ipv4 is looped in forwarding plane */
1040 if ((*ttl
== BFD_TTL_VAL
) && (sd
== bvrf
->bg_echov6
)) {
1041 bp_udp_send(sd
, *ttl
- 1, msgbuf
, rlen
,
1042 (struct sockaddr
*)&peer
,
1043 (sd
== bvrf
->bg_echo
) ? sizeof(peer
.sa_sin
)
1044 : sizeof(peer
.sa_sin6
));
1048 /* Read my discriminator from BFD Echo packet. */
1049 bep
= (struct bfd_echo_pkt
*)(msgbuf
+ bfd_offset
);
1050 *my_discr
= ntohl(bep
->my_discr
);
1051 if (*my_discr
== 0) {
1052 cp_debug(false, &peer
, &local
, ifindex
, vrfid
,
1053 "invalid echo packet discriminator (zero)");
1062 * send a bfd packet with src/dst same IP so that the peer will receive
1063 * the packet and forward it back to sender in the forwarding plane
1065 int bp_udp_send_fp(int sd
, uint8_t *data
, size_t datalen
,
1066 struct bfd_session
*bfd
)
1070 struct iovec iov
[1];
1071 uint8_t msgctl
[255];
1072 struct sockaddr_ll sadr_ll
;
1075 sadr_ll
.sll_ifindex
= bfd
->ifp
->ifindex
;
1076 sadr_ll
.sll_halen
= ETH_ALEN
;
1077 memcpy(sadr_ll
.sll_addr
, bfd
->peer_hw_addr
, sizeof(bfd
->peer_hw_addr
));
1078 sadr_ll
.sll_protocol
= htons(ETH_P_IP
);
1080 /* Prepare message data. */
1081 iov
[0].iov_base
= data
;
1082 iov
[0].iov_len
= datalen
;
1084 memset(&msg
, 0, sizeof(msg
));
1085 memset(msgctl
, 0, sizeof(msgctl
));
1086 msg
.msg_name
= &sadr_ll
;
1087 msg
.msg_namelen
= sizeof(sadr_ll
);
1091 /* Send echo to peer */
1092 wlen
= sendmsg(sd
, &msg
, 0);
1095 if (bglobal
.debug_network
)
1096 zlog_debug("udp-send: loopback failure: (%d) %s", errno
,
1099 } else if (wlen
< (ssize_t
)datalen
) {
1100 if (bglobal
.debug_network
)
1101 zlog_debug("udp-send: partial send: %zd expected %zu",
1110 int bp_udp_send(int sd
, uint8_t ttl
, uint8_t *data
, size_t datalen
,
1111 struct sockaddr
*to
, socklen_t tolen
)
1113 struct cmsghdr
*cmsg
;
1116 bool is_ipv6
= to
->sa_family
== AF_INET6
;
1118 struct iovec iov
[1];
1119 uint8_t msgctl
[255];
1121 /* Prepare message data. */
1122 iov
[0].iov_base
= data
;
1123 iov
[0].iov_len
= datalen
;
1125 memset(&msg
, 0, sizeof(msg
));
1126 memset(msgctl
, 0, sizeof(msgctl
));
1128 msg
.msg_namelen
= tolen
;
1132 /* Prepare the packet TTL information. */
1134 /* Use ancillary data. */
1135 msg
.msg_control
= msgctl
;
1136 msg
.msg_controllen
= CMSG_LEN(sizeof(ttlval
));
1138 /* Configure the ancillary data. */
1139 cmsg
= CMSG_FIRSTHDR(&msg
);
1140 cmsg
->cmsg_len
= CMSG_LEN(sizeof(ttlval
));
1142 cmsg
->cmsg_level
= IPPROTO_IPV6
;
1143 cmsg
->cmsg_type
= IPV6_HOPLIMIT
;
1146 cmsg
->cmsg_level
= IPPROTO_IP
;
1147 cmsg
->cmsg_type
= IP_TTL
;
1149 /* FreeBSD does not support TTL in ancillary data. */
1150 msg
.msg_control
= NULL
;
1151 msg
.msg_controllen
= 0;
1153 bp_set_ttl(sd
, ttl
);
1154 #endif /* BFD_BSD */
1156 memcpy(CMSG_DATA(cmsg
), &ttlval
, sizeof(ttlval
));
1159 /* Send echo back. */
1160 wlen
= sendmsg(sd
, &msg
, 0);
1162 if (bglobal
.debug_network
)
1163 zlog_debug("udp-send: loopback failure: (%d) %s", errno
,
1166 } else if (wlen
< (ssize_t
)datalen
) {
1167 if (bglobal
.debug_network
)
1168 zlog_debug("udp-send: partial send: %zd expected %zu",
1185 int bp_set_ttl(int sd
, uint8_t value
)
1189 if (setsockopt(sd
, IPPROTO_IP
, IP_TTL
, &ttl
, sizeof(ttl
)) == -1) {
1190 zlog_warn("set-ttl: setsockopt(IP_TTL, %d): %s", value
,
1198 int bp_set_tos(int sd
, uint8_t value
)
1202 if (setsockopt(sd
, IPPROTO_IP
, IP_TOS
, &tos
, sizeof(tos
)) == -1) {
1203 zlog_warn("set-tos: setsockopt(IP_TOS, %d): %s", value
,
1211 static void bp_set_ipopts(int sd
)
1213 int rcvttl
= BFD_RCV_TTL_VAL
;
1215 if (bp_set_ttl(sd
, BFD_TTL_VAL
) != 0)
1216 zlog_fatal("set-ipopts: TTL configuration failed");
1218 if (setsockopt(sd
, IPPROTO_IP
, IP_RECVTTL
, &rcvttl
, sizeof(rcvttl
))
1220 zlog_fatal("set-ipopts: setsockopt(IP_RECVTTL, %d): %s", rcvttl
,
1224 int pktinfo
= BFD_PKT_INFO_VAL
;
1226 /* Figure out address and interface to do the peer matching. */
1227 if (setsockopt(sd
, IPPROTO_IP
, IP_PKTINFO
, &pktinfo
, sizeof(pktinfo
))
1229 zlog_fatal("set-ipopts: setsockopt(IP_PKTINFO, %d): %s",
1230 pktinfo
, strerror(errno
));
1231 #endif /* BFD_LINUX */
1235 /* Find out our address for peer matching. */
1236 if (setsockopt(sd
, IPPROTO_IP
, IP_RECVDSTADDR
, &yes
, sizeof(yes
)) == -1)
1237 zlog_fatal("set-ipopts: setsockopt(IP_RECVDSTADDR, %d): %s",
1238 yes
, strerror(errno
));
1240 /* Find out interface where the packet came in. */
1241 if (setsockopt_ifindex(AF_INET
, sd
, yes
) == -1)
1242 zlog_fatal("set-ipopts: setsockopt_ipv4_ifindex(%d): %s", yes
,
1244 #endif /* BFD_BSD */
1247 static void bp_bind_ip(int sd
, uint16_t port
)
1249 struct sockaddr_in sin
;
1251 memset(&sin
, 0, sizeof(sin
));
1252 sin
.sin_family
= AF_INET
;
1253 sin
.sin_addr
.s_addr
= htonl(INADDR_ANY
);
1254 sin
.sin_port
= htons(port
);
1255 if (bind(sd
, (struct sockaddr
*)&sin
, sizeof(sin
)) == -1)
1256 zlog_fatal("bind-ip: bind: %s", strerror(errno
));
1259 int bp_udp_shop(const struct vrf
*vrf
)
1263 frr_with_privs(&bglobal
.bfdd_privs
) {
1264 sd
= vrf_socket(AF_INET
, SOCK_DGRAM
, PF_UNSPEC
, vrf
->vrf_id
,
1268 zlog_fatal("udp-shop: socket: %s", strerror(errno
));
1271 bp_bind_ip(sd
, BFD_DEFDESTPORT
);
1275 int bp_udp_mhop(const struct vrf
*vrf
)
1279 frr_with_privs(&bglobal
.bfdd_privs
) {
1280 sd
= vrf_socket(AF_INET
, SOCK_DGRAM
, PF_UNSPEC
, vrf
->vrf_id
,
1284 zlog_fatal("udp-mhop: socket: %s", strerror(errno
));
1287 bp_bind_ip(sd
, BFD_DEF_MHOP_DEST_PORT
);
1292 int bp_peer_socket(const struct bfd_session
*bs
)
1295 struct sockaddr_in sin
;
1296 static int srcPort
= BFD_SRCPORTINIT
;
1297 const char *device_to_bind
= NULL
;
1299 if (bs
->key
.ifname
[0])
1300 device_to_bind
= (const char *)bs
->key
.ifname
;
1301 else if ((!vrf_is_backend_netns() && bs
->vrf
->vrf_id
!= VRF_DEFAULT
)
1302 || ((CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_MH
)
1303 && bs
->key
.vrfname
[0])))
1304 device_to_bind
= (const char *)bs
->key
.vrfname
;
1306 frr_with_privs(&bglobal
.bfdd_privs
) {
1307 sd
= vrf_socket(AF_INET
, SOCK_DGRAM
, PF_UNSPEC
,
1308 bs
->vrf
->vrf_id
, device_to_bind
);
1311 zlog_err("ipv4-new: failed to create socket: %s",
1316 /* Set TTL to 255 for all transmitted packets */
1317 if (bp_set_ttl(sd
, BFD_TTL_VAL
) != 0) {
1322 /* Set TOS to CS6 for all transmitted packets */
1323 if (bp_set_tos(sd
, BFD_TOS_VAL
) != 0) {
1328 /* Find an available source port in the proper range */
1329 memset(&sin
, 0, sizeof(sin
));
1330 sin
.sin_family
= AF_INET
;
1331 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1332 sin
.sin_len
= sizeof(sin
);
1333 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1334 memcpy(&sin
.sin_addr
, &bs
->key
.local
, sizeof(sin
.sin_addr
));
1335 if (CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_MH
) == 0)
1336 sin
.sin_addr
.s_addr
= INADDR_ANY
;
1340 if ((++pcount
) > (BFD_SRCPORTMAX
- BFD_SRCPORTINIT
)) {
1341 /* Searched all ports, none available */
1342 zlog_err("ipv4-new: failed to bind port: %s",
1347 if (srcPort
>= BFD_SRCPORTMAX
)
1348 srcPort
= BFD_SRCPORTINIT
;
1349 sin
.sin_port
= htons(srcPort
++);
1350 } while (bind(sd
, (struct sockaddr
*)&sin
, sizeof(sin
)) < 0);
1360 int bp_peer_socketv6(const struct bfd_session
*bs
)
1363 struct sockaddr_in6 sin6
;
1364 static int srcPort
= BFD_SRCPORTINIT
;
1365 const char *device_to_bind
= NULL
;
1367 if (bs
->key
.ifname
[0])
1368 device_to_bind
= (const char *)bs
->key
.ifname
;
1369 else if ((!vrf_is_backend_netns() && bs
->vrf
->vrf_id
!= VRF_DEFAULT
)
1370 || ((CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_MH
)
1371 && bs
->key
.vrfname
[0])))
1372 device_to_bind
= (const char *)bs
->key
.vrfname
;
1374 frr_with_privs(&bglobal
.bfdd_privs
) {
1375 sd
= vrf_socket(AF_INET6
, SOCK_DGRAM
, PF_UNSPEC
,
1376 bs
->vrf
->vrf_id
, device_to_bind
);
1379 zlog_err("ipv6-new: failed to create socket: %s",
1384 /* Set TTL to 255 for all transmitted packets */
1385 if (bp_set_ttlv6(sd
, BFD_TTL_VAL
) != 0) {
1390 /* Set TOS to CS6 for all transmitted packets */
1391 if (bp_set_tosv6(sd
, BFD_TOS_VAL
) != 0) {
1396 /* Find an available source port in the proper range */
1397 memset(&sin6
, 0, sizeof(sin6
));
1398 sin6
.sin6_family
= AF_INET6
;
1399 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1400 sin6
.sin6_len
= sizeof(sin6
);
1401 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1402 memcpy(&sin6
.sin6_addr
, &bs
->key
.local
, sizeof(sin6
.sin6_addr
));
1403 if (bs
->ifp
&& IN6_IS_ADDR_LINKLOCAL(&sin6
.sin6_addr
))
1404 sin6
.sin6_scope_id
= bs
->ifp
->ifindex
;
1408 if ((++pcount
) > (BFD_SRCPORTMAX
- BFD_SRCPORTINIT
)) {
1409 /* Searched all ports, none available */
1410 zlog_err("ipv6-new: failed to bind port: %s",
1415 if (srcPort
>= BFD_SRCPORTMAX
)
1416 srcPort
= BFD_SRCPORTINIT
;
1417 sin6
.sin6_port
= htons(srcPort
++);
1418 } while (bind(sd
, (struct sockaddr
*)&sin6
, sizeof(sin6
)) < 0);
1423 int bp_set_ttlv6(int sd
, uint8_t value
)
1427 if (setsockopt(sd
, IPPROTO_IPV6
, IPV6_UNICAST_HOPS
, &ttl
, sizeof(ttl
))
1429 zlog_warn("set-ttlv6: setsockopt(IPV6_UNICAST_HOPS, %d): %s",
1430 value
, strerror(errno
));
1437 int bp_set_tosv6(int sd
, uint8_t value
)
1441 if (setsockopt(sd
, IPPROTO_IPV6
, IPV6_TCLASS
, &tos
, sizeof(tos
))
1443 zlog_warn("set-tosv6: setsockopt(IPV6_TCLASS, %d): %s", value
,
1451 static void bp_set_ipv6opts(int sd
)
1453 int ipv6_pktinfo
= BFD_IPV6_PKT_INFO_VAL
;
1454 int ipv6_only
= BFD_IPV6_ONLY_VAL
;
1456 if (bp_set_ttlv6(sd
, BFD_TTL_VAL
) == -1)
1458 "set-ipv6opts: setsockopt(IPV6_UNICAST_HOPS, %d): %s",
1459 BFD_TTL_VAL
, strerror(errno
));
1461 if (setsockopt_ipv6_hoplimit(sd
, BFD_RCV_TTL_VAL
) == -1)
1462 zlog_fatal("set-ipv6opts: setsockopt(IPV6_HOPLIMIT, %d): %s",
1463 BFD_RCV_TTL_VAL
, strerror(errno
));
1465 if (setsockopt_ipv6_pktinfo(sd
, ipv6_pktinfo
) == -1)
1466 zlog_fatal("set-ipv6opts: setsockopt(IPV6_PKTINFO, %d): %s",
1467 ipv6_pktinfo
, strerror(errno
));
1469 if (setsockopt(sd
, IPPROTO_IPV6
, IPV6_V6ONLY
, &ipv6_only
,
1472 zlog_fatal("set-ipv6opts: setsockopt(IPV6_V6ONLY, %d): %s",
1473 ipv6_only
, strerror(errno
));
1476 static void bp_bind_ipv6(int sd
, uint16_t port
)
1478 struct sockaddr_in6 sin6
;
1480 memset(&sin6
, 0, sizeof(sin6
));
1481 sin6
.sin6_family
= AF_INET6
;
1482 sin6
.sin6_addr
= in6addr_any
;
1483 sin6
.sin6_port
= htons(port
);
1484 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1485 sin6
.sin6_len
= sizeof(sin6
);
1486 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1487 if (bind(sd
, (struct sockaddr
*)&sin6
, sizeof(sin6
)) == -1)
1488 zlog_fatal("bind-ipv6: bind: %s", strerror(errno
));
1491 int bp_udp6_shop(const struct vrf
*vrf
)
1495 frr_with_privs(&bglobal
.bfdd_privs
) {
1496 sd
= vrf_socket(AF_INET6
, SOCK_DGRAM
, PF_UNSPEC
, vrf
->vrf_id
,
1500 if (errno
!= EAFNOSUPPORT
)
1501 zlog_fatal("udp6-shop: socket: %s", strerror(errno
));
1503 zlog_warn("udp6-shop: V6 is not supported, continuing");
1508 bp_set_ipv6opts(sd
);
1509 bp_bind_ipv6(sd
, BFD_DEFDESTPORT
);
1514 int bp_udp6_mhop(const struct vrf
*vrf
)
1518 frr_with_privs(&bglobal
.bfdd_privs
) {
1519 sd
= vrf_socket(AF_INET6
, SOCK_DGRAM
, PF_UNSPEC
, vrf
->vrf_id
,
1523 if (errno
!= EAFNOSUPPORT
)
1524 zlog_fatal("udp6-mhop: socket: %s", strerror(errno
));
1526 zlog_warn("udp6-mhop: V6 is not supported, continuing");
1531 bp_set_ipv6opts(sd
);
1532 bp_bind_ipv6(sd
, BFD_DEF_MHOP_DEST_PORT
);
1538 /* tcpdump -dd udp dst port 3785 */
1539 struct sock_filter my_filterudp
[] = {
1540 {0x28, 0, 0, 0x0000000c}, {0x15, 0, 8, 0x00000800},
1541 {0x30, 0, 0, 0x00000017}, {0x15, 0, 6, 0x00000011},
1542 {0x28, 0, 0, 0x00000014}, {0x45, 4, 0, 0x00001fff},
1543 {0xb1, 0, 0, 0x0000000e}, {0x48, 0, 0, 0x00000010},
1544 {0x15, 0, 1, 0x00000ec9}, {0x6, 0, 0, 0x00040000},
1545 {0x6, 0, 0, 0x00000000},
1548 #define MY_FILTER_LENGTH 11
1550 int bp_echo_socket(const struct vrf
*vrf
)
1554 frr_with_privs (&bglobal
.bfdd_privs
) {
1555 s
= vrf_socket(AF_PACKET
, SOCK_RAW
, ETH_P_IP
, vrf
->vrf_id
,
1560 zlog_fatal("echo-socket: socket: %s", strerror(errno
));
1562 struct sock_fprog pf
;
1563 struct sockaddr_ll sll
;
1565 /* adjust filter for socket to only receive ECHO packets */
1566 pf
.filter
= my_filterudp
;
1567 pf
.len
= MY_FILTER_LENGTH
;
1568 if (setsockopt(s
, SOL_SOCKET
, SO_ATTACH_FILTER
, &pf
, sizeof(pf
)) ==
1570 zlog_warn("%s: setsockopt(SO_ATTACH_FILTER): %s", __func__
,
1577 sll
.sll_family
= AF_PACKET
;
1578 sll
.sll_protocol
= htons(ETH_P_IP
);
1579 sll
.sll_ifindex
= 0;
1580 if (bind(s
, (struct sockaddr
*)&sll
, sizeof(sll
)) < 0) {
1581 zlog_warn("Failed to bind echo socket: %s",
1582 safe_strerror(errno
));
1590 int bp_echo_socket(const struct vrf
*vrf
)
1594 frr_with_privs(&bglobal
.bfdd_privs
) {
1595 s
= vrf_socket(AF_INET
, SOCK_DGRAM
, 0, vrf
->vrf_id
, vrf
->name
);
1598 zlog_fatal("echo-socket: socket: %s", strerror(errno
));
1601 bp_bind_ip(s
, BFD_DEF_ECHO_PORT
);
1607 int bp_echov6_socket(const struct vrf
*vrf
)
1611 frr_with_privs(&bglobal
.bfdd_privs
) {
1612 s
= vrf_socket(AF_INET6
, SOCK_DGRAM
, 0, vrf
->vrf_id
, vrf
->name
);
1615 if (errno
!= EAFNOSUPPORT
)
1616 zlog_fatal("echov6-socket: socket: %s",
1619 zlog_warn("echov6-socket: V6 is not supported, continuing");
1625 bp_bind_ipv6(s
, BFD_DEF_ECHO_PORT
);
1631 /* get peer's mac address to be used with Echo packets when they are looped in
1632 * peers forwarding plane
1634 void bfd_peer_mac_set(int sd
, struct bfd_session
*bfd
,
1635 struct sockaddr_any
*peer
, struct interface
*ifp
)
1637 struct arpreq arpreq_
;
1639 if (CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_MAC_SET
))
1642 if (peer
->sa_sin
.sin_family
== AF_INET
) {
1644 struct sockaddr_in
*addr
=
1645 (struct sockaddr_in
*)&arpreq_
.arp_pa
;
1647 memset(&arpreq_
, 0, sizeof(struct arpreq
));
1648 addr
->sin_family
= AF_INET
;
1649 memcpy(&addr
->sin_addr
.s_addr
, &peer
->sa_sin
.sin_addr
,
1650 sizeof(addr
->sin_addr
));
1651 strlcpy(arpreq_
.arp_dev
, ifp
->name
, sizeof(arpreq_
.arp_dev
));
1653 if (ioctl(sd
, SIOCGARP
, &arpreq_
) < 0) {
1654 zlog_warn("BFD: getting peer's mac failed error %s",
1656 UNSET_FLAG(bfd
->flags
, BFD_SESS_FLAG_MAC_SET
);
1657 memset(bfd
->peer_hw_addr
, 0, sizeof(bfd
->peer_hw_addr
));
1660 memcpy(bfd
->peer_hw_addr
, arpreq_
.arp_ha
.sa_data
,
1661 sizeof(bfd
->peer_hw_addr
));
1662 SET_FLAG(bfd
->flags
, BFD_SESS_FLAG_MAC_SET
);