]> git.ipfire.org Git - thirdparty/dhcp.git/commitdiff
Add pool/permit support.
authorTed Lemon <source@isc.org>
Mon, 9 Nov 1998 02:46:58 +0000 (02:46 +0000)
committerTed Lemon <source@isc.org>
Mon, 9 Nov 1998 02:46:58 +0000 (02:46 +0000)
server/confpars.c
server/dhcp.c

index acce3f461089fae2cf6276fa5d568d6d93986df5..29cbff103bb833c1134b3beb65e5a329c8531200 100644 (file)
@@ -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);
 }
 
index bd43c073f4780c767513a5b876baa6719bcba9ca..be9dc30f00e33c19002f9aa2e9967ddbb7c4db74 100644 (file)
@@ -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;
+}
+