one-lease-per-mac patch

bart at kuleuven.net bart at kuleuven.net
Wed Sep 19 19:47:33 UTC 2007


Since, as so many before us, we'd like to have only one DHCP lease per MAC
address (e.g. for double/triple boot machines) and the same lease (with
the same IP) each time (e.g. for PXE boots with NFS mounts) [1], for the
last few years we've been using a patched ISC DHCP server with a patch
extremely similar to the (in)famous patch from Didi at
<http://www.cs.tau.ac.il/~didi/dhcp/>.  This has so far worked very well
for us.

However, we feel it's time to change to a new setup using DHCP failover.
Didi's patch still works, but doesn't seem to handle the case of 2 active
failover peers.  That's why after further analysis of the find_lease()
function I've come up with a new patch, which modifies two more areas in
that function.  So far, my tests of the patched server have been
successful.  In order to make the patch more acceptable, I've added a
flag 'one-lease-per-mac' which can be globally set in dhcpd.conf.  The
patch is also pretty verbose if you define DEBUG_FIND_LEASE.

If you set the new flag, the DHCP server will try hard to ignore the
client identifier when trying to find a suitable lease for a client.  This
means that if there's *one* lease for the same MAC address as the client's,
that's the IP the client will get.  So only one lease will be used for
that MAC address, not only one active lease, but only one lease of
whatever kind (active, expired, ...).

One problem might remain though (I guess it existed with Didi's patch
too): if the client would request a different IP (e.g. using a REBIND
during a Windows boot), it will get it, resulting in more than one lease
for the MAC address.  If, for that reason or another one, there is more
than one lease that matches the MAC address, that situation seems to
remain (at least until some of the leases disappear, e.g. because they are
used for another MAC address).  So when using the patched server in a
pre-existing network, you must make sure the first lease a MAC address
gets is the one recorded by the clients using that MAC address (in our
case the lease recorded by the Windows installation).

I've included the patch at the end.  It's in Debian's dpatch format.  The
patch is also available at:
<http://bart.kulnet.kuleuven.be/dhcp/dhcp.c.one-lease-per-mac.dpatch>

Any comments, improvements, thoughts, etc. are welcome.

Kind regards
Bart Van den Broeck
---------------------- ICT-Infrastructuur - Netwerken aka KULeuvenNet --
---------------------- LUDIT - ICTS - K.U.Leuven --


References: [1] "'deny duplicates' not working as documented ?", Yves
   Tavernier, <http://marc.info/?l=dhcp-server&m=101706266805607&w=2>






############################################################
#! /bin/sh /usr/share/dpatch/dpatch-run
## dhcp.c.one-lease-per-mac.dpatch by Bart Van den Broeck <bart () kuleuven ! net>
##
## All lines beginning with `## DP:' are a description of the patch.
## DP: Ignore client identifier mismatches when finding matching existing lease.

@DPATCH@
diff -urNad dhcp3-3.1.0/includes/dhcpd.h tmp/dhcp3-3.1.0/includes/dhcpd.h
--- dhcp3-3.1.0/includes/dhcpd.h        2007-05-29 19:49:44.000000000 +0200
+++ tmp/dhcp3-3.1.0/includes/dhcpd.h        2007-08-24 11:08:19.902431456 +0200
@@ -553,6 +553,7 @@
 #define SV_ADAPTIVE_LEASE_TIME_THRESHOLD        50
 #define SV_DO_REVERSE_UPDATES                51
 #define SV_FQDN_REPLY                        52
+#define SV_ONE_LEASE_PER_MAC                100
 
 #if !defined (DEFAULT_PING_TIMEOUT)
 # define DEFAULT_PING_TIMEOUT 1
diff -urNad dhcp3-3.1.0/server/dhcp.c tmp/dhcp3-3.1.0/server/dhcp.c
--- dhcp3-3.1.0/server/dhcp.c        2007-05-22 00:13:50.000000000 +0200
+++ tmp/dhcp3-3.1.0/server/dhcp.c        2007-08-27 07:39:17.353124232 +0200
@@ -2996,6 +2996,33 @@
         struct data_string client_identifier;
         int status;
         struct hardware h;
+        /* XXX (Bart Van den Broeck <bart at kuleuven.net>)
+         * XXX (bart) Record whether the one-lease-per-mac option is set.
+         * XXX If it is, client identifier mismatches will be ignored. */
+        int one_lease_per_mac = 0;
+        int one_lease_per_mac_fx = 0;
+        struct option_state *options = (struct option_state *)0;
+        int ignorep;
+        option_state_allocate (&options, MDL);
+        execute_statements_in_scope ((struct binding_value **)0,
+                                     (struct packet *)0,
+                                     (struct lease *)0,
+                                     (struct client_state *)0,
+                                     (struct option_state *)0,
+                                     options, &global_scope,
+                                     root_group,
+                                     (struct group *)0);
+        if ((oc = lookup_option (&server_universe, options,
+                                 SV_ONE_LEASE_PER_MAC)) &&
+            evaluate_boolean_option_cache (&ignorep,
+                                           (struct packet *)0,
+                                           (struct lease *)0,
+                                           (struct client_state *)0,
+                                           options, (struct option_state *)0,
+                                           &global_scope, oc, MDL)) {
+                one_lease_per_mac = 1;
+        }
+        option_state_dereference (&options, MDL);
 
         /* Quick check to see if the peer has leases. */
         if (peer_has_leases) {
@@ -3210,13 +3237,16 @@
                 }
 #endif
 
+                /* XXX (bart) Only consider client identifier mismatches
+                 * XXX if the one-lease-per-mac option is not set. */
                 if (hw_lease -> binding_state != FTS_FREE &&
                     hw_lease -> binding_state != FTS_BACKUP &&
                     hw_lease -> uid &&
                     (!have_client_identifier ||
                      hw_lease -> uid_len != client_identifier.len ||
                      memcmp (hw_lease -> uid, client_identifier.data,
-                             hw_lease -> uid_len))) {
+                             hw_lease -> uid_len)) &&
+                    (one_lease_per_mac_fx = 1) && !one_lease_per_mac) {
 #if defined (DEBUG_FIND_LEASE)
                         log_info ("wrong client identifier: %s",
                                   piaddr (hw_lease -> ip_addr));
@@ -3224,6 +3254,13 @@
                         goto n_hw;
                         continue;
                 }
+#if defined (DEBUG_FIND_LEASE)
+                if (one_lease_per_mac && one_lease_per_mac_fx == 1) {
+                        log_info ("Skipping \"%s%s\" due to one-lease-per-mac set",
+                                  "wrong client identifier: ",
+                                  piaddr (hw_lease -> ip_addr));
+                }
+#endif
                 if (hw_lease -> subnet -> shared_network != share) {
 #if defined (DEBUG_FIND_LEASE)
                         log_info ("wrong network segment: %s",
@@ -3298,12 +3335,20 @@
 
         /* Toss ip_lease if it hasn't yet expired and doesn't belong to the
            client. */
+        /* XXX (bart) Only consider client identifier mismatches
+         * XXX if the one-lease-per-mac option is not set. */
         if (ip_lease &&
             (ip_lease -> uid ?
-             (!have_client_identifier ||
-              ip_lease -> uid_len != client_identifier.len ||
-              memcmp (ip_lease -> uid, client_identifier.data,
-                      ip_lease -> uid_len)) :
+             ((!have_client_identifier ||
+               ip_lease -> uid_len != client_identifier.len ||
+               memcmp (ip_lease -> uid, client_identifier.data,
+                       ip_lease -> uid_len)) &&
+              (!one_lease_per_mac || !(one_lease_per_mac_fx = 3) ||
+               (ip_lease -> hardware_addr.hbuf [0] != packet -> raw -> htype ||
+                ip_lease -> hardware_addr.hlen != packet -> raw -> hlen + 1 ||
+                memcmp (&ip_lease -> hardware_addr.hbuf [1],
+                        packet -> raw -> chaddr,
+                        (unsigned)(ip_lease -> hardware_addr.hlen - 1))))) :
              (ip_lease -> hardware_addr.hbuf [0] != packet -> raw -> htype ||
               ip_lease -> hardware_addr.hlen != packet -> raw -> hlen + 1 ||
               memcmp (&ip_lease -> hardware_addr.hbuf [1],
@@ -3326,6 +3371,15 @@
                         lease_dereference (&ip_lease, MDL);
                 }
         }
+#if defined (DEBUG_FIND_LEASE)
+        if (one_lease_per_mac && one_lease_per_mac_fx == 3) {
+                if (ip_lease -> binding_state != FTS_FREE &&
+                    ip_lease -> binding_state != FTS_BACKUP) {
+                        log_info ("Skipping \"%s\" due to one-lease-per-mac set.",
+                                  "rejecting lease for requested address.");
+                }
+        }
+#endif
 
         /* If we got an ip_lease and a uid_lease or hw_lease, and ip_lease
            is not active, and is not ours to reallocate, forget about it. */
@@ -3399,12 +3453,21 @@
                                 "database conflict - call for help!");
                 }
 
-                if (ip_lease && ip_lease != uid_lease) {
+                /* XXX (bart) Ignore client identifier mismatches
+                 * XXX if the one-lease-per-mac option is set. */
+                if (ip_lease && ip_lease != uid_lease &&
+                    (one_lease_per_mac_fx = 4) && !one_lease_per_mac) {
 #if defined (DEBUG_FIND_LEASE)
                         log_info ("requested address not available.");
 #endif
                         lease_dereference (&ip_lease, MDL);
                 }
+#if defined (DEBUG_FIND_LEASE)
+                if (one_lease_per_mac && one_lease_per_mac_fx == 4) {
+                        log_info ("Skipping \"%s\" due to one-lease-per-mac set.",
+                                  "requested address not available.");
+                }
+#endif
         }
 
         /* If we get to here with both fixed_lease and ip_lease not
@@ -3555,18 +3618,32 @@
                            send a client identifier and it's a bootp client,
                            but the lease has a client identifier, we still
                            let the client have a lease. */
+                        /* XXX (bart) Ignore client identifier mismatches
+                         * XXX if the one-lease-per-mac option is set. */
                         if (!hw_lease -> uid_len ||
                             (have_client_identifier
-                             ? (hw_lease -> uid_len ==
-                                client_identifier.len &&
-                                !memcmp (hw_lease -> uid,
-                                         client_identifier.data,
-                                         client_identifier.len))
-                             : packet -> packet_type == 0)) {
+                             ? ((hw_lease -> uid_len ==
+                                 client_identifier.len &&
+                                 !memcmp (hw_lease -> uid,
+                                          client_identifier.data,
+                                          client_identifier.len)) ||
+                                (one_lease_per_mac &&
+                                 (one_lease_per_mac_fx = 2)))
+                             : packet -> packet_type == 0 ||
+                                (one_lease_per_mac &&
+                                 (one_lease_per_mac_fx = 2)))) {
                                 lease_reference (&lease, hw_lease, MDL);
                                 if (lease -> host)
                                         host_dereference (&lease -> host, MDL);
 #if defined (DEBUG_FIND_LEASE)
+                                if (one_lease_per_mac &&
+                                    one_lease_per_mac_fx == 2) {
+                                        log_info ("Skipping \"%s%s.\" due to one-lease-per-mac set.",
+                                                  "not choosing hardware lease: ",
+                                                  "uid mismatch");
+                                }
+#endif
+#if defined (DEBUG_FIND_LEASE)
                                 log_info ("choosing hardware lease.");
 #endif
                         } else {
diff -urNad dhcp3-3.1.0/server/stables.c tmp/dhcp3-3.1.0/server/stables.c
--- dhcp3-3.1.0/server/stables.c        2007-04-28 00:48:10.000000000 +0200
+++ tmp/dhcp3-3.1.0/server/stables.c        2007-08-24 11:08:19.905431000 +0200
@@ -238,6 +238,7 @@
         { "adaptive-lease-time-threshold", "B",        &server_universe,  50, 1 },
         { "do-reverse-updates", "f",                &server_universe,  51, 1 },
         { "fqdn-reply", "f",                        &server_universe,  52, 1 },
+        { "one-lease-per-mac", "f",                &server_universe, 100, 1 },
         { NULL, NULL, NULL, 0, 0 }
 };


Disclaimer: http://www.kuleuven.be/cwis/email_disclaimer.htm



More information about the dhcp-users mailing list