From b31320a79e2054c6739b5229259dbf98f3afc547 Mon Sep 17 00:00:00 2001 From: Chris Fraire Date: Fri, 18 Nov 2016 11:29:24 -0700 Subject: [PATCH] 7388 Support -h for ipadm DHCP 8517 Add ipadm and nwam options to allow designating a DHCP address as the primary interface 8518 Allow using system-generated IAID/DUID for all DHCPv4 8519 Add ns_name_pton2 to libresolv2 mapfile Reviewed by: Dan McDonald Reviewed by: Toomas Soome Reviewed by: Peter Tribble Approved by: Gordon Ross --- usr/src/cmd/cmd-inet/etc/dhcp/inittab | 5 +- usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_door.c | 60 ++- usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_impl.h | 18 +- usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_persist.c | 71 ++- usr/src/cmd/cmd-inet/lib/nwamd/ncu.c | 70 ++- usr/src/cmd/cmd-inet/lib/nwamd/ncu.h | 3 + usr/src/cmd/cmd-inet/sbin/dhcpagent/Makefile | 4 +- usr/src/cmd/cmd-inet/sbin/dhcpagent/agent.c | 166 +++++- usr/src/cmd/cmd-inet/sbin/dhcpagent/bound.c | 3 + usr/src/cmd/cmd-inet/sbin/dhcpagent/defaults.c | 7 +- usr/src/cmd/cmd-inet/sbin/dhcpagent/defaults.h | 7 +- usr/src/cmd/cmd-inet/sbin/dhcpagent/dhcpagent.dfl | 68 ++- usr/src/cmd/cmd-inet/sbin/dhcpagent/init_reboot.c | 29 +- usr/src/cmd/cmd-inet/sbin/dhcpagent/ipc_action.c | 9 +- usr/src/cmd/cmd-inet/sbin/dhcpagent/packet.c | 79 ++- usr/src/cmd/cmd-inet/sbin/dhcpagent/packet.h | 5 +- usr/src/cmd/cmd-inet/sbin/dhcpagent/renew.c | 4 +- usr/src/cmd/cmd-inet/sbin/dhcpagent/request.c | 6 +- usr/src/cmd/cmd-inet/sbin/dhcpagent/select.c | 28 +- usr/src/cmd/cmd-inet/sbin/dhcpagent/states.c | 19 +- usr/src/cmd/cmd-inet/sbin/dhcpagent/states.h | 14 + usr/src/cmd/cmd-inet/sbin/dhcpagent/util.c | 568 +++++++++++++++++++-- usr/src/cmd/cmd-inet/sbin/dhcpagent/util.h | 5 + usr/src/cmd/cmd-inet/usr.sbin/ipadm/ipadm.c | 41 +- usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/Makefile | 3 +- usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/nwamcfg.c | 24 +- usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/nwamcfg.h | 5 +- .../cmd-inet/usr.sbin/nwamcfg/nwamcfg_grammar.y | 25 + .../cmd/cmd-inet/usr.sbin/nwamcfg/nwamcfg_lex.l | 3 + usr/src/common/net/dhcp/dhcp_impl.h | 8 +- usr/src/head/arpa/nameser.h | 3 + usr/src/lib/Makefile | 5 +- usr/src/lib/libipadm/Makefile.com | 3 +- usr/src/lib/libipadm/common/ipadm_addr.c | 525 ++++++++++++++++--- usr/src/lib/libipadm/common/ipadm_ipmgmt.h | 32 +- usr/src/lib/libipadm/common/ipadm_ndpd.c | 3 +- usr/src/lib/libipadm/common/libipadm.c | 87 +++- usr/src/lib/libipadm/common/libipadm.h | 25 +- usr/src/lib/libipadm/common/libipadm_impl.h | 8 +- usr/src/lib/libipadm/common/mapfile-vers | 4 + usr/src/lib/libnwam/Makefile.com | 4 +- usr/src/lib/libnwam/common/libnwam.h | 3 + usr/src/lib/libnwam/common/libnwam_ncp.c | 25 +- usr/src/lib/libresolv2/common/mapfile-vers | 2 + usr/src/man/man1m/dhcpagent.1m | 187 ++++++- usr/src/man/man1m/ipadm.1m | 53 +- usr/src/man/man4/dhcp_inittab.4 | 13 +- usr/src/uts/common/netinet/dhcp.h | 10 +- 48 files changed, 2018 insertions(+), 331 deletions(-) diff --git a/usr/src/cmd/cmd-inet/etc/dhcp/inittab b/usr/src/cmd/cmd-inet/etc/dhcp/inittab index 42a0d099c6..162183939d 100644 --- a/usr/src/cmd/cmd-inet/etc/dhcp/inittab +++ b/usr/src/cmd/cmd-inet/etc/dhcp/inittab @@ -1,6 +1,7 @@ # # Copyright 2005 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. +# Copyright (c) 2016, Chris Fraire . # # CDDL HEADER START # @@ -22,7 +23,6 @@ # # CDDL HEADER END # -# ident "%Z%%M% %I% %E% SMI" # # This file provides information about all supported DHCP options, for # use by DHCP-related programs. This file should only be modified to @@ -114,6 +114,7 @@ STDAservs STANDARD, 76, IP, 1, 0, sdmi 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 +178,7 @@ SHTTPproxy VENDOR, 17, ASCII, 1, 0, smi # 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 diff --git a/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_door.c b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_door.c index 8e9e153b21..c83c7627ad 100644 --- a/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_door.c +++ b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_door.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016-2017, Chris Fraire . */ /* @@ -225,8 +226,6 @@ i_ipmgmt_nvl2aobjnode(nvlist_t *nvl, ipmgmt_aobjmap_t *nodep) 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,37 @@ i_ipmgmt_nvl2aobjnode(nvlist_t *nvl, ipmgmt_aobjmap_t *nodep) 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; + af = AF_INET; addrtype = IPADM_ADDR_DHCP; + + /* + * 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 +284,15 @@ i_ipmgmt_nvl2aobjnode(nvlist_t *nvl, ipmgmt_aobjmap_t *nodep) 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 +300,6 @@ i_ipmgmt_nvl2aobjnode(nvlist_t *nvl, ipmgmt_aobjmap_t *nodep) 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,15 +314,15 @@ i_ipmgmt_nvl2aobjnode(nvlist_t *nvl, ipmgmt_aobjmap_t *nodep) /* * 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) { 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 +338,11 @@ ipmgmt_setaddr_handler(void *argp) 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 +356,7 @@ ret: } /* - * 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 +458,7 @@ ipmgmt_aobjop_handler(void *argp) * have am_ifid set. */ if (head->am_atype != IPADM_ADDR_IPV6_ADDRCONF || - head->am_linklocal) { + head->ipmgmt_am_linklocal) { break; } } @@ -456,9 +473,7 @@ ipmgmt_aobjop_handler(void *argp) 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; + aobjrval.ir_atype_cache = head->am_atype_cache; (void) pthread_rwlock_unlock(&aobjmap.aobjmap_rwlock); break; case IPMGMT_CMD_LIF2ADDROBJ: @@ -487,6 +502,7 @@ ipmgmt_aobjop_handler(void *argp) sizeof (aobjrval.ir_aobjname)); aobjrval.ir_atype = head->am_atype; aobjrval.ir_flags = head->am_flags; + aobjrval.ir_atype_cache = head->am_atype_cache; (void) pthread_rwlock_unlock(&aobjmap.aobjmap_rwlock); break; default: diff --git a/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_impl.h b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_impl.h index 0d905f2ff0..e95e5c7e00 100644 --- a/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_impl.h +++ b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_impl.h @@ -21,6 +21,7 @@ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, Chris Fraire . */ #ifndef _IPMGMT_IMPL_H @@ -103,8 +104,11 @@ extern db_wfunc_t ipmgmt_db_initif; * 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_reqhost (macro) */ typedef struct ipmgmt_aobjmap_s { struct ipmgmt_aobjmap_s *am_next; @@ -115,10 +119,16 @@ typedef struct ipmgmt_aobjmap_s { 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_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; diff --git a/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_persist.c b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_persist.c index 09fe54e5ac..a5031e9950 100644 --- a/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_persist.c +++ b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_persist.c @@ -22,6 +22,7 @@ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2016 Argo Technologie SA. + * Copyright (c) 2016-2017, Chris Fraire . */ /* @@ -908,7 +909,8 @@ ipmgmt_aobjmap_op(ipmgmt_aobjmap_t *nodep, uint32_t op) 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,7 @@ ipmgmt_aobjmap_op(ipmgmt_aobjmap_t *nodep, uint32_t op) 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; - } + head->am_atype_cache = nodep->am_atype_cache; } else { for (head = aobjmap.aobjmap_head; head != NULL; head = head->am_next) { @@ -1068,29 +1067,43 @@ i_ipmgmt_node2nvl(nvlist_t **nvl, ipmgmt_aobjmap_t *np) 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 +1151,18 @@ ipmgmt_aobjmap_init(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, 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; } } } diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/ncu.c b/usr/src/cmd/cmd-inet/lib/nwamd/ncu.c index 52d03076f7..46b8391e51 100644 --- a/usr/src/cmd/cmd-inet/lib/nwamd/ncu.c +++ b/usr/src/cmd/cmd-inet/lib/nwamd/ncu.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, Chris Fraire . */ #include @@ -33,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -128,6 +130,17 @@ nwamd_get_ncu_uint(nwam_ncu_handle_t ncuh, nwam_value_t *val, 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 @@ populate_ip_ncu_properties(nwam_ncu_handle_t ncuh, nwamd_ncu_t *ncu_data) { 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 @@ populate_ip_ncu_properties(nwam_ncu_handle_t ncuh, nwamd_ncu_t *ncu_data) 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 @@ populate_ip_ncu_properties(nwam_ncu_handle_t ncuh, nwamd_ncu_t *ncu_data) 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 @@ skip_ipv4_dhcp: 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 @@ skip_ipv6_addrconf: 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++) { diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/ncu.h b/usr/src/cmd/cmd-inet/lib/nwamd/ncu.h index a0c41751ec..7f0f5a1d7d 100644 --- a/usr/src/cmd/cmd-inet/lib/nwamd/ncu.h +++ b/usr/src/cmd/cmd-inet/lib/nwamd/ncu.h @@ -21,6 +21,7 @@ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, Chris Fraire . */ #ifndef _NCU_H @@ -225,6 +226,8 @@ extern nwam_error_t nwamd_get_ncu_uint(nwam_ncu_handle_t, nwam_value_t *, 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); diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/Makefile b/usr/src/cmd/cmd-inet/sbin/dhcpagent/Makefile index 34c68c967b..f94222fc72 100644 --- a/usr/src/cmd/cmd-inet/sbin/dhcpagent/Makefile +++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/Makefile @@ -21,6 +21,7 @@ # # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. +# Copyright (c) 2016, Chris Fraire . # PROG = dhcpagent @@ -45,7 +46,8 @@ CERRWARN += -_gcc=-Wno-parentheses # 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 diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/agent.c b/usr/src/cmd/cmd-inet/sbin/dhcpagent/agent.c index ef25380c32..812bb8e414 100644 --- a/usr/src/cmd/cmd-inet/sbin/dhcpagent/agent.c +++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/agent.c @@ -20,6 +20,7 @@ */ /* * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016-2017, Chris Fraire . */ #include @@ -36,6 +37,8 @@ #include #include #include +#include +#include #include #include #include @@ -70,6 +73,10 @@ static boolean_t shutdown_started = B_FALSE; 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 @@ ipc_event(iu_eh_t *ehp, int fd, short events, iu_event_id_t id, void *arg) break; /* not an immediate function */ case DHCP_EXTEND: + dhcp_smach_set_msg_reqhost(dsmp, iap); (void) dhcp_extending(dsmp); break; @@ -793,8 +801,8 @@ load_option: 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 @@ load_option: 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 @@ load_option: } /* - * 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,8 @@ load_option: (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 +975,7 @@ load_option: PKT_LIST *plp[2]; deprecate_leases(dsmp); + dhcp_smach_set_msg_reqhost(dsmp, iap); /* * if we have a valid hostconf lying around, then jump @@ -1059,6 +1064,147 @@ load_option: } /* + * 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) +{ + DHCP_OPT *d4o; + dhcp_symbol_t *entry; + char *value; + + 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; + } + + /* + * 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"); + return; + } 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); + return; + } + + 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); + return; + } + + 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); + return; + } 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); + return; + } + + 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"); + return; + } + + 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); +} + +/* + * 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. + */ + +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 * diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/bound.c b/usr/src/cmd/cmd-inet/sbin/dhcpagent/bound.c index 9907b8397d..1acf2d5626 100644 --- a/usr/src/cmd/cmd-inet/sbin/dhcpagent/bound.c +++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/bound.c @@ -20,6 +20,7 @@ */ /* * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, Chris Fraire . * * BOUND state of the DHCP client state machine. */ @@ -106,6 +107,8 @@ dhcp_bound(dhcp_smach_t *dsmp, PKT_LIST *ack) /* Save the first ack as the original */ if (dsmp->dsm_orig_ack == NULL) dsmp->dsm_orig_ack = ack; + + save_domainname(dsmp, ack); } oldstate = dsmp->dsm_state; diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/defaults.c b/usr/src/cmd/cmd-inet/sbin/dhcpagent/defaults.c index 0381f0c080..133254be4a 100644 --- a/usr/src/cmd/cmd-inet/sbin/dhcpagent/defaults.c +++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/defaults.c @@ -21,6 +21,7 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright (c) 2016-2017, Chris Fraire . */ #include @@ -59,7 +60,11 @@ static struct dhcp_default defaults[] = { { "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 }, + { "DNS_DOMAINNAME", NULL, 0, 0 }, + { "ADOPT_DOMAINNAME", "0", 0, 0 }, }; /* diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/defaults.h b/usr/src/cmd/cmd-inet/sbin/dhcpagent/defaults.h index 89063362d0..17a60b8125 100644 --- a/usr/src/cmd/cmd-inet/sbin/dhcpagent/defaults.h +++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/defaults.h @@ -21,6 +21,7 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright (c) 2016-2017, Chris Fraire . */ #ifndef DEFAULTS_H @@ -54,7 +55,11 @@ enum { 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 FQDN associated with interface */ + DF_V4_DEFAULT_IAID_DUID, /* IAID/DUID if no DF_CLIENT_ID */ + DF_DNS_DOMAINNAME, /* static domain name if not in --reqhost */ + DF_ADOPT_DOMAINNAME /* adopt DHCP domain if not in --reqhost */ }; #define DHCP_AGENT_DEFAULTS "/etc/default/dhcpagent" diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/dhcpagent.dfl b/usr/src/cmd/cmd-inet/sbin/dhcpagent/dhcpagent.dfl index 3b1c194784..7f5ffd2559 100644 --- a/usr/src/cmd/cmd-inet/sbin/dhcpagent/dhcpagent.dfl +++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/dhcpagent.dfl @@ -22,6 +22,7 @@ # # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. +# Copyright (c) 2016-2017, Chris Fraire . # # @@ -90,16 +91,73 @@ # # 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 DNS_DOMAINNAME +# will be appended if defined or ADOPT_DOMAINNAME discernment will be used +# if active. 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 not attempt to construct an FQDN from a +# PQDN specified by the -h,--reqhost option of ipadm(1M), by the ncu +# ip-reqhost property of nwamcfg(1M), or by nodename(4). Set and +# uncomment the following parameter to indicate a domain name to be used by +# the DHCP agent to construct if necessary an FQDN. +# +# DNS_DOMAINNAME= + +# By default, the DHCP agent will not attempt to use a domain name returned +# by the DHCP server or the domain in resolv.conf(4) to construct an FQDN +# from a PQDN specified by the -h,--reqhost option of ipadm(1M), by the ncu +# ip-reqhost property of nwamcfg(1M), or by nodename(4). Set and uncomment +# the following parameter to indicate that a returned DHCPv4 DNSdmain or the +# domain from resolv.conf(4) should be adopted by the DHCP agent to +# construct if necessary an FQDN. +# +# ADOPT_DOMAINNAME=yes + # 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., -# 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., 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 diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/init_reboot.c b/usr/src/cmd/cmd-inet/sbin/dhcpagent/init_reboot.c index c239287e1d..e2562345ec 100644 --- a/usr/src/cmd/cmd-inet/sbin/dhcpagent/init_reboot.c +++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/init_reboot.c @@ -21,13 +21,13 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright (c) 2016-2017, Chris Fraire . * * INIT_REBOOT state of the DHCP client state machine. */ #include #include -#include #include #include #include @@ -58,8 +58,6 @@ static void 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,29 +77,8 @@ dhcp_init_reboot_v4(dhcp_smach_t *dsmp) (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. - */ - 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 ((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); - } - } + if (!dhcp_add_fqdn_opt(dpkt, dsmp)) + (void) dhcp_add_hostname_opt(dpkt, dsmp); (void) add_pkt_opt(dpkt, CD_END, NULL, 0); diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/ipc_action.c b/usr/src/cmd/cmd-inet/sbin/dhcpagent/ipc_action.c index 4f33b2593e..5519e50e22 100644 --- a/usr/src/cmd/cmd-inet/sbin/dhcpagent/ipc_action.c +++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/ipc_action.c @@ -21,10 +21,9 @@ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright (c) 2016, Chris Fraire . */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include #include #include @@ -106,8 +105,10 @@ ipc_action_start(dhcp_smach_t *dsmp, ipc_action_t *iareq) /* 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; diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/packet.c b/usr/src/cmd/cmd-inet/sbin/dhcpagent/packet.c index a763530436..92adeb1a30 100644 --- a/usr/src/cmd/cmd-inet/sbin/dhcpagent/packet.c +++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/packet.c @@ -21,6 +21,7 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright (c) 2016, Chris Fraire . */ #include @@ -413,15 +414,13 @@ add_pkt_opt(dhcp_pkt_t *dpkt, uint_t opt_type, const void *opt_val, 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 (d6o); + req_len = opt_len + sizeof (dhcpv6_option_t); if (dpkt->pkt_cur_len + req_len > dpkt->pkt_max_len) { dhcpmsg(MSG_WARNING, @@ -430,17 +429,8 @@ add_pkt_opt(dhcp_pkt_t *dpkt, uint_t opt_type, const void *opt_val, 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 @@ add_pkt_opt(dhcp_pkt_t *dpkt, uint_t opt_type, const void *opt_val, "packet", opt_type); return (NULL); } + } + + 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; - raw_pkt[dpkt->pkt_cur_len++] = opt_type; + return (optr); +} - 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; - } +/* + * 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); } /* diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/packet.h b/usr/src/cmd/cmd-inet/sbin/dhcpagent/packet.h index 0ec64c4117..1d97a4b01f 100644 --- a/usr/src/cmd/cmd-inet/sbin/dhcpagent/packet.h +++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/packet.h @@ -21,13 +21,12 @@ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright (c) 2016, Chris Fraire . */ #ifndef _PACKET_H #define _PACKET_H -#pragma ident "%Z%%M% %I% %E% SMI" - #include #include #include @@ -122,6 +121,8 @@ dhcp_pkt_t *init_pkt(dhcp_smach_t *, uchar_t); 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); diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/renew.c b/usr/src/cmd/cmd-inet/sbin/dhcpagent/renew.c index 8d0c46c8a5..8b86f10abf 100644 --- a/usr/src/cmd/cmd-inet/sbin/dhcpagent/renew.c +++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/renew.c @@ -20,6 +20,7 @@ */ /* * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016-2017, Chris Fraire . */ #include @@ -490,7 +491,8 @@ dhcp_extending(dhcp_smach_t *dsmp) * 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) && + dsmp->dsm_reqhost != NULL) { (void) add_pkt_opt(dpkt, CD_HOSTNAME, dsmp->dsm_reqhost, strlen(dsmp->dsm_reqhost)); } diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/request.c b/usr/src/cmd/cmd-inet/sbin/dhcpagent/request.c index 78da07aebf..6b5a08a51a 100644 --- a/usr/src/cmd/cmd-inet/sbin/dhcpagent/request.c +++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/request.c @@ -21,6 +21,7 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright (c) 2016-2017, Chris Fraire . * * REQUESTING state of the client state machine. */ @@ -245,6 +246,8 @@ dhcp_requesting(iu_tq_t *tqp, void *arg) return; } + save_domainname(dsmp, offer); + if (isv6) { const char *estr, *msg; const dhcpv6_option_t *d6o; @@ -313,7 +316,8 @@ dhcp_requesting(iu_tq_t *tqp, void *arg) * 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) && + dsmp->dsm_reqhost != NULL) { (void) add_pkt_opt(dpkt, CD_HOSTNAME, dsmp->dsm_reqhost, strlen(dsmp->dsm_reqhost)); } diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/select.c b/usr/src/cmd/cmd-inet/sbin/dhcpagent/select.c index 611e8339af..e2570a4f0f 100644 --- a/usr/src/cmd/cmd-inet/sbin/dhcpagent/select.c +++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/select.c @@ -20,6 +20,7 @@ */ /* * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016-2017, Chris Fraire . * * SELECTING state of the client state machine. */ @@ -29,7 +30,6 @@ #include #include #include -#include #include #include #include @@ -106,8 +106,6 @@ void 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. @@ -201,27 +199,9 @@ dhcp_selecting(dhcp_smach_t *dsmp) } (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 ((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"); - } - } + if (!dhcp_add_fqdn_opt(dpkt, dsmp)) + (void) dhcp_add_hostname_opt(dpkt, dsmp); + (void) add_pkt_opt(dpkt, CD_END, NULL, 0); (void) send_pkt(dsmp, dpkt, htonl(INADDR_BROADCAST), diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/states.c b/usr/src/cmd/cmd-inet/sbin/dhcpagent/states.c index 8f9649cf2b..5a85ced3cd 100644 --- a/usr/src/cmd/cmd-inet/sbin/dhcpagent/states.c +++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/states.c @@ -20,6 +20,7 @@ */ /* * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016-2017, Chris Fraire . * * This module contains core functions for managing DHCP state machine * instances. @@ -332,6 +333,8 @@ free_smach(dhcp_smach_t *dsmp) free(dsmp->dsm_pil); free(dsmp->dsm_routers); free(dsmp->dsm_reqhost); + free(dsmp->dsm_msg_reqhost); + free(dsmp->dsm_dhcp_domainname); free(dsmp); /* no big deal if this fails */ @@ -1049,14 +1052,17 @@ no_specified_id: * 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 * 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 @@ reset_smach(dhcp_smach_t *dsmp) 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_dhcp_domainname); + dsmp->dsm_dhcp_domainname = NULL; + cancel_smach_timers(dsmp); (void) set_smach_state(dsmp, INIT); diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/states.h b/usr/src/cmd/cmd-inet/sbin/dhcpagent/states.h index 1630555d87..14dc8331db 100644 --- a/usr/src/cmd/cmd-inet/sbin/dhcpagent/states.h +++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/states.h @@ -21,6 +21,7 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright (c) 2016-2017, Chris Fraire . */ #ifndef STATES_H @@ -201,6 +202,19 @@ struct dhcp_smach_s { 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 domain name returned for v4 DNSdmain is decoded here for use + * (if configured and needed) to determine an FQDN. + */ + char *dsm_dhcp_domainname; + + /* * 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 diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/util.c b/usr/src/cmd/cmd-inet/sbin/dhcpagent/util.c index eb1887f947..6b69dfffe7 100644 --- a/usr/src/cmd/cmd-inet/sbin/dhcpagent/util.c +++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/util.c @@ -20,11 +20,13 @@ */ /* * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016-2017, Chris Fraire . */ #include #include #include +#include #include #include /* struct in_addr */ #include @@ -35,16 +37,25 @@ #include #include #include +#include +#include +#include #include #include #include #include +#include +#include +#include +#include +#include #include "states.h" #include "agent.h" #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 +78,12 @@ * o true miscellany -- anything else */ +#define ETCNODENAME "/etc/nodename" + +static boolean_t is_fqdn(const char *); +static boolean_t dhcp_assemble_fqdn(char *fqdnbuf, size_t buflen, + dhcp_smach_t *dsmp); + /* * pkt_type_to_string(): stringifies a packet type * @@ -466,35 +483,6 @@ bind_sock_v6(int fd, in_port_t port_hbo, const in6_addr_t *addr_nbo) } /* - * 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 +544,7 @@ iffile_to_hostname(const char *path) " host name too long"); return (NULL); } - if (valid_hostname(p)) { + if (ipadm_is_valid_hostname(p)) { return (p); } else { dhcpmsg(MSG_WARNING, @@ -705,3 +693,523 @@ write_lease_to_hostconf(dhcp_smach_t *dsmp) "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. + * + * input: const char *: file name; + * char *: allocated buffer space; + * size_t: space available in buf; + * output: boolean_t: B_TRUE if a non-empty string was written to buf; + * B_FALSE otherwise. + */ + +static boolean_t +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'); +} + +/* + * 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. + * + * input: char *: allocated buffer space; + * size_t: space available in buf; + * output: boolean_t: B_TRUE if a non-empty string was written to buf; + * B_FALSE otherwise. + */ + +static boolean_t +dhcp_get_nodename(char *buf, size_t buflen) +{ + return (dhcp_get_oneline(ETCNODENAME, 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. 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; + * output: B_TRUE if a client hostname was added; B_FALSE otherwise. + */ + +boolean_t +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 (B_FALSE); + + 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))) { + 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 (B_FALSE); + } else { + dhcpmsg(MSG_DEBUG, "dhcp_add_hostname_opt: no hostname for %s", + dsmp->dsm_name); + } + + return (B_TRUE); +} + +/* + * dhcp_add_fqdn_opt(): Set client FQDN option if dhcp_assemble_fqdn() + * initializes an FQDN, or else do nothing. + * + * input: dhcp_pkt_t *: pointer to DHCP message being constructed; + * dhcp_smach_t *: pointer to interface DHCP state machine; + * output: B_TRUE if a client FQDN was added; B_FALSE otherwise. + */ + +boolean_t +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). + */ + + const uint8_t S_BIT_POS = 7; + const uint8_t E_BIT_POS = 5; + const uint8_t S_BIT = 1 << (7 - S_BIT_POS); + const uint8_t E_BIT = 1 << (7 - E_BIT_POS); + const size_t OPT_FQDN_METALEN = 3; + char fqdnbuf[MAXNAMELEN]; + uchar_t enc_fqdnbuf[MAXNAMELEN]; + uint8_t fqdnopt[MAXNAMELEN + OPT_FQDN_METALEN]; + uint_t fqdncode; + size_t len, metalen; + + if (dsmp->dsm_isv6) + return (B_FALSE); + + if (!dhcp_assemble_fqdn(fqdnbuf, sizeof (fqdnbuf), dsmp)) + return (B_FALSE); + + /* encode the FQDN in canonical wire format */ + + if (ns_name_pton2(fqdnbuf, enc_fqdnbuf, sizeof (enc_fqdnbuf), + &len) < 0) { + dhcpmsg(MSG_WARNING, "dhcp_add_fqdn_opt: error encoding domain" + " name %s", fqdnbuf); + return (B_FALSE); + } + + dhcpmsg(MSG_DEBUG, "dhcp_add_fqdn_opt: interface FQDN is %s" + " for %s", fqdnbuf, dsmp->dsm_name); + + bzero(fqdnopt, sizeof (fqdnopt)); + fqdncode = CD_CLIENTFQDN; + metalen = OPT_FQDN_METALEN; + *fqdnopt = S_BIT | E_BIT; + (void) memcpy(fqdnopt + metalen, enc_fqdnbuf, len); + (void) add_pkt_opt(dpkt, fqdncode, fqdnopt, metalen + len); + + return (B_TRUE); +} + +/* + * dhcp_adopt_domainname(): Set namebuf if either dsm_dhcp_domainname or + * resolv's "default domain (deprecated)" is defined. + * + * input: char *: pointer to buffer to which domain name will be written; + * size_t length of buffer; + * dhcp_smach_t *: pointer to interface DHCP state machine; + * output: B_TRUE if namebuf was set to a valid domain name; B_FALSE + * otherwise. + */ + +static boolean_t +dhcp_adopt_domainname(char *namebuf, size_t buflen, dhcp_smach_t *dsmp) +{ + const char *domainname; + struct __res_state res_state; + int lasterrno; + + domainname = dsmp->dsm_dhcp_domainname; + + if (ipadm_is_nil_hostname(domainname)) { + /* + * fall back to resolv's "default domain (deprecated)" + */ + bzero(&res_state, sizeof (struct __res_state)); + + if ((lasterrno = res_ninit(&res_state)) != 0) { + dhcpmsg(MSG_WARNING, "dhcp_adopt_domainname: error %d" + " initializing resolver", lasterrno); + return (B_FALSE); + } + + domainname = NULL; + if (!ipadm_is_nil_hostname(res_state.defdname)) + domainname = res_state.defdname; + + /* N.b. res_state.defdname survives the following call */ + res_ndestroy(&res_state); + } + + if (domainname == NULL) + return (B_FALSE); + + if (strlcpy(namebuf, domainname, buflen) >= buflen) { + dhcpmsg(MSG_WARNING, + "dhcp_adopt_domainname: too long adopted domain" + " name %s for %s", domainname, dsmp->dsm_name); + return (B_FALSE); + } + + return (B_TRUE); +} + +/* + * dhcp_pick_domainname(): Set namebuf if DNS_DOMAINNAME is defined in + * /etc/default/dhcpagent or if dhcp_adopt_domainname() + * succeeds. + * + * input: char *: pointer to buffer to which domain name will be written; + * size_t length of buffer; + * dhcp_smach_t *: pointer to interface DHCP state machine; + * output: B_TRUE if namebuf was set to a valid domain name; B_FALSE + * otherwise. + */ + +static boolean_t +dhcp_pick_domainname(char *namebuf, size_t buflen, dhcp_smach_t *dsmp) +{ + const char *domainname; + + /* + * Try to use a static DNS_DOMAINNAME if defined in + * /etc/default/dhcpagent. + */ + domainname = df_get_string(dsmp->dsm_name, dsmp->dsm_isv6, + DF_DNS_DOMAINNAME); + if (!ipadm_is_nil_hostname(domainname)) { + if (strlcpy(namebuf, domainname, buflen) >= buflen) { + dhcpmsg(MSG_WARNING, "dhcp_pick_domainname: too long" + " DNS_DOMAINNAME %s for %s", domainname, + dsmp->dsm_name); + return (B_FALSE); + } + return (B_TRUE); + } else if (df_get_bool(dsmp->dsm_name, dsmp->dsm_isv6, + DF_ADOPT_DOMAINNAME)) { + return (dhcp_adopt_domainname(namebuf, buflen, dsmp)); + } else { + return (B_FALSE); + } +} + +/* + * dhcp_assemble_fqdn(): Set fqdnbuf 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 dhcp_pick_domainname() is tried to select a + * domain to be used to construct an FQDN. + * + * input: char *: pointer to buffer to which FQDN will be written; + * size_t length of buffer; + * dhcp_smach_t *: pointer to interface DHCP state machine; + * output: B_TRUE if fqdnbuf was assigned a valid FQDN; B_FALSE otherwise. + */ + +static boolean_t +dhcp_assemble_fqdn(char *fqdnbuf, size_t buflen, dhcp_smach_t *dsmp) +{ + char nodename[MAXNAMELEN], *reqhost; + size_t pos, len; + + + if (!df_get_bool(dsmp->dsm_name, dsmp->dsm_isv6, DF_REQUEST_FQDN)) + return (B_FALSE); + + dhcpmsg(MSG_DEBUG, "dhcp_assemble_fqdn: DF_REQUEST_FQDN"); + + /* It's convenient to ensure fqdnbuf is always null-terminated */ + bzero(fqdnbuf, buflen); + + reqhost = dsmp->dsm_msg_reqhost; + if (ipadm_is_nil_hostname(reqhost) && + (dsmp->dsm_dflags & DHCP_IF_PRIMARY) && + dhcp_get_nodename(nodename, sizeof (nodename))) { + reqhost = nodename; + } + + if (ipadm_is_nil_hostname(reqhost)) { + dhcpmsg(MSG_DEBUG, + "dhcp_assemble_fqdn: no interface reqhost for %s", + dsmp->dsm_name); + return (B_FALSE); + } + + if ((pos = strlcpy(fqdnbuf, reqhost, buflen)) >= buflen) { + dhcpmsg(MSG_WARNING, "dhcp_assemble_fqdn: too long reqhost %s" + " for %s", reqhost, dsmp->dsm_name); + return (B_FALSE); + } + + /* + * If not yet FQDN, construct if possible + */ + if (!is_fqdn(reqhost)) { + char domainname[MAXNAMELEN]; + size_t needdots; + + if (!dhcp_pick_domainname(domainname, sizeof (domainname), + dsmp)) { + dhcpmsg(MSG_DEBUG, + "dhcp_assemble_fqdn: no domain name for %s", + dsmp->dsm_name); + return (B_FALSE); + } + + /* + * 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 >= buflen) { + dhcpmsg(MSG_WARNING, "dhcp_assemble_fqdn: too long" + " FQDN %s.%s for %s", fqdnbuf, domainname, + dsmp->dsm_name); + return (B_FALSE); + } + + /* add separator and then domain name */ + fqdnbuf[pos++] = '.'; + if (strlcpy(fqdnbuf + pos, domainname, buflen - pos) >= + buflen - pos) { + /* shouldn't get here as we checked above */ + return (B_FALSE); + } + 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 (B_FALSE); + } + + return (B_TRUE); +} + +/* + * 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); +} + +/* + * terminate_at_space(): Reset the first space, 0x20, to 0x0 in the + * specified string. + * + * input: char *: NULL or a null-terminated string; + * output: void. + */ + +static void +terminate_at_space(char *value) +{ + if (value != NULL) { + char *sp; + + sp = strchr(value, ' '); + if (sp != NULL) + *sp = '\0'; + } +} + +/* + * get_offered_domainname_v4(): decode a defined v4 DNSdmain value if it + * exists to return a copy of the domain + * name. + * + * input: dhcp_smach_t *: the state machine REQUESTs are being sent from; + * PKT_LIST *: the best packet to be used to construct a REQUEST; + * output: char *: NULL or a copy of the domain name ('\0' terminated); + */ + +static char * +get_offered_domainname_v4(PKT_LIST *offer) +{ + char *domainname = NULL; + DHCP_OPT *opt; + + if ((opt = offer->opts[CD_DNSDOMAIN]) != NULL) { + uchar_t *valptr; + dhcp_symbol_t *symp; + + valptr = (uchar_t *)opt + DHCP_OPT_META_LEN; + + symp = inittab_getbycode( + ITAB_CAT_STANDARD, ITAB_CONS_INFO, opt->code); + if (symp != NULL) { + domainname = inittab_decode(symp, valptr, + opt->len, B_TRUE); + terminate_at_space(domainname); + free(symp); + } + } + + return (domainname); +} + +/* + * save_domainname(): assign dsm_dhcp_domainname from + * get_offered_domainname_v4 or leave the field NULL if no + * option is present. + * + * input: dhcp_smach_t *: the state machine REQUESTs are being sent from; + * PKT_LIST *: the best packet to be used to construct a REQUEST; + * output: void + */ + +void +save_domainname(dhcp_smach_t *dsmp, PKT_LIST *offer) +{ + char *domainname = NULL; + + free(dsmp->dsm_dhcp_domainname); + dsmp->dsm_dhcp_domainname = NULL; + + if (!dsmp->dsm_isv6) { + domainname = get_offered_domainname_v4(offer); + } + + dsmp->dsm_dhcp_domainname = domainname; +} diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/util.h b/usr/src/cmd/cmd-inet/sbin/dhcpagent/util.h index 587b9a9a31..906d3599f0 100644 --- a/usr/src/cmd/cmd-inet/sbin/dhcpagent/util.h +++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/util.h @@ -20,6 +20,7 @@ */ /* * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016-2017, Chris Fraire . */ #ifndef UTIL_H @@ -33,6 +34,7 @@ #include #include "common.h" +#include "packet.h" /* * general utility functions which have no better home. see util.c @@ -75,6 +77,9 @@ const char *iffile_to_hostname(const char *); int dhcpv6_status_code(const dhcpv6_option_t *, uint_t, const char **, const char **, uint_t *); void write_lease_to_hostconf(dhcp_smach_t *); +boolean_t dhcp_add_hostname_opt(dhcp_pkt_t *, dhcp_smach_t *); +boolean_t dhcp_add_fqdn_opt(dhcp_pkt_t *, dhcp_smach_t *); +void save_domainname(dhcp_smach_t *, PKT_LIST *); #ifdef __cplusplus } diff --git a/usr/src/cmd/cmd-inet/usr.sbin/ipadm/ipadm.c b/usr/src/cmd/cmd-inet/usr.sbin/ipadm/ipadm.c index 4e32ac67e0..14f8a70c72 100644 --- a/usr/src/cmd/cmd-inet/usr.sbin/ipadm/ipadm.c +++ b/usr/src/cmd/cmd-inet/usr.sbin/ipadm/ipadm.c @@ -23,6 +23,7 @@ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2017 Nexenta Systems, Inc. * Copyright 2017 Gary Mills + * Copyright (c) 2016, Chris Fraire . */ #include @@ -91,7 +92,8 @@ static cmd_t cmds[] = { { "create-addr", do_create_addr, "\tcreate-addr\t[-t] -T static [-d] " "-a{local|remote}=addr[/prefixlen]\n\t\t\t\n" - "\tcreate-addr\t[-t] -T dhcp [-w | forever] \n" + "\tcreate-addr\t[-t] -T dhcp [-w | forever]\n" + "\t\t\t[-1] [-h ] \n" "\tcreate-addr\t[-t] -T addrconf [-i interface-id]\n" "\t\t\t[-p {stateful|stateless}={yes|no}] " }, { "down-addr", do_down_addr, "\tdown-addr\t[-t] " }, @@ -164,7 +166,9 @@ static const struct option addr_longopts[] = { {"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' }, @@ -615,8 +619,8 @@ show_property(void *arg, const char *pname, uint_t proto) /* * 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 @@ -1241,14 +1245,19 @@ do_create_addr(int argc, char *argv[], const char *use) 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; @@ -1260,6 +1269,9 @@ do_create_addr(int argc, char *argv[], const char *use) flags &= ~IPADM_OPT_UP; s_opt = _B_TRUE; break; + case 'h': + reqhost = optarg; + break; case 'i': interface_id = optarg; auto_opt = _B_TRUE; @@ -1291,7 +1303,8 @@ do_create_addr(int argc, char *argv[], const char *use) * 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); } @@ -1328,13 +1341,27 @@ do_create_addr(int argc, char *argv[], const char *use) 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) { diff --git a/usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/Makefile b/usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/Makefile index 3fc271c39a..0c51a0301b 100644 --- a/usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/Makefile +++ b/usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/Makefile @@ -22,6 +22,7 @@ # # Copyright 2010 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. +# Copyright (c) 2016, Chris Fraire . # # cmd/cmd-inet/usr.sbin/nwamcfg/Makefile @@ -56,7 +57,7 @@ install: all $(ROOTUSRSBINPROG) 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 = diff --git a/usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/nwamcfg.c b/usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/nwamcfg.c index e4896f565d..690377cc2d 100644 --- a/usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/nwamcfg.c +++ b/usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/nwamcfg.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, Chris Fraire . */ /* @@ -198,7 +199,9 @@ static char *pt_types[] = { 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 @@ static prop_table_entry_t ncu_prop_table[] = { { 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 } }; @@ -626,7 +631,8 @@ rt2_to_str(int res_type) /* Returns "ncp, "ncu", "loc", "enm", or "wlan" according to the scope */ static const char * -scope_to_str(int scope) { +scope_to_str(int scope) +{ switch (scope) { case NWAM_SCOPE_GBL: return ("global"); @@ -664,11 +670,15 @@ pt_to_str(int prop_type) 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 +2207,12 @@ static prop_display_entry_t ncu_prop_display_entry_table[] = { /* 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-reqhost if ipv4-addrsrc == dhcp */ + { NWAM_NCU_PROP_IP_REQHOST, NWAM_NCU_PROP_IPV4_ADDRSRC, + { NWAM_ADDRSRC_DHCP, -1 } }, { NULL, NULL, { -1 } } }; diff --git a/usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/nwamcfg.h b/usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/nwamcfg.h index c9b1049c99..63ad154d87 100644 --- a/usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/nwamcfg.h +++ b/usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/nwamcfg.h @@ -22,6 +22,7 @@ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright (c) 2016, Chris Fraire . */ #ifndef _NWAMCFG_H @@ -133,12 +134,14 @@ extern "C" { #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 diff --git a/usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/nwamcfg_grammar.y b/usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/nwamcfg_grammar.y index c40dc2c963..f7c3d64b26 100644 --- a/usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/nwamcfg_grammar.y +++ b/usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/nwamcfg_grammar.y @@ -23,6 +23,7 @@ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright (c) 2016, Chris Fraire . */ #include @@ -67,6 +68,7 @@ extern boolean_t newline_terminated; %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 TOKEN EQUAL OPTION %type resource1_type LOC NCP ENM WLAN @@ -86,6 +88,7 @@ extern boolean_t newline_terminated; 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 command %type cancel_command CANCEL %type clear_command CLEAR @@ -617,11 +620,26 @@ list_command: LIST 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 @@ select_command: SELECT 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 @@ property_type: UNKNOWN { $$ = PT_UNKNOWN; } | 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; } %% diff --git a/usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/nwamcfg_lex.l b/usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/nwamcfg_lex.l index e6fc23ff20..e237f43475 100644 --- a/usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/nwamcfg_lex.l +++ b/usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/nwamcfg_lex.l @@ -23,6 +23,7 @@ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright (c) 2016, Chris Fraire . */ #include @@ -182,6 +183,8 @@ char *safe_strdup(char *s); ipv6-addrsrc { return IPV6_ADDRSRC; } ipv6-addr { return IPV6_ADDR; } ipv6-default-route { return IPV6_DEFAULT_ROUTE; } +ip-primary { return IP_PRIMARY; } +ip-reqhost { return IP_REQHOST; } state { return ENM_STATE; } fmri { return ENM_FMRI; } diff --git a/usr/src/common/net/dhcp/dhcp_impl.h b/usr/src/common/net/dhcp/dhcp_impl.h index 8d9dca577b..de9255aeaa 100644 --- a/usr/src/common/net/dhcp/dhcp_impl.h +++ b/usr/src/common/net/dhcp/dhcp_impl.h @@ -21,13 +21,12 @@ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright (c) 2016, Chris Fraire . */ #ifndef _DHCP_IMPL_H #define _DHCP_IMPL_H -#pragma ident "%Z%%M% %I% %E% SMI" - /* * Common definitions used by Sun DHCP implementations */ @@ -94,6 +93,11 @@ typedef struct { 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; diff --git a/usr/src/head/arpa/nameser.h b/usr/src/head/arpa/nameser.h index 29a1c5b38e..dc199077b3 100644 --- a/usr/src/head/arpa/nameser.h +++ b/usr/src/head/arpa/nameser.h @@ -1,6 +1,7 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright (c) 2016, Chris Fraire . */ /* @@ -576,6 +577,7 @@ typedef enum __ns_cert_types { #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 @@ uint32_t ns_datetosecs(const char *cp, int *errp); 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, diff --git a/usr/src/lib/Makefile b/usr/src/lib/Makefile index 36367793ac..debd9cd0e4 100644 --- a/usr/src/lib/Makefile +++ b/usr/src/lib/Makefile @@ -28,6 +28,7 @@ # Copyright (c) 2015 Gary Mills # Copyright 2016 Toomas Soome # Copyright 2017 Nexenta Systems, Inc. +# Copyright (c) 2016, Chris Fraire . # include ../Makefile.master @@ -619,7 +620,7 @@ $(INTEL_BLD)libgrubmgmt: libfdisk 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 @@ -629,7 +630,7 @@ libldap5: libsasl 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 diff --git a/usr/src/lib/libipadm/Makefile.com b/usr/src/lib/libipadm/Makefile.com index 33685ff755..d195ea19c6 100644 --- a/usr/src/lib/libipadm/Makefile.com +++ b/usr/src/lib/libipadm/Makefile.com @@ -20,6 +20,7 @@ # # # Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2016, Chris Fraire . # # @@ -35,7 +36,7 @@ include ../../Makefile.rootfs LIBS = $(DYNLIB) $(LINTLIB) LDLIBS += -lc -lnsl -linetutil -lsocket -ldlpi -lnvpair -ldhcpagent \ - -ldladm -lsecdb + -ldladm -lsecdb -ldhcputil SRCDIR = ../common $(LINTLIB) := SRCS = $(SRCDIR)/$(LINTSRC) diff --git a/usr/src/lib/libipadm/common/ipadm_addr.c b/usr/src/lib/libipadm/common/ipadm_addr.c index a62bcb165b..0b1de43c14 100644 --- a/usr/src/lib/libipadm/common/ipadm_addr.c +++ b/usr/src/lib/libipadm/common/ipadm_addr.c @@ -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-2017, Chris Fraire . */ /* @@ -32,6 +33,7 @@ */ #include #include +#include #include #include #include @@ -48,6 +50,8 @@ #include #include #include +#include +#include #include #include #include @@ -64,10 +68,13 @@ 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_dhcp_status(ipadm_addrobj_t addr, + dhcp_status_t *status, int *dhcperror); static ipadm_status_t i_ipadm_validate_create_addr(ipadm_handle_t, ipadm_addrobj_t, uint32_t); static ipadm_status_t i_ipadm_addr_persist_nvl(ipadm_handle_t, nvlist_t *, @@ -77,6 +84,7 @@ static ipadm_status_t i_ipadm_get_default_prefixlen(struct sockaddr_storage *, 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,15 +93,20 @@ static boolean_t i_ipadm_is_user_aobjname_valid(const char *); * 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[] = { @@ -104,13 +117,24 @@ ipadm_prop_desc_t ipadm_addrprop_table[] = { 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 +225,23 @@ i_ipadm_get_addrobj(ipadm_handle_t iph, ipadm_addrobj_t ipaddr) 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: + if (strlcpy(ipaddr->ipadm_reqhost, rval.ipmgmt_ir_reqhost, + sizeof (ipaddr->ipadm_reqhost)) >= + sizeof (ipaddr->ipadm_reqhost)) { + /* + * shouldn't get here as the buffers are defined + * with same length, MAX_NAME_LEN + */ + return (IPADM_FAILURE); + } + break; + default: + break; } return (IPADM_SUCCESS); @@ -1041,6 +1079,84 @@ i_ipadm_set_zone(ipadm_handle_t iph, const void *arg, } /* + * 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 +1487,90 @@ i_ipadm_get_zone(ipadm_handle_t iph, const void *arg, 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) { + dhcp_status_t dhcp_status; + ipadm_status_t ipc_status; + int error; + + ipc_status = i_ipadm_dhcp_status(ipaddr, &dhcp_status, + &error); + if (ipc_status != IPADM_SUCCESS && + ipc_status != IPADM_NOTFOUND) + return (ipc_status); + + onoff = dhcp_status.if_dflags & DHCP_IF_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 +1975,7 @@ ipadm_get_addr(const ipadm_addrobj_t ipaddr, struct sockaddr_storage *addr) return (IPADM_SUCCESS); } + /* * Set up tunnel destination address in ipaddr by contacting DNS. * The function works similar to ipadm_set_addr(). @@ -1901,6 +2102,28 @@ ipadm_set_wait_time(ipadm_addrobj_t ipaddr, int32_t wait) } /* + * 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 +2273,15 @@ i_ipadm_enable_static(ipadm_handle_t iph, const char *ifname, nvlist_t *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; @@ -2067,6 +2291,8 @@ i_ipadm_enable_dhcp(ipadm_handle_t iph, const char *ifname, nvlist_t *nvl) 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 +2314,9 @@ i_ipadm_enable_dhcp(ipadm_handle_t iph, const char *ifname, nvlist_t *nvl) 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 +2959,7 @@ retry: } } status = i_ipadm_addr_persist(iph, ipaddr, default_prefixlen, - flags); + flags, NULL); } ret: if (status != IPADM_SUCCESS && !legacy) @@ -2887,7 +3116,7 @@ retry: 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 +3176,10 @@ 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; @@ -2956,9 +3189,37 @@ i_ipadm_op_dhcp(ipadm_addrobj_t addr, dhcp_ipc_type_t type, int *dhcperror) 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 +3230,7 @@ i_ipadm_op_dhcp(ipadm_addrobj_t addr, dhcp_ipc_type_t type, int *dhcperror) /* 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); @@ -2986,6 +3248,62 @@ i_ipadm_op_dhcp(ipadm_addrobj_t addr, dhcp_ipc_type_t type, int *dhcperror) } /* + * Communicates with the dhcpagent to send a dhcp message of type + * DHCP_STATUS, and copy on success into the `status' instance owned by the + * caller. It returns any dhcp error in `dhcperror' if a non-null pointer + * is provided. + */ +static ipadm_status_t +i_ipadm_dhcp_status(ipadm_addrobj_t addr, dhcp_status_t *status, + int *dhcperror) +{ + dhcp_ipc_type_t type = DHCP_STATUS; + dhcp_ipc_request_t *request; + dhcp_ipc_reply_t *reply; + dhcp_status_t *private_status; + size_t reply_size; + int error; + + if (addr->ipadm_af == AF_INET6) + type |= DHCP_V6; + + request = dhcp_ipc_alloc_request(type, addr->ipadm_ifname, NULL, 0, + DHCP_TYPE_NONE); + if (request == NULL) + return (IPADM_NO_MEMORY); + + error = dhcp_ipc_make_request(request, &reply, DHCP_IPC_WAIT_DEFAULT); + free(request); + if (error != 0) { + if (dhcperror != NULL) + *dhcperror = error; + return (error != DHCP_IPC_E_TIMEOUT ? IPADM_DHCP_IPC_ERROR + : IPADM_DHCP_IPC_TIMEOUT); + } + + error = reply->return_code; + if (error == DHCP_IPC_E_UNKIF) { + free(reply); + bzero(status, sizeof (dhcp_status_t)); + return (IPADM_NOTFOUND); + } + + private_status = dhcp_ipc_get_data(reply, &reply_size, NULL); + if (reply_size < DHCP_STATUS_VER1_SIZE) { + free(reply); + return (IPADM_DHCP_IPC_ERROR); + } + + /* + * Copy the status out of the memory allocated by this function into + * memory owned by the caller. + */ + *status = *private_status; + free(reply); + return (IPADM_SUCCESS); +} + +/* * Returns the IP addresses of the specified interface in both the * active and the persistent configuration. If no * interface is specified, it returns all non-zero IP addresses @@ -3026,21 +3344,19 @@ ipadm_free_addr_info(ipadm_addr_info_t *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. @@ -3060,8 +3376,6 @@ i_ipadm_addr_persist(ipadm_handle_t iph, const ipadm_addrobj_t ipaddr, 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 +3385,18 @@ i_ipadm_addr_persist(ipadm_handle_t iph, const ipadm_addrobj_t ipaddr, 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 +3420,64 @@ i_ipadm_addr_persist(ipadm_handle_t iph, const ipadm_addrobj_t ipaddr, 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; + } + + if (pval != NULL) { + assert(pdp != NULL); + status = i_ipadm_persist_propval(iph, pdp, pval, + ipaddr, flags); } - assert(pdp != NULL); - status = i_ipadm_persist_propval(iph, pdp, pval, ipaddr, flags); } + ret: nvlist_free(nvl); return (status); @@ -3322,7 +3687,6 @@ ipadm_refresh_addr(ipadm_handle_t iph, const char *aobjname, 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 +3728,7 @@ ipadm_refresh_addr(ipadm_handle_t iph, const char *aobjname, 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 +3736,30 @@ ipadm_refresh_addr(ipadm_handle_t iph, const char *aobjname, } /* + * 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 +3874,45 @@ i_ipadm_validate_create_addr(ipadm_handle_t iph, ipadm_addrobj_t ipaddr, } 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 +3961,9 @@ ipadm_enable_addr(ipadm_handle_t iph, const char *aobjname, uint32_t flags) 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; diff --git a/usr/src/lib/libipadm/common/ipadm_ipmgmt.h b/usr/src/lib/libipadm/common/ipadm_ipmgmt.h index 6a53cebc4b..06e870f30b 100644 --- a/usr/src/lib/libipadm/common/ipadm_ipmgmt.h +++ b/usr/src/lib/libipadm/common/ipadm_ipmgmt.h @@ -20,6 +20,7 @@ */ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, Chris Fraire . */ #ifndef _IPADM_IPMGMT_H @@ -51,8 +52,8 @@ extern "C" { * 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 @@ extern "C" { #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,20 @@ typedef struct ipmgmt_prop_arg_s { #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 { + 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 +237,15 @@ typedef struct ipmgmt_aobjop_arg_s { * - 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 +281,14 @@ typedef struct ipmgmt_aobjop_rval_s { 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_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, diff --git a/usr/src/lib/libipadm/common/ipadm_ndpd.c b/usr/src/lib/libipadm/common/ipadm_ndpd.c index 88b9560236..29ea8ccf51 100644 --- a/usr/src/lib/libipadm/common/ipadm_ndpd.c +++ b/usr/src/lib/libipadm/common/ipadm_ndpd.c @@ -20,6 +20,7 @@ */ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, Chris Fraire . */ /* @@ -107,7 +108,7 @@ i_ipadm_create_ipv6addrs(ipadm_handle_t iph, ipadm_addrobj_t addr, } /* 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, diff --git a/usr/src/lib/libipadm/common/libipadm.c b/usr/src/lib/libipadm/common/libipadm.c index 527f735e17..e384229c29 100644 --- a/usr/src/lib/libipadm/common/libipadm.c +++ b/usr/src/lib/libipadm/common/libipadm.c @@ -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 . */ #include @@ -728,12 +729,13 @@ i_ipadm_init_ifobj(ipadm_handle_t iph, const char *ifname, nvlist_t *ifnvl) } 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; @@ -956,3 +958,80 @@ reopen: } 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: + * + * ::= | " " + * ::=