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
#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"
{
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);
#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"
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;
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;
}
}
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:
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;
}
}
-.\" $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
.\" 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
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
#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"
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;
{
#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"
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
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
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);
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;
+};