summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--server/confpars.c217
-rw-r--r--server/dhcp.c162
2 files changed, 348 insertions, 31 deletions
diff --git a/server/confpars.c b/server/confpars.c
index acce3f46..29cbff10 100644
--- a/server/confpars.c
+++ b/server/confpars.c
@@ -42,7 +42,7 @@
#ifndef lint
static char copyright[] =
-"$Id: confpars.c,v 1.54 1998/11/06 02:58:17 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium. All rights reserved.\n";
+"$Id: confpars.c,v 1.55 1998/11/09 02:46:36 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium. All rights reserved.\n";
#endif /* not lint */
#include "dhcpd.h"
@@ -320,6 +320,14 @@ int parse_statement (cfile, group, type, host_decl, declaration)
}
break;
+ case POOL:
+ next_token (&val, cfile);
+ if (type != SUBNET_DECL && type != SHARED_NET_DECL) {
+ parse_warn ("pool declared outside of network");
+ }
+ parse_pool_statement (cfile, group, type);
+ return declaration;
+
case RANGE:
next_token (&val, cfile);
if (type != SUBNET_DECL || !group -> subnet) {
@@ -327,7 +335,7 @@ int parse_statement (cfile, group, type, host_decl, declaration)
skip_to_semi (cfile);
return declaration;
}
- parse_address_range (cfile, group -> subnet);
+ parse_address_range (cfile, group, type, (struct pool *)0);
return declaration;
case ALLOW:
@@ -420,6 +428,136 @@ int parse_statement (cfile, group, type, host_decl, declaration)
return 0;
}
+void parse_pool_statement (cfile, group, type)
+ FILE *cfile;
+ struct group *group;
+ int type;
+{
+ enum dhcp_token token;
+ char *val;
+ int done = 0;
+ struct pool *pool, **p;
+ struct permit *permit;
+ struct permit **permit_head;
+
+ pool = new_pool ("parse_pool_statement");
+ if (!pool)
+ error ("no memory for pool.");
+
+ if (!parse_lbrace (cfile))
+ return;
+ do {
+ switch (peek_token (&val, cfile)) {
+ case RANGE:
+ next_token (&val, cfile);
+ parse_address_range (cfile, group, type, pool);
+ break;
+ case ALLOW:
+ permit_head = &pool -> permit_list;
+ get_permit:
+ permit = new_permit ("parse_pool_statement");
+ if (!permit)
+ error ("no memory for permit");
+ next_token (&val, cfile);
+ token = next_token (&val, cfile);
+ switch (token) {
+ case UNKNOWN:
+ permit -> type = permit_unknown_clients;
+ get_clients:
+ if (next_token (&val, cfile) != CLIENTS) {
+ parse_warn ("expecting \"hosts\"");
+ skip_to_semi (cfile);
+ free_permit (permit,
+ "parse_pool_statement");
+ continue;
+ }
+ break;
+
+ case KNOWN:
+ permit -> type = permit_known_clients;
+ goto get_clients;
+
+ case AUTHENTICATED:
+ permit -> type = permit_authenticated_clients;
+ goto get_clients;
+
+ case UNAUTHENTICATED:
+ permit -> type =
+ permit_unauthenticated_clients;
+ goto get_clients;
+
+ case ALL:
+ permit -> type = permit_all_clients;
+ goto get_clients;
+ break;
+
+ case DYNAMIC:
+ permit -> type = permit_dynamic_bootp_clients;
+ if (next_token (&val, cfile) != BOOTP) {
+ parse_warn ("expecting \"bootp\"");
+ skip_to_semi (cfile);
+ free_permit (permit,
+ "parse_pool_statement");
+ continue;
+ }
+ goto get_clients;
+
+ case MEMBERS:
+ if (next_token (&val, cfile) != OF) {
+ parse_warn ("expecting \"of\"");
+ skip_to_semi (cfile);
+ free_permit (permit,
+ "parse_pool_statement");
+ continue;
+ }
+ if (next_token (&val, cfile) != STRING) {
+ parse_warn ("expecting class name.");
+ skip_to_semi (cfile);
+ free_permit (permit,
+ "parse_pool_statement");
+ continue;
+ }
+ permit -> type = permit_class;
+ permit -> class = find_class (val);
+ if (!permit -> class)
+ parse_warn ("no such class: %s", val);
+ default:
+ parse_warn ("expecting permit type.");
+ skip_to_semi (cfile);
+ break;
+ }
+ while (*permit_head)
+ permit_head = &((*permit_head) -> next);
+ *permit_head = permit;
+ break;
+
+ case DENY:
+ permit_head = &pool -> prohibit_list;
+ goto get_permit;
+
+ case RBRACE:
+ next_token (&val, cfile);
+ done = 1;
+ break;
+
+ default:
+ parse_warn ("expecting address range or permit list.");
+ skip_to_semi (cfile);
+ break;
+ }
+ } while (!done);
+
+ if (type == SUBNET_DECL)
+ pool -> shared_network = group -> subnet -> shared_network;
+ else
+ pool -> shared_network = group -> shared_network;
+
+ p = &pool -> shared_network -> pools;
+ for (; *p; p = &((*p) -> next))
+ ;
+ *p = pool;
+}
+
/* allow-deny-keyword :== BOOTP
| BOOTING
| DYNAMIC_BOOTP
@@ -807,9 +945,7 @@ void parse_shared_net_declaration (cfile, group)
share = new_shared_network ("parse_shared_net_declaration");
if (!share)
error ("No memory for shared subnet");
- share -> leases = (struct lease *)0;
- share -> last_lease = (struct lease *)0;
- share -> insertion_point = (struct lease *)0;
+ share -> pools = (struct pool *)0;
share -> next = (struct shared_network *)0;
share -> interface = (struct interface_info *)0;
share -> group = clone_group (group, "parse_shared_net_declaration");
@@ -1229,16 +1365,21 @@ struct lease *parse_lease_declaration (cfile)
/* address-range-declaration :== ip-address ip-address SEMI
| DYNAMIC_BOOTP ip-address ip-address SEMI */
-void parse_address_range (cfile, subnet)
+void parse_address_range (cfile, group, type, pool)
FILE *cfile;
- struct subnet *subnet;
+ struct group *group;
+ int type;
+ struct pool *pool;
{
- struct iaddr low, high;
+ struct iaddr low, high, net;
unsigned char addr [4];
int len = sizeof addr;
enum dhcp_token token;
char *val;
int dynamic = 0;
+ struct subnet *subnet;
+ struct shared_network *share;
+ struct pool *p;
if ((token = peek_token (&val, cfile)) == DYNAMIC_BOOTP) {
token = next_token (&val, cfile);
@@ -1270,7 +1411,65 @@ void parse_address_range (cfile, subnet)
return;
}
+ if (type == SUBNET_DECL) {
+ subnet = group -> subnet;
+ share = subnet -> shared_network;
+ } else {
+ share = group -> shared_network;
+ for (subnet = share -> subnets;
+ subnet; subnet = subnet -> next_sibling) {
+ net = subnet_number (low, subnet -> netmask);
+ if (addr_eq (low, subnet -> net))
+ break;
+ }
+ if (!subnet) {
+ parse_warn ("address range not on network %s",
+ group -> shared_network -> name);
+ return;
+ }
+ }
+
+ if (!pool) {
+ struct pool *last;
+ /* If we're permitting dynamic bootp for this range,
+ then look for a pool with an empty prohibit list and
+ a permit list with one entry which permits dynamic
+ bootp. */
+ for (pool = share -> pools; pool; pool = pool -> next) {
+ if ((!dynamic &&
+ !pool -> permit_list && !pool -> prohibit_list) ||
+ (dynamic &&
+ !pool -> prohibit_list &&
+ pool -> permit_list &&
+ !pool -> permit_list -> next &&
+ (pool -> permit_list -> type ==
+ permit_dynamic_bootp_clients))) {
+ break;
+ }
+ last = pool;
+ }
+
+ /* If we didn't get a pool, make one. */
+ if (!pool) {
+ pool = new_pool ("parse_address_range");
+ if (!pool)
+ error ("no memory for ad-hoc pool.");
+ if (dynamic) {
+ pool -> permit_list =
+ new_permit ("parse_address_range");
+ if (!pool -> permit_list)
+ error ("no memory for ad-hoc permit.");
+ pool -> permit_list -> type =
+ permit_dynamic_bootp_clients;
+ }
+ if (share -> pools)
+ last -> next = pool;
+ else
+ share -> pools = pool;
+ }
+ }
+
/* Create the new address range... */
- new_address_range (low, high, subnet, dynamic);
+ new_address_range (low, high, subnet, pool);
}
diff --git a/server/dhcp.c b/server/dhcp.c
index bd43c073..be9dc30f 100644
--- a/server/dhcp.c
+++ b/server/dhcp.c
@@ -42,7 +42,7 @@
#ifndef lint
static char copyright[] =
-"$Id: dhcp.c,v 1.70 1998/11/06 02:59:11 mellon Exp $ Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium. All rights reserved.\n";
+"$Id: dhcp.c,v 1.71 1998/11/09 02:46:58 mellon Exp $ Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium. All rights reserved.\n";
#endif /* not lint */
#include "dhcpd.h"
@@ -110,24 +110,16 @@ void dhcpdiscover (packet)
/* If we didn't find a lease, try to allocate one... */
if (!lease) {
- lease = packet -> shared_network -> last_lease;
-
- /* If there are no leases in that subnet that have
- expired, we have nothing to offer this client. */
- if (!lease || lease -> ends > cur_time) {
- note ("no free leases on subnet %s",
- packet -> shared_network -> name);
+ lease = allocate_lease (packet,
+ packet -> shared_network -> pools, 0);
+ if (!lease) {
+ note ("no free leases on network %s match %s",
+ packet -> shared_network -> name,
+ print_hw_addr (packet -> raw -> htype,
+ packet -> raw -> hlen,
+ packet -> raw -> chaddr));
return;
}
-
- /* If we find an abandoned lease, take it, but print a
- warning message, so that if it continues to lose,
- the administrator will eventually investigate. */
- if (lease -> flags & ABANDONED_LEASE) {
- warn ("Reclaiming abandoned IP address %s.",
- piaddr (lease -> ip_addr));
- lease -> flags &= ~ABANDONED_LEASE;
- }
}
ack_lease (packet, lease, DHCPOFFER, cur_time + 120);
@@ -596,7 +588,7 @@ void ack_lease (packet, lease, offer, when)
(struct group *)0);
/* Vendor and user classes are only supported for DHCP clients. */
- if (state -> offer) {
+ if (offer) {
for (i = packet -> class_count; i > 0; i--) {
execute_statements_in_scope
(packet, &state -> options,
@@ -1376,6 +1368,8 @@ struct lease *find_lease (packet, share, ours)
hp = find_hosts_by_uid (client_identifier.data,
client_identifier.len);
if (hp) {
+ /* Remember if we know of this client. */
+ packet -> known = 1;
fixed_lease = mockup_lease (packet, share, hp);
} else
fixed_lease = (struct lease *)0;
@@ -1422,6 +1416,8 @@ struct lease *find_lease (packet, share, ours)
packet -> raw -> chaddr,
packet -> raw -> hlen);
if (hp) {
+ /* Remember if we know of this client. */
+ packet -> known = 1;
host = hp; /* Save it for later. */
fixed_lease = mockup_lease (packet, share, hp);
#if defined (DEBUG_FIND_LEASE)
@@ -1443,10 +1439,13 @@ struct lease *find_lease (packet, share, ours)
if (hw_lease -> shared_network == share) {
if (hw_lease -> flags & ABANDONED_LEASE)
continue;
- if (packet -> packet_type)
- break;
- if (hw_lease -> flags &
- (BOOTP_LEASE | DYNAMIC_BOOTP_OK))
+ /* If we're allowed to use this lease, do so. */
+ if (!((lease -> pool -> prohibit_list &&
+ permitted (packet,
+ lease -> pool -> prohibit_list)) ||
+ (lease -> pool -> permit_list &&
+ !permitted (packet,
+ lease -> pool -> permit_list))))
break;
}
}
@@ -1743,3 +1742,122 @@ struct lease *mockup_lease (packet, share, hp)
mock.flags = STATIC_LEASE;
return &mock;
}
+
+/* Look through all the pools in a list starting with the specified pool
+ for a free lease. We try to find a virgin lease if we can. If we
+ don't find a virgin lease, we try to find a non-virgin lease that's
+ free. If we can't find one of those, we try to reclaim an abandoned
+ lease. If all of these possibilities fail to pan out, we don't return
+ a lease at all. */
+
+struct lease *allocate_lease (packet, pool, ok)
+ struct packet *packet;
+ struct pool *pool;
+ int ok;
+{
+ struct lease *lease, *lp;
+ struct permit *permit;
+
+ if (!pool)
+ return (struct lease *)0;
+
+ /* If we aren't elegible to try this pool, try a subsequent one. */
+ if ((pool -> prohibit_list &&
+ permitted (packet, pool -> prohibit_list)) ||
+ (pool -> permit_list && !permitted (packet, pool -> permit_list)))
+ return allocate_lease (packet, pool -> next, ok);
+
+ lease = pool -> last_lease;
+
+ /* If there are no leases in the pool that have
+ expired, try the next one. */
+ if (!lease || lease -> ends > cur_time)
+ return allocate_lease (packet, pool -> next, ok);
+
+ /* If we find an abandoned lease, and no other lease qualifies
+ better, take it. */
+ if (lease -> flags & ABANDONED_LEASE) {
+ /* If we already have a non-abandoned lease that we didn't
+ love, but that's okay, don't reclaim the abandoned lease. */
+ if (ok)
+ return allocate_lease (packet, pool -> next, ok);
+ lp = allocate_lease (packet, pool -> next, 0);
+ if (!lp) {
+ warn ("Reclaiming abandoned IP address %s.",
+ piaddr (lease -> ip_addr));
+ lease -> flags &= ~ABANDONED_LEASE;
+ return lease;
+ }
+ return lp;
+ }
+
+ /* If there's a lease we could take, but it had previously been
+ allocated to a different client, try for a virgin lease before
+ stealing it. */
+ if (lease -> uid_len || lease -> hardware_addr.hlen) {
+ /* If we're already in that boat, no need to consider
+ allocating this particular lease. */
+ if (ok)
+ return allocate_lease (packet, pool -> next, ok);
+
+ lp = allocate_lease (packet, pool -> next, 1);
+ if (lp)
+ return lp;
+ return lease;
+ }
+
+ return lease;
+}
+
+/* Determine whether or not a permit exists on a particular permit list
+ that matches the specified packet, returning nonzero if so, zero if
+ not. */
+
+int permitted (packet, permit_list)
+ struct packet *packet;
+ struct permit *permit_list;
+{
+ struct permit *p;
+ int i;
+
+ for (p = permit_list; p; p = p -> next) {
+ switch (p -> type) {
+ case permit_unknown_clients:
+ if (!packet -> known)
+ return 1;
+ break;
+
+ case permit_known_clients:
+ if (packet -> known)
+ return 1;
+ break;
+
+ case permit_authenticated_clients:
+ if (packet -> authenticated)
+ return 1;
+ break;
+
+ case permit_unauthenticated_clients:
+ if (!packet -> authenticated)
+ return 1;
+ break;
+
+ case permit_all_clients:
+ return 1;
+
+ case permit_dynamic_bootp_clients:
+ if (!packet -> options_valid ||
+ !packet -> packet_type)
+ return 1;
+ break;
+
+ case permit_class:
+ for (i = 0; i < packet -> class_count; i++)
+ if (p -> class == packet -> classes [i])
+ return 1;
+ break;
+ }
+ }
+ return 0;
+}
+