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"
43 struct bfd_raw_echo_pkt
{
46 #endif /* BFD_LINUX */
51 struct bfd_echo_pkt data
;
54 #define IP_ECHO_PKT_LEN (IP_HDR_LEN + UDP_HDR_LEN + BFD_ECHO_PKT_LEN)
55 #define UDP_ECHO_PKT_LEN (UDP_HDR_LEN + BFD_ECHO_PKT_LEN)
61 static uint16_t ptm_bfd_gen_IP_ID(struct bfd_session
*bfd
);
62 static void ptm_bfd_echo_pkt_create(struct bfd_session
*bfd
);
63 static int ptm_bfd_echo_loopback(uint8_t *pkt
, int pkt_len
, struct sockaddr
*ss
,
65 static int ptm_bfd_process_echo_pkt(int s
);
67 static void bfd_sd_reschedule(int sd
);
68 ssize_t
bfd_recv_ipv4(int sd
, uint8_t *msgbuf
, size_t msgbuflen
, uint8_t *ttl
,
69 char *port
, size_t portlen
, char *vrfname
,
70 size_t vrfnamelen
, struct sockaddr_any
*local
,
71 struct sockaddr_any
*peer
);
72 ssize_t
bfd_recv_ipv6(int sd
, uint8_t *msgbuf
, size_t msgbuflen
, uint8_t *ttl
,
73 char *port
, size_t portlen
, char *vrfname
,
74 size_t vrfnamelen
, struct sockaddr_any
*local
,
75 struct sockaddr_any
*peer
);
77 /* socket related prototypes */
78 static void bp_set_ipopts(int sd
);
79 static void bp_bind_ip(int sd
, uint16_t port
);
80 static void bp_set_ipv6opts(int sd
);
81 static void bp_bind_ipv6(int sd
, uint16_t port
);
87 uint16_t checksum(uint16_t *buf
, int len
)
92 int size
= sizeof(uint16_t);
100 *(uint8_t *)(&csum
) = *(uint8_t *)buf
;
104 sum
= (sum
>> 16) + (sum
& 0xFFFF);
110 static uint16_t ptm_bfd_gen_IP_ID(struct bfd_session
*bfd
)
112 return (++bfd
->ip_id
);
115 static int _ptm_bfd_send(struct bfd_session
*bs
, bool use_layer2
,
116 uint16_t *port
, const void *data
, size_t datalen
)
119 struct sockaddr_in sin
;
120 struct sockaddr_in6 sin6
;
122 struct sockaddr_ll dll
;
123 #endif /* BFD_LINUX */
130 memset(&dll
, 0, sizeof(dll
));
131 dll
.sll_family
= AF_PACKET
;
132 dll
.sll_protocol
= htons(ETH_P_IP
);
133 memcpy(dll
.sll_addr
, bs
->peer_mac
, ETHERNET_ADDRESS_LENGTH
);
134 dll
.sll_halen
= htons(ETHERNET_ADDRESS_LENGTH
);
135 dll
.sll_ifindex
= bs
->ifindex
;
137 sd
= bglobal
.bg_echo
;
138 sa
= (struct sockaddr
*)&dll
;
141 /* TODO: implement layer 2 send for *BSDs. */
142 log_warning("packet-send: not implemented");
145 } else if (BFD_CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_IPV6
)) {
146 memset(&sin6
, 0, sizeof(sin6
));
147 sin6
.sin6_family
= AF_INET6
;
148 sin6
.sin6_addr
= bs
->shop
.peer
.sa_sin6
.sin6_addr
;
151 : (BFD_CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_MH
))
152 ? htons(BFD_DEF_MHOP_DEST_PORT
)
153 : htons(BFD_DEFDESTPORT
);
156 sa
= (struct sockaddr
*)&sin6
;
159 memset(&sin
, 0, sizeof(sin
));
160 sin
.sin_family
= AF_INET
;
161 sin
.sin_addr
= bs
->shop
.peer
.sa_sin
.sin_addr
;
164 : (BFD_CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_MH
))
165 ? htons(BFD_DEF_MHOP_DEST_PORT
)
166 : htons(BFD_DEFDESTPORT
);
169 sa
= (struct sockaddr
*)&sin
;
173 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
175 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
176 rv
= sendto(sd
, data
, datalen
, 0, sa
, slen
);
178 log_debug("packet-send: send failure: %s", strerror(errno
));
181 if (rv
< (ssize_t
)datalen
)
182 log_debug("packet-send: send partial", strerror(errno
));
187 static void ptm_bfd_echo_pkt_create(struct bfd_session
*bfd
)
189 struct bfd_raw_echo_pkt ep
;
190 uint8_t *pkt
= bfd
->echo_pkt
;
192 memset(&ep
, 0, sizeof(ep
));
193 memset(bfd
->echo_pkt
, 0, sizeof(bfd
->echo_pkt
));
195 /* Construct ethernet header information */
196 memcpy(pkt
, bfd
->peer_mac
, ETHERNET_ADDRESS_LENGTH
);
197 pkt
= pkt
+ ETHERNET_ADDRESS_LENGTH
;
198 memcpy(pkt
, bfd
->local_mac
, ETHERNET_ADDRESS_LENGTH
);
199 pkt
= pkt
+ ETHERNET_ADDRESS_LENGTH
;
201 pkt
[0] = ETH_P_IP
/ 256;
202 pkt
[1] = ETH_P_IP
% 256;
203 #endif /* BFD_LINUX */
205 pkt
[0] = ETHERTYPE_IP
/ 256;
206 pkt
[1] = ETHERTYPE_IP
% 256;
210 /* Construct IP header information */
215 ep
.ip
.tot_len
= htons(IP_ECHO_PKT_LEN
);
216 ep
.ip
.id
= htons(ptm_bfd_gen_IP_ID(bfd
));
218 ep
.ip
.ttl
= BFD_TTL_VAL
;
219 ep
.ip
.protocol
= IPPROTO_UDP
;
220 ep
.ip
.saddr
= bfd
->local_ip
.sa_sin
.sin_addr
.s_addr
;
221 ep
.ip
.daddr
= bfd
->shop
.peer
.sa_sin
.sin_addr
.s_addr
;
222 ep
.ip
.check
= checksum((uint16_t *)&ep
.ip
, IP_HDR_LEN
);
223 #endif /* BFD_LINUX */
228 ep
.ip
.ip_len
= htons(IP_ECHO_PKT_LEN
);
229 ep
.ip
.ip_id
= htons(ptm_bfd_gen_IP_ID(bfd
));
231 ep
.ip
.ip_ttl
= BFD_TTL_VAL
;
232 ep
.ip
.ip_p
= IPPROTO_UDP
;
233 ep
.ip
.ip_src
= bfd
->local_ip
.sa_sin
.sin_addr
;
234 ep
.ip
.ip_dst
= bfd
->shop
.peer
.sa_sin
.sin_addr
;
235 ep
.ip
.ip_sum
= checksum((uint16_t *)&ep
.ip
, IP_HDR_LEN
);
238 /* Construct UDP header information */
240 ep
.udp
.source
= htons(BFD_DEF_ECHO_PORT
);
241 ep
.udp
.dest
= htons(BFD_DEF_ECHO_PORT
);
242 ep
.udp
.len
= htons(UDP_ECHO_PKT_LEN
);
243 #endif /* BFD_LINUX */
245 ep
.udp
.uh_sport
= htons(BFD_DEF_ECHO_PORT
);
246 ep
.udp
.uh_dport
= htons(BFD_DEF_ECHO_PORT
);
247 ep
.udp
.uh_ulen
= htons(UDP_ECHO_PKT_LEN
);
250 /* Construct Echo packet information */
251 ep
.data
.ver
= BFD_ECHO_VERSION
;
252 ep
.data
.len
= BFD_ECHO_PKT_LEN
;
253 ep
.data
.my_discr
= htonl(bfd
->discrs
.my_discr
);
256 #endif /* BFD_LINUX */
260 udp4_checksum(&ep
.ip
, (uint8_t *)&ep
.udp
,
263 memcpy(pkt
, &ep
, sizeof(ep
));
266 void ptm_bfd_echo_snd(struct bfd_session
*bfd
)
268 struct bfd_raw_echo_pkt
*ep
;
269 bool use_layer2
= false;
272 uint16_t port
= htons(BFD_DEF_ECHO_PORT
);
274 if (!BFD_CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_ECHO_ACTIVE
)) {
275 ptm_bfd_echo_pkt_create(bfd
);
276 BFD_SET_FLAG(bfd
->flags
, BFD_SESS_FLAG_ECHO_ACTIVE
);
278 /* just update the checksum and ip Id */
279 ep
= (struct bfd_raw_echo_pkt
*)(bfd
->echo_pkt
+ ETH_HDR_LEN
);
281 ep
->ip
.id
= htons(ptm_bfd_gen_IP_ID(bfd
));
283 ep
->ip
.check
= checksum((uint16_t *)&ep
->ip
, IP_HDR_LEN
);
284 #endif /* BFD_LINUX */
286 ep
->ip
.ip_id
= htons(ptm_bfd_gen_IP_ID(bfd
));
288 ep
->ip
.ip_sum
= checksum((uint16_t *)&ep
->ip
, IP_HDR_LEN
);
294 pktlen
= BFD_ECHO_PKT_TOT_LEN
;
296 pkt
= &bfd
->echo_pkt
[ETH_HDR_LEN
+ IP_HDR_LEN
+ UDP_HDR_LEN
];
297 pktlen
= BFD_ECHO_PKT_TOT_LEN
298 - (ETH_HDR_LEN
+ IP_HDR_LEN
+ UDP_HDR_LEN
);
301 if (_ptm_bfd_send(bfd
, use_layer2
, &port
, pkt
, pktlen
) != 0) {
302 log_debug("echo-packet: send failure: %s", strerror(errno
));
306 bfd
->stats
.tx_echo_pkt
++;
309 static int ptm_bfd_echo_loopback(uint8_t *pkt
, int pkt_len
, struct sockaddr
*ss
,
313 struct bfd_raw_echo_pkt
*ep
=
314 (struct bfd_raw_echo_pkt
*)(pkt
+ ETH_HDR_LEN
);
315 uint8_t temp_mac
[ETHERNET_ADDRESS_LENGTH
];
317 struct ethhdr
*eth
= (struct ethhdr
*)pkt
;
319 /* swap the mac addresses */
320 memcpy(temp_mac
, eth
->h_source
, ETHERNET_ADDRESS_LENGTH
);
321 memcpy(eth
->h_source
, eth
->h_dest
, ETHERNET_ADDRESS_LENGTH
);
322 memcpy(eth
->h_dest
, temp_mac
, ETHERNET_ADDRESS_LENGTH
);
324 /* swap ip addresses */
325 temp_ip
= ep
->ip
.saddr
;
326 ep
->ip
.saddr
= ep
->ip
.daddr
;
327 ep
->ip
.daddr
= temp_ip
;
329 ep
->ip
.ttl
= ep
->ip
.ttl
- 1;
331 ep
->ip
.check
= checksum((uint16_t *)ep
, IP_HDR_LEN
);
332 #endif /* BFD_LINUX */
333 #ifdef BFD_BSD_FILTER
334 struct bfd_raw_echo_pkt_t
*ep
=
335 (struct bfd_raw_echo_pkt
*)(pkt
+ ETH_HDR_LEN
);
336 uint8_t temp_mac
[ETHERNET_ADDRESS_LENGTH
];
337 struct in_addr temp_ip
;
338 struct ether_header
*ether
= (struct ether_header
*)pkt
;
341 * TODO: this is not yet implemented and requires BPF code for
342 * OmniOS, NetBSD and FreeBSD9.
345 /* swap the mac addresses */
346 memcpy(temp_mac
, ether
->ether_shost
, ETHERNET_ADDRESS_LENGTH
);
347 memcpy(ether
->ether_shost
, ether
->ether_dhost
, ETHERNET_ADDRESS_LENGTH
);
348 memcpy(ether
->ether_dhost
, temp_mac
, ETHERNET_ADDRESS_LENGTH
);
350 /* swap ip addresses */
351 temp_ip
= ep
->ip
.ip_src
;
352 ep
->ip
.ip_src
= ep
->ip
.ip_dst
;
353 ep
->ip
.ip_dst
= temp_ip
;
355 ep
->ip
.ip_ttl
= ep
->ip
.ip_ttl
- 1;
357 ep
->ip
.ip_sum
= checksum((uint16_t *)ep
, IP_HDR_LEN
);
358 #endif /* BFD_BSD_FILTER */
360 if (sendto(bglobal
.bg_echo
, pkt
, pkt_len
, 0, ss
, sslen
) < 0) {
361 log_debug("echo-loopback: send failure: %s", strerror(errno
));
368 static int ptm_bfd_process_echo_pkt(int s
)
370 uint32_t my_discr
= 0;
371 struct sockaddr_storage ss
;
372 socklen_t sslen
= sizeof(ss
);
373 uint8_t rx_pkt
[BFD_RX_BUF_LEN
];
374 ssize_t pkt_len
= sizeof(rx_pkt
);
375 struct bfd_session
*bfd
;
377 struct bfd_raw_echo_pkt
*ep
;
380 * valgrind: memset() ss so valgrind doesn't complain about
381 * uninitialized memory.
383 memset(&ss
, 0, sizeof(ss
));
384 pkt_len
= recvfrom(s
, rx_pkt
, sizeof(rx_pkt
), MSG_DONTWAIT
,
385 (struct sockaddr
*)&ss
, &sslen
);
388 log_error("echo-packet: read failure: %s",
394 /* Check if we have at least the basic headers to send back. */
395 if (pkt_len
< BFD_ECHO_PKT_TOT_LEN
) {
396 log_debug("echo-packet: too short (got %ld, expected %d)",
397 pkt_len
, BFD_ECHO_PKT_TOT_LEN
);
401 ep
= (struct bfd_raw_echo_pkt
*)(rx_pkt
+ ETH_HDR_LEN
);
402 /* if TTL = 255, assume that the received echo packet has
405 if (ep
->ip
.ttl
== BFD_TTL_VAL
)
406 return ptm_bfd_echo_loopback(rx_pkt
, pkt_len
,
407 (struct sockaddr
*)&ss
,
408 sizeof(struct sockaddr_ll
));
410 my_discr
= ntohl(ep
->data
.my_discr
);
411 if (ep
->data
.my_discr
== 0) {
412 log_debug("echo-packet: 'my discriminator' is zero");
415 #endif /* BFD_LINUX */
421 * bsd_echo_sock_read() already treats invalid TTL values and
422 * zeroed discriminators.
424 rv
= bsd_echo_sock_read(s
, rx_pkt
, &pkt_len
, &ss
, &sslen
, &ttl
,
429 if (ttl
== BFD_TTL_VAL
)
430 return ptm_bfd_echo_loopback(rx_pkt
, pkt_len
,
431 (struct sockaddr
*)&ss
, sslen
);
434 /* Your discriminator not zero - use it to find session */
435 bfd
= bfd_id_lookup(my_discr
);
437 log_debug("echo-packet: no matching session (id:%u)", my_discr
);
441 if (!BFD_CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_ECHO_ACTIVE
)) {
442 log_debug("echo-packet: echo disabled [%s]", my_discr
,
447 bfd
->stats
.rx_echo_pkt
++;
449 /* Compute detect time */
450 bfd
->echo_detect_TO
= bfd
->remote_detect_mult
* bfd
->echo_xmt_TO
;
452 /* Update echo receive timeout. */
453 bfd_echo_recvtimer_update(bfd
);
458 void ptm_bfd_snd(struct bfd_session
*bfd
, int fbit
)
462 /* Set fields according to section 6.5.7 */
463 cp
.diag
= bfd
->local_diag
;
464 BFD_SETVER(cp
.diag
, BFD_VERSION
);
466 BFD_SETSTATE(cp
.flags
, bfd
->ses_state
);
467 BFD_SETDEMANDBIT(cp
.flags
, BFD_DEF_DEMAND
);
468 BFD_SETPBIT(cp
.flags
, bfd
->polling
);
469 BFD_SETFBIT(cp
.flags
, fbit
);
470 cp
.detect_mult
= bfd
->detect_mult
;
471 cp
.len
= BFD_PKT_LEN
;
472 cp
.discrs
.my_discr
= htonl(bfd
->discrs
.my_discr
);
473 cp
.discrs
.remote_discr
= htonl(bfd
->discrs
.remote_discr
);
475 cp
.timers
.desired_min_tx
=
476 htonl(bfd
->new_timers
.desired_min_tx
);
477 cp
.timers
.required_min_rx
=
478 htonl(bfd
->new_timers
.required_min_rx
);
480 cp
.timers
.desired_min_tx
= htonl(bfd
->timers
.desired_min_tx
);
481 cp
.timers
.required_min_rx
= htonl(bfd
->timers
.required_min_rx
);
483 cp
.timers
.required_min_echo
= htonl(bfd
->timers
.required_min_echo
);
485 if (_ptm_bfd_send(bfd
, false, NULL
, &cp
, BFD_PKT_LEN
) != 0)
488 bfd
->stats
.tx_ctrl_pkt
++;
491 ssize_t
bfd_recv_ipv4(int sd
, uint8_t *msgbuf
, size_t msgbuflen
, uint8_t *ttl
,
492 char *port
, size_t portlen
, char *vrfname
,
493 size_t vrfnamelen
, struct sockaddr_any
*local
,
494 struct sockaddr_any
*peer
)
499 struct sockaddr_in msgaddr
;
500 struct msghdr msghdr
;
502 uint8_t cmsgbuf
[255];
504 /* Prepare the recvmsg params. */
505 iov
[0].iov_base
= msgbuf
;
506 iov
[0].iov_len
= msgbuflen
;
508 memset(&msghdr
, 0, sizeof(msghdr
));
509 msghdr
.msg_name
= &msgaddr
;
510 msghdr
.msg_namelen
= sizeof(msgaddr
);
511 msghdr
.msg_iov
= iov
;
512 msghdr
.msg_iovlen
= 1;
513 msghdr
.msg_control
= cmsgbuf
;
514 msghdr
.msg_controllen
= sizeof(cmsgbuf
);
516 mlen
= recvmsg(sd
, &msghdr
, MSG_DONTWAIT
);
519 log_error("ipv4-recv: recv failed: %s",
525 /* Get source address */
526 peer
->sa_sin
= *((struct sockaddr_in
*)(msghdr
.msg_name
));
528 /* Get and check TTL */
529 for (cm
= CMSG_FIRSTHDR(&msghdr
); cm
!= NULL
;
530 cm
= CMSG_NXTHDR(&msghdr
, cm
)) {
531 if (cm
->cmsg_level
!= IPPROTO_IP
)
534 switch (cm
->cmsg_type
) {
539 memcpy(&ttlval
, CMSG_DATA(cm
), sizeof(ttlval
));
541 log_debug("ipv4-recv: invalid TTL: %u", ttlval
);
549 struct in_pktinfo
*pi
=
550 (struct in_pktinfo
*)CMSG_DATA(cm
);
555 local
->sa_sin
.sin_family
= AF_INET
;
556 local
->sa_sin
.sin_addr
= pi
->ipi_addr
;
557 fetch_portname_from_ifindex(pi
->ipi_ifindex
, port
,
561 #endif /* BFD_LINUX */
564 memcpy(ttl
, CMSG_DATA(cm
), sizeof(*ttl
));
568 case IP_RECVDSTADDR
: {
571 memcpy(&ia
, CMSG_DATA(cm
), sizeof(ia
));
572 local
->sa_sin
.sin_family
= AF_INET
;
573 local
->sa_sin
.sin_addr
= ia
;
580 * On *BSDs we expect to land here when skipping
581 * the IP_RECVIF header. It will be handled by
582 * getsockopt_ifindex() below.
589 /* OS agnostic way of getting interface name. */
591 ifindex
= getsockopt_ifindex(AF_INET
, &msghdr
);
593 fetch_portname_from_ifindex(ifindex
, port
, portlen
);
599 ssize_t
bfd_recv_ipv6(int sd
, uint8_t *msgbuf
, size_t msgbuflen
, uint8_t *ttl
,
600 char *port
, size_t portlen
, char *vrfname
,
601 size_t vrfnamelen
, struct sockaddr_any
*local
,
602 struct sockaddr_any
*peer
)
605 struct in6_pktinfo
*pi6
= NULL
;
609 struct sockaddr_in6 msgaddr6
;
610 struct msghdr msghdr6
;
612 uint8_t cmsgbuf6
[255];
614 /* Prepare the recvmsg params. */
615 iov
[0].iov_base
= msgbuf
;
616 iov
[0].iov_len
= msgbuflen
;
618 memset(&msghdr6
, 0, sizeof(msghdr6
));
619 msghdr6
.msg_name
= &msgaddr6
;
620 msghdr6
.msg_namelen
= sizeof(msgaddr6
);
621 msghdr6
.msg_iov
= iov
;
622 msghdr6
.msg_iovlen
= 1;
623 msghdr6
.msg_control
= cmsgbuf6
;
624 msghdr6
.msg_controllen
= sizeof(cmsgbuf6
);
626 mlen
= recvmsg(sd
, &msghdr6
, MSG_DONTWAIT
);
629 log_error("ipv6-recv: recv failed: %s",
635 /* Get source address */
636 peer
->sa_sin6
= *((struct sockaddr_in6
*)(msghdr6
.msg_name
));
638 /* Get and check TTL */
639 for (cm
= CMSG_FIRSTHDR(&msghdr6
); cm
!= NULL
;
640 cm
= CMSG_NXTHDR(&msghdr6
, cm
)) {
641 if (cm
->cmsg_level
!= IPPROTO_IPV6
)
644 if (cm
->cmsg_type
== IPV6_HOPLIMIT
) {
645 memcpy(&ttlval
, CMSG_DATA(cm
), sizeof(ttlval
));
647 log_debug("ipv6-recv: invalid TTL: %u", ttlval
);
652 } else if (cm
->cmsg_type
== IPV6_PKTINFO
) {
653 pi6
= (struct in6_pktinfo
*)CMSG_DATA(cm
);
655 local
->sa_sin
.sin_family
= AF_INET6
;
656 local
->sa_sin6
.sin6_addr
= pi6
->ipi6_addr
;
657 fetch_portname_from_ifindex(pi6
->ipi6_ifindex
,
659 ifindex
= pi6
->ipi6_ifindex
;
664 /* Set scope ID for link local addresses. */
665 if (IN6_IS_ADDR_LINKLOCAL(&peer
->sa_sin6
.sin6_addr
))
666 peer
->sa_sin6
.sin6_scope_id
= ifindex
;
667 if (IN6_IS_ADDR_LINKLOCAL(&local
->sa_sin6
.sin6_addr
))
668 local
->sa_sin6
.sin6_scope_id
= ifindex
;
673 static void bfd_sd_reschedule(int sd
)
675 if (sd
== bglobal
.bg_shop
) {
676 bglobal
.bg_ev
[0] = NULL
;
677 thread_add_read(master
, bfd_recv_cb
, NULL
, bglobal
.bg_shop
,
679 } else if (sd
== bglobal
.bg_mhop
) {
680 bglobal
.bg_ev
[1] = NULL
;
681 thread_add_read(master
, bfd_recv_cb
, NULL
, bglobal
.bg_mhop
,
683 } else if (sd
== bglobal
.bg_shop6
) {
684 bglobal
.bg_ev
[2] = NULL
;
685 thread_add_read(master
, bfd_recv_cb
, NULL
, bglobal
.bg_shop6
,
687 } else if (sd
== bglobal
.bg_mhop6
) {
688 bglobal
.bg_ev
[3] = NULL
;
689 thread_add_read(master
, bfd_recv_cb
, NULL
, bglobal
.bg_mhop6
,
691 } else if (sd
== bglobal
.bg_echo
) {
692 bglobal
.bg_ev
[4] = NULL
;
693 thread_add_read(master
, bfd_recv_cb
, NULL
, bglobal
.bg_echo
,
698 static void cp_debug(bool mhop
, struct sockaddr_any
*peer
,
699 struct sockaddr_any
*local
, const char *port
,
700 const char *vrf
, const char *fmt
, ...)
702 char buf
[512], peerstr
[128], localstr
[128], portstr
[64], vrfstr
[64];
705 if (peer
->sa_sin
.sin_family
)
706 snprintf(peerstr
, sizeof(peerstr
), " peer:%s", satostr(peer
));
710 if (local
->sa_sin
.sin_family
)
711 snprintf(localstr
, sizeof(localstr
), " local:%s",
717 snprintf(portstr
, sizeof(portstr
), " port:%s", port
);
722 snprintf(vrfstr
, sizeof(vrfstr
), " vrf:%s", port
);
727 vsnprintf(buf
, sizeof(buf
), fmt
, vl
);
730 log_debug("control-packet: %s [mhop:%s%s%s%s%s]", buf
,
731 mhop
? "yes" : "no", peerstr
, localstr
, portstr
, vrfstr
);
734 int bfd_recv_cb(struct thread
*t
)
736 int sd
= THREAD_FD(t
);
737 struct bfd_session
*bfd
;
741 uint32_t oldEchoXmt_TO
, oldXmtTime
;
743 struct sockaddr_any local
, peer
;
744 char port
[MAXNAMELEN
+ 1], vrfname
[MAXNAMELEN
+ 1];
745 uint8_t msgbuf
[1516];
747 /* Schedule next read. */
748 bfd_sd_reschedule(sd
);
750 /* Handle echo packets. */
751 if (sd
== bglobal
.bg_echo
) {
752 ptm_bfd_process_echo_pkt(sd
);
756 /* Sanitize input/output. */
757 memset(port
, 0, sizeof(port
));
758 memset(vrfname
, 0, sizeof(vrfname
));
759 memset(&local
, 0, sizeof(local
));
760 memset(&peer
, 0, sizeof(peer
));
762 /* Handle control packets. */
764 if (sd
== bglobal
.bg_shop
|| sd
== bglobal
.bg_mhop
) {
765 is_mhop
= sd
== bglobal
.bg_mhop
;
766 mlen
= bfd_recv_ipv4(sd
, msgbuf
, sizeof(msgbuf
), &ttl
, port
,
767 sizeof(port
), vrfname
, sizeof(vrfname
),
769 } else if (sd
== bglobal
.bg_shop6
|| sd
== bglobal
.bg_mhop6
) {
770 is_mhop
= sd
== bglobal
.bg_mhop6
;
771 mlen
= bfd_recv_ipv6(sd
, msgbuf
, sizeof(msgbuf
), &ttl
, port
,
772 sizeof(port
), vrfname
, sizeof(vrfname
),
776 /* Implement RFC 5880 6.8.6 */
777 if (mlen
< BFD_PKT_LEN
) {
778 cp_debug(is_mhop
, &peer
, &local
, port
, vrfname
,
779 "too small (%ld bytes)", mlen
);
783 /* Validate packet TTL. */
784 if ((is_mhop
== false) && (ttl
!= BFD_TTL_VAL
)) {
785 cp_debug(is_mhop
, &peer
, &local
, port
, vrfname
,
786 "invalid TTL: %d expected %d", ttl
, BFD_TTL_VAL
);
791 * Parse the control header for inconsistencies:
793 * - Bad multiplier configuration;
795 * - Invalid discriminator;
797 cp
= (struct bfd_pkt
*)(msgbuf
);
798 if (BFD_GETVER(cp
->diag
) != BFD_VERSION
) {
799 cp_debug(is_mhop
, &peer
, &local
, port
, vrfname
,
800 "bad version %d", BFD_GETVER(cp
->diag
));
804 if (cp
->detect_mult
== 0) {
805 cp_debug(is_mhop
, &peer
, &local
, port
, vrfname
,
806 "detect multiplier set to zero");
810 if ((cp
->len
< BFD_PKT_LEN
) || (cp
->len
> mlen
)) {
811 cp_debug(is_mhop
, &peer
, &local
, port
, vrfname
, "too small");
815 if (cp
->discrs
.my_discr
== 0) {
816 cp_debug(is_mhop
, &peer
, &local
, port
, vrfname
,
817 "'my discriminator' is zero");
821 /* Find the session that this packet belongs. */
822 bfd
= ptm_bfd_sess_find(cp
, port
, &peer
, &local
, vrfname
, is_mhop
);
824 cp_debug(is_mhop
, &peer
, &local
, port
, vrfname
,
829 bfd
->stats
.rx_ctrl_pkt
++;
832 * Multi hop: validate packet TTL.
833 * Single hop: set local address that received the packet.
836 if ((BFD_TTL_VAL
- bfd
->mh_ttl
) > BFD_TTL_VAL
) {
837 cp_debug(is_mhop
, &peer
, &local
, port
, vrfname
,
838 "exceeded max hop count (expected %d, got %d)",
839 bfd
->mh_ttl
, BFD_TTL_VAL
);
842 } else if (bfd
->local_ip
.sa_sin
.sin_family
== AF_UNSPEC
) {
843 bfd
->local_ip
= local
;
847 * If no interface was detected, save the interface where the
850 if (bfd
->ifindex
== 0)
851 bfd
->ifindex
= ptm_bfd_fetch_ifindex(port
);
853 /* Log remote discriminator changes. */
854 if ((bfd
->discrs
.remote_discr
!= 0)
855 && (bfd
->discrs
.remote_discr
!= ntohl(cp
->discrs
.my_discr
)))
856 cp_debug(is_mhop
, &peer
, &local
, port
, vrfname
,
857 "remote discriminator mismatch (expected %d, got %d)",
858 bfd
->discrs
.remote_discr
, ntohl(cp
->discrs
.my_discr
));
860 bfd
->discrs
.remote_discr
= ntohl(cp
->discrs
.my_discr
);
862 /* If received the Final bit, the new values should take effect */
863 if (bfd
->polling
&& BFD_GETFBIT(cp
->flags
)) {
864 bfd
->timers
.desired_min_tx
= bfd
->new_timers
.desired_min_tx
;
865 bfd
->timers
.required_min_rx
= bfd
->new_timers
.required_min_rx
;
866 bfd
->new_timers
.desired_min_tx
= 0;
867 bfd
->new_timers
.required_min_rx
= 0;
871 if (!bfd
->demand_mode
) {
872 /* Compute detect time */
873 bfd
->detect_TO
= cp
->detect_mult
874 * ((bfd
->timers
.required_min_rx
875 > ntohl(cp
->timers
.desired_min_tx
))
876 ? bfd
->timers
.required_min_rx
877 : ntohl(cp
->timers
.desired_min_tx
));
878 bfd
->remote_detect_mult
= cp
->detect_mult
;
880 cp_debug(is_mhop
, &peer
, &local
, port
, vrfname
,
881 "unsupported demand mode");
883 /* Save remote diagnostics before state switch. */
884 bfd
->remote_diag
= cp
->diag
& BFD_DIAGMASK
;
886 /* State switch from section 6.8.6 */
887 if (BFD_GETSTATE(cp
->flags
) == PTM_BFD_ADM_DOWN
) {
888 if (bfd
->ses_state
!= PTM_BFD_DOWN
)
889 ptm_bfd_ses_dn(bfd
, BFD_DIAGNEIGHDOWN
);
891 switch (bfd
->ses_state
) {
893 if (BFD_GETSTATE(cp
->flags
) == PTM_BFD_INIT
)
895 else if (BFD_GETSTATE(cp
->flags
) == PTM_BFD_DOWN
)
896 bfd
->ses_state
= PTM_BFD_INIT
;
899 if (BFD_GETSTATE(cp
->flags
) == PTM_BFD_INIT
900 || BFD_GETSTATE(cp
->flags
) == PTM_BFD_UP
)
904 if (BFD_GETSTATE(cp
->flags
) == PTM_BFD_DOWN
)
905 ptm_bfd_ses_dn(bfd
, BFD_DIAGNEIGHDOWN
);
911 * Handle echo packet status:
912 * - Start echo packets if configured and permitted
913 * (required_min_echo > 0);
914 * - Stop echo packets if not allowed (required_min_echo == 0);
915 * - Recalculate echo packet interval;
917 if (BFD_CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_ECHO
)) {
918 if (BFD_CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_ECHO_ACTIVE
)) {
919 if (!ntohl(cp
->timers
.required_min_echo
)) {
920 ptm_bfd_echo_stop(bfd
, 1);
922 oldEchoXmt_TO
= bfd
->echo_xmt_TO
;
924 bfd
->timers
.required_min_echo
;
925 if (ntohl(cp
->timers
.required_min_echo
)
927 bfd
->echo_xmt_TO
= ntohl(
928 cp
->timers
.required_min_echo
);
929 if (oldEchoXmt_TO
!= bfd
->echo_xmt_TO
)
930 ptm_bfd_echo_start(bfd
);
932 } else if (ntohl(cp
->timers
.required_min_echo
)) {
933 bfd
->echo_xmt_TO
= bfd
->timers
.required_min_echo
;
934 if (ntohl(cp
->timers
.required_min_echo
)
937 ntohl(cp
->timers
.required_min_echo
);
938 ptm_bfd_echo_start(bfd
);
942 if (BFD_CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_ECHO_ACTIVE
)) {
943 bfd
->echo_xmt_TO
= bfd
->timers
.required_min_echo
;
944 if (ntohl(cp
->timers
.required_min_echo
) > bfd
->echo_xmt_TO
)
945 bfd
->echo_xmt_TO
= ntohl(cp
->timers
.required_min_echo
);
948 /* Calculate new transmit time */
949 oldXmtTime
= bfd
->xmt_TO
;
951 (bfd
->timers
.desired_min_tx
> ntohl(cp
->timers
.required_min_rx
))
952 ? bfd
->timers
.desired_min_tx
953 : ntohl(cp
->timers
.required_min_rx
);
955 /* If transmit time has changed, and too much time until next xmt,
958 if (BFD_GETPBIT(cp
->flags
)) {
959 ptm_bfd_xmt_TO(bfd
, 1);
960 } else if (oldXmtTime
!= bfd
->xmt_TO
) {
961 /* XXX add some skid to this as well */
962 ptm_bfd_start_xmt_timer(bfd
, false);
965 /* Restart detection timer (packet received) */
966 if (!bfd
->demand_mode
)
967 bfd_recvtimer_update(bfd
);
970 * Save the timers and state sent by the remote end
971 * for debugging and statistics.
973 if (BFD_GETFBIT(cp
->flags
)) {
974 bfd
->remote_timers
.desired_min_tx
=
975 ntohl(cp
->timers
.desired_min_tx
);
976 bfd
->remote_timers
.required_min_rx
=
977 ntohl(cp
->timers
.required_min_rx
);
978 bfd
->remote_timers
.required_min_echo
=
979 ntohl(cp
->timers
.required_min_echo
);
981 control_notify_config(BCM_NOTIFY_CONFIG_UPDATE
, bfd
);
996 int bp_set_ttl(int sd
, uint8_t value
)
1000 if (setsockopt(sd
, IPPROTO_IP
, IP_TTL
, &ttl
, sizeof(ttl
)) == -1) {
1001 log_warning("set-ttl: setsockopt(IP_TTL, %d): %s", value
,
1009 int bp_set_tos(int sd
, uint8_t value
)
1013 if (setsockopt(sd
, IPPROTO_IP
, IP_TOS
, &tos
, sizeof(tos
)) == -1) {
1014 log_warning("set-tos: setsockopt(IP_TOS, %d): %s", value
,
1022 static void bp_set_ipopts(int sd
)
1024 int rcvttl
= BFD_RCV_TTL_VAL
;
1026 if (bp_set_ttl(sd
, BFD_TTL_VAL
) != 0)
1027 log_fatal("set-ipopts: TTL configuration failed");
1029 if (setsockopt(sd
, IPPROTO_IP
, IP_RECVTTL
, &rcvttl
, sizeof(rcvttl
))
1031 log_fatal("set-ipopts: setsockopt(IP_RECVTTL, %d): %s", rcvttl
,
1035 int pktinfo
= BFD_PKT_INFO_VAL
;
1037 /* Figure out address and interface to do the peer matching. */
1038 if (setsockopt(sd
, IPPROTO_IP
, IP_PKTINFO
, &pktinfo
, sizeof(pktinfo
))
1040 log_fatal("set-ipopts: setsockopt(IP_PKTINFO, %d): %s", pktinfo
,
1042 #endif /* BFD_LINUX */
1046 /* Find out our address for peer matching. */
1047 if (setsockopt(sd
, IPPROTO_IP
, IP_RECVDSTADDR
, &yes
, sizeof(yes
)) == -1)
1048 log_fatal("set-ipopts: setsockopt(IP_RECVDSTADDR, %d): %s", yes
,
1051 /* Find out interface where the packet came in. */
1052 if (setsockopt_ifindex(AF_INET
, sd
, yes
) == -1)
1053 log_fatal("set-ipopts: setsockopt_ipv4_ifindex(%d): %s", yes
,
1055 #endif /* BFD_BSD */
1058 static void bp_bind_ip(int sd
, uint16_t port
)
1060 struct sockaddr_in sin
;
1062 memset(&sin
, 0, sizeof(sin
));
1063 sin
.sin_family
= AF_INET
;
1064 sin
.sin_addr
.s_addr
= htonl(INADDR_ANY
);
1065 sin
.sin_port
= htons(port
);
1066 if (bind(sd
, (struct sockaddr
*)&sin
, sizeof(sin
)) == -1)
1067 log_fatal("bind-ip: bind: %s", strerror(errno
));
1070 int bp_udp_shop(void)
1074 sd
= socket(AF_INET
, SOCK_DGRAM
, PF_UNSPEC
);
1076 log_fatal("udp-shop: socket: %s", strerror(errno
));
1079 bp_bind_ip(sd
, BFD_DEFDESTPORT
);
1084 int bp_udp_mhop(void)
1088 sd
= socket(AF_INET
, SOCK_DGRAM
, PF_UNSPEC
);
1090 log_fatal("udp-mhop: socket: %s", strerror(errno
));
1093 bp_bind_ip(sd
, BFD_DEF_MHOP_DEST_PORT
);
1098 int bp_peer_socket(struct bfd_peer_cfg
*bpc
)
1101 struct sockaddr_in sin
;
1102 static int srcPort
= BFD_SRCPORTINIT
;
1104 sd
= socket(AF_INET
, SOCK_DGRAM
, PF_UNSPEC
);
1106 log_error("ipv4-new: failed to create socket: %s",
1111 /* Set TTL to 255 for all transmitted packets */
1112 if (bp_set_ttl(sd
, BFD_TTL_VAL
) != 0) {
1117 /* Set TOS to CS6 for all transmitted packets */
1118 if (bp_set_tos(sd
, BFD_TOS_VAL
) != 0) {
1123 if (bpc
->bpc_has_localif
) {
1124 if (bp_bind_dev(sd
, bpc
->bpc_localif
) != 0) {
1128 } else if (bpc
->bpc_mhop
&& bpc
->bpc_has_vrfname
) {
1129 if (bp_bind_dev(sd
, bpc
->bpc_vrfname
) != 0) {
1135 /* Find an available source port in the proper range */
1136 memset(&sin
, 0, sizeof(sin
));
1137 sin
= bpc
->bpc_local
.sa_sin
;
1138 sin
.sin_family
= AF_INET
;
1139 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1140 sin
.sin_len
= sizeof(sin
);
1141 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1143 sin
.sin_addr
= bpc
->bpc_local
.sa_sin
.sin_addr
;
1145 sin
.sin_addr
.s_addr
= INADDR_ANY
;
1149 if ((++pcount
) > (BFD_SRCPORTMAX
- BFD_SRCPORTINIT
)) {
1150 /* Searched all ports, none available */
1151 log_error("ipv4-new: failed to bind port: %s",
1156 if (srcPort
>= BFD_SRCPORTMAX
)
1157 srcPort
= BFD_SRCPORTINIT
;
1158 sin
.sin_port
= htons(srcPort
++);
1159 } while (bind(sd
, (struct sockaddr
*)&sin
, sizeof(sin
)) < 0);
1169 int bp_peer_socketv6(struct bfd_peer_cfg
*bpc
)
1171 int sd
, pcount
, ifindex
;
1172 struct sockaddr_in6 sin6
;
1173 static int srcPort
= BFD_SRCPORTINIT
;
1175 sd
= socket(AF_INET6
, SOCK_DGRAM
, PF_UNSPEC
);
1177 log_error("ipv6-new: failed to create socket: %s",
1182 /* Set TTL to 255 for all transmitted packets */
1183 if (bp_set_ttlv6(sd
, BFD_TTL_VAL
) != 0) {
1188 /* Set TOS to CS6 for all transmitted packets */
1189 if (bp_set_tosv6(sd
, BFD_TOS_VAL
) != 0) {
1194 /* Find an available source port in the proper range */
1195 memset(&sin6
, 0, sizeof(sin6
));
1196 sin6
.sin6_family
= AF_INET6
;
1197 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1198 sin6
.sin6_len
= sizeof(sin6
);
1199 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1200 sin6
= bpc
->bpc_local
.sa_sin6
;
1201 ifindex
= ptm_bfd_fetch_ifindex(bpc
->bpc_localif
);
1202 if (IN6_IS_ADDR_LINKLOCAL(&sin6
.sin6_addr
))
1203 sin6
.sin6_scope_id
= ifindex
;
1205 if (bpc
->bpc_has_localif
) {
1206 if (bp_bind_dev(sd
, bpc
->bpc_localif
) != 0) {
1210 } else if (bpc
->bpc_mhop
&& bpc
->bpc_has_vrfname
) {
1211 if (bp_bind_dev(sd
, bpc
->bpc_vrfname
) != 0) {
1219 if ((++pcount
) > (BFD_SRCPORTMAX
- BFD_SRCPORTINIT
)) {
1220 /* Searched all ports, none available */
1221 log_error("ipv6-new: failed to bind port: %s",
1226 if (srcPort
>= BFD_SRCPORTMAX
)
1227 srcPort
= BFD_SRCPORTINIT
;
1228 sin6
.sin6_port
= htons(srcPort
++);
1229 } while (bind(sd
, (struct sockaddr
*)&sin6
, sizeof(sin6
)) < 0);
1234 int bp_set_ttlv6(int sd
, uint8_t value
)
1238 if (setsockopt(sd
, IPPROTO_IPV6
, IPV6_UNICAST_HOPS
, &ttl
, sizeof(ttl
))
1240 log_warning("set-ttlv6: setsockopt(IPV6_UNICAST_HOPS, %d): %s",
1241 value
, strerror(errno
));
1248 int bp_set_tosv6(int sd
, uint8_t value
)
1252 if (setsockopt(sd
, IPPROTO_IPV6
, IPV6_TCLASS
, &tos
, sizeof(tos
))
1254 log_warning("set-tosv6: setsockopt(IPV6_TCLASS, %d): %s", value
,
1262 static void bp_set_ipv6opts(int sd
)
1264 int ipv6_pktinfo
= BFD_IPV6_PKT_INFO_VAL
;
1265 int ipv6_only
= BFD_IPV6_ONLY_VAL
;
1267 if (bp_set_ttlv6(sd
, BFD_TTL_VAL
) == -1)
1268 log_fatal("set-ipv6opts: setsockopt(IPV6_UNICAST_HOPS, %d): %s",
1269 BFD_TTL_VAL
, strerror(errno
));
1271 if (setsockopt_ipv6_hoplimit(sd
, BFD_RCV_TTL_VAL
) == -1)
1272 log_fatal("set-ipv6opts: setsockopt(IPV6_HOPLIMIT, %d): %s",
1273 BFD_RCV_TTL_VAL
, strerror(errno
));
1275 if (setsockopt_ipv6_pktinfo(sd
, ipv6_pktinfo
) == -1)
1276 log_fatal("set-ipv6opts: setsockopt(IPV6_PKTINFO, %d): %s",
1277 ipv6_pktinfo
, strerror(errno
));
1279 if (setsockopt(sd
, IPPROTO_IPV6
, IPV6_V6ONLY
, &ipv6_only
,
1282 log_fatal("set-ipv6opts: setsockopt(IPV6_V6ONLY, %d): %s",
1283 ipv6_only
, strerror(errno
));
1286 static void bp_bind_ipv6(int sd
, uint16_t port
)
1288 struct sockaddr_in6 sin6
;
1290 memset(&sin6
, 0, sizeof(sin6
));
1291 sin6
.sin6_family
= AF_INET6
;
1292 sin6
.sin6_addr
= in6addr_any
;
1293 sin6
.sin6_port
= htons(port
);
1294 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1295 sin6
.sin6_len
= sizeof(sin6
);
1296 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1297 if (bind(sd
, (struct sockaddr
*)&sin6
, sizeof(sin6
)) == -1)
1298 log_fatal("bind-ipv6: bind: %s", strerror(errno
));
1301 int bp_udp6_shop(void)
1305 sd
= socket(AF_INET6
, SOCK_DGRAM
, PF_UNSPEC
);
1307 log_fatal("udp6-shop: socket: %s", strerror(errno
));
1309 bp_set_ipv6opts(sd
);
1310 bp_bind_ipv6(sd
, BFD_DEFDESTPORT
);
1315 int bp_udp6_mhop(void)
1319 sd
= socket(AF_INET6
, SOCK_DGRAM
, PF_UNSPEC
);
1321 log_fatal("udp6-mhop: socket: %s", strerror(errno
));
1323 bp_set_ipv6opts(sd
);
1324 bp_bind_ipv6(sd
, BFD_DEF_MHOP_DEST_PORT
);