From 656b1ecebe193cace9afc6be7b370e541f81a7df Mon Sep 17 00:00:00 2001 From: Tomek Mrugalski Date: Fri, 22 Apr 2011 13:21:35 +0000 Subject: [PATCH] The DHCP server now responds to DHCPLEASEQUERY messages from agents using IP addresses not covered by a subnet in configuration. Server also returns vendor-class-id option, if client sent it. [ISC-Bugs #21094] --- RELNOTES | 7 +++ server/dhcp.c | 14 ++++++ server/dhcpd.leases.5 | 7 ++- server/dhcpleasequery.c | 100 +++++++++++++++++++++++++++++++++------- 4 files changed, 110 insertions(+), 18 deletions(-) diff --git a/RELNOTES b/RELNOTES index 4ee77fa4b..5dd054f78 100644 --- a/RELNOTES +++ b/RELNOTES @@ -98,6 +98,13 @@ work on other platforms. Please report any problems and suggested fixes to - If a 'next-server' parameter is configured in a dynamic host record via OMAPI as a domain name, the syntax written to disk is now correctly parsed upon restart. [ISC-Bugs #22266] + +- The DHCP server now responds to DHCPLEASEQUERY messages from agents using + IP addresses not covered by a subnet in configuration. Whether or not to + respond to such an agent is still governed by the 'allow leasequery;' + configuration parameter, in the case of an agent not covered by a configured + subnet the root configuration area is examined. Server now also returns + vendor-class-id option, if client sent it. [ISC-Bugs #21094] Changes since 4.2.0 diff --git a/server/dhcp.c b/server/dhcp.c index 57f39d5fe..28a203222 100644 --- a/server/dhcp.c +++ b/server/dhcp.c @@ -2329,6 +2329,20 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp) option_chain_head_reference (< -> agent_options, lease -> agent_options, MDL); + /* Save the vendor-class-identifier for DHCPLEASEQUERY. */ + oc = lookup_option(&dhcp_universe, packet->options, + DHO_VENDOR_CLASS_IDENTIFIER); + if (oc != NULL && + evaluate_option_cache(&d1, packet, NULL, NULL, packet->options, + NULL, &lease->scope, oc, MDL)) { + if (d1.len != 0) { + bind_ds_value(&lease->scope, "vendor-class-identifier", + &d1); + } + + data_string_forget(&d1, MDL); + } + /* If we got relay agent information options from the packet, then * cache them for renewal in case the relay agent can't supply them * when the client unicasts. The options may be from an addressed diff --git a/server/dhcpd.leases.5 b/server/dhcpd.leases.5 index 377c0f631..f077c33b2 100644 --- a/server/dhcpd.leases.5 +++ b/server/dhcpd.leases.5 @@ -28,7 +28,7 @@ .\" see ``http://www.vix.com''. To learn more about Nominum, Inc., see .\" ``http://www.nominum.com''. .\" -.\" $Id: dhcpd.leases.5,v 1.15 2009/11/24 02:06:57 sar Exp $ +.\" $Id: dhcpd.leases.5,v 1.16 2011/04/22 13:21:35 tomasz Exp $ .\" .TH dhcpd.leases 5 .SH NAME @@ -241,6 +241,11 @@ variable will record the name that the DHCP server used for the PTR record. The name to which the PTR record points will be either the \fIddns-fwd-name\fR or the \fIddns-client-fqdn\fR. .PP +.B The \fIvendor-class-identifier\fB variable +.PP +The server retains the client-supplied Vendor Class Identifier option +for informational purposes, and to render them in DHCPLEASEQUERY responses. +.PP .B on \fIevents\fB { \fIstatements...\fB } The \fBon\fI statement records a list of statements to execute if a certain event occurs. The possible events that can occur for an diff --git a/server/dhcpleasequery.c b/server/dhcpleasequery.c index ab937d061..9daff8949 100644 --- a/server/dhcpleasequery.c +++ b/server/dhcpleasequery.c @@ -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; @@ -180,23 +178,26 @@ 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; @@ -209,8 +210,9 @@ dhcpleasequery(struct packet *packet, int ms_nulltp) { packet->options, options, &global_scope, - subnet->group, + relay_group, NULL); + for (i=packet->class_count-1; i>=0; i--) { execute_statements_in_scope(NULL, packet, @@ -220,11 +222,9 @@ dhcpleasequery(struct packet *packet, int ms_nulltp) { options, &global_scope, packet->classes[i]->group, - subnet->group); + relay_group); } - subnet_dereference(&subnet, MDL); - /* * Because LEASEQUERY has some privacy concerns, default to deny. */ @@ -245,7 +245,7 @@ dhcpleasequery(struct packet *packet, int ms_nulltp) { option_state_dereference(&options, MDL); return; } - + /* * Copy out the client IP address. @@ -385,6 +385,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. @@ -430,7 +454,11 @@ dhcpleasequery(struct packet *packet, int ms_nulltp) { (lease_duration / 8); if (time_renewal > cur_time) { - time_renewal = htonl(time_renewal - cur_time); + if (time_renewal < cur_time) + time_renewal = 0; + else + time_renewal = htonl(time_renewal - cur_time); + if (!add_option(options, DHO_DHCP_RENEWAL_TIME, &time_renewal, @@ -445,6 +473,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, @@ -458,6 +487,14 @@ dhcpleasequery(struct packet *packet, int ms_nulltp) { } if (lease->ends > cur_time) { + if (time_expiry < cur_time) { + log_error("Impossible condition at %s:%d.", + MDL); + + option_state_dereference(&options, MDL); + lease_dereference(&lease, MDL); + return; + } time_expiry = htonl(lease->ends - cur_time); if (!add_option(options, DHO_DHCP_LEASE_TIME, @@ -471,9 +508,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) { -- 2.39.5