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"
44 static int ptm_bfd_process_echo_pkt(int s
);
45 int _ptm_bfd_send(struct bfd_session
*bs
, uint16_t *port
, const void *data
,
48 static void bfd_sd_reschedule(int sd
);
49 ssize_t
bfd_recv_ipv4(int sd
, uint8_t *msgbuf
, size_t msgbuflen
, uint8_t *ttl
,
50 char *port
, size_t portlen
, char *vrfname
,
51 size_t vrfnamelen
, 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 char *port
, size_t portlen
, char *vrfname
,
55 size_t vrfnamelen
, struct sockaddr_any
*local
,
56 struct sockaddr_any
*peer
);
57 int bp_udp_send(int sd
, uint8_t ttl
, uint8_t *data
, size_t datalen
,
58 struct sockaddr
*to
, socklen_t tolen
);
59 int bp_bfd_echo_in(int sd
, uint8_t *ttl
, uint32_t *my_discr
);
61 /* socket related prototypes */
62 static void bp_set_ipopts(int sd
);
63 static void bp_bind_ip(int sd
, uint16_t port
);
64 static void bp_set_ipv6opts(int sd
);
65 static void bp_bind_ipv6(int sd
, uint16_t port
);
71 int _ptm_bfd_send(struct bfd_session
*bs
, uint16_t *port
, const void *data
,
75 struct sockaddr_in sin
;
76 struct sockaddr_in6 sin6
;
81 if (BFD_CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_IPV6
)) {
82 memset(&sin6
, 0, sizeof(sin6
));
83 sin6
.sin6_family
= AF_INET6
;
84 sin6
.sin6_addr
= bs
->shop
.peer
.sa_sin6
.sin6_addr
;
87 : (BFD_CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_MH
))
88 ? htons(BFD_DEF_MHOP_DEST_PORT
)
89 : htons(BFD_DEFDESTPORT
);
92 sa
= (struct sockaddr
*)&sin6
;
95 memset(&sin
, 0, sizeof(sin
));
96 sin
.sin_family
= AF_INET
;
97 sin
.sin_addr
= bs
->shop
.peer
.sa_sin
.sin_addr
;
100 : (BFD_CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_MH
))
101 ? htons(BFD_DEF_MHOP_DEST_PORT
)
102 : htons(BFD_DEFDESTPORT
);
105 sa
= (struct sockaddr
*)&sin
;
109 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
111 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
112 rv
= sendto(sd
, data
, datalen
, 0, sa
, slen
);
114 log_debug("packet-send: send failure: %s", strerror(errno
));
117 if (rv
< (ssize_t
)datalen
)
118 log_debug("packet-send: send partial", strerror(errno
));
123 void ptm_bfd_echo_snd(struct bfd_session
*bfd
)
125 struct sockaddr_any
*sa
;
128 struct bfd_echo_pkt bep
;
129 struct sockaddr_in sin
;
130 struct sockaddr_in6 sin6
;
132 if (!BFD_CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_ECHO_ACTIVE
))
133 BFD_SET_FLAG(bfd
->flags
, BFD_SESS_FLAG_ECHO_ACTIVE
);
135 memset(&bep
, 0, sizeof(bep
));
136 bep
.ver
= BFD_ECHO_VERSION
;
137 bep
.len
= BFD_ECHO_PKT_LEN
;
138 bep
.my_discr
= htonl(bfd
->discrs
.my_discr
);
140 sa
= BFD_CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_MH
) ? &bfd
->mhop
.peer
142 if (BFD_CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_IPV6
)) {
143 sd
= bglobal
.bg_echov6
;
145 sin6
.sin6_port
= htons(BFD_DEF_ECHO_PORT
);
146 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
147 sin6
.sin6_len
= sizeof(sin6
);
148 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
150 sa
= (struct sockaddr_any
*)&sin6
;
151 salen
= sizeof(sin6
);
153 sd
= bglobal
.bg_echo
;
155 sin
.sin_port
= htons(BFD_DEF_ECHO_PORT
);
156 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
157 sin
.sin_len
= sizeof(sin
);
158 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
160 sa
= (struct sockaddr_any
*)&sin
;
163 if (bp_udp_send(sd
, BFD_TTL_VAL
, (uint8_t *)&bep
, sizeof(bep
),
164 (struct sockaddr
*)sa
, salen
)
168 bfd
->stats
.tx_echo_pkt
++;
171 static int ptm_bfd_process_echo_pkt(int s
)
173 struct bfd_session
*bfd
;
174 uint32_t my_discr
= 0;
177 /* Receive and parse echo packet. */
178 if (bp_bfd_echo_in(s
, &ttl
, &my_discr
) == -1)
181 /* Your discriminator not zero - use it to find session */
182 bfd
= bfd_id_lookup(my_discr
);
184 log_debug("echo-packet: no matching session (id:%u)", my_discr
);
188 if (!BFD_CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_ECHO_ACTIVE
)) {
189 log_debug("echo-packet: echo disabled [%s] (id:%u)",
190 bs_to_string(bfd
), my_discr
);
194 bfd
->stats
.rx_echo_pkt
++;
196 /* Compute detect time */
197 bfd
->echo_detect_TO
= bfd
->remote_detect_mult
* bfd
->echo_xmt_TO
;
199 /* Update echo receive timeout. */
200 if (bfd
->echo_detect_TO
> 0)
201 bfd_echo_recvtimer_update(bfd
);
206 void ptm_bfd_snd(struct bfd_session
*bfd
, int fbit
)
210 /* Set fields according to section 6.5.7 */
211 cp
.diag
= bfd
->local_diag
;
212 BFD_SETVER(cp
.diag
, BFD_VERSION
);
214 BFD_SETSTATE(cp
.flags
, bfd
->ses_state
);
215 BFD_SETDEMANDBIT(cp
.flags
, BFD_DEF_DEMAND
);
216 BFD_SETPBIT(cp
.flags
, bfd
->polling
);
217 BFD_SETFBIT(cp
.flags
, fbit
);
218 cp
.detect_mult
= bfd
->detect_mult
;
219 cp
.len
= BFD_PKT_LEN
;
220 cp
.discrs
.my_discr
= htonl(bfd
->discrs
.my_discr
);
221 cp
.discrs
.remote_discr
= htonl(bfd
->discrs
.remote_discr
);
223 cp
.timers
.desired_min_tx
=
224 htonl(bfd
->new_timers
.desired_min_tx
);
225 cp
.timers
.required_min_rx
=
226 htonl(bfd
->new_timers
.required_min_rx
);
228 cp
.timers
.desired_min_tx
= htonl(bfd
->timers
.desired_min_tx
);
229 cp
.timers
.required_min_rx
= htonl(bfd
->timers
.required_min_rx
);
231 cp
.timers
.required_min_echo
= htonl(bfd
->timers
.required_min_echo
);
233 if (_ptm_bfd_send(bfd
, NULL
, &cp
, BFD_PKT_LEN
) != 0)
236 bfd
->stats
.tx_ctrl_pkt
++;
239 ssize_t
bfd_recv_ipv4(int sd
, uint8_t *msgbuf
, size_t msgbuflen
, uint8_t *ttl
,
240 char *port
, size_t portlen
, char *vrfname
,
241 size_t vrfnamelen
, struct sockaddr_any
*local
,
242 struct sockaddr_any
*peer
)
244 struct interface
*ifp
;
248 struct sockaddr_in msgaddr
;
249 struct msghdr msghdr
;
251 uint8_t cmsgbuf
[255];
255 /* Prepare the recvmsg params. */
256 iov
[0].iov_base
= msgbuf
;
257 iov
[0].iov_len
= msgbuflen
;
259 memset(&msghdr
, 0, sizeof(msghdr
));
260 msghdr
.msg_name
= &msgaddr
;
261 msghdr
.msg_namelen
= sizeof(msgaddr
);
262 msghdr
.msg_iov
= iov
;
263 msghdr
.msg_iovlen
= 1;
264 msghdr
.msg_control
= cmsgbuf
;
265 msghdr
.msg_controllen
= sizeof(cmsgbuf
);
267 mlen
= recvmsg(sd
, &msghdr
, MSG_DONTWAIT
);
270 log_error("ipv4-recv: recv failed: %s",
276 /* Get source address */
277 peer
->sa_sin
= *((struct sockaddr_in
*)(msghdr
.msg_name
));
279 /* Get and check TTL */
280 for (cm
= CMSG_FIRSTHDR(&msghdr
); cm
!= NULL
;
281 cm
= CMSG_NXTHDR(&msghdr
, cm
)) {
282 if (cm
->cmsg_level
!= IPPROTO_IP
)
285 switch (cm
->cmsg_type
) {
290 memcpy(&ttlval
, CMSG_DATA(cm
), sizeof(ttlval
));
292 log_debug("ipv4-recv: invalid TTL: %u", ttlval
);
300 struct in_pktinfo
*pi
=
301 (struct in_pktinfo
*)CMSG_DATA(cm
);
306 local
->sa_sin
.sin_family
= AF_INET
;
307 local
->sa_sin
.sin_addr
= pi
->ipi_addr
;
308 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
309 local
->sa_sin
.sin_len
= sizeof(local
->sa_sin
);
310 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
312 ifp
= if_lookup_by_index(pi
->ipi_ifindex
, VRF_DEFAULT
);
316 if (strlcpy(port
, ifp
->name
, portlen
) >= portlen
)
318 "ipv4-recv: interface name truncated");
321 #endif /* BFD_LINUX */
324 memcpy(ttl
, CMSG_DATA(cm
), sizeof(*ttl
));
328 case IP_RECVDSTADDR
: {
331 memcpy(&ia
, CMSG_DATA(cm
), sizeof(ia
));
332 local
->sa_sin
.sin_family
= AF_INET
;
333 local
->sa_sin
.sin_addr
= ia
;
334 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
335 local
->sa_sin
.sin_len
= sizeof(local
->sa_sin
);
336 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
343 * On *BSDs we expect to land here when skipping
344 * the IP_RECVIF header. It will be handled by
345 * getsockopt_ifindex() below.
352 /* OS agnostic way of getting interface name. */
354 ifindex
= getsockopt_ifindex(AF_INET
, &msghdr
);
358 ifp
= if_lookup_by_index(ifindex
, VRF_DEFAULT
);
362 if (strlcpy(port
, ifp
->name
, portlen
) >= portlen
)
363 log_debug("ipv4-recv: interface name truncated");
369 ssize_t
bfd_recv_ipv6(int sd
, uint8_t *msgbuf
, size_t msgbuflen
, uint8_t *ttl
,
370 char *port
, size_t portlen
, char *vrfname
,
371 size_t vrfnamelen
, struct sockaddr_any
*local
,
372 struct sockaddr_any
*peer
)
374 struct interface
*ifp
;
376 struct in6_pktinfo
*pi6
= NULL
;
380 struct sockaddr_in6 msgaddr6
;
381 struct msghdr msghdr6
;
383 uint8_t cmsgbuf6
[255];
385 /* Prepare the recvmsg params. */
386 iov
[0].iov_base
= msgbuf
;
387 iov
[0].iov_len
= msgbuflen
;
389 memset(&msghdr6
, 0, sizeof(msghdr6
));
390 msghdr6
.msg_name
= &msgaddr6
;
391 msghdr6
.msg_namelen
= sizeof(msgaddr6
);
392 msghdr6
.msg_iov
= iov
;
393 msghdr6
.msg_iovlen
= 1;
394 msghdr6
.msg_control
= cmsgbuf6
;
395 msghdr6
.msg_controllen
= sizeof(cmsgbuf6
);
397 mlen
= recvmsg(sd
, &msghdr6
, MSG_DONTWAIT
);
400 log_error("ipv6-recv: recv failed: %s",
406 /* Get source address */
407 peer
->sa_sin6
= *((struct sockaddr_in6
*)(msghdr6
.msg_name
));
409 /* Get and check TTL */
410 for (cm
= CMSG_FIRSTHDR(&msghdr6
); cm
!= NULL
;
411 cm
= CMSG_NXTHDR(&msghdr6
, cm
)) {
412 if (cm
->cmsg_level
!= IPPROTO_IPV6
)
415 if (cm
->cmsg_type
== IPV6_HOPLIMIT
) {
416 memcpy(&ttlval
, CMSG_DATA(cm
), sizeof(ttlval
));
418 log_debug("ipv6-recv: invalid TTL: %u", ttlval
);
423 } else if (cm
->cmsg_type
== IPV6_PKTINFO
) {
424 pi6
= (struct in6_pktinfo
*)CMSG_DATA(cm
);
426 local
->sa_sin6
.sin6_family
= AF_INET6
;
427 local
->sa_sin6
.sin6_addr
= pi6
->ipi6_addr
;
428 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
429 local
->sa_sin6
.sin6_len
= sizeof(local
->sa_sin6
);
430 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
432 ifindex
= pi6
->ipi6_ifindex
;
433 ifp
= if_lookup_by_index(ifindex
, VRF_DEFAULT
);
437 if (strlcpy(port
, ifp
->name
, portlen
)
440 "ipv6-recv: interface name truncated");
445 /* Set scope ID for link local addresses. */
446 if (IN6_IS_ADDR_LINKLOCAL(&peer
->sa_sin6
.sin6_addr
))
447 peer
->sa_sin6
.sin6_scope_id
= ifindex
;
448 if (IN6_IS_ADDR_LINKLOCAL(&local
->sa_sin6
.sin6_addr
))
449 local
->sa_sin6
.sin6_scope_id
= ifindex
;
454 static void bfd_sd_reschedule(int sd
)
456 if (sd
== bglobal
.bg_shop
) {
457 THREAD_OFF(bglobal
.bg_ev
[0]);
458 thread_add_read(master
, bfd_recv_cb
, NULL
, bglobal
.bg_shop
,
460 } else if (sd
== bglobal
.bg_mhop
) {
461 THREAD_OFF(bglobal
.bg_ev
[1]);
462 thread_add_read(master
, bfd_recv_cb
, NULL
, bglobal
.bg_mhop
,
464 } else if (sd
== bglobal
.bg_shop6
) {
465 THREAD_OFF(bglobal
.bg_ev
[2]);
466 thread_add_read(master
, bfd_recv_cb
, NULL
, bglobal
.bg_shop6
,
468 } else if (sd
== bglobal
.bg_mhop6
) {
469 THREAD_OFF(bglobal
.bg_ev
[3]);
470 thread_add_read(master
, bfd_recv_cb
, NULL
, bglobal
.bg_mhop6
,
472 } else if (sd
== bglobal
.bg_echo
) {
473 THREAD_OFF(bglobal
.bg_ev
[4]);
474 thread_add_read(master
, bfd_recv_cb
, NULL
, bglobal
.bg_echo
,
476 } else if (sd
== bglobal
.bg_echov6
) {
477 THREAD_OFF(bglobal
.bg_ev
[5]);
478 thread_add_read(master
, bfd_recv_cb
, NULL
, bglobal
.bg_echov6
,
483 static void cp_debug(bool mhop
, struct sockaddr_any
*peer
,
484 struct sockaddr_any
*local
, const char *port
,
485 const char *vrf
, const char *fmt
, ...)
487 char buf
[512], peerstr
[128], localstr
[128], portstr
[64], vrfstr
[64];
490 if (peer
->sa_sin
.sin_family
)
491 snprintf(peerstr
, sizeof(peerstr
), " peer:%s", satostr(peer
));
495 if (local
->sa_sin
.sin_family
)
496 snprintf(localstr
, sizeof(localstr
), " local:%s",
502 snprintf(portstr
, sizeof(portstr
), " port:%s", port
);
507 snprintf(vrfstr
, sizeof(vrfstr
), " vrf:%s", port
);
512 vsnprintf(buf
, sizeof(buf
), fmt
, vl
);
515 log_debug("control-packet: %s [mhop:%s%s%s%s%s]", buf
,
516 mhop
? "yes" : "no", peerstr
, localstr
, portstr
, vrfstr
);
519 int bfd_recv_cb(struct thread
*t
)
521 int sd
= THREAD_FD(t
);
522 struct bfd_session
*bfd
;
526 uint32_t oldEchoXmt_TO
, oldXmtTime
;
528 struct sockaddr_any local
, peer
;
529 char port
[MAXNAMELEN
+ 1], vrfname
[MAXNAMELEN
+ 1];
530 uint8_t msgbuf
[1516];
532 /* Schedule next read. */
533 bfd_sd_reschedule(sd
);
535 /* Handle echo packets. */
536 if (sd
== bglobal
.bg_echo
|| sd
== bglobal
.bg_echov6
) {
537 ptm_bfd_process_echo_pkt(sd
);
541 /* Sanitize input/output. */
542 memset(port
, 0, sizeof(port
));
543 memset(vrfname
, 0, sizeof(vrfname
));
544 memset(&local
, 0, sizeof(local
));
545 memset(&peer
, 0, sizeof(peer
));
547 /* Handle control packets. */
549 if (sd
== bglobal
.bg_shop
|| sd
== bglobal
.bg_mhop
) {
550 is_mhop
= sd
== bglobal
.bg_mhop
;
551 mlen
= bfd_recv_ipv4(sd
, msgbuf
, sizeof(msgbuf
), &ttl
, port
,
552 sizeof(port
), vrfname
, sizeof(vrfname
),
554 } else if (sd
== bglobal
.bg_shop6
|| sd
== bglobal
.bg_mhop6
) {
555 is_mhop
= sd
== bglobal
.bg_mhop6
;
556 mlen
= bfd_recv_ipv6(sd
, msgbuf
, sizeof(msgbuf
), &ttl
, port
,
557 sizeof(port
), vrfname
, sizeof(vrfname
),
561 /* Implement RFC 5880 6.8.6 */
562 if (mlen
< BFD_PKT_LEN
) {
563 cp_debug(is_mhop
, &peer
, &local
, port
, vrfname
,
564 "too small (%ld bytes)", mlen
);
568 /* Validate packet TTL. */
569 if ((is_mhop
== false) && (ttl
!= BFD_TTL_VAL
)) {
570 cp_debug(is_mhop
, &peer
, &local
, port
, vrfname
,
571 "invalid TTL: %d expected %d", ttl
, BFD_TTL_VAL
);
576 * Parse the control header for inconsistencies:
578 * - Bad multiplier configuration;
580 * - Invalid discriminator;
582 cp
= (struct bfd_pkt
*)(msgbuf
);
583 if (BFD_GETVER(cp
->diag
) != BFD_VERSION
) {
584 cp_debug(is_mhop
, &peer
, &local
, port
, vrfname
,
585 "bad version %d", BFD_GETVER(cp
->diag
));
589 if (cp
->detect_mult
== 0) {
590 cp_debug(is_mhop
, &peer
, &local
, port
, vrfname
,
591 "detect multiplier set to zero");
595 if ((cp
->len
< BFD_PKT_LEN
) || (cp
->len
> mlen
)) {
596 cp_debug(is_mhop
, &peer
, &local
, port
, vrfname
, "too small");
600 if (cp
->discrs
.my_discr
== 0) {
601 cp_debug(is_mhop
, &peer
, &local
, port
, vrfname
,
602 "'my discriminator' is zero");
606 /* Find the session that this packet belongs. */
607 bfd
= ptm_bfd_sess_find(cp
, port
, &peer
, &local
, vrfname
, is_mhop
);
609 cp_debug(is_mhop
, &peer
, &local
, port
, vrfname
,
614 bfd
->stats
.rx_ctrl_pkt
++;
617 * Multi hop: validate packet TTL.
618 * Single hop: set local address that received the packet.
621 if ((BFD_TTL_VAL
- bfd
->mh_ttl
) > BFD_TTL_VAL
) {
622 cp_debug(is_mhop
, &peer
, &local
, port
, vrfname
,
623 "exceeded max hop count (expected %d, got %d)",
624 bfd
->mh_ttl
, BFD_TTL_VAL
);
627 } else if (bfd
->local_ip
.sa_sin
.sin_family
== AF_UNSPEC
) {
628 bfd
->local_ip
= local
;
632 * If no interface was detected, save the interface where the
635 if (bfd
->ifp
== NULL
)
636 bfd
->ifp
= if_lookup_by_name(port
, VRF_DEFAULT
);
638 /* Log remote discriminator changes. */
639 if ((bfd
->discrs
.remote_discr
!= 0)
640 && (bfd
->discrs
.remote_discr
!= ntohl(cp
->discrs
.my_discr
)))
641 cp_debug(is_mhop
, &peer
, &local
, port
, vrfname
,
642 "remote discriminator mismatch (expected %d, got %d)",
643 bfd
->discrs
.remote_discr
, ntohl(cp
->discrs
.my_discr
));
645 bfd
->discrs
.remote_discr
= ntohl(cp
->discrs
.my_discr
);
647 /* If received the Final bit, the new values should take effect */
648 if (bfd
->polling
&& BFD_GETFBIT(cp
->flags
)) {
649 bfd
->timers
.desired_min_tx
= bfd
->new_timers
.desired_min_tx
;
650 bfd
->timers
.required_min_rx
= bfd
->new_timers
.required_min_rx
;
651 bfd
->new_timers
.desired_min_tx
= 0;
652 bfd
->new_timers
.required_min_rx
= 0;
656 if (!bfd
->demand_mode
) {
657 /* Compute detect time */
658 bfd
->detect_TO
= cp
->detect_mult
659 * ((bfd
->timers
.required_min_rx
660 > ntohl(cp
->timers
.desired_min_tx
))
661 ? bfd
->timers
.required_min_rx
662 : ntohl(cp
->timers
.desired_min_tx
));
663 bfd
->remote_detect_mult
= cp
->detect_mult
;
665 cp_debug(is_mhop
, &peer
, &local
, port
, vrfname
,
666 "unsupported demand mode");
668 /* Save remote diagnostics before state switch. */
669 bfd
->remote_diag
= cp
->diag
& BFD_DIAGMASK
;
671 /* State switch from section 6.8.6 */
672 if (BFD_GETSTATE(cp
->flags
) == PTM_BFD_ADM_DOWN
) {
673 if (bfd
->ses_state
!= PTM_BFD_DOWN
)
674 ptm_bfd_ses_dn(bfd
, BD_NEIGHBOR_DOWN
);
676 switch (bfd
->ses_state
) {
678 if (BFD_GETSTATE(cp
->flags
) == PTM_BFD_INIT
)
680 else if (BFD_GETSTATE(cp
->flags
) == PTM_BFD_DOWN
)
681 bfd
->ses_state
= PTM_BFD_INIT
;
684 if (BFD_GETSTATE(cp
->flags
) == PTM_BFD_INIT
685 || BFD_GETSTATE(cp
->flags
) == PTM_BFD_UP
)
689 if (BFD_GETSTATE(cp
->flags
) == PTM_BFD_DOWN
)
690 ptm_bfd_ses_dn(bfd
, BD_NEIGHBOR_DOWN
);
696 * Handle echo packet status:
697 * - Start echo packets if configured and permitted
698 * (required_min_echo > 0);
699 * - Stop echo packets if not allowed (required_min_echo == 0);
700 * - Recalculate echo packet interval;
702 if (BFD_CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_ECHO
)) {
703 if (BFD_CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_ECHO_ACTIVE
)) {
704 if (!ntohl(cp
->timers
.required_min_echo
)) {
705 ptm_bfd_echo_stop(bfd
, 1);
707 oldEchoXmt_TO
= bfd
->echo_xmt_TO
;
709 bfd
->timers
.required_min_echo
;
710 if (ntohl(cp
->timers
.required_min_echo
)
712 bfd
->echo_xmt_TO
= ntohl(
713 cp
->timers
.required_min_echo
);
714 if (oldEchoXmt_TO
!= bfd
->echo_xmt_TO
)
715 ptm_bfd_echo_start(bfd
);
717 } else if (ntohl(cp
->timers
.required_min_echo
)) {
718 bfd
->echo_xmt_TO
= bfd
->timers
.required_min_echo
;
719 if (ntohl(cp
->timers
.required_min_echo
)
722 ntohl(cp
->timers
.required_min_echo
);
723 ptm_bfd_echo_start(bfd
);
727 if (BFD_CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_ECHO_ACTIVE
)) {
728 bfd
->echo_xmt_TO
= bfd
->timers
.required_min_echo
;
729 if (ntohl(cp
->timers
.required_min_echo
) > bfd
->echo_xmt_TO
)
730 bfd
->echo_xmt_TO
= ntohl(cp
->timers
.required_min_echo
);
733 /* Calculate new transmit time */
734 oldXmtTime
= bfd
->xmt_TO
;
736 (bfd
->timers
.desired_min_tx
> ntohl(cp
->timers
.required_min_rx
))
737 ? bfd
->timers
.desired_min_tx
738 : ntohl(cp
->timers
.required_min_rx
);
740 /* If transmit time has changed, and too much time until next xmt,
743 if (BFD_GETPBIT(cp
->flags
)) {
744 ptm_bfd_xmt_TO(bfd
, 1);
745 } else if (oldXmtTime
!= bfd
->xmt_TO
) {
746 /* XXX add some skid to this as well */
747 ptm_bfd_start_xmt_timer(bfd
, false);
750 /* Restart detection timer (packet received) */
751 if (!bfd
->demand_mode
)
752 bfd_recvtimer_update(bfd
);
755 * Save the timers and state sent by the remote end
756 * for debugging and statistics.
758 if (BFD_GETFBIT(cp
->flags
)) {
759 bfd
->remote_timers
.desired_min_tx
=
760 ntohl(cp
->timers
.desired_min_tx
);
761 bfd
->remote_timers
.required_min_rx
=
762 ntohl(cp
->timers
.required_min_rx
);
763 bfd
->remote_timers
.required_min_echo
=
764 ntohl(cp
->timers
.required_min_echo
);
766 control_notify_config(BCM_NOTIFY_CONFIG_UPDATE
, bfd
);
773 * bp_bfd_echo_in: proccesses an BFD echo packet. On TTL == BFD_TTL_VAL
774 * the packet is looped back or returns the my discriminator ID along
777 * Returns -1 on error or loopback or 0 on success.
779 int bp_bfd_echo_in(int sd
, uint8_t *ttl
, uint32_t *my_discr
)
781 struct bfd_echo_pkt
*bep
;
783 struct sockaddr_any local
, peer
;
784 char port
[MAXNAMELEN
+ 1], vrfname
[MAXNAMELEN
+ 1];
785 uint8_t msgbuf
[1516];
787 if (sd
== bglobal
.bg_echo
)
788 rlen
= bfd_recv_ipv4(sd
, msgbuf
, sizeof(msgbuf
), ttl
, port
,
789 sizeof(port
), vrfname
, sizeof(vrfname
),
792 rlen
= bfd_recv_ipv6(sd
, msgbuf
, sizeof(msgbuf
), ttl
, port
,
793 sizeof(port
), vrfname
, sizeof(vrfname
),
796 /* Short packet, better not risk reading it. */
797 if (rlen
< (ssize_t
)sizeof(*bep
)) {
798 cp_debug(false, &peer
, &local
, port
, vrfname
,
799 "small echo packet");
803 /* Test for loopback. */
804 if (*ttl
== BFD_TTL_VAL
) {
805 bp_udp_send(sd
, *ttl
- 1, msgbuf
, rlen
,
806 (struct sockaddr
*)&peer
,
807 (sd
== bglobal
.bg_echo
) ? sizeof(peer
.sa_sin
)
808 : sizeof(peer
.sa_sin6
));
812 /* Read my discriminator from BFD Echo packet. */
813 bep
= (struct bfd_echo_pkt
*)msgbuf
;
814 *my_discr
= ntohl(bep
->my_discr
);
815 if (*my_discr
== 0) {
816 cp_debug(false, &peer
, &local
, port
, vrfname
,
817 "invalid echo packet discriminator (zero)");
824 int bp_udp_send(int sd
, uint8_t ttl
, uint8_t *data
, size_t datalen
,
825 struct sockaddr
*to
, socklen_t tolen
)
827 struct cmsghdr
*cmsg
;
830 bool is_ipv6
= to
->sa_family
== AF_INET6
;
835 /* Prepare message data. */
836 iov
[0].iov_base
= data
;
837 iov
[0].iov_len
= datalen
;
839 memset(&msg
, 0, sizeof(msg
));
840 memset(msgctl
, 0, sizeof(msgctl
));
842 msg
.msg_namelen
= tolen
;
846 /* Prepare the packet TTL information. */
848 /* Use ancillary data. */
849 msg
.msg_control
= msgctl
;
850 msg
.msg_controllen
= CMSG_LEN(sizeof(ttlval
));
852 /* Configure the ancillary data. */
853 cmsg
= CMSG_FIRSTHDR(&msg
);
854 cmsg
->cmsg_len
= CMSG_LEN(sizeof(ttlval
));
856 cmsg
->cmsg_level
= IPPROTO_IPV6
;
857 cmsg
->cmsg_type
= IPV6_HOPLIMIT
;
860 cmsg
->cmsg_level
= IPPROTO_IP
;
861 cmsg
->cmsg_type
= IP_TTL
;
863 /* FreeBSD does not support TTL in ancillary data. */
864 msg
.msg_control
= NULL
;
865 msg
.msg_controllen
= 0;
870 memcpy(CMSG_DATA(cmsg
), &ttlval
, sizeof(ttlval
));
873 /* Send echo back. */
874 wlen
= sendmsg(sd
, &msg
, 0);
876 log_debug("udp-send: loopback failure: (%d) %s", errno
, strerror(errno
));
878 } else if (wlen
< (ssize_t
)datalen
) {
879 log_debug("udp-send: partial send: %ld expected %ld", wlen
,
896 int bp_set_ttl(int sd
, uint8_t value
)
900 if (setsockopt(sd
, IPPROTO_IP
, IP_TTL
, &ttl
, sizeof(ttl
)) == -1) {
901 log_warning("set-ttl: setsockopt(IP_TTL, %d): %s", value
,
909 int bp_set_tos(int sd
, uint8_t value
)
913 if (setsockopt(sd
, IPPROTO_IP
, IP_TOS
, &tos
, sizeof(tos
)) == -1) {
914 log_warning("set-tos: setsockopt(IP_TOS, %d): %s", value
,
922 static void bp_set_ipopts(int sd
)
924 int rcvttl
= BFD_RCV_TTL_VAL
;
926 if (bp_set_ttl(sd
, BFD_TTL_VAL
) != 0)
927 log_fatal("set-ipopts: TTL configuration failed");
929 if (setsockopt(sd
, IPPROTO_IP
, IP_RECVTTL
, &rcvttl
, sizeof(rcvttl
))
931 log_fatal("set-ipopts: setsockopt(IP_RECVTTL, %d): %s", rcvttl
,
935 int pktinfo
= BFD_PKT_INFO_VAL
;
937 /* Figure out address and interface to do the peer matching. */
938 if (setsockopt(sd
, IPPROTO_IP
, IP_PKTINFO
, &pktinfo
, sizeof(pktinfo
))
940 log_fatal("set-ipopts: setsockopt(IP_PKTINFO, %d): %s", pktinfo
,
942 #endif /* BFD_LINUX */
946 /* Find out our address for peer matching. */
947 if (setsockopt(sd
, IPPROTO_IP
, IP_RECVDSTADDR
, &yes
, sizeof(yes
)) == -1)
948 log_fatal("set-ipopts: setsockopt(IP_RECVDSTADDR, %d): %s", yes
,
951 /* Find out interface where the packet came in. */
952 if (setsockopt_ifindex(AF_INET
, sd
, yes
) == -1)
953 log_fatal("set-ipopts: setsockopt_ipv4_ifindex(%d): %s", yes
,
958 static void bp_bind_ip(int sd
, uint16_t port
)
960 struct sockaddr_in sin
;
962 memset(&sin
, 0, sizeof(sin
));
963 sin
.sin_family
= AF_INET
;
964 sin
.sin_addr
.s_addr
= htonl(INADDR_ANY
);
965 sin
.sin_port
= htons(port
);
966 if (bind(sd
, (struct sockaddr
*)&sin
, sizeof(sin
)) == -1)
967 log_fatal("bind-ip: bind: %s", strerror(errno
));
970 int bp_udp_shop(void)
974 sd
= socket(AF_INET
, SOCK_DGRAM
, PF_UNSPEC
);
976 log_fatal("udp-shop: socket: %s", strerror(errno
));
979 bp_bind_ip(sd
, BFD_DEFDESTPORT
);
984 int bp_udp_mhop(void)
988 sd
= socket(AF_INET
, SOCK_DGRAM
, PF_UNSPEC
);
990 log_fatal("udp-mhop: socket: %s", strerror(errno
));
993 bp_bind_ip(sd
, BFD_DEF_MHOP_DEST_PORT
);
998 int bp_peer_socket(struct bfd_peer_cfg
*bpc
)
1001 struct sockaddr_in sin
;
1002 static int srcPort
= BFD_SRCPORTINIT
;
1004 sd
= socket(AF_INET
, SOCK_DGRAM
, PF_UNSPEC
);
1006 log_error("ipv4-new: failed to create socket: %s",
1011 /* Set TTL to 255 for all transmitted packets */
1012 if (bp_set_ttl(sd
, BFD_TTL_VAL
) != 0) {
1017 /* Set TOS to CS6 for all transmitted packets */
1018 if (bp_set_tos(sd
, BFD_TOS_VAL
) != 0) {
1023 if (bpc
->bpc_has_localif
) {
1024 if (bp_bind_dev(sd
, bpc
->bpc_localif
) != 0) {
1028 } else if (bpc
->bpc_mhop
&& bpc
->bpc_has_vrfname
) {
1029 if (bp_bind_dev(sd
, bpc
->bpc_vrfname
) != 0) {
1035 /* Find an available source port in the proper range */
1036 memset(&sin
, 0, sizeof(sin
));
1037 sin
= bpc
->bpc_local
.sa_sin
;
1038 sin
.sin_family
= AF_INET
;
1039 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1040 sin
.sin_len
= sizeof(sin
);
1041 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1043 sin
.sin_addr
= bpc
->bpc_local
.sa_sin
.sin_addr
;
1045 sin
.sin_addr
.s_addr
= INADDR_ANY
;
1049 if ((++pcount
) > (BFD_SRCPORTMAX
- BFD_SRCPORTINIT
)) {
1050 /* Searched all ports, none available */
1051 log_error("ipv4-new: failed to bind port: %s",
1056 if (srcPort
>= BFD_SRCPORTMAX
)
1057 srcPort
= BFD_SRCPORTINIT
;
1058 sin
.sin_port
= htons(srcPort
++);
1059 } while (bind(sd
, (struct sockaddr
*)&sin
, sizeof(sin
)) < 0);
1069 int bp_peer_socketv6(struct bfd_peer_cfg
*bpc
)
1071 struct interface
*ifp
;
1073 struct sockaddr_in6 sin6
;
1074 static int srcPort
= BFD_SRCPORTINIT
;
1076 sd
= socket(AF_INET6
, SOCK_DGRAM
, PF_UNSPEC
);
1078 log_error("ipv6-new: failed to create socket: %s",
1083 /* Set TTL to 255 for all transmitted packets */
1084 if (bp_set_ttlv6(sd
, BFD_TTL_VAL
) != 0) {
1089 /* Set TOS to CS6 for all transmitted packets */
1090 if (bp_set_tosv6(sd
, BFD_TOS_VAL
) != 0) {
1095 /* Find an available source port in the proper range */
1096 memset(&sin6
, 0, sizeof(sin6
));
1097 sin6
.sin6_family
= AF_INET6
;
1098 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1099 sin6
.sin6_len
= sizeof(sin6
);
1100 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1101 sin6
= bpc
->bpc_local
.sa_sin6
;
1102 if (IN6_IS_ADDR_LINKLOCAL(&sin6
.sin6_addr
)) {
1103 ifp
= if_lookup_by_name(bpc
->bpc_localif
, VRF_DEFAULT
);
1104 sin6
.sin6_scope_id
=
1105 (ifp
!= NULL
) ? ifp
->ifindex
: IFINDEX_INTERNAL
;
1108 if (bpc
->bpc_has_localif
) {
1109 if (bp_bind_dev(sd
, bpc
->bpc_localif
) != 0) {
1113 } else if (bpc
->bpc_mhop
&& bpc
->bpc_has_vrfname
) {
1114 if (bp_bind_dev(sd
, bpc
->bpc_vrfname
) != 0) {
1122 if ((++pcount
) > (BFD_SRCPORTMAX
- BFD_SRCPORTINIT
)) {
1123 /* Searched all ports, none available */
1124 log_error("ipv6-new: failed to bind port: %s",
1129 if (srcPort
>= BFD_SRCPORTMAX
)
1130 srcPort
= BFD_SRCPORTINIT
;
1131 sin6
.sin6_port
= htons(srcPort
++);
1132 } while (bind(sd
, (struct sockaddr
*)&sin6
, sizeof(sin6
)) < 0);
1137 int bp_set_ttlv6(int sd
, uint8_t value
)
1141 if (setsockopt(sd
, IPPROTO_IPV6
, IPV6_UNICAST_HOPS
, &ttl
, sizeof(ttl
))
1143 log_warning("set-ttlv6: setsockopt(IPV6_UNICAST_HOPS, %d): %s",
1144 value
, strerror(errno
));
1151 int bp_set_tosv6(int sd
, uint8_t value
)
1155 if (setsockopt(sd
, IPPROTO_IPV6
, IPV6_TCLASS
, &tos
, sizeof(tos
))
1157 log_warning("set-tosv6: setsockopt(IPV6_TCLASS, %d): %s", value
,
1165 static void bp_set_ipv6opts(int sd
)
1167 int ipv6_pktinfo
= BFD_IPV6_PKT_INFO_VAL
;
1168 int ipv6_only
= BFD_IPV6_ONLY_VAL
;
1170 if (bp_set_ttlv6(sd
, BFD_TTL_VAL
) == -1)
1171 log_fatal("set-ipv6opts: setsockopt(IPV6_UNICAST_HOPS, %d): %s",
1172 BFD_TTL_VAL
, strerror(errno
));
1174 if (setsockopt_ipv6_hoplimit(sd
, BFD_RCV_TTL_VAL
) == -1)
1175 log_fatal("set-ipv6opts: setsockopt(IPV6_HOPLIMIT, %d): %s",
1176 BFD_RCV_TTL_VAL
, strerror(errno
));
1178 if (setsockopt_ipv6_pktinfo(sd
, ipv6_pktinfo
) == -1)
1179 log_fatal("set-ipv6opts: setsockopt(IPV6_PKTINFO, %d): %s",
1180 ipv6_pktinfo
, strerror(errno
));
1182 if (setsockopt(sd
, IPPROTO_IPV6
, IPV6_V6ONLY
, &ipv6_only
,
1185 log_fatal("set-ipv6opts: setsockopt(IPV6_V6ONLY, %d): %s",
1186 ipv6_only
, strerror(errno
));
1189 static void bp_bind_ipv6(int sd
, uint16_t port
)
1191 struct sockaddr_in6 sin6
;
1193 memset(&sin6
, 0, sizeof(sin6
));
1194 sin6
.sin6_family
= AF_INET6
;
1195 sin6
.sin6_addr
= in6addr_any
;
1196 sin6
.sin6_port
= htons(port
);
1197 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1198 sin6
.sin6_len
= sizeof(sin6
);
1199 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1200 if (bind(sd
, (struct sockaddr
*)&sin6
, sizeof(sin6
)) == -1)
1201 log_fatal("bind-ipv6: bind: %s", strerror(errno
));
1204 int bp_udp6_shop(void)
1208 sd
= socket(AF_INET6
, SOCK_DGRAM
, PF_UNSPEC
);
1210 log_fatal("udp6-shop: socket: %s", strerror(errno
));
1212 bp_set_ipv6opts(sd
);
1213 bp_bind_ipv6(sd
, BFD_DEFDESTPORT
);
1218 int bp_udp6_mhop(void)
1222 sd
= socket(AF_INET6
, SOCK_DGRAM
, PF_UNSPEC
);
1224 log_fatal("udp6-mhop: socket: %s", strerror(errno
));
1226 bp_set_ipv6opts(sd
);
1227 bp_bind_ipv6(sd
, BFD_DEF_MHOP_DEST_PORT
);
1232 int bp_echo_socket(void)
1236 s
= socket(AF_INET
, SOCK_DGRAM
, 0);
1238 log_fatal("echo-socket: socket: %s", strerror(errno
));
1241 bp_bind_ip(s
, BFD_DEF_ECHO_PORT
);
1246 int bp_echov6_socket(void)
1250 s
= socket(AF_INET6
, SOCK_DGRAM
, 0);
1252 log_fatal("echov6-socket: socket: %s", strerror(errno
));
1255 bp_bind_ipv6(s
, BFD_DEF_ECHO_PORT
);