2
0
mirror of https://gitlab.isc.org/isc-projects/dhcp synced 2025-08-22 18:07:25 +00:00

- 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]
This commit is contained in:
David Hankins 2006-05-15 15:07:50 +00:00
parent 6da113fb95
commit febbd40203
8 changed files with 233 additions and 29 deletions

View File

@ -81,6 +81,10 @@ and for prodding me into improving it.
their arguments from upper to lower and lower to upper cases respectively. their arguments from upper to lower and lower to upper cases respectively.
Thanks to a patch from Albert Herranz. 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 Changes since 3.0.4
- A warning that host statements declared within subnet or shared-network - A warning that host statements declared within subnet or shared-network

View File

@ -34,7 +34,7 @@
#ifndef lint #ifndef lint
static char copyright[] = 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 */ #endif /* not lint */
#include "dhcpd.h" #include "dhcpd.h"
@ -1124,24 +1124,49 @@ void parse_reject_statement (cfile, config)
{ {
int token; int token;
const char *val; const char *val;
struct iaddr addr; struct iaddrmatch match;
struct iaddrlist *list; struct iaddrmatchlist *list;
int i;
do { do {
if (!parse_ip_addr (cfile, &addr)) { if (!parse_ip_addr_with_subnet (cfile, &match)) {
parse_warn (cfile, "expecting IP address."); /* no warn: parser will have reported what's wrong */
skip_to_semi (cfile); skip_to_semi (cfile);
return; return;
} }
list = (struct iaddrlist *)dmalloc (sizeof (struct iaddrlist), /* check mask is not all zeros (because that would
MDL); * 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) if (!list)
log_fatal ("no memory for reject list!"); log_fatal ("no memory for reject list!");
list -> addr = addr; list->match = match;
list -> next = config -> reject_list; list->next = config->reject_list;
config -> reject_list = list; config->reject_list = list;
token = next_token (&val, (unsigned *)0, cfile); token = next_token (&val, (unsigned *)0, cfile);
} while (token == COMMA); } while (token == COMMA);

View File

@ -32,7 +32,7 @@
#ifndef lint #ifndef lint
static char ocopyright[] = 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 */ #endif /* not lint */
#include "dhcpd.h" #include "dhcpd.h"
@ -1004,7 +1004,9 @@ void db_startup (testp)
void bootp (packet) void bootp (packet)
struct packet *packet; struct packet *packet;
{ {
struct iaddrlist *ap; struct iaddrmatchlist *ap;
char addrbuf[4*16];
char maskbuf[4*16];
if (packet -> raw -> op != BOOTREPLY) if (packet -> raw -> op != BOOTREPLY)
return; return;
@ -1013,9 +1015,17 @@ void bootp (packet)
on it. */ on it. */
for (ap = packet -> interface -> client -> config -> reject_list; for (ap = packet -> interface -> client -> config -> reject_list;
ap; ap = ap -> next) { ap; ap = ap -> next) {
if (addr_eq (packet -> client_addr, ap -> addr)) { if (addr_match(&packet->client_addr, &ap->match)) {
log_info ("BOOTREPLY from %s rejected.",
piaddr (ap -> addr)); /* 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; return;
} }
} }
@ -1027,9 +1037,11 @@ void bootp (packet)
void dhcp (packet) void dhcp (packet)
struct packet *packet; struct packet *packet;
{ {
struct iaddrlist *ap; struct iaddrmatchlist *ap;
void (*handler) PROTO ((struct packet *)); void (*handler) PROTO ((struct packet *));
const char *type; const char *type;
char addrbuf[4*16];
char maskbuf[4*16];
switch (packet -> packet_type) { switch (packet -> packet_type) {
case DHCPOFFER: case DHCPOFFER:
@ -1055,9 +1067,17 @@ void dhcp (packet)
on it. */ on it. */
for (ap = packet -> interface -> client -> config -> reject_list; for (ap = packet -> interface -> client -> config -> reject_list;
ap; ap = ap -> next) { ap; ap = ap -> next) {
if (addr_eq (packet -> client_addr, ap -> addr)) { if (addr_match(&packet->client_addr, &ap->match)) {
log_info ("%s from %s rejected.",
type, piaddr (ap -> addr)); /* 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; return;
} }
} }

View 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) 2004 by Internet Systems Consortium, Inc. ("ISC")
.\" Copyright (c) 1996-2003 by Internet Software Consortium .\" Copyright (c) 1996-2003 by Internet Software Consortium
@ -28,7 +28,7 @@
.\" see ``http://www.vix.com''. To learn more about Nominum, Inc., see .\" see ``http://www.vix.com''. To learn more about Nominum, Inc., see
.\" ``http://www.nominum.com''. .\" ``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 .TH dhclient.conf 5
.SH NAME .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. A medium statement should never be included in an alias
declaration. declaration.
.SH OTHER DECLARATIONS .SH OTHER DECLARATIONS
\fBreject \fIip-address\fB;\fR \fBreject \fIcidr-ip-address\fR [\fB,\fR \fI...\fB \fIcidr-ip-address\fR ] \fB;\fR
.PP .PP
The The
.B reject .B reject
statement causes the DHCP client to reject offers from statement causes the DHCP client to reject offers from
servers who use the specified address as a server identifier. This servers whose server identifier matches any of the specified hosts or
can be used to avoid being configured by rogue or misconfigured dhcp subnets. This can be used to avoid being configured by rogue or
servers, although it should be a last resort - better to track down misconfigured dhcp servers, although it should be a last resort -
the bad DHCP server and fix it. 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 .PP
\fBinterface "\fIname\fB" { \fIdeclarations ... \fB } \fBinterface "\fIname\fB" { \fIdeclarations ... \fB }
.PP .PP

View File

@ -35,7 +35,7 @@
#ifndef lint #ifndef lint
static char copyright[] = 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 */ #endif /* not lint */
#include "dhcpd.h" #include "dhcpd.h"
@ -185,6 +185,31 @@ int addr_eq (addr1, addr2)
return memcmp (addr1.iabuf, addr2.iabuf, addr1.len) == 0; 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) char *piaddr (addr)
struct iaddr addr; struct iaddr addr;
{ {

View File

@ -34,7 +34,7 @@
#ifndef lint #ifndef lint
static char copyright[] = 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 */ #endif /* not lint */
#include "dhcpd.h" #include "dhcpd.h"
@ -321,6 +321,98 @@ int parse_ip_addr (cfile, addr)
return 0; 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-parameter :== HARDWARE hardware-type colon-seperated-hex-list SEMI
* hardware-type :== ETHERNET | TOKEN_RING | FDDI * hardware-type :== ETHERNET | TOKEN_RING | FDDI

View File

@ -744,7 +744,7 @@ struct client_config {
authenticate. */ authenticate. */
struct string_list *medium; /* Current network medium. */ 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 int omapi_port; /* port on which to accept OMAPI
connections, or -1 for no 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)); struct iaddr broadcast_addr PROTO ((struct iaddr, struct iaddr));
u_int32_t host_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_eq PROTO ((struct iaddr, struct iaddr));
int addr_match(struct iaddr *, struct iaddrmatch *);
char *piaddr PROTO ((struct iaddr)); char *piaddr PROTO ((struct iaddr));
char *piaddrmask (struct iaddr, struct iaddr, const char *, int); char *piaddrmask (struct iaddr, struct iaddr, const char *, int);

View File

@ -43,3 +43,27 @@ struct iaddrlist {
struct iaddrlist *next; struct iaddrlist *next;
struct iaddr addr; 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;
};