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 ifindex_t
*ifindex
, struct sockaddr_any
*local
,
51 struct sockaddr_any
*peer
);
52 ssize_t
bfd_recv_ipv6(int sd
, uint8_t *msgbuf
, size_t msgbuflen
, uint8_t *ttl
,
53 ifindex_t
*ifindex
, struct sockaddr_any
*local
,
54 struct sockaddr_any
*peer
);
55 int bp_udp_send(int sd
, uint8_t ttl
, uint8_t *data
, size_t datalen
,
56 struct sockaddr
*to
, socklen_t tolen
);
57 int bp_bfd_echo_in(int sd
, 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 (BFD_CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_IPV6
)) {
80 memset(&sin6
, 0, sizeof(sin6
));
81 sin6
.sin6_family
= AF_INET6
;
82 sin6
.sin6_addr
= bs
->shop
.peer
.sa_sin6
.sin6_addr
;
85 : (BFD_CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_MH
))
86 ? htons(BFD_DEF_MHOP_DEST_PORT
)
87 : htons(BFD_DEFDESTPORT
);
90 sa
= (struct sockaddr
*)&sin6
;
93 memset(&sin
, 0, sizeof(sin
));
94 sin
.sin_family
= AF_INET
;
95 sin
.sin_addr
= bs
->shop
.peer
.sa_sin
.sin_addr
;
98 : (BFD_CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_MH
))
99 ? htons(BFD_DEF_MHOP_DEST_PORT
)
100 : htons(BFD_DEFDESTPORT
);
103 sa
= (struct sockaddr
*)&sin
;
107 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
109 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
110 rv
= sendto(sd
, data
, datalen
, 0, sa
, slen
);
112 log_debug("packet-send: send failure: %s", strerror(errno
));
115 if (rv
< (ssize_t
)datalen
)
116 log_debug("packet-send: send partial", strerror(errno
));
121 void ptm_bfd_echo_snd(struct bfd_session
*bfd
)
123 struct sockaddr_any
*sa
;
126 struct bfd_echo_pkt bep
;
127 struct sockaddr_in sin
;
128 struct sockaddr_in6 sin6
;
130 if (!BFD_CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_ECHO_ACTIVE
))
131 BFD_SET_FLAG(bfd
->flags
, BFD_SESS_FLAG_ECHO_ACTIVE
);
133 memset(&bep
, 0, sizeof(bep
));
134 bep
.ver
= BFD_ECHO_VERSION
;
135 bep
.len
= BFD_ECHO_PKT_LEN
;
136 bep
.my_discr
= htonl(bfd
->discrs
.my_discr
);
138 sa
= BFD_CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_MH
) ? &bfd
->mhop
.peer
140 if (BFD_CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_IPV6
)) {
141 sd
= bglobal
.bg_echov6
;
143 sin6
.sin6_port
= htons(BFD_DEF_ECHO_PORT
);
144 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
145 sin6
.sin6_len
= sizeof(sin6
);
146 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
148 sa
= (struct sockaddr_any
*)&sin6
;
149 salen
= sizeof(sin6
);
151 sd
= bglobal
.bg_echo
;
153 sin
.sin_port
= htons(BFD_DEF_ECHO_PORT
);
154 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
155 sin
.sin_len
= sizeof(sin
);
156 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
158 sa
= (struct sockaddr_any
*)&sin
;
161 if (bp_udp_send(sd
, BFD_TTL_VAL
, (uint8_t *)&bep
, sizeof(bep
),
162 (struct sockaddr
*)sa
, salen
)
166 bfd
->stats
.tx_echo_pkt
++;
169 static int ptm_bfd_process_echo_pkt(int s
)
171 struct bfd_session
*bfd
;
172 uint32_t my_discr
= 0;
175 /* Receive and parse echo packet. */
176 if (bp_bfd_echo_in(s
, &ttl
, &my_discr
) == -1)
179 /* Your discriminator not zero - use it to find session */
180 bfd
= bfd_id_lookup(my_discr
);
182 log_debug("echo-packet: no matching session (id:%u)", my_discr
);
186 if (!BFD_CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_ECHO_ACTIVE
)) {
187 log_debug("echo-packet: echo disabled [%s] (id:%u)",
188 bs_to_string(bfd
), my_discr
);
192 bfd
->stats
.rx_echo_pkt
++;
194 /* Compute detect time */
195 bfd
->echo_detect_TO
= bfd
->remote_detect_mult
* bfd
->echo_xmt_TO
;
197 /* Update echo receive timeout. */
198 if (bfd
->echo_detect_TO
> 0)
199 bfd_echo_recvtimer_update(bfd
);
204 void ptm_bfd_snd(struct bfd_session
*bfd
, int fbit
)
208 /* Set fields according to section 6.5.7 */
209 cp
.diag
= bfd
->local_diag
;
210 BFD_SETVER(cp
.diag
, BFD_VERSION
);
212 BFD_SETSTATE(cp
.flags
, bfd
->ses_state
);
213 BFD_SETDEMANDBIT(cp
.flags
, BFD_DEF_DEMAND
);
216 * Polling and Final can't be set at the same time.
218 * RFC 5880, Section 6.5.
220 BFD_SETFBIT(cp
.flags
, fbit
);
222 BFD_SETPBIT(cp
.flags
, bfd
->polling
);
224 cp
.detect_mult
= bfd
->detect_mult
;
225 cp
.len
= BFD_PKT_LEN
;
226 cp
.discrs
.my_discr
= htonl(bfd
->discrs
.my_discr
);
227 cp
.discrs
.remote_discr
= htonl(bfd
->discrs
.remote_discr
);
229 cp
.timers
.desired_min_tx
=
230 htonl(bfd
->timers
.desired_min_tx
);
231 cp
.timers
.required_min_rx
=
232 htonl(bfd
->timers
.required_min_rx
);
235 * We can only announce current setting on poll, this
236 * avoids timing mismatch with our peer and give it
237 * the oportunity to learn. See `bs_final_handler` for
240 cp
.timers
.desired_min_tx
=
241 htonl(bfd
->cur_timers
.desired_min_tx
);
242 cp
.timers
.required_min_rx
=
243 htonl(bfd
->cur_timers
.required_min_rx
);
245 cp
.timers
.required_min_echo
= htonl(bfd
->timers
.required_min_echo
);
247 if (_ptm_bfd_send(bfd
, NULL
, &cp
, BFD_PKT_LEN
) != 0)
250 bfd
->stats
.tx_ctrl_pkt
++;
253 ssize_t
bfd_recv_ipv4(int sd
, uint8_t *msgbuf
, size_t msgbuflen
, uint8_t *ttl
,
254 ifindex_t
*ifindex
, struct sockaddr_any
*local
,
255 struct sockaddr_any
*peer
)
259 struct sockaddr_in msgaddr
;
260 struct msghdr msghdr
;
262 uint8_t cmsgbuf
[255];
264 /* Prepare the recvmsg params. */
265 iov
[0].iov_base
= msgbuf
;
266 iov
[0].iov_len
= msgbuflen
;
268 memset(&msghdr
, 0, sizeof(msghdr
));
269 msghdr
.msg_name
= &msgaddr
;
270 msghdr
.msg_namelen
= sizeof(msgaddr
);
271 msghdr
.msg_iov
= iov
;
272 msghdr
.msg_iovlen
= 1;
273 msghdr
.msg_control
= cmsgbuf
;
274 msghdr
.msg_controllen
= sizeof(cmsgbuf
);
276 mlen
= recvmsg(sd
, &msghdr
, MSG_DONTWAIT
);
279 log_error("ipv4-recv: recv failed: %s",
285 /* Get source address */
286 peer
->sa_sin
= *((struct sockaddr_in
*)(msghdr
.msg_name
));
288 /* Get and check TTL */
289 for (cm
= CMSG_FIRSTHDR(&msghdr
); cm
!= NULL
;
290 cm
= CMSG_NXTHDR(&msghdr
, cm
)) {
291 if (cm
->cmsg_level
!= IPPROTO_IP
)
294 switch (cm
->cmsg_type
) {
299 memcpy(&ttlval
, CMSG_DATA(cm
), sizeof(ttlval
));
301 log_debug("ipv4-recv: invalid TTL: %u", ttlval
);
309 struct in_pktinfo
*pi
=
310 (struct in_pktinfo
*)CMSG_DATA(cm
);
315 local
->sa_sin
.sin_family
= AF_INET
;
316 local
->sa_sin
.sin_addr
= pi
->ipi_addr
;
317 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
318 local
->sa_sin
.sin_len
= sizeof(local
->sa_sin
);
319 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
321 *ifindex
= pi
->ipi_ifindex
;
324 #endif /* BFD_LINUX */
327 memcpy(ttl
, CMSG_DATA(cm
), sizeof(*ttl
));
331 case IP_RECVDSTADDR
: {
334 memcpy(&ia
, CMSG_DATA(cm
), sizeof(ia
));
335 local
->sa_sin
.sin_family
= AF_INET
;
336 local
->sa_sin
.sin_addr
= ia
;
337 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
338 local
->sa_sin
.sin_len
= sizeof(local
->sa_sin
);
339 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
346 * On *BSDs we expect to land here when skipping
347 * the IP_RECVIF header. It will be handled by
348 * getsockopt_ifindex() below.
355 /* OS agnostic way of getting interface name. */
356 if (*ifindex
== IFINDEX_INTERNAL
)
357 *ifindex
= getsockopt_ifindex(AF_INET
, &msghdr
);
362 ssize_t
bfd_recv_ipv6(int sd
, uint8_t *msgbuf
, size_t msgbuflen
, uint8_t *ttl
,
363 ifindex_t
*ifindex
, struct sockaddr_any
*local
,
364 struct sockaddr_any
*peer
)
367 struct in6_pktinfo
*pi6
= NULL
;
370 struct sockaddr_in6 msgaddr6
;
371 struct msghdr msghdr6
;
373 uint8_t cmsgbuf6
[255];
375 /* Prepare the recvmsg params. */
376 iov
[0].iov_base
= msgbuf
;
377 iov
[0].iov_len
= msgbuflen
;
379 memset(&msghdr6
, 0, sizeof(msghdr6
));
380 msghdr6
.msg_name
= &msgaddr6
;
381 msghdr6
.msg_namelen
= sizeof(msgaddr6
);
382 msghdr6
.msg_iov
= iov
;
383 msghdr6
.msg_iovlen
= 1;
384 msghdr6
.msg_control
= cmsgbuf6
;
385 msghdr6
.msg_controllen
= sizeof(cmsgbuf6
);
387 mlen
= recvmsg(sd
, &msghdr6
, MSG_DONTWAIT
);
390 log_error("ipv6-recv: recv failed: %s",
396 /* Get source address */
397 peer
->sa_sin6
= *((struct sockaddr_in6
*)(msghdr6
.msg_name
));
399 /* Get and check TTL */
400 for (cm
= CMSG_FIRSTHDR(&msghdr6
); cm
!= NULL
;
401 cm
= CMSG_NXTHDR(&msghdr6
, cm
)) {
402 if (cm
->cmsg_level
!= IPPROTO_IPV6
)
405 if (cm
->cmsg_type
== IPV6_HOPLIMIT
) {
406 memcpy(&ttlval
, CMSG_DATA(cm
), sizeof(ttlval
));
408 log_debug("ipv6-recv: invalid TTL: %u", ttlval
);
413 } else if (cm
->cmsg_type
== IPV6_PKTINFO
) {
414 pi6
= (struct in6_pktinfo
*)CMSG_DATA(cm
);
416 local
->sa_sin6
.sin6_family
= AF_INET6
;
417 local
->sa_sin6
.sin6_addr
= pi6
->ipi6_addr
;
418 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
419 local
->sa_sin6
.sin6_len
= sizeof(local
->sa_sin6
);
420 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
422 *ifindex
= pi6
->ipi6_ifindex
;
427 /* Set scope ID for link local addresses. */
428 if (IN6_IS_ADDR_LINKLOCAL(&peer
->sa_sin6
.sin6_addr
))
429 peer
->sa_sin6
.sin6_scope_id
= *ifindex
;
430 if (IN6_IS_ADDR_LINKLOCAL(&local
->sa_sin6
.sin6_addr
))
431 local
->sa_sin6
.sin6_scope_id
= *ifindex
;
436 static void bfd_sd_reschedule(int sd
)
438 if (sd
== bglobal
.bg_shop
) {
439 THREAD_OFF(bglobal
.bg_ev
[0]);
440 thread_add_read(master
, bfd_recv_cb
, NULL
, bglobal
.bg_shop
,
442 } else if (sd
== bglobal
.bg_mhop
) {
443 THREAD_OFF(bglobal
.bg_ev
[1]);
444 thread_add_read(master
, bfd_recv_cb
, NULL
, bglobal
.bg_mhop
,
446 } else if (sd
== bglobal
.bg_shop6
) {
447 THREAD_OFF(bglobal
.bg_ev
[2]);
448 thread_add_read(master
, bfd_recv_cb
, NULL
, bglobal
.bg_shop6
,
450 } else if (sd
== bglobal
.bg_mhop6
) {
451 THREAD_OFF(bglobal
.bg_ev
[3]);
452 thread_add_read(master
, bfd_recv_cb
, NULL
, bglobal
.bg_mhop6
,
454 } else if (sd
== bglobal
.bg_echo
) {
455 THREAD_OFF(bglobal
.bg_ev
[4]);
456 thread_add_read(master
, bfd_recv_cb
, NULL
, bglobal
.bg_echo
,
458 } else if (sd
== bglobal
.bg_echov6
) {
459 THREAD_OFF(bglobal
.bg_ev
[5]);
460 thread_add_read(master
, bfd_recv_cb
, NULL
, bglobal
.bg_echov6
,
465 static void cp_debug(bool mhop
, struct sockaddr_any
*peer
,
466 struct sockaddr_any
*local
, ifindex_t ifindex
,
467 vrf_id_t vrfid
, const char *fmt
, ...)
469 char buf
[512], peerstr
[128], localstr
[128], portstr
[64], vrfstr
[64];
472 if (peer
->sa_sin
.sin_family
)
473 snprintf(peerstr
, sizeof(peerstr
), " peer:%s", satostr(peer
));
477 if (local
->sa_sin
.sin_family
)
478 snprintf(localstr
, sizeof(localstr
), " local:%s",
483 if (ifindex
!= IFINDEX_INTERNAL
)
484 snprintf(portstr
, sizeof(portstr
), " port:%u", ifindex
);
488 if (vrfid
!= VRF_DEFAULT
)
489 snprintf(vrfstr
, sizeof(vrfstr
), " vrf:%u", vrfid
);
494 vsnprintf(buf
, sizeof(buf
), fmt
, vl
);
497 log_debug("control-packet: %s [mhop:%s%s%s%s%s]", buf
,
498 mhop
? "yes" : "no", peerstr
, localstr
, portstr
, vrfstr
);
501 int bfd_recv_cb(struct thread
*t
)
503 int sd
= THREAD_FD(t
);
504 struct bfd_session
*bfd
;
509 vrf_id_t vrfid
= VRF_DEFAULT
;
510 ifindex_t ifindex
= IFINDEX_INTERNAL
;
511 struct sockaddr_any local
, peer
;
512 uint8_t msgbuf
[1516];
514 /* Schedule next read. */
515 bfd_sd_reschedule(sd
);
517 /* Handle echo packets. */
518 if (sd
== bglobal
.bg_echo
|| sd
== bglobal
.bg_echov6
) {
519 ptm_bfd_process_echo_pkt(sd
);
523 /* Sanitize input/output. */
524 memset(&local
, 0, sizeof(local
));
525 memset(&peer
, 0, sizeof(peer
));
527 /* Handle control packets. */
529 if (sd
== bglobal
.bg_shop
|| sd
== bglobal
.bg_mhop
) {
530 is_mhop
= sd
== bglobal
.bg_mhop
;
531 mlen
= bfd_recv_ipv4(sd
, msgbuf
, sizeof(msgbuf
), &ttl
, &ifindex
,
533 } else if (sd
== bglobal
.bg_shop6
|| sd
== bglobal
.bg_mhop6
) {
534 is_mhop
= sd
== bglobal
.bg_mhop6
;
535 mlen
= bfd_recv_ipv6(sd
, msgbuf
, sizeof(msgbuf
), &ttl
, &ifindex
,
539 /* Implement RFC 5880 6.8.6 */
540 if (mlen
< BFD_PKT_LEN
) {
541 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
,
542 "too small (%ld bytes)", mlen
);
546 /* Validate packet TTL. */
547 if ((!is_mhop
) && (ttl
!= BFD_TTL_VAL
)) {
548 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
,
549 "invalid TTL: %d expected %d", ttl
, BFD_TTL_VAL
);
554 * Parse the control header for inconsistencies:
556 * - Bad multiplier configuration;
558 * - Invalid discriminator;
560 cp
= (struct bfd_pkt
*)(msgbuf
);
561 if (BFD_GETVER(cp
->diag
) != BFD_VERSION
) {
562 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
,
563 "bad version %d", BFD_GETVER(cp
->diag
));
567 if (cp
->detect_mult
== 0) {
568 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
,
569 "detect multiplier set to zero");
573 if ((cp
->len
< BFD_PKT_LEN
) || (cp
->len
> mlen
)) {
574 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
, "too small");
578 if (cp
->discrs
.my_discr
== 0) {
579 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
,
580 "'my discriminator' is zero");
584 /* Find the session that this packet belongs. */
585 bfd
= ptm_bfd_sess_find(cp
, &peer
, &local
, ifindex
, vrfid
, is_mhop
);
587 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
,
592 bfd
->stats
.rx_ctrl_pkt
++;
595 * Multi hop: validate packet TTL.
596 * Single hop: set local address that received the packet.
599 if ((BFD_TTL_VAL
- bfd
->mh_ttl
) > BFD_TTL_VAL
) {
600 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
,
601 "exceeded max hop count (expected %d, got %d)",
602 bfd
->mh_ttl
, BFD_TTL_VAL
);
605 } else if (bfd
->local_ip
.sa_sin
.sin_family
== AF_UNSPEC
) {
606 bfd
->local_ip
= local
;
610 * If no interface was detected, save the interface where the
613 if (bfd
->ifp
== NULL
)
614 bfd
->ifp
= if_lookup_by_index(ifindex
, vrfid
);
616 /* Log remote discriminator changes. */
617 if ((bfd
->discrs
.remote_discr
!= 0)
618 && (bfd
->discrs
.remote_discr
!= ntohl(cp
->discrs
.my_discr
)))
619 cp_debug(is_mhop
, &peer
, &local
, ifindex
, vrfid
,
620 "remote discriminator mismatch (expected %u, got %u)",
621 bfd
->discrs
.remote_discr
, ntohl(cp
->discrs
.my_discr
));
623 bfd
->discrs
.remote_discr
= ntohl(cp
->discrs
.my_discr
);
625 /* Save remote diagnostics before state switch. */
626 bfd
->remote_diag
= cp
->diag
& BFD_DIAGMASK
;
628 /* Update remote timers settings. */
629 bfd
->remote_timers
.desired_min_tx
= ntohl(cp
->timers
.desired_min_tx
);
630 bfd
->remote_timers
.required_min_rx
= ntohl(cp
->timers
.required_min_rx
);
631 bfd
->remote_timers
.required_min_echo
=
632 ntohl(cp
->timers
.required_min_echo
);
633 bfd
->remote_detect_mult
= cp
->detect_mult
;
635 /* State switch from section 6.2. */
636 bs_state_handler(bfd
, BFD_GETSTATE(cp
->flags
));
638 /* RFC 5880, Section 6.5: handle POLL/FINAL negotiation sequence. */
639 if (bfd
->polling
&& BFD_GETFBIT(cp
->flags
)) {
640 /* Disable pooling. */
643 /* Handle poll finalization. */
644 bs_final_handler(bfd
);
646 /* Received a packet, lets update the receive timer. */
647 bfd_recvtimer_update(bfd
);
650 /* Handle echo timers changes. */
651 bs_echo_timer_handler(bfd
);
654 * We've received a packet with the POLL bit set, we must send
655 * a control packet back with the FINAL bit set.
657 * RFC 5880, Section 6.5.
659 if (BFD_GETPBIT(cp
->flags
)) {
660 /* We are finalizing a poll negotiation. */
661 bs_final_handler(bfd
);
663 /* Send the control packet with the final bit immediately. */
671 * bp_bfd_echo_in: proccesses an BFD echo packet. On TTL == BFD_TTL_VAL
672 * the packet is looped back or returns the my discriminator ID along
675 * Returns -1 on error or loopback or 0 on success.
677 int bp_bfd_echo_in(int sd
, uint8_t *ttl
, uint32_t *my_discr
)
679 struct bfd_echo_pkt
*bep
;
681 struct sockaddr_any local
, peer
;
682 ifindex_t ifindex
= IFINDEX_INTERNAL
;
683 vrf_id_t vrfid
= VRF_DEFAULT
;
684 uint8_t msgbuf
[1516];
686 if (sd
== bglobal
.bg_echo
)
687 rlen
= bfd_recv_ipv4(sd
, msgbuf
, sizeof(msgbuf
), ttl
, &ifindex
,
690 rlen
= bfd_recv_ipv6(sd
, msgbuf
, sizeof(msgbuf
), ttl
, &ifindex
,
693 /* Short packet, better not risk reading it. */
694 if (rlen
< (ssize_t
)sizeof(*bep
)) {
695 cp_debug(false, &peer
, &local
, ifindex
, vrfid
,
696 "small echo packet");
700 /* Test for loopback. */
701 if (*ttl
== BFD_TTL_VAL
) {
702 bp_udp_send(sd
, *ttl
- 1, msgbuf
, rlen
,
703 (struct sockaddr
*)&peer
,
704 (sd
== bglobal
.bg_echo
) ? sizeof(peer
.sa_sin
)
705 : sizeof(peer
.sa_sin6
));
709 /* Read my discriminator from BFD Echo packet. */
710 bep
= (struct bfd_echo_pkt
*)msgbuf
;
711 *my_discr
= ntohl(bep
->my_discr
);
712 if (*my_discr
== 0) {
713 cp_debug(false, &peer
, &local
, ifindex
, vrfid
,
714 "invalid echo packet discriminator (zero)");
721 int bp_udp_send(int sd
, uint8_t ttl
, uint8_t *data
, size_t datalen
,
722 struct sockaddr
*to
, socklen_t tolen
)
724 struct cmsghdr
*cmsg
;
727 bool is_ipv6
= to
->sa_family
== AF_INET6
;
732 /* Prepare message data. */
733 iov
[0].iov_base
= data
;
734 iov
[0].iov_len
= datalen
;
736 memset(&msg
, 0, sizeof(msg
));
737 memset(msgctl
, 0, sizeof(msgctl
));
739 msg
.msg_namelen
= tolen
;
743 /* Prepare the packet TTL information. */
745 /* Use ancillary data. */
746 msg
.msg_control
= msgctl
;
747 msg
.msg_controllen
= CMSG_LEN(sizeof(ttlval
));
749 /* Configure the ancillary data. */
750 cmsg
= CMSG_FIRSTHDR(&msg
);
751 cmsg
->cmsg_len
= CMSG_LEN(sizeof(ttlval
));
753 cmsg
->cmsg_level
= IPPROTO_IPV6
;
754 cmsg
->cmsg_type
= IPV6_HOPLIMIT
;
757 cmsg
->cmsg_level
= IPPROTO_IP
;
758 cmsg
->cmsg_type
= IP_TTL
;
760 /* FreeBSD does not support TTL in ancillary data. */
761 msg
.msg_control
= NULL
;
762 msg
.msg_controllen
= 0;
767 memcpy(CMSG_DATA(cmsg
), &ttlval
, sizeof(ttlval
));
770 /* Send echo back. */
771 wlen
= sendmsg(sd
, &msg
, 0);
773 log_debug("udp-send: loopback failure: (%d) %s", errno
, strerror(errno
));
775 } else if (wlen
< (ssize_t
)datalen
) {
776 log_debug("udp-send: partial send: %ld expected %ld", wlen
,
793 int bp_set_ttl(int sd
, uint8_t value
)
797 if (setsockopt(sd
, IPPROTO_IP
, IP_TTL
, &ttl
, sizeof(ttl
)) == -1) {
798 log_warning("set-ttl: setsockopt(IP_TTL, %d): %s", value
,
806 int bp_set_tos(int sd
, uint8_t value
)
810 if (setsockopt(sd
, IPPROTO_IP
, IP_TOS
, &tos
, sizeof(tos
)) == -1) {
811 log_warning("set-tos: setsockopt(IP_TOS, %d): %s", value
,
819 static void bp_set_ipopts(int sd
)
821 int rcvttl
= BFD_RCV_TTL_VAL
;
823 if (bp_set_ttl(sd
, BFD_TTL_VAL
) != 0)
824 log_fatal("set-ipopts: TTL configuration failed");
826 if (setsockopt(sd
, IPPROTO_IP
, IP_RECVTTL
, &rcvttl
, sizeof(rcvttl
))
828 log_fatal("set-ipopts: setsockopt(IP_RECVTTL, %d): %s", rcvttl
,
832 int pktinfo
= BFD_PKT_INFO_VAL
;
834 /* Figure out address and interface to do the peer matching. */
835 if (setsockopt(sd
, IPPROTO_IP
, IP_PKTINFO
, &pktinfo
, sizeof(pktinfo
))
837 log_fatal("set-ipopts: setsockopt(IP_PKTINFO, %d): %s", pktinfo
,
839 #endif /* BFD_LINUX */
843 /* Find out our address for peer matching. */
844 if (setsockopt(sd
, IPPROTO_IP
, IP_RECVDSTADDR
, &yes
, sizeof(yes
)) == -1)
845 log_fatal("set-ipopts: setsockopt(IP_RECVDSTADDR, %d): %s", yes
,
848 /* Find out interface where the packet came in. */
849 if (setsockopt_ifindex(AF_INET
, sd
, yes
) == -1)
850 log_fatal("set-ipopts: setsockopt_ipv4_ifindex(%d): %s", yes
,
855 static void bp_bind_ip(int sd
, uint16_t port
)
857 struct sockaddr_in sin
;
859 memset(&sin
, 0, sizeof(sin
));
860 sin
.sin_family
= AF_INET
;
861 sin
.sin_addr
.s_addr
= htonl(INADDR_ANY
);
862 sin
.sin_port
= htons(port
);
863 if (bind(sd
, (struct sockaddr
*)&sin
, sizeof(sin
)) == -1)
864 log_fatal("bind-ip: bind: %s", strerror(errno
));
867 int bp_udp_shop(void)
871 sd
= socket(AF_INET
, SOCK_DGRAM
, PF_UNSPEC
);
873 log_fatal("udp-shop: socket: %s", strerror(errno
));
876 bp_bind_ip(sd
, BFD_DEFDESTPORT
);
881 int bp_udp_mhop(void)
885 sd
= socket(AF_INET
, SOCK_DGRAM
, PF_UNSPEC
);
887 log_fatal("udp-mhop: socket: %s", strerror(errno
));
890 bp_bind_ip(sd
, BFD_DEF_MHOP_DEST_PORT
);
895 int bp_peer_socket(const struct bfd_session
*bs
)
898 struct sockaddr_in sin
;
899 static int srcPort
= BFD_SRCPORTINIT
;
901 sd
= socket(AF_INET
, SOCK_DGRAM
, PF_UNSPEC
);
903 log_error("ipv4-new: failed to create socket: %s",
908 /* Set TTL to 255 for all transmitted packets */
909 if (bp_set_ttl(sd
, BFD_TTL_VAL
) != 0) {
914 /* Set TOS to CS6 for all transmitted packets */
915 if (bp_set_tos(sd
, BFD_TOS_VAL
) != 0) {
920 if (bs
->shop
.ifindex
!= IFINDEX_INTERNAL
) {
921 if (bp_bind_dev(sd
, bs
->ifp
->name
) != 0) {
925 } else if (BFD_CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_MH
) &&
926 bs
->mhop
.vrfid
!= VRF_DEFAULT
) {
927 if (bp_bind_dev(sd
, bs
->vrf
->name
) != 0) {
933 /* Find an available source port in the proper range */
934 sin
= bs
->local_ip
.sa_sin
;
935 sin
.sin_family
= AF_INET
;
936 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
937 sin
.sin_len
= sizeof(sin
);
938 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
939 if (BFD_CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_MH
) == 0)
940 sin
.sin_addr
.s_addr
= INADDR_ANY
;
944 if ((++pcount
) > (BFD_SRCPORTMAX
- BFD_SRCPORTINIT
)) {
945 /* Searched all ports, none available */
946 log_error("ipv4-new: failed to bind port: %s",
951 if (srcPort
>= BFD_SRCPORTMAX
)
952 srcPort
= BFD_SRCPORTINIT
;
953 sin
.sin_port
= htons(srcPort
++);
954 } while (bind(sd
, (struct sockaddr
*)&sin
, sizeof(sin
)) < 0);
964 int bp_peer_socketv6(const struct bfd_session
*bs
)
967 struct sockaddr_in6 sin6
;
968 static int srcPort
= BFD_SRCPORTINIT
;
970 sd
= socket(AF_INET6
, SOCK_DGRAM
, PF_UNSPEC
);
972 log_error("ipv6-new: failed to create socket: %s",
977 /* Set TTL to 255 for all transmitted packets */
978 if (bp_set_ttlv6(sd
, BFD_TTL_VAL
) != 0) {
983 /* Set TOS to CS6 for all transmitted packets */
984 if (bp_set_tosv6(sd
, BFD_TOS_VAL
) != 0) {
989 /* Find an available source port in the proper range */
990 sin6
= bs
->local_ip
.sa_sin6
;
991 sin6
.sin6_family
= AF_INET6
;
992 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
993 sin6
.sin6_len
= sizeof(sin6
);
994 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
996 if (bs
->shop
.ifindex
!= IFINDEX_INTERNAL
) {
997 if (bp_bind_dev(sd
, bs
->ifp
->name
) != 0) {
1001 } else if (BFD_CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_MH
) &&
1002 bs
->mhop
.vrfid
!= VRF_DEFAULT
) {
1003 if (bp_bind_dev(sd
, bs
->vrf
->name
) != 0) {
1011 if ((++pcount
) > (BFD_SRCPORTMAX
- BFD_SRCPORTINIT
)) {
1012 /* Searched all ports, none available */
1013 log_error("ipv6-new: failed to bind port: %s",
1018 if (srcPort
>= BFD_SRCPORTMAX
)
1019 srcPort
= BFD_SRCPORTINIT
;
1020 sin6
.sin6_port
= htons(srcPort
++);
1021 } while (bind(sd
, (struct sockaddr
*)&sin6
, sizeof(sin6
)) < 0);
1026 int bp_set_ttlv6(int sd
, uint8_t value
)
1030 if (setsockopt(sd
, IPPROTO_IPV6
, IPV6_UNICAST_HOPS
, &ttl
, sizeof(ttl
))
1032 log_warning("set-ttlv6: setsockopt(IPV6_UNICAST_HOPS, %d): %s",
1033 value
, strerror(errno
));
1040 int bp_set_tosv6(int sd
, uint8_t value
)
1044 if (setsockopt(sd
, IPPROTO_IPV6
, IPV6_TCLASS
, &tos
, sizeof(tos
))
1046 log_warning("set-tosv6: setsockopt(IPV6_TCLASS, %d): %s", value
,
1054 static void bp_set_ipv6opts(int sd
)
1056 int ipv6_pktinfo
= BFD_IPV6_PKT_INFO_VAL
;
1057 int ipv6_only
= BFD_IPV6_ONLY_VAL
;
1059 if (bp_set_ttlv6(sd
, BFD_TTL_VAL
) == -1)
1060 log_fatal("set-ipv6opts: setsockopt(IPV6_UNICAST_HOPS, %d): %s",
1061 BFD_TTL_VAL
, strerror(errno
));
1063 if (setsockopt_ipv6_hoplimit(sd
, BFD_RCV_TTL_VAL
) == -1)
1064 log_fatal("set-ipv6opts: setsockopt(IPV6_HOPLIMIT, %d): %s",
1065 BFD_RCV_TTL_VAL
, strerror(errno
));
1067 if (setsockopt_ipv6_pktinfo(sd
, ipv6_pktinfo
) == -1)
1068 log_fatal("set-ipv6opts: setsockopt(IPV6_PKTINFO, %d): %s",
1069 ipv6_pktinfo
, strerror(errno
));
1071 if (setsockopt(sd
, IPPROTO_IPV6
, IPV6_V6ONLY
, &ipv6_only
,
1074 log_fatal("set-ipv6opts: setsockopt(IPV6_V6ONLY, %d): %s",
1075 ipv6_only
, strerror(errno
));
1078 static void bp_bind_ipv6(int sd
, uint16_t port
)
1080 struct sockaddr_in6 sin6
;
1082 memset(&sin6
, 0, sizeof(sin6
));
1083 sin6
.sin6_family
= AF_INET6
;
1084 sin6
.sin6_addr
= in6addr_any
;
1085 sin6
.sin6_port
= htons(port
);
1086 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1087 sin6
.sin6_len
= sizeof(sin6
);
1088 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1089 if (bind(sd
, (struct sockaddr
*)&sin6
, sizeof(sin6
)) == -1)
1090 log_fatal("bind-ipv6: bind: %s", strerror(errno
));
1093 int bp_udp6_shop(void)
1097 sd
= socket(AF_INET6
, SOCK_DGRAM
, PF_UNSPEC
);
1099 log_fatal("udp6-shop: socket: %s", strerror(errno
));
1101 bp_set_ipv6opts(sd
);
1102 bp_bind_ipv6(sd
, BFD_DEFDESTPORT
);
1107 int bp_udp6_mhop(void)
1111 sd
= socket(AF_INET6
, SOCK_DGRAM
, PF_UNSPEC
);
1113 log_fatal("udp6-mhop: socket: %s", strerror(errno
));
1115 bp_set_ipv6opts(sd
);
1116 bp_bind_ipv6(sd
, BFD_DEF_MHOP_DEST_PORT
);
1121 int bp_echo_socket(void)
1125 s
= socket(AF_INET
, SOCK_DGRAM
, 0);
1127 log_fatal("echo-socket: socket: %s", strerror(errno
));
1130 bp_bind_ip(s
, BFD_DEF_ECHO_PORT
);
1135 int bp_echov6_socket(void)
1139 s
= socket(AF_INET6
, SOCK_DGRAM
, 0);
1141 log_fatal("echov6-socket: socket: %s", strerror(errno
));
1144 bp_bind_ipv6(s
, BFD_DEF_ECHO_PORT
);