[ovs-dev] [netlink v5 55/61] datapath: Convert ODP_VPORT_* to use AF_NETLINK socket layer.

Ben Pfaff blp at nicira.com
Thu Jan 27 00:23:38 UTC 2011


This commit calls genl_lock() and thus doesn't support Linux before
2.6.35, which wasn't exported before that version.  That problem will
be fixed once the whole userspace interface transitions to Generic
Netlink a few commits from now.

Signed-off-by: Ben Pfaff <blp at nicira.com>
---
 datapath/datapath.c                     |  345 +++++++++++++++----------------
 include/openvswitch/datapath-protocol.h |   63 ++++--
 lib/dpif-linux.c                        |  153 +++++++-------
 lib/dpif-linux.h                        |    5 +-
 lib/netdev-vport.c                      |    6 +-
 5 files changed, 295 insertions(+), 277 deletions(-)

diff --git a/datapath/datapath.c b/datapath/datapath.c
index f42ead1..36cc803 100644
--- a/datapath/datapath.c
+++ b/datapath/datapath.c
@@ -1530,32 +1530,47 @@ static struct genl_ops dp_datapath_genl_ops[] = {
 };
 
 static const struct nla_policy vport_policy[ODP_VPORT_ATTR_MAX + 1] = {
+#ifdef HAVE_NLA_NUL_STRING
 	[ODP_VPORT_ATTR_NAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ - 1 },
 	[ODP_VPORT_ATTR_PORT_NO] = { .type = NLA_U32 },
 	[ODP_VPORT_ATTR_TYPE] = { .type = NLA_U32 },
 	[ODP_VPORT_ATTR_STATS] = { .len = sizeof(struct rtnl_link_stats64) },
 	[ODP_VPORT_ATTR_ADDRESS] = { .len = ETH_ALEN },
+#else
+	[ODP_VPORT_ATTR_STATS] = { .minlen = sizeof(struct rtnl_link_stats64) },
+	[ODP_VPORT_ATTR_ADDRESS] = { .minlen = ETH_ALEN },
+#endif
 	[ODP_VPORT_ATTR_MTU] = { .type = NLA_U32 },
 	[ODP_VPORT_ATTR_OPTIONS] = { .type = NLA_NESTED },
 };
 
-/* Called with RCU read lock. */
-static struct sk_buff *odp_vport_build_info(struct vport *vport, uint32_t total_len)
+static struct genl_family dp_vport_genl_family = {
+	.id = GENL_ID_GENERATE,
+	.hdrsize = sizeof(struct odp_header),
+	.name = ODP_VPORT_FAMILY,
+	.version = 1,
+	.maxattr = ODP_VPORT_ATTR_MAX
+};
+
+static struct genl_multicast_group dp_vport_multicast_group = {
+	.name = ODP_VPORT_MCGROUP
+};
+
+/* Called with RTNL lock or RCU read lock. */
+static int odp_vport_cmd_fill_info(struct vport *vport, struct sk_buff *skb,
+				   u32 pid, u32 seq, u32 flags, u8 cmd)
 {
-	struct odp_vport *odp_vport;
-	struct sk_buff *skb;
+	struct odp_header *odp_header;
 	struct nlattr *nla;
 	int ifindex, iflink;
 	int err;
 
-	skb = alloc_skb(NLMSG_GOODSIZE, GFP_ATOMIC);
-	err = -ENOMEM;
-	if (!skb)
-		goto err;
+	odp_header = genlmsg_put(skb, pid, seq, &dp_vport_genl_family,
+				 flags, cmd);
+	if (!odp_header)
+		return -EMSGSIZE;
 
-	odp_vport = (struct odp_vport*)__skb_put(skb, sizeof(struct odp_vport));
-	odp_vport->dp_idx = vport->dp->dp_idx;
-	odp_vport->total_len = total_len;
+	odp_header->dp_idx = vport->dp->dp_idx;
 
 	NLA_PUT_U32(skb, ODP_VPORT_ATTR_PORT_NO, vport->port_no);
 	NLA_PUT_U32(skb, ODP_VPORT_ATTR_TYPE, vport_get_type(vport));
@@ -1572,6 +1587,8 @@ static struct sk_buff *odp_vport_build_info(struct vport *vport, uint32_t total_
 	NLA_PUT_U32(skb, ODP_VPORT_ATTR_MTU, vport_get_mtu(vport));
 
 	err = vport_get_options(vport, skb);
+	if (err)
+		goto error;
 
 	ifindex = vport_get_ifindex(vport);
 	if (ifindex > 0)
@@ -1581,65 +1598,41 @@ static struct sk_buff *odp_vport_build_info(struct vport *vport, uint32_t total_
 	if (iflink > 0)
 		NLA_PUT_U32(skb, ODP_VPORT_ATTR_IFLINK, iflink);
 
-	err = -EMSGSIZE;
-	if (skb->len > total_len)
-		goto err_free;
-
-	odp_vport->len = skb->len;
-	return skb;
+	return genlmsg_end(skb, odp_header);
 
 nla_put_failure:
 	err = -EMSGSIZE;
-err_free:
-	kfree_skb(skb);
-err:
-	return ERR_PTR(err);
+error:
+	genlmsg_cancel(skb, odp_header);
+	return err;
 }
 
-static struct sk_buff *copy_vport_from_user(struct odp_vport __user *uodp_vport,
-					    struct nlattr *a[ODP_VPORT_ATTR_MAX + 1])
+/* Called with RTNL lock or RCU read lock. */
+static struct sk_buff *odp_vport_cmd_build_info(struct vport *vport, u32 pid,
+						u32 seq, u8 cmd)
 {
-	struct odp_vport *odp_vport;
 	struct sk_buff *skb;
-	u32 len;
-	int err;
-
-	if (get_user(len, &uodp_vport->len))
-		return ERR_PTR(-EFAULT);
-	if (len < sizeof(struct odp_vport))
-		return ERR_PTR(-EINVAL);
+	int retval;
 
-	skb = alloc_skb(len, GFP_KERNEL);
+	skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
 	if (!skb)
 		return ERR_PTR(-ENOMEM);
 
-	err = -EFAULT;
-	if (copy_from_user(__skb_put(skb, len), uodp_vport, len))
-		goto error_free_skb;
-
-	odp_vport = (struct odp_vport *)skb->data;
-	err = -EINVAL;
-	if (odp_vport->len != len)
-		goto error_free_skb;
-
-	err = nla_parse(a, ODP_VPORT_ATTR_MAX, (struct nlattr *)(skb->data + sizeof(struct odp_vport)),
-			skb->len - sizeof(struct odp_vport), vport_policy);
-	if (err)
-		goto error_free_skb;
-
-	err = VERIFY_NUL_STRING(a[ODP_VPORT_ATTR_NAME], IFNAMSIZ - 1);
-	if (err)
-		goto error_free_skb;
-
+	retval = odp_vport_cmd_fill_info(vport, skb, pid, seq, 0, cmd);
+	if (retval < 0) {
+		kfree_skb(skb);
+		return ERR_PTR(retval);
+	}
 	return skb;
+}
 
-error_free_skb:
-	kfree_skb(skb);
-	return ERR_PTR(err);
+static int odp_vport_cmd_validate(struct nlattr *a[ODP_VPORT_ATTR_MAX + 1])
+{
+	return VERIFY_NUL_STRING(a[ODP_VPORT_ATTR_NAME], IFNAMSIZ - 1);
 }
 
 /* Called with RTNL lock or RCU read lock. */
-static struct vport *lookup_vport(struct odp_vport *odp_vport,
+static struct vport *lookup_vport(struct odp_header *odp_header,
 				  struct nlattr *a[ODP_VPORT_ATTR_MAX + 1])
 {
 	struct datapath *dp;
@@ -1654,9 +1647,9 @@ static struct vport *lookup_vport(struct odp_vport *odp_vport,
 		u32 port_no = nla_get_u32(a[ODP_VPORT_ATTR_PORT_NO]);
 
 		if (port_no >= DP_MAX_PORTS)
-			return ERR_PTR(-EINVAL);
+			return ERR_PTR(-EFBIG);
 
-		dp = get_dp(odp_vport->dp_idx);
+		dp = get_dp(odp_header->dp_idx);
 		if (!dp)
 			return ERR_PTR(-ENODEV);
 
@@ -1681,30 +1674,27 @@ static int change_vport(struct vport *vport, struct nlattr *a[ODP_VPORT_ATTR_MAX
 	return err;
 }
 
-static int attach_vport(struct odp_vport __user *uodp_vport)
+static int odp_vport_cmd_new(struct sk_buff *skb, struct genl_info *info)
 {
-	struct nlattr *a[ODP_VPORT_ATTR_MAX + 1];
-	struct odp_vport *odp_vport;
+	struct nlattr **a = info->attrs;
+	struct odp_header *odp_header = info->userhdr;
 	struct vport_parms parms;
 	struct sk_buff *reply;
 	struct vport *vport;
-	struct sk_buff *skb;
 	struct datapath *dp;
 	u32 port_no;
 	int err;
 
-	skb = copy_vport_from_user(uodp_vport, a);
-	err = PTR_ERR(skb);
-	if (IS_ERR(skb))
-		goto exit;
-	odp_vport = (struct odp_vport *)skb->data;
-
 	err = -EINVAL;
 	if (!a[ODP_VPORT_ATTR_NAME] || !a[ODP_VPORT_ATTR_TYPE])
-		goto exit_kfree_skb;
+		goto exit;
+
+	err = odp_vport_cmd_validate(a);
+	if (err)
+		goto exit;
 
 	rtnl_lock();
-	dp = get_dp(odp_vport->dp_idx);
+	dp = get_dp(odp_header->dp_idx);
 	err = -ENODEV;
 	if (!dp)
 		goto exit_unlock;
@@ -1747,41 +1737,39 @@ static int attach_vport(struct odp_vport __user *uodp_vport)
  	dp_sysfs_add_if(vport);
 
 	err = change_vport(vport, a);
+	if (!err) {
+		reply = odp_vport_cmd_build_info(vport, info->snd_pid,
+						 info->snd_seq, ODP_VPORT_CMD_NEW);
+		if (IS_ERR(reply))
+			err = PTR_ERR(reply);
+	}
 	if (err) {
 		dp_detach_port(vport);
 		goto exit_unlock;
 	}
+	genl_notify(reply, genl_info_net(info), info->snd_pid,
+		    dp_vport_multicast_group.id, info->nlhdr, GFP_KERNEL);
 
-	reply = odp_vport_build_info(vport, odp_vport->total_len);
-	err = PTR_ERR(reply);
-	if (IS_ERR(reply))
-		goto exit_unlock;
-
-	err = copy_to_user(uodp_vport, reply->data, reply->len) ? -EFAULT : 0;
-	kfree_skb(reply);
 
 exit_unlock:
 	rtnl_unlock();
-exit_kfree_skb:
-	kfree_skb(skb);
 exit:
 	return err;
 }
 
-static int set_vport(unsigned int cmd, struct odp_vport __user *uodp_vport)
+static int odp_vport_cmd_set(struct sk_buff *skb, struct genl_info *info)
 {
-	struct nlattr *a[ODP_VPORT_ATTR_MAX + 1];
+	struct nlattr **a = info->attrs;
+	struct sk_buff *reply;
 	struct vport *vport;
-	struct sk_buff *skb;
 	int err;
 
-	skb = copy_vport_from_user(uodp_vport, a);
-	err = PTR_ERR(skb);
-	if (IS_ERR(skb))
+	err = odp_vport_cmd_validate(a);
+	if (err)
 		goto exit;
 
 	rtnl_lock();
-	vport = lookup_vport((struct odp_vport *)skb->data, a);
+	vport = lookup_vport(info->userhdr, a);
 	err = PTR_ERR(vport);
 	if (IS_ERR(vport))
 		goto exit_free;
@@ -1792,6 +1780,19 @@ static int set_vport(unsigned int cmd, struct odp_vport __user *uodp_vport)
 	if (!err)
 		err = change_vport(vport, a);
 
+	reply = odp_vport_cmd_build_info(vport, info->snd_pid, info->snd_seq,
+					 ODP_VPORT_CMD_NEW);
+	if (IS_ERR(reply)) {
+		err = PTR_ERR(reply);
+		netlink_set_err(INIT_NET_GENL_SOCK, 0,
+				dp_vport_multicast_group.id, err);
+		return 0;
+	}
+
+	genl_notify(reply, genl_info_net(info), info->snd_pid,
+		    dp_vport_multicast_group.id, info->nlhdr, GFP_KERNEL);
+
+
 exit_free:
 	kfree_skb(skb);
 	rtnl_unlock();
@@ -1799,127 +1800,133 @@ exit:
 	return err;
 }
 
-static int del_vport(unsigned int cmd, struct odp_vport __user *uodp_vport)
+static int odp_vport_cmd_del(struct sk_buff *skb, struct genl_info *info)
 {
-	struct nlattr *a[ODP_VPORT_ATTR_MAX + 1];
+	struct nlattr **a = info->attrs;
+	struct sk_buff *reply;
 	struct vport *vport;
-	struct sk_buff *skb;
 	int err;
 
-	skb = copy_vport_from_user(uodp_vport, a);
-	err = PTR_ERR(skb);
-	if (IS_ERR(skb))
+	err = odp_vport_cmd_validate(a);
+	if (err)
 		goto exit;
 
 	rtnl_lock();
-	vport = lookup_vport((struct odp_vport *)skb->data, a);
+	vport = lookup_vport(info->userhdr, a);
 	err = PTR_ERR(vport);
-	if (!IS_ERR(vport))
-		err = dp_detach_port(vport);
+	if (IS_ERR(vport))
+		goto exit_unlock;
 
-	kfree_skb(skb);
+	if (vport->port_no == ODPP_LOCAL) {
+		err = -EINVAL;
+		goto exit_unlock;
+	}
+
+	reply = odp_vport_cmd_build_info(vport, info->snd_pid, info->snd_seq,
+					 ODP_VPORT_CMD_DEL);
+	err = PTR_ERR(reply);
+	if (IS_ERR(reply))
+		goto exit_unlock;
+
+	err = dp_detach_port(vport);
+
+	genl_notify(reply, genl_info_net(info), info->snd_pid,
+		    dp_vport_multicast_group.id, info->nlhdr, GFP_KERNEL);
+
+exit_unlock:
 	rtnl_unlock();
 exit:
 	return err;
 }
 
-static int get_vport(struct odp_vport __user *uodp_vport)
+static int odp_vport_cmd_get(struct sk_buff *skb, struct genl_info *info)
 {
-	struct nlattr *a[ODP_VPORT_ATTR_MAX + 1];
-	struct odp_vport *odp_vport;
+	struct nlattr **a = info->attrs;
+	struct odp_header *odp_header = info->userhdr;
 	struct sk_buff *reply;
 	struct vport *vport;
-	struct sk_buff *skb;
 	int err;
 
-	skb = copy_vport_from_user(uodp_vport, a);
-	err = PTR_ERR(skb);
-	if (IS_ERR(skb))
-		goto err;
-	odp_vport = (struct odp_vport *)skb->data;
+	err = odp_vport_cmd_validate(a);
+	if (err)
+		goto exit;
 
 	rcu_read_lock();
-	vport = lookup_vport(odp_vport, a);
+	vport = lookup_vport(odp_header, a);
 	err = PTR_ERR(vport);
 	if (IS_ERR(vport))
-		goto err_unlock_rcu;
-	reply = odp_vport_build_info(vport, odp_vport->total_len);
-	rcu_read_unlock();
+		goto exit_unlock;
 
+	reply = odp_vport_cmd_build_info(vport, info->snd_pid, info->snd_seq,
+					 ODP_VPORT_CMD_NEW);
 	err = PTR_ERR(reply);
 	if (IS_ERR(reply))
-		goto err_kfree_skb;
-
-	err = copy_to_user(uodp_vport, reply->data, reply->len) ? -EFAULT : 0;
-	kfree_skb(reply);
-	kfree_skb(skb);
+		goto exit_unlock;
 
-	return err;
+	err = genlmsg_reply(reply, info);
 
-err_unlock_rcu:
+exit_unlock:
 	rcu_read_unlock();
-err_kfree_skb:
-	kfree_skb(skb);
-err:
+exit:
 	return err;
 }
 
-static int dump_vport(struct odp_vport __user *uodp_vport)
+static int odp_vport_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
 {
-	struct nlattr *a[ODP_VPORT_ATTR_MAX + 1];
-	struct odp_vport *odp_vport;
-	struct sk_buff *skb;
+	struct odp_header *odp_header = genlmsg_data(nlmsg_data(cb->nlh));
 	struct datapath *dp;
 	u32 port_no;
-	int err;
-
-	skb = copy_vport_from_user(uodp_vport, a);
-	err = PTR_ERR(skb);
-	if (IS_ERR(skb))
-		goto err;
-	odp_vport = (struct odp_vport *)skb->data;
+	int retval;
 
-	dp = get_dp(odp_vport->dp_idx);
-	err = -ENODEV;
+	dp = get_dp(odp_header->dp_idx);
 	if (!dp)
-		goto err_kfree_skb;
-
-	port_no = 0;
-	if (a[ODP_VPORT_ATTR_PORT_NO])
-		port_no = nla_get_u32(a[ODP_VPORT_ATTR_PORT_NO]);
+		return -ENODEV;
 
 	rcu_read_lock();
-	for (; port_no < DP_MAX_PORTS; port_no++) {
-		struct sk_buff *skb_out;
+	for (port_no = cb->args[0]; port_no < DP_MAX_PORTS; port_no++) {
 		struct vport *vport;
-		int retval;
 
 		vport = get_vport_protected(dp, port_no);
 		if (!vport)
 			continue;
 
-		skb_out = odp_vport_build_info(vport, odp_vport->total_len);
-		rcu_read_unlock();
-
-		err = PTR_ERR(skb_out);
-		if (IS_ERR(skb_out))
-			goto err_kfree_skb;
-
-		retval = copy_to_user(uodp_vport, skb_out->data, skb_out->len);
-		kfree_skb(skb_out);
-		kfree_skb(skb);
-
-		return retval ? -EFAULT : 0;
+		if (odp_vport_cmd_fill_info(vport, skb, NETLINK_CB(cb->skb).pid,
+					    cb->nlh->nlmsg_seq, NLM_F_MULTI,
+					    ODP_VPORT_CMD_NEW) < 0)
+			break;
 	}
 	rcu_read_unlock();
-	err = -ENODEV;
 
-err_kfree_skb:
-	kfree_skb(skb);
-err:
-	return err;
+	cb->args[0] = port_no;
+	retval = skb->len;
+
+	return retval;
 }
 
+static struct genl_ops dp_vport_genl_ops[] = {
+	{ .cmd = ODP_VPORT_CMD_NEW,
+	  .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */
+	  .policy = vport_policy,
+	  .doit = odp_vport_cmd_new
+	},
+	{ .cmd = ODP_VPORT_CMD_DEL,
+	  .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */
+	  .policy = vport_policy,
+	  .doit = odp_vport_cmd_del
+	},
+	{ .cmd = ODP_VPORT_CMD_GET,
+	  .flags = 0,		    /* OK for unprivileged users. */
+	  .policy = vport_policy,
+	  .doit = odp_vport_cmd_get,
+	  .dumpit = odp_vport_cmd_dump
+	},
+	{ .cmd = ODP_VPORT_CMD_SET,
+	  .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */
+	  .policy = vport_policy,
+	  .doit = odp_vport_cmd_set,
+	},
+};
+
 static long openvswitch_ioctl(struct file *f, unsigned int cmd,
 			   unsigned long argp)
 {
@@ -1927,26 +1934,6 @@ static long openvswitch_ioctl(struct file *f, unsigned int cmd,
 
 	genl_lock();
 	switch (cmd) {
-	case ODP_VPORT_NEW:
-		err = attach_vport((struct odp_vport __user *)argp);
-		goto exit;
-
-	case ODP_VPORT_GET:
-		err = get_vport((struct odp_vport __user *)argp);
-		goto exit;
-
-	case ODP_VPORT_DEL:
-		err = del_vport(cmd, (struct odp_vport __user *)argp);
-		goto exit;
-
-	case ODP_VPORT_SET:
-		err = set_vport(cmd, (struct odp_vport __user *)argp);
-		goto exit;
-
-	case ODP_VPORT_DUMP:
-		err = dump_vport((struct odp_vport __user *)argp);
-		goto exit;
-
 	case ODP_FLOW_FLUSH:
 		err = flush_flows(argp);
 		goto exit;
@@ -1982,11 +1969,6 @@ static long openvswitch_compat_ioctl(struct file *f, unsigned int cmd, unsigned
 		/* Ioctls that don't need any translation at all. */
 		return openvswitch_ioctl(f, cmd, argp);
 
-	case ODP_VPORT_NEW:
-	case ODP_VPORT_DEL:
-	case ODP_VPORT_GET:
-	case ODP_VPORT_SET:
-	case ODP_VPORT_DUMP:
 	case ODP_FLOW_NEW:
 	case ODP_FLOW_DEL:
 	case ODP_FLOW_GET:
@@ -2022,6 +2004,9 @@ static const struct genl_family_and_ops dp_genl_families[] = {
 	{ &dp_datapath_genl_family,
 	  dp_datapath_genl_ops, ARRAY_SIZE(dp_datapath_genl_ops),
 	  &dp_datapath_multicast_group },
+	{ &dp_vport_genl_family,
+	  dp_vport_genl_ops, ARRAY_SIZE(dp_vport_genl_ops),
+	  &dp_vport_multicast_group },
 	{ &dp_packet_genl_family,
 	  dp_packet_genl_ops, ARRAY_SIZE(dp_packet_genl_ops),
 	  NULL },
diff --git a/include/openvswitch/datapath-protocol.h b/include/openvswitch/datapath-protocol.h
index 39a3365..8183651 100644
--- a/include/openvswitch/datapath-protocol.h
+++ b/include/openvswitch/datapath-protocol.h
@@ -70,12 +70,6 @@
 #include <linux/if_link.h>
 #include <linux/netlink.h>
 
-#define ODP_VPORT_NEW           _IOR('O', 7, struct odp_vport)
-#define ODP_VPORT_DEL           _IOR('O', 8, struct odp_vport)
-#define ODP_VPORT_GET           _IOWR('O', 9, struct odp_vport)
-#define ODP_VPORT_SET           _IOR('O', 22, struct odp_vport)
-#define ODP_VPORT_DUMP          _IOWR('O', 10, struct odp_vport)
-
 #define ODP_FLOW_NEW            _IOWR('O', 13, struct odp_flow)
 #define ODP_FLOW_DEL            _IOWR('O', 14, struct odp_flow)
 #define ODP_FLOW_GET            _IOWR('O', 15, struct odp_flow)
@@ -224,24 +218,53 @@ enum odp_vport_type {
 };
 
 #define ODP_VPORT_TYPE_MAX (__ODP_VPORT_TYPE_MAX - 1)
+
+#define ODP_VPORT_FAMILY  "odp_vport"
+#define ODP_VPORT_MCGROUP "odp_vport"
+
+enum odp_vport_cmd {
+	ODP_VPORT_CMD_UNSPEC,
+	ODP_VPORT_CMD_NEW,
+	ODP_VPORT_CMD_DEL,
+	ODP_VPORT_CMD_GET,
+	ODP_VPORT_CMD_SET
+};
 
 /**
- * struct odp_vport - header with basic information about a virtual port.
- * @dp_idx: Number of datapath to which the vport belongs.
- * @len: Length of this structure plus the Netlink attributes following it.
- * @total_len: Total space available for kernel reply to request.
+ * enum odp_vport_attr - attributes for %ODP_VPORT_* commands.
+ * @ODP_VPORT_ATTR_PORT_NO: 32-bit port number within datapath.
+ * @ODP_VPORT_ATTR_TYPE: 32-bit %ODP_VPORT_TYPE_* constant describing the type
+ * of vport.
+ * @ODP_VPORT_ATTR_NAME: Name of vport.  For a vport based on a network device
+ * this is the name of the network device.  Maximum length %IFNAMSIZ-1 bytes
+ * plus a null terminator.
+ * @ODP_VPORT_ATTR_STATS: A &struct rtnl_link_stats64 giving statistics for
+ * packets sent or received through the vport.
+ * @ODP_VPORT_ATTR_ADDRESS: A 6-byte Ethernet address for the vport.
+ * @ODP_VPORT_ATTR_MTU: MTU for the vport.
+ * @ODP_VPORT_ATTR_IFINDEX: ifindex of the underlying network device, if any.
+ * @ODP_VPORT_ATTR_IFLINK: ifindex of the device on which packets are sent (for
+ * tunnels), if any.
  *
- * Followed by &struct nlattr attributes, whose types are drawn from
- * %ODP_VPORT_ATTR_*, up to a length of @len bytes including the &struct
- * odp_vport header.
+ * These attributes follow the &struct odp_header within the Generic Netlink
+ * payload for %ODP_VPORT_* commands.
+ *
+ * All attributes applicable to a given port are present in notifications.
+ * This means that, for example, a vport that has no corresponding network
+ * device would omit %ODP_VPORT_ATTR_IFINDEX.
+ *
+ * For %ODP_VPORT_CMD_NEW requests, the %ODP_VPORT_ATTR_TYPE and
+ * %ODP_VPORT_ATTR_NAME attributes are required.  %ODP_VPORT_ATTR_PORT_NO is
+ * optional; if not specified a free port number is automatically selected.
+ * Whether %ODP_VPORT_ATTR_OPTIONS is required or optional depends on the type
+ * of vport.  %ODP_VPORT_ATTR_STATS, %ODP_VPORT_ATTR_ADDRESS, and
+ * %ODP_VPORT_ATTR_MTU are optional, and other attributes are ignored.
+ *
+ * For other requests, if %ODP_VPORT_ATTR_NAME is specified then it is used to
+ * look up the vport to operate on; otherwise dp_idx from the &struct
+ * odp_header plus %ODP_VPORT_ATTR_PORT_NO determine the vport.
  */
-struct odp_vport {
-	uint32_t dp_idx;
-	uint32_t len;
-	uint32_t total_len;
-};
-
-enum {
+enum odp_vport_attr {
 	ODP_VPORT_ATTR_UNSPEC,
 	ODP_VPORT_ATTR_PORT_NO,	/* port number within datapath */
 	ODP_VPORT_ATTR_TYPE,	/* 32-bit ODP_VPORT_TYPE_* constant. */
diff --git a/lib/dpif-linux.c b/lib/dpif-linux.c
index 5fb7035..b2d65ed 100644
--- a/lib/dpif-linux.c
+++ b/lib/dpif-linux.c
@@ -135,6 +135,7 @@ static struct vlog_rate_limit error_rl = VLOG_RATE_LIMIT_INIT(9999, 5);
 
 /* Generic Netlink family numbers for ODP. */
 static int odp_datapath_family;
+static int odp_vport_family;
 static int odp_packet_family;
 
 /* Generic Netlink socket. */
@@ -150,6 +151,11 @@ static int make_openvswitch_device(int minor, char **fnp);
 static void dpif_linux_port_changed(const struct rtnetlink_link_change *,
                                     void *dpif);
 
+static void dpif_linux_vport_to_ofpbuf(const struct dpif_linux_vport *,
+                                       struct ofpbuf *);
+static int dpif_linux_vport_from_ofpbuf(struct dpif_linux_vport *,
+                                        const struct ofpbuf *);
+
 static struct dpif_linux *
 dpif_linux_cast(const struct dpif *dpif)
 {
@@ -218,7 +224,7 @@ dpif_linux_open(const struct dpif_class *class OVS_UNUSED, const char *name,
 
     /* Look up local port. */
     dpif_linux_vport_init(&vport_request);
-    vport_request.cmd = ODP_VPORT_GET;
+    vport_request.cmd = ODP_VPORT_CMD_GET;
     vport_request.dp_idx = dp.dp_idx;
     vport_request.port_no = ODPP_LOCAL;
     vport_request.name = minor < 0 ? name : NULL;
@@ -376,7 +382,7 @@ dpif_linux_port_add(struct dpif *dpif_, struct netdev *netdev,
     int error;
 
     dpif_linux_vport_init(&request);
-    request.cmd = ODP_VPORT_NEW;
+    request.cmd = ODP_VPORT_CMD_NEW;
     request.dp_idx = dpif->minor;
     request.type = netdev_vport_get_vport_type(netdev);
     if (request.type == ODP_VPORT_TYPE_UNSPEC) {
@@ -409,7 +415,7 @@ dpif_linux_port_del(struct dpif *dpif_, uint16_t port_no)
     struct dpif_linux_vport vport;
 
     dpif_linux_vport_init(&vport);
-    vport.cmd = ODP_VPORT_DEL;
+    vport.cmd = ODP_VPORT_CMD_DEL;
     vport.dp_idx = dpif->minor;
     vport.port_no = port_no;
     return dpif_linux_vport_transact(&vport, NULL, NULL);
@@ -425,7 +431,7 @@ dpif_linux_port_query__(const struct dpif *dpif, uint32_t port_no,
     int error;
 
     dpif_linux_vport_init(&request);
-    request.cmd = ODP_VPORT_GET;
+    request.cmd = ODP_VPORT_CMD_GET;
     request.dp_idx = dpif_linux_cast(dpif)->minor;
     request.port_no = port_no;
     request.name = port_name;
@@ -470,53 +476,62 @@ dpif_linux_flow_flush(struct dpif *dpif_)
 }
 
 struct dpif_linux_port_state {
-    struct ofpbuf *buf;
-    uint32_t next;
+    struct nl_dump dump;
 };
 
 static int
-dpif_linux_port_dump_start(const struct dpif *dpif OVS_UNUSED, void **statep)
+dpif_linux_port_dump_start(const struct dpif *dpif_, void **statep)
 {
-    *statep = xzalloc(sizeof(struct dpif_linux_port_state));
+    struct dpif_linux *dpif = dpif_linux_cast(dpif_);
+    struct dpif_linux_port_state *state;
+    struct dpif_linux_vport request;
+    struct ofpbuf *buf;
+
+    *statep = state = xmalloc(sizeof *state);
+
+    dpif_linux_vport_init(&request);
+    request.cmd = ODP_DP_CMD_GET;
+    request.dp_idx = dpif->minor;
+
+    buf = ofpbuf_new(1024);
+    dpif_linux_vport_to_ofpbuf(&request, buf);
+    nl_dump_start(&state->dump, genl_sock, buf);
+    ofpbuf_delete(buf);
+
     return 0;
 }
 
 static int
-dpif_linux_port_dump_next(const struct dpif *dpif, void *state_,
+dpif_linux_port_dump_next(const struct dpif *dpif OVS_UNUSED, void *state_,
                           struct dpif_port *dpif_port)
 {
     struct dpif_linux_port_state *state = state_;
-    struct dpif_linux_vport request, reply;
-    struct ofpbuf *buf;
+    struct dpif_linux_vport vport;
+    struct ofpbuf buf;
     int error;
 
-    ofpbuf_delete(state->buf);
-    state->buf = NULL;
+    if (!nl_dump_next(&state->dump, &buf)) {
+        return EOF;
+    }
 
-    dpif_linux_vport_init(&request);
-    request.cmd = ODP_VPORT_DUMP;
-    request.dp_idx = dpif_linux_cast(dpif)->minor;
-    request.port_no = state->next;
-    error = dpif_linux_vport_transact(&request, &reply, &buf);
+    error = dpif_linux_vport_from_ofpbuf(&vport, &buf);
     if (error) {
-        return error == ENODEV ? EOF : error;
-    } else {
-        dpif_port->name = (char *) reply.name;
-        dpif_port->type = (char *) netdev_vport_get_netdev_type(&reply);
-        dpif_port->port_no = reply.port_no;
-        state->buf = buf;
-        state->next = reply.port_no + 1;
-        return 0;
+        return error;
     }
+
+    dpif_port->name = (char *) vport.name;
+    dpif_port->type = (char *) netdev_vport_get_netdev_type(&vport);
+    dpif_port->port_no = vport.port_no;
+    return 0;
 }
 
 static int
 dpif_linux_port_dump_done(const struct dpif *dpif OVS_UNUSED, void *state_)
 {
     struct dpif_linux_port_state *state = state_;
-    ofpbuf_delete(state->buf);
+    int error = nl_dump_done(&state->dump);
     free(state);
-    return 0;
+    return error;
 }
 
 static int
@@ -990,6 +1005,9 @@ dpif_linux_init(void)
         error = nl_lookup_genl_family(ODP_DATAPATH_FAMILY,
                                       &odp_datapath_family);
         if (!error) {
+            error = nl_lookup_genl_family(ODP_VPORT_FAMILY, &odp_vport_family);
+        }
+        if (!error) {
             error = nl_lookup_genl_family(ODP_PACKET_FAMILY,
                                           &odp_packet_family);
         }
@@ -1197,7 +1215,7 @@ get_dp0_fd(int *dp0_fdp)
     return 0;
 }
 
-/* Parses the contents of 'buf', which contains a "struct odp_vport" followed
+/* Parses the contents of 'buf', which contains a "struct odp_header" followed
  * by Netlink attributes, into 'vport'.  Returns 0 if successful, otherwise a
  * positive errno value.
  *
@@ -1225,18 +1243,27 @@ dpif_linux_vport_from_ofpbuf(struct dpif_linux_vport *vport,
         [ODP_VPORT_ATTR_IFLINK] = { .type = NL_A_U32, .optional = true },
     };
 
-    struct odp_vport *odp_vport;
     struct nlattr *a[ARRAY_SIZE(odp_vport_policy)];
+    struct odp_header *odp_header;
+    struct nlmsghdr *nlmsg;
+    struct genlmsghdr *genl;
+    struct ofpbuf b;
 
     dpif_linux_vport_init(vport);
 
-    if (!nl_policy_parse(buf, sizeof *odp_vport, odp_vport_policy,
-                         a, ARRAY_SIZE(odp_vport_policy))) {
+    ofpbuf_use_const(&b, buf->data, buf->size);
+    nlmsg = ofpbuf_try_pull(&b, sizeof *nlmsg);
+    genl = ofpbuf_try_pull(&b, sizeof *genl);
+    odp_header = ofpbuf_try_pull(&b, sizeof *odp_header);
+    if (!nlmsg || !genl || !odp_header
+        || nlmsg->nlmsg_type != odp_vport_family
+        || !nl_policy_parse(&b, 0, odp_vport_policy, a,
+                            ARRAY_SIZE(odp_vport_policy))) {
         return EINVAL;
     }
-    odp_vport = buf->data;
 
-    vport->dp_idx = odp_vport->dp_idx;
+    vport->cmd = genl->cmd;
+    vport->dp_idx = odp_header->dp_idx;
     vport->port_no = nl_attr_get_u32(a[ODP_VPORT_ATTR_PORT_NO]);
     vport->type = nl_attr_get_u32(a[ODP_VPORT_ATTR_TYPE]);
     vport->name = nl_attr_get_string(a[ODP_VPORT_ATTR_NAME]);
@@ -1262,15 +1289,19 @@ dpif_linux_vport_from_ofpbuf(struct dpif_linux_vport *vport,
     return 0;
 }
 
-/* Appends to 'buf' (which must initially be empty) a "struct odp_vport"
+/* Appends to 'buf' (which must initially be empty) a "struct odp_header"
  * followed by Netlink attributes corresponding to 'vport'. */
 static void
 dpif_linux_vport_to_ofpbuf(const struct dpif_linux_vport *vport,
                            struct ofpbuf *buf)
 {
-    struct odp_vport *odp_vport;
+    struct odp_header *odp_header;
 
-    ofpbuf_reserve(buf, sizeof odp_vport);
+    nl_msg_put_genlmsghdr(buf, 0, odp_vport_family, NLM_F_REQUEST | NLM_F_ECHO,
+                          vport->cmd, 1);
+
+    odp_header = ofpbuf_put_uninit(buf, sizeof *odp_header);
+    odp_header->dp_idx = vport->dp_idx;
 
     if (vport->port_no != UINT32_MAX) {
         nl_msg_put_u32(buf, ODP_VPORT_ATTR_PORT_NO, vport->port_no);
@@ -1310,11 +1341,6 @@ dpif_linux_vport_to_ofpbuf(const struct dpif_linux_vport *vport,
     if (vport->iflink) {
         nl_msg_put_u32(buf, ODP_VPORT_ATTR_IFLINK, vport->iflink);
     }
-
-    odp_vport = ofpbuf_push_uninit(buf, sizeof *odp_vport);
-    odp_vport->dp_idx = vport->dp_idx;
-    odp_vport->len = buf->size;
-    odp_vport->total_len = (char *) ofpbuf_end(buf) - (char *) buf->data;
 }
 
 /* Clears 'vport' to "empty" values. */
@@ -1337,42 +1363,25 @@ dpif_linux_vport_transact(const struct dpif_linux_vport *request,
                           struct dpif_linux_vport *reply,
                           struct ofpbuf **bufp)
 {
-    struct ofpbuf *buf = NULL;
+    struct ofpbuf *request_buf;
     int error;
-    int fd;
 
     assert((reply != NULL) == (bufp != NULL));
 
-    error = get_dp0_fd(&fd);
-    if (error) {
-        goto error;
-    }
-
-    buf = ofpbuf_new(1024);
-    dpif_linux_vport_to_ofpbuf(request, buf);
-
-    error = ioctl(fd, request->cmd, buf->data) ? errno : 0;
-    if (error) {
-        goto error;
-    }
+    request_buf = ofpbuf_new(1024);
+    dpif_linux_vport_to_ofpbuf(request, request_buf);
+    error = nl_sock_transact(genl_sock, request_buf, bufp);
+    ofpbuf_delete(request_buf);
 
-    if (bufp) {
-        buf->size = ((struct odp_vport *) buf->data)->len;
-        error = dpif_linux_vport_from_ofpbuf(reply, buf);
+    if (reply) {
+        if (!error) {
+            error = dpif_linux_vport_from_ofpbuf(reply, *bufp);
+        }
         if (error) {
-            goto error;
+            dpif_linux_vport_init(reply);
+            ofpbuf_delete(*bufp);
+            *bufp = NULL;
         }
-        *bufp = buf;
-    } else {
-        ofpbuf_delete(buf);
-    }
-    return 0;
-
-error:
-    ofpbuf_delete(buf);
-    if (bufp) {
-        memset(reply, 0, sizeof *reply);
-        *bufp = NULL;
     }
     return error;
 }
@@ -1387,7 +1396,7 @@ dpif_linux_vport_get(const char *name, struct dpif_linux_vport *reply,
     struct dpif_linux_vport request;
 
     dpif_linux_vport_init(&request);
-    request.cmd = ODP_VPORT_GET;
+    request.cmd = ODP_VPORT_CMD_GET;
     request.name = name;
 
     return dpif_linux_vport_transact(&request, reply, bufp);
diff --git a/lib/dpif-linux.h b/lib/dpif-linux.h
index 30e927f..6ccd54a 100644
--- a/lib/dpif-linux.h
+++ b/lib/dpif-linux.h
@@ -18,13 +18,14 @@
 #define DPIF_LINUX_H 1
 
 #include <stdbool.h>
+#include <stdint.h>
 #include "openvswitch/datapath-protocol.h"
 
 struct ofpbuf;
 
 struct dpif_linux_vport {
-    /* ioctl command argument. */
-    int cmd;
+    /* Generic Netlink header. */
+    uint8_t cmd;
 
     /* odp_vport header. */
     uint32_t dp_idx;
diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c
index 732fb2a..00017b8 100644
--- a/lib/netdev-vport.c
+++ b/lib/netdev-vport.c
@@ -310,7 +310,7 @@ netdev_vport_set_config(struct netdev_dev *dev_, const struct shash *args)
         struct dpif_linux_vport vport;
 
         dpif_linux_vport_init(&vport);
-        vport.cmd = ODP_VPORT_SET;
+        vport.cmd = ODP_VPORT_CMD_SET;
         vport.name = name;
         vport.options = options->data;
         vport.options_len = options->size;
@@ -338,7 +338,7 @@ netdev_vport_set_etheraddr(struct netdev *netdev,
     int error;
 
     dpif_linux_vport_init(&vport);
-    vport.cmd = ODP_VPORT_SET;
+    vport.cmd = ODP_VPORT_CMD_SET;
     vport.name = netdev_get_name(netdev);
     vport.address = mac;
 
@@ -456,7 +456,7 @@ netdev_vport_set_stats(struct netdev *netdev, const struct netdev_stats *stats)
     rtnl_stats.tx_window_errors = stats->tx_window_errors;
 
     dpif_linux_vport_init(&vport);
-    vport.cmd = ODP_VPORT_SET;
+    vport.cmd = ODP_VPORT_CMD_SET;
     vport.name = netdev_get_name(netdev);
     vport.stats = &rtnl_stats;
 
-- 
1.7.1





More information about the dev mailing list