--- /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,
+ ðerstub, &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, ðerstub, 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(ð_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, ðeraddrlen)) == 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, ð_key.eth_src, ETHERADDRL);
+ src_set = _B_TRUE;
+ }
+ } else if (strcmp(pval, "ether_dst") == 0) {
+ if (dst_set) {
+ err = EINVAL;
+ } else {
+ bcopy(etheraddr, ð_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, ð_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)'