24681506 The OVS drop action does not work as designed - userland part
authorakshay.kale@oracle.com <akshay.kale@oracle.com>
Mon, 19 Sep 2016 09:53:17 -0700
changeset 6924 e8aaad6b5075
parent 6923 338aea22bf22
child 6925 71a106abd05d
24681506 The OVS drop action does not work as designed - userland part 24681488 too many open files error with ovs-vswitchd - userland part 24600087 vswitchd core dumps when many ports are added
components/openvswitch/files/lib/dpif-solaris.c
components/openvswitch/files/lib/util-solaris.c
--- a/components/openvswitch/files/lib/dpif-solaris.c	Fri Sep 16 23:33:54 2016 -0500
+++ b/components/openvswitch/files/lib/dpif-solaris.c	Mon Sep 19 09:53:17 2016 -0700
@@ -73,6 +73,21 @@
 	bool recv_set;
 	int event_rfd;
 	int event_wfd;
+	uint32_t n_handlers;
+	/*
+	 * List of lower links of the OVS ports, over which the upcall_fd(s)
+	 * are created
+	 */
+	struct hmap lowerlinks;
+};
+
+struct dpif_solaris_lowerlink {
+	struct hmap_node node;	/* Node in dpif_solaris's 'lowerlinks'. */
+	char physname[MAXLINKNAMELEN];
+	struct dpif_solaris_handler *handlers;
+	uint32_t n_handlers;
+	uint32_t next_idx;
+	uint32_t nports;
 };
 
 static struct ovs_mutex dp_solaris_mutex = OVS_MUTEX_INITIALIZER;
@@ -83,6 +98,11 @@
 
 static struct vlog_rate_limit error_rl = VLOG_RATE_LIMIT_INIT(9999, 5);
 
+struct dpif_solaris_handler {
+	int upcall_fd;
+	odp_port_t pf_port_no;
+};
+
 struct dpif_solaris_bridge {
 	char *name;
 	struct hmap_node node;	/* Node in dpif_solaris's 'bridges'. */
@@ -108,6 +128,7 @@
 
 	/* Receive the upcalls */
 	int upcall_fd;			/* PF_PACKET fd for MISS event */
+	struct dpif_solaris_lowerlink *lowerlink;
 
 	/* Send the packet */
 	int xfd;			/* PF_PACKET to execute output action */
@@ -131,19 +152,24 @@
 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)
+    struct dpif_solaris_port *port, boolean_t notify)
     OVS_REQ_WRLOCK(dpif->port_rwlock);
+static void dpif_solaris_destroy_port_channel(struct dpif_solaris *dpif,
+    struct dpif_solaris_port *port);
 static int dpif_solaris_get_port_by_number(struct dpif_solaris *dpif,
-    odp_port_t port_no, struct dpif_solaris_port **portp)
+    odp_port_t port_no, struct dpif_solaris_port **portp);
     OVS_REQ_RDLOCK(dpif->port_rwlock);
-static int
-dpif_solaris_get_uplink_port_info(struct dpif_solaris *dpif,
+static int dpif_solaris_get_uplink_port_info(struct dpif_solaris *dpif,
     odp_port_t port_no, odp_port_t *uport_nop, int *xfdp,
     enum ovs_vport_type *vtypep);
 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 void dpif_solaris_destroy_lowerlink(struct dpif_solaris *dpif,
+    struct dpif_solaris_lowerlink *lowerlink);
+static int dpif_solaris_create_lowerlink(struct dpif_solaris *dpif,
+    char *physname, struct dpif_solaris_lowerlink **lowerlinkp);
 
 static struct dpif_solaris *
 dpif_solaris_cast(const struct dpif *dpif)
@@ -238,7 +264,9 @@
 		/* port related */
 		ovs_rwlock_init(&dpif->port_rwlock);
 		hmap_init(&dpif->ports);
+		hmap_init(&dpif->lowerlinks);
 		dpif->event_rfd = dpif->event_wfd = -1;
+		dpif->n_handlers = 0;
 
 		/* flow related */
 		ovs_rwlock_init(&dpif->flow_rwlock);
@@ -287,6 +315,7 @@
 	HMAP_FOR_EACH_SAFE(port, next, node, &dpif->ports) {
 		dpif_solaris_port_del__(dpif, port);
 	}
+	ovs_assert(hmap_count(&dpif->lowerlinks) == 0);
 	ovs_rwlock_unlock(&dpif->port_rwlock);
 
 	dpif_solaris_flow_flush__(dpif);
@@ -296,6 +325,7 @@
 	ovs_rwlock_destroy(&dpif->bridge_rwlock);
 
 	hmap_destroy(&dpif->ports);
+	hmap_destroy(&dpif->lowerlinks);
 	ovs_rwlock_destroy(&dpif->port_rwlock);
 
 	hmap_destroy(&dpif->flows);
@@ -490,15 +520,53 @@
 }
 
 static boolean_t
-port_not_used(struct dpif_solaris *dpif, uint32_t port_no)
+port_no_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) {
+		if (port->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)
+    OVS_REQ_WRLOCK(dp->port_rwlock)
+{
+	uint32_t port_no;
+
+	for (port_no = PORT_PF_PACKET_UPLINK + 1;
+	    port_no < MAC_OF_MAXPORT; port_no++) {
+		if (port_no_used(dpif, port_no))
+			continue;
+
+		return (u32_to_odp(port_no));
+	}
+
+	return (ODPP_NONE);
+}
+
+static boolean_t
+pf_port_no_used(struct dpif_solaris *dpif, uint32_t pf_port_no)
+{
+	struct dpif_solaris_lowerlink *lowerlink;
+	int i;
+
+	HMAP_FOR_EACH(lowerlink, node, &dpif->lowerlinks) {
+		for (i = 0; i < lowerlink->n_handlers; i++) {
+			ovs_assert(lowerlink->handlers != NULL);
+			ovs_assert(lowerlink->handlers[i].upcall_fd != -1);
+			if (lowerlink->handlers[i].pf_port_no == pf_port_no)
+				return (true);
 		}
 	}
 	return (false);
@@ -511,18 +579,29 @@
  * need improvement. TBD
  */
 static odp_port_t
-choose_port(struct dpif_solaris *dpif, uint32_t exclude_port_no)
+choose_pf_port(struct dpif_solaris *dpif,
+    struct dpif_solaris_lowerlink *lowerlink, int n)
     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));
+	uint32_t pf_port_no;
+	int i;
+
+	for (pf_port_no = MAC_OF_MAXPORT + 1; pf_port_no < UINT32_MAX;
+	    pf_port_no++) {
+		if (pf_port_no_used(dpif, pf_port_no))
+			continue;
+
+		for (i = 0; i < n; i++) {
+			ovs_assert(lowerlink->handlers != NULL);
+			ovs_assert(lowerlink->handlers[i].upcall_fd != -1);
+			if (lowerlink->handlers[i].pf_port_no == pf_port_no)
+				break;
 		}
+
+		if (i < n)
+			continue;
+
+		return (u32_to_odp(pf_port_no));
 	}
 
 	return (ODPP_NONE);
@@ -655,7 +734,6 @@
 	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);
@@ -703,18 +781,12 @@
 	ovs_rwlock_wrlock(&dpif->port_rwlock);
 
 	if (*port_nop == ODPP_NONE) {
-		*port_nop = choose_port(dpif, ODPP_NONE);
+		*port_nop = choose_port(dpif);
 		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 ||
 	    vtype == OVS_VPORT_TYPE_INTERNAL) {
 		uint64_t u64 = (uint32_t)(*port_nop);
@@ -732,7 +804,7 @@
 
 	port = xzalloc(sizeof (*port));
 	port->port_no = *port_nop;
-	port->pf_port_no = pf_port_no;
+	port->pf_port_no = ODPP_NONE;
 	port->name = xstrdup(name);
 	port->linkname = xstrdup(linkname);
 	port->physname = xstrdup(physname);
@@ -753,18 +825,18 @@
 		}
 	}
 
-	error = dpif_solaris_refresh_port_channel(dpif, port, physname, true);
+	error = dpif_solaris_refresh_port_channel(dpif, port, true);
 	if (error != 0) {
-		VLOG_ERR("dpif_solaris_refresh_port_channel on %s failed: %s",
+		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 ||
 	    vtype == OVS_VPORT_TYPE_INTERNAL)
-		dpif_solaris_bridge_add_port(dpif, physname, port);
+		(void) 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);
@@ -772,11 +844,7 @@
 	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);
-		}
+		dpif_solaris_destroy_port_channel(dpif, port);
 		free(port->name);
 		free(port->linkname);
 		free(port->type);
@@ -877,23 +945,19 @@
 
 	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);
-	}
+	dpif_solaris_destroy_port_channel(dpif, port);
 	if (port->vtype == OVS_VPORT_TYPE_NETDEV || port->vtype ==
 	    OVS_VPORT_TYPE_VXLAN || port->vtype == OVS_VPORT_TYPE_INTERNAL) {
-			VLOG_DBG("1.reset portno on %s", port->linkname);
-			(void) solaris_set_dlprop_ulong(port->linkname,
-			    "ofport", NULL, B_TRUE);
+		VLOG_DBG("1.reset portno on %s", port->linkname);
+		(void) solaris_set_dlprop_ulong(port->linkname,
+		    "ofport", NULL, B_TRUE);
 		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);
+	VLOG_DBG("dpif_solaris_port_del %s close xfd %d", port->name,
+	    port->xfd);
 
 	free(port->type);
 	free(port->name);
@@ -2034,15 +2098,11 @@
 		HMAP_FOR_EACH(port, node, &dpif->ports) {
 			if (strcmp(port->name, bridge) != 0)
 				continue;
-			if (port->upcall_fd != -1) {
-				(void) setsockopt(port->upcall_fd, SOL_PACKET,
-				    PACKET_REM_OF_DEFFLOW, NULL, 0);
-				(void) close(port->upcall_fd);
-			}
+			dpif_solaris_destroy_port_channel(dpif, port);
 			free(port->physname);
 			port->physname = xstrdup(physname);
 			err = dpif_solaris_refresh_port_channel(dpif, port,
-			    physname, false);
+			    false);
 			if (err == 0)
 				VLOG_DBG("dpif_solaris_migrate_internal_port %s"
 				    " on %s succeed", bridge, physname);
@@ -2053,7 +2113,6 @@
 		}
 		ovs_rwlock_unlock(&dpif->port_rwlock);
 	}
-
 }
 
 static void
@@ -2209,6 +2268,50 @@
 }
 
 static void
+dpif_solaris_destroy_lowerlink(struct dpif_solaris *dpif,
+    struct dpif_solaris_lowerlink *lowerlink)
+    OVS_REQ_WRLOCK(dpif->port_rwlock)
+{
+	int i;
+
+	VLOG_DBG("dpif_solaris_destroy_lowerlink %s", lowerlink->physname);
+	ovs_assert(lowerlink->nports == 0);
+	hmap_remove(&dpif->lowerlinks, &lowerlink->node);
+	for (i = 0; i < lowerlink->n_handlers; i++) {
+		ovs_assert(lowerlink->handlers != NULL);
+		if (lowerlink->handlers[i].upcall_fd != -1)
+			(void) close(lowerlink->handlers[i].upcall_fd);
+	}
+	if (lowerlink->handlers != NULL)
+		free(lowerlink->handlers);
+	free(lowerlink);
+}
+
+static void
+dpif_solaris_destroy_port_channel(struct dpif_solaris *dpif,
+    struct dpif_solaris_port *port)
+{
+	struct dpif_solaris_lowerlink *lowerlink;
+	ovs_assert(dpif->recv_set);
+
+	if (port->upcall_fd == -1)
+		return;
+
+	VLOG_DBG("dpif_solaris_destroy_port_channel port %s lower-link %s",
+	    port->linkname, port->physname);
+	(void) setsockopt(port->upcall_fd, SOL_PACKET,
+	    PACKET_REM_OF_DEFFLOW, &port->port_no, sizeof (uint32_t));
+	port->upcall_fd = -1;
+	port->pf_port_no = ODPP_NONE;
+
+	lowerlink = port->lowerlink;
+	port->lowerlink = NULL;
+
+	if (--lowerlink->nports == 0)
+		dpif_solaris_destroy_lowerlink(dpif, lowerlink);
+}
+
+static void
 dpif_solaris_destroy_channels(struct dpif_solaris *dpif)
     OVS_REQ_WRLOCK(dpif->upcall_lock)
 {
@@ -2217,25 +2320,101 @@
 	if (!dpif->recv_set)
 		return;
 
+	VLOG_DBG("dpif_solaris_destroy_channels");
+
 	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;
-		}
+		dpif_solaris_destroy_port_channel(dpif, port);
 	}
 	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;
+	dpif->n_handlers = 0;
 }
 
 static int
 dpif_solaris_refresh_port_channel(struct dpif_solaris *dpif,
-    struct dpif_solaris_port *port, const char *physname, boolean_t notify)
+    struct dpif_solaris_port *port, boolean_t notify)
     OVS_REQ_WRLOCK(dpif->port_rwlock)
 {
+	struct dpif_solaris_lowerlink *lowerlink;
+	mac_of_ports_t mofport;
+	odp_port_t pf_port_no;
+	int fd, idx, error;
+	boolean_t lowerlink_found = false;
+
+	if (!dpif->recv_set || (port->vtype != OVS_VPORT_TYPE_NETDEV &&
+	    port->vtype != OVS_VPORT_TYPE_VXLAN && port->vtype !=
+	    OVS_VPORT_TYPE_INTERNAL))
+		return (0);
+
+	VLOG_DBG("dpif_solaris_refresh_port_channel(%s) port_no %d on %s%s",
+	    port->linkname, port->port_no, port->physname,
+	    notify ? " notify" : "");
+
+	HMAP_FOR_EACH(lowerlink, node, &dpif->lowerlinks) {
+		if (strcmp(lowerlink->physname, port->physname) == 0) {
+			lowerlink_found = true;
+			break;
+		}
+	}
+
+	/* Create the lower link if it is not found */
+	if (!lowerlink_found) {
+		error = dpif_solaris_create_lowerlink(dpif, port->physname,
+		    &lowerlink);
+		if (error != 0) {
+			VLOG_DBG("dpif_solaris_refresh_port_channel lowerlink "
+			    "%s creation failed %s", port->physname,
+			    ovs_strerror(error));
+			return (error);
+		}
+	}
+
+	idx = port->is_uplink ? 0 :
+	    (lowerlink->next_idx % lowerlink->n_handlers);
+	fd = lowerlink->handlers[idx].upcall_fd;
+	pf_port_no = lowerlink->handlers[idx].pf_port_no;
+
+	VLOG_DBG("dpif_solaris_refresh_port_channel(%s) port_no %d pf_port_no "
+	    "%d upcall_fd %d", port->linkname, port->port_no, pf_port_no, fd);
+
+	mofport.mop_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));
+		if (!lowerlink_found)
+			dpif_solaris_destroy_lowerlink(dpif, lowerlink);
+		return (error);
+	}
+	if (!port->is_uplink)
+		lowerlink->next_idx ++;
+	lowerlink->nports ++;
+	port->lowerlink = lowerlink;
+	port->upcall_fd = fd;
+	port->pf_port_no = pf_port_no;
+
+	/* 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->linkname, ovs_strerror(errno));
+	}
+
+	return (0);
+}
+
+static int
+dpif_solaris_create_upfd(struct dpif_solaris *dpif, const char *physname,
+    int *upcall_fdp) OVS_REQ_WRLOCK(dpif->port_rwlock)
+{
 	int fd, flags, onoff = 1;
 	struct sockaddr_ll sll;
 	datalink_id_t linkid;
@@ -2243,18 +2422,15 @@
 	dladm_status_t status;
 	int error;
 
-	if (!dpif->recv_set || (port->vtype != OVS_VPORT_TYPE_NETDEV &&
-	    port->vtype != OVS_VPORT_TYPE_VXLAN && port->vtype !=
-	    OVS_VPORT_TYPE_INTERNAL))
-		return (0);
-
-	VLOG_DBG("dpif_solaris_refresh_port_channel(%s) port_no: %d on %s%s",
-	    port->linkname, port->port_no, physname, notify ? " notify" : "");
+	if (!dpif->recv_set)
+		return (EINVAL);
+
+	VLOG_DBG("dpif_solaris_create_upfd() on %s", physname);
 
 	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));
+		VLOG_ERR("dpif_solaris_create_upfd %s failed to "
+		    "create socket: %s", physname, ovs_strerror(errno));
 		return (errno);
 	}
 
@@ -2262,8 +2438,8 @@
 	    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));
+		VLOG_ERR("dpif_solaris_create_upfd %s failed to get "
+		    "linkid: %s", physname, ovs_strerror(error));
 		(void) close(fd);
 		return (error);
 	}
@@ -2274,20 +2450,8 @@
 	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));
+		VLOG_ERR("dpif_solaris_create_upfd %s failed to bind: "
+		    "%s", physname, ovs_strerror(error));
 		(void) close(fd);
 		return (error);
 	}
@@ -2295,42 +2459,111 @@
 	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,
+		VLOG_ERR("dpif_solaris_create_upfd %s failed to set "
+		    "PACKET_AUXDATAQ: %s", physname,
 		    ovs_strerror(error));
-		(void) setsockopt(fd, SOL_PACKET, PACKET_REM_OF_DEFFLOW,
-		    NULL, 0);
 		(void) close(fd);
 		return (error);
 	}
 
+	/* Disable the promiscous mode on this socket */
+	mofport.mop_port = 0;
+	mofport.mop_sport = 0;
+	if (setsockopt(fd, SOL_PACKET, PACKET_ADD_OF_DEFFLOW,
+	    &mofport, sizeof (mac_of_ports_t)) != 0) {
+		error = errno;
+		VLOG_ERR("dpif_solaris_create_upfd %s failed to set "
+		    "PACKET_ADD_OF_DEFFLOW port 0: %s", physname,
+		    ovs_strerror(error));
+		(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);
+		VLOG_ERR("dpif_solaris_create_upfd %s failed to set "
+		    "non-block: %s", physname, ovs_strerror(error));
 		(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));
-	}
+	VLOG_DBG("dpif_solaris_create_upfd %s fd=%d", physname, fd);
+	*upcall_fdp = fd;
 
 	return (0);
 }
 
+static int
+dpif_solaris_create_lowerlink(struct dpif_solaris *dpif, char *physname,
+    struct dpif_solaris_lowerlink **lowerlinkp)
+    OVS_REQ_WRLOCK(dpif->port_rwlock)
+{
+	struct dpif_solaris_lowerlink *lowerlink;
+	odp_port_t pf_port_no;
+	int i, err;
+
+	if (!dpif->recv_set)
+		return (0);
+
+	VLOG_DBG("dpif_solaris_create_lowerlink create %d upfds over lower "
+	    "link %s", dpif->n_handlers, physname);
+
+	lowerlink = xzalloc(sizeof (*lowerlink));
+	if (strlcpy(lowerlink->physname, physname,
+	    sizeof (lowerlink->physname)) >= sizeof (lowerlink->physname)) {
+		free(lowerlink);
+		return (ENOBUFS);
+	}
+
+	lowerlink->n_handlers = dpif->n_handlers;
+	lowerlink->handlers = xzalloc(lowerlink->n_handlers *
+	    sizeof (struct dpif_solaris_handler));
+	lowerlink->nports = 0;
+	lowerlink->next_idx = 0;
+
+	for (i = 0; i < lowerlink->n_handlers; i++)
+		lowerlink->handlers[i].upcall_fd = -1;
+
+	for (i = 0; i < lowerlink->n_handlers; i++) {
+		err = dpif_solaris_create_upfd(dpif,
+		    lowerlink->physname, &(lowerlink->handlers[i].upcall_fd));
+		if (err != 0) {
+			VLOG_ERR("dpif_solaris_create_lowerlink create %dth "
+			    "upfd over %s failed %s", i, lowerlink->physname,
+			    ovs_strerror(err));
+			goto fail;
+		} else {
+			VLOG_DBG("dpif_solaris_create_lowerlink create %dth "
+			    "upfd over %s: %d", i, lowerlink->physname,
+			    lowerlink->handlers[i].upcall_fd);
+		}
+		pf_port_no = (i == 0) ? PORT_PF_PACKET_UPLINK :
+		    choose_pf_port(dpif, lowerlink, i);
+		if (pf_port_no == ODPP_NONE) {
+			VLOG_ERR("dpif_solaris_create_lowerlink no usable "
+			    "port_no avaiable for %dth upfd over %s", i,
+			    lowerlink->physname);
+			err = EFBIG;
+			goto fail;
+		} else {
+			VLOG_DBG("dpif_solaris_create_lowerlink "
+			    "avaiable pf_port_no for %dth upfd over %s: %d", i,
+			    lowerlink->physname, pf_port_no);
+		}
+		lowerlink->handlers[i].pf_port_no = pf_port_no;
+	}
+	hmap_insert(&dpif->lowerlinks, &lowerlink->node,
+	    hash_string(lowerlink->physname, 0));
+
+	*lowerlinkp = lowerlink;
+	return (0);
+
+fail:
+	dpif_solaris_destroy_lowerlink(dpif, lowerlink);
+	return (err);
+}
+
 /*
  * Synchronizes 'channels' in 'dpif->handlers'  with the set of vports
  * currently in 'dpif' in the kernel, by adding a new set of channels for
@@ -2373,14 +2606,14 @@
 		return (error);
 	}
 
+	dpif->n_handlers = n_handlers;
 	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->physname, false);
+		error = dpif_solaris_refresh_port_channel(dpif, port, false);
 		if (error != 0) {
 			ovs_rwlock_unlock(&dpif->port_rwlock);
 			dpif_solaris_destroy_channels(dpif);
@@ -2510,10 +2743,10 @@
 	    _CMSG_HDR_ALIGNMENT];
 	uint8_t pktbuf[65536];
 	int pktlen;
-	struct dpif_solaris_port *port;
+	struct dpif_solaris_lowerlink *lowerlink;
 	odp_port_t port_no;
 	uint16_t action_type;
-	int error;
+	int i, fd, error;
 
 	if (!dpif->recv_set) {
 		return (EAGAIN);
@@ -2522,86 +2755,103 @@
 	(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 = NULL;
-			struct flow_tnl		*tun;
-			struct ofpbuf		pkt;
-			struct tpacket_auxdata	aux;
-
-			action_type = FLOW_ACTION_OF_MAX;
-			for (cmsg = CMSG_FIRSTHDR(&msg); cmsg;
-			    cmsg = CMSG_NXTHDR(&msg, cmsg)) {
-				if (cmsg->cmsg_len <
-				    CMSG_LEN(sizeof (struct tpacket_auxdata)) ||
-				    cmsg->cmsg_level != SOL_PACKET ||
-				    cmsg->cmsg_type != PACKET_AUXDATA)
+	ovs_rwlock_rdlock(&dpif->port_rwlock);
+	HMAP_FOR_EACH(lowerlink, node, &dpif->lowerlinks) {
+		for (i = 0; i < lowerlink->n_handlers; i++) {
+			if ((fd = lowerlink->handlers[i].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(fd, &msg, MSG_TRUNC);
+			if (pktlen > 0) {
+				struct pkt_metadata	md;
+				mactun_info_t		*tuninfop = NULL;
+				struct flow_tnl		*tun;
+				struct ofpbuf		pkt;
+				struct tpacket_auxdata	aux;
+
+				action_type = FLOW_ACTION_OF_MAX;
+				for (cmsg = CMSG_FIRSTHDR(&msg); cmsg;
+				    cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+					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 %s fd "
+					    "%d len %d but with unknown "
+					    "auxdata type %d",
+					    lowerlink->physname, fd, pktlen,
+					    action_type);
 					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);
-
-			ovs_assert(tuninfop != NULL);
-			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,
+				}
+
+				if (port_no == 0) {
+					VLOG_ERR("dpif_solaris_recv__ on %s fd "
+					    "%d len %d but with unknown "
+					    "port_no 0",
+					    lowerlink->physname, fd, pktlen);
+					continue;
+				}
+
+				VLOG_DBG("dpif_solaris_recv__ on %s fd %d len "
+				    "%d type %s (src_port = %u)",
+				    lowerlink->physname, fd, pktlen,
 				    action_type == FLOW_ACTION_MISSED_PKT ?
-				    "miss_pkt" : "fwd_to_controller",
-				    error);
-			} else {
-				ovs_rwlock_unlock(&dpif->port_rwlock);
-				return (0);
+				    "miss_pkt" : "fwd_controller", port_no);
+
+				ovs_assert(tuninfop != NULL);
+				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 %s fd %d action %s "
+					    "failed %d", lowerlink->physname,
+					    fd, action_type ==
+					    FLOW_ACTION_MISSED_PKT ?
+					    "miss_pkt" : "fwd_to_controller",
+					    error);
+				} else {
+					ovs_rwlock_unlock(&dpif->port_rwlock);
+					return (0);
+				}
 			}
 		}
 	}
@@ -2628,19 +2878,24 @@
 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;
+	struct dpif_solaris_lowerlink *lowerlink;
+	int i, fd;
 
 	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);
+	if (!dpif->recv_set)
+		goto done;
+
+	poll_fd_wait(dpif->event_rfd, POLLIN);
+	ovs_rwlock_rdlock(&dpif->port_rwlock);
+	HMAP_FOR_EACH(lowerlink, node, &dpif->lowerlinks) {
+		for (i = 0; i < lowerlink->n_handlers; i++) {
+			if ((fd = lowerlink->handlers[i].upcall_fd) != -1) {
+				poll_fd_wait(fd, POLLIN);
 			}
 		}
-		ovs_rwlock_unlock(&dpif->port_rwlock);
 	}
+	ovs_rwlock_unlock(&dpif->port_rwlock);
+done:
 	ovs_rwlock_unlock(&dpif->upcall_lock);
 }
 
@@ -2648,16 +2903,22 @@
 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);
+	struct dpif_solaris_lowerlink *lowerlink;
+	int i, fd;
+
+	if (!dpif->recv_set)
+		return;
+
+	drain_rcvbuf(dpif->event_rfd);
+	ovs_rwlock_rdlock(&dpif->port_rwlock);
+	HMAP_FOR_EACH(lowerlink, node, &dpif->lowerlinks) {
+		for (i = 0; i < lowerlink->n_handlers; i++) {
+			if ((fd = lowerlink->handlers[i].upcall_fd) != -1) {
+				drain_rcvbuf(fd);
+			}
 		}
-		ovs_rwlock_unlock(&dpif->port_rwlock);
 	}
+	ovs_rwlock_unlock(&dpif->port_rwlock);
 }
 
 static void
@@ -2672,10 +2933,17 @@
 }
 
 static int
-dpif_solaris_handlers_set(struct dpif *dpif OVS_UNUSED,
-    uint32_t n_handlers OVS_UNUSED)
+dpif_solaris_handlers_set(struct dpif *dpif_, uint32_t n_handlers)
 {
-	return (0);
+	struct dpif_solaris *dpif = dpif_solaris_cast(dpif_);
+	int err = 0;
+
+	VLOG_DBG("dpif_solaris_handlers_set %d", n_handlers);
+	ovs_rwlock_wrlock(&dpif->upcall_lock);
+	if (dpif->recv_set)
+		err = dpif_solaris_refresh_channels(dpif, n_handlers);
+	ovs_rwlock_unlock(&dpif->upcall_lock);
+	return (err);
 }
 
 const struct dpif_class dpif_solaris_class = {
--- a/components/openvswitch/files/lib/util-solaris.c	Fri Sep 16 23:33:54 2016 -0500
+++ b/components/openvswitch/files/lib/util-solaris.c	Mon Sep 19 09:53:17 2016 -0700
@@ -1341,18 +1341,9 @@
 	int	i, err, range_cnt;
 	ofport_range_t	*ofports_range;
 
-	if (nofports == 0) {
-		if (snprintf(buf, sizeof (buf), "%soutports%cdrop",
-		    strlen(str) == 0 ? "" : FP_MULTI_ACTION_DELIM_STR,
-		    FP_NAME_VAL_DELIM) >= sizeof (buf)) {
-			return (ENOBUFS);
-		}
-
-		if (strlcat(str, buf, strsize) >= strsize)
-			return (ENOBUFS);
-
+
+	if (nofports == 0)
 		return (0);
-	}
 
 	ofports_range = malloc(nofports * sizeof (ofport_range_t));
 	if (ofports_range == NULL)
@@ -1990,10 +1981,8 @@
 		goto out;
 
 	/* if actions_len == 0, then the action is drop */
-	if (actions_len == 0) {
-		err = flow_ofports2propstr(str, sizeof (str), ofports, 0);
+	if (actions_len == 0)
 		goto out;
-	}
 
 	NL_ATTR_FOR_EACH_UNSAFE(a, left, actions_nlattr, actions_len) {
 		lasttype = type;
@@ -2562,28 +2551,6 @@
 }
 
 static int
-flow_propval2action_outports_drop(char **propvals, int nval,
-    struct ofpbuf *action)
-{
-	int i;
-	char *endp = NULL;
-	int64_t n;
-
-	if (strcmp(propvals[0], "drop") == 0)
-		return (0);
-
-	errno = 0;
-	for (i = 0; i < nval; i++) {
-		n = strtoull(propvals[i], &endp, 10);
-		if ((errno != 0) || *endp != '\0')
-			return (EINVAL);
-		nl_msg_put_u32(action, OVS_ACTION_ATTR_OUTPUT, (uint32_t)n);
-	}
-
-	return (0);
-}
-
-static int
 flow_propval2action_setpri(char **propvals OVS_UNUSED, int nval OVS_UNUSED,
     struct ofpbuf *action OVS_UNUSED)
 {
@@ -2856,8 +2823,7 @@
 			errno = 0;
 			endp = NULL;
 			value = strtoul(sep, &endp, 16);
-			if (errno != 0 || value == 0 || value > 0xff ||
-			    *endp != '\0') {
+			if (errno != 0 || value > 0xff || *endp != '\0') {
 				goto out;
 			}
 			ipv6.ipv6_label = value;
@@ -3098,80 +3064,89 @@
 }
 
 static int
-flow_propstr2vals(char *key, char *val, char ***propvalsp, int *valcntp)
+flow_propstr2outports(char *key, char *val, uint32_t outports[], int *valcntp)
 {
-	char **propvals = *propvalsp, *curr;
-	int i, j, len;
+	char *curr;
+	int maxcnt = *valcntp, i, j, len;
+	char *ofp_min, *ofp_max, *tmp = NULL, *endp = NULL;
+	uint32_t of_min, of_max, i_of;
+	boolean_t match, is_range;
 	char c;
 
 	dpif_log(0, "flow_propstr2vals key %s val %s", key, val);
 	len = strlen(val);
 
-	if (strcmp(key, "outports") == 0) {
-		char *ofp_min, *ofp_max, *tmp = NULL, *endp = NULL;
-		char ofport_val[10];
-		uint32_t of_min, of_max, i_of;
-		boolean_t match, is_range;
-		match = is_range = B_FALSE;
-		ofp_min = ofp_max = val;
-
-		for (i = 0, j = 0, curr = val; i < len; i++) {
-			ofp_min = ofp_max = curr;
-			c = val[i];
-			match = (c == FP_ACTION_MULTI_VAL_DELIM ||
-			    c == FP_ACTION_PORT_RANGE_DELIM);
-			if (!match && i != len -1)
-				continue;
-			if (match)
-				val[i] = '\0';
-
-			if (c == FP_ACTION_PORT_RANGE_DELIM) {
-				tmp = curr;
-				curr = val + i + 1;
-				is_range = B_TRUE;
-				continue;
-			}
-
-			if (is_range == B_TRUE) {
-				ofp_min = tmp;
-				is_range = B_FALSE;
-			}
-			of_min = (uint32_t)strtoul(ofp_min, &endp, 10);
-			of_max = (uint32_t)strtoul(ofp_max, &endp, 10);
-
-			for (i_of = of_min; i_of <= of_max; i_of++) {
-				bzero(ofport_val, sizeof (ofport_val));
-				snprintf(ofport_val, sizeof (ofport_val), "%u",
-				    i_of);
-
-				if (strlcpy(propvals[j++], ofport_val,
-				    DLADM_PROP_VAL_MAX) >= DLADM_PROP_VAL_MAX) {
-					dpif_log(EINVAL, "flow_propstr2vals"
-					    "key %s %dth string too long %s",
-					    key, j - 1, ofport_val);
-					return (EINVAL);
-				}
-			}
+	ovs_assert(strcmp(key, "outports") == 0);
+
+	match = is_range = B_FALSE;
+	ofp_min = ofp_max = val;
+
+	for (i = 0, j = 0, curr = val; i < len && j < maxcnt; i++) {
+		ofp_min = ofp_max = curr;
+		c = val[i];
+		match = (c == FP_ACTION_MULTI_VAL_DELIM ||
+		    c == FP_ACTION_PORT_RANGE_DELIM);
+		if (!match && i != len -1)
+			continue;
+		if (match)
+			val[i] = '\0';
+
+		if (c == FP_ACTION_PORT_RANGE_DELIM) {
+			tmp = curr;
 			curr = val + i + 1;
+			is_range = B_TRUE;
+			continue;
 		}
-	} else {
-		for (i = 0, j = 0, curr = val; i < len; i++) {
-			if ((c = val[i]) != FP_ACTION_MULTI_VAL_DELIM &&
-			    i != len -1)
-				continue;
-
-			if (c == FP_ACTION_MULTI_VAL_DELIM)
-				val[i] = '\0';
-
-			if (strlcpy(propvals[j++], curr, DLADM_PROP_VAL_MAX) >=
-			    DLADM_PROP_VAL_MAX) {
-				dpif_log(EINVAL, "flow_propstr2vals key %s %dth"
-				    " string too long %s", key, j - 1, curr);
-				return (EINVAL);
-			}
-			curr = val + i + 1;
+
+		if (is_range == B_TRUE) {
+			ofp_min = tmp;
+			is_range = B_FALSE;
 		}
+		of_min = (uint32_t)strtoul(ofp_min, &endp, 10);
+		of_max = (uint32_t)strtoul(ofp_max, &endp, 10);
+
+		for (i_of = of_min; i_of <= of_max; i_of++)
+			outports[j++] = i_of;
+		curr = val + i + 1;
 	}
+	if (j >= maxcnt)
+		dpif_log(ENOBUFS, "flow_propstr2outports action truncated");
+	*valcntp = j;
+	for (i = 0; i < j; i++)
+		dpif_log(0, "flow_propstr2outports key %s %dth: %d", key, i+1,
+		    outports[i]);
+	return (0);
+}
+
+static int
+flow_propstr2vals(char *key, char *val, char ***propvalsp, int *valcntp)
+{
+	char **propvals = *propvalsp, *curr;
+	int maxcnt = *valcntp, i, j, len;
+	char c;
+
+	dpif_log(0, "flow_propstr2vals key %s val %s", key, val);
+	len = strlen(val);
+
+	ovs_assert(strcmp(key, "outports") != 0);
+	for (i = 0, j = 0, curr = val; i < len && j < maxcnt; i++) {
+		if ((c = val[i]) != FP_ACTION_MULTI_VAL_DELIM &&
+		    i != len -1)
+			continue;
+
+		if (c == FP_ACTION_MULTI_VAL_DELIM)
+			val[i] = '\0';
+
+		if (strlcpy(propvals[j++], curr, DLADM_PROP_VAL_MAX) >=
+		    DLADM_PROP_VAL_MAX) {
+			dpif_log(EINVAL, "flow_propstr2vals key %s %dth"
+			    " string too long %s", key, j - 1, curr);
+			return (EINVAL);
+		}
+		curr = val + i + 1;
+	}
+	if (j >= maxcnt)
+		dpif_log(ENOBUFS, "flow_propstr2vals action truncated");
 	*valcntp = j;
 	for (i = 0; i < j; i++)
 		dpif_log(0, "flow_propstr2vals key %s %dth: %s", key, i+1,
@@ -3184,17 +3159,18 @@
 {
 	char ofaction_str[4096];
 	char **pvals, *buf = NULL;
+	uint32_t outports[MAC_OF_MAXPORT];
 	size_t len;
 	char *curr, *key, c;
 	boolean_t match;
-	int nval, err = 0, i;
-
-	buf = malloc((sizeof (char *) +
-	    DLADM_PROP_VAL_MAX) * DLADM_MAX_PROP_VALCNT);
+	int nval, err = 0, i, j;
+
+	buf = malloc((sizeof (char *) + DLADM_PROP_VAL_MAX) *
+	    DLADM_PROP_VAL_MAX);
 
 	pvals = (char **)(void *)buf;
-	for (i = 0; i < DLADM_MAX_PROP_VALCNT; i++) {
-		pvals[i] = buf + sizeof (char *) * DLADM_MAX_PROP_VALCNT +
+	for (i = 0; i < DLADM_PROP_VAL_MAX; i++) {
+		pvals[i] = buf + sizeof (char *) * DLADM_PROP_VAL_MAX +
 		    i * DLADM_PROP_VAL_MAX;
 	}
 
@@ -3231,14 +3207,21 @@
 			goto done;
 		}
 
-		err = flow_propstr2vals(key, curr, &pvals, &nval);
+		if (strcmp(key, "outports") == 0) {
+			nval = MAC_OF_MAXPORT;
+			err = flow_propstr2outports(key, curr, outports, &nval);
+		} else {
+			nval = DLADM_PROP_VAL_MAX;
+			err = flow_propstr2vals(key, curr, &pvals, &nval);
+		}
 		if (err != 0)
 			goto done;
 
-		if (strcmp(key, "outports") == 0)
-			err = flow_propval2action_outports_drop(pvals, nval,
-			    action);
-		else if (strcmp(key, "max-bw") == 0)
+		if (strcmp(key, "outports") == 0) {
+			for (j = 0; j < nval; j++)
+				nl_msg_put_u32(action, OVS_ACTION_ATTR_OUTPUT,
+				    outports[j]);
+		} else if (strcmp(key, "max-bw") == 0)
 			err = flow_propval2action_setpri(pvals, nval, action);
 		else if (strcmp(key, "controller") == 0)
 			err = flow_propval2action_controller(pvals, nval,
@@ -3292,7 +3275,7 @@
 			goto out;
 		valcnt = 1;
 		if (strlcpy(propval, val->ddlv_sval, sizeof (propval)) >=
-		    DLADM_STRSIZE)
+		    sizeof (propval))
 			goto out;
 		break;
 	case DDLVT_STRINGS:
@@ -3302,7 +3285,7 @@
 		if (valcnt != 1 || strlen(val->ddlv_slist[0]) == 0)
 			goto out;
 		if (strlcpy(propval, val->ddlv_slist[0], sizeof (propval)) >=
-		    DLADM_STRSIZE)
+		    sizeof (propval))
 			goto out;
 		break;
 	case DDLVT_ULONG: