diff --git a/bfdd/bfd.c b/bfdd/bfd.c index f32bc2598bdc..ced592319bae 100644 --- a/bfdd/bfd.c +++ b/bfdd/bfd.c @@ -23,6 +23,7 @@ DEFINE_MTYPE_STATIC(BFDD, BFDD_CONFIG, "long-lived configuration memory"); DEFINE_MTYPE_STATIC(BFDD, BFDD_PROFILE, "long-lived profile memory"); DEFINE_MTYPE_STATIC(BFDD, BFDD_SESSION_OBSERVER, "Session observer"); DEFINE_MTYPE_STATIC(BFDD, BFDD_VRF, "BFD VRF"); +DEFINE_MTYPE_STATIC(BFDD, SBFD_REFLECTOR, "SBFD REFLECTOR"); /* * Prototypes @@ -39,6 +40,10 @@ static void bs_down_handler(struct bfd_session *bs, int nstate); static void bs_init_handler(struct bfd_session *bs, int nstate); static void bs_up_handler(struct bfd_session *bs, int nstate); +static void ptm_sbfd_echo_xmt_TO(struct bfd_session *bfd); +static void sbfd_down_handler(struct bfd_session *bs, int nstate); +static void sbfd_up_handler(struct bfd_session *bs, int nstate); + /** * Remove BFD profile from all BFD sessions so we don't leave dangling * pointers. @@ -192,10 +197,12 @@ void bfd_session_apply(struct bfd_session *bs) } /* Toggle 'passive-mode' if default value. */ - if (bs->peer_profile.passive == false) - bfd_set_passive_mode(bs, bp->passive); - else - bfd_set_passive_mode(bs, bs->peer_profile.passive); + if (bs->bfd_mode == BFD_MODE_TYPE_BFD) { + if (bs->peer_profile.passive == false) + bfd_set_passive_mode(bs, bp->passive); + else + bfd_set_passive_mode(bs, bs->peer_profile.passive); + } /* Toggle 'no shutdown' if default value. */ if (bs->peer_profile.admin_shutdown == false) @@ -224,8 +231,10 @@ void bfd_profile_remove(struct bfd_session *bs) void gen_bfd_key(struct bfd_key *key, struct sockaddr_any *peer, struct sockaddr_any *local, bool mhop, const char *ifname, - const char *vrfname) + const char *vrfname, const char *bfdname) { + struct vrf *vrf = NULL; + memset(key, 0, sizeof(*key)); switch (peer->sa_sin.sin_family) { @@ -248,10 +257,20 @@ void gen_bfd_key(struct bfd_key *key, struct sockaddr_any *peer, key->mhop = mhop; if (ifname && ifname[0]) strlcpy(key->ifname, ifname, sizeof(key->ifname)); - if (vrfname && vrfname[0]) - strlcpy(key->vrfname, vrfname, sizeof(key->vrfname)); - else + if (vrfname && vrfname[0] && strcmp(vrfname, VRF_DEFAULT_NAME) != 0) { + vrf = vrf_lookup_by_name(vrfname); + if (vrf) { + strlcpy(key->vrfname, vrf->name, sizeof(key->vrfname)); + } else { + strlcpy(key->vrfname, vrfname, sizeof(key->vrfname)); + } + } else { strlcpy(key->vrfname, VRF_DEFAULT_NAME, sizeof(key->vrfname)); + } + + if (bfdname && bfdname[0]) { + strlcpy(key->bfdname, bfdname, sizeof(key->bfdname)); + } } struct bfd_session *bs_peer_find(struct bfd_peer_cfg *bpc) @@ -260,7 +279,7 @@ struct bfd_session *bs_peer_find(struct bfd_peer_cfg *bpc) /* Otherwise fallback to peer/local hash lookup. */ gen_bfd_key(&key, &bpc->bpc_peer, &bpc->bpc_local, bpc->bpc_mhop, - bpc->bpc_localif, bpc->bpc_vrfname); + bpc->bpc_localif, bpc->bpc_vrfname, bpc->bfd_name); return bfd_key_lookup(key); } @@ -333,14 +352,24 @@ int bfd_session_enable(struct bfd_session *bs) * could use the destination port (3784) for the source * port we wouldn't need a socket per session. */ - if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_IPV6) == 0) { + if (bs->bfd_mode == BFD_MODE_TYPE_SBFD_ECHO || bs->bfd_mode == BFD_MODE_TYPE_SBFD_INIT) { + psock = bp_peer_srh_socketv6(bs); + if (psock <= 0) { + zlog_err("bp_peer_srh_socketv6 error"); + return 0; + } + } else if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_IPV6) == 0) { psock = bp_peer_socket(bs); - if (psock == -1) + if (psock == -1) { + zlog_err("bp_peer_socket error"); return 0; + } } else { psock = bp_peer_socketv6(bs); - if (psock == -1) + if (psock == -1) { + zlog_err("bp_peer_socketv6 error"); return 0; + } } /* @@ -351,10 +380,18 @@ int bfd_session_enable(struct bfd_session *bs) /* Only start timers if we are using active mode. */ if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_PASSIVE) == 0) { - bfd_recvtimer_update(bs); - ptm_bfd_start_xmt_timer(bs, false); - } + if (bs->bfd_mode == BFD_MODE_TYPE_SBFD_ECHO) { + /*enable receive echo response*/ + bfd_set_echo(bs, true); + bs->echo_detect_TO = (bs->remote_detect_mult * bs->echo_xmt_TO); + sbfd_echo_recvtimer_update(bs); + ptm_bfd_start_xmt_timer(bs, true); + } else { + bfd_recvtimer_update(bs); + ptm_bfd_start_xmt_timer(bs, false); + } + } /* initialize RTT */ bfd_rtt_init(bs); @@ -383,6 +420,8 @@ void bfd_session_disable(struct bfd_session *bs) bfd_recvtimer_delete(bs); bfd_xmttimer_delete(bs); ptm_bfd_echo_stop(bs); + bs->vrf = NULL; + bs->ifp = NULL; /* Set session down so it doesn't report UP and disabled. */ ptm_bfd_sess_dn(bs, BD_PATH_DOWN); @@ -422,10 +461,18 @@ void ptm_bfd_start_xmt_timer(struct bfd_session *bfd, bool is_echo) jitter = (xmt_TO * (75 + (frr_weak_random() % maxpercent))) / 100; /* XXX remove that division above */ - if (is_echo) - bfd_echo_xmttimer_update(bfd, jitter); - else - bfd_xmttimer_update(bfd, jitter); + if (bfd->bfd_mode == BFD_MODE_TYPE_SBFD_ECHO || bfd->bfd_mode == BFD_MODE_TYPE_SBFD_INIT) { + if (is_echo) + sbfd_echo_xmttimer_update(bfd, jitter); + else + sbfd_init_xmttimer_update(bfd, jitter); + + } else { + if (is_echo) + bfd_echo_xmttimer_update(bfd, jitter); + else + bfd_xmttimer_update(bfd, jitter); + } } static void ptm_bfd_echo_xmt_TO(struct bfd_session *bfd) @@ -456,6 +503,37 @@ void ptm_bfd_xmt_TO(struct bfd_session *bfd, int fbit) ptm_bfd_start_xmt_timer(bfd, false); } +static void ptm_sbfd_echo_xmt_TO(struct bfd_session *bfd) +{ + /* Send the scheduled sbfd-echo packet */ + ptm_sbfd_echo_snd(bfd); + + /* Restart the timer for next time */ + ptm_bfd_start_xmt_timer(bfd, true); +} + +void ptm_sbfd_init_xmt_TO(struct bfd_session *bfd, int fbit) +{ + /* Send the scheduled control packet */ + ptm_sbfd_initiator_snd(bfd, fbit); + + /* Restart the timer for next time */ + ptm_bfd_start_xmt_timer(bfd, false); +} + +void ptm_sbfd_init_reset(struct bfd_session *bfd) +{ + bfd->xmt_TO = BFD_DEF_SLOWTX; + bfd->detect_TO = 0; + ptm_sbfd_init_xmt_TO(bfd, 0); +} +void ptm_sbfd_echo_reset(struct bfd_session *bfd) +{ + bfd->echo_xmt_TO = SBFD_ECHO_DEF_SLOWTX; + bfd->echo_detect_TO = 0; + ptm_sbfd_echo_xmt_TO(bfd); +} + void ptm_bfd_echo_stop(struct bfd_session *bfd) { bfd->echo_xmt_TO = 0; @@ -550,12 +628,103 @@ void ptm_bfd_sess_dn(struct bfd_session *bfd, uint8_t diag) UNSET_FLAG(bfd->flags, BFD_SESS_FLAG_MAC_SET); memset(bfd->peer_hw_addr, 0, sizeof(bfd->peer_hw_addr)); /* reset local address ,it might has been be changed after bfd is up*/ - memset(&bfd->local_address, 0, sizeof(bfd->local_address)); + if (bfd->bfd_mode == BFD_MODE_TYPE_BFD) + memset(&bfd->local_address, 0, sizeof(bfd->local_address)); /* reset RTT */ bfd_rtt_init(bfd); } +/*sbfd session up , include sbfd and sbfd echo*/ +void ptm_sbfd_sess_up(struct bfd_session *bfd) +{ + int old_state = bfd->ses_state; + + bfd->local_diag = 0; + bfd->ses_state = PTM_BFD_UP; + monotime(&bfd->uptime); + + /*notify session up*/ + ptm_bfd_notify(bfd, bfd->ses_state); + + if (old_state != bfd->ses_state) { + bfd->stats.session_up++; + if (bglobal.debug_peer_event) + zlog_info("state-change: [%s] %s -> %s", bs_to_string(bfd), + state_list[old_state].str, state_list[bfd->ses_state].str); + } +} + +/*sbfd init session TO */ +void ptm_sbfd_init_sess_dn(struct bfd_session *bfd, uint8_t diag) +{ + int old_state = bfd->ses_state; + + bfd->local_diag = diag; + bfd->ses_state = PTM_BFD_DOWN; + bfd->polling = 0; + bfd->demand_mode = 0; + monotime(&bfd->downtime); + + /* + * Only attempt to send if we have a valid socket: + * this function might be called by session disablers and in + * this case we won't have a valid socket (i.e. interface was + * removed or VRF doesn't exist anymore). + */ + if (bfd->sock != -1) + ptm_sbfd_init_reset(bfd); + + /* Slow down the control packets, the connection is down. */ + bs_set_slow_timers(bfd); + + /* only signal clients when going from up->down state */ + if (old_state == PTM_BFD_UP) + ptm_bfd_notify(bfd, PTM_BFD_DOWN); + + /* Stop attempting to transmit or expect control packets if passive. */ + if (CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_PASSIVE)) { + sbfd_init_recvtimer_delete(bfd); + sbfd_init_xmttimer_delete(bfd); + } + + if (old_state != bfd->ses_state) { + bfd->stats.session_down++; + if (bglobal.debug_peer_event) + zlog_debug("state-change: [%s] %s -> %s reason:%s", bs_to_string(bfd), + state_list[old_state].str, state_list[bfd->ses_state].str, + get_diag_str(bfd->local_diag)); + } + /* reset local address ,it might has been be changed after bfd is up*/ + //memset(&bfd->local_address, 0, sizeof(bfd->local_address)); +} + +/*sbfd echo session TO */ +void ptm_sbfd_echo_sess_dn(struct bfd_session *bfd, uint8_t diag) +{ + int old_state = bfd->ses_state; + + bfd->local_diag = diag; + bfd->discrs.remote_discr = 0; + bfd->ses_state = PTM_BFD_DOWN; + bfd->polling = 0; + bfd->demand_mode = 0; + monotime(&bfd->downtime); + /* only signal clients when going from up->down state */ + if (old_state == PTM_BFD_UP) + ptm_bfd_notify(bfd, PTM_BFD_DOWN); + + ptm_sbfd_echo_reset(bfd); + + if (old_state != bfd->ses_state) { + bfd->stats.session_down++; + if (bglobal.debug_peer_event) + zlog_warn("state-change: [%s] %s -> %s reason:%s", bs_to_string(bfd), + state_list[old_state].str, state_list[bfd->ses_state].str, + get_diag_str(bfd->local_diag)); + } +} + static struct bfd_session *bfd_find_disc(struct sockaddr_any *sa, uint32_t ldisc) { @@ -599,7 +768,7 @@ struct bfd_session *ptm_bfd_sess_find(struct bfd_pkt *cp, vrf = vrf_lookup_by_id(vrfid); gen_bfd_key(&key, peer, local, is_mhop, ifp ? ifp->name : NULL, - vrf ? vrf->name : VRF_DEFAULT_NAME); + vrf ? vrf->name : VRF_DEFAULT_NAME, NULL); /* XXX maybe remoteDiscr should be checked for remoteHeard cases. */ return bfd_key_lookup(key); @@ -620,6 +789,21 @@ void bfd_echo_xmt_cb(struct event *t) ptm_bfd_echo_xmt_TO(bs); } +void sbfd_init_xmt_cb(struct event *t) +{ + struct bfd_session *bs = EVENT_ARG(t); + + ptm_sbfd_init_xmt_TO(bs, 0); +} + +void sbfd_echo_xmt_cb(struct event *t) +{ + struct bfd_session *bs = EVENT_ARG(t); + + if (bs->echo_xmt_TO > 0) + ptm_sbfd_echo_xmt_TO(bs); +} + /* Was ptm_bfd_detect_TO() */ void bfd_recvtimer_cb(struct event *t) { @@ -638,6 +822,11 @@ void bfd_echo_recvtimer_cb(struct event *t) { struct bfd_session *bs = EVENT_ARG(t); + if (bglobal.debug_peer_event) { + zlog_debug("%s: time-out bfd: [%s] bfd'state is %s", __func__, bs_to_string(bs), + state_list[bs->ses_state].str); + } + switch (bs->ses_state) { case PTM_BFD_INIT: case PTM_BFD_UP: @@ -646,11 +835,49 @@ void bfd_echo_recvtimer_cb(struct event *t) } } -struct bfd_session *bfd_session_new(void) +void sbfd_init_recvtimer_cb(struct event *t) +{ + struct bfd_session *bs = EVENT_ARG(t); + + switch (bs->ses_state) { + case PTM_BFD_INIT: + case PTM_BFD_UP: + ptm_sbfd_init_sess_dn(bs, BD_PATH_DOWN); + break; + + default: + /* Second detect time expiration, zero remote discr (section + * 6.5.1) + */ + break; + } +} +void sbfd_echo_recvtimer_cb(struct event *t) +{ + struct bfd_session *bs = EVENT_ARG(t); + + if (bglobal.debug_peer_event) { + zlog_debug("%s: time-out bfd: [%s] bfd'state is %s", __func__, bs_to_string(bs), + state_list[bs->ses_state].str); + } + + switch (bs->ses_state) { + case PTM_BFD_INIT: + case PTM_BFD_UP: + ptm_sbfd_echo_sess_dn(bs, BD_PATH_DOWN); + break; + case PTM_BFD_DOWN: + break; + } +} + +struct bfd_session *bfd_session_new(enum bfd_mode_type mode) { struct bfd_session *bs; - bs = XCALLOC(MTYPE_BFDD_CONFIG, sizeof(*bs)); + bs = XCALLOC(MTYPE_BFDD_CONFIG, sizeof(struct bfd_session)); + bs->segnum = 0; + bs->bfd_mode = mode; /* Set peer session defaults. */ bfd_profile_set_default(&bs->peer_profile); @@ -788,7 +1015,7 @@ struct bfd_session *ptm_bfd_sess_new(struct bfd_peer_cfg *bpc) } /* Get BFD session storage with its defaults. */ - bfd = bfd_session_new(); + bfd = bfd_session_new(BFD_MODE_TYPE_BFD); /* * Store interface/VRF name in case we need to delay session @@ -970,6 +1197,30 @@ static void bs_down_handler(struct bfd_session *bs, int nstate) } } +static void sbfd_down_handler(struct bfd_session *bs, int nstate) +{ + switch (nstate) { + case PTM_BFD_ADM_DOWN: + /* + * Remote peer doesn't want to talk, so lets keep the + * connection down. + */ + break; + case PTM_BFD_UP: + /* down - > up*/ + ptm_sbfd_sess_up(bs); + break; + + case PTM_BFD_DOWN: + break; + + default: + if (bglobal.debug_peer_event) + zlog_err("state-change: unhandled sbfd state: %d", nstate); + break; + } +} + static void bs_init_handler(struct bfd_session *bs, int nstate) { switch (nstate) { @@ -1021,6 +1272,29 @@ static void bs_up_handler(struct bfd_session *bs, int nstate) } } +static void sbfd_up_handler(struct bfd_session *bs, int nstate) +{ + switch (nstate) { + case PTM_BFD_ADM_DOWN: + case PTM_BFD_DOWN: + if (bs->bfd_mode == BFD_MODE_TYPE_SBFD_ECHO) { + ptm_sbfd_echo_sess_dn(bs, BD_ECHO_FAILED); + } else + ptm_sbfd_init_sess_dn(bs, BD_ECHO_FAILED); + + break; + + case PTM_BFD_UP: + /* Path is up and working. */ + break; + + default: + if (bglobal.debug_peer_event) + zlog_debug("state-change: unhandled neighbor state: %d", nstate); + break; + } +} + void bs_state_handler(struct bfd_session *bs, int nstate) { switch (bs->ses_state) { @@ -1045,6 +1319,58 @@ void bs_state_handler(struct bfd_session *bs, int nstate) } } +void sbfd_echo_state_handler(struct bfd_session *bs, int nstate) +{ + if (bglobal.debug_peer_event) + zlog_debug("%s: bfd(%u) state: %s , notify state: %s", __func__, + bs->discrs.my_discr, state_list[bs->ses_state].str, + state_list[nstate].str); + + switch (bs->ses_state) { + case PTM_BFD_ADM_DOWN: + // bs_admin_down_handler(bs, nstate); + break; + case PTM_BFD_DOWN: + sbfd_down_handler(bs, nstate); + break; + case PTM_BFD_UP: + sbfd_up_handler(bs, nstate); + break; + + default: + if (bglobal.debug_peer_event) + zlog_debug("state-change: [%s] is in invalid state: %d", bs_to_string(bs), + nstate); + break; + } +} + +void sbfd_initiator_state_handler(struct bfd_session *bs, int nstate) +{ + if (bglobal.debug_peer_event) + zlog_debug("%s: sbfd(%u) state: %s , notify state: %s", __func__, + bs->discrs.my_discr, state_list[bs->ses_state].str, + state_list[nstate].str); + + switch (bs->ses_state) { + case PTM_BFD_ADM_DOWN: + // bs_admin_down_handler(bs, nstate); + break; + case PTM_BFD_DOWN: + sbfd_down_handler(bs, nstate); + break; + case PTM_BFD_UP: + sbfd_up_handler(bs, nstate); + break; + + default: + if (bglobal.debug_peer_event) + zlog_debug("state-change: [%s] is in invalid state: %d", bs_to_string(bs), + nstate); + break; + } +} + /* * Handles echo timer manipulation after updating timer. */ @@ -1147,6 +1473,15 @@ void bs_set_slow_timers(struct bfd_session *bs) /* Set the appropriated timeouts for slow connection. */ bs->detect_TO = (BFD_DEFDETECTMULT * BFD_DEF_SLOWTX); bs->xmt_TO = BFD_DEF_SLOWTX; + + /* add for sbfd-echo slow connection */ + if (BFD_MODE_TYPE_SBFD_ECHO == bs->bfd_mode) { + bs->echo_xmt_TO = SBFD_ECHO_DEF_SLOWTX; + bs->timers.desired_min_echo_tx = BFD_DEFDESIREDMINTX; + bs->timers.required_min_echo_rx = BFD_DEFDESIREDMINTX; + bs->peer_profile.min_echo_rx = BFD_DEFDESIREDMINTX; + bs->peer_profile.min_echo_tx = BFD_DEFDESIREDMINTX; + } } void bfd_set_echo(struct bfd_session *bs, bool echo) @@ -1438,6 +1773,8 @@ const char *bs_to_string(const struct bfd_session *bs) if (bs->key.ifname[0]) pos += snprintf(buf + pos, sizeof(buf) - pos, " ifname:%s", bs->key.ifname); + if (bs->bfd_name[0]) + pos += snprintf(buf + pos, sizeof(buf) - pos, " bfd_name:%s", bs->bfd_name); (void)pos; @@ -1516,6 +1853,10 @@ void bs_to_bpc(struct bfd_session *bs, struct bfd_peer_cfg *bpc) static struct hash *bfd_id_hash; static struct hash *bfd_key_hash; +/*sbfd reflector discr hash*/ +static struct hash *sbfd_rflt_hash; +static unsigned int sbfd_discr_hash_do(const void *p); + static unsigned int bfd_id_hash_do(const void *p); static unsigned int bfd_key_hash_do(const void *p); @@ -1567,6 +1908,8 @@ static bool bfd_key_hash_cmp(const void *n1, const void *n2) if (memcmp(bs1->key.vrfname, bs2->key.vrfname, sizeof(bs1->key.vrfname))) return false; + if (memcmp(bs1->key.bfdname, bs2->key.bfdname, sizeof(bs1->key.bfdname))) + return false; /* * Local address is optional and can be empty. @@ -1591,6 +1934,20 @@ static bool bfd_key_hash_cmp(const void *n1, const void *n2) return true; } +/* SBFD disr hash . */ +static unsigned int sbfd_discr_hash_do(const void *p) +{ + const struct sbfd_reflector *sr = p; + + return jhash_1word(sr->discr, 0); +} + +static bool sbfd_discr_hash_cmp(const void *n1, const void *n2) +{ + const struct sbfd_reflector *sr1 = n1, *sr2 = n2; + + return sr1->discr == sr2->discr; +} /* * Hash public interface / exported functions. @@ -1615,6 +1972,15 @@ struct bfd_session *bfd_key_lookup(struct bfd_key key) return hash_lookup(bfd_key_hash, &bs); } +struct sbfd_reflector *sbfd_discr_lookup(uint32_t discr) +{ + struct sbfd_reflector sr; + + sr.discr = discr; + + return hash_lookup(sbfd_rflt_hash, &sr); +} + /* * Delete functions. * @@ -1643,6 +2009,15 @@ struct bfd_session *bfd_key_delete(struct bfd_key key) return hash_release(bfd_key_hash, &bs); } +struct sbfd_reflector *sbfd_discr_delete(uint32_t discr) +{ + struct sbfd_reflector sr; + + sr.discr = discr; + + return hash_release(sbfd_rflt_hash, &sr); +} + /* Iteration functions. */ void bfd_id_iterate(hash_iter_func hif, void *arg) { @@ -1654,6 +2029,11 @@ void bfd_key_iterate(hash_iter_func hif, void *arg) hash_iterate(bfd_key_hash, hif, arg); } +void sbfd_discr_iterate(hash_iter_func hif, void *arg) +{ + hash_iterate(sbfd_rflt_hash, hif, arg); +} + /* * Insert functions. * @@ -1670,12 +2050,24 @@ bool bfd_key_insert(struct bfd_session *bs) return (hash_get(bfd_key_hash, bs, hash_alloc_intern) == bs); } +bool sbfd_discr_insert(struct sbfd_reflector *sr) +{ + return (hash_get(sbfd_rflt_hash, sr, hash_alloc_intern) == sr); +} + +unsigned long sbfd_discr_get_count(void) +{ + return sbfd_rflt_hash->count; +} + void bfd_initialize(void) { bfd_id_hash = hash_create(bfd_id_hash_do, bfd_id_hash_cmp, "BFD session discriminator hash"); bfd_key_hash = hash_create(bfd_key_hash_do, bfd_key_hash_cmp, "BFD session hash"); + sbfd_rflt_hash = hash_create(sbfd_discr_hash_do, sbfd_discr_hash_cmp, + "SBFD reflector discriminator hash"); TAILQ_INIT(&bplist); } @@ -1687,6 +2079,14 @@ static void _bfd_free(struct hash_bucket *hb, bfd_session_free(bs); } +static void _sbfd_reflector_free(struct hash_bucket *hb, void *arg __attribute__((__unused__))) +{ + struct sbfd_reflector *sr = hb->data; + + + sbfd_reflector_free(sr->discr); +} + void bfd_shutdown(void) { struct bfd_profile *bp; @@ -1701,9 +2101,13 @@ void bfd_shutdown(void) bfd_id_iterate(_bfd_free, NULL); assert(bfd_key_hash->count == 0); + sbfd_discr_iterate(_sbfd_reflector_free, NULL); + assert(sbfd_rflt_hash->count == 0); + /* Now free the hashes themselves. */ hash_free(bfd_id_hash); hash_free(bfd_key_hash); + hash_free(sbfd_rflt_hash); /* Free all profile allocations. */ while ((bp = TAILQ_FIRST(&bplist)) != NULL) @@ -1713,6 +2117,7 @@ void bfd_shutdown(void) struct bfd_session_iterator { int bsi_stop; bool bsi_mhop; + uint32_t bsi_bfdmode; const struct bfd_session *bsi_bs; }; @@ -1724,7 +2129,7 @@ static int _bfd_session_next(struct hash_bucket *hb, void *arg) /* Previous entry signaled stop. */ if (bsi->bsi_stop == 1) { /* Match the single/multi hop sessions. */ - if (bs->key.mhop != bsi->bsi_mhop) + if ((bs->key.mhop != bsi->bsi_mhop) || (bs->bfd_mode != bsi->bsi_bfdmode)) return HASHWALK_CONTINUE; bsi->bsi_bs = bs; @@ -1736,7 +2141,8 @@ static int _bfd_session_next(struct hash_bucket *hb, void *arg) bsi->bsi_stop = 1; /* Set entry to NULL to signal end of list. */ bsi->bsi_bs = NULL; - } else if (bsi->bsi_bs == NULL && bsi->bsi_mhop == bs->key.mhop) { + } else if (bsi->bsi_bs == NULL && bsi->bsi_mhop == bs->key.mhop && + bsi->bsi_bfdmode == bs->bfd_mode) { /* We want the first list item. */ bsi->bsi_stop = 1; bsi->bsi_bs = hb->data; @@ -1752,13 +2158,14 @@ static int _bfd_session_next(struct hash_bucket *hb, void *arg) * `bs` might point to NULL to get the first item of the data structure. */ const struct bfd_session *bfd_session_next(const struct bfd_session *bs, - bool mhop) + bool mhop, uint32_t bfd_mode) { struct bfd_session_iterator bsi; bsi.bsi_stop = 0; bsi.bsi_bs = bs; bsi.bsi_mhop = mhop; + bsi.bsi_bfdmode = bfd_mode; hash_walk(bfd_key_hash, _bfd_session_next, &bsi); if (bsi.bsi_stop == 0) return NULL; @@ -1924,6 +2331,7 @@ static int bfd_vrf_new(struct vrf *vrf) bvrf->bg_mhop6 = -1; bvrf->bg_echo = -1; bvrf->bg_echov6 = -1; + bvrf->bg_initv6 = -1; return 0; } @@ -1957,6 +2365,8 @@ static int bfd_vrf_enable(struct vrf *vrf) bvrf->bg_shop6 = bp_udp6_shop(vrf); if (bvrf->bg_mhop6 == -1) bvrf->bg_mhop6 = bp_udp6_mhop(vrf); + if (bvrf->bg_initv6 == -1) + bvrf->bg_initv6 = bp_initv6_socket(vrf); if (bvrf->bg_ev[0] == NULL && bvrf->bg_shop != -1) event_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_shop, @@ -1970,6 +2380,8 @@ static int bfd_vrf_enable(struct vrf *vrf) if (bvrf->bg_ev[3] == NULL && bvrf->bg_mhop6 != -1) event_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_mhop6, &bvrf->bg_ev[3]); + if (bvrf->bg_ev[6] == NULL && bvrf->bg_initv6 != -1) + event_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_initv6, &bvrf->bg_ev[6]); /* Toggle echo if VRF was disabled. */ bfd_vrf_toggle_echo(bvrf); @@ -2006,6 +2418,7 @@ static int bfd_vrf_disable(struct vrf *vrf) EVENT_OFF(bvrf->bg_ev[3]); EVENT_OFF(bvrf->bg_ev[4]); EVENT_OFF(bvrf->bg_ev[5]); + EVENT_OFF(bvrf->bg_ev[6]); /* Close all descriptors. */ socket_close(&bvrf->bg_echo); @@ -2014,6 +2427,7 @@ static int bfd_vrf_disable(struct vrf *vrf) socket_close(&bvrf->bg_shop6); socket_close(&bvrf->bg_mhop6); socket_close(&bvrf->bg_echov6); + socket_close(&bvrf->bg_initv6); return 0; } @@ -2050,6 +2464,79 @@ unsigned long bfd_get_session_count(void) return bfd_key_hash->count; } +struct sbfd_reflector *sbfd_reflector_new(const uint32_t discr, struct in6_addr *sip) +{ + struct sbfd_reflector *sr; + + sr = sbfd_discr_lookup(discr); + if (sr) + return sr; + + sr = XCALLOC(MTYPE_SBFD_REFLECTOR, sizeof(*sr)); + sr->discr = discr; + memcpy(&sr->local, sip, sizeof(struct in6_addr)); + + sbfd_discr_insert(sr); + + + return sr; +} + +void sbfd_reflector_free(const uint32_t discr) +{ + struct sbfd_reflector *sr; + + sr = sbfd_discr_lookup(discr); + if (!sr) + return; + + sbfd_discr_delete(discr); + XFREE(MTYPE_SBFD_REFLECTOR, sr); + + return; +} + +void sbfd_reflector_flush() +{ + sbfd_discr_iterate(_sbfd_reflector_free, NULL); + return; +} + +struct bfd_session_name_match_unique { + const char *bfd_name; + struct bfd_session *bfd_found; +}; + +static int _bfd_session_name_cmp(struct hash_bucket *hb, void *arg) +{ + struct bfd_session *bs = hb->data; + struct bfd_session_name_match_unique *match = (struct bfd_session_name_match_unique *)arg; + + if (strlen(bs->bfd_name) != strlen(match->bfd_name)) { + return HASHWALK_CONTINUE; + } + + if (!strncmp(bs->bfd_name, match->bfd_name, strlen(bs->bfd_name))) { + match->bfd_found = bs; + return HASHWALK_ABORT; + } + return HASHWALK_CONTINUE; +} + +struct bfd_session *bfd_session_get_by_name(const char *name) +{ + if (!name || name[0] == '\0') + return NULL; + + struct bfd_session_name_match_unique match; + match.bfd_name = name; + match.bfd_found = NULL; + + hash_walk(bfd_key_hash, _bfd_session_name_cmp, &match); + + return match.bfd_found; +} + void bfd_rtt_init(struct bfd_session *bfd) { uint8_t i; diff --git a/bfdd/bfd.h b/bfdd/bfd.h index d4d14ffce675..9ba554948d9a 100644 --- a/bfdd/bfd.h +++ b/bfdd/bfd.h @@ -19,6 +19,7 @@ #include "lib/qobj.h" #include "lib/queue.h" #include "lib/vrf.h" +#include "lib/bfd.h" #ifdef BFD_DEBUG #define BFDD_JSON_CONV_OPTIONS (JSON_C_TO_STRING_PRETTY) @@ -86,6 +87,10 @@ struct bfd_peer_cfg { bool bpc_has_profile; char bpc_profile[64]; + + vrf_id_t vrf_id; + char bfd_name[BFD_NAME_SIZE + 1]; + uint8_t bfd_name_len; }; /* bfd Authentication Type. */ @@ -147,7 +152,6 @@ struct bfd_echo_pkt { uint64_t time_sent_usec; }; - /* Macros for manipulating control packets */ #define BFD_VERMASK 0x07 #define BFD_DIAGMASK 0x1F @@ -194,6 +198,8 @@ struct bfd_echo_pkt { #define BFD_ECHO_VERSION 1 #define BFD_ECHO_PKT_LEN sizeof(struct bfd_echo_pkt) +#define RTH_BASE_HEADER_LEN 8 +#define GET_RTH_HDR_LEN(size) (((size) >> 3) - 1) enum bfd_diagnosticis { BD_OK = 0, /* Control Detection Time Expired. */ @@ -235,6 +241,12 @@ enum bfd_session_flags { BFD_SESS_FLAG_MAC_SET = 1 << 11, /* MAC of peer known */ }; +enum bfd_mode_type { + BFD_MODE_TYPE_BFD = 0, + BFD_MODE_TYPE_SBFD_ECHO = 1, + BFD_MODE_TYPE_SBFD_INIT = 2, +}; + /* * BFD session hash key. * @@ -254,6 +266,7 @@ struct bfd_key { struct in6_addr local; char ifname[IFNAMSIZ]; char vrfname[VRF_NAMSIZ]; + char bfdname[BFD_NAME_SIZE + 1]; } __attribute__((packed)); struct bfd_session_stats { @@ -264,6 +277,7 @@ struct bfd_session_stats { uint64_t session_up; uint64_t session_down; uint64_t znotification; + uint64_t tx_fail_pkt; }; /** @@ -375,6 +389,12 @@ struct bfd_session { uint8_t rtt_valid; /* number of valid samples */ uint8_t rtt_index; /* last index added */ uint64_t rtt[BFD_RTT_SAMPLE]; /* RRT in usec for echo to be looped */ + char bfd_name[BFD_NAME_SIZE + 1]; + + uint32_t bfd_mode; + uint8_t segnum; + struct in6_addr out_sip6; + struct in6_addr seg_list[SRV6_MAX_SEGS]; }; struct bfd_diag_str_list { @@ -396,6 +416,11 @@ struct bfd_session_observer { }; TAILQ_HEAD(obslist, bfd_session_observer); +/*sbfd reflector struct*/ +struct sbfd_reflector { + uint32_t discr; + struct in6_addr local; +}; /* States defined per 4.1 */ #define PTM_BFD_ADM_DOWN 0 @@ -413,6 +438,7 @@ TAILQ_HEAD(obslist, bfd_session_observer); #define BFD_DEF_DES_MIN_ECHO_TX (50 * 1000) /* microseconds. */ #define BFD_DEF_REQ_MIN_ECHO_RX (50 * 1000) /* microseconds. */ #define BFD_DEF_SLOWTX (1000 * 1000) /* microseconds. */ +#define SBFD_ECHO_DEF_SLOWTX (1000 * 1000) /* microseconds. */ /** Minimum multi hop TTL. */ #define BFD_DEF_MHOP_TTL 254 #define BFD_PKT_LEN 24 /* Length of control packet */ @@ -427,7 +453,9 @@ TAILQ_HEAD(obslist, bfd_session_observer); #define BFD_DEFDESTPORT 3784 #define BFD_DEF_ECHO_PORT 3785 #define BFD_DEF_MHOP_DEST_PORT 4784 +#define BFD_DEF_SBFD_DEST_PORT 7784 +#define BFD_SBFD_INITIATOR_DEMAND 1 /* * bfdd.c @@ -441,9 +469,10 @@ struct bfd_vrf_global { int bg_mhop6; int bg_echo; int bg_echov6; + int bg_initv6; struct vrf *vrf; - struct event *bg_ev[6]; + struct event *bg_ev[7]; }; /* Forward declaration of data plane context struct. */ @@ -519,6 +548,7 @@ int bp_set_ttl(int sd, uint8_t value); int bp_set_tosv6(int sd, uint8_t value); int bp_set_tos(int sd, uint8_t value); int bp_bind_dev(int sd, const char *dev); +void bp_set_prio(int sd, int value); int bp_udp_shop(const struct vrf *vrf); int bp_udp_mhop(const struct vrf *vrf); @@ -528,10 +558,15 @@ int bp_peer_socket(const struct bfd_session *bs); int bp_peer_socketv6(const struct bfd_session *bs); int bp_echo_socket(const struct vrf *vrf); int bp_echov6_socket(const struct vrf *vrf); +int bp_peer_srh_socketv6(struct bfd_session *bs); +int bp_sbfd_socket(const struct vrf *vrf); +int bp_initv6_socket(const struct vrf *vrf); void ptm_bfd_snd(struct bfd_session *bfd, int fbit); void ptm_bfd_echo_snd(struct bfd_session *bfd); void ptm_bfd_echo_fp_snd(struct bfd_session *bfd); +void ptm_sbfd_echo_snd(struct bfd_session *bfd); +void ptm_sbfd_initiator_snd(struct bfd_session *bfd, int fbit); void bfd_recv_cb(struct event *t); @@ -545,13 +580,21 @@ typedef void (*bfd_ev_cb)(struct event *t); void bfd_recvtimer_update(struct bfd_session *bs); void bfd_echo_recvtimer_update(struct bfd_session *bs); +void sbfd_init_recvtimer_update(struct bfd_session *bs); +void sbfd_echo_recvtimer_update(struct bfd_session *bs); void bfd_xmttimer_update(struct bfd_session *bs, uint64_t jitter); void bfd_echo_xmttimer_update(struct bfd_session *bs, uint64_t jitter); +void sbfd_init_xmttimer_update(struct bfd_session *bs, uint64_t jitter); +void sbfd_echo_xmttimer_update(struct bfd_session *bs, uint64_t jitter); void bfd_xmttimer_delete(struct bfd_session *bs); void bfd_echo_xmttimer_delete(struct bfd_session *bs); +void sbfd_init_xmttimer_delete(struct bfd_session *bs); +void sbfd_echo_xmttimer_delete(struct bfd_session *bs); void bfd_recvtimer_delete(struct bfd_session *bs); void bfd_echo_recvtimer_delete(struct bfd_session *bs); +void sbfd_init_recvtimer_delete(struct bfd_session *bs); +void sbfd_echo_recvtimer_delete(struct bfd_session *bs); void bfd_recvtimer_assign(struct bfd_session *bs, bfd_ev_cb cb, int sd); void bfd_echo_recvtimer_assign(struct bfd_session *bs, bfd_ev_cb cb, int sd); @@ -574,6 +617,9 @@ void ptm_bfd_echo_stop(struct bfd_session *bfd); void ptm_bfd_echo_start(struct bfd_session *bfd); void ptm_bfd_xmt_TO(struct bfd_session *bfd, int fbit); void ptm_bfd_start_xmt_timer(struct bfd_session *bfd, bool is_echo); +void ptm_sbfd_init_xmt_TO(struct bfd_session *bfd, int fbit); +void ptm_sbfd_init_reset(struct bfd_session *bfd); +void ptm_sbfd_echo_reset(struct bfd_session *bfd); struct bfd_session *ptm_bfd_sess_find(struct bfd_pkt *cp, struct sockaddr_any *peer, struct sockaddr_any *local, @@ -600,14 +646,15 @@ void bs_to_bpc(struct bfd_session *bs, struct bfd_peer_cfg *bpc); void gen_bfd_key(struct bfd_key *key, struct sockaddr_any *peer, struct sockaddr_any *local, bool mhop, const char *ifname, - const char *vrfname); -struct bfd_session *bfd_session_new(void); + const char *vrfname, const char *bfdname); +struct bfd_session *bfd_session_new(enum bfd_mode_type mode); struct bfd_session *bs_registrate(struct bfd_session *bs); void bfd_session_free(struct bfd_session *bs); const struct bfd_session *bfd_session_next(const struct bfd_session *bs, - bool mhop); + bool mhop, uint32_t bfd_mode); void bfd_sessions_remove_manual(void); void bfd_profiles_remove(void); +void bs_sbfd_echo_timer_handler(struct bfd_session *bs); void bfd_rtt_init(struct bfd_session *bfd); extern void bfd_vrf_toggle_echo(struct bfd_vrf_global *bfd_vrf); @@ -653,18 +700,22 @@ void bfd_vrf_terminate(void); struct bfd_vrf_global *bfd_vrf_look_by_session(struct bfd_session *bfd); struct bfd_session *bfd_id_lookup(uint32_t id); struct bfd_session *bfd_key_lookup(struct bfd_key key); - +struct sbfd_reflector *sbfd_discr_lookup(uint32_t discr); struct bfd_session *bfd_id_delete(uint32_t id); struct bfd_session *bfd_key_delete(struct bfd_key key); +struct sbfd_reflector *sbfd_discr_delete(uint32_t discr); bool bfd_id_insert(struct bfd_session *bs); bool bfd_key_insert(struct bfd_session *bs); +bool sbfd_discr_insert(struct sbfd_reflector *sr); typedef void (*hash_iter_func)(struct hash_bucket *hb, void *arg); void bfd_id_iterate(hash_iter_func hif, void *arg); void bfd_key_iterate(hash_iter_func hif, void *arg); +void sbfd_discr_iterate(hash_iter_func hif, void *arg); unsigned long bfd_get_session_count(void); +unsigned long sbfd_discr_get_count(void); /* Export callback functions for `event.c`. */ extern struct event_loop *master; @@ -674,6 +725,11 @@ void bfd_echo_recvtimer_cb(struct event *t); void bfd_xmt_cb(struct event *t); void bfd_echo_xmt_cb(struct event *t); +void sbfd_init_recvtimer_cb(struct event *t); +void sbfd_echo_recvtimer_cb(struct event *t); +void sbfd_init_xmt_cb(struct event *t); +void sbfd_echo_xmt_cb(struct event *t); + extern struct in6_addr zero_addr; /** @@ -809,4 +865,17 @@ int bfd_dplane_update_session_counters(struct bfd_session *bs); void bfd_dplane_show_counters(struct vty *vty); +/*sbfd relfector*/ +struct sbfd_reflector *sbfd_reflector_new(const uint32_t discr, struct in6_addr *sip); +void sbfd_reflector_free(const uint32_t discr); +void sbfd_reflector_flush(void); + +/*sbfd*/ +void ptm_sbfd_echo_sess_dn(struct bfd_session *bfd, uint8_t diag); +void ptm_sbfd_init_sess_dn(struct bfd_session *bfd, uint8_t diag); +void ptm_sbfd_sess_up(struct bfd_session *bfd); +void sbfd_echo_state_handler(struct bfd_session *bs, int nstate); +void sbfd_initiator_state_handler(struct bfd_session *bs, int nstate); + +struct bfd_session *bfd_session_get_by_name(const char *name); #endif /* _BFD_H_ */ diff --git a/bfdd/bfd_packet.c b/bfdd/bfd_packet.c index f9397fa128db..0fee42ea8442 100644 --- a/bfdd/bfd_packet.c +++ b/bfdd/bfd_packet.c @@ -16,20 +16,28 @@ #ifdef GNU_LINUX #include +#include +#include #endif #ifdef BFD_LINUX #include +#include +#include #endif /* BFD_LINUX */ #include #include +#include +#include #include "lib/sockopt.h" #include "lib/checksum.h" #include "lib/network.h" #include "bfd.h" +#define BUF_SIZ 1024 +#define SOCK_OPT_PRIO_HIGH 6 /* * Prototypes @@ -49,11 +57,18 @@ int bp_udp_send(int sd, uint8_t ttl, uint8_t *data, size_t datalen, struct sockaddr *to, socklen_t tolen); int bp_bfd_echo_in(struct bfd_vrf_global *bvrf, int sd, uint8_t *ttl, uint32_t *my_discr, uint64_t *my_rtt); +static int ptm_bfd_reflector_process_init_packet(struct bfd_vrf_global *bvrf, int s); +int _ptm_sbfd_init_send(struct bfd_session *bs, const void *data, size_t datalen); + #ifdef BFD_LINUX -ssize_t bfd_recv_ipv4_fp(int sd, uint8_t *msgbuf, size_t msgbuflen, +static int bp_raw_sbfd_red_send(int sd, uint8_t *data, size_t datalen, uint16_t family, + struct in6_addr *out_sip, struct in6_addr *sip, + struct in6_addr *dip, uint16_t src_port, uint16_t dst_port, + uint8_t seg_num, struct in6_addr *segment_list); +static 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, +static 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); @@ -374,7 +389,24 @@ static int ptm_bfd_process_echo_pkt(struct bfd_vrf_global *bvrf, int s) /* Compute detect time */ bfd->echo_detect_TO = bfd->remote_detect_mult * bfd->echo_xmt_TO; - /* Update echo receive timeout. */ + /* Update sbfd-echo session state */ + if (bfd->bfd_mode == BFD_MODE_TYPE_SBFD_ECHO) { + sbfd_echo_state_handler(bfd, PTM_BFD_UP); + + if (bfd->echo_xmt_TO != bfd->timers.desired_min_echo_tx) { + bfd->echo_xmt_TO = bfd->timers.desired_min_echo_tx; + //reset xmt timer TO after UP + ptm_bfd_start_xmt_timer(bfd, true); + } + + bfd->echo_detect_TO = bfd->detect_mult * bfd->echo_xmt_TO; + /* Update sbfd echo receive timeout. */ + if (bfd->echo_detect_TO > 0) + sbfd_echo_recvtimer_update(bfd); + return 0; + } + + /* Update bfd-echo receive timeout. */ if (bfd->echo_detect_TO > 0) bfd_echo_recvtimer_update(bfd); @@ -438,7 +470,7 @@ void ptm_bfd_snd(struct bfd_session *bfd, int fbit) /* * 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, +static 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) { @@ -729,6 +761,9 @@ static void bfd_sd_reschedule(struct bfd_vrf_global *bvrf, int sd) EVENT_OFF(bvrf->bg_ev[5]); event_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_echov6, &bvrf->bg_ev[5]); + } else if (sd == bvrf->bg_initv6) { + EVENT_OFF(bvrf->bg_ev[6]); + event_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_initv6, &bvrf->bg_ev[6]); } } @@ -822,6 +857,11 @@ void bfd_recv_cb(struct event *t) /* Schedule next read. */ bfd_sd_reschedule(bvrf, sd); + /* The reflector handle SBFD init packets. */ + if (sd == bvrf->bg_initv6) { + ptm_bfd_reflector_process_init_packet(bvrf, sd); + return; + } /* Handle echo packets. */ if (sd == bvrf->bg_echo || sd == bvrf->bg_echov6) { ptm_bfd_process_echo_pkt(bvrf, sd); @@ -996,6 +1036,29 @@ void bfd_recv_cb(struct event *t) else bfd->remote_cbit = 0; + /* The initiator handle SBFD reflect packet. */ + if (bfd->bfd_mode == BFD_MODE_TYPE_SBFD_INIT) { + sbfd_initiator_state_handler(bfd, PTM_BFD_UP); + if (bfd->xmt_TO != bfd->timers.desired_min_tx) { + bfd->xmt_TO = bfd->timers.desired_min_tx; + //reset xmt timer TO after UP + ptm_bfd_start_xmt_timer(bfd, false); + } + + bfd->detect_TO = bfd->detect_mult * bfd->xmt_TO; + sbfd_init_recvtimer_update(bfd); + + if (bfd->polling && BFD_GETFBIT(cp->flags)) { + /* Disable polling. */ + bfd->polling = 0; + /* Start using our new timers. */ + bfd->cur_timers.desired_min_tx = bfd->timers.desired_min_tx; + bfd->cur_timers.required_min_rx = bfd->timers.required_min_rx; + } + + return; + } + /* State switch from section 6.2. */ bs_state_handler(bfd, BFD_GETSTATE(cp->flags)); @@ -1352,6 +1415,16 @@ static void bp_bind_ip(int sd, uint16_t port) zlog_fatal("bind-ip: bind: %s", strerror(errno)); } +void bp_set_prio(int sd, int value) +{ +#if defined(GNU_LINUX) + int priority = value; + + if (setsockopt(sd, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority)) < 0) + zlog_warn("set_prio: setsockopt(SO_PRIORITY, %d): %s", value, strerror(errno)); +#endif +} + int bp_udp_shop(const struct vrf *vrf) { int sd; @@ -1421,6 +1494,8 @@ int bp_peer_socket(const struct bfd_session *bs) return -1; } + bp_set_prio(sd, SOCK_OPT_PRIO_HIGH); + /* Find an available source port in the proper range */ memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; @@ -1487,6 +1562,8 @@ int bp_peer_socketv6(const struct bfd_session *bs) return -1; } + bp_set_prio(sd, SOCK_OPT_PRIO_HIGH); + /* Find an available source port in the proper range */ memset(&sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_INET6; @@ -1731,7 +1808,7 @@ int bp_echov6_socket(const struct vrf *vrf) /* 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, +static void bfd_peer_mac_set(int sd, struct bfd_session *bfd, struct sockaddr_any *peer, struct interface *ifp) { struct arpreq arpreq_; @@ -1768,3 +1845,725 @@ void bfd_peer_mac_set(int sd, struct bfd_session *bfd, } } #endif + +int _ptm_sbfd_init_send(struct bfd_session *bfd, const void *data, size_t datalen) +{ +#ifdef BFD_LINUX + int sd = -1; + struct bfd_vrf_global *bvrf = bfd_vrf_look_by_session(bfd); + + int seg_num; + struct in6_addr *segment_list = NULL; + struct in6_addr peer; + struct in6_addr local; + + if (!bvrf) + return -1; + + seg_num = bfd->segnum; + if (seg_num > 0) + segment_list = bfd->seg_list; + + sd = bfd->sock; + + local = bfd->key.local; + peer = bfd->key.peer; + + /*SBFD Control pkt dst port should be 7784, src port can be any but NOT 7784 according to RFC7881 */ + if (bp_raw_sbfd_red_send(sd, (uint8_t *)data, datalen, bfd->key.family, &bfd->out_sip6, + &local, &peer, + CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_MH) ? BFD_DEF_MHOP_DEST_PORT + : BFD_DEFDESTPORT, + BFD_DEF_SBFD_DEST_PORT, seg_num, segment_list) < 0) { + if (bfd->stats.tx_fail_pkt <= 1) { + char dst[INET6_ADDRSTRLEN] = { 0 }; + + inet_ntop(AF_INET6, seg_num > 0 ? segment_list : (&bfd->key.peer), dst, + sizeof(dst)); + zlog_err("sbfd initiator send failed, dst:%s, errno:%s", dst, + safe_strerror(errno)); + } + + bfd->stats.tx_fail_pkt++; + return -1; + } + + if (bfd->stats.tx_fail_pkt > 0) { + char dst[INET6_ADDRSTRLEN] = { 0 }; + + inet_ntop(AF_INET6, seg_num > 0 ? segment_list : (&bfd->key.peer), dst, sizeof(dst)); + zlog_warn("sbfd initiator send success, dst:%s, previous tx_fail_pkt:%d", dst, + (int)bfd->stats.tx_fail_pkt); + } + bfd->stats.tx_fail_pkt = 0; + + bfd->stats.tx_ctrl_pkt++; +#endif + return 0; +} + +static int _ptm_sbfd_echo_send(struct bfd_session *bfd, const void *data, size_t datalen) +{ +#ifdef BFD_LINUX + int sd = -1; + struct bfd_vrf_global *bvrf = bfd_vrf_look_by_session(bfd); + + int seg_num; + struct in6_addr *segment_list = NULL; + struct in6_addr peer; + struct in6_addr local; + + if (!bvrf) + return -1; + + seg_num = bfd->segnum; + if (seg_num > 0) + segment_list = bfd->seg_list; + + sd = bfd->sock; + + local = bfd->key.local; + peer = bfd->key.peer; + + /*SBFD echo pkt dst port should use BFD Echo port 3785, src port can be any according to RFC7881*/ + if (bp_raw_sbfd_red_send(sd, (uint8_t *)data, datalen, bfd->key.family, &bfd->out_sip6, + &local, &peer, BFD_DEF_ECHO_PORT, BFD_DEF_ECHO_PORT, seg_num, + segment_list) < 0) { + if (bfd->stats.tx_fail_pkt <= 1) { + char dst[INET6_ADDRSTRLEN] = { 0 }; + + inet_ntop(AF_INET6, seg_num > 0 ? segment_list : (&bfd->key.peer), dst, + sizeof(dst)); + zlog_err("sbfd echo send failed, bfd_name:%s, dst:%s, errno:%s", + bfd->bfd_name, dst, safe_strerror(errno)); + } + + bfd->stats.tx_fail_pkt++; + return -1; + } + + if (bfd->stats.tx_fail_pkt > 0) { + char dst[INET6_ADDRSTRLEN] = { 0 }; + + inet_ntop(AF_INET6, seg_num > 0 ? segment_list : (&bfd->key.peer), dst, sizeof(dst)); + zlog_warn("sbfd echo send success, bfd_name:%s, dst:%s, previous tx_fail_pkt:%d", + bfd->bfd_name, dst, (int)bfd->stats.tx_fail_pkt); + } + bfd->stats.tx_fail_pkt = 0; + + bfd->stats.tx_echo_pkt++; +#endif + return 0; +} + +void ptm_sbfd_initiator_snd(struct bfd_session *bfd, int fbit) +{ + struct bfd_pkt cp = {}; + + /* 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); + + if (CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_CBIT)) + BFD_SETCBIT(cp.flags, BFD_CBIT); + + BFD_SETDEMANDBIT(cp.flags, BFD_SBFD_INITIATOR_DEMAND); + + /* + * Polling and Final can't be set at the same time. + * + * RFC 5880, Section 6.5. + */ + BFD_SETFBIT(cp.flags, fbit); + if (fbit == 0) + BFD_SETPBIT(cp.flags, bfd->polling); + + 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 = htonl(bfd->timers.desired_min_tx); + } else { + /* + * 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 = 0; + cp.timers.required_min_echo = 0; + + if (_ptm_sbfd_init_send(bfd, &cp, BFD_PKT_LEN) != 0) + return; + + bfd->stats.tx_ctrl_pkt++; +} +void ptm_sbfd_echo_snd(struct bfd_session *bfd) +{ + struct bfd_echo_pkt bep; + + memset(&bep, 0, sizeof(bep)); + BFD_SETVER(bep.ver, BFD_ECHO_VERSION); + bep.len = BFD_ECHO_PKT_LEN; + bep.my_discr = htonl(bfd->discrs.my_discr); + + if (_ptm_sbfd_echo_send(bfd, &bep, BFD_ECHO_PKT_LEN) != 0) + return; + + if (!CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE)) + SET_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE); +} + +static int ptm_bfd_reflector_process_init_packet(struct bfd_vrf_global *bvrf, int sd) +{ + //uint32_t my_discr = 0; + //uint32_t remote_discr = 0; + uint8_t ttl = 0; + struct sockaddr *sa; + struct sbfd_reflector *sr; + /* Receive and parse echo packet. */ + struct bfd_pkt *cp; + ssize_t rlen; + struct sockaddr_any local, peer; + ifindex_t ifindex = IFINDEX_INTERNAL; + //vrf_id_t vrfid = VRF_DEFAULT; + uint8_t msgbuf[1516]; + + rlen = bfd_recv_ipv6(sd, msgbuf, sizeof(msgbuf), &ttl, &ifindex, &local, &peer); + /* Short packet, better not risk reading it. */ + if (rlen < (ssize_t)sizeof(*cp)) { + zlog_debug("small bfd packet"); + return 0; + } + cp = (struct bfd_pkt *)(msgbuf); + if (!CHECK_FLAG(cp->flags, BFD_DEMANDBIT)) { + /*Control Packet from SBFDInitiator should have Demand bit set to 1 according to RFC7880*/ + return 0; + } + + sr = sbfd_discr_lookup(ntohl(cp->discrs.remote_discr)); + if (sr) { + uint32_t temp = cp->discrs.my_discr; + + cp->discrs.my_discr = cp->discrs.remote_discr; + cp->discrs.remote_discr = temp; + UNSET_FLAG(cp->flags, BFD_DEMANDBIT); + BFD_SETSTATE(cp->flags, PTM_BFD_UP); + if (CHECK_FLAG(cp->flags, BFD_PBIT)) { + UNSET_FLAG(cp->flags, BFD_PBIT); + SET_FLAG(cp->flags, BFD_FBIT); + } + + sa = (struct sockaddr *)&peer.sa_sin6; + + if (sendto(sd, msgbuf, rlen, 0, sa, sizeof(peer.sa_sin6)) <= 0) { + zlog_debug("packet-send: send failure: %s", strerror(errno)); + return -1; + } + } else { + zlog_debug("no reflector found in %u", cp->discrs.remote_discr); + } + return 0; +} + +int bp_peer_srh_socketv6(struct bfd_session *bs) +{ + int sd; //, pcount; + //struct sockaddr_in6 sin6; + //static int srcPort = BFD_SRCPORTINIT; + const char *device_to_bind = NULL; + + if (bs->key.ifname[0]) { + device_to_bind = (const char *)bs->key.ifname; + zlog_debug("device_to_bind to ifname:%s", device_to_bind); + } else if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH) && bs->key.vrfname[0]) { + device_to_bind = (const char *)bs->key.vrfname; + zlog_debug("device_to_bind to vrf:%s", device_to_bind); + } else { + zlog_debug("device_to_bind to NULL"); + } + + frr_with_privs (&bglobal.bfdd_privs) { + sd = vrf_socket(AF_INET6, SOCK_RAW, IPPROTO_RAW, bs->vrf->vrf_id, device_to_bind); + } + if (sd == -1) { + zlog_err("ipv6-new: failed to create socket: %s", strerror(errno)); + return -1; + } + + /* Set TTL to 255 for all transmitted packets */ + if (bp_set_ttlv6(sd, BFD_TTL_VAL) != 0) { + close(sd); + return -1; + } + + /* Set TOS to CS6 for all transmitted packets */ + if (bp_set_tosv6(sd, BFD_TOS_VAL) != 0) { + close(sd); + return -1; + } +#if defined(HAVE_IPV6_HDRINCL) + int on = 1; + + /*manage the IP6 header all on own onwn*/ + if (setsockopt(sd, IPPROTO_IPV6, IPV6_HDRINCL, &on, sizeof(on))) { + zlog_err("setsockopt IPV6_HDRINCL error: %s", strerror(errno)); + close(sd); + return -1; + } +#endif + + return sd; +} + +int bp_initv6_socket(const struct vrf *vrf) +{ + int sd; + + frr_with_privs (&bglobal.bfdd_privs) { + sd = vrf_socket(AF_INET6, SOCK_DGRAM, 0, vrf->vrf_id, vrf->name); + } + if (sd == -1) { + if (errno != EAFNOSUPPORT) + zlog_fatal("echov6-socket: socket: %s", strerror(errno)); + else + zlog_warn("echov6-socket: V6 is not supported, continuing"); + + return -1; + } + + bp_set_ipv6opts(sd); + bp_bind_ipv6(sd, BFD_DEF_SBFD_DEST_PORT); + + return sd; +} + +#ifdef BFD_LINUX +static uint16_t checksum(uint16_t *addr, int len) +{ + int count = len; + uint16_t answer = 0; + + register uint32_t sum = 0; + + // Sum up 2-byte values until none or only one byte left. + while (count > 1) { + sum += *(addr++); + count -= 2; + } + + // Add left-over byte, if any. + if (count > 0) + sum += *(uint8_t *)addr; + + // Fold 32-bit sum into 16 bits; we lose information by doing this, + // increasing the chances of a collision. + // sum = (lower 16 bits) + (upper 16 bits shifted right 16 bits) + while (sum >> 16) + sum = (sum & 0xffff) + (sum >> 16); + + // Checksum is one's compliment of sum. + answer = ~sum; + + return answer; +} + +static uint16_t udp6_checksum(struct ip6_hdr iphdr, struct udphdr udp_hdr, uint8_t *payload, + int payloadlen) +{ + char buf[IP_MAXPACKET]; + char *ptr; + int chksumlen = 0; + int i; + + ptr = &buf[0]; // ptr points to beginning of buffer buf + + // Copy source IP address into buf (128 bits) + memcpy(ptr, &iphdr.ip6_src.s6_addr, sizeof(iphdr.ip6_src.s6_addr)); + ptr += sizeof(iphdr.ip6_src.s6_addr); + chksumlen += sizeof(iphdr.ip6_src.s6_addr); + + // Copy destination IP address into buf (128 bits) + memcpy(ptr, &iphdr.ip6_dst.s6_addr, sizeof(iphdr.ip6_dst.s6_addr)); + ptr += sizeof(iphdr.ip6_dst.s6_addr); + chksumlen += sizeof(iphdr.ip6_dst.s6_addr); + + // Copy UDP length into buf (32 bits) + memcpy(ptr, &udp_hdr.len, sizeof(udp_hdr.len)); + ptr += sizeof(udp_hdr.len); + chksumlen += sizeof(udp_hdr.len); + + // Copy zero field to buf (24 bits) + *ptr = 0; + ptr++; + *ptr = 0; + ptr++; + *ptr = 0; + ptr++; + chksumlen += 3; + + // Copy next header field to buf (8 bits) + memcpy(ptr, &iphdr.ip6_nxt, sizeof(iphdr.ip6_nxt)); + ptr += sizeof(iphdr.ip6_nxt); + chksumlen += sizeof(iphdr.ip6_nxt); + + // Copy UDP source port to buf (16 bits) + memcpy(ptr, &udp_hdr.source, sizeof(udp_hdr.source)); + ptr += sizeof(udp_hdr.source); + chksumlen += sizeof(udp_hdr.source); + + // Copy UDP destination port to buf (16 bits) + memcpy(ptr, &udp_hdr.dest, sizeof(udp_hdr.dest)); + ptr += sizeof(udp_hdr.dest); + chksumlen += sizeof(udp_hdr.dest); + + // Copy UDP length again to buf (16 bits) + memcpy(ptr, &udp_hdr.len, sizeof(udp_hdr.len)); + ptr += sizeof(udp_hdr.len); + chksumlen += sizeof(udp_hdr.len); + + // Copy UDP checksum to buf (16 bits) + // Zero, since we don't know it yet + *ptr = 0; + ptr++; + *ptr = 0; + ptr++; + chksumlen += 2; + + // Copy payload to buf + memcpy(ptr, payload, payloadlen * sizeof(uint8_t)); + ptr += payloadlen; + chksumlen += payloadlen; + + // Pad to the next 16-bit boundary + for (i = 0; i < payloadlen % 2; i++, ptr++) { + *ptr = 0; + ptr++; + chksumlen++; + } + + return checksum((uint16_t *)buf, chksumlen); +} + +// Build IPv4 UDP pseudo-header and call checksum function. +static uint16_t udp4_checksum(struct ip iphdr, struct udphdr udp_hdr, uint8_t *payload, + int payloadlen) +{ + char buf[IP_MAXPACKET]; + char *ptr; + int chksumlen = 0; + int i; + + ptr = &buf[0]; // ptr points to beginning of buffer buf + + // Copy source IP address into buf (32 bits) + memcpy(ptr, &iphdr.ip_src.s_addr, sizeof(iphdr.ip_src.s_addr)); + ptr += sizeof(iphdr.ip_src.s_addr); + chksumlen += sizeof(iphdr.ip_src.s_addr); + + // Copy destination IP address into buf (32 bits) + memcpy(ptr, &iphdr.ip_dst.s_addr, sizeof(iphdr.ip_dst.s_addr)); + ptr += sizeof(iphdr.ip_dst.s_addr); + chksumlen += sizeof(iphdr.ip_dst.s_addr); + + // Copy zero field to buf (8 bits) + *ptr = 0; + ptr++; + chksumlen += 1; + + // Copy transport layer protocol to buf (8 bits) + memcpy(ptr, &iphdr.ip_p, sizeof(iphdr.ip_p)); + ptr += sizeof(iphdr.ip_p); + chksumlen += sizeof(iphdr.ip_p); + + // Copy UDP length to buf (16 bits) + memcpy(ptr, &udp_hdr.len, sizeof(udp_hdr.len)); + ptr += sizeof(udp_hdr.len); + chksumlen += sizeof(udp_hdr.len); + + // Copy UDP source port to buf (16 bits) + memcpy(ptr, &udp_hdr.source, sizeof(udp_hdr.source)); + ptr += sizeof(udp_hdr.source); + chksumlen += sizeof(udp_hdr.source); + + // Copy UDP destination port to buf (16 bits) + memcpy(ptr, &udp_hdr.dest, sizeof(udp_hdr.dest)); + ptr += sizeof(udp_hdr.dest); + chksumlen += sizeof(udp_hdr.dest); + + // Copy UDP length again to buf (16 bits) + memcpy(ptr, &udp_hdr.len, sizeof(udp_hdr.len)); + ptr += sizeof(udp_hdr.len); + chksumlen += sizeof(udp_hdr.len); + + // Copy UDP checksum to buf (16 bits) + // Zero, since we don't know it yet + *ptr = 0; + ptr++; + *ptr = 0; + ptr++; + chksumlen += 2; + + // Copy payload to buf + memcpy(ptr, payload, payloadlen); + ptr += payloadlen; + chksumlen += payloadlen; + + // Pad to the next 16-bit boundary + for (i = 0; i < payloadlen % 2; i++, ptr++) { + *ptr = 0; + ptr++; + chksumlen++; + } + + return checksum((uint16_t *)buf, chksumlen); +} +#endif + +int bp_sbfd_socket(const struct vrf *vrf) +{ + int s; + + frr_with_privs (&bglobal.bfdd_privs) { + s = vrf_socket(AF_INET6, SOCK_RAW, IPPROTO_RAW, vrf->vrf_id, vrf->name); + } + if (s == -1) { + if (errno != EAFNOSUPPORT) + zlog_fatal("sbfdv6-socket: socket: %s", strerror(errno)); + else + zlog_warn("sbfdv6-socket: V6 is not supported, continuing"); + + return -1; + } + + bp_set_prio(s, SOCK_OPT_PRIO_HIGH); + + return s; +} + +#ifdef BFD_LINUX +static void bp_sbfd_encap_srh_ip6h_red(struct ip6_hdr *srh_ip6h, struct in6_addr *sip, + struct in6_addr *dip, uint8_t seg_num, size_t datalen, + uint16_t family) +{ + /* SRH IPv6 Header */ + srh_ip6h->ip6_flow = (BFD_TOS_VAL << 20); + srh_ip6h->ip6_vfc = 6 << 4; + + if (seg_num == 1) { + if (family == AF_INET6) { + srh_ip6h->ip6_plen = + htons(sizeof(struct ip6_hdr) + sizeof(struct udphdr) + datalen); + srh_ip6h->ip6_nxt = IPPROTO_IPV6; + } else { + srh_ip6h->ip6_plen = + htons(sizeof(struct ip) + sizeof(struct udphdr) + datalen); + srh_ip6h->ip6_nxt = IPPROTO_IPIP; + } + + } else { + srh_ip6h->ip6_plen = htons(sizeof(struct ip6_hdr) + sizeof(struct udphdr) + + sizeof(struct ipv6_sr_hdr) + + sizeof(struct in6_addr) * (seg_num - 1) + datalen); + srh_ip6h->ip6_nxt = IPPROTO_ROUTING; + } + srh_ip6h->ip6_hlim = BFD_TTL_VAL; + + memcpy(&(srh_ip6h->ip6_src), sip, sizeof(struct in6_addr)); + memcpy(&(srh_ip6h->ip6_dst), dip, sizeof(struct in6_addr)); +} + +static void bp_sbfd_encap_srh_rth_red(struct ipv6_sr_hdr *srv6h, struct in6_addr *segment_list, + uint8_t seg_num) +{ + //caller should make sure: seg_num > 1 + srv6h->nexthdr = IPPROTO_IPV6; + srv6h->hdrlen = + GET_RTH_HDR_LEN(RTH_BASE_HEADER_LEN + sizeof(struct in6_addr) * (seg_num - 1)); + srv6h->type = IPV6_SRCRT_TYPE_4; + srv6h->segments_left = seg_num - 1; //if encap reduce mode , seg_num-1 + srv6h->first_segment = seg_num - 2; //if encap reduce mode , seg_num-2 + srv6h->flags = 0; + srv6h->tag = 0; + + for (int i = 0; i < seg_num - 1; i++) + memcpy(&srv6h->segments[i], &segment_list[seg_num - 1 - i], sizeof(struct in6_addr)); +} + +static void bp_sbfd_encap_inner_ip6h(struct ip6_hdr *ip6h, struct in6_addr *sip, + struct in6_addr *dip, size_t datalen) +{ + /* IPv6 Header */ + ip6h->ip6_flow = (BFD_TOS_VAL << 20); + ip6h->ip6_vfc = 6 << 4; + ip6h->ip6_plen = htons(sizeof(struct udphdr) + datalen); + ip6h->ip6_nxt = IPPROTO_UDP; + ip6h->ip6_hlim = BFD_TTL_VAL; + + memcpy(&(ip6h->ip6_src), sip, sizeof(struct in6_addr)); + memcpy(&(ip6h->ip6_dst), dip, sizeof(struct in6_addr)); +} + +static void bp_sbfd_encap_inner_iph(struct ip *iph, struct in6_addr *sip, struct in6_addr *dip, + size_t datalen) +{ + /* IPv4 Header */ + iph->ip_v = 4; + iph->ip_hl = 5; + iph->ip_tos = BFD_TOS_VAL; + iph->ip_len = htons(sizeof(struct ip) + sizeof(struct udphdr) + datalen); + iph->ip_id = (uint16_t)frr_weak_random(); + iph->ip_ttl = BFD_TTL_VAL; + iph->ip_p = IPPROTO_UDP; + iph->ip_sum = 0; + memcpy(&iph->ip_src, sip, sizeof(iph->ip_src)); + memcpy(&iph->ip_dst, dip, sizeof(iph->ip_dst)); +} + +static void bp_sbfd_encap_udp6(struct udphdr *udph, struct ip6_hdr *ip6h, uint16_t src_port, + uint16_t dst_port, uint8_t *payload, int payloadlen) +{ + udph->source = htons(src_port); + udph->dest = htons(dst_port); + udph->len = htons(sizeof(struct udphdr) + payloadlen); + udph->check = udp6_checksum(*ip6h, *udph, payload, payloadlen); +} + +static void bp_sbfd_encap_udp4(struct udphdr *udph, struct ip *iph, uint16_t src_port, + uint16_t dst_port, uint8_t *payload, int payloadlen) +{ + udph->source = htons(src_port); + udph->dest = htons(dst_port); + udph->len = htons(sizeof(struct udphdr) + payloadlen); + udph->check = udp4_checksum(*iph, *udph, payload, payloadlen); +} + +/** + * @brief encap srv6 to send raw socker red mode, just support ecore 2.5 case + * + * @param sd sokcet + * @param data actual data, e.g. bfd packet or bfd echo packet + * @param datalen actual data length + * @param sip source ip address of outer ipv6 header and inner ipv6 header + * @param dip destination ip address of inner ipv6 header + * @param src_port source port of udp + * @param dst_port destination port of udp + * @param seg_num segment number of srh header + * @param segment_list segment list of srh header and the last one segment is destination ip address of outer ipv6 header + * @param ifname out interface name + * @param vrfname vrf name + * @param nhp specified nexthop + * @return int + */ +static int bp_raw_sbfd_red_send(int sd, uint8_t *data, size_t datalen, uint16_t family, + struct in6_addr *out_sip, struct in6_addr *sip, + struct in6_addr *dip, uint16_t src_port, uint16_t dst_port, + uint8_t seg_num, struct in6_addr *segment_list) +{ + static uint8_t sendbuf[BUF_SIZ]; + struct msghdr msg = { 0 }; + struct iovec iov; + int flags = 0; + int ret = 0; + + struct ip6_hdr *srh_ip6h; + struct ipv6_sr_hdr *psrv6h; // srh Routing header + struct ip6_hdr *ip6h; + struct ip *iph; + struct udphdr *udp; + uint8_t *payload; + + struct ipaddr out_sip_addr = { 0 }; + struct sockaddr_in6 dst_sin6 = { 0 }; + char buf_addr[INET6_ADDRSTRLEN] = { 0 }; + + memset(sendbuf, 0, sizeof(sendbuf)); + int total_len = 0; + + /* SRH IPv6 Header */ + if (seg_num > 0) { + memcpy(&out_sip_addr.ipaddr_v6, out_sip, sizeof(struct in6_addr)); + + srh_ip6h = (struct ip6_hdr *)(sendbuf + total_len); + bp_sbfd_encap_srh_ip6h_red(srh_ip6h, &out_sip_addr.ipaddr_v6, &segment_list[0], + seg_num, datalen, family); + total_len += sizeof(struct ip6_hdr); + + memcpy(&dst_sin6.sin6_addr, &segment_list[0], sizeof(struct in6_addr)); + } + + //case with srh header + if (seg_num > 1) { + psrv6h = (struct ipv6_sr_hdr *)(sendbuf + total_len); + bp_sbfd_encap_srh_rth_red(psrv6h, segment_list, seg_num); + total_len += sizeof(struct ipv6_sr_hdr) + sizeof(struct in6_addr) * (seg_num - 1); + } + + if (family == AF_INET6) { + if (seg_num == 0) + memcpy(&dst_sin6.sin6_addr, dip, sizeof(struct in6_addr)); + + /* Inner IPv6 Header */ + ip6h = (struct ip6_hdr *)(sendbuf + total_len); + bp_sbfd_encap_inner_ip6h(ip6h, sip, dip, datalen); + total_len += sizeof(struct ip6_hdr); + + /* UDP Header */ + udp = (struct udphdr *)(sendbuf + total_len); + bp_sbfd_encap_udp6(udp, ip6h, src_port, dst_port, data, datalen); + total_len += sizeof(struct udphdr); + } else { + if (seg_num == 0) { + //should never come to here, just print a error hint + zlog_err("%s error, empty sidlist for ipv4 bfd", __func__); + } + + /* Inner IPv4 Header */ + iph = (struct ip *)(sendbuf + total_len); + bp_sbfd_encap_inner_iph(iph, sip, dip, datalen); + total_len += sizeof(struct ip); + + /* UDP Header */ + udp = (struct udphdr *)(sendbuf + total_len); + bp_sbfd_encap_udp4(udp, iph, src_port, dst_port, data, datalen); + total_len += sizeof(struct udphdr); + + iph->ip_sum = in_cksum((const void *)iph, sizeof(struct ip)); + } + + /* BFD payload*/ + payload = (uint8_t *)(sendbuf + total_len); + memcpy(payload, data, datalen); + total_len += datalen; + + dst_sin6.sin6_family = AF_INET6; + dst_sin6.sin6_port = 0; //we don't use sin6_port in raw, but should set to 0!! + + /* message data. */ + iov.iov_base = (uint8_t *)sendbuf; + iov.iov_len = total_len; + + msg.msg_name = &dst_sin6; + msg.msg_namelen = sizeof(struct sockaddr_in6); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + /* sendmsg */ + ret = sendmsg(sd, &msg, flags); + if (ret < 0) { + inet_ntop(AF_INET6, &dst_sin6.sin6_addr, buf_addr, INET6_ADDRSTRLEN); + zlog_debug("sbfd send to:%s failed , ret:%d, errno:%s", buf_addr, ret, + safe_strerror(errno)); + + return ret; + } + + return 0; +} + +#endif diff --git a/bfdd/bfdd_cli.c b/bfdd/bfdd_cli.c index 6527ec5f41a3..a1710ec12756 100644 --- a/bfdd/bfdd_cli.c +++ b/bfdd/bfdd_cli.c @@ -11,8 +11,12 @@ #include "lib/command.h" #include "lib/log.h" #include "lib/northbound_cli.h" +#include "lib/termtable.h" +#include "lib/ipaddr.h" +#ifndef VTYSH_EXTRACT_PL #include "bfdd/bfdd_cli_clippy.c" +#endif /* VTYSH_EXTRACT_PL */ #include "bfd.h" #include "bfdd_nb.h" @@ -31,6 +35,10 @@ #define LOCAL_INTF_STR "Configure local interface name to use\n" #define VRF_STR "Configure VRF\n" #define VRF_NAME_STR "Configure VRF name\n" +#define SESSION_NAME_STR "Specify bfd session name\n" +#define SET_SESSION_NAME_STR "bfd session name\n" +#define SESSION_MODE_STR "Specify bfd session mode\n" +#define APPLY_SESSION_MODE_STR "Enable bfd mode\n" /* * Prototypes. @@ -41,6 +49,12 @@ bfd_cli_is_single_hop(struct vty *vty) return strstr(VTY_CURR_XPATH, "/single-hop") != NULL; } +static bool bfd_cli_is_sbfd_echo(struct vty *vty) +{ + return strstr(VTY_CURR_XPATH, "/sbfd-echo") != NULL; +} + + static bool bfd_cli_is_profile(struct vty *vty) { @@ -215,45 +229,486 @@ DEFPY_YANG( return nb_cli_apply_changes(vty, NULL); } +DEFPY_YANG_NOSH( + sbfd_echo_peer_enter, sbfd_echo_peer_enter_cmd, + "peer bfd-mode sbfd-echo bfd-name BFDNAME$bfdname \ + [multihop$multihop] local-address [vrf NAME] \ + srv6-source-ipv6 X:X::X:X srv6-encap-data X:X::X:X...", + PEER_STR + PEER_IPV4_STR + PEER_IPV6_STR + SESSION_MODE_STR + "Enable sbfd-echo mode\n" + SESSION_NAME_STR + SET_SESSION_NAME_STR + MHOP_STR + LOCAL_STR + LOCAL_IPV4_STR + LOCAL_IPV6_STR + VRF_STR + VRF_NAME_STR + "Configure source ipv6 address for srv6 encap\n" + LOCAL_IPV6_STR + "Configure sidlist data for srv6 encap\n" + "X:X::X:X IPv6 sid address\n") +{ + int ret, slen, data_idx = 11; + char xpath[XPATH_MAXLEN], xpath_sl[XPATH_MAXLEN + 32], xpath_mh[XPATH_MAXLEN + 32]; + + if (!bfdname) { + vty_out(vty, "%% ERROR: bfd name is required\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (strcmp(peer_str, local_address_str)) { + vty_out(vty, + "%% ERROR: peer and local-address must be the same in sbfd-echo mode\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + slen = snprintf(xpath, sizeof(xpath), + "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo[source-addr='%s'][bfd-name='%s']", + local_address_str, bfdname); + + if (vrf) { + snprintf(xpath + slen, sizeof(xpath) - slen, "[vrf='%s']", vrf); + data_idx += 2; + } else + snprintf(xpath + slen, sizeof(xpath) - slen, "[vrf='%s']", VRF_DEFAULT_NAME); + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + if (multihop) { + snprintf(xpath_mh, sizeof(xpath_mh), "%s/multi-hop", xpath); + nb_cli_enqueue_change(vty, xpath_mh, NB_OP_MODIFY, "true"); + data_idx += 1; + } + + for (int i = data_idx; i < argc; i++) { + snprintf(xpath_sl, sizeof(xpath_sl), "%s/srv6-encap-data", xpath); + nb_cli_enqueue_change(vty, xpath_sl, NB_OP_CREATE, argv[i]->arg); + } + + snprintf(xpath_sl, sizeof(xpath_sl), "%s/srv6-source-ipv6", xpath); + nb_cli_enqueue_change(vty, xpath_sl, NB_OP_MODIFY, srv6_source_ipv6_str); + + snprintf(xpath_sl, sizeof(xpath_sl), "%s/dest-addr", xpath); + nb_cli_enqueue_change(vty, xpath_sl, NB_OP_MODIFY, peer_str); + + /* Apply settings immediately. */ + ret = nb_cli_apply_changes(vty, NULL); + if (ret == CMD_SUCCESS) + VTY_PUSH_XPATH(BFD_PEER_NODE, xpath); + + return ret; +} + +DEFPY_YANG( + sbfd_echo_no_peer, sbfd_echo_no_peer_cmd, + "no peer bfd-mode sbfd-echo bfd-name BFDNAME$bfdname \ + [multihop$multihop] local-address [vrf NAME] \ + srv6-source-ipv6 X:X::X:X srv6-encap-data X:X::X:X...", + NO_STR + PEER_STR + PEER_IPV4_STR + PEER_IPV6_STR + SESSION_MODE_STR + "Enable sbfd-echo mode\n" + SESSION_NAME_STR + SET_SESSION_NAME_STR + MHOP_STR + LOCAL_STR + LOCAL_IPV4_STR + LOCAL_IPV6_STR + VRF_STR + VRF_NAME_STR + "Configure source ipv6 address for srv6 encap\n" + LOCAL_IPV6_STR + "Configure sidlist data for srv6 encap\n" + "X:X::X:X IPv6 sid address\n") +{ + int slen; + char xpath[XPATH_MAXLEN]; + + slen = snprintf(xpath, sizeof(xpath), + "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo[source-addr='%s'][bfd-name='%s']", + local_address_str, bfdname); + + if (vrf) + snprintf(xpath + slen, sizeof(xpath) - slen, "[vrf='%s']", vrf); + else + snprintf(xpath + slen, sizeof(xpath) - slen, "[vrf='%s']", VRF_DEFAULT_NAME); + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + /* Apply settings immediatly. */ + return nb_cli_apply_changes(vty, NULL); +} + + +DEFPY_YANG_NOSH( + sbfd_init_peer_enter, sbfd_init_peer_enter_cmd, + "peer bfd-mode sbfd-init bfd-name BFDNAME$bfdname \ + [multihop$multihop] local-address [vrf NAME] \ + remote-discr (1-4294967295)$discr srv6-source-ipv6 X:X::X:X srv6-encap-data X:X::X:X...", + PEER_STR + PEER_IPV4_STR + PEER_IPV6_STR + SESSION_MODE_STR + "Enable sbfd-init mode\n" + SESSION_NAME_STR + SET_SESSION_NAME_STR + MHOP_STR + LOCAL_STR + LOCAL_IPV4_STR + LOCAL_IPV6_STR + VRF_STR + VRF_NAME_STR + "Configure bfd session remote discriminator\n" + "Configure remote discriminator\n" + "Configure source ipv6 address for srv6 encap\n" + LOCAL_IPV6_STR + "Configure sidlist data for srv6 encap\n" + "X:X::X:X IPv6 sid address\n") +{ + int ret, slen, data_idx = 13; + char xpath[XPATH_MAXLEN], xpath_sl[XPATH_MAXLEN + 32], xpath_rd[XPATH_MAXLEN + 32], + xpath_mh[XPATH_MAXLEN + 32]; + struct ipaddr peer_addr = { 0 }; + struct ipaddr local_addr = { 0 }; + + if (!bfdname) { + vty_out(vty, "%% ERROR: bfd name is required\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + str2ipaddr(peer_str, &peer_addr); + if (peer_addr.ipa_type == AF_UNSPEC) { + vty_out(vty, "%% ERROR: peer is invalid address\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + str2ipaddr(local_address_str, &local_addr); + if (local_addr.ipa_type == AF_UNSPEC) { + vty_out(vty, "%% ERROR: local_address is invalid address\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (peer_addr.ipa_type != local_addr.ipa_type) { + vty_out(vty, "%% ERROR: peer and local_address are not the same ip version\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + slen = snprintf(xpath, sizeof(xpath), + "/frr-bfdd:bfdd/bfd/sessions/sbfd-init[source-addr='%s'][dest-addr='%s'][bfd-name='%s']", + local_address_str, peer_str, bfdname); + + if (vrf) { + snprintf(xpath + slen, sizeof(xpath) - slen, "[vrf='%s']", vrf); + data_idx += 2; + } else + snprintf(xpath + slen, sizeof(xpath) - slen, "[vrf='%s']", VRF_DEFAULT_NAME); + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + if (multihop) { + snprintf(xpath_mh, sizeof(xpath_mh), "%s/multi-hop", xpath); + nb_cli_enqueue_change(vty, xpath_mh, NB_OP_MODIFY, "true"); + data_idx += 1; + } + + if (srv6_source_ipv6_str) { + for (int i = data_idx; i < argc; i++) { + snprintf(xpath_sl, sizeof(xpath_sl), "%s/srv6-encap-data", xpath); + nb_cli_enqueue_change(vty, xpath_sl, NB_OP_CREATE, argv[i]->arg); + } + + snprintf(xpath_sl, sizeof(xpath_sl), "%s/srv6-source-ipv6", xpath); + nb_cli_enqueue_change(vty, xpath_sl, NB_OP_MODIFY, srv6_source_ipv6_str); + } + + snprintf(xpath_rd, sizeof(xpath_rd), "%s/remote-discr", xpath); + nb_cli_enqueue_change(vty, xpath_rd, NB_OP_MODIFY, discr_str); + + /* Apply settings immediately. */ + ret = nb_cli_apply_changes(vty, NULL); + if (ret == CMD_SUCCESS) + VTY_PUSH_XPATH(BFD_PEER_NODE, xpath); + + return ret; +} + +DEFPY_YANG( + sbfd_init_no_peer, sbfd_init_no_peer_cmd, + "no peer bfd-mode sbfd-init bfd-name BFDNAME$bfdname \ + [multihop$multihop] local-address [vrf NAME] \ + remote-discr (0-4294967295)$discr srv6-source-ipv6 X:X::X:X srv6-encap-data X:X::X:X...", + NO_STR + PEER_STR + PEER_IPV4_STR + PEER_IPV6_STR + SESSION_MODE_STR + "Enable sbfd-init mode\n" + SESSION_NAME_STR + SET_SESSION_NAME_STR + MHOP_STR + LOCAL_STR + LOCAL_IPV4_STR + LOCAL_IPV6_STR + VRF_STR + VRF_NAME_STR + "Configure bfd session remote discriminator\n" + "Configure remote discriminator\n" + "Configure source ipv6 address for srv6 encap\n" + LOCAL_IPV6_STR + "Configure sidlist data for srv6 encap\n" + "X:X::X:X IPv6 sid address\n") +{ + int slen; + char xpath[XPATH_MAXLEN]; + + slen = snprintf(xpath, sizeof(xpath), + "/frr-bfdd:bfdd/bfd/sessions/sbfd-init[source-addr='%s'][dest-addr='%s'][bfd-name='%s']", + local_address_str, peer_str, bfdname); + + if (vrf) + snprintf(xpath + slen, sizeof(xpath) - slen, "[vrf='%s']", vrf); + else + snprintf(xpath + slen, sizeof(xpath) - slen, "[vrf='%s']", VRF_DEFAULT_NAME); + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + /* Apply settings immediatly. */ + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG_NOSH( + sbfd_init_peer_raw_enter, sbfd_init_peer_raw_enter_cmd, + "peer bfd-mode sbfd-init bfd-name BFDNAME$bfdname \ + [multihop$multihop] local-address [vrf NAME] \ + remote-discr (1-4294967295)$discr", + PEER_STR + PEER_IPV4_STR + PEER_IPV6_STR + SESSION_MODE_STR + "Enable sbfd-init mode\n" + SESSION_NAME_STR + SET_SESSION_NAME_STR + MHOP_STR + LOCAL_STR + LOCAL_IPV4_STR + LOCAL_IPV6_STR + VRF_STR + VRF_NAME_STR + "Configure bfd session remote discriminator\n" + "Configure remote discriminator\n") +{ + int ret, slen; + char xpath[XPATH_MAXLEN], xpath_rd[XPATH_MAXLEN + 32], xpath_mh[XPATH_MAXLEN + 32]; + struct ipaddr peer_addr = { 0 }; + struct ipaddr local_addr = { 0 }; + + if (!bfdname) { + vty_out(vty, "%% ERROR: bfd name is required\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + str2ipaddr(peer_str, &peer_addr); + if (peer_addr.ipa_type == AF_UNSPEC) { + vty_out(vty, "%% ERROR: peer is invalid address\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + str2ipaddr(local_address_str, &local_addr); + if (local_addr.ipa_type == AF_UNSPEC) { + vty_out(vty, "%% ERROR: local_address is invalid address\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (peer_addr.ipa_type != local_addr.ipa_type) { + vty_out(vty, "%% ERROR: peer and local_address are not the same ip version\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + slen = snprintf(xpath, sizeof(xpath), + "/frr-bfdd:bfdd/bfd/sessions/sbfd-init[source-addr='%s'][dest-addr='%s'][bfd-name='%s']", + local_address_str, peer_str, bfdname); + + if (vrf) + snprintf(xpath + slen, sizeof(xpath) - slen, "[vrf='%s']", vrf); + else + snprintf(xpath + slen, sizeof(xpath) - slen, "[vrf='%s']", VRF_DEFAULT_NAME); + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + if (multihop) { + snprintf(xpath_mh, sizeof(xpath_mh), "%s/multi-hop", xpath); + nb_cli_enqueue_change(vty, xpath_mh, NB_OP_MODIFY, "true"); + } + + snprintf(xpath_rd, sizeof(xpath_rd), "%s/remote-discr", xpath); + nb_cli_enqueue_change(vty, xpath_rd, NB_OP_MODIFY, discr_str); + + /* Apply settings immediately. */ + ret = nb_cli_apply_changes(vty, NULL); + if (ret == CMD_SUCCESS) + VTY_PUSH_XPATH(BFD_PEER_NODE, xpath); + + return ret; +} + +DEFPY_YANG( + sbfd_init_no_peer_raw, sbfd_init_no_peer_raw_cmd, + "no peer bfd-mode sbfd-init bfd-name BFDNAME$bfdname \ + [multihop$multihop] local-address [vrf NAME] \ + remote-discr (0-4294967295)$discr", + NO_STR + PEER_STR + PEER_IPV4_STR + PEER_IPV6_STR + SESSION_MODE_STR + "Enable sbfd-init mode\n" + SESSION_NAME_STR + SET_SESSION_NAME_STR + MHOP_STR + LOCAL_STR + LOCAL_IPV4_STR + LOCAL_IPV6_STR + VRF_STR + VRF_NAME_STR + "Configure bfd session remote discriminator\n" + "Configure remote discriminator\n") +{ + int slen; + char xpath[XPATH_MAXLEN]; + + slen = snprintf(xpath, sizeof(xpath), + "/frr-bfdd:bfdd/bfd/sessions/sbfd-init[source-addr='%s'][dest-addr='%s'][bfd-name='%s']", + local_address_str, peer_str, bfdname); + + if (vrf) + snprintf(xpath + slen, sizeof(xpath) - slen, "[vrf='%s']", vrf); + else + snprintf(xpath + slen, sizeof(xpath) - slen, "[vrf='%s']", VRF_DEFAULT_NAME); + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + /* Apply settings immediatly. */ + return nb_cli_apply_changes(vty, NULL); +} + +static const char *_bfd_cli_bfd_mode_type_to_string(enum bfd_mode_type mode) +{ + switch (mode) { + case BFD_MODE_TYPE_BFD: + return "bfd"; + case BFD_MODE_TYPE_SBFD_ECHO: + return "sbfd-echo"; + case BFD_MODE_TYPE_SBFD_INIT: + return "sbfd-init"; + default: + return "Unknown"; + } +} + +struct sidlist_show_iter { + char buf[INET6_ADDRSTRLEN * SRV6_MAX_SEGS]; +}; + +static int sidlist_show_iter_cb(const struct lyd_node *dnode, void *arg) +{ + struct sidlist_show_iter *iter = arg; + const char *addr; + + addr = yang_dnode_get_string(dnode, NULL); + + strlcat(iter->buf, addr, INET6_ADDRSTRLEN * SRV6_MAX_SEGS); + strlcat(iter->buf, " ", INET6_ADDRSTRLEN * SRV6_MAX_SEGS); + + return YANG_ITER_CONTINUE; +} + static void _bfd_cli_show_peer(struct vty *vty, const struct lyd_node *dnode, - bool show_defaults __attribute__((__unused__)), - bool mhop) + bool show_defaults __attribute__((__unused__)), bool mhop, + uint32_t bfd_mode) { const char *vrf = yang_dnode_get_string(dnode, "vrf"); + struct sidlist_show_iter iter = { 0 }; - vty_out(vty, " peer %s", - yang_dnode_get_string(dnode, "dest-addr")); + vty_out(vty, " peer %s", yang_dnode_get_string(dnode, "./dest-addr")); + if (bfd_mode == BFD_MODE_TYPE_BFD) { + if (mhop) + vty_out(vty, " multihop"); - if (mhop) - vty_out(vty, " multihop"); + if (yang_dnode_exists(dnode, "./source-addr")) + vty_out(vty, " local-address %s", + yang_dnode_get_string(dnode, "./source-addr")); - if (yang_dnode_exists(dnode, "source-addr")) - vty_out(vty, " local-address %s", - yang_dnode_get_string(dnode, "source-addr")); + if (strcmp(vrf, VRF_DEFAULT_NAME)) + vty_out(vty, " vrf %s", vrf); - if (strcmp(vrf, VRF_DEFAULT_NAME)) - vty_out(vty, " vrf %s", vrf); + if (!mhop) { + const char *ifname = yang_dnode_get_string(dnode, "./interface"); - if (!mhop) { - const char *ifname = - yang_dnode_get_string(dnode, "interface"); - if (strcmp(ifname, "*")) - vty_out(vty, " interface %s", ifname); - } + if (strcmp(ifname, "*")) + vty_out(vty, " interface %s", ifname); + } + vty_out(vty, "\n"); + } else if (bfd_mode == BFD_MODE_TYPE_SBFD_ECHO || bfd_mode == BFD_MODE_TYPE_SBFD_INIT) { + vty_out(vty, " bfd-mode %s", _bfd_cli_bfd_mode_type_to_string(bfd_mode)); + + if (yang_dnode_exists(dnode, "bfd-name")) + vty_out(vty, " bfd-name %s", yang_dnode_get_string(dnode, "bfd-name")); - vty_out(vty, "\n"); + if (mhop) + vty_out(vty, " multihop"); + + if (yang_dnode_exists(dnode, "source-addr")) + vty_out(vty, " local-address %s", + yang_dnode_get_string(dnode, "source-addr")); + + if (strcmp(vrf, VRF_DEFAULT_NAME)) + vty_out(vty, " vrf %s", vrf); + + if (bfd_mode == BFD_MODE_TYPE_SBFD_INIT) { + if (yang_dnode_exists(dnode, "remote-discr")) + vty_out(vty, " remote-discr %u", + yang_dnode_get_uint32(dnode, "remote-discr")); + } + + if (yang_dnode_exists(dnode, "srv6-source-ipv6")) + vty_out(vty, " srv6-source-ipv6 %s", + yang_dnode_get_string(dnode, "srv6-source-ipv6")); + + if (yang_dnode_exists(dnode, "srv6-encap-data")) { + yang_dnode_iterate(sidlist_show_iter_cb, &iter, dnode, "./srv6-encap-data"); + vty_out(vty, " srv6-encap-data %s", iter.buf); + } + + vty_out(vty, "\n"); + } } void bfd_cli_show_single_hop_peer(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { - _bfd_cli_show_peer(vty, dnode, show_defaults, false); + _bfd_cli_show_peer(vty, dnode, show_defaults, false, BFD_MODE_TYPE_BFD); } void bfd_cli_show_multi_hop_peer(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { - _bfd_cli_show_peer(vty, dnode, show_defaults, true); + _bfd_cli_show_peer(vty, dnode, show_defaults, true, BFD_MODE_TYPE_BFD); +} + +void bfd_cli_show_sbfd_echo_peer(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + _bfd_cli_show_peer(vty, dnode, show_defaults, false, BFD_MODE_TYPE_SBFD_ECHO); +} + +void bfd_cli_show_sbfd_init_peer(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + _bfd_cli_show_peer(vty, dnode, show_defaults, true, BFD_MODE_TYPE_SBFD_INIT); } void bfd_cli_show_peer_end(struct vty *vty, const struct lyd_node *dnode @@ -446,8 +901,9 @@ DEFPY_YANG( { char value[32]; - if (!bfd_cli_is_profile(vty) && !bfd_cli_is_single_hop(vty)) { - vty_out(vty, "%% Echo mode is only available for single hop sessions.\n"); + if (!bfd_cli_is_profile(vty) && !bfd_cli_is_single_hop(vty) && !bfd_cli_is_sbfd_echo(vty)) { + vty_out(vty, + "%% Echo mode is only available for single hop or sbfd echo sessions.\n"); return CMD_WARNING_CONFIG_FAILED; } @@ -470,8 +926,9 @@ DEFPY_YANG( { char value[32]; - if (!bfd_cli_is_profile(vty) && !bfd_cli_is_single_hop(vty)) { - vty_out(vty, "%% Echo mode is only available for single hop sessions.\n"); + if (!bfd_cli_is_profile(vty) && !bfd_cli_is_single_hop(vty) && !bfd_cli_is_sbfd_echo(vty)) { + vty_out(vty, + "%% Echo mode is only available for single hop or sbfd echo sessions.\n"); return CMD_WARNING_CONFIG_FAILED; } @@ -501,11 +958,13 @@ DEFPY_YANG( { char value[32]; - if (!bfd_cli_is_profile(vty) && !bfd_cli_is_single_hop(vty)) { - vty_out(vty, "%% Echo mode is only available for single hop sessions.\n"); + if (!bfd_cli_is_profile(vty) && !bfd_cli_is_single_hop(vty) && !bfd_cli_is_sbfd_echo(vty)) { + vty_out(vty, + "%% Echo mode is only available for single hop or sbfd echo sessions.\n"); return CMD_WARNING_CONFIG_FAILED; } + if (disabled) snprintf(value, sizeof(value), "0"); else @@ -657,6 +1116,160 @@ DEFPY_YANG(bfd_peer_profile, bfd_peer_profile_cmd, return nb_cli_apply_changes(vty, NULL); } +DEFPY( + sbfd_reflector, sbfd_reflector_cmd, + "sbfd reflector source-address X:X::X:X$srcip discriminator WORD...", + "seamless BFD\n" + "sbfd reflector\n" + "binding source ip address\n" + IPV6_STR + "discriminator\n" + "discriminator value or range (e.g. 100 or 100 200 300 or 100-300)\n") +{ + int idx_discr = 5; + int i; + uint32_t j; + uint32_t discr = 0; + uint32_t discr_from = 0; + uint32_t discr_to = 0; + + for (i = idx_discr; i < argc; i++) { + /* check validity*/ + char *pstr = argv[i]->arg; + + /*single discr*/ + if (strspn(pstr, "0123456789") == strlen(pstr)) { + discr = atol(pstr); + sbfd_reflector_new(discr, &srcip); + } + /*discr segment*/ + else if (strspn(pstr, "0123456789-") == strlen(pstr)) { + char *token = strtok(argv[i]->arg, "-"); + + if (token) + discr_from = atol(token); + + token = strtok(NULL, "-"); + if (token) + discr_to = atol(token); + + if (discr_from >= discr_to) { + vty_out(vty, "input discriminator range %u-%u is illegal\n", + discr_from, discr_to); + } + + for (j = discr_from; j <= discr_to; j++) + sbfd_reflector_new(j, &srcip); + } + /*illegal input*/ + else + vty_out(vty, "input discriminator %s is illegal\n", (char *)argv[i]); + } + + return CMD_SUCCESS; +} + +DEFPY( + no_sbfd_reflector_all, no_sbfd_reflector_all_cmd, + "no sbfd reflector [all]", + NO_STR + "seamless BFD\n" + "sbfd reflector\n" + "all\n") +{ + sbfd_reflector_flush(); + + if (sbfd_discr_get_count()) { + vty_out(vty, "delete all reflector discriminator failed.\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + return CMD_SUCCESS; +} + +DEFPY( + no_sbfd_reflector, no_sbfd_reflector_cmd, + "no sbfd reflector (0-4294967295)$start_discr [(0-4294967295)$end_discr]", + NO_STR + "seamless BFD\n" + "sbfd reflector\n" + "start discriminator\n" + "end discriminator\n") +{ + struct sbfd_reflector *sr; + int32_t i; + + if (end_discr == 0) { + if (start_discr == 0) { + vty_out(vty, "input reflector discriminator is illegal.\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + sr = sbfd_discr_lookup(start_discr); + if (!sr) { + vty_out(vty, "input reflector discriminator does not exist.\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + // notify bfdsyncd + //bfd_fpm_sbfd_reflector_sendmsg(sr, false); + sbfd_reflector_free(start_discr); + + } else { + if (end_discr <= start_discr) { + vty_out(vty, "input reflector discriminator is illegal.\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + for (i = start_discr; i <= end_discr; i++) { + sr = sbfd_discr_lookup(i); + if (sr) { + // notify bfdsyncd + //bfd_fpm_sbfd_reflector_sendmsg(sr, false); + sbfd_reflector_free(i); + } + } + } + + return CMD_SUCCESS; +} + +static void _sbfd_reflector_show(struct hash_bucket *hb, void *arg) +{ + struct sbfd_reflector *sr = hb->data; + struct ttable *tt; + char buf[INET6_ADDRSTRLEN]; + + tt = (struct ttable *)arg; + + ttable_add_row(tt, "%u|%s|%s|%s", sr->discr, + inet_ntop(AF_INET6, &sr->local, buf, sizeof(buf)), "Active", "Software"); +} + +DEFPY( + sbfd_reflector_show_info, sbfd_reflector_show_info_cmd, + "show sbfd reflector", + "show\n" + "seamless BFD\n" + "sbfd reflector\n") +{ + struct ttable *tt; + char *out; + + vty_out(vty, "sbfd reflector discriminator :\n"); + tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]); + ttable_add_row(tt, "SBFD-Discr|SourceIP|State|CreateType"); + ttable_rowseps(tt, 0, BOTTOM, true, '-'); + + sbfd_discr_iterate(_sbfd_reflector_show, tt); + + out = ttable_dump(tt, "\n"); + vty_out(vty, "%s", out); + XFREE(MTYPE_TMP, out); + ttable_del(tt); + + return CMD_SUCCESS; +} void bfd_cli_peer_profile_show(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { @@ -694,6 +1307,18 @@ bfdd_cli_init(void) install_element(BFD_NODE, &bfd_peer_enter_cmd); install_element(BFD_NODE, &bfd_no_peer_cmd); + install_element(BFD_NODE, &sbfd_echo_peer_enter_cmd); + install_element(BFD_NODE, &sbfd_echo_no_peer_cmd); + + install_element(BFD_NODE, &sbfd_init_peer_enter_cmd); + install_element(BFD_NODE, &sbfd_init_no_peer_cmd); + install_element(BFD_NODE, &sbfd_init_peer_raw_enter_cmd); + install_element(BFD_NODE, &sbfd_init_no_peer_raw_cmd); + + install_element(BFD_NODE, &sbfd_reflector_cmd); + install_element(BFD_NODE, &no_sbfd_reflector_all_cmd); + install_element(BFD_NODE, &no_sbfd_reflector_cmd); + install_element(VIEW_NODE, &sbfd_reflector_show_info_cmd); install_element(BFD_PEER_NODE, &bfd_peer_shutdown_cmd); install_element(BFD_PEER_NODE, &bfd_peer_mult_cmd); install_element(BFD_PEER_NODE, &bfd_peer_rx_cmd); diff --git a/bfdd/bfdd_nb.c b/bfdd/bfdd_nb.c index 114fbc2bdd87..f60d8397bb66 100644 --- a/bfdd/bfdd_nb.c +++ b/bfdd/bfdd_nb.c @@ -483,6 +483,459 @@ const struct frr_yang_module_info frr_bfdd_info = { .get_elem = bfdd_bfd_sessions_single_hop_stats_echo_packet_output_count_get_elem, } }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo", + .cbs = { + .create = bfdd_bfd_sessions_sbfd_echo_create, + .destroy = bfdd_bfd_sessions_sbfd_echo_destroy, + .get_next = bfdd_bfd_sessions_sbfd_echo_get_next, + .get_keys = bfdd_bfd_sessions_sbfd_echo_get_keys, + .lookup_entry = bfdd_bfd_sessions_sbfd_echo_lookup_entry, + .cli_show = bfd_cli_show_sbfd_echo_peer, /* TODO */ + .cli_show_end = bfd_cli_show_peer_end, /* TODO */ + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/dest-addr", + .cbs = { + .modify = bfdd_bfd_sessions_sbfd_echo_dest_addr_modify, + .destroy = bfdd_bfd_sessions_sbfd_echo_dest_addr_destroy, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/srv6-source-ipv6", + .cbs = { + .modify = bfdd_bfd_sessions_sbfd_srv6_source_ipv6_modify, + .destroy = bfdd_bfd_sessions_sbfd_srv6_source_ipv6_destroy, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/profile", + .cbs = { + .modify = bfdd_bfd_sessions_single_hop_profile_modify, + .destroy = bfdd_bfd_sessions_single_hop_profile_destroy, + .cli_show = bfd_cli_peer_profile_show, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/detection-multiplier", + .cbs = { + .modify = bfdd_bfd_sessions_single_hop_detection_multiplier_modify, + .cli_show = bfd_cli_show_mult, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/desired-transmission-interval", + .cbs = { + .modify = bfdd_bfd_sessions_single_hop_desired_transmission_interval_modify, + .cli_show = bfd_cli_show_tx, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/required-receive-interval", + .cbs = { + .modify = bfdd_bfd_sessions_single_hop_required_receive_interval_modify, + .cli_show = bfd_cli_show_rx, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/echo-mode", + .cbs = { + .modify = bfdd_bfd_sessions_sbfd_echo_mode_modify, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/desired-echo-transmission-interval", + .cbs = { + .modify = bfdd_bfd_sessions_single_hop_desired_echo_transmission_interval_modify, + .cli_show = bfd_cli_show_desired_echo_transmission_interval, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/required-echo-receive-interval", + .cbs = { + .modify = bfdd_bfd_sessions_single_hop_required_echo_receive_interval_modify, + .cli_show = bfd_cli_show_required_echo_receive_interval, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/administrative-down", + .cbs = { + .modify = bfdd_bfd_sessions_single_hop_administrative_down_modify, + .cli_show = bfd_cli_show_shutdown, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/passive-mode", + .cbs = { + .modify = bfdd_bfd_sessions_single_hop_passive_mode_modify, + .cli_show = bfd_cli_show_passive, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/bfd-mode", + .cbs = { + .modify = bfdd_bfd_sessions_bfd_mode_modify, + .destroy = bfdd_bfd_sessions_bfd_mode_destroy, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/srv6-encap-data", + .cbs = { + .create = bfdd_bfd_sessions_segment_list_create, + .destroy = bfdd_bfd_sessions_segment_list_destroy, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/minimum-ttl", + .cbs = { + .modify = bfdd_bfd_sessions_multi_hop_minimum_ttl_modify, + .cli_show = bfd_cli_show_minimum_ttl, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/multi-hop", + .cbs = { + .modify = bfdd_bfd_sessions_sbfd_multi_hop_modify, + .destroy = bfdd_bfd_sessions_sbfd_multi_hop_destroy, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/stats/local-discriminator", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_local_discriminator_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/stats/local-state", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_local_state_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/stats/local-diagnostic", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_local_diagnostic_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/stats/local-multiplier", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_local_multiplier_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/stats/remote-discriminator", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_remote_discriminator_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/stats/remote-state", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_remote_state_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/stats/remote-diagnostic", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_remote_diagnostic_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/stats/remote-multiplier", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_remote_multiplier_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/stats/negotiated-transmission-interval", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_negotiated_transmission_interval_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/stats/negotiated-receive-interval", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_negotiated_receive_interval_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/stats/detection-mode", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_detection_mode_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/stats/last-down-time", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_last_down_time_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/stats/last-up-time", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_last_up_time_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/stats/session-down-count", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_session_down_count_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/stats/session-up-count", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_session_up_count_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/stats/control-packet-input-count", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_control_packet_input_count_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/stats/control-packet-output-count", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_control_packet_output_count_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/stats/negotiated-echo-transmission-interval", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_negotiated_echo_transmission_interval_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/stats/echo-packet-input-count", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_echo_packet_input_count_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/stats/echo-packet-output-count", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_echo_packet_output_count_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init", + .cbs = { + .create = bfdd_bfd_sessions_sbfd_init_create, + .destroy = bfdd_bfd_sessions_sbfd_init_destroy, + .get_next = bfdd_bfd_sessions_sbfd_init_get_next, + .get_keys = bfdd_bfd_sessions_sbfd_init_get_keys, + .lookup_entry = bfdd_bfd_sessions_sbfd_init_lookup_entry, + .cli_show = bfd_cli_show_sbfd_init_peer, /* TODO */ + .cli_show_end = bfd_cli_show_peer_end, /* TODO */ + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/srv6-source-ipv6", + .cbs = { + .modify = bfdd_bfd_sessions_sbfd_srv6_source_ipv6_modify, + .destroy = bfdd_bfd_sessions_sbfd_srv6_source_ipv6_destroy, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/remote-discr", + .cbs = { + .modify = bfdd_bfd_sessions_sbfd_init_remote_discr_modify, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/profile", + .cbs = { + .modify = bfdd_bfd_sessions_single_hop_profile_modify, + .destroy = bfdd_bfd_sessions_single_hop_profile_destroy, + .cli_show = bfd_cli_peer_profile_show, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/detection-multiplier", + .cbs = { + .modify = bfdd_bfd_sessions_single_hop_detection_multiplier_modify, + .cli_show = bfd_cli_show_mult, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/desired-transmission-interval", + .cbs = { + .modify = bfdd_bfd_sessions_single_hop_desired_transmission_interval_modify, + .cli_show = bfd_cli_show_tx, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/required-receive-interval", + .cbs = { + .modify = bfdd_bfd_sessions_single_hop_required_receive_interval_modify, + .cli_show = bfd_cli_show_rx, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/administrative-down", + .cbs = { + .modify = bfdd_bfd_sessions_single_hop_administrative_down_modify, + .cli_show = bfd_cli_show_shutdown, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/passive-mode", + .cbs = { + .modify = bfdd_bfd_sessions_single_hop_passive_mode_modify, + .cli_show = bfd_cli_show_passive, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/bfd-mode", + .cbs = { + .modify = bfdd_bfd_sessions_bfd_mode_modify, + .destroy = bfdd_bfd_sessions_bfd_mode_destroy, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/srv6-encap-data", + .cbs = { + .create = bfdd_bfd_sessions_segment_list_create, + .destroy = bfdd_bfd_sessions_segment_list_destroy, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/minimum-ttl", + .cbs = { + .modify = bfdd_bfd_sessions_multi_hop_minimum_ttl_modify, + .cli_show = bfd_cli_show_minimum_ttl, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/multi-hop", + .cbs = { + .modify = bfdd_bfd_sessions_sbfd_multi_hop_modify, + .destroy = bfdd_bfd_sessions_sbfd_multi_hop_destroy, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/stats/local-discriminator", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_local_discriminator_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/stats/local-state", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_local_state_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/stats/local-diagnostic", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_local_diagnostic_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/stats/local-multiplier", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_local_multiplier_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/stats/remote-discriminator", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_remote_discriminator_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/stats/remote-state", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_remote_state_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/stats/remote-diagnostic", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_remote_diagnostic_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/stats/remote-multiplier", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_remote_multiplier_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/stats/negotiated-transmission-interval", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_negotiated_transmission_interval_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/stats/negotiated-receive-interval", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_negotiated_receive_interval_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/stats/detection-mode", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_detection_mode_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/stats/last-down-time", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_last_down_time_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/stats/last-up-time", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_last_up_time_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/stats/session-down-count", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_session_down_count_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/stats/session-up-count", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_session_up_count_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/stats/control-packet-input-count", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_control_packet_input_count_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/stats/control-packet-output-count", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_control_packet_output_count_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/stats/negotiated-echo-transmission-interval", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_negotiated_echo_transmission_interval_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/stats/echo-packet-input-count", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_echo_packet_input_count_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/stats/echo-packet-output-count", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_echo_packet_output_count_get_elem, + } + }, { .xpath = NULL, }, diff --git a/bfdd/bfdd_nb.h b/bfdd/bfdd_nb.h index b5b00b57e4b0..6621973ae365 100644 --- a/bfdd/bfdd_nb.h +++ b/bfdd/bfdd_nb.h @@ -112,6 +112,26 @@ bfdd_bfd_sessions_single_hop_stats_echo_packet_output_count_get_elem( struct nb_cb_get_elem_args *args); int bfdd_bfd_sessions_multi_hop_create(struct nb_cb_create_args *args); int bfdd_bfd_sessions_multi_hop_destroy(struct nb_cb_destroy_args *args); +int bfdd_bfd_sessions_sbfd_echo_create(struct nb_cb_create_args *args); +int bfdd_bfd_sessions_sbfd_echo_destroy(struct nb_cb_destroy_args *args); +int bfdd_bfd_sessions_sbfd_echo_dest_addr_modify(struct nb_cb_modify_args *args); +int bfdd_bfd_sessions_sbfd_echo_mode_modify(struct nb_cb_modify_args *args); +int bfdd_bfd_sessions_sbfd_echo_dest_addr_destroy(struct nb_cb_destroy_args *args); +int bfdd_bfd_sessions_sbfd_srv6_source_ipv6_modify(struct nb_cb_modify_args *args); +int bfdd_bfd_sessions_sbfd_srv6_source_ipv6_destroy(struct nb_cb_destroy_args *args); +int bfdd_bfd_sessions_sbfd_init_remote_discr_modify(struct nb_cb_modify_args *args); +int bfdd_bfd_sessions_sbfd_multi_hop_modify(struct nb_cb_modify_args *args); +int bfdd_bfd_sessions_sbfd_multi_hop_destroy(struct nb_cb_destroy_args *args); + +int bfdd_bfd_sessions_sbfd_init_create(struct nb_cb_create_args *args); +int bfdd_bfd_sessions_sbfd_init_destroy(struct nb_cb_destroy_args *args); +const void *bfdd_bfd_sessions_sbfd_echo_get_next(struct nb_cb_get_next_args *args); +int bfdd_bfd_sessions_sbfd_echo_get_keys(struct nb_cb_get_keys_args *args); +const void *bfdd_bfd_sessions_sbfd_echo_lookup_entry(struct nb_cb_lookup_entry_args *args); +const void *bfdd_bfd_sessions_sbfd_init_get_next(struct nb_cb_get_next_args *args); +int bfdd_bfd_sessions_sbfd_init_get_keys(struct nb_cb_get_keys_args *args); +const void *bfdd_bfd_sessions_sbfd_init_lookup_entry(struct nb_cb_lookup_entry_args *args); + const void * bfdd_bfd_sessions_multi_hop_get_next(struct nb_cb_get_next_args *args); int bfdd_bfd_sessions_multi_hop_get_keys(struct nb_cb_get_keys_args *args); @@ -185,6 +205,8 @@ void bfd_cli_show_single_hop_peer(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); void bfd_cli_show_multi_hop_peer(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); +void bfd_cli_show_sbfd_echo_peer(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); +void bfd_cli_show_sbfd_init_peer(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); void bfd_cli_show_peer_end(struct vty *vty, const struct lyd_node *dnode); void bfd_cli_show_mult(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); @@ -210,4 +232,10 @@ void bfd_cli_show_passive(struct vty *vty, const struct lyd_node *dnode, void bfd_cli_show_minimum_ttl(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); +int bfdd_bfd_sessions_bfd_mode_modify(struct nb_cb_modify_args *args); +int bfdd_bfd_sessions_bfd_mode_destroy(struct nb_cb_destroy_args *args); + +int bfdd_bfd_sessions_segment_list_create(struct nb_cb_create_args *args); +int bfdd_bfd_sessions_segment_list_destroy(struct nb_cb_destroy_args *args); + #endif /* _FRR_BFDD_NB_H_ */ diff --git a/bfdd/bfdd_nb_config.c b/bfdd/bfdd_nb_config.c index 48fbe7139c27..15da1e2440d5 100644 --- a/bfdd/bfdd_nb_config.c +++ b/bfdd/bfdd_nb_config.c @@ -13,14 +13,50 @@ #include "bfd.h" #include "bfdd_nb.h" +#include /* * Helpers. */ +static void get_ip_by_interface(const char *ifname, const char *vrfname, int family, char *ifip) +{ + char intfip[INET6_ADDRSTRLEN]; + const struct interface *interface; + const struct connected *connected; + struct vrf *vrf; + + vrf = vrf_lookup_by_name(vrfname ? vrfname : VRF_DEFAULT_NAME); + if (!vrf) + return; + + interface = if_lookup_by_name_vrf(ifname, vrf); + if (interface == NULL) + return; + + frr_each (if_connected_const, interface->connected, connected) { + if (!connected->address) + continue; + + if (family != connected->address->family) + continue; + + if (family == AF_INET6 && IN6_IS_ADDR_LINKLOCAL(&connected->address->u.prefix6)) + continue; + + inet_ntop(family, + family == AF_INET ? (void *)(&connected->address->u.prefix4) + : (void *)(&connected->address->u.prefix6), + intfip, INET6_ADDRSTRLEN); + strlcpy(ifip, intfip, INET6_ADDRSTRLEN - 1); + break; + } +} + static void bfd_session_get_key(bool mhop, const struct lyd_node *dnode, struct bfd_key *bk) { const char *ifname = NULL, *vrfname = NULL; + char ifip[INET6_ADDRSTRLEN] = { 0 }; struct sockaddr_any psa, lsa; /* Required destination parameter. */ @@ -37,10 +73,36 @@ static void bfd_session_get_key(bool mhop, const struct lyd_node *dnode, ifname = yang_dnode_get_string(dnode, "interface"); if (strcmp(ifname, "*") == 0) ifname = NULL; + + if (ifname != NULL && !yang_dnode_exists(dnode, "source-addr") && + psa.sa_sin.sin_family != 0) { + get_ip_by_interface(ifname, vrfname, psa.sa_sin.sin_family, ifip); + strtosa(ifip, &lsa); + } } /* Generate the corresponding key. */ - gen_bfd_key(bk, &psa, &lsa, mhop, ifname, vrfname); + gen_bfd_key(bk, &psa, &lsa, mhop, ifname, vrfname, NULL); +} + +static void sbfd_session_get_key(bool mhop, const struct lyd_node *dnode, struct bfd_key *bk) +{ + const char *ifname = NULL, *vrfname = NULL, *bfdname = NULL; + struct sockaddr_any psa, lsa; + + /* Required source parameter. */ + strtosa(yang_dnode_get_string(dnode, "source-addr"), &lsa); + + strtosa(yang_dnode_get_string(dnode, "dest-addr"), &psa); + + if (yang_dnode_exists(dnode, "bfd-name")) + bfdname = yang_dnode_get_string(dnode, "bfd-name"); + + if (yang_dnode_exists(dnode, "vrf")) + vrfname = yang_dnode_get_string(dnode, "vrf"); + + /* Generate the corresponding key. */ + gen_bfd_key(bk, &psa, &lsa, mhop, ifname, vrfname, bfdname); } struct session_iter { @@ -63,7 +125,25 @@ static int session_iter_cb(const struct lyd_node *dnode, void *arg) return YANG_ITER_CONTINUE; } -static int bfd_session_create(struct nb_cb_create_args *args, bool mhop) +static int segment_list_iter_cb(const struct lyd_node *dnode, void *arg) +{ + struct bfd_session *bs = arg; + uint8_t segnum = bs->segnum; + const char *addr; + struct sockaddr_any sa; + + addr = yang_dnode_get_string(dnode, NULL); + + if (strtosa(addr, &sa) < 0 || sa.sa_sin6.sin6_family != AF_INET6) + return YANG_ITER_STOP; + + memcpy(&bs->seg_list[segnum], &sa.sa_sin6.sin6_addr, sizeof(struct in6_addr)); + bs->segnum = segnum + 1; + + return YANG_ITER_CONTINUE; +} + +static int bfd_session_create(struct nb_cb_create_args *args, bool mhop, uint32_t bfd_mode) { const struct lyd_node *sess_dnode; struct session_iter iter; @@ -73,10 +153,20 @@ static int bfd_session_create(struct nb_cb_create_args *args, bool mhop) const char *vrfname; struct bfd_key bk; struct prefix p; + const char *bfd_name = NULL; + struct sockaddr_any out_sip6; switch (args->event) { case NB_EV_VALIDATE: - yang_dnode_get_prefix(&p, args->dnode, "dest-addr"); + if ((bfd_mode == BFD_MODE_TYPE_SBFD_ECHO) || (bfd_mode == BFD_MODE_TYPE_SBFD_INIT)) { + if (bfd_session_get_by_name(yang_dnode_get_string(args->dnode, "bfd-name"))) { + snprintf(args->errmsg, args->errmsg_len, "bfd name already exist."); + return NB_ERR_VALIDATION; + } + return NB_OK; + } + + yang_dnode_get_prefix(&p, args->dnode, "./dest-addr"); if (mhop) { /* @@ -128,34 +218,123 @@ static int bfd_session_create(struct nb_cb_create_args *args, bool mhop) break; case NB_EV_PREPARE: - bfd_session_get_key(mhop, args->dnode, &bk); - bs = bfd_key_lookup(bk); + if (bfd_mode == BFD_MODE_TYPE_BFD) { + bfd_session_get_key(mhop, args->dnode, &bk); + bs = bfd_key_lookup(bk); + + /* This session was already configured by another daemon. */ + if (bs != NULL) { + /* Now it is configured also by CLI. */ + SET_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG); + bs->refcount++; + + args->resource->ptr = bs; + break; + } + + bs = bfd_session_new(BFD_MODE_TYPE_BFD); - /* This session was already configured by another daemon. */ - if (bs != NULL) { - /* Now it is configured also by CLI. */ + /* Fill the session key. */ + bfd_session_get_key(mhop, args->dnode, &bs->key); + /* Set configuration flags. */ + bs->refcount = 1; SET_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG); - bs->refcount++; + if (mhop) + SET_FLAG(bs->flags, BFD_SESS_FLAG_MH); + if (bs->key.family == AF_INET6) + SET_FLAG(bs->flags, BFD_SESS_FLAG_IPV6); args->resource->ptr = bs; break; - } + } else if (bfd_mode == BFD_MODE_TYPE_SBFD_ECHO || + bfd_mode == BFD_MODE_TYPE_SBFD_INIT) { + sbfd_session_get_key(mhop, args->dnode, &bk); + bs = bfd_key_lookup(bk); + + /* This session was already configured by another daemon. */ + if (bs != NULL) { + /* Now it is configured also by CLI. */ + SET_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG); + bs->refcount++; + + args->resource->ptr = bs; + break; + } - bs = bfd_session_new(); + if (bfd_mode == BFD_MODE_TYPE_SBFD_ECHO && + !yang_dnode_exists(args->dnode, "srv6-encap-data")) { + //srv6-encap-data should not be null for sbfd echo + snprintf(args->errmsg, args->errmsg_len, + "srv6-encap-data should not be null"); + return NB_ERR_RESOURCE; + } - /* Fill the session key. */ - bfd_session_get_key(mhop, args->dnode, &bs->key); + if (bfd_mode == BFD_MODE_TYPE_SBFD_ECHO && + !yang_dnode_exists(args->dnode, "srv6-source-ipv6")) { + snprintf(args->errmsg, args->errmsg_len, + "source_ipv6 should not be null"); + return NB_ERR_RESOURCE; + } - /* Set configuration flags. */ - bs->refcount = 1; - SET_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG); - if (mhop) - SET_FLAG(bs->flags, BFD_SESS_FLAG_MH); - if (bs->key.family == AF_INET6) - SET_FLAG(bs->flags, BFD_SESS_FLAG_IPV6); + if (bfd_mode == BFD_MODE_TYPE_SBFD_INIT) { + if (!yang_dnode_exists(args->dnode, "remote-discr")) { + snprintf(args->errmsg, args->errmsg_len, + "remote-discr should not be null"); + return NB_ERR_RESOURCE; + } + } - args->resource->ptr = bs; - break; + bfd_name = yang_dnode_get_string(args->dnode, "bfd-name"); + + bs = bfd_session_new(bfd_mode); + if (bs == NULL) { + snprintf(args->errmsg, args->errmsg_len, + "session-new: allocation failed"); + return NB_ERR_RESOURCE; + } + /* Fill the session key. */ + sbfd_session_get_key(mhop, args->dnode, &bs->key); + strlcpy(bs->bfd_name, bfd_name, BFD_NAME_SIZE); + + if (yang_dnode_exists(args->dnode, "srv6-encap-data")) { + yang_dnode_iterate(segment_list_iter_cb, bs, args->dnode, + "./srv6-encap-data"); + + + strtosa(yang_dnode_get_string(args->dnode, "./srv6-source-ipv6"), + &out_sip6); + memcpy(&bs->out_sip6, &out_sip6.sa_sin6.sin6_addr, + sizeof(struct in6_addr)); + } + + if (bfd_mode == BFD_MODE_TYPE_SBFD_INIT) { + bs->discrs.remote_discr = yang_dnode_get_uint32(args->dnode, + "./remote-discr"); + } + + /* Set configuration flags. */ + bs->refcount = 1; + SET_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG); + if (mhop) + SET_FLAG(bs->flags, BFD_SESS_FLAG_MH); + + if (bs->key.family == AF_INET6) + SET_FLAG(bs->flags, BFD_SESS_FLAG_IPV6); + + if (bfd_mode == BFD_MODE_TYPE_SBFD_ECHO) { + memcpy(&bs->key.peer, &bs->key.local, sizeof(struct in6_addr)); + } else { + bs->xmt_TO = bs->timers.desired_min_tx; + bs->detect_TO = bs->detect_mult * bs->xmt_TO; + } + + args->resource->ptr = bs; + break; + + } else { + snprintf(args->errmsg, args->errmsg_len, "bfd mode must be bfd or sbfd."); + return NB_ERR_VALIDATION; + } case NB_EV_APPLY: bs = args->resource->ptr; @@ -177,15 +356,19 @@ static int bfd_session_create(struct nb_cb_create_args *args, bool mhop) return NB_OK; } -static int bfd_session_destroy(enum nb_event event, - const struct lyd_node *dnode, bool mhop) +static int bfd_session_destroy(enum nb_event event, const struct lyd_node *dnode, bool mhop, + uint32_t bfd_mode) { struct bfd_session *bs; struct bfd_key bk; switch (event) { case NB_EV_VALIDATE: - bfd_session_get_key(mhop, dnode, &bk); + if (bfd_mode == BFD_MODE_TYPE_BFD) + bfd_session_get_key(mhop, dnode, &bk); + else + sbfd_session_get_key(mhop, dnode, &bk); + if (bfd_key_lookup(bk) == NULL) return NB_ERR_INCONSISTENCY; break; @@ -206,6 +389,12 @@ static int bfd_session_destroy(enum nb_event event, if (bs->refcount > 0) break; + if (bglobal.debug_peer_event) + zlog_info("session destroy: %s", bs_to_string(bs)); + + if (bfd_mode == BFD_MODE_TYPE_SBFD_ECHO || bfd_mode == BFD_MODE_TYPE_SBFD_INIT) + ptm_bfd_notify(bs, PTM_BFD_DOWN); + bfd_session_free(bs); break; @@ -510,12 +699,12 @@ int bfdd_bfd_profile_required_echo_receive_interval_modify( */ int bfdd_bfd_sessions_single_hop_create(struct nb_cb_create_args *args) { - return bfd_session_create(args, false); + return bfd_session_create(args, false, BFD_MODE_TYPE_BFD); } int bfdd_bfd_sessions_single_hop_destroy(struct nb_cb_destroy_args *args) { - return bfd_session_destroy(args->event, args->dnode, false); + return bfd_session_destroy(args->event, args->dnode, false, BFD_MODE_TYPE_BFD); } /* @@ -714,6 +903,45 @@ int bfdd_bfd_sessions_single_hop_passive_mode_modify( return NB_OK; } +/* + * XPath: /frr-bfdd:bfdd/bfd/sessions/sbfd-init/bfd-mode + * /frr-bfdd:bfdd/bfd/sessions/sbfd-echo/bfd-mode + */ +int bfdd_bfd_sessions_bfd_mode_modify(struct nb_cb_modify_args *args) +{ + uint32_t bfd_mode = yang_dnode_get_uint32(args->dnode, NULL); + struct bfd_session *bs; + + switch (args->event) { + case NB_EV_VALIDATE: + if ((bfd_mode != BFD_MODE_TYPE_BFD) && (bfd_mode != BFD_MODE_TYPE_SBFD_ECHO) && + (bfd_mode != BFD_MODE_TYPE_SBFD_INIT)) { + snprintf(args->errmsg, args->errmsg_len, "bfd mode is invalid."); + return NB_ERR_VALIDATION; + } + return NB_OK; + case NB_EV_PREPARE: + return NB_OK; + + case NB_EV_APPLY: + break; + + case NB_EV_ABORT: + return NB_OK; + } + + bs = nb_running_get_entry(args->dnode, NULL, true); + bs->bfd_mode = bfd_mode; + bfd_session_apply(bs); + + return NB_OK; +} + +int bfdd_bfd_sessions_bfd_mode_destroy(struct nb_cb_destroy_args *args) +{ + return NB_OK; +} + /* * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/echo-mode */ @@ -811,12 +1039,12 @@ int bfdd_bfd_sessions_single_hop_required_echo_receive_interval_modify( */ int bfdd_bfd_sessions_multi_hop_create(struct nb_cb_create_args *args) { - return bfd_session_create(args, true); + return bfd_session_create(args, true, BFD_MODE_TYPE_BFD); } int bfdd_bfd_sessions_multi_hop_destroy(struct nb_cb_destroy_args *args) { - return bfd_session_destroy(args->event, args->dnode, true); + return bfd_session_destroy(args->event, args->dnode, true, BFD_MODE_TYPE_BFD); } /* @@ -845,3 +1073,106 @@ int bfdd_bfd_sessions_multi_hop_minimum_ttl_modify( return NB_OK; } + +/* + * XPath: /frr-bfdd:bfdd/bfd/sessions/sbfd-echo + */ +int bfdd_bfd_sessions_sbfd_echo_create(struct nb_cb_create_args *args) +{ + return bfd_session_create(args, yang_dnode_exists(args->dnode, "multi-hop"), + BFD_MODE_TYPE_SBFD_ECHO); +} + +int bfdd_bfd_sessions_sbfd_echo_destroy(struct nb_cb_destroy_args *args) +{ + return bfd_session_destroy(args->event, args->dnode, + yang_dnode_exists(args->dnode, "multi-hop"), + BFD_MODE_TYPE_SBFD_ECHO); +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/sessions/sbfd-echo/srv6-encap-data + * XPath: /frr-bfdd:bfdd/bfd/sessions/sbfd-init/srv6-encap-data + */ +int bfdd_bfd_sessions_segment_list_create(struct nb_cb_create_args *args) +{ + return NB_OK; +} + +int bfdd_bfd_sessions_segment_list_destroy(struct nb_cb_destroy_args *args) +{ + return NB_OK; +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/sessions/sbfd-echo/dest-addr + */ +int bfdd_bfd_sessions_sbfd_echo_dest_addr_modify(struct nb_cb_modify_args *args) +{ + return NB_OK; +} + +int bfdd_bfd_sessions_sbfd_echo_dest_addr_destroy(struct nb_cb_destroy_args *args) +{ + return NB_OK; +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/sessions/sbfd-echo/echo-mode + */ +int bfdd_bfd_sessions_sbfd_echo_mode_modify(struct nb_cb_modify_args *args) +{ + return NB_OK; +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/sessions/sbfd-echo/srv6-source-ipv6 + * XPath: /frr-bfdd:bfdd/bfd/sessions/sbfd-init/srv6-source-ipv6 + */ +int bfdd_bfd_sessions_sbfd_srv6_source_ipv6_modify(struct nb_cb_modify_args *args) +{ + return NB_OK; +} + +int bfdd_bfd_sessions_sbfd_srv6_source_ipv6_destroy(struct nb_cb_destroy_args *args) +{ + return NB_OK; +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/sessions/sbfd-init + */ +int bfdd_bfd_sessions_sbfd_init_create(struct nb_cb_create_args *args) +{ + return bfd_session_create(args, yang_dnode_exists(args->dnode, "multi-hop"), + BFD_MODE_TYPE_SBFD_INIT); +} + +int bfdd_bfd_sessions_sbfd_init_destroy(struct nb_cb_destroy_args *args) +{ + return bfd_session_destroy(args->event, args->dnode, + yang_dnode_exists(args->dnode, "multi-hop"), + BFD_MODE_TYPE_SBFD_INIT); +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/sessions/sbfd-init/remote-discr + */ +int bfdd_bfd_sessions_sbfd_init_remote_discr_modify(struct nb_cb_modify_args *args) +{ + return NB_OK; +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/sessions/sbfd-echo/multi-hop + * XPath: /frr-bfdd:bfdd/bfd/sessions/sbfd-init/multi-hop + */ +int bfdd_bfd_sessions_sbfd_multi_hop_modify(struct nb_cb_modify_args *args) +{ + return NB_OK; +} + +int bfdd_bfd_sessions_sbfd_multi_hop_destroy(struct nb_cb_destroy_args *args) +{ + return NB_OK; +} diff --git a/bfdd/bfdd_nb_state.c b/bfdd/bfdd_nb_state.c index 12acda8fd8a6..c52847823148 100644 --- a/bfdd/bfdd_nb_state.c +++ b/bfdd/bfdd_nb_state.c @@ -20,7 +20,7 @@ const void * bfdd_bfd_sessions_single_hop_get_next(struct nb_cb_get_next_args *args) { - return bfd_session_next(args->list_entry, false); + return bfd_session_next(args->list_entry, false, BFD_MODE_TYPE_BFD); } int bfdd_bfd_sessions_single_hop_get_keys(struct nb_cb_get_keys_args *args) @@ -50,7 +50,7 @@ bfdd_bfd_sessions_single_hop_lookup_entry(struct nb_cb_lookup_entry_args *args) strtosa(dest_addr, &psa); memset(&lsa, 0, sizeof(lsa)); - gen_bfd_key(&bk, &psa, &lsa, false, ifname, vrf); + gen_bfd_key(&bk, &psa, &lsa, false, ifname, vrf, NULL); return bfd_key_lookup(bk); } @@ -323,7 +323,7 @@ bfdd_bfd_sessions_single_hop_stats_echo_packet_output_count_get_elem( const void * bfdd_bfd_sessions_multi_hop_get_next(struct nb_cb_get_next_args *args) { - return bfd_session_next(args->list_entry, true); + return bfd_session_next(args->list_entry, true, BFD_MODE_TYPE_BFD); } int bfdd_bfd_sessions_multi_hop_get_keys(struct nb_cb_get_keys_args *args) @@ -354,7 +354,87 @@ bfdd_bfd_sessions_multi_hop_lookup_entry(struct nb_cb_lookup_entry_args *args) strtosa(dest_addr, &psa); strtosa(source_addr, &lsa); - gen_bfd_key(&bk, &psa, &lsa, true, NULL, vrf); + gen_bfd_key(&bk, &psa, &lsa, true, NULL, vrf, NULL); + + return bfd_key_lookup(bk); +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/sessions/sbfd-echo + */ +const void *bfdd_bfd_sessions_sbfd_echo_get_next(struct nb_cb_get_next_args *args) +{ + return bfd_session_next(args->list_entry, true, BFD_MODE_TYPE_SBFD_ECHO); +} + +int bfdd_bfd_sessions_sbfd_echo_get_keys(struct nb_cb_get_keys_args *args) +{ + const struct bfd_session *bs = args->list_entry; + char srcbuf[INET6_ADDRSTRLEN]; + + inet_ntop(bs->key.family, &bs->key.local, srcbuf, sizeof(srcbuf)); + + args->keys->num = 3; + strlcpy(args->keys->key[0], srcbuf, sizeof(args->keys->key[0])); + strlcpy(args->keys->key[1], bs->key.bfdname, sizeof(args->keys->key[1])); + strlcpy(args->keys->key[2], bs->key.vrfname, sizeof(args->keys->key[2])); + + return NB_OK; +} + +const void *bfdd_bfd_sessions_sbfd_echo_lookup_entry(struct nb_cb_lookup_entry_args *args) +{ + const char *source_addr = args->keys->key[0]; + const char *bfdname = args->keys->key[1]; + const char *vrf = args->keys->key[2]; + struct sockaddr_any psa, lsa; + struct bfd_key bk; + + strtosa(source_addr, &lsa); + memset(&psa, 0, sizeof(psa)); + gen_bfd_key(&bk, &psa, &lsa, true, NULL, vrf, bfdname); + + return bfd_key_lookup(bk); +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/sessions/sbfd-init + */ +const void *bfdd_bfd_sessions_sbfd_init_get_next(struct nb_cb_get_next_args *args) +{ + return bfd_session_next(args->list_entry, true, BFD_MODE_TYPE_SBFD_INIT); +} + +int bfdd_bfd_sessions_sbfd_init_get_keys(struct nb_cb_get_keys_args *args) +{ + const struct bfd_session *bs = args->list_entry; + char srcbuf[INET6_ADDRSTRLEN]; + char dstbuf[INET6_ADDRSTRLEN]; + + inet_ntop(bs->key.family, &bs->key.local, srcbuf, sizeof(srcbuf)); + inet_ntop(bs->key.family, &bs->key.peer, dstbuf, sizeof(dstbuf)); + + args->keys->num = 4; + strlcpy(args->keys->key[0], srcbuf, sizeof(args->keys->key[0])); + strlcpy(args->keys->key[1], dstbuf, sizeof(args->keys->key[1])); + strlcpy(args->keys->key[2], bs->key.bfdname, sizeof(args->keys->key[2])); + strlcpy(args->keys->key[3], bs->key.vrfname, sizeof(args->keys->key[3])); + + return NB_OK; +} + +const void *bfdd_bfd_sessions_sbfd_init_lookup_entry(struct nb_cb_lookup_entry_args *args) +{ + const char *source_addr = args->keys->key[0]; + const char *dest_addr = args->keys->key[1]; + const char *bfdname = args->keys->key[2]; + const char *vrf = args->keys->key[3]; + struct sockaddr_any psa, lsa; + struct bfd_key bk; + + strtosa(source_addr, &lsa); + strtosa(dest_addr, &psa); + gen_bfd_key(&bk, &psa, &lsa, true, NULL, vrf, bfdname); return bfd_key_lookup(bk); } diff --git a/bfdd/bfdd_vty.c b/bfdd/bfdd_vty.c index 26554e1496b8..c2811978491b 100644 --- a/bfdd/bfdd_vty.c +++ b/bfdd/bfdd_vty.c @@ -58,7 +58,49 @@ _find_peer_or_error(struct vty *vty, int argc, struct cmd_token **argv, const char *label, const char *peer_str, const char *local_str, const char *ifname, const char *vrfname); +static void _display_bfd_by_bfdname_json_iter(struct hash_bucket *hb, void *arg); +static void _display_bfd_by_bfdname_iter(struct hash_bucket *hb, void *arg); +static void _display_bfd_by_bfdname(struct vty *vty, const char *vrfname, const char *bfdname, + bool use_json); +static void _display_bfd_counters_by_bfdname_iter(struct hash_bucket *hb, void *arg); +static void _display_bfd_counters_json_by_bfdname_iter(struct hash_bucket *hb, void *arg); +static void _display_bfd_counters_by_bfdname(struct vty *vty, const char *vrfname, + const char *bfdname, bool use_json); +static void _clear_bfd_counters_by_bfdname(const char *vrfname, const char *bfdname); +static void _clear_peer_counter(struct bfd_session *bs); + +static const char *bfd_mode_type_to_string(enum bfd_mode_type mode) +{ + switch (mode) { + case BFD_MODE_TYPE_BFD: + return "bfd"; + case BFD_MODE_TYPE_SBFD_ECHO: + return "sbfd-echo"; + case BFD_MODE_TYPE_SBFD_INIT: + return "sbfd-init"; + default: + return "Unknown"; + } +} + +static char *sbfd_sidlist_to_string(struct in6_addr *sidlist, uint8_t segnum) +{ + static char buf[INET6_ADDRSTRLEN * SRV6_MAX_SEGS]; + int pos = 0; + uint8_t i = 0; + char addr_buf[INET6_ADDRSTRLEN]; + memset(buf, 0, 256); + + pos = snprintf(buf, sizeof(buf), "%s", + inet_ntop(AF_INET6, &sidlist[0], addr_buf, sizeof(addr_buf))); + + for (i = 1; i < segnum; i++) + pos += snprintf(buf + pos, sizeof(buf) - pos, " %s", + inet_ntop(AF_INET6, &sidlist[i], addr_buf, sizeof(addr_buf))); + + return buf; +} /* * Show commands helper functions @@ -71,6 +113,12 @@ static void _display_peer_header(struct vty *vty, struct bfd_session *bs) inet_ntop(bs->key.family, &bs->key.peer, addr_buf, sizeof(addr_buf))); + if (bs->bfd_mode == BFD_MODE_TYPE_SBFD_ECHO || bs->bfd_mode == BFD_MODE_TYPE_SBFD_INIT) + vty_out(vty, " bfd-mode %s", bfd_mode_type_to_string(bs->bfd_mode)); + + if (bs->bfd_name[0]) + vty_out(vty, " bfd-name %s", bs->bfd_name); + if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) vty_out(vty, " multihop"); @@ -83,6 +131,20 @@ static void _display_peer_header(struct vty *vty, struct bfd_session *bs) vty_out(vty, " vrf %s", bs->key.vrfname); if (bs->key.ifname[0]) vty_out(vty, " interface %s", bs->key.ifname); + + if (bs->bfd_mode == BFD_MODE_TYPE_SBFD_ECHO || bs->bfd_mode == BFD_MODE_TYPE_SBFD_INIT) { + if (bs->bfd_mode == BFD_MODE_TYPE_SBFD_INIT) + vty_out(vty, " remote-discr %u", bs->discrs.remote_discr); + + if (bs->bfd_name[0] && bs->segnum) { + vty_out(vty, " srv6-source-ipv6 %s", + inet_ntop(AF_INET6, &bs->out_sip6, addr_buf, sizeof(addr_buf))); + + vty_out(vty, " srv6-encap-data %s", + sbfd_sidlist_to_string(bs->seg_list, bs->segnum)); + } + } + vty_out(vty, "\n"); } @@ -135,10 +197,16 @@ static void _display_peer(struct vty *vty, struct bfd_session *bs) vty_out(vty, "\t\tDiagnostics: %s\n", diag2str(bs->local_diag)); vty_out(vty, "\t\tRemote diagnostics: %s\n", diag2str(bs->remote_diag)); - vty_out(vty, "\t\tPeer Type: %s\n", - CHECK_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG) ? "configured" : "dynamic"); - _display_rtt(&min, &avg, &max, bs); - vty_out(vty, "\t\tRTT min/avg/max: %u/%u/%u usec\n", min, avg, max); + if (bs->bfd_mode == BFD_MODE_TYPE_SBFD_INIT) { + vty_out(vty, "\t\tPeer Type: sbfd initiator\n"); + } else if (bs->bfd_mode == BFD_MODE_TYPE_SBFD_ECHO) { + vty_out(vty, "\t\tPeer Type: echo\n"); + } else { + vty_out(vty, "\t\tPeer Type: %s\n", + CHECK_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG) ? "configured" : "dynamic"); + _display_rtt(&min, &avg, &max, bs); + vty_out(vty, "\t\tRTT min/avg/max: %u/%u/%u usec\n", min, avg, max); + } vty_out(vty, "\t\tLocal timers:\n"); vty_out(vty, "\t\t\tDetect-multiplier: %u\n", @@ -152,24 +220,32 @@ static void _display_peer(struct vty *vty, struct bfd_session *bs) bs->timers.required_min_echo_rx / 1000); else vty_out(vty, "\t\t\tEcho receive interval: disabled\n"); - if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO)) + if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO) || bs->bfd_mode == BFD_MODE_TYPE_SBFD_ECHO) vty_out(vty, "\t\t\tEcho transmission interval: %ums\n", bs->timers.desired_min_echo_tx / 1000); else vty_out(vty, "\t\t\tEcho transmission interval: disabled\n"); - vty_out(vty, "\t\tRemote timers:\n"); - vty_out(vty, "\t\t\tDetect-multiplier: %u\n", - bs->remote_detect_mult); - vty_out(vty, "\t\t\tReceive interval: %ums\n", - bs->remote_timers.required_min_rx / 1000); - vty_out(vty, "\t\t\tTransmission interval: %ums\n", - bs->remote_timers.desired_min_tx / 1000); - if (bs->remote_timers.required_min_echo != 0) - vty_out(vty, "\t\t\tEcho receive interval: %ums\n", - bs->remote_timers.required_min_echo / 1000); - else - vty_out(vty, "\t\t\tEcho receive interval: disabled\n"); + + if (bs->bfd_mode == BFD_MODE_TYPE_SBFD_INIT || bs->bfd_mode == BFD_MODE_TYPE_SBFD_ECHO) { + vty_out(vty, "\t\tRemote timers:\n"); + vty_out(vty, "\t\t\tDetect-multiplier: -\n"); + vty_out(vty, "\t\t\tReceive interval: -\n"); + vty_out(vty, "\t\t\tTransmission interval: -\n"); + vty_out(vty, "\t\t\tEcho receive interval: -\n"); + } else { + vty_out(vty, "\t\tRemote timers:\n"); + vty_out(vty, "\t\t\tDetect-multiplier: %u\n", bs->remote_detect_mult); + vty_out(vty, "\t\t\tReceive interval: %ums\n", + bs->remote_timers.required_min_rx / 1000); + vty_out(vty, "\t\t\tTransmission interval: %ums\n", + bs->remote_timers.desired_min_tx / 1000); + if (bs->remote_timers.required_min_echo != 0) + vty_out(vty, "\t\t\tEcho receive interval: %ums\n", + bs->remote_timers.required_min_echo / 1000); + else + vty_out(vty, "\t\t\tEcho receive interval: disabled\n"); + } vty_out(vty, "\n"); } @@ -216,6 +292,9 @@ static struct json_object *__display_peer_json(struct bfd_session *bs) if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) json_object_int_add(jo, "minimum-ttl", bs->mh_ttl); + if (bs->bfd_mode != BFD_MODE_TYPE_BFD) + json_object_string_add(jo, "bfd-name", bs->bfd_name); + switch (bs->ses_state) { case PTM_BFD_ADM_DOWN: json_object_string_add(jo, "status", "shutdown"); @@ -253,7 +332,13 @@ static struct json_object *__display_peer_json(struct bfd_session *bs) bs->timers.desired_min_tx / 1000); json_object_int_add(jo, "echo-receive-interval", bs->timers.required_min_echo_rx / 1000); - if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO)) + if (bs->bfd_mode == BFD_MODE_TYPE_SBFD_INIT || bs->bfd_mode == BFD_MODE_TYPE_SBFD_ECHO) { + json_object_int_add(jo, "configured-echo-transmit-interval", + bs->timers.desired_min_echo_tx / 1000); + json_object_int_add(jo, "current-echo-transmit-interval", bs->echo_xmt_TO / 1000); + json_object_int_add(jo, "current-detect-echo-receive-interval", + bs->echo_detect_TO / 1000); + } else if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO)) json_object_int_add(jo, "echo-transmit-interval", bs->timers.desired_min_echo_tx / 1000); else @@ -289,6 +374,7 @@ struct bfd_vrf_tuple { const char *vrfname; struct vty *vty; struct json_object *jo; + const char *bfdname; }; static void _display_peer_iter(struct hash_bucket *hb, void *arg) @@ -309,6 +395,30 @@ static void _display_peer_iter(struct hash_bucket *hb, void *arg) _display_peer(vty, bs); } +static void _display_bfd_by_bfdname_iter(struct hash_bucket *hb, void *arg) +{ + struct bfd_vrf_tuple *bvt = (struct bfd_vrf_tuple *)arg; + struct vty *vty; + struct bfd_session *bs = hb->data; + + if (!bvt) + return; + vty = bvt->vty; + + if (bvt->vrfname) { + if (!bs->key.vrfname[0] || !strmatch(bs->key.vrfname, bvt->vrfname)) + return; + } + + if (bvt->bfdname) { + if ((!bs->key.bfdname[0] || !strmatch(bs->key.bfdname, bvt->bfdname)) && + (!bs->bfd_name[0] || !strmatch(bs->bfd_name, bvt->bfdname))) + return; + } + + _display_peer(vty, bs); +} + static void _display_peer_json_iter(struct hash_bucket *hb, void *arg) { struct bfd_vrf_tuple *bvt = (struct bfd_vrf_tuple *)arg; @@ -334,6 +444,58 @@ static void _display_peer_json_iter(struct hash_bucket *hb, void *arg) json_object_array_add(jo, jon); } +static void _display_bfd_by_bfdname_json_iter(struct hash_bucket *hb, void *arg) +{ + struct bfd_vrf_tuple *bvt = (struct bfd_vrf_tuple *)arg; + struct json_object *jo, *jon = NULL; + struct bfd_session *bs = hb->data; + + if (!bvt) + return; + jo = bvt->jo; + + if (bvt->vrfname) { + if (!bs->key.vrfname[0] || !strmatch(bs->key.vrfname, bvt->vrfname)) + return; + } + + if (bvt->bfdname) { + if ((!bs->key.bfdname[0] || !strmatch(bs->key.bfdname, bvt->bfdname)) && + (!bs->bfd_name[0] || !strmatch(bs->bfd_name, bvt->bfdname))) + return; + } + + jon = __display_peer_json(bs); + if (jon == NULL) { + zlog_warn("%s: not enough memory", __func__); + return; + } + + json_object_array_add(jo, jon); +} +static void _display_bfd_by_bfdname(struct vty *vty, const char *vrfname, const char *bfdname, + bool use_json) +{ + struct json_object *jo; + struct bfd_vrf_tuple bvt = { 0 }; + + bvt.vrfname = vrfname; + bvt.bfdname = bfdname; + + if (!use_json) { + bvt.vty = vty; + vty_out(vty, "BFD Peers:\n"); + bfd_id_iterate(_display_bfd_by_bfdname_iter, &bvt); + return; + } + + jo = json_object_new_array(); + bvt.jo = jo; + bfd_id_iterate(_display_bfd_by_bfdname_json_iter, &bvt); + + vty_json(vty, jo); +} + static void _display_all_peers(struct vty *vty, char *vrfname, bool use_json) { struct json_object *jo; @@ -378,6 +540,7 @@ static void _display_peer_counter(struct vty *vty, struct bfd_session *bs) bs->stats.session_down); vty_out(vty, "\t\tZebra notifications: %" PRIu64 "\n", bs->stats.znotification); + vty_out(vty, "\t\tTx fail packet: %" PRIu64 "\n", bs->stats.tx_fail_pkt); vty_out(vty, "\n"); } @@ -398,6 +561,9 @@ static struct json_object *__display_peer_counters_json(struct bfd_session *bs) json_object_int_add(jo, "session-down", bs->stats.session_down); json_object_int_add(jo, "zebra-notifications", bs->stats.znotification); + if (bs->bfd_mode == BFD_MODE_TYPE_SBFD_INIT || bs->bfd_mode == BFD_MODE_TYPE_SBFD_ECHO) + json_object_int_add(jo, "tx-fail-packet", bs->stats.tx_fail_pkt); + return jo; } @@ -472,6 +638,109 @@ static void _display_peers_counter(struct vty *vty, char *vrfname, bool use_json vty_json(vty, jo); } +static void _display_bfd_counters_by_bfdname_iter(struct hash_bucket *hb, void *arg) +{ + struct bfd_vrf_tuple *bvt = arg; + struct vty *vty; + struct bfd_session *bs = hb->data; + + if (!bvt) + return; + vty = bvt->vty; + + if (bvt->vrfname) { + if (!bs->key.vrfname[0] || !strmatch(bs->key.vrfname, bvt->vrfname)) + return; + } + + if (bvt->bfdname) { + if (!bs->key.bfdname[0] || !strmatch(bs->key.bfdname, bvt->bfdname)) + return; + } + + _display_peer_counter(vty, bs); +} +static void _display_bfd_counters_json_by_bfdname_iter(struct hash_bucket *hb, void *arg) +{ + struct json_object *jo, *jon = NULL; + struct bfd_session *bs = hb->data; + struct bfd_vrf_tuple *bvt = arg; + + if (!bvt) + return; + jo = bvt->jo; + + if (bvt->vrfname) { + if (!bs->key.vrfname[0] || !strmatch(bs->key.vrfname, bvt->vrfname)) + return; + } + + if (bvt->bfdname) { + if (!bs->key.bfdname[0] || !strmatch(bs->key.bfdname, bvt->bfdname)) + return; + } + + jon = __display_peer_counters_json(bs); + if (jon == NULL) { + zlog_warn("%s: not enough memory", __func__); + return; + } + + json_object_array_add(jo, jon); +} +static void _display_bfd_counters_by_bfdname(struct vty *vty, const char *vrfname, + const char *bfdname, bool use_json) +{ + struct json_object *jo; + struct bfd_vrf_tuple bvt = { 0 }; + + bvt.vrfname = vrfname; + bvt.bfdname = bfdname; + + if (!use_json) { + bvt.vty = vty; + vty_out(vty, "BFD Peers:\n"); + bfd_id_iterate(_display_bfd_counters_by_bfdname_iter, &bvt); + return; + } + + jo = json_object_new_array(); + bvt.jo = jo; + bfd_id_iterate(_display_bfd_counters_json_by_bfdname_iter, &bvt); + + vty_json(vty, jo); +} +static void _clear_bfd_counters_by_bfdname_iter(struct hash_bucket *hb, void *arg) +{ + struct bfd_vrf_tuple *bvt = arg; + struct bfd_session *bs = hb->data; + + if (!bvt) + return; + + if (bvt->vrfname) { + if (!bs->key.vrfname[0] || !strmatch(bs->key.vrfname, bvt->vrfname)) + return; + } + + if (bvt->bfdname) { + if (!bs->key.bfdname[0] || !strmatch(bs->key.bfdname, bvt->bfdname)) + return; + } + + _clear_peer_counter(bs); +} + +static void _clear_bfd_counters_by_bfdname(const char *vrfname, const char *bfdname) +{ + struct bfd_vrf_tuple bvt = { 0 }; + + bvt.vrfname = vrfname; + bvt.bfdname = bfdname; + + bfd_id_iterate(_clear_bfd_counters_by_bfdname_iter, &bvt); +} + static void _clear_peer_counter(struct bfd_session *bs) { /* Clear only pkt stats, intention is not to loose system @@ -486,12 +755,21 @@ static void _display_peer_brief(struct vty *vty, struct bfd_session *bs) { char addr_buf[INET6_ADDRSTRLEN]; - vty_out(vty, "%-10u", bs->discrs.my_discr); - inet_ntop(bs->key.family, &bs->key.local, addr_buf, sizeof(addr_buf)); - vty_out(vty, " %-40s", addr_buf); - inet_ntop(bs->key.family, &bs->key.peer, addr_buf, sizeof(addr_buf)); - vty_out(vty, " %-40s", addr_buf); - vty_out(vty, "%-15s\n", state_list[bs->ses_state].str); + if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) { + vty_out(vty, "%-10u", bs->discrs.my_discr); + inet_ntop(bs->key.family, &bs->key.local, addr_buf, sizeof(addr_buf)); + vty_out(vty, " %-40s", addr_buf); + inet_ntop(bs->key.family, &bs->key.peer, addr_buf, sizeof(addr_buf)); + vty_out(vty, " %-40s", addr_buf); + vty_out(vty, "%-15s\n", state_list[bs->ses_state].str); + } else { + vty_out(vty, "%-10u", bs->discrs.my_discr); + vty_out(vty, " %-40s", satostr(&bs->local_address)); + inet_ntop(bs->key.family, &bs->key.peer, addr_buf, sizeof(addr_buf)); + vty_out(vty, " %-40s", addr_buf); + + vty_out(vty, "%-15s\n", state_list[bs->ses_state].str); + } } static void _display_peer_brief_iter(struct hash_bucket *hb, void *arg) @@ -558,6 +836,8 @@ _find_peer_or_error(struct vty *vty, int argc, struct cmd_token **argv, struct bfd_peer_cfg bpc; struct sockaddr_any psa, lsa, *lsap; char errormsg[128]; + struct vrf *vrf = NULL; + char *tmpName = NULL; if (peer_str) { strtosa(peer_str, &psa); @@ -570,9 +850,18 @@ _find_peer_or_error(struct vty *vty, int argc, struct cmd_token **argv, idx = 0; mhop = argv_find(argv, argc, "multihop", &idx); - if (bfd_configure_peer(&bpc, mhop, &psa, lsap, ifname, vrfname, - errormsg, sizeof(errormsg)) - != 0) { + tmpName = (char *)vrfname; + if (vrfname) { + vrf = vrf_lookup_by_name(vrfname); + if (vrf == NULL) { + vty_out(vty, "%% Vrf is not exist: %s\n", vrfname); + return NULL; + } + tmpName = vrf->name; + } + + if (bfd_configure_peer(&bpc, mhop, &psa, lsap, ifname, tmpName, errormsg, + sizeof(errormsg)) != 0) { vty_out(vty, "%% Invalid peer configuration: %s\n", errormsg); return NULL; @@ -631,6 +920,49 @@ void _display_rtt(uint32_t *min, uint32_t *avg, uint32_t *max, /* * Show commands. */ +DEFPY(bfd_show_by_bfdname, bfd_show_by_bfdname_cmd, + "show bfd [vrf NAME$vrf_name] bfd-name BFDNAME$bfdname [json]", + SHOW_STR + "Bidirection Forwarding Detection\n" + VRF_CMD_HELP_STR + "Specify bfd session name\n" + "bfd session name\n" + JSON_STR) +{ + _display_bfd_by_bfdname(vty, vrf_name, bfdname, use_json(argc, argv)); + + return CMD_SUCCESS; +} + +DEFPY(bfd_show_counters_by_bfdname, bfd_show_counters_by_bfdname_cmd, + "show bfd [vrf NAME$vrf_name] bfd-name BFDNAME$bfdname counters [json]", + SHOW_STR + "Bidirection Forwarding Detection\n" + VRF_CMD_HELP_STR + "Specify bfd session name\n" + "bfd session name\n" + "Show BFD peer counters information\n" + JSON_STR) +{ + _display_bfd_counters_by_bfdname(vty, vrf_name, bfdname, use_json(argc, argv)); + + return CMD_SUCCESS; +} + +DEFPY(bfd_clear_counters_by_bfdname, bfd_clear_counters_by_bfdname_cmd, + "clear bfd [vrf NAME$vrfname] bfd-name BFDNAME$bfdname counters", + CLEAR_STR + "Bidirection Forwarding Detection\n" + VRF_CMD_HELP_STR + "Specify bfd session name\n" + "bfd session name\n" + "clear BFD peer counters information\n") +{ + _clear_bfd_counters_by_bfdname(vrfname, bfdname); + + return CMD_SUCCESS; +} + DEFPY(bfd_show_peers, bfd_show_peers_cmd, "show bfd [vrf NAME] peers [json]", SHOW_STR "Bidirection Forwarding Detection\n" @@ -971,6 +1303,22 @@ struct cmd_node bfd_peer_node = { .prompt = "%s(config-bfd-peer)# ", }; +static void _sbfd_reflector_write_config(struct hash_bucket *hb, void *arg) +{ + struct sbfd_reflector *sr = hb->data; + char buf[INET6_ADDRSTRLEN]; + struct vty *vty; + + vty = (struct vty *)arg; + inet_ntop(AF_INET6, &sr->local, buf, sizeof(buf)); + vty_out(vty, " sbfd reflector source-address %s discriminator %u\n", buf, sr->discr); +} + +static void sbfd_reflector_write_config(struct vty *vty) +{ + sbfd_discr_iterate(_sbfd_reflector_write_config, vty); +} + static int bfdd_write_config(struct vty *vty) { struct lyd_node *dnode; @@ -1002,6 +1350,9 @@ static int bfdd_write_config(struct vty *vty) written = 1; } + /*sbfd config*/ + sbfd_reflector_write_config(vty); + return written; } @@ -1011,6 +1362,9 @@ void bfdd_vty_init(void) install_element(ENABLE_NODE, &bfd_show_peer_counters_cmd); install_element(ENABLE_NODE, &bfd_clear_peer_counters_cmd); install_element(ENABLE_NODE, &bfd_show_peers_cmd); + install_element(ENABLE_NODE, &bfd_show_by_bfdname_cmd); + install_element(ENABLE_NODE, &bfd_show_counters_by_bfdname_cmd); + install_element(ENABLE_NODE, &bfd_clear_counters_by_bfdname_cmd); install_element(ENABLE_NODE, &bfd_show_peer_cmd); install_element(ENABLE_NODE, &bfd_show_peers_brief_cmd); install_element(ENABLE_NODE, &show_bfd_distributed_cmd); diff --git a/bfdd/event.c b/bfdd/event.c index e797e71f050a..e5f43b6cc698 100644 --- a/bfdd/event.c +++ b/bfdd/event.c @@ -58,6 +58,73 @@ void bfd_echo_recvtimer_update(struct bfd_session *bs) &bs->echo_recvtimer_ev); } +void sbfd_init_recvtimer_update(struct bfd_session *bs) +{ + struct timeval tv = { .tv_sec = 0, .tv_usec = bs->detect_TO }; + + /* Remove previous schedule if any. */ + sbfd_init_recvtimer_delete(bs); + + /* Don't add event if peer is deactivated. */ + if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN) || bs->sock == -1) + return; + + tv_normalize(&tv); +#ifdef BFD_EVENT_DEBUG + log_debug("%s: sec = %ld, usec = %ld", __func__, tv.tv_sec, tv.tv_usec); +#endif /* BFD_EVENT_DEBUG */ + + event_add_timer_tv(master, sbfd_init_recvtimer_cb, bs, &tv, &bs->recvtimer_ev); +} + +void sbfd_echo_recvtimer_update(struct bfd_session *bs) +{ + struct timeval tv = { .tv_sec = 0, .tv_usec = bs->echo_detect_TO }; + + /* Remove previous schedule if any. */ + sbfd_echo_recvtimer_delete(bs); + + /* Don't add event if peer is deactivated. */ + if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN) || bs->sock == -1) + return; + + tv_normalize(&tv); + + event_add_timer_tv(master, sbfd_echo_recvtimer_cb, bs, &tv, &bs->echo_recvtimer_ev); +} + +void sbfd_init_xmttimer_update(struct bfd_session *bs, uint64_t jitter) +{ + struct timeval tv = { .tv_sec = 0, .tv_usec = jitter }; + + /* Remove previous schedule if any. */ + sbfd_init_xmttimer_delete(bs); + + /* Don't add event if peer is deactivated. */ + if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN) || bs->sock == -1) + return; + + tv_normalize(&tv); + + event_add_timer_tv(master, sbfd_init_xmt_cb, bs, &tv, &bs->xmttimer_ev); +} + +void sbfd_echo_xmttimer_update(struct bfd_session *bs, uint64_t jitter) +{ + struct timeval tv = { .tv_sec = 0, .tv_usec = jitter }; + + /* Remove previous schedule if any. */ + sbfd_echo_xmttimer_delete(bs); + + /* Don't add event if peer is deactivated. */ + if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN) || bs->sock == -1) + return; + + tv_normalize(&tv); + + event_add_timer_tv(master, sbfd_echo_xmt_cb, bs, &tv, &bs->echo_xmttimer_ev); +} + void bfd_xmttimer_update(struct bfd_session *bs, uint64_t jitter) { struct timeval tv = {.tv_sec = 0, .tv_usec = jitter}; @@ -112,3 +179,23 @@ void bfd_echo_xmttimer_delete(struct bfd_session *bs) { EVENT_OFF(bs->echo_xmttimer_ev); } + +void sbfd_init_recvtimer_delete(struct bfd_session *bs) +{ + EVENT_OFF(bs->recvtimer_ev); +} + +void sbfd_echo_recvtimer_delete(struct bfd_session *bs) +{ + EVENT_OFF(bs->echo_recvtimer_ev); +} + +void sbfd_init_xmttimer_delete(struct bfd_session *bs) +{ + EVENT_OFF(bs->xmttimer_ev); +} + +void sbfd_echo_xmttimer_delete(struct bfd_session *bs) +{ + EVENT_OFF(bs->echo_xmttimer_ev); +} diff --git a/bfdd/ptm_adapter.c b/bfdd/ptm_adapter.c index f6ebefb7becb..74f2d39878a6 100644 --- a/bfdd/ptm_adapter.c +++ b/bfdd/ptm_adapter.c @@ -70,10 +70,10 @@ static void bfdd_client_deregister(struct stream *msg); PRINTFRR(2, 3) static void debug_printbpc(const struct bfd_peer_cfg *bpc, const char *fmt, ...) { - char timers[3][128] = {}; + char timers[3][160] = {}; char minttl_str[32] = {}; - char addr[3][128] = {}; - char profile[128] = {}; + char addr[3][160] = {}; + char profile[160] = {}; char cbit_str[32]; char msgbuf[512]; va_list vl; @@ -134,7 +134,9 @@ static void _ptm_bfd_session_del(struct bfd_session *bs, uint8_t diag) /* Change state and notify peer. */ bs->ses_state = PTM_BFD_DOWN; bs->local_diag = diag; - ptm_bfd_snd(bs, 0); + + if (bs->bfd_mode == BFD_MODE_TYPE_BFD) + ptm_bfd_snd(bs, 0); /* Session reached refcount == 0, lets delete it. */ if (bs->refcount == 0) { @@ -200,6 +202,8 @@ int ptm_bfd_notify(struct bfd_session *bs, uint8_t notify_state) * - 16 bytes: ipv6 * - c: prefix length * - c: cbit + * - c: bfd name len + * - Xbytes: bfd name * * Commands: ZEBRA_BFD_DEST_REPLAY * @@ -238,9 +242,12 @@ int ptm_bfd_notify(struct bfd_session *bs, uint8_t notify_state) case PTM_BFD_DOWN: case PTM_BFD_INIT: - stream_putl(msg, BFD_STATUS_DOWN); - break; + if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN)) + stream_putl(msg, BFD_STATUS_ADMIN_DOWN); + else + stream_putl(msg, BFD_STATUS_DOWN); + break; default: stream_putl(msg, BFD_STATUS_UNKNOWN); break; @@ -251,6 +258,9 @@ int ptm_bfd_notify(struct bfd_session *bs, uint8_t notify_state) stream_putc(msg, bs->remote_cbit); + stream_putc(msg, strlen(bs->bfd_name)); + stream_put(msg, bs->bfd_name, strlen(bs->bfd_name)); + /* Write packet size. */ stream_putw_at(msg, 0, stream_get_endp(msg)); diff --git a/configure.ac b/configure.ac index e8036fcff1f5..8b5cfe910291 100644 --- a/configure.ac +++ b/configure.ac @@ -2443,6 +2443,23 @@ AC_COMPILE_IFELSE( AC_MSG_RESULT([no]) ]) +dnl ---------------------- +dnl checking for IPV6_HDRINCL +dnl ---------------------- +AC_MSG_CHECKING([for IPV6_HDRINCL]) +AC_COMPILE_IFELSE( +[ AC_LANG_PROGRAM([[ + #include + ]], [[ + int opt = IPV6_HDRINCL; + ]]) +],[ + AC_MSG_RESULT([yes]) + AC_DEFINE([HAVE_IPV6_HDRINCL], [1], [Have IPV6_HDRINCL]) +],[ + AC_MSG_RESULT([no]) +]) + dnl -------------------------------------- dnl checking for be32dec existence or not dnl -------------------------------------- diff --git a/doc/developer/sbfd.rst b/doc/developer/sbfd.rst new file mode 100644 index 000000000000..7bbd2428ddd7 --- /dev/null +++ b/doc/developer/sbfd.rst @@ -0,0 +1,140 @@ +.. _sbfd: + +**** +SBFD +**** + +:abbr:`SBFD (Seamless Bidirectional Forwarding Detection)` is: + + Seamless Bidirectional Forwarding Detection, a simplified mechanism for using BFD with a large + proportion of negotiation aspects eliminated, thus providing benefits + such as quick provisioning, as well as improved control and + flexibility for network nodes initiating path monitoring. + + -- :rfc:`7880` + +It is described and extended by the following RFCs: + +* :rfc:`7880` +* :rfc:`7881` + +.. _sbfd-sate-machine: + +SBFD state machine +================== + +SBFD takes the same data packet format as BFD, but with a much simpler state machine. +According to RFC7880, SBFD has a stateless SBFDReflector and a stateful SBFDInitiator with the state machine as below: + +:: + + +--+ + ADMIN DOWN, | | + TIMER | V + +------+ UP +------+ + | |-------------------->| |----+ + | DOWN | | UP | | UP + | |<--------------------| |<---+ + +------+ ADMIN DOWN, +------+ + TIMER + + Figure 1: SBFDInitiator Finite State Machine + +.. _sbfd-extention: + +SBFD extension - SRv6 encapsulation +=================================== + +SBFDInitiator periodically send packets to monitor the connection to SBFDReflector. We set up an SBFD connection between the source and the destination node of a path, +with the source node serving as Initiator and the destination node as Reflector. The communicated SBFD packets should also follow every exact hop in the path, +from the source to the destination, which could be achieved by segment routing. This requirement extends the node verification to the path verification. + +.. _sbfd-implement: + +implementation +=============== + +Some considerations when implementing sbfd. + + + +.. _sbfd-implement-coexist: + +SBFD Co-exist with BFD +-------------------------- + +Both SBFD and Classical BFD have their unique discriminator, SBFD can co-exist with BFD since they sharing a same discriminator pool in bfdd. +Also in bfdd SBFD and BFD can share most code logic, SBFD packet and BFD packet are demultiplexed by different discriminators. + + +.. _sbfd-implement-bfdname: + +SBFD name +--------- + +We introduced a bfd-name for every sbfd session. A unique bfd-name can be used to identify a sbfd session quickly. This is quite useful in our Srv6 deployment for path protection case. +A bfd-name is always associated with a TE path, for example if we use the sbfd session to protect the path A-B-D, we would assign the name 'path-a-b-d' or 'a-b-d' to the session. + +Meanwhile bfdd will notify the sbfd status to the Pathd, we should add the bfd-name field in PTM bfd notify message ZEBRA_BFD_DEST_REPLAY: + +:: + * Message format: + * - header: command, vrf + * - l: interface index + * - c: family + * - AF_INET: + * - 4 bytes: ipv4 + * - AF_INET6: + * - 16 bytes: ipv6 + * - c: prefix length + * - l: bfd status + * - c: family + * - AF_INET: + * - 4 bytes: ipv4 + * - AF_INET6: + * - 16 bytes: ipv6 + * - c: prefix length + * - c: cbit + * - c: bfd name len <---- new field + * - Xbytes: bfd name <---- new field + * + * Commands: ZEBRA_BFD_DEST_REPLAY + * + * q(64), l(32), w(16), c(8) + + + +.. _sbfd-implement-port: + +SBFD UDP port +------------- + +According to RFC7881, SBFD Control packet dst port should be 7784, src port can be any but NOT 7784. In our implementation, the UDP ports in packet are set as: + + +:: + UDP(sport=4784, dport=7784)/BFD() or UDP(sport=3784, dport=7784)/BFD() + +if "multihop" is specified for sbfd initiator we choose the 4784 as the source port, so the reflected packet will take 4784 as the dst port, this is a local BFD_MULTI_HOP_PORT so the reflected packet can be handled by the existing bfd_recv_cb function. +if "multihop" is not specified for sbfd initiator we choose the 3784 as the source port, this is a local BFD_DEFDESTPORT so the reflected packet can be handled by the existing bfd_recv_cb function. + + +For echo SBFD with SRv6 encapsulation case, we re-use the BFD Echo port, the UDP ports in packet are set as: + +:: + UDP(sport=3785, dport=3785)/BFD() + + +we choose the 3785 as the source port, so the echo back packet will take 3785 as the dst port, this is a local BFD_DEF_ECHO_PORT so the packet can be handled by the existing bfd_recv_cb function. + + +.. _sbfd-not-implemented: + +Todo list for SBFD +------------------ + + Currently some features are not yet implemented for SBFD, will add it in future: + 1) SBFD in IPv4 only packet + 2) The ADMIN DOWN logic + 3) SBFD echo function in a initiator session + 4) SBFD over MPLS diff --git a/doc/developer/subdir.am b/doc/developer/subdir.am index 652ee4e1af6a..67f42bc11f27 100644 --- a/doc/developer/subdir.am +++ b/doc/developer/subdir.am @@ -82,6 +82,7 @@ dev_RSTFILES = \ doc/developer/northbound/transactional-cli.rst \ doc/developer/northbound/yang-module-translator.rst \ doc/developer/northbound/yang-tools.rst \ + doc/developer/sbfd.rst \ # end EXTRA_DIST += \ diff --git a/doc/user/sbfd.rst b/doc/user/sbfd.rst new file mode 100644 index 000000000000..390d82a6c021 --- /dev/null +++ b/doc/user/sbfd.rst @@ -0,0 +1,304 @@ +.. _sbfd: + +**** +SBFD +**** + +:abbr:`SBFD (Seamless Bidirectional Forwarding Detection)` is: + + Seamless Bidirectional Forwarding Detection, a simplified mechanism for using BFD with a large + proportion of negotiation aspects eliminated, thus providing benefits + such as quick provisioning, as well as improved control and + flexibility for network nodes initiating path monitoring. + + -- :rfc:`7880` + +It is described and extended by the following RFCs: + +* :rfc:`7880` +* :rfc:`7881` + +.. _sbfd-sate-machine: + +SBFD state machine +================== + +SBFD takes the same data packet format as BFD, but with a much simpler state machine. +According to RFC7880, SBFD has a stateless SBFDReflector and a stateful SBFDInitiator with the state machine as below: + +:: + + +--+ + ADMIN DOWN, | | + TIMER | V + +------+ UP +------+ + | |-------------------->| |----+ + | DOWN | | UP | | UP + | |<--------------------| |<---+ + +------+ ADMIN DOWN, +------+ + TIMER + + Figure 1: SBFDInitiator Finite State Machine + +* If SBFDInitiator doesn't receive the response packet in time, session is DOWN. +* If SBFDInitiator receives the response packet in time: reponse state is ADMINDOWN, session goes DOWN; reponse state is UP, session goes UP. + +.. note:: + + SBFDReflector is stateless, it just transmit a packet in response to a received S-BFD packet having a valid S-BFD Discriminator in the Your Discriminator field. + + +.. _sbfd-extention: + +SBFD extension - SRv6 encapsulation +=================================== + +SBFDInitiator periodically send packets to monitor the connection to SBFDReflector. We set up an SBFD connection between the source and the destination node of a path, +with the source node serving as Initiator and the destination node as Reflector. The communicated SBFD packets should also follow every exact hop in the path, +from the source to the destination, which could be achieved by segment routing. This requirement extends the node verification to the path verification. +In the following example, we set up a sbfd session to monitor the path A-B-D (all nodes in the topo are SRv6 ready, which can decap and forward SRv6 packets). + +:: + + +------------C-----------+ + / \ + A---------------B---------------D + ^ ^ ^ + | | | + End: 100::A End: 100::B End: 100::D + Loopback: 200::A Loopback: 200::D + BFD Discrim: 123 BFD Discrim: 456 + + +A is the SBFDInitiator, and D is the SBFDReflector, A will trasmit the SBFD packet to B as the format: + +:: + IPv6(src="200::A", dst="100::B", nh=43)/IPv6ExtHdrSegmentRouting(addresses=["100::D"], nh=41, segleft=1)/IPv6(src="200::A", dst="200::D")/UDP(dport=7784)/BFD(my_dis=123, your_disc=456, state=UP) + + +Upon receiving the packet, B will take the Srv6 End action since the dst ip 100::B is the End address, B will the shift the dst address according to Srv6 spec, then trasmit the SBFD packet to D as the format: + +:: + IPv6(src="200::A", dst="100::D", nh=41)/IPv6(src="200::A", dst="200::D")/UDP(dport=7784)/BFD(my_dis=123, your_disc=456, state=UP) + + +After D receive the packet, It will decap the outer IPv6 header since the dst ip 100::D is the End address, the decapped packet is: + +:: + IPv6(src="200::A", dst="200::D")/UDP(dport=7784)/BFD(my_dis=123, your_disc=456, state=UP) + + +This packet will be routed to kernel stack of D since its dst is 200::D. Then the SBFDReflector service on D will get the packet and Reflect it. The response packet will be: + +:: + IPv6(src="200::D", dst="200::A")/UDP(sport=7784)/BFD(my_dis=456, your_disc=123, state=UP) + + +This packet will be routed in the topo according to the dst ip 200::A, it will go back to A by D-B-A or D-C-A in this case. + + + + In this example, Command used to configure the SBFDInitiator on A is: + +.. clicmd:: peer 200::D bfd-mode sbfd-init bfd-name a-b-d multihop local-address 200::A remote-discr 456 srv6-source-ipv6 200::A srv6-encap-data 100::B 100::D + + + Command used to configure the SBFDReflector on D is: + +.. clicmd:: sbfd reflector source-address 200::D discriminator 456 + + +.. _sbfd-echo: + +Echo SBFD with SRv6 encapsulation +================================= + +The SBFD Initiator-Reflector mode requires the configuration on both source and destination nodes. It can not work if the remote node has no SBD feature supported, especial on some third-party devices. +The Echo SBFD can solve this kind of deployment issue since it only requires the configuration on source node. This is also known as One-Arm BFD Echo or unaffiliated BFD Echo. +For example, we use Echo SBFD session to protect Srv6 path: A-B-D + +:: + + +------------C-----------+ + / \ + A---------------B---------------D + ^ ^ ^ + | | | + End: 100::A End: 100::B End: 100::D + Loopback: 200::A Loopback: 200::D + BFD Discrim: 123 + + +A is also the SBFDInitiator, and B, C, D is Srv6 ready nodes, A will trasmit the SBFD packet to B as the format: + +:: + IPv6(src="200::A", dst="100::B", nh=43)/IPv6ExtHdrSegmentRouting(addresses=["100::D"], nh=41, segleft=1)/IPv6(src="200::A", dst="200::A")/UDP(dport=3785)/BFD(my_dis=123, your_disc=123, state=UP) + + +Upon receiving the packet, B will take the Srv6 End action since the dst ip 100::B is the End address, B will the shift the dst address according to Srv6 spec, then trasmit the SBFD packet to D as the format: + +:: + IPv6(src="200::A", dst="100::D", nh=41)/IPv6(src="200::A", dst="200::A")/UDP(dport=3785)/BFD(my_dis=123, your_disc=123, state=UP) + + +After D receive the packet, It will decap the outer IPv6 header since the dst ip 100::D is the End address, the decapped packet is: + +:: + IPv6(src="200::A", dst="200::A")/UDP(dport=3785)/BFD(my_dis=123, your_disc=123, state=UP) + + +This packet will be routed in the topo according to the dst ip 200::A, it will go back to A by D-B-A or D-C-A in this case. + + + + In this example, Command used to configure the SBFDInitiator on A is: + +.. clicmd:: peer 200::A bfd-mode sbfd-echo bfd-name a-b-d local-address 200::A srv6-source-ipv6 200::A srv6-encap-data 100::B 100::D + + + no configuration needed on D. + + +.. _sbfd-normal: + +normal SBFD with no SRv6 encapsulation +====================================== + +We can also configure a SBFD Initiator-Reflector session based on simple IPv6/IPv4 packet, no Srv6 involved in this case. + +:: + + +------------C-----------+ + / \ + A---------------B---------------D + ^ ^ ^ + | | | + Loopback: 200::A Loopback: 200::D + BFD Discrim: 123 BFD Discrim: 456 + + + +A is the SBFDInitiator, and D is the SBFDReflector, A will trasmit the SBFD packet to B or C as the format: + +:: + IPv6(src="200::A", dst="200::D")/UDP(dport=7784)/BFD(my_dis=123, your_disc=456, state=UP) + + +Upon receiving the packet, B/C will route the packet to D according to the dst ip 200::D. + +After D receive the packet, packet will be sent to kernel stack of D since its dst is 200::D. Then the SBFDReflector service on D will get the packet and reflect it. The response packet will be: + +:: + IPv6(src="200::D", dst="200::A")/UDP(sport=7784)/BFD(my_dis=456, your_disc=123, state=UP) + + +This packet will be routed in the topo according to the dst ip 200::A, it will go back to A by D-B-A or D-C-A in this case. + + + In this example, Command used to configure the SBFDInitiator on A is: + +.. clicmd:: peer 200::D bfd-mode sbfd-init bfd-name a-d local-address 200::A remote-discr 456 + + + Command used to configure the SBFDReflector on D is: + +.. clicmd:: sbfd reflector source-address 200::D discriminator 456 + + +.. note:: + + Currently some features are not yet implemented: + 1) SBFD in IPv4 only packet + 2) The ADMIN DOWN logic + 3) SBFD echo function in a initiator session + 4) SBFD over MPLS + + +.. _sbfd-show: + +show command +============ + +The exsiting bfd show command is also appliable to SBFD sessions, for example: +This command will show all the BFD and SBFD sessions in the bfdd: + +.. clicmd:: show bfd peers + + +:: + BFD Peers: + peer 200::D bfd-mode sbfd-init bfd-name a-d multihop local-address 200::A vrf default remote-discr 456 + ID: 1421669725 + Remote ID: 456 + Active mode + Minimum TTL: 254 + Status: up + Uptime: 5 hour(s), 48 minute(s), 39 second(s) + Diagnostics: ok + Remote diagnostics: ok + Peer Type: sbfd initiator + Local timers: + Detect-multiplier: 3 + Receive interval: 300ms + Transmission interval: 1000ms + Echo receive interval: 50ms + Echo transmission interval: disabled + Remote timers: + Detect-multiplier: - + Receive interval: - + Transmission interval: - + Echo receive interval: - + +This command will show all the BFD and SBFD session packet counters: + +.. clicmd:: show bfd peers counters + +:: + BFD Peers: + peer 200::A bfd-mode sbfd-echo bfd-name a-b-d local-address 200::A vrf default srv6-source-ipv6 200::A srv6-encap-data 100::B 100::D + Control packet input: 0 packets + Control packet output: 0 packets + Echo packet input: 23807 packets + Echo packet output: 23807 packets + Session up events: 1 + Session down events: 0 + Zebra notifications: 1 + Tx fail packet: 0 + + peer 200::D bfd-mode sbfd-init bfd-name a-d local-address 200::A vrf default remote-discr 456 + Control packet input: 25289 packets + Control packet output: 51812 packets + Echo packet input: 0 packets + Echo packet output: 0 packets + Session up events: 5 + Session down events: 4 + Zebra notifications: 9 + Tx fail packet: 0 + + +we also implemented a new show command to display BFD sessions with a bfd-name, the bfd-name is the key to search the sessioon. + +.. clicmd:: show bfd bfd-name a-b-d + +:: + BFD Peers: + peer 200::A bfd-mode sbfd-echo bfd-name a-b-d local-address 200::A vrf default srv6-source-ipv6 200::A srv6-encap-data 100::B 100::D + ID: 123 + Remote ID: 123 + Active mode + Status: up + Uptime: 5 hour(s), 39 minute(s), 34 second(s) + Diagnostics: ok + Remote diagnostics: ok + Peer Type: echo + Local timers: + Detect-multiplier: 3 + Receive interval: 300ms + Transmission interval: 300ms + Echo receive interval: 300ms + Echo transmission interval: 1000ms + Remote timers: + Detect-multiplier: - + Receive interval: - + Transmission interval: - + Echo receive interval: - diff --git a/doc/user/subdir.am b/doc/user/subdir.am index 395ce305fe94..e4e12788e70d 100644 --- a/doc/user/subdir.am +++ b/doc/user/subdir.am @@ -55,6 +55,7 @@ user_RSTFILES = \ doc/user/watchfrr.rst \ doc/user/wecmp_linkbw.rst \ doc/user/mgmtd.rst \ + doc/user/sbfd.rst \ # end EXTRA_DIST += \ diff --git a/lib/bfd.c b/lib/bfd.c index bc4b1c5b51e6..f7ae57e8e448 100644 --- a/lib/bfd.c +++ b/lib/bfd.c @@ -18,6 +18,7 @@ #include "table.h" #include "vty.h" #include "bfd.h" +#include "bfdd/bfd.h" DEFINE_MTYPE_STATIC(LIB, BFD_INFO, "BFD info"); DEFINE_MTYPE_STATIC(LIB, BFD_SOURCE, "BFD source cache"); @@ -140,14 +141,15 @@ static void bfd_source_cache_put(struct bfd_session_params *session); * bfd_get_peer_info - Extract the Peer information for which the BFD session * went down from the message sent from Zebra to clients. */ -static struct interface *bfd_get_peer_info(struct stream *s, struct prefix *dp, - struct prefix *sp, int *status, - int *remote_cbit, vrf_id_t vrf_id) +static struct interface *bfd_get_peer_info(struct stream *s, struct prefix *dp, struct prefix *sp, + int *status, int *remote_cbit, vrf_id_t vrf_id, + char *bfd_name) { unsigned int ifindex; struct interface *ifp = NULL; int plen; int local_remote_cbit; + uint8_t bfd_name_len = 0; /* * If the ifindex lookup fails the @@ -194,6 +196,13 @@ static struct interface *bfd_get_peer_info(struct stream *s, struct prefix *dp, STREAM_GETC(s, local_remote_cbit); if (remote_cbit) *remote_cbit = local_remote_cbit; + + STREAM_GETC(s, bfd_name_len); + if (bfd_name_len) { + STREAM_GET(bfd_name, s, bfd_name_len); + *(bfd_name + bfd_name_len) = 0; + } + return ifp; stream_failure: @@ -365,6 +374,7 @@ int zclient_bfd_command(struct zclient *zc, struct bfd_session_arg *args) stream_putc(s, args->profilelen); if (args->profilelen) stream_put(s, args->profile, args->profilelen); + #else /* PTM BFD */ /* Encode timers if this is a registration message. */ if (args->command != ZEBRA_BFD_DEST_DEREGISTER) { @@ -742,6 +752,7 @@ void bfd_sess_install(struct bfd_session_params *bsp) event_add_event(bsglobal.tm, _bfd_sess_send, bsp, 0, &bsp->installev); } + void bfd_sess_uninstall(struct bfd_session_params *bsp) { bsp->lastev = BSE_UNINSTALL; @@ -918,6 +929,7 @@ int zclient_bfd_session_update(ZAPI_CALLBACK_ARGS) struct prefix dp; struct prefix sp; char ifstr[128], cbitstr[32]; + char bfd_name[BFD_NAME_SIZE + 1] = { 0 }; if (!zclient->bfd_integration) return 0; @@ -926,8 +938,7 @@ int zclient_bfd_session_update(ZAPI_CALLBACK_ARGS) if (bsglobal.shutting_down) return 0; - ifp = bfd_get_peer_info(zclient->ibuf, &dp, &sp, &state, &remote_cbit, - vrf_id); + ifp = bfd_get_peer_info(zclient->ibuf, &dp, &sp, &state, &remote_cbit, vrf_id, bfd_name); /* * When interface lookup fails or an invalid stream is read, we must * not proceed otherwise it will trigger an assertion while checking diff --git a/lib/bfd.h b/lib/bfd.h index 99790f96a54b..07d4c9781dbe 100644 --- a/lib/bfd.h +++ b/lib/bfd.h @@ -26,6 +26,8 @@ extern "C" { #define BFD_PROFILE_NAME_LEN 64 +#define BFD_NAME_SIZE 255 + const char *bfd_get_status_str(int status); extern void bfd_client_sendmsg(struct zclient *zclient, int command, @@ -409,6 +411,8 @@ struct bfd_session_arg { uint32_t min_tx; /** Detection multiplier. */ uint32_t detection_multiplier; + /* bfd session name*/ + char bfd_name[BFD_NAME_SIZE + 1]; }; /** diff --git a/tests/topotests/sbfd_topo1/__init__.py b/tests/topotests/sbfd_topo1/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/topotests/sbfd_topo1/r1/bfdd.conf b/tests/topotests/sbfd_topo1/r1/bfdd.conf new file mode 100644 index 000000000000..437f063d8f1a --- /dev/null +++ b/tests/topotests/sbfd_topo1/r1/bfdd.conf @@ -0,0 +1,7 @@ +! +debug bfd network +debug bfd peer +debug bfd zebra +! +bfd +! diff --git a/tests/topotests/sbfd_topo1/r1/zebra.conf b/tests/topotests/sbfd_topo1/r1/zebra.conf new file mode 100644 index 000000000000..5f19bd87aa2a --- /dev/null +++ b/tests/topotests/sbfd_topo1/r1/zebra.conf @@ -0,0 +1,7 @@ +ip forwarding +ipv6 forwarding +! + +interface r1-eth0 + ipv6 address 2001::10/64 +! diff --git a/tests/topotests/sbfd_topo1/r2/bfdd.conf b/tests/topotests/sbfd_topo1/r2/bfdd.conf new file mode 100644 index 000000000000..437f063d8f1a --- /dev/null +++ b/tests/topotests/sbfd_topo1/r2/bfdd.conf @@ -0,0 +1,7 @@ +! +debug bfd network +debug bfd peer +debug bfd zebra +! +bfd +! diff --git a/tests/topotests/sbfd_topo1/r2/zebra.conf b/tests/topotests/sbfd_topo1/r2/zebra.conf new file mode 100644 index 000000000000..0c6ee75f705d --- /dev/null +++ b/tests/topotests/sbfd_topo1/r2/zebra.conf @@ -0,0 +1,7 @@ +ip forwarding +ipv6 forwarding +! + +interface r2-eth0 + ipv6 address 2001::20/64 +! diff --git a/tests/topotests/sbfd_topo1/sbfd_topo1.dot b/tests/topotests/sbfd_topo1/sbfd_topo1.dot new file mode 100644 index 000000000000..437e8230ce20 --- /dev/null +++ b/tests/topotests/sbfd_topo1/sbfd_topo1.dot @@ -0,0 +1,45 @@ +## Color coding: +######################### +## Main FRR: #f08080 red +## Switches: #d0e0d0 gray +## RIP: #19e3d9 Cyan +## RIPng: #fcb314 dark yellow +## OSPFv2: #32b835 Green +## OSPFv3: #19e3d9 Cyan +## ISIS IPv4 #fcb314 dark yellow +## ISIS IPv6 #9a81ec purple +## BGP IPv4 #eee3d3 beige +## BGP IPv6 #fdff00 yellow +##### Colors (see http://www.color-hex.com/) + +graph template { + label="template"; + + # Routers + r1 [ + shape=doubleoctagon, + label="A\nAS 100\n1.1.1.1", + fillcolor="#f08080", + style=filled, + ]; + r2 [ + shape=doubleoctagon + label="B\nAS 200\n1.1.1.2", + fillcolor="#f08080", + style=filled, + ]; + + # Switches + s1 [ + shape=oval, + label="s1\n192.168.0.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + + + # Connections + r1 -- s1 [label="A-eth0"]; + r2 -- s1 [label="B-eth0"]; + +} diff --git a/tests/topotests/sbfd_topo1/test_sbfd_topo1.py b/tests/topotests/sbfd_topo1/test_sbfd_topo1.py new file mode 100644 index 000000000000..b60cc89fdc62 --- /dev/null +++ b/tests/topotests/sbfd_topo1/test_sbfd_topo1.py @@ -0,0 +1,237 @@ +#!/usr/bin/env python + +# +# test_sbfd_topo1.py +# basic test cases for sbfd initiator and reflector +# +# Copyright (c) 2025 by Alibaba, Inc. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +