[ovs-dev] [netlink v4 27/52] datapath: Change userspace vport interface to use Netlink attributes.

Ben Pfaff blp at nicira.com
Wed Jan 12 05:49:39 UTC 2011


One of the goals for Open vSwitch is to decouple kernel and userspace
software, so that either one can be upgraded or rolled back independent of
the other.  To do this in full generality, it must be possible to add new
features to the kernel vport layer without changing userspace software.
The customary way to do this in the Linux networking stack is to use
Netlink and in particular Netlink attributes.  This commit adopts that
model for the vport layer.  It does not yet actually start using the
Netlink socket layer, which will come later.

Signed-off-by: Ben Pfaff <blp at nicira.com>
---
 datapath/datapath.c                     |  580 ++++++++++++++++++-----------
 datapath/odp-compat.h                   |    6 -
 datapath/tunnel.c                       |   38 ++-
 datapath/tunnel.h                       |    8 +-
 datapath/vport-capwap.c                 |    6 +-
 datapath/vport-gre.c                    |    6 +-
 datapath/vport-patch.c                  |   82 +++--
 datapath/vport.c                        |  299 +--------------
 datapath/vport.h                        |   26 +-
 include/openvswitch/datapath-protocol.h |  100 +++---
 include/openvswitch/tunnel.h            |   11 +-
 lib/dpif-linux.c                        |  606 +++++++++++++++++++------------
 lib/dpif-linux.h                        |   33 ++-
 lib/netdev-vport.c                      |  400 +++++++++++++--------
 lib/netdev-vport.h                      |   10 +-
 15 files changed, 1182 insertions(+), 1029 deletions(-)

diff --git a/datapath/datapath.c b/datapath/datapath.c
index 0ba8ab6..d9e6848 100644
--- a/datapath/datapath.c
+++ b/datapath/datapath.c
@@ -71,7 +71,7 @@ EXPORT_SYMBOL(dp_ioctl_hook);
 static struct datapath __rcu *dps[ODP_MAX];
 static DEFINE_MUTEX(dp_mutex);
 
-static int new_vport(struct datapath *, struct odp_port *, int port_no);
+static struct vport *new_vport(const struct vport_parms *);
 
 /* Must be called with rcu_read_lock or dp_mutex. */
 struct datapath *get_dp(int dp_idx)
@@ -210,8 +210,9 @@ static struct kobj_type dp_ktype = {
 
 static int create_dp(int dp_idx, const char __user *devnamep)
 {
-	struct odp_port internal_dev_port;
+	struct vport_parms parms;
 	char devname[IFNAMSIZ];
+	struct vport *vport;
 	struct datapath *dp;
 	int err;
 	int i;
@@ -266,11 +267,16 @@ static int create_dp(int dp_idx, const char __user *devnamep)
 		goto err_free_dp;
 
 	/* Set up our datapath device. */
-	BUILD_BUG_ON(sizeof(internal_dev_port.devname) != sizeof(devname));
-	strcpy(internal_dev_port.devname, devname);
-	internal_dev_port.type = ODPVT_INTERNAL;
-	err = new_vport(dp, &internal_dev_port, ODPP_LOCAL);
-	if (err) {
+	parms.name = devname;
+	parms.type = ODPVT_INTERNAL;
+	parms.options = NULL;
+	parms.dp = dp;
+	parms.port_no = ODPP_LOCAL;
+	vport_lock();
+	vport = new_vport(&parms);
+	vport_unlock();
+	if (IS_ERR(vport)) {
+		err = PTR_ERR(vport);
 		if (err == -EBUSY)
 			err = -EEXIST;
 
@@ -356,74 +362,20 @@ out:
 	return err;
 }
 
-/* Called with RTNL lock and dp->mutex. */
-static int new_vport(struct datapath *dp, struct odp_port *odp_port, int port_no)
-{
-	struct vport_parms parms;
-	struct vport *vport;
-
-	parms.name = odp_port->devname;
-	parms.type = odp_port->type;
-	parms.config = odp_port->config;
-	parms.dp = dp;
-	parms.port_no = port_no;
-
-	vport_lock();
-	vport = vport_add(&parms);
-	vport_unlock();
-
-	if (IS_ERR(vport))
-		return PTR_ERR(vport);
-
-	rcu_assign_pointer(dp->ports[port_no], vport);
-	list_add_rcu(&vport->node, &dp->port_list);
-	dp->n_ports++;
-
-	dp_ifinfo_notify(RTM_NEWLINK, vport);
-
-	return 0;
-}
-
-static int attach_port(int dp_idx, struct odp_port __user *portp)
+/* Called with RTNL lock, dp->mutex, and vport_lock. */
+static struct vport *new_vport(const struct vport_parms *parms)
 {
-	struct datapath *dp;
-	struct odp_port port;
-	int port_no;
-	int err;
-
-	err = -EFAULT;
-	if (copy_from_user(&port, portp, sizeof port))
-		goto out;
-	port.devname[IFNAMSIZ - 1] = '\0';
-
-	rtnl_lock();
-	dp = get_dp_locked(dp_idx);
-	err = -ENODEV;
-	if (!dp)
-		goto out_unlock_rtnl;
-
-	for (port_no = 1; port_no < DP_MAX_PORTS; port_no++)
-		if (!dp->ports[port_no])
-			goto got_port_no;
-	err = -EFBIG;
-	goto out_unlock_dp;
-
-got_port_no:
-	err = new_vport(dp, &port, port_no);
-	if (err)
-		goto out_unlock_dp;
+	struct vport *vport = vport_add(parms);
+	if (!IS_ERR(vport)) {
+		struct datapath *dp = parms->dp;
 
-	set_internal_devs_mtu(dp);
-	dp_sysfs_add_if(get_vport_protected(dp, port_no));
+		rcu_assign_pointer(dp->ports[parms->port_no], vport);
+		list_add_rcu(&vport->node, &dp->port_list);
+		dp->n_ports++;
 
-	err = put_user(port_no, &portp->port);
-
-out_unlock_dp:
-	mutex_unlock(&dp->mutex);
-out_unlock_rtnl:
-	rtnl_unlock();
-out:
-	return err;
+		dp_ifinfo_notify(RTM_NEWLINK, vport);
+	}
+	return vport;
 }
 
 int dp_detach_port(struct vport *p)
@@ -449,37 +401,6 @@ int dp_detach_port(struct vport *p)
 	return err;
 }
 
-static int detach_port(int dp_idx, int port_no)
-{
-	struct vport *p;
-	struct datapath *dp;
-	int err;
-
-	err = -EINVAL;
-	if (port_no < 0 || port_no >= DP_MAX_PORTS || port_no == ODPP_LOCAL)
-		goto out;
-
-	rtnl_lock();
-	dp = get_dp_locked(dp_idx);
-	err = -ENODEV;
-	if (!dp)
-		goto out_unlock_rtnl;
-
-	p = get_vport_protected(dp, port_no);
-	err = -ENOENT;
-	if (!p)
-		goto out_unlock_dp;
-
-	err = dp_detach_port(p);
-
-out_unlock_dp:
-	mutex_unlock(&dp->mutex);
-out_unlock_rtnl:
-	rtnl_unlock();
-out:
-	return err;
-}
-
 /* Must be called with rcu_read_lock. */
 void dp_process_received_packet(struct vport *p, struct sk_buff *skb)
 {
@@ -1347,95 +1268,364 @@ void set_internal_devs_mtu(const struct datapath *dp)
 	}
 }
 
-static void compose_odp_port(const struct vport *vport, struct odp_port *odp_port)
+static int get_listen_mask(const struct file *f)
 {
+	return (long)f->private_data;
+}
+
+static void set_listen_mask(struct file *f, int listen_mask)
+{
+	f->private_data = (void*)(long)listen_mask;
+}
+
+static struct nla_policy vport_policy[ODPPT_MAX + 1] = {
+	[ODPPT_NAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ - 1 },
+	[ODPPT_STATS] = { .len = sizeof(struct rtnl_link_stats64) },
+	[ODPPT_ADDRESS] = { .len = ETH_ALEN },
+	[ODPPT_MTU] = { .type = NLA_U32 },
+	[ODPPT_OPTIONS] = { .type = NLA_NESTED },
+};
+
+static int copy_vport_to_user(void __user *dst, struct vport *vport, uint32_t total_len)
+{
+	struct odp_vport *odp_vport;
+	struct sk_buff *skb;
+	struct nlattr *nla;
+	int ifindex, iflink;
+	int err;
+
+	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
+	err = -ENOMEM;
+	if (!skb)
+		goto exit;
+
 	rcu_read_lock();
-	strncpy(odp_port->devname, vport_get_name(vport), sizeof(odp_port->devname));
-	odp_port->type = vport_get_type(vport);
-	vport_get_config(vport, odp_port->config);
-	odp_port->port = vport->port_no;
-	odp_port->dp_idx = vport->dp->dp_idx;
+	odp_vport = (struct odp_vport*)__skb_put(skb, sizeof(struct odp_vport));
+	odp_vport->dp_idx = vport->dp->dp_idx;
+	odp_vport->port_no = vport->port_no;
+	odp_vport->type = vport_get_type(vport);
+	odp_vport->total_len = total_len;
+
+	NLA_PUT_STRING(skb, ODPPT_NAME, vport_get_name(vport));
+
+	nla = nla_reserve(skb, ODPPT_STATS, sizeof(struct rtnl_link_stats64));
+	if (!nla)
+		goto nla_put_failure;
+	if (vport_get_stats(vport, nla_data(nla)))
+		__skb_trim(skb, skb->len - nla->nla_len);
+
+	NLA_PUT(skb, ODPPT_ADDRESS, ETH_ALEN, vport_get_addr(vport));
+
+	NLA_PUT_U32(skb, ODPPT_MTU, vport_get_mtu(vport));
+
+	if (vport->ops->get_options) {
+		nla = nla_nest_start(skb, ODPPT_OPTIONS);
+		if (!nla)
+			goto nla_put_failure;
+		err = vport->ops->get_options(vport, skb);
+		if (err)
+			goto exit_unlock;
+		nla_nest_end(skb, nla);
+	}
+
+	ifindex = vport_get_ifindex(vport);
+	if (ifindex > 0)
+		NLA_PUT_U32(skb, ODPPT_IFINDEX, ifindex);
+
+	iflink = vport_get_iflink(vport);
+	if (iflink > 0)
+		NLA_PUT_U32(skb, ODPPT_IFLINK, iflink);
+
+	err = -EMSGSIZE;
+	if (skb->len > total_len)
+		goto exit_unlock;
+
+	odp_vport->len = skb->len;
+	err = copy_to_user(dst, skb->data, skb->len) ? -EFAULT : 0;
+	goto exit_unlock;
+
+nla_put_failure:
+	err = -EMSGSIZE;
+exit_unlock:
 	rcu_read_unlock();
+	kfree_skb(skb);
+exit:
+	return err;
 }
 
-static int query_port(int dp_idx, struct odp_port __user *uport)
+static struct sk_buff *copy_vport_from_user(struct odp_vport __user *uodp_vport, struct nlattr *a[ODPPT_MAX + 1])
 {
-	struct odp_port port;
+	struct odp_vport *odp_vport;
+	struct sk_buff *skb;
+	u32 len;
+	int err;
 
-	if (copy_from_user(&port, uport, sizeof port))
-		return -EFAULT;
+	if (get_user(len, &uodp_vport->len))
+		return ERR_PTR(-EFAULT);
+	if (len < sizeof(struct odp_vport))
+		return ERR_PTR(-EINVAL);
 
-	if (port.devname[0]) {
-		struct vport *vport;
+	skb = alloc_skb(len, GFP_KERNEL);
+	if (!skb)
+		return ERR_PTR(-ENOMEM);
 
-		port.devname[IFNAMSIZ - 1] = '\0';
+	err = -EFAULT;
+	if (copy_from_user(__skb_put(skb, len), uodp_vport, len))
+		goto error_free_skb;
 
-		vport_lock();
-		vport = vport_locate(port.devname);
-		if (vport)
-			compose_odp_port(vport, &port);
-		vport_unlock();
+	odp_vport = (struct odp_vport *)skb->data;
+	err = -EINVAL;
+	if (odp_vport->len != len)
+		goto error_free_skb;
 
-		if (!vport)
-			return -ENODEV;
-	} else {
-		struct vport *vport;
-		struct datapath *dp;
+	err = nla_parse(a, ODPPT_MAX, (struct nlattr *)(skb->data + sizeof(struct odp_vport)),
+			skb->len - sizeof(struct odp_vport), vport_policy);
+	if (err)
+		goto error_free_skb;
 
-		if (port.port >= DP_MAX_PORTS)
-			return -EINVAL;
+	err = VERIFY_NUL_STRING(a[ODPPT_NAME]);
+	if (err)
+		goto error_free_skb;
+
+	return skb;
+
+error_free_skb:
+	kfree_skb(skb);
+	return ERR_PTR(err);
+}
+
+static struct vport *lookup_vport(struct odp_vport *odp_vport, struct nlattr *a[ODPPT_MAX + 1])
+{
+	struct datapath *dp;
+	struct vport *vport;
+
+	if (a[ODPPT_NAME]) {
+		int dp_idx, port_no;
+
+	retry:
+		vport_lock();
+		vport = vport_locate(nla_data(a[ODPPT_NAME]));
+		if (!vport) {
+			vport_unlock();
+			return ERR_PTR(-ENODEV);
+		}
+		dp_idx = vport->dp->dp_idx;
+		port_no = vport->port_no;
+		vport_unlock();
 
 		dp = get_dp_locked(dp_idx);
 		if (!dp)
-			return -ENODEV;
+			goto retry;
 
-		vport = get_vport_protected(dp, port.port);
-		if (vport)
-			compose_odp_port(vport, &port);
-		mutex_unlock(&dp->mutex);
+		vport = get_vport_protected(dp, port_no);
+		if (!vport || strcmp(vport_get_name(vport), nla_data(a[ODPPT_NAME]))) {
+			mutex_unlock(&dp->mutex);
+			goto retry;
+		}
+
+		return vport;
+	} else {
+		if (odp_vport->port_no < 0 || odp_vport->port_no >= DP_MAX_PORTS)
+			return ERR_PTR(-EINVAL);
 
-		if (!vport)
-			return -ENOENT;
+		dp = get_dp_locked(odp_vport->dp_idx);
+		if (!dp)
+			return ERR_PTR(-ENODEV);
+
+		vport = get_vport_protected(dp, odp_vport->port_no);
+		if (!vport) {
+			mutex_unlock(&dp->mutex);
+			return ERR_PTR(-ENOENT);
+		}
+		return vport;
 	}
+}
 
-	return copy_to_user(uport, &port, sizeof(struct odp_port));
+static int change_vport(struct vport *vport, struct nlattr *a[ODPPT_MAX + 1])
+{
+	int err = 0;
+	if (a[ODPPT_STATS])
+		err = vport_set_stats(vport, nla_data(a[ODPPT_STATS]));
+	if (!err && a[ODPPT_ADDRESS])
+		err = vport_set_addr(vport, nla_data(a[ODPPT_ADDRESS]));
+	if (!err && a[ODPPT_MTU])
+		err = vport_set_mtu(vport, nla_get_u32(a[ODPPT_MTU]));
+	return err;
 }
 
-static int do_dump_port(struct datapath *dp, struct odp_vport_dump *dump)
+static int attach_vport(struct odp_vport __user *uodp_vport)
 {
-	u32 port_no;
+	struct nlattr *a[ODPPT_MAX + 1];
+	struct odp_vport *odp_vport;
+	struct vport_parms parms;
+	struct vport *vport;
+	struct sk_buff *skb;
+	struct datapath *dp;
+	int port_no;
+	int err;
 
-	for (port_no = dump->port_no; port_no < DP_MAX_PORTS; port_no++) {
-		struct vport *vport = get_vport_protected(dp, port_no);
-		if (vport) {
-			struct odp_port odp_port;
+	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[ODPPT_NAME])
+		goto exit_kfree_skb;
+
+	rtnl_lock();
+
+	dp = get_dp_locked(odp_vport->dp_idx);
+	err = -ENODEV;
+	if (!dp)
+		goto exit_unlock_rtnl;
 
-			compose_odp_port(vport, &odp_port);
-			return copy_to_user((struct odp_port __force __user*)dump->port, &odp_port, sizeof(struct odp_port));
+	port_no = odp_vport->port_no;
+	if (port_no > 0) {
+		vport = get_vport_protected(dp, odp_vport->port_no);
+		err = -EBUSY;
+		if (vport)
+			goto exit_unlock_dp;
+	} else {
+		for (port_no = 1; ; port_no++) {
+			if (port_no >= DP_MAX_PORTS) {
+				err = -EFBIG;
+				goto exit_unlock_dp;
+			}
+			vport = get_vport_protected(dp, port_no);
+			if (!vport)
+				break;
 		}
 	}
 
-	return put_user('\0', (char __force __user*)&dump->port->devname[0]);
+	parms.name = nla_data(a[ODPPT_NAME]);
+	parms.type = odp_vport->type;
+	parms.options = a[ODPPT_OPTIONS];
+	parms.dp = dp;
+	parms.port_no = port_no;
+
+	vport_lock();
+	vport = new_vport(&parms);
+	err = PTR_ERR(vport);
+	if (IS_ERR(vport))
+		goto exit_unlock_vport;
+
+	err = change_vport(vport, a);
+	if (err)
+		dp_detach_port(vport);
+
+exit_unlock_vport:
+	vport_unlock();
+exit_unlock_dp:
+	mutex_unlock(&vport->dp->mutex);
+exit_unlock_rtnl:
+	rtnl_unlock();
+exit_kfree_skb:
+	kfree_skb(skb);
+exit:
+	return err;
 }
 
-static int dump_port(struct datapath *dp, struct odp_vport_dump __user *udump)
+static int modify_vport(unsigned int cmd, struct odp_vport __user *uodp_vport)
 {
-	struct odp_vport_dump dump;
+	struct nlattr *a[ODPPT_MAX + 1];
+	struct datapath *dp;
+	struct vport *vport;
+	struct sk_buff *skb;
+	int err;
 
-	if (copy_from_user(&dump, udump, sizeof(dump)))
-		return -EFAULT;
+	skb = copy_vport_from_user(uodp_vport, a);
+	err = PTR_ERR(skb);
+	if (IS_ERR(skb))
+		goto exit;
 
-	return do_dump_port(dp, &dump);
+	rtnl_lock();
+	vport = lookup_vport((struct odp_vport *)skb->data, a);
+	err = PTR_ERR(vport);
+	if (IS_ERR(vport))
+		goto exit_free;
+	dp = vport->dp;
+
+	if (cmd == ODP_VPORT_DEL)
+		err = dp_detach_port(vport);
+	else {
+		err = 0;
+		if (a[ODPPT_OPTIONS])
+			err = vport_set_options(vport, a[ODPPT_OPTIONS]);
+		if (!err)
+			err = change_vport(vport, a);
+	}
+	mutex_unlock(&dp->mutex);
+exit_free:
+	kfree_skb(skb);
+	rtnl_unlock();
+exit:
+	return err;
 }
 
-static int get_listen_mask(const struct file *f)
+static int get_vport(struct odp_vport __user *uodp_vport)
 {
-	return (long)f->private_data;
+	struct nlattr *a[ODPPT_MAX + 1];
+	struct odp_vport *odp_vport;
+	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 exit;
+	odp_vport = (struct odp_vport *)skb->data;
+
+	vport = lookup_vport(odp_vport, a);
+	err = PTR_ERR(vport);
+	if (IS_ERR(vport))
+		goto exit_free;
+
+	err = copy_vport_to_user(uodp_vport, vport, odp_vport->total_len);
+	mutex_unlock(&vport->dp->mutex);
+exit_free:
+	kfree_skb(skb);
+exit:
+	return err;
 }
 
-static void set_listen_mask(struct file *f, int listen_mask)
+static int dump_vport(struct odp_vport __user *uodp_vport)
 {
-	f->private_data = (void*)(long)listen_mask;
+	struct nlattr *a[ODPPT_MAX + 1];
+	struct odp_vport *odp_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;
+
+	dp = get_dp_locked(odp_vport->dp_idx);
+	err = -ENODEV;
+	if (!dp)
+		goto exit_free;
+
+	for (port_no = odp_vport->port_no; port_no < DP_MAX_PORTS; port_no++) {
+		struct vport *vport = get_vport_protected(dp, port_no);
+		if (vport) {
+			err = copy_vport_to_user(uodp_vport, vport, odp_vport->total_len);
+			goto exit_unlock_dp;
+		}
+	}
+	err = -ENODEV;
+
+exit_unlock_dp:
+	mutex_unlock(&dp->mutex);
+exit_free:
+	kfree_skb(skb);
+exit:
+	return err;
 }
 
 static long openvswitch_ioctl(struct file *f, unsigned int cmd,
@@ -1443,7 +1633,7 @@ static long openvswitch_ioctl(struct file *f, unsigned int cmd,
 {
 	int dp_idx = iminor(f->f_dentry->d_inode);
 	struct datapath *dp;
-	int drop_frags, listeners, port_no;
+	int drop_frags, listeners;
 	unsigned int sflow_probability;
 	int err;
 
@@ -1457,46 +1647,21 @@ static long openvswitch_ioctl(struct file *f, unsigned int cmd,
 		err = destroy_dp(dp_idx);
 		goto exit;
 
-	case ODP_VPORT_ATTACH:
-		err = attach_port(dp_idx, (struct odp_port __user *)argp);
-		goto exit;
-
-	case ODP_VPORT_DETACH:
-		err = get_user(port_no, (int __user *)argp);
-		if (!err)
-			err = detach_port(dp_idx, port_no);
-		goto exit;
-
-	case ODP_VPORT_QUERY:
-		err = query_port(dp_idx, (struct odp_port __user *)argp);
-		goto exit;
-
-	case ODP_VPORT_MOD:
-		err = vport_user_mod((struct odp_port __user *)argp);
-		goto exit;
-
-	case ODP_VPORT_STATS_GET:
-		err = vport_user_stats_get((struct odp_vport_stats_req __user *)argp);
-		goto exit;
-
-	case ODP_VPORT_STATS_SET:
-		err = vport_user_stats_set((struct odp_vport_stats_req __user *)argp);
+	case ODP_VPORT_NEW:
+		err = attach_vport((struct odp_vport __user *)argp);
 		goto exit;
 
-	case ODP_VPORT_ETHER_GET:
-		err = vport_user_ether_get((struct odp_vport_ether __user *)argp);
+	case ODP_VPORT_GET:
+		err = get_vport((struct odp_vport __user *)argp);
 		goto exit;
 
-	case ODP_VPORT_ETHER_SET:
-		err = vport_user_ether_set((struct odp_vport_ether __user *)argp);
+	case ODP_VPORT_DEL:
+	case ODP_VPORT_SET:
+		err = modify_vport(cmd, (struct odp_vport __user *)argp);
 		goto exit;
 
-	case ODP_VPORT_MTU_GET:
-		err = vport_user_mtu_get((struct odp_vport_mtu __user *)argp);
-		goto exit;
-
-	case ODP_VPORT_MTU_SET:
-		err = vport_user_mtu_set((struct odp_vport_mtu __user *)argp);
+	case ODP_VPORT_DUMP:
+		err = dump_vport((struct odp_vport __user *)argp);
 		goto exit;
 	}
 
@@ -1550,10 +1715,6 @@ static long openvswitch_ioctl(struct file *f, unsigned int cmd,
 			dp->sflow_probability = sflow_probability;
 		break;
 
-	case ODP_VPORT_DUMP:
-		err = dump_port(dp, (struct odp_vport_dump __user *)argp);
-		break;
-
 	case ODP_FLOW_FLUSH:
 		err = flush_flows(dp);
 		break;
@@ -1598,20 +1759,6 @@ static int dp_has_packet_of_interest(struct datapath *dp, int listeners)
 }
 
 #ifdef CONFIG_COMPAT
-static int compat_dump_port(struct datapath *dp, struct compat_odp_vport_dump __user *compat)
-{
-	struct odp_vport_dump dump;
-	compat_uptr_t port;
-
-	if (!access_ok(VERIFY_READ, compat, sizeof(struct compat_odp_vport_dump)) ||
-	    __get_user(port, &compat->port) ||
-	    __get_user(dump.port_no, &compat->port_no))
-		return -EFAULT;
-
-	dump.port = (struct odp_port __force *)compat_ptr(port);
-	return do_dump_port(dp, &dump);
-}
-
 static int compat_get_flow(struct odp_flow *flow, const struct compat_odp_flow __user *compat)
 {
 	compat_uptr_t key, actions;
@@ -1809,15 +1956,11 @@ static long openvswitch_compat_ioctl(struct file *f, unsigned int cmd, unsigned
 		return openvswitch_ioctl(f, cmd, argp);
 
 	case ODP_DP_CREATE:
-	case ODP_VPORT_ATTACH:
-	case ODP_VPORT_DETACH:
-	case ODP_VPORT_MOD:
-	case ODP_VPORT_MTU_SET:
-	case ODP_VPORT_MTU_GET:
-	case ODP_VPORT_ETHER_SET:
-	case ODP_VPORT_ETHER_GET:
-	case ODP_VPORT_STATS_SET:
-	case ODP_VPORT_STATS_GET:
+	case ODP_VPORT_NEW:
+	case ODP_VPORT_DEL:
+	case ODP_VPORT_GET:
+	case ODP_VPORT_SET:
+	case ODP_VPORT_DUMP:
 	case ODP_DP_STATS:
 	case ODP_GET_DROP_FRAGS:
 	case ODP_SET_DROP_FRAGS:
@@ -1825,7 +1968,6 @@ static long openvswitch_compat_ioctl(struct file *f, unsigned int cmd, unsigned
 	case ODP_GET_LISTEN_MASK:
 	case ODP_SET_SFLOW_PROBABILITY:
 	case ODP_GET_SFLOW_PROBABILITY:
-	case ODP_VPORT_QUERY:
 		/* Ioctls that just need their pointer argument extended. */
 		return openvswitch_ioctl(f, cmd, (unsigned long)compat_ptr(argp));
 	}
@@ -1836,10 +1978,6 @@ static long openvswitch_compat_ioctl(struct file *f, unsigned int cmd, unsigned
 		goto exit;
 
 	switch (cmd) {
-	case ODP_VPORT_DUMP32:
-		err = compat_dump_port(dp, compat_ptr(argp));
-		break;
-
 	case ODP_FLOW_PUT32:
 		err = compat_put_flow(dp, compat_ptr(argp));
 		break;
diff --git a/datapath/odp-compat.h b/datapath/odp-compat.h
index eca6d8b..11089dd 100644
--- a/datapath/odp-compat.h
+++ b/datapath/odp-compat.h
@@ -15,7 +15,6 @@
 #include "openvswitch/datapath-protocol.h"
 #include <linux/compat.h>
 
-#define ODP_VPORT_DUMP32	_IOWR('O', 10, struct compat_odp_vport_dump)
 #define ODP_FLOW_GET32		_IOWR('O', 13, struct compat_odp_flowvec)
 #define ODP_FLOW_PUT32		_IOWR('O', 14, struct compat_odp_flow)
 #define ODP_FLOW_DUMP32		_IOWR('O', 15, struct compat_odp_flow_dump)
@@ -23,11 +22,6 @@
 #define ODP_EXECUTE32		_IOR('O', 18, struct compat_odp_execute)
 #define ODP_FLOW_DEL32		_IOWR('O', 17, struct compat_odp_flow)
 
-struct compat_odp_vport_dump {
-	compat_uptr_t port;
-	u32 port_no;
-};
-
 struct compat_odp_flow {
 	struct odp_flow_stats stats;
 	compat_uptr_t key;
diff --git a/datapath/tunnel.c b/datapath/tunnel.c
index af0df46..ba1bf97 100644
--- a/datapath/tunnel.c
+++ b/datapath/tunnel.c
@@ -1323,14 +1323,24 @@ out:
 	return sent_len;
 }
 
-static int tnl_set_config(const void *config, const struct tnl_ops *tnl_ops,
+static int tnl_set_config(struct nlattr *options, const struct tnl_ops *tnl_ops,
 			  const struct vport *cur_vport,
 			  struct tnl_mutable_config *mutable)
 {
 	const struct vport *old_vport;
 	const struct tnl_mutable_config *old_mutable;
+	struct nlattr *nla;
 
-	mutable->port_config = *(struct tnl_port_config *)config;
+	if (!options)
+		return -EINVAL;
+	nla = nla_find_nested(options, TNLAT_CONFIG);
+	if (!nla || nla_len(nla) != sizeof(struct tnl_port_config))
+		return -EINVAL;
+
+	/* Use memcpy instead of struct assignment since Netlink attributes
+	 * only have 4-byte alignment.
+	 */
+	memcpy(&mutable->port_config, nla_data(nla), sizeof(struct tnl_port_config));
 
 	if (mutable->port_config.daddr == 0)
 		return -EINVAL;
@@ -1399,7 +1409,7 @@ struct vport *tnl_create(const struct vport_parms *parms,
 	get_random_bytes(&initial_frag_id, sizeof(int));
 	atomic_set(&tnl_vport->frag_id, initial_frag_id);
 
-	err = tnl_set_config(parms->config, tnl_ops, NULL, mutable);
+	err = tnl_set_config(parms->options, tnl_ops, NULL, mutable);
 	if (err)
 		goto error_free_mutable;
 
@@ -1426,7 +1436,7 @@ error:
 	return ERR_PTR(err);
 }
 
-int tnl_modify(struct vport *vport, struct odp_port *port)
+int tnl_set_options(struct vport *vport, struct nlattr *options)
 {
 	struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
 	struct tnl_mutable_config *mutable;
@@ -1439,7 +1449,7 @@ int tnl_modify(struct vport *vport, struct odp_port *port)
 		goto error;
 	}
 
-	err = tnl_set_config(port->config, tnl_vport->tnl_ops, vport, mutable);
+	err = tnl_set_config(options, tnl_vport->tnl_ops, vport, mutable);
 	if (err)
 		goto error_free;
 
@@ -1457,6 +1467,15 @@ error:
 	return err;
 }
 
+int tnl_get_options(const struct vport *vport, struct sk_buff *skb)
+{
+	const struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
+	struct tnl_port_config *port_config;
+
+	port_config = &rcu_dereference_rtnl(tnl_vport->mutable)->port_config;
+	return nla_put(skb, TNLAT_CONFIG, sizeof(struct tnl_port_config), port_config);
+}
+
 static void free_port_rcu(struct rcu_head *rcu)
 {
 	struct tnl_vport *tnl_vport = container_of(rcu,
@@ -1528,15 +1547,6 @@ const unsigned char *tnl_get_addr(const struct vport *vport)
 	return rcu_dereference_rtnl(tnl_vport->mutable)->eth_addr;
 }
 
-void tnl_get_config(const struct vport *vport, void *config)
-{
-	const struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
-	struct tnl_port_config *port_config;
-	
-	port_config = &rcu_dereference_rtnl(tnl_vport->mutable)->port_config;
-	memcpy(config, port_config, sizeof(*port_config));
-}
-
 int tnl_get_mtu(const struct vport *vport)
 {
 	const struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
diff --git a/datapath/tunnel.h b/datapath/tunnel.h
index 29f5922..6c167fe 100644
--- a/datapath/tunnel.h
+++ b/datapath/tunnel.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010 Nicira Networks.
+ * Copyright (c) 2010, 2011 Nicira Networks.
  * Distributed under the terms of the GNU GPL version 2.
  *
  * Significant portions of this file may be copied from parts of the Linux
@@ -186,13 +186,15 @@ struct tnl_vport {
 
 struct vport *tnl_create(const struct vport_parms *, const struct vport_ops *,
 			 const struct tnl_ops *);
-int tnl_modify(struct vport *, struct odp_port *);
 int tnl_destroy(struct vport *);
+
+int tnl_set_options(struct vport *, struct nlattr *);
+int tnl_get_options(const struct vport *, struct sk_buff *);
+
 int tnl_set_mtu(struct vport *vport, int mtu);
 int tnl_set_addr(struct vport *vport, const unsigned char *addr);
 const char *tnl_get_name(const struct vport *vport);
 const unsigned char *tnl_get_addr(const struct vport *vport);
-void tnl_get_config(const struct vport *vport, void *config);
 int tnl_get_mtu(const struct vport *vport);
 int tnl_send(struct vport *vport, struct sk_buff *skb);
 void tnl_rcv(struct vport *vport, struct sk_buff *skb);
diff --git a/datapath/vport-capwap.c b/datapath/vport-capwap.c
index 4ef5386..41de500 100644
--- a/datapath/vport-capwap.c
+++ b/datapath/vport-capwap.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010 Nicira Networks.
+ * Copyright (c) 2010, 2011 Nicira Networks.
  * Distributed under the terms of the GNU GPL version 2.
  *
  * Significant portions of this file may be copied from parts of the Linux
@@ -650,13 +650,13 @@ const struct vport_ops capwap_vport_ops = {
 	.init		= capwap_init,
 	.exit		= capwap_exit,
 	.create		= capwap_create,
-	.modify		= tnl_modify,
 	.destroy	= tnl_destroy,
 	.set_mtu	= tnl_set_mtu,
 	.set_addr	= tnl_set_addr,
 	.get_name	= tnl_get_name,
 	.get_addr	= tnl_get_addr,
-	.get_config	= tnl_get_config,
+	.get_options	= tnl_get_options,
+	.set_options	= tnl_set_options,
 	.get_dev_flags	= vport_gen_get_dev_flags,
 	.is_running	= vport_gen_is_running,
 	.get_operstate	= vport_gen_get_operstate,
diff --git a/datapath/vport-gre.c b/datapath/vport-gre.c
index 88e1abe..4a9ca41 100644
--- a/datapath/vport-gre.c
+++ b/datapath/vport-gre.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010 Nicira Networks.
+ * Copyright (c) 2010, 2011 Nicira Networks.
  * Distributed under the terms of the GNU GPL version 2.
  *
  * Significant portions of this file may be copied from parts of the Linux
@@ -392,13 +392,13 @@ const struct vport_ops gre_vport_ops = {
 	.init		= gre_init,
 	.exit		= gre_exit,
 	.create		= gre_create,
-	.modify		= tnl_modify,
 	.destroy	= tnl_destroy,
 	.set_mtu	= tnl_set_mtu,
 	.set_addr	= tnl_set_addr,
 	.get_name	= tnl_get_name,
 	.get_addr	= tnl_get_addr,
-	.get_config	= tnl_get_config,
+	.get_options	= tnl_get_options,
+	.set_options	= tnl_set_options,
 	.get_dev_flags	= vport_gen_get_dev_flags,
 	.is_running	= vport_gen_is_running,
 	.get_operstate	= vport_gen_get_operstate,
diff --git a/datapath/vport-patch.c b/datapath/vport-patch.c
index 012aa6f..242414b 100644
--- a/datapath/vport-patch.c
+++ b/datapath/vport-patch.c
@@ -85,14 +85,29 @@ static void patch_exit(void)
 	kfree(peer_table);
 }
 
-static int patch_set_config(struct vport *vport, const void *config, 
+static struct nla_policy patch_policy[ODPPT_PATCH_MAX + 1] = {
+	[ODPPT_PATCH_PEER] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ - 1 },
+};
+
+static int patch_set_config(struct vport *vport, const struct nlattr *options,
 			    struct patch_config *patchconf)
 {
 	struct patch_vport *patch_vport = patch_vport_priv(vport);
-	char peer_name[IFNAMSIZ];
+	struct nlattr *a[ODPPT_PATCH_MAX + 1];
+	const char *peer_name;
+	int err;
+
+	if (!options)
+		return -EINVAL;
 
-	strlcpy(peer_name, config, IFNAMSIZ);
+	err = nla_parse_nested(a, ODPPT_PATCH_MAX, options, patch_policy);
+	if (err)
+		return err;
+
+	if (!a[ODPPT_PATCH_PEER] || VERIFY_NUL_STRING(a[ODPPT_PATCH_PEER]))
+		return -EINVAL;
 
+	peer_name = nla_data(a[ODPPT_PATCH_PEER]);
 	if (!strcmp(patch_vport->name, peer_name))
 		return -EINVAL;
 
@@ -125,7 +140,7 @@ static struct vport *patch_create(const struct vport_parms *parms)
 		goto error_free_vport;
 	}
 
-	err = patch_set_config(vport, parms->config, patchconf);
+	err = patch_set_config(vport, parms->options, patchconf);
 	if (err)
 		goto error_free_patchconf;
 
@@ -152,7 +167,27 @@ error:
 	return ERR_PTR(err);
 }
 
-static int patch_modify(struct vport *vport, struct odp_port *port)
+static void free_port_rcu(struct rcu_head *rcu)
+{
+	struct patch_vport *patch_vport = container_of(rcu,
+					  struct patch_vport, rcu);
+
+	kfree((struct patch_config __force *)patch_vport->patchconf);
+	vport_free(vport_from_priv(patch_vport));
+}
+
+static int patch_destroy(struct vport *vport)
+{
+	struct patch_vport *patch_vport = patch_vport_priv(vport);
+
+	update_peers(patch_vport->name, NULL);
+	hlist_del(&patch_vport->hash_node);
+	call_rcu(&patch_vport->rcu, free_port_rcu);
+
+	return 0;
+}
+
+static int patch_set_options(struct vport *vport, struct nlattr *options)
 {
 	struct patch_vport *patch_vport = patch_vport_priv(vport);
 	struct patch_config *patchconf;
@@ -165,12 +200,12 @@ static int patch_modify(struct vport *vport, struct odp_port *port)
 		goto error;
 	}
 
-	err = patch_set_config(vport, port->config, patchconf);
+	err = patch_set_config(vport, options, patchconf);
 	if (err)
 		goto error_free;
 
 	assign_config_rcu(vport, patchconf);
-	
+
 	hlist_del(&patch_vport->hash_node);
 
 	rcu_assign_pointer(patch_vport->peer, vport_locate(patchconf->peer_name));
@@ -184,26 +219,6 @@ error:
 	return err;
 }
 
-static void free_port_rcu(struct rcu_head *rcu)
-{
-	struct patch_vport *patch_vport = container_of(rcu,
-					  struct patch_vport, rcu);
-
-	kfree((struct patch_config __force *)patch_vport->patchconf);
-	vport_free(vport_from_priv(patch_vport));
-}
-
-static int patch_destroy(struct vport *vport)
-{
-	struct patch_vport *patch_vport = patch_vport_priv(vport);
-
-	update_peers(patch_vport->name, NULL);
-	hlist_del(&patch_vport->hash_node);
-	call_rcu(&patch_vport->rcu, free_port_rcu);
-
-	return 0;
-}
-
 static void update_peers(const char *name, struct vport *vport)
 {
 	struct hlist_head *bucket = hash_bucket(name);
@@ -264,13 +279,12 @@ static const unsigned char *patch_get_addr(const struct vport *vport)
 	return rcu_dereference_rtnl(patch_vport->patchconf)->eth_addr;
 }
 
-static void patch_get_config(const struct vport *vport, void *config)
+static int patch_get_options(const struct vport *vport, struct sk_buff *skb)
 {
-	const struct patch_vport *patch_vport = patch_vport_priv(vport);
-	const char *peer_name;
+	struct patch_vport *patch_vport = patch_vport_priv(vport);
+	struct patch_config *patchconf = rcu_dereference_rtnl(patch_vport->patchconf);
 
-	peer_name = rcu_dereference_rtnl(patch_vport->patchconf)->peer_name;
-	strlcpy(config, peer_name, VPORT_CONFIG_SIZE);
+	return nla_put_string(skb, ODPPT_PATCH_PEER, patchconf->peer_name);
 }
 
 static int patch_get_mtu(const struct vport *vport)
@@ -302,13 +316,13 @@ const struct vport_ops patch_vport_ops = {
 	.init		= patch_init,
 	.exit		= patch_exit,
 	.create		= patch_create,
-	.modify		= patch_modify,
 	.destroy	= patch_destroy,
 	.set_mtu	= patch_set_mtu,
 	.set_addr	= patch_set_addr,
 	.get_name	= patch_get_name,
 	.get_addr	= patch_get_addr,
-	.get_config	= patch_get_config,
+	.get_options	= patch_get_options,
+	.set_options	= patch_set_options,
 	.get_dev_flags	= vport_gen_get_dev_flags,
 	.is_running	= vport_gen_is_running,
 	.get_operstate	= vport_gen_get_operstate,
diff --git a/datapath/vport.c b/datapath/vport.c
index a391932..0fa4dd6 100644
--- a/datapath/vport.c
+++ b/datapath/vport.c
@@ -177,277 +177,6 @@ void vport_exit(void)
 	kfree(dev_table);
 }
 
-/**
- *	vport_user_mod - modify existing vport device (for userspace callers)
- *
- * @uport: New configuration for vport
- *
- * Modifies an existing device with the specified configuration (which is
- * dependent on device type).  This function is for userspace callers and
- * assumes no locks are held.
- */
-int vport_user_mod(const struct odp_port __user *uport)
-{
-	struct odp_port port;
-	struct vport *vport;
-	int err;
-
-	if (copy_from_user(&port, uport, sizeof(port)))
-		return -EFAULT;
-
-	port.devname[IFNAMSIZ - 1] = '\0';
-
-	rtnl_lock();
-
-	vport = vport_locate(port.devname);
-	if (!vport) {
-		err = -ENODEV;
-		goto out;
-	}
-
-	vport_lock();
-	err = vport_mod(vport, &port);
-	vport_unlock();
-
-out:
-	rtnl_unlock();
-	return err;
-}
-
-/**
- *	vport_user_stats_get - retrieve device stats (for userspace callers)
- *
- * @ustats_req: Stats request parameters.
- *
- * Retrieves transmit, receive, and error stats for the given device.  This
- * function is for userspace callers and assumes no locks are held.
- */
-int vport_user_stats_get(struct odp_vport_stats_req __user *ustats_req)
-{
-	struct odp_vport_stats_req stats_req;
-	struct vport *vport;
-	int err;
-
-	if (copy_from_user(&stats_req, ustats_req, sizeof(struct odp_vport_stats_req)))
-		return -EFAULT;
-
-	stats_req.devname[IFNAMSIZ - 1] = '\0';
-
-	vport_lock();
-
-	vport = vport_locate(stats_req.devname);
-	if (!vport) {
-		err = -ENODEV;
-		goto out;
-	}
-
-	err = vport_get_stats(vport, &stats_req.stats);
-
-out:
-	vport_unlock();
-
-	if (!err)
-		if (copy_to_user(ustats_req, &stats_req, sizeof(struct odp_vport_stats_req)))
-			err = -EFAULT;
-
-	return err;
-}
-
-/**
- *	vport_user_stats_set - sets offset device stats (for userspace callers)
- *
- * @ustats_req: Stats set parameters.
- *
- * Provides a set of transmit, receive, and error stats to be added as an
- * offset to the collect data when stats are retreived.  Some devices may not
- * support setting the stats, in which case the result will always be
- * -EOPNOTSUPP.  This function is for userspace callers and assumes no locks
- * are held.
- */
-int vport_user_stats_set(struct odp_vport_stats_req __user *ustats_req)
-{
-	struct odp_vport_stats_req stats_req;
-	struct vport *vport;
-	int err;
-
-	if (copy_from_user(&stats_req, ustats_req, sizeof(struct odp_vport_stats_req)))
-		return -EFAULT;
-
-	stats_req.devname[IFNAMSIZ - 1] = '\0';
-
-	rtnl_lock();
-	vport_lock();
-
-	vport = vport_locate(stats_req.devname);
-	if (!vport) {
-		err = -ENODEV;
-		goto out;
-	}
-
-	err = vport_set_stats(vport, &stats_req.stats);
-
-out:
-	vport_unlock();
-	rtnl_unlock();
-	return err;
-}
-
-
-/**
- *	vport_user_ether_get - retrieve device Ethernet address (for userspace callers)
- *
- * @uvport_ether: Ethernet address request parameters.
- *
- * Retrieves the Ethernet address of the given device.  This function is for
- * userspace callers and assumes no locks are held.
- */
-int vport_user_ether_get(struct odp_vport_ether __user *uvport_ether)
-{
-	struct odp_vport_ether vport_ether;
-	struct vport *vport;
-	int err = 0;
-
-	if (copy_from_user(&vport_ether, uvport_ether, sizeof(struct odp_vport_ether)))
-		return -EFAULT;
-
-	vport_ether.devname[IFNAMSIZ - 1] = '\0';
-
-	vport_lock();
-
-	vport = vport_locate(vport_ether.devname);
-	if (!vport) {
-		err = -ENODEV;
-		goto out;
-	}
-
-	rcu_read_lock();
-	memcpy(vport_ether.ether_addr, vport_get_addr(vport), ETH_ALEN);
-	rcu_read_unlock();
-
-out:
-	vport_unlock();
-
-	if (!err)
-		if (copy_to_user(uvport_ether, &vport_ether, sizeof(struct odp_vport_ether)))
-			err = -EFAULT;
-
-	return err;
-}
-
-/**
- *	vport_user_ether_set - set device Ethernet address (for userspace callers)
- *
- * @uvport_ether: Ethernet address request parameters.
- *
- * Sets the Ethernet address of the given device.  Some devices may not support
- * setting the Ethernet address, in which case the result will always be
- * -EOPNOTSUPP.  This function is for userspace callers and assumes no locks
- * are held.
- */
-int vport_user_ether_set(struct odp_vport_ether __user *uvport_ether)
-{
-	struct odp_vport_ether vport_ether;
-	struct vport *vport;
-	int err;
-
-	if (copy_from_user(&vport_ether, uvport_ether, sizeof(struct odp_vport_ether)))
-		return -EFAULT;
-
-	vport_ether.devname[IFNAMSIZ - 1] = '\0';
-
-	rtnl_lock();
-	vport_lock();
-
-	vport = vport_locate(vport_ether.devname);
-	if (!vport) {
-		err = -ENODEV;
-		goto out;
-	}
-
-	err = vport_set_addr(vport, vport_ether.ether_addr);
-
-out:
-	vport_unlock();
-	rtnl_unlock();
-	return err;
-}
-
-/**
- *	vport_user_mtu_get - retrieve device MTU (for userspace callers)
- *
- * @uvport_mtu: MTU request parameters.
- *
- * Retrieves the MTU of the given device.  This function is for userspace
- * callers and assumes no locks are held.
- */
-int vport_user_mtu_get(struct odp_vport_mtu __user *uvport_mtu)
-{
-	struct odp_vport_mtu vport_mtu;
-	struct vport *vport;
-	int err = 0;
-
-	if (copy_from_user(&vport_mtu, uvport_mtu, sizeof(struct odp_vport_mtu)))
-		return -EFAULT;
-
-	vport_mtu.devname[IFNAMSIZ - 1] = '\0';
-
-	vport_lock();
-
-	vport = vport_locate(vport_mtu.devname);
-	if (!vport) {
-		err = -ENODEV;
-		goto out;
-	}
-
-	vport_mtu.mtu = vport_get_mtu(vport);
-
-out:
-	vport_unlock();
-
-	if (!err)
-		if (copy_to_user(uvport_mtu, &vport_mtu, sizeof(struct odp_vport_mtu)))
-			err = -EFAULT;
-
-	return err;
-}
-
-/**
- *	vport_user_mtu_set - set device MTU (for userspace callers)
- *
- * @uvport_mtu: MTU request parameters.
- *
- * Sets the MTU of the given device.  Some devices may not support setting the
- * MTU, in which case the result will always be -EOPNOTSUPP.  This function is
- * for userspace callers and assumes no locks are held.
- */
-int vport_user_mtu_set(struct odp_vport_mtu __user *uvport_mtu)
-{
-	struct odp_vport_mtu vport_mtu;
-	struct vport *vport;
-	int err;
-
-	if (copy_from_user(&vport_mtu, uvport_mtu, sizeof(struct odp_vport_mtu)))
-		return -EFAULT;
-
-	vport_mtu.devname[IFNAMSIZ - 1] = '\0';
-
-	rtnl_lock();
-	vport_lock();
-
-	vport = vport_locate(vport_mtu.devname);
-	if (!vport) {
-		err = -ENODEV;
-		goto out;
-	}
-
-	err = vport_set_mtu(vport, vport_mtu.mtu);
-
-out:
-	vport_unlock();
-	rtnl_unlock();
-	return err;
-}
-
 static struct hlist_head *hash_bucket(const char *name)
 {
 	unsigned int hash = full_name_hash(name, strlen(name));
@@ -610,23 +339,28 @@ out:
 }
 
 /**
- *	vport_mod - modify existing vport device (for kernel callers)
+ *	vport_set_options - modify existing vport device (for kernel callers)
  *
  * @vport: vport to modify.
  * @port: New configuration.
  *
  * Modifies an existing device with the specified configuration (which is
- * dependent on device type).  Both RTNL and vport locks must be held.
+ * dependent on device type).  RTNL lock must be held.
  */
-int vport_mod(struct vport *vport, struct odp_port *port)
+int vport_set_options(struct vport *vport, struct nlattr *options)
 {
+	int err;
+
 	ASSERT_RTNL();
-	ASSERT_VPORT();
 
-	if (vport->ops->modify)
-		return vport->ops->modify(vport, port);
+	vport_lock();
+	if (vport->ops->set_options)
+		err = vport->ops->set_options(vport, options);
 	else
-		return -EOPNOTSUPP;
+		err = -EOPNOTSUPP;
+	vport_unlock();
+
+	return err;
 }
 
 /**
@@ -969,7 +703,7 @@ int vport_get_mtu(const struct vport *vport)
 }
 
 /**
- *	vport_get_config - retrieve device configuration
+ *	vport_get_options - retrieve device configuration
  *
  * @vport: vport from which to retrieve the configuration.
  * @config: buffer to store config, which must be at least the length
@@ -978,10 +712,11 @@ int vport_get_mtu(const struct vport *vport)
  * Retrieves the configuration of the given device.  Either RTNL lock or
  * rcu_read_lock must be held.
  */
-void vport_get_config(const struct vport *vport, void *config)
+int vport_get_options(const struct vport *vport, struct sk_buff *skb)
 {
-	if (vport->ops->get_config)
-		vport->ops->get_config(vport, config);
+	if (!vport->ops->get_options)
+		return 0;
+	return vport->ops->get_options(vport, skb);
 }
 
 /**
diff --git a/datapath/vport.h b/datapath/vport.h
index f112745..e9a27c6 100644
--- a/datapath/vport.h
+++ b/datapath/vport.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010 Nicira Networks.
+ * Copyright (c) 2010, 2011 Nicira Networks.
  * Distributed under the terms of the GNU GPL version 2.
  *
  * Significant portions of this file may be copied from parts of the Linux
@@ -23,15 +23,6 @@ struct vport_parms;
 
 /* The following definitions are for users of the vport subsytem: */
 
-int vport_user_mod(const struct odp_port __user *);
-
-int vport_user_stats_get(struct odp_vport_stats_req __user *);
-int vport_user_stats_set(struct odp_vport_stats_req __user *);
-int vport_user_ether_get(struct odp_vport_ether __user *);
-int vport_user_ether_set(struct odp_vport_ether __user *);
-int vport_user_mtu_get(struct odp_vport_mtu __user *);
-int vport_user_mtu_set(struct odp_vport_mtu __user *);
-
 void vport_lock(void);
 void vport_unlock(void);
 
@@ -39,7 +30,6 @@ int vport_init(void);
 void vport_exit(void);
 
 struct vport *vport_add(const struct vport_parms *);
-int vport_mod(struct vport *, struct odp_port *);
 int vport_del(struct vport *);
 
 struct vport *vport_locate(const char *name);
@@ -63,7 +53,9 @@ int vport_get_ifindex(const struct vport *);
 int vport_get_iflink(const struct vport *);
 
 int vport_get_mtu(const struct vport *);
-void vport_get_config(const struct vport *, void *);
+
+int vport_set_options(struct vport *, struct nlattr *options);
+int vport_get_options(const struct vport *, struct sk_buff *);
 
 int vport_send(struct vport *, struct sk_buff *);
 
@@ -133,15 +125,15 @@ struct vport {
  *
  * @name: New vport's name.
  * @type: New vport's type.
- * @config: Kernel copy of 'config' member of &struct odp_port describing
- * configuration for new port.  Exactly %VPORT_CONFIG_SIZE bytes.
+ * @options: %ODPPT_OPTIONS attribute from Netlink message, %NULL if none was
+ * supplied.
  * @dp: New vport's datapath.
  * @port_no: New vport's port number.
  */
 struct vport_parms {
 	const char *name;
 	enum odp_vport_type type;
-	const void *config;
+	struct nlattr *options;
 
 	/* For vport_alloc(). */
 	struct datapath *dp;
@@ -196,9 +188,11 @@ struct vport_ops {
 
 	/* Called with RTNL lock. */
 	struct vport *(*create)(const struct vport_parms *);
-	int (*modify)(struct vport *, struct odp_port *);
 	int (*destroy)(struct vport *);
 
+	int (*set_options)(struct vport *, struct nlattr *);
+	int (*get_options)(const struct vport *, struct sk_buff *);
+
 	int (*set_mtu)(struct vport *, int mtu);
 	int (*set_addr)(struct vport *, const unsigned char *);
 	int (*set_stats)(const struct vport *, struct rtnl_link_stats64 *);
diff --git a/include/openvswitch/datapath-protocol.h b/include/openvswitch/datapath-protocol.h
index b6ed979..ae82531 100644
--- a/include/openvswitch/datapath-protocol.h
+++ b/include/openvswitch/datapath-protocol.h
@@ -82,10 +82,11 @@
 #define ODP_GET_LISTEN_MASK     _IOW('O', 5, int)
 #define ODP_SET_LISTEN_MASK     _IOR('O', 6, int)
 
-#define ODP_VPORT_ATTACH        _IOR('O', 7, struct odp_port)
-#define ODP_VPORT_DETACH        _IOR('O', 8, int)
-#define ODP_VPORT_QUERY         _IOWR('O', 9, struct odp_port)
-#define ODP_VPORT_DUMP          _IOWR('O', 10, struct odp_vport_dump)
+#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_GET            _IOWR('O', 13, struct odp_flowvec)
 #define ODP_FLOW_PUT            _IOWR('O', 14, struct odp_flow)
@@ -98,13 +99,6 @@
 #define ODP_SET_SFLOW_PROBABILITY _IOR('O', 19, int)
 #define ODP_GET_SFLOW_PROBABILITY _IOW('O', 20, int)
 
-#define ODP_VPORT_MOD           _IOR('O', 22, struct odp_port)
-#define ODP_VPORT_STATS_GET     _IOWR('O', 24, struct odp_vport_stats_req)
-#define ODP_VPORT_ETHER_GET     _IOWR('O', 25, struct odp_vport_ether)
-#define ODP_VPORT_ETHER_SET     _IOW('O', 26, struct odp_vport_ether)
-#define ODP_VPORT_MTU_GET       _IOWR('O', 27, struct odp_vport_mtu)
-#define ODP_VPORT_MTU_SET       _IOW('O', 28, struct odp_vport_mtu)
-#define ODP_VPORT_STATS_SET     _IOWR('O', 29, struct odp_vport_stats_req)
 
 struct odp_stats {
     /* Flows. */
@@ -175,16 +169,6 @@ struct odp_upcall {
 	uint32_t type;
 };
 
-#define VPORT_CONFIG_SIZE     32
-struct odp_port {
-    char devname[16];           /* IFNAMSIZ */
-    uint32_t type;		/* One of ODPVT_*. */
-    uint16_t port;
-    uint16_t dp_idx;
-    uint32_t reserved2;
-    __aligned_u64 config[VPORT_CONFIG_SIZE / 8]; /* type-specific */
-};
-
 enum odp_vport_type {
 	ODPVT_UNSPEC,
 	ODPVT_NETDEV,		/* network device */
@@ -198,20 +182,47 @@ enum odp_vport_type {
 #define ODPVT_MAX (__ODPVT_MAX - 1)
 
 /**
- * struct odp_vport_dump - ODP_VPORT_DUMP argument.
- * @port: Points to port structure to fill in.
- * @port_no: Minimum port number of interest.
+ * struct odp_vport - header with basic information about a virtual port.
+ * @dp_idx: Number of datapath to which the vport belongs.
+ * @port_no: Port number within datapath.
+ * @type: One of the %ODPVT_* constants.
+ * @len: Length of this structure plus the Netlink attributes following it.
+ * @total_len: Total space available for kernel reply to request.
  *
- * Used to iterate through vports one at a time.  The kernel fills in @port
- * with the information for the configured port with the smallest port number
- * greater than or equal to @port_no.  If there is no such port, it sets
- * @port->devname to the empty string.
+ * Followed by &struct nlattr attributes, whose types are drawn from %ODPPT_*,
+ * up to a length of @len bytes including the &struct odp_vport header.
  */
-struct odp_vport_dump {
-    struct odp_port *port;
-    uint32_t port_no;
+struct odp_vport {
+	int32_t dp_idx;
+	int32_t port_no;
+	uint32_t type;
+	uint32_t len;
+	uint32_t total_len;
+};
+
+enum {
+        ODPPT_UNSPEC,
+        ODPPT_NAME,		/* string name, up to IFNAMSIZ bytes long */
+        ODPPT_STATS,		/* struct rtnl_link_stats64 */
+        ODPPT_ADDRESS,		/* hardware address */
+        ODPPT_MTU,		/* 32-bit maximum transmission unit */
+        ODPPT_OPTIONS,		/* nested attributes, varies by vport type */
+        ODPPT_IFINDEX,		/* 32-bit ifindex of backing netdev */
+        ODPPT_IFLINK,		/* 32-bit ifindex on which packets are sent */
+        __ODPPT_MAX
 };
 
+#define ODPPT_MAX (__ODPPT_MAX - 1)
+
+/* ODPPT_OPTIONS attributes for patch vports. */
+enum {
+	ODPPT_PATCH_UNSPEC,
+	ODPPT_PATCH_PEER,	/* name of peer vport, as a string */
+	__ODPPT_PATCH_MAX
+};
+
+#define ODPPT_PATCH_MAX (__ODPPT_PATCH_MAX - 1)
+
 struct odp_flow_stats {
     uint64_t n_packets;         /* Number of matched packets. */
     uint64_t n_bytes;           /* Number of matched bytes. */
@@ -352,31 +363,4 @@ struct odp_execute {
     uint32_t length;
 };
 
-#define VPORT_TYPE_SIZE     16
-struct odp_vport_add {
-    char port_type[VPORT_TYPE_SIZE];
-    char devname[16];           /* IFNAMSIZ */
-    void *config;
-};
-
-struct odp_vport_mod {
-    char devname[16];           /* IFNAMSIZ */
-    void *config;
-};
-
-struct odp_vport_stats_req {
-    char devname[16];           /* IFNAMSIZ */
-    struct rtnl_link_stats64 stats;
-};
-
-struct odp_vport_ether {
-    char devname[16];           /* IFNAMSIZ */
-    unsigned char ether_addr[6];
-};
-
-struct odp_vport_mtu {
-    char devname[16];           /* IFNAMSIZ */
-    uint16_t mtu;
-};
-
 #endif  /* openvswitch/datapath-protocol.h */
diff --git a/include/openvswitch/tunnel.h b/include/openvswitch/tunnel.h
index 44facfa..1e7d2b4 100644
--- a/include/openvswitch/tunnel.h
+++ b/include/openvswitch/tunnel.h
@@ -43,6 +43,15 @@
 #include <linux/types.h>
 #include "openvswitch/datapath-protocol.h"
 
+/* ODPPT_OPTIONS attributes for tunnels. */
+enum {
+	TNLAT_UNSPEC,
+	TNLAT_CONFIG,		/* struct tnl_port_config */
+	__TNLAT_MAX
+};
+
+#define TNLAT_MAX (__TNLAT_MAX - 1)
+
 #define TNL_F_CSUM		(1 << 1) /* Checksum packets. */
 #define TNL_F_IN_KEY_MATCH	(1 << 2) /* Store the key in tun_id to match in flow table. */
 #define TNL_F_OUT_KEY_ACTION	(1 << 3) /* Get the key from a SET_TUNNEL action. */
@@ -52,7 +61,7 @@
 #define TNL_F_HDR_CACHE		(1 << 7) /* Enable tunnel header caching. */
 #define TNL_F_IPSEC		(1 << 8) /* Traffic is IPsec encrypted. */
 
-/* This goes in the "config" member of struct odp_port for tunnel vports. */
+/* Contents of TNLAT_CONFIG attribute, nested inside ODPPT_OPTIONS. */
 struct tnl_port_config {
 	__aligned_be64	in_key;
 	__aligned_be64	out_key;
diff --git a/lib/dpif-linux.c b/lib/dpif-linux.c
index 9c0974f..f931e25 100644
--- a/lib/dpif-linux.c
+++ b/lib/dpif-linux.c
@@ -71,11 +71,11 @@ static struct vlog_rate_limit error_rl = VLOG_RATE_LIMIT_INIT(9999, 5);
 
 static int do_ioctl(const struct dpif *, int cmd, const void *arg);
 static int lookup_internal_device(const char *name, int *dp_idx, int *port_no);
-static int lookup_minor(const char *name, int *minor);
-static int finish_open(struct dpif *, const char *local_ifname);
+static int open_dpif(const struct dpif_linux_vport *local_vport,
+                     struct dpif **);
 static int get_openvswitch_major(void);
-static int create_minor(const char *name, int minor, struct dpif **dpifp);
-static int open_minor(int minor, struct dpif **dpifp);
+static int create_minor(const char *name, int minor);
+static int open_minor(int minor, int *fdp);
 static int make_openvswitch_device(int minor, char **fnp);
 static void dpif_linux_port_changed(const struct rtnetlink_link_change *,
                                     void *dpif);
@@ -122,60 +122,102 @@ static int
 dpif_linux_open(const struct dpif_class *class OVS_UNUSED, const char *name,
                 bool create, struct dpif **dpifp)
 {
+    struct dpif_linux_vport request, reply;
+    struct ofpbuf *buf;
     int minor;
+    int error;
 
     minor = !strncmp(name, "dp", 2)
             && isdigit((unsigned char)name[2]) ? atoi(name + 2) : -1;
     if (create) {
         if (minor >= 0) {
-            return create_minor(name, minor, dpifp);
+            error = create_minor(name, minor);
+            if (error) {
+                return error;
+            }
         } else {
             /* Scan for unused minor number. */
-            for (minor = 0; minor < ODP_MAX; minor++) {
-                int error = create_minor(name, minor, dpifp);
-                if (error != EBUSY) {
+            for (minor = 0; ; minor++) {
+                if (minor >= ODP_MAX) {
+                    /* All datapath numbers in use. */
+                    return ENOBUFS;
+                }
+
+                error = create_minor(name, minor);
+                if (!error) {
+                    break;
+                } else if (error != EBUSY) {
                     return error;
                 }
             }
-
-            /* All datapath numbers in use. */
-            return ENOBUFS;
         }
+    }
+
+    dpif_linux_vport_init(&request);
+    request.cmd = ODP_VPORT_GET;
+    if (minor >= 0) {
+        request.dp_idx = minor;
     } else {
-        struct dpif_linux *dpif;
-        struct odp_port port;
-        int error;
+        request.name = name;
+    }
 
-        if (minor < 0) {
-            error = lookup_minor(name, &minor);
-            if (error) {
-                return error;
-            }
-        }
+    error = dpif_linux_vport_transact(&request, &reply, &buf);
+    if (error) {
+        return error;
+    } else if (reply.port_no != ODPP_LOCAL) {
+        /* This is an Open vSwitch device but not the local port.  We
+         * intentionally support only using the name of the local port as the
+         * name of a datapath; otherwise, it would be too difficult to
+         * enumerate all the names of a datapath. */
+        error = EOPNOTSUPP;
+    } else {
+        error = open_dpif(&reply, dpifp);
+    }
 
-        error = open_minor(minor, dpifp);
-        if (error) {
-            return error;
-        }
-        dpif = dpif_linux_cast(*dpifp);
-
-        /* We need the local port's ifindex for the poll function.  Start by
-         * getting the local port's name. */
-        memset(&port, 0, sizeof port);
-        port.port = ODPP_LOCAL;
-        if (ioctl(dpif->fd, ODP_VPORT_QUERY, &port)) {
-            error = errno;
-            if (error != ENODEV) {
-                VLOG_WARN("%s: probe returned unexpected error: %s",
-                          dpif_name(*dpifp), strerror(error));
-            }
-            dpif_uninit(*dpifp, true);
-            return error;
-        }
+    ofpbuf_delete(buf);
+    return error;
+}
+
+static int
+open_dpif(const struct dpif_linux_vport *local_vport, struct dpif **dpifp)
+{
+    int dp_idx = local_vport->dp_idx;
+    struct dpif_linux *dpif;
+    char *name;
+    int error;
+    int fd;
 
-        /* Then use that to finish up opening. */
-        return finish_open(&dpif->dpif, port.devname);
+    error = open_minor(dp_idx, &fd);
+    if (error) {
+        goto error;
+    }
+
+    dpif = xmalloc(sizeof *dpif);
+    error = rtnetlink_link_notifier_register(&dpif->port_notifier,
+                                             dpif_linux_port_changed, dpif);
+    if (error) {
+        goto error_free;
     }
+
+    name = xasprintf("dp%d", dp_idx);
+    dpif_init(&dpif->dpif, &dpif_linux_class, name, dp_idx, dp_idx);
+    free(name);
+
+    dpif->fd = fd;
+    dpif->local_ifname = xstrdup(local_vport->name);
+    dpif->local_ifindex = local_vport->ifindex;
+    dpif->minor = dp_idx;
+    shash_init(&dpif->changed_ports);
+    dpif->change_error = false;
+    *dpifp = &dpif->dpif;
+
+    return 0;
+
+error_free:
+    free(dpif);
+    close(fd);
+error:
+    return error;
 }
 
 static void
@@ -232,117 +274,86 @@ dpif_linux_set_drop_frags(struct dpif *dpif_, bool drop_frags)
     return do_ioctl(dpif_, ODP_SET_DROP_FRAGS, &drop_frags_int);
 }
 
-static const char *
-vport_type_to_netdev_type(const struct odp_port *odp_port)
-{
-    struct tnl_port_config tnl_config;
-
-    switch (odp_port->type) {
-    case ODPVT_UNSPEC:
-        break;
-
-    case ODPVT_NETDEV:
-        return "system";
-
-    case ODPVT_INTERNAL:
-        return "internal";
-
-    case ODPVT_PATCH:
-        return "patch";
-
-    case ODPVT_GRE:
-        memcpy(&tnl_config, odp_port->config, sizeof tnl_config);
-        return tnl_config.flags & TNL_F_IPSEC ? "ipsec_gre" : "gre";
-
-    case ODPVT_CAPWAP:
-        return "capwap";
-
-    case __ODPVT_MAX:
-        break;
-    }
-
-    VLOG_WARN_RL(&error_rl, "dp%d: port `%s' has unsupported type %"PRIu32,
-                 odp_port->dp_idx, odp_port->devname, odp_port->type);
-    return "unknown";
-}
-
-static enum odp_vport_type
-netdev_type_to_vport_type(const char *type)
-{
-    return (!strcmp(type, "system") ? ODPVT_NETDEV
-            : !strcmp(type, "internal") ? ODPVT_INTERNAL
-            : !strcmp(type, "patch") ? ODPVT_PATCH
-            : !strcmp(type, "gre") || !strcmp(type, "ipsec_gre") ? ODPVT_GRE
-            : !strcmp(type, "capwap") ? ODPVT_CAPWAP
-            : ODPVT_UNSPEC);
-}
-
 static int
 dpif_linux_port_add(struct dpif *dpif, struct netdev *netdev,
                     uint16_t *port_nop)
 {
     const char *name = netdev_get_name(netdev);
     const char *type = netdev_get_type(netdev);
-    struct odp_port port;
+    struct dpif_linux_vport request, reply;
+    const struct ofpbuf *options;
+    struct ofpbuf *buf;
     int error;
 
-    memset(&port, 0, sizeof port);
-    strncpy(port.devname, name, sizeof port.devname);
-    netdev_vport_get_config(netdev, port.config);
-
-    port.type = netdev_type_to_vport_type(type);
-    if (port.type == ODPVT_UNSPEC) {
+    dpif_linux_vport_init(&request);
+    request.cmd = ODP_VPORT_NEW;
+    request.type = netdev_vport_get_vport_type(netdev);
+    if (request.type == ODPVT_UNSPEC) {
         VLOG_WARN_RL(&error_rl, "%s: cannot create port `%s' because it has "
                      "unsupported type `%s'",
                      dpif_name(dpif), name, type);
         return EINVAL;
     }
+    request.name = name;
+
+    options = netdev_vport_get_options(netdev);
+    if (options && options->size) {
+        request.options = options->data;
+        request.options_len = options->size;
+    }
 
-    error = do_ioctl(dpif, ODP_VPORT_ATTACH, &port);
+    error = dpif_linux_vport_transact(&request, &reply, &buf);
     if (!error) {
-        *port_nop = port.port;
+        *port_nop = reply.port_no;
+        ofpbuf_delete(buf);
     }
 
     return error;
 }
 
 static int
-dpif_linux_port_del(struct dpif *dpif_, uint16_t port_no_)
+dpif_linux_port_del(struct dpif *dpif_, uint16_t port_no)
 {
-    int port_no = port_no_;     /* Kernel expects an "int". */
-    return do_ioctl(dpif_, ODP_VPORT_DETACH, &port_no);
+    struct dpif_linux *dpif = dpif_linux_cast(dpif_);
+    struct dpif_linux_vport vport;
+
+    dpif_linux_vport_init(&vport);
+    vport.cmd = ODP_VPORT_DEL;
+    vport.dp_idx = dpif->minor;
+    vport.port_no = port_no;
+    return dpif_linux_vport_transact(&vport, NULL, NULL);
 }
 
 static int
 dpif_linux_port_query__(const struct dpif *dpif, uint32_t port_no,
                         const char *port_name, struct dpif_port *dpif_port)
 {
-    struct odp_port odp_port;
+    struct dpif_linux_vport request;
+    struct dpif_linux_vport reply;
+    struct ofpbuf *buf;
     int error;
 
-    memset(&odp_port, 0, sizeof odp_port);
-    odp_port.port = port_no;
-    strncpy(odp_port.devname, port_name, sizeof odp_port.devname);
+    dpif_linux_vport_init(&request);
+    request.cmd = ODP_VPORT_GET;
+    request.dp_idx = dpif_linux_cast(dpif)->minor;
+    request.port_no = port_no;
+    request.name = port_name;
 
-    error = do_ioctl(dpif, ODP_VPORT_QUERY, &odp_port);
-    if (error) {
-        return error;
-    } else if (odp_port.dp_idx != dpif_linux_cast(dpif)->minor) {
-        /* A vport named 'port_name' exists but in some other datapath.  */
-        return ENOENT;
-    } else {
-        dpif_port->name = xstrdup(odp_port.devname);
-        dpif_port->type = xstrdup(vport_type_to_netdev_type(&odp_port));
-        dpif_port->port_no = odp_port.port;
-        return 0;
+    error = dpif_linux_vport_transact(&request, &reply, &buf);
+    if (!error) {
+        dpif_port->name = xstrdup(reply.name);
+        dpif_port->type = xstrdup(netdev_vport_get_netdev_type(&reply));
+        dpif_port->port_no = reply.port_no;
+        ofpbuf_delete(buf);
     }
+    return error;
 }
 
 static int
 dpif_linux_port_query_by_number(const struct dpif *dpif, uint16_t port_no,
                                 struct dpif_port *dpif_port)
 {
-    return dpif_linux_port_query__(dpif, port_no, "", dpif_port);
+    return dpif_linux_port_query__(dpif, port_no, NULL, dpif_port);
 }
 
 static int
@@ -358,40 +369,53 @@ dpif_linux_flow_flush(struct dpif *dpif_)
     return do_ioctl(dpif_, ODP_FLOW_FLUSH, NULL);
 }
 
+struct dpif_linux_port_state {
+    struct ofpbuf *buf;
+    uint32_t next;
+};
+
+
 static int
 dpif_linux_port_dump_start(const struct dpif *dpif OVS_UNUSED, void **statep)
 {
-    *statep = xzalloc(sizeof(struct odp_port));
+    *statep = xzalloc(sizeof(struct dpif_linux_port_state));
     return 0;
 }
 
 static int
-dpif_linux_port_dump_next(const struct dpif *dpif, void *state,
+dpif_linux_port_dump_next(const struct dpif *dpif, void *state_,
                           struct dpif_port *dpif_port)
 {
-    struct odp_port *odp_port = state;
-    struct odp_vport_dump dump;
+    struct dpif_linux_port_state *state = state_;
+    struct dpif_linux_vport request, reply;
+    struct ofpbuf *buf;
     int error;
 
-    dump.port = odp_port;
-    dump.port_no = odp_port->port;
-    error = do_ioctl(dpif, ODP_VPORT_DUMP, &dump);
+    ofpbuf_delete(state->buf);
+    state->buf = NULL;
+
+    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);
     if (error) {
-        return error;
-    } else if (odp_port->devname[0] == '\0') {
-        return EOF;
+        return error == ENODEV ? EOF : error;
     } else {
-        dpif_port->name = odp_port->devname;
-        dpif_port->type = (char *) vport_type_to_netdev_type(odp_port);
-        dpif_port->port_no = odp_port->port;
-        odp_port->port++;
+        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;
     }
 }
 
 static int
-dpif_linux_port_dump_done(const struct dpif *dpif OVS_UNUSED, void *state)
+dpif_linux_port_dump_done(const struct dpif *dpif OVS_UNUSED, void *state_)
 {
+    struct dpif_linux_port_state *state = state_;
+    ofpbuf_delete(state->buf);
     free(state);
     return 0;
 }
@@ -669,64 +693,31 @@ do_ioctl(const struct dpif *dpif_, int cmd, const void *arg)
 static int
 lookup_internal_device(const char *name, int *dp_idx, int *port_no)
 {
-    struct odp_port odp_port;
-    static int dp0_fd = -1;
-
-    if (dp0_fd < 0) {
-        int error;
-        char *fn;
-
-        error = make_openvswitch_device(0, &fn);
-        if (error) {
-            return error;
-        }
-
-        dp0_fd = open(fn, O_RDONLY | O_NONBLOCK);
-        if (dp0_fd < 0) {
-            VLOG_WARN_RL(&error_rl, "%s: open failed (%s)",
-                         fn, strerror(errno));
-            free(fn);
-            return errno;
-        }
-        free(fn);
-    }
+    struct dpif_linux_vport request, reply;
+    struct ofpbuf *buf;
+    int error;
 
-    memset(&odp_port, 0, sizeof odp_port);
-    strncpy(odp_port.devname, name, sizeof odp_port.devname);
-    if (ioctl(dp0_fd, ODP_VPORT_QUERY, &odp_port)) {
-        if (errno != ENODEV) {
+    dpif_linux_vport_init(&request);
+    request.cmd = ODP_VPORT_GET;
+    request.name = name;
+    error = dpif_linux_vport_transact(&request, &reply, &buf);
+    if (error) {
+        if (error != ENODEV) {
             VLOG_WARN_RL(&error_rl, "%s: vport query failed (%s)",
-                         name, strerror(errno));
+                         name, strerror(error));
         }
-        return errno;
-    } else if (odp_port.type == ODPVT_INTERNAL) {
-        *dp_idx = odp_port.dp_idx;
-        *port_no = odp_port.port;
-        return 0;
-    } else {
-        return EINVAL;
+        return error;
     }
-}
 
-static int
-lookup_minor(const char *name, int *minorp)
-{
-    int minor, port_no;
-    int error;
-
-    error = lookup_internal_device(name, &minor, &port_no);
-    if (error) {
-        return error;
-    } else if (port_no != ODPP_LOCAL) {
-        /* This is an Open vSwitch device but not the local port.  We
-         * intentionally support only using the name of the local port as the
-         * name of a datapath; otherwise, it would be too difficult to
-         * enumerate all the names of a datapath. */
-        return EOPNOTSUPP;
+    if (reply.type == ODPVT_INTERNAL) {
+        *dp_idx = reply.dp_idx;
+        *port_no = reply.port_no;
     } else {
-        *minorp = minor;
-        return 0;
+        error = EINVAL;
     }
+
+    ofpbuf_delete(buf);
+    return error;
 }
 
 bool
@@ -856,78 +847,41 @@ get_major(const char *target)
 }
 
 static int
-finish_open(struct dpif *dpif_, const char *local_ifname)
+create_minor(const char *name, int minor)
 {
-    struct dpif_linux *dpif = dpif_linux_cast(dpif_);
-    dpif->local_ifname = xstrdup(local_ifname);
-    dpif->local_ifindex = if_nametoindex(local_ifname);
-    if (!dpif->local_ifindex) {
-        int error = errno;
-        dpif_uninit(dpif_, true);
-        VLOG_WARN("could not get ifindex of %s device: %s",
-                  local_ifname, strerror(errno));
+    int error;
+    int fd;
+
+    error = open_minor(minor, &fd);
+    if (error) {
         return error;
     }
-    return 0;
-}
 
-static int
-create_minor(const char *name, int minor, struct dpif **dpifp)
-{
-    int error = open_minor(minor, dpifp);
-    if (!error) {
-        error = do_ioctl(*dpifp, ODP_DP_CREATE, name);
-        if (!error) {
-            error = finish_open(*dpifp, name);
-        } else {
-            dpif_uninit(*dpifp, true);
-        }
-    }
+    error = ioctl(fd, ODP_DP_CREATE, name) ? errno : 0;
+    close(fd);
     return error;
 }
 
 static int
-open_minor(int minor, struct dpif **dpifp)
+open_minor(int minor, int *fdp)
 {
     int error;
     char *fn;
-    int fd;
 
     error = make_openvswitch_device(minor, &fn);
     if (error) {
         return error;
     }
 
-    fd = open(fn, O_RDONLY | O_NONBLOCK);
-    if (fd >= 0) {
-        struct dpif_linux *dpif = xmalloc(sizeof *dpif);
-        error = rtnetlink_link_notifier_register(&dpif->port_notifier,
-                                                 dpif_linux_port_changed,
-                                                 dpif);
-        if (!error) {
-            char *name;
-
-            name = xasprintf("dp%d", minor);
-            dpif_init(&dpif->dpif, &dpif_linux_class, name, minor, minor);
-            free(name);
-
-            dpif->fd = fd;
-            dpif->local_ifname = NULL;
-            dpif->minor = minor;
-            dpif->local_ifindex = 0;
-            shash_init(&dpif->changed_ports);
-            dpif->change_error = false;
-            *dpifp = &dpif->dpif;
-        } else {
-            free(dpif);
-        }
-    } else {
+    *fdp = open(fn, O_RDONLY | O_NONBLOCK);
+    if (*fdp < 0) {
         error = errno;
         VLOG_WARN("%s: open failed (%s)", fn, strerror(error));
+        free(fn);
+        return error;
     }
     free(fn);
-
-    return error;
+    return 0;
 }
 
 static void
@@ -949,3 +903,193 @@ dpif_linux_port_changed(const struct rtnetlink_link_change *change,
         dpif->change_error = true;
     }
 }
+
+/* Parses the contents of 'buf', which contains a "struct odp_vport" followed
+ * by Netlink attributes, into 'vport'.  Returns 0 if successful, otherwise a
+ * positive errno value.
+ *
+ * 'vport' will contain pointers into 'buf', so the caller should not free
+ * 'buf' while 'vport' is still in use. */
+static int
+dpif_linux_vport_from_ofpbuf(struct dpif_linux_vport *vport,
+                             const struct ofpbuf *buf)
+{
+    static const struct nl_policy odp_vport_policy[] = {
+        [ODPPT_NAME] = { .type = NL_A_STRING, .max_len = IFNAMSIZ },
+        [ODPPT_STATS] = { .type = NL_A_UNSPEC,
+                          .min_len = sizeof(struct rtnl_link_stats64),
+                          .max_len = sizeof(struct rtnl_link_stats64),
+                          .optional = true },
+        [ODPPT_ADDRESS] = { .type = NL_A_UNSPEC,
+                            .min_len = ETH_ADDR_LEN,
+                            .max_len = ETH_ADDR_LEN,
+                            .optional = true },
+        [ODPPT_MTU] = { .type = NL_A_U32, .optional = true },
+        [ODPPT_OPTIONS] = { .type = NL_A_NESTED, .optional = true },
+        [ODPPT_IFINDEX] = { .type = NL_A_U32, .optional = true },
+        [ODPPT_IFLINK] = { .type = NL_A_U32, .optional = true },
+    };
+
+    struct odp_vport *odp_vport;
+    struct nlattr *a[ARRAY_SIZE(odp_vport_policy)];
+
+    dpif_linux_vport_init(vport);
+
+    if (!nl_policy_parse(buf, sizeof *odp_vport, odp_vport_policy,
+                         a, ARRAY_SIZE(odp_vport_policy))) {
+        return EINVAL;
+    }
+    odp_vport = buf->data;
+
+    vport->dp_idx = odp_vport->dp_idx;
+    vport->port_no = odp_vport->port_no;
+    vport->type = odp_vport->type;
+    vport->name = nl_attr_get_string(a[ODPPT_NAME]);
+    if (a[ODPPT_STATS]) {
+        vport->stats = nl_attr_get(a[ODPPT_STATS]);
+    }
+    if (a[ODPPT_ADDRESS]) {
+        vport->address = nl_attr_get(a[ODPPT_ADDRESS]);
+    }
+    if (a[ODPPT_MTU]) {
+        vport->mtu = nl_attr_get_u32(a[ODPPT_MTU]);
+    }
+    if (a[ODPPT_OPTIONS]) {
+        vport->options = nl_attr_get(a[ODPPT_OPTIONS]);
+        vport->options_len = nl_attr_get_size(a[ODPPT_OPTIONS]);
+    }
+    if (a[ODPPT_IFINDEX]) {
+        vport->ifindex = nl_attr_get_u32(a[ODPPT_IFINDEX]);
+    }
+    if (a[ODPPT_IFLINK]) {
+        vport->iflink = nl_attr_get_u32(a[ODPPT_IFLINK]);
+    }
+    return 0;
+}
+
+/* Appends to 'buf' (which must initially be empty) a "struct odp_vport"
+ * 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;
+
+    ofpbuf_reserve(buf, sizeof odp_vport);
+
+    if (vport->name) {
+        nl_msg_put_string(buf, ODPPT_NAME, vport->name);
+    }
+
+    if (vport->stats) {
+        nl_msg_put_unspec(buf, ODPPT_STATS,
+                          vport->stats, sizeof *vport->stats);
+    }
+
+    if (vport->address) {
+        nl_msg_put_unspec(buf, ODPPT_ADDRESS, vport->address, ETH_ADDR_LEN);
+    }
+
+    if (vport->mtu) {
+        nl_msg_put_u32(buf, ODPPT_MTU, vport->mtu);
+    }
+
+    if (vport->options) {
+        nl_msg_put_nested(buf, ODPPT_OPTIONS,
+                          vport->options, vport->options_len);
+    }
+
+    if (vport->ifindex) {
+        nl_msg_put_u32(buf, ODPPT_IFINDEX, vport->ifindex);
+    }
+
+    if (vport->iflink) {
+        nl_msg_put_u32(buf, ODPPT_IFLINK, vport->iflink);
+    }
+
+    odp_vport = ofpbuf_push_uninit(buf, sizeof *odp_vport);
+    odp_vport->dp_idx = vport->dp_idx;
+    odp_vport->port_no = vport->port_no;
+    odp_vport->type = vport->type;
+    odp_vport->len = buf->size;
+    odp_vport->total_len = (char *) ofpbuf_end(buf) - (char *) buf->data;
+}
+
+/* Clears 'vport' to "empty" values. */
+void
+dpif_linux_vport_init(struct dpif_linux_vport *vport)
+{
+    memset(vport, 0, sizeof *vport);
+}
+
+/* Executes 'request' in the kernel datapath.  If the command fails, returns a
+ * positive errno value.  Otherwise, if 'reply' and 'bufp' are null, returns 0
+ * without doing anything else.  If 'reply' and 'bufp' are nonnull, then the
+ * result of the command is expected to be an odp_vport also, which is decoded
+ * and stored in '*reply' and '*bufp'.  The caller must free '*bufp' when the
+ * reply is no longer needed ('reply' will contain pointers into '*bufp'). */
+int
+dpif_linux_vport_transact(const struct dpif_linux_vport *request,
+                          struct dpif_linux_vport *reply,
+                          struct ofpbuf **bufp)
+{
+    static int dp0_fd = -1;
+    struct ofpbuf *buf = NULL;
+    int error;
+
+    assert((reply != NULL) == (bufp != NULL));
+    if (dp0_fd < 0) {
+        int fd;
+
+        error = open_minor(0, &fd);
+        if (error) {
+            goto error;
+        }
+        dp0_fd = fd;
+    }
+
+    buf = ofpbuf_new(1024);
+    dpif_linux_vport_to_ofpbuf(request, buf);
+
+    error = ioctl(dp0_fd, request->cmd, buf->data) ? errno : 0;
+    if (error) {
+        goto error;
+    }
+
+    if (bufp) {
+        buf->size = ((struct odp_vport *) buf->data)->len;
+        error = dpif_linux_vport_from_ofpbuf(reply, buf);
+        if (error) {
+            goto error;
+        }
+        *bufp = buf;
+    } else {
+        ofpbuf_delete(buf);
+    }
+    return 0;
+
+error:
+    ofpbuf_delete(buf);
+    if (bufp) {
+        memset(reply, 0, sizeof *reply);
+        *bufp = NULL;
+    }
+    return error;
+}
+
+/* Obtains information about the kernel vport named 'name' and stores it into
+ * '*reply' and '*bufp'.  The caller must free '*bufp' when the reply is no
+ * longer needed ('reply' will contain pointers into '*bufp').  */
+int
+dpif_linux_vport_get(const char *name, struct dpif_linux_vport *reply,
+                     struct ofpbuf **bufp)
+{
+    struct dpif_linux_vport request;
+
+    dpif_linux_vport_init(&request);
+    request.cmd = ODP_VPORT_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 1dff4b7..552f432 100644
--- a/lib/dpif-linux.h
+++ b/lib/dpif-linux.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010 Nicira Networks.
+ * Copyright (c) 2010, 2011 Nicira Networks.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -18,6 +18,37 @@
 #define DPIF_LINUX_H 1
 
 #include <stdbool.h>
+#include "openvswitch/datapath-protocol.h"
+
+struct ofpbuf;
+
+struct dpif_linux_vport {
+    /* ioctl command argument. */
+    int cmd;
+
+    /* odp_vport header. */
+    int dp_idx;
+    int port_no;
+    enum odp_vport_type type;
+
+    /* Attributes. */
+    const char *name;                      /* ODPPT_NAME. */
+    const struct rtnl_link_stats64 *stats; /* ODPPT_STATS. */
+    const uint8_t *address;                /* ODPPT_ADDRESS. */
+    int mtu;                               /* ODPPT_MTU. */
+    const struct nlattr *options;          /* ODPPT_OPTIONS. */
+    size_t options_len;
+    int ifindex;                           /* ODPPT_IFINDEX. */
+    int iflink;                            /* ODPPT_IFLINK. */
+};
+
+void dpif_linux_vport_init(struct dpif_linux_vport *);
+
+int dpif_linux_vport_transact(const struct dpif_linux_vport *request,
+                              struct dpif_linux_vport *reply,
+                              struct ofpbuf **bufp);
+int dpif_linux_vport_get(const char *name, struct dpif_linux_vport *reply,
+                         struct ofpbuf **bufp);
 
 bool dpif_linux_is_internal_device(const char *name);
 
diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c
index 4085dd0..79c0f0a 100644
--- a/lib/netdev-vport.c
+++ b/lib/netdev-vport.c
@@ -26,6 +26,7 @@
 #include <sys/ioctl.h>
 
 #include "byte-order.h"
+#include "dpif-linux.h"
 #include "hash.h"
 #include "hmap.h"
 #include "list.h"
@@ -37,8 +38,8 @@
 #include "openvswitch/tunnel.h"
 #include "packets.h"
 #include "rtnetlink.h"
-#include "rtnetlink-route.h"
 #include "rtnetlink-link.h"
+#include "rtnetlink-route.h"
 #include "shash.h"
 #include "socket-util.h"
 #include "vlog.h"
@@ -72,7 +73,7 @@ struct netdev_vport_notifier {
 
 struct netdev_dev_vport {
     struct netdev_dev netdev_dev;
-    uint64_t config[VPORT_CONFIG_SIZE / 8];
+    struct ofpbuf *options;
 };
 
 struct netdev_vport {
@@ -83,9 +84,10 @@ struct vport_class {
     enum odp_vport_type type;
     struct netdev_class netdev_class;
     int (*parse_config)(const char *name, const char *type,
-                        const struct shash *args, void *config);
+                        const struct shash *args, struct ofpbuf *options);
     int (*unparse_config)(const char *name, const char *type,
-                          const void *config, struct shash *args);
+                          const struct nlattr *options, size_t options_len,
+                          struct shash *args);
 };
 
 static struct shash netdev_vport_notifiers =
@@ -93,10 +95,11 @@ static struct shash netdev_vport_notifiers =
 
 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
 
-static int netdev_vport_do_ioctl(int cmd, void *arg);
 static int netdev_vport_create(const struct netdev_class *, const char *,
                                const struct shash *, struct netdev_dev **);
 static void netdev_vport_poll_notify(const struct netdev *);
+static const struct tnl_port_config *tnl_port_config_from_nlattr(
+    const struct nlattr *options, size_t options_len);
 
 static void netdev_vport_tnl_iface_init(void);
 static void netdev_vport_route_change(const struct rtnetlink_route_change *,
@@ -132,17 +135,67 @@ netdev_vport_cast(const struct netdev *netdev)
     return CONTAINER_OF(netdev, struct netdev_vport, netdev);
 }
 
-/* If 'netdev' is a vport netdev, copies its kernel configuration into
- * 'config'.  Otherwise leaves 'config' untouched. */
-void
-netdev_vport_get_config(const struct netdev *netdev, void *config)
+/* If 'netdev' is a vport netdev, returns an ofpbuf that contains Netlink
+ * options to include in ODPPT_OPTIONS for configuring that vport.  Otherwise
+ * returns NULL. */
+const struct ofpbuf *
+netdev_vport_get_options(const struct netdev *netdev)
 {
     const struct netdev_dev *dev = netdev_get_dev(netdev);
 
-    if (is_vport_class(netdev_dev_get_class(dev))) {
-        const struct netdev_dev_vport *vport = netdev_dev_vport_cast(dev);
-        memcpy(config, vport->config, VPORT_CONFIG_SIZE);
+    return (is_vport_class(netdev_dev_get_class(dev))
+            ? netdev_dev_vport_cast(dev)->options
+            : NULL);
+}
+
+enum odp_vport_type
+netdev_vport_get_vport_type(const struct netdev *netdev)
+{
+    const struct netdev_dev *dev = netdev_get_dev(netdev);
+    const struct netdev_class *class = netdev_dev_get_class(dev);
+
+    return (is_vport_class(class) ? vport_class_cast(class)->type
+            : class == &netdev_internal_class ? ODPVT_INTERNAL
+            : class == &netdev_linux_class ? ODPVT_NETDEV
+            : ODPVT_UNSPEC);
+}
+
+const char *
+netdev_vport_get_netdev_type(const struct dpif_linux_vport *vport)
+{
+    const struct tnl_port_config *tnl_config;
+
+    switch (vport->type) {
+    case ODPVT_UNSPEC:
+        break;
+
+    case ODPVT_NETDEV:
+        return "system";
+
+    case ODPVT_INTERNAL:
+        return "internal";
+
+    case ODPVT_PATCH:
+        return "patch";
+
+    case ODPVT_GRE:
+        tnl_config = tnl_port_config_from_nlattr(vport->options,
+                                                 vport->options_len);
+        if (tnl_config) {
+            return tnl_config->flags & TNL_F_IPSEC ? "ipsec_gre" : "gre";
+        }
+        break;
+
+    case ODPVT_CAPWAP:
+        return "capwap";
+
+    case __ODPVT_MAX:
+        break;
     }
+
+    VLOG_WARN_RL(&rl, "dp%d: port `%s' has unsupported type %u",
+                 vport->dp_idx, vport->name, (unsigned int) vport->type);
+    return "unknown";
 }
 
 static int
@@ -158,38 +211,41 @@ netdev_vport_create(const struct netdev_class *netdev_class, const char *name,
                     struct netdev_dev **netdev_devp)
 {
     const struct vport_class *vport_class = vport_class_cast(netdev_class);
-    uint64_t config[VPORT_CONFIG_SIZE / 8];
+    struct ofpbuf *options = NULL;
     struct shash fetched_args;
     int error;
 
-    memset(config, 0, sizeof config);
     shash_init(&fetched_args);
 
     if (!shash_is_empty(args)) {
         /* Parse the provided configuration. */
+        options = ofpbuf_new(64);
         error = vport_class->parse_config(name, netdev_class->type,
-                                          args, config);
+                                          args, options);
     } else {
         /* Fetch an existing configuration from the kernel.
          *
          * This case could be ambiguous with initializing a new vport with an
          * empty configuration, but none of the existing vport classes accept
          * an empty configuration. */
-        struct odp_port odp_port;
+        struct dpif_linux_vport request, reply;
+        struct ofpbuf *buf;
 
-        memset(&odp_port, 0, sizeof odp_port);
-        strncpy(odp_port.devname, name, sizeof odp_port.devname);
-        error = netdev_vport_do_ioctl(ODP_VPORT_QUERY, &odp_port);
+        dpif_linux_vport_init(&request);
+        request.cmd = ODP_VPORT_GET;
+        request.name = name;
+        error = dpif_linux_vport_transact(&request, &reply, &buf);
         if (!error) {
             /* XXX verify correct type */
-            memcpy(config, odp_port.config, sizeof config);
             error = vport_class->unparse_config(name, netdev_class->type,
-                                                odp_port.config,
+                                                reply.options,
+                                                reply.options_len,
                                                 &fetched_args);
             if (error) {
                 VLOG_ERR_RL(&rl, "%s: failed to parse kernel config (%s)",
                             name, strerror(error));
             }
+            ofpbuf_delete(buf);
         } else {
             VLOG_ERR_RL(&rl, "%s: vport query failed (%s)",
                         name, strerror(error));
@@ -203,9 +259,11 @@ netdev_vport_create(const struct netdev_class *netdev_class, const char *name,
         netdev_dev_init(&dev->netdev_dev, name,
                         shash_is_empty(&fetched_args) ? args : &fetched_args,
                         netdev_class);
-        memcpy(dev->config, config, sizeof dev->config);
+        dev->options = options;
 
         *netdev_devp = &dev->netdev_dev;
+    } else {
+        ofpbuf_delete(options);
     }
 
     shash_destroy(&fetched_args);
@@ -247,24 +305,35 @@ netdev_vport_set_config(struct netdev_dev *dev_, const struct shash *args)
     const struct netdev_class *netdev_class = netdev_dev_get_class(dev_);
     const struct vport_class *vport_class = vport_class_cast(netdev_class);
     struct netdev_dev_vport *dev = netdev_dev_vport_cast(dev_);
-    struct odp_port port;
+    const char *name = netdev_dev_get_name(dev_);
+    struct ofpbuf *options;
     int error;
 
-    memset(&port, 0, sizeof port);
-    strncpy(port.devname, netdev_dev_get_name(dev_), sizeof port.devname);
-    port.type = vport_class->type;
-    error = vport_class->parse_config(netdev_dev_get_name(dev_),
-                                      netdev_dev_get_type(dev_),
-                                      args, port.config);
-    if (!error && memcmp(port.config, dev->config, sizeof dev->config)) {
-        error = netdev_vport_do_ioctl(ODP_VPORT_MOD, &port);
+    options = ofpbuf_new(64);
+    error = vport_class->parse_config(name, netdev_dev_get_type(dev_),
+                                      args, options);
+    if (!error
+        && (options->size != dev->options->size
+            || memcmp(options->data, dev->options->data, options->size))) {
+        struct dpif_linux_vport vport;
+
+        dpif_linux_vport_init(&vport);
+        vport.cmd = ODP_VPORT_SET;
+        vport.name = name;
+        vport.options = options->data;
+        vport.options_len = options->size;
+        error = dpif_linux_vport_transact(&vport, NULL, NULL);
         if (!error || error == ENODEV) {
             /* Either reconfiguration succeeded or this vport is not installed
              * in the kernel (e.g. it hasn't been added to a dpif yet with
              * dpif_port_add()). */
-            memcpy(dev->config, port.config, sizeof dev->config);
+            ofpbuf_delete(dev->options);
+            dev->options = options;
+            options = NULL;
         }
     }
+    ofpbuf_delete(options);
+
     return error;
 }
 
@@ -272,94 +341,92 @@ static int
 netdev_vport_set_etheraddr(struct netdev *netdev,
                            const uint8_t mac[ETH_ADDR_LEN])
 {
-    struct odp_vport_ether vport_ether;
-    int err;
-
-    ovs_strlcpy(vport_ether.devname, netdev_get_name(netdev),
-                sizeof vport_ether.devname);
+    struct dpif_linux_vport vport;
+    int error;
 
-    memcpy(vport_ether.ether_addr, mac, ETH_ADDR_LEN);
+    dpif_linux_vport_init(&vport);
+    vport.cmd = ODP_VPORT_SET;
+    vport.name = netdev_get_name(netdev);
+    vport.address = mac;
 
-    err = netdev_vport_do_ioctl(ODP_VPORT_ETHER_SET, &vport_ether);
-    if (err) {
-        return err;
+    error = dpif_linux_vport_transact(&vport, NULL, NULL);
+    if (!error) {
+        netdev_vport_poll_notify(netdev);
     }
-
-    netdev_vport_poll_notify(netdev);
-    return 0;
+    return error;
 }
 
 static int
 netdev_vport_get_etheraddr(const struct netdev *netdev,
                            uint8_t mac[ETH_ADDR_LEN])
 {
-    struct odp_vport_ether vport_ether;
-    int err;
-
-    ovs_strlcpy(vport_ether.devname, netdev_get_name(netdev),
-                sizeof vport_ether.devname);
+    struct dpif_linux_vport reply;
+    struct ofpbuf *buf;
+    int error;
 
-    err = netdev_vport_do_ioctl(ODP_VPORT_ETHER_GET, &vport_ether);
-    if (err) {
-        return err;
+    error = dpif_linux_vport_get(netdev_get_name(netdev), &reply, &buf);
+    if (!error) {
+        if (reply.address) {
+            memcpy(mac, reply.address, ETH_ADDR_LEN);
+        } else {
+            error = EOPNOTSUPP;
+        }
+        ofpbuf_delete(buf);
     }
-
-    memcpy(mac, vport_ether.ether_addr, ETH_ADDR_LEN);
-    return 0;
+    return error;
 }
 
 static int
 netdev_vport_get_mtu(const struct netdev *netdev, int *mtup)
 {
-    struct odp_vport_mtu vport_mtu;
-    int err;
-
-    ovs_strlcpy(vport_mtu.devname, netdev_get_name(netdev),
-                sizeof vport_mtu.devname);
+    struct dpif_linux_vport reply;
+    struct ofpbuf *buf;
+    int error;
 
-    err = netdev_vport_do_ioctl(ODP_VPORT_MTU_GET, &vport_mtu);
-    if (err) {
-        return err;
+    error = dpif_linux_vport_get(netdev_get_name(netdev), &reply, &buf);
+    if (!error) {
+        *mtup = reply.mtu;
+        ofpbuf_delete(buf);
     }
-
-    *mtup = vport_mtu.mtu;
-    return 0;
+    return error;
 }
 
 int
 netdev_vport_get_stats(const struct netdev *netdev, struct netdev_stats *stats)
 {
-    const char *name = netdev_get_name(netdev);
-    struct odp_vport_stats_req ovsr;
-    int err;
+    struct dpif_linux_vport reply;
+    struct ofpbuf *buf;
+    int error;
 
-    ovs_strlcpy(ovsr.devname, name, sizeof ovsr.devname);
-    err = netdev_vport_do_ioctl(ODP_VPORT_STATS_GET, &ovsr);
-    if (err) {
-        return err;
+    error = dpif_linux_vport_get(netdev_get_name(netdev), &reply, &buf);
+    if (error) {
+        return error;
+    } else if (!reply.stats) {
+        ofpbuf_delete(buf);
+        return EOPNOTSUPP;
     }
 
-    stats->rx_packets = ovsr.stats.rx_packets;
-    stats->tx_packets = ovsr.stats.tx_packets;
-    stats->rx_bytes = ovsr.stats.rx_bytes;
-    stats->tx_bytes = ovsr.stats.tx_bytes;
-    stats->rx_errors = ovsr.stats.rx_errors;
-    stats->tx_errors = ovsr.stats.tx_errors;
-    stats->rx_dropped = ovsr.stats.rx_dropped;
-    stats->tx_dropped = ovsr.stats.tx_dropped;
-    stats->multicast = ovsr.stats.multicast;
-    stats->collisions = ovsr.stats.collisions;
-    stats->rx_length_errors = ovsr.stats.rx_length_errors;
-    stats->rx_over_errors = ovsr.stats.rx_over_errors;
-    stats->rx_crc_errors = ovsr.stats.rx_crc_errors;
-    stats->rx_frame_errors = ovsr.stats.rx_frame_errors;
-    stats->rx_fifo_errors = ovsr.stats.rx_fifo_errors;
-    stats->rx_missed_errors = ovsr.stats.rx_missed_errors;
-    stats->tx_aborted_errors = ovsr.stats.tx_aborted_errors;
-    stats->tx_carrier_errors = ovsr.stats.tx_carrier_errors;
-    stats->tx_fifo_errors = ovsr.stats.tx_fifo_errors;
-    stats->tx_heartbeat_errors = ovsr.stats.tx_heartbeat_errors;
-    stats->tx_window_errors = ovsr.stats.tx_window_errors;
+    stats->rx_packets = reply.stats->rx_packets;
+    stats->tx_packets = reply.stats->tx_packets;
+    stats->rx_bytes = reply.stats->rx_bytes;
+    stats->tx_bytes = reply.stats->tx_bytes;
+    stats->rx_errors = reply.stats->rx_errors;
+    stats->tx_errors = reply.stats->tx_errors;
+    stats->rx_dropped = reply.stats->rx_dropped;
+    stats->tx_dropped = reply.stats->tx_dropped;
+    stats->multicast = reply.stats->multicast;
+    stats->collisions = reply.stats->collisions;
+    stats->rx_length_errors = reply.stats->rx_length_errors;
+    stats->rx_over_errors = reply.stats->rx_over_errors;
+    stats->rx_crc_errors = reply.stats->rx_crc_errors;
+    stats->rx_frame_errors = reply.stats->rx_frame_errors;
+    stats->rx_fifo_errors = reply.stats->rx_fifo_errors;
+    stats->rx_missed_errors = reply.stats->rx_missed_errors;
+    stats->tx_aborted_errors = reply.stats->tx_aborted_errors;
+    stats->tx_carrier_errors = reply.stats->tx_carrier_errors;
+    stats->tx_fifo_errors = reply.stats->tx_fifo_errors;
+    stats->tx_heartbeat_errors = reply.stats->tx_heartbeat_errors;
+    stats->tx_window_errors = reply.stats->tx_window_errors;
 
     return 0;
 }
@@ -367,34 +434,38 @@ netdev_vport_get_stats(const struct netdev *netdev, struct netdev_stats *stats)
 int
 netdev_vport_set_stats(struct netdev *netdev, const struct netdev_stats *stats)
 {
-    struct odp_vport_stats_req ovsr;
+    struct rtnl_link_stats64 rtnl_stats;
+    struct dpif_linux_vport vport;
     int err;
 
-    ovs_strlcpy(ovsr.devname, netdev_get_name(netdev), sizeof ovsr.devname);
-
-    ovsr.stats.rx_packets = stats->rx_packets;
-    ovsr.stats.tx_packets = stats->tx_packets;
-    ovsr.stats.rx_bytes = stats->rx_bytes;
-    ovsr.stats.tx_bytes = stats->tx_bytes;
-    ovsr.stats.rx_errors = stats->rx_errors;
-    ovsr.stats.tx_errors = stats->tx_errors;
-    ovsr.stats.rx_dropped = stats->rx_dropped;
-    ovsr.stats.tx_dropped = stats->tx_dropped;
-    ovsr.stats.multicast = stats->multicast;
-    ovsr.stats.collisions = stats->collisions;
-    ovsr.stats.rx_length_errors = stats->rx_length_errors;
-    ovsr.stats.rx_over_errors = stats->rx_over_errors;
-    ovsr.stats.rx_crc_errors = stats->rx_crc_errors;
-    ovsr.stats.rx_frame_errors = stats->rx_frame_errors;
-    ovsr.stats.rx_fifo_errors = stats->rx_fifo_errors;
-    ovsr.stats.rx_missed_errors = stats->rx_missed_errors;
-    ovsr.stats.tx_aborted_errors = stats->tx_aborted_errors;
-    ovsr.stats.tx_carrier_errors = stats->tx_carrier_errors;
-    ovsr.stats.tx_fifo_errors = stats->tx_fifo_errors;
-    ovsr.stats.tx_heartbeat_errors = stats->tx_heartbeat_errors;
-    ovsr.stats.tx_window_errors = stats->tx_window_errors;
-
-    err = netdev_vport_do_ioctl(ODP_VPORT_STATS_SET, &ovsr);
+    rtnl_stats.rx_packets = stats->rx_packets;
+    rtnl_stats.tx_packets = stats->tx_packets;
+    rtnl_stats.rx_bytes = stats->rx_bytes;
+    rtnl_stats.tx_bytes = stats->tx_bytes;
+    rtnl_stats.rx_errors = stats->rx_errors;
+    rtnl_stats.tx_errors = stats->tx_errors;
+    rtnl_stats.rx_dropped = stats->rx_dropped;
+    rtnl_stats.tx_dropped = stats->tx_dropped;
+    rtnl_stats.multicast = stats->multicast;
+    rtnl_stats.collisions = stats->collisions;
+    rtnl_stats.rx_length_errors = stats->rx_length_errors;
+    rtnl_stats.rx_over_errors = stats->rx_over_errors;
+    rtnl_stats.rx_crc_errors = stats->rx_crc_errors;
+    rtnl_stats.rx_frame_errors = stats->rx_frame_errors;
+    rtnl_stats.rx_fifo_errors = stats->rx_fifo_errors;
+    rtnl_stats.rx_missed_errors = stats->rx_missed_errors;
+    rtnl_stats.tx_aborted_errors = stats->tx_aborted_errors;
+    rtnl_stats.tx_carrier_errors = stats->tx_carrier_errors;
+    rtnl_stats.tx_fifo_errors = stats->tx_fifo_errors;
+    rtnl_stats.tx_heartbeat_errors = stats->tx_heartbeat_errors;
+    rtnl_stats.tx_window_errors = stats->tx_window_errors;
+
+    dpif_linux_vport_init(&vport);
+    vport.cmd = ODP_VPORT_SET;
+    vport.name = netdev_get_name(netdev);
+    vport.stats = &rtnl_stats;
+
+    err = dpif_linux_vport_transact(&vport, NULL, NULL);
 
     /* If the vport layer doesn't know about the device, that doesn't mean it
      * doesn't exist (after all were able to open it when netdev_open() was
@@ -706,11 +777,16 @@ netdev_vport_get_tnl_iface(const struct netdev *netdev)
     int dst_len;
     uint32_t route;
     struct netdev_dev_vport *ndv;
-    struct tnl_port_config *config;
+    const struct tnl_port_config *config;
     struct route_node *rn, *rn_def, *rn_iter;
+    const struct nlattr *nla;
 
     ndv = netdev_dev_vport_cast(netdev_get_dev(netdev));
-    config = (struct tnl_port_config *) ndv->config;
+    nla = nl_attr_find(ndv->options, 0, TNLAT_CONFIG);
+    if (!nla || nl_attr_get_size(nla) != sizeof *config) {
+        return NULL;
+    }
+    config = nl_attr_get(nla);
     route = ntohl(config->daddr);
 
     dst_len = 0;
@@ -751,22 +827,6 @@ netdev_vport_get_tnl_iface(const struct netdev *netdev)
 
 /* Helper functions. */
 
-static int
-netdev_vport_do_ioctl(int cmd, void *arg)
-{
-    static int ioctl_fd = -1;
-
-    if (ioctl_fd < 0) {
-        ioctl_fd = open("/dev/net/dp0", O_RDONLY | O_NONBLOCK);
-        if (ioctl_fd < 0) {
-            VLOG_ERR_RL(&rl, "failed to open ioctl fd: %s", strerror(errno));
-            return errno;
-        }
-    }
-
-    return ioctl(ioctl_fd, cmd, arg) ? errno : 0;
-}
-
 static void
 netdev_vport_poll_notify(const struct netdev *netdev)
 {
@@ -790,7 +850,7 @@ netdev_vport_poll_notify(const struct netdev *netdev)
 
 static int
 parse_tunnel_config(const char *name, const char *type,
-                    const struct shash *args, void *configp)
+                    const struct shash *args, struct ofpbuf *options)
 {
     bool is_gre = false;
     bool is_ipsec = false;
@@ -925,16 +985,42 @@ parse_tunnel_config(const char *name, const char *type,
         return EINVAL;
     }
 
-    BUILD_ASSERT(sizeof config <= VPORT_CONFIG_SIZE);
-    memcpy(configp, &config, sizeof config);
+    nl_msg_put_unspec(options, TNLAT_CONFIG, &config, sizeof config);
     return 0;
 }
 
+static const struct tnl_port_config *
+tnl_port_config_from_nlattr(const struct nlattr *options, size_t options_len)
+{
+    static const struct nl_policy odp_tunnel_policy[] = {
+        [TNLAT_CONFIG] = { .type = NL_A_UNSPEC,
+                           .min_len = sizeof(struct tnl_port_config),
+                           .max_len = sizeof(struct tnl_port_config),
+                           .optional = false }
+    };
+    struct nlattr *a[ARRAY_SIZE(odp_tunnel_policy)];
+    struct ofpbuf buf;
+
+    ofpbuf_use_const(&buf, options, options_len);
+    if (!nl_policy_parse(&buf, 0, odp_tunnel_policy,
+                         a, ARRAY_SIZE(odp_tunnel_policy))) {
+        return NULL;
+    }
+
+    return nl_attr_get(a[TNLAT_CONFIG]);
+}
+
 static int
 unparse_tunnel_config(const char *name OVS_UNUSED, const char *type OVS_UNUSED,
-                      const void *config_, struct shash *args)
+                      const struct nlattr *options, size_t options_len,
+                      struct shash *args)
 {
-    const struct tnl_port_config *config = config_;
+    const struct tnl_port_config *config;
+
+    config = tnl_port_config_from_nlattr(options, options_len);
+    if (!config) {
+        return EINVAL;
+    }
 
     if (!(config->flags & TNL_F_HDR_CACHE) == !(config->flags & TNL_F_IPSEC)) {
         smap_add(args, "header_cache",
@@ -986,7 +1072,7 @@ unparse_tunnel_config(const char *name OVS_UNUSED, const char *type OVS_UNUSED,
 
 static int
 parse_patch_config(const char *name, const char *type OVS_UNUSED,
-                   const struct shash *args, void *configp)
+                   const struct shash *args, struct ofpbuf *options)
 {
     const char *peer;
 
@@ -1001,7 +1087,7 @@ parse_patch_config(const char *name, const char *type OVS_UNUSED,
         return EINVAL;
     }
 
-    if (strlen(peer) >= MIN(IFNAMSIZ, VPORT_CONFIG_SIZE)) {
+    if (strlen(peer) >= IFNAMSIZ) {
         VLOG_WARN("%s: patch 'peer' arg too long", name);
         return EINVAL;
     }
@@ -1011,22 +1097,32 @@ parse_patch_config(const char *name, const char *type OVS_UNUSED,
         return EINVAL;
     }
 
-    strncpy(configp, peer, VPORT_CONFIG_SIZE);
+    nl_msg_put_string(options, ODPPT_PATCH_PEER, peer);
 
     return 0;
 }
 
 static int
 unparse_patch_config(const char *name OVS_UNUSED, const char *type OVS_UNUSED,
-                     const void *config_, struct shash *args)
+                     const struct nlattr *options, size_t options_len,
+                     struct shash *args)
 {
-    char peer[IFNAMSIZ];
+    static const struct nl_policy odp_patch_policy[] = {
+        [ODPPT_PATCH_PEER] = { .type = NL_A_STRING,
+                               .max_len = IFNAMSIZ,
+                               .optional = false }
+    };
 
-    ovs_strlcpy(peer, config_, MIN(sizeof peer, VPORT_CONFIG_SIZE));
-    if (peer[0]) {
-        smap_add(args, "peer", peer);
+    struct nlattr *a[ARRAY_SIZE(odp_patch_policy)];
+    struct ofpbuf buf;
+
+    ofpbuf_use_const(&buf, options, options_len);
+    if (!nl_policy_parse(&buf, 0, odp_patch_policy,
+                         a, ARRAY_SIZE(odp_patch_policy))) {
+        return EINVAL;
     }
 
+    smap_add(args, "peer", nl_attr_get_string(a[ODPPT_PATCH_PEER]));
     return 0;
 }
 
diff --git a/lib/netdev-vport.h b/lib/netdev-vport.h
index eb05a09..3fe4625 100644
--- a/lib/netdev-vport.h
+++ b/lib/netdev-vport.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010 Nicira Networks.
+ * Copyright (c) 2010, 2011 Nicira Networks.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -17,14 +17,16 @@
 #ifndef NETDEV_VPORT_H
 #define NETDEV_VPORT_H 1
 
+struct dpif_linux_vport;
 struct netdev;
 struct netdev_stats;
-struct odp_port;
-struct shash;
 
 void netdev_vport_register(void);
 
-void netdev_vport_get_config(const struct netdev *, void *config);
+const struct ofpbuf *netdev_vport_get_options(const struct netdev *);
+
+enum odp_vport_type netdev_vport_get_vport_type(const struct netdev *);
+const char *netdev_vport_get_netdev_type(const struct dpif_linux_vport *);
 
 int netdev_vport_get_stats(const struct netdev *, struct netdev_stats *);
 int netdev_vport_set_stats(struct netdev *, const struct netdev_stats *);
-- 
1.7.1





More information about the dev mailing list