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
];
81 static struct iovec msgiov
= {&(msgbuf
[0]), sizeof(msgbuf
)};
82 static uint8_t cmsgbuf
[255];
84 static struct sockaddr_in msgaddr
;
85 static struct msghdr msghdr
= {(void *)&msgaddr
, sizeof(msgaddr
), &msgiov
, 1,
86 (void *)&cmsgbuf
, sizeof(cmsgbuf
), 0};
88 static uint8_t cmsgbuf6
[255];
90 static struct sockaddr_in6 msgaddr6
;
91 static struct msghdr msghdr6
= {(void *)&msgaddr6
, sizeof(msgaddr6
), &msgiov
, 1,
92 (void *)&cmsgbuf6
, sizeof(cmsgbuf6
), 0};
94 static int ttlval
= BFD_TTL_VAL
;
95 static int tosval
= BFD_TOS_VAL
;
96 static int rcvttl
= BFD_RCV_TTL_VAL
;
101 static uint16_t ptm_bfd_gen_IP_ID(struct bfd_session
*bfd
);
102 static void ptm_bfd_echo_pkt_create(struct bfd_session
*bfd
);
103 static int ptm_bfd_echo_loopback(uint8_t *pkt
, int pkt_len
, struct sockaddr
*ss
,
105 static void ptm_bfd_vxlan_pkt_snd(struct bfd_session
*bfd
, int fbit
);
106 static int ptm_bfd_process_echo_pkt(int s
);
108 ptm_bfd_validate_vxlan_pkt(struct bfd_session
*bfd
,
109 struct bfd_session_vxlan_info
*vxlan_info
);
111 static void bfd_sd_reschedule(int sd
);
112 static ssize_t
bfd_recv_ipv4(int sd
, bool is_mhop
, char *port
, size_t portlen
,
113 char *vrfname
, size_t vrfnamelen
,
114 struct sockaddr_any
*local
,
115 struct sockaddr_any
*peer
);
116 static ssize_t
bfd_recv_ipv6(int sd
, bool is_mhop
, char *port
, size_t portlen
,
117 char *vrfname
, size_t vrfnamelen
,
118 struct sockaddr_any
*local
,
119 struct sockaddr_any
*peer
);
121 /* socket related prototypes */
122 static void bp_set_ipopts(int sd
);
123 static void bp_bind_ip(int sd
, uint16_t port
);
124 static void bp_set_ipv6opts(int sd
);
125 static void bp_bind_ipv6(int sd
, uint16_t port
);
131 uint16_t checksum(uint16_t *buf
, int len
)
136 int size
= sizeof(uint16_t);
144 *(uint8_t *)(&csum
) = *(uint8_t *)buf
;
148 sum
= (sum
>> 16) + (sum
& 0xFFFF);
154 static uint16_t ptm_bfd_gen_IP_ID(struct bfd_session
*bfd
)
156 return (++bfd
->ip_id
);
159 static int _ptm_bfd_send(struct bfd_session
*bs
, bool use_layer2
,
160 uint16_t *port
, const void *data
, size_t datalen
)
163 struct sockaddr_in sin
;
164 struct sockaddr_in6 sin6
;
166 struct sockaddr_ll dll
;
167 #endif /* BFD_LINUX */
174 memset(&dll
, 0, sizeof(dll
));
175 dll
.sll_family
= AF_PACKET
;
176 dll
.sll_protocol
= htons(ETH_P_IP
);
177 memcpy(dll
.sll_addr
, bs
->peer_mac
, ETHERNET_ADDRESS_LENGTH
);
178 dll
.sll_halen
= htons(ETHERNET_ADDRESS_LENGTH
);
179 dll
.sll_ifindex
= bs
->ifindex
;
181 sd
= bglobal
.bg_echo
;
182 sa
= (struct sockaddr
*)&dll
;
186 * TODO: implement layer 2 send for *BSDs. This is
189 log_warning("packet-send: not implemented");
192 } else if (BFD_CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_IPV6
)) {
193 memset(&sin6
, 0, sizeof(sin6
));
194 sin6
.sin6_family
= AF_INET6
;
195 sin6
.sin6_addr
= bs
->shop
.peer
.sa_sin6
.sin6_addr
;
198 : (BFD_CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_MH
))
199 ? htons(BFD_DEF_MHOP_DEST_PORT
)
200 : htons(BFD_DEFDESTPORT
);
203 sa
= (struct sockaddr
*)&sin6
;
206 memset(&sin
, 0, sizeof(sin
));
207 sin
.sin_family
= AF_INET
;
208 sin
.sin_addr
= bs
->shop
.peer
.sa_sin
.sin_addr
;
211 : (BFD_CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_MH
))
212 ? htons(BFD_DEF_MHOP_DEST_PORT
)
213 : htons(BFD_DEFDESTPORT
);
216 sa
= (struct sockaddr
*)&sin
;
220 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
222 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
223 rv
= sendto(sd
, data
, datalen
, 0, sa
, slen
);
225 log_debug("packet-send: send failure: %s", strerror(errno
));
228 if (rv
< (ssize_t
)datalen
)
229 log_debug("packet-send: send partial", strerror(errno
));
234 static void ptm_bfd_echo_pkt_create(struct bfd_session
*bfd
)
236 struct bfd_raw_echo_pkt ep
;
237 uint8_t *pkt
= bfd
->echo_pkt
;
239 memset(&ep
, 0, sizeof(ep
));
240 memset(bfd
->echo_pkt
, 0, sizeof(bfd
->echo_pkt
));
242 /* Construct ethernet header information */
243 memcpy(pkt
, bfd
->peer_mac
, ETHERNET_ADDRESS_LENGTH
);
244 pkt
= pkt
+ ETHERNET_ADDRESS_LENGTH
;
245 memcpy(pkt
, bfd
->local_mac
, ETHERNET_ADDRESS_LENGTH
);
246 pkt
= pkt
+ ETHERNET_ADDRESS_LENGTH
;
248 pkt
[0] = ETH_P_IP
/ 256;
249 pkt
[1] = ETH_P_IP
% 256;
250 #endif /* BFD_LINUX */
252 pkt
[0] = ETHERTYPE_IP
/ 256;
253 pkt
[1] = ETHERTYPE_IP
% 256;
257 /* Construct IP header information */
262 ep
.ip
.tot_len
= htons(IP_ECHO_PKT_LEN
);
263 ep
.ip
.id
= htons(ptm_bfd_gen_IP_ID(bfd
));
265 ep
.ip
.ttl
= BFD_TTL_VAL
;
266 ep
.ip
.protocol
= IPPROTO_UDP
;
267 ep
.ip
.saddr
= bfd
->local_ip
.sa_sin
.sin_addr
.s_addr
;
268 ep
.ip
.daddr
= bfd
->shop
.peer
.sa_sin
.sin_addr
.s_addr
;
269 ep
.ip
.check
= checksum((uint16_t *)&ep
.ip
, IP_HDR_LEN
);
270 #endif /* BFD_LINUX */
275 ep
.ip
.ip_len
= htons(IP_ECHO_PKT_LEN
);
276 ep
.ip
.ip_id
= htons(ptm_bfd_gen_IP_ID(bfd
));
278 ep
.ip
.ip_ttl
= BFD_TTL_VAL
;
279 ep
.ip
.ip_p
= IPPROTO_UDP
;
280 ep
.ip
.ip_src
= bfd
->local_ip
.sa_sin
.sin_addr
;
281 ep
.ip
.ip_dst
= bfd
->shop
.peer
.sa_sin
.sin_addr
;
282 ep
.ip
.ip_sum
= checksum((uint16_t *)&ep
.ip
, IP_HDR_LEN
);
285 /* Construct UDP header information */
287 ep
.udp
.source
= htons(BFD_DEF_ECHO_PORT
);
288 ep
.udp
.dest
= htons(BFD_DEF_ECHO_PORT
);
289 ep
.udp
.len
= htons(UDP_ECHO_PKT_LEN
);
290 #endif /* BFD_LINUX */
292 ep
.udp
.uh_sport
= htons(BFD_DEF_ECHO_PORT
);
293 ep
.udp
.uh_dport
= htons(BFD_DEF_ECHO_PORT
);
294 ep
.udp
.uh_ulen
= htons(UDP_ECHO_PKT_LEN
);
297 /* Construct Echo packet information */
298 ep
.data
.ver
= BFD_ECHO_VERSION
;
299 ep
.data
.len
= BFD_ECHO_PKT_LEN
;
300 ep
.data
.my_discr
= htonl(bfd
->discrs
.my_discr
);
303 #endif /* BFD_LINUX */
307 udp4_checksum(&ep
.ip
, (uint8_t *)&ep
.udp
,
310 memcpy(pkt
, &ep
, sizeof(ep
));
313 void ptm_bfd_echo_snd(struct bfd_session
*bfd
)
315 struct bfd_raw_echo_pkt
*ep
;
316 bool use_layer2
= false;
319 uint16_t port
= htons(BFD_DEF_ECHO_PORT
);
321 if (!BFD_CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_ECHO_ACTIVE
)) {
322 ptm_bfd_echo_pkt_create(bfd
);
323 BFD_SET_FLAG(bfd
->flags
, BFD_SESS_FLAG_ECHO_ACTIVE
);
325 /* just update the checksum and ip Id */
326 ep
= (struct bfd_raw_echo_pkt
*)(bfd
->echo_pkt
+ ETH_HDR_LEN
);
328 ep
->ip
.id
= htons(ptm_bfd_gen_IP_ID(bfd
));
330 ep
->ip
.check
= checksum((uint16_t *)&ep
->ip
, IP_HDR_LEN
);
331 #endif /* BFD_LINUX */
333 ep
->ip
.ip_id
= htons(ptm_bfd_gen_IP_ID(bfd
));
335 ep
->ip
.ip_sum
= checksum((uint16_t *)&ep
->ip
, IP_HDR_LEN
);
341 pktlen
= BFD_ECHO_PKT_TOT_LEN
;
343 pkt
= &bfd
->echo_pkt
[ETH_HDR_LEN
+ IP_HDR_LEN
+ UDP_HDR_LEN
];
344 pktlen
= BFD_ECHO_PKT_TOT_LEN
345 - (ETH_HDR_LEN
+ IP_HDR_LEN
+ UDP_HDR_LEN
);
348 if (_ptm_bfd_send(bfd
, use_layer2
, &port
, pkt
, pktlen
) != 0) {
349 log_debug("echo-packet: send failure: %s", strerror(errno
));
353 bfd
->stats
.tx_echo_pkt
++;
356 static int ptm_bfd_echo_loopback(uint8_t *pkt
, int pkt_len
, struct sockaddr
*ss
,
360 struct bfd_raw_echo_pkt
*ep
=
361 (struct bfd_raw_echo_pkt
*)(pkt
+ ETH_HDR_LEN
);
362 uint8_t temp_mac
[ETHERNET_ADDRESS_LENGTH
];
364 struct ethhdr
*eth
= (struct ethhdr
*)pkt
;
366 /* swap the mac addresses */
367 memcpy(temp_mac
, eth
->h_source
, ETHERNET_ADDRESS_LENGTH
);
368 memcpy(eth
->h_source
, eth
->h_dest
, ETHERNET_ADDRESS_LENGTH
);
369 memcpy(eth
->h_dest
, temp_mac
, ETHERNET_ADDRESS_LENGTH
);
371 /* swap ip addresses */
372 temp_ip
= ep
->ip
.saddr
;
373 ep
->ip
.saddr
= ep
->ip
.daddr
;
374 ep
->ip
.daddr
= temp_ip
;
376 ep
->ip
.ttl
= ep
->ip
.ttl
- 1;
378 ep
->ip
.check
= checksum((uint16_t *)ep
, IP_HDR_LEN
);
379 #endif /* BFD_LINUX */
380 #ifdef BFD_BSD_FILTER
381 struct bfd_raw_echo_pkt_t
*ep
=
382 (struct bfd_raw_echo_pkt
*)(pkt
+ ETH_HDR_LEN
);
383 uint8_t temp_mac
[ETHERNET_ADDRESS_LENGTH
];
384 struct in_addr temp_ip
;
385 struct ether_header
*ether
= (struct ether_header
*)pkt
;
388 * TODO: this is not yet implemented and requires BPF code for
389 * OmniOS, NetBSD and FreeBSD9.
392 /* swap the mac addresses */
393 memcpy(temp_mac
, ether
->ether_shost
, ETHERNET_ADDRESS_LENGTH
);
394 memcpy(ether
->ether_shost
, ether
->ether_dhost
, ETHERNET_ADDRESS_LENGTH
);
395 memcpy(ether
->ether_dhost
, temp_mac
, ETHERNET_ADDRESS_LENGTH
);
397 /* swap ip addresses */
398 temp_ip
= ep
->ip
.ip_src
;
399 ep
->ip
.ip_src
= ep
->ip
.ip_dst
;
400 ep
->ip
.ip_dst
= temp_ip
;
402 ep
->ip
.ip_ttl
= ep
->ip
.ip_ttl
- 1;
404 ep
->ip
.ip_sum
= checksum((uint16_t *)ep
, IP_HDR_LEN
);
405 #endif /* BFD_BSD_FILTER */
407 if (sendto(bglobal
.bg_echo
, pkt
, pkt_len
, 0, ss
, sslen
) < 0) {
408 log_debug("echo-loopback: send failure: %s", strerror(errno
));
415 static void ptm_bfd_vxlan_pkt_snd(struct bfd_session
*bfd
416 __attribute__((__unused__
)),
417 int fbit
__attribute__((__unused__
)))
419 #if 0 /* TODO: VxLAN support. */
420 struct bfd_raw_ctrl_pkt cp
;
421 uint8_t vxlan_pkt
[BFD_VXLAN_PKT_TOT_LEN
];
422 uint8_t *pkt
= vxlan_pkt
;
423 struct sockaddr_in sin
;
424 struct vxlan_hdr
*vhdr
;
426 memset(vxlan_pkt
, 0, sizeof(vxlan_pkt
));
427 memset(&cp
, 0, sizeof(cp
));
429 /* Construct VxLAN header information */
430 vhdr
= (struct vxlan_hdr
*)pkt
;
431 vhdr
->flags
= htonl(0x08000000);
432 vhdr
->vnid
= htonl(bfd
->vxlan_info
.vnid
<< 8);
433 pkt
+= VXLAN_HDR_LEN
;
435 /* Construct ethernet header information */
436 memcpy(pkt
, bfd
->vxlan_info
.peer_dst_mac
, ETHERNET_ADDRESS_LENGTH
);
437 pkt
= pkt
+ ETHERNET_ADDRESS_LENGTH
;
438 memcpy(pkt
, bfd
->vxlan_info
.local_dst_mac
, ETHERNET_ADDRESS_LENGTH
);
439 pkt
= pkt
+ ETHERNET_ADDRESS_LENGTH
;
440 pkt
[0] = ETH_P_IP
/ 256;
441 pkt
[1] = ETH_P_IP
% 256;
444 /* Construct IP header information */
448 cp
.ip
.tot_len
= htons(IP_CTRL_PKT_LEN
);
449 cp
.ip
.id
= ptm_bfd_gen_IP_ID(bfd
);
451 cp
.ip
.ttl
= BFD_TTL_VAL
;
452 cp
.ip
.protocol
= IPPROTO_UDP
;
453 cp
.ip
.daddr
= bfd
->vxlan_info
.peer_dst_ip
.s_addr
;
454 cp
.ip
.saddr
= bfd
->vxlan_info
.local_dst_ip
.s_addr
;
455 cp
.ip
.check
= checksum((uint16_t *)&cp
.ip
, IP_HDR_LEN
);
457 /* Construct UDP header information */
458 cp
.udp
.source
= htons(BFD_DEFDESTPORT
);
459 cp
.udp
.dest
= htons(BFD_DEFDESTPORT
);
460 cp
.udp
.len
= htons(UDP_CTRL_PKT_LEN
);
462 /* Construct BFD control packet information */
463 cp
.data
.diag
= bfd
->local_diag
;
464 BFD_SETVER(cp
.data
.diag
, BFD_VERSION
);
465 BFD_SETSTATE(cp
.data
.flags
, bfd
->ses_state
);
466 BFD_SETDEMANDBIT(cp
.data
.flags
, BFD_DEF_DEMAND
);
467 BFD_SETPBIT(cp
.data
.flags
, bfd
->polling
);
468 BFD_SETFBIT(cp
.data
.flags
, fbit
);
469 cp
.data
.detect_mult
= bfd
->detect_mult
;
470 cp
.data
.len
= BFD_PKT_LEN
;
471 cp
.data
.discrs
.my_discr
= htonl(bfd
->discrs
.my_discr
);
472 cp
.data
.discrs
.remote_discr
= htonl(bfd
->discrs
.remote_discr
);
473 cp
.data
.timers
.desired_min_tx
= htonl(bfd
->timers
.desired_min_tx
);
474 cp
.data
.timers
.required_min_rx
= htonl(bfd
->timers
.required_min_rx
);
475 cp
.data
.timers
.required_min_echo
= htonl(bfd
->timers
.required_min_echo
);
478 udp4_checksum(&cp
.ip
, (uint8_t *)&cp
.udp
, UDP_CTRL_PKT_LEN
);
480 memcpy(pkt
, &cp
, sizeof(cp
));
481 sin
.sin_family
= AF_INET
;
482 sin
.sin_addr
= bfd
->shop
.peer
.sa_sin
.sin_addr
;
483 sin
.sin_port
= htons(4789);
485 if (sendto(bfd
->sock
, vxlan_pkt
, BFD_VXLAN_PKT_TOT_LEN
, 0,
486 (struct sockaddr
*)&sin
, sizeof(struct sockaddr_in
))
488 ERRLOG("Error sending vxlan bfd pkt: %s", strerror(errno
));
490 bfd
->stats
.tx_ctrl_pkt
++;
495 static int ptm_bfd_process_echo_pkt(int s
)
497 uint32_t my_discr
= 0;
498 struct sockaddr_storage ss
;
499 socklen_t sslen
= sizeof(ss
);
500 uint8_t rx_pkt
[BFD_RX_BUF_LEN
];
501 ssize_t pkt_len
= sizeof(rx_pkt
);
502 struct bfd_session
*bfd
;
504 struct bfd_raw_echo_pkt
*ep
;
507 * valgrind: memset() ss so valgrind doesn't complain about
508 * uninitialized memory.
510 memset(&ss
, 0, sizeof(ss
));
511 pkt_len
= recvfrom(s
, rx_pkt
, sizeof(rx_pkt
), MSG_DONTWAIT
,
512 (struct sockaddr
*)&ss
, &sslen
);
515 log_error("echo-packet: read failure: %s",
521 /* Check if we have at least the basic headers to send back. */
522 if (pkt_len
< BFD_ECHO_PKT_TOT_LEN
) {
523 log_debug("echo-packet: too short (got %ld, expected %d)",
524 pkt_len
, BFD_ECHO_PKT_TOT_LEN
);
528 ep
= (struct bfd_raw_echo_pkt
*)(rx_pkt
+ ETH_HDR_LEN
);
529 /* if TTL = 255, assume that the received echo packet has
532 if (ep
->ip
.ttl
== BFD_TTL_VAL
)
533 return ptm_bfd_echo_loopback(rx_pkt
, pkt_len
,
534 (struct sockaddr
*)&ss
,
535 sizeof(struct sockaddr_ll
));
537 my_discr
= ntohl(ep
->data
.my_discr
);
538 if (ep
->data
.my_discr
== 0) {
539 log_debug("echo-packet: 'my discriminator' is zero");
542 #endif /* BFD_LINUX */
548 * bsd_echo_sock_read() already treats invalid TTL values and
549 * zeroed discriminators.
551 rv
= bsd_echo_sock_read(s
, rx_pkt
, &pkt_len
, &ss
, &sslen
, &ttl
,
556 if (ttl
== BFD_TTL_VAL
)
557 return ptm_bfd_echo_loopback(rx_pkt
, pkt_len
,
558 (struct sockaddr
*)&ss
, sslen
);
561 /* Your discriminator not zero - use it to find session */
562 bfd
= bfd_id_lookup(my_discr
);
564 log_debug("echo-packet: no matching session (id:%u)", my_discr
);
568 if (!BFD_CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_ECHO_ACTIVE
)) {
569 log_debug("echo-packet: echo disabled [%s]", my_discr
,
574 bfd
->stats
.rx_echo_pkt
++;
576 /* Compute detect time */
577 bfd
->echo_detect_TO
= bfd
->remote_detect_mult
* bfd
->echo_xmt_TO
;
579 /* Update echo receive timeout. */
580 bfd_echo_recvtimer_update(bfd
);
585 void ptm_bfd_snd(struct bfd_session
*bfd
, int fbit
)
589 /* if the BFD session is for VxLAN tunnel, then construct and
590 * send bfd raw packet
592 if (BFD_CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_VXLAN
)) {
593 ptm_bfd_vxlan_pkt_snd(bfd
, fbit
);
597 /* Set fields according to section 6.5.7 */
598 cp
.diag
= bfd
->local_diag
;
599 BFD_SETVER(cp
.diag
, BFD_VERSION
);
601 BFD_SETSTATE(cp
.flags
, bfd
->ses_state
);
602 BFD_SETDEMANDBIT(cp
.flags
, BFD_DEF_DEMAND
);
603 BFD_SETPBIT(cp
.flags
, bfd
->polling
);
604 BFD_SETFBIT(cp
.flags
, fbit
);
605 cp
.detect_mult
= bfd
->detect_mult
;
606 cp
.len
= BFD_PKT_LEN
;
607 cp
.discrs
.my_discr
= htonl(bfd
->discrs
.my_discr
);
608 cp
.discrs
.remote_discr
= htonl(bfd
->discrs
.remote_discr
);
610 cp
.timers
.desired_min_tx
=
611 htonl(bfd
->new_timers
.desired_min_tx
);
612 cp
.timers
.required_min_rx
=
613 htonl(bfd
->new_timers
.required_min_rx
);
615 cp
.timers
.desired_min_tx
= htonl(bfd
->timers
.desired_min_tx
);
616 cp
.timers
.required_min_rx
= htonl(bfd
->timers
.required_min_rx
);
618 cp
.timers
.required_min_echo
= htonl(bfd
->timers
.required_min_echo
);
620 if (_ptm_bfd_send(bfd
, false, NULL
, &cp
, BFD_PKT_LEN
) != 0)
623 bfd
->stats
.tx_ctrl_pkt
++;
626 #if 0 /* TODO VxLAN Support */
627 static struct bfd_pkt
*
628 ptm_bfd_process_vxlan_pkt(int s
, ptm_sockevent_e se
, void *udata
, int *ifindex
,
629 struct sockaddr_in
*sin
,
630 struct bfd_session_vxlan_info_t
*vxlan_info
,
631 uint8_t *rx_pkt
, int *mlen
)
633 struct sockaddr_ll sll
;
634 uint32_t from_len
= sizeof(struct sockaddr_ll
);
635 struct bfd_raw_ctrl_pkt
*cp
;
636 uint8_t *pkt
= rx_pkt
;
638 struct ethhdr
*inner_ethh
;
640 *mlen
= recvfrom(s
, rx_pkt
, BFD_RX_BUF_LEN
, MSG_DONTWAIT
,
641 (struct sockaddr
*)&sll
, &from_len
);
645 ERRLOG("Error receiving from BFD Vxlan socket %d: %m",
650 iph
= (struct iphdr
*)(pkt
+ ETH_HDR_LEN
);
651 pkt
= pkt
+ ETH_HDR_LEN
+ IP_HDR_LEN
+ UDP_HDR_LEN
;
652 vxlan_info
->vnid
= ntohl(*((int *)(pkt
+ 4)));
653 vxlan_info
->vnid
= vxlan_info
->vnid
>> 8;
655 pkt
= pkt
+ VXLAN_HDR_LEN
;
656 inner_ethh
= (struct ethhdr
*)pkt
;
658 cp
= (struct bfd_raw_ctrl_pkt
*)(pkt
+ ETH_HDR_LEN
);
660 /* Discard the non BFD packets */
661 if (ntohs(cp
->udp
.dest
) != BFD_DEFDESTPORT
)
664 *ifindex
= sll
.sll_ifindex
;
665 sin
->sin_addr
.s_addr
= iph
->saddr
;
666 sin
->sin_port
= ntohs(cp
->udp
.dest
);
668 vxlan_info
->local_dst_ip
.s_addr
= cp
->ip
.daddr
;
669 memcpy(vxlan_info
->local_dst_mac
, inner_ethh
->h_dest
,
670 ETHERNET_ADDRESS_LENGTH
);
677 ptm_bfd_validate_vxlan_pkt(struct bfd_session
*bfd
,
678 struct bfd_session_vxlan_info
*vxlan_info
)
680 if (bfd
->vxlan_info
.check_tnl_key
&& (vxlan_info
->vnid
!= 0)) {
681 log_error("vxlan-packet: vnid not zero: %d", vxlan_info
->vnid
);
685 if (bfd
->vxlan_info
.local_dst_ip
.s_addr
686 != vxlan_info
->local_dst_ip
.s_addr
) {
687 log_error("vxlan-packet: wrong inner destination",
688 inet_ntoa(vxlan_info
->local_dst_ip
));
692 if (memcmp(bfd
->vxlan_info
.local_dst_mac
, vxlan_info
->local_dst_mac
,
693 ETHERNET_ADDRESS_LENGTH
)) {
695 "vxlan-packet: wrong inner mac: %02x:%02x:%02x:%02x:%02x:%02x",
696 vxlan_info
->local_dst_mac
[0],
697 vxlan_info
->local_dst_mac
[1],
698 vxlan_info
->local_dst_mac
[2],
699 vxlan_info
->local_dst_mac
[3],
700 vxlan_info
->local_dst_mac
[4],
701 vxlan_info
->local_dst_mac
[5]);
708 static ssize_t
bfd_recv_ipv4(int sd
, bool is_mhop
, char *port
, size_t portlen
,
709 char *vrfname
, size_t vrfnamelen
,
710 struct sockaddr_any
*local
,
711 struct sockaddr_any
*peer
)
717 memset(port
, 0, portlen
);
718 memset(vrfname
, 0, vrfnamelen
);
719 memset(local
, 0, sizeof(*local
));
720 memset(peer
, 0, sizeof(*peer
));
722 mlen
= recvmsg(sd
, &msghdr
, MSG_DONTWAIT
);
725 log_error("ipv4-recv: recv failed: %s",
731 /* Get source address */
732 peer
->sa_sin
= *((struct sockaddr_in
*)(msghdr
.msg_name
));
734 /* Get and check TTL */
735 for (cm
= CMSG_FIRSTHDR(&msghdr
); cm
!= NULL
;
736 cm
= CMSG_NXTHDR(&msghdr
, cm
)) {
737 if (cm
->cmsg_level
!= IPPROTO_IP
)
740 switch (cm
->cmsg_type
) {
745 memcpy(&ttl
, CMSG_DATA(cm
), sizeof(ttl
));
746 if ((is_mhop
== false) && (ttl
!= BFD_TTL_VAL
)) {
748 "ipv4-recv: invalid TTL from %s (expected %d, got %d flags %d)",
749 satostr(peer
), ttl
, BFD_TTL_VAL
,
757 struct in_pktinfo
*pi
=
758 (struct in_pktinfo
*)CMSG_DATA(cm
);
763 local
->sa_sin
.sin_family
= AF_INET
;
764 local
->sa_sin
.sin_addr
= pi
->ipi_addr
;
765 fetch_portname_from_ifindex(pi
->ipi_ifindex
, port
,
769 #endif /* BFD_LINUX */
774 memcpy(&ttl
, CMSG_DATA(cm
), sizeof(ttl
));
775 if ((is_mhop
== false) && (ttl
!= BFD_TTL_VAL
)) {
777 "ipv4-recv: invalid TTL from %s (expected %d, got %d flags %d)",
778 satostr(peer
), ttl
, BFD_TTL_VAL
,
785 case IP_RECVDSTADDR
: {
788 memcpy(&ia
, CMSG_DATA(cm
), sizeof(ia
));
789 local
->sa_sin
.sin_family
= AF_INET
;
790 local
->sa_sin
.sin_addr
= ia
;
797 * On *BSDs we expect to land here when skipping
798 * the IP_RECVIF header. It will be handled by
799 * getsockopt_ifindex() below.
806 /* OS agnostic way of getting interface name. */
808 ifindex
= getsockopt_ifindex(AF_INET
, &msghdr
);
810 fetch_portname_from_ifindex(ifindex
, port
, portlen
);
816 ssize_t
bfd_recv_ipv6(int sd
, bool is_mhop
, char *port
, size_t portlen
,
817 char *vrfname
, size_t vrfnamelen
,
818 struct sockaddr_any
*local
, struct sockaddr_any
*peer
)
821 struct in6_pktinfo
*pi6
= NULL
;
824 memset(port
, 0, portlen
);
825 memset(vrfname
, 0, vrfnamelen
);
826 memset(local
, 0, sizeof(*local
));
827 memset(peer
, 0, sizeof(*peer
));
829 mlen
= recvmsg(sd
, &msghdr6
, MSG_DONTWAIT
);
832 log_error("ipv4-recv: recv failed: %s",
838 /* Get source address */
839 peer
->sa_sin6
= *((struct sockaddr_in6
*)(msghdr6
.msg_name
));
841 /* Get and check TTL */
842 for (cm
= CMSG_FIRSTHDR(&msghdr6
); cm
!= NULL
;
843 cm
= CMSG_NXTHDR(&msghdr6
, cm
)) {
844 if (cm
->cmsg_level
!= IPPROTO_IPV6
)
847 if (cm
->cmsg_type
== IPV6_HOPLIMIT
) {
848 memcpy(&ttlval
, CMSG_DATA(cm
), 4);
849 if ((is_mhop
== false) && (ttlval
!= BFD_TTL_VAL
)) {
851 "ipv6-recv: invalid TTL from %s (expected %d, got %d flags %d)",
852 satostr(peer
), ttlval
, BFD_TTL_VAL
,
856 } else if (cm
->cmsg_type
== IPV6_PKTINFO
) {
857 pi6
= (struct in6_pktinfo
*)CMSG_DATA(cm
);
859 local
->sa_sin
.sin_family
= AF_INET6
;
860 local
->sa_sin6
.sin6_addr
= pi6
->ipi6_addr
;
861 fetch_portname_from_ifindex(pi6
->ipi6_ifindex
,
870 static void bfd_sd_reschedule(int sd
)
872 if (sd
== bglobal
.bg_shop
) {
873 bglobal
.bg_ev
[0] = NULL
;
874 thread_add_read(master
, bfd_recv_cb
, NULL
, bglobal
.bg_shop
,
876 } else if (sd
== bglobal
.bg_mhop
) {
877 bglobal
.bg_ev
[1] = NULL
;
878 thread_add_read(master
, bfd_recv_cb
, NULL
, bglobal
.bg_mhop
,
880 } else if (sd
== bglobal
.bg_shop6
) {
881 bglobal
.bg_ev
[2] = NULL
;
882 thread_add_read(master
, bfd_recv_cb
, NULL
, bglobal
.bg_shop6
,
884 } else if (sd
== bglobal
.bg_mhop6
) {
885 bglobal
.bg_ev
[3] = NULL
;
886 thread_add_read(master
, bfd_recv_cb
, NULL
, bglobal
.bg_mhop6
,
888 } else if (sd
== bglobal
.bg_echo
) {
889 bglobal
.bg_ev
[4] = NULL
;
890 thread_add_read(master
, bfd_recv_cb
, NULL
, bglobal
.bg_echo
,
892 } else if (sd
== bglobal
.bg_vxlan
) {
893 bglobal
.bg_ev
[5] = NULL
;
894 thread_add_read(master
, bfd_recv_cb
, NULL
, bglobal
.bg_vxlan
,
899 static void cp_debug(bool mhop
, struct sockaddr_any
*peer
,
900 struct sockaddr_any
*local
, const char *port
,
901 const char *vrf
, const char *fmt
, ...)
903 char buf
[512], peerstr
[128], localstr
[128], portstr
[64], vrfstr
[64];
906 if (peer
->sa_sin
.sin_family
)
907 snprintf(peerstr
, sizeof(peerstr
), " peer:%s", satostr(peer
));
911 if (local
->sa_sin
.sin_family
)
912 snprintf(localstr
, sizeof(localstr
), " local:%s",
918 snprintf(portstr
, sizeof(portstr
), " port:%s", port
);
923 snprintf(vrfstr
, sizeof(vrfstr
), " vrf:%s", port
);
928 vsnprintf(buf
, sizeof(buf
), fmt
, vl
);
931 log_debug("control-packet: %s [mhop:%s%s%s%s%s]", buf
,
932 mhop
? "yes" : "no", peerstr
, localstr
, portstr
, vrfstr
);
935 int bfd_recv_cb(struct thread
*t
)
937 int sd
= THREAD_FD(t
);
938 struct bfd_session
*bfd
;
940 bool is_mhop
, is_vxlan
;
942 uint32_t oldEchoXmt_TO
, oldXmtTime
;
943 struct sockaddr_any local
, peer
;
944 char port
[MAXNAMELEN
+ 1], vrfname
[MAXNAMELEN
+ 1];
945 struct bfd_session_vxlan_info vxlan_info
;
947 /* Schedule next read. */
948 bfd_sd_reschedule(sd
);
950 /* Handle echo packets. */
951 if (sd
== bglobal
.bg_echo
) {
952 ptm_bfd_process_echo_pkt(sd
);
956 /* Handle control packets. */
957 is_mhop
= is_vxlan
= false;
958 if (sd
== bglobal
.bg_shop
|| sd
== bglobal
.bg_mhop
) {
959 is_mhop
= sd
== bglobal
.bg_mhop
;
960 mlen
= bfd_recv_ipv4(sd
, is_mhop
, port
, sizeof(port
), vrfname
,
961 sizeof(vrfname
), &local
, &peer
);
962 } else if (sd
== bglobal
.bg_shop6
|| sd
== bglobal
.bg_mhop6
) {
963 is_mhop
= sd
== bglobal
.bg_mhop6
;
964 mlen
= bfd_recv_ipv6(sd
, is_mhop
, port
, sizeof(port
), vrfname
,
965 sizeof(vrfname
), &local
, &peer
);
967 #if 0 /* TODO vxlan handling */
968 cp
= ptm_bfd_process_vxlan_pkt(s
, se
, udata
, &local_ifindex
,
969 &sin
, &vxlan_info
, rx_pkt
, &mlen
);
974 /* keep in network-byte order */
975 peer
.ip4_addr
.s_addr
= sin
.sin_addr
.s_addr
;
976 peer
.family
= AF_INET
;
977 strcpy(peer_addr
, inet_ntoa(sin
.sin_addr
));
980 /* Implement RFC 5880 6.8.6 */
981 if (mlen
< BFD_PKT_LEN
) {
982 cp_debug(is_mhop
, &peer
, &local
, port
, vrfname
,
983 "too small (%ld bytes)", mlen
);
988 * Parse the control header for inconsistencies:
990 * - Bad multiplier configuration;
992 * - Invalid discriminator;
994 cp
= (struct bfd_pkt
*)(msghdr
.msg_iov
->iov_base
);
995 if (BFD_GETVER(cp
->diag
) != BFD_VERSION
) {
996 cp_debug(is_mhop
, &peer
, &local
, port
, vrfname
,
997 "bad version %d", BFD_GETVER(cp
->diag
));
1001 if (cp
->detect_mult
== 0) {
1002 cp_debug(is_mhop
, &peer
, &local
, port
, vrfname
,
1003 "detect multiplier set to zero");
1007 if ((cp
->len
< BFD_PKT_LEN
) || (cp
->len
> mlen
)) {
1008 cp_debug(is_mhop
, &peer
, &local
, port
, vrfname
, "too small");
1012 if (cp
->discrs
.my_discr
== 0) {
1013 cp_debug(is_mhop
, &peer
, &local
, port
, vrfname
,
1014 "'my discriminator' is zero");
1018 /* Find the session that this packet belongs. */
1019 bfd
= ptm_bfd_sess_find(cp
, port
, &peer
, &local
, vrfname
, is_mhop
);
1021 cp_debug(is_mhop
, &peer
, &local
, port
, vrfname
,
1022 "no session found");
1026 /* Handle VxLAN cases. */
1027 if (is_vxlan
&& !ptm_bfd_validate_vxlan_pkt(bfd
, &vxlan_info
))
1030 bfd
->stats
.rx_ctrl_pkt
++;
1033 * Multi hop: validate packet TTL.
1034 * Single hop: set local address that received the packet.
1037 if ((BFD_TTL_VAL
- bfd
->mh_ttl
) > ttlval
) {
1038 cp_debug(is_mhop
, &peer
, &local
, port
, vrfname
,
1039 "exceeded max hop count (expected %d, got %d)",
1040 bfd
->mh_ttl
, ttlval
);
1043 } else if (bfd
->local_ip
.sa_sin
.sin_family
== AF_UNSPEC
) {
1044 bfd
->local_ip
= local
;
1048 * If no interface was detected, save the interface where the
1051 if (bfd
->ifindex
== 0)
1052 bfd
->ifindex
= ptm_bfd_fetch_ifindex(port
);
1054 /* Log remote discriminator changes. */
1055 if ((bfd
->discrs
.remote_discr
!= 0)
1056 && (bfd
->discrs
.remote_discr
!= ntohl(cp
->discrs
.my_discr
)))
1057 cp_debug(is_mhop
, &peer
, &local
, port
, vrfname
,
1058 "remote discriminator mismatch (expected %d, got %d)",
1059 bfd
->discrs
.remote_discr
, ntohl(cp
->discrs
.my_discr
));
1061 bfd
->discrs
.remote_discr
= ntohl(cp
->discrs
.my_discr
);
1063 /* If received the Final bit, the new values should take effect */
1064 if (bfd
->polling
&& BFD_GETFBIT(cp
->flags
)) {
1065 bfd
->timers
.desired_min_tx
= bfd
->new_timers
.desired_min_tx
;
1066 bfd
->timers
.required_min_rx
= bfd
->new_timers
.required_min_rx
;
1067 bfd
->new_timers
.desired_min_tx
= 0;
1068 bfd
->new_timers
.required_min_rx
= 0;
1072 if (!bfd
->demand_mode
) {
1073 /* Compute detect time */
1074 bfd
->detect_TO
= cp
->detect_mult
1075 * ((bfd
->timers
.required_min_rx
1076 > ntohl(cp
->timers
.desired_min_tx
))
1077 ? bfd
->timers
.required_min_rx
1078 : ntohl(cp
->timers
.desired_min_tx
));
1079 bfd
->remote_detect_mult
= cp
->detect_mult
;
1081 cp_debug(is_mhop
, &peer
, &local
, port
, vrfname
,
1082 "unsupported demand mode");
1084 /* Save remote diagnostics before state switch. */
1085 bfd
->remote_diag
= cp
->diag
& BFD_DIAGMASK
;
1087 /* State switch from section 6.8.6 */
1088 if (BFD_GETSTATE(cp
->flags
) == PTM_BFD_ADM_DOWN
) {
1089 if (bfd
->ses_state
!= PTM_BFD_DOWN
)
1090 ptm_bfd_ses_dn(bfd
, BFD_DIAGNEIGHDOWN
);
1092 switch (bfd
->ses_state
) {
1093 case (PTM_BFD_DOWN
):
1094 if (BFD_GETSTATE(cp
->flags
) == PTM_BFD_INIT
)
1095 ptm_bfd_ses_up(bfd
);
1096 else if (BFD_GETSTATE(cp
->flags
) == PTM_BFD_DOWN
)
1097 bfd
->ses_state
= PTM_BFD_INIT
;
1099 case (PTM_BFD_INIT
):
1100 if (BFD_GETSTATE(cp
->flags
) == PTM_BFD_INIT
1101 || BFD_GETSTATE(cp
->flags
) == PTM_BFD_UP
)
1102 ptm_bfd_ses_up(bfd
);
1105 if (BFD_GETSTATE(cp
->flags
) == PTM_BFD_DOWN
)
1106 ptm_bfd_ses_dn(bfd
, BFD_DIAGNEIGHDOWN
);
1112 * Handle echo packet status:
1113 * - Start echo packets if configured and permitted
1114 * (required_min_echo > 0);
1115 * - Stop echo packets if not allowed (required_min_echo == 0);
1116 * - Recalculate echo packet interval;
1118 if (BFD_CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_ECHO
)) {
1119 if (BFD_CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_ECHO_ACTIVE
)) {
1120 if (!ntohl(cp
->timers
.required_min_echo
)) {
1121 ptm_bfd_echo_stop(bfd
, 1);
1123 oldEchoXmt_TO
= bfd
->echo_xmt_TO
;
1125 bfd
->timers
.required_min_echo
;
1126 if (ntohl(cp
->timers
.required_min_echo
)
1128 bfd
->echo_xmt_TO
= ntohl(
1129 cp
->timers
.required_min_echo
);
1130 if (oldEchoXmt_TO
!= bfd
->echo_xmt_TO
)
1131 ptm_bfd_echo_start(bfd
);
1133 } else if (ntohl(cp
->timers
.required_min_echo
)) {
1134 bfd
->echo_xmt_TO
= bfd
->timers
.required_min_echo
;
1135 if (ntohl(cp
->timers
.required_min_echo
)
1138 ntohl(cp
->timers
.required_min_echo
);
1139 ptm_bfd_echo_start(bfd
);
1143 if (BFD_CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_ECHO_ACTIVE
)) {
1144 bfd
->echo_xmt_TO
= bfd
->timers
.required_min_echo
;
1145 if (ntohl(cp
->timers
.required_min_echo
) > bfd
->echo_xmt_TO
)
1146 bfd
->echo_xmt_TO
= ntohl(cp
->timers
.required_min_echo
);
1149 /* Calculate new transmit time */
1150 oldXmtTime
= bfd
->xmt_TO
;
1152 (bfd
->timers
.desired_min_tx
> ntohl(cp
->timers
.required_min_rx
))
1153 ? bfd
->timers
.desired_min_tx
1154 : ntohl(cp
->timers
.required_min_rx
);
1156 /* If transmit time has changed, and too much time until next xmt,
1159 if (BFD_GETPBIT(cp
->flags
)) {
1160 ptm_bfd_xmt_TO(bfd
, 1);
1161 } else if (oldXmtTime
!= bfd
->xmt_TO
) {
1162 /* XXX add some skid to this as well */
1163 ptm_bfd_start_xmt_timer(bfd
, false);
1166 /* Restart detection timer (packet received) */
1167 if (!bfd
->demand_mode
)
1168 bfd_recvtimer_update(bfd
);
1171 * Save the timers and state sent by the remote end
1172 * for debugging and statistics.
1174 if (BFD_GETFBIT(cp
->flags
)) {
1175 bfd
->remote_timers
.desired_min_tx
=
1176 ntohl(cp
->timers
.desired_min_tx
);
1177 bfd
->remote_timers
.required_min_rx
=
1178 ntohl(cp
->timers
.required_min_rx
);
1179 bfd
->remote_timers
.required_min_echo
=
1180 ntohl(cp
->timers
.required_min_echo
);
1182 control_notify_config(BCM_NOTIFY_CONFIG_UPDATE
, bfd
);
1197 int bp_set_ttl(int sd
)
1199 if (setsockopt(sd
, IPPROTO_IP
, IP_TTL
, &ttlval
, sizeof(ttlval
)) == -1) {
1200 log_warning("%s: setsockopt(IP_TTL): %s", __func__
,
1208 int bp_set_tos(int sd
)
1210 if (setsockopt(sd
, IPPROTO_IP
, IP_TOS
, &tosval
, sizeof(tosval
)) == -1) {
1211 log_warning("%s: setsockopt(IP_TOS): %s", __func__
,
1219 static void bp_set_ipopts(int sd
)
1221 if (bp_set_ttl(sd
) != 0)
1222 log_fatal("%s: TTL configuration failed", __func__
);
1224 if (setsockopt(sd
, IPPROTO_IP
, IP_RECVTTL
, &rcvttl
, sizeof(rcvttl
))
1226 log_fatal("%s: setsockopt(IP_RECVTTL): %s", __func__
,
1230 int pktinfo
= BFD_PKT_INFO_VAL
;
1231 /* Figure out address and interface to do the peer matching. */
1232 if (setsockopt(sd
, IPPROTO_IP
, IP_PKTINFO
, &pktinfo
, sizeof(pktinfo
))
1234 log_fatal("%s: setsockopt(IP_PKTINFO): %s", __func__
,
1236 #endif /* BFD_LINUX */
1240 /* Find out our address for peer matching. */
1241 if (setsockopt(sd
, IPPROTO_IP
, IP_RECVDSTADDR
, &yes
, sizeof(yes
)) == -1)
1242 log_fatal("%s: setsockopt(IP_RECVDSTADDR): %s", __func__
,
1245 /* Find out interface where the packet came in. */
1246 if (setsockopt_ifindex(AF_INET
, sd
, yes
) == -1)
1247 log_fatal("%s: setsockopt_ipv4_ifindex: %s", __func__
,
1249 #endif /* BFD_BSD */
1252 static void bp_bind_ip(int sd
, uint16_t port
)
1254 struct sockaddr_in sin
;
1256 memset(&sin
, 0, sizeof(sin
));
1257 sin
.sin_family
= AF_INET
;
1258 sin
.sin_addr
.s_addr
= htonl(INADDR_ANY
);
1259 sin
.sin_port
= htons(port
);
1260 if (bind(sd
, (struct sockaddr
*)&sin
, sizeof(sin
)) == -1)
1261 log_fatal("%s: bind: %s", __func__
, strerror(errno
));
1264 int bp_udp_shop(void)
1268 sd
= socket(AF_INET
, SOCK_DGRAM
, PF_UNSPEC
);
1270 log_fatal("%s: socket: %s", __func__
, strerror(errno
));
1273 bp_bind_ip(sd
, BFD_DEFDESTPORT
);
1278 int bp_udp_mhop(void)
1282 sd
= socket(AF_INET
, SOCK_DGRAM
, PF_UNSPEC
);
1284 log_fatal("%s: socket: %s", __func__
, strerror(errno
));
1287 bp_bind_ip(sd
, BFD_DEF_MHOP_DEST_PORT
);
1292 int bp_peer_socket(struct bfd_peer_cfg
*bpc
)
1295 struct sockaddr_in sin
;
1296 static int srcPort
= BFD_SRCPORTINIT
;
1298 sd
= socket(AF_INET
, SOCK_DGRAM
, PF_UNSPEC
);
1300 log_error("ipv4-new: failed to create socket: %s",
1305 if (!bpc
->bpc_has_vxlan
) {
1306 /* Set TTL to 255 for all transmitted packets */
1307 if (bp_set_ttl(sd
) != 0) {
1313 /* Set TOS to CS6 for all transmitted packets */
1314 if (bp_set_tos(sd
) != 0) {
1319 /* dont bind-to-device incase of vxlan */
1320 if (!bpc
->bpc_has_vxlan
&& bpc
->bpc_has_localif
) {
1321 if (bp_bind_dev(sd
, bpc
->bpc_localif
) != 0) {
1325 } else if (bpc
->bpc_mhop
&& bpc
->bpc_has_vrfname
) {
1326 if (bp_bind_dev(sd
, bpc
->bpc_vrfname
) != 0) {
1332 /* Find an available source port in the proper range */
1333 memset(&sin
, 0, sizeof(sin
));
1334 sin
= bpc
->bpc_local
.sa_sin
;
1335 sin
.sin_family
= AF_INET
;
1336 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1337 sin
.sin_len
= sizeof(sin
);
1338 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1339 if (bpc
->bpc_mhop
|| bpc
->bpc_has_vxlan
)
1340 sin
.sin_addr
= bpc
->bpc_local
.sa_sin
.sin_addr
;
1342 sin
.sin_addr
.s_addr
= INADDR_ANY
;
1346 if ((++pcount
) > (BFD_SRCPORTMAX
- BFD_SRCPORTINIT
)) {
1347 /* Searched all ports, none available */
1348 log_error("ipv4-new: failed to bind port: %s",
1353 if (srcPort
>= BFD_SRCPORTMAX
)
1354 srcPort
= BFD_SRCPORTINIT
;
1355 sin
.sin_port
= htons(srcPort
++);
1356 } while (bind(sd
, (struct sockaddr
*)&sin
, sizeof(sin
)) < 0);
1366 int bp_peer_socketv6(struct bfd_peer_cfg
*bpc
)
1368 int sd
, pcount
, ifindex
;
1369 struct sockaddr_in6 sin6
;
1370 static int srcPort
= BFD_SRCPORTINIT
;
1372 sd
= socket(AF_INET6
, SOCK_DGRAM
, PF_UNSPEC
);
1374 log_error("ipv6-new: failed to create socket: %s",
1379 if (!bpc
->bpc_has_vxlan
) {
1380 /* Set TTL to 255 for all transmitted packets */
1381 if (bp_set_ttlv6(sd
) != 0) {
1387 /* Set TOS to CS6 for all transmitted packets */
1388 if (bp_set_tosv6(sd
) != 0) {
1393 /* Find an available source port in the proper range */
1394 memset(&sin6
, 0, sizeof(sin6
));
1395 sin6
.sin6_family
= AF_INET6
;
1396 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1397 sin6
.sin6_len
= sizeof(sin6
);
1398 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1399 sin6
= bpc
->bpc_local
.sa_sin6
;
1400 if (sin6
.sin6_family
!= AF_INET6
) {
1401 #if 0 /* XXX what is this? */
1402 ifindex
= ptm_bfd_fetch_ifindex(bpc
->bpc_localif
);
1403 if (IN6_IS_ADDR_LINKLOCAL(&sin6
.sin6_addr
))
1404 sin6
.sin6_scope_id
= ifindex
;
1406 } else if (bpc
->bpc_has_localif
) {
1407 ifindex
= ptm_bfd_fetch_ifindex(bpc
->bpc_localif
);
1408 sin6
.sin6_scope_id
= ifindex
;
1411 if (bpc
->bpc_has_localif
) {
1412 if (bp_bind_dev(sd
, bpc
->bpc_localif
) != 0) {
1416 } else if (bpc
->bpc_mhop
&& bpc
->bpc_has_vrfname
) {
1417 if (bp_bind_dev(sd
, bpc
->bpc_vrfname
) != 0) {
1425 if ((++pcount
) > (BFD_SRCPORTMAX
- BFD_SRCPORTINIT
)) {
1426 /* Searched all ports, none available */
1427 log_error("ipv6-new: failed to bind port: %s",
1432 if (srcPort
>= BFD_SRCPORTMAX
)
1433 srcPort
= BFD_SRCPORTINIT
;
1434 sin6
.sin6_port
= htons(srcPort
++);
1435 } while (bind(sd
, (struct sockaddr
*)&sin6
, sizeof(sin6
)) < 0);
1440 int bp_set_ttlv6(int sd
)
1442 if (setsockopt(sd
, IPPROTO_IPV6
, IPV6_UNICAST_HOPS
, &ttlval
,
1445 log_warning("%s: setsockopt(IPV6_UNICAST_HOPS): %s", __func__
,
1453 int bp_set_tosv6(int sd
)
1455 if (setsockopt(sd
, IPPROTO_IPV6
, IPV6_TCLASS
, &tosval
, sizeof(tosval
))
1457 log_warning("%s: setsockopt(IPV6_TCLASS): %s", __func__
,
1465 static void bp_set_ipv6opts(int sd
)
1467 static int ipv6_pktinfo
= BFD_IPV6_PKT_INFO_VAL
;
1468 static int ipv6_only
= BFD_IPV6_ONLY_VAL
;
1470 if (setsockopt(sd
, IPPROTO_IPV6
, IPV6_UNICAST_HOPS
, &ttlval
,
1473 log_fatal("%s: setsockopt(IPV6_UNICAST_HOPS): %s", __func__
,
1476 if (setsockopt_ipv6_hoplimit(sd
, rcvttl
) == -1)
1477 log_fatal("%s: setsockopt(IPV6_HOPLIMIT): %s", __func__
,
1480 if (setsockopt_ipv6_pktinfo(sd
, ipv6_pktinfo
) == -1)
1481 log_fatal("%s: setsockopt(IPV6_PKTINFO): %s", __func__
,
1484 if (setsockopt(sd
, IPPROTO_IPV6
, IPV6_V6ONLY
, &ipv6_only
,
1487 log_fatal("%s: setsockopt(IPV6_V6ONLY): %s", __func__
,
1491 static void bp_bind_ipv6(int sd
, uint16_t port
)
1493 struct sockaddr_in6 sin6
;
1495 memset(&sin6
, 0, sizeof(sin6
));
1496 sin6
.sin6_family
= AF_INET6
;
1497 sin6
.sin6_addr
= in6addr_any
;
1498 sin6
.sin6_port
= htons(port
);
1499 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1500 sin6
.sin6_len
= sizeof(sin6
);
1501 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1502 if (bind(sd
, (struct sockaddr
*)&sin6
, sizeof(sin6
)) == -1)
1503 log_fatal("%s: bind: %s", __func__
, strerror(errno
));
1506 int bp_udp6_shop(void)
1510 sd
= socket(AF_INET6
, SOCK_DGRAM
, PF_UNSPEC
);
1512 log_fatal("%s: socket: %s", __func__
, strerror(errno
));
1514 bp_set_ipv6opts(sd
);
1515 bp_bind_ipv6(sd
, BFD_DEFDESTPORT
);
1520 int bp_udp6_mhop(void)
1524 sd
= socket(AF_INET6
, SOCK_DGRAM
, PF_UNSPEC
);
1526 log_fatal("%s: socket: %s", __func__
, strerror(errno
));
1528 bp_set_ipv6opts(sd
);
1529 bp_bind_ipv6(sd
, BFD_DEF_MHOP_DEST_PORT
);