[ovs-dev] [netlink v5 29/61] datapath: Change userspace vport interface to use Netlink attributes.

Ben Pfaff blp at nicira.com
Thu Jan 27 00:23:12 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                     |  616 ++++++++++++++++++++-----------
 datapath/odp-compat.h                   |    6 -
 datapath/tunnel.c                       |  168 ++++++---
 datapath/tunnel.h                       |   50 ++-
 datapath/vport-capwap.c                 |   17 +-
 datapath/vport-gre.c                    |   34 +-
 datapath/vport-patch.c                  |   84 +++--
 datapath/vport.c                        |  317 ++--------------
 datapath/vport.h                        |   31 +-
 include/openvswitch/datapath-protocol.h |   99 ++---
 include/openvswitch/tunnel.h            |   44 ++-
 lib/dpif-linux.c                        |  631 +++++++++++++++++++------------
 lib/dpif-linux.h                        |   33 ++-
 lib/netdev-vport.c                      |  576 +++++++++++++++++-----------
 lib/netdev-vport.h                      |   10 +-
 15 files changed, 1513 insertions(+), 1203 deletions(-)

diff --git a/datapath/datapath.c b/datapath/datapath.c
index baf7940..ffa22e2 100644
--- a/datapath/datapath.c
+++ b/datapath/datapath.c
@@ -69,7 +69,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)
@@ -208,8 +208,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;
@@ -264,11 +265,14 @@ 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 = ODP_VPORT_TYPE_INTERNAL;
-	err = new_vport(dp, &internal_dev_port, ODPP_LOCAL);
-	if (err) {
+	parms.name = devname;
+	parms.type = ODP_VPORT_TYPE_INTERNAL;
+	parms.options = NULL;
+	parms.dp = dp;
+	parms.port_no = ODPP_LOCAL;
+	vport = new_vport(&parms);
+	if (IS_ERR(vport)) {
+		err = PTR_ERR(vport);
 		if (err == -EBUSY)
 			err = -EEXIST;
 
@@ -355,73 +359,24 @@ out:
 }
 
 /* Called with RTNL lock and dp->mutex. */
-static int new_vport(struct datapath *dp, struct odp_port *odp_port, int port_no)
+static struct vport *new_vport(const struct vport_parms *parms)
 {
-	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();
+	vport = vport_add(parms);
+	if (!IS_ERR(vport)) {
+		struct datapath *dp = parms->dp;
 
-	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++;
+		rcu_assign_pointer(dp->ports[parms->port_no], vport);
+		list_add_rcu(&vport->node, &dp->port_list);
+		dp->n_ports++;
 
-	dp_ifinfo_notify(RTM_NEWLINK, vport);
+		dp_ifinfo_notify(RTM_NEWLINK, vport);
+	}
+	vport_unlock();
 
-	return 0;
-}
-
-static int attach_port(int dp_idx, struct odp_port __user *portp)
-{
-	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;
-
-	set_internal_devs_mtu(dp);
-	dp_sysfs_add_if(get_vport_protected(dp, port_no));
-
-	err = put_user(port_no, &portp->port);
-
-out_unlock_dp:
-	mutex_unlock(&dp->mutex);
-out_unlock_rtnl:
-	rtnl_unlock();
-out:
-	return err;
+	return vport;
 }
 
 int dp_detach_port(struct vport *p)
@@ -447,37 +402,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)
 {
@@ -1348,95 +1272,408 @@ 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 const struct nla_policy vport_policy[ODP_VPORT_ATTR_MAX + 1] = {
+	[ODP_VPORT_ATTR_NAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ - 1 },
+	[ODP_VPORT_ATTR_PORT_NO] = { .type = NLA_U32 },
+	[ODP_VPORT_ATTR_TYPE] = { .type = NLA_U32 },
+	[ODP_VPORT_ATTR_STATS] = { .len = sizeof(struct rtnl_link_stats64) },
+	[ODP_VPORT_ATTR_ADDRESS] = { .len = ETH_ALEN },
+	[ODP_VPORT_ATTR_MTU] = { .type = NLA_U32 },
+	[ODP_VPORT_ATTR_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->total_len = total_len;
+
+	NLA_PUT_U32(skb, ODP_VPORT_ATTR_PORT_NO, vport->port_no);
+	NLA_PUT_U32(skb, ODP_VPORT_ATTR_TYPE, vport_get_type(vport));
+	NLA_PUT_STRING(skb, ODP_VPORT_ATTR_NAME, vport_get_name(vport));
+
+	nla = nla_reserve(skb, ODP_VPORT_ATTR_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, ODP_VPORT_ATTR_ADDRESS, ETH_ALEN, vport_get_addr(vport));
+
+	NLA_PUT_U32(skb, ODP_VPORT_ATTR_MTU, vport_get_mtu(vport));
+
+	err = vport_get_options(vport, skb);
+
+	ifindex = vport_get_ifindex(vport);
+	if (ifindex > 0)
+		NLA_PUT_U32(skb, ODP_VPORT_ATTR_IFINDEX, ifindex);
+
+	iflink = vport_get_iflink(vport);
+	if (iflink > 0)
+		NLA_PUT_U32(skb, ODP_VPORT_ATTR_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[ODP_VPORT_ATTR_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, ODP_VPORT_ATTR_MAX, (struct nlattr *)(skb->data + sizeof(struct odp_vport)),
+			skb->len - sizeof(struct odp_vport), vport_policy);
+	if (err)
+		goto error_free_skb;
 
-		if (port.port >= DP_MAX_PORTS)
-			return -EINVAL;
+	err = VERIFY_NUL_STRING(a[ODP_VPORT_ATTR_NAME], IFNAMSIZ - 1);
+	if (err)
+		goto error_free_skb;
+
+	return skb;
+
+error_free_skb:
+	kfree_skb(skb);
+	return ERR_PTR(err);
+}
+
+
+/* Called without any locks (or with RTNL lock).
+ * Returns holding vport->dp->mutex.
+ */
+static struct vport *lookup_vport(struct odp_vport *odp_vport,
+				  struct nlattr *a[ODP_VPORT_ATTR_MAX + 1])
+{
+	struct datapath *dp;
+	struct vport *vport;
+
+	if (a[ODP_VPORT_ATTR_NAME]) {
+		int dp_idx, port_no;
+
+	retry:
+		vport_lock();
+		vport = vport_locate(nla_data(a[ODP_VPORT_ATTR_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[ODP_VPORT_ATTR_NAME]))) {
+			mutex_unlock(&dp->mutex);
+			goto retry;
+		}
 
-		if (!vport)
-			return -ENOENT;
-	}
+		return vport;
+	} else if (a[ODP_VPORT_ATTR_PORT_NO]) {
+		u32 port_no = nla_get_u32(a[ODP_VPORT_ATTR_PORT_NO]);
+
+		if (port_no >= DP_MAX_PORTS)
+			return ERR_PTR(-EINVAL);
+
+		dp = get_dp_locked(odp_vport->dp_idx);
+		if (!dp)
+			return ERR_PTR(-ENODEV);
 
-	return copy_to_user(uport, &port, sizeof(struct odp_port));
+		vport = get_vport_protected(dp, port_no);
+		if (!vport) {
+			mutex_unlock(&dp->mutex);
+			return ERR_PTR(-ENOENT);
+		}
+		return vport;
+	} else
+		return ERR_PTR(-EINVAL);
 }
 
-static int do_dump_port(struct datapath *dp, struct odp_vport_dump *dump)
+static int change_vport(struct vport *vport, struct nlattr *a[ODP_VPORT_ATTR_MAX + 1])
 {
+	int err = 0;
+	if (a[ODP_VPORT_ATTR_STATS])
+		err = vport_set_stats(vport, nla_data(a[ODP_VPORT_ATTR_STATS]));
+	if (!err && a[ODP_VPORT_ATTR_ADDRESS])
+		err = vport_set_addr(vport, nla_data(a[ODP_VPORT_ATTR_ADDRESS]));
+	if (!err && a[ODP_VPORT_ATTR_MTU])
+		err = vport_set_mtu(vport, nla_get_u32(a[ODP_VPORT_ATTR_MTU]));
+	return err;
+}
+
+static int attach_vport(struct odp_vport __user *uodp_vport)
+{
+	struct nlattr *a[ODP_VPORT_ATTR_MAX + 1];
+	struct odp_vport *odp_vport;
+	struct vport_parms parms;
+	struct vport *vport;
+	struct sk_buff *skb;
+	struct datapath *dp;
 	u32 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[ODP_VPORT_ATTR_NAME] || !a[ODP_VPORT_ATTR_TYPE])
+		goto exit_kfree_skb;
+
+	rtnl_lock();
+
+	dp = get_dp_locked(odp_vport->dp_idx);
+	err = -ENODEV;
+	if (!dp)
+		goto exit_unlock_rtnl;
+
+	if (a[ODP_VPORT_ATTR_PORT_NO]) {
+		port_no = nla_get_u32(a[ODP_VPORT_ATTR_PORT_NO]);
 
-			compose_odp_port(vport, &odp_port);
-			return copy_to_user((struct odp_port __force __user*)dump->port, &odp_port, sizeof(struct odp_port));
+		err = -EFBIG;
+		if (port_no >= DP_MAX_PORTS)
+			goto exit_unlock_dp;
+
+		vport = get_vport_protected(dp, 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[ODP_VPORT_ATTR_NAME]);
+	parms.type = nla_get_u32(a[ODP_VPORT_ATTR_TYPE]);
+	parms.options = a[ODP_VPORT_ATTR_OPTIONS];
+	parms.dp = dp;
+	parms.port_no = port_no;
+
+	vport = new_vport(&parms);
+	err = PTR_ERR(vport);
+	if (IS_ERR(vport))
+		goto exit_unlock_dp;
+
+ 	set_internal_devs_mtu(dp);
+ 	dp_sysfs_add_if(vport);
+
+	err = change_vport(vport, a);
+	if (err) {
+		dp_detach_port(vport);
+		goto exit_unlock_dp;
+	}
+
+	err = copy_vport_to_user(uodp_vport, vport, odp_vport->total_len);
+
+exit_unlock_dp:
+	mutex_unlock(&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 set_vport(unsigned int cmd, struct odp_vport __user *uodp_vport)
 {
-	struct odp_vport_dump dump;
+	struct nlattr *a[ODP_VPORT_ATTR_MAX + 1];
+	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;
+
+	rtnl_lock();
+	vport = lookup_vport((struct odp_vport *)skb->data, a);
+	err = PTR_ERR(vport);
+	if (IS_ERR(vport))
+		goto exit_free;
 
-	return do_dump_port(dp, &dump);
+	err = 0;
+	if (a[ODP_VPORT_ATTR_OPTIONS])
+		err = vport_set_options(vport, a[ODP_VPORT_ATTR_OPTIONS]);
+	if (!err)
+		err = change_vport(vport, a);
+
+	mutex_unlock(&vport->dp->mutex);
+exit_free:
+	kfree_skb(skb);
+	rtnl_unlock();
+exit:
+	return err;
 }
 
-static int get_listen_mask(const struct file *f)
+static int del_vport(unsigned int cmd, struct odp_vport __user *uodp_vport)
 {
-	return (long)f->private_data;
+	struct nlattr *a[ODP_VPORT_ATTR_MAX + 1];
+	struct datapath *dp;
+	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;
+
+	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;
+
+	err = -EINVAL;
+	if (vport->port_no == ODPP_LOCAL)
+		goto exit_free;
+
+	err = dp_detach_port(vport);
+	mutex_unlock(&dp->mutex);
+exit_free:
+	kfree_skb(skb);
+	rtnl_unlock();
+exit:
+	return err;
 }
 
-static void set_listen_mask(struct file *f, int listen_mask)
+static int get_vport(struct odp_vport __user *uodp_vport)
 {
-	f->private_data = (void*)(long)listen_mask;
+	struct nlattr *a[ODP_VPORT_ATTR_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 int dump_vport(struct odp_vport __user *uodp_vport)
+{
+	struct nlattr *a[ODP_VPORT_ATTR_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;
+
+	port_no = 0;
+	if (a[ODP_VPORT_ATTR_PORT_NO])
+		port_no = nla_get_u32(a[ODP_VPORT_ATTR_PORT_NO]);
+	for (; 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,
@@ -1444,7 +1681,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;
 
@@ -1458,46 +1695,24 @@ 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);
+	case ODP_VPORT_NEW:
+		err = attach_vport((struct odp_vport __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);
+	case ODP_VPORT_GET:
+		err = get_vport((struct odp_vport __user *)argp);
 		goto exit;
 
-	case ODP_VPORT_STATS_SET:
-		err = vport_user_stats_set((struct odp_vport_stats_req __user *)argp);
+	case ODP_VPORT_DEL:
+		err = del_vport(cmd, (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_SET:
+		err = set_vport(cmd, (struct odp_vport __user *)argp);
 		goto exit;
 
-	case ODP_VPORT_ETHER_SET:
-		err = vport_user_ether_set((struct odp_vport_ether __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;
 	}
 
@@ -1551,10 +1766,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;
@@ -1599,20 +1810,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;
@@ -1810,15 +2007,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:
@@ -1826,7 +2019,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));
 	}
@@ -1837,10 +2029,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..08906e4 100644
--- a/datapath/tunnel.c
+++ b/datapath/tunnel.c
@@ -160,13 +160,13 @@ static void assign_cache_rcu(struct vport *vport, struct tnl_cache *new_cache)
 
 static unsigned int *find_port_pool(const struct tnl_mutable_config *mutable)
 {
-	if (mutable->port_config.flags & TNL_F_IN_KEY_MATCH) {
-		if (mutable->port_config.saddr)
+	if (mutable->flags & TNL_F_IN_KEY_MATCH) {
+		if (mutable->saddr)
 			return &local_remote_ports;
 		else
 			return &remote_ports;
 	} else {
-		if (mutable->port_config.saddr)
+		if (mutable->saddr)
 			return &key_local_remote_ports;
 		else
 			return &key_remote_ports;
@@ -193,9 +193,9 @@ static int port_cmp(const struct tbl_node *node, void *target)
 	lookup->mutable = rcu_dereference_rtnl(tnl_vport->mutable);
 
 	return (lookup->mutable->tunnel_type == lookup->tunnel_type &&
-		lookup->mutable->port_config.daddr == lookup->daddr &&
-		lookup->mutable->port_config.in_key == lookup->key &&
-		lookup->mutable->port_config.saddr == lookup->saddr);
+		lookup->mutable->daddr == lookup->daddr &&
+		lookup->mutable->in_key == lookup->key &&
+		lookup->mutable->saddr == lookup->saddr);
 }
 
 static u32 port_hash(struct port_lookup_key *k)
@@ -209,9 +209,9 @@ static u32 mutable_hash(const struct tnl_mutable_config *mutable)
 {
 	struct port_lookup_key lookup;
 
-	lookup.saddr = mutable->port_config.saddr;
-	lookup.daddr = mutable->port_config.daddr;
-	lookup.key = mutable->port_config.in_key;
+	lookup.saddr = mutable->saddr;
+	lookup.daddr = mutable->daddr;
+	lookup.key = mutable->in_key;
 	lookup.tunnel_type = mutable->tunnel_type;
 
 	return port_hash(&lookup);
@@ -701,7 +701,7 @@ bool tnl_frag_needed(struct vport *vport, const struct tnl_mutable_config *mutab
 	 * not symmetric then PMTUD needs to be disabled since we won't have
 	 * any way of synthesizing packets.
 	 */
-	if ((mutable->port_config.flags & (TNL_F_IN_KEY_MATCH | TNL_F_OUT_KEY_ACTION)) ==
+	if ((mutable->flags & (TNL_F_IN_KEY_MATCH | TNL_F_OUT_KEY_ACTION)) ==
 	    (TNL_F_IN_KEY_MATCH | TNL_F_OUT_KEY_ACTION))
 		OVS_CB(nskb)->tun_id = flow_key;
 
@@ -719,7 +719,7 @@ static bool check_mtu(struct sk_buff *skb,
 	int mtu;
 	__be16 frag_off;
 
-	frag_off = (mutable->port_config.flags & TNL_F_PMTUD) ? htons(IP_DF) : 0;
+	frag_off = (mutable->flags & TNL_F_PMTUD) ? htons(IP_DF) : 0;
 	if (frag_off)
 		mtu = dst_mtu(&rt_dst(rt))
 			- ETH_HLEN
@@ -777,10 +777,10 @@ static void create_tunnel_header(const struct vport *vport,
 	iph->ihl	= sizeof(struct iphdr) >> 2;
 	iph->frag_off	= htons(IP_DF);
 	iph->protocol	= tnl_vport->tnl_ops->ipproto;
-	iph->tos	= mutable->port_config.tos;
+	iph->tos	= mutable->tos;
 	iph->daddr	= rt->rt_dst;
 	iph->saddr	= rt->rt_src;
-	iph->ttl	= mutable->port_config.ttl;
+	iph->ttl	= mutable->ttl;
 	if (!iph->ttl)
 		iph->ttl = dst_metric(&rt_dst(rt), RTAX_HOPLIMIT);
 
@@ -866,7 +866,7 @@ static struct tnl_cache *build_cache(struct vport *vport,
 	void *cache_data;
 	int cache_len;
 
-	if (!(mutable->port_config.flags & TNL_F_HDR_CACHE))
+	if (!(mutable->flags & TNL_F_HDR_CACHE))
 		return NULL;
 
 	/*
@@ -964,22 +964,21 @@ static struct rtable *find_route(struct vport *vport,
 	*cache = NULL;
 	tos = RT_TOS(tos);
 
-	if (likely(tos == mutable->port_config.tos &&
-		   check_cache_valid(cur_cache, mutable))) {
+	if (likely(tos == mutable->tos && check_cache_valid(cur_cache, mutable))) {
 		*cache = cur_cache;
 		return cur_cache->rt;
 	} else {
 		struct rtable *rt;
 		struct flowi fl = { .nl_u = { .ip4_u =
-					      { .daddr = mutable->port_config.daddr,
-						.saddr = mutable->port_config.saddr,
+					      { .daddr = mutable->daddr,
+						.saddr = mutable->saddr,
 						.tos = tos } },
 				    .proto = tnl_vport->tnl_ops->ipproto };
 
 		if (unlikely(ip_route_output_key(&init_net, &rt, &fl)))
 			return NULL;
 
-		if (likely(tos == mutable->port_config.tos))
+		if (likely(tos == mutable->tos))
 			*cache = build_cache(vport, mutable, rt);
 
 		return rt;
@@ -1189,10 +1188,10 @@ int tnl_send(struct vport *vport, struct sk_buff *skb)
 	else
 		inner_tos = 0;
 
-	if (mutable->port_config.flags & TNL_F_TOS_INHERIT)
+	if (mutable->flags & TNL_F_TOS_INHERIT)
 		tos = inner_tos;
 	else
-		tos = mutable->port_config.tos;
+		tos = mutable->tos;
 
 	tos = INET_ECN_encapsulate(tos, inner_tos);
 
@@ -1231,11 +1230,11 @@ int tnl_send(struct vport *vport, struct sk_buff *skb)
 	}
 
 	/* TTL */
-	ttl = mutable->port_config.ttl;
+	ttl = mutable->ttl;
 	if (!ttl)
 		ttl = dst_metric(&rt_dst(rt), RTAX_HOPLIMIT);
 
-	if (mutable->port_config.flags & TNL_F_TTL_INHERIT) {
+	if (mutable->flags & TNL_F_TTL_INHERIT) {
 		if (skb->protocol == htons(ETH_P_IP))
 			ttl = ip_hdr(skb)->ttl;
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
@@ -1323,46 +1322,78 @@ out:
 	return sent_len;
 }
 
-static int tnl_set_config(const void *config, const struct tnl_ops *tnl_ops,
+static const struct nla_policy tnl_policy[ODP_TUNNEL_ATTR_MAX + 1] = {
+	[ODP_TUNNEL_ATTR_FLAGS]    = { .type = NLA_U32 },
+	[ODP_TUNNEL_ATTR_DST_IPV4] = { .type = NLA_U32 },
+	[ODP_TUNNEL_ATTR_SRC_IPV4] = { .type = NLA_U32 },
+	[ODP_TUNNEL_ATTR_OUT_KEY]  = { .type = NLA_U64 },
+	[ODP_TUNNEL_ATTR_IN_KEY]   = { .type = NLA_U64 },
+	[ODP_TUNNEL_ATTR_TOS]      = { .type = NLA_U8 },
+	[ODP_TUNNEL_ATTR_TTL]      = { .type = NLA_U8 },
+};
+
+/* Sets ODP_TUNNEL_ATTR_* fields in 'mutable', which must initially be zeroed. */
+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 *a[ODP_TUNNEL_ATTR_MAX + 1];
+	int err;
 
-	mutable->port_config = *(struct tnl_port_config *)config;
-
-	if (mutable->port_config.daddr == 0)
+	if (!options)
 		return -EINVAL;
 
-	if (mutable->port_config.tos != RT_TOS(mutable->port_config.tos))
+	err = nla_parse_nested(a, ODP_TUNNEL_ATTR_MAX, options, tnl_policy);
+	if (err)
+		return err;
+
+	if (!a[ODP_TUNNEL_ATTR_FLAGS] || !a[ODP_TUNNEL_ATTR_DST_IPV4])
 		return -EINVAL;
 
-	mutable->tunnel_hlen = tnl_ops->hdr_len(&mutable->port_config);
+	mutable->flags = nla_get_u32(a[ODP_TUNNEL_ATTR_FLAGS]) & TNL_F_PUBLIC;
+
+	if (a[ODP_TUNNEL_ATTR_SRC_IPV4])
+		mutable->saddr = nla_get_be32(a[ODP_TUNNEL_ATTR_SRC_IPV4]);
+	mutable->daddr = nla_get_be32(a[ODP_TUNNEL_ATTR_DST_IPV4]);
+
+	if (a[ODP_TUNNEL_ATTR_TOS]) {
+		mutable->tos = nla_get_u8(a[ODP_TUNNEL_ATTR_TOS]);
+		if (mutable->tos != RT_TOS(mutable->tos))
+			return -EINVAL;
+	}
+
+	if (a[ODP_TUNNEL_ATTR_TTL])
+		mutable->ttl = nla_get_u8(a[ODP_TUNNEL_ATTR_TTL]);
+
+	mutable->tunnel_hlen = tnl_ops->hdr_len(mutable);
 	if (mutable->tunnel_hlen < 0)
 		return mutable->tunnel_hlen;
 
 	mutable->tunnel_hlen += sizeof(struct iphdr);
 
 	mutable->tunnel_type = tnl_ops->tunnel_type;
-	if (mutable->port_config.flags & TNL_F_IN_KEY_MATCH) {
+	if (!a[ODP_TUNNEL_ATTR_IN_KEY]) {
 		mutable->tunnel_type |= TNL_T_KEY_MATCH;
-		mutable->port_config.in_key = 0;
-	} else
+		mutable->flags |= TNL_F_IN_KEY_MATCH;
+	} else {
 		mutable->tunnel_type |= TNL_T_KEY_EXACT;
+		mutable->in_key = nla_get_be64(a[ODP_TUNNEL_ATTR_IN_KEY]);
+	}
+
+	if (!a[ODP_TUNNEL_ATTR_OUT_KEY])
+		mutable->flags |= TNL_F_OUT_KEY_ACTION;
+	else
+		mutable->out_key = nla_get_be64(a[ODP_TUNNEL_ATTR_OUT_KEY]);
 
-	old_vport = tnl_find_port(mutable->port_config.saddr,
-				  mutable->port_config.daddr,
-				  mutable->port_config.in_key,
-				  mutable->tunnel_type,
+	old_vport = tnl_find_port(mutable->saddr, mutable->daddr,
+				  mutable->in_key, mutable->tunnel_type,
 				  &old_mutable);
 
 	if (old_vport && old_vport != cur_vport)
 		return -EEXIST;
 
-	if (mutable->port_config.flags & TNL_F_OUT_KEY_ACTION)
-		mutable->port_config.out_key = 0;
-
 	return 0;
 }
 
@@ -1399,7 +1430,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,25 +1457,30 @@ 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);
+	const struct tnl_mutable_config *old_mutable;
 	struct tnl_mutable_config *mutable;
 	int err;
 
-	mutable = kmemdup(rtnl_dereference(tnl_vport->mutable),
-			  sizeof(struct tnl_mutable_config), GFP_KERNEL);
+	mutable = kzalloc(sizeof(struct tnl_mutable_config), GFP_KERNEL);
 	if (!mutable) {
 		err = -ENOMEM;
 		goto error;
 	}
 
-	err = tnl_set_config(port->config, tnl_vport->tnl_ops, vport, mutable);
+	/* Copy fields whose values should be retained. */
+	old_mutable = rtnl_dereference(tnl_vport->mutable);
+	mutable->seq = old_mutable->seq + 1;
+	memcpy(mutable->eth_addr, old_mutable->eth_addr, ETH_ALEN);
+	mutable->mtu = old_mutable->mtu;
+
+	/* Parse the others configured by userspace. */
+	err = tnl_set_config(options, tnl_vport->tnl_ops, vport, mutable);
 	if (err)
 		goto error_free;
 
-	mutable->seq++;
-
 	err = move_port(vport, mutable);
 	if (err)
 		goto error_free;
@@ -1457,6 +1493,31 @@ 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);
+	const struct tnl_mutable_config *mutable = rcu_dereference_rtnl(tnl_vport->mutable);
+
+	NLA_PUT_U32(skb, ODP_TUNNEL_ATTR_FLAGS, mutable->flags & TNL_F_PUBLIC);
+	NLA_PUT_BE32(skb, ODP_TUNNEL_ATTR_DST_IPV4, mutable->daddr);
+
+	if (!(mutable->flags & TNL_F_IN_KEY_MATCH))
+		NLA_PUT_BE64(skb, ODP_TUNNEL_ATTR_IN_KEY, mutable->in_key);
+	if (!(mutable->flags & TNL_F_OUT_KEY_ACTION))
+		NLA_PUT_BE64(skb, ODP_TUNNEL_ATTR_OUT_KEY, mutable->out_key);
+	if (mutable->saddr)
+		NLA_PUT_BE32(skb, ODP_TUNNEL_ATTR_SRC_IPV4, mutable->saddr);
+	if (mutable->tos)
+		NLA_PUT_U8(skb, ODP_TUNNEL_ATTR_TOS, mutable->tos);
+	if (mutable->ttl)
+		NLA_PUT_U8(skb, ODP_TUNNEL_ATTR_TTL, mutable->ttl);
+
+	return 0;
+
+nla_put_failure:
+	return -EMSGSIZE;
+}
+
 static void free_port_rcu(struct rcu_head *rcu)
 {
 	struct tnl_vport *tnl_vport = container_of(rcu,
@@ -1474,9 +1535,9 @@ int tnl_destroy(struct vport *vport)
 
 	mutable = rtnl_dereference(tnl_vport->mutable);
 
-	if (vport == tnl_find_port(mutable->port_config.saddr,
-	    mutable->port_config.daddr, mutable->port_config.in_key,
-	    mutable->tunnel_type, &old_mutable))
+	if (vport == tnl_find_port(mutable->saddr, mutable->daddr,
+				   mutable->in_key, mutable->tunnel_type,
+				   &old_mutable))
 		del_port(vport);
 
 	call_rcu(&tnl_vport->rcu, free_port_rcu);
@@ -1528,15 +1589,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..c3bc8dd 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
@@ -36,18 +36,50 @@
 #define TNL_T_KEY_MATCH		(1 << 11)
 #define TNL_T_KEY_EITHER	(TNL_T_KEY_EXACT | TNL_T_KEY_MATCH)
 
+/* Private flags not exposed to userspace in this form. */
+#define TNL_F_IN_KEY_MATCH      (1 << 16) /* Store the key in tun_id to match in flow table. */
+#define TNL_F_OUT_KEY_ACTION	(1 << 17) /* Get the key from a SET_TUNNEL action. */
+
+/* All public tunnel flags. */
+#define TNL_F_PUBLIC (TNL_F_CSUM | TNL_F_TOS_INHERIT | TNL_F_TTL_INHERIT | \
+		      TNL_F_PMTUD | TNL_F_HDR_CACHE | TNL_F_IPSEC)
+
+/**
+ * struct tnl_mutable_config - modifiable configuration for a tunnel.
+ * @rcu: RCU callback head for deferred destruction.
+ * @seq: Sequence number for distinguishing configuration versions.
+ * @tunnel_type: Set of TNL_T_* flags that define lookup.
+ * @tunnel_hlen: Tunnel header length.
+ * @eth_addr: Source address for packets generated by tunnel itself
+ * (e.g. ICMP fragmentation needed messages).
+ * @mtu: MTU of tunnel.
+ * @in_key: Key to match on input, 0 for wildcard.
+ * @out_key: Key to use on output, 0 if this tunnel has no fixed output key.
+ * @flags: TNL_F_* flags.
+ * @saddr: IPv4 source address to match, 0 to accept any source address.
+ * @daddr: IPv4 destination of tunnel.
+ * @tos: IPv4 TOS value to use for tunnel, 0 if no fixed TOS.
+ * @ttl: IPv4 TTL value to use for tunnel, 0 if no fixed TTL.
+ */
 struct tnl_mutable_config {
 	struct rcu_head rcu;
 
-	unsigned seq;		/* Sequence number to identify this config. */
+	unsigned seq;
 
-	u32 tunnel_type;	/* Set of TNL_T_* flags that define lookup. */
-	unsigned tunnel_hlen; 	/* Tunnel header length. */
+	u32 tunnel_type;
+	unsigned tunnel_hlen;
 
 	unsigned char eth_addr[ETH_ALEN];
 	unsigned mtu;
 
-	struct tnl_port_config port_config;
+	/* Configured via ODP_TUNNEL_ATTR_* attributes. */
+	__be64	in_key;
+	__be64	out_key;
+	u32	flags;
+	__be32	saddr;
+	__be32	daddr;
+	u8	tos;
+	u8	ttl;
 };
 
 struct tnl_ops {
@@ -59,7 +91,7 @@ struct tnl_ops {
 	 * build_header() (i.e. excludes the IP header).  Returns a negative
 	 * error code if the configuration is invalid.
 	 */
-	int (*hdr_len)(const struct tnl_port_config *);
+	int (*hdr_len)(const struct tnl_mutable_config *);
 
 	/*
 	 * Builds the static portion of the tunnel header, which is stored in
@@ -186,13 +218,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 a674b61..4fb4393 100644
--- a/datapath/vport-capwap.c
+++ b/datapath/vport-capwap.c
@@ -115,14 +115,17 @@ static struct netns_frags frag_netns_state = {
 
 static struct socket *capwap_rcv_socket;
 
-static int capwap_hdr_len(const struct tnl_port_config *port_config)
+static int capwap_hdr_len(const struct tnl_mutable_config *mutable)
 {
-	/* CAPWAP has neither checksums nor keys, so reject ports with those. */
-	if (port_config->flags & (TNL_F_CSUM | TNL_F_IN_KEY_MATCH |
-				  TNL_F_OUT_KEY_ACTION))
+	/* CAPWAP has no checksums. */
+	if (mutable->flags & TNL_F_CSUM)
 		return -EINVAL;
 
-	if (port_config->in_key != 0 || port_config->out_key != 0)
+	/* CAPWAP has no keys, so check that the configuration for keys is the
+	 * default if no key-specific attributes are used.
+	 */
+	if ((mutable->flags & (TNL_F_IN_KEY_MATCH | TNL_F_OUT_KEY_ACTION)) !=
+	    (TNL_F_IN_KEY_MATCH | TNL_F_OUT_KEY_ACTION))
 		return -EINVAL;
 
 	return CAPWAP_HLEN;
@@ -650,13 +653,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 426b6ce..fc489e9 100644
--- a/datapath/vport-gre.c
+++ b/datapath/vport-gre.c
@@ -34,17 +34,16 @@ struct gre_base_hdr {
 	__be16 protocol;
 };
 
-static int gre_hdr_len(const struct tnl_port_config *port_config)
+static int gre_hdr_len(const struct tnl_mutable_config *mutable)
 {
 	int len;
 
 	len = GRE_HEADER_SECTION;
 
-	if (port_config->flags & TNL_F_CSUM)
+	if (mutable->flags & TNL_F_CSUM)
 		len += GRE_HEADER_SECTION;
 
-	if (port_config->out_key ||
-	    port_config->flags & TNL_F_OUT_KEY_ACTION)
+	if (mutable->out_key || mutable->flags & TNL_F_OUT_KEY_ACTION)
 		len += GRE_HEADER_SECTION;
 
 	return len;
@@ -70,18 +69,17 @@ static void gre_build_header(const struct vport *vport,
 	greh->protocol = htons(ETH_P_TEB);
 	greh->flags = 0;
 
-	if (mutable->port_config.flags & TNL_F_CSUM) {
+	if (mutable->flags & TNL_F_CSUM) {
 		greh->flags |= GRE_CSUM;
 		*options = 0;
 		options++;
 	}
 
-	if (mutable->port_config.out_key ||
-	    mutable->port_config.flags & TNL_F_OUT_KEY_ACTION)
+	if (mutable->out_key || mutable->flags & TNL_F_OUT_KEY_ACTION)
 		greh->flags |= GRE_KEY;
 
-	if (mutable->port_config.out_key)
-		*options = be64_get_low32(mutable->port_config.out_key);
+	if (mutable->out_key)
+		*options = be64_get_low32(mutable->out_key);
 }
 
 static struct sk_buff *gre_update_header(const struct vport *vport,
@@ -93,12 +91,12 @@ static struct sk_buff *gre_update_header(const struct vport *vport,
 					       - GRE_HEADER_SECTION);
 
 	/* Work backwards over the options so the checksum is last. */
-	if (mutable->port_config.flags & TNL_F_OUT_KEY_ACTION) {
+	if (mutable->flags & TNL_F_OUT_KEY_ACTION) {
 		*options = be64_get_low32(OVS_CB(skb)->tun_id);
 		options--;
 	}
 
-	if (mutable->port_config.flags & TNL_F_CSUM)
+	if (mutable->flags & TNL_F_CSUM)
 		*(__sum16 *)options = csum_fold(skb_checksum(skb,
 						skb_transport_offset(skb),
 						skb->len - skb_transport_offset(skb),
@@ -206,14 +204,14 @@ static void gre_err(struct sk_buff *skb, u32 info)
 	 * out key as if it were the in key and then check to see if the input
 	 * and output keys are the same.
 	 */
-	if (mutable->port_config.in_key != mutable->port_config.out_key)
+	if (mutable->in_key != mutable->out_key)
 		return;
 
-	if (!!(mutable->port_config.flags & TNL_F_IN_KEY_MATCH) !=
-	    !!(mutable->port_config.flags & TNL_F_OUT_KEY_ACTION))
+	if (!!(mutable->flags & TNL_F_IN_KEY_MATCH) !=
+	    !!(mutable->flags & TNL_F_OUT_KEY_ACTION))
 		return;
 
-	if ((mutable->port_config.flags & TNL_F_CSUM) && !(flags & GRE_CSUM))
+	if ((mutable->flags & TNL_F_CSUM) && !(flags & GRE_CSUM))
 		return;
 
 	tunnel_hdr_len += iph->ihl << 2;
@@ -336,7 +334,7 @@ static int gre_rcv(struct sk_buff *skb)
 		goto error;
 	}
 
-	if (mutable->port_config.flags & TNL_F_IN_KEY_MATCH)
+	if (mutable->flags & TNL_F_IN_KEY_MATCH)
 		OVS_CB(skb)->tun_id = key;
 	else
 		OVS_CB(skb)->tun_id = 0;
@@ -392,13 +390,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 1c32c5a..7dfea6b 100644
--- a/datapath/vport-patch.c
+++ b/datapath/vport-patch.c
@@ -85,14 +85,31 @@ static void patch_exit(void)
 	kfree(peer_table);
 }
 
-static int patch_set_config(struct vport *vport, const void *config, 
+static const struct nla_policy patch_policy[ODP_PATCH_ATTR_MAX + 1] = {
+#ifdef HAVE_NLA_NUL_STRING
+	[ODP_PATCH_ATTR_PEER] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ - 1 },
+#endif
+};
+
+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[ODP_PATCH_ATTR_MAX + 1];
+	const char *peer_name;
+	int err;
+
+	if (!options)
+		return -EINVAL;
 
-	strlcpy(peer_name, config, IFNAMSIZ);
+	err = nla_parse_nested(a, ODP_PATCH_ATTR_MAX, options, patch_policy);
+	if (err)
+		return err;
+
+	if (!a[ODP_PATCH_ATTR_PEER] || VERIFY_NUL_STRING(a[ODP_PATCH_ATTR_PEER], IFNAMSIZ - 1))
+		return -EINVAL;
 
+	peer_name = nla_data(a[ODP_PATCH_ATTR_PEER]);
 	if (!strcmp(patch_vport->name, peer_name))
 		return -EINVAL;
 
@@ -125,7 +142,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 +169,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 +202,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 +221,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 +281,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, ODP_PATCH_ATTR_PEER, patchconf->peer_name);
 }
 
 static int patch_get_mtu(const struct vport *vport)
@@ -302,13 +318,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 2d18f28..8289c01 100644
--- a/datapath/vport.c
+++ b/datapath/vport.c
@@ -155,277 +155,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));
@@ -588,23 +317,21 @@ 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)
 {
 	ASSERT_RTNL();
-	ASSERT_VPORT();
 
-	if (vport->ops->modify)
-		return vport->ops->modify(vport, port);
-	else
+	if (!vport->ops->set_options)
 		return -EOPNOTSUPP;
+	return vport->ops->set_options(vport, options);
 }
 
 /**
@@ -945,19 +672,35 @@ int vport_get_mtu(const struct vport *vport)
 }
 
 /**
- *	vport_get_config - retrieve device configuration
+ *	vport_get_options - retrieve device options
  *
- * @vport: vport from which to retrieve the configuration.
- * @config: buffer to store config, which must be at least the length
- *          of VPORT_CONFIG_SIZE.
+ * @vport: vport from which to retrieve the options.
+ * @skb: sk_buff where options should be appended.
  *
- * Retrieves the configuration of the given device.  Either RTNL lock or
- * rcu_read_lock must be held.
+ * Retrieves the configuration of the given device, appending an
+ * %ODP_VPORT_ATTR_OPTIONS attribute that in turn contains nested
+ * vport-specific attributes to @skb.  Either RTNL lock or rcu_read_lock must
+ * be held.
+ *
+ * Returns 0 if successful, -EMSGSIZE if @skb has insufficient room, or another
+ * negative error code if a real error occurred.
  */
-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);
+	struct nlattr *nla;
+
+	nla = nla_nest_start(skb, ODP_VPORT_ATTR_OPTIONS);
+	if (!nla)
+		return -EMSGSIZE;
+
+	if (vport->ops->get_options) {
+		int err = vport->ops->get_options(vport, skb);
+		if (err)
+			return err;
+	}
+
+	nla_nest_end(skb, nla);
+	return 0;
 }
 
 /**
diff --git a/datapath/vport.h b/datapath/vport.h
index 5e75f56..56817e8 100644
--- a/datapath/vport.h
+++ b/datapath/vport.h
@@ -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: %ODP_VPORT_ATTR_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;
@@ -160,9 +152,12 @@ struct vport_parms {
  * @exit: Called at module unload.
  * @create: Create a new vport configured as specified.  On success returns
  * a new vport allocated with vport_alloc(), otherwise an ERR_PTR() value.
- * @modify: Modify the configuration of an existing vport.  May be null if
- * modification is not supported.
  * @destroy: Detach and destroy a vport.
+ * @set_options: Modify the configuration of an existing vport.  May be %NULL
+ * if modification is not supported.
+ * @get_options: Appends vport-specific attributes for the configuration of an
+ * existing vport to a &struct sk_buff.  May be %NULL for a vport that does not
+ * have any configuration.
  * @set_mtu: Set the device's MTU.  May be null if not supported.
  * @set_addr: Set the device's MAC address.  May be null if not supported.
  * @get_name: Get the device's name.
@@ -194,9 +189,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 *);
 
diff --git a/include/openvswitch/datapath-protocol.h b/include/openvswitch/datapath-protocol.h
index 1d68445..4a2afb2 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. */
@@ -176,16 +170,6 @@ struct odp_packet {
 	uint32_t len;
 };
 
-#define VPORT_CONFIG_SIZE     32
-struct odp_port {
-    char devname[16];           /* IFNAMSIZ */
-    uint32_t type;              /* One of ODP_VPORT_TYPE_*. */
-    uint16_t port;
-    uint16_t dp_idx;
-    uint32_t reserved2;
-    __aligned_u64 config[VPORT_CONFIG_SIZE / 8]; /* type-specific */
-};
-
 enum odp_vport_type {
 	ODP_VPORT_TYPE_UNSPEC,
 	ODP_VPORT_TYPE_NETDEV,   /* network device */
@@ -199,20 +183,46 @@ enum odp_vport_type {
 #define ODP_VPORT_TYPE_MAX (__ODP_VPORT_TYPE_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.
+ * @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
+ * %ODP_VPORT_ATTR_*, 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 {
+	uint32_t dp_idx;
+	uint32_t len;
+	uint32_t total_len;
+};
+
+enum {
+	ODP_VPORT_ATTR_UNSPEC,
+	ODP_VPORT_ATTR_PORT_NO,	/* port number within datapath */
+	ODP_VPORT_ATTR_TYPE,	/* 32-bit ODP_VPORT_TYPE_* constant. */
+	ODP_VPORT_ATTR_NAME,	/* string name, up to IFNAMSIZ bytes long */
+	ODP_VPORT_ATTR_STATS,	/* struct rtnl_link_stats64 */
+	ODP_VPORT_ATTR_ADDRESS, /* hardware address */
+	ODP_VPORT_ATTR_MTU,	/* 32-bit maximum transmission unit */
+	ODP_VPORT_ATTR_OPTIONS, /* nested attributes, varies by vport type */
+	ODP_VPORT_ATTR_IFINDEX, /* 32-bit ifindex of backing netdev */
+	ODP_VPORT_ATTR_IFLINK,	/* 32-bit ifindex on which packets are sent */
+	__ODP_VPORT_ATTR_MAX
 };
 
+#define ODP_VPORT_ATTR_MAX (__ODP_VPORT_ATTR_MAX - 1)
+
+/* ODP_VPORT_ATTR_OPTIONS attributes for patch vports. */
+enum {
+	ODP_PATCH_ATTR_UNSPEC,
+	ODP_PATCH_ATTR_PEER,	/* name of peer vport, as a string */
+	__ODP_PATCH_ATTR_MAX
+};
+
+#define ODP_PATCH_ATTR_MAX (__ODP_PATCH_ATTR_MAX - 1)
+
 struct odp_flow_stats {
     uint64_t n_packets;         /* Number of matched packets. */
     uint64_t n_bytes;           /* Number of matched bytes. */
@@ -353,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..a394f8a 100644
--- a/include/openvswitch/tunnel.h
+++ b/include/openvswitch/tunnel.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
  *
  * This file is offered under your choice of two licenses: Apache 2.0 or GNU
  * GPL 2.0 or later.  The permission statements for each of these licenses is
@@ -43,24 +43,30 @@
 #include <linux/types.h>
 #include "openvswitch/datapath-protocol.h"
 
-#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. */
-#define TNL_F_TOS_INHERIT	(1 << 4) /* Inherit the ToS from the inner packet. */
-#define TNL_F_TTL_INHERIT	(1 << 5) /* Inherit the TTL from the inner packet. */
-#define TNL_F_PMTUD		(1 << 6) /* Enable path MTU discovery. */
-#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. */
-struct tnl_port_config {
-	__aligned_be64	in_key;
-	__aligned_be64	out_key;
-	__u32		flags;
-	__be32		saddr;
-	__be32		daddr;
-	__u8		tos;
-	__u8		ttl;
+/* ODP_VPORT_ATTR_OPTIONS attributes for tunnels.
+ *
+ * ODP_TUNNEL_ATTR_FLAGS and ODP_TUNNEL_ATTR_DST_IPV4 are required.  All other
+ * attributes are optional.
+ */
+enum {
+	ODP_TUNNEL_ATTR_UNSPEC,
+	ODP_TUNNEL_ATTR_FLAGS,    /* 32-bit TNL_F_*. */
+	ODP_TUNNEL_ATTR_DST_IPV4, /* IPv4 destination address. */
+	ODP_TUNNEL_ATTR_SRC_IPV4, /* IPv4 source address. */
+	ODP_TUNNEL_ATTR_OUT_KEY,  /* __be64 key to use on output. */
+	ODP_TUNNEL_ATTR_IN_KEY,   /* __be64 key to match on input. */
+	ODP_TUNNEL_ATTR_TOS,      /* 8-bit TOS value. */
+	ODP_TUNNEL_ATTR_TTL,      /* 8-bit TTL value. */
+	__ODP_TUNNEL_ATTR_MAX
 };
 
+#define ODP_TUNNEL_ATTR_MAX (__ODP_TUNNEL_ATTR_MAX - 1)
+
+#define TNL_F_CSUM		(1 << 0) /* Checksum packets. */
+#define TNL_F_TOS_INHERIT	(1 << 1) /* Inherit the ToS from the inner packet. */
+#define TNL_F_TTL_INHERIT	(1 << 2) /* Inherit the TTL from the inner packet. */
+#define TNL_F_PMTUD		(1 << 3) /* Enable path MTU discovery. */
+#define TNL_F_HDR_CACHE		(1 << 4) /* Enable tunnel header caching. */
+#define TNL_F_IPSEC		(1 << 5) /* Traffic is IPsec encrypted. */
+
 #endif /* openvswitch/tunnel.h */
diff --git a/lib/dpif-linux.c b/lib/dpif-linux.c
index cf3c9b3..8e584ea 100644
--- a/lib/dpif-linux.c
+++ b/lib/dpif-linux.c
@@ -70,12 +70,11 @@ struct dpif_linux {
 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 +121,103 @@ 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;
+    request.port_no = ODPP_LOCAL;
+    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;
+
+    error = open_minor(dp_idx, &fd);
+    if (error) {
+        goto error;
+    }
 
-        /* Then use that to finish up opening. */
-        return finish_open(&dpif->dpif, port.devname);
+    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,118 +274,88 @@ 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 ODP_VPORT_TYPE_UNSPEC:
-        break;
-
-    case ODP_VPORT_TYPE_NETDEV:
-        return "system";
-
-    case ODP_VPORT_TYPE_INTERNAL:
-        return "internal";
-
-    case ODP_VPORT_TYPE_PATCH:
-        return "patch";
-
-    case ODP_VPORT_TYPE_GRE:
-        memcpy(&tnl_config, odp_port->config, sizeof tnl_config);
-        return tnl_config.flags & TNL_F_IPSEC ? "ipsec_gre" : "gre";
-
-    case ODP_VPORT_TYPE_CAPWAP:
-        return "capwap";
-
-    case __ODP_VPORT_TYPE_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") ? ODP_VPORT_TYPE_NETDEV
-            : !strcmp(type, "internal") ? ODP_VPORT_TYPE_INTERNAL
-            : !strcmp(type, "patch") ? ODP_VPORT_TYPE_PATCH
-            : (!strcmp(type, "gre")
-               || !strcmp(type, "ipsec_gre")) ? ODP_VPORT_TYPE_GRE
-            : !strcmp(type, "capwap") ? ODP_VPORT_TYPE_CAPWAP
-            : ODP_VPORT_TYPE_UNSPEC);
-}
-
 static int
-dpif_linux_port_add(struct dpif *dpif, struct netdev *netdev,
+dpif_linux_port_add(struct dpif *dpif_, struct netdev *netdev,
                     uint16_t *port_nop)
 {
+    struct dpif_linux *dpif = dpif_linux_cast(dpif_);
     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 == ODP_VPORT_TYPE_UNSPEC) {
+    dpif_linux_vport_init(&request);
+    request.cmd = ODP_VPORT_NEW;
+    request.dp_idx = dpif->minor;
+    request.type = netdev_vport_get_vport_type(netdev);
+    if (request.type == ODP_VPORT_TYPE_UNSPEC) {
         VLOG_WARN_RL(&error_rl, "%s: cannot create port `%s' because it has "
                      "unsupported type `%s'",
-                     dpif_name(dpif), name, type);
+                     dpif_name(dpif_), name, type);
         return EINVAL;
     }
+    request.name = name;
 
-    error = do_ioctl(dpif, ODP_VPORT_ATTACH, &port);
+    options = netdev_vport_get_options(netdev);
+    if (options && options->size) {
+        request.options = options->data;
+        request.options_len = options->size;
+    }
+
+    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
@@ -359,40 +371,52 @@ 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,75 +693,22 @@ do_ioctl(const struct dpif *dpif_, int cmd, const void *arg)
     return ioctl(dpif->fd, cmd, arg) ? errno : 0;
 }
 
-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);
-    }
-
-    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) {
-            VLOG_WARN_RL(&error_rl, "%s: vport query failed (%s)",
-                         name, strerror(errno));
-        }
-        return errno;
-    } else if (odp_port.type == ODP_VPORT_TYPE_INTERNAL) {
-        *dp_idx = odp_port.dp_idx;
-        *port_no = odp_port.port;
-        return 0;
-    } else {
-        return EINVAL;
-    }
-}
-
-static int
-lookup_minor(const char *name, int *minorp)
+bool
+dpif_linux_is_internal_device(const char *name)
 {
-    int minor, port_no;
+    struct dpif_linux_vport reply;
+    struct ofpbuf *buf;
     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;
-    } else {
-        *minorp = minor;
-        return 0;
+    error = dpif_linux_vport_get(name, &reply, &buf);
+    if (!error) {
+        ofpbuf_delete(buf);
+    } else if (error != ENODEV) {
+        VLOG_WARN_RL(&error_rl, "%s: vport query failed (%s)",
+                     name, strerror(error));
     }
-}
 
-bool
-dpif_linux_is_internal_device(const char *name)
-{
-    int minor, port_no;
-
-    return !lookup_internal_device(name, &minor, &port_no);
+    return reply.type == ODP_VPORT_TYPE_INTERNAL;
 }
 
 static int
@@ -859,78 +830,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
@@ -952,3 +886,204 @@ 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[] = {
+        [ODP_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32 },
+        [ODP_VPORT_ATTR_TYPE] = { .type = NL_A_U32 },
+        [ODP_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .max_len = IFNAMSIZ },
+        [ODP_VPORT_ATTR_STATS] = { .type = NL_A_UNSPEC,
+                                   .min_len = sizeof(struct rtnl_link_stats64),
+                                   .max_len = sizeof(struct rtnl_link_stats64),
+                                   .optional = true },
+        [ODP_VPORT_ATTR_ADDRESS] = { .type = NL_A_UNSPEC,
+                                     .min_len = ETH_ADDR_LEN,
+                                     .max_len = ETH_ADDR_LEN,
+                                     .optional = true },
+        [ODP_VPORT_ATTR_MTU] = { .type = NL_A_U32, .optional = true },
+        [ODP_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = true },
+        [ODP_VPORT_ATTR_IFINDEX] = { .type = NL_A_U32, .optional = true },
+        [ODP_VPORT_ATTR_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 = nl_attr_get_u32(a[ODP_VPORT_ATTR_PORT_NO]);
+    vport->type = nl_attr_get_u32(a[ODP_VPORT_ATTR_TYPE]);
+    vport->name = nl_attr_get_string(a[ODP_VPORT_ATTR_NAME]);
+    if (a[ODP_VPORT_ATTR_STATS]) {
+        vport->stats = nl_attr_get(a[ODP_VPORT_ATTR_STATS]);
+    }
+    if (a[ODP_VPORT_ATTR_ADDRESS]) {
+        vport->address = nl_attr_get(a[ODP_VPORT_ATTR_ADDRESS]);
+    }
+    if (a[ODP_VPORT_ATTR_MTU]) {
+        vport->mtu = nl_attr_get_u32(a[ODP_VPORT_ATTR_MTU]);
+    }
+    if (a[ODP_VPORT_ATTR_OPTIONS]) {
+        vport->options = nl_attr_get(a[ODP_VPORT_ATTR_OPTIONS]);
+        vport->options_len = nl_attr_get_size(a[ODP_VPORT_ATTR_OPTIONS]);
+    }
+    if (a[ODP_VPORT_ATTR_IFINDEX]) {
+        vport->ifindex = nl_attr_get_u32(a[ODP_VPORT_ATTR_IFINDEX]);
+    }
+    if (a[ODP_VPORT_ATTR_IFLINK]) {
+        vport->iflink = nl_attr_get_u32(a[ODP_VPORT_ATTR_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->port_no != UINT32_MAX) {
+        nl_msg_put_u32(buf, ODP_VPORT_ATTR_PORT_NO, vport->port_no);
+    }
+
+    if (vport->type != ODP_VPORT_TYPE_UNSPEC) {
+        nl_msg_put_u32(buf, ODP_VPORT_ATTR_TYPE, vport->type);
+    }
+
+    if (vport->name) {
+        nl_msg_put_string(buf, ODP_VPORT_ATTR_NAME, vport->name);
+    }
+
+    if (vport->stats) {
+        nl_msg_put_unspec(buf, ODP_VPORT_ATTR_STATS,
+                          vport->stats, sizeof *vport->stats);
+    }
+
+    if (vport->address) {
+        nl_msg_put_unspec(buf, ODP_VPORT_ATTR_ADDRESS,
+                          vport->address, ETH_ADDR_LEN);
+    }
+
+    if (vport->mtu) {
+        nl_msg_put_u32(buf, ODP_VPORT_ATTR_MTU, vport->mtu);
+    }
+
+    if (vport->options) {
+        nl_msg_put_nested(buf, ODP_VPORT_ATTR_OPTIONS,
+                          vport->options, vport->options_len);
+    }
+
+    if (vport->ifindex) {
+        nl_msg_put_u32(buf, ODP_VPORT_ATTR_IFINDEX, vport->ifindex);
+    }
+
+    if (vport->iflink) {
+        nl_msg_put_u32(buf, ODP_VPORT_ATTR_IFLINK, vport->iflink);
+    }
+
+    odp_vport = ofpbuf_push_uninit(buf, sizeof *odp_vport);
+    odp_vport->dp_idx = vport->dp_idx;
+    odp_vport->len = buf->size;
+    odp_vport->total_len = (char *) ofpbuf_end(buf) - (char *) buf->data;
+}
+
+/* Clears 'vport' to "empty" values. */
+void
+dpif_linux_vport_init(struct dpif_linux_vport *vport)
+{
+    memset(vport, 0, sizeof *vport);
+    vport->dp_idx = UINT32_MAX;
+    vport->port_no = UINT32_MAX;
+}
+
+/* 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..30e927f 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. */
+    uint32_t dp_idx;
+    uint32_t port_no;                      /* UINT32_MAX if unknown. */
+    enum odp_vport_type type;
+
+    /* Attributes. */
+    const char *name;                      /* ODP_VPORT_ATTR_NAME. */
+    const struct rtnl_link_stats64 *stats; /* ODP_VPORT_ATTR_STATS. */
+    const uint8_t *address;                /* ODP_VPORT_ATTR_ADDRESS. */
+    int mtu;                               /* ODP_VPORT_ATTR_MTU. */
+    const struct nlattr *options;          /* ODP_VPORT_ATTR_OPTIONS. */
+    size_t options_len;
+    int ifindex;                           /* ODP_VPORT_ATTR_IFINDEX. */
+    int iflink;                            /* ODP_VPORT_ATTR_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 e930e1b..732fb2a 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"
@@ -36,8 +37,8 @@
 #include "openvswitch/datapath-protocol.h"
 #include "openvswitch/tunnel.h"
 #include "packets.h"
-#include "rtnetlink.h"
 #include "route-table.h"
+#include "rtnetlink.h"
 #include "rtnetlink-link.h"
 #include "shash.h"
 #include "socket-util.h"
@@ -63,7 +64,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 {
@@ -74,9 +75,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 =
@@ -84,10 +86,12 @@ 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 int tnl_port_config_from_nlattr(const struct nlattr *options,
+                                       size_t options_len,
+                                       struct nlattr *a[ODP_TUNNEL_ATTR_MAX + 1]);
 
 static void netdev_vport_tnl_iface_init(void);
 static void netdev_vport_link_change(const struct rtnetlink_link_change *,
@@ -122,17 +126,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 ODP_VPORT_ATTR_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);
+
+    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 ? ODP_VPORT_TYPE_INTERNAL
+            : class == &netdev_linux_class ? ODP_VPORT_TYPE_NETDEV
+            : ODP_VPORT_TYPE_UNSPEC);
+}
+
+const char *
+netdev_vport_get_netdev_type(const struct dpif_linux_vport *vport)
+{
+    struct nlattr *a[ODP_TUNNEL_ATTR_MAX + 1];
+
+    switch (vport->type) {
+    case ODP_VPORT_TYPE_UNSPEC:
+        break;
+
+    case ODP_VPORT_TYPE_NETDEV:
+        return "system";
+
+    case ODP_VPORT_TYPE_INTERNAL:
+        return "internal";
 
-    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);
+    case ODP_VPORT_TYPE_PATCH:
+        return "patch";
+
+    case ODP_VPORT_TYPE_GRE:
+        if (tnl_port_config_from_nlattr(vport->options, vport->options_len,
+                                        a)) {
+            break;
+        }
+        return (nl_attr_get_u32(a[ODP_TUNNEL_ATTR_FLAGS]) & TNL_F_IPSEC
+                ? "ipsec_gre" : "gre");
+
+    case ODP_VPORT_TYPE_CAPWAP:
+        return "capwap";
+
+    case __ODP_VPORT_TYPE_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
@@ -149,38 +203,40 @@ 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 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);
+        error = dpif_linux_vport_get(name, &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));
+            } else {
+                options = ofpbuf_clone_data(reply.options, reply.options_len);
             }
+            ofpbuf_delete(buf);
         } else {
             VLOG_ERR_RL(&rl, "%s: vport query failed (%s)",
                         name, strerror(error));
@@ -194,9 +250,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);
@@ -239,24 +297,36 @@ 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;
+            error = 0;
         }
     }
+    ofpbuf_delete(options);
+
     return error;
 }
 
@@ -264,94 +334,94 @@ 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;
+
+    error = dpif_linux_vport_get(netdev_get_name(netdev), &reply, &buf);
+    if (error) {
+        return error;
+    } else if (!reply.stats) {
+        ofpbuf_delete(buf);
+        return EOPNOTSUPP;
+    }
 
-    ovs_strlcpy(ovsr.devname, name, sizeof ovsr.devname);
-    err = netdev_vport_do_ioctl(ODP_VPORT_STATS_GET, &ovsr);
-    if (err) {
-        return err;
-    }
-
-    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;
+
+    ofpbuf_delete(buf);
 
     return 0;
 }
@@ -359,34 +429,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
@@ -615,14 +689,17 @@ netdev_vport_tnl_iface_init(void)
 static const char *
 netdev_vport_get_tnl_iface(const struct netdev *netdev)
 {
+    struct nlattr *a[ODP_TUNNEL_ATTR_MAX + 1];
     int ifindex;
     uint32_t route;
     struct netdev_dev_vport *ndv;
-    struct tnl_port_config *config;
 
     ndv = netdev_dev_vport_cast(netdev_get_dev(netdev));
-    config = (struct tnl_port_config *) ndv->config;
-    route = config->daddr;
+    if (tnl_port_config_from_nlattr(ndv->options->data, ndv->options->size,
+                                    a)) {
+        return NULL;
+    }
+    route = nl_attr_get_be32(a[ODP_TUNNEL_ATTR_DST_IPV4]);
 
     if (route_table_get_ifindex(route, &ifindex)) {
         struct name_node *nn;
@@ -638,22 +715,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)
 {
@@ -675,30 +736,46 @@ netdev_vport_poll_notify(const struct netdev *netdev)
 
 /* Code specific to individual vport types. */
 
+static void
+set_key(const struct shash *args, const char *name, uint16_t type,
+        struct ofpbuf *options)
+{
+    const char *s;
+
+    s = shash_find_data(args, name);
+    if (!s) {
+        s = shash_find_data(args, "key");
+        if (!s) {
+            s = "0";
+        }
+    }
+
+    if (!strcmp(s, "flow")) {
+        /* This is the default if no attribute is present. */
+    } else {
+        nl_msg_put_be64(options, type, htonll(strtoull(s, NULL, 0)));
+    }
+}
+
 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;
-    struct tnl_port_config config;
     struct shash_node *node;
     bool ipsec_mech_set = false;
+    ovs_be32 daddr = htonl(0);
+    uint32_t flags;
 
-    memset(&config, 0, sizeof config);
-    config.flags |= TNL_F_PMTUD;
-    config.flags |= TNL_F_HDR_CACHE;
-
+    flags = TNL_F_PMTUD | TNL_F_HDR_CACHE;
     if (!strcmp(type, "gre")) {
         is_gre = true;
     } else if (!strcmp(type, "ipsec_gre")) {
         is_gre = true;
         is_ipsec = true;
-
-        config.flags |= TNL_F_IPSEC;
-
-        /* IPsec doesn't work when header caching is enabled. */
-        config.flags &= ~TNL_F_HDR_CACHE;
+        flags |= TNL_F_IPSEC;
+        flags &= ~TNL_F_HDR_CACHE;
     }
 
     SHASH_FOR_EACH (node, args) {
@@ -707,58 +784,39 @@ parse_tunnel_config(const char *name, const char *type,
             if (lookup_ip(node->data, &in_addr)) {
                 VLOG_WARN("%s: bad %s 'remote_ip'", name, type);
             } else {
-                config.daddr = in_addr.s_addr;
+                daddr = in_addr.s_addr;
             }
         } else if (!strcmp(node->name, "local_ip")) {
             struct in_addr in_addr;
             if (lookup_ip(node->data, &in_addr)) {
                 VLOG_WARN("%s: bad %s 'local_ip'", name, type);
             } else {
-                config.saddr = in_addr.s_addr;
-            }
-        } else if (!strcmp(node->name, "key") && is_gre) {
-            if (!strcmp(node->data, "flow")) {
-                config.flags |= TNL_F_IN_KEY_MATCH;
-                config.flags |= TNL_F_OUT_KEY_ACTION;
-            } else {
-                uint64_t key = strtoull(node->data, NULL, 0);
-                config.out_key = config.in_key = htonll(key);
-            }
-        } else if (!strcmp(node->name, "in_key") && is_gre) {
-            if (!strcmp(node->data, "flow")) {
-                config.flags |= TNL_F_IN_KEY_MATCH;
-            } else {
-                config.in_key = htonll(strtoull(node->data, NULL, 0));
-            }
-        } else if (!strcmp(node->name, "out_key") && is_gre) {
-            if (!strcmp(node->data, "flow")) {
-                config.flags |= TNL_F_OUT_KEY_ACTION;
-            } else {
-                config.out_key = htonll(strtoull(node->data, NULL, 0));
+                nl_msg_put_be32(options, ODP_TUNNEL_ATTR_SRC_IPV4,
+                                in_addr.s_addr);
             }
         } else if (!strcmp(node->name, "tos")) {
             if (!strcmp(node->data, "inherit")) {
-                config.flags |= TNL_F_TOS_INHERIT;
+                flags |= TNL_F_TOS_INHERIT;
             } else {
-                config.tos = atoi(node->data);
+                nl_msg_put_u8(options, ODP_TUNNEL_ATTR_TOS, atoi(node->data));
             }
         } else if (!strcmp(node->name, "ttl")) {
             if (!strcmp(node->data, "inherit")) {
-                config.flags |= TNL_F_TTL_INHERIT;
+                flags |= TNL_F_TTL_INHERIT;
             } else {
-                config.ttl = atoi(node->data);
+                nl_msg_put_u8(options, ODP_TUNNEL_ATTR_TTL, atoi(node->data));
             }
         } else if (!strcmp(node->name, "csum") && is_gre) {
             if (!strcmp(node->data, "true")) {
-                config.flags |= TNL_F_CSUM;
+                flags |= TNL_F_CSUM;
             }
         } else if (!strcmp(node->name, "pmtud")) {
             if (!strcmp(node->data, "false")) {
-                config.flags &= ~TNL_F_PMTUD;
+                flags &= ~TNL_F_PMTUD;
             }
         } else if (!strcmp(node->name, "header_cache")) {
             if (!strcmp(node->data, "false")) {
-                config.flags &= ~TNL_F_HDR_CACHE;
+                flags &= ~TNL_F_HDR_CACHE;
             }
         } else if (!strcmp(node->name, "peer_cert") && is_ipsec) {
             if (shash_find(args, "certificate")) {
@@ -787,9 +845,12 @@ parse_tunnel_config(const char *name, const char *type,
                     || !strcmp(node->name, "private_key")
                     || !strcmp(node->name, "use_ssl_cert"))) {
             /* Ignore options not used by the netdev. */
+        } else if (is_gre && (!strcmp(node->name, "key") &&
+                              !strcmp(node->name, "in_key") &&
+                              !strcmp(node->name, "out_key"))) {
+            /* Handled separately below. */
         } else {
-            VLOG_WARN("%s: unknown %s argument '%s'",
-                      name, type, node->name);
+            VLOG_WARN("%s: unknown %s argument '%s'", name, type, node->name);
         }
     }
 
@@ -806,65 +867,122 @@ parse_tunnel_config(const char *name, const char *type,
         }
     }
 
-    if (!config.daddr) {
+    if (is_gre) {
+        set_key(args, "in_key", ODP_TUNNEL_ATTR_IN_KEY, options);
+        set_key(args, "out_key", ODP_TUNNEL_ATTR_OUT_KEY, options);
+    }
+
+    if (!daddr) {
         VLOG_WARN("%s: %s type requires valid 'remote_ip' argument",
                   name, type);
         return EINVAL;
     }
+    nl_msg_put_be32(options, ODP_TUNNEL_ATTR_DST_IPV4, daddr);
+
+    nl_msg_put_u32(options, ODP_TUNNEL_ATTR_FLAGS, flags);
 
-    BUILD_ASSERT(sizeof config <= VPORT_CONFIG_SIZE);
-    memcpy(configp, &config, sizeof config);
     return 0;
 }
 
 static int
+tnl_port_config_from_nlattr(const struct nlattr *options, size_t options_len,
+                            struct nlattr *a[ODP_TUNNEL_ATTR_MAX + 1])
+{
+    static const struct nl_policy odp_tunnel_policy[] = {
+        [ODP_TUNNEL_ATTR_FLAGS] = { .type = NL_A_U32 },
+        [ODP_TUNNEL_ATTR_DST_IPV4] = { .type = NL_A_BE32 },
+        [ODP_TUNNEL_ATTR_SRC_IPV4] = { .type = NL_A_BE32, .optional = true },
+        [ODP_TUNNEL_ATTR_IN_KEY] = { .type = NL_A_BE64, .optional = true },
+        [ODP_TUNNEL_ATTR_OUT_KEY] = { .type = NL_A_BE64, .optional = true },
+        [ODP_TUNNEL_ATTR_TOS] = { .type = NL_A_U8, .optional = true },
+        [ODP_TUNNEL_ATTR_TTL] = { .type = NL_A_U8, .optional = true },
+    };
+    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 EINVAL;
+    }
+    return 0;
+}
+
+static uint64_t
+get_be64_or_zero(const struct nlattr *a)
+{
+    return a ? ntohll(nl_attr_get_be64(a)) : 0;
+}
+
+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_;
+    struct nlattr *a[ODP_TUNNEL_ATTR_MAX + 1];
+    ovs_be32 daddr;
+    uint32_t flags;
+    int error;
+
+    error = tnl_port_config_from_nlattr(options, options_len, a);
+    if (error) {
+        return error;
+    }
 
-    if (!(config->flags & TNL_F_HDR_CACHE) == !(config->flags & TNL_F_IPSEC)) {
+    flags = nl_attr_get_u32(a[ODP_TUNNEL_ATTR_FLAGS]);
+    if (!(flags & TNL_F_HDR_CACHE) == !(flags & TNL_F_IPSEC)) {
         smap_add(args, "header_cache",
-                 config->flags & TNL_F_HDR_CACHE ? "true" : "false");
+                 flags & TNL_F_HDR_CACHE ? "true" : "false");
     }
-    shash_add(args, "remote_ip", xasprintf(IP_FMT, IP_ARGS(&config->daddr)));
-    if (config->saddr) {
-        shash_add(args, "local_ip",
-                  xasprintf(IP_FMT, IP_ARGS(&config->saddr)));
+
+    daddr = nl_attr_get_be32(a[ODP_TUNNEL_ATTR_DST_IPV4]);
+    shash_add(args, "remote_ip", xasprintf(IP_FMT, IP_ARGS(&daddr)));
+
+    if (a[ODP_TUNNEL_ATTR_SRC_IPV4]) {
+        ovs_be32 saddr = nl_attr_get_be32(a[ODP_TUNNEL_ATTR_SRC_IPV4]);
+        shash_add(args, "local_ip", xasprintf(IP_FMT, IP_ARGS(&saddr)));
     }
 
-    if ((config->flags & (TNL_F_IN_KEY_MATCH | TNL_F_OUT_KEY_ACTION))
-                      == (TNL_F_IN_KEY_MATCH | TNL_F_OUT_KEY_ACTION)) {
+    if (!a[ODP_TUNNEL_ATTR_IN_KEY] && !a[ODP_TUNNEL_ATTR_OUT_KEY]) {
         smap_add(args, "key", "flow");
-    } else if (config->in_key && config->in_key == config->out_key) {
-        shash_add(args, "key",
-                  xasprintf("%"PRIu64, ntohll(config->in_key)));
     } else {
-        if (config->flags & TNL_F_IN_KEY_MATCH) {
-            smap_add(args, "in_key", "flow");
-        } else if (config->in_key) {
-            shash_add(args, "in_key",
-                      xasprintf("%"PRIu64, ntohll(config->in_key)));
-        }
+        uint64_t in_key = get_be64_or_zero(a[ODP_TUNNEL_ATTR_IN_KEY]);
+        uint64_t out_key = get_be64_or_zero(a[ODP_TUNNEL_ATTR_OUT_KEY]);
+
+        if (in_key && in_key == out_key) {
+            shash_add(args, "key", xasprintf("%"PRIu64, in_key));
+        } else {
+            if (!a[ODP_TUNNEL_ATTR_IN_KEY]) {
+                smap_add(args, "in_key", "flow");
+            } else if (in_key) {
+                shash_add(args, "in_key", xasprintf("%"PRIu64, in_key));
+            }
 
-        if (config->flags & TNL_F_OUT_KEY_ACTION) {
-            smap_add(args, "out_key", "flow");
-        } else if (config->out_key) {
-            shash_add(args, "out_key",
-                      xasprintf("%"PRIu64, ntohll(config->out_key)));
+            if (!a[ODP_TUNNEL_ATTR_OUT_KEY]) {
+                smap_add(args, "out_key", "flow");
+            } else if (out_key) {
+                shash_add(args, "out_key", xasprintf("%"PRIu64, out_key));
+            }
         }
     }
 
-    if (config->flags & TNL_F_TTL_INHERIT) {
+    if (flags & TNL_F_TTL_INHERIT) {
+        smap_add(args, "tos", "inherit");
+    } else if (a[ODP_TUNNEL_ATTR_TTL]) {
+        int ttl = nl_attr_get_u8(a[ODP_TUNNEL_ATTR_TTL]);
+        shash_add(args, "tos", xasprintf("%d", ttl));
+    }
+
+    if (flags & TNL_F_TOS_INHERIT) {
         smap_add(args, "tos", "inherit");
-    } else if (config->ttl) {
-        shash_add(args, "tos", xasprintf("%"PRIu8, config->ttl));
+    } else if (a[ODP_TUNNEL_ATTR_TOS]) {
+        int tos = nl_attr_get_u8(a[ODP_TUNNEL_ATTR_TOS]);
+        shash_add(args, "tos", xasprintf("%d", tos));
     }
 
-    if (config->flags & TNL_F_CSUM) {
+    if (flags & TNL_F_CSUM) {
         smap_add(args, "csum", "true");
     }
-    if (!(config->flags & TNL_F_PMTUD)) {
+    if (!(flags & TNL_F_PMTUD)) {
         smap_add(args, "pmtud", "false");
     }
 
@@ -873,7 +991,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;
 
@@ -888,7 +1006,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;
     }
@@ -898,22 +1016,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, ODP_PATCH_ATTR_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[] = {
+        [ODP_PATCH_ATTR_PEER] = { .type = NL_A_STRING,
+                               .max_len = IFNAMSIZ,
+                               .optional = false }
+    };
+
+    struct nlattr *a[ARRAY_SIZE(odp_patch_policy)];
+    struct ofpbuf buf;
 
-    ovs_strlcpy(peer, config_, MIN(sizeof peer, VPORT_CONFIG_SIZE));
-    if (peer[0]) {
-        smap_add(args, "peer", peer);
+    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[ODP_PATCH_ATTR_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