diff options
41 files changed, 4893 insertions, 183 deletions
diff --git a/bfdd/bfd.c b/bfdd/bfd.c index f32bc2598b..164910556b 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) @@ -222,10 +229,11 @@ void bfd_profile_remove(struct bfd_session *bs)  	bfd_session_apply(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) +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 *bfdname)  { +	struct vrf *vrf = NULL; +  	memset(key, 0, sizeof(*key));  	switch (peer->sa_sin.sin_family) { @@ -248,10 +256,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) @@ -259,8 +277,8 @@ struct bfd_session *bs_peer_find(struct bfd_peer_cfg *bpc)  	struct bfd_key key;  	/* 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); +	gen_bfd_key(&key, &bpc->bpc_peer, &bpc->bpc_local, bpc->bpc_mhop, bpc->bpc_localif, +		    bpc->bpc_vrfname, bpc->bfd_name);  	return bfd_key_lookup(key);  } @@ -333,14 +351,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 +379,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 +419,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 +460,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 +502,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 +627,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 +767,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 +788,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 +821,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 +834,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 +1014,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 +1196,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 +1271,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 +1318,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 +1472,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 +1772,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 +1852,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 +1907,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 +1933,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 +1971,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 +2008,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 +2028,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 +2049,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 +2078,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 +2100,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 +2116,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 +2128,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 +2140,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; @@ -1751,14 +2156,15 @@ 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) +const struct bfd_session *bfd_session_next(const struct bfd_session *bs, 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 +2330,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 +2364,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 +2379,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 +2417,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 +2426,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 +2463,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 d4d14ffce6..d9119d16c2 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, @@ -598,16 +644,16 @@ void bs_observer_del(struct bfd_session_observer *bso);  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); +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 *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); +const struct bfd_session *bfd_session_next(const struct bfd_session *bs, 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 +699,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 +724,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 +864,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 f9397fa128..cecced3ccc 100644 --- a/bfdd/bfd_packet.c +++ b/bfdd/bfd_packet.c @@ -16,20 +16,28 @@  #ifdef GNU_LINUX  #include <linux/filter.h> +#include <linux/seg6.h> +#include <linux/ipv6.h>  #endif  #ifdef BFD_LINUX  #include <linux/if_packet.h> +#include <linux/seg6.h> +#include <linux/ipv6.h>  #endif /* BFD_LINUX */  #include <netinet/if_ether.h>  #include <netinet/udp.h> +#include <netinet/ip6.h> +#include <ifaddrs.h>  #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,12 +57,19 @@ 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, -			 uint8_t *ttl, ifindex_t *ifindex, -			 struct sockaddr_any *local, struct sockaddr_any *peer); -void bfd_peer_mac_set(int sd, struct bfd_session *bfd, -		      struct sockaddr_any *peer, struct interface *ifp); +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); +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);  ssize_t bfd_recv_fp_echo(int sd, uint8_t *msgbuf, size_t msgbuflen, @@ -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,9 +470,9 @@ 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, -			 uint8_t *ttl, ifindex_t *ifindex, -			 struct sockaddr_any *local, struct sockaddr_any *peer) +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)  {  	ssize_t mlen;  	struct sockaddr_ll msgaddr; @@ -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,8 +1808,8 @@ 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, -		      struct sockaddr_any *peer, struct interface *ifp) +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,727 @@ 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; +	} +#ifdef IPV6_HDRINCL +	int on = 1; + +	/*manage the IP6 header all on own onwn*/ +	if (setsockopt(sd, IPPROTO_IPV6, IPV6_HDRINCL, &on, sizeof(on))) { +#else +	if (true) { +#endif +		zlog_err("setsockopt IPV6_HDRINCL error: %s", strerror(errno)); +		close(sd); +		return -1; +	} + +	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 6527ec5f41..a1710ec127 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 <A.B.C.D|X:X::X:X> bfd-mode sbfd-echo bfd-name BFDNAME$bfdname \ +	[multihop$multihop] local-address <A.B.C.D|X:X::X:X> [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 <A.B.C.D|X:X::X:X> bfd-mode sbfd-echo bfd-name BFDNAME$bfdname \ +	[multihop$multihop] local-address <A.B.C.D|X:X::X:X> [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 <A.B.C.D|X:X::X:X> bfd-mode sbfd-init bfd-name BFDNAME$bfdname \ +	[multihop$multihop] local-address <A.B.C.D|X:X::X:X> [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 <A.B.C.D|X:X::X:X> bfd-mode sbfd-init bfd-name BFDNAME$bfdname \ +	[multihop$multihop] local-address <A.B.C.D|X:X::X:X> [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 <A.B.C.D|X:X::X:X> bfd-mode sbfd-init bfd-name BFDNAME$bfdname \ +	[multihop$multihop] local-address <A.B.C.D|X:X::X:X> [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 <A.B.C.D|X:X::X:X> bfd-mode sbfd-init bfd-name BFDNAME$bfdname \ +	[multihop$multihop] local-address <A.B.C.D|X:X::X:X> [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 114fbc2bdd..f60d8397bb 100644 --- a/bfdd/bfdd_nb.c +++ b/bfdd/bfdd_nb.c @@ -484,6 +484,459 @@ const struct frr_yang_module_info frr_bfdd_info = {  			}  		},  		{ +			.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 b5b00b57e4..6621973ae3 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 48fbe7139c..15da1e2440 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 <ifaddrs.h>  /*   * 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);  }  /* @@ -715,6 +904,45 @@ int bfdd_bfd_sessions_single_hop_passive_mode_modify(  }  /* + * 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   */  int bfdd_bfd_sessions_single_hop_echo_mode_modify( @@ -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 12acda8fd8..c528478231 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 26554e1496..c281197849 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 e797e71f05..e5f43b6cc6 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 f6ebefb7be..74f2d39878 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/bgpd/bgp_main.c b/bgpd/bgp_main.c index 9ca20c949a..9dbef791b0 100644 --- a/bgpd/bgp_main.c +++ b/bgpd/bgp_main.c @@ -327,7 +327,7 @@ static int bgp_vrf_disable(struct vrf *vrf)  	if (BGP_DEBUG(zebra, ZEBRA))  		zlog_debug("VRF disable %s id %d", vrf->name, vrf->vrf_id); -	bgp = bgp_lookup_by_name(vrf->name); +	bgp = bgp_lookup_by_name_filter(vrf->name, false);  	if (bgp) {  		vpn_leak_zebra_vrf_label_withdraw(bgp, AFI_IP); diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index f2e61e1e7f..e932738cd4 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -3885,6 +3885,12 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_dest *dest,  					 BGP_PATH_ATTR_CHANGED);  		UNSET_FLAG(new_select->flags, BGP_PATH_MULTIPATH_CHG);  		UNSET_FLAG(new_select->flags, BGP_PATH_LINK_BW_CHG); +	} else { +		/* +		 * Ensure that on uninstall that the INSTALL_PENDING +		 * is no longer set +		 */ +		UNSET_FLAG(dest->flags, BGP_NODE_FIB_INSTALL_PENDING);  	}  	/* call bmp hook for loc-rib route update / withdraw after flags were diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 046b18f224..98021f7ef4 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -122,6 +122,12 @@ FRR_CFG_DEFAULT_BOOL(BGP_ENFORCE_FIRST_AS,  	{ .val_bool = false, .match_version = "< 9.1", },  	{ .val_bool = true },  ); +FRR_CFG_DEFAULT_BOOL(BGP_RR_ALLOW_OUTBOUND_POLICY, +	{ .val_bool = false }, +); +FRR_CFG_DEFAULT_BOOL(BGP_COMPARE_AIGP, +	{ .val_bool = false }, +);  DEFINE_HOOK(bgp_inst_config_write,  		(struct bgp *bgp, struct vty *vty), @@ -622,6 +628,10 @@ int bgp_get_vty(struct bgp **bgp, as_t *as, const char *name,  				 BGP_FLAG_DYNAMIC_CAPABILITY);  		if (DFLT_BGP_ENFORCE_FIRST_AS)  			SET_FLAG((*bgp)->flags, BGP_FLAG_ENFORCE_FIRST_AS); +		if (DFLT_BGP_RR_ALLOW_OUTBOUND_POLICY) +			SET_FLAG((*bgp)->flags, BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY); +		if (DFLT_BGP_COMPARE_AIGP) +			SET_FLAG((*bgp)->flags, BGP_FLAG_COMPARE_AIGP);  		ret = BGP_SUCCESS;  	} @@ -16681,9 +16691,9 @@ static int bgp_show_route_leak_vty(struct vty *vty, const char *name,  						json_object_new_string(vname));  			json_object_object_add(json, "exportToVrfs",  					       json_export_vrfs); -			json_object_string_addf( -				json, "routeDistinguisher", "%s", -				bgp->vpn_policy[afi].tovpn_rd_pretty); +			json_object_string_addf(json, "routeDistinguisher", +						BGP_RD_AS_FORMAT(bgp->asnotation), +						&bgp->vpn_policy[afi].tovpn_rd);  			dir = BGP_VPN_POLICY_DIR_TOVPN;  			if (bgp->vpn_policy[afi].rtlist[dir]) {  				ecom_str = ecommunity_ecom2str( @@ -19780,14 +19790,19 @@ int bgp_config_write(struct vty *vty)  			}  		} -		if (CHECK_FLAG(bgp->flags, BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY)) { -			vty_out(vty, -				" bgp route-reflector allow-outbound-policy\n"); -		} +		if (!!CHECK_FLAG(bgp->flags, BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY) != +		    SAVE_BGP_RR_ALLOW_OUTBOUND_POLICY) +			vty_out(vty, " %sbgp route-reflector allow-outbound-policy\n", +				CHECK_FLAG(bgp->flags, BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY) ? "" +											  : "no "); +  		if (CHECK_FLAG(bgp->flags, BGP_FLAG_COMPARE_ROUTER_ID))  			vty_out(vty, " bgp bestpath compare-routerid\n"); -		if (CHECK_FLAG(bgp->flags, BGP_FLAG_COMPARE_AIGP)) -			vty_out(vty, " bgp bestpath aigp\n"); + +		if (!!CHECK_FLAG(bgp->flags, BGP_FLAG_COMPARE_AIGP) != SAVE_BGP_COMPARE_AIGP) +			vty_out(vty, " %sbgp bestpath aigp\n", +				CHECK_FLAG(bgp->flags, BGP_FLAG_COMPARE_AIGP) ? "" : "no "); +  		if (CHECK_FLAG(bgp->flags, BGP_FLAG_MED_CONFED)  		    || CHECK_FLAG(bgp->flags, BGP_FLAG_MED_MISSING_AS_WORST)) {  			vty_out(vty, " bgp bestpath med"); diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index edf90d3dd8..efb2c00fa5 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -3988,6 +3988,7 @@ int bgp_delete(struct bgp *bgp)  	uint32_t a_ann_cnt = 0, a_l2_cnt = 0, a_l3_cnt = 0;  	struct bgp *bgp_to_proc = NULL;  	struct bgp *bgp_to_proc_next = NULL; +	struct bgp *bgp_default = bgp_get_default();  	assert(bgp); @@ -4041,13 +4042,26 @@ int bgp_delete(struct bgp *bgp)  	bgp_soft_reconfig_table_task_cancel(bgp, NULL, NULL);  	/* make sure we withdraw any exported routes */ -	vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, AFI_IP, bgp_get_default(), -			   bgp); -	vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, AFI_IP6, bgp_get_default(), -			   bgp); +	vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, AFI_IP, bgp_default, bgp); +	vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, AFI_IP6, bgp_default, bgp);  	bgp_vpn_leak_unimport(bgp); +	/* +	 * Release SRv6 SIDs, like it's done in `vpn_leak_postchange()` +	 * and bgp_sid_vpn_export_cmd/af_sid_vpn_export_cmd commands. +	 */ +	bgp->tovpn_sid_index = 0; +	UNSET_FLAG(bgp->vrf_flags, BGP_VRF_TOVPN_SID_AUTO); +	delete_vrf_tovpn_sid_per_vrf(bgp_default, bgp); +	for (afi = AFI_IP; afi < AFI_MAX; afi++) { +		bgp->vpn_policy[afi].tovpn_sid_index = 0; +		UNSET_FLAG(bgp->vpn_policy[afi].flags, BGP_VPN_POLICY_TOVPN_SID_AUTO); +		delete_vrf_tovpn_sid_per_af(bgp_default, bgp, afi); + +		vpn_leak_zebra_vrf_sid_withdraw(bgp, afi); +	} +  	bgp_vpn_release_label(bgp, AFI_IP, true);  	bgp_vpn_release_label(bgp, AFI_IP6, true); diff --git a/doc/developer/sbfd.rst b/doc/developer/sbfd.rst new file mode 100644 index 0000000000..7bbd2428dd --- /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 bdf93a05c1..9f6986cd33 100644 --- a/doc/developer/subdir.am +++ b/doc/developer/subdir.am @@ -83,6 +83,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 0000000000..390d82a6c0 --- /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 395ce305fe..e4e12788e7 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 += \ @@ -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: @@ -918,6 +927,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 +936,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 @@ -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/lib/event.c b/lib/event.c index d95b3021a7..6081ba4727 100644 --- a/lib/event.c +++ b/lib/event.c @@ -111,6 +111,11 @@ static struct cpu_event_history *cpu_records_get(struct event_loop *loop,  	return res;  } +static void cpu_records_clear(struct cpu_event_history *p) +{ +	memset(p->_clear_begin, 0, p->_clear_end - p->_clear_begin); +} +  static void cpu_records_free(struct cpu_event_history **p)  {  	XFREE(MTYPE_EVENT_STATS, *p); @@ -250,20 +255,15 @@ static void cpu_record_clear(uint8_t filter)  		for (ALL_LIST_ELEMENTS_RO(masters, ln, m)) {  			frr_with_mutex (&m->mtx) {  				struct cpu_event_history *item; -				struct cpu_records_head old[1]; -				cpu_records_init(old); -				cpu_records_swap_all(old, m->cpu_records); - -				while ((item = cpu_records_pop(old))) { +				/* it isn't possible to free the memory here +				 * because some of these will be in use (e.g. +				 * the one we're currently running in!) +				 */ +				frr_each (cpu_records, m->cpu_records, item) {  					if (item->types & filter) -						cpu_records_free(&item); -					else -						cpu_records_add(m->cpu_records, -								item); +						cpu_records_clear(item);  				} - -				cpu_records_fini(old);  			}  		}  	} diff --git a/lib/frrevent.h b/lib/frrevent.h index 44776b29a7..c35b39a147 100644 --- a/lib/frrevent.h +++ b/lib/frrevent.h @@ -139,6 +139,10 @@ struct cpu_event_history {  	struct cpu_records_item item;  	void (*func)(struct event *e); + +	/* fields between the pair of these two are nulled on "clear event cpu" */ +	char _clear_begin[0]; +  	atomic_size_t total_cpu_warn;  	atomic_size_t total_wall_warn;  	atomic_size_t total_starv_warn; @@ -149,6 +153,10 @@ struct cpu_event_history {  	} real;  	struct time_stats cpu;  	atomic_uint_fast32_t types; + +	/* end of cleared region */ +	char _clear_end[0]; +  	const char *funcname;  }; diff --git a/lib/privs.c b/lib/privs.c index b0809bf690..e7df383e5d 100644 --- a/lib/privs.c +++ b/lib/privs.c @@ -210,10 +210,11 @@ int zprivs_change_caps(zebra_privs_ops_t op)  {  	cap_flag_value_t cflag; -	/* should be no possibility of being called without valid caps */ -	assert(zprivs_state.syscaps_p && zprivs_state.caps); -	if (!(zprivs_state.syscaps_p && zprivs_state.caps)) -		exit(1); +	/* Called without valid caps - just return. Not every daemon needs +	 * privs. +	 */ +	if (zprivs_state.syscaps_p == NULL || zprivs_state.caps == NULL) +		return 0;  	if (op == ZPRIVS_RAISE)  		cflag = CAP_SET; diff --git a/staticd/static_vty.c b/staticd/static_vty.c index ed2805d3ea..f93e81e8dc 100644 --- a/staticd/static_vty.c +++ b/staticd/static_vty.c @@ -1273,8 +1273,8 @@ DEFPY_YANG(no_srv6_sid, no_srv6_sid_cmd,  {  	char xpath[XPATH_MAXLEN + 37]; -	snprintf(xpath, sizeof(xpath), FRR_STATIC_SRV6_INFO_KEY_XPATH, "frr-staticd:staticd", -		 "staticd", VRF_DEFAULT_NAME); +	snprintf(xpath, sizeof(xpath), FRR_STATIC_SRV6_SID_KEY_XPATH, "frr-staticd:staticd", +		 "staticd", VRF_DEFAULT_NAME, sid_str);  	nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); diff --git a/staticd/static_zebra.c b/staticd/static_zebra.c index 057193aa08..6da2dfec90 100644 --- a/staticd/static_zebra.c +++ b/staticd/static_zebra.c @@ -1169,6 +1169,19 @@ static int static_zebra_srv6_sid_notify(ZAPI_CALLBACK_ARGS)  		DEBUGD(&static_dbg_srv6, "%s: SRv6 SID %pI6 %s: RELEASED", __func__, &sid_addr,  		       srv6_sid_ctx2str(buf, sizeof(buf), &ctx)); +		for (ALL_LIST_ELEMENTS_RO(srv6_sids, node, sid)) { +			if (IPV6_ADDR_SAME(&sid->addr.prefix, &sid_addr)) { +				found = true; +				break; +			} +		} + +		if (!found || !sid) { +			zlog_err("SRv6 SID %pI6 %s: not found", &sid_addr, +				 srv6_sid_ctx2str(buf, sizeof(buf), &ctx)); +			return 0; +		} +  		UNSET_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_VALID);  		break; diff --git a/tests/topotests/bgp_srv6_sid_reachability/test_bgp_srv6_sid_reachability.py b/tests/topotests/bgp_srv6_sid_reachability/test_bgp_srv6_sid_reachability.py index cf590ad01d..5c0b909517 100755 --- a/tests/topotests/bgp_srv6_sid_reachability/test_bgp_srv6_sid_reachability.py +++ b/tests/topotests/bgp_srv6_sid_reachability/test_bgp_srv6_sid_reachability.py @@ -159,6 +159,36 @@ def test_sid_reachable_again_bgp_update():      check_ping("c11", "192.168.2.1", True, 10, 1) +def test_sid_unreachable_no_router(): +    get_topogen().gears["r2"].vtysh_cmd( +        """ +        configure terminal +        no router bgp 65002 vrf vrf10 +        """ +    ) +    check_ping("c11", "192.168.2.1", False, 10, 1) + + +def test_sid_reachable_again_no_router(): +    get_topogen().gears["r2"].vtysh_cmd( +        """ +        configure terminal +        router bgp 65002 vrf vrf10 +        bgp router-id 192.0.2.2 +        ! +         address-family ipv4 unicast +          redistribute connected +          sid vpn export 1 +          rd vpn export 65002:10 +          rt vpn both 0:10 +          import vpn +          export vpn +         exit-address-family +        """ +    ) +    check_ping("c11", "192.168.2.1", True, 10, 1) + +  if __name__ == "__main__":      args = ["-s"] + sys.argv[1:]      sys.exit(pytest.main(args)) diff --git a/tests/topotests/sbfd_topo1/__init__.py b/tests/topotests/sbfd_topo1/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/sbfd_topo1/__init__.py diff --git a/tests/topotests/sbfd_topo1/r1/frr.conf b/tests/topotests/sbfd_topo1/r1/frr.conf new file mode 100644 index 0000000000..f865b81303 --- /dev/null +++ b/tests/topotests/sbfd_topo1/r1/frr.conf @@ -0,0 +1,8 @@ +ip forwarding +ipv6 forwarding +! + +interface r1-eth0 + ipv6 address 2001::10/64 +! +! diff --git a/tests/topotests/sbfd_topo1/r2/frr.conf b/tests/topotests/sbfd_topo1/r2/frr.conf new file mode 100644 index 0000000000..c9d166cabb --- /dev/null +++ b/tests/topotests/sbfd_topo1/r2/frr.conf @@ -0,0 +1,8 @@ +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 0000000000..437e8230ce --- /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 0000000000..e20902ebf5 --- /dev/null +++ b/tests/topotests/sbfd_topo1/test_sbfd_topo1.py @@ -0,0 +1,248 @@ +#!/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. +# + +""" +<template>.py: Test <template>. +""" + +import os +import sys +import pytest +import json +import re +import time +import pdb +from functools import partial + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, '../')) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from lib.common_config import required_linux_kernel_version + +""" +test_sbfd_topo1.py: test simple sbfd with IPv6 encap. RT1 is sbfd Initiator, RT2 is sbfd Reflector + + +----+----+        +----+----+ + |         |        |         | + |   RT1   |   1    |   RT2   | + |         +--------+         | + | 2001::10|        | 2001::20| + +----+----+        +----+----+ + +""" +pytestmark = [pytest.mark.bfdd, pytest.mark.sbfd] + +def show_bfd_check(router, status, type='echo', encap=None): +    output = router.cmd("vtysh -c 'show bfd peers'") +    if encap: +        # check encap data if any +        pattern1 = re.compile(r'encap-data {}'.format(encap)) +        ret = pattern1.findall(output) +        if len(ret) <= 0: +            logger.info("encap-data not match") +            return False + +    # check  status +    pattern2 = re.compile(r'Status: {}'.format(status)) +    ret = pattern2.findall(output) +    if len(ret) <= 0: +        logger.info("Status not match") +        return False + +    # check type +    pattern3 = re.compile(r'Peer Type: {}'.format(type)) +    ret = pattern3.findall(output) +    if len(ret) <= 0: +        logger.info("Peer Type not match") +        return False + +    logger.info("all check passed") +    return True + +def build_topo(tgen): +    "Test topology builder" + +    # This function only purpose is to define allocation and relationship +    # between routers, switches and hosts. +    # +    # Example +    # +    # Create 2 routers +    for routern in range(1, 3): +        tgen.add_router('r{}'.format(routern)) + +    # Create a switch with just one router connected to it to simulate a +    # empty network. +    switch = tgen.add_switch('s1') +    switch.add_link(tgen.gears['r1']) +    switch.add_link(tgen.gears['r2']) + +def setup_module(mod): +    "Sets up the pytest environment" +    # This function initiates the topology build with Topogen... +    tgen = Topogen(build_topo, mod.__name__) +    # ... and here it calls Mininet initialization functions. +    tgen.start_topology() + +    # This is a sample of configuration loading. +    router_list = tgen.routers() + +    for rname, router in router_list.items(): +        router.load_frr_config( +            os.path.join(CWD, "{}/frr.conf".format(rname)), +            [(TopoRouter.RD_ZEBRA, None), (TopoRouter.RD_BFD, None)]) + +    # After loading the configurations, this function loads configured daemons. +    tgen.start_router() + +    # Verify that we are using the proper version and that the BFD +    # daemon exists. +    for router in router_list.values(): +        # Check for Version +        if router.has_version('<', '5.1'): +            tgen.set_error('Unsupported FRR version') +            break + +def teardown_module(mod): +    "Teardown the pytest environment" +    tgen = get_topogen() +    # This function tears down the whole topology. +    tgen.stop_topology() + + +# step 1 : config sbfd Initiator and reflector +def test_sbfd_config_check(): +    "Assert that config sbfd and check sbfd status." +    # Required linux kernel version for this suite to run. +    result = required_linux_kernel_version("4.5") +    if result is not True: +        pytest.skip("Kernel requirements are not met") + +    tgen = get_topogen() +    if tgen.routers_have_failure(): +        pytest.skip(tgen.errors) + +    # config sbfd +    r1 = tgen.net['r1'] +    r1.cmd("ping -c 5 2001::20") +    r1.cmd("vtysh -c 'config t' -c 'bfd' -c 'peer 2001::20 bfd-mode sbfd-init bfd-name 2-44 local-address 2001::10 remote-discr 1234'") + +    r2 = tgen.net['r2'] +    r2.cmd("vtysh -c 'config t' -c 'bfd' -c 'sbfd reflector source-address 2001::20 discriminator 1234'") + +    check_func = partial( +        show_bfd_check, r1, 'up', type='sbfd initiator' +    ) +    success, _ = topotest.run_and_expect(check_func, True, count=15, wait=1) +    assert success is True, "sbfd not up in 15 seconds" + +# step 2: shutdown if and no shutdown if then check sbfd status +def test_sbfd_updown_interface(): +    "Assert that updown interface then check sbfd status." +    # Required linux kernel version for this suite to run. +    result = required_linux_kernel_version("4.5") +    if result is not True: +        pytest.skip("Kernel requirements are not met") + +    tgen = get_topogen() +    if tgen.routers_have_failure(): +        pytest.skip(tgen.errors) + +    r1 = tgen.net['r1'] +    r2 = tgen.net['r2'] + +    # shutdown interface +    r2.cmd("vtysh -c 'config t' -c 'interface r2-eth0' -c 'shutdown'") + +    check_func = partial( +        show_bfd_check, r1, 'down', type='sbfd initiator' +    ) +    success, _ = topotest.run_and_expect(check_func, True, count=15, wait=1) +    assert success is True, "sbfd not down in 15 seconds after shut" + +    # up interface +    r2.cmd("vtysh -c 'config t' -c 'interface r2-eth0' -c 'no shutdown'") +    check_func = partial( +        show_bfd_check, r1, 'up', type='sbfd initiator' +    ) +    success, _ = topotest.run_and_expect(check_func, True, count=15, wait=1) +    assert success is True, "sbfd not up in 15 seconds after no shut" + +# step 3: change transmit-interval and check sbfd status according to the interval time +def test_sbfd_change_transmit_interval(): +    "Assert that sbfd status changes align with transmit-interval." +    # Required linux kernel version for this suite to run. +    result = required_linux_kernel_version("4.5") +    if result is not True: +        pytest.skip("Kernel requirements are not met") + +    tgen = get_topogen() +    if tgen.routers_have_failure(): +        pytest.skip(tgen.errors) + +    r1 = tgen.net['r1'] +    r2 = tgen.net['r2'] + +    r1.cmd("vtysh -c 'config t' -c 'bfd' -c 'peer 2001::20 bfd-mode sbfd-init bfd-name 2-44 local-address 2001::10 remote-discr 1234' -c 'transmit-interval 3000'") +    #wait sometime for polling finish +    time.sleep(1) + +    # shutdown interface +    r2.cmd("vtysh -c 'config t' -c 'interface r2-eth0' -c 'shutdown'") + +    #wait enough time for timeout +    check_func = partial( +        show_bfd_check, r1, 'down', type='sbfd initiator' +    ) +    success, _ = topotest.run_and_expect(check_func, True, count=5, wait=3) +    assert success is True, "sbfd not down as expected" + +    r2.cmd("vtysh -c 'config t' -c 'interface r2-eth0' -c 'no shutdown'") +    check_func = partial( +        show_bfd_check, r1, 'up', type='sbfd initiator' +    ) +    success, _ = topotest.run_and_expect(check_func, True, count=15, wait=1) +    assert success is True, "sbfd not up in 15 seconds after no shut" + +    r1.cmd("vtysh -c 'config t' -c 'bfd' -c 'no peer 2001::20 bfd-mode sbfd-init bfd-name 2-44 local-address 2001::10 remote-discr 1234'") +    success = show_bfd_check(r1, 'up', type='sbfd initiator') +    assert success is False, "sbfd not deleted as unexpected" + +# Memory leak test template +def test_memory_leak(): +    "Run the memory leak test and report results." +    tgen = get_topogen() +    if not tgen.is_memleak_enabled(): +        pytest.skip('Memory leak test/report is disabled') + +    tgen.report_memory_leaks() + +if __name__ == '__main__': +    args = ["-s"] + sys.argv[1:] +    sys.exit(pytest.main(args)) diff --git a/tests/topotests/static_srv6_sids/expected_srv6_sids.json b/tests/topotests/static_srv6_sids/expected_srv6_sids.json index e1a2a16afe..5799d97988 100644 --- a/tests/topotests/static_srv6_sids/expected_srv6_sids.json +++ b/tests/topotests/static_srv6_sids/expected_srv6_sids.json @@ -1,4 +1,39 @@  { +	"fcbb:bbbb:1::/48": [ +		{ +			"prefix": "fcbb:bbbb:1::/48", +			"prefixLen": 48, +			"protocol": "static", +			"vrfId": 0, +			"vrfName": "default", +			"selected": true, +			"destSelected": true, +			"distance": 1, +			"metric": 0, +			"installed": true, +			"table": 254, +			"internalStatus": 16, +			"internalFlags": 9, +			"internalNextHopNum": 1, +			"internalNextHopActiveNum": 1, +			"nexthops": [ +				{ +					"flags": 3, +					"fib": true, +					"directlyConnected": true, +					"interfaceName": "sr0", +					"active": true, +					"weight": 1, +					"seg6local": { +						"action": "End" +					}, +					"seg6localContext": { + +					} +				} +			] +		} +	],  	"fcbb:bbbb:1:fe10::/64": [  		{  			"prefix": "fcbb:bbbb:1:fe10::/64", diff --git a/tests/topotests/static_srv6_sids/expected_srv6_sids_sid_delete_1.json b/tests/topotests/static_srv6_sids/expected_srv6_sids_sid_delete_1.json new file mode 100644 index 0000000000..e1a2a16afe --- /dev/null +++ b/tests/topotests/static_srv6_sids/expected_srv6_sids_sid_delete_1.json @@ -0,0 +1,107 @@ +{ +	"fcbb:bbbb:1:fe10::/64": [ +		{ +			"prefix": "fcbb:bbbb:1:fe10::/64", +			"prefixLen": 64, +			"protocol": "static", +			"vrfId": 0, +			"vrfName": "default", +			"selected": true, +			"destSelected": true, +			"distance": 1, +			"metric": 0, +			"installed": true, +			"table": 254, +			"internalStatus": 16, +			"internalFlags": 9, +			"internalNextHopNum": 1, +			"internalNextHopActiveNum": 1, +			"nexthops": [ +				{ +					"flags": 3, +					"fib": true, +					"directlyConnected": true, +					"interfaceName": "Vrf10", +					"active": true, +					"weight": 1, +					"seg6local": { +						"action": "End.DT4" +					}, +					"seg6localContext": { +						"table": 10 +					} +				} +			] +		} +	], +	"fcbb:bbbb:1:fe20::/64": [ +		{ +			"prefix": "fcbb:bbbb:1:fe20::/64", +			"prefixLen": 64, +			"protocol": "static", +			"vrfId": 0, +			"vrfName": "default", +			"selected": true, +			"destSelected": true, +			"distance": 1, +			"metric": 0, +			"installed": true, +			"table": 254, +			"internalStatus": 16, +			"internalFlags": 9, +			"internalNextHopNum": 1, +			"internalNextHopActiveNum": 1, +			"nexthops": [ +				{ +					"flags": 3, +					"fib": true, +					"directlyConnected": true, +					"interfaceName": "Vrf20", +					"active": true, +					"weight": 1, +					"seg6local": { +						"action": "End.DT6" +					}, +					"seg6localContext": { +						"table": 20 +					} +				} +			] +		} +	], +	"fcbb:bbbb:1:fe30::/64": [ +		{ +			"prefix": "fcbb:bbbb:1:fe30::/64", +			"prefixLen": 64, +			"protocol": "static", +			"vrfId": 0, +			"vrfName": "default", +			"selected": true, +			"destSelected": true, +			"distance": 1, +			"metric": 0, +			"installed": true, +			"table": 254, +			"internalStatus": 16, +			"internalFlags": 9, +			"internalNextHopNum": 1, +			"internalNextHopActiveNum": 1, +			"nexthops": [ +				{ +					"flags": 3, +					"fib": true, +					"directlyConnected": true, +					"interfaceName": "Vrf30", +					"active": true, +					"weight": 1, +					"seg6local": { +						"action": "End.DT46" +					}, +					"seg6localContext": { +						"table": 30 +					} +				} +			] +		} +	] +}
\ No newline at end of file diff --git a/tests/topotests/static_srv6_sids/expected_srv6_sids_sid_delete_2.json b/tests/topotests/static_srv6_sids/expected_srv6_sids_sid_delete_2.json new file mode 100644 index 0000000000..b5801d354b --- /dev/null +++ b/tests/topotests/static_srv6_sids/expected_srv6_sids_sid_delete_2.json @@ -0,0 +1,72 @@ +{ +	"fcbb:bbbb:1:fe10::/64": [ +		{ +			"prefix": "fcbb:bbbb:1:fe10::/64", +			"prefixLen": 64, +			"protocol": "static", +			"vrfId": 0, +			"vrfName": "default", +			"selected": true, +			"destSelected": true, +			"distance": 1, +			"metric": 0, +			"installed": true, +			"table": 254, +			"internalStatus": 16, +			"internalFlags": 9, +			"internalNextHopNum": 1, +			"internalNextHopActiveNum": 1, +			"nexthops": [ +				{ +					"flags": 3, +					"fib": true, +					"directlyConnected": true, +					"interfaceName": "Vrf10", +					"active": true, +					"weight": 1, +					"seg6local": { +						"action": "End.DT4" +					}, +					"seg6localContext": { +						"table": 10 +					} +				} +			] +		} +	], +	"fcbb:bbbb:1:fe30::/64": [ +		{ +			"prefix": "fcbb:bbbb:1:fe30::/64", +			"prefixLen": 64, +			"protocol": "static", +			"vrfId": 0, +			"vrfName": "default", +			"selected": true, +			"destSelected": true, +			"distance": 1, +			"metric": 0, +			"installed": true, +			"table": 254, +			"internalStatus": 16, +			"internalFlags": 9, +			"internalNextHopNum": 1, +			"internalNextHopActiveNum": 1, +			"nexthops": [ +				{ +					"flags": 3, +					"fib": true, +					"directlyConnected": true, +					"interfaceName": "Vrf30", +					"active": true, +					"weight": 1, +					"seg6local": { +						"action": "End.DT46" +					}, +					"seg6localContext": { +						"table": 30 +					} +				} +			] +		} +	] +}
\ No newline at end of file diff --git a/tests/topotests/static_srv6_sids/r1/frr.conf b/tests/topotests/static_srv6_sids/r1/frr.conf index 999e35c35b..b4904d9ac2 100644 --- a/tests/topotests/static_srv6_sids/r1/frr.conf +++ b/tests/topotests/static_srv6_sids/r1/frr.conf @@ -8,6 +8,7 @@ segment-routing     !    !    static-sids +   sid fcbb:bbbb:1::/48 locator MAIN behavior uN     sid fcbb:bbbb:1:fe10::/64 locator MAIN behavior uDT4 vrf Vrf10     sid fcbb:bbbb:1:fe20::/64 locator MAIN behavior uDT6 vrf Vrf20     sid fcbb:bbbb:1:fe30::/64 locator MAIN behavior uDT46 vrf Vrf30 diff --git a/tests/topotests/static_srv6_sids/test_static_srv6_sids.py b/tests/topotests/static_srv6_sids/test_static_srv6_sids.py index 453a30af48..cdcc6fd29e 100755 --- a/tests/topotests/static_srv6_sids/test_static_srv6_sids.py +++ b/tests/topotests/static_srv6_sids/test_static_srv6_sids.py @@ -78,6 +78,100 @@ def test_srv6_static_sids():      check_srv6_static_sids(router, "expected_srv6_sids.json") +def test_srv6_static_sids_sid_delete(): +    """ +    Remove the static SID and verify it gets removed +    """ +    tgen = get_topogen() +    if tgen.routers_have_failure(): +        pytest.skip(tgen.errors) +    router = tgen.gears["r1"] + +    def _check_srv6_static_sids(router, expected_route_file): +        logger.info("checking zebra srv6 static sids") +        output = json.loads(router.vtysh_cmd("show ipv6 route static json")) +        expected = open_json_file("{}/{}".format(CWD, expected_route_file)) +        return topotest.json_cmp(output, expected) + +    def check_srv6_static_sids(router, expected_file): +        func = functools.partial(_check_srv6_static_sids, router, expected_file) +        _, result = topotest.run_and_expect(func, None, count=15, wait=1) +        assert result is None, "Failed" + +    router.vtysh_cmd( +        """ +        configure terminal +         segment-routing +          srv6 +           static-sids +            no sid fcbb:bbbb:1::/48 +        """ +    ) + +    # FOR DEVELOPER: +    # If you want to stop some specific line and start interactive shell, +    # please use tgen.mininet_cli() to start it. + +    logger.info("Test for srv6 sids configuration") +    check_srv6_static_sids(router, "expected_srv6_sids_sid_delete_1.json") + +    router.vtysh_cmd( +        """ +        configure terminal +         segment-routing +          srv6 +           static-sids +            no sid fcbb:bbbb:1:fe20::/64 locator MAIN behavior uDT6 vrf Vrf20 +        """ +    ) + +    # FOR DEVELOPER: +    # If you want to stop some specific line and start interactive shell, +    # please use tgen.mininet_cli() to start it. + +    logger.info("Test for srv6 sids configuration") +    check_srv6_static_sids(router, "expected_srv6_sids_sid_delete_2.json") + + +def test_srv6_static_sids_sid_readd(): +    """ +    Re-add the static SID and verify the routing table +    """ +    tgen = get_topogen() +    if tgen.routers_have_failure(): +        pytest.skip(tgen.errors) +    router = tgen.gears["r1"] + +    def _check_srv6_static_sids(router, expected_route_file): +        logger.info("checking zebra srv6 static sids") +        output = json.loads(router.vtysh_cmd("show ipv6 route static json")) +        expected = open_json_file("{}/{}".format(CWD, expected_route_file)) +        return topotest.json_cmp(output, expected) + +    def check_srv6_static_sids(router, expected_file): +        func = functools.partial(_check_srv6_static_sids, router, expected_file) +        _, result = topotest.run_and_expect(func, None, count=15, wait=1) +        assert result is None, "Failed" + +    router.vtysh_cmd( +        """ +        configure terminal +         segment-routing +          srv6 +           static-sids +            sid fcbb:bbbb:1::/48 locator MAIN behavior uN +            sid fcbb:bbbb:1:fe20::/64 locator MAIN behavior uDT6 vrf Vrf20 +        """ +    ) + +    # FOR DEVELOPER: +    # If you want to stop some specific line and start interactive shell, +    # please use tgen.mininet_cli() to start it. + +    logger.info("Test for srv6 sids configuration") +    check_srv6_static_sids(router, "expected_srv6_sids.json") + +  if __name__ == "__main__":      args = ["-s"] + sys.argv[1:]      sys.exit(pytest.main(args)) diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index a1b16c2b66..0559e89f92 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -2400,6 +2400,79 @@ DEFUNSH(VTYSH_BFDD, bfd_peer_enter, bfd_peer_enter_cmd,  	return CMD_SUCCESS;  } +DEFUNSH(VTYSH_BFDD, sbfd_echo_peer_enter, sbfd_echo_peer_enter_cmd, +	"peer <A.B.C.D|X:X::X:X> bfd-mode sbfd-echo bfd-name BFDNAME [multihop$multihop] local-address <A.B.C.D|X:X::X:X> [vrf NAME] srv6-source-ipv6 X:X::X:X srv6-encap-data X:X::X:X...", +	"Configure peer\n" +	"IPv4 peer address\n" +	"IPv6 peer address\n" +	"Specify bfd session mode\n" +	"Enable sbfd-echo mode\n" +	"Specify bfd session name\n" +	"bfd session name\n" +	"Configure multihop\n" +	"Configure local\n" +	"IPv4 local address\n" +	"IPv6 local address\n" +	"Configure VRF\n" +	"Configure VRF name\n" +	"Configure source ipv6 address for srv6 encap\n" +	"IPv6 local address\n" +	"Configure sidlist data for srv6 encap\n" +	"X:X::X:X IPv6 sid address\n") +{ +	vty->node = BFD_PEER_NODE; +	return CMD_SUCCESS; +} + +DEFUNSH(VTYSH_BFDD, sbfd_init_peer_enter, sbfd_init_peer_enter_cmd, +	"peer <A.B.C.D|X:X::X:X> bfd-mode sbfd-init bfd-name BFDNAME [multihop$multihop] local-address <A.B.C.D|X:X::X:X> [vrf NAME] remote-discr (1-4294967295) srv6-source-ipv6 X:X::X:X srv6-encap-data X:X::X:X...", +	"Configure peer\n" +	"IPv4 peer address\n" +	"IPv6 peer address\n" +	"Specify bfd session mode\n" +	"Enable sbfd-init mode\n" +	"Specify bfd session name\n" +	"bfd session name\n" +	"Configure multihop\n" +	"Configure local\n" +	"IPv4 local address\n" +	"IPv6 local address\n" +	"Configure VRF\n" +	"Configure VRF name\n" +	"Configure bfd session remote discriminator\n" +	"Configure remote discriminator\n" +	"Configure source ipv6 address for srv6 encap\n" +	"IPv6 local address\n" +	"Configure sidlist data for srv6 encap\n" +	"X:X::X:X IPv6 sid address\n" +	) +{ +	vty->node = BFD_PEER_NODE; +	return CMD_SUCCESS; +} + +DEFUNSH(VTYSH_BFDD, sbfd_init_peer_raw_enter, sbfd_init_peer_raw_enter_cmd, +	"peer <A.B.C.D|X:X::X:X> bfd-mode sbfd-init bfd-name BFDNAME [multihop$multihop] local-address <A.B.C.D|X:X::X:X> [vrf NAME] remote-discr (1-4294967295)", +	"Configure peer\n" +	"IPv4 peer address\n" +	"IPv6 peer address\n" +	"Specify bfd session mode\n" +	"Enable sbfd-init mode\n" +	"Specify bfd session name\n" +	"bfd session name\n" +	"Configure multihop\n" +	"Configure local\n" +	"IPv4 local address\n" +	"IPv6 local address\n" +	"Configure VRF\n" +	"Configure VRF name\n" +	"Configure bfd session remote discriminator\n" +	"Configure remote discriminator\n") +{ +	vty->node = BFD_PEER_NODE; +	return CMD_SUCCESS; +} +  DEFUNSH(VTYSH_BFDD, bfd_profile_enter, bfd_profile_enter_cmd,  	"profile BFDPROF",  	BFD_PROFILE_STR @@ -5296,6 +5369,9 @@ void vtysh_init_vty(void)  	install_element(BFD_NODE, &vtysh_end_all_cmd);  	install_element(BFD_NODE, &bfd_peer_enter_cmd); +	install_element(BFD_NODE, &sbfd_init_peer_enter_cmd); +	install_element(BFD_NODE, &sbfd_init_peer_raw_enter_cmd); +	install_element(BFD_NODE, &sbfd_echo_peer_enter_cmd);  	install_element(BFD_PEER_NODE, &vtysh_exit_bfdd_cmd);  	install_element(BFD_PEER_NODE, &vtysh_quit_bfdd_cmd);  	install_element(BFD_PEER_NODE, &vtysh_end_all_cmd); diff --git a/yang/frr-bfdd.yang b/yang/frr-bfdd.yang index c5c824f792..ceed6fa5a1 100644 --- a/yang/frr-bfdd.yang +++ b/yang/frr-bfdd.yang @@ -503,6 +503,136 @@ module frr-bfdd {              config false;            }          } + +        list sbfd-echo { +          key "source-addr bfd-name vrf"; +          description "support a special echo SBFD session in RFC7880, and enhanced with SRv6 encapsulation"; + +          leaf source-addr { +            type inet:ip-address; +            description "Local IP address"; +          } + +          leaf dest-addr { +            type inet:ip-address; +            description "IP address of the peer"; +          } + +          leaf bfd-name { +            type string; +            default ""; +            description "Bfd session name."; +          } + +          leaf vrf { +            type frr-vrf:vrf-ref; +            description "Virtual Routing Domain name"; +          } + +          leaf profile { +            type profile-ref; +            description "Override defaults with profile."; +          } + +          leaf-list srv6-encap-data { +            type inet:ipv6-address; +            max-elements 6; + +            description "segment list ipv6 addresses for srv6 encapsulation"; +          } + +          leaf srv6-source-ipv6 { +            type inet:ipv6-address; +            description "source ipv6 address for srv6 encapsulation"; +          } + +          leaf bfd-mode { +            type uint32; +            description "Bfd session mode."; +          } + +          leaf multi-hop { +            type boolean; +            description "Use multi hop session instead of single hop."; +          } + +          uses session-common; +          uses session-multi-hop; +          uses session-echo; + +          container stats { +            uses session-states; +            config false; +          } +        } + +        list sbfd-init { +          key "source-addr dest-addr bfd-name vrf"; +          description "support SBFD session in RFC7880, and optional with SRv6 encapsulation"; + +          leaf source-addr { +            type inet:ip-address; +            description "Local IP address"; +          } + +          leaf dest-addr { +            type inet:ip-address; +            description "IP address of the peer"; +          } + +          leaf bfd-name { +            type string; +            default ""; +            description "Bfd session name."; +          } + +          leaf vrf { +            type frr-vrf:vrf-ref; +            description "Virtual Routing Domain name"; +          } + +          leaf profile { +            type profile-ref; +            description "Override defaults with profile."; +          } + +          leaf-list srv6-encap-data { +            type inet:ipv6-address; +            max-elements 6; + +            description "segment list ipv6 addresses for srv6 encapsulation"; +          } + +          leaf srv6-source-ipv6 { +            type inet:ip-address; +            description "source ipv6 address for srv6 encapsulation"; +          } + +          leaf remote-discr { +            type uint32; +            default 0; +            description +              "Remote session identifier"; +          } + +          leaf bfd-mode { +            type uint32; +            description "Bfd session mode."; +          } + +          leaf multi-hop { +            type boolean; +            description "Use multi hop session instead of single hop."; +          } + +          uses session-common; +          uses session-multi-hop; + +          container stats { +            uses session-states; +            config false; +          } +        }        }      }    } diff --git a/zebra/zebra_srv6.c b/zebra/zebra_srv6.c index 624f60e815..6d228c5e24 100644 --- a/zebra/zebra_srv6.c +++ b/zebra/zebra_srv6.c @@ -1547,9 +1547,26 @@ static int get_srv6_sid_explicit(struct zebra_srv6_sid **sid,  	}  	if (ctx->behavior == ZEBRA_SEG6_LOCAL_ACTION_END) { -		zlog_err("%s: invalid SM request arguments: explicit SID allocation not allowed for End/uN behavior", -			 __func__); -		return -1; +		zctx = zebra_srv6_sid_ctx_alloc(); +		zctx->ctx = *ctx; + +		*sid = zebra_srv6_sid_alloc(zctx, sid_value, locator, block, sid_func, +					    SRV6_SID_ALLOC_MODE_EXPLICIT); +		if (!(*sid)) { +			flog_err(EC_ZEBRA_SM_CANNOT_ASSIGN_SID, +				 "%s: failed to create SRv6 SID %s (%pI6)", __func__, +				 srv6_sid_ctx2str(buf, sizeof(buf), ctx), sid_value); +			return -1; +		} +		(*sid)->ctx = zctx; +		zctx->sid = *sid; +		listnode_add(srv6->sids, zctx); + +		if (IS_ZEBRA_DEBUG_SRV6) +			zlog_debug("%s: allocated explicit SRv6 SID %pI6 for context %s", __func__, +				   &(*sid)->value, srv6_sid_ctx2str(buf, sizeof(buf), ctx)); + +		return 1;  	}  	/* Allocate an explicit SID function for the SID */ diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index c60eeab946..04585f6312 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -1584,7 +1584,7 @@ static void svd_nh_del_terminate(void *ptr)  	struct zebra_neigh *n = ptr;  	n->refcnt = 0; -	svd_nh_del(n); +	XFREE(MTYPE_L3NEIGH, n);  }  | 
