--- old/usr/src/cmd/cmd-inet/etc/dhcp/inittab	Mon Nov 14 16:21:12 2016
+++ new/usr/src/cmd/cmd-inet/etc/dhcp/inittab	Mon Nov 14 16:21:12 2016
@@ -1,6 +1,7 @@
 #
 # Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
+# Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
 #
 # CDDL HEADER START
 #
@@ -114,6 +115,7 @@
 UserClas	STANDARD,	77,	ASCII,	   1,	0,	sdi
 SLP_DA		STANDARD,	78,	OCTET,	   1,	0,	sdmi
 SLP_SS		STANDARD,	79,	OCTET,	   1,	0,	sdmi
+ClientFQDN	STANDARD,	81,	OCTET,	   1,	0,	sdmi
 AgentOpt	STANDARD,	82,	OCTET,	   1,	0,	sdi
 FQDN		STANDARD,	89,	OCTET,	   1,	0,	sdmi
 
@@ -177,7 +179,7 @@
 # The following option describes an option named ipPairs, that is in
 # the  SITE category, meaning it is defined by each individual site.
 # It is option code 132, which is of type IP Address, consisting of
-# a potentially infinite number of pairs of IP addresses.  (See 
+# a potentially infinite number of pairs of IP addresses.  (See
 # dhcp_inittab(4) for details)
 #
 # ipPairs     SITE,           132,    IP,     2,      0,      sdmi
--- old/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_door.c	Mon Nov 14 16:21:12 2016
+++ new/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_door.c	Mon Nov 14 16:21:12 2016
@@ -21,6 +21,7 @@
 
 /*
  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
  */
 
 /*
@@ -225,8 +226,6 @@
 	char			*aobjname = NULL, *ifname = NULL;
 	int32_t			lnum;
 	nvlist_t		*nvladdr;
-	struct sockaddr_storage	addr;
-	uint_t			n;
 	sa_family_t		af = AF_UNSPEC;
 	ipadm_addr_type_t	addrtype = IPADM_ADDR_NONE;
 	int			err = 0;
@@ -244,16 +243,42 @@
 	if (nvlist_exists(nvl, IPADM_NVP_IPV4ADDR)) {
 		af = AF_INET;
 		addrtype = IPADM_ADDR_STATIC;
-	} else if (nvlist_exists(nvl, IPADM_NVP_DHCP)) {
+	} else if (nvlist_lookup_nvlist(nvl, IPADM_NVP_DHCP, &nvladdr) == 0) {
+		char	*reqhost;
+		boolean_t	primary;
+
 		af = AF_INET;
 		addrtype = IPADM_ADDR_DHCP;
+
+		if (nvlist_lookup_boolean_value(nvladdr, IPADM_NVP_PRIMARY,
+		    &primary) != 0)
+			return (EINVAL);
+		nodep->ipmgmt_am_primary = primary;
+
+		/*
+		 * ipmgmt_am_reqhost comes through in `nvl' for purposes of updating
+		 * the cached representation, but it is persisted as a stand-alone
+		 * DB line; so remove it after copying it.
+		 */
+		if (!nvlist_exists(nvl, IPADM_NVP_REQHOST)) {
+			*nodep->ipmgmt_am_reqhost = '\0';
+		} else {
+			if ((err = nvlist_lookup_string(nvl, IPADM_NVP_REQHOST,
+			    &reqhost)) != 0)
+				return (err);
+
+			(void) strlcpy(nodep->ipmgmt_am_reqhost, reqhost,
+			    sizeof (nodep->ipmgmt_am_reqhost));
+			(void) nvlist_remove(nvl, IPADM_NVP_REQHOST, DATA_TYPE_STRING);
+		}
 	} else if (nvlist_exists(nvl, IPADM_NVP_IPV6ADDR)) {
 		af = AF_INET6;
 		addrtype = IPADM_ADDR_STATIC;
 	} else if (nvlist_lookup_nvlist(nvl, IPADM_NVP_INTFID, &nvladdr) == 0) {
-		struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&addr;
+		struct sockaddr_in6		sin6 = {0};
 		uint8_t	*addr6;
 		uint32_t plen;
+		uint_t n;
 
 		af = AF_INET6;
 		addrtype = IPADM_ADDR_IPV6_ADDRCONF;
@@ -264,14 +289,15 @@
 			if (nvlist_lookup_uint8_array(nvladdr,
 			    IPADM_NVP_IPNUMADDR, &addr6, &n) != 0)
 				return (EINVAL);
-			bcopy(addr6, &sin6->sin6_addr, n);
-		} else {
-			bzero(&sin6->sin6_addr, sizeof (sin6->sin6_addr));
+			bcopy(addr6, &sin6.sin6_addr, n);
 		}
+
+		nodep->ipmgmt_am_linklocal = B_TRUE;
+		nodep->ipmgmt_am_ifid = sin6;
 	}
 
 	/*
-	 * populate the `*nodep' with retrieved values.
+	 * populate the non-addrtype-specific `*nodep' with retrieved values.
 	 */
 	(void) strlcpy(nodep->am_ifname, ifname, sizeof (nodep->am_ifname));
 	(void) strlcpy(nodep->am_aobjname, aobjname,
@@ -279,10 +305,6 @@
 	nodep->am_lnum = lnum;
 	nodep->am_family = af;
 	nodep->am_atype = addrtype;
-	if (addrtype == IPADM_ADDR_IPV6_ADDRCONF) {
-		nodep->am_linklocal = B_TRUE;
-		nodep->am_ifid = addr;
-	}
 	nodep->am_next = NULL;
 
 	/*
@@ -297,8 +319,8 @@
 
 /*
  * Handles the door command IPMGMT_CMD_SETADDR. It adds a new address object
- * node to the list `aobjmap' and then persists the address information in the
- * DB.
+ * node to the list `aobjmap' and optionally persists the address
+ * information in the DB.
  */
 static void
 ipmgmt_setaddr_handler(void *argp)
@@ -305,7 +327,7 @@
 {
 	ipmgmt_setaddr_arg_t	*sargp = argp;
 	ipmgmt_retval_t		rval;
-	ipmgmt_aobjmap_t	node;
+	ipmgmt_aobjmap_t	node = {0};
 	nvlist_t		*nvl = NULL;
 	char			*nvlbuf;
 	size_t			nvlsize = sargp->ia_nvlsize;
@@ -321,11 +343,11 @@
 		if (flags & IPMGMT_INIT)
 			node.am_flags = (IPMGMT_ACTIVE|IPMGMT_PERSIST);
 		else
-			node.am_flags = flags;
+			node.am_flags = flags & ~IPMGMT_PROPS_ONLY;
 		if ((err = ipmgmt_aobjmap_op(&node, ADDROBJ_ADD)) != 0)
 			goto ret;
 	}
-	if (flags & IPMGMT_PERSIST) {
+	if ((flags & IPMGMT_PERSIST) && !(flags & IPMGMT_PROPS_ONLY)) {
 		ipadm_dbwrite_cbarg_t	cb;
 
 		cb.dbw_nvl = nvl;
@@ -339,7 +361,7 @@
 }
 
 /*
- * Handles the door commands that modify the `aobjmap' structure.
+ * Handles the door commands that read or modify the `aobjmap' structure.
  *
  * IPMGMT_CMD_ADDROBJ_LOOKUPADD - places a stub address object in `aobjmap'
  *	after ensuring that the namespace is not taken. If required, also
@@ -441,7 +463,7 @@
 			 * have am_ifid set.
 			 */
 			if (head->am_atype != IPADM_ADDR_IPV6_ADDRCONF ||
-			    head->am_linklocal) {
+			    head->ipmgmt_am_linklocal) {
 				break;
 			}
 		}
@@ -456,9 +478,8 @@
 		aobjrval.ir_family = head->am_family;
 		aobjrval.ir_flags = head->am_flags;
 		aobjrval.ir_atype = head->am_atype;
-		if (head->am_atype == IPADM_ADDR_IPV6_ADDRCONF &&
-		    head->am_linklocal)
-			aobjrval.ir_ifid = head->am_ifid;
+		(void) memcpy(&aobjrval.ir_atype_cache, &head->am_atype_cache,
+		    sizeof (aobjrval.ir_atype_cache));
 		(void) pthread_rwlock_unlock(&aobjmap.aobjmap_rwlock);
 		break;
 	case IPMGMT_CMD_LIF2ADDROBJ:
@@ -487,6 +508,8 @@
 		    sizeof (aobjrval.ir_aobjname));
 		aobjrval.ir_atype = head->am_atype;
 		aobjrval.ir_flags = head->am_flags;
+		(void) memcpy(&aobjrval.ir_atype_cache, &head->am_atype_cache,
+		    sizeof (aobjrval.ir_atype_cache));
 		(void) pthread_rwlock_unlock(&aobjmap.aobjmap_rwlock);
 		break;
 	default:
--- old/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_impl.h	Mon Nov 14 16:21:12 2016
+++ new/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_impl.h	Mon Nov 14 16:21:12 2016
@@ -21,6 +21,7 @@
 
 /*
  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
  */
 
 #ifndef	_IPMGMT_IMPL_H
@@ -103,8 +104,12 @@
  *	  of `aobjname'.
  *	- address type (static, dhcp or addrconf)
  *	- `am_flags' indicates if this addrobj in active and/or persist config
- *	- if `am_atype' is IPADM_ADDR_IPV6_ADDRCONF then `am_ifid' holds the
- *	  interface-id used to configure auto-configured addresses
+ *	- other, ipadm_addr_type_t-specific values are cached in
+ *	  am_addr_cache (see type ipmgmt_addr_cache_u):
+ *	  -	ipv6: ipmgmt_am_linklocal (macro)
+ *	  -	ipv6: ipmgmt_am_ifid (macro)
+ *	  -	dhcp: ipmgmt_am_primary (macro)
+ *	  -	dhcp: ipmgmt_am_reqhost (macro)
  */
 typedef struct ipmgmt_aobjmap_s {
 	struct ipmgmt_aobjmap_s	*am_next;
@@ -115,10 +120,14 @@
 	ipadm_addr_type_t	am_atype;
 	uint32_t		am_nextnum;
 	uint32_t		am_flags;
-	boolean_t		am_linklocal;
-	struct sockaddr_storage	am_ifid;
+	ipmgmt_addr_type_cache_u	am_atype_cache;
 } ipmgmt_aobjmap_t;
 
+#define	ipmgmt_am_linklocal	am_atype_cache.ipmgmt_ipv6_cache_s.ipmgmt_linklocal
+#define	ipmgmt_am_ifid		am_atype_cache.ipmgmt_ipv6_cache_s.ipmgmt_ifid
+#define	ipmgmt_am_primary	am_atype_cache.ipmgmt_dhcp_cache_s.ipmgmt_primary
+#define	ipmgmt_am_reqhost	am_atype_cache.ipmgmt_dhcp_cache_s.ipmgmt_reqhost
+
 /* linked list of `aobjmap' nodes, protected by RW lock */
 typedef struct ipmgmt_aobjmap_list_s {
 	ipmgmt_aobjmap_t	*aobjmap_head;
--- old/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_persist.c	Mon Nov 14 16:21:12 2016
+++ new/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_persist.c	Mon Nov 14 16:21:12 2016
@@ -22,6 +22,7 @@
 /*
  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright 2016 Argo Technologie SA.
+ * Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
  */
 
 /*
@@ -908,7 +909,8 @@
 			if (strcmp(head->am_aobjname,
 			    nodep->am_aobjname) == 0 &&
 			    (head->am_atype != IPADM_ADDR_IPV6_ADDRCONF ||
-			    head->am_linklocal == nodep->am_linklocal))
+			    head->ipmgmt_am_linklocal ==
+			    nodep->ipmgmt_am_linklocal))
 				break;
 		}
 
@@ -920,10 +922,8 @@
 			head->am_family = nodep->am_family;
 			head->am_flags = nodep->am_flags;
 			head->am_atype = nodep->am_atype;
-			if (head->am_atype == IPADM_ADDR_IPV6_ADDRCONF) {
-				head->am_ifid = nodep->am_ifid;
-				head->am_linklocal = nodep->am_linklocal;
-			}
+			(void) memcpy(&head->am_atype_cache, &nodep->am_atype_cache,
+			    sizeof (head->am_atype_cache));
 		} else {
 			for (head = aobjmap.aobjmap_head; head != NULL;
 			    head = head->am_next) {
@@ -1068,29 +1068,41 @@
 	if ((err = nvlist_add_string(*nvl, ATYPE, strval)) != 0)
 		goto fail;
 
-	if (np->am_atype == IPADM_ADDR_IPV6_ADDRCONF) {
-		struct sockaddr_in6	*in6;
+	switch (np->am_atype) {
+		case IPADM_ADDR_IPV6_ADDRCONF: {
+			struct sockaddr_in6	*in6;
 
-		in6 = (struct sockaddr_in6 *)&np->am_ifid;
-		if (np->am_linklocal &&
-		    IN6_IS_ADDR_UNSPECIFIED(&in6->sin6_addr)) {
-			if ((err = nvlist_add_string(*nvl, IPADM_NVP_IPNUMADDR,
-			    "default")) != 0)
-				goto fail;
-		} else {
-			if (inet_ntop(AF_INET6, &in6->sin6_addr, strval,
-			    IPMGMT_STRSIZE) == NULL) {
-				err = errno;
-				goto fail;
+			in6 = &np->ipmgmt_am_ifid;
+			if (np->ipmgmt_am_linklocal &&
+				IN6_IS_ADDR_UNSPECIFIED(&in6->sin6_addr)) {
+				if ((err = nvlist_add_string(*nvl, IPADM_NVP_IPNUMADDR,
+					"default")) != 0)
+					goto fail;
+			} else {
+				if (inet_ntop(AF_INET6, &in6->sin6_addr, strval,
+					IPMGMT_STRSIZE) == NULL) {
+					err = errno;
+					goto fail;
+				}
+				if ((err = nvlist_add_string(*nvl, IPADM_NVP_IPNUMADDR,
+					strval)) != 0)
+					goto fail;
 			}
-			if ((err = nvlist_add_string(*nvl, IPADM_NVP_IPNUMADDR,
-			    strval)) != 0)
+		}
+			break;
+		case IPADM_ADDR_DHCP: {
+			if (np->ipmgmt_am_reqhost &&
+			    *np->ipmgmt_am_reqhost != '\0' &&
+			    (err = nvlist_add_string(*nvl, IPADM_NVP_REQHOST,
+			    np->ipmgmt_am_reqhost)) != 0)
 				goto fail;
 		}
-	} else {
-		if ((err = nvlist_add_string(*nvl, IPADM_NVP_IPNUMADDR,
-		    "")) != 0)
-			goto fail;
+			/* FALLTHRU */
+		default:
+			if ((err = nvlist_add_string(*nvl, IPADM_NVP_IPNUMADDR,
+			    "")) != 0)
+				goto fail;
+			break;
 	}
 	return (err);
 fail:
@@ -1138,16 +1150,16 @@
 			node.am_atype = (ipadm_addr_type_t)atoi(strval);
 		} else if (strcmp(IPADM_NVP_IPNUMADDR, name) == 0) {
 			if (node.am_atype == IPADM_ADDR_IPV6_ADDRCONF) {
-				in6 = (struct sockaddr_in6 *)&node.am_ifid;
+				in6 = &node.ipmgmt_am_ifid;
 				if (strcmp(strval, "default") == 0) {
-					bzero(in6, sizeof (node.am_ifid));
-					node.am_linklocal = B_TRUE;
+					bzero(in6, sizeof (node.ipmgmt_am_ifid));
+					node.ipmgmt_am_linklocal = B_TRUE;
 				} else {
 					(void) inet_pton(AF_INET6, strval,
 					    &in6->sin6_addr);
 					if (IN6_IS_ADDR_UNSPECIFIED(
 					    &in6->sin6_addr))
-						node.am_linklocal = B_TRUE;
+						node.ipmgmt_am_linklocal = B_TRUE;
 				}
 			}
 		}
--- old/usr/src/cmd/cmd-inet/lib/nwamd/ncu.c	Mon Nov 14 16:21:13 2016
+++ new/usr/src/cmd/cmd-inet/lib/nwamd/ncu.c	Mon Nov 14 16:21:13 2016
@@ -21,6 +21,7 @@
 
 /*
  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
  */
 
 #include <arpa/inet.h>
@@ -33,6 +34,7 @@
 #include <netinet/in.h>
 #include <stdlib.h>
 #include <strings.h>
+#include <sys/param.h>
 #include <sys/socket.h>
 #include <sys/time.h>
 #include <sys/types.h>
@@ -128,6 +130,17 @@
 	return (nwam_value_get_uint64_array(*val, uintval, cnt));
 }
 
+nwam_error_t
+nwamd_get_ncu_boolean(nwam_ncu_handle_t ncuh, nwam_value_t *val,
+    boolean_t **boolval, uint_t *cnt, const char *prop)
+{
+	nwam_error_t err;
+
+	if ((err = nwam_ncu_get_prop_value(ncuh, prop, val)) != NWAM_SUCCESS)
+		return (err);
+	return (nwam_value_get_boolean_array(*val, boolval, cnt));
+}
+
 /*
  * Run link/interface state machine in response to a state change
  * or enable/disable action event.
@@ -733,13 +746,13 @@
 {
 	nwamd_if_t *nif = &ncu_data->ncu_if;
 	struct nwamd_if_address **nifa, *nifai, *nifait;
-	boolean_t static_addr = B_FALSE;
+	boolean_t static_addr = B_FALSE, *boolvalue, dhcp_primary = B_FALSE;
 	uint64_t *addrsrcvalue;
 	nwam_value_t ncu_prop;
 	nwam_error_t err;
 	ipadm_addrobj_t ipaddr;
 	ipadm_status_t ipstatus;
-	char **addrvalue;
+	char **addrvalue, ipreqhost[MAXNAMELEN];
 	uint_t numvalues;
 	uint64_t *ipversion;
 	int i;
@@ -776,6 +789,39 @@
 		nwam_value_free(ncu_prop);
 	}
 
+	/* ip-primary */
+	if ((err = nwamd_get_ncu_boolean(ncuh, &ncu_prop, &boolvalue,
+	    &numvalues, NWAM_NCU_PROP_IP_PRIMARY)) != NWAM_SUCCESS) {
+		/* ip-primary is optional, so do not LOG_ERR */
+		nlog(LOG_DEBUG, "populate_ip_ncu_properties: "
+		    "could not get %s value: %s",
+		    NWAM_NCU_PROP_IP_PRIMARY, nwam_strerror(err));
+	} else {
+		if (numvalues > 0)
+			dhcp_primary = boolvalue[0];
+		nwam_value_free(ncu_prop);
+	}
+
+	/* ip-reqhost */
+	*ipreqhost = '\0';
+
+	if ((err = nwamd_get_ncu_string(ncuh, &ncu_prop, &addrvalue,
+	    &numvalues, NWAM_NCU_PROP_IP_REQHOST)) != NWAM_SUCCESS) {
+		/* ip-reqhost is optional, so do not LOG_ERR */
+		nlog(LOG_DEBUG, "populate_ip_ncu_properties: "
+		    "could not get %s value: %s",
+		    NWAM_NCU_PROP_IP_REQHOST, nwam_strerror(err));
+	} else {
+		if (numvalues > 0 && strlcpy(ipreqhost, addrvalue[0],
+		    sizeof (ipreqhost)) >= sizeof (ipreqhost)) {
+			nlog(LOG_WARNING, "populate_ip_ncu_properties: "
+			    "too long %s value: %s",
+			    NWAM_NCU_PROP_IP_REQHOST, addrvalue[0]);
+			*ipreqhost = '\0';
+		}
+		nwam_value_free(ncu_prop);
+	}
+
 	/* Free the old list. */
 	for (nifai = nif->nwamd_if_list; nifai != NULL; nifai = nifait) {
 		nifait = nifai->next;
@@ -828,6 +874,22 @@
 			ipadm_destroy_addrobj(ipaddr);
 			goto skip_ipv4_dhcp;
 		}
+		ipstatus = ipadm_set_primary(ipaddr, dhcp_primary);
+		if (ipstatus != IPADM_SUCCESS) {
+			nlog(LOG_ERR, "populate_ip_ncu_properties: "
+			    "ipadm_set_primary failed for v4 dhcp: %s",
+			    ipadm_status2str(ipstatus));
+			ipadm_destroy_addrobj(ipaddr);
+			goto skip_ipv4_dhcp;
+		}
+		ipstatus = ipadm_set_reqhost(ipaddr, ipreqhost);
+		if (ipstatus != IPADM_SUCCESS) {
+			nlog(LOG_ERR, "populate_ip_ncu_properties: "
+			    "ipadm_set_reqhost failed for v4 dhcp: %s",
+			    ipadm_status2str(ipstatus));
+			ipadm_destroy_addrobj(ipaddr);
+			goto skip_ipv4_dhcp;
+		}
 		if ((*nifa = calloc(sizeof (**nifa), 1)) != NULL) {
 			(*nifa)->family = AF_INET;
 			(*nifa)->ipaddr_atype = IPADM_ADDR_DHCP;
@@ -848,7 +910,7 @@
 		if ((err = nwamd_get_ncu_string(ncuh, &ncu_prop, &addrvalue,
 		    &numvalues, NWAM_NCU_PROP_IPV4_ADDR)) != NWAM_SUCCESS) {
 			nlog(LOG_ERR, "populate_ip_ncu_properties: "
-			    "could not get %s value; %s",
+			    "could not get %s value: %s",
 			    NWAM_NCU_PROP_IPV4_ADDR, nwam_strerror(err));
 		} else {
 			for (i = 0; i < numvalues; i++) {
@@ -995,7 +1057,7 @@
 		if ((err = nwamd_get_ncu_string(ncuh, &ncu_prop, &addrvalue,
 		    &numvalues, NWAM_NCU_PROP_IPV6_ADDR)) != NWAM_SUCCESS) {
 			nlog(LOG_ERR, "populate_ip_ncu_properties: "
-			    "could not get %s value; %s",
+			    "could not get %s value: %s",
 			    NWAM_NCU_PROP_IPV6_ADDR, nwam_strerror(err));
 		} else {
 			for (i = 0; i < numvalues; i++) {
--- old/usr/src/cmd/cmd-inet/lib/nwamd/ncu.h	Mon Nov 14 16:21:13 2016
+++ new/usr/src/cmd/cmd-inet/lib/nwamd/ncu.h	Mon Nov 14 16:21:13 2016
@@ -21,6 +21,7 @@
 
 /*
  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
  */
 
 #ifndef _NCU_H
@@ -225,6 +226,8 @@
     uint64_t **, uint_t *, const char *);
 extern nwam_error_t nwamd_get_ncu_string(nwam_ncu_handle_t, nwam_value_t *,
     char ***, uint_t *, const char *);
+extern nwam_error_t nwamd_get_ncu_boolean(nwam_ncu_handle_t, nwam_value_t *,
+    boolean_t **, uint_t *, const char *);
 
 extern void nwamd_walk_physical_configuration(void);
 
--- old/usr/src/cmd/cmd-inet/sbin/dhcpagent/Makefile	Mon Nov 14 16:21:13 2016
+++ new/usr/src/cmd/cmd-inet/sbin/dhcpagent/Makefile	Mon Nov 14 16:21:13 2016
@@ -21,6 +21,7 @@
 #
 # Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
+# Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
 #
 
 PROG    = dhcpagent
@@ -45,7 +46,8 @@
 #
 
 CPPFLAGS  += -D_XOPEN_SOURCE=500 -D__EXTENSIONS__
-LDLIBS    += -lxnet -lnvpair -ldhcpagent -ldhcputil -linetutil -ldevinfo -ldlpi
+LDLIBS    += -lxnet -lnvpair -ldhcpagent -ldhcputil -linetutil -ldevinfo \
+	-ldlpi -lresolv -lsocket -lipadm
 
 # Disable warnings that affect all XPG applications.
 LINTFLAGS += -erroff=E_INCONS_ARG_DECL2 -erroff=E_INCONS_VAL_TYPE_DECL2
--- old/usr/src/cmd/cmd-inet/sbin/dhcpagent/agent.c	Mon Nov 14 16:21:13 2016
+++ new/usr/src/cmd/cmd-inet/sbin/dhcpagent/agent.c	Mon Nov 14 16:21:13 2016
@@ -20,6 +20,7 @@
  */
 /*
  * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
  */
 
 #include <sys/types.h>
@@ -36,6 +37,8 @@
 #include <dhcpagent_ipc.h>
 #include <dhcpagent_util.h>
 #include <dhcpmsg.h>
+#include <dhcp_inittab.h>
+#include <dhcp_symbol.h>
 #include <netinet/dhcp.h>
 #include <net/route.h>
 #include <sys/sockio.h>
@@ -70,6 +73,10 @@
 static boolean_t	do_adopt = B_FALSE;
 static unsigned int	debug_level = 0;
 static iu_eh_callback_t	accept_event, ipc_event, rtsock_event;
+static void dhcp_smach_set_msg_reqhost(dhcp_smach_t *dsmp,
+		ipc_action_t *iap);
+static DHCP_OPT * dhcp_get_ack_or_state(const dhcp_smach_t *dsmp,
+		const PKT_LIST *plp, uint_t codenum, boolean_t *did_alloc);
 
 /*
  * The ipc_cmd_allowed[] table indicates which IPC commands are allowed in
@@ -754,6 +761,7 @@
 		break;		/* not an immediate function */
 
 	case DHCP_EXTEND:
+		dhcp_smach_set_msg_reqhost(dsmp, iap);
 		(void) dhcp_extending(dsmp);
 		break;
 
@@ -793,8 +801,8 @@
 				opt = dhcpv6_pkt_option(ack, NULL, optnum.code,
 				    NULL);
 			} else {
-				if (optnum.code <= DHCP_LAST_OPT)
-					opt = ack->opts[optnum.code];
+				opt = dhcp_get_ack_or_state(dsmp, ack, optnum.code,
+				    &did_alloc);
 			}
 			break;
 
@@ -876,11 +884,7 @@
 				if (optnum.code + optnum.size > sizeof (PKT))
 					break;
 
-				/*
-				 * + 2 to account for option code and length
-				 * byte
-				 */
-				opt = malloc(optnum.size + 2);
+				opt = malloc(optnum.size + DHCP_OPT_META_LEN);
 				if (opt != NULL) {
 					DHCP_OPT *v4opt = opt;
 
@@ -905,8 +909,7 @@
 		}
 
 		/*
-		 * return the option payload, if there was one.  the "+ 2"
-		 * accounts for the option code number and length byte.
+		 * return the option payload, if there was one.
 		 */
 
 		if (opt != NULL) {
@@ -916,7 +919,7 @@
 				(void) memcpy(&d6ov, opt, sizeof (d6ov));
 				optlen = ntohs(d6ov.d6o_len) + sizeof (d6ov);
 			} else {
-				optlen = ((DHCP_OPT *)opt)->len + 2;
+				optlen = ((DHCP_OPT *)opt)->len + DHCP_OPT_META_LEN;
 			}
 			send_data_reply(iap, 0, DHCP_TYPE_OPTION, opt, optlen);
 
@@ -971,6 +974,7 @@
 		PKT_LIST *plp[2];
 
 		deprecate_leases(dsmp);
+		dhcp_smach_set_msg_reqhost(dsmp, iap);
 
 		/*
 		 * if we have a valid hostconf lying around, then jump
@@ -1058,6 +1062,143 @@
 	}
 }
 
+/*
+ * dhcp_smach_set_msg_reqhost(): set dsm_msg_reqhost based on the message
+ * content of a DHCP IPC message
+ *
+ *   input: dhcp_smach_t *: the state machine instance;
+ *	    ipc_action_t *: the decoded DHCP IPC message;
+ *  output: void
+ */
+
+static void
+dhcp_smach_set_msg_reqhost(dhcp_smach_t *dsmp, ipc_action_t *iap)
+{
+	if (dsmp->dsm_msg_reqhost != NULL) {
+		dhcpmsg(MSG_DEBUG,
+		    "dhcp_smach_set_msg_reqhost: nullify former value, %s",
+		    dsmp->dsm_msg_reqhost);
+		free(dsmp->dsm_msg_reqhost);
+		dsmp->dsm_msg_reqhost = NULL;
+	}
+	free(dsmp->dsm_reqfqdn);
+	dsmp->dsm_reqfqdn = NULL;
+
+	/*
+	 * if a STANDARD/HOSTNAME was sent in the IPC request, then copy that
+	 * value into the state machine data if decoding succeeds. Otherwise,
+	 * log to indicate at what step the decoding stopped.
+	 */
+
+	if (dsmp->dsm_isv6) {
+		dhcpmsg(MSG_DEBUG, "dhcp_smach_set_msg_reqhost: ipv6 is not"
+		    " handled");
+	} else if (iap->ia_request->data_type != DHCP_TYPE_OPTION) {
+		dhcpmsg(MSG_DEBUG, "dhcp_smach_set_msg_reqhost: request type %d is"
+		    " not DHCP_TYPE_OPTION", iap->ia_request->data_type);
+	} else {
+		if (iap->ia_request->buffer == NULL
+		    || iap->ia_request->data_length <= DHCP_OPT_META_LEN) {
+			dhcpmsg(MSG_WARNING, "dhcp_smach_set_msg_reqhost:"
+			    " DHCP_TYPE_OPTION ia_request buffer is NULL (0) or"
+			    " short (1): %d",
+			    iap->ia_request->buffer == NULL ? 0 : 1);
+		} else {
+			DHCP_OPT *d4o = (DHCP_OPT *)iap->ia_request->buffer;
+
+			if (d4o->code != CD_HOSTNAME)
+			{
+				dhcpmsg(MSG_DEBUG,
+				    "dhcp_smach_set_msg_reqhost: ignoring DHCPv4"
+				    " option %u", d4o->code);
+			} else if (iap->ia_request->data_length - DHCP_OPT_META_LEN
+			    != d4o->len) {
+				dhcpmsg(MSG_WARNING, "dhcp_smach_set_msg_reqhost:"
+				    " unexpected DHCP_OPT buffer length %u for CD_HOSTNAME"
+				    " option length %u", iap->ia_request->data_length,
+				    d4o->len);
+			} else {
+
+				dhcp_symbol_t *entry = inittab_getbycode(ITAB_CAT_STANDARD,
+				    ITAB_CONS_INFO, CD_HOSTNAME);
+				if (entry == NULL) {
+					dhcpmsg(MSG_WARNING,
+					    "dhcp_smach_set_msg_reqhost: error getting"
+					    " ITAB_CAT_STANDARD ITAB_CONS_INFO"
+					    " CD_HOSTNAME entry");
+				} else {
+					char *value = inittab_decode(entry, d4o->value,
+					    d4o->len, /* just_payload */ B_TRUE);
+					if (value == NULL) {
+						dhcpmsg(MSG_WARNING,
+						    "dhcp_smach_set_msg_reqhost: error decoding"
+						    " CD_HOSTNAME value from DHCP_OPT");
+					} else {
+						dhcpmsg(MSG_DEBUG,
+						    "dhcp_smach_set_msg_reqhost: host %s", value);
+						free(dsmp->dsm_msg_reqhost);
+						dsmp->dsm_msg_reqhost = value;
+					}
+					free(entry);
+					entry = NULL;
+				}
+			}
+		}
+	}
+}
+
+/*
+ * dhcp_get_ack_or_state(): get a v4 option from the ACK or from the state
+ * machine state for certain codes that are not ACKed (e.g., CD_CLIENT_ID)
+ *
+ *   input: dhcp_smach_t *: the state machine instance;
+ *	    PKT_LIST *: the decoded DHCP IPC message;
+ *	    uint_t: the DHCP client option code;
+ *	    boolean_t *: a pointer to a value that will be set to B_TRUE if
+ *	        the return value must be freed (or else set to B_FALSE);
+ *  output: the option if found or else NULL.
+ */
+
+/* ARGSUSED */
+static DHCP_OPT *
+dhcp_get_ack_or_state(const dhcp_smach_t *dsmp, const PKT_LIST *plp,
+	uint_t codenum, boolean_t *did_alloc)
+{
+	DHCP_OPT *opt;
+
+	*did_alloc = B_FALSE;
+
+	if (codenum > DHCP_LAST_OPT)
+		return NULL;
+
+	/* check the ACK first for all codes */
+	opt = plp->opts[codenum];
+	if (opt != NULL)
+		return opt;
+
+	/* check the machine state also for certain codes */
+	switch (codenum) {
+	case CD_CLIENT_ID:
+		/*
+		 * CD_CLIENT_ID is not sent in an ACK, but it's possibly available
+		 * from the state machine data
+		 */
+
+		if (dsmp->dsm_cidlen > 0) {
+			if ((opt = malloc(dsmp->dsm_cidlen + DHCP_OPT_META_LEN))
+			    != NULL) {
+				*did_alloc = B_TRUE;
+				(void) encode_dhcp_opt(opt, B_FALSE /* is IPv6 */,
+				    CD_CLIENT_ID, dsmp->dsm_cid, dsmp->dsm_cidlen);
+			}
+		}
+		break;
+	default:
+		break;
+	}
+	return (opt);
+}
+
 /*
  * check_rtm_addr(): determine if routing socket message matches interface
  *		     address
--- old/usr/src/cmd/cmd-inet/sbin/dhcpagent/defaults.c	Mon Nov 14 16:21:13 2016
+++ new/usr/src/cmd/cmd-inet/sbin/dhcpagent/defaults.c	Mon Nov 14 16:21:13 2016
@@ -21,6 +21,7 @@
 /*
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
  */
 
 #include <sys/types.h>
@@ -59,7 +60,9 @@
 	{ "DEBUG_LEVEL",	 "0",	 0,   3   },
 	{ "VERBOSE",		 "0",	 0,   0   },
 	{ "VERIFIED_LEASE_ONLY", "0",	 0,   0	  },
-	{ "PARAM_IGNORE_LIST",	 NULL,	 0,   0   }
+	{ "PARAM_IGNORE_LIST",	 NULL,	 0,   0   },
+	{ "REQUEST_FQDN",	 "1",	 0,   0	  },
+	{ "V4_DEFAULT_IAID_DUID",  "0",	 0,   0	  },
 };
 
 /*
--- old/usr/src/cmd/cmd-inet/sbin/dhcpagent/defaults.h	Mon Nov 14 16:21:13 2016
+++ new/usr/src/cmd/cmd-inet/sbin/dhcpagent/defaults.h	Mon Nov 14 16:21:13 2016
@@ -21,6 +21,7 @@
 /*
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
  */
 
 #ifndef	DEFAULTS_H
@@ -54,7 +55,9 @@
 	DF_DEBUG_LEVEL,		/* set debug level (undocumented) */
 	DF_VERBOSE,		/* set verbose mode (undocumented) */
 	DF_VERIFIED_LEASE_ONLY,	/* send RELEASE on SIGTERM and need verify */
-	DF_PARAM_IGNORE_LIST	/* our parameter ignore list */
+	DF_PARAM_IGNORE_LIST,	/* our parameter ignore list */
+	DF_REQUEST_FQDN,	/* request client FQDN associated with interface */
+	DF_V4_DEFAULT_IAID_DUID	/* always IAID/DUID if no DF_CLIENT_ID */
 };
 
 #define	DHCP_AGENT_DEFAULTS	"/etc/default/dhcpagent"
--- old/usr/src/cmd/cmd-inet/sbin/dhcpagent/dhcpagent.dfl	Mon Nov 14 16:21:13 2016
+++ new/usr/src/cmd/cmd-inet/sbin/dhcpagent/dhcpagent.dfl	Mon Nov 14 16:21:13 2016
@@ -22,6 +22,7 @@
 #
 # Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
+# Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
 #
 
 #
@@ -90,16 +91,55 @@
 #
 # CLIENT_ID=
 
+# By default, for an IPv4 interface that is not in an IP network
+# multipathing (IPMP) group, that is not IP over InfiniBand (IPoIB), and
+# that is not a logical interface, the DHCP agent will forgo sending a
+# client identifier unless CLIENT_ID is defined.
+#
+# To use a system-managed, RFC 3315-style (i.e., DHCPv6-style) binding
+# identifier as documented in RFC 4361, "Node-specific Client Identifiers
+# for DHCPv4," for all IPv4 interfaces (unless CLIENT_ID is defined),
+# uncomment the following line.
+#
+# V4_DEFAULT_IAID_DUID=yes
+
+# By default, the DHCP agent will try to request the Fully Qualified Domain
+# Name (FQDN) currently associated with the interface performing DHCP.  The
+# hostname is defined by using the -h,--reqhost option of ipadm(1M) or the
+# ncu ip-reqhost property of nwamcfg(1M) or by flagging the interface as
+# primary so that nodename(4) is used as the hostname.
+#
+# A defined hostname will be used as the FQDN if it is "rooted" (i.e., if
+# it ends with a '.') or if it consists of at least three DNS labels (e.g.,
+# srv.example.com).  If the hostname is not an FQDN, then either a defined
+# defaultdomain(4) or a defined resolv.conf(4) domain will be appended to
+# form an FQDN.  If no FQDN can be determined, the option will not be used.
+#
+# If this REQUEST_FQDN option is enabled, an FQDN will be sent in messages
+# to the DHCP server along with RFC 4702 options to request that a
+# collaborating DNS server perform DNS updates for A and PTR resource
+# records.  To prevent sending FQDN and DNS options, uncomment the line
+# below.
+#
+# If an FQDN is sent, REQUEST_HOSTNAME processing will not be done, per RFC
+# 4702 (3.1):  "clients that send the Client FQDN option in their messages
+# MUST NOT also send the Host Name."
+#
+# REQUEST_FQDN=no
+
 # By default, the DHCP agent will try to request the hostname currently
 # associated with the interface performing DHCP.  If this option is
-# enabled, the agent will attempt to find a host name in /etc/hostname.<if>,
-# which must contain a line of the form
+# enabled, the agent will attempt to use an -h,--reqhost option saved with
+# ipadm(1M) or an ncu ip-reqhost property set with nwamcfg(1M); or else
+# attempt to find a host name in /etc/hostname.<if>, which must contain a
+# line of the form
 #
 #	inet name
 #
-# where "name" is a single RFC 1101-compliant token.  If found, the token
-# will be used to request that host name from the DHCP server.  To prevent
-# this, uncomment the following line.
+# where "name" is a single RFC 1101-compliant token; or else use
+# nodename(4) for a DHCP interface flagged as primary.  If found in any of
+# these configurations, the token will be used to request that host name
+# from the DHCP server.  To prevent this, uncomment the following line.
 #
 # REQUEST_HOSTNAME=no
 
--- old/usr/src/cmd/cmd-inet/sbin/dhcpagent/init_reboot.c	Mon Nov 14 16:21:14 2016
+++ new/usr/src/cmd/cmd-inet/sbin/dhcpagent/init_reboot.c	Mon Nov 14 16:21:14 2016
@@ -21,6 +21,7 @@
 /*
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
  *
  * INIT_REBOOT state of the DHCP client state machine.
  */
@@ -27,7 +28,6 @@
 
 #include <sys/types.h>
 #include <stdio.h>
-#include <limits.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <netinet/dhcp.h>
@@ -58,8 +58,6 @@
 dhcp_init_reboot_v4(dhcp_smach_t *dsmp)
 {
 	dhcp_pkt_t		*dpkt;
-	const char		*reqhost;
-	char			hostfile[PATH_MAX + 1];
 
 	/*
 	 * assemble DHCPREQUEST message.  The max dhcp message size
@@ -79,30 +77,9 @@
 		(void) add_pkt_opt(dpkt, CD_CLASS_ID, class_id, class_id_len);
 	(void) add_pkt_prl(dpkt, dsmp);
 
-	/*
-	 * Set CD_HOSTNAME option if REQUEST_HOSTNAME is set and a hostname
-	 * is found in /etc/hostname.<ifname>
-	 */
-	if (df_get_bool(dsmp->dsm_name, dsmp->dsm_isv6, DF_REQUEST_HOSTNAME)) {
-		(void) snprintf(hostfile, sizeof (hostfile), "/etc/hostname.%s",
-		    dsmp->dsm_name);
+	if (dhcp_add_fqdn_opt(dpkt, dsmp) != 0)
+		(void) dhcp_add_hostname_opt(dpkt, dsmp);
 
-		if ((reqhost = iffile_to_hostname(hostfile)) != NULL) {
-			dhcpmsg(MSG_DEBUG, "dhcp_selecting: host %s", reqhost);
-			if ((dsmp->dsm_reqhost = strdup(reqhost)) != NULL)
-				(void) add_pkt_opt(dpkt, CD_HOSTNAME,
-				    dsmp->dsm_reqhost,
-				    strlen(dsmp->dsm_reqhost));
-			else
-				dhcpmsg(MSG_WARNING, "dhcp_selecting: cannot"
-				    " allocate memory for host name option");
-		} else {
-			dhcpmsg(MSG_DEBUG,
-			    "dhcp_selecting: no hostname for %s",
-			    dsmp->dsm_name);
-		}
-	}
-
 	(void) add_pkt_opt(dpkt, CD_END, NULL, 0);
 
 	(void) send_pkt(dsmp, dpkt, htonl(INADDR_BROADCAST), stop_init_reboot);
@@ -182,6 +159,9 @@
 	/* Add required Option Request option */
 	(void) add_pkt_prl(dpkt, dsmp);
 
+	/* Add FQDN if configured */
+	(void) dhcp_add_fqdn_opt(dpkt, dsmp);
+
 	(void) send_pkt_v6(dsmp, dpkt, ipv6_all_dhcp_relay_and_servers,
 	    stop_init_reboot, DHCPV6_CNF_TIMEOUT, DHCPV6_CNF_MAX_RT);
 
--- old/usr/src/cmd/cmd-inet/sbin/dhcpagent/ipc_action.c	Mon Nov 14 16:21:14 2016
+++ new/usr/src/cmd/cmd-inet/sbin/dhcpagent/ipc_action.c	Mon Nov 14 16:21:14 2016
@@ -21,6 +21,7 @@
 /*
  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
  */
 
 #pragma ident	"%Z%%M%	%I%	%E% SMI"
@@ -106,8 +107,10 @@
 	/* We've taken ownership, so the input request is now invalid */
 	ipc_action_init(iareq);
 
-	dhcpmsg(MSG_DEBUG, "ipc_action_start: started %s (command %d) on %s",
-	    dhcp_ipc_type_to_string(ia->ia_cmd), ia->ia_cmd, dsmp->dsm_name);
+	dhcpmsg(MSG_DEBUG, "ipc_action_start: started %s (command %d) on %s,"
+	    " buffer length %u",
+	    dhcp_ipc_type_to_string(ia->ia_cmd), ia->ia_cmd, dsmp->dsm_name,
+	    ia->ia_request == NULL ? 0 : ia->ia_request->data_length);
 
 	dsmp->dsm_dflags |= DHCP_IF_BUSY;
 
--- old/usr/src/cmd/cmd-inet/sbin/dhcpagent/packet.c	Mon Nov 14 16:21:14 2016
+++ new/usr/src/cmd/cmd-inet/sbin/dhcpagent/packet.c	Mon Nov 14 16:21:14 2016
@@ -21,6 +21,7 @@
 /*
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
  */
 
 #include <string.h>
@@ -413,16 +414,14 @@
     uint_t opt_len)
 {
 	uchar_t		*raw_pkt;
-	int		req_len;
+	size_t		req_len;
 	void		*optr;
 
 	raw_pkt = (uchar_t *)dpkt->pkt;
 	optr = raw_pkt + dpkt->pkt_cur_len;
 	if (dpkt->pkt_isv6) {
-		dhcpv6_option_t d6o;
+		req_len = opt_len + sizeof (dhcpv6_option_t);
 
-		req_len = opt_len + sizeof (d6o);
-
 		if (dpkt->pkt_cur_len + req_len > dpkt->pkt_max_len) {
 			dhcpmsg(MSG_WARNING,
 			    "add_pkt_opt: not enough room for v6 option %u in "
@@ -430,17 +429,8 @@
 			    dpkt->pkt_cur_len, req_len, dpkt->pkt_max_len);
 			return (NULL);
 		}
-		d6o.d6o_code = htons(opt_type);
-		d6o.d6o_len = htons(opt_len);
-		(void) memcpy(&raw_pkt[dpkt->pkt_cur_len], &d6o, sizeof (d6o));
-		dpkt->pkt_cur_len += sizeof (d6o);
-		if (opt_len > 0) {
-			(void) memcpy(&raw_pkt[dpkt->pkt_cur_len], opt_val,
-			    opt_len);
-			dpkt->pkt_cur_len += opt_len;
-		}
 	} else {
-		req_len = opt_len + 2; /* + 2 for code & length bytes */
+		req_len = opt_len + DHCP_OPT_META_LEN;
 
 		/* CD_END and CD_PAD options don't have a length field */
 		if (opt_type == CD_END || opt_type == CD_PAD) {
@@ -457,19 +447,62 @@
 			    "packet", opt_type);
 			return (NULL);
 		}
+	}
 
-		raw_pkt[dpkt->pkt_cur_len++] = opt_type;
+	req_len = encode_dhcp_opt(&raw_pkt[dpkt->pkt_cur_len], dpkt->pkt_isv6,
+	    opt_type, opt_val, opt_len);
+	dpkt->pkt_cur_len += req_len;
 
-		if (req_len > 1) {
-			raw_pkt[dpkt->pkt_cur_len++] = opt_len;
-			if (opt_len > 0) {
-				(void) memcpy(&raw_pkt[dpkt->pkt_cur_len],
-				    opt_val, opt_len);
-				dpkt->pkt_cur_len += opt_len;
-			}
+	return (optr);
+}
+
+/*
+ * encode_dhcp_opt(): sets the fields of an allocated DHCP option buffer
+ *
+ *   input: void *: the buffer allocated for enough space for
+ *			(DHCPv6) dhcpv6_option_t and value, or for (DHCPv4) opt_type +
+ *			length + value (length/value are skipped for CD_END or
+ *			CD_PAD);
+ *	    boolean_t: a value indicating whether DHCPv6 or not;
+ *	    uint_t: the type of option being added;
+ *	    const void *: the value of that option;
+ *	    uint_t: the length of the value of the option
+ *  output: size_t: the number of bytes written starting at opt.
+ */
+
+size_t
+encode_dhcp_opt(void *dopt, boolean_t isv6, uint_t opt_type,
+    const void *opt_val, uint_t opt_len)
+{
+	boolean_t do_copy_value = B_FALSE;
+	size_t res_len = 0;
+	uint8_t *pval;
+
+	if (isv6) {
+		dhcpv6_option_t d6o;
+		d6o.d6o_code = htons(opt_type);
+		d6o.d6o_len = htons(opt_len);
+		(void) memcpy(dopt, &d6o, sizeof (d6o));
+		res_len += sizeof (d6o);
+
+		do_copy_value = B_TRUE;
+	} else {
+		pval = (uint8_t *)dopt;
+		pval[res_len++] = opt_type;
+
+		if (opt_type != CD_END && opt_type != CD_PAD) {
+			pval[res_len++] = opt_len;
+			do_copy_value = B_TRUE;
 		}
 	}
-	return (optr);
+
+	pval = (uint8_t *)dopt + res_len;
+	if (do_copy_value && opt_len > 0) {
+		(void) memcpy(pval, opt_val, opt_len);
+		res_len += opt_len;
+	}
+
+	return (res_len);
 }
 
 /*
--- old/usr/src/cmd/cmd-inet/sbin/dhcpagent/packet.h	Mon Nov 14 16:21:14 2016
+++ new/usr/src/cmd/cmd-inet/sbin/dhcpagent/packet.h	Mon Nov 14 16:21:14 2016
@@ -21,6 +21,7 @@
 /*
  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
  */
 
 #ifndef	_PACKET_H
@@ -122,6 +123,8 @@
 boolean_t	remove_pkt_opt(dhcp_pkt_t *, uint_t);
 boolean_t	update_v6opt_len(dhcpv6_option_t *, int);
 void		*add_pkt_opt(dhcp_pkt_t *, uint_t, const void *, uint_t);
+size_t		encode_dhcp_opt(void *, boolean_t, uint_t, const void *,
+			uint_t);
 void		*add_pkt_subopt(dhcp_pkt_t *, dhcpv6_option_t *, uint_t,
 		    const void *, uint_t);
 void		*add_pkt_opt16(dhcp_pkt_t *, uint_t, uint16_t);
--- old/usr/src/cmd/cmd-inet/sbin/dhcpagent/renew.c	Mon Nov 14 16:21:14 2016
+++ new/usr/src/cmd/cmd-inet/sbin/dhcpagent/renew.c	Mon Nov 14 16:21:14 2016
@@ -20,6 +20,7 @@
  */
 /*
  * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
  */
 
 #include <sys/types.h>
@@ -462,6 +463,9 @@
 		/* Add required Option Request option */
 		(void) add_pkt_prl(dpkt, dsmp);
 
+		/* Add FQDN if configured */
+		(void) dhcp_add_fqdn_opt(dpkt, dsmp);
+
 		return (send_pkt_v6(dsmp, dpkt, dsmp->dsm_server,
 		    stop_extending, irt, mrt));
 	} else {
@@ -490,7 +494,8 @@
 		 * dhcp_selecting() if the REQUEST_HOSTNAME option was set and
 		 * a host name was found.
 		 */
-		if (dsmp->dsm_reqhost != NULL) {
+		if (dhcp_add_fqdn_opt(dpkt, dsmp) != 0 &&
+		    dsmp->dsm_reqhost != NULL) {
 			(void) add_pkt_opt(dpkt, CD_HOSTNAME, dsmp->dsm_reqhost,
 			    strlen(dsmp->dsm_reqhost));
 		}
--- old/usr/src/cmd/cmd-inet/sbin/dhcpagent/request.c	Mon Nov 14 16:21:14 2016
+++ new/usr/src/cmd/cmd-inet/sbin/dhcpagent/request.c	Mon Nov 14 16:21:14 2016
@@ -21,6 +21,7 @@
 /*
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
  *
  * REQUESTING state of the client state machine.
  */
@@ -79,6 +80,9 @@
 	/* Add required Option Request option */
 	(void) add_pkt_prl(dpkt, dsmp);
 
+	/* Add FQDN if configured */
+	(void) dhcp_add_fqdn_opt(dpkt, dsmp);
+
 	(void) send_pkt_v6(dsmp, dpkt, dsmp->dsm_server, stop_requesting,
 	    DHCPV6_REQ_TIMEOUT, DHCPV6_REQ_MAX_RT);
 
@@ -313,7 +317,8 @@
 		 * dhcp_selecting() if the DF_REQUEST_HOSTNAME option set and a
 		 * host name was found
 		 */
-		if (dsmp->dsm_reqhost != NULL) {
+		if (dhcp_add_fqdn_opt(dpkt, dsmp) != 0 &&
+		    dsmp->dsm_reqhost != NULL) {
 			(void) add_pkt_opt(dpkt, CD_HOSTNAME, dsmp->dsm_reqhost,
 			    strlen(dsmp->dsm_reqhost));
 		}
--- old/usr/src/cmd/cmd-inet/sbin/dhcpagent/select.c	Mon Nov 14 16:21:14 2016
+++ new/usr/src/cmd/cmd-inet/sbin/dhcpagent/select.c	Mon Nov 14 16:21:14 2016
@@ -20,6 +20,7 @@
  */
 /*
  * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
  *
  * SELECTING state of the client state machine.
  */
@@ -29,7 +30,6 @@
 #include <stdlib.h>
 #include <strings.h>
 #include <time.h>
-#include <limits.h>
 #include <netinet/in.h>
 #include <net/route.h>
 #include <net/if.h>
@@ -106,8 +106,6 @@
 dhcp_selecting(dhcp_smach_t *dsmp)
 {
 	dhcp_pkt_t		*dpkt;
-	const char		*reqhost;
-	char			hostfile[PATH_MAX + 1];
 
 	/*
 	 * We first set up to collect OFFER/Advertise packets as they arrive.
@@ -178,6 +176,9 @@
 
 		/* xxx add Reconfigure Accept */
 
+		/* Add FQDN if configured */
+		(void) dhcp_add_fqdn_opt(dpkt, dsmp);
+
 		(void) send_pkt_v6(dsmp, dpkt, ipv6_all_dhcp_relay_and_servers,
 		    stop_selecting, DHCPV6_SOL_TIMEOUT, DHCPV6_SOL_MAX_RT);
 	} else {
@@ -201,27 +202,9 @@
 		}
 		(void) add_pkt_prl(dpkt, dsmp);
 
-		if (df_get_bool(dsmp->dsm_name, dsmp->dsm_isv6,
-		    DF_REQUEST_HOSTNAME)) {
-			dhcpmsg(MSG_DEBUG,
-			    "dhcp_selecting: DF_REQUEST_HOSTNAME");
-			(void) snprintf(hostfile, sizeof (hostfile),
-			    "/etc/hostname.%s", dsmp->dsm_name);
+		if (dhcp_add_fqdn_opt(dpkt, dsmp) != 0)
+			(void) dhcp_add_hostname_opt(dpkt, dsmp);
 
-			if ((reqhost = iffile_to_hostname(hostfile)) != NULL) {
-				dhcpmsg(MSG_DEBUG, "dhcp_selecting: host %s",
-				    reqhost);
-				dsmp->dsm_reqhost = strdup(reqhost);
-				if (dsmp->dsm_reqhost != NULL)
-					(void) add_pkt_opt(dpkt, CD_HOSTNAME,
-					    dsmp->dsm_reqhost,
-					    strlen(dsmp->dsm_reqhost));
-				else
-					dhcpmsg(MSG_WARNING,
-					    "dhcp_selecting: cannot allocate "
-					    "memory for host name option");
-			}
-		}
 		(void) add_pkt_opt(dpkt, CD_END, NULL, 0);
 
 		(void) send_pkt(dsmp, dpkt, htonl(INADDR_BROADCAST),
--- old/usr/src/cmd/cmd-inet/sbin/dhcpagent/states.c	Mon Nov 14 16:21:14 2016
+++ new/usr/src/cmd/cmd-inet/sbin/dhcpagent/states.c	Mon Nov 14 16:21:14 2016
@@ -20,6 +20,7 @@
  */
 /*
  * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
  *
  * This module contains core functions for managing DHCP state machine
  * instances.
@@ -332,6 +333,8 @@
 	free(dsmp->dsm_pil);
 	free(dsmp->dsm_routers);
 	free(dsmp->dsm_reqhost);
+	free(dsmp->dsm_msg_reqhost);
+	free(dsmp->dsm_reqfqdn);
 	free(dsmp);
 
 	/* no big deal if this fails */
@@ -1049,7 +1052,8 @@
 	 * unable to parse it.  We need to determine if a Client ID is required
 	 * and, if so, generate one.
 	 *
-	 * If it's IPv4, not in an IPMP group, and not a logical interface,
+	 * If it's IPv4, not in an IPMP group, not a logical interface,
+	 * and a DHCP default for DF_V4_DEFAULT_IAID_DUID is not affirmative,
 	 * then we need to preserve backward-compatibility by avoiding
 	 * new-fangled DUID/IAID construction.  (Note: even for IPMP test
 	 * addresses, we construct a DUID/IAID since we may renew a lease for
@@ -1056,7 +1060,9 @@
 	 * an IPMP test address on any functioning IP interface in the group.)
 	 */
 	if (!pif->pif_isv6 && pif->pif_grifname[0] == '\0' &&
-	    strchr(dsmp->dsm_name, ':') == NULL) {
+	    strchr(dsmp->dsm_name, ':') == NULL &&
+	    !df_get_bool(dsmp->dsm_name, pif->pif_isv6,
+	    DF_V4_DEFAULT_IAID_DUID)) {
 		if (pif->pif_hwtype == ARPHRD_IB) {
 			/*
 			 * This comes from the DHCP over IPoIB specification.
@@ -1218,6 +1224,15 @@
 	free(dsmp->dsm_reqhost);
 	dsmp->dsm_reqhost = NULL;
 
+	/*
+	 * Do not reset dsm_msg_reqhost here. Unlike dsm_reqhost coming from
+	 * /etc/host.*, dsm_msg_reqhost comes externally, and it survives until
+	 * it is reset from another external message.
+	 */
+
+	free(dsmp->dsm_reqfqdn);
+	dsmp->dsm_reqfqdn = NULL;
+
 	cancel_smach_timers(dsmp);
 
 	(void) set_smach_state(dsmp, INIT);
--- old/usr/src/cmd/cmd-inet/sbin/dhcpagent/states.h	Mon Nov 14 16:21:15 2016
+++ new/usr/src/cmd/cmd-inet/sbin/dhcpagent/states.h	Mon Nov 14 16:21:15 2016
@@ -21,6 +21,7 @@
 /*
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
  */
 
 #ifndef	STATES_H
@@ -201,6 +202,19 @@
 	char		*dsm_reqhost;
 
 	/*
+	 * The host name we've been asked by IPC message (e.g.,
+	 * `ipadm -T dhcp -h ...') to request is remembered here until it is
+	 * reset by another external message.
+	 */
+	char		*dsm_msg_reqhost;
+
+	/*
+	 * The determined FQDN is remembered here between the DISCOVER/SOLICIT
+	 * and the REQUEST.
+	 */
+	char		*dsm_reqfqdn;
+
+	/*
 	 * V4 and V6 use slightly different timers.  For v4, we must count
 	 * seconds from the point where we first try to configure the
 	 * interface.  For v6, only seconds while performing a transaction
--- old/usr/src/cmd/cmd-inet/sbin/dhcpagent/util.c	Mon Nov 14 16:21:15 2016
+++ new/usr/src/cmd/cmd-inet/sbin/dhcpagent/util.c	Mon Nov 14 16:21:15 2016
@@ -20,11 +20,13 @@
  */
 /*
  * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
  */
 
 #include <unistd.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <sys/utsname.h>
 #include <stdlib.h>
 #include <netinet/in.h>		/* struct in_addr */
 #include <netinet/dhcp.h>
@@ -35,10 +37,16 @@
 #include <string.h>
 #include <dhcpmsg.h>
 #include <ctype.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
 #include <netdb.h>
 #include <fcntl.h>
 #include <stdio.h>
 #include <dhcp_hostconf.h>
+#include <limits.h>
+#include <strings.h>
+#include <libipadm.h>
 
 #include "states.h"
 #include "agent.h"
@@ -45,6 +53,7 @@
 #include "interface.h"
 #include "util.h"
 #include "packet.h"
+#include "defaults.h"
 
 /*
  * this file contains utility functions that have no real better home
@@ -67,6 +76,12 @@
  *  o  true miscellany -- anything else
  */
 
+#define	ETCNODENAME		"/etc/nodename"
+#define	ETCDEFAULTDOMAIN	"/etc/defaultdomain"
+
+static	boolean_t	is_fqdn(const char *);
+static int			dhcp_assemble_fqdn(dhcp_smach_t *dsmp);
+
 /*
  * pkt_type_to_string(): stringifies a packet type
  *
@@ -466,35 +481,6 @@
 }
 
 /*
- * valid_hostname(): check whether a string is a valid hostname
- *
- *   input: const char *: the string to verify as a hostname
- *  output: boolean_t: B_TRUE if the string is a valid hostname
- *
- * Note that we accept both host names beginning with a digit and
- * those containing hyphens.  Neither is strictly legal according
- * to the RFCs, but both are in common practice, so we endeavour
- * to not break what customers are using.
- */
-
-static boolean_t
-valid_hostname(const char *hostname)
-{
-	unsigned int i;
-
-	for (i = 0; hostname[i] != '\0'; i++) {
-
-		if (isalpha(hostname[i]) || isdigit(hostname[i]) ||
-		    (((hostname[i] == '-') || (hostname[i] == '.')) && (i > 0)))
-			continue;
-
-		return (B_FALSE);
-	}
-
-	return (i > 0);
-}
-
-/*
  * iffile_to_hostname(): return the hostname contained on a line of the form
  *
  * [ ^I]*inet[ ^I]+hostname[\n]*\0
@@ -556,7 +542,7 @@
 					    " host name too long");
 					return (NULL);
 				}
-				if (valid_hostname(p)) {
+				if (ipadm_is_valid_hostname(p)) {
 					return (p);
 				} else {
 					dhcpmsg(MSG_WARNING,
@@ -704,4 +690,416 @@
 		dhcpmsg(MSG_ERR, "cannot write %s (reboot will "
 		    "not use cached configuration)", hcfile);
 	}
+}
+
+/*
+ * Try to get a string from the first line of a file, up to but not
+ * including any space (0x20) or newline.
+ */
+
+static int
+dhcp_get_oneline(const char *filename, char *buf, size_t buflen)
+{
+	char	value[SYS_NMLN], *c;
+	int		fd, i;
+
+	if ((fd = open(filename, O_RDONLY)) <= 0) {
+		dhcpmsg(MSG_DEBUG, "dhcp_get_oneline: could not open %s",
+		    filename);
+		*buf = '\0';
+	} else {
+		if ((i = read(fd, value, SYS_NMLN - 1)) <= 0) {
+			dhcpmsg(MSG_WARNING, "dhcp_get_oneline: no line in %s",
+			    filename);
+			*buf = '\0';
+		} else {
+			value[i] = '\0';
+			if ((c = strchr(value, '\n')) != NULL)
+				*c = '\0';
+			if ((c = strchr(value, ' ')) != NULL)
+				*c = '\0';
+
+			if (strlcpy(buf, value, buflen) >= buflen) {
+				dhcpmsg(MSG_WARNING, "dhcp_get_oneline: too long value, %s",
+				    value);
+				*buf = '\0';
+			}
+		}
+		(void) close(fd);
+	}
+
+	return (*buf != '\0' ? 0 : -1);
+}
+
+/*
+ * Try to get the hostname from the /etc/nodename file. uname(2) cannot
+ * be used, because that is initialized after DHCP has solicited, in order
+ * to allow for the possibility that utsname.nodename can be set from
+ * DHCP Hostname. Here, though, we want to send a value specified
+ * advance of DHCP, so read /etc/nodename directly.
+ */
+
+static int
+dhcp_get_nodename(char *buf, size_t buflen)
+{
+	return dhcp_get_oneline(ETCNODENAME, buf, buflen);
+}
+
+/*
+ * Try to get the value from the /etc/defaultdomain file. libnsl's
+ * domainname() cannot be used, because that is initialized after DHCP has
+ * solicited. Here, though, we want to send a value specified in advance
+ * of DHCP, so read /etc/defaultdomain directly.
+ */
+
+static int
+dhcp_get_defaultdomain(char *buf, size_t buflen)
+{
+	return dhcp_get_oneline(ETCDEFAULTDOMAIN, buf, buflen);
+}
+
+/*
+ * dhcp_add_hostname_opt(): Set CD_HOSTNAME option if REQUEST_HOSTNAME is
+ *			 affirmative and if 1) dsm_msg_reqhost is available; or
+ *			 2) hostname is read from an extant /etc/hostname.<ifname>
+ *			 file; or 3) interface is primary and nodename(4) is defined.
+ *
+ *   input: dhcp_pkt_t *: pointer to DHCP message being constructed;
+ *	    dhcp_smach_t *: pointer to interface DHCP state machine;
+ */
+
+int
+dhcp_add_hostname_opt(dhcp_pkt_t *dpkt, dhcp_smach_t *dsmp)
+{
+	const char	*reqhost;
+	char	nodename[MAXNAMELEN];
+
+	if (!df_get_bool(dsmp->dsm_name, dsmp->dsm_isv6, DF_REQUEST_HOSTNAME))
+		return (0);
+
+	dhcpmsg(MSG_DEBUG, "dhcp_add_hostname_opt: DF_REQUEST_HOSTNAME");
+
+	if (dsmp->dsm_msg_reqhost != NULL
+	    && ipadm_is_valid_hostname(dsmp->dsm_msg_reqhost)) {
+		reqhost = dsmp->dsm_msg_reqhost;
+	} else {
+		char		hostfile[PATH_MAX + 1];
+
+		(void) snprintf(hostfile, sizeof (hostfile),
+		    "/etc/hostname.%s", dsmp->dsm_name);
+		reqhost = iffile_to_hostname(hostfile);
+	}
+
+	if (reqhost == NULL && (dsmp->dsm_dflags & DHCP_IF_PRIMARY) &&
+	    dhcp_get_nodename(nodename, sizeof (nodename)) == 0) {
+		reqhost = nodename;
+	}
+
+	if (reqhost != NULL) {
+		free(dsmp->dsm_reqhost);
+		if ((dsmp->dsm_reqhost = strdup(reqhost)) == NULL)
+			dhcpmsg(MSG_WARNING, "dhcp_add_hostname_opt: cannot allocate "
+			    "memory for host name option");
+	}
+
+	if (dsmp->dsm_reqhost != NULL) {
+		dhcpmsg(MSG_DEBUG, "dhcp_add_hostname_opt: host %s for %s",
+		    dsmp->dsm_reqhost, dsmp->dsm_name);
+		(void) add_pkt_opt(dpkt, CD_HOSTNAME, dsmp->dsm_reqhost,
+		    strlen(dsmp->dsm_reqhost));
+		return 1;
+	}
+	else {
+		dhcpmsg(MSG_DEBUG, "dhcp_add_hostname_opt: no hostname for %s",
+		    dsmp->dsm_name);
+	}
+
+	return (0);
+}
+
+/*
+ * dhcp_add_fqdn_opt(): Set CD_CLIENTFQDN option if dsm_reqfqdn is not NULL
+ *			 or if dhcp_assemble_fqdn() initializes it. If dsm_reqfqdn
+ *			 cannot be set, no option will be added.
+ *
+ *   input: dhcp_pkt_t *: pointer to DHCP message being constructed;
+ *	    dhcp_smach_t *: pointer to interface DHCP state machine;
+ *  output: 0 if a CLIENT_FQDN was added; non-zero if otherwise;
+ */
+
+int
+dhcp_add_fqdn_opt(dhcp_pkt_t *dpkt, dhcp_smach_t *dsmp)
+{
+	/*
+	 * RFC 4702 section 2:
+	 *
+	 * The format of the Client FQDN option is:
+	 *
+	 *  Code   Len    Flags  RCODE1 RCODE2   Domain Name
+	 * +------+------+------+------+------+------+--
+	 * |  81  |   n  |      |      |      |       ...
+	 * +------+------+------+------+------+------+--
+	 *
+	 * Code and Len are distinct, and the remainder is in a single buffer,
+	 * opt81, for Flags + (unused) RCODE1 and RCODE2 (all octets) and a
+	 * potentially maximum-length domain name.
+	 *
+	 * The format of the Flags field is:
+	 *
+	 *  0 1 2 3 4 5 6 7
+	 * +-+-+-+-+-+-+-+-+
+	 * |  MBZ  |N|E|O|S|
+	 * +-+-+-+-+-+-+-+-+
+	 *
+	 * where MBZ is ignored and NEOS are:
+	 *
+	 * S = 1 to request that "the server SHOULD perform the A RR (FQDN-to-
+	 * address) DNS updates;
+	 *
+	 * O = 0, for a server-only response bit;
+	 *
+	 * E = 1 to indicate the domain name is in "canonical wire format,
+	 * without compression (i.e., ns_name_pton2) ....  This encoding SHOULD
+	 * be used by clients ....";
+	 *
+	 * N = 0 to request that "the server SHALL perform DNS updates [of the
+	 * PTR RR]." (1 would request SHALL NOT update).
+	 *
+	 * The format of the DHCPv6 Client FQDN option is shown below:
+	 *
+	 *     0                   1                   2                   3
+	 *     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+	 *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+	 *    |          OPTION_FQDN          |         option-len            |
+	 *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+	 *    |   flags       |                                               |
+	 *    +-+-+-+-+-+-+-+-+                                               |
+	 *    .                                                               .
+	 *    .                          domain-name                          .
+	 *    .                                                               .
+	 *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+	 *
+	 *      option-code      OPTION_CLIENT_FQDN (39)
+	 *
+	 *      option-len       1 + length of domain name
+	 *
+	 *      flags            flag bits used between client and server to
+	 *                       negotiate who performs which updates
+	 *
+	 *      domain-name      the partial or fully qualified domain name
+	 *                       (with length option-len - 1)
+	 *
+	 *    The DHCPv6 format of the Flags field is:
+	 *
+	 *         0 1 2 3 4 5 6 7
+	 *        +-+-+-+-+-+-+-+-+
+	 *        |  MBZ    |N|O|S|
+	 *        +-+-+-+-+-+-+-+-+
+	 */
+
+	const uint8_t	S_BITPOS = 7 - 7;
+	const uint8_t	E_BITPOS = 7 - 5;
+	const size_t	OPT_FQDN_METALEN = 3, OPT_V6_FQDN_METALEN = 1;
+	uint_t		fqdncode;
+	u_char		enc_fqdnbuf[MAXNAMELEN];
+	uint8_t		fqdnopt[MAXNAMELEN + OPT_FQDN_METALEN];
+	size_t		len, metalen;
+
+	if (dsmp->dsm_reqfqdn == NULL && dhcp_assemble_fqdn(dsmp) != 0)
+		return (-1);
+
+	/* encode the FQDN in canonical wire format */
+
+	if (ns_name_pton2(dsmp->dsm_reqfqdn, enc_fqdnbuf, sizeof (enc_fqdnbuf),
+	    &len) < 0) {
+		dhcpmsg(MSG_WARNING, "dhcp_add_fqdn_opt: error encoding domain"
+		    " name %s", dsmp->dsm_reqfqdn);
+		return (-1);
+	}
+
+	dhcpmsg(MSG_DEBUG, "dhcp_add_fqdn_opt: interface FQDN is %s"
+	    " for %s", dsmp->dsm_reqfqdn, dsmp->dsm_name);
+
+	bzero(fqdnopt, sizeof (fqdnopt));
+	if (dsmp->dsm_isv6) {
+		fqdncode = DHCPV6_OPT_CLIENT_FQDN;
+		metalen = OPT_V6_FQDN_METALEN;
+		*fqdnopt = (uint8_t)(1 << S_BITPOS);
+	} else {
+		fqdncode = CD_CLIENTFQDN;
+		metalen = OPT_FQDN_METALEN;
+		*fqdnopt = (uint8_t)((1 << S_BITPOS) | (1 << E_BITPOS));
+	}
+	(void) memcpy(fqdnopt + metalen, enc_fqdnbuf, len);
+	(void) add_pkt_opt(dpkt, fqdncode, fqdnopt, metalen + len);
+
+	return (0);
+}
+
+/*
+ * dhcp_assemble_fqdn(): Set dsm_reqfqdn if REQUEST_FQDN is set and
+ *			 either a host name was sent in the IPC message (e.g., from
+ *			 ipadm(1M) -h,--reqhost) or the interface is primary and a
+ *			 nodename(4) is defined. If the host name is not already fully
+ *			 qualified per is_fqdn(), then a value from
+ *			 dhcp_get_defaultdomain() or from resolv.conf(4)--if defined--
+ *			 is used to construct an FQDN.
+ *
+ *			 If no FQDN can be determined, dsm_reqfqdn will be NULL.
+ *
+ *   input: dhcp_smach_t *: pointer to interface DHCP state machine;
+ *  output: 0 if dsm_reqfqdn was assigned; non-zero if otherwise;
+ */
+
+static int
+dhcp_assemble_fqdn(dhcp_smach_t *dsmp)
+{
+	char		fqdnbuf[MAXNAMELEN], nodename[MAXNAMELEN], *reqhost;
+	size_t		pos, len;
+
+	if (dsmp->dsm_reqfqdn != NULL) {
+		free(dsmp->dsm_reqfqdn);
+		dsmp->dsm_reqfqdn = NULL;
+	}
+
+	if (!df_get_bool(dsmp->dsm_name, dsmp->dsm_isv6, DF_REQUEST_FQDN))
+		return (-1);
+
+	dhcpmsg(MSG_DEBUG, "dhcp_assemble_fqdn: DF_REQUEST_FQDN");
+
+	bzero(fqdnbuf, sizeof (fqdnbuf));
+
+	reqhost = dsmp->dsm_msg_reqhost;
+	if (ipadm_is_nil_hostname(reqhost) &&
+	    (dsmp->dsm_dflags & DHCP_IF_PRIMARY) &&
+	    dhcp_get_nodename(nodename, sizeof (nodename)) == 0) {
+		reqhost = nodename;
+	}
+
+	if (ipadm_is_nil_hostname(reqhost)) {
+		dhcpmsg(MSG_DEBUG,
+		    "dhcp_assemble_fqdn: no interface reqhost for %s",
+		    dsmp->dsm_name);
+		return (-1);
+	}
+
+	if ((pos = strlcpy(fqdnbuf, reqhost, sizeof (fqdnbuf))) >=
+	    sizeof (fqdnbuf)) {
+		dhcpmsg(MSG_WARNING, "dhcp_assemble_fqdn: too long reqhost %s"
+		    " for %s", reqhost, dsmp->dsm_name);
+		return (-1);
+	}
+
+	if (!is_fqdn(reqhost)) {
+		char		dnamebuf[MAXHOSTNAMELEN];
+		size_t		needdots;
+		int			lasterrno;
+		char		*domainname = NULL;
+
+		/*
+		 * determine FQDN domain name, if possible
+		 */
+
+		if (dhcp_get_defaultdomain(dnamebuf, sizeof (dnamebuf)) == 0 &&
+		    !ipadm_is_nil_hostname(dnamebuf)) {
+			domainname = dnamebuf;
+		} else {
+			/*
+			 * fall back to resolv's "default domain (deprecated)"
+			 */
+
+			struct __res_state	res_state;
+			bzero(&res_state, sizeof (struct __res_state));
+
+			/* initialize resolver or warn */
+			if ((lasterrno = res_ninit(&res_state)) != 0) {
+				dhcpmsg(MSG_WARNING, "dhcp_assemble_fqdn: error %d"
+				    " initializing resolver", lasterrno);
+			}
+			else {
+				if (*res_state.defdname != '\0') {
+					(void) strlcpy(dnamebuf, res_state.defdname,
+					    sizeof (dnamebuf));
+					domainname = dnamebuf;
+				}
+				res_ndestroy(&res_state);
+			}
+		}
+
+		if (ipadm_is_nil_hostname(domainname)) {
+			dhcpmsg(MSG_DEBUG, "dhcp_assemble_fqdn: no domain name for %s",
+			    dsmp->dsm_name);
+			return (-1);
+		}
+
+		/*
+		 * Finish constructing FQDN. Account for space needed to hold a
+		 * separator '.' and a terminating '.'.
+		 */
+		len = strlen(domainname);
+		needdots = 1 + (domainname[len - 1] != '.');
+
+		if (pos + len + needdots >= sizeof (fqdnbuf)) {
+			dhcpmsg(MSG_WARNING, "dhcp_assemble_fqdn: too long FQDN %s.%s"
+			    " for %s", fqdnbuf, domainname, dsmp->dsm_name);
+			return (-1);
+		}
+
+		/* add separator and then domain name */
+		fqdnbuf[pos++] = '.';
+		(void) strlcpy(fqdnbuf + pos, domainname, sizeof (fqdnbuf) - pos);
+		pos += len;
+
+		/* ensure the final character is '.' */
+		if (needdots > 1)
+			fqdnbuf[pos++] = '.'; /* following is already zeroed */
+	}
+
+	if (!ipadm_is_valid_hostname(fqdnbuf)) {
+		dhcpmsg(MSG_WARNING, "dhcp_assemble_fqdn: invalid FQDN %s for %s",
+		    fqdnbuf, dsmp->dsm_name);
+		return (-1);
+	}
+
+	free(dsmp->dsm_reqfqdn);
+	if ((dsmp->dsm_reqfqdn = strdup(fqdnbuf)) == NULL) {
+		dhcpmsg(MSG_WARNING, "dhcp_assemble_fqdn: cannot allocate memory");
+		return (-1);
+	}
+	return (0);
+}
+
+/*
+ * is_fqdn() : Determine if the `hostname' can be considered as a Fully
+ *			Qualified Domain Name by being "rooted" (i.e., ending in '.')
+ *			or by containing at least three DNS labels (e.g.,
+ *			srv.example.com).
+ *
+ *   input: const char *: the hostname to inspect;
+ *  output: boolean_t: B_TRUE if `hostname' is not NULL satisfies the
+ *			criteria above; otherwise, B_FALSE;
+ */
+
+boolean_t
+is_fqdn(const char *hostname)
+{
+	const char *c;
+	size_t i;
+
+	if (hostname == NULL)
+		return (B_FALSE);
+
+	i = strlen(hostname);
+	if (i > 0 && hostname[i - 1] == '.')
+		return (B_TRUE);
+
+	c = hostname;
+	i = 0;
+	while ((c = strchr(c, '.')) != NULL) {
+		++i;
+		++c;
+	}
+
+	/* at least two separators is inferred to be fully-qualified */
+	return (i >= 2);
 }
--- old/usr/src/cmd/cmd-inet/sbin/dhcpagent/util.h	Mon Nov 14 16:21:15 2016
+++ new/usr/src/cmd/cmd-inet/sbin/dhcpagent/util.h	Mon Nov 14 16:21:15 2016
@@ -20,6 +20,7 @@
  */
 /*
  * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
  */
 
 #ifndef	UTIL_H
@@ -33,6 +34,7 @@
 #include <dhcpagent_ipc.h>
 
 #include "common.h"
+#include "packet.h"
 
 /*
  * general utility functions which have no better home.  see util.c
@@ -75,6 +77,8 @@
 int		dhcpv6_status_code(const dhcpv6_option_t *, uint_t,
     const char **, const char **, uint_t *);
 void		write_lease_to_hostconf(dhcp_smach_t *);
+int			dhcp_add_hostname_opt(dhcp_pkt_t *, dhcp_smach_t *);
+int			dhcp_add_fqdn_opt(dhcp_pkt_t *, dhcp_smach_t *);
 
 #ifdef	__cplusplus
 }
--- old/usr/src/cmd/cmd-inet/usr.sbin/ipadm/ipadm.c	Mon Nov 14 16:21:15 2016
+++ new/usr/src/cmd/cmd-inet/usr.sbin/ipadm/ipadm.c	Mon Nov 14 16:21:15 2016
@@ -21,6 +21,7 @@
 /*
  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
+ * Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
  */
 #include <arpa/inet.h>
 #include <errno.h>
@@ -88,7 +89,8 @@
 	{ "create-addr", do_create_addr,
 	    "\tcreate-addr\t[-t] -T static [-d] "
 	    "-a{local|remote}=addr[/prefixlen]\n\t\t\t<addrobj>\n"
-	    "\tcreate-addr\t[-t] -T dhcp [-w <seconds> | forever] <addrobj>\n"
+	    "\tcreate-addr\t[-t] -T dhcp [-w <seconds> | forever]\n"
+	    "\t\t\t[-1] [-h <hostname>] <addrobj>\n"
 	    "\tcreate-addr\t[-t] -T addrconf [-i interface-id]\n"
 	    "\t\t\t[-p {stateful|stateless}={yes|no}] <addrobj>" },
 	{ "down-addr",	do_down_addr,	"\tdown-addr\t[-t] <addrobj>"	},
@@ -161,7 +163,9 @@
 	{"address",	required_argument,	0, 'a'	},
 	{"down",	no_argument,		0, 'd'	},
 	{"interface-id", required_argument,	0, 'i'	},
+	{"primary",	no_argument,		0, '1'	},
 	{"prop",	required_argument,	0, 'p'	},
+	{"reqhost", required_argument,	0, 'h'	},
 	{"temporary",	no_argument,		0, 't'	},
 	{"type",	required_argument,	0, 'T'	},
 	{"wait",	required_argument,	0, 'w'	},
@@ -632,8 +636,8 @@
 
 /*
  * Properties to be displayed is in `statep->sps_proplist'. If it is NULL,
- * for all the properties for the specified object, relavant information, will
- * be displayed. Otherwise, for the selected property set, display relevant
+ * for all the properties for the specified object, display relevant
+ * information. Otherwise, for the selected property set, display relevant
  * information
  */
 static void
@@ -1258,14 +1262,19 @@
 	char		*addrconf_arg = NULL;
 	char		*interface_id = NULL;
 	char		*wait = NULL;
+	char		*reqhost = NULL;
 	boolean_t	s_opt = _B_FALSE;	/* static addr options */
 	boolean_t	auto_opt = _B_FALSE;	/* Addrconf options */
 	boolean_t	dhcp_opt = _B_FALSE;	/* dhcp options */
+	boolean_t	primary_opt = _B_FALSE;	/* dhcp primary option */
 
 	opterr = 0;
-	while ((option = getopt_long(argc, argv, ":T:a:di:p:w:t",
+	while ((option = getopt_long(argc, argv, ":1T:a:dh:i:p:w:t",
 	    addr_longopts, NULL)) != -1) {
 		switch (option) {
+		case '1':
+			primary_opt = _B_TRUE;
+			break;
 		case 'T':
 			atype = optarg;
 			break;
@@ -1277,6 +1286,9 @@
 			flags &= ~IPADM_OPT_UP;
 			s_opt = _B_TRUE;
 			break;
+		case 'h':
+			reqhost = optarg;
+			break;
 		case 'i':
 			interface_id = optarg;
 			auto_opt = _B_TRUE;
@@ -1308,7 +1320,8 @@
 	 * Allocate and initialize the addrobj based on the address type.
 	 */
 	if (strcmp(atype, "static") == 0) {
-		if (static_arg == NULL || auto_opt || dhcp_opt) {
+		if (static_arg == NULL || auto_opt || dhcp_opt
+		    || reqhost != NULL || primary_opt) {
 			die("Invalid arguments for type %s\nusage: %s",
 			    atype, use);
 		}
@@ -1345,13 +1358,27 @@
 				    ipadm_status2str(status));
 			}
 		}
+		if (primary_opt) {
+			status = ipadm_set_primary(ipaddr, _B_TRUE);
+			if (status != IPADM_SUCCESS) {
+				die("Error in setting primary flag: %s",
+				    ipadm_status2str(status));
+			}
+		}
+		if (reqhost != NULL) {
+			status = ipadm_set_reqhost(ipaddr, reqhost);
+			if (status != IPADM_SUCCESS) {
+				die("Error in setting reqhost: %s",
+				    ipadm_status2str(status));
+			}
+		}
 	} else if (strcmp(atype, "addrconf") == 0) {
-		if (dhcp_opt || s_opt) {
+		if (dhcp_opt || s_opt || reqhost != NULL || primary_opt) {
 			die("Invalid arguments for type %s\nusage: %s",
 			    atype, use);
 		}
 
-		/* Initialize the addrobj for dhcp addresses. */
+		/* Initialize the addrobj for ipv6-addrconf addresses. */
 		status = ipadm_create_addrobj(IPADM_ADDR_IPV6_ADDRCONF,
 		    argv[optind], &ipaddr);
 		if (status != IPADM_SUCCESS) {
--- old/usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/Makefile	Mon Nov 14 16:21:15 2016
+++ new/usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/Makefile	Mon Nov 14 16:21:15 2016
@@ -22,6 +22,7 @@
 #
 # Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
+# Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
 #
 # cmd/cmd-inet/usr.sbin/nwamcfg/Makefile
 
@@ -56,7 +57,7 @@
 nwamcfg_lex.c:	nwamcfg_lex.l nwamcfg_grammar.tab.h nwamcfg.h
 	$(LEX) $(LFLAGS) nwamcfg_lex.l > $@
 
-nwamcfg_grammar.tab.h nwamcfg_grammar.tab.c:	nwamcfg_grammar.y nwamcfg.h
+nwamcfg_grammar.tab.h + nwamcfg_grammar.tab.c:	nwamcfg_grammar.y nwamcfg.h
 	$(YACC) $(YFLAGS) nwamcfg_grammar.y
 
 nwamcfg_lex.o nwamcfg_grammar.tab.o := CCVERBOSE =
--- old/usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/nwamcfg.c	Mon Nov 14 16:21:15 2016
+++ new/usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/nwamcfg.c	Mon Nov 14 16:21:15 2016
@@ -21,6 +21,7 @@
 
 /*
  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
  */
 
 /*
@@ -198,7 +199,9 @@
 	NWAM_KNOWN_WLAN_PROP_PRIORITY,
 	NWAM_KNOWN_WLAN_PROP_KEYNAME,
 	NWAM_KNOWN_WLAN_PROP_KEYSLOT,
-	NWAM_KNOWN_WLAN_PROP_SECURITY_MODE
+	NWAM_KNOWN_WLAN_PROP_SECURITY_MODE,
+	NWAM_NCU_PROP_IP_PRIMARY,
+	NWAM_NCU_PROP_IP_REQHOST
 };
 
 /* properties table: maps PT_* constants to property names */
@@ -226,6 +229,8 @@
 	{ PT_IPV6_ADDRSRC, 		NWAM_NCU_PROP_IPV6_ADDRSRC },
 	{ PT_IPV6_ADDR, 		NWAM_NCU_PROP_IPV6_ADDR },
 	{ PT_IPV6_DEFAULT_ROUTE,	NWAM_NCU_PROP_IPV6_DEFAULT_ROUTE },
+	{ PT_IP_PRIMARY, 		NWAM_NCU_PROP_IP_PRIMARY },
+	{ PT_IP_REQHOST, 		NWAM_NCU_PROP_IP_REQHOST },
 	{ 0, NULL }
 };
 
@@ -664,11 +669,15 @@
 	return (pt_types[prop_type]);
 }
 
-/* Return B_TRUE if string starts with "t" or is 1, B_FALSE otherwise */
+/*
+ * Return B_TRUE if string starts with "t" or "on" or is 1;
+ * B_FALSE otherwise
+ */
 static boolean_t
 str_to_boolean(const char *str)
 {
-	if (strncasecmp(str, "t", 1) == 0 || atoi(str) == 1)
+	if (strncasecmp(str, "t", 1) == 0 || strncasecmp(str, "on", 2) == 0 ||
+	    atoi(str) == 1)
 		return (B_TRUE);
 	else
 		return (B_FALSE);
@@ -2197,6 +2206,18 @@
 	/* show ipv6-default-route if ip-version == ipv6 */
 	{ NWAM_NCU_PROP_IPV6_DEFAULT_ROUTE, NWAM_NCU_PROP_IP_VERSION,
 	    { IPV6_VERSION, -1 } },
+	/* show ip-primary if ipv4-addrsrc == dhcp */
+	{ NWAM_NCU_PROP_IP_PRIMARY, NWAM_NCU_PROP_IPV4_ADDRSRC,
+	    { NWAM_ADDRSRC_DHCP, -1 } },
+	/* show ip-primary if ipv6-addrsrc == dhcp */
+	{ NWAM_NCU_PROP_IP_PRIMARY, NWAM_NCU_PROP_IPV6_ADDRSRC,
+	    { NWAM_ADDRSRC_DHCP, -1 } },
+	/* show ip-reqhost if ipv4-addrsrc == dhcp */
+	{ NWAM_NCU_PROP_IP_REQHOST, NWAM_NCU_PROP_IPV4_ADDRSRC,
+	    { NWAM_ADDRSRC_DHCP, -1 } },
+	/* show ip-reqhost if ipv6-addrsrc == dhcp */
+	{ NWAM_NCU_PROP_IP_REQHOST, NWAM_NCU_PROP_IPV6_ADDRSRC,
+	    { NWAM_ADDRSRC_DHCP, -1 } },
 	{ NULL, NULL, { -1 } }
 };
 
--- old/usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/nwamcfg.h	Mon Nov 14 16:21:16 2016
+++ new/usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/nwamcfg.h	Mon Nov 14 16:21:16 2016
@@ -22,6 +22,7 @@
 /*
  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
  */
 
 #ifndef _NWAMCFG_H
@@ -133,12 +134,14 @@
 #define	PT_WLAN_KEYNAME		42
 #define	PT_WLAN_KEYSLOT		43
 #define	PT_WLAN_SECURITY_MODE	44
+#define	PT_IP_PRIMARY		45
+#define	PT_IP_REQHOST		46
 /*
  * If any new PT_ are defined here, make sure it is added in the same
  * order into the pt_types array in nwamcfg.c
  */
 #define	PT_MIN			PT_UNKNOWN
-#define	PT_MAX			PT_WLAN_SECURITY_MODE
+#define	PT_MAX			PT_IP_REQHOST
 
 #define	MAX_SUBCMD_ARGS	3
 
--- old/usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/nwamcfg_grammar.y	Mon Nov 14 16:21:16 2016
+++ new/usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/nwamcfg_grammar.y	Mon Nov 14 16:21:16 2016
@@ -23,6 +23,7 @@
 /*
  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
  */
 
 #include <stdio.h>
@@ -67,6 +68,7 @@
 %token LOC_IPF_CONFIG LOC_IPF_V6_CONFIG
 %token LOC_IPNAT_CONFIG LOC_IPPOOL_CONFIG LOC_IKE_CONFIG LOC_IPSECPOL_CONFIG
 %token WLAN_BSSIDS WLAN_PRIORITY WLAN_KEYNAME WLAN_KEYSLOT WLAN_SECURITY_MODE
+%token IP_PRIMARY IP_REQHOST
 
 %type <strval> TOKEN EQUAL OPTION
 %type <ival> resource1_type LOC NCP ENM WLAN
@@ -86,6 +88,7 @@
     LOC_IPF_CONFIG LOC_IPF_V6_CONFIG
     LOC_IPNAT_CONFIG LOC_IPPOOL_CONFIG LOC_IKE_CONFIG LOC_IPSECPOL_CONFIG
     WLAN_BSSIDS WLAN_PRIORITY WLAN_KEYNAME WLAN_KEYSLOT WLAN_SECURITY_MODE
+    IP_PRIMARY IP_REQHOST
 %type <cmd> command
 %type <cmd> cancel_command CANCEL
 %type <cmd> clear_command CLEAR
@@ -617,11 +620,26 @@
 		command_usage(CMD_LIST);
 		YYERROR;
 	}
+	|	LIST OPTION resource1_type
+	{
+		command_usage(CMD_LIST);
+		YYERROR;
+	}
 	|	LIST resource2_type
 	{
 		command_usage(CMD_LIST);
 		YYERROR;
 	}
+	|	LIST OPTION resource2_type
+	{
+		command_usage(CMD_LIST);
+		YYERROR;
+	}
+	|	LIST OPTION resource2_type ncu_class_type
+	{
+		command_usage(CMD_LIST);
+		YYERROR;
+	}
 	|	LIST resource1_type TOKEN
 	{
 		/* list enm/loc/ncp test */
@@ -743,6 +761,11 @@
 		command_usage(CMD_SELECT);
 		YYERROR;
 	}
+	|	SELECT resource2_type ncu_class_type
+	{
+		command_usage(CMD_SELECT);
+		YYERROR;
+	}
 	|	SELECT resource1_type TOKEN
 	{
 		/* select enm/loc/ncp test */
@@ -900,5 +923,7 @@
 	|	WLAN_KEYNAME		{ $$ = PT_WLAN_KEYNAME; }
 	|	WLAN_KEYSLOT		{ $$ = PT_WLAN_KEYSLOT; }
 	|	WLAN_SECURITY_MODE	{ $$ = PT_WLAN_SECURITY_MODE; }
+	|	IP_PRIMARY		{ $$ = PT_IP_PRIMARY; }
+	|	IP_REQHOST		{ $$ = PT_IP_REQHOST; }
 
 %%
--- old/usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/nwamcfg_lex.l	Mon Nov 14 16:21:16 2016
+++ new/usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/nwamcfg_lex.l	Mon Nov 14 16:21:16 2016
@@ -23,6 +23,7 @@
 /*
  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
  */
 
 #include <string.h>
@@ -182,6 +183,8 @@
 <TSTATE>ipv6-addrsrc		{ return IPV6_ADDRSRC; }
 <TSTATE>ipv6-addr		{ return IPV6_ADDR; }
 <TSTATE>ipv6-default-route	{ return IPV6_DEFAULT_ROUTE; }
+<TSTATE>ip-primary		{ return IP_PRIMARY; }
+<TSTATE>ip-reqhost		{ return IP_REQHOST; }
 
 <TSTATE>state		{ return ENM_STATE; }
 <TSTATE>fmri		{ return ENM_FMRI; }
--- old/usr/src/common/net/dhcp/dhcp_impl.h	Mon Nov 14 16:21:16 2016
+++ new/usr/src/common/net/dhcp/dhcp_impl.h	Mon Nov 14 16:21:16 2016
@@ -21,6 +21,7 @@
 /*
  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
  */
 
 #ifndef	_DHCP_IMPL_H
@@ -94,6 +95,11 @@
 	uint8_t    value[1];
 } DHCP_OPT;
 
+/*
+ * Defines the size of DHCP_OPT code + len
+ */
+#define DHCP_OPT_META_LEN	2
+
 typedef union sockaddr46_s {
 	struct sockaddr_in v4;
 	struct sockaddr_in6 v6;
--- old/usr/src/head/arpa/nameser.h	Mon Nov 14 16:21:16 2016
+++ new/usr/src/head/arpa/nameser.h	Mon Nov 14 16:21:16 2016
@@ -1,6 +1,7 @@
 /*
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
  */
 
 /*
@@ -576,6 +577,7 @@
 #define	ns_name_ntol		__ns_name_ntol
 #define	ns_name_ntop		__ns_name_ntop
 #define	ns_name_pton		__ns_name_pton
+#define	ns_name_pton2		__ns_name_pton2
 #define	ns_name_unpack		__ns_name_unpack
 #define	ns_name_pack		__ns_name_pack
 #define	ns_name_compress	__ns_name_compress
@@ -632,6 +634,7 @@
 int		ns_name_ntol(const uchar_t *, uchar_t *, size_t);
 int		ns_name_ntop(const uchar_t *, char *, size_t);
 int		ns_name_pton(const char *, uchar_t *, size_t);
+int		ns_name_pton2(const char *, uchar_t *, size_t, size_t *);
 int		ns_name_unpack(const uchar_t *, const uchar_t *,
 				    const uchar_t *, uchar_t *, size_t);
 int		ns_name_pack(const uchar_t *, uchar_t *, int,
--- old/usr/src/lib/Makefile	Mon Nov 14 16:21:16 2016
+++ new/usr/src/lib/Makefile	Mon Nov 14 16:21:16 2016
@@ -28,6 +28,7 @@
 # Copyright (c) 2015 Gary Mills
 # Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
 # Copyright 2016 Toomas Soome <tsoome@me.com>
+# Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
 #
 
 include ../Makefile.master
@@ -616,7 +617,7 @@
 libidmap:	libavl libuutil
 libinetsvc:	libscf
 libinstzones:	libzonecfg libcontract
-libipadm:	libinetutil libdlpi libdhcpagent libdladm libsecdb
+libipadm:	libinetutil libdlpi libdhcpagent libdladm libsecdb libdhcputil
 libipmp:	libinetutil
 libipsecutil:	libtecla libtsol
 libiscsit:	libstmf libuuid
@@ -626,7 +627,7 @@
 libmapid:	libresolv2 libscf
 libndmp:	libscf
 libnisdb:	libldap5
-libnwam:	libscf libbsm libdladm
+libnwam:	libscf libbsm libdladm libipadm
 libpcp:		libumem libdevinfo
 libpctx:	libproc
 libpkg:		libwanboot libscf libadm
--- old/usr/src/lib/libipadm/Makefile.com	Mon Nov 14 16:21:16 2016
+++ new/usr/src/lib/libipadm/Makefile.com	Mon Nov 14 16:21:16 2016
@@ -20,6 +20,7 @@
 #
 #
 # Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
 #
 #
 
@@ -35,7 +36,7 @@
 
 LIBS =		$(DYNLIB) $(LINTLIB)
 LDLIBS +=	-lc -lnsl -linetutil -lsocket -ldlpi -lnvpair -ldhcpagent \
-	        -ldladm -lsecdb
+	        -ldladm -lsecdb -ldhcputil
 
 SRCDIR =	../common
 $(LINTLIB) :=	SRCS = $(SRCDIR)/$(LINTSRC)
--- old/usr/src/lib/libipadm/common/ipadm_addr.c	Mon Nov 14 16:21:16 2016
+++ new/usr/src/lib/libipadm/common/ipadm_addr.c	Mon Nov 14 16:21:16 2016
@@ -21,6 +21,7 @@
 /*
  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2013 by Delphix. All rights reserved.
+ * Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
  */
 
 /*
@@ -32,6 +33,7 @@
  */
 #include <sys/types.h>
 #include <sys/socket.h>
+#include <sys/param.h>
 #include <netdb.h>
 #include <inet/ip.h>
 #include <string.h>
@@ -48,6 +50,8 @@
 #include <ctype.h>
 #include <dhcpagent_util.h>
 #include <dhcpagent_ipc.h>
+#include <dhcp_inittab.h>
+#include <dhcp_symbol.h>
 #include <ipadm_ndpd.h>
 #include <libdladm.h>
 #include <libdllink.h>
@@ -64,6 +68,7 @@
 			    uint32_t);
 static ipadm_status_t	i_ipadm_delete_dhcp(ipadm_handle_t, ipadm_addrobj_t,
 			    boolean_t);
+static ipadm_status_t	i_ipadm_refresh_dhcp(ipadm_addrobj_t);
 static ipadm_status_t	i_ipadm_get_db_addr(ipadm_handle_t, const char *,
 			    const char *, nvlist_t **);
 static ipadm_status_t	i_ipadm_op_dhcp(ipadm_addrobj_t, dhcp_ipc_type_t,
@@ -77,6 +82,7 @@
 static ipadm_status_t	i_ipadm_get_static_addr_db(ipadm_handle_t,
 			    ipadm_addrobj_t);
 static boolean_t	i_ipadm_is_user_aobjname_valid(const char *);
+static ipadm_prop_desc_t *	i_ipadm_get_addrprop_desc(const char *pname);
 
 /*
  * Callback functions to retrieve property values from the kernel. These
@@ -85,16 +91,21 @@
  * for a given property.
  */
 static ipadm_pd_getf_t	i_ipadm_get_prefixlen, i_ipadm_get_addr_flag,
-			i_ipadm_get_zone, i_ipadm_get_broadcast;
+			i_ipadm_get_zone, i_ipadm_get_broadcast, i_ipadm_get_primary,
+			i_ipadm_get_reqhost;
 
 /*
  * Callback functions to set property values. These functions translate the
  * values to a format suitable for kernel consumption, allocate the necessary
- * ioctl buffers and then invoke ioctl().
+ * ioctl buffers and then invoke ioctl(); or in the case of reqhost, get the
+ * collaborating agent to set the value.
  */
 static ipadm_pd_setf_t	i_ipadm_set_prefixlen, i_ipadm_set_addr_flag,
-			i_ipadm_set_zone;
+			i_ipadm_set_zone, i_ipadm_set_reqhost;
 
+static ipadm_status_t	i_ipadm_set_aobj_addrprop(ipadm_handle_t iph,
+			ipadm_addrobj_t ipaddr, uint_t flags, const char *propname);
+
 /* address properties description table */
 ipadm_prop_desc_t ipadm_addrprop_table[] = {
 	{ "broadcast", NULL, IPADMPROP_CLASS_ADDR, MOD_PROTO_NONE, 0,
@@ -104,13 +115,24 @@
 	    i_ipadm_set_addr_flag, i_ipadm_get_onoff,
 	    i_ipadm_get_addr_flag },
 
-	{ "prefixlen", NULL, IPADMPROP_CLASS_ADDR, MOD_PROTO_NONE, 0,
+	{ IPADM_NVP_PREFIXLEN, NULL, IPADMPROP_CLASS_ADDR, MOD_PROTO_NONE, 0,
 	    i_ipadm_set_prefixlen, i_ipadm_get_prefixlen,
 	    i_ipadm_get_prefixlen },
 
+	/*
+	 * primary is read-only because there is no operation to un-set
+	 * DHCP_IF_PRIMARY in dhcpagent except to delete-addr and then
+	 * re-create-addr.
+	 */
+	{ "primary", NULL, IPADMPROP_CLASS_ADDR, MOD_PROTO_NONE, 0,
+		NULL, NULL, i_ipadm_get_primary },
+
 	{ "private", NULL, IPADMPROP_CLASS_ADDR, MOD_PROTO_NONE, 0,
 	    i_ipadm_set_addr_flag, i_ipadm_get_onoff, i_ipadm_get_addr_flag },
 
+	{ IPADM_NVP_REQHOST, NULL, IPADMPROP_CLASS_ADDR, MOD_PROTO_NONE, 0,
+	    i_ipadm_set_reqhost, NULL, i_ipadm_get_reqhost },
+
 	{ "transmit", NULL, IPADMPROP_CLASS_ADDR, MOD_PROTO_NONE, 0,
 	    i_ipadm_set_addr_flag, i_ipadm_get_onoff, i_ipadm_get_addr_flag },
 
@@ -201,9 +223,17 @@
 	ipaddr->ipadm_atype = rval.ir_atype;
 	ipaddr->ipadm_af = rval.ir_family;
 	ipaddr->ipadm_flags = rval.ir_flags;
-	if (rval.ir_atype == IPADM_ADDR_IPV6_ADDRCONF) {
-		(void) memcpy(&ipaddr->ipadm_intfid, &rval.ir_ifid,
-		    sizeof (ipaddr->ipadm_intfid));
+	switch (rval.ir_atype) {
+	case IPADM_ADDR_IPV6_ADDRCONF:
+		ipaddr->ipadm_intfid = rval.ipmgmt_ir_intfid;
+		break;
+	case IPADM_ADDR_DHCP:
+		ipaddr->ipadm_primary = rval.ipmgmt_ir_primary;
+		(void) strlcpy(ipaddr->ipadm_reqhost, rval.ipmgmt_ir_reqhost,
+		    sizeof (ipaddr->ipadm_reqhost));
+		break;
+	default:
+		break;
 	}
 
 	return (IPADM_SUCCESS);
@@ -1041,6 +1071,84 @@
 }
 
 /*
+ * Callback function that sets the property `reqhost' on the address
+ * object in `arg' to the value in `pval'.
+ */
+/* ARGSUSED */
+static ipadm_status_t
+i_ipadm_set_reqhost(ipadm_handle_t iph, const void *arg,
+    ipadm_prop_desc_t *pdp, const void *pval, uint_t af, uint_t flags)
+{
+	ipadm_status_t	status;
+	ipadm_addrobj_t		ipaddr = (ipadm_addrobj_t)arg;
+
+	if (ipaddr->ipadm_atype != IPADM_ADDR_DHCP)
+		return (IPADM_NOTSUP);
+
+	/*
+	 * If requested to set reqhost just from active config but the
+	 * address is not in active config, return error.
+	 */
+	if (!(ipaddr->ipadm_flags & IPMGMT_ACTIVE) &&
+	    (flags & IPADM_OPT_ACTIVE) && !(flags & IPADM_OPT_PERSIST)) {
+		return (IPADM_NOTFOUND);
+	}
+
+	status = ipadm_set_reqhost(ipaddr, pval);
+	if (status != IPADM_SUCCESS)
+		return (status);
+
+	if (ipaddr->ipadm_flags & IPMGMT_ACTIVE) {
+		status = i_ipadm_refresh_dhcp(ipaddr);
+
+		/*
+		 * We do not report a problem for IPADM_DHCP_IPC_TIMEOUT since it is
+		 * only a soft error to indicate the caller that the lease might be
+		 * renewed after the function returns.
+		 */
+		if (status != IPADM_SUCCESS && status != IPADM_DHCP_IPC_TIMEOUT)
+			return (status);
+	}
+
+	status = i_ipadm_set_aobj_addrprop(iph, ipaddr, flags,
+	    IPADM_NVP_REQHOST);
+	return (status);
+}
+
+/*
+ * Used by address object property callback functions that need to do a
+ * two-stage update because the addrprop is cached on the address object.
+ */
+static ipadm_status_t
+i_ipadm_set_aobj_addrprop(ipadm_handle_t iph, ipadm_addrobj_t ipaddr,
+			uint_t flags, const char *propname)
+{
+	ipadm_status_t	status;
+	uint32_t		two_stage_flags;
+
+	/*
+	 * Send the updated address object information to ipmgmtd, since the
+	 * cached version of an addrprop resides on an aobjmap, but do
+	 * not change the ACTIVE/PERSIST state of the aobjmap. Instead, request
+	 * a two-stage, SET_PROPS update with ACTIVE/PERSIST as the first stage
+	 * per the existing aobjmap flags and a second stage encoded in
+	 * IPADM_OPT_PERSIST_PROPS.
+	 */
+	two_stage_flags = (flags | IPADM_OPT_SET_PROPS)
+	    & ~(IPADM_OPT_ACTIVE | IPADM_OPT_PERSIST);
+	if (ipaddr->ipadm_flags & IPMGMT_ACTIVE)
+		two_stage_flags |= IPADM_OPT_ACTIVE;
+	if (ipaddr->ipadm_flags & IPMGMT_PERSIST)
+		two_stage_flags |= IPADM_OPT_PERSIST;
+	if (flags & IPADM_OPT_PERSIST)
+		two_stage_flags |= IPADM_OPT_PERSIST_PROPS;
+
+	status = i_ipadm_addr_persist(iph, ipaddr, B_FALSE, two_stage_flags,
+	    propname);
+	return (status);
+}
+
+/*
  * Callback function that gets the property `broadcast' for the address
  * object in `arg'.
  */
@@ -1371,6 +1479,78 @@
 	return (IPADM_SUCCESS);
 }
 
+/*
+ * Callback function that retrieves the value of the property `primary'
+ * for the address object in `arg'.
+ */
+/* ARGSUSED */
+static ipadm_status_t
+i_ipadm_get_primary(ipadm_handle_t iph, const void *arg,
+    ipadm_prop_desc_t *pdp, char *buf, uint_t *bufsize, uint_t af,
+    uint_t valtype)
+{
+	ipadm_addrobj_t	ipaddr = (ipadm_addrobj_t)arg;
+	const char *	onoff = "";
+	size_t		nbytes;
+
+	switch (valtype) {
+	case MOD_PROP_DEFAULT:
+		if (ipaddr->ipadm_atype == IPADM_ADDR_DHCP)
+			onoff = IPADM_OFFSTR;
+		break;
+	case MOD_PROP_ACTIVE:
+		if (ipaddr->ipadm_atype == IPADM_ADDR_DHCP)
+			onoff = ipaddr->ipadm_primary ? IPADM_ONSTR : IPADM_OFFSTR;
+		break;
+	default:
+		return (IPADM_INVALID_ARG);
+	}
+
+	nbytes = strlcpy(buf, onoff, *bufsize);
+	if (nbytes >= *bufsize) {
+		/* insufficient buffer space */
+		*bufsize = nbytes + 1;
+		return (IPADM_NO_BUFS);
+	}
+
+	return (IPADM_SUCCESS);
+}
+
+/*
+ * Callback function that retrieves the value of the property `reqhost'
+ * for the address object in `arg'.
+ */
+/* ARGSUSED */
+static ipadm_status_t
+i_ipadm_get_reqhost(ipadm_handle_t iph, const void *arg,
+    ipadm_prop_desc_t *pdp, char *buf, uint_t *bufsize, uint_t af,
+    uint_t valtype)
+{
+	ipadm_addrobj_t	ipaddr = (ipadm_addrobj_t)arg;
+	const char *	reqhost = "";
+	size_t		nbytes;
+
+	switch (valtype) {
+	case MOD_PROP_DEFAULT:
+		break;
+	case MOD_PROP_ACTIVE:
+		if (ipaddr->ipadm_atype == IPADM_ADDR_DHCP)
+			reqhost = ipaddr->ipadm_reqhost;
+		break;
+	default:
+		return (IPADM_INVALID_ARG);
+	}
+
+	nbytes = strlcpy(buf, reqhost, *bufsize);
+	if (nbytes >= *bufsize) {
+		/* insufficient buffer space */
+		*bufsize = nbytes + 1;
+		return (IPADM_NO_BUFS);
+	}
+
+	return (IPADM_SUCCESS);
+}
+
 static ipadm_prop_desc_t *
 i_ipadm_get_addrprop_desc(const char *pname)
 {
@@ -1775,6 +1955,7 @@
 
 	return (IPADM_SUCCESS);
 }
+
 /*
  * Set up tunnel destination address in ipaddr by contacting DNS.
  * The function works similar to ipadm_set_addr().
@@ -1901,6 +2082,28 @@
 }
 
 /*
+ * Sets the dhcp parameter `ipadm_reqhost' in the address object `ipaddr',
+ * but validate any non-nil value using ipadm_is_valid_hostname() and also
+ * check length.
+ */
+ipadm_status_t
+ipadm_set_reqhost(ipadm_addrobj_t ipaddr, const char *reqhost)
+{
+	const size_t HNLEN = sizeof (ipaddr->ipadm_reqhost);
+
+	if (ipaddr == NULL || ipaddr->ipadm_atype != IPADM_ADDR_DHCP)
+		return (IPADM_INVALID_ARG);
+
+	if (ipadm_is_nil_hostname(reqhost))
+		*ipaddr->ipadm_reqhost = '\0';
+	else if (!ipadm_is_valid_hostname(reqhost))
+		return (IPADM_INVALID_ARG);
+	else if (strlcpy(ipaddr->ipadm_reqhost, reqhost, HNLEN) >= HNLEN)
+		return (IPADM_INVALID_ARG);
+	return (IPADM_SUCCESS);
+}
+
+/*
  * Creates a placeholder for the `ipadm_aobjname' in the ipmgmtd `aobjmap'.
  * If the `aobjname' already exists in the daemon's `aobjmap' then
  * IPADM_ADDROBJ_EXISTS will be returned.
@@ -2050,14 +2253,15 @@
 ipadm_status_t
 i_ipadm_enable_dhcp(ipadm_handle_t iph, const char *ifname, nvlist_t *nvl)
 {
-	int32_t			wait;
-	boolean_t		primary;
-	nvlist_t		*nvdhcp;
+	int32_t			wait = IPADM_DHCP_WAIT_DEFAULT;
+	boolean_t		primary = B_FALSE;
+	nvlist_t		*nvdhcp = NULL;
 	nvpair_t		*nvp;
 	char			*name;
 	struct ipadm_addrobj_s	ipaddr;
-	char			*aobjname;
+	char			*aobjname = NULL, *reqhost = NULL;
 	int			err = 0;
+	ipadm_status_t	ipadm_err = IPADM_SUCCESS;
 
 	/* Extract the dhcp parameters */
 	for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL;
@@ -2067,6 +2271,8 @@
 			err = nvpair_value_nvlist(nvp, &nvdhcp);
 		else if (strcmp(name, IPADM_NVP_AOBJNAME) == 0)
 			err = nvpair_value_string(nvp, &aobjname);
+		else if (strcmp(name, IPADM_NVP_REQHOST) == 0)
+			err = nvpair_value_string(nvp, &reqhost);
 		if (err != 0)
 			return (ipadm_errno2status(err));
 	}
@@ -2088,6 +2294,9 @@
 		ipaddr.ipadm_wait = 0;
 	else
 		ipaddr.ipadm_wait = wait;
+	ipadm_err = ipadm_set_reqhost(&ipaddr, reqhost);
+	if (ipadm_err != IPADM_SUCCESS)
+		return ipadm_err;
 	ipaddr.ipadm_af = AF_INET;
 	return (i_ipadm_create_dhcp(iph, &ipaddr, IPADM_OPT_ACTIVE));
 }
@@ -2730,7 +2939,7 @@
 			}
 		}
 		status = i_ipadm_addr_persist(iph, ipaddr, default_prefixlen,
-		    flags);
+		    flags, NULL);
 	}
 ret:
 	if (status != IPADM_SUCCESS && !legacy)
@@ -2887,7 +3096,7 @@
 	dh_status = status;
 
 	/* Persist the address object information in ipmgmtd. */
-	status = i_ipadm_addr_persist(iph, addr, B_FALSE, flags);
+	status = i_ipadm_addr_persist(iph, addr, B_FALSE, flags, NULL);
 	if (status != IPADM_SUCCESS)
 		goto fail;
 
@@ -2947,6 +3156,10 @@
 {
 	dhcp_ipc_request_t	*request;
 	dhcp_ipc_reply_t	*reply	= NULL;
+	dhcp_symbol_t		*entry = NULL;
+	dhcp_data_type_t	dtype = DHCP_TYPE_NONE;
+	void				*d4o = NULL;
+	uint16_t			d4olen = 0;
 	char			ifname[LIFNAMSIZ];
 	int			error;
 	int			dhcp_timeout;
@@ -2956,9 +3169,38 @@
 	i_ipadm_addrobj2lifname(addr, ifname, sizeof (ifname));
 	if (addr->ipadm_primary)
 		type |= DHCP_PRIMARY;
-	request = dhcp_ipc_alloc_request(type, ifname, NULL, 0, DHCP_TYPE_NONE);
-	if (request == NULL)
+
+	/* Set up a CD_HOSTNAME option, if applicable, to send through IPC */
+	switch (DHCP_IPC_CMD(type)) {
+	case DHCP_START:
+	case DHCP_EXTEND:
+		if (addr->ipadm_af == AF_INET && addr->ipadm_reqhost != NULL
+		    && *addr->ipadm_reqhost != '\0')
+		{
+			entry = inittab_getbycode(ITAB_CAT_STANDARD, ITAB_CONS_INFO,
+			    CD_HOSTNAME);
+			if (entry == NULL) {
+				return (IPADM_FAILURE);
+			} else {
+				d4o = inittab_encode(entry, addr->ipadm_reqhost, &d4olen,
+				    B_FALSE);
+				free(entry);
+				entry = NULL;
+				if (d4o == NULL)
+					return (IPADM_FAILURE);
+				dtype = DHCP_TYPE_OPTION;
+			}
+		}
+		break;
+	default:
+		break;
+	}
+
+	request = dhcp_ipc_alloc_request(type, ifname, d4o, d4olen, dtype);
+	if (request == NULL) {
+		free(d4o);
 		return (IPADM_NO_MEMORY);
+	}
 
 	if (addr->ipadm_wait == IPADM_DHCP_WAIT_FOREVER)
 		dhcp_timeout = DHCP_IPC_WAIT_FOREVER;
@@ -2969,6 +3211,7 @@
 	/* Send the message to dhcpagent. */
 	error = dhcp_ipc_make_request(request, &reply, dhcp_timeout);
 	free(request);
+	free(d4o);
 	if (error == 0) {
 		error = reply->return_code;
 		free(reply);
@@ -3026,21 +3269,19 @@
 
 /*
  * Makes a door call to ipmgmtd to update its `aobjmap' with the address
- * object in `ipaddr'. This door call also updates the persistent DB to
+ * object in `ipaddr'. This door call also can update the persistent DB to
  * remember address object to be recreated on next reboot or on an
  * ipadm_enable_addr()/ipadm_enable_if() call.
  */
 ipadm_status_t
 i_ipadm_addr_persist(ipadm_handle_t iph, const ipadm_addrobj_t ipaddr,
-    boolean_t default_prefixlen, uint32_t flags)
+    boolean_t default_prefixlen, uint32_t flags, const char *propname)
 {
 	char			*aname = ipaddr->ipadm_aobjname;
 	nvlist_t		*nvl;
 	int			err = 0;
 	ipadm_status_t		status;
-	char			pval[MAXPROPVALLEN];
 	uint_t			pflags = 0;
-	ipadm_prop_desc_t	*pdp = NULL;
 
 	/*
 	 * Construct the nvl to send to the door.
@@ -3060,8 +3301,6 @@
 		status = i_ipadm_add_ipaddr2nvl(nvl, ipaddr);
 		if (status != IPADM_SUCCESS)
 			goto ret;
-		(void) snprintf(pval, sizeof (pval), "%d",
-		    ipaddr->ipadm_static_prefixlen);
 		if (flags & IPADM_OPT_UP)
 			err = nvlist_add_string(nvl, "up", "yes");
 		else
@@ -3071,6 +3310,18 @@
 	case IPADM_ADDR_DHCP:
 		status = i_ipadm_add_dhcp2nvl(nvl, ipaddr->ipadm_primary,
 		    ipaddr->ipadm_wait);
+		if (status != IPADM_SUCCESS)
+			goto ret;
+
+		/*
+		 * For purposes of updating the ipmgmtd cached representation of
+		 * reqhost (ipmgmt_am_reqhost), include a value here in `nvl', but
+		 * the value is actually fully persisted as a separate
+		 * i_ipadm_persist_propval below.
+		 */
+		err = nvlist_add_string(nvl, IPADM_NVP_REQHOST,
+		    ipaddr->ipadm_reqhost);
+		status = ipadm_errno2status(err);
 		break;
 	case IPADM_ADDR_IPV6_ADDRCONF:
 		status = i_ipadm_add_intfid2nvl(nvl, ipaddr);
@@ -3094,25 +3345,62 @@
 			pflags |= IPMGMT_ACTIVE;
 		if (flags & IPADM_OPT_PERSIST)
 			pflags |= IPMGMT_PERSIST;
+		if (flags & IPADM_OPT_SET_PROPS)
+			pflags |= IPMGMT_PROPS_ONLY;
 	}
 	status = i_ipadm_addr_persist_nvl(iph, nvl, pflags);
-	/*
-	 * prefixlen is stored in a separate line in the DB and not along
-	 * with the address itself, since it is also an address property and
-	 * all address properties are stored in separate lines. We need to
-	 * persist the prefixlen by calling the function that persists
-	 * address properties.
-	 */
-	if (status == IPADM_SUCCESS && !default_prefixlen &&
-	    ipaddr->ipadm_atype == IPADM_ADDR_STATIC &&
-	    (flags & IPADM_OPT_PERSIST)) {
-		for (pdp = ipadm_addrprop_table; pdp->ipd_name != NULL; pdp++) {
-			if (strcmp("prefixlen", pdp->ipd_name) == 0)
-				break;
+
+	if (flags & IPADM_OPT_SET_PROPS) {
+		/*
+		 * Set PERSIST per IPADM_OPT_PROPS_PERSIST, and then un-set the
+		 * SET_PROPS bits.
+		 */
+		flags |= IPADM_OPT_ACTIVE;
+		if (flags & IPADM_OPT_PERSIST_PROPS)
+			flags |= IPADM_OPT_PERSIST;
+		else
+			flags &= ~IPADM_OPT_PERSIST;
+		flags &= ~(IPADM_OPT_SET_PROPS | IPADM_OPT_PERSIST_PROPS);
+	}
+
+	if (status == IPADM_SUCCESS && (flags & IPADM_OPT_PERSIST)) {
+		char	pbuf[MAXPROPVALLEN], *pval = NULL;
+		ipadm_prop_desc_t	*pdp = NULL;
+
+		/*
+		 * addprop properties are stored on separate lines in the DB and
+		 * not along with the address itself. Call the function that
+		 * persists address properties.
+		 */
+
+		switch (ipaddr->ipadm_atype) {
+		case IPADM_ADDR_STATIC:
+			if (!default_prefixlen && (propname == NULL ||
+			    strcmp(propname, IPADM_NVP_PREFIXLEN) == 0)) {
+				pdp = i_ipadm_get_addrprop_desc(IPADM_NVP_PREFIXLEN);
+				(void) snprintf(pbuf, sizeof (pbuf), "%u",
+				    ipaddr->ipadm_static_prefixlen);
+				pval = pbuf;
+			}
+			break;
+		case IPADM_ADDR_DHCP:
+			if (propname == NULL ||
+			    strcmp(propname, IPADM_NVP_REQHOST) == 0) {
+				pdp = i_ipadm_get_addrprop_desc(IPADM_NVP_REQHOST);
+				pval = ipaddr->ipadm_reqhost;
+			}
+			break;
+		default:
+			break;
 		}
-		assert(pdp != NULL);
-		status = i_ipadm_persist_propval(iph, pdp, pval, ipaddr, flags);
+
+		if (pval != NULL) {
+			assert(pdp != NULL);
+			status = i_ipadm_persist_propval(iph, pdp, pval,
+				ipaddr, flags);
+		}
 	}
+
 ret:
 	nvlist_free(nvl);
 	return (status);
@@ -3322,7 +3610,6 @@
 	char			lifname[LIFNAMSIZ];
 	boolean_t		inform =
 	    ((ipadm_flags & IPADM_OPT_INFORM) != 0);
-	int			dherr;
 
 	/* check for solaris.network.interface.config authorization */
 	if (!ipadm_check_auth())
@@ -3364,15 +3651,7 @@
 			return (IPADM_SUCCESS);
 		status = i_ipadm_set_flags(iph, lifname, af, IFF_UP, 0);
 	} else if (ipaddr.ipadm_atype == IPADM_ADDR_DHCP) {
-		status = i_ipadm_op_dhcp(&ipaddr, DHCP_EXTEND, &dherr);
-		/*
-		 * Restart the dhcp address negotiation with server if no
-		 * address has been acquired yet.
-		 */
-		if (status != IPADM_SUCCESS && dherr == DHCP_IPC_E_OUTSTATE) {
-			ipaddr.ipadm_wait = IPADM_DHCP_WAIT_DEFAULT;
-			status = i_ipadm_op_dhcp(&ipaddr, DHCP_START, NULL);
-		}
+		status = i_ipadm_refresh_dhcp(&ipaddr);
 	} else {
 		status = IPADM_NOTSUP;
 	}
@@ -3380,6 +3659,30 @@
 }
 
 /*
+ * This is called from ipadm_refresh_addr() and i_ipadm_set_reqhost() to
+ * send a DHCP_EXTEND message and possibly a DHCP_START message
+ * to the dhcpagent.
+ */
+static ipadm_status_t
+i_ipadm_refresh_dhcp(ipadm_addrobj_t ipaddr)
+{
+	ipadm_status_t		status;
+	int			dherr;
+
+	status = i_ipadm_op_dhcp(ipaddr, DHCP_EXTEND, &dherr);
+	/*
+	 * Restart the dhcp address negotiation with server if no
+	 * address has been acquired yet.
+	 */
+	if (status != IPADM_SUCCESS && dherr == DHCP_IPC_E_OUTSTATE) {
+		ipaddr->ipadm_wait = IPADM_DHCP_WAIT_DEFAULT;
+		status = i_ipadm_op_dhcp(ipaddr, DHCP_START, NULL);
+	}
+
+	return (status);
+}
+
+/*
  * This is called from ipadm_create_addr() to validate the address parameters.
  * It does the following steps:
  * 1. Validates the interface name.
@@ -3494,29 +3797,41 @@
 }
 
 ipadm_status_t
-i_ipadm_merge_prefixlen_from_nvl(nvlist_t *invl, nvlist_t *onvl,
+i_ipadm_merge_addrprops_from_nvl(nvlist_t *invl, nvlist_t *onvl,
     const char *aobjname)
 {
-	nvpair_t	*nvp, *prefixnvp;
+	const char * const	ADDRPROPS[] =
+	    { IPADM_NVP_PREFIXLEN, IPADM_NVP_REQHOST };
+	const size_t		ADDRPROPSLEN =
+	    sizeof (ADDRPROPS) / sizeof (*ADDRPROPS);
+	nvpair_t	*nvp, *propnvp;
 	nvlist_t	*tnvl;
 	char		*aname;
+	const char	*propname;
+	size_t		i;
 	int		err;
 
-	for (nvp = nvlist_next_nvpair(invl, NULL); nvp != NULL;
-	    nvp = nvlist_next_nvpair(invl, nvp)) {
-		if (nvpair_value_nvlist(nvp, &tnvl) == 0 &&
-		    nvlist_exists(tnvl, IPADM_NVP_PREFIXLEN) &&
-		    nvlist_lookup_string(tnvl, IPADM_NVP_AOBJNAME,
-		    &aname) == 0 && strcmp(aname, aobjname) == 0) {
-			/* prefixlen exists for given address object */
-			(void) nvlist_lookup_nvpair(tnvl, IPADM_NVP_PREFIXLEN,
-			    &prefixnvp);
-			err = nvlist_add_nvpair(onvl, prefixnvp);
-			if (err == 0) {
-				err = nvlist_remove(invl, nvpair_name(nvp),
-				    nvpair_type(nvp));
+	for (i = 0; i < ADDRPROPSLEN; ++i) {
+		propname = ADDRPROPS[i];
+
+		for (nvp = nvlist_next_nvpair(invl, NULL); nvp != NULL;
+			nvp = nvlist_next_nvpair(invl, nvp)) {
+			if (nvpair_value_nvlist(nvp, &tnvl) == 0 &&
+			    nvlist_exists(tnvl, propname) &&
+			    nvlist_lookup_string(tnvl, IPADM_NVP_AOBJNAME,
+			    &aname) == 0 && strcmp(aname, aobjname) == 0) {
+
+				/* property named `propname' exists for given aobj */
+				(void) nvlist_lookup_nvpair(tnvl, propname, &propnvp);
+				err = nvlist_add_nvpair(onvl, propnvp);
+				if (err == 0) {
+					err = nvlist_remove(invl, nvpair_name(nvp),
+					    nvpair_type(nvp));
+				}
+				if (err != 0)
+					return (ipadm_errno2status(err));
+				break;
 			}
-			return (ipadm_errno2status(err));
 		}
 	}
 	return (IPADM_SUCCESS);
@@ -3565,8 +3880,9 @@
 			continue;
 
 		if (nvlist_exists(nvl, IPADM_NVP_IPV4ADDR) ||
-		    nvlist_exists(nvl, IPADM_NVP_IPV6ADDR)) {
-			status = i_ipadm_merge_prefixlen_from_nvl(addrnvl, nvl,
+		    nvlist_exists(nvl, IPADM_NVP_IPV6ADDR) ||
+		    nvlist_exists(nvl, IPADM_NVP_DHCP)) {
+			status = i_ipadm_merge_addrprops_from_nvl(addrnvl, nvl,
 			    aobjname);
 			if (status != IPADM_SUCCESS)
 				continue;
--- old/usr/src/lib/libipadm/common/ipadm_ipmgmt.h	Mon Nov 14 16:21:17 2016
+++ new/usr/src/lib/libipadm/common/ipadm_ipmgmt.h	Mon Nov 14 16:21:17 2016
@@ -20,6 +20,7 @@
  */
 /*
  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
  */
 
 #ifndef _IPADM_IPMGMT_H
@@ -51,8 +52,8 @@
  * For more information on these definitions please refer to the top of
  * ipadm_persist.c. These are the name of the nvpairs which hold the
  * respective values. All nvpairs private to ipadm have names that begin
- * with "_". Note below that 'prefixlen' is an address property and therefore
- * not a private nvpair name.
+ * with "_". Note below that 'prefixlen' and 'reqhost' are address
+ * properties and therefore not a private nvpair name.
  */
 #define	IPADM_NVP_PROTONAME	"_protocol"	/* protocol name */
 #define	IPADM_NVP_IFNAME	"_ifname"	/* interface name */
@@ -63,6 +64,7 @@
 #define	IPADM_NVP_IPADDRHNAME	"_aname"	/* local hostname */
 #define	IPADM_NVP_IPDADDRHNAME	"_dname"	/* remote hostname */
 #define	IPADM_NVP_PREFIXLEN	"prefixlen"	/* prefixlen */
+#define	IPADM_NVP_REQHOST	"reqhost"	/* requested hostname */
 #define	IPADM_NVP_IPV6ADDR	"_ipv6addr"	/* name of IPv6 addr nvlist */
 #define	IPADM_NVP_DHCP		"_dhcp"		/* name of DHCP nvlist */
 #define	IPADM_NVP_WAIT		"_wait"		/* DHCP timeout value */
@@ -152,6 +154,21 @@
 #define	IPMGMT_APPEND	0x00000001
 #define	IPMGMT_REMOVE	0x00000002
 
+/*
+ * ipadm_addr_type_t-specific values that are cached in ipmgmtd and can
+ * make a round-trip back to client programs
+ */
+typedef union {
+	struct {
+		boolean_t		ipmgmt_linklocal;
+		struct sockaddr_in6		ipmgmt_ifid;
+	} ipmgmt_ipv6_cache_s;
+	struct {
+		boolean_t		ipmgmt_primary;
+		char			ipmgmt_reqhost[MAXNAMELEN];
+	} ipmgmt_dhcp_cache_s;
+} ipmgmt_addr_type_cache_u;
+
 /* IPMGMT_CMD_GETIF door_call argument structure */
 typedef struct ipmgmt_getif_arg_s {
 	ipmgmt_door_cmd_type_t	ia_cmd;
@@ -221,10 +238,15 @@
  *	- PERSIST updates the permanent data store
  *	- INIT	indicates that operation being performed is under init
  *		    context
+ *	- PROPS_ONLY indicates the update changes the running configuration of
+ *		    "props" data on the interface/address object. The props are
+ *		    cached there on the parent, so a PROPS_ONLY change does not
+ *		    affect the ACTIVE/PERSIST state of the parent.
  */
 #define	IPMGMT_ACTIVE		0x00000001
 #define	IPMGMT_PERSIST		0x00000002
 #define	IPMGMT_INIT		0x00000004
+#define	IPMGMT_PROPS_ONLY		0x00000008
 
 /* door call return value */
 typedef struct ipmgmt_retval_s {
@@ -260,9 +282,16 @@
 	sa_family_t		ir_family;
 	uint32_t		ir_flags;
 	ipadm_addr_type_t	ir_atype;
-	struct sockaddr_storage	ir_ifid;
+	ipmgmt_addr_type_cache_u	ir_atype_cache;
 } ipmgmt_aobjop_rval_t;
 
+#define	ipmgmt_ir_intfid	ir_atype_cache. \
+	ipmgmt_ipv6_cache_s.ipmgmt_ifid
+#define	ipmgmt_ir_primary	ir_atype_cache. \
+	ipmgmt_dhcp_cache_s.ipmgmt_primary
+#define	ipmgmt_ir_reqhost	ir_atype_cache. \
+	ipmgmt_dhcp_cache_s.ipmgmt_reqhost
+
 /* DB walk callback functions */
 typedef boolean_t	db_wfunc_t(void *, nvlist_t *, char *, size_t, int *);
 extern int		ipadm_rw_db(db_wfunc_t *, void *, const char *, mode_t,
--- old/usr/src/lib/libipadm/common/ipadm_ndpd.c	Mon Nov 14 16:21:17 2016
+++ new/usr/src/lib/libipadm/common/ipadm_ndpd.c	Mon Nov 14 16:21:17 2016
@@ -107,7 +107,7 @@
 	}
 
 	/* Persist the intfid. */
-	status = i_ipadm_addr_persist(iph, addr, B_FALSE, i_flags);
+	status = i_ipadm_addr_persist(iph, addr, B_FALSE, i_flags, NULL);
 	if (status != IPADM_SUCCESS) {
 		(void) i_ipadm_delete_addr(iph, addr);
 		(void) i_ipadm_send_ndpd_cmd(addr->ipadm_ifname, addr,
--- old/usr/src/lib/libipadm/common/libipadm.c	Mon Nov 14 16:21:17 2016
+++ new/usr/src/lib/libipadm/common/libipadm.c	Mon Nov 14 16:21:17 2016
@@ -22,6 +22,7 @@
 /*
  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
  */
 
 #include <stdio.h>
@@ -728,12 +729,13 @@
 		} else if (nvlist_lookup_string(nvl, IPADM_NVP_AOBJNAME,
 		    &aobjstr) == 0) {
 			/*
-			 * For a static address, we need to search for
-			 * the prefixlen in the nvlist `ifnvl'.
+			 * For addresses, we need to relocate addrprops from the
+			 * nvlist `ifnvl'.
 			 */
 			if (nvlist_exists(nvl, IPADM_NVP_IPV4ADDR) ||
-			    nvlist_exists(nvl, IPADM_NVP_IPV6ADDR)) {
-				status = i_ipadm_merge_prefixlen_from_nvl(ifnvl,
+			    nvlist_exists(nvl, IPADM_NVP_IPV6ADDR) ||
+			    nvlist_exists(nvl, IPADM_NVP_DHCP)) {
+				status = i_ipadm_merge_addrprops_from_nvl(ifnvl,
 				    nvl, aobjstr);
 				if (status != IPADM_SUCCESS)
 					continue;
@@ -955,4 +957,81 @@
 			err = EBADE;
 	}
 	return (err);
+}
+
+/*
+ * ipadm_is_nil_hostname() : Determine if the `hostname' is nil: i.e.,
+ *			NULL, empty, or a single space (e.g., as returned by
+ *			domainname(1M)/sysinfo).
+ *
+ *   input: const char *: the hostname to inspect;
+ *  output: boolean_t: B_TRUE if `hostname' is not NULL satisfies the
+ *			criteria above; otherwise, B_FALSE;
+ */
+
+boolean_t
+ipadm_is_nil_hostname(const char *hostname)
+{
+	return (hostname == NULL || *hostname == '\0'
+	    || (*hostname == ' ' && hostname[1] == '\0'));
+}
+
+/*
+ * ipadm_is_valid_hostname(): check whether a string is a valid hostname
+ *
+ *   input: const char *: the string to verify as a hostname
+ *  output: boolean_t: B_TRUE if the string is a valid hostname
+ *
+ * Note that we accept host names beginning with a digit, which is not
+ * strictly legal according to the RFCs but is in common practice, so we
+ * endeavour to not break what customers are using.
+ *
+ * RFC 1035 limits a wire-format domain name to 255 octets. For a printable
+ * `hostname' as we have, the limit is therefore 253 characters (excluding
+ * the terminating '\0'--or 254 characters if the last character of
+ * `hostname' is a '.'.
+ *
+ * Excerpt from section 2.3.1., Preferred name syntax:
+ *
+ * <domain> ::= <subdomain> | " "
+ * <subdomain> ::= <label> | <subdomain> "." <label>
+ * <label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
+ * <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
+ * <let-dig-hyp> ::= <let-dig> | "-"
+ * <let-dig> ::= <letter> | <digit>
+ */
+boolean_t
+ipadm_is_valid_hostname(const char *hostname)
+{
+	const size_t MAX_READABLE_NAME_LEN = 253;
+	char last_char;
+	size_t has_last_dot, namelen, i;
+
+	if (hostname == NULL)
+		return B_FALSE;
+
+	namelen = strlen(hostname);
+	if (namelen < 1)
+		return B_FALSE;
+
+	last_char = hostname[namelen - 1];
+	has_last_dot = last_char == '.';
+
+	if (namelen > MAX_READABLE_NAME_LEN + has_last_dot
+	    || last_char == '-')
+		return B_FALSE;
+
+	for (i = 0; hostname[i] != '\0'; i++) {
+		/*
+		 * As noted above, this deviates from RFC 1035 in that it allows a
+		 * leading digit.
+		 */
+		if (isalpha(hostname[i]) || isdigit(hostname[i]) ||
+		    (((hostname[i] == '-') || (hostname[i] == '.')) && (i > 0)))
+			continue;
+
+		return (B_FALSE);
+	}
+
+	return B_TRUE;
 }
--- old/usr/src/lib/libipadm/common/libipadm.h	Mon Nov 14 16:21:17 2016
+++ new/usr/src/lib/libipadm/common/libipadm.h	Mon Nov 14 16:21:17 2016
@@ -20,6 +20,7 @@
  */
 /*
  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
  */
 #ifndef _LIBIPADM_H
 #define	_LIBIPADM_H
@@ -29,6 +30,7 @@
 #endif
 
 #include <sys/types.h>
+#include <sys/param.h>
 #include <sys/socket.h>
 #include <net/if.h>
 #include <netdb.h>
@@ -101,7 +103,7 @@
  *  - IPADM_OPT_PERSIST:
  *	For all the create/delete/up/down/set/get functions,
  * 	requests to persist the configuration so that it can be
- *	re-enabled or reapplied on boot.
+ *	re-enabled or re-applied on boot.
  *
  *  - IPADM_OPT_ACTIVE:
  *	Requests to apply configuration without persisting it and
@@ -141,7 +143,17 @@
  *	Used to bring up a static address on creation
  *
  *  - IPADM_OPT_V46
- *      Used to plumb both IPv4 and IPv6 interfaces by ipadm_create_addr()
+ *	Used to plumb both IPv4 and IPv6 interfaces by ipadm_create_addr()
+ *
+ *  - IPADM_OPT_SET_PROPS
+ *	Used to indicate the update changes the running configuration of
+ *	"props" data on the object. The props are cached there on the parent,
+ *	but the PROPS_ONLY change does not affect the ACTIVE/PERSIST state of
+ *	the parent.
+ *
+ *  - IPADM_OPT_PERSIST_PROPS
+ *	Used when IPADM_OPT_SET_PROPS is active to indicate the update changes
+ *  the persistent configuration of the "props" data on the object.
  */
 #define	IPADM_OPT_PERSIST	0x00000001
 #define	IPADM_OPT_ACTIVE	0x00000002
@@ -157,6 +169,8 @@
 #define	IPADM_OPT_INFORM	0x00000800
 #define	IPADM_OPT_UP		0x00001000
 #define	IPADM_OPT_V46		0x00002000
+#define	IPADM_OPT_SET_PROPS	0x00004000
+#define	IPADM_OPT_PERSIST_PROPS		0x00008000
 
 /* IPADM property class */
 #define	IPADMPROP_CLASS_MODULE	0x00000001	/* on 'protocol' only */
@@ -257,7 +271,7 @@
 /* Check authorization for network configuration */
 extern boolean_t	ipadm_check_auth(void);
 /*
- * Interface mangement functions
+ * Interface management functions
  */
 extern ipadm_status_t	ipadm_create_if(ipadm_handle_t, char *, sa_family_t,
 			    uint32_t);
@@ -315,6 +329,7 @@
 /* Functions to set fields in addrobj for DHCP */
 extern ipadm_status_t	ipadm_set_primary(ipadm_addrobj_t, boolean_t);
 extern ipadm_status_t	ipadm_set_wait_time(ipadm_addrobj_t, int32_t);
+extern ipadm_status_t	ipadm_set_reqhost(ipadm_addrobj_t, const char *);
 
 /*
  * Property management functions
@@ -357,6 +372,8 @@
 			    uint_t, uint_t *);
 extern int		ipadm_new2legacy_propname(const char *, char *,
 			    uint_t, uint_t);
+extern boolean_t	ipadm_is_valid_hostname(const char *hostname);
+extern boolean_t	ipadm_is_nil_hostname(const char *hostname);
 
 #ifdef	__cplusplus
 }
--- old/usr/src/lib/libipadm/common/libipadm_impl.h	Mon Nov 14 16:21:17 2016
+++ new/usr/src/lib/libipadm/common/libipadm_impl.h	Mon Nov 14 16:21:17 2016
@@ -21,6 +21,7 @@
 /*
  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2013 by Delphix. All rights reserved.
+ * Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
  */
 
 #ifndef _LIBIPADM_IMPL_H
@@ -87,6 +88,7 @@
 		struct {
 			boolean_t		ipadm_primary;
 			int32_t			ipadm_wait;
+			char			ipadm_reqhost[MAXNAMELEN];
 		} ipadm_dhcp_s;
 	} ipadm_addr_u;
 };
@@ -102,6 +104,7 @@
 #define	ipadm_stateful		ipadm_addr_u.ipadm_ipv6_intfid_s.ipadm_stateful
 #define	ipadm_primary		ipadm_addr_u.ipadm_dhcp_s.ipadm_primary
 #define	ipadm_wait		ipadm_addr_u.ipadm_dhcp_s.ipadm_wait
+#define	ipadm_reqhost	ipadm_addr_u.ipadm_dhcp_s.ipadm_reqhost
 
 /*
  * Data structures and callback functions related to property management
@@ -145,7 +148,7 @@
 			    nvlist_t *);
 extern ipadm_status_t	i_ipadm_init_addrobj(ipadm_handle_t, nvlist_t *);
 extern ipadm_status_t	i_ipadm_addr_persist(ipadm_handle_t,
-			    const ipadm_addrobj_t, boolean_t, uint32_t);
+			    const ipadm_addrobj_t, boolean_t, uint32_t, const char *);
 extern ipadm_status_t	i_ipadm_delete_addr(ipadm_handle_t, ipadm_addrobj_t);
 extern int		i_ipadm_strioctl(int, int, char *, int);
 extern boolean_t	i_ipadm_is_loopback(const char *);
@@ -185,7 +188,7 @@
 /* ipadm_addr.c */
 extern void		i_ipadm_init_addr(ipadm_addrobj_t, const char *,
 			    const char *, ipadm_addr_type_t);
-extern ipadm_status_t	i_ipadm_merge_prefixlen_from_nvl(nvlist_t *, nvlist_t *,
+extern ipadm_status_t	i_ipadm_merge_addrprops_from_nvl(nvlist_t *, nvlist_t *,
 			    const char *);
 extern ipadm_status_t	i_ipadm_get_addrobj(ipadm_handle_t, ipadm_addrobj_t);
 extern ipadm_status_t	i_ipadm_enable_static(ipadm_handle_t, const char *,
--- old/usr/src/lib/libipadm/common/mapfile-vers	Mon Nov 14 16:21:17 2016
+++ new/usr/src/lib/libipadm/common/mapfile-vers	Mon Nov 14 16:21:17 2016
@@ -20,6 +20,7 @@
 #
 #
 # Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
 #
 
 #
@@ -67,6 +68,8 @@
 	ipadm_if_info;
 	ipadm_if_move;
 	ipadm_init_net_from_gz;
+	ipadm_is_nil_hostname;
+	ipadm_is_valid_hostname;
 	ipadm_legacy2new_propname;
 	ipadm_ndpd_read;
 	ipadm_ndpd_write;
@@ -80,6 +83,7 @@
 	ipadm_set_addr;
 	ipadm_set_addrprop;
 	ipadm_set_dst_addr;
+	ipadm_set_reqhost;
 	ipadm_set_ifprop;
 	ipadm_set_interface_id;
 	ipadm_set_primary;
--- old/usr/src/lib/libnwam/Makefile.com	Mon Nov 14 16:21:17 2016
+++ new/usr/src/lib/libnwam/Makefile.com	Mon Nov 14 16:21:17 2016
@@ -21,6 +21,7 @@
 #
 # Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
+# Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
 #
 
 LIBRARY=	libnwam.a
@@ -43,7 +44,8 @@
 include ../../Makefile.rootfs
 
 LIBS =		$(DYNLIB) $(LINTLIB)
-LDLIBS +=	-lbsm -lc -ldladm -lnsl -lnvpair -lscf -lsecdb -lsocket
+LDLIBS +=	-lbsm -lc -ldladm -lnsl -lnvpair -lscf -lsecdb -lsocket \
+		-lipadm
 
 SRCDIR =	../common
 $(LINTLIB) := SRCS=	$(SRCDIR)/$(LINTSRC)
--- old/usr/src/lib/libnwam/common/libnwam.h	Mon Nov 14 16:21:17 2016
+++ new/usr/src/lib/libnwam/common/libnwam.h	Mon Nov 14 16:21:17 2016
@@ -20,6 +20,7 @@
  */
 /*
  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
  */
 
 /*
@@ -447,6 +448,8 @@
 #define	NWAM_NCU_PROP_IPV6_ADDRSRC		"ipv6-addrsrc"
 #define	NWAM_NCU_PROP_IPV6_ADDR			"ipv6-addr"
 #define	NWAM_NCU_PROP_IPV6_DEFAULT_ROUTE	"ipv6-default-route"
+#define	NWAM_NCU_PROP_IP_PRIMARY		"ip-primary"
+#define	NWAM_NCU_PROP_IP_REQHOST		"ip-reqhost"
 
 /* Some properties should only be set on creation */
 #define	NWAM_NCU_PROP_SETONCE(prop)	\
--- old/usr/src/lib/libnwam/common/libnwam_ncp.c	Mon Nov 14 16:21:18 2016
+++ new/usr/src/lib/libnwam/common/libnwam_ncp.c	Mon Nov 14 16:21:18 2016
@@ -22,6 +22,7 @@
 /*
  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
  */
 
 #include <assert.h>
@@ -39,6 +40,7 @@
 #include <strings.h>
 #include <unistd.h>
 #include <libdladm.h>
+#include <libipadm.h>
 
 #include "libnwam_impl.h"
 #include <libnwam_priv.h>
@@ -73,6 +75,7 @@
 static nwam_error_t valid_ip_version(nwam_value_t);
 static nwam_error_t valid_addrsrc_v4(nwam_value_t);
 static nwam_error_t valid_addrsrc_v6(nwam_value_t);
+static nwam_error_t valid_reqhost(nwam_value_t);
 
 struct nwam_prop_table_entry ncu_prop_table_entries[] = {
 	{NWAM_NCU_PROP_TYPE, NWAM_VALUE_TYPE_UINT64, B_FALSE, 1, 1, valid_type,
@@ -149,7 +152,16 @@
 	{NWAM_NCU_PROP_IPV6_DEFAULT_ROUTE, NWAM_VALUE_TYPE_STRING, B_FALSE, 0,
 	    1, nwam_valid_route_v6,
 	    "specifies per-interface default IPv6 route",
-	    NWAM_FLAG_NCU_TYPE_INTERFACE, NWAM_FLAG_NCU_CLASS_ALL_INTERFACE}
+	    NWAM_FLAG_NCU_TYPE_INTERFACE, NWAM_FLAG_NCU_CLASS_ALL_INTERFACE},
+	{NWAM_NCU_PROP_IP_PRIMARY, NWAM_VALUE_TYPE_BOOLEAN, B_FALSE, 0,
+	    1, nwam_valid_boolean,
+	    "specifies the status of an interface as primary for the delivery"
+	    " of client-wide configuration data",
+	    NWAM_FLAG_NCU_TYPE_INTERFACE, NWAM_FLAG_NCU_CLASS_ALL_INTERFACE},
+	{NWAM_NCU_PROP_IP_REQHOST, NWAM_VALUE_TYPE_STRING, B_FALSE, 0,
+	    1, valid_reqhost,
+	    "specifies a requested hostname for the interface",
+	    NWAM_FLAG_NCU_TYPE_INTERFACE, NWAM_FLAG_NCU_CLASS_ALL_INTERFACE},
 };
 
 #define	NWAM_NUM_NCU_PROPS	(sizeof (ncu_prop_table_entries) / \
@@ -1648,6 +1660,17 @@
 		return (NWAM_ENTITY_INVALID_VALUE);
 }
 
+static nwam_error_t
+valid_reqhost(nwam_value_t value)
+{
+	char *hostname;
+
+	if (nwam_value_get_string(value, &hostname) != NWAM_SUCCESS)
+		return (NWAM_ENTITY_INVALID_VALUE);
+	return (ipadm_is_valid_hostname(hostname) ? NWAM_SUCCESS
+	    : NWAM_ENTITY_INVALID_VALUE);
+}
+
 /* ARGSUSED0 */
 static nwam_error_t
 valid_link_mtu(nwam_value_t value)
--- old/usr/src/lib/libresolv2/common/mapfile-vers	Mon Nov 14 16:21:18 2016
+++ new/usr/src/lib/libresolv2/common/mapfile-vers	Mon Nov 14 16:21:18 2016
@@ -20,6 +20,7 @@
 #
 #
 # Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
 #
 
 #
@@ -104,6 +105,7 @@
 	isc_puthexstring;
 	__log_close_debug_channels;
 	__memactive;
+	__ns_name_pton2;
 	p_sockun;
 	res_gethostbyname2;
 	res_getservers;
--- old/usr/src/man/man1m/dhcpagent.1m	Mon Nov 14 16:21:18 2016
+++ new/usr/src/man/man1m/dhcpagent.1m	Mon Nov 14 16:21:18 2016
@@ -1,9 +1,10 @@
 '\" te
 .\"  Copyright (c) 1992-1996 Competitive Automation, Inc. Copyright (c) 2009 Sun Microsystems, Inc. All Rights Reserved.
+.\"  Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
 .\" 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]
-.TH DHCPAGENT 1M "Dec 11, 2015"
+.TH DHCPAGENT 1M "Nov 7, 2016"
 .SH NAME
 dhcpagent \- Dynamic Host Configuration Protocol (DHCP) client daemon
 .SH SYNOPSIS
@@ -15,7 +16,7 @@
 .SH DESCRIPTION
 .LP
 \fBdhcpagent\fR implements the client half of the Dynamic Host Configuration
-Protocol \fB(DHCP)\fR for machines running Solaris software.
+Protocol \fB(DHCP)\fR for machines running illumos software.
 .sp
 .LP
 The \fBdhcpagent\fR daemon obtains configuration parameters for the client
@@ -28,11 +29,12 @@
 powers down.
 .sp
 .LP
-For IPv4, the \fBdhcpagent\fR daemon is controlled through \fBifconfig\fR(1M)
-in much the same way that the \fBinit\fR(1M) daemon is controlled by
-\fBtelinit\fR(1M). \fBdhcpagent\fR can be invoked as a user process, albeit one
-requiring root privileges, but this is not necessary, as \fBifconfig\fR(1M)
-will start it automatically.
+For IPv4, the \fBdhcpagent\fR daemon is controlled through \fBipadm\fR(1M),
+\fBnwamcfg\fR(1M), or \fBifconfig\fR(1M) in much the same way that the
+\fBinit\fR(1M) daemon is controlled by \fBtelinit\fR(1M). \fBdhcpagent\fR can
+be invoked as a user process, albeit one requiring root privileges, but this is
+not necessary, as \fBipadm\fR(1M), \fBnwamcfg\fR(1M), or \fBifconfig\fR(1M)
+will start \fBdhcpagent\fR automatically.
 .sp
 .LP
 For IPv6, the \fBdhcpagent\fR daemon is invoked automatically by
@@ -41,16 +43,20 @@
 .sp
 .LP
 When invoked, \fBdhcpagent\fR enters a passive state while it awaits
-instructions from \fBifconfig\fR(1M) or \fBin.ndpd\fR(1M). When it receives a
-command to configure an interface, it brings up the interface (if necessary)
-and starts DHCP. Once DHCP is complete, \fBdhcpagent\fR can be queried for the
-values of the various network parameters. In addition, if DHCP was used to
-obtain a lease on an address for an interface, it configures the address for
-use. When a lease is obtained, it is automatically renewed as necessary. If the
+instructions from \fBipadm\fR(1M), \fBnwamcfg\fR(1M), \fBifconfig\fR(1M), or
+\fBin.ndpd\fR(1M). When \fBdhcpagent\fR receives a command to configure an
+interface, \fBdhcpagent\fR brings up the interface (if necessary) and starts
+DHCP. Once DHCP is complete, \fBdhcpagent\fR can be queried for the values of
+the various network parameters. In addition, if DHCP was used to obtain a lease
+on an address for an interface, \fBdhcpagent\fR configures the address for use.
+When a lease is obtained, it is automatically renewed as necessary. If the
 lease cannot be renewed, \fBdhcpagent\fR will unconfigure the address, but the
-interface will be left up and \fBdhcpagent\fR will attempt to acquire a new
-address lease. \fBdhcpagent\fR monitors system suspend/resume events and will
-validate any non-permanent leases with the DHCP server upon resume. Similarly,
+interface will be left up, and \fBdhcpagent\fR will attempt to acquire a new
+address lease.
+.sp
+.LP
+\fBdhcpagent\fR monitors system suspend/resume events and will validate any
+non-permanent leases with the DHCP server upon resume. Similarly,
 \fBdhcpagent\fR monitors link up/down events and will validate any
 non-permanent leases with the DHCP server when the downed link is brought back
 up. The lease validation mechanism will restart DHCP if the server indicates
@@ -102,10 +108,10 @@
 .sp
 .LP
 For IPv4, the \fBdhcpagent\fR daemon can be configured to request a particular
-host name. See the \fBREQUEST_HOSTNAME\fR description in the \fBFILES\fR
-section. When first configuring a client to request a host name, you must
-perform the following steps as root to ensure that the full DHCP negotiation
-takes place:
+Fully Qualified Domain Name (FQDN) or host name. See the \fBREQUEST_FQDN\fR or
+\fBREQUEST_HOSTNAME\fR description in the \fBFILES\fR section. When first
+configuring a client to request an FQDN or host name, you must perform the
+following steps as root to ensure that the full DHCP negotiation takes place:
 .sp
 .in +2
 .nf
@@ -494,9 +500,14 @@
 .ad
 .sp .6
 .RS 4n
-Contains persistent storage for DUID (DHCP Unique Identifier) and IAID
-(Identity Association Identifier) values. The format of these files is
-undocumented, and applications should not read from or write to them.
+Contains persistent storage for system-generated DUID (DHCP Unique Identifier)
+and interface-specific IAID (Identity Association Identifier) values which are
+used if no \fBCLIENT_ID\fR is defined (see below). The format of these files is
+undocumented, and applications should not read from or write to them.  Instead,
+\fBdhcpinfo\fR(1) can be used to query the \fBdhcpagent\fR for \fIClientID\fR.
+For DHCPv6 interfaces, the result will contain the DUID. For DHCPv4 interfaces
+with \fBV4_DEFAULT_IAID_DUID\fR enabled (see below), the result will contain
+the IAID and DUID.
 .RE
 
 .sp
@@ -536,6 +547,8 @@
 .sp
 Enabling this option is often desirable on mobile systems, such as laptops, to
 allow the system to recover quickly from moves.
+.sp
+Default value of this option is \fIno\fR.
 .RE
 
 .sp
@@ -545,9 +558,11 @@
 .ad
 .sp .6
 .RS 4n
-Indicates how long to wait between checking for valid \fBOFFER\fRs after
-sending a \fBDISCOVER\fR. For DHCPv6, sets the time to wait between checking
-for valid Advertisements after sending a Solicit.
+Indicates how long to wait in seconds between checking for valid
+\fBOFFER\fRs after sending a \fBDISCOVER\fR. For DHCPv6, sets the time to
+wait between checking for valid Advertisements after sending a Solicit.
+.sp
+Default value of this option is \fI3\fR.
 .RE
 
 .sp
@@ -630,6 +645,26 @@
 .sp
 .ne 2
 .na
+\fB\fBV4_DEFAULT_IAID_DUID\fR\fR
+.ad
+.sp .6
+.RS 4n
+Indicates whether to use, when CLIENT_ID is not defined, a system-managed,
+RFC 3315-style (i.e., DHCPv6-style) binding identifier as documented in
+RFC 4361, "Node-specific Client Identifiers for DHCPv4," for IPv4
+interfaces which for purposes of backward compatibility do not normally get
+default binding identifiers.
+.sp
+An IPv4 interface that is not in an IP network multipathing (IPMP) group,
+that is not IP over InfiniBand (IPoIB), and that is not a logical interface
+does not normally get a default binding identifier.
+.sp
+Default value of this option is \fIno\fR.
+.RE
+
+.sp
+.ne 2
+.na
 \fB\fBPARAM_REQUEST_LIST\fR\fR
 .ad
 .sp .6
@@ -658,6 +693,64 @@
 .sp
 .ne 2
 .na
+\fB\fBREQUEST_FQDN\fR\fR
+.ad
+.sp .6
+.RS 4n
+Indicates the client requests the DHCP server to map the client's leased
+IPv4 address to the Fully Qualified Domain Name (FQDN) associated with the
+network interface that performs DHCP on the client and to collaborate with
+a compatible DNS server to manage A and PTR resource records for the FQDN
+for the life of the lease.
+.sp .6
+The hostname in the FQDN is determined from the following possible
+configurations:
+.sp
+.ne 2
+.na
+1.  \fBipadm\fR(1M): include the \fB-1,--primary\fR flag when creating an
+address that uses DHCP so that \fBnodename\fR(4) is used as the
+\fIhostname\fR.
+.ad
+.sp
+.ne 2
+.na
+2.  \fBipadm\fR(1M): include the \fB-h,--reqhost\fR \fIhostname\fR switch
+when executing the \fBcreate-addr -T dhcp\fR subcommand, or use the
+\fBset-addrprop -p reqhost=\fR\fIhostname\fR subcommand for any existing
+DHCP address.
+.ad
+.sp
+.ne 2
+.na
+3.  \fBnwamcfg\fR(1M): set a property,
+\fBip-primary=\fR\fIon\fR, for an ncu ip that uses DHCP so that
+\fBnodename\fR(4) is used as the \fIhostname\fR.
+.ad
+.sp
+.ne 2
+.na
+4.  \fBnwamcfg\fR(1M): set a property,
+\fBip-reqhost=\fR\fIhostname\fR, for an ncu ip that uses DHCP.
+.ad
+.sp
+The \fIhostname\fR value is either a Partially Qualified Domain Name (PQDN)
+or an FQDN (i.e., a "rooted" domain name ending with a '.' or one inferred
+to be an FQDN if it contains at least three DNS labels such as
+srv.example.com).  If a PQDN is specified, then an FQDN is constructed if
+either a \fBdefaultdomain\fR(4) or a \fBresolv.conf\fR(4) \fBdomain\fR is
+defined.
+.sp
+If an FQDN is sent, \fBREQUEST_HOSTNAME\fR processing will not be done,
+per RFC 4702 (3.1):  "clients that send the Client FQDN option in their
+messages MUST NOT also send the Host Name."
+.sp
+Default value of this option is \fIyes\fR.
+.RE
+
+.sp
+.ne 2
+.na
 \fB\fBREQUEST_HOSTNAME\fR\fR
 .ad
 .sp .6
@@ -664,7 +757,8 @@
 .RS 4n
 Indicates the client requests the DHCP server to map the client's leased IPv4
 address to the host name associated with the network interface that performs
-DHCP on the client. The host name must be specified in the
+DHCP on the client. The host name must be specified as documented for a
+PQDN in \fBREQUEST_FQDN\fR above or specified in the
 \fB/etc/hostname.\fIinterface\fR\fR file for the relevant interface on a line
 of the form
 .sp
@@ -678,6 +772,8 @@
 where \fIhostname\fR is the host name requested.
 .sp
 This option works with DHCPv4 only.
+.sp
+Default value of this option is \fIyes\fR.
 .RE
 
 .RE
@@ -710,7 +806,9 @@
 .SH SEE ALSO
 .LP
 \fBdhcpinfo\fR(1), \fBifconfig\fR(1M), \fBinit\fR(1M), \fBin.mpathd\fR(1M),
-\fBin.ndpd\fR(1M), \fBsyslog\fR(3C), \fBattributes\fR(5), \fBdhcp\fR(5)
+\fBin.ndpd\fR(1M), \fBipadm\fR(1M), \fBnwamcfg\fR(1M), \fBsyslog\fR(3C),
+\fBdefaultdomain\fR(4), \fBnodename\fR(4), \fBresolv.conf\fR(4),
+\fBattributes\fR(5), \fBdhcp\fR(5)
 .sp
 .LP
 \fI\fR
--- old/usr/src/man/man1m/ipadm.1m	Mon Nov 14 16:21:18 2016
+++ new/usr/src/man/man1m/ipadm.1m	Mon Nov 14 16:21:18 2016
@@ -12,8 +12,9 @@
 .\" Copyright (c) 2012, Joyent, Inc. All Rights Reserved
 .\" Copyright (c) 2013 by Delphix. All rights reserved.
 .\" Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
+.\" Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
 .\"
-.Dd April 9, 2016
+.Dd November 7, 2016
 .Dt IPADM 1M
 .Os
 .Sh NAME
@@ -70,6 +71,8 @@
 .Op Fl t
 .Fl T Cm dhcp
 .Op Fl w Ar seconds Ns | Ns Cm forever
+.Op Fl 1
+.Op Fl h Ar hostname
 .Ar addrobj
 .Nm
 .Ic create-addr
@@ -411,6 +414,8 @@
 .Op Fl t
 .Fl T Cm dhcp
 .Op Fl w Ar seconds Ns | Ns Cm forever
+.Op Fl 1
+.Op Fl h Ar hostname
 .Ar addrobj
 .br
 .Nm
@@ -454,7 +459,30 @@
 Obtain the address via DHCP. This takes the following options:
 .Bl -tag -width ""
 .It Fl w Ns , Ns Fl -wait
-Specify the time, in seconds, that the command should wait to obtain an address.
+Specify the time, in seconds, that the command should wait to obtain an
+address; or specify
+.Cm forever
+to wait without interruption.  The default value is 120.
+.It Fl 1 Ns , Ns Fl -primary
+Specify that the interface is primary.  One effect will be that
+.Xr nodename 4
+will serve as
+.Fl h Ns , Ns Fl -reqhost
+if that switch is not otherwise specified.
+.It Fl h Ns , Ns Fl -reqhost
+Specify the host name to send to the DHCP server in order to request an
+association of a Fully Qualified Domain Name to the interface. An FQDN is
+determined from
+.Ar hostname
+if it is "rooted" (ending in a '.'), or if it consists of at least three
+DNS labels, or by appending to
+.Ar hostname
+the value configured separately using either
+.Xr defaultdomain 4
+or
+.Xr resolv.conf 4 .
+N.b. that the DHCP server implementation ultimately determines whether and
+how the client-sent FQDN is used.
 .El
 .It Fl T Cm addrconf
 Create an auto-configured address. This takes the following options:
@@ -631,14 +659,27 @@
 .Pq Cm on Ns / Ns Cm off .
 .It Cm prefixlen
 The number of bits in the IPv4 netmask or IPv6 prefix.
+.It Cm primary
+The DHCP primary interface flag (read-only).
 .It Cm private
 The address is not advertised to routing
 .Pq Cm on Ns / Ns Cm off .
+.It Cm reqhost
+The host name to send to the DHCP server in order to request an association
+of an FQDN to the interface.  For a primary DHCP interface,
+.Xr nodename 4
+is sent if this property is not defined.  See the
+.Nm
+.Ic create-addr
+.Fl T Cm dhcp
+subcommand for an explanation of how an FQDN is determined.
 .It Cm transmit
 Packets can be transmitted
 .Pq Cm on Ns / Ns Cm off .
 .It Cm zone
-The zone the addrobj is in.
+The zone the addrobj is in. (temporary-only.  Use
+.Xr zonecfg 1M
+to make persistent).
 .El
 .It Fl t Ns , Ns Fl -temporary
 Temporary, not persistent across reboots.
@@ -830,5 +871,8 @@
 .Xr ifconfig 1M ,
 .Xr ndd 1M ,
 .Xr zonecfg 1M ,
+.Xr defaultdomain 4 ,
+.Xr nodename 4 ,
 .Xr nsswitch.conf 4 ,
+.Xr resolv.conf 4 ,
 .Xr dhcp 5
--- old/usr/src/man/man4/dhcp_inittab.4	Mon Nov 14 16:21:18 2016
+++ new/usr/src/man/man4/dhcp_inittab.4	Mon Nov 14 16:21:18 2016
@@ -1,9 +1,10 @@
 '\" te
 .\" Copyright (C) 2009, Sun Microsystems, Inc. All Rights Reserved
+.\" Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
 .\" 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]
-.TH DHCP_INITTAB 4 "Aug 31, 2009"
+.TH DHCP_INITTAB 4 "Oct 31, 2016"
 .SH NAME
 dhcp_inittab \- information repository for DHCP options
 .SH DESCRIPTION
@@ -377,7 +378,7 @@
 Subnet Mask, dotted Internet address (IP).
 T}
 \fBUTCoffst\fR	\fB2\fR	T{
-Coordinated Universal time offset (seconds).
+Coordinated Universal time offset (seconds) [deprecated].
 T}
 \fBRouter\fR	\fB3\fR	List of Routers, IP.
 \fBTimeserv\fR	\fB4\fR	List of RFC-868 servers, IP.
@@ -475,6 +476,7 @@
 \fBUserClas\fR	\fB77\fR	User class information, ASCII.
 \fBSLP_DA\fR	\fB78\fR	Directory agent, OCTET.
 \fBSLP_SS\fR	\fB79\fR	Service scope, OCTET.
+\fBClientFQDN\fR	\fB81\fR	Fully Qualified Domain Name, OCTET.
 \fBAgentOpt\fR	\fB82\fR	Agent circuit ID, OCTET.
 \fBFQDN\fR	\fB89\fR	Fully Qualified Domain Name, OCTET.
 \fBPXEarch\fR	\fB93\fR	Client system architecture, NUMBER.
--- old/usr/src/tools/scripts/onu.1onbld	Mon Nov 14 16:21:19 2016
+++ new/usr/src/tools/scripts/onu.1onbld	Mon Nov 14 16:21:19 2016
@@ -19,8 +19,9 @@
 .\" "
 .\" "Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
 .\" "Copyright 2010, Richard Lowe
+.\" "Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
 .\" "
-.TH ONU 1ONBLD "Aug 28, 2010"
+.TH ONU 1ONBLD "Nov 7, 2016"
 .SH NAME
 .I onu
 \- OS/Net Update
@@ -98,6 +99,12 @@
 One of the commands used by the script failed.
 .SH NOTES
 .LP
+Distributions which segment certain system directories onto separate, child
+datasets (e.g., /var for openindiana Text Install) must run
+.B beadm unmount <new beName>
+after running onu or else the child dataset will not mount at the correct
+location after reboot.
+.LP
 Former users of bfu should note the following ways in which onu behaves
 differently from bfu.  First, as noted above, onu creates a new BE, rather
 than updating an existing BE in-place.  Second, onu will not install packages
--- old/usr/src/uts/common/netinet/dhcp.h	Mon Nov 14 16:21:19 2016
+++ new/usr/src/uts/common/netinet/dhcp.h	Mon Nov 14 16:21:19 2016
@@ -22,6 +22,7 @@
 /*
  * Copyright 1996-2003 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
  */
 
 /*
@@ -157,8 +158,15 @@
 
 #define	CD_SLPDA		78
 #define	CD_SLPSS		79
+#define	CD_CLIENTFQDN	81
 #define	CD_AGENTOPT		82
+
+/*
+ * Per RFC 3679, option 89 was "Never published as standard and [is] not in
+ * general use". See active CD_CLIENTFQDN and RFC 4702.
+ */
 #define	CD_FQDN			89
+
 #define	CD_PXEARCHi		93
 #define	CD_PXENIIi		94
 #define	CD_PXECID		95
