diff options
author | Ted Lemon <source@isc.org> | 1996-02-21 12:11:09 +0000 |
---|---|---|
committer | Ted Lemon <source@isc.org> | 1996-02-21 12:11:09 +0000 |
commit | 97ca16995f70c396d4d69e742953cf80edf612cd (patch) | |
tree | f8ebd8a7fe4cc5a52e32b88bdd0a1aa4b0d8949b | |
parent | d9c6d82c49360ee7d451d7cfc20d90bf600f9f2c (diff) | |
download | isc-dhcp-97ca16995f70c396d4d69e742953cf80edf612cd.tar.gz |
Intermediate changes to support actual DHCP protocol engine
-rw-r--r-- | Makefile | 6 | ||||
-rw-r--r-- | client/dhclient.c | 50 | ||||
-rw-r--r-- | common/conflex.c | 12 | ||||
-rw-r--r-- | common/hash.c | 4 | ||||
-rw-r--r-- | common/inet.c | 8 | ||||
-rw-r--r-- | common/memory.c | 123 | ||||
-rw-r--r-- | common/options.c | 4 | ||||
-rw-r--r-- | common/print.c | 45 | ||||
-rw-r--r-- | common/socket.c | 3 | ||||
-rw-r--r-- | conflex.c | 12 | ||||
-rw-r--r-- | confpars.c | 16 | ||||
-rw-r--r-- | dhclient.c | 50 | ||||
-rw-r--r-- | dhcp.c | 200 | ||||
-rw-r--r-- | dhcpd.c | 52 | ||||
-rw-r--r-- | dhcpd.conf | 3 | ||||
-rw-r--r-- | dhcpd.h | 17 | ||||
-rw-r--r-- | dhctoken.h | 6 | ||||
-rw-r--r-- | hash.c | 4 | ||||
-rw-r--r-- | includes/dhcpd.h | 17 | ||||
-rw-r--r-- | includes/dhctoken.h | 6 | ||||
-rw-r--r-- | inet.c | 8 | ||||
-rw-r--r-- | memory.c | 123 | ||||
-rw-r--r-- | options.c | 4 | ||||
-rw-r--r-- | print.c | 45 | ||||
-rw-r--r-- | server/confpars.c | 16 | ||||
-rw-r--r-- | server/dhcp.c | 200 | ||||
-rw-r--r-- | server/dhcpd.c | 52 | ||||
-rw-r--r-- | server/dhcpd.conf | 3 | ||||
-rw-r--r-- | socket.c | 3 |
29 files changed, 972 insertions, 120 deletions
@@ -11,6 +11,8 @@ CFLAGS += -DDEBUG -g -Wall -Wstrict-prototypes -Wno-unused \ -Wno-uninitialized -Werror dhclient: dhclient.o confpars.o alloc.o memory.o options.o \ - hash.o tables.o inet.o convert.o conflex.o errwarn.o tree.o + hash.o tables.o inet.o convert.o conflex.o errwarn.o \ + tree.o print.o cc -o dhclient dhclient.o confpars.o alloc.o memory.o options.o \ - hash.o tables.o inet.o convert.o conflex.o errwarn.o tree.o
\ No newline at end of file + hash.o tables.o inet.o convert.o conflex.o errwarn.o \ + print.o tree.o
\ No newline at end of file diff --git a/client/dhclient.c b/client/dhclient.c index 0a66c41d..c9d9e64d 100644 --- a/client/dhclient.c +++ b/client/dhclient.c @@ -189,11 +189,51 @@ int main (argc, argv, envp) cons_options ((struct packet *)0, &outgoing, &decl, bufs); - memset (&raw.ciaddr, 0, sizeof raw.ciaddr); - memset (&raw.yiaddr, 0, sizeof raw.ciaddr); - memset (&raw.siaddr, 0, sizeof raw.ciaddr); - memset (&raw.giaddr, 0, sizeof raw.ciaddr); - + + if (decl.ciaddr) { + if (tree_evaluate (decl.ciaddr) != 4) + warn ("ciaddr is more" + " than one address"); + else + memcpy (&raw.ciaddr, + decl.ciaddr -> value, + decl.ciaddr -> len); + } else + memset (&raw.ciaddr, 0, sizeof raw.ciaddr); + + if (decl.yiaddr) { + if (tree_evaluate (decl.yiaddr) != 4) + warn ("yiaddr is more" + " than one address"); + else + memcpy (&raw.yiaddr, + decl.yiaddr -> value, + decl.yiaddr -> len); + } else + memset (&raw.yiaddr, 0, sizeof raw.yiaddr); + + if (decl.siaddr) { + if (tree_evaluate (decl.siaddr) != 4) + warn ("siaddr is more" + " than one address"); + else + memcpy (&raw.siaddr, + decl.siaddr -> value, + decl.siaddr -> len); + } else + memset (&raw.siaddr, 0, sizeof raw.siaddr); + + if (decl.giaddr) { + if (tree_evaluate (decl.giaddr) != 4) + warn ("giaddr is more" + " than one address"); + else + memcpy (&raw.giaddr, + decl.giaddr -> value, + decl.giaddr -> len); + } else + memset (&raw.giaddr, 0, sizeof raw.giaddr); + raw.xid = xid++; raw.xid = htons (raw.xid); raw.secs = 0; diff --git a/common/conflex.c b/common/conflex.c index 33e58318..28a9f65e 100644 --- a/common/conflex.c +++ b/common/conflex.c @@ -274,6 +274,8 @@ static int intern (atom, dfv) case 'c': if (!strcasecmp (atom + 1, "lass")) return CLASS; + if (!strcasecmp (atom + 1, "iaddr")) + return CIADDR; break; case 'e': if (!strcasecmp (atom + 1, "thernet")) @@ -287,6 +289,10 @@ static int intern (atom, dfv) if (!strcasecmp (atom + 1, "ixed-address")) return FIXED_ADDR; break; + case 'g': + if (!strcasecmp (atom + 1, "iaddr")) + return GIADDR; + break; case 'h': if (!strcasecmp (atom + 1, "ost")) return HOST; @@ -312,6 +318,8 @@ static int intern (atom, dfv) case 's': if (!strcasecmp (atom + 1, "tarts")) return STARTS; + if (!strcasecmp (atom + 1, "iaddr")) + return SIADDR; break; case 't': if (!strcasecmp (atom + 1, "timestamp")) @@ -321,6 +329,10 @@ static int intern (atom, dfv) if (!strcasecmp (atom + 1, "id")) return UID; break; + case 'y': + if (!strcasecmp (atom + 1, "iaddr")) + return YIADDR; + break; } return dfv; } diff --git a/common/hash.c b/common/hash.c index 8bc724e7..cf15ba31 100644 --- a/common/hash.c +++ b/common/hash.c @@ -103,6 +103,7 @@ void add_hash (table, name, len, pointer) bp -> name = name; bp -> value = pointer; bp -> next = table -> buckets [hashno]; + bp -> len = len; table -> buckets [hashno] = bp; } @@ -140,10 +141,11 @@ unsigned char *hash_lookup (table, name, len) struct hash_bucket *bp; if (len) { - for (bp = table -> buckets [hashno]; bp; bp = bp -> next) + for (bp = table -> buckets [hashno]; bp; bp = bp -> next) { if (len == bp -> len && !memcmp (bp -> name, name, len)) return bp -> value; + } } else { for (bp = table -> buckets [hashno]; bp; bp = bp -> next) if (!strcmp (bp -> name, name)) diff --git a/common/inet.c b/common/inet.c index fa25efc1..ce63a2b4 100644 --- a/common/inet.c +++ b/common/inet.c @@ -87,7 +87,7 @@ struct iaddr ip_addr (subnet, mask, host_address) j = rv.len - sizeof habuf; for (i = sizeof habuf - 1; i >= 0; i--) { if (mask.iabuf [i + j]) { - if (habuf [i] > ~mask.iabuf [i + j]) { + if (habuf [i] > (mask.iabuf [i + j] ^ 0xFF)) { rv.len = 0; return rv; } @@ -97,10 +97,10 @@ struct iaddr ip_addr (subnet, mask, host_address) return rv; } } - rv.iabuf [i + j] &= habuf [i]; + rv.iabuf [i + j] |= habuf [i]; break; - } - rv.iabuf [i + j] = habuf [i]; + } else + rv.iabuf [i + j] = habuf [i]; } return rv; diff --git a/common/memory.c b/common/memory.c index 21b1c010..13bf3492 100644 --- a/common/memory.c +++ b/common/memory.c @@ -48,7 +48,7 @@ static char copyright[] = #include "dhcpd.h" static struct host_decl *hosts; -static struct hash_table *subnet_hash; +static struct subnet *subnets; static struct hash_table *lease_uid_hash; static struct hash_table *lease_ip_addr_hash; static struct hash_table *lease_hw_addr_hash; @@ -98,12 +98,10 @@ void new_address_range (low, high, netmask) struct lease *address_range, *lp, *plp; struct subnet *subnet; struct iaddr net; - int i, max; + int min, max, i; char lowbuf [16], highbuf [16], netbuf [16]; /* Initialize the hash table if it hasn't been done yet. */ - if (!subnet_hash) - subnet_hash = new_hash (); if (!lease_uid_hash) lease_uid_hash = new_hash (); if (!lease_ip_addr_hash) @@ -130,32 +128,40 @@ void new_address_range (low, high, netmask) subnet -> net = net; subnet -> netmask = netmask; subnet -> leases = (struct lease *)0; + subnet -> last_lease = (struct lease *)0; + subnet -> next = (struct subnet *)0; + subnet -> default_lease_time = default_lease_time; + subnet -> max_lease_time = max_lease_time; enter_subnet (subnet); } /* Get the high and low host addresses... */ max = host_addr (high, netmask); - i = host_addr (low, netmask); + min = host_addr (low, netmask); /* Allow range to be specified high-to-low as well as low-to-high. */ - if (i > max) { - max = i; - i = host_addr (high, netmask); + if (min > max) { + max = min; + min = host_addr (high, netmask); } /* Get a lease structure for each address in the range. */ - address_range = new_leases (max - i + 1, "new_address_range"); + address_range = new_leases (max - min + 1, "new_address_range"); if (!address_range) { strcpy (lowbuf, piaddr (low)); strcpy (highbuf, piaddr (high)); error ("No memory for address range %s-%s.", lowbuf, highbuf); } - memset (address_range, 0, (sizeof *address_range) * (max - i + 1)); + memset (address_range, 0, (sizeof *address_range) * (max - min + 1)); + + /* Fill in the last lease if it hasn't been already... */ + if (!subnet -> last_lease) + subnet -> last_lease = &address_range [0]; /* Fill out the lease structures with some minimal information. */ - for (; i <= max; i++) { + for (i = 0; i < max - min + 1; i++) { address_range [i].ip_addr = - ip_addr (subnet -> net, subnet -> netmask, i); + ip_addr (subnet -> net, subnet -> netmask, i + min); address_range [i].starts = address_range [i].timestamp = MIN_TIME; address_range [i].ends = MIN_TIME; @@ -200,13 +206,16 @@ void new_address_range (low, high, netmask) } } -struct subnet *find_subnet (subnet) - struct iaddr subnet; +struct subnet *find_subnet (addr) + struct iaddr addr; { struct subnet *rv; - return (struct subnet *)hash_lookup (subnet_hash, - (char *)subnet.iabuf, subnet.len); + for (rv = subnets; rv; rv = rv -> next) { + if (addr_eq (subnet_number (addr, rv -> netmask), rv -> net)) + return rv; + } + return (struct subnet *)0; } /* Enter a new subnet into the subnet hash. */ @@ -214,8 +223,9 @@ struct subnet *find_subnet (subnet) void enter_subnet (subnet) struct subnet *subnet; { - add_hash (subnet_hash, (char *)subnet -> net.iabuf, - subnet -> net.len, (unsigned char *)subnet); + /* XXX Sort the nets into a balanced tree to make searching quicker. */ + subnet -> next = subnets; + subnets = subnet; } /* Enter a lease into the system. This is called by the parser each @@ -259,6 +269,11 @@ void supersede_lease (comp, lease) struct subnet *parent; struct lease *lp; +printf ("Supersede_lease:\n"); +print_lease (comp); +print_lease (lease); +printf ("\n"); + /* If the existing lease hasn't expired and has a different unique identifier or, if it doesn't have a unique identifier, a different hardware address, then the two @@ -290,7 +305,9 @@ void supersede_lease (comp, lease) enter_uid = 1; } free (comp -> uid); - } + } else + enter_uid = 1; + if (comp -> hardware_addr.htype && ((comp -> hardware_addr.hlen != lease -> hardware_addr.hlen) || @@ -303,7 +320,8 @@ void supersede_lease (comp, lease) comp -> hardware_addr.haddr, comp -> hardware_addr.hlen); enter_hwaddr = 1; - } + } else if (!comp -> hardware_addr.htype) + enter_hwaddr = 1; /* Copy the data files, but not the linkages. */ comp -> starts = lease -> starts; @@ -317,16 +335,16 @@ void supersede_lease (comp, lease) /* Record the lease in the uid hash if necessary. */ if (enter_uid && lease -> uid) { - add_hash (lease_uid_hash, lease -> uid, - lease -> uid_len, (unsigned char *)lease); + add_hash (lease_uid_hash, comp -> uid, + comp -> uid_len, (unsigned char *)comp); } /* Record it in the hardware address hash if necessary. */ if (enter_hwaddr && lease -> hardware_addr.htype) { add_hash (lease_hw_addr_hash, - lease -> hardware_addr.haddr, - lease -> hardware_addr.hlen, - (unsigned char *)lease); + comp -> hardware_addr.haddr, + comp -> hardware_addr.hlen, + (unsigned char *)comp); } /* Remove the lease from its current place in the list. */ @@ -338,6 +356,9 @@ void supersede_lease (comp, lease) if (comp -> next) { comp -> next -> prev = comp -> prev; } + if (comp -> contain -> last_lease == comp) { + comp -> contain -> last_lease = comp -> prev; + } /* Find the last insertion point... */ if (comp == comp -> contain -> insertion_point || @@ -351,18 +372,20 @@ void supersede_lease (comp, lease) /* Nothing on the list yet? Just make comp the head of the list. */ comp -> contain -> leases = comp; - } else if (lp -> ends <= comp -> ends) { + comp -> contain -> last_lease = comp; + } else if (lp -> ends > comp -> ends) { /* Skip down the list until we run out of list or find a place for comp. */ - while (lp -> next && lp -> ends < comp -> ends) { + while (lp -> next && lp -> ends > comp -> ends) { lp = lp -> next; } - if (lp -> ends < comp -> ends) { + if (lp -> ends > comp -> ends) { /* If we ran out of list, put comp at the end. */ lp -> next = comp; comp -> prev = lp; comp -> next = (struct lease *)0; + comp -> contain -> last_lease = comp; } else { /* If we didn't, put it between lp and the previous item on the list. */ @@ -372,12 +395,12 @@ void supersede_lease (comp, lease) lp -> prev = comp; } } else { - /* Skip ip the list until we run out of list + /* Skip up the list until we run out of list or find a place for comp. */ - while (lp -> prev && lp -> ends > comp -> ends) { + while (lp -> prev && lp -> ends < comp -> ends) { lp = lp -> prev; } - if (lp -> ends > comp -> ends) { + if (lp -> ends < comp -> ends) { /* If we ran out of list, put comp at the beginning. */ lp -> prev = comp; @@ -397,6 +420,18 @@ void supersede_lease (comp, lease) } } +/* Release the specified lease and re-hash it as appropriate. */ + +void release_lease (lease) + struct lease *lease; +{ + struct lease lt; + + lease -> ends = 0; + lt = *lease; + supersede_lease (lease, <); +} + /* Locate the lease associated with a given IP address... */ struct lease *find_lease_by_ip_addr (addr) @@ -417,3 +452,29 @@ struct lease *find_lease_by_uid (uid, len) return lease; } +struct lease *find_lease_by_hw_addr (hwaddr, hwlen) + unsigned char *hwaddr; + int hwlen; +{ + struct lease *lease = (struct lease *)hash_lookup (lease_hw_addr_hash, + hwaddr, hwlen); + return lease; +} + +void dump_subnets () +{ + struct lease *l; + struct subnet *s; + int i; + + for (s = subnets; s; s = s -> next) { + printf ("Subnet %s", piaddr (s -> net)); + printf (" netmask %s\n", + piaddr (s -> netmask)); + for (l = s -> leases; l; l = l -> next) { + print_lease (l); + } + printf ("Last Lease:\n"); + print_lease (s -> last_lease); + } +} diff --git a/common/options.c b/common/options.c index 75bfc5e5..f9fa44d3 100644 --- a/common/options.c +++ b/common/options.c @@ -94,8 +94,6 @@ void parse_option_buffer (packet, buffer, length) int len; int code; -printf ("parse_option_buffer (%x, %x, %d)\n", -(unsigned long)packet, (unsigned long)buffer, length); for (s = buffer; *s != DHO_END && s < end; ) { code = s [0]; /* Pad options don't have a length - just skip them. */ @@ -126,8 +124,6 @@ printf ("parse_option_buffer (%x, %x, %d)\n", t [len] = 0; packet -> options [code].len = len; packet -> options [code].data = t; -printf ("%s=%s\n", dhcp_options [code].name, -pretty_print_option (code, t, len)); } else { /* If it's a repeat, concatenate it to whatever we last saw. This is really only required diff --git a/common/print.c b/common/print.c index 55d17159..4686e2e3 100644 --- a/common/print.c +++ b/common/print.c @@ -55,13 +55,46 @@ char *print_hw_addr (htype, hlen, data) char *s; int i; - s = habuf; - for (i = 0; i < hlen; i++) { - sprintf (s, "%x", data [i]); - s += strlen (s); - *s++ = ':'; + if (htype == 0 || hlen == 0) { + strcpy (habuf, "<null>"); + } else { + s = habuf; + for (i = 0; i < hlen; i++) { + sprintf (s, "%x", data [i]); + s += strlen (s); + *s++ = ':'; + } + *--s = 0; } - *--s = 0; return habuf; } +void print_lease (lease) + struct lease *lease; +{ + struct tm *t; + char tbuf [32]; + + printf (" Lease %s", + piaddr (lease -> ip_addr)); + + t = gmtime (&lease -> starts); + strftime (tbuf, sizeof tbuf, "%D %H:%M:%S", t); + printf (" start %s", tbuf); + + t = gmtime (&lease -> ends); + strftime (tbuf, sizeof tbuf, "%D %H:%M:%S", t); + printf (" end %s", tbuf); + + t = gmtime (&lease -> timestamp); + strftime (tbuf, sizeof tbuf, "%D %H:%M:%S", t); + printf (" stamp %s\n", tbuf); + + printf (" hardware addr = %s", + print_hw_addr (lease -> hardware_addr.htype, + lease -> hardware_addr.hlen, + lease -> hardware_addr.haddr)); + printf (" host %s state %x\n", + lease -> host ? lease -> host -> name : "<none>", + lease -> state); +} diff --git a/common/socket.c b/common/socket.c index 3a013cf0..c69e68cc 100644 --- a/common/socket.c +++ b/common/socket.c @@ -184,6 +184,9 @@ void dispatch () /* Wait for a packet or a timeout... XXX */ count = select (max + 1, &r, &w, &x, (struct timeval *)0); + /* Get the current time... */ + GET_TIME (&cur_time); + /* Not likely to be transitory... */ if (count < 0) error ("select: %m"); @@ -274,6 +274,8 @@ static int intern (atom, dfv) case 'c': if (!strcasecmp (atom + 1, "lass")) return CLASS; + if (!strcasecmp (atom + 1, "iaddr")) + return CIADDR; break; case 'e': if (!strcasecmp (atom + 1, "thernet")) @@ -287,6 +289,10 @@ static int intern (atom, dfv) if (!strcasecmp (atom + 1, "ixed-address")) return FIXED_ADDR; break; + case 'g': + if (!strcasecmp (atom + 1, "iaddr")) + return GIADDR; + break; case 'h': if (!strcasecmp (atom + 1, "ost")) return HOST; @@ -312,6 +318,8 @@ static int intern (atom, dfv) case 's': if (!strcasecmp (atom + 1, "tarts")) return STARTS; + if (!strcasecmp (atom + 1, "iaddr")) + return SIADDR; break; case 't': if (!strcasecmp (atom + 1, "timestamp")) @@ -321,6 +329,10 @@ static int intern (atom, dfv) if (!strcasecmp (atom + 1, "id")) return UID; break; + case 'y': + if (!strcasecmp (atom + 1, "iaddr")) + return YIADDR; + break; } return dfv; } @@ -234,6 +234,22 @@ void parse_host_decl (cfile, bc, decl) case OPTION: parse_option_decl (cfile, bc, decl); break; + case CIADDR: + decl -> ciaddr = + tree_cache (parse_ip_addr_or_hostname (cfile, bc, 0)); + break; + case YIADDR: + decl -> yiaddr = + tree_cache (parse_ip_addr_or_hostname (cfile, bc, 0)); + break; + case SIADDR: + decl -> siaddr = + tree_cache (parse_ip_addr_or_hostname (cfile, bc, 0)); + break; + case GIADDR: + decl -> giaddr = + tree_cache (parse_ip_addr_or_hostname (cfile, bc, 0)); + break; default: parse_warn ("expecting a dhcp option declaration."); skip_to_semi (cfile); @@ -189,11 +189,51 @@ int main (argc, argv, envp) cons_options ((struct packet *)0, &outgoing, &decl, bufs); - memset (&raw.ciaddr, 0, sizeof raw.ciaddr); - memset (&raw.yiaddr, 0, sizeof raw.ciaddr); - memset (&raw.siaddr, 0, sizeof raw.ciaddr); - memset (&raw.giaddr, 0, sizeof raw.ciaddr); - + + if (decl.ciaddr) { + if (tree_evaluate (decl.ciaddr) != 4) + warn ("ciaddr is more" + " than one address"); + else + memcpy (&raw.ciaddr, + decl.ciaddr -> value, + decl.ciaddr -> len); + } else + memset (&raw.ciaddr, 0, sizeof raw.ciaddr); + + if (decl.yiaddr) { + if (tree_evaluate (decl.yiaddr) != 4) + warn ("yiaddr is more" + " than one address"); + else + memcpy (&raw.yiaddr, + decl.yiaddr -> value, + decl.yiaddr -> len); + } else + memset (&raw.yiaddr, 0, sizeof raw.yiaddr); + + if (decl.siaddr) { + if (tree_evaluate (decl.siaddr) != 4) + warn ("siaddr is more" + " than one address"); + else + memcpy (&raw.siaddr, + decl.siaddr -> value, + decl.siaddr -> len); + } else + memset (&raw.siaddr, 0, sizeof raw.siaddr); + + if (decl.giaddr) { + if (tree_evaluate (decl.giaddr) != 4) + warn ("giaddr is more" + " than one address"); + else + memcpy (&raw.giaddr, + decl.giaddr -> value, + decl.giaddr -> len); + } else + memset (&raw.giaddr, 0, sizeof raw.giaddr); + raw.xid = xid++; raw.xid = htons (raw.xid); raw.secs = 0; @@ -49,5 +49,205 @@ static char copyright[] = void dhcp (packet) struct packet *packet; { + struct lease *uid_lease, *ip_lease, *hw_lease, *lease; + struct iaddr cip; + struct lease lt; + TIME lease_time; + dump_packet (packet); + + /* Try to find a lease that's been assigned to the specified + unique client identifier. */ + if (packet -> options [DHO_DHCP_CLIENT_IDENTIFIER].len) + uid_lease = + find_lease_by_uid (packet -> options + [DHO_DHCP_CLIENT_IDENTIFIER].data, + packet -> options + [DHO_DHCP_CLIENT_IDENTIFIER].len); + else + uid_lease = (struct lease *)0; + + /* Try to find a lease that's been attached to the client's + hardware address... */ + hw_lease = find_lease_by_hw_addr (packet -> raw -> chaddr, + packet -> raw -> hlen); + + /* Try to find a lease that's been allocated to the client's + IP address. */ + if (packet -> options [DHO_DHCP_REQUESTED_ADDRESS].len && + packet -> options [DHO_DHCP_REQUESTED_ADDRESS].len + <= sizeof cip.iabuf) { + cip.len = packet -> options [DHO_DHCP_REQUESTED_ADDRESS].len; + memcpy (cip.iabuf, + packet -> options [DHO_DHCP_REQUESTED_ADDRESS].data, + packet -> options [DHO_DHCP_REQUESTED_ADDRESS].len); + memcpy (cip.iabuf, &packet -> raw -> ciaddr, 4); + ip_lease = find_lease_by_ip_addr (cip); + } else + ip_lease = (struct lease *)0; + + printf ("First blush:\n"); + if (ip_lease) { + printf ("ip_lease: "); + print_lease (ip_lease); + } + if (hw_lease) { + printf ("hw_lease: "); + print_lease (hw_lease); + } + if (uid_lease) { + printf ("uid_lease: "); + print_lease (uid_lease); + } + + /* Toss extra pointers to the same lease... */ + if (ip_lease == hw_lease) + ip_lease = (struct lease *)0; + if (hw_lease == uid_lease) + hw_lease = (struct lease *)0; + if (ip_lease == uid_lease) + ip_lease = (struct lease *)0; + + printf ("Second blush:\n"); + if (ip_lease) { + printf ("ip_lease: "); + print_lease (ip_lease); + } + if (hw_lease) { + printf ("hw_lease: "); + print_lease (hw_lease); + } + if (uid_lease) { + printf ("uid_lease: "); + print_lease (uid_lease); + } + + /* If we got an ip address lease, make sure it isn't assigned to + some *other* client! If it was assigned to this client, we'd + have zeroed it out above, so the only way we can take it at this + point is if some other client had it but it's timed out, or if no + other client has ever had it. */ + if (ip_lease && + ip_lease -> ends >= cur_time) + ip_lease = (struct lease *)0; + + /* Now eliminate leases that are on the wrong subnet... */ + if (ip_lease && packet -> subnet != ip_lease -> contain) { + release_lease (ip_lease); + ip_lease = (struct lease *)0; + } + if (uid_lease && packet -> subnet != uid_lease -> contain) { + release_lease (uid_lease); + uid_lease = (struct lease *)0; + } + if (hw_lease && packet -> subnet != hw_lease -> contain) { + release_lease (hw_lease); + hw_lease = (struct lease *)0; + } + + printf ("Third blush:\n"); + if (ip_lease) { + printf ("ip_lease: "); + print_lease (ip_lease); + } + if (hw_lease) { + printf ("hw_lease: "); + print_lease (hw_lease); + } + if (uid_lease) { + printf ("uid_lease: "); + print_lease (uid_lease); + } + + /* At this point, if ip_lease is nonzero, we can assign it to + this client. */ + lease = ip_lease; + + /* If we got a lease that matched the client identifier, we may want + to use it, but if we already have a lease we like, we must free + the lease that matched the client identifier. */ + if (uid_lease) { + if (lease) { + release_lease (uid_lease); + } else + lease = uid_lease; + } + + /* The lease that matched the hardware address is treated likewise. */ + if (hw_lease) { + if (lease) { + release_lease (hw_lease); + } else + lease = hw_lease; + } + + /* If we didn't find a lease, try to allocate one... */ + if (!lease) { + lease = packet -> subnet -> last_lease; + + /* If there are no leases in that subnet that have + expired, we have nothing to offer this client. */ + if (lease -> ends >= cur_time) { + note ("no free leases on subnet %s", + piaddr (packet -> subnet -> net)); + return; + } + lease -> host = (struct host_decl *)0; + } + + /* At this point, we have a lease that we can offer the client. + Now we construct a lease structure that contains what we want, + and call supersede_lease to do the right thing with it. */ + + memset (<, 0, sizeof lt); + + /* Use the ip address of the lease that we finally found in + the database. */ + lt.ip_addr = lease -> ip_addr; + + /* Start now. */ + lt.starts = cur_time; + + /* Figure out how long a lease to assign. */ + if (packet -> options [DHO_DHCP_LEASE_TIME].len == 4) { + lease_time = getULong (packet -> + options [DHO_DHCP_LEASE_TIME].data); + + /* Don't let the client ask for a longer lease than + is supported for this subnet. */ + if (lease_time > packet -> subnet -> max_lease_time) + lease_time = packet -> subnet -> max_lease_time; + } else + lease_time = packet -> subnet -> default_lease_time; + lt.ends = cur_time + lease_time; + + lt.timestamp = cur_time; + + /* Record the uid, if given... */ + if (packet -> options [DHO_DHCP_CLIENT_IDENTIFIER].len) { + lt.uid_len = + packet -> options [DHO_DHCP_CLIENT_IDENTIFIER].len; + lt.uid = packet -> options [DHO_DHCP_CLIENT_IDENTIFIER].data; + packet -> options [DHO_DHCP_CLIENT_IDENTIFIER].data = + (unsigned char *)0; + packet -> options [DHO_DHCP_CLIENT_IDENTIFIER].len = 0; + } + + /* Record the hardware address, if given... */ + lt.hardware_addr.hlen = packet -> raw -> hlen; + lt.hardware_addr.htype = packet -> raw -> htype; + memcpy (lt.hardware_addr.haddr, packet -> raw -> chaddr, + packet -> raw -> hlen); + + lt.host = lease -> host; /* XXX */ + lt.contain = lease -> contain; + + /* Record the transaction id... */ + lt.xid = packet -> raw -> xid; + + /* Install the new information about this lease in the database. */ + supersede_lease (lease, <); + + /* Send a response to the client... */ + dump_subnets (); } @@ -50,7 +50,10 @@ static char copyright[] = static void usage PROTO ((void)); TIME cur_time; +TIME default_lease_time = 43200; /* 12 hours... */ +TIME max_lease_time = 86400; /* 24 hours... */ +struct subnet *local_subnet; u_int32_t *server_addrlist; int server_addrcount; u_int16_t server_port; @@ -63,6 +66,7 @@ int main (argc, argv, envp) int port = 0; int i; struct sockaddr_in name; + struct iaddr taddr; u_int32_t *addrlist = (u_int32_t *)0; int addrcount = 0; struct tree *addrtree = (struct tree *)0; @@ -148,7 +152,23 @@ int main (argc, argv, envp) } #endif + taddr.len = 0; server_addrlist = get_interface_list (&server_addrcount); + for (i = 0; i < server_addrcount; i++) { + struct sockaddr_in foo; + foo.sin_addr.s_addr = server_addrlist [i]; + printf ("Address %d: %s\n", i, inet_ntoa (foo.sin_addr)); + + if (server_addrlist [i] != htonl (INADDR_LOOPBACK)) { + if (taddr.len) { + error ("dhcpd currently does not support " + "multiple interfaces"); + } + taddr.len = 4; + memcpy (taddr.iabuf, &server_addrlist [i], 4); + local_subnet = find_subnet (taddr); + } + } /* Listen on the specified (or default) port on each specified (or default) IP address. */ @@ -164,6 +184,8 @@ int main (argc, argv, envp) close (i); } + dump_subnets (); + /* Receive packets and dispatch them... */ dispatch (); @@ -191,6 +213,7 @@ void do_packet (packbuf, len, from_port, from, sock) { struct packet *tp; struct dhcp_packet *tdp; + struct iaddr ia; if (!(tp = new_packet ("do_packet"))) return; @@ -205,12 +228,29 @@ void do_packet (packbuf, len, from_port, from, sock) tp -> client_port = from_port; tp -> client_addr = from; tp -> client_sock = sock; - parse_options (tp); - if (tp -> options_valid && - tp -> options [DHO_DHCP_MESSAGE_TYPE].data) - dhcp (tp); - else - bootp (tp); + + /* If this came through a gateway, find the corresponding subnet... */ + if (tp -> raw -> giaddr.s_addr) { + ia.len = 4; + memcpy (ia.iabuf, &tp -> raw -> giaddr, 4); + tp -> subnet = find_subnet (ia); + } else { + tp -> subnet = local_subnet; + } + + /* If the subnet from whence this packet came is unknown to us, + drop it on the floor... */ + if (!tp -> subnet) + note ("Packet from unknown subnet: %s", + inet_ntoa (tp -> raw -> giaddr)); + else { + parse_options (tp); + if (tp -> options_valid && + tp -> options [DHO_DHCP_MESSAGE_TYPE].data) + dhcp (tp); + else + bootp (tp); + } } void dump_packet (tp) @@ -1,4 +1,5 @@ -range 204.254.239.11 204.254.239.254 255.255.255.0; +range 204.254.239.11 204.254.239.17 255.255.255.0; +range 204.254.240.11 204.254.240.12 255.255.255.0; host minuet hardware ethernet 08:00:2b:35:0c:18 @@ -53,6 +53,7 @@ #include <stdlib.h> #include <sys/stat.h> #include <ctype.h> +#include <time.h> #include "dhcp.h" #include "cdefs.h" @@ -70,6 +71,7 @@ struct packet { int client_port; struct iaddr client_addr; int client_sock; + struct subnet *subnet; struct { int len; unsigned char *data; @@ -91,6 +93,10 @@ struct host_decl { char *filename; char *server_name; struct tree_cache *fixed_addr; + struct tree_cache *ciaddr; + struct tree_cache *yiaddr; + struct tree_cache *siaddr; + struct tree_cache *giaddr; struct tree_cache *options [256]; }; @@ -106,13 +112,18 @@ struct lease { struct subnet *contain; struct hardware hardware_addr; int state; + int xid; }; struct subnet { + struct subnet *next; struct iaddr net; struct iaddr netmask; + TIME default_lease_time; + TIME max_lease_time; struct lease *leases; struct lease *insertion_point; + struct lease *last_lease; }; /* Bitmask of dhcp option codes. */ @@ -162,6 +173,8 @@ int parse_warn PROTO ((char *, ...)); /* dhcpd.c */ TIME cur_time; +TIME default_lease_time; +TIME max_lease_time; extern u_int32_t *server_addrlist; extern int server_addrcount; extern u_int16_t server_port; @@ -225,8 +238,11 @@ extern struct subnet *find_subnet (struct iaddr); void enter_subnet (struct subnet *); void enter_lease PROTO ((struct lease *)); void supersede_lease PROTO ((struct lease *, struct lease *)); +void release_lease PROTO ((struct lease *)); struct lease *find_lease_by_uid PROTO ((unsigned char *, int)); +struct lease *find_lease_by_hw_addr PROTO ((unsigned char *, int)); struct lease *find_lease_by_ip_addr PROTO ((struct iaddr)); +void dump_subnets PROTO ((void)); /* alloc.c */ VOIDPTR dmalloc PROTO ((int, char *)); @@ -251,6 +267,7 @@ void free_tree PROTO ((struct tree *, char *)); /* print.c */ char *print_hw_addr PROTO ((int, int, unsigned char *)); +void print_lease PROTO ((struct lease *)); /* socket.c */ u_int32_t *get_interface_list PROTO ((int *)); @@ -64,7 +64,11 @@ #define LEASE 271 #define RANGE 272 #define PACKET 273 -#define LAST_TOKEN PACKET +#define CIADDR 274 +#define YIADDR 275 +#define SIADDR 276 +#define GIADDR 277 +#define LAST_TOKEN GIADDR #define is_identifier(x) ((x) >= FIRST_TOKEN && \ (x) <= LAST_TOKEN && \ @@ -103,6 +103,7 @@ void add_hash (table, name, len, pointer) bp -> name = name; bp -> value = pointer; bp -> next = table -> buckets [hashno]; + bp -> len = len; table -> buckets [hashno] = bp; } @@ -140,10 +141,11 @@ unsigned char *hash_lookup (table, name, len) struct hash_bucket *bp; if (len) { - for (bp = table -> buckets [hashno]; bp; bp = bp -> next) + for (bp = table -> buckets [hashno]; bp; bp = bp -> next) { if (len == bp -> len && !memcmp (bp -> name, name, len)) return bp -> value; + } } else { for (bp = table -> buckets [hashno]; bp; bp = bp -> next) if (!strcmp (bp -> name, name)) diff --git a/includes/dhcpd.h b/includes/dhcpd.h index 655b57eb..81d5cccc 100644 --- a/includes/dhcpd.h +++ b/includes/dhcpd.h @@ -53,6 +53,7 @@ #include <stdlib.h> #include <sys/stat.h> #include <ctype.h> +#include <time.h> #include "dhcp.h" #include "cdefs.h" @@ -70,6 +71,7 @@ struct packet { int client_port; struct iaddr client_addr; int client_sock; + struct subnet *subnet; struct { int len; unsigned char *data; @@ -91,6 +93,10 @@ struct host_decl { char *filename; char *server_name; struct tree_cache *fixed_addr; + struct tree_cache *ciaddr; + struct tree_cache *yiaddr; + struct tree_cache *siaddr; + struct tree_cache *giaddr; struct tree_cache *options [256]; }; @@ -106,13 +112,18 @@ struct lease { struct subnet *contain; struct hardware hardware_addr; int state; + int xid; }; struct subnet { + struct subnet *next; struct iaddr net; struct iaddr netmask; + TIME default_lease_time; + TIME max_lease_time; struct lease *leases; struct lease *insertion_point; + struct lease *last_lease; }; /* Bitmask of dhcp option codes. */ @@ -162,6 +173,8 @@ int parse_warn PROTO ((char *, ...)); /* dhcpd.c */ TIME cur_time; +TIME default_lease_time; +TIME max_lease_time; extern u_int32_t *server_addrlist; extern int server_addrcount; extern u_int16_t server_port; @@ -225,8 +238,11 @@ extern struct subnet *find_subnet (struct iaddr); void enter_subnet (struct subnet *); void enter_lease PROTO ((struct lease *)); void supersede_lease PROTO ((struct lease *, struct lease *)); +void release_lease PROTO ((struct lease *)); struct lease *find_lease_by_uid PROTO ((unsigned char *, int)); +struct lease *find_lease_by_hw_addr PROTO ((unsigned char *, int)); struct lease *find_lease_by_ip_addr PROTO ((struct iaddr)); +void dump_subnets PROTO ((void)); /* alloc.c */ VOIDPTR dmalloc PROTO ((int, char *)); @@ -251,6 +267,7 @@ void free_tree PROTO ((struct tree *, char *)); /* print.c */ char *print_hw_addr PROTO ((int, int, unsigned char *)); +void print_lease PROTO ((struct lease *)); /* socket.c */ u_int32_t *get_interface_list PROTO ((int *)); diff --git a/includes/dhctoken.h b/includes/dhctoken.h index f04fae5b..e3132f99 100644 --- a/includes/dhctoken.h +++ b/includes/dhctoken.h @@ -64,7 +64,11 @@ #define LEASE 271 #define RANGE 272 #define PACKET 273 -#define LAST_TOKEN PACKET +#define CIADDR 274 +#define YIADDR 275 +#define SIADDR 276 +#define GIADDR 277 +#define LAST_TOKEN GIADDR #define is_identifier(x) ((x) >= FIRST_TOKEN && \ (x) <= LAST_TOKEN && \ @@ -87,7 +87,7 @@ struct iaddr ip_addr (subnet, mask, host_address) j = rv.len - sizeof habuf; for (i = sizeof habuf - 1; i >= 0; i--) { if (mask.iabuf [i + j]) { - if (habuf [i] > ~mask.iabuf [i + j]) { + if (habuf [i] > (mask.iabuf [i + j] ^ 0xFF)) { rv.len = 0; return rv; } @@ -97,10 +97,10 @@ struct iaddr ip_addr (subnet, mask, host_address) return rv; } } - rv.iabuf [i + j] &= habuf [i]; + rv.iabuf [i + j] |= habuf [i]; break; - } - rv.iabuf [i + j] = habuf [i]; + } else + rv.iabuf [i + j] = habuf [i]; } return rv; @@ -48,7 +48,7 @@ static char copyright[] = #include "dhcpd.h" static struct host_decl *hosts; -static struct hash_table *subnet_hash; +static struct subnet *subnets; static struct hash_table *lease_uid_hash; static struct hash_table *lease_ip_addr_hash; static struct hash_table *lease_hw_addr_hash; @@ -98,12 +98,10 @@ void new_address_range (low, high, netmask) struct lease *address_range, *lp, *plp; struct subnet *subnet; struct iaddr net; - int i, max; + int min, max, i; char lowbuf [16], highbuf [16], netbuf [16]; /* Initialize the hash table if it hasn't been done yet. */ - if (!subnet_hash) - subnet_hash = new_hash (); if (!lease_uid_hash) lease_uid_hash = new_hash (); if (!lease_ip_addr_hash) @@ -130,32 +128,40 @@ void new_address_range (low, high, netmask) subnet -> net = net; subnet -> netmask = netmask; subnet -> leases = (struct lease *)0; + subnet -> last_lease = (struct lease *)0; + subnet -> next = (struct subnet *)0; + subnet -> default_lease_time = default_lease_time; + subnet -> max_lease_time = max_lease_time; enter_subnet (subnet); } /* Get the high and low host addresses... */ max = host_addr (high, netmask); - i = host_addr (low, netmask); + min = host_addr (low, netmask); /* Allow range to be specified high-to-low as well as low-to-high. */ - if (i > max) { - max = i; - i = host_addr (high, netmask); + if (min > max) { + max = min; + min = host_addr (high, netmask); } /* Get a lease structure for each address in the range. */ - address_range = new_leases (max - i + 1, "new_address_range"); + address_range = new_leases (max - min + 1, "new_address_range"); if (!address_range) { strcpy (lowbuf, piaddr (low)); strcpy (highbuf, piaddr (high)); error ("No memory for address range %s-%s.", lowbuf, highbuf); } - memset (address_range, 0, (sizeof *address_range) * (max - i + 1)); + memset (address_range, 0, (sizeof *address_range) * (max - min + 1)); + + /* Fill in the last lease if it hasn't been already... */ + if (!subnet -> last_lease) + subnet -> last_lease = &address_range [0]; /* Fill out the lease structures with some minimal information. */ - for (; i <= max; i++) { + for (i = 0; i < max - min + 1; i++) { address_range [i].ip_addr = - ip_addr (subnet -> net, subnet -> netmask, i); + ip_addr (subnet -> net, subnet -> netmask, i + min); address_range [i].starts = address_range [i].timestamp = MIN_TIME; address_range [i].ends = MIN_TIME; @@ -200,13 +206,16 @@ void new_address_range (low, high, netmask) } } -struct subnet *find_subnet (subnet) - struct iaddr subnet; +struct subnet *find_subnet (addr) + struct iaddr addr; { struct subnet *rv; - return (struct subnet *)hash_lookup (subnet_hash, - (char *)subnet.iabuf, subnet.len); + for (rv = subnets; rv; rv = rv -> next) { + if (addr_eq (subnet_number (addr, rv -> netmask), rv -> net)) + return rv; + } + return (struct subnet *)0; } /* Enter a new subnet into the subnet hash. */ @@ -214,8 +223,9 @@ struct subnet *find_subnet (subnet) void enter_subnet (subnet) struct subnet *subnet; { - add_hash (subnet_hash, (char *)subnet -> net.iabuf, - subnet -> net.len, (unsigned char *)subnet); + /* XXX Sort the nets into a balanced tree to make searching quicker. */ + subnet -> next = subnets; + subnets = subnet; } /* Enter a lease into the system. This is called by the parser each @@ -259,6 +269,11 @@ void supersede_lease (comp, lease) struct subnet *parent; struct lease *lp; +printf ("Supersede_lease:\n"); +print_lease (comp); +print_lease (lease); +printf ("\n"); + /* If the existing lease hasn't expired and has a different unique identifier or, if it doesn't have a unique identifier, a different hardware address, then the two @@ -290,7 +305,9 @@ void supersede_lease (comp, lease) enter_uid = 1; } free (comp -> uid); - } + } else + enter_uid = 1; + if (comp -> hardware_addr.htype && ((comp -> hardware_addr.hlen != lease -> hardware_addr.hlen) || @@ -303,7 +320,8 @@ void supersede_lease (comp, lease) comp -> hardware_addr.haddr, comp -> hardware_addr.hlen); enter_hwaddr = 1; - } + } else if (!comp -> hardware_addr.htype) + enter_hwaddr = 1; /* Copy the data files, but not the linkages. */ comp -> starts = lease -> starts; @@ -317,16 +335,16 @@ void supersede_lease (comp, lease) /* Record the lease in the uid hash if necessary. */ if (enter_uid && lease -> uid) { - add_hash (lease_uid_hash, lease -> uid, - lease -> uid_len, (unsigned char *)lease); + add_hash (lease_uid_hash, comp -> uid, + comp -> uid_len, (unsigned char *)comp); } /* Record it in the hardware address hash if necessary. */ if (enter_hwaddr && lease -> hardware_addr.htype) { add_hash (lease_hw_addr_hash, - lease -> hardware_addr.haddr, - lease -> hardware_addr.hlen, - (unsigned char *)lease); + comp -> hardware_addr.haddr, + comp -> hardware_addr.hlen, + (unsigned char *)comp); } /* Remove the lease from its current place in the list. */ @@ -338,6 +356,9 @@ void supersede_lease (comp, lease) if (comp -> next) { comp -> next -> prev = comp -> prev; } + if (comp -> contain -> last_lease == comp) { + comp -> contain -> last_lease = comp -> prev; + } /* Find the last insertion point... */ if (comp == comp -> contain -> insertion_point || @@ -351,18 +372,20 @@ void supersede_lease (comp, lease) /* Nothing on the list yet? Just make comp the head of the list. */ comp -> contain -> leases = comp; - } else if (lp -> ends <= comp -> ends) { + comp -> contain -> last_lease = comp; + } else if (lp -> ends > comp -> ends) { /* Skip down the list until we run out of list or find a place for comp. */ - while (lp -> next && lp -> ends < comp -> ends) { + while (lp -> next && lp -> ends > comp -> ends) { lp = lp -> next; } - if (lp -> ends < comp -> ends) { + if (lp -> ends > comp -> ends) { /* If we ran out of list, put comp at the end. */ lp -> next = comp; comp -> prev = lp; comp -> next = (struct lease *)0; + comp -> contain -> last_lease = comp; } else { /* If we didn't, put it between lp and the previous item on the list. */ @@ -372,12 +395,12 @@ void supersede_lease (comp, lease) lp -> prev = comp; } } else { - /* Skip ip the list until we run out of list + /* Skip up the list until we run out of list or find a place for comp. */ - while (lp -> prev && lp -> ends > comp -> ends) { + while (lp -> prev && lp -> ends < comp -> ends) { lp = lp -> prev; } - if (lp -> ends > comp -> ends) { + if (lp -> ends < comp -> ends) { /* If we ran out of list, put comp at the beginning. */ lp -> prev = comp; @@ -397,6 +420,18 @@ void supersede_lease (comp, lease) } } +/* Release the specified lease and re-hash it as appropriate. */ + +void release_lease (lease) + struct lease *lease; +{ + struct lease lt; + + lease -> ends = 0; + lt = *lease; + supersede_lease (lease, <); +} + /* Locate the lease associated with a given IP address... */ struct lease *find_lease_by_ip_addr (addr) @@ -417,3 +452,29 @@ struct lease *find_lease_by_uid (uid, len) return lease; } +struct lease *find_lease_by_hw_addr (hwaddr, hwlen) + unsigned char *hwaddr; + int hwlen; +{ + struct lease *lease = (struct lease *)hash_lookup (lease_hw_addr_hash, + hwaddr, hwlen); + return lease; +} + +void dump_subnets () +{ + struct lease *l; + struct subnet *s; + int i; + + for (s = subnets; s; s = s -> next) { + printf ("Subnet %s", piaddr (s -> net)); + printf (" netmask %s\n", + piaddr (s -> netmask)); + for (l = s -> leases; l; l = l -> next) { + print_lease (l); + } + printf ("Last Lease:\n"); + print_lease (s -> last_lease); + } +} @@ -94,8 +94,6 @@ void parse_option_buffer (packet, buffer, length) int len; int code; -printf ("parse_option_buffer (%x, %x, %d)\n", -(unsigned long)packet, (unsigned long)buffer, length); for (s = buffer; *s != DHO_END && s < end; ) { code = s [0]; /* Pad options don't have a length - just skip them. */ @@ -126,8 +124,6 @@ printf ("parse_option_buffer (%x, %x, %d)\n", t [len] = 0; packet -> options [code].len = len; packet -> options [code].data = t; -printf ("%s=%s\n", dhcp_options [code].name, -pretty_print_option (code, t, len)); } else { /* If it's a repeat, concatenate it to whatever we last saw. This is really only required @@ -55,13 +55,46 @@ char *print_hw_addr (htype, hlen, data) char *s; int i; - s = habuf; - for (i = 0; i < hlen; i++) { - sprintf (s, "%x", data [i]); - s += strlen (s); - *s++ = ':'; + if (htype == 0 || hlen == 0) { + strcpy (habuf, "<null>"); + } else { + s = habuf; + for (i = 0; i < hlen; i++) { + sprintf (s, "%x", data [i]); + s += strlen (s); + *s++ = ':'; + } + *--s = 0; } - *--s = 0; return habuf; } +void print_lease (lease) + struct lease *lease; +{ + struct tm *t; + char tbuf [32]; + + printf (" Lease %s", + piaddr (lease -> ip_addr)); + + t = gmtime (&lease -> starts); + strftime (tbuf, sizeof tbuf, "%D %H:%M:%S", t); + printf (" start %s", tbuf); + + t = gmtime (&lease -> ends); + strftime (tbuf, sizeof tbuf, "%D %H:%M:%S", t); + printf (" end %s", tbuf); + + t = gmtime (&lease -> timestamp); + strftime (tbuf, sizeof tbuf, "%D %H:%M:%S", t); + printf (" stamp %s\n", tbuf); + + printf (" hardware addr = %s", + print_hw_addr (lease -> hardware_addr.htype, + lease -> hardware_addr.hlen, + lease -> hardware_addr.haddr)); + printf (" host %s state %x\n", + lease -> host ? lease -> host -> name : "<none>", + lease -> state); +} diff --git a/server/confpars.c b/server/confpars.c index 7335de6f..1be52bb9 100644 --- a/server/confpars.c +++ b/server/confpars.c @@ -234,6 +234,22 @@ void parse_host_decl (cfile, bc, decl) case OPTION: parse_option_decl (cfile, bc, decl); break; + case CIADDR: + decl -> ciaddr = + tree_cache (parse_ip_addr_or_hostname (cfile, bc, 0)); + break; + case YIADDR: + decl -> yiaddr = + tree_cache (parse_ip_addr_or_hostname (cfile, bc, 0)); + break; + case SIADDR: + decl -> siaddr = + tree_cache (parse_ip_addr_or_hostname (cfile, bc, 0)); + break; + case GIADDR: + decl -> giaddr = + tree_cache (parse_ip_addr_or_hostname (cfile, bc, 0)); + break; default: parse_warn ("expecting a dhcp option declaration."); skip_to_semi (cfile); diff --git a/server/dhcp.c b/server/dhcp.c index fa49eccc..640c726f 100644 --- a/server/dhcp.c +++ b/server/dhcp.c @@ -49,5 +49,205 @@ static char copyright[] = void dhcp (packet) struct packet *packet; { + struct lease *uid_lease, *ip_lease, *hw_lease, *lease; + struct iaddr cip; + struct lease lt; + TIME lease_time; + dump_packet (packet); + + /* Try to find a lease that's been assigned to the specified + unique client identifier. */ + if (packet -> options [DHO_DHCP_CLIENT_IDENTIFIER].len) + uid_lease = + find_lease_by_uid (packet -> options + [DHO_DHCP_CLIENT_IDENTIFIER].data, + packet -> options + [DHO_DHCP_CLIENT_IDENTIFIER].len); + else + uid_lease = (struct lease *)0; + + /* Try to find a lease that's been attached to the client's + hardware address... */ + hw_lease = find_lease_by_hw_addr (packet -> raw -> chaddr, + packet -> raw -> hlen); + + /* Try to find a lease that's been allocated to the client's + IP address. */ + if (packet -> options [DHO_DHCP_REQUESTED_ADDRESS].len && + packet -> options [DHO_DHCP_REQUESTED_ADDRESS].len + <= sizeof cip.iabuf) { + cip.len = packet -> options [DHO_DHCP_REQUESTED_ADDRESS].len; + memcpy (cip.iabuf, + packet -> options [DHO_DHCP_REQUESTED_ADDRESS].data, + packet -> options [DHO_DHCP_REQUESTED_ADDRESS].len); + memcpy (cip.iabuf, &packet -> raw -> ciaddr, 4); + ip_lease = find_lease_by_ip_addr (cip); + } else + ip_lease = (struct lease *)0; + + printf ("First blush:\n"); + if (ip_lease) { + printf ("ip_lease: "); + print_lease (ip_lease); + } + if (hw_lease) { + printf ("hw_lease: "); + print_lease (hw_lease); + } + if (uid_lease) { + printf ("uid_lease: "); + print_lease (uid_lease); + } + + /* Toss extra pointers to the same lease... */ + if (ip_lease == hw_lease) + ip_lease = (struct lease *)0; + if (hw_lease == uid_lease) + hw_lease = (struct lease *)0; + if (ip_lease == uid_lease) + ip_lease = (struct lease *)0; + + printf ("Second blush:\n"); + if (ip_lease) { + printf ("ip_lease: "); + print_lease (ip_lease); + } + if (hw_lease) { + printf ("hw_lease: "); + print_lease (hw_lease); + } + if (uid_lease) { + printf ("uid_lease: "); + print_lease (uid_lease); + } + + /* If we got an ip address lease, make sure it isn't assigned to + some *other* client! If it was assigned to this client, we'd + have zeroed it out above, so the only way we can take it at this + point is if some other client had it but it's timed out, or if no + other client has ever had it. */ + if (ip_lease && + ip_lease -> ends >= cur_time) + ip_lease = (struct lease *)0; + + /* Now eliminate leases that are on the wrong subnet... */ + if (ip_lease && packet -> subnet != ip_lease -> contain) { + release_lease (ip_lease); + ip_lease = (struct lease *)0; + } + if (uid_lease && packet -> subnet != uid_lease -> contain) { + release_lease (uid_lease); + uid_lease = (struct lease *)0; + } + if (hw_lease && packet -> subnet != hw_lease -> contain) { + release_lease (hw_lease); + hw_lease = (struct lease *)0; + } + + printf ("Third blush:\n"); + if (ip_lease) { + printf ("ip_lease: "); + print_lease (ip_lease); + } + if (hw_lease) { + printf ("hw_lease: "); + print_lease (hw_lease); + } + if (uid_lease) { + printf ("uid_lease: "); + print_lease (uid_lease); + } + + /* At this point, if ip_lease is nonzero, we can assign it to + this client. */ + lease = ip_lease; + + /* If we got a lease that matched the client identifier, we may want + to use it, but if we already have a lease we like, we must free + the lease that matched the client identifier. */ + if (uid_lease) { + if (lease) { + release_lease (uid_lease); + } else + lease = uid_lease; + } + + /* The lease that matched the hardware address is treated likewise. */ + if (hw_lease) { + if (lease) { + release_lease (hw_lease); + } else + lease = hw_lease; + } + + /* If we didn't find a lease, try to allocate one... */ + if (!lease) { + lease = packet -> subnet -> last_lease; + + /* If there are no leases in that subnet that have + expired, we have nothing to offer this client. */ + if (lease -> ends >= cur_time) { + note ("no free leases on subnet %s", + piaddr (packet -> subnet -> net)); + return; + } + lease -> host = (struct host_decl *)0; + } + + /* At this point, we have a lease that we can offer the client. + Now we construct a lease structure that contains what we want, + and call supersede_lease to do the right thing with it. */ + + memset (<, 0, sizeof lt); + + /* Use the ip address of the lease that we finally found in + the database. */ + lt.ip_addr = lease -> ip_addr; + + /* Start now. */ + lt.starts = cur_time; + + /* Figure out how long a lease to assign. */ + if (packet -> options [DHO_DHCP_LEASE_TIME].len == 4) { + lease_time = getULong (packet -> + options [DHO_DHCP_LEASE_TIME].data); + + /* Don't let the client ask for a longer lease than + is supported for this subnet. */ + if (lease_time > packet -> subnet -> max_lease_time) + lease_time = packet -> subnet -> max_lease_time; + } else + lease_time = packet -> subnet -> default_lease_time; + lt.ends = cur_time + lease_time; + + lt.timestamp = cur_time; + + /* Record the uid, if given... */ + if (packet -> options [DHO_DHCP_CLIENT_IDENTIFIER].len) { + lt.uid_len = + packet -> options [DHO_DHCP_CLIENT_IDENTIFIER].len; + lt.uid = packet -> options [DHO_DHCP_CLIENT_IDENTIFIER].data; + packet -> options [DHO_DHCP_CLIENT_IDENTIFIER].data = + (unsigned char *)0; + packet -> options [DHO_DHCP_CLIENT_IDENTIFIER].len = 0; + } + + /* Record the hardware address, if given... */ + lt.hardware_addr.hlen = packet -> raw -> hlen; + lt.hardware_addr.htype = packet -> raw -> htype; + memcpy (lt.hardware_addr.haddr, packet -> raw -> chaddr, + packet -> raw -> hlen); + + lt.host = lease -> host; /* XXX */ + lt.contain = lease -> contain; + + /* Record the transaction id... */ + lt.xid = packet -> raw -> xid; + + /* Install the new information about this lease in the database. */ + supersede_lease (lease, <); + + /* Send a response to the client... */ + dump_subnets (); } diff --git a/server/dhcpd.c b/server/dhcpd.c index b9e527c8..159d3b85 100644 --- a/server/dhcpd.c +++ b/server/dhcpd.c @@ -50,7 +50,10 @@ static char copyright[] = static void usage PROTO ((void)); TIME cur_time; +TIME default_lease_time = 43200; /* 12 hours... */ +TIME max_lease_time = 86400; /* 24 hours... */ +struct subnet *local_subnet; u_int32_t *server_addrlist; int server_addrcount; u_int16_t server_port; @@ -63,6 +66,7 @@ int main (argc, argv, envp) int port = 0; int i; struct sockaddr_in name; + struct iaddr taddr; u_int32_t *addrlist = (u_int32_t *)0; int addrcount = 0; struct tree *addrtree = (struct tree *)0; @@ -148,7 +152,23 @@ int main (argc, argv, envp) } #endif + taddr.len = 0; server_addrlist = get_interface_list (&server_addrcount); + for (i = 0; i < server_addrcount; i++) { + struct sockaddr_in foo; + foo.sin_addr.s_addr = server_addrlist [i]; + printf ("Address %d: %s\n", i, inet_ntoa (foo.sin_addr)); + + if (server_addrlist [i] != htonl (INADDR_LOOPBACK)) { + if (taddr.len) { + error ("dhcpd currently does not support " + "multiple interfaces"); + } + taddr.len = 4; + memcpy (taddr.iabuf, &server_addrlist [i], 4); + local_subnet = find_subnet (taddr); + } + } /* Listen on the specified (or default) port on each specified (or default) IP address. */ @@ -164,6 +184,8 @@ int main (argc, argv, envp) close (i); } + dump_subnets (); + /* Receive packets and dispatch them... */ dispatch (); @@ -191,6 +213,7 @@ void do_packet (packbuf, len, from_port, from, sock) { struct packet *tp; struct dhcp_packet *tdp; + struct iaddr ia; if (!(tp = new_packet ("do_packet"))) return; @@ -205,12 +228,29 @@ void do_packet (packbuf, len, from_port, from, sock) tp -> client_port = from_port; tp -> client_addr = from; tp -> client_sock = sock; - parse_options (tp); - if (tp -> options_valid && - tp -> options [DHO_DHCP_MESSAGE_TYPE].data) - dhcp (tp); - else - bootp (tp); + + /* If this came through a gateway, find the corresponding subnet... */ + if (tp -> raw -> giaddr.s_addr) { + ia.len = 4; + memcpy (ia.iabuf, &tp -> raw -> giaddr, 4); + tp -> subnet = find_subnet (ia); + } else { + tp -> subnet = local_subnet; + } + + /* If the subnet from whence this packet came is unknown to us, + drop it on the floor... */ + if (!tp -> subnet) + note ("Packet from unknown subnet: %s", + inet_ntoa (tp -> raw -> giaddr)); + else { + parse_options (tp); + if (tp -> options_valid && + tp -> options [DHO_DHCP_MESSAGE_TYPE].data) + dhcp (tp); + else + bootp (tp); + } } void dump_packet (tp) diff --git a/server/dhcpd.conf b/server/dhcpd.conf index 10d66a37..bc8a1d77 100644 --- a/server/dhcpd.conf +++ b/server/dhcpd.conf @@ -1,4 +1,5 @@ -range 204.254.239.11 204.254.239.254 255.255.255.0; +range 204.254.239.11 204.254.239.17 255.255.255.0; +range 204.254.240.11 204.254.240.12 255.255.255.0; host minuet hardware ethernet 08:00:2b:35:0c:18 @@ -184,6 +184,9 @@ void dispatch () /* Wait for a packet or a timeout... XXX */ count = select (max + 1, &r, &w, &x, (struct timeval *)0); + /* Get the current time... */ + GET_TIME (&cur_time); + /* Not likely to be transitory... */ if (count < 0) error ("select: %m"); |