diff options
| author | Russ White <russ@riw.us> | 2022-03-15 19:58:16 -0400 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-03-15 19:58:16 -0400 | 
| commit | 5d97021ba36666884c7caed69e3565e01f73eaf5 (patch) | |
| tree | d27dc8e34ceee6c63b333c91f8c57a7a95ecbe67 /zebra | |
| parent | b975463458958856a94c4763dbf00fb582353c11 (diff) | |
| parent | 47c1d76a6c79fb0460a7079f9e24228bc9052877 (diff) | |
Merge pull request #10427 from sworleys/Protodown-Reason-Upstream
Add Support for Setting Protodown Reason Code
Diffstat (limited to 'zebra')
| -rw-r--r-- | zebra/debug_nl.c | 110 | ||||
| -rw-r--r-- | zebra/dplane_fpm_nl.c | 3 | ||||
| -rw-r--r-- | zebra/if_netlink.c | 276 | ||||
| -rw-r--r-- | zebra/if_netlink.h | 25 | ||||
| -rw-r--r-- | zebra/if_socket.c | 41 | ||||
| -rw-r--r-- | zebra/interface.c | 349 | ||||
| -rw-r--r-- | zebra/interface.h | 30 | ||||
| -rw-r--r-- | zebra/kernel_netlink.c | 10 | ||||
| -rw-r--r-- | zebra/kernel_socket.c | 6 | ||||
| -rw-r--r-- | zebra/rt.h | 3 | ||||
| -rw-r--r-- | zebra/rt_netlink.h | 2 | ||||
| -rw-r--r-- | zebra/subdir.am | 1 | ||||
| -rw-r--r-- | zebra/zapi_msg.c | 22 | ||||
| -rw-r--r-- | zebra/zebra_dplane.c | 204 | ||||
| -rw-r--r-- | zebra/zebra_dplane.h | 19 | ||||
| -rw-r--r-- | zebra/zebra_errors.c | 9 | ||||
| -rw-r--r-- | zebra/zebra_errors.h | 1 | ||||
| -rw-r--r-- | zebra/zebra_evpn_mh.c | 52 | ||||
| -rw-r--r-- | zebra/zebra_evpn_mh.h | 2 | ||||
| -rw-r--r-- | zebra/zebra_nhg.c | 3 | ||||
| -rw-r--r-- | zebra/zebra_rib.c | 8 | ||||
| -rw-r--r-- | zebra/zebra_router.h | 16 | ||||
| -rw-r--r-- | zebra/zebra_vty.c | 28 | 
23 files changed, 1027 insertions, 193 deletions
diff --git a/zebra/debug_nl.c b/zebra/debug_nl.c index 260ba30b3c..b7d12bf537 100644 --- a/zebra/debug_nl.c +++ b/zebra/debug_nl.c @@ -255,6 +255,40 @@ const char *ifi_type2str(int type)  	}  } +const char *ifla_pdr_type2str(int type) +{ +	switch (type) { +	case IFLA_PROTO_DOWN_REASON_UNSPEC: +		return "UNSPEC"; +	case IFLA_PROTO_DOWN_REASON_MASK: +		return "MASK"; +	case IFLA_PROTO_DOWN_REASON_VALUE: +		return "VALUE"; +	default: +		return "UNKNOWN"; +	} +} + +const char *ifla_info_type2str(int type) +{ +	switch (type) { +	case IFLA_INFO_UNSPEC: +		return "UNSPEC"; +	case IFLA_INFO_KIND: +		return "KIND"; +	case IFLA_INFO_DATA: +		return "DATA"; +	case IFLA_INFO_XSTATS: +		return "XSTATS"; +	case IFLA_INFO_SLAVE_KIND: +		return "SLAVE_KIND"; +	case IFLA_INFO_SLAVE_DATA: +		return "SLAVE_DATA"; +	default: +		return "UNKNOWN"; +	} +} +  const char *rta_type2str(int type)  {  	switch (type) { @@ -358,6 +392,8 @@ const char *rta_type2str(int type)  	case IFLA_EVENT:  		return "EVENT";  #endif /* IFLA_EVENT */ +	case IFLA_PROTO_DOWN_REASON: +		return "PROTO_DOWN_REASON";  	default:  		return "UNKNOWN";  	} @@ -838,6 +874,42 @@ const char *nh_flags2str(uint32_t flags, char *buf, size_t buflen)  /*   * Netlink abstractions.   */ +static void nllink_pdr_dump(struct rtattr *rta, size_t msglen) +{ +	size_t plen; +	uint32_t u32v; + +next_rta: +	/* Check the header for valid length and for outbound access. */ +	if (RTA_OK(rta, msglen) == 0) +		return; + +	plen = RTA_PAYLOAD(rta); +	zlog_debug("      linkinfo [len=%d (payload=%zu) type=(%d) %s]", +		   rta->rta_len, plen, rta->rta_type, +		   ifla_pdr_type2str(rta->rta_type)); +	switch (rta->rta_type) { +	case IFLA_PROTO_DOWN_REASON_MASK: +	case IFLA_PROTO_DOWN_REASON_VALUE: +		if (plen < sizeof(uint32_t)) { +			zlog_debug("        invalid length"); +			break; +		} + +		u32v = *(uint32_t *)RTA_DATA(rta); +		zlog_debug("        %u", u32v); +		break; + +	default: +		/* NOTHING: unhandled. */ +		break; +	} + +	/* Get next pointer and start iteration again. */ +	rta = RTA_NEXT(rta, msglen); +	goto next_rta; +} +  static void nllink_linkinfo_dump(struct rtattr *rta, size_t msglen)  {  	size_t plen; @@ -851,7 +923,7 @@ next_rta:  	plen = RTA_PAYLOAD(rta);  	zlog_debug("      linkinfo [len=%d (payload=%zu) type=(%d) %s]",  		   rta->rta_len, plen, rta->rta_type, -		   rta_type2str(rta->rta_type)); +		   ifla_info_type2str(rta->rta_type));  	switch (rta->rta_type) {  	case IFLA_INFO_KIND:  		if (plen == 0) { @@ -888,8 +960,10 @@ static void nllink_dump(struct ifinfomsg *ifi, size_t msglen)  	struct rtattr *rta;  	size_t plen, it;  	uint32_t u32v; +	uint8_t u8v;  	char bytestr[16];  	char dbuf[128]; +	unsigned short rta_type;  	/* Get the first attribute and go from there. */  	rta = IFLA_RTA(ifi); @@ -899,10 +973,10 @@ next_rta:  		return;  	plen = RTA_PAYLOAD(rta); +	rta_type = rta->rta_type & ~NLA_F_NESTED;  	zlog_debug("    rta [len=%d (payload=%zu) type=(%d) %s]", rta->rta_len, -		   plen, rta->rta_type, rta_type2str(rta->rta_type)); -	switch (rta->rta_type) { -	case IFLA_IFNAME: +		   plen, rta_type, rta_type2str(rta_type)); +	switch (rta_type) {  	case IFLA_IFALIAS:  		if (plen == 0) {  			zlog_debug("      invalid length"); @@ -927,6 +1001,7 @@ next_rta:  #endif /* IFLA_GSO_MAX_SIZE */  	case IFLA_CARRIER_CHANGES:  	case IFLA_MASTER: +	case IFLA_LINK:  		if (plen < sizeof(uint32_t)) {  			zlog_debug("      invalid length");  			break; @@ -936,6 +1011,15 @@ next_rta:  		zlog_debug("      %u", u32v);  		break; +	case IFLA_PROTO_DOWN: +		if (plen < sizeof(uint8_t)) { +			zlog_debug("      invalid length"); +			break; +		} + +		u8v = *(uint8_t *)RTA_DATA(rta); +		zlog_debug("      %u", u8v); +		break;  	case IFLA_ADDRESS:  		datap = RTA_DATA(rta);  		dbuf[0] = 0; @@ -952,7 +1036,11 @@ next_rta:  		break;  	case IFLA_LINKINFO: -		nllink_linkinfo_dump(RTA_DATA(rta), msglen); +		nllink_linkinfo_dump(RTA_DATA(rta), plen); +		break; + +	case IFLA_PROTO_DOWN_REASON: +		nllink_pdr_dump(RTA_DATA(rta), plen);  		break;  	default: @@ -1027,6 +1115,7 @@ static void nlneigh_dump(struct ndmsg *ndm, size_t msglen)  	uint16_t vid;  	char bytestr[16];  	char dbuf[128]; +	unsigned short rta_type;  #ifndef NDA_RTA  #define NDA_RTA(ndm)                                                           \ @@ -1043,9 +1132,10 @@ next_rta:  		return;  	plen = RTA_PAYLOAD(rta); +	rta_type = rta->rta_type & ~NLA_F_NESTED;  	zlog_debug("    rta [len=%d (payload=%zu) type=(%d) %s]", rta->rta_len, -		   plen, rta->rta_type, neigh_rta2str(rta->rta_type)); -	switch (rta->rta_type & ~ NLA_F_NESTED) { +		   plen, rta->rta_type, neigh_rta2str(rta_type)); +	switch (rta_type) {  	case NDA_LLADDR:  		datap = RTA_DATA(rta);  		dbuf[0] = 0; @@ -1153,6 +1243,7 @@ static void nlnh_dump(struct nhmsg *nhm, size_t msglen)  	uint32_t u32v;  	unsigned long count, i;  	struct nexthop_grp *nhgrp; +	unsigned short rta_type;  	rta = RTM_NHA(nhm); @@ -1162,9 +1253,10 @@ next_rta:  		return;  	plen = RTA_PAYLOAD(rta); +	rta_type = rta->rta_type & ~NLA_F_NESTED;  	zlog_debug("    rta [len=%d (payload=%zu) type=(%d) %s]", rta->rta_len, -		   plen, rta->rta_type, nhm_rta2str(rta->rta_type)); -	switch (rta->rta_type & ~NLA_F_NESTED) { +		   plen, rta->rta_type, nhm_rta2str(rta_type)); +	switch (rta_type) {  	case NHA_ID:  		u32v = *(uint32_t *)RTA_DATA(rta);  		zlog_debug("      %u", u32v); diff --git a/zebra/dplane_fpm_nl.c b/zebra/dplane_fpm_nl.c index 4cd2bef307..ec4ea372f1 100644 --- a/zebra/dplane_fpm_nl.c +++ b/zebra/dplane_fpm_nl.c @@ -812,6 +812,9 @@ static int fpm_nl_enqueue(struct fpm_nl_ctx *fnc, struct zebra_dplane_ctx *ctx)  	case DPLANE_OP_INTF_ADDR_ADD:  	case DPLANE_OP_INTF_ADDR_DEL:  	case DPLANE_OP_INTF_NETCONFIG: +	case DPLANE_OP_INTF_INSTALL: +	case DPLANE_OP_INTF_UPDATE: +	case DPLANE_OP_INTF_DELETE:  	case DPLANE_OP_NONE:  		break; diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index e3506ecb14..fca03e55bf 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -77,6 +77,7 @@  #include "zebra/netconf_netlink.h"  extern struct zebra_privs_t zserv_privs; +uint8_t frr_protodown_r_bit = FRR_PROTODOWN_REASON_DEFAULT_BIT;  /* Note: on netlink systems, there should be a 1-to-1 mapping between interface     names and ifindex values. */ @@ -814,33 +815,90 @@ static int netlink_bridge_interface(struct nlmsghdr *h, int len, ns_id_t ns_id,  	return 0;  } -/* If the interface is an es bond member then it must follow EVPN's - * protodown setting +static bool is_if_protodown_reason_only_frr(uint32_t rc_bitfield) +{ +	/* This shouldn't be possible */ +	assert(frr_protodown_r_bit < 32); +	return (rc_bitfield == (((uint32_t)1) << frr_protodown_r_bit)); +} + +/* + * Process interface protodown dplane update. + * + * If the interface is an es bond member then it must follow EVPN's + * protodown setting.   */  static void netlink_proc_dplane_if_protodown(struct zebra_if *zif, -					     bool protodown) +					     struct rtattr **tb)  { -	bool zif_protodown; +	bool protodown; +	bool old_protodown; +	uint32_t rc_bitfield = 0; +	struct rtattr *pd_reason_info[IFLA_MAX + 1]; + +	protodown = !!*(uint8_t *)RTA_DATA(tb[IFLA_PROTO_DOWN]); + +	if (tb[IFLA_PROTO_DOWN_REASON]) { +		netlink_parse_rtattr_nested(pd_reason_info, IFLA_INFO_MAX, +					    tb[IFLA_PROTO_DOWN_REASON]); -	zif_protodown = !!(zif->flags & ZIF_FLAG_PROTODOWN); -	if (protodown == zif_protodown) +		if (pd_reason_info[IFLA_PROTO_DOWN_REASON_VALUE]) +			rc_bitfield = *(uint32_t *)RTA_DATA( +				pd_reason_info[IFLA_PROTO_DOWN_REASON_VALUE]); +	} + +	/* +	 * Set our reason code to note it wasn't us. +	 * If the reason we got from the kernel is ONLY frr though, don't +	 * set it. +	 */ +	COND_FLAG(zif->protodown_rc, ZEBRA_PROTODOWN_EXTERNAL, +		  protodown && rc_bitfield && +			  !is_if_protodown_reason_only_frr(rc_bitfield)); + + +	old_protodown = !!ZEBRA_IF_IS_PROTODOWN(zif); +	if (protodown == old_protodown)  		return;  	if (IS_ZEBRA_DEBUG_EVPN_MH_ES || IS_ZEBRA_DEBUG_KERNEL)  		zlog_debug("interface %s dplane change, protdown %s",  			   zif->ifp->name, protodown ? "on" : "off"); +	/* Set protodown, respectively */ +	COND_FLAG(zif->flags, ZIF_FLAG_PROTODOWN, protodown); +  	if (zebra_evpn_is_es_bond_member(zif->ifp)) { +		/* Check it's not already being sent to the dplane first */ +		if (protodown && +		    CHECK_FLAG(zif->flags, ZIF_FLAG_SET_PROTODOWN)) { +			if (IS_ZEBRA_DEBUG_EVPN_MH_ES || IS_ZEBRA_DEBUG_KERNEL) +				zlog_debug( +					"bond mbr %s protodown on recv'd but already sent protodown on to the dplane", +					zif->ifp->name); +			return; +		} + +		if (!protodown && +		    CHECK_FLAG(zif->flags, ZIF_FLAG_UNSET_PROTODOWN)) { +			if (IS_ZEBRA_DEBUG_EVPN_MH_ES || IS_ZEBRA_DEBUG_KERNEL) +				zlog_debug( +					"bond mbr %s protodown off recv'd but already sent protodown off to the dplane", +					zif->ifp->name); +			return; +		} +  		if (IS_ZEBRA_DEBUG_EVPN_MH_ES || IS_ZEBRA_DEBUG_KERNEL)  			zlog_debug( -				"bond mbr %s re-instate protdown %s in the dplane", -				zif->ifp->name, zif_protodown ? "on" : "off"); -		netlink_protodown(zif->ifp, zif_protodown); -	} else { -		if (protodown) -			zif->flags |= ZIF_FLAG_PROTODOWN; +				"bond mbr %s reinstate protodown %s in the dplane", +				zif->ifp->name, old_protodown ? "on" : "off"); + +		if (old_protodown) +			SET_FLAG(zif->flags, ZIF_FLAG_SET_PROTODOWN);  		else -			zif->flags &= ~ZIF_FLAG_PROTODOWN; +			SET_FLAG(zif->flags, ZIF_FLAG_UNSET_PROTODOWN); + +		dplane_intf_update(zif->ifp);  	}  } @@ -859,6 +917,29 @@ static uint8_t netlink_parse_lacp_bypass(struct rtattr **linkinfo)  }  /* + * Only called at startup to cleanup leftover protodown reasons we may + * have not cleaned up. We leave protodown set though. + */ +static void if_sweep_protodown(struct zebra_if *zif) +{ +	bool protodown; + +	protodown = !!ZEBRA_IF_IS_PROTODOWN(zif); + +	if (!protodown) +		return; + +	if (IS_ZEBRA_DEBUG_KERNEL) +		zlog_debug("interface %s sweeping protodown %s reason 0x%x", +			   zif->ifp->name, protodown ? "on" : "off", +			   zif->protodown_rc); + +	/* Only clear our reason codes, leave external if it was set */ +	UNSET_FLAG(zif->protodown_rc, ZEBRA_PROTODOWN_ALL); +	dplane_intf_update(zif->ifp); +} + +/*   * Called from interface_lookup_netlink().  This function is only used   * during bootstrap.   */ @@ -905,7 +986,8 @@ static int netlink_interface(struct nlmsghdr *h, ns_id_t ns_id, int startup)  	/* Looking up interface name. */  	memset(linkinfo, 0, sizeof(linkinfo)); -	netlink_parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len); +	netlink_parse_rtattr_flags(tb, IFLA_MAX, IFLA_RTA(ifi), len, +				   NLA_F_NESTED);  	/* check for wireless messages to ignore */  	if ((tb[IFLA_WIRELESS] != NULL) && (ifi->ifi_change == 0)) { @@ -1020,10 +1102,8 @@ static int netlink_interface(struct nlmsghdr *h, ns_id_t ns_id, int startup)  		zebra_l2if_update_bond_slave(ifp, bond_ifindex, !!bypass);  	if (tb[IFLA_PROTO_DOWN]) { -		uint8_t protodown; - -		protodown = *(uint8_t *)RTA_DATA(tb[IFLA_PROTO_DOWN]); -		netlink_proc_dplane_if_protodown(zif, !!protodown); +		netlink_proc_dplane_if_protodown(zif, tb); +		if_sweep_protodown(zif);  	}  	return 0; @@ -1244,6 +1324,41 @@ netlink_put_address_update_msg(struct nl_batch *bth,  				     false);  } +static ssize_t netlink_intf_msg_encoder(struct zebra_dplane_ctx *ctx, void *buf, +					size_t buflen) +{ +	enum dplane_op_e op; +	int cmd = 0; + +	op = dplane_ctx_get_op(ctx); + +	switch (op) { +	case DPLANE_OP_INTF_UPDATE: +		cmd = RTM_SETLINK; +		break; +	case DPLANE_OP_INTF_INSTALL: +		cmd = RTM_NEWLINK; +		break; +	case DPLANE_OP_INTF_DELETE: +		cmd = RTM_DELLINK; +		break; +	default: +		flog_err( +			EC_ZEBRA_NHG_FIB_UPDATE, +			"Context received for kernel interface update with incorrect OP code (%u)", +			op); +		return -1; +	} + +	return netlink_intf_msg_encode(cmd, ctx, buf, buflen); +} + +enum netlink_msg_status +netlink_put_intf_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx) +{ +	return netlink_batch_add_msg(bth, ctx, netlink_intf_msg_encoder, false); +} +  int netlink_interface_addr(struct nlmsghdr *h, ns_id_t ns_id, int startup)  {  	int len; @@ -1716,7 +1831,8 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)  	/* Looking up interface name. */  	memset(linkinfo, 0, sizeof(linkinfo)); -	netlink_parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len); +	netlink_parse_rtattr_flags(tb, IFLA_MAX, IFLA_RTA(ifi), len, +				   NLA_F_NESTED);  	/* check for wireless messages to ignore */  	if ((tb[IFLA_WIRELESS] != NULL) && (ifi->ifi_change == 0)) { @@ -1856,14 +1972,9 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)  				zebra_l2if_update_bond_slave(ifp, bond_ifindex,  							     !!bypass); -			if (tb[IFLA_PROTO_DOWN]) { -				uint8_t protodown; +			if (tb[IFLA_PROTO_DOWN]) +				netlink_proc_dplane_if_protodown(ifp->info, tb); -				protodown = *(uint8_t *)RTA_DATA( -					tb[IFLA_PROTO_DOWN]); -				netlink_proc_dplane_if_protodown(ifp->info, -								 !!protodown); -			}  		} else if (ifp->vrf->vrf_id != vrf_id) {  			/* VRF change for an interface. */  			if (IS_ZEBRA_DEBUG_KERNEL) @@ -1910,14 +2021,8 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)  				netlink_to_zebra_link_type(ifi->ifi_type);  			netlink_interface_update_hw_addr(tb, ifp); -			if (tb[IFLA_PROTO_DOWN]) { -				uint8_t protodown; - -				protodown = *(uint8_t *)RTA_DATA( -					tb[IFLA_PROTO_DOWN]); -				netlink_proc_dplane_if_protodown(zif, -								 !!protodown); -			} +			if (tb[IFLA_PROTO_DOWN]) +				netlink_proc_dplane_if_protodown(ifp->info, tb);  			if (if_is_no_ptm_operative(ifp)) {  				bool is_up = if_is_operative(ifp); @@ -2049,30 +2154,72 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)  	return 0;  } -int netlink_protodown(struct interface *ifp, bool down) -{ -	struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT); +/** + * Interface encoding helper function. + * + * \param[in] cmd netlink command. + * \param[in] ctx dataplane context (information snapshot). + * \param[out] buf buffer to hold the packet. + * \param[in] buflen amount of buffer bytes. + */ +ssize_t netlink_intf_msg_encode(uint16_t cmd, +				const struct zebra_dplane_ctx *ctx, void *buf, +				size_t buflen) +{  	struct {  		struct nlmsghdr n;  		struct ifinfomsg ifa; -		char buf[NL_PKT_BUF_SIZE]; -	} req; +		char buf[]; +	} *req = buf; -	memset(&req, 0, sizeof(req)); +	struct rtattr *nest_protodown_reason; +	ifindex_t ifindex = dplane_ctx_get_ifindex(ctx); +	bool down = dplane_ctx_intf_is_protodown(ctx); +	bool pd_reason_val = dplane_ctx_get_intf_pd_reason_val(ctx); +	struct nlsock *nl = +		kernel_netlink_nlsock_lookup(dplane_ctx_get_ns_sock(ctx)); -	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); -	req.n.nlmsg_flags = NLM_F_REQUEST; -	req.n.nlmsg_type = RTM_SETLINK; -	req.n.nlmsg_pid = zns->netlink_cmd.snl.nl_pid; +	if (buflen < sizeof(*req)) +		return 0; -	req.ifa.ifi_index = ifp->ifindex; +	memset(req, 0, sizeof(*req)); -	nl_attr_put(&req.n, sizeof(req), IFLA_PROTO_DOWN, &down, sizeof(down)); -	nl_attr_put32(&req.n, sizeof(req), IFLA_LINK, ifp->ifindex); +	if (cmd != RTM_SETLINK) +		flog_err( +			EC_ZEBRA_INTF_UPDATE_FAILURE, +			"Only RTM_SETLINK message type currently supported in dplane pthread"); -	return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, -			    false); +	req->n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); +	req->n.nlmsg_flags = NLM_F_REQUEST; +	req->n.nlmsg_type = cmd; +	req->n.nlmsg_pid = nl->snl.nl_pid; + +	req->ifa.ifi_index = ifindex; + +	nl_attr_put8(&req->n, buflen, IFLA_PROTO_DOWN, down); +	nl_attr_put32(&req->n, buflen, IFLA_LINK, ifindex); + +	/* Reason info nest */ +	nest_protodown_reason = +		nl_attr_nest(&req->n, buflen, IFLA_PROTO_DOWN_REASON); + +	if (!nest_protodown_reason) +		return -1; + +	nl_attr_put32(&req->n, buflen, IFLA_PROTO_DOWN_REASON_MASK, +		      (1 << frr_protodown_r_bit)); +	nl_attr_put32(&req->n, buflen, IFLA_PROTO_DOWN_REASON_VALUE, +		      ((int)pd_reason_val) << frr_protodown_r_bit); + +	nl_attr_nest_end(&req->n, nest_protodown_reason); + +	if (IS_ZEBRA_DEBUG_KERNEL) +		zlog_debug("%s: %s, protodown=%d reason_val=%d ifindex=%u", +			   __func__, nl_msg_type_to_str(cmd), down, +			   pd_reason_val, ifindex); + +	return NLMSG_ALIGN(req->n.nlmsg_len);  }  /* Interface information read by netlink. */ @@ -2088,4 +2235,35 @@ void interface_list(struct zebra_ns *zns)  	interface_addr_lookup_netlink(zns);  } +void if_netlink_set_frr_protodown_r_bit(uint8_t bit) +{ +	if (IS_ZEBRA_DEBUG_KERNEL) +		zlog_debug( +			"Protodown reason bit index changed: bit-index %u -> bit-index %u", +			frr_protodown_r_bit, bit); + +	frr_protodown_r_bit = bit; +} + +void if_netlink_unset_frr_protodown_r_bit(void) +{ +	if (IS_ZEBRA_DEBUG_KERNEL) +		zlog_debug( +			"Protodown reason bit index changed: bit-index %u -> bit-index %u", +			frr_protodown_r_bit, FRR_PROTODOWN_REASON_DEFAULT_BIT); + +	frr_protodown_r_bit = FRR_PROTODOWN_REASON_DEFAULT_BIT; +} + + +bool if_netlink_frr_protodown_r_bit_is_set(void) +{ +	return (frr_protodown_r_bit != FRR_PROTODOWN_REASON_DEFAULT_BIT); +} + +uint8_t if_netlink_get_frr_protodown_r_bit(void) +{ +	return frr_protodown_r_bit; +} +  #endif /* GNU_LINUX */ diff --git a/zebra/if_netlink.h b/zebra/if_netlink.h index a1ce7af8c7..46eac25377 100644 --- a/zebra/if_netlink.h +++ b/zebra/if_netlink.h @@ -40,6 +40,9 @@ int netlink_interface_addr_dplane(struct nlmsghdr *h, ns_id_t ns_id,  extern int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup);  extern int interface_lookup_netlink(struct zebra_ns *zns); +extern ssize_t netlink_intf_msg_encode(uint16_t cmd, +				       const struct zebra_dplane_ctx *ctx, +				       void *buf, size_t buflen);  extern enum netlink_msg_status  netlink_put_gre_set_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx); @@ -47,19 +50,19 @@ extern enum netlink_msg_status  netlink_put_address_update_msg(struct nl_batch *bth,  			       struct zebra_dplane_ctx *ctx); -/* - * Set protodown status of interface. - * - * ifp - *    Interface to set protodown on. - * - * down - *    If true, set protodown on. If false, set protodown off. +extern enum netlink_msg_status +netlink_put_intf_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx); + +#define FRR_PROTODOWN_REASON_DEFAULT_BIT 7 +/* Protodown bit setter/getter   * - * Returns: - *    0 + * Allow users to change the bit if it conflicts with another + * on their system.   */ -int netlink_protodown(struct interface *ifp, bool down); +extern void if_netlink_set_frr_protodown_r_bit(uint8_t bit); +extern void if_netlink_unset_frr_protodown_r_bit(void); +extern bool if_netlink_frr_protodown_r_bit_is_set(void); +extern uint8_t if_netlink_get_frr_protodown_r_bit(void);  #ifdef __cplusplus  } diff --git a/zebra/if_socket.c b/zebra/if_socket.c new file mode 100644 index 0000000000..309d5a3f3e --- /dev/null +++ b/zebra/if_socket.c @@ -0,0 +1,41 @@ +/* + * Zebra Interface interaction with the kernel using socket. + * Copyright (C) 2022  NVIDIA CORPORATION & AFFILIATES + *                     Stephen Worley + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FRR; see the file COPYING.  If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include <zebra.h> + +#ifndef HAVE_NETLINK + +#include "lib_errors.h" + +#include "zebra/rt.h" +#include "zebra/zebra_dplane.h" +#include "zebra/zebra_errors.h" + +enum zebra_dplane_result kernel_intf_update(struct zebra_dplane_ctx *ctx) +{ +	flog_err(EC_LIB_UNAVAILABLE, "%s not Implemented for this platform", +		 __func__); +	return ZEBRA_DPLANE_REQUEST_FAILURE; +} + +#endif diff --git a/zebra/interface.c b/zebra/interface.c index a76f8741e0..69d611e583 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -261,6 +261,13 @@ static int if_zebra_delete_hook(struct interface *ifp)  	if (ifp->info) {  		zebra_if = ifp->info; +		/* If we set protodown, clear our reason now from the kernel */ +		if (ZEBRA_IF_IS_PROTODOWN(zebra_if) && zebra_if->protodown_rc && +		    !ZEBRA_IF_IS_PROTODOWN_ONLY_EXTERNAL(zebra_if)) +			zebra_if_update_protodown_rc(ifp, true, +						     (zebra_if->protodown_rc & +						      ~ZEBRA_PROTODOWN_ALL)); +  		/* Free installed address chains tree. */  		if (zebra_if->ipv4_subnets)  			route_table_finish(zebra_if->ipv4_subnets); @@ -1229,58 +1236,130 @@ void zebra_if_update_all_links(struct zebra_ns *zns)  	}  } -void zebra_if_set_protodown(struct interface *ifp, bool down) +static bool if_ignore_set_protodown(const struct interface *ifp, bool new_down, +				    uint32_t new_protodown_rc) +{ +	struct zebra_if *zif; +	bool old_down, old_set_down, old_unset_down; + +	zif = ifp->info; + +	/* Current state as we know it */ +	old_down = !!(ZEBRA_IF_IS_PROTODOWN(zif)); +	old_set_down = !!CHECK_FLAG(zif->flags, ZIF_FLAG_SET_PROTODOWN); +	old_unset_down = !!CHECK_FLAG(zif->flags, ZIF_FLAG_UNSET_PROTODOWN); + +	if (new_protodown_rc == zif->protodown_rc) { +		/* Early return if already down & reason bitfield matches */ +		if (new_down == old_down) { +			if (IS_ZEBRA_DEBUG_KERNEL) +				zlog_debug( +					"Ignoring request to set protodown %s for interface %s (%u): protodown %s is already set (reason bitfield: old 0x%x new 0x%x)", +					new_down ? "on" : "off", ifp->name, +					ifp->ifindex, new_down ? "on" : "off", +					zif->protodown_rc, new_protodown_rc); + +			return true; +		} + +		/* Early return if already set queued & reason bitfield matches +		 */ +		if (new_down && old_set_down) { +			if (IS_ZEBRA_DEBUG_KERNEL) +				zlog_debug( +					"Ignoring request to set protodown %s for interface %s (%u): protodown %s is already queued to dplane (reason bitfield: old 0x%x new 0x%x)", +					new_down ? "on" : "off", ifp->name, +					ifp->ifindex, new_down ? "on" : "off", +					zif->protodown_rc, new_protodown_rc); + +			return true; +		} + +		/* Early return if already unset queued & reason bitfield +		 * matches */ +		if (!new_down && old_unset_down) { +			if (IS_ZEBRA_DEBUG_KERNEL) +				zlog_debug( +					"Ignoring request to set protodown %s for interface %s (%u): protodown %s is already queued to dplane (reason bitfield: old 0x%x new 0x%x)", +					new_down ? "on" : "off", ifp->name, +					ifp->ifindex, new_down ? "on" : "off", +					zif->protodown_rc, new_protodown_rc); + +			return true; +		} +	} + +	return false; +} + +int zebra_if_update_protodown_rc(struct interface *ifp, bool new_down, +				 uint32_t new_protodown_rc)  { +	struct zebra_if *zif; + +	zif = ifp->info; + +	/* Check if we already have this state or it's queued */ +	if (if_ignore_set_protodown(ifp, new_down, new_protodown_rc)) +		return 1; + +	zlog_info( +		"Setting protodown %s - interface %s (%u): reason bitfield change from 0x%x --> 0x%x", +		new_down ? "on" : "off", ifp->name, ifp->ifindex, +		zif->protodown_rc, new_protodown_rc); + +	zif->protodown_rc = new_protodown_rc; + +	if (new_down) +		SET_FLAG(zif->flags, ZIF_FLAG_SET_PROTODOWN); +	else +		SET_FLAG(zif->flags, ZIF_FLAG_UNSET_PROTODOWN); +  #ifdef HAVE_NETLINK -	netlink_protodown(ifp, down); +	dplane_intf_update(ifp);  #else  	zlog_warn("Protodown is not supported on this platform");  #endif +	return 0; +} + +int zebra_if_set_protodown(struct interface *ifp, bool new_down, +			   enum protodown_reasons new_reason) +{ +	struct zebra_if *zif; +	uint32_t new_protodown_rc; + +	zif = ifp->info; + +	if (new_down) +		new_protodown_rc = zif->protodown_rc | new_reason; +	else +		new_protodown_rc = zif->protodown_rc & ~new_reason; + +	return zebra_if_update_protodown_rc(ifp, new_down, new_protodown_rc);  }  /* - * Handle an interface addr event based on info in a dplane context object. + * Handle an interface events based on info in a dplane context object.   * This runs in the main pthread, using the info in the context object to   * modify an interface.   */ -void zebra_if_addr_update_ctx(struct zebra_dplane_ctx *ctx) +static void zebra_if_addr_update_ctx(struct zebra_dplane_ctx *ctx, +				     struct interface *ifp)  { -	struct interface *ifp;  	uint8_t flags = 0;  	const char *label = NULL; -	ns_id_t ns_id; -	struct zebra_ns *zns;  	uint32_t metric = METRIC_MAX; -	ifindex_t ifindex;  	const struct prefix *addr, *dest = NULL;  	enum dplane_op_e op;  	op = dplane_ctx_get_op(ctx); -	ns_id = dplane_ctx_get_ns_id(ctx); - -	zns = zebra_ns_lookup(ns_id); -	if (zns == NULL) { -		/* No ns - deleted maybe? */ -		if (IS_ZEBRA_DEBUG_KERNEL) -			zlog_debug("%s: can't find zns id %u", __func__, ns_id); -		goto done; -	} - -	ifindex = dplane_ctx_get_ifindex(ctx); - -	ifp = if_lookup_by_index_per_ns(zns, ifindex); -	if (ifp == NULL) { -		if (IS_ZEBRA_DEBUG_KERNEL) -			zlog_debug("%s: can't find ifp at nsid %u index %d", -				   __func__, ns_id, ifindex); -		goto done; -	} -  	addr = dplane_ctx_get_intf_addr(ctx);  	if (IS_ZEBRA_DEBUG_KERNEL) -		zlog_debug("%s: %s: ifindex %u, addr %pFX", __func__, -			   dplane_op2str(op), ifindex, addr); +		zlog_debug("%s: %s: ifindex %s(%u), addr %pFX", __func__, +			   dplane_op2str(dplane_ctx_get_op(ctx)), ifp->name, +			   ifp->ifindex, addr);  	/* Is there a peer or broadcast address? */  	dest = dplane_ctx_get_intf_dest(ctx); @@ -1335,41 +1414,66 @@ void zebra_if_addr_update_ctx(struct zebra_dplane_ctx *ctx)  	 */  	if (op != DPLANE_OP_INTF_ADDR_ADD)  		rib_update(RIB_UPDATE_KERNEL); +} + +static void zebra_if_update_ctx(struct zebra_dplane_ctx *ctx, +				struct interface *ifp) +{ +	enum zebra_dplane_result dp_res; +	struct zebra_if *zif; +	bool pd_reason_val; +	bool down; + +	dp_res = dplane_ctx_get_status(ctx); +	pd_reason_val = dplane_ctx_get_intf_pd_reason_val(ctx); +	down = dplane_ctx_intf_is_protodown(ctx); + +	if (IS_ZEBRA_DEBUG_KERNEL) +		zlog_debug("%s: %s: if %s(%u) ctx-protodown %s ctx-reason %d", +			   __func__, dplane_op2str(dplane_ctx_get_op(ctx)), +			   ifp->name, ifp->ifindex, down ? "on" : "off", +			   pd_reason_val); + +	zif = ifp->info; +	if (!zif) { +		if (IS_ZEBRA_DEBUG_KERNEL) +			zlog_debug("%s: if %s(%u) zebra info pointer is NULL", +				   __func__, ifp->name, ifp->ifindex); +		return; +	} + +	if (dp_res != ZEBRA_DPLANE_REQUEST_SUCCESS) { +		if (IS_ZEBRA_DEBUG_KERNEL) +			zlog_debug("%s: if %s(%u) dplane update failed", +				   __func__, ifp->name, ifp->ifindex); +		goto done; +	} + +	/* Update our info */ +	COND_FLAG(zif->flags, ZIF_FLAG_PROTODOWN, down);  done: -	/* We're responsible for the ctx object */ -	dplane_ctx_fini(&ctx); +	/* Clear our dplane flags */ +	UNSET_FLAG(zif->flags, ZIF_FLAG_SET_PROTODOWN); +	UNSET_FLAG(zif->flags, ZIF_FLAG_UNSET_PROTODOWN);  }  /*   * Handle netconf change from a dplane context object; runs in the main   * pthread so it can update zebra data structs.   */ -int zebra_if_netconf_update_ctx(struct zebra_dplane_ctx *ctx) +static void zebra_if_netconf_update_ctx(struct zebra_dplane_ctx *ctx, +					struct interface *ifp)  { -	struct zebra_ns *zns; -	struct interface *ifp;  	struct zebra_if *zif;  	enum dplane_netconf_status_e mpls; -	int ret = 0; - -	zns = zebra_ns_lookup(dplane_ctx_get_netconf_ns_id(ctx)); -	if (zns == NULL) { -		ret = -1; -		goto done; -	} - -	ifp = if_lookup_by_index_per_ns(zns, -					dplane_ctx_get_netconf_ifindex(ctx)); -	if (ifp == NULL) { -		ret = -1; -		goto done; -	}  	zif = ifp->info; -	if (zif == NULL) { -		ret = -1; -		goto done; +	if (!zif) { +		if (IS_ZEBRA_DEBUG_KERNEL) +			zlog_debug("%s: if %s(%u) zebra info pointer is NULL", +				   __func__, ifp->name, ifp->ifindex); +		return;  	}  	mpls = dplane_ctx_get_netconf_mpls(ctx); @@ -1383,12 +1487,105 @@ int zebra_if_netconf_update_ctx(struct zebra_dplane_ctx *ctx)  		zlog_debug("%s: if %s, ifindex %d, mpls %s",  			   __func__, ifp->name, ifp->ifindex,  			   (zif->mpls ? "ON" : "OFF")); +} +void zebra_if_dplane_result(struct zebra_dplane_ctx *ctx) +{ +	struct zebra_ns *zns; +	struct interface *ifp; +	ns_id_t ns_id; +	enum dplane_op_e op; +	enum zebra_dplane_result dp_res; +	ifindex_t ifindex; + +	ns_id = dplane_ctx_get_ns_id(ctx); +	dp_res = dplane_ctx_get_status(ctx); +	op = dplane_ctx_get_op(ctx); +	ifindex = dplane_ctx_get_ifindex(ctx); + +	if (IS_ZEBRA_DEBUG_DPLANE_DETAIL || IS_ZEBRA_DEBUG_KERNEL) +		zlog_debug("Intf dplane ctx %p, op %s, ifindex (%u), result %s", +			   ctx, dplane_op2str(op), ifindex, +			   dplane_res2str(dp_res)); + +	zns = zebra_ns_lookup(ns_id); +	if (zns == NULL) { +		/* No ns - deleted maybe? */ +		if (IS_ZEBRA_DEBUG_KERNEL) +			zlog_debug("%s: can't find zns id %u", __func__, ns_id); + +		goto done; +	} + +	ifp = if_lookup_by_index_per_ns(zns, ifindex); +	if (ifp == NULL) { +		if (IS_ZEBRA_DEBUG_KERNEL) +			zlog_debug("%s: can't find ifp at nsid %u index %d", +				   __func__, ns_id, ifindex); + +		goto done; +	} + +	switch (op) { +	case DPLANE_OP_INTF_ADDR_ADD: +	case DPLANE_OP_INTF_ADDR_DEL: +		zebra_if_addr_update_ctx(ctx, ifp); +		break; + +	case DPLANE_OP_INTF_INSTALL: +	case DPLANE_OP_INTF_UPDATE: +	case DPLANE_OP_INTF_DELETE: +		zebra_if_update_ctx(ctx, ifp); +		break; + +	case DPLANE_OP_INTF_NETCONFIG: +		zebra_if_netconf_update_ctx(ctx, ifp); +		break; + +	case DPLANE_OP_ROUTE_INSTALL: +	case DPLANE_OP_ROUTE_UPDATE: +	case DPLANE_OP_ROUTE_DELETE: +	case DPLANE_OP_NH_DELETE: +	case DPLANE_OP_NH_INSTALL: +	case DPLANE_OP_NH_UPDATE: +	case DPLANE_OP_ROUTE_NOTIFY: +	case DPLANE_OP_LSP_INSTALL: +	case DPLANE_OP_LSP_UPDATE: +	case DPLANE_OP_LSP_DELETE: +	case DPLANE_OP_LSP_NOTIFY: +	case DPLANE_OP_PW_INSTALL: +	case DPLANE_OP_PW_UNINSTALL: +	case DPLANE_OP_SYS_ROUTE_ADD: +	case DPLANE_OP_SYS_ROUTE_DELETE: +	case DPLANE_OP_ADDR_INSTALL: +	case DPLANE_OP_ADDR_UNINSTALL: +	case DPLANE_OP_MAC_INSTALL: +	case DPLANE_OP_MAC_DELETE: +	case DPLANE_OP_NEIGH_INSTALL: +	case DPLANE_OP_NEIGH_UPDATE: +	case DPLANE_OP_NEIGH_DELETE: +	case DPLANE_OP_NEIGH_IP_INSTALL: +	case DPLANE_OP_NEIGH_IP_DELETE: +	case DPLANE_OP_VTEP_ADD: +	case DPLANE_OP_VTEP_DELETE: +	case DPLANE_OP_RULE_ADD: +	case DPLANE_OP_RULE_DELETE: +	case DPLANE_OP_RULE_UPDATE: +	case DPLANE_OP_NEIGH_DISCOVER: +	case DPLANE_OP_BR_PORT_UPDATE: +	case DPLANE_OP_NONE: +	case DPLANE_OP_IPTABLE_ADD: +	case DPLANE_OP_IPTABLE_DELETE: +	case DPLANE_OP_IPSET_ADD: +	case DPLANE_OP_IPSET_DELETE: +	case DPLANE_OP_IPSET_ENTRY_ADD: +	case DPLANE_OP_IPSET_ENTRY_DELETE: +	case DPLANE_OP_NEIGH_TABLE_UPDATE: +	case DPLANE_OP_GRE_SET: +		break; /* should never hit here */ +	}  done: -	/* Free the context */  	dplane_ctx_fini(&ctx); - -	return ret;  }  /* Dump if address information to vty. */ @@ -1651,28 +1848,34 @@ static void ifs_dump_brief_vty_json(json_object *json, struct vrf *vrf)  	}  } -const char *zebra_protodown_rc_str(enum protodown_reasons protodown_rc, -				   char *pd_buf, uint32_t pd_buf_len) +const char *zebra_protodown_rc_str(uint32_t protodown_rc, char *pd_buf, +				   uint32_t pd_buf_len)  { -	bool first = true; -  	pd_buf[0] = '\0'; +	size_t len;  	strlcat(pd_buf, "(", pd_buf_len); -	if (protodown_rc & ZEBRA_PROTODOWN_EVPN_STARTUP_DELAY) { -		if (first) -			first = false; -		else -			strlcat(pd_buf, ",", pd_buf_len); -		strlcat(pd_buf, "startup-delay", pd_buf_len); -	} +	if (CHECK_FLAG(protodown_rc, ZEBRA_PROTODOWN_EXTERNAL)) +		strlcat(pd_buf, "external,", pd_buf_len); -	if (protodown_rc & ZEBRA_PROTODOWN_EVPN_UPLINK_DOWN) { -		if (!first) -			strlcat(pd_buf, ",", pd_buf_len); -		strlcat(pd_buf, "uplinks-down", pd_buf_len); -	} +	if (CHECK_FLAG(protodown_rc, ZEBRA_PROTODOWN_EVPN_STARTUP_DELAY)) +		strlcat(pd_buf, "startup-delay,", pd_buf_len); + +	if (CHECK_FLAG(protodown_rc, ZEBRA_PROTODOWN_EVPN_UPLINK_DOWN)) +		strlcat(pd_buf, "uplinks-down,", pd_buf_len); + +	if (CHECK_FLAG(protodown_rc, ZEBRA_PROTODOWN_VRRP)) +		strlcat(pd_buf, "vrrp,", pd_buf_len); + +	if (CHECK_FLAG(protodown_rc, ZEBRA_PROTODOWN_SHARP)) +		strlcat(pd_buf, "sharp,", pd_buf_len); + +	len = strnlen(pd_buf, pd_buf_len); + +	/* Remove trailing comma */ +	if (pd_buf[len - 1] == ',') +		pd_buf[len - 1] = '\0';  	strlcat(pd_buf, ")", pd_buf_len); @@ -1878,7 +2081,7 @@ static void if_dump_vty(struct vty *vty, struct interface *ifp)  	zebra_evpn_if_es_print(vty, NULL, zebra_if);  	vty_out(vty, "  protodown: %s %s\n", -		(zebra_if->flags & ZIF_FLAG_PROTODOWN) ? "on" : "off", +		(ZEBRA_IF_IS_PROTODOWN(zebra_if)) ? "on" : "off",  		if_is_protodown_applicable(ifp) ? "" : "(n/a)");  	if (zebra_if->protodown_rc)  		vty_out(vty, "  protodown reasons: %s\n", @@ -2229,7 +2432,7 @@ static void if_dump_vty_json(struct vty *vty, struct interface *ifp,  	if (if_is_protodown_applicable(ifp)) {  		json_object_string_add(  			json_if, "protodown", -			(zebra_if->flags & ZIF_FLAG_PROTODOWN) ? "on" : "off"); +			(ZEBRA_IF_IS_PROTODOWN(zebra_if)) ? "on" : "off");  		if (zebra_if->protodown_rc)  			json_object_string_add(  				json_if, "protodownReason", diff --git a/zebra/interface.h b/zebra/interface.h index 315a3170d8..4b06603e7f 100644 --- a/zebra/interface.h +++ b/zebra/interface.h @@ -308,14 +308,22 @@ enum zebra_if_flags {  	/* Dataplane protodown-on */  	ZIF_FLAG_PROTODOWN = (1 << 2), +	/* Dataplane protodown-on Queued to the dplane */ +	ZIF_FLAG_SET_PROTODOWN = (1 << 3), +	/* Dataplane protodown-off Queued to the dplane */ +	ZIF_FLAG_UNSET_PROTODOWN = (1 << 4),  	/* LACP bypass state is set by the dataplane on a bond member  	 * and inherited by the bond (if one or more bond members are in  	 * a bypass state the bond is placed in a bypass state)  	 */ -	ZIF_FLAG_LACP_BYPASS = (1 << 3) +	ZIF_FLAG_LACP_BYPASS = (1 << 5)  }; +#define ZEBRA_IF_IS_PROTODOWN(zif) ((zif)->flags & ZIF_FLAG_PROTODOWN) +#define ZEBRA_IF_IS_PROTODOWN_ONLY_EXTERNAL(zif)                               \ +	((zif)->protodown_rc == ZEBRA_PROTODOWN_EXTERNAL) +  /* `zebra' daemon local interface structure. */  struct zebra_if {  	/* back pointer to the interface */ @@ -403,7 +411,7 @@ struct zebra_if {  	 * in the dataplane. This results in a carrier/L1 down on the  	 * physical device.  	 */ -	enum protodown_reasons protodown_rc; +	uint32_t protodown_rc;  	/* list of zebra_mac entries using this interface as destination */  	struct list *mac_list; @@ -497,7 +505,16 @@ extern void if_handle_vrf_change(struct interface *ifp, vrf_id_t vrf_id);  extern void zebra_if_update_link(struct interface *ifp, ifindex_t link_ifindex,  				 ns_id_t ns_id);  extern void zebra_if_update_all_links(struct zebra_ns *zns); -extern void zebra_if_set_protodown(struct interface *ifp, bool down); +/** + * Directly update entire protodown & reason code bitfield. + */ +extern int zebra_if_update_protodown_rc(struct interface *ifp, bool new_down, +					uint32_t new_protodown_rc); +/** + * Set protodown with single reason. + */ +extern int zebra_if_set_protodown(struct interface *ifp, bool down, +				  enum protodown_reasons new_reason);  extern int if_ip_address_install(struct interface *ifp, struct prefix *prefix,  				 const char *label, struct prefix *pp);  extern int if_ipv6_address_install(struct interface *ifp, struct prefix *prefix, @@ -521,10 +538,9 @@ extern bool if_nhg_dependents_is_empty(const struct interface *ifp);  extern void vrf_add_update(struct vrf *vrfp);  extern void zebra_l2_map_slave_to_bond(struct zebra_if *zif, vrf_id_t vrf);  extern void zebra_l2_unmap_slave_from_bond(struct zebra_if *zif); -extern const char *zebra_protodown_rc_str(enum protodown_reasons protodown_rc, -					  char *pd_buf, uint32_t pd_buf_len); -void zebra_if_addr_update_ctx(struct zebra_dplane_ctx *ctx); -int zebra_if_netconf_update_ctx(struct zebra_dplane_ctx *ctx); +extern const char *zebra_protodown_rc_str(uint32_t protodown_rc, char *pd_buf, +					  uint32_t pd_buf_len); +void zebra_if_dplane_result(struct zebra_dplane_ctx *ctx);  #ifdef HAVE_PROC_NET_DEV  extern void ifstat_update_proc(void); diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c index d84b0c1325..0dd76e3253 100644 --- a/zebra/kernel_netlink.c +++ b/zebra/kernel_netlink.c @@ -94,6 +94,7 @@ static const struct message nlmsg_str[] = {{RTM_NEWROUTE, "RTM_NEWROUTE"},  					   {RTM_DELROUTE, "RTM_DELROUTE"},  					   {RTM_GETROUTE, "RTM_GETROUTE"},  					   {RTM_NEWLINK, "RTM_NEWLINK"}, +					   {RTM_SETLINK, "RTM_SETLINK"},  					   {RTM_DELLINK, "RTM_DELLINK"},  					   {RTM_GETLINK, "RTM_GETLINK"},  					   {RTM_NEWADDR, "RTM_NEWADDR"}, @@ -209,6 +210,10 @@ int netlink_config_write_helper(struct vty *vty)  		vty_out(vty, "zebra kernel netlink batch-tx-buf %u %u\n", size,  			threshold); +	if (if_netlink_frr_protodown_r_bit_is_set()) +		vty_out(vty, "zebra protodown reason-bit %u\n", +			if_netlink_get_frr_protodown_r_bit()); +  	return 0;  } @@ -1491,6 +1496,11 @@ static enum netlink_msg_status nl_put_msg(struct nl_batch *bth,  	case DPLANE_OP_INTF_NETCONFIG:  	case DPLANE_OP_NONE:  		return FRR_NETLINK_ERROR; + +	case DPLANE_OP_INTF_INSTALL: +	case DPLANE_OP_INTF_UPDATE: +	case DPLANE_OP_INTF_DELETE: +		return netlink_put_intf_update_msg(bth, ctx);  	}  	return FRR_NETLINK_ERROR; diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c index ce1f17111b..6583af0a54 100644 --- a/zebra/kernel_socket.c +++ b/zebra/kernel_socket.c @@ -1577,6 +1577,12 @@ void kernel_update_multi(struct dplane_ctx_q *ctx_list)  			res = kernel_pbr_rule_update(ctx);  			break; +		case DPLANE_OP_INTF_INSTALL: +		case DPLANE_OP_INTF_UPDATE: +		case DPLANE_OP_INTF_DELETE: +			res = kernel_intf_update(ctx); +			break; +  		/* Ignore 'notifications' - no-op */  		case DPLANE_OP_SYS_ROUTE_ADD:  		case DPLANE_OP_SYS_ROUTE_DELETE: diff --git a/zebra/rt.h b/zebra/rt.h index 5e626928d9..4952c3eb1a 100644 --- a/zebra/rt.h +++ b/zebra/rt.h @@ -66,6 +66,9 @@ enum zebra_dplane_result kernel_neigh_update_ctx(struct zebra_dplane_ctx *ctx);  extern enum zebra_dplane_result  kernel_pbr_rule_update(struct zebra_dplane_ctx *ctx); +extern enum zebra_dplane_result +kernel_intf_update(struct zebra_dplane_ctx *ctx); +  #endif /* !HAVE_NETLINK */  extern int kernel_neigh_update(int cmd, int ifindex, void *addr, char *lla, diff --git a/zebra/rt_netlink.h b/zebra/rt_netlink.h index 0a79771708..b1af4b20e1 100644 --- a/zebra/rt_netlink.h +++ b/zebra/rt_netlink.h @@ -128,6 +128,8 @@ const char *af_type2str(int type);  const char *ifi_type2str(int type);  const char *rta_type2str(int type);  const char *rtm_type2str(int type); +const char *ifla_pdr_type2str(int type); +const char *ifla_info_type2str(int type);  const char *rtm_protocol2str(int type);  const char *rtm_scope2str(int type);  const char *rtm_rta2str(int type); diff --git a/zebra/subdir.am b/zebra/subdir.am index 77e0898d81..8cb1237c22 100644 --- a/zebra/subdir.am +++ b/zebra/subdir.am @@ -60,6 +60,7 @@ zebra_zebra_SOURCES = \  	zebra/debug.c \  	zebra/if_ioctl.c \  	zebra/if_netlink.c \ +	zebra/if_socket.c \  	zebra/if_sysctl.c \  	zebra/interface.c \  	zebra/ioctl.c \ diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index 1b6f37ec6a..e94aee5c1a 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -1487,6 +1487,7 @@ static void zread_interface_set_protodown(ZAPI_HANDLER_ARGS)  	ifindex_t ifindex;  	struct interface *ifp;  	char down; +	enum protodown_reasons reason;  	STREAM_GETL(msg, ifindex);  	STREAM_GETC(msg, down); @@ -1494,16 +1495,27 @@ static void zread_interface_set_protodown(ZAPI_HANDLER_ARGS)  	/* set ifdown */  	ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(NS_DEFAULT), ifindex); -	if (ifp) { -		zlog_info("Setting interface %s (%u): protodown %s", ifp->name, -			  ifindex, down ? "on" : "off"); -		zebra_if_set_protodown(ifp, down); -	} else { +	if (!ifp) {  		zlog_warn(  			"Cannot set protodown %s for interface %u; does not exist",  			down ? "on" : "off", ifindex); + +		return; +	} + +	switch (client->proto) { +	case ZEBRA_ROUTE_VRRP: +		reason = ZEBRA_PROTODOWN_VRRP; +		break; +	case ZEBRA_ROUTE_SHARP: +		reason = ZEBRA_PROTODOWN_SHARP; +		break; +	default: +		reason = 0; +		break;  	} +	zebra_if_set_protodown(ifp, down, reason);  stream_failure:  	return; diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index 6de2be3ab8..d034c8f306 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -193,6 +193,9 @@ struct dplane_intf_info {  	uint32_t metric;  	uint32_t flags; +	bool protodown; +	bool pd_reason_val; +  #define DPLANE_INTF_CONNECTED   (1 << 0) /* Connected peer, p2p */  #define DPLANE_INTF_SECONDARY   (1 << 1)  #define DPLANE_INTF_BROADCAST   (1 << 2) @@ -526,6 +529,9 @@ static struct zebra_dplane_globals {  	_Atomic uint32_t dg_gre_set_in;  	_Atomic uint32_t dg_gre_set_errors; +	_Atomic uint32_t dg_intfs_in; +	_Atomic uint32_t dg_intf_errors; +  	/* Dataplane pthread */  	struct frr_pthread *dg_pthread; @@ -760,6 +766,9 @@ static void dplane_ctx_free_internal(struct zebra_dplane_ctx *ctx)  	case DPLANE_OP_NONE:  	case DPLANE_OP_IPSET_ADD:  	case DPLANE_OP_IPSET_DELETE: +	case DPLANE_OP_INTF_INSTALL: +	case DPLANE_OP_INTF_UPDATE: +	case DPLANE_OP_INTF_DELETE:  		break;  	case DPLANE_OP_IPSET_ENTRY_ADD: @@ -1073,6 +1082,16 @@ const char *dplane_op2str(enum dplane_op_e op)  	case DPLANE_OP_INTF_NETCONFIG:  		return "INTF_NETCONFIG"; + +	case DPLANE_OP_INTF_INSTALL: +		ret = "INTF_INSTALL"; +		break; +	case DPLANE_OP_INTF_UPDATE: +		ret = "INTF_UPDATE"; +		break; +	case DPLANE_OP_INTF_DELETE: +		ret = "INTF_DELETE"; +		break;  	}  	return ret; @@ -1771,6 +1790,27 @@ void dplane_ctx_set_intf_metric(struct zebra_dplane_ctx *ctx, uint32_t metric)  	ctx->u.intf.metric = metric;  } +uint32_t dplane_ctx_get_intf_pd_reason_val(const struct zebra_dplane_ctx *ctx) +{ +	DPLANE_CTX_VALID(ctx); + +	return ctx->u.intf.pd_reason_val; +} + +void dplane_ctx_set_intf_pd_reason_val(struct zebra_dplane_ctx *ctx, bool val) +{ +	DPLANE_CTX_VALID(ctx); + +	ctx->u.intf.pd_reason_val = val; +} + +bool dplane_ctx_intf_is_protodown(const struct zebra_dplane_ctx *ctx) +{ +	DPLANE_CTX_VALID(ctx); + +	return ctx->u.intf.protodown; +} +  /* Is interface addr p2p? */  bool dplane_ctx_intf_is_connected(const struct zebra_dplane_ctx *ctx)  { @@ -2638,6 +2678,73 @@ done:  	return ret;  } +/** + * dplane_ctx_intf_init() - Initialize a context block for a inteface update + * + * @ctx:	Dataplane context to init + * @op:		Operation being performed + * @ifp:	Interface + * + * Return:	Result status + */ +int dplane_ctx_intf_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op, +			 const struct interface *ifp) +{ +	struct zebra_ns *zns; +	struct zebra_if *zif; +	int ret = EINVAL; +	bool set_pdown, unset_pdown; + +	if (!ctx || !ifp) +		goto done; + +	ctx->zd_op = op; +	ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS; +	ctx->zd_vrf_id = ifp->vrf->vrf_id; + +	strlcpy(ctx->zd_ifname, ifp->name, sizeof(ctx->zd_ifname)); +	ctx->zd_ifindex = ifp->ifindex; + +	zns = zebra_ns_lookup(ifp->vrf->vrf_id); +	dplane_ctx_ns_init(ctx, zns, false); + + +	/* Copy over ifp info */ +	ctx->u.intf.metric = ifp->metric; +	ctx->u.intf.flags = ifp->flags; + +	/* Copy over extra zebra info, if available */ +	zif = (struct zebra_if *)ifp->info; + +	if (zif) { +		set_pdown = !!(zif->flags & ZIF_FLAG_SET_PROTODOWN); +		unset_pdown = !!(zif->flags & ZIF_FLAG_UNSET_PROTODOWN); + +		if (zif->protodown_rc && +		    ZEBRA_IF_IS_PROTODOWN_ONLY_EXTERNAL(zif) == false) +			ctx->u.intf.pd_reason_val = true; + +		/* +		 * See if we have new protodown state to set, otherwise keep +		 * current state +		 */ +		if (set_pdown) +			ctx->u.intf.protodown = true; +		else if (unset_pdown) +			ctx->u.intf.protodown = false; +		else +			ctx->u.intf.protodown = !!ZEBRA_IF_IS_PROTODOWN(zif); +	} + +	dplane_ctx_ns_init(ctx, zns, (op == DPLANE_OP_INTF_UPDATE)); +	ctx->zd_is_update = (op == DPLANE_OP_INTF_UPDATE); + +	ret = AOK; + +done: +	return ret; +} +  /*   * Capture information for an LSP update in a dplane context.   */ @@ -3824,6 +3931,85 @@ static enum zebra_dplane_result intf_addr_update_internal(  	return result;  } +/** + * dplane_intf_update_internal() - Helper for enqueuing interface changes + * + * @ifp:	Interface where the change occured + * @op:		The operation to be enqued + * + * Return:	Result of the change + */ +static enum zebra_dplane_result +dplane_intf_update_internal(const struct interface *ifp, enum dplane_op_e op) +{ +	enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE; +	int ret = EINVAL; +	struct zebra_dplane_ctx *ctx = NULL; + +	/* Obtain context block */ +	ctx = dplane_ctx_alloc(); +	if (!ctx) { +		ret = ENOMEM; +		goto done; +	} + +	ret = dplane_ctx_intf_init(ctx, op, ifp); +	if (ret == AOK) +		ret = dplane_update_enqueue(ctx); + +done: +	/* Update counter */ +	atomic_fetch_add_explicit(&zdplane_info.dg_intfs_in, 1, +				  memory_order_relaxed); + +	if (ret == AOK) +		result = ZEBRA_DPLANE_REQUEST_QUEUED; +	else { +		atomic_fetch_add_explicit(&zdplane_info.dg_intf_errors, 1, +					  memory_order_relaxed); +		if (ctx) +			dplane_ctx_free(&ctx); +	} + +	return result; +} + +/* + * Enqueue a interface add for the dataplane. + */ +enum zebra_dplane_result dplane_intf_add(const struct interface *ifp) +{ +	enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE; + +	if (ifp) +		ret = dplane_intf_update_internal(ifp, DPLANE_OP_INTF_INSTALL); +	return ret; +} + +/* + * Enqueue a interface update for the dataplane. + */ +enum zebra_dplane_result dplane_intf_update(const struct interface *ifp) +{ +	enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE; + +	if (ifp) +		ret = dplane_intf_update_internal(ifp, DPLANE_OP_INTF_UPDATE); +	return ret; +} + +/* + * Enqueue a interface delete for the dataplane. + */ +enum zebra_dplane_result dplane_intf_delete(const struct interface *ifp) +{ +	enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE; + +	if (ifp) +		ret = dplane_intf_update_internal(ifp, DPLANE_OP_INTF_DELETE); +	return ret; +} +  /*   * Enqueue vxlan/evpn mac add (or update).   */ @@ -5241,6 +5427,15 @@ static void kernel_dplane_log_detail(struct zebra_dplane_ctx *ctx)  			   dplane_ctx_get_netconf_mpls(ctx),  			   dplane_ctx_get_netconf_mcast(ctx));  		break; + +	case DPLANE_OP_INTF_INSTALL: +	case DPLANE_OP_INTF_UPDATE: +	case DPLANE_OP_INTF_DELETE: +		zlog_debug("Dplane intf %s, idx %u, protodown %d", +			   dplane_op2str(dplane_ctx_get_op(ctx)), +			   dplane_ctx_get_ifindex(ctx), +			   dplane_ctx_intf_is_protodown(ctx)); +		break;  	}  } @@ -5375,6 +5570,15 @@ static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx)  				&zdplane_info.dg_gre_set_errors, 1,  				memory_order_relaxed);  		break; + +	case DPLANE_OP_INTF_INSTALL: +	case DPLANE_OP_INTF_UPDATE: +	case DPLANE_OP_INTF_DELETE: +		if (res != ZEBRA_DPLANE_REQUEST_SUCCESS) +			atomic_fetch_add_explicit(&zdplane_info.dg_intf_errors, +						  1, memory_order_relaxed); +		break; +  	/* Ignore 'notifications' - no-op */  	case DPLANE_OP_SYS_ROUTE_ADD:  	case DPLANE_OP_SYS_ROUTE_DELETE: diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h index 29555d5b56..334d440a2f 100644 --- a/zebra/zebra_dplane.h +++ b/zebra/zebra_dplane.h @@ -188,6 +188,11 @@ enum dplane_op_e {  	/* Incoming interface config events */  	DPLANE_OP_INTF_NETCONFIG, + +	/* Interface update */ +	DPLANE_OP_INTF_INSTALL, +	DPLANE_OP_INTF_UPDATE, +	DPLANE_OP_INTF_DELETE,  };  /* @@ -480,6 +485,9 @@ dplane_ctx_get_pw_backup_nhg(const struct zebra_dplane_ctx *ctx);  /* Accessors for interface information */  uint32_t dplane_ctx_get_intf_metric(const struct zebra_dplane_ctx *ctx);  void dplane_ctx_set_intf_metric(struct zebra_dplane_ctx *ctx, uint32_t metric); +uint32_t dplane_ctx_get_intf_pd_reason_val(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_intf_pd_reason_val(struct zebra_dplane_ctx *ctx, bool val); +bool dplane_ctx_intf_is_protodown(const struct zebra_dplane_ctx *ctx);  /* Is interface addr p2p? */  bool dplane_ctx_intf_is_connected(const struct zebra_dplane_ctx *ctx);  void dplane_ctx_intf_set_connected(struct zebra_dplane_ctx *ctx); @@ -677,6 +685,13 @@ enum zebra_dplane_result dplane_intf_addr_unset(const struct interface *ifp,  						const struct connected *ifc);  /* + * Enqueue interface link changes for the dataplane. + */ +enum zebra_dplane_result dplane_intf_add(const struct interface *ifp); +enum zebra_dplane_result dplane_intf_update(const struct interface *ifp); +enum zebra_dplane_result dplane_intf_delete(const struct interface *ifp); + +/*   * Link layer operations for the dataplane.   */  enum zebra_dplane_result dplane_neigh_ip_update(enum dplane_op_e op, @@ -814,6 +829,10 @@ int dplane_ctx_route_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op,  int dplane_ctx_nexthop_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op,  			    struct nhg_hash_entry *nhe); +/* Encode interface information into data plane context. */ +int dplane_ctx_intf_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op, +			 const struct interface *ifp); +  /* Retrieve the limit on the number of pending, unprocessed updates. */  uint32_t dplane_get_in_queue_limit(void); diff --git a/zebra/zebra_errors.c b/zebra/zebra_errors.c index c3890f7220..7549a3d5c0 100644 --- a/zebra/zebra_errors.c +++ b/zebra/zebra_errors.c @@ -792,6 +792,15 @@ static struct log_ref ferr_zebra_err[] = {  		.suggestion = "Ignore this error.",  	},  	{ +		.code = EC_ZEBRA_INTF_UPDATE_FAILURE, +		.title = +			"Zebra failed to update interface in the kernel", +		.description = +			"Zebra made an attempt to update an interfce in the kernel, but it was not successful.", +		.suggestion = +			"Wait for Zebra to reattempt update.", +	}, +	{  		.code = END_FERR,  	}  }; diff --git a/zebra/zebra_errors.h b/zebra/zebra_errors.h index 540c6dd7d0..5164de09d6 100644 --- a/zebra/zebra_errors.h +++ b/zebra/zebra_errors.h @@ -136,6 +136,7 @@ enum zebra_log_refs {  	EC_ZEBRA_ES_CREATE,  	EC_ZEBRA_GRE_SET_UPDATE,  	EC_ZEBRA_SRV6M_UNRELEASED_LOCATOR_CHUNK, +	EC_ZEBRA_INTF_UPDATE_FAILURE,  };  void zebra_error_init(void); diff --git a/zebra/zebra_evpn_mh.c b/zebra/zebra_evpn_mh.c index 50eecd31d5..9099c066b1 100644 --- a/zebra/zebra_evpn_mh.c +++ b/zebra/zebra_evpn_mh.c @@ -3463,11 +3463,13 @@ void zebra_evpn_mh_json(json_object *json)  	if (zmh_info->protodown_rc) {  		json_array = json_object_new_array(); -		if (zmh_info->protodown_rc & ZEBRA_PROTODOWN_EVPN_STARTUP_DELAY) +		if (CHECK_FLAG(zmh_info->protodown_rc, +			       ZEBRA_PROTODOWN_EVPN_STARTUP_DELAY))  			json_object_array_add(  				json_array,  				json_object_new_string("startupDelay")); -		if (zmh_info->protodown_rc & ZEBRA_PROTODOWN_EVPN_UPLINK_DOWN) +		if (CHECK_FLAG(zmh_info->protodown_rc, +			       ZEBRA_PROTODOWN_EVPN_UPLINK_DOWN))  			json_object_array_add(  				json_array,  				json_object_new_string("uplinkDown")); @@ -3623,10 +3625,10 @@ bool zebra_evpn_is_es_bond_member(struct interface *ifp)  void zebra_evpn_mh_update_protodown_bond_mbr(struct zebra_if *zif, bool clear,  					     const char *caller)  { -	bool old_protodown;  	bool new_protodown; -	enum protodown_reasons old_protodown_rc = 0; -	enum protodown_reasons protodown_rc = 0; +	uint32_t old_protodown_rc = 0; +	uint32_t new_protodown_rc = 0; +	uint32_t protodown_rc = 0;  	if (!clear) {  		struct zebra_if *bond_zif; @@ -3635,32 +3637,23 @@ void zebra_evpn_mh_update_protodown_bond_mbr(struct zebra_if *zif, bool clear,  		protodown_rc = bond_zif->protodown_rc;  	} -	old_protodown = !!(zif->flags & ZIF_FLAG_PROTODOWN);  	old_protodown_rc = zif->protodown_rc; -	zif->protodown_rc &= ~ZEBRA_PROTODOWN_EVPN_ALL; -	zif->protodown_rc |= (protodown_rc & ZEBRA_PROTODOWN_EVPN_ALL); -	new_protodown = !!zif->protodown_rc; +	new_protodown_rc = (old_protodown_rc & ~ZEBRA_PROTODOWN_EVPN_ALL); +	new_protodown_rc |= (protodown_rc & ZEBRA_PROTODOWN_EVPN_ALL); +	new_protodown = !!new_protodown_rc; -	if (IS_ZEBRA_DEBUG_EVPN_MH_ES -	    && (zif->protodown_rc != old_protodown_rc)) +	if (IS_ZEBRA_DEBUG_EVPN_MH_ES && (new_protodown_rc != old_protodown_rc))  		zlog_debug(  			"%s bond mbr %s protodown_rc changed; old 0x%x new 0x%x",  			caller, zif->ifp->name, old_protodown_rc, -			zif->protodown_rc); - -	if (old_protodown == new_protodown) -		return; +			new_protodown_rc); -	if (new_protodown) -		zif->flags |= ZIF_FLAG_PROTODOWN; -	else -		zif->flags &= ~ZIF_FLAG_PROTODOWN; - -	if (IS_ZEBRA_DEBUG_EVPN_MH_ES) -		zlog_debug("%s protodown %s", zif->ifp->name, -			   new_protodown ? "on" : "off"); - -	zebra_if_set_protodown(zif->ifp, new_protodown); +	if (zebra_if_update_protodown_rc(zif->ifp, new_protodown, +					 new_protodown_rc) == 0) { +		if (IS_ZEBRA_DEBUG_EVPN_MH_ES) +			zlog_debug("%s protodown %s", zif->ifp->name, +				   new_protodown ? "on" : "off"); +	}  }  /* The bond members inherit the protodown reason code from the bond */ @@ -3683,7 +3676,7 @@ static void zebra_evpn_mh_update_protodown_es(struct zebra_evpn_es *es,  					      bool resync_dplane)  {  	struct zebra_if *zif; -	enum protodown_reasons old_protodown_rc; +	uint32_t old_protodown_rc;  	zif = es->zif;  	/* if the reason code is the same bail unless it is a new @@ -3714,7 +3707,7 @@ static void zebra_evpn_mh_update_protodown_es(struct zebra_evpn_es *es,  static void zebra_evpn_mh_clear_protodown_es(struct zebra_evpn_es *es)  {  	struct zebra_if *zif; -	enum protodown_reasons old_protodown_rc; +	uint32_t old_protodown_rc;  	zif = es->zif;  	if (!(zif->protodown_rc & ZEBRA_PROTODOWN_EVPN_ALL)) @@ -3742,10 +3735,9 @@ static void zebra_evpn_mh_update_protodown_es_all(void)  		zebra_evpn_mh_update_protodown_es(es, false /*resync_dplane*/);  } -static void zebra_evpn_mh_update_protodown(enum protodown_reasons protodown_rc, -					   bool set) +static void zebra_evpn_mh_update_protodown(uint32_t protodown_rc, bool set)  { -	enum protodown_reasons old_protodown_rc = zmh_info->protodown_rc; +	uint32_t old_protodown_rc = zmh_info->protodown_rc;  	if (set) {  		if ((protodown_rc & zmh_info->protodown_rc) == protodown_rc) diff --git a/zebra/zebra_evpn_mh.h b/zebra/zebra_evpn_mh.h index af6832092b..ce7b920de1 100644 --- a/zebra/zebra_evpn_mh.h +++ b/zebra/zebra_evpn_mh.h @@ -263,7 +263,7 @@ struct zebra_evpn_mh_info {  	uint32_t uplink_oper_up_cnt;  	/* These protodown bits are inherited by all ES bonds */ -	enum protodown_reasons protodown_rc; +	uint32_t protodown_rc;  };  /* returns TRUE if the EVPN is ready to be sent to BGP */ diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c index 469a94a65b..1b926dba5f 100644 --- a/zebra/zebra_nhg.c +++ b/zebra/zebra_nhg.c @@ -2993,6 +2993,9 @@ void zebra_nhg_dplane_result(struct zebra_dplane_ctx *ctx)  	case DPLANE_OP_INTF_ADDR_ADD:  	case DPLANE_OP_INTF_ADDR_DEL:  	case DPLANE_OP_INTF_NETCONFIG: +	case DPLANE_OP_INTF_INSTALL: +	case DPLANE_OP_INTF_UPDATE: +	case DPLANE_OP_INTF_DELETE:  		break;  	} diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index e376d4b2af..c6840a503c 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -4318,11 +4318,11 @@ static void rib_process_dplane_results(struct thread *thread)  			case DPLANE_OP_INTF_ADDR_ADD:  			case DPLANE_OP_INTF_ADDR_DEL: -				zebra_if_addr_update_ctx(ctx); -				break; - +			case DPLANE_OP_INTF_INSTALL: +			case DPLANE_OP_INTF_UPDATE: +			case DPLANE_OP_INTF_DELETE:  			case DPLANE_OP_INTF_NETCONFIG: -				zebra_if_netconf_update_ctx(ctx); +				zebra_if_dplane_result(ctx);  				break;  			/* Some op codes not handled here */ diff --git a/zebra/zebra_router.h b/zebra/zebra_router.h index 63a61d5293..7aca91959c 100644 --- a/zebra/zebra_router.h +++ b/zebra/zebra_router.h @@ -68,19 +68,27 @@ enum multicast_mode {   * physical device.   */  enum protodown_reasons { +	/* A process outside of FRR's control protodowned the interface */ +	ZEBRA_PROTODOWN_EXTERNAL = (1 << 0),  	/* On startup local ESs are held down for some time to  	 * allow the underlay to converge and EVPN routes to  	 * get learnt  	 */ -	ZEBRA_PROTODOWN_EVPN_STARTUP_DELAY = (1 << 0), +	ZEBRA_PROTODOWN_EVPN_STARTUP_DELAY = (1 << 1),  	/* If all the uplinks are down the switch has lost access  	 * to the VxLAN overlay and must shut down the access  	 * ports to allow servers to re-direct their traffic to  	 * other switches on the Ethernet Segment  	 */ -	ZEBRA_PROTODOWN_EVPN_UPLINK_DOWN = (1 << 1), -	ZEBRA_PROTODOWN_EVPN_ALL = (ZEBRA_PROTODOWN_EVPN_UPLINK_DOWN -				    | ZEBRA_PROTODOWN_EVPN_STARTUP_DELAY) +	ZEBRA_PROTODOWN_EVPN_UPLINK_DOWN = (1 << 2), +	ZEBRA_PROTODOWN_EVPN_ALL = (ZEBRA_PROTODOWN_EVPN_UPLINK_DOWN | +				    ZEBRA_PROTODOWN_EVPN_STARTUP_DELAY), +	ZEBRA_PROTODOWN_VRRP = (1 << 3), +	/* This reason used exclusively for testing */ +	ZEBRA_PROTODOWN_SHARP = (1 << 4), +	/* Just used to clear our fields on shutdown, externel not included */ +	ZEBRA_PROTODOWN_ALL = (ZEBRA_PROTODOWN_EVPN_ALL | ZEBRA_PROTODOWN_VRRP | +			       ZEBRA_PROTODOWN_SHARP)  };  #define ZEBRA_PROTODOWN_RC_STR_LEN 80 diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index cc1ba3d8f8..32bbfd6654 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -60,6 +60,7 @@  #include "northbound_cli.h"  #include "zebra/zebra_nb.h"  #include "zebra/kernel_netlink.h" +#include "zebra/if_netlink.h"  #include "zebra/table_manager.h"  #include "zebra/zebra_script.h"  #include "zebra/rtadv.h" @@ -4357,6 +4358,31 @@ DEFUN_HIDDEN(no_zebra_kernel_netlink_batch_tx_buf,  	return CMD_SUCCESS;  } +DEFPY (zebra_protodown_bit, +       zebra_protodown_bit_cmd, +       "zebra protodown reason-bit (0-31)$bit", +       ZEBRA_STR +       "Protodown Configuration\n" +       "Reason Bit used in the kernel for application\n" +       "Reason Bit range\n") +{ +	if_netlink_set_frr_protodown_r_bit(bit); +	return CMD_SUCCESS; +} + +DEFPY (no_zebra_protodown_bit, +       no_zebra_protodown_bit_cmd, +       "no zebra protodown reason-bit [(0-31)$bit]", +       NO_STR +       ZEBRA_STR +       "Protodown Configuration\n" +       "Reason Bit used in the kernel for setting protodown\n" +       "Reason Bit Range\n") +{ +	if_netlink_unset_frr_protodown_r_bit(); +	return CMD_SUCCESS; +} +  #endif /* HAVE_NETLINK */  DEFUN(ip_table_range, ip_table_range_cmd, @@ -4562,6 +4588,8 @@ void zebra_vty_init(void)  #ifdef HAVE_NETLINK  	install_element(CONFIG_NODE, &zebra_kernel_netlink_batch_tx_buf_cmd);  	install_element(CONFIG_NODE, &no_zebra_kernel_netlink_batch_tx_buf_cmd); +	install_element(CONFIG_NODE, &zebra_protodown_bit_cmd); +	install_element(CONFIG_NODE, &no_zebra_protodown_bit_cmd);  #endif /* HAVE_NETLINK */  #ifdef HAVE_SCRIPTING  | 
