@@ -21,6 +21,7 @@

/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
*/

#include <arpa/inet.h>
@@ -33,6 +34,7 @@
#include <netinet/in.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
@@ -128,6 +130,17 @@ 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 @@ populate_ip_ncu_properties(nwam_ncu_handle_t ncuh, nwamd_ncu_t *ncu_data)
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 @@ populate_ip_ncu_properties(nwam_ncu_handle_t ncuh, nwamd_ncu_t *ncu_data)
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++) {
@@ -21,6 +21,7 @@

/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
*/

#ifndef _NCU_H
@@ -225,6 +226,8 @@ 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);

@@ -21,6 +21,7 @@
#
# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
# Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
#

PROG = dhcpagent
@@ -45,7 +46,8 @@ 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
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016-2017, Chris Fraire <cfraire@me.com>.
*/

#include <sys/types.h>
@@ -36,6 +37,8 @@
#include <dhcpagent_ipc.h>
#include <dhcpagent_util.h>
#include <dhcpmsg.h>
#include <dhcp_inittab.h>
#include <dhcp_symbol.h>
#include <netinet/dhcp.h>
#include <net/route.h>
#include <sys/sockio.h>
@@ -70,6 +73,10 @@ static boolean_t 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 @@ ipc_event(iu_eh_t *ehp, int fd, short events, iu_event_id_t id, void *arg)
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 @@ ipc_event(iu_eh_t *ehp, int fd, short events, iu_event_id_t id, void *arg)
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 @@ ipc_event(iu_eh_t *ehp, int fd, short events, iu_event_id_t id, void *arg)
}

/*
* 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 @@ ipc_event(iu_eh_t *ehp, int fd, short events, iu_event_id_t id, void *arg)
(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 @@ ipc_event(iu_eh_t *ehp, int fd, short events, iu_event_id_t id, void *arg)
PKT_LIST *plp[2];

deprecate_leases(dsmp);
dhcp_smach_set_msg_reqhost(dsmp, iap);

/*
* if we have a valid hostconf lying around, then jump
@@ -1058,6 +1063,147 @@ ipc_event(iu_eh_t *ehp, int fd, short events, iu_event_id_t id, void *arg)
}
}

/*
* 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
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2017, Chris Fraire <cfraire@me.com>.
*
* 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;
@@ -21,6 +21,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
* Copyright (c) 2016-2017, Chris Fraire <cfraire@me.com>.
*/

#include <sys/types.h>
@@ -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 },
};

/*
@@ -21,6 +21,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
* Copyright (c) 2016-2017, Chris Fraire <cfraire@me.com>.
*/

#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"
@@ -22,6 +22,7 @@
#
# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
# Copyright (c) 2016-2017, Chris Fraire <cfraire@me.com>.
#

#
@@ -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.<if>,
# which must contain a line of the form
# enabled, the agent will attempt to use an -h,--reqhost option saved with
# ipadm(1M) or an ncu ip-reqhost property set with nwamcfg(1M); or else
# attempt to find a host name in /etc/hostname.<if>, which must contain a
# line of the form
#
# inet name
#
# where "name" is a single RFC 1101-compliant token. If found, the token
# will be used to request that host name from the DHCP server. To prevent
# this, uncomment the following line.
# where "name" is a single RFC 1101-compliant token; or else use
# nodename(4) for a DHCP interface flagged as primary. If found in any of
# these configurations, the token will be used to request that host name
# from the DHCP server. To prevent this, uncomment the following line.
#
# REQUEST_HOSTNAME=no

@@ -21,13 +21,13 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
* Copyright (c) 2016-2017, Chris Fraire <cfraire@me.com>.
*
* INIT_REBOOT state of the DHCP client state machine.
*/

#include <sys/types.h>
#include <stdio.h>
#include <limits.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/dhcp.h>
@@ -58,8 +58,6 @@ 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.<ifname>
*/
if (df_get_bool(dsmp->dsm_name, dsmp->dsm_isv6, DF_REQUEST_HOSTNAME)) {
(void) snprintf(hostfile, sizeof (hostfile), "/etc/hostname.%s",
dsmp->dsm_name);

if ((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);

@@ -21,10 +21,9 @@
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
* Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
*/

#pragma ident "%Z%%M% %I% %E% SMI"

#include <stdlib.h>
#include <sys/types.h>
#include <dhcpmsg.h>
@@ -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;

@@ -21,6 +21,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
* Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
*/

#include <string.h>
@@ -413,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);
}

/*
@@ -21,13 +21,12 @@
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
* Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
*/

#ifndef _PACKET_H
#define _PACKET_H

#pragma ident "%Z%%M% %I% %E% SMI"

#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/dhcp.h>
@@ -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);
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016-2017, Chris Fraire <cfraire@me.com>.
*/

#include <sys/types.h>
@@ -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));
}
@@ -21,6 +21,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
* Copyright (c) 2016-2017, Chris Fraire <cfraire@me.com>.
*
* 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));
}
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016-2017, Chris Fraire <cfraire@me.com>.
*
* SELECTING state of the client state machine.
*/
@@ -29,7 +30,6 @@
#include <stdlib.h>
#include <strings.h>
#include <time.h>
#include <limits.h>
#include <netinet/in.h>
#include <net/route.h>
#include <net/if.h>
@@ -106,8 +106,6 @@ 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),
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016-2017, Chris Fraire <cfraire@me.com>.
*
* 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 @@ get_smach_cid(dhcp_smach_t *dsmp)
* 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);
@@ -21,6 +21,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
* Copyright (c) 2016-2017, Chris Fraire <cfraire@me.com>.
*/

#ifndef STATES_H
@@ -200,6 +201,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

Large diffs are not rendered by default.

@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016-2017, Chris Fraire <cfraire@me.com>.
*/

#ifndef UTIL_H
@@ -33,6 +34,7 @@
#include <dhcpagent_ipc.h>

#include "common.h"
#include "packet.h"

/*
* general utility functions which have no better home. see util.c
@@ -75,6 +77,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
}
@@ -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 <cfraire@me.com>.
*/

#include <arpa/inet.h>
@@ -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<addrobj>\n"
"\tcreate-addr\t[-t] -T dhcp [-w <seconds> | forever] <addrobj>\n"
"\tcreate-addr\t[-t] -T dhcp [-w <seconds> | forever]\n"
"\t\t\t[-1] [-h <hostname>] <addrobj>\n"
"\tcreate-addr\t[-t] -T addrconf [-i interface-id]\n"
"\t\t\t[-p {stateful|stateless}={yes|no}] <addrobj>" },
{ "down-addr", do_down_addr, "\tdown-addr\t[-t] <addrobj>" },
@@ -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) {
@@ -22,6 +22,7 @@
#
# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
# Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
#
# cmd/cmd-inet/usr.sbin/nwamcfg/Makefile

@@ -56,7 +57,7 @@ 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 =
@@ -21,6 +21,7 @@

/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
*/

/*
@@ -198,7 +199,9 @@ 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 } }
};

@@ -22,6 +22,7 @@
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
* Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
*/

#ifndef _NWAMCFG_H
@@ -133,12 +134,14 @@ 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

@@ -23,6 +23,7 @@
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
* Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
*/

#include <stdio.h>
@@ -67,6 +68,7 @@ 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 <strval> TOKEN EQUAL OPTION
%type <ival> 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 <cmd> command
%type <cmd> cancel_command CANCEL
%type <cmd> 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; }

%%
@@ -23,6 +23,7 @@
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
* Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
*/

#include <string.h>
@@ -182,6 +183,8 @@ char *safe_strdup(char *s);
<TSTATE>ipv6-addrsrc { return IPV6_ADDRSRC; }
<TSTATE>ipv6-addr { return IPV6_ADDR; }
<TSTATE>ipv6-default-route { return IPV6_DEFAULT_ROUTE; }
<TSTATE>ip-primary { return IP_PRIMARY; }
<TSTATE>ip-reqhost { return IP_REQHOST; }

<TSTATE>state { return ENM_STATE; }
<TSTATE>fmri { return ENM_FMRI; }
@@ -21,13 +21,12 @@
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
* Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
*/

#ifndef _DHCP_IMPL_H
#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;
@@ -1,6 +1,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
* Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
*/

/*
@@ -576,6 +577,7 @@ 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,
@@ -28,6 +28,7 @@
# Copyright (c) 2015 Gary Mills
# Copyright 2016 Toomas Soome <tsoome@me.com>
# Copyright 2017 Nexenta Systems, Inc.
# Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
#

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
@@ -20,6 +20,7 @@
#
#
# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
#
#

@@ -35,7 +36,7 @@ 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)

Large diffs are not rendered by default.

@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
*/

#ifndef _IPADM_IPMGMT_H
@@ -51,8 +52,8 @@ 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,
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
*/

/*
@@ -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,
@@ -22,6 +22,7 @@
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright 2014 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
*/

#include <stdio.h>
@@ -728,12 +729,13 @@ 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 @@ ipadm_door_call(ipadm_handle_t iph, void *arg, size_t asize, void **rbufp,
}
return (err);
}

/*
* ipadm_is_nil_hostname() : Determine if the `hostname' is nil: i.e.,
* NULL, empty, or a single space (e.g., as returned by
* domainname(1M)/sysinfo).
*
* input: const char *: the hostname to inspect;
* output: boolean_t: B_TRUE if `hostname' is not NULL satisfies the
* criteria above; otherwise, B_FALSE;
*/

boolean_t
ipadm_is_nil_hostname(const char *hostname)
{
return (hostname == NULL || *hostname == '\0' ||
(*hostname == ' ' && hostname[1] == '\0'));
}

/*
* ipadm_is_valid_hostname(): check whether a string is a valid hostname
*
* input: const char *: the string to verify as a hostname
* output: boolean_t: B_TRUE if the string is a valid hostname
*
* Note that we accept host names beginning with a digit, which is not
* strictly legal according to the RFCs but is in common practice, so we
* endeavour to not break what customers are using.
*
* RFC 1035 limits a wire-format domain name to 255 octets. For a printable
* `hostname' as we have, the limit is therefore 253 characters (excluding
* the terminating '\0'--or 254 characters if the last character of
* `hostname' is a '.'.
*
* Excerpt from section 2.3.1., Preferred name syntax:
*
* <domain> ::= <subdomain> | " "
* <subdomain> ::= <label> | <subdomain> "." <label>
* <label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
* <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
* <let-dig-hyp> ::= <let-dig> | "-"
* <let-dig> ::= <letter> | <digit>
*/
boolean_t
ipadm_is_valid_hostname(const char *hostname)
{
const size_t MAX_READABLE_NAME_LEN = 253;
char last_char;
size_t has_last_dot, namelen, i;

if (hostname == NULL)
return (B_FALSE);

namelen = strlen(hostname);
if (namelen < 1)
return (B_FALSE);

last_char = hostname[namelen - 1];
has_last_dot = last_char == '.';

if (namelen > MAX_READABLE_NAME_LEN + has_last_dot ||
last_char == '-')
return (B_FALSE);

for (i = 0; hostname[i] != '\0'; i++) {
/*
* As noted above, this deviates from RFC 1035 in that it
* allows a leading digit.
*/
if (isalpha(hostname[i]) || isdigit(hostname[i]) ||
(((hostname[i] == '-') || (hostname[i] == '.')) && (i > 0)))
continue;

return (B_FALSE);
}

return (B_TRUE);
}
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
*/
#ifndef _LIBIPADM_H
#define _LIBIPADM_H
@@ -29,6 +30,7 @@ extern "C" {
#endif

#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netdb.h>
@@ -100,8 +102,8 @@ typedef enum {
*
* - IPADM_OPT_PERSIST:
* For all the create/delete/up/down/set/get functions,
* requests to persist the configuration so that it can be
* re-enabled or reapplied on boot.
* requests to persist the configuration so that it can be
* re-enabled or re-applied on boot.
*
* - IPADM_OPT_ACTIVE:
* Requests to apply configuration without persisting it and
@@ -141,7 +143,17 @@ typedef enum {
* Used to bring up a static address on creation
*
* - IPADM_OPT_V46
* Used to plumb both IPv4 and IPv6 interfaces by ipadm_create_addr()
* Used to plumb both IPv4 and IPv6 interfaces by ipadm_create_addr()
*
* - IPADM_OPT_SET_PROPS
* Used to indicate the update changes the running configuration of
* "props" data on the object. The props are cached there on the parent,
* but the PROPS_ONLY change does not affect the ACTIVE/PERSIST state of
* the parent.
*
* - IPADM_OPT_PERSIST_PROPS
* Used when IPADM_OPT_SET_PROPS is active to indicate the update changes
* the persistent configuration of the "props" data on the object.
*/
#define IPADM_OPT_PERSIST 0x00000001
#define IPADM_OPT_ACTIVE 0x00000002
@@ -157,6 +169,8 @@ typedef enum {
#define IPADM_OPT_INFORM 0x00000800
#define IPADM_OPT_UP 0x00001000
#define IPADM_OPT_V46 0x00002000
#define IPADM_OPT_SET_PROPS 0x00004000
#define IPADM_OPT_PERSIST_PROPS 0x00008000

/* IPADM property class */
#define IPADMPROP_CLASS_MODULE 0x00000001 /* on 'protocol' only */
@@ -257,7 +271,7 @@ extern void ipadm_close(ipadm_handle_t);
/* Check authorization for network configuration */
extern boolean_t ipadm_check_auth(void);
/*
* Interface mangement functions
* Interface management functions
*/
extern ipadm_status_t ipadm_create_if(ipadm_handle_t, char *, sa_family_t,
uint32_t);
@@ -315,6 +329,7 @@ extern ipadm_status_t ipadm_set_stateful(ipadm_addrobj_t, boolean_t);
/* Functions to set fields in addrobj for DHCP */
extern ipadm_status_t ipadm_set_primary(ipadm_addrobj_t, boolean_t);
extern ipadm_status_t ipadm_set_wait_time(ipadm_addrobj_t, int32_t);
extern ipadm_status_t ipadm_set_reqhost(ipadm_addrobj_t, const char *);

/*
* Property management functions
@@ -357,6 +372,8 @@ extern int ipadm_legacy2new_propname(const char *, char *,
uint_t, uint_t *);
extern int ipadm_new2legacy_propname(const char *, char *,
uint_t, uint_t);
extern boolean_t ipadm_is_valid_hostname(const char *hostname);
extern boolean_t ipadm_is_nil_hostname(const char *hostname);

#ifdef __cplusplus
}
@@ -21,6 +21,7 @@
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013 by Delphix. All rights reserved.
* Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
*/

#ifndef _LIBIPADM_IMPL_H
@@ -87,6 +88,7 @@ struct ipadm_addrobj_s {
struct {
boolean_t ipadm_primary;
int32_t ipadm_wait;
char ipadm_reqhost[MAXNAMELEN];
} ipadm_dhcp_s;
} ipadm_addr_u;
};
@@ -102,6 +104,7 @@ struct ipadm_addrobj_s {
#define ipadm_stateful ipadm_addr_u.ipadm_ipv6_intfid_s.ipadm_stateful
#define ipadm_primary ipadm_addr_u.ipadm_dhcp_s.ipadm_primary
#define ipadm_wait ipadm_addr_u.ipadm_dhcp_s.ipadm_wait
#define ipadm_reqhost ipadm_addr_u.ipadm_dhcp_s.ipadm_reqhost

/*
* Data structures and callback functions related to property management
@@ -145,7 +148,8 @@ extern ipadm_status_t i_ipadm_init_ifobj(ipadm_handle_t, const char *,
nvlist_t *);
extern ipadm_status_t i_ipadm_init_addrobj(ipadm_handle_t, nvlist_t *);
extern ipadm_status_t i_ipadm_addr_persist(ipadm_handle_t,
const ipadm_addrobj_t, boolean_t, uint32_t);
const ipadm_addrobj_t, boolean_t, uint32_t,
const char *);
extern ipadm_status_t i_ipadm_delete_addr(ipadm_handle_t, ipadm_addrobj_t);
extern int i_ipadm_strioctl(int, int, char *, int);
extern boolean_t i_ipadm_is_loopback(const char *);
@@ -185,7 +189,7 @@ extern ipadm_status_t i_ipadm_get_persist_propval(ipadm_handle_t,
/* ipadm_addr.c */
extern void i_ipadm_init_addr(ipadm_addrobj_t, const char *,
const char *, ipadm_addr_type_t);
extern ipadm_status_t i_ipadm_merge_prefixlen_from_nvl(nvlist_t *, nvlist_t *,
extern ipadm_status_t i_ipadm_merge_addrprops_from_nvl(nvlist_t *, nvlist_t *,
const char *);
extern ipadm_status_t i_ipadm_get_addrobj(ipadm_handle_t, ipadm_addrobj_t);
extern ipadm_status_t i_ipadm_enable_static(ipadm_handle_t, const char *,
@@ -20,6 +20,7 @@
#
#
# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
#

#
@@ -67,6 +68,8 @@ SYMBOL_VERSION SUNWprivate_1.1 {
ipadm_if_info;
ipadm_if_move;
ipadm_init_net_from_gz;
ipadm_is_nil_hostname;
ipadm_is_valid_hostname;
ipadm_legacy2new_propname;
ipadm_ndpd_read;
ipadm_ndpd_write;
@@ -80,6 +83,7 @@ SYMBOL_VERSION SUNWprivate_1.1 {
ipadm_set_addr;
ipadm_set_addrprop;
ipadm_set_dst_addr;
ipadm_set_reqhost;
ipadm_set_ifprop;
ipadm_set_interface_id;
ipadm_set_primary;
@@ -21,6 +21,7 @@
#
# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
# Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
#

LIBRARY= libnwam.a
@@ -43,7 +44,8 @@ include ../../Makefile.lib
include ../../Makefile.rootfs

LIBS = $(DYNLIB) $(LINTLIB)
LDLIBS += -lbsm -lc -ldladm -lnsl -lnvpair -lscf -lsecdb -lsocket
LDLIBS += -lbsm -lc -ldladm -lnsl -lnvpair -lscf -lsecdb -lsocket \
-lipadm

SRCDIR = ../common
$(LINTLIB) := SRCS= $(SRCDIR)/$(LINTSRC)
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
*/

/*
@@ -447,6 +448,8 @@ typedef enum {
#define NWAM_NCU_PROP_IPV6_ADDRSRC "ipv6-addrsrc"
#define NWAM_NCU_PROP_IPV6_ADDR "ipv6-addr"
#define NWAM_NCU_PROP_IPV6_DEFAULT_ROUTE "ipv6-default-route"
#define NWAM_NCU_PROP_IP_PRIMARY "ip-primary"
#define NWAM_NCU_PROP_IP_REQHOST "ip-reqhost"

/* Some properties should only be set on creation */
#define NWAM_NCU_PROP_SETONCE(prop) \
@@ -22,6 +22,7 @@
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
* Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
*/

#include <assert.h>
@@ -39,6 +40,7 @@
#include <strings.h>
#include <unistd.h>
#include <libdladm.h>
#include <libipadm.h>

#include "libnwam_impl.h"
#include <libnwam_priv.h>
@@ -73,6 +75,7 @@ static nwam_error_t valid_link_mtu(nwam_value_t);
static nwam_error_t valid_ip_version(nwam_value_t);
static nwam_error_t valid_addrsrc_v4(nwam_value_t);
static nwam_error_t valid_addrsrc_v6(nwam_value_t);
static nwam_error_t valid_reqhost(nwam_value_t);

struct nwam_prop_table_entry ncu_prop_table_entries[] = {
{NWAM_NCU_PROP_TYPE, NWAM_VALUE_TYPE_UINT64, B_FALSE, 1, 1, valid_type,
@@ -149,7 +152,16 @@ struct nwam_prop_table_entry ncu_prop_table_entries[] = {
{NWAM_NCU_PROP_IPV6_DEFAULT_ROUTE, NWAM_VALUE_TYPE_STRING, B_FALSE, 0,
1, nwam_valid_route_v6,
"specifies per-interface default IPv6 route",
NWAM_FLAG_NCU_TYPE_INTERFACE, NWAM_FLAG_NCU_CLASS_ALL_INTERFACE}
NWAM_FLAG_NCU_TYPE_INTERFACE, NWAM_FLAG_NCU_CLASS_ALL_INTERFACE},
{NWAM_NCU_PROP_IP_PRIMARY, NWAM_VALUE_TYPE_BOOLEAN, B_FALSE, 0,
1, nwam_valid_boolean,
"specifies the status of an interface as primary for the delivery"
" of client-wide configuration data",
NWAM_FLAG_NCU_TYPE_INTERFACE, NWAM_FLAG_NCU_CLASS_ALL_INTERFACE},
{NWAM_NCU_PROP_IP_REQHOST, NWAM_VALUE_TYPE_STRING, B_FALSE, 0,
1, valid_reqhost,
"specifies a requested hostname for the interface",
NWAM_FLAG_NCU_TYPE_INTERFACE, NWAM_FLAG_NCU_CLASS_ALL_INTERFACE},
};

#define NWAM_NUM_NCU_PROPS (sizeof (ncu_prop_table_entries) / \
@@ -1648,6 +1660,17 @@ valid_addrsrc_v6(nwam_value_t value)
return (NWAM_ENTITY_INVALID_VALUE);
}

static nwam_error_t
valid_reqhost(nwam_value_t value)
{
char *hostname;

if (nwam_value_get_string(value, &hostname) != NWAM_SUCCESS)
return (NWAM_ENTITY_INVALID_VALUE);
return (ipadm_is_valid_hostname(hostname) ? NWAM_SUCCESS
: NWAM_ENTITY_INVALID_VALUE);
}

/* ARGSUSED0 */
static nwam_error_t
valid_link_mtu(nwam_value_t value)
@@ -20,6 +20,7 @@
#
#
# Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
#

#
@@ -104,6 +105,7 @@ SYMBOL_VERSION SUNWprivate_2.2 {
isc_puthexstring;
__log_close_debug_channels;
__memactive;
__ns_name_pton2;
p_sockun;
res_gethostbyname2;
res_getservers;
@@ -1,9 +1,10 @@
'\" te
.\" Copyright (c) 1992-1996 Competitive Automation, Inc. Copyright (c) 2009 Sun Microsystems, Inc. All Rights Reserved.
.\" Copyright (c) 2016-2017, Chris Fraire <cfraire@me.com>.
.\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License"). You may not use this file except in compliance with the License. You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.
.\" See the License for the specific language governing permissions and limitations under the License. When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE. If applicable, add the following below this CDDL HEADER, with the
.\" fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
.TH DHCPAGENT 1M "Dec 11, 2015"
.TH DHCPAGENT 1M "Jun 30, 2017"
.SH NAME
dhcpagent \- Dynamic Host Configuration Protocol (DHCP) client daemon
.SH SYNOPSIS
@@ -15,7 +16,7 @@ dhcpagent \- Dynamic Host Configuration Protocol (DHCP) client daemon
.SH DESCRIPTION
.LP
\fBdhcpagent\fR implements the client half of the Dynamic Host Configuration
Protocol \fB(DHCP)\fR for machines running Solaris software.
Protocol \fB(DHCP)\fR for machines running illumos software.
.sp
.LP
The \fBdhcpagent\fR daemon obtains configuration parameters for the client
@@ -28,11 +29,12 @@ it must negotiate an extension using \fBDHCP\fR. For this reason,
powers down.
.sp
.LP
For IPv4, the \fBdhcpagent\fR daemon is controlled through \fBifconfig\fR(1M)
in much the same way that the \fBinit\fR(1M) daemon is controlled by
\fBtelinit\fR(1M). \fBdhcpagent\fR can be invoked as a user process, albeit one
requiring root privileges, but this is not necessary, as \fBifconfig\fR(1M)
will start it automatically.
For IPv4, the \fBdhcpagent\fR daemon is controlled through \fBipadm\fR(1M),
\fBnwamcfg\fR(1M), or \fBifconfig\fR(1M) in much the same way that the
\fBinit\fR(1M) daemon is controlled by \fBtelinit\fR(1M). \fBdhcpagent\fR can
be invoked as a user process, albeit one requiring root privileges, but this is
not necessary, as \fBipadm\fR(1M), \fBnwamcfg\fR(1M), or \fBifconfig\fR(1M)
will start \fBdhcpagent\fR automatically.
.sp
.LP
For IPv6, the \fBdhcpagent\fR daemon is invoked automatically by
@@ -41,16 +43,20 @@ necessary.
.sp
.LP
When invoked, \fBdhcpagent\fR enters a passive state while it awaits
instructions from \fBifconfig\fR(1M) or \fBin.ndpd\fR(1M). When it receives a
command to configure an interface, it brings up the interface (if necessary)
and starts DHCP. Once DHCP is complete, \fBdhcpagent\fR can be queried for the
values of the various network parameters. In addition, if DHCP was used to
obtain a lease on an address for an interface, it configures the address for
use. When a lease is obtained, it is automatically renewed as necessary. If the
instructions from \fBipadm\fR(1M), \fBnwamcfg\fR(1M), \fBifconfig\fR(1M), or
\fBin.ndpd\fR(1M). When \fBdhcpagent\fR receives a command to configure an
interface, \fBdhcpagent\fR brings up the interface (if necessary) and starts
DHCP. Once DHCP is complete, \fBdhcpagent\fR can be queried for the values of
the various network parameters. In addition, if DHCP was used to obtain a lease
on an address for an interface, \fBdhcpagent\fR configures the address for use.
When a lease is obtained, it is automatically renewed as necessary. If the
lease cannot be renewed, \fBdhcpagent\fR will unconfigure the address, but the
interface will be left up and \fBdhcpagent\fR will attempt to acquire a new
address lease. \fBdhcpagent\fR monitors system suspend/resume events and will
validate any non-permanent leases with the DHCP server upon resume. Similarly,
interface will be left up, and \fBdhcpagent\fR will attempt to acquire a new
address lease.
.sp
.LP
\fBdhcpagent\fR monitors system suspend/resume events and will validate any
non-permanent leases with the DHCP server upon resume. Similarly,
\fBdhcpagent\fR monitors link up/down events and will validate any
non-permanent leases with the DHCP server when the downed link is brought back
up. The lease validation mechanism will restart DHCP if the server indicates
@@ -102,10 +108,10 @@ parameters in the case where no specific interface is requested. See
.sp
.LP
For IPv4, the \fBdhcpagent\fR daemon can be configured to request a particular
host name. See the \fBREQUEST_HOSTNAME\fR description in the \fBFILES\fR
section. When first configuring a client to request a host name, you must
perform the following steps as root to ensure that the full DHCP negotiation
takes place:
Fully Qualified Domain Name (FQDN) or host name. See the \fBREQUEST_FQDN\fR or
\fBREQUEST_HOSTNAME\fR description in the \fBFILES\fR section. When first
configuring a client to request an FQDN or host name, you must perform the
following steps as root to ensure that the full DHCP negotiation takes place:
.sp
.in +2
.nf
@@ -494,9 +500,14 @@ using REQUEST (for DHCPv4) or Confirm (DHCPv6).
.ad
.sp .6
.RS 4n
Contains persistent storage for DUID (DHCP Unique Identifier) and IAID
(Identity Association Identifier) values. The format of these files is
undocumented, and applications should not read from or write to them.
Contains persistent storage for system-generated DUID (DHCP Unique Identifier)
and interface-specific IAID (Identity Association Identifier) values which are
used if no \fBCLIENT_ID\fR is defined (see below). The format of these files is
undocumented, and applications should not read from or write to them. Instead,
\fBdhcpinfo\fR(1) can be used to query the \fBdhcpagent\fR for \fIClientID\fR.
For DHCPv6 interfaces, the result will contain the DUID. For DHCPv4 interfaces
with \fBV4_DEFAULT_IAID_DUID\fR enabled (see below), the result will contain
the IAID and DUID.
.RE

.sp
@@ -536,6 +547,8 @@ remaining) and a new one obtained.
.sp
Enabling this option is often desirable on mobile systems, such as laptops, to
allow the system to recover quickly from moves.
.sp
Default value of this option is \fIno\fR.
.RE

.sp
@@ -545,9 +558,11 @@ allow the system to recover quickly from moves.
.ad
.sp .6
.RS 4n
Indicates how long to wait between checking for valid \fBOFFER\fRs after
sending a \fBDISCOVER\fR. For DHCPv6, sets the time to wait between checking
for valid Advertisements after sending a Solicit.
Indicates how long to wait in seconds between checking for valid
\fBOFFER\fRs after sending a \fBDISCOVER\fR. For DHCPv6, sets the time to
wait between checking for valid Advertisements after sending a Solicit.
.sp
Default value of this option is \fI3\fR.
.RE

.sp
@@ -627,6 +642,26 @@ both represent raw Client ID (without RFC 4361), in hex, or NVT ASCII string
format. Thus, "\fBSun\fR" and \fB0x53756E\fR are equivalent.
.RE

.sp
.ne 2
.na
\fB\fBV4_DEFAULT_IAID_DUID\fR\fR
.ad
.sp .6
.RS 4n
Indicates whether to use, when CLIENT_ID is not defined, a system-managed,
RFC 3315-style (i.e., DHCPv6-style) binding identifier as documented in
RFC 4361, "Node-specific Client Identifiers for DHCPv4," for IPv4
interfaces which for purposes of backward compatibility do not normally get
default binding identifiers.
.sp
An IPv4 interface that is not in an IP network multipathing (IPMP) group,
that is not IP over InfiniBand (IPoIB), and that is not a logical interface
does not normally get a default binding identifier.
.sp
Default value of this option is \fIno\fR.
.RE

.sp
.ne 2
.na
@@ -655,6 +690,98 @@ parameter can be used, for example, to disable an unwanted client name or
default router.
.RE

.sp
.ne 2
.na
\fB\fBREQUEST_FQDN\fR\fR
.ad
.sp .6
.RS 4n
Indicates the client requests the DHCP server to map the client's leased
IPv4 address to the Fully Qualified Domain Name (FQDN) associated with the
network interface that performs DHCP on the client and to collaborate with
a compatible DNS server to manage A and PTR resource records for the FQDN
for the life of the lease.
.sp .6
The \fIhostname\fR in the FQDN is determined from the following possible
configurations:
.sp
.ne 2
.na
1. \fBipadm\fR(1M): include the \fB-1,--primary\fR flag when creating an
address that uses DHCP so that \fBnodename\fR(4) is used as the
\fIhostname\fR.
.ad
.sp
.ne 2
.na
2. \fBipadm\fR(1M): include the \fB-h,--reqhost\fR \fIhostname\fR switch
when executing the \fBcreate-addr -T dhcp\fR subcommand, or use the
\fBset-addrprop -p reqhost=\fR\fIhostname\fR subcommand for any existing
DHCP address.
.ad
.sp
.ne 2
.na
3. \fBnwamcfg\fR(1M): set a property,
\fBip-primary=\fR\fIon\fR, for an ncu ip that uses DHCP so that
\fBnodename\fR(4) is used as the \fIhostname\fR.
.ad
.sp
.ne 2
.na
4. \fBnwamcfg\fR(1M): set a property,
\fBip-reqhost=\fR\fIhostname\fR, for an ncu ip that uses DHCP.
.ad
.sp
The \fIhostname\fR value is either a Partially Qualified Domain Name (PQDN)
or an FQDN (i.e., a "rooted" domain name ending with a '.' or one inferred
to be an FQDN if it contains at least three DNS labels such as
srv.example.com). If a PQDN is specified, then an FQDN is constructed if
\fBDNS_DOMAINNAME\fR is defined or if \fBADOPT_DOMAINNAME\fR is set to
\fIyes\fR and an eligible domain name (as described below) is available.
.sp
If an FQDN is sent, \fBREQUEST_HOSTNAME\fR processing will not be done,
per RFC 4702 (3.1): "clients that send the Client FQDN option in their
messages MUST NOT also send the Host Name."
.sp
Default value of this option is \fIyes\fR.
.RE

.sp
.ne 2
.na
\fB\fBDNS_DOMAINNAME\fR\fR
.ad
.sp .6
.RS 4n
Indicates the value that should be appended to a PQDN specified by the
\fB-h,--reqhost\fR option of \fBipadm\fR(1M), by the ncu \fBip-reqhost\fR
property of \fBnwamcfg\fR(1M), or by \fBnodename\fR(4) to construct an FQDN
for \fBREQUEST_FQDN\fR processing.
If the \fIhostname\fR value is already an FQDN, then the value of this
option is not used.
.RE

.sp
.ne 2
.na
\fB\fBADOPT_DOMAINNAME\fR\fR
.ad
.sp .6
.RS 4n
Indicates that a domain name returned by the DHCP server or the \fBdomain\fR
from \fBresolv.conf\fR(4) should be adopted if needed to construct an FQDN
from a PQDN specified by the \fB-h,--reqhost\fR option of \fBipadm\fR(1M),
by the ncu \fBip-reqhost\fR property of \fBnwamcfg\fR(1M), or by
\fBnodename\fR(4).
If the \fIhostname\fR value is already an FQDN, then the value of this
option is not applicable.
The eligible DHCP option for domain name is DHCPv4 \fBDNSdmain\fR.
.sp
Default value of this option is \fIno\fR.
.RE

.sp
.ne 2
.na
@@ -664,7 +791,8 @@ default router.
.RS 4n
Indicates the client requests the DHCP server to map the client's leased IPv4
address to the host name associated with the network interface that performs
DHCP on the client. The host name must be specified in the
DHCP on the client. The host name must be specified as documented for a
PQDN in \fBREQUEST_FQDN\fR above or specified in the
\fB/etc/hostname.\fIinterface\fR\fR file for the relevant interface on a line
of the form
.sp
@@ -678,6 +806,8 @@ inet \fIhostname\fR
where \fIhostname\fR is the host name requested.
.sp
This option works with DHCPv4 only.
.sp
Default value of this option is \fIyes\fR.
.RE

.RE
@@ -710,7 +840,8 @@ Interface Stability Committed
.SH SEE ALSO
.LP
\fBdhcpinfo\fR(1), \fBifconfig\fR(1M), \fBinit\fR(1M), \fBin.mpathd\fR(1M),
\fBin.ndpd\fR(1M), \fBsyslog\fR(3C), \fBattributes\fR(5), \fBdhcp\fR(5)
\fBin.ndpd\fR(1M), \fBipadm\fR(1M), \fBnwamcfg\fR(1M), \fBsyslog\fR(3C),
\fBnodename\fR(4), \fBresolv.conf\fR(4), \fBattributes\fR(5), \fBdhcp\fR(5)
.sp
.LP
\fI\fR
@@ -12,8 +12,9 @@
.\" Copyright (c) 2012, Joyent, Inc. All Rights Reserved
.\" Copyright (c) 2013 by Delphix. All rights reserved.
.\" Copyright 2014 Nexenta Systems, Inc. All rights reserved.
.\" Copyright (c) 2016-2017, Chris Fraire <cfraire@me.com>.
.\"
.Dd April 9, 2016
.Dd June 16, 2017
.Dt IPADM 1M
.Os
.Sh NAME
@@ -70,6 +71,8 @@
.Op Fl t
.Fl T Cm dhcp
.Op Fl w Ar seconds Ns | Ns Cm forever
.Op Fl 1
.Op Fl h Ar hostname
.Ar addrobj
.Nm
.Ic create-addr
@@ -418,6 +421,8 @@ subcommand for the list of property names.
.Op Fl t
.Fl T Cm dhcp
.Op Fl w Ar seconds Ns | Ns Cm forever
.Op Fl 1
.Op Fl h Ar hostname
.Ar addrobj
.br
.Nm
@@ -466,7 +471,32 @@ Obtain the address via DHCP.
This takes the following options:
.Bl -tag -width ""
.It Fl w Ns \&, Ns Fl -wait
Specify the time, in seconds, that the command should wait to obtain an address.
Specify the time, in seconds, that the command should wait to obtain an
address; or specify
.Cm forever
to wait without interruption.
The default value is 120.
.It Fl 1 Ns , Ns Fl -primary
Specify that the interface is primary.
One effect will be that
.Xr nodename 4
will serve as
.Fl h Ns , Ns Fl -reqhost
if that switch is not otherwise specified.
.It Fl h Ns , Ns Fl -reqhost
Specify the host name to send to the DHCP server in order to request an
association of a Fully Qualified Domain Name to the interface.
An FQDN is determined from
.Ar hostname
if it is "rooted" (ending in a '.'), or if it consists of at least three
DNS labels, or by appending to
.Ar hostname
the DNS domain name value configured in
.Pa /etc/default/dhcpagent
for
.Xr dhcpagent 1m .
N.b. that the DHCP server implementation ultimately determines whether and
how the client-sent FQDN is used.
.El
.It Fl T Cm addrconf
Create an auto-configured address.
@@ -647,14 +677,29 @@ The address should not be used to send packets but can still receive packets
.Pq Cm on Ns / Ns Cm off .
.It Cm prefixlen
The number of bits in the IPv4 netmask or IPv6 prefix.
.It Cm primary
The DHCP primary interface flag (read-only).
.It Cm private
The address is not advertised to routing
.Pq Cm on Ns / Ns Cm off .
.It Cm reqhost
The host name to send to the DHCP server in order to request an association
of an FQDN to the interface.
For a primary DHCP interface,
.Xr nodename 4
is sent if this property is not defined.
See the
.Nm
.Ic create-addr
.Fl T Cm dhcp
subcommand for an explanation of how an FQDN is determined.
.It Cm transmit
Packets can be transmitted
.Pq Cm on Ns / Ns Cm off .
.It Cm zone
The zone the addrobj is in.
The zone the addrobj is in (temporary-only--use
.Xr zonecfg 1M
to make persistent).
.El
.It Fl t Ns \&, Ns Fl -temporary
Temporary, not persistent across reboots.
@@ -847,10 +892,12 @@ subcommand for the list of property names.
.Sh SEE ALSO
.Xr arp 1M ,
.Xr cfgadm 1M ,
.Xr dhcpagent 1M ,
.Xr dladm 1M ,
.Xr if_mpadm 1M ,
.Xr ifconfig 1M ,
.Xr ndd 1M ,
.Xr zonecfg 1M ,
.Xr nodename 4 ,
.Xr nsswitch.conf 4 ,
.Xr dhcp 5
@@ -1,13 +1,13 @@
'\" te
.\" Copyright (C) 2009, Sun Microsystems, Inc. All Rights Reserved
.\" Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
.\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License"). You may not use this file except in compliance with the License. You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.
.\" See the License for the specific language governing permissions and limitations under the License. When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE. If applicable, add the following below this CDDL HEADER, with
.\" the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
.TH DHCP_INITTAB 4 "Aug 31, 2009"
.TH DHCP_INITTAB 4 "Oct 31, 2016"
.SH NAME
dhcp_inittab \- information repository for DHCP options
.SH DESCRIPTION
.sp
.LP
The \fB/etc/dhcp/inittab\fR and the \fB/etc/dhcp/inittab6\fR files contain
information about the Dynamic Host Configuration Protocol (\fBDHCP\fR) options,
@@ -123,7 +123,6 @@ be further defined.
.RE

.SS "DHCP \fBinittab\fR and \fBinittab6\fR Format"
.sp
.LP
Data entries are written one per line and have seven fields; each entry
provides information for one option. Each field is separated by a comma, except
@@ -361,7 +360,6 @@ this information, and should always be defined as \fBsdmi\fR for newly added
options.
.RE
.SS "Mnemonic Identifiers for IPv4 Options"
.sp
.LP
The following table maps the mnemonic identifiers used in Solaris DHCP to
\fIRFC 2132\fR options:
@@ -377,7 +375,7 @@ _
Subnet Mask, dotted Internet address (IP).
T}
\fBUTCoffst\fR \fB2\fR T{
Coordinated Universal time offset (seconds).
Coordinated Universal time offset (seconds) [deprecated].
T}
\fBRouter\fR \fB3\fR List of Routers, IP.
\fBTimeserv\fR \fB4\fR List of RFC-868 servers, IP.
@@ -475,6 +473,7 @@ T}
\fBUserClas\fR \fB77\fR User class information, ASCII.
\fBSLP_DA\fR \fB78\fR Directory agent, OCTET.
\fBSLP_SS\fR \fB79\fR Service scope, OCTET.
\fBClientFQDN\fR \fB81\fR Fully Qualified Domain Name, OCTET.
\fBAgentOpt\fR \fB82\fR Agent circuit ID, OCTET.
\fBFQDN\fR \fB89\fR Fully Qualified Domain Name, OCTET.
\fBPXEarch\fR \fB93\fR Client system architecture, NUMBER.
@@ -491,7 +490,6 @@ T}
.TE

.SS "Mnemonic Identifiers for IPv6 Options"
.sp
.LP
The following table maps the mnemonic identifiers used in Solaris DHCP to RFC
3315, 3319, 3646, 3898, 4075, and 4280 options:
@@ -559,7 +557,6 @@ is of type \fBIP\fR Address, consisting of a potentially infinite number of
pairs of \fBIP\fR addresses.

.SH FILES
.br
.in +2
\fB/etc/dhcp/inittab\fR
.in -2
@@ -568,7 +565,6 @@ pairs of \fBIP\fR addresses.
\fB/etc/dhcp/inittabv6\fR
.in -2
.SH ATTRIBUTES
.sp
.LP
See \fBattributes\fR(5) for descriptions of the following attributes:
.sp
@@ -584,7 +580,6 @@ Interface Stability Committed
.TE

.SH SEE ALSO
.sp
.LP
\fBdhcpinfo\fR(1), \fBdhcpagent\fR(1M), \fBisspace\fR(3C), \fBdhcptab\fR(4),
\fBattributes\fR(5), \fBdhcp\fR(5), \fBdhcp_modules\fR(5)
@@ -22,6 +22,7 @@
/*
* Copyright 1996-2003 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
* Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
*/

/*
@@ -31,8 +32,6 @@
#ifndef _DHCP_H
#define _DHCP_H

#pragma ident "%Z%%M% %I% %E% SMI"

#ifdef __cplusplus
extern "C" {
#endif
@@ -157,8 +156,15 @@ extern "C" {

#define CD_SLPDA 78
#define CD_SLPSS 79
#define CD_CLIENTFQDN 81
#define CD_AGENTOPT 82

/*
* Per RFC 3679, option 89 was "Never published as standard and [is] not in
* general use". See active CD_CLIENTFQDN and RFC 4702.
*/
#define CD_FQDN 89

#define CD_PXEARCHi 93
#define CD_PXENIIi 94
#define CD_PXECID 95