]> git.ipfire.org Git - thirdparty/dhcp.git/commitdiff
[v4_1_esv] Add support for sending encapsulated v6 options from the server
authorShawn Routhier <sar@isc.org>
Fri, 3 Jun 2016 18:43:21 +0000 (11:43 -0700)
committerShawn Routhier <sar@isc.org>
Fri, 3 Jun 2016 18:43:21 +0000 (11:43 -0700)
Even if there was a definition for an ecncapsulated v6 option
and a set of values for it the v6 server wouldn't add it to
the outgoing packet.  This patch updates the server to do so.

RELNOTES
common/options.c
common/tables.c

index acc00abac082a2b8c4bf7463551394dbbf9be046..720994d931609767e1f712ca98fc04ea22cb994a 100644 (file)
--- a/RELNOTES
+++ b/RELNOTES
@@ -64,6 +64,11 @@ by Eric Young (eay@cryptsoft.com).
   of circuit id length.
   [ISC-Bugs #42556]
 
+- Add support for including an encapsulated option in a response
+  from the DHCPv6 server.  This allows the v6 FQDN option to be
+  returned in responses.
+  [ISC-Bugs #29246]
+
                        Changes since 4.1-ESV-R13b1
 
 - None
index 19ff43293c3bd8a3182cc4d5f849f1a74d277fd2..0f0edbda1d5a66ea05882064902c2fded9c3fe97 100644 (file)
@@ -327,7 +327,7 @@ int parse_encapsulated_suboptions (struct option_state *options,
           decoded the packet and executed the scopes that it matches). */
        if (!universe)
                return 0;
-               
+
        /* If we don't have a decoding function for it, we can't decode
           it. */
        if (!universe -> decode)
@@ -435,7 +435,7 @@ int fqdn_universe_decode (struct option_state *options,
                        if (len > 63) {
                                log_info ("fancy bits in fqdn option");
                                return 0;
-                       }       
+                       }
                        if (len == 0) {
                                terminated = 1;
                                break;
@@ -615,7 +615,7 @@ cons_options(struct packet *inpacket, struct dhcp_packet *outpacket,
 
        /*
         * Set offsets for buffer data to be copied into filename
-        * and servername fields 
+        * and servername fields
         */
        if (mb_size > agent_size)
                mb_max = mb_size - agent_size;
@@ -631,7 +631,7 @@ cons_options(struct packet *inpacket, struct dhcp_packet *outpacket,
                of2 = mb_max;
                mb_max += DHCP_SNAME_LEN;
        }
-               
+
        /*
         * Preload the option priority list with protocol-mandatory options.
         * This effectively gives these options the highest priority.
@@ -849,12 +849,12 @@ struct vsio_state {
 static void
 vsio_options(struct option_cache *oc,
             struct packet *packet,
-            struct lease *dummy_lease, 
+            struct lease *dummy_lease,
             struct client_state *dummy_client_state,
             struct option_state *dummy_opt_state,
             struct option_state *opt_state,
             struct binding_scope **dummy_binding_scope,
-            struct universe *universe, 
+            struct universe *universe,
             void *void_vsio_state) {
        struct vsio_state *vs = (struct vsio_state *)void_vsio_state;
        struct data_string ds;
@@ -862,7 +862,7 @@ vsio_options(struct option_cache *oc,
 
        memset(&ds, 0, sizeof(ds));
        if (evaluate_option_cache(&ds, packet, NULL,
-                                 NULL, opt_state, NULL, 
+                                 NULL, opt_state, NULL,
                                  &global_scope, oc, MDL)) {
                total_len = ds.len + universe->tag_size + universe->length_size;
                if (total_len <= (vs->buflen - vs->bufpos)) {
@@ -880,11 +880,11 @@ vsio_options(struct option_cache *oc,
                        if (universe->length_size == 1) {
                                vs->buf[vs->bufpos++] = ds.len;
                        } else if (universe->length_size == 2) {
-                               putUShort((unsigned char *)vs->buf+vs->bufpos, 
+                               putUShort((unsigned char *)vs->buf+vs->bufpos,
                                          ds.len);
                                vs->bufpos += 2;
                        } else if (universe->length_size == 4) {
-                               putULong((unsigned char *)vs->buf+vs->bufpos, 
+                               putULong((unsigned char *)vs->buf+vs->bufpos,
                                         ds.len);
                                vs->bufpos += 4;
                        }
@@ -901,6 +901,102 @@ vsio_options(struct option_cache *oc,
        }
 }
 
+/*!
+ *
+ * \brief Add a v6 option to the buffer
+ *
+ * Put the requested v6 option including tag, length and value
+ * into the specified buffer.  If there isn't enough space for
+ * the entire option it is skipped.
+ *
+ * \param buf    buffer to put the option
+ * \param buflen total length of buffer
+ * \param bufpos on input where to start putting the option
+ *               on output the starting point for the next option
+ * \param code   the option code number
+ * \param ds     the string to put into the option
+ *
+ * \return void
+ */
+static void
+add_option6_data(char *buf, int buflen, int* bufpos, uint16_t code,
+               struct data_string* ds) {
+       if ((ds->len + 4) > (buflen - *bufpos)) {
+               log_debug("No space for option %d", code);
+       } else {
+               unsigned char* tmp = (unsigned char *)buf + *bufpos;
+               /* option tag */
+               putUShort(tmp, code);
+               /* option length */
+               putUShort(tmp+2, ds->len);
+               /* option data */
+               memcpy(tmp+4, ds->data, ds->len);
+               /* update position */
+               *bufpos += 4 + ds->len;
+       }
+}
+
+/*!
+ *
+ * \brief Add a v6 encapsulated option to a buffer
+ *
+ * Find the universe for the requested option and if it exists
+ * call it's encapsualtion routine to produce a data string which
+ * can then be added to the current buffer.
+ *
+ * Note 1: currently we only do simple encapsulations, where the
+ * entire value of the option is in the option universe.  This is
+ * the 'E' format, we don't handle the 'e' format as we haven't
+ * defined any such universes yet.  This means that if there is
+ * a simple value for the option store_options6 should handle it
+ * directly and not call this routine.
+ *
+ * \param buf    buffer to put the option
+ * \param buflen total length of buffer
+ * \param bufpos on input where to start putting the option
+ *               on output the starting point for the next option
+ * \param opt_state information about option values to use
+ * \param packet    structure containing what we know about the packet
+ * \param encap_opt information about the structure of the option
+ * \param code      the option code number
+ *
+ * \return void
+ */
+static void
+store_encap6 (char *buf, int buflen, int* bufpos,
+             struct option_state *opt_state, struct packet *packet,
+             struct option* encap_opt, uint16_t code) {
+       /* We need to extract the name of the universe
+        * to use for this option.  We expect a format string
+        * of the form "Ename.".  If we don't find a name we bail. */
+       struct data_string ds;
+       struct data_string name;
+       char* s = (char*)encap_opt->format;
+       char* t;
+       if ((s == NULL) || (*s != 'E') || (strlen(s) <= 2)) {
+               return;
+       }
+
+       t = strchr(++s, '.');
+       if ((t == NULL) || (t == s)) {
+               return;
+       }
+
+       memset(&ds, 0, sizeof(ds));
+       memset(&name, 0, sizeof(name));
+       name.data = (unsigned char *)s;
+       name.len = t - s;
+
+       /* Now we call the routine to find and encapsulate the requested
+        * option/universe.  A return of 0 means no option information was
+        * available and nothing is added to the buffer */
+       if (option_space_encapsulate(&ds, packet, NULL, NULL, NULL, opt_state,
+                                    &global_scope, &name) != 0) {
+               add_option6_data(buf, buflen, bufpos, code, &ds);
+               data_string_forget(&ds, MDL);
+       }
+}
+
 /*
  * Stores the options from the DHCPv6 universe into the buffer given.
  *
@@ -909,8 +1005,8 @@ vsio_options(struct option_cache *oc,
  */
 
 int
-store_options6(char *buf, int buflen, 
-              struct option_state *opt_state, 
+store_options6(char *buf, int buflen,
+              struct option_state *opt_state,
               struct packet *packet,
               const int *required_opts,
               struct data_string *oro) {
@@ -935,11 +1031,11 @@ store_options6(char *buf, int buflen,
         */
        vsio_option_code = 0;
        o = vsio_universe.enc_opt;
-       while (o != NULL) { 
+       while (o != NULL) {
                if (o->universe == &dhcpv6_universe) {
                        vsio_option_code = o->code;
                        break;
-               } 
+               }
                o = o->universe->enc_opt;
        }
        if (vsio_option_code == 0) {
@@ -952,7 +1048,7 @@ store_options6(char *buf, int buflen,
                                vsio_wanted = 1;
                        }
 
-                       oc = lookup_option(&dhcpv6_universe, 
+                       oc = lookup_option(&dhcpv6_universe,
                                           opt_state, required_opts[i]);
                        if (oc == NULL) {
                                continue;
@@ -963,24 +1059,8 @@ store_options6(char *buf, int buflen,
                                                          NULL, opt_state,
                                                          NULL, &global_scope,
                                                          oc, MDL)) {
-                                       if ((ds.len + 4) <=
-                                           (buflen - bufpos)) {
-                                               tmp = (unsigned char *)buf;
-                                               tmp += bufpos;
-                                               /* option tag */
-                                               putUShort(tmp,
-                                                         required_opts[i]);
-                                               /* option length */
-                                               putUShort(tmp+2, ds.len);
-                                               /* option data */
-                                               memcpy(tmp+4, ds.data, ds.len);
-                                               /* update position */
-                                               bufpos += (4 + ds.len);
-                                       } else {
-                                               log_debug("No space for "
-                                                         "option %d",
-                                                         required_opts[i]);
-                                       }
+                                       add_option6_data(buf, buflen, &bufpos,
+                                                        (uint16_t)required_opts[i], &ds);
                                        data_string_forget(&ds, MDL);
                                } else {
                                        log_error("Error evaluating option %d",
@@ -999,7 +1079,7 @@ store_options6(char *buf, int buflen,
                memcpy(&code, oro->data+(i*2), 2);
                code = ntohs(code);
 
-               /* 
+               /*
                 * See if we've already included this option because
                 * it is required.
                 */
@@ -1021,34 +1101,44 @@ store_options6(char *buf, int buflen,
                 */
                if (code == vsio_option_code) {
                        vsio_wanted = 1;
+                       continue;
                }
 
-               /* 
+               /*
                 * Not already added, find this option.
                 */
                oc = lookup_option(&dhcpv6_universe, opt_state, code);
                memset(&ds, 0, sizeof(ds));
-               for (; oc != NULL ; oc = oc->next) {
-                       if (evaluate_option_cache(&ds, packet, NULL, NULL,
-                                                 opt_state, NULL,
-                                                 &global_scope, oc, MDL)) {
-                               if ((ds.len + 4) <= (buflen - bufpos)) {
-                                       tmp = (unsigned char *)buf + bufpos;
-                                       /* option tag */
-                                       putUShort(tmp, code);
-                                       /* option length */
-                                       putUShort(tmp+2, ds.len);
-                                       /* option data */
-                                       memcpy(tmp+4, ds.data, ds.len);
-                                       /* update position */
-                                       bufpos += (4 + ds.len);
+               if (oc != NULL) {
+                       /* We have a simple value for the option */
+                       for (; oc != NULL ; oc = oc->next) {
+                               if (evaluate_option_cache(&ds, packet, NULL,
+                                                         NULL, opt_state, NULL,
+                                                         &global_scope, oc,
+                                                         MDL)) {
+                                       add_option6_data(buf, buflen, &bufpos,
+                                                        code, &ds);
+                                       data_string_forget(&ds, MDL);
                                } else {
-                                       log_debug("No space for option %d",
+                                       log_error("Error evaluating option %d",
                                                  code);
                                }
-                               data_string_forget(&ds, MDL);
-                       } else {
-                               log_error("Error evaluating option %d", code);
+                       }
+               } else {
+                       /*
+                        * We don't have a simple value, check to see if we
+                        * have an universe to encapsulate into an option.
+                        */
+                       struct option *encap_opt = NULL;
+                       unsigned int code_int = code;
+
+                       option_code_hash_lookup(&encap_opt,
+                                               dhcpv6_universe.code_hash,
+                                               &code_int, 0, MDL);
+                       if (encap_opt != NULL) {
+                               store_encap6(buf, buflen, &bufpos, opt_state,
+                                            packet, encap_opt, code);
+                               option_dereference(&encap_opt, MDL);
                        }
                }
        }
@@ -1057,7 +1147,7 @@ store_options6(char *buf, int buflen,
                for (i=0; i < opt_state->universe_count; i++) {
                        if (opt_state->universes[i] != NULL) {
                                o = universes[i]->enc_opt;
-                               if ((o != NULL) && 
+                               if ((o != NULL) &&
                                    (o->universe == &vsio_universe)) {
                                        /*
                                         * Add the data from this VSIO option.
@@ -1066,14 +1156,14 @@ store_options6(char *buf, int buflen,
                                        vs.buflen = buflen;
                                        vs.bufpos = bufpos+8;
                                        option_space_foreach(packet, NULL,
-                                                            NULL, 
+                                                            NULL,
                                                             NULL, opt_state,
-                                                            NULL, 
-                                                            universes[i], 
+                                                            NULL,
+                                                            universes[i],
                                                             (void *)&vs,
                                                             vsio_options);
 
-                                       /* 
+                                       /*
                                         * If there was actually data here,
                                         * add the "header".
                                         */
@@ -1123,7 +1213,7 @@ store_options(int *ocount,
        unsigned code;
 
        /*
-        * These arguments are relative to the start of the buffer, so 
+        * These arguments are relative to the start of the buffer, so
         * reduce them by the current buffer index, and advance the
         * buffer pointer to where we're going to start writing.
         */
@@ -1214,7 +1304,7 @@ store_options(int *ocount,
 
            /* Code for next option to try to store. */
            code = priority_list [i];
-           
+
            /* Look up the option in the site option space if the code
               is above the cutoff, otherwise in the DHCP option space. */
            if (code >= cfg_options -> site_code_min)
@@ -1272,7 +1362,7 @@ store_options(int *ocount,
                        name.data = (unsigned char *)s;
                        name.len = t - s;
                    }
-                       
+
                    /* If we found a universe, and there are options configured
                       for that universe, try to encapsulate it. */
                    if (name.len) {
@@ -1297,7 +1387,7 @@ store_options(int *ocount,
            if (!oc && !have_encapsulation) {
                    continue;
            }
-           
+
            /* Find the value of the option... */
            od.len = 0;
            if (oc) {
@@ -1363,7 +1453,7 @@ store_options(int *ocount,
            }
 
            /* Try to store the option. */
-           
+
            /* If the option's length is more than 255, we must store it
               in multiple hunks.   Store 255-byte hunks first.  However,
               in any case, if the option data will cross a buffer
@@ -1811,7 +1901,7 @@ const char *pretty_print_option (option, data, len, emit_commas, emit_quotes)
                              option -> name,
                              &(option -> format [i]));
                        break;
-               } 
+               }
        }
 
        /* Check for too few bytes... */
@@ -1962,7 +2052,7 @@ const char *pretty_print_option (option, data, len, emit_commas, emit_quotes)
                                                 * count.
                                                 */
                                                count = MRns_name_ntop(
-                                                               nbuff, op, 
+                                                               nbuff, op,
                                                                (endbuf-op)-1);
 
                                                if (count <= 0) {
@@ -2449,12 +2539,12 @@ prepare_option_buffer(struct universe *universe, struct buffer *bp,
 static void
 count_options(struct option_cache *dummy_oc,
              struct packet *dummy_packet,
-             struct lease *dummy_lease, 
+             struct lease *dummy_lease,
              struct client_state *dummy_client_state,
              struct option_state *dummy_opt_state,
              struct option_state *opt_state,
              struct binding_scope **dummy_binding_scope,
-             struct universe *dummy_universe, 
+             struct universe *dummy_universe,
              void *void_accumulator) {
        int *accumulator = (int *)void_accumulator;
 
@@ -2464,12 +2554,12 @@ count_options(struct option_cache *dummy_oc,
 static void
 collect_oro(struct option_cache *oc,
            struct packet *dummy_packet,
-           struct lease *dummy_lease, 
+           struct lease *dummy_lease,
            struct client_state *dummy_client_state,
            struct option_state *dummy_opt_state,
            struct option_state *opt_state,
            struct binding_scope **dummy_binding_scope,
-           struct universe *dummy_universe, 
+           struct universe *dummy_universe,
            void *void_oro) {
        struct data_string *oro = (struct data_string *)void_oro;
 
@@ -2482,7 +2572,7 @@ collect_oro(struct option_cache *oc,
  * option value contents).
  */
 void
-build_server_oro(struct data_string *server_oro, 
+build_server_oro(struct data_string *server_oro,
                 struct option_state *options,
                 const char *file, int line) {
        int num_opts;
@@ -2904,7 +2994,7 @@ int option_space_encapsulate (result, packet, lease, client_state,
        struct universe *u = NULL;
        int status = 0;
 
-       universe_hash_lookup(&u, universe_hash, 
+       universe_hash_lookup(&u, universe_hash,
                             (const char *)name->data, name->len, MDL);
        if (u == NULL) {
                log_error("option_space_encapsulate: option space '%.*s' does "
@@ -3411,7 +3501,7 @@ fqdn6_option_space_encapsulate(struct data_string *result,
 
       exit:
        for (i = 1 ; i <= FQDN_SUBOPTION_COUNT ; i++) {
-               if (result[i].len)
+               if (results[i].len)
                        data_string_forget(&results[i], MDL);
        }
 
@@ -3824,7 +3914,7 @@ void do_packet (interface, packet, len, from_port, from, hfrom)
 
                if (decoded_packet->options_valid &&
                    (op = lookup_option(&dhcp_universe,
-                                       decoded_packet->options, 
+                                       decoded_packet->options,
                                        DHO_DHCP_MESSAGE_TYPE))) {
                        struct data_string dp;
                        memset(&dp, 0, sizeof dp);
@@ -3866,7 +3956,7 @@ packet6_len_okay(const char *packet, int len) {
        if (len < 1) {
                return 0;
        }
-       if ((packet[0] == DHCPV6_RELAY_FORW) || 
+       if ((packet[0] == DHCPV6_RELAY_FORW) ||
            (packet[0] == DHCPV6_RELAY_REPL)) {
                if (len >= offsetof(struct dhcpv6_relay_packet, options)) {
                        return 1;
@@ -3883,13 +3973,13 @@ packet6_len_okay(const char *packet, int len) {
 }
 
 #ifdef DHCPv6
-void 
-do_packet6(struct interface_info *interface, const char *packet, 
-          int len, int from_port, const struct iaddr *from, 
+void
+do_packet6(struct interface_info *interface, const char *packet,
+          int len, int from_port, const struct iaddr *from,
           isc_boolean_t was_unicast) {
        unsigned char msg_type;
        const struct dhcpv6_packet *msg;
-       const struct dhcpv6_relay_packet *relay; 
+       const struct dhcpv6_relay_packet *relay;
        struct packet *decoded_packet;
 #if defined (DEBUG_MEMORY_LEAKAGE)
        unsigned long previous_outstanding = dmalloc_outstanding;
@@ -3930,7 +4020,7 @@ do_packet6(struct interface_info *interface, const char *packet,
        decoded_packet->unicast = was_unicast;
 
        msg_type = packet[0];
-       if ((msg_type == DHCPV6_RELAY_FORW) || 
+       if ((msg_type == DHCPV6_RELAY_FORW) ||
            (msg_type == DHCPV6_RELAY_REPL)) {
                int relaylen = (int)(offsetof(struct dhcpv6_relay_packet, options));
                relay = (const struct dhcpv6_relay_packet *)packet;
@@ -3943,8 +4033,8 @@ do_packet6(struct interface_info *interface, const char *packet,
                memcpy(&decoded_packet->dhcpv6_peer_address,
                       relay->peer_address, sizeof(relay->peer_address));
 
-               if (!parse_option_buffer(decoded_packet->options, 
-                                        relay->options, len - relaylen, 
+               if (!parse_option_buffer(decoded_packet->options,
+                                        relay->options, len - relaylen,
                                         &dhcpv6_universe)) {
                        /* no logging here, as parse_option_buffer() logs all
                           cases where it fails */
@@ -3957,12 +4047,12 @@ do_packet6(struct interface_info *interface, const char *packet,
                decoded_packet->dhcpv6_msg_type = msg->msg_type;
 
                /* message-specific data */
-               memcpy(decoded_packet->dhcpv6_transaction_id, 
-                      msg->transaction_id, 
+               memcpy(decoded_packet->dhcpv6_transaction_id,
+                      msg->transaction_id,
                       sizeof(decoded_packet->dhcpv6_transaction_id));
 
-               if (!parse_option_buffer(decoded_packet->options, 
-                                        msg->options, len - msglen, 
+               if (!parse_option_buffer(decoded_packet->options,
+                                        msg->options, len - msglen,
                                         &dhcpv6_universe)) {
                        /* no logging here, as parse_option_buffer() logs all
                           cases where it fails */
@@ -4144,7 +4234,7 @@ add_option(struct option_state *options,
        /* INSIST(data != NULL); */
 
        option = NULL;
-       if (!option_code_hash_lookup(&option, dhcp_universe.code_hash, 
+       if (!option_code_hash_lookup(&option, dhcp_universe.code_hash,
                                     &option_num, 0, MDL)) {
                log_error("Attempting to add unknown option %d.", option_num);
                return 0;
@@ -4157,11 +4247,11 @@ add_option(struct option_state *options,
                return 0;
        }
 
-       if (!make_const_data(&oc->expression, 
-                            data, 
+       if (!make_const_data(&oc->expression,
+                            data,
                             data_len,
-                            0, 
-                            0, 
+                            0,
+                            0,
                             MDL)) {
                log_error("No memory for constant data adding %s (option %d).",
                          option->name, option_num);
@@ -4206,7 +4296,7 @@ int validate_packet(struct packet *packet)
                                "a future version of ISC DHCP will reject this");
                }
        } else {
-               /* 
+               /*
                 * If hlen is 0 we don't have any identifier, we warn the user
                 * but continue processing the packet as we can.
                 */
index d8b2a7dbc71afa1917289e3cfbe812dece4c6f6a..2849d57534f8e5a1076f654d7d124df1f8a4f364 100644 (file)
@@ -1398,8 +1398,10 @@ void initialize_common_option_spaces()
                          &vsio_universe, MDL);
        universe_hash_add(universe_hash, isc6_universe.name, 0,
                          &isc6_universe, MDL);
-/* This should not be necessary.  Listing here just for consistency.
- *     universe_hash_add(universe_hash, fqdn6_universe.name, 0,
- *                       &fqdn6_universe, MDL);
- */
+       /* previously this wasn't necessary, now that we can send
+        * v6 encapsulated options it is.
+        */
+       universe_hash_add(universe_hash, fqdn6_universe.name, 0,
+                         &fqdn6_universe, MDL);
+
 }