Print this page
7388 Support DHCP Client FQDN. Allow IAID/DUID for all v4.

@@ -19,10 +19,11 @@
  * CDDL HEADER END
  */
 /*
  * 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>.
  */
 
 /*
  * This file contains functions for address management such as creating
  * an address, deleting an address, enabling an address, disabling an

@@ -30,10 +31,11 @@
  * on an address object and listing address information
  * for all addresses in active as well as persistent configuration.
  */
 #include <sys/types.h>
 #include <sys/socket.h>
+#include <sys/param.h>
 #include <netdb.h>
 #include <inet/ip.h>
 #include <string.h>
 #include <strings.h>
 #include <assert.h>

@@ -46,10 +48,12 @@
 #include <arpa/inet.h>
 #include <fcntl.h>
 #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>
 #include <libdliptun.h>
 #include <ifaddrs.h>

@@ -62,10 +66,11 @@
                             uint32_t);
 static ipadm_status_t   i_ipadm_create_dhcp(ipadm_handle_t, ipadm_addrobj_t,
                             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,
                             int *);
 static ipadm_status_t   i_ipadm_validate_create_addr(ipadm_handle_t,

@@ -75,44 +80,61 @@
 static ipadm_status_t   i_ipadm_get_default_prefixlen(struct sockaddr_storage *,
                             uint32_t *);
 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
  * functions, when required, translate the values from the kernel to a format
  * suitable for printing. They also retrieve DEFAULT, PERM and POSSIBLE values
  * 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,
             NULL, NULL, i_ipadm_get_broadcast },
 
         { "deprecated", NULL, IPADMPROP_CLASS_ADDR, MOD_PROTO_NONE, 0,
             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 },
 
         { "zone", NULL, IPADMPROP_CLASS_ADDR, MOD_PROTO_NONE, 0,
             i_ipadm_set_zone, NULL, i_ipadm_get_zone },

@@ -199,13 +221,21 @@
             sizeof (ipaddr->ipadm_ifname));
         ipaddr->ipadm_lifnum = rval.ir_lnum;
         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);
 }
 

@@ -1039,10 +1069,88 @@
 
         return (IPADM_SUCCESS);
 }
 
 /*
+ * 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'.
  */
 /* ARGSUSED */
 static ipadm_status_t

@@ -1369,10 +1477,82 @@
         }
 
         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)
 {
         int i;
 

@@ -1773,10 +1953,11 @@
         }
         *addr = ipaddr->ipadm_static_addr;
 
         return (IPADM_SUCCESS);
 }
+
 /*
  * Set up tunnel destination address in ipaddr by contacting DNS.
  * The function works similar to ipadm_set_addr().
  * The dst_addr must resolve to exactly one address. IPADM_BAD_ADDR is returned
  * if dst_addr resolves to more than one address. The caller has to verify

@@ -1899,10 +2080,32 @@
         ipaddr->ipadm_wait = wait;
         return (IPADM_SUCCESS);
 }
 
 /*
+ * 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.
  *
  * If the libipadm consumer set `ipaddr.ipadm_aobjname[0]' to `\0', then the

@@ -2048,27 +2251,30 @@
  * IPADM_ADDR_DHCP address object parameters from the nvlist `nvl'.
  */
 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;
             nvp = nvlist_next_nvpair(nvl, nvp)) {
                 name = nvpair_name(nvp);
                 if (strcmp(name, IPADM_NVP_DHCP) == 0)
                         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));
         }
         for (nvp = nvlist_next_nvpair(nvdhcp, NULL); nvp != NULL;
             nvp = nvlist_next_nvpair(nvdhcp, nvp)) {

@@ -2086,10 +2292,13 @@
         ipaddr.ipadm_primary = primary;
         if (iph->iph_flags & IPH_INIT)
                 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));
 }
 
 /*

@@ -2728,11 +2937,11 @@
                             legacy_addr.ipadm_lifnum >= 0) {
                                 return (status);
                         }
                 }
                 status = i_ipadm_addr_persist(iph, ipaddr, default_prefixlen,
-                    flags);
+                    flags, NULL);
         }
 ret:
         if (status != IPADM_SUCCESS && !legacy)
                 (void) i_ipadm_delete_addr(iph, ipaddr);
         return (status);

@@ -2885,11 +3094,11 @@
         if (status != IPADM_SUCCESS && status != IPADM_DHCP_IPC_TIMEOUT)
                 goto fail;
         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;
 
         return (dh_status);
 fail:

@@ -2945,22 +3154,55 @@
 static ipadm_status_t
 i_ipadm_op_dhcp(ipadm_addrobj_t addr, dhcp_ipc_type_t type, int *dhcperror)
 {
         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;
 
         /* Construct a message to the dhcpagent. */
         bzero(&ifname, sizeof (ifname));
         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;
         else if (addr->ipadm_wait == IPADM_DHCP_WAIT_DEFAULT)
                 dhcp_timeout = DHCP_IPC_WAIT_DEFAULT;

@@ -2967,10 +3209,11 @@
         else
                 dhcp_timeout = addr->ipadm_wait;
         /* 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);
         }
         if (error != 0) {

@@ -3024,25 +3267,23 @@
         freeifaddrs((struct ifaddrs *)ainfo);
 }
 
 /*
  * 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.
          */
         if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)

@@ -3058,21 +3299,31 @@
         switch (ipaddr->ipadm_atype) {
         case IPADM_ADDR_STATIC:
                 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
                         err = nvlist_add_string(nvl, "up", "no");
                 status = ipadm_errno2status(err);
                 break;
         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);
                 break;
         }

@@ -3092,29 +3343,66 @@
         } else {
                 if (flags & IPADM_OPT_ACTIVE)
                         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);
+
+        if (flags & IPADM_OPT_SET_PROPS) {
         /*
-         * 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.
+                 * Set PERSIST per IPADM_OPT_PROPS_PERSIST, and then un-set the
+                 * SET_PROPS bits.
          */
-        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)
+                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;
+                }
+
+                if (pval != NULL) {
                 assert(pdp != NULL);
-                status = i_ipadm_persist_propval(iph, pdp, pval, ipaddr, flags);
+                        status = i_ipadm_persist_propval(iph, pdp, pval,
+                                ipaddr, flags);
         }
+        }
+
 ret:
         nvlist_free(nvl);
         return (status);
 }
 

@@ -3320,11 +3608,10 @@
         struct ipadm_addrobj_s  ipaddr;
         sa_family_t             af;
         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())
                 return (IPADM_EAUTH);
 

@@ -3362,22 +3649,38 @@
                 }
                 if (!(flags & IFF_DUPLICATE))
                         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);
+                status = i_ipadm_refresh_dhcp(&ipaddr);
+        } else {
+                status = IPADM_NOTSUP;
+        }
+        return (status);
+}
+
+/*
+ * 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);
+                ipaddr->ipadm_wait = IPADM_DHCP_WAIT_DEFAULT;
+                status = i_ipadm_op_dhcp(ipaddr, DHCP_START, NULL);
                 }
-        } else {
-                status = IPADM_NOTSUP;
-        }
+
         return (status);
 }
 
 /*
  * This is called from ipadm_create_addr() to validate the address parameters.

@@ -3492,35 +3795,47 @@
 
         return (IPADM_SUCCESS);
 }
 
 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 (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, IPADM_NVP_PREFIXLEN) &&
+                            nvlist_exists(tnvl, propname) &&
                     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);
+
+                                /* 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_SUCCESS);
 }
 
 /*
  * Re-enables the address object `aobjname' based on the saved

@@ -3563,12 +3878,13 @@
             nvp = nvlist_next_nvpair(addrnvl, nvp)) {
                 if (nvpair_value_nvlist(nvp, &nvl) != 0)
                         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;
                 }
                 iph->iph_flags |= IPH_INIT;