diff options
| -rw-r--r-- | zebra/if_netlink.c | 232 | ||||
| -rw-r--r-- | zebra/if_netlink.h | 3 | ||||
| -rw-r--r-- | zebra/kernel_netlink.c | 7 | ||||
| -rw-r--r-- | zebra/rt.h | 1 | ||||
| -rw-r--r-- | zebra/rtread_netlink.c | 6 | ||||
| -rw-r--r-- | zebra/rtread_sysctl.c | 4 | ||||
| -rw-r--r-- | zebra/zebra_ns.c | 2 | ||||
| -rw-r--r-- | zebra/zebra_vxlan_if.c | 30 | ||||
| -rw-r--r-- | zebra/zebra_vxlan_if.h | 2 | 
9 files changed, 287 insertions, 0 deletions
diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index 02b9ab9d7f..22dd2b956d 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -2522,4 +2522,236 @@ int netlink_tunneldump_read(struct zebra_ns *zns)  	return 0;  } + +static const char *port_state2str(uint8_t state) +{ +	switch (state) { +	case BR_STATE_DISABLED: +		return "DISABLED"; +	case BR_STATE_LISTENING: +		return "LISTENING"; +	case BR_STATE_LEARNING: +		return "LEARNING"; +	case BR_STATE_FORWARDING: +		return "FORWARDING"; +	case BR_STATE_BLOCKING: +		return "BLOCKING"; +	} + +	return "UNKNOWN"; +} + +static void vxlan_vni_state_change(struct zebra_if *zif, uint16_t id, +				   uint8_t state) +{ +	struct zebra_vxlan_vni *vnip; + +	vnip = zebra_vxlan_if_vlanid_vni_find(zif, id); + +	if (!vnip) { +		if (IS_ZEBRA_DEBUG_VXLAN) +			zlog_debug( +				"Cannot find VNI for VID (%u) IF %s for vlan state update", +				id, zif->ifp->name); + +		return; +	} + +	switch (state) { +	case BR_STATE_FORWARDING: +		zebra_vxlan_if_vni_up(zif->ifp, vnip); +		break; +	case BR_STATE_BLOCKING: +		zebra_vxlan_if_vni_down(zif->ifp, vnip); +		break; +	case BR_STATE_DISABLED: +	case BR_STATE_LISTENING: +	case BR_STATE_LEARNING: +	default: +		/* Not used for anything at the moment */ +		break; +	} +} + +static void vlan_id_range_state_change(struct interface *ifp, uint16_t id_start, +				       uint16_t id_end, uint8_t state) +{ +	struct zebra_if *zif; + +	zif = (struct zebra_if *)ifp->info; + +	if (!zif) +		return; + +	for (uint16_t i = id_start; i <= id_end; i++) +		vxlan_vni_state_change(zif, i, state); +} + +/** + * netlink_vlan_change() - Read in change about vlans from the kernel + * + * @h:		Netlink message header + * @ns_id:	Namspace id + * @startup:	Are we reading under startup conditions? + * + * Return:	Result status + */ +int netlink_vlan_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) +{ +	int len, rem; +	struct interface *ifp; +	struct br_vlan_msg *bvm; +	struct bridge_vlan_info *vinfo; +	struct rtattr *vtb[BRIDGE_VLANDB_ENTRY_MAX + 1] = {}; +	struct rtattr *attr; +	uint8_t state; +	uint32_t vrange; +	int type; + +	/* We only care about state changes for now */ +	if (!(h->nlmsg_type == RTM_NEWVLAN)) +		return 0; + +	len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct br_vlan_msg)); +	if (len < 0) { +		zlog_warn( +			"%s: Message received from netlink is of a broken size %d %zu", +			__func__, h->nlmsg_len, +			(size_t)NLMSG_LENGTH(sizeof(struct br_vlan_msg))); +		return -1; +	} + +	bvm = NLMSG_DATA(h); + +	if (bvm->family != AF_BRIDGE) +		return 0; + +	ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(ns_id), bvm->ifindex); +	if (!ifp) { +		zlog_debug("Cannot find bridge-vlan IF (%u) for vlan update", +			   bvm->ifindex); +		return 0; +	} + +	if (!IS_ZEBRA_IF_VXLAN(ifp)) { +		if (IS_ZEBRA_DEBUG_KERNEL) +			zlog_debug("Ignoring non-vxlan IF (%s) for vlan update", +				   ifp->name); + +		return 0; +	} + +	if (IS_ZEBRA_DEBUG_KERNEL || IS_ZEBRA_DEBUG_VXLAN) +		zlog_debug("%s %s IF %s NS %u", +			   nl_msg_type_to_str(h->nlmsg_type), +			   nl_family_to_str(bvm->family), ifp->name, ns_id); + +	/* Loop over "ALL" BRIDGE_VLANDB_ENTRY */ +	rem = len; +	for (attr = BRVLAN_RTA(bvm); RTA_OK(attr, rem); +	     attr = RTA_NEXT(attr, rem)) { +		vinfo = NULL; +		state = 0; +		vrange = 0; + +		type = attr->rta_type & NLA_TYPE_MASK; + +		if (type != BRIDGE_VLANDB_ENTRY) +			continue; + +		/* Parse nested entry data */ +		netlink_parse_rtattr_nested(vtb, BRIDGE_VLANDB_ENTRY_MAX, attr); + +		/* It must have info for the ID */ +		if (!vtb[BRIDGE_VLANDB_ENTRY_INFO]) +			continue; + +		vinfo = (struct bridge_vlan_info *)RTA_DATA( +			vtb[BRIDGE_VLANDB_ENTRY_INFO]); + +		/* +		 * We only care about state info, if there is none, just ignore +		 * it. +		 */ +		if (!vtb[BRIDGE_VLANDB_ENTRY_STATE]) +			continue; + +		state = *(uint8_t *)RTA_DATA(vtb[BRIDGE_VLANDB_ENTRY_STATE]); + +		if (vtb[BRIDGE_VLANDB_ENTRY_RANGE]) +			vrange = *(uint32_t *)RTA_DATA( +				vtb[BRIDGE_VLANDB_ENTRY_RANGE]); + +		if (IS_ZEBRA_DEBUG_KERNEL || IS_ZEBRA_DEBUG_VXLAN) { +			if (vrange) +				zlog_debug("VLANDB_ENTRY: VID (%u-%u) state=%s", +					   vinfo->vid, vrange, +					   port_state2str(state)); +			else +				zlog_debug("VLANDB_ENTRY: VID (%u) state=%s", +					   vinfo->vid, port_state2str(state)); +		} + +		vlan_id_range_state_change( +			ifp, vinfo->vid, (vrange ? vrange : vinfo->vid), state); +	} + +	return 0; +} + +/** + * netlink_request_vlan() - Request vlan information from the kernel + * @zns:	Zebra namespace + * @family:	AF_* netlink family + * @type:	RTM_* type + * + * Return:	Result status + */ +static int netlink_request_vlan(struct zebra_ns *zns, int family, int type) +{ +	struct { +		struct nlmsghdr n; +		struct br_vlan_msg bvm; +		char buf[256]; +	} req; + +	/* Form the request, specifying filter (rtattr) if needed. */ +	memset(&req, 0, sizeof(req)); +	req.n.nlmsg_type = type; +	req.n.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST; +	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct br_vlan_msg)); +	req.bvm.family = family; + +	nl_attr_put32(&req.n, sizeof(req), BRIDGE_VLANDB_DUMP_FLAGS, +		      BRIDGE_VLANDB_DUMPF_STATS); + +	return netlink_request(&zns->netlink_cmd, &req); +} + +/** + * netlink_vlan_read() - Vlan read function using netlink interface + * + * @zns:	Zebra name space + * + * Return:	Result status + * Only called at bootstrap time. + */ +int netlink_vlan_read(struct zebra_ns *zns) +{ +	int ret; +	struct zebra_dplane_info dp_info; + +	zebra_dplane_info_from_zns(&dp_info, zns, true /*is_cmd*/); + +	/* Get bridg vlan info */ +	ret = netlink_request_vlan(zns, PF_BRIDGE, RTM_GETVLAN); +	if (ret < 0) +		return ret; + +	ret = netlink_parse_info(netlink_vlan_change, &zns->netlink_cmd, +				 &dp_info, 0, 1); + +	return ret; +} +  #endif /* GNU_LINUX */ diff --git a/zebra/if_netlink.h b/zebra/if_netlink.h index 21ae1713be..f215968657 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 int netlink_vlan_change(struct nlmsghdr *h, ns_id_t ns_id, int startup); +extern int netlink_vlan_read(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); diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c index 45fb24a210..2fd1543b31 100644 --- a/zebra/kernel_netlink.c +++ b/zebra/kernel_netlink.c @@ -124,6 +124,9 @@ static const struct message nlmsg_str[] = {{RTM_NEWROUTE, "RTM_NEWROUTE"},  					   {RTM_NEWTFILTER, "RTM_NEWTFILTER"},  					   {RTM_DELTFILTER, "RTM_DELTFILTER"},  					   {RTM_GETTFILTER, "RTM_GETTFILTER"}, +					   {RTM_NEWVLAN, "RTM_NEWVLAN"}, +					   {RTM_DELVLAN, "RTM_DELVLAN"}, +					   {RTM_GETVLAN, "RTM_GETVLAN"},  					   {0}};  static const struct message rtproto_str[] = { @@ -432,6 +435,10 @@ static int netlink_information_fetch(struct nlmsghdr *h, ns_id_t ns_id,  	case RTM_NEWTFILTER:  	case RTM_DELTFILTER:  		return netlink_tfilter_change(h, ns_id, startup); +	case RTM_NEWVLAN: +		return netlink_vlan_change(h, ns_id, startup); +	case RTM_DELVLAN: +		return netlink_vlan_change(h, ns_id, startup);  	/* Messages handled in the dplane thread */  	case RTM_NEWADDR: diff --git a/zebra/rt.h b/zebra/rt.h index f17d11a5fb..4ebf479754 100644 --- a/zebra/rt.h +++ b/zebra/rt.h @@ -129,6 +129,7 @@ extern void kernel_update_multi(struct dplane_ctx_list_head *ctx_list);   * Called by the dplane pthread to read incoming OS messages and dispatch them.   */  int kernel_dplane_read(struct zebra_dplane_info *info); +extern void vlan_read(struct zebra_ns *zns);  #ifdef __cplusplus  } diff --git a/zebra/rtread_netlink.c b/zebra/rtread_netlink.c index a85d8a4894..f9d4605562 100644 --- a/zebra/rtread_netlink.c +++ b/zebra/rtread_netlink.c @@ -28,6 +28,7 @@  #include "zebra/zebra_pbr.h"  #include "zebra/zebra_tc.h"  #include "zebra/rt_netlink.h" +#include "zebra/if_netlink.h"  #include "zebra/rule_netlink.h"  #include "zebra/tc_netlink.h" @@ -84,4 +85,9 @@ void kernel_read_tc_qdisc(struct zebra_ns *zns)  	netlink_qdisc_read(zns);  } +void vlan_read(struct zebra_ns *zns) +{ +	netlink_vlan_read(zns); +} +  #endif /* GNU_LINUX */ diff --git a/zebra/rtread_sysctl.c b/zebra/rtread_sysctl.c index c8a89b3a55..f5b918c022 100644 --- a/zebra/rtread_sysctl.c +++ b/zebra/rtread_sysctl.c @@ -118,4 +118,8 @@ void kernel_read_tc_qdisc(struct zebra_ns *zns)  {  } +void vlan_read(struct zebra_ns *zns) +{ +} +  #endif /* !defined(GNU_LINUX) */ diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c index 7b076b8a4a..457a4955b4 100644 --- a/zebra/zebra_ns.c +++ b/zebra/zebra_ns.c @@ -127,6 +127,8 @@ int zebra_ns_enable(ns_id_t ns_id, void **info)  	zebra_dplane_ns_enable(zns, true);  	interface_list(zns);  	route_read(zns); + +	vlan_read(zns);  	kernel_read_pbr_rules(zns);  	kernel_read_tc_qdisc(zns); diff --git a/zebra/zebra_vxlan_if.c b/zebra/zebra_vxlan_if.c index df9ad7795f..e01d06c71a 100644 --- a/zebra/zebra_vxlan_if.c +++ b/zebra/zebra_vxlan_if.c @@ -672,6 +672,36 @@ struct zebra_vxlan_vni *zebra_vxlan_if_vni_find(const struct zebra_if *zif,  	return vnip;  } +static int zif_vlanid_vni_walker(struct zebra_if *zif, +				 struct zebra_vxlan_vni *vnip, void *arg) +{ +	struct zebra_vxlan_if_vlan_ctx *ctx; + +	ctx = (struct zebra_vxlan_if_vlan_ctx *)arg; + +	if (vnip->access_vlan == ctx->vid) { +		ctx->vni = vnip; +		return HASHWALK_ABORT; +	} + +	return HASHWALK_CONTINUE; +} + +struct zebra_vxlan_vni *zebra_vxlan_if_vlanid_vni_find(struct zebra_if *zif, +						       vlanid_t vid) +{ +	struct zebra_vxlan_if_vlan_ctx ctx = {}; + +	if (!IS_ZEBRA_VXLAN_IF_SVD(zif)) +		return NULL; + +	ctx.vid = vid; + +	zebra_vxlan_if_vni_walk(zif, zif_vlanid_vni_walker, &ctx); + +	return ctx.vni; +} +  void zebra_vxlan_if_vni_iterate(struct zebra_if *zif,  				int (*func)(struct zebra_if *zif,  					    struct zebra_vxlan_vni *, void *), diff --git a/zebra/zebra_vxlan_if.h b/zebra/zebra_vxlan_if.h index a09e3dbe8c..e5ee6dc2d9 100644 --- a/zebra/zebra_vxlan_if.h +++ b/zebra/zebra_vxlan_if.h @@ -50,6 +50,8 @@ extern int zebra_vxlan_if_vni_table_create(struct zebra_if *zif);  extern int zebra_vxlan_if_vni_table_destroy(struct zebra_if *zif);  extern struct zebra_vxlan_vni *  zebra_vxlan_if_vni_find(const struct zebra_if *zif, vni_t vni); +extern struct zebra_vxlan_vni * +zebra_vxlan_if_vlanid_vni_find(struct zebra_if *zif, vlanid_t vni);  extern void zebra_vxlan_if_vni_iterate(struct zebra_if *zif,  				       int (*func)(struct zebra_if *zif,  						   struct zebra_vxlan_vni *,  | 
