PSARC/2015/311 Open vSwitch (OVS)
authorMark Haywood <Mark.Haywood@Oracle.COM>
Mon, 16 Nov 2015 16:49:19 -0500
changeset 5090 5f131162e136
parent 5089 8d5767cc3ddc
child 5091 81e5d6b75c3c
PSARC/2015/311 Open vSwitch (OVS) 21653217 Integrate OpenvSwitch into Userland
components/openvswitch/Makefile
components/openvswitch/files/include/solaris/automake.mk
components/openvswitch/files/include/solaris/solarisdefs.h
components/openvswitch/files/lib/dpif-solaris.c
components/openvswitch/files/lib/dpif-solaris.h
components/openvswitch/files/lib/netdev-solaris.c
components/openvswitch/files/lib/netdev-solaris.h
components/openvswitch/files/lib/route-table-solaris.c
components/openvswitch/files/lib/util-solaris.c
components/openvswitch/files/lib/util-solaris.h
components/openvswitch/files/ovs-clean.py
components/openvswitch/files/ovs-svc
components/openvswitch/files/ovs.auth_attr
components/openvswitch/files/ovs.exec_attr
components/openvswitch/files/ovs.prof_attr
components/openvswitch/files/ovs.user_attr
components/openvswitch/files/ovsdb.xml
components/openvswitch/files/vswitch.xml
components/openvswitch/openvswitch.license
components/openvswitch/openvswitch.p5m
components/openvswitch/patches/01-solaris-port.patch
components/openvswitch/patches/02-bridge.patch
components/openvswitch/patches/03-dpif-provider.patch
components/openvswitch/patches/04-netdev-provider.patch
components/openvswitch/patches/05-usage.patch
components/openvswitch/patches/06-controller-fix.patch
components/openvswitch/patches/07-ovsthread_key_destruct-fix.patch
components/openvswitch/patches/08-self-test-fix.patch
components/openvswitch/test/results-64.master
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openvswitch/Makefile	Mon Nov 16 16:49:19 2015 -0500
@@ -0,0 +1,105 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+
+#
+# Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+#
+
+include ../../make-rules/shared-macros.mk
+
+COMPONENT_NAME=		openvswitch
+COMPONENT_VERSION=	2.3.1
+COMPONENT_SRC=		$(COMPONENT_NAME)-$(COMPONENT_VERSION)
+COMPONENT_ARCHIVE=	$(COMPONENT_SRC).tar.gz
+COMPONENT_ARCHIVE_HASH=	\
+    sha256:d6d96e45fd9c070cc2696a4a09b4cc4b48dd7fc367c0455725d00f7daa343bf0
+COMPONENT_PROJECT_URL=  http://openvswitch.org/
+COMPONENT_ARCHIVE_URL=  $(COMPONENT_PROJECT_URL)/releases/$(COMPONENT_ARCHIVE)
+COMPONENT_BUGDB=	service/openvswitch
+IPS_COMPONENT_VERSION=	$(COMPONENT_VERSION) 
+
+TPNO=			21407
+
+include ../../make-rules/prep.mk
+include ../../make-rules/configure.mk
+include ../../make-rules/ips.mk
+include ../../make-rules/lint-libraries.mk
+
+PKG_PROTO_DIRS += $(COMPONENT_DIR)/files
+PKG_PROTO_DIRS += $(BUILD_DIR_64)
+
+CONFIGURE_ENV += CFLAGS="$(CFLAGS)"
+
+COMPONENT_PRE_CONFIGURE_ACTION += \
+    ($(LN) -fs $(COMPONENT_DIR)/files/include/solaris $(SOURCE_DIR)/include/solaris; \
+    $(LN) -fs $(COMPONENT_DIR)/files/lib/* $(SOURCE_DIR)/lib; \
+    cd $(SOURCE_DIR); $(SOURCE_DIR)/boot.sh)
+
+COMPILER =	gcc
+
+CONFIGURE_PREFIX	= /usr/lib/ovs
+
+CONFIGURE_OPTIONS	+= --mandir=/usr/share/man
+CONFIGURE_OPTIONS	+= --localstatedir=/var/run/ovs
+CONFIGURE_OPTIONS	+= --bindir=/usr/sbin
+CONFIGURE_OPTIONS	+= --sbindir=/usr/lib/ovs
+CONFIGURE_OPTIONS	+= --with-logdir=/var/log/ovs
+CONFIGURE_OPTIONS	+= --with-rundir=/var/run/ovs/
+CONFIGURE_OPTIONS	+= --with-dbdir=/var/lib/ovs/etc
+CONFIGURE_OPTIONS	+= --datarootdir=/usr/lib/ovs/share
+
+# Enable aslr for this component
+ASLR_MODE = $(ASLR_ENABLE)
+
+COMPONENT_PRE_BUILD_ACTION = \
+    ($(LN) -fs $(COMPONENT_DIR)/files/include/solaris $(SOURCE_DIR)/include/solaris)
+
+COMPONENT_TEST_TRANSFORMS += \
+    '-e "s|^tests/pki/test-req.pem.*||g" ' \
+    '-e "s|^tests/pki/test2-req.pem.*||g" ' \
+    '-e "s|^.*fingerprint.*||g" '
+
+COMPONENT_TEST_ENV += PATH="$(GNUBIN):$(PATH)"
+
+# common targets
+configure:	$(CONFIGURE_64)
+
+build:		$(BUILD_64)
+
+install:	$(INSTALL_64)
+
+test:		$(TEST_64)
+
+system-test:	$(SYSTEM_TESTS_NOT_IMPLEMENTED)
+
+BUILD_PKG_DEPENDENCIES =	$(BUILD_TOOLS)
+
+REQUIRED_PACKAGES += library/security/openssl
+REQUIRED_PACKAGES += runtime/python-27
+REQUIRED_PACKAGES += shell/ksh93
+REQUIRED_PACKAGES += system/core-os
+REQUIRED_PACKAGES += system/library
+REQUIRED_PACKAGES += system/library/math
+REQUIRED_PACKAGES += system/management/rad
+REQUIRED_PACKAGES += system/management/rad/client/rad-c
+REQUIRED_PACKAGES += system/management/rad/client/rad-python
+REQUIRED_PACKAGES += system/network
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openvswitch/files/include/solaris/automake.mk	Mon Nov 16 16:49:19 2015 -0500
@@ -0,0 +1,28 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+
+#
+# Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+#
+
+noinst_HEADERS += \
+	include/solaris/solarisdefs.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openvswitch/files/include/solaris/solarisdefs.h	Mon Nov 16 16:49:19 2015 -0500
@@ -0,0 +1,40 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ */
+
+#ifndef	SOLARISDEFS_H
+#define	SOLARISDEFS_H
+
+#include <sys/types.h>
+
+#define	u_int8_t uint8_t
+#define	u_int16_t uint16_t
+#define	u_int32_t uint32_t
+#define	u_int64_t uint64_t
+
+struct ip6_ext {
+	uint8_t  ip6e_nxt;
+	uint8_t  ip6e_len;
+};
+
+#endif /* SOLARISDEFS_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openvswitch/files/lib/dpif-solaris.c	Mon Nov 16 16:49:19 2015 -0500
@@ -0,0 +1,2465 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ */
+
+/*
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+#include "dpif-solaris.h"
+#include <fcntl.h>
+#include <strings.h>
+#include <unistd.h>
+#include "classifier.h"
+#include "dpif-provider.h"
+#include "dynamic-string.h"
+#include "netdev-solaris.h"
+#include "netdev-vport.h"
+#include "netlink.h"
+#include "odp-execute.h"
+#include "poll-loop.h"
+#include "shash.h"
+#include "socket-util.h"
+#include "sset.h"
+#include "vlog.h"
+#include <netpacket/packet.h>
+#include <zone.h>
+#include <libdllink.h>
+
+VLOG_DEFINE_THIS_MODULE(dpif_solaris);
+
+static kstat2_handle_t	dpif_khandle;
+static boolean_t	kstat2_handle_initialized = B_FALSE;
+static struct ovs_mutex	kstat_mutex = OVS_MUTEX_INITIALIZER;
+
+/* Datapath interface for the openvswitch Solaris kernel module. */
+struct dpif_solaris {
+	struct dpif dpif;
+	dladm_handle_t dh;
+	int dp_ifindex;	/* datapath id */
+	const struct dpif_class *class;
+	char *name;
+	atomic_flag destroyed;
+	struct ovs_refcount ref_cnt;
+
+	/* BRIDGES */
+	struct ovs_rwlock bridge_rwlock;
+	struct hmap bridges;
+
+	/* PORT */
+	struct ovs_rwlock port_rwlock;
+	struct hmap ports;
+
+	/* FLOW */
+	struct ovs_rwlock flow_rwlock;
+	struct hmap flows OVS_GUARDED;
+	struct classifier cls;		/* rule, flow & mask */
+
+	/* Upcall messages. */
+	struct ovs_rwlock upcall_lock;
+	bool recv_set;
+	int event_rfd;
+	int event_wfd;
+};
+
+static struct ovs_mutex dp_solaris_mutex = OVS_MUTEX_INITIALIZER;
+
+/* Contains all 'struct dpif_solaris's. */
+static struct shash dp_all_solaris OVS_GUARDED_BY(dp_solaris_mutex) =
+    SHASH_INITIALIZER(&dp_all_solaris);
+
+static struct vlog_rate_limit error_rl = VLOG_RATE_LIMIT_INIT(9999, 5);
+
+struct dpif_solaris_bridge {
+	char *physname;
+	struct hmap_node node;	/* Node in dpif_solaris's 'bridges'. */
+	struct hmap ports;
+	struct dpif_solaris_port *uplink_port;
+};
+
+struct dpif_solaris_port {
+	struct hmap_node node;		/* Node in dpif_solaris's 'ports'. */
+	struct hmap_node brnode;	/* Node in dpif_bridge's 'ports'. */
+	odp_port_t port_no;		/* OF port no of this port */
+	odp_port_t pf_port_no;		/* OF port no of the PF_PACKET socket */
+	char *type;			/* Port type as requested by user. */
+	char *name;
+	char *linkname;
+	enum ovs_vport_type vtype;
+	struct netdev *netdev;
+	struct dpif_solaris_bridge *bridge;
+	boolean_t is_uplink;
+
+	/* Receive the upcalls */
+	int upcall_fd;			/* PF_PACKET fd for MISS event */
+
+	/* Send the packet */
+	int xfd;			/* PF_PACKET to execute output action */
+};
+
+struct dpif_solaris_flow {
+	/* Packet classification. */
+	struct cls_rule cr;	/* Node in dpif_solaris's 'cls'. */
+
+	/* Hash table index by unmasked flow. */
+	struct hmap_node node;	/* Node in dpif_solaris's 'flows'. */
+	struct flow flow;	/* The flow that created this entry. */
+	char *physname;
+	char flowname[MAXUSERFLOWNAMELEN];
+};
+
+static void dpif_solaris_port_del__(struct dpif_solaris *dpif,
+    struct dpif_solaris_port *port)
+    OVS_REQ_WRLOCK(dpif->port_rwlock);
+static void dpif_solaris_destroy_channels(struct dpif_solaris *dpif)
+    OVS_REQ_WRLOCK(dpif->upcall_lock);
+static int dpif_solaris_refresh_port_channel(struct dpif_solaris *dpif,
+    struct dpif_solaris_port *port, const char *physname, boolean_t notify)
+    OVS_REQ_WRLOCK(dpif->port_rwlock);
+static int dpif_solaris_get_port_by_number(struct dpif_solaris *dpif,
+    odp_port_t port_no, struct dpif_solaris_port **portp)
+    OVS_REQ_RDLOCK(dpif->port_rwlock);
+static int dpif_solaris_get_uplink_port(struct dpif_solaris *dpif,
+    struct dpif_solaris_port **portp)
+    OVS_REQ_RDLOCK(dpif->port_rwlock);
+static void dpif_solaris_flow_remove(struct dpif_solaris *dpif,
+    struct dpif_solaris_flow *flow)
+    OVS_REQ_WRLOCK(dpif->flow_rwlock);
+static int dpif_solaris_flow_flush__(struct dpif_solaris *dpif);
+
+static struct dpif_solaris *
+dpif_solaris_cast(const struct dpif *dpif)
+{
+	dpif_assert_class(dpif, &dpif_solaris_class);
+	return (CONTAINER_OF(dpif, struct dpif_solaris, dpif));
+}
+
+static int
+dpif_solaris_enumerate(struct sset *all_dps)
+{
+	int error;
+
+	VLOG_DBG("dpif_solaris_enumerate");
+
+	error = solaris_init_rad();
+	if (error != 0)
+		return (error);
+
+	if (netdev_impl_etherstub_exists()) {
+		VLOG_DBG("dpif_solaris_enumerate ovs-system");
+		sset_add(all_dps, "ovs-system");
+	}
+	return (0);
+}
+
+static int
+dpif_solaris_open(const struct dpif_class *class, const char *name,
+    bool create, struct dpif **dpifp)
+{
+	struct dpif_solaris *dpif;
+	int error = 0;
+	boolean_t dp_exists;
+	dladm_status_t status;
+
+	VLOG_DBG("dpif_solaris_open class type %s name %s do %screate",
+	    class->type, name, create ? "" : "not ");
+
+	if (strcmp(name, "ovs-system") != 0)
+		return (ENODEV);
+
+	error = solaris_init_rad();
+	if (error != 0)
+		return (error);
+
+	ovs_mutex_lock(&dp_solaris_mutex);
+
+	dp_exists = netdev_impl_etherstub_exists();
+	dpif = shash_find_data(&dp_all_solaris, name);
+
+	/*
+	 * The same function is shared by ovs-vswitchd and other utilities as
+	 * they call into the same library. Note that for other utilities,
+	 * dpif does not exists at first, create it here if dp_exists indicate
+	 * the datapath already exists.
+	 */
+again:
+	if ((dp_exists && dpif == NULL) || (!dp_exists && create)) {
+		dladm_handle_t dh = NULL;
+
+		status = dladm_open(&dh);
+		error = solaris_dladm_status2error(status);
+		if (error != 0)
+			return (error);
+
+		if (!dp_exists) {
+			error = netdev_create_impl_etherstub();
+			if (error != 0) {
+				dladm_close(dh);
+				return (error);
+			}
+		}
+
+		dpif = xzalloc(sizeof (*dpif));
+		dpif->dh = dh;
+		dpif->dp_ifindex = getzoneid();
+		dpif->class = class;
+		dpif->name = xstrdup(name);
+		atomic_flag_clear(&dpif->destroyed);
+
+		/* UPCALL related */
+		ovs_rwlock_init(&dpif->upcall_lock);
+		dpif->recv_set = false;
+
+		/* uplink related */
+		ovs_rwlock_init(&dpif->bridge_rwlock);
+		hmap_init(&dpif->bridges);
+
+		/* port related */
+		ovs_rwlock_init(&dpif->port_rwlock);
+		hmap_init(&dpif->ports);
+		dpif->event_rfd = dpif->event_wfd = -1;
+
+		/* flow related */
+		ovs_rwlock_init(&dpif->flow_rwlock);
+		classifier_init(&dpif->cls, NULL);
+		hmap_init(&dpif->flows);
+
+		ovs_refcount_init(&dpif->ref_cnt);
+		shash_add(&dp_all_solaris, name, dpif);
+		if (dp_exists) {
+			ovs_refcount_ref(&dpif->ref_cnt);
+			goto again;
+		}
+	} else if (!dp_exists) {
+		error = ENODEV;
+	} else {
+		error = (dpif->class != class ? EOPNOTSUPP :
+		    create ? EEXIST : 0);
+	}
+
+	if (!error) {
+		ovs_refcount_ref(&dpif->ref_cnt);
+
+		/* Choose dp_ifindex to be used as netflow engine type and id */
+		dpif_init(&dpif->dpif, class, name, dpif->dp_ifindex,
+		    dpif->dp_ifindex);
+		*dpifp = &dpif->dpif;
+	}
+	ovs_mutex_unlock(&dp_solaris_mutex);
+	return (error);
+}
+
+/*
+ * Requires dp_netdev_mutex so that we can't get a new reference to 'dp'
+ * through the 'dp_netdevs' shash while freeing 'dp'.
+ */
+static void
+dp_solaris_free(struct dpif_solaris *dpif)
+    OVS_REQUIRES(dp_solaris_mutex)
+{
+	struct dpif_solaris_port *port, *next;
+
+	VLOG_DBG("dp_solaris_free %s", dpif->name);
+
+	shash_find_and_delete(&dp_all_solaris, dpif->name);
+	ovs_rwlock_wrlock(&dpif->port_rwlock);
+	HMAP_FOR_EACH_SAFE(port, next, node, &dpif->ports) {
+		dpif_solaris_port_del__(dpif, port);
+	}
+	ovs_rwlock_unlock(&dpif->port_rwlock);
+
+	dpif_solaris_flow_flush__(dpif);
+	classifier_destroy(&dpif->cls);
+
+	hmap_destroy(&dpif->bridges);
+	ovs_rwlock_destroy(&dpif->bridge_rwlock);
+
+	hmap_destroy(&dpif->ports);
+	ovs_rwlock_destroy(&dpif->port_rwlock);
+
+	hmap_destroy(&dpif->flows);
+	ovs_rwlock_destroy(&dpif->flow_rwlock);
+
+	(void) close(dpif->event_rfd);
+	(void) close(dpif->event_wfd);
+
+	free(dpif->name);
+	dladm_close(dpif->dh);
+	netdev_delete_impl_etherstub();
+	ovs_rwlock_destroy(&dpif->upcall_lock);
+	free(dpif);
+}
+
+static void
+dp_solaris_unref(struct dpif_solaris *dpif)
+{
+	if (dpif != NULL) {
+		/*
+		 * Take dp_solaris_mutex so that, if dpif->ref_cnt falls to
+		 * zero, we can't get a hold of 'dpif'.
+		 */
+		ovs_mutex_lock(&dp_solaris_mutex);
+
+		if (ovs_refcount_unref(&dpif->ref_cnt) == 2) {
+			dp_solaris_free(dpif);
+		}
+		ovs_mutex_unlock(&dp_solaris_mutex);
+	}
+}
+
+static void
+dpif_solaris_close(struct dpif *dpif_)
+{
+	struct dpif_solaris *dpif = dpif_solaris_cast(dpif_);
+
+	VLOG_DBG("dpif_solaris_close %s", dpif->name);
+
+	ovs_rwlock_wrlock(&dpif->upcall_lock);
+	dpif_solaris_destroy_channels(dpif);
+	ovs_rwlock_unlock(&dpif->upcall_lock);
+
+	dp_solaris_unref(dpif);
+}
+
+static int
+dpif_solaris_destroy(struct dpif *dpif_)
+{
+	struct dpif_solaris *dpif = dpif_solaris_cast(dpif_);
+	int cnt;
+
+	VLOG_DBG("dpif_solaris_destroy %s", dpif->name);
+	if (!atomic_flag_test_and_set(&dpif->destroyed)) {
+		cnt = ovs_refcount_unref(&dpif->ref_cnt);
+		if (cnt <= 2) {
+			OVS_NOT_REACHED();
+		}
+	}
+
+	return (0);
+}
+
+static int
+dpif_solaris_get_stats(const struct dpif *dpif_, struct dpif_dp_stats *stats)
+{
+	struct dpif_solaris		*dpif = dpif_solaris_cast(dpif_);
+	struct dpif_solaris_bridge	*bridge;
+	kstat2_status_t			stat;
+	kstat2_map_t			map;
+	char				kuri[1024];
+	char				kstat_name[MAXLINKNAMELEN];
+	char				*name;
+	zoneid_t			zid;
+	uint_t				instance;
+
+	bzero(stats, sizeof (struct dpif_dp_stats));
+	ovs_mutex_lock(&kstat_mutex);
+	if (!kstat2_handle_initialized) {
+		VLOG_DBG("dpif_solaris_get_stats initializing handle");
+		if (!kstat_handle_init(&dpif_khandle)) {
+			ovs_mutex_unlock(&kstat_mutex);
+			VLOG_DBG("dpif_solaris_get_stats: error initializing"
+			    " kstat handle");
+			return (-1);
+		}
+		kstat2_handle_initialized = B_TRUE;
+	} else if (!kstat_handle_update(dpif_khandle)) {
+		kstat_handle_close(&dpif_khandle);
+		kstat2_handle_initialized = B_FALSE;
+		ovs_mutex_unlock(&kstat_mutex);
+		VLOG_DBG("dpif_solaris_get_stats: error updating kstats");
+		return (-1);
+	}
+	ovs_rwlock_rdlock(&dpif->bridge_rwlock);
+	HMAP_FOR_EACH(bridge, node, &dpif->bridges) {
+		name = (char *)bridge->physname;
+		instance = 0;
+		if (strchr(bridge->physname, '/') != NULL) {
+			(void) solaris_dlparse_zonelinkname(bridge->physname,
+			    kstat_name, &zid);
+			name = kstat_name;
+			instance = zid;
+		}
+		(void) snprintf(kuri, sizeof (kuri), "kstat:/net/link/%s/%d",
+		    name, instance);
+		stat = kstat2_lookup_map(dpif_khandle, kuri, &map);
+
+		if (stat != KSTAT2_S_OK) {
+			ovs_rwlock_unlock(&dpif->bridge_rwlock);
+			ovs_mutex_unlock(&kstat_mutex);
+			VLOG_WARN("dpif_solaris_get_stats kstat_lookup of %s"
+			    " failed: %s", kuri, kstat2_status_string(stat));
+			return (-1);
+		}
+		stats->n_hit += (get_nvvt_int(map, "ipkthit") +
+		    get_nvvt_int(map, "opkthit"));
+		stats->n_missed += (get_nvvt_int(map, "ipktmiss") +
+		    get_nvvt_int(map, "opktmiss"));
+	}
+	ovs_rwlock_unlock(&dpif->bridge_rwlock);
+	ovs_mutex_unlock(&kstat_mutex);
+	stats->n_lost   = 0;
+	stats->n_flows  = solaris_flow_walk(NULL, NULL, B_TRUE, NULL);
+	stats->n_masks  = UINT32_MAX;
+	stats->n_mask_hit  = UINT64_MAX;
+	return (0);
+}
+
+static enum ovs_vport_type
+netdev_to_ovs_vport_type(const struct netdev *netdev)
+{
+	const char *type = netdev_get_type(netdev);
+
+	if (strcmp(type, "system") == 0) {
+		return (OVS_VPORT_TYPE_NETDEV);
+	} else if (strcmp(type, "internal") == 0) {
+		return (OVS_VPORT_TYPE_INTERNAL);
+	} else if (strcmp(type, "vxlan") == 0) {
+		return (OVS_VPORT_TYPE_VXLAN);
+	} else {
+		return (OVS_VPORT_TYPE_UNSPEC);
+	}
+}
+
+static int
+dpif_solaris_create_xsocket(struct dpif_solaris *dpif OVS_UNUSED,
+    struct dpif_solaris_port *port)
+{
+	int fd;
+	struct sockaddr_ll sll;
+	datalink_id_t linkid;
+	dladm_status_t status;
+	int error;
+
+	if (port->vtype != OVS_VPORT_TYPE_NETDEV &&
+	    port->vtype != OVS_VPORT_TYPE_VXLAN)
+		return (0);
+
+	if ((fd = socket(PF_PACKET, SOCK_RAW, ETH_P_ALL)) == -1) {
+		return (errno);
+	}
+
+	status = dladm_name2info(dpif->dh, port->linkname, &linkid,
+	    NULL, NULL, NULL);
+	error = solaris_dladm_status2error(status);
+	if (error != 0) {
+		(void) close(fd);
+		return (error);
+	}
+
+	(void) memset(&sll, 0, sizeof (sll));
+	sll.sll_family = AF_PACKET;
+	sll.sll_ifindex = linkid;
+	sll.sll_protocol = ETH_P_ALL;
+	if (bind(fd, (struct sockaddr *)&sll, sizeof (sll)) == -1) {
+		error = errno;
+		(void) close(fd);
+		return (error);
+	}
+	port->xfd = fd;
+	VLOG_DBG("dpif_solaris_create_xsocket %d on %s port_no: %d", fd,
+	    port->name, port->port_no);
+
+	return (0);
+}
+
+static boolean_t
+port_not_used(struct dpif_solaris *dpif, uint32_t port_no)
+    OVS_REQ_WRLOCK(dp->port_rwlock)
+{
+	struct dpif_solaris_port *port;
+	HMAP_FOR_EACH(port, node, &dpif->ports) {
+
+		if (port->port_no == port_no ||
+		    port->pf_port_no == port_no) {
+			return (true);
+		}
+	}
+	return (false);
+}
+
+/*
+ * Choose an unused, non-zero port number and return it on success.
+ * Return ODPP_NONE on failure.
+ * The current approach to choose unused port number is quite inefficient,
+ * need improvement. TBD
+ */
+static odp_port_t
+choose_port(struct dpif_solaris *dpif, uint32_t exclude_port_no)
+    OVS_REQ_WRLOCK(dp->port_rwlock)
+{
+	uint32_t port_no;
+
+	for (port_no = PORT_PF_PACKET_UPLINK + 1; port_no < OFPP_MAX;
+	    port_no++) {
+		if ((exclude_port_no == ODPP_NONE ||
+		    port_no != exclude_port_no) &&
+		    (!port_not_used(dpif, port_no))) {
+			return (u32_to_odp(port_no));
+		}
+	}
+
+	return (ODPP_NONE);
+}
+
+static struct dpif_solaris_bridge *
+dpif_solaris_lookup_bridge(const struct dpif_solaris *dpif,
+    const char *physname)
+    OVS_REQ_RDLOCK(dpif->bridge_rwlock)
+{
+	struct dpif_solaris_bridge *bridge;
+
+	HMAP_FOR_EACH(bridge, node, &dpif->bridges) {
+		if (strcmp(bridge->physname, physname) == 0) {
+			return (bridge);
+		}
+	}
+
+	return (NULL);
+}
+
+static struct dpif_solaris_bridge *
+dpif_solaris_bridge_add_port(struct dpif_solaris *dpif,
+    const char *physname, struct dpif_solaris_port *port)
+    OVS_REQ_WRLOCK(dpif->port_rwlock)
+{
+	struct dpif_solaris_bridge *bridge;
+
+	ovs_rwlock_wrlock(&dpif->bridge_rwlock);
+	VLOG_DBG("dpif_solaris_bridge_add_port adding port %d to uplink %s",
+	    port->port_no, physname);
+	bridge = dpif_solaris_lookup_bridge(dpif, physname);
+	if (bridge == NULL) {
+		VLOG_DBG("dpif_solaris_bridge_add_port creating bridge");
+		bridge = xzalloc(sizeof (*bridge));
+		bridge->physname = xstrdup(physname);
+		hmap_insert(&dpif->bridges, &bridge->node,
+		    hash_string(bridge->physname, 0));
+		hmap_init(&bridge->ports);
+	}
+	port->bridge = bridge;
+	hmap_insert(&bridge->ports, &port->brnode,
+	    hash_odp_port(port->port_no));
+	if (port->is_uplink)
+		bridge->uplink_port = port;
+
+	ovs_rwlock_unlock(&dpif->bridge_rwlock);
+	return (bridge);
+}
+
+static void
+dpif_solaris_bridge_del_port(struct dpif_solaris *dpif,
+    struct dpif_solaris_port *port)
+    OVS_REQ_WRLOCK(dpif->port_rwlock)
+{
+	struct dpif_solaris_bridge *bridge = port->bridge;
+
+	if (bridge == NULL) {
+		VLOG_DBG("dpif_solaris_bridge_del_port port %d not assigned "
+		    "to a bridge", port->port_no);
+		return;
+	}
+	VLOG_DBG("dpif_solaris_bridge_del_port deleting port %d from %s",
+	    port->port_no, bridge->physname);
+
+	ovs_rwlock_wrlock(&dpif->bridge_rwlock);
+	hmap_remove(&bridge->ports, &port->brnode);
+	if (port == bridge->uplink_port)
+		bridge->uplink_port = NULL;
+
+	if (hmap_is_empty(&bridge->ports)) {
+		VLOG_DBG("dpif_solaris_bridge_del_port destroying bridge");
+		hmap_destroy(&bridge->ports);
+		hmap_remove(&dpif->bridges, &bridge->node);
+		free(bridge->physname);
+		free(bridge);
+	}
+	ovs_rwlock_unlock(&dpif->bridge_rwlock);
+}
+
+static int
+dpif_solaris_port_add__(struct dpif_solaris *dpif, struct netdev *netdev,
+			odp_port_t *port_nop)
+{
+	char namebuf[NETDEV_VPORT_NAME_BUFSIZE];
+	const char *name = netdev_vport_get_dpif_port(netdev,
+	    namebuf, sizeof (namebuf));
+	const char *linkname = netdev_get_name(netdev);
+	const char *type = netdev_get_type(netdev);
+	enum ovs_vport_type vtype;
+	struct dpif_solaris_port *port = NULL;
+	char physname[MAXLINKNAMELEN];
+	char dlbuffer[DLADM_PROP_VAL_MAX];
+	int error = 0;
+	odp_port_t pf_port_no;
+	boolean_t is_uplink = false;
+
+	vtype = netdev_to_ovs_vport_type(netdev);
+	if (vtype == OVS_VPORT_TYPE_UNSPEC) {
+		VLOG_WARN_RL(&error_rl, "%s: cannot create port `%s' because "
+		    "it has unsupported type `%s'", dpif_name(&dpif->dpif),
+		    name, type);
+		return (EINVAL);
+	}
+
+	VLOG_DBG("dpif_solaris_port_add %s (%s) type %s port_no %d vtype %d",
+	    name, linkname, type, *port_nop, vtype);
+
+	if (vtype == OVS_VPORT_TYPE_NETDEV || vtype == OVS_VPORT_TYPE_VXLAN) {
+		error = solaris_get_dlclass(linkname, dlbuffer,
+		    sizeof (dlbuffer));
+		if (error != 0)
+			return (error);
+
+		if (solaris_is_uplink_class(dlbuffer)) {
+			if (strlcpy(physname, linkname, sizeof (physname)) >=
+			    sizeof (physname))
+				return (EINVAL);
+			VLOG_DBG("dpif_solaris_port_add primary port %s",
+			    name);
+			is_uplink = true;
+		} else {
+			error = solaris_get_dllower(linkname, dlbuffer,
+			    sizeof (dlbuffer));
+			if (error != 0)
+				return (error);
+			if (strlcpy(physname, dlbuffer, sizeof (physname)) >=
+			    sizeof (physname))
+				return (EINVAL);
+			VLOG_DBG("dpif_solaris_port_add non-primary port "
+			    "%s to %s", name, dlbuffer);
+		}
+	} else if (vtype == OVS_VPORT_TYPE_INTERNAL) {
+		VLOG_DBG("dpif_solaris_port_add adding internal port %s",
+		    name);
+	} else {
+		VLOG_DBG("dpif_solaris_port_add adding unknown type");
+		return (EINVAL);
+	}
+	ovs_rwlock_wrlock(&dpif->upcall_lock);
+	ovs_rwlock_wrlock(&dpif->port_rwlock);
+
+	if (*port_nop == ODPP_NONE) {
+		*port_nop = choose_port(dpif, ODPP_NONE);
+		if (*port_nop == ODPP_NONE) {
+			error = EFBIG;
+			goto fail;
+		}
+	}
+	pf_port_no = is_uplink ? PORT_PF_PACKET_UPLINK : choose_port(dpif,
+	    *port_nop);
+	if (pf_port_no == ODPP_NONE) {
+		error = EFBIG;
+		goto fail;
+	}
+	if (vtype == OVS_VPORT_TYPE_NETDEV || vtype == OVS_VPORT_TYPE_VXLAN) {
+		uint64_t u64 = (uint32_t)(*port_nop);
+
+		VLOG_DBG("set portno %d on %s", (uint32_t)(*port_nop),
+		    linkname);
+		if ((error = solaris_set_dlprop_ulong(linkname, "ofport",
+		    &u64)) != 0) {
+			VLOG_ERR("set portno %d on %s failed: %s",
+			    (uint32_t)(*port_nop), linkname,
+			    ovs_strerror(error));
+			goto fail;
+		}
+	}
+
+	port = xzalloc(sizeof (*port));
+	port->port_no = *port_nop;
+	port->pf_port_no = pf_port_no;
+	port->name = xstrdup(name);
+	port->linkname = xstrdup(linkname);
+	port->type = xstrdup(type);
+	port->vtype = vtype;
+	port->netdev = netdev;
+	port->upcall_fd = -1;
+	port->xfd = -1;
+	port->is_uplink = is_uplink;
+
+	/* Only create the TX PF_SOCKET on the physical link */
+	if (is_uplink) {
+		error = dpif_solaris_create_xsocket(dpif, port);
+		if (error != 0) {
+			VLOG_ERR("dpif_solaris_create_xsocket on %s failed: %s",
+			    port->linkname, ovs_strerror(error));
+			goto fail;
+		}
+	}
+
+	error = dpif_solaris_refresh_port_channel(dpif, port, physname, true);
+	if (error != 0) {
+		VLOG_ERR("dpif_solaris_refresh_port_channel on %s failed: %s",
+		    linkname, ovs_strerror(error));
+		goto fail;
+	}
+
+	if (vtype == OVS_VPORT_TYPE_NETDEV || vtype == OVS_VPORT_TYPE_VXLAN)
+		dpif_solaris_bridge_add_port(dpif, physname, port);
+	hmap_insert(&dpif->ports, &port->node, hash_odp_port(port->port_no));
+
+	ovs_rwlock_unlock(&dpif->port_rwlock);
+	ovs_rwlock_unlock(&dpif->upcall_lock);
+	return (0);
+fail:
+	if (port != NULL) {
+		if (port->xfd != -1)
+			(void) close(port->xfd);
+		if (port->upcall_fd != -1) {
+			(void) setsockopt(port->upcall_fd, SOL_PACKET,
+			    PACKET_REM_OF_DEFFLOW, NULL, 0);
+			(void) close(port->upcall_fd);
+		}
+		free(port->name);
+		free(port->linkname);
+		free(port->type);
+		free(port);
+	}
+	if (vtype == OVS_VPORT_TYPE_NETDEV || vtype == OVS_VPORT_TYPE_VXLAN) {
+		VLOG_DBG("reset portno on %s", linkname);
+		(void) solaris_set_dlprop_ulong(linkname, "ofport", NULL);
+	}
+	ovs_rwlock_unlock(&dpif->port_rwlock);
+	ovs_rwlock_unlock(&dpif->upcall_lock);
+	return (error);
+}
+
+static int
+dpif_solaris_port_add(struct dpif *dpif_, struct netdev *netdev,
+    odp_port_t *port_nop)
+{
+	struct dpif_solaris *dpif = dpif_solaris_cast(dpif_);
+	int error;
+
+	error = dpif_solaris_port_add__(dpif, netdev, port_nop);
+	return (error);
+}
+
+static int
+dpif_solaris_get_port_by_number(struct dpif_solaris *dpif, odp_port_t port_no,
+    struct dpif_solaris_port **portp)
+    OVS_REQ_RDLOCK(dpif->port_rwlock)
+{
+	struct dpif_solaris_port *port;
+
+	if (port_no == ODPP_NONE) {
+		*portp = NULL;
+		return (EINVAL);
+	}
+
+	HMAP_FOR_EACH_WITH_HASH(port, node,
+	    hash_odp_port(port_no), &dpif->ports) {
+		if (port->port_no == port_no) {
+			*portp = port;
+			return (0);
+		}
+	}
+
+	*portp = NULL;
+	return (ENOENT);
+}
+
+static int
+dpif_solaris_get_uplink_port(struct dpif_solaris *dpif,
+    struct dpif_solaris_port **portp)
+    OVS_REQ_RDLOCK(dpif->port_rwlock)
+{
+	struct dpif_solaris_port *port;
+
+	HMAP_FOR_EACH(port, node, &dpif->ports) {
+		if (port->is_uplink) {
+			*portp = port;
+			return (0);
+		}
+	}
+
+	*portp = NULL;
+	return (ENOENT);
+}
+
+static void
+dpif_solaris_port_del__(struct dpif_solaris *dpif,
+    struct dpif_solaris_port *port)
+    OVS_REQ_WRLOCK(dpif->port_rwlock)
+{
+	VLOG_DBG("dpif_solaris_port_del__ port %s # %d", port->name,
+	    port->port_no);
+
+	hmap_remove(&dpif->ports, &port->node);
+	dpif_solaris_bridge_del_port(dpif, port);
+
+	if (port->xfd != -1)
+		(void) close(port->xfd);
+	if (port->upcall_fd != -1) {
+		(void) setsockopt(port->upcall_fd, SOL_PACKET,
+		    PACKET_REM_OF_DEFFLOW, NULL, 0);
+		(void) close(port->upcall_fd);
+	}
+	if (port->vtype == OVS_VPORT_TYPE_NETDEV || port->vtype ==
+	    OVS_VPORT_TYPE_VXLAN) {
+			VLOG_DBG("1.reset portno on %s", port->linkname);
+			(void) solaris_set_dlprop_ulong(port->linkname,
+			    "ofport", NULL);
+		if (port->is_uplink) {
+			VLOG_DBG("dpif_solaris_port_del__ primary port "
+			    "%s", port->name);
+		}
+	}
+	VLOG_DBG("dpif_solaris_port_del %s close xfd %d upcall_fd %d",
+	    port->name, port->xfd, port->upcall_fd);
+
+	free(port->type);
+	free(port->name);
+	free(port->linkname);
+	free(port);
+}
+
+static int
+dpif_solaris_port_del(struct dpif *dpif_, odp_port_t port_no)
+{
+	struct dpif_solaris *dpif = dpif_solaris_cast(dpif_);
+	struct dpif_solaris_port *port;
+	int error;
+
+	VLOG_DBG("dpif_solaris_port_del port # %d", port_no);
+
+	if (port_no == ODPP_LOCAL) {
+		VLOG_ERR("dpif_solaris_port_del invalid port # %d", port_no);
+		return (EINVAL);
+	}
+
+	ovs_rwlock_wrlock(&dpif->port_rwlock);
+	error = dpif_solaris_get_port_by_number(dpif, port_no, &port);
+	if (error) {
+		ovs_rwlock_unlock(&dpif->port_rwlock);
+		VLOG_ERR("dpif_solaris_port_del port # %d failed %d", port_no,
+		    error);
+		return (error);
+	}
+	dpif_solaris_port_del__(dpif, port);
+	ovs_rwlock_unlock(&dpif->port_rwlock);
+	return (0);
+}
+
+static int
+dpif_solaris_port_query_by_number(const struct dpif *dpif_, odp_port_t port_no,
+    struct dpif_port *dpif_port)
+{
+	struct dpif_solaris *dpif = dpif_solaris_cast(dpif_);
+	struct dpif_solaris_port *port = NULL;
+	int error;
+
+	ovs_rwlock_rdlock(&dpif->port_rwlock);
+	error = dpif_solaris_get_port_by_number(dpif, port_no, &port);
+	ovs_rwlock_unlock(&dpif->port_rwlock);
+	if (!error && dpif_port) {
+		dpif_port->name = xstrdup(port->name);
+		dpif_port->type = xstrdup(port->type);
+		dpif_port->port_no = port->port_no;
+	}
+	return (error);
+}
+
+static int
+dpif_solaris_port_query_by_name(const struct dpif *dpif_, const char *devname,
+				struct dpif_port *dpif_port)
+{
+	struct dpif_solaris *dpif = dpif_solaris_cast(dpif_);
+	struct dpif_solaris_port *port = NULL;
+
+	ovs_rwlock_wrlock(&dpif->port_rwlock);
+	HMAP_FOR_EACH(port, node, &dpif->ports) {
+		if (strcmp(port->name, devname) == 0) {
+			if (dpif_port) {
+				dpif_port->name = xstrdup(devname);
+				dpif_port->type = xstrdup(port->type);
+				dpif_port->port_no = port->port_no;
+			}
+			ovs_rwlock_unlock(&dpif->port_rwlock);
+			return (0);
+		}
+	}
+	ovs_rwlock_unlock(&dpif->port_rwlock);
+	return (ENOENT);
+}
+
+static int
+dpif_solaris_configure_bridge_port(const struct dpif *dpif_,
+    const char *devname)
+{
+	struct dpif_solaris *dpif = dpif_solaris_cast(dpif_);
+	struct dpif_solaris_port *port = NULL;
+	char dlbuffer[DLADM_PROP_VAL_MAX];
+	char physname[MAXLINKNAMELEN];
+	uint64_t u64;
+	int error = 0;
+
+	VLOG_DBG("dpif_solaris_configure_bridge_port %s", devname);
+
+	ovs_rwlock_wrlock(&dpif->port_rwlock);
+	HMAP_FOR_EACH(port, node, &dpif->ports) {
+		if (strcmp(port->name, devname) == 0) {
+			error = solaris_get_dllower(devname, dlbuffer,
+			    sizeof (dlbuffer));
+			if (error != 0) {
+				VLOG_ERR("failed to get lowerlink: %s",
+				    ovs_strerror(error));
+				goto out;
+			}
+			if (strlcpy(physname, dlbuffer, sizeof (physname)) >=
+			    sizeof (physname)) {
+				VLOG_ERR("invalid lowerlink size");
+				error = EINVAL;
+				goto out;
+			}
+
+			u64 = (uint32_t)(port->port_no);
+			VLOG_DBG("set portno %d on %s",
+			    (uint32_t)(port->port_no), devname);
+			error = solaris_set_dlprop_ulong(devname, "ofport",
+			    &u64);
+			if (error != 0) {
+				VLOG_ERR("set portno %d on %s failed: %s",
+				    (uint32_t)(port->port_no), devname,
+				    ovs_strerror(error));
+				goto out;
+			}
+			VLOG_DBG("dpif_solaris_port_add internal port "
+			    "%s to %s", devname, physname);
+			(void) dpif_solaris_bridge_add_port(dpif, physname,
+			    port);
+			goto out;
+		}
+	}
+	error = ENOENT;
+
+out:
+	ovs_rwlock_unlock(&dpif->port_rwlock);
+	return (error);
+}
+
+struct dpif_solaris_port_state {
+	struct hmap ports;
+	struct ovs_rwlock port_rwlock;
+	uint32_t bucket;
+	uint32_t offset;
+	char *name;
+};
+
+static void
+port_dump_start(void *arg, const char *name, char *type, odp_port_t portno)
+{
+	struct dpif_solaris_port_state *state = arg;
+	struct dpif_solaris_port *port;
+
+	port = xzalloc(sizeof (struct dpif_solaris_port));
+	port->name = xstrdup(name);
+	port->type = type;
+	port->port_no = portno;
+	ovs_rwlock_wrlock(&state->port_rwlock);
+	hmap_insert(&state->ports, &port->node, hash_odp_port(port->port_no));
+	ovs_rwlock_unlock(&state->port_rwlock);
+}
+
+static int
+dpif_solaris_port_dump_start(const struct dpif *dpif_ OVS_UNUSED,
+    void **statep)
+{
+	struct dpif_solaris_port_state *state;
+
+	*statep = state = xzalloc(sizeof (struct dpif_solaris_port_state));
+	hmap_init(&state->ports);
+	ovs_rwlock_init(&state->port_rwlock);
+	solaris_port_walk(state, port_dump_start);
+	return (0);
+}
+
+static int
+dpif_solaris_port_dump_next(const struct dpif *dpif_ OVS_UNUSED, void *state_,
+    struct dpif_port *dpif_port)
+{
+	struct dpif_solaris_port_state *state = state_;
+	struct hmap_node *node;
+	int retval;
+
+	ovs_rwlock_wrlock(&state->port_rwlock);
+	node = hmap_at_position(&state->ports, &state->bucket, &state->offset);
+	if (node) {
+		struct dpif_solaris_port *port;
+
+		port = CONTAINER_OF(node, struct dpif_solaris_port, node);
+
+		free(state->name);
+		state->name = xstrdup(port->name);
+		dpif_port->name = state->name;
+		dpif_port->type = port->type;
+		dpif_port->port_no = port->port_no;
+		retval = 0;
+	} else {
+		retval = EOF;
+	}
+	ovs_rwlock_unlock(&state->port_rwlock);
+
+	return (retval);
+}
+
+static int
+dpif_solaris_port_dump_done(const struct dpif *dpif_ OVS_UNUSED, void *state_)
+{
+	struct dpif_solaris_port_state *state = state_;
+	struct dpif_solaris_port *port, *next;
+
+	ovs_rwlock_wrlock(&state->port_rwlock);
+	HMAP_FOR_EACH_SAFE(port, next, node, &state->ports) {
+		free(port->name);
+		hmap_remove(&state->ports, &port->node);
+	}
+	ovs_rwlock_unlock(&state->port_rwlock);
+	hmap_destroy(&state->ports);
+	ovs_rwlock_destroy(&state->port_rwlock);
+	free(state->name);
+	free(state);
+	return (0);
+}
+
+static int
+dpif_solaris_port_poll(const struct dpif *dpif_ OVS_UNUSED,
+    char **devnamep OVS_UNUSED)
+{
+	return (EAGAIN);
+}
+
+static void
+dpif_solaris_port_poll_wait(const struct dpif *dpif_ OVS_UNUSED)
+{
+}
+
+static int
+dpif_solaris_key_to_flow(const struct nlattr *key, size_t key_len,
+    struct flow *f)
+{
+	if (odp_flow_key_to_flow(key, key_len, f)) {
+		/*
+		 * This should not happen: it indicates that
+		 * odp_flow_key_from_flow() and odp_flow_key_to_flow()
+		 * disagree on the acceptable form of a flow.
+		 * Log the problem as an error, with enough details to enable
+		 * debugging.
+		 */
+		static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+
+		if (!VLOG_DROP_ERR(&rl)) {
+			struct ds s;
+
+			ds_init(&s);
+
+			odp_flow_format(key, key_len, NULL, 0, NULL, &s, true);
+			VLOG_ERR("internal error parsing flow key %s",
+			    ds_cstr(&s));
+			ds_destroy(&s);
+		}
+
+		return (EINVAL);
+	}
+	return (0);
+}
+
+static int
+dpif_solaris_key_to_mask(const struct nlattr *key, uint32_t key_len,
+    const struct nlattr *mask_key, uint32_t mask_key_len,
+    const struct flow *f, struct flow *m)
+{
+	enum mf_field_id id;
+	enum odp_key_fitness fitness;
+
+	/*
+	 * Force unwildcard the in_port.
+	 * We need to do this even in the case where we unwildcard "everything"
+	 * above because "everything" only includes the 16-bit OpenFlow port
+	 * number mask->in_port.ofp_port, which only covers half of the 32-bit
+	 * datapath port number mask->in_port.odp_port.
+	 */
+	m->in_port.odp_port = ODPP_NONE;
+
+	if (!mask_key_len) {
+		/*
+		 * No mask key, unwildcard everything except fields whose
+		 * prerequisities are not met.
+		 */
+		memset(m, 0x0, sizeof (*m));
+
+		for (id = 0; id < MFF_N_IDS; ++id) {
+			/* Skip registers and metadata. */
+			if (!(id >= MFF_REG0 && id < MFF_REG0 + FLOW_N_REGS) &&
+			    id != MFF_METADATA) {
+				const struct mf_field *mf = mf_from_id(id);
+				if (mf_are_prereqs_ok(mf, f)) {
+					mf_mask_field(mf, m);
+				}
+			}
+		}
+		return (0);
+	}
+
+	fitness = odp_flow_key_to_mask(mask_key, mask_key_len, m, f);
+	if (fitness) {
+		/*
+		 * This should not happen: it indicates that
+		 * odp_flow_key_from_mask() and odp_flow_key_to_mask()
+		 * disagree on the acceptable form of a mask.
+		 * Log the problem as an error, with enough details to
+		 * enable debugging.
+		 */
+		static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+
+		if (!VLOG_DROP_ERR(&rl)) {
+			struct ds s;
+
+			ds_init(&s);
+			odp_flow_format(key, key_len, mask_key, mask_key_len,
+			    NULL, &s, true);
+			VLOG_ERR("internal error parsing flow mask %s (%s)",
+			    ds_cstr(&s), odp_key_fitness_to_string(fitness));
+			ds_destroy(&s);
+		}
+
+		return (EINVAL);
+	}
+
+	return (0);
+}
+
+static struct dpif_solaris_flow *
+dpif_solaris_flow_cast(const struct cls_rule *cr)
+{
+	return (cr ? CONTAINER_OF(cr, struct dpif_solaris_flow, cr) : NULL);
+}
+
+static struct dpif_solaris_flow *
+dpif_solaris_lookup_flow(const struct dpif_solaris *dpif,
+    const struct miniflow *key)
+    OVS_EXCLUDED(dpif->cls.rwlock)
+{
+	struct dpif_solaris_flow *solaris_flow;
+	struct cls_rule *rule;
+
+	fat_rwlock_rdlock(&dpif->cls.rwlock);
+	rule = classifier_lookup_miniflow_first(&dpif->cls, key);
+	solaris_flow = dpif_solaris_flow_cast(rule);
+	fat_rwlock_unlock(&dpif->cls.rwlock);
+
+	return (solaris_flow);
+}
+
+static struct dpif_solaris_flow *
+dpif_solaris_find_flow(const struct dpif_solaris *dpif, const struct flow *flow)
+{
+	struct dpif_solaris_flow *solaris_flow;
+
+	HMAP_FOR_EACH_WITH_HASH(solaris_flow, node, flow_hash(flow, 0),
+	    &dpif->flows) {
+		if (flow_equal(&solaris_flow->flow, flow)) {
+			return (solaris_flow);
+		}
+	}
+
+	return (NULL);
+}
+
+static struct dpif_solaris_flow *
+dpif_solaris_flow_add(struct dpif_solaris *dpif, const char *physname,
+    struct flow *flow, const struct flow_wildcards *wc)
+    OVS_REQ_WRLOCK(dpif->flow_rwlock)
+{
+	struct dpif_solaris_flow *solaris_flow;
+	struct match match;
+
+	solaris_flow = xzalloc(sizeof (*solaris_flow));
+
+	solaris_flow->physname = xstrdup(physname);
+	(void) snprintf(solaris_flow->flowname, MAXUSERFLOWNAMELEN,
+	    "%p.sys.of", (void *)solaris_flow);
+	solaris_flow->flow = *flow;
+	match_init(&match, flow, wc);
+	fat_rwlock_wrlock(&dpif->cls.rwlock);
+	cls_rule_init(&solaris_flow->cr, &match, 0x8001);
+	hmap_insert(&dpif->flows, &solaris_flow->node, flow_hash(flow, 0));
+	classifier_insert(&dpif->cls, &solaris_flow->cr);
+	fat_rwlock_unlock(&dpif->cls.rwlock);
+	return (solaris_flow);
+}
+
+static void
+dpif_solaris_flow_remove(struct dpif_solaris *dpif,
+    struct dpif_solaris_flow *solaris_flow) OVS_REQ_WRLOCK(dpif->flow_rwlock)
+{
+	struct cls_rule *cr;
+	struct hmap_node *node;
+
+	fat_rwlock_wrlock(&dpif->cls.rwlock);
+	cr = &solaris_flow->cr;
+	node = CONST_CAST(struct hmap_node *, &solaris_flow->node);
+	classifier_remove(&dpif->cls, cr);
+	hmap_remove(&dpif->flows, node);
+	cls_rule_destroy(CONST_CAST(struct cls_rule *, cr));
+	fat_rwlock_unlock(&dpif->cls.rwlock);
+	free(solaris_flow->physname);
+	free(solaris_flow);
+}
+
+static int
+dpif_solaris_get_flowstats(char *flowname, struct dpif_flow_stats *stats)
+{
+	uint64_t npackets, nbytes, lastused;
+	int error;
+
+	bzero(stats, sizeof (*stats));
+	error = solaris_get_flowstats(flowname, &npackets, &nbytes, &lastused);
+	if (error == 0) {
+		stats->n_packets = npackets;
+		stats->n_bytes = nbytes;
+		stats->used = lastused / 1000000;
+	}
+	return (error);
+}
+
+static int
+dpif_solaris_flow_get(const struct dpif *dpif_,
+    const struct nlattr *key, size_t key_len,
+    struct ofpbuf **bufp,
+    struct nlattr **maskp, size_t *mask_len,
+    struct nlattr **actionsp, size_t *actions_len,
+    struct dpif_flow_stats *stats)
+{
+	const struct dpif_solaris *dpif = dpif_solaris_cast(dpif_);
+	struct flow f, m;
+	struct dpif_solaris_flow *solaris_flow;
+	char flowname[MAXUSERFLOWNAMELEN];
+	struct ofpbuf actions;
+	int error;
+
+	VLOG_DBG("dpif_solaris_flow_get");
+
+	error = dpif_solaris_key_to_flow(key, key_len, &f);
+	if (error) {
+		VLOG_ERR("dpif_solaris_key_to_flow failed %d", error);
+		return (error);
+	}
+
+	ovs_rwlock_rdlock(&dpif->flow_rwlock);
+	solaris_flow = dpif_solaris_find_flow(dpif, &f);
+	if (solaris_flow == NULL) {
+		ovs_rwlock_unlock(&dpif->flow_rwlock);
+		error = ENOENT;
+		VLOG_ERR("dpif_solaris_flow_get failed %d", error);
+		return (error);
+	}
+	(void) strlcpy(flowname, solaris_flow->flowname, MAXUSERFLOWNAMELEN);
+	error = solaris_get_flowattr(flowname, &f, &m);
+	if (error) {
+		VLOG_ERR("solaris_get_flowattr failed %d", error);
+		ovs_rwlock_unlock(&dpif->flow_rwlock);
+		return (error);
+	}
+
+	ovs_rwlock_unlock(&dpif->flow_rwlock);
+
+	/* Allocate buffer big enough for everything. */
+	*bufp = ofpbuf_new(4096);
+	ofpbuf_init(*bufp, 4096);
+
+	/* Mask. */
+	odp_flow_key_from_mask(*bufp, &m, &f, m.in_port.odp_port,
+	    SIZE_MAX);
+	*maskp = ofpbuf_data(*bufp);
+	*mask_len = ofpbuf_size(*bufp);
+
+	/* Actions. */
+	ofpbuf_init(&actions, 0);
+	(void) solaris_get_flowaction(flowname, &actions);
+	if (ofpbuf_size(&actions) == 0) {
+		*actionsp = NULL;
+		*actions_len = 0;
+	} else {
+		*actionsp = ofpbuf_put(*bufp, ofpbuf_data(&actions),
+		    ofpbuf_size(&actions));
+		*actions_len = ofpbuf_size(&actions);
+	}
+	ofpbuf_uninit(&actions);
+
+	/* Stats. */
+	error = dpif_solaris_get_flowstats(flowname, stats);
+	return (0);
+}
+
+int
+dpif_solaris_get_priority_details(void *cookie, odp_port_t outport,
+    uint_t queueid, struct smap *details)
+{
+	struct dpif_solaris		*dpif = (struct dpif_solaris *)cookie;
+	struct dpif_solaris_port	*port = NULL;
+	int				error = 0;
+	struct netdev			*netdev;
+
+	VLOG_DBG("dpif_solaris_get_priority_details: "
+	" Port : %d, Queue: %d\n", outport, queueid);
+
+	ovs_rwlock_wrlock(&dpif->port_rwlock);
+	error = dpif_solaris_get_port_by_number(dpif, outport, &port);
+	ovs_rwlock_unlock(&dpif->port_rwlock);
+	if (error != 0) {
+		VLOG_DBG("dpif_solaris_get_priority_details: "
+		"Error getting port %d\n", outport);
+		return (error);
+	}
+
+	netdev = port->netdev;
+	error = netdev_get_queue(netdev, queueid, details);
+	if (error != 0) {
+		VLOG_DBG("dpif_solaris_get_priority_details: "
+		" Error getting queue %d\n", queueid);
+		return (error);
+	}
+	VLOG_DBG("dpif_solaris_get_priority_details: done");
+	return (0);
+}
+
+void
+dpif_log(int err, const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	if (err == 0)
+		vlog_valist(THIS_MODULE, VLL_DBG, fmt, ap);
+	else
+		vlog_valist(THIS_MODULE, VLL_ERR, fmt, ap);
+	va_end(ap);
+}
+
+static int
+dpif_solaris_flow_put(struct dpif *dpif_, const struct dpif_flow_put *put)
+{
+	struct dpif_solaris *dpif = dpif_solaris_cast(dpif_);
+	struct flow f;
+	struct flow_wildcards wc;
+	struct miniflow miniflow;
+	struct dpif_solaris_flow *solaris_flow;
+	char flowname[MAXUSERFLOWNAMELEN];
+	int error;
+	struct ds fs, as;
+
+	VLOG_DBG("dpif_solaris_flow_put");
+
+	ds_init(&fs);
+	ds_init(&as);
+	odp_flow_format(put->key, put->key_len, put->mask,
+	    put->mask_len, NULL, &fs, true);
+	ds_put_cstr(&as, ", actions:");
+	format_odp_actions(&as, put->actions, put->actions_len);
+
+	error = dpif_solaris_key_to_flow(put->key, put->key_len, &f);
+	if (error) {
+		VLOG_ERR("dpif_solaris_key_to_flow failed %d", error);
+		goto done;
+	}
+	error = dpif_solaris_key_to_mask(put->key, put->key_len, put->mask,
+	    put->mask_len, &f, &wc.masks);
+	if (error) {
+		VLOG_ERR("dpif_solaris_key_to_mask failed %d", error);
+		goto done;
+	}
+
+	miniflow_init(&miniflow, &f);
+
+	ovs_rwlock_wrlock(&dpif->flow_rwlock);
+	solaris_flow = dpif_solaris_lookup_flow(dpif, &miniflow);
+	miniflow_destroy(&miniflow);
+
+	if (solaris_flow == NULL) {
+		if (put->flags & DPIF_FP_CREATE) {
+			struct dpif_solaris_port	*port = NULL;
+			odp_port_t			inport;
+
+			inport = f.in_port.odp_port;
+			ovs_rwlock_rdlock(&dpif->port_rwlock);
+			error = dpif_solaris_get_port_by_number(dpif, inport,
+			    &port);
+			ovs_rwlock_unlock(&dpif->port_rwlock);
+			if (error == 0) {
+				solaris_flow = dpif_solaris_flow_add(dpif,
+				    port->bridge->physname, &f, &wc);
+				(void) strlcpy(flowname,
+				    solaris_flow->flowname,
+				    MAXUSERFLOWNAMELEN);
+				VLOG_DBG("dpif_solaris_flow_put %s mask %s "
+				    "actions %s", flowname, ds_cstr(&fs),
+				    ds_cstr(&as));
+				/*
+				 * workaround to passing a struct dpif to
+				 * solaris_add_flow adding dpif_provider.h to
+				 * util-solaris brings up a conflict in
+				 * Solaris's list and the OVS list
+				 * implementations.
+				 */
+				error = solaris_add_flow((void *)dpif,
+				    port->bridge->physname, flowname, &f,
+				    &wc.masks, put->actions, put->actions_len);
+				if (error == 0) {
+					VLOG_DBG("dpif_solaris_flow_put "
+					    "solaris_add_flow %s on %s succeed",
+					    flowname, port->bridge->physname);
+				} else {
+					VLOG_ERR("dpif_solaris_flow_put "
+					    "solaris_add_flow %s on %s failed "
+					    "%d", flowname,
+					    port->bridge->physname,
+					    error);
+					dpif_solaris_flow_remove(dpif,
+					    solaris_flow);
+				}
+			} else {
+				VLOG_DBG("dpif_solaris_flow_put(): "
+				    "Error getting inport %d\n", inport);
+			}
+		} else {
+			VLOG_ERR("dpif_solaris_flow_put mask %s "
+			    "actions %s not found", ds_cstr(&fs),
+			    ds_cstr(&as));
+			error = ENOENT;
+		}
+	} else {
+		VLOG_DBG("dpif_solaris_flow_put exsting flow %s found for"
+		    " mask %s actions %s", solaris_flow->flowname,
+		    ds_cstr(&fs), ds_cstr(&as));
+		if ((put->flags & DPIF_FP_MODIFY) &&
+		    flow_equal(&f, &solaris_flow->flow)) {
+			(void) strlcpy(flowname, solaris_flow->flowname,
+			    MAXUSERFLOWNAMELEN);
+			error = solaris_modify_flow((void *)dpif,
+			    flowname, put->actions, put->actions_len);
+			if (error == 0) {
+				VLOG_DBG("dpif_solaris_flow_put "
+				    "exsting flow %s found and updated,"
+				    "for mask %s actions %s", flowname,
+				    ds_cstr(&fs), ds_cstr(&as));
+			} else {
+				VLOG_ERR("dpif_solaris_flow_put "
+				    "exsting flow %s found but update "
+				    "failed %d, for mask %s actions %s",
+				    flowname, error, ds_cstr(&fs),
+				    ds_cstr(&as));
+			}
+		} else if (put->flags & DPIF_FP_CREATE) {
+			VLOG_DBG("dpif_solaris_flow_put existing flow "
+			    "%s already exists for mask %s actions %s",
+			    solaris_flow->flowname, ds_cstr(&fs),
+			    ds_cstr(&as));
+			error = EEXIST;
+		} else {
+			/*
+			 * Overlapping flow. (Flow & Mask) matches but flow
+			 * itself does not match
+			 */
+			VLOG_ERR("dpif_solaris_flow_put overlapped "
+			    "with existing flow %s, for mask %s actions %s",
+			    solaris_flow->flowname, ds_cstr(&fs),
+			    ds_cstr(&as));
+			error = EINVAL;
+		}
+	}
+
+	if (error == 0) {
+		if (put->stats) {
+			if (dpif_solaris_get_flowstats(flowname,
+			    put->stats) == 0) {
+				VLOG_DBG("dpif_solaris_flow_put "
+				    "get_flowstats %s succeed", flowname);
+			} else {
+				VLOG_ERR("dpif_solaris_flow_put "
+				    "get_flowstats %s failed", flowname);
+			}
+		}
+		/*
+		 * If DPIF_FP_MODIFY and DPIF_FP_ZERO_STATS is set in
+		 * put->flags, we should zero out the statistics of the
+		 * given flow. This is currently not supported, and so far,
+		 * we don't see any problem yet.
+		 */
+	}
+	ovs_rwlock_unlock(&dpif->flow_rwlock);
+done:
+	ds_destroy(&fs);
+	ds_destroy(&as);
+	return (error);
+}
+
+static int
+dpif_solaris_flow_del(struct dpif *dpif_, const struct dpif_flow_del *del)
+{
+	struct dpif_solaris *dpif = dpif_solaris_cast(dpif_);
+	struct dpif_solaris_flow *solaris_flow;
+	char flowname[MAXUSERFLOWNAMELEN];
+	char *physname;
+	struct flow f;
+	int error;
+	struct ds fs;
+
+	ds_init(&fs);
+	odp_flow_format(del->key, del->key_len, NULL, 0, NULL, &fs, true);
+	VLOG_DBG("dpif_solaris_flow_del %s", ds_cstr(&fs));
+	ds_destroy(&fs);
+
+	error = dpif_solaris_key_to_flow(del->key, del->key_len, &f);
+	if (error) {
+		VLOG_ERR("dpif_solaris_key_to_flow failed %d", error);
+		return (error);
+	}
+
+	ovs_rwlock_wrlock(&dpif->flow_rwlock);
+	solaris_flow = dpif_solaris_find_flow(dpif, &f);
+	if (solaris_flow == NULL) {
+		ovs_rwlock_unlock(&dpif->flow_rwlock);
+		error = ENOENT;
+		VLOG_ERR("dpif_solaris_flow_del failed %d", error);
+		return (error);
+	}
+	(void) strlcpy(flowname, solaris_flow->flowname, MAXUSERFLOWNAMELEN);
+	physname = xstrdup(solaris_flow->physname);
+	dpif_solaris_flow_remove(dpif, solaris_flow);
+	if (del->stats) {
+		error = dpif_solaris_get_flowstats(flowname, del->stats);
+		if (error == 0) {
+			VLOG_DBG("dpif_solaris_flow_del "
+			    "get_flowstats %s succeed", flowname);
+		} else {
+			VLOG_ERR("dpif_solaris_flow_del "
+			    "get_flowstats %s failed %d", flowname, error);
+		}
+	}
+	error = solaris_remove_flow(physname, flowname);
+	if (error == 0) {
+		VLOG_DBG("dpif_solaris_flow_del solaris_remove_flow %s "
+		    "succeed", flowname);
+	} else {
+		VLOG_ERR("dpif_solaris_flow_del solaris_remove_flow %s "
+		    "failed %d", flowname, error);
+	}
+	ovs_rwlock_unlock(&dpif->flow_rwlock);
+
+	free(physname);
+	return (error);
+}
+
+static int
+dpif_solaris_flow_flush__(struct dpif_solaris *dpif)
+{
+	struct dpif_solaris_flow *solaris_flow, *next;
+
+	ovs_rwlock_wrlock(&dpif->flow_rwlock);
+	HMAP_FOR_EACH_SAFE(solaris_flow, next, node, &dpif->flows) {
+		(void) solaris_remove_flow(solaris_flow->physname,
+		    solaris_flow->flowname);
+		dpif_solaris_flow_remove(dpif, solaris_flow);
+	}
+	ovs_rwlock_unlock(&dpif->flow_rwlock);
+	return (0);
+}
+
+static int
+dpif_solaris_flow_flush(struct dpif *dpif_)
+{
+	struct dpif_solaris *dpif = dpif_solaris_cast(dpif_);
+	return (dpif_solaris_flow_flush__(dpif));
+}
+
+struct dpif_solaris_flow_state {
+	struct odputil_keybuf keybuf;
+	struct odputil_keybuf maskbuf;
+};
+
+struct dpif_solaris_flow_ent {
+	struct hmap_node node;
+	char flowname[MAXUSERFLOWNAMELEN];
+	struct flow f;
+	struct flow m;
+	struct ofpbuf action;
+	struct dpif_flow_stats stats;
+};
+
+struct dpif_solaris_flow_iter {
+	struct hmap flows;
+	uint32_t bucket;
+	uint32_t offset;
+	int status;
+	struct ovs_mutex mutex;
+};
+
+static void
+dpif_solaris_flow_dump_state_init(void **statep)
+{
+	struct dpif_solaris_flow_state *state;
+
+	*statep = state = xmalloc(sizeof (*state));
+}
+
+static void
+dpif_solaris_flow_dump_state_uninit(void *state_)
+{
+	struct dpif_solaris_flow_state *state = state_;
+
+	free(state);
+}
+
+static void
+walk_flow(void *arg, const char *name, boolean_t is_default, struct flow *f,
+    struct flow *m, struct ofpbuf *action, uint64_t npackets, uint64_t nbytes,
+    uint64_t lastused)
+{
+	struct dpif_solaris_flow_iter *iter = arg;
+	struct dpif_solaris_flow_ent *flow;
+
+	VLOG_DBG("dpif_solaris_flow_dump_start walk flow %s", name);
+	ovs_assert(!is_default);
+	flow = xzalloc(sizeof (*flow));
+	strlcpy(flow->flowname, name, sizeof (flow->flowname));
+	bcopy(f, &flow->f, sizeof (*f));
+	bcopy(m, &flow->m, sizeof (*m));
+	flow->stats.n_packets = npackets;
+	flow->stats.n_bytes = nbytes;
+	flow->stats.used = lastused / 1000000;
+	ofpbuf_init(&flow->action, 0);
+	if (ofpbuf_size(action) != 0) {
+		ofpbuf_put(&flow->action, ofpbuf_data(action),
+		    ofpbuf_size(action));
+	}
+
+	hmap_insert(&iter->flows, &flow->node,
+	    hash_words((const uint32_t *)flow, sizeof (*flow) / 4, 0));
+	ofpbuf_reinit(action, 0);
+}
+
+static int
+dpif_solaris_flow_dump_start(const struct dpif *dpif OVS_UNUSED, void **iterp)
+{
+	struct dpif_solaris_flow_iter *iter;
+	struct ofpbuf action;
+
+	*iterp = iter = xmalloc(sizeof (*iter));
+	iter->bucket = 0;
+	iter->offset = 0;
+	iter->status = 0;
+	hmap_init(&iter->flows);
+	ovs_mutex_init(&iter->mutex);
+	ovs_mutex_lock(&iter->mutex);
+	ofpbuf_init(&action, 0);
+	(void) solaris_flow_walk(iter, &action, B_TRUE, walk_flow);
+	ofpbuf_uninit(&action);
+	ovs_mutex_unlock(&iter->mutex);
+	return (0);
+}
+
+static int
+dpif_solaris_flow_dump_next(const struct dpif *dpif_ OVS_UNUSED, void *iter_,
+    void *state_, const struct nlattr **key, size_t *key_len,
+    const struct nlattr **mask, size_t *mask_len,
+    const struct nlattr **actions, size_t *actions_len,
+    const struct dpif_flow_stats **stats)
+{
+	struct dpif_solaris_flow_iter *iter = iter_;
+	struct dpif_solaris_flow_state *state = state_;
+	struct dpif_solaris_flow_ent *flow;
+	int error;
+
+	ovs_mutex_lock(&iter->mutex);
+	error = iter->status;
+	if (!error) {
+		struct hmap_node *node;
+
+		node = hmap_at_position(&iter->flows, &iter->bucket,
+		    &iter->offset);
+		if (node) {
+			flow = CONTAINER_OF(node,
+			    struct dpif_solaris_flow_ent, node);
+			VLOG_DBG("dpif_solaris_flow_dump_next %s",
+			    flow->flowname);
+		} else {
+			iter->status = error = EOF;
+		}
+	}
+	ovs_mutex_unlock(&iter->mutex);
+	if (error) {
+		return (error);
+	}
+
+	if (key) {
+		struct ofpbuf buf;
+		struct ds s;
+
+		ofpbuf_use_stack(&buf, &state->keybuf, sizeof (state->keybuf));
+		odp_flow_key_from_flow(&buf, &flow->f, &flow->m,
+		    flow->f.in_port.odp_port);
+
+		*key = ofpbuf_data(&buf);
+		*key_len = ofpbuf_size(&buf);
+
+		ds_init(&s);
+		odp_flow_format(*key, *key_len, NULL, 0, NULL, &s, true);
+
+		VLOG_DBG("dpif_solaris_flow_dump_next %s key %s",
+		    flow->flowname, ds_cstr(&s));
+		ds_destroy(&s);
+	}
+
+	if (key && mask) {
+		struct ofpbuf buf;
+		struct ds s;
+
+		ofpbuf_use_stack(&buf, &state->maskbuf,
+		    sizeof (state->maskbuf));
+		odp_flow_key_from_mask(&buf, &flow->m, &flow->m,
+		    flow->f.in_port.odp_port, SIZE_MAX);
+
+		*mask = ofpbuf_data(&buf);
+		*mask_len = ofpbuf_size(&buf);
+
+		ds_init(&s);
+		odp_flow_format(*key, *key_len, *mask, *mask_len, NULL, &s,
+		    true);
+		VLOG_DBG("dpif_solaris_flow_dump_next %s mask %s "
+		    "mask_key_len %"PRIuSIZE, flow->flowname,
+		    ds_cstr(&s), *mask_len);
+		ds_destroy(&s);
+	}
+
+	if (actions || stats) {
+		if (actions) {
+			*actions = ofpbuf_data(&flow->action);
+			*actions_len = ofpbuf_size(&flow->action);
+		}
+
+		if (stats) {
+			*stats = &flow->stats;
+		}
+	}
+	VLOG_DBG("dpif_solaris_flow_dump_next %s succeed",
+	    flow->flowname);
+
+	return (0);
+}
+
+static int
+dpif_solaris_flow_dump_done(const struct dpif *dpif OVS_UNUSED, void *iter_)
+{
+	struct dpif_solaris_flow_iter *iter = iter_;
+	struct dpif_solaris_flow_ent *flow, *next;
+
+	ovs_mutex_lock(&iter->mutex);
+	HMAP_FOR_EACH_SAFE(flow, next, node, &iter->flows) {
+		ofpbuf_uninit(&flow->action);
+		hmap_remove(&iter->flows, &flow->node);
+		free(flow);
+	}
+	ovs_mutex_unlock(&iter->mutex);
+	hmap_destroy(&iter->flows);
+	ovs_mutex_destroy(&iter->mutex);
+	free(iter);
+	return (0);
+}
+
+static int
+dpif_solaris_port_output(struct dpif_solaris *dpif, odp_port_t port_no,
+    struct ofpbuf *packet, bool may_steal)
+{
+	struct dpif_solaris_port	*port;
+	struct msghdr			msghdr;
+	struct iovec 			iov;
+	struct cmsghdr			*cmsg;
+	struct tpacket_auxdata		auxdata;
+	char coutmsg[sizeof (auxdata) + sizeof (*cmsg) + _CMSG_HDR_ALIGNMENT];
+	size_t		nwritten;
+	ssize_t		nbytes = 0;
+	const char	*buf = ofpbuf_data(packet);
+	size_t		buflen = ofpbuf_size(packet);
+	int error, fd = -1;
+
+	VLOG_DBG("dpif_solaris_port_output %d", port_no);
+
+	ovs_rwlock_rdlock(&dpif->port_rwlock);
+	error = dpif_solaris_get_uplink_port(dpif, &port);
+	if (error == 0 && port->is_uplink)
+		fd = port->xfd;
+	ovs_rwlock_unlock(&dpif->port_rwlock);
+	if (fd == -1) {
+		if (may_steal) {
+			ofpbuf_delete(packet);
+		}
+		return (error);
+	}
+
+	bzero(&msghdr, sizeof (msghdr));
+	msghdr.msg_iov = &iov;
+	msghdr.msg_iovlen = 1;
+	msghdr.msg_control = (void *)_CMSG_HDR_ALIGN(coutmsg);
+	msghdr.msg_controllen = sizeof (*cmsg) + sizeof (auxdata);
+
+	iov.iov_len = buflen;
+	iov.iov_base = (char *)buf;
+
+	cmsg = CMSG_FIRSTHDR(&msghdr);
+	cmsg->cmsg_level = SOL_PACKET;
+	cmsg->cmsg_type = PACKET_AUXDATA;
+	cmsg->cmsg_len = CMSG_DATA(cmsg) + sizeof (auxdata) - (uchar_t *)cmsg;
+
+	memcpy(&auxdata, CMSG_DATA(cmsg), sizeof (auxdata));
+	auxdata.tp_of_port = port_no;
+	memcpy(CMSG_DATA(cmsg), &auxdata, sizeof (auxdata));
+	for (nwritten = 0; nwritten < buflen; nwritten += nbytes) {
+		nbytes = sendmsg(fd, &msghdr, 0);
+		if (nbytes == -1) {
+			if (errno != EAGAIN) {
+				error = errno;
+				break;
+			}
+			nbytes = 0;
+		} else if (nbytes == 0) {
+			error = EIO;
+			break;
+		}
+		iov.iov_len = buflen - (nwritten + nbytes);
+		iov.iov_base = (char *)buf + (nwritten + nbytes);
+	}
+
+	VLOG_DBG("dpif_solaris_port_output len %"PRIuSIZE" to %d %s",
+	    buflen, port_no, error == 0 ? "succeed" :
+	    ovs_strerror(error));
+	ovs_assert(error != 0 || nwritten == buflen);
+
+	return (0);
+}
+
+/*
+ * Return the port number of the uplink port; 0 if there is no uplink port
+ * Assumes caller holds port_rwlock.
+ */
+static odp_port_t
+dp_solaris_uplink_port(struct dpif_solaris *dpif)
+{
+	odp_port_t			port_no = 0;
+	struct dpif_solaris_port	*port;
+
+	ovs_rwlock_rdlock(&dpif->port_rwlock);
+	HMAP_FOR_EACH(port, node, &dpif->ports) {
+		if (port->is_uplink) {
+			port_no = port->port_no;
+			break;
+		}
+	}
+	ovs_rwlock_unlock(&dpif->port_rwlock);
+
+	return (port_no);
+}
+
+static void
+dp_solaris_execute_cb(void *aux_, struct ofpbuf *packet,
+    struct pkt_metadata *md OVS_UNUSED, const struct nlattr *a, bool may_steal)
+{
+	struct dpif_solaris *dpif = aux_;
+	int type = nl_attr_type(a);
+
+	VLOG_DBG("dp_solaris_execute_cb type %d", type);
+
+	switch ((enum ovs_action_attr)type) {
+	case OVS_ACTION_ATTR_OUTPUT: {
+		odp_port_t port_no = u32_to_odp(nl_attr_get_u32(a));
+
+		VLOG_DBG("dp_solaris_execute_cb OVS_ACTION_ATTR_OUTPUT "
+		    "%d", port_no);
+
+		(void) dpif_solaris_port_output(dpif, port_no, packet,
+		    may_steal);
+		break;
+	}
+
+	/*
+	 * We'll send the packet back to the kernel and it'll be classified
+	 * and we should get it back. XXX Check if this could cause an
+	 * infinite loop. This is not terribly effective, but if we use
+	 * sockets, it will still get to the kernel before coming back.
+	 * Need a short-cut. Note we don't care about any userland data
+	 * since we will get it when the packets comes back from the
+	 * kernel. Also, currently we always send using the uplink in
+	 * dpif_solaris_port_output so we don't have to dp_solaris_uplink_port.
+	 */
+	case OVS_ACTION_ATTR_USERSPACE:	{	/* controller */
+		odp_port_t port_no = dp_solaris_uplink_port(dpif);
+
+		VLOG_DBG("dp_solaris_execute_cb OVS_ACTION_ATTR_USERSPACE");
+		if (port_no == 0) {
+			VLOG_DBG("dp_solaris_execute_cb "
+			"OVS_ACTION_ATTR_USERSPACE: no uplink port.");
+			if (may_steal) {
+				ofpbuf_delete(packet);
+			}
+			break;
+		}
+		VLOG_DBG("dp_solaris_execute_cb OVS_ACTION_ATTR_USERSPACE "
+		    "%d", port_no);
+		(void) dpif_solaris_port_output(dpif, port_no, packet,
+		    may_steal);
+		break;
+	}
+	case OVS_ACTION_ATTR_HASH:		/* for bonding */
+	case OVS_ACTION_ATTR_RECIRC:		/* for bonding */
+	case OVS_ACTION_ATTR_PUSH_VLAN:
+	case OVS_ACTION_ATTR_POP_VLAN:
+	case OVS_ACTION_ATTR_PUSH_MPLS:
+	case OVS_ACTION_ATTR_POP_MPLS:
+	case OVS_ACTION_ATTR_SET:
+	case OVS_ACTION_ATTR_SAMPLE:
+	case OVS_ACTION_ATTR_UNSPEC:
+	case __OVS_ACTION_ATTR_MAX:
+		VLOG_DBG("dp_solaris_execute_cb type %d", type);
+		OVS_NOT_REACHED();
+	}
+}
+
+static int
+dpif_solaris_execute(struct dpif *dpif_, struct dpif_execute *execute)
+{
+	struct dpif_solaris *dpif = dpif_solaris_cast(dpif_);
+	struct pkt_metadata *md = &execute->md;
+
+	if (ofpbuf_size(execute->packet) < ETH_HEADER_LEN ||
+	    ofpbuf_size(execute->packet) > UINT16_MAX) {
+		VLOG_ERR("dpif_solaris_execute invalid size %d",
+		    ofpbuf_size(execute->packet));
+		return (EINVAL);
+	}
+
+	odp_execute_actions(dpif, execute->packet, false, md, execute->actions,
+	    execute->actions_len, dp_solaris_execute_cb);
+	return (0);
+}
+
+static void
+dpif_solaris_destroy_channels(struct dpif_solaris *dpif)
+    OVS_REQ_WRLOCK(dpif->upcall_lock)
+{
+	struct dpif_solaris_port *port;
+
+	if (!dpif->recv_set)
+		return;
+
+	ovs_rwlock_wrlock(&dpif->port_rwlock);
+	HMAP_FOR_EACH(port, node, &dpif->ports) {
+		if (port->upcall_fd != -1) {
+			(void) close(port->upcall_fd);
+			port->upcall_fd = -1;
+		}
+	}
+	ovs_rwlock_unlock(&dpif->port_rwlock);
+	(void) close(dpif->event_rfd);
+	(void) close(dpif->event_wfd);
+	dpif->event_rfd = dpif->event_wfd = -1;
+	dpif->recv_set = false;
+}
+
+static int
+dpif_solaris_refresh_port_channel(struct dpif_solaris *dpif,
+    struct dpif_solaris_port *port, const char *physname, boolean_t notify)
+    OVS_REQ_WRLOCK(dpif->port_rwlock)
+{
+	int fd, flags, onoff = 1;
+	struct sockaddr_ll sll;
+	datalink_id_t linkid;
+	mac_of_ports_t mofport;
+	dladm_status_t status;
+	int error;
+
+	if (!dpif->recv_set || (port->vtype != OVS_VPORT_TYPE_NETDEV &&
+	    port->vtype != OVS_VPORT_TYPE_VXLAN))
+		return (0);
+
+	VLOG_DBG("dpif_solaris_refresh_port_channel on %s port_no: %d%s",
+	    port->linkname, port->port_no, notify ? " notify" : "");
+
+	fd = socket(PF_PACKET, SOCK_RAW, ETH_P_ALL);
+	if (fd == -1) {
+		VLOG_ERR("dpif_solaris_refresh_port_channel %s failed to "
+		    "create socket: %s", port->linkname, ovs_strerror(errno));
+		return (errno);
+	}
+
+	status = dladm_name2info(dpif->dh, physname, &linkid,
+	    NULL, NULL, NULL);
+	error = solaris_dladm_status2error(status);
+	if (error != 0) {
+		VLOG_ERR("dpif_solaris_refresh_port_channel %s failed to get "
+		    "linkid: %s", port->linkname, ovs_strerror(error));
+		(void) close(fd);
+		return (error);
+	}
+
+	(void) memset(&sll, 0, sizeof (sll));
+	sll.sll_family = AF_PACKET;
+	sll.sll_ifindex = linkid;
+	sll.sll_protocol = ETH_P_ALL;
+	if (bind(fd, (struct sockaddr *)&sll, sizeof (sll)) == -1) {
+		error = errno;
+		VLOG_ERR("dpif_solaris_refresh_port_channel %s failed to bind: "
+		    "%s", port->name, ovs_strerror(error));
+		(void) close(fd);
+		return (error);
+	}
+
+	mofport.mop_port = port->pf_port_no;
+	mofport.mop_sport = port->port_no;
+	if (setsockopt(fd, SOL_PACKET,
+	    PACKET_ADD_OF_DEFFLOW, &mofport, sizeof (mac_of_ports_t)) != 0) {
+		error = errno;
+		VLOG_ERR("dpif_solaris_refresh_port_channel %s failed to set "
+		    "PACKET_ADD_OF_DEFFLOW: %s", port->name,
+		    ovs_strerror(error));
+		(void) close(fd);
+		return (error);
+	}
+
+	if (setsockopt(fd, SOL_PACKET,
+	    PACKET_AUXDATA, &onoff, sizeof (onoff)) != 0) {
+		error = errno;
+		VLOG_ERR("dpif_solaris_refresh_port_channel %s failed to set "
+		    "PACKET_AUXDATAQ: %s", port->name,
+		    ovs_strerror(error));
+		(void) setsockopt(fd, SOL_PACKET, PACKET_REM_OF_DEFFLOW,
+		    NULL, 0);
+		(void) close(fd);
+		return (error);
+	}
+
+	/* must not block */
+	if (((flags = fcntl(fd, F_GETFL, 0)) == -1) ||
+	    (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1)) {
+		error = errno;
+		VLOG_ERR("dpif_solaris_refresh_port_channel %s failed to set "
+		    "non-block: %s", port->name, ovs_strerror(error));
+		(void) setsockopt(fd, SOL_PACKET, PACKET_REM_OF_DEFFLOW,
+		    NULL, 0);
+		(void) close(fd);
+		return (error);
+	}
+
+	port->upcall_fd = fd;
+
+	VLOG_DBG("dpif_solaris_refresh_port_channel %s upcall_fd=%d",
+	    port->name, port->upcall_fd);
+
+	/* Inform the event_rfd that the port->upcall_fd has been changed */
+	if (notify && write(dpif->event_wfd, (char *)(&port->port_no),
+	    sizeof (odp_port_t)) < 0) {
+		VLOG_ERR("dpif_solaris_refresh_port_channel %s event "
+		    "notify failed: %s", port->name, ovs_strerror(errno));
+	}
+
+	return (0);
+}
+
+/*
+ * Synchronizes 'channels' in 'dpif->handlers'  with the set of vports
+ * currently in 'dpif' in the kernel, by adding a new set of channels for
+ * any kernel vport that lacks one and deleting any channels that have no
+ * backing kernel vports.
+ */
+static int
+dpif_solaris_refresh_channels(struct dpif_solaris *dpif, uint32_t n_handlers)
+    OVS_REQ_WRLOCK(dpif->upcall_lock)
+{
+	struct dpif_solaris_port *port;
+	int error, flags;
+	odp_port_t port_no = 0;
+	int fds[2];
+
+	VLOG_DBG("dpif_solaris_refresh_channels %d", n_handlers);
+	dpif_solaris_destroy_channels(dpif);
+
+	/*
+	 * Setup the event pipe to monitor the port changes, so that
+	 * port_channel_fd is able to be refreshed into the pollfds set.
+	 */
+	if ((pipe(fds)) < 0) {
+		error = errno;
+		return (error);
+	}
+	/* must not block */
+	if (((flags = fcntl(fds[0], F_GETFL, 0)) == -1) ||
+	    (fcntl(fds[0], F_SETFL, flags | O_NONBLOCK) == -1)) {
+		error = errno;
+		(void) close(fds[0]);
+		(void) close(fds[1]);
+		return (error);
+	}
+	if (((flags = fcntl(fds[1], F_GETFL, 0)) == -1) ||
+	    (fcntl(fds[1], F_SETFL, flags | O_NONBLOCK) == -1)) {
+		error = errno;
+		(void) close(fds[0]);
+		(void) close(fds[1]);
+		return (error);
+	}
+
+	dpif->recv_set = true;
+	dpif->event_rfd = fds[0];
+	dpif->event_wfd = fds[1];
+
+	ovs_rwlock_wrlock(&dpif->port_rwlock);
+	HMAP_FOR_EACH(port, node, &dpif->ports) {
+		error = dpif_solaris_refresh_port_channel(dpif, port,
+		    port->bridge->physname, false);
+		if (error != 0) {
+			ovs_rwlock_unlock(&dpif->port_rwlock);
+			dpif_solaris_destroy_channels(dpif);
+			return (error);
+		}
+	}
+	ovs_rwlock_unlock(&dpif->port_rwlock);
+
+	/* Inform the event_rfd that all ports' upcall_fd has been changed */
+	if (write(dpif->event_wfd, (char *)&port_no, sizeof (odp_port_t)) < 0) {
+		VLOG_ERR("dpif_solaris_refresh_channels event notify"
+		    "failed: %s", ovs_strerror(errno));
+	}
+	return (0);
+}
+
+static int
+dpif_solaris_recv_set_(struct dpif_solaris *dpif, bool enable)
+{
+	if (dpif->recv_set == enable)
+		return (0);
+
+	if (!enable) {
+		dpif_solaris_destroy_channels(dpif);
+		return (0);
+	} else {
+		return (dpif_solaris_refresh_channels(dpif, 1));
+	}
+}
+
+static int
+dpif_solaris_recv_set(struct dpif *dpif_, bool enable)
+{
+	struct dpif_solaris *dpif = dpif_solaris_cast(dpif_);
+	int error = 0;
+
+	VLOG_DBG("dpif_solaris_recv_set %s", enable ? "true" : "false");
+
+	ovs_rwlock_wrlock(&dpif->upcall_lock);
+	dpif_solaris_recv_set_(dpif, enable);
+	ovs_rwlock_unlock(&dpif->upcall_lock);
+	return (error);
+}
+
+/*
+ * For Solaris we will use the queue_id as the priority. The queue id
+ * will be used to get the QoS information and used to configure
+ * the bandwidth and priority
+ */
+static int
+dpif_solaris_queue_to_priority(const struct dpif *dpif OVS_UNUSED,
+    uint32_t queue_id, uint32_t *priority)
+{
+	*priority = queue_id;
+	return (0);
+}
+
+static int
+dpif_solaris_parse_pkt(struct dpif_solaris *dpif OVS_UNUSED,
+    struct ofpbuf *packet, struct pkt_metadata *md, uint16_t action_type,
+    struct dpif_upcall *upcall, struct ofpbuf *buf)
+{
+	struct flow flow;
+	void *data;
+	size_t buf_size;
+	const struct nlattr *userdata = NULL;
+
+	VLOG_DBG("dpif_solaris_parse_pkt");
+
+	if ((buf_size = ofpbuf_size(packet)) < ETH_HEADER_LEN) {
+		VLOG_ERR("dpif_solaris_parse_pkt bufsize too small %"PRIuSIZE,
+		    buf_size);
+		return (EINVAL);
+	}
+
+	flow_extract(packet, md, &flow);
+	upcall->type = (action_type == FLOW_ACTION_MISSED_PKT) ? DPIF_UC_MISS :
+	    DPIF_UC_ACTION;
+
+	/* Allocate buffer big enough for everything. */
+	buf_size = ODPUTIL_FLOW_KEY_BYTES;
+	if (action_type == FLOW_ACTION_OF_CONTROLLER) {
+		struct ofpbuf abuf;
+		char slow_path_buf[128];
+		const struct nlattr *a;
+
+		VLOG_DBG("dpif_solaris_parse_p:: FLOW_ACTION_OF_CONTROLLER!!");
+		ofpbuf_use_stack(&abuf, slow_path_buf, sizeof (slow_path_buf));
+		slowpath_to_actions(SLOW_CONTROLLER, &abuf);
+		a = nl_attr_find(&abuf, 0, OVS_ACTION_ATTR_USERSPACE);
+		if (a != NULL) {
+			userdata = nl_attr_find_nested(a,
+			    OVS_USERSPACE_ATTR_USERDATA);
+			if (userdata != NULL)
+				buf_size += NLA_ALIGN(userdata->nla_len);
+		}
+	}
+	buf_size += ofpbuf_size(packet);
+	ofpbuf_init(buf, buf_size);
+
+	VLOG_DBG("PARSE flow in_port %d", flow.in_port.odp_port);
+	odp_flow_key_from_flow(buf, &flow, NULL, flow.in_port.odp_port);
+	upcall->key = ofpbuf_data(buf);
+	upcall->key_len = ofpbuf_size(buf);
+
+	if (userdata != NULL) {
+		upcall->userdata = ofpbuf_put(buf, userdata,
+		    NLA_ALIGN(userdata->nla_len));
+	}
+
+	data = ofpbuf_put(buf, ofpbuf_data(packet), ofpbuf_size(packet));
+	ofpbuf_use_stub(&upcall->packet, data, ofpbuf_size(packet));
+	ofpbuf_set_size(&upcall->packet, ofpbuf_size(packet));
+	return (0);
+}
+
+static int
+dpif_solaris_recv__(struct dpif_solaris *dpif, uint32_t handler_id OVS_UNUSED,
+    struct dpif_upcall *upcall, struct ofpbuf *buf)
+    OVS_REQ_WRLOCK(dpif->upcall_lock)
+{
+	struct iovec iov;
+	struct msghdr msg;
+	struct cmsghdr *cmsg;
+	char cmsg_buf[sizeof (*cmsg) + sizeof (struct tpacket_auxdata) +
+	    _CMSG_HDR_ALIGNMENT];
+	uint8_t pktbuf[65536];
+	int pktlen;
+	struct dpif_solaris_port *port;
+	odp_port_t port_no;
+	uint16_t action_type;
+	int error;
+
+	if (!dpif->recv_set) {
+		return (EAGAIN);
+	}
+
+	(void) read(dpif->event_rfd, (char *)&port_no,
+	    sizeof (odp_port_t));
+
+	ovs_rwlock_wrlock(&dpif->port_rwlock);
+	HMAP_FOR_EACH(port, node, &dpif->ports) {
+		if (port->upcall_fd == -1)
+			continue;
+
+		msg.msg_name = NULL;
+		msg.msg_namelen = 0;
+		msg.msg_iov = &iov;
+		msg.msg_iovlen = 1;
+		msg.msg_control = (void *)_CMSG_HDR_ALIGN(cmsg_buf);
+		msg.msg_controllen = sizeof (cmsg_buf) -
+		    (_CMSG_HDR_ALIGN(cmsg_buf) - (uintptr_t)cmsg_buf);
+		msg.msg_flags = 0;
+
+		iov.iov_len = sizeof (pktbuf);
+		iov.iov_base = pktbuf;
+		pktlen = recvmsg(port->upcall_fd, &msg, MSG_TRUNC);
+		if (pktlen > 0) {
+			struct pkt_metadata	md;
+			mactun_info_t		*tuninfop;
+			struct flow_tnl		*tun;
+			struct ofpbuf		pkt;
+
+			action_type = FLOW_ACTION_OF_MAX;
+			for (cmsg = CMSG_FIRSTHDR(&msg); cmsg;
+			    cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+				struct tpacket_auxdata	aux;
+
+				if (cmsg->cmsg_len <
+				    CMSG_LEN(sizeof (struct tpacket_auxdata)) ||
+				    cmsg->cmsg_level != SOL_PACKET ||
+				    cmsg->cmsg_type != PACKET_AUXDATA)
+					continue;
+
+				memcpy(&aux, CMSG_DATA(cmsg), sizeof (aux));
+				action_type = aux.tp_of_action;
+				port_no = aux.tp_of_port;
+				tuninfop = &aux.tp_tun_info;
+				break;
+			}
+
+			if (action_type != FLOW_ACTION_MISSED_PKT &&
+			    action_type != FLOW_ACTION_OF_CONTROLLER) {
+				VLOG_ERR("dpif_solaris_recv__ on port %s len %d"
+				    " but with unknown auxdata type %d",
+				    port->name, pktlen, action_type);
+				continue;
+			}
+
+			VLOG_DBG("dpif_solaris_recv__ on port %s len %d "
+			    "type %s (src_port = %u)", port->name, pktlen,
+			    action_type == FLOW_ACTION_MISSED_PKT ?
+			    "miss_pkt" : "fwd_controller", port_no);
+
+			md = PKT_METADATA_INITIALIZER(port_no);
+			if (tuninfop->mti_dst._S6_un._S6_u32[3] != 0) {
+				tun = &md.tunnel;
+				tun->tun_id = htonll(tuninfop->mti_id);
+				tun->flags |= FLOW_TNL_F_KEY;
+				tun->ip_src =
+				    tuninfop->mti_src._S6_un._S6_u32[3];
+				tun->ip_dst =
+				    tuninfop->mti_dst._S6_un._S6_u32[3];
+				tun->ip_tos = tuninfop->mti_tos;
+				tun->ip_ttl = tuninfop->mti_ttl;
+				/* TBD? */
+				tun->flags |= FLOW_TNL_F_DONT_FRAGMENT;
+			}
+			ofpbuf_use_const(&pkt, pktbuf, pktlen);
+			error = dpif_solaris_parse_pkt(dpif, &pkt, &md,
+			    action_type, upcall, buf);
+			if (error != 0) {
+				VLOG_ERR("dpif_solaris_recv__ parse_pkt on "
+				    "port %s action %s failed %d", port->name,
+				    action_type == FLOW_ACTION_MISSED_PKT ?
+				    "miss_pkt" : "fwd_to_controller",
+				    error);
+			} else {
+				ovs_rwlock_unlock(&dpif->port_rwlock);
+				return (0);
+			}
+		}
+	}
+
+	ovs_rwlock_unlock(&dpif->port_rwlock);
+	return (EAGAIN);
+}
+
+static int
+dpif_solaris_recv(struct dpif *dpif_, uint32_t handler_id,
+    struct dpif_upcall *upcall, struct ofpbuf *buf)
+{
+	struct dpif_solaris *dpif = dpif_solaris_cast(dpif_);
+	int error;
+
+	ovs_rwlock_rdlock(&dpif->upcall_lock);
+	error = dpif_solaris_recv__(dpif, handler_id, upcall, buf);
+	ovs_rwlock_unlock(&dpif->upcall_lock);
+
+	return (error);
+}
+
+static void
+dpif_solaris_recv_wait(struct dpif *dpif_, uint32_t handler_id OVS_UNUSED)
+{
+	struct dpif_solaris *dpif = dpif_solaris_cast(dpif_);
+	struct dpif_solaris_port *port;
+
+	ovs_rwlock_rdlock(&dpif->upcall_lock);
+	if (dpif->recv_set) {
+		poll_fd_wait(dpif->event_rfd, POLLIN);
+		ovs_rwlock_wrlock(&dpif->port_rwlock);
+		HMAP_FOR_EACH(port, node, &dpif->ports) {
+			if (port->upcall_fd != -1) {
+				poll_fd_wait(port->upcall_fd, POLLIN);
+			}
+		}
+		ovs_rwlock_unlock(&dpif->port_rwlock);
+	}
+	ovs_rwlock_unlock(&dpif->upcall_lock);
+}
+
+static void
+dpif_solaris_recv_purge__(struct dpif_solaris *dpif)
+    OVS_REQ_WRLOCK(dpif->upcall_lock)
+{
+	struct dpif_solaris_port *port;
+
+	if (dpif->recv_set) {
+		drain_rcvbuf(dpif->event_rfd);
+		ovs_rwlock_wrlock(&dpif->port_rwlock);
+		HMAP_FOR_EACH(port, node, &dpif->ports) {
+			drain_rcvbuf(port->upcall_fd);
+		}
+		ovs_rwlock_unlock(&dpif->port_rwlock);
+	}
+}
+
+static void
+dpif_solaris_recv_purge(struct dpif *dpif_)
+{
+	struct dpif_solaris *dpif = dpif_solaris_cast(dpif_);
+
+	VLOG_DBG("dpif_solaris_recv_purge");
+	ovs_rwlock_wrlock(&dpif->upcall_lock);
+	dpif_solaris_recv_purge__(dpif);
+	ovs_rwlock_unlock(&dpif->upcall_lock);
+}
+
+static int
+dpif_solaris_handlers_set(struct dpif *dpif OVS_UNUSED,
+    uint32_t n_handlers OVS_UNUSED)
+{
+	return (0);
+}
+
+const struct dpif_class dpif_solaris_class = {
+	"system",
+	dpif_solaris_enumerate,
+	NULL,					/* port_open_type */
+	dpif_solaris_open,
+	dpif_solaris_close,
+	dpif_solaris_destroy,
+	NULL,					/* run */
+	NULL,					/* wait */
+	dpif_solaris_get_stats,
+	dpif_solaris_port_add,
+	dpif_solaris_port_del,
+	dpif_solaris_port_query_by_number,
+	dpif_solaris_port_query_by_name,
+	NULL, 					/* port_get_pid */
+	dpif_solaris_port_dump_start,
+	dpif_solaris_port_dump_next,
+	dpif_solaris_port_dump_done,
+	dpif_solaris_port_poll,
+	dpif_solaris_port_poll_wait,
+	dpif_solaris_flow_get,
+	dpif_solaris_flow_put,
+	dpif_solaris_flow_del,
+	dpif_solaris_flow_flush,
+	dpif_solaris_flow_dump_state_init,
+	dpif_solaris_flow_dump_start,
+	dpif_solaris_flow_dump_next,
+	NULL,				/* flow_dump_next_may_destroy_keys */
+	dpif_solaris_flow_dump_done,
+	dpif_solaris_flow_dump_state_uninit,
+	dpif_solaris_execute,
+	NULL,					/* operate */
+	dpif_solaris_recv_set,
+	dpif_solaris_handlers_set,		/* handlers_set */
+	dpif_solaris_queue_to_priority,
+	dpif_solaris_recv,
+	dpif_solaris_recv_wait,
+	dpif_solaris_recv_purge,
+	dpif_solaris_configure_bridge_port
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openvswitch/files/lib/dpif-solaris.h	Mon Nov 16 16:49:19 2015 -0500
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ */
+
+/*
+ * Copyright (c) 2010, 2011 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef	DPIF_SOLARIS_H
+#define	DPIF_SOLARIS_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include "openvswitch/types.h"
+#include "smap.h"
+#include "util-solaris.h"
+
+#define	PORT_PF_PACKET_UPLINK	1
+struct dpif_flow_stats;
+
+int dpif_solaris_get_priority_details(void *, odp_port_t, uint_t,
+    struct smap *);
+void dpif_log(int, const char *, ...);
+#endif	/* DPIF_SOLARIS_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openvswitch/files/lib/netdev-solaris.c	Mon Nov 16 16:49:19 2015 -0500
@@ -0,0 +1,2728 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ */
+
+/*
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <arpa/inet.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netpacket/packet.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <poll.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/sockio.h>
+#include <strings.h>
+
+#include <libdllink.h>
+#include <kstat2.h>
+
+#include "coverage.h"
+#include "dpif-netdev.h"
+#include "dynamic-string.h"
+#include "fatal-signal.h"
+#include "hash.h"
+#include "hmap.h"
+#include "netdev-provider.h"
+#include "netdev-vport.h"
+#include "ofpbuf.h"
+#include "openflow/openflow.h"
+#include "ovs-atomic.h"
+#include "packets.h"
+#include "poll-loop.h"
+#include "shash.h"
+#include "socket-util.h"
+#include "sset.h"
+#include "timer.h"
+#include "unaligned.h"
+#include "vlog.h"
+
+#include "netdev-solaris.h"
+#include "util-solaris.h"
+
+/*
+ * Enable vlog() for this module
+ */
+VLOG_DEFINE_THIS_MODULE(netdev_solaris);
+
+/*
+ * Per-network device state.
+ */
+struct netdev_solaris {
+	struct netdev up;
+
+	/* Protects all members below. */
+	struct ovs_mutex mutex;
+
+	/*
+	 * Bit mask of cached properties. These can be cached because
+	 * nothing besides vswitchd is altering the properties.
+	 */
+	unsigned int cache_valid;
+
+	/*
+	 * Network device features (e.g., speed, duplex, etc)
+	 */
+	enum netdev_features current;
+	enum netdev_features advertised;
+	enum netdev_features supported;
+	int get_features_error;
+
+	int ifindex;			/* interface index */
+	struct in_addr in4;		/* IPv4 address */
+	struct in_addr netmask;		/* IPv4 netmask */
+	struct in6_addr in6;		/* IPv6 address */
+	unsigned int plumbed;		 /* per-family plumbed */
+	unsigned int implicitly_plumbed; /* per-family implicitly plumbed */
+
+	char class[DLADM_PROP_VAL_MAX];	/* datalink class */
+	char brname[MAXLINKNAMELEN];	/* bridge name */
+	int mtu;			/* datalink MTU */
+	uint8_t etheraddr[ETH_ADDR_LEN]; /* MAC address */
+
+	struct tc *tc;			/* traffic control */
+};
+
+/*
+ * Bits indicating what network device configuration is cached.
+ * See cache_valid field above.
+ */
+enum {
+	VALID_IFINDEX		= 1 << 0,
+	VALID_ETHERADDR		= 1 << 1,
+	VALID_IN4		= 1 << 2,
+	VALID_IN6		= 1 << 3,
+	VALID_MTU		= 1 << 4,
+	VALID_LINKCLASS		= 1 << 5,
+	VALID_FEATURES		= 1 << 6
+};
+
+/*
+ * When network devices are constructed, the internal devices (i.e., bridges)
+ * are initially created over this etherstub and are moved when uplink ports
+ * are added to the bridge.
+ */
+#define	NETDEV_IMPL_ETHERSTUB	"ovs.etherstub0"
+
+/*
+ * Used to track state of IP plumbing on network devices.
+ */
+#define	SOLARIS_IPV4 0x01
+#define	SOLARIS_IPV6 0x02
+
+/*
+ * Cached socket descriptors.
+ */
+static int sock4;
+static int sock6;
+
+/*
+ * Cached kstat2_handle_t. Re-use unless an error causes it to be
+ * closed. In that case, cache the handle on the next open.
+ */
+static kstat2_handle_t	nd_khandle;
+static boolean_t	kstat2_handle_initialized = B_FALSE;
+static struct ovs_mutex	kstat_mutex = OVS_MUTEX_INITIALIZER;
+
+static int netdev_solaris_init(void);
+static struct netdev_solaris *netdev_solaris_cast(const struct netdev *);
+
+/*
+ * An instance of a traffic control class.
+ *
+ * Always associated with a particular network device.
+ *
+ * Each TC implementation subclasses this with whatever additional data
+ * it needs.
+ */
+struct tc {
+	const struct tc_ops *ops;
+
+	/*
+	 * Contains "struct tc_queues"s.
+	 * Read by generic TC layer.
+	 * Written only by TC implementation.
+	 */
+	struct hmap queues;
+};
+
+#define	TC_INITIALIZER(TC, OPS)	{ OPS, HMAP_INITIALIZER(&(TC)->queues) }
+
+/*
+ * One traffic control queue.
+ *
+ * Each TC implementation subclasses this with whatever additional
+ * data it needs.
+ */
+struct tc_queue {
+	struct hmap_node hmap_node;	/* In struct tc's "queues" hmap. */
+	unsigned int	queue_id;	/* OpenFlow queue ID. */
+	long long int	created;	/* Time queue was created, in msecs. */
+};
+
+/*
+ * A particular kind of traffic control.
+ *
+ * Each implementation generally maps to one particular Linux qdisc class.
+ *
+ * The functions below return 0 if successful or a positive errno value on
+ * failure, except where otherwise noted.  All of them must be provided,
+ * except where otherwise noted.
+ */
+struct tc_ops {
+	/*
+	 * Name used by kernel in the TCA_KIND attribute of tcmsg, e.g. "htb".
+	 * This is null for tc_ops_default since there is no appropriate
+	 * value.
+	 */
+	const char *linux_name;
+
+	/*
+	 * Name used in OVS database, e.g. "linux-htb".  Must be nonnull.
+	 */
+	const char *ovs_name;
+
+	/*
+	 * Number of supported OpenFlow queues, 0 for qdiscs that have no
+	 * queues.  The queues are numbered 0 through n_queues - 1.
+	 */
+	unsigned int n_queues;
+
+	/*
+	 * Called to install this TC class on 'netdev'.  The implementation
+	 * should make the calls required to set up 'netdev' with the right
+	 * qdisc and configure it according to 'details'.  The implementation
+	 * may assume that the current qdisc is the default; that is, there
+	 * is no need for it to delete the current qdisc before installing
+	 * itself.
+	 *
+	 * The contents of 'details' should be documented as valid for
+	 * 'ovs_name' in the "other_config" column in the "QoS" table in
+	 * vswitchd/vswitch.xml (which is built as ovs-vswitchd.conf.db(8)).
+	 *
+	 * This function must return 0 if and only if it sets 'netdev->tc'
+	 * to an initialized 'struct tc'.
+	 *
+	 * This function should always be nonnull.
+	 */
+	int (*tc_install)(struct netdev *netdev, const struct smap *details);
+
+	/*
+	 * Called when the netdev code determines that this TC class's qdisc
+	 * is installed on 'netdev', but we didn't install it ourselves and
+	 * so don't know any of the details.
+	 *
+	 * There is nothing for this function to do on Solaris.
+	 *
+	 * This function must return 0 if and only if it sets 'netdev->tc'
+	 * to an initialized 'struct tc'.
+	 */
+	int (*tc_load)(struct netdev *netdev, struct ofpbuf *nlmsg);
+
+	/*
+	 * Destroys the data structures allocated by the implementation as
+	 * part of 'tc'.  (This includes destroying 'tc->queues' by calling
+	 * tc_destroy(tc).
+	 *
+	 * This function may be null if 'tc' is trivial.
+	 */
+	void (*tc_destroy)(struct tc *tc);
+
+	/*
+	 * Retrieves details of 'netdev->tc' configuration into 'details'.
+	 *
+	 * The contents of 'details' should be documented as valid for
+	 * 'ovs_name' in the "other_config" column in the "QoS" table in
+	 * vswitchd/vswitch.xml (which is built as ovs-vswitchd.conf.db(8)).
+	 *
+	 * This function may be null if 'tc' is not configurable.
+	 */
+	int (*qdisc_get)(const struct netdev *netdev, struct smap *details);
+
+	/*
+	 * Reconfigures 'netdev->tc' according to 'details'.
+	 *
+	 * The contents of 'details' should be documented as valid for
+	 * 'ovs_name' in the "other_config" column in the "QoS" table in
+	 * vswitchd/vswitch.xml (which is built as ovs-vswitchd.conf.db(8)).
+	 *
+	 * This function may be null if 'tc' is not configurable.
+	 */
+	int (*qdisc_set)(struct netdev *, const struct smap *details);
+
+	/*
+	 * Retrieves details of 'queue' on 'netdev->tc' into 'details'.
+	 * 'queue' is one of the 'struct tc_queue's within
+	 * 'netdev->tc->queues'.
+	 *
+	 * The contents of 'details' should be documented as valid for
+	 * 'ovs_name' in the "other_config" column in the "Queue" table in
+	 * vswitchd/vswitch.xml (which is built as ovs-vswitchd.conf.db(8)).
+	 *
+	 * This function may be null if 'tc' does not have queues
+	 * ('n_queues' is 0).
+	 */
+	int (*class_get)(const struct netdev *netdev,
+	    const struct tc_queue *queue, struct smap *details);
+
+	/*
+	 * Configures or reconfigures 'queue_id' on 'netdev->tc' according to
+	 * 'details'. The caller ensures that 'queue_id' is less than
+	 * 'n_queues'.
+	 *
+	 * The contents of 'details' should be documented as valid for
+	 * 'ovs_name' in the "other_config" column in the "Queue" table in
+	 * vswitchd/vswitch.xml (which is built as ovs-vswitchd.conf.db(8)).
+	 *
+	 * This function may be null if 'tc' does not have queues or its
+	 * queues are not configurable.
+	 */
+	int (*class_set)(struct netdev *, unsigned int queue_id,
+	    const struct smap *details);
+
+	/*
+	 * Deletes 'queue' from 'netdev->tc'.  'queue' is one of the 'struct
+	 * tc_queue's within 'netdev->tc->queues'.
+	 *
+	 * This function may be null if 'tc' does not have queues or its queues
+	 * cannot be deleted.
+	 */
+	int (*class_delete)(struct netdev *, struct tc_queue *queue);
+
+	/*
+	 * Obtains stats for 'queue' from 'netdev->tc'.  'queue' is one of the
+	 * 'struct tc_queue's within 'netdev->tc->queues'.
+	 *
+	 * On success, initializes '*stats'.
+	 *
+	 * This function may be null if 'tc' does not have queues or if it
+	 * cannot report queue statistics.
+	 */
+	int (*class_get_stats)(const struct netdev *netdev,
+	    const struct tc_queue *queue, struct netdev_queue_stats *stats);
+
+	/*
+	 * Extracts queue stats from 'nlmsg', which is a response to a
+	 * RTM_GETTCLASS message, and passes them to 'cb' along with 'aux'.
+	 *
+	 * This function may be null if 'tc' does not have queues or if it
+	 * cannot report queue statistics.
+	 */
+	int (*class_dump_stats)(const struct netdev *netdev,
+	    const struct ofpbuf *nlmsg, netdev_dump_queue_stats_cb *cb,
+	    void *aux);
+};
+
+static void
+tc_init(struct tc *tc, const struct tc_ops *ops)
+{
+	tc->ops = ops;
+	hmap_init(&tc->queues);
+}
+
+static void
+tc_destroy(struct tc *tc)
+{
+	hmap_destroy(&tc->queues);
+}
+
+static const struct tc_ops tc_ops_htb;
+static const struct tc_ops tc_ops_default;
+
+static const struct tc_ops *const tcs[] = {
+	&tc_ops_htb,		/* Hierarchy token bucket */
+	&tc_ops_default,	/* Default qdisc */
+	NULL
+};
+
+static const struct tc_ops *
+tc_lookup_ovs_name(const char *name)
+{
+	const struct tc_ops *const *opsp;
+
+	for (opsp = tcs; *opsp != NULL; opsp++) {
+		const struct tc_ops *ops = *opsp;
+		if (strcmp(name, ops->ovs_name) == 0) {
+			return (ops);
+		}
+	}
+	return (NULL);
+}
+
+static struct tc_queue *
+tc_find_queue__(const struct netdev *netdev_, unsigned int queue_id,
+    size_t hash)
+{
+	struct netdev_solaris	*netdev = netdev_solaris_cast(netdev_);
+	struct tc_queue		*queue;
+
+	HMAP_FOR_EACH_IN_BUCKET(queue, hmap_node, hash, &netdev->tc->queues) {
+		if (queue->queue_id == queue_id) {
+			return (queue);
+		}
+	}
+	return (NULL);
+}
+
+static struct tc_queue *
+tc_find_queue(const struct netdev *netdev, unsigned int queue_id)
+{
+	return (tc_find_queue__(netdev, queue_id, hash_int(queue_id, 0)));
+}
+
+static int
+tc_delete_class(const struct netdev *netdev OVS_UNUSED,
+    unsigned int handle OVS_UNUSED)
+{
+	return (0);
+}
+
+static int
+tc_del_qdisc(struct netdev *netdev_)
+{
+	const char		*netdev_name = netdev_get_name(netdev_);
+	struct netdev_solaris	*netdev = netdev_solaris_cast(netdev_);
+
+	VLOG_DBG("tc_del_qdisc device %s", netdev_name);
+	if (netdev->tc) {
+		if (netdev->tc->ops->tc_destroy) {
+			netdev->tc->ops->tc_destroy(netdev->tc);
+		}
+		netdev->tc = NULL;
+	}
+	return (0);
+}
+
+/*
+ * If 'netdev''s qdisc type and parameters are not yet known, queries the
+ * kernel to determine what they are.  Returns 0 if successful, otherwise a
+ * positive errno value.
+ */
+static int
+tc_query_qdisc(const struct netdev *netdev_)
+{
+	struct netdev_solaris	*netdev = netdev_solaris_cast(netdev_);
+	const struct tc_ops	*ops;
+	int			load_error;
+	int			error;
+
+	if (netdev->tc) {
+		return (0);
+	}
+
+	/*
+	 * Either it's a built-in qdisc, or it's a qdisc set up by some
+	 * other entity that doesn't have a handle 1:0.  We will assume
+	 * that it's the system default qdisc.
+	 */
+	ops = &tc_ops_default;
+	error = 0;
+
+	/* Instantiate it. */
+	load_error = ops->tc_load(CONST_CAST(struct netdev *, netdev_), NULL);
+	ovs_assert((load_error == 0) == (netdev->tc != NULL));
+
+	return (error ? error : load_error);
+}
+
+static bool
+is_netdev_solaris_class(const struct netdev_class *netdev_class)
+{
+	return (netdev_class->init == netdev_solaris_init);
+}
+
+static struct netdev_solaris *
+netdev_solaris_cast(const struct netdev *netdev)
+{
+	ovs_assert(is_netdev_solaris_class(netdev_get_class(netdev)));
+
+	return (CONTAINER_OF(netdev, struct netdev_solaris, up));
+}
+
+static int
+netdev_solaris_plumb(const struct netdev *netdev_, sa_family_t af)
+{
+	struct netdev_solaris	*netdev = netdev_solaris_cast(netdev_);
+	const char		*netdev_name = netdev_get_name(netdev_);
+	char			*astring;
+	int			sock;
+	int			proto;
+	int			error;
+
+	ovs_assert(af == AF_INET || af == AF_INET6);
+
+	if (af == AF_INET) {
+		sock = sock4;
+		astring = "IPv4";
+		proto = SOLARIS_IPV4;
+	} else {
+		sock = sock6;
+		astring = "IPv6";
+		proto = SOLARIS_IPV6;
+	}
+
+	if ((netdev->plumbed & proto) != 0)
+		return (0);
+
+	error = solaris_plumb_if(sock, netdev_name, af);
+	if (error != 0) {
+		VLOG_ERR("%s device could not be plumbed", netdev_name);
+		return (error);
+	}
+	VLOG_DBG("%s device plumbed for %s", netdev_name, astring);
+
+	netdev->implicitly_plumbed |= proto;
+	netdev->plumbed |= proto;
+	return (0);
+}
+
+static int
+netdev_solaris_unplumb(const struct netdev *netdev_, sa_family_t af)
+{
+	struct netdev_solaris	*netdev = netdev_solaris_cast(netdev_);
+	const char		*netdev_name = netdev_get_name(netdev_);
+	char			*astring;
+	int			sock;
+	int			proto;
+	int			error;
+
+	ovs_assert(af == AF_INET || af == AF_INET6);
+
+	if (af == AF_INET) {
+		sock = sock4;
+		astring = "IPv4";
+		proto = SOLARIS_IPV4;
+	} else {
+		sock = sock6;
+		astring = "IPv6";
+		proto = SOLARIS_IPV6;
+	}
+
+	if ((netdev->implicitly_plumbed & proto) == 0)
+		return (0);
+
+	error = solaris_unplumb_if(sock, netdev_name, af);
+	if (error != 0) {
+		VLOG_ERR("%s device could not be unplumbed", netdev_name);
+		return (error);
+	}
+	VLOG_ERR("%s device unplumbed for %s", netdev_name, astring);
+	netdev->implicitly_plumbed &= ~proto;
+	netdev->plumbed &= ~proto;
+
+	return (0);
+}
+
+struct solaris_netdev_sdmap {
+	const char *sns_val;
+	enum netdev_features sns_feature;
+};
+
+static int
+netdev_solaris_chk_speed_duplex(const char *netdev_name,
+    const char *field_name, enum netdev_features *features)
+{
+	struct solaris_netdev_sdmap map [] = {
+		{"10G-f", NETDEV_F_10GB_FD},
+		{"1G-f", NETDEV_F_1GB_FD},
+		{"1G-h", NETDEV_F_1GB_HD},
+		{"100M-f", NETDEV_F_100MB_FD},
+		{"100M-h", NETDEV_F_100MB_HD},
+		{"10M-f", NETDEV_F_100MB_FD},
+		{"10M-h", NETDEV_F_10MB_HD},
+		{"40G-f", NETDEV_F_40GB_FD},
+		{"100G-f", NETDEV_F_100GB_FD},
+		{"1T-f", NETDEV_F_1TB_FD},
+		{NULL, 	NETDEV_F_OTHER},
+	};
+	struct solaris_netdev_sdmap *snsp;
+	char	buffer[DLADM_PROP_VAL_MAX];
+	int	i;
+	int	error;
+
+	error = solaris_get_dlprop(netdev_name, "speed-duplex", field_name,
+	    buffer, sizeof (buffer));
+	if (error != 0) {
+		VLOG_ERR("Unable to retrieve feature speed-duplex for %s "
+		    "device", netdev_name);
+		return (error);
+	}
+	snsp = map;
+	for (i = 0; snsp[i].sns_val != NULL; i++) {
+		if (strstr(buffer, snsp[i].sns_val) != NULL)
+			*features |= snsp[i].sns_feature;
+	}
+	return (0);
+}
+
+static int
+netdev_solaris_read_features(struct netdev_solaris *netdev,
+    const char *netdev_name)
+{
+	char	buffer[DLADM_PROP_VAL_MAX];
+	int	error = 0;
+	int	speed;
+	bool	full;
+
+	if (netdev->cache_valid & VALID_FEATURES)
+		goto exit;
+
+	/*
+	 * Supported features
+	 *
+	 * Unsupported features:
+	 * NETDEV_F_COPPER
+	 * NETDEV_F_FIBER
+	 * NETDEV_F_PAUSE;
+	 * NETDEV_F_PAUSE_ASYM;
+	 */
+	netdev->supported = 0;
+	if ((error = netdev_solaris_chk_speed_duplex(netdev_name,
+	    "possible", &netdev->supported)) != 0)
+		goto exit;
+	/*
+	 * Advertised features.
+	 *
+	 * Unsupported features:
+	 * NETDEV_F_COPPER
+	 * NETDEV_F_FIBER
+	 * NETDEV_F_PAUSE;
+	 * NETDEV_F_PAUSE_ASYM;
+	 */
+	netdev->advertised = 0;
+	if ((error = netdev_solaris_chk_speed_duplex(netdev_name,
+	    "current", &netdev->advertised)) != 0)
+		goto exit;
+
+	/* Current settings. */
+	error = solaris_get_dlprop(netdev_name, "speed", "current", buffer,
+	    sizeof (buffer));
+	if (error != 0) {
+		VLOG_ERR("Unable to retrieve speed for %s device",
+		    netdev_name);
+		goto exit;
+	}
+	speed = atoi(buffer);
+
+	error = solaris_get_dlprop(netdev_name, "duplex", "current", buffer,
+	    sizeof (buffer));
+	if (error != 0) {
+		VLOG_ERR("Unable to retrieve duplex for %s device",
+		    netdev_name);
+		goto exit;
+	}
+	full = (strcmp(buffer, "full") == 0);
+
+	speed /= 1000000;
+	if (speed == 10) {
+		netdev->current = full ? NETDEV_F_10MB_FD : NETDEV_F_10MB_HD;
+	} else if (speed == 100) {
+		netdev->current = full ? NETDEV_F_100MB_FD : NETDEV_F_100MB_HD;
+	} else if (speed == 1000) {
+		netdev->current = full ? NETDEV_F_1GB_FD : NETDEV_F_1GB_HD;
+	} else if (speed == 10000) {
+		netdev->current = NETDEV_F_10GB_FD;
+	} else if (speed == 40000) {
+		netdev->current = NETDEV_F_40GB_FD;
+	} else if (speed == 100000) {
+		netdev->current = NETDEV_F_100GB_FD;
+	} else if (speed == 1000000) {
+		netdev->current = NETDEV_F_1TB_FD;
+	} else {
+		netdev->current = 0;
+	}
+	netdev->advertised |= (netdev->supported & NETDEV_F_AUTONEG);
+	netdev->cache_valid |= VALID_FEATURES;
+exit:
+	netdev->get_features_error = error;
+	return (error);
+}
+
+static int
+netdev_solaris_init(void)
+{
+	int error;
+
+	sock4 = socket(AF_INET, SOCK_DGRAM, 0);
+	if (sock4 < 0) {
+		error = errno;
+		VLOG_ERR("failed to create AF_INET socket (%s)",
+		    ovs_strerror(error));
+		return (error);
+	}
+
+	sock6 = socket(AF_INET6, SOCK_DGRAM, 0);
+	if (sock6 < 0) {
+		error = errno;
+		VLOG_ERR("failed to create AF_INET6 socket (%s)",
+		    ovs_strerror(error));
+		return (error);
+	}
+
+	error = solaris_init_rad();
+	return (error);
+}
+
+static void
+netdev_solaris_run(void)
+{
+}
+
+static void
+netdev_solaris_wait(void)
+{
+}
+
+static struct netdev *
+netdev_solaris_alloc(void)
+{
+	struct netdev_solaris *netdev = xzalloc(sizeof (*netdev));
+
+	return (&netdev->up);
+}
+
+static int
+netdev_solaris_unconfigure_uplink(const struct netdev *netdev_)
+{
+	struct netdev_solaris	*netdev = netdev_solaris_cast(netdev_);
+	const char		*netdev_name = netdev_get_name(netdev_);
+	int			error;
+
+	VLOG_DBG("netdev_solaris_unconfigure_uplink device %s", netdev_name);
+
+	error = solaris_modify_vnic(NETDEV_IMPL_ETHERSTUB, netdev->brname);
+	if (error != 0 && error != ENODEV) {
+		VLOG_ERR("failed to unconfigure %s as uplink for %s: "
+		    "%s", netdev_name, netdev->brname,
+		    ovs_strerror(error));
+	}
+	return (0);
+}
+
+static int
+netdev_solaris_configure_uplink(const struct netdev *netdev_,
+    const char *brname)
+{
+	struct netdev_solaris	*netdev = netdev_solaris_cast(netdev_);
+	const char		*netdev_name = netdev_get_name(netdev_);
+	char			buffer[DLADM_PROP_VAL_MAX];
+	int			error;
+
+	VLOG_DBG("netdev_solaris_configure_uplink device %s", netdev_name);
+
+	/*
+	 * Normally, we would expect to see that the bridge VNIC has already
+	 * been created by a call to netdev_solaris_construct() to create the
+	 * bridge netdev. However, on a service restart ovs-vswitchd sometimes
+	 * adds the lower-link port prior to adding the bridge's internal port.
+	 * As a result, we need to create the bridge VNIC here if it does not
+	 * already exist.
+	 */
+	error = solaris_get_dlprop(brname, "lower-link", "current",
+	    buffer, sizeof (buffer));
+	if (error == ENODEV) {
+		VLOG_DBG("%s vnic being created on %s",
+		    brname, netdev_name);
+		error = solaris_create_vnic(netdev_name, brname);
+		if (error != 0) {
+			VLOG_ERR("Failed to create vnic for %s: %s",
+			    brname, ovs_strerror(error));
+		}
+		goto exit;
+	} else if (error != 0) {
+		VLOG_ERR("Failed to get the lower-link for %s: %s",
+		    brname, ovs_strerror(error));
+		goto exit;
+	}
+
+	/*
+	 * If the lower-link is already set correctly, then return with
+	 * success.
+	 */
+	if (strcmp(buffer, netdev_name) == 0) {
+		error = 0;
+		goto exit;
+	}
+
+	/*
+	 * If the lower-link is already set to something other than the
+	 * etherstub, something is wrong.
+	 */
+	if (strcmp(buffer, NETDEV_IMPL_ETHERSTUB) != 0) {
+		VLOG_ERR("Bridge already has uplink %s", buffer);
+		error = EEXIST;
+		goto exit;
+	}
+
+	/*
+	 * This is the "normal" case, where the bridge VNIC existed and had
+	 * the etherstub as its lower-link. Move the VNIC to its uplink.
+	 */
+	error = solaris_modify_vnic(netdev_name, brname);
+	if (error != 0) {
+		VLOG_ERR("failed to configure %s as uplink: %s",
+		    netdev_name, ovs_strerror(error));
+		goto exit;
+	}
+	(void) strlcpy(netdev->brname, brname, sizeof (netdev->brname));
+
+exit:
+	return (error);
+}
+
+static boolean_t
+solaris_is_if_plumbed(struct netdev *netdev_, sa_family_t af, uint64_t *flags)
+{
+	struct netdev_solaris	*netdev = netdev_solaris_cast(netdev_);
+	const char		*netdev_name = netdev_get_name(netdev_);
+	int			sock;
+	int			proto;
+	int			error;
+	char			*astring;
+
+	ovs_assert(af == AF_INET || af == AF_INET6);
+
+	if (af == AF_INET) {
+		sock = sock4;
+		astring = "IPv4";
+		proto = SOLARIS_IPV4;
+	} else {
+		sock = sock6;
+		astring = "IPv6";
+		proto = SOLARIS_IPV6;
+	}
+	error = solaris_if_enabled(sock, netdev_name, flags);
+	if (error == 0) {
+		netdev->plumbed |= proto;
+	} else if (error != ENXIO) {
+		VLOG_DBG("netdev_is_if_plumbed %s device encountered "
+		    "error %d for %s", netdev_name, error, astring);
+	}
+	return (error == 0);
+}
+
+int
+netdev_create_impl_etherstub(void)
+{
+	int error;
+
+	error = solaris_create_etherstub(NETDEV_IMPL_ETHERSTUB);
+	if (error != 0) {
+		VLOG_ERR("netdev_create_impl_etherstub: failed to create %s: "
+		    "%s", NETDEV_IMPL_ETHERSTUB, ovs_strerror(error));
+
+	} else {
+		boolean_t bval = B_TRUE;
+
+		error = solaris_set_dlprop_boolean(NETDEV_IMPL_ETHERSTUB,
+		    "openvswitch", &bval);
+		if (error != 0) {
+			(void) solaris_delete_etherstub(NETDEV_IMPL_ETHERSTUB);
+			VLOG_ERR("netdev_create_impl_etherstub: failed to "
+			    "set 'openvswitch' property on %s: %s: ",
+			    NETDEV_IMPL_ETHERSTUB, ovs_strerror(error));
+		}
+	}
+	return (error);
+}
+
+void
+netdev_delete_impl_etherstub(void)
+{
+	boolean_t	bval = B_FALSE;
+	int		error;
+
+	error = solaris_set_dlprop_boolean(NETDEV_IMPL_ETHERSTUB,
+	    "openvswitch", &bval);
+	if (error != 0) {
+		VLOG_ERR("netdev_create_impl_etherstub: failed to "
+		    "set 'openvswitch' property on %s: %s: ",
+		    NETDEV_IMPL_ETHERSTUB, ovs_strerror(error));
+	} else {
+		error = solaris_delete_etherstub(NETDEV_IMPL_ETHERSTUB);
+		if (error != 0) {
+			VLOG_ERR("netdev_delete_impl_etherstub: failed to "
+			    "delete %s: %s", NETDEV_IMPL_ETHERSTUB,
+			    ovs_strerror(error));
+		}
+	}
+}
+
+boolean_t
+netdev_impl_etherstub_exists(void)
+{
+	return (solaris_etherstub_exists(NETDEV_IMPL_ETHERSTUB));
+}
+
+/*
+ * Creates system and internal devices. Really very little to
+ * do here given that neither system or internal devices must
+ * exist in the kernel yet.
+ */
+static int
+netdev_solaris_construct(struct netdev *netdev_)
+{
+	struct netdev_solaris	*netdev = netdev_solaris_cast(netdev_);
+	const char		*netdev_name = netdev_get_name(netdev_);
+	char			buffer[DLADM_PROP_VAL_MAX];
+	uint64_t		flags;
+	int			error;
+
+	ovs_mutex_init(&netdev->mutex);
+	if (netdev->up.netdev_class != &netdev_internal_class) {
+		VLOG_DBG("netdev_solaris_construct system device %s",
+		    netdev_name);
+	} else {
+		VLOG_DBG("netdev_solaris_construct internal device %s",
+		    netdev_name);
+	}
+	netdev->implicitly_plumbed = 0;
+	netdev->plumbed = 0;
+
+	(void) solaris_is_if_plumbed(netdev_, AF_INET, &flags);
+	(void) solaris_is_if_plumbed(netdev_, AF_INET6, &flags);
+
+	/*
+	 * loopback is illegal.
+	 */
+	if (netdev->plumbed != 0 && flags & NETDEV_LOOPBACK) {
+		VLOG_ERR("%s: cannot add a loopback device",
+		    netdev_name);
+		return (EINVAL);
+	}
+
+	if (netdev->up.netdev_class == &netdev_internal_class) {
+		error = solaris_get_dlprop(netdev_name, "lower-link",
+		    "current", buffer, sizeof (buffer));
+		if (error == ENODEV) {
+			error = solaris_create_vnic(NETDEV_IMPL_ETHERSTUB,
+			    netdev_name);
+			if (error != 0) {
+				VLOG_ERR("failed to configure %s as uplink: "
+				"%s", netdev_name, ovs_strerror(error));
+				return (error);
+			}
+		} else if (error != 0) {
+			VLOG_ERR("Failed to get the lower-link for %s: %s",
+			    netdev_name, ovs_strerror(error));
+		}
+	}
+	return (0);
+}
+
+static void
+netdev_solaris_destruct(struct netdev *netdev_)
+{
+	struct netdev_solaris	*netdev = netdev_solaris_cast(netdev_);
+	const char		*netdev_name = netdev_get_name(netdev_);
+	int			error;
+
+	VLOG_DBG("netdev_solaris_destruct device %s", netdev_name);
+
+	/*
+	 * If implicitly plumbed, then unplumb it.
+	 */
+	(void) netdev_solaris_unplumb(netdev_, AF_INET);
+	(void) netdev_solaris_unplumb(netdev_, AF_INET6);
+
+	if (netdev->up.netdev_class == &netdev_internal_class) {
+		error = solaris_delete_vnic(netdev_name);
+		if (error != 0) {
+			VLOG_ERR("failed to delete %s: %s",
+			    netdev_name, ovs_strerror(error));
+		}
+	} else {
+		if (netdev->brname[0] != '\0') {
+			netdev_solaris_unconfigure_uplink(netdev_);
+		}
+	}
+
+	ovs_mutex_destroy(&netdev->mutex);
+}
+
+static void
+netdev_solaris_dealloc(struct netdev *netdev_)
+{
+	struct netdev_solaris *netdev = netdev_solaris_cast(netdev_);
+
+	free(netdev);
+}
+
+/*
+ * Attempts to set 'netdev''s MAC address to 'mac'. Returns 0 if successful,
+ * otherwise a positive errno value.
+ */
+static int
+netdev_solaris_set_etheraddr(struct netdev *netdev_,
+    const uint8_t mac[ETH_ADDR_LEN])
+{
+	struct netdev_solaris	*netdev = netdev_solaris_cast(netdev_);
+	const char		*netdev_name = netdev_get_name(netdev_);
+	char			buffer[128];
+	int			error = 0;
+
+	VLOG_DBG("netdev_solaris_set_etheraddr device %s", netdev_name);
+
+	ovs_mutex_lock(&netdev->mutex);
+	if ((netdev->cache_valid & VALID_ETHERADDR) &&
+	    (eth_addr_equals(netdev->etheraddr, mac))) {
+		goto exit;
+	}
+
+	/*
+	 * In case there is some kind of failure, invalidate
+	 * the cached value.
+	 */
+	netdev->cache_valid &= ~VALID_ETHERADDR;
+
+	/*
+	 * MAC addresses are datalink properties.
+	 */
+	(void) snprintf(buffer, sizeof (buffer), ETH_ADDR_FMT,
+	    ETH_ADDR_ARGS(mac));
+	error = solaris_set_dlprop_string(netdev_name, "mac-address", buffer);
+	if (error != 0) {
+		VLOG_ERR("set etheraddr %s on %s device failed: %s",
+		    buffer, netdev_name, ovs_strerror(error));
+		goto exit;
+	}
+
+	memcpy(netdev->etheraddr, mac, ETH_ADDR_LEN);
+	netdev->cache_valid |= VALID_ETHERADDR;
+	netdev_change_seq_changed(netdev_);
+
+exit:
+	ovs_mutex_unlock(&netdev->mutex);
+	return (error);
+}
+
+static int
+netdev_solaris_get_dlclass(const struct netdev *netdev_)
+{
+	struct netdev_solaris	*netdev = netdev_solaris_cast(netdev_);
+	const char		*netdev_name = netdev_get_name(netdev_);
+	char			buffer[DLADM_PROP_VAL_MAX];
+	int			error = 0;
+
+	if (netdev->cache_valid & VALID_LINKCLASS)
+		goto exit;
+
+	/*
+	 * Get the datalink class for this link.
+	 */
+	error = solaris_get_dlclass(netdev_name, buffer, sizeof (buffer));
+	if (error != 0) {
+		VLOG_ERR("Unable to retrieve linkclass for %s device, %d",
+		    netdev_name, error);
+		goto exit;
+	}
+
+	(void) strlcpy(netdev->class, buffer, sizeof (netdev->class));
+	netdev->cache_valid |= VALID_LINKCLASS;
+exit:
+	return (error);
+}
+
+/*
+ * Determines if this netdev is an uplink for the bridge.
+ */
+static bool
+netdev_solaris_is_uplink(const struct netdev *netdev_)
+{
+	struct netdev_solaris	*netdev = netdev_solaris_cast(netdev_);
+	const char		*netdev_name = netdev_get_name(netdev_);
+
+	VLOG_DBG("netdev_solaris_is_uplink device %s", netdev_name);
+	if (netdev_solaris_get_dlclass(netdev_) != 0)
+		return (false);
+	return (solaris_is_uplink_class(netdev->class));
+}
+
+static int
+netdev_solaris_get_etheraddr(const struct netdev *netdev_,
+    uint8_t mac[ETH_ADDR_LEN])
+{
+	struct netdev_solaris	*netdev = netdev_solaris_cast(netdev_);
+	const char		*netdev_name = netdev_get_name(netdev_);
+	char			buffer[DLADM_PROP_VAL_MAX];
+	int			error = 0;
+
+	VLOG_DBG("netdev_solaris_get_etheraddr device %s", netdev_name);
+
+	ovs_mutex_lock(&netdev->mutex);
+	/*
+	 * Only the bridge ethernet address can be changed outside the
+	 * knowledge of vswitchd (when the uplink is added and the bridge
+	 * VNIC is moved to the uplink).
+	 */
+	if ((netdev->cache_valid & VALID_ETHERADDR) &&
+	    (netdev->up.netdev_class != &netdev_internal_class)) {
+		memcpy(mac, netdev->etheraddr, ETH_ADDR_LEN);
+		goto exit;
+	}
+
+	/*
+	 * MAC addresses are datalink properties.
+	 */
+	error = solaris_get_dlprop(netdev_name, "mac-address", "current",
+	    buffer, sizeof (buffer));
+	if (error != 0) {
+		VLOG_ERR("Unable to retrieve etheraddr for %s device",
+		    netdev_name);
+		goto exit;
+	}
+
+	if (!eth_addr_from_string(buffer, mac)) {
+		VLOG_ERR("Invalid etheraddr for %s device", netdev_name);
+		error = EINVAL;
+		goto exit;
+	}
+
+	memcpy(netdev->etheraddr, mac, ETH_ADDR_LEN);
+	netdev->cache_valid |= VALID_ETHERADDR;
+exit:
+	ovs_mutex_unlock(&netdev->mutex);
+	return (error);
+}
+
+static int
+netdev_solaris_get_mtu__(struct netdev_solaris *netdev, int *mtup,
+    const char *netdev_name)
+{
+	char	buffer[DLADM_PROP_VAL_MAX];
+	int	mtu;
+	int	error = 0;
+
+	if (netdev->cache_valid & VALID_MTU) {
+		*mtup = netdev->mtu;
+		return (0);
+	}
+
+	/*
+	 * MTU is a datalink property
+	 */
+	error = solaris_get_dlprop(netdev_name, "mtu", "current", buffer,
+	    sizeof (buffer));
+	if (error != 0) {
+		VLOG_ERR("Unable to retrieve mtu for %s device",
+		    netdev_name);
+		return (error);
+	}
+
+	mtu = atoi(buffer);
+	if (mtu == 0) {
+		VLOG_ERR("Invalid mtu for %s device", netdev_name);
+		error = EINVAL;
+		return (error);
+	}
+
+	netdev->mtu = *mtup = mtu;
+	netdev->cache_valid |= VALID_MTU;
+	return (0);
+}
+
+/*
+ * Returns the maximum size of transmitted (and received) packets on 'netdev',
+ * in bytes, not including the hardware header; thus, this is typically 1500
+ * bytes for Ethernet devices.
+ */
+static int
+netdev_solaris_get_mtu(const struct netdev *netdev_, int *mtup)
+{
+	struct netdev_solaris	*netdev = netdev_solaris_cast(netdev_);
+	const char		*netdev_name = netdev_get_name(netdev_);
+	int			error = 0;
+
+	VLOG_DBG("netdev_solaris_get_mtu device %s", netdev_name);
+
+	if (!netdev_) {
+		VLOG_DBG("netdev_solaris_get_mtu null netdevs");
+		return (error);
+	}
+
+	ovs_mutex_lock(&netdev->mutex);
+	error = netdev_solaris_get_mtu__(netdev, mtup, netdev_name);
+	ovs_mutex_unlock(&netdev->mutex);
+	return (error);
+}
+
+/*
+ * Sets the maximum size of transmitted (MTU) for given device.
+ */
+static int
+netdev_solaris_set_mtu(const struct netdev *netdev_ OVS_UNUSED, int mtu)
+{
+	struct netdev_solaris	*netdev = netdev_solaris_cast(netdev_);
+	const char		*netdev_name = netdev_get_name(netdev_);
+	uint64_t		ulval = mtu;
+	int			error = 0;
+
+	VLOG_DBG("netdev_solaris_set_mtu device %s", netdev_name);
+
+	ovs_mutex_lock(&netdev->mutex);
+	if ((netdev->cache_valid & VALID_MTU && netdev->mtu == mtu))
+		goto exit;
+
+	/*
+	 * In case there is some kind of failure, invalidate
+	 * the cached value.
+	 */
+	netdev->cache_valid &= ~VALID_MTU;
+
+	/*
+	 * MTU is a datalink property.
+	 */
+	error = solaris_set_dlprop_ulong(netdev_name, "mtu", &ulval);
+	if (error != 0) {
+		VLOG_ERR("set mtu on %s device failed: %s",
+		    netdev_name, ovs_strerror(error));
+		goto exit;
+	}
+
+	netdev->mtu = mtu;
+	netdev->cache_valid |= VALID_MTU;
+	netdev_change_seq_changed(netdev_);
+
+exit:
+	ovs_mutex_unlock(&netdev->mutex);
+	return (error);
+}
+
+/*
+ * Returns the ifindex of 'netdev', if successful, as a positive number.
+ * On failure, returns a negative errno.
+ */
+static int
+netdev_solaris_get_ifindex(const struct netdev *netdev_)
+{
+	struct netdev_solaris 	*netdev = netdev_solaris_cast(netdev_);
+	const char		*netdev_name = netdev_get_name(netdev_);
+	int			ifindex;
+	int			error = 0;
+
+	VLOG_DBG("netdev_solaris_get_ifindex device %s", netdev_name);
+
+	ovs_mutex_lock(&netdev->mutex);
+	if (netdev->cache_valid & VALID_IFINDEX) {
+		ifindex = netdev->ifindex;
+		goto exit;
+	}
+
+	ifindex = (int)if_nametoindex(netdev_name);
+	if (ifindex <= 0) {
+		error = errno;
+		goto exit;
+	}
+
+	netdev->ifindex = ifindex;
+	netdev->cache_valid |= VALID_IFINDEX;
+
+exit:
+	ovs_mutex_unlock(&netdev->mutex);
+	return (error ? -error : ifindex);
+}
+
+/*
+ * Retrieves current device stats for 'netdev-solaris'.
+ */
+static int
+netdev_solaris_get_stats(const struct netdev *netdev_,
+    struct netdev_stats *stats)
+{
+	struct netdev_solaris	*netdev = netdev_solaris_cast(netdev_);
+	const char		*netdev_name = netdev_get_name(netdev_);
+	kstat2_status_t		stat;
+	kstat2_map_t		map;
+	char			kuri[1024];
+	char			devname[DLADM_PROP_VAL_MAX];
+	char			modname[DLADM_PROP_VAL_MAX];
+	char			kstat_name[MAXLINKNAMELEN];
+	char			*name;
+	zoneid_t		zid;
+	uint_t			instance;
+	int			error = 0;
+	boolean_t		is_uplink;
+
+	VLOG_DBG("netdev_solaris_get_stats device %s", netdev_name);
+
+	is_uplink = netdev_solaris_is_uplink(netdev_);
+
+	/*
+	 * Initialize statistics only supported on uplink.
+	 */
+	stats->rx_length_errors = 0;
+	stats->rx_missed_errors = 0;
+	stats->rx_over_errors = 0;
+
+	/*
+	 * Unsupported on Solaris.
+	 */
+	stats->rx_crc_errors = UINT64_MAX;
+	stats->rx_frame_errors = UINT64_MAX;
+	stats->rx_fifo_errors = UINT64_MAX;
+	stats->tx_aborted_errors = UINT64_MAX;
+	stats->tx_carrier_errors = UINT64_MAX;
+	stats->tx_fifo_errors = UINT64_MAX;
+	stats->tx_heartbeat_errors = UINT64_MAX;
+	stats->tx_window_errors	= UINT64_MAX;
+
+	ovs_mutex_lock(&kstat_mutex);
+	if (!kstat2_handle_initialized) {
+		VLOG_DBG("netdev_solaris_get_stats initializing handle");
+		if (!kstat_handle_init(&nd_khandle)) {
+			error = -1;
+			goto done;
+		}
+		kstat2_handle_initialized = B_TRUE;
+	} else if (!kstat_handle_update(nd_khandle)) {
+		VLOG_DBG("netdev_solaris_get_stats error updating stats");
+		kstat_handle_close(&nd_khandle);
+		kstat2_handle_initialized = B_FALSE;
+		error = -1;
+		goto done;
+	}
+	name = (char *)netdev_name;
+	instance = 0;
+	if (strchr(netdev_name, '/') != NULL) {
+		(void) solaris_dlparse_zonelinkname(netdev_name, kstat_name,
+		    &zid);
+		name = kstat_name;
+		instance = zid;
+	}
+	(void) snprintf(kuri, sizeof (kuri), "kstat:/net/link/%s/%d",
+	    name, instance);
+	stat = kstat2_lookup_map(nd_khandle, kuri, &map);
+	if (stat != KSTAT2_S_OK) {
+		VLOG_WARN("kstat2_lookup_map of %s failed: %s", kuri,
+		    kstat2_status_string(stat));
+	} else {
+		if (is_uplink) {
+			stats->rx_packets = get_nvvt_int(map, "ipackets64");
+			stats->tx_packets = get_nvvt_int(map, "opackets64");
+			stats->rx_bytes = get_nvvt_int(map, "rbytes");
+			stats->tx_bytes = get_nvvt_int(map, "obytes");
+			stats->rx_errors = get_nvvt_int(map, "ierrors");
+			stats->tx_errors = get_nvvt_int(map, "oerrors");
+		} else {
+			stats->tx_packets = get_nvvt_int(map, "ipackets64");
+			stats->rx_packets = get_nvvt_int(map, "opackets64");
+			stats->tx_bytes = get_nvvt_int(map, "rbytes");
+			stats->rx_bytes = get_nvvt_int(map, "obytes");
+			stats->tx_errors = get_nvvt_int(map, "ierrors");
+			stats->rx_errors = get_nvvt_int(map, "oerrors");
+		}
+		stats->collisions = get_nvvt_int(map, "collisions");
+		stats->multicast = get_nvvt_int(map, "multircv") +
+		    get_nvvt_int(map, "multixmt");
+	}
+
+	(void) snprintf(kuri, sizeof (kuri), "kstat:/net/%s/link/%d",
+	    name, instance);
+	stat = kstat2_lookup_map(nd_khandle, kuri, &map);
+	if (stat != KSTAT2_S_OK) {
+		VLOG_WARN("kstat2_lookup_map of %s failed: %s", kuri,
+		    kstat2_status_string(stat));
+	} else {
+		if (is_uplink) {
+			stats->rx_dropped = get_nvvt_int(map, "idrops");
+			stats->tx_dropped = get_nvvt_int(map, "odrops");
+		} else {
+			stats->tx_dropped = get_nvvt_int(map, "idrops");
+			stats->rx_dropped = get_nvvt_int(map, "odrops");
+		}
+	}
+
+	if (!is_uplink)
+		goto done;
+
+	/*
+	 * Can we do anything for aggr?
+	 */
+	if (strcmp("phys", netdev->class) != 0)
+		goto done;
+
+	if (solaris_get_devname(netdev_name, devname, sizeof (devname)) != 0) {
+		VLOG_WARN("Failed to retrieve devname of uplink\n");
+		error = -1;
+		goto done;
+	}
+
+	if (!dlparse_drvppa(devname, modname, sizeof (modname),
+	    &instance)) {
+		VLOG_DBG("netdev_solaris_get_stats error getting "
+		    "mod/instance for %s\n", devname);
+		error = -1;
+		goto done;
+	}
+
+	(void) snprintf(kuri, sizeof (kuri), "kstat:/net/%s/statistics/%d",
+	    modname, instance);
+	stat = kstat2_lookup_map(nd_khandle, kuri, &map);
+	if (stat != KSTAT2_S_OK) {
+		VLOG_WARN("kstat2_lookup_map of %s failed: %s", kuri,
+		    kstat2_status_string(stat));
+		error = -1;
+		goto done;
+	}
+	stats->rx_length_errors = get_nvvt_int(map, "Recv_Length_Errors");
+	stats->rx_missed_errors = get_nvvt_int(map, "Recv_Missed_Packets");
+	stats->rx_over_errors = get_nvvt_int(map, "Recv_Oversize");
+
+done:
+	ovs_mutex_unlock(&kstat_mutex);
+	return (error);
+}
+
+/*
+ * Stores the features supported by 'netdev' into of '*current', '*advertised',
+ * '*supported', and '*peer'.  Each value is a bitmap of NETDEV_* bits.
+ * Returns 0 if successful, otherwise a positive errno value.
+ */
+static int
+netdev_solaris_get_features(const struct netdev *netdev_,
+    enum netdev_features *current,
+    enum netdev_features *advertised,
+    enum netdev_features *supported,
+    enum netdev_features *peer)
+{
+	struct netdev_solaris	*netdev = netdev_solaris_cast(netdev_);
+	const char		*netdev_name = netdev_get_name(netdev_);
+	const char		*physname;
+	char			buffer[DLADM_PROP_VAL_MAX];
+	int			error = 0;
+
+	VLOG_DBG("netdev_solaris_get_features device %s", netdev_name);
+	ovs_mutex_lock(&netdev->mutex);
+
+	if (netdev_solaris_is_uplink(netdev_)) {
+		physname = netdev_name;
+	} else {
+		error = solaris_get_dllower(netdev_name, buffer,
+		    sizeof (buffer));
+		if (error != 0)
+			goto exit;
+		physname = buffer;
+	}
+
+	error = netdev_solaris_read_features(netdev, physname);
+	if (error != 0)
+		goto exit;
+
+	*current = netdev->current;
+	*advertised = netdev->advertised;
+	*supported = netdev->supported;
+	*peer = 0;
+
+exit:
+	ovs_mutex_unlock(&netdev->mutex);
+	return (error);
+}
+
+/*
+ * Attempts to set input rate limiting (policing) policy.  Returns 0 if
+ * successful, otherwise a positive errno value.
+ */
+static int
+netdev_solaris_set_policing(struct netdev *netdev_,
+    uint32_t kbits_rate OVS_UNUSED, uint32_t kbits_burst OVS_UNUSED)
+{
+	const char	*netdev_name = netdev_get_name(netdev_);
+	int		error = 0;
+
+	VLOG_DBG("netdev_solaris_set_policing device %s", netdev_name);
+
+	/* XXXSolaris check libdladm:setlinkrop maxbw/priority */
+
+	return (error);
+}
+
+static int
+netdev_solaris_get_qos_types(const struct netdev *netdev_,
+    struct sset *types)
+{
+	const char	*netdev_name = netdev_get_name(netdev_);
+	const struct tc_ops *const *opsp;
+
+	VLOG_DBG("netdev_solaris_get_qos_types device %s", netdev_name);
+
+	/*
+	 * XXXSolaris we will support only HTB-equivalent which will translate
+	 * to maxbw/priority.
+	 */
+	for (opsp = tcs; *opsp != NULL; opsp++) {
+		const struct tc_ops	*ops = *opsp;
+
+		if (ops->tc_install && ops->ovs_name[0] != '\0')
+			sset_add(types, ops->ovs_name);
+	}
+	return (0);
+}
+
+static int
+netdev_solaris_get_qos_capabilities(const struct netdev *netdev_,
+    const char *type, struct netdev_qos_capabilities *caps)
+{
+	const char	*netdev_name = netdev_get_name(netdev_);
+	int		error = EOPNOTSUPP;
+	const struct tc_ops *ops = tc_lookup_ovs_name(type);
+
+	VLOG_DBG("netdev_solaris_get_qos_capabilities device %s",
+	    netdev_name);
+
+	if (!ops)
+		return (EOPNOTSUPP);
+
+	/*
+	 * Arbit number for now. In theory we are not limited for bandwidth
+	 * limit. But, with shares and priority this can be revisited.
+	 */
+	caps->n_queues = ops->n_queues;
+
+	return (error);
+}
+
+static int
+netdev_solaris_get_qos(const struct netdev *netdev_,
+    const char **typep, struct smap *details)
+{
+	struct netdev_solaris	*netdev = netdev_solaris_cast(netdev_);
+	const char		*netdev_name = netdev_get_name(netdev_);
+	int			error = 0;
+
+	VLOG_DBG("netdev_solaris_get_qos device %s", netdev_name);
+
+	ovs_mutex_lock(&netdev->mutex);
+	error = tc_query_qdisc(netdev_);
+	if (!error) {
+		*typep = netdev->tc->ops->ovs_name;
+		error = (netdev->tc->ops->qdisc_get ?
+		    netdev->tc->ops->qdisc_get(netdev_, details) : 0);
+	}
+	ovs_mutex_unlock(&netdev->mutex);
+	return (error);
+}
+
+static int
+netdev_solaris_set_qos(struct netdev *netdev_,
+    const char *type, const struct smap *details)
+{
+	struct netdev_solaris	*netdev = netdev_solaris_cast(netdev_);
+	const char		*netdev_name = netdev_get_name(netdev_);
+	const struct tc_ops	*new_ops;
+	int			error = 0;
+
+	VLOG_DBG("netdev_solaris_set_qos device %s", netdev_name);
+	new_ops = tc_lookup_ovs_name(type);
+	if (!new_ops) {
+		VLOG_DBG("netdev_solaris_set_qos type %s not found", type);
+		return (EOPNOTSUPP);
+	}
+	ovs_mutex_lock(&netdev->mutex);
+	error = tc_query_qdisc(netdev_);
+	if (error) {
+		VLOG_DBG("netdev_solaris_set_qos qdisc querry failed");
+		goto exit;
+	}
+
+	if (new_ops == netdev->tc->ops) {
+		error = new_ops->qdisc_set ?
+		    new_ops->qdisc_set(netdev_, details) :
+		    0;
+	} else {
+		error = tc_del_qdisc(netdev_);
+		if (error) {
+			VLOG_DBG("netdev_solaris_set_qos error deleting %s",
+			    type);
+			goto exit;
+		}
+		ovs_assert(netdev->tc == NULL);
+		/* Install new qdisc. */
+		error = new_ops->tc_install(netdev_, details);
+		ovs_assert((error == 0) == (netdev->tc != NULL));
+		VLOG_DBG("netdev_solaris_set_qos installed %s, %d", type,
+		    error);
+	}
+exit:
+	ovs_mutex_unlock(&netdev->mutex);
+	return (error);
+}
+
+static int
+netdev_solaris_get_queue(const struct netdev *netdev_,
+    unsigned int queue_id, struct smap *details)
+{
+	struct netdev_solaris	*netdev = netdev_solaris_cast(netdev_);
+	const char		*netdev_name = netdev_get_name(netdev_);
+	int			error = 0;
+
+	VLOG_DBG("netdev_solaris_get_queue device %s", netdev_name);
+
+	ovs_mutex_lock(&netdev->mutex);
+	error = tc_query_qdisc(netdev_);
+	if (!error) {
+		struct tc_queue *queue = tc_find_queue(netdev_, queue_id);
+		error = (queue ?
+		    netdev->tc->ops->class_get(netdev_, queue, details) :
+		    ENOENT);
+	}
+	ovs_mutex_unlock(&netdev->mutex);
+	VLOG_DBG("netdev_solaris_get_queue device %s done %d", netdev_name,
+	    error);
+	return (error);
+}
+
+static int
+netdev_solaris_set_queue(struct netdev *netdev_,
+    unsigned int queue_id, const struct smap *details)
+{
+	struct netdev_solaris	*netdev = netdev_solaris_cast(netdev_);
+	const char		*netdev_name = netdev_get_name(netdev_);
+	int			error = 0;
+
+	VLOG_DBG("netdev_solaris_set_queue device %s", netdev_name);
+
+	ovs_mutex_lock(&netdev->mutex);
+	error = tc_query_qdisc(netdev_);
+	if (!error) {
+		error = (queue_id < netdev->tc->ops->n_queues &&
+		    netdev->tc->ops->class_set ?
+		    netdev->tc->ops->class_set(netdev_, queue_id, details) :
+		    EINVAL);
+	} else {
+		VLOG_DBG("netdev_solaris_set_queue %s: no qdisc", netdev_name);
+	}
+	ovs_mutex_unlock(&netdev->mutex);
+	return (error);
+}
+
+static int
+netdev_solaris_delete_queue(struct netdev *netdev_,
+    unsigned int queue_id)
+{
+	struct netdev_solaris	*netdev = netdev_solaris_cast(netdev_);
+	const char		*netdev_name = netdev_get_name(netdev_);
+	int			error = 0;
+
+	VLOG_DBG("netdev_solaris_delete_queue device %s", netdev_name);
+
+	ovs_mutex_lock(&netdev->mutex);
+	error = tc_query_qdisc(netdev_);
+	if (!error) {
+		if (netdev->tc->ops->class_delete) {
+			struct tc_queue *queue =
+			    tc_find_queue(netdev_, queue_id);
+
+			error = (queue
+			    ? netdev->tc->ops->class_delete(netdev_, queue)
+			    : ENOENT);
+		} else {
+			error = EINVAL;
+		}
+	}
+	ovs_mutex_unlock(&netdev->mutex);
+	return (error);
+}
+
+static int
+netdev_solaris_get_queue_stats(const struct netdev *netdev_,
+    unsigned int queue_id OVS_UNUSED,
+    struct netdev_queue_stats *stats OVS_UNUSED)
+{
+	const char	*netdev_name = netdev_get_name(netdev_);
+	int		error = 0;
+
+	VLOG_DBG("netdev_solaris_set_queue_stats device %s", netdev_name);
+
+	return (error);
+}
+
+struct netdev_solaris_queue_state {
+	unsigned int	*queues;
+	size_t		cur_queue;
+	size_t		n_queues;
+};
+
+static int
+netdev_solaris_queue_dump_start(const struct netdev *netdev_,
+    void **statep)
+{
+	struct netdev_solaris	*netdev = netdev_solaris_cast(netdev_);
+	const char		*netdev_name = netdev_get_name(netdev_);
+	int			error = 0;
+
+	VLOG_DBG("netdev_solaris_queue_dump_start device %s", netdev_name);
+
+	ovs_mutex_lock(&netdev->mutex);
+	error = tc_query_qdisc(netdev_);
+	if (!error) {
+		if (netdev->tc->ops->class_get) {
+			struct netdev_solaris_queue_state *state;
+			struct tc_queue *queue;
+			size_t i;
+
+			*statep = state = xmalloc(sizeof (*state));
+			state->n_queues = hmap_count(&netdev->tc->queues);
+			state->cur_queue = 0;
+			state->queues =
+			    xmalloc(state->n_queues * sizeof (*state->queues));
+
+			i = 0;
+			HMAP_FOR_EACH(queue, hmap_node, &netdev->tc->queues) {
+				state->queues[i++] = queue->queue_id;
+			}
+		} else {
+			error = EOPNOTSUPP;
+		}
+	} else {
+		VLOG_DBG("netdev_solaris_queue_dump_start: no qdisc");
+	}
+	ovs_mutex_unlock(&netdev->mutex);
+
+	return (error);
+}
+
+static int
+netdev_solaris_queue_dump_next(const struct netdev *netdev_,
+    void *state_, unsigned int *queue_idp, struct smap *details)
+{
+	struct netdev_solaris	*netdev = netdev_solaris_cast(netdev_);
+	const char		*netdev_name = netdev_get_name(netdev_);
+	int			error = EOF;
+	struct netdev_solaris_queue_state *state = state_;
+
+	if (!state) {
+		VLOG_DBG("netdev_solaris_queue_dump_next %s: null state\n",
+		    netdev_name);
+		return (EOF);
+	}
+
+	VLOG_DBG("netdev_solaris_queue_dump_next device %s, %"PRIuSIZE
+	    ", %"PRIuSIZE, netdev_name, state->cur_queue, state->n_queues);
+	ovs_mutex_lock(&netdev->mutex);
+	while (state->cur_queue < state->n_queues) {
+		unsigned int queue_id = state->queues[state->cur_queue++];
+		struct tc_queue *queue = tc_find_queue(netdev_, queue_id);
+
+		if (queue) {
+			*queue_idp = queue_id;
+			if (!netdev->tc || !netdev->tc->ops ||
+			    !netdev->tc->ops->class_get) {
+				break;
+			}
+			error = netdev->tc->ops->class_get(netdev_, queue,
+			    details);
+			break;
+		}
+	}
+	ovs_mutex_unlock(&netdev->mutex);
+	return (error);
+}
+
+static int
+netdev_solaris_queue_dump_done(const struct netdev *netdev_,
+    void *state_)
+{
+	const char	*netdev_name = netdev_get_name(netdev_);
+	struct netdev_solaris_queue_state *state = state_;
+
+	VLOG_DBG("netdev_solaris_queue_dump_done device %s", netdev_name);
+	if (state) {
+		if (state->queues)
+			free(state->queues);
+		free(state);
+	}
+	return (0);
+}
+
+static int
+netdev_solaris_dump_queue_stats(const struct netdev *netdev_,
+    netdev_dump_queue_stats_cb *cb OVS_UNUSED, void *aux OVS_UNUSED)
+{
+	const char	*netdev_name = netdev_get_name(netdev_);
+	int		error = 0;
+
+	VLOG_DBG("netdev_solaris_queue_dump_stats device %s", netdev_name);
+
+	return (error);
+}
+
+/*
+ * If 'netdev' has an assigned IPv4 address, sets '*address' to that
+ * address and '*netmask' to the associated netmask. Otherwise, returns
+ * errno.
+ */
+static int
+netdev_solaris_get_in4(const struct netdev *netdev_,
+    struct in_addr *address, struct in_addr *netmask)
+{
+	struct netdev_solaris	*netdev = netdev_solaris_cast(netdev_);
+	const char		*netdev_name = netdev_get_name(netdev_);
+	struct lifreq		lifr;
+	const struct sockaddr_in *sin;
+	int			error = 0;
+
+	VLOG_DBG("netdev_solaris_get_in4 device %s", netdev_name);
+
+	ovs_mutex_lock(&netdev->mutex);
+	if (netdev->cache_valid & VALID_IN4) {
+		*address = netdev->in4;
+		*netmask = netdev->netmask;
+		goto exit;
+	}
+
+	error = netdev_solaris_plumb(netdev_, AF_INET);
+	if (error != 0)
+		goto exit;
+
+	/*
+	 * In the future, a RAD IP module might be a good provider
+	 * of this information. For now, use the SIOCGLIFADDR.
+	 */
+	bzero(&lifr, sizeof (lifr));
+	(void) strncpy(lifr.lifr_name, netdev_name, sizeof (lifr.lifr_name));
+	sin = ALIGNED_CAST(struct sockaddr_in *, &lifr.lifr_addr);
+	if (ioctl(sock4, SIOCGLIFADDR, &lifr) < 0) {
+		error = errno;
+		goto exit;
+	}
+	netdev->in4 = sin->sin_addr;
+
+	if (ioctl(sock4, SIOCGLIFNETMASK, (caddr_t)&lifr) < 0) {
+		error = errno;
+		goto exit;
+	}
+	netdev->netmask = sin->sin_addr;
+	netdev->cache_valid |= VALID_IN4;
+
+	if (netdev->in4.s_addr != INADDR_ANY) {
+		*address = netdev->in4;
+		*netmask = netdev->netmask;
+	} else {
+		error = EADDRNOTAVAIL;
+	}
+
+exit:
+	ovs_mutex_unlock(&netdev->mutex);
+	return (error);
+}
+
+static int
+netdev_solaris_set_in4(struct netdev *netdev_, struct in_addr address,
+    struct in_addr netmask)
+{
+	struct netdev_solaris	*netdev = netdev_solaris_cast(netdev_);
+	const char		*netdev_name = netdev_get_name(netdev_);
+	struct lifreq		lifr;
+	struct sockaddr_in	*sin;
+	int			error = 0;
+
+	VLOG_DBG("netdev_solaris_set_in4 device %s", netdev_name);
+
+	ovs_mutex_lock(&netdev->mutex);
+
+	error = netdev_solaris_plumb(netdev_, AF_INET);
+	if (error != 0)
+		goto exit;
+
+	/*
+	 * In the future, a RAD IP module might be a good provider
+	 * of this information. For now, use the SIOCSLIFADDR.
+	 */
+	bzero(&lifr, sizeof (lifr));
+	(void) strncpy(lifr.lifr_name, netdev_name, sizeof (lifr.lifr_name));
+	sin = ALIGNED_CAST(struct sockaddr_in *, &lifr.lifr_addr);
+	sin->sin_addr = address;
+	sin->sin_family = AF_INET;
+	if (ioctl(sock4, SIOCSLIFADDR, &lifr) < 0) {
+		error = errno;
+		goto exit;
+	}
+	if (address.s_addr == htonl(INADDR_ANY))
+		goto done;
+
+	sin->sin_addr = netmask;
+	if (ioctl(sock4, SIOCSLIFNETMASK, (caddr_t)&lifr) < 0) {
+		error = errno;
+		goto done;
+	}
+	netdev->in4 = address;
+	netdev->netmask = netmask;
+	netdev->cache_valid |= VALID_IN4;
+
+done:
+	netdev_change_seq_changed(netdev_);
+exit:
+	ovs_mutex_unlock(&netdev->mutex);
+	return (error);
+}
+
+/*
+ * If 'netdev' has an assigned IPv6 address, sets '*address' to that
+ * address. Otherwise, returns errno.
+ */
+static int
+netdev_solaris_get_in6(const struct netdev *netdev_, struct in6_addr *address)
+{
+	struct netdev_solaris	*netdev = netdev_solaris_cast(netdev_);
+	const char		*netdev_name = netdev_get_name(netdev_);
+	struct lifreq		lifr;
+	const struct sockaddr_in6 *sin6;
+	int			error = 0;
+
+	VLOG_DBG("netdev_solaris_get_in6 device %s", netdev_name);
+
+	ovs_mutex_lock(&netdev->mutex);
+	if (netdev->cache_valid & VALID_IN6) {
+		if (!IN6_IS_ADDR_UNSPECIFIED(address))
+			*address = netdev->in6;
+		else
+			error = EADDRNOTAVAIL;
+		goto exit;
+	}
+
+	error = netdev_solaris_plumb(netdev_, AF_INET6);
+	if (error != 0)
+		goto exit;
+
+	/*
+	 * In the future, a RAD IP module might be a good provider
+	 * of this information. For now, use the SIOCGLIFADDR.
+	 */
+	bzero(&lifr, sizeof (lifr));
+	(void) strncpy(lifr.lifr_name, netdev_name, sizeof (lifr.lifr_name));
+	if (ioctl(sock6, SIOCGLIFADDR, &lifr) < 0) {
+		error = errno;
+		goto exit;
+	}
+	sin6 = ALIGNED_CAST(struct sockaddr_in6 *, &lifr.lifr_addr);
+	netdev->in6 = sin6->sin6_addr;
+	netdev->cache_valid |= VALID_IN6;
+
+	if (!IN6_IS_ADDR_UNSPECIFIED(address))
+		*address = netdev->in6;
+	else
+		error = EADDRNOTAVAIL;
+
+exit:
+	ovs_mutex_unlock(&netdev->mutex);
+	return (error);
+}
+
+#define	ROUNDUP_LONG(a) ((a) > 0 ? (1 + (((a) - 1) | (sizeof (long) - 1))) : \
+    sizeof (long))
+#define	RT_ADVANCE(x, n) ((x) += ROUNDUP_LONG(salen(n)))
+#define	BUF_SIZE 2048
+
+typedef struct rtmsg {
+	struct rt_msghdr	m_rtm;
+	char			m_space[BUF_SIZE];
+} rtmsg_t;
+
+static int
+salen(const struct sockaddr *sa)
+{
+	switch (sa->sa_family) {
+	case AF_INET:
+		return (sizeof (struct sockaddr_in));
+	case AF_LINK:
+		return (sizeof (struct sockaddr_dl));
+	case AF_INET6:
+		return (sizeof (struct sockaddr_in6));
+	default:
+		return (sizeof (struct sockaddr));
+	}
+}
+
+/*
+ * Adds 'router' as a default IP gateway.
+ */
+static int
+netdev_solaris_add_router(struct netdev *netdev_, struct in_addr router)
+{
+	const char		*netdev_name = netdev_get_name(netdev_);
+	rtmsg_t			m_rtmsg;
+	struct rt_msghdr	*rtm = &m_rtmsg.m_rtm;
+	char			*cp = m_rtmsg.m_space;
+	struct sockaddr_in	gateway;
+	struct sockaddr_in	dest;
+	struct sockaddr_in	mask;
+	int			rtsock_fd;
+	int			error = 0;
+	int			l;
+
+	VLOG_DBG("netdev_solaris_add_router %s", netdev_name);
+
+	rtsock_fd = socket(PF_ROUTE, SOCK_RAW, 0);
+	if (rtsock_fd == -1) {
+		error = errno;
+		VLOG_ERR("failed to create PF_ROUTE socket (%s)",
+		    ovs_strerror(error));
+		return (error);
+	}
+
+	memset(&gateway, 0, sizeof (gateway));
+	gateway.sin_family = AF_INET;
+	gateway.sin_addr = router;
+
+	memset(&dest, 0, sizeof (dest));
+	dest.sin_family = AF_INET;
+	dest.sin_addr.s_addr = htonl(INADDR_ANY);
+
+	memset(&dest, 0, sizeof (mask));
+	mask.sin_family = AF_INET;
+	mask.sin_addr.s_addr = htonl(INADDR_ANY);
+
+	(void) memset(&m_rtmsg, 0, sizeof (m_rtmsg));
+	rtm->rtm_version = RTM_VERSION;
+	rtm->rtm_type	 = RTM_ADD;
+	rtm->rtm_flags	 = RTF_GATEWAY | RTF_STATIC | RTF_UP;
+	rtm->rtm_addrs	 = RTA_GATEWAY | RTA_DST | RTA_NETMASK;
+
+	l = ROUNDUP_LONG(sizeof (struct sockaddr_in));
+	(void) memcpy(cp, &dest, l);
+	cp += l;
+	(void) memcpy(cp, &gateway, l);
+	cp += l;
+	(void) memcpy(cp, &mask, l);
+	cp += l;
+
+	rtm->rtm_msglen = l = cp - (char *)&m_rtmsg;
+	if (write(rtsock_fd, (char *)&m_rtmsg, l) != l) {
+		char buffer[INET_ADDRSTRLEN];
+		(void) inet_ntop(AF_INET, &router, buffer, sizeof (buffer));
+		error = errno;
+		VLOG_ERR("failed to add router %s: %s", buffer,
+		    ovs_strerror(error));
+	}
+	close(rtsock_fd);
+	return (error);
+}
+
+/*
+ * Looks up the next hop for 'host' in the host's routing table.  If
+ * successful, stores the next hop gateway's address (0 if 'host' is on a
+ * directly connected network) in '*next_hop' and a copy of the name of the
+ * device to reach 'host' in '*netdev_name', and returns 0. The caller is
+ * responsible for freeing '*netdev_name' (by calling free()).
+ */
+static int
+netdev_solaris_get_next_hop(const struct in_addr *host,
+    struct in_addr *next_hop, char **netdev_name)
+{
+	rtmsg_t			m_rtmsg;
+	struct rt_msghdr	*rtm = &m_rtmsg.m_rtm;
+	char			*cp = m_rtmsg.m_space;
+	int			rtsock_fd;
+	struct sockaddr_in	sin;
+	struct sockaddr_dl	sdl;
+	const pid_t		pid = getpid();
+	static int		seq;
+	boolean_t		gateway = B_FALSE;
+	ssize_t			ssz;
+	char			*ifname = NULL;
+	int			saved_errno;
+	int			error = 0;
+	int			rlen;
+	int			l;
+	int			i;
+
+	memset(&sin, 0, sizeof (sin));
+	sin.sin_family = AF_INET;
+	sin.sin_port = 0;
+	sin.sin_addr = *host;
+
+	memset(&sdl, 0, sizeof (sdl));
+	sdl.sdl_family = AF_LINK;
+
+	rtsock_fd = socket(PF_ROUTE, SOCK_RAW, 0);
+	if (rtsock_fd == -1) {
+		error = errno;
+		VLOG_ERR("failed to create PF_ROUTE socket (%s)",
+		    ovs_strerror(error));
+		return (error);
+	}
+
+	(void) memset(&m_rtmsg, 0, sizeof (m_rtmsg));
+	rtm->rtm_type = RTM_GET;
+	rtm->rtm_flags = RTF_HOST|RTF_UP;
+	rtm->rtm_version = RTM_VERSION;
+	rtm->rtm_seq = ++seq;
+	rtm->rtm_addrs = RTA_DST|RTA_IFP;
+
+	l = ROUNDUP_LONG(sizeof (struct sockaddr_in));
+	(void) memcpy(cp, &sin, l);
+	cp += l;
+
+	l = ROUNDUP_LONG(sizeof (struct sockaddr_dl));
+	(void) memcpy(cp, &sdl, l);
+	cp += l;
+
+	rtm->rtm_msglen = l = cp - (char *)&m_rtmsg;
+	if ((rlen = write(rtsock_fd, (char *)&m_rtmsg, l)) < l) {
+		error = errno;
+		VLOG_ERR("failed to get route: %s", ovs_strerror(error));
+		close(rtsock_fd);
+		return (errno);
+	}
+
+	memset(next_hop, 0, sizeof (*next_hop));
+	*netdev_name = NULL;
+	memset(&m_rtmsg, 0, sizeof (m_rtmsg));
+	do {
+		ssz = read(rtsock_fd, &m_rtmsg, sizeof (m_rtmsg));
+	} while (ssz > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != pid));
+	saved_errno = errno;
+	close(rtsock_fd);
+	if (ssz <= 0) {
+		if (ssz < 0) {
+			return (saved_errno);
+		}
+		return (EPIPE);
+	}
+	cp = (void *)&m_rtmsg.m_space;
+	for (i = 1; i; i <<= 1) {
+		if ((rtm->rtm_addrs & i) != 0) {
+			const struct sockaddr *sa = (const void *)cp;
+
+			if ((i == RTA_GATEWAY) && sa->sa_family == AF_INET) {
+				const struct sockaddr_in * const sin =
+				    ALIGNED_CAST(const struct sockaddr_in *,
+				    sa);
+
+				*next_hop = sin->sin_addr;
+				gateway = B_TRUE;
+			}
+			if ((i == RTA_IFP) && sa->sa_family == AF_LINK) {
+				const struct sockaddr_dl * const sdl =
+				    ALIGNED_CAST(const struct sockaddr_dl *,
+				    sa);
+
+				ifname = xmemdup0(sdl->sdl_data,
+				    sdl->sdl_nlen);
+			}
+			RT_ADVANCE(cp, sa);
+		}
+	}
+	if (ifname == NULL) {
+		return (ENXIO);
+	}
+	if (!gateway) {
+		*next_hop = *host;
+	}
+	*netdev_name = ifname;
+	VLOG_DBG("host " IP_FMT " next-hop " IP_FMT " if %s\n",
+	    IP_ARGS(host->s_addr), IP_ARGS(next_hop->s_addr), *netdev_name);
+
+	return (0);
+}
+
+static int
+netdev_solaris_get_status(const struct netdev *netdev_,
+    struct smap *smap OVS_UNUSED)
+{
+	const char	*netdev_name = netdev_get_name(netdev_);
+	int		error = EOPNOTSUPP;
+
+	VLOG_DBG("netdev_solaris_get_status %s", netdev_name);
+
+	/*
+	 * It looks like this is used to populate a column,
+	 * OVSREC_INTERFACE_COL_STATUS in the OVSDB. It doesn't appear
+	 * to be required though. If we wanted to return the driver name
+	 * then we could return that using libdladm.
+	 */
+	return (error);
+}
+
+/*
+ * Looks up the ARP table entry for 'ip' on 'netdev'.  If one exists and can be
+ * successfully retrieved, it stores the corresponding MAC address in 'mac' and
+ * returns 0.  Otherwise, it returns a positive errno value; in particular,
+ * ENXIO indicates that there is not ARP table entry for 'ip' on 'netdev'.
+ */
+static int
+netdev_solaris_arp_lookup(const struct netdev *netdev_,
+    ovs_be32 ip OVS_UNUSED, uint8_t mac[ETH_ADDR_LEN] OVS_UNUSED)
+{
+	const char		*netdev_name = netdev_get_name(netdev_);
+	int			error = 0;
+	struct xarpreq		ar;
+	struct sockaddr_in	*sin;
+
+	VLOG_DBG("netdev_solaris_arp_lookup %s", netdev_name);
+
+	bzero(&ar, sizeof (ar));
+	sin = ALIGNED_CAST(struct sockaddr_in *, &ar.xarp_pa);
+	sin->sin_addr.s_addr = ip;
+	sin->sin_family = AF_INET;
+	ar.xarp_ha.sdl_family = AF_LINK;
+
+	if (ioctl(sock4, SIOCGXARP, &ar) < 0) {
+		error = errno;
+		goto out;
+	}
+	if (!(ar.xarp_flags & ATF_COM)) {
+		errno = EOPNOTSUPP; /* XXX */
+		goto out;
+	}
+	memcpy(mac, LLADDR(&ar.xarp_ha), ETH_ADDR_LEN);
+out:
+	return (error);
+}
+
+static int
+lifr_to_nd_flags(int64_t lifrflags)
+{
+	enum netdev_flags nd_flags = 0;
+
+	if (lifrflags & IFF_UP) {
+		nd_flags |= NETDEV_UP;
+	}
+	if (lifrflags & IFF_PROMISC) {
+		nd_flags |= NETDEV_PROMISC;
+	}
+	if (lifrflags & IFF_LOOPBACK) {
+		nd_flags |= NETDEV_LOOPBACK;
+	}
+	return (nd_flags);
+}
+
+static int64_t
+nd_to_lifr_flags(enum netdev_flags nd_flags)
+{
+	int64_t lifrflags = 0;
+
+	if (nd_flags & NETDEV_UP) {
+		lifrflags |= IFF_UP;
+	}
+	if (nd_flags & NETDEV_PROMISC) {
+		lifrflags |= IFF_PROMISC;
+	}
+	if (nd_flags & NETDEV_LOOPBACK) {
+		lifrflags |= IFF_LOOPBACK;
+	}
+	return (lifrflags);
+}
+
+/*
+ * Retrieves the current set of flags on 'netdev' into '*old_flags'.  Then,
+ * turns off the flags that are set to 1 in 'off' and turns on the flags
+ * that are set to 1 in 'on'.  (No bit will be set to 1 in both 'off' and
+ * 'on'; that is, off & on == 0.)
+ */
+static int
+netdev_solaris_update_flags(struct netdev *netdev_, enum netdev_flags off,
+    enum netdev_flags on, enum netdev_flags *old_flagsp)
+{
+	const char		*netdev_name = netdev_get_name(netdev_);
+	struct lifreq		lifr;
+	int64_t			old_lifr_flags;
+	int64_t			new_lifr_flags;
+	int			error = 0;
+
+	VLOG_DBG("netdev_solaris_update_flags %s", netdev_name);
+
+	bzero(&lifr, sizeof (lifr));
+	(void) strncpy(lifr.lifr_name, netdev_name, sizeof (lifr.lifr_name));
+	if (ioctl(sock4, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0) {
+		error = errno;
+		goto exit;
+	}
+	old_lifr_flags = lifr.lifr_flags;
+	*old_flagsp = lifr_to_nd_flags(old_lifr_flags);
+	new_lifr_flags = (old_lifr_flags & ~nd_to_lifr_flags(off)) |
+	    nd_to_lifr_flags(on);
+
+	if (new_lifr_flags == old_lifr_flags)
+		goto exit;
+
+	lifr.lifr_flags = new_lifr_flags;
+	if (ioctl(sock4, SIOCSLIFFLAGS, (caddr_t)&lifr) < 0) {
+		error = errno;
+		goto exit;
+	}
+	netdev_change_seq_changed(netdev_);
+
+exit:
+	return (error);
+}
+
+static int
+netdev_internal_get_stats(const struct netdev *netdev_ OVS_UNUSED,
+    struct netdev_stats *stats OVS_UNUSED)
+{
+	return (0);
+}
+
+static int
+netdev_internal_get_status(const struct netdev *netdev OVS_UNUSED,
+    struct smap *smap)
+{
+	smap_add(smap, "driver_name", "openvswitch");
+	return (0);
+}
+
+#define	NETDEV_SOLARIS_CLASS(NAME, CONSTRUCT, GET_STATS, SET_STATS,	\
+    GET_FEATURES, GET_STATUS)						\
+{									\
+	NAME,								\
+	netdev_solaris_init,						\
+	netdev_solaris_run,						\
+	netdev_solaris_wait,						\
+	netdev_solaris_alloc,						\
+	CONSTRUCT,							\
+	netdev_solaris_destruct,					\
+	netdev_solaris_dealloc,						\
+	NULL,								\
+	NULL,								\
+	NULL,								\
+	NULL,								\
+	NULL,								\
+	netdev_solaris_set_etheraddr,					\
+	netdev_solaris_get_etheraddr,					\
+	netdev_solaris_get_mtu,						\
+	netdev_solaris_set_mtu,						\
+	netdev_solaris_get_ifindex,					\
+	NULL,								\
+	NULL,								\
+	NULL,								\
+	GET_STATS,							\
+	SET_STATS,							\
+	GET_FEATURES,							\
+	NULL,								\
+	netdev_solaris_set_policing,					\
+	netdev_solaris_get_qos_types,					\
+	netdev_solaris_get_qos_capabilities,				\
+	netdev_solaris_get_qos,						\
+	netdev_solaris_set_qos,						\
+	netdev_solaris_get_queue,					\
+	netdev_solaris_set_queue,					\
+	netdev_solaris_delete_queue,					\
+	netdev_solaris_get_queue_stats,					\
+	netdev_solaris_queue_dump_start,				\
+	netdev_solaris_queue_dump_next,					\
+	netdev_solaris_queue_dump_done,					\
+	netdev_solaris_dump_queue_stats,				\
+	netdev_solaris_get_in4,						\
+	netdev_solaris_set_in4,						\
+	netdev_solaris_get_in6,						\
+	netdev_solaris_add_router,					\
+	netdev_solaris_get_next_hop,					\
+	GET_STATUS,							\
+	netdev_solaris_arp_lookup,					\
+	netdev_solaris_update_flags,					\
+	netdev_solaris_configure_uplink,				\
+	netdev_solaris_is_uplink,					\
+	NULL,								\
+	NULL,								\
+	NULL,								\
+	NULL,								\
+	NULL,								\
+	NULL,								\
+	NULL								\
+}
+
+const struct netdev_class netdev_solaris_class =
+    NETDEV_SOLARIS_CLASS(
+	"system",
+	netdev_solaris_construct,
+	netdev_solaris_get_stats,
+	NULL,				/* set_stats */
+	netdev_solaris_get_features,
+	netdev_solaris_get_status);
+
+const struct netdev_class netdev_internal_class =
+    NETDEV_SOLARIS_CLASS(
+	"internal",
+	netdev_solaris_construct,
+	netdev_internal_get_stats,
+	NULL,				/* set_stats */
+	NULL,				/* get_features */
+	netdev_internal_get_status);
+
+/* Solaris HTB traffic control class */
+#define	HTB_N_QUEUES	0xf000
+
+struct htb {
+	struct tc	tc;
+	unsigned int	max_rate;	/* In bytes/s. */
+};
+
+struct htb_class {
+	struct tc_queue	tc_queue;
+	unsigned int	min_rate;	/* In bytes/s */
+	unsigned int	max_rate;	/* In bytes/s */
+	unsigned int	burst;		/* In bytes/s -- unused */
+	unsigned int	priority;	/* Lower value is higher priority */
+};
+
+
+/*
+ * Create an HTB qdisc.
+ *
+ * Equivalent to "tc qdisc add dev <dev> root handle 1: htb default 1".
+ */
+static int
+htb_setup_qdisc__(struct netdev *netdev)
+{
+	VLOG_DBG("htb_setup_qdisc__ device %s", netdev->name);
+	tc_del_qdisc(netdev);
+
+	return (0);
+}
+
+static struct htb *
+htb_get__(const struct netdev *netdev_)
+{
+	struct netdev_solaris	*netdev = netdev_solaris_cast(netdev_);
+	const char		*netdev_name = netdev_get_name(netdev_);
+
+	VLOG_DBG("htb_get__ device %s", netdev_name);
+	return (CONTAINER_OF(netdev->tc, struct htb, tc));
+}
+
+static void
+htb_install__(struct netdev *netdev_, uint64_t max_rate)
+{
+	const char		*netdev_name = netdev_get_name(netdev_);
+	struct netdev_solaris	*netdev = netdev_solaris_cast(netdev_);
+	struct htb		*htb;
+
+	VLOG_DBG("htb_install__ device %s", netdev_name);
+	htb = xmalloc(sizeof (*htb));
+	tc_init(&htb->tc, &tc_ops_htb);
+	htb->max_rate = max_rate;
+
+	netdev->tc = &htb->tc;
+	VLOG_DBG("htb_install__ device %s TC configured ", netdev_name);
+}
+
+static int
+htb_setup_class__(struct netdev *netdev, unsigned int handle OVS_UNUSED,
+    unsigned int parent OVS_UNUSED,
+    struct htb_class *class OVS_UNUSED)
+{
+	VLOG_DBG("htb_setup_class__ device %s", netdev->name);
+
+	return (0);
+}
+
+static void
+htb_parse_qdisc_details__(struct netdev *netdev_,
+			const struct smap *details, struct htb_class *hc)
+{
+	struct netdev_solaris	*netdev = netdev_solaris_cast(netdev_);
+	const char		*netdev_name = netdev_get_name(netdev_);
+	const char		*max_rate_s;
+	const char		*physname;
+	char			buffer[DLADM_PROP_VAL_MAX];
+	int			error = 0;
+
+	VLOG_DBG("htb_parse_qdisc_details__ device %s", netdev_name);
+
+	/*
+	 * Initialize in case of early return.
+	 */
+	hc->max_rate = 0;
+	hc->min_rate = 0;
+	hc->burst = 0;
+	hc->priority = 0;
+
+	if (netdev_solaris_is_uplink(netdev_)) {
+		physname = netdev_name;
+	} else {
+		error = solaris_get_dllower(netdev_name, buffer,
+		    sizeof (buffer));
+		if (error != 0)
+			return;
+		physname = buffer;
+	}
+
+	max_rate_s = smap_get(details, "max-rate");
+	hc->max_rate = max_rate_s ? strtoull(max_rate_s, NULL, 10) / 8 : 0;
+	if (!hc->max_rate) {
+		enum netdev_features current;
+
+		netdev_solaris_read_features(netdev, physname);
+		current = !netdev->get_features_error ? netdev->current : 0;
+		hc->max_rate = netdev_features_to_bps(current,
+		    100 * 1000 * 1000) / 8;
+	}
+}
+
+static int
+htb_tc_install(struct netdev *netdev, const struct smap *details)
+{
+	int error;
+
+	VLOG_DBG("htb_tc_install device %s", netdev->name);
+
+	error = htb_setup_qdisc__(netdev);
+	if (!error) {
+		struct htb_class hc;
+
+		htb_parse_qdisc_details__(netdev, details, &hc);
+		error = htb_setup_class__(netdev, 0, 0, &hc);
+		if (!error) {
+			htb_install__(netdev, hc.max_rate);
+		}
+	}
+	return (error);
+}
+
+static void
+htb_tc_destroy(struct tc *tc)
+{
+	struct htb		*htb = CONTAINER_OF(tc, struct htb, tc);
+	struct htb_class	*hc, *next;
+
+	HMAP_FOR_EACH_SAFE(hc, next, tc_queue.hmap_node, &htb->tc.queues) {
+		hmap_remove(&htb->tc.queues, &hc->tc_queue.hmap_node);
+		free(hc);
+	}
+	tc_destroy(tc);
+	free(htb);
+}
+
+static int
+htb_qdisc_get(const struct netdev *netdev, struct smap *details)
+{
+	const struct htb *htb = htb_get__(netdev);
+
+	VLOG_DBG("htb_qdisc_get device %s", netdev->name);
+	smap_add_format(details, "max-rate", "%llu", 8ULL * htb->max_rate);
+	return (0);
+}
+
+static int
+htb_qdisc_set(struct netdev *netdev, const struct smap *details)
+{
+	struct htb_class	hc;
+	int			error;
+
+	VLOG_DBG("htb_qdisc_set device %s", netdev->name);
+	htb_parse_qdisc_details__(netdev, details, &hc);
+	/* Solaris: don't care about the handles */
+	error = htb_setup_class__(netdev, 0, 0, &hc);
+	if (!error) {
+		htb_get__(netdev)->max_rate = hc.max_rate;
+	}
+
+	return (error);
+}
+
+static struct htb_class *
+htb_class_cast__(const struct tc_queue *queue)
+{
+	return (CONTAINER_OF(queue, struct htb_class, tc_queue));
+}
+
+static int
+htb_class_get(const struct netdev *netdev,
+    const struct tc_queue *queue, struct smap *details)
+{
+	const struct htb_class *hc = htb_class_cast__(queue);
+
+	VLOG_DBG("htb_class_get device %s", netdev->name);
+
+	if (hc->max_rate > 0)
+		smap_add_format(details, "max-rate", "%llu",
+		    8ULL * hc->max_rate);
+
+	VLOG_DBG("htb_class_get device done");
+	return (0);
+}
+
+/* Solaris: currently, min-rate, burst and priority are not supported */
+static int
+htb_parse_class_details__(struct netdev *netdev,
+    const struct smap *details, struct htb_class *hc)
+{
+	const struct htb	*htb = htb_get__(netdev);
+	const char		*max_rate_s = smap_get(details, "max-rate");
+
+	VLOG_DBG("htb_parse_class_details__ device %s", netdev->name);
+
+	/* max-rate */
+	hc->max_rate = (max_rate_s
+	    ? strtoull(max_rate_s, NULL, 10) / 8
+	    : htb->max_rate);
+	VLOG_DBG("htb_parse_class_details__ device max_rate is %u",
+	    hc->max_rate);
+
+	return (0);
+}
+
+static void
+htb_update_queue__(struct netdev *netdev, unsigned int queue_id,
+    const struct htb_class *hc)
+{
+	struct htb		*htb = htb_get__(netdev);
+	size_t			hash = hash_int(queue_id, 0);
+	struct tc_queue		*queue;
+	struct htb_class	*hcp;
+
+	VLOG_DBG("htb_update_queue__ %s", netdev->name);
+
+	queue = tc_find_queue__(netdev, queue_id, hash);
+	if (queue) {
+		hcp = htb_class_cast__(queue);
+	} else {
+		hcp = xmalloc(sizeof (*hcp));
+		queue = &hcp->tc_queue;
+		queue->queue_id = queue_id;
+		queue->created = time_msec();
+		hmap_insert(&htb->tc.queues, &queue->hmap_node, hash);
+	}
+
+	hcp->max_rate = hc->max_rate;
+}
+
+static int
+htb_class_set(struct netdev *netdev, unsigned int queue_id,
+    const struct smap *details)
+{
+	struct htb_class	hc;
+	int			error;
+
+	VLOG_DBG("htb_class_set %s", netdev->name);
+
+	error = htb_parse_class_details__(netdev, details, &hc);
+	if (error) {
+		return (error);
+	}
+
+	error = htb_setup_class__(netdev, 0, 0, &hc);
+	if (error) {
+		return (error);
+	}
+
+	htb_update_queue__(netdev, queue_id, &hc);
+	return (0);
+}
+
+static int
+htb_class_delete(struct netdev *netdev, struct tc_queue *queue)
+{
+	struct htb_class	*hc = htb_class_cast__(queue);
+	struct htb		*htb = htb_get__(netdev);
+	int			error;
+
+	VLOG_DBG("htb_class_delete %s", netdev->name);
+	error = tc_delete_class(netdev, 0);
+	if (!error) {
+		hmap_remove(&htb->tc.queues, &hc->tc_queue.hmap_node);
+		free(hc);
+	}
+	return (error);
+}
+
+/*
+ * Used to get existing configuration for qdiscs in the kernel, not used in
+ * Solaris.
+ */
+static int
+htb_tc_load(struct netdev *netdev, struct ofpbuf *nlmsg OVS_UNUSED)
+{
+	VLOG_DBG("htb_tc_load %s", netdev->name);
+
+	return (0);
+}
+
+static const struct tc_ops tc_ops_htb = {
+	"htb",			/* linux_name */
+	"linux-htb",		/* ovs_name */
+	HTB_N_QUEUES,		/* n_queues */
+	htb_tc_install,
+	htb_tc_load,
+	htb_tc_destroy,
+	htb_qdisc_get,
+	htb_qdisc_set,
+	htb_class_get,
+	htb_class_set,
+	htb_class_delete,
+	NULL,
+	NULL
+};
+
+/*
+ * The default traffic control class.
+ *
+ * This class represents the default, unnamed Linux qdisc.  It corresponds to
+ * the "" (empty string) QoS type in the OVS database.
+ */
+static void
+default_install__(struct netdev *netdev_)
+{
+	const char		*netdev_name = netdev_get_name(netdev_);
+	struct netdev_solaris	*netdev = netdev_solaris_cast(netdev_);
+	static const struct tc	tc = TC_INITIALIZER(&tc, &tc_ops_default);
+
+	VLOG_DBG("default_install__ device %s", netdev_name);
+
+	/*
+	 * Nothing but a tc class implementation is allowed to write to a tc.
+	 * This class never does that, so we can legitimately use a const tc
+	 * object.
+	 */
+	netdev->tc = CONST_CAST(struct tc *, &tc);
+}
+
+static int
+default_tc_install(struct netdev *netdev,
+    const struct smap *details OVS_UNUSED)
+{
+	VLOG_DBG("default_tc_install device %s", netdev->name);
+	default_install__(netdev);
+	return (0);
+}
+
+static int
+default_tc_load(struct netdev *netdev, struct ofpbuf *nlmsg OVS_UNUSED)
+{
+	VLOG_DBG("default_tc_load device %s", netdev->name);
+	default_install__(netdev);
+	return (0);
+}
+
+static const struct tc_ops tc_ops_default = {
+	NULL,			/* linux_name */
+	"",			/* ovs_name */
+	0,			/* n_queues */
+	default_tc_install,
+	default_tc_load,
+	NULL,			/* tc_destroy */
+	NULL,			/* qdisc_get */
+	NULL,			/* qdisc_set */
+	NULL,			/* class_get */
+	NULL,			/* class_set */
+	NULL,			/* class_delete */
+	NULL,			/* class_get_stats */
+	NULL			/* class_dump_stats */
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openvswitch/files/lib/netdev-solaris.h	Mon Nov 16 16:49:19 2015 -0500
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ */
+
+/*
+ * Copyright (c) 2011, 2013, 2014 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef	NETDEV_SOLARIS_H
+#define	NETDEV_SOLARIS_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+/*
+ * These functions are Solaris specific, so they should be used directly only by
+ * Solaris-specific code.
+ */
+
+struct netdev;
+
+int netdev_create_impl_etherstub(void);
+void netdev_delete_impl_etherstub(void);
+boolean_t netdev_impl_etherstub_exists(void);
+
+#endif	/* NETDEV_SOLARIS_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openvswitch/files/lib/route-table-solaris.c	Mon Nov 16 16:49:19 2015 -0500
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ */
+
+/*
+ * Copyright (c) 2012 Ed Maste. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+
+#include "route-table.h"
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <net/if_dl.h>
+#include <netinet/in.h>
+
+#include <string.h>
+#include <unistd.h>
+
+#include "util.h"
+
+static int pid;
+static unsigned int register_count = 0;
+
+#define	ROUNDUP_LONG(a) ((a) > 0 ? (1 + (((a) - 1) | (sizeof (long) - 1))) : \
+    sizeof (long))
+#define	RT_ADVANCE(x, n) ((x) += ROUNDUP_LONG(salen(n)))
+#define	BUF_SIZE 2048
+
+typedef struct rtmsg {
+	struct rt_msghdr	m_rtm;
+	char			m_space[BUF_SIZE];
+} rtmsg_t;
+
+static int
+salen(const struct sockaddr *sa)
+{
+	switch (sa->sa_family) {
+	case AF_INET:
+		return (sizeof (struct sockaddr_in));
+	case AF_LINK:
+		return (sizeof (struct sockaddr_dl));
+	case AF_INET6:
+		return (sizeof (struct sockaddr_in6));
+	default:
+		return (sizeof (struct sockaddr));
+	}
+}
+
+bool
+route_table_get_name(ovs_be32 ip, char name[IFNAMSIZ])
+{
+	rtmsg_t			m_rtmsg;
+	struct rt_msghdr	*rtm = &m_rtmsg.m_rtm;
+	char			*cp = m_rtmsg.m_space;
+	int			rtsock_fd;
+	struct sockaddr_in	sin;
+	struct sockaddr_dl	sdl;
+	ssize_t			ssz;
+	static int		seq;
+	int			rlen;
+	int			i;
+	int			l;
+
+	rtsock_fd = socket(PF_ROUTE, SOCK_RAW, 0);
+	if (rtsock_fd == -1)
+		return (false);
+
+	memset(&sin, 0, sizeof (sin));
+	sin.sin_family = AF_INET;
+	sin.sin_port = 0;
+	sin.sin_addr.s_addr = ip;
+
+	memset(&sdl, 0, sizeof (sdl));
+	sdl.sdl_family = AF_LINK;
+
+	memset(&m_rtmsg, 0, sizeof (m_rtmsg));
+	rtm->rtm_version = RTM_VERSION;
+	rtm->rtm_type = RTM_GET;
+	rtm->rtm_addrs = RTA_DST|RTA_IFP;
+	rtm->rtm_seq = ++seq;
+
+	l = ROUNDUP_LONG(sizeof (struct sockaddr_in));
+	(void) memcpy(cp, &sin, l);
+	cp += l;
+
+	l = ROUNDUP_LONG(sizeof (struct sockaddr_dl));
+	(void) memcpy(cp, &sdl, l);
+	cp += l;
+
+	rtm->rtm_msglen = l = cp - (char *)&m_rtmsg;
+	if ((rlen = write(rtsock_fd, (char *)&m_rtmsg, l)) < l) {
+		close(rtsock_fd);
+		return (false);
+	}
+
+	do {
+		ssz = read(rtsock_fd, &m_rtmsg, sizeof (m_rtmsg));
+	} while (ssz > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != pid));
+	close(rtsock_fd);
+
+	if (ssz < 0)
+		return (false);
+
+	cp = (void *)&m_rtmsg.m_space;
+	for (i = 1; i; i <<= 1) {
+		if ((rtm->rtm_addrs & i) != 0) {
+			const struct sockaddr *sa = (const void *)cp;
+
+			if ((i == RTA_IFP) && sa->sa_family == AF_LINK) {
+				const struct sockaddr_dl * const sdlp =
+				    ALIGNED_CAST(const struct sockaddr_dl *,
+				    sa);
+				int namelen;
+
+				namelen = sdlp->sdl_nlen;
+				if (namelen > IFNAMSIZ - 1)
+					namelen = IFNAMSIZ - 1;
+				memcpy(name, sdlp->sdl_data, namelen);
+				name[namelen] = '\0';
+				return (true);
+			}
+			RT_ADVANCE(cp, sa);
+		}
+	}
+	return (false);
+}
+
+uint64_t
+route_table_get_change_seq(void)
+{
+	return (0);
+}
+
+void
+route_table_register(void)
+{
+	if (!register_count) {
+		pid = getpid();
+	}
+
+	register_count++;
+}
+
+void
+route_table_unregister(void)
+{
+	register_count--;
+}
+
+void
+route_table_run(void)
+{
+}
+
+void
+route_table_wait(void)
+{
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openvswitch/files/lib/util-solaris.c	Mon Nov 16 16:49:19 2015 -0500
@@ -0,0 +1,3789 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <alloca.h>
+#include <errno.h>
+#include <limits.h>
+#include <string.h>
+#include <strings.h>
+#include <stropts.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <inet/ip.h>
+#include <inet/ip6.h>
+#include <arpa/inet.h>
+#include <sys/sockio.h>
+#include <libdlpi.h>
+#include <libdladm.h>
+#include <libdllink.h>
+#include <zone.h>
+#include <net/if_types.h>
+#include <inet/arp.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <rad/radclient.h>
+#include <rad/client/1/dlmgr.h>
+
+#undef	IP_VERSION
+#include "netlink-protocol.h"
+#include "netlink.h"
+#include "flow.h"
+#include "packets.h"
+#include "util-solaris.h"
+#include "util.h"
+#include "dpif-solaris.h"
+
+static rc_conn_t *rad_conn = NULL;
+static boolean_t b_true = B_TRUE;
+
+typedef struct {
+	uint_t		ifsp_ppa;	/* Physical Point of Attachment */
+	uint_t		ifsp_lun;	/* Logical Unit number */
+	boolean_t	ifsp_lunvalid;	/* TRUE if lun is valid */
+	char		ifsp_devnm[LIFNAMSIZ]; /* only the device name */
+} ifspec_t;
+
+static int
+extract_uint(const char *valstr, uint_t *val)
+{
+	char		*ep;
+	unsigned long	ul;
+
+	errno = 0;
+	ul = strtoul(valstr, &ep, 10);
+	if (errno != 0 || *ep != '\0' || ul > UINT_MAX)
+		return (-1);
+	*val = (uint_t)ul;
+	return (0);
+}
+
+/*
+ * Given a token with a logical unit spec, return the logical unit converted
+ * to a uint_t.
+ *
+ * Returns: 0 for success, nonzero if an error occurred. errno is set if
+ * necessary.
+ */
+static int
+getlun(const char *bp, size_t bpsize, uint_t *lun)
+{
+	const char	*ep = &bp[bpsize - 1];
+	const char	*tp;
+
+	/* Lun must be all digits */
+	for (tp = ep; tp > bp && isdigit(*tp); tp--)
+		/* Null body */;
+
+	if (tp == ep || tp != bp || extract_uint(bp + 1, lun) != 0) {
+		errno = EINVAL;
+		return (-1);
+	}
+	return (0);
+}
+
+/*
+ * Given a single token ending with a ppa spec, return the ppa spec converted
+ * to a uint_t.
+ *
+ * Returns: 0 for success, nonzero if an error occurred. errno is set if
+ * necessary.
+ */
+static int
+getppa(const char *bp, size_t bpsize, uint_t *ppa)
+{
+	const char	*ep = &bp[bpsize - 1];
+	const char	*tp;
+
+	for (tp = ep; tp >= bp && isdigit(*tp); tp--)
+		/* Null body */;
+
+	/*
+	 * If the device name does not end with a digit or the device
+	 * name is a sequence of numbers or a PPA contains a leading
+	 * zero, return error.
+	 */
+	if (tp == ep || tp < bp || ((ep - tp) > 1 && *(tp + 1) == '0'))
+		goto fail;
+
+	if (extract_uint(tp + 1, ppa) != 0)
+		goto fail;
+
+	/* max value of PPA is 4294967294, which is (UINT_MAX - 1) */
+	if (*ppa > UINT_MAX - 1)
+		goto fail;
+	return (0);
+fail:
+	errno = EINVAL;
+	return (-1);
+}
+
+/*
+ * Given a `linkname' of the form drv(ppa), parse it into `driver' and `ppa'.
+ * If the `dsize' for the `driver' is not atleast MAXLINKNAMELEN then part of
+ * the driver name will be copied to `driver'.
+ *
+ * This function also validates driver name and PPA and therefore callers can
+ * call this function with `driver' and `ppa' set to NULL, to just verify the
+ * linkname.
+ */
+boolean_t
+dlparse_drvppa(const char *linknamep, char *driver, uint_t dsize, uint_t *ppa)
+{
+	char	*tp;
+	char    linkname[MAXLINKNAMELEN];
+	size_t	len;
+	uint_t	lppa;
+
+	if (linknamep == NULL || linknamep[0] == '\0')
+		goto fail;
+
+	len = strlcpy(linkname, linknamep, MAXLINKNAMELEN);
+	if (len >= MAXLINKNAMELEN)
+		goto fail;
+
+	/* Get PPA */
+	if (getppa(linkname, len, &lppa) != 0)
+		return (_B_FALSE);
+
+	/* strip the ppa off of the linkname, if present */
+	for (tp = &linkname[len - 1]; tp >= linkname && isdigit(*tp); tp--)
+		*tp = '\0';
+
+	/*
+	 * Now check for the validity of the device name. The legal characters
+	 * in a device name are: alphanumeric (a-z,  A-Z,  0-9), underscore
+	 * ('_'), hyphen ('-'), and period ('.'). The first character
+	 * of the device name cannot be a digit and should be an alphabetic
+	 * character.
+	 */
+	if (!isalpha(linkname[0]))
+		goto fail;
+	for (tp = linkname + 1; *tp != '\0'; tp++) {
+		if (!isalnum(*tp) && *tp != '_' && *tp != '-' && *tp != '.')
+			goto fail;
+	}
+
+	if (driver != NULL)
+		(void) strlcpy(driver, linkname, dsize);
+
+	if (ppa != NULL)
+		*ppa = lppa;
+
+	return (_B_TRUE);
+fail:
+	errno = EINVAL;
+	return (_B_FALSE);
+}
+
+/*
+ * Given an IP interface name, which is either a
+ *	- datalink name (which is driver name plus PPA), for e.g. bge0 or
+ *	- datalink name plus a logical interface identifier (delimited by ':'),
+ *		for e.g. bge0:34
+ * the following function validates its form and decomposes the contents into
+ * ifspec_t.
+ *
+ * Returns _B_TRUE for success, otherwise _B_FALSE is returned.
+ */
+static boolean_t
+ifparse_ifspec(const char *ifname, ifspec_t *ifsp)
+{
+	char	*lp;
+	char	ifnamecp[LIFNAMSIZ];
+
+	if (ifname == NULL || ifname[0] == '\0' ||
+	    strlcpy(ifnamecp, ifname, LIFNAMSIZ) >= LIFNAMSIZ) {
+		errno = EINVAL;
+		return (_B_FALSE);
+	}
+
+	ifsp->ifsp_lunvalid = _B_FALSE;
+
+	/* Any logical units? */
+	lp = strchr(ifnamecp, ':');
+	if (lp != NULL) {
+		if (getlun(lp, strlen(lp), &ifsp->ifsp_lun) != 0)
+			return (_B_FALSE);
+		*lp = '\0';
+		ifsp->ifsp_lunvalid = _B_TRUE;
+	}
+
+	return (dlparse_drvppa(ifnamecp, ifsp->ifsp_devnm,
+	    sizeof (ifsp->ifsp_devnm), &ifsp->ifsp_ppa));
+}
+
+/*
+ * Issues the ioctl SIOCSLIFNAME to kernel.
+ */
+static int
+slifname(const char *ifname, uint64_t flags, int fd)
+{
+	struct lifreq	lifr;
+	int		status = 0;
+	ifspec_t	ifsp;
+	boolean_t	valid_if;
+
+	bzero(&lifr, sizeof (lifr));
+
+	/* We should have already validated the interface name. */
+	valid_if = ifparse_ifspec(ifname, &ifsp);
+	ovs_assert(valid_if);
+
+	lifr.lifr_ppa = ifsp.ifsp_ppa;
+	lifr.lifr_flags = flags;
+	(void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
+	if (ioctl(fd, SIOCSLIFNAME, &lifr) == -1) {
+		status = errno;
+	}
+
+	return (status);
+}
+
+/*
+ * Wrapper for sending a non-transparent I_STR ioctl().
+ * Returns: Result from ioctl().
+ */
+static int
+strioctl(int s, int cmd, char *buf, uint_t buflen)
+{
+	struct strioctl ioc;
+
+	(void) memset(&ioc, 0, sizeof (ioc));
+	ioc.ic_cmd = cmd;
+	ioc.ic_timout = 0;
+	ioc.ic_len = buflen;
+	ioc.ic_dp = buf;
+
+	return (ioctl(s, I_STR, (char *)&ioc));
+}
+
+/*
+ * Issues the ioctl SIOCSLIFNAME to kernel on the given ARP stream fd.
+ */
+static int
+slifname_arp(const char *ifname, uint64_t flags, int fd)
+{
+	struct lifreq	lifr;
+	ifspec_t	ifsp;
+
+	bzero(&lifr, sizeof (lifr));
+	(void) ifparse_ifspec(ifname, &ifsp);
+	lifr.lifr_ppa = ifsp.ifsp_ppa;
+	lifr.lifr_flags = flags;
+	(void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
+	/*
+	 * Tell ARP the name and unit number for this interface.
+	 * Note that arp has no support for transparent ioctls.
+	 */
+	if (strioctl(fd, SIOCSLIFNAME, (char *)&lifr,
+	    sizeof (lifr)) == -1) {
+		return (errno);
+	}
+	return (0);
+}
+
+/*
+ * Open "/dev/udp{,6}" for use as a multiplexor to PLINK the interface stream
+ * under. We use "/dev/udp" instead of "/dev/ip" since STREAMS will not let
+ * you PLINK a driver under itself, and "/dev/ip" is typically the driver at
+ * the bottom of the stream for tunneling interfaces.
+ */
+static int
+open_arp_on_udp(const char *udp_dev_name, int *fd)
+{
+	int err;
+
+	if ((*fd = open(udp_dev_name, O_RDWR)) == -1)
+		return (errno);
+	/*
+	 * Pop off all undesired modules (note that the user may have
+	 * configured autopush to add modules above udp), and push the
+	 * arp module onto the resulting stream. This is used to make
+	 * IP+ARP be able to atomically track the muxid for the I_PLINKed
+	 * STREAMS, thus it isn't related to ARP running the ARP protocol.
+	 */
+	while (ioctl(*fd, I_POP, 0) != -1)
+		;
+	if (errno == EINVAL && ioctl(*fd, I_PUSH, ARP_MOD_NAME) != -1)
+		return (0);
+
+	err = errno;
+	(void) close(*fd);
+	return (err);
+}
+
+static char *
+solaris_proto2str(uint8_t protocol)
+{
+	if (protocol == IPPROTO_TCP)
+		return ("tcp");
+	if (protocol == IPPROTO_UDP)
+		return ("udp");
+	if (protocol == IPPROTO_SCTP)
+		return ("sctp");
+	if (protocol == IPPROTO_ICMPV6)
+		return ("icmpv6");
+	if (protocol == IPPROTO_ICMP)
+		return ("icmp");
+	else
+		return ("");
+}
+
+static uint8_t
+solaris_str2proto(const char *protostr)
+{
+	if (strcasecmp(protostr, "tcp") == 0)
+		return (IPPROTO_TCP);
+	else if (strcasecmp(protostr, "udp") == 0)
+		return (IPPROTO_UDP);
+	else if (strcasecmp(protostr, "sctp") == 0)
+		return (IPPROTO_SCTP);
+	else if (strcasecmp(protostr, "icmpv6") == 0)
+		return (IPPROTO_ICMPV6);
+	else if (strcasecmp(protostr, "icmp") == 0)
+		return (IPPROTO_ICMP);
+	return (0);
+}
+
+/*
+ * Returns the flags value for the logical interface in `lifname'
+ * in the buffer pointed to by `flags'.
+ */
+static int
+solaris_get_flags(int sock, const char *lifname, uint64_t *flags)
+{
+	struct lifreq	lifr;
+
+	bzero(&lifr, sizeof (lifr));
+	(void) strlcpy(lifr.lifr_name, lifname, sizeof (lifr.lifr_name));
+
+	if (ioctl(sock, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0)
+		return (errno);
+	*flags = lifr.lifr_flags;
+
+	return (0);
+}
+
+/*
+ * For a given interface name, checks if IP interface exists.
+ */
+int
+solaris_if_enabled(int sock, const char *ifname, uint64_t *flags)
+{
+	struct lifreq	lifr;
+	int		error = 0;
+
+	bzero(&lifr, sizeof (lifr));
+	(void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
+	if (ioctl(sock, SIOCGLIFFLAGS, (caddr_t)&lifr) != 0)
+		error = errno;
+
+	if (error == 0 && flags != NULL)
+		*flags = lifr.lifr_flags;
+
+	return (error);
+}
+
+int
+solaris_unplumb_if(int sock, const char *ifname, sa_family_t af)
+{
+	int		ip_muxid;
+	int		arp_muxid;
+	int		mux_fd = -1;
+	int		muxid_fd = -1;
+	char		*udp_dev_name;
+	uint64_t	ifflags = 0;
+	boolean_t	changed_arp_muxid = B_FALSE;
+	struct lifreq	lifr;
+	int		ret = 0;
+	boolean_t	v6 = (af == AF_INET6);
+
+	/*
+	 * We used /dev/udp or udp6 to set up the mux. So we have to use
+	 * the same now for PUNLINK also.
+	 */
+	udp_dev_name = (v6 ?  UDP6_DEV_NAME : UDP_DEV_NAME);
+	if ((muxid_fd = open(udp_dev_name, O_RDWR)) == -1) {
+		ret = errno;
+		goto done;
+	}
+	ret = open_arp_on_udp(udp_dev_name, &mux_fd);
+	if (ret != 0)
+		goto done;
+
+	bzero(&lifr, sizeof (lifr));
+	(void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
+	if (ioctl(muxid_fd, SIOCGLIFMUXID, (caddr_t)&lifr) < 0) {
+		ret = errno;
+		goto done;
+	}
+	arp_muxid = lifr.lifr_arp_muxid;
+	ip_muxid = lifr.lifr_ip_muxid;
+
+	ret = solaris_get_flags(sock, ifname, &ifflags);
+	if (ret != 0)
+		goto done;
+	/*
+	 * We don't have a good way of knowing whether the arp stream is
+	 * plumbed. We can't rely on IFF_NOARP because someone could
+	 * have turned it off later using "ifconfig xxx -arp".
+	 */
+	if (arp_muxid != 0) {
+		if (ioctl(mux_fd, I_PUNLINK, arp_muxid) < 0) {
+			if ((errno == EINVAL) &&
+			    (ifflags & (IFF_NOARP | IFF_IPV6))) {
+				/*
+				 * Some plumbing utilities set the muxid to
+				 * -1 or some invalid value to signify that
+				 * there is no arp stream. Set the muxid to 0
+				 * before trying to unplumb the IP stream.
+				 * IP does not allow the IP stream to be
+				 * unplumbed if it sees a non-null arp muxid,
+				 * for consistency of IP-ARP streams.
+				 */
+				lifr.lifr_arp_muxid = 0;
+				(void) ioctl(muxid_fd, SIOCSLIFMUXID,
+				    (caddr_t)&lifr);
+				changed_arp_muxid = B_TRUE;
+			}
+		}
+	}
+
+	if (ioctl(mux_fd, I_PUNLINK, ip_muxid) < 0) {
+		if (changed_arp_muxid) {
+			/*
+			 * Some error occurred, and we need to restore
+			 * everything back to what it was.
+			 */
+			ret = errno;
+			lifr.lifr_arp_muxid = arp_muxid;
+			lifr.lifr_ip_muxid = ip_muxid;
+			(void) ioctl(muxid_fd, SIOCSLIFMUXID, (caddr_t)&lifr);
+		}
+	}
+done:
+	if (muxid_fd != -1)
+		(void) close(muxid_fd);
+	if (mux_fd != -1)
+		(void) close(mux_fd);
+
+	return (ret);
+}
+
+/*
+ * Plumbs the interface `ifname'.
+ */
+int
+solaris_plumb_if(int sock, const char *ifname, sa_family_t af)
+{
+	int		ip_muxid;
+	int		mux_fd = -1, ip_fd, arp_fd;
+	char		*udp_dev_name;
+	dlpi_handle_t	dh_arp = NULL, dh_ip = NULL;
+	uint64_t	ifflags;
+	uint_t		dlpi_flags;
+	int		status = 0;
+	const char	*linkname;
+	int		ret;
+
+	if (solaris_if_enabled(sock, ifname, NULL) == 0) {
+		status = EEXIST;
+		goto done;
+	}
+
+	dlpi_flags = DLPI_NOATTACH;
+	linkname = ifname;
+
+	/*
+	 * We use DLPI_NOATTACH because the ip module will do the attach
+	 * itself for DLPI style-2 devices.
+	 */
+	ret = dlpi_open(linkname, &dh_ip, dlpi_flags);
+	if (ret != DLPI_SUCCESS) {
+		ret = (ret == DL_SYSERR) ? errno : EOPNOTSUPP;
+		goto done;
+	}
+
+	ip_fd = dlpi_fd(dh_ip);
+	if (ioctl(ip_fd, I_PUSH, IP_MOD_NAME) == -1) {
+		status = errno;
+		goto done;
+	}
+
+	if (af == AF_INET) {
+		ifflags = IFF_IPV4;
+	} else {
+		ifflags = IFF_IPV6;
+		ifflags |= IFF_NOLINKLOCAL;
+	}
+
+	status = slifname(ifname, ifflags, ip_fd);
+	if (status != 0)
+		goto done;
+
+	/* Get the full set of existing flags for this stream */
+	status = solaris_get_flags(sock, ifname, &ifflags);
+	if (status != 0)
+		goto done;
+
+	udp_dev_name = (af == AF_INET6 ? UDP6_DEV_NAME : UDP_DEV_NAME);
+	status = open_arp_on_udp(udp_dev_name, &mux_fd);
+	if (status != 0)
+		goto done;
+
+	/* Check if arp is not needed */
+	if (ifflags & (IFF_NOARP|IFF_IPV6)) {
+		/*
+		 * PLINK the interface stream so that the application can exit
+		 * without tearing down the stream.
+		 */
+		if ((ip_muxid = ioctl(mux_fd, I_PLINK, ip_fd)) == -1)
+			status = errno;
+		goto done;
+	}
+
+	/*
+	 * This interface does use ARP, so set up a separate stream
+	 * from the interface to ARP.
+	 *
+	 * We use DLPI_NOATTACH because the arp module will do the attach
+	 * itself for DLPI style-2 devices.
+	 */
+	ret = dlpi_open(linkname, &dh_arp, dlpi_flags);
+	if (ret != DLPI_SUCCESS) {
+		ret = (ret == DL_SYSERR) ? errno : EOPNOTSUPP;
+		goto done;
+	}
+
+	arp_fd = dlpi_fd(dh_arp);
+	if (ioctl(arp_fd, I_PUSH, ARP_MOD_NAME) == -1) {
+		status = errno;
+		goto done;
+	}
+	status = slifname_arp(ifname, ifflags, arp_fd);
+	if (status != 0)
+		goto done;
+	/*
+	 * PLINK the IP and ARP streams so that we can exit
+	 * without tearing down the stream.
+	 */
+	if ((ip_muxid = ioctl(mux_fd, I_PLINK, ip_fd)) == -1) {
+		status = errno;
+		goto done;
+	}
+
+	if (ioctl(mux_fd, I_PLINK, arp_fd) < 0) {
+		status = errno;
+		(void) ioctl(mux_fd, I_PUNLINK, ip_muxid);
+	}
+
+done:
+	dlpi_close(dh_ip);
+	if (dh_arp != NULL)
+		dlpi_close(dh_arp);
+	if (mux_fd != -1)
+		(void) close(mux_fd);
+
+	return (status);
+}
+
+int
+solaris_init_rad()
+{
+	if (rad_conn == NULL) {
+		rad_conn = rc_connect_unix(NULL, B_TRUE, NULL);
+		if (rad_conn == NULL) {
+			return (ENODEV); /* Not sure what to return */
+		}
+	}
+	return (0);
+}
+
+static rc_err_t
+test_dlclass(const char *key, dlmgr_DLValue_t *dlval, void *arg)
+{
+	if (strcmp(key, "class") == 0)
+		(void) strlcpy((char *)arg, dlval->ddlv_sval, DLADM_STRSIZE);
+	dlmgr_DLValue_free(dlval);
+	return (RCE_OK);
+}
+
+static rc_err_t
+test_dllower(const char *key, dlmgr_DLValue_t *dlval, void *arg)
+{
+	/* TODO(gmoodalb): seems like we need physname/devname here */
+	if (strcmp(key, "over") == 0) {
+		(void) strlcpy((char *)arg, dlval->ddlv_slist[0],
+		    DLADM_STRSIZE);
+	}
+	dlmgr_DLValue_free(dlval);
+	return (RCE_OK);
+}
+
+static int
+solaris_get_dlinfo(const char *netdev_name, char *info_val, size_t info_len,
+    rc_err_t (*test_cb)(const char *, dlmgr_DLValue_t *, void *))
+{
+	dlmgr__rad_dict_string_DLValue_t *linkinfo = NULL;
+	dlmgr_DatalinkError_t	*derrp = NULL;
+	rc_instance_t		*link = NULL;
+	rc_err_t		status;
+	char			propstr[DLADM_STRSIZE];
+	int			error = 0;
+
+	status = dlmgr_Datalink__rad_lookup(rad_conn, B_TRUE, &link, 1,
+	    "name", netdev_name);
+	if (status != RCE_OK) {
+		error = ENODEV;
+		goto out;
+	}
+
+	status = dlmgr_Datalink_getInfo(link, NULL, 0, &linkinfo, &derrp);
+	if (status != RCE_OK) {
+		if (status == RCE_SERVER_OBJECT) {
+			dpif_log(derrp->dde_err,
+			    "failed Datalink_getInfo(%s): %s",
+			    netdev_name, derrp->dde_errmsg);
+		}
+		error = ENOTSUP;
+		goto out;
+	}
+
+	propstr[0] = '\0';
+	status = dlmgr__rad_dict_string_DLValue_map(linkinfo, test_cb,
+	    propstr);
+	if (status != RCE_OK) {
+		error = EINVAL;
+		goto out;
+	}
+
+	memcpy(info_val, propstr, info_len);
+out:
+	dlmgr__rad_dict_string_DLValue_free(linkinfo);
+	dlmgr_DatalinkError_free(derrp);
+	rc_instance_rele(link);
+	return (error);
+}
+
+int
+solaris_get_devname(const char *netdev_name, char *name_val, size_t name_len)
+{
+	dlmgr__rad_dict_string_DLValue_t *linkinfo = NULL;
+	dlmgr_DLValue_t		*dlval = NULL;
+	dlmgr_DatalinkError_t	*derrp = NULL;
+	rc_instance_t		*link = NULL;
+	rc_err_t		status;
+	int			error = 0;
+
+	status = dlmgr_Physical__rad_lookup(rad_conn, B_TRUE, &link, 1,
+	    "name", netdev_name);
+	if (status != RCE_OK) {
+		error = ENODEV;
+		goto out;
+	}
+
+	status = dlmgr_Physical_getInfo(link, NULL, 0, &linkinfo, &derrp);
+	if (status != RCE_OK) {
+		if (status == RCE_SERVER_OBJECT) {
+			dpif_log(derrp->dde_err,
+			    "failed Physical_getInfo(%s): %s",
+			    netdev_name, derrp->dde_errmsg);
+		}
+		error = ENODEV;
+		goto out;
+	}
+
+	status = dlmgr__rad_dict_string_DLValue_get(linkinfo, "device",
+	    &dlval);
+	if (status != RCE_OK) {
+		error = ENODEV;
+		goto out;
+	}
+
+	memcpy(name_val, dlval->ddlv_sval, name_len);
+out:
+	dlmgr_DLValue_free(dlval);
+	dlmgr_DatalinkError_free(derrp);
+	dlmgr__rad_dict_string_DLValue_free(linkinfo);
+	rc_instance_rele(link);
+	return (error);
+}
+
+int
+solaris_get_dlclass(const char *netdev_name, char *class_val, size_t class_len)
+{
+	return (solaris_get_dlinfo(netdev_name, class_val, class_len,
+	    test_dlclass));
+}
+
+int
+solaris_get_dllower(const char *netdev_name, char *lower_val, size_t lower_len)
+{
+	return (solaris_get_dlinfo(netdev_name, lower_val, lower_len,
+	    test_dllower));
+}
+
+int
+solaris_get_dlprop(const char *netdev_name, const char *prop_name,
+    const char *field_name, char *prop_value, size_t prop_len)
+{
+	dlmgr_DLDict_t		**dlist = NULL;
+	dlmgr_DLValue_t		*dlval = NULL;
+	dlmgr_DatalinkError_t   *derrp = NULL;
+	rc_instance_t		*link = NULL;
+	rc_err_t		status;
+	const char		*props[1];
+	const char		*fields[1];
+	int			ndlist = 0;
+	int			error = 0, i = 0;
+	char			buf[DLADM_STRSIZE];
+
+	status = dlmgr_Datalink__rad_lookup(rad_conn, B_TRUE, &link, 1,
+	    "name", netdev_name);
+	if (status != RCE_OK) {
+		error = ENODEV;
+		goto out;
+	}
+
+	props[0] = prop_name;
+	fields[0] = field_name;
+	status = dlmgr_Datalink_getProperties(link, props, 1, fields, 1,
+	    &dlist, &ndlist, &derrp);
+	if (status != RCE_OK) {
+		if (status == RCE_SERVER_OBJECT) {
+			dpif_log(derrp->dde_err,
+			    "failed Datalink_getProperties(%s, %s): %s",
+			    netdev_name, prop_name, derrp->dde_errmsg);
+		}
+		error = ENOTSUP;
+		goto out;
+	}
+	status = dlmgr__rad_dict_string_DLValue_get((*dlist)->ddld_map,
+	    field_name, &dlval);
+	if (status != RCE_OK) {
+		error = EINVAL;
+		goto out;
+	}
+	switch (dlval->ddlv_type) {
+	case DDLVT_STRING:
+		if (dlval->ddlv_sval == NULL) {
+			error = EINVAL;
+			goto out;
+		}
+		memcpy(prop_value, dlval->ddlv_sval, prop_len);
+		break;
+	case DDLVT_STRINGS:
+		if (dlval->ddlv_slist_count == 0) {
+			error = EINVAL;
+			goto out;
+		}
+		for (i = 0; i < dlval->ddlv_slist_count; i++) {
+			(void) snprintf(buf, sizeof (buf), "%s%s",
+			    (i != 0 ? "," : ""), dlval->ddlv_slist[i]);
+		}
+		memcpy(prop_value, buf, prop_len);
+		break;
+	case DDLVT_ULONG:
+		if (dlval->ddlv_ulval == NULL) {
+			error = EINVAL;
+			goto out;
+		}
+		(void) snprintf(buf, sizeof (buf), "%llu", *dlval->ddlv_ulval);
+		memcpy(prop_value, buf, prop_len);
+		break;
+	case DDLVT_BOOLEAN:
+	case DDLVT_BOOLEANS:
+	case DDLVT_LONG:
+	case DDLVT_LONGS:
+	case DDLVT_ULONGS:
+	case DDLVT_DICTIONARY:
+	case DDLVT_DICTIONARYS:
+	default:
+		ovs_assert(0);
+		break;
+	}
+out:
+	dlmgr_DLValue_free(dlval);
+	dlmgr_DatalinkError_free(derrp);
+	dlmgr_DLDict_array_free(dlist, ndlist);
+	rc_instance_rele(link);
+	return (error);
+}
+
+static int
+solaris_set_dlprop(const char *netdev_name, const char *propname, void *arg,
+    dlmgr_DLValueType_t vtype)
+{
+	dlmgr__rad_dict_string_DLValue_t *sprop_dict = NULL;
+	dlmgr_DLValue_t			*old_val = NULL;
+	dlmgr_DLValue_t			new_val;
+	rc_instance_t			*link = NULL;
+	rc_err_t			status;
+	dlmgr_DatalinkError_t   	*derrp = NULL;
+	int				error = 0;
+
+	status = dlmgr_Datalink__rad_lookup(rad_conn, B_TRUE, &link, 1,
+	    "name", netdev_name);
+	if (status != RCE_OK) {
+		error = ENODEV;
+		goto out;
+	}
+
+	sprop_dict = dlmgr__rad_dict_string_DLValue_create(link);
+	if (sprop_dict == NULL) {
+		status = ENOMEM;
+		goto out;
+	}
+
+	bzero(&new_val, sizeof (new_val));
+	new_val.ddlv_type = vtype;
+	switch (vtype) {
+	case DDLVT_BOOLEAN:
+		new_val.ddlv_bval = arg;
+		status = dlmgr__rad_dict_string_DLValue_put(
+		    sprop_dict, propname, &new_val, &old_val);
+		if (status != RCE_OK) {
+			error = EINVAL;
+			goto out;
+		}
+		dlmgr_DLValue_free(old_val);
+		break;
+	case DDLVT_ULONG:
+		new_val.ddlv_ulval = arg;
+		status = dlmgr__rad_dict_string_DLValue_put(
+		    sprop_dict, propname, &new_val, &old_val);
+		if (status != RCE_OK) {
+			error = EINVAL;
+			goto out;
+		}
+		dlmgr_DLValue_free(old_val);
+		break;
+	case DDLVT_STRING:
+		new_val.ddlv_sval = arg;
+		status = dlmgr__rad_dict_string_DLValue_put(
+		    sprop_dict, propname, &new_val, &old_val);
+		if (status != RCE_OK) {
+			error = EINVAL;
+			goto out;
+		}
+		dlmgr_DLValue_free(old_val);
+		break;
+	case DDLVT_LONG:
+	case DDLVT_LONGS:
+	case DDLVT_ULONGS:
+	case DDLVT_STRINGS:
+	case DDLVT_BOOLEANS:
+	case DDLVT_DICTIONARY:
+	case DDLVT_DICTIONARYS:
+	default:
+		ovs_assert(0);
+		break;
+	}
+
+	/* we need to add temporary flag */
+	bzero(&new_val, sizeof (new_val));
+	old_val = NULL;
+	new_val.ddlv_type = DDLVT_BOOLEAN;
+	new_val.ddlv_bval = &b_true;
+	status = dlmgr__rad_dict_string_DLValue_put(
+	    sprop_dict, "temporary", &new_val, &old_val);
+	if (status != RCE_OK) {
+		error = EINVAL;
+		goto out;
+	}
+	dlmgr_DLValue_free(old_val);
+
+	status = dlmgr_Datalink_setProperties(link, sprop_dict, &derrp);
+	if (status != RCE_OK) {
+		if (status == RCE_SERVER_OBJECT) {
+			dpif_log(derrp->dde_err,
+			    "failed Datalink_setPropertiess(%s, %s): %s",
+			    netdev_name, propname, derrp->dde_errmsg);
+		}
+		error = ENOTSUP;
+	}
+out:
+	dlmgr_DatalinkError_free(derrp);
+	dlmgr__rad_dict_string_DLValue_free(sprop_dict);
+	rc_instance_rele(link);
+	return (error);
+}
+
+int
+solaris_set_dlprop_boolean(const char *netdev_name, const char *propname,
+    void *arg)
+{
+	return (solaris_set_dlprop(netdev_name, propname, arg, DDLVT_BOOLEAN));
+}
+
+int
+solaris_set_dlprop_ulong(const char *netdev_name, const char *propname,
+    void *arg)
+{
+	return (solaris_set_dlprop(netdev_name, propname, arg, DDLVT_ULONG));
+}
+
+int
+solaris_set_dlprop_string(const char *netdev_name, const char *propname,
+    void *arg)
+{
+	return (solaris_set_dlprop(netdev_name, propname, arg, DDLVT_STRING));
+}
+
+int
+solaris_create_vnic(const char *linkname, const char *vnicname)
+{
+	dlmgr__rad_dict_string_DLValue_t *prop = NULL;
+	dlmgr__rad_dict_string_DLValue_t *macaddr_info = NULL;
+	dlmgr_DatalinkError_t		*derrp = NULL;
+	dlmgr_DLValue_t			*old_val = NULL;
+	dlmgr_DLValue_t			name_val;
+	dlmgr_DLValue_t			temp_val;
+	dlmgr_DLValue_t			type_val;
+	dlmgr_DLValue_t			macaddr_info_val;
+	rc_instance_t			*linkmgr = NULL;
+	rc_instance_t			*vnic = NULL;
+	rc_err_t			status;
+
+	status = dlmgr_DatalinkManager__rad_lookup(rad_conn, B_TRUE,
+	    &linkmgr, 0);
+	if (status != RCE_OK)
+		goto out;
+
+	prop = dlmgr__rad_dict_string_DLValue_create(linkmgr);
+	if (prop == NULL)
+		goto out;
+
+	bzero(&name_val, sizeof (name_val));
+	name_val.ddlv_type = DDLVT_STRING;
+	/* linkname is 'const char *' and ddlv_sval is 'char *' */
+	name_val.ddlv_sval = strdupa(linkname);
+	status = dlmgr__rad_dict_string_DLValue_put(prop, "lower-link",
+	    &name_val, &old_val);
+	if (status != RCE_OK)
+		goto out;
+	dlmgr_DLValue_free(old_val);
+	old_val = NULL;
+
+	bzero(&temp_val, sizeof (temp_val));
+	temp_val.ddlv_type = DDLVT_BOOLEAN;
+	temp_val.ddlv_bval = &b_true;
+	status = dlmgr__rad_dict_string_DLValue_put(prop, "temporary",
+	    &temp_val, &old_val);
+	if (status != RCE_OK)
+		goto out;
+	dlmgr_DLValue_free(old_val);
+	old_val = NULL;
+
+	macaddr_info = dlmgr__rad_dict_string_DLValue_create(linkmgr);
+	if (macaddr_info == NULL)
+		goto out;
+
+	bzero(&type_val, sizeof (type_val));
+	type_val.ddlv_type = DDLVT_STRING;
+	type_val.ddlv_sval = strdupa("auto");
+	status = dlmgr__rad_dict_string_DLValue_put(macaddr_info,
+	    "mac-address-type", &type_val, &old_val);
+	if (status != RCE_OK)
+		goto out;
+	dlmgr_DLValue_free(old_val);
+	old_val = NULL;
+
+	bzero(&macaddr_info_val, sizeof (macaddr_info_val));
+	macaddr_info_val.ddlv_type = DDLVT_DICTIONARY;
+	macaddr_info_val.ddlv_dval = macaddr_info;
+	status = dlmgr__rad_dict_string_DLValue_put(prop, "mac-address-info",
+	    &macaddr_info_val, &old_val);
+	if (status != RCE_OK)
+		goto out;
+	dlmgr_DLValue_free(old_val);
+
+	status = dlmgr_DatalinkManager_createVNIC(linkmgr, vnicname, prop,
+	    &vnic, &derrp);
+	if (status == RCE_SERVER_OBJECT) {
+		dpif_log(derrp->dde_err,
+		    "failed DatalinkManager_createVNIC(%s): %s",
+		    vnicname, derrp->dde_errmsg);
+	}
+	rc_instance_rele(vnic);
+	dlmgr_DatalinkError_free(derrp);
+out:
+	dlmgr__rad_dict_string_DLValue_free(macaddr_info);
+	dlmgr__rad_dict_string_DLValue_free(prop);
+	rc_instance_rele(linkmgr);
+	return ((status != RCE_OK) ? ENOTSUP : 0);
+}
+
+int
+solaris_modify_vnic(const char *linkname, const char *vnicname)
+{
+	dlmgr__rad_dict_string_DLValue_t *sprop_dict = NULL;
+	dlmgr__rad_dict_string_DLValue_t *macaddr_info = NULL;
+	dlmgr_DLValue_t			*old_val = NULL;
+	dlmgr_DLValue_t			new_val;
+	dlmgr_DLValue_t			type_val;
+	dlmgr_DLValue_t			macaddr_info_val;
+	rc_instance_t			*link = NULL;
+	rc_err_t			status;
+	dlmgr_DatalinkError_t   	*derrp = NULL;
+	int				error = 0;
+
+	status = dlmgr_Datalink__rad_lookup(rad_conn, B_TRUE, &link, 1,
+	    "name", vnicname);
+	if (status != RCE_OK) {
+		error = ENODEV;
+		goto out;
+	}
+
+	sprop_dict = dlmgr__rad_dict_string_DLValue_create(link);
+	if (sprop_dict == NULL) {
+		status = ENOMEM;
+		goto out;
+	}
+
+	bzero(&new_val, sizeof (new_val));
+	new_val.ddlv_type = DDLVT_STRING;
+	new_val.ddlv_sval = strdupa(linkname);
+	status = dlmgr__rad_dict_string_DLValue_put(
+	    sprop_dict, "lower-link", &new_val, &old_val);
+	if (status != RCE_OK) {
+		error = EINVAL;
+		goto out;
+	}
+	dlmgr_DLValue_free(old_val);
+
+	/* we need to add temporary flag */
+	bzero(&new_val, sizeof (new_val));
+	old_val = NULL;
+	new_val.ddlv_type = DDLVT_BOOLEAN;
+	new_val.ddlv_bval = &b_true;
+	status = dlmgr__rad_dict_string_DLValue_put(
+	    sprop_dict, "temporary", &new_val, &old_val);
+	if (status != RCE_OK) {
+		error = EINVAL;
+		goto out;
+	}
+	dlmgr_DLValue_free(old_val);
+
+	macaddr_info = dlmgr__rad_dict_string_DLValue_create(link);
+	if (macaddr_info == NULL)
+		goto out;
+
+	bzero(&type_val, sizeof (type_val));
+	old_val = NULL;
+	type_val.ddlv_type = DDLVT_STRING;
+	type_val.ddlv_sval = strdupa("auto");
+	status = dlmgr__rad_dict_string_DLValue_put(macaddr_info,
+	    "mac-address-type", &type_val, &old_val);
+	if (status != RCE_OK)
+		goto out;
+	dlmgr_DLValue_free(old_val);
+
+	bzero(&macaddr_info_val, sizeof (macaddr_info_val));
+	old_val = NULL;
+	macaddr_info_val.ddlv_type = DDLVT_DICTIONARY;
+	macaddr_info_val.ddlv_dval = macaddr_info;
+	status = dlmgr__rad_dict_string_DLValue_put(sprop_dict,
+	    "mac-address-info", &macaddr_info_val, &old_val);
+	if (status != RCE_OK)
+		goto out;
+	dlmgr_DLValue_free(old_val);
+
+	status = dlmgr_Datalink_setProperties(link, sprop_dict, &derrp);
+	if (status != RCE_OK) {
+		if (status == RCE_SERVER_OBJECT) {
+			dpif_log(derrp->dde_err,
+			    "failed Datalink_setPropertiess(%s, lower-link): "
+			    " %s", vnicname, derrp->dde_errmsg);
+		}
+		error = ENOTSUP;
+	}
+out:
+	dlmgr_DatalinkError_free(derrp);
+	dlmgr__rad_dict_string_DLValue_free(macaddr_info);
+	dlmgr__rad_dict_string_DLValue_free(sprop_dict);
+	rc_instance_rele(link);
+	return (error);
+}
+
+int
+solaris_delete_vnic(const char *vnicname)
+{
+	dlmgr__rad_dict_string_DLValue_t *prop = NULL;
+	dlmgr_DatalinkError_t		*derrp = NULL;
+	dlmgr_DLValue_t			*old_val = NULL;
+	dlmgr_DLValue_t			temp_val;
+	rc_instance_t			*linkmgr = NULL;
+	rc_err_t			status;
+	int				err = 0;
+
+	status = dlmgr_DatalinkManager__rad_lookup(rad_conn, B_TRUE,
+	    &linkmgr, 0);
+	if (status != RCE_OK) {
+		err = EINVAL;
+		goto out;
+	}
+
+	prop = dlmgr__rad_dict_string_DLValue_create(linkmgr);
+	if (prop == NULL) {
+		err = EINVAL;
+		goto out;
+	}
+
+	bzero(&temp_val, sizeof (temp_val));
+	temp_val.ddlv_type = DDLVT_BOOLEAN;
+	temp_val.ddlv_bval = &b_true;
+	status = dlmgr__rad_dict_string_DLValue_put(prop, "temporary",
+	    &temp_val, &old_val);
+	if (status != RCE_OK) {
+		err = EINVAL;
+		goto out;
+	}
+	dlmgr_DLValue_free(old_val);
+
+	status = dlmgr_DatalinkManager_deleteVNIC(linkmgr, vnicname, prop,
+	    &derrp);
+	if (status != RCE_OK) {
+		err = -1;
+		if (status == RCE_SERVER_OBJECT) {
+			err = derrp->dde_err;
+			dpif_log(err,
+			    "failed DatalinkManager_deleteVNIC(%s): %s",
+			    vnicname, derrp->dde_errmsg);
+		}
+	}
+	dlmgr_DatalinkError_free(derrp);
+
+out:
+	dlmgr__rad_dict_string_DLValue_free(prop);
+	rc_instance_rele(linkmgr);
+	return (err);
+}
+
+int
+solaris_create_etherstub(const char *name)
+{
+	dlmgr__rad_dict_string_DLValue_t *prop = NULL;
+	dlmgr_DatalinkError_t		*derrp = NULL;
+	dlmgr_DLValue_t			*old_val = NULL;
+	dlmgr_DLValue_t			temp_val;
+	rc_instance_t			*linkmgr = NULL;
+	rc_instance_t			*etherstub = NULL;
+	rc_err_t			status;
+
+	status = dlmgr_DatalinkManager__rad_lookup(rad_conn, B_TRUE,
+	    &linkmgr, 0);
+	if (status != RCE_OK)
+		goto out;
+
+	prop = dlmgr__rad_dict_string_DLValue_create(linkmgr);
+	if (prop == NULL)
+		goto out;
+
+	bzero(&temp_val, sizeof (temp_val));
+	temp_val.ddlv_type = DDLVT_BOOLEAN;
+	temp_val.ddlv_bval = &b_true;
+	status = dlmgr__rad_dict_string_DLValue_put(prop, "temporary",
+	    &temp_val, &old_val);
+	if (status != RCE_OK)
+		goto out;
+	dlmgr_DLValue_free(old_val);
+
+	status = dlmgr_DatalinkManager_createEtherstub(linkmgr, name, prop,
+	    &etherstub, &derrp);
+	if (status == RCE_SERVER_OBJECT) {
+		dpif_log(derrp->dde_err,
+		    "failed DatalinkManager_createEtherstub(%s): %s",
+		    name, derrp->dde_errmsg);
+	}
+	rc_instance_rele(etherstub);
+	dlmgr_DatalinkError_free(derrp);
+
+out:
+	dlmgr__rad_dict_string_DLValue_free(prop);
+	rc_instance_rele(linkmgr);
+
+	return ((status != RCE_OK) ? ENOTSUP : 0);
+}
+
+int
+solaris_delete_etherstub(const char *name)
+{
+	dlmgr__rad_dict_string_DLValue_t *prop = NULL;
+	dlmgr_DatalinkError_t		*derrp = NULL;
+	dlmgr_DLValue_t			*old_val = NULL;
+	dlmgr_DLValue_t			temp_val;
+	rc_instance_t			*linkmgr = NULL;
+	rc_err_t			status;
+
+	status = dlmgr_DatalinkManager__rad_lookup(rad_conn, B_TRUE,
+	    &linkmgr, 0);
+	if (status != RCE_OK)
+		goto out;
+
+	prop = dlmgr__rad_dict_string_DLValue_create(linkmgr);
+	if (prop == NULL)
+		goto out;
+
+	bzero(&temp_val, sizeof (temp_val));
+	temp_val.ddlv_type = DDLVT_BOOLEAN;
+	temp_val.ddlv_bval = &b_true;
+	status = dlmgr__rad_dict_string_DLValue_put(prop, "temporary",
+	    &temp_val, &old_val);
+	if (status != RCE_OK)
+		goto out;
+	dlmgr_DLValue_free(old_val);
+
+	status = dlmgr_DatalinkManager_deleteEtherstub(linkmgr, name, prop,
+	    &derrp);
+	if (status == RCE_SERVER_OBJECT) {
+		dpif_log(derrp->dde_err,
+		    "failed DatalinkManager_deleteEtherstub(%s): %s",
+		    name, derrp->dde_errmsg);
+	}
+	dlmgr_DatalinkError_free(derrp);
+
+out:
+	dlmgr__rad_dict_string_DLValue_free(prop);
+	rc_instance_rele(linkmgr);
+	return ((status != RCE_OK) ? ENOTSUP : 0);
+}
+
+boolean_t
+solaris_etherstub_exists(const char *name)
+{
+	rc_instance_t	*etherstub = NULL;
+	boolean_t	exists;
+	rc_err_t	status;
+
+	status = dlmgr_Etherstub__rad_lookup(rad_conn, B_TRUE, &etherstub, 1,
+	    "name", name);
+	if (status == RCE_OK) {
+		dlmgr__rad_dict_string_DLValue_t *linkinfo = NULL;
+		dlmgr_DatalinkError_t *derrp = NULL;
+
+		status = dlmgr_Etherstub_getInfo(etherstub, NULL, 0,
+		    &linkinfo, &derrp);
+		if (status == RCE_OK) {
+			exists = _B_TRUE;
+			dlmgr__rad_dict_string_DLValue_free(linkinfo);
+		} else {
+			exists = _B_FALSE;
+			dlmgr_DatalinkError_free(derrp);
+		}
+	} else {
+		exists = _B_FALSE;
+	}
+
+	rc_instance_rele(etherstub);
+	return (exists);
+}
+
+static int
+flow_str2mac(const char *str, uchar_t *f, size_t maclen)
+{
+	uchar_t		*addr = NULL;
+	int		len, err = 0;
+
+	if ((addr = _link_aton(str, &len)) == NULL)
+		return ((len == -1) ? EINVAL : ENOMEM);
+
+	if (len != maclen) {
+		err = EINVAL;
+		goto done;
+	}
+
+	bcopy(addr, f, maclen);
+done:
+	free(addr);
+	return (err);
+}
+
+static int
+flow_str2addr(const char *str, in6_addr_t *f, int *afp)
+{
+	struct in_addr	v4addr;
+	struct in6_addr	v6addr;
+	int		af;
+
+	if (inet_pton(AF_INET, str, &v4addr.s_addr) == 1) {
+		af = AF_INET;
+	} else if (inet_pton(AF_INET6, str, v6addr.s6_addr) == 1) {
+		af = AF_INET6;
+	} else {
+		return (EINVAL);
+	}
+
+	if (af == AF_INET) {
+		IN6_INADDR_TO_V4MAPPED(&v4addr, f);
+	} else {
+		*f = v6addr;
+	}
+	*afp = af;
+	return (0);
+}
+
+static int
+solaris_flowinfo2linkname(dlmgr__rad_dict_string_DLValue_t *flowinfo,
+    char *linkname, size_t size)
+{
+	dlmgr__rad_dict_string_DLValue_t *fdict;
+	dlmgr_DLValue_t		*flist = NULL, *dlval = NULL;
+	rc_err_t		status;
+	int			err = 0;
+
+	status = dlmgr__rad_dict_string_DLValue_get(flowinfo, "filters",
+	    &flist);
+	if (status != RCE_OK)
+		return (EINVAL);
+
+	fdict = flist->ddlv_dlist[0]->ddld_map;
+
+	status = dlmgr__rad_dict_string_DLValue_get(fdict,
+	    "linkname", &dlval);
+	if (status != RCE_OK) {
+		err = EINVAL;
+		goto out;
+	}
+	(void) strlcpy(linkname, dlval->ddlv_sval, size);
+	dlmgr_DLValue_free(dlval);
+out:
+	dlmgr_DLValue_free(flist);
+	return (err);
+}
+
+static void
+flow_mac2str(uint8_t *f, uint8_t *m, char *buf, char *rbuf, size_t buf_len,
+    size_t rbuf_len)
+{
+	char *str = NULL;
+	char *pstr = NULL;
+
+	buf[0] = '\0';
+	rbuf[0] = '\0';
+
+	if (!eth_addr_is_zero(f) || !eth_addr_is_zero(m)) {
+		str = _link_ntoa(f, NULL, ETHERADDRL, IFT_ETHER);
+		if (str != NULL) {
+			pstr = _link_ntoa(m, NULL, ETHERADDRL, IFT_ETHER);
+			if (pstr != NULL) {
+				(void) snprintf(buf, buf_len, "%s", str);
+				(void) snprintf(rbuf, rbuf_len, "%s", pstr);
+				free(pstr);
+			}
+			free(str);
+		}
+	}
+}
+
+static void
+flow_addr2str(struct in6_addr *f6, struct in6_addr *m6, uint32_t f4,
+    uint32_t m4, char *buf, char *rbuf, size_t buf_len, size_t rbuf_len)
+{
+	struct in_addr ipaddr;
+	char abuf[INET6_ADDRSTRLEN], mbuf[INET6_ADDRSTRLEN];
+
+	buf[0] = '\0';
+	rbuf[0] = '\0';
+	if ((f6 != NULL) && (m6 != NULL) &&
+	    (!ipv6_addr_equals(f6, &in6addr_any) ||
+	    !ipv6_addr_equals(m6, &in6addr_any))) {
+		(void) inet_ntop(AF_INET6, f6, abuf, INET6_ADDRSTRLEN);
+		(void) inet_ntop(AF_INET6, m6, mbuf, INET6_ADDRSTRLEN);
+		(void) snprintf(buf, buf_len, "%s", abuf);
+		(void) snprintf(rbuf, rbuf_len, "%s", mbuf);
+	} else if (f4 != 0 || m4 != 0) {
+		ipaddr.s_addr = f4;
+		(void) strlcpy(abuf, inet_ntoa(ipaddr), sizeof (abuf));
+		ipaddr.s_addr = m4;
+		(void) strlcpy(mbuf, inet_ntoa(ipaddr), sizeof (mbuf));
+		(void) snprintf(buf, buf_len, "%s", abuf);
+		(void) snprintf(rbuf, rbuf_len, "%s", mbuf);
+	}
+}
+
+static int
+flow_ofports2propstr(uint32_t *ofports, int nofports, char ***ofportstr,
+    int *cnt)
+{
+	dladm_status_t		dlstatus;
+	mac_propval_range_t	*pv_range = NULL;
+	mac_propval_uint32_range_t *ur;
+	char			buf[DLADM_STRSIZE];
+	int			i, error = 0;
+	char			**strs;
+
+	*ofportstr = NULL;
+	*cnt = 0;
+	if (nofports != 0) {
+		/* Sort OF port list and convert it to a mac_propval_range */
+		dlstatus = dladm_list2range(ofports, nofports,
+		    MAC_PROPVAL_UINT32, &pv_range);
+		if (dlstatus != DLADM_STATUS_OK) {
+			error = EINVAL;
+			goto out;
+		}
+
+		strs = calloc(pv_range->mpr_count, sizeof (char *));
+		if (strs == NULL) {
+			error = ENOMEM;
+			goto out;
+		}
+		/*
+		 * Write ranges and individual elements into their own
+		 * buffer.
+		 */
+		for (i = 0; i < pv_range->mpr_count; i++, ur++) {
+			ur = &pv_range->mpr_range_uint32[i];
+			if (ur->mpur_min == ur->mpur_max) {
+				/* single element */
+				(void) snprintf(buf, sizeof (buf), "%u",
+				    ur->mpur_min);
+				strs[i] = strdup(buf);
+			} else {
+				/* range of elements */
+				(void) snprintf(buf, sizeof (buf), "%u-%u",
+				    ur->mpur_min, ur->mpur_max);
+				strs[i] = strdup(buf);
+			}
+			if (strs[i] == NULL) {
+				while (i >= 0) {
+					free(strs[i]);
+					i--;
+				}
+				free(strs);
+				error = ENOMEM;
+				goto out;
+			}
+		}
+		*cnt = pv_range->mpr_count;
+		*ofportstr = strs;
+	}
+out:
+	free(pv_range);
+	return (error);
+}
+
+static int
+dlmgr_DLValue_putstring(dlmgr__rad_dict_string_DLValue_t *ddvp,
+    const char *key, char *buf, char *dstr, size_t dstrlen)
+{
+	dlmgr_DLValue_t  *old_val = NULL;
+	dlmgr_DLValue_t  new_val;
+	rc_err_t status;
+
+	if (strlen(buf) != 0) {
+		bzero(&new_val, sizeof (new_val));
+		new_val.ddlv_type = DDLVT_STRING;
+		new_val.ddlv_sval = buf;
+		if ((status = dlmgr__rad_dict_string_DLValue_put(ddvp, key,
+		    &new_val, &old_val)) != RCE_OK) {
+			return (EINVAL);
+		}
+		snprintf(dstr, dstrlen, "%s,%s=%s", dstr, key, buf);
+		dlmgr_DLValue_free(old_val);
+	}
+	return (0);
+}
+
+static int
+dlmgr_DLValue_fm_putstring(dlmgr__rad_dict_string_DLValue_t *ddvp,
+    dlmgr__rad_dict_string_DLValue_t *ddmp, const char *key,
+    char *buf, char *rbuf, char *dstr, size_t dstrlen)
+{
+	dlmgr_DLValue_t  *old_val = NULL;
+	dlmgr_DLValue_t  new_val;
+	rc_err_t status;
+
+	if (strlen(buf) != 0) {
+		bzero(&new_val, sizeof (new_val));
+		new_val.ddlv_type = DDLVT_STRING;
+		new_val.ddlv_sval = buf;
+		if ((status = dlmgr__rad_dict_string_DLValue_put(ddvp, key,
+		    &new_val, &old_val)) != RCE_OK) {
+			return (EINVAL);
+		}
+		dlmgr_DLValue_free(old_val);
+		old_val = NULL;
+		new_val.ddlv_sval = rbuf;
+		if ((status = dlmgr__rad_dict_string_DLValue_put(ddmp, key,
+		    &new_val, &old_val)) != RCE_OK) {
+			return (EINVAL);
+		}
+		dlmgr_DLValue_free(old_val);
+
+		snprintf(dstr, dstrlen, "%s,%s=%s/%s", dstr, key, buf, rbuf);
+	}
+	return (0);
+}
+
+/*
+ * Caller allocated "pvals" is freed here
+ */
+static int
+dlmgr_DLValue_putstrings(dlmgr__rad_dict_string_DLValue_t *ddvp,
+    const char *key, char **pvals, int pvalcnt, char *dstr, size_t dstrlen)
+{
+	dlmgr_DLValue_t  *old_val = NULL;
+	dlmgr_DLValue_t  new_val;
+	rc_err_t status;
+	int i, err = 0;
+
+	if (pvalcnt != 0) {
+		bzero(&new_val, sizeof (new_val));
+		new_val.ddlv_type = DDLVT_STRINGS;
+		new_val.ddlv_slist = pvals;
+		new_val.ddlv_slist_count = pvalcnt;
+		status = dlmgr__rad_dict_string_DLValue_put(
+		    ddvp, key, &new_val, &old_val);
+		if (status != RCE_OK) {
+			err = EINVAL;
+			goto out;
+		}
+		for (i = 0; i < pvalcnt; i++)
+			snprintf(dstr, dstrlen, "%s,%s=%s", dstr, key,
+			    pvals[i]);
+		dlmgr_DLValue_free(old_val);
+out:
+		for (i = 0; i < pvalcnt; i++)
+			free(pvals[i]);
+		free(pvals);
+	}
+	return (err);
+}
+
+static int
+dlmgr_DLValue_putboolean(dlmgr__rad_dict_string_DLValue_t *ddvp,
+    const char *key, boolean_t val, char *dstr, size_t dstrlen)
+{
+	dlmgr_DLValue_t  *old_val = NULL;
+	dlmgr_DLValue_t  new_val;
+	rc_err_t status;
+
+	bzero(&new_val, sizeof (new_val));
+	new_val.ddlv_type = DDLVT_BOOLEAN;
+	new_val.ddlv_bval = &val;
+	if ((status = dlmgr__rad_dict_string_DLValue_put(ddvp, key, &new_val,
+	    &old_val)) != RCE_OK) {
+		return (EINVAL);
+	}
+	snprintf(dstr, dstrlen, "%s,%s=%s", dstr, key,
+	    val ? "true" : "false");
+	dlmgr_DLValue_free(old_val);
+	return (0);
+}
+
+static int
+dlmgr_DLValue_putulong(dlmgr__rad_dict_string_DLValue_t *ddvp, const char *key,
+    uint64_t val, char *dstr, size_t dstrlen)
+{
+	dlmgr_DLValue_t  *old_val = NULL;
+	dlmgr_DLValue_t  new_val;
+	unsigned long long ulval = val;
+	rc_err_t status;
+
+	bzero(&new_val, sizeof (new_val));
+	new_val.ddlv_type = DDLVT_ULONG;
+	new_val.ddlv_ulval = &ulval;
+	if ((status = dlmgr__rad_dict_string_DLValue_put(ddvp, key, &new_val,
+	    &old_val)) != RCE_OK) {
+		return (EINVAL);
+	}
+	snprintf(dstr, dstrlen, "%s,%s=%"PRIu64, dstr, key, val);
+	dlmgr_DLValue_free(old_val);
+	return (0);
+}
+
+static int
+dlmgr_DLValue_fm_putulong(dlmgr__rad_dict_string_DLValue_t *ddvp,
+    dlmgr__rad_dict_string_DLValue_t *ddmp, const char *key,
+    uint64_t f, uint64_t m, char *dstr, size_t dstrlen)
+{
+	dlmgr_DLValue_t  *old_val = NULL;
+	dlmgr_DLValue_t  new_val;
+	unsigned long long f_ulval = f;
+	unsigned long long m_ulval = m;
+	rc_err_t status;
+
+	bzero(&new_val, sizeof (new_val));
+	new_val.ddlv_type = DDLVT_ULONG;
+	new_val.ddlv_ulval = &f_ulval;
+	if ((status = dlmgr__rad_dict_string_DLValue_put(ddvp, key, &new_val,
+	    &old_val)) != RCE_OK) {
+		return (EINVAL);
+	}
+	dlmgr_DLValue_free(old_val);
+	old_val = NULL;
+	new_val.ddlv_ulval = &m_ulval;
+	if ((status = dlmgr__rad_dict_string_DLValue_put(ddmp, key, &new_val,
+	    &old_val)) != RCE_OK) {
+		return (EINVAL);
+	}
+	dlmgr_DLValue_free(old_val);
+	snprintf(dstr, dstrlen, "%s,%s=%"PRIu64"/%"PRIu64, dstr, key, f, m);
+	return (0);
+}
+
+static int
+solaris_flow_to_DLVal(struct flow *f, struct flow *m,
+    dlmgr__rad_dict_string_DLValue_t *ddvp,
+    dlmgr__rad_dict_string_DLValue_t *ddmp)
+{
+	char		buf[DLADM_STRSIZE], rbuf[DLADM_STRSIZE];
+	char		dstr[DLADM_STRSIZE];
+	int		err = 0;
+	boolean_t	is_arp = (ntohs(f->dl_type) == 0x806);
+
+	dstr[0] = '\0';
+	if (f->dl_type != htons(FLOW_DL_TYPE_NONE)) {
+		err = dlmgr_DLValue_fm_putulong(ddvp, ddmp, "sap",
+		    ntohs(f->dl_type), ntohs(m->dl_type), dstr, sizeof (dstr));
+		if (err != 0)
+			goto out;
+	}
+
+	if (is_arp) {
+		if (f->nw_proto != 0 || m->nw_proto != 0) {
+			err = dlmgr_DLValue_fm_putulong(ddvp, ddmp, "arp-op",
+			    f->nw_proto, m->nw_proto, dstr, sizeof (dstr));
+			if (err != 0)
+				goto out;
+		}
+
+		flow_mac2str(f->arp_sha, m->arp_sha, buf, rbuf, sizeof (buf),
+		    sizeof (rbuf));
+		if (strlen(buf) != 0) {
+			err = dlmgr_DLValue_fm_putstring(ddvp, ddmp,
+			    "arp-sender", buf, rbuf, dstr, sizeof (dstr));
+			if (err != 0)
+				goto out;
+		}
+
+		flow_mac2str(f->arp_tha, m->arp_tha, buf, rbuf, sizeof (buf),
+		    sizeof (rbuf));
+		if (strlen(buf) != 0) {
+			err = dlmgr_DLValue_fm_putstring(ddvp, ddmp,
+			    "arp-target", buf, rbuf, dstr, sizeof (dstr));
+			if (err != 0)
+				goto out;
+		}
+
+		flow_addr2str(NULL, NULL, f->nw_src, m->nw_src, buf, rbuf,
+		    sizeof (buf), sizeof (rbuf));
+		if (strlen(buf) != 0) {
+			err = dlmgr_DLValue_fm_putstring(ddvp, ddmp, "arp-sip",
+			    buf, rbuf, dstr, sizeof (dstr));
+			if (err != 0)
+				goto out;
+		}
+
+		flow_addr2str(NULL, NULL, f->nw_dst, m->nw_dst, buf, rbuf,
+		    sizeof (buf), sizeof (rbuf));
+		if (strlen(buf) != 0) {
+			err = dlmgr_DLValue_fm_putstring(ddvp, ddmp, "arp-tip",
+			    buf, rbuf,  dstr, sizeof (dstr));
+			if (err != 0)
+				goto out;
+		}
+	} else {
+		if (f->nw_proto != 0 || m->nw_proto != 0) {
+			err = dlmgr_DLValue_fm_putulong(ddvp, ddmp, "transport",
+			    f->nw_proto, m->nw_proto, dstr, sizeof (dstr));
+			if (err != 0)
+				goto out;
+		}
+
+		flow_addr2str(&f->ipv6_src, &m->ipv6_src, f->nw_src, m->nw_src,
+		    buf, rbuf, sizeof (buf), sizeof (rbuf));
+		if (strlen(buf) != 0) {
+			err = dlmgr_DLValue_fm_putstring(ddvp, ddmp, "local-ip",
+			    buf, rbuf, dstr, sizeof (dstr));
+			if (err != 0)
+				goto out;
+		}
+
+		flow_addr2str(&f->ipv6_dst, &m->ipv6_dst, f->nw_dst, m->nw_dst,
+		    buf, rbuf, sizeof (buf), sizeof (rbuf));
+		if (strlen(buf) != 0) {
+			err = dlmgr_DLValue_fm_putstring(ddvp, ddmp,
+			    "remote-ip", buf, rbuf, dstr, sizeof (dstr));
+			if (err != 0)
+				goto out;
+		}
+
+		if (f->tcp_flags != 0 || m->tcp_flags != 0) {
+			err = dlmgr_DLValue_fm_putulong(ddvp, ddmp,
+			    "tcp-flags", (uint8_t)(ntohs(f->tcp_flags)),
+			    (uint8_t)(ntohs(m->tcp_flags)), dstr,
+			    sizeof (dstr));
+			if (err != 0)
+				goto out;
+		}
+
+		if (f->nw_proto != IPPROTO_ICMP &&
+		    f->nw_proto != IPPROTO_ICMPV6) {
+			if (f->tp_src != 0 || m->tp_src != 0) {
+				err = dlmgr_DLValue_fm_putulong(ddvp, ddmp,
+				    "local-port", ntohs(f->tp_src),
+				    ntohs(m->tp_src), dstr, sizeof (dstr));
+				if (err != 0)
+					goto out;
+			}
+
+			if (f->tp_dst != 0 || m->tp_dst != 0) {
+				err = dlmgr_DLValue_fm_putulong(ddvp, ddmp,
+				    "remote-port", ntohs(f->tp_dst),
+				    ntohs(m->tp_dst), dstr, sizeof (dstr));
+				if (err != 0)
+					goto out;
+			}
+		} else {
+			if (f->tp_src != 0 || m->tp_src != 0) {
+				err = dlmgr_DLValue_fm_putulong(ddvp, ddmp,
+				    "icmp-type", ntohs(f->tp_src),
+				    ntohs(m->tp_src), dstr, sizeof (dstr));
+				if (err != 0)
+					goto out;
+			}
+			if (f->tp_dst != 0 || m->tp_dst != 0) {
+				err = dlmgr_DLValue_fm_putulong(ddvp, ddmp,
+				    "icmp-code", ntohs(f->tp_dst),
+				    ntohs(m->tp_dst), dstr, sizeof (dstr));
+				if (err != 0)
+					goto out;
+			}
+		}
+	}
+
+
+	flow_mac2str(f->dl_src, m->dl_src, buf, rbuf, sizeof (buf),
+	    sizeof (rbuf));
+	if (strlen(buf) != 0) {
+		err = dlmgr_DLValue_fm_putstring(ddvp, ddmp, "src-mac", buf,
+		    rbuf, dstr, sizeof (dstr));
+		if (err != 0)
+			goto out;
+	}
+
+	flow_mac2str(f->dl_dst, m->dl_dst, buf, rbuf, sizeof (buf),
+	    sizeof (rbuf));
+	if (strlen(buf) != 0) {
+		err = dlmgr_DLValue_fm_putstring(ddvp, ddmp, "dst-mac", buf,
+		    rbuf, dstr, sizeof (dstr));
+		if (err != 0)
+			goto out;
+	}
+
+	if (f->in_port.odp_port != ODPP_NONE) {
+		err = dlmgr_DLValue_putulong(ddvp, "srcport",
+		    f->in_port.odp_port, dstr, sizeof (dstr));
+		if (err != 0)
+			goto out;
+	}
+
+	if (f->vlan_tci != 0 || m->vlan_tci != 0) {
+		err = dlmgr_DLValue_fm_putulong(ddvp, ddmp,
+		    "vlan-tci", ntohs(f->vlan_tci),
+		    ntohs(m->vlan_tci), dstr, sizeof (dstr));
+		if (err != 0)
+			goto out;
+	}
+
+	if (f->nw_tos != 0 || m->nw_tos != 0) {
+		err = dlmgr_DLValue_fm_putulong(ddvp, ddmp,
+		    "dsfield", f->nw_tos, m->nw_tos, dstr, sizeof (dstr));
+		if (err != 0)
+			goto out;
+	}
+
+	if (f->nw_ttl != 0 || m->nw_ttl != 0) {
+		err = dlmgr_DLValue_fm_putulong(ddvp, ddmp,
+		    "ttl", f->nw_ttl, m->nw_ttl, dstr, sizeof (dstr));
+		if (err != 0)
+			goto out;
+	}
+
+out:
+	dpif_log(err, "solaris_flow_to_DLVal FLOWATTR: %s", dstr);
+	return (err);
+}
+
+static int
+solaris_outports_action_to_DLVal(dlmgr__rad_dict_string_DLValue_t *prop,
+    void *cookie, uint32_t ofports[], int nofports, uint32_t queueid,
+    char *dstr, size_t dstrlen)
+{
+	struct smap details;
+	const char *max_rate = NULL;
+	uint64_t maxbw;
+	char *endp = NULL;
+	char **pvals = NULL;
+	int  pvalcnt, err;
+
+	smap_init(&details);
+	if (queueid == UINT32_MAX || nofports != 1)
+		goto outport;
+
+	if ((err = dpif_solaris_get_priority_details(cookie, ofports[0],
+	    queueid, &details)) != 0) {
+		smap_destroy(&details);
+		goto outport;
+	}
+	/* min-rate and priority not currently used */
+	max_rate = smap_get(&details, "max-rate");
+	if (max_rate == NULL)
+		goto outport;
+
+	errno = 0;
+	maxbw = strtoull(max_rate, &endp, 10);
+	if (errno != 0 || *endp != '\0')
+		goto outport;
+
+	err = dlmgr_DLValue_putulong(prop, "max-bw", maxbw, dstr, dstrlen);
+
+outport:
+	err = flow_ofports2propstr(ofports, nofports, &pvals, &pvalcnt);
+	if (err == 0) {
+		err = dlmgr_DLValue_putstrings(prop, "outports", pvals,
+		    pvalcnt, dstr, dstrlen);
+	}
+	smap_destroy(&details);
+
+	dpif_log(err, "dpif_solaris_to_outport_action PROP: %s", dstr);
+	return (err);
+}
+
+static int
+solaris_setether_action_to_DLVal(dlmgr__rad_dict_string_DLValue_t *prop,
+    const struct ovs_key_ethernet *ek, char *ds, size_t dslen)
+{
+	char *sstr = NULL, *dstr = NULL;
+	char **pvals = NULL;
+	char buf[DLADM_STRSIZE];
+	int err;
+
+	sstr = _link_ntoa(ek->eth_src, NULL, ETHERADDRL, IFT_ETHER);
+	dstr = _link_ntoa(ek->eth_dst, NULL, ETHERADDRL, IFT_ETHER);
+	if (sstr != NULL && dstr != NULL) {
+		(void) snprintf(buf, sizeof (buf), "ether_src:%s,ether_dst:%s",
+		    sstr, dstr);
+	}
+	free(sstr);
+	free(dstr);
+	if (sstr == NULL || dstr == NULL) {
+		err = ENOMEM;
+		goto out;
+	}
+	pvals = calloc(1, sizeof (char *));
+	if (pvals == NULL) {
+		err = ENOMEM;
+		goto out;
+	}
+	pvals[0] = strdup(buf);
+	if (pvals[0] == NULL) {
+		err = ENOMEM;
+		free(pvals);
+		goto out;
+	}
+
+	/* TODO(gmoodalb): this should be two entries. fix later */
+	err = dlmgr_DLValue_putstrings(prop, "set-ether", pvals, 1, ds,
+	    dslen);
+
+out:
+	dpif_log(err, "solaris_setether_action_to_DLVal PROP: %s", ds);
+	return (err);
+}
+
+static int
+solaris_setipv4_action_to_DLVal(dlmgr__rad_dict_string_DLValue_t *prop,
+    const struct ovs_key_ipv4 *ipv4, char *dstr, size_t dstrlen)
+{
+	struct in_addr ipaddr;
+	char *cp;
+	char **pvals = NULL;
+	char buf[DLADM_STRSIZE];
+	int err;
+
+	ipaddr.s_addr = ipv4->ipv4_src;
+	cp = inet_ntoa(ipaddr);
+	(void) snprintf(buf, sizeof (buf), "src:%s,", cp);
+	ipaddr.s_addr = ipv4->ipv4_dst;
+	cp = inet_ntoa(ipaddr);
+	(void) snprintf(buf, sizeof (buf),
+	    "%sdst:%s,protocol:%s,tos:0x%x,hoplimit:%d",
+	    buf, cp, solaris_proto2str(ipv4->ipv4_proto), ipv4->ipv4_tos,
+	    ipv4->ipv4_ttl);
+
+	pvals = calloc(1, sizeof (char *));
+	if (pvals == NULL) {
+		err = ENOMEM;
+		goto out;
+	}
+	pvals[0] = strdup(buf);
+	if (pvals[0] == NULL) {
+		err = ENOMEM;
+		free(pvals);
+		goto out;
+	}
+
+	err = dlmgr_DLValue_putstrings(prop, "set-ipv4", pvals, 1, dstr,
+	    dstrlen);
+
+out:
+	dpif_log(err, "solaris_setipv4_action_to_DLVal PROP: %s", dstr);
+	return (err);
+}
+
+static int
+solaris_setipv6_action_to_DLVal(dlmgr__rad_dict_string_DLValue_t *prop,
+    const struct ovs_key_ipv6 *ipv6, char *dstr, size_t dstrlen)
+{
+	char abuf[INET6_ADDRSTRLEN];
+	char **pvals = NULL;
+	char buf[DLADM_STRSIZE];
+	int err;
+
+	(void) inet_ntop(AF_INET6, ipv6->ipv6_src, abuf, INET6_ADDRSTRLEN);
+	(void) snprintf(buf, sizeof (buf), "src:%s,", abuf);
+	(void) inet_ntop(AF_INET6, ipv6->ipv6_dst, abuf, INET6_ADDRSTRLEN);
+	(void) snprintf(buf, sizeof (buf),
+	    "%sdst:%s,label:0x%x,protocol:%s,tos:0x%x,hoplimit:%d", buf, abuf,
+	    ipv6->ipv6_label, solaris_proto2str(ipv6->ipv6_proto),
+	    ipv6->ipv6_tclass, ipv6->ipv6_hlimit);
+
+	pvals = calloc(1, sizeof (char *));
+	if (pvals == NULL) {
+		err = ENOMEM;
+		goto out;
+	}
+
+	pvals[0] = strdup(buf);
+	if (pvals[0] == NULL) {
+		err = ENOMEM;
+		free(pvals);
+		goto out;
+	}
+
+	err = dlmgr_DLValue_putstrings(prop, "set-ipv6", pvals, 1, dstr,
+	    dstrlen);
+
+out:
+	dpif_log(err, "solaris_setipv6_action_to_DLVal PROP: %s", dstr);
+	return (err);
+}
+
+static int
+solaris_settransport_action_to_DLVal(dlmgr__rad_dict_string_DLValue_t *prop,
+    const char *key, uint16_t src, uint16_t dst, char *dstr, size_t dstrlen)
+{
+	char **pvals = NULL;
+	char buf[DLADM_STRSIZE];
+	int err;
+
+	(void) snprintf(buf, sizeof (buf), "sport:%d,dport:%d", src, dst);
+
+	pvals = calloc(1, sizeof (char *));
+	if (pvals == NULL) {
+		err = ENOMEM;
+		goto out;
+	}
+	pvals[0] = strdup(buf);
+	if (pvals[0] == NULL) {
+		err = ENOMEM;
+		free(pvals);
+		goto out;
+	}
+
+	err = dlmgr_DLValue_putstrings(prop, key, pvals, 1, dstr, dstrlen);
+
+out:
+	dpif_log(err, "solaris_settransport_action_to_DLVal PROP: %s %s", key,
+	    dstr);
+	return (err);
+}
+
+static int
+solaris_settnl_action_to_DLVal(dlmgr__rad_dict_string_DLValue_t *prop,
+    const struct flow_tnl *tnl, char *dstr, size_t dstrlen)
+{
+	struct in_addr ipaddr;
+	char *cp;
+	char **pvals = NULL;
+	char buf[DLADM_STRSIZE];
+	int err;
+
+	ipaddr.s_addr = tnl->ip_src;
+	cp = inet_ntoa(ipaddr);
+	(void) snprintf(buf, sizeof (buf), "src:%s,", cp);
+	ipaddr.s_addr = tnl->ip_dst;
+	cp = inet_ntoa(ipaddr);
+	(void) snprintf(buf, sizeof (buf),
+	    "%sdst:%s,tun_id:0x%"PRIx64",tos:0x%x,hoplimit:%d",
+	    buf, cp, ntohll(tnl->tun_id), tnl->ip_tos, tnl->ip_ttl);
+
+	pvals = calloc(1, sizeof (char *));
+	if (pvals == NULL) {
+		err = ENOMEM;
+		goto out;
+	}
+	pvals[0] = strdup(buf);
+	if (pvals[0] == NULL) {
+		err = ENOMEM;
+		free(pvals);
+		goto out;
+	}
+
+	err = dlmgr_DLValue_putstrings(prop, "set-tunnel", pvals, 1, dstr,
+	    dstrlen);
+
+out:
+	dpif_log(err, "solaris_setipv6_action_to_DLVal PROP: %s", dstr);
+	return (err);
+}
+
+static int
+solaris_nlattr_to_DLVal(void *cookie,
+    const struct nlattr *actions_nlattr, size_t actions_len,
+    dlmgr__rad_dict_string_DLValue_t **propp)
+{
+	const struct nlattr *a;
+	unsigned int left;
+	dlmgr__rad_dict_string_DLValue_t *prop = *propp;
+	char buf[DLADM_STRSIZE];
+	char dstr[DLADM_STRSIZE];
+	char **pvals;
+	int err = 0, nofports = 0;
+	uint32_t ofports[MAC_OF_MAXPORT];
+	enum ovs_action_attr type = -1, lasttype;
+	uint_t queueid = UINT32_MAX;
+
+	dstr[0] = '\0';
+	err = dlmgr_DLValue_putboolean(prop, "temporary", B_TRUE, dstr,
+	    sizeof (dstr));
+	if (err != 0)
+		goto out;
+
+	/* if actions_len == 0, then the action is drop */
+	if (actions_len == 0) {
+		pvals = calloc(1, sizeof (char *));
+		if (pvals == NULL) {
+			err = ENOMEM;
+			goto out;
+		}
+		(void) snprintf(buf, sizeof (buf), "drop");
+		pvals[0] = strdup(buf);
+		if (pvals[0] == NULL) {
+			err = ENOMEM;
+			free(pvals);
+			goto out;
+		}
+		err = dlmgr_DLValue_putstrings(prop, "outports", pvals, 1,
+		    dstr, sizeof (dstr));
+		goto out;
+	}
+
+	NL_ATTR_FOR_EACH_UNSAFE(a, left, actions_nlattr, actions_len) {
+		lasttype = type;
+		type = nl_attr_type(a);
+		if ((type != OVS_ACTION_ATTR_OUTPUT) ||
+		    (lasttype != OVS_ACTION_ATTR_OUTPUT)) {
+			if (lasttype == OVS_ACTION_ATTR_OUTPUT) {
+				dpif_log(0, "solaris_nlattr_to_DLVal outports "
+				    "total %d ports", nofports);
+				err = solaris_outports_action_to_DLVal(prop,
+				    cookie, ofports, nofports, queueid,
+				    dstr, sizeof (dstr));
+				if (err != 0)
+					goto out;
+			}
+			nofports = 0;
+		}
+
+		switch ((enum ovs_action_attr) type) {
+		/* These only make sense in the context of a datapath. */
+		case OVS_ACTION_ATTR_OUTPUT:
+			if (nofports + 1 > MAC_OF_MAXPORT) {
+				err = ENOBUFS;
+				break;
+			}
+			dpif_log(0, "solaris_nlattr_to_DLVal %d ports: %u",
+			    nofports+1, nl_attr_get_u32(a));
+			ofports[nofports++] = u32_to_odp(nl_attr_get_u32(a));
+			break;
+		case OVS_ACTION_ATTR_USERSPACE: {
+			const struct nlattr *userdata;
+			size_t userdata_len;
+			union user_action_cookie cookie;
+
+			userdata = nl_attr_find_nested(a,
+			    OVS_USERSPACE_ATTR_USERDATA);
+			userdata_len = nl_attr_get_size(userdata);
+			memcpy(&cookie, nl_attr_get(userdata), userdata_len);
+			if (userdata_len < sizeof (cookie.type) ||
+			    userdata_len > sizeof (cookie)) {
+				err = EINVAL;
+				dpif_log(err,
+				    "unexpected action size %"PRIuSIZE,
+				    userdata_len);
+				break;
+			}
+			if (userdata_len != MAX(8, sizeof (cookie.slow_path)) ||
+			    cookie.type != USER_ACTION_COOKIE_SLOW_PATH) {
+				err = EOPNOTSUPP;
+				dpif_log(err,
+				    "userspace action size %"PRIuSIZE" "
+				    "type unsupported %d", userdata_len,
+				    cookie.type);
+				break;
+			}
+			if (cookie.slow_path.reason != SLOW_CONTROLLER)
+				break;
+
+			pvals = calloc(1, sizeof (char *));
+			if (pvals == NULL) {
+				err = ENOMEM;
+				break;
+			}
+			(void) snprintf(buf, sizeof (buf), "%u",
+			    PORT_PF_PACKET_UPLINK);
+			pvals[0] = strdup(buf);
+			if (pvals[0] == NULL) {
+				err = ENOMEM;
+				free(pvals);
+				break;
+			}
+			err = dlmgr_DLValue_putstrings(prop, "controller",
+			    pvals, 1, dstr, sizeof (dstr));
+			break;
+		}
+		case OVS_ACTION_ATTR_SET: {
+			const struct nlattr *aset = nl_attr_get(a);
+
+			switch (nl_attr_type(aset)) {
+			case OVS_KEY_ATTR_PRIORITY:
+				queueid = nl_attr_get_u32(aset);
+				break;
+
+			case OVS_KEY_ATTR_ETHERNET: {
+				const struct ovs_key_ethernet *ek;
+
+				ek = nl_attr_get_unspec(aset,
+				    sizeof (struct ovs_key_ethernet));
+				err = solaris_setether_action_to_DLVal(prop,
+				    ek, dstr, sizeof (dstr));
+				break;
+			}
+			case OVS_KEY_ATTR_IPV4: {
+				const struct ovs_key_ipv4 *eip4;
+
+				eip4 = nl_attr_get_unspec(aset,
+				    sizeof (struct ovs_key_ipv4));
+				err = solaris_setipv4_action_to_DLVal(prop,
+				    eip4, dstr, sizeof (dstr));
+				break;
+			}
+			case OVS_KEY_ATTR_IPV6: {
+				const struct ovs_key_ipv6 *eip6;
+
+				eip6 = nl_attr_get_unspec(aset,
+				    sizeof (struct ovs_key_ipv6));
+				err = solaris_setipv6_action_to_DLVal(prop,
+				    eip6, dstr, sizeof (dstr));
+				break;
+			}
+			case OVS_KEY_ATTR_TCP: {
+				const struct ovs_key_tcp *etcp;
+				etcp = nl_attr_get_unspec(aset,
+				    sizeof (struct ovs_key_tcp));
+				err = solaris_settransport_action_to_DLVal(
+				    prop, "set-tcp", etcp->tcp_src,
+				    etcp->tcp_dst, dstr, sizeof (dstr));
+				break;
+			}
+			case OVS_KEY_ATTR_UDP: {
+				const struct ovs_key_udp *eudp;
+				eudp = nl_attr_get_unspec(aset,
+				    sizeof (struct ovs_key_udp));
+				err = solaris_settransport_action_to_DLVal(
+				    prop, "set-udp", eudp->udp_src,
+				    eudp->udp_dst, dstr, sizeof (dstr));
+				break;
+			}
+			case OVS_KEY_ATTR_SCTP: {
+				const struct ovs_key_sctp *esctp;
+				esctp = nl_attr_get_unspec(aset,
+				    sizeof (struct ovs_key_sctp));
+				err = solaris_settransport_action_to_DLVal(
+				    prop, "set-sctp", esctp->sctp_src,
+				    esctp->sctp_dst, dstr, sizeof (dstr));
+				break;
+			}
+			case OVS_KEY_ATTR_TUNNEL: {
+				struct flow_tnl tnl;
+				enum odp_key_fitness fitness;
+
+				memset(&tnl, 0, sizeof (tnl));
+				fitness = odp_tun_key_from_attr(aset, &tnl);
+				ovs_assert(fitness != ODP_FIT_ERROR);
+				err = solaris_settnl_action_to_DLVal(
+				    prop, &tnl, dstr, sizeof (dstr));
+				break;
+			}
+			default:
+				err = EOPNOTSUPP;
+				dpif_log(err, "solaris_nlattr_to_DLVal set "
+				    "%d not supported",
+				    nl_attr_type(nl_attr_get(a)));
+			}
+			break;
+		}
+		case OVS_ACTION_ATTR_PUSH_VLAN: {
+			const struct ovs_action_push_vlan *vlan;
+
+			vlan = nl_attr_get_unspec(a,
+			    sizeof (struct ovs_action_push_vlan));
+
+			(void) snprintf(buf, sizeof (buf), "%u",
+			    ntohs(vlan->vlan_tci));
+			err = dlmgr_DLValue_putstring(prop, "vlan-tag", buf,
+			    dstr, sizeof (dstr));
+			break;
+		}
+		case OVS_ACTION_ATTR_POP_VLAN:
+			(void) snprintf(buf, sizeof (buf), "on");
+			err = dlmgr_DLValue_putstring(prop,
+			    "vlan-strip", buf, dstr, sizeof (dstr));
+			break;
+		case OVS_ACTION_ATTR_RECIRC:
+		case OVS_ACTION_ATTR_HASH:
+		case OVS_ACTION_ATTR_PUSH_MPLS:
+		case OVS_ACTION_ATTR_POP_MPLS:
+		case OVS_ACTION_ATTR_SAMPLE:
+			/* TBD */
+			err = EOPNOTSUPP;
+			dpif_log(err, "solaris_nlattr_to_DLVal type %d "
+			    "not supported", type);
+		case OVS_ACTION_ATTR_UNSPEC:
+		case __OVS_ACTION_ATTR_MAX:
+			OVS_NOT_REACHED();
+		}
+		if (err != 0)
+			goto out;
+	}
+	if (type == OVS_ACTION_ATTR_OUTPUT) {
+		dpif_log(0, "solaris_nlattr_to_DLVal outports total %d ports",
+		    nofports);
+		err = solaris_outports_action_to_DLVal(prop, cookie, ofports,
+		    nofports, queueid, dstr, sizeof (dstr));
+	}
+out:
+	dpif_log(err, "solaris_nlattr_to_DLVal %s", dstr);
+	return (err);
+}
+
+int
+solaris_add_flow(void *cookie, const char *linkname,
+    const char *flowname, struct flow *f, struct flow *m,
+    const struct nlattr *actions_nlattr, size_t actions_len)
+{
+	dlmgr_DLDict_t dff, dfm, *dffp, *dfmp;
+	dlmgr__rad_dict_string_DLValue_t *prop = NULL;
+	rc_instance_t *link = NULL, *flow = NULL;
+	dlmgr_DatalinkError_t *derrp = NULL;
+	int err = 0;
+	rc_err_t status;
+
+	bzero(&dff, sizeof (dff));
+	bzero(&dfm, sizeof (dfm));
+	dffp = &dff;
+	dfmp = &dfm;
+	status = dlmgr_Datalink__rad_lookup(rad_conn, B_TRUE, &link, 1,
+	    "name", linkname);
+	if (status != RCE_OK) {
+		err = ENODEV;
+		goto out;
+	}
+
+	dff.ddld_map = dlmgr__rad_dict_string_DLValue_create(link);
+	if (dff.ddld_map == NULL) {
+		err = ENOMEM;
+		goto out;
+	}
+
+	dfm.ddld_map = dlmgr__rad_dict_string_DLValue_create(link);
+	if (dfm.ddld_map == NULL) {
+		err = ENOMEM;
+		goto out;
+	}
+
+	prop = dlmgr__rad_dict_string_DLValue_create(link);
+	if (prop == NULL) {
+		err = ENOMEM;
+		goto out;
+	}
+
+	err = solaris_flow_to_DLVal(f, m, dff.ddld_map, dfm.ddld_map);
+	if (err != 0)
+		goto out;
+
+	err = solaris_nlattr_to_DLVal(cookie, actions_nlattr,
+	    actions_len, &prop);
+	if (err != 0)
+		goto out;
+
+	status = dlmgr_Datalink_addFlow(link, flowname, &dffp, 1, &dfmp, 1,
+	    prop, &flow, &derrp);
+	if (status != RCE_OK) {
+		err = -1;
+		if (status == RCE_SERVER_OBJECT) {
+			err = derrp->dde_err;
+			dpif_log(err,
+			    "failed Datalink_addFlow(%s, %s): %s",
+			    flowname, linkname, derrp->dde_errmsg);
+		}
+	}
+
+	rc_instance_rele(flow);
+	dlmgr_DatalinkError_free(derrp);
+
+out:
+	dlmgr__rad_dict_string_DLValue_free(dfm.ddld_map);
+	dlmgr__rad_dict_string_DLValue_free(dff.ddld_map);
+	dlmgr__rad_dict_string_DLValue_free(prop);
+	rc_instance_rele(link);
+	return (err);
+}
+
+int
+solaris_modify_flow(void *cookie, const char *flowname,
+    const struct nlattr *actions_nlattr, size_t actions_len)
+{
+	rc_instance_t *flow = NULL;
+	dlmgr__rad_dict_string_DLValue_t *prop = NULL;
+	dlmgr_DatalinkError_t *derrp = NULL;
+	rc_err_t status;
+	int err;
+
+	status = dlmgr_Flow__rad_lookup(rad_conn, B_TRUE, &flow, 1,
+	    "name", flowname);
+	if (status != RCE_OK) {
+		err = ENODEV;
+		goto out;
+	}
+
+	prop = dlmgr__rad_dict_string_DLValue_create(flow);
+	if (prop == NULL) {
+		err = ENOMEM;
+		goto out;
+	}
+
+	err = solaris_nlattr_to_DLVal(cookie, actions_nlattr,
+	    actions_len, &prop);
+	if (err != 0)
+		goto out;
+
+	status = dlmgr_Flow_setProperties(flow, prop, &derrp);
+	if (status != RCE_OK) {
+		err = -1;
+		if (status == RCE_SERVER_OBJECT) {
+			err = derrp->dde_err;
+			dpif_log(err,
+			    "failed Flow_setProperties(%s): %s",
+			    flowname, derrp->dde_errmsg);
+		}
+	}
+
+	dlmgr_DatalinkError_free(derrp);
+
+out:
+	dlmgr__rad_dict_string_DLValue_free(prop);
+	rc_instance_rele(flow);
+	return (err);
+}
+
+int
+solaris_remove_flow(const char *linkname, const char *flowname)
+{
+	dlmgr__rad_dict_string_DLValue_t *prop = NULL;
+	dlmgr_DatalinkError_t		*derrp = NULL;
+	dlmgr_DLValue_t			*old_val = NULL;
+	dlmgr_DLValue_t			val;
+	rc_instance_t			*link = NULL;
+	rc_err_t			status;
+	int				error = 0;
+
+	status = dlmgr_Datalink__rad_lookup(rad_conn, B_TRUE, &link, 1,
+	    "name", linkname);
+	if (status != RCE_OK) {
+		error = ENODEV;
+		goto out;
+	}
+
+	prop = dlmgr__rad_dict_string_DLValue_create(link);
+	if (prop == NULL) {
+		error = EINVAL;
+		goto out;
+	}
+
+	bzero(&val, sizeof (val));
+	val.ddlv_type = DDLVT_BOOLEAN;
+	val.ddlv_bval = &b_true;
+	status = dlmgr__rad_dict_string_DLValue_put(prop, "temporary",
+	    &val, &old_val);
+	if (status != RCE_OK) {
+		error = EINVAL;
+		goto out;
+	}
+	dlmgr_DLValue_free(old_val);
+
+	status = dlmgr_Datalink_removeFlow(link, flowname, prop, &derrp);
+	if (status != RCE_OK) {
+		error = -1;
+		if (status == RCE_SERVER_OBJECT) {
+			error = derrp->dde_err;
+			dpif_log(error, "failed Datalink_removeFlow(%s): %s",
+			    flowname, derrp->dde_errmsg);
+		}
+	}
+	dlmgr_DatalinkError_free(derrp);
+out:
+	dlmgr__rad_dict_string_DLValue_free(prop);
+	rc_instance_rele(link);
+	return (error);
+}
+
+static rc_err_t
+solaris_flowinfo2flowmap(const char *key, dlmgr_DLValue_t *val, void *arg)
+{
+	struct flow *f = arg;
+	in6_addr_t fa;
+	struct in_addr v4;
+	int af, err = 0;
+
+	if (strcmp(key, "dst-mac") == 0) {
+		err = flow_str2mac(val->ddlv_sval, f->dl_dst, 6);
+	} else if (strcmp(key, "src-mac") == 0) {
+		err = flow_str2mac(val->ddlv_sval, f->dl_src, 6);
+	} else if (strcmp(key, "local-ip") == 0 ||
+	    strcmp(key, "remote-ip") == 0 || strcmp(key, "arp-sip") == 0 ||
+	    strcmp(key, "arp-tip") == 0) {
+		err = flow_str2addr(val->ddlv_sval, &fa, &af);
+		if (err != 0)
+			goto out;
+		if (af == AF_INET) {
+			IN6_V4MAPPED_TO_INADDR(&fa, &v4);
+			if (strcmp(key, "local-ip") == 0 ||
+			    strcmp(key, "arp-sip") == 0) {
+				f->nw_src = v4.s_addr;
+			} else {
+				f->nw_dst = v4.s_addr;
+			}
+		} else {
+			if (strcmp(key, "local-ip") == 0) {
+				bcopy(&fa, &f->ipv6_src, sizeof (f->ipv6_src));
+			} else if (strcmp(key, "remote-ip") == 0) {
+				bcopy(&fa, &f->ipv6_dst, sizeof (f->ipv6_dst));
+			} else {
+				err = EINVAL;
+			}
+		}
+	} else if (strcmp(key, "transport") == 0) {
+		f->nw_proto = (uint8_t)*val->ddlv_ulval;
+	} else if (strcmp(key, "local-port") == 0) {
+		f->tp_src = htons((uint16_t)*val->ddlv_ulval);
+	} else if (strcmp(key, "remote-port") == 0) {
+		f->tp_dst = htons((uint16_t)*val->ddlv_ulval);
+	} else if (strcmp(key, "dsfield") == 0) {
+		f->nw_tos = (uint8_t)*val->ddlv_ulval;
+	} else if (strcmp(key, "srcport") == 0) {
+		f->in_port.odp_port = (odp_port_t)*val->ddlv_ulval;
+	} else if (strcmp(key, "arp-target") == 0) {
+		err = flow_str2mac(val->ddlv_sval, f->arp_tha, 6);
+	} else if (strcmp(key, "arp-sender") == 0) {
+		err = flow_str2mac(val->ddlv_sval, f->arp_sha, 6);
+	} else if (strcmp(key, "arp-op") == 0) {
+		f->nw_proto = (uint8_t)*val->ddlv_ulval;
+	} else if (strcmp(key, "sap") == 0) {
+		f->dl_type = htons((uint16_t)*val->ddlv_ulval);
+	} else if (strcmp(key, "ttl") == 0) {
+		f->nw_ttl = (uint8_t)*val->ddlv_ulval;
+	} else if (strcmp(key, "vlan-tci") == 0) {
+		f->vlan_tci = htons((uint16_t)*val->ddlv_ulval);
+	} else if (strcmp(key, "icmp-type") == 0) {
+		f->tp_src = htons((uint16_t)*val->ddlv_ulval);
+	} else if (strcmp(key, "icmp-code") == 0) {
+		f->tp_dst = htons((uint16_t)*val->ddlv_ulval);
+	} else if (strcmp(key, "tcp-flags") == 0) {
+		f->tcp_flags = htons((uint16_t)*val->ddlv_ulval);
+	}
+
+out:
+	dlmgr_DLValue_free(val);
+	return (err == 0 ? RCE_OK : -1);
+}
+
+static int
+solaris_flowinfo2flow(dlmgr__rad_dict_string_DLValue_t *flowinfo,
+    struct flow *f, struct flow *m)
+{
+	dlmgr__rad_dict_string_DLValue_t *fdict, *mdict;
+	dlmgr_DLValue_t  *flist = NULL, *mlist = NULL;
+	rc_err_t status;
+
+	bzero(f, sizeof (*f));
+	bzero(m, sizeof (*m));
+
+	status = dlmgr__rad_dict_string_DLValue_get(flowinfo, "filters",
+	    &flist);
+	if (status != RCE_OK)
+		return (EINVAL);
+
+	fdict = flist->ddlv_dlist[0]->ddld_map;
+
+	status = dlmgr__rad_dict_string_DLValue_map(fdict,
+	    solaris_flowinfo2flowmap, f);
+	dlmgr_DLValue_free(flist);
+	if (status != RCE_OK)
+		return (EINVAL);
+
+	status = dlmgr__rad_dict_string_DLValue_get(flowinfo, "masks", &mlist);
+	if (status != RCE_OK)
+		return (EINVAL);
+
+	mdict = mlist->ddlv_dlist[0]->ddld_map;
+
+	status = dlmgr__rad_dict_string_DLValue_map(mdict,
+	    solaris_flowinfo2flowmap, m);
+	dlmgr_DLValue_free(mlist);
+	if (status != RCE_OK)
+		return (EINVAL);
+	return (0);
+}
+
+int
+solaris_get_flowattr(const char *flowname, struct flow *f, struct flow *m)
+{
+	dlmgr__rad_dict_string_DLValue_t *flowinfo = NULL;
+	dlmgr_DatalinkError_t	*derrp = NULL;
+	rc_instance_t		*flow = NULL;
+	rc_err_t		status;
+	int			err = 0;
+
+	status = dlmgr_Flow__rad_lookup(rad_conn, B_TRUE, &flow, 1,
+	    "name", flowname);
+	if (status != RCE_OK) {
+		return (ENODEV);
+	}
+
+	status = dlmgr_Flow_getInfo(flow, NULL, 0, &flowinfo, &derrp);
+	if (status != RCE_OK) {
+		err = -1;
+		if (status == RCE_SERVER_OBJECT) {
+			err = derrp->dde_err;
+			/*
+			 * XXX For now, log DDLSTATUS_NOT_FOUND as debug until
+			 * we can determine how to handle the RAD caching
+			 * issue.
+			 */
+			dpif_log(err == DDLSTATUS_NOT_FOUND ? 0 : err,
+			    "failed Flow_getInfo(%s): %s",
+			    flowname, derrp->dde_errmsg);
+		}
+	}
+
+	dlmgr_DatalinkError_free(derrp);
+	rc_instance_rele(flow);
+
+	if (err == 0) {
+		err = solaris_flowinfo2flow(flowinfo, f, m);
+		dlmgr__rad_dict_string_DLValue_free(flowinfo);
+	}
+
+	return (err);
+}
+
+static int
+flow_propval2action_drop(char **propvals, int nval,
+    struct ofpbuf *action OVS_UNUSED)
+{
+	if (nval == 1 && strcmp(propvals[0], "drop") == 0)
+		return (0);
+
+	return (EINVAL);
+}
+
+static int
+flow_propval2action_outports(char **propvals, int nval, struct ofpbuf *action)
+{
+	mac_propval_range_t *pv_range = NULL;
+	uint32_t ofports[MAC_OF_MAXPORT], i, nofports = MAC_OF_MAXPORT;
+	dladm_status_t status;
+	int err = 0;
+
+	status = dladm_strs2range(propvals, nval, MAC_PROPVAL_UINT32,
+	    &pv_range);
+	if (status != DLADM_STATUS_OK)
+		return (solaris_dladm_status2error(status));
+
+	/* Convert mac_propval_range to a single ofports list */
+	status = dladm_range2list(pv_range, ofports, &nofports);
+	if (status != DLADM_STATUS_OK) {
+		err = solaris_dladm_status2error(status);
+		goto out;
+	}
+	for (i = 0; i < nofports; i++)
+		nl_msg_put_u32(action, OVS_ACTION_ATTR_OUTPUT, ofports[i]);
+
+out:
+	free(pv_range);
+	return (err);
+}
+
+static int
+flow_propval2action_setpri(char **propvals OVS_UNUSED, int nval OVS_UNUSED,
+    struct ofpbuf *action OVS_UNUSED)
+{
+	/* TBD */
+	return (0);
+}
+
+void
+slowpath_to_actions(enum slow_path_reason reason, struct ofpbuf *buf)
+{
+	union user_action_cookie cookie;
+
+	cookie.type = USER_ACTION_COOKIE_SLOW_PATH;
+	cookie.slow_path.unused = 0;
+	cookie.slow_path.reason = reason;
+
+	odp_put_userspace_action(0, &cookie, sizeof (cookie.slow_path), buf);
+}
+
+static int
+flow_propval2action_controller(char **propvals OVS_UNUSED, int nval OVS_UNUSED,
+    struct ofpbuf *action)
+{
+	slowpath_to_actions(SLOW_CONTROLLER, action);
+	return (0);
+}
+
+static int
+flow_propval2action_pushvlan(char **propvals, int nval OVS_UNUSED,
+    struct ofpbuf *action)
+{
+	char *endp = NULL;
+	int64_t n;
+	struct ovs_action_push_vlan push;
+	int tpid = ETH_TYPE_VLAN;
+
+	errno = 0;
+	n = strtoull(propvals[0], &endp, 10);
+	if ((errno != 0) || *endp != '\0' || n > 0xffff)
+		return (EINVAL);
+
+	push.vlan_tpid = htons(tpid);
+	push.vlan_tci = htons((uint16_t)n);
+	nl_msg_put_unspec(action, OVS_ACTION_ATTR_PUSH_VLAN,
+	    &push, sizeof (push));
+	return (0);
+}
+
+static int
+flow_propval2action_popvlan(char **propvals OVS_UNUSED, int nval OVS_UNUSED,
+    struct ofpbuf *action)
+{
+	nl_msg_put_flag(action, OVS_ACTION_ATTR_POP_VLAN);
+	return (0);
+}
+
+static int
+flow_propval2action_setether(char **propvals, int nval, struct ofpbuf *action)
+{
+	char pval[DLADM_PROP_VAL_MAX];
+	struct ovs_key_ethernet eth_key;
+	uchar_t *etheraddr;
+	int etheraddrlen;
+	boolean_t src_set, dst_set;
+	char *sep;
+	size_t start_ofs;
+	uint_t i;
+	int err = 0;
+
+	/*
+	 * The property value is in the format of "ether_src:xxx"
+	 * "ether_dst:xxx"
+	 */
+	bzero(&eth_key, sizeof (eth_key));
+	src_set = dst_set = B_FALSE;
+	for (i = 0; i < nval; i++) {
+		bcopy(propvals[i], pval, strlen(propvals[i]) + 1);
+		if ((sep = strchr(pval, ':')) == NULL) {
+			err = EINVAL;
+			goto out;
+		}
+		*sep = '\0';
+		sep++;
+		if ((etheraddr = _link_aton(sep, &etheraddrlen)) == NULL) {
+			err = (etheraddrlen == -1) ? EINVAL : ENOMEM;
+			goto out;
+		}
+		/* Only ethernet address is supported */
+		if (etheraddrlen != ETHERADDRL) {
+			err = EINVAL;
+			free(etheraddr);
+			goto out;
+		}
+		if (strcmp(pval, "ether_src") == 0) {
+			if (src_set) {
+				err = EINVAL;
+			} else {
+				bcopy(etheraddr, &eth_key.eth_src, ETHERADDRL);
+				src_set = _B_TRUE;
+			}
+		} else if (strcmp(pval, "ether_dst") == 0) {
+			if (dst_set) {
+				err = EINVAL;
+			} else {
+				bcopy(etheraddr, &eth_key.eth_dst, ETHERADDRL);
+				dst_set = _B_TRUE;
+			}
+		} else {
+			err = EINVAL;
+		}
+		free(etheraddr);
+		if (err != 0)
+			goto out;
+	}
+	if (!src_set || !dst_set) {
+		err = EINVAL;
+		goto out;
+	}
+
+	start_ofs = nl_msg_start_nested(action, OVS_ACTION_ATTR_SET);
+	nl_msg_put_unspec(action, OVS_KEY_ATTR_ETHERNET, &eth_key,
+	    sizeof (eth_key));
+	nl_msg_end_nested(action, start_ofs);
+out:
+	return (err);
+}
+
+static int
+flow_propval2action_setipv4(char **propvals, int nval, struct ofpbuf *action)
+{
+	struct ovs_key_ipv4 ipv4;
+	char pval[DLADM_PROP_VAL_MAX];
+	char *sep, *endp;
+	uint_t i, value;
+	uint8_t protocol;
+	size_t start_ofs;
+	int err = EINVAL;
+	boolean_t tos_set = B_FALSE;
+
+	/*
+	 * The property value is in the format of "src:xxx" "dst:xxx"
+	 * "protocol:xxx" "tos:xxx" "hoplimit:xxx"
+	 */
+	bzero(&ipv4, sizeof (ipv4));
+	for (i = 0; i < nval; i++) {
+		bcopy(propvals[i], pval, strlen(propvals[i]) + 1);
+		if ((sep = strchr(pval, ':')) == NULL) {
+			err = EINVAL;
+			goto out;
+		}
+		*sep = '\0';
+		sep++;
+		if (strcmp(pval, "src") == 0) {
+			if (ipv4.ipv4_src != 0)
+				goto out;
+			if (inet_pton(AF_INET, sep, &ipv4.ipv4_src) != 1)
+				goto out;
+		} else if (strcmp(pval, "dst") == 0) {
+			if (ipv4.ipv4_dst != 0)
+				goto out;
+			if (inet_pton(AF_INET, sep, &ipv4.ipv4_dst) != 1)
+				goto out;
+		} else if (strcmp(pval, "protocol") == 0) {
+			if (ipv4.ipv4_proto != 0)
+				goto out;
+			protocol = solaris_str2proto(sep);
+			if (protocol == 0)
+				goto out;
+			ipv4.ipv4_proto = protocol;
+		} else if (strcmp(pval, "tos") == 0) {
+			if (tos_set)
+				goto out;
+			errno = 0;
+			endp = NULL;
+			value = strtoul(sep, &endp, 16);
+			if (errno != 0 || value > 0xff || *endp != '\0')
+				goto out;
+			tos_set = B_TRUE;
+			ipv4.ipv4_tos = value;
+		} else if (strcmp(pval, "hoplimit") == 0) {
+			if (ipv4.ipv4_ttl != 0)
+				goto out;
+			errno = 0;
+			endp = NULL;
+			value = strtoul(sep, &endp, 10);
+			if (errno != 0 || value == 0 || value > 0xff ||
+			    *endp != '\0')
+				goto out;
+			ipv4.ipv4_ttl = value;
+		}
+	}
+	start_ofs = nl_msg_start_nested(action, OVS_ACTION_ATTR_SET);
+	nl_msg_put_unspec(action, OVS_KEY_ATTR_IPV4, &ipv4, sizeof (ipv4));
+	nl_msg_end_nested(action, start_ofs);
+	err = 0;
+out:
+	return (err);
+}
+
+static int
+flow_propval2action_setipv6(char **propvals, int nval, struct ofpbuf *action)
+{
+	struct ovs_key_ipv6 ipv6;
+	char pval[DLADM_PROP_VAL_MAX];
+	char *sep, *endp;
+	uint_t i, value;
+	uint8_t protocol;
+	struct in6_addr in6;
+	size_t start_ofs;
+	int err = EINVAL;
+	boolean_t tos_set = B_FALSE;
+
+	/*
+	 * The property value is in the format of "src:xxx" "dst:xxx"
+	 * "label:0xxxxx" "protocol:xxx" "tos:xxx" "hoplimit:xxx"
+	 */
+	bzero(&ipv6, sizeof (ipv6));
+	for (i = 0; i < nval; i++) {
+		bcopy(propvals[i], pval, strlen(propvals[i]) + 1);
+		if ((sep = strchr(pval, ':')) == NULL)
+			goto out;
+		*sep = '\0';
+		sep++;
+		if (strcmp(pval, "src") == 0) {
+			bcopy(&ipv6.ipv6_src, &in6.s6_addr,
+			    sizeof (struct in6_addr));
+			if (!IN6_IS_ADDR_UNSPECIFIED(&in6))
+				goto out;
+			if (inet_pton(AF_INET6, sep, ipv6.ipv6_src) != 1)
+				goto out;
+		} else if (strcmp(pval, "dst") == 0) {
+			bcopy(&ipv6.ipv6_dst, &in6.s6_addr,
+			    sizeof (struct in6_addr));
+			if (!IN6_IS_ADDR_UNSPECIFIED(&in6))
+				goto out;
+			if (inet_pton(AF_INET6, sep, ipv6.ipv6_dst) != 1)
+				goto out;
+		} else if (strcmp(pval, "protocol") == 0) {
+			if (ipv6.ipv6_proto != 0)
+				goto out;
+			protocol = solaris_str2proto(sep);
+			if (protocol == 0)
+				goto out;
+			ipv6.ipv6_proto = protocol;
+		} else if (strcmp(pval, "tos") == 0) {
+			if (tos_set)
+				goto out;
+			errno = 0;
+			endp = NULL;
+			value = strtoul(sep, &endp, 16);
+			if (errno != 0 || value > 0xff || *endp != '\0')
+				goto out;
+			tos_set = B_TRUE;
+			ipv6.ipv6_tclass = value;
+		} else if (strcmp(pval, "ttl") == 0) {
+			if (ipv6.ipv6_hlimit != 0)
+				goto out;
+			errno = 0;
+			endp = NULL;
+			value = strtoul(sep, &endp, 10);
+			if (errno != 0 || value == 0 || value > 0xff ||
+			    *endp != '\0') {
+				goto out;
+			}
+			ipv6.ipv6_hlimit = value;
+		} else if (strcmp(pval, "label") == 0) {
+			if (ipv6.ipv6_label != 0)
+				goto out;
+			errno = 0;
+			endp = NULL;
+			value = strtoul(sep, &endp, 16);
+			if (errno != 0 || value == 0 || value > 0xff ||
+			    *endp != '\0') {
+				goto out;
+			}
+			ipv6.ipv6_label = value;
+		}
+	}
+	start_ofs = nl_msg_start_nested(action, OVS_ACTION_ATTR_SET);
+	nl_msg_put_unspec(action, OVS_KEY_ATTR_IPV6, &ipv6, sizeof (ipv6));
+	nl_msg_end_nested(action, start_ofs);
+	err = 0;
+out:
+	return (err);
+}
+
+static int
+flow_propval2action_settransport(char **propvals, int nval,
+    uint16_t *sportp, uint16_t *dportp)
+{
+	char pval[DLADM_PROP_VAL_MAX];
+	char *sep, *endp;
+	uint_t i, value;
+	int err = EINVAL;
+	uint16_t sport = 0, dport = 0;
+
+	/* The property value is in the format of "sport:xxx" "dport:xxx" */
+	for (i = 0; i < nval; i++) {
+		bcopy(propvals, pval, strlen(propvals[i]) + 1);
+		if ((sep = strchr(pval, ':')) == NULL)
+			goto out;
+		*sep = '\0';
+		sep++;
+		if (strcmp(pval, "sport") == 0) {
+			if (sport != 0)
+				goto out;
+			errno = 0;
+			endp = NULL;
+			value = strtoul(sep, &endp, 10);
+			if (errno != 0 || value == 0 || value > 0xffff ||
+			    *endp != '\0') {
+				goto out;
+			}
+			sport = value;
+		} else if (strcmp(pval, "dport") == 0) {
+			if (dport != 0)
+				return (DLADM_STATUS_DUPLICATE_ARG);
+			errno = 0;
+			endp = NULL;
+			value = strtoul(sep, &endp, 10);
+			if (errno != 0 || value == 0 || value > 0xffff ||
+			    *endp != '\0') {
+				goto out;
+			}
+			dport = value;
+		}
+	}
+	err = 0;
+out:
+	*sportp = sport;
+	*dportp = dport;
+	return (err);
+}
+
+static int
+flow_propval2action_settcp(char **propvals, int nval, struct ofpbuf *action)
+{
+	struct ovs_key_tcp tcp;
+	size_t start_ofs;
+	int err;
+
+	err = flow_propval2action_settransport(propvals, nval, &tcp.tcp_src,
+	    &tcp.tcp_dst);
+	if (err != 0)
+		return (err);
+
+	start_ofs = nl_msg_start_nested(action, OVS_ACTION_ATTR_SET);
+	nl_msg_put_unspec(action, OVS_KEY_ATTR_TCP, &tcp, sizeof (tcp));
+	nl_msg_end_nested(action, start_ofs);
+	return (0);
+}
+
+static int
+flow_propval2action_setudp(char **propvals, int nval, struct ofpbuf *action)
+{
+	struct ovs_key_udp udp;
+	size_t start_ofs;
+	int err;
+
+	err = flow_propval2action_settransport(propvals, nval, &udp.udp_src,
+	    &udp.udp_dst);
+	if (err != 0)
+		return (err);
+
+	start_ofs = nl_msg_start_nested(action, OVS_ACTION_ATTR_SET);
+	nl_msg_put_unspec(action, OVS_KEY_ATTR_UDP, &udp, sizeof (udp));
+	nl_msg_end_nested(action, start_ofs);
+	return (0);
+}
+
+static int
+flow_propval2action_setsctp(char **propvals, int nval, struct ofpbuf *action)
+{
+	struct ovs_key_sctp sctp;
+	size_t start_ofs;
+	int err;
+
+	err = flow_propval2action_settransport(propvals, nval, &sctp.sctp_src,
+	    &sctp.sctp_dst);
+	if (err != 0)
+		return (err);
+
+	start_ofs = nl_msg_start_nested(action, OVS_ACTION_ATTR_SET);
+	nl_msg_put_unspec(action, OVS_KEY_ATTR_SCTP, &sctp, sizeof (sctp));
+	nl_msg_end_nested(action, start_ofs);
+	return (0);
+}
+
+static int
+set_addr(char *pval, uint32_t *addrp)
+{
+	struct addrinfo		hints;
+	struct addrinfo		*ai;
+	struct addrinfo		*next_ai;
+	void			*ptr;
+
+	(void) memset(&hints, 0, sizeof (hints));
+	hints.ai_family = AF_UNSPEC;
+	if (getaddrinfo(pval, NULL, &hints, &ai) != 0)
+		return (EINVAL);
+
+	/* Check if hostname resolves to multiple addresses */
+	for (next_ai = ai->ai_next; next_ai != NULL;
+	    next_ai = next_ai->ai_next) {
+		if (next_ai->ai_addrlen != ai->ai_addrlen ||
+		    bcmp(next_ai->ai_addr, ai->ai_addr,
+		    ai->ai_addrlen) != 0) {
+			/* maps to more than one address */
+			freeaddrinfo(ai);
+			return (EINVAL);
+		}
+	}
+	if (ai->ai_family != AF_INET) {
+		freeaddrinfo(ai);
+		return (EINVAL);
+	}
+
+	ptr = ((uint8_t *)ai->ai_addr) +
+	    offsetof(struct sockaddr_in, sin_addr);
+	memcpy(addrp, ptr, sizeof (struct in_addr));
+	freeaddrinfo(ai);
+	return (0);
+}
+
+static int
+flow_propval2action_settnl(char **propvals, int nval, struct ofpbuf *action)
+{
+	char pval[DLADM_PROP_VAL_MAX];
+	char *sep, *endp;
+	uint_t i, value;
+	struct flow_tnl tnl;
+	size_t start_ofs;
+	boolean_t id_set, src_set, dst_set, tos_set, ttl_set;
+	int err = 0;
+
+	id_set = src_set = dst_set = tos_set = ttl_set = B_FALSE;
+
+	/*
+	 * The property value is in the format of "src:xxx" "dst:xxx"
+	 * "tun_id:0x%x" "tos:0x%x" "hoplimit:xxx"
+	 */
+	tnl.ip_tos = 0xff;
+	for (i = 0; i < nval; i++) {
+		bcopy(propvals[i], pval, strlen(propvals[i]) + 1);
+		if ((sep = strchr(pval, ':')) == NULL)
+			return (EINVAL);
+		*sep = '\0';
+		sep++;
+		if (strcmp(pval, "tun_id") == 0) {
+			if (id_set)
+				return (EINVAL);
+			errno = 0;
+			endp = NULL;
+			value = strtoull(sep, &endp, 16);
+			if (errno != 0 || *endp != '\0')
+				return (EINVAL);
+			tnl.tun_id = value;
+			id_set = B_TRUE;
+		} else if (strcmp(pval, "src") == 0) {
+			if (src_set)
+				return (EINVAL);
+			err = set_addr(sep, &tnl.ip_src);
+			if (err != 0)
+				return (err);
+			src_set = B_TRUE;
+		} else if (strcmp(pval, "dst") == 0) {
+			if (dst_set)
+				return (EINVAL);
+			err = set_addr(sep, &tnl.ip_dst);
+			if (err != 0)
+				return (err);
+			dst_set = B_TRUE;
+		} else if (strcmp(pval, "tos") == 0) {
+			if (tos_set)
+				return (EINVAL);
+			errno = 0;
+			endp = NULL;
+			value = strtoul(sep, &endp, 16);
+			if (errno != 0 || value > 0xff || *endp != '\0')
+				return (EINVAL);
+			tnl.ip_tos = value;
+			tos_set = B_TRUE;
+		} else if (strcmp(pval, "hoplimit") == 0) {
+			if (ttl_set)
+				return (EINVAL);
+			errno = 0;
+			endp = NULL;
+			value = strtoul(sep, &endp, 10);
+			if (errno != 0 || value == 0 || value > 0xff ||
+			    *endp != '\0') {
+				return (EINVAL);
+			}
+			tnl.ip_ttl = value;
+			ttl_set = B_TRUE;
+		}
+	}
+
+	start_ofs = nl_msg_start_nested(action, OVS_ACTION_ATTR_SET);
+	nl_msg_put_be64(action, OVS_TUNNEL_KEY_ATTR_ID, (uint64_t)tnl.tun_id);
+	nl_msg_put_be32(action, OVS_TUNNEL_KEY_ATTR_IPV4_SRC, tnl.ip_src);
+	nl_msg_put_be32(action, OVS_TUNNEL_KEY_ATTR_IPV4_DST, tnl.ip_dst);
+	nl_msg_put_u8(action, OVS_TUNNEL_KEY_ATTR_TOS, tnl.ip_tos);
+	nl_msg_put_u8(action, OVS_TUNNEL_KEY_ATTR_TTL, tnl.ip_ttl);
+	nl_msg_put_flag(action, OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT);
+	nl_msg_end_nested(action, start_ofs);
+	return (0);
+}
+
+static rc_err_t
+solaris_flowinfo2actionmap(const char *key, dlmgr_DLValue_t *val,
+    void *arg)
+{
+	struct ofpbuf *action = arg;
+	char **propvals, *buf = NULL;
+	int valcnt, err = 0, i;
+
+	buf = malloc((sizeof (char *) +
+	    DLADM_PROP_VAL_MAX) * DLADM_MAX_PROP_VALCNT);
+
+	propvals = (char **)(void *)buf;
+	for (i = 0; i < DLADM_MAX_PROP_VALCNT; i++) {
+		propvals[i] = buf + sizeof (char *) * DLADM_MAX_PROP_VALCNT +
+		    i * DLADM_PROP_VAL_MAX;
+	}
+
+	switch (val->ddlv_type) {
+	case DDLVT_STRING:
+		if (val->ddlv_sval == NULL)
+			goto out;
+		if (strlen(val->ddlv_sval) == 0)
+			goto out;
+		valcnt = 1;
+		memcpy(propvals[0], val->ddlv_sval, DLADM_PROP_VAL_MAX);
+		break;
+	case DDLVT_STRINGS:
+		if (val->ddlv_slist_count == 0)
+			goto out;
+		valcnt = val->ddlv_slist_count;
+		if (valcnt == 1 && strlen(val->ddlv_slist[0]) == 0)
+			goto out;
+		for (i = 0; i < val->ddlv_slist_count; i++) {
+			memcpy(propvals[i], val->ddlv_slist[i],
+			    DLADM_PROP_VAL_MAX);
+		}
+		break;
+	case DDLVT_ULONG:
+		if (val->ddlv_ulval == NULL || *val->ddlv_ulval == 0)
+			goto out;
+		valcnt = 1;
+		(void) snprintf(propvals[0], DLADM_PROP_VAL_MAX, "%llu",
+		    *val->ddlv_ulval);
+		break;
+	case DDLVT_BOOLEAN:
+	case DDLVT_BOOLEANS:
+	case DDLVT_LONG:
+	case DDLVT_LONGS:
+	case DDLVT_ULONGS:
+	case DDLVT_DICTIONARY:
+	case DDLVT_DICTIONARYS:
+	default:
+		goto out;
+	}
+	dpif_log(0, "solaris_flowinfo2actionmap %s:%d %s", key, valcnt,
+	    propvals[0]);
+
+	if (strcmp(key, "outports") == 0) {
+		err = flow_propval2action_drop(propvals, valcnt, action);
+		if (err == 0)
+			goto out;
+
+		err = flow_propval2action_outports(propvals, valcnt, action);
+	} else if (strcmp(key, "max-bw") == 0) {
+		err = flow_propval2action_setpri(propvals, valcnt, action);
+	} else if (strcmp(key, "controller") == 0) {
+		err = flow_propval2action_controller(propvals, valcnt, action);
+	} else if (strcmp(key, "vlan-tag") == 0) {
+		err = flow_propval2action_pushvlan(propvals, valcnt, action);
+	} else if (strcmp(key, "vlan-strip") == 0) {
+		err = flow_propval2action_popvlan(propvals, valcnt, action);
+	} else if (strcmp(key, "set-ether") == 0) {
+		err = flow_propval2action_setether(propvals, valcnt, action);
+	} else if (strcmp(key, "set-ipv4") == 0) {
+		err = flow_propval2action_setipv4(propvals, valcnt, action);
+	} else if (strcmp(key, "set-ipv6") == 0) {
+		err = flow_propval2action_setipv6(propvals, valcnt, action);
+	} else if (strcmp(key, "set-tcp") == 0) {
+		err = flow_propval2action_settcp(propvals, valcnt, action);
+	} else if (strcmp(key, "set-udp") == 0) {
+		err = flow_propval2action_setudp(propvals, valcnt, action);
+	} else if (strcmp(key, "set-sctp") == 0) {
+		err = flow_propval2action_setsctp(propvals, valcnt, action);
+	} else if (strcmp(key, "set-tunnel") == 0) {
+		err = flow_propval2action_settnl(propvals, valcnt, action);
+	}
+out:
+	dlmgr_DLValue_free(val);
+	free(buf);
+	return (err == 0 ? RCE_OK : -1);
+}
+
+static int
+solaris_flowinfo2action(dlmgr__rad_dict_string_DLValue_t *flowinfo,
+    struct ofpbuf *action)
+{
+	dlmgr__rad_dict_string_DLValue_t *fdict;
+	dlmgr_DLValue_t  *flist = NULL;
+	rc_err_t status;
+	int err = 0;
+
+	status = dlmgr__rad_dict_string_DLValue_get(flowinfo, "properties",
+	    &flist);
+	if (status != RCE_OK)
+		return (EINVAL);
+
+	fdict = flist->ddlv_dval;
+	status = dlmgr__rad_dict_string_DLValue_map(fdict,
+	    solaris_flowinfo2actionmap, action);
+	if (status != RCE_OK)
+		err = EINVAL;
+
+	dlmgr_DLValue_free(flist);
+	return (err);
+}
+
+int
+solaris_get_flowaction(const char *flowname, struct ofpbuf *action)
+{
+	dlmgr__rad_dict_string_DLValue_t *flowinfo = NULL;
+	dlmgr_DatalinkError_t	*derrp = NULL;
+	rc_instance_t		*flow = NULL;
+	rc_err_t		status;
+	int			err = 0;
+
+	status = dlmgr_Flow__rad_lookup(rad_conn, B_TRUE, &flow, 1,
+	    "name", flowname);
+	if (status != RCE_OK) {
+		return (ENODEV);
+	}
+
+	status = dlmgr_Flow_getInfo(flow, NULL, 0, &flowinfo, &derrp);
+	if (status != RCE_OK) {
+		err = -1;
+		if (status == RCE_SERVER_OBJECT) {
+			err = derrp->dde_err;
+			/*
+			 * XXX For now, log DDLSTATUS_NOT_FOUND as debug until
+			 * we can determine how to handle the RAD caching
+			 * issue.
+			 */
+			dpif_log(err == DDLSTATUS_NOT_FOUND ? 0 : err,
+			    "failed Flow_getInfo(%s): %s",
+			    flowname, derrp->dde_errmsg);
+		}
+	}
+
+	dlmgr_DatalinkError_free(derrp);
+	rc_instance_rele(flow);
+
+	if (err == 0) {
+		err = solaris_flowinfo2action(flowinfo, action);
+		dlmgr__rad_dict_string_DLValue_free(flowinfo);
+	}
+
+	return (err);
+}
+
+static int
+i_solaris_get_flowstats(rc_instance_t *flow, uint64_t *npackets,
+    uint64_t *nbytes, uint64_t *lastused)
+{
+	dlmgr__rad_dict_string_DLValue_t *flowstat = NULL;
+	dlmgr_DLValue_t		*dlval = NULL;
+	dlmgr_DatalinkError_t	*derrp = NULL;
+	rc_err_t		status;
+	int			err = 0;
+
+	status = dlmgr_Flow_getStatistics(flow, NULL, 0, &flowstat, &derrp);
+	if (status != RCE_OK) {
+		err = -1;
+		if (status == RCE_SERVER_OBJECT) {
+			err = derrp->dde_err;
+			dpif_log(err, "failed Flow_getStatistics(): %s",
+			    derrp->dde_errmsg);
+		}
+		goto out;
+	}
+
+#if _TBD_
+	uint16_t tcp_flags; /* bitmaps of tcp_flags */
+#endif
+	status = dlmgr__rad_dict_string_DLValue_get(flowstat, "ipackets",
+	    &dlval);
+	if (status != RCE_OK) {
+		err = EINVAL;
+		goto out;
+	}
+	*npackets = *dlval->ddlv_ulval;
+	dlmgr_DLValue_free(dlval);
+
+	status = dlmgr__rad_dict_string_DLValue_get(flowstat, "opackets",
+	    &dlval);
+	if (status != RCE_OK) {
+		err = EINVAL;
+		goto out;
+	}
+	*npackets += *dlval->ddlv_ulval;
+	dlmgr_DLValue_free(dlval);
+
+	status = dlmgr__rad_dict_string_DLValue_get(flowstat, "ibytes",
+	    &dlval);
+	if (status != RCE_OK) {
+		err = EINVAL;
+		goto out;
+	}
+	*nbytes = *dlval->ddlv_ulval;
+	dlmgr_DLValue_free(dlval);
+
+	status = dlmgr__rad_dict_string_DLValue_get(flowstat, "obytes",
+	    &dlval);
+	if (status != RCE_OK) {
+		err = EINVAL;
+		goto out;
+	}
+	*nbytes += *dlval->ddlv_ulval;
+	dlmgr_DLValue_free(dlval);
+
+	status = dlmgr__rad_dict_string_DLValue_get(flowstat, "lastused",
+	    &dlval);
+	if (status != RCE_OK) {
+		err = EINVAL;
+		goto out;
+	}
+	*lastused = *dlval->ddlv_ulval;
+	dlmgr_DLValue_free(dlval);
+out:
+	dlmgr__rad_dict_string_DLValue_free(flowstat);
+	dlmgr_DatalinkError_free(derrp);
+	return (err);
+}
+
+int
+solaris_get_flowstats(const char *flowname, uint64_t *npackets,
+    uint64_t *nbytes, uint64_t *lastused)
+{
+	rc_err_t status;
+	rc_instance_t *flow = NULL;
+	int err;
+
+	status = dlmgr_Flow__rad_lookup(rad_conn, B_TRUE, &flow, 1,
+	    "name", flowname);
+	if (status != RCE_OK) {
+		err = ENODEV;
+		goto out;
+	}
+
+	err = i_solaris_get_flowstats(flow, npackets, nbytes, lastused);
+out:
+	rc_instance_rele(flow);
+	return (err);
+}
+
+boolean_t
+kstat_handle_init(kstat2_handle_t *khandlep)
+{
+	kstat2_status_t	stat;
+
+	stat = kstat2_open(khandlep);
+	return (stat != KSTAT2_S_OK ? B_FALSE: B_TRUE);
+}
+
+boolean_t
+kstat_handle_update(kstat2_handle_t khandle)
+{
+	kstat2_status_t	stat;
+
+	stat = kstat2_update(khandle);
+	return (stat != KSTAT2_S_OK ? B_FALSE: B_TRUE);
+}
+
+void
+kstat_handle_close(kstat2_handle_t *khandlep)
+{
+	kstat2_close(khandlep);
+}
+
+uint64_t
+get_nvvt_int(kstat2_map_t map, char *name)
+{
+	kstat2_status_t stat;
+	kstat2_nv_t val;
+
+	stat = kstat2_map_get(map, name, &val);
+	if (stat != KSTAT2_S_OK) {
+		(void) printf("can't get value: %s\n",
+		    kstat2_status_string(stat));
+		return (0);
+	}
+
+	if (val->type != KSTAT2_NVVT_INT) {
+		(void) printf("%s is not KSTAT2_NVVT_INT type\n", name);
+		return (0);
+	}
+
+	return (val->kstat2_integer);
+}
+
+void
+solaris_port_walk(void *arg, void (*fn)(void *, const char *, char *,
+    odp_port_t))
+{
+	adr_name_t	**anamearr = NULL;
+	int		anamecnt = 0, i;
+	rc_err_t	rerr;
+
+	rerr = dlmgr_Datalink__rad_list(rad_conn, B_FALSE, NS_GLOB, &anamearr,
+	    &anamecnt, 0);
+	if (rerr != RCE_OK)
+		return;
+
+	if (anamecnt == 0) {
+		free(anamearr);
+		return;
+	}
+
+	for (i = 0; i < anamecnt; i++) {
+		dlmgr_DLDict_t		**dlist = NULL;
+		dlmgr_DLValue_t		*dlval = NULL;
+		const char		*props[1];
+		const char		*fields[1];
+		int			ndlist = 0;
+		rc_instance_t		*rip = NULL;
+		dlmgr_DatalinkError_t	*derrp = NULL;
+		rc_err_t 		rerr;
+
+		rerr = rc_lookup(rad_conn, anamearr[i], NULL, B_FALSE, &rip);
+		if (rerr != RCE_OK)
+			continue;
+
+		props[0] = "ofport";
+		fields[0] = "current";
+		rerr = dlmgr_Datalink_getProperties(rip, props, 1, fields, 1,
+		    &dlist, &ndlist, &derrp);
+		rc_instance_rele(rip);
+		if (rerr != RCE_OK) {
+			if (rerr == RCE_SERVER_OBJECT) {
+				dpif_log(derrp->dde_err,
+				    "failed Datalink_getProperties(%s, %s): %s",
+				    adr_name_key(anamearr[i], "name"),
+				    props[0], derrp->dde_errmsg);
+			}
+			dlmgr_DatalinkError_free(derrp);
+			continue;
+		}
+		rerr = dlmgr__rad_dict_string_DLValue_get((*dlist)->ddld_map,
+		    "current", &dlval);
+		if (rerr != RCE_OK || dlval->ddlv_sval == NULL) {
+			dlmgr_DatalinkError_free(derrp);
+			dlmgr_DLValue_free(dlval);
+			dlmgr_DLDict_array_free(dlist, ndlist);
+			continue;
+		}
+
+		if (dlval->ddlv_sval != NULL && dlval->ddlv_sval[0] != '\0') {
+			fn(arg, adr_name_key(anamearr[i], "name"),
+			    "system", atoi(dlval->ddlv_sval));
+		}
+		dlmgr_DatalinkError_free(derrp);
+		dlmgr_DLValue_free(dlval);
+		dlmgr_DLDict_array_free(dlist, ndlist);
+	}
+	for (i = 0; i < anamecnt; i++)
+		adr_name_rele(anamearr[i]);
+	free(anamearr);
+}
+
+uint64_t
+solaris_flow_walk(void *arg, struct ofpbuf *action, boolean_t no_default,
+    void (*fn)(void *, const char *, boolean_t, struct flow *, struct flow *,
+    struct ofpbuf *, uint64_t, uint64_t, uint64_t))
+{
+	adr_name_t	**anamearr = NULL;
+	int		anamecnt = 0, i;
+	rc_err_t	rerr;
+	int		err = 0;
+	uint64_t	n_flows = 0;
+
+	rerr = dlmgr_Flow__rad_list(rad_conn, B_FALSE, NS_GLOB, &anamearr,
+	    &anamecnt, 0);
+	if (rerr != RCE_OK)
+		return (0);
+
+	if (anamecnt == 0) {
+		free(anamearr);
+		return (0);
+	}
+
+	for (i = 0; i < anamecnt; i++) {
+		dlmgr__rad_dict_string_DLValue_t *flowinfo;
+		char			linkname[MAXLINKNAMELEN];
+		rc_instance_t		*rip = NULL;
+		dlmgr_DLDict_t		**dlist;
+		dlmgr_DLValue_t		*dlval;
+		const char		*props[1];
+		const char		*fields[1];
+		struct flow		f, m;
+		int			ndlist = 0;
+		uint64_t		npackets, nbytes, lastused;
+		boolean_t		is_default;
+		dlmgr_DatalinkError_t	*derrp = NULL;
+
+		flowinfo = NULL;
+		dlist = NULL;
+		dlval = NULL;
+		is_default = B_FALSE;
+		if (strstr(adr_name_key(anamearr[i], "name"), "defflow") !=
+		    NULL) {
+			if (no_default)
+				continue;
+			else
+				is_default = B_TRUE;
+		}
+		rerr = rc_lookup(rad_conn, anamearr[i], NULL, B_FALSE, &rip);
+		if (rerr != RCE_OK)
+			continue;
+
+		if ((err = i_solaris_get_flowstats(rip, &npackets, &nbytes,
+		    &lastused)) != 0) {
+			dpif_log(err, "solaris_flow_walk get_flowstats "
+			    "failed for %s: %d", adr_name_key(anamearr[i],
+			    "name"), err);
+			rc_instance_rele(rip);
+			goto done;
+		}
+
+		rerr = dlmgr_Flow_getInfo(rip, NULL, 0, &flowinfo, &derrp);
+		rc_instance_rele(rip);
+		if (rerr != RCE_OK) {
+			err = -1;
+			if (rerr == RCE_SERVER_OBJECT) {
+				err = derrp->dde_err;
+				/*
+				 * XXX For now, log DDLSTATUS_NOT_FOUND as
+				 * debug until we can determine how to handle
+				 * the RAD caching issue.
+				 */
+				dpif_log(err == DDLSTATUS_NOT_FOUND ? 0 : err,
+				    "failed Flow_getInfo(%s): %s",
+				    adr_name_key(anamearr[i], "name"),
+				    derrp->dde_errmsg);
+			}
+			dlmgr_DatalinkError_free(derrp);
+			goto done;
+		}
+
+		/* See whether this flow is created over of enabled link */
+		err = solaris_flowinfo2linkname(flowinfo, linkname,
+		    MAXLINKNAMELEN);
+		if (err != 0) {
+			goto done;
+		}
+
+		rerr = dlmgr_Datalink__rad_lookup(rad_conn, B_TRUE, &rip,
+		    1, "name", linkname);
+		if (rerr != RCE_OK) {
+			goto done;
+		}
+
+		props[0] = "openvswitch";
+		fields[0] = "current";
+		rerr = dlmgr_Datalink_getProperties(rip, props, 1, fields, 1,
+		    &dlist, &ndlist, &derrp);
+		rc_instance_rele(rip);
+		if (rerr != RCE_OK) {
+			err = -1;
+			if (rerr == RCE_SERVER_OBJECT) {
+				err = derrp->dde_err;
+				dpif_log(err,
+				    "failed Datalink_getProperties(%s, %s): %s",
+				    linkname, props[0], derrp->dde_errmsg);
+			}
+			dlmgr_DatalinkError_free(derrp);
+			goto done;
+		}
+		rerr = dlmgr__rad_dict_string_DLValue_get((*dlist)->ddld_map,
+		    "current", &dlval);
+		if (rerr != RCE_OK)
+			goto done;
+
+		if (!dlval->ddlv_bval)
+			goto done;
+
+		err = solaris_flowinfo2flow(flowinfo, &f, &m);
+		if (err != 0) {
+			dpif_log(err, "solaris_flow_walk flowinfo2flow "
+			    "failed for %s: %d", adr_name_key(anamearr[i],
+			    "name"), err);
+			goto done;
+		}
+
+		if (action != NULL) {
+			err = solaris_flowinfo2action(flowinfo, action);
+			if (err != 0) {
+				dpif_log(err, "solaris_flow_walk "
+				    "flowinfo2flow failed for %s: %d",
+				    adr_name_key(anamearr[i], "name"), err);
+				goto done;
+			}
+		}
+
+		if (fn != NULL) {
+			fn(arg, adr_name_key(anamearr[i], "name"), is_default,
+			    &f, &m, action, npackets, nbytes, lastused);
+		}
+		n_flows++;
+done:
+		dlmgr__rad_dict_string_DLValue_free(flowinfo);
+		dlmgr_DLValue_free(dlval);
+		dlmgr_DLDict_array_free(dlist, ndlist);
+	}
+
+	for (i = 0; i < anamecnt; i++)
+		adr_name_rele(anamearr[i]);
+	free(anamearr);
+	return (n_flows);
+}
+
+int
+solaris_dladm_status2error(dladm_status_t status)
+{
+	int error;
+
+	if (status == DLADM_STATUS_NOMEM) {
+		error = ENOMEM;
+	} else if (status == DLADM_STATUS_DENIED) {
+		error = EPERM;
+	} else if (status == DLADM_STATUS_OK) {
+		error = 0;
+	} else if (status == DLADM_STATUS_IOERR) {
+		error = EIO;
+	} else {
+		error = EINVAL;
+	}
+	return (error);
+}
+
+boolean_t
+solaris_is_uplink_class(const char *class)
+{
+	return (strcmp("phys", class) == 0 ||
+	    strcmp("aggr", class) == 0 ||
+	    strcmp("etherstub", class) == 0 ||
+	    strcmp("vxlan", class) == 0 ||
+	    strcmp("simnet", class) == 0);
+}
+
+/*
+ * This is a copy of dlparse_zonelinkname() function in libinetutil. libinetutil
+ * is not a public interface, therefore we make a copy here.
+ *
+ * Given a linkname that can be specified using a zonename prefix retrieve
+ * the optional linkname and/or zone ID value. If no zonename prefix was
+ * specified we set the optional linkname and set optional zone ID return
+ * value to ALL_ZONES.
+ */
+boolean_t
+solaris_dlparse_zonelinkname(const char *name, char *link_name,
+    zoneid_t *zoneidp)
+{
+	char buffer[MAXLINKNAMESPECIFIER];
+	char *search = "/";
+	char *zonetoken;
+	char *linktoken;
+	char *last;
+	size_t namelen;
+
+	if (link_name != NULL)
+		link_name[0] = '\0';
+	if (zoneidp != NULL)
+		*zoneidp = ALL_ZONES;
+
+	if ((namelen = strlcpy(buffer, name, sizeof (buffer))) >=
+	    sizeof (buffer))
+		return (_B_FALSE);
+
+	if ((zonetoken = strtok_r(buffer, search, &last)) == NULL)
+		return (_B_FALSE);
+
+	/* If there are no other strings, return given name as linkname */
+	if ((linktoken = strtok_r(NULL, search, &last)) == NULL) {
+		if (namelen >= MAXLINKNAMELEN)
+			return (_B_FALSE);
+		if (link_name != NULL)
+			(void) strlcpy(link_name, name, MAXLINKNAMELEN);
+		return (_B_TRUE);
+	}
+
+	/* First token is the zonename. Check zone and link lengths */
+	if (strlen(zonetoken) >= ZONENAME_MAX || strlen(linktoken) >=
+	    MAXLINKNAMELEN)
+		return (_B_FALSE);
+	/*
+	 * If there are more '/' separated strings in the input
+	 * name  then we return failure. We only support a single
+	 * zone prefix or a devnet directory (f.e. net/bge0).
+	 */
+	if (strtok_r(NULL, search, &last) != NULL)
+		return (_B_FALSE);
+
+	if (link_name != NULL)
+		(void) strlcpy(link_name, linktoken, MAXLINKNAMELEN);
+	if (zoneidp != NULL) {
+		if ((*zoneidp = getzoneidbyname(zonetoken)) < MIN_ZONEID)
+			return (_B_FALSE);
+	}
+
+	return (_B_TRUE);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openvswitch/files/lib/util-solaris.h	Mon Nov 16 16:49:19 2015 -0500
@@ -0,0 +1,107 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ */
+
+#ifndef	UTIL_SOLARIS_H
+#define	UTIL_SOLARIS_H
+
+#include <libdladm.h>
+#include <linux/openvswitch.h>
+#include <lib/packets.h>
+#include <sys/types.h>
+#include <kstat2.h>
+#include "odp-util.h"
+
+extern kstat2_handle_t	khandle;
+
+/*
+ * These functions are Solaris specific, so they should be used directly
+ * only by Solaris-specific code.
+ */
+
+struct action_output {
+	int nofports;
+	uint32_t ofports[MAC_OF_MAXPORT];
+};
+
+struct action_set {
+	uint_t	flags;
+	uint_t	priority;
+};
+
+#define	FLOW_SET_PRIORITY 	0x1
+#define	MAX_FLOW_ACTIONS	64
+
+struct flow;
+struct nlattr;
+
+int solaris_dladm_status2error(dladm_status_t);
+int solaris_if_enabled(int, const char *, uint64_t *);
+int solaris_init_rad(void);
+int solaris_get_dlclass(const char *, char *, size_t);
+int solaris_get_devname(const char *, char *, size_t);
+int solaris_get_dllower(const char *, char *, size_t);
+int solaris_get_dlprop(const char *, const char *, const char *, char *,
+    size_t);
+int solaris_set_dlprop_ulong(const char *, const char *, void *);
+int solaris_set_dlprop_boolean(const char *, const char *, void *);
+int solaris_set_dlprop_string(const char *, const char *, void *);
+int solaris_plumb_if(int, const char *, sa_family_t);
+int solaris_unplumb_if(int, const char *, sa_family_t);
+boolean_t dlparse_drvppa(const char *, char *, uint_t, uint_t *);
+
+int solaris_create_vnic(const char *, const char *);
+int solaris_delete_vnic(const char *);
+int solaris_modify_vnic(const char *, const char *);
+int solaris_create_etherstub(const char *);
+int solaris_delete_etherstub(const char *);
+boolean_t solaris_etherstub_exists(const char *);
+
+/* void * since struct dpif causes conflict with the list implementation */
+int solaris_add_flow(void *, const char *, const char *, struct flow *,
+    struct flow *, const struct nlattr *, size_t);
+int solaris_modify_flow(void *, const char *, const struct nlattr *, size_t);
+int solaris_remove_flow(const char *, const char *);
+int solaris_get_flowattr(const char *, struct flow *, struct flow *);
+int solaris_get_flowaction(const char *, struct ofpbuf *);
+void slowpath_to_actions(enum slow_path_reason, struct ofpbuf *);
+int solaris_get_flowstats(const char *, uint64_t *, uint64_t *, uint64_t *);
+
+boolean_t kstat_handle_init(kstat2_handle_t *);
+boolean_t kstat_handle_update(kstat2_handle_t);
+void kstat_handle_close(kstat2_handle_t *);
+uint64_t get_nvvt_int(kstat2_map_t, char *);
+
+void solaris_port_walk(void *, void (*)(void *, const char *, char *,
+    odp_port_t));
+uint64_t solaris_flow_walk(void *, struct ofpbuf *, boolean_t,
+    void (*)(void *, const char *, boolean_t, struct flow *, struct flow *,
+    struct ofpbuf *, uint64_t, uint64_t, uint64_t));
+
+boolean_t solaris_is_uplink_class(const char *);
+boolean_t solaris_dlparse_zonelinkname(const char *, char *, zoneid_t *);
+
+
+#define	SOLARIS_MAX_BUFSIZE	1024
+#endif	/* UTIL_SOLARIS_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openvswitch/files/ovs-clean.py	Mon Nov 16 16:49:19 2015 -0500
@@ -0,0 +1,197 @@
+#!/usr/bin/python2.7
+#
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+
+#
+# Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+#
+
+from rad.bindings.com.oracle.solaris.rad.dlmgr import *
+import rad.client as radcli
+import rad.connect as radcon
+
+def dlvalue_to_propval(adict):
+    pval = None
+    for key, value in adict.iteritems():
+        for k, v in vars(value).iteritems():
+            if k == 'type':
+                continue
+            if v is not None:
+                pval = v
+                break
+    return pval
+
+def printError(ex):
+    dlerr = ex.get_payload()
+    if dlerr != None:
+        print "Error: %s" % dlerr.errmsg
+    else:
+        print "Exception: %s" % ex
+
+def deleteVNIC(argv, numargs, dm):
+    if numargs !=  3:
+        print "usage: %s delete-vnic vnicname" % (argv[0])
+        return False
+
+    try:
+        dm.deleteVNIC(argv[2])
+    except radcli.ObjectError as ex:
+        printError(ex)
+        return False
+
+    return True
+
+def resetOFPorts(argv, numargs, rc):
+    if numargs != 2:
+        print "usage: %s reset-ofports" % (argv[0])
+        return False
+
+    # Enumerate all the datalinks and reset any ofports that are set
+    rval = True
+    datalink_name_list = rc.list_objects(Datalink())
+    for datalink_name in datalink_name_list:
+        datalink = rc.get_object(datalink_name)
+
+        try:
+            props = datalink.getProperties(["ofport", "temporary"], ["current"])
+        except radcli.ObjectError as ex:
+            dlerr = ex.get_payload()
+            # ignore if not a supported property
+	    if dlerr.err == DLSTATUS.INVALID_ARGUMENT._value:
+                continue
+            printError(ex)
+            rval = False
+            continue
+
+        prop = props[0]
+        pval =  dlvalue_to_propval(prop.map)
+        if pval == None or pval == 0:
+            continue
+        prop = props[1]
+        pval =  dlvalue_to_propval(prop.map)
+        try:
+            datalink.setProperties({
+                'temporary': DLValue(type=DLValueType.BOOLEAN, bval=pval),
+                'ofport': DLValue(type=DLValueType.ULONG, ulval=None)
+            })
+        except radcli.ObjectError as ex:
+            dlerr = ex.get_payload()
+            printError(ex)
+            rval = False
+
+    return rval
+
+def removeFlows(argv, numargs, rc):
+    if numargs != 2:
+        print "usage: %s remove-flows" % (argv[0])
+        return False
+
+    # Enumerate all the datalinks and look for OF links
+    rval = True
+    datalink_list = rc.list_objects(Datalink())
+    for datalink_name in datalink_list:
+        datalink = rc.get_object(datalink_name)
+        linkname = datalink_name._kvpairs["name"]
+
+	try:
+            props = datalink.getProperties(["openvswitch"], ["current"])
+        except radcli.ObjectError as ex:
+            dlerr = ex.get_payload()
+            # ignore if not a supported property
+	    if dlerr.err == DLSTATUS.INVALID_ARGUMENT._value:
+                continue
+            printError(ex)
+            rval = False
+            continue
+
+        prop = props[0]
+        pval =  dlvalue_to_propval(prop.map)
+        if pval == str("off"):
+            continue
+
+        # Enumerate all the flows for the datalink and remove them
+        flow_list = rc.list_objects(Flow(), ADRGlobPattern({'linkname': linkname}))
+        for flow_name in flow_list:
+            try:
+                datalink.removeFlow(flow_name._kvpairs["name"])
+            except radcli.ObjectError as ex:
+                dlerr = ex.get_payload()
+                # ignore if the flow is no longer found
+                if dlerr.err == DLSTATUS.NOT_FOUND._value:
+                    continue
+                printError(ex)
+                rval = False
+
+    return rval
+
+def deleteEtherstub(argv, numargs, rc, dm):
+    if numargs != 3:
+        print "usage: %s delete-etherstub etherstub-name" % (argv[0])
+        return False
+
+    name = argv[2]
+    try:
+        etherstub = rc.get_object(Etherstub(), ADRGlobPattern({'name': name}))
+        etherstub.setProperties({
+            'temporary': DLValue(type=DLValueType.BOOLEAN, bval="True"),
+            'openvswitch': DLValue(type=DLValueType.BOOLEAN, bval=None)
+            })
+        dm.deleteEtherstub(name)
+    except radcli.NotFoundError as ex:
+        pass
+    except radcli.ObjectError as ex:
+        dlerr = ex.get_payload()
+        printError(ex)
+        return False
+
+    return True
+
+def main():
+    numargs = len(sys.argv)
+    if numargs < 2:
+        print "usage: %s command [args]" % (sys.argv[0])
+        print "       %s delete-vnic vnic-name" % (sys.argv[0])
+        print "       %s reset-ofports" % (sys.argv[0])
+        print "       %s remove-flows" % (sys.argv[0])
+        print "       %s delete-etherstub etherstub-name" % (sys.argv[0])
+        sys.exit(1)
+
+    rc = radcon.connect_unix()
+    dm = rc.get_object(DatalinkManager())
+
+    if sys.argv[1] == "delete-vnic":
+        rval = deleteVNIC(sys.argv, numargs, dm)
+    elif sys.argv[1] == "reset-ofports":
+        rval = resetOFPorts(sys.argv, numargs, rc)
+    elif sys.argv[1] == "remove-flows":
+        rval = removeFlows(sys.argv, numargs, rc)
+    elif sys.argv[1] == "delete-etherstub":
+        rval = deleteEtherstub(sys.argv, numargs, rc, dm)
+    else:
+        print "\nInvalid ovs shutdown function"
+        rval = False;
+
+    sys.exit(rval == False)
+
+if __name__ == "__main__":
+    main()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openvswitch/files/ovs-svc	Mon Nov 16 16:49:19 2015 -0500
@@ -0,0 +1,201 @@
+#!/sbin/sh
+#
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+
+#
+# Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+#
+
+. /lib/svc/share/smf_include.sh
+. /lib/svc/share/net_include.sh
+
+typeset -r OVS_OVSDB_FMRI=svc:/application/openvswitch/ovsdb-server:default
+typeset -r OVS_VSWITCHD_FMRI=svc:/application/openvswitch/vswitch-server:default
+
+typeset -r OVS_USRLIB_DIR=/usr/lib/ovs
+typeset -r OVS_VARLIB_DIR=/var/lib/ovs
+typeset -r OVS_TMP_DIR=/var/run/ovs
+typeset -r OVS_LOG_DIR=/var/log/ovs
+
+typeset -r OVS_USER=_ovs
+typeset -r OVS_GROUP=_ovs
+
+typeset -r OVS_SBIN_DIR=/usr/sbin
+typeset -r OVSDB_TOOL=${OVS_SBIN_DIR}/ovsdb-tool
+typeset -r OVS_VSCTL=${OVS_SBIN_DIR}/ovs-vsctl
+
+typeset -r OVS_VSWITCHD=ovs-vswitchd
+typeset -r OVSDB_SERVER=ovsdb-server
+typeset -r OVS_CLEAN=ovs-clean.py
+typeset -r OVSDB_SERVER_PATH=${OVS_USRLIB_DIR}/${OVSDB_SERVER}
+typeset -r OVS_VSWITCHD_PATH=${OVS_USRLIB_DIR}/${OVS_VSWITCHD}
+typeset -r OVS_CLEAN_PATH=${OVS_USRLIB_DIR}/${OVS_CLEAN}
+
+typeset -r OVSDB_REMOTE=${OVS_TMP_DIR}/db.sock
+
+typeset -r PFEXEC=/usr/bin/pfexec
+typeset -r MKDIR=/usr/bin/mkdir
+typeset -r CHOWN=/usr/bin/chown
+typeset -r PKILL=/usr/bin/pkill
+typeset -r DLADM=/usr/sbin/dladm
+typeset -r FLOWADM=/usr/sbin/flowadm
+
+errlog () {
+	echo $1 >&2
+}
+
+create_ovs_tempdir() {
+	if [[ ! -d ${OVS_TMP_DIR} ]]; then
+		${PFEXEC} ${MKDIR} -m 775 ${OVS_TMP_DIR} || exit $SMF_EXIT_ERR_CONFIG
+                ${PFEXEC} ${CHOWN} ${OVS_USER}:${OVS_GROUP} ${OVS_TMP_DIR}
+	fi
+}
+
+start_ovsdb_server() {
+	typeset -i OVSDB_INIT=0
+	typeset -r OVSDB_DATABASE=${OVS_VARLIB_DIR}/etc/conf.db
+
+	create_ovs_tempdir
+
+	if [[ ! -f ${OVSDB_DATABASE} ]]; then
+		typeset -r OVSDB_SCHEMA=${OVS_USRLIB_DIR}/share/vswitch.ovsschema
+
+		echo "Creating ${OVSDB_DATABASE} from ${OVSDB_SCHEMA}"
+		${OVSDB_TOOL} create ${OVSDB_DATABASE} ${OVSDB_SCHEMA}
+		if [ $? -ne 0 ]; then
+			errlog "Error creating database, exiting"
+			return 1
+		fi
+		OVSDB_INIT=1
+	fi
+
+	typeset -r OVSDB_LOGFILE=${OVS_LOG_DIR}/ovsdb-server.log
+	typeset -r OVSDB_PIDFILE=${OVS_TMP_DIR}/ovsdb-server.pid
+
+	${OVSDB_SERVER_PATH} ${OVSDB_DATABASE} \
+	    -vconsole:emer -vsyslog:err -vfile:info \
+	    --remote=punix:${OVSDB_REMOTE} \
+	    --remote=db:Open_vSwitch,Open_vSwitch,manager_options \
+	    --private-key=db:Open_vSwitch,SSL,private_key \
+	    --certificate=db:Open_vSwitch,SSL,certificate \
+	    --bootstrap-ca-cert=db:Open_vSwitch,SSL,ca_cert \
+	    --no-chdir --log-file=${OVSDB_LOGFILE} \
+	    --pidfile=${OVSDB_PIDFILE} \
+	    --detach
+	if [ $? -ne 0 ]; then
+		errlog "${OVSDB_SERVER_PATH} failed with $?"
+		exit $SMF_EXIT_ERR_FATAL
+	fi
+
+	if [ ${OVSDB_INIT} -ne 0 ]; then
+		echo "Initializing OVSDB database"
+		${OVS_VSCTL} --no-wait init
+		if [ $? -ne 0 ]; then
+			errlog "${OVSDB_VSCTL} failed with $?"
+			exit $SMF_EXIT_ERR_FATAL
+		fi
+	fi
+}
+
+stop_vswitch_server() {
+        typeset BRIDGENAME
+
+        $PKILL -x -u ${OVS_USER} -z `smf_zonename` ${OVS_VSWITCHD}
+        if [[ $? -ne 0 && $? -ne 1 ]]; then
+                errlog "pkill of ${OVS_VSWITCHD} failed with $?"
+        fi
+
+        ${OVS_VSCTL} list-br |
+                while read BRIDGENAME; do
+                        ${OVS_CLEAN_PATH} delete-vnic ${BRIDGENAME}
+                        if [ $? -ne 0 ]; then
+                                errlog "Error $? removing ${BRIDGENAME} VNIC"
+                        fi
+                done
+        ${OVS_CLEAN_PATH} reset-ofports
+	if [ $? -ne 0 ]; then
+        	errlog "Error $? resetting OF ports"
+	fi
+
+        ${OVS_CLEAN_PATH} remove-flows
+        if [ $? -ne 0 ]; then
+                errlog "Error $? removing OF flows"
+        fi
+
+        ${OVS_CLEAN_PATH} delete-etherstub ovs.etherstub0
+        if [ $? -ne 0 ]; then
+                errlog "Error $? deleting the OVS etherstub"
+        fi
+
+}
+
+start_vswitch_server() {
+	typeset -r VSWITCHD_LOGFILE=${OVS_LOG_DIR}/ovs-vswitchd.log
+	typeset -r VSWITCHD_PIDFILE=${OVS_TMP_DIR}/ovs-vswitchd.pid
+
+	create_ovs_tempdir
+
+	${OVS_VSWITCHD_PATH} unix:${OVSDB_REMOTE} \
+	    -vconsole:emer -vsyslog:err -vfile:info --mlockall --no-chdir \
+	    --log-file=${VSWITCHD_LOGFILE} \
+	    --pidfile=${VSWITCHD_PIDFILE} \
+	    --detach
+	if [ $? -ne 0 ]; then
+		errlog "${OVS_VSWITCHD} failed with $?"
+		exit $SMF_EXIT_ERR_FATAL
+	fi
+}
+
+case "$1" in
+'start')
+	case "$SMF_FMRI" in
+	"${OVS_OVSDB_FMRI}")
+		start_ovsdb_server
+		;;
+	"${OVS_VSWITCHD_FMRI}")
+		start_vswitch_server
+		;;
+	*)
+		echo "$SMF_FMRI does not support start method"
+		exit $SMF_EXIT_ERR_FATAL
+		;;
+	esac
+	;;
+'stop')
+	case "$SMF_FMRI" in
+	"${OVS_VSWITCHD_FMRI}")
+		stop_vswitch_server
+		;;
+	*)
+		echo "$SMF_FMRI does not support stop method"
+		exit $SMF_EXIT_ERR_FATAL
+		;;
+	esac 
+	;;
+*)
+	echo "Service must be invoked from within SMF"
+	exit $SMF_EXIT_ERR_FATAL
+	;;
+esac
+
+exit $SMF_EXIT_OK
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openvswitch/files/ovs.auth_attr	Mon Nov 16 16:49:19 2015 -0500
@@ -0,0 +1,3 @@
+solaris.smf.manage.ovs:RO::Manage Open Virtual Switch States::
+solaris.smf.value.ovs:RO::Change Values of Open Virtual Switch Properties::
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openvswitch/files/ovs.exec_attr	Mon Nov 16 16:49:19 2015 -0500
@@ -0,0 +1,21 @@
+ovs-agent:solaris:cmd:RO::/usr/lib/rad/module/mod_dlmgr.so.1:\
+privs=sys_dl_config
+
+ovs-agent:solaris:cmd:::/usr/bin/mkdir:privs={zone}\:/system/volatile/ovs
+ovs-agent:solaris:cmd:::/usr/bin/chown:privs={zone}\:/system/volatile/ovs
+
+OVS Administration:solaris:cmd:RO::/usr/sbin/ovs-ofctl:\
+privs={file_dac_write}\:/system/volatile/ovs/*,\
+{file_dac_read}\:/system/volatile/ovs/*
+
+OVS Administration:solaris:cmd:RO::/usr/sbin/ovs-vsctl:\
+privs={file_dac_write}\:/system/volatile/ovs/*,\
+{file_dac_read}\:/system/volatile/ovs/*
+
+OVS Administration:solaris:cmd:RO::/usr/sbin/ovs-appctl:\
+privs={file_dac_write}\:/system/volatile/ovs/*,\
+{file_dac_read}\:/system/volatile/ovs/*
+
+OVS Administration:solaris:cmd:RO::/usr/sbin/ovsdb-client:\
+privs={file_dac_write}\:/system/volatile/ovs/*,\
+{file_dac_read}\:/system/volatile/ovs/*
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openvswitch/files/ovs.prof_attr	Mon Nov 16 16:49:19 2015 -0500
@@ -0,0 +1,10 @@
+OVS Administration:RO::\
+Administer Privileged OVS Operations:\
+auths=solaris.smf.manage.ovs,solaris.smf.value.ovs;\
+defaultpriv={file_dac_search}\:/var/log/ovs,\
+{file_dac_read}\:/var/log/ovs/*
+
+ovs-agent:RO::\
+Do not assign to users. \
+Authorizations required by vswitch-server:\
+auths=solaris.network.interface.config,solaris.smf.modify
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openvswitch/files/ovs.user_attr	Mon Nov 16 16:49:19 2015 -0500
@@ -0,0 +1,1 @@
+_ovs::RO::profiles=ovs-agent
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openvswitch/files/ovsdb.xml	Mon Nov 16 16:49:19 2015 -0500
@@ -0,0 +1,91 @@
+<?xml version="1.0"?>
+<!DOCTYPE service_bundle SYSTEM '/usr/share/lib/xml/dtd/service_bundle.dtd.1'>
+<!--
+CDDL HEADER START
+
+The contents of this file are subject to the terms of the
+Common Development and Distribution License (the "License").
+You may not use this file except in compliance with the License.
+
+You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+or http://www.opensolaris.org/os/licensing.
+See the License for the specific language governing permissions
+and limitations under the License.
+
+When distributing Covered Code, include this CDDL HEADER in each
+file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+If applicable, add the following below this CDDL HEADER, with the
+fields enclosed by brackets "[]" replaced with your own identifying
+information: Portions Copyright [yyyy] [name of copyright owner]
+
+CDDL HEADER END
+
+ Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+
+ NOTE:  This service manifest is not editable; its contents will
+ be overwritten by package or patch operations, including
+ operating system upgrade.  Make customizations in a different
+ file.
+-->
+
+<service_bundle type="manifest" name="ovsdb">
+
+    <service name="application/openvswitch/ovsdb-server" type="service" version="1">
+        <dependency
+            name='multiuser'
+            grouping='require_all'
+            restart_on='error'
+            type='service'>
+            <service_fmri value='svc:/milestone/multi-user:default'/>
+        </dependency>
+
+        <instance name='default' enabled='true'>
+            <exec_method
+                type="method"
+                name="start"
+                timeout_seconds="60" 
+                exec="/lib/svc/method/ovs-svc %m">
+
+                <method_context>
+                    <method_credential user='_ovs' group='_ovs'/>
+                </method_context>
+            </exec_method>
+
+            <exec_method
+                type="method" 
+                name="stop"
+                timeout_seconds="60"
+                exec=":kill"/>
+
+            <!-- to start/stop/refresh the service -->
+            <property_group name='general' type='framework'>
+                <propval
+                    name='action_authorization'
+                    type='astring'
+                    value='solaris.smf.manage.ovs'/>
+
+                <propval
+                    name='value_authorization'
+                    type='astring'
+                    value='solaris.smf.value.ovs'/>
+            </property_group>
+
+            <template>
+                <common_name>
+                    <loctext xml:lang="C">
+                        Open Virtual Switch Database Server
+                    </loctext>
+                </common_name>
+                <description>
+                    <loctext xml:lang="C">
+                        Server to manage Open Virtual Switch Database
+                    </loctext>
+                </description>
+                <documentation>
+		    <external_logfile path='/var/log/ovs/ovsdb-server.log' />
+                </documentation>
+            </template>
+        </instance>
+    </service>
+</service_bundle>
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openvswitch/files/vswitch.xml	Mon Nov 16 16:49:19 2015 -0500
@@ -0,0 +1,104 @@
+<?xml version="1.0"?>
+<!DOCTYPE service_bundle SYSTEM '/usr/share/lib/xml/dtd/service_bundle.dtd.1'>
+<!--
+CDDL HEADER START
+
+The contents of this file are subject to the terms of the
+Common Development and Distribution License (the "License").
+You may not use this file except in compliance with the License.
+
+You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+or http://www.opensolaris.org/os/licensing.
+See the License for the specific language governing permissions
+and limitations under the License.
+
+When distributing Covered Code, include this CDDL HEADER in each
+file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+If applicable, add the following below this CDDL HEADER, with the
+fields enclosed by brackets "[]" replaced with your own identifying
+information: Portions Copyright [yyyy] [name of copyright owner]
+
+CDDL HEADER END
+
+ Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+
+ NOTE:  This service manifest is not editable; its contents will
+ be overwritten by package or patch operations, including
+ operating system upgrade.  Make customizations in a different
+ file.
+-->
+
+<service_bundle type="manifest" name="vswitch">
+
+    <service name="application/openvswitch/vswitch-server" type="service" version="1">
+        <dependency
+            name='multiuser'
+            grouping='require_all'
+            restart_on='error'
+            type='service'>
+            <service_fmri value='svc:/milestone/multi-user:default'/>
+        </dependency>
+
+        <dependency
+            name='ovsdb-server'
+            grouping='require_all'
+            restart_on='none'
+            type='service'>
+            <service_fmri
+                value='svc:/application/openvswitch/ovsdb-server:default'/>
+        </dependency>
+
+        <instance name='default' enabled='true'>
+            <exec_method
+                type="method"
+                name="start"
+                exec="/lib/svc/method/ovs-svc %m"
+                timeout_seconds="60">
+
+                <method_context>
+                    <method_credential
+                        user='_ovs'
+                        group='_ovs'
+                        privileges='basic,net_rawaccess,proc_lock_memory'/>
+                </method_context>
+            </exec_method>
+
+            <exec_method
+                type="method" 
+                name="stop"
+                timeout_seconds="60"
+                exec="/lib/svc/method/ovs-svc %m">
+            </exec_method>
+
+            <!-- to start/stop/refresh the service -->
+            <property_group name='general' type='framework'>
+                <propval
+                    name='action_authorization'
+                    type='astring'
+                    value='solaris.smf.manage.ovs'/>
+
+                <propval
+                    name='value_authorization'
+                    type='astring'
+                    value='solaris.smf.value.ovs'/>
+            </property_group>
+
+            <template>
+                <common_name>
+                    <loctext xml:lang="C">
+                        Open Virtual Switch Daemon
+                    </loctext>
+                </common_name>
+                <description>
+                    <loctext xml:lang="C">
+                        Daemon that manages Open Virtual Switch switches
+                    </loctext>
+                </description>
+                <documentation>
+		    <external_logfile path='/var/log/ovs/ovs-vswitchd.log' />
+                </documentation>
+            </template>
+        </instance>
+    </service>
+</service_bundle>
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openvswitch/openvswitch.license	Mon Nov 16 16:49:19 2015 -0500
@@ -0,0 +1,211 @@
+The following applies to all products licensed under the Apache 2.0 License:
+
+You may not use the identified files except in compliance with the Apache License, Version 2.0 (the "License.")
+ 
+You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.  A copy of the license is also reproduced below.
+
+Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+
+See the License for the specific language governing permissions and limitations under the License.
+
+		                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openvswitch/openvswitch.p5m	Mon Nov 16 16:49:19 2015 -0500
@@ -0,0 +1,84 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+#
+
+<transform file path=usr.*/man/.+ -> default mangler.man.stability committed>
+set name=pkg.fmri \
+    value=pkg:/service/network/openvswitch@$(COMPONENT_VERSION),$(BUILD_VERSION)
+set name=pkg.summary value="Open Virtual Switch and Utilities"
+set name=pkg.description \
+    value="Open vSwitch is a production quality open source software switch designed to be used as a vswitch in virtualized server environments.  A vswitch forwards traffic between different VMs on the same physical host and also forwards traffic between VMs and the physical network."
+set name=com.oracle.info.tpno value=$(TPNO)
+set name=info.classification value=org.opensolaris.category.2008:System/Services
+set name=info.source-url value=$(COMPONENT_ARCHIVE_URL)
+set name=info.upstream-url value=$(COMPONENT_PROJECT_URL)
+set name=org.opensolaris.arc-caseid value=PSARC/2015/999
+set name=org.opensolaris.consolidation value=$(CONSOLIDATION)
+file ovs.user_attr path=etc/security/auth_attr.d/application:openswitch:ovs \
+    group=sys
+file ovs.exec_attr path=etc/security/exec_attr.d/application:openswitch:ovs \
+    group=sys
+file ovs.prof_attr path=etc/security/prof_attr.d/application:openswitch:ovs \
+    group=sys
+file ovs.user_attr path=etc/user_attr.d/application:openswitch:ovs group=sys
+file ovsdb.xml path=lib/svc/manifest/application/openvswitch/ovsdb.xml group=sys
+# The following pkg.depend.bypass-generate is needed because the vswitch
+# service depends upon the ovsdb service. pkgdepend fails to find the file
+# containing the ovsdb service (since ovsdb.xml is under
+# $(COMPONENT_DIR)/files) and fails.
+file vswitch.xml path=lib/svc/manifest/application/openvswitch/vswitch.xml \
+    group=sys pkg.depend.bypass-generate=.*
+file ovs-svc path=lib/svc/method/ovs-svc mode=0555
+file ovs-clean.py path=usr/lib/ovs/ovs-clean.py mode=0555
+file path=usr/lib/ovs/ovs-vswitchd mode=0555
+file path=usr/lib/ovs/ovsdb-server mode=0555
+file usr/lib/ovs/share/openvswitch/vswitch.ovsschema \
+    path=usr/lib/ovs/share/vswitch.ovsschema
+file tests/test-controller path=usr/lib/ovs/tests/test-controller mode=0555 \
+    facet.optional.test=true
+file path=usr/sbin/ovs-appctl
+file path=usr/sbin/ovs-dpctl
+file path=usr/sbin/ovs-ofctl
+file path=usr/sbin/ovs-pki
+file path=usr/sbin/ovs-vsctl
+file path=usr/sbin/ovsdb-client
+file path=usr/sbin/ovsdb-tool
+file path=usr/share/man/man1/ovsdb-client.1
+file path=usr/share/man/man1/ovsdb-server.1
+file path=usr/share/man/man1/ovsdb-tool.1
+file path=usr/share/man/man8/ovs-appctl.8
+file path=usr/share/man/man8/ovs-dpctl.8
+file path=usr/share/man/man8/ovs-ofctl.8
+file path=usr/share/man/man8/ovs-pki.8
+file path=usr/share/man/man8/ovs-vsctl.8
+file path=usr/share/man/man8/ovs-vswitchd.8
+dir  path=var/lib/ovs/etc owner=_ovs group=_ovs mode=0700
+dir  path=var/log/ovs owner=_ovs group=_ovs mode=0700
+group groupname=_ovs gid=99
+user username=_ovs ftpuser=false gcos-field="Open Virtual Switch" group=_ovs \
+    home-dir=/var/lib/ovs uid=99
+license openvswitch.license license="openvswitch license"
+depend type=require fmri=__TBD \
+    pkg.debug.depend.file=lib/svc/share/net_include.sh \
+    pkg.debug.reason=lib/svc/method/ovs
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openvswitch/patches/01-solaris-port.patch	Mon Nov 16 16:49:19 2015 -0500
@@ -0,0 +1,361 @@
+This patch includes changes that allow OVS to build on
+on Solaris. The specific changes are:
+
+    acinclude.m4           Use dlmgr to identify Solaris build
+    configure.ac
+
+    include/automake.mk    Build for Solaris
+    lib/automake.mk
+
+    lib/byte-order.h       Solaris supports htonll natively
+
+    lib/flow.h             Add ip6_ext structure for Solaris
+
+    lib/sflow.h            Map OVS int types to Solaris
+    lib/socket_util.h
+
+    lib/socket-util.c      Fix set_dscp setsockopt error return for Solaris
+    python/ovs/socket_util.py
+
+    vswitchd/automake.mk   Solaris sed whitespace fix
+    vtep/automake.mk
+
+    lib/socket-util.c      Rename 'sun' argument
+    lib/lib-stream-unix.c
+
+This patch has not been proposed upstream because we are not yet
+proposing Solaris specific requirements upstream.
+
+diff --git a/acinclude.m4 b/acinclude.m4
+index 8d10360..2b5944e 100644
+--- a/acinclude.m4
++++ b/acinclude.m4
+@@ -378,6 +378,22 @@ AC_DEFUN([OVS_CHECK_IF_DL],
+       AC_SEARCH_LIBS([pcap_open_live], [pcap])
+    fi])
+ 
++dnl Checks for rad/client/1/dlmgr.h
++dnl
++dnl (For now, we use this as a proxy for checking whether we're building on
++dnl Solaris). Also, note that LDFLAGS has to be modified because
++dnl libopenvswitch.la consumes the dlmgr RAD client.
++AC_DEFUN([OVS_CHECK_DLMGR],
++  [AC_CHECK_HEADER([rad/client/1/dlmgr.h],
++                   [HAVE_DLMGR=yes],
++                   [HAVE_DLMGR=no])
++   AM_CONDITIONAL([SOLARIS], [test "$HAVE_DLMGR" = yes])
++   if test "$HAVE_DLMGR" = yes; then
++      AC_DEFINE([SOLARIS], [1],
++                [Define to 1 if rad/client/1/dlmgr.h is available.])
++      LDFLAGS="$LDFLAGS -R /usr/lib/rad/client/c/64"
++   fi])
++
+ dnl Checks for buggy strtok_r.
+ dnl
+ dnl Some versions of glibc 2.7 has a bug in strtok_r when compiling
+diff --git a/configure.ac b/configure.ac
+index 5c1f398..56ce096 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -61,6 +61,7 @@ OVS_CHECK_PYTHON_COMPAT
+ OVS_CHECK_DOT
+ OVS_CHECK_IF_PACKET
+ OVS_CHECK_IF_DL
++OVS_CHECK_DLMGR
+ OVS_CHECK_STRTOK_R
+ AC_CHECK_DECLS([sys_siglist], [], [], [[#include <signal.h>]])
+ AC_CHECK_MEMBERS([struct stat.st_mtim.tv_nsec, struct stat.st_mtimensec],
+diff --git a/include/automake.mk b/include/automake.mk
+index 37a6f77..302329e 100644
+--- a/include/automake.mk
++++ b/include/automake.mk
+@@ -1,5 +1,6 @@
+ include include/linux/automake.mk
+ include include/openflow/automake.mk
+ include include/openvswitch/automake.mk
++include include/solaris/automake.mk
+ include include/sparse/automake.mk
+ include include/windows/automake.mk
+diff --git a/lib/automake.mk b/lib/automake.mk
+index de3f068..5029dd3 100644
+--- a/lib/automake.mk
++++ b/lib/automake.mk
+@@ -13,8 +13,21 @@ if WIN32
+ lib_libopenvswitch_la_LIBADD += ${PTHREAD_LIBS}
+ endif
+ 
++if SOLARIS
++lib_libopenvswitch_la_LIBADD += -ldladm
++lib_libopenvswitch_la_LIBADD += -ldlpi
++lib_libopenvswitch_la_LIBADD += -ldlmgr_client
++lib_libopenvswitch_la_LIBADD += -lradclient
++lib_libopenvswitch_la_LIBADD += -lkstat2
++lib_libopenvswitch_la_LIBADD += -ladr
++endif
++
+ lib_libopenvswitch_la_LDFLAGS = -release $(VERSION)
+ 
++if SOLARIS
++lib_libopenvswitch_la_LDFLAGS += -L/usr/lib/rad/client/c/64
++endif
++
+ lib_libopenvswitch_la_SOURCES = \
+ 	lib/aes128.c \
+ 	lib/aes128.h \
+@@ -304,6 +317,16 @@ lib_libopenvswitch_la_SOURCES += \
+ 	lib/route-table.h
+ endif
+ 
++if SOLARIS
++lib_libopenvswitch_la_SOURCES += \
++	lib/dpif-solaris.c \
++	lib/dpif-solaris.h \
++	lib/netdev-solaris.c \
++	lib/netdev-solaris.h \
++	lib/util-solaris.c \
++	lib/util-solaris.h
++endif
++
+ if DPDK_NETDEV
+ lib_libopenvswitch_la_SOURCES += \
+        lib/netdev-dpdk.c \
+@@ -322,12 +345,16 @@ lib_libopenvswitch_la_SOURCES += \
+ endif
+ 
+ if HAVE_IF_DL
++if SOLARIS
+ lib_libopenvswitch_la_SOURCES += \
++	lib/route-table-solaris.c
++else
+ 	lib/netdev-bsd.c \
+ 	lib/rtbsd.c \
+ 	lib/rtbsd.h \
+ 	lib/route-table-bsd.c
+ endif
++endif
+ 
+ if HAVE_OPENSSL
+ lib_libopenvswitch_la_SOURCES += lib/stream-ssl.c
+diff --git a/lib/byte-order.h b/lib/byte-order.h
+index 544f46f..b63a2b9 100644
+--- a/lib/byte-order.h
++++ b/lib/byte-order.h
+@@ -22,7 +22,7 @@
+ #include "openvswitch/types.h"
+ 
+ #ifndef __CHECKER__
+-#ifndef _WIN32
++#if !defined (_WIN32) && !defined (__sun)
+ static inline ovs_be64
+ htonll(uint64_t n)
+ {
+@@ -34,7 +34,7 @@ ntohll(ovs_be64 n)
+ {
+     return htonl(1) == 1 ? n : ((uint64_t) ntohl(n) << 32) | ntohl(n >> 32);
+ }
+-#endif /* _WIN32 */
++#endif /* _WIN32/__sun */
+ #else
+ /* Making sparse happy with these functions also makes them unreadable, so
+  * don't bother to show it their implementations. */
+diff --git a/lib/flow.h b/lib/flow.h
+index 0f3ffde..f141cb0 100644
+--- a/lib/flow.h
++++ b/lib/flow.h
+@@ -28,6 +28,10 @@
+ #include "hash.h"
+ #include "util.h"
+ 
++#ifdef __sun
++#include "solaris/solarisdefs.h"
++#endif
++
+ struct dpif_flow_stats;
+ struct ds;
+ struct flow_wildcards;
+diff --git a/lib/sflow.h b/lib/sflow.h
+index c6cde7f..2279a62 100644
+--- a/lib/sflow.h
++++ b/lib/sflow.h
+@@ -12,6 +12,10 @@
+ #include "windefs.h"
+ #endif
+ 
++#ifdef __sun
++#include "solaris/solarisdefs.h"
++#endif
++
+ typedef enum {
+     SFL_DSCLASS_IFINDEX = 0,
+     SFL_DSCLASS_VLAN = 1,
+diff --git a/lib/socket-util.h b/lib/socket-util.h
+index 2acc974..ebd3ae5 100644
+--- a/lib/socket-util.h
++++ b/lib/socket-util.h
+@@ -27,6 +27,10 @@
+ #include <netinet/in_systm.h>
+ #include <netinet/ip.h>
+ 
++#ifdef __sun
++#include "solaris/solarisdefs.h"
++#endif
++
+ int set_nonblocking(int fd);
+ void xset_nonblocking(int fd);
+ int set_dscp(int fd, uint8_t dscp);
+diff --git a/lib/socket-util.c b/lib/socket-util.c
+index aa0c719..afc607e 100644
+--- a/lib/socket-util.c
++++ b/lib/socket-util.c
+@@ -111,6 +111,7 @@ set_dscp(int fd, uint8_t dscp)
+ {
+     int val;
+     bool success;
++    int err = 0;
+ 
+     if (dscp > 63) {
+         return EINVAL;
+@@ -120,29 +121,39 @@ set_dscp(int fd, uint8_t dscp)
+     success = false;
+     val = dscp << 2;
+     if (setsockopt(fd, IPPROTO_IP, IP_TOS, &val, sizeof val)) {
++	err = sock_errno();
++#ifndef __sun
+ #ifndef _WIN32
+-        if (sock_errno() != ENOPROTOOPT) {
++        if (err != ENOPROTOOPT) {
+ #else
+-        if (sock_errno() != WSAENOPROTOOPT) {
++        if (err != WSAENOPROTOOPT) {
+ #endif
+-            return sock_errno();
++            return err;
+         }
++#endif
+     } else {
+         success = true;
+     }
+     if (setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &val, sizeof val)) {
++	err = sock_errno();
++#ifndef __sun
+ #ifndef _WIN32
+-        if (sock_errno() != ENOPROTOOPT) {
++        if (err != ENOPROTOOPT) {
+ #else
+-        if (sock_errno() != WSAENOPROTOOPT) {
++        if (err != WSAENOPROTOOPT) {
+ #endif
+-            return sock_errno();
++            return err;
+         }
++#endif
+     } else {
+         success = true;
+     }
+     if (!success) {
++#ifndef __sun
+         return ENOPROTOOPT;
++#else
++	return err;
++#endif
+     }
+ 
+     return 0;
+@@ -505,11 +516,11 @@ free_sockaddr_un(int dirfd, const char *linkname)
+ 
+ /* Binds Unix domain socket 'fd' to a file with permissions 0700. */
+ static int
+-bind_unix_socket(int fd, struct sockaddr *sun, socklen_t sun_len)
++bind_unix_socket(int fd, struct sockaddr *addr, socklen_t addrlen)
+ {
+     /* According to _Unix Network Programming_, umask should affect bind(). */
+     mode_t old_umask = umask(0077);
+-    int error = bind(fd, sun, sun_len) ? errno : 0;
++    int error = bind(fd, addr, addrlen) ? errno : 0;
+     umask(old_umask);
+     return error;
+ }
+@@ -1115,15 +1126,15 @@ describe_sockaddr(struct ds *string, int fd,
+                           ss_get_port(&ss));
+ #ifndef _WIN32
+         } else if (ss.ss_family == AF_UNIX) {
+-            struct sockaddr_un sun;
++            struct sockaddr_un addr;
+             const char *null;
+             size_t maxlen;
+ 
+-            memcpy(&sun, &ss, sizeof sun);
++            memcpy(&addr, &ss, sizeof addr);
+             maxlen = len - offsetof(struct sockaddr_un, sun_path);
+-            null = memchr(sun.sun_path, '\0', maxlen);
+-            ds_put_buffer(string, sun.sun_path,
+-                          null ? null - sun.sun_path : maxlen);
++            null = memchr(addr.sun_path, '\0', maxlen);
++            ds_put_buffer(string, addr.sun_path,
++                          null ? null - addr.sun_path : maxlen);
+ #endif
+         }
+ #ifdef HAVE_NETLINK
+diff --git a/lib/stream-unix.c b/lib/stream-unix.c
+index b3d70b6..a8cacc4 100644
+--- a/lib/stream-unix.c
++++ b/lib/stream-unix.c
+@@ -108,12 +108,12 @@ static int
+ punix_accept(int fd, const struct sockaddr_storage *ss, size_t ss_len,
+              struct stream **streamp)
+ {
+-    const struct sockaddr_un *sun = (const struct sockaddr_un *) ss;
++    const struct sockaddr_un *addr = (const struct sockaddr_un *) ss;
+     int name_len = get_unix_name_len(ss_len);
+     char name[128];
+ 
+     if (name_len > 0) {
+-        snprintf(name, sizeof name, "unix:%.*s", name_len, sun->sun_path);
++        snprintf(name, sizeof name, "unix:%.*s", name_len, addr->sun_path);
+     } else {
+         strcpy(name, "unix");
+     }
+diff --git a/python/ovs/socket_util.py b/python/ovs/socket_util.py
+index 1af6474..b334d7e 100644
+--- a/python/ovs/socket_util.py
++++ b/python/ovs/socket_util.py
+@@ -302,11 +302,11 @@ def set_dscp(sock, dscp):
+     try:
+         sock.setsockopt(socket.IPPROTO_IP, socket.IP_TOS, val)
+     except socket.error, e:
+-        if get_exception_errno(e) != errno.ENOPROTOOPT:
++        if get_exception_errno(e) != errno.EINVAL:
+             raise
+     success = True
+     try:
+         sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_TCLASS, val)
+     except socket.error, e:
+-        if get_exception_errno(e) != errno.ENOPROTOOPT or not success:
++        if get_exception_errno(e) != errno.EINVAL or not success:
+             raise
+diff --git a/vswitchd/automake.mk b/vswitchd/automake.mk
+index a09605f..19bf9ea 100644
+--- a/vswitchd/automake.mk
++++ b/vswitchd/automake.mk
+@@ -57,7 +57,7 @@ vswitchd/ovs-vswitchd.conf.db.5: \
+ # Version checking for vswitch.ovsschema.
+ ALL_LOCAL += vswitchd/vswitch.ovsschema.stamp
+ vswitchd/vswitch.ovsschema.stamp: vswitchd/vswitch.ovsschema
+-	@sum=`sed '/cksum/d' $? | cksum`; \
++	@sum=`sed '/cksum/d' $? | cksum | sed 's/	/ /'`; \
+ 	expected=`sed -n 's/.*"cksum": "\(.*\)".*/\1/p' $?`; \
+ 	if test "X$$sum" = "X$$expected"; then \
+ 	  touch $@; \
+diff --git a/vtep/automake.mk b/vtep/automake.mk
+index a15a067..9f8e9a9 100644
+--- a/vtep/automake.mk
++++ b/vtep/automake.mk
+@@ -59,7 +59,7 @@ $(srcdir)/vtep/vtep.5: \
+ # Version checking for vtep.ovsschema.
+ ALL_LOCAL += vtep/vtep.ovsschema.stamp
+ vtep/vtep.ovsschema.stamp: vtep/vtep.ovsschema
+-	@sum=`sed '/cksum/d' $? | cksum`; \
++	@sum=`sed '/cksum/d' $? | cksum | sed 's/	/ /'`; \
+ 	expected=`sed -n 's/.*"cksum": "\(.*\)".*/\1/p' $?`; \
+ 	if test "X$$sum" = "X$$expected"; then \
+ 	  touch $@; \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openvswitch/patches/02-bridge.patch	Mon Nov 16 16:49:19 2015 -0500
@@ -0,0 +1,155 @@
+This patch includes Solaris bridge specific changes to OVS.
+
+This patch has not been proposed upstream because we are not yet
+proposing Solaris specific requirements upstream.
+
+diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c
+index 6cd30b8..19ff759 100644
+--- a/vswitchd/bridge.c
++++ b/vswitchd/bridge.c
+@@ -943,6 +943,12 @@ port_configure(struct port *port)
+     free(s.lacp_slaves);
+ }
+ 
++static boolean_t
++bridge_is_system(const struct bridge *br)
++{
++    return (strcmp(br->type, "system") == 0);
++}
++
+ /* Pick local port hardware address and datapath ID for 'br'. */
+ static void
+ bridge_configure_datapath_id(struct bridge *br)
+@@ -954,6 +960,14 @@ bridge_configure_datapath_id(struct bridge *br)
+     char *dpid_string;
+ 
+     bridge_pick_local_hw_addr(br, ea, &hw_addr_iface);
++#ifdef __sun
++    /* Solaris "system" bridges are implicitly created VNICs. They 
++     * already have their ethernet addresses set, so there is no
++     * reason to set them. We prefer that they be "auto" generated.
++     */
++    if (bridge_is_system(br))
++	goto skip;
++#endif
+     local_iface = iface_from_ofp_port(br, OFPP_LOCAL);
+     if (local_iface) {
+         int error = netdev_set_etheraddr(local_iface->netdev, ea);
+@@ -964,6 +978,9 @@ bridge_configure_datapath_id(struct bridge *br)
+                         br->name, ovs_strerror(error));
+         }
+     }
++#ifdef __sun
++skip:
++#endif
+     memcpy(br->ea, ea, ETH_ADDR_LEN);
+ 
+     dpid = bridge_pick_datapath_id(br, ea, hw_addr_iface);
+@@ -1485,6 +1502,16 @@ iface_do_create(const struct bridge *br,
+         goto error;
+     }
+ 
++#ifdef __sun
++    if (bridge_is_system(br) && netdev_is_uplink(netdev)) {
++        VLOG_INFO("bridge %s: uplink %s", br->name, iface_cfg->name);
++        error = netdev_configure_uplink(netdev, br->name);
++        if (error) {
++            goto error;
++        }
++    }
++#endif
++
+     *ofp_portp = iface_pick_ofport(iface_cfg);
+     error = ofproto_port_add(br->ofproto, netdev, ofp_portp);
+     if (error) {
+@@ -1494,6 +1521,15 @@ iface_do_create(const struct bridge *br,
+     VLOG_INFO("bridge %s: added interface %s on port %d",
+               br->name, iface_cfg->name, *ofp_portp);
+ 
++#ifdef __sun
++    if (bridge_is_system(br) && iface_is_internal(iface_cfg, br->cfg)) {
++	error = ofproto_configure_bridge_port(br->ofproto, br->name);
++        if (error) {
++            goto error;
++        }
++    }
++#endif
++
+     if (port_cfg->vlan_mode && !strcmp(port_cfg->vlan_mode, "splinter")) {
+         netdev_turn_flags_on(netdev, NETDEV_UP, NULL);
+     }
+@@ -1620,10 +1656,31 @@ find_local_hw_addr(const struct bridge *br, uint8_t ea[ETH_ADDR_LEN],
+ {
+     struct hmapx mirror_output_ports;
+     struct port *port;
++    int i;
++    struct iface *iface = NULL;
++    uint8_t iface_ea[ETH_ADDR_LEN];
+     bool found_addr = false;
+     int error;
+-    int i;
+ 
++#ifdef __sun
++    /*
++     * Solaris does not currently support fake bridges.
++     */
++    if (bridge_is_system(br)) {
++	if (fake_br == NULL) {
++	    iface = iface_lookup(br, br->name);
++	    if (iface != NULL) {
++		error = netdev_get_etheraddr(iface->netdev, iface_ea);
++		if (error == 0) {
++		    memcpy(ea, iface_ea, ETH_ADDR_LEN);
++		    *hw_addr_iface = iface;
++		    found_addr = true;
++		}
++            }
++        }
++	goto skip;
++    }
++#endif
+     /* Mirror output ports don't participate in picking the local hardware
+      * address.  ofproto can't help us find out whether a given port is a
+      * mirror output because we haven't configured mirrors yet, so we need to
+@@ -1639,9 +1696,7 @@ find_local_hw_addr(const struct bridge *br, uint8_t ea[ETH_ADDR_LEN],
+     /* Otherwise choose the minimum non-local MAC address among all of the
+      * interfaces. */
+     HMAP_FOR_EACH (port, hmap_node, &br->ports) {
+-        uint8_t iface_ea[ETH_ADDR_LEN];
+         struct iface *candidate;
+-        struct iface *iface;
+ 
+         /* Mirror output ports don't participate. */
+         if (hmapx_contains(&mirror_output_ports, port->cfg)) {
+@@ -1708,13 +1763,15 @@ find_local_hw_addr(const struct bridge *br, uint8_t ea[ETH_ADDR_LEN],
+             found_addr = true;
+         }
+     }
++    hmapx_destroy(&mirror_output_ports);
++#ifdef __sun
++skip:
++#endif
+ 
+     if (!found_addr) {
+         memcpy(ea, br->default_ea, ETH_ADDR_LEN);
+         *hw_addr_iface = NULL;
+     }
+-
+-    hmapx_destroy(&mirror_output_ports);
+ }
+ 
+ static void
+diff --git a/vswitchd/system-stats.c b/vswitchd/system-stats.c
+index 7789787..cee0255 100644
+--- a/vswitchd/system-stats.c
++++ b/vswitchd/system-stats.c
+@@ -29,6 +29,9 @@
+ #if HAVE_SYS_STATVFS_H
+ #include <sys/statvfs.h>
+ #endif
++#ifdef __sun
++#include <sys/loadavg.h>
++#endif
+ #include <unistd.h>
+ 
+ #include "daemon.h"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openvswitch/patches/03-dpif-provider.patch	Mon Nov 16 16:49:19 2015 -0500
@@ -0,0 +1,208 @@
+This patch includes support for the Solaris dpif provider.
+
+This patch has not been proposed upstream because we are not yet
+proposing Solaris specific requirements upstream.
+
+diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
+index 78f8636..d9b18d0 100644
+--- a/lib/dpif-netdev.c
++++ b/lib/dpif-netdev.c
+@@ -2269,6 +2269,7 @@ const struct dpif_class dpif_netdev_class = {
+     dpif_netdev_recv,
+     dpif_netdev_recv_wait,
+     dpif_netdev_recv_purge,
++    NULL,
+ };
+ 
+ static void
+diff --git a/lib/dpif-provider.h b/lib/dpif-provider.h
+index 389e84e..7df0e3c 100644
+--- a/lib/dpif-provider.h
++++ b/lib/dpif-provider.h
+@@ -436,9 +436,13 @@ struct dpif_class {
+     /* Throws away any queued upcalls that 'dpif' currently has ready to
+      * return. */
+     void (*recv_purge)(struct dpif *dpif);
++
++    /* Configure port on bridge. */
++    int (*configure_bridge_port)(const struct dpif *, const char *devname);
+ };
+ 
+ extern const struct dpif_class dpif_linux_class;
++extern const struct dpif_class dpif_solaris_class;
+ extern const struct dpif_class dpif_netdev_class;
+ 
+ #ifdef  __cplusplus
+diff --git a/lib/dpif.c b/lib/dpif.c
+index 450c6c8..d5a89da 100644
+--- a/lib/dpif.c
++++ b/lib/dpif.c
+@@ -60,6 +60,9 @@ static const struct dpif_class *base_dpif_classes[] = {
+ #ifdef __linux__
+     &dpif_linux_class,
+ #endif
++#ifdef __sun
++    &dpif_solaris_class,
++#endif
+     &dpif_netdev_class,
+ };
+ 
+@@ -661,6 +664,14 @@ dpif_port_get_pid(const struct dpif *dpif, odp_port_t port_no, uint32_t hash)
+             : 0);
+ }
+ 
++int
++dpif_configure_bridge_port(const struct dpif *dpif, const char *devname)
++{
++    return (dpif->dpif_class->configure_bridge_port
++	    ? (dpif->dpif_class->configure_bridge_port)(dpif, devname)
++	    : 0);
++}
++
+ /* Looks up port number 'port_no' in 'dpif'.  On success, returns 0 and copies
+  * the port's name into the 'name_size' bytes in 'name', ensuring that the
+  * result is null-terminated.  On failure, returns a positive errno value and
+diff --git a/lib/dpif.h b/lib/dpif.h
+index f13cc36..25b4845 100644
+--- a/lib/dpif.h
++++ b/lib/dpif.h
+@@ -468,6 +468,7 @@ int dpif_port_get_name(struct dpif *, odp_port_t port_no,
+                        char *name, size_t name_size);
+ uint32_t dpif_port_get_pid(const struct dpif *, odp_port_t port_no,
+                            uint32_t hash);
++int dpif_configure_bridge_port(const struct dpif *dpif, const char *devname);
+ 
+ struct dpif_port_dump {
+     const struct dpif *dpif;
+diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
+index 46595f8..2bb200c 100644
+--- a/ofproto/ofproto-dpif.c
++++ b/ofproto/ofproto-dpif.c
+@@ -1431,7 +1431,7 @@ run(struct ofproto *ofproto_)
+ }
+ 
+ static void
+-wait(struct ofproto *ofproto_)
++dpwait(struct ofproto *ofproto_)
+ {
+     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+ 
+@@ -2839,6 +2839,17 @@ port_query_by_name(const struct ofproto *ofproto_, const char *devname,
+ }
+ 
+ static int
++configure_bridge_port(const struct ofproto *ofproto_, const char *devname)
++{
++    struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
++
++    if (!sset_contains(&ofproto->ports, devname)) {
++        return ENODEV;
++    }
++    return (dpif_configure_bridge_port(ofproto->backer->dpif, devname));
++}
++
++static int
+ port_add(struct ofproto *ofproto_, struct netdev *netdev)
+ {
+     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+@@ -4867,7 +4878,7 @@ const struct ofproto_class ofproto_dpif_class = {
+     destruct,
+     dealloc,
+     run,
+-    wait,
++    dpwait,
+     NULL,                       /* get_memory_usage. */
+     type_get_memory_usage,
+     flush,
+@@ -4934,4 +4945,5 @@ const struct ofproto_class ofproto_dpif_class = {
+     group_dealloc,              /* group_dealloc */
+     group_modify,               /* group_modify */
+     group_get_stats,            /* group_get_stats */
++    configure_bridge_port       /* configure_bridge_port */
+ };
+diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h
+index 1978a20..f743a8a 100644
+--- a/ofproto/ofproto-provider.h
++++ b/ofproto/ofproto-provider.h
+@@ -1695,6 +1695,8 @@ struct ofproto_class {
+ 
+     enum ofperr (*group_get_stats)(const struct ofgroup *,
+                                    struct ofputil_group_stats *);
++    int (*configure_bridge_port)(const struct ofproto *ofproto,
++                              const char *devname);
+ };
+ 
+ extern const struct ofproto_class ofproto_dpif_class;
+diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
+index 2048fde..2b25fe4 100644
+--- a/ofproto/ofproto.c
++++ b/ofproto/ofproto.c
+@@ -1826,6 +1826,13 @@ ofproto_port_query_by_name(const struct ofproto *ofproto, const char *devname,
+     return error;
+ }
+ 
++int
++ofproto_configure_bridge_port(const struct ofproto *ofproto,
++    const char *devname)
++{
++    return (ofproto->ofproto_class->configure_bridge_port(ofproto, devname));
++}
++
+ /* Deletes port number 'ofp_port' from the datapath for 'ofproto'.
+  * Returns 0 if successful, otherwise a positive errno. */
+ int
+diff --git a/ofproto/ofproto.h b/ofproto/ofproto.h
+index 9a06849..94dce34 100644
+--- a/ofproto/ofproto.h
++++ b/ofproto/ofproto.h
+@@ -224,6 +224,7 @@ int ofproto_port_get_stats(const struct ofport *, struct netdev_stats *stats);
+ 
+ int ofproto_port_query_by_name(const struct ofproto *, const char *devname,
+                                struct ofproto_port *);
++int ofproto_configure_bridge_port(const struct ofproto *, const char *);
+ 
+ /* Top-level configuration. */
+ uint64_t ofproto_get_datapath_id(const struct ofproto *);
+diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
+index 4f77ac5..b9f1801 100644
+--- a/ofproto/ofproto-dpif-xlate.c
++++ b/ofproto/ofproto-dpif-xlate.c
+@@ -3184,6 +3184,7 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
+     ovs_rwlock_unlock(&xlate_rwlock);
+ }
+ 
++#ifdef __linux__
+ /* Returns the maximum number of packets that the Linux kernel is willing to
+  * queue up internally to certain kinds of software-implemented ports, or the
+  * default (and rarely modified) value if it cannot be determined. */
+@@ -3236,7 +3237,9 @@ count_output_actions(const struct ofpbuf *odp_actions)
+     }
+     return n;
+ }
++#endif
+ 
++#ifdef __linux__
+ /* Returns true if 'odp_actions' contains more output actions than the datapath
+  * can reliably handle in one go.  On Linux, this is the value of the
+  * net.core.netdev_max_backlog sysctl, which limits the maximum number of
+@@ -3245,15 +3248,18 @@ count_output_actions(const struct ofpbuf *odp_actions)
+ static bool
+ too_many_output_actions(const struct ofpbuf *odp_actions)
+ {
+-#ifdef __linux__
+     return (ofpbuf_size(odp_actions) / NL_A_U32_SIZE > netdev_max_backlog()
+             && count_output_actions(odp_actions) > netdev_max_backlog());
++}
+ #else
++static bool
++too_many_output_actions(const struct ofpbuf *odp_actions OVS_UNUSED)
++{
+     /* OSes other than Linux might have similar limits, but we don't know how
+      * to determine them.*/
+     return false;
+-#endif
+ }
++#endif
+ 
+ /* Translates the 'ofpacts_len' bytes of "struct ofpacts" starting at 'ofpacts'
+  * into datapath actions in 'odp_actions', using 'ctx'.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openvswitch/patches/04-netdev-provider.patch	Mon Nov 16 16:49:19 2015 -0500
@@ -0,0 +1,136 @@
+This patch includes support for the Solaris netdev provider.
+
+This patch has not been proposed upstream because we are not yet
+proposing Solaris specific requirements upstream.
+
+diff --git a/lib/netdev-dummy.c b/lib/netdev-dummy.c
+index 501fb82..1051f08 100644
+--- a/lib/netdev-dummy.c
++++ b/lib/netdev-dummy.c
+@@ -1043,7 +1043,10 @@ static const struct netdev_class dummy_class = {
+     NULL,                       /* arp_lookup */
+ 
+     netdev_dummy_update_flags,
+-
++#ifdef __sun
++    NULL,
++    NULL,
++#endif
+     netdev_dummy_rxq_alloc,
+     netdev_dummy_rxq_construct,
+     netdev_dummy_rxq_destruct,
+diff --git a/lib/netdev-provider.h b/lib/netdev-provider.h
+index 37b9da3..bcec96a 100644
+--- a/lib/netdev-provider.h
++++ b/lib/netdev-provider.h
+@@ -647,6 +647,14 @@ struct netdev_class {
+     int (*update_flags)(struct netdev *netdev, enum netdev_flags off,
+                         enum netdev_flags on, enum netdev_flags *old_flags);
+ 
++
++    /* Returns true if netdev represents uplink for the switch */
++    int (*configure_uplink)(const struct netdev *netdev,
++                            const char *brname);
++
++    /* Returns true if netdev represents uplink for the switch */
++    bool (*is_uplink)(const struct netdev *netdev);
++
+ /* ## -------------------- ## */
+ /* ## netdev_rxq Functions ## */
+ /* ## -------------------- ## */
+@@ -692,6 +700,9 @@ extern const struct netdev_class netdev_tap_class;
+ #if defined(__FreeBSD__) || defined(__NetBSD__)
+ extern const struct netdev_class netdev_bsd_class;
+ #endif
++#ifdef __sun
++extern const struct netdev_class netdev_solaris_class;
++#endif
+ 
+ #ifdef  __cplusplus
+ }
+diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c
+index a676784..54c9e0b 100644
+--- a/lib/netdev-vport.c
++++ b/lib/netdev-vport.c
+@@ -814,7 +814,9 @@ get_stats(const struct netdev *netdev, struct netdev_stats *stats)
+     NULL,                   /* rx_dealloc */                \
+     NULL,                   /* rx_recv */                   \
+     NULL,                   /* rx_wait */                   \
+-    NULL,                   /* rx_drain */
++    NULL,                   /* rx_drain */		    \
++    NULL,                   /* configure_uplink */	    \
++    NULL                    /* is_uplink */
+ 
+ #define TUNNEL_CLASS(NAME, DPIF_PORT)                       \
+     { DPIF_PORT,                                            \
+@@ -827,12 +829,14 @@ void
+ netdev_vport_tunnel_register(void)
+ {
+     static const struct vport_class vport_classes[] = {
++#ifndef __sun
+         TUNNEL_CLASS("gre", "gre_system"),
+         TUNNEL_CLASS("ipsec_gre", "gre_system"),
+         TUNNEL_CLASS("gre64", "gre64_system"),
+         TUNNEL_CLASS("ipsec_gre64", "gre64_system"),
+-        TUNNEL_CLASS("vxlan", "vxlan_system"),
+-        TUNNEL_CLASS("lisp", "lisp_system")
++        TUNNEL_CLASS("lisp", "lisp_system"),
++#endif
++        TUNNEL_CLASS("vxlan", "vxlan_system")
+     };
+     static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
+ 
+diff --git a/lib/netdev.c b/lib/netdev.c
+index 5bf2220..c59a308 100644
+--- a/lib/netdev.c
++++ b/lib/netdev.c
+@@ -135,6 +135,11 @@ netdev_initialize(void)
+         netdev_register_provider(&netdev_tap_class);
+         netdev_register_provider(&netdev_bsd_class);
+ #endif
++#ifdef __sun
++	netdev_register_provider(&netdev_solaris_class);
++	netdev_register_provider(&netdev_internal_class);
++        netdev_vport_tunnel_register();
++#endif
+         netdev_dpdk_register();
+ 
+         ovsthread_once_done(&once);
+@@ -684,6 +689,23 @@ netdev_get_etheraddr(const struct netdev *netdev, uint8_t mac[ETH_ADDR_LEN])
+     return netdev->netdev_class->get_etheraddr(netdev, mac);
+ }
+ 
++int
++netdev_configure_uplink(const struct netdev *netdev, const char *brname)
++{
++    const struct netdev_class *class = netdev->netdev_class;
++
++    return (class->configure_uplink
++           ? class->configure_uplink(netdev, brname) : 0);
++}
++
++bool
++netdev_is_uplink(const struct netdev *netdev)
++{
++    const struct netdev_class *class = netdev->netdev_class;
++
++    return(class->is_uplink ? class->is_uplink(netdev) : false);
++}
++
+ /* Returns the name of the network device that 'netdev' represents,
+  * e.g. "eth0".  The caller must not modify or free the returned string. */
+ const char *
+diff --git a/lib/netdev.h b/lib/netdev.h
+index 7cb3c80..9b663b7 100644
+--- a/lib/netdev.h
++++ b/lib/netdev.h
+@@ -177,6 +177,9 @@ void netdev_send_wait(struct netdev *);
+ int netdev_set_etheraddr(struct netdev *, const uint8_t mac[6]);
+ int netdev_get_etheraddr(const struct netdev *, uint8_t mac[6]);
+ 
++int netdev_configure_uplink(const struct netdev *, const char *);
++bool netdev_is_uplink(const struct netdev *);
++
+ /* PHY interface. */
+ bool netdev_get_carrier(const struct netdev *);
+ long long int netdev_get_carrier_resets(const struct netdev *);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openvswitch/patches/05-usage.patch	Mon Nov 16 16:49:19 2015 -0500
@@ -0,0 +1,1367 @@
+This patch updates usage and man pages.
+
+This patch has not been proposed upstream because we are not yet
+proposing Solaris specific requirements upstream.
+
+diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in
+index 5b0c52e..6a2b0a1 100644
+--- a/utilities/ovs-ofctl.8.in
++++ b/utilities/ovs-ofctl.8.in
+@@ -96,22 +96,11 @@ may be any one of the following:
+ Enable or disable the interface.  This is equivalent to \fBifconfig
+ up\fR or \fBifconfig down\fR on a Unix system.
+ .
+-.IP \fBstp\fR
+-.IQ \fBno\-stp\fR
+-Enable or disable 802.1D spanning tree protocol (STP) on the
+-interface.  OpenFlow implementations that don't support STP will
+-refuse to enable it.
+-.
+ .IP \fBreceive\fR
+ .IQ \fBno\-receive\fR
+-.IQ \fBreceive\-stp\fR
+-.IQ \fBno\-receive\-stp\fR
+ Enable or disable OpenFlow processing of packets received on this
+ interface.  When packet processing is disabled, packets will be
+-dropped instead of being processed through the OpenFlow table.  The
+-\fBreceive\fR or \fBno\-receive\fR setting applies to all packets
+-except 802.1D spanning tree packets, which are separately controlled
+-by \fBreceive\-stp\fR or \fBno\-receive\-stp\fR.
++dropped instead of being processed through the OpenFlow table.
+ .
+ .IP \fBforward\fR
+ .IQ \fBno\-forward\fR
+@@ -669,15 +658,6 @@ When \fBdl_type\fR is wildcarded or set to a value other than 0x0800 or
+ 0x86dd, the value of \fBip_dscp\fR is ignored (see \fBFlow Syntax\fR
+ above).
+ .
+-.IP \fBnw_ecn=\fIecn\fR
+-.IQ \fBip_ecn=\fIecn\fR
+-Matches \fIecn\fR bits in IP ToS or IPv6 traffic class fields, which is
+-specified as a decimal number between 0 and 3, inclusive.
+-.IP
+-When \fBdl_type\fR is wildcarded or set to a value other than 0x0800 or
+-0x86dd, the value of \fBnw_ecn\fR is ignored (see \fBFlow Syntax\fR
+-above).
+-.
+ .IP \fBnw_ttl=\fIttl\fR
+ Matches IP TTL or IPv6 hop limit value \fIttl\fR, which is
+ specified as a decimal number between 0 and 255, inclusive.
+@@ -760,48 +740,6 @@ Like the exact-match forms of \fBtp_src\fR and \fBtp_dst\fR described
+ above, the bitwise match forms apply only when \fBdl_type\fR and
+ \fBnw_proto\fR specify TCP or UDP or SCTP.
+ .
+-.IP \fBtcp_flags=\fIflags\fB/\fImask\fR
+-.IQ \fBtcp_flags=\fR[\fB+\fIflag\fR...][\fB-\fIflag\fR...]
+-Bitwise match on TCP flags.  The \fIflags\fR and \fImask\fR are 16-bit
+-numbers written in decimal or in hexadecimal prefixed by \fB0x\fR.
+-Each 1-bit in \fImask\fR requires that the corresponding bit in
+-\fIflags\fR must match.  Each 0-bit in \fImask\fR causes the corresponding
+-bit to be ignored.
+-.IP
+-Alternatively, the flags can be specified by their symbolic names
+-(listed below), each preceded by either \fB+\fR for a flag that must
+-be set, or \fB\-\fR for a flag that must be unset, without any other
+-delimiters between the flags.  Flags not mentioned are wildcarded.
+-For example, \fBtcp,tcp_flags=+syn\-ack\fR matches TCP SYNs that are
+-not ACKs.
+-.IP
+-TCP protocol currently defines 9 flag bits, and additional 3 bits are
+-reserved (must be transmitted as zero), see RFCs 793, 3168, and 3540.
+-The flag bits are, numbering from the least significant bit:
+-.RS
+-.IP "\fB0: fin\fR"
+-No more data from sender.
+-.IP "\fB1: syn\fR"
+-Synchronize sequence numbers.
+-.IP "\fB2: rst\fR"
+-Reset the connection.
+-.IP "\fB3: psh\fR"
+-Push function.
+-.IP "\fB4: ack\fR"
+-Acknowledgement field significant.
+-.IP "\fB5: urg\fR"
+-Urgent pointer field significant.
+-.IP "\fB6: ece\fR"
+-ECN Echo.
+-.IP "\fB7: cwr\fR"
+-Congestion Windows Reduced.
+-.IP "\fB8: ns\fR"
+-Nonce Sum.
+-.IP "\fB9-11:\fR"
+-Reserved.
+-.IP "\fB12-15:\fR"
+-Not matchable, must be zero.
+-.RE
+ .IP \fBicmp_type=\fItype\fR
+ .IQ \fBicmp_code=\fIcode\fR
+ When \fBdl_type\fR and \fBnw_proto\fR specify ICMP or ICMPv6, \fItype\fR
+@@ -943,27 +881,6 @@ Match packets with no 802.1Q header or tagged with VLAN 0 and priority
+ Some of these matching possibilities can also be achieved with
+ \fBdl_vlan\fR and \fBdl_vlan_pcp\fR.
+ .
+-.IP \fBip_frag=\fIfrag_type\fR
+-When \fBdl_type\fR specifies IP or IPv6, \fIfrag_type\fR
+-specifies what kind of IP fragments or non-fragments to match.  The
+-following values of \fIfrag_type\fR are supported:
+-.RS
+-.IP "\fBno\fR"
+-Matches only non-fragmented packets.
+-.IP "\fByes\fR"
+-Matches all fragments.
+-.IP "\fBfirst\fR"
+-Matches only fragments with offset 0.
+-.IP "\fBlater\fR"
+-Matches only fragments with nonzero offset.
+-.IP "\fBnot_later\fR"
+-Matches non-fragmented packets and fragments with zero offset.
+-.RE
+-.IP
+-The \fBip_frag\fR match type is likely to be most useful in
+-\fBnx\-match\fR mode.  See the description of the \fBset\-frags\fR
+-command, above, for more details.
+-.
+ .IP \fBarp_spa=\fIip\fR[\fB/\fInetmask\fR]
+ .IQ \fBarp_tpa=\fIip\fR[\fB/\fInetmask\fR]
+ When \fBdl_type\fR specifies either ARP or RARP, \fBarp_spa\fR and
+@@ -989,7 +906,6 @@ When \fBdl_type\fR specifies either ARP or RARP, \fBarp_sha\fR and
+ address is specified as 6 pairs of hexadecimal digits delimited by colons
+ (e.g. \fB00:0A:E4:25:6B:B0\fR), with a wildcard mask following the slash.
+ .
+-
+ .IP \fBipv6_src=\fIipv6\fR[\fB/\fInetmask\fR]
+ .IQ \fBipv6_dst=\fIipv6\fR[\fB/\fInetmask\fR]
+ When \fBdl_type\fR is 0x86dd (possibly via shorthand, e.g., \fBipv6\fR
+@@ -1006,73 +922,6 @@ and later support arbitrary masks; earlier versions support only CIDR
+ masks, that is, CIDR block and IPv6 addresses that are equivalent to
+ CIDR blocks.
+ .
+-.IP \fBipv6_label=\fIlabel\fR
+-When \fBdl_type\fR is 0x86dd (possibly via shorthand, e.g., \fBipv6\fR
+-or \fBtcp6\fR), matches IPv6 flow label \fIlabel\fR.
+-.
+-.IP \fBnd_target=\fIipv6\fR[\fB/\fInetmask\fR]
+-When \fBdl_type\fR, \fBnw_proto\fR, and \fBicmp_type\fR specify
+-IPv6 Neighbor Discovery (ICMPv6 type 135 or 136), matches the target address
+-\fIipv6\fR.  \fIipv6\fR is in the same format described earlier for the
+-\fBipv6_src\fR and \fBipv6_dst\fR fields.
+-.
+-.IP \fBnd_sll=\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fR
+-When \fBdl_type\fR, \fBnw_proto\fR, and \fBicmp_type\fR specify IPv6
+-Neighbor Solicitation (ICMPv6 type 135), matches the source link\-layer
+-address option.  An address is specified as 6 pairs of hexadecimal
+-digits delimited by colons.
+-.
+-.IP \fBnd_tll=\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fR
+-When \fBdl_type\fR, \fBnw_proto\fR, and \fBicmp_type\fR specify IPv6
+-Neighbor Advertisement (ICMPv6 type 136), matches the target link\-layer
+-address option.  An address is specified as 6 pairs of hexadecimal
+-digits delimited by colons.
+-.
+-.IP \fBmpls_bos=\fIbos\fR
+-When \fBdl_type\fR is 0x8847 or 0x8848 (possibly via shorthand e.g.,
+-\fBmpls\fR or \fBmplsm\fR), matches the bottom-of-stack bit of the
+-outer-most MPLS label stack entry. Valid values are 0 and 1.
+-.IP
+-If 1 then for a packet with a well-formed MPLS label stack the
+-bottom-of-stack bit indicates that the outer label stack entry is also
+-the inner-most label stack entry and thus that is that there is only one
+-label stack entry present.  Conversely, if 0 then for a packet with a
+-well-formed MPLS label stack the bottom-of-stack bit indicates that the
+-outer label stack entry is not the inner-most label stack entry and
+-thus there is more than one label stack entry present.
+-.
+-.IP \fBmpls_label=\fIlabel\fR
+-When \fBdl_type\fR is 0x8847 or 0x8848 (possibly via shorthand e.g.,
+-\fBmpls\fR or \fBmplsm\fR), matches the label of the outer
+-MPLS label stack entry. The label is a 20-bit value that is decimal by default;
+-use a \fB0x\fR prefix to specify them in hexadecimal.
+-.
+-.IP \fBmpls_tc=\fItc\fR
+-When \fBdl_type\fR is 0x8847 or 0x8848 (possibly via shorthand e.g.,
+-\fBmpls\fR or \fBmplsm\fR), matches the traffic-class of the outer
+-MPLS label stack entry. Valid values are between 0 (lowest) and 7 (highest).
+-.
+-.IP \fBtun_id=\fItunnel-id\fR[\fB/\fImask\fR]
+-.IQ \fBtunnel_id=\fItunnel-id\fR[\fB/\fImask\fR]
+-Matches tunnel identifier \fItunnel-id\fR.  Only packets that arrive
+-over a tunnel that carries a key (e.g. GRE with the RFC 2890 key
+-extension and a nonzero key value) will have a nonzero tunnel ID.
+-If \fImask\fR is omitted, \fItunnel-id\fR is the exact tunnel ID to match;
+-if \fImask\fR is specified, then a 1-bit in \fImask\fR indicates that the
+-corresponding bit in \fItunnel-id\fR must match exactly, and a 0-bit
+-wildcards that bit.
+-.
+-.IP \fBtun_src=\fIip\fR[\fB/\fInetmask\fR]
+-.IQ \fBtun_dst=\fIip\fR[\fB/\fInetmask\fR]
+-Matches tunnel IPv4 source (or destination) address \fIip\fR. Only packets
+-that arrive over a tunnel will have nonzero tunnel addresses.
+-The address may be specified as an IP address or host name
+-(e.g. \fB192.168.1.1\fR or \fBwww.example.com\fR).  The optional
+-\fInetmask\fR allows restricting a match to a masked IPv4 address.
+-The netmask may be specified as a dotted quad
+-(e.g. \fB192.168.1.0/255.255.255.0\fR) or as a CIDR block
+-(e.g. \fB192.168.1.0/24\fR).
+-.
+ .IP "\fBreg\fIidx\fB=\fIvalue\fR[\fB/\fImask\fR]"
+ Matches \fIvalue\fR either exactly or with optional \fImask\fR in
+ register number \fIidx\fR.  The valid range of \fIidx\fR depends on
+@@ -1085,13 +934,6 @@ exactly, and a 0-bit wildcards that bit.
+ When a packet enters an OpenFlow switch, all of the registers are set
+ to 0.  Only explicit Nicira extension actions change register values.
+ .
+-.IP \fBpkt_mark=\fIvalue\fR[\fB/\fImask\fR]
+-Matches packet metadata mark \fIvalue\fR either exactly or with optional
+-\fImask\fR. The mark is associated data that may be passed into other
+-system components in order to facilitate interaction between subsystems.
+-On Linux this corresponds to the skb mark but the exact implementation is
+-platform-dependent.
+-.
+ .PP
+ Defining IPv6 flows (those with \fBdl_type\fR equal to 0x86dd) requires
+ support for NXM.  The following shorthand notations are available for
+@@ -1212,12 +1054,6 @@ as necessary to match the value specified.  If the VLAN tag is added,
+ a priority of zero is used (see the \fBmod_vlan_pcp\fR action to set
+ this).
+ .
+-.IP \fBmod_vlan_pcp\fR:\fIvlan_pcp\fR
+-Modifies the VLAN priority on a packet.  The VLAN tag is added or modified 
+-as necessary to match the value specified.  Valid values are between 0
+-(lowest) and 7 (highest).  If the VLAN tag is added, a vid of zero is used 
+-(see the \fBmod_vlan_vid\fR action to set this).
+-.
+ .IP \fBstrip_vlan\fR
+ Strips the VLAN tag from a packet if it is present.
+ .
+@@ -1227,31 +1063,6 @@ for the tag. Only ethertype 0x8100 should be used. (0x88a8 which the spec
+ allows isn't supported at the moment.)
+ A priority of zero and the tag of zero are used for the new tag.
+ .
+-.IP \fBpush_mpls\fR:\fIethertype\fR
+-Changes the packet's Ethertype to \fIethertype\fR, which must be either
+-\fB0x8847\fR or \fB0x8848\fR, and pushes an MPLS LSE.
+-.IP
+-If the packet does not already contain any MPLS labels then an initial
+-label stack entry is pushed.  The label stack entry's label is 2 if the
+-packet contains IPv6 and 0 otherwise, its default traffic control value is
+-the low 3 bits of the packet's DSCP value (0 if the packet is not IP), and
+-its TTL is copied from the IP TTL (64 if the packet is not IP).
+-.IP
+-If the packet does already contain an MPLS label, pushes a new
+-outermost label as a copy of the existing outermost label.
+-.IP
+-A limitation of the implementation is that processing of actions will stop
+-if \fBpush_mpls\fR follows another \fBpush_mpls\fR unless there is a
+-\fBpop_mpls\fR in between.
+-.
+-.IP \fBpop_mpls\fR:\fIethertype\fR
+-Strips the outermost MPLS label stack entry.
+-Currently the implementation restricts \fIethertype\fR to a non-MPLS Ethertype
+-and thus \fBpop_mpls\fR should only be applied to packets with
+-an MPLS label stack depth of one. A further limitation is that processing of
+-actions will stop if \fBpop_mpls\fR follows another \fBpop_mpls\fR unless
+-there is a \fBpush_mpls\fR in between.
+-.
+ .IP \fBmod_dl_src\fB:\fImac\fR
+ Sets the source Ethernet address to \fImac\fR.
+ .
+@@ -1275,13 +1086,6 @@ Sets the DSCP bits in the IPv4 ToS/DSCP or IPv6 traffic class field to
+ \fItos\fR, which must be a multiple of 4 between 0 and 255.  This action
+ does not modify the two least significant bits of the ToS field (the ECN bits).
+ .
+-.IP \fBmod_nw_ecn\fB:\fIecn\fR
+-Sets the ECN bits in the IPv4 ToS or IPv6 traffic class field to \fIecn\fR,
+-which must be a value between 0 and 3, inclusive.  This action does not modify
+-the six most significant bits of the field (the DSCP bits).
+-.IP
+-Requires OpenFlow 1.1 or later.
+-.
+ .IP \fBmod_nw_ttl\fB:\fIttl\fR
+ Sets the IPv4 TTL or IPv6 hop limit field to \fIttl\fR, which is specified as
+ a decimal number between 0 and 255, inclusive.  Switch behavior when setting
+@@ -1325,45 +1129,6 @@ OpenFlow implementations do not support queuing at all.
+ Restores the queue to the value it was before any \fBset_queue\fR
+ actions were applied.
+ .
+-.IP \fBdec_ttl\fR
+-.IQ \fBdec_ttl\fB[\fR(\fIid1,id2\fI)\fR]\fR
+-Decrement TTL of IPv4 packet or hop limit of IPv6 packet.  If the
+-TTL or hop limit is initially zero or decrementing would make it so, no
+-decrement occurs, as packets reaching TTL zero must be rejected.  Instead,
+-a ``packet-in'' message with reason code \fBOFPR_INVALID_TTL\fR is
+-sent to each connected controller that has enabled receiving them,
+-if any.  Processing the current set of actions then stops.  However,
+-if the current set of actions was reached through ``resubmit'' then
+-remaining actions in outer levels resume processing.  This action
+-also optionally supports the ability to specify a list of valid
+-controller ids.  Each of controllers in the list will receive the
+-``packet_in'' message only if they have registered to receive the
+-invalid ttl packets.  If controller ids are not specified, the
+-``packet_in'' message will be sent only to the controllers having
+-controller id zero which have registered for the invalid ttl packets.
+-.
+-.IP \fBset_mpls_label\fR:\fIlabel\fR
+-Set the label of the outer MPLS label stack entry of a packet.
+-\fIlabel\fR should be a 20-bit value that is decimal by default;
+-use a \fB0x\fR prefix to specify them in hexadecimal.
+-.
+-.IP \fBset_mpls_tc\fR:\fItc\fR
+-Set the traffic-class of the outer MPLS label stack entry of a packet.
+-\fItc\fR should be a in the range 0 to 7 inclusive.
+-.
+-.IP \fBset_mpls_ttl\fR:\fIttl\fR
+-Set the TTL of the outer MPLS label stack entry of a packet.
+-\fIttl\fR should be in the range 0 to 255 inclusive.
+-.
+-.IP \fBdec_mpls_ttl\fR
+-Decrement TTL of the outer MPLS label stack entry of a packet.  If the TTL
+-is initially zero or decrementing would make it so, no decrement occurs.
+-Instead, a ``packet-in'' message with reason code \fBOFPR_INVALID_TTL\fR
+-is sent to the main controller (id zero), if it has enabled receiving them.
+-Processing the current set of actions then stops.  However, if the current
+-set of actions was reached through ``resubmit'' then remaining actions in
+-outer levels resume processing.
+-.
+ .IP \fBnote:\fR[\fIhh\fR]...
+ Does nothing at all.  Any number of bytes represented as hex digits
+ \fIhh\fR may be included.  Pairs of hex digits may be separated by
+@@ -1373,22 +1138,6 @@ payload, so the provided bytes will be padded on the right by enough
+ bytes with value 0 to make the total number 6 more than a multiple of
+ 8.
+ .
+-.IP "\fBmove:\fIsrc\fB[\fIstart\fB..\fIend\fB]\->\fIdst\fB[\fIstart\fB..\fIend\fB]\fR"
+-Copies the named bits from field \fIsrc\fR to field \fIdst\fR.
+-\fIsrc\fR and \fIdst\fR must be NXM field names as defined in
+-\fBnicira\-ext.h\fR, e.g. \fBNXM_OF_UDP_SRC\fR or \fBNXM_NX_REG0\fR.
+-Each \fIstart\fR and \fIend\fR pair, which are inclusive, must specify
+-the same number of bits and must fit within its respective field.
+-Shorthands for \fB[\fIstart\fB..\fIend\fB]\fR exist: use
+-\fB[\fIbit\fB]\fR to specify a single bit or \fB[]\fR to specify an
+-entire field.
+-.IP
+-Examples: \fBmove:NXM_NX_REG0[0..5]\->NXM_NX_REG1[26..31]\fR copies the
+-six bits numbered 0 through 5, inclusive, in register 0 into bits 26
+-through 31, inclusive;
+-\fBmove:NXM_NX_REG0[0..15]\->NXM_OF_VLAN_TCI[]\fR copies the least
+-significant 16 bits of register 0 into the VLAN TCI field.
+-.
+ .IP "\fBload:\fIvalue\fB\->\fIdst\fB[\fIstart\fB..\fIend\fB]"
+ Writes \fIvalue\fR to bits \fIstart\fR through \fIend\fR, inclusive,
+ in field \fIdst\fR.
+@@ -1396,23 +1145,6 @@ in field \fIdst\fR.
+ Example: \fBload:55\->NXM_NX_REG2[0..5]\fR loads value 55 (bit pattern
+ \fB110111\fR) into bits 0 through 5, inclusive, in register 2.
+ .
+-.IP "\fBpush:\fIsrc\fB[\fIstart\fB..\fIend\fB]"
+-Pushes \fIstart\fR to \fIend\fR bits inclusive, in fields
+-on top of the stack.
+-.IP
+-Example: \fBpush:NXM_NX_REG2[0..5]\fR push the value stored in register
+-2 bits 0 through 5, inclusive, on to the internal stack.
+-.
+-.IP "\fBpop:\fIdst\fB[\fIstart\fB..\fIend\fB]"
+-Pops from the top of the stack, retrieves the \fIstart\fR to \fIend\fR bits
+-inclusive, from the value popped and store them into the corresponding
+-bits in \fIdst\fR.
+-.
+-.IP
+-Example: \fBpop:NXM_NX_REG2[0..5]\fR pops the value from top of the stack.
+-Set register 2 bits 0 through 5, inclusive, based on bits 0 through 5 from the
+-value just popped.
+-.
+ .IP "\fBset_field:\fIvalue\fB\->\fIdst"
+ Writes the literal \fIvalue\fR into the field \fIdst\fR, which should
+ be specified as a name used for matching.  (This is similar to
+@@ -1422,50 +1154,6 @@ OpenFlow 1.2 and above.)
+ .IP
+ Example: \fBset_field:00:11:22:33:44:55->eth_src\fR.
+ .
+-.IP "\fBmultipath(\fIfields\fB, \fIbasis\fB, \fIalgorithm\fB, \fIn_links\fB, \fIarg\fB, \fIdst\fB[\fIstart\fB..\fIend\fB])\fR"
+-Hashes \fIfields\fR using \fIbasis\fR as a universal hash parameter,
+-then the applies multipath link selection \fIalgorithm\fR (with
+-parameter \fIarg\fR) to choose one of \fIn_links\fR output links
+-numbered 0 through \fIn_links\fR minus 1, and stores the link into
+-\fIdst\fB[\fIstart\fB..\fIend\fB]\fR, which must be an NXM field as
+-described above.
+-.IP
+-Currently, \fIfields\fR must be either \fBeth_src\fR or
+-\fBsymmetric_l4\fR and \fIalgorithm\fR must be one of \fBmodulo_n\fR,
+-\fBhash_threshold\fR, \fBhrw\fR, and \fBiter_hash\fR.  Only
+-the \fBiter_hash\fR algorithm uses \fIarg\fR.
+-.IP
+-Refer to \fBnicira\-ext.h\fR for more details.
+-.
+-.IP "\fBbundle(\fIfields\fB, \fIbasis\fB, \fIalgorithm\fB, \fIslave_type\fB, slaves:[\fIs1\fB, \fIs2\fB, ...])\fR"
+-Hashes \fIfields\fR using \fIbasis\fR as a universal hash parameter, then
+-applies the bundle link selection \fIalgorithm\fR to choose one of the listed
+-slaves represented as \fIslave_type\fR.  Currently the only supported
+-\fIslave_type\fR is \fBofport\fR.  Thus, each \fIs1\fR through \fIsN\fR should
+-be an OpenFlow port number. Outputs to the selected slave.
+-.IP
+-Currently, \fIfields\fR must be either \fBeth_src\fR or \fBsymmetric_l4\fR and
+-\fIalgorithm\fR must be one of \fBhrw\fR and \fBactive_backup\fR.
+-.IP
+-Example: \fBbundle(eth_src,0,hrw,ofport,slaves:4,8)\fR uses an Ethernet source
+-hash with basis 0, to select between OpenFlow ports 4 and 8 using the Highest
+-Random Weight algorithm.
+-.IP
+-Refer to \fBnicira\-ext.h\fR for more details.
+-.
+-.IP "\fBbundle_load(\fIfields\fB, \fIbasis\fB, \fIalgorithm\fB, \fIslave_type\fB, \fIdst\fB[\fIstart\fB..\fIend\fB], slaves:[\fIs1\fB, \fIs2\fB, ...])\fR"
+-Has the same behavior as the \fBbundle\fR action, with one exception.  Instead
+-of outputting to the selected slave, it writes its selection to
+-\fIdst\fB[\fIstart\fB..\fIend\fB]\fR, which must be an NXM field as described
+-above.
+-.IP
+-Example: \fBbundle_load(eth_src, 0, hrw, ofport, NXM_NX_REG0[],
+-slaves:4, 8)\fR uses an Ethernet source hash with basis 0, to select
+-between OpenFlow ports 4 and 8 using the Highest Random Weight
+-algorithm, and writes the selection to \fBNXM_NX_REG0[]\fR.
+-.IP
+-Refer to \fBnicira\-ext.h\fR for more details.
+-.
+ .IP "\fBlearn(\fIargument\fR[\fB,\fIargument\fR]...\fB)\fR"
+ This action adds or modifies a flow in an OpenFlow table, similar to
+ \fBovs\-ofctl \-\-strict mod\-flows\fR.  The arguments specify the
+@@ -1559,21 +1247,11 @@ the action set, the one written later replaces the earlier action:
+ .RS
+ .IP 1.
+ \fBstrip_vlan\fR
+-.IQ
+-\fBpop_mpls\fR
+ .
+ .IP 2.
+-\fBpush_mpls\fR
+-.
+-.IP 3.
+ \fBpush_vlan\fR
+ .
+-.IP 4.
+-\fBdec_ttl\fR
+-.IQ
+-\fBdec_mpls_ttl\fR
+-.
+-.IP 5.
++.IP 3.
+ \fBload\fR
+ .IQ
+ \fBmod_dl_dst\fR
+@@ -1586,8 +1264,6 @@ the action set, the one written later replaces the earlier action:
+ .IQ
+ \fBmod_nw_tos\fR
+ .IQ
+-\fBmod_nw_ecn\fR
+-.IQ
+ \fBmod_nw_ttl\fR
+ .IQ
+ \fBmod_tp_dst\fR
+@@ -1610,10 +1286,10 @@ part of a field, the later modification takes effect, and when they
+ modify different parts of a field (or different fields), then both
+ modifications are applied.
+ .
+-.IP 6.
++.IP 4.
+ \fBset_queue\fR
+ .
+-.IP 7.
++.IP 5.
+ \fBgroup\fR
+ .IQ
+ \fBoutput\fR
+@@ -1627,14 +1303,6 @@ anywhere and thus the modifications are not visible.)
+ .IP
+ Only the actions listed above may be written to the action set.
+ .
+-.IP \fBwrite_metadata\fB:\fIvalue\fR[/\fImask\fR]
+-Updates the metadata field for the flow. If \fImask\fR is omitted, the
+-metadata field is set exactly to \fIvalue\fR; if \fImask\fR is specified, then
+-a 1-bit in \fImask\fR indicates that the corresponding bit in the metadata
+-field will be replaced with the corresponding bit from \fIvalue\fR. Both
+-\fIvalue\fR and \fImask\fR are 64-bit values that are decimal by default; use
+-a \fB0x\fR prefix to specify them in hexadecimal.
+-.
+ .IP \fBmeter\fR:\fImeter_id\fR
+ Apply the \fImeter_id\fR before any other actions. If a meter band rate is
+ exceeded, the packet may be dropped, or modified, depending on the meter
+@@ -1644,94 +1312,6 @@ for more details.
+ .IP \fBgoto_table\fR:\fItable\fR
+ Indicates the next table in the process pipeline.
+ .
+-.IP "\fBfin_timeout(\fIargument\fR[\fB,\fIargument\fR]\fB)"
+-This action changes the idle timeout or hard timeout, or both, of this
+-OpenFlow rule when the rule matches a TCP packet with the FIN or RST
+-flag.  When such a packet is observed, the action reduces the rule's
+-timeouts to those specified on the action.  If the rule's existing
+-timeout is already shorter than the one that the action specifies,
+-then that timeout is unaffected.
+-.IP
+-\fIargument\fR takes the following forms:
+-.RS
+-.IP "\fBidle_timeout=\fIseconds\fR"
+-Causes the flow to expire after the given number of seconds of
+-inactivity.
+-.
+-.IP "\fBhard_timeout=\fIseconds\fR"
+-Causes the flow to expire after the given number of seconds,
+-regardless of activity.  (\fIseconds\fR specifies time since the
+-flow's creation, not since the receipt of the FIN or RST.)
+-.RE
+-.IP
+-This action was added in Open vSwitch 1.5.90.
+-.
+-.IP "\fBsample(\fIargument\fR[\fB,\fIargument\fR]...\fB)\fR"
+-Samples packets and sends one sample for every sampled packet.
+-.IP
+-\fIargument\fR takes the following forms:
+-.RS
+-.IP "\fBprobability=\fIpackets\fR"
+-The number of sampled packets out of 65535.  Must be greater or equal to 1.
+-.IP "\fBcollector_set_id=\fIid\fR"
+-The unsigned 32-bit integer identifier of the set of sample collectors
+-to send sampled packets to.  Defaults to 0.
+-.IP "\fBobs_domain_id=\fIid\fR"
+-When sending samples to IPFIX collectors, the unsigned 32-bit integer
+-Observation Domain ID sent in every IPFIX flow record.  Defaults to 0.
+-.IP "\fBobs_point_id=\fIid\fR"
+-When sending samples to IPFIX collectors, the unsigned 32-bit integer
+-Observation Point ID sent in every IPFIX flow record.  Defaults to 0.
+-.RE
+-.IP
+-Refer to \fBovs\-vswitchd.conf.db\fR(8) for more details on
+-configuring sample collector sets.
+-.IP
+-This action was added in Open vSwitch 1.10.90.
+-.
+-.IP "\fBexit\fR"
+-This action causes Open vSwitch to immediately halt execution of
+-further actions.  Those actions which have already been executed are
+-unaffected.  Any further actions, including those which may be in
+-other tables, or different levels of the \fBresubmit\fR call stack,
+-are ignored.  Actions in the action set is still executed (specify
+-\fBclear_actions\fR before \fBexit\fR to discard them).
+-.RE
+-.
+-.PP
+-An opaque identifier called a cookie can be used as a handle to identify
+-a set of flows:
+-.
+-.IP \fBcookie=\fIvalue\fR
+-.
+-A cookie can be associated with a flow using the \fBadd\-flow\fR,
+-\fBadd\-flows\fR, and \fBmod\-flows\fR commands.  \fIvalue\fR can be any
+-64-bit number and need not be unique among flows.  If this field is
+-omitted, a default cookie value of 0 is used.
+-.
+-.IP \fBcookie=\fIvalue\fR\fB/\fImask\fR
+-.
+-When using NXM, the cookie can be used as a handle for querying,
+-modifying, and deleting flows.  \fIvalue\fR and \fImask\fR may be
+-supplied for the \fBdel\-flows\fR, \fBmod\-flows\fR, \fBdump\-flows\fR, and
+-\fBdump\-aggregate\fR commands to limit matching cookies.  A 1-bit in
+-\fImask\fR indicates that the corresponding bit in \fIcookie\fR must
+-match exactly, and a 0-bit wildcards that bit.  A mask of \-1 may be used
+-to exactly match a cookie.
+-.IP
+-The \fBmod\-flows\fR command can update the cookies of flows that
+-match a cookie by specifying the \fIcookie\fR field twice (once with a
+-mask for matching and once without to indicate the new value):
+-.RS
+-.IP "\fBovs\-ofctl mod\-flows br0 cookie=1,actions=normal\fR"
+-Change all flows' cookies to 1 and change their actions to \fBnormal\fR.
+-.IP "\fBovs\-ofctl mod\-flows br0 cookie=1/\-1,cookie=2,actions=normal\fR"
+-Update cookies with a value of 1 to 2 and change their actions to
+-\fBnormal\fR.
+-.RE
+-.IP
+-The ability to match on cookies was added in Open vSwitch 1.5.0.
+-.
+ .PP
+ The following additional field sets the priority for flows added by
+ the \fBadd\-flow\fR and \fBadd\-flows\fR commands.  For
+diff --git a/utilities/ovs-vsctl.8.in b/utilities/ovs-vsctl.8.in
+index 43b00bf..e60e659 100644
+--- a/utilities/ovs-vsctl.8.in
++++ b/utilities/ovs-vsctl.8.in
+@@ -48,26 +48,6 @@ itself starts with command-specific options, if any, followed by the
+ command name and any arguments.  See \fBEXAMPLES\fR below for syntax
+ examples.
+ .
+-.SS "Linux VLAN Bridging Compatibility"
+-The \fBovs\-vsctl\fR program supports the model of a bridge
+-implemented by Open vSwitch, in which a single bridge supports ports
+-on multiple VLANs.  In this model, each port on a bridge is either a
+-trunk port that potentially passes packets tagged with 802.1Q headers
+-that designate VLANs or it is assigned a single implicit VLAN that is
+-never tagged with an 802.1Q header.
+-.PP
+-For compatibility with software designed for the Linux bridge,
+-\fBovs\-vsctl\fR also supports a model in which traffic associated
+-with a given 802.1Q VLAN is segregated into a separate bridge.  A
+-special form of the \fBadd\-br\fR command (see below) creates a ``fake
+-bridge'' within an Open vSwitch bridge to simulate this behavior.
+-When such a ``fake bridge'' is active, \fBovs\-vsctl\fR will treat it
+-much like a bridge separate from its ``parent bridge,'' but the actual
+-implementation in Open vSwitch uses only a single bridge, with ports on
+-the fake bridge assigned the implicit VLAN of the fake bridge of which
+-they are members.  (A fake bridge for VLAN 0 receives packets that
+-have no 802.1Q tag or a tag with VLAN 0.)
+-.
+ .SH OPTIONS
+ .
+ The following options affect the behavior \fBovs\-vsctl\fR as a whole.
+@@ -188,44 +168,17 @@ Without \fB\-\-may\-exist\fR, attempting to create a bridge that
+ exists is an error.  With \fB\-\-may\-exist\fR, this command does
+ nothing if \fIbridge\fR already exists as a real bridge.
+ .
+-.IP "[\fB\-\-may\-exist\fR] \fBadd\-br \fIbridge parent vlan\fR"
+-Creates a ``fake bridge'' named \fIbridge\fR within the existing Open
+-vSwitch bridge \fIparent\fR, which must already exist and must not
+-itself be a fake bridge.  The new fake bridge will be on 802.1Q VLAN
+-\fIvlan\fR, which must be an integer between 0 and 4095.  Initially
+-\fIbridge\fR will have no ports (other than \fIbridge\fR itself).
+-.IP
+-Without \fB\-\-may\-exist\fR, attempting to create a bridge that
+-exists is an error.  With \fB\-\-may\-exist\fR, this command does
+-nothing if \fIbridge\fR already exists as a VLAN bridge under
+-\fIparent\fR for \fIvlan\fR.
+-.
+ .IP "[\fB\-\-if\-exists\fR] \fBdel\-br \fIbridge\fR"
+-Deletes \fIbridge\fR and all of its ports.  If \fIbridge\fR is a real
+-bridge, this command also deletes any fake bridges that were created
+-with \fIbridge\fR as parent, including all of their ports.
++Deletes \fIbridge\fR and all of its ports.
+ .IP
+ Without \fB\-\-if\-exists\fR, attempting to delete a bridge that does
+ not exist is an error.  With \fB\-\-if\-exists\fR, attempting to
+ delete a bridge that does not exist has no effect.
+ .
+-.IP "[\fB\-\-real\fR|\fB\-\-fake\fR] \fBlist\-br\fR"
+-Lists all existing real and fake bridges on standard output, one per
+-line.  With \fB\-\-real\fR or \fB\-\-fake\fR, only bridges of that type
+-are returned.
+-.
+ .IP "\fBbr\-exists \fIbridge\fR"
+-Tests whether \fIbridge\fR exists as a real or fake bridge.  If so,
+-\fBovs\-vsctl\fR exits successfully with exit code 0.  If not,
+-\fBovs\-vsctl\fR exits unsuccessfully with exit code 2.
+-.
+-.IP "\fBbr\-to\-vlan \fIbridge\fR"
+-If \fIbridge\fR is a fake bridge, prints the bridge's 802.1Q VLAN as a
+-decimal integer.  If \fIbridge\fR is a real bridge, prints 0.
+-.
+-.IP "\fBbr\-to\-parent \fIbridge\fR"
+-If \fIbridge\fR is a fake bridge, prints the name of its parent
+-bridge.  If \fIbridge\fR is a real bridge, print \fIbridge\fR.
++Tests whether \fIbridge\fR exists. If so, \fBovs\-vsctl\fR exits
++successfully with exit code 0.  If not, \fBovs\-vsctl\fR exits
++unsuccessfully with exit code 2.
+ .
+ .IP "\fBbr\-set\-external\-id \fIbridge key\fR [\fIvalue\fR]"
+ Sets or clears an ``external ID'' value on \fIbridge\fR.  These values
+@@ -239,27 +192,16 @@ If \fIvalue\fR is specified, then \fIkey\fR is set to \fIvalue\fR for
+ \fIbridge\fR, overwriting any previous value.  If \fIvalue\fR is
+ omitted, then \fIkey\fR is removed from \fIbridge\fR's set of external
+ IDs (if it was present).
+-.IP
+-For real bridges, the effect of this command is similar to that of a
+-\fBset\fR or \fBremove\fR command in the \fBexternal\-ids\fR column of
+-the \fBBridge\fR table.  For fake bridges, it actually modifies keys
+-with names prefixed by \fBfake\-bridge\-\fR in the \fBPort\fR table.
+ .
+ .IP "\fBbr\-get\-external\-id \fIbridge\fR [\fIkey\fR]"
+ Queries the external IDs on \fIbridge\fR.  If \fIkey\fR is specified,
+ the output is the value for that \fIkey\fR or the empty string if
+ \fIkey\fR is unset.  If \fIkey\fR is omitted, the output is
+ \fIkey\fB=\fIvalue\fR, one per line, for each key-value pair.
+-.IP
+-For real bridges, the effect of this command is similar to that of a
+-\fBget\fR command in the \fBexternal\-ids\fR column of the
+-\fBBridge\fR table.  For fake bridges, it queries keys with names
+-prefixed by \fBfake\-bridge\-\fR in the \fBPort\fR table.
+ .
+ .SS "Port Commands"
+ .
+-These commands examine and manipulate Open vSwitch ports.  These
+-commands treat a bonded port as a single entity.
++These commands examine and manipulate Open vSwitch ports.
+ .
+ .IP "\fBlist\-ports \fIbridge\fR"
+ Lists all of the ports within \fIbridge\fR on standard output, one per
+@@ -276,30 +218,12 @@ command (see \fBDatabase Commands\fR below).
+ .IP
+ Without \fB\-\-may\-exist\fR, attempting to create a port that exists
+ is an error.  With \fB\-\-may\-exist\fR, this command does nothing if
+-\fIport\fR already exists on \fIbridge\fR and is not a bonded port.
+-.
+-.IP "[\fB\-\-fake\-iface\fR] \fBadd\-bond \fIbridge port iface\fR\&... [\fIcolumn\fR[\fB:\fIkey\fR]\fR=\fIvalue\fR]\&...\fR"
+-Creates on \fIbridge\fR a new port named \fIport\fR that bonds
+-together the network devices given as each \fIiface\fR.  At least two
+-interfaces must be named.
+-.IP
+-Optional arguments set values of column in the Port record created by
+-the command.  The syntax is the same as that for the \fBset\fR command
+-(see \fBDatabase Commands\fR below).
+-.IP
+-With \fB\-\-fake\-iface\fR, a fake interface with the name \fIport\fR is
+-created.  This should only be used for compatibility with legacy
+-software that requires it.
+-.IP
+-Without \fB\-\-may\-exist\fR, attempting to create a port that exists
+-is an error.  With \fB\-\-may\-exist\fR, this command does nothing if
+-\fIport\fR already exists on \fIbridge\fR and bonds together exactly
+-the specified interfaces.
++\fIport\fR already exists on \fIbridge\fR.
+ .
+ .IP "[\fB\-\-if\-exists\fR] \fBdel\-port \fR[\fIbridge\fR] \fIport\fR"
+ Deletes \fIport\fR.  If \fIbridge\fR is omitted, \fIport\fR is removed
+ from whatever bridge contains it; if \fIbridge\fR is specified, it
+-must be the real or fake bridge that contains \fIport\fR.
++must the bridge that contains \fIport\fR.
+ .IP
+ Without \fB\-\-if\-exists\fR, attempting to delete a port that does
+ not exist is an error.  With \fB\-\-if\-exists\fR, attempting to
+@@ -309,7 +233,7 @@ delete a port that does not exist has no effect.
+ Deletes the port named \fIiface\fR or that has an interface named
+ \fIiface\fR.  If \fIbridge\fR is omitted, the port is removed from
+ whatever bridge contains it; if \fIbridge\fR is specified, it must be
+-the real or fake bridge that contains the port.
++the bridge that contains the port.
+ .IP
+ Without \fB\-\-if\-exists\fR, attempting to delete the port for an
+ interface that does not exist is an error.  With \fB\-\-if\-exists\fR,
+@@ -323,8 +247,7 @@ output.
+ .SS "Interface Commands"
+ .
+ These commands examine the interfaces attached to an Open vSwitch
+-bridge.  These commands treat a bonded port as a collection of two or
+-more interfaces, rather than as a single port.
++bridge.
+ .
+ .IP "\fBlist\-ifaces \fIbridge\fR"
+ Lists all of the interfaces within \fIbridge\fR on standard output,
+@@ -507,37 +430,21 @@ identified by port name.
+ .IP "\fBQueue\fR"
+ Configuration for one queue within a \fBQoS\fR configuration.  Records
+ may only be identified by UUID.
+-.IP "\fBMirror\fR"
+-A port mirroring configuration attached to a bridge.  Records may be
+-identified by mirror name.
+ .IP "\fBController\fR"
+ Configuration for an OpenFlow controller.  A controller attached to a
+ particular bridge may be identified by the bridge's name.
+ .IP "\fBManager\fR"
+ Configuration for an OVSDB connection.  Records may be identified
+ by target (e.g. \fBtcp:1.2.3.4\fR).
+-.IP "\fBNetFlow\fR"
+-A NetFlow configuration attached to a bridge.  Records may be
+-identified by bridge name.
+ .IP "\fBSSL\fR"
+ The global SSL configuration for \fBovs\-vswitchd\fR.  The record
+ attached to the \fBOpen_vSwitch\fR table may be identified by
+ specifying \fB.\fR as the record name.
+-.IP "\fBsFlow\fR"
+-An sFlow exporter configuration attached to a bridge.  Records may be
+-identified by bridge name.
+-.IP "\fBIPFIX\fR"
+-An IPFIX exporter configuration attached to a bridge.  Records may be
+-identified by bridge name.
+-.IP "\fBFlow_Sample_Collector_Set\fR"
+-An IPFIX exporter configuration attached to a bridge for sampling
+-packets on a per-flow basis using OpenFlow \fBsample\fR actions.
+ .PP
+ Record names must be specified in full and with correct
+ capitalization.  Names of tables and columns are not case-sensitive,
+ and \fB\-\-\fR and \fB_\fR are treated interchangeably.  Unique
+-abbreviations are acceptable, e.g. \fBnet\fR or \fBn\fR is sufficient
+-to identify the \fBNetFlow\fR table.
++abbreviations are acceptable.
+ .
+ .ST "Database Values"
+ .PP
+@@ -798,15 +705,15 @@ after waiting only at most 5 seconds.
+ This command has no effect on behavior, but any database log record
+ created by the command will include the command and its arguments.
+ .SH "EXAMPLES"
+-Create a new bridge named br0 and add port eth0 to it:
++Create a new bridge named br0 and add port net1 to it:
+ .IP
+ .B "ovs\-vsctl add\-br br0"
+ .br
+-.B "ovs\-vsctl add\-port br0 eth0"
++.B "ovs\-vsctl add\-port br0 net1"
+ .PP
+ Alternatively, perform both operations in a single atomic transaction:
+ .IP 
+-.B "ovs\-vsctl add\-br br0 \-\- add\-port br0 eth0"
++.B "ovs\-vsctl add\-br br0 \-\- add\-port br0 net1"
+ .PP
+ Delete bridge \fBbr0\fR, reporting an error if it does not exist:
+ .IP
+@@ -816,62 +723,22 @@ Delete bridge \fBbr0\fR if it exists:
+ .IP
+ .B "ovs\-vsctl \-\-if\-exists del\-br br0"
+ .PP
+-Set the \fBqos\fR column of the \fBPort\fR record for \fBeth0\fR to
++Set the \fBqos\fR column of the \fBPort\fR record for \fBnet1\fR to
+ point to a new \fBQoS\fR record, which in turn points with its queue 0
+ to a new \fBQueue\fR record:
+ .IP
+-.B "ovs\-vsctl \-\- set port eth0 qos=@newqos \-\- \-\-id=@newqos create qos type=linux\-htb other\-config:max\-rate=1000000 queues:0=@newqueue \-\- \-\-id=@newqueue create queue other\-config:min\-rate=1000000 other\-config:max\-rate=1000000"
++.B "ovs\-vsctl \-\- set port net1 qos=@newqos \-\- \-\-id=@newqos create qos type=linux\-htb other\-config:max\-rate=1000000 queues:0=@newqueue \-\- \-\-id=@newqueue create queue other\-config:min\-rate=1000000 other\-config:max\-rate=1000000"
+ .SH "CONFIGURATION COOKBOOK"
+ .SS "Port Configuration"
+ .PP
+-Add an ``internal port'' \fBvlan10\fR to bridge \fBbr0\fR as a VLAN
+-access port for VLAN 10, and configure it with an IP address:
+-.IP
+-.B "ovs\-vsctl add\-port br0 vlan10 tag=10 \-\- set Interface vlan10 type=internal"
+-.IP
+-.B "ifconfig vlan10 192.168.0.123"
+-.
+-.PP
+-Add a GRE tunnel port \fBgre0\fR to remote IP address 1.2.3.4 to
+-bridge \fBbr0\fR:
+-.IP
+-.B "ovs\-vsctl add\-port br0 gre0 \-\- set Interface gre0 type=gre options:remote_ip=1.2.3.4"
+-.
+-.SS "Port Mirroring"
+-.PP
+-Mirror all packets received or sent on \fBeth0\fR or \fBeth1\fR onto
+-\fBeth2\fR, assuming that all of those ports exist on bridge \fBbr0\fR
+-(as a side-effect this causes any packets received on \fBeth2\fR to be
+-ignored):
+-.IP
+-.B "ovs\-vsctl \-\- set Bridge br0 mirrors=@m \(rs"
+-.IP
+-.B "\-\- \-\-id=@eth0 get Port eth0 \(rs"
+-.IP
+-.B "\-\- \-\-id=@eth1 get Port eth1 \(rs"
+-.IP
+-.B "\-\- \-\-id=@eth2 get Port eth2 \(rs"
+-.IP
+-.B "\-\- \-\-id=@m create Mirror name=mymirror select-dst-port=@eth0,@eth1 select-src-port=@eth0,@eth1 output-port=@eth2"
+-.PP
+-Remove the mirror created above from \fBbr0\fR, which also destroys
+-the Mirror record (since it is now unreferenced):
+-.IP
+-.B "ovs\-vsctl \-\- \-\-id=@rec get Mirror mymirror \(rs"
+-.IP
+-.B "\-\- remove Bridge br0 mirrors @rec"
+-.PP
+-The following simpler command also works:
+-.IP
+-.B "ovs\-vsctl clear Bridge br0 mirrors"
+ .SS "Quality of Service (QoS)"
+ .PP
+ Create a \fBlinux\-htb\fR QoS record that points to a few queues and
+-use it on \fBeth0\fR and \fBeth1\fR:
++use it on \fBnet1\fR and \fBnet2\fR:
+ .IP
+-.B "ovs\-vsctl \-\- set Port eth0 qos=@newqos \(rs"
++.B "ovs\-vsctl \-\- set Port net1 qos=@newqos \(rs"
+ .IP
+-.B "\-\- set Port eth1 qos=@newqos \(rs"
++.B "\-\- set Port net2 qos=@newqos \(rs"
+ .IP
+ .B "\-\- \-\-id=@newqos create QoS type=linux\-htb other\-config:max\-rate=1000000000 queues=0=@q0,1=@q1 \(rs"
+ .IP
+@@ -879,15 +746,15 @@ use it on \fBeth0\fR and \fBeth1\fR:
+ .IP
+ .B "\-\- \-\-id=@q1 create Queue other\-config:min\-rate=500000000"
+ .PP
+-Deconfigure the QoS record above from \fBeth1\fR only:
++Deconfigure the QoS record above from \fBnet2\fR only:
+ .IP
+-.B "ovs\-vsctl clear Port eth1 qos"
++.B "ovs\-vsctl clear Port net2 qos"
+ .PP
+-To deconfigure the QoS record from both \fBeth0\fR and \fBeth1\fR and
++To deconfigure the QoS record from both \fBnet1\fR and \fBnet2\fR and
+ then delete the QoS record (which must be done explicitly because
+ unreferenced QoS records are not automatically destroyed):
+ .IP
+-.B "ovs\-vsctl \-\- destroy QoS eth0 \-\- clear Port eth0 qos \-\- clear Port eth1 qos"
++.B "ovs\-vsctl \-\- destroy QoS net1 \-\- clear Port net1 qos \-\- clear Port net2 qos"
+ .PP
+ (This command will leave two unreferenced Queue records in the
+ database.  To delete them, use "\fBovs\-vsctl list Queue\fR" to find
+@@ -896,77 +763,13 @@ their UUIDs, then "\fBovs\-vsctl destroy Queue \fIuuid1\fR
+ "\fBovs\-vsctl -- --all destroy Queue\fR" to delete all records.)
+ .SS "Connectivity Monitoring"
+ .PP
+-Monitor connectivity to a remote maintenance point on eth0.
++Monitor connectivity to a remote maintenance point on net1.
+ .IP
+-.B "ovs\-vsctl set Interface eth0 cfm_mpid=1"
++.B "ovs\-vsctl set Interface net1 cfm_mpid=1"
+ .PP
+ Deconfigure connectivity monitoring from above:
+ .IP
+-.B "ovs\-vsctl clear Interface eth0 cfm_mpid"
+-.SS "NetFlow"
+-.PP
+-Configure bridge \fBbr0\fR to send NetFlow records to UDP port 5566 on
+-host 192.168.0.34, with an active timeout of 30 seconds:
+-.IP
+-.B "ovs\-vsctl \-\- set Bridge br0 netflow=@nf \(rs"
+-.IP
+-.B "\-\- \-\-id=@nf create NetFlow targets=\(rs\(dq192.168.0.34:5566\(rs\(dq active\-timeout=30"
+-.PP
+-Update the NetFlow configuration created by the previous command to
+-instead use an active timeout of 60 seconds:
+-.IP
+-.B "ovs\-vsctl set NetFlow br0 active_timeout=60"
+-.PP
+-Deconfigure the NetFlow settings from \fBbr0\fR, which also destroys
+-the NetFlow record (since it is now unreferenced):
+-.IP
+-.B "ovs\-vsctl clear Bridge br0 netflow"
+-.SS "sFlow"
+-.PP
+-Configure bridge \fBbr0\fR to send sFlow records to a collector on
+-10.0.0.1 at port 6343, using \fBeth1\fR\'s IP address as the source,
+-with specific sampling parameters:
+-.IP
+-.B "ovs\-vsctl \-\- \-\-id=@s create sFlow agent=eth1 target=\(rs\(dq10.0.0.1:6343\(rs\(dq header=128 sampling=64 polling=10 \(rs"
+-.IP
+-.B "\-\- set Bridge br0 sflow=@s"
+-.PP
+-Deconfigure sFlow from \fBbr0\fR, which also destroys the sFlow record
+-(since it is now unreferenced):
+-.IP
+-.B "ovs\-vsctl \-\- clear Bridge br0 sflow"
+-.SS "IPFIX"
+-.PP
+-Configure bridge \fBbr0\fR to send one IPFIX flow record per packet
+-sample to UDP port 4739 on host 192.168.0.34, with Observation Domain
+-ID 123 and Observation Point ID 456, a flow cache active timeout of 1
+-minute (60 seconds), and a maximum flow cache size of 13 flows:
+-.IP
+-.B "ovs\-vsctl \-\- set Bridge br0 ipfix=@i \(rs"
+-.IP
+-.B "\-\- \-\-id=@i create IPFIX targets=\(rs\(dq192.168.0.34:4739\(rs\(dq obs_domain_id=123 obs_point_id=456 cache_active_timeout=60 cache_max_flows=13"
+-.PP
+-Deconfigure the IPFIX settings from \fBbr0\fR, which also destroys the
+-IPFIX record (since it is now unreferenced):
+-.IP
+-.B "ovs\-vsctl clear Bridge br0 ipfix"
+-.SS "802.1D Spanning Tree Protocol (STP)"
+-.PP
+-Configure bridge \fBbr0\fR to participate in an 802.1D spanning tree:
+-.IP
+-.B "ovs\-vsctl set Bridge br0 stp_enable=true"
+-.PP
+-Set the bridge priority of \fBbr0\fR to 0x7800:
+-.IP
+-.B "ovs\-vsctl set Bridge br0 other_config:stp-priority=0x7800"
+-.PP
+-Set the path cost of port \fBeth0\fR to 10:
+-.IP
+-.B "ovs\-vsctl set Port eth0 other_config:stp-path-cost=10"
+-.PP
+-Deconfigure STP from above:
+-.IP
+-.B "ovs\-vsctl set Bridge br0 stp_enable=false"
++.B "ovs\-vsctl clear Interface net1 cfm_mpid"
+ .PP
+ .SS "OpenFlow Version"
+ .PP
+diff --git a/utilities/ovs-dpctl.8.in b/utilities/ovs-dpctl.8.in
+index 5a0dd70..6de87ad 100644
+--- a/utilities/ovs-dpctl.8.in
++++ b/utilities/ovs-dpctl.8.in
+@@ -15,7 +15,7 @@ ovs\-dpctl \- administer Open vSwitch datapaths
+ .
+ .SH DESCRIPTION
+ .PP
+-The \fBovs\-dpctl\fR program can create, modify, and delete Open vSwitch
++The \fBovs\-dpctl\fR program can be used to display Open vSwitch
+ datapaths.  A single machine may host any number of datapaths.
+ .PP
+ A newly created datapath is associated with only one network device, a
+@@ -38,58 +38,6 @@ default provider \fBsystem\fR is assumed.
+ The following commands manage datapaths.
+ .
+ .TP
+-\fBadd\-dp \fIdp\fR [\fInetdev\fR[\fB,\fIoption\fR]...]
+-Creates datapath \fIdp\fR, with a local port also named \fIdp\fR.
+-This will fail if a network device \fIdp\fR already exists.
+-.IP
+-If \fInetdev\fRs are specified, \fBovs\-dpctl\fR adds them to the
+-new datapath, just as if \fBadd\-if\fR was specified.
+-.
+-.TP
+-\fBdel\-dp \fIdp\fR
+-Deletes datapath \fIdp\fR.  If \fIdp\fR is associated with any network
+-devices, they are automatically removed.
+-.
+-.TP
+-\fBadd\-if \fIdp netdev\fR[\fB,\fIoption\fR]...
+-Adds each \fInetdev\fR to the set of network devices datapath
+-\fIdp\fR monitors, where \fIdp\fR is the name of an existing
+-datapath, and \fInetdev\fR is the name of one of the host's
+-network devices, e.g. \fBeth0\fR.  Once a network device has been added
+-to a datapath, the datapath has complete ownership of the network device's
+-traffic and the network device appears silent to the rest of the
+-system.
+-.IP
+-A \fInetdev\fR may be followed by a comma-separated list of options.
+-The following options are currently supported:
+-.
+-.RS
+-.IP "\fBtype=\fItype\fR"
+-Specifies the type of port to add.  The default type is \fBsystem\fR.
+-.IP "\fBport_no=\fIport\fR"
+-Requests a specific port number within the datapath.  If this option is
+-not specified then one will be automatically assigned.
+-.IP "\fIkey\fB=\fIvalue\fR"
+-Adds an arbitrary key-value option to the port's configuration.
+-.RE
+-.IP
+-\fBovs\-vswitchd.conf.db\fR(5) documents the available port types and
+-options.
+-.
+-.IP "\fBset\-if \fIdp port\fR[\fB,\fIoption\fR]..."
+-Reconfigures each \fIport\fR in \fIdp\fR as specified.  An
+-\fIoption\fR of the form \fIkey\fB=\fIvalue\fR adds the specified
+-key-value option to the port or overrides an existing key's value.  An
+-\fIoption\fR of the form \fIkey\fB=\fR, that is, without a value,
+-deletes the key-value named \fIkey\fR.  The type and port number of a
+-port cannot be changed, so \fBtype\fR and \fBport_no\fR are only allowed if
+-they match the existing configuration.
+-.TP
+-\fBdel\-if \fIdp netdev\fR...
+-Removes each \fInetdev\fR from the list of network devices datapath
+-\fIdp\fR monitors.
+-.
+-.TP
+ \fBdump\-dps\fR
+ Prints the name of each configured datapath on a separate line.
+ .
+@@ -151,37 +99,6 @@ The \fIfilter\fR is also useful to match wildcarded fields in the datapath
+ flow. As an example, \fBfilter='tcp,tp_src=100'\fR will match the
+ datapath flow containing '\fBtcp(src=80/0xff00,dst=8080/0xff)\fR'.
+ .
+-.IP "\fBadd\-flow\fR [\fIdp\fR] \fIflow actions\fR"
+-.IQ "[\fB\-\-clear\fR] [\fB\-\-may-create\fR] [\fB\-s\fR | \fB\-\-statistics\fR] \fBmod\-flow\fR [\fIdp\fR] \fIflow actions\fR"
+-Adds or modifies a flow in \fIdp\fR's flow table that, when a packet
+-matching \fIflow\fR arrives, causes \fIactions\fR to be executed.
+-.IP
+-The \fBadd\-flow\fR command succeeds only if \fIflow\fR does not
+-already exist in \fIdp\fR.  Contrariwise, \fBmod\-flow\fR without
+-\fB\-\-may\-create\fR only modifies the actions for an existing flow.
+-With \fB\-\-may\-create\fR, \fBmod\-flow\fR will add a new flow or
+-modify an existing one.
+-.IP
+-If \fB\-s\fR or \fB\-\-statistics\fR is specified, then
+-\fBmod\-flows\fR prints the modified flow's statistics.  A flow's
+-statistics are the number of packets and bytes that have passed
+-through the flow, the elapsed time since the flow last processed a
+-packet (if ever), and (for TCP flows) the union of the TCP flags
+-processed through the flow.
+-.IP
+-With \fB\-\-clear\fR, \fBmod\-flows\fR zeros out the flow's
+-statistics.  The statistics printed if \fB\-s\fR or
+-\fB\-\-statistics\fR is also specified are those from just before
+-clearing the statistics.
+-.
+-.IP "[\fB\-s\fR | \fB\-\-statistics\fR] \fBdel\-flow\fR [\fIdp\fR] \fIflow\fR"
+-Deletes the flow from \fIdp\fR's flow table that matches \fIflow\fR.
+-If \fB\-s\fR or \fB\-\-statistics\fR is specified, then
+-\fBmod\-flows\fR prints the deleted flow's statistics.
+-.
+-.IP "\fBdel\-flows\fR [\fIdp\fR]"
+-Deletes all flow entries from datapath \fIdp\fR's flow table.
+-.
+ .SH OPTIONS
+ .IP "\fB\-s\fR"
+ .IQ "\fB\-\-statistics\fR"
+diff --git a/utilities/ovs-dpctl.c b/utilities/ovs-dpctl.c
+index ccc55b5..4db43fd 100644
+--- a/utilities/ovs-dpctl.c
++++ b/utilities/ovs-dpctl.c
+@@ -158,6 +158,7 @@ parse_options(int argc, char *argv[])
+ static void
+ usage(void)
+ {
++#ifndef __sun
+     printf("%s: Open vSwitch datapath management utility\n"
+            "usage: %s [OPTIONS] COMMAND [ARG...]\n"
+            "  add-dp DP [IFACE...]     add new datapath DP (with IFACEs)\n"
+@@ -191,6 +192,26 @@ usage(void)
+            "  -t, --timeout=SECS          give up after SECS seconds\n"
+            "  -h, --help                  display this help message\n"
+            "  -V, --version               display version information\n");
++#else
++    printf("%s: Open vSwitch datapath management utility\n"
++           "usage: %s [OPTIONS] COMMAND [ARG...]\n"
++           "  dump-dps                 display names of all datapaths\n"
++           "  show                     show basic info on all datapaths\n"
++           "  show DP...               show basic info on each DP\n"
++           "  dump-flows [DP]          display flows in DP\n"
++           "For COMMAND dump-flows DP is optional if there is only\n"
++           " one datapath.\n",
++           program_name, program_name);
++    vlog_usage();
++    printf("\nOptions for show:\n"
++           "  -s,  --statistics           print statistics for port or flow\n"
++           "\nOptions for dump-flows:\n"
++           "  -m, --more                  increase verbosity of output\n"
++           "\nOther options:\n"
++           "  -t, --timeout=SECS          give up after SECS seconds\n"
++           "  -h, --help                  display this help message\n"
++           "  -V, --version               display version information\n");
++#endif
+     exit(EXIT_SUCCESS);
+ }
+ 
+diff --git a/utilities/ovs-vsctl.c b/utilities/ovs-vsctl.c
+index f837158..d9d92a3 100644
+--- a/utilities/ovs-vsctl.c
++++ b/utilities/ovs-vsctl.c
+@@ -642,6 +642,7 @@ vsctl_exit(int status)
+ static void
+ usage(void)
+ {
++#ifndef __sun
+     printf("\
+ %s: ovs-vswitchd management utility\n\
+ usage: %s [OPTIONS] COMMAND [ARG...]\n\
+@@ -718,6 +719,82 @@ Options:\n\
+   --dry-run                   do not commit changes to database\n\
+   --oneline                   print exactly one line of output per command\n",
+            program_name, program_name, default_db());
++#else
++    printf("\
++%s: ovs-vswitchd management utility\n\
++usage: %s [OPTIONS] COMMAND [ARG...]\n\
++\n\
++Open vSwitch commands:\n\
++  init                        initialize database, if not yet initialized\n\
++  show                        print overview of database contents\n\
++  emer-reset                  reset configuration to clean state\n\
++\n\
++Bridge commands:\n\
++  add-br BRIDGE               create a new bridge named BRIDGE\n\
++  add-br BRIDGE PARENT VLAN   create new fake BRIDGE in PARENT on VLAN\n\
++  del-br BRIDGE               delete BRIDGE and all of its ports\n\
++  list-br                     print the names of all the bridges\n\
++  br-exists BRIDGE            exit 2 if BRIDGE does not exist\n\
++  br-to-parent BRIDGE         print the parent of BRIDGE\n\
++  br-set-external-id BRIDGE KEY VALUE  set KEY on BRIDGE to VALUE\n\
++  br-set-external-id BRIDGE KEY  unset KEY on BRIDGE\n\
++  br-get-external-id BRIDGE KEY  print value of KEY on BRIDGE\n\
++  br-get-external-id BRIDGE  list key-value pairs on BRIDGE\n\
++\n\
++Port commands:\n\
++  list-ports BRIDGE           print the names of all the ports on BRIDGE\n\
++  add-port BRIDGE PORT        add network device PORT to BRIDGE\n\
++  del-port [BRIDGE] PORT      delete PORT (which may be bonded) from BRIDGE\n\
++  port-to-br PORT             print name of bridge that contains PORT\n\
++\n\
++Interface commands:\n\
++  list-ifaces BRIDGE          print the names of all interfaces on BRIDGE\n\
++  iface-to-br IFACE           print name of bridge that contains IFACE\n\
++\n\
++Controller commands:\n\
++  get-controller BRIDGE      print the controllers for BRIDGE\n\
++  del-controller BRIDGE      delete the controllers for BRIDGE\n\
++  set-controller BRIDGE TARGET...  set the controllers for BRIDGE\n\
++  get-fail-mode BRIDGE       print the fail-mode for BRIDGE\n\
++  del-fail-mode BRIDGE       delete the fail-mode for BRIDGE\n\
++  set-fail-mode BRIDGE MODE  set the fail-mode for BRIDGE to MODE\n\
++\n\
++Manager commands:\n\
++  get-manager                print the managers\n\
++  del-manager                delete the managers\n\
++  set-manager TARGET...      set the list of managers to TARGET...\n\
++\n\
++SSL commands:\n\
++  get-ssl                     print the SSL configuration\n\
++  del-ssl                     delete the SSL configuration\n\
++  set-ssl PRIV-KEY CERT CA-CERT  set the SSL configuration\n\
++\n\
++Switch commands:\n\
++  emer-reset                  reset switch to known good state\n\
++\n\
++Database commands:\n\
++  list TBL [REC]              list RECord (or all records) in TBL\n\
++  find TBL CONDITION...       list records satisfying CONDITION in TBL\n\
++  get TBL REC COL[:KEY]       print values of COLumns in RECord in TBL\n\
++  set TBL REC COL[:KEY]=VALUE set COLumn values in RECord in TBL\n\
++  add TBL REC COL [KEY=]VALUE add (KEY=)VALUE to COLumn in RECord in TBL\n\
++  remove TBL REC COL [KEY=]VALUE  remove (KEY=)VALUE from COLumn\n\
++  clear TBL REC COL           clear values from COLumn in RECord in TBL\n\
++  create TBL COL[:KEY]=VALUE  create and initialize new record\n\
++  destroy TBL REC             delete RECord from TBL\n\
++  wait-until TBL REC [COL[:KEY]=VALUE]  wait until condition is true\n\
++Potentially unsafe database commands require --force option.\n\
++\n\
++Options:\n\
++  --db=DATABASE               connect to DATABASE\n\
++                              (default: %s)\n\
++  --no-wait                   do not wait for ovs-vswitchd to reconfigure\n\
++  --retry                     keep trying to connect to server forever\n\
++  -t, --timeout=SECS          wait at most SECS seconds for ovs-vswitchd\n\
++  --dry-run                   do not commit changes to database\n\
++  --oneline                   print exactly one line of output per command\n",
++           program_name, program_name, default_db());
++#endif
+     vlog_usage();
+     printf("\
+   --no-syslog             equivalent to --verbose=vsctl:syslog:warn\n");
+diff --git a/vswitchd/ovs-vswitchd.8.in b/vswitchd/ovs-vswitchd.8.in
+index 8ffa7a0..12f8e55 100644
+--- a/vswitchd/ovs-vswitchd.8.in
++++ b/vswitchd/ovs-vswitchd.8.in
+@@ -30,32 +30,6 @@ switching across each bridge described in its configuration files.  As
+ the database changes, \fBovs\-vswitchd\fR automatically updates its
+ configuration to match.
+ .PP
+-\fBovs\-vswitchd\fR switches may be configured with any of the following
+-features:
+-.
+-.IP \(bu
+-L2 switching with MAC learning.
+-.
+-.IP \(bu
+-NIC bonding with automatic fail-over and source MAC-based TX load
+-balancing ("SLB").
+-.
+-.IP \(bu
+-802.1Q VLAN support.
+-.
+-.IP \(bu
+-Port mirroring, with optional VLAN tagging.
+-.
+-.IP \(bu
+-NetFlow v5 flow logging.
+-.
+-.IP \(bu
+-sFlow(R) monitoring.
+-.
+-.IP \(bu
+-Connectivity to an external OpenFlow controller, such as NOX.
+-.
+-.PP
+ Only a single instance of \fBovs\-vswitchd\fR is intended to run at a time.
+ A single \fBovs\-vswitchd\fR can manage any number of switch instances, up
+ to the maximum number of supported Open vSwitch datapaths.
+@@ -66,11 +40,6 @@ managing datapaths in conjunction with \fBovs\-vswitchd\fR, and their use
+ to modify datapaths when \fBovs\-vswitchd\fR is running can interfere with
+ its operation.  (\fBovs\-dpctl\fR may still be useful for diagnostics.)
+ .PP
+-An Open vSwitch datapath kernel module must be loaded for \fBovs\-vswitchd\fR
+-to be useful.  Please refer to the \fBINSTALL.Linux\fR file included in the
+-Open vSwitch distribution for instructions on how to build and load
+-the Open vSwitch kernel module.
+-.PP
+ .SH OPTIONS
+ .IP "\fB\-\-mlockall\fR"
+ Causes \fBovs\-vswitchd\fR to call the \fBmlockall()\fR function, to
+@@ -105,41 +74,8 @@ how to configure Open vSwitch.
+ .SS "GENERAL COMMANDS"
+ .IP "\fBexit\fR"
+ Causes \fBovs\-vswitchd\fR to gracefully terminate.
+-.IP "\fBqos/show\fR \fIinterface\fR"
+-Queries the kernel for Quality of Service configuration and statistics
+-associated with the given \fIinterface\fR.
+-.IP "\fBbfd/show\fR [\fIinterface\fR]"
+-Displays detailed information about Bidirectional Forwarding Detection
+-configured on \fIinterface\fR.  If \fIinterface\fR is not specified,
+-then displays detailed information about all interfaces with BFD
+-enabled.
+-.IP "\fBbfd/set-forwarding\fR [\fIinterface\fR] \fIstatus\fR"
+-Force the fault status of the BFD module on \fIinterface\fR (or all
+-interfaces if none is given) to be \fIstatus\fR.  \fIstatus\fR can be
+-"true", "false", or "normal" which reverts to the standard behavior.
+-.IP "\fBcfm/show\fR [\fIinterface\fR]"
+-Displays detailed information about Connectivity Fault Management
+-configured on \fIinterface\fR.  If \fIinterface\fR is not specified,
+-then displays detailed information about all interfaces with CFM
+-enabled.
+-.IP "\fBcfm/set-fault\fR [\fIinterface\fR] \fIstatus\fR"
+-Force the fault status of the CFM module on \fIinterface\fR (or all
+-interfaces if none is given) to be \fIstatus\fR.  \fIstatus\fR can be
+-"true", "false", or "normal" which reverts to the standard behavior.
+-.IP "\fBstp/tcn\fR [\fIbridge\fR]"
+-Forces a topology change event on \fIbridge\fR if it's running STP.  This
+-may cause it to send Topology Change Notifications to its peers and flush
+-its MAC table..  If no \fIbridge\fR is given, forces a topology change
+-event on all bridges.
+ .SS "BRIDGE COMMANDS"
+ These commands manage bridges.
+-.IP "\fBfdb/flush\fR [\fIbridge\fR]"
+-Flushes \fIbridge\fR MAC address learning table, or all learning tables
+-if no \fIbridge\fR is given.
+-.IP "\fBfdb/show\fR \fIbridge\fR"
+-Lists each MAC address/VLAN pair learned by the specified \fIbridge\fR,
+-along with the port on which it was learned and the age of the entry,
+-in seconds.
+ .IP "\fBbridge/reconnect\fR [\fIbridge\fR]"
+ Makes \fIbridge\fR drop all of its OpenFlow controller connections and
+ reconnect.  If \fIbridge\fR is not specified, then all bridges drop
+@@ -152,63 +88,6 @@ Lists all flows in \fIbridge\fR, including those normally hidden to
+ commands such as \fBovs\-ofctl dump\-flows\fR.  Flows set up by mechanisms
+ such as in-band control and fail-open are hidden from the controller
+ since it is not allowed to modify or override them.
+-.SS "BOND COMMANDS"
+-These commands manage bonded ports on an Open vSwitch's bridges.  To
+-understand some of these commands, it is important to understand a
+-detail of the bonding implementation called ``source load balancing''
+-(SLB).  Instead of directly assigning Ethernet source addresses to
+-slaves, the bonding implementation computes a function that maps an
+-48-bit Ethernet source addresses into an 8-bit value (a ``MAC hash''
+-value).  All of the Ethernet addresses that map to a single 8-bit
+-value are then assigned to a single slave.
+-.IP "\fBbond/list\fR"
+-Lists all of the bonds, and their slaves, on each bridge.
+-.
+-.IP "\fBbond/show\fR [\fIport\fR]"
+-Lists all of the bond-specific information (updelay, downdelay, time
+-until the next rebalance) about the given bonded \fIport\fR, or all
+-bonded ports if no \fIport\fR is given.  Also lists information about
+-each slave: whether it is enabled or disabled, the time to completion
+-of an updelay or downdelay if one is in progress, whether it is the
+-active slave, the hashes assigned to the slave.  Any LACP information
+-related to this bond may be found using the \fBlacp/show\fR command.
+-.
+-.IP "\fBbond/migrate\fR \fIport\fR \fIhash\fR \fIslave\fR"
+-Only valid for SLB bonds.  Assigns a given MAC hash to a new slave.
+-\fIport\fR specifies the bond port, \fIhash\fR the MAC hash to be
+-migrated (as a decimal number between 0 and 255), and \fIslave\fR the
+-new slave to be assigned.
+-.IP
+-The reassignment is not permanent: rebalancing or fail-over will
+-cause the MAC hash to be shifted to a new slave in the usual
+-manner.
+-.IP
+-A MAC hash cannot be migrated to a disabled slave.
+-.IP "\fBbond/set\-active\-slave\fR \fIport\fR \fIslave\fR"
+-Sets \fIslave\fR as the active slave on \fIport\fR.  \fIslave\fR must
+-currently be enabled.
+-.IP
+-The setting is not permanent: a new active slave will be selected
+-if \fIslave\fR becomes disabled.
+-.IP "\fBbond/enable\-slave\fR \fIport\fR \fIslave\fR"
+-.IQ "\fBbond/disable\-slave\fR \fIport\fR \fIslave\fR"
+-Enables (or disables) \fIslave\fR on the given bond \fIport\fR, skipping any
+-updelay (or downdelay).
+-.IP
+-This setting is not permanent: it persists only until the carrier
+-status of \fIslave\fR changes.
+-.IP "\fBbond/hash\fR \fImac\fR [\fIvlan\fR] [\fIbasis\fR]"
+-Returns the hash value which would be used for \fImac\fR with \fIvlan\fR
+-and \fIbasis\fR if specified.
+-.
+-.IP "\fBlacp/show\fR [\fIport\fR]"
+-Lists all of the LACP related information about the given \fIport\fR:
+-active or passive, aggregation key, system id, and system priority.  Also
+-lists information about each slave: whether it is enabled or disabled,
+-whether it is attached or detached, port id and priority, actor
+-information, and partner information.  If \fIport\fR is not specified,
+-then displays detailed information about all interfaces with CFM
+-enabled.
+ .
+ .so ofproto/ofproto-dpif-unixctl.man
+ .so ofproto/ofproto-unixctl.man
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openvswitch/patches/06-controller-fix.patch	Mon Nov 16 16:49:19 2015 -0500
@@ -0,0 +1,75 @@
+This patch fixes a bug in the test controller.
+
+This patch was developed upstream in OVS 2.4.
+
+diff --git a/lib/learning-switch.c b/lib/learning-switch.c
+index ca57911..e037310 100644
+--- a/lib/learning-switch.c
++++ b/lib/learning-switch.c
+@@ -173,10 +173,49 @@ static void
+ lswitch_handshake(struct lswitch *sw)
+ {
+     enum ofputil_protocol protocol;
+-
++    enum ofp_version version;
+     send_features_request(sw);
++    version = rconn_get_version(sw->rconn);
++    protocol = ofputil_protocol_from_ofp_version(version);
++    if (version >= OFP13_VERSION) {
++        /* OpenFlow 1.3 and later by default drop packets that miss in the flow
++         * table.  Set up a flow to send packets to the controller by
++         * default. */
++        struct ofputil_flow_mod fm;
++        struct ofpact_output output;
++        struct ofpbuf *msg;
++        int error;
+ 
+-    protocol = ofputil_protocol_from_ofp_version(rconn_get_version(sw->rconn));
++        ofpact_init_OUTPUT(&output);
++        output.port = OFPP_CONTROLLER;
++        output.max_len = OFP_DEFAULT_MISS_SEND_LEN;
++
++        match_init_catchall(&fm.match);
++        fm.priority = 0;
++        fm.cookie = 0;
++        fm.cookie_mask = 0;
++        fm.new_cookie = 0;
++        fm.modify_cookie = false;
++        fm.table_id = 0;
++        fm.command = OFPFC_ADD;
++        fm.idle_timeout = 0;
++        fm.hard_timeout = 0;
++        fm.importance = 0;
++        fm.buffer_id = UINT32_MAX;
++        fm.out_port = OFPP_NONE;
++        fm.out_group = OFPG_ANY;
++        fm.flags = 0;
++        fm.ofpacts = &output.ofpact;
++        fm.ofpacts_len = sizeof output;
++        fm.delete_reason = 0;
++
++        msg = ofputil_encode_flow_mod(&fm, protocol);
++        error = rconn_send(sw->rconn, msg, NULL);
++        if (error) {
++            VLOG_INFO_RL(&rl, "%s: failed to add default flow (%s)",
++                         rconn_get_name(sw->rconn), ovs_strerror(error));
++        }
++    }
+     if (sw->default_flows) {
+         struct ofpbuf *msg = NULL;
+         int error = 0;
+diff --git a/lib/ofp-util.h b/lib/ofp-util.h
+index ce6045b..622b928 100644
+--- a/lib/ofp-util.h
++++ b/lib/ofp-util.h
+@@ -305,8 +305,10 @@ struct ofputil_flow_mod {
+     ofp_port_t out_port;
+     uint32_t out_group;
+     enum ofputil_flow_mod_flags flags;
++    uint16_t importance;     /* Eviction precedence. */
+     struct ofpact *ofpacts;  /* Series of "struct ofpact"s. */
+     size_t ofpacts_len;      /* Length of ofpacts, in bytes. */
++    enum ofp_flow_removed_reason delete_reason;
+ };
+ 
+ enum ofperr ofputil_decode_flow_mod(struct ofputil_flow_mod *,
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openvswitch/patches/07-ovsthread_key_destruct-fix.patch	Mon Nov 16 16:49:19 2015 -0500
@@ -0,0 +1,21 @@
+This patch fixes a bug in ovsthread_key_destruct__.
+
+This patch has not been proposed upstream but will be proposed for 2.4.
+
+diff --git a/lib/ovs-thread.c b/lib/ovs-thread.c
+index 529756f..11735f9 100644
+--- a/lib/ovs-thread.c
++++ b/lib/ovs-thread.c
+@@ -641,8 +641,10 @@ ovsthread_key_destruct__(void *slots_)
+     n = n_keys;
+     ovs_mutex_unlock(&key_mutex);
+ 
+-    for (i = 0; i < n / L2_SIZE; i++) {
+-        free(slots->p1[i]);
++    if (n > 0) {
++	    for (i = 0; i <= (n - 1) / L2_SIZE; i++) {
++                free(slots->p1[i]);
++        }
+     }
+     free(slots);
+ }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openvswitch/patches/08-self-test-fix.patch	Mon Nov 16 16:49:19 2015 -0500
@@ -0,0 +1,473 @@
+This patch fixes a bug in the self test.
+
+This patch has not been proposed upstream but will be proposed for 2.4.
+
+diff --git a/tests/ofp-print.at b/tests/ofp-print.at
+index c25da53..07cea7b 100644
+--- a/tests/ofp-print.at
++++ b/tests/ofp-print.at
+@@ -1412,7 +1412,6 @@ AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
+  done
+  echo "  254: table254: $mid active=2
+                lookup=0, matched=0$tail") > expout
+-
+ (pad32="\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+  pad7="00 00 00 00 00 00 00 "
+  mid="00 00 00 0f ff ff ff ff \
+@@ -1421,27 +1420,26 @@ AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
+ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \
+ 00 00 00 07 00 00 00 00 00 0f 42 40 "
+  tail="00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"
+-
+- echo -n "03 13 7f 90 00 00 00 02 00 03 00 00 00 00 00 00 "
++ printf "03 13 7f 90 00 00 00 02 00 03 00 00 00 00 00 00 "
+ 
+  x=0
+  printf "%02x $pad7" $x
+  printf "%s$pad32" "classifier" | od -A n -t x1 -v -N 32 | tr '\n' ' '
+- echo -n "$mid 00 00 00 01  "
+- echo -n "00 00 00 00 00 01 23 76 00 00 00 00 00 01 9e 28 "
++ printf "$mid 00 00 00 01  "
++ printf "00 00 00 00 00 01 23 76 00 00 00 00 00 01 9e 28 "
+ 
+  x=1
+  while test $x -lt 254; do
+    printf "%02x $pad7" $x
+    printf "%s$pad32" "table$x" | od -A n -t x1 -v -N 32 | tr '\n' ' '
+-   echo -n "$mid 00 00 00 00 $tail "
++   printf "$mid 00 00 00 00 $tail "
+    x=`expr $x + 1`
+  done
+ 
+  x=254
+  printf "%02x $pad7" $x
+  printf "%s$pad32" "table$x" | od -A n -t x1 -v -N 32 | tr '\n' ' '
+- echo -n "$mid 00 00 00 02 $tail") > in
++ printf "$mid 00 00 00 02 $tail") > in
+ AT_CHECK([ovs-ofctl ofp-print "$(cat in)"], [0], [expout])
+ AT_CLEANUP
+ 
+diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
+index 1fcd937..97433a8 100644
+--- a/tests/ofproto-dpif.at
++++ b/tests/ofproto-dpif.at
+@@ -55,147 +55,156 @@ OVS_VSWITCHD_STOP
+ AT_CLEANUP
+ 
+ AT_SETUP([ofproto-dpif, active-backup bonding])
++#
++# Disabled - bonds not supported on Solaris
++#
+ # Create br0 with interfaces p1, p2 and p7, creating bond0 with p1 and p2
+ #    and br1 with interfaces p3, p4 and p8.
+ # toggle p1,p2 of bond0 up and down to test bonding in active-backup mode.
+-OVS_VSWITCHD_START(
+-  [add-bond br0 bond0 p1 p2 bond_mode=active-backup --\
+-   set interface p1 type=dummy options:pstream=punix:$OVS_RUNDIR/p1.sock ofport_request=1 -- \
+-   set interface p2 type=dummy options:pstream=punix:$OVS_RUNDIR/p2.sock ofport_request=2 -- \
+-   add-port br0 p7 -- set interface p7 ofport_request=7 type=dummy -- \
+-   add-br br1 -- \
+-   set bridge br1 other-config:hwaddr=aa:66:aa:66:00:00 -- \
+-   set bridge br1 datapath-type=dummy other-config:datapath-id=1234 \
+-                  fail-mode=secure -- \
+-   add-port br1 p3 -- set interface p3 type=dummy options:stream=unix:$OVS_RUNDIR/p1.sock ofport_request=3 -- \
+-   add-port br1 p4 -- set interface p4 type=dummy options:stream=unix:$OVS_RUNDIR/p2.sock ofport_request=4 -- \
+-   add-port br1 p8 -- set interface p8 ofport_request=8 type=dummy --])
+-AT_CHECK([ovs-appctl vlog/set dpif:dbg])
+-
+-AT_CHECK([ovs-ofctl add-flow br0 action=normal])
+-AT_CHECK([ovs-ofctl add-flow br1 action=normal])
+-ovs-appctl netdev-dummy/set-admin-state up
+-ovs-appctl time/warp 100
+-ovs-appctl netdev-dummy/set-admin-state p2 down
+-ovs-appctl time/stop
+-ovs-appctl time/warp 100
+-AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(7),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
+-AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(7),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
+-ovs-appctl time/warp 100
+-ovs-appctl netdev-dummy/set-admin-state p2 up
+-ovs-appctl netdev-dummy/set-admin-state p1 down
+-ovs-appctl time/warp 100
+-AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(7),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0d),eth_type(0x0800),ipv4(src=10.0.0.5,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
+-AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(7),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0e),eth_type(0x0800),ipv4(src=10.0.0.6,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
+-ovs-appctl time/warp 100
+-ovs-appctl time/warp 100
+-sleep 1
+-AT_CHECK([cat ovs-vswitchd.log | grep 'in_port([[348]])' | FILTER_FLOW_INSTALL | STRIP_XOUT], [0], [dnl
+-skb_priority(0),skb_mark(0/0),in_port(3),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff),icmp(type=8/0,code=0/0), actions: <del>
+-skb_priority(0),skb_mark(0/0),in_port(3),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.3/0.0.0.0,dst=10.0.0.4/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff),icmp(type=8/0,code=0/0), actions: <del>
+-skb_priority(0),skb_mark(0/0),in_port(4),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0d),eth_type(0x0800),ipv4(src=10.0.0.5/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff),icmp(type=8/0,code=0/0), actions: <del>
+-skb_priority(0),skb_mark(0/0),in_port(4),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0e),eth_type(0x0800),ipv4(src=10.0.0.6/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff),icmp(type=8/0,code=0/0), actions: <del>
+-skb_priority(0),skb_mark(0/0),in_port(4),eth(src=50:54:00:00:00:09,dst=ff:ff:ff:ff:ff:ff),eth_type(0x8035),arp(sip=0.0.0.0/0.0.0.0,tip=0.0.0.0/0.0.0.0,op=3/0,sha=50:54:00:00:00:09/00:00:00:00:00:00,tha=50:54:00:00:00:09/00:00:00:00:00:00), actions: <del>
+-skb_priority(0),skb_mark(0/0),in_port(4),eth(src=50:54:00:00:00:0b,dst=ff:ff:ff:ff:ff:ff),eth_type(0x8035),arp(sip=0.0.0.0/0.0.0.0,tip=0.0.0.0/0.0.0.0,op=3/0,sha=50:54:00:00:00:0b/00:00:00:00:00:00,tha=50:54:00:00:00:0b/00:00:00:00:00:00), actions: <del>
+-])
+-OVS_VSWITCHD_STOP
++#OVS_VSWITCHD_START(
++#  [add-bond br0 bond0 p1 p2 bond_mode=active-backup --\
++#   set interface p1 type=dummy options:pstream=punix:$OVS_RUNDIR/p1.sock ofport_request=1 -- \
++#   set interface p2 type=dummy options:pstream=punix:$OVS_RUNDIR/p2.sock ofport_request=2 -- \
++#   add-port br0 p7 -- set interface p7 ofport_request=7 type=dummy -- \
++#   add-br br1 -- \
++#   set bridge br1 other-config:hwaddr=aa:66:aa:66:00:00 -- \
++#   set bridge br1 datapath-type=dummy other-config:datapath-id=1234 \
++#                  fail-mode=secure -- \
++#   add-port br1 p3 -- set interface p3 type=dummy options:stream=unix:$OVS_RUNDIR/p1.sock ofport_request=3 -- \
++#   add-port br1 p4 -- set interface p4 type=dummy options:stream=unix:$OVS_RUNDIR/p2.sock ofport_request=4 -- \
++#   add-port br1 p8 -- set interface p8 ofport_request=8 type=dummy --])
++#AT_CHECK([ovs-appctl vlog/set dpif:dbg])
++#
++#AT_CHECK([ovs-ofctl add-flow br0 action=normal])
++#AT_CHECK([ovs-ofctl add-flow br1 action=normal])
++#ovs-appctl netdev-dummy/set-admin-state up
++#ovs-appctl time/warp 100
++#ovs-appctl netdev-dummy/set-admin-state p2 down
++#ovs-appctl time/stop
++#ovs-appctl time/warp 100
++#AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(7),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
++#AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(7),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
++#ovs-appctl time/warp 100
++#ovs-appctl netdev-dummy/set-admin-state p2 up
++#ovs-appctl netdev-dummy/set-admin-state p1 down
++#ovs-appctl time/warp 100
++#AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(7),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0d),eth_type(0x0800),ipv4(src=10.0.0.5,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
++#AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(7),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0e),eth_type(0x0800),ipv4(src=10.0.0.6,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
++#ovs-appctl time/warp 100
++#ovs-appctl time/warp 100
++#sleep 1
++#AT_CHECK([cat ovs-vswitchd.log | grep 'in_port([[348]])' | FILTER_FLOW_INSTALL | STRIP_XOUT], [0], [dnl
++#skb_priority(0),skb_mark(0/0),in_port(3),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff),icmp(type=8/0,code=0/0), actions: <del>
++#skb_priority(0),skb_mark(0/0),in_port(3),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.3/0.0.0.0,dst=10.0.0.4/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff),icmp(type=8/0,code=0/0), actions: <del>
++#skb_priority(0),skb_mark(0/0),in_port(4),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0d),eth_type(0x0800),ipv4(src=10.0.0.5/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff),icmp(type=8/0,code=0/0), actions: <del>
++#skb_priority(0),skb_mark(0/0),in_port(4),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0e),eth_type(0x0800),ipv4(src=10.0.0.6/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff),icmp(type=8/0,code=0/0), actions: <del>
++#skb_priority(0),skb_mark(0/0),in_port(4),eth(src=50:54:00:00:00:09,dst=ff:ff:ff:ff:ff:ff),eth_type(0x8035),arp(sip=0.0.0.0/0.0.0.0,tip=0.0.0.0/0.0.0.0,op=3/0,sha=50:54:00:00:00:09/00:00:00:00:00:00,tha=50:54:00:00:00:09/00:00:00:00:00:00), actions: <del>
++#skb_priority(0),skb_mark(0/0),in_port(4),eth(src=50:54:00:00:00:0b,dst=ff:ff:ff:ff:ff:ff),eth_type(0x8035),arp(sip=0.0.0.0/0.0.0.0,tip=0.0.0.0/0.0.0.0,op=3/0,sha=50:54:00:00:00:0b/00:00:00:00:00:00,tha=50:54:00:00:00:0b/00:00:00:00:00:00), actions: <del>
++#])
++#OVS_VSWITCHD_STOP
+ AT_CLEANUP
+ 
+ AT_SETUP([ofproto-dpif, balance-slb bonding])
++#
++# Disabled - bonds not supported on Solaris
++#
+ # Create br0 with interfaces bond0(p1, p2, p3) and p7,
+ #    and br1 with interfaces p4, p5, p6 and p8.
+ #    p1 <-> p4, p2 <-> p5, p3 <-> p6
+ # Send some traffic, make sure the traffic are spread based on source mac.
+-OVS_VSWITCHD_START(
+-  [add-bond br0 bond0 p1 p2 p3 bond_mode=balance-slb --\
+-   set interface p1 type=dummy options:pstream=punix:$OVS_RUNDIR/p1.sock ofport_request=1 -- \
+-   set interface p2 type=dummy options:pstream=punix:$OVS_RUNDIR/p2.sock ofport_request=2 -- \
+-   set interface p3 type=dummy options:pstream=punix:$OVS_RUNDIR/p3.sock ofport_request=3 -- \
+-   add-port br0 p7 -- set interface p7 ofport_request=7 type=dummy -- \
+-   add-br br1 -- \
+-   set bridge br1 other-config:hwaddr=aa:66:aa:66:00:00 -- \
+-   set bridge br1 datapath-type=dummy other-config:datapath-id=1234 \
+-                  fail-mode=secure -- \
+-   add-port br1 p4 -- set interface p4 type=dummy options:stream=unix:$OVS_RUNDIR/p1.sock ofport_request=4 -- \
+-   add-port br1 p5 -- set interface p5 type=dummy options:stream=unix:$OVS_RUNDIR/p2.sock ofport_request=5 -- \
+-   add-port br1 p6 -- set interface p6 type=dummy options:stream=unix:$OVS_RUNDIR/p3.sock ofport_request=6 -- \
+-   add-port br1 p8 -- set interface p8 ofport_request=8 type=dummy --])
+-
+-AT_CHECK([ovs-ofctl add-flow br0 action=normal])
+-AT_CHECK([ovs-ofctl add-flow br1 action=normal])
+-AT_CHECK([ovs-appctl netdev-dummy/set-admin-state up], 0, [OK
+-])
+-ovs-appctl netdev-dummy/set-admin-state up
+-ovs-appctl time/stop
+-ovs-appctl time/warp 100
+-(
+-for i in `seq 0 100 |xargs printf '%02x\n'`;
+-    do
+-    pkt="in_port(7),eth(src=50:54:00:00:00:$i,dst=50:54:00:00:01:00),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)"
+-    AT_CHECK([ovs-appctl netdev-dummy/receive p7 $pkt])
+-    done
+-)
+-ovs-appctl time/warp 100
+-AT_CHECK([ovs-appctl dpif/dump-flows br1 > br1_flows.txt])
++#OVS_VSWITCHD_START(
++#  [add-bond br0 bond0 p1 p2 p3 bond_mode=balance-slb --\
++#   set interface p1 type=dummy options:pstream=punix:$OVS_RUNDIR/p1.sock ofport_request=1 -- \
++#   set interface p2 type=dummy options:pstream=punix:$OVS_RUNDIR/p2.sock ofport_request=2 -- \
++#   set interface p3 type=dummy options:pstream=punix:$OVS_RUNDIR/p3.sock ofport_request=3 -- \
++#   add-port br0 p7 -- set interface p7 ofport_request=7 type=dummy -- \
++#   add-br br1 -- \
++#   set bridge br1 other-config:hwaddr=aa:66:aa:66:00:00 -- \
++#   set bridge br1 datapath-type=dummy other-config:datapath-id=1234 \
++#                  fail-mode=secure -- \
++#   add-port br1 p4 -- set interface p4 type=dummy options:stream=unix:$OVS_RUNDIR/p1.sock ofport_request=4 -- \
++#   add-port br1 p5 -- set interface p5 type=dummy options:stream=unix:$OVS_RUNDIR/p2.sock ofport_request=5 -- \
++#   add-port br1 p6 -- set interface p6 type=dummy options:stream=unix:$OVS_RUNDIR/p3.sock ofport_request=6 -- \
++#   add-port br1 p8 -- set interface p8 ofport_request=8 type=dummy --])
++
++#AT_CHECK([ovs-ofctl add-flow br0 action=normal])
++#AT_CHECK([ovs-ofctl add-flow br1 action=normal])
++#AT_CHECK([ovs-appctl netdev-dummy/set-admin-state up], 0, [OK
++#])
++#ovs-appctl netdev-dummy/set-admin-state up
++#ovs-appctl time/stop
++#ovs-appctl time/warp 100
++#(
++#for i in `seq 0 100 |xargs printf '%02x\n'`;
++#    do
++#    pkt="in_port(7),eth(src=50:54:00:00:00:$i,dst=50:54:00:00:01:00),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)"
++#    AT_CHECK([ovs-appctl netdev-dummy/receive p7 $pkt])
++#    done
++#)
++#ovs-appctl time/warp 100
++#AT_CHECK([ovs-appctl dpif/dump-flows br1 > br1_flows.txt])
+ # Make sure there is resonable distribution to all three ports.
+ # We don't want to make this check precise, in case hash function changes.
+-AT_CHECK([test `egrep 'in_port\(4\)' br1_flows.txt |wc -l` -gt 3])
+-AT_CHECK([test `egrep 'in_port\(5\)' br1_flows.txt |wc -l` -gt 3])
+-AT_CHECK([test `egrep 'in_port\(6\)' br1_flows.txt |wc -l` -gt 3])
+-OVS_VSWITCHD_STOP
++#AT_CHECK([test `egrep 'in_port\(4\)' br1_flows.txt |wc -l` -gt 3])
++#AT_CHECK([test `egrep 'in_port\(5\)' br1_flows.txt |wc -l` -gt 3])
++#AT_CHECK([test `egrep 'in_port\(6\)' br1_flows.txt |wc -l` -gt 3])
++#OVS_VSWITCHD_STOP
+ AT_CLEANUP
+ 
+ AT_SETUP([ofproto-dpif, balance-tcp bonding])
++#
++# Disabled - bonds not supported on Solaris
++#
+ # Create br0 with interfaces bond0(p1, p2, p3) and p7,
+ #    and br1 with interfaces bond1(p4, p5, p6) and p8.
+ #    bond0 <-> bond1
+ # Send some traffic, make sure the traffic are spread based on L4 headers.
+-OVS_VSWITCHD_START(
+-  [add-bond br0 bond0 p1 p2 p3 bond_mode=balance-tcp lacp=active \
+-        other-config:lacp-time=fast other-config:bond-rebalance-interval=0 --\
+-   set interface p1 type=dummy options:pstream=punix:$OVS_RUNDIR/p1.sock ofport_request=1 -- \
+-   set interface p2 type=dummy options:pstream=punix:$OVS_RUNDIR/p2.sock ofport_request=2 -- \
+-   set interface p3 type=dummy options:pstream=punix:$OVS_RUNDIR/p3.sock ofport_request=3 -- \
+-   add-port br0 p7 -- set interface p7 ofport_request=7 type=dummy -- \
+-   add-br br1 -- \
+-   set bridge br1 other-config:hwaddr=aa:66:aa:66:00:00 -- \
+-   set bridge br1 datapath-type=dummy other-config:datapath-id=1234 \
+-                  fail-mode=secure -- \
+-   add-bond br1 bond1 p4 p5 p6 bond_mode=balance-tcp lacp=active \
+-        other-config:lacp-time=fast other-config:bond-rebalance-interval=0 --\
+-   set interface p4 type=dummy options:stream=unix:$OVS_RUNDIR/p1.sock ofport_request=4 -- \
+-   set interface p5 type=dummy options:stream=unix:$OVS_RUNDIR/p2.sock ofport_request=5 -- \
+-   set interface p6 type=dummy options:stream=unix:$OVS_RUNDIR/p3.sock ofport_request=6 -- \
+-   add-port br1 p8 -- set interface p8 ofport_request=8 type=dummy --])
+-AT_CHECK([ovs-appctl netdev-dummy/set-admin-state up], 0, [OK
+-])
+-AT_CHECK([ovs-ofctl add-flow br0 action=normal])
+-AT_CHECK([ovs-ofctl add-flow br1 action=normal])
+-AT_CHECK([ovs-appctl upcall/disable-megaflows], [0], [megaflows disabled
+-], [])
+-sleep 1;
+-ovs-appctl time/stop
+-ovs-appctl time/warp 100
+-ovs-appctl lacp/show > lacp.txt
+-ovs-appctl bond/show > bond.txt
+-(
+-for i in `seq 0 255` ;
+-    do
+-    pkt="in_port(7),eth(src=50:54:00:00:00:05,dst=50:54:00:00:01:00),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=$i),tcp_flags(0x010)"
+-    AT_CHECK([ovs-appctl netdev-dummy/receive p7 $pkt])
+-    done
+-)
+-ovs-appctl time/warp 100
+-ovs-appctl time/warp 100
+-ovs-appctl time/warp 100
+-AT_CHECK([ovs-appctl dpif/dump-flows br0 |grep tcp > br0_flows.txt])
+-AT_CHECK([ovs-appctl dpif/dump-flows br1 |grep tcp > br1_flows.txt])
+-# Make sure there is resonable distribution to all three ports.
+-# We don't want to make this check precise, in case hash function changes.
+-AT_CHECK([test `grep in_port.4 br1_flows.txt |wc -l` -gt 24])
+-AT_CHECK([test `grep in_port.5 br1_flows.txt |wc -l` -gt 24])
+-AT_CHECK([test `grep in_port.6 br1_flows.txt |wc -l` -gt 24])
+-OVS_VSWITCHD_STOP()
++#OVS_VSWITCHD_START(
++#  [add-bond br0 bond0 p1 p2 p3 bond_mode=balance-tcp lacp=active \
++#        other-config:lacp-time=fast other-config:bond-rebalance-interval=0 --\
++#   set interface p1 type=dummy options:pstream=punix:$OVS_RUNDIR/p1.sock ofport_request=1 -- \
++#   set interface p2 type=dummy options:pstream=punix:$OVS_RUNDIR/p2.sock ofport_request=2 -- \
++#   set interface p3 type=dummy options:pstream=punix:$OVS_RUNDIR/p3.sock ofport_request=3 -- \
++#   add-port br0 p7 -- set interface p7 ofport_request=7 type=dummy -- \
++#   add-br br1 -- \
++#   set bridge br1 other-config:hwaddr=aa:66:aa:66:00:00 -- \
++#   set bridge br1 datapath-type=dummy other-config:datapath-id=1234 \
++#                  fail-mode=secure -- \
++#   add-bond br1 bond1 p4 p5 p6 bond_mode=balance-tcp lacp=active \
++#        other-config:lacp-time=fast other-config:bond-rebalance-interval=0 --\
++#   set interface p4 type=dummy options:stream=unix:$OVS_RUNDIR/p1.sock ofport_request=4 -- \
++#   set interface p5 type=dummy options:stream=unix:$OVS_RUNDIR/p2.sock ofport_request=5 -- \
++#   set interface p6 type=dummy options:stream=unix:$OVS_RUNDIR/p3.sock ofport_request=6 -- \
++#   add-port br1 p8 -- set interface p8 ofport_request=8 type=dummy --])
++#AT_CHECK([ovs-appctl netdev-dummy/set-admin-state up], 0, [OK
++#])
++#AT_CHECK([ovs-ofctl add-flow br0 action=normal])
++#AT_CHECK([ovs-ofctl add-flow br1 action=normal])
++#AT_CHECK([ovs-appctl upcall/disable-megaflows], [0], [megaflows disabled
++#], [])
++#sleep 1;
++#ovs-appctl time/stop
++#ovs-appctl time/warp 100
++#ovs-appctl lacp/show > lacp.txt
++#ovs-appctl bond/show > bond.txt
++#(
++#for i in `seq 0 255` ;
++#    do
++#    pkt="in_port(7),eth(src=50:54:00:00:00:05,dst=50:54:00:00:01:00),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=$i),tcp_flags(0x010)"
++#    AT_CHECK([ovs-appctl netdev-dummy/receive p7 $pkt])
++#    done
++#)
++#ovs-appctl time/warp 100
++#ovs-appctl time/warp 100
++#ovs-appctl time/warp 100
++#AT_CHECK([ovs-appctl dpif/dump-flows br0 |grep tcp > br0_flows.txt])
++#AT_CHECK([ovs-appctl dpif/dump-flows br1 |grep tcp > br1_flows.txt])
++## Make sure there is resonable distribution to all three ports.
++## We don't want to make this check precise, in case hash function changes.
++#AT_CHECK([test `grep in_port.4 br1_flows.txt |wc -l` -gt 24])
++#AT_CHECK([test `grep in_port.5 br1_flows.txt |wc -l` -gt 24])
++#AT_CHECK([test `grep in_port.6 br1_flows.txt |wc -l` -gt 24])
++#OVS_VSWITCHD_STOP()
+ AT_CLEANUP
+ 
+ AT_SETUP([ofproto-dpif - resubmit])
+diff --git a/tests/cfm.at b/tests/cfm.at
+index 06cab90..a8572f0 100644
+--- a/tests/cfm.at
++++ b/tests/cfm.at
+@@ -215,71 +215,71 @@ AT_CLEANUP
+ # test cfm_flap_count.
+ AT_SETUP([cfm - flap_count])
+ #Create 2 bridges connected by patch ports and enable cfm
+-OVS_VSWITCHD_START([add-br br1 -- \
+-                    set bridge br1 datapath-type=dummy \
+-                    other-config:hwaddr=aa:55:aa:56:00:00 -- \
+-                    add-port br1 p1 -- set Interface p1 type=patch \
+-                    options:peer=p0 -- \
+-                    add-port br0 p0 -- set Interface p0 type=patch \
+-                    options:peer=p1 -- \
+-                    set Interface p0 cfm_mpid=1 other_config:cfm_interval=100 other_config:cfm_extended=true -- \
+-                    set Interface p1 cfm_mpid=2 other_config:cfm_interval=100 other_config:cfm_extended=true])
+-
+-ovs-appctl time/stop
+-
+-# wait for a while to stablize cfm.
+-for i in `seq 0 100`; do ovs-appctl time/warp 100; done
+-CFM_CHECK_EXTENDED([p0], [1], [100], [up], [up], [100ms], [2], [up])
+-CFM_CHECK_EXTENDED([p1], [2], [100], [up], [up], [100ms], [1], [up])
+-
+-# turn cfm on p1 off, should increment the cfm_flap_count on p0.
+-AT_CHECK([ovs-vsctl remove interface p1 cfm_mpid 2])
+-for i in `seq 0 10`; do ovs-appctl time/warp 100; done
+-CFM_VSCTL_LIST_IFACE([p0], [cfm_flap_count], [cfm_flap_count      : 1])
+-CFM_VSCTL_LIST_IFACE([p1], [cfm_flap_count], [cfm_flap_count      : [[]]])
+-
+-# turn cfm on p1 on again, should increment the cfm_flap_count on p0.
+-AT_CHECK([ovs-vsctl set interface p1 cfm_mpid=2])
+-for i in `seq 0 10`; do ovs-appctl time/warp 100; done
+-CFM_VSCTL_LIST_IFACE([p0], [cfm_flap_count], [cfm_flap_count      : 2])
+-
+-OVS_VSWITCHD_STOP
++#OVS_VSWITCHD_START([add-br br1 -- \
++#                    set bridge br1 datapath-type=dummy \
++#                    other-config:hwaddr=aa:55:aa:56:00:00 -- \
++#                    add-port br1 p1 -- set Interface p1 type=patch \
++#                    options:peer=p0 -- \
++#                    add-port br0 p0 -- set Interface p0 type=patch \
++#                    options:peer=p1 -- \
++#                    set Interface p0 cfm_mpid=1 other_config:cfm_interval=100 other_config:cfm_extended=true -- \
++#                    set Interface p1 cfm_mpid=2 other_config:cfm_interval=100 other_config:cfm_extended=true])
++#
++#ovs-appctl time/stop
++#
++## wait for a while to stablize cfm.
++#for i in `seq 0 100`; do ovs-appctl time/warp 100; done
++#CFM_CHECK_EXTENDED([p0], [1], [100], [up], [up], [100ms], [2], [up])
++#CFM_CHECK_EXTENDED([p1], [2], [100], [up], [up], [100ms], [1], [up])
++#
++## turn cfm on p1 off, should increment the cfm_flap_count on p0.
++#AT_CHECK([ovs-vsctl remove interface p1 cfm_mpid 2])
++#for i in `seq 0 10`; do ovs-appctl time/warp 100; done
++#CFM_VSCTL_LIST_IFACE([p0], [cfm_flap_count], [cfm_flap_count      : 1])
++#CFM_VSCTL_LIST_IFACE([p1], [cfm_flap_count], [cfm_flap_count      : [[]]])
++#
++## turn cfm on p1 on again, should increment the cfm_flap_count on p0.
++#AT_CHECK([ovs-vsctl set interface p1 cfm_mpid=2])
++#for i in `seq 0 10`; do ovs-appctl time/warp 100; done
++#CFM_VSCTL_LIST_IFACE([p0], [cfm_flap_count], [cfm_flap_count      : 2])
++#
++#OVS_VSWITCHD_STOP
+ AT_CLEANUP
+ 
+ AT_SETUP([cfm - fault_override])
+-OVS_VSWITCHD_START([add-br br1 -- \
+-                    set bridge br1 datapath-type=dummy \
+-                    other-config:hwaddr=aa:55:aa:56:00:00 -- \
+-                    add-port br1 p1 -- set Interface p1 type=patch \
+-                    options:peer=p0 -- \
+-                    add-port br0 p0 -- set Interface p0 type=patch \
+-                    options:peer=p1 -- \
+-                    set Interface p0 cfm_mpid=1 other_config:cfm_interval=100 other_config:cfm_extended=true -- \
+-                    set Interface p1 cfm_mpid=2 other_config:cfm_interval=100 other_config:cfm_extended=true])
+-
+-ovs-appctl time/stop
+-# wait for a while to stablize cfm.
+-for i in `seq 0 100`; do ovs-appctl time/warp 100; done
+-CFM_CHECK_EXTENDED([p0], [1], [100], [up], [up], [100ms], [2], [up])
+-CFM_CHECK_EXTENDED([p1], [2], [100], [up], [up], [100ms], [1], [up])
+-AT_CHECK([ovs-appctl cfm/show p1 | grep 'fault_override'], [1], [ignore])
+-CFM_VSCTL_LIST_IFACE([p1], [cfm_fault_status], [cfm_fault_status    : [[]]])
+-
+-# set a fault and see that this is shown in the CFM module and the database
+-AT_CHECK([ovs-appctl cfm/set-fault p1 true], [0], [OK
+-])
+-AT_CHECK([ovs-appctl time/warp 100], [0], [ignore])
+-AT_CHECK([ovs-appctl cfm/show p1 | grep 'fault_override' | sed -e 's/MPID [[0-9]]*: extended //'], [0], [dnl
+-fault_override
+-])
+-CFM_VSCTL_LIST_IFACE([p1], [cfm_fault_status], [cfm_fault_status    : [[override]]])
+-
++#OVS_VSWITCHD_START([add-br br1 -- \
++#                    set bridge br1 datapath-type=dummy \
++#                    other-config:hwaddr=aa:55:aa:56:00:00 -- \
++#                    add-port br1 p1 -- set Interface p1 type=patch \
++#                    options:peer=p0 -- \
++#                    add-port br0 p0 -- set Interface p0 type=patch \
++#                    options:peer=p1 -- \
++#                    set Interface p0 cfm_mpid=1 other_config:cfm_interval=100 other_config:cfm_extended=true -- \
++#                    set Interface p1 cfm_mpid=2 other_config:cfm_interval=100 other_config:cfm_extended=true])
++#
++#ovs-appctl time/stop
++## wait for a while to stablize cfm.
++#for i in `seq 0 100`; do ovs-appctl time/warp 100; done
++#CFM_CHECK_EXTENDED([p0], [1], [100], [up], [up], [100ms], [2], [up])
++#CFM_CHECK_EXTENDED([p1], [2], [100], [up], [up], [100ms], [1], [up])
++#AT_CHECK([ovs-appctl cfm/show p1 | grep 'fault_override'], [1], [ignore])
++#CFM_VSCTL_LIST_IFACE([p1], [cfm_fault_status], [cfm_fault_status    : [[]]])
++#
++## set a fault and see that this is shown in the CFM module and the database
++#AT_CHECK([ovs-appctl cfm/set-fault p1 true], [0], [OK
++#])
++#AT_CHECK([ovs-appctl time/warp 100], [0], [ignore])
++#AT_CHECK([ovs-appctl cfm/show p1 | grep 'fault_override' | sed -e 's/MPID [[0-9]]*: extended //'], [0], [dnl
++#fault_override
++#])
++#CFM_VSCTL_LIST_IFACE([p1], [cfm_fault_status], [cfm_fault_status    : [[override]]])
++#
+ # reset and see that it returned to normal
+-AT_CHECK([ovs-appctl cfm/set-fault normal], [0], [OK
+-])
+-AT_CHECK([ovs-appctl time/warp 100], [0], [ignore])
+-AT_CHECK([ovs-appctl cfm/show p1 | grep 'fault_override'], [1], [ignore])
+-CFM_VSCTL_LIST_IFACE([p1], [cfm_fault_status], [cfm_fault_status    : [[]]])
+-
+-OVS_VSWITCHD_STOP
+-AT_CLEANUP
+\ No newline at end of file
++#AT_CHECK([ovs-appctl cfm/set-fault normal], [0], [OK
++#])
++#AT_CHECK([ovs-appctl time/warp 100], [0], [ignore])
++#AT_CHECK([ovs-appctl cfm/show p1 | grep 'fault_override'], [1], [ignore])
++#CFM_VSCTL_LIST_IFACE([p1], [cfm_fault_status], [cfm_fault_status    : [[]]])
++#
++#OVS_VSWITCHD_STOP
++AT_CLEANUP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openvswitch/test/results-64.master	Mon Nov 16 16:49:19 2015 -0500
@@ -0,0 +1,1890 @@
+make[1]: Entering directory `$(@D)'
+/usr/gnu/bin/make  check-recursive
+make[2]: Entering directory `$(@D)'
+Making check in datapath
+make[3]: Entering directory `$(@D)/datapath'
+make[4]: Entering directory `$(@D)/datapath'
+make[4]: Nothing to be done for `check-am'.
+make[4]: Leaving directory `$(@D)/datapath'
+make[3]: Leaving directory `$(@D)/datapath'
+make[3]: Entering directory `$(@D)'
+/usr/gnu/bin/make  tests/flowgen.pl tests/testpki-cacert.pem tests/testpki-cert.pem tests/testpki-privkey.pem tests/testpki-req.pem tests/testpki-cert2.pem tests/testpki-privkey2.pem tests/testpki-req2.pem
+make[4]: Entering directory `$(@D)'
+make[4]: Nothing to be done for `$(SOURCE_DIR)/tests/flowgen.pl'.
+rm -f tests/pki/stamp
+rm -rf tests/pki
+/bin/bash $(SOURCE_DIR)/utilities/ovs-pki.in --dir=tests/pki --log=tests/ovs-pki.log init
+Creating controllerca...
+Creating switchca...
+/bin/bash $(SOURCE_DIR)/utilities/ovs-pki.in --dir=tests/pki --log=tests/ovs-pki.log req+sign tests/pki/test
+
+
+/bin/bash $(SOURCE_DIR)/utilities/ovs-pki.in --dir=tests/pki --log=tests/ovs-pki.log req+sign tests/pki/test2
+
+
+: > tests/pki/stamp
+cp tests/pki/switchca/cacert.pem tests/testpki-cacert.pem
+cp tests/pki/test-cert.pem tests/testpki-cert.pem
+cp tests/pki/test-privkey.pem tests/testpki-privkey.pem
+cp tests/pki/test-req.pem tests/testpki-req.pem
+cp tests/pki/test2-cert.pem tests/testpki-cert2.pem
+cp tests/pki/test2-privkey.pem tests/testpki-privkey2.pem
+cp tests/pki/test2-req.pem tests/testpki-req2.pem
+make[4]: Leaving directory `$(@D)'
+/usr/gnu/bin/make  check-local
+make[4]: Entering directory `$(@D)'
+/bin/bash '$(SOURCE_DIR)/tests/testsuite' -C tests AUTOTEST_PATH=utilities:vswitchd:ovsdb:vtep:tests 
+## ----------------------------- ##
+## openvswitch 2.3.1 test suite. ##
+## ----------------------------- ##
+
+bfd
+
+  1: bfd - basic config on different bridges         ok
+  2: bfd - Verify tunnel down detection              ok
+  3: bfd - concatenated path down                    ok
+  4: bfd - Edit the Min Tx/Rx values                 ok
+  5: bfd - check_tnl_key                             FAILED (bfd.at:226)
+  6: bfd - bfd decay                                 ok
+  7: bfd - bfd forwarding_if_rx - bfd on one side    ok
+  8: bfd - bfd forwarding_if_rx - bfd on both sides  ok
+  9: bfd - bfd forwarding_if_rx - with bfd decay     ok
+ 10: bfd - bfd forwarding_if_rx - demand_rx_bfd      ok
+ 11: bfd - flap_count                                ok
+
+cfm
+
+ 12: cfm - check update ovsdb 1                      FAILED (cfm.at:50)
+ 13: cfm - check update ovsdb 2                      ok
+ 14: cfm - demand mode                               ok
+ 15: cfm - demand_rx_ccm                             ok
+ 16: cfm - flap_count                                ok
+ 17: cfm - fault_override                            ok
+
+lacp
+
+ 18: lacp - config                                   ok
+ 19: lacp - multi port config                        ok
+ 20: lacp - negotiation                              ok
+
+library unit tests
+
+ 21: test flow extractor                             ok
+ 22: test TCP/IP checksumming                        ok
+ 23: test hash functions                             ok
+ 24: test hash map                                   ok
+ 25: test hash index                                 ok
+ 26: test atomic operations                          ok
+ 27: test linked lists                               ok
+ 28: test packet library                             ok
+ 29: test SHA-1                                      ok
+ 30: test type properties                            ok
+ 31: test strtok_r bug fix                           FAILED (library.at:51)
+ 32: test byte order conversion                      ok
+ 33: test random number generator                    ok
+ 34: ctz() function                                  ok
+ 35: clz() function                                  ok
+ 36: round_up_pow2() function                        ok
+ 37: round_down_pow2() function                      ok
+ 38: count_1bits() function                          ok
+ 39: log_2_floor() function                          ok
+ 40: bitwise_copy() function                         ok
+ 41: bitwise_zero() function                         ok
+ 42: bitwise_one() function                          ok
+ 43: bitwise_is_all_zeros() function                 ok
+ 44: ovs_scan() function                             ok
+ 45: test unix socket, short pathname - C            ok
+ 46: test unix socket, long pathname - C             ok
+ 47: test unix socket, short pathname - Python       ok
+ 48: test unix socket, long pathname - Python        ok
+ 49: ovs_assert                                      ok
+ 50: snprintf                                        ok
+
+heap library
+
+ 51: heap library -- insert delete same order        ok
+ 52: heap library -- insert delete reverse order     ok
+ 53: heap library -- insert delete every order       ok
+ 54: heap library -- insert delete same order with dups ok
+ 55: heap library -- raw insert                      ok
+ 56: heap library -- raw delete                      ok
+
+bundle link selection
+
+ 57: hrw bundle link selection                       ok
+ 58: active_backup bundle link selection             ok
+ 59: hrw bundle single link selection                ok
+ 60: hrw bundle no link selection                    ok
+ 61: bundle action missing argument                  ok
+ 62: bundle action bad fields                        ok
+ 63: bundle action bad algorithm                     ok
+ 64: bundle action bad slave type                    ok
+ 65: bundle action bad slave delimiter               ok
+
+flow classifier unit tests
+
+ 66: flow classifier - empty                         ok
+ 67: flow classifier - destroy null                  ok
+ 68: flow classifier - single rule                   ok
+ 69: flow classifier - rule replacement              ok
+ 70: flow classifier - many rules in one list        ok
+ 71: flow classifier - many rules in one table       ok
+ 72: flow classifier - many rules in two tables      ok
+ 73: flow classifier - many rules in five tables     ok
+
+miniflow unit tests
+
+ 74: miniflow - miniflow                             ok
+ 75: miniflow - minimask_has_extra                   ok
+ 76: miniflow - minimask_combine                     ok
+
+flow classifier lookup segmentation
+
+ 77: flow classifier - lookup segmentation           ok
+
+flow classifier prefix lookup
+
+ 78: flow classifier - prefix lookup                 ok
+
+struct alignment checker unit tests
+
+ 79: check struct tail padding                       ok
+ 80: check struct internal alignment                 ok
+ 81: check struct declared size                      ok
+ 82: check wrong struct's declared size              ok
+
+daemon unit tests - C
+
+ 83: daemon                                          ok
+ 84: daemon --monitor                                ok
+ 85: daemon --detach                                 ok
+ 86: daemon --detach --monitor                       ok
+ 87: daemon --detach startup errors                  ok
+ 88: daemon --detach --monitor startup errors        ok
+
+daemon unit tests - Python
+
+ 89: daemon - Python                                 ok
+ 90: daemon --monitor - Python                       ok
+ 91: daemon --monitor restart exit code - Python     ok
+ 92: daemon --detach - Python                        ok
+ 93: daemon --detach --monitor - Python              ok
+ 94: daemon --detach startup errors - Python         ok
+ 95: daemon --detach --monitor startup errors - Python ok
+ 96: daemon --detach closes standard fds - Python    ok
+ 97: daemon --detach --monitor closes standard fds   ok
+
+OpenFlow actions
+
+ 98: OpenFlow 1.0 action translation                 ok
+ 99: OpenFlow 1.1 action translation                 ok
+100: OpenFlow 1.1 instruction translation            ok
+101: ofp-actions - inconsistent MPLS actions         ok
+
+ofp-print
+
+102: empty                                           ok
+103: too short                                       ok
+104: wrong OpenFlow version                          ok
+105: truncated message                               ok
+106: message only uses part of buffer                ok
+107: OFPT_HELLO - ordinary                           ok
+108: OFPT_HELLO with extra data                      ok
+109: OFPT_HELLO with version bitmap                  ok
+110: OFPT_HELLO with version bitmap and extra data   ok
+111: OFPT_HELLO with higher than supported version   ok
+112: OFPT_HELLO with contradictory version bitmaps   ok
+113: OFPT_ECHO_REQUEST, empty payload                ok
+114: OFPT_ECHO_REQUEST, 5-byte payload               ok
+115: OFPT_ECHO_REPLY, empty payload                  ok
+116: OFPT_ECHO_REPLY, 5-byte payload                 ok
+117: OFPT_FEATURES_REQUEST                           ok
+118: OFPT_FEATURES_REPLY - OF1.0                     ok
+119: OFPT_FEATURES_REPLY cut off mid-port - OF1.0    ok
+120: OFPT_FEATURES_REPLY - OF1.1                     ok
+121: OFPT_FEATURES_REPLY cut off mid-port - OF1.1    ok
+122: OFPT_FEATURES_REPLY - OF1.2                     ok
+123: OFPT_FEATURES_REPLY cut off mid-port - OF1.2    ok
+124: OFPT_FEATURES_REPLY - OF1.3                     ok
+125: OFPT_FEATURES_REPLY - with auxiliary_id - OF1.3 ok
+126: OFPT_GET_CONFIG_REQUEST                         ok
+127: OFPT_GET_CONFIG_REPLY, most common form         ok
+128: OFPT_GET_CONFIG_REPLY, frags and miss_send_len  ok
+129: OFPT_PACKET_IN - OF1.0                          ok
+130: OFPT_PACKET_IN - OF1.0, with hex output of packet data ok
+131: OFPT_PACKET_IN - OF1.1                          ok
+132: OFPT_PACKET_IN - OF1.2                          ok
+133: OFPT_PACKET_IN - OF1.2, with hex output of packet data ok
+134: OFPT_PACKET_IN - OF1.3                          ok
+135: OFPT_PACKET_IN - OF1.3, with hex output of packet data ok
+136: OFPT_FLOW_REMOVED - OF1.0                       ok
+137: OFPT_FLOW_REMOVED - OF1.2                       ok
+138: OFPT_FLOW_REMOVED - OF1.3                       ok
+139: OFPT_PORT_STATUS - OF1.0                        ok
+140: OFPT_PORT_STATUS - OF1.1                        ok
+141: OFPT_PORT_STATUS - OF1.4                        ok
+142: OFPT_PACKET_OUT - OF1.0                         ok
+143: OFPT_PACKET_OUT - OF1.0, with packet            ok
+144: OFPT_PACKET_OUT - OF1.0, with hex output of packet data ok
+145: OFPT_PACKET_OUT - OF1.1                         ok
+146: OFPT_PACKET_OUT - OF1.1, with packet            ok
+147: OFPT_FLOW_MOD - OF1.0 - low verbosity           ok
+148: OFPT_FLOW_MOD - OF1.1 - low verbosity           ok
+149: OFPT_FLOW_MOD - OF1.2 - low verbosity           ok
+150: OFPT_FLOW_MOD - OF1.0 - high verbosity          ok
+151: OFPT_FLOW_MOD - OF1.2 - low verbosity           ok
+152: OFPT_FLOW_MOD - OF1.3 - flags - low verbosity   ok
+153: OFPT_FLOW_MOD - OF1.2 - set-field ip_src        ok
+154: OFPT_FLOW_MOD - OF1.2 - set-field ip_dst        ok
+155: OFPT_FLOW_MOD - OF1.2 - set-field sctp_src      ok
+156: OFPT_FLOW_MOD - OF1.2 - set-field sctp_dst      ok
+157: OFPT_FLOW_MOD - OF1.3 - meter                   ok
+158: OFPT_FLOW reply - OF1.2 - set-field ip_src      ok
+159: OFPT_FLOW reply - OF1.2 - set-field ip_dst      ok
+160: OFPT_FLOW reply - OF1.2 - set-field sctp_src    ok
+161: OFPT_FLOW reply - OF1.2 - set-field sctp_dst    ok
+162: OFPT_PORT_MOD - OF1.0                           ok
+163: OFPT_PORT_MOD - OF1.1                           ok
+164: OFPT_PORT_MOD - OF1.2                           ok
+165: OFPT_PORT_MOD - OF1.3                           ok
+166: OFPT_PORT_MOD - OF1.4                           ok
+167: OFPT_TABLE_MOD - OF1.1                          ok
+168: OFPT_TABLE_MOD - OF1.2                          ok
+169: OFPT_TABLE_MOD - OF1.3                          ok
+170: OFPT_TABLE_MOD - OF1.4                          ok
+171: OFPST_DESC request                              ok
+172: OFPST_DESC reply                                ok
+173: OFPST_FLOW request - OF1.0                      ok
+174: OFPST_FLOW request - OF1.2                      ok
+175: OFPST_FLOW request - OF1.3                      ok
+176: OFPST_FLOW reply - OF1.0                        ok
+177: OFPST_FLOW reply - OF1.2                        ok
+178: OFPST_AGGREGATE request - OF1.0                 ok
+179: OFPST_AGGREGATE request - OF1.2                 ok
+180: OFPST_AGGREGATE request - OF1.3                 ok
+181: OFPST_AGGREGATE reply - OF1.0                   ok
+182: OFPST_AGGREGATE reply - OF1.2                   ok
+183: OFPST_AGGREGATE reply - OF1.3                   ok
+184: OFPST_TABLE request - OF1.0                     ok
+185: OFPST_TABLE request - OF1.1                     ok
+186: OFPST_TABLE request - OF1.2                     ok
+187: OFPST_TABLE request - OF1.3                     ok
+188: OFPST_TABLE reply - OF1.0                       ok
+189: OFPST_TABLE reply - OF1.2                       ok
+190: OFPST_TABLE reply - OF1.3                       ok
+191: OFPST_PORT request - 1.0                        ok
+192: OFPST_PORT request - 1.1                        ok
+193: OFPST_PORT request - 1.2                        ok
+194: OFPST_PORT request - 1.3                        ok
+195: OFPST_PORT reply - OF1.0                        ok
+196: OFPST_PORT reply - OF1.2                        ok
+197: OFPST_PORT reply - OF1.3                        ok
+198: OFPST_PORT reply - OF1.4                        ok
+199: OFPST_QUEUE request - OF1.0                     ok
+200: OFPST_QUEUE request - OF1.1                     ok
+201: OFPST_QUEUE request - OF1.2                     ok
+202: OFPST_QUEUE request - OF1.3                     ok
+203: OFPST_QUEUE reply - OF1.0                       ok
+204: OFPST_QUEUE reply - OF1.1                       ok
+205: OFPST_QUEUE reply - OF1.2                       ok
+206: OFPST_QUEUE reply - OF1.3                       ok
+207: OFPST_QUEUE reply - OF1.4                       ok
+208: OFPST_GROUP request                             ok
+209: OFPST_GROUP reply - OF1.1                       ok
+210: OFPST_GROUP reply - OF1.3                       ok
+211: OFPST_GROUP_DESC request - OF1.1                ok
+212: OFPST_GROUP_DESC request - OF1.5                ok
+213: OFPST_GROUP_DESC reply                          ok
+214: OFPST_GROUP_FEATURES request                    ok
+215: OFPST_GROUP_FEATURES reply                      ok
+216: OFPST_PORT_DESC request - OF1.0                 ok
+217: OFPST_PORT_DESC request - OF1.5                 ok
+218: OFPST_PORT_DESC reply - OF1.0                   ok
+219: OFPST_PORT_DESC reply - OF1.4                   ok
+220: OFPT_METER_MOD request - OF1.3                  ok
+221: OFPT_METER_MOD request - bad band - OF1.3       ok
+222: OFPT_METER_MOD request - bad command - OF1.3    ok
+223: OFPT_METER_MOD request - bad flags - OF1.3      ok
+224: OFPST_METER request - OF1.3                     ok
+225: OFPST_METER_CONFIG request - OF1.3              ok
+226: OFPST_METER_FEATURES request - OF1.3            ok
+227: OFPST_METER_FEATURES reply - OF1.3              ok
+228: OFPST_METER_CONFIG reply - OF1.3                ok
+229: OFPST_METER reply - OF1.3                       ok
+230: OFPST_TABLE_FEATURES request - OF1.3            ok
+231: OFPT_BARRIER_REQUEST - OF1.0                    ok
+232: OFPT_BARRIER_REQUEST - OF1.1                    ok
+233: OFPT_BARRIER_REQUEST - OF1.2                    ok
+234: OFPT_BARRIER_REQUEST - OF1.3                    ok
+235: OFPT_BARRIER_REPLY - OF1.0                      ok
+236: OFPT_BARRIER_REPLY - OF1.1                      ok
+237: OFPT_BARRIER_REPLY - OF1.2                      ok
+238: OFPT_BARRIER_REPLY - OF1.3                      ok
+239: OFPT_QUEUE_GET_CONFIG_REQUEST - OF1.0           ok
+240: OFPT_QUEUE_GET_CONFIG_REQUEST - OF1.2           ok
+241: OFPT_QUEUE_GET_CONFIG_REPLY - OF1.0             ok
+242: OFPT_QUEUE_GET_CONFIG_REPLY - OF1.2             ok
+243: OFPT_SET_ASYNC - OF1.3                          ok
+244: OFPT_ROLE_REQUEST - OF1.2                       ok
+245: OFPT_ROLE_REQUEST - nochange - OF1.2            ok
+246: NXT_ROLE_REQUEST                                ok
+247: OFPT_ROLE_REPLY - OF1.2                         ok
+248: NXT_ROLE_REPLY                                  ok
+249: OFP_ROLE_STATUS - master, experimenter - OF1.4  ok
+250: OFP_ROLE_STATUS - master, config - OF1.4        ok
+251: OFP_ROLE_STATUS - master, config,generation - OF1.4 ok
+252: NXT_SET_PACKET_IN                               ok
+253: NXT_PACKET_IN                                   ok
+254: NXT_PACKET_IN, with hex output of packet data   ok
+255: NXT_SET_ASYNC_CONFIG                            ok
+256: NXT_SET_CONTROLLER_ID                           ok
+257: NXT_FLOW_MONITOR_CANCEL                         ok
+258: NXT_FLOW_MONITOR_PAUSED                         ok
+259: NXT_FLOW_MONITOR_RESUMED                        ok
+260: NXT_SET_FLOW_FORMAT                             ok
+261: NXT_FLOW_MOD, low verbosity                     ok
+262: NXT_FLOW_MOD, high verbosity                    ok
+263: OFPT_GROUP_MOD                                  ok
+264: NXT_FLOW_REMOVED                                ok
+265: NXT_FLOW_MOD_TABLE_ID                           ok
+266: NXST_FLOW request                               ok
+267: NXST_FLOW reply                                 ok
+268: NXST_AGGREGATE request                          ok
+269: NXST_AGGREGATE reply                            ok
+270: NXST_FLOW_MONITOR request                       ok
+271: NXST_FLOW_MONITOR reply                         ok
+272: OFPT_BUNDLE_CONTROL - OPEN_REQUEST              ok
+273: OFPT_BUNDLE_CONTROL - OPEN_REQUEST              ok
+274: OFPT_BUNDLE_CONTROL - OPEN_REQUEST              ok
+275: OFPT_BUNDLE_CONTROL - OPEN_REPLY                ok
+276: OFPT_BUNDLE_CONTROL - CLOSE_REQUEST             ok
+277: OFPT_BUNDLE_CONTROL - CLOSE_REPLY               ok
+278: OFPT_BUNDLE_CONTROL - COMMIT_REQUEST            ok
+279: OFPT_BUNDLE_CONTROL - COMMIT_REPLY              ok
+280: OFPT_BUNDLE_CONTROL - DISCARD_REQUEST           ok
+281: OFPT_BUNDLE_CONTROL - DISCARD_REPLY             ok
+282: OFPT_BUNDLE_ADD_MESSAGE - OFPT_HELLO            ok
+
+OpenFlow utilities
+
+283: encoding hellos                                 ok
+
+ofp-errors tests
+
+284: OFPT_ERROR with type OFPET_HELLO_FAILED - OF1.0 ok
+285: OFPT_ERROR with type OFPET_HELLO_FAILED - OF1.1 ok
+286: OFPT_ERROR with type OFPET_BAD_REQUEST - OF1.0  ok
+287: OFPT_ERROR prints type of truncated inner messages ok
+288: OFPT_ERROR with code OFPBMC_BAD_PREREQ - OF1.0  ok
+289: OFPT_ERROR with code OFPBMC_BAD_PREREQ - OF1.1  ok
+290: OFPT_ERROR with type OFPFMFC_OVERLAP - OF1.0    ok
+291: OFPT_ERROR with type OFPBIC_UNSUP_INST - OF1.1  ok
+292: encoding OFPBIC_* experimenter errors           ok
+293: encoding errors extension that became official  ok
+294: decoding OFPBIC_* experimenter errors           ok
+295: decoding experimenter errors                    ok
+296: encoding experimenter errors                    ok
+
+ovs-ofctl
+
+297: ovs-ofctl parse-flows choice of protocol        ok
+298: ovs-ofctl parse-flows (OpenFlow 1.0)            ok
+299: ovs-ofctl parse-flows (OpenFlow 1.1)            ok
+300: ovs-ofctl parse-flows (OpenFlow 1.2)            ok
+301: ovs-ofctl parse-flow with invalid mask          ok
+302: ovs-ofctl action inconsistency (OpenFlow 1.1)   ok
+303: ovs-ofctl parse-flows (With Tunnel-Parameters)  ok
+304: ovs-ofctl parse-flows (skb_priority)            ok
+305: ovs-ofctl parse-flows (NXM)                     ok
+306: ovs-ofctl -F nxm parse-flows                    ok
+307: ovs-ofctl -F nxm -mmm parse-flows               ok
+308: ovs-ofctl parse-nx-match                        ok
+309: ovs-ofctl parse-ofp10-match                     ok
+310: ovs-ofctl parse-ofp11-match                     ok
+311: ovs-ofctl parse-nx-match loose                  ok
+312: ovs-ofctl parse-oxm                             ok
+313: ovs-ofctl parse-oxm loose                       ok
+314: check TCP flags expression in OXM and NXM       ok
+315: ovs-ofctl check-vlan                            ok
+316: ovs-ofctl -F option and NXM features            ok
+317: ovs-ofctl -F option with flow_mods              ok
+318: ovs-ofctl dump-flows honors -F option           ok
+319: ovs-ofctl dump-flows rejects bad -F option      ok
+320: ovs-ofctl add-flow reports non-normalized flows ok
+321: ovs-ofctl dump-flows with sorting               ok
+322: ovs-ofctl diff-flows                            ok
+323: ovs-ofctl diff-flows - suppress false differences ok
+324: ovs-ofctl -F and -O interaction                 ok
+325: ovs-ofctl ofp-parse                             ok
+326: tcp flags - filtering                           ok
+
+datapath parsing and formatting
+
+327: OVS datapath key parsing and formatting - valid forms ok
+328: OVS datapath wildcarded key parsing and formatting - valid forms ok
+329: OVS datapath wildcarded key filtering.          ok
+330: OVS datapath actions parsing and formatting - valid forms ok
+
+multipath link selection
+
+331: modulo_n multipath link selection               ok
+332: hash_threshold multipath link selection         ok
+333: hrw multipath link selection                    ok
+334: iter_hash multipath link selection              ok
+335: multipath action missing argument               ok
+336: multipath action bad fields                     ok
+337: multipath action bad algorithm                  ok
+338: multipath action bad n_links                    ok
+339: multipath action destination too narrow         ok
+
+learning action
+
+340: learning action - parsing and formatting        ok
+341: learning action - parsing and formatting - illegal in_port_oxm ok
+342: learning action - parsing and formatting - OXM  ok
+343: learning action - examples                      ok
+344: learning action - satisfied prerequisites       ok
+345: learning action - invalid prerequisites         ok
+346: learning action - standard VLAN+MAC learning    ok
+347: learning action - learn refreshes hard_age      ok
+348: learning action - TCPv4 port learning           ok
+349: learning action - TCPv6 port learning           ok
+350: learning action - self-modifying flow           ok
+351: learning action - self-modifying flow with idle_timeout ok
+352: learning action - self-modifying flow with hard_timeout ok
+353: learning action - fin_timeout feature           ok
+
+vconn library -- unix class
+
+354: unix vconn - refuse connection                  ok
+355: unix vconn - accept then close                  ok
+356: unix vconn - read hello                         ok
+357: unix vconn - send plain hello                   ok
+358: unix vconn - send long hello                    ok
+359: unix vconn - send echo hello                    ok
+360: unix vconn - send short hello                   ok
+361: unix vconn - send invalid version hello         ok
+
+vconn library -- tcp class
+
+362: tcp vconn - refuse connection                   ok
+363: tcp vconn - accept then close                   ok
+364: tcp vconn - read hello                          ok
+365: tcp vconn - send plain hello                    ok
+366: tcp vconn - send long hello                     ok
+367: tcp vconn - send echo hello                     ok
+368: tcp vconn - send short hello                    ok
+369: tcp vconn - send invalid version hello          ok
+
+vconn library -- ssl class
+
+370: ssl vconn - refuse connection                   ok
+371: ssl vconn - accept then close                   ok
+372: ssl vconn - read hello                          ok
+373: ssl vconn - send plain hello                    ok
+374: ssl vconn - send long hello                     ok
+375: ssl vconn - send echo hello                     ok
+376: ssl vconn - send short hello                    ok
+377: ssl vconn - send invalid version hello          ok
+
+test dir_name and base_name functions
+
+378: components of "/usr/lib" are "/usr", "lib"      ok
+379: components of "/usr/" are "/", "usr"            ok
+380: components of "usr" are ".", "usr"              ok
+381: components of "/" are "/", "/"                  ok
+382: components of "." are ".", "."                  ok
+383: components of ".." are ".", ".."                ok
+384: components of "//" are "//", "//"               ok
+385: components of "//foo" are "//", "foo"           ok
+386: components of "" are ".", "."                   ok
+387: components of "dir/file" are "dir", "file"      ok
+388: components of "dir/file/" are "dir", "file"     ok
+389: components of "dir/file//" are "dir", "file"    ok
+390: components of "///foo" are "/", "foo"           ok
+
+test follow_symlinks function
+
+391: follow_symlinks - relative symlinks             ok
+392: follow_symlinks - absolute symlinks             ok
+393: follow_symlinks - symlinks to directories       ok
+394: follow_symlinks - nonexistent targets           ok
+395: follow_symlinks - regular files                 ok
+396: follow_symlinks - device targets                skipped (file_name.at:98)
+397: follow_symlinks - nonexistent files             ok
+398: follow_symlinks - symlink loop                  ok
+
+AES-128 unit tests
+
+399: wikipedia test vector 1                         ok
+400: wikipedia test vector 2                         ok
+401: NIST KAT ECBKeySbox128e vector 0                ok
+402: NIST KAT ECBKeySbox128e vector 1                ok
+403: NIST KAT ECBKeySbox128e vector 2                ok
+404: NIST KAT ECBKeySbox128e vector 3                ok
+405: NIST KAT ECBKeySbox128e vector 4                ok
+406: NIST KAT ECBKeySbox128e vector 5                ok
+407: NIST KAT ECBKeySbox128e vector 6                ok
+408: NIST KAT ECBKeySbox128e vector 7                ok
+409: NIST KAT ECBKeySbox128e vector 8                ok
+410: NIST KAT ECBKeySbox128e vector 9                ok
+411: NIST KAT ECBKeySbox128e vector 10               ok
+412: NIST KAT ECBKeySbox128e vector 11               ok
+413: NIST KAT ECBKeySbox128e vector 12               ok
+414: NIST KAT ECBKeySbox128e vector 13               ok
+415: NIST KAT ECBKeySbox128e vector 14               ok
+416: NIST KAT ECBKeySbox128e vector 15               ok
+417: NIST KAT ECBKeySbox128e vector 16               ok
+418: NIST KAT ECBKeySbox128e vector 17               ok
+419: NIST KAT ECBKeySbox128e vector 18               ok
+420: NIST KAT ECBKeySbox128e vector 19               ok
+421: NIST KAT ECBKeySbox128e vector 20               ok
+
+unixctl
+
+422: unixctl ovs-vswitchd exit - Python              ok
+423: unixctl ovs-vswitchd help - Python              ok
+424: unixctl ovs-vswitchd arguments - Python         ok
+425: unixctl bad target - Python                     ok
+426: unixctl server - Python                         ok
+427: unixctl server errors - Python                  ok
+
+UUID unit tests
+
+428: UUID generation, parsing, serialization         ok
+
+JSON -- arrays
+
+429: empty array - C                                 ok
+430: empty array - Python                            ok
+431: single-element array - C                        ok
+432: single-element array - Python                   ok
+433: 2-element array - C                             ok
+434: 2-element array - Python                        ok
+435: many-element array - C                          ok
+436: many-element array - Python                     ok
+437: missing comma - C                               ok
+438: missing comma - Python                          ok
+439: trailing comma not allowed - C                  ok
+440: trailing comma not allowed - Python             ok
+441: doubled comma not allowed - C                   ok
+442: doubled comma not allowed - Python              ok
+
+JSON -- strings
+
+443: empty string - C                                ok
+444: empty string - Python                           ok
+445: 1-character strings - C                         ok
+446: 1-character strings - Python                    ok
+447: escape sequences - C                            ok
+448: escape sequences - Python                       ok
+449: Unicode escape sequences - C                    ok
+450: Unicode escape sequences - Python               ok
+451: surrogate pairs - C                             ok
+452: surrogate pairs - Python                        expected failure (json.at:93)
+453: a string by itself is not valid JSON - C        ok
+454: a string by itself is not valid JSON - Python   ok
+455: end of line in quoted string - C                ok
+456: end of line in quoted string - Python           ok
+457: formfeed in quoted string - C                   ok
+458: formfeed in quoted string - Python              ok
+459: bad escape in quoted string - C                 ok
+460: bad escape in quoted string - Python            ok
+461: \u must be followed by 4 hex digits (1) - C     ok
+462: \u must be followed by 4 hex digits (1) - Python ok
+463: \u must be followed by 4 hex digits (2) - C     ok
+464: \u must be followed by 4 hex digits (2) - Python ok
+465: isolated leading surrogate not allowed - C      ok
+466: isolated leading surrogate not allowed - Python ok
+467: surrogatess must paired properly - C            ok
+468: surrogatess must paired properly - Python       ok
+469: null bytes not allowed - C                      ok
+470: null bytes not allowed - Python                 ok
+471: properly quoted backslash at end of string - C  ok
+472: properly quoted backslash at end of string - Python ok
+473: stray backslash at end of string - C            ok
+474: stray backslash at end of string - Python       ok
+475: end of input in quoted string - C               ok
+476: end of input in quoted string - Python          ok
+
+JSON -- objects
+
+477: empty object - C                                ok
+478: empty object - Python                           ok
+479: simple object - C                               ok
+480: simple object - Python                          ok
+481: bad value - C                                   ok
+482: bad value - Python                              ok
+483: missing colon - C                               ok
+484: missing colon - Python                          ok
+485: missing comma - C                               ok
+486: missing comma - Python                          ok
+487: trailing comma not allowed - C                  ok
+488: trailing comma not allowed - Python             ok
+489: doubled comma not allowed - C                   ok
+490: doubled comma not allowed - Python              ok
+491: names must be strings - C                       ok
+492: names must be strings - Python                  ok
+
+JSON -- literal names
+
+493: null - C                                        ok
+494: null - Python                                   ok
+495: false - C                                       ok
+496: false - Python                                  ok
+497: true - C                                        ok
+498: true - Python                                   ok
+499: a literal by itself is not valid JSON - C       ok
+500: a literal by itself is not valid JSON - Python  ok
+501: nullify is invalid - C                          ok
+502: nullify is invalid - Python                     ok
+503: nubs is invalid - C                             ok
+504: nubs is invalid - Python                        ok
+505: xxx is invalid - C                              ok
+506: xxx is invalid - Python                         ok
+
+JSON -- numbers
+
+507: integers expressed as reals - C                 ok
+508: integers expressed as reals - Python            ok
+509: large integers - C                              ok
+510: large integers - Python                         ok
+511: large integers expressed as reals - C           ok
+512: large integers expressed as reals - Python      ok
+513: large integers that overflow to reals - C       ok
+514: large integers that overflow to reals - Python  ok
+515: negative zero - C                               ok
+516: negative zero - Python                          ok
+517: reals - C                                       ok
+518: reals - Python                                  ok
+519: scientific notation - C                         ok
+520: scientific notation - Python                    ok
+521: +/- DBL_MAX - C                                 ok
+522: +/- DBL_MAX - Python                            ok
+523: negative reals - C                              ok
+524: negative reals - Python                         ok
+525: negative scientific notation - C                ok
+526: negative scientific notation - Python           ok
+527: 1e-9999 underflows to 0 - C                     ok
+528: 1e-9999 underflows to 0 - Python                ok
+529: a number by itself is not valid JSON - C        ok
+530: a number by itself is not valid JSON - Python   ok
+531: leading zeros not allowed - C                   ok
+532: leading zeros not allowed - Python              ok
+533: 1e9999 is too big - C                           ok
+534: 1e9999 is too big - Python                      ok
+535: exponent bigger than INT_MAX - C                ok
+536: exponent bigger than INT_MAX - Python           ok
+537: decimal point must be followed by digit - C     ok
+538: decimal point must be followed by digit - Python ok
+539: exponent must contain at least one digit (1) - C ok
+540: exponent must contain at least one digit (1) - Python ok
+541: exponent must contain at least one digit (2) - C ok
+542: exponent must contain at least one digit (2) - Python ok
+543: exponent must contain at least one digit (3) - C ok
+544: exponent must contain at least one digit (3) - Python ok
+
+JSON -- RFC 4627 examples
+
+545: RFC 4267 object example - C                     ok
+546: RFC 4267 object example - Python                ok
+547: RFC 4267 array example - C                      ok
+548: RFC 4267 array example - Python                 ok
+
+JSON -- pathological cases
+
+549: trailing garbage - C                            ok
+550: trailing garbage - Python                       ok
+551: formfeeds are not valid white space - C         ok
+552: formfeeds are not valid white space - Python    ok
+553: ';' is not a valid token - C                    ok
+554: ';' is not a valid token - Python               ok
+555: arrays nesting too deep - C                     ok
+556: arrays nesting too deep - Python                ok
+557: objects nesting too deep - C                    ok
+558: objects nesting too deep - Python               ok
+559: input may not be empty                          ok
+
+JSON -- multiple inputs
+
+560: multiple adjacent objects - C                   ok
+561: multiple adjacent objects - Python              ok
+562: multiple space-separated objects - C            ok
+563: multiple space-separated objects - Python       ok
+564: multiple objects on separate lines - C          ok
+565: multiple objects on separate lines - Python     ok
+566: multiple objects and arrays - C                 ok
+567: multiple objects and arrays - Python            ok
+568: garbage between multiple objects - C            ok
+569: garbage between multiple objects - Python       ok
+570: garbage after multiple objects - C              ok
+571: garbage after multiple objects - Python         ok
+
+JSON-RPC - C
+
+572: JSON-RPC request and successful reply           ok
+573: JSON-RPC request and error reply                ok
+574: JSON-RPC notification                           ok
+
+JSON-RPC - Python
+
+575: JSON-RPC request and successful reply - Python  ok
+576: JSON-RPC request and error reply - Python       ok
+577: JSON-RPC notification - Python                  ok
+
+tunnel
+
+578: tunnel - input                                  FAILED (tunnel.at:4)
+579: tunnel - ECN decapsulation                      FAILED (tunnel.at:65)
+580: tunnel - output                                 FAILED (tunnel.at:112)
+581: tunnel - ToS and TTL inheritance                FAILED (tunnel.at:144)
+582: tunnel - set_tunnel                             FAILED (tunnel.at:182)
+583: tunnel - key                                    FAILED (tunnel.at:216)
+584: tunnel - key match                              FAILED (tunnel.at:265)
+585: tunnel - VXLAN                                  ok
+586: tunnel - LISP                                   FAILED (tunnel.at:326)
+587: tunnel - different VXLAN UDP port               ok
+588: ofproto-dpif - set_field - tun_src/tun_dst/tun_id FAILED (tunnel.at:367)
+
+lockfile unit tests
+
+589: lock and unlock                                 ok
+590: lock and unlock twice                           ok
+591: lock blocks same process                        ok
+592: lock blocks same process twice                  ok
+593: lock blocks other process                       ok
+594: lock twice blocks other process                 ok
+595: lock and unlock allows other process            ok
+596: lock multiple                                   ok
+597: lock symlink                                    ok
+598: lock symlink to dir                             ok
+
+reconnect library
+
+599: nothing happens if not enabled - C              ok
+600: nothing happens if not enabled - Python         ok
+601: quick connect, idle disconnect - C              ok
+602: quick connect, idle disconnect - Python         ok
+603: slow connect, idle disconnect - C               ok
+604: slow connect, idle disconnect - Python          ok
+605: connect backs off - C                           ok
+606: connect backs off - Python                      ok
+607: connections with no data preserve backoff - C   ok
+608: connections with no data preserve backoff - Python ok
+609: brief connection preserves backoff - C          ok
+610: brief connection preserves backoff - Python     ok
+611: brief connection with data preserves backoff - C ok
+612: brief connection with data preserves backoff - Python ok
+613: long connection resets backoff - C              ok
+614: long connection resets backoff - Python         ok
+615: connection attempt fails quickly - C            ok
+616: connection attempt fails quickly - Python       ok
+617: max-tries of 1 honored - C                      ok
+618: max-tries of 1 honored - Python                 ok
+619: max-tries of 0 honored - C                      ok
+620: max-tries of 0 honored - Python                 ok
+621: passive mode - C                                ok
+622: passive mode - Python                           ok
+
+ovs-vswitchd
+
+623: ovs-vswitchd detaches correctly with empty db   ok
+624: ovs-vswitchd -- stats-update-interval           ok
+625: ovs-vswitchd -- start additional ovs-vswitchd process ok
+626: ovs-vswitchd -- switch over to another ovs-vswitchd process ok
+627: ovs-vswitchd -- invalid database path           ok
+
+ofproto
+
+628: ofproto - echo request                          ok
+629: ofproto - handling messages with bad version    ok
+630: ofproto - feature request, config request       ok
+631: ofproto - set OpenFlow port number              ok
+632: ofproto - port stats - (OpenFlow 1.0)           ok
+633: ofproto - port stats - (OpenFlow 1.2)           ok
+634: ofproto - port stats - (OpenFlow 1.4)           ok
+635: ofproto - port-desc stats (OpenFlow 1.0)        ok
+636: ofproto - port-desc stats (OpenFlow 1.2)        ok
+637: ofproto - port-desc stats (OpenFlow 1.5)        ok
+638: ofproto - queue stats - (OpenFlow 1.0)          ok
+639: ofproto - queue stats - (OpenFlow 1.2)          ok
+640: ofproto - queue stats - (OpenFlow 1.4)          ok
+641: ofproto - queue configuration - (OpenFlow 1.0)  ok
+642: ofproto - queue configuration - (OpenFlow 1.2)  ok
+643: ofproto - del group                             ok
+644: ofproto - del group deletes flows               ok
+645: ofproto - flow mod checks group availability    ok
+646: ofproto - group description                     ok
+647: ofproto - group description                     ok
+648: ofproto - group features                        ok
+649: ofproto - group stats                           ok
+650: ofproto - mod-port (OpenFlow 1.0)               ok
+651: ofproto - mod-port (OpenFlow 1.2)               ok
+652: ofproto - mod-port (OpenFlow 1.4)               ok
+653: ofproto - basic flow_mod commands (NXM)         ok
+654: ofproto - basic flow_mod commands (OpenFlow 1.0) ok
+655: ofproto - basic flow_mod commands (OpenFlow 1.1) ok
+656: ofproto - flow_mod negative test (OpenFlow 1.1) ok
+657: ofproto - set-field flow_mod commands (NXM)     ok
+658: ofproto - basic flow_mod commands (OpenFlow 1.2) ok
+659: ofproto - dump flows with cookie                ok
+660: ofproto - mod flow with cookie change (OpenFlow 1.0) ok
+661: ofproto - mod flow with cookie change (NXM)     ok
+662: ofproto - no mod flow with cookie change (OpenFlow 1.1) ok
+663: ofproto - no mod flow with cookie change (OpenFlow 1.2) ok
+664: ofproto - mod flows based on cookie mask (OpenFlow 1.0) ok
+665: ofproto - mod flows based on cookie mask (OpenFlow 1.1) ok
+666: ofproto - mod flows based on cookie mask (OpenFlow 1.2) ok
+667: ofproto - mod flows based on cookie mask with cookie change ok
+668: ofproto - mod flow with cookie miss (mask==0) - NXM ok
+669: ofproto - mod flow with cookie miss (mask==0) - OF1.1 ok
+670: ofproto - mod flow with cookie miss (mask==0) - OF1.2 ok
+671: ofproto - mod flow with cookie miss (mask!=0) - NXM ok
+672: ofproto - mod flow with cookie miss (mask!=0) - OF1.1 ok
+673: ofproto - mod flow with cookie miss (mask!=0) - OF1.2 ok
+674: ofproto - del flows with cookies                ok
+675: ofproto - del flows based on cookie             ok
+676: ofproto - del flows based on cookie mask        ok
+677: ofproto - del flows based on table id (NXM)     ok
+678: ofproto - del flows based on table id (OpenFlow 1.1) ok
+679: ofproto - del flows based on table id (OpenFlow 1.2) ok
+680: ofproto - flow table configuration (OpenFlow 1.0) ok
+681: ofproto - flow table configuration (OpenFlow 1.2) ok
+682: ofproto - hard limits on flow table size (OpenFlow 1.0) ok
+683: ofproto - hard limits on flow table size (OpenFlow 1.2) ok
+684: ofproto - eviction upon table overflow (OpenFlow 1.0) ok
+685: ofproto - eviction upon table overflow (OpenFlow 1.2) ok
+686: ofproto - eviction upon table overflow, with fairness (OpenFlow 1.0) ok
+687: ofproto - eviction upon table overflow, with fairness (OpenFlow 1.2) ok
+688: ofproto - eviction upon table overflow, with modified hard timeout ok
+689: ofproto - eviction upon table overflow, with modified idle timeout ok
+690: ofproto - asynchronous message control (OpenFlow 1.0) ok
+691: ofproto - asynchronous message control (OpenFlow 1.2) ok
+692: ofproto - asynchronous message control (OpenFlow 1.3) ok
+693: ofproto - controller role (OpenFlow 1.2)        ok
+694: ofproto - controller role (OpenFlow 1.4)        ok
+695: ofproto - packet-out from controller (OpenFlow 1.0) ok
+696: ofproto - packet-out from controller (OpenFlow 1.2) ok
+697: ofproto - packet-out from controller (OpenFlow 1.1) ok
+698: ofproto - packet-out with metadata (NXM)        ok
+699: ofproto - packet-out with metadata (OpenFlow 1.2) ok
+700: ofproto - packet-out with tunnel metadata (OpenFlow 1.2) ok
+701: ofproto - flow monitoring                       ok
+702: ofproto - flow monitoring pause and resume      skipped (ofproto.at:2351)
+703: ofproto - event filtering (OpenFlow 1.3)        ok
+704: ofproto - ofport_request                        ok
+705: ofproto - bundles, open (OpenFlow 1.4)          ok
+706: ofproto - bundles, double open (OpenFlow 1.4)   ok
+707: ofproto - bundle close without open (OpenFlow 1.4) ok
+708: ofproto - bundle double close (OpenFlow 1.4)    ok
+709: ofproto - bundle close, different flags (OpenFlow 1.4) ok
+710: ofproto - bundle commit without open (OpenFlow 1.4) ok
+711: ofproto - bundle commit, different flags (OpenFlow 1.4) ok
+712: ofproto - bundle discard without open (OpenFlow 1.4) ok
+
+ofproto-dpif
+
+713: ofproto-dpif - dummy interface                  ok
+714: ofproto-dpif, active-backup bonding             ok
+715: ofproto-dpif, balance-slb bonding               ok
+716: ofproto-dpif, balance-tcp bonding               ok
+717: ofproto-dpif - resubmit                         ok
+718: ofproto-dpif - goto table                       ok
+719: ofproto-dpif - write actions                    ok
+720: ofproto-dpif - clear actions                    ok
+721: ofproto-dpif - group chaining not supported     ok
+722: ofproto-dpif - all group in action list         ok
+723: ofproto-dpif - indirect group in action list    ok
+724: ofproto-dpif - all group in action set          ok
+725: ofproto-dpif - indirect group in action set     ok
+726: ofproto-dpif - select group                     ok
+727: ofproto-dpif - select group with watch port     ok
+728: ofproto-dpif - select group with weight         ok
+729: ofproto-dpif - fast failover group              ok
+730: ofproto-dpif - registers                        ok
+731: ofproto-dpif - push-pop                         ok
+732: ofproto-dpif - output                           ok
+733: ofproto-dpif - dec_ttl                          ok
+734: ofproto-dpif - dec_ttl without arguments at offset 32 in ofpacts ok
+735: ofproto-dpif - dec_ttl with arguments at offset 32 in ofpacts ok
+736: ofproto-dpif - note at offset 24 in ofpacts     ok
+737: ofproto-dpif - output, OFPP_NONE ingress port   ok
+738: ofproto-dpif - DSCP                             ok
+739: ofproto-dpif - output/flood flags               ok
+740: ofproto-dpif - Default Table Miss - OF1.0 (OFPTC_TABLE_MISS_CONTROLLER) ok
+741: ofproto-dpif - Default Table Miss - OF1.3 (OFPTC_TABLE_MISS_DROP) ok
+742: ofproto-dpif - Table Miss - goto table and OFPTC_TABLE_MISS_CONTROLLER ok
+743: ofproto-dpif - Table Miss - resubmit and OFPTC_TABLE_MISS_CONTROLLER ok
+744: ofproto-dpif - Table Miss - OFPTC_TABLE_MISS_CONTINUE ok
+745: ofproto-dpif - Table Miss - goto table and OFPTC_TABLE_MISS_CONTINUE ok
+746: ofproto-dpif - Table Miss - resubmit and OFPTC_TABLE_MISS_CONTINUE ok
+747: ofproto-dpif - Table Miss - OFPTC_TABLE_MISS_DROP ok
+748: ofproto-dpif - Table Miss - goto table and OFPTC_TABLE_MISS_DROP ok
+749: ofproto-dpif - Table Miss - resubmit and OFPTC_TABLE_MISS_DROP ok
+750: ofproto-dpif - controller                       ok
+751: ofproto-dpif - table-miss flow (OpenFlow 1.0)   ok
+752: ofproto-dpif - table-miss flow (OpenFlow 1.3)   ok
+753: ofproto-dpif - ARP modification slow-path       ok
+754: ofproto-dpif - VLAN handling                    ok
+755: ofproto-dpif - MPLS handling                    ok
+756: ofproto-dpif - VLAN+MPLS handling               ok
+757: ofproto-dpif - fragment handling                ok
+758: ofproto-dpif - exit                             ok
+759: ofproto-dpif - mirroring, select_all            ok
+760: ofproto-dpif - mirroring, select_src            ok
+761: ofproto-dpif - mirroring, OFPP_NONE ingress port ok
+762: ofproto-dpif - mirroring, select_dst            ok
+763: ofproto-dpif - mirroring, select_vlan           ok
+764: ofproto-dpif - mirroring, output_port           ok
+765: ofproto-dpif - mirroring, output_vlan           ok
+766: ofproto-dpif - ofproto/trace command 1          ok
+767: ofproto-dpif - ofproto/trace command 2          ok
+768: ofproto-dpif - ofproto/trace-packet-out         ok
+769: ofproto-dpif - MAC learning                     ok
+770: ofproto-dpif - MAC table overflow               ok
+771: ofproto-dpif - sFlow packet sampling - IPv4 collector FAILED (ofproto-dpif.at:3345)
+772: ofproto-dpif - sFlow packet sampling - IPv6 collector FAILED (ofproto-dpif.at:3346)
+773: ofproto-dpif - NetFlow flow expiration - IPv4 collector ok
+774: ofproto-dpif - NetFlow flow expiration - IPv6 collector ok
+775: ofproto-dpif - NetFlow active expiration - IPv4 collector ok
+776: ofproto-dpif - NetFlow active expiration - IPv6 collector ok
+777: ofproto-dpif - flow stats                       ok
+778: ofproto-dpif - flow stats, set-n-threads        ok
+779: idle_age and hard_age increase over time        ok
+780: ofproto-dpif - fin_timeout                      ok
+781: ofproto-dpif - ovs-appctl dpif/dump-dps         ok
+782: ofproto-dpif - ovs-appctl dpif/show             ok
+783: ofproto-dpif - ovs-appctl dpif/dump-flows       ok
+784: ofproto-dpif - MPLS actions that result in a userspace action ok
+785: ofproto-dpif - MPLS actions that result in a drop ok
+786: ofproto-dpif - patch ports                      ok
+787: ofproto-dpif - port duration                    ok
+
+ofproto-dpif -- megaflows
+
+788: ofproto-dpif megaflow - port classification     ok
+789: ofproto-dpif megaflow - L2 classification       ok
+790: ofproto-dpif megaflow - L3 classification       ok
+791: ofproto-dpif megaflow - IPv6 classification     ok
+792: ofproto-dpif megaflow - L4 classification       ok
+793: ofproto-dpif megaflow - normal                  ok
+794: ofproto-dpif megaflow - mpls                    ok
+795: ofproto-dpif megaflow - netflow - IPv4 collector ok
+796: ofproto-dpif megaflow - netflow - IPv6 collector ok
+797: ofproto-dpif megaflow - normal, active-backup bonding ok
+798: ofproto-dpif megaflow - normal, balance-slb bonding ok
+799: ofproto-dpif megaflow - normal, balance-tcp bonding ok
+800: ofproto-dpif megaflow - resubmit port action    ok
+801: ofproto-dpif megaflow - resubmit table action   ok
+802: ofproto-dpif megaflow - goto_table action       ok
+803: ofproto-dpif megaflow - mirroring, select_all   ok
+804: ofproto-dpif megaflow - mirroring, select_vlan  ok
+805: ofproto-dpif megaflow - move action             ok
+806: ofproto-dpif megaflow - push action             ok
+807: ofproto-dpif megaflow - learning                ok
+808: ofproto-dpif megaflow - tunnels                 FAILED (ofproto-dpif.at:4302)
+809: ofproto-dpif megaflow - dec_ttl                 ok
+810: ofproto-dpif megaflow - set dl_dst              ok
+811: ofproto-dpif megaflow - disabled                ok
+812: ofproto-dpif - datapath port number change      ok
+813: ofproto - bundle with variable bfd/cfm config   ok
+814: ofproto-dpif - ofproto-dpif-monitor 1           FAILED (ofproto-dpif.at:4510)
+815: ofproto-dpif - ofproto-dpif-monitor 2           FAILED (ofproto-dpif.at:4561)
+
+ofproto-dpif - flow translation resource limits
+
+816: ofproto-dpif - infinite resubmit                ok
+817: ofproto-dpif - exponential resubmit chain       ok
+818: ofproto-dpif - too many output actions          ok
+819: ofproto-dpif - stack too deep                   ok
+820: ofproto-dpif - ICMPv6                           ok
+
+VLAN splinters
+
+821: VLAN splinters                                  ok
+
+OVSDB -- logging
+
+822: create empty, reread                            ok
+823: write one, reread                               ok
+824: check that create fails if file exists          ok
+825: write one, reread                               ok
+826: write one, reread, append                       ok
+827: write, reread one, overwrite                    ok
+828: write, add corrupted data, read                 ok
+829: write, add corrupted data, read, overwrite      ok
+830: write, corrupt some data, read, overwrite       ok
+831: write, truncate file, read, overwrite           ok
+832: write bad JSON, read, overwrite                 ok
+
+OVSDB -- atomic types
+
+833: integer - C                                     ok
+834: integer - Python                                ok
+835: real - C                                        ok
+836: real - Python                                   ok
+837: boolean - C                                     ok
+838: boolean - Python                                ok
+839: string - C                                      ok
+840: string - Python                                 ok
+841: uuid - C                                        ok
+842: uuid - Python                                   ok
+843: void is not a valid atomic-type - C             ok
+844: void is not a valid atomic-type - Python        ok
+
+OVSDB -- base types
+
+845: integer enum - C                                ok
+846: integer enum - Python                           ok
+847: integer >= 5 - C                                ok
+848: integer >= 5 - Python                           ok
+849: integer <= 7 - C                                ok
+850: integer <= 7 - Python                           ok
+851: integer between -5 and 10 - C                   ok
+852: integer between -5 and 10 - Python              ok
+853: integer max may not be less than min - C        ok
+854: integer max may not be less than min - Python   ok
+855: real enum - C                                   ok
+856: real enum - Python                              ok
+857: real >= -1.5 - C                                ok
+858: real >= -1.5 - Python                           ok
+859: real <= 1e5 - C                                 ok
+860: real <= 1e5 - Python                            ok
+861: real between -2.5 and 3.75 - C                  ok
+862: real between -2.5 and 3.75 - Python             ok
+863: real max may not be less than min - C           ok
+864: real max may not be less than min - Python      ok
+865: boolean - C                                     ok
+866: boolean - Python                                ok
+867: boolean enum - C                                ok
+868: boolean enum - Python                           ok
+869: string enum - C                                 ok
+870: string enum - Python                            ok
+871: string minLength - C                            ok
+872: string minLength - Python                       ok
+873: string maxLength - C                            ok
+874: string maxLength - Python                       ok
+875: string minLength and maxLength - C              ok
+876: string minLength and maxLength - Python         ok
+877: maxLength must not be less than minLength - C   ok
+878: maxLength must not be less than minLength - Python ok
+879: maxLength must not be negative - C              ok
+880: maxLength must not be negative - Python         ok
+881: uuid enum - C                                   ok
+882: uuid enum - Python                              ok
+883: uuid refTable - C                               ok
+884: uuid refTable - Python                          ok
+885: uuid refTable must be valid id - C              ok
+886: uuid refTable must be valid id - Python         ok
+887: void is not a valid base-type - C               ok
+888: void is not a valid base-type - Python          ok
+889: "type" member must be present - C               ok
+890: "type" member must be present - Python          ok
+
+OVSDB -- simple types
+
+891: simple integer - C                              ok
+892: simple integer - Python                         ok
+893: simple real - C                                 ok
+894: simple real - Python                            ok
+895: simple boolean - C                              ok
+896: simple boolean - Python                         ok
+897: simple string - C                               ok
+898: simple string - Python                          ok
+899: simple uuid - C                                 ok
+900: simple uuid - Python                            ok
+901: integer in object - C                           ok
+902: integer in object - Python                      ok
+903: real in object with explicit min and max - C    ok
+904: real in object with explicit min and max - Python ok
+905: key type is required - C                        ok
+906: key type is required - Python                   ok
+907: void is not a valid type - C                    ok
+908: void is not a valid type - Python               ok
+
+OVSDB -- set types
+
+909: optional boolean - C                            ok
+910: optional boolean - Python                       ok
+911: set of 1 to 3 uuids - C                         ok
+912: set of 1 to 3 uuids - Python                    ok
+913: set of 0 to 3 strings - C                       ok
+914: set of 0 to 3 strings - Python                  ok
+915: set of 0 or more integers - C                   ok
+916: set of 0 or more integers - Python              ok
+917: set of 1 or more reals - C                      ok
+918: set of 1 or more reals - Python                 ok
+919: set max cannot be less than min - C             ok
+920: set max cannot be less than min - Python        ok
+921: set max cannot be negative - C                  ok
+922: set max cannot be negative - Python             ok
+923: set min cannot be negative - C                  ok
+924: set min cannot be negative - Python             ok
+925: set min cannot be greater than one - C          ok
+926: set min cannot be greater than one - Python     ok
+
+OVSDB -- map types
+
+927: map of 1 integer to boolean - C                 ok
+928: map of 1 integer to boolean - Python            ok
+929: map of 1 boolean to integer, explicit min and max - C ok
+930: map of 1 boolean to integer, explicit min and max - Python ok
+931: map of 1 to 5 uuid to real - C                  ok
+932: map of 1 to 5 uuid to real - Python             ok
+933: map of 0 to 10 string to uuid - C               ok
+934: map of 0 to 10 string to uuid - Python          ok
+935: map of 1 to 20 real to string - C               ok
+936: map of 1 to 20 real to string - Python          ok
+937: map of 0 or more string to real - C             ok
+938: map of 0 or more string to real - Python        ok
+939: map key type is required - C                    ok
+940: map key type is required - Python               ok
+
+OVSDB -- default values
+
+941: default atoms - C                               ok
+942: default atoms - Python                          ok
+943: default data - C                                ok
+944: default data - Python                           ok
+
+OVSDB -- atoms without constraints
+
+945: integer atom from JSON - C                      ok
+946: integer atom from JSON - Python                 ok
+947: integer atom from string                        ok
+948: real atom from JSON - C                         ok
+949: real atom from JSON - Python                    ok
+950: real atom from string                           ok
+951: boolean atom from JSON - C                      ok
+952: boolean atom from JSON - Python                 ok
+953: boolean atom from string                        ok
+954: string atom from JSON - C                       ok
+955: string atom from JSON - Python                  ok
+956: string atom from string                         ok
+957: uuid atom from JSON - C                         ok
+958: uuid atom from JSON - Python                    ok
+959: uuid atom from string                           ok
+960: integer atom sorting - C                        ok
+961: integer atom sorting - Python                   ok
+962: real atom sorting - C                           ok
+963: real atom sorting - Python                      ok
+964: boolean atom sorting - C                        ok
+965: boolean atom sorting - Python                   ok
+966: string atom sorting - C                         ok
+967: string atom sorting - Python                    ok
+968: uuid atom sorting - C                           ok
+969: uuid atom sorting - Python                      ok
+970: real not acceptable integer JSON atom - C       ok
+971: real not acceptable integer JSON atom - Python  ok
+972: no invalid UTF-8 sequences in strings           ok
+973: no invalid UTF-8 sequences in strings - Python  ok
+974: real not acceptable integer string atom         ok
+975: string "true" not acceptable boolean JSON atom - C ok
+976: string "true" not acceptable boolean JSON atom - Python ok
+977: string "true" not acceptable boolean string atom ok
+978: integer not acceptable string JSON atom - C     ok
+979: integer not acceptable string JSON atom - Python ok
+980: uuid atom must be expressed as JSON array - C   ok
+981: uuid atom must be expressed as JSON array - Python ok
+982: named-uuid requires symbol table - C            ok
+983: named-uuid requires symbol table - Python       ok
+984: empty string atom must be quoted                ok
+985: quotes must be balanced                         ok
+986: uuids must be valid                             ok
+
+OVSDB -- atoms with enum constraints
+
+987: integer atom enum - C                           ok
+988: integer atom enum - Python                      ok
+989: real atom enum - C                              ok
+990: real atom enum - Python                         ok
+991: boolean atom enum - C                           ok
+992: boolean atom enum - Python                      ok
+993: string atom enum - C                            ok
+994: string atom enum - Python                       ok
+995: uuid atom enum - C                              ok
+996: uuid atom enum - Python                         ok
+
+OVSDB -- atoms with other constraints
+
+997: integers >= 5 - C                               ok
+998: integers >= 5 - Python                          ok
+999: integers <= -1 - C                              ok
+1000: integers <= -1 - Python                         ok
+1001: integers in range -10 to 10 - C                 ok
+1002: integers in range -10 to 10 - Python            ok
+1003: reals >= 5 - C                                  ok
+1004: reals >= 5 - Python                             ok
+1005: reals <= -1 - C                                 ok
+1006: reals <= -1 - Python                            ok
+1007: reals in range -10 to 10 - C                    ok
+1008: reals in range -10 to 10 - Python               ok
+1009: strings at least 2 characters long - C          ok
+1010: strings at least 2 characters long - Python     skipped (ovsdb-data.at:458)
+1011: strings no more than 2 characters long - C      ok
+1012: strings no more than 2 characters long - Python ok
+
+OSVDB -- simple data
+
+1013: integer JSON datum - C                          ok
+1014: integer JSON datum - Python                     ok
+1015: integer string datum                            ok
+1016: real JSON datum - C                             ok
+1017: real JSON datum - Python                        ok
+1018: real string datum                               ok
+1019: boolean JSON datum - C                          ok
+1020: boolean JSON datum - Python                     ok
+1021: boolean string datum                            ok
+1022: string JSON datum - C                           ok
+1023: string JSON datum - Python                      ok
+1024: string string datum                             ok
+
+OVSDB -- set data
+
+1025: JSON optional boolean - C                       ok
+1026: JSON optional boolean - Python                  ok
+1027: string optional boolean                         ok
+1028: JSON set of 0 or more integers - C              ok
+1029: JSON set of 0 or more integers - Python         ok
+1030: string set of 0 or more integers                ok
+1031: JSON set of 1 to 3 uuids - C                    ok
+1032: JSON set of 1 to 3 uuids - Python               ok
+1033: string set of 1 to 3 uuids                      ok
+1034: JSON set of 0 to 3 strings - C                  ok
+1035: JSON set of 0 to 3 strings - Python             ok
+1036: string set of 0 to 3 strings                    ok
+1037: duplicate boolean not allowed in JSON set - C   ok
+1038: duplicate boolean not allowed in JSON set - Python ok
+1039: duplicate boolean not allowed in string set     ok
+1040: duplicate integer not allowed in JSON set - C   ok
+1041: duplicate integer not allowed in JSON set - Python ok
+1042: duplicate integer not allowed in string set     ok
+1043: duplicate real not allowed in JSON set - C      ok
+1044: duplicate real not allowed in JSON set - Python ok
+1045: duplicate real not allowed in string set        ok
+1046: duplicate string not allowed in JSON set - C    ok
+1047: duplicate string not allowed in JSON set - Python ok
+1048: duplicate string not allowed in string set      ok
+1049: duplicate uuid not allowed in JSON set - C      ok
+1050: duplicate uuid not allowed in JSON set - Python ok
+1051: duplicate uuid not allowed in string set        ok
+
+OVSDB -- map data
+
+1052: JSON map of 1 integer to boolean - C            ok
+1053: JSON map of 1 integer to boolean - Python       ok
+1054: string map of 1 integer to boolean              ok
+1055: JSON map of at least 1 integer to boolean - C   ok
+1056: JSON map of at least 1 integer to boolean - Python ok
+1057: string map of at least 1 integer to boolean     ok
+1058: JSON map of 1 boolean to integer - C            ok
+1059: JSON map of 1 boolean to integer - Python       ok
+1060: string map of 1 boolean to integer              ok
+1061: JSON map of 1 uuid to real - C                  ok
+1062: JSON map of 1 uuid to real - Python             ok
+1063: string map of 1 uuid to real                    ok
+1064: JSON map of 10 string to string - C             ok
+1065: JSON map of 10 string to string - Python        ok
+1066: string map of 10 string to string               ok
+1067: duplicate integer key not allowed in JSON map - C ok
+1068: duplicate integer key not allowed in JSON map - Python ok
+1069: duplicate integer key not allowed in string map ok
+
+OVSDB -- columns
+
+1070: ordinary column - C                             ok
+1071: ordinary column - Python                        ok
+1072: immutable column - C                            ok
+1073: immutable column - Python                       ok
+1074: ephemeral column - C                            ok
+1075: ephemeral column - Python                       ok
+
+OVSDB -- tables
+
+1076: non-root table with one column - C              ok
+1077: non-root table with one column - Python         ok
+1078: immutable table with one column - C             ok
+1079: immutable table with one column - Python        ok
+1080: root table with one column - C                  ok
+1081: root table with one column - Python             ok
+1082: non-root table with default_is_root=true - C    ok
+1083: non-root table with default_is_root=true - Python ok
+1084: root table with default_is_root=true - C        ok
+1085: root table with default_is_root=true - Python   ok
+1086: table with maxRows of 2 - C                     ok
+1087: table with maxRows of 2 - Python                ok
+1088: table with index - C                            ok
+1089: table with index - Python                       ok
+1090: table with syntax error in index - C            ok
+1091: table with syntax error in index - Python       ok
+1092: table with empty index - C                      ok
+1093: table with empty index - Python                 ok
+1094: table with index of ephemeral column - C        ok
+1095: table with index of ephemeral column - Python   ok
+1096: column names may not begin with _ - C           ok
+1097: column names may not begin with _ - Python      ok
+1098: table must have at least one column (1) - C     ok
+1099: table must have at least one column (1) - Python ok
+1100: table must have at least one column (2) - C     ok
+1101: table must have at least one column (2) - Python ok
+1102: table maxRows must be positive - C              ok
+1103: table maxRows must be positive - Python         ok
+
+OVSDB -- rows
+
+1104: row with one string column                      ok
+1105: row with one integer column                     ok
+1106: row with one real column                        ok
+1107: row with one boolean column                     ok
+1108: row with one uuid column                        ok
+1109: row with set of 1 to 2 elements                 ok
+1110: row with map of 1 to 2 elements                 ok
+1111: row with several columns                        ok
+1112: row hashing (scalars)                           ok
+1113: row hashing (sets)                              ok
+1114: row hashing (maps)                              ok
+
+OVSDB -- schemas
+
+1115: schema with valid refTables - C                 ok
+1116: schema with valid refTables - Python            ok
+1117: schema with ephemeral strong references - C     ok
+1118: schema with ephemeral strong references - Python ok
+1119: schema without version number - C               ok
+1120: schema without version number - Python          ok
+1121: schema with invalid refTables - C               ok
+1122: schema with invalid refTables - Python          ok
+1123: schema with invalid version number - C          ok
+1124: schema with invalid version number - Python     ok
+
+OVSDB -- conditions
+
+1125: null condition                                  ok
+1126: conditions on scalars                           ok
+1127: disallowed conditions on scalars                ok
+1128: conditions on sets                              ok
+1129: condition sorting                               ok
+1130: evaluating null condition                       ok
+1131: evaluating conditions on integers               ok
+1132: evaluating conditions on reals                  ok
+1133: evaluating conditions on booleans               ok
+1134: evaluating conditions on strings                ok
+1135: evaluating conditions on UUIDs                  ok
+1136: evaluating conditions on sets                   ok
+1137: evaluating conditions on maps (1)               ok
+1138: evaluating conditions on maps (2)               ok
+
+OVSDB -- mutations
+
+1139: null mutation                                   ok
+1140: mutations on scalars                            ok
+1141: disallowed mutations on scalars                 ok
+1142: disallowed mutations on immutable columns       ok
+1143: mutations on sets                               ok
+1144: executing null mutation                         ok
+1145: executing mutations on integers                 ok
+1146: integer overflow detection                      ok
+1147: executing mutations on integers with constraints ok
+1148: executing mutations on reals                    ok
+1149: real overflow detection                         ok
+1150: executing mutations on reals with constraints   ok
+1151: executing mutations on integer sets             ok
+1152: executing mutations on integer sets with constraints ok
+1153: executing mutations on real sets                ok
+1154: executing mutations on boolean sets             ok
+1155: executing mutations on string sets              ok
+1156: executing mutations on uuid sets                ok
+1157: executing mutations on integer maps             ok
+
+OVSDB -- queries
+
+1158: queries on scalars                              ok
+1159: queries on sets                                 ok
+1160: queries on maps (1)                             ok
+1161: queries on maps (2)                             ok
+1162: UUID-distinct queries on scalars                ok
+1163: Boolean-distinct queries on scalars             ok
+1164: parse colunn set containing bad name            ok
+
+OVSDB -- transactions
+
+1165: empty table, empty transaction                  ok
+1166: nonempty table, empty transaction               ok
+1167: insert, commit                                  ok
+1168: insert, abort                                   ok
+1169: modify, commit                                  ok
+1170: modify, abort                                   ok
+1171: delete, commit                                  ok
+1172: delete, abort                                   ok
+1173: modify, delete, commit                          ok
+1174: modify, delete, abort                           ok
+1175: insert, delete, commit                          ok
+1176: insert, delete, abort                           ok
+1177: insert, modify, delete, commit                  ok
+1178: insert, modify, delete, abort                   ok
+1179: deletes are aborted cleanly                     ok
+
+OVSDB -- execution
+
+1180: uuid-name must be <id>                          ok
+1181: named-uuid must be <id>                         ok
+1182: duplicate uuid-name not allowed                 ok
+1183: insert default row, query table                 ok
+1184: insert row, query table                         ok
+1185: insert rows, query by value                     ok
+1186: insert rows, query by named-uuid                ok
+1187: insert rows, update rows by value               ok
+1188: insert rows, mutate rows                        ok
+1189: insert rows, delete by named-uuid               ok
+1190: insert rows, delete rows by value               ok
+1191: insert rows, delete by (non-matching) value     ok
+1192: insert rows, delete all                         ok
+1193: insert row, query table, commit                 ok
+1194: insert row, query table, commit durably         ok
+1195: equality wait with correct rows                 ok
+1196: equality wait with extra row                    ok
+1197: equality wait with missing row                  ok
+1198: inequality wait with correct rows               ok
+1199: inequality wait with extra row                  ok
+1200: inequality wait with missing row                ok
+1201: insert and update constraints                   ok
+1202: index uniqueness checking                       ok
+1203: referential integrity -- simple                 ok
+1204: referential integrity -- mutual references      ok
+1205: weak references                                 ok
+1206: immutable columns                               ok
+1207: garbage collection                              ok
+
+OVSDB -- triggers
+
+1208: trigger fires immediately                       ok
+1209: trigger times out                               ok
+1210: trigger fires after delay                       ok
+1211: delayed trigger modifies database               ok
+1212: one delayed trigger wakes up another            ok
+
+OVSDB -- ovsdb-tool
+
+1213: insert default row, query table                 ok
+1214: insert row, query table                         ok
+1215: insert rows, query by value                     ok
+1216: insert rows, query by named-uuid                ok
+1217: insert rows, update rows by value               ok
+1218: insert rows, mutate rows                        ok
+1219: insert rows, delete by named-uuid               ok
+1220: insert rows, delete rows by value               ok
+1221: insert rows, delete by (non-matching) value     ok
+1222: insert rows, delete all                         ok
+1223: insert row, query table, commit                 ok
+1224: insert row, query table, commit durably         ok
+1225: equality wait with correct rows                 ok
+1226: equality wait with extra row                    ok
+1227: equality wait with missing row                  ok
+1228: inequality wait with correct rows               ok
+1229: inequality wait with extra row                  ok
+1230: inequality wait with missing row                ok
+1231: insert and update constraints                   ok
+1232: index uniqueness checking                       ok
+1233: referential integrity -- simple                 ok
+1234: referential integrity -- mutual references      ok
+1235: weak references                                 ok
+1236: immutable columns                               ok
+1237: garbage collection                              ok
+1238: transaction comments                            ok
+1239: ovsdb-tool compact                              ok
+1240: ovsdb-tool convert -- removing a column         ok
+1241: ovsdb-tool convert -- adding a column           ok
+1242: ovsdb-tool schema-version                       ok
+1243: ovsdb-tool db-version                           ok
+1244: ovsdb-tool schema-cksum                         ok
+1245: ovsdb-tool db-cksum                             ok
+1246: ovsdb-tool needs-conversion (no conversion needed) ok
+1247: ovsdb-tool needs-conversion (conversion needed) ok
+
+OVSDB -- ovsdb-server transactions (Unix sockets)
+
+1248: insert default row, query table                 ok
+1249: insert row, query table                         ok
+1250: insert rows, query by value                     ok
+1251: insert rows, query by named-uuid                ok
+1252: insert rows, update rows by value               ok
+1253: insert rows, mutate rows                        ok
+1254: insert rows, delete by named-uuid               ok
+1255: insert rows, delete rows by value               ok
+1256: insert rows, delete by (non-matching) value     ok
+1257: insert rows, delete all                         ok
+1258: insert row, query table, commit                 ok
+1259: insert row, query table, commit durably         ok
+1260: equality wait with correct rows                 ok
+1261: equality wait with extra row                    ok
+1262: equality wait with missing row                  ok
+1263: inequality wait with correct rows               ok
+1264: inequality wait with extra row                  ok
+1265: inequality wait with missing row                ok
+1266: insert and update constraints                   ok
+1267: index uniqueness checking                       ok
+1268: referential integrity -- simple                 ok
+1269: referential integrity -- mutual references      ok
+1270: weak references                                 ok
+1271: immutable columns                               ok
+1272: garbage collection                              ok
+
+ovsdb-server miscellaneous features
+
+1273: truncating corrupted database log               ok
+1274: truncating database log with bad transaction    ok
+1275: ovsdb-client get-schema-version                 ok
+1276: database multiplexing implementation            ok
+1277: ovsdb-server/add-db and remove-db               ok
+1278: ovsdb-server/add-db with --monitor              ok
+1279: ovsdb-server/add-db and remove-db with --monitor ok
+1280: --remote=db: implementation                     ok
+1281: ovsdb-server/add-remote and remove-remote       ok
+1282: ovsdb-server/add-remote with --monitor          ok
+1283: ovsdb-server/add-remote and remove-remote with --monitor ok
+1284: SSL db: implementation                          ok
+1285: compacting online                               ok
+1286: ovsdb-server combines updates on backlogged connections skipped (ovsdb-server.at:681)
+
+OVSDB -- ovsdb-server transactions (SSL IPv4 sockets)
+
+1287: insert default row, query table                 ok
+1288: insert row, query table                         ok
+1289: insert rows, query by value                     ok
+1290: insert rows, query by named-uuid                ok
+1291: insert rows, update rows by value               ok
+1292: insert rows, mutate rows                        ok
+1293: insert rows, delete by named-uuid               ok
+1294: insert rows, delete rows by value               ok
+1295: insert rows, delete by (non-matching) value     ok
+1296: insert rows, delete all                         ok
+1297: insert row, query table, commit                 ok
+1298: insert row, query table, commit durably         ok
+1299: equality wait with correct rows                 ok
+1300: equality wait with extra row                    ok
+1301: equality wait with missing row                  ok
+1302: inequality wait with correct rows               ok
+1303: inequality wait with extra row                  ok
+1304: inequality wait with missing row                ok
+1305: insert and update constraints                   ok
+1306: index uniqueness checking                       ok
+1307: referential integrity -- simple                 ok
+1308: referential integrity -- mutual references      ok
+1309: weak references                                 ok
+1310: immutable columns                               ok
+1311: garbage collection                              ok
+
+OVSDB -- ovsdb-server transactions (SSL IPv6 sockets)
+
+1312: insert default row, query table                 ok
+
+OVSDB -- ovsdb-server transactions (TCP IPv4 sockets)
+
+1313: ovsdb-client get-schema-version - tcp socket    ok
+1314: insert default row, query table                 ok
+1315: insert row, query table                         ok
+1316: insert rows, query by value                     ok
+1317: insert rows, query by named-uuid                ok
+1318: insert rows, update rows by value               ok
+1319: insert rows, mutate rows                        ok
+1320: insert rows, delete by named-uuid               ok
+1321: insert rows, delete rows by value               ok
+1322: insert rows, delete by (non-matching) value     ok
+1323: insert rows, delete all                         ok
+1324: insert row, query table, commit                 ok
+1325: insert row, query table, commit durably         ok
+1326: equality wait with correct rows                 ok
+1327: equality wait with extra row                    ok
+1328: equality wait with missing row                  ok
+1329: inequality wait with correct rows               ok
+1330: inequality wait with extra row                  ok
+1331: inequality wait with missing row                ok
+1332: insert and update constraints                   ok
+1333: index uniqueness checking                       ok
+1334: referential integrity -- simple                 ok
+1335: referential integrity -- mutual references      ok
+1336: weak references                                 ok
+1337: immutable columns                               ok
+1338: garbage collection                              ok
+
+OVSDB -- ovsdb-server transactions (TCP IPv6 sockets)
+
+1339: insert default row, query table                 ok
+
+OVSDB -- transactions on transient ovsdb-server
+
+1340: insert default row, query table                 ok
+1341: insert row, query table                         ok
+1342: insert rows, query by value                     ok
+1343: insert rows, query by named-uuid                ok
+1344: insert rows, update rows by value               ok
+1345: insert rows, mutate rows                        ok
+1346: insert rows, delete by named-uuid               ok
+1347: insert rows, delete rows by value               ok
+1348: insert rows, delete by (non-matching) value     ok
+1349: insert rows, delete all                         ok
+1350: insert row, query table, commit                 ok
+1351: insert row, query table, commit durably         ok
+1352: equality wait with correct rows                 ok
+1353: equality wait with extra row                    ok
+1354: equality wait with missing row                  ok
+1355: inequality wait with correct rows               ok
+1356: inequality wait with extra row                  ok
+1357: inequality wait with missing row                ok
+1358: insert and update constraints                   ok
+1359: index uniqueness checking                       ok
+1360: referential integrity -- simple                 ok
+1361: referential integrity -- mutual references      ok
+1362: weak references                                 ok
+1363: immutable columns                               ok
+1364: garbage collection                              ok
+
+OVSDB -- ovsdb-server monitors
+
+1365: monitor insert into empty table                 ok
+1366: monitor insert into populated table             ok
+1367: monitor delete                                  ok
+1368: monitor row update                              ok
+1369: monitor no-op row updates                       ok
+1370: monitor insert-and-update transaction           ok
+1371: monitor insert-update-and-delete transaction    ok
+1372: monitor weak reference change                   ok
+1373: monitor insert-update-and-delete transaction    ok
+
+ovsdb -- ovsdb-monitor monitor only some operations
+
+1374: monitor all operations                          ok
+1375: monitor initial only                            ok
+1376: monitor insert only                             ok
+1377: monitor delete only                             ok
+1378: monitor modify only                             ok
+
+OVSDB -- interface description language (IDL)
+
+1379: simple idl, initially empty, no ops - C         ok
+1380: simple idl, initially empty, no ops - Python    ok
+1381: simple idl, initially empty, no ops - Python tcp ok
+1382: simple idl, initially empty, no ops - Python tcp6 ok
+1383: simple idl, initially empty, various ops - C    ok
+1384: simple idl, initially empty, various ops - Python ok
+1385: simple idl, initially empty, various ops - Python tcp ok
+1386: simple idl, initially empty, various ops - Python tcp6 ok
+1387: simple idl, initially populated - C             ok
+1388: simple idl, initially populated - Python        ok
+1389: simple idl, initially populated - Python tcp    ok
+1390: simple idl, initially populated - Python tcp6   ok
+1391: simple idl, writing via IDL - C                 ok
+1392: simple idl, writing via IDL - Python            ok
+1393: simple idl, writing via IDL - Python tcp        ok
+1394: simple idl, writing via IDL - Python tcp6       ok
+1395: simple idl, handling verification failure - C   ok
+1396: simple idl, handling verification failure - Python ok
+1397: simple idl, handling verification failure - Python tcp ok
+1398: simple idl, handling verification failure - Python tcp6 ok
+1399: simple idl, increment operation - C             ok
+1400: simple idl, increment operation - Python        ok
+1401: simple idl, increment operation - Python tcp    ok
+1402: simple idl, increment operation - Python tcp6   ok
+1403: simple idl, aborting - C                        ok
+1404: simple idl, aborting - Python                   ok
+1405: simple idl, aborting - Python tcp               ok
+1406: simple idl, aborting - Python tcp6              ok
+1407: simple idl, destroy without commit or abort - C ok
+1408: simple idl, destroy without commit or abort - Python ok
+1409: simple idl, destroy without commit or abort - Python tcp ok
+1410: simple idl, destroy without commit or abort - Python tcp6 ok
+1411: self-linking idl, consistent ops - C            ok
+1412: self-linking idl, consistent ops - Python       ok
+1413: self-linking idl, consistent ops - Python tcp   ok
+1414: self-linking idl, consistent ops - Python tcp6  ok
+1415: self-linking idl, inconsistent ops - C          ok
+1416: self-linking idl, inconsistent ops - Python     ok
+1417: self-linking idl, inconsistent ops - Python tcp ok
+1418: self-linking idl, inconsistent ops - Python tcp6 ok
+1419: self-linking idl, sets - C                      ok
+1420: self-linking idl, sets - Python                 ok
+1421: self-linking idl, sets - Python tcp             ok
+1422: self-linking idl, sets - Python tcp6            ok
+1423: external-linking idl, consistent ops - C        ok
+1424: external-linking idl, consistent ops - Python   ok
+1425: external-linking idl, consistent ops - Python tcp ok
+1426: external-linking idl, consistent ops - Python tcp6 ok
+1427: external-linking idl, insert ops - Python       ok
+1428: getattr idl, insert ops - Python                ok
+
+ovs-vsctl unit tests
+
+1429: ovs-vsctl connection retry                      ok
+
+ovs-vsctl unit tests -- real bridges
+
+1430: add-br a                                        ok
+1431: add-br a, add-br a                              ok
+1432: add-br a, add-br b                              ok
+1433: add-br a, add-br b, del-br a                    ok
+1434: add-br a, del-br a, add-br a                    ok
+1435: add-br a, add-port a a1, add-port a a2          ok
+1436: add-br a, add-port a a1, add-port a a1          ok
+1437: add-br a b, add-port a a1, add-port b b1, del-br a ok
+1438: add-br a, add-bond a bond0 a1 a2 a3             ok
+1439: add-br a b, add-port a a1, add-port b b1, del-port a a1 ok
+1440: add-br a, add-bond a bond0 a1 a2 a3, del-port bond0 ok
+1441: external IDs                                    ok
+1442: controllers                                     ok
+
+ovs-vsctl unit tests -- fake bridges (VLAN 9)
+
+1443: simple fake bridge (VLAN 9)                     ok
+1444: list bridges -- real and fake (VLAN 9)          ok
+1445: simple fake bridge + del-br fake bridge (VLAN 9) ok
+1446: simple fake bridge + del-br real bridge (VLAN 9) ok
+1447: simple fake bridge + external IDs (VLAN 9)      ok
+
+ovs-vsctl unit tests -- fake bridges (VLAN 0)
+
+1448: simple fake bridge (VLAN 0)                     ok
+1449: list bridges -- real and fake (VLAN 0)          ok
+1450: simple fake bridge + del-br fake bridge (VLAN 0) ok
+1451: simple fake bridge + del-br real bridge (VLAN 0) ok
+1452: simple fake bridge + external IDs (VLAN 0)      ok
+1453: fake bridge on bond                             ok
+1454: fake bridge on bond + del-br fake bridge        ok
+1455: fake bridge on bond + del-br real bridge        ok
+
+ovs-vsctl unit tests -- manager commands
+
+1456: managers                                        ok
+
+ovs-vsctl unit tests -- database commands
+
+1457: database commands -- positive checks            ok
+1458: database commands -- negative checks            ok
+1459: database commands -- conditions                 ok
+1460: database commands -- wait-until immediately true ok
+1461: database commands -- wait-until must wait       ok
+1462: --id option on create, get commands             ok
+1463: unreferenced record warnings                    ok
+1464: created row UUID is wrong in same execution     ok
+1465: --all option on destroy command                 ok
+
+ovs-vsctl add-port -- reserved port names
+
+1466: add-port -- reserved names 1                    FAILED (ovs-vsctl.at:1205)
+1467: add-port -- reserved names 2                    FAILED (ovs-vsctl.at:1231)
+
+ovs-monitor-ipsec
+
+1468: ovs-monitor-ipsec                               ok
+
+ovs-xapi-sync
+
+1469: ovs-xapi-sync                                   ok
+
+interface-reconfigure
+
+1470: non-VLAN, non-bond                              ok
+1471: VLAN, non-bond                                  ok
+1472: Bond, non-VLAN                                  ok
+1473: VLAN on bond                                    ok
+1474: Re-create port with different types             FAILED (interface-reconfigure.at:1038)
+
+Spanning Tree Protocol unit tests
+
+1475: STP example from IEEE 802.1D-1998               ok
+1476: STP example from IEEE 802.1D-2004 figures 17.4 and 17.5 ok
+1477: STP example from IEEE 802.1D-2004 figure 17.6   ok
+1478: STP example from IEEE 802.1D-2004 figure 17.7   ok
+1479: STP.io.1.1: Link Failure                        ok
+1480: STP.io.1.2: Repeated Network                    ok
+1481: STP.io.1.4: Network Initialization              ok
+1482: STP.io.1.5: Topology Change                     ok
+1483: STP.op.1.1 and STP.op.1.2                       ok
+1484: STP.op.1.4: All Ports Initialized to Designated Ports ok
+1485: STP.op.3.1: Root Bridge Selection: Root ID Values ok
+1486: STP.op.3.3: Root Bridge Selection: Bridge ID Values ok
+1487: STP.op.3.3: Root Bridge Selection: Bridge ID Values ok
+
+vlog
+
+1488: vlog - Python                                   ok
+1489: vlog - vlog/reopen - Python                     ok
+1490: vlog - vlog/reopen without log file - Python    ok
+1491: vlog - vlog/reopen can't reopen log file - Python skipped (vlog.at:155)
+1492: vlog - vlog/set and vlog/list - Python          ok
+
+vtep-ctl unit tests -- physical switch tests
+
+1493: add-ps a                                        ok
+1494: add-ps a, add-ps a                              ok
+1495: add-ps a, add-ps b                              ok
+1496: add-ps a, add-ps b, del-ps a                    ok
+1497: add-ps a, del-ps a, add-ps a                    ok
+1498: add-ps a, add-port a a1, add-port a a2          ok
+1499: add-ps a, add-port a a1, add-port a a1          ok
+1500: add-ps a b, add-port a a1, add-port b b1, del-ps a ok
+1501: add-ps a b, add-port a a1, add-port b b1, del-port a a1 ok
+1502: add-ps a b, add-port a p1, add-port b p1, del-port a p1 ok
+
+vtep-ctl unit tests -- logical switch tests
+
+1503: add-ls a                                        ok
+1504: add-ls a, add-ls a                              ok
+1505: add-ls a, add-ls b                              ok
+1506: add-ls a, add-ls b, del-ls a                    ok
+1507: add-ls a, del-ls a, add-ls a                    ok
+
+vtep-ctl unit tests -- logical binding tests
+
+1508: bind-ls ps1 pp1 300 ls1                         ok
+1509: bind-ls ps1 pp1 300 ls1, bind-ls ps1 pp1 400 ls2 ok
+1510: bind-ls ps1 pp1 300, bind-ls ps2 pp2 300 ls2    ok
+
+vtep-ctl unit tests -- MAC binding tests
+
+1511: add-ucast-local ls1                             ok
+1512: add-ucast-local ls1, overwrite                  ok
+1513: add-ucast-local ls1, del-ucast-local ls1        ok
+1514: add-ucast-remote ls1                            ok
+1515: add-ucast-remote ls1, overwrite                 ok
+1516: add-ucast-remote ls1, del-ucast-remote ls1      ok
+1517: add-ucast-local ls1, add-ucast-remote ls1       ok
+1518: add-mcast-local ls1                             ok
+1519: add-mcast-local ls1, del-mcast-local ls1        ok
+1520: add-mcast-remote ls1                            ok
+1521: add-mcast-remote ls1, del-mcast-remote ls1      ok
+1522: add-mcast-local ls1, add-mcast-remote ls1       ok
+1523: add local and remote macs, clear-local-macs     ok
+1524: add local and remote macs, clear-remote-macs    ok
+
+vtep-ctl unit tests -- manager commands
+
+1525: managers                                        ok
+
+## ------------- ##
+## Test results. ##
+## ------------- ##
+
+ERROR: 1520 tests were run,
+21 failed (1 expected failure).
+5 tests were skipped.
+## -------------------------- ##
+## testsuite.log was created. ##
+## -------------------------- ##
+
+Please send `tests/testsuite.log' and all information you think might help:
+
+   To: <[email protected]>
+   Subject: [openvswitch 2.3.1] testsuite: 5 12 31 578 579 580 581 582 583 584 586 588 771 772 808 814 815 1466 1467 1474 failed
+
+You may investigate any problem if you feel able to do so, in which
+case the test suite provides a good starting point.  Its output may
+be found below `tests/testsuite.dir'.
+
+make[4]: *** [check-local] Error 1
+make[4]: Leaving directory `$(@D)'
+make[3]: *** [check-am] Error 2
+make[3]: Leaving directory `$(@D)'
+make[2]: *** [check-recursive] Error 1
+make[2]: Leaving directory `$(@D)'
+make[1]: *** [check] Error 2
+make[1]: Leaving directory `$(@D)'