mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-03 20:27:03 +00:00
bfdd: implement session interface observer
Allow `bfdd` to configure inexisting interfaces / VRF and only activate them once the interface/VRF start existing. This implementation doesn't handle dynamic VRFs yet. Signed-off-by: Rafael Zalamena <rzalamena@opensourcerouting.org>
This commit is contained in:
parent
843b324ddd
commit
d245e522f0
376
bfdd/bfd.c
376
bfdd/bfd.c
@ -36,10 +36,12 @@ DEFINE_QOBJ_TYPE(bfd_session);
|
||||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
struct bfd_session *bs_peer_waiting_find(struct bfd_peer_cfg *);
|
||||
|
||||
static uint32_t ptm_bfd_gen_ID(void);
|
||||
static void ptm_bfd_echo_xmt_TO(struct bfd_session *bfd);
|
||||
static void bfd_session_free(struct bfd_session *bs);
|
||||
static struct bfd_session *bfd_session_new(int sd);
|
||||
static struct bfd_session *bfd_session_new(void);
|
||||
static struct bfd_session *bfd_find_disc(struct sockaddr_any *sa,
|
||||
uint32_t ldisc);
|
||||
static int bfd_session_update(struct bfd_session *bs, struct bfd_peer_cfg *bpc);
|
||||
@ -49,6 +51,54 @@ static const char *get_diag_str(int diag);
|
||||
/*
|
||||
* Functions
|
||||
*/
|
||||
struct bfd_session *bs_peer_waiting_find(struct bfd_peer_cfg *bpc)
|
||||
{
|
||||
struct bfd_session_observer *bso;
|
||||
struct bfd_session *bs = NULL;
|
||||
bool is_shop, is_ipv4;
|
||||
|
||||
TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) {
|
||||
bs = bso->bso_bs;
|
||||
|
||||
is_shop = !BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH);
|
||||
is_ipv4 = !BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_IPV6);
|
||||
/* Quick checks first. */
|
||||
if (is_shop != (!bpc->bpc_mhop))
|
||||
continue;
|
||||
if (is_ipv4 != bpc->bpc_ipv4)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Slow lookup without hash because we don't have all
|
||||
* information yet.
|
||||
*/
|
||||
if (is_shop) {
|
||||
if (strcmp(bs->ifname, bpc->bpc_localif))
|
||||
continue;
|
||||
if (memcmp(&bs->shop.peer, &bpc->bpc_peer,
|
||||
sizeof(bs->shop.peer)))
|
||||
continue;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (strcmp(bs->vrfname, bpc->bpc_vrfname))
|
||||
continue;
|
||||
if (memcmp(&bs->mhop.peer, &bpc->bpc_peer,
|
||||
sizeof(bs->mhop.peer)))
|
||||
continue;
|
||||
if (memcmp(&bs->mhop.local, &bpc->bpc_local,
|
||||
sizeof(bs->mhop.local)))
|
||||
continue;
|
||||
|
||||
break;
|
||||
}
|
||||
if (bso == NULL)
|
||||
bs = NULL;
|
||||
|
||||
return bs;
|
||||
}
|
||||
|
||||
struct bfd_session *bs_peer_find(struct bfd_peer_cfg *bpc)
|
||||
{
|
||||
struct bfd_session *bs;
|
||||
@ -95,7 +145,151 @@ struct bfd_session *bs_peer_find(struct bfd_peer_cfg *bpc)
|
||||
bs = bfd_shop_lookup(shop);
|
||||
}
|
||||
|
||||
return bs;
|
||||
if (bs != NULL)
|
||||
return bs;
|
||||
|
||||
/* Search for entries that are incomplete. */
|
||||
return bs_peer_waiting_find(bpc);
|
||||
}
|
||||
|
||||
/*
|
||||
* Starts a disabled BFD session.
|
||||
*
|
||||
* A session is disabled when the specified interface/VRF doesn't exist
|
||||
* yet. It might happen on FRR boot or with virtual interfaces.
|
||||
*/
|
||||
int bfd_session_enable(struct bfd_session *bs)
|
||||
{
|
||||
struct sockaddr_in6 *sin6;
|
||||
struct interface *ifp = NULL;
|
||||
struct vrf *vrf = NULL;
|
||||
int psock;
|
||||
|
||||
/*
|
||||
* If the interface or VRF doesn't exist, then we must register
|
||||
* the session but delay its start.
|
||||
*/
|
||||
if (bs->ifname[0] != 0) {
|
||||
ifp = if_lookup_by_name_all_vrf(bs->ifname);
|
||||
if (ifp == NULL) {
|
||||
log_error(
|
||||
"session-enable: specified interface doesn't exists.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
vrf = vrf_lookup_by_id(ifp->vrf_id);
|
||||
if (vrf == NULL) {
|
||||
log_error("session-enable: specified VRF doesn't exists.");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (bs->vrfname[0] != 0) {
|
||||
vrf = vrf_lookup_by_name(bs->vrfname);
|
||||
if (vrf == NULL) {
|
||||
log_error("session-enable: specified VRF doesn't exists.");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Assign interface/VRF pointers. */
|
||||
bs->vrf = vrf;
|
||||
if (bs->vrf == NULL)
|
||||
bs->vrf = vrf_lookup_by_id(VRF_DEFAULT);
|
||||
|
||||
if (bs->ifname[0] != 0 &&
|
||||
BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH) == 0)
|
||||
bs->ifp = ifp;
|
||||
|
||||
/* Set the IPv6 scope id for link-local addresses. */
|
||||
if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_IPV6)) {
|
||||
sin6 = &bs->mhop.peer.sa_sin6;
|
||||
if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
|
||||
sin6->sin6_scope_id = bs->ifp != NULL
|
||||
? bs->ifp->ifindex
|
||||
: IFINDEX_INTERNAL;
|
||||
|
||||
sin6 = &bs->mhop.local.sa_sin6;
|
||||
if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
|
||||
sin6->sin6_scope_id = bs->ifp != NULL
|
||||
? bs->ifp->ifindex
|
||||
: IFINDEX_INTERNAL;
|
||||
|
||||
bs->local_ip.sa_sin6 = *sin6;
|
||||
bs->local_address.sa_sin6 = *sin6;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get socket for transmitting control packets. Note that if we
|
||||
* could use the destination port (3784) for the source
|
||||
* port we wouldn't need a socket per session.
|
||||
*/
|
||||
if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_IPV6) == 0) {
|
||||
psock = bp_peer_socket(bs);
|
||||
if (psock == -1)
|
||||
return -1;
|
||||
} else {
|
||||
psock = bp_peer_socketv6(bs);
|
||||
if (psock == -1)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* We've got a valid socket, lets start the timers and the
|
||||
* protocol.
|
||||
*/
|
||||
bs->sock = psock;
|
||||
bfd_recvtimer_update(bs);
|
||||
ptm_bfd_start_xmt_timer(bs, false);
|
||||
|
||||
/* Registrate session into data structures. */
|
||||
bs->discrs.my_discr = ptm_bfd_gen_ID();
|
||||
bfd_id_insert(bs);
|
||||
if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) {
|
||||
if (vrf != NULL)
|
||||
bs->mhop.vrfid = vrf->vrf_id;
|
||||
else
|
||||
bs->mhop.vrfid = VRF_DEFAULT;
|
||||
|
||||
bfd_mhop_insert(bs);
|
||||
} else {
|
||||
if (ifp != NULL)
|
||||
bs->shop.ifindex = ifp->ifindex;
|
||||
else
|
||||
bs->shop.ifindex = IFINDEX_INTERNAL;
|
||||
|
||||
bfd_shop_insert(bs);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Disabled a running BFD session.
|
||||
*
|
||||
* A session is disabled when the specified interface/VRF gets removed
|
||||
* (e.g. virtual interfaces).
|
||||
*/
|
||||
void bfd_session_disable(struct bfd_session *bs)
|
||||
{
|
||||
/* Free up socket resources. */
|
||||
if (bs->sock != -1) {
|
||||
close(bs->sock);
|
||||
bs->sock = -1;
|
||||
}
|
||||
|
||||
/* Disable all timers. */
|
||||
bfd_recvtimer_delete(bs);
|
||||
bfd_echo_recvtimer_delete(bs);
|
||||
bfd_xmttimer_delete(bs);
|
||||
bfd_echo_xmttimer_delete(bs);
|
||||
|
||||
/* Unregister session from hashes to avoid unwanted activation. */
|
||||
bfd_id_delete(bs->discrs.my_discr);
|
||||
if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH))
|
||||
bfd_mhop_delete(bs->mhop);
|
||||
else
|
||||
bfd_shop_delete(bs->shop);
|
||||
}
|
||||
|
||||
static uint32_t ptm_bfd_gen_ID(void)
|
||||
@ -347,7 +541,7 @@ int bfd_echo_recvtimer_cb(struct thread *t)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct bfd_session *bfd_session_new(int sd)
|
||||
static struct bfd_session *bfd_session_new(void)
|
||||
{
|
||||
struct bfd_session *bs;
|
||||
|
||||
@ -362,6 +556,7 @@ static struct bfd_session *bfd_session_new(int sd)
|
||||
bs->timers.required_min_echo = BFD_DEF_REQ_MIN_ECHO;
|
||||
bs->detect_mult = BFD_DEFDETECTMULT;
|
||||
bs->mh_ttl = BFD_DEF_MHOP_TTL;
|
||||
bs->ses_state = PTM_BFD_DOWN;
|
||||
|
||||
/* Initiate connection with slow timers. */
|
||||
bs_set_slow_timers(bs);
|
||||
@ -370,7 +565,7 @@ static struct bfd_session *bfd_session_new(int sd)
|
||||
bs->remote_timers = bs->cur_timers;
|
||||
bs->remote_detect_mult = BFD_DEFDETECTMULT;
|
||||
|
||||
bs->sock = sd;
|
||||
bs->sock = -1;
|
||||
monotime(&bs->uptime);
|
||||
bs->downtime = bs->uptime;
|
||||
|
||||
@ -464,7 +659,9 @@ skip_echo:
|
||||
bs->ses_state = PTM_BFD_ADM_DOWN;
|
||||
control_notify(bs);
|
||||
|
||||
ptm_bfd_snd(bs, 0);
|
||||
/* Don't try to send packets with a disabled session. */
|
||||
if (bs->sock != -1)
|
||||
ptm_bfd_snd(bs, 0);
|
||||
} else {
|
||||
/* Check if already working. */
|
||||
if (!BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN))
|
||||
@ -497,19 +694,19 @@ static int bfd_session_update(struct bfd_session *bs, struct bfd_peer_cfg *bpc)
|
||||
|
||||
static void bfd_session_free(struct bfd_session *bs)
|
||||
{
|
||||
if (bs->sock != -1)
|
||||
close(bs->sock);
|
||||
struct bfd_session_observer *bso;
|
||||
|
||||
bfd_recvtimer_delete(bs);
|
||||
bfd_echo_recvtimer_delete(bs);
|
||||
bfd_xmttimer_delete(bs);
|
||||
bfd_echo_xmttimer_delete(bs);
|
||||
bfd_session_disable(bs);
|
||||
|
||||
bfd_id_delete(bs->discrs.my_discr);
|
||||
if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH))
|
||||
bfd_mhop_delete(bs->mhop);
|
||||
else
|
||||
bfd_shop_delete(bs->shop);
|
||||
/* Remove observer if any. */
|
||||
TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) {
|
||||
if (bso->bso_bs != bs)
|
||||
continue;
|
||||
|
||||
break;
|
||||
}
|
||||
if (bso != NULL)
|
||||
bs_observer_del(bso);
|
||||
|
||||
pl_free(bs->pl);
|
||||
|
||||
@ -520,9 +717,6 @@ static void bfd_session_free(struct bfd_session *bs)
|
||||
struct bfd_session *ptm_bfd_sess_new(struct bfd_peer_cfg *bpc)
|
||||
{
|
||||
struct bfd_session *bfd, *l_bfd;
|
||||
struct interface *ifp = NULL;
|
||||
struct vrf *vrf = NULL;
|
||||
int psock;
|
||||
|
||||
/* check to see if this needs a new session */
|
||||
l_bfd = bs_peer_find(bpc);
|
||||
@ -534,115 +728,50 @@ struct bfd_session *ptm_bfd_sess_new(struct bfd_peer_cfg *bpc)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* No session found, we have to allocate a new one.
|
||||
*
|
||||
* First a few critical checks:
|
||||
*
|
||||
* * Check that the specified interface exists.
|
||||
* * Check that the specified VRF exists.
|
||||
* * Attempt to create the UDP socket (might fail if we exceed
|
||||
* our limits).
|
||||
*/
|
||||
if (bpc->bpc_has_localif) {
|
||||
ifp = if_lookup_by_name_all_vrf(bpc->bpc_localif);
|
||||
if (ifp == NULL) {
|
||||
log_error(
|
||||
"session-new: specified interface doesn't exists.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
vrf = vrf_lookup_by_id(ifp->vrf_id);
|
||||
if (vrf == NULL) {
|
||||
log_error("session-new: specified VRF doesn't exists.");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (bpc->bpc_has_vrfname) {
|
||||
vrf = vrf_lookup_by_name(bpc->bpc_vrfname);
|
||||
if (vrf == NULL) {
|
||||
log_error("session-new: specified VRF doesn't exists.");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Get socket for transmitting control packets. Note that if we
|
||||
* could use the destination port (3784) for the source
|
||||
* port we wouldn't need a socket per session.
|
||||
*/
|
||||
if (bpc->bpc_ipv4) {
|
||||
psock = bp_peer_socket(bpc);
|
||||
if (psock == -1)
|
||||
return NULL;
|
||||
} else {
|
||||
psock = bp_peer_socketv6(bpc);
|
||||
if (psock == -1)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Get memory */
|
||||
bfd = bfd_session_new(psock);
|
||||
/* Get BFD session storage with its defaults. */
|
||||
bfd = bfd_session_new();
|
||||
if (bfd == NULL) {
|
||||
log_error("session-new: allocation failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Assign VRF pointer. */
|
||||
bfd->vrf = vrf;
|
||||
if (bfd->vrf == NULL)
|
||||
bfd->vrf = vrf_lookup_by_id(VRF_DEFAULT);
|
||||
/*
|
||||
* Store interface/VRF name in case we need to delay session
|
||||
* start. See `bfd_session_enable` for more information.
|
||||
*/
|
||||
if (bpc->bpc_has_localif)
|
||||
strlcpy(bfd->ifname, bpc->bpc_localif, sizeof(bfd->ifname));
|
||||
|
||||
if (bpc->bpc_has_localif && !bpc->bpc_mhop)
|
||||
bfd->ifp = ifp;
|
||||
if (bpc->bpc_has_vrfname)
|
||||
strlcpy(bfd->vrfname, bpc->bpc_vrfname, sizeof(bfd->vrfname));
|
||||
|
||||
if (bpc->bpc_ipv4 == false) {
|
||||
/* Add observer if we have moving parts. */
|
||||
if (bfd->ifname[0] || bfd->vrfname[0])
|
||||
bs_observer_add(bfd);
|
||||
|
||||
/* Copy remaining data. */
|
||||
if (bpc->bpc_ipv4 == false)
|
||||
BFD_SET_FLAG(bfd->flags, BFD_SESS_FLAG_IPV6);
|
||||
|
||||
/* Set the IPv6 scope id for link-local addresses. */
|
||||
if (IN6_IS_ADDR_LINKLOCAL(&bpc->bpc_local.sa_sin6.sin6_addr))
|
||||
bpc->bpc_local.sa_sin6.sin6_scope_id =
|
||||
bfd->ifp != NULL ? bfd->ifp->ifindex
|
||||
: IFINDEX_INTERNAL;
|
||||
if (IN6_IS_ADDR_LINKLOCAL(&bpc->bpc_peer.sa_sin6.sin6_addr))
|
||||
bpc->bpc_peer.sa_sin6.sin6_scope_id =
|
||||
bfd->ifp != NULL ? bfd->ifp->ifindex
|
||||
: IFINDEX_INTERNAL;
|
||||
}
|
||||
|
||||
/* Initialize the session */
|
||||
bfd->ses_state = PTM_BFD_DOWN;
|
||||
bfd->discrs.my_discr = ptm_bfd_gen_ID();
|
||||
bfd->discrs.remote_discr = 0;
|
||||
bfd->local_ip = bpc->bpc_local;
|
||||
bfd->local_address = bpc->bpc_local;
|
||||
bfd_recvtimer_update(bfd);
|
||||
ptm_bfd_start_xmt_timer(bfd, false);
|
||||
|
||||
/* Registrate session into data structures. */
|
||||
bfd_id_insert(bfd);
|
||||
|
||||
if (bpc->bpc_mhop) {
|
||||
BFD_SET_FLAG(bfd->flags, BFD_SESS_FLAG_MH);
|
||||
bfd->mhop.peer = bpc->bpc_peer;
|
||||
bfd->mhop.local = bpc->bpc_local;
|
||||
if (vrf != NULL)
|
||||
bfd->mhop.vrfid = vrf->vrf_id;
|
||||
else
|
||||
bfd->mhop.vrfid = VRF_DEFAULT;
|
||||
|
||||
bfd_mhop_insert(bfd);
|
||||
} else {
|
||||
bfd->shop.peer = bpc->bpc_peer;
|
||||
if (ifp != NULL)
|
||||
bfd->shop.ifindex = ifp->ifindex;
|
||||
else
|
||||
bfd->shop.ifindex = IFINDEX_INTERNAL;
|
||||
|
||||
bfd_shop_insert(bfd);
|
||||
}
|
||||
|
||||
bfd->local_ip = bpc->bpc_local;
|
||||
bfd->local_address = bpc->bpc_local;
|
||||
|
||||
/* Try to enable session and schedule for packet receive/send. */
|
||||
if (bfd_session_enable(bfd) == -1) {
|
||||
/* Unrecoverable failure, remove the session/peer. */
|
||||
bfd_session_free(bfd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Apply other configurations. */
|
||||
_bfd_session_update(bfd, bpc);
|
||||
|
||||
log_info("session-new: %s", bs_to_string(bfd));
|
||||
@ -1123,6 +1252,31 @@ const char *bs_to_string(struct bfd_session *bs)
|
||||
return buf;
|
||||
}
|
||||
|
||||
int bs_observer_add(struct bfd_session *bs)
|
||||
{
|
||||
struct bfd_session_observer *bso;
|
||||
|
||||
bso = XMALLOC(MTYPE_BFDD_SESSION_OBSERVER, sizeof(*bso));
|
||||
bso->bso_bs = bs;
|
||||
bso->bso_isinterface = !BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH);
|
||||
if (bso->bso_isinterface)
|
||||
strlcpy(bso->bso_entryname, bs->ifname,
|
||||
sizeof(bso->bso_entryname));
|
||||
else
|
||||
strlcpy(bso->bso_entryname, bs->vrfname,
|
||||
sizeof(bso->bso_entryname));
|
||||
|
||||
TAILQ_INSERT_TAIL(&bglobal.bg_obslist, bso, bso_entry);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void bs_observer_del(struct bfd_session_observer *bso)
|
||||
{
|
||||
TAILQ_REMOVE(&bglobal.bg_obslist, bso, bso_entry);
|
||||
XFREE(MTYPE_BFDD_SESSION_OBSERVER, bso);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* BFD hash data structures to find sessions.
|
||||
|
23
bfdd/bfd.h
23
bfdd/bfd.h
@ -48,6 +48,7 @@ DECLARE_MTYPE(BFDD_TMP);
|
||||
DECLARE_MTYPE(BFDD_CONFIG);
|
||||
DECLARE_MTYPE(BFDD_LABEL);
|
||||
DECLARE_MTYPE(BFDD_CONTROL);
|
||||
DECLARE_MTYPE(BFDD_SESSION_OBSERVER);
|
||||
DECLARE_MTYPE(BFDD_NOTIFICATION);
|
||||
|
||||
struct bfd_timers {
|
||||
@ -239,6 +240,8 @@ struct bfd_session {
|
||||
struct sockaddr_any local_ip;
|
||||
struct interface *ifp;
|
||||
struct vrf *vrf;
|
||||
char ifname[MAXNAMELEN];
|
||||
char vrfname[MAXNAMELEN];
|
||||
uint8_t local_mac[ETHERNET_ADDRESS_LENGTH];
|
||||
uint8_t peer_mac[ETHERNET_ADDRESS_LENGTH];
|
||||
|
||||
@ -279,6 +282,15 @@ struct bfd_state_str_list {
|
||||
int type;
|
||||
};
|
||||
|
||||
struct bfd_session_observer {
|
||||
struct bfd_session *bso_bs;
|
||||
bool bso_isinterface;
|
||||
char bso_entryname[MAXNAMELEN];
|
||||
|
||||
TAILQ_ENTRY(bfd_session_observer) bso_entry;
|
||||
};
|
||||
TAILQ_HEAD(obslist, bfd_session_observer);
|
||||
|
||||
|
||||
/* States defined per 4.1 */
|
||||
#define PTM_BFD_ADM_DOWN 0
|
||||
@ -392,6 +404,8 @@ struct bfd_global {
|
||||
struct bcslist bg_bcslist;
|
||||
|
||||
struct pllist bg_pllist;
|
||||
|
||||
struct obslist bg_obslist;
|
||||
};
|
||||
extern struct bfd_global bglobal;
|
||||
extern struct bfd_diag_str_list diag_list[];
|
||||
@ -460,8 +474,8 @@ int bp_udp_shop(void);
|
||||
int bp_udp_mhop(void);
|
||||
int bp_udp6_shop(void);
|
||||
int bp_udp6_mhop(void);
|
||||
int bp_peer_socket(struct bfd_peer_cfg *bpc);
|
||||
int bp_peer_socketv6(struct bfd_peer_cfg *bpc);
|
||||
int bp_peer_socket(const struct bfd_session *);
|
||||
int bp_peer_socketv6(const struct bfd_session *);
|
||||
int bp_echo_socket(void);
|
||||
int bp_echov6_socket(void);
|
||||
|
||||
@ -499,6 +513,8 @@ void bfd_echo_xmttimer_assign(struct bfd_session *bs, bfd_ev_cb cb);
|
||||
*
|
||||
* BFD protocol specific code.
|
||||
*/
|
||||
int bfd_session_enable(struct bfd_session *);
|
||||
void bfd_session_disable(struct bfd_session *);
|
||||
struct bfd_session *ptm_bfd_sess_new(struct bfd_peer_cfg *bpc);
|
||||
int ptm_bfd_ses_del(struct bfd_peer_cfg *bpc);
|
||||
void ptm_bfd_ses_dn(struct bfd_session *bfd, uint8_t diag);
|
||||
@ -524,6 +540,9 @@ int strtosa(const char *addr, struct sockaddr_any *sa);
|
||||
void integer2timestr(uint64_t time, char *buf, size_t buflen);
|
||||
const char *bs_to_string(struct bfd_session *bs);
|
||||
|
||||
int bs_observer_add(struct bfd_session *);
|
||||
void bs_observer_del(struct bfd_session_observer *);
|
||||
|
||||
/* BFD hash data structures interface */
|
||||
void bfd_initialize(void);
|
||||
void bfd_shutdown(void);
|
||||
|
@ -887,7 +887,7 @@ int bp_udp_mhop(void)
|
||||
return sd;
|
||||
}
|
||||
|
||||
int bp_peer_socket(struct bfd_peer_cfg *bpc)
|
||||
int bp_peer_socket(const struct bfd_session *bs)
|
||||
{
|
||||
int sd, pcount;
|
||||
struct sockaddr_in sin;
|
||||
@ -912,28 +912,26 @@ int bp_peer_socket(struct bfd_peer_cfg *bpc)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (bpc->bpc_has_localif) {
|
||||
if (bp_bind_dev(sd, bpc->bpc_localif) != 0) {
|
||||
if (bs->shop.ifindex != IFINDEX_INTERNAL) {
|
||||
if (bp_bind_dev(sd, bs->ifp->name) != 0) {
|
||||
close(sd);
|
||||
return -1;
|
||||
}
|
||||
} else if (bpc->bpc_mhop && bpc->bpc_has_vrfname) {
|
||||
if (bp_bind_dev(sd, bpc->bpc_vrfname) != 0) {
|
||||
} else if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH) &&
|
||||
bs->mhop.vrfid != VRF_DEFAULT) {
|
||||
if (bp_bind_dev(sd, bs->vrf->name) != 0) {
|
||||
close(sd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Find an available source port in the proper range */
|
||||
memset(&sin, 0, sizeof(sin));
|
||||
sin = bpc->bpc_local.sa_sin;
|
||||
sin = bs->local_ip.sa_sin;
|
||||
sin.sin_family = AF_INET;
|
||||
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
|
||||
sin.sin_len = sizeof(sin);
|
||||
#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
|
||||
if (bpc->bpc_mhop)
|
||||
sin.sin_addr = bpc->bpc_local.sa_sin.sin_addr;
|
||||
else
|
||||
if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH) == 0)
|
||||
sin.sin_addr.s_addr = INADDR_ANY;
|
||||
|
||||
pcount = 0;
|
||||
@ -958,9 +956,8 @@ int bp_peer_socket(struct bfd_peer_cfg *bpc)
|
||||
* IPv6 sockets
|
||||
*/
|
||||
|
||||
int bp_peer_socketv6(struct bfd_peer_cfg *bpc)
|
||||
int bp_peer_socketv6(const struct bfd_session *bs)
|
||||
{
|
||||
struct interface *ifp;
|
||||
int sd, pcount;
|
||||
struct sockaddr_in6 sin6;
|
||||
static int srcPort = BFD_SRCPORTINIT;
|
||||
@ -985,25 +982,20 @@ int bp_peer_socketv6(struct bfd_peer_cfg *bpc)
|
||||
}
|
||||
|
||||
/* Find an available source port in the proper range */
|
||||
memset(&sin6, 0, sizeof(sin6));
|
||||
sin6 = bs->local_ip.sa_sin6;
|
||||
sin6.sin6_family = AF_INET6;
|
||||
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
|
||||
sin6.sin6_len = sizeof(sin6);
|
||||
#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
|
||||
sin6 = bpc->bpc_local.sa_sin6;
|
||||
if (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr)) {
|
||||
ifp = if_lookup_by_name(bpc->bpc_localif, VRF_DEFAULT);
|
||||
sin6.sin6_scope_id =
|
||||
(ifp != NULL) ? ifp->ifindex : IFINDEX_INTERNAL;
|
||||
}
|
||||
|
||||
if (bpc->bpc_has_localif) {
|
||||
if (bp_bind_dev(sd, bpc->bpc_localif) != 0) {
|
||||
if (bs->shop.ifindex != IFINDEX_INTERNAL) {
|
||||
if (bp_bind_dev(sd, bs->ifp->name) != 0) {
|
||||
close(sd);
|
||||
return -1;
|
||||
}
|
||||
} else if (bpc->bpc_mhop && bpc->bpc_has_vrfname) {
|
||||
if (bp_bind_dev(sd, bpc->bpc_vrfname) != 0) {
|
||||
} else if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH) &&
|
||||
bs->mhop.vrfid != VRF_DEFAULT) {
|
||||
if (bp_bind_dev(sd, bs->vrf->name) != 0) {
|
||||
close(sd);
|
||||
return -1;
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ DEFINE_MTYPE(BFDD, BFDD_TMP, "short-lived temporary memory");
|
||||
DEFINE_MTYPE(BFDD, BFDD_CONFIG, "long-lived configuration memory");
|
||||
DEFINE_MTYPE(BFDD, BFDD_LABEL, "long-lived label memory");
|
||||
DEFINE_MTYPE(BFDD, BFDD_CONTROL, "long-lived control socket memory");
|
||||
DEFINE_MTYPE(BFDD, BFDD_SESSION_OBSERVER, "Session observer");
|
||||
DEFINE_MTYPE(BFDD, BFDD_NOTIFICATION, "short-lived control notification data");
|
||||
|
||||
/* Master of threads. */
|
||||
@ -153,6 +154,7 @@ struct bfd_state_str_list state_list[] = {
|
||||
static void bg_init(void)
|
||||
{
|
||||
TAILQ_INIT(&bglobal.bg_bcslist);
|
||||
TAILQ_INIT(&bglobal.bg_obslist);
|
||||
|
||||
bglobal.bg_shop = bp_udp_shop();
|
||||
bglobal.bg_mhop = bp_udp_mhop();
|
||||
|
@ -51,7 +51,8 @@
|
||||
*/
|
||||
static int bfdd_write_config(struct vty *vty);
|
||||
static int bfdd_peer_write_config(struct vty *vty);
|
||||
static void _bfdd_peer_write_config(struct hash_backet *hb, void *arg);
|
||||
static void _bfdd_peer_write_config(struct vty *vty, struct bfd_session *bs);
|
||||
static void _bfdd_peer_write_config_iter(struct hash_backet *hb, void *arg);
|
||||
static int bfd_configure_peer(struct bfd_peer_cfg *bpc, bool mhop,
|
||||
const struct sockaddr_any *peer,
|
||||
const struct sockaddr_any *local,
|
||||
@ -369,16 +370,16 @@ static void _display_peer_header(struct vty *vty, struct bfd_session *bs)
|
||||
vty_out(vty, "\tpeer %s", satostr(&bs->mhop.peer));
|
||||
vty_out(vty, " multihop");
|
||||
vty_out(vty, " local-address %s", satostr(&bs->mhop.local));
|
||||
if (bs->mhop.vrfid != VRF_DEFAULT)
|
||||
vty_out(vty, " vrf %s", bs->vrf->name);
|
||||
if (bs->vrfname[0])
|
||||
vty_out(vty, " vrf %s", bs->vrfname);
|
||||
vty_out(vty, "\n");
|
||||
} else {
|
||||
vty_out(vty, "\tpeer %s", satostr(&bs->shop.peer));
|
||||
if (bs->local_address.sa_sin.sin_family != AF_UNSPEC)
|
||||
vty_out(vty, " local-address %s",
|
||||
satostr(&bs->local_address));
|
||||
if (bs->shop.ifindex != IFINDEX_INTERNAL)
|
||||
vty_out(vty, " interface %s", bs->ifp->name);
|
||||
if (bs->ifname[0])
|
||||
vty_out(vty, " interface %s", bs->ifname);
|
||||
vty_out(vty, "\n");
|
||||
}
|
||||
|
||||
@ -454,16 +455,16 @@ static struct json_object *_peer_json_header(struct bfd_session *bs)
|
||||
json_object_boolean_true_add(jo, "multihop");
|
||||
json_object_string_add(jo, "peer", satostr(&bs->mhop.peer));
|
||||
json_object_string_add(jo, "local", satostr(&bs->mhop.local));
|
||||
if (bs->mhop.vrfid != VRF_DEFAULT)
|
||||
json_object_string_add(jo, "vrf", bs->vrf->name);
|
||||
if (bs->vrfname[0])
|
||||
json_object_string_add(jo, "vrf", bs->vrfname);
|
||||
} else {
|
||||
json_object_boolean_false_add(jo, "multihop");
|
||||
json_object_string_add(jo, "peer", satostr(&bs->shop.peer));
|
||||
if (bs->local_address.sa_sin.sin_family != AF_UNSPEC)
|
||||
json_object_string_add(jo, "local",
|
||||
satostr(&bs->local_address));
|
||||
if (bs->shop.ifindex != IFINDEX_INTERNAL)
|
||||
json_object_string_add(jo, "interface", bs->ifp->name);
|
||||
if (bs->ifname[0])
|
||||
json_object_string_add(jo, "interface", bs->ifname);
|
||||
}
|
||||
|
||||
if (bs->pl)
|
||||
@ -910,28 +911,28 @@ static int bfdd_write_config(struct vty *vty)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _bfdd_peer_write_config(struct hash_backet *hb, void *arg)
|
||||
static void _bfdd_peer_write_config(struct vty *vty, struct bfd_session *bs)
|
||||
{
|
||||
struct vty *vty = arg;
|
||||
struct bfd_session *bs = hb->data;
|
||||
|
||||
if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) {
|
||||
vty_out(vty, " peer %s", satostr(&bs->mhop.peer));
|
||||
vty_out(vty, " multihop");
|
||||
vty_out(vty, " local-address %s", satostr(&bs->mhop.local));
|
||||
if (bs->mhop.vrfid != VRF_DEFAULT)
|
||||
vty_out(vty, " vrf %s", bs->vrf->name);
|
||||
if (bs->vrfname[0])
|
||||
vty_out(vty, " vrf %s", bs->vrfname);
|
||||
vty_out(vty, "\n");
|
||||
} else {
|
||||
vty_out(vty, " peer %s", satostr(&bs->shop.peer));
|
||||
if (bs->local_address.sa_sin.sin_family != AF_UNSPEC)
|
||||
vty_out(vty, " local-address %s",
|
||||
satostr(&bs->local_address));
|
||||
if (bs->shop.ifindex != IFINDEX_INTERNAL)
|
||||
vty_out(vty, " interface %s", bs->ifp->name);
|
||||
if (bs->ifname[0])
|
||||
vty_out(vty, " interface %s", bs->ifname);
|
||||
vty_out(vty, "\n");
|
||||
}
|
||||
|
||||
if (bs->sock == -1)
|
||||
vty_out(vty, " ! vrf or interface doesn't exist\n");
|
||||
|
||||
if (bs->detect_mult != BPC_DEF_DETECTMULTIPLIER)
|
||||
vty_out(vty, " detect-multiplier %d\n", bs->detect_mult);
|
||||
if (bs->timers.required_min_rx != (BPC_DEF_RECEIVEINTERVAL * 1000))
|
||||
@ -966,9 +967,27 @@ DEFUN_NOSH(show_debugging_bfd,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static void _bfdd_peer_write_config_iter(struct hash_backet *hb, void *arg)
|
||||
{
|
||||
struct vty *vty = arg;
|
||||
struct bfd_session *bs = hb->data;
|
||||
|
||||
_bfdd_peer_write_config(vty, bs);
|
||||
}
|
||||
|
||||
static int bfdd_peer_write_config(struct vty *vty)
|
||||
{
|
||||
bfd_id_iterate(_bfdd_peer_write_config, vty);
|
||||
struct bfd_session_observer *bso;
|
||||
|
||||
bfd_id_iterate(_bfdd_peer_write_config_iter, vty);
|
||||
TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) {
|
||||
/* Only print disabled sessions here. */
|
||||
if (bso->bso_bs->sock != -1)
|
||||
continue;
|
||||
|
||||
_bfdd_peer_write_config(vty, bso->bso_bs);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -321,9 +321,9 @@ static int parse_peer_label_config(struct json_object *jo,
|
||||
}
|
||||
} else {
|
||||
bpc->bpc_peer = pl->pl_bs->shop.peer;
|
||||
if (pl->pl_bs->shop.ifindex != IFINDEX_INTERNAL) {
|
||||
if (pl->pl_bs->ifname[0]) {
|
||||
bpc->bpc_has_localif = true;
|
||||
strlcpy(bpc->bpc_localif, pl->pl_bs->ifp->name,
|
||||
strlcpy(bpc->bpc_localif, pl->pl_bs->ifname,
|
||||
sizeof(bpc->bpc_localif));
|
||||
}
|
||||
}
|
||||
@ -531,8 +531,8 @@ static int json_object_add_peer(struct json_object *jo, struct bfd_session *bs)
|
||||
satostr(&bs->mhop.peer));
|
||||
json_object_string_add(jo, "local-address",
|
||||
satostr(&bs->mhop.local));
|
||||
if (bs->mhop.vrfid != VRF_DEFAULT)
|
||||
json_object_string_add(jo, "vrf-name", bs->vrf->name);
|
||||
if (bs->vrfname[0])
|
||||
json_object_string_add(jo, "vrf-name", bs->vrfname);
|
||||
} else {
|
||||
json_object_boolean_false_add(jo, "multihop");
|
||||
json_object_string_add(jo, "peer-address",
|
||||
@ -540,9 +540,9 @@ static int json_object_add_peer(struct json_object *jo, struct bfd_session *bs)
|
||||
if (bs->local_address.sa_sin.sin_family != AF_UNSPEC)
|
||||
json_object_string_add(jo, "local-address",
|
||||
satostr(&bs->local_address));
|
||||
if (bs->shop.ifindex != IFINDEX_INTERNAL)
|
||||
if (bs->ifname[0])
|
||||
json_object_string_add(jo, "local-interface",
|
||||
bs->ifp->name);
|
||||
bs->ifname);
|
||||
}
|
||||
|
||||
if (bs->pl)
|
||||
|
12
bfdd/event.c
12
bfdd/event.c
@ -43,7 +43,8 @@ void bfd_recvtimer_update(struct bfd_session *bs)
|
||||
bfd_recvtimer_delete(bs);
|
||||
|
||||
/* Don't add event if peer is deactivated. */
|
||||
if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN))
|
||||
if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN) ||
|
||||
bs->sock == -1)
|
||||
return;
|
||||
|
||||
tv_normalize(&tv);
|
||||
@ -63,7 +64,8 @@ void bfd_echo_recvtimer_update(struct bfd_session *bs)
|
||||
bfd_echo_recvtimer_delete(bs);
|
||||
|
||||
/* Don't add event if peer is deactivated. */
|
||||
if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN))
|
||||
if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN) ||
|
||||
bs->sock == -1)
|
||||
return;
|
||||
|
||||
tv_normalize(&tv);
|
||||
@ -83,7 +85,8 @@ void bfd_xmttimer_update(struct bfd_session *bs, uint64_t jitter)
|
||||
bfd_xmttimer_delete(bs);
|
||||
|
||||
/* Don't add event if peer is deactivated. */
|
||||
if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN))
|
||||
if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN) ||
|
||||
bs->sock == -1)
|
||||
return;
|
||||
|
||||
tv_normalize(&tv);
|
||||
@ -102,7 +105,8 @@ void bfd_echo_xmttimer_update(struct bfd_session *bs, uint64_t jitter)
|
||||
bfd_echo_xmttimer_delete(bs);
|
||||
|
||||
/* Don't add event if peer is deactivated. */
|
||||
if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN))
|
||||
if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN) ||
|
||||
bs->sock == -1)
|
||||
return;
|
||||
|
||||
tv_normalize(&tv);
|
||||
|
@ -597,23 +597,78 @@ static void bfdd_zebra_connected(struct zclient *zc)
|
||||
zclient_send_message(zclient);
|
||||
}
|
||||
|
||||
static int bfdd_interface_update(int cmd, struct zclient *zc, uint16_t len,
|
||||
static void bfdd_sessions_enable_interface(struct interface *ifp)
|
||||
{
|
||||
struct bfd_session_observer *bso;
|
||||
struct bfd_session *bs;
|
||||
|
||||
TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) {
|
||||
if (bso->bso_isinterface == false)
|
||||
continue;
|
||||
|
||||
/* Interface name mismatch. */
|
||||
bs = bso->bso_bs;
|
||||
if (strcmp(ifp->name, bs->ifname))
|
||||
continue;
|
||||
/* Skip enabled sessions. */
|
||||
if (bs->sock != -1)
|
||||
continue;
|
||||
|
||||
/* Try to enable it. */
|
||||
bfd_session_enable(bs);
|
||||
}
|
||||
}
|
||||
|
||||
static void bfdd_sessions_disable_interface(struct interface *ifp)
|
||||
{
|
||||
struct bfd_session_observer *bso;
|
||||
struct bfd_session *bs;
|
||||
|
||||
TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) {
|
||||
if (bso->bso_isinterface == false)
|
||||
continue;
|
||||
|
||||
/* Interface name mismatch. */
|
||||
bs = bso->bso_bs;
|
||||
if (strcmp(ifp->name, bs->ifname))
|
||||
continue;
|
||||
/* Skip disabled sessions. */
|
||||
if (bs->sock == -1)
|
||||
continue;
|
||||
|
||||
/* Try to enable it. */
|
||||
bfd_session_disable(bs);
|
||||
|
||||
TAILQ_INSERT_HEAD(&bglobal.bg_obslist, bso, bso_entry);
|
||||
}
|
||||
}
|
||||
|
||||
static int bfdd_interface_update(int cmd, struct zclient *zc,
|
||||
uint16_t len __attribute__((__unused__)),
|
||||
vrf_id_t vrfid)
|
||||
{
|
||||
struct interface *ifp;
|
||||
|
||||
/*
|
||||
* `zebra_interface_add_read` will handle the interface creation
|
||||
* on `lib/if.c`. We'll use that data structure instead of
|
||||
* rolling our own.
|
||||
*/
|
||||
if (cmd == ZEBRA_INTERFACE_ADD) {
|
||||
zebra_interface_add_read(zc->ibuf, vrfid);
|
||||
ifp = zebra_interface_add_read(zc->ibuf, vrfid);
|
||||
if (ifp == NULL)
|
||||
return 0;
|
||||
|
||||
bfdd_sessions_enable_interface(ifp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Update interface information. */
|
||||
zebra_interface_state_read(zc->ibuf, vrfid);
|
||||
ifp = zebra_interface_state_read(zc->ibuf, vrfid);
|
||||
if (ifp == NULL)
|
||||
return 0;
|
||||
|
||||
/* TODO: stop all sessions using this interface. */
|
||||
bfdd_sessions_disable_interface(ifp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user