]> git.ipfire.org Git - thirdparty/dhcp.git/commitdiff
- The dhclient 'reject ...;' statement, which rejects leases given by named
authorDavid Hankins <dhankins@isc.org>
Mon, 15 May 2006 15:07:50 +0000 (15:07 +0000)
committerDavid Hankins <dhankins@isc.org>
Mon, 15 May 2006 15:07:50 +0000 (15:07 +0000)
  server-identifiers, now permits address ranges to be specified in CIDR
  notation. [ISC-Bugs #1435]

RELNOTES
client/clparse.c
client/dhclient.c
client/dhclient.conf.5
common/inet.c
common/parse.c
includes/dhcpd.h
includes/inet.h

index 1f58332840e7ffe9beeaba4191538a9dcc0dcc6f..a409cc6ddbe836705873ff85c74dc2fea2b220f4 100644 (file)
--- a/RELNOTES
+++ b/RELNOTES
@@ -81,6 +81,10 @@ and for prodding me into improving it.
   their arguments from upper to lower and lower to upper cases respectively.
   Thanks to a patch from Albert Herranz.
 
+- The dhclient 'reject ...;' statement, which rejects leases given by named
+  server-identifiers, now permits address ranges to be specified in CIDR
+  notation.
+
                        Changes since 3.0.4
 
 - A warning that host statements declared within subnet or shared-network
index dbd54ebed1b4751a84cea61a2056dde4c61eea5b..4af95e15c8aabea5a0da9eb40cafa288fc4d9e52 100644 (file)
@@ -34,7 +34,7 @@
 
 #ifndef lint
 static char copyright[] =
-"$Id: clparse.c,v 1.65 2006/02/24 23:16:27 dhankins Exp $ Copyright (c) 2004-2006 Internet Systems Consortium.  All rights reserved.\n";
+"$Id: clparse.c,v 1.66 2006/05/15 15:07:49 dhankins Exp $ Copyright (c) 2004-2006 Internet Systems Consortium.  All rights reserved.\n";
 #endif /* not lint */
 
 #include "dhcpd.h"
@@ -1124,24 +1124,49 @@ void parse_reject_statement (cfile, config)
 {
        int token;
        const char *val;
-       struct iaddr addr;
-       struct iaddrlist *list;
+       struct iaddrmatch match;
+       struct iaddrmatchlist *list;
+       int i;
 
        do {
-               if (!parse_ip_addr (cfile, &addr)) {
-                       parse_warn (cfile, "expecting IP address.");
+               if (!parse_ip_addr_with_subnet (cfile, &match)) {
+                       /* no warn: parser will have reported what's wrong */
                        skip_to_semi (cfile);
                        return;
                }
 
-               list = (struct iaddrlist *)dmalloc (sizeof (struct iaddrlist),
-                                                   MDL);
+               /* check mask is not all zeros (because that would
+                * reject EVERY address).  This check could be
+                * simplified if we assume that the mask *always*
+                * represents a prefix .. but perhaps it might be
+                * useful to have a mask which is not a proper prefix
+                * (perhaps for ipv6?).  The following is almost as
+                * efficient as inspection of match.mask.iabuf[0] when
+                * it IS a true prefix, and is more general when it is
+                * not.
+                */
+
+               for (i=0 ; i < match.mask.len ; i++) {
+                   if (match.mask.iabuf[i]) {
+                       break;
+                   }
+               }
+
+               if (i == match.mask.len) {
+                   /* oops we found all zeros */
+                   parse_warn(cfile, "zero-length prefix is not permitted "
+                                     "for reject statement");
+                   skip_to_semi(cfile);
+                   return;
+               } 
+
+               list = dmalloc(sizeof(struct iaddrmatchlist), MDL);
                if (!list)
                        log_fatal ("no memory for reject list!");
 
-               list -> addr = addr;
-               list -> next = config -> reject_list;
-               config -> reject_list = list;
+               list->match = match;
+               list->next = config->reject_list;
+               config->reject_list = list;
 
                token = next_token (&val, (unsigned *)0, cfile);
        } while (token == COMMA);
index 8485364f333668ed6c59b8495d35396cf0845a90..c58135a7f1b1a5ab7f42ff4d97a50d8dc93f3e1d 100644 (file)
@@ -32,7 +32,7 @@
 
 #ifndef lint
 static char ocopyright[] =
-"$Id: dhclient.c,v 1.137 2006/05/11 14:48:58 shane Exp $ Copyright (c) 2004-2006 Internet Systems Consortium.  All rights reserved.\n";
+"$Id: dhclient.c,v 1.138 2006/05/15 15:07:49 dhankins Exp $ Copyright (c) 2004-2006 Internet Systems Consortium.  All rights reserved.\n";
 #endif /* not lint */
 
 #include "dhcpd.h"
@@ -1004,7 +1004,9 @@ void db_startup (testp)
 void bootp (packet)
        struct packet *packet;
 {
-       struct iaddrlist *ap;
+       struct iaddrmatchlist *ap;
+       char addrbuf[4*16];
+       char maskbuf[4*16];
 
        if (packet -> raw -> op != BOOTREPLY)
                return;
@@ -1013,9 +1015,17 @@ void bootp (packet)
           on it. */
        for (ap = packet -> interface -> client -> config -> reject_list;
             ap; ap = ap -> next) {
-               if (addr_eq (packet -> client_addr, ap -> addr)) {
-                       log_info ("BOOTREPLY from %s rejected.",
-                             piaddr (ap -> addr));
+               if (addr_match(&packet->client_addr, &ap->match)) {
+
+                       /* piaddr() returns its result in a static
+                          buffer sized 4*16 (see common/inet.c). */
+
+                       strcpy(addrbuf, piaddr(ap->match.addr));
+                       strcpy(maskbuf, piaddr(ap->match.mask));
+
+                       log_info("BOOTREPLY from %s rejected by rule %s "
+                                "mask %s.", piaddr(packet->client_addr),
+                                addrbuf, maskbuf);
                        return;
                }
        }
@@ -1027,9 +1037,11 @@ void bootp (packet)
 void dhcp (packet)
        struct packet *packet;
 {
-       struct iaddrlist *ap;
+       struct iaddrmatchlist *ap;
        void (*handler) PROTO ((struct packet *));
        const char *type;
+       char addrbuf[4*16];
+       char maskbuf[4*16];
 
        switch (packet -> packet_type) {
              case DHCPOFFER:
@@ -1055,9 +1067,17 @@ void dhcp (packet)
           on it. */
        for (ap = packet -> interface -> client -> config -> reject_list;
             ap; ap = ap -> next) {
-               if (addr_eq (packet -> client_addr, ap -> addr)) {
-                       log_info ("%s from %s rejected.",
-                             type, piaddr (ap -> addr));
+               if (addr_match(&packet->client_addr, &ap->match)) {
+
+                       /* piaddr() returns its result in a static
+                          buffer sized 4*16 (see common/inet.c). */
+
+                       strcpy(addrbuf, piaddr(ap->match.addr));
+                       strcpy(maskbuf, piaddr(ap->match.mask));
+
+                       log_info("%s from %s rejected by rule %s mask %s.",
+                                type, piaddr(packet->client_addr),
+                                addrbuf, maskbuf);
                        return;
                }
        }
index 49a46b50bb715b4fcde2baf23ee342d0390e524f..73ec86cdf646e3603b5f5dcfad2d5d0ab95c3d69 100644 (file)
@@ -1,4 +1,4 @@
-.\"    $Id: dhclient.conf.5,v 1.16 2005/07/07 16:39:07 dhankins Exp $
+.\"    $Id: dhclient.conf.5,v 1.17 2006/05/15 15:07:49 dhankins Exp $
 .\"
 .\" Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
 .\" Copyright (c) 1996-2003 by Internet Software Consortium
@@ -28,7 +28,7 @@
 .\" see ``http://www.vix.com''.   To learn more about Nominum, Inc., see
 .\" ``http://www.nominum.com''.
 .\"
-.\" $Id: dhclient.conf.5,v 1.16 2005/07/07 16:39:07 dhankins Exp $
+.\" $Id: dhclient.conf.5,v 1.17 2006/05/15 15:07:49 dhankins Exp $
 .\"
 .TH dhclient.conf 5
 .SH NAME
@@ -504,15 +504,28 @@ declaration for the IP alias address, and a subnet-mask option
 declaration.   A medium statement should never be included in an alias
 declaration.
 .SH OTHER DECLARATIONS
- \fBreject \fIip-address\fB;\fR
+ \fBreject \fIcidr-ip-address\fR [\fB,\fR \fI...\fB \fIcidr-ip-address\fR ] \fB;\fR
 .PP
 The
 .B reject
 statement causes the DHCP client to reject offers from
-servers who use the specified address as a server identifier.   This
-can be used to avoid being configured by rogue or misconfigured dhcp
-servers, although it should be a last resort - better to track down
-the bad DHCP server and fix it.
+servers whose server identifier matches any of the specified hosts or
+subnets.  This can be used to avoid being configured by rogue or
+misconfigured dhcp servers, although it should be a last resort -
+better to track down the bad DHCP server and fix it.
+.PP
+The \fIcidr-ip-address\fR configuration type is of the
+form \fIip-address\fR[\fB/\fIprefixlen\fR], where \fIip-address\fR is a
+dotted quad IP address, and \fRprefixlen\fR is the CIDR prefix length of
+the subnet, counting the number of significant bits in the netmask starting
+from the leftmost end.  Example configuration syntax:
+.PP
+.I \fIreject\fR 192.168.0.0\fB/\fR16\fB,\fR 10.0.0.5\fB;\fR
+.RE
+.PP
+The above example would cause offers from any server identifier in the
+entire RFC 1918 "Class C" network 192.168.0.0/16, or the specific
+single address 10.0.0.5, to be rejected.
 .PP
  \fBinterface "\fIname\fB" { \fIdeclarations ... \fB }
 .PP
index e272bd21c156ece5c2d02baf65420571994b9c98..934eba19832a67422607368452af7609e4a7fb56 100644 (file)
@@ -35,7 +35,7 @@
 
 #ifndef lint
 static char copyright[] =
-"$Id: inet.c,v 1.10 2005/03/17 20:14:58 dhankins Exp $ Copyright (c) 2004-2005 Internet Systems Consortium.  All rights reserved.\n";
+"$Id: inet.c,v 1.11 2006/05/15 15:07:49 dhankins Exp $ Copyright (c) 2004-2005 Internet Systems Consortium.  All rights reserved.\n";
 #endif /* not lint */
 
 #include "dhcpd.h"
@@ -185,6 +185,31 @@ int addr_eq (addr1, addr2)
        return memcmp (addr1.iabuf, addr2.iabuf, addr1.len) == 0;
 }
 
+/* addr_match 
+ *
+ * compares an IP address against a network/mask combination
+ * by ANDing the IP with the mask and seeing whether the result
+ * matches the masked network value.
+ */
+int
+addr_match(addr, match)
+       struct iaddr *addr;
+       struct iaddrmatch *match;
+{
+        int i;
+
+       if (addr->len != match->addr.len)
+               return 0;
+       
+       i = 0;
+       for (i = 0 ; i < addr->len ; i++) {
+               if ((addr->iabuf[i] & match->mask.iabuf[i]) !=
+                                                       match->addr.iabuf[i])
+                       return 0;
+       }
+       return 1;
+}
+
 char *piaddr (addr)
        struct iaddr addr;
 {
index 8aefd5d6d6adbfbd36397e8ab1fed4a569286991..247e39b78cc0d11321b7578c0cb9c00c0bcadbd0 100644 (file)
@@ -34,7 +34,7 @@
 
 #ifndef lint
 static char copyright[] =
-"$Id: parse.c,v 1.108 2006/05/11 16:31:29 dhankins Exp $ Copyright (c) 2004-2006 Internet Systems Consortium.  All rights reserved.\n";
+"$Id: parse.c,v 1.109 2006/05/15 15:07:49 dhankins Exp $ Copyright (c) 2004-2006 Internet Systems Consortium.  All rights reserved.\n";
 #endif /* not lint */
 
 #include "dhcpd.h"
@@ -321,6 +321,98 @@ int parse_ip_addr (cfile, addr)
        return 0;
 }      
 
+/*
+ * ip-address-with-subnet :== ip-address |
+ *                          ip-address "/" NUMBER
+ */
+
+int
+parse_ip_addr_with_subnet(cfile, match)
+       struct parse *cfile;
+       struct iaddrmatch *match;
+{
+       const char *val, *orig;
+       enum dhcp_token token;
+       int prefixlen;
+       int fflen;
+       unsigned char newval, warnmask=0;
+
+       if (parse_ip_addr(cfile, &match->addr)) {
+               /* default to host mask */
+               prefixlen = match->addr.len * 8;
+
+               token = peek_token(&val, NULL, cfile);
+
+               if (token == SLASH) {
+                       next_token(&val, NULL, cfile);
+                       token = next_token(&val, NULL, cfile);
+
+                       if (token != NUMBER) {
+                               parse_warn(cfile, "Invalid CIDR prefix length:"
+                                                 " expecting a number.");
+                               return 0;
+                       }
+
+                       prefixlen = atoi(val);
+
+                       if (prefixlen < 0 ||
+                           prefixlen > (match->addr.len * 8)) {
+                               parse_warn(cfile, "subnet prefix is out of "
+                                                 "range [0..%d].",
+                                                 match->addr.len * 8);
+                               return 0;
+                       }
+               }
+
+               /* construct a suitable mask field */
+
+               /* copy length */
+               match->mask.len = match->addr.len;
+
+               /* count of 0xff bytes in mask */
+               fflen = prefixlen / 8;
+
+               /* set leading mask */
+               memset(match->mask.iabuf, 0xff, fflen);
+
+               /* set zeroes */
+               if (fflen < match->mask.len) {
+                       match->mask.iabuf[fflen] =
+                           "\x00\x80\xc0\xe0\xf0\xf8\xfc\xfe"[prefixlen % 8];
+
+                       memset(match->mask.iabuf+fflen+1, 0x00, 
+                              match->mask.len - fflen - 1);
+
+                       /* AND-out insignificant bits from supplied netmask. */
+                       orig = piaddr(match->addr);
+                       do {
+                               newval = match->addr.iabuf[fflen] &
+                                        match->mask.iabuf[fflen];
+
+                               if (newval != match->addr.iabuf[fflen]) {
+                                       warnmask = 1;
+                                       match->addr.iabuf[fflen] = newval;
+                               }
+                       } while (++fflen < match->mask.len);
+
+                       if (warnmask) {
+                               log_error("Warning: Extraneous bits removed "
+                                         "in address component of %s/%d.",
+                                         orig, prefixlen);
+                               log_error("New value: %s/%d.",
+                                         piaddr(match->addr), prefixlen);
+                       }
+               }
+
+               return 1;
+       }
+
+       parse_warn(cfile,
+                  "expecting ip-address or ip-address/prefixlen");
+
+       return 0;  /* let caller pick up pieces */ 
+}
+
 /*
  * hardware-parameter :== HARDWARE hardware-type colon-seperated-hex-list SEMI
  * hardware-type :== ETHERNET | TOKEN_RING | FDDI
index 55b9a7405053735e930f9529cdc06cf4e69a4458..9ee2fd9859f32dffc4b073ffcc99f0ad59e7eb1f 100644 (file)
@@ -744,7 +744,7 @@ struct client_config {
                                           authenticate. */
        struct string_list *medium;     /* Current network medium. */
 
-       struct iaddrlist *reject_list;  /* Servers to reject. */
+       struct iaddrmatchlist *reject_list;     /* Servers to reject. */
 
        int omapi_port;                 /* port on which to accept OMAPI
                                           connections, or -1 for no
@@ -1876,6 +1876,7 @@ struct iaddr ip_addr PROTO ((struct iaddr, struct iaddr, u_int32_t));
 struct iaddr broadcast_addr PROTO ((struct iaddr, struct iaddr));
 u_int32_t host_addr PROTO ((struct iaddr, struct iaddr));
 int addr_eq PROTO ((struct iaddr, struct iaddr));
+int addr_match(struct iaddr *, struct iaddrmatch *);
 char *piaddr PROTO ((struct iaddr));
 char *piaddrmask (struct iaddr, struct iaddr, const char *, int);
 
index d037d78be4ec7765b3b8fbe5a031375c2509fd12..ddc2d9ea7faf7f700277c3164c774238f45331b3 100644 (file)
@@ -43,3 +43,27 @@ struct iaddrlist {
        struct iaddrlist *next;
        struct iaddr addr;
 };
+
+
+/* struct iaddrmatch - used to compare a host IP against a subnet spec
+ *
+ * There is a space/speed tradeoff here implied by the use of a second
+ * struct iaddr to hold the mask; while using an unsigned (byte!) to
+ * represent the subnet prefix length would be more memory efficient,
+ * it makes run-time mask comparisons more expensive.  Since such
+ * entries are used currently only in restricted circumstances
+ * (wanting to reject a subnet), the decision is in favour of run-time
+ * efficiency.
+ */
+
+struct iaddrmatch {
+       struct iaddr addr;
+       struct iaddr mask;
+};
+
+/* its list ... */
+struct iaddrmatchlist {
+       struct iaddrmatchlist *next;
+       struct iaddrmatch match;
+};