--- old/usr/src/cmd/zoneadm/zoneadm.c	Wed Aug 31 11:44:07 2016
+++ new/usr/src/cmd/zoneadm/zoneadm.c	Wed Aug 31 11:44:07 2016
@@ -102,6 +102,7 @@
 	char		zroot[MAXPATHLEN];
 	char		zuuid[UUID_PRINTABLE_STRING_LENGTH];
 	zone_iptype_t	ziptype;
+	zoneid_t	zdid;
 } zone_entry_t;
 
 #define	CLUSTER_BRAND_NAME	"cluster"
@@ -444,6 +445,7 @@
 	}
 	if (!verbose) {
 		char *cp, *clim;
+		char zdid[80];
 
 		if (!parsable) {
 			(void) printf("%s\n", zent->zname);
@@ -459,8 +461,12 @@
 			(void) printf("%.*s\\:", clim - cp, cp);
 			cp = clim + 1;
 		}
-		(void) printf("%s:%s:%s:%s\n", cp, zent->zuuid, zent->zbrand,
-		    ip_type_str);
+		if (zent->zdid == -1)
+			zdid[0] = '\0';
+		else
+			(void) snprintf(zdid, sizeof (zdid), "%d", zent->zdid);
+		(void) printf("%s:%s:%s:%s:%s\n", cp, zent->zuuid, zent->zbrand,
+		    ip_type_str, zdid);
 		return;
 	}
 	if (zent->zstate_str != NULL) {
@@ -555,6 +561,22 @@
 		return (Z_OK);
 	}
 
+	if ((handle = zonecfg_init_handle()) == NULL) {
+		zperror2(zent->zname, gettext("could not init handle"));
+		return (Z_ERR);
+	}
+	if ((err = zonecfg_get_handle(zent->zname, handle)) != Z_OK) {
+		zperror2(zent->zname, gettext("could not get handle"));
+		zonecfg_fini_handle(handle);
+		return (Z_ERR);
+	}
+
+	if ((err = zonecfg_get_iptype(handle, &zent->ziptype)) != Z_OK) {
+		zperror2(zent->zname, gettext("could not get ip-type"));
+		zonecfg_fini_handle(handle);
+		return (Z_ERR);
+	}
+
 	/*
 	 * There is a race condition where the zone could boot while
 	 * we're walking the index file.  In this case the zone state
@@ -575,25 +597,11 @@
 				zent->ziptype = ZS_EXCLUSIVE;
 			else
 				zent->ziptype = ZS_SHARED;
-			return (Z_OK);
 		}
 	}
 
-	if ((handle = zonecfg_init_handle()) == NULL) {
-		zperror2(zent->zname, gettext("could not init handle"));
-		return (Z_ERR);
-	}
-	if ((err = zonecfg_get_handle(zent->zname, handle)) != Z_OK) {
-		zperror2(zent->zname, gettext("could not get handle"));
-		zonecfg_fini_handle(handle);
-		return (Z_ERR);
-	}
+	zent->zdid = zonecfg_get_did(handle);
 
-	if ((err = zonecfg_get_iptype(handle, &zent->ziptype)) != Z_OK) {
-		zperror2(zent->zname, gettext("could not get ip-type"));
-		zonecfg_fini_handle(handle);
-		return (Z_ERR);
-	}
 	zonecfg_fini_handle(handle);
 
 	return (Z_OK);
@@ -2788,6 +2796,61 @@
 	return (return_code);
 }
 
+/*
+ * Called when readying or booting a zone.  We double check that the zone's
+ * debug ID is set and is unique.  This covers the case of pre-existing zones
+ * with no ID.  Also, its possible that a zone was migrated to this host
+ * and as a result it has a duplicate ID.  In this case we preserve the ID
+ * of the first zone we match on in the index file (since it was there before
+ * the current zone) and we assign a new unique ID to the current zone.
+ * Return true if we assigned a new ID, indicating that the zone configuration
+ * needs to be saved.
+ */
+static boolean_t
+verify_fix_did(zone_dochandle_t handle)
+{
+	zoneid_t mydid;
+	zone_entry_t zent;
+	FILE *cookie;
+	char *name;
+	boolean_t fix = B_FALSE;
+
+	mydid = zonecfg_get_did(handle);
+	if (mydid == -1) {
+		zonecfg_set_did(handle);
+		return (B_TRUE);
+	}
+
+	/* Get the full list of zones from the configuration. */
+	cookie = setzoneent();
+	while ((name = getzoneent(cookie)) != NULL) {
+		if (strcmp(target_zone, name) == 0) {
+			free(name);
+			break;	/* Once we find our entry, stop. */
+		}
+
+		if (strcmp(name, "global") == 0 ||
+		    lookup_zone_info(name, ZONE_ID_UNDEFINED, &zent) != Z_OK) {
+			free(name);
+			continue;
+		}
+
+		free(name);
+		if (zent.zdid == mydid) {
+			fix = B_TRUE;
+			break;
+		}
+	}
+	endzoneent(cookie);
+
+	if (fix) {
+		zonecfg_set_did(handle);
+		return (B_TRUE);
+	}
+
+	return (B_FALSE);
+}
+
 static int
 verify_details(int cmd_num, char *argv[])
 {
@@ -2850,9 +2913,7 @@
 	if (cmd_num == CMD_READY || cmd_num == CMD_BOOT) {
 		int vcommit = 0, obscommit = 0;
 
-#if 0 /* XXX KEBE not yet */
 		vcommit = verify_fix_did(handle);
-#endif /* XXX KEBE not yet */
 		obscommit = zonecfg_fix_obsolete(handle);
 
 		if (vcommit || obscommit)
--- old/usr/src/cmd/zoneadmd/zcons.c	Wed Aug 31 11:44:07 2016
+++ new/usr/src/cmd/zoneadmd/zcons.c	Wed Aug 31 11:44:07 2016
@@ -118,6 +118,8 @@
 
 #define	CONSOLE_SOCKPATH	ZONES_TMPDIR "/%s.console_sock"
 
+#define	ZCONS_RETRY		10
+
 static int	serverfd = -1;	/* console server unix domain socket fd */
 char boot_args[BOOTARGS_MAX];
 
@@ -129,8 +131,11 @@
  */
 static int eventstream[2];
 
+/* flag used to cope with race creating master zcons devlink */
+static boolean_t master_zcons_failed = B_FALSE;
+/* flag to track if we've seen a state change when there is no master zcons */
+static boolean_t state_changed = B_FALSE;
 
-
 int
 eventstream_init()
 {
@@ -407,6 +412,11 @@
 	 * Open the master side of the console and issue the ZC_HOLDSLAVE ioctl,
 	 * which will cause the master to retain a reference to the slave.
 	 * This prevents ttymon from blowing through the slave's STREAMS anchor.
+	 *
+	 * In very rare cases the open returns ENOENT if devfs doesn't have
+	 * everything setup yet due to heavy zone startup load. Wait for
+	 * 1 sec. and retry a few times. Even if we can't setup the zone's
+	 * console, we still go ahead and boot the zone.
 	 */
 	(void) snprintf(conspath, sizeof (conspath), "/dev/zcons/%s/%s",
 	    zone_name, ZCONS_MASTER_NAME);
@@ -413,37 +423,45 @@
 	if ((masterfd = open(conspath, O_RDWR | O_NOCTTY)) == -1) {
 		zerror(zlogp, B_TRUE, "ERROR: could not open master side of "
 		    "zone console for %s to acquire slave handle", zone_name);
-		goto error;
+		master_zcons_failed = B_TRUE;
 	}
 	(void) snprintf(conspath, sizeof (conspath), "/dev/zcons/%s/%s",
 	    zone_name, ZCONS_SLAVE_NAME);
-	if ((slavefd = open(conspath, O_RDWR | O_NOCTTY)) == -1) {
+	for (i = 0; i < ZCONS_RETRY; i++) {
+		slavefd = open(conspath, O_RDWR | O_NOCTTY);
+		if (slavefd >= 0 || errno != ENOENT)
+			break;
+		(void) sleep(1);
+	}
+	if (slavefd == -1)
 		zerror(zlogp, B_TRUE, "ERROR: could not open slave side of zone"
 		    " console for %s to acquire slave handle", zone_name);
-		(void) close(masterfd);
-		goto error;
-	}
+
 	/*
 	 * This ioctl can occasionally return ENXIO if devfs doesn't have
 	 * everything plumbed up yet due to heavy zone startup load. Wait for
 	 * 1 sec. and retry a few times before we fail to boot the zone.
 	 */
-	for (i = 0; i < 5; i++) {
-		if (ioctl(masterfd, ZC_HOLDSLAVE, (caddr_t)(intptr_t)slavefd)
-		    == 0) {
-			rv = 0;
-			break;
-		} else if (errno != ENXIO) {
-			break;
+	if (masterfd != -1 && slavefd != -1) {
+		for (i = 0; i < ZCONS_RETRY; i++) {
+			if (ioctl(masterfd, ZC_HOLDSLAVE,
+			    (caddr_t)(intptr_t)slavefd) == 0) {
+				rv = 0;
+				break;
+			} else if (errno != ENXIO) {
+				break;
+			}
+			(void) sleep(1);
 		}
-		(void) sleep(1);
+		if (rv != 0)
+			zerror(zlogp, B_TRUE, "ERROR: error while acquiring "
+			    "slave handle of zone console for %s", zone_name);
 	}
-	if (rv != 0)
-		zerror(zlogp, B_TRUE, "ERROR: error while acquiring slave "
-		    "handle of zone console for %s", zone_name);
 
-	(void) close(slavefd);
-	(void) close(masterfd);
+	if (slavefd != -1)
+		(void) close(slavefd);
+	if (masterfd != -1)
+		(void) close(masterfd);
 
 error:
 	if (ddef_hdl)
@@ -874,7 +892,6 @@
 	if (init_console_dev(zlogp) == -1) {
 		zerror(zlogp, B_FALSE,
 		    "console setup: device initialization failed");
-		return (-1);
 	}
 
 	if ((serverfd = init_console_sock(zlogp)) == -1) {
@@ -886,6 +903,17 @@
 }
 
 /*
+ * Maintain a simple flag that tracks if we have seen at least one state
+ * change. This is currently only used to handle the special case where we are
+ * running without a console device, which is what normally drives shutdown.
+ */
+void
+zcons_statechanged()
+{
+	state_changed = B_TRUE;
+}
+
+/*
  * serve_console() is the master loop for driving console I/O.  It is also the
  * routine which is ultimately responsible for "pulling the plug" on zoneadmd
  * when it realizes that the daemon should shut down.
@@ -903,6 +931,7 @@
 	int masterfd;
 	zone_state_t zstate;
 	char conspath[MAXPATHLEN];
+	static boolean_t cons_warned = B_FALSE;
 
 	(void) snprintf(conspath, sizeof (conspath),
 	    "/dev/zcons/%s/%s", zone_name, ZCONS_MASTER_NAME);
@@ -910,6 +939,46 @@
 	for (;;) {
 		masterfd = open(conspath, O_RDWR|O_NONBLOCK|O_NOCTTY);
 		if (masterfd == -1) {
+			if (master_zcons_failed) {
+				/*
+				 * If we don't have a console and the zone is
+				 * not shutting down, there may have been a
+				 * race/failure with devfs while creating the
+				 * console. In this case we want to leave the
+				 * zone up, even without a console, so
+				 * periodically recheck.
+				 */
+				int i;
+
+				/*
+				 * In the normal flow of this loop, we use
+				 * do_console_io to give things a chance to get
+				 * going first. However, in this case we can't
+				 * use that, so we have to wait for at least
+				 * one state change before checking the state.
+				 */
+				for (i = 0; i < 60; i++) {
+					if (state_changed)
+						break;
+					(void) sleep(1);
+				}
+
+				if (i < 60 && zone_get_state(zone_name,
+				    &zstate) == Z_OK &&
+				    (zstate == ZONE_STATE_READY ||
+				    zstate == ZONE_STATE_RUNNING)) {
+					if (!cons_warned) {
+						zerror(zlogp, B_FALSE,
+						    "WARNING: missing zone "
+						    "console for %s",
+						    zone_name);
+						cons_warned = B_TRUE;
+					}
+					(void) sleep(ZCONS_RETRY);
+					continue;
+				}
+			}
+
 			zerror(zlogp, B_TRUE, "failed to open console master");
 			(void) mutex_lock(&lock);
 			goto death;
--- old/usr/src/cmd/zoneadmd/zoneadmd.c	Wed Aug 31 11:44:08 2016
+++ new/usr/src/cmd/zoneadmd/zoneadmd.c	Wed Aug 31 11:44:08 2016
@@ -145,6 +145,9 @@
 
 #define	DEFAULT_LOCALE	"C"
 
+#define	RSRC_NET	"net"
+#define	RSRC_DEV	"device"
+
 static const char *
 z_cmd_name(zone_cmd_t zcmd)
 {
@@ -681,6 +684,8 @@
 		char opt_buf[MAX_MNTOPT_STR];
 		int optlen = 0;
 		int mflag = MS_DATA;
+		int i;
+		int ret;
 
 		(void) ct_tmpl_clear(tmpl_fd);
 		/*
@@ -708,9 +713,26 @@
 			optlen = MAX_MNTOPT_STR;
 			mflag = MS_OPTIONSTR;
 		}
-		if (mount(spec, dir, mflag, fstype, NULL, 0, opt, optlen) != 0)
-			_exit(errno);
-		_exit(0);
+
+		/*
+		 * There is an obscure race condition which can cause mount
+		 * to return EBUSY. This happens for example on the mount
+		 * of the zone's /etc/svc/volatile file system if there is
+		 * a GZ process running svcs -Z, which will touch the
+		 * mountpoint, just as we're trying to do the mount. To cope
+		 * with this, we retry up to 3 times to let this transient
+		 * process get out of the way.
+		 */
+		for (i = 0; i < 3; i++) {
+			ret = 0;
+			if (mount(spec, dir, mflag, fstype, NULL, 0, opt,
+			    optlen) != 0)
+				ret = errno;
+			if (ret != EBUSY)
+				break;
+			(void) sleep(1);
+		}
+		_exit(ret);
 	}
 
 	/* parent */
@@ -734,6 +756,115 @@
 }
 
 /*
+ * env variable name format
+ *	_ZONECFG;{resource name};{identifying attr. name};{property name}
+ */
+static void
+set_zonecfg_env(char *rsrc, char *attr, char *name, char *val)
+{
+	char *p;
+	/* Enough for maximal name, rsrc + attr, & slop for ZONECFG & _'s */
+	char nm[2 * MAXNAMELEN + 32];
+
+	if (attr == NULL)
+		(void) snprintf(nm, sizeof (nm), "_ZONECFG_%s_%s", rsrc,
+		    name);
+	else
+		(void) snprintf(nm, sizeof (nm), "_ZONECFG_%s_%s_%s", rsrc,
+		    attr, name);
+
+	p = nm;
+	while ((p = strchr(p, '-')) != NULL)
+		*p++ = '_';
+
+	(void) setenv(nm, val, 1);
+}
+
+/*
+ * Export zonecfg network and device properties into environment for the boot
+ * and state change hooks.
+ * If debug is true, export the brand hook debug env. variable as well.
+ *
+ * We could export more of the config in the future, as necessary.
+ */
+static int
+setup_subproc_env()
+{
+	int res;
+	zone_dochandle_t handle;
+	struct zone_nwiftab ntab;
+	struct zone_devtab dtab;
+	char net_resources[MAXNAMELEN * 2];
+	char dev_resources[MAXNAMELEN * 2];
+
+	if ((handle = zonecfg_init_handle()) == NULL)
+		exit(Z_NOMEM);
+
+	if ((res = zonecfg_get_handle(zone_name, handle)) != Z_OK)
+		goto done;
+
+	if ((res = zonecfg_setnwifent(handle)) != Z_OK)
+		goto done;
+
+	while (zonecfg_getnwifent(handle, &ntab) == Z_OK) {
+		struct zone_res_attrtab *rap;
+		char *phys;
+
+		phys = ntab.zone_nwif_physical;
+
+		(void) strlcat(net_resources, phys, sizeof (net_resources));
+		(void) strlcat(net_resources, " ", sizeof (net_resources));
+
+		set_zonecfg_env(RSRC_NET, phys, "physical", phys);
+
+		set_zonecfg_env(RSRC_NET, phys, "address",
+		    ntab.zone_nwif_address);
+		set_zonecfg_env(RSRC_NET, phys, "allowed-address",
+		    ntab.zone_nwif_allowed_address);
+		set_zonecfg_env(RSRC_NET, phys, "defrouter",
+		    ntab.zone_nwif_defrouter);
+		set_zonecfg_env(RSRC_NET, phys, "global-nic",
+		    ntab.zone_nwif_gnic);
+		set_zonecfg_env(RSRC_NET, phys, "mac-addr", ntab.zone_nwif_mac);
+		set_zonecfg_env(RSRC_NET, phys, "vlan-id",
+		    ntab.zone_nwif_vlan_id);
+
+		for (rap = ntab.zone_nwif_attrp; rap != NULL;
+		    rap = rap->zone_res_attr_next)
+			set_zonecfg_env(RSRC_NET, phys, rap->zone_res_attr_name,
+			    rap->zone_res_attr_value);
+	}
+
+	(void) zonecfg_endnwifent(handle);
+
+	if ((res = zonecfg_setdevent(handle)) != Z_OK)
+		goto done;
+
+	while (zonecfg_getdevent(handle, &dtab) == Z_OK) {
+		struct zone_res_attrtab *rap;
+		char *match;
+
+		match = dtab.zone_dev_match;
+
+		(void) strlcat(dev_resources, match, sizeof (dev_resources));
+		(void) strlcat(dev_resources, " ", sizeof (dev_resources));
+
+		for (rap = dtab.zone_dev_attrp; rap != NULL;
+		    rap = rap->zone_res_attr_next)
+			set_zonecfg_env(RSRC_DEV, match,
+			    rap->zone_res_attr_name, rap->zone_res_attr_value);
+	}
+
+	(void) zonecfg_enddevent(handle);
+
+	res = Z_OK;
+
+done:
+	zonecfg_fini_handle(handle);
+	return (res);
+}
+
+/*
  * If retstr is not NULL, the output of the subproc is returned in the str,
  * otherwise it is output using zerror().  Any memory allocated for retstr
  * should be freed by the caller.
@@ -758,6 +889,11 @@
 		inbuf = buf;
 	}
 
+	if (setup_subproc_env() != Z_OK) {
+		zerror(zlogp, B_FALSE, "failed to setup environment");
+		return (-1);
+	}
+
 	file = popen(cmdbuf, "r");
 	if (file == NULL) {
 		zerror(zlogp, B_TRUE, "could not launch: %s", cmdbuf);
@@ -1463,6 +1599,7 @@
 			rval = zone_ready(zlogp, Z_MNT_BOOT, zstate);
 			if (rval == 0)
 				eventstream_write(Z_EVT_ZONE_READIED);
+			zcons_statechanged();
 			break;
 		case Z_BOOT:
 		case Z_FORCEBOOT:
@@ -1473,6 +1610,7 @@
 				    zstate);
 			}
 			audit_put_record(zlogp, uc, rval, "boot");
+			zcons_statechanged();
 			if (rval != 0) {
 				bringup_failure_recovery = B_TRUE;
 				(void) zone_halt(zlogp, B_FALSE, B_FALSE,
@@ -1595,6 +1733,7 @@
 			eventstream_write(Z_EVT_ZONE_BOOTING);
 			rval = zone_bootup(zlogp, zargp->bootbuf, zstate);
 			audit_put_record(zlogp, uc, rval, "boot");
+			zcons_statechanged();
 			if (rval != 0) {
 				bringup_failure_recovery = B_TRUE;
 				(void) zone_halt(zlogp, B_FALSE, B_TRUE,
@@ -1609,6 +1748,7 @@
 			if ((rval = zone_halt(zlogp, B_FALSE, B_FALSE, zstate))
 			    != 0)
 				break;
+			zcons_statechanged();
 			eventstream_write(Z_EVT_ZONE_HALTED);
 			break;
 		case Z_SHUTDOWN:
@@ -1656,6 +1796,7 @@
 			if ((rval = zone_halt(zlogp, B_FALSE, B_TRUE, zstate))
 			    != 0)
 				break;
+			zcons_statechanged();
 			if ((rval = zone_ready(zlogp, Z_MNT_BOOT, zstate)) == 0)
 				eventstream_write(Z_EVT_ZONE_READIED);
 			else
@@ -1681,6 +1822,7 @@
 			    != 0)
 				break;
 			eventstream_write(Z_EVT_ZONE_HALTED);
+			zcons_statechanged();
 			break;
 		case Z_REBOOT:
 			(void) strlcpy(boot_args, zargp->bootbuf,
@@ -1692,8 +1834,9 @@
 				boot_args[0] = '\0';
 				break;
 			}
-			if ((rval = zone_ready(zlogp, Z_MNT_BOOT, zstate))
-			    != 0) {
+			zcons_statechanged();
+			if ((rval = zone_ready(zlogp, Z_MNT_BOOT, zstate)) !=
+			    0) {
 				eventstream_write(Z_EVT_ZONE_BOOTFAILED);
 				boot_args[0] = '\0';
 				break;
--- old/usr/src/cmd/zoneadmd/zoneadmd.h	Wed Aug 31 11:44:08 2016
+++ new/usr/src/cmd/zoneadmd/zoneadmd.h	Wed Aug 31 11:44:08 2016
@@ -155,6 +155,7 @@
  */
 extern int init_console(zlog_t *);
 extern void serve_console(zlog_t *);
+extern void zcons_statechanged();
 
 /*
  * Memory capping thread creation.
--- old/usr/src/cmd/zonecfg/zonecfg.c	Wed Aug 31 11:44:08 2016
+++ new/usr/src/cmd/zonecfg/zonecfg.c	Wed Aug 31 11:44:08 2016
@@ -235,6 +235,11 @@
 	"fs-allowed",
 	ALIAS_MAXPROCS,
 	"allowed-address",
+	ALIAS_ZFSPRI,
+	"mac-addr",
+	"vlan-id",
+	"global-nic",
+	"property",
 	NULL
 };
 
@@ -407,9 +412,20 @@
 	"exit",
 	"help",
 	"info",
+	"add property ",
+	"clear allowed-address",
+	"clear defrouter",
+	"clear global-nic",
+	"clear mac-addr",
+	"clear vlan-id",
+	"remove property ",
 	"set address=",
-	"set physical=",
+	"set allowed-address=",
 	"set defrouter=",
+	"set global-nic=",
+	"set mac-addr=",
+	"set physical=",
+	"set vlan-id=",
 	NULL
 };
 
@@ -419,6 +435,7 @@
 	"exit",
 	"help",
 	"info",
+	"add property ",
 	"set match=",
 	NULL
 };
@@ -1078,11 +1095,20 @@
 			(void) fprintf(fp, gettext("Valid commands:\n"));
 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
 			    pt_to_str(PT_ADDRESS), gettext("<IP-address>"));
+			(void) fprintf(fp, "\t%s %s (%s=<value>,%s=<value>)\n",
+			    cmd_to_str(CMD_ADD), pt_to_str(PT_NPROP),
+			    pt_to_str(PT_NAME), pt_to_str(PT_VALUE));
 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
 			    pt_to_str(PT_ALLOWED_ADDRESS),
 			    gettext("<IP-address>"));
 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
 			    pt_to_str(PT_PHYSICAL), gettext("<interface>"));
+			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
+			    pt_to_str(PT_MAC), gettext("<mac-address>"));
+			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
+			    pt_to_str(PT_GNIC), gettext("<global zone NIC>"));
+			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
+			    pt_to_str(PT_VLANID), gettext("<vlan ID>"));
 			(void) fprintf(fp, gettext("See ifconfig(1M) for "
 			    "details of the <interface> string.\n"));
 			(void) fprintf(fp, gettext("%s %s is valid "
@@ -1090,10 +1116,12 @@
 			    "must not be set.\n"),
 			    cmd_to_str(CMD_SET), pt_to_str(PT_ADDRESS),
 			    pt_to_str(PT_IPTYPE), gettext("shared"));
-			(void) fprintf(fp, gettext("%s %s is valid "
-			    "if the %s property is set to %s, otherwise it "
-			    "must not be set.\n"),
-			    cmd_to_str(CMD_SET), pt_to_str(PT_ALLOWED_ADDRESS),
+			(void) fprintf(fp, gettext("%s (%s, %s, %s, %s) are "
+			    "valid if the %s property is set to %s, otherwise "
+			    "they must not be set.\n"),
+			    cmd_to_str(CMD_SET),
+			    pt_to_str(PT_ALLOWED_ADDRESS), pt_to_str(PT_MAC),
+			    pt_to_str(PT_VLANID), pt_to_str(PT_GNIC),
 			    pt_to_str(PT_IPTYPE), gettext("exclusive"));
 			(void) fprintf(fp, gettext("\t%s %s=%s\n%s %s "
 			    "is valid if the %s or %s property is set, "
@@ -1109,6 +1137,9 @@
 			    "used to configure a device node.\n"),
 			    rt_to_str(resource_scope));
 			(void) fprintf(fp, gettext("Valid commands:\n"));
+			(void) fprintf(fp, "\t%s %s (%s=<value>,%s=<value>)\n",
+			    cmd_to_str(CMD_ADD), pt_to_str(PT_NPROP),
+			    pt_to_str(PT_NAME), pt_to_str(PT_VALUE));
 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
 			    pt_to_str(PT_MATCH), gettext("<device-path>"));
 			break;
@@ -1336,11 +1367,14 @@
 		    rt_to_str(RT_FS), pt_to_str(PT_DIR),
 		    pt_to_str(PT_SPECIAL), pt_to_str(PT_RAW),
 		    pt_to_str(PT_TYPE), pt_to_str(PT_OPTIONS));
-		(void) fprintf(fp, "\t%s\t\t%s, %s, %s|%s\n", rt_to_str(RT_NET),
+		(void) fprintf(fp, "\t%s\t\t%s, %s, %s, %s, %s, %s, %s %s\n",
+		    rt_to_str(RT_NET),
 		    pt_to_str(PT_ADDRESS), pt_to_str(PT_ALLOWED_ADDRESS),
-		    pt_to_str(PT_PHYSICAL), pt_to_str(PT_DEFROUTER));
-		(void) fprintf(fp, "\t%s\t\t%s\n", rt_to_str(RT_DEVICE),
-		    pt_to_str(PT_MATCH));
+		    pt_to_str(PT_GNIC), pt_to_str(PT_MAC),
+		    pt_to_str(PT_PHYSICAL), pt_to_str(PT_NPROP),
+		    pt_to_str(PT_VLANID), pt_to_str(PT_DEFROUTER));
+		(void) fprintf(fp, "\t%s\t\t%s, %s\n", rt_to_str(RT_DEVICE),
+		    pt_to_str(PT_MATCH), pt_to_str(PT_NPROP));
 		(void) fprintf(fp, "\t%s\t\t%s, %s\n", rt_to_str(RT_RCTL),
 		    pt_to_str(PT_NAME), pt_to_str(PT_VALUE));
 		(void) fprintf(fp, "\t%s\t\t%s, %s, %s\n", rt_to_str(RT_ATTR),
@@ -1698,6 +1732,17 @@
 			(void) strlcpy(zone_template, optarg,
 			    sizeof (zone_template));
 			break;
+		case 'X':
+			(void) snprintf(zone_template, sizeof (zone_template),
+			    "%s/%s.xml", ZONE_CONFIG_ROOT, zone);
+			err = zonecfg_get_xml_handle(zone_template, handle);
+			if (err != Z_OK) {
+				zone_perror(execname, err, B_TRUE);
+				exit(Z_ERR);
+			}
+			got_handle = B_TRUE;
+			need_to_commit = B_TRUE;
+			return;
 		default:
 			short_usage(CMD_CREATE);
 			arg_err = B_TRUE;
@@ -1801,6 +1846,7 @@
 	struct zone_dstab dstab;
 	struct zone_psettab psettab;
 	struct zone_rctlvaltab *valptr;
+	struct zone_res_attrtab *rap;
 	struct zone_admintab admintab;
 	int err, arg;
 	char zonepath[MAXPATHLEN], outfile[MAXPATHLEN], pool[MAXNAMELEN];
@@ -1970,7 +2016,17 @@
 		export_prop(of, PT_ALLOWED_ADDRESS,
 		    nwiftab.zone_nwif_allowed_address);
 		export_prop(of, PT_PHYSICAL, nwiftab.zone_nwif_physical);
+		export_prop(of, PT_MAC, nwiftab.zone_nwif_mac);
+		export_prop(of, PT_VLANID, nwiftab.zone_nwif_vlan_id);
+		export_prop(of, PT_GNIC, nwiftab.zone_nwif_gnic);
 		export_prop(of, PT_DEFROUTER, nwiftab.zone_nwif_defrouter);
+		for (rap = nwiftab.zone_nwif_attrp; rap != NULL;
+		    rap = rap->zone_res_attr_next) {
+			fprintf(of, "%s %s (%s=%s,%s=\"%s\")\n",
+			    cmd_to_str(CMD_ADD), pt_to_str(PT_NPROP),
+			    pt_to_str(PT_NAME), rap->zone_res_attr_name,
+			    pt_to_str(PT_VALUE), rap->zone_res_attr_value);
+		}
 		(void) fprintf(of, "%s\n", cmd_to_str(CMD_END));
 	}
 	(void) zonecfg_endnwifent(handle);
@@ -1983,6 +2039,13 @@
 		(void) fprintf(of, "%s %s\n", cmd_to_str(CMD_ADD),
 		    rt_to_str(RT_DEVICE));
 		export_prop(of, PT_MATCH, devtab.zone_dev_match);
+		for (rap = devtab.zone_dev_attrp; rap != NULL;
+		    rap = rap->zone_res_attr_next) {
+			fprintf(of, "%s %s (%s=%s,%s=\"%s\")\n",
+			    cmd_to_str(CMD_ADD), pt_to_str(PT_NPROP),
+			    pt_to_str(PT_NAME), rap->zone_res_attr_name,
+			    pt_to_str(PT_VALUE), rap->zone_res_attr_value);
+		}
 		(void) fprintf(of, "%s\n", cmd_to_str(CMD_END));
 	}
 	(void) zonecfg_enddevent(handle);
@@ -2349,7 +2412,69 @@
 	zonecfg_free_rctl_value_list(rctlvaltab);
 }
 
+/*
+ * Resource attribute ("property" resource embedded on net or dev resource)
+ */
 static void
+do_res_attr(struct zone_res_attrtab **headp, complex_property_ptr_t cpp)
+{
+	complex_property_ptr_t cp;
+	struct zone_res_attrtab *np;
+	int err;
+	boolean_t seen_name = B_FALSE, seen_value = B_FALSE;
+
+	if ((np = calloc(1, sizeof (struct zone_res_attrtab))) == NULL) {
+		zone_perror(zone, Z_NOMEM, B_TRUE);
+		exit(Z_ERR);
+	}
+
+	for (cp = cpp; cp != NULL; cp = cp->cp_next) {
+		switch (cp->cp_type) {
+		case PT_NAME:
+			if (seen_name) {
+				zerr(gettext("%s already specified"),
+				    pt_to_str(PT_NAME));
+				goto bad;
+			}
+			(void) strlcpy(np->zone_res_attr_name, cp->cp_value,
+			    sizeof (np->zone_res_attr_name));
+			seen_name = B_TRUE;
+			break;
+		case PT_VALUE:
+			if (seen_value) {
+				zerr(gettext("%s already specified"),
+				    pt_to_str(PT_VALUE));
+				goto bad;
+			}
+			(void) strlcpy(np->zone_res_attr_value, cp->cp_value,
+			    sizeof (np->zone_res_attr_value));
+			seen_value = B_TRUE;
+			break;
+		default:
+			zone_perror(pt_to_str(PT_NPROP), Z_NO_PROPERTY_TYPE,
+			    B_TRUE);
+			long_usage(CMD_ADD, B_TRUE);
+			usage(B_FALSE, HELP_PROPS);
+			zonecfg_free_res_attr_list(np);
+			return;
+		}
+	}
+
+	if (!seen_name)
+		zerr(gettext("%s not specified"), pt_to_str(PT_NAME));
+	if (!seen_value)
+		zerr(gettext("%s not specified"), pt_to_str(PT_VALUE));
+
+	err = zonecfg_add_res_attr(headp, np);
+	if (err != Z_OK)
+		zone_perror(pt_to_str(PT_NPROP), err, B_TRUE);
+	return;
+
+bad:
+	zonecfg_free_res_attr_list(np);
+}
+
+static void
 add_property(cmd_t *cmd)
 {
 	char *prop_id;
@@ -2416,6 +2541,44 @@
 			}
 		}
 		return;
+	case RT_NET:
+		if (prop_type != PT_NPROP) {
+			zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
+			    B_TRUE);
+			long_usage(CMD_ADD, B_TRUE);
+			usage(B_FALSE, HELP_PROPS);
+			return;
+		}
+		pp = cmd->cmd_property_ptr[0];
+		if (pp->pv_type != PROP_VAL_COMPLEX) {
+			zerr(gettext("A %s value was expected here."),
+			    pvt_to_str(PROP_VAL_COMPLEX));
+			saw_error = B_TRUE;
+			return;
+		}
+
+		do_res_attr(&(in_progress_nwiftab.zone_nwif_attrp),
+		    pp->pv_complex);
+		return;
+	case RT_DEVICE:
+		if (prop_type != PT_NPROP) {
+			zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
+			    B_TRUE);
+			long_usage(CMD_ADD, B_TRUE);
+			usage(B_FALSE, HELP_PROPS);
+			return;
+		}
+		pp = cmd->cmd_property_ptr[0];
+		if (pp->pv_type != PROP_VAL_COMPLEX) {
+			zerr(gettext("A %s value was expected here."),
+			    pvt_to_str(PROP_VAL_COMPLEX));
+			saw_error = B_TRUE;
+			return;
+		}
+
+		do_res_attr(&(in_progress_devtab.zone_dev_attrp),
+		    pp->pv_complex);
+		return;
 	case RT_RCTL:
 		if (prop_type != PT_VALUE) {
 			zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
@@ -2520,8 +2683,9 @@
 		resource_scope = cmd->cmd_res_type;
 		end_op = CMD_ADD;
 		add_resource(cmd);
-	} else
+	} else {
 		add_property(cmd);
+	}
 }
 
 /*
@@ -2719,6 +2883,21 @@
 			    pp->pv_simple,
 			    sizeof (nwiftab->zone_nwif_physical));
 			break;
+		case PT_MAC:
+			(void) strlcpy(nwiftab->zone_nwif_mac,
+			    pp->pv_simple,
+			    sizeof (nwiftab->zone_nwif_mac));
+			break;
+		case PT_VLANID:
+			(void) strlcpy(nwiftab->zone_nwif_vlan_id,
+			    pp->pv_simple,
+			    sizeof (nwiftab->zone_nwif_vlan_id));
+			break;
+		case PT_GNIC:
+			(void) strlcpy(nwiftab->zone_nwif_gnic,
+			    pp->pv_simple,
+			    sizeof (nwiftab->zone_nwif_gnic));
+			break;
 		case PT_DEFROUTER:
 			(void) strlcpy(nwiftab->zone_nwif_defrouter,
 			    pp->pv_simple,
@@ -3401,6 +3580,7 @@
 	int err, res_type, prop_type;
 	property_value_ptr_t pp;
 	struct zone_rctlvaltab *rctlvaltab;
+	struct zone_res_attrtab *np;
 	complex_property_ptr_t cx;
 
 	res_type = resource_scope;
@@ -3461,6 +3641,56 @@
 			}
 		}
 		return;
+	case RT_NET:		/* FALLTHRU */
+	case RT_DEVICE:
+		if (prop_type != PT_NPROP) {
+			zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
+			    B_TRUE);
+			long_usage(CMD_REMOVE, B_TRUE);
+			usage(B_FALSE, HELP_PROPS);
+			return;
+		}
+		pp = cmd->cmd_property_ptr[0];
+		if (pp->pv_type != PROP_VAL_COMPLEX) {
+			zerr(gettext("A %s value was expected here."),
+			    pvt_to_str(PROP_VAL_COMPLEX));
+			saw_error = B_TRUE;
+			return;
+		}
+
+		np = alloca(sizeof (struct zone_res_attrtab));
+		for (cx = pp->pv_complex; cx != NULL; cx = cx->cp_next) {
+			switch (cx->cp_type) {
+			case PT_NAME:
+				(void) strlcpy(np->zone_res_attr_name,
+				    cx->cp_value,
+				    sizeof (np->zone_res_attr_name));
+				break;
+			case PT_VALUE:
+				(void) strlcpy(np->zone_res_attr_value,
+				    cx->cp_value,
+				    sizeof (np->zone_res_attr_value));
+				break;
+			default:
+				zone_perror(pt_to_str(prop_type),
+				    Z_NO_PROPERTY_TYPE, B_TRUE);
+				long_usage(CMD_REMOVE, B_TRUE);
+				usage(B_FALSE, HELP_PROPS);
+				return;
+			}
+		}
+		np->zone_res_attr_next = NULL;
+
+		if (res_type == RT_NET) {
+			err = zonecfg_remove_res_attr(
+			    &(in_progress_nwiftab.zone_nwif_attrp), np);
+		} else {				/* RT_DEVICE */
+			err = zonecfg_remove_res_attr(
+			    &(in_progress_devtab.zone_dev_attrp), np);
+		}
+		if (err != Z_OK)
+			zone_perror(pt_to_str(prop_type), err, B_TRUE);
+		return;
 	case RT_RCTL:
 		if (prop_type != PT_VALUE) {
 			zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
@@ -3513,18 +3743,6 @@
 			zone_perror(pt_to_str(prop_type), err, B_TRUE);
 		zonecfg_free_rctl_value_list(rctlvaltab);
 		return;
-	case RT_NET:
-		if (prop_type != PT_DEFROUTER) {
-			zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
-			    B_TRUE);
-			long_usage(CMD_REMOVE, B_TRUE);
-			usage(B_FALSE, HELP_PROPS);
-			return;
-		} else {
-			bzero(&in_progress_nwiftab.zone_nwif_defrouter,
-			    sizeof (in_progress_nwiftab.zone_nwif_defrouter));
-			return;
-		}
 	default:
 		zone_perror(rt_to_str(res_type), Z_NO_RESOURCE_TYPE, B_TRUE);
 		long_usage(CMD_REMOVE, B_TRUE);
@@ -3597,6 +3815,30 @@
 			return;
 		}
 		break;
+	case RT_NET:
+		switch (prop_type) {
+		case PT_ALLOWED_ADDRESS:
+			in_progress_nwiftab.zone_nwif_allowed_address[0] = '\0';
+			need_to_commit = B_TRUE;
+			return;
+		case PT_DEFROUTER:
+			in_progress_nwiftab.zone_nwif_defrouter[0] = '\0';
+			need_to_commit = B_TRUE;
+			return;
+		case PT_GNIC:
+			in_progress_nwiftab.zone_nwif_gnic[0] = '\0';
+			need_to_commit = B_TRUE;
+			return;
+		case PT_MAC:
+			in_progress_nwiftab.zone_nwif_mac[0] = '\0';
+			need_to_commit = B_TRUE;
+			return;
+		case PT_VLANID:
+			in_progress_nwiftab.zone_nwif_vlan_id[0] = '\0';
+			need_to_commit = B_TRUE;
+			return;
+		}
+		break;
 	default:
 		break;
 	}
@@ -4203,10 +4445,12 @@
 	 * A nasty expression but not that complicated:
 	 * 1. fs options are simple or list (tested below)
 	 * 2. rctl value's are complex or list (tested below)
+	 * 3. net attr's are complex (tested below)
 	 * Anything else should be simple.
 	 */
 	if (!(res_type == RT_FS && prop_type == PT_OPTIONS) &&
 	    !(res_type == RT_RCTL && prop_type == PT_VALUE) &&
+	    !(res_type == RT_NET && prop_type == PT_NPROP) &&
 	    (pp->pv_type != PROP_VAL_SIMPLE ||
 	    (prop_id = pp->pv_simple) == NULL)) {
 		zerr(gettext("A %s value was expected here."),
@@ -4466,6 +4710,21 @@
 			    prop_id,
 			    sizeof (in_progress_nwiftab.zone_nwif_physical));
 			break;
+		case PT_MAC:
+			(void) strlcpy(in_progress_nwiftab.zone_nwif_mac,
+			    prop_id,
+			    sizeof (in_progress_nwiftab.zone_nwif_mac));
+			break;
+		case PT_VLANID:
+			(void) strlcpy(in_progress_nwiftab.zone_nwif_vlan_id,
+			    prop_id,
+			    sizeof (in_progress_nwiftab.zone_nwif_vlan_id));
+			break;
+		case PT_GNIC:
+			(void) strlcpy(in_progress_nwiftab.zone_nwif_gnic,
+			    prop_id,
+			    sizeof (in_progress_nwiftab.zone_nwif_gnic));
+			break;
 		case PT_DEFROUTER:
 			if (validate_net_address_syntax(prop_id, B_TRUE)
 			    != Z_OK) {
@@ -4476,6 +4735,20 @@
 			    prop_id,
 			    sizeof (in_progress_nwiftab.zone_nwif_defrouter));
 			break;
+		case PT_NPROP:
+			if (pp->pv_type != PROP_VAL_COMPLEX) {
+				zerr(gettext("A %s value was expected here."),
+				    pvt_to_str(PROP_VAL_COMPLEX));
+				saw_error = B_TRUE;
+				return;
+			}
+			zonecfg_free_res_attr_list(
+			    in_progress_nwiftab.zone_nwif_attrp);
+			in_progress_nwiftab.zone_nwif_attrp = NULL;
+			if (!(pp->pv_type == PROP_VAL_LIST &&
+			    pp->pv_list == NULL))
+				add_property(cmd);
+			break;
 		default:
 			zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
 			    B_TRUE);
@@ -4491,6 +4764,20 @@
 			    prop_id,
 			    sizeof (in_progress_devtab.zone_dev_match));
 			break;
+		case PT_NPROP:
+			if (pp->pv_type != PROP_VAL_COMPLEX) {
+				zerr(gettext("A %s value was expected here."),
+				    pvt_to_str(PROP_VAL_COMPLEX));
+				saw_error = B_TRUE;
+				return;
+			}
+			zonecfg_free_res_attr_list(
+			    in_progress_devtab.zone_dev_attrp);
+			in_progress_devtab.zone_dev_attrp = NULL;
+			if (!(pp->pv_type == PROP_VAL_LIST &&
+			    pp->pv_list == NULL))
+				add_property(cmd);
+			break;
 		default:
 			zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
 			    B_TRUE);
@@ -5030,12 +5317,25 @@
 static void
 output_net(FILE *fp, struct zone_nwiftab *nwiftab)
 {
+	struct zone_res_attrtab *np;
+
 	(void) fprintf(fp, "%s:\n", rt_to_str(RT_NET));
 	output_prop(fp, PT_ADDRESS, nwiftab->zone_nwif_address, B_TRUE);
 	output_prop(fp, PT_ALLOWED_ADDRESS,
 	    nwiftab->zone_nwif_allowed_address, B_TRUE);
-	output_prop(fp, PT_PHYSICAL, nwiftab->zone_nwif_physical, B_TRUE);
 	output_prop(fp, PT_DEFROUTER, nwiftab->zone_nwif_defrouter, B_TRUE);
+	output_prop(fp, PT_GNIC, nwiftab->zone_nwif_gnic, B_TRUE);
+	output_prop(fp, PT_MAC, nwiftab->zone_nwif_mac, B_TRUE);
+	output_prop(fp, PT_PHYSICAL, nwiftab->zone_nwif_physical, B_TRUE);
+	output_prop(fp, PT_VLANID, nwiftab->zone_nwif_vlan_id, B_TRUE);
+
+	for (np = nwiftab->zone_nwif_attrp; np != NULL;
+	    np = np->zone_res_attr_next) {
+		fprintf(fp, "\t%s: (%s=%s,%s=\"%s\")\n",
+		    pt_to_str(PT_NPROP),
+		    pt_to_str(PT_NAME), np->zone_res_attr_name,
+		    pt_to_str(PT_VALUE), np->zone_res_attr_value);
+	}
 }
 
 static void
@@ -5078,8 +5378,18 @@
 static void
 output_dev(FILE *fp, struct zone_devtab *devtab)
 {
+	struct zone_res_attrtab *np;
+
 	(void) fprintf(fp, "%s:\n", rt_to_str(RT_DEVICE));
 	output_prop(fp, PT_MATCH, devtab->zone_dev_match, B_TRUE);
+
+	for (np = devtab->zone_dev_attrp; np != NULL;
+	    np = np->zone_res_attr_next) {
+		fprintf(fp, "\t%s: (%s=%s,%s=\"%s\")\n",
+		    pt_to_str(PT_NPROP),
+		    pt_to_str(PT_NAME), np->zone_res_attr_name,
+		    pt_to_str(PT_VALUE), np->zone_res_attr_value);
+	}
 }
 
 static void
@@ -6106,6 +6416,12 @@
 
 	if (save) {
 		if (ret_val == Z_OK) {
+			/*
+			 * If the zone doesn't yet have a debug ID, set one now.
+			 */
+			if (zonecfg_get_did(handle) == -1)
+				zonecfg_set_did(handle);
+
 			if ((ret_val = zonecfg_save(handle)) == Z_OK) {
 				need_to_commit = B_FALSE;
 				(void) strlcpy(revert_zone, zone,
--- old/usr/src/cmd/zonecfg/zonecfg.h	Wed Aug 31 11:44:08 2016
+++ new/usr/src/cmd/zonecfg/zonecfg.h	Wed Aug 31 11:44:08 2016
@@ -137,9 +137,14 @@
 #define	PT_FS_ALLOWED	39
 #define	PT_MAXPROCS	40
 #define	PT_ALLOWED_ADDRESS	41
+#define	PT_ZFSPRI	42
+#define	PT_MAC		43
+#define	PT_VLANID	44
+#define	PT_GNIC		45
+#define	PT_NPROP	46
 
 #define	PT_MIN		PT_UNKNOWN
-#define	PT_MAX		PT_ALLOWED_ADDRESS
+#define	PT_MAX		PT_NPROP
 
 #define	MAX_EQ_PROP_PAIRS	3
 
--- old/usr/src/cmd/zonecfg/zonecfg_grammar.y	Wed Aug 31 11:44:09 2016
+++ new/usr/src/cmd/zonecfg/zonecfg_grammar.y	Wed Aug 31 11:44:09 2016
@@ -136,6 +136,7 @@
 %token OPEN_PAREN CLOSE_PAREN COMMA DATASET LIMITPRIV BOOTARGS BRAND PSET PCAP
 %token MCAP NCPUS IMPORTANCE SHARES MAXLWPS MAXSHMMEM MAXSHMIDS MAXMSGIDS
 %token MAXSEMIDS LOCKED SWAP SCHED CLEAR DEFROUTER ADMIN USER AUTHS MAXPROCS
+%token ZFSPRI MAC VLANID GNIC NPROP
 
 %type <strval> TOKEN EQUAL OPEN_SQ_BRACKET CLOSE_SQ_BRACKET
     property_value OPEN_PAREN CLOSE_PAREN COMMA simple_prop_val
@@ -145,7 +146,7 @@
 %type <ival> property_name SPECIAL RAW DIR OPTIONS TYPE ADDRESS PHYSICAL NAME
     MATCH ZONENAME ZONEPATH AUTOBOOT POOL LIMITPRIV BOOTARGS VALUE PRIV LIMIT
     ACTION BRAND SCHED IPTYPE DEFROUTER HOSTID USER AUTHS FS_ALLOWED
-    ALLOWED_ADDRESS
+    ALLOWED_ADDRESS MAC VLANID GNIC NPROP
 %type <cmd> command
 %type <cmd> add_command ADD
 %type <cmd> cancel_command CANCEL
@@ -976,6 +977,10 @@
 	| ALLOWED_ADDRESS	{ $$ = PT_ALLOWED_ADDRESS; }
 	| PHYSICAL	{ $$ = PT_PHYSICAL; }
 	| DEFROUTER	{ $$ = PT_DEFROUTER; }
+	| MAC		{ $$ = PT_MAC; }
+	| VLANID	{ $$ = PT_VLANID; }
+	| GNIC		{ $$ = PT_GNIC; }
+	| NPROP		{ $$ = PT_NPROP; }
 	| NAME		{ $$ = PT_NAME; }
 	| VALUE		{ $$ = PT_VALUE; }
 	| MATCH		{ $$ = PT_MATCH; }
--- old/usr/src/cmd/zonecfg/zonecfg_lex.l	Wed Aug 31 11:44:09 2016
+++ new/usr/src/cmd/zonecfg/zonecfg_lex.l	Wed Aug 31 11:44:09 2016
@@ -57,10 +57,11 @@
 static char *create_token(char *s);
 %}
 
-%a 7000
+%a 8000
 %p 5000
 %e 2000
 %n 1000
+%o 13000
 
 %{
 /*
@@ -236,6 +237,18 @@
 <TSTATE>defrouter	{ return DEFROUTER; }
 <CSTATE>defrouter	{ return DEFROUTER; }
 
+<TSTATE>mac-addr	{ return MAC; }
+<CSTATE>mac-addr	{ return MAC; }
+
+<TSTATE>vlan-id		{ return VLANID; }
+<CSTATE>vlan-id		{ return VLANID; }
+
+<TSTATE>global-nic	{ return GNIC; }
+<CSTATE>global-nic	{ return GNIC; }
+
+<TSTATE>property	{ return NPROP; }
+<CSTATE>property	{ return NPROP; }
+
 <TSTATE>dir	{ return DIR; }
 <CSTATE>dir	{ return DIR; }
 
@@ -356,6 +369,13 @@
 			yylval.strval = create_token(yytext);
 			return TOKEN;
 		}
+
+<CSTATE>\"[^\"\n]*[\"\n] {
+			yylval.strval = create_token(yytext + 1);
+			if (yylval.strval[yyleng - 2] == '"')
+				yylval.strval[yyleng - 2] = 0;
+			return TOKEN;
+		}
 
 <TSTATE>\"[^\"\n]*[\"\n] {
 			yylval.strval = create_token(yytext + 1);
--- old/usr/src/head/libzonecfg.h	Wed Aug 31 11:44:09 2016
+++ new/usr/src/head/libzonecfg.h	Wed Aug 31 11:44:09 2016
@@ -42,6 +42,7 @@
 #include <netinet/in.h>
 #include <sys/socket.h>
 #include <net/if.h>
+#include <sys/mac.h>
 #include <stdio.h>
 #include <rctl.h>
 #include <zone.h>
@@ -127,6 +128,8 @@
 #define	MAXAUTHS		4096
 #define	ZONE_MGMT_PROF		"Zone Management"
 
+#define	ZONE_INT32SZ		11		/* string to hold 32bit int. */
+
 /* Owner, group, and mode (defined by packaging) for the config directory */
 #define	ZONE_CONFIG_UID		0		/* root */
 #define	ZONE_CONFIG_GID		3		/* sys */
@@ -154,6 +157,7 @@
 #define	ALIAS_SHARES		"cpu-shares"
 #define	ALIAS_CPUCAP		"cpu-cap"
 #define	ALIAS_MAXPROCS		"max-processes"
+#define	ALIAS_ZFSPRI		"zfs-io-priority"
 
 /* Default name for zone detached manifest */
 #define	ZONE_DETACHED	"SUNWdetached.xml"
@@ -192,15 +196,30 @@
 	char		zone_fs_raw[MAXPATHLEN];	/* device to fsck */
 };
 
+/*
+ * Generic resource attribute list.
+ * Key/value resource that can be attached to net or device.
+ */
+struct zone_res_attrtab {
+	char	zone_res_attr_name[MAXNAMELEN];
+	char	zone_res_attr_value[MAXNAMELEN];
+	struct zone_res_attrtab *zone_res_attr_next;
+};
+
 struct zone_nwiftab {
 	char	zone_nwif_address[INET6_ADDRSTRLEN]; /* shared-ip only */
 	char	zone_nwif_allowed_address[INET6_ADDRSTRLEN]; /* excl-ip only */
 	char	zone_nwif_physical[LIFNAMSIZ];
+	char	zone_nwif_mac[MAXMACADDRLEN];		/* excl-ip only */
+	char	zone_nwif_vlan_id[ZONE_INT32SZ];	/* excl-ip only */
+	char	zone_nwif_gnic[LIFNAMSIZ];		/* excl-ip only */
 	char	zone_nwif_defrouter[INET6_ADDRSTRLEN];
+	struct zone_res_attrtab *zone_nwif_attrp;
 };
 
 struct zone_devtab {
 	char	zone_dev_match[MAXPATHLEN];
+	struct zone_res_attrtab *zone_dev_attrp;
 };
 
 struct zone_rctlvaltab {
@@ -314,6 +333,8 @@
 extern	int	zonecfg_get_sched_class(zone_dochandle_t, char *, size_t);
 extern	int	zonecfg_set_sched(zone_dochandle_t, char *);
 extern	int	zonecfg_get_dflt_sched_class(zone_dochandle_t, char *, int);
+extern	zoneid_t zonecfg_get_did(zone_dochandle_t);
+extern	void	zonecfg_set_did(zone_dochandle_t);
 
 /*
  * Set/retrieve the brand for the zone
@@ -338,6 +359,15 @@
     void *), void *);
 
 /*
+ * Resource key/value attributes (properties).
+ */
+extern	int	zonecfg_add_res_attr(struct zone_res_attrtab **,
+    struct zone_res_attrtab *);
+extern	void	zonecfg_free_res_attr_list(struct zone_res_attrtab *);
+extern	int	zonecfg_remove_res_attr(struct zone_res_attrtab **,
+    struct zone_res_attrtab *);
+
+/*
  * Network interface configuration.
  */
 extern	int	zonecfg_add_nwif(zone_dochandle_t, struct zone_nwiftab *);
@@ -498,6 +528,7 @@
  * Higher-level routines.
  */
 extern  int	zone_get_brand(char *, char *, size_t);
+extern  zoneid_t zone_get_did(char *);
 extern	int	zone_get_rootpath(char *, char *, size_t);
 extern	int	zone_get_devroot(char *, char *, size_t);
 extern	int	zone_get_zonepath(char *, char *, size_t);
--- old/usr/src/lib/brand/lx/lx_init/lxinit.c	Wed Aug 31 11:44:09 2016
+++ new/usr/src/lib/brand/lx/lx_init/lxinit.c	Wed Aug 31 11:44:09 2016
@@ -187,7 +187,6 @@
 
 }
 
-#if 0 /* XXX KEBE SAYS NOT YET */
 static int
 zone_find_attr(struct zone_res_attrtab *attrs, const char *name,
     const char **result)
@@ -202,7 +201,6 @@
 	}
 	return (-1);
 }
-#endif /* XXX KEBE SAYS NOT YET */
 
 static void
 lxi_svc_start(char *name, char *path, char *fmri)
@@ -407,7 +405,6 @@
 	return (err);
 }
 
-#if 0 /* XXX KEBE SAYS NOT YET */
 static int
 lxi_iface_dhcp(const char *origiface, boolean_t *first_ipv4_configured)
 {
@@ -459,7 +456,6 @@
 	free(dhcpreply);
 	return (err);
 }
-#endif /* XXX KEBE SAYS NOT YET */
 
 /*
  * Initialize an IPv6 link-local address on a given interface
@@ -577,59 +573,6 @@
 	(void) lxi_iface_ipv6_link_local(iface);
 }
 
-
-
-#if 0 /* XXX KEBE SAYS NOT YET */
-/*
- * This function is used when the "ips" property doesn't exist in a zone's
- * configuration. It may be an older configuration, so we should search for
- * "ip" and "netmask" and convert them into the new format.
- */
-static int
-lxi_get_old_ip(struct zone_res_attrtab *attrs, const char **ipaddrs,
-    char *cidraddr, int len)
-{
-
-	const char *netmask;
-	int prefixlen;
-	struct sockaddr_in mask_sin;
-
-	lxi_warn("Could not find \"ips\" property for zone. Looking "
-	    "for older \"ip\" and \"netmask\" properties, instead.");
-
-	if (zone_find_attr(attrs, "ip", ipaddrs) != 0) {
-		return (-1);
-	}
-
-	if (strcmp(*ipaddrs, "dhcp") == 0) {
-		return (0);
-	}
-
-	if (zone_find_attr(attrs, "netmask", &netmask) != 0) {
-		lxi_err("could not find netmask for interface");
-		/* NOTREACHED */
-	}
-
-	/* Convert the netmask to a number */
-	mask_sin.sin_family = AF_INET;
-	if (inet_pton(AF_INET, netmask, &mask_sin.sin_addr) != 1) {
-		lxi_err("invalid netmask address: %s\n",
-		    strerror(errno));
-		/* NOTREACHED */
-	}
-	prefixlen = mask2plen((struct sockaddr *)&mask_sin);
-
-	/*
-	 * Write out the IP address in the new format and use
-	 * that instead
-	 */
-	(void) snprintf(cidraddr, len, "%s/%d", *ipaddrs, prefixlen);
-
-	*ipaddrs = cidraddr;
-	return (0);
-}
-#endif /* XXX KEBE SAYS NOT YET */
-
 static void
 lxi_net_setup(zone_dochandle_t handle)
 {
@@ -640,37 +583,49 @@
 		return;
 	while (zonecfg_getnwifent(handle, &lookup) == Z_OK) {
 		const char *iface = lookup.zone_nwif_physical;
-#if 0	/* XXX KEBE SAYS NOT YET */
 		struct zone_res_attrtab *attrs = lookup.zone_nwif_attrp;
 		const char *ipaddrs, *primary, *gateway;
-		char ipaddrs_copy[MAXNAMELEN], cidraddr[BUFSIZ],
+		char ipaddrs_copy[MAXNAMELEN], /* cidraddr[BUFSIZ], */
 		    *ipaddr, *tmp, *lasts;
 		boolean_t first_ipv4_configured = B_FALSE;
 		boolean_t *ficp = &first_ipv4_configured;
-#endif	/* XXX KEBE */
+		boolean_t no_zonecfg;
 
+		/*
+		 * Regardless of whether we're configured in zonecfg(1M), or
+		 * configured by other means, make sure we plumb every
+		 * physical=<foo> for IPv4 and IPv6.
+		 */
 		lxi_net_plumb(iface);
 
-		/* XXX KEBE SAYS this bit was shuffled around. */
+		if (zone_find_attr(attrs, "ips", &ipaddrs) != 0 /* &&
+		    lxi_get_old_ip(attrs, &ipaddrs, cidraddr, BUFSIZ) != 0*/) {
+			/*
+			 * Do not panic.  This interface has no in-zonecfg(1M)
+			 * configuration.  We keep a warning around.
+			 */
+			lxi_warn("Could not find zonecfg(1M) network "
+			    "configuration for the %s interface", iface);
+			no_zonecfg = B_TRUE;
+		} else {
+			no_zonecfg = B_FALSE;
+		}
+
 		if (lxi_iface_ipv6_link_local(iface) != 0) {
 			lxi_warn("unable to bring up link-local address on "
 			    "interface %s", iface);
-		} else {
-			/* XXX KEBE SAYS this bit is new. */
-			do_addrconf = B_TRUE;
 		}
 
-#if 0	/* XXX KEBE SAYS NOT YET */
-		if (zone_find_attr(attrs, "ips", &ipaddrs) != 0 &&
-		    lxi_get_old_ip(attrs, &ipaddrs, cidraddr, BUFSIZ) != 0) {
-			lxi_warn("Could not find a valid network configuration "
-			    "for the %s interface", iface);
+		/*
+		 * Every thing else below only happens if we have zonecfg(1M)
+		 * network configuration.
+		 */
+		if (no_zonecfg)
 			continue;
-		}
 
 		/*
-		 * If we're going to be doing DHCP, we have to do it first since
-		 * dhcpagent doesn't like to operate on non-zero logical
+		 * If we're going to be doing DHCP, we have to do it first
+		 * since dhcpagent doesn't like to operate on non-zero logical
 		 * interfaces.
 		 */
 		if (strstr(ipaddrs, "dhcp") != NULL &&
@@ -705,7 +660,6 @@
 		    zone_find_attr(attrs, "gateway", &gateway) == 0) {
 			lxi_iface_gateway(iface, NULL, 0, gateway);
 		}
-#endif /* XXX KEBE */
 	}
 
 	if (do_addrconf) {
--- old/usr/src/lib/libzonecfg/common/libzonecfg.c	Wed Aug 31 11:44:09 2016
+++ new/usr/src/lib/libzonecfg/common/libzonecfg.c	Wed Aug 31 11:44:09 2016
@@ -82,6 +82,8 @@
 #define	ZONE_EVENT_PING_SUBCLASS	"ping"
 #define	ZONE_EVENT_PING_PUBLISHER	"solaris"
 
+#define	DEBUGID_FILE	"/etc/zones/did.txt"
+
 /* Hard-code the DTD element/attribute/entity names just once, here. */
 #define	DTD_ELEM_ATTR		(const xmlChar *) "attr"
 #define	DTD_ELEM_COMMENT	(const xmlChar *) "comment"
@@ -89,6 +91,7 @@
 #define	DTD_ELEM_FS		(const xmlChar *) "filesystem"
 #define	DTD_ELEM_FSOPTION	(const xmlChar *) "fsoption"
 #define	DTD_ELEM_NET		(const xmlChar *) "network"
+#define	DTD_ELEM_NETATTR	(const xmlChar *) "net-attr"
 #define	DTD_ELEM_RCTL		(const xmlChar *) "rctl"
 #define	DTD_ELEM_RCTLVALUE	(const xmlChar *) "rctl-value"
 #define	DTD_ELEM_ZONE		(const xmlChar *) "zone"
@@ -108,10 +111,12 @@
 #define	DTD_ATTR_IPTYPE		(const xmlChar *) "ip-type"
 #define	DTD_ATTR_DEFROUTER	(const xmlChar *) "defrouter"
 #define	DTD_ATTR_DIR		(const xmlChar *) "directory"
+#define	DTD_ATTR_GNIC		(const xmlChar *) "global-nic"
 #define	DTD_ATTR_LIMIT		(const xmlChar *) "limit"
 #define	DTD_ATTR_LIMITPRIV	(const xmlChar *) "limitpriv"
 #define	DTD_ATTR_BOOTARGS	(const xmlChar *) "bootargs"
 #define	DTD_ATTR_SCHED		(const xmlChar *) "scheduling-class"
+#define	DTD_ATTR_MAC		(const xmlChar *) "mac-addr"
 #define	DTD_ATTR_MATCH		(const xmlChar *) "match"
 #define	DTD_ATTR_NAME		(const xmlChar *) "name"
 #define	DTD_ATTR_PHYSICAL	(const xmlChar *) "physical"
@@ -121,6 +126,7 @@
 #define	DTD_ATTR_SPECIAL	(const xmlChar *) "special"
 #define	DTD_ATTR_TYPE		(const xmlChar *) "type"
 #define	DTD_ATTR_VALUE		(const xmlChar *) "value"
+#define	DTD_ATTR_VLANID		(const xmlChar *) "vlan-id"
 #define	DTD_ATTR_ZONEPATH	(const xmlChar *) "zonepath"
 #define	DTD_ATTR_NCPU_MIN	(const xmlChar *) "ncpu_min"
 #define	DTD_ATTR_NCPU_MAX	(const xmlChar *) "ncpu_max"
@@ -133,6 +139,7 @@
 #define	DTD_ATTR_MODE		(const xmlChar *) "mode"
 #define	DTD_ATTR_ACL		(const xmlChar *) "acl"
 #define	DTD_ATTR_BRAND		(const xmlChar *) "brand"
+#define	DTD_ATTR_DID		(const xmlChar *) "debugid"
 #define	DTD_ATTR_HOSTID		(const xmlChar *) "hostid"
 #define	DTD_ATTR_USER		(const xmlChar *) "user"
 #define	DTD_ATTR_AUTHS		(const xmlChar *) "auths"
@@ -2090,13 +2097,17 @@
 int
 zonecfg_lookup_nwif(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
 {
-	xmlNodePtr cur;
+	xmlNodePtr cur, val;
 	xmlNodePtr firstmatch;
 	int err;
 	char address[INET6_ADDRSTRLEN];
 	char physical[LIFNAMSIZ];
+	char mac[MAXMACADDRLEN];
+	char gnic[LIFNAMSIZ];
 	size_t addrspec;		/* nonzero if tabptr has IP addr */
 	size_t physspec;		/* nonzero if tabptr has interface */
+	size_t macspec;			/* nonzero if tabptr has mac addr */
+	size_t gnicspec;		/* nonzero if tabptr has gnic */
 	size_t defrouterspec;		/* nonzero if tabptr has def. router */
 	size_t allowed_addrspec;
 	zone_iptype_t iptype;
@@ -2108,17 +2119,20 @@
 	 * Determine the fields that will be searched.  There must be at least
 	 * one.
 	 *
-	 * zone_nwif_address, zone_nwif_physical, and zone_nwif_defrouter are
+	 * zone_nwif_address, zone_nwif_physical, zone_nwif_defrouter,
+	 * zone_nwif_mac, zone_nwif_vlan_id and zone_nwif_gnic  are
 	 * arrays, so no NULL checks are necessary.
 	 */
 	addrspec = strlen(tabptr->zone_nwif_address);
 	physspec = strlen(tabptr->zone_nwif_physical);
+	macspec = strlen(tabptr->zone_nwif_mac);
+	gnicspec = strlen(tabptr->zone_nwif_gnic);
 	defrouterspec = strlen(tabptr->zone_nwif_defrouter);
 	allowed_addrspec = strlen(tabptr->zone_nwif_allowed_address);
 	if (addrspec != 0 && allowed_addrspec != 0)
 		return (Z_INVAL); /* can't specify both */
 	if (addrspec == 0 && physspec == 0 && defrouterspec == 0 &&
-	    allowed_addrspec == 0)
+	    allowed_addrspec == 0 && macspec == 0 && gnicspec == 0)
 		return (Z_INSUFFICIENT_SPEC);
 
 	if ((err = operation_prep(handle)) != Z_OK)
@@ -2145,6 +2159,15 @@
 		    physical, sizeof (physical)) != Z_OK ||
 		    strcmp(tabptr->zone_nwif_physical, physical) != 0))
 			continue;
+		if (iptype == ZS_EXCLUSIVE && macspec != 0 &&
+		    (fetchprop(cur, DTD_ATTR_MAC, mac, sizeof (mac)) != Z_OK ||
+		    strcmp(tabptr->zone_nwif_mac, mac) != 0))
+			continue;
+		if (iptype == ZS_EXCLUSIVE && gnicspec != 0 &&
+		    (fetchprop(cur, DTD_ATTR_GNIC, gnic,
+		    sizeof (gnic)) != Z_OK ||
+		    strcmp(tabptr->zone_nwif_gnic, gnic) != 0))
+			continue;
 		if (iptype == ZS_SHARED && addrspec != 0 &&
 		    (fetchprop(cur, DTD_ATTR_ADDRESS, address,
 		    sizeof (address)) != Z_OK ||
@@ -2187,6 +2210,21 @@
 		return (err);
 
 	if (iptype == ZS_EXCLUSIVE &&
+	    (err = fetchprop(cur, DTD_ATTR_MAC, tabptr->zone_nwif_mac,
+	    sizeof (tabptr->zone_nwif_mac))) != Z_OK)
+		return (err);
+
+	if (iptype == ZS_EXCLUSIVE &&
+	    (err = fetchprop(cur, DTD_ATTR_VLANID, tabptr->zone_nwif_vlan_id,
+	    sizeof (tabptr->zone_nwif_vlan_id))) != Z_OK)
+		return (err);
+
+	if (iptype == ZS_EXCLUSIVE &&
+	    (err = fetchprop(cur, DTD_ATTR_GNIC, tabptr->zone_nwif_gnic,
+	    sizeof (tabptr->zone_nwif_gnic))) != Z_OK)
+		return (err);
+
+	if (iptype == ZS_EXCLUSIVE &&
 	    (err = fetchprop(cur, DTD_ATTR_ALLOWED_ADDRESS,
 	    tabptr->zone_nwif_allowed_address,
 	    sizeof (tabptr->zone_nwif_allowed_address))) != Z_OK)
@@ -2197,6 +2235,32 @@
 	    sizeof (tabptr->zone_nwif_defrouter))) != Z_OK)
 		return (err);
 
+	tabptr->zone_nwif_attrp = NULL;
+	for (val = cur->xmlChildrenNode; val != NULL; val = val->next) {
+		struct zone_res_attrtab *valptr;
+
+		valptr = (struct zone_res_attrtab *)malloc(
+		    sizeof (struct zone_res_attrtab));
+		if (valptr == NULL)
+			return (Z_NOMEM);
+
+		valptr->zone_res_attr_name[0] =
+		    valptr->zone_res_attr_value[0] = '\0';
+		if (zonecfg_add_res_attr(&(tabptr->zone_nwif_attrp), valptr)
+		    != Z_OK) {
+			free(valptr);
+			break;
+		}
+
+		if ((fetchprop(val, DTD_ATTR_NAME, valptr->zone_res_attr_name,
+		    sizeof (valptr->zone_res_attr_name)) != Z_OK))
+			break;
+		if ((fetchprop(val, DTD_ATTR_VALUE,
+		    valptr->zone_res_attr_value,
+		    sizeof (valptr->zone_res_attr_value)) != Z_OK))
+			break;
+	}
+
 	return (Z_OK);
 }
 
@@ -2203,7 +2267,8 @@
 static int
 zonecfg_add_nwif_core(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
 {
-	xmlNodePtr newnode, cur = handle->zone_dh_cur;
+	xmlNodePtr newnode, cur = handle->zone_dh_cur, valnode;
+	struct zone_res_attrtab *valptr;
 	int err;
 
 	newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_NET, NULL);
@@ -2219,13 +2284,40 @@
 	    tabptr->zone_nwif_physical)) != Z_OK)
 		return (err);
 	/*
-	 * Do not add this property when it is not set, for backwards
-	 * compatibility and because it is optional.
+	 * Do not add these properties when they are not set, for backwards
+	 * compatibility and because they are optional.
 	 */
 	if ((strlen(tabptr->zone_nwif_defrouter) > 0) &&
 	    ((err = newprop(newnode, DTD_ATTR_DEFROUTER,
 	    tabptr->zone_nwif_defrouter)) != Z_OK))
 		return (err);
+	if (strlen(tabptr->zone_nwif_mac) > 0 &&
+	    (err = newprop(newnode, DTD_ATTR_MAC,
+	    tabptr->zone_nwif_mac)) != Z_OK)
+		return (err);
+	if (strlen(tabptr->zone_nwif_vlan_id) > 0 &&
+	    (err = newprop(newnode, DTD_ATTR_VLANID,
+	    tabptr->zone_nwif_vlan_id)) != Z_OK)
+		return (err);
+	if (strlen(tabptr->zone_nwif_gnic) > 0 &&
+	    (err = newprop(newnode, DTD_ATTR_GNIC,
+	    tabptr->zone_nwif_gnic)) != Z_OK)
+		return (err);
+
+	for (valptr = tabptr->zone_nwif_attrp; valptr != NULL;
+	    valptr = valptr->zone_res_attr_next) {
+		valnode = xmlNewTextChild(newnode, NULL, DTD_ELEM_NETATTR,
+		    NULL);
+		err = newprop(valnode, DTD_ATTR_NAME,
+		    valptr->zone_res_attr_name);
+		if (err != Z_OK)
+			return (err);
+		err = newprop(valnode, DTD_ATTR_VALUE,
+		    valptr->zone_res_attr_value);
+		if (err != Z_OK)
+			return (err);
+	}
+
 	return (Z_OK);
 }
 
@@ -2250,7 +2342,8 @@
 zonecfg_delete_nwif_core(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
 {
 	xmlNodePtr cur = handle->zone_dh_cur;
-	boolean_t addr_match, phys_match, allowed_addr_match;
+	boolean_t addr_match, phys_match, allowed_addr_match, mac_match,
+	    gnic_match;
 
 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
 		if (xmlStrcmp(cur->name, DTD_ELEM_NET))
@@ -2262,8 +2355,13 @@
 		    tabptr->zone_nwif_allowed_address);
 		phys_match = match_prop(cur, DTD_ATTR_PHYSICAL,
 		    tabptr->zone_nwif_physical);
+		mac_match = match_prop(cur, DTD_ATTR_MAC,
+		    tabptr->zone_nwif_mac);
+		gnic_match = match_prop(cur, DTD_ATTR_GNIC,
+		    tabptr->zone_nwif_gnic);
 
-		if (addr_match && allowed_addr_match && phys_match) {
+		if ((addr_match || allowed_addr_match || mac_match ||
+		    gnic_match) && phys_match) {
 			xmlUnlinkNode(cur);
 			xmlFreeNode(cur);
 			return (Z_OK);
@@ -2312,6 +2410,58 @@
 	return (Z_OK);
 }
 
+void
+zonecfg_free_res_attr_list(struct zone_res_attrtab *valtab)
+{
+	if (valtab == NULL)
+		return;
+	zonecfg_free_res_attr_list(valtab->zone_res_attr_next);
+	free(valtab);
+}
+
+int
+zonecfg_add_res_attr(struct zone_res_attrtab **headptr,
+    struct zone_res_attrtab *valtabptr)
+{
+	struct zone_res_attrtab *last, *old, *new;
+
+	last = *headptr;
+	for (old = last; old != NULL; old = old->zone_res_attr_next)
+		last = old;	/* walk to the end of the list */
+	new = valtabptr;	/* alloc'd by caller */
+	new->zone_res_attr_next = NULL;
+	if (last == NULL)
+		*headptr = new;
+	else
+		last->zone_res_attr_next = new;
+	return (Z_OK);
+}
+
+int
+zonecfg_remove_res_attr(struct zone_res_attrtab **headptr,
+    struct zone_res_attrtab *valtabptr)
+{
+	struct zone_res_attrtab *last, *this, *next;
+
+	last = *headptr;
+	for (this = last; this != NULL; this = this->zone_res_attr_next) {
+		if (strcmp(this->zone_res_attr_name,
+		    valtabptr->zone_res_attr_name) == 0 &&
+		    strcmp(this->zone_res_attr_value,
+		    valtabptr->zone_res_attr_value) == 0) {
+			next = this->zone_res_attr_next;
+			if (this == *headptr)
+				*headptr = next;
+			else
+				last->zone_res_attr_next = next;
+			free(this);
+			return (Z_OK);
+		} else
+			last = this;
+	}
+	return (Z_NO_PROPERTY_ID);
+}
+
 /*
  * Must be a comma-separated list of alpha-numeric file system names.
  */
@@ -2461,7 +2611,7 @@
 int
 zonecfg_lookup_dev(zone_dochandle_t handle, struct zone_devtab *tabptr)
 {
-	xmlNodePtr cur, firstmatch;
+	xmlNodePtr cur, val, firstmatch;
 	int err;
 	char match[MAXPATHLEN];
 
@@ -2506,6 +2656,32 @@
 	    sizeof (tabptr->zone_dev_match))) != Z_OK)
 		return (err);
 
+	tabptr->zone_dev_attrp = NULL;
+	for (val = cur->xmlChildrenNode; val != NULL; val = val->next) {
+		struct zone_res_attrtab *valptr;
+
+		valptr = (struct zone_res_attrtab *)malloc(
+		    sizeof (struct zone_res_attrtab));
+		if (valptr == NULL)
+			return (Z_NOMEM);
+
+		valptr->zone_res_attr_name[0] =
+		    valptr->zone_res_attr_value[0] = '\0';
+		if (zonecfg_add_res_attr(&(tabptr->zone_dev_attrp), valptr)
+		    != Z_OK) {
+			free(valptr);
+			break;
+		}
+
+		if ((fetchprop(val, DTD_ATTR_NAME, valptr->zone_res_attr_name,
+		    sizeof (valptr->zone_res_attr_name)) != Z_OK))
+			break;
+		if ((fetchprop(val, DTD_ATTR_VALUE,
+		    valptr->zone_res_attr_value,
+		    sizeof (valptr->zone_res_attr_value)) != Z_OK))
+			break;
+	}
+
 	return (Z_OK);
 }
 
@@ -2512,7 +2688,8 @@
 static int
 zonecfg_add_dev_core(zone_dochandle_t handle, struct zone_devtab *tabptr)
 {
-	xmlNodePtr newnode, cur = handle->zone_dh_cur;
+	xmlNodePtr newnode, cur = handle->zone_dh_cur, valnode;
+	struct zone_res_attrtab *valptr;
 	int err;
 
 	newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_DEVICE, NULL);
@@ -2521,6 +2698,21 @@
 	    tabptr->zone_dev_match)) != Z_OK)
 		return (err);
 
+	for (valptr = tabptr->zone_dev_attrp; valptr != NULL;
+	    valptr = valptr->zone_res_attr_next) {
+		valnode = xmlNewTextChild(newnode, NULL, DTD_ELEM_NETATTR,
+		    NULL);
+		err = newprop(valnode, DTD_ATTR_NAME,
+		    valptr->zone_res_attr_name);
+		if (err != Z_OK)
+			return (err);
+		err = newprop(valnode, DTD_ATTR_VALUE,
+		    valptr->zone_res_attr_value);
+		if (err != Z_OK)
+			return (err);
+	}
+
+
 	return (Z_OK);
 }
 
@@ -4718,7 +4910,8 @@
 int
 zonecfg_getnwifent(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
 {
-	xmlNodePtr cur;
+	xmlNodePtr cur, val;
+	struct zone_res_attrtab *valptr;
 	int err;
 
 	if (handle == NULL)
@@ -4754,6 +4947,24 @@
 		return (err);
 	}
 
+	if ((err = fetchprop(cur, DTD_ATTR_MAC, tabptr->zone_nwif_mac,
+	    sizeof (tabptr->zone_nwif_mac))) != Z_OK) {
+		handle->zone_dh_cur = handle->zone_dh_top;
+		return (err);
+	}
+
+	if ((err = fetchprop(cur, DTD_ATTR_VLANID, tabptr->zone_nwif_vlan_id,
+	    sizeof (tabptr->zone_nwif_vlan_id))) != Z_OK) {
+		handle->zone_dh_cur = handle->zone_dh_top;
+		return (err);
+	}
+
+	if ((err = fetchprop(cur, DTD_ATTR_GNIC, tabptr->zone_nwif_gnic,
+	    sizeof (tabptr->zone_nwif_gnic))) != Z_OK) {
+		handle->zone_dh_cur = handle->zone_dh_top;
+		return (err);
+	}
+
 	if ((err = fetchprop(cur, DTD_ATTR_DEFROUTER,
 	    tabptr->zone_nwif_defrouter,
 	    sizeof (tabptr->zone_nwif_defrouter))) != Z_OK) {
@@ -4761,6 +4972,29 @@
 		return (err);
 	}
 
+	tabptr->zone_nwif_attrp = NULL;
+	for (val = cur->xmlChildrenNode; val != NULL; val = val->next) {
+		valptr = (struct zone_res_attrtab *)malloc(
+		    sizeof (struct zone_res_attrtab));
+		if (valptr == NULL)
+			return (Z_NOMEM);
+
+		valptr->zone_res_attr_name[0] =
+		    valptr->zone_res_attr_value[0] = '\0';
+		if (zonecfg_add_res_attr(&(tabptr->zone_nwif_attrp), valptr)
+		    != Z_OK) {
+			free(valptr);
+			break;
+		}
+
+		if (fetchprop(val, DTD_ATTR_NAME, valptr->zone_res_attr_name,
+		    sizeof (valptr->zone_res_attr_name)) != Z_OK)
+			break;
+		if (fetchprop(val, DTD_ATTR_VALUE, valptr->zone_res_attr_value,
+		    sizeof (valptr->zone_res_attr_value)) != Z_OK)
+			break;
+	}
+
 	handle->zone_dh_cur = cur->next;
 	return (Z_OK);
 }
@@ -4780,7 +5014,7 @@
 int
 zonecfg_getdevent(zone_dochandle_t handle, struct zone_devtab *tabptr)
 {
-	xmlNodePtr cur;
+	xmlNodePtr cur, val;
 	int err;
 
 	if (handle == NULL)
@@ -4803,6 +5037,31 @@
 		return (err);
 	}
 
+	tabptr->zone_dev_attrp = NULL;
+	for (val = cur->xmlChildrenNode; val != NULL; val = val->next) {
+		struct zone_res_attrtab *valptr;
+
+		valptr = (struct zone_res_attrtab *)malloc(
+		    sizeof (struct zone_res_attrtab));
+		if (valptr == NULL)
+			return (Z_NOMEM);
+
+		valptr->zone_res_attr_name[0] =
+		    valptr->zone_res_attr_value[0] = '\0';
+		if (zonecfg_add_res_attr(&(tabptr->zone_dev_attrp), valptr)
+		    != Z_OK) {
+			free(valptr);
+			break;
+		}
+
+		if ((fetchprop(val, DTD_ATTR_NAME, valptr->zone_res_attr_name,
+		    sizeof (valptr->zone_res_attr_name)) != Z_OK))
+			break;
+		if ((fetchprop(val, DTD_ATTR_VALUE, valptr->zone_res_attr_value,
+		    sizeof (valptr->zone_res_attr_value)) != Z_OK))
+			break;
+	}
+
 	handle->zone_dh_cur = cur->next;
 	return (Z_OK);
 }
@@ -5530,6 +5789,164 @@
 	return (err);
 }
 
+/*
+ * Atomically get a new zone_did value.  The currently allocated value
+ * is stored in /etc/zones/did.txt.  Lock the file, read the current value,
+ * increment, save the new value and unlock the file.  Return the new value
+ * or -1 if there was an error.  The ID namespace is large enough that we
+ * don't worry about recycling an ID when a zone is deleted.
+ */
+static zoneid_t
+new_zone_did()
+{
+	int fd;
+	int len;
+	int val;
+	struct flock lck;
+	char buf[80];
+
+	if ((fd = open(DEBUGID_FILE, O_RDWR | O_CREAT,
+	    S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) < 0) {
+		perror("new_zone_did open failed");
+		return (-1);
+	}
+
+	/* Initialize the lock. */
+	lck.l_whence = SEEK_SET;
+	lck.l_start = 0;
+	lck.l_len = 0;
+
+	/* Wait until we acquire an exclusive lock on the file. */
+	lck.l_type = F_WRLCK;
+	if (fcntl(fd, F_SETLKW, &lck) == -1) {
+		perror("new_zone_did lock failed");
+		(void) close(fd);
+		return (-1);
+	}
+
+	/* Get currently allocated value */
+	len = read(fd, buf, sizeof (buf));
+	if (len == -1) {
+		perror("new_zone_did read failed");
+		val = -1;
+	} else {
+		if (lseek(fd, 0L, SEEK_SET) == -1) {
+			perror("new_zone_did seek failed");
+			val = -1;
+		} else {
+			if (len == 0) {
+				/* Just created the file, initialize at 1 */
+				val = 1;
+			} else {
+				val = atoi(buf);
+				val++;
+			}
+
+			(void) snprintf(buf, sizeof (buf), "%d\n", val);
+			len = strlen(buf);
+
+			/* Save newly allocated value */
+			if (write(fd, buf, len) == -1) {
+				perror("new_zone_did write failed");
+				val = -1;
+			}
+		}
+	}
+
+	/* Release the file lock. */
+	lck.l_type = F_UNLCK;
+	if (fcntl(fd, F_SETLK, &lck) == -1) {
+		perror("new_zone_did unlock failed");
+		val = -1;
+	}
+
+	if (close(fd) != 0)
+		perror("new_zone_did close failed");
+
+	return (val);
+}
+
+/*
+ * Called by zoneadmd to get the zone's debug ID.
+ * If the zone doesn't already have an ID, a new one is generated and
+ * persistently saved onto the zone.  Normally either zoneadm or zonecfg
+ * will assign a new ID for the zone, so zoneadmd should never have to
+ * generate one, but we also handle that here just to be paranoid.
+ */
+zoneid_t
+zone_get_did(char *zone_name)
+{
+	int res;
+	zoneid_t new_did;
+	zone_dochandle_t handle;
+	char did_str[80];
+
+	if ((handle = zonecfg_init_handle()) == NULL)
+		return (getpid());
+
+	if (zonecfg_get_handle((char *)zone_name, handle) != Z_OK)
+		return (getpid());
+
+	res = getrootattr(handle, DTD_ATTR_DID, did_str, sizeof (did_str));
+
+	/* If the zone already has an assigned debug ID, return it. */
+	if (res == Z_OK && did_str[0] != '\0') {
+		zonecfg_fini_handle(handle);
+		return (atoi(did_str));
+	}
+
+	/*
+	 * The zone doesn't have an assigned debug ID yet, generate one and
+	 * save it as part of the zone definition.
+	 */
+	if ((new_did = new_zone_did()) == -1) {
+		/*
+		 * We should really never hit this block of code.
+		 * Generating a new ID failed for some reason.  Use the current
+		 * pid as a temporary ID so that the zone can continue to boot
+		 * but we don't persistently save this temporary ID on the zone.
+		 */
+		zonecfg_fini_handle(handle);
+		return (getpid());
+	}
+
+	/* Now persistently save this new ID onto the zone. */
+	(void) snprintf(did_str, sizeof (did_str), "%d", new_did);
+	(void) setrootattr(handle, DTD_ATTR_DID, did_str);
+	(void) zonecfg_save(handle);
+
+	zonecfg_fini_handle(handle);
+	return (new_did);
+}
+
+zoneid_t
+zonecfg_get_did(zone_dochandle_t handle)
+{
+	char did_str[80];
+	int err;
+	zoneid_t did;
+
+	err = getrootattr(handle, DTD_ATTR_DID, did_str, sizeof (did_str));
+	if (err == Z_OK && did_str[0] != '\0')
+		did = atoi(did_str);
+	else
+		did = -1;
+
+	return (did);
+}
+
+void
+zonecfg_set_did(zone_dochandle_t handle)
+{
+	zoneid_t new_did;
+	char did_str[80];
+
+	if ((new_did = new_zone_did()) == -1)
+		return;
+	(void) snprintf(did_str, sizeof (did_str), "%d", new_did);
+	(void) setrootattr(handle, DTD_ATTR_DID, did_str);
+}
+
 /*
  * Return the appropriate root for the active /dev.
  * For normal zone, the path is $ZONEPATH/root;
--- old/usr/src/lib/libzonecfg/common/mapfile-vers	Wed Aug 31 11:44:10 2016
+++ new/usr/src/lib/libzonecfg/common/mapfile-vers	Wed Aug 31 11:44:10 2016
@@ -20,6 +20,7 @@
 #
 #
 # Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, Joyent Inc. All rights reserved.
 #
 
 #
@@ -53,6 +54,7 @@
 	zonecfg_add_fs_option;
 	zonecfg_add_admin;
 	zonecfg_add_nwif;
+	zonecfg_add_res_attr;
 	zonecfg_add_pkg;
 	zonecfg_add_pset;
 	zonecfg_add_rctl;
@@ -105,6 +107,7 @@
 	zonecfg_fini_handle;
 	zonecfg_fix_obsolete;
 	zonecfg_free_fs_option_list;
+	zonecfg_free_res_attr_list;
 	zonecfg_free_rctl_value_list;
 	zonecfg_get_aliased_rctl;
 	zonecfg_get_attach_handle;
@@ -118,6 +121,7 @@
 	zonecfg_get_bootargs;
 	zonecfg_get_brand;
 	zonecfg_get_dflt_sched_class;
+	zonecfg_get_did;
 	zonecfg_getdevent;
 	zonecfg_getdevperment;
 	zonecfg_getdsent;
@@ -180,6 +184,7 @@
 	zonecfg_ping_zoneadmd;
 	zonecfg_release_lock_file;
 	zonecfg_remove_fs_option;
+	zonecfg_remove_res_attr;
 	zonecfg_remove_rctl_value;
 	zonecfg_remove_userauths;
 	zonecfg_reverse_scratch;
@@ -193,6 +198,7 @@
 	zonecfg_set_autoboot;
 	zonecfg_set_bootargs;
 	zonecfg_set_brand;
+	zonecfg_set_did;
 	zonecfg_setdevent;
 	zonecfg_setdevperment;
 	zonecfg_setdsent;
@@ -226,6 +232,7 @@
 	zonecfg_verify_save;
 	zonecfg_warn_poold;
 	zone_get_brand;
+	zone_get_did;
 	zone_get_devroot;
 	zone_get_id;
 	zone_get_rootpath;
--- old/usr/src/lib/libzonecfg/dtd/zonecfg.dtd.1	Wed Aug 31 11:44:10 2016
+++ new/usr/src/lib/libzonecfg/dtd/zonecfg.dtd.1	Wed Aug 31 11:44:10 2016
@@ -21,6 +21,7 @@
  CDDL HEADER END
 
  Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ Copyright (c) 2011, Joyent Inc. All rights reserved.
 
 -->
 
@@ -46,14 +47,21 @@
 
 <!ATTLIST inherited-pkg-dir	directory	CDATA #REQUIRED>
 
-<!ELEMENT network	EMPTY>
+<!ELEMENT net-attr	EMPTY>
+<!ATTLIST net-attr	name		CDATA #REQUIRED
+			value		CDATA #REQUIRED>
 
+<!ELEMENT network	(net-attr)*>
+
 <!ATTLIST network	address		CDATA ""
 			allowed-address	CDATA ""
 			defrouter	CDATA ""
-			physical	CDATA #REQUIRED>
+			global-nic	CDATA ""
+			mac-addr	CDATA ""
+			physical	CDATA #REQUIRED
+			vlan-id		CDATA "">
 
-<!ELEMENT device	EMPTY>
+<!ELEMENT device	(net-attr)*>
 
 <!ATTLIST device	match		CDATA #REQUIRED>
 
@@ -156,6 +164,7 @@
 			limitpriv	CDATA ""
 			bootargs	CDATA ""
 			brand		CDATA ""
+			debugid		CDATA ""
 			scheduling-class	CDATA ""
 			fs-allowed	CDATA ""
 			version		NMTOKEN #FIXED '1'>
