diff options
Diffstat (limited to 'zebra/if_netlink.c')
| -rw-r--r-- | zebra/if_netlink.c | 232 | 
1 files changed, 232 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 */  | 
