[pisa-src] r2568 - in trunk/tools/dhcp: README.isc-dhcp-patches isc-dhcp-3.1.3-mobac.patch

  • From: Christoph Viethen <christoph.viethen@xxxxxxxxxxxxxx>
  • To: pisa-src@xxxxxxxxxxxxx
  • Date: Mon, 09 May 2011 15:05:24 +0200

Author: viethen
Date: Mon May  9 15:05:23 2011
New Revision: 2568

Log:
Add patch to make ISC DHCP server 3.1.3 hand out IP addresses
according to client's hardware addresses. Add documentation for
this patch (and for its companion patch for the ISC 4.1.1-P1
server).

Added:
   trunk/tools/dhcp/README.isc-dhcp-patches
   trunk/tools/dhcp/isc-dhcp-3.1.3-mobac.patch

Added: trunk/tools/dhcp/README.isc-dhcp-patches
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ trunk/tools/dhcp/README.isc-dhcp-patches    Mon May  9 15:05:23 2011        
(r2568)
@@ -0,0 +1,76 @@
+tools/dhcp/isc-dhcp-3.1.3-mobac.patch
+tools/dhcp/isc-dhcp-4.1.1-P1-mobac.patch
+
+These patches make the well-known DHCP servers from ISC hand out IP addresses
+according to the MobileACcess scheme, based on the hardware address of the
+requesting client.
+
+Release notes & documentation
+=============================
+
+a) Configuration
+
+Quite generally, the special code for generating IP addresses according to
+hardware address will only be used for those IP ranges, as defined in the
+DHCP server's config, which have the special "hash-mode" parameter.
+
+For example, your /etc/dhcp/dhcpd.conf may contain the following:
+
+subnet 192.168.8.0 netmask 255.255.255.0 {
+        range 192.168.8.100 192.168.8.250 hash-mode pisa;
+}
+
+This means that for clients which the DHCP server believes to belong
+to this subnet and to be supposed to get an address from this range,
+the "pisa" hash mode will be used. Other clients will be unaffected
+and will get an IP address according to the other rules in the
+config file.
+
+Currently, the hash-mode parameter may contain
+
+default (behave as if no special hash-mode given)
+pisa    (hand out IP addresses according to hardware address)
+pisa24h (alias for pisa)
+
+More will be added.
+
+
+b) Missing functionality
+
+Right now, the IP address that's generated is dependent on the day of year -
+this will be used as a seed in IP generation. The idea is that this way,
+a hash collision will more or less automatically disappear the next day.
+
+It is planned to make this timeslot considerably smaller, making sure IP
+addresses are dependent on "parts of days", for example being different for
+every sixth of a day.
+
+
+It is somewhat undecided what exactly to do when a hash collision turns
+up. Right now, the client for which the same IP address gets generated that
+some other client is still holding in form of a lease simply won't get an
+IP address at all. This needs to be fixed.
+
+
+c) Known flaws / bugs
+
+The code could be more informative in some specific situations, creating
+log entries when certain things go wrong. Instead, it just silently fails,
+e.g. not handing out an IP address.
+
+When getting DHCP packets through a DHCP relay of some sort, the packets'
+hardware addresses most probably indicate the hardware address of the
+relay (and not the hardware address of the actual client). This will lead
+to wrong assignment of IP addresses and lots of hash collisions. This
+really need to be fixed. An actual DHCP relay for testing purposes would come
+in very handy.
+
+The interaction of this code with a setup of redundant DHCP servers in a
+fail-over configuration is entirely untested. It may work well, or it
+may fail terribly.
+
+This code will only deal with 6-byte hardware addresses in a typical
+ethernet environment. Attempting to use it in, e.g., FDDI or other network
+setups is unsupported, though it should fail gracefully.
+
+

Added: trunk/tools/dhcp/isc-dhcp-3.1.3-mobac.patch
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ trunk/tools/dhcp/isc-dhcp-3.1.3-mobac.patch Mon May  9 15:05:23 2011        
(r2568)
@@ -0,0 +1,448 @@
+diff -u -r dhcp-3.1.3-orig/common/conflex.c dhcp-3.1.3/common/conflex.c
+--- dhcp-3.1.3-orig/common/conflex.c   2009-09-01 22:32:27.000000000 +0200
++++ dhcp-3.1.3/common/conflex.c        2011-05-02 15:11:38.000000000 +0200
+@@ -791,6 +791,8 @@
+             case 'h':
+               if (!strcasecmp(atom + 1, "ash"))
+                       return HASH;
++              if (!strcasecmp(atom + 1, "ash-mode"))
++                      return HASH_MODE;
+               if (!strcasecmp (atom + 1, "ba"))
+                       return HBA;
+               if (!strcasecmp (atom + 1, "ost"))
+diff -u -r dhcp-3.1.3-orig/includes/dhcpd.h dhcp-3.1.3/includes/dhcpd.h
+--- dhcp-3.1.3-orig/includes/dhcpd.h   2009-09-01 22:32:27.000000000 +0200
++++ dhcp-3.1.3/includes/dhcpd.h        2011-05-09 14:17:18.669621432 +0200
+@@ -686,6 +686,17 @@
+       struct class *class;
+ };
+ 
++#define HASH_MODE_DEFAULT     0
++#define HASH_MODE_PISA                1
++#define HASH_MODE_PISA24H     1
++#define HASH_MODE_PISA1H      2
++#define HASH_MODE_PISA2H      3
++#define HASH_MODE_PISA3H      4
++#define HASH_MODE_PISA4H      5
++#define HASH_MODE_PISA6H      6
++#define HASH_MODE_PISA8H      7
++#define HASH_MODE_PISA12H     8
++
+ struct pool {
+       OMAPI_OBJECT_PREAMBLE;
+       struct pool *next;
+@@ -704,6 +715,9 @@
+       int free_leases;
+       int backup_leases;
+       int index;
++      struct in_addr range_low;       /* lowest IP in pool */
++      struct in_addr range_high;      /* highest IP "  "   */
++      int hash_mode;                  /* MobileACcess hash mode */
+ #if defined (FAILOVER_PROTOCOL)
+       dhcp_failover_state_t *failover_peer;
+ #endif
+@@ -1118,6 +1132,11 @@
+ HASH_FUNCTIONS_DECL (host, const unsigned char *, struct host_decl, 
host_hash_t)
+ HASH_FUNCTIONS_DECL (class, const char *, struct class, class_hash_t)
+ 
++/* mac2ip.c */
++
++void hash_mac_to_rnd_ip_OAAT(const unsigned char *, struct in_addr *);
++void adjust_ip_to_bounds(struct in_addr *, const struct in_addr *, const 
struct in_addr *);
++
+ /* options.c */
+ 
+ extern struct option *vendor_cfg_option;
+@@ -2006,6 +2025,7 @@
+ 
+ extern struct enumeration ddns_styles;
+ extern struct enumeration syslog_enum;
++extern struct enumeration hash_modes;
+ void initialize_server_option_spaces PROTO ((void));
+ 
+ /* inet.c */
+diff -u -r dhcp-3.1.3-orig/includes/dhctoken.h dhcp-3.1.3/includes/dhctoken.h
+--- dhcp-3.1.3-orig/includes/dhctoken.h        2009-09-01 22:32:27.000000000 
+0200
++++ dhcp-3.1.3/includes/dhctoken.h     2011-05-02 15:15:36.000000000 +0200
+@@ -326,7 +326,8 @@
+       DOMAIN_LIST = 630,
+       LEASEQUERY = 631,
+       EXECUTE = 632,
+-      CONFLICT_DONE = 660
++      CONFLICT_DONE = 660,
++      HASH_MODE = 800
+ };
+ 
+ #define is_identifier(x)      ((x) >= FIRST_TOKEN &&  \
+diff -u -r dhcp-3.1.3-orig/server/confpars.c dhcp-3.1.3/server/confpars.c
+--- dhcp-3.1.3-orig/server/confpars.c  2009-09-01 22:32:28.000000000 +0200
++++ dhcp-3.1.3/server/confpars.c       2011-05-02 15:57:41.000000000 +0200
+@@ -3181,8 +3181,8 @@
+ }
+ 
+ /* address-range-declaration :== ip-address ip-address SEMI
+-                             | DYNAMIC_BOOTP ip-address ip-address SEMI */
+-
++                             | DYNAMIC_BOOTP ip-address ip-address SEMI
++                             | ip-address ip-address HASH_MODE mode SEMI */
+ void parse_address_range (cfile, group, type, inpool, lpchain)
+       struct parse *cfile;
+       struct group *group;
+@@ -3202,6 +3202,9 @@
+       struct pool *pool;
+       isc_result_t status;
+ 
++      struct enumeration_value *evalue;
++      int hashmode = 0;
++
+       if ((token = peek_token (&val,
+                                (unsigned *)0, cfile)) == DYNAMIC_BOOTP) {
+               token = next_token (&val, (unsigned *)0, cfile);
+@@ -3226,6 +3229,35 @@
+               high.len = len;
+       }
+ 
++      /* evaluate the optional hash-mode parameter */
++      if ((token = peek_token(&val, (unsigned *) 0, cfile)) == HASH_MODE) {
++          token = next_token(&val, (unsigned *) 0, cfile);
++
++          if (dynamic == 1) {
++              parse_warn(cfile, "parameter unsupported for dynamic-bootp "
++                                                              "ranges.");
++              skip_to_semi(cfile);
++              return;
++          }
++
++          token = next_token(&val, (unsigned *) 0, cfile);
++
++          if (token != NAME && token != DEFAULT) {
++              parse_warn(cfile, "expected a hash-mode string");
++              skip_to_semi(cfile);
++              return;
++          }
++
++          if ((evalue = find_enumeration_value("hash-modes", 10, val))
++                                                              != NULL) {
++              hashmode = evalue -> value;
++          } else {
++              parse_warn(cfile, "expected a valid hash-mode");
++              skip_to_semi(cfile);
++              return;
++          }
++      }
++
+       token = next_token (&val, (unsigned *)0, cfile);
+       if (token != SEMI) {
+               parse_warn (cfile, "semicolon expected.");
+@@ -3341,6 +3373,23 @@
+       }
+ #endif /* FAILOVER_PROTOCOL */
+ 
++      /* enter the hash-mode parameter into the pool structure */
++      pool -> hash_mode = hashmode;
++
++      /* same for the low and high IP values - at later times,
++          when we need them again, we really don't want to have to look
++          through /all/ the leases individually to find them out again! */
++      if (low.len == 4 && high.len == 4) {
++          if (ntohl(*((uint32_t *)low.iabuf)) >       /* swap if necessary */
++              ntohl(*((uint32_t *)high.iabuf))) {
++              pool->range_low.s_addr  = *((uint32_t *) high.iabuf);
++              pool->range_high.s_addr = *((uint32_t *) low.iabuf);
++          } else {
++              pool->range_low.s_addr  = *((uint32_t *) low.iabuf);
++              pool->range_high.s_addr = *((uint32_t *) high.iabuf);
++          }
++      }
++
+       /* Create the new address range... */
+       new_address_range (cfile, low, high, subnet, pool, lpchain);
+       pool_dereference (&pool, MDL);
+diff -u -r dhcp-3.1.3-orig/server/dhcp.c dhcp-3.1.3/server/dhcp.c
+--- dhcp-3.1.3-orig/server/dhcp.c      2009-09-01 22:32:28.000000000 +0200
++++ dhcp-3.1.3/server/dhcp.c   2011-05-09 13:02:33.378235331 +0200
+@@ -1479,6 +1479,9 @@
+       int val;
+       int ignorep;
+ 
++      struct tm mobac_cur_tm = { 0 };
++      TIME mobac_seconds_till_end = 0, mobac_lease_end = 0;
++
+       /* If we're already acking this lease, don't do it again. */
+       if (lease -> state)
+               return;
+@@ -2211,6 +2214,47 @@
+               lt -> next_binding_state = FTS_ACTIVE;
+       }
+ 
++      /* MobileACcess: If we are using special hash modes, the expiry time
++          that we offer to the client must end before a certain point in
++          time. E.g., if due to our hash mode we hand out a different IP
++          address on every calendar day, we must make sure we never offer
++          a lease time that extends beyond midnight. It's this particular
++          point in time that we call mobac_lease_end. */
++
++      if (lease -> pool -> hash_mode > 0)
++          if (gmtime_r(&cur_time, &mobac_cur_tm) != NULL)
++          {
++              /* take the current time-of-day and find out
++                  how many seconds are missing to reach mobac_lease_end
++                  (currently, only "midnight" is implemented). */
++              mobac_seconds_till_end = 59 - mobac_cur_tm.tm_sec;
++
++                /* cater for leap seconds ... */
++              if (mobac_seconds_till_end < 0) mobac_seconds_till_end = 0;
++
++              mobac_seconds_till_end += (59 - mobac_cur_tm.tm_min) * 60;
++              mobac_seconds_till_end += (23 - mobac_cur_tm.tm_hour) * 3600;
++
++              mobac_lease_end = cur_time + mobac_seconds_till_end;
++
++              /* make sure neither "state -> offered_expiry" nor
++                  "lt -> ends" reach beyond our cut-off time */
++              if (state -> offered_expiry > mobac_lease_end)
++                  state -> offered_expiry = mobac_lease_end;
++
++              if (lt -> ends > mobac_lease_end)
++                  lt -> ends = mobac_lease_end;
++          }
++          else  /* gmtime_r() failed - clean up */
++          {
++              free_lease_state (state, MDL);
++              lease_dereference (&lt, MDL);
++
++              if (host)
++                  host_dereference (&host, MDL);
++              return;
++          }
++
+       /* Update Client Last Transaction Time. */
+       lt->cltt = cur_time;
+ 
+@@ -3717,6 +3761,11 @@
+       struct lease *lease = (struct lease *)0;
+       struct lease *candl = (struct lease *)0;
+ 
++      unsigned char mobac_mac_address[6];
++      struct in_addr mobac_ip_address;
++      struct lease *mobac_lease = NULL;
++      struct iaddr mobac_iaddr;
++
+       for (; pool ; pool = pool -> next) {
+               if ((pool -> prohibit_list &&
+                    permitted (packet, pool -> prohibit_list)) ||
+@@ -3827,6 +3876,64 @@
+       }
+ 
+       if (lease) {
++          /* MobileACcess: Through some magic, dhcpd has determined a lease
++              to hand out to the requester. We wait until this point because
++              we trust all the choices the dhcpd authors made regarding
++              finding the "right" pool of leases; so now, finally, it's the
++              time for looking up that pool from the lease so a special
++              hash mode may be applied if so configured.
++              (This may look a bit clumsy, but seems to be the only way to
++              really hit the right pool without too much code duplication.) */
++
++          /* if "pisa" hash mode is demanded, apply our algorithms
++              - otherwise (hash_mode == 0), we don't need to do anything,
++              because the lease picked by the DHCP server should be okay
++              already */
++          if (lease->pool->hash_mode == 1)
++          {
++              /* we do not touch anything that's not standard ethernet-style
++                  (somebody could be trying to use this code on an FDDI
++                  network or something similarly obscure) */
++              if (packet->haddr->hlen != 7 || packet->haddr->hbuf[0] != 
ARPHRD_ETHER)
++              {
++                  return 0;
++              }
++
++              memcpy(mobac_mac_address, packet->haddr->hbuf + 1, 
packet->haddr->hlen - 1);
++
++              /* apply the MobileACcess-specific hash function to generate
++                  IPv4 address based on the hardware address */
++              hash_mac_to_rnd_ip_OAAT(mobac_mac_address, &mobac_ip_address);
++
++              /* make the generated IP address fit inbetween lower and upper
++                  IP address boundaries of configured IP range */
++              adjust_ip_to_bounds(&mobac_ip_address, 
&lease->pool->range_high, &lease->pool->range_low);
++
++              /* put the resulting IP address into the internal format
++                  expected by ISC DHCP server code */
++              memcpy(mobac_iaddr.iabuf, &mobac_ip_address.s_addr, 4);
++              mobac_iaddr.len = 4;
++
++              if (find_lease_by_ip_addr(&mobac_lease, mobac_iaddr, MDL))
++              {
++                  /* now try to find out whether the lease we want to hand
++                      out is a free or at least an abandoned one; everything
++                      else points to some sort of error, possibly a hash
++                      collision that made somebody else get this lease
++                      before */
++                  if((mobac_lease -> binding_state == FTS_FREE &&
++                      !(mobac_lease -> flags & RESERVED_LEASE)) ||
++                        mobac_lease -> binding_state == FTS_ABANDONED)
++                  {
++                      lease = mobac_lease;
++                  }
++                  else
++                      return 0; /* lease is not free - potential HASH 
COLLISION */
++              }
++              else
++                  return 0; /* hashing error? would point to a serious bug */
++          }
++
+               if (lease -> binding_state == FTS_ABANDONED)
+                       log_error ("Reclaiming abandoned lease %s.",
+                                  piaddr (lease -> ip_addr));
+diff -u -r dhcp-3.1.3-orig/server/dhcpd.c dhcp-3.1.3/server/dhcpd.c
+--- dhcp-3.1.3-orig/server/dhcpd.c     2009-09-01 22:32:28.000000000 +0200
++++ dhcp-3.1.3/server/dhcpd.c  2011-05-02 19:06:46.281479040 +0200
+@@ -433,6 +433,10 @@
+       add_enumeration (&ddns_styles);
+       add_enumeration (&syslog_enum);
+ 
++      /* MobileACcess: add the different hash modes so they can be
++            recognized in the config. */
++        add_enumeration (&hash_modes);
++
+       if (!group_allocate (&root_group, MDL))
+               log_fatal ("Can't allocate root group!");
+       root_group -> authoritative = 0;
+diff -u -r dhcp-3.1.3-orig/server/mac2ip.c dhcp-3.1.3/server/mac2ip.c
+--- dhcp-3.1.3-orig/server/mac2ip.c    2011-05-09 12:26:05.754138795 +0200
++++ dhcp-3.1.3/server/mac2ip.c 2011-05-09 12:04:06.586819275 +0200
+@@ -0,0 +1,87 @@
++/**
++ * This code calculates IPv4s based on Ethernet addresses
++ *
++ * Original author: Max Gelmroth
++ *
++ */
++
++#include <arpa/inet.h>
++#include <assert.h>
++#include <inttypes.h>
++#include <stdbool.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <time.h>
++
++
++/*******************************************************************************
++ *                          HASH ALGORITHM
++ 
*******************************************************************************/
++
++
++/* Jenkins one-at-a-time hash (see 
http://burtleburtle.net/bob/hash/doobs.html) */
++uint32_t jenkins_one_at_a_time_hash(const unsigned char *key, const size_t 
key_len)
++{
++    uint32_t hash = 0;
++    size_t   i;
++
++    for (i = 0; i < key_len; i++) {
++        hash += key[i];
++        hash += (hash << 10);
++        hash ^= (hash >> 6);
++    }
++    hash += (hash << 3);
++    hash ^= (hash >> 11);
++    hash += (hash << 15);
++    return hash;
++}
++
++/* hash mac random ip */
++void hash_mac_to_rnd_ip_OAAT(const unsigned char *mac, struct in_addr *ip)
++{
++    const uint8_t uint8_max_value = 255;
++    time_t        cur_time;
++    struct tm     timestruct;
++    uint32_t      hash;
++    unsigned char mac_duplicate[6];
++
++    memcpy(mac_duplicate, mac, sizeof(mac_duplicate));
++
++    /*
++     * use day of the year in the hashfunction to avoid
++     * that two MAC-addresses that are hashed to the same
++     * IP today causes a collision tomorrow, too.
++     */
++    cur_time = time(NULL);
++    gmtime_r(&cur_time, &timestruct);
++
++    mac_duplicate[3] = (mac_duplicate[3] + timestruct.tm_yday + 1) % 
(uint8_max_value + 1);
++
++    /* hash it! */
++    hash = jenkins_one_at_a_time_hash(mac_duplicate, sizeof(mac_duplicate));
++
++    /* store the hash value (making sure it ends up in network byte order) */
++    ip->s_addr = htonl(hash);
++}
++
++/* adjusts the ip to the given bounds */
++void adjust_ip_to_bounds(struct in_addr *ip, const struct in_addr 
*upper_bound, const struct in_addr *lower_bound)
++{
++    uint32_t lower_bounds_value;
++    uint64_t num_bounds_ips;
++    uint32_t old_ip_value, new_ip_value;
++
++    /* determine number of IPs that fit within bounds */
++    lower_bounds_value = ntohl(lower_bound->s_addr);
++    num_bounds_ips     = ntohl(upper_bound->s_addr) - lower_bounds_value + 1;
++
++    /* turn IP into host byte order value to enable the following calculation 
*/
++    old_ip_value = ntohl(ip->s_addr);
++
++    /* old_ip_value modulo "number of IPs within the bounds" is offset within 
the bounds */
++    new_ip_value = lower_bounds_value + (old_ip_value % num_bounds_ips);
++
++    /* ... and back into network byte order */
++    ip->s_addr = htonl(new_ip_value);
++}
+diff -u -r dhcp-3.1.3-orig/server/Makefile.dist dhcp-3.1.3/server/Makefile.dist
+--- dhcp-3.1.3-orig/server/Makefile.dist       2009-07-23 23:43:35.000000000 
+0200
++++ dhcp-3.1.3/server/Makefile.dist    2011-05-02 16:25:41.000000000 +0200
+@@ -25,9 +25,9 @@
+ CATMANPAGES = dhcpd.cat8 dhcpd.conf.cat5 dhcpd.leases.cat5
+ SEDMANPAGES = dhcpd.man8 dhcpd.conf.man5 dhcpd.leases.man5
+ SRCS   = dhcpd.c dhcp.c bootp.c confpars.c db.c class.c failover.c \
+-       omapi.c mdb.c stables.c salloc.c ddns.c dhcpleasequery.c
++       omapi.c mdb.c stables.c salloc.c ddns.c dhcpleasequery.c mac2ip.c
+ OBJS   = dhcpd.o dhcp.o bootp.o confpars.o db.o class.o failover.o \
+-       omapi.o mdb.o stables.o salloc.o ddns.o dhcpleasequery.o
++       omapi.o mdb.o stables.o salloc.o ddns.o dhcpleasequery.o mac2ip.o
+ PROG   = dhcpd
+ MAN    = dhcpd.8 dhcpd.conf.5 dhcpd.leases.5
+ 
+diff -u -r dhcp-3.1.3-orig/server/stables.c dhcp-3.1.3/server/stables.c
+--- dhcp-3.1.3-orig/server/stables.c   2009-07-23 23:43:35.000000000 +0200
++++ dhcp-3.1.3/server/stables.c        2011-05-09 14:21:50.479855933 +0200
+@@ -324,6 +324,26 @@
+       syslog_values
+ };
+ 
++struct enumeration_value hash_modes_values [] = {
++      { "default", 0 },
++      { "pisa", 1 },
++      { "pisa24h", 1 },
++      { "pisa1h", 2 },
++      { "pisa2h", 3 },
++      { "pisa3h", 4 },
++      { "pisa4h", 5 },
++      { "pisa6h", 6 },
++      { "pisa8h", 7 },
++      { "pisa12h", 8 },
++      { (char *)0, 0 }
++};
++
++struct enumeration hash_modes = {
++      (struct enumeration *)0,
++      "hash-modes",
++      hash_modes_values
++};
++
+ void initialize_server_option_spaces()
+ {
+       int i;
-- 
This is the pisa developer mailing list. Please also subscribe to the main pisa 
list at:
//www.freelists.org/list/pisa

Other related posts:

  • » [pisa-src] r2568 - in trunk/tools/dhcp: README.isc-dhcp-patches isc-dhcp-3.1.3-mobac.patch - Christoph Viethen