Commit febbd402 authored by David Hankins's avatar David Hankins

- The dhclient 'reject ...;' statement, which rejects leases given by named

  server-identifiers, now permits address ranges to be specified in CIDR
  notation. [ISC-Bugs #1435]
parent 6da113fb
......@@ -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
......
......@@ -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);
......
......@@ -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;
}
}
......
.\" $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
......
......@@ -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;
{
......
......@@ -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
......
......@@ -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);
......
......@@ -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;
};
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment