PSARC 2010/166 layer-3 net properties for exclusive-IP zones
authorSowmini Varadhan <Sowmini.Varadhan@oracle.COM>
Thu, 01 Jul 2010 17:10:52 -0400
changeset 12748 40c62600acd3
parent 12747 7e3cc6e624d4
child 12749 cd0d8608930c
PSARC 2010/166 layer-3 net properties for exclusive-IP zones 6944327 need to support address and defrouter resources for exclusive-IP zones
usr/src/cmd/cmd-inet/lib/ipmgmtd/Makefile
usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_door.c
usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_impl.h
usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_main.c
usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_persist.c
usr/src/cmd/cmd-inet/usr.bin/netstat/netstat.c
usr/src/cmd/cmd-inet/usr.sbin/ifconfig/ifconfig.c
usr/src/cmd/cmd-inet/usr.sbin/ipadm/ipadm.c
usr/src/cmd/cmd-inet/usr.sbin/route.c
usr/src/cmd/svc/milestone/net-physical
usr/src/cmd/svc/shell/net_include.sh
usr/src/cmd/zoneadmd/Makefile
usr/src/cmd/zoneadmd/vplat.c
usr/src/cmd/zonecfg/zonecfg.c
usr/src/cmd/zonecfg/zonecfg.h
usr/src/cmd/zonecfg/zonecfg_grammar.y
usr/src/cmd/zonecfg/zonecfg_lex.l
usr/src/head/libzonecfg.h
usr/src/lib/libdladm/common/linkprop.c
usr/src/lib/libipadm/Makefile.com
usr/src/lib/libipadm/common/ipadm_addr.c
usr/src/lib/libipadm/common/ipadm_if.c
usr/src/lib/libipadm/common/ipadm_ipmgmt.h
usr/src/lib/libipadm/common/ipadm_ngz.c
usr/src/lib/libipadm/common/libipadm.c
usr/src/lib/libipadm/common/libipadm.h
usr/src/lib/libipadm/common/libipadm_impl.h
usr/src/lib/libipadm/common/mapfile-vers
usr/src/lib/libzonecfg/common/libzonecfg.c
usr/src/lib/libzonecfg/dtd/zonecfg.dtd.1
usr/src/uts/common/inet/ip.h
usr/src/uts/common/inet/ip/ip.c
usr/src/uts/common/inet/ip/ip6_if.c
usr/src/uts/common/inet/ip/ip_if.c
usr/src/uts/common/inet/ip_if.h
usr/src/uts/common/io/dld/dld_proto.c
usr/src/uts/common/io/dld/dld_str.c
usr/src/uts/common/io/mac/mac_protect.c
usr/src/uts/common/net/if.h
usr/src/uts/common/net/route.h
usr/src/uts/common/os/zone.c
usr/src/uts/common/sys/dlpi.h
usr/src/uts/common/sys/mac.h
usr/src/uts/common/sys/mac_provider.h
usr/src/uts/common/sys/zone.h
--- a/usr/src/cmd/cmd-inet/lib/ipmgmtd/Makefile	Thu Jul 01 13:48:34 2010 -0700
+++ b/usr/src/cmd/cmd-inet/lib/ipmgmtd/Makefile	Thu Jul 01 17:10:52 2010 -0400
@@ -44,7 +44,7 @@
 
 ROOTCMDDIR=	$(ROOTFS_LIBDIR)/inet
 
-LDLIBS += -lipadm -lnvpair -lsecdb -lnsl -lumem
+LDLIBS += -lipadm -lnvpair -lsecdb -lnsl -lumem -lscf
 
 #
 # Instrument ipmgmtd with CTF data to ease debugging.
--- a/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_door.c	Thu Jul 01 13:48:34 2010 -0700
+++ b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_door.c	Thu Jul 01 17:10:52 2010 -0400
@@ -554,33 +554,9 @@
 static void
 ipmgmt_setif_handler(void *argp)
 {
-	ipmgmt_if_arg_t		*sargp = argp;
 	ipmgmt_retval_t		rval;
-	ipadm_dbwrite_cbarg_t	cb;
-	uint32_t		flags = sargp->ia_flags;
-	nvlist_t		*nvl = NULL;
-	int			err = 0;
-	char			strval[IPMGMT_STRSIZE];
 
-	if (!(flags & IPMGMT_PERSIST) || sargp->ia_family == AF_UNSPEC ||
-	    sargp->ia_ifname[0] == '\0') {
-		err = EINVAL;
-		goto ret;
-	}
-	if ((err = nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0)) != 0)
-		goto ret;
-	if ((err = nvlist_add_string(nvl, IPADM_NVP_IFNAME,
-	    sargp->ia_ifname)) != 0)
-		goto ret;
-	(void) snprintf(strval, IPMGMT_STRSIZE, "%d", sargp->ia_family);
-	if ((err = nvlist_add_string(nvl, IPADM_NVP_FAMILY, strval)) != 0)
-		goto ret;
-	cb.dbw_nvl = nvl;
-	cb.dbw_flags = 0;
-	err = ipmgmt_db_walk(ipmgmt_db_add, &cb, IPADM_DB_WRITE);
-ret:
-	rval.ir_err = err;
-	nvlist_free(nvl);
+	rval.ir_err = ipmgmt_persist_if(argp);
 	(void) door_return((char *)&rval, sizeof (rval), NULL, 0);
 }
 
@@ -856,3 +832,33 @@
 	rvalp->ir_err = err;
 	(void) door_return((char *)rvalp, sizeof (*rvalp), NULL, 0);
 }
+
+int
+ipmgmt_persist_if(ipmgmt_if_arg_t *sargp)
+{
+	ipadm_dbwrite_cbarg_t	cb;
+	uint32_t		flags = sargp->ia_flags;
+	nvlist_t		*nvl = NULL;
+	int			err = 0;
+	char			strval[IPMGMT_STRSIZE];
+
+	if (!(flags & IPMGMT_PERSIST) || sargp->ia_family == AF_UNSPEC ||
+	    sargp->ia_ifname[0] == '\0') {
+		err = EINVAL;
+		goto ret;
+	}
+	if ((err = nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0)) != 0)
+		goto ret;
+	if ((err = nvlist_add_string(nvl, IPADM_NVP_IFNAME,
+	    sargp->ia_ifname)) != 0)
+		goto ret;
+	(void) snprintf(strval, IPMGMT_STRSIZE, "%d", sargp->ia_family);
+	if ((err = nvlist_add_string(nvl, IPADM_NVP_FAMILY, strval)) != 0)
+		goto ret;
+	cb.dbw_nvl = nvl;
+	cb.dbw_flags = 0;
+	err = ipmgmt_db_walk(ipmgmt_db_add, &cb, IPADM_DB_WRITE);
+ret:
+	nvlist_free(nvl);
+	return (err);
+}
--- a/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_impl.h	Thu Jul 01 13:48:34 2010 -0700
+++ b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_impl.h	Thu Jul 01 17:10:52 2010 -0400
@@ -38,6 +38,7 @@
 #include <pthread.h>
 
 #define	IPMGMT_STRSIZE		256
+#define	IPMGMTD_FMRI		"svc:/network/ip-interface-management:default"
 
 /* ipmgmt_door.c */
 extern void	ipmgmt_handler(void *, char *, size_t, door_desc_t *, uint_t);
@@ -145,6 +146,8 @@
 extern int 		ipmgmt_persist_aobjmap(ipmgmt_aobjmap_t *,
 			    ipadm_db_op_t);
 
+extern boolean_t	ipmgmt_first_boot();
+extern int		ipmgmt_persist_if(ipmgmt_if_arg_t *);
 #ifdef  __cplusplus
 }
 #endif
--- a/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_main.c	Thu Jul 01 13:48:34 2010 -0700
+++ b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_main.c	Thu Jul 01 17:10:52 2010 -0400
@@ -20,8 +20,7 @@
  */
 
 /*
- * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
@@ -63,6 +62,13 @@
 #include <sys/stat.h>
 #include <unistd.h>
 #include "ipmgmt_impl.h"
+#include <zone.h>
+#include <libipadm.h>
+#include <libdladm.h>
+#include <libdllink.h>
+#include <net/route.h>
+#include <ipadm_ipmgmt.h>
+#include <sys/brand.h>
 
 const char		*progname;
 
@@ -81,6 +87,18 @@
 static void		ipmgmt_exit(int);
 static int		ipmgmt_init();
 static int		ipmgmt_init_privileges();
+static void		ipmgmt_ngz_init();
+static void		ipmgmt_ngz_persist_if();
+
+static ipadm_handle_t iph;
+typedef struct ipmgmt_pif_s {
+	struct ipmgmt_pif_s	*pif_next;
+	char			pif_ifname[LIFNAMSIZ];
+	boolean_t		pif_v4;
+	boolean_t		pif_v6;
+} ipmgmt_pif_t;
+
+static ipmgmt_pif_t *ngz_pifs;
 
 static int
 ipmgmt_db_init()
@@ -115,6 +133,9 @@
 	}
 
 	(void) pthread_rwlock_init(&ipmgmt_dbconf_lock, NULL);
+
+	ipmgmt_ngz_persist_if(); /* create persistent interface info for NGZ */
+
 	return (err);
 }
 
@@ -214,11 +235,89 @@
 }
 
 /*
- * Set the uid of this daemon to the "ipadm" user. Finish the following
+ * On the first reboot after installation of an ipkg zone,
+ * ipmgmt_persist_if_cb() is used in non-global zones to track the interfaces
+ * that have IP address configuration assignments from the global zone.
+ * Persistent configuration for the interfaces is created on the first boot
+ * by ipmgmtd, and the addresses assigned to the interfaces by the GZ
+ * will be subsequently configured when the interface is enabled.
+ * Note that ipmgmt_persist_if_cb() only sets up a list of interfaces
+ * that need to be persisted- the actual update of the ipadm data-store happens
+ * in ipmgmt_persist_if() after the appropriate privs/uid state has been set up.
+ */
+static void
+ipmgmt_persist_if_cb(char *ifname, boolean_t v4, boolean_t v6)
+{
+	ipmgmt_pif_t *pif;
+
+	pif = calloc(1, sizeof (*pif));
+	if (pif == NULL) {
+		ipmgmt_log(LOG_WARNING,
+		    "Could not allocate memory to configure %s", ifname);
+		return;
+	}
+	(void) strlcpy(pif->pif_ifname, ifname, sizeof (pif->pif_ifname));
+	pif->pif_v4 = v4;
+	pif->pif_v6 = v6;
+	pif->pif_next = ngz_pifs;
+	ngz_pifs = pif;
+}
+
+/*
+ * ipmgmt_ngz_init() initializes exclusive-IP stack non-global zones by
+ * extracting configuration that has been saved in the kernel and applying
+ * it at zone boot.
+ */
+static void
+ipmgmt_ngz_init()
+{
+	zoneid_t zoneid;
+	boolean_t firstboot = B_TRUE, s10c = B_FALSE;
+	char brand[MAXNAMELEN];
+	ipadm_status_t ipstatus;
+
+	zoneid = getzoneid();
+	if (zoneid != GLOBAL_ZONEID) {
+
+		if (zone_getattr(zoneid, ZONE_ATTR_BRAND, brand,
+		    sizeof (brand)) < 0) {
+			ipmgmt_log(LOG_ERR, "Could not get brand name");
+			return;
+		}
+		/*
+		 * firstboot is always true for S10C zones, where ipadm is not
+		 * available for restoring persistent configuration.
+		 */
+		if (strcmp(brand, NATIVE_BRAND_NAME) == 0)
+			firstboot = ipmgmt_first_boot();
+		else
+			s10c = B_TRUE;
+
+		if (!firstboot)
+			return;
+
+		ipstatus = ipadm_open(&iph, IPH_IPMGMTD);
+		if (ipstatus != IPADM_SUCCESS) {
+			ipmgmt_log(LOG_ERR, "could not open ipadm handle",
+			    ipadm_status2str(ipstatus));
+			return;
+		}
+		/*
+		 * Only pass down the callback to persist the interface
+		 * for NATIVE (ipkg) zones.
+		 */
+		(void) ipadm_init_net_from_gz(iph, NULL,
+		    (s10c ? NULL : ipmgmt_persist_if_cb));
+		ipadm_close(iph);
+	}
+}
+
+/*
+ * Set the uid of this daemon to the "netadm" user. Finish the following
  * operations before setuid() because they need root privileges:
  *
  *    - create the /etc/svc/volatile/ipadm directory;
- *    - change its uid/gid to "ipadm"/"sys";
+ *    - change its uid/gid to "netadm"/"netadm";
  */
 static int
 ipmgmt_init_privileges()
@@ -246,6 +345,14 @@
 	}
 
 	/*
+	 * initialize any NGZ specific network information before dropping
+	 * privileges. We need these privileges to plumb IP interfaces handed
+	 * down from the GZ (for dlpi_open() etc.) and also to configure the
+	 * address itself (for any IPI_PRIV ioctls like SLIFADDR)
+	 */
+	ipmgmt_ngz_init();
+
+	/*
 	 * limit the privileges of this daemon and set the uid of this
 	 * daemon to UID_NETADM
 	 */
@@ -380,3 +487,61 @@
 	ipmgmt_inform_parent_exit(EXIT_FAILURE);
 	return (EXIT_FAILURE);
 }
+
+/*
+ * Return TRUE if `ifname' has persistent configuration for the `af' address
+ * family in the datastore
+ */
+static boolean_t
+ipmgmt_persist_if_exists(char *ifname, sa_family_t af)
+{
+	ipmgmt_getif_cbarg_t cbarg;
+	boolean_t exists = B_FALSE;
+	ipadm_if_info_t *ifp;
+
+	bzero(&cbarg, sizeof (cbarg));
+	cbarg.cb_ifname = ifname;
+	(void) ipmgmt_db_walk(ipmgmt_db_getif, &cbarg, IPADM_DB_READ);
+	if ((ifp = cbarg.cb_ifinfo) != NULL) {
+		if ((af == AF_INET && (ifp->ifi_pflags & IFIF_IPV4)) ||
+		    (af == AF_INET6 && (ifp->ifi_pflags & IFIF_IPV6))) {
+			exists = B_TRUE;
+		}
+	}
+	free(ifp);
+	return (exists);
+}
+
+/*
+ * Persist any NGZ interfaces assigned to us from the global zone if they do
+ * not already exist in the persistent db. We need to
+ * do this before any calls to ipadm_enable_if() can succeed (i.e.,
+ * before opening up for door_calls), and after setuid to 'netadm' so that
+ * the persistent db is created with the right permissions.
+ */
+static void
+ipmgmt_ngz_persist_if()
+{
+	ipmgmt_pif_t *pif, *next;
+	ipmgmt_if_arg_t ifarg;
+
+	for (pif = ngz_pifs; pif != NULL; pif = next) {
+		next = pif->pif_next;
+		bzero(&ifarg, sizeof (ifarg));
+		(void) strlcpy(ifarg.ia_ifname, pif->pif_ifname,
+		    sizeof (ifarg.ia_ifname));
+		ifarg.ia_flags = IPMGMT_PERSIST;
+		if (pif->pif_v4 &&
+		    !ipmgmt_persist_if_exists(pif->pif_ifname, AF_INET)) {
+			ifarg.ia_family = AF_INET;
+			(void) ipmgmt_persist_if(&ifarg);
+		}
+		if (pif->pif_v6 &&
+		    !ipmgmt_persist_if_exists(pif->pif_ifname, AF_INET6)) {
+			ifarg.ia_family = AF_INET6;
+			(void) ipmgmt_persist_if(&ifarg);
+		}
+		free(pif);
+	}
+	ngz_pifs = NULL; /* no red herrings */
+}
--- a/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_persist.c	Thu Jul 01 13:48:34 2010 -0700
+++ b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_persist.c	Thu Jul 01 17:10:52 2010 -0400
@@ -56,6 +56,7 @@
 #include <arpa/inet.h>
 #include <unistd.h>
 #include "ipmgmt_impl.h"
+#include <libscf.h>
 
 #define	ATYPE	"_atype"		/* name of the address type nvpair */
 #define	FLAGS	"_flags"		/* name of the flags nvpair */
@@ -1164,3 +1165,203 @@
 	}
 	return (err);
 }
+
+typedef struct scf_resources {
+	scf_handle_t *sr_handle;
+	scf_instance_t *sr_inst;
+	scf_propertygroup_t *sr_pg;
+	scf_property_t *sr_prop;
+	scf_value_t *sr_val;
+	scf_transaction_t *sr_tx;
+	scf_transaction_entry_t *sr_ent;
+} scf_resources_t;
+
+/*
+ * Inputs:
+ *   res is a pointer to the scf_resources_t to be released.
+ */
+static void
+ipmgmt_release_scf_resources(scf_resources_t *res)
+{
+	scf_entry_destroy(res->sr_ent);
+	scf_transaction_destroy(res->sr_tx);
+	scf_value_destroy(res->sr_val);
+	scf_property_destroy(res->sr_prop);
+	scf_pg_destroy(res->sr_pg);
+	scf_instance_destroy(res->sr_inst);
+	(void) scf_handle_unbind(res->sr_handle);
+	scf_handle_destroy(res->sr_handle);
+}
+
+/*
+ * Inputs:
+ *   fmri is the instance to look up
+ * Outputs:
+ *   res is a pointer to an scf_resources_t.  This is an internal
+ *   structure that holds all the handles needed to get a specific
+ *   property from the running snapshot; on a successful return it
+ *   contains the scf_value_t that should be passed to the desired
+ *   scf_value_get_foo() function, and must be freed after use by
+ *   calling release_scf_resources().  On a failure return, any
+ *   resources that may have been assigned to res are released, so
+ *   the caller does not need to do any cleanup in the failure case.
+ * Returns:
+ *    0 on success
+ *   -1 on failure
+ */
+
+static int
+ipmgmt_create_scf_resources(const char *fmri, scf_resources_t *res)
+{
+	res->sr_tx = NULL;
+	res->sr_ent = NULL;
+	res->sr_inst = NULL;
+	res->sr_pg = NULL;
+	res->sr_prop = NULL;
+	res->sr_val = NULL;
+
+	if ((res->sr_handle = scf_handle_create(SCF_VERSION)) == NULL) {
+		return (-1);
+	}
+
+	if (scf_handle_bind(res->sr_handle) != 0) {
+		scf_handle_destroy(res->sr_handle);
+		return (-1);
+	}
+	if ((res->sr_inst = scf_instance_create(res->sr_handle)) == NULL) {
+		goto failure;
+	}
+	if (scf_handle_decode_fmri(res->sr_handle, fmri, NULL, NULL,
+	    res->sr_inst, NULL, NULL, SCF_DECODE_FMRI_REQUIRE_INSTANCE) != 0) {
+		goto failure;
+	}
+	if ((res->sr_pg = scf_pg_create(res->sr_handle)) == NULL) {
+		goto failure;
+	}
+	if ((res->sr_prop = scf_property_create(res->sr_handle)) == NULL) {
+		goto failure;
+	}
+	if ((res->sr_val = scf_value_create(res->sr_handle)) == NULL) {
+		goto failure;
+	}
+	if ((res->sr_tx = scf_transaction_create(res->sr_handle)) == NULL) {
+		goto failure;
+	}
+	if ((res->sr_ent = scf_entry_create(res->sr_handle)) == NULL) {
+		goto failure;
+	}
+	return (0);
+
+failure:
+	ipmgmt_release_scf_resources(res);
+	return (-1);
+}
+
+static int
+ipmgmt_set_property_value(scf_resources_t *res, const char *propname,
+    scf_type_t proptype)
+{
+	int result = -1;
+	boolean_t new;
+
+retry:
+	new = (scf_pg_get_property(res->sr_pg, propname, res->sr_prop) != 0);
+
+	if (scf_transaction_start(res->sr_tx, res->sr_pg) == -1) {
+		goto failure;
+	}
+	if (new) {
+		if (scf_transaction_property_new(res->sr_tx, res->sr_ent,
+		    propname, proptype) == -1) {
+			goto failure;
+		}
+	} else {
+		if (scf_transaction_property_change(res->sr_tx, res->sr_ent,
+		    propname, proptype) == -1) {
+			goto failure;
+		}
+	}
+
+	if (scf_entry_add_value(res->sr_ent, res->sr_val) != 0) {
+		goto failure;
+	}
+
+	result = scf_transaction_commit(res->sr_tx);
+	if (result == 0) {
+		scf_transaction_reset(res->sr_tx);
+		if (scf_pg_update(res->sr_pg) == -1) {
+			goto failure;
+		}
+		goto retry;
+	}
+	if (result == -1)
+		goto failure;
+	return (0);
+
+failure:
+	return (-1);
+}
+
+/*
+ * Returns TRUE if this is the first boot, else return FALSE. The
+ * "ipmgmtd/first_boot_done" property is persistently set up on
+ * IPMGMTD_FMRI on the first boot. Note that the presence of
+ * "first_boot_done" itself is sufficient to indicate that this is
+ * not the first boot i.e., the value of the property is immaterial.
+ */
+extern boolean_t
+ipmgmt_first_boot()
+{
+	scf_simple_prop_t *prop;
+	ssize_t	numvals;
+	scf_resources_t res;
+	scf_error_t err;
+
+	if (ipmgmt_create_scf_resources(IPMGMTD_FMRI, &res) != 0)
+		return (B_TRUE); /* err on the side of caution */
+	prop = scf_simple_prop_get(res.sr_handle,
+	    IPMGMTD_FMRI, "ipmgmtd", "first_boot_done");
+	numvals = scf_simple_prop_numvalues(prop);
+	if (numvals > 0) {
+		scf_simple_prop_free(prop);
+		ipmgmt_release_scf_resources(&res);
+		return (B_FALSE);
+	}
+
+	/*
+	 * mark the first boot by setting ipmgmtd/first_boot_done to true
+	 */
+	if (scf_instance_add_pg(res.sr_inst, "ipmgmtd", SCF_GROUP_APPLICATION,
+	    0, res.sr_pg) != 0) {
+		if ((err = scf_error()) != SCF_ERROR_EXISTS)
+			goto failure;
+		/*
+		 * err == SCF_ERROR_EXISTS is by itself sufficient to declare
+		 * that this is not the first boot, but we create a simple
+		 * property as a place-holder, so that we don't leave an
+		 * empty process group behind.
+		 */
+		if (scf_instance_get_pg_composed(res.sr_inst, NULL, "ipmgmtd",
+		    res.sr_pg) != 0) {
+			err = scf_error();
+			goto failure;
+		}
+	}
+
+	if (scf_value_set_astring(res.sr_val, "true") != 0) {
+		err = scf_error();
+		goto failure;
+	}
+
+	if (ipmgmt_set_property_value(&res, "first_boot_done",
+	    SCF_TYPE_ASTRING) != 0) {
+		ipmgmt_log(LOG_WARNING,
+		    "Could not set rval of first_boot_done");
+	}
+
+failure:
+	ipmgmt_log(LOG_WARNING, "ipmgmt_first_boot scf error %s",
+	    scf_strerror(err));
+	ipmgmt_release_scf_resources(&res);
+	return (B_TRUE);
+}
--- a/usr/src/cmd/cmd-inet/usr.bin/netstat/netstat.c	Thu Jul 01 13:48:34 2010 -0700
+++ b/usr/src/cmd/cmd-inet/usr.bin/netstat/netstat.c	Thu Jul 01 17:10:52 2010 -0400
@@ -19,11 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
- */
-
-/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 1990  Mentat Inc.
  * netstat.c 2.2, last change 9/9/91
  * MROUTING Revision 3.5
@@ -314,8 +310,9 @@
 #define	FLF_I		0x00000400	/* RTF_INDIRECT */
 #define	FLF_R		0x00000800	/* RTF_REJECT */
 #define	FLF_B		0x00001000	/* RTF_BLACKHOLE */
-
-static const char flag_list[] = "AbDGHLUMSCIRB";
+#define	FLF_Z		0x00100000	/* RTF_ZONE */
+
+static const char flag_list[] = "AbDGHLUMSCIRBZ";
 
 typedef struct filter_rule filter_t;
 
@@ -4342,6 +4339,10 @@
 		(void) strcat(flags, "B");
 		flag_b |= FLF_B;
 	}
+	if (rp->ipRouteInfo.re_flags & RTF_ZONE) {
+		(void) strcat(flags, "Z");
+		flag_b |= FLF_Z;
+	}
 	return (flag_b);
 }
 
@@ -4589,6 +4590,10 @@
 		(void) strcat(flags, "B");
 		flag_b |= FLF_B;
 	}
+	if (rp6->ipv6RouteInfo.re_flags & RTF_ZONE) {
+		(void) strcat(flags, "Z");
+		flag_b |= FLF_Z;
+	}
 	return (flag_b);
 }
 
--- a/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/ifconfig.c	Thu Jul 01 13:48:34 2010 -0700
+++ b/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/ifconfig.c	Thu Jul 01 17:10:52 2010 -0400
@@ -73,7 +73,8 @@
 	{ IFF_DUPLICATE,	"DUPLICATE" },
 	{ IFF_IPMP,		"IPMP"},
 	{ IFF_VRRP,		"VRRP"},
-	{ IFF_NOACCEPT,		"NOACCEPT"}
+	{ IFF_NOACCEPT,		"NOACCEPT"},
+	{ IFF_L3PROTECT,	"L3PROTECT"}
 };
 
 typedef struct {
--- a/usr/src/cmd/cmd-inet/usr.sbin/ipadm/ipadm.c	Thu Jul 01 13:48:34 2010 -0700
+++ b/usr/src/cmd/cmd-inet/usr.sbin/ipadm/ipadm.c	Thu Jul 01 17:10:52 2010 -0400
@@ -308,7 +308,7 @@
 /* name,	field width,	id,		callback */
 { "IFNAME",	11,		SI_IFNAME,	print_si_cb},
 { "STATE",	9,		SI_STATE,	print_si_cb},
-{ "CURRENT",	12,		SI_CURRENT,	print_si_cb},
+{ "CURRENT",	13,		SI_CURRENT,	print_si_cb},
 { "PERSISTENT",	11,		SI_PERSISTENT,	print_si_cb},
 { NULL,		0,		0,		NULL}
 };
@@ -1604,6 +1604,43 @@
 	}
 }
 
+/*
+ * return true if the address for lifname comes to us from the global zone
+ * with 'allowed-ips' constraints.
+ */
+static boolean_t
+is_from_gz(const char *lifname)
+{
+	ipadm_if_info_t		*if_info;
+	char			phyname[LIFNAMSIZ], *cp;
+	boolean_t		ret = _B_FALSE;
+	ipadm_status_t		status;
+	zoneid_t		zoneid;
+	ushort_t		zflags;
+
+	if ((zoneid = getzoneid()) == GLOBAL_ZONEID)
+		return (_B_FALSE); /* from-gz only  makes sense in a NGZ */
+
+	if (zone_getattr(zoneid, ZONE_ATTR_FLAGS, &zflags, sizeof (zflags)) < 0)
+		return (_B_FALSE);
+
+	if (!(zflags & ZF_NET_EXCL))
+		return (_B_TRUE);  /* everything is from the GZ for shared-ip */
+
+	(void) strncpy(phyname, lifname, sizeof (phyname));
+	if ((cp = strchr(phyname, ':')) != NULL)
+		*cp = '\0';
+	status = ipadm_if_info(iph, phyname, &if_info, 0, LIFC_DEFAULT);
+	if (status != IPADM_SUCCESS)
+		return (ret);
+
+	if (if_info->ifi_cflags & IFIF_L3PROTECT)
+		ret = _B_TRUE;
+	if (if_info)
+		ipadm_free_if_info(if_info);
+	return (ret);
+}
+
 static boolean_t
 print_sa_cb(ofmt_arg_t *ofarg, char *buf, uint_t bufsize)
 {
@@ -1668,7 +1705,11 @@
 		    buf, bufsize);
 		break;
 	case SA_TYPE:
-		flags2str(ainfo->ia_atype, type, _B_FALSE, buf, bufsize);
+		if (is_from_gz(ifa->ifa_name))
+			(void) snprintf(buf, bufsize, "from-gz");
+		else
+			flags2str(ainfo->ia_atype, type, _B_FALSE, buf,
+			    bufsize);
 		break;
 	case SA_CURRENT:
 		flags2str(ainfo->ia_cflags, cflags_mask, _B_TRUE, buf, bufsize);
@@ -1877,6 +1918,7 @@
 		{ "i",	IFIF_INACTIVE,		IFIF_INACTIVE	},
 		{ "V",	IFIF_VRRP,		IFIF_VRRP	},
 		{ "a",	IFIF_NOACCEPT,		IFIF_NOACCEPT	},
+		{ "Z",	IFIF_L3PROTECT,		IFIF_L3PROTECT	},
 		{ "4",	IFIF_IPV4,		IFIF_IPV4	},
 		{ "6",	IFIF_IPV6,		IFIF_IPV6	},
 		{ NULL,	0,			0		}
--- a/usr/src/cmd/cmd-inet/usr.sbin/route.c	Thu Jul 01 13:48:34 2010 -0700
+++ b/usr/src/cmd/cmd-inet/usr.sbin/route.c	Thu Jul 01 17:10:52 2010 -0400
@@ -1,6 +1,5 @@
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
@@ -2567,7 +2566,8 @@
 static char routeflags[] =
 "\1UP\2GATEWAY\3HOST\4REJECT\5DYNAMIC\6MODIFIED\7DONE\010MASK_PRESENT"
 	"\011CLONING\012XRESOLVE\013LLINFO\014STATIC\015BLACKHOLE"
-	"\016PRIVATE\017PROTO2\020PROTO1\021MULTIRT\022SETSRC\023INDIRECT";
+	"\016PRIVATE\017PROTO2\020PROTO1\021MULTIRT\022SETSRC\023INDIRECT"
+	"\024KERNEL\025ZONE";
 static char ifnetflags[] =
 "\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5PTP\6NOTRAILERS\7RUNNING\010NOARP"
 	"\011PPROMISC\012ALLMULTI\013INTELLIGENT\014MULTICAST"
--- a/usr/src/cmd/svc/milestone/net-physical	Thu Jul 01 13:48:34 2010 -0700
+++ b/usr/src/cmd/svc/milestone/net-physical	Thu Jul 01 17:10:52 2010 -0400
@@ -305,21 +305,35 @@
 fi
 
 #
-# Finally configure interfaces set up with ipadm.
+# Finally configure interfaces set up with ipadm. Any /etc/hostname*.intf
+# files take precedence over ipadm defined configurations except when
+# we are in a non-global zone and Layer-3 protection of IP addresses is
+# enforced on the interface by the global zone.
 #
-for showif_output in `/sbin/ipadm show-if -p -o ifname,state`; do
+for showif_output in `/sbin/ipadm show-if -p -o ifname,state,current`; do
 	intf=`echo $showif_output | /usr/bin/cut -f1 -d:`
 	state=`echo $showif_output | /usr/bin/cut -f2 -d:`
-	if  [ "$state" != "disabled" ]; then
-		# skip if not a persistent interface
+	current=`echo $showif_output | /usr/bin/cut -f3 -d:`
+	if  [[ "$state" != "disabled" && $current != *Z* ]]; then
+		#
+		# skip if not a persistent interface, or if it should get IP
+		# configuration from the global zone ('Z' flag is set)
+		#
 		continue;
 	elif is_iptun $intf; then
 		# skip IP tunnel interfaces plumbed by net-iptun
 		continue;
 	elif [ -f /etc/hostname.$intf ] || [ -f /etc/hostname6.$intf ]; then
-		echo "found /etc/hostname.$intf or /etc/hostname6.$intf, "\
-		    "ignoring ipadm configuration" > /dev/msglog
-		continue;
+		if [[ $current != *Z* ]]; then
+			echo "found /etc/hostname.$intf "\
+			    "or /etc/hostname6.$intf, "\
+			    "ignoring ipadm configuration" > /dev/msglog
+			continue;
+		else
+			echo "Ignoring /etc/hostname*.$intf" > /dev/msglog 
+			/sbin/ifconfig $intf unplumb > /dev/null 2>&1
+			/sbin/ifconfig $intf inet6 unplumb > /dev/null 2>&1
+		fi
 	fi
 
 	# Enable the interface managed by ipadm
--- a/usr/src/cmd/svc/shell/net_include.sh	Thu Jul 01 13:48:34 2010 -0700
+++ b/usr/src/cmd/svc/shell/net_include.sh	Thu Jul 01 17:10:52 2010 -0400
@@ -20,8 +20,7 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
 #
 # Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T.
 # All rights reserved.
@@ -547,6 +546,38 @@
 }
 
 #
+# ipadm_from_gz_if ifname
+#
+# Return true if we are in a non-global zone and Layer-3 protection of
+# IP addresses is being enforced on the interface by the global zone
+#
+ipadm_from_gz_if()
+{ 
+	pif=`/sbin/ipadm show-if -o persistent -p $1 2>/dev/null | egrep '4|6'`
+	if smf_is_globalzone || ![[ $pif == *4* || $pif == *6* ]]; then
+		return 1
+	else
+		#
+		# In the non-global zone, plumb the interface to show current
+		# flags and check if Layer-3 protection has been enforced by
+		# the global zone. Note that this function may return
+		# with a plumbed interface. Ideally, we would not have to
+		# plumb the interface to check l3protect, but since we
+		# the `allowed-ips' datalink property cannot currently be
+		# examined in any other way from the non-global zone, we
+		# resort to plumbing the interface
+		# 
+		/sbin/ifconfig $1 plumb > /dev/null 2>&1
+		l3protect=`/sbin/ipadm show-if -o current -p $1|grep -c 'Z'`
+		if [ $l3protect = 0 ]; then
+			return 1
+		else
+			return 0
+		fi
+	fi
+}
+
+#
 # if_configure type class interface_list
 #
 # Configure all of the interfaces of type `type' (e.g., "inet6") in
@@ -575,7 +606,10 @@
 	while [ $# -gt 0 ]; do
 		$process_func /sbin/ifconfig $1 $type < $hostpfx.$1 >/dev/null
 		if [ $? != 0 ]; then
-			fail="$fail $1"
+			ipadm_from_gz_if $1
+			if [ $? != 0 ]; then
+				fail="$fail $1"
+			fi
 		elif [ "$type" = inet6 ]; then
 			#
 			# only bring the interface up if it is not a
--- a/usr/src/cmd/zoneadmd/Makefile	Thu Jul 01 13:48:34 2010 -0700
+++ b/usr/src/cmd/zoneadmd/Makefile	Thu Jul 01 17:10:52 2010 -0400
@@ -22,10 +22,7 @@
 #
 
 #
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-#
-# ident	"%Z%%M%	%I%	%E% SMI"
+# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
 #
 
 PROG= zoneadmd
@@ -41,7 +38,8 @@
 
 CFLAGS += $(CCVERBOSE)
 LDLIBS += -lsocket -lzonecfg -lnsl -ldevinfo -ldevice -lnvpair \
-	-lgen -lbsm -lcontract -lzfs -luuid -lbrand -ldladm -ltsnet -ltsol
+	-lgen -lbsm -lcontract -lzfs -luuid -lbrand -ldladm -ltsnet -ltsol \
+	-linetutil
 XGETFLAGS += -a -x zoneadmd.xcl
 
 .KEEP_STATE:
--- a/usr/src/cmd/zoneadmd/vplat.c	Thu Jul 01 13:48:34 2010 -0700
+++ b/usr/src/cmd/zoneadmd/vplat.c	Thu Jul 01 17:10:52 2010 -0400
@@ -119,6 +119,7 @@
 #include <tsol/label.h>
 #include <libtsnet.h>
 #include <sys/priv.h>
+#include <libinetutil.h>
 
 #define	V4_ADDR_LEN	32
 #define	V6_ADDR_LEN	128
@@ -159,11 +160,29 @@
 extern char query_hook[];
 
 /*
+ * For each "net" resource configured in zonecfg, we track a zone_addr_list_t
+ * node in a linked list that is sorted by linkid.  The list is constructed as
+ * the xml configuration file is parsed, and the information
+ * contained in each node is added to the kernel before the zone is
+ * booted, to be retrieved and applied from within the exclusive-IP NGZ
+ * on boot.
+ */
+typedef struct zone_addr_list {
+	struct zone_addr_list *za_next;
+	datalink_id_t za_linkid;	/* datalink_id_t of interface */
+	struct zone_nwiftab za_nwiftab; /* address, defrouter properties */
+} zone_addr_list_t;
+
+/*
  * An optimization for build_mnttable: reallocate (and potentially copy the
  * data) only once every N times through the loop.
  */
 #define	MNTTAB_HUNK	32
 
+/* some handy macros */
+#define	SIN(s)	((struct sockaddr_in *)s)
+#define	SIN6(s)	((struct sockaddr_in6 *)s)
+
 /*
  * Private autofs system call
  */
@@ -2502,13 +2521,369 @@
 	return (0);
 }
 
+static boolean_t
+sockaddr_to_str(sa_family_t af, const struct sockaddr *sockaddr,
+    char *straddr, size_t len)
+{
+	struct sockaddr_in *sin;
+	struct sockaddr_in6 *sin6;
+	const char *str = NULL;
+
+	if (af == AF_INET) {
+		/* LINTED E_BAD_PTR_CAST_ALIGN */
+		sin = SIN(sockaddr);
+		str = inet_ntop(AF_INET, (void *)&sin->sin_addr, straddr, len);
+	} else if (af == AF_INET6) {
+		/* LINTED E_BAD_PTR_CAST_ALIGN */
+		sin6 = SIN6(sockaddr);
+		str = inet_ntop(AF_INET6, (void *)&sin6->sin6_addr, straddr,
+		    len);
+	}
+
+	return (str != NULL);
+}
+
+static int
+ipv4_prefixlen(struct sockaddr_in *sin)
+{
+	struct sockaddr_in *m;
+	struct sockaddr_storage mask;
+
+	m = SIN(&mask);
+	m->sin_family = AF_INET;
+	if (getnetmaskbyaddr(sin->sin_addr, &m->sin_addr) == 0) {
+		return (mask2plen(&mask));
+	} else if (IN_CLASSA(htonl(sin->sin_addr.s_addr))) {
+		return (8);
+	} else if (IN_CLASSB(ntohl(sin->sin_addr.s_addr))) {
+		return (16);
+	} else if (IN_CLASSC(ntohl(sin->sin_addr.s_addr))) {
+		return (24);
+	}
+	return (0);
+}
+
+static int
+zone_setattr_network(int type, zoneid_t zoneid, datalink_id_t linkid,
+    void *buf, size_t bufsize)
+{
+	zone_net_data_t *zndata;
+	size_t znsize;
+	int err;
+
+	znsize = sizeof (*zndata) + bufsize;
+	zndata = calloc(1, znsize);
+	if (zndata == NULL)
+		return (ENOMEM);
+	zndata->zn_type = type;
+	zndata->zn_len = bufsize;
+	zndata->zn_linkid = linkid;
+	bcopy(buf, zndata->zn_val, zndata->zn_len);
+	err = zone_setattr(zoneid, ZONE_ATTR_NETWORK, zndata, znsize);
+	free(zndata);
+	return (err);
+}
+
+static int
+add_net_for_linkid(zlog_t *zlogp, zoneid_t zoneid, zone_addr_list_t *start)
+{
+	struct lifreq lifr;
+	char **astr, *address;
+	dladm_status_t dlstatus;
+	char *ip_nospoof = "ip-nospoof";
+	int nnet, naddr, err = 0, j;
+	size_t zlen, cpleft;
+	zone_addr_list_t *ptr, *end;
+	char  tmp[INET6_ADDRSTRLEN], *maskstr;
+	char *zaddr, *cp;
+	struct in6_addr *routes = NULL;
+	boolean_t is_set;
+	datalink_id_t linkid;
+
+	assert(start != NULL);
+	naddr = 0; /* number of addresses */
+	nnet = 0; /* number of net resources */
+	linkid = start->za_linkid;
+	for (ptr = start; ptr != NULL && ptr->za_linkid == linkid;
+	    ptr = ptr->za_next) {
+		nnet++;
+	}
+	end = ptr;
+	zlen = nnet * (INET6_ADDRSTRLEN + 1);
+	astr = calloc(1, nnet * sizeof (uintptr_t));
+	zaddr = calloc(1, zlen);
+	if (astr == NULL || zaddr == NULL) {
+		err = ENOMEM;
+		goto done;
+	}
+	cp = zaddr;
+	cpleft = zlen;
+	j = 0;
+	for (ptr = start; ptr != end; ptr = ptr->za_next) {
+		address = ptr->za_nwiftab.zone_nwif_allowed_address;
+		if (address[0] == '\0')
+			continue;
+		(void) snprintf(tmp, sizeof (tmp), "%s", address);
+		/*
+		 * Validate the data. zonecfg_valid_net_address() clobbers
+		 * the /<mask> in the address string.
+		 */
+		if (zonecfg_valid_net_address(address, &lifr) != Z_OK) {
+			zerror(zlogp, B_FALSE, "invalid address [%s]\n",
+			    address);
+			err = EINVAL;
+			goto done;
+		}
+		/*
+		 * convert any hostnames to numeric address strings.
+		 */
+		if (!sockaddr_to_str(lifr.lifr_addr.ss_family,
+		    (const struct sockaddr *)&lifr.lifr_addr, cp, cpleft)) {
+			err = EINVAL;
+			goto done;
+		}
+		/*
+		 * make a copy of the numeric string for the data needed
+		 * by the "allowed-ips" datalink property.
+		 */
+		astr[j] = strdup(cp);
+		if (astr[j] == NULL) {
+			err = ENOMEM;
+			goto done;
+		}
+		j++;
+		/*
+		 * compute the default netmask from the address, if necessary
+		 */
+		if ((maskstr = strchr(tmp, '/')) == NULL) {
+			int prefixlen;
+
+			if (lifr.lifr_addr.ss_family == AF_INET) {
+				prefixlen = ipv4_prefixlen(
+				    SIN(&lifr.lifr_addr));
+			} else {
+				struct sockaddr_in6 *sin6;
+
+				sin6 = SIN6(&lifr.lifr_addr);
+				if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
+					prefixlen = 10;
+				else
+					prefixlen = 64;
+			}
+			(void) snprintf(tmp, sizeof (tmp), "%d", prefixlen);
+			maskstr = tmp;
+		} else {
+			maskstr++;
+		}
+		/* append the "/<netmask>" */
+		(void) strlcat(cp, "/", cpleft);
+		(void) strlcat(cp, maskstr, cpleft);
+		(void) strlcat(cp, ",", cpleft);
+		cp += strnlen(cp, zlen);
+		cpleft = &zaddr[INET6_ADDRSTRLEN] - cp;
+	}
+	naddr = j; /* the actual number of addresses in the net resource */
+	assert(naddr <= nnet);
+
+	/*
+	 * zonecfg has already verified that the defrouter property can only
+	 * be set if there is at least one address defined for the net resource.
+	 * If j is 0, there are no addresses defined, and therefore no routers
+	 * to configure, and we are done at that point.
+	 */
+	if (j == 0)
+		goto done;
+
+	/* over-write last ',' with '\0' */
+	zaddr[strnlen(zaddr, zlen) + 1] = '\0';
+
+	/*
+	 * First make sure L3 protection is not already set on the link.
+	 */
+	dlstatus = dladm_linkprop_is_set(dld_handle, linkid, DLADM_OPT_ACTIVE,
+	    "protection", &is_set);
+	if (dlstatus != DLADM_STATUS_OK) {
+		err = EINVAL;
+		zerror(zlogp, B_FALSE, "unable to check if protection is set");
+		goto done;
+	}
+	if (is_set) {
+		err = EINVAL;
+		zerror(zlogp, B_FALSE, "Protection is already set");
+		goto done;
+	}
+	dlstatus = dladm_linkprop_is_set(dld_handle, linkid, DLADM_OPT_ACTIVE,
+	    "allowed-ips", &is_set);
+	if (dlstatus != DLADM_STATUS_OK) {
+		err = EINVAL;
+		zerror(zlogp, B_FALSE, "unable to check if allowed-ips is set");
+		goto done;
+	}
+	if (is_set) {
+		zerror(zlogp, B_FALSE, "allowed-ips is already set");
+		err = EINVAL;
+		goto done;
+	}
+
+	/*
+	 * Enable ip-nospoof for the link, and add address to the allowed-ips
+	 * list.
+	 */
+	dlstatus = dladm_set_linkprop(dld_handle, linkid, "protection",
+	    &ip_nospoof, 1, DLADM_OPT_ACTIVE);
+	if (dlstatus != DLADM_STATUS_OK) {
+		zerror(zlogp, B_FALSE, "could not set protection\n");
+		err = EINVAL;
+		goto done;
+	}
+	dlstatus = dladm_set_linkprop(dld_handle, linkid, "allowed-ips",
+	    astr, naddr, DLADM_OPT_ACTIVE);
+	if (dlstatus != DLADM_STATUS_OK) {
+		zerror(zlogp, B_FALSE, "could not set allowed-ips\n");
+		err = EINVAL;
+		goto done;
+	}
+
+	/* now set the address in the data-store */
+	err = zone_setattr_network(ZONE_NETWORK_ADDRESS, zoneid, linkid,
+	    zaddr, strnlen(zaddr, zlen) + 1);
+	if (err != 0)
+		goto done;
+
+	/*
+	 * add the defaultrouters
+	 */
+	routes = calloc(1, nnet * sizeof (*routes));
+	j = 0;
+	for (ptr = start; ptr != end; ptr = ptr->za_next) {
+		address = ptr->za_nwiftab.zone_nwif_defrouter;
+		if (address[0] == '\0')
+			continue;
+		if (strchr(address, '/') == NULL && strchr(address, ':') != 0) {
+			/*
+			 * zonecfg_valid_net_address() expects numeric IPv6
+			 * addresses to have a CIDR format netmask.
+			 */
+			(void) snprintf(tmp, sizeof (tmp), "/%d", V6_ADDR_LEN);
+			(void) strlcat(address, tmp, INET6_ADDRSTRLEN);
+		}
+		if (zonecfg_valid_net_address(address, &lifr) != Z_OK) {
+			zerror(zlogp, B_FALSE,
+			    "invalid router [%s]\n", address);
+			err = EINVAL;
+			goto done;
+		}
+		if (lifr.lifr_addr.ss_family == AF_INET6) {
+			routes[j] = SIN6(&lifr.lifr_addr)->sin6_addr;
+		} else {
+			IN6_INADDR_TO_V4MAPPED(&SIN(&lifr.lifr_addr)->sin_addr,
+			    &routes[j]);
+		}
+		j++;
+	}
+	assert(j <= nnet);
+	if (j > 0) {
+		err = zone_setattr_network(ZONE_NETWORK_DEFROUTER, zoneid,
+		    linkid, routes, j * sizeof (*routes));
+	}
+done:
+	free(routes);
+	for (j = 0; j < naddr; j++)
+		free(astr[j]);
+	free(astr);
+	free(zaddr);
+	return (err);
+
+}
+
+static int
+add_net(zlog_t *zlogp, zoneid_t zoneid, zone_addr_list_t *zalist)
+{
+	zone_addr_list_t *ptr;
+	datalink_id_t linkid;
+	int err;
+
+	if (zalist == NULL)
+		return (0);
+
+	linkid = zalist->za_linkid;
+
+	err = add_net_for_linkid(zlogp, zoneid, zalist);
+	if (err != 0)
+		return (err);
+
+	for (ptr = zalist; ptr != NULL; ptr = ptr->za_next) {
+		if (ptr->za_linkid == linkid)
+			continue;
+		linkid = ptr->za_linkid;
+		err = add_net_for_linkid(zlogp, zoneid, ptr);
+		if (err != 0)
+			return (err);
+	}
+	return (0);
+}
+
+/*
+ * Add "new" to the list of network interfaces to be configured  by
+ * add_net on zone boot in "old". The list of interfaces in "old" is
+ * sorted by datalink_id_t, with interfaces sorted FIFO for a given
+ * datalink_id_t.
+ *
+ * Returns the merged list of IP interfaces containing "old" and "new"
+ */
+static zone_addr_list_t *
+add_ip_interface(zone_addr_list_t *old, zone_addr_list_t *new)
+{
+	zone_addr_list_t *ptr, *next;
+	datalink_id_t linkid = new->za_linkid;
+
+	assert(old != new);
+
+	if (old == NULL)
+		return (new);
+	for (ptr = old; ptr != NULL; ptr = ptr->za_next) {
+		if (ptr->za_linkid == linkid)
+			break;
+	}
+	if (ptr == NULL) {
+		/* linkid does not already exist, add to the beginning */
+		new->za_next = old;
+		return (new);
+	}
+	/*
+	 * adding to the middle of the list; ptr points at the first
+	 * occurrence of linkid. Find the last occurrence.
+	 */
+	while ((next = ptr->za_next) != NULL) {
+		if (next->za_linkid != linkid)
+			break;
+		ptr = next;
+	}
+	/* insert new after ptr */
+	new->za_next = next;
+	ptr->za_next = new;
+	return (old);
+}
+
+void
+free_ip_interface(zone_addr_list_t *zalist)
+{
+	zone_addr_list_t *ptr, *new;
+
+	for (ptr = zalist; ptr != NULL; ) {
+		new = ptr;
+		ptr = ptr->za_next;
+		free(new);
+	}
+}
+
 /*
  * Add the kernel access control information for the interface names.
  * If anything goes wrong, we log a general error message, attempt to tear down
  * whatever we set up, and return an error.
  */
 static int
-configure_exclusive_network_interfaces(zlog_t *zlogp)
+configure_exclusive_network_interfaces(zlog_t *zlogp, zoneid_t zoneid)
 {
 	zone_dochandle_t handle;
 	struct zone_nwiftab nwiftab;
@@ -2517,6 +2892,7 @@
 	datalink_id_t linkid;
 	di_prof_t prof = NULL;
 	boolean_t added = B_FALSE;
+	zone_addr_list_t *zalist = NULL, *new;
 
 	if ((handle = zonecfg_init_handle()) == NULL) {
 		zerror(zlogp, B_TRUE, "getting zone configuration handle");
@@ -2579,6 +2955,28 @@
 			zerror(zlogp, B_TRUE, "failed to add network device");
 			return (-1);
 		}
+		/* set up the new IP interface, and add them all later */
+		new = malloc(sizeof (*new));
+		if (new == NULL) {
+			zerror(zlogp, B_TRUE, "no memory for %s",
+			    nwiftab.zone_nwif_physical);
+			zonecfg_fini_handle(handle);
+			free_ip_interface(zalist);
+		}
+		bzero(new, sizeof (*new));
+		new->za_nwiftab = nwiftab;
+		new->za_linkid = linkid;
+		zalist = add_ip_interface(zalist, new);
+	}
+	if (zalist != NULL) {
+		if ((errno = add_net(zlogp, zoneid, zalist)) != 0) {
+			(void) zonecfg_endnwifent(handle);
+			zonecfg_fini_handle(handle);
+			zerror(zlogp, B_TRUE, "failed to add address");
+			free_ip_interface(zalist);
+			return (-1);
+		}
+		free_ip_interface(zalist);
 	}
 	(void) zonecfg_endnwifent(handle);
 	zonecfg_fini_handle(handle);
@@ -2610,8 +3008,7 @@
 	if (zone_getattr(zoneid, ZONE_ATTR_FLAGS, &flags,
 	    sizeof (flags)) < 0) {
 		if (vplat_get_iptype(zlogp, &iptype) < 0) {
-			zerror(zlogp, B_TRUE, "unable to determine "
-			    "ip-type");
+			zerror(zlogp, B_FALSE, "unable to determine ip-type");
 			return (-1);
 		}
 	} else {
@@ -2662,6 +3059,74 @@
 }
 
 static int
+remove_datalink_protect(zlog_t *zlogp, zoneid_t zoneid)
+{
+	ushort_t flags;
+	zone_iptype_t iptype;
+	int i, dlnum = 0;
+	dladm_status_t dlstatus;
+	datalink_id_t *dllink, *dllinks = NULL;
+
+	if (zone_getattr(zoneid, ZONE_ATTR_FLAGS, &flags,
+	    sizeof (flags)) < 0) {
+		if (vplat_get_iptype(zlogp, &iptype) < 0) {
+			zerror(zlogp, B_FALSE, "unable to determine ip-type");
+			return (-1);
+		}
+	} else {
+		if (flags & ZF_NET_EXCL)
+			iptype = ZS_EXCLUSIVE;
+		else
+			iptype = ZS_SHARED;
+	}
+
+	if (iptype != ZS_EXCLUSIVE)
+		return (0);
+
+	/*
+	 * Get the datalink count and for each datalink,
+	 * attempt to clear the pool property and clear
+	 * the pool_name.
+	 */
+	if (zone_list_datalink(zoneid, &dlnum, NULL) != 0) {
+		zerror(zlogp, B_TRUE, "unable to count network interfaces");
+		return (-1);
+	}
+
+	if (dlnum == 0)
+		return (0);
+
+	if ((dllinks = malloc(dlnum * sizeof (datalink_id_t))) == NULL) {
+		zerror(zlogp, B_TRUE, "memory allocation failed");
+		return (-1);
+	}
+	if (zone_list_datalink(zoneid, &dlnum, dllinks) != 0) {
+		zerror(zlogp, B_TRUE, "unable to list network interfaces");
+		free(dllinks);
+		return (-1);
+	}
+
+	for (i = 0, dllink = dllinks; i < dlnum; i++, dllink++) {
+		dlstatus = dladm_set_linkprop(dld_handle, *dllink,
+		    "protection", NULL, 0, DLADM_OPT_ACTIVE);
+		if (dlstatus != DLADM_STATUS_OK) {
+			zerror(zlogp, B_TRUE, "could not reset protection\n");
+			free(dllinks);
+			return (-1);
+		}
+		dlstatus = dladm_set_linkprop(dld_handle, *dllink,
+		    "allowed-ips", NULL, 0, DLADM_OPT_ACTIVE);
+		if (dlstatus != DLADM_STATUS_OK) {
+			zerror(zlogp, B_TRUE, "could not reset allowed-ips\n");
+			free(dllinks);
+			return (-1);
+		}
+	}
+	free(dllinks);
+	return (0);
+}
+
+static int
 unconfigure_exclusive_network_interfaces(zlog_t *zlogp, zoneid_t zoneid)
 {
 	int dlnum = 0;
@@ -4542,7 +5007,8 @@
 			}
 			break;
 		case ZS_EXCLUSIVE:
-			if (configure_exclusive_network_interfaces(zlogp) !=
+			if (configure_exclusive_network_interfaces(zlogp,
+			    zoneid) !=
 			    0) {
 				lofs_discard_mnttab();
 				return (-1);
@@ -4667,6 +5133,19 @@
 		goto error;
 	}
 
+	if (remove_datalink_protect(zlogp, zoneid) != 0) {
+		zerror(zlogp, B_FALSE,
+		    "unable clear datalink protect property");
+		goto error;
+	}
+
+	/*
+	 * The datalinks assigned to the zone will be removed from the NGZ as
+	 * part of zone_shutdown() so that we need to remove protect/pool etc.
+	 * before zone_shutdown(). Even if the shutdown itself fails, the zone
+	 * will not be able to violate any constraints applied because the
+	 * datalinks are no longer available to the zone.
+	 */
 	if (zone_shutdown(zoneid) != 0) {
 		zerror(zlogp, B_TRUE, "unable to shutdown zone");
 		goto error;
--- a/usr/src/cmd/zonecfg/zonecfg.c	Thu Jul 01 13:48:34 2010 -0700
+++ b/usr/src/cmd/zonecfg/zonecfg.c	Thu Jul 01 17:10:52 2010 -0400
@@ -76,6 +76,7 @@
 #include <libdladm.h>
 #include <libinetutil.h>
 #include <pwd.h>
+#include <inet/ip.h>
 
 #include <libzonecfg.h>
 #include "zonecfg.h"
@@ -230,6 +231,7 @@
 	"auths",
 	"fs-allowed",
 	ALIAS_MAXPROCS,
+	"allowed-address",
 	NULL
 };
 
@@ -500,8 +502,18 @@
 	NULL
 };
 
+struct xif {
+	struct xif	*xif_next;
+	char		xif_name[LIFNAMSIZ];
+	boolean_t	xif_has_address;
+	boolean_t	xif_has_defrouter;
+};
+
 /* Global variables */
 
+/* list of network interfaces specified for exclusive IP zone */
+struct xif *xif;
+
 /* set early in main(), never modified thereafter, used all over the place */
 static char *execname;
 
@@ -976,17 +988,30 @@
 			(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\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, gettext("See ifconfig(1M) for "
 			    "details of the <interface> string.\n"));
-			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
-			    pt_to_str(PT_DEFROUTER), gettext("<IP-address>"));
-			(void) fprintf(fp, gettext("%s %s and %s %s are valid "
-			    "if the %s property is set to %s, otherwise they "
+			(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_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),
+			    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, "
+			    "otherwise it must not be set\n"),
+			    cmd_to_str(CMD_SET),
+			    pt_to_str(PT_DEFROUTER), gettext("<IP-address>"),
 			    cmd_to_str(CMD_SET), pt_to_str(PT_DEFROUTER),
-			    pt_to_str(PT_IPTYPE), "shared");
+			    gettext(pt_to_str(PT_ADDRESS)),
+			    gettext(pt_to_str(PT_ALLOWED_ADDRESS)));
 			break;
 		case RT_DEVICE:
 			(void) fprintf(fp, gettext("The '%s' resource scope is "
@@ -1220,9 +1245,9 @@
 		    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\n", rt_to_str(RT_NET),
-		    pt_to_str(PT_ADDRESS), pt_to_str(PT_PHYSICAL),
-		    pt_to_str(PT_DEFROUTER));
+		(void) fprintf(fp, "\t%s\t\t%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));
 		(void) fprintf(fp, "\t%s\t\t%s, %s\n", rt_to_str(RT_RCTL),
@@ -1849,6 +1874,8 @@
 		(void) fprintf(of, "%s %s\n", cmd_to_str(CMD_ADD),
 		    rt_to_str(RT_NET));
 		export_prop(of, PT_ADDRESS, nwiftab.zone_nwif_address);
+		export_prop(of, PT_ALLOWED_ADDRESS,
+		    nwiftab.zone_nwif_allowed_address);
 		export_prop(of, PT_PHYSICAL, nwiftab.zone_nwif_physical);
 		export_prop(of, PT_DEFROUTER, nwiftab.zone_nwif_defrouter);
 		(void) fprintf(of, "%s\n", cmd_to_str(CMD_END));
@@ -2601,6 +2628,11 @@
 			(void) strlcpy(nwiftab->zone_nwif_address,
 			    pp->pv_simple, sizeof (nwiftab->zone_nwif_address));
 			break;
+		case PT_ALLOWED_ADDRESS:
+			(void) strlcpy(nwiftab->zone_nwif_allowed_address,
+			    pp->pv_simple,
+			    sizeof (nwiftab->zone_nwif_allowed_address));
+			break;
 		case PT_PHYSICAL:
 			(void) strlcpy(nwiftab->zone_nwif_physical,
 			    pp->pv_simple,
@@ -3758,10 +3790,14 @@
  * IPv4 addresses and host names, 0 to 128 for IPv6 addresses.
  * Host names must start with an alpha-numeric character, and all subsequent
  * characters must be either alpha-numeric or "-".
+ *
+ * In some cases, e.g., the nexthop for the defrouter, the context indicates
+ * that this is the IPV4_ABITS or IPV6_ABITS netmask, in which case we don't
+ * require the /<prefix length> (and should ignore it if provided).
  */
 
 static int
-validate_net_address_syntax(char *address)
+validate_net_address_syntax(char *address, boolean_t ishost)
 {
 	char *slashp, part1[MAXHOSTNAMELEN];
 	struct in6_addr in6;
@@ -3781,8 +3817,17 @@
 		(void) strlcpy(part1, address, sizeof (part1));
 	}
 
+	if (ishost && slashp != NULL) {
+		zerr(gettext("Warning: prefix length in %s is not required and "
+		    "will be ignored. The default host-prefix length "
+		    "will be used"), address);
+	}
+
+
 	if (inet_pton(AF_INET6, part1, &in6) == 1) {
-		if (slashp == NULL) {
+		if (ishost) {
+			prefixlen = IPV6_ABITS;
+		} else if (slashp == NULL) {
 			zerr(gettext("%s: IPv6 addresses "
 			    "require /prefix-length suffix."), address);
 			return (Z_ERR);
@@ -3796,13 +3841,16 @@
 	}
 
 	/* At this point, any /prefix must be for IPv4. */
-	if (slashp != NULL) {
+	if (ishost)
+		prefixlen = IPV4_ABITS;
+	else if (slashp != NULL) {
 		if (prefixlen < 0 || prefixlen > 32) {
 			zerr(gettext("%s: IPv4 address "
 			    "prefix lengths must be 0 - 32."), address);
 			return (Z_ERR);
 		}
 	}
+
 	if (inet_pton(AF_INET, part1, &in4) == 1)
 		return (Z_OK);
 
@@ -3953,6 +4001,20 @@
 	}
 }
 
+static void
+set_in_progress_nwiftab_address(char *prop_id, int prop_type)
+{
+	if (prop_type == PT_ADDRESS) {
+		(void) strlcpy(in_progress_nwiftab.zone_nwif_address, prop_id,
+		    sizeof (in_progress_nwiftab.zone_nwif_address));
+	} else {
+		assert(prop_type == PT_ALLOWED_ADDRESS);
+		(void) strlcpy(in_progress_nwiftab.zone_nwif_allowed_address,
+		    prop_id,
+		    sizeof (in_progress_nwiftab.zone_nwif_allowed_address));
+	}
+}
+
 void
 set_func(cmd_t *cmd)
 {
@@ -4312,13 +4374,13 @@
 	case RT_NET:
 		switch (prop_type) {
 		case PT_ADDRESS:
-			if (validate_net_address_syntax(prop_id) != Z_OK) {
+		case PT_ALLOWED_ADDRESS:
+			if (validate_net_address_syntax(prop_id, B_FALSE)
+			    != Z_OK) {
 				saw_error = B_TRUE;
 				return;
 			}
-			(void) strlcpy(in_progress_nwiftab.zone_nwif_address,
-			    prop_id,
-			    sizeof (in_progress_nwiftab.zone_nwif_address));
+			set_in_progress_nwiftab_address(prop_id, prop_type);
 			break;
 		case PT_PHYSICAL:
 			if (validate_net_physical_syntax(prop_id) != Z_OK) {
@@ -4330,7 +4392,8 @@
 			    sizeof (in_progress_nwiftab.zone_nwif_physical));
 			break;
 		case PT_DEFROUTER:
-			if (validate_net_address_syntax(prop_id) != Z_OK) {
+			if (validate_net_address_syntax(prop_id, B_TRUE)
+			    != Z_OK) {
 				saw_error = B_TRUE;
 				return;
 			}
@@ -4879,6 +4942,8 @@
 {
 	(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);
 }
@@ -5597,6 +5662,41 @@
 }
 
 /*
+ * Track the network interfaces listed in zonecfg(1m) in a linked list
+ * so that we can later check that defrouter is specified for an exclusive IP
+ * zone if and only if at least one allowed-address has been specified.
+ */
+static boolean_t
+add_nwif(struct zone_nwiftab *nwif)
+{
+	struct xif *tmp;
+
+	for (tmp = xif; tmp != NULL; tmp = tmp->xif_next) {
+		if (strcmp(tmp->xif_name, nwif->zone_nwif_physical) == 0) {
+			if (strlen(nwif->zone_nwif_allowed_address) > 0)
+				tmp->xif_has_address = B_TRUE;
+			if (strlen(nwif->zone_nwif_defrouter) > 0)
+				tmp->xif_has_defrouter = B_TRUE;
+			return (B_TRUE);
+		}
+	}
+
+	tmp = malloc(sizeof (*tmp));
+	if (tmp == NULL) {
+		zerr(gettext("memory allocation failed for %s"),
+		    nwif->zone_nwif_physical);
+		return (B_FALSE);
+	}
+	strlcpy(tmp->xif_name, nwif->zone_nwif_physical,
+	    sizeof (tmp->xif_name));
+	tmp->xif_has_defrouter = (strlen(nwif->zone_nwif_defrouter) > 0);
+	tmp->xif_has_address = (strlen(nwif->zone_nwif_allowed_address) > 0);
+	tmp->xif_next = xif;
+	xif = tmp;
+	return (B_TRUE);
+}
+
+/*
  * See the DTD for which attributes are required for which resources.
  *
  * This function can be called by commit_func(), which needs to save things,
@@ -5627,6 +5727,7 @@
 	zone_iptype_t iptype;
 	boolean_t has_cpu_shares = B_FALSE;
 	boolean_t has_cpu_cap = B_FALSE;
+	struct xif *tmp;
 
 	optind = 0;
 	while ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?")) != EOF) {
@@ -5740,6 +5841,15 @@
 		case ZS_SHARED:
 			check_reqd_prop(nwiftab.zone_nwif_address, RT_NET,
 			    PT_ADDRESS, &ret_val);
+			if (strlen(nwiftab.zone_nwif_allowed_address) > 0) {
+				zerr(gettext("%s: %s cannot be specified "
+				    "for a shared IP type"),
+				    rt_to_str(RT_NET),
+				    pt_to_str(PT_ALLOWED_ADDRESS));
+				saw_error = B_TRUE;
+				if (ret_val == Z_OK)
+					ret_val = Z_INVAL;
+			}
 			break;
 		case ZS_EXCLUSIVE:
 			if (strlen(nwiftab.zone_nwif_address) > 0) {
@@ -5749,18 +5859,28 @@
 				saw_error = B_TRUE;
 				if (ret_val == Z_OK)
 					ret_val = Z_INVAL;
-			}
-			if (strlen(nwiftab.zone_nwif_defrouter) > 0) {
-				zerr(gettext("%s: %s cannot be specified "
-				    "for an exclusive IP type"),
-				    rt_to_str(RT_NET), pt_to_str(PT_DEFROUTER));
-				saw_error = B_TRUE;
-				if (ret_val == Z_OK)
-					ret_val = Z_INVAL;
+			} else {
+				if (!add_nwif(&nwiftab)) {
+					saw_error = B_TRUE;
+					if (ret_val == Z_OK)
+						ret_val = Z_INVAL;
+				}
 			}
 			break;
 		}
 	}
+	for (tmp = xif; tmp != NULL; tmp = tmp->xif_next) {
+		if (!tmp->xif_has_address && tmp->xif_has_defrouter) {
+			zerr(gettext("%s: %s for %s cannot be specified "
+			    "without %s for an exclusive IP type"),
+			    rt_to_str(RT_NET), pt_to_str(PT_DEFROUTER),
+			    tmp->xif_name, pt_to_str(PT_ALLOWED_ADDRESS));
+			saw_error = B_TRUE;
+			ret_val = Z_INVAL;
+		}
+	}
+	free(xif);
+	xif = NULL;
 	(void) zonecfg_endnwifent(handle);
 
 	if ((err = zonecfg_setrctlent(handle)) != Z_OK) {
@@ -6023,6 +6143,28 @@
 	return (Z_OK);
 }
 
+static void
+net_exists_error(struct zone_nwiftab nwif)
+{
+	if (strlen(nwif.zone_nwif_address) > 0) {
+		zerr(gettext("A %s resource with the %s '%s', "
+		    "and %s '%s' already exists."),
+		    rt_to_str(RT_NET),
+		    pt_to_str(PT_PHYSICAL),
+		    nwif.zone_nwif_physical,
+		    pt_to_str(PT_ADDRESS),
+		    in_progress_nwiftab.zone_nwif_address);
+	} else {
+		zerr(gettext("A %s resource with the %s '%s', "
+		    "and %s '%s' already exists."),
+		    rt_to_str(RT_NET),
+		    pt_to_str(PT_PHYSICAL),
+		    nwif.zone_nwif_physical,
+		    pt_to_str(PT_ALLOWED_ADDRESS),
+		    nwif.zone_nwif_allowed_address);
+	}
+}
+
 void
 end_func(cmd_t *cmd)
 {
@@ -6151,14 +6293,14 @@
 			(void) strlcpy(tmp_nwiftab.zone_nwif_address,
 			    in_progress_nwiftab.zone_nwif_address,
 			    sizeof (tmp_nwiftab.zone_nwif_address));
+			(void) strlcpy(tmp_nwiftab.zone_nwif_allowed_address,
+			    in_progress_nwiftab.zone_nwif_allowed_address,
+			    sizeof (tmp_nwiftab.zone_nwif_allowed_address));
+			(void) strlcpy(tmp_nwiftab.zone_nwif_defrouter,
+			    in_progress_nwiftab.zone_nwif_defrouter,
+			    sizeof (tmp_nwiftab.zone_nwif_defrouter));
 			if (zonecfg_lookup_nwif(handle, &tmp_nwiftab) == Z_OK) {
-				zerr(gettext("A %s resource with the %s '%s', "
-				    "and %s '%s' already exists."),
-				    rt_to_str(RT_NET),
-				    pt_to_str(PT_PHYSICAL),
-				    in_progress_nwiftab.zone_nwif_physical,
-				    pt_to_str(PT_ADDRESS),
-				    in_progress_nwiftab.zone_nwif_address);
+				net_exists_error(in_progress_nwiftab);
 				saw_error = B_TRUE;
 				return;
 			}
--- a/usr/src/cmd/zonecfg/zonecfg.h	Thu Jul 01 13:48:34 2010 -0700
+++ b/usr/src/cmd/zonecfg/zonecfg.h	Thu Jul 01 17:10:52 2010 -0400
@@ -136,9 +136,10 @@
 #define	PT_AUTHS	38
 #define	PT_FS_ALLOWED	39
 #define	PT_MAXPROCS	40
+#define	PT_ALLOWED_ADDRESS	41
 
 #define	PT_MIN		PT_UNKNOWN
-#define	PT_MAX		PT_MAXPROCS
+#define	PT_MAX		PT_ALLOWED_ADDRESS
 
 #define	MAX_EQ_PROP_PAIRS	3
 
--- a/usr/src/cmd/zonecfg/zonecfg_grammar.y	Thu Jul 01 13:48:34 2010 -0700
+++ b/usr/src/cmd/zonecfg/zonecfg_grammar.y	Thu Jul 01 17:10:52 2010 -0400
@@ -123,7 +123,7 @@
 %token HELP CREATE EXPORT ADD DELETE REMOVE SELECT SET INFO CANCEL END VERIFY
 %token COMMIT REVERT EXIT SEMICOLON TOKEN ZONENAME ZONEPATH AUTOBOOT POOL NET
 %token FS ATTR DEVICE RCTL SPECIAL RAW DIR OPTIONS TYPE ADDRESS PHYSICAL
-%token IPTYPE HOSTID FS_ALLOWED
+%token IPTYPE HOSTID FS_ALLOWED ALLOWED_ADDRESS
 %token NAME MATCH PRIV LIMIT ACTION VALUE EQUAL OPEN_SQ_BRACKET CLOSE_SQ_BRACKET
 %token OPEN_PAREN CLOSE_PAREN COMMA DATASET LIMITPRIV BOOTARGS BRAND PSET PCAP
 %token MCAP NCPUS IMPORTANCE SHARES MAXLWPS MAXSHMMEM MAXSHMIDS MAXMSGIDS
@@ -137,6 +137,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
 %type <cmd> command
 %type <cmd> add_command ADD
 %type <cmd> cancel_command CANCEL
@@ -956,6 +957,7 @@
 	| LIMITPRIV	{ $$ = PT_LIMITPRIV; }
 	| BOOTARGS	{ $$ = PT_BOOTARGS; }
 	| ADDRESS	{ $$ = PT_ADDRESS; }
+	| ALLOWED_ADDRESS	{ $$ = PT_ALLOWED_ADDRESS; }
 	| PHYSICAL	{ $$ = PT_PHYSICAL; }
 	| DEFROUTER	{ $$ = PT_DEFROUTER; }
 	| NAME		{ $$ = PT_NAME; }
--- a/usr/src/cmd/zonecfg/zonecfg_lex.l	Thu Jul 01 13:48:34 2010 -0700
+++ b/usr/src/cmd/zonecfg/zonecfg_lex.l	Thu Jul 01 17:10:52 2010 -0400
@@ -204,6 +204,9 @@
 <TSTATE>options	{ return OPTIONS; }
 <CSTATE>options	{ return OPTIONS; }
 
+<TSTATE>allowed-address { return ALLOWED_ADDRESS; }
+<CSTATE>allowed-address { return ALLOWED_ADDRESS; }
+
 <TSTATE>address	{ return ADDRESS; }
 <CSTATE>address	{ return ADDRESS; }
 
--- a/usr/src/head/libzonecfg.h	Thu Jul 01 13:48:34 2010 -0700
+++ b/usr/src/head/libzonecfg.h	Thu Jul 01 17:10:52 2010 -0400
@@ -192,7 +192,8 @@
 };
 
 struct zone_nwiftab {
-	char	zone_nwif_address[INET6_ADDRSTRLEN];
+	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_defrouter[INET6_ADDRSTRLEN];
 };
--- a/usr/src/lib/libdladm/common/linkprop.c	Thu Jul 01 13:48:34 2010 -0700
+++ b/usr/src/lib/libdladm/common/linkprop.c	Thu Jul 01 17:10:52 2010 -0400
@@ -4503,11 +4503,13 @@
 	uint_t		valcnt = DLADM_MAX_PROP_VALCNT;
 	int		i;
 	dladm_status_t	status = DLADM_STATUS_OK;
+	size_t		bufsize;
 
 	*is_set = B_FALSE;
 
-	if ((buf = malloc((sizeof (char *) + DLADM_PROP_VAL_MAX) *
-	    DLADM_MAX_PROP_VALCNT)) == NULL)
+	bufsize = (sizeof (char *) + DLADM_PROP_VAL_MAX) *
+	    DLADM_MAX_PROP_VALCNT;
+	if ((buf = calloc(1, bufsize)) == NULL)
 		return (DLADM_STATUS_NOMEM);
 
 	propvals = (char **)(void *)buf;
@@ -4522,7 +4524,15 @@
 		goto done;
 	}
 
-	if ((strcmp(prop_name, "pool") == 0) && (strlen(*propvals) != 0)) {
+	/*
+	 * valcnt is always set to 1 by get_pool(), hence we need to check
+	 * for a non-null string to see if it is set. For protection and
+	 * allowed-ips, we can check either the *propval or the valcnt.
+	 */
+	if ((strcmp(prop_name, "pool") == 0 ||
+	    strcmp(prop_name, "protection") == 0 ||
+	    strcmp(prop_name, "allowed-ips") == 0) &&
+	    (strlen(*propvals) != 0)) {
 		*is_set = B_TRUE;
 	} else if ((strcmp(prop_name, "cpus") == 0) && (valcnt != 0)) {
 		*is_set = B_TRUE;
--- a/usr/src/lib/libipadm/Makefile.com	Thu Jul 01 13:48:34 2010 -0700
+++ b/usr/src/lib/libipadm/Makefile.com	Thu Jul 01 17:10:52 2010 -0400
@@ -19,15 +19,14 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
 #
 #
 
 LIBRARY = libipadm.a
 VERS    = .1
 OBJECTS = libipadm.o ipadm_prop.o ipadm_persist.o ipadm_addr.o ipadm_if.o \
-	  ipadm_ndpd.o
+	  ipadm_ndpd.o ipadm_ngz.o
 
 include ../../Makefile.lib
 
--- a/usr/src/lib/libipadm/common/ipadm_addr.c	Thu Jul 01 13:48:34 2010 -0700
+++ b/usr/src/lib/libipadm/common/ipadm_addr.c	Thu Jul 01 17:10:52 2010 -0400
@@ -1312,7 +1312,7 @@
 	int		s;
 	size_t		nbytes = 0;
 
-	if (getzoneid() != GLOBAL_ZONEID) {
+	if (iph->iph_zoneid != GLOBAL_ZONEID) {
 		buf[0] = '\0';
 		return (IPADM_SUCCESS);
 	}
@@ -1660,7 +1660,7 @@
 	return (IPADM_SUCCESS);
 }
 
-static ipadm_status_t
+ipadm_status_t
 i_ipadm_resolve_addr(const char *name, sa_family_t af,
     struct sockaddr_storage *ss)
 {
@@ -1935,6 +1935,9 @@
 	ipmgmt_retval_t		rval, *rvalp;
 	int			err;
 
+	if (iph->iph_flags & IPH_IPMGMTD)
+		return (IPADM_SUCCESS);
+
 	bzero(&larg, sizeof (larg));
 	larg.ia_cmd = IPMGMT_CMD_ADDROBJ_SETLIFNUM;
 	(void) strlcpy(larg.ia_aobjname, ipaddr->ipadm_aobjname,
@@ -2266,6 +2269,8 @@
  * also checks if the interface is under DHCP control. If the condition is true,
  * the output argument `exists' will be set to B_TRUE. Otherwise, `exists'
  * is set to B_FALSE.
+ *
+ * Note that *exists will not be initialized if an error is encountered.
  */
 static ipadm_status_t
 i_ipadm_addr_exists_on_if(ipadm_handle_t iph, const char *ifname,
@@ -2405,6 +2410,7 @@
 	boolean_t		is_6to4;
 	struct lifreq		lifr;
 	uint64_t		ifflags;
+	boolean_t		is_boot = (iph->iph_flags & IPH_IPMGMTD);
 
 	/* check for solaris.network.interface.config authorization */
 	if (!ipadm_check_auth())
@@ -2450,10 +2456,16 @@
 	af = addr->ipadm_af;
 	/*
 	 * Create a placeholder for this address object in the daemon.
-	 * Skip this step for IPH_LEGACY case if the addrobj already
-	 * exists.
+	 * Skip this step if we are booting a zone (and therefore being called
+	 * from ipmgmtd itself), and, for IPH_LEGACY case if the
+	 * addrobj already exists.
+	 *
+	 * Note that the placeholder is not needed in the NGZ boot case,
+	 * when zoneadmd has itself applied the "allowed-ips" property to clamp
+	 * down any interface configuration, so the namespace for the interface
+	 * is fully controlled by the GZ.
 	 */
-	if (!legacy || !aobjfound) {
+	if (!is_boot && (!legacy || !aobjfound)) {
 		status = i_ipadm_lookupadd_addrobj(iph, addr);
 		if (status != IPADM_SUCCESS)
 			return (status);
@@ -2479,11 +2491,23 @@
 			created_other_af = B_TRUE;
 	}
 
-	/* Validate static addresses for IFF_POINTOPOINT interfaces. */
+	/*
+	 * Some input validation based on the interface flags:
+	 * 1. in non-global zones, make sure that we are not persistently
+	 *    creating addresses on interfaces that are acquiring
+	 *    address from the global zone.
+	 * 2. Validate static addresses for IFF_POINTOPOINT interfaces.
+	 */
 	if (addr->ipadm_atype == IPADM_ADDR_STATIC) {
 		status = i_ipadm_get_flags(iph, ifname, af, &ifflags);
 		if (status != IPADM_SUCCESS)
 			goto fail;
+
+		if (iph->iph_zoneid != GLOBAL_ZONEID &&
+		    (ifflags & IFF_L3PROTECT) && (flags & IPADM_OPT_PERSIST)) {
+			status = IPADM_GZ_PERM;
+			goto fail;
+		}
 		daf = addr->ipadm_static_dst_addr.ss_family;
 		if (ifflags & IFF_POINTOPOINT) {
 			if (is_6to4) {
@@ -2596,7 +2620,9 @@
 	boolean_t			legacy = (iph->iph_flags & IPH_LEGACY);
 	struct ipadm_addrobj_s		legacy_addr;
 	boolean_t			default_prefixlen = B_FALSE;
-
+	boolean_t			is_boot;
+
+	is_boot = ((iph->iph_flags & IPH_IPMGMTD) != 0);
 	af = ipaddr->ipadm_af;
 	sock = (af == AF_INET ? iph->iph_sock : iph->iph_sock6);
 
@@ -2668,7 +2694,7 @@
 			status = IPADM_SUCCESS;
 	}
 
-	if (status == IPADM_SUCCESS) {
+	if (status == IPADM_SUCCESS && !is_boot) {
 		/*
 		 * For IPH_LEGACY, we might be modifying the address on
 		 * an address object that already exists e.g. by doing
--- a/usr/src/lib/libipadm/common/ipadm_if.c	Thu Jul 01 13:48:34 2010 -0700
+++ b/usr/src/lib/libipadm/common/ipadm_if.c	Thu Jul 01 17:10:52 2010 -0400
@@ -169,6 +169,8 @@
 			ifp->ifi_cflags |= IFIF_IPV4;
 		if (lifrl.lifr_flags & IFF_IPV6)
 			ifp->ifi_cflags |= IFIF_IPV6;
+		if (lifrl.lifr_flags & IFF_L3PROTECT)
+			ifp->ifi_cflags |= IFIF_L3PROTECT;
 	}
 	free(buf);
 	return (IPADM_SUCCESS);
@@ -376,6 +378,15 @@
 	ipadm_if_info_t	*ifinfo;
 	ipadm_status_t	status;
 
+	/*
+	 * if IPH_IPMGMTD is set, we know that the caller (ipmgmtd) already
+	 * knows about persistent configuration in the first place, so we
+	 * just return success.
+	 */
+	if (iph->iph_flags & IPH_IPMGMTD) {
+		*exists = B_FALSE;
+		return (IPADM_SUCCESS);
+	}
 	status = i_ipadm_persist_if_info(iph, ifname, &ifinfo);
 	if (status == IPADM_SUCCESS) {
 		*exists = ((af == AF_INET &&
@@ -688,7 +699,7 @@
 	 * that the non-global zones don't need this check, because zoneadm
 	 * has taken care of this when the zones boot.
 	 */
-	if (getzoneid() == GLOBAL_ZONEID && dlstatus == DLADM_STATUS_OK) {
+	if (iph->iph_zoneid == GLOBAL_ZONEID && dlstatus == DLADM_STATUS_OK) {
 		zoneid = ALL_ZONES;
 		if (zone_check_datalink(&zoneid, linkid) == 0) {
 			/* interface is in use by a non-global zone. */
--- a/usr/src/lib/libipadm/common/ipadm_ipmgmt.h	Thu Jul 01 13:48:34 2010 -0700
+++ b/usr/src/lib/libipadm/common/ipadm_ipmgmt.h	Thu Jul 01 17:10:52 2010 -0400
@@ -265,6 +265,17 @@
 extern int		ipadm_rw_db(db_wfunc_t *, void *, const char *, mode_t,
 			    ipadm_db_op_t);
 
+/* zone related functions */
+/*
+ *  callback function to persist an interface in ipmgmtd data store
+ */
+typedef void (*persist_cb_t)(char *, boolean_t, boolean_t);
+/*
+ * ipmgmtd/libipadm network initialization interface.
+ */
+extern ipadm_status_t	ipadm_init_net_from_gz(ipadm_handle_t, char *,
+			    persist_cb_t);
+
 #ifdef	__cplusplus
 }
 #endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libipadm/common/ipadm_ngz.c	Thu Jul 01 17:10:52 2010 -0400
@@ -0,0 +1,485 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <priv_utils.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <strings.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <zone.h>
+#include <libipadm.h>
+#include <libdladm.h>
+#include <libdllink.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <net/route.h>
+#include <errno.h>
+#include <inet/ip.h>
+#include <string.h>
+#include <libinetutil.h>
+#include <unistd.h>
+#include <libipadm_impl.h>
+#include <sys/brand.h>
+
+#define	ROUNDUP_LONG(a) \
+	((a) > 0 ? (1 + (((a) - 1) | (sizeof (long) - 1))) : sizeof (long))
+#define	HOST_MASK	0xffffffffU
+
+typedef struct ngz_walk_data_s {
+	ipadm_handle_t	ngz_iph;
+	zoneid_t	ngz_zoneid;
+	char		*ngz_ifname;
+	boolean_t	ngz_s10c;
+	ipadm_status_t  ngz_ipstatus;
+	persist_cb_t	ngz_persist_if;
+} ngz_walk_data_t;
+
+/*
+ * Tell the kernel to add, delete or change a route
+ */
+static void
+i_ipadm_rtioctl4(int rtsock,
+    int action,			/* RTM_DELETE, etc */
+    in_addr_t dst,
+    in_addr_t gate,
+    uint_t masklen,
+    char *ifname,
+    uint8_t metric,
+    int flags)
+{
+	static int rt_sock_seqno = 0;
+	struct {
+		struct rt_msghdr w_rtm;
+		struct sockaddr_in w_dst;
+		struct sockaddr_in w_gate;
+		uint8_t w_space[512];
+	} w;
+	struct sockaddr_in w_mask;
+	struct sockaddr_dl w_ifp;
+	uint8_t *cp;
+	long cc;
+
+again:
+	(void) memset(&w, 0, sizeof (w));
+	(void) memset(&w_mask, 0, sizeof (w_mask));
+	(void) memset(&w_ifp, 0, sizeof (w_ifp));
+	cp = w.w_space;
+	w.w_rtm.rtm_msglen = sizeof (struct rt_msghdr) +
+	    2 * ROUNDUP_LONG(sizeof (struct sockaddr_in));
+	w.w_rtm.rtm_version = RTM_VERSION;
+	w.w_rtm.rtm_type = action;
+	w.w_rtm.rtm_flags = (flags | RTF_ZONE);
+	w.w_rtm.rtm_seq = ++rt_sock_seqno;
+	w.w_rtm.rtm_addrs = RTA_DST|RTA_GATEWAY;
+	if (metric != 0 || action == RTM_CHANGE) {
+		w.w_rtm.rtm_rmx.rmx_hopcount = metric;
+		w.w_rtm.rtm_inits |= RTV_HOPCOUNT;
+	}
+	w.w_dst.sin_family = AF_INET;
+	w.w_dst.sin_addr.s_addr = dst;
+	w.w_gate.sin_family = AF_INET;
+	w.w_gate.sin_addr.s_addr = gate;
+	if (masklen == HOST_MASK) {
+		w.w_rtm.rtm_flags |= RTF_HOST;
+	} else {
+		struct sockaddr_storage m4;
+
+		w.w_rtm.rtm_addrs |= RTA_NETMASK;
+		w_mask.sin_family = AF_INET;
+		if (plen2mask(masklen, AF_INET, &m4) != 0) {
+			return;
+		}
+		w_mask.sin_addr = ((struct sockaddr_in *)&m4)->sin_addr;
+		(void) memmove(cp, &w_mask, sizeof (w_mask));
+		cp += ROUNDUP_LONG(sizeof (struct sockaddr_in));
+		w.w_rtm.rtm_msglen += ROUNDUP_LONG(sizeof (struct sockaddr_in));
+	}
+	w_ifp.sdl_family = AF_LINK;
+	w.w_rtm.rtm_addrs |= RTA_IFP;
+	w_ifp.sdl_index = if_nametoindex(ifname);
+	(void) memmove(cp, &w_ifp, sizeof (w_ifp));
+	w.w_rtm.rtm_msglen += ROUNDUP_LONG(sizeof (struct sockaddr_dl));
+
+	cc = write(rtsock, &w, w.w_rtm.rtm_msglen);
+	if (cc < 0) {
+		if (errno == ESRCH && (action == RTM_CHANGE ||
+		    action == RTM_DELETE)) {
+			if (action == RTM_CHANGE) {
+				action = RTM_ADD;
+				goto again;
+			}
+			return;
+		}
+		return;
+	} else if (cc != w.w_rtm.rtm_msglen) {
+		return;
+	}
+}
+
+static void
+i_ipadm_rtioctl6(int rtsock,
+    int action,			/* RTM_DELETE, etc */
+    in6_addr_t dst,
+    in6_addr_t gate,
+    uint_t prefix_length,
+    char *ifname,
+    int flags)
+{
+	static int rt_sock_seqno = 0;
+	struct {
+		struct rt_msghdr w_rtm;
+		struct sockaddr_in6 w_dst;
+		struct sockaddr_in6 w_gate;
+		uint8_t w_space[512];
+	} w;
+	struct sockaddr_in6 w_mask;
+	struct sockaddr_dl w_ifp;
+	uint8_t *cp;
+	long cc;
+
+again:
+	(void) memset(&w, 0, sizeof (w));
+	(void) memset(&w_mask, 0, sizeof (w_mask));
+	(void) memset(&w_ifp, 0, sizeof (w_ifp));
+	cp = w.w_space;
+	w.w_rtm.rtm_msglen = sizeof (struct rt_msghdr) +
+	    2 * ROUNDUP_LONG(sizeof (struct sockaddr_in6));
+	w.w_rtm.rtm_version = RTM_VERSION;
+	w.w_rtm.rtm_type = action;
+	w.w_rtm.rtm_flags = (flags | RTF_ZONE);
+	w.w_rtm.rtm_seq = ++rt_sock_seqno;
+	w.w_rtm.rtm_addrs = RTA_DST|RTA_GATEWAY;
+	w.w_dst.sin6_family = AF_INET6;
+	w.w_dst.sin6_addr = dst;
+	w.w_gate.sin6_family = AF_INET6;
+	w.w_gate.sin6_addr = gate;
+	if (prefix_length == IPV6_ABITS) {
+		w.w_rtm.rtm_flags |= RTF_HOST;
+	} else {
+		struct sockaddr_storage m6;
+
+		w.w_rtm.rtm_addrs |= RTA_NETMASK;
+		w_mask.sin6_family = AF_INET6;
+		if (plen2mask(prefix_length, AF_INET6, &m6) != 0) {
+			return;
+		}
+		w_mask.sin6_addr = ((struct sockaddr_in6 *)&m6)->sin6_addr;
+		(void) memmove(cp, &w_mask, sizeof (w_mask));
+		cp += ROUNDUP_LONG(sizeof (struct sockaddr_in6));
+		w.w_rtm.rtm_msglen +=
+		    ROUNDUP_LONG(sizeof (struct sockaddr_in6));
+	}
+	w_ifp.sdl_family = AF_LINK;
+	w.w_rtm.rtm_addrs |= RTA_IFP;
+	w_ifp.sdl_index = if_nametoindex(ifname);
+	(void) memmove(cp, &w_ifp, sizeof (w_ifp));
+	w.w_rtm.rtm_msglen += ROUNDUP_LONG(sizeof (struct sockaddr_dl));
+
+	cc = write(rtsock, &w, w.w_rtm.rtm_msglen);
+	if (cc < 0) {
+		if (errno == ESRCH && (action == RTM_CHANGE ||
+		    action == RTM_DELETE)) {
+			if (action == RTM_CHANGE) {
+				action = RTM_ADD;
+				goto again;
+			}
+			return;
+		}
+		return;
+	} else if (cc != w.w_rtm.rtm_msglen) {
+		return;
+	}
+}
+
+/*
+ * Return TRUE if running in a Solaris 10 Container.
+ */
+static boolean_t
+i_ipadm_zone_is_s10c(zoneid_t zoneid)
+{
+	char brand[MAXNAMELEN];
+
+	if (zone_getattr(zoneid, ZONE_ATTR_BRAND, brand, sizeof (brand)) < 0)
+		return (B_FALSE);
+	return (strcmp(brand, NATIVE_BRAND_NAME) != 0);
+}
+
+/*
+ * Configure addresses on link. `buf' is a string of comma-separated
+ * IP addresses.
+ */
+static ipadm_status_t
+i_ipadm_ngz_addr(ipadm_handle_t iph, char *link, char *buf)
+{
+	ipadm_status_t ipstatus;
+	ipadm_addrobj_t ipaddr;
+	char *cp;
+
+	for (cp = strtok(buf, ","); cp != NULL; cp = strtok(NULL, ",")) {
+		ipstatus = ipadm_create_addrobj(IPADM_ADDR_STATIC, link,
+		    &ipaddr);
+		if (ipstatus != IPADM_SUCCESS)
+			return (ipstatus);
+		/*
+		 * ipadm_set_addr does the appropriate name resolution and
+		 * sets up the ipadm_static_addr field.
+		 */
+		ipstatus = ipadm_set_addr(ipaddr, cp, AF_UNSPEC);
+		if (ipstatus != IPADM_SUCCESS) {
+			ipadm_destroy_addrobj(ipaddr);
+			return (ipstatus);
+		}
+
+		ipstatus = ipadm_create_addr(iph, ipaddr,
+		    (IPADM_OPT_ACTIVE | IPADM_OPT_UP));
+		if (ipstatus != IPADM_SUCCESS) {
+			ipadm_destroy_addrobj(ipaddr);
+			return (ipstatus);
+		}
+		ipadm_destroy_addrobj(ipaddr);
+	}
+	return (IPADM_SUCCESS);
+}
+
+/*
+ * The (*persist_if)() will set up persistent information for the interface,
+ * based on what interface families are required, so just resolve the
+ * address and inform the callback about the linkname, and required address
+ * families.
+ */
+static ipadm_status_t
+i_ipadm_ngz_persist_if(char *link, char *buf,
+    void (*ngz_persist_if)(char *, boolean_t, boolean_t))
+{
+	char *cp, *slashp, addr[INET6_ADDRSTRLEN];
+	ipadm_status_t ipstatus;
+	struct sockaddr_storage ss;
+	boolean_t v4 = B_FALSE;
+	boolean_t v6 = B_FALSE;
+
+	for (cp = strtok(buf, ","); cp != NULL; cp = strtok(NULL, ",")) {
+		/* remove the /<masklen> that's always added by zoneadmd */
+		slashp = strchr(cp, '/');
+		(void) strlcpy(addr, cp, (slashp - cp + 1));
+
+		/* resolve the address to find the family */
+		bzero(&ss, sizeof (ss));
+		ipstatus = i_ipadm_resolve_addr(addr, AF_UNSPEC, &ss);
+		if (ipstatus != IPADM_SUCCESS)
+			return (ipstatus);
+		switch (ss.ss_family) {
+		case AF_INET:
+			v4 = B_TRUE;
+			break;
+		case AF_INET6:
+			v6 = B_TRUE;
+			break;
+		default:
+			return (IPADM_BAD_ADDR);
+		}
+	}
+	(*ngz_persist_if)(link, v4, v6);
+	return (IPADM_SUCCESS);
+}
+
+static void
+i_ipadm_create_ngz_route(int rtsock, char *link, uint8_t *buf, size_t buflen)
+{
+	struct in6_addr defrouter;
+	boolean_t isv6;
+	struct in_addr gw4;
+	uint8_t *cp;
+	const in6_addr_t ipv6_all_zeros = { 0, 0, 0, 0 };
+
+	if (rtsock == -1)
+		return;
+
+	for (cp = buf; cp < buf + buflen; cp += sizeof (defrouter)) {
+		bcopy(cp, &defrouter, sizeof (defrouter));
+		if (IN6_IS_ADDR_UNSPECIFIED(&defrouter))
+			break;
+		isv6 = !IN6_IS_ADDR_V4MAPPED(&defrouter);
+		if (isv6) {
+			i_ipadm_rtioctl6(rtsock, RTM_ADD, ipv6_all_zeros,
+			    defrouter, 0, link, RTF_GATEWAY);
+		} else {
+			IN6_V4MAPPED_TO_INADDR(&defrouter, &gw4);
+			i_ipadm_rtioctl4(rtsock, RTM_ADD, INADDR_ANY,
+			    gw4.s_addr, 0, link, 0, RTF_GATEWAY);
+		}
+	}
+}
+
+/*
+ * Wrapper function to zone_getattr() for retrieving from-gz attributes that
+ * were made availabe for exclusive IP non-global zones by zoneadmd from teh
+ * global zone.
+ */
+static ipadm_status_t
+i_ipadm_zone_get_network(zoneid_t zoneid, datalink_id_t linkid, int type,
+    void *buf, size_t *bufsize)
+{
+	zone_net_data_t *zndata;
+
+	zndata = calloc(1, sizeof (*zndata) + *bufsize);
+	if (zndata == NULL)
+		return (IPADM_NO_MEMORY);
+	zndata->zn_type = type;
+	zndata->zn_linkid = linkid;
+	zndata->zn_len = *bufsize;
+
+	if (zone_getattr(zoneid, ZONE_ATTR_NETWORK, zndata,
+	    sizeof (*zndata) + *bufsize) < 0) {
+		return (ipadm_errno2status(errno));
+	}
+	*bufsize = zndata->zn_len;
+	bcopy(zndata->zn_val, buf, *bufsize);
+	return (IPADM_SUCCESS);
+}
+
+/*
+ * Callback function that configures a single datalink in a non-global zone.
+ */
+static int
+i_ipadm_zone_network_attr(dladm_handle_t dh, datalink_id_t linkid, void *arg)
+{
+	ngz_walk_data_t *nwd = arg;
+	zoneid_t zoneid = nwd->ngz_zoneid;
+	uint8_t buf[PIPE_BUF];
+	dladm_status_t dlstatus;
+	ipadm_status_t ipstatus;
+	char link[MAXLINKNAMELEN];
+	ipadm_handle_t iph = nwd->ngz_iph;
+	int rtsock = iph->iph_rtsock;
+	char *ifname = nwd->ngz_ifname;
+	boolean_t s10c = nwd->ngz_s10c;
+	boolean_t is_ipmgmtd = (iph->iph_flags & IPH_IPMGMTD);
+	size_t bufsize = sizeof (buf);
+
+	bzero(buf, bufsize);
+	ipstatus = i_ipadm_zone_get_network(zoneid, linkid,
+	    ZONE_NETWORK_ADDRESS, buf, &bufsize);
+	if (ipstatus != IPADM_SUCCESS)
+		goto fail;
+
+	dlstatus = dladm_datalink_id2info(dh, linkid, NULL, NULL,
+	    NULL, link, sizeof (link));
+	if (dlstatus != DLADM_STATUS_OK)
+		return (DLADM_WALK_CONTINUE);
+
+	/*
+	 * if ifname has been specified, then skip interfaces that don't match
+	 */
+	if (ifname != NULL && strcmp(ifname, link) != 0)
+		return (DLADM_WALK_CONTINUE);
+
+	/*
+	 * Plumb the interface and configure addresses on for S10 Containers.
+	 * We need to always do this for S10C because ipadm persistent
+	 * configuration is not available in S10C. For ipkg zones,
+	 * we skip the actual plumbing/configuration, but will call the
+	 * (*ngz_persist_if)() callback to create the persistent state for the
+	 * interface. The interface will be configured in ipkg zones when
+	 * ipadm_enable_if() is invoked to restore persistent configuration.
+	 */
+	if (is_ipmgmtd && !s10c) {
+		(void) i_ipadm_ngz_persist_if(link, (char *)buf,
+		    nwd->ngz_persist_if);
+		return (DLADM_WALK_CONTINUE);
+	}
+	ipstatus = i_ipadm_ngz_addr(iph, link, (char *)buf);
+	if (ipstatus != IPADM_SUCCESS)
+		goto fail;
+
+	/* apply any default router information.  */
+	bufsize = sizeof (buf);
+	bzero(buf, bufsize);
+	ipstatus = i_ipadm_zone_get_network(zoneid, linkid,
+	    ZONE_NETWORK_DEFROUTER, buf, &bufsize);
+	if (ipstatus != IPADM_SUCCESS)
+		goto fail;
+
+	i_ipadm_create_ngz_route(rtsock, link, buf, bufsize);
+
+	return (DLADM_WALK_CONTINUE);
+fail:
+	if (ifname != NULL) {
+		nwd->ngz_ipstatus = ipstatus;
+		return (DLADM_WALK_TERMINATE);
+	}
+	return (DLADM_WALK_CONTINUE);
+}
+
+/*
+ * ipmgmt_net_from_gz_init() initializes exclusive-IP stack non-global zones by
+ * extracting configuration that has been saved in the kernel and applying
+ * that information to the appropriate datalinks for the zone. If an ifname
+ * argument is passed in, only the selected IP interface corresponding to
+ * datalink will be initialized, otherwise all datalinks will be plumbed for IP
+ * and IP address and route information will be configured.
+ */
+ipadm_status_t
+ipadm_init_net_from_gz(ipadm_handle_t iph, char *ifname,
+	void (*persist_if)(char *, boolean_t, boolean_t))
+{
+	ngz_walk_data_t nwd;
+	uint64_t flags;
+	dladm_handle_t dlh = iph->iph_dlh;
+	datalink_id_t linkid;
+
+	if (iph->iph_zoneid == GLOBAL_ZONEID)
+		return (IPADM_NOTSUP);
+
+	if (ifname != NULL &&
+	    i_ipadm_get_flags(iph, ifname, AF_INET, &flags) != IPADM_SUCCESS &&
+	    i_ipadm_get_flags(iph, ifname, AF_INET6, &flags) != IPADM_SUCCESS)
+		return (IPADM_ENXIO);
+
+	if (ifname != NULL && !(flags & IFF_L3PROTECT))
+		return (IPADM_SUCCESS); /* nothing to initialize */
+
+	nwd.ngz_iph = iph;
+	nwd.ngz_zoneid = iph->iph_zoneid;
+	nwd.ngz_ifname = ifname;
+	nwd.ngz_persist_if = persist_if;
+	nwd.ngz_s10c = i_ipadm_zone_is_s10c(iph->iph_zoneid);
+	nwd.ngz_ipstatus = IPADM_SUCCESS;
+	if (ifname != NULL) {
+		if (dladm_name2info(dlh, ifname, &linkid, NULL, NULL,
+		    NULL) != DLADM_STATUS_OK) {
+			return (IPADM_ENXIO);
+		}
+		(void) i_ipadm_zone_network_attr(dlh, linkid, &nwd);
+	} else {
+		(void) dladm_walk_datalink_id(i_ipadm_zone_network_attr, dlh,
+		    &nwd, DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE,
+		    DLADM_OPT_PERSIST);
+	}
+	return (nwd.ngz_ipstatus);
+}
--- a/usr/src/lib/libipadm/common/libipadm.c	Thu Jul 01 13:48:34 2010 -0700
+++ b/usr/src/lib/libipadm/common/libipadm.c	Thu Jul 01 17:10:52 2010 -0400
@@ -97,7 +97,8 @@
 	{ IPADM_IPC_ERROR,	"Could not communicate with ipmgmtd" },
 	{ IPADM_NOTSUP,		"Operation not supported" },
 	{ IPADM_OP_DISABLE_OBJ, "Operation not supported on disabled object" },
-	{ IPADM_EBADE,		"Invalid data exchange with daemon" }
+	{ IPADM_EBADE,		"Invalid data exchange with daemon" },
+	{ IPADM_GZ_PERM,	"Operation not permitted on from-gz interface"}
 };
 
 #define	IPADM_NUM_ERRORS	(sizeof (ipadm_errors) / sizeof (*ipadm_errors))
@@ -186,7 +187,7 @@
 		return (IPADM_INVALID_ARG);
 	*handle = NULL;
 
-	if (flags & ~(IPH_VRRP|IPH_LEGACY|IPH_INIT))
+	if (flags & ~(IPH_VRRP|IPH_LEGACY|IPH_INIT|IPH_IPMGMTD))
 		return (IPADM_INVALID_ARG);
 
 	if ((iph = calloc(1, sizeof (struct ipadm_handle))) == NULL)
@@ -194,6 +195,7 @@
 	iph->iph_sock = -1;
 	iph->iph_sock6 = -1;
 	iph->iph_door_fd = -1;
+	iph->iph_rtsock = -1;
 	iph->iph_flags = flags;
 	(void) pthread_mutex_init(&iph->iph_lock, NULL);
 
@@ -213,6 +215,7 @@
 	 * dladm_open() for such zones.
 	 */
 	zoneid = getzoneid();
+	iph->iph_zoneid = zoneid;
 	if (zoneid != GLOBAL_ZONEID) {
 		if (zone_getattr(zoneid, ZONE_ATTR_FLAGS, &zflags,
 		    sizeof (zflags)) < 0) {
@@ -224,6 +227,14 @@
 			ipadm_close(iph);
 			return (IPADM_DLADM_FAILURE);
 		}
+		if (zoneid != GLOBAL_ZONEID) {
+			iph->iph_rtsock = socket(PF_ROUTE, SOCK_RAW, 0);
+			/*
+			 * Failure to open rtsock is ignored as this is
+			 * only used in non-global zones to initialize
+			 * routing socket information.
+			 */
+		}
 	} else {
 		assert(zoneid != GLOBAL_ZONEID);
 		iph->iph_dlh = NULL;
@@ -256,6 +267,8 @@
 		(void) close(iph->iph_sock);
 	if (iph->iph_sock6 != -1)
 		(void) close(iph->iph_sock6);
+	if (iph->iph_rtsock != -1)
+		(void) close(iph->iph_rtsock);
 	if (iph->iph_door_fd != -1)
 		(void) close(iph->iph_door_fd);
 	dladm_close(iph->iph_dlh);
@@ -494,7 +507,7 @@
 	datalink_id_t		linkid;
 
 	if (iph->iph_dlh == NULL) {
-		assert(getzoneid() != GLOBAL_ZONEID);
+		assert(iph->iph_zoneid != GLOBAL_ZONEID);
 		return (B_FALSE);
 	}
 	dlstatus = dladm_name2info(iph->iph_dlh, ifname, &linkid, NULL,
@@ -682,6 +695,8 @@
 	ipadm_status_t	ret_status = IPADM_SUCCESS;
 	char		newifname[LIFNAMSIZ];
 	char		*aobjstr;
+	sa_family_t	af = AF_UNSPEC;
+	boolean_t	is_ngz = (iph->iph_zoneid != GLOBAL_ZONEID);
 
 	(void) strlcpy(newifname, ifname, sizeof (newifname));
 	/*
@@ -705,6 +720,9 @@
 			 */
 			if (status == IPADM_IF_EXISTS)
 				status = IPADM_SUCCESS;
+
+			if (is_ngz)
+				af = atoi(afstr);
 		} else if (nvlist_lookup_string(nvl, IPADM_NVP_AOBJNAME,
 		    &aobjstr) == 0) {
 			/*
@@ -736,6 +754,9 @@
 		if (status != IPADM_SUCCESS)
 			return (status);
 	}
+
+	if (is_ngz && af != AF_UNSPEC)
+		ret_status = ipadm_init_net_from_gz(iph, newifname, NULL);
 	return (ret_status);
 }
 
--- a/usr/src/lib/libipadm/common/libipadm.h	Thu Jul 01 13:48:34 2010 -0700
+++ b/usr/src/lib/libipadm/common/libipadm.h	Thu Jul 01 17:10:52 2010 -0400
@@ -91,7 +91,8 @@
 	IPADM_IPC_ERROR,	/* Cannot communicate with ipmgmtd */
 	IPADM_OP_DISABLE_OBJ,	/* Operation on disable object */
 	IPADM_NOTSUP,		/* Operation not supported */
-	IPADM_EBADE		/* Invalid data exchange with ipmgmtd */
+	IPADM_EBADE,		/* Invalid data exchange with ipmgmtd */
+	IPADM_GZ_PERM		/* Operation not permitted on from-gz intf */
 } ipadm_status_t;
 
 /*
@@ -171,6 +172,7 @@
 /* ipadm_handle flags */
 #define	IPH_VRRP		0x00000001	/* Caller is VRRP */
 #define	IPH_LEGACY		0x00000002	/* Caller is legacy app */
+#define	IPH_IPMGMTD		0x00000004	/* Caller is ipmgmtd itself */
 
 /* opaque address object structure */
 typedef struct ipadm_addrobj_s	*ipadm_addrobj_t;
@@ -204,6 +206,7 @@
 #define	IFIF_NOACCEPT		0x00000100
 #define	IFIF_IPV4		0x00000200
 #define	IFIF_IPV6		0x00000400
+#define	IFIF_L3PROTECT		0x00000800
 
 /* ipadm_addr_info_t state */
 typedef enum {
--- a/usr/src/lib/libipadm/common/libipadm_impl.h	Thu Jul 01 13:48:34 2010 -0700
+++ b/usr/src/lib/libipadm/common/libipadm_impl.h	Thu Jul 01 17:10:52 2010 -0400
@@ -55,9 +55,11 @@
 	int		iph_sock;	/* socket to interface */
 	int		iph_sock6;	/* socket to interface */
 	int		iph_door_fd;	/* door descriptor to ipmgmtd */
+	int		iph_rtsock;	/* routing socket */
 	dladm_handle_t	iph_dlh;	/* handle to libdladm library */
 	uint32_t	iph_flags;	/* internal flags */
 	pthread_mutex_t	iph_lock;	/* lock to set door_fd */
+	zoneid_t	iph_zoneid;	/* zoneid where handle was opened */
 };
 
 /*
@@ -210,6 +212,8 @@
 			    const ipadm_addrobj_t, uint32_t);
 extern boolean_t	i_ipadm_name2atype(const char *, sa_family_t *,
 			    ipadm_addr_type_t *);
+extern ipadm_status_t	i_ipadm_resolve_addr(const char *, sa_family_t,
+			    struct sockaddr_storage *);
 
 /* ipadm_if.c */
 extern ipadm_status_t	i_ipadm_create_if(ipadm_handle_t, char *,  sa_family_t,
--- a/usr/src/lib/libipadm/common/mapfile-vers	Thu Jul 01 13:48:34 2010 -0700
+++ b/usr/src/lib/libipadm/common/mapfile-vers	Thu Jul 01 17:10:52 2010 -0400
@@ -67,6 +67,7 @@
 	ipadm_if_info;
 	ipadm_if_move;
 	ipadm_init_prop;
+	ipadm_init_net_from_gz;
 	ipadm_ndpd_read;
 	ipadm_ndpd_write;
 	ipadm_nvlist2str;
--- a/usr/src/lib/libzonecfg/common/libzonecfg.c	Thu Jul 01 13:48:34 2010 -0700
+++ b/usr/src/lib/libzonecfg/common/libzonecfg.c	Thu Jul 01 17:10:52 2010 -0400
@@ -99,6 +99,7 @@
 
 #define	DTD_ATTR_ACTION		(const xmlChar *) "action"
 #define	DTD_ATTR_ADDRESS	(const xmlChar *) "address"
+#define	DTD_ATTR_ALLOWED_ADDRESS	(const xmlChar *) "allowed-address"
 #define	DTD_ATTR_AUTOBOOT	(const xmlChar *) "autoboot"
 #define	DTD_ATTR_IPTYPE		(const xmlChar *) "ip-type"
 #define	DTD_ATTR_DEFROUTER	(const xmlChar *) "defrouter"
@@ -2090,6 +2091,8 @@
 	size_t addrspec;		/* nonzero if tabptr has IP addr */
 	size_t physspec;		/* nonzero if tabptr has interface */
 	size_t defrouterspec;		/* nonzero if tabptr has def. router */
+	size_t allowed_addrspec;
+	zone_iptype_t iptype;
 
 	if (tabptr == NULL)
 		return (Z_INVAL);
@@ -2104,12 +2107,18 @@
 	addrspec = strlen(tabptr->zone_nwif_address);
 	physspec = strlen(tabptr->zone_nwif_physical);
 	defrouterspec = strlen(tabptr->zone_nwif_defrouter);
-	if (addrspec == 0 && physspec == 0 && defrouterspec == 0)
+	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)
 		return (Z_INSUFFICIENT_SPEC);
 
 	if ((err = operation_prep(handle)) != Z_OK)
 		return (err);
 
+	if ((err = zonecfg_get_iptype(handle, &iptype)) != Z_OK)
+		return (err);
 	/*
 	 * Iterate over the configuration's elements and look for net elements
 	 * that match the query.
@@ -2129,11 +2138,18 @@
 		    physical, sizeof (physical)) != Z_OK ||
 		    strcmp(tabptr->zone_nwif_physical, physical) != 0))
 			continue;
-		if (addrspec != 0 && (fetchprop(cur, DTD_ATTR_ADDRESS, address,
+		if (iptype == ZS_SHARED && addrspec != 0 &&
+		    (fetchprop(cur, DTD_ATTR_ADDRESS, address,
 		    sizeof (address)) != Z_OK ||
 		    !zonecfg_same_net_address(tabptr->zone_nwif_address,
 		    address)))
 			continue;
+		if (iptype == ZS_EXCLUSIVE && allowed_addrspec != 0 &&
+		    (fetchprop(cur, DTD_ATTR_ALLOWED_ADDRESS, address,
+		    sizeof (address)) != Z_OK ||
+		    !zonecfg_same_net_address(tabptr->zone_nwif_allowed_address,
+		    address)))
+			continue;
 		if (defrouterspec != 0 && (fetchprop(cur, DTD_ATTR_DEFROUTER,
 		    address, sizeof (address)) != Z_OK ||
 		    !zonecfg_same_net_address(tabptr->zone_nwif_defrouter,
@@ -2158,10 +2174,17 @@
 	    sizeof (tabptr->zone_nwif_physical))) != Z_OK)
 		return (err);
 
-	if ((err = fetchprop(cur, DTD_ATTR_ADDRESS, tabptr->zone_nwif_address,
+	if (iptype == ZS_SHARED &&
+	    (err = fetchprop(cur, DTD_ATTR_ADDRESS, tabptr->zone_nwif_address,
 	    sizeof (tabptr->zone_nwif_address))) != 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)
+		return (err);
+
 	if ((err = fetchprop(cur, DTD_ATTR_DEFROUTER,
 	    tabptr->zone_nwif_defrouter,
 	    sizeof (tabptr->zone_nwif_defrouter))) != Z_OK)
@@ -2177,9 +2200,14 @@
 	int err;
 
 	newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_NET, NULL);
-	if ((err = newprop(newnode, DTD_ATTR_ADDRESS,
+	if (strlen(tabptr->zone_nwif_address) > 0 &&
+	    (err = newprop(newnode, DTD_ATTR_ADDRESS,
 	    tabptr->zone_nwif_address)) != Z_OK)
 		return (err);
+	if (strlen(tabptr->zone_nwif_allowed_address) > 0 &&
+	    (err = newprop(newnode, DTD_ATTR_ALLOWED_ADDRESS,
+	    tabptr->zone_nwif_allowed_address)) != Z_OK)
+		return (err);
 	if ((err = newprop(newnode, DTD_ATTR_PHYSICAL,
 	    tabptr->zone_nwif_physical)) != Z_OK)
 		return (err);
@@ -2215,7 +2243,7 @@
 zonecfg_delete_nwif_core(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
 {
 	xmlNodePtr cur = handle->zone_dh_cur;
-	boolean_t addr_match, phys_match;
+	boolean_t addr_match, phys_match, allowed_addr_match;
 
 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
 		if (xmlStrcmp(cur->name, DTD_ELEM_NET))
@@ -2223,10 +2251,12 @@
 
 		addr_match = match_prop(cur, DTD_ATTR_ADDRESS,
 		    tabptr->zone_nwif_address);
+		allowed_addr_match = match_prop(cur, DTD_ATTR_ALLOWED_ADDRESS,
+		    tabptr->zone_nwif_allowed_address);
 		phys_match = match_prop(cur, DTD_ATTR_PHYSICAL,
 		    tabptr->zone_nwif_physical);
 
-		if (addr_match && phys_match) {
+		if ((addr_match || allowed_addr_match) && phys_match) {
 			xmlUnlinkNode(cur);
 			xmlFreeNode(cur);
 			return (Z_OK);
@@ -4734,6 +4764,13 @@
 		return (err);
 	}
 
+	if ((err = fetchprop(cur, DTD_ATTR_ALLOWED_ADDRESS,
+	    tabptr->zone_nwif_allowed_address,
+	    sizeof (tabptr->zone_nwif_allowed_address))) != Z_OK) {
+		handle->zone_dh_cur = handle->zone_dh_top;
+		return (err);
+	}
+
 	if ((err = fetchprop(cur, DTD_ATTR_PHYSICAL, tabptr->zone_nwif_physical,
 	    sizeof (tabptr->zone_nwif_physical))) != Z_OK) {
 		handle->zone_dh_cur = handle->zone_dh_top;
--- a/usr/src/lib/libzonecfg/dtd/zonecfg.dtd.1	Thu Jul 01 13:48:34 2010 -0700
+++ b/usr/src/lib/libzonecfg/dtd/zonecfg.dtd.1	Thu Jul 01 17:10:52 2010 -0400
@@ -49,6 +49,7 @@
 <!ELEMENT network	EMPTY>
 
 <!ATTLIST network	address		CDATA ""
+			allowed-address	CDATA ""
 			defrouter	CDATA ""
 			physical	CDATA #REQUIRED>
 
--- a/usr/src/uts/common/inet/ip.h	Thu Jul 01 13:48:34 2010 -0700
+++ b/usr/src/uts/common/inet/ip.h	Thu Jul 01 17:10:52 2010 -0400
@@ -1801,6 +1801,8 @@
 	timeout_id_t	ill_refresh_tid; /* ill refresh retry timeout id */
 
 	uint32_t	ill_mrouter_cnt; /* mrouter allmulti joins */
+	uint32_t	ill_allowed_ips_cnt;
+	in6_addr_t	*ill_allowed_ips;
 } ill_t;
 
 /*
--- a/usr/src/uts/common/inet/ip/ip.c	Thu Jul 01 13:48:34 2010 -0700
+++ b/usr/src/uts/common/inet/ip/ip.c	Thu Jul 01 17:10:52 2010 -0400
@@ -8579,6 +8579,10 @@
 			ill_capability_reset(ill, B_TRUE);
 			ipsq_current_finish(ipsq);
 			break;
+
+		case DL_NOTE_ALLOWED_IPS:
+			ill_set_allowed_ips(ill, mp);
+			break;
 		default:
 			ip0dbg(("ip_rput_dlpi_writer: unknown notification "
 			    "type 0x%x for DL_NOTIFY_IND\n",
--- a/usr/src/uts/common/inet/ip/ip6_if.c	Thu Jul 01 13:48:34 2010 -0700
+++ b/usr/src/uts/common/inet/ip/ip6_if.c	Thu Jul 01 17:10:52 2010 -0400
@@ -19,10 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
- */
-/*
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 1990 Mentat Inc.
  */
 
@@ -2302,7 +2299,7 @@
 	    (DL_NOTE_PHYS_ADDR | DL_NOTE_SDU_SIZE | DL_NOTE_FASTPATH_FLUSH |
 	    DL_NOTE_LINK_UP | DL_NOTE_LINK_DOWN | DL_NOTE_CAPAB_RENEG |
 	    DL_NOTE_PROMISC_ON_PHYS | DL_NOTE_PROMISC_OFF_PHYS |
-	    DL_NOTE_REPLUMB);
+	    DL_NOTE_REPLUMB | DL_NOTE_ALLOWED_IPS);
 
 	phys_mp = ip_dlpi_alloc(sizeof (dl_phys_addr_req_t) +
 	    sizeof (t_scalar_t), DL_PHYS_ADDR_REQ);
--- a/usr/src/uts/common/inet/ip/ip_if.c	Thu Jul 01 13:48:34 2010 -0700
+++ b/usr/src/uts/common/inet/ip/ip_if.c	Thu Jul 01 17:10:52 2010 -0400
@@ -94,6 +94,7 @@
 #include <inet/ipclassifier.h>
 #include <sys/mac_client.h>
 #include <sys/dld.h>
+#include <sys/mac_flow.h>
 
 #include <sys/systeminfo.h>
 #include <sys/bootconf.h>
@@ -546,6 +547,15 @@
 		ill->ill_dld_capab = NULL;
 	}
 
+	/* Clean up ill_allowed_ips* related state */
+	if (ill->ill_allowed_ips != NULL) {
+		ASSERT(ill->ill_allowed_ips_cnt > 0);
+		kmem_free(ill->ill_allowed_ips,
+		    ill->ill_allowed_ips_cnt * sizeof (in6_addr_t));
+		ill->ill_allowed_ips = NULL;
+		ill->ill_allowed_ips_cnt = 0;
+	}
+
 	while (ill->ill_ipif != NULL)
 		ipif_free_tail(ill->ill_ipif);
 
@@ -2683,6 +2693,9 @@
 	if (IS_LOOPBACK(ill))
 		return (EINVAL);
 
+	if (enable && ill->ill_allowed_ips_cnt > 0)
+		return (EPERM);
+
 	if (IS_IPMP(ill) || IS_UNDER_IPMP(ill)) {
 		/*
 		 * Update all of the interfaces in the group.
@@ -9656,15 +9669,17 @@
 	int err = 0;
 	in6_addr_t v6addr;
 	boolean_t need_up = B_FALSE;
+	ill_t *ill;
+	int i;
 
 	ip1dbg(("ip_sioctl_addr(%s:%u %p)\n",
 	    ipif->ipif_ill->ill_name, ipif->ipif_id, (void *)ipif));
 
 	ASSERT(IAM_WRITER_IPIF(ipif));
 
+	ill = ipif->ipif_ill;
 	if (ipif->ipif_isv6) {
 		sin6_t *sin6;
-		ill_t *ill;
 		phyint_t *phyi;
 
 		if (sin->sin_family != AF_INET6)
@@ -9672,7 +9687,6 @@
 
 		sin6 = (sin6_t *)sin;
 		v6addr = sin6->sin6_addr;
-		ill = ipif->ipif_ill;
 		phyi = ill->ill_phyint;
 
 		/*
@@ -9731,7 +9745,21 @@
 
 		IN6_IPADDR_TO_V4MAPPED(addr, &v6addr);
 	}
-
+	/*
+	 * verify that the address being configured is permitted by the
+	 * ill_allowed_ips[] for the interface.
+	 */
+	if (ill->ill_allowed_ips_cnt > 0) {
+		for (i = 0; i < ill->ill_allowed_ips_cnt; i++) {
+			if (IN6_ARE_ADDR_EQUAL(&ill->ill_allowed_ips[i],
+			    &v6addr))
+				break;
+		}
+		if (i == ill->ill_allowed_ips_cnt) {
+			pr_addr_dbg("!allowed addr %s\n", AF_INET6, &v6addr);
+			return (EPERM);
+		}
+	}
 	/*
 	 * Even if there is no change we redo things just to rerun
 	 * ipif_set_default.
@@ -10373,8 +10401,11 @@
 	 * If ILLF_ROUTER changes, we need to change the ip forwarding
 	 * status of the interface.
 	 */
-	if ((turn_on | turn_off) & ILLF_ROUTER)
-		(void) ill_forward_set(ill, ((turn_on & ILLF_ROUTER) != 0));
+	if ((turn_on | turn_off) & ILLF_ROUTER) {
+		err = ill_forward_set(ill, ((turn_on & ILLF_ROUTER) != 0));
+		if (err != 0)
+			return (err);
+	}
 
 	/*
 	 * If the interface is not UP and we are not going to
@@ -17781,6 +17812,51 @@
 }
 
 /*
+ * When the allowed-ips link property is set on the datalink, IP receives a
+ * DL_NOTE_ALLOWED_IPS notification that is processed in ill_set_allowed_ips()
+ * to initialize the ill_allowed_ips[] array in the ill_t. This array is then
+ * used to vet addresses passed to ip_sioctl_addr() and to ensure that the
+ * only IP addresses configured on the ill_t are those in the ill_allowed_ips[]
+ * array.
+ */
+void
+ill_set_allowed_ips(ill_t *ill, mblk_t *mp)
+{
+	ipsq_t *ipsq = ill->ill_phyint->phyint_ipsq;
+	dl_notify_ind_t	*dlip = (dl_notify_ind_t *)mp->b_rptr;
+	mac_protect_t *mrp;
+	int i;
+
+	ASSERT(IAM_WRITER_IPSQ(ipsq));
+	mrp = (mac_protect_t *)&dlip[1];
+
+	if (mrp->mp_ipaddrcnt == 0) { /* reset allowed-ips */
+		kmem_free(ill->ill_allowed_ips,
+		    ill->ill_allowed_ips_cnt * sizeof (in6_addr_t));
+		ill->ill_allowed_ips_cnt = 0;
+		ill->ill_allowed_ips = NULL;
+		mutex_enter(&ill->ill_phyint->phyint_lock);
+		ill->ill_phyint->phyint_flags &= ~PHYI_L3PROTECT;
+		mutex_exit(&ill->ill_phyint->phyint_lock);
+		return;
+	}
+
+	if (ill->ill_allowed_ips != NULL) {
+		kmem_free(ill->ill_allowed_ips,
+		    ill->ill_allowed_ips_cnt * sizeof (in6_addr_t));
+	}
+	ill->ill_allowed_ips_cnt = mrp->mp_ipaddrcnt;
+	ill->ill_allowed_ips = kmem_alloc(
+	    ill->ill_allowed_ips_cnt * sizeof (in6_addr_t), KM_SLEEP);
+	for (i = 0; i < mrp->mp_ipaddrcnt;  i++)
+		ill->ill_allowed_ips[i] = mrp->mp_ipaddrs[i].ip_addr;
+
+	mutex_enter(&ill->ill_phyint->phyint_lock);
+	ill->ill_phyint->phyint_flags |= PHYI_L3PROTECT;
+	mutex_exit(&ill->ill_phyint->phyint_lock);
+}
+
+/*
  * Once the ill associated with `q' has quiesced, set its physical address
  * information to the values in `addrmp'.  Note that two copies of `addrmp'
  * are passed (linked by b_cont), since we sometimes need to save two distinct
--- a/usr/src/uts/common/inet/ip_if.h	Thu Jul 01 13:48:34 2010 -0700
+++ b/usr/src/uts/common/inet/ip_if.h	Thu Jul 01 17:10:52 2010 -0400
@@ -19,10 +19,9 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1990 Mentat Inc.
  */
-/* Copyright (c) 1990 Mentat Inc. */
 
 #ifndef	_INET_IP_IF_H
 #define	_INET_IP_IF_H
@@ -76,7 +75,7 @@
  */
 #define	IFF_PHYINT_FLAGS	(IFF_LOOPBACK|IFF_RUNNING|IFF_PROMISC| \
     IFF_ALLMULTI|IFF_INTELLIGENT|IFF_MULTI_BCAST|IFF_FAILED|IFF_STANDBY| \
-    IFF_INACTIVE|IFF_OFFLINE|IFF_VIRTUAL|IFF_IPMP)
+    IFF_INACTIVE|IFF_OFFLINE|IFF_VIRTUAL|IFF_IPMP|IFF_L3PROTECT)
 
 #define	IFF_PHYINTINST_FLAGS	(IFF_DEBUG|IFF_NOTRAILERS|IFF_NOARP| \
     IFF_MULTICAST|IFF_ROUTER|IFF_NONUD|IFF_NORTEXCH|IFF_IPV4|IFF_IPV6| \
@@ -99,6 +98,7 @@
 #define	PHYI_OFFLINE		IFF_OFFLINE	/* NIC has been offlined */
 #define	PHYI_VIRTUAL		IFF_VIRTUAL	/* Will not send or recv pkts */
 #define	PHYI_IPMP		IFF_IPMP	/* IPMP meta-interface */
+#define	PHYI_L3PROTECT		IFF_L3PROTECT	/* Layer-3 protected */
 
 #define	ILLF_DEBUG		IFF_DEBUG	/* turn on debugging */
 #define	ILLF_NOTRAILERS		IFF_NOTRAILERS	/* avoid use of trailers */
@@ -195,6 +195,7 @@
 extern	void	ill_set_inputfn(ill_t *);
 extern	void	ill_set_inputfn_all(ip_stack_t *);
 extern	int	ill_set_phys_addr(ill_t *, mblk_t *);
+extern	void	ill_set_allowed_ips(ill_t *, mblk_t *);
 extern	int	ill_replumb(ill_t *, mblk_t *);
 extern	void	ill_set_ndmp(ill_t *, mblk_t *, uint_t, uint_t);
 
--- a/usr/src/uts/common/io/dld/dld_proto.c	Thu Jul 01 13:48:34 2010 -0700
+++ b/usr/src/uts/common/io/dld/dld_proto.c	Thu Jul 01 17:10:52 2010 -0400
@@ -1127,7 +1127,8 @@
 	    DL_NOTE_CAPAB_RENEG |
 	    DL_NOTE_FASTPATH_FLUSH |
 	    DL_NOTE_SPEED |
-	    DL_NOTE_SDU_SIZE;
+	    DL_NOTE_SDU_SIZE|
+	    DL_NOTE_ALLOWED_IPS;
 
 	if (MBLKL(mp) < sizeof (dl_notify_req_t)) {
 		dl_err = DL_BADPRIM;
--- a/usr/src/uts/common/io/dld/dld_str.c	Thu Jul 01 13:48:34 2010 -0700
+++ b/usr/src/uts/common/io/dld/dld_str.c	Thu Jul 01 17:10:52 2010 -0400
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
@@ -38,6 +37,7 @@
 #include	<sys/list.h>
 #include	<sys/mac_client.h>
 #include	<sys/mac_client_priv.h>
+#include	<sys/mac_flow.h>
 
 static int	str_constructor(void *, void *, int);
 static void	str_destructor(void *, void *);
@@ -1719,6 +1719,35 @@
 	qreply(dsp->ds_wq, mp);
 }
 
+static void
+str_notify_allowed_ips(dld_str_t *dsp)
+{
+	mblk_t		*mp;
+	dl_notify_ind_t	*dlip;
+	size_t		mp_size;
+	mac_protect_t	mrp;
+
+	if (!(dsp->ds_notifications & DL_NOTE_ALLOWED_IPS))
+		return;
+
+	mp_size = sizeof (mac_protect_t) + sizeof (dl_notify_ind_t);
+	if ((mp = mexchange(dsp->ds_wq, NULL, mp_size, M_PROTO, 0)) == NULL)
+		return;
+
+	mac_protect_get(dsp->ds_mh, &mrp);
+	bzero(mp->b_rptr, mp_size);
+	dlip = (dl_notify_ind_t *)mp->b_rptr;
+	dlip->dl_primitive = DL_NOTIFY_IND;
+	dlip->dl_notification = DL_NOTE_ALLOWED_IPS;
+	dlip->dl_data = 0;
+	dlip->dl_addr_offset = sizeof (dl_notify_ind_t);
+	dlip->dl_addr_length = sizeof (mac_protect_t);
+	bcopy(&mrp, mp->b_rptr + sizeof (dl_notify_ind_t),
+	    sizeof (mac_protect_t));
+
+	qreply(dsp->ds_wq, mp);
+}
+
 /*
  * MAC notification callback.
  */
@@ -1849,6 +1878,10 @@
 	case MAC_NOTE_MARGIN:
 		break;
 
+	case MAC_NOTE_ALLOWED_IPS:
+		str_notify_allowed_ips(dsp);
+		break;
+
 	default:
 		ASSERT(B_FALSE);
 		break;
--- a/usr/src/uts/common/io/mac/mac_protect.c	Thu Jul 01 13:48:34 2010 -0700
+++ b/usr/src/uts/common/io/mac/mac_protect.c	Thu Jul 01 17:10:52 2010 -0400
@@ -20,8 +20,7 @@
  */
 
 /*
- * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #include <sys/strsun.h>
@@ -194,6 +193,7 @@
 } dhcpv6_txn_t;
 
 static void	start_txn_cleanup_timer(mac_client_impl_t *);
+static boolean_t allowed_ips_set(mac_resource_props_t *, uint32_t);
 
 #define	BUMP_STAT(m, s)	(m)->mci_misc_stat.mms_##s++
 
@@ -552,29 +552,34 @@
 /*
  * Core logic for intercepting outbound DHCPv4 packets.
  */
-static void
+static boolean_t
 intercept_dhcpv4_outbound(mac_client_impl_t *mcip, ipha_t *ipha, uchar_t *end)
 {
-	struct dhcp	*dh4;
-	uchar_t		*opt;
-	dhcpv4_txn_t	*txn, *ctxn;
-	ipaddr_t	ipaddr;
-	uint8_t		opt_len, mtype, cid[DHCP_MAX_OPT_SIZE], cid_len;
+	struct dhcp		*dh4;
+	uchar_t			*opt;
+	dhcpv4_txn_t		*txn, *ctxn;
+	ipaddr_t		ipaddr;
+	uint8_t			opt_len, mtype, cid[DHCP_MAX_OPT_SIZE], cid_len;
+	mac_resource_props_t	*mrp = MCIP_RESOURCE_PROPS(mcip);
 
 	if (get_dhcpv4_info(ipha, end, &dh4) != 0)
-		return;
+		return (B_TRUE);
+
+	/* ip_nospoof/allowed-ips and DHCP are mutually exclusive by default */
+	if (allowed_ips_set(mrp, IPV4_VERSION))
+		return (B_FALSE);
 
 	if (get_dhcpv4_option(dh4, end, CD_DHCP_TYPE, &opt, &opt_len) != 0 ||
 	    opt_len != 1) {
 		DTRACE_PROBE2(mtype__not__found, mac_client_impl_t *, mcip,
 		    struct dhcp *, dh4);
-		return;
+		return (B_TRUE);
 	}
 	mtype = *opt;
 	if (mtype != REQUEST && mtype != RELEASE) {
 		DTRACE_PROBE3(ignored__mtype, mac_client_impl_t *, mcip,
 		    struct dhcp *, dh4, uint8_t, mtype);
-		return;
+		return (B_TRUE);
 	}
 
 	/* client ID is optional for IPv4 */
@@ -639,6 +644,7 @@
 
 done:
 	mutex_exit(&mcip->mci_protect_lock);
+	return (B_TRUE);
 }
 
 /*
@@ -1208,7 +1214,7 @@
 /*
  * Core logic for intercepting outbound DHCPv6 packets.
  */
-static void
+static boolean_t
 intercept_dhcpv6_outbound(mac_client_impl_t *mcip, ip6_t *ip6h, uchar_t *end)
 {
 	dhcpv6_message_t	*dh6;
@@ -1216,17 +1222,22 @@
 	dhcpv6_cid_t		*cid = NULL;
 	uint32_t		xid;
 	uint8_t			mtype;
+	mac_resource_props_t *mrp = MCIP_RESOURCE_PROPS(mcip);
 
 	if (get_dhcpv6_info(ip6h, end, &dh6) != 0)
-		return;
+		return (B_TRUE);
+
+	/* ip_nospoof/allowed-ips and DHCP are mutually exclusive by default */
+	if (allowed_ips_set(mrp, IPV6_VERSION))
+		return (B_FALSE);
 
 	mtype = dh6->d6m_msg_type;
 	if (mtype != DHCPV6_MSG_REQUEST && mtype != DHCPV6_MSG_RENEW &&
 	    mtype != DHCPV6_MSG_REBIND && mtype != DHCPV6_MSG_RELEASE)
-		return;
+		return (B_TRUE);
 
 	if ((cid = create_dhcpv6_cid(dh6, end)) == NULL)
-		return;
+		return (B_TRUE);
 
 	mutex_enter(&mcip->mci_protect_lock);
 	if (mtype == DHCPV6_MSG_RELEASE) {
@@ -1260,6 +1271,7 @@
 		free_dhcpv6_cid(cid);
 
 	mutex_exit(&mcip->mci_protect_lock);
+	return (B_TRUE);
 }
 
 /*
@@ -1524,7 +1536,8 @@
 		    V4_PART_OF_V6(v4addr->ip_addr) == *addr)
 			return (B_TRUE);
 	}
-	return (check_dhcpv4_dyn_ip(mcip, *addr));
+	return (protect->mp_ipaddrcnt == 0 ?
+	    check_dhcpv4_dyn_ip(mcip, *addr) : B_FALSE);
 }
 
 static boolean_t
@@ -1549,7 +1562,8 @@
 		    IN6_ARE_ADDR_EQUAL(&v6addr->ip_addr, addr))
 			return (B_TRUE);
 	}
-	return (check_dhcpv6_dyn_ip(mcip, addr));
+	return (protect->mp_ipaddrcnt == 0 ?
+	    check_dhcpv6_dyn_ip(mcip, addr) : B_FALSE);
 }
 
 /*
@@ -1694,7 +1708,8 @@
 		if (!ipnospoof_check_v4(mcip, protect, &ipha->ipha_src))
 			goto fail;
 
-		intercept_dhcpv4_outbound(mcip, ipha, end);
+		if (!intercept_dhcpv4_outbound(mcip, ipha, end))
+			goto fail;
 		break;
 	}
 	case ETHERTYPE_ARP: {
@@ -1739,7 +1754,8 @@
 		if (!ipnospoof_check_ndp(mcip, protect, ip6h, end))
 			goto fail;
 
-		intercept_dhcpv6_outbound(mcip, ip6h, end);
+		if (!intercept_dhcpv6_outbound(mcip, ip6h, end))
+			goto fail;
 		break;
 	}
 	}
@@ -2187,7 +2203,12 @@
 	if ((err = mac_protect_validate(mrp)) != 0)
 		return (err);
 
+	if (err != 0)
+		return (err);
+
 	mac_update_resources(mrp, MCIP_RESOURCE_PROPS(mcip), B_FALSE);
+	i_mac_notify(((mcip->mci_state_flags & MCIS_IS_VNIC) != 0 ?
+	    mcip->mci_upper_mip : mip), MAC_NOTE_ALLOWED_IPS);
 	return (0);
 }
 
@@ -2261,3 +2282,23 @@
 	mcip->mci_protect_flags = 0;
 	mutex_destroy(&mcip->mci_protect_lock);
 }
+
+static boolean_t
+allowed_ips_set(mac_resource_props_t *mrp, uint32_t af)
+{
+	int i;
+
+	for (i = 0; i < mrp->mrp_protect.mp_ipaddrcnt; i++) {
+		if (mrp->mrp_protect.mp_ipaddrs[i].ip_version == af)
+			return (B_TRUE);
+	}
+	return (B_FALSE);
+}
+
+void
+mac_protect_get(mac_handle_t mh, mac_protect_t *mrp)
+{
+	mac_impl_t *mip = (mac_impl_t *)mh;
+
+	*mrp = mip->mi_resource_props.mrp_protect;
+}
--- a/usr/src/uts/common/net/if.h	Thu Jul 01 13:48:34 2010 -0700
+++ b/usr/src/uts/common/net/if.h	Thu Jul 01 17:10:52 2010 -0400
@@ -1,6 +1,5 @@
 /*
- * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
@@ -171,6 +170,7 @@
 #define	IFF_VRRP	0x10000000000ll	/* Managed by VRRP */
 
 #define	IFF_NOLINKLOCAL	0x20000000000ll	/* No default linklocal */
+#define	IFF_L3PROTECT	0x40000000000ll	/* Layer-3 protection enforced */
 
 /* flags that cannot be changed by userland on any interface */
 #define	IFF_CANTCHANGE \
@@ -178,7 +178,7 @@
 	IFF_MULTICAST | IFF_MULTI_BCAST | IFF_UNNUMBERED | IFF_IPV4 | \
 	IFF_IPV6 | IFF_IPMP | IFF_FIXEDMTU | IFF_VIRTUAL | \
 	IFF_LOOPBACK | IFF_ALLMULTI | IFF_DUPLICATE | IFF_COS_ENABLED | \
-	IFF_VRRP | IFF_NOLINKLOCAL)
+	IFF_VRRP | IFF_NOLINKLOCAL | IFF_L3PROTECT)
 
 /* flags that cannot be changed by userland on an IPMP interface */
 #define	IFF_IPMP_CANTCHANGE 	IFF_FAILED
--- a/usr/src/uts/common/net/route.h	Thu Jul 01 13:48:34 2010 -0700
+++ b/usr/src/uts/common/net/route.h	Thu Jul 01 17:10:52 2010 -0400
@@ -1,6 +1,5 @@
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 /*
  * Copyright (c) 1980, 1986, 1993
@@ -132,6 +131,7 @@
 #define	RTF_SETSRC	0x20000		/* set default outgoing src address */
 #define	RTF_INDIRECT	0x40000		/* gateway not directly reachable */
 #define	RTF_KERNEL	0x80000		/* created by kernel; can't delete */
+#define	RTF_ZONE	0x100000	/* (NGZ only) route from global zone */
 
 /*
  * OLD statistics not used by the kernel. The kernel uses <inet/mib2.h>.
--- a/usr/src/uts/common/os/zone.c	Thu Jul 01 13:48:34 2010 -0700
+++ b/usr/src/uts/common/os/zone.c	Thu Jul 01 17:10:52 2010 -0400
@@ -252,6 +252,7 @@
 /* List of data link IDs which are accessible from the zone */
 typedef struct zone_dl {
 	datalink_id_t	zdl_id;
+	nvlist_t	*zdl_net;
 	list_node_t	zdl_linkage;
 } zone_dl_t;
 
@@ -364,6 +365,8 @@
 static int zone_add_datalink(zoneid_t, datalink_id_t);
 static int zone_remove_datalink(zoneid_t, datalink_id_t);
 static int zone_list_datalink(zoneid_t, int *, datalink_id_t *);
+static int zone_set_network(zoneid_t, zone_net_data_t *);
+static int zone_get_network(zoneid_t, zone_net_data_t *);
 
 typedef boolean_t zsd_applyfn_t(kmutex_t *, boolean_t, zone_t *, zone_key_t);
 
@@ -4719,6 +4722,7 @@
 	boolean_t global = (curzone == global_zone);
 	boolean_t inzone = (curzone->zone_id == zoneid);
 	ushort_t flags;
+	zone_net_data_t *zbuf;
 
 	mutex_enter(&zonehash_lock);
 	if ((zone = zone_find_all_by_id(zoneid)) == NULL) {
@@ -4973,6 +4977,17 @@
 				error = EFAULT;
 		}
 		break;
+	case ZONE_ATTR_NETWORK:
+		zbuf = kmem_alloc(bufsize, KM_SLEEP);
+		if (copyin(buf, zbuf, bufsize) != 0) {
+			error = EFAULT;
+		} else {
+			error = zone_get_network(zoneid, zbuf);
+			if (error == 0 && copyout(zbuf, buf, bufsize) != 0)
+				error = EFAULT;
+		}
+		kmem_free(zbuf, bufsize);
+		break;
 	default:
 		if ((attr >= ZONE_ATTR_BRAND_ATTRS) && ZONE_IS_BRANDED(zone)) {
 			size = bufsize;
@@ -4998,6 +5013,7 @@
 	zone_t *zone;
 	zone_status_t zone_status;
 	int err;
+	zone_net_data_t *zbuf;
 
 	if (secpolicy_zone_config(CRED()) != 0)
 		return (set_errno(EPERM));
@@ -5055,6 +5071,19 @@
 			err = EINVAL;
 		}
 		break;
+	case ZONE_ATTR_NETWORK:
+		if (bufsize > (PIPE_BUF + sizeof (zone_net_data_t))) {
+			err = EINVAL;
+			goto done;
+		}
+		zbuf = kmem_alloc(bufsize, KM_SLEEP);
+		if (copyin(buf, zbuf, bufsize) != 0) {
+			err = EFAULT;
+			goto done;
+		}
+		err = zone_set_network(zoneid, zbuf);
+		kmem_free(zbuf, bufsize);
+		break;
 	default:
 		if ((attr >= ZONE_ATTR_BRAND_ATTRS) && ZONE_IS_BRANDED(zone))
 			err = ZBROP(zone)->b_setattr(zone, attr, buf, bufsize);
@@ -6328,6 +6357,7 @@
 
 	zdl = kmem_zalloc(sizeof (*zdl), KM_SLEEP);
 	zdl->zdl_id = linkid;
+	zdl->zdl_net = NULL;
 	mutex_enter(&thiszone->zone_lock);
 	list_insert_head(&thiszone->zone_dl_list, zdl);
 	mutex_exit(&thiszone->zone_lock);
@@ -6351,6 +6381,8 @@
 		err = ENXIO;
 	} else {
 		list_remove(&zone->zone_dl_list, zdl);
+		if (zdl->zdl_net != NULL)
+			nvlist_free(zdl->zdl_net);
 		kmem_free(zdl, sizeof (zone_dl_t));
 	}
 	mutex_exit(&zone->zone_lock);
@@ -6524,3 +6556,125 @@
 	kmem_free(idarray, sizeof (datalink_id_t) * idcount);
 	return (ret);
 }
+
+static char *
+zone_net_type2name(int type)
+{
+	switch (type) {
+	case ZONE_NETWORK_ADDRESS:
+		return (ZONE_NET_ADDRNAME);
+	case ZONE_NETWORK_DEFROUTER:
+		return (ZONE_NET_RTRNAME);
+	default:
+		return (NULL);
+	}
+}
+
+static int
+zone_set_network(zoneid_t zoneid, zone_net_data_t *znbuf)
+{
+	zone_t *zone;
+	zone_dl_t *zdl;
+	nvlist_t *nvl;
+	int err = 0;
+	uint8_t *new = NULL;
+	char *nvname;
+	int bufsize;
+	datalink_id_t linkid = znbuf->zn_linkid;
+
+	if (secpolicy_zone_config(CRED()) != 0)
+		return (set_errno(EPERM));
+
+	if (zoneid == GLOBAL_ZONEID)
+		return (set_errno(EINVAL));
+
+	nvname = zone_net_type2name(znbuf->zn_type);
+	bufsize = znbuf->zn_len;
+	new = znbuf->zn_val;
+	if (nvname == NULL)
+		return (set_errno(EINVAL));
+
+	if ((zone = zone_find_by_id(zoneid)) == NULL) {
+		return (set_errno(EINVAL));
+	}
+
+	mutex_enter(&zone->zone_lock);
+	if ((zdl = zone_find_dl(zone, linkid)) == NULL) {
+		err = ENXIO;
+		goto done;
+	}
+	if ((nvl = zdl->zdl_net) == NULL) {
+		if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP)) {
+			err = ENOMEM;
+			goto done;
+		} else {
+			zdl->zdl_net = nvl;
+		}
+	}
+	if (nvlist_exists(nvl, nvname)) {
+		err = EINVAL;
+		goto done;
+	}
+	err = nvlist_add_uint8_array(nvl, nvname, new, bufsize);
+	ASSERT(err == 0);
+done:
+	mutex_exit(&zone->zone_lock);
+	zone_rele(zone);
+	if (err != 0)
+		return (set_errno(err));
+	else
+		return (0);
+}
+
+static int
+zone_get_network(zoneid_t zoneid, zone_net_data_t *znbuf)
+{
+	zone_t *zone;
+	zone_dl_t *zdl;
+	nvlist_t *nvl;
+	uint8_t *ptr;
+	uint_t psize;
+	int err = 0;
+	char *nvname;
+	int bufsize;
+	void *buf;
+	datalink_id_t linkid = znbuf->zn_linkid;
+
+	if (zoneid == GLOBAL_ZONEID)
+		return (set_errno(EINVAL));
+
+	nvname = zone_net_type2name(znbuf->zn_type);
+	bufsize = znbuf->zn_len;
+	buf = znbuf->zn_val;
+
+	if (nvname == NULL)
+		return (set_errno(EINVAL));
+	if ((zone = zone_find_by_id(zoneid)) == NULL)
+		return (set_errno(EINVAL));
+
+	mutex_enter(&zone->zone_lock);
+	if ((zdl = zone_find_dl(zone, linkid)) == NULL) {
+		err = ENXIO;
+		goto done;
+	}
+	if ((nvl = zdl->zdl_net) == NULL || !nvlist_exists(nvl, nvname)) {
+		err = ENOENT;
+		goto done;
+	}
+	err = nvlist_lookup_uint8_array(nvl, nvname, &ptr, &psize);
+	ASSERT(err == 0);
+
+	if (psize > bufsize) {
+		err = ENOBUFS;
+		goto done;
+	}
+	znbuf->zn_len = psize;
+	bcopy(ptr, buf, psize);
+done:
+	mutex_exit(&zone->zone_lock);
+	zone_rele(zone);
+	if (err != 0)
+		return (set_errno(err));
+	else
+		return (0);
+}
--- a/usr/src/uts/common/sys/dlpi.h	Thu Jul 01 13:48:34 2010 -0700
+++ b/usr/src/uts/common/sys/dlpi.h	Thu Jul 01 17:10:52 2010 -0400
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
@@ -407,6 +406,7 @@
 #define	DL_NOTE_FASTPATH_FLUSH	0x0200	/* Fast Path info changes */
 #define	DL_NOTE_CAPAB_RENEG	0x0400	/* Initiate capability renegotiation */
 #define	DL_NOTE_REPLUMB		0x0800	/* Inform the link to replumb */
+#define	DL_NOTE_ALLOWED_IPS	0x1000	/* "allowed-ips"  notification */
 
 /*
  * DLPI notification codes for DL_NOTIFY_CONF primitives.
--- a/usr/src/uts/common/sys/mac.h	Thu Jul 01 13:48:34 2010 -0700
+++ b/usr/src/uts/common/sys/mac.h	Thu Jul 01 17:10:52 2010 -0400
@@ -349,6 +349,7 @@
 	MAC_NOTE_MARGIN,
 	MAC_NOTE_CAPAB_CHG,
 	MAC_NOTE_LOWLINK,
+	MAC_NOTE_ALLOWED_IPS,
 	MAC_NNOTE	/* must be the last entry */
 } mac_notify_type_t;
 
--- a/usr/src/uts/common/sys/mac_provider.h	Thu Jul 01 13:48:34 2010 -0700
+++ b/usr/src/uts/common/sys/mac_provider.h	Thu Jul 01 17:10:52 2010 -0400
@@ -20,8 +20,7 @@
  */
 
 /*
- * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #ifndef	_SYS_MAC_PROVIDER_H
@@ -33,6 +32,7 @@
 #include <sys/stream.h>
 #include <sys/mkdev.h>
 #include <sys/mac.h>
+#include <sys/mac_flow.h>
 
 /*
  * MAC Provider Interface
@@ -455,6 +455,7 @@
 /*
  * Driver interface functions.
  */
+extern void			mac_protect_get(mac_handle_t, mac_protect_t *);
 extern void			mac_sdu_get(mac_handle_t, uint_t *, uint_t *);
 extern int			mac_maxsdu_update(mac_handle_t, uint_t);
 
--- a/usr/src/uts/common/sys/zone.h	Thu Jul 01 13:48:34 2010 -0700
+++ b/usr/src/uts/common/sys/zone.h	Thu Jul 01 17:10:52 2010 -0400
@@ -36,6 +36,8 @@
 #include <sys/netstack.h>
 #include <sys/uadmin.h>
 #include <sys/ksynch.h>
+#include <sys/socket_impl.h>
+#include <netinet/in.h>
 
 #ifdef	__cplusplus
 extern "C" {
@@ -97,6 +99,7 @@
 #define	ZONE_ATTR_FLAGS		14
 #define	ZONE_ATTR_HOSTID	15
 #define	ZONE_ATTR_FS_ALLOWED	16
+#define	ZONE_ATTR_NETWORK	17
 
 /* Start of the brand-specific attribute namespace */
 #define	ZONE_ATTR_BRAND_ATTRS	32768
@@ -279,6 +282,21 @@
 /* zone_create flags */
 #define	ZCF_NET_EXCL		0x1	/* Create a zone with exclusive IP */
 
+/* zone network properties */
+#define	ZONE_NETWORK_ADDRESS	1
+#define	ZONE_NETWORK_DEFROUTER	2
+
+#define	ZONE_NET_ADDRNAME	"address"
+#define	ZONE_NET_RTRNAME	"route"
+
+typedef struct zone_net_data {
+	int zn_type;
+	int zn_len;
+	datalink_id_t zn_linkid;
+	uint8_t zn_val[1];
+} zone_net_data_t;
+
+
 #ifdef _KERNEL
 /*
  * We need to protect the definition of 'list_t' from userland applications and