2023-02-08 13:17:09 +01:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2018-06-27 16:29:02 +02:00
|
|
|
/*********************************************************************
|
|
|
|
* Copyright 2017 Cumulus Networks, Inc. All rights reserved.
|
|
|
|
*
|
|
|
|
* bfd_packet.c: implements the BFD protocol packet handling.
|
|
|
|
*
|
|
|
|
* Authors
|
|
|
|
* -------
|
|
|
|
* Shrijeet Mukherjee [shm@cumulusnetworks.com]
|
|
|
|
* Kanna Rajagopal [kanna@cumulusnetworks.com]
|
|
|
|
* Radhika Mahankali [Radhika@cumulusnetworks.com]
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <zebra.h>
|
|
|
|
|
|
|
|
#ifdef BFD_LINUX
|
|
|
|
#include <linux/if_packet.h>
|
|
|
|
#endif /* BFD_LINUX */
|
|
|
|
|
|
|
|
#include <netinet/if_ether.h>
|
|
|
|
#include <netinet/udp.h>
|
|
|
|
|
|
|
|
#include "lib/sockopt.h"
|
2022-06-07 01:40:17 +02:00
|
|
|
#include "lib/checksum.h"
|
|
|
|
#include "lib/network.h"
|
2018-06-27 16:29:02 +02:00
|
|
|
|
|
|
|
#include "bfd.h"
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Prototypes
|
|
|
|
*/
|
2019-03-26 10:23:18 +01:00
|
|
|
static int ptm_bfd_process_echo_pkt(struct bfd_vrf_global *bvrf, int s);
|
2018-08-29 20:17:49 +02:00
|
|
|
int _ptm_bfd_send(struct bfd_session *bs, uint16_t *port, const void *data,
|
|
|
|
size_t datalen);
|
2018-06-27 16:29:02 +02:00
|
|
|
|
2019-03-26 10:23:18 +01:00
|
|
|
static void bfd_sd_reschedule(struct bfd_vrf_global *bvrf, int sd);
|
2018-08-29 19:17:29 +02:00
|
|
|
ssize_t bfd_recv_ipv4(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl,
|
2019-02-01 12:22:00 +01:00
|
|
|
ifindex_t *ifindex, struct sockaddr_any *local,
|
2018-08-29 19:17:29 +02:00
|
|
|
struct sockaddr_any *peer);
|
|
|
|
ssize_t bfd_recv_ipv6(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl,
|
2019-02-01 12:22:00 +01:00
|
|
|
ifindex_t *ifindex, struct sockaddr_any *local,
|
2018-08-29 19:17:29 +02:00
|
|
|
struct sockaddr_any *peer);
|
2018-08-29 20:17:49 +02:00
|
|
|
int bp_udp_send(int sd, uint8_t ttl, uint8_t *data, size_t datalen,
|
|
|
|
struct sockaddr *to, socklen_t tolen);
|
2022-07-26 01:02:46 +02:00
|
|
|
int bp_bfd_echo_in(struct bfd_vrf_global *bvrf, int sd, uint8_t *ttl,
|
|
|
|
uint32_t *my_discr, uint64_t *my_rtt);
|
2022-06-07 01:40:17 +02:00
|
|
|
#ifdef BFD_LINUX
|
|
|
|
ssize_t bfd_recv_ipv4_fp(int sd, uint8_t *msgbuf, size_t msgbuflen,
|
|
|
|
uint8_t *ttl, ifindex_t *ifindex,
|
|
|
|
struct sockaddr_any *local, struct sockaddr_any *peer);
|
|
|
|
void bfd_peer_mac_set(int sd, struct bfd_session *bfd,
|
|
|
|
struct sockaddr_any *peer, struct interface *ifp);
|
|
|
|
int bp_udp_send_fp(int sd, uint8_t *data, size_t datalen,
|
|
|
|
struct bfd_session *bfd);
|
|
|
|
ssize_t bfd_recv_fp_echo(int sd, uint8_t *msgbuf, size_t msgbuflen,
|
|
|
|
uint8_t *ttl, ifindex_t *ifindex,
|
|
|
|
struct sockaddr_any *local, struct sockaddr_any *peer);
|
|
|
|
#endif
|
2018-06-27 16:29:02 +02:00
|
|
|
|
|
|
|
/* socket related prototypes */
|
|
|
|
static void bp_set_ipopts(int sd);
|
|
|
|
static void bp_bind_ip(int sd, uint16_t port);
|
|
|
|
static void bp_set_ipv6opts(int sd);
|
|
|
|
static void bp_bind_ipv6(int sd, uint16_t port);
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Functions
|
|
|
|
*/
|
2018-08-29 20:17:49 +02:00
|
|
|
int _ptm_bfd_send(struct bfd_session *bs, uint16_t *port, const void *data,
|
|
|
|
size_t datalen)
|
2018-06-27 16:29:02 +02:00
|
|
|
{
|
|
|
|
struct sockaddr *sa;
|
|
|
|
struct sockaddr_in sin;
|
|
|
|
struct sockaddr_in6 sin6;
|
|
|
|
socklen_t slen;
|
|
|
|
ssize_t rv;
|
|
|
|
int sd = -1;
|
|
|
|
|
2020-04-09 21:52:49 +02:00
|
|
|
if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_IPV6)) {
|
2018-06-27 16:29:02 +02:00
|
|
|
memset(&sin6, 0, sizeof(sin6));
|
|
|
|
sin6.sin6_family = AF_INET6;
|
2019-03-11 19:09:15 +01:00
|
|
|
memcpy(&sin6.sin6_addr, &bs->key.peer, sizeof(sin6.sin6_addr));
|
2020-11-05 17:46:27 +01:00
|
|
|
if (bs->ifp && IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr))
|
2019-03-11 19:09:15 +01:00
|
|
|
sin6.sin6_scope_id = bs->ifp->ifindex;
|
|
|
|
|
2018-06-27 16:29:02 +02:00
|
|
|
sin6.sin6_port =
|
|
|
|
(port) ? *port
|
2020-04-09 21:52:49 +02:00
|
|
|
: (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH))
|
2018-06-27 16:29:02 +02:00
|
|
|
? htons(BFD_DEF_MHOP_DEST_PORT)
|
|
|
|
: htons(BFD_DEFDESTPORT);
|
|
|
|
|
|
|
|
sd = bs->sock;
|
|
|
|
sa = (struct sockaddr *)&sin6;
|
|
|
|
slen = sizeof(sin6);
|
|
|
|
} else {
|
|
|
|
memset(&sin, 0, sizeof(sin));
|
|
|
|
sin.sin_family = AF_INET;
|
2019-03-11 19:09:15 +01:00
|
|
|
memcpy(&sin.sin_addr, &bs->key.peer, sizeof(sin.sin_addr));
|
2018-06-27 16:29:02 +02:00
|
|
|
sin.sin_port =
|
|
|
|
(port) ? *port
|
2020-04-09 21:52:49 +02:00
|
|
|
: (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH))
|
2018-06-27 16:29:02 +02:00
|
|
|
? htons(BFD_DEF_MHOP_DEST_PORT)
|
|
|
|
: htons(BFD_DEFDESTPORT);
|
|
|
|
|
|
|
|
sd = bs->sock;
|
|
|
|
sa = (struct sockaddr *)&sin;
|
|
|
|
slen = sizeof(sin);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
|
|
|
|
sa->sa_len = slen;
|
|
|
|
#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
|
|
|
|
rv = sendto(sd, data, datalen, 0, sa, slen);
|
|
|
|
if (rv <= 0) {
|
2020-04-13 12:36:23 +02:00
|
|
|
if (bglobal.debug_network)
|
|
|
|
zlog_debug("packet-send: send failure: %s",
|
|
|
|
strerror(errno));
|
2018-06-27 16:29:02 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2020-04-13 12:36:23 +02:00
|
|
|
if (rv < (ssize_t)datalen) {
|
|
|
|
if (bglobal.debug_network)
|
|
|
|
zlog_debug("packet-send: send partial: %s",
|
|
|
|
strerror(errno));
|
|
|
|
}
|
2018-06-27 16:29:02 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-06-07 01:40:17 +02:00
|
|
|
#ifdef BFD_LINUX
|
|
|
|
/*
|
|
|
|
* Compute the UDP checksum.
|
|
|
|
*
|
|
|
|
* Checksum is not set in the packet, just computed.
|
|
|
|
*
|
|
|
|
* pkt
|
|
|
|
* Packet, fully filled out except for checksum field.
|
|
|
|
*
|
|
|
|
* pktsize
|
|
|
|
* sizeof(*pkt)
|
|
|
|
*
|
|
|
|
* ip
|
2023-03-02 10:57:17 +01:00
|
|
|
* IP address that pkt will be transmitted from and to.
|
2022-06-07 01:40:17 +02:00
|
|
|
*
|
|
|
|
* Returns:
|
|
|
|
* Checksum in network byte order.
|
|
|
|
*/
|
|
|
|
static uint16_t bfd_pkt_checksum(struct udphdr *pkt, size_t pktsize,
|
|
|
|
struct in6_addr *ip, sa_family_t family)
|
|
|
|
{
|
|
|
|
uint16_t chksum;
|
|
|
|
|
|
|
|
pkt->check = 0;
|
|
|
|
|
|
|
|
if (family == AF_INET6) {
|
|
|
|
struct ipv6_ph ph = {};
|
|
|
|
|
|
|
|
memcpy(&ph.src, ip, sizeof(ph.src));
|
|
|
|
memcpy(&ph.dst, ip, sizeof(ph.dst));
|
|
|
|
ph.ulpl = htons(pktsize);
|
|
|
|
ph.next_hdr = IPPROTO_UDP;
|
|
|
|
chksum = in_cksum_with_ph6(&ph, pkt, pktsize);
|
|
|
|
} else {
|
|
|
|
struct ipv4_ph ph = {};
|
|
|
|
|
|
|
|
memcpy(&ph.src, ip, sizeof(ph.src));
|
|
|
|
memcpy(&ph.dst, ip, sizeof(ph.dst));
|
|
|
|
ph.proto = IPPROTO_UDP;
|
|
|
|
ph.len = htons(pktsize);
|
|
|
|
chksum = in_cksum_with_ph4(&ph, pkt, pktsize);
|
|
|
|
}
|
|
|
|
|
|
|
|
return chksum;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This routine creates the entire ECHO packet so that it will be looped
|
|
|
|
* in the forwarding plane of the peer router instead of going up the
|
|
|
|
* stack in BFD to be looped. If we haven't learned the peers MAC yet
|
|
|
|
* no echo is sent.
|
|
|
|
*
|
|
|
|
* echo packet with src/dst IP equal to local IP
|
|
|
|
* dest MAC as peer's MAC
|
|
|
|
*
|
|
|
|
* currently support ipv4
|
|
|
|
*/
|
|
|
|
void ptm_bfd_echo_fp_snd(struct bfd_session *bfd)
|
|
|
|
{
|
|
|
|
int sd;
|
|
|
|
struct bfd_vrf_global *bvrf = bfd_vrf_look_by_session(bfd);
|
|
|
|
int total_len = 0;
|
|
|
|
struct ethhdr *eth;
|
|
|
|
struct udphdr *uh;
|
|
|
|
struct iphdr *iph;
|
|
|
|
struct bfd_echo_pkt *beph;
|
|
|
|
static char sendbuff[100];
|
2022-07-26 01:02:46 +02:00
|
|
|
struct timeval time_sent;
|
2022-06-07 01:40:17 +02:00
|
|
|
|
|
|
|
if (!bvrf)
|
|
|
|
return;
|
|
|
|
if (!CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_MAC_SET))
|
|
|
|
return;
|
|
|
|
if (!CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE))
|
|
|
|
SET_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE);
|
|
|
|
|
|
|
|
memset(sendbuff, 0, sizeof(sendbuff));
|
|
|
|
|
|
|
|
/* add eth hdr */
|
|
|
|
eth = (struct ethhdr *)(sendbuff);
|
2022-07-06 12:52:17 +02:00
|
|
|
memcpy(eth->h_source, bfd->ifp->hw_addr, sizeof(eth->h_source));
|
|
|
|
memcpy(eth->h_dest, bfd->peer_hw_addr, sizeof(eth->h_dest));
|
2022-06-07 01:40:17 +02:00
|
|
|
|
|
|
|
total_len += sizeof(struct ethhdr);
|
|
|
|
|
|
|
|
sd = bvrf->bg_echo;
|
|
|
|
eth->h_proto = htons(ETH_P_IP);
|
|
|
|
|
|
|
|
/* add ip hdr */
|
|
|
|
iph = (struct iphdr *)(sendbuff + sizeof(struct ethhdr));
|
|
|
|
|
|
|
|
iph->ihl = sizeof(struct ip) >> 2;
|
|
|
|
iph->version = IPVERSION;
|
|
|
|
iph->tos = IPTOS_PREC_INTERNETCONTROL;
|
|
|
|
iph->id = (uint16_t)frr_weak_random();
|
|
|
|
iph->ttl = BFD_TTL_VAL;
|
|
|
|
iph->protocol = IPPROTO_UDP;
|
|
|
|
memcpy(&iph->saddr, &bfd->local_address.sa_sin.sin_addr,
|
|
|
|
sizeof(bfd->local_address.sa_sin.sin_addr));
|
|
|
|
memcpy(&iph->daddr, &bfd->local_address.sa_sin.sin_addr,
|
|
|
|
sizeof(bfd->local_address.sa_sin.sin_addr));
|
|
|
|
total_len += sizeof(struct iphdr);
|
|
|
|
|
|
|
|
/* add udp hdr */
|
|
|
|
uh = (struct udphdr *)(sendbuff + sizeof(struct iphdr) +
|
|
|
|
sizeof(struct ethhdr));
|
|
|
|
uh->source = htons(BFD_DEF_ECHO_PORT);
|
|
|
|
uh->dest = htons(BFD_DEF_ECHO_PORT);
|
|
|
|
|
|
|
|
total_len += sizeof(struct udphdr);
|
|
|
|
|
|
|
|
/* add bfd echo */
|
|
|
|
beph = (struct bfd_echo_pkt *)(sendbuff + sizeof(struct udphdr) +
|
|
|
|
sizeof(struct iphdr) +
|
|
|
|
sizeof(struct ethhdr));
|
|
|
|
|
|
|
|
beph->ver = BFD_ECHO_VERSION;
|
|
|
|
beph->len = BFD_ECHO_PKT_LEN;
|
|
|
|
beph->my_discr = htonl(bfd->discrs.my_discr);
|
|
|
|
|
2022-07-26 01:02:46 +02:00
|
|
|
/* RTT calculation: add starting time in packet */
|
|
|
|
monotime(&time_sent);
|
|
|
|
beph->time_sent_sec = htobe64(time_sent.tv_sec);
|
|
|
|
beph->time_sent_usec = htobe64(time_sent.tv_usec);
|
|
|
|
|
2022-06-07 01:40:17 +02:00
|
|
|
total_len += sizeof(struct bfd_echo_pkt);
|
|
|
|
uh->len =
|
|
|
|
htons(total_len - sizeof(struct iphdr) - sizeof(struct ethhdr));
|
|
|
|
uh->check = bfd_pkt_checksum(
|
|
|
|
uh, (total_len - sizeof(struct iphdr) - sizeof(struct ethhdr)),
|
|
|
|
(struct in6_addr *)&iph->saddr, AF_INET);
|
|
|
|
|
|
|
|
iph->tot_len = htons(total_len - sizeof(struct ethhdr));
|
|
|
|
iph->check = in_cksum((const void *)iph, sizeof(struct iphdr));
|
|
|
|
|
|
|
|
if (bp_udp_send_fp(sd, (uint8_t *)&sendbuff, total_len, bfd) == -1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
bfd->stats.tx_echo_pkt++;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2018-06-27 16:29:02 +02:00
|
|
|
void ptm_bfd_echo_snd(struct bfd_session *bfd)
|
|
|
|
{
|
2019-03-11 19:09:15 +01:00
|
|
|
struct sockaddr *sa;
|
2018-08-29 20:17:49 +02:00
|
|
|
socklen_t salen;
|
|
|
|
int sd;
|
|
|
|
struct bfd_echo_pkt bep;
|
|
|
|
struct sockaddr_in sin;
|
|
|
|
struct sockaddr_in6 sin6;
|
2019-03-26 10:23:18 +01:00
|
|
|
struct bfd_vrf_global *bvrf = bfd_vrf_look_by_session(bfd);
|
2018-06-27 16:29:02 +02:00
|
|
|
|
2019-03-26 10:23:18 +01:00
|
|
|
if (!bvrf)
|
|
|
|
return;
|
2020-04-09 21:52:49 +02:00
|
|
|
if (!CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE))
|
|
|
|
SET_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE);
|
2018-06-27 16:29:02 +02:00
|
|
|
|
2018-08-29 20:17:49 +02:00
|
|
|
memset(&bep, 0, sizeof(bep));
|
|
|
|
bep.ver = BFD_ECHO_VERSION;
|
|
|
|
bep.len = BFD_ECHO_PKT_LEN;
|
|
|
|
bep.my_discr = htonl(bfd->discrs.my_discr);
|
|
|
|
|
2020-04-09 21:52:49 +02:00
|
|
|
if (CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_IPV6)) {
|
2020-08-05 19:10:27 +02:00
|
|
|
if (bvrf->bg_echov6 == -1)
|
|
|
|
return;
|
2019-03-26 10:23:18 +01:00
|
|
|
sd = bvrf->bg_echov6;
|
2019-03-11 19:09:15 +01:00
|
|
|
memset(&sin6, 0, sizeof(sin6));
|
2019-03-19 21:25:00 +01:00
|
|
|
sin6.sin6_family = AF_INET6;
|
2019-03-11 19:09:15 +01:00
|
|
|
memcpy(&sin6.sin6_addr, &bfd->key.peer, sizeof(sin6.sin6_addr));
|
|
|
|
if (bfd->ifp && IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr))
|
|
|
|
sin6.sin6_scope_id = bfd->ifp->ifindex;
|
|
|
|
|
2018-08-29 20:17:49 +02:00
|
|
|
sin6.sin6_port = htons(BFD_DEF_ECHO_PORT);
|
|
|
|
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
|
|
|
|
sin6.sin6_len = sizeof(sin6);
|
|
|
|
#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
|
|
|
|
|
2019-03-11 19:09:15 +01:00
|
|
|
sa = (struct sockaddr *)&sin6;
|
2018-08-29 20:17:49 +02:00
|
|
|
salen = sizeof(sin6);
|
2018-06-27 16:29:02 +02:00
|
|
|
} else {
|
2019-03-26 10:23:18 +01:00
|
|
|
sd = bvrf->bg_echo;
|
2021-01-30 20:31:47 +01:00
|
|
|
memset(&sin, 0, sizeof(sin));
|
2019-03-19 21:25:00 +01:00
|
|
|
sin.sin_family = AF_INET;
|
2019-03-11 19:09:15 +01:00
|
|
|
memcpy(&sin.sin_addr, &bfd->key.peer, sizeof(sin.sin_addr));
|
2018-08-29 20:17:49 +02:00
|
|
|
sin.sin_port = htons(BFD_DEF_ECHO_PORT);
|
|
|
|
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
|
|
|
|
sin.sin_len = sizeof(sin);
|
|
|
|
#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
|
2018-06-27 16:29:02 +02:00
|
|
|
|
2019-03-11 19:09:15 +01:00
|
|
|
sa = (struct sockaddr *)&sin;
|
2018-08-29 20:17:49 +02:00
|
|
|
salen = sizeof(sin);
|
2018-06-27 16:29:02 +02:00
|
|
|
}
|
2019-03-11 19:09:15 +01:00
|
|
|
if (bp_udp_send(sd, BFD_TTL_VAL, (uint8_t *)&bep, sizeof(bep), sa,
|
|
|
|
salen)
|
2018-08-29 20:17:49 +02:00
|
|
|
== -1)
|
|
|
|
return;
|
2018-06-27 16:29:02 +02:00
|
|
|
|
|
|
|
bfd->stats.tx_echo_pkt++;
|
|
|
|
}
|
|
|
|
|
2019-03-26 10:23:18 +01:00
|
|
|
static int ptm_bfd_process_echo_pkt(struct bfd_vrf_global *bvrf, int s)
|
2018-06-27 16:29:02 +02:00
|
|
|
{
|
|
|
|
struct bfd_session *bfd;
|
2018-08-29 20:17:49 +02:00
|
|
|
uint32_t my_discr = 0;
|
2022-07-26 01:02:46 +02:00
|
|
|
uint64_t my_rtt = 0;
|
2018-08-29 20:17:49 +02:00
|
|
|
uint8_t ttl = 0;
|
2018-06-27 16:29:02 +02:00
|
|
|
|
2018-08-29 20:17:49 +02:00
|
|
|
/* Receive and parse echo packet. */
|
2022-07-26 01:02:46 +02:00
|
|
|
if (bp_bfd_echo_in(bvrf, s, &ttl, &my_discr, &my_rtt) == -1)
|
2018-08-29 20:17:49 +02:00
|
|
|
return 0;
|
2018-06-27 16:29:02 +02:00
|
|
|
|
|
|
|
/* Your discriminator not zero - use it to find session */
|
|
|
|
bfd = bfd_id_lookup(my_discr);
|
|
|
|
if (bfd == NULL) {
|
2020-04-13 12:36:23 +02:00
|
|
|
if (bglobal.debug_network)
|
|
|
|
zlog_debug("echo-packet: no matching session (id:%u)",
|
|
|
|
my_discr);
|
2018-06-27 16:29:02 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2020-04-09 21:52:49 +02:00
|
|
|
if (!CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE)) {
|
2020-04-13 12:36:23 +02:00
|
|
|
if (bglobal.debug_network)
|
|
|
|
zlog_debug("echo-packet: echo disabled [%s] (id:%u)",
|
|
|
|
bs_to_string(bfd), my_discr);
|
2018-06-27 16:29:02 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2022-07-26 01:02:46 +02:00
|
|
|
/* RTT Calculation: add current RTT to samples */
|
|
|
|
if (my_rtt != 0) {
|
|
|
|
bfd->rtt[bfd->rtt_index] = my_rtt;
|
|
|
|
bfd->rtt_index++;
|
|
|
|
if (bfd->rtt_index >= BFD_RTT_SAMPLE)
|
|
|
|
bfd->rtt_index = 0;
|
|
|
|
if (bfd->rtt_valid < BFD_RTT_SAMPLE)
|
|
|
|
bfd->rtt_valid++;
|
|
|
|
}
|
|
|
|
|
2018-06-27 16:29:02 +02:00
|
|
|
bfd->stats.rx_echo_pkt++;
|
|
|
|
|
|
|
|
/* Compute detect time */
|
|
|
|
bfd->echo_detect_TO = bfd->remote_detect_mult * bfd->echo_xmt_TO;
|
|
|
|
|
|
|
|
/* Update echo receive timeout. */
|
2018-09-13 15:09:03 +02:00
|
|
|
if (bfd->echo_detect_TO > 0)
|
|
|
|
bfd_echo_recvtimer_update(bfd);
|
2018-06-27 16:29:02 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ptm_bfd_snd(struct bfd_session *bfd, int fbit)
|
|
|
|
{
|
2020-04-09 22:44:56 +02:00
|
|
|
struct bfd_pkt cp = {};
|
2018-06-27 16:29:02 +02:00
|
|
|
|
|
|
|
/* Set fields according to section 6.5.7 */
|
|
|
|
cp.diag = bfd->local_diag;
|
|
|
|
BFD_SETVER(cp.diag, BFD_VERSION);
|
|
|
|
cp.flags = 0;
|
|
|
|
BFD_SETSTATE(cp.flags, bfd->ses_state);
|
bfdd, lib, bgpd: add bfd cbit usage
bfd cbit is a value carried out in bfd messages, that permit to keep or
not, the independence between control plane and dataplane. In other
words, while most of the cases plan to flush entries, when bfd goes
down, there are some cases where that bfd event should be ignored. this
is the case with non stop forwarding mechanisms where entries may be
kept. this is the case for BGP, when graceful restart capability is
used. If BFD event down happens, and bgp is in graceful restart mode, it
is wished to ignore the BFD event while waiting for the remote router to
restart.
The changes take into account the following:
- add a config flag across zebra layer so that daemon can set or not the
cbit capability.
- ability for daemons to read the remote bfd capability associated to a bfd
notification.
- in bfdd, according to the value, the cbit value is set
- in bfdd, the received value is retrived and stored in the bfd session
context.
- by default, the local cbit announced to remote is set to 1 while
preservation of the local path is not set.
Signed-off-by: Philippe Guibert <philippe.guibert@6wind.com>
2019-04-15 17:20:25 +02:00
|
|
|
|
2020-04-09 21:52:49 +02:00
|
|
|
if (CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_CBIT))
|
bfdd, lib, bgpd: add bfd cbit usage
bfd cbit is a value carried out in bfd messages, that permit to keep or
not, the independence between control plane and dataplane. In other
words, while most of the cases plan to flush entries, when bfd goes
down, there are some cases where that bfd event should be ignored. this
is the case with non stop forwarding mechanisms where entries may be
kept. this is the case for BGP, when graceful restart capability is
used. If BFD event down happens, and bgp is in graceful restart mode, it
is wished to ignore the BFD event while waiting for the remote router to
restart.
The changes take into account the following:
- add a config flag across zebra layer so that daemon can set or not the
cbit capability.
- ability for daemons to read the remote bfd capability associated to a bfd
notification.
- in bfdd, according to the value, the cbit value is set
- in bfdd, the received value is retrived and stored in the bfd session
context.
- by default, the local cbit announced to remote is set to 1 while
preservation of the local path is not set.
Signed-off-by: Philippe Guibert <philippe.guibert@6wind.com>
2019-04-15 17:20:25 +02:00
|
|
|
BFD_SETCBIT(cp.flags, BFD_CBIT);
|
|
|
|
|
2018-06-27 16:29:02 +02:00
|
|
|
BFD_SETDEMANDBIT(cp.flags, BFD_DEF_DEMAND);
|
2019-01-30 21:11:55 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Polling and Final can't be set at the same time.
|
|
|
|
*
|
|
|
|
* RFC 5880, Section 6.5.
|
|
|
|
*/
|
2018-06-27 16:29:02 +02:00
|
|
|
BFD_SETFBIT(cp.flags, fbit);
|
2019-01-30 21:11:55 +01:00
|
|
|
if (fbit == 0)
|
|
|
|
BFD_SETPBIT(cp.flags, bfd->polling);
|
|
|
|
|
2018-06-27 16:29:02 +02:00
|
|
|
cp.detect_mult = bfd->detect_mult;
|
|
|
|
cp.len = BFD_PKT_LEN;
|
|
|
|
cp.discrs.my_discr = htonl(bfd->discrs.my_discr);
|
|
|
|
cp.discrs.remote_discr = htonl(bfd->discrs.remote_discr);
|
|
|
|
if (bfd->polling) {
|
|
|
|
cp.timers.desired_min_tx =
|
2019-01-31 21:10:32 +01:00
|
|
|
htonl(bfd->timers.desired_min_tx);
|
2018-06-27 16:29:02 +02:00
|
|
|
cp.timers.required_min_rx =
|
2019-01-31 21:10:32 +01:00
|
|
|
htonl(bfd->timers.required_min_rx);
|
2018-06-27 16:29:02 +02:00
|
|
|
} else {
|
2019-01-31 21:10:32 +01:00
|
|
|
/*
|
|
|
|
* We can only announce current setting on poll, this
|
|
|
|
* avoids timing mismatch with our peer and give it
|
|
|
|
* the oportunity to learn. See `bs_final_handler` for
|
|
|
|
* more information.
|
|
|
|
*/
|
|
|
|
cp.timers.desired_min_tx =
|
|
|
|
htonl(bfd->cur_timers.desired_min_tx);
|
|
|
|
cp.timers.required_min_rx =
|
|
|
|
htonl(bfd->cur_timers.required_min_rx);
|
2018-06-27 16:29:02 +02:00
|
|
|
}
|
2021-03-10 15:31:57 +01:00
|
|
|
cp.timers.required_min_echo = htonl(bfd->timers.required_min_echo_rx);
|
2018-06-27 16:29:02 +02:00
|
|
|
|
2018-08-29 20:17:49 +02:00
|
|
|
if (_ptm_bfd_send(bfd, NULL, &cp, BFD_PKT_LEN) != 0)
|
2018-06-27 16:29:02 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
bfd->stats.tx_ctrl_pkt++;
|
|
|
|
}
|
|
|
|
|
2022-06-07 01:40:17 +02:00
|
|
|
#ifdef BFD_LINUX
|
|
|
|
/*
|
|
|
|
* receive the ipv4 echo packet that was loopback in the peers forwarding plane
|
|
|
|
*/
|
|
|
|
ssize_t bfd_recv_ipv4_fp(int sd, uint8_t *msgbuf, size_t msgbuflen,
|
|
|
|
uint8_t *ttl, ifindex_t *ifindex,
|
|
|
|
struct sockaddr_any *local, struct sockaddr_any *peer)
|
|
|
|
{
|
|
|
|
ssize_t mlen;
|
|
|
|
struct sockaddr_ll msgaddr;
|
|
|
|
struct msghdr msghdr;
|
|
|
|
struct iovec iov[1];
|
|
|
|
uint16_t recv_checksum;
|
|
|
|
uint16_t checksum;
|
|
|
|
struct iphdr *ip;
|
|
|
|
struct udphdr *uh;
|
|
|
|
|
|
|
|
/* Prepare the recvmsg params. */
|
|
|
|
iov[0].iov_base = msgbuf;
|
|
|
|
iov[0].iov_len = msgbuflen;
|
|
|
|
|
|
|
|
memset(&msghdr, 0, sizeof(msghdr));
|
|
|
|
msghdr.msg_name = &msgaddr;
|
|
|
|
msghdr.msg_namelen = sizeof(msgaddr);
|
|
|
|
msghdr.msg_iov = iov;
|
|
|
|
msghdr.msg_iovlen = 1;
|
|
|
|
|
|
|
|
mlen = recvmsg(sd, &msghdr, MSG_DONTWAIT);
|
|
|
|
if (mlen == -1) {
|
|
|
|
if (errno != EAGAIN || errno != EWOULDBLOCK || errno != EINTR)
|
|
|
|
zlog_err("%s: recv failed: %s", __func__,
|
|
|
|
strerror(errno));
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ip = (struct iphdr *)(msgbuf + sizeof(struct ethhdr));
|
|
|
|
|
|
|
|
/* verify ip checksum */
|
|
|
|
recv_checksum = ip->check;
|
|
|
|
ip->check = 0;
|
|
|
|
checksum = in_cksum((const void *)ip, sizeof(struct iphdr));
|
|
|
|
if (recv_checksum != checksum) {
|
|
|
|
if (bglobal.debug_network)
|
|
|
|
zlog_debug(
|
|
|
|
"%s: invalid iphdr checksum expected 0x%x rcvd 0x%x",
|
|
|
|
__func__, checksum, recv_checksum);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
*ttl = ip->ttl;
|
|
|
|
if (*ttl != 254) {
|
|
|
|
if (bglobal.debug_network)
|
|
|
|
zlog_debug("%s: invalid TTL: %u", __func__, *ttl);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
local->sa_sin.sin_family = AF_INET;
|
|
|
|
memcpy(&local->sa_sin.sin_addr, &ip->saddr, sizeof(ip->saddr));
|
|
|
|
peer->sa_sin.sin_family = AF_INET;
|
|
|
|
memcpy(&peer->sa_sin.sin_addr, &ip->daddr, sizeof(ip->daddr));
|
|
|
|
|
|
|
|
*ifindex = msgaddr.sll_ifindex;
|
|
|
|
|
|
|
|
/* verify udp checksum */
|
|
|
|
uh = (struct udphdr *)(msgbuf + sizeof(struct iphdr) +
|
|
|
|
sizeof(struct ethhdr));
|
|
|
|
recv_checksum = uh->check;
|
|
|
|
uh->check = 0;
|
|
|
|
checksum = bfd_pkt_checksum(uh, ntohs(uh->len),
|
|
|
|
(struct in6_addr *)&ip->saddr, AF_INET);
|
|
|
|
if (recv_checksum != checksum) {
|
|
|
|
if (bglobal.debug_network)
|
|
|
|
zlog_debug(
|
|
|
|
"%s: invalid udphdr checksum expected 0x%x rcvd 0x%x",
|
|
|
|
__func__, checksum, recv_checksum);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return mlen;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2018-08-29 19:17:29 +02:00
|
|
|
ssize_t bfd_recv_ipv4(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl,
|
2019-02-01 12:22:00 +01:00
|
|
|
ifindex_t *ifindex, struct sockaddr_any *local,
|
2018-08-29 19:17:29 +02:00
|
|
|
struct sockaddr_any *peer)
|
2018-06-27 16:29:02 +02:00
|
|
|
{
|
|
|
|
struct cmsghdr *cm;
|
|
|
|
ssize_t mlen;
|
2018-08-14 20:10:25 +02:00
|
|
|
struct sockaddr_in msgaddr;
|
|
|
|
struct msghdr msghdr;
|
|
|
|
struct iovec iov[1];
|
|
|
|
uint8_t cmsgbuf[255];
|
|
|
|
|
|
|
|
/* Prepare the recvmsg params. */
|
|
|
|
iov[0].iov_base = msgbuf;
|
2018-08-29 19:17:29 +02:00
|
|
|
iov[0].iov_len = msgbuflen;
|
2018-08-14 20:10:25 +02:00
|
|
|
|
|
|
|
memset(&msghdr, 0, sizeof(msghdr));
|
|
|
|
msghdr.msg_name = &msgaddr;
|
|
|
|
msghdr.msg_namelen = sizeof(msgaddr);
|
|
|
|
msghdr.msg_iov = iov;
|
|
|
|
msghdr.msg_iovlen = 1;
|
|
|
|
msghdr.msg_control = cmsgbuf;
|
|
|
|
msghdr.msg_controllen = sizeof(cmsgbuf);
|
|
|
|
|
2018-06-27 16:29:02 +02:00
|
|
|
mlen = recvmsg(sd, &msghdr, MSG_DONTWAIT);
|
|
|
|
if (mlen == -1) {
|
2018-07-25 05:03:47 +02:00
|
|
|
if (errno != EAGAIN)
|
2020-04-10 16:06:22 +02:00
|
|
|
zlog_err("ipv4-recv: recv failed: %s", strerror(errno));
|
2018-07-25 05:03:47 +02:00
|
|
|
|
2018-06-27 16:29:02 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get source address */
|
|
|
|
peer->sa_sin = *((struct sockaddr_in *)(msghdr.msg_name));
|
|
|
|
|
|
|
|
/* Get and check TTL */
|
|
|
|
for (cm = CMSG_FIRSTHDR(&msghdr); cm != NULL;
|
|
|
|
cm = CMSG_NXTHDR(&msghdr, cm)) {
|
|
|
|
if (cm->cmsg_level != IPPROTO_IP)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
switch (cm->cmsg_type) {
|
|
|
|
#ifdef BFD_LINUX
|
|
|
|
case IP_TTL: {
|
2018-08-29 19:17:29 +02:00
|
|
|
uint32_t ttlval;
|
|
|
|
|
|
|
|
memcpy(&ttlval, CMSG_DATA(cm), sizeof(ttlval));
|
|
|
|
if (ttlval > 255) {
|
2020-04-13 12:36:23 +02:00
|
|
|
if (bglobal.debug_network)
|
2022-09-05 10:39:35 +02:00
|
|
|
zlog_debug("%s: invalid TTL: %u",
|
|
|
|
__func__, ttlval);
|
2018-06-27 16:29:02 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2018-08-29 19:17:29 +02:00
|
|
|
*ttl = ttlval;
|
2018-06-27 16:29:02 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case IP_PKTINFO: {
|
|
|
|
struct in_pktinfo *pi =
|
|
|
|
(struct in_pktinfo *)CMSG_DATA(cm);
|
|
|
|
|
|
|
|
if (pi == NULL)
|
|
|
|
break;
|
|
|
|
|
|
|
|
local->sa_sin.sin_family = AF_INET;
|
|
|
|
local->sa_sin.sin_addr = pi->ipi_addr;
|
2018-11-30 18:02:23 +01:00
|
|
|
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
|
|
|
|
local->sa_sin.sin_len = sizeof(local->sa_sin);
|
|
|
|
#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
|
2019-01-10 20:13:32 +01:00
|
|
|
|
2019-02-01 12:22:00 +01:00
|
|
|
*ifindex = pi->ipi_ifindex;
|
2018-06-27 16:29:02 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
#endif /* BFD_LINUX */
|
|
|
|
#ifdef BFD_BSD
|
|
|
|
case IP_RECVTTL: {
|
2018-08-29 19:17:29 +02:00
|
|
|
memcpy(ttl, CMSG_DATA(cm), sizeof(*ttl));
|
2018-06-27 16:29:02 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case IP_RECVDSTADDR: {
|
|
|
|
struct in_addr ia;
|
|
|
|
|
|
|
|
memcpy(&ia, CMSG_DATA(cm), sizeof(ia));
|
|
|
|
local->sa_sin.sin_family = AF_INET;
|
|
|
|
local->sa_sin.sin_addr = ia;
|
2018-11-30 18:02:23 +01:00
|
|
|
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
|
|
|
|
local->sa_sin.sin_len = sizeof(local->sa_sin);
|
|
|
|
#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
|
2018-06-27 16:29:02 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
#endif /* BFD_BSD */
|
|
|
|
|
|
|
|
default:
|
|
|
|
/*
|
|
|
|
* On *BSDs we expect to land here when skipping
|
|
|
|
* the IP_RECVIF header. It will be handled by
|
|
|
|
* getsockopt_ifindex() below.
|
|
|
|
*/
|
|
|
|
/* NOTHING */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* OS agnostic way of getting interface name. */
|
2019-02-01 12:22:00 +01:00
|
|
|
if (*ifindex == IFINDEX_INTERNAL)
|
|
|
|
*ifindex = getsockopt_ifindex(AF_INET, &msghdr);
|
2018-06-27 16:29:02 +02:00
|
|
|
|
|
|
|
return mlen;
|
|
|
|
}
|
|
|
|
|
2018-08-29 19:17:29 +02:00
|
|
|
ssize_t bfd_recv_ipv6(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl,
|
2019-02-01 12:22:00 +01:00
|
|
|
ifindex_t *ifindex, struct sockaddr_any *local,
|
2018-08-29 19:17:29 +02:00
|
|
|
struct sockaddr_any *peer)
|
2018-06-27 16:29:02 +02:00
|
|
|
{
|
|
|
|
struct cmsghdr *cm;
|
|
|
|
struct in6_pktinfo *pi6 = NULL;
|
|
|
|
ssize_t mlen;
|
2018-08-28 17:33:45 +02:00
|
|
|
uint32_t ttlval;
|
2018-08-14 20:10:25 +02:00
|
|
|
struct sockaddr_in6 msgaddr6;
|
|
|
|
struct msghdr msghdr6;
|
|
|
|
struct iovec iov[1];
|
|
|
|
uint8_t cmsgbuf6[255];
|
|
|
|
|
|
|
|
/* Prepare the recvmsg params. */
|
|
|
|
iov[0].iov_base = msgbuf;
|
2018-08-29 19:17:29 +02:00
|
|
|
iov[0].iov_len = msgbuflen;
|
2018-08-14 20:10:25 +02:00
|
|
|
|
|
|
|
memset(&msghdr6, 0, sizeof(msghdr6));
|
|
|
|
msghdr6.msg_name = &msgaddr6;
|
|
|
|
msghdr6.msg_namelen = sizeof(msgaddr6);
|
|
|
|
msghdr6.msg_iov = iov;
|
|
|
|
msghdr6.msg_iovlen = 1;
|
|
|
|
msghdr6.msg_control = cmsgbuf6;
|
|
|
|
msghdr6.msg_controllen = sizeof(cmsgbuf6);
|
|
|
|
|
2018-06-27 16:29:02 +02:00
|
|
|
mlen = recvmsg(sd, &msghdr6, MSG_DONTWAIT);
|
|
|
|
if (mlen == -1) {
|
2018-07-25 05:03:47 +02:00
|
|
|
if (errno != EAGAIN)
|
2020-04-10 16:06:22 +02:00
|
|
|
zlog_err("ipv6-recv: recv failed: %s", strerror(errno));
|
2018-07-25 05:03:47 +02:00
|
|
|
|
2018-06-27 16:29:02 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get source address */
|
|
|
|
peer->sa_sin6 = *((struct sockaddr_in6 *)(msghdr6.msg_name));
|
|
|
|
|
|
|
|
/* Get and check TTL */
|
|
|
|
for (cm = CMSG_FIRSTHDR(&msghdr6); cm != NULL;
|
|
|
|
cm = CMSG_NXTHDR(&msghdr6, cm)) {
|
|
|
|
if (cm->cmsg_level != IPPROTO_IPV6)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (cm->cmsg_type == IPV6_HOPLIMIT) {
|
2018-08-28 17:33:45 +02:00
|
|
|
memcpy(&ttlval, CMSG_DATA(cm), sizeof(ttlval));
|
2018-08-29 19:17:29 +02:00
|
|
|
if (ttlval > 255) {
|
2020-04-13 12:36:23 +02:00
|
|
|
if (bglobal.debug_network)
|
2022-09-05 10:39:35 +02:00
|
|
|
zlog_debug("%s: invalid TTL: %u",
|
|
|
|
__func__, ttlval);
|
2018-06-27 16:29:02 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2018-08-29 19:17:29 +02:00
|
|
|
|
|
|
|
*ttl = ttlval;
|
2018-06-27 16:29:02 +02:00
|
|
|
} else if (cm->cmsg_type == IPV6_PKTINFO) {
|
|
|
|
pi6 = (struct in6_pktinfo *)CMSG_DATA(cm);
|
|
|
|
if (pi6) {
|
2018-11-30 18:02:23 +01:00
|
|
|
local->sa_sin6.sin6_family = AF_INET6;
|
2018-06-27 16:29:02 +02:00
|
|
|
local->sa_sin6.sin6_addr = pi6->ipi6_addr;
|
2018-11-30 18:02:23 +01:00
|
|
|
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
|
|
|
|
local->sa_sin6.sin6_len = sizeof(local->sa_sin6);
|
|
|
|
#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
|
2019-01-10 20:13:32 +01:00
|
|
|
|
2019-02-01 12:22:00 +01:00
|
|
|
*ifindex = pi6->ipi6_ifindex;
|
2019-06-04 21:06:26 +02:00
|
|
|
|
|
|
|
/* Set scope ID for link local addresses. */
|
|
|
|
if (IN6_IS_ADDR_LINKLOCAL(
|
|
|
|
&peer->sa_sin6.sin6_addr))
|
|
|
|
peer->sa_sin6.sin6_scope_id = *ifindex;
|
|
|
|
if (IN6_IS_ADDR_LINKLOCAL(
|
|
|
|
&local->sa_sin6.sin6_addr))
|
|
|
|
local->sa_sin6.sin6_scope_id = *ifindex;
|
2018-06-27 16:29:02 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return mlen;
|
|
|
|
}
|
|
|
|
|
2019-03-26 10:23:18 +01:00
|
|
|
static void bfd_sd_reschedule(struct bfd_vrf_global *bvrf, int sd)
|
2018-06-27 16:29:02 +02:00
|
|
|
{
|
2019-03-26 10:23:18 +01:00
|
|
|
if (sd == bvrf->bg_shop) {
|
2022-12-25 16:26:52 +01:00
|
|
|
EVENT_OFF(bvrf->bg_ev[0]);
|
2022-05-20 20:19:08 +02:00
|
|
|
event_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_shop,
|
|
|
|
&bvrf->bg_ev[0]);
|
2019-03-26 10:23:18 +01:00
|
|
|
} else if (sd == bvrf->bg_mhop) {
|
2022-12-25 16:26:52 +01:00
|
|
|
EVENT_OFF(bvrf->bg_ev[1]);
|
2022-05-20 20:19:08 +02:00
|
|
|
event_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_mhop,
|
|
|
|
&bvrf->bg_ev[1]);
|
2019-03-26 10:23:18 +01:00
|
|
|
} else if (sd == bvrf->bg_shop6) {
|
2022-12-25 16:26:52 +01:00
|
|
|
EVENT_OFF(bvrf->bg_ev[2]);
|
2022-05-20 20:19:08 +02:00
|
|
|
event_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_shop6,
|
|
|
|
&bvrf->bg_ev[2]);
|
2019-03-26 10:23:18 +01:00
|
|
|
} else if (sd == bvrf->bg_mhop6) {
|
2022-12-25 16:26:52 +01:00
|
|
|
EVENT_OFF(bvrf->bg_ev[3]);
|
2022-05-20 20:19:08 +02:00
|
|
|
event_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_mhop6,
|
|
|
|
&bvrf->bg_ev[3]);
|
2019-03-26 10:23:18 +01:00
|
|
|
} else if (sd == bvrf->bg_echo) {
|
2022-12-25 16:26:52 +01:00
|
|
|
EVENT_OFF(bvrf->bg_ev[4]);
|
2022-05-20 20:19:08 +02:00
|
|
|
event_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_echo,
|
|
|
|
&bvrf->bg_ev[4]);
|
2019-03-26 10:23:18 +01:00
|
|
|
} else if (sd == bvrf->bg_echov6) {
|
2022-12-25 16:26:52 +01:00
|
|
|
EVENT_OFF(bvrf->bg_ev[5]);
|
2022-05-20 20:19:08 +02:00
|
|
|
event_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_echov6,
|
|
|
|
&bvrf->bg_ev[5]);
|
2018-06-27 16:29:02 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-26 14:21:02 +01:00
|
|
|
PRINTFRR(6, 7)
|
2018-07-25 05:03:47 +02:00
|
|
|
static void cp_debug(bool mhop, struct sockaddr_any *peer,
|
2019-02-01 12:22:00 +01:00
|
|
|
struct sockaddr_any *local, ifindex_t ifindex,
|
|
|
|
vrf_id_t vrfid, const char *fmt, ...)
|
2018-07-25 05:03:47 +02:00
|
|
|
{
|
|
|
|
char buf[512], peerstr[128], localstr[128], portstr[64], vrfstr[64];
|
|
|
|
va_list vl;
|
|
|
|
|
2020-04-13 12:36:23 +02:00
|
|
|
/* Don't to any processing if debug is disabled. */
|
|
|
|
if (bglobal.debug_network == false)
|
|
|
|
return;
|
|
|
|
|
2018-07-25 05:03:47 +02:00
|
|
|
if (peer->sa_sin.sin_family)
|
|
|
|
snprintf(peerstr, sizeof(peerstr), " peer:%s", satostr(peer));
|
|
|
|
else
|
|
|
|
peerstr[0] = 0;
|
|
|
|
|
|
|
|
if (local->sa_sin.sin_family)
|
|
|
|
snprintf(localstr, sizeof(localstr), " local:%s",
|
|
|
|
satostr(local));
|
|
|
|
else
|
|
|
|
localstr[0] = 0;
|
|
|
|
|
2019-02-01 12:22:00 +01:00
|
|
|
if (ifindex != IFINDEX_INTERNAL)
|
|
|
|
snprintf(portstr, sizeof(portstr), " port:%u", ifindex);
|
2018-07-25 05:03:47 +02:00
|
|
|
else
|
|
|
|
portstr[0] = 0;
|
|
|
|
|
2019-02-01 12:22:00 +01:00
|
|
|
if (vrfid != VRF_DEFAULT)
|
|
|
|
snprintf(vrfstr, sizeof(vrfstr), " vrf:%u", vrfid);
|
2018-07-25 05:03:47 +02:00
|
|
|
else
|
|
|
|
vrfstr[0] = 0;
|
|
|
|
|
|
|
|
va_start(vl, fmt);
|
|
|
|
vsnprintf(buf, sizeof(buf), fmt, vl);
|
|
|
|
va_end(vl);
|
|
|
|
|
2020-04-10 16:06:22 +02:00
|
|
|
zlog_debug("control-packet: %s [mhop:%s%s%s%s%s]", buf,
|
|
|
|
mhop ? "yes" : "no", peerstr, localstr, portstr, vrfstr);
|
2018-07-25 05:03:47 +02:00
|
|
|
}
|
|
|
|
|
2023-04-25 19:48:18 +02:00
|
|
|
static bool bfd_check_auth(const struct bfd_session *bfd,
|
|
|
|
const struct bfd_pkt *cp)
|
|
|
|
{
|
|
|
|
if (CHECK_FLAG(cp->flags, BFD_ABIT)) {
|
|
|
|
/* RFC5880 4.1: Authentication Section is present. */
|
|
|
|
struct bfd_auth *auth = (struct bfd_auth *)(cp + 1);
|
|
|
|
uint16_t pkt_auth_type = ntohs(auth->type);
|
|
|
|
|
|
|
|
if (cp->len < BFD_PKT_LEN + sizeof(struct bfd_auth))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (cp->len < BFD_PKT_LEN + auth->length)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
switch (pkt_auth_type) {
|
|
|
|
case BFD_AUTH_NULL:
|
|
|
|
return false;
|
|
|
|
case BFD_AUTH_SIMPLE:
|
|
|
|
/* RFC5880 6.7: To be finshed. */
|
|
|
|
return false;
|
|
|
|
case BFD_AUTH_CRYPTOGRAPHIC:
|
|
|
|
/* RFC5880 6.7: To be finshed. */
|
|
|
|
return false;
|
|
|
|
default:
|
|
|
|
/* RFC5880 6.7: To be finshed. */
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-03-01 22:18:12 +01:00
|
|
|
void bfd_recv_cb(struct event *t)
|
2018-06-27 16:29:02 +02:00
|
|
|
{
|
2022-12-25 16:26:52 +01:00
|
|
|
int sd = EVENT_FD(t);
|
2018-06-27 16:29:02 +02:00
|
|
|
struct bfd_session *bfd;
|
|
|
|
struct bfd_pkt *cp;
|
2018-08-28 21:20:52 +02:00
|
|
|
bool is_mhop;
|
2018-06-27 16:29:02 +02:00
|
|
|
ssize_t mlen = 0;
|
2019-02-05 01:05:39 +01:00
|
|
|
uint8_t ttl = 0;
|
2019-11-14 14:29:09 +01:00
|
|
|
vrf_id_t vrfid;
|
2019-02-01 12:22:00 +01:00
|
|
|
ifindex_t ifindex = IFINDEX_INTERNAL;
|
2018-06-27 16:29:02 +02:00
|
|
|
struct sockaddr_any local, peer;
|
2018-08-29 19:17:29 +02:00
|
|
|
uint8_t msgbuf[1516];
|
2021-01-08 10:38:04 +01:00
|
|
|
struct interface *ifp = NULL;
|
2022-12-25 16:26:52 +01:00
|
|
|
struct bfd_vrf_global *bvrf = EVENT_ARG(t);
|
2018-06-27 16:29:02 +02:00
|
|
|
|
|
|
|
/* Schedule next read. */
|
2019-03-26 10:23:18 +01:00
|
|
|
bfd_sd_reschedule(bvrf, sd);
|
2018-06-27 16:29:02 +02:00
|
|
|
|
2018-07-25 05:03:47 +02:00
|
|
|
/* Handle echo packets. */
|
2019-03-26 10:23:18 +01:00
|
|
|
if (sd == bvrf->bg_echo || sd == bvrf->bg_echov6) {
|
|
|
|
ptm_bfd_process_echo_pkt(bvrf, sd);
|
2022-02-23 01:04:25 +01:00
|
|
|
return;
|
2018-06-27 16:29:02 +02:00
|
|
|
}
|
|
|
|
|
2018-08-15 23:00:24 +02:00
|
|
|
/* Sanitize input/output. */
|
|
|
|
memset(&local, 0, sizeof(local));
|
|
|
|
memset(&peer, 0, sizeof(peer));
|
|
|
|
|
2018-07-25 05:03:47 +02:00
|
|
|
/* Handle control packets. */
|
2018-08-28 21:20:52 +02:00
|
|
|
is_mhop = false;
|
2019-03-26 10:23:18 +01:00
|
|
|
if (sd == bvrf->bg_shop || sd == bvrf->bg_mhop) {
|
|
|
|
is_mhop = sd == bvrf->bg_mhop;
|
2019-02-01 12:22:00 +01:00
|
|
|
mlen = bfd_recv_ipv4(sd, msgbuf, sizeof(msgbuf), &ttl, &ifindex,
|
2018-08-29 19:17:29 +02:00
|
|
|
&local, &peer);
|
2019-03-26 10:23:18 +01:00
|
|
|
} else if (sd == bvrf->bg_shop6 || sd == bvrf->bg_mhop6) {
|
|
|
|
is_mhop = sd == bvrf->bg_mhop6;
|
2019-02-01 12:22:00 +01:00
|
|
|
mlen = bfd_recv_ipv6(sd, msgbuf, sizeof(msgbuf), &ttl, &ifindex,
|
2018-08-29 19:17:29 +02:00
|
|
|
&local, &peer);
|
2018-06-27 16:29:02 +02:00
|
|
|
}
|
|
|
|
|
2021-10-14 17:55:33 +02:00
|
|
|
/*
|
|
|
|
* With netns backend, we have a separate socket in each VRF. It means
|
|
|
|
* that bvrf here is correct and we believe the bvrf->vrf->vrf_id.
|
|
|
|
* With VRF-lite backend, we have a single socket in the default VRF.
|
|
|
|
* It means that we can't believe the bvrf->vrf->vrf_id. But in
|
|
|
|
* VRF-lite, the ifindex is globally unique, so we can retrieve the
|
|
|
|
* correct vrf_id from the interface.
|
2021-01-08 10:38:04 +01:00
|
|
|
*/
|
2021-10-14 17:55:33 +02:00
|
|
|
vrfid = bvrf->vrf->vrf_id;
|
2021-01-08 10:38:04 +01:00
|
|
|
if (ifindex) {
|
|
|
|
ifp = if_lookup_by_index(ifindex, vrfid);
|
|
|
|
if (ifp)
|
2021-10-22 00:17:40 +02:00
|
|
|
vrfid = ifp->vrf->vrf_id;
|
2021-01-08 10:38:04 +01:00
|
|
|
}
|
|
|
|
|
2018-06-27 16:29:02 +02:00
|
|
|
/* Implement RFC 5880 6.8.6 */
|
|
|
|
if (mlen < BFD_PKT_LEN) {
|
2019-02-01 12:22:00 +01:00
|
|
|
cp_debug(is_mhop, &peer, &local, ifindex, vrfid,
|
2023-01-27 11:14:35 +01:00
|
|
|
"too small (%zd bytes)", mlen);
|
2022-02-23 01:04:25 +01:00
|
|
|
return;
|
2018-06-27 16:29:02 +02:00
|
|
|
}
|
|
|
|
|
2020-08-11 19:43:56 +02:00
|
|
|
/* Validate single hop packet TTL. */
|
2019-02-25 19:51:33 +01:00
|
|
|
if ((!is_mhop) && (ttl != BFD_TTL_VAL)) {
|
2019-02-01 12:22:00 +01:00
|
|
|
cp_debug(is_mhop, &peer, &local, ifindex, vrfid,
|
2018-08-29 19:17:29 +02:00
|
|
|
"invalid TTL: %d expected %d", ttl, BFD_TTL_VAL);
|
2022-02-23 01:04:25 +01:00
|
|
|
return;
|
2018-08-29 19:17:29 +02:00
|
|
|
}
|
|
|
|
|
2018-07-25 05:03:47 +02:00
|
|
|
/*
|
|
|
|
* Parse the control header for inconsistencies:
|
|
|
|
* - Invalid version;
|
|
|
|
* - Bad multiplier configuration;
|
|
|
|
* - Short packets;
|
|
|
|
* - Invalid discriminator;
|
|
|
|
*/
|
2018-08-14 20:10:25 +02:00
|
|
|
cp = (struct bfd_pkt *)(msgbuf);
|
2018-06-27 16:29:02 +02:00
|
|
|
if (BFD_GETVER(cp->diag) != BFD_VERSION) {
|
2019-02-01 12:22:00 +01:00
|
|
|
cp_debug(is_mhop, &peer, &local, ifindex, vrfid,
|
2018-07-25 05:03:47 +02:00
|
|
|
"bad version %d", BFD_GETVER(cp->diag));
|
2022-02-23 01:04:25 +01:00
|
|
|
return;
|
2018-06-27 16:29:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (cp->detect_mult == 0) {
|
2019-02-01 12:22:00 +01:00
|
|
|
cp_debug(is_mhop, &peer, &local, ifindex, vrfid,
|
2018-07-25 05:03:47 +02:00
|
|
|
"detect multiplier set to zero");
|
2022-02-23 01:04:25 +01:00
|
|
|
return;
|
2018-06-27 16:29:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if ((cp->len < BFD_PKT_LEN) || (cp->len > mlen)) {
|
2019-02-01 12:22:00 +01:00
|
|
|
cp_debug(is_mhop, &peer, &local, ifindex, vrfid, "too small");
|
2022-02-23 01:04:25 +01:00
|
|
|
return;
|
2018-06-27 16:29:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (cp->discrs.my_discr == 0) {
|
2019-02-01 12:22:00 +01:00
|
|
|
cp_debug(is_mhop, &peer, &local, ifindex, vrfid,
|
2018-07-25 05:03:47 +02:00
|
|
|
"'my discriminator' is zero");
|
2022-02-23 01:04:25 +01:00
|
|
|
return;
|
2018-06-27 16:29:02 +02:00
|
|
|
}
|
|
|
|
|
2018-07-25 05:03:47 +02:00
|
|
|
/* Find the session that this packet belongs. */
|
2021-10-14 17:55:33 +02:00
|
|
|
bfd = ptm_bfd_sess_find(cp, &peer, &local, ifp, vrfid, is_mhop);
|
2018-06-27 16:29:02 +02:00
|
|
|
if (bfd == NULL) {
|
2019-02-01 12:22:00 +01:00
|
|
|
cp_debug(is_mhop, &peer, &local, ifindex, vrfid,
|
2018-07-25 05:03:47 +02:00
|
|
|
"no session found");
|
2022-02-23 01:04:25 +01:00
|
|
|
return;
|
2018-06-27 16:29:02 +02:00
|
|
|
}
|
bfdd: allow l3vrf bfd sessions without udp leaking
Until now, when in vrf-lite mode, the BFD implementation
creates a single UDP socket and relies on the following
sysctl value to 1:
echo 1 > /proc/sys/net/ipv4/udp_l3mdev_accept
With this setting, the incoming BFD packets from a given
vrf, would leak to the default vrf, and would match the
UDP socket.
The drawback of this solution is that udp packets received
on a given vrf may leak to an other vrf. This may be a
security concern.
The commit addresses this issue by avoiding this leak
mechanism. An UDP socket is created for each vrf, and each
socket uses new setsockopt option: SO_REUSEADDR + SO_REUSEPORT.
With this option, the incoming UDP packets are distributed on
the available sockets. The impact of those options with l3mdev
devices is unknown. It has been observed that this option is not
needed, until the default vrf sockets are created.
To ensure the BFD packets are correctly routed to the appropriate
socket, a BPF filter has been put in place and attached to the
sockets : SO_ATTACH_REUSEPORT_CBPF. This option adds a criterium
to force the packet to choose a given socket. If initial criteria
from the default distribution algorithm were not good, at least
two sockets would be available, and the CBPF would force the
selection to the same socket. This would come to the situation
where an incoming packet would be processed on a different vrf.
The bpf code is the following one:
struct sock_filter code[] = {
{ BPF_RET | BPF_K, 0, 0, 0 },
};
struct sock_fprog p = {
.len = sizeof(code)/sizeof(struct sock_filter),
.filter = code,
};
if (setsockopt(sd, SOL_SOCKET, SO_ATTACH_REUSEPORT_CBPF, &p, sizeof(p))) {
zlog_warn("unable to set SO_ATTACH_REUSEPORT_CBPF on socket: %s",
strerror(errno));
return -1;
}
Some tests have been done with by creating vrf contexts, and by using
the below vtysh configuration:
ip route 2.2.2.2/32 10.126.0.2
vrf vrf2
ip route 2.2.2.2/32 10.126.0.2
!
interface ntfp2
ip address 10.126.0.1/24
!
interface ntfp3 vrf vrf4
ip address 10.126.0.1/24
!
interface ntfp2 vrf vrf1
ip address 10.126.0.1/24
!
interface ntfp2.100 vrf vrf2
ip address 10.126.0.1/24
!
interface ntfp2.200 vrf vrf3
ip address 10.126.0.1/24
!
line vty
!
bfd
peer 10.126.0.2 vrf vrf2
!
peer 10.126.0.2 vrf vrf3
!
peer 10.126.0.2
!
peer 10.126.0.2 vrf vrf4
!
peer 2.2.2.2 multihop local-address 1.1.1.1
!
peer 2.2.2.2 multihop local-address 1.1.1.1 vrf vrf2
transmit-interval 1500
receive-interval 1500
!
The results showed no issue related to packets received by
the wrong vrf. Even changing the udp_l3mdev_accept flag to
1 did not change the test results.
Signed-off-by: Philippe Guibert <philippe.guibert@6wind.com>
2022-07-07 14:33:48 +02:00
|
|
|
/*
|
|
|
|
* We may have a situation where received packet is on wrong vrf
|
|
|
|
*/
|
2023-05-10 16:04:33 +02:00
|
|
|
if (bfd && bfd->vrf && bfd->vrf->vrf_id != vrfid) {
|
bfdd: allow l3vrf bfd sessions without udp leaking
Until now, when in vrf-lite mode, the BFD implementation
creates a single UDP socket and relies on the following
sysctl value to 1:
echo 1 > /proc/sys/net/ipv4/udp_l3mdev_accept
With this setting, the incoming BFD packets from a given
vrf, would leak to the default vrf, and would match the
UDP socket.
The drawback of this solution is that udp packets received
on a given vrf may leak to an other vrf. This may be a
security concern.
The commit addresses this issue by avoiding this leak
mechanism. An UDP socket is created for each vrf, and each
socket uses new setsockopt option: SO_REUSEADDR + SO_REUSEPORT.
With this option, the incoming UDP packets are distributed on
the available sockets. The impact of those options with l3mdev
devices is unknown. It has been observed that this option is not
needed, until the default vrf sockets are created.
To ensure the BFD packets are correctly routed to the appropriate
socket, a BPF filter has been put in place and attached to the
sockets : SO_ATTACH_REUSEPORT_CBPF. This option adds a criterium
to force the packet to choose a given socket. If initial criteria
from the default distribution algorithm were not good, at least
two sockets would be available, and the CBPF would force the
selection to the same socket. This would come to the situation
where an incoming packet would be processed on a different vrf.
The bpf code is the following one:
struct sock_filter code[] = {
{ BPF_RET | BPF_K, 0, 0, 0 },
};
struct sock_fprog p = {
.len = sizeof(code)/sizeof(struct sock_filter),
.filter = code,
};
if (setsockopt(sd, SOL_SOCKET, SO_ATTACH_REUSEPORT_CBPF, &p, sizeof(p))) {
zlog_warn("unable to set SO_ATTACH_REUSEPORT_CBPF on socket: %s",
strerror(errno));
return -1;
}
Some tests have been done with by creating vrf contexts, and by using
the below vtysh configuration:
ip route 2.2.2.2/32 10.126.0.2
vrf vrf2
ip route 2.2.2.2/32 10.126.0.2
!
interface ntfp2
ip address 10.126.0.1/24
!
interface ntfp3 vrf vrf4
ip address 10.126.0.1/24
!
interface ntfp2 vrf vrf1
ip address 10.126.0.1/24
!
interface ntfp2.100 vrf vrf2
ip address 10.126.0.1/24
!
interface ntfp2.200 vrf vrf3
ip address 10.126.0.1/24
!
line vty
!
bfd
peer 10.126.0.2 vrf vrf2
!
peer 10.126.0.2 vrf vrf3
!
peer 10.126.0.2
!
peer 10.126.0.2 vrf vrf4
!
peer 2.2.2.2 multihop local-address 1.1.1.1
!
peer 2.2.2.2 multihop local-address 1.1.1.1 vrf vrf2
transmit-interval 1500
receive-interval 1500
!
The results showed no issue related to packets received by
the wrong vrf. Even changing the udp_l3mdev_accept flag to
1 did not change the test results.
Signed-off-by: Philippe Guibert <philippe.guibert@6wind.com>
2022-07-07 14:33:48 +02:00
|
|
|
cp_debug(is_mhop, &peer, &local, ifindex, vrfid,
|
|
|
|
"wrong vrfid.");
|
|
|
|
return;
|
|
|
|
}
|
2018-06-27 16:29:02 +02:00
|
|
|
|
bfdd: fix override between sessions
After two single-hop sessions (*no local address are configured*) on two
interfaces are UP, remove one address of one interface, both of them
(actually, quite independent sessions) come to be DOWN, not just one.
Consider two boxes: A with `a1` and `a2` adddress on two interfaces,
and B with `b1` and `b2`.
Two sessions are set up and ok: `s1` with <a1,b1> and `s2` with <a2,b2>.
After `a1` of A is removed, there is an unhappy coincidence:
1) On A: `s1` changes local address, and sends <a2,b1> packets with help
of route.
2) On B: wrongly regarded <a2,b1> packets with non-zero remote descriminator
as part of `s2`, and are dropped for mismatched remote remote descriminator.
3) On A: `s1` sends <a2,b1> packets with zero remote descriminator to
initialize this session.
4) On B: wrongly regarded <a2,b1> packets with zero remote descriminator as
part of `s2`. Then `s2` will vibrate.
So the good sessions are overridden.
In this case, the <a2,b1> packets with zero remote descriminator won't take
effect until the current good sessions become bad.
Since single-hop sessions are allowed to be set without bound inteface in
current code, this commit adds one check in `bfd_recv_cb()` to avoid wrong
override.
Signed-off-by: anlan_cs <vic.lan@pica8.com>
2022-05-03 15:26:15 +02:00
|
|
|
/* Ensure that existing good sessions are not overridden. */
|
|
|
|
if (!cp->discrs.remote_discr && bfd->ses_state != PTM_BFD_DOWN &&
|
|
|
|
bfd->ses_state != PTM_BFD_ADM_DOWN) {
|
|
|
|
cp_debug(is_mhop, &peer, &local, ifindex, vrfid,
|
|
|
|
"'remote discriminator' is zero, not overridden");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-07-25 05:03:47 +02:00
|
|
|
/*
|
|
|
|
* Multi hop: validate packet TTL.
|
2022-06-07 01:40:17 +02:00
|
|
|
* Single hop: set local address that received the packet.
|
|
|
|
* set peers mac address for echo packets
|
2018-07-25 05:03:47 +02:00
|
|
|
*/
|
2018-06-27 16:29:02 +02:00
|
|
|
if (is_mhop) {
|
2020-08-11 19:43:56 +02:00
|
|
|
if (ttl < bfd->mh_ttl) {
|
2019-02-01 12:22:00 +01:00
|
|
|
cp_debug(is_mhop, &peer, &local, ifindex, vrfid,
|
2018-07-25 05:03:47 +02:00
|
|
|
"exceeded max hop count (expected %d, got %d)",
|
2020-08-11 19:43:56 +02:00
|
|
|
bfd->mh_ttl, ttl);
|
2022-02-23 01:04:25 +01:00
|
|
|
return;
|
2018-06-27 16:29:02 +02:00
|
|
|
}
|
2022-06-07 01:40:17 +02:00
|
|
|
} else {
|
|
|
|
|
|
|
|
if (bfd->local_address.sa_sin.sin_family == AF_UNSPEC)
|
|
|
|
bfd->local_address = local;
|
|
|
|
#ifdef BFD_LINUX
|
|
|
|
if (ifp)
|
|
|
|
bfd_peer_mac_set(sd, bfd, &peer, ifp);
|
|
|
|
#endif
|
2018-06-27 16:29:02 +02:00
|
|
|
}
|
|
|
|
|
2022-01-18 11:00:45 +01:00
|
|
|
bfd->stats.rx_ctrl_pkt++;
|
|
|
|
|
2018-06-27 16:29:02 +02:00
|
|
|
/*
|
|
|
|
* If no interface was detected, save the interface where the
|
|
|
|
* packet came in.
|
|
|
|
*/
|
2021-05-28 13:33:44 +02:00
|
|
|
if (!is_mhop && bfd->ifp == NULL)
|
2021-10-14 17:55:33 +02:00
|
|
|
bfd->ifp = ifp;
|
2018-06-27 16:29:02 +02:00
|
|
|
|
2018-07-25 05:03:47 +02:00
|
|
|
/* Log remote discriminator changes. */
|
2018-06-27 16:29:02 +02:00
|
|
|
if ((bfd->discrs.remote_discr != 0)
|
2018-07-25 05:03:47 +02:00
|
|
|
&& (bfd->discrs.remote_discr != ntohl(cp->discrs.my_discr)))
|
2019-02-01 12:22:00 +01:00
|
|
|
cp_debug(is_mhop, &peer, &local, ifindex, vrfid,
|
2019-02-04 16:51:24 +01:00
|
|
|
"remote discriminator mismatch (expected %u, got %u)",
|
2018-07-25 05:03:47 +02:00
|
|
|
bfd->discrs.remote_discr, ntohl(cp->discrs.my_discr));
|
2018-06-27 16:29:02 +02:00
|
|
|
|
|
|
|
bfd->discrs.remote_discr = ntohl(cp->discrs.my_discr);
|
|
|
|
|
2023-04-25 19:48:18 +02:00
|
|
|
/* Check authentication. */
|
|
|
|
if (!bfd_check_auth(bfd, cp)) {
|
|
|
|
cp_debug(is_mhop, &peer, &local, ifindex, vrfid,
|
|
|
|
"Authentication failed");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-06-27 16:29:02 +02:00
|
|
|
/* Save remote diagnostics before state switch. */
|
|
|
|
bfd->remote_diag = cp->diag & BFD_DIAGMASK;
|
|
|
|
|
2019-01-29 20:33:16 +01:00
|
|
|
/* Update remote timers settings. */
|
|
|
|
bfd->remote_timers.desired_min_tx = ntohl(cp->timers.desired_min_tx);
|
|
|
|
bfd->remote_timers.required_min_rx = ntohl(cp->timers.required_min_rx);
|
|
|
|
bfd->remote_timers.required_min_echo =
|
|
|
|
ntohl(cp->timers.required_min_echo);
|
|
|
|
bfd->remote_detect_mult = cp->detect_mult;
|
|
|
|
|
bfdd, lib, bgpd: add bfd cbit usage
bfd cbit is a value carried out in bfd messages, that permit to keep or
not, the independence between control plane and dataplane. In other
words, while most of the cases plan to flush entries, when bfd goes
down, there are some cases where that bfd event should be ignored. this
is the case with non stop forwarding mechanisms where entries may be
kept. this is the case for BGP, when graceful restart capability is
used. If BFD event down happens, and bgp is in graceful restart mode, it
is wished to ignore the BFD event while waiting for the remote router to
restart.
The changes take into account the following:
- add a config flag across zebra layer so that daemon can set or not the
cbit capability.
- ability for daemons to read the remote bfd capability associated to a bfd
notification.
- in bfdd, according to the value, the cbit value is set
- in bfdd, the received value is retrived and stored in the bfd session
context.
- by default, the local cbit announced to remote is set to 1 while
preservation of the local path is not set.
Signed-off-by: Philippe Guibert <philippe.guibert@6wind.com>
2019-04-15 17:20:25 +02:00
|
|
|
if (BFD_GETCBIT(cp->flags))
|
|
|
|
bfd->remote_cbit = 1;
|
|
|
|
else
|
|
|
|
bfd->remote_cbit = 0;
|
|
|
|
|
2019-01-15 23:23:06 +01:00
|
|
|
/* State switch from section 6.2. */
|
|
|
|
bs_state_handler(bfd, BFD_GETSTATE(cp->flags));
|
2018-06-27 16:29:02 +02:00
|
|
|
|
2019-01-29 20:33:16 +01:00
|
|
|
/* RFC 5880, Section 6.5: handle POLL/FINAL negotiation sequence. */
|
|
|
|
if (bfd->polling && BFD_GETFBIT(cp->flags)) {
|
2022-01-24 02:25:21 +01:00
|
|
|
/* Disable polling. */
|
2019-01-29 20:33:16 +01:00
|
|
|
bfd->polling = 0;
|
2018-06-27 16:29:02 +02:00
|
|
|
|
2019-01-29 20:33:16 +01:00
|
|
|
/* Handle poll finalization. */
|
|
|
|
bs_final_handler(bfd);
|
2018-06-27 16:29:02 +02:00
|
|
|
}
|
|
|
|
|
2021-11-24 13:01:41 +01:00
|
|
|
/*
|
|
|
|
* Detection timeout calculation:
|
|
|
|
* The minimum detection timeout is the remote detection
|
|
|
|
* multipler (number of packets to be missed) times the agreed
|
|
|
|
* transmission interval.
|
|
|
|
*
|
|
|
|
* RFC 5880, Section 6.8.4.
|
|
|
|
*/
|
|
|
|
if (bfd->cur_timers.required_min_rx > bfd->remote_timers.desired_min_tx)
|
|
|
|
bfd->detect_TO = bfd->remote_detect_mult
|
|
|
|
* bfd->cur_timers.required_min_rx;
|
|
|
|
else
|
|
|
|
bfd->detect_TO = bfd->remote_detect_mult
|
|
|
|
* bfd->remote_timers.desired_min_tx;
|
|
|
|
|
|
|
|
/* Apply new receive timer immediately. */
|
|
|
|
bfd_recvtimer_update(bfd);
|
|
|
|
|
2019-01-29 20:33:16 +01:00
|
|
|
/* Handle echo timers changes. */
|
|
|
|
bs_echo_timer_handler(bfd);
|
2018-06-27 16:29:02 +02:00
|
|
|
|
|
|
|
/*
|
2019-01-29 20:33:16 +01:00
|
|
|
* We've received a packet with the POLL bit set, we must send
|
|
|
|
* a control packet back with the FINAL bit set.
|
|
|
|
*
|
|
|
|
* RFC 5880, Section 6.5.
|
2018-06-27 16:29:02 +02:00
|
|
|
*/
|
2019-02-04 16:44:52 +01:00
|
|
|
if (BFD_GETPBIT(cp->flags)) {
|
|
|
|
/* We are finalizing a poll negotiation. */
|
|
|
|
bs_final_handler(bfd);
|
|
|
|
|
|
|
|
/* Send the control packet with the final bit immediately. */
|
2019-01-29 20:33:16 +01:00
|
|
|
ptm_bfd_snd(bfd, 1);
|
2019-02-04 16:44:52 +01:00
|
|
|
}
|
2018-06-27 16:29:02 +02:00
|
|
|
}
|
|
|
|
|
2018-08-29 20:17:49 +02:00
|
|
|
/*
|
|
|
|
* bp_bfd_echo_in: proccesses an BFD echo packet. On TTL == BFD_TTL_VAL
|
|
|
|
* the packet is looped back or returns the my discriminator ID along
|
|
|
|
* with the TTL.
|
|
|
|
*
|
|
|
|
* Returns -1 on error or loopback or 0 on success.
|
|
|
|
*/
|
2022-07-26 01:02:46 +02:00
|
|
|
int bp_bfd_echo_in(struct bfd_vrf_global *bvrf, int sd, uint8_t *ttl,
|
|
|
|
uint32_t *my_discr, uint64_t *my_rtt)
|
2018-08-29 20:17:49 +02:00
|
|
|
{
|
|
|
|
struct bfd_echo_pkt *bep;
|
|
|
|
ssize_t rlen;
|
|
|
|
struct sockaddr_any local, peer;
|
2019-02-01 12:22:00 +01:00
|
|
|
ifindex_t ifindex = IFINDEX_INTERNAL;
|
|
|
|
vrf_id_t vrfid = VRF_DEFAULT;
|
2018-08-29 20:17:49 +02:00
|
|
|
uint8_t msgbuf[1516];
|
2022-06-07 01:40:17 +02:00
|
|
|
size_t bfd_offset = 0;
|
|
|
|
|
|
|
|
if (sd == bvrf->bg_echo) {
|
|
|
|
#ifdef BFD_LINUX
|
|
|
|
rlen = bfd_recv_ipv4_fp(sd, msgbuf, sizeof(msgbuf), ttl,
|
|
|
|
&ifindex, &local, &peer);
|
2018-08-29 20:17:49 +02:00
|
|
|
|
2022-06-07 01:40:17 +02:00
|
|
|
/* silently drop echo packet that is looped in fastpath but
|
|
|
|
* still comes up to BFD
|
|
|
|
*/
|
|
|
|
if (rlen == -1)
|
|
|
|
return -1;
|
|
|
|
bfd_offset = sizeof(struct udphdr) + sizeof(struct iphdr) +
|
|
|
|
sizeof(struct ethhdr);
|
|
|
|
#else
|
2019-02-01 12:22:00 +01:00
|
|
|
rlen = bfd_recv_ipv4(sd, msgbuf, sizeof(msgbuf), ttl, &ifindex,
|
2018-08-29 20:17:49 +02:00
|
|
|
&local, &peer);
|
2022-06-07 01:40:17 +02:00
|
|
|
bfd_offset = 0;
|
|
|
|
#endif
|
|
|
|
} else {
|
2019-02-01 12:22:00 +01:00
|
|
|
rlen = bfd_recv_ipv6(sd, msgbuf, sizeof(msgbuf), ttl, &ifindex,
|
2018-08-29 20:17:49 +02:00
|
|
|
&local, &peer);
|
2022-06-07 01:40:17 +02:00
|
|
|
bfd_offset = 0;
|
|
|
|
}
|
2018-08-29 20:17:49 +02:00
|
|
|
|
|
|
|
/* Short packet, better not risk reading it. */
|
|
|
|
if (rlen < (ssize_t)sizeof(*bep)) {
|
2019-02-01 12:22:00 +01:00
|
|
|
cp_debug(false, &peer, &local, ifindex, vrfid,
|
2018-08-29 20:17:49 +02:00
|
|
|
"small echo packet");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2022-06-07 01:40:17 +02:00
|
|
|
/* Test for loopback for ipv6, ipv4 is looped in forwarding plane */
|
|
|
|
if ((*ttl == BFD_TTL_VAL) && (sd == bvrf->bg_echov6)) {
|
2018-08-29 20:17:49 +02:00
|
|
|
bp_udp_send(sd, *ttl - 1, msgbuf, rlen,
|
|
|
|
(struct sockaddr *)&peer,
|
2019-03-26 10:23:18 +01:00
|
|
|
(sd == bvrf->bg_echo) ? sizeof(peer.sa_sin)
|
2018-08-29 20:17:49 +02:00
|
|
|
: sizeof(peer.sa_sin6));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Read my discriminator from BFD Echo packet. */
|
2022-06-07 01:40:17 +02:00
|
|
|
bep = (struct bfd_echo_pkt *)(msgbuf + bfd_offset);
|
2018-08-29 20:17:49 +02:00
|
|
|
*my_discr = ntohl(bep->my_discr);
|
|
|
|
if (*my_discr == 0) {
|
2019-02-01 12:22:00 +01:00
|
|
|
cp_debug(false, &peer, &local, ifindex, vrfid,
|
2018-08-29 20:17:49 +02:00
|
|
|
"invalid echo packet discriminator (zero)");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2022-07-26 01:02:46 +02:00
|
|
|
#ifdef BFD_LINUX
|
|
|
|
/* RTT Calculation: determine RTT time of IPv4 echo pkt */
|
|
|
|
if (sd == bvrf->bg_echo) {
|
|
|
|
struct timeval time_sent = {0, 0};
|
|
|
|
|
|
|
|
time_sent.tv_sec = be64toh(bep->time_sent_sec);
|
|
|
|
time_sent.tv_usec = be64toh(bep->time_sent_usec);
|
|
|
|
*my_rtt = monotime_since(&time_sent, NULL);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2018-08-29 20:17:49 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-06-07 01:40:17 +02:00
|
|
|
#ifdef BFD_LINUX
|
|
|
|
/*
|
|
|
|
* send a bfd packet with src/dst same IP so that the peer will receive
|
|
|
|
* the packet and forward it back to sender in the forwarding plane
|
|
|
|
*/
|
|
|
|
int bp_udp_send_fp(int sd, uint8_t *data, size_t datalen,
|
|
|
|
struct bfd_session *bfd)
|
|
|
|
{
|
|
|
|
ssize_t wlen;
|
2022-07-26 01:02:46 +02:00
|
|
|
struct msghdr msg = {0};
|
2022-06-07 01:40:17 +02:00
|
|
|
struct iovec iov[1];
|
|
|
|
uint8_t msgctl[255];
|
2022-07-26 01:02:46 +02:00
|
|
|
struct sockaddr_ll sadr_ll = {0};
|
2022-06-07 01:40:17 +02:00
|
|
|
|
|
|
|
sadr_ll.sll_ifindex = bfd->ifp->ifindex;
|
|
|
|
sadr_ll.sll_halen = ETH_ALEN;
|
|
|
|
memcpy(sadr_ll.sll_addr, bfd->peer_hw_addr, sizeof(bfd->peer_hw_addr));
|
|
|
|
sadr_ll.sll_protocol = htons(ETH_P_IP);
|
|
|
|
|
|
|
|
/* Prepare message data. */
|
|
|
|
iov[0].iov_base = data;
|
|
|
|
iov[0].iov_len = datalen;
|
|
|
|
|
|
|
|
memset(msgctl, 0, sizeof(msgctl));
|
|
|
|
msg.msg_name = &sadr_ll;
|
|
|
|
msg.msg_namelen = sizeof(sadr_ll);
|
|
|
|
msg.msg_iov = iov;
|
|
|
|
msg.msg_iovlen = 1;
|
|
|
|
|
|
|
|
/* Send echo to peer */
|
|
|
|
wlen = sendmsg(sd, &msg, 0);
|
|
|
|
|
|
|
|
if (wlen <= 0) {
|
|
|
|
if (bglobal.debug_network)
|
2022-09-05 10:39:35 +02:00
|
|
|
zlog_debug("%s: loopback failure: (%d) %s", __func__,
|
|
|
|
errno, strerror(errno));
|
2022-06-07 01:40:17 +02:00
|
|
|
return -1;
|
|
|
|
} else if (wlen < (ssize_t)datalen) {
|
|
|
|
if (bglobal.debug_network)
|
2022-09-05 10:39:35 +02:00
|
|
|
zlog_debug("%s: partial send: %zd expected %zu",
|
|
|
|
__func__, wlen, datalen);
|
2022-06-07 01:40:17 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2018-08-29 20:17:49 +02:00
|
|
|
int bp_udp_send(int sd, uint8_t ttl, uint8_t *data, size_t datalen,
|
|
|
|
struct sockaddr *to, socklen_t tolen)
|
|
|
|
{
|
|
|
|
struct cmsghdr *cmsg;
|
|
|
|
ssize_t wlen;
|
|
|
|
int ttlval = ttl;
|
|
|
|
bool is_ipv6 = to->sa_family == AF_INET6;
|
|
|
|
struct msghdr msg;
|
|
|
|
struct iovec iov[1];
|
|
|
|
uint8_t msgctl[255];
|
|
|
|
|
|
|
|
/* Prepare message data. */
|
|
|
|
iov[0].iov_base = data;
|
|
|
|
iov[0].iov_len = datalen;
|
|
|
|
|
|
|
|
memset(&msg, 0, sizeof(msg));
|
|
|
|
memset(msgctl, 0, sizeof(msgctl));
|
|
|
|
msg.msg_name = to;
|
|
|
|
msg.msg_namelen = tolen;
|
|
|
|
msg.msg_iov = iov;
|
|
|
|
msg.msg_iovlen = 1;
|
|
|
|
|
|
|
|
/* Prepare the packet TTL information. */
|
|
|
|
if (ttl > 0) {
|
|
|
|
/* Use ancillary data. */
|
|
|
|
msg.msg_control = msgctl;
|
|
|
|
msg.msg_controllen = CMSG_LEN(sizeof(ttlval));
|
|
|
|
|
|
|
|
/* Configure the ancillary data. */
|
|
|
|
cmsg = CMSG_FIRSTHDR(&msg);
|
|
|
|
cmsg->cmsg_len = CMSG_LEN(sizeof(ttlval));
|
|
|
|
if (is_ipv6) {
|
|
|
|
cmsg->cmsg_level = IPPROTO_IPV6;
|
|
|
|
cmsg->cmsg_type = IPV6_HOPLIMIT;
|
|
|
|
} else {
|
2020-03-30 11:22:38 +02:00
|
|
|
#ifdef BFD_LINUX
|
2018-08-29 20:17:49 +02:00
|
|
|
cmsg->cmsg_level = IPPROTO_IP;
|
|
|
|
cmsg->cmsg_type = IP_TTL;
|
|
|
|
#else
|
|
|
|
/* FreeBSD does not support TTL in ancillary data. */
|
|
|
|
msg.msg_control = NULL;
|
|
|
|
msg.msg_controllen = 0;
|
|
|
|
|
|
|
|
bp_set_ttl(sd, ttl);
|
|
|
|
#endif /* BFD_BSD */
|
|
|
|
}
|
|
|
|
memcpy(CMSG_DATA(cmsg), &ttlval, sizeof(ttlval));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Send echo back. */
|
|
|
|
wlen = sendmsg(sd, &msg, 0);
|
|
|
|
if (wlen <= 0) {
|
2020-04-13 12:36:23 +02:00
|
|
|
if (bglobal.debug_network)
|
2022-09-05 10:39:35 +02:00
|
|
|
zlog_debug("%s: loopback failure: (%d) %s", __func__,
|
|
|
|
errno, strerror(errno));
|
2018-08-29 20:17:49 +02:00
|
|
|
return -1;
|
|
|
|
} else if (wlen < (ssize_t)datalen) {
|
2020-04-13 12:36:23 +02:00
|
|
|
if (bglobal.debug_network)
|
2022-09-05 10:39:35 +02:00
|
|
|
zlog_debug("%s: partial send: %zd expected %zu",
|
|
|
|
__func__, wlen, datalen);
|
2018-08-29 20:17:49 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-06-27 16:29:02 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Sockets creation.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* IPv4 sockets
|
|
|
|
*/
|
2018-08-28 17:33:45 +02:00
|
|
|
int bp_set_ttl(int sd, uint8_t value)
|
2018-06-27 16:29:02 +02:00
|
|
|
{
|
2018-08-28 17:33:45 +02:00
|
|
|
int ttl = value;
|
|
|
|
|
|
|
|
if (setsockopt(sd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)) == -1) {
|
2022-09-05 10:39:35 +02:00
|
|
|
zlog_warn("%s: setsockopt(IP_TTL, %d): %s", __func__, value,
|
2020-04-10 16:06:22 +02:00
|
|
|
strerror(errno));
|
2018-06-27 16:29:02 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-08-28 17:33:45 +02:00
|
|
|
int bp_set_tos(int sd, uint8_t value)
|
2018-06-27 16:29:02 +02:00
|
|
|
{
|
2018-08-28 17:33:45 +02:00
|
|
|
int tos = value;
|
|
|
|
|
|
|
|
if (setsockopt(sd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) == -1) {
|
2022-09-05 10:39:35 +02:00
|
|
|
zlog_warn("%s: setsockopt(IP_TOS, %d): %s", __func__, value,
|
2020-04-10 16:06:22 +02:00
|
|
|
strerror(errno));
|
2018-06-27 16:29:02 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
bfdd: allow l3vrf bfd sessions without udp leaking
Until now, when in vrf-lite mode, the BFD implementation
creates a single UDP socket and relies on the following
sysctl value to 1:
echo 1 > /proc/sys/net/ipv4/udp_l3mdev_accept
With this setting, the incoming BFD packets from a given
vrf, would leak to the default vrf, and would match the
UDP socket.
The drawback of this solution is that udp packets received
on a given vrf may leak to an other vrf. This may be a
security concern.
The commit addresses this issue by avoiding this leak
mechanism. An UDP socket is created for each vrf, and each
socket uses new setsockopt option: SO_REUSEADDR + SO_REUSEPORT.
With this option, the incoming UDP packets are distributed on
the available sockets. The impact of those options with l3mdev
devices is unknown. It has been observed that this option is not
needed, until the default vrf sockets are created.
To ensure the BFD packets are correctly routed to the appropriate
socket, a BPF filter has been put in place and attached to the
sockets : SO_ATTACH_REUSEPORT_CBPF. This option adds a criterium
to force the packet to choose a given socket. If initial criteria
from the default distribution algorithm were not good, at least
two sockets would be available, and the CBPF would force the
selection to the same socket. This would come to the situation
where an incoming packet would be processed on a different vrf.
The bpf code is the following one:
struct sock_filter code[] = {
{ BPF_RET | BPF_K, 0, 0, 0 },
};
struct sock_fprog p = {
.len = sizeof(code)/sizeof(struct sock_filter),
.filter = code,
};
if (setsockopt(sd, SOL_SOCKET, SO_ATTACH_REUSEPORT_CBPF, &p, sizeof(p))) {
zlog_warn("unable to set SO_ATTACH_REUSEPORT_CBPF on socket: %s",
strerror(errno));
return -1;
}
Some tests have been done with by creating vrf contexts, and by using
the below vtysh configuration:
ip route 2.2.2.2/32 10.126.0.2
vrf vrf2
ip route 2.2.2.2/32 10.126.0.2
!
interface ntfp2
ip address 10.126.0.1/24
!
interface ntfp3 vrf vrf4
ip address 10.126.0.1/24
!
interface ntfp2 vrf vrf1
ip address 10.126.0.1/24
!
interface ntfp2.100 vrf vrf2
ip address 10.126.0.1/24
!
interface ntfp2.200 vrf vrf3
ip address 10.126.0.1/24
!
line vty
!
bfd
peer 10.126.0.2 vrf vrf2
!
peer 10.126.0.2 vrf vrf3
!
peer 10.126.0.2
!
peer 10.126.0.2 vrf vrf4
!
peer 2.2.2.2 multihop local-address 1.1.1.1
!
peer 2.2.2.2 multihop local-address 1.1.1.1 vrf vrf2
transmit-interval 1500
receive-interval 1500
!
The results showed no issue related to packets received by
the wrong vrf. Even changing the udp_l3mdev_accept flag to
1 did not change the test results.
Signed-off-by: Philippe Guibert <philippe.guibert@6wind.com>
2022-07-07 14:33:48 +02:00
|
|
|
static bool bp_set_reuse_addr(int sd)
|
|
|
|
{
|
|
|
|
int one = 1;
|
|
|
|
|
|
|
|
if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) == -1) {
|
2022-09-05 10:39:35 +02:00
|
|
|
zlog_warn("%s: setsockopt(SO_REUSEADDR, %d): %s", __func__, one,
|
|
|
|
strerror(errno));
|
bfdd: allow l3vrf bfd sessions without udp leaking
Until now, when in vrf-lite mode, the BFD implementation
creates a single UDP socket and relies on the following
sysctl value to 1:
echo 1 > /proc/sys/net/ipv4/udp_l3mdev_accept
With this setting, the incoming BFD packets from a given
vrf, would leak to the default vrf, and would match the
UDP socket.
The drawback of this solution is that udp packets received
on a given vrf may leak to an other vrf. This may be a
security concern.
The commit addresses this issue by avoiding this leak
mechanism. An UDP socket is created for each vrf, and each
socket uses new setsockopt option: SO_REUSEADDR + SO_REUSEPORT.
With this option, the incoming UDP packets are distributed on
the available sockets. The impact of those options with l3mdev
devices is unknown. It has been observed that this option is not
needed, until the default vrf sockets are created.
To ensure the BFD packets are correctly routed to the appropriate
socket, a BPF filter has been put in place and attached to the
sockets : SO_ATTACH_REUSEPORT_CBPF. This option adds a criterium
to force the packet to choose a given socket. If initial criteria
from the default distribution algorithm were not good, at least
two sockets would be available, and the CBPF would force the
selection to the same socket. This would come to the situation
where an incoming packet would be processed on a different vrf.
The bpf code is the following one:
struct sock_filter code[] = {
{ BPF_RET | BPF_K, 0, 0, 0 },
};
struct sock_fprog p = {
.len = sizeof(code)/sizeof(struct sock_filter),
.filter = code,
};
if (setsockopt(sd, SOL_SOCKET, SO_ATTACH_REUSEPORT_CBPF, &p, sizeof(p))) {
zlog_warn("unable to set SO_ATTACH_REUSEPORT_CBPF on socket: %s",
strerror(errno));
return -1;
}
Some tests have been done with by creating vrf contexts, and by using
the below vtysh configuration:
ip route 2.2.2.2/32 10.126.0.2
vrf vrf2
ip route 2.2.2.2/32 10.126.0.2
!
interface ntfp2
ip address 10.126.0.1/24
!
interface ntfp3 vrf vrf4
ip address 10.126.0.1/24
!
interface ntfp2 vrf vrf1
ip address 10.126.0.1/24
!
interface ntfp2.100 vrf vrf2
ip address 10.126.0.1/24
!
interface ntfp2.200 vrf vrf3
ip address 10.126.0.1/24
!
line vty
!
bfd
peer 10.126.0.2 vrf vrf2
!
peer 10.126.0.2 vrf vrf3
!
peer 10.126.0.2
!
peer 10.126.0.2 vrf vrf4
!
peer 2.2.2.2 multihop local-address 1.1.1.1
!
peer 2.2.2.2 multihop local-address 1.1.1.1 vrf vrf2
transmit-interval 1500
receive-interval 1500
!
The results showed no issue related to packets received by
the wrong vrf. Even changing the udp_l3mdev_accept flag to
1 did not change the test results.
Signed-off-by: Philippe Guibert <philippe.guibert@6wind.com>
2022-07-07 14:33:48 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool bp_set_reuse_port(int sd)
|
|
|
|
{
|
|
|
|
int one = 1;
|
|
|
|
|
|
|
|
if (setsockopt(sd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)) == -1) {
|
2022-09-05 10:39:35 +02:00
|
|
|
zlog_warn("%s: setsockopt(SO_REUSEPORT, %d): %s", __func__, one,
|
|
|
|
strerror(errno));
|
bfdd: allow l3vrf bfd sessions without udp leaking
Until now, when in vrf-lite mode, the BFD implementation
creates a single UDP socket and relies on the following
sysctl value to 1:
echo 1 > /proc/sys/net/ipv4/udp_l3mdev_accept
With this setting, the incoming BFD packets from a given
vrf, would leak to the default vrf, and would match the
UDP socket.
The drawback of this solution is that udp packets received
on a given vrf may leak to an other vrf. This may be a
security concern.
The commit addresses this issue by avoiding this leak
mechanism. An UDP socket is created for each vrf, and each
socket uses new setsockopt option: SO_REUSEADDR + SO_REUSEPORT.
With this option, the incoming UDP packets are distributed on
the available sockets. The impact of those options with l3mdev
devices is unknown. It has been observed that this option is not
needed, until the default vrf sockets are created.
To ensure the BFD packets are correctly routed to the appropriate
socket, a BPF filter has been put in place and attached to the
sockets : SO_ATTACH_REUSEPORT_CBPF. This option adds a criterium
to force the packet to choose a given socket. If initial criteria
from the default distribution algorithm were not good, at least
two sockets would be available, and the CBPF would force the
selection to the same socket. This would come to the situation
where an incoming packet would be processed on a different vrf.
The bpf code is the following one:
struct sock_filter code[] = {
{ BPF_RET | BPF_K, 0, 0, 0 },
};
struct sock_fprog p = {
.len = sizeof(code)/sizeof(struct sock_filter),
.filter = code,
};
if (setsockopt(sd, SOL_SOCKET, SO_ATTACH_REUSEPORT_CBPF, &p, sizeof(p))) {
zlog_warn("unable to set SO_ATTACH_REUSEPORT_CBPF on socket: %s",
strerror(errno));
return -1;
}
Some tests have been done with by creating vrf contexts, and by using
the below vtysh configuration:
ip route 2.2.2.2/32 10.126.0.2
vrf vrf2
ip route 2.2.2.2/32 10.126.0.2
!
interface ntfp2
ip address 10.126.0.1/24
!
interface ntfp3 vrf vrf4
ip address 10.126.0.1/24
!
interface ntfp2 vrf vrf1
ip address 10.126.0.1/24
!
interface ntfp2.100 vrf vrf2
ip address 10.126.0.1/24
!
interface ntfp2.200 vrf vrf3
ip address 10.126.0.1/24
!
line vty
!
bfd
peer 10.126.0.2 vrf vrf2
!
peer 10.126.0.2 vrf vrf3
!
peer 10.126.0.2
!
peer 10.126.0.2 vrf vrf4
!
peer 2.2.2.2 multihop local-address 1.1.1.1
!
peer 2.2.2.2 multihop local-address 1.1.1.1 vrf vrf2
transmit-interval 1500
receive-interval 1500
!
The results showed no issue related to packets received by
the wrong vrf. Even changing the udp_l3mdev_accept flag to
1 did not change the test results.
Signed-off-by: Philippe Guibert <philippe.guibert@6wind.com>
2022-07-07 14:33:48 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-06-27 16:29:02 +02:00
|
|
|
static void bp_set_ipopts(int sd)
|
|
|
|
{
|
2018-08-28 17:33:45 +02:00
|
|
|
int rcvttl = BFD_RCV_TTL_VAL;
|
|
|
|
|
bfdd: allow l3vrf bfd sessions without udp leaking
Until now, when in vrf-lite mode, the BFD implementation
creates a single UDP socket and relies on the following
sysctl value to 1:
echo 1 > /proc/sys/net/ipv4/udp_l3mdev_accept
With this setting, the incoming BFD packets from a given
vrf, would leak to the default vrf, and would match the
UDP socket.
The drawback of this solution is that udp packets received
on a given vrf may leak to an other vrf. This may be a
security concern.
The commit addresses this issue by avoiding this leak
mechanism. An UDP socket is created for each vrf, and each
socket uses new setsockopt option: SO_REUSEADDR + SO_REUSEPORT.
With this option, the incoming UDP packets are distributed on
the available sockets. The impact of those options with l3mdev
devices is unknown. It has been observed that this option is not
needed, until the default vrf sockets are created.
To ensure the BFD packets are correctly routed to the appropriate
socket, a BPF filter has been put in place and attached to the
sockets : SO_ATTACH_REUSEPORT_CBPF. This option adds a criterium
to force the packet to choose a given socket. If initial criteria
from the default distribution algorithm were not good, at least
two sockets would be available, and the CBPF would force the
selection to the same socket. This would come to the situation
where an incoming packet would be processed on a different vrf.
The bpf code is the following one:
struct sock_filter code[] = {
{ BPF_RET | BPF_K, 0, 0, 0 },
};
struct sock_fprog p = {
.len = sizeof(code)/sizeof(struct sock_filter),
.filter = code,
};
if (setsockopt(sd, SOL_SOCKET, SO_ATTACH_REUSEPORT_CBPF, &p, sizeof(p))) {
zlog_warn("unable to set SO_ATTACH_REUSEPORT_CBPF on socket: %s",
strerror(errno));
return -1;
}
Some tests have been done with by creating vrf contexts, and by using
the below vtysh configuration:
ip route 2.2.2.2/32 10.126.0.2
vrf vrf2
ip route 2.2.2.2/32 10.126.0.2
!
interface ntfp2
ip address 10.126.0.1/24
!
interface ntfp3 vrf vrf4
ip address 10.126.0.1/24
!
interface ntfp2 vrf vrf1
ip address 10.126.0.1/24
!
interface ntfp2.100 vrf vrf2
ip address 10.126.0.1/24
!
interface ntfp2.200 vrf vrf3
ip address 10.126.0.1/24
!
line vty
!
bfd
peer 10.126.0.2 vrf vrf2
!
peer 10.126.0.2 vrf vrf3
!
peer 10.126.0.2
!
peer 10.126.0.2 vrf vrf4
!
peer 2.2.2.2 multihop local-address 1.1.1.1
!
peer 2.2.2.2 multihop local-address 1.1.1.1 vrf vrf2
transmit-interval 1500
receive-interval 1500
!
The results showed no issue related to packets received by
the wrong vrf. Even changing the udp_l3mdev_accept flag to
1 did not change the test results.
Signed-off-by: Philippe Guibert <philippe.guibert@6wind.com>
2022-07-07 14:33:48 +02:00
|
|
|
if (!bp_set_reuse_addr(sd))
|
|
|
|
zlog_fatal("set-reuse-addr: failed");
|
|
|
|
|
|
|
|
if (!bp_set_reuse_port(sd))
|
|
|
|
zlog_fatal("set-reuse-port: failed");
|
|
|
|
|
2018-08-28 17:33:45 +02:00
|
|
|
if (bp_set_ttl(sd, BFD_TTL_VAL) != 0)
|
2020-04-10 16:06:22 +02:00
|
|
|
zlog_fatal("set-ipopts: TTL configuration failed");
|
2018-06-27 16:29:02 +02:00
|
|
|
|
|
|
|
if (setsockopt(sd, IPPROTO_IP, IP_RECVTTL, &rcvttl, sizeof(rcvttl))
|
|
|
|
== -1)
|
2020-04-10 16:06:22 +02:00
|
|
|
zlog_fatal("set-ipopts: setsockopt(IP_RECVTTL, %d): %s", rcvttl,
|
|
|
|
strerror(errno));
|
2018-06-27 16:29:02 +02:00
|
|
|
|
|
|
|
#ifdef BFD_LINUX
|
|
|
|
int pktinfo = BFD_PKT_INFO_VAL;
|
2018-08-28 17:33:45 +02:00
|
|
|
|
2018-06-27 16:29:02 +02:00
|
|
|
/* Figure out address and interface to do the peer matching. */
|
|
|
|
if (setsockopt(sd, IPPROTO_IP, IP_PKTINFO, &pktinfo, sizeof(pktinfo))
|
|
|
|
== -1)
|
2020-04-10 16:06:22 +02:00
|
|
|
zlog_fatal("set-ipopts: setsockopt(IP_PKTINFO, %d): %s",
|
|
|
|
pktinfo, strerror(errno));
|
2018-06-27 16:29:02 +02:00
|
|
|
#endif /* BFD_LINUX */
|
|
|
|
#ifdef BFD_BSD
|
|
|
|
int yes = 1;
|
|
|
|
|
|
|
|
/* Find out our address for peer matching. */
|
|
|
|
if (setsockopt(sd, IPPROTO_IP, IP_RECVDSTADDR, &yes, sizeof(yes)) == -1)
|
2020-04-10 16:06:22 +02:00
|
|
|
zlog_fatal("set-ipopts: setsockopt(IP_RECVDSTADDR, %d): %s",
|
|
|
|
yes, strerror(errno));
|
2018-06-27 16:29:02 +02:00
|
|
|
|
|
|
|
/* Find out interface where the packet came in. */
|
|
|
|
if (setsockopt_ifindex(AF_INET, sd, yes) == -1)
|
2020-04-10 16:06:22 +02:00
|
|
|
zlog_fatal("set-ipopts: setsockopt_ipv4_ifindex(%d): %s", yes,
|
|
|
|
strerror(errno));
|
2018-06-27 16:29:02 +02:00
|
|
|
#endif /* BFD_BSD */
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bp_bind_ip(int sd, uint16_t port)
|
|
|
|
{
|
|
|
|
struct sockaddr_in sin;
|
|
|
|
|
|
|
|
memset(&sin, 0, sizeof(sin));
|
|
|
|
sin.sin_family = AF_INET;
|
|
|
|
sin.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
|
|
sin.sin_port = htons(port);
|
|
|
|
if (bind(sd, (struct sockaddr *)&sin, sizeof(sin)) == -1)
|
2020-04-10 16:06:22 +02:00
|
|
|
zlog_fatal("bind-ip: bind: %s", strerror(errno));
|
2018-06-27 16:29:02 +02:00
|
|
|
}
|
|
|
|
|
2019-10-15 19:56:27 +02:00
|
|
|
int bp_udp_shop(const struct vrf *vrf)
|
2018-06-27 16:29:02 +02:00
|
|
|
{
|
|
|
|
int sd;
|
|
|
|
|
2019-08-13 15:47:23 +02:00
|
|
|
frr_with_privs(&bglobal.bfdd_privs) {
|
2019-10-15 19:56:27 +02:00
|
|
|
sd = vrf_socket(AF_INET, SOCK_DGRAM, PF_UNSPEC, vrf->vrf_id,
|
|
|
|
vrf->name);
|
2019-03-26 10:23:18 +01:00
|
|
|
}
|
2018-06-27 16:29:02 +02:00
|
|
|
if (sd == -1)
|
2020-04-10 16:06:22 +02:00
|
|
|
zlog_fatal("udp-shop: socket: %s", strerror(errno));
|
2018-06-27 16:29:02 +02:00
|
|
|
|
|
|
|
bp_set_ipopts(sd);
|
|
|
|
bp_bind_ip(sd, BFD_DEFDESTPORT);
|
|
|
|
return sd;
|
|
|
|
}
|
|
|
|
|
2019-10-15 19:56:27 +02:00
|
|
|
int bp_udp_mhop(const struct vrf *vrf)
|
2018-06-27 16:29:02 +02:00
|
|
|
{
|
|
|
|
int sd;
|
|
|
|
|
2019-08-13 15:47:23 +02:00
|
|
|
frr_with_privs(&bglobal.bfdd_privs) {
|
2019-10-15 19:56:27 +02:00
|
|
|
sd = vrf_socket(AF_INET, SOCK_DGRAM, PF_UNSPEC, vrf->vrf_id,
|
|
|
|
vrf->name);
|
2019-03-26 10:23:18 +01:00
|
|
|
}
|
2018-06-27 16:29:02 +02:00
|
|
|
if (sd == -1)
|
2020-04-10 16:06:22 +02:00
|
|
|
zlog_fatal("udp-mhop: socket: %s", strerror(errno));
|
2018-06-27 16:29:02 +02:00
|
|
|
|
|
|
|
bp_set_ipopts(sd);
|
|
|
|
bp_bind_ip(sd, BFD_DEF_MHOP_DEST_PORT);
|
|
|
|
|
|
|
|
return sd;
|
|
|
|
}
|
|
|
|
|
2019-02-02 12:57:08 +01:00
|
|
|
int bp_peer_socket(const struct bfd_session *bs)
|
2018-06-27 16:29:02 +02:00
|
|
|
{
|
|
|
|
int sd, pcount;
|
|
|
|
struct sockaddr_in sin;
|
|
|
|
static int srcPort = BFD_SRCPORTINIT;
|
2019-03-25 17:50:21 +01:00
|
|
|
const char *device_to_bind = NULL;
|
|
|
|
|
|
|
|
if (bs->key.ifname[0])
|
|
|
|
device_to_bind = (const char *)bs->key.ifname;
|
2021-01-08 08:51:33 +01:00
|
|
|
else if ((!vrf_is_backend_netns() && bs->vrf->vrf_id != VRF_DEFAULT)
|
|
|
|
|| ((CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)
|
|
|
|
&& bs->key.vrfname[0])))
|
2019-03-25 17:50:21 +01:00
|
|
|
device_to_bind = (const char *)bs->key.vrfname;
|
2018-06-27 16:29:02 +02:00
|
|
|
|
2019-08-13 15:47:23 +02:00
|
|
|
frr_with_privs(&bglobal.bfdd_privs) {
|
2019-03-25 17:19:25 +01:00
|
|
|
sd = vrf_socket(AF_INET, SOCK_DGRAM, PF_UNSPEC,
|
2019-03-25 17:50:21 +01:00
|
|
|
bs->vrf->vrf_id, device_to_bind);
|
2019-03-25 17:19:25 +01:00
|
|
|
}
|
2018-07-25 05:03:47 +02:00
|
|
|
if (sd == -1) {
|
2020-04-10 16:06:22 +02:00
|
|
|
zlog_err("ipv4-new: failed to create socket: %s",
|
|
|
|
strerror(errno));
|
2018-06-27 16:29:02 +02:00
|
|
|
return -1;
|
2018-07-25 05:03:47 +02:00
|
|
|
}
|
2018-06-27 16:29:02 +02:00
|
|
|
|
2018-08-28 21:20:52 +02:00
|
|
|
/* Set TTL to 255 for all transmitted packets */
|
|
|
|
if (bp_set_ttl(sd, BFD_TTL_VAL) != 0) {
|
|
|
|
close(sd);
|
|
|
|
return -1;
|
2018-06-27 16:29:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Set TOS to CS6 for all transmitted packets */
|
2018-08-28 17:33:45 +02:00
|
|
|
if (bp_set_tos(sd, BFD_TOS_VAL) != 0) {
|
2018-06-27 16:29:02 +02:00
|
|
|
close(sd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Find an available source port in the proper range */
|
2019-03-11 19:09:15 +01:00
|
|
|
memset(&sin, 0, sizeof(sin));
|
2018-06-27 16:29:02 +02:00
|
|
|
sin.sin_family = AF_INET;
|
|
|
|
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
|
|
|
|
sin.sin_len = sizeof(sin);
|
|
|
|
#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
|
2019-03-11 19:09:15 +01:00
|
|
|
memcpy(&sin.sin_addr, &bs->key.local, sizeof(sin.sin_addr));
|
2018-06-27 16:29:02 +02:00
|
|
|
|
|
|
|
pcount = 0;
|
|
|
|
do {
|
|
|
|
if ((++pcount) > (BFD_SRCPORTMAX - BFD_SRCPORTINIT)) {
|
|
|
|
/* Searched all ports, none available */
|
2020-04-10 16:06:22 +02:00
|
|
|
zlog_err("ipv4-new: failed to bind port: %s",
|
|
|
|
strerror(errno));
|
2018-06-27 16:29:02 +02:00
|
|
|
close(sd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (srcPort >= BFD_SRCPORTMAX)
|
|
|
|
srcPort = BFD_SRCPORTINIT;
|
|
|
|
sin.sin_port = htons(srcPort++);
|
|
|
|
} while (bind(sd, (struct sockaddr *)&sin, sizeof(sin)) < 0);
|
|
|
|
|
|
|
|
return sd;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* IPv6 sockets
|
|
|
|
*/
|
|
|
|
|
2019-02-02 12:57:08 +01:00
|
|
|
int bp_peer_socketv6(const struct bfd_session *bs)
|
2018-06-27 16:29:02 +02:00
|
|
|
{
|
2019-01-10 20:13:32 +01:00
|
|
|
int sd, pcount;
|
2018-06-27 16:29:02 +02:00
|
|
|
struct sockaddr_in6 sin6;
|
|
|
|
static int srcPort = BFD_SRCPORTINIT;
|
2019-03-25 17:50:21 +01:00
|
|
|
const char *device_to_bind = NULL;
|
|
|
|
|
|
|
|
if (bs->key.ifname[0])
|
|
|
|
device_to_bind = (const char *)bs->key.ifname;
|
2021-01-08 08:51:33 +01:00
|
|
|
else if ((!vrf_is_backend_netns() && bs->vrf->vrf_id != VRF_DEFAULT)
|
|
|
|
|| ((CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)
|
|
|
|
&& bs->key.vrfname[0])))
|
2019-03-25 17:50:21 +01:00
|
|
|
device_to_bind = (const char *)bs->key.vrfname;
|
2018-06-27 16:29:02 +02:00
|
|
|
|
2019-08-13 15:47:23 +02:00
|
|
|
frr_with_privs(&bglobal.bfdd_privs) {
|
2019-03-25 17:19:25 +01:00
|
|
|
sd = vrf_socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC,
|
2019-03-25 17:50:21 +01:00
|
|
|
bs->vrf->vrf_id, device_to_bind);
|
2019-03-25 17:19:25 +01:00
|
|
|
}
|
2018-07-25 05:03:47 +02:00
|
|
|
if (sd == -1) {
|
2020-04-10 16:06:22 +02:00
|
|
|
zlog_err("ipv6-new: failed to create socket: %s",
|
|
|
|
strerror(errno));
|
2018-06-27 16:29:02 +02:00
|
|
|
return -1;
|
2018-07-25 05:03:47 +02:00
|
|
|
}
|
2018-06-27 16:29:02 +02:00
|
|
|
|
2018-08-28 21:20:52 +02:00
|
|
|
/* Set TTL to 255 for all transmitted packets */
|
|
|
|
if (bp_set_ttlv6(sd, BFD_TTL_VAL) != 0) {
|
|
|
|
close(sd);
|
|
|
|
return -1;
|
2018-06-27 16:29:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Set TOS to CS6 for all transmitted packets */
|
2018-08-28 17:33:45 +02:00
|
|
|
if (bp_set_tosv6(sd, BFD_TOS_VAL) != 0) {
|
2018-06-27 16:29:02 +02:00
|
|
|
close(sd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Find an available source port in the proper range */
|
2019-03-11 19:09:15 +01:00
|
|
|
memset(&sin6, 0, sizeof(sin6));
|
2018-06-27 16:29:02 +02:00
|
|
|
sin6.sin6_family = AF_INET6;
|
|
|
|
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
|
|
|
|
sin6.sin6_len = sizeof(sin6);
|
|
|
|
#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
|
2019-03-11 19:09:15 +01:00
|
|
|
memcpy(&sin6.sin6_addr, &bs->key.local, sizeof(sin6.sin6_addr));
|
2020-11-05 17:46:27 +01:00
|
|
|
if (bs->ifp && IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr))
|
2019-03-11 19:09:15 +01:00
|
|
|
sin6.sin6_scope_id = bs->ifp->ifindex;
|
2018-06-27 16:29:02 +02:00
|
|
|
|
|
|
|
pcount = 0;
|
|
|
|
do {
|
|
|
|
if ((++pcount) > (BFD_SRCPORTMAX - BFD_SRCPORTINIT)) {
|
|
|
|
/* Searched all ports, none available */
|
2020-04-10 16:06:22 +02:00
|
|
|
zlog_err("ipv6-new: failed to bind port: %s",
|
|
|
|
strerror(errno));
|
2018-06-27 16:29:02 +02:00
|
|
|
close(sd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (srcPort >= BFD_SRCPORTMAX)
|
|
|
|
srcPort = BFD_SRCPORTINIT;
|
|
|
|
sin6.sin6_port = htons(srcPort++);
|
|
|
|
} while (bind(sd, (struct sockaddr *)&sin6, sizeof(sin6)) < 0);
|
|
|
|
|
|
|
|
return sd;
|
|
|
|
}
|
|
|
|
|
2018-08-28 17:33:45 +02:00
|
|
|
int bp_set_ttlv6(int sd, uint8_t value)
|
2018-06-27 16:29:02 +02:00
|
|
|
{
|
2018-08-28 17:33:45 +02:00
|
|
|
int ttl = value;
|
|
|
|
|
|
|
|
if (setsockopt(sd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl))
|
2018-06-27 16:29:02 +02:00
|
|
|
== -1) {
|
2020-04-10 16:06:22 +02:00
|
|
|
zlog_warn("set-ttlv6: setsockopt(IPV6_UNICAST_HOPS, %d): %s",
|
|
|
|
value, strerror(errno));
|
2018-06-27 16:29:02 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-08-28 17:33:45 +02:00
|
|
|
int bp_set_tosv6(int sd, uint8_t value)
|
2018-06-27 16:29:02 +02:00
|
|
|
{
|
2018-08-28 17:33:45 +02:00
|
|
|
int tos = value;
|
|
|
|
|
|
|
|
if (setsockopt(sd, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos))
|
2018-06-27 16:29:02 +02:00
|
|
|
== -1) {
|
2020-04-10 16:06:22 +02:00
|
|
|
zlog_warn("set-tosv6: setsockopt(IPV6_TCLASS, %d): %s", value,
|
|
|
|
strerror(errno));
|
2018-06-27 16:29:02 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bp_set_ipv6opts(int sd)
|
|
|
|
{
|
2018-08-28 17:33:45 +02:00
|
|
|
int ipv6_pktinfo = BFD_IPV6_PKT_INFO_VAL;
|
|
|
|
int ipv6_only = BFD_IPV6_ONLY_VAL;
|
2018-06-27 16:29:02 +02:00
|
|
|
|
bfdd: allow l3vrf bfd sessions without udp leaking
Until now, when in vrf-lite mode, the BFD implementation
creates a single UDP socket and relies on the following
sysctl value to 1:
echo 1 > /proc/sys/net/ipv4/udp_l3mdev_accept
With this setting, the incoming BFD packets from a given
vrf, would leak to the default vrf, and would match the
UDP socket.
The drawback of this solution is that udp packets received
on a given vrf may leak to an other vrf. This may be a
security concern.
The commit addresses this issue by avoiding this leak
mechanism. An UDP socket is created for each vrf, and each
socket uses new setsockopt option: SO_REUSEADDR + SO_REUSEPORT.
With this option, the incoming UDP packets are distributed on
the available sockets. The impact of those options with l3mdev
devices is unknown. It has been observed that this option is not
needed, until the default vrf sockets are created.
To ensure the BFD packets are correctly routed to the appropriate
socket, a BPF filter has been put in place and attached to the
sockets : SO_ATTACH_REUSEPORT_CBPF. This option adds a criterium
to force the packet to choose a given socket. If initial criteria
from the default distribution algorithm were not good, at least
two sockets would be available, and the CBPF would force the
selection to the same socket. This would come to the situation
where an incoming packet would be processed on a different vrf.
The bpf code is the following one:
struct sock_filter code[] = {
{ BPF_RET | BPF_K, 0, 0, 0 },
};
struct sock_fprog p = {
.len = sizeof(code)/sizeof(struct sock_filter),
.filter = code,
};
if (setsockopt(sd, SOL_SOCKET, SO_ATTACH_REUSEPORT_CBPF, &p, sizeof(p))) {
zlog_warn("unable to set SO_ATTACH_REUSEPORT_CBPF on socket: %s",
strerror(errno));
return -1;
}
Some tests have been done with by creating vrf contexts, and by using
the below vtysh configuration:
ip route 2.2.2.2/32 10.126.0.2
vrf vrf2
ip route 2.2.2.2/32 10.126.0.2
!
interface ntfp2
ip address 10.126.0.1/24
!
interface ntfp3 vrf vrf4
ip address 10.126.0.1/24
!
interface ntfp2 vrf vrf1
ip address 10.126.0.1/24
!
interface ntfp2.100 vrf vrf2
ip address 10.126.0.1/24
!
interface ntfp2.200 vrf vrf3
ip address 10.126.0.1/24
!
line vty
!
bfd
peer 10.126.0.2 vrf vrf2
!
peer 10.126.0.2 vrf vrf3
!
peer 10.126.0.2
!
peer 10.126.0.2 vrf vrf4
!
peer 2.2.2.2 multihop local-address 1.1.1.1
!
peer 2.2.2.2 multihop local-address 1.1.1.1 vrf vrf2
transmit-interval 1500
receive-interval 1500
!
The results showed no issue related to packets received by
the wrong vrf. Even changing the udp_l3mdev_accept flag to
1 did not change the test results.
Signed-off-by: Philippe Guibert <philippe.guibert@6wind.com>
2022-07-07 14:33:48 +02:00
|
|
|
if (!bp_set_reuse_addr(sd))
|
|
|
|
zlog_fatal("set-reuse-addr: failed");
|
|
|
|
|
|
|
|
if (!bp_set_reuse_port(sd))
|
|
|
|
zlog_fatal("set-reuse-port: failed");
|
|
|
|
|
2018-08-28 17:33:45 +02:00
|
|
|
if (bp_set_ttlv6(sd, BFD_TTL_VAL) == -1)
|
2020-04-10 16:06:22 +02:00
|
|
|
zlog_fatal(
|
|
|
|
"set-ipv6opts: setsockopt(IPV6_UNICAST_HOPS, %d): %s",
|
|
|
|
BFD_TTL_VAL, strerror(errno));
|
2018-06-27 16:29:02 +02:00
|
|
|
|
2018-08-28 17:33:45 +02:00
|
|
|
if (setsockopt_ipv6_hoplimit(sd, BFD_RCV_TTL_VAL) == -1)
|
2020-04-10 16:06:22 +02:00
|
|
|
zlog_fatal("set-ipv6opts: setsockopt(IPV6_HOPLIMIT, %d): %s",
|
|
|
|
BFD_RCV_TTL_VAL, strerror(errno));
|
2018-06-27 16:29:02 +02:00
|
|
|
|
|
|
|
if (setsockopt_ipv6_pktinfo(sd, ipv6_pktinfo) == -1)
|
2020-04-10 16:06:22 +02:00
|
|
|
zlog_fatal("set-ipv6opts: setsockopt(IPV6_PKTINFO, %d): %s",
|
|
|
|
ipv6_pktinfo, strerror(errno));
|
2018-06-27 16:29:02 +02:00
|
|
|
|
|
|
|
if (setsockopt(sd, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6_only,
|
|
|
|
sizeof(ipv6_only))
|
|
|
|
== -1)
|
2020-04-10 16:06:22 +02:00
|
|
|
zlog_fatal("set-ipv6opts: setsockopt(IPV6_V6ONLY, %d): %s",
|
|
|
|
ipv6_only, strerror(errno));
|
2018-06-27 16:29:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void bp_bind_ipv6(int sd, uint16_t port)
|
|
|
|
{
|
|
|
|
struct sockaddr_in6 sin6;
|
|
|
|
|
|
|
|
memset(&sin6, 0, sizeof(sin6));
|
|
|
|
sin6.sin6_family = AF_INET6;
|
|
|
|
sin6.sin6_addr = in6addr_any;
|
|
|
|
sin6.sin6_port = htons(port);
|
|
|
|
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
|
|
|
|
sin6.sin6_len = sizeof(sin6);
|
|
|
|
#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
|
|
|
|
if (bind(sd, (struct sockaddr *)&sin6, sizeof(sin6)) == -1)
|
2020-04-10 16:06:22 +02:00
|
|
|
zlog_fatal("bind-ipv6: bind: %s", strerror(errno));
|
2018-06-27 16:29:02 +02:00
|
|
|
}
|
|
|
|
|
2019-10-15 19:56:27 +02:00
|
|
|
int bp_udp6_shop(const struct vrf *vrf)
|
2018-06-27 16:29:02 +02:00
|
|
|
{
|
|
|
|
int sd;
|
|
|
|
|
2019-08-13 15:47:23 +02:00
|
|
|
frr_with_privs(&bglobal.bfdd_privs) {
|
2019-10-15 19:56:27 +02:00
|
|
|
sd = vrf_socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC, vrf->vrf_id,
|
|
|
|
vrf->name);
|
2019-03-26 10:23:18 +01:00
|
|
|
}
|
2020-08-05 19:10:27 +02:00
|
|
|
if (sd == -1) {
|
|
|
|
if (errno != EAFNOSUPPORT)
|
|
|
|
zlog_fatal("udp6-shop: socket: %s", strerror(errno));
|
|
|
|
else
|
|
|
|
zlog_warn("udp6-shop: V6 is not supported, continuing");
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
2018-06-27 16:29:02 +02:00
|
|
|
|
|
|
|
bp_set_ipv6opts(sd);
|
|
|
|
bp_bind_ipv6(sd, BFD_DEFDESTPORT);
|
|
|
|
|
|
|
|
return sd;
|
|
|
|
}
|
|
|
|
|
2019-10-15 19:56:27 +02:00
|
|
|
int bp_udp6_mhop(const struct vrf *vrf)
|
2018-06-27 16:29:02 +02:00
|
|
|
{
|
|
|
|
int sd;
|
|
|
|
|
2019-08-13 15:47:23 +02:00
|
|
|
frr_with_privs(&bglobal.bfdd_privs) {
|
2019-10-15 19:56:27 +02:00
|
|
|
sd = vrf_socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC, vrf->vrf_id,
|
|
|
|
vrf->name);
|
2019-03-26 10:23:18 +01:00
|
|
|
}
|
2020-08-05 19:10:27 +02:00
|
|
|
if (sd == -1) {
|
|
|
|
if (errno != EAFNOSUPPORT)
|
|
|
|
zlog_fatal("udp6-mhop: socket: %s", strerror(errno));
|
|
|
|
else
|
|
|
|
zlog_warn("udp6-mhop: V6 is not supported, continuing");
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
2018-06-27 16:29:02 +02:00
|
|
|
|
|
|
|
bp_set_ipv6opts(sd);
|
|
|
|
bp_bind_ipv6(sd, BFD_DEF_MHOP_DEST_PORT);
|
|
|
|
|
|
|
|
return sd;
|
|
|
|
}
|
2018-08-29 20:17:49 +02:00
|
|
|
|
2022-06-07 01:40:17 +02:00
|
|
|
#ifdef BFD_LINUX
|
|
|
|
/* tcpdump -dd udp dst port 3785 */
|
|
|
|
struct sock_filter my_filterudp[] = {
|
|
|
|
{0x28, 0, 0, 0x0000000c}, {0x15, 0, 8, 0x00000800},
|
|
|
|
{0x30, 0, 0, 0x00000017}, {0x15, 0, 6, 0x00000011},
|
|
|
|
{0x28, 0, 0, 0x00000014}, {0x45, 4, 0, 0x00001fff},
|
|
|
|
{0xb1, 0, 0, 0x0000000e}, {0x48, 0, 0, 0x00000010},
|
|
|
|
{0x15, 0, 1, 0x00000ec9}, {0x6, 0, 0, 0x00040000},
|
|
|
|
{0x6, 0, 0, 0x00000000},
|
|
|
|
};
|
|
|
|
|
|
|
|
#define MY_FILTER_LENGTH 11
|
|
|
|
|
|
|
|
int bp_echo_socket(const struct vrf *vrf)
|
|
|
|
{
|
|
|
|
int s;
|
|
|
|
|
|
|
|
frr_with_privs (&bglobal.bfdd_privs) {
|
|
|
|
s = vrf_socket(AF_PACKET, SOCK_RAW, ETH_P_IP, vrf->vrf_id,
|
|
|
|
vrf->name);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (s == -1)
|
|
|
|
zlog_fatal("echo-socket: socket: %s", strerror(errno));
|
|
|
|
|
|
|
|
struct sock_fprog pf;
|
2022-07-26 01:02:46 +02:00
|
|
|
struct sockaddr_ll sll = {0};
|
2022-06-07 01:40:17 +02:00
|
|
|
|
|
|
|
/* adjust filter for socket to only receive ECHO packets */
|
|
|
|
pf.filter = my_filterudp;
|
|
|
|
pf.len = MY_FILTER_LENGTH;
|
|
|
|
if (setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &pf, sizeof(pf)) ==
|
|
|
|
-1) {
|
|
|
|
zlog_warn("%s: setsockopt(SO_ATTACH_FILTER): %s", __func__,
|
|
|
|
strerror(errno));
|
2022-07-06 12:49:56 +02:00
|
|
|
close(s);
|
2022-06-07 01:40:17 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2022-07-19 21:23:50 +02:00
|
|
|
memset(&sll, 0, sizeof(sll));
|
2022-06-07 01:40:17 +02:00
|
|
|
sll.sll_family = AF_PACKET;
|
|
|
|
sll.sll_protocol = htons(ETH_P_IP);
|
|
|
|
sll.sll_ifindex = 0;
|
|
|
|
if (bind(s, (struct sockaddr *)&sll, sizeof(sll)) < 0) {
|
|
|
|
zlog_warn("Failed to bind echo socket: %s",
|
|
|
|
safe_strerror(errno));
|
2022-07-06 12:49:56 +02:00
|
|
|
close(s);
|
2022-06-07 01:40:17 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
#else
|
2019-10-15 19:56:27 +02:00
|
|
|
int bp_echo_socket(const struct vrf *vrf)
|
2018-08-29 20:17:49 +02:00
|
|
|
{
|
|
|
|
int s;
|
|
|
|
|
2019-08-13 15:47:23 +02:00
|
|
|
frr_with_privs(&bglobal.bfdd_privs) {
|
2019-10-15 19:56:27 +02:00
|
|
|
s = vrf_socket(AF_INET, SOCK_DGRAM, 0, vrf->vrf_id, vrf->name);
|
2019-03-26 10:23:18 +01:00
|
|
|
}
|
2018-08-29 20:17:49 +02:00
|
|
|
if (s == -1)
|
2020-04-10 16:06:22 +02:00
|
|
|
zlog_fatal("echo-socket: socket: %s", strerror(errno));
|
2018-08-29 20:17:49 +02:00
|
|
|
|
|
|
|
bp_set_ipopts(s);
|
|
|
|
bp_bind_ip(s, BFD_DEF_ECHO_PORT);
|
|
|
|
|
|
|
|
return s;
|
|
|
|
}
|
2022-06-07 01:40:17 +02:00
|
|
|
#endif
|
2018-08-29 20:17:49 +02:00
|
|
|
|
2019-10-15 19:56:27 +02:00
|
|
|
int bp_echov6_socket(const struct vrf *vrf)
|
2018-08-29 20:17:49 +02:00
|
|
|
{
|
|
|
|
int s;
|
|
|
|
|
2019-08-13 15:47:23 +02:00
|
|
|
frr_with_privs(&bglobal.bfdd_privs) {
|
2019-10-15 19:56:27 +02:00
|
|
|
s = vrf_socket(AF_INET6, SOCK_DGRAM, 0, vrf->vrf_id, vrf->name);
|
2019-03-26 10:23:18 +01:00
|
|
|
}
|
2020-08-05 19:10:27 +02:00
|
|
|
if (s == -1) {
|
|
|
|
if (errno != EAFNOSUPPORT)
|
|
|
|
zlog_fatal("echov6-socket: socket: %s",
|
|
|
|
strerror(errno));
|
|
|
|
else
|
|
|
|
zlog_warn("echov6-socket: V6 is not supported, continuing");
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
2018-08-29 20:17:49 +02:00
|
|
|
|
|
|
|
bp_set_ipv6opts(s);
|
|
|
|
bp_bind_ipv6(s, BFD_DEF_ECHO_PORT);
|
|
|
|
|
|
|
|
return s;
|
|
|
|
}
|
2022-06-07 01:40:17 +02:00
|
|
|
|
|
|
|
#ifdef BFD_LINUX
|
|
|
|
/* get peer's mac address to be used with Echo packets when they are looped in
|
|
|
|
* peers forwarding plane
|
|
|
|
*/
|
|
|
|
void bfd_peer_mac_set(int sd, struct bfd_session *bfd,
|
|
|
|
struct sockaddr_any *peer, struct interface *ifp)
|
|
|
|
{
|
|
|
|
struct arpreq arpreq_;
|
|
|
|
|
|
|
|
if (CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_MAC_SET))
|
|
|
|
return;
|
2022-08-06 03:40:11 +02:00
|
|
|
if (ifp->flags & IFF_NOARP)
|
|
|
|
return;
|
2022-06-07 01:40:17 +02:00
|
|
|
|
|
|
|
if (peer->sa_sin.sin_family == AF_INET) {
|
|
|
|
/* IPV4 */
|
|
|
|
struct sockaddr_in *addr =
|
|
|
|
(struct sockaddr_in *)&arpreq_.arp_pa;
|
|
|
|
|
|
|
|
memset(&arpreq_, 0, sizeof(struct arpreq));
|
|
|
|
addr->sin_family = AF_INET;
|
|
|
|
memcpy(&addr->sin_addr.s_addr, &peer->sa_sin.sin_addr,
|
|
|
|
sizeof(addr->sin_addr));
|
|
|
|
strlcpy(arpreq_.arp_dev, ifp->name, sizeof(arpreq_.arp_dev));
|
|
|
|
|
|
|
|
if (ioctl(sd, SIOCGARP, &arpreq_) < 0) {
|
2023-07-28 17:46:30 +02:00
|
|
|
if (bglobal.debug_network)
|
|
|
|
zlog_debug(
|
|
|
|
"BFD: getting peer's mac on %s failed error %s",
|
|
|
|
ifp->name, strerror(errno));
|
2022-06-07 01:40:17 +02:00
|
|
|
UNSET_FLAG(bfd->flags, BFD_SESS_FLAG_MAC_SET);
|
|
|
|
memset(bfd->peer_hw_addr, 0, sizeof(bfd->peer_hw_addr));
|
|
|
|
|
|
|
|
} else {
|
|
|
|
memcpy(bfd->peer_hw_addr, arpreq_.arp_ha.sa_data,
|
|
|
|
sizeof(bfd->peer_hw_addr));
|
|
|
|
SET_FLAG(bfd->flags, BFD_SESS_FLAG_MAC_SET);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|