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 static int ptm_bfd_process_echo_pkt(struct bfd_vrf_global
*bvrf
, int s
);
44 int _ptm_bfd_send(struct bfd_session
*bs
, uint16_t *port
, const void *data
,
47 static void bfd_sd_reschedule(struct bfd_vrf_global
*bvrf
, int sd
);
48 ssize_t
bfd_recv_ipv4(int sd
, uint8_t *msgbuf
, size_t msgbuflen
, uint8_t *ttl
,
49 ifindex_t
*ifindex
, struct sockaddr_any
*local
,
50 struct sockaddr_any
*peer
);
51 ssize_t
bfd_recv_ipv6(int sd
, uint8_t *msgbuf
, size_t msgbuflen
, uint8_t *ttl
,
52 ifindex_t
*ifindex
, struct sockaddr_any
*local
,
53 struct sockaddr_any
*peer
);
54 int bp_udp_send(int sd
, uint8_t ttl
, uint8_t *data
, size_t datalen
,
55 struct sockaddr
*to
, socklen_t tolen
);
56 int bp_bfd_echo_in(struct bfd_vrf_global
*bvrf
, int sd
,
57 uint8_t *ttl
, uint32_t *my_discr
);
59 /* socket related prototypes */
60 static void bp_set_ipopts(int sd
);
61 static void bp_bind_ip(int sd
, uint16_t port
);
62 static void bp_set_ipv6opts(int sd
);
63 static void bp_bind_ipv6(int sd
, uint16_t port
);
69 int _ptm_bfd_send(struct bfd_session
*bs
, uint16_t *port
, const void *data
,
73 struct sockaddr_in sin
;
74 struct sockaddr_in6 sin6
;
79 if (CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_IPV6
)) {
80 memset(&sin6
, 0, sizeof(sin6
));
81 sin6
.sin6_family
= AF_INET6
;
82 memcpy(&sin6
.sin6_addr
, &bs
->key
.peer
, sizeof(sin6
.sin6_addr
));
83 if (bs
->ifp
&& IN6_IS_ADDR_LINKLOCAL(&sin6
.sin6_addr
))
84 sin6
.sin6_scope_id
= bs
->ifp
->ifindex
;
88 : (CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_MH
))
89 ? htons(BFD_DEF_MHOP_DEST_PORT
)
90 : htons(BFD_DEFDESTPORT
);
93 sa
= (struct sockaddr
*)&sin6
;
96 memset(&sin
, 0, sizeof(sin
));
97 sin
.sin_family
= AF_INET
;
98 memcpy(&sin
.sin_addr
, &bs
->key
.peer
, sizeof(sin
.sin_addr
));
101 : (CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_MH
))
102 ? htons(BFD_DEF_MHOP_DEST_PORT
)
103 : htons(BFD_DEFDESTPORT
);
106 sa
= (struct sockaddr
*)&sin
;
110 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
112 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
113 rv
= sendto(sd
, data
, datalen
, 0, sa
, slen
);
115 if (bglobal
.debug_network
)
116 zlog_debug("packet-send: send failure: %s",
120 if (rv
< (ssize_t
)datalen
) {
121 if (bglobal
.debug_network
)
122 zlog_debug("packet-send: send partial: %s",
129 void ptm_bfd_echo_snd(struct bfd_session
*bfd
)
134 struct bfd_echo_pkt bep
;
135 struct sockaddr_in sin
;
136 struct sockaddr_in6 sin6
;
137 struct bfd_vrf_global
*bvrf
= bfd_vrf_look_by_session(bfd
);
141 if (!CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_ECHO_ACTIVE
))
142 SET_FLAG(bfd
->flags
, BFD_SESS_FLAG_ECHO_ACTIVE
);
144 memset(&bep
, 0, sizeof(bep
));
145 bep
.ver
= BFD_ECHO_VERSION
;
146 bep
.len
= BFD_ECHO_PKT_LEN
;
147 bep
.my_discr
= htonl(bfd
->discrs
.my_discr
);
149 if (CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_IPV6
)) {
150 if (bvrf
->bg_echov6
== -1)
152 sd
= bvrf
->bg_echov6
;
153 memset(&sin6
, 0, sizeof(sin6
));
154 sin6
.sin6_family
= AF_INET6
;
155 memcpy(&sin6
.sin6_addr
, &bfd
->key
.peer
, sizeof(sin6
.sin6_addr
));
156 if (bfd
->ifp
&& IN6_IS_ADDR_LINKLOCAL(&sin6
.sin6_addr
))
157 sin6
.sin6_scope_id
= bfd
->ifp
->ifindex
;
159 sin6
.sin6_port
= htons(BFD_DEF_ECHO_PORT
);
160 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
161 sin6
.sin6_len
= sizeof(sin6
);
162 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
164 sa
= (struct sockaddr
*)&sin6
;
165 salen
= sizeof(sin6
);
168 memset(&sin6
, 0, sizeof(sin6
));
169 sin
.sin_family
= AF_INET
;
170 memcpy(&sin
.sin_addr
, &bfd
->key
.peer
, sizeof(sin
.sin_addr
));
171 sin
.sin_port
= htons(BFD_DEF_ECHO_PORT
);
172 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
173 sin
.sin_len
= sizeof(sin
);
174 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
176 sa
= (struct sockaddr
*)&sin
;
179 if (bp_udp_send(sd
, BFD_TTL_VAL
, (uint8_t *)&bep
, sizeof(bep
), sa
,
184 bfd
->stats
.tx_echo_pkt
++;
187 static int ptm_bfd_process_echo_pkt(struct bfd_vrf_global
*bvrf
, int s
)
189 struct bfd_session
*bfd
;
190 uint32_t my_discr
= 0;
193 /* Receive and parse echo packet. */
194 if (bp_bfd_echo_in(bvrf
, s
, &ttl
, &my_discr
) == -1)
197 /* Your discriminator not zero - use it to find session */
198 bfd
= bfd_id_lookup(my_discr
);
200 if (bglobal
.debug_network
)
201 zlog_debug("echo-packet: no matching session (id:%u)",
206 if (!CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_ECHO_ACTIVE
)) {
207 if (bglobal
.debug_network
)
208 zlog_debug("echo-packet: echo disabled [%s] (id:%u)",
209 bs_to_string(bfd
), my_discr
);
213 bfd
->stats
.rx_echo_pkt
++;
215 /* Compute detect time */
216 bfd
->echo_detect_TO
= bfd
->remote_detect_mult
* bfd
->echo_xmt_TO
;
218 /* Update echo receive timeout. */
219 if (bfd
->echo_detect_TO
> 0)
220 bfd_echo_recvtimer_update(bfd
);
225 void ptm_bfd_snd(struct bfd_session
*bfd
, int fbit
)
227 struct bfd_pkt cp
= {};
229 /* Set fields according to section 6.5.7 */
230 cp
.diag
= bfd
->local_diag
;
231 BFD_SETVER(cp
.diag
, BFD_VERSION
);
233 BFD_SETSTATE(cp
.flags
, bfd
->ses_state
);
235 if (CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_CBIT
))
236 BFD_SETCBIT(cp
.flags
, BFD_CBIT
);
238 BFD_SETDEMANDBIT(cp
.flags
, BFD_DEF_DEMAND
);
241 * Polling and Final can't be set at the same time.
243 * RFC 5880, Section 6.5.
245 BFD_SETFBIT(cp
.flags
, fbit
);
247 BFD_SETPBIT(cp
.flags
, bfd
->polling
);
249 cp
.detect_mult
= bfd
->detect_mult
;
250 cp
.len
= BFD_PKT_LEN
;
251 cp
.discrs
.my_discr
= htonl(bfd
->discrs
.my_discr
);
252 cp
.discrs
.remote_discr
= htonl(bfd
->discrs
.remote_discr
);
254 cp
.timers
.desired_min_tx
=
255 htonl(bfd
->timers
.desired_min_tx
);
256 cp
.timers
.required_min_rx
=
257 htonl(bfd
->timers
.required_min_rx
);
260 * We can only announce current setting on poll, this
261 * avoids timing mismatch with our peer and give it
262 * the oportunity to learn. See `bs_final_handler` for
265 cp
.timers
.desired_min_tx
=
266 htonl(bfd
->cur_timers
.desired_min_tx
);
267 cp
.timers
.required_min_rx
=
268 htonl(bfd
->cur_timers
.required_min_rx
);
270 cp
.timers
.required_min_echo
= htonl(bfd
->timers
.required_min_echo
);
272 if (_ptm_bfd_send(bfd
, NULL
, &cp
, BFD_PKT_LEN
) != 0)
275 bfd
->stats
.tx_ctrl_pkt
++;
278 ssize_t
bfd_recv_ipv4(int sd
, uint8_t *msgbuf
, size_t msgbuflen
, uint8_t *ttl
,
279 ifindex_t
*ifindex
, struct sockaddr_any
*local
,
280 struct sockaddr_any
*peer
)
284 struct sockaddr_in msgaddr
;
285 struct msghdr msghdr
;
287 uint8_t cmsgbuf
[255];
289 /* Prepare the recvmsg params. */
290 iov
[0].iov_base
= msgbuf
;
291 iov
[0].iov_len
= msgbuflen
;
293 memset(&msghdr
, 0, sizeof(msghdr
));
294 msghdr
.msg_name
= &msgaddr
;
295 msghdr
.msg_namelen
= sizeof(msgaddr
);
296 msghdr
.msg_iov
= iov
;
297 msghdr
.msg_iovlen
= 1;
298 msghdr
.msg_control
= cmsgbuf
;
299 msghdr
.msg_controllen
= sizeof(cmsgbuf
);
301 mlen
= recvmsg(sd
, &msghdr
, MSG_DONTWAIT
);
304 zlog_err("ipv4-recv: recv failed: %s", strerror(errno
));
309 /* Get source address */
310 peer
->sa_sin
= *((struct sockaddr_in
*)(msghdr
.msg_name
));
312 /* Get and check TTL */
313 for (cm
= CMSG_FIRSTHDR(&msghdr
); cm
!= NULL
;
314 cm
= CMSG_NXTHDR(&msghdr
, cm
)) {
315 if (cm
->cmsg_level
!= IPPROTO_IP
)
318 switch (cm
->cmsg_type
) {
323 memcpy(&ttlval
, CMSG_DATA(cm
), sizeof(ttlval
));
325 if (bglobal
.debug_network
)
326 zlog_debug("ipv4-recv: invalid TTL: %u",
335 struct in_pktinfo
*pi
=
336 (struct in_pktinfo
*)CMSG_DATA(cm
);
341 local
->sa_sin
.sin_family
= AF_INET
;
342 local
->sa_sin
.sin_addr
= pi
->ipi_addr
;
343 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
344 local
->sa_sin
.sin_len
= sizeof(local
->sa_sin
);
345 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
347 *ifindex
= pi
->ipi_ifindex
;
350 #endif /* BFD_LINUX */
353 memcpy(ttl
, CMSG_DATA(cm
), sizeof(*ttl
));
357 case IP_RECVDSTADDR
: {
360 memcpy(&ia
, CMSG_DATA(cm
), sizeof(ia
));
361 local
->sa_sin
.sin_family
= AF_INET
;
362 local
->sa_sin
.sin_addr
= ia
;
363 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
364 local
->sa_sin
.sin_len
= sizeof(local
->sa_sin
);
365 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
372 * On *BSDs we expect to land here when skipping
373 * the IP_RECVIF header. It will be handled by
374 * getsockopt_ifindex() below.
381 /* OS agnostic way of getting interface name. */
382 if (*ifindex
== IFINDEX_INTERNAL
)
383 *ifindex
= getsockopt_ifindex(AF_INET
, &msghdr
);
388 ssize_t
bfd_recv_ipv6(int sd
, uint8_t *msgbuf
, size_t msgbuflen
, uint8_t *ttl
,
389 ifindex_t
*ifindex
, struct sockaddr_any
*local
,
390 struct sockaddr_any
*peer
)
393 struct in6_pktinfo
*pi6
= NULL
;
396 struct sockaddr_in6 msgaddr6
;
397 struct msghdr msghdr6
;
399 uint8_t cmsgbuf6
[255];
401 /* Prepare the recvmsg params. */
402 iov
[0].iov_base
= msgbuf
;
403 iov
[0].iov_len
= msgbuflen
;
405 memset(&msghdr6
, 0, sizeof(msghdr6
));
406 msghdr6
.msg_name
= &msgaddr6
;
407 msghdr6
.msg_namelen
= sizeof(msgaddr6
);
408 msghdr6
.msg_iov
= iov
;
409 msghdr6
.msg_iovlen
= 1;
410 msghdr6
.msg_control
= cmsgbuf6
;
411 msghdr6
.msg_controllen
= sizeof(cmsgbuf6
);
413 mlen
= recvmsg(sd
, &msghdr6
, MSG_DONTWAIT
);
416 zlog_err("ipv6-recv: recv failed: %s", strerror(errno
));
421 /* Get source address */
422 peer
->sa_sin6
= *((struct sockaddr_in6
*)(msghdr6
.msg_name
));
424 /* Get and check TTL */
425 for (cm
= CMSG_FIRSTHDR(&msghdr6
); cm
!= NULL
;
426 cm
= CMSG_NXTHDR(&msghdr6
, cm
)) {
427 if (cm
->cmsg_level
!= IPPROTO_IPV6
)
430 if (cm
->cmsg_type
== IPV6_HOPLIMIT
) {
431 memcpy(&ttlval
, CMSG_DATA(cm
), sizeof(ttlval
));
433 if (bglobal
.debug_network
)
434 zlog_debug("ipv6-recv: invalid TTL: %u",
440 } else if (cm
->cmsg_type
== IPV6_PKTINFO
) {
441 pi6
= (struct in6_pktinfo
*)CMSG_DATA(cm
);
443 local
->sa_sin6
.sin6_family
= AF_INET6
;
444 local
->sa_sin6
.sin6_addr
= pi6
->ipi6_addr
;
445 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
446 local
->sa_sin6
.sin6_len
= sizeof(local
->sa_sin6
);
447 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
449 *ifindex
= pi6
->ipi6_ifindex
;
451 /* Set scope ID for link local addresses. */
452 if (IN6_IS_ADDR_LINKLOCAL(
453 &peer
->sa_sin6
.sin6_addr
))
454 peer
->sa_sin6
.sin6_scope_id
= *ifindex
;
455 if (IN6_IS_ADDR_LINKLOCAL(
456 &local
->sa_sin6
.sin6_addr
))
457 local
->sa_sin6
.sin6_scope_id
= *ifindex
;
465 static void bfd_sd_reschedule(struct bfd_vrf_global
*bvrf
, int sd
)
467 if (sd
== bvrf
->bg_shop
) {
468 THREAD_OFF(bvrf
->bg_ev
[0]);
469 thread_add_read(master
, bfd_recv_cb
, bvrf
, bvrf
->bg_shop
,
471 } else if (sd
== bvrf
->bg_mhop
) {
472 THREAD_OFF(bvrf
->bg_ev
[1]);
473 thread_add_read(master
, bfd_recv_cb
, bvrf
, bvrf
->bg_mhop
,
475 } else if (sd
== bvrf
->bg_shop6
) {
476 THREAD_OFF(bvrf
->bg_ev
[2]);
477 thread_add_read(master
, bfd_recv_cb
, bvrf
, bvrf
->bg_shop6
,
479 } else if (sd
== bvrf
->bg_mhop6
) {
480 THREAD_OFF(bvrf
->bg_ev
[3]);
481 thread_add_read(master
, bfd_recv_cb
, bvrf
, bvrf
->bg_mhop6
,
483 } else if (sd
== bvrf
->bg_echo
) {
484 THREAD_OFF(bvrf
->bg_ev
[4]);
485 thread_add_read(master
, bfd_recv_cb
, bvrf
, bvrf
->bg_echo
,
487 } else if (sd
== bvrf
->bg_echov6
) {
488 THREAD_OFF(bvrf
->bg_ev
[5]);
489 thread_add_read(master
, bfd_recv_cb
, bvrf
, bvrf
->bg_echov6
,
494 static void cp_debug(bool mhop
, struct sockaddr_any
*peer
,
495 struct sockaddr_any
*local
, ifindex_t ifindex
,
496 vrf_id_t vrfid
, const char *fmt
, ...)
498 char buf
[512], peerstr
[128], localstr
[128], portstr
[64], vrfstr
[64];
501 /* Don't to any processing if debug is disabled. */
502 if (bglobal
.debug_network
== false)
505 if (peer
->sa_sin
.sin_family
)
506 snprintf(peerstr
, sizeof(peerstr
), " peer:%s", satostr(peer
));
510 if (local
->sa_sin
.sin_family
)
511 snprintf(localstr
, sizeof(localstr
), " local:%s",
516 if (ifindex
!= IFINDEX_INTERNAL
)
517 snprintf(portstr
, sizeof(portstr
), " port:%u", ifindex
);
521 if (vrfid
!= VRF_DEFAULT
)
522 snprintf(vrfstr
, sizeof(vrfstr
), " vrf:%u", vrfid
);
527 vsnprintf(buf
, sizeof(buf
), fmt
, vl
);
530 zlog_debug("control-packet: %s [mhop:%s%s%s%s%s]", buf
,
531 mhop
? "yes" : "no", peerstr
, localstr
, portstr
, vrfstr
);
534 int bfd_recv_cb(struct thread
*t
)
536 int sd
= THREAD_FD(t
);
537 struct bfd_session
*bfd
;
543 ifindex_t ifindex
= IFINDEX_INTERNAL
;
544 struct sockaddr_any local
, peer
;
545 uint8_t msgbuf
[1516];
546 struct interface
*ifp
= NULL
;
547 struct bfd_vrf_global
*bvrf
= THREAD_ARG(t
);
549 vrfid
= bvrf
->vrf
->vrf_id
;
551 /* Schedule next read. */
552 bfd_sd_reschedule(bvrf
, sd
);
554 /* Handle echo packets. */
555 if (sd
== bvrf
->bg_echo
|| sd
== bvrf
->bg_echov6
) {
556 ptm_bfd_process_echo_pkt(bvrf
, sd
);
560 /* Sanitize input/output. */
561 memset(&local
, 0, sizeof(local
));
562 memset(&peer
, 0, sizeof(peer
));
564 /* Handle control packets. */
566 if (sd
== bvrf
->bg_shop
|| sd
== bvrf
->bg_mhop
) {
567 is_mhop
= sd
== bvrf
->bg_mhop
;
568 mlen
= bfd_recv_ipv4(sd
, msgbuf
, sizeof(msgbuf
), &ttl
, &ifindex
,
570 } else if (sd
== bvrf
->bg_shop6
|| sd
== bvrf
->bg_mhop6
) {
571 is_mhop
= sd
== bvrf
->bg_mhop6
;
572 mlen
= bfd_recv_ipv6(sd
, msgbuf
, sizeof(msgbuf
), &ttl
, &ifindex
,
576 /* update vrf-id because when in vrf-lite mode,
577 * the socket is on default namespace
580 ifp
= if_lookup_by_index(ifindex
, vrfid
);
585 /* Implement RFC 5880 6.8.6 */
586 if (mlen
< BFD_PKT_LEN
) {
587 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
,
588 "too small (%ld bytes)", mlen
);
592 /* Validate single hop packet TTL. */
593 if ((!is_mhop
) && (ttl
!= BFD_TTL_VAL
)) {
594 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
,
595 "invalid TTL: %d expected %d", ttl
, BFD_TTL_VAL
);
600 * Parse the control header for inconsistencies:
602 * - Bad multiplier configuration;
604 * - Invalid discriminator;
606 cp
= (struct bfd_pkt
*)(msgbuf
);
607 if (BFD_GETVER(cp
->diag
) != BFD_VERSION
) {
608 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
,
609 "bad version %d", BFD_GETVER(cp
->diag
));
613 if (cp
->detect_mult
== 0) {
614 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
,
615 "detect multiplier set to zero");
619 if ((cp
->len
< BFD_PKT_LEN
) || (cp
->len
> mlen
)) {
620 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
, "too small");
624 if (cp
->discrs
.my_discr
== 0) {
625 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
,
626 "'my discriminator' is zero");
630 /* Find the session that this packet belongs. */
631 bfd
= ptm_bfd_sess_find(cp
, &peer
, &local
, ifindex
, vrfid
, is_mhop
);
633 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
,
638 bfd
->stats
.rx_ctrl_pkt
++;
641 * Multi hop: validate packet TTL.
642 * Single hop: set local address that received the packet.
645 if (ttl
< bfd
->mh_ttl
) {
646 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
,
647 "exceeded max hop count (expected %d, got %d)",
651 } else if (bfd
->local_address
.sa_sin
.sin_family
== AF_UNSPEC
) {
652 bfd
->local_address
= local
;
656 * If no interface was detected, save the interface where the
659 if (bfd
->ifp
== NULL
)
660 bfd
->ifp
= if_lookup_by_index(ifindex
, vrfid
);
662 /* Log remote discriminator changes. */
663 if ((bfd
->discrs
.remote_discr
!= 0)
664 && (bfd
->discrs
.remote_discr
!= ntohl(cp
->discrs
.my_discr
)))
665 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
,
666 "remote discriminator mismatch (expected %u, got %u)",
667 bfd
->discrs
.remote_discr
, ntohl(cp
->discrs
.my_discr
));
669 bfd
->discrs
.remote_discr
= ntohl(cp
->discrs
.my_discr
);
671 /* Save remote diagnostics before state switch. */
672 bfd
->remote_diag
= cp
->diag
& BFD_DIAGMASK
;
674 /* Update remote timers settings. */
675 bfd
->remote_timers
.desired_min_tx
= ntohl(cp
->timers
.desired_min_tx
);
676 bfd
->remote_timers
.required_min_rx
= ntohl(cp
->timers
.required_min_rx
);
677 bfd
->remote_timers
.required_min_echo
=
678 ntohl(cp
->timers
.required_min_echo
);
679 bfd
->remote_detect_mult
= cp
->detect_mult
;
681 if (BFD_GETCBIT(cp
->flags
))
682 bfd
->remote_cbit
= 1;
684 bfd
->remote_cbit
= 0;
686 /* State switch from section 6.2. */
687 bs_state_handler(bfd
, BFD_GETSTATE(cp
->flags
));
689 /* RFC 5880, Section 6.5: handle POLL/FINAL negotiation sequence. */
690 if (bfd
->polling
&& BFD_GETFBIT(cp
->flags
)) {
691 /* Disable pooling. */
694 /* Handle poll finalization. */
695 bs_final_handler(bfd
);
697 /* Received a packet, lets update the receive timer. */
698 bfd_recvtimer_update(bfd
);
701 /* Handle echo timers changes. */
702 bs_echo_timer_handler(bfd
);
705 * We've received a packet with the POLL bit set, we must send
706 * a control packet back with the FINAL bit set.
708 * RFC 5880, Section 6.5.
710 if (BFD_GETPBIT(cp
->flags
)) {
711 /* We are finalizing a poll negotiation. */
712 bs_final_handler(bfd
);
714 /* Send the control packet with the final bit immediately. */
722 * bp_bfd_echo_in: proccesses an BFD echo packet. On TTL == BFD_TTL_VAL
723 * the packet is looped back or returns the my discriminator ID along
726 * Returns -1 on error or loopback or 0 on success.
728 int bp_bfd_echo_in(struct bfd_vrf_global
*bvrf
, int sd
,
729 uint8_t *ttl
, uint32_t *my_discr
)
731 struct bfd_echo_pkt
*bep
;
733 struct sockaddr_any local
, peer
;
734 ifindex_t ifindex
= IFINDEX_INTERNAL
;
735 vrf_id_t vrfid
= VRF_DEFAULT
;
736 uint8_t msgbuf
[1516];
738 if (sd
== bvrf
->bg_echo
)
739 rlen
= bfd_recv_ipv4(sd
, msgbuf
, sizeof(msgbuf
), ttl
, &ifindex
,
742 rlen
= bfd_recv_ipv6(sd
, msgbuf
, sizeof(msgbuf
), ttl
, &ifindex
,
745 /* Short packet, better not risk reading it. */
746 if (rlen
< (ssize_t
)sizeof(*bep
)) {
747 cp_debug(false, &peer
, &local
, ifindex
, vrfid
,
748 "small echo packet");
752 /* Test for loopback. */
753 if (*ttl
== BFD_TTL_VAL
) {
754 bp_udp_send(sd
, *ttl
- 1, msgbuf
, rlen
,
755 (struct sockaddr
*)&peer
,
756 (sd
== bvrf
->bg_echo
) ? sizeof(peer
.sa_sin
)
757 : sizeof(peer
.sa_sin6
));
761 /* Read my discriminator from BFD Echo packet. */
762 bep
= (struct bfd_echo_pkt
*)msgbuf
;
763 *my_discr
= ntohl(bep
->my_discr
);
764 if (*my_discr
== 0) {
765 cp_debug(false, &peer
, &local
, ifindex
, vrfid
,
766 "invalid echo packet discriminator (zero)");
773 int bp_udp_send(int sd
, uint8_t ttl
, uint8_t *data
, size_t datalen
,
774 struct sockaddr
*to
, socklen_t tolen
)
776 struct cmsghdr
*cmsg
;
779 bool is_ipv6
= to
->sa_family
== AF_INET6
;
784 /* Prepare message data. */
785 iov
[0].iov_base
= data
;
786 iov
[0].iov_len
= datalen
;
788 memset(&msg
, 0, sizeof(msg
));
789 memset(msgctl
, 0, sizeof(msgctl
));
791 msg
.msg_namelen
= tolen
;
795 /* Prepare the packet TTL information. */
797 /* Use ancillary data. */
798 msg
.msg_control
= msgctl
;
799 msg
.msg_controllen
= CMSG_LEN(sizeof(ttlval
));
801 /* Configure the ancillary data. */
802 cmsg
= CMSG_FIRSTHDR(&msg
);
803 cmsg
->cmsg_len
= CMSG_LEN(sizeof(ttlval
));
805 cmsg
->cmsg_level
= IPPROTO_IPV6
;
806 cmsg
->cmsg_type
= IPV6_HOPLIMIT
;
809 cmsg
->cmsg_level
= IPPROTO_IP
;
810 cmsg
->cmsg_type
= IP_TTL
;
812 /* FreeBSD does not support TTL in ancillary data. */
813 msg
.msg_control
= NULL
;
814 msg
.msg_controllen
= 0;
819 memcpy(CMSG_DATA(cmsg
), &ttlval
, sizeof(ttlval
));
822 /* Send echo back. */
823 wlen
= sendmsg(sd
, &msg
, 0);
825 if (bglobal
.debug_network
)
826 zlog_debug("udp-send: loopback failure: (%d) %s", errno
,
829 } else if (wlen
< (ssize_t
)datalen
) {
830 if (bglobal
.debug_network
)
831 zlog_debug("udp-send: partial send: %zd expected %zu",
848 int bp_set_ttl(int sd
, uint8_t value
)
852 if (setsockopt(sd
, IPPROTO_IP
, IP_TTL
, &ttl
, sizeof(ttl
)) == -1) {
853 zlog_warn("set-ttl: setsockopt(IP_TTL, %d): %s", value
,
861 int bp_set_tos(int sd
, uint8_t value
)
865 if (setsockopt(sd
, IPPROTO_IP
, IP_TOS
, &tos
, sizeof(tos
)) == -1) {
866 zlog_warn("set-tos: setsockopt(IP_TOS, %d): %s", value
,
874 static void bp_set_ipopts(int sd
)
876 int rcvttl
= BFD_RCV_TTL_VAL
;
878 if (bp_set_ttl(sd
, BFD_TTL_VAL
) != 0)
879 zlog_fatal("set-ipopts: TTL configuration failed");
881 if (setsockopt(sd
, IPPROTO_IP
, IP_RECVTTL
, &rcvttl
, sizeof(rcvttl
))
883 zlog_fatal("set-ipopts: setsockopt(IP_RECVTTL, %d): %s", rcvttl
,
887 int pktinfo
= BFD_PKT_INFO_VAL
;
889 /* Figure out address and interface to do the peer matching. */
890 if (setsockopt(sd
, IPPROTO_IP
, IP_PKTINFO
, &pktinfo
, sizeof(pktinfo
))
892 zlog_fatal("set-ipopts: setsockopt(IP_PKTINFO, %d): %s",
893 pktinfo
, strerror(errno
));
894 #endif /* BFD_LINUX */
898 /* Find out our address for peer matching. */
899 if (setsockopt(sd
, IPPROTO_IP
, IP_RECVDSTADDR
, &yes
, sizeof(yes
)) == -1)
900 zlog_fatal("set-ipopts: setsockopt(IP_RECVDSTADDR, %d): %s",
901 yes
, strerror(errno
));
903 /* Find out interface where the packet came in. */
904 if (setsockopt_ifindex(AF_INET
, sd
, yes
) == -1)
905 zlog_fatal("set-ipopts: setsockopt_ipv4_ifindex(%d): %s", yes
,
910 static void bp_bind_ip(int sd
, uint16_t port
)
912 struct sockaddr_in sin
;
914 memset(&sin
, 0, sizeof(sin
));
915 sin
.sin_family
= AF_INET
;
916 sin
.sin_addr
.s_addr
= htonl(INADDR_ANY
);
917 sin
.sin_port
= htons(port
);
918 if (bind(sd
, (struct sockaddr
*)&sin
, sizeof(sin
)) == -1)
919 zlog_fatal("bind-ip: bind: %s", strerror(errno
));
922 int bp_udp_shop(const struct vrf
*vrf
)
926 frr_with_privs(&bglobal
.bfdd_privs
) {
927 sd
= vrf_socket(AF_INET
, SOCK_DGRAM
, PF_UNSPEC
, vrf
->vrf_id
,
931 zlog_fatal("udp-shop: socket: %s", strerror(errno
));
934 bp_bind_ip(sd
, BFD_DEFDESTPORT
);
938 int bp_udp_mhop(const struct vrf
*vrf
)
942 frr_with_privs(&bglobal
.bfdd_privs
) {
943 sd
= vrf_socket(AF_INET
, SOCK_DGRAM
, PF_UNSPEC
, vrf
->vrf_id
,
947 zlog_fatal("udp-mhop: socket: %s", strerror(errno
));
950 bp_bind_ip(sd
, BFD_DEF_MHOP_DEST_PORT
);
955 int bp_peer_socket(const struct bfd_session
*bs
)
958 struct sockaddr_in sin
;
959 static int srcPort
= BFD_SRCPORTINIT
;
960 const char *device_to_bind
= NULL
;
962 if (bs
->key
.ifname
[0])
963 device_to_bind
= (const char *)bs
->key
.ifname
;
964 else if ((!vrf_is_backend_netns() && bs
->vrf
->vrf_id
!= VRF_DEFAULT
)
965 || ((CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_MH
)
966 && bs
->key
.vrfname
[0])))
967 device_to_bind
= (const char *)bs
->key
.vrfname
;
969 frr_with_privs(&bglobal
.bfdd_privs
) {
970 sd
= vrf_socket(AF_INET
, SOCK_DGRAM
, PF_UNSPEC
,
971 bs
->vrf
->vrf_id
, device_to_bind
);
974 zlog_err("ipv4-new: failed to create socket: %s",
979 /* Set TTL to 255 for all transmitted packets */
980 if (bp_set_ttl(sd
, BFD_TTL_VAL
) != 0) {
985 /* Set TOS to CS6 for all transmitted packets */
986 if (bp_set_tos(sd
, BFD_TOS_VAL
) != 0) {
991 /* Find an available source port in the proper range */
992 memset(&sin
, 0, sizeof(sin
));
993 sin
.sin_family
= AF_INET
;
994 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
995 sin
.sin_len
= sizeof(sin
);
996 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
997 memcpy(&sin
.sin_addr
, &bs
->key
.local
, sizeof(sin
.sin_addr
));
998 if (CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_MH
) == 0)
999 sin
.sin_addr
.s_addr
= INADDR_ANY
;
1003 if ((++pcount
) > (BFD_SRCPORTMAX
- BFD_SRCPORTINIT
)) {
1004 /* Searched all ports, none available */
1005 zlog_err("ipv4-new: failed to bind port: %s",
1010 if (srcPort
>= BFD_SRCPORTMAX
)
1011 srcPort
= BFD_SRCPORTINIT
;
1012 sin
.sin_port
= htons(srcPort
++);
1013 } while (bind(sd
, (struct sockaddr
*)&sin
, sizeof(sin
)) < 0);
1023 int bp_peer_socketv6(const struct bfd_session
*bs
)
1026 struct sockaddr_in6 sin6
;
1027 static int srcPort
= BFD_SRCPORTINIT
;
1028 const char *device_to_bind
= NULL
;
1030 if (bs
->key
.ifname
[0])
1031 device_to_bind
= (const char *)bs
->key
.ifname
;
1032 else if ((!vrf_is_backend_netns() && bs
->vrf
->vrf_id
!= VRF_DEFAULT
)
1033 || ((CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_MH
)
1034 && bs
->key
.vrfname
[0])))
1035 device_to_bind
= (const char *)bs
->key
.vrfname
;
1037 frr_with_privs(&bglobal
.bfdd_privs
) {
1038 sd
= vrf_socket(AF_INET6
, SOCK_DGRAM
, PF_UNSPEC
,
1039 bs
->vrf
->vrf_id
, device_to_bind
);
1042 zlog_err("ipv6-new: failed to create socket: %s",
1047 /* Set TTL to 255 for all transmitted packets */
1048 if (bp_set_ttlv6(sd
, BFD_TTL_VAL
) != 0) {
1053 /* Set TOS to CS6 for all transmitted packets */
1054 if (bp_set_tosv6(sd
, BFD_TOS_VAL
) != 0) {
1059 /* Find an available source port in the proper range */
1060 memset(&sin6
, 0, sizeof(sin6
));
1061 sin6
.sin6_family
= AF_INET6
;
1062 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1063 sin6
.sin6_len
= sizeof(sin6
);
1064 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1065 memcpy(&sin6
.sin6_addr
, &bs
->key
.local
, sizeof(sin6
.sin6_addr
));
1066 if (bs
->ifp
&& IN6_IS_ADDR_LINKLOCAL(&sin6
.sin6_addr
))
1067 sin6
.sin6_scope_id
= bs
->ifp
->ifindex
;
1071 if ((++pcount
) > (BFD_SRCPORTMAX
- BFD_SRCPORTINIT
)) {
1072 /* Searched all ports, none available */
1073 zlog_err("ipv6-new: failed to bind port: %s",
1078 if (srcPort
>= BFD_SRCPORTMAX
)
1079 srcPort
= BFD_SRCPORTINIT
;
1080 sin6
.sin6_port
= htons(srcPort
++);
1081 } while (bind(sd
, (struct sockaddr
*)&sin6
, sizeof(sin6
)) < 0);
1086 int bp_set_ttlv6(int sd
, uint8_t value
)
1090 if (setsockopt(sd
, IPPROTO_IPV6
, IPV6_UNICAST_HOPS
, &ttl
, sizeof(ttl
))
1092 zlog_warn("set-ttlv6: setsockopt(IPV6_UNICAST_HOPS, %d): %s",
1093 value
, strerror(errno
));
1100 int bp_set_tosv6(int sd
, uint8_t value
)
1104 if (setsockopt(sd
, IPPROTO_IPV6
, IPV6_TCLASS
, &tos
, sizeof(tos
))
1106 zlog_warn("set-tosv6: setsockopt(IPV6_TCLASS, %d): %s", value
,
1114 static void bp_set_ipv6opts(int sd
)
1116 int ipv6_pktinfo
= BFD_IPV6_PKT_INFO_VAL
;
1117 int ipv6_only
= BFD_IPV6_ONLY_VAL
;
1119 if (bp_set_ttlv6(sd
, BFD_TTL_VAL
) == -1)
1121 "set-ipv6opts: setsockopt(IPV6_UNICAST_HOPS, %d): %s",
1122 BFD_TTL_VAL
, strerror(errno
));
1124 if (setsockopt_ipv6_hoplimit(sd
, BFD_RCV_TTL_VAL
) == -1)
1125 zlog_fatal("set-ipv6opts: setsockopt(IPV6_HOPLIMIT, %d): %s",
1126 BFD_RCV_TTL_VAL
, strerror(errno
));
1128 if (setsockopt_ipv6_pktinfo(sd
, ipv6_pktinfo
) == -1)
1129 zlog_fatal("set-ipv6opts: setsockopt(IPV6_PKTINFO, %d): %s",
1130 ipv6_pktinfo
, strerror(errno
));
1132 if (setsockopt(sd
, IPPROTO_IPV6
, IPV6_V6ONLY
, &ipv6_only
,
1135 zlog_fatal("set-ipv6opts: setsockopt(IPV6_V6ONLY, %d): %s",
1136 ipv6_only
, strerror(errno
));
1139 static void bp_bind_ipv6(int sd
, uint16_t port
)
1141 struct sockaddr_in6 sin6
;
1143 memset(&sin6
, 0, sizeof(sin6
));
1144 sin6
.sin6_family
= AF_INET6
;
1145 sin6
.sin6_addr
= in6addr_any
;
1146 sin6
.sin6_port
= htons(port
);
1147 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1148 sin6
.sin6_len
= sizeof(sin6
);
1149 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1150 if (bind(sd
, (struct sockaddr
*)&sin6
, sizeof(sin6
)) == -1)
1151 zlog_fatal("bind-ipv6: bind: %s", strerror(errno
));
1154 int bp_udp6_shop(const struct vrf
*vrf
)
1158 frr_with_privs(&bglobal
.bfdd_privs
) {
1159 sd
= vrf_socket(AF_INET6
, SOCK_DGRAM
, PF_UNSPEC
, vrf
->vrf_id
,
1163 if (errno
!= EAFNOSUPPORT
)
1164 zlog_fatal("udp6-shop: socket: %s", strerror(errno
));
1166 zlog_warn("udp6-shop: V6 is not supported, continuing");
1171 bp_set_ipv6opts(sd
);
1172 bp_bind_ipv6(sd
, BFD_DEFDESTPORT
);
1177 int bp_udp6_mhop(const struct vrf
*vrf
)
1181 frr_with_privs(&bglobal
.bfdd_privs
) {
1182 sd
= vrf_socket(AF_INET6
, SOCK_DGRAM
, PF_UNSPEC
, vrf
->vrf_id
,
1186 if (errno
!= EAFNOSUPPORT
)
1187 zlog_fatal("udp6-mhop: socket: %s", strerror(errno
));
1189 zlog_warn("udp6-mhop: V6 is not supported, continuing");
1194 bp_set_ipv6opts(sd
);
1195 bp_bind_ipv6(sd
, BFD_DEF_MHOP_DEST_PORT
);
1200 int bp_echo_socket(const struct vrf
*vrf
)
1204 frr_with_privs(&bglobal
.bfdd_privs
) {
1205 s
= vrf_socket(AF_INET
, SOCK_DGRAM
, 0, vrf
->vrf_id
, vrf
->name
);
1208 zlog_fatal("echo-socket: socket: %s", strerror(errno
));
1211 bp_bind_ip(s
, BFD_DEF_ECHO_PORT
);
1216 int bp_echov6_socket(const struct vrf
*vrf
)
1220 frr_with_privs(&bglobal
.bfdd_privs
) {
1221 s
= vrf_socket(AF_INET6
, SOCK_DGRAM
, 0, vrf
->vrf_id
, vrf
->name
);
1224 if (errno
!= EAFNOSUPPORT
)
1225 zlog_fatal("echov6-socket: socket: %s",
1228 zlog_warn("echov6-socket: V6 is not supported, continuing");
1234 bp_bind_ipv6(s
, BFD_DEF_ECHO_PORT
);