====== DHCPINFORM with RFC 3011 subnet selection option ====== Sometimes it is useful to obtain configuration options for a different subnet. Unfortunately ISC DHCPD 4.2.0 does not support RFC 3011 for DHCPINFORM. This patch implements the missing function: Index: server/dhcp.c =================================================================== --- server/dhcp.c (Revision 138) +++ server/dhcp.c (Revision 140) @@ -947,12 +947,12 @@ struct packet outgoing; unsigned char dhcpack = DHCPACK; struct subnet *subnet = NULL; - struct iaddr cip, gip; + struct iaddr cip, gip, sip; unsigned i; int nulltp; struct sockaddr_in to; struct in_addr from; - isc_boolean_t zeroed_ciaddr; + isc_boolean_t zeroed_ciaddr, sso_found; /* The client should set ciaddr to its IP address, but apparently it's common for clients not to do this, so we'll use their IP @@ -987,24 +987,73 @@ return; } - /* Find the subnet that the client is on. */ + /* Find the subnet that the client is on. + * CC: Do the link selection / subnet selection + */ + + option_state_allocate (&options, MDL); + + sso_found = ISC_FALSE; + if ((oc = lookup_option(&agent_universe, packet->options, + RAI_LINK_SELECT)) == NULL) + oc = lookup_option(&dhcp_universe, packet->options, + DHO_SUBNET_SELECTION); + + memset (&d1, 0, sizeof d1); + if (oc && evaluate_option_cache (&d1, packet, (struct lease *)0, + (struct client_state *)0, + packet->options, + (struct option_state *)0, + &global_scope, oc, MDL)) { + if (d1.len != 4) { + log_info ("%s: ignored (invalid subnet selection option).", msgbuf); + option_state_dereference (&options, MDL); + return; + } + memcpy (sip.iabuf, d1.data, 4); + data_string_forget (&d1, MDL); + /* Make a copy of the data. */ + struct option_cache *noc = (struct option_cache *)0; + if (option_cache_allocate (&noc, MDL)) { + if (oc->data.len) + data_string_copy (&noc->data, + &oc->data, MDL); + if (oc->expression) + expression_reference (&noc->expression, + oc->expression, MDL); + if (oc->option) + option_reference(&(noc->option), oc->option, + MDL); + } + save_option (&dhcp_universe, options, noc); + option_cache_dereference (&noc, MDL); + sso_found = ISC_TRUE; + } + sip.len = 4; + if (zeroed_ciaddr && (gip.len != 0)) { - /* XXX - do subnet selection relay agent suboption here */ - find_subnet(&subnet, gip, MDL); + if (!sso_found) + memcpy (sip.iabuf, gip.iabuf, 4); + + find_subnet(&subnet, sip, MDL); if (subnet == NULL) { - log_info("%s: unknown subnet for relay address %s", - msgbuf, piaddr(gip)); + log_info("%s: unknown subnet for %s address %s", + msgbuf, sso_found ? "relay link select" : "relay", piaddr(sip)); + option_state_dereference (&options, MDL); return; } } else { - /* XXX - do subnet selection (not relay agent) option here */ - find_subnet(&subnet, cip, MDL); + if (!sso_found) + memcpy (sip.iabuf, cip.iabuf, 4); + + find_subnet(&subnet, sip, MDL); if (subnet == NULL) { log_info("%s: unknown subnet for %s address %s", - msgbuf, zeroed_ciaddr ? "source" : "client", + msgbuf, sso_found ? "selected" : (zeroed_ciaddr ? "source" : "client"), piaddr(sip)); + option_state_dereference (&options, MDL); return; } } @@ -1031,10 +1080,10 @@ if (eso++ == 100) eso = 0; subnet_dereference (&subnet, MDL); + option_state_dereference (&options, MDL); return; } - - option_state_allocate (&options, MDL); + memset (&outgoing, 0, sizeof outgoing); memset (&raw, 0, sizeof raw); outgoing.raw = &raw;