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 /* iov for BFD control frames */
45 #define CMSG_HDR_LEN sizeof(struct cmsghdr)
46 #define CMSG_TTL_LEN (CMSG_HDR_LEN + sizeof(uint32_t))
47 #define CMSG_IN_PKT_INFO_LEN (CMSG_HDR_LEN + sizeof(struct in_pktinfo) + 4)
48 #define CMSG_IN6_PKT_INFO_LEN \
49 (CMSG_HDR_LEN + sizeof(struct in6_addr) + sizeof(int) + 4)
51 struct bfd_raw_echo_pkt
{
54 #endif /* BFD_LINUX */
59 struct bfd_echo_pkt data
;
62 #if 0 /* TODO: VxLAN support. */
63 struct bfd_raw_ctrl_pkt
{
75 #define IP_ECHO_PKT_LEN (IP_HDR_LEN + UDP_HDR_LEN + BFD_ECHO_PKT_LEN)
76 #define UDP_ECHO_PKT_LEN (UDP_HDR_LEN + BFD_ECHO_PKT_LEN)
77 #define IP_CTRL_PKT_LEN (IP_HDR_LEN + UDP_HDR_LEN + BFD_PKT_LEN)
78 #define UDP_CTRL_PKT_LEN (UDP_HDR_LEN + BFD_PKT_LEN)
80 static uint8_t msgbuf
[BFD_PKT_LEN
];
85 static uint16_t ptm_bfd_gen_IP_ID(struct bfd_session
*bfd
);
86 static void ptm_bfd_echo_pkt_create(struct bfd_session
*bfd
);
87 static int ptm_bfd_echo_loopback(uint8_t *pkt
, int pkt_len
, struct sockaddr
*ss
,
89 static void ptm_bfd_vxlan_pkt_snd(struct bfd_session
*bfd
, int fbit
);
90 static int ptm_bfd_process_echo_pkt(int s
);
92 ptm_bfd_validate_vxlan_pkt(struct bfd_session
*bfd
,
93 struct bfd_session_vxlan_info
*vxlan_info
);
95 static void bfd_sd_reschedule(int sd
);
96 static ssize_t
bfd_recv_ipv4(int sd
, bool is_mhop
, char *port
, size_t portlen
,
97 char *vrfname
, size_t vrfnamelen
,
98 struct sockaddr_any
*local
,
99 struct sockaddr_any
*peer
);
100 static ssize_t
bfd_recv_ipv6(int sd
, bool is_mhop
, char *port
, size_t portlen
,
101 char *vrfname
, size_t vrfnamelen
,
102 struct sockaddr_any
*local
,
103 struct sockaddr_any
*peer
);
105 /* socket related prototypes */
106 static void bp_set_ipopts(int sd
);
107 static void bp_bind_ip(int sd
, uint16_t port
);
108 static void bp_set_ipv6opts(int sd
);
109 static void bp_bind_ipv6(int sd
, uint16_t port
);
115 uint16_t checksum(uint16_t *buf
, int len
)
120 int size
= sizeof(uint16_t);
128 *(uint8_t *)(&csum
) = *(uint8_t *)buf
;
132 sum
= (sum
>> 16) + (sum
& 0xFFFF);
138 static uint16_t ptm_bfd_gen_IP_ID(struct bfd_session
*bfd
)
140 return (++bfd
->ip_id
);
143 static int _ptm_bfd_send(struct bfd_session
*bs
, bool use_layer2
,
144 uint16_t *port
, const void *data
, size_t datalen
)
147 struct sockaddr_in sin
;
148 struct sockaddr_in6 sin6
;
150 struct sockaddr_ll dll
;
151 #endif /* BFD_LINUX */
158 memset(&dll
, 0, sizeof(dll
));
159 dll
.sll_family
= AF_PACKET
;
160 dll
.sll_protocol
= htons(ETH_P_IP
);
161 memcpy(dll
.sll_addr
, bs
->peer_mac
, ETHERNET_ADDRESS_LENGTH
);
162 dll
.sll_halen
= htons(ETHERNET_ADDRESS_LENGTH
);
163 dll
.sll_ifindex
= bs
->ifindex
;
165 sd
= bglobal
.bg_echo
;
166 sa
= (struct sockaddr
*)&dll
;
170 * TODO: implement layer 2 send for *BSDs. This is
173 log_warning("packet-send: not implemented");
176 } else if (BFD_CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_IPV6
)) {
177 memset(&sin6
, 0, sizeof(sin6
));
178 sin6
.sin6_family
= AF_INET6
;
179 sin6
.sin6_addr
= bs
->shop
.peer
.sa_sin6
.sin6_addr
;
182 : (BFD_CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_MH
))
183 ? htons(BFD_DEF_MHOP_DEST_PORT
)
184 : htons(BFD_DEFDESTPORT
);
187 sa
= (struct sockaddr
*)&sin6
;
190 memset(&sin
, 0, sizeof(sin
));
191 sin
.sin_family
= AF_INET
;
192 sin
.sin_addr
= bs
->shop
.peer
.sa_sin
.sin_addr
;
195 : (BFD_CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_MH
))
196 ? htons(BFD_DEF_MHOP_DEST_PORT
)
197 : htons(BFD_DEFDESTPORT
);
200 sa
= (struct sockaddr
*)&sin
;
204 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
206 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
207 rv
= sendto(sd
, data
, datalen
, 0, sa
, slen
);
209 log_debug("packet-send: send failure: %s", strerror(errno
));
212 if (rv
< (ssize_t
)datalen
)
213 log_debug("packet-send: send partial", strerror(errno
));
218 static void ptm_bfd_echo_pkt_create(struct bfd_session
*bfd
)
220 struct bfd_raw_echo_pkt ep
;
221 uint8_t *pkt
= bfd
->echo_pkt
;
223 memset(&ep
, 0, sizeof(ep
));
224 memset(bfd
->echo_pkt
, 0, sizeof(bfd
->echo_pkt
));
226 /* Construct ethernet header information */
227 memcpy(pkt
, bfd
->peer_mac
, ETHERNET_ADDRESS_LENGTH
);
228 pkt
= pkt
+ ETHERNET_ADDRESS_LENGTH
;
229 memcpy(pkt
, bfd
->local_mac
, ETHERNET_ADDRESS_LENGTH
);
230 pkt
= pkt
+ ETHERNET_ADDRESS_LENGTH
;
232 pkt
[0] = ETH_P_IP
/ 256;
233 pkt
[1] = ETH_P_IP
% 256;
234 #endif /* BFD_LINUX */
236 pkt
[0] = ETHERTYPE_IP
/ 256;
237 pkt
[1] = ETHERTYPE_IP
% 256;
241 /* Construct IP header information */
246 ep
.ip
.tot_len
= htons(IP_ECHO_PKT_LEN
);
247 ep
.ip
.id
= htons(ptm_bfd_gen_IP_ID(bfd
));
249 ep
.ip
.ttl
= BFD_TTL_VAL
;
250 ep
.ip
.protocol
= IPPROTO_UDP
;
251 ep
.ip
.saddr
= bfd
->local_ip
.sa_sin
.sin_addr
.s_addr
;
252 ep
.ip
.daddr
= bfd
->shop
.peer
.sa_sin
.sin_addr
.s_addr
;
253 ep
.ip
.check
= checksum((uint16_t *)&ep
.ip
, IP_HDR_LEN
);
254 #endif /* BFD_LINUX */
259 ep
.ip
.ip_len
= htons(IP_ECHO_PKT_LEN
);
260 ep
.ip
.ip_id
= htons(ptm_bfd_gen_IP_ID(bfd
));
262 ep
.ip
.ip_ttl
= BFD_TTL_VAL
;
263 ep
.ip
.ip_p
= IPPROTO_UDP
;
264 ep
.ip
.ip_src
= bfd
->local_ip
.sa_sin
.sin_addr
;
265 ep
.ip
.ip_dst
= bfd
->shop
.peer
.sa_sin
.sin_addr
;
266 ep
.ip
.ip_sum
= checksum((uint16_t *)&ep
.ip
, IP_HDR_LEN
);
269 /* Construct UDP header information */
271 ep
.udp
.source
= htons(BFD_DEF_ECHO_PORT
);
272 ep
.udp
.dest
= htons(BFD_DEF_ECHO_PORT
);
273 ep
.udp
.len
= htons(UDP_ECHO_PKT_LEN
);
274 #endif /* BFD_LINUX */
276 ep
.udp
.uh_sport
= htons(BFD_DEF_ECHO_PORT
);
277 ep
.udp
.uh_dport
= htons(BFD_DEF_ECHO_PORT
);
278 ep
.udp
.uh_ulen
= htons(UDP_ECHO_PKT_LEN
);
281 /* Construct Echo packet information */
282 ep
.data
.ver
= BFD_ECHO_VERSION
;
283 ep
.data
.len
= BFD_ECHO_PKT_LEN
;
284 ep
.data
.my_discr
= htonl(bfd
->discrs
.my_discr
);
287 #endif /* BFD_LINUX */
291 udp4_checksum(&ep
.ip
, (uint8_t *)&ep
.udp
,
294 memcpy(pkt
, &ep
, sizeof(ep
));
297 void ptm_bfd_echo_snd(struct bfd_session
*bfd
)
299 struct bfd_raw_echo_pkt
*ep
;
300 bool use_layer2
= false;
303 uint16_t port
= htons(BFD_DEF_ECHO_PORT
);
305 if (!BFD_CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_ECHO_ACTIVE
)) {
306 ptm_bfd_echo_pkt_create(bfd
);
307 BFD_SET_FLAG(bfd
->flags
, BFD_SESS_FLAG_ECHO_ACTIVE
);
309 /* just update the checksum and ip Id */
310 ep
= (struct bfd_raw_echo_pkt
*)(bfd
->echo_pkt
+ ETH_HDR_LEN
);
312 ep
->ip
.id
= htons(ptm_bfd_gen_IP_ID(bfd
));
314 ep
->ip
.check
= checksum((uint16_t *)&ep
->ip
, IP_HDR_LEN
);
315 #endif /* BFD_LINUX */
317 ep
->ip
.ip_id
= htons(ptm_bfd_gen_IP_ID(bfd
));
319 ep
->ip
.ip_sum
= checksum((uint16_t *)&ep
->ip
, IP_HDR_LEN
);
325 pktlen
= BFD_ECHO_PKT_TOT_LEN
;
327 pkt
= &bfd
->echo_pkt
[ETH_HDR_LEN
+ IP_HDR_LEN
+ UDP_HDR_LEN
];
328 pktlen
= BFD_ECHO_PKT_TOT_LEN
329 - (ETH_HDR_LEN
+ IP_HDR_LEN
+ UDP_HDR_LEN
);
332 if (_ptm_bfd_send(bfd
, use_layer2
, &port
, pkt
, pktlen
) != 0) {
333 log_debug("echo-packet: send failure: %s", strerror(errno
));
337 bfd
->stats
.tx_echo_pkt
++;
340 static int ptm_bfd_echo_loopback(uint8_t *pkt
, int pkt_len
, struct sockaddr
*ss
,
344 struct bfd_raw_echo_pkt
*ep
=
345 (struct bfd_raw_echo_pkt
*)(pkt
+ ETH_HDR_LEN
);
346 uint8_t temp_mac
[ETHERNET_ADDRESS_LENGTH
];
348 struct ethhdr
*eth
= (struct ethhdr
*)pkt
;
350 /* swap the mac addresses */
351 memcpy(temp_mac
, eth
->h_source
, ETHERNET_ADDRESS_LENGTH
);
352 memcpy(eth
->h_source
, eth
->h_dest
, ETHERNET_ADDRESS_LENGTH
);
353 memcpy(eth
->h_dest
, temp_mac
, ETHERNET_ADDRESS_LENGTH
);
355 /* swap ip addresses */
356 temp_ip
= ep
->ip
.saddr
;
357 ep
->ip
.saddr
= ep
->ip
.daddr
;
358 ep
->ip
.daddr
= temp_ip
;
360 ep
->ip
.ttl
= ep
->ip
.ttl
- 1;
362 ep
->ip
.check
= checksum((uint16_t *)ep
, IP_HDR_LEN
);
363 #endif /* BFD_LINUX */
364 #ifdef BFD_BSD_FILTER
365 struct bfd_raw_echo_pkt_t
*ep
=
366 (struct bfd_raw_echo_pkt
*)(pkt
+ ETH_HDR_LEN
);
367 uint8_t temp_mac
[ETHERNET_ADDRESS_LENGTH
];
368 struct in_addr temp_ip
;
369 struct ether_header
*ether
= (struct ether_header
*)pkt
;
372 * TODO: this is not yet implemented and requires BPF code for
373 * OmniOS, NetBSD and FreeBSD9.
376 /* swap the mac addresses */
377 memcpy(temp_mac
, ether
->ether_shost
, ETHERNET_ADDRESS_LENGTH
);
378 memcpy(ether
->ether_shost
, ether
->ether_dhost
, ETHERNET_ADDRESS_LENGTH
);
379 memcpy(ether
->ether_dhost
, temp_mac
, ETHERNET_ADDRESS_LENGTH
);
381 /* swap ip addresses */
382 temp_ip
= ep
->ip
.ip_src
;
383 ep
->ip
.ip_src
= ep
->ip
.ip_dst
;
384 ep
->ip
.ip_dst
= temp_ip
;
386 ep
->ip
.ip_ttl
= ep
->ip
.ip_ttl
- 1;
388 ep
->ip
.ip_sum
= checksum((uint16_t *)ep
, IP_HDR_LEN
);
389 #endif /* BFD_BSD_FILTER */
391 if (sendto(bglobal
.bg_echo
, pkt
, pkt_len
, 0, ss
, sslen
) < 0) {
392 log_debug("echo-loopback: send failure: %s", strerror(errno
));
399 static void ptm_bfd_vxlan_pkt_snd(struct bfd_session
*bfd
400 __attribute__((__unused__
)),
401 int fbit
__attribute__((__unused__
)))
403 #if 0 /* TODO: VxLAN support. */
404 struct bfd_raw_ctrl_pkt cp
;
405 uint8_t vxlan_pkt
[BFD_VXLAN_PKT_TOT_LEN
];
406 uint8_t *pkt
= vxlan_pkt
;
407 struct sockaddr_in sin
;
408 struct vxlan_hdr
*vhdr
;
410 memset(vxlan_pkt
, 0, sizeof(vxlan_pkt
));
411 memset(&cp
, 0, sizeof(cp
));
413 /* Construct VxLAN header information */
414 vhdr
= (struct vxlan_hdr
*)pkt
;
415 vhdr
->flags
= htonl(0x08000000);
416 vhdr
->vnid
= htonl(bfd
->vxlan_info
.vnid
<< 8);
417 pkt
+= VXLAN_HDR_LEN
;
419 /* Construct ethernet header information */
420 memcpy(pkt
, bfd
->vxlan_info
.peer_dst_mac
, ETHERNET_ADDRESS_LENGTH
);
421 pkt
= pkt
+ ETHERNET_ADDRESS_LENGTH
;
422 memcpy(pkt
, bfd
->vxlan_info
.local_dst_mac
, ETHERNET_ADDRESS_LENGTH
);
423 pkt
= pkt
+ ETHERNET_ADDRESS_LENGTH
;
424 pkt
[0] = ETH_P_IP
/ 256;
425 pkt
[1] = ETH_P_IP
% 256;
428 /* Construct IP header information */
432 cp
.ip
.tot_len
= htons(IP_CTRL_PKT_LEN
);
433 cp
.ip
.id
= ptm_bfd_gen_IP_ID(bfd
);
435 cp
.ip
.ttl
= BFD_TTL_VAL
;
436 cp
.ip
.protocol
= IPPROTO_UDP
;
437 cp
.ip
.daddr
= bfd
->vxlan_info
.peer_dst_ip
.s_addr
;
438 cp
.ip
.saddr
= bfd
->vxlan_info
.local_dst_ip
.s_addr
;
439 cp
.ip
.check
= checksum((uint16_t *)&cp
.ip
, IP_HDR_LEN
);
441 /* Construct UDP header information */
442 cp
.udp
.source
= htons(BFD_DEFDESTPORT
);
443 cp
.udp
.dest
= htons(BFD_DEFDESTPORT
);
444 cp
.udp
.len
= htons(UDP_CTRL_PKT_LEN
);
446 /* Construct BFD control packet information */
447 cp
.data
.diag
= bfd
->local_diag
;
448 BFD_SETVER(cp
.data
.diag
, BFD_VERSION
);
449 BFD_SETSTATE(cp
.data
.flags
, bfd
->ses_state
);
450 BFD_SETDEMANDBIT(cp
.data
.flags
, BFD_DEF_DEMAND
);
451 BFD_SETPBIT(cp
.data
.flags
, bfd
->polling
);
452 BFD_SETFBIT(cp
.data
.flags
, fbit
);
453 cp
.data
.detect_mult
= bfd
->detect_mult
;
454 cp
.data
.len
= BFD_PKT_LEN
;
455 cp
.data
.discrs
.my_discr
= htonl(bfd
->discrs
.my_discr
);
456 cp
.data
.discrs
.remote_discr
= htonl(bfd
->discrs
.remote_discr
);
457 cp
.data
.timers
.desired_min_tx
= htonl(bfd
->timers
.desired_min_tx
);
458 cp
.data
.timers
.required_min_rx
= htonl(bfd
->timers
.required_min_rx
);
459 cp
.data
.timers
.required_min_echo
= htonl(bfd
->timers
.required_min_echo
);
462 udp4_checksum(&cp
.ip
, (uint8_t *)&cp
.udp
, UDP_CTRL_PKT_LEN
);
464 memcpy(pkt
, &cp
, sizeof(cp
));
465 sin
.sin_family
= AF_INET
;
466 sin
.sin_addr
= bfd
->shop
.peer
.sa_sin
.sin_addr
;
467 sin
.sin_port
= htons(4789);
469 if (sendto(bfd
->sock
, vxlan_pkt
, BFD_VXLAN_PKT_TOT_LEN
, 0,
470 (struct sockaddr
*)&sin
, sizeof(struct sockaddr_in
))
472 ERRLOG("Error sending vxlan bfd pkt: %s", strerror(errno
));
474 bfd
->stats
.tx_ctrl_pkt
++;
479 static int ptm_bfd_process_echo_pkt(int s
)
481 uint32_t my_discr
= 0;
482 struct sockaddr_storage ss
;
483 socklen_t sslen
= sizeof(ss
);
484 uint8_t rx_pkt
[BFD_RX_BUF_LEN
];
485 ssize_t pkt_len
= sizeof(rx_pkt
);
486 struct bfd_session
*bfd
;
488 struct bfd_raw_echo_pkt
*ep
;
491 * valgrind: memset() ss so valgrind doesn't complain about
492 * uninitialized memory.
494 memset(&ss
, 0, sizeof(ss
));
495 pkt_len
= recvfrom(s
, rx_pkt
, sizeof(rx_pkt
), MSG_DONTWAIT
,
496 (struct sockaddr
*)&ss
, &sslen
);
499 log_error("echo-packet: read failure: %s",
505 /* Check if we have at least the basic headers to send back. */
506 if (pkt_len
< BFD_ECHO_PKT_TOT_LEN
) {
507 log_debug("echo-packet: too short (got %ld, expected %d)",
508 pkt_len
, BFD_ECHO_PKT_TOT_LEN
);
512 ep
= (struct bfd_raw_echo_pkt
*)(rx_pkt
+ ETH_HDR_LEN
);
513 /* if TTL = 255, assume that the received echo packet has
516 if (ep
->ip
.ttl
== BFD_TTL_VAL
)
517 return ptm_bfd_echo_loopback(rx_pkt
, pkt_len
,
518 (struct sockaddr
*)&ss
,
519 sizeof(struct sockaddr_ll
));
521 my_discr
= ntohl(ep
->data
.my_discr
);
522 if (ep
->data
.my_discr
== 0) {
523 log_debug("echo-packet: 'my discriminator' is zero");
526 #endif /* BFD_LINUX */
532 * bsd_echo_sock_read() already treats invalid TTL values and
533 * zeroed discriminators.
535 rv
= bsd_echo_sock_read(s
, rx_pkt
, &pkt_len
, &ss
, &sslen
, &ttl
,
540 if (ttl
== BFD_TTL_VAL
)
541 return ptm_bfd_echo_loopback(rx_pkt
, pkt_len
,
542 (struct sockaddr
*)&ss
, sslen
);
545 /* Your discriminator not zero - use it to find session */
546 bfd
= bfd_id_lookup(my_discr
);
548 log_debug("echo-packet: no matching session (id:%u)", my_discr
);
552 if (!BFD_CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_ECHO_ACTIVE
)) {
553 log_debug("echo-packet: echo disabled [%s]", my_discr
,
558 bfd
->stats
.rx_echo_pkt
++;
560 /* Compute detect time */
561 bfd
->echo_detect_TO
= bfd
->remote_detect_mult
* bfd
->echo_xmt_TO
;
563 /* Update echo receive timeout. */
564 bfd_echo_recvtimer_update(bfd
);
569 void ptm_bfd_snd(struct bfd_session
*bfd
, int fbit
)
573 /* if the BFD session is for VxLAN tunnel, then construct and
574 * send bfd raw packet
576 if (BFD_CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_VXLAN
)) {
577 ptm_bfd_vxlan_pkt_snd(bfd
, fbit
);
581 /* Set fields according to section 6.5.7 */
582 cp
.diag
= bfd
->local_diag
;
583 BFD_SETVER(cp
.diag
, BFD_VERSION
);
585 BFD_SETSTATE(cp
.flags
, bfd
->ses_state
);
586 BFD_SETDEMANDBIT(cp
.flags
, BFD_DEF_DEMAND
);
587 BFD_SETPBIT(cp
.flags
, bfd
->polling
);
588 BFD_SETFBIT(cp
.flags
, fbit
);
589 cp
.detect_mult
= bfd
->detect_mult
;
590 cp
.len
= BFD_PKT_LEN
;
591 cp
.discrs
.my_discr
= htonl(bfd
->discrs
.my_discr
);
592 cp
.discrs
.remote_discr
= htonl(bfd
->discrs
.remote_discr
);
594 cp
.timers
.desired_min_tx
=
595 htonl(bfd
->new_timers
.desired_min_tx
);
596 cp
.timers
.required_min_rx
=
597 htonl(bfd
->new_timers
.required_min_rx
);
599 cp
.timers
.desired_min_tx
= htonl(bfd
->timers
.desired_min_tx
);
600 cp
.timers
.required_min_rx
= htonl(bfd
->timers
.required_min_rx
);
602 cp
.timers
.required_min_echo
= htonl(bfd
->timers
.required_min_echo
);
604 if (_ptm_bfd_send(bfd
, false, NULL
, &cp
, BFD_PKT_LEN
) != 0)
607 bfd
->stats
.tx_ctrl_pkt
++;
610 #if 0 /* TODO VxLAN Support */
611 static struct bfd_pkt
*
612 ptm_bfd_process_vxlan_pkt(int s
, ptm_sockevent_e se
, void *udata
, int *ifindex
,
613 struct sockaddr_in
*sin
,
614 struct bfd_session_vxlan_info_t
*vxlan_info
,
615 uint8_t *rx_pkt
, int *mlen
)
617 struct sockaddr_ll sll
;
618 uint32_t from_len
= sizeof(struct sockaddr_ll
);
619 struct bfd_raw_ctrl_pkt
*cp
;
620 uint8_t *pkt
= rx_pkt
;
622 struct ethhdr
*inner_ethh
;
624 *mlen
= recvfrom(s
, rx_pkt
, BFD_RX_BUF_LEN
, MSG_DONTWAIT
,
625 (struct sockaddr
*)&sll
, &from_len
);
629 ERRLOG("Error receiving from BFD Vxlan socket %d: %m",
634 iph
= (struct iphdr
*)(pkt
+ ETH_HDR_LEN
);
635 pkt
= pkt
+ ETH_HDR_LEN
+ IP_HDR_LEN
+ UDP_HDR_LEN
;
636 vxlan_info
->vnid
= ntohl(*((int *)(pkt
+ 4)));
637 vxlan_info
->vnid
= vxlan_info
->vnid
>> 8;
639 pkt
= pkt
+ VXLAN_HDR_LEN
;
640 inner_ethh
= (struct ethhdr
*)pkt
;
642 cp
= (struct bfd_raw_ctrl_pkt
*)(pkt
+ ETH_HDR_LEN
);
644 /* Discard the non BFD packets */
645 if (ntohs(cp
->udp
.dest
) != BFD_DEFDESTPORT
)
648 *ifindex
= sll
.sll_ifindex
;
649 sin
->sin_addr
.s_addr
= iph
->saddr
;
650 sin
->sin_port
= ntohs(cp
->udp
.dest
);
652 vxlan_info
->local_dst_ip
.s_addr
= cp
->ip
.daddr
;
653 memcpy(vxlan_info
->local_dst_mac
, inner_ethh
->h_dest
,
654 ETHERNET_ADDRESS_LENGTH
);
661 ptm_bfd_validate_vxlan_pkt(struct bfd_session
*bfd
,
662 struct bfd_session_vxlan_info
*vxlan_info
)
664 if (bfd
->vxlan_info
.check_tnl_key
&& (vxlan_info
->vnid
!= 0)) {
665 log_error("vxlan-packet: vnid not zero: %d", vxlan_info
->vnid
);
669 if (bfd
->vxlan_info
.local_dst_ip
.s_addr
670 != vxlan_info
->local_dst_ip
.s_addr
) {
671 log_error("vxlan-packet: wrong inner destination",
672 inet_ntoa(vxlan_info
->local_dst_ip
));
676 if (memcmp(bfd
->vxlan_info
.local_dst_mac
, vxlan_info
->local_dst_mac
,
677 ETHERNET_ADDRESS_LENGTH
)) {
679 "vxlan-packet: wrong inner mac: %02x:%02x:%02x:%02x:%02x:%02x",
680 vxlan_info
->local_dst_mac
[0],
681 vxlan_info
->local_dst_mac
[1],
682 vxlan_info
->local_dst_mac
[2],
683 vxlan_info
->local_dst_mac
[3],
684 vxlan_info
->local_dst_mac
[4],
685 vxlan_info
->local_dst_mac
[5]);
692 static ssize_t
bfd_recv_ipv4(int sd
, bool is_mhop
, char *port
, size_t portlen
,
693 char *vrfname
, size_t vrfnamelen
,
694 struct sockaddr_any
*local
,
695 struct sockaddr_any
*peer
)
700 struct sockaddr_in msgaddr
;
701 struct msghdr msghdr
;
703 uint8_t cmsgbuf
[255];
705 /* Prepare the recvmsg params. */
706 iov
[0].iov_base
= msgbuf
;
707 iov
[0].iov_len
= sizeof(msgbuf
);
709 memset(&msghdr
, 0, sizeof(msghdr
));
710 msghdr
.msg_name
= &msgaddr
;
711 msghdr
.msg_namelen
= sizeof(msgaddr
);
712 msghdr
.msg_iov
= iov
;
713 msghdr
.msg_iovlen
= 1;
714 msghdr
.msg_control
= cmsgbuf
;
715 msghdr
.msg_controllen
= sizeof(cmsgbuf
);
717 mlen
= recvmsg(sd
, &msghdr
, MSG_DONTWAIT
);
720 log_error("ipv4-recv: recv failed: %s",
726 /* Get source address */
727 peer
->sa_sin
= *((struct sockaddr_in
*)(msghdr
.msg_name
));
729 /* Get and check TTL */
730 for (cm
= CMSG_FIRSTHDR(&msghdr
); cm
!= NULL
;
731 cm
= CMSG_NXTHDR(&msghdr
, cm
)) {
732 if (cm
->cmsg_level
!= IPPROTO_IP
)
735 switch (cm
->cmsg_type
) {
740 memcpy(&ttl
, CMSG_DATA(cm
), sizeof(ttl
));
741 if ((is_mhop
== false) && (ttl
!= BFD_TTL_VAL
)) {
743 "ipv4-recv: invalid TTL from %s (expected %d, got %d flags %d)",
744 satostr(peer
), ttl
, BFD_TTL_VAL
,
752 struct in_pktinfo
*pi
=
753 (struct in_pktinfo
*)CMSG_DATA(cm
);
758 local
->sa_sin
.sin_family
= AF_INET
;
759 local
->sa_sin
.sin_addr
= pi
->ipi_addr
;
760 fetch_portname_from_ifindex(pi
->ipi_ifindex
, port
,
764 #endif /* BFD_LINUX */
769 memcpy(&ttl
, CMSG_DATA(cm
), sizeof(ttl
));
770 if ((is_mhop
== false) && (ttl
!= BFD_TTL_VAL
)) {
772 "ipv4-recv: invalid TTL from %s (expected %d, got %d flags %d)",
773 satostr(peer
), ttl
, BFD_TTL_VAL
,
780 case IP_RECVDSTADDR
: {
783 memcpy(&ia
, CMSG_DATA(cm
), sizeof(ia
));
784 local
->sa_sin
.sin_family
= AF_INET
;
785 local
->sa_sin
.sin_addr
= ia
;
792 * On *BSDs we expect to land here when skipping
793 * the IP_RECVIF header. It will be handled by
794 * getsockopt_ifindex() below.
801 /* OS agnostic way of getting interface name. */
803 ifindex
= getsockopt_ifindex(AF_INET
, &msghdr
);
805 fetch_portname_from_ifindex(ifindex
, port
, portlen
);
811 ssize_t
bfd_recv_ipv6(int sd
, bool is_mhop
, char *port
, size_t portlen
,
812 char *vrfname
, size_t vrfnamelen
,
813 struct sockaddr_any
*local
, struct sockaddr_any
*peer
)
816 struct in6_pktinfo
*pi6
= NULL
;
820 struct sockaddr_in6 msgaddr6
;
821 struct msghdr msghdr6
;
823 uint8_t cmsgbuf6
[255];
825 /* Prepare the recvmsg params. */
826 iov
[0].iov_base
= msgbuf
;
827 iov
[0].iov_len
= sizeof(msgbuf
);
829 memset(&msghdr6
, 0, sizeof(msghdr6
));
830 msghdr6
.msg_name
= &msgaddr6
;
831 msghdr6
.msg_namelen
= sizeof(msgaddr6
);
832 msghdr6
.msg_iov
= iov
;
833 msghdr6
.msg_iovlen
= 1;
834 msghdr6
.msg_control
= cmsgbuf6
;
835 msghdr6
.msg_controllen
= sizeof(cmsgbuf6
);
837 mlen
= recvmsg(sd
, &msghdr6
, MSG_DONTWAIT
);
840 log_error("ipv6-recv: recv failed: %s",
846 /* Get source address */
847 peer
->sa_sin6
= *((struct sockaddr_in6
*)(msghdr6
.msg_name
));
849 /* Get and check TTL */
850 for (cm
= CMSG_FIRSTHDR(&msghdr6
); cm
!= NULL
;
851 cm
= CMSG_NXTHDR(&msghdr6
, cm
)) {
852 if (cm
->cmsg_level
!= IPPROTO_IPV6
)
855 if (cm
->cmsg_type
== IPV6_HOPLIMIT
) {
856 memcpy(&ttlval
, CMSG_DATA(cm
), sizeof(ttlval
));
857 if ((is_mhop
== false) && (ttlval
!= BFD_TTL_VAL
)) {
859 "ipv6-recv: invalid TTL from %s (expected %d, got %d flags %d)",
860 satostr(peer
), ttlval
, BFD_TTL_VAL
,
864 } else if (cm
->cmsg_type
== IPV6_PKTINFO
) {
865 pi6
= (struct in6_pktinfo
*)CMSG_DATA(cm
);
867 local
->sa_sin
.sin_family
= AF_INET6
;
868 local
->sa_sin6
.sin6_addr
= pi6
->ipi6_addr
;
869 fetch_portname_from_ifindex(pi6
->ipi6_ifindex
,
871 ifindex
= pi6
->ipi6_ifindex
;
876 /* Set scope ID for link local addresses. */
877 if (IN6_IS_ADDR_LINKLOCAL(&peer
->sa_sin6
.sin6_addr
))
878 peer
->sa_sin6
.sin6_scope_id
= ifindex
;
879 if (IN6_IS_ADDR_LINKLOCAL(&local
->sa_sin6
.sin6_addr
))
880 local
->sa_sin6
.sin6_scope_id
= ifindex
;
885 static void bfd_sd_reschedule(int sd
)
887 if (sd
== bglobal
.bg_shop
) {
888 bglobal
.bg_ev
[0] = NULL
;
889 thread_add_read(master
, bfd_recv_cb
, NULL
, bglobal
.bg_shop
,
891 } else if (sd
== bglobal
.bg_mhop
) {
892 bglobal
.bg_ev
[1] = NULL
;
893 thread_add_read(master
, bfd_recv_cb
, NULL
, bglobal
.bg_mhop
,
895 } else if (sd
== bglobal
.bg_shop6
) {
896 bglobal
.bg_ev
[2] = NULL
;
897 thread_add_read(master
, bfd_recv_cb
, NULL
, bglobal
.bg_shop6
,
899 } else if (sd
== bglobal
.bg_mhop6
) {
900 bglobal
.bg_ev
[3] = NULL
;
901 thread_add_read(master
, bfd_recv_cb
, NULL
, bglobal
.bg_mhop6
,
903 } else if (sd
== bglobal
.bg_echo
) {
904 bglobal
.bg_ev
[4] = NULL
;
905 thread_add_read(master
, bfd_recv_cb
, NULL
, bglobal
.bg_echo
,
907 } else if (sd
== bglobal
.bg_vxlan
) {
908 bglobal
.bg_ev
[5] = NULL
;
909 thread_add_read(master
, bfd_recv_cb
, NULL
, bglobal
.bg_vxlan
,
914 static void cp_debug(bool mhop
, struct sockaddr_any
*peer
,
915 struct sockaddr_any
*local
, const char *port
,
916 const char *vrf
, const char *fmt
, ...)
918 char buf
[512], peerstr
[128], localstr
[128], portstr
[64], vrfstr
[64];
921 if (peer
->sa_sin
.sin_family
)
922 snprintf(peerstr
, sizeof(peerstr
), " peer:%s", satostr(peer
));
926 if (local
->sa_sin
.sin_family
)
927 snprintf(localstr
, sizeof(localstr
), " local:%s",
933 snprintf(portstr
, sizeof(portstr
), " port:%s", port
);
938 snprintf(vrfstr
, sizeof(vrfstr
), " vrf:%s", port
);
943 vsnprintf(buf
, sizeof(buf
), fmt
, vl
);
946 log_debug("control-packet: %s [mhop:%s%s%s%s%s]", buf
,
947 mhop
? "yes" : "no", peerstr
, localstr
, portstr
, vrfstr
);
950 int bfd_recv_cb(struct thread
*t
)
952 int sd
= THREAD_FD(t
);
953 struct bfd_session
*bfd
;
955 bool is_mhop
, is_vxlan
;
957 uint32_t oldEchoXmt_TO
, oldXmtTime
;
958 struct sockaddr_any local
, peer
;
959 char port
[MAXNAMELEN
+ 1], vrfname
[MAXNAMELEN
+ 1];
960 struct bfd_session_vxlan_info vxlan_info
;
962 /* Schedule next read. */
963 bfd_sd_reschedule(sd
);
965 /* Handle echo packets. */
966 if (sd
== bglobal
.bg_echo
) {
967 ptm_bfd_process_echo_pkt(sd
);
971 /* Sanitize input/output. */
972 memset(port
, 0, sizeof(port
));
973 memset(vrfname
, 0, sizeof(vrfname
));
974 memset(&local
, 0, sizeof(local
));
975 memset(&peer
, 0, sizeof(peer
));
977 /* Handle control packets. */
978 is_mhop
= is_vxlan
= false;
979 if (sd
== bglobal
.bg_shop
|| sd
== bglobal
.bg_mhop
) {
980 is_mhop
= sd
== bglobal
.bg_mhop
;
981 mlen
= bfd_recv_ipv4(sd
, is_mhop
, port
, sizeof(port
), vrfname
,
982 sizeof(vrfname
), &local
, &peer
);
983 } else if (sd
== bglobal
.bg_shop6
|| sd
== bglobal
.bg_mhop6
) {
984 is_mhop
= sd
== bglobal
.bg_mhop6
;
985 mlen
= bfd_recv_ipv6(sd
, is_mhop
, port
, sizeof(port
), vrfname
,
986 sizeof(vrfname
), &local
, &peer
);
988 #if 0 /* TODO vxlan handling */
989 cp
= ptm_bfd_process_vxlan_pkt(s
, se
, udata
, &local_ifindex
,
990 &sin
, &vxlan_info
, rx_pkt
, &mlen
);
995 /* keep in network-byte order */
996 peer
.ip4_addr
.s_addr
= sin
.sin_addr
.s_addr
;
997 peer
.family
= AF_INET
;
998 strcpy(peer_addr
, inet_ntoa(sin
.sin_addr
));
1001 /* Implement RFC 5880 6.8.6 */
1002 if (mlen
< BFD_PKT_LEN
) {
1003 cp_debug(is_mhop
, &peer
, &local
, port
, vrfname
,
1004 "too small (%ld bytes)", mlen
);
1009 * Parse the control header for inconsistencies:
1010 * - Invalid version;
1011 * - Bad multiplier configuration;
1013 * - Invalid discriminator;
1015 cp
= (struct bfd_pkt
*)(msgbuf
);
1016 if (BFD_GETVER(cp
->diag
) != BFD_VERSION
) {
1017 cp_debug(is_mhop
, &peer
, &local
, port
, vrfname
,
1018 "bad version %d", BFD_GETVER(cp
->diag
));
1022 if (cp
->detect_mult
== 0) {
1023 cp_debug(is_mhop
, &peer
, &local
, port
, vrfname
,
1024 "detect multiplier set to zero");
1028 if ((cp
->len
< BFD_PKT_LEN
) || (cp
->len
> mlen
)) {
1029 cp_debug(is_mhop
, &peer
, &local
, port
, vrfname
, "too small");
1033 if (cp
->discrs
.my_discr
== 0) {
1034 cp_debug(is_mhop
, &peer
, &local
, port
, vrfname
,
1035 "'my discriminator' is zero");
1039 /* Find the session that this packet belongs. */
1040 bfd
= ptm_bfd_sess_find(cp
, port
, &peer
, &local
, vrfname
, is_mhop
);
1042 cp_debug(is_mhop
, &peer
, &local
, port
, vrfname
,
1043 "no session found");
1047 /* Handle VxLAN cases. */
1048 if (is_vxlan
&& !ptm_bfd_validate_vxlan_pkt(bfd
, &vxlan_info
))
1051 bfd
->stats
.rx_ctrl_pkt
++;
1054 * Multi hop: validate packet TTL.
1055 * Single hop: set local address that received the packet.
1058 if ((BFD_TTL_VAL
- bfd
->mh_ttl
) > BFD_TTL_VAL
) {
1059 cp_debug(is_mhop
, &peer
, &local
, port
, vrfname
,
1060 "exceeded max hop count (expected %d, got %d)",
1061 bfd
->mh_ttl
, BFD_TTL_VAL
);
1064 } else if (bfd
->local_ip
.sa_sin
.sin_family
== AF_UNSPEC
) {
1065 bfd
->local_ip
= local
;
1069 * If no interface was detected, save the interface where the
1072 if (bfd
->ifindex
== 0)
1073 bfd
->ifindex
= ptm_bfd_fetch_ifindex(port
);
1075 /* Log remote discriminator changes. */
1076 if ((bfd
->discrs
.remote_discr
!= 0)
1077 && (bfd
->discrs
.remote_discr
!= ntohl(cp
->discrs
.my_discr
)))
1078 cp_debug(is_mhop
, &peer
, &local
, port
, vrfname
,
1079 "remote discriminator mismatch (expected %d, got %d)",
1080 bfd
->discrs
.remote_discr
, ntohl(cp
->discrs
.my_discr
));
1082 bfd
->discrs
.remote_discr
= ntohl(cp
->discrs
.my_discr
);
1084 /* If received the Final bit, the new values should take effect */
1085 if (bfd
->polling
&& BFD_GETFBIT(cp
->flags
)) {
1086 bfd
->timers
.desired_min_tx
= bfd
->new_timers
.desired_min_tx
;
1087 bfd
->timers
.required_min_rx
= bfd
->new_timers
.required_min_rx
;
1088 bfd
->new_timers
.desired_min_tx
= 0;
1089 bfd
->new_timers
.required_min_rx
= 0;
1093 if (!bfd
->demand_mode
) {
1094 /* Compute detect time */
1095 bfd
->detect_TO
= cp
->detect_mult
1096 * ((bfd
->timers
.required_min_rx
1097 > ntohl(cp
->timers
.desired_min_tx
))
1098 ? bfd
->timers
.required_min_rx
1099 : ntohl(cp
->timers
.desired_min_tx
));
1100 bfd
->remote_detect_mult
= cp
->detect_mult
;
1102 cp_debug(is_mhop
, &peer
, &local
, port
, vrfname
,
1103 "unsupported demand mode");
1105 /* Save remote diagnostics before state switch. */
1106 bfd
->remote_diag
= cp
->diag
& BFD_DIAGMASK
;
1108 /* State switch from section 6.8.6 */
1109 if (BFD_GETSTATE(cp
->flags
) == PTM_BFD_ADM_DOWN
) {
1110 if (bfd
->ses_state
!= PTM_BFD_DOWN
)
1111 ptm_bfd_ses_dn(bfd
, BFD_DIAGNEIGHDOWN
);
1113 switch (bfd
->ses_state
) {
1114 case (PTM_BFD_DOWN
):
1115 if (BFD_GETSTATE(cp
->flags
) == PTM_BFD_INIT
)
1116 ptm_bfd_ses_up(bfd
);
1117 else if (BFD_GETSTATE(cp
->flags
) == PTM_BFD_DOWN
)
1118 bfd
->ses_state
= PTM_BFD_INIT
;
1120 case (PTM_BFD_INIT
):
1121 if (BFD_GETSTATE(cp
->flags
) == PTM_BFD_INIT
1122 || BFD_GETSTATE(cp
->flags
) == PTM_BFD_UP
)
1123 ptm_bfd_ses_up(bfd
);
1126 if (BFD_GETSTATE(cp
->flags
) == PTM_BFD_DOWN
)
1127 ptm_bfd_ses_dn(bfd
, BFD_DIAGNEIGHDOWN
);
1133 * Handle echo packet status:
1134 * - Start echo packets if configured and permitted
1135 * (required_min_echo > 0);
1136 * - Stop echo packets if not allowed (required_min_echo == 0);
1137 * - Recalculate echo packet interval;
1139 if (BFD_CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_ECHO
)) {
1140 if (BFD_CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_ECHO_ACTIVE
)) {
1141 if (!ntohl(cp
->timers
.required_min_echo
)) {
1142 ptm_bfd_echo_stop(bfd
, 1);
1144 oldEchoXmt_TO
= bfd
->echo_xmt_TO
;
1146 bfd
->timers
.required_min_echo
;
1147 if (ntohl(cp
->timers
.required_min_echo
)
1149 bfd
->echo_xmt_TO
= ntohl(
1150 cp
->timers
.required_min_echo
);
1151 if (oldEchoXmt_TO
!= bfd
->echo_xmt_TO
)
1152 ptm_bfd_echo_start(bfd
);
1154 } else if (ntohl(cp
->timers
.required_min_echo
)) {
1155 bfd
->echo_xmt_TO
= bfd
->timers
.required_min_echo
;
1156 if (ntohl(cp
->timers
.required_min_echo
)
1159 ntohl(cp
->timers
.required_min_echo
);
1160 ptm_bfd_echo_start(bfd
);
1164 if (BFD_CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_ECHO_ACTIVE
)) {
1165 bfd
->echo_xmt_TO
= bfd
->timers
.required_min_echo
;
1166 if (ntohl(cp
->timers
.required_min_echo
) > bfd
->echo_xmt_TO
)
1167 bfd
->echo_xmt_TO
= ntohl(cp
->timers
.required_min_echo
);
1170 /* Calculate new transmit time */
1171 oldXmtTime
= bfd
->xmt_TO
;
1173 (bfd
->timers
.desired_min_tx
> ntohl(cp
->timers
.required_min_rx
))
1174 ? bfd
->timers
.desired_min_tx
1175 : ntohl(cp
->timers
.required_min_rx
);
1177 /* If transmit time has changed, and too much time until next xmt,
1180 if (BFD_GETPBIT(cp
->flags
)) {
1181 ptm_bfd_xmt_TO(bfd
, 1);
1182 } else if (oldXmtTime
!= bfd
->xmt_TO
) {
1183 /* XXX add some skid to this as well */
1184 ptm_bfd_start_xmt_timer(bfd
, false);
1187 /* Restart detection timer (packet received) */
1188 if (!bfd
->demand_mode
)
1189 bfd_recvtimer_update(bfd
);
1192 * Save the timers and state sent by the remote end
1193 * for debugging and statistics.
1195 if (BFD_GETFBIT(cp
->flags
)) {
1196 bfd
->remote_timers
.desired_min_tx
=
1197 ntohl(cp
->timers
.desired_min_tx
);
1198 bfd
->remote_timers
.required_min_rx
=
1199 ntohl(cp
->timers
.required_min_rx
);
1200 bfd
->remote_timers
.required_min_echo
=
1201 ntohl(cp
->timers
.required_min_echo
);
1203 control_notify_config(BCM_NOTIFY_CONFIG_UPDATE
, bfd
);
1218 int bp_set_ttl(int sd
, uint8_t value
)
1222 if (setsockopt(sd
, IPPROTO_IP
, IP_TTL
, &ttl
, sizeof(ttl
)) == -1) {
1223 log_warning("set-ttl: setsockopt(IP_TTL, %d): %s", value
,
1231 int bp_set_tos(int sd
, uint8_t value
)
1235 if (setsockopt(sd
, IPPROTO_IP
, IP_TOS
, &tos
, sizeof(tos
)) == -1) {
1236 log_warning("set-tos: setsockopt(IP_TOS, %d): %s", value
,
1244 static void bp_set_ipopts(int sd
)
1246 int rcvttl
= BFD_RCV_TTL_VAL
;
1248 if (bp_set_ttl(sd
, BFD_TTL_VAL
) != 0)
1249 log_fatal("set-ipopts: TTL configuration failed");
1251 if (setsockopt(sd
, IPPROTO_IP
, IP_RECVTTL
, &rcvttl
, sizeof(rcvttl
))
1253 log_fatal("set-ipopts: setsockopt(IP_RECVTTL, %d): %s", rcvttl
,
1257 int pktinfo
= BFD_PKT_INFO_VAL
;
1259 /* Figure out address and interface to do the peer matching. */
1260 if (setsockopt(sd
, IPPROTO_IP
, IP_PKTINFO
, &pktinfo
, sizeof(pktinfo
))
1262 log_fatal("set-ipopts: setsockopt(IP_PKTINFO, %d): %s", pktinfo
,
1264 #endif /* BFD_LINUX */
1268 /* Find out our address for peer matching. */
1269 if (setsockopt(sd
, IPPROTO_IP
, IP_RECVDSTADDR
, &yes
, sizeof(yes
)) == -1)
1270 log_fatal("set-ipopts: setsockopt(IP_RECVDSTADDR, %d): %s", yes
,
1273 /* Find out interface where the packet came in. */
1274 if (setsockopt_ifindex(AF_INET
, sd
, yes
) == -1)
1275 log_fatal("set-ipopts: setsockopt_ipv4_ifindex(%d): %s", yes
,
1277 #endif /* BFD_BSD */
1280 static void bp_bind_ip(int sd
, uint16_t port
)
1282 struct sockaddr_in sin
;
1284 memset(&sin
, 0, sizeof(sin
));
1285 sin
.sin_family
= AF_INET
;
1286 sin
.sin_addr
.s_addr
= htonl(INADDR_ANY
);
1287 sin
.sin_port
= htons(port
);
1288 if (bind(sd
, (struct sockaddr
*)&sin
, sizeof(sin
)) == -1)
1289 log_fatal("bind-ip: bind: %s", strerror(errno
));
1292 int bp_udp_shop(void)
1296 sd
= socket(AF_INET
, SOCK_DGRAM
, PF_UNSPEC
);
1298 log_fatal("udp-shop: socket: %s", strerror(errno
));
1301 bp_bind_ip(sd
, BFD_DEFDESTPORT
);
1306 int bp_udp_mhop(void)
1310 sd
= socket(AF_INET
, SOCK_DGRAM
, PF_UNSPEC
);
1312 log_fatal("udp-mhop: socket: %s", strerror(errno
));
1315 bp_bind_ip(sd
, BFD_DEF_MHOP_DEST_PORT
);
1320 int bp_peer_socket(struct bfd_peer_cfg
*bpc
)
1323 struct sockaddr_in sin
;
1324 static int srcPort
= BFD_SRCPORTINIT
;
1326 sd
= socket(AF_INET
, SOCK_DGRAM
, PF_UNSPEC
);
1328 log_error("ipv4-new: failed to create socket: %s",
1333 if (!bpc
->bpc_has_vxlan
) {
1334 /* Set TTL to 255 for all transmitted packets */
1335 if (bp_set_ttl(sd
, BFD_TTL_VAL
) != 0) {
1341 /* Set TOS to CS6 for all transmitted packets */
1342 if (bp_set_tos(sd
, BFD_TOS_VAL
) != 0) {
1347 /* dont bind-to-device incase of vxlan */
1348 if (!bpc
->bpc_has_vxlan
&& bpc
->bpc_has_localif
) {
1349 if (bp_bind_dev(sd
, bpc
->bpc_localif
) != 0) {
1353 } else if (bpc
->bpc_mhop
&& bpc
->bpc_has_vrfname
) {
1354 if (bp_bind_dev(sd
, bpc
->bpc_vrfname
) != 0) {
1360 /* Find an available source port in the proper range */
1361 memset(&sin
, 0, sizeof(sin
));
1362 sin
= bpc
->bpc_local
.sa_sin
;
1363 sin
.sin_family
= AF_INET
;
1364 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1365 sin
.sin_len
= sizeof(sin
);
1366 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1367 if (bpc
->bpc_mhop
|| bpc
->bpc_has_vxlan
)
1368 sin
.sin_addr
= bpc
->bpc_local
.sa_sin
.sin_addr
;
1370 sin
.sin_addr
.s_addr
= INADDR_ANY
;
1374 if ((++pcount
) > (BFD_SRCPORTMAX
- BFD_SRCPORTINIT
)) {
1375 /* Searched all ports, none available */
1376 log_error("ipv4-new: failed to bind port: %s",
1381 if (srcPort
>= BFD_SRCPORTMAX
)
1382 srcPort
= BFD_SRCPORTINIT
;
1383 sin
.sin_port
= htons(srcPort
++);
1384 } while (bind(sd
, (struct sockaddr
*)&sin
, sizeof(sin
)) < 0);
1394 int bp_peer_socketv6(struct bfd_peer_cfg
*bpc
)
1396 int sd
, pcount
, ifindex
;
1397 struct sockaddr_in6 sin6
;
1398 static int srcPort
= BFD_SRCPORTINIT
;
1400 sd
= socket(AF_INET6
, SOCK_DGRAM
, PF_UNSPEC
);
1402 log_error("ipv6-new: failed to create socket: %s",
1407 if (!bpc
->bpc_has_vxlan
) {
1408 /* Set TTL to 255 for all transmitted packets */
1409 if (bp_set_ttlv6(sd
, BFD_TTL_VAL
) != 0) {
1415 /* Set TOS to CS6 for all transmitted packets */
1416 if (bp_set_tosv6(sd
, BFD_TOS_VAL
) != 0) {
1421 /* Find an available source port in the proper range */
1422 memset(&sin6
, 0, sizeof(sin6
));
1423 sin6
.sin6_family
= AF_INET6
;
1424 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1425 sin6
.sin6_len
= sizeof(sin6
);
1426 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1427 sin6
= bpc
->bpc_local
.sa_sin6
;
1428 ifindex
= ptm_bfd_fetch_ifindex(bpc
->bpc_localif
);
1429 if (IN6_IS_ADDR_LINKLOCAL(&sin6
.sin6_addr
))
1430 sin6
.sin6_scope_id
= ifindex
;
1432 if (bpc
->bpc_has_localif
) {
1433 if (bp_bind_dev(sd
, bpc
->bpc_localif
) != 0) {
1437 } else if (bpc
->bpc_mhop
&& bpc
->bpc_has_vrfname
) {
1438 if (bp_bind_dev(sd
, bpc
->bpc_vrfname
) != 0) {
1446 if ((++pcount
) > (BFD_SRCPORTMAX
- BFD_SRCPORTINIT
)) {
1447 /* Searched all ports, none available */
1448 log_error("ipv6-new: failed to bind port: %s",
1453 if (srcPort
>= BFD_SRCPORTMAX
)
1454 srcPort
= BFD_SRCPORTINIT
;
1455 sin6
.sin6_port
= htons(srcPort
++);
1456 } while (bind(sd
, (struct sockaddr
*)&sin6
, sizeof(sin6
)) < 0);
1461 int bp_set_ttlv6(int sd
, uint8_t value
)
1465 if (setsockopt(sd
, IPPROTO_IPV6
, IPV6_UNICAST_HOPS
, &ttl
, sizeof(ttl
))
1467 log_warning("set-ttlv6: setsockopt(IPV6_UNICAST_HOPS, %d): %s",
1468 value
, strerror(errno
));
1475 int bp_set_tosv6(int sd
, uint8_t value
)
1479 if (setsockopt(sd
, IPPROTO_IPV6
, IPV6_TCLASS
, &tos
, sizeof(tos
))
1481 log_warning("set-tosv6: setsockopt(IPV6_TCLASS, %d): %s", value
,
1489 static void bp_set_ipv6opts(int sd
)
1491 int ipv6_pktinfo
= BFD_IPV6_PKT_INFO_VAL
;
1492 int ipv6_only
= BFD_IPV6_ONLY_VAL
;
1494 if (bp_set_ttlv6(sd
, BFD_TTL_VAL
) == -1)
1495 log_fatal("set-ipv6opts: setsockopt(IPV6_UNICAST_HOPS, %d): %s",
1496 BFD_TTL_VAL
, strerror(errno
));
1498 if (setsockopt_ipv6_hoplimit(sd
, BFD_RCV_TTL_VAL
) == -1)
1499 log_fatal("set-ipv6opts: setsockopt(IPV6_HOPLIMIT, %d): %s",
1500 BFD_RCV_TTL_VAL
, strerror(errno
));
1502 if (setsockopt_ipv6_pktinfo(sd
, ipv6_pktinfo
) == -1)
1503 log_fatal("set-ipv6opts: setsockopt(IPV6_PKTINFO, %d): %s",
1504 ipv6_pktinfo
, strerror(errno
));
1506 if (setsockopt(sd
, IPPROTO_IPV6
, IPV6_V6ONLY
, &ipv6_only
,
1509 log_fatal("set-ipv6opts: setsockopt(IPV6_V6ONLY, %d): %s",
1510 ipv6_only
, strerror(errno
));
1513 static void bp_bind_ipv6(int sd
, uint16_t port
)
1515 struct sockaddr_in6 sin6
;
1517 memset(&sin6
, 0, sizeof(sin6
));
1518 sin6
.sin6_family
= AF_INET6
;
1519 sin6
.sin6_addr
= in6addr_any
;
1520 sin6
.sin6_port
= htons(port
);
1521 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1522 sin6
.sin6_len
= sizeof(sin6
);
1523 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1524 if (bind(sd
, (struct sockaddr
*)&sin6
, sizeof(sin6
)) == -1)
1525 log_fatal("bind-ipv6: bind: %s", strerror(errno
));
1528 int bp_udp6_shop(void)
1532 sd
= socket(AF_INET6
, SOCK_DGRAM
, PF_UNSPEC
);
1534 log_fatal("udp6-shop: socket: %s", strerror(errno
));
1536 bp_set_ipv6opts(sd
);
1537 bp_bind_ipv6(sd
, BFD_DEFDESTPORT
);
1542 int bp_udp6_mhop(void)
1546 sd
= socket(AF_INET6
, SOCK_DGRAM
, PF_UNSPEC
);
1548 log_fatal("udp6-mhop: socket: %s", strerror(errno
));
1550 bp_set_ipv6opts(sd
);
1551 bp_bind_ipv6(sd
, BFD_DEF_MHOP_DEST_PORT
);