]> git.ipfire.org Git - thirdparty/dhcp.git/commitdiff
[master] Added echo-client-id server parameter
authorThomas Markwalder <tmark@isc.org>
Fri, 17 Oct 2014 11:56:01 +0000 (07:56 -0400)
committerThomas Markwalder <tmark@isc.org>
Fri, 17 Oct 2014 11:56:01 +0000 (07:56 -0400)
    Merges in rt35958 which includes fix for rt32545
    Also updated dhcp/.gitignore

.gitignore
RELNOTES
common/options.c
includes/dhcpd.h
server/dhcp.c
server/dhcpd.conf.5
server/stables.c

index a238dcd5e8752fe33eab7ef2ff261d49dc3ffcd7..a68a802e4462e260f0b6d95c6b55855bb23c8b03 100644 (file)
@@ -4,3 +4,26 @@ doc/html
 .deps
 Makefile
 autom4te.cache
+client/dhclient
+client/tests/duid_unittests
+client/tests/test-suite.log
+common/tests/alloc_unittest
+common/tests/dns_unittest
+common/tests/misc_unittest
+config.log
+config.report
+config.status
+dhcpctl/cltest
+dhcpctl/omshell
+doc/devel/doxyfile
+dst/libdst.a
+includes/stamp-h1
+omapip/svtest
+relay/dhcrelay
+server/dhcpd
+server/tests/dhcpd_unittests
+server/tests/hash_unittests
+server/tests/legacy_unittests
+server/tests/load_bal_unittests
+server/tests/test-suite.log
+tests/libt_api.a
index a6ca36e05613c8fbd1a0493e551ccbd3a9d24377..b86316ab4308cb7338b17771b600ae8ec1219144 100644 (file)
--- a/RELNOTES
+++ b/RELNOTES
@@ -112,8 +112,18 @@ by Eric Young (eay@cryptsoft.com).
   [ISC-Bugs #29769]
   Inbound packets with UPD checksums of 0xffff now validate correctly rather
   than being dropped.
-  [ISC-Bus #24216]
-  [ISC-Bus #25587]
+  [ISC-Bugs #24216]
+  [ISC-Bugs #25587]
+
+- Added the echo-client-id configuration parameter to the server configuration.
+  The server now supports RFC 6842 compliant behavior by setting a new
+  configuration parameter, echo-client-id.  When enabled, the server will
+  include the client identifier option (Option code 61) if received, in its
+  responses.  The server identifier returned in NAKs (if enabled) will now
+  be the globally defined value (if one) if the server cannot attribute the
+  inbound request to a known subnet.
+  [ISC-Bugs 35958]
+  [ISC-Bugs 32545]
 
                        Changes since 4.3.1b1
 
index dc96d8275d97321418c312e57530cc4659e7facd..58610d7efd942ffab3d9236b31f654ee97c1fc75 100644 (file)
@@ -662,6 +662,15 @@ cons_options(struct packet *inpacket, struct dhcp_packet *outpacket,
                                        DHO_SUBNET_SELECTION;
                }
 
+               /* If echo-client-id is on, then we add client identifier to
+                * the priority_list. This way we'll send it whether or not it
+                * is in the PRL. */
+               if ((inpacket != NULL) && (priority_len < PRIORITY_COUNT) &&
+                   (inpacket->sv_echo_client_id == ISC_TRUE)) {
+                       priority_list[priority_len++] =
+                               DHO_DHCP_CLIENT_IDENTIFIER;
+               }
+
                data_string_truncate(prl, (PRIORITY_COUNT - priority_len));
 
                /*
index faec6fdce92e2f56ea40692a42fcb65fa5847201..86773ecb83049b59216c5f9473ee3619a75bfbd6 100644 (file)
@@ -426,6 +426,10 @@ struct packet {
         * Only used in DHCPv6.
         */
        isc_boolean_t unicast;
+
+       /* Propogates server value SV_ECHO_CLIENT_ID so it is available
+         * in cons_options() */
+       int sv_echo_client_id;
 };
 
 /*
@@ -732,6 +736,7 @@ struct lease_state {
 #define SV_IGNORE_CLIENT_UIDS          82
 #define SV_LOG_THRESHOLD_LOW           83
 #define SV_LOG_THRESHOLD_HIGH          84
+#define SV_ECHO_CLIENT_ID              85
 
 #if !defined (DEFAULT_PING_TIMEOUT)
 # define DEFAULT_PING_TIMEOUT 1
@@ -2223,9 +2228,11 @@ void dhcprequest (struct packet *, int, struct lease *);
 void dhcprelease (struct packet *, int);
 void dhcpdecline (struct packet *, int);
 void dhcpinform (struct packet *, int);
-void nak_lease (struct packet *, struct iaddr *cip);
+void nak_lease (struct packet *, struct iaddr *cip, struct group*);
 void ack_lease (struct packet *, struct lease *,
                unsigned int, TIME, char *, int, struct host_decl *);
+void echo_client_id(struct packet*, struct lease*, struct option_state*,
+                   struct option_state*);
 void delayed_ack_enqueue(struct lease *);
 void commit_leases_readerdry(void *);
 void flush_ackqueue(void *);
@@ -2250,9 +2257,10 @@ void get_server_source_address(struct in_addr *from,
                               struct option_state *options,
                               struct option_state *out_options,
                               struct packet *packet);
-void setup_server_source_address(struct in_addr *from,
-                                struct option_state *options,
-                                struct packet *packet);
+
+void eval_network_statements(struct option_state **options,
+                           struct packet *packet,
+                           struct group *network_group);
 
 /* dhcpleasequery.c */
 void dhcpleasequery (struct packet *, int);
index a3d7255819d95532f0e138f14420f30ae8eb9b56..492487a15de52a0db4d5d23303482ae85f13ad5b 100644 (file)
@@ -546,7 +546,7 @@ void dhcprequest (packet, ms_nulltp, ip_lease)
                if (lease -> binding_state == FTS_RESET &&
                    !lease_mine_to_reallocate (lease)) {
                        log_debug ("%s: lease reset by administrator", msgbuf);
-                       nak_lease (packet, &cip);
+                       nak_lease (packet, &cip, lease->subnet->group);
                        goto out;
                }
 
@@ -563,7 +563,12 @@ void dhcprequest (packet, ms_nulltp, ip_lease)
                if ((sip.len == 4) &&
                    (memcmp(sip.iabuf, "\0\0\0\0", sip.len) != 0)) {
                        struct in_addr from;
-                       setup_server_source_address(&from, NULL, packet);
+                       struct option_state *eval_options = NULL;
+
+                       eval_network_statements(&eval_options, packet, NULL);
+                       get_server_source_address(&from, eval_options, NULL,
+                                                 packet);
+                       option_state_dereference (&eval_options, MDL);
                        if (memcmp(sip.iabuf, &from, sip.len) != 0) {
                                log_debug("%s: not our server id", msgbuf);
                                goto out;
@@ -651,7 +656,7 @@ void dhcprequest (packet, ms_nulltp, ip_lease)
                if (!packet -> shared_network) {
                        if (subnet && subnet -> group -> authoritative) {
                                log_info ("%s: wrong network.", msgbuf);
-                               nak_lease (packet, &cip);
+                               nak_lease (packet, &cip, NULL);
                                goto out;
                        }
                        /* Otherwise, ignore it. */
@@ -670,7 +675,7 @@ void dhcprequest (packet, ms_nulltp, ip_lease)
                        if (packet -> shared_network -> group -> authoritative)
                        {
                                log_info ("%s: wrong network.", msgbuf);
-                               nak_lease (packet, &cip);
+                               nak_lease (packet, &cip, NULL);
                                goto out;
                        }
                        log_info ("%s: ignored (not authoritative).", msgbuf);
@@ -682,7 +687,7 @@ void dhcprequest (packet, ms_nulltp, ip_lease)
           available for the client, NAK it. */
        if (!lease && ours) {
                log_info ("%s: lease %s unavailable.", msgbuf, piaddr (cip));
-               nak_lease (packet, &cip);
+               nak_lease (packet, &cip, (subnet ? subnet->group : NULL));
                goto out;
        }
 
@@ -1562,9 +1567,22 @@ void dhcpinform (packet, ms_nulltp)
                subnet_dereference (&subnet, MDL);
 }
 
-void nak_lease (packet, cip)
+/*!
+ * \brief Constructs and sends a DHCP Nak
+ *
+ * In order to populate options such as dhcp-server-id and
+ * dhcp-client-identifier, the function creates a temporary option cache
+ * and evaluates options based on the packet's shared-network or the
+ * network_group in its absence, as well as the packet->clasess (if any).
+ *
+ * \param packet inbound packet received from the client
+ * \param cip address requested by the client
+ * \param network_group optional scope for use in setting up options
+ */
+void nak_lease (packet, cip, network_group)
        struct packet *packet;
        struct iaddr *cip;
+       struct group *network_group; /* scope to use for options */
 {
        struct sockaddr_in to;
        struct in_addr from;
@@ -1575,6 +1593,7 @@ void nak_lease (packet, cip)
        unsigned i;
        struct option_state *options = (struct option_state *)0;
        struct option_cache *oc = (struct option_cache *)0;
+       struct option_state *eval_options = NULL;
 
        option_state_allocate (&options, MDL);
        memset (&outgoing, 0, sizeof outgoing);
@@ -1620,23 +1639,21 @@ void nak_lease (packet, cip)
        save_option (&dhcp_universe, options, oc);
        option_cache_dereference (&oc, MDL);
 
-       /*
-        * If we are configured to do so we try to find a server id
-        * option even for NAKS by calling setup_server_source_address().
-        * This function will set up an options list from the global
-        * and subnet scopes before trying to get the source address.
-        * 
-        * Otherwise we simply call get_server_source_address()
-        * directly, without a server options list, this means
-        * we'll get the source address from the interface address.
-        */
+       /* Setup the options at the global and subnet scopes.  These
+        * may be used to locate sever id option if enabled as well
+        * for echo-client-id further on. (This allocates eval_options). */
+       eval_network_statements(&eval_options, packet, network_group);
+
 #if defined(SERVER_ID_FOR_NAK)
-       setup_server_source_address(&from, options, packet);
+       /* Pass in the evaluated options so they can be searched for
+         * server-id, otherwise source address comes from the interface
+        * address. */
+       get_server_source_address(&from, eval_options, options, packet);
 #else
+       /* Get server source address from the interface address */
        get_server_source_address(&from, NULL, options, packet);
 #endif /* if defined(SERVER_ID_FOR_NAK) */
 
-
        /* If there were agent options in the incoming packet, return
         * them.  We do not check giaddr to detect the presence of a
         * relay, as this excludes "l2" relay agents which have no
@@ -1652,6 +1669,20 @@ void nak_lease (packet, cip)
                     MDL);
        }
 
+        /* echo-client-id can specified at the class level so add class-scoped
+        * options into eval_options. */
+        for (i = packet->class_count; i > 0; i--) {
+                execute_statements_in_scope(NULL, packet, NULL, NULL,
+                                           packet->options, eval_options,
+                                           &global_scope,
+                                           packet->classes[i - 1]->group,
+                                           NULL, NULL);
+        }
+
+       /* Echo client id if we received and it's enabled */
+       echo_client_id(packet, NULL, eval_options, options);
+       option_state_dereference (&eval_options, MDL);
+
        /* Do not use the client's requested parameter list. */
        delete_option (&dhcp_universe, packet -> options,
                       DHO_DHCP_PARAMETER_REQUEST_LIST);
@@ -1744,6 +1775,75 @@ void nak_lease (packet, cip)
 
 }
 
+/*!
+ * \brief Adds a dhcp-client-id option to a set of options
+ * Given a set of input options, it searches for echo-client-id.  If it is
+ * defined and enabled, the given packet is searched for dhcp-client-id.  If
+ * the option is found it is replicated into the given set of output options.
+ * This allows us to provide compliance with RFC 6842. It is called when we ack
+ * or nak a lease.  In the latter case we may or may not have created the
+ * requisite scope to lookup echo-client-id.
+ *
+ * Note the flag packet.sv_echo_client_id is set to reflect the configuration
+ * option.  This bypases inaccessiblity of server_universe in cons_options()
+ * which must amend the PRL (when not empty) if echoing is enabled.
+ *
+ * \param packet inbound packet received from the client
+ * \param lease lease associated with this client (if one)
+ * \param in_options options in which to search for echo-client-id
+ * \param out_options options to which to save the client-id
+ */
+void echo_client_id(packet, lease, in_options, out_options)
+       struct packet *packet;
+       struct lease *lease;
+       struct option_state *in_options;
+       struct option_state *out_options;
+{
+       struct option_cache *oc;
+       int ignorep;
+
+       /* Check if echo-client-id is enabled */
+       oc = lookup_option(&server_universe, in_options, SV_ECHO_CLIENT_ID);
+       if (oc && evaluate_boolean_option_cache(&ignorep, packet, lease,
+                                                NULL, packet->options,
+                                               in_options,
+                                                (lease ? &lease->scope : NULL),
+                                               oc, MDL)) {
+               struct data_string client_id;
+               unsigned int opcode = DHO_DHCP_CLIENT_IDENTIFIER;
+
+               /* Save knowledge that echo is enabled to the packet */
+               packet->sv_echo_client_id = ISC_TRUE;
+
+               /* Now see if inbound packet contains client-id */
+               oc = lookup_option(&dhcp_universe, packet->options, opcode);
+               memset(&client_id, 0, sizeof client_id);
+               if (oc && evaluate_option_cache(&client_id,
+                                               packet, NULL, NULL,
+                                               packet->options, NULL,
+                                               (lease ? &lease->scope : NULL),
+                                               oc, MDL)) {
+                       /* Packet contained client-id, add it to out_options. */
+                       oc = NULL;
+                       if (option_cache_allocate(&oc, MDL)) {
+                               if (make_const_data(&oc->expression,
+                                                   client_id.data,
+                                                   client_id.len,
+                                                    1, 0, MDL)) {
+                                       option_code_hash_lookup(&oc->option,
+                                                               dhcp_universe.
+                                                                code_hash,
+                                                               &opcode,
+                                                                0, MDL);
+                                       save_option(&dhcp_universe,
+                                                   out_options, oc);
+                               }
+                               option_cache_dereference(&oc, MDL);
+                       }
+               }
+       }
+}
+
 void check_pool_threshold (packet, lease, state)
      struct packet *packet;
      struct lease *lease;
@@ -2495,7 +2595,7 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp)
                        /* NAK leases before pool activation date */
                        cip.len = 4;
                        memcpy (cip.iabuf, &lt->ip_addr.iabuf, 4);
-                       nak_lease(packet, &cip);
+                       nak_lease(packet, &cip, lease->subnet->group);
                        free_lease_state (state, MDL);
                        lease_dereference (&lt, MDL);
                        if (host)
@@ -2525,7 +2625,7 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp)
                                /* NAK leases after pool expiration date */
                                cip.len = 4;
                                memcpy (cip.iabuf, &lt->ip_addr.iabuf, 4);
-                               nak_lease(packet, &cip);
+                               nak_lease(packet, &cip, lease->subnet->group);
                                free_lease_state (state, MDL);
                                lease_dereference (&lt, MDL);
                                if (host)
@@ -3144,6 +3244,9 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp)
                }
        }
 
+       /* Send client_id back if we received it and echo-client-id is on. */
+       echo_client_id(packet, lease, state->options, state->options);
+
        /* If we don't have a hostname yet, and we've been asked to do
           a reverse lookup to find the hostname, do it. */
        i = DHO_HOST_NAME;
@@ -4788,9 +4891,9 @@ int locate_network (packet)
  * options from the incoming packet and configuration information.
  *
  * out_options is the outgoing option cache.  This cache
- * may be the same as options.  If send_options isn't NULL
+ * may be the same as options.  If out_options isn't NULL
  * we may save the server address option into it.  We do so
- * if send_options is different than options or if the option
+ * if out_options is different than options or if the option
  * wasn't in options and we needed to find the address elsewhere.
  *
  * packet is the state structure for the incoming packet
@@ -4873,29 +4976,39 @@ get_server_source_address(struct in_addr *from,
        return;
 }
 
-/*
- * Set up an option state list to try and find a server option.
- * We don't go through all possible options - in particualr we
- * skip the hosts and we don't include the lease to avoid 
- * making changes to it.  This means that we won't get the
- * correct server id if the admin puts them on hosts or
- * builds the server id with information from the lease.
+/*!
+ * \brief Builds option set from statements at the global and network scope
  *
- * As this is a fallback function (used to handle NAKs or
- * sort out server id mismatch in failover) and requires
- * configuration by the admin, it should be okay.
+ * Set up an option state list based on the global and network scopes.
+ * These are primarily used by NAK logic to locate dhcp-server-id and
+ * echo-client-id.
+ *
+ * We don't go through all possible options - in particualr we skip the hosts
+ * and we don't include the lease to avoid making changes to it. This means
+ * that using these, we won't get the correct server id if the admin puts them
+ * on hosts or builds the server id with information from the lease.
+ *
+ * As this is a fallback function (used to handle NAKs or sort out server id
+ * mismatch in failover) and requires configuration by the admin, it should be
+ * okay.
+ *
+ * \param network_options option_state to which options will be added. If it
+ * refers to NULL, it will be allocated.  Caller is responsible to delete it.
+ * \param packet inbound packet
+ * \param network_group scope group to use if packet->shared_network is null.
  */
 void
-setup_server_source_address(struct in_addr *from,
-                           struct option_state *options,
-                           struct packet *packet) {
+eval_network_statements(struct option_state **network_options,
+                       struct packet *packet,
+                       struct group *network_group) {
 
-       struct option_state *sid_options = NULL;
+       if (*network_options == NULL) {
+               option_state_allocate (network_options, MDL);
+       }
 
+       /* Use the packet's shared_network if it has one.  If not use
+         * network_group and if it is null then use global scope. */
        if (packet->shared_network != NULL) {
-               option_state_allocate (&sid_options, MDL);
-
                /*
                 * If we have a subnet and group start with that else start
                 * with the shared network group.  The first will recurse and
@@ -4904,13 +5017,13 @@ setup_server_source_address(struct in_addr *from,
                if ((packet->shared_network->subnets != NULL) &&
                    (packet->shared_network->subnets->group != NULL)) {
                        execute_statements_in_scope(NULL, packet, NULL, NULL,
-                                       packet->options, sid_options,
+                                       packet->options, *network_options,
                                        &global_scope,
                                        packet->shared_network->subnets->group,
                                        NULL, NULL);
                } else {
                        execute_statements_in_scope(NULL, packet, NULL, NULL,
-                                       packet->options, sid_options,
+                                       packet->options, *network_options,
                                        &global_scope,
                                        packet->shared_network->group,
                                        NULL, NULL);
@@ -4919,23 +5032,23 @@ setup_server_source_address(struct in_addr *from,
                /* do the pool if there is one */
                if (packet->shared_network->pools != NULL) {
                        execute_statements_in_scope(NULL, packet, NULL, NULL,
-                                       packet->options, sid_options,
+                                       packet->options, *network_options,
                                        &global_scope,
                                        packet->shared_network->pools->group,
                                        packet->shared_network->group,
                                        NULL);
                }
-
-               /* currently we don't bother with classes or hosts as
-                * neither seems to be useful in this case */
-       }
-
-       /* Make the call to get the server address */
-       get_server_source_address(from, sid_options, options, packet);
-
-       /* get rid of the option cache */
-       if (sid_options != NULL)
-               option_state_dereference(&sid_options, MDL);
+       } else if (network_group != NULL) {
+                execute_statements_in_scope(NULL, packet, NULL, NULL,
+                                            packet->options, *network_options,
+                                            &global_scope, network_group,
+                                            NULL, NULL);
+       } else {
+                execute_statements_in_scope(NULL, packet, NULL, NULL,
+                                            packet->options, *network_options,
+                                            &global_scope, root_group,
+                                            NULL, NULL);
+    }
 }
 
 /*
index bdf382361efb80ef8b3bb75f0728639147370e0c..2f7eb7802bd5ee647c19395a004029f0fa5c9ea0 100644 (file)
@@ -2202,6 +2202,22 @@ caution.
 .RE
 .PP
 The
+.I echo-client-id
+statement
+.RS 0.25i
+.PP
+.B echo-client-id\fR \fIflag\fR\fB;\fR
+.PP
+The \fIecho-client-id\fR statement is used to enable or disable RFC 6842
+compliant behavior.  If the echo-client-id statement is present and has a
+value of true or on, and a DHCP DISCOVER or REQUEST is received which contains
+the client identifier option (Option code 61), the server will copy the option
+into its response (DHCP ACK or NAK) per RFC 6842.  In other words if the
+client sends the option it will receive it back. By default, this flag is off
+and client identifiers will not echoed back to the client.
+.RE
+.PP
+The
 .I filename
 statement
 .RS 0.25i
index f9b3a511cdb9c6549526f3058a297fad6c7f936d..ef3d74c36462ee431326913ad291dae6d9b8bcdb 100644 (file)
@@ -267,6 +267,7 @@ static struct option server_options[] = {
        { "ignore-client-uids", "f",            &server_universe,  82, 1 },
        { "log-threshold-low", "B",             &server_universe,  83, 1 },
        { "log-threshold-high", "B",            &server_universe,  84, 1 },
+       { "echo-client-id", "f",                &server_universe,  SV_ECHO_CLIENT_ID, 1 },
        { NULL, NULL, NULL, 0, 0 }
 };