Print this page
7388 Support DHCP Client FQDN. Allow IAID/DUID for all v4.
*** 18,27 ****
--- 18,28 ----
*
* CDDL HEADER END
*/
/*
* Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
*/
#include <sys/types.h>
#include <stdlib.h>
#include <assert.h>
*** 34,43 ****
--- 35,46 ----
#include <stdio_ext.h>
#include <dhcp_hostconf.h>
#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>
#include <sys/stat.h>
#include <stropts.h>
*** 68,77 ****
--- 71,84 ----
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
* which states; a non-zero value indicates the command is permitted.
*
*** 752,761 ****
--- 759,769 ----
(void) script_start(dsmp, isv6 ? EVENT_DROP6 : EVENT_DROP,
dhcp_drop, NULL, NULL);
break; /* not an immediate function */
case DHCP_EXTEND:
+ dhcp_smach_set_msg_reqhost(dsmp, iap);
(void) dhcp_extending(dsmp);
break;
case DHCP_GET_TAG: {
dhcp_optnum_t optnum;
*** 791,802 ****
break;
if (isv6) {
opt = dhcpv6_pkt_option(ack, NULL, optnum.code,
NULL);
} else {
! if (optnum.code <= DHCP_LAST_OPT)
! opt = ack->opts[optnum.code];
}
break;
case DSYM_VENDOR:
if (isv6) {
--- 799,810 ----
break;
if (isv6) {
opt = dhcpv6_pkt_option(ack, NULL, optnum.code,
NULL);
} else {
! opt = dhcp_get_ack_or_state(dsmp, ack, optnum.code,
! &did_alloc);
}
break;
case DSYM_VENDOR:
if (isv6) {
*** 874,888 ****
}
} else {
if (optnum.code + optnum.size > sizeof (PKT))
break;
! /*
! * + 2 to account for option code and length
! * byte
! */
! opt = malloc(optnum.size + 2);
if (opt != NULL) {
DHCP_OPT *v4opt = opt;
v4opt->len = optnum.size;
v4opt->code = optnum.code;
--- 882,892 ----
}
} else {
if (optnum.code + optnum.size > sizeof (PKT))
break;
! opt = malloc(optnum.size + DHCP_OPT_META_LEN);
if (opt != NULL) {
DHCP_OPT *v4opt = opt;
v4opt->len = optnum.size;
v4opt->code = optnum.code;
*** 903,924 ****
send_error_reply(iap, DHCP_IPC_E_PROTO);
return;
}
/*
! * return the option payload, if there was one. the "+ 2"
! * accounts for the option code number and length byte.
*/
if (opt != NULL) {
if (isv6) {
dhcpv6_option_t d6ov;
(void) memcpy(&d6ov, opt, sizeof (d6ov));
optlen = ntohs(d6ov.d6o_len) + sizeof (d6ov);
} else {
! optlen = ((DHCP_OPT *)opt)->len + 2;
}
send_data_reply(iap, 0, DHCP_TYPE_OPTION, opt, optlen);
if (did_alloc)
free(opt);
--- 907,927 ----
send_error_reply(iap, DHCP_IPC_E_PROTO);
return;
}
/*
! * return the option payload, if there was one.
*/
if (opt != NULL) {
if (isv6) {
dhcpv6_option_t d6ov;
(void) memcpy(&d6ov, opt, sizeof (d6ov));
optlen = ntohs(d6ov.d6o_len) + sizeof (d6ov);
} else {
! optlen = ((DHCP_OPT *)opt)->len + DHCP_OPT_META_LEN;
}
send_data_reply(iap, 0, DHCP_TYPE_OPTION, opt, optlen);
if (did_alloc)
free(opt);
*** 969,978 ****
--- 972,982 ----
case DHCP_START: {
PKT_LIST *ack, *oack;
PKT_LIST *plp[2];
deprecate_leases(dsmp);
+ dhcp_smach_set_msg_reqhost(dsmp, iap);
/*
* if we have a valid hostconf lying around, then jump
* into INIT_REBOOT. if it fails, we'll end up going
* through the whole selecting() procedure again.
*** 1056,1065 ****
--- 1060,1206 ----
break;
}
}
}
+ /*
+ * dhcp_smach_set_msg_reqhost(): set dsm_msg_reqhost based on the message
+ * content of a DHCP IPC message
+ *
+ * input: dhcp_smach_t *: the state machine instance;
+ * ipc_action_t *: the decoded DHCP IPC message;
+ * output: void
+ */
+
+ static void
+ dhcp_smach_set_msg_reqhost(dhcp_smach_t *dsmp, ipc_action_t *iap)
+ {
+ if (dsmp->dsm_msg_reqhost != NULL) {
+ dhcpmsg(MSG_DEBUG,
+ "dhcp_smach_set_msg_reqhost: nullify former value, %s",
+ dsmp->dsm_msg_reqhost);
+ free(dsmp->dsm_msg_reqhost);
+ dsmp->dsm_msg_reqhost = NULL;
+ }
+ free(dsmp->dsm_reqfqdn);
+ dsmp->dsm_reqfqdn = NULL;
+
+ /*
+ * if a STANDARD/HOSTNAME was sent in the IPC request, then copy that
+ * value into the state machine data if decoding succeeds. Otherwise,
+ * log to indicate at what step the decoding stopped.
+ */
+
+ if (dsmp->dsm_isv6) {
+ dhcpmsg(MSG_DEBUG, "dhcp_smach_set_msg_reqhost: ipv6 is not"
+ " handled");
+ } else if (iap->ia_request->data_type != DHCP_TYPE_OPTION) {
+ dhcpmsg(MSG_DEBUG, "dhcp_smach_set_msg_reqhost: request type %d is"
+ " not DHCP_TYPE_OPTION", iap->ia_request->data_type);
+ } else {
+ if (iap->ia_request->buffer == NULL
+ || iap->ia_request->data_length <= DHCP_OPT_META_LEN) {
+ dhcpmsg(MSG_WARNING, "dhcp_smach_set_msg_reqhost:"
+ " DHCP_TYPE_OPTION ia_request buffer is NULL (0) or"
+ " short (1): %d",
+ iap->ia_request->buffer == NULL ? 0 : 1);
+ } else {
+ DHCP_OPT *d4o = (DHCP_OPT *)iap->ia_request->buffer;
+
+ if (d4o->code != CD_HOSTNAME)
+ {
+ dhcpmsg(MSG_DEBUG,
+ "dhcp_smach_set_msg_reqhost: ignoring DHCPv4"
+ " option %u", d4o->code);
+ } else if (iap->ia_request->data_length - DHCP_OPT_META_LEN
+ != d4o->len) {
+ dhcpmsg(MSG_WARNING, "dhcp_smach_set_msg_reqhost:"
+ " unexpected DHCP_OPT buffer length %u for CD_HOSTNAME"
+ " option length %u", iap->ia_request->data_length,
+ d4o->len);
+ } else {
+
+ dhcp_symbol_t *entry = inittab_getbycode(ITAB_CAT_STANDARD,
+ ITAB_CONS_INFO, CD_HOSTNAME);
+ if (entry == NULL) {
+ dhcpmsg(MSG_WARNING,
+ "dhcp_smach_set_msg_reqhost: error getting"
+ " ITAB_CAT_STANDARD ITAB_CONS_INFO"
+ " CD_HOSTNAME entry");
+ } else {
+ char *value = inittab_decode(entry, d4o->value,
+ d4o->len, /* just_payload */ B_TRUE);
+ if (value == NULL) {
+ dhcpmsg(MSG_WARNING,
+ "dhcp_smach_set_msg_reqhost: error decoding"
+ " CD_HOSTNAME value from DHCP_OPT");
+ } else {
+ dhcpmsg(MSG_DEBUG,
+ "dhcp_smach_set_msg_reqhost: host %s", value);
+ free(dsmp->dsm_msg_reqhost);
+ dsmp->dsm_msg_reqhost = value;
+ }
+ free(entry);
+ entry = NULL;
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ * dhcp_get_ack_or_state(): get a v4 option from the ACK or from the state
+ * machine state for certain codes that are not ACKed (e.g., CD_CLIENT_ID)
+ *
+ * input: dhcp_smach_t *: the state machine instance;
+ * PKT_LIST *: the decoded DHCP IPC message;
+ * uint_t: the DHCP client option code;
+ * boolean_t *: a pointer to a value that will be set to B_TRUE if
+ * the return value must be freed (or else set to B_FALSE);
+ * output: the option if found or else NULL.
+ */
+
+ /* ARGSUSED */
+ static DHCP_OPT *
+ dhcp_get_ack_or_state(const dhcp_smach_t *dsmp, const PKT_LIST *plp,
+ uint_t codenum, boolean_t *did_alloc)
+ {
+ DHCP_OPT *opt;
+
+ *did_alloc = B_FALSE;
+
+ if (codenum > DHCP_LAST_OPT)
+ return NULL;
+
+ /* check the ACK first for all codes */
+ opt = plp->opts[codenum];
+ if (opt != NULL)
+ return opt;
+
+ /* check the machine state also for certain codes */
+ switch (codenum) {
+ case CD_CLIENT_ID:
+ /*
+ * CD_CLIENT_ID is not sent in an ACK, but it's possibly available
+ * from the state machine data
+ */
+
+ if (dsmp->dsm_cidlen > 0) {
+ if ((opt = malloc(dsmp->dsm_cidlen + DHCP_OPT_META_LEN))
+ != NULL) {
+ *did_alloc = B_TRUE;
+ (void) encode_dhcp_opt(opt, B_FALSE /* is IPv6 */,
+ CD_CLIENT_ID, dsmp->dsm_cid, dsmp->dsm_cidlen);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ return (opt);
+ }
+
/*
* check_rtm_addr(): determine if routing socket message matches interface
* address
*
* input: const struct if_msghdr *: pointer to routing socket message