]> git.ipfire.org Git - thirdparty/dhcp.git/blobdiff - server/dhcpleasequery.c
Update RELNOTES
[thirdparty/dhcp.git] / server / dhcpleasequery.c
index dad45182d2f62fd1ef21cc23bc6f7f5dfbbd4476..0f1d4f77cd4b1510d5eb92de73d0857e59f4572e 100644 (file)
@@ -1,9 +1,9 @@
 /*
- * Copyright (C) 2006-2007 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2006-2017 by Internet Systems Consortium, Inc. ("ISC")
  *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  *
  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
@@ -17,9 +17,6 @@
 #include "dhcpd.h"
 
 /*
- * TODO: RFC4388 specifies that the server SHOULD store the
- *       vendor-class-id.
- *
  * TODO: RFC4388 specifies that the server SHOULD return the same
  *       options it would for a DHCREQUEST message, if no Parameter
  *       Request List option (option 55) is passed. We do not do that.
@@ -145,6 +142,7 @@ dhcpleasequery(struct packet *packet, int ms_nulltp) {
        unsigned char dhcpMsgType;
        const char *dhcp_msg_type_name;
        struct subnet *subnet;
+       struct group *relay_group;
        struct option_state *options;
        struct option_cache *oc;
        int allow_leasequery;
@@ -154,6 +152,9 @@ dhcpleasequery(struct packet *packet, int ms_nulltp) {
        u_int32_t time_rebinding;
        u_int32_t time_expiry;
        u_int32_t client_last_transaction_time;
+#if defined(RELAY_PORT)
+       u_int16_t relay_port = 0;
+#endif
        struct sockaddr_in to;
        struct in_addr siaddr;
        struct data_string prl;
@@ -173,6 +174,11 @@ dhcpleasequery(struct packet *packet, int ms_nulltp) {
        /* 
         * We can't reply if there is no giaddr field.
         */
+       /*
+        * Note: this makes DHCPv4-over-DHCPv6 always fail but it should not
+        * really be a problem because it is not a specified use case
+        * (or even one that makes sense).
+        */
        if (!packet->raw->giaddr.s_addr) {
                log_info("%s: missing giaddr, ciaddr is %s, no reply sent", 
                         msgbuf, inet_ntoa(packet->raw->ciaddr));
@@ -180,51 +186,43 @@ dhcpleasequery(struct packet *packet, int ms_nulltp) {
        }
 
        /* 
-        * Set up our options, scope, and, um... stuff.
-        * This is basically copied from dhcpinform() in dhcp.c.
+        * Initially we use the 'giaddr' subnet options scope to determine if
+        * the giaddr-identified relay agent is permitted to perform a
+        * leasequery.  The subnet is not required, and may be omitted, in
+        * which case we are essentially interrogating the root options class
+        * to find a globally permit.
         */
        gip.len = sizeof(packet->raw->giaddr);
        memcpy(gip.iabuf, &packet->raw->giaddr, sizeof(packet->raw->giaddr));
 
        subnet = NULL;
        find_subnet(&subnet, gip, MDL);
-       if (subnet == NULL) {
-               log_info("%s: unknown subnet for address %s", 
-                        msgbuf, piaddr(gip));
-               return;
-       }
+       if (subnet != NULL)
+               relay_group = subnet->group;
+       else
+               relay_group = root_group;
+
+       subnet_dereference(&subnet, MDL);
 
        options = NULL;
        if (!option_state_allocate(&options, MDL)) {
-               subnet_dereference(&subnet, MDL);
                log_error("No memory for option state.");
                log_info("%s: out of memory, no reply sent", msgbuf);
                return;
        }
 
-       execute_statements_in_scope(NULL,
-                                   packet,
-                                   NULL,
-                                   NULL,
-                                   packet->options,
-                                   options,
-                                   &global_scope,
-                                   subnet->group,
-                                   NULL);
+       execute_statements_in_scope(NULL, packet, NULL, NULL, packet->options,
+                                   options, &global_scope, relay_group,
+                                   NULL, NULL);
+
        for (i=packet->class_count-1; i>=0; i--) {
-               execute_statements_in_scope(NULL,
-                                           packet,
-                                           NULL,
-                                           NULL,
-                                           packet->options,
-                                           options,
+               execute_statements_in_scope(NULL, packet, NULL, NULL,
+                                           packet->options, options,
                                            &global_scope,
                                            packet->classes[i]->group,
-                                           subnet->group);
+                                           relay_group, NULL);
        }
 
-       subnet_dereference(&subnet, MDL);
-
        /* 
         * Because LEASEQUERY has some privacy concerns, default to deny.
         */
@@ -245,7 +243,7 @@ dhcpleasequery(struct packet *packet, int ms_nulltp) {
                option_state_dereference(&options, MDL);
                return;
        }
-           
+
 
        /* 
         * Copy out the client IP address.
@@ -385,6 +383,30 @@ dhcpleasequery(struct packet *packet, int ms_nulltp) {
 
        if (dhcpMsgType == DHCPLEASEACTIVE)
        {
+               /*
+                * RFC 4388 uses the PRL to request options for the agent to
+                * receive that are "about" the client.  It is confusing
+                * because in some cases it wants to know what was sent to
+                * the client (lease times, adjusted), and in others it wants
+                * to know information the client sent.  You're supposed to
+                * know this on a case-by-case basis.
+                *
+                * "Name servers", "domain name", and the like from the relay
+                * agent's scope seems less than useful.  Our options are to
+                * restart the option cache from the lease's best point of view
+                * (execute statements from the lease pool's group), or to
+                * simply restart the option cache from empty.
+                *
+                * I think restarting the option cache from empty best
+                * approaches RFC 4388's intent; specific options are included.
+                */
+               option_state_dereference(&options, MDL);
+
+               if (!option_state_allocate(&options, MDL)) {
+                       log_error("%s: out of memory, no reply sent", msgbuf);
+                       lease_dereference(&lease, MDL);
+                       return;
+               }
 
                /* 
                 * Set the hardware address fields.
@@ -431,6 +453,7 @@ dhcpleasequery(struct packet *packet, int ms_nulltp) {
 
                if (time_renewal > cur_time) {
                        time_renewal = htonl(time_renewal - cur_time);
+
                        if (!add_option(options, 
                                        DHO_DHCP_RENEWAL_TIME,
                                        &time_renewal, 
@@ -445,6 +468,7 @@ dhcpleasequery(struct packet *packet, int ms_nulltp) {
 
                if (time_rebinding > cur_time) {
                        time_rebinding = htonl(time_rebinding - cur_time);
+
                        if (!add_option(options, 
                                        DHO_DHCP_REBINDING_TIME,
                                        &time_rebinding, 
@@ -459,6 +483,7 @@ dhcpleasequery(struct packet *packet, int ms_nulltp) {
 
                if (lease->ends > cur_time) {
                        time_expiry = htonl(lease->ends - cur_time);
+
                        if (!add_option(options, 
                                        DHO_DHCP_LEASE_TIME,
                                        &time_expiry, 
@@ -471,9 +496,38 @@ dhcpleasequery(struct packet *packet, int ms_nulltp) {
                        }
                }
 
+               /* Supply the Vendor-Class-Identifier. */
+               if (lease->scope != NULL) {
+                       struct data_string vendor_class;
+
+                       memset(&vendor_class, 0, sizeof(vendor_class));
+
+                       if (find_bound_string(&vendor_class, lease->scope,
+                                             "vendor-class-identifier")) {
+                               if (!add_option(options,
+                                               DHO_VENDOR_CLASS_IDENTIFIER,
+                                               (void *)vendor_class.data,
+                                               vendor_class.len)) {
+                                       option_state_dereference(&options,
+                                                                MDL);
+                                       lease_dereference(&lease, MDL);
+                                       log_error("%s: error adding vendor "
+                                                 "class identifier, no reply "
+                                                 "sent", msgbuf);
+                                       data_string_forget(&vendor_class, MDL);
+                                       return;
+                               }
+                               data_string_forget(&vendor_class, MDL);
+                       }
+               }
 
                /*
                 * Set the relay agent info.
+                *
+                * Note that because agent info is appended without regard
+                * to the PRL in cons_options(), this will be sent as the
+                * last option in the packet whether it is listed on PRL or
+                * not.
                 */
 
                if (lease->agent_options != NULL) {
@@ -559,7 +613,7 @@ dhcpleasequery(struct packet *packet, int ms_nulltp) {
        /*
         * Figure out which address to use to send from.
         */
-       get_server_source_address(&siaddr, options, packet);
+       get_server_source_address(&siaddr, options, options, packet);
 
        /* 
         * Set up the option buffer.
@@ -609,12 +663,20 @@ dhcpleasequery(struct packet *packet, int ms_nulltp) {
 #endif
        memset(to.sin_zero, 0, sizeof(to.sin_zero));
 
+#if defined(RELAY_PORT)
+       relay_port = dhcp_check_relayport(packet);
+#endif
+
        /* 
         * Leasequery packets are be sent to the gateway address.
         */
        to.sin_addr = packet->raw->giaddr;
        if (packet->raw->giaddr.s_addr != htonl(INADDR_LOOPBACK)) {
+#if defined(RELAY_PORT)
+               to.sin_port = relay_port ? relay_port : local_port;
+#else
                to.sin_port = local_port;
+#endif
        } else {
                to.sin_port = remote_port; /* XXXSK: For debugging. */
        }
@@ -656,10 +718,6 @@ dhcpleasequery(struct packet *packet, int ms_nulltp) {
  *
  * TODO: RFC5007 ORO in query-options.
  *
- * TODO: RFC5007 not default preferred and valid time.
- *
- * TODO: RFC5007 not zero Client Last Transaction Time (clt-time).
- *
  * TODO: RFC5007 lq-relay-data.
  *
  * TODO: RFC5007 lq-client-link.
@@ -722,7 +780,7 @@ get_lq_query(struct lq6_state *lq)
         * Verify our lq_query structure is empty.
         */
        if ((lq_query->data != NULL) || (lq_query->len != 0)) {
-               return ISC_R_INVALIDARG;
+               return DHCP_R_INVALIDARG;
        }
 
        oc = lookup_option(&dhcpv6_universe, packet->options, D6O_LQ_QUERY);
@@ -815,15 +873,9 @@ valid_query_msg(struct lq6_state *lq) {
 
 exit:
        if (!ret_val) {
-               if (lq->client_id.len > 0) {
-                       data_string_forget(&lq->client_id, MDL);
-               }
-               if (lq->server_id.len > 0) {
-                       data_string_forget(&lq->server_id, MDL);
-               }
-               if (lq->lq_query.len > 0) {
-                       data_string_forget(&lq->lq_query, MDL);
-               }
+               data_string_forget(&lq->client_id, MDL);
+               data_string_forget(&lq->server_id, MDL);
+               data_string_forget(&lq->lq_query, MDL);
        }
        return ret_val;
 }
@@ -866,7 +918,7 @@ process_lq_by_address(struct lq6_state *lq) {
        struct ipv6_pool *pool = NULL;
        struct data_string data;
        struct in6_addr addr;
-       struct iaaddr *iaaddr = NULL;
+       struct iasubopt *iaaddr = NULL;
        struct option_state *opt_state = NULL;
        u_int32_t lifetime;
        unsigned opt_cursor;
@@ -903,7 +955,7 @@ process_lq_by_address(struct lq6_state *lq) {
         * or the ia-aadr when it is :: but in any case the ia-addr has
         * to be on the link, so we ignore the link-address here.
         */
-       if (find_ipv6_pool(&pool, &addr) != ISC_R_SUCCESS) {
+       if (find_ipv6_pool(&pool, D6O_IA_NA, &addr) != ISC_R_SUCCESS) {
                if (!set_error(lq, STATUS_NotConfigured,
                               "Address not in a pool.")) {
                        log_error("process_lq_by_address: unable "
@@ -913,14 +965,13 @@ process_lq_by_address(struct lq6_state *lq) {
                ret_val = 1;
                goto exit;
        }
-       if (iaaddr_hash_lookup(&iaaddr, pool->addrs, &addr,
-                              sizeof(addr), MDL) == 0) {
+       if (iasubopt_hash_lookup(&iaaddr, pool->leases, &addr,
+                                sizeof(addr), MDL) == 0) {
                ret_val = 1;
                goto exit;
        }
        if ((iaaddr == NULL) || (iaaddr->state != FTS_ACTIVE) ||
-           (iaaddr->ia_na == NULL) ||
-           (iaaddr->ia_na->iaid_duid.len <= 4)) {
+           (iaaddr->ia == NULL) || (iaaddr->ia->iaid_duid.len <= 4)) {
                ret_val = 1;
                goto exit;
        }
@@ -934,7 +985,7 @@ process_lq_by_address(struct lq6_state *lq) {
                goto exit;
        }
 
-       data_string_copy(&data, &iaaddr->ia_na->iaid_duid, MDL);
+       data_string_copy(&data, &iaaddr->ia->iaid_duid, MDL);
        data.data += 4;
        data.len -= 4;
        if (!save_option_buffer(&dhcpv6_universe, opt_state,
@@ -952,10 +1003,9 @@ process_lq_by_address(struct lq6_state *lq) {
        }
        data.data = data.buffer->data;
        memcpy(data.buffer->data, &iaaddr->addr, 16);
-       lifetime = DEFAULT_DEFAULT_LEASE_TIME;
-       lifetime = (lifetime / 2) + (lifetime / 8);
+       lifetime = iaaddr->prefer;
        putULong(data.buffer->data + 16, lifetime);
-       lifetime = DEFAULT_DEFAULT_LEASE_TIME;
+       lifetime = iaaddr->valid;
        putULong(data.buffer->data + 20, lifetime);
        if (!save_option_buffer(&dhcpv6_universe, opt_state,
                                NULL, (unsigned char *)data.data, data.len,
@@ -965,7 +1015,7 @@ process_lq_by_address(struct lq6_state *lq) {
        }
        data_string_forget(&data, MDL);
 
-       lifetime = 0;
+       lifetime = htonl(iaaddr->ia->cltt);
        if (!save_option_buffer(&dhcpv6_universe, opt_state,
                                NULL, (unsigned char *)&lifetime, 4,
                                D6O_CLT_TIME, 0)) {
@@ -999,7 +1049,7 @@ process_lq_by_address(struct lq6_state *lq) {
        if (pool != NULL)
                ipv6_pool_dereference(&pool, MDL);
        if (iaaddr != NULL)
-               iaaddr_dereference(&iaaddr, MDL);
+               iasubopt_dereference(&iaaddr, MDL);
        if (opt_state != NULL)
                option_state_dereference(&opt_state, MDL);
        return ret_val;
@@ -1012,7 +1062,6 @@ process_lq_by_address(struct lq6_state *lq) {
 void
 dhcpv6_leasequery(struct data_string *reply_ret, struct packet *packet) {
        static struct lq6_state lq;
-       struct data_string server_duid;
        struct option_cache *oc;
        int allow_lq;
 
@@ -1043,7 +1092,7 @@ dhcpv6_leasequery(struct data_string *reply_ret, struct packet *packet) {
        }
        execute_statements_in_scope(NULL, lq.packet, NULL, NULL,
                                    lq.packet->options, lq.reply_opts,
-                                   &global_scope, root_group, NULL);
+                                   &global_scope, root_group, NULL, NULL);
 
        lq.buf.reply.msg_type = DHCPV6_LEASEQUERY_REPLY;