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;
Copyright © 2024 Christof Chen