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

@@ -18,10 +18,11 @@
  *
  * 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,10 +35,12 @@
 #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,10 +71,14 @@
 
 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,10 +759,11 @@
                 (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,12 +799,12 @@
                                 break;
                         if (isv6) {
                                 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;
 
                 case DSYM_VENDOR:
                         if (isv6) {

@@ -874,15 +882,11 @@
                                 }
                         } else {
                                 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;
 
                                         v4opt->len  = optnum.size;
                                         v4opt->code = optnum.code;

@@ -903,22 +907,21 @@
                         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.
+                 * 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 + 2;
+                                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,10 +972,11 @@
         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,10 +1060,147 @@
                 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