summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Kelley <simon@thekelleys.org.uk>2004-04-03 21:10:00 +0100
committerSimon Kelley <simon@thekelleys.org.uk>2012-01-05 17:31:10 +0000
commit33820b7ed94cee97017eba9ec695951944c96a4d (patch)
treeceecea1557af5a432dfbe2981478c44e48fa5c87
parent8a911ccc75ceeb7229b4bf9619b36652e01ef3d9 (diff)
downloaddnsmasq-33820b7ed94cee97017eba9ec695951944c96a4d.tar.gz
import of dnsmasq-2.6.tar.gzv2.6
-rw-r--r--CHANGELOG64
-rw-r--r--FAQ35
-rw-r--r--UPGRADING_to_2.015
-rw-r--r--dnsmasq-mdk.spec130
-rw-r--r--dnsmasq-rh.spec5
-rw-r--r--dnsmasq-suse.spec2
-rw-r--r--dnsmasq.844
-rw-r--r--dnsmasq.conf.example18
-rw-r--r--src/Makefile4
-rw-r--r--src/config.h19
-rw-r--r--src/dhcp.c143
-rw-r--r--src/dnsmasq.c37
-rw-r--r--src/dnsmasq.h15
-rw-r--r--src/isc.c249
-rw-r--r--src/option.c288
-rw-r--r--src/rfc2131.c152
16 files changed, 860 insertions, 360 deletions
diff --git a/CHANGELOG b/CHANGELOG
index bebe3f3..0a9caa7 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -860,4 +860,68 @@ release 2.5
interface a packet was sent to. Thanks to Javier Kohen
for the bug report.
+release 2.6
+ Support Token Ring DHCP. Thanks to Dag Wieers for help
+ testing. Note that Token ring support only works on Linux
+ currently.
+
+ Fix compilation on MacOS X. Thanks to Bernhard Ehlers for
+ the patch.
+
+ Added new "ignore" keyword for
+ dhcp-host. "dhcp-host=11:22:33:44:55:66,ignore" will
+ cause the DHCP server to ignore any host with the given
+ MAC address, leaving it to other servers on the
+ network. This also works with client-id and hostnames.
+ Suggestion by Alex Melt.
+
+ Fixed parsing of hex client IDs. Problem spotted by Peter
+ Fichtner.
+
+ Allow conf-file options in configuration file, to
+ provide an include function.
+
+ Re-read /etc/ethers on receipt of SIGHUP.
+
+ Added back the ability to read ISC dhcpd lease files, by
+ popular demand. Note that this is deprecated and for
+ backwards compatibility only. You can get back the 4K of
+ memory that the code occupies by undefining
+ "HAVE_ISC_READER" in src/config.h
+
+ Added ability to disable "pool" DHCP address allocation
+ whilst leaving static leases working. The syntax is
+ "dhcp-range=192.168.0.0,static"
+ Thanks to Grzegorz Nosek for the suggestion.
+
+ Generalized dnsmasq-rh.spec file to work on Mandrake too,
+ and removed dnsmasq-mdk.spec. Thanks to Doug Keller.
+
+ Allow DHCP options which are tied to specific static
+ leases in the same way as to specific networks.
+
+ Generalised the dhcp-option parser a bit to allow hex
+ strings as parameters. This is now legal:
+ dhcp-option=128,e4:45:74:68:00:00
+ Inspired by a patch from Joel Nordell.
+
+ Changed the semantics of argument-less dhcp-options for
+ the default-setting ones, ie 1, 3, 6 and 28. Now, doing
+ eg, dhcp-option=3 stops dnsmasq from sending a default
+ router option at all. Thanks to Scott Emmons for pointing
+ out that this is useful.
+
+ Fixed dnsmasq.conf parsing bug which interpreted port
+ numbers in server= lines as a comment. To start a
+ comment, a '#' character must now be a the start of a
+ line or preceded by whitespace. Thanks to Christian
+ Haggstrom for the bug report.
+
+
+
+
+
+
+
+
diff --git a/FAQ b/FAQ
index cbf7a66..8349bfc 100644
--- a/FAQ
+++ b/FAQ
@@ -181,7 +181,42 @@ A: There are a couple of configuration gotchas which have been
and from ports 67 and 68 and broadcast packets with source
address 0.0.0.0 and destination address 255.255.255.255 are not
dropped by iptables/ipchains.
+
+Q: I'm running Debian, and my machines get an address fine with DHCP,
+ but their names are not appearing in the DNS.
+
+A: By default, none of the DHCP clients send the host-name when asking
+ for a lease. For most of the clients, you can set the host-name to
+ send with the "hostname" keyword in /etc/network/interfaces. (See
+ "man interfaces" for details.) That doesn't work for dhclient, were
+ you have to add something like "send host-name daisy" to
+ /etc/dhclient.conf
+
+Q: I'm network booting my machines, and trying to give them static
+ DHCP-assigned addresses. The machine gets its correct address
+ whilst booting, but then the OS starts and it seems to get
+ allocated a different address.
+
+A: What is happening is this: The boot process sends a DHCP
+ request and gets allocated the static address corresponding to its
+ MAC address. The boot loader does not send a client-id. Then the OS
+ starts and repeats the DHCP process, but it it does send a
+ client-id. Dnsmasq cannot assume that the two requests are from the
+ same machine (since the client ID's don't match) and even though
+ the MAC address has a static allocation, that address is still in
+ use by the first incarnation of the machine (the one from the boot,
+ without a client ID.) dnsmasq therefore has to give the machine a
+ dynamic address from its pool. There are two ways to solve this:
+ (1) persuade your DHCP client not to send a client ID, or (2) set up
+ the static assignment to the client ID, not the MAC address. The
+ default client-id will be 01:<MAC address>, so change the dhcp-host
+ line from "dhcp-host=11:22:33:44:55:66,1.2.3.4" to
+ "dhcp-host=id:01:11:22:33:44:55:66,1.2.3.4"
+
+Q: What network types are supported by the DHCP server?
+A: Ethernet (and 802.11 wireless) are supported on all platforms. On
+ Linux Token Ring is also supported.
diff --git a/UPGRADING_to_2.0 b/UPGRADING_to_2.0
index e1204fa..218d5d3 100644
--- a/UPGRADING_to_2.0
+++ b/UPGRADING_to_2.0
@@ -7,9 +7,11 @@ Version 1.x of dnsmasq includes a facility for reading the dhcp.leases
file written by ISC dhcpd. This allows the names of machines which
have addresses allocated by DHCP to be included in the DNS.
-Version 2.x of dnsmasq removes the ISC dhcpd integration and replaces
-it with a DHCP server integrated into dnsmasq. This is an incompatible
-change in dnsmasq but it has the following advantages:
+Version 2.x of dnsmasq replaces the ISC dhcpd integration with a DHCP
+server integrated into dnsmasq. Versions 2.0-2.5 removed the ISC
+integration completely, but in version 2.6 it was re-enabled for
+backwards compatibility purposes. The change to an integrated DHCP
+server has the following advantages:
* Small. ISC dhcpd is a large and comprehensive DHCP solution. The
dnsmasq DHCP server adds about 15k to DNS-only dnsmasq and provides
@@ -29,7 +31,6 @@ change in dnsmasq but it has the following advantages:
with the dnsmasq DHCP server.
-
DHCP configuration
------------------
@@ -55,9 +56,9 @@ in the same way as before.
Having started dnsmasq, tell any hosts on the network to renew their
DHCP lease, so that dnsmasq's DHCP server becomes aware of them. For
Linux, this is best done by killing-and-restarting the DHCP client
-daemon or taking the network interface down and then back up. For
-Windows use winipcfg.exe
-
+daemon or taking the network interface down and then back up. For
+Windows 9x/Me, use the graphical tool "winipcfg". For Windows
+NT/2000/XP, use the command-line "ipconfig /renew"
For more complex DHCP configuration, refer to the doc/setup.html, the
dnsmasq manpage and the annotated example configuration file. Also
diff --git a/dnsmasq-mdk.spec b/dnsmasq-mdk.spec
deleted file mode 100644
index 89a247a..0000000
--- a/dnsmasq-mdk.spec
+++ /dev/null
@@ -1,130 +0,0 @@
-###############################################################################
-#
-# General mumbojumbo
-#
-###############################################################################
-
-Name: dnsmasq
-Version: 2.5
-Release: 1
-Copyright: GPL
-Group: System Environment/Daemons
-Vendor: Simon Kelley
-Packager: Simon Kelley
-Distribution: Mandrake Linux
-URL: http://www.thekelleys.org.uk/dnsmasq
-Source0: %{name}-%{version}.tar.gz
-Requires: chkconfig
-BuildRoot: /var/tmp/%{name}-%{version}
-Summary: A lightweight caching nameserver
-
-%description
-Dnsmasq is lightweight, easy to configure DNS forwarder and DHCP server. It
-is designed to provide DNS and, optionally, DHCP, to a small network. It can
-serve the names of local machines which are not in the global DNS. The DHCP
-server integrates with the DNS server and allows machines with DHCP-allocated
-addresses to appear in the DNS with names configured either in each host or
-in a central configuration file. Dnsmasq supports static and dynamic DHCP
-leases and BOOTP for network booting of diskless machines.
-
-
-###############################################################################
-#
-# Build
-#
-###############################################################################
-
-%prep
-%setup -q
-%build
-make
-
-
-###############################################################################
-#
-# Install
-#
-###############################################################################
-
-%install
-rm -rf $RPM_BUILD_ROOT
-
-mkdir -p -m 755 $RPM_BUILD_ROOT/usr/sbin
-mkdir -p -m 755 $RPM_BUILD_ROOT/etc/rc.d/init.d
-mkdir -p -m 755 $RPM_BUILD_ROOT/usr/share/man/man8
-
-cp rpm/dnsmasq.rh $RPM_BUILD_ROOT/etc/rc.d/init.d/dnsmasq
-strip src/dnsmasq
-cp src/dnsmasq $RPM_BUILD_ROOT/usr/sbin
-cp dnsmasq.8 $RPM_BUILD_ROOT/usr/share/man/man8
-cp dnsmasq.conf.example $RPM_BUILD_ROOT/etc/dnsmasq.conf
-###############################################################################
-#
-# Clean up
-#
-###############################################################################
-
-%clean
-rm -rf $RPM_BUILD_ROOT
-
-
-###############################################################################
-#
-# Post-install scriptlet
-#
-###############################################################################
-
-%post
-/sbin/chkconfig --add dnsmasq
-
-
-###############################################################################
-#
-# Pre-uninstall scriptlet
-#
-# If there's a time when your package needs to have one last look around before
-# the user erases it, the place to do it is in the %preun script. Anything that
-# a package needs to do immediately prior to RPM taking any action to erase the
-# package, can be done here.
-#
-###############################################################################
-
-%preun
-if [ $1 = 0 ]; then # execute this only if we are NOT doing an upgrade
- service dnsmasq stop >/dev/null 2>&1
- /sbin/chkconfig --del dnsmasq
-fi
-
-
-###############################################################################
-#
-# Post-uninstall scriptlet
-#
-# The %postun script executes after the package has been removed. It is the
-# last chance for a package to clean up after itself.
-#
-###############################################################################
-
-%postun
-if [ "$1" -ge "1" ]; then
- service dnsmasq restart >/dev/null 2>&1
-fi
-
-
-###############################################################################
-#
-# File list
-#
-###############################################################################
-
-%files
-%defattr(-,root,root)
-%doc CHANGELOG COPYING FAQ doc.html setup.html UPGRADING_to_2.0
-%attr(0755,root,root) /etc/rc.d/init.d/dnsmasq
-%attr(0664,root,root) /etc/dnsmasq.conf
-%config /etc/rc.d/init.d/dnsmasq
-%config /etc/dnsmasq.conf
-%attr(0755,root,root) /usr/sbin/dnsmasq
-%attr(0644,root,root) /usr/share/man/man8/dnsmasq.8.bz2
-
-
diff --git a/dnsmasq-rh.spec b/dnsmasq-rh.spec
index ac36c85..847a267 100644
--- a/dnsmasq-rh.spec
+++ b/dnsmasq-rh.spec
@@ -5,7 +5,7 @@
###############################################################################
Name: dnsmasq
-Version: 2.5
+Version: 2.6
Release: 1
Copyright: GPL
Group: System Environment/Daemons
@@ -58,7 +58,6 @@ cp rpm/dnsmasq.rh $RPM_BUILD_ROOT/etc/rc.d/init.d/dnsmasq
strip src/dnsmasq
cp src/dnsmasq $RPM_BUILD_ROOT/usr/sbin
cp dnsmasq.8 $RPM_BUILD_ROOT/usr/share/man/man8
-gzip $RPM_BUILD_ROOT/usr/share/man/man8/dnsmasq.8
cp dnsmasq.conf.example $RPM_BUILD_ROOT/etc/dnsmasq.conf
###############################################################################
@@ -128,6 +127,6 @@ fi
%attr(0755,root,root) /etc/rc.d/init.d/dnsmasq
%attr(0664,root,root) /etc/dnsmasq.conf
%attr(0755,root,root) /usr/sbin/dnsmasq
-%attr(0644,root,root) /usr/share/man/man8/dnsmasq.8.gz
+%attr(0644,root,root) /usr/share/man/man8/dnsmasq*
diff --git a/dnsmasq-suse.spec b/dnsmasq-suse.spec
index 339a8ad..984cb3e 100644
--- a/dnsmasq-suse.spec
+++ b/dnsmasq-suse.spec
@@ -5,7 +5,7 @@
###############################################################################
Name: dnsmasq
-Version: 2.5
+Version: 2.6
Release: 1
Copyright: GPL
Group: Productivity/Networking/DNS/Servers
diff --git a/dnsmasq.8 b/dnsmasq.8
index 3cd6dd0..8e5afd1 100644
--- a/dnsmasq.8
+++ b/dnsmasq.8
@@ -23,7 +23,10 @@ is lightweight and easy to configure. It is intended as be run on
small router/firewalls and provide a DNS (and optionally, DHCP) service to a LAN.
.SH OPTIONS
Note that in general missing parameters are allowed and switch off
-functions, for instance "--pid-file=" disables writing a PID file.
+functions, for instance "--pid-file=" disables writing a PID file. On
+BSD, unless the GNU getopt library is linked, the long form of the
+options does not work on the command line; it is still recognised in
+the configuration file.
.TP
.B \-h, --no-hosts
Don't read the hostnames in /etc/hosts.
@@ -271,9 +274,16 @@ given using the
.B interface
option. This limitation currently affects OpenBSD. The optional
network-id is a alphanumeric label which marks this network so that
-dhcp options may be specified on a per-network basis.
+dhcp options may be specified on a per-network basis. The end address
+may be replaced by the keyword
+.B static
+which tells dnsmasq to enable DHCP for the network specified, but not
+to dynamically allocate IP addresses. Only hosts which have static
+addresses given via
+.B dhcp-host
+or from /etc/ethers will be served.
.TP
-.B \-G, --dhcp-host=[[<hwaddr>]|[id:<client_id>]][,<ipaddr>][,<hostname>][,<lease_time>]
+.B \-G, --dhcp-host=[[<hwaddr>]|[id:<client_id>]][net:<netid>][,<ipaddr>][,<hostname>][,<lease_time>][,ignore]
Specify per host parameters for the DHCP server. This allows a machine
with a particular hardware address to be always allocated the same
hostname, IP address and lease time. A hostname specified like this
@@ -299,7 +309,16 @@ allowed to specify the client ID as text, like this:
If a name appears in /etc/hosts, the associated address can be
allocated to a DHCP lease, but only if a
.B --dhcp-host
-option specifying the name also exists.
+option specifying the name also exists. The special keyword "ignore"
+tells dnsmasq to never offer a DHCP lease to a machine. The machine
+can be specified by hardware address, client ID or hostname, for
+instance
+.B --dhcp-host=00:20:e0:3b:13:af,ignore
+This is
+useful when there is another DHCP server on the network which should
+be used by some machines. The net:<network-id> parameter enables DHCP options just
+for this host in the same way as the the network-id in
+.B dhcp-range.
.TP
.B \-Z, --read-ethers
Read /etc/ethers for information about hosts for the DHCP server. The
@@ -323,7 +342,9 @@ specfied in RFC2132. For example, to set the default route option to
and to set the time-server address to 192.168.0.4, do
.B dhcp-option=42,192.168.0.4
The special address 0.0.0.0 is taken to mean "the address of the
-machine running dnsmasq". If the optional network-id is given then
+machine running dnsmasq". Data types allowed are comma seperated
+dotted-quad IP addresses, a decimal number, colon-seperated hex digits
+and a text string. If the optional network-id is given then
this option is only sent to machines on the network whose dhcp-range
contains a matching network-id.
Be careful: no checking is done that the correct type of data for the
@@ -344,7 +365,12 @@ create thousands of leases and use lots of memory in the dnsmasq
process.
.TP
.B \-l, --dhcp-leasefile=<path>
-Use the specified file to store DHCP lease information.
+Use the specified file to store DHCP lease information. If this option
+is given but no dhcp-range option is given then dnsmasq version 1
+behaviour is activated. The file given is assumed to be an ISC dhcpd
+lease file and parsed for leases which are then added to the DNS
+system if they have a hostname. This functionality may have been
+excluded from dnsmasq at compile time, in which case an error will occur.
.TP
.B \-s, --domain=<domain>
Specifies the domain for the DHCP server. This has two effects;
@@ -361,12 +387,14 @@ Add the domain-suffix to simple names (without a period) in /etc/hosts
in the same way as for DHCP-derived names.
.SH CONFIG FILE
At startup, dnsmasq reads /etc/dnsmasq.conf, if it exists. (On
-FreeBSD, the file is /usr/local/etc/dnsmasq.conf) The format of this
+FreeBSD and OpenBSD, the file is /usr/local/etc/dnsmasq.conf) The format of this
file consists of one option per line, exactly as the long options detailed
in the OPTIONS section but without the leading "--". Lines starting with # are comments and ignored. For
options which may only be specified once, the configuration file overrides
the command line. Use the --conf-file option to specify a different
-configuration file.
+configuration file. The conf-file option is also allowed in
+configuration files, to include multiple configuration files. Only one
+level of nesting is allowed.
.SH NOTES
When it receives a SIGHUP,
.B dnsmasq
diff --git a/dnsmasq.conf.example b/dnsmasq.conf.example
index 73e96e0..ce3bcde 100644
--- a/dnsmasq.conf.example
+++ b/dnsmasq.conf.example
@@ -157,6 +157,14 @@ filterwin2k
# it asks for a DHCP lease.
#dhcp-host=judge
+# Never offer DHCP service to a machine whose ethernet
+# address is 11:22:33:44:55:66
+#dhcp-host=11:22:33:44:55:66,ignore
+
+# Send extra options which are tagged as "red" to
+# the machine with ethernet address 11:22:33:44:55:66
+#dhcp-host=11:22:33:44:55:66,net:red
+
# If this line is uncommented, dnsmasq will read /etc/ethers and act
# on the ethernet-address/IP pairs found there just as if they had
# been given as --dhcp-host options. Useful if you keep
@@ -193,6 +201,10 @@ filterwin2k
# Set the "all subnets are local" flag
#dhcp-option=27,1
+# Send the etherboot magic flag and then etherboot options (a string).
+#dhcp-option=128,e4:45:74:68:00:00
+#dhcp-option=129,NIC=eepro100
+
# Specify an option which will only be sent to the "red" network
# (see dhcp-range for the declaration of the "red" network)
#dhcp-option=red,42,192.168.1.1
@@ -250,13 +262,13 @@ filterwin2k
# and this maps 1.2.3.x to 5.6.7.x
#alias=1.2.3.0,5.6.7.0,255.255.255.0
-
-
# For debugging purposes, log each DNS query as it passes through
# dnsmasq.
#log-queries
-
+# Include a another lot of configuration options.
+#conf-file=/etc/dnsmasq.more.conf
+
diff --git a/src/Makefile b/src/Makefile
index c2cb07f..77229d7 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -3,8 +3,8 @@
CFLAGS?= -O2
-OBJS = cache.o rfc1035.o util.o option.o forward.o \
- network.o dnsmasq.o dhcp.o lease.o rfc2131.o
+OBJS = cache.o rfc1035.o util.o option.o forward.o isc.o \
+ network.o dnsmasq.o dhcp.o lease.o rfc2131.o
.c.o: dnsmasq.h config.h
$(CC) $(CFLAGS) $(RPM_OPT_FLAGS) -Wall -W -c $*.c
diff --git a/src/config.h b/src/config.h
index ea503f9..ad9e08d 100644
--- a/src/config.h
+++ b/src/config.h
@@ -12,12 +12,13 @@
/* Author's email: simon@thekelleys.org.uk */
-#define VERSION "2.5"
+#define VERSION "2.6"
#define FTABSIZ 150 /* max number of outstanding requests */
#define TIMEOUT 20 /* drop queries after TIMEOUT seconds */
#define LOGRATE 120 /* log table overflows every LOGRATE seconds */
#define CACHESIZ 150 /* default cache size */
+#define MAXTOK 50 /* token in DHCP leases */
#define MAXLEASES 150 /* maximum number of DHCP leases */
#define SMALLDNAME 40 /* most domain names are smaller than this */
#define HOSTSFILE "/etc/hosts"
@@ -121,6 +122,10 @@ HAVE_BROKEN_RTC
work on other systems by teaching dnsmasq_time() in utils.c how to
read the system uptime.
+HAVE_ISC_READER
+ define this to include the old ISC dhcpcd integration. Note that you cannot
+ set both HAVE_ISC_READER and HAVE_BROKEN_RTC.
+
HAVE_GETOPT_LONG
define this if you have GNU libc or GNU getopt.
@@ -175,6 +180,16 @@ NOTES:
*/
+/* platform independent options. */
+#undef HAVE_BROKEN_RTC
+#define HAVE_ISC_READER
+
+#if defined(HAVE_BROKEN_RTC) && defined(HAVE_ISC_READER)
+# error HAVE_ISC_READER is not compatible with HAVE_BROKEN_RTC
+#endif
+
+/* platform dependent options. */
+
/* Must preceed __linux__ since uClinux defines __linux__ too. */
#if defined(__uClinux__) || defined(__UCLIBC__)
#undef HAVE_LINUX_IPV6_PROC
@@ -255,6 +270,7 @@ typedef unsigned long in_addr_t;
#define HAVE_SOCKADDR_SA_LEN
#undef HAVE_PSELECT
#define HAVE_BPF
+#define BIND_8_COMPAT
/* Define before sys/socket.h is included so we get socklen_t */
#define _BSD_SOCKLEN_T_
/* The two below are not defined in Mac OS X arpa/nameserv.h */
@@ -287,3 +303,4 @@ typedef unsigned long in_addr_t;
+
diff --git a/src/dhcp.c b/src/dhcp.c
index f4f71a6..02e42b9 100644
--- a/src/dhcp.c
+++ b/src/dhcp.c
@@ -362,6 +362,10 @@ int address_available(struct dhcp_context *context, struct in_addr taddr)
start = ntohl(context->start.s_addr);
end = ntohl(context->end.s_addr);
+ /* static leases only. */
+ if (start == end)
+ return 0;
+
if (addr < start)
return 0;
@@ -382,7 +386,11 @@ int address_allocate(struct dhcp_context *context, struct dhcp_config *configs,
struct dhcp_config *config;
struct in_addr start = context->last;
-
+
+ /* start == end means no dynamic leases. */
+ if (context->end.s_addr == context->start.s_addr)
+ return 0;
+
do {
if (context->last.s_addr == context->end.s_addr)
context->last = context->start;
@@ -393,7 +401,7 @@ int address_allocate(struct dhcp_context *context, struct dhcp_config *configs,
if (!lease_find_by_addr(context->last))
{
for (config = configs; config; config = config->next)
- if (config->addr.s_addr == context->last.s_addr)
+ if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == context->last.s_addr)
break;
if (!config)
@@ -411,7 +419,7 @@ static int is_addr_in_context(struct dhcp_context *context, struct dhcp_config *
{
if (!context)
return 1;
- if (config->addr.s_addr == 0)
+ if (!(config->flags & CONFIG_ADDR))
return 1;
if ((config->addr.s_addr & context->netmask.s_addr) == (context->start.s_addr & context->netmask.s_addr))
return 1;
@@ -428,28 +436,31 @@ struct dhcp_config *find_config(struct dhcp_config *configs,
if (clid_len)
for (config = configs; config; config = config->next)
- {
- if (config->clid_len == clid_len &&
- memcmp(config->clid, clid, clid_len) == 0 &&
- is_addr_in_context(context, config))
- return config;
-
- /* dhcpcd prefixes ASCII client IDs by zero which is wrong, but we try and
- cope with that here */
- if (*clid == 0 && config->clid_len == clid_len-1 &&
- memcmp(config->clid, clid+1, clid_len-1) == 0 &&
- is_addr_in_context(context, config))
- return config;
- }
-
+ if (config->flags & CONFIG_CLID)
+ {
+ if (config->clid_len == clid_len &&
+ memcmp(config->clid, clid, clid_len) == 0 &&
+ is_addr_in_context(context, config))
+ return config;
+
+ /* dhcpcd prefixes ASCII client IDs by zero which is wrong, but we try and
+ cope with that here */
+ if (*clid == 0 && config->clid_len == clid_len-1 &&
+ memcmp(config->clid, clid+1, clid_len-1) == 0 &&
+ is_addr_in_context(context, config))
+ return config;
+ }
+
for (config = configs; config; config = config->next)
- if (memcmp(config->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0 &&
+ if ((config->flags & CONFIG_HWADDR) &&
+ memcmp(config->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0 &&
is_addr_in_context(context, config))
return config;
if (hostname)
for (config = configs; config; config = config->next)
- if (config->hostname && hostname_isequal(config->hostname, hostname) &&
+ if ((config->flags & CONFIG_NAME) &&
+ hostname_isequal(config->hostname, hostname) &&
is_addr_in_context(context, config))
return config;
@@ -459,28 +470,29 @@ struct dhcp_config *find_config(struct dhcp_config *configs,
struct dhcp_config *dhcp_read_ethers(struct dhcp_config *configs, char *buff)
{
FILE *f = fopen(ETHERSFILE, "r");
- unsigned int e0, e1, e2, e3, e4, e5;
- char *ip, *cp, *name;
+ unsigned int flags, e0, e1, e2, e3, e4, e5;
+ char *ip, *cp;
struct in_addr addr;
+ unsigned char hwaddr[ETHER_ADDR_LEN];
struct dhcp_config *config;
+ int count = 0;
if (!f)
- die("failed to open " ETHERSFILE ":%s", NULL);
-
+ {
+ syslog(LOG_ERR, "failed to read " ETHERSFILE ":%m");
+ return configs;
+ }
+
while (fgets(buff, MAXDNAME, f))
{
- while (strlen(buff) > 0 &&
- (buff[strlen(buff)-1] == '\n' ||
- buff[strlen(buff)-1] == ' ' ||
- buff[strlen(buff)-1] == '\r' ||
- buff[strlen(buff)-1] == '\t'))
+ while (strlen(buff) > 0 && isspace(buff[strlen(buff)-1]))
buff[strlen(buff)-1] = 0;
if ((*buff == '#') || (*buff == '+'))
continue;
- for (ip = buff; *ip && *ip != ' ' && *ip != '\t'; ip++);
- for(; *ip && (*ip == ' ' || *ip == '\t'); ip++)
+ for (ip = buff; *ip && !isspace(*ip); ip++);
+ for(; *ip && isspace(*ip); ip++)
*ip = 0;
if (!*ip)
continue;
@@ -488,6 +500,13 @@ struct dhcp_config *dhcp_read_ethers(struct dhcp_config *configs, char *buff)
if (!sscanf(buff, "%x:%x:%x:%x:%x:%x", &e0, &e1, &e2, &e3, &e4, &e5))
continue;
+ hwaddr[0] = e0;
+ hwaddr[1] = e1;
+ hwaddr[2] = e2;
+ hwaddr[3] = e3;
+ hwaddr[4] = e4;
+ hwaddr[5] = e5;
+
/* check for name or dotted-quad */
for (cp = ip; *cp; cp++)
if (!(*cp == '.' || (*cp >='0' && *cp <= '9')))
@@ -495,47 +514,64 @@ struct dhcp_config *dhcp_read_ethers(struct dhcp_config *configs, char *buff)
if (!*cp)
{
- name = NULL;
if ((addr.s_addr = inet_addr(ip)) == (in_addr_t)-1)
continue;
+ flags = CONFIG_ADDR;
for (config = configs; config; config = config->next)
- if (config->addr.s_addr == addr.s_addr)
+ if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
break;
}
else
{
if (!canonicalise(ip))
continue;
- name = ip;
- addr.s_addr = 0;
+ flags = CONFIG_NAME;
for (config = configs; config; config = config->next)
- if (config->hostname && hostname_isequal(config->hostname, name))
+ if ((config->flags & CONFIG_NAME) && hostname_isequal(config->hostname, ip))
break;
}
if (!config)
{
- config = safe_malloc(sizeof(struct dhcp_config));
- config->clid_len = 0;
- config->clid = NULL;
- config->lease_time = 0;
- config->hostname = safe_string_alloc(name);
- config->addr = addr;
- config->next = configs;
- configs = config;
+ for (config = configs; config; config = config->next)
+ if ((config->flags & CONFIG_HWADDR) &&
+ memcmp(config->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0)
+ break;
+
+ if (!config)
+ {
+ if (!(config = malloc(sizeof(struct dhcp_config))))
+ continue;
+ config->flags = 0;
+ config->next = configs;
+ configs = config;
+ }
+
+ config->flags |= flags;
+
+ if (flags & CONFIG_NAME)
+ {
+ if ((config->hostname = malloc(strlen(ip)+1)))
+ strcpy(config->hostname, ip);
+ else
+ config->flags &= ~CONFIG_NAME;
+ }
+
+ if (flags & CONFIG_ADDR)
+ config->addr = addr;
}
+
+ config->flags |= CONFIG_HWADDR;
+ memcpy(config->hwaddr, hwaddr, ETHER_ADDR_LEN);
- config->hwaddr[0] = e0;
- config->hwaddr[1] = e1;
- config->hwaddr[2] = e2;
- config->hwaddr[3] = e3;
- config->hwaddr[4] = e4;
- config->hwaddr[5] = e5;
+ count++;
}
fclose(f);
+
+ syslog(LOG_INFO, "read " ETHERSFILE " - %d addresses", count);
return configs;
}
@@ -549,10 +585,13 @@ void dhcp_update_configs(struct dhcp_config *configs)
struct crec *crec;
for (config = configs; config; config = config->next)
- if (config->addr.s_addr == 0 && config->hostname &&
+ if (!(config->flags & CONFIG_ADDR) &&
+ (config->flags & CONFIG_NAME) &&
(crec = cache_find_by_name(NULL, config->hostname, 0, F_IPV4)) &&
(crec->flags & F_HOSTS))
- config->addr = crec->addr.addr.addr4;
-
+ {
+ config->addr = crec->addr.addr.addr4;
+ config->flags |= CONFIG_ADDR;
+ }
}
diff --git a/src/dnsmasq.c b/src/dnsmasq.c
index c626ed9..3169e55 100644
--- a/src/dnsmasq.c
+++ b/src/dnsmasq.c
@@ -71,7 +71,7 @@ int main (int argc, char **argv)
int leasefd = -1, dhcpfd = -1, dhcp_raw_fd = -1;
struct sigaction sigact;
sigset_t sigmask;
-
+
sighup = 1; /* init cache the first time through */
sigusr1 = 0; /* but don't dump */
sigterm = 0; /* or die */
@@ -120,17 +120,14 @@ int main (int argc, char **argv)
#endif
if (!lease_file)
- lease_file = LEASEFILE;
- else
{
- if (!dhcp)
- {
- complain("********* dhcp-lease option set, but not dhcp-range.", NULL);
- complain("********* Are you trying to use the obsolete ISC dhcpd integration?", NULL);
- complain("********* Please configure the dnsmasq integrated DHCP server by using", NULL);
- complain("********* the \"dhcp-range\" option, and remove any other DHCP server.", NULL);
- }
+ if (dhcp)
+ lease_file = LEASEFILE;
}
+#ifndef HAVE_ISC_READER
+ else if (!dhcp)
+ die("ISC dhcpd integration not available: set HAVE_ISC_READER in src/config.h", NULL);
+#endif
interfaces = enumerate_interfaces(if_names, if_addrs, if_except, port);
if (options & OPT_NOWILD)
@@ -152,11 +149,6 @@ int main (int argc, char **argv)
{
dhcp_init(&dhcpfd, &dhcp_raw_fd);
leasefd = lease_init(lease_file, domain_suffix, dnamebuff, packet, now, maxleases);
- if (options & OPT_ETHERS)
- dhcp_configs = dhcp_read_ethers(dhcp_configs, dnamebuff);
- lease_update_from_configs(dhcp_configs, domain_suffix); /* must follow cache_init and lease_init */
- lease_update_file(0, now);
- lease_update_dns();
}
setbuf(stdout, NULL);
@@ -248,7 +240,10 @@ int main (int argc, char **argv)
sprintf(packet, "infinite");
else
sprintf(packet, "%ds", (int)dhcp_tmp->lease_time);
- syslog(LOG_INFO, "DHCP, IP range %s -- %s, lease time %s",
+ syslog(LOG_INFO,
+ dhcp_tmp->start.s_addr == dhcp_tmp->end.s_addr ?
+ "DHCP, static leases only on %.0s%s, lease time %s" :
+ "DHCP, IP range %s -- %s, lease time %s",
dnamebuff, inet_ntoa(dhcp_tmp->end), packet);
}
@@ -271,6 +266,8 @@ int main (int argc, char **argv)
cache_reload(options, dnamebuff, domain_suffix, addn_hosts);
if (dhcp)
{
+ if (options & OPT_ETHERS)
+ dhcp_configs = dhcp_read_ethers(dhcp_configs, dnamebuff);
dhcp_update_configs(dhcp_configs);
lease_update_from_configs(dhcp_configs, domain_suffix);
lease_update_file(0, now);
@@ -350,11 +347,17 @@ int main (int argc, char **argv)
if (last == 0 || difftime(now, last) > 1.0)
{
last = now;
+
+#ifdef HAVE_ISC_READER
+ if (lease_file && !dhcp)
+ load_dhcp(lease_file, domain_suffix, now, dnamebuff);
+#endif
+
if (!(options & OPT_NO_POLL))
{
struct resolvc *res = resolv, *latest = NULL;
- time_t last_change = 0;
struct stat statbuf;
+ time_t last_change = 0;
/* There may be more than one possible file.
Go through and find the one which changed _last_.
Warn of any which can't be read. */
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index a67e488..edb10ad 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -238,15 +238,24 @@ struct dhcp_lease {
};
struct dhcp_config {
+ unsigned int flags;
int clid_len; /* length of client identifier */
unsigned char *clid; /* clientid */
unsigned char hwaddr[ETHER_ADDR_LEN];
- char *hostname;
+ char *hostname, *netid;
struct in_addr addr;
unsigned int lease_time;
struct dhcp_config *next;
};
+#define CONFIG_DISABLE 1
+#define CONFIG_CLID 2
+#define CONFIG_HWADDR 4
+#define CONFIG_TIME 8
+#define CONFIG_NAME 16
+#define CONFIG_ADDR 32
+#define CONFIG_NETID 64
+
struct dhcp_opt {
int opt, len, is_addr;
unsigned char *val;
@@ -406,3 +415,7 @@ int dhcp_reply(struct dhcp_context *context,
char *domain_suffix, char *dhcp_file, char *dhcp_sname,
struct in_addr dhcp_next_server, struct in_addr router);
+/* isc.c */
+#ifdef HAVE_ISC_READER
+void load_dhcp(char *file, char *suffix, time_t now, char *hostname);
+#endif
diff --git a/src/isc.c b/src/isc.c
new file mode 100644
index 0000000..a807eab
--- /dev/null
+++ b/src/isc.c
@@ -0,0 +1,249 @@
+/* dnsmasq is Copyright (c) 2000 - 2004 by Simon Kelley
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 dated June, 1991.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+
+/* Code in this file is based on contributions by John Volpe. */
+
+#include "dnsmasq.h"
+
+#ifdef HAVE_ISC_READER
+
+struct isc_lease {
+ char *name, *fqdn;
+ time_t expires;
+ struct in_addr addr;
+ struct isc_lease *next;
+};
+
+static struct isc_lease *leases = NULL;
+static off_t lease_file_size = (off_t)0;
+static ino_t lease_file_inode = (ino_t)0;
+static int logged_lease = 0;
+
+static int next_token (char *token, int buffsize, FILE * fp)
+{
+ int c, count = 0;
+ char *cp = token;
+
+ while((c = getc(fp)) != EOF)
+ {
+ if (c == '#')
+ do { c = getc(fp); } while (c != '\n' && c != EOF);
+
+ if (c == ' ' || c == '\t' || c == '\n' || c == ';')
+ {
+ if (count)
+ break;
+ }
+ else if ((c != '"') && (count<buffsize-1))
+ {
+ *cp++ = c;
+ count++;
+ }
+ }
+
+ *cp = 0;
+ return count ? 1 : 0;
+}
+
+void load_dhcp(char *file, char *suffix, time_t now, char *hostname)
+{
+ char token[MAXTOK], *dot;
+ struct in_addr host_address;
+ time_t ttd, tts;
+ FILE *fp;
+ struct isc_lease *lease, *tmp, **up;
+ struct stat statbuf;
+
+ if (stat(file, &statbuf) == -1)
+ {
+ if (!logged_lease)
+ syslog(LOG_WARNING, "failed to access %s: %m", file);
+ logged_lease = 1;
+ return;
+ }
+
+ logged_lease = 0;
+
+ if ((statbuf.st_size <= lease_file_size) &&
+ (statbuf.st_ino == lease_file_inode))
+ return;
+
+ lease_file_size = statbuf.st_size;
+ lease_file_inode = statbuf.st_ino;
+
+ if (!(fp = fopen (file, "r")))
+ {
+ syslog (LOG_ERR, "failed to load %s: %m", file);
+ return;
+ }
+
+ syslog (LOG_INFO, "reading %s", file);
+
+ while ((next_token(token, MAXTOK, fp)))
+ {
+ if (strcmp(token, "lease") == 0)
+ {
+ hostname[0] = '\0';
+ ttd = tts = (time_t)(-1);
+ if (next_token(token, MAXTOK, fp) &&
+ (host_address.s_addr = inet_addr(token)) != (in_addr_t) -1)
+ {
+ if (next_token(token, MAXTOK, fp) && *token == '{')
+ {
+ while (next_token(token, MAXTOK, fp) && *token != '}')
+ {
+ if ((strcmp(token, "client-hostname") == 0) ||
+ (strcmp(token, "hostname") == 0))
+ {
+ if (next_token(hostname, MAXDNAME, fp))
+ if (!canonicalise(hostname))
+ {
+ *hostname = 0;
+ syslog(LOG_ERR, "bad name in %s", file);
+ }
+ }
+ else if ((strcmp(token, "ends") == 0) ||
+ (strcmp(token, "starts") == 0))
+ {
+ struct tm lease_time;
+ int is_ends = (strcmp(token, "ends") == 0);
+ if (next_token(token, MAXTOK, fp) && /* skip weekday */
+ next_token(token, MAXTOK, fp) && /* Get date from lease file */
+ sscanf (token, "%d/%d/%d",
+ &lease_time.tm_year,
+ &lease_time.tm_mon,
+ &lease_time.tm_mday) == 3 &&
+ next_token(token, MAXTOK, fp) &&
+ sscanf (token, "%d:%d:%d:",
+ &lease_time.tm_hour,
+ &lease_time.tm_min,
+ &lease_time.tm_sec) == 3)
+ {
+ /* There doesn't seem to be a universally available library function
+ which converts broken-down _GMT_ time to seconds-in-epoch.
+ The following was borrowed from ISC dhcpd sources, where
+ it is noted that it might not be entirely accurate for odd seconds.
+ Since we're trying to get the same answer as dhcpd, that's just
+ fine here. */
+ static int months [11] = { 31, 59, 90, 120, 151, 181,
+ 212, 243, 273, 304, 334 };
+ time_t time = ((((((365 * (lease_time.tm_year - 1970) + /* Days in years since '70 */
+ (lease_time.tm_year - 1969) / 4 + /* Leap days since '70 */
+ (lease_time.tm_mon > 1 /* Days in months this year */
+ ? months [lease_time.tm_mon - 2]
+ : 0) +
+ (lease_time.tm_mon > 2 && /* Leap day this year */
+ !((lease_time.tm_year - 1972) & 3)) +
+ lease_time.tm_mday - 1) * 24) + /* Day of month */
+ lease_time.tm_hour) * 60) +
+ lease_time.tm_min) * 60) + lease_time.tm_sec;
+ if (is_ends)
+ ttd = time;
+ else
+ tts = time; }
+ }
+ }
+
+ /* missing info? */
+ if (!*hostname)
+ continue;
+ if (ttd == (time_t)(-1))
+ continue;
+
+ /* We use 0 as infinite in ttd */
+ if ((tts != -1) && (ttd == tts - 1))
+ ttd = (time_t)0;
+ else if (difftime(now, ttd) > 0)
+ continue;
+
+ if ((dot = strchr(hostname, '.')))
+ {
+ if (!suffix || hostname_isequal(dot+1, suffix))
+ {
+ syslog(LOG_WARNING,
+ "Ignoring DHCP lease for %s because it has an illegal domain part",
+ hostname);
+ continue;
+ }
+ *dot = 0;
+ }
+
+ for (lease = leases; lease; lease = lease->next)
+ if (hostname_isequal(lease->name, hostname))
+ {
+ lease->expires = ttd;
+ lease->addr = host_address;
+ break;
+ }
+
+ if (!lease && (lease = malloc(sizeof(struct isc_lease))))
+ {
+ lease->expires = ttd;
+ lease->addr = host_address;
+ lease->fqdn = NULL;
+ lease->next = leases;
+ if (!(lease->name = malloc(strlen(hostname)+1)))
+ free(lease);
+ else
+ {
+ leases = lease;
+ strcpy(lease->name, hostname);
+ if (suffix && (lease->fqdn = malloc(strlen(hostname) + strlen(suffix) + 2)))
+ {
+ strcpy(lease->fqdn, hostname);
+ strcat(lease->fqdn, ".");
+ strcat(lease->fqdn, suffix);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ fclose(fp);
+
+ /* prune expired leases */
+ for (lease = leases, up = &leases; lease; lease = tmp)
+ {
+ tmp = lease->next;
+ if (lease->expires != (time_t)0 && difftime(now, lease->expires) > 0)
+ {
+ *up = lease->next; /* unlink */
+ free(lease->name);
+ if (lease->fqdn)
+ free(lease->fqdn);
+ free(lease);
+ }
+ else
+ up = &lease->next;
+ }
+
+
+ /* remove all existing DHCP cache entries */
+ cache_unhash_dhcp();
+
+ for (lease = leases; lease; lease = lease->next)
+ {
+ if (lease->fqdn)
+ {
+ cache_add_dhcp_entry(lease->fqdn, &lease->addr, lease->expires, F_REVERSE);
+ cache_add_dhcp_entry(lease->name, &lease->addr, lease->expires, 0);
+ }
+ else
+ cache_add_dhcp_entry(lease->name, &lease->addr, lease->expires, F_REVERSE);
+ }
+}
+
+#endif
+
diff --git a/src/option.c b/src/option.c
index 8fe6a44..2aab234 100644
--- a/src/option.c
+++ b/src/option.c
@@ -161,10 +161,10 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
{
int option = 0, i;
unsigned int flags = 0;
- FILE *f = NULL;
- char *conffile = CONFFILE;
+ FILE *file_save = NULL, *f = NULL;
+ char *file_name_save = NULL, *conffile = CONFFILE;
int conffile_set = 0;
- int lineno = 0;
+ int line_save = 0, lineno = 0;
opterr = 0;
*min_leasetime = UINT_MAX;
@@ -179,26 +179,38 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
#endif
else
{ /* f non-NULL, reading from conffile. */
+ reread:
if (!fgets(buff, MAXDNAME, f))
{
/* At end of file, all done */
fclose(f);
+ if (file_save)
+ {
+ /* may be nested */
+ conffile = file_name_save;
+ f = file_save;
+ file_save = NULL;
+ lineno = line_save;
+ goto reread;
+ }
break;
}
else
{
char *p;
+ int white;
lineno++;
/* dump comments */
- for (p = buff; *p; p++)
- if (*p == '#')
- *p = 0;
+ for (white = 1, p = buff; *p; p++)
+ if (white && *p == '#')
+ {
+ *p = 0;
+ break;
+ }
+ else
+ white = isspace(*p);
/* fgets gets end of line char too. */
- while (strlen(buff) > 0 &&
- (buff[strlen(buff)-1] == '\n' ||
- buff[strlen(buff)-1] == ' ' ||
- buff[strlen(buff)-1] == '\r' ||
- buff[strlen(buff)-1] == '\t'))
+ while (strlen(buff) > 0 && isspace(buff[strlen(buff)-1]))
buff[strlen(buff)-1] = 0;
if (*buff == 0)
continue;
@@ -227,6 +239,7 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
{ /* end of command line args, start reading conffile. */
if (!conffile)
break; /* "confile=" option disables */
+ fileopen:
option = 0;
if (!(f = fopen(conffile, "r")))
{
@@ -274,9 +287,27 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
switch (option)
{
case 'C':
+ if (!f)
+ {
+ conffile = safe_string_alloc(optarg);
+ conffile_set = 1;
+ break;
+ }
+
+ /* nest conffiles one deep */
+ if (file_save)
+ {
+ sprintf(buff, "nested includes not allowed at line %d of %s ", lineno, conffile);
+ complain(buff, NULL);
+ continue;
+ }
+ file_name_save = conffile;
+ file_save = f;
+ line_save = lineno;
conffile = safe_string_alloc(optarg);
conffile_set = 1;
- break;
+ lineno = 0;
+ goto fileopen;
case 'x':
*runfile = safe_string_alloc(optarg);
@@ -643,11 +674,15 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
*(a[k]++) = 0;
}
- if ((k < 2) ||
- ((new->start.s_addr = inet_addr(a[0])) == (in_addr_t)-1) ||
- ((new->end.s_addr = inet_addr(a[1])) == (in_addr_t)-1))
+ if ((k < 2) || ((new->start.s_addr = inet_addr(a[0])) == (in_addr_t)-1))
+ option = '?';
+ else if (strcmp(a[1], "static") == 0)
+ new->end = new->start;
+ else if ((new->end.s_addr = inet_addr(a[1])) == (in_addr_t)-1)
+ option = '?';
+
+ if (option == '?')
{
- option = '?';
free(new);
break;
}
@@ -700,22 +735,17 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
case 'G':
{
int j, k;
- char *a[4] = { NULL, NULL, NULL, NULL };
+ char *a[6] = { NULL, NULL, NULL, NULL, NULL, NULL };
unsigned int e0, e1, e2, e3, e4, e5;
struct dhcp_config *new = safe_malloc(sizeof(struct dhcp_config));
struct in_addr in;
new->next = *dhcp_conf;
-
- memset(new->hwaddr, 0, ETHER_ADDR_LEN);
- new->clid_len = 0;
- new->clid = NULL;
- new->hostname = NULL;
- new->addr.s_addr = 0;
- new->lease_time = 0;
+ new->flags = 0;
+
a[0] = optarg;
- for (k = 1; k < 4; k++)
+ for (k = 1; k < 6; k++)
{
if (!(a[k] = strchr(a[k-1], ',')))
break;
@@ -723,37 +753,60 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
}
for(j = 0; j < k; j++)
- if (strchr(a[j], ':')) /* ethernet address or binary CLID */
+ if (strchr(a[j], ':')) /* ethernet address, netid or binary CLID */
{
char *arg = a[j];
if ((arg[0] == 'i' || arg[0] == 'I') &&
(arg[1] == 'd' || arg[1] == 'D') &&
arg[2] == ':')
{
- int s, len;
+ int len;
arg += 3; /* dump id: */
if (strchr(arg, ':'))
{
- s = (strlen(arg)/3) + 1;
- /* decode in place */
- for (len = 0; len < s; len++)
+ /* decode hex in place */
+ char *p = arg, *q = arg, *r;
+ while (*p)
{
- if (arg[(len*3)+2] != ':')
- option = '?';
- arg[(len*3)+2] = 0;
- arg[len] = strtol(&arg[len*3], NULL, 16);
+ for (r = p; *r && *r != ':'; r++);
+ if (*r)
+ {
+ if (r != p)
+ {
+ *r = 0;
+ *(q++) = strtol(p, NULL, 16);
+ }
+ p = r+1;
+ }
+ else
+ {
+ if (*p)
+ *(q++) = strtol(p, NULL, 16);
+ break;
+ }
}
+ len = q - arg;
}
else
len = strlen(arg);
+ new->flags |= CONFIG_CLID;
new->clid_len = len;
new->clid = safe_malloc(len);
memcpy(new->clid, arg, len);
}
+ else if ((arg[0] == 'n' || arg[0] == 'N') &&
+ (arg[1] == 'e' || arg[1] == 'E') &&
+ (arg[2] == 't' || arg[3] == 'T') &&
+ arg[3] == ':')
+ {
+ new->flags |= CONFIG_NETID;
+ new->netid = safe_string_alloc(arg+4);
+ }
else if (sscanf(a[j], "%x:%x:%x:%x:%x:%x",
&e0, &e1, &e2, &e3, &e4, &e5) == 6)
{
+ new->flags |= CONFIG_HWADDR;
new->hwaddr[0] = e0;
new->hwaddr[1] = e1;
new->hwaddr[2] = e2;
@@ -765,7 +818,10 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
option = '?';
}
else if (strchr(a[j], '.') && (in.s_addr = inet_addr(a[j])) != (in_addr_t)-1)
- new->addr = in;
+ {
+ new->addr = in;
+ new->flags |= CONFIG_ADDR;
+ }
else
{
char *cp, *lastp = NULL, last = 0;
@@ -800,19 +856,38 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
if (lastp)
*lastp = last;
if (strcmp(a[j], "infinite") == 0)
- new->lease_time = 0xffffffff;
+ {
+ new->lease_time = 0xffffffff;
+ new->flags |= CONFIG_TIME;
+ }
+ else if (strcmp(a[j], "ignore") == 0)
+ new->flags |= CONFIG_DISABLE;
else
- new->hostname = safe_string_alloc(a[j]);
+ {
+ new->hostname = safe_string_alloc(a[j]);
+ new->flags |= CONFIG_NAME;
+ }
}
else
- new->lease_time = atoi(a[j]) * fac;
+ {
+ new->lease_time = atoi(a[j]) * fac;
+ new->flags |= CONFIG_TIME;
+ }
}
if (option == '?')
- free(new);
+ {
+ if (new->flags & CONFIG_NAME)
+ free(new->hostname);
+ if (new->flags & CONFIG_CLID)
+ free(new->clid);
+ if (new->flags & CONFIG_NETID)
+ free(new->netid);
+ free(new);
+ }
else
{
- if (new->lease_time < *min_leasetime)
+ if ((new->flags & CONFIG_TIME) && new->lease_time < *min_leasetime)
*min_leasetime = new->lease_time;
*dhcp_conf = new;
}
@@ -823,7 +898,7 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
{
struct dhcp_opt *new = safe_malloc(sizeof(struct dhcp_opt));
char *cp, *comma;
- int addrs, is_addr;
+ int addrs, digs, is_addr, is_hex, is_dec;
new->next = *dhcp_opts;
new->len = 0;
@@ -853,58 +928,113 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
free(new);
break;
}
-
+
+ *dhcp_opts = new;
+
if (!comma)
- {
- *dhcp_opts = new;
- break;
- }
-
- /* check for non-address list characters */
- for (addrs = 1, is_addr = 0, cp = comma+1; *cp; cp++)
+ break;
+
+ /* characterise the value */
+ is_addr = is_hex = is_dec = 1;
+ addrs = digs = 1;
+ for (cp = comma+1; *cp; cp++)
if (*cp == ',')
- addrs++;
- else if (!(*cp == '.' || *cp == ' ' || (*cp >='0' && *cp <= '9')))
- break;
+ {
+ addrs++;
+ is_dec = is_hex = 0;
+ }
+ else if (*cp == ':')
+ {
+ digs++;
+ is_dec = is_addr = 0;
+ }
else if (*cp == '.')
- is_addr = 1;
-
- if (*cp)
+ is_dec = is_hex = 0;
+ else if (!(*cp >='0' && *cp <= '9'))
+ {
+ is_dec = is_addr = 0;
+ if (!((*cp >='A' && *cp <= 'F') ||
+ (*cp >='a' && *cp <= 'F')))
+ is_hex = 0;
+ }
+
+ if (is_hex && digs > 1)
{
- /* text arg */
- new->len = strlen(comma+1);
- new->val = safe_malloc(new->len);
- memcpy(new->val, comma+1, new->len);
+ char *p = comma+1, *q, *r;
+ new->len = digs;
+ q = new->val = safe_malloc(new->len);
+ while (*p)
+ {
+ for (r = p; *r && *r != ':'; r++);
+ if (*r)
+ {
+ if (r != p)
+ {
+ *r = 0;
+ *(q++) = strtol(p, NULL, 16);
+ }
+ p = r+1;
+ }
+ else
+ {
+ if (*p)
+ *(q++) = strtol(p, NULL, 16);
+ break;
+ }
+ }
}
- else
+ else if (is_dec)
{
- struct in_addr in;
- unsigned char *op;
-
- if (addrs == 1 && !is_addr)
+ /* Given that we don't know the length,
+ this applaing hack is the best available */
+ unsigned int val = atoi(comma+1);
+ if (val < 256)
{
new->len = 1;
new->val = safe_malloc(1);
- *(new->val) = atoi(comma+1);
+ *(new->val) = val;
+ }
+ else if (val < 65536)
+ {
+ new->len = 2;
+ new->val = safe_malloc(2);
+ *(new->val) = val>>8;
+ *(new->val+1) = val;
}
else
{
- new->len = INADDRSZ * addrs;
- new->val = op = safe_malloc(new->len);
- new->is_addr = 1;
- while (addrs--)
- {
- cp = comma;
- if (cp && (comma = strchr(cp+1, ',')))
- *comma = 0;
- if (cp && (in.s_addr = inet_addr(cp+1)) == (in_addr_t)-1)
- option = '?';
- memcpy(op, &in, INADDRSZ);
- op += INADDRSZ;
- }
+ new->len = 4;
+ new->val = safe_malloc(4);
+ *(new->val) = val>>24;
+ *(new->val+1) = val>>16;
+ *(new->val+2) = val>>8;
+ *(new->val+3) = val;
}
}
- *dhcp_opts = new;
+ else if (is_addr)
+ {
+ struct in_addr in;
+ unsigned char *op;
+ new->len = INADDRSZ * addrs;
+ new->val = op = safe_malloc(new->len);
+ new->is_addr = 1;
+ while (addrs--)
+ {
+ cp = comma;
+ if ((comma = strchr(cp+1, ',')))
+ *comma = 0;
+ in.s_addr = inet_addr(cp+1);
+ memcpy(op, &in, INADDRSZ);
+ op += INADDRSZ;
+ }
+ }
+ else
+ {
+ /* text arg */
+ new->len = strlen(comma+1);
+ new->val = safe_malloc(new->len);
+ memcpy(new->val, comma+1, new->len);
+ }
break;
}
diff --git a/src/rfc2131.c b/src/rfc2131.c
index a4bd5c0..1b7364d 100644
--- a/src/rfc2131.c
+++ b/src/rfc2131.c
@@ -63,8 +63,12 @@ static unsigned char *do_req_options(struct dhcp_context *context,
char *domainname, char *hostname,
struct in_addr router,
struct in_addr iface_addr,
- int iface_mtu);
+ int iface_mtu, char *netid);
+static int have_config(struct dhcp_config *config, unsigned int mask)
+{
+ return config && (config->flags & mask);
+}
int dhcp_reply(struct dhcp_context *context,
struct in_addr iface_addr,
@@ -88,13 +92,27 @@ int dhcp_reply(struct dhcp_context *context,
char *message = NULL;
unsigned int renewal_time, expires_time, def_time;
struct dhcp_config *config;
-
+ char *netid;
+
if (mess->op != BOOTREQUEST ||
- mess->htype != ARPHRD_ETHER ||
mess->hlen != ETHER_ADDR_LEN ||
mess->cookie != htonl(DHCP_COOKIE))
- return 0;
+ return 0;
+ /* Token ring is supported when we have packet sockets
+ to make the HW headers for us. We don't have the code to build
+ token ring headers when using BPF. We rely on the fact that
+ token ring hwaddrs are the same size as ethernet hwaddrs. */
+
+#ifdef HAVE_BPF
+ if (mess->htype != ARPHRD_ETHER)
+ return 0;
+#else
+ if (mess->htype != ARPHRD_ETHER &&
+ mess->htype != ARPHRD_IEEE802)
+ return 0;
+#endif
+
mess->op = BOOTREPLY;
if ((opt = option_find(mess, sz, OPTION_MAXMESSAGE)))
@@ -130,9 +148,9 @@ int dhcp_reply(struct dhcp_context *context,
memcpy(req_options, option_ptr(opt), len);
req_options[len] = OPTION_END;
}
-
+
if ((config = find_config(dhcp_configs, context, clid, clid_len, mess->chaddr, NULL)) &&
- config->hostname)
+ have_config(config, CONFIG_NAME))
hostname = config->hostname;
else if ((opt = option_find(mess, sz, OPTION_HOSTNAME)))
{
@@ -164,7 +182,8 @@ int dhcp_reply(struct dhcp_context *context,
/* search again now we have a hostname */
config = find_config(dhcp_configs, context, clid, clid_len, mess->chaddr, hostname);
- def_time = config && config->lease_time ? config->lease_time : context->lease_time;
+ def_time = have_config(config, CONFIG_TIME) ? config->lease_time : context->lease_time;
+ netid = have_config(config, CONFIG_NETID) ? config->netid : context->netid;
if ((opt = option_find(mess, sz, OPTION_LEASE_TIME)))
{
@@ -219,11 +238,11 @@ int dhcp_reply(struct dhcp_context *context,
if (lease && lease->addr.s_addr == option_addr(opt).s_addr)
lease_prune(lease, now);
- if (config && config->addr.s_addr &&
+ if (have_config(config, CONFIG_ADDR) &&
config->addr.s_addr == option_addr(opt).s_addr)
{
syslog(LOG_WARNING, "disabling DHCP static address %s", inet_ntoa(config->addr));
- config->addr.s_addr = 0;
+ config->flags &= ~CONFIG_ADDR ;
}
return 0;
@@ -244,9 +263,9 @@ int dhcp_reply(struct dhcp_context *context,
if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP)))
mess->yiaddr = option_addr(opt);
- log_packet("DISCOVER", opt ? &mess->yiaddr : NULL, mess->chaddr, iface_name, NULL);
-
- if (config && config->addr.s_addr && !lease_find_by_addr(config->addr))
+ if (have_config(config, CONFIG_DISABLE))
+ message = "ignored";
+ else if (have_config(config, CONFIG_ADDR) && !lease_find_by_addr(config->addr))
mess->yiaddr = config->addr;
else if (lease &&
((lease->addr.s_addr & context->netmask.s_addr) ==
@@ -254,18 +273,19 @@ int dhcp_reply(struct dhcp_context *context,
mess->yiaddr = lease->addr;
else if ((!opt || !address_available(context, mess->yiaddr)) &&
!address_allocate(context, dhcp_configs, &mess->yiaddr))
- {
- syslog(LOG_WARNING, "address pool exhausted");
- return 0;
- }
-
+ message = "no address available";
+
+ log_packet("DISCOVER", opt ? &mess->yiaddr : NULL, mess->chaddr, iface_name, message);
+ if (message)
+ return 0;
+
bootp_option_put(mess, dhcp_file, dhcp_sname);
mess->siaddr = dhcp_next_server;
p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPOFFER);
p = option_put(p, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(iface_addr.s_addr));
p = option_put(p, end, OPTION_LEASE_TIME, 4, expires_time);
p = do_req_options(context, p, end, req_options, dhcp_opts, domain_suffix,
- NULL, router, iface_addr, iface_mtu);
+ NULL, router, iface_addr, iface_mtu, netid);
p = option_put(p, end, OPTION_END, 0, 0);
log_packet("OFFER" , &mess->yiaddr, mess->chaddr, iface_name, NULL);
@@ -297,7 +317,7 @@ int dhcp_reply(struct dhcp_context *context,
if (!lease)
{
if (!address_available(context, mess->yiaddr) &&
- (!config || config->addr.s_addr == 0 || config->addr.s_addr != mess->yiaddr.s_addr))
+ (!have_config(config, CONFIG_ADDR) || config->addr.s_addr != mess->yiaddr.s_addr))
message = "address unavailable";
else if (!(lease = lease_allocate(clid, clid_len, mess->yiaddr)))
message = "no leases left";
@@ -319,6 +339,9 @@ int dhcp_reply(struct dhcp_context *context,
if ((mess->yiaddr.s_addr & context->netmask.s_addr) != (context->start.s_addr & context->netmask.s_addr))
message = "wrong network";
+ if (have_config(config, CONFIG_DISABLE))
+ message = "disabled";
+
log_packet("REQUEST", &mess->yiaddr, mess->chaddr, iface_name, NULL);
if (message)
@@ -354,17 +377,23 @@ int dhcp_reply(struct dhcp_context *context,
p = option_put(p, end, OPTION_T2, 4, ((renewal_time * 7)/8) - fuzz);
}
p = do_req_options(context, p, end, req_options, dhcp_opts, domain_suffix,
- hostname, router, iface_addr, iface_mtu);
+ hostname, router, iface_addr, iface_mtu, netid);
p = option_put(p, end, OPTION_END, 0, 0);
return p - (unsigned char *)mess;
case DHCPINFORM:
+ if (have_config(config, CONFIG_DISABLE))
+ {
+ log_packet("INFORM", &mess->ciaddr, mess->chaddr, iface_name, "ignored");
+ return 0;
+ }
+
log_packet("INFORM", &mess->ciaddr, mess->chaddr, iface_name, NULL);
p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
p = option_put(p, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(iface_addr.s_addr));
p = do_req_options(context, p, end, req_options, dhcp_opts, domain_suffix,
- hostname, router, iface_addr, iface_mtu);
+ hostname, router, iface_addr, iface_mtu, netid);
p = option_put(p, end, OPTION_END, 0, 0);
log_packet("ACK", &mess->ciaddr, mess->chaddr, iface_name, hostname);
@@ -524,11 +553,11 @@ static int in_list(unsigned char *list, int opt)
return 0;
}
-static struct dhcp_opt *option_find2(struct dhcp_context *context, struct dhcp_opt *opts, int opt)
+static struct dhcp_opt *option_find2(char *netid, struct dhcp_opt *opts, int opt)
{
for (; opts; opts = opts->next)
if (opts->opt == opt &&
- (!opts->netid || (context->netid && strcmp(opts->netid, context->netid) == 0)))
+ (!opts->netid || (netid && strcmp(opts->netid, netid) == 0)))
return opts;
return NULL;
}
@@ -540,7 +569,7 @@ static unsigned char *do_req_options(struct dhcp_context *context,
char *domainname, char *hostname,
struct in_addr router,
struct in_addr iface_addr,
- int iface_mtu)
+ int iface_mtu, char *netid)
{
int i;
@@ -553,24 +582,24 @@ static unsigned char *do_req_options(struct dhcp_context *context,
iface_mtu : DNSMASQ_PACKETSZ);
if (in_list(req_options, OPTION_NETMASK) &&
- !option_find2(context, config_opts, OPTION_NETMASK))
+ !option_find2(netid, config_opts, OPTION_NETMASK))
p = option_put(p, end, OPTION_NETMASK, INADDRSZ, ntohl(context->netmask.s_addr));
if (in_list(req_options, OPTION_BROADCAST) &&
- !option_find2(context, config_opts, OPTION_BROADCAST))
+ !option_find2(netid, config_opts, OPTION_BROADCAST))
p = option_put(p, end, OPTION_BROADCAST, INADDRSZ, ntohl(context->broadcast.s_addr));
if (in_list(req_options, OPTION_ROUTER) &&
- !option_find2(context, config_opts, OPTION_ROUTER))
+ !option_find2(netid, config_opts, OPTION_ROUTER))
p = option_put(p, end, OPTION_ROUTER, INADDRSZ,
ntohl(router.s_addr));
if (in_list(req_options, OPTION_DNSSERVER) &&
- !option_find2(context, config_opts, OPTION_DNSSERVER))
+ !option_find2(netid, config_opts, OPTION_DNSSERVER))
p = option_put(p, end, OPTION_DNSSERVER, INADDRSZ, ntohl(iface_addr.s_addr));
if (domainname && in_list(req_options, OPTION_DOMAINNAME) &&
- !option_find2(context, config_opts, OPTION_DOMAINNAME))
+ !option_find2(netid, config_opts, OPTION_DOMAINNAME))
p = option_put_string(p, end, OPTION_DOMAINNAME, domainname);
/* Note that we ignore attempts to set the hostname using
@@ -580,38 +609,49 @@ static unsigned char *do_req_options(struct dhcp_context *context,
for (i = 0; req_options[i] != OPTION_END; i++)
{
- struct dhcp_opt *opt = option_find2(context, config_opts, req_options[i]);
- if (req_options[i] != OPTION_HOSTNAME &&
- req_options[i] != OPTION_MAXMESSAGE &&
- opt && (p + opt->len + 3 < end))
+ struct dhcp_opt *opt;
+
+ if (req_options[i] == OPTION_HOSTNAME ||
+ req_options[i] == OPTION_MAXMESSAGE ||
+ !(opt = option_find2(netid, config_opts, req_options[i])) ||
+ (p + opt->len + 3 >= end))
+ continue;
+
+ /* For the options we have default values on
+ dhc-option=<optionno> means "don't include this option"
+ not "include a zero-length option" */
+ if (opt->len == 0 &&
+ (opt->opt == OPTION_NETMASK ||
+ opt->opt == OPTION_BROADCAST ||
+ opt->opt == OPTION_ROUTER ||
+ opt->opt == OPTION_DNSSERVER))
+ continue;
+
+ *(p++) = opt->opt;
+ *(p++) = opt->len;
+ if (opt->len == 0)
+ continue;
+
+ if (opt->is_addr)
{
- *(p++) = opt->opt;
- *(p++) = opt->len;
- if (opt->len != 0)
+ int j;
+ struct in_addr *a = (struct in_addr *)opt->val;
+ for (j = 0; j < opt->len; j+=INADDRSZ, a++)
{
- if (opt->is_addr)
- {
- int j;
- struct in_addr *a = (struct in_addr *)opt->val;
- for (j = 0; j < opt->len; j+=INADDRSZ, a++)
- {
- /* zero means "self" */
- if (a->s_addr == 0)
- memcpy(p, &iface_addr, INADDRSZ);
- else
- memcpy(p, a, INADDRSZ);
- p += INADDRSZ;
- }
- }
+ /* zero means "self" */
+ if (a->s_addr == 0)
+ memcpy(p, &iface_addr, INADDRSZ);
else
- {
- memcpy(p, opt->val, opt->len);
- p += opt->len;
- }
+ memcpy(p, a, INADDRSZ);
+ p += INADDRSZ;
}
}
- }
-
+ else
+ {
+ memcpy(p, opt->val, opt->len);
+ p += opt->len;
+ }
+ }
return p;
}