[lldp-devel] [PATCH 1/3] vdp22 filter interface support

Thomas Richter tmricht at linux.vnet.ibm.com
Thu Nov 21 09:26:39 UTC 2013


The VDP22 protocol allows the return of changed filter
information data from the switch to the originator. This
requires a change in the netlink message format. Now lldpad
support 2 netlink message formats:
1. The draft 0.2 version. In this version the filter information
   data is received but not returned to the originator.

   The receive message format is:
   IFLA_IFNAME
   IFLA_VFINFO_LIST
        IFLA_VF_INFO
	     IFLA_VF_MAC
	     IFLA_VF_VLAN
	end
   IFLA_VF_PORTS
        IFLA_VF_PORT
	     IFLA_PORT_VSI_TYPE
	     IFLA_PORT_REQUEST
	     IFLA_PORT_INSTANCE_UUID
	end

   The returned message format sent as reply from lldpad to the
   originator is:
   IFLA_IFNAME
   IFLA_VF_PORTS
        IFLA_VF_PORT
	     IFLA_PORT_VF
	     IFLA_PORT_INSTANCE_UUID
	     IFLA_PORT_RESPONSE
	end

2. The ratified standard version VDP22 has the following
   netlink message format:
   IFLA_IFNAME
   IFLA_VF_PORTS
        IFLA_VF_PORT
	     IFLA_PORT_VSI_TYPE22
	     IFLA_PORT_VSI_FILTER
	     IFLA_PORT_REQUEST
	     IFLA_PORT_INSTANCE_UUID
	end

   The IFLA_VFINFO_LIST attribute has been removed. It was
   borrowed from other interfaces and the same information
   is now packed into the IFLA_PORT_VSI_FILTER attribute.

   The structure behind netlink attribute IFLA_PORT_VSI_TYPE22
   contains the modifications between draft 0.2 and the ratified
   standard. This is the new 16 byte manager identifier plus format
   and the new fields for VM actions (suspend/migrate).

   The structure behind netlink attribute IFLA_PORT_VSI_FILTER
   contains supplied vlan/qos, mac and group addresses.

   The returned message format sent as reply from lldpad to the
   originator is:
   IFLA_IFNAME
   IFLA_VF_PORTS
        IFLA_VF_PORT
	     IFLA_PORT_VF
	     IFLA_PORT_INSTANCE_UUID
	     IFLA_PORT_RESPONSE
	     IFLA_PORT_VSI_TYPE22 --> optional
	     IFLA_PORT_VSI_FILTER --> optional

   If the response does not contain modified filter information
   from the switch both netlink attributes IFLA_PORT_VSI_TYPE22
   and IFLA_PORT_VSI_FILTER are omitted.

When there is no changed filter information to report back
from the switch to the VSI originator, the reply message format
for draft 0.2 and ratified standard are the same.

The draft 0.2 implementation has not changed at all. Changed
filter information is not supported and not returned to the
VSI originator.

The function vdpnl_recv() can handle both netlink message formats.
The distinction between both formats is based on the contents of
message. Sanity checks are performed as well.
This means the new code is fully backward compatible and
does not break anything.

Signed-off-by: Thomas Richter <tmricht at linux.vnet.ibm.com>
---
 include/qbg_vdpnl.h |  40 +++
 qbg/vdpnl.c         | 703 ++++++++++++++++++++++++++++++++--------------------
 2 files changed, 474 insertions(+), 269 deletions(-)

diff --git a/include/qbg_vdpnl.h b/include/qbg_vdpnl.h
index ece030d..11e5fee 100644
--- a/include/qbg_vdpnl.h
+++ b/include/qbg_vdpnl.h
@@ -75,4 +75,44 @@ int vdp22_request(struct vdpnl_vsi *);
 int vdp_status(int, struct vdpnl_vsi *);
 int vdp22_status(int, struct vdpnl_vsi *);
 int event_trigger(struct nlmsghdr *, pid_t);
+
+/*
+ * New version, implemented as library function and header file in lldpad-devel
+ * package.
+ *
+ * Netlink message for QBG 2.2 ratified standard
+ */
+
+enum {					/* 802.1Qbg VDP ratified standard */
+	IFLA_PORT_VSI_TYPE22 = IFLA_PORT_MAX,
+	IFLA_PORT_VSI_FILTER,
+	__IFLA_PORT_MAX_NEW
+};
+
+#undef	IFLA_PORT_MAX
+#define IFLA_PORT_MAX (__IFLA_PORT_MAX_NEW - 1)
+
+/*
+ * Filter information data. Valid fields are determined by the
+ * filter information format type member named  'vsi_filter_fmt'. The
+ * number of the entries available is stored in the member named
+ * 'vsi_filter_num', see below.
+ */
+struct ifla_port_vsi_filter {
+	__u32 gpid;			/* Group Identifier*/
+	__u16 vlanid;			/* Vlan id and QoS */
+	__u8 mac[6];			/* MAC address */
+};
+
+struct ifla_port_vsi22 {		/* 802.1 Qbg Ratified standard */
+	__u8 vsi_mgrid[PORT_UUID_MAX];	/* Manager identifier */
+	__u8 vsi_uuid[PORT_UUID_MAX];	/* VSI identifier */
+	__u8 vsi_uuidfmt;		/* Format of UUID string */
+	__u8 vsi_type_id[3];		/* Type identifier */
+	__u8 vsi_type_version;		/* Type version identifier */
+	__u8 vsi_hints;			/* Hint bits */
+	__u8 vsi_filter_fmt;		/* Filter information format */
+	__u16 vsi_filter_num;		/* # of filter data entries */
+};
+
 #endif
diff --git a/qbg/vdpnl.c b/qbg/vdpnl.c
index 955447f..141b369 100644
--- a/qbg/vdpnl.c
+++ b/qbg/vdpnl.c
@@ -43,62 +43,6 @@
 #include "qbg_vdp22.h"
 #include "qbg_vdpnl.h"
 #include "qbg_utils.h"
-#include "lldp_rtnl.h"
-
-static struct nla_policy ifla_vf_policy[IFLA_VF_MAX + 1] = {
-	[IFLA_VF_MAC] = {
-		.minlen = sizeof(struct ifla_vf_mac),
-		.maxlen = sizeof(struct ifla_vf_mac)
-	},
-	[IFLA_VF_VLAN] = {
-		.minlen = sizeof(struct ifla_vf_vlan),
-		.maxlen = sizeof(struct ifla_vf_vlan)
-	}
-};
-
-static struct nla_policy ifla_port_policy[IFLA_PORT_MAX + 1] = {
-	[IFLA_PORT_VF]            = { .type = NLA_U32 },
-	[IFLA_PORT_PROFILE]       = { .type = NLA_STRING },
-	[IFLA_PORT_VSI_TYPE]      = { .minlen = sizeof(struct ifla_port_vsi) },
-	[IFLA_PORT_INSTANCE_UUID] = { .minlen = PORT_UUID_MAX,
-				      .maxlen = PORT_UUID_MAX, },
-	[IFLA_PORT_HOST_UUID]     = { .minlen = PORT_UUID_MAX,
-				      .maxlen = PORT_UUID_MAX, },
-	[IFLA_PORT_REQUEST]       = { .type = NLA_U8  },
-	[IFLA_PORT_RESPONSE]      = { .type = NLA_U16 },
-};
-
-/*
- * Retrieve name of interface and its index value from the netlink messaage
- * and store it in the data structure.
- * The GETLINK message may or may not contain the IFLA_IFNAME attribute.
- * Return 0 on success and errno on error.
- */
-static int vdpnl_get(struct nlmsghdr *nlh, struct vdpnl_vsi *p)
-{
-	struct nlattr *tb[IFLA_MAX + 1];
-	struct ifinfomsg *ifinfo;
-
-	if (nlmsg_parse(nlh, sizeof(struct ifinfomsg),
-			(struct nlattr **)&tb, IFLA_MAX, NULL)) {
-		LLDPAD_ERR("%s:error parsing GETLINK request\n", __func__);
-		return -EINVAL;
-	}
-
-	ifinfo = (struct ifinfomsg *)NLMSG_DATA(nlh);
-	p->ifindex = ifinfo->ifi_index;
-	if (tb[IFLA_IFNAME]) {
-		memcpy(p->ifname, (char *)RTA_DATA(tb[IFLA_IFNAME]),
-		       sizeof p->ifname);
-	} else if (!if_indextoname(p->ifindex, p->ifname)) {
-		LLDPAD_ERR("%s:ifindex %d without interface name\n", __func__,
-			   p->ifindex);
-		return -EINVAL;
-	}
-	LLDPAD_DBG("%s:IFLA_IFNAME:%s ifindex:%d\n", __func__, p->ifname,
-		   p->ifindex);
-	return 0;
-}
 
 static void vdpnl_show(struct vdpnl_vsi *vsi)
 {
@@ -106,14 +50,15 @@ static void vdpnl_show(struct vdpnl_vsi *vsi)
 	struct vdpnl_mac *mac;
 	int i;
 
-	LLDPAD_DBG("%s:IFLA_IFNAME:%s index:%d\n", __func__, vsi->ifname,
+	LLDPAD_DBG("%s:ifname:%s index:%d\n", __func__, vsi->ifname,
 		   vsi->ifindex);
 	for (i = 0, mac = vsi->maclist; i < vsi->macsz; ++i, ++mac) {
 		LLDPAD_DBG("%s:IFLA_VF_MAC:%2x:%2x:%2x:%2x:%2x:%2x\n",
 			   __func__, mac->mac[0], mac->mac[1], mac->mac[2],
 			   mac->mac[3], mac->mac[4], mac->mac[5]);
-		LLDPAD_DBG("%s:IFLA_VF_VLAN:%d QOS:%d\n", __func__, mac->vlan,
-			   mac->qos);
+		LLDPAD_DBG("%s:IFLA_VF_VLAN:%d qos:%d gpid:%ld\n", __func__,
+			   vdp22_get_vlanid(mac->vlan), vdp22_get_qos(mac->vlan),
+			   mac->gpid);
 	}
 	LLDPAD_DBG("%s:IFLA_PORT_VSI_TYPE:mgr_id:%d type_id:%ld "
 		   "typeid_version:%d\n",
@@ -126,228 +71,441 @@ static void vdpnl_show(struct vdpnl_vsi *vsi)
 }
 
 /*
- * Parse the IFLA_IFLA_VF_PORTIFLA_VF_PORTS block of the netlink message.
+ * Return the error code (can be zero) to the sender. Assume buffer is
+ * large enough to hold the information.
+ * Construct the netlink response on the input buffer.
+ */
+static int vdpnl_error(int err, struct nlmsghdr *from, size_t len)
+{
+	struct nlmsgerr nlmsgerr;
+
+	LLDPAD_DBG("%s:error %d\n", __func__, err);
+	nlmsgerr.error = err;
+	nlmsgerr.msg = *from;
+	memset(from, 0, len);
+	from->nlmsg_type = NLMSG_ERROR;
+	from->nlmsg_seq = nlmsgerr.msg.nlmsg_seq;
+	from->nlmsg_pid = nlmsgerr.msg.nlmsg_pid;
+	from->nlmsg_flags = 0;
+	from->nlmsg_len = NLMSG_SPACE(sizeof nlmsgerr);
+	memcpy(NLMSG_DATA(from), &nlmsgerr, sizeof nlmsgerr);
+	return from->nlmsg_len;
+}
+
+/*
+ * Read the contents of the IFLA_PORT_VSI_FILTER netlink attribute.
+ * It is an array of struct ifla_port_vsi_filter entries.
+ * Return 0 on success and errno on failure.
+ *
+ * Code to parse netlink message format 2.
+ */
+static void parse_filter_data(struct vdpnl_vsi *vsip, struct nlattr *tb)
+{
+	int i = 0;
+	struct ifla_port_vsi_filter elem[vsip->macsz];
+
+	nla_memcpy(elem, tb, sizeof(elem));
+	for (i = 0; i < vsip->macsz; ++i) {
+		struct vdpnl_mac *macp = &vsip->maclist[i];
+		struct ifla_port_vsi_filter *ep = &elem[i];
+
+		macp->vlan = ep->vlanid;
+		macp->gpid = ep->gpid;
+		memcpy(macp->mac, ep->mac, sizeof(macp->mac));
+	}
+}
+
+/*
+ * Parse the IFLA_VF_PORT block of the netlink message.
  * Return zero on success and errno else.
+ *
+ * Code to parse netlink message format 1. May contain IFLA_PORT_VSI_TYPE22
+ * and IFLA_PORT_VSI_FILTER attributes which make it netlink message format 2.
  */
-static int vdpnl_vfports(struct nlattr *vfports, struct vdpnl_vsi *vsi)
+static struct nla_policy  pc_vfport[IFLA_MAX + 1] = {
+	[IFLA_PORT_VF] = {
+				.type = NLA_U32
+			},
+	[IFLA_PORT_VSI_TYPE] = {
+				.minlen = sizeof(struct ifla_port_vsi)
+			},
+	[IFLA_PORT_INSTANCE_UUID] = {
+				.minlen = PORT_UUID_MAX
+			},
+	[IFLA_PORT_HOST_UUID] = {
+				.minlen = PORT_UUID_MAX
+			},
+	[IFLA_PORT_REQUEST] = {
+				.type = NLA_U8
+			},
+};
+
+static int parse_vf_port(struct vdpnl_vsi *vsi, struct nlattr *tb)
 {
 	char instance[VDP_UUID_STRLEN + 2];
-	struct nlattr *tb_vf_ports, *tb3[IFLA_PORT_MAX + 1];
-	int rem;
+	struct nlattr *vf[IFLA_PORT_MAX + 1];
+	size_t dim = sizeof(vf) / sizeof(vf[0]);
+	int rc;
 
-	if (!vfports) {
-		LLDPAD_DBG("%s:FOUND NO IFLA_VF_PORTS\n", __func__);
+	memset(vf, 0, sizeof(vf));
+	rc = nla_parse(vf, dim, nla_data(tb), nla_len(tb), pc_vfport);
+	if (rc) {
+		LLDPAD_ERR("%s:IFLA_VF_PORT parsing error\n", __func__);
 		return -EINVAL;
 	}
-
-	nla_for_each_nested(tb_vf_ports, vfports, rem) {
-		if (nla_type(tb_vf_ports) != IFLA_VF_PORT) {
-			LLDPAD_DBG("%s:not a IFLA_VF_PORT skipping\n",
+	if (vf[IFLA_PORT_VF])
+		vsi->vf = nla_get_u32(vf[IFLA_PORT_VF]);
+	if (vf[IFLA_PORT_HOST_UUID]) {
+		unsigned char *uuid;
+
+		uuid = (unsigned char *)nla_data(vf[IFLA_PORT_HOST_UUID]);
+		vdp_uuid2str(uuid, instance, sizeof(instance));
+		LLDPAD_DBG("%s:IFLA_PORT_HOST_UUID:%s\n", __func__, instance);
+	}
+	if (vf[IFLA_PORT_RESPONSE]) {
+		vsi->response = nla_get_u16(vf[IFLA_PORT_RESPONSE]);
+		LLDPAD_DBG("%s:IFLA_PORT_RESPONSE:%d\n", __func__,
+			    vsi->response);
+	}
+	if (vf[IFLA_PORT_VSI_TYPE]) {
+		struct ifla_port_vsi *p;
+
+		p = (struct ifla_port_vsi *)nla_data(vf[IFLA_PORT_VSI_TYPE]);
+		vsi->vsi_typeid = p->vsi_type_id[2] << 16
+				| p->vsi_type_id[1] << 8 | p->vsi_type_id[0];
+		vsi->vsi_mgrid = p->vsi_mgr_id;
+		vsi->vsi_typeversion = p->vsi_type_version;
+		vsi->nl_version = vdpnl_nlf1;
+		if (!vsi->maclist) {
+			LLDPAD_ERR("%s:VDP 0.2/2.2 filter error\n",
 				   __func__);
-			continue;
+			return -EINVAL;
 		}
-		if (nla_parse_nested(tb3, IFLA_PORT_MAX, tb_vf_ports,
-			ifla_port_policy)) {
-			LLDPAD_ERR("%s:IFLA_PORT_MAX parsing failed\n",
+	} else if(vf[IFLA_PORT_VSI_TYPE22] && vf[IFLA_PORT_VSI_FILTER]) {
+		struct ifla_port_vsi22 *p;
+
+		if (vsi->maclist) {
+			LLDPAD_ERR("%s:VDP 0.2/2.2 filter error\n",
 				   __func__);
 			return -EINVAL;
 		}
-		if (tb3[IFLA_PORT_VF])
-			LLDPAD_DBG("%s:IFLA_PORT_VF:%d\n", __func__,
-			    *(uint32_t *) RTA_DATA(tb3[IFLA_PORT_VF]));
-		if (tb3[IFLA_PORT_PROFILE])
-			LLDPAD_DBG("%s:IFLA_PORT_PROFILE:%s\n", __func__,
-				   (char *)RTA_DATA(tb3[IFLA_PORT_PROFILE]));
-		if (tb3[IFLA_PORT_HOST_UUID]) {
-			unsigned char *uuid;
-
-			uuid = (unsigned char *)
-				RTA_DATA(tb3[IFLA_PORT_HOST_UUID]);
-			vdp_uuid2str(uuid, instance, sizeof(instance));
-			LLDPAD_DBG("%s:IFLA_PORT_HOST_UUID:%s\n", __func__,
-				   instance);
-		}
-		if (tb3[IFLA_PORT_VSI_TYPE]) {
-			struct ifla_port_vsi *pvsi;
-			int tid = 0;
-
-			pvsi = (struct ifla_port_vsi *)
-			    RTA_DATA(tb3[IFLA_PORT_VSI_TYPE]);
-			tid = pvsi->vsi_type_id[2] << 16 |
-			    pvsi->vsi_type_id[1] << 8 |
-			    pvsi->vsi_type_id[0];
-			vsi->vsi_mgrid = pvsi->vsi_mgr_id;
-			vsi->vsi_typeversion = pvsi->vsi_type_version;
-			vsi->vsi_typeid = tid;
-		}
-		if (tb3[IFLA_PORT_INSTANCE_UUID]) {
-			unsigned char *uuid = (unsigned char *)
-				RTA_DATA(tb3[IFLA_PORT_INSTANCE_UUID]);
-			memcpy(vsi->vsi_uuid, uuid, sizeof vsi->vsi_uuid);
-		}
-		if (tb3[IFLA_PORT_REQUEST])
-			vsi->request =
-				*(uint8_t *) RTA_DATA(tb3[IFLA_PORT_REQUEST]);
-		if (tb3[IFLA_PORT_RESPONSE])
-			vsi->response =
-				*(uint16_t *) RTA_DATA(tb3[IFLA_PORT_RESPONSE]);
+		p = (struct ifla_port_vsi22 *)
+					nla_data(vf[IFLA_PORT_VSI_TYPE22]);
+		vsi->vsi_typeid = p->vsi_type_id[2] << 16
+				| p->vsi_type_id[1] << 8 | p->vsi_type_id[0];
+		vsi->vsi_typeversion = p->vsi_type_version;
+		vsi->hints = p->vsi_hints;
+		vsi->filter_fmt = p->vsi_filter_fmt;
+		vsi->vsiid_fmt = p->vsi_uuidfmt;
+		memcpy(vsi->vsi_mgrid2, p->vsi_mgrid, PORT_UUID_MAX);
+		vsi->nl_version = vdpnl_nlf2;
+		vsi->macsz = p->vsi_filter_num;
+		vsi->maclist = calloc(vsi->macsz, sizeof(*vsi->maclist));
+		if (!vsi->maclist)
+			return -ENOMEM;
+		parse_filter_data(vsi, vf[IFLA_PORT_VSI_FILTER]);
+	} else {
+		LLDPAD_ERR("%s:IFLA_PORT_VSI_TYPE/FILTER missing\n", __func__);
+		return -EINVAL;
+	}
+	if (vf[IFLA_PORT_INSTANCE_UUID])
+		nla_memcpy(vsi->vsi_uuid, vf[IFLA_PORT_INSTANCE_UUID],
+		       sizeof(vsi->vsi_uuid));
+	else {
+		LLDPAD_ERR("%s:IFLA_PORT_INSTANCE_UUID missing\n", __func__);
+		return -EINVAL;
+	}
+	if (vf[IFLA_PORT_REQUEST])
+		vsi->request = nla_get_u8(vf[IFLA_PORT_REQUEST]);
+	else {
+		LLDPAD_ERR("%s:IFLA_PORT_REQUEST missing\n", __func__);
+		return -EINVAL;
 	}
 	return 0;
 }
 
 /*
- * Parse the IFLA_VFINFO_LIST block of the netlink message.
+ * Parse the IFLA_VF_PORTS block of the netlink message. Expect one
+ * IFLA_VF_PORT attribute.
  * Return zero on success and errno else.
+ *
+ * Code to parse netlink message format 1.
  */
-static int vdpnl_vfinfolist(struct nlattr *vfinfolist, struct vdpnl_vsi *vsi)
+static int parse_vf_ports(struct vdpnl_vsi *vsi, struct nlattr *tb)
 {
-	struct nlattr *le1, *vf[IFLA_VF_MAX + 1];
-	int rem;
+	struct nlattr *vf[IFLA_VF_PORT_MAX + 1];
+	size_t dim = sizeof(vf) / sizeof(vf[0]);
+	int rc;
 
-	if (!vfinfolist) {
-		LLDPAD_ERR("%s:IFLA_VFINFO_LIST missing\n", __func__);
+	memset(vf, 0, sizeof(vf));
+	rc = nla_parse(vf, dim, nla_data(tb), nla_len(tb), 0);
+	if (rc || !vf[IFLA_VF_PORT]) {
+		LLDPAD_ERR("%s:IFLA_VF_PORT missing\n", __func__);
 		return -EINVAL;
 	}
-	nla_for_each_nested(le1, vfinfolist, rem) {
-		bool have_mac = false, have_vid = false;
+	return parse_vf_port(vsi, vf[IFLA_VF_PORT]);
+}
 
-		if (nla_type(le1) != IFLA_VF_INFO) {
-			LLDPAD_ERR("%s:parsing of IFLA_VFINFO_LIST failed\n",
-				   __func__);
-			return -EINVAL;
-		}
-		if (nla_parse_nested(vf, IFLA_VF_MAX, le1, ifla_vf_policy)) {
-			LLDPAD_ERR("%s:parsing of IFLA_VF_INFO failed\n",
-				   __func__);
-			return -EINVAL;
-		}
+/*
+ * Parse the IFLA_VF_INFO block of the netlink message.
+ * Return zero on success and errno else.
+ *
+ * Code and parse netlink message format 1.
+ */
+static struct nla_policy  pc_vlanmac[IFLA_MAX + 1] = {
+	[IFLA_VF_MAC] = {
+				.minlen = sizeof(struct ifla_vf_mac)
+			},
+	[IFLA_VF_VLAN] = {
+				.minlen = sizeof(struct ifla_vf_vlan)
+			}
+};
 
-		if (vf[IFLA_VF_MAC]) {
-			struct ifla_vf_mac *mac = RTA_DATA(vf[IFLA_VF_MAC]);
+static int parse_vf_info(struct vdpnl_vsi *vsi, struct nlattr *tb)
+{
+	struct nlattr *vf[IFLA_VF_MAX + 1];
+	size_t dim = sizeof(vf) / sizeof(vf[0]);
+	int rc;
+	bool have_mac = false, have_vid = false;
 
-			memcpy(vsi->maclist->mac, mac->mac, ETH_ALEN);
-			have_mac = true;
-		}
+	memset(vf, 0, sizeof(vf));
+	rc = nla_parse(vf, dim, nla_data(tb), nla_len(tb), pc_vlanmac);
+	if (rc || !vf[IFLA_VF_VLAN]) {
+		LLDPAD_ERR("%s:IFLA_VF_VLAN missing\n", __func__);
+		return -EINVAL;
+	}
+	vsi->nl_version = vdpnl_nlf1;
+	vsi->macsz = 1;
+	vsi->maclist = calloc(1, sizeof(*vsi->maclist));
+	if (!vsi->maclist)
+		return -ENOMEM;
+	if (vf[IFLA_VF_MAC]) {
+		struct ifla_vf_mac *mac = nla_data(vf[IFLA_VF_MAC]);
+
+		memcpy(vsi->maclist->mac, mac->mac, ETH_ALEN);
+		have_mac = true;
+	}
 
-		if (vf[IFLA_VF_VLAN]) {
-			struct ifla_vf_vlan *vlan = RTA_DATA(vf[IFLA_VF_VLAN]);
+	if (vf[IFLA_VF_VLAN]) {
+		struct ifla_vf_vlan *vlan = nla_data(vf[IFLA_VF_VLAN]);
 
-			vsi->maclist->vlan = vlan->vlan;
-			vsi->maclist->qos = vlan->qos;
+		vsi->maclist->vlan = vlan->vlan;
+		vsi->maclist->qos = vlan->qos;
+		if (vlan->vlan <= 4095 && vlan->qos <= 7)
 			have_vid = true;
-		}
-		LLDPAD_DBG("%s:have_vid:%d have_mac:%d\n", __func__, have_vid,
+	}
+	LLDPAD_DBG("%s:have_vid:%d have_mac:%d\n", __func__, have_vid,
 			   have_mac);
-		if (have_vid && have_mac)
-			vsi->filter_fmt = VDP22_FFMT_MACVID;
-		else if (have_vid)
-			vsi->filter_fmt = VDP22_FFMT_VID;
-		else
-			return -EINVAL;
+	if (have_vid && have_mac)
+		vsi->filter_fmt = VDP22_FFMT_MACVID;
+	else if (have_vid)
+		vsi->filter_fmt = VDP22_FFMT_VID;
+	else
+		rc = -EINVAL;
+	return rc;
+}
+
+/*
+ * Parse the IFLA_VFINFO_LIST block of the netlink message.
+ * Return zero on success and errno else.
+ *
+ * Code and parse netlink message format 1. This format supports only one
+ * mav/vlan pair.
+ */
+static int parse_vfinfo_list(struct vdpnl_vsi *vsi, struct nlattr *tb)
+{
+	struct nlattr *vf[IFLA_VF_INFO_MAX + 1];
+	size_t dim = sizeof(vf) / sizeof(vf[0]);
+	int rc;
+
+	memset(vf, 0, sizeof(vf));
+	rc = nla_parse(vf, dim, nla_data(tb), nla_len(tb), 0);
+	if (rc || !vf[IFLA_VF_INFO]) {
+		LLDPAD_ERR("%s:IFLA_VF_INFO missing\n", __func__);
+		return -EINVAL;
 	}
-	return 0;
+	return parse_vf_info(vsi, vf[IFLA_VF_INFO]);
+}
+
+/*
+ * Get interface name (either from netlink message for from ifi_index).
+ * Return error when no interface available.
+ */
+static int vdpnl_ifname(struct vdpnl_vsi *p, struct nlattr *tb)
+{
+	int rc = 0;
+
+	if (tb)
+		nla_strlcpy(p->ifname, tb, sizeof(p->ifname));
+	else if (!if_indextoname(p->ifindex, p->ifname)) {
+		LLDPAD_ERR("%s:ifindex %d without interface name\n", __func__,
+			   p->ifindex);
+		rc = -EINVAL;
+	}
+	return rc;
 }
 
 /*
- * Convert the SETLINK message into internal data structure.
+ * Parse a received netlink request message and return a filled VSI data
+ * structure.
+ *
+ * Code and parse netlink message format 1 and 2.
  */
-static int vdpnl_set(struct nlmsghdr *nlh, struct vdpnl_vsi *vsi)
+static struct nla_policy  pc_max[IFLA_MAX + 1] = {
+	[IFLA_IFNAME] = {
+				.minlen = 1,
+				.maxlen = IFNAMSIZ + 1,
+				.type = NLA_STRING
+			}
+};
+
+static int vdpnl_request_parse(struct vdpnl_vsi *vsip, struct nlmsghdr *nlh)
 {
+	struct ifinfomsg *ifinfo = NLMSG_DATA(nlh);
 	struct nlattr *tb[IFLA_MAX + 1];
-	struct ifinfomsg *ifinfo = (struct ifinfomsg *)NLMSG_DATA(nlh);
 	int rc;
 
-	if (nlmsg_parse(nlh, sizeof(struct ifinfomsg),
-			(struct nlattr **)&tb, IFLA_MAX, NULL)) {
+	memset(tb, 0, sizeof(tb));
+	vsip->req_pid = nlh->nlmsg_pid;
+	vsip->req_seq = nlh->nlmsg_seq;
+	rc = nla_parse(tb, sizeof(tb) / sizeof(tb[0]),
+			 (struct nlattr *)IFLA_RTA(NLMSG_DATA(nlh)),
+			 IFLA_PAYLOAD(nlh), pc_max);
+	if (rc || !tb[IFLA_VF_PORTS]) {
 		LLDPAD_ERR("%s:error parsing SETLINK request\n", __func__);
 		return -EINVAL;
 	}
-
-	vsi->ifindex = ifinfo->ifi_index;
-	if (tb[IFLA_IFNAME])
-		strncpy(vsi->ifname, (char *)RTA_DATA(tb[IFLA_IFNAME]),
-			sizeof vsi->ifname);
-	else {
-		if (!if_indextoname(ifinfo->ifi_index, vsi->ifname)) {
-			LLDPAD_ERR("%s:can not find name for interface %i\n",
-				   __func__, ifinfo->ifi_index);
-			return -ENXIO;
-		}
-	}
-	vsi->req_pid = nlh->nlmsg_pid;
-	vsi->req_seq = nlh->nlmsg_seq;
-	rc = vdpnl_vfinfolist(tb[IFLA_VFINFO_LIST], vsi);
-	if (!rc) {
-		rc = vdpnl_vfports(tb[IFLA_VF_PORTS], vsi);
-		if (!rc)
-			vdpnl_show(vsi);
+	vsip->ifindex = ifinfo->ifi_index;
+	rc = vdpnl_ifname(vsip, tb[IFLA_IFNAME]);
+	if (rc)
+		return rc;
+	if (tb[IFLA_VFINFO_LIST])	/* Netlink message format 1 */
+		rc = parse_vfinfo_list(vsip, tb[IFLA_VFINFO_LIST]);
+	if (!rc && tb[IFLA_VF_PORTS])	/* Netlink message format 1 or 2 */
+		rc = parse_vf_ports(vsip, tb[IFLA_VF_PORTS]);
+	if (rc) {
+		LLDPAD_ERR("%s:%s IFLA_VFINFO_LIST or IFLA_VF_PORTS error\n",
+			   __func__, vsip->ifname);
+		return -EINVAL;
 	}
 	return rc;
 }
 
 /*
- * Return the error code (can be zero) to the sender. Assume buffer is
- * large enough to hold the information.
- * Construct the netlink response on the input buffer.
+ * Parse incoming command and create a data structure to store the VSI data.
  */
-static int vdpnl_error(int err, struct nlmsghdr *from, size_t len)
+static int vdpnl_setlink(struct nlmsghdr *nlh, size_t len)
 {
-	struct nlmsgerr nlmsgerr;
+	int rc = -ENOMEM;
+	struct vdpnl_vsi p;
 
-	LLDPAD_DBG("%s:error %d\n", __func__, err);
-	nlmsgerr.error = err;
-	nlmsgerr.msg = *from;
-	memset(from, 0, len);
-	from->nlmsg_type = NLMSG_ERROR;
-	from->nlmsg_seq = nlmsgerr.msg.nlmsg_seq;
-	from->nlmsg_pid = nlmsgerr.msg.nlmsg_pid;
-	from->nlmsg_flags = 0;
-	from->nlmsg_len = NLMSG_SPACE(sizeof nlmsgerr);
-	memcpy(NLMSG_DATA(from), &nlmsgerr, sizeof nlmsgerr);
-	return from->nlmsg_len;
+	memset(&p, 0, sizeof p);
+	p.vsiid_fmt = VDP22_ID_UUID;
+	rc = vdpnl_request_parse(&p, nlh);
+	if (!rc)
+		rc = vdp22_query(p.ifname) ? vdp22_request(&p)
+					   : vdp_request(&p);
+	if (p.maclist)
+		free(p.maclist);
+	return vdpnl_error(rc, nlh, len);
 }
 
 /*
- * Build the first part of the netlink reply message for status inquiry.
- * It contains the header and the ifinfo data structure.
+ * Construct the vsi and filter information to be returned to originator.
  */
-static void vdpnl_reply1(struct vdpnl_vsi *p, struct nlmsghdr *nlh, size_t len)
+static void vdpnl_reply3(struct vdpnl_vsi *vsip, struct nl_msg *nlh)
 {
-	struct nlmsghdr to;
-	struct ifinfomsg ifinfo;
-
-	to.nlmsg_type = NLMSG_DONE;
-	to.nlmsg_seq = nlh->nlmsg_seq;
-	to.nlmsg_pid = nlh->nlmsg_pid;
-	to.nlmsg_flags = 0;
-	to.nlmsg_len = NLMSG_SPACE(sizeof ifinfo);
+	struct ifla_port_vsi22 p;
+	struct ifla_port_vsi_filter elem[vsip->macsz];
+	struct vdpnl_mac *macp = vsip->maclist;
+	int i;
 
-	memset(&ifinfo, 0, sizeof ifinfo);
-	ifinfo.ifi_index = p->ifindex;
-	memset(nlh, 0, len);
-	memcpy(nlh, &to, sizeof to);
-	memcpy(NLMSG_DATA(nlh), &ifinfo, sizeof ifinfo);
+	memset(&p, 0, sizeof(p));
+	memset(&elem, 0, sizeof(elem));
+	memcpy(p.vsi_mgrid, vsip->vsi_mgrid2, sizeof(p.vsi_mgrid));
+	p.vsi_uuidfmt = vsip->vsiid_fmt;
+	p.vsi_type_id[0] = (vsip->vsi_typeid >> 16) & 0xff;
+	p.vsi_type_id[1] = (vsip->vsi_typeid >> 8) & 0xff;
+	p.vsi_type_id[2] = vsip->vsi_typeid & 0xff;
+	p.vsi_type_version = vsip->vsi_typeversion;
+	p.vsi_filter_fmt = vsip->filter_fmt;
+	p.vsi_filter_num = vsip->macsz;
+	nla_put(nlh, IFLA_PORT_VSI_TYPE22, sizeof(p), &p);
+	for (i = 0; i < vsip->macsz; ++i, ++macp) {
+		elem[i].vlanid = vdp22_set_vlanid(macp->vlan);
+		if (macp->qos)
+			elem[i].vlanid |= vdp22_set_qos(macp->qos);
+		LLDPAD_DBG("%s:%s %d IFLA_VSI_FILTER_DATA_ELEM:%#x\n", __func__,
+			   vsip->ifname, i, elem[i].vlanid);
+	}
+	nla_put(nlh, IFLA_PORT_VSI_FILTER, sizeof(elem), elem);
 }
 
 /*
  * Build the variable part of the netlink reply message for status inquiry.
  * It contains the UUID and the response field for the VSI profile.
+ *
+ * If the vlan number changed, return the new vlan/qos to the caller.
  */
-static void vdpnl_reply2(struct vdpnl_vsi *p, struct nlmsghdr *nlh)
+static void vdpnl_reply2(struct vdpnl_vsi *p, struct nl_msg *nlh)
 {
 	char instance[VDP_UUID_STRLEN + 2];
 
-	mynla_put(nlh, IFLA_PORT_INSTANCE_UUID, sizeof p->vsi_uuid,
+	nla_put(nlh, IFLA_PORT_INSTANCE_UUID, sizeof p->vsi_uuid,
 		  p->vsi_uuid);
 	vdp_uuid2str(p->vsi_uuid, instance, sizeof instance);
 	LLDPAD_DBG("%s:IFLA_PORT_INSTANCE_UUID:%s\n", __func__, instance);
-	mynla_put_u32(nlh, IFLA_PORT_VF, PORT_SELF_VF);
+	nla_put_u32(nlh, IFLA_PORT_VF, PORT_SELF_VF);
 	LLDPAD_DBG("%s:IFLA_PORT_VF:%d\n", __func__,  PORT_SELF_VF);
 	if (p->response != VDP_RESPONSE_NO_RESPONSE) {
-		mynla_put_u16(nlh, IFLA_PORT_RESPONSE, p->response);
+		nla_put_u16(nlh, IFLA_PORT_RESPONSE, p->response);
 		LLDPAD_DBG("%s:IFLA_PORT_RESPONSE:%d\n", __func__,
 			   p->response);
+		if (p->macsz && p->maclist->changed)
+			vdpnl_reply3(p, nlh);
+	}
+}
+
+/*
+ * Return bytes needed  netlink message for one VSI.
+ */
+static size_t vdp_nllen(unsigned short macsz)
+{
+	size_t needed;
+
+	needed = nla_total_size(sizeof(struct nlattr)); /* IFLA_VF_PORT */
+	needed += nla_total_size(4);	/* IFLA_PORT_VF */
+	needed += nla_total_size(PORT_UUID_MAX); /* IFLA_PORT_INSTANCE_UUID */
+	needed += nla_total_size(2);	/* IFLA_PORT_RESPONSE */
+	needed += nla_total_size(sizeof(struct ifla_port_vsi22));
+	needed += nla_total_size(sizeof(struct nlattr)
+				+ macsz * sizeof(struct ifla_port_vsi_filter));
+	return needed;
+}
+
+
+/*
+ * Retrieve name of interface and its index value from the netlink messaage
+ * and store it in the data structure.
+ * The GETLINK message may or may not contain the IFLA_IFNAME attribute.
+ * Return 0 on success and errno on error.
+ */
+static int vdpnl_get(struct vdpnl_vsi *p, struct nlmsghdr *nlh)
+{
+	int rc;
+	struct nlattr *tb[IFLA_MAX + 1];
+	struct ifinfomsg *ifinfo = (struct ifinfomsg *)NLMSG_DATA(nlh);
+
+	memset(tb, 0, sizeof(tb));
+	rc = nla_parse(tb, sizeof(tb) / sizeof(tb[0]),
+			 (struct nlattr *)IFLA_RTA(NLMSG_DATA(nlh)),
+			 IFLA_PAYLOAD(nlh), pc_max);
+	if (rc) {
+		LLDPAD_ERR("%s:error parsing GETLINK request\n", __func__);
+		return -EINVAL;
 	}
+	p->ifindex = ifinfo->ifi_index;
+	return vdpnl_ifname(p, tb[IFLA_IFNAME]);
 }
 
 /*
@@ -359,24 +517,40 @@ static void vdpnl_reply2(struct vdpnl_vsi *p, struct nlmsghdr *nlh)
  */
 static int vdpnl_getlink(struct nlmsghdr *nlh, size_t len)
 {
+	struct nlmsghdr *nlh_new;
+	struct nl_msg *msg;
 	struct vdpnl_vsi p;
-	int i = 0, rc;
+	int i = 0, rc = -ENOMEM;
 	struct nlattr *vf_ports, *vf_port;
+	struct ifinfomsg ifinfo;
+	size_t mylen = nla_total_size(sizeof(struct ifinfomsg))
+			+ nla_total_size(sizeof(struct nlattr));
+			/* Header + IFLA_VF_PORTS */
 
+	mylen = nlmsg_total_size(mylen);
+	memset(&ifinfo, 0, sizeof ifinfo);
 	memset(&p, 0, sizeof p);
-	rc = vdpnl_get(nlh, &p);
-	if (rc)
+	msg = nlmsg_alloc();
+	if (msg)
+		rc = vdpnl_get(&p, nlh);
+	if (rc) {
+		nlmsg_free(msg);
 		return vdpnl_error(rc, nlh, len);
-	vdpnl_reply1(&p, nlh, len);
-	vf_ports = mynla_nest_start(nlh, IFLA_VF_PORTS);
+	}
+	nlmsg_put(msg, nlh->nlmsg_pid, nlh->nlmsg_seq, NLMSG_DONE, 0, 0);
+	ifinfo.ifi_index = p.ifindex;
+	nlmsg_append(msg, &ifinfo, sizeof(ifinfo), 0);
+
+	vf_ports = nla_nest_start(msg, IFLA_VF_PORTS);
 	/* Iterate over all profiles */
 	do {
 		rc = vdp22_query(p.ifname) ? vdp22_status(++i, &p)
 					   : vdp_status(++i, &p);
-		if (rc == 1) {
-			vf_port = mynla_nest_start(nlh, IFLA_VF_PORT);
-			vdpnl_reply2(&p, nlh);
-			mynla_nest_end(nlh, vf_port);
+		mylen += vdp_nllen(p.macsz);
+		if (rc == 1 && mylen < len) {
+			vf_port = nla_nest_start(msg, IFLA_VF_PORT);
+			vdpnl_reply2(&p, msg);
+			nla_nest_end(msg, vf_port);
 		}
 		if (p.maclist) {
 			free(p.maclist);
@@ -384,32 +558,17 @@ static int vdpnl_getlink(struct nlmsghdr *nlh, size_t len)
 			p.macsz = 0;
 		}
 	} while (rc == 1);
-	mynla_nest_end(nlh, vf_ports);
-	if (rc < 0)
+	nla_nest_end(msg, vf_ports);
+	if (rc < 0) {
+		nlmsg_free(msg);
 		return vdpnl_error(rc, nlh, len);
-	LLDPAD_DBG("%s:message-size:%d\n", __func__, nlh->nlmsg_len);
-	return nlh->nlmsg_len;
-}
-
-/*
- * Parse incoming command and create a data structure to store the VSI data.
- */
-static int vdpnl_setlink(struct nlmsghdr *nlh, size_t len)
-{
-	int rc = -ENOMEM;
-	struct vdpnl_mac mac;
-	struct vdpnl_vsi p;
-
-	memset(&p, 0, sizeof p);
-	memset(&mac, 0, sizeof mac);
-	p.vsiid_fmt = VDP22_ID_UUID;
-	p.macsz = 1;
-	p.maclist = &mac;
-	rc = vdpnl_set(nlh, &p);
-	if (!rc)
-		rc = vdp22_query(p.ifname) ? vdp22_request(&p)
-					   : vdp_request(&p);
-	return vdpnl_error(rc, nlh, len);
+	}
+	nlh_new = nlmsg_hdr(msg);
+	rc = nlh_new->nlmsg_len;
+	memcpy((unsigned char *)nlh, nlh_new, rc);
+	nlmsg_free(msg);
+	LLDPAD_DBG("%s:message-size:%d\n", __func__, rc);
+	return rc;
 }
 
 /*
@@ -446,7 +605,7 @@ int vdpnl_recv(unsigned char *buf, size_t buflen)
 /*
  * Add one entry in the list of MAC,VLAN pairs.
  */
-static void add_pair(struct vdpnl_mac *mac, struct nlmsghdr *nlh)
+static void add_pair(struct vdpnl_mac *mac, struct nl_msg *nlh)
 {
 	struct nlattr *vfinfo;
 	struct ifla_vf_mac ifla_vf_mac = {
@@ -459,25 +618,25 @@ static void add_pair(struct vdpnl_mac *mac, struct nlmsghdr *nlh)
 		.qos = mac->qos
 	};
 
-	vfinfo = mynla_nest_start(nlh, IFLA_VF_INFO);
+	vfinfo = nla_nest_start(nlh, IFLA_VF_INFO);
 	memcpy(ifla_vf_mac.mac, mac->mac, sizeof mac->mac);
-	mynla_put(nlh, IFLA_VF_MAC, sizeof ifla_vf_mac, &ifla_vf_mac);
-	mynla_put(nlh, IFLA_VF_VLAN, sizeof ifla_vf_vlan, &ifla_vf_vlan);
-	mynla_nest_end(nlh, vfinfo);
+	nla_put(nlh, IFLA_VF_MAC, sizeof ifla_vf_mac, &ifla_vf_mac);
+	nla_put(nlh, IFLA_VF_VLAN, sizeof ifla_vf_vlan, &ifla_vf_vlan);
+	nla_nest_end(nlh, vfinfo);
 }
 
 /*
  * Walk along the MAC,VLAN ID list and add each entry into the message.
  */
-static void add_mac_vlan(struct vdpnl_vsi *vsi, struct nlmsghdr *nlh)
+static void add_mac_vlan(struct vdpnl_vsi *vsi, struct nl_msg *nlh)
 {
 	struct nlattr *vfinfolist;
 	int i;
 
-	vfinfolist = mynla_nest_start(nlh, IFLA_VFINFO_LIST);
+	vfinfolist = nla_nest_start(nlh, IFLA_VFINFO_LIST);
 	for (i = 0; i < vsi->macsz; ++i)
 		add_pair(&vsi->maclist[i], nlh);
-	mynla_nest_end(nlh, vfinfolist);
+	nla_nest_end(nlh, vfinfolist);
 }
 
 /*
@@ -488,40 +647,46 @@ static void add_mac_vlan(struct vdpnl_vsi *vsi, struct nlmsghdr *nlh)
  */
 int vdpnl_send(struct vdpnl_vsi *vsi)
 {
-	unsigned char buf[MAX_PAYLOAD];
-	struct nlmsghdr *nlh = (struct nlmsghdr *)buf;
+	struct nlmsghdr *nlh;
 	struct nlattr *vf_ports, *vf_port;
 	struct ifinfomsg ifinfo;
 	struct ifla_port_vsi portvsi;
+	struct nl_msg *msg;
+	int rc = -ENOMEM;
 
-	memset(buf, 0, sizeof buf);
-	nlh->nlmsg_pid = getpid();
-	nlh->nlmsg_seq = vsi->req_seq;
-	nlh->nlmsg_type = RTM_SETLINK;
-	nlh->nlmsg_len = NLMSG_SPACE(sizeof ifinfo);
+	msg = nlmsg_alloc();
+	if (!msg) {
+		LLDPAD_DBG("%s:%s error allocating netlink message memory:\n",
+			   __func__, vsi->ifname);
+		return rc;
+	}
+	nlmsg_put(msg,  getpid(), vsi->req_seq, RTM_SETLINK, 0, 0);
 
 	memset(&ifinfo, 0, sizeof ifinfo);
 	ifinfo.ifi_index = vsi->ifindex;
-	memcpy(NLMSG_DATA(nlh), &ifinfo, sizeof ifinfo);
-	mynla_put(nlh, IFLA_IFNAME, 1 + strlen(vsi->ifname), vsi->ifname);
+	nlmsg_append(msg, &ifinfo, sizeof(ifinfo), 0);
+	nla_put_string(msg, IFLA_IFNAME, vsi->ifname);
 
-	add_mac_vlan(vsi, nlh);
+	add_mac_vlan(vsi, msg);
 	portvsi.vsi_mgr_id = vsi->vsi_mgrid;
 	portvsi.vsi_type_id[0] = vsi->vsi_typeid & 0xff;
 	portvsi.vsi_type_id[1] = (vsi->vsi_typeid >> 8) & 0xff;
 	portvsi.vsi_type_id[2] = (vsi->vsi_typeid >> 16) & 0xff;
 	portvsi.vsi_type_version = vsi->vsi_typeversion;
-	vf_ports = mynla_nest_start(nlh, IFLA_VF_PORTS);
-	vf_port = mynla_nest_start(nlh, IFLA_VF_PORT);
-	mynla_put(nlh, IFLA_PORT_VSI_TYPE, sizeof portvsi, &portvsi);
-	mynla_put(nlh, IFLA_PORT_INSTANCE_UUID, PORT_UUID_MAX, vsi->vsi_uuid);
-	mynla_put_u32(nlh, IFLA_PORT_VF, PORT_SELF_VF);
-	mynla_put_u16(nlh, IFLA_PORT_REQUEST, vsi->request);
-	mynla_nest_end(nlh, vf_port);
-	mynla_nest_end(nlh, vf_ports);
+	vf_ports = nla_nest_start(msg, IFLA_VF_PORTS);
+	vf_port = nla_nest_start(msg, IFLA_VF_PORT);
+	nla_put(msg, IFLA_PORT_VSI_TYPE, sizeof portvsi, &portvsi);
+	nla_put(msg, IFLA_PORT_INSTANCE_UUID, PORT_UUID_MAX, vsi->vsi_uuid);
+	nla_put_u32(msg, IFLA_PORT_VF, PORT_SELF_VF);
+	nla_put_u16(msg, IFLA_PORT_REQUEST, vsi->request);
+	nla_nest_end(msg, vf_port);
+	nla_nest_end(msg, vf_ports);
 	vdpnl_show(vsi);
+	nlh = nlmsg_hdr(msg);
 	LLDPAD_DBG("%s:nlh.nl_pid:%d nlh_type:%d nlh_seq:%d nlh_len:%d\n",
 		    __func__, nlh->nlmsg_pid, nlh->nlmsg_type, nlh->nlmsg_seq,
 		    nlh->nlmsg_len);
-	return event_trigger(nlh, vsi->req_pid);
+	rc = event_trigger(nlh, vsi->req_pid);
+	nlmsg_free(msg);
+	return rc;
 }
-- 
1.8.3.1



More information about the lldp-devel mailing list