From d245e522f0a48934bc0f815e0efa126054aa0b04 Mon Sep 17 00:00:00 2001 From: Rafael Zalamena Date: Sat, 2 Feb 2019 09:57:08 -0200 Subject: [PATCH] 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 --- bfdd/bfd.c | 374 ++++++++++++++++++++++++++++++++------------- bfdd/bfd.h | 23 ++- bfdd/bfd_packet.c | 38 ++--- bfdd/bfdd.c | 2 + bfdd/bfdd_vty.c | 55 ++++--- bfdd/config.c | 12 +- bfdd/event.c | 12 +- bfdd/ptm_adapter.c | 63 +++++++- 8 files changed, 412 insertions(+), 167 deletions(-) diff --git a/bfdd/bfd.c b/bfdd/bfd.c index aa09d0be4a..f2f63480f7 100644 --- a/bfdd/bfd.c +++ b/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); - - if (bpc->bpc_has_localif && !bpc->bpc_mhop) - bfd->ifp = ifp; + /* + * 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_ipv4 == false) { - BFD_SET_FLAG(bfd->flags, BFD_SESS_FLAG_IPV6); + if (bpc->bpc_has_vrfname) + strlcpy(bfd->vrfname, bpc->bpc_vrfname, sizeof(bfd->vrfname)); - /* 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; - } + /* Add observer if we have moving parts. */ + if (bfd->ifname[0] || bfd->vrfname[0]) + bs_observer_add(bfd); - /* 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); + /* Copy remaining data. */ + if (bpc->bpc_ipv4 == false) + BFD_SET_FLAG(bfd->flags, BFD_SESS_FLAG_IPV6); 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. diff --git a/bfdd/bfd.h b/bfdd/bfd.h index 64121ae979..b4d123647a 100644 --- a/bfdd/bfd.h +++ b/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); diff --git a/bfdd/bfd_packet.c b/bfdd/bfd_packet.c index 18d6ad2502..992ad09d32 100644 --- a/bfdd/bfd_packet.c +++ b/bfdd/bfd_packet.c @@ -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; } diff --git a/bfdd/bfdd.c b/bfdd/bfdd.c index 250f8d21c0..6023b5e4f0 100644 --- a/bfdd/bfdd.c +++ b/bfdd/bfdd.c @@ -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(); diff --git a/bfdd/bfdd_vty.c b/bfdd/bfdd_vty.c index 71429ffbbf..8d99ccc693 100644 --- a/bfdd/bfdd_vty.c +++ b/bfdd/bfdd_vty.c @@ -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; } diff --git a/bfdd/config.c b/bfdd/config.c index 9e3abcb1b8..d1342eff1e 100644 --- a/bfdd/config.c +++ b/bfdd/config.c @@ -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) diff --git a/bfdd/event.c b/bfdd/event.c index 3f48921af9..5ba54c2b0b 100644 --- a/bfdd/event.c +++ b/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); diff --git a/bfdd/ptm_adapter.c b/bfdd/ptm_adapter.c index 8ce33e5270..3f1512d8e7 100644 --- a/bfdd/ptm_adapter.c +++ b/bfdd/ptm_adapter.c @@ -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; } -- 2.39.5