diff options
-rw-r--r-- | server/confpars.c | 217 | ||||
-rw-r--r-- | server/dhcp.c | 162 |
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; +} + |