Compare commits

...
43 Commits
Author SHA1 Message Date
Simon Kelley 0793380b40 Implement dhcp-ignore-names and DNSMASQ_RELAY_ADDRESS for IPv6
Build DHCPv6 by default.
2012-02-14 20:55:25 +00:00
Simon Kelley 1adadf585d Tweak Makefile so it works again in BSD make.
First cut at man page changes for DHCPv6
2012-02-13 22:15:58 +00:00
Simon Kelley e5ffdb9c77 BUILD_DIR -> BUILDDIR, for consistency. 2012-02-13 14:28:13 +00:00
Simon Kelley 6da5201092 Merge branch 'makefile' 2012-02-13 12:55:33 +00:00
Simon Kelley b36ae19434 Add BUILD_DIR variable to makefile. 2012-02-13 12:54:34 +00:00
Simon Kelley 2307eac613 Fix code parsing --domain, broken whilst added IPv6 stuff. 2012-02-13 10:13:13 +00:00
Simon Kelley 127ea40ae7 Don't build DHCPv6 by default, except when build Debian package. 2012-02-11 22:14:52 +00:00
Simon Kelley 6aef600d48 Correct duid generate on FreeBSD 2012-02-11 22:01:50 +00:00
Simon Kelley 98d76a0326 Tweaks to fix compilation on FreeBSD. 2012-02-10 22:16:45 +00:00
Simon Kelley 6ea6dcf05b Update Debian readme for new build options and (belatedly) for the
introduction of dnsmasq-utils.
2012-02-10 21:26:52 +00:00
Simon Kelley 627797800d Report correct error if prefix in dhcp-range is less than 64
Update debian/rules to provide no_dhcp6 and use_lua build opts.
Log DHCPv6 information request packets.
2012-02-10 21:19:25 +00:00
Simon Kelley c6cc03ed0c Merge branch 'dhcpv6' 2012-02-10 17:36:20 +00:00
Simon Kelley 3d7b550f52 missed DHCP6 conditional compilation. 2012-02-10 17:35:37 +00:00
Simon Kelley 751d6f4ae6 Allow the TFP server or boot server in --pxe-service, to
be a domain name instead of an IP address. This allows for
 round-robin to multiple servers, in the same way as
 --dhcp-boot.
2012-02-10 15:24:51 +00:00
Simon Kelley a5c72ab51d DHCPv6 vendor class option includes an enterprise number. Handle that. 2012-02-10 13:42:47 +00:00
Simon Kelley 9bbc88762b Fix shell scripting bug in bug scripts. 2012-02-09 21:33:09 +00:00
Simon Kelley ceae00dddf lease script should work with IPv6 now. 2012-02-09 21:28:14 +00:00
Simon Kelley 3634c54e8d dhcp-ignore and dhcp-match implemented for DHCPv6 now. 2012-02-08 14:22:37 +00:00
Simon Kelley d74942a03d IPv6 address range parsing for --domain.
Counted string DHCP option type printing
2012-02-07 20:51:56 +00:00
Simon Kelley 70c5e3e076 DHCPDECLINE handling, domain handling, more complete address selection. 2012-02-06 22:05:15 +00:00
Simon Kelley 4cb1b32009 Clean compile, basic DHCPv6 functionality is there.
TODO
     hostname handling.
     update DHCP6 configs from dns
     parse domain=<domain>,<IPv6 range>
     pretty-print counted string options.
     DECLINE messages
     lease-script fro DHCPv6
2012-02-06 14:30:41 +00:00
Simon Kelley 3268e90f5e Make default NO_DHCP6 and remove compiler warning. 2012-01-22 16:15:02 +00:00
Simon Kelley e98170816a Merge branch 'dhcpv6' 2012-01-22 16:07:22 +00:00
Simon Kelley 52b92f4db8 It compiles and it allocates a lease! No renewals yet. 2012-01-22 16:05:15 +00:00
Simon Kelley a2761754da Fix problem if dnsmasq is started without the stdin,
stdout and stderr file descriptors open. This can manifest
itself as 100% CPU use. Thanks to Chris Moore for finding
this.
2012-01-18 16:07:21 +00:00
Simon Kelley 805a11345c We don't need to check that a cache record is F_HOST in the
duplicate address code, since we're now searching a temporary hash
which holds only F_HOST records.
2012-01-13 11:51:46 +00:00
Simon Kelley 1ab62aec37 Further tuning of the fast hostfile reading code.
Use the packet buffer for hash-buckets, better hash function.
2012-01-12 11:33:16 +00:00
Simon Kelley 915363f976 Tweaks to hostfile performance work. 2012-01-11 22:00:48 +00:00
Simon Kelley 205fafa577 Improve performance when reading large hostfiles. 2012-01-11 21:31:51 +00:00
Simon Kelley be2daf4ad5 Enable DHCPv6 compilation 2012-01-07 17:51:57 +00:00
Simon Kelley 8ecfaa4adf Tidied up usage strings. 2012-01-07 15:29:48 +00:00
Simon Kelley 03bfcf6462 FAQ and example config additions for Windows 7 WPAD problem. 2012-01-07 14:37:37 +00:00
Simon Kelley 39bec5ff32 Remove duplicate tags in data supplied to lease script. 2012-01-06 22:36:58 +00:00
Simon Kelley 246839d64a Minimal update of doc.html - remove broken links and add git info. 2012-01-06 20:39:54 +00:00
Simon Kelley 3862deb398 Debian bug #654897 2012-01-06 20:16:07 +00:00
Simon Kelley 5954608577 Updated copyright notices. Happy new year! 2012-01-06 20:02:04 +00:00
Simon Kelley 984d2fded6 CHANGELOG update 2012-01-06 14:34:32 +00:00
Simon Kelley a4f04ed45a Generate version string from git automatically 2012-01-06 11:47:02 +00:00
Simon Kelley 07736e8dcb VERSION file 2012-01-05 22:00:08 +00:00
Simon Kelley 00fc082d68 bump version in config.h 2012-01-05 21:42:12 +00:00
Simon Kelley c72daea868 Accumulated 2.60 changes going into git 2012-01-05 21:33:27 +00:00
Simon Kelley 74c95c2542 import of dnsmasq-2.59.tar.gz 2012-01-05 17:31:15 +00:00
Simon Kelley 7de060b08d import of dnsmasq-2.58.tar.gz 2012-01-05 17:31:15 +00:00
71 changed files with 14312 additions and 6897 deletions

No files matched your search

+170
View File
@@ -1,3 +1,173 @@
version 2.60
Fix compilation problem in Mac OS X Lion. Thanks to Olaf
Flebbe for the patch.
Fix DHCP when using --listen-address with an IP address
which is not the primary address of an interface.
Add --dhcp-client-update option.
Add Lua integration. Dnsmasq can now execute a DHCP
lease-change script written in Lua. This needs to be
enabled at compile time by setting HAVE_LUASCRIPT in
src/config.h or running "make COPTS=-DHAVE_LUASCRIPT"
Thanks to Jan-Piet Mens for the idea and proof-of-concept
implementation.
Tidied src/config.h to distinguish between
platform-dependent compile-time options which are selected
automatically, and builder-selectable compile time
options. Document the latter better, and describe how to
set them from the make command line.
Tidied up IPPROTO_IP/SOL_IP (and IPv6 equivalent)
confusion. IPPROTO_IP works everywhere now.
Set TOS on DHCP sockets, this improves things on busy
wireless networks. Thanks to Dave Taht for the patch.
Determine VERSION automatically based on git magic:
release tags or hash values.
Improve start-up speed when reading large hosts files
containing many distinct addresses.
Fix problem if dnsmasq is started without the stdin,
stdout and stderr file descriptors open. This can manifest
itself as 100% CPU use. Thanks to Chris Moore for finding
this.
Fix shell-scripting bug in bld/pkg-wrapper. Thanks to
Mark Mitchell for the patch.
Allow the TFP server or boot server in --pxe-service, to
be a domain name instead of an IP address. This allows for
round-robin to multiple servers, in the same way as
--dhcp-boot. A good suggestion from Cristiano Cumer.
Support BUILDDIR variable in the Makefile. Allows
builds from multiple architectures in the same tree with eg
make BUILDDIR=linux
If BUILDDIR is not set, compilation happens in the src
directory, as before. Suggestion from Mark Mitchell.
First cut at supporting DHCPv6. Support is pretty much
there for the sort of things the existing v4 server does,
including tags, options, static addresses and relay
support. Missing is prefix delegation. This is lightly
tested alpha code, it is NOT YET PRODUCTION READY. Test
reports would be greatly valued.
version 2.59
Fix regression in 2.58 which caused failure to start up
with some combinations of dnsmasq config and IPv6 kernel
network config. Thanks to Brielle Bruns for the bug
report.
Improve dnsmasq's behaviour when network interfaces are
still doing duplicate address detection (DAD). Previously,
dnsmasq would wait up to 20 seconds at start-up for the
DAD state to terminate. This is broken for bridge
interfaces on recent Linux kernels, which don't start DAD
until the bridge comes up, and so can take arbitrary
time. The new behaviour lets dnsmasq poll for an arbitrary
time whilst providing service on other interfaces. Thanks
to Stephen Hemminger for pointing out the problem.
version 2.58
Provide a definition of the SA_SIZE macro where it's
missing. Fixes build failure on openBSD.
Don't include a zero terminator at the end of messages
sent to /dev/log when /dev/log is a datagram socket.
Thanks to Didier Rabound for spotting the problem.
Add --dhcp-sequential-ip flag, to force allocation of IP
addresses in ascending order. Note that the default
pseudo-random mode is in general better but some
server-deployment applications need this.
Fix problem where a server-id of 0.0.0.0 is sent to a
client when a dhcp-relay is in use if a client renews a
lease after dnsmasq restart and before any clients on the
subnet get a new lease. Thanks to Mike Ruiz for assistance
in chasing this one down.
Don't return NXDOMAIN to an AAAA query if we have CNAME
which points to an A record only: NODATA is the correct
reply in this case. Thanks to Tom Fernandes for spotting
the problem.
Relax the need to supply a netmask in --dhcp-range for
networks which use a DHCP relay. Whilst this is still
desireable, in the absence of a netmask dnsmasq will use
a default based on the class (A, B, or C) of the address.
This should at least remove a cause of mysterious failure
for people using RFC1918 addresses and relays.
Add support for Linux conntrack connection marking. If
enabled with --conntrack, the connection mark for incoming
DNS queries will be copied to the outgoing connections
used to answer those queries. This allows clever firewall
and accounting stuff. Only available if dnsmasq is
compiled with HAVE_CONNTRACK and adds a dependency on
libnetfilter-conntrack. Thanks to Ed Wildgoose for the
initial idea, testing and sponsorship of this function.
Provide a sane error message when someone attempts to
match a tag in --dhcp-host.
Tweak the behaviour of --domain-needed, to avoid problems
with recursive nameservers downstream of dnsmasq. The new
behaviour only stops A and AAAA queries, and returns
NODATA rather than NXDOMAIN replies.
Efficiency fix for very large DHCP configurations, thanks
to James Gartrell and Mike Ruiz for help with this.
Allow the TFTP-server address in --dhcp-boot to be a
domain-name which is looked up in /etc/hosts. This can
give multiple IP addresses which are used round-robin,
thus doing TFTP server load-balancing. Thanks to Sushil
Agrawal for the patch.
When two tagged dhcp-options for a particular option
number are both valid, use the one which is valid without
a tag from the dhcp-range. Allows overriding of the value
of a DHCP option for a particular host as well as
per-network values. So
--dhcp-range=set:interface1,......
--dhcp-host=set:myhost,.....
--dhcp-option=tag:interface1,option:nis-domain,"domain1"
--dhcp-option=tag:myhost,option:nis-domain,"domain2"
will set the NIS-domain to domain1 for hosts in the range, but
override that to domain2 for a particular host.
Fix bug which resulted in truncated files and timeouts for
some TFTP transfers. The bug only occurs with netascii
transfers and needs an unfortunate relationship between
file size, blocksize and the number of newlines in the
last block before it manifests itself. Many thanks to
Alkis Georgopoulos for spotting the problem and providing
a comprehensive test-case.
Fix regression in TFTP server on *BSD platforms introduced
in version 2.56, due to confusion with sockaddr
length. Many thanks to Loïc Pefferkorn for finding this.
Support scope-ids in IPv6 addresses of nameservers from
/etc/resolv.conf and in --server options. Eg
nameserver fe80::202:a412:4512:7bbf%eth0 or
server=fe80::202:a412:4512:7bbf%eth0. Thanks to
Michael Stapelberg for the suggestion.
Update Polish translation, thanks to Jan Psota.
Update French translation. Thanks to Gildas Le Nadan.
version 2.57 version 2.57
Add patches to allow build under Android. Add patches to allow build under Android.
+12 -2
View File
@@ -303,7 +303,7 @@ A: Yes, new releases of dnsmasq are always announced through
Q: What does the dhcp-authoritative option do? Q: What does the dhcp-authoritative option do?
A: See http://www.isc.org/index.pl?/sw/dhcp/authoritative.php - that's A: See http://www.isc.org/files/auth.html - that's
for the ISC daemon, but the same applies to dnsmasq. for the ISC daemon, but the same applies to dnsmasq.
Q: Why does my Gentoo box pause for a minute before getting a new Q: Why does my Gentoo box pause for a minute before getting a new
@@ -381,7 +381,7 @@ A: Probably the nameserver is an authoritative nameserver for a
Q: Does the dnsmasq DHCP server probe addresses before allocating Q: Does the dnsmasq DHCP server probe addresses before allocating
them, as recommended in RFC2131? them, as recommended in RFC2131?
A: Yes, dynmaically allocated IP addresses are checked by sending an A: Yes, dynamically allocated IP addresses are checked by sending an
ICMP echo request (ping). If a reply is received, then dnsmasq ICMP echo request (ping). If a reply is received, then dnsmasq
assumes that the address is in use, and attempts to allocate an assumes that the address is in use, and attempts to allocate an
different address. The wait for a reply is between two and three different address. The wait for a reply is between two and three
@@ -467,8 +467,18 @@ A: The DHCP client on windows Vista (and possibly later versions)
work). work).
Q: DHCP doesn't work with windows 7 but everything else is fine.
A: There seems to be a problem if Windows 7 doesn't get a value for
DHCP option 252 in DHCP packets it gets from the server. The
symtoms have beeen variously reported as continual DHCPINFORM
requests in an attempt to get an option-252, or even ignoring DHCP
offers completely (and failing to get an IP address) if there is no
option-252 supplied. DHCP option 252 is for WPAD, WWW Proxy
Auto Detection and if you don't want or need to use that, then
simplest fix seems to be to supply an empty option with:
dhcp-option=252,"\n"
+47 -31
View File
@@ -1,4 +1,4 @@
# dnsmasq is Copyright (c) 2000-2011 Simon Kelley # dnsmasq is Copyright (c) 2000-2012 Simon Kelley
# #
# This program is free software; you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@@ -18,78 +18,94 @@ BINDIR = ${PREFIX}/sbin
MANDIR = ${PREFIX}/share/man MANDIR = ${PREFIX}/share/man
LOCALEDIR = ${PREFIX}/share/locale LOCALEDIR = ${PREFIX}/share/locale
BUILDDIR = $(SRC)
CFLAGS = -Wall -W -O2
#################################################################
PKG_CONFIG = pkg-config PKG_CONFIG = pkg-config
INSTALL = install INSTALL = install
MSGMERGE = msgmerge MSGMERGE = msgmerge
MSGFMT = msgfmt MSGFMT = msgfmt
XGETTEXT = xgettext XGETTEXT = xgettext
CFLAGS = -Wall -W -O2
#################################################################
SRC = src SRC = src
PO = po PO = po
MAN = man MAN = man
DNSMASQ_CFLAGS=`echo $(COPTS) | ../bld/pkg-wrapper HAVE_DBUS $(PKG_CONFIG) --cflags dbus-1` DBUS_CFLAGS=`echo $(COPTS) | ../bld/pkg-wrapper HAVE_DBUS $(PKG_CONFIG) --cflags dbus-1`
DNSMASQ_LIBS= `echo $(COPTS) | ../bld/pkg-wrapper HAVE_DBUS $(PKG_CONFIG) --libs dbus-1` DBUS_LIBS= `echo $(COPTS) | ../bld/pkg-wrapper HAVE_DBUS $(PKG_CONFIG) --libs dbus-1`
IDN_CFLAGS=`echo $(COPTS) | ../bld/pkg-wrapper HAVE_IDN $(PKG_CONFIG) --cflags libidn` IDN_CFLAGS= `echo $(COPTS) | ../bld/pkg-wrapper HAVE_IDN $(PKG_CONFIG) --cflags libidn`
IDN_LIBS= `echo $(COPTS) | ../bld/pkg-wrapper HAVE_IDN $(PKG_CONFIG) --libs libidn` IDN_LIBS= `echo $(COPTS) | ../bld/pkg-wrapper HAVE_IDN $(PKG_CONFIG) --libs libidn`
CT_CFLAGS= `echo $(COPTS) | ../bld/pkg-wrapper HAVE_CONNTRACK $(PKG_CONFIG) --cflags libnetfilter_conntrack`
CT_LIBS= `echo $(COPTS) | ../bld/pkg-wrapper HAVE_CONNTRACK $(PKG_CONFIG) --libs libnetfilter_conntrack`
LUA_CFLAGS= `echo $(COPTS) | ../bld/pkg-wrapper HAVE_LUASCRIPT $(PKG_CONFIG) --cflags lua5.1`
LUA_LIBS= `echo $(COPTS) | ../bld/pkg-wrapper HAVE_LUASCRIPT $(PKG_CONFIG) --libs lua5.1`
SUNOS_LIBS= `if uname | grep SunOS 2>&1 >/dev/null; then echo -lsocket -lnsl -lposix4; fi` SUNOS_LIBS= `if uname | grep SunOS 2>&1 >/dev/null; then echo -lsocket -lnsl -lposix4; fi`
VERSION= -DVERSION='\"`../bld/get-version`\"'
OBJS = cache.o rfc1035.o util.o option.o forward.o network.o \ OBJS = cache.o rfc1035.o util.o option.o forward.o network.o \
dnsmasq.o dhcp.o lease.o rfc2131.o netlink.o dbus.o bpf.o \ dnsmasq.o dhcp.o lease.o rfc2131.o netlink.o dbus.o bpf.o \
helper.o tftp.o log.o helper.o tftp.o log.o conntrack.o dhcp6.o rfc3315.o dhcp-common.o
all : HDRS = dnsmasq.h config.h dhcp_protocol.h dhcp6_protocol.h dns_protocol.h
@cd $(SRC) && $(MAKE) \
BUILD_CFLAGS="$(DNSMASQ_CFLAGS) $(IDN_CFLAGS)" \
BUILD_LIBS="$(DNSMASQ_LIBS) $(IDN_LIBS) $(SUNOS_LIBS)" \ all : $(BUILDDIR)
@cd $(BUILDDIR) && $(MAKE) \
BUILD_CFLAGS="$(VERSION) $(DBUS_CFLAGS) $(IDN_CFLAGS) $(CT_CFLAGS) $(LUA_CFLAGS)" \
BUILD_LIBS="$(DBUS_LIBS) $(IDN_LIBS) $(CT_LIBS) $(LUA_LIBS) $(SUNOS_LIBS)" \
-f ../Makefile dnsmasq -f ../Makefile dnsmasq
clean : clean :
rm -f *~ $(SRC)/*.mo contrib/*/*~ */*~ $(SRC)/*.pot rm -f *~ $(BUILDDIR)/*.mo contrib/*/*~ */*~ $(BUILDDIR)/*.pot
rm -f $(SRC)/*.o $(SRC)/dnsmasq.a $(SRC)/dnsmasq core */core rm -f $(BUILDDIR)/*.o $(BUILDDIR)/dnsmasq.a $(BUILDDIR)/dnsmasq core */core
install : all install-common install : all install-common
install-common : install-common :
$(INSTALL) -d $(DESTDIR)$(BINDIR) -d $(DESTDIR)$(MANDIR)/man8 $(INSTALL) -d $(DESTDIR)$(BINDIR) -d $(DESTDIR)$(MANDIR)/man8
$(INSTALL) -m 644 $(MAN)/dnsmasq.8 $(DESTDIR)$(MANDIR)/man8 $(INSTALL) -m 644 $(MAN)/dnsmasq.8 $(DESTDIR)$(MANDIR)/man8
$(INSTALL) -m 755 $(SRC)/dnsmasq $(DESTDIR)$(BINDIR) $(INSTALL) -m 755 $(BUILDDIR)/dnsmasq $(DESTDIR)$(BINDIR)
all-i18n : all-i18n : $(BUILDDIR)
@cd $(SRC) && $(MAKE) \ @cd $(BUILDDIR) && $(MAKE) \
I18N=-DLOCALEDIR='\"$(LOCALEDIR)\"' \ I18N=-DLOCALEDIR=\'\"$(LOCALEDIR)\"\' \
BUILD_CFLAGS="$(DNSMASQ_CFLAGS) `$(PKG_CONFIG) --cflags libidn`" \ BUILD_CFLAGS="$(VERSION) $(DBUS_CFLAGS) $(CT_CFLAGS) $(LUA_CFLAGS) `$(PKG_CONFIG) --cflags libidn`" \
BUILD_LIBS="$(DNSMASQ_LIBS) $(SUNOS_LIBS) `$(PKG_CONFIG) --libs libidn`" \ BUILD_LIBS="$(DBUS_LIBS) $(CT_LIBS) $(LUA_LIBS) $(SUNOS_LIBS) `$(PKG_CONFIG) --libs libidn`" \
-f ../Makefile dnsmasq -f ../Makefile dnsmasq
@cd $(PO); for f in *.po; do \ @cd $(PO); for f in *.po; do \
cd ../$(SRC) && $(MAKE) \ cd ../$(BUILDDIR) && $(MAKE) \
-f ../Makefile $${f%.po}.mo; \ -f ../Makefile $${f%.po}.mo; \
done done
install-i18n : all-i18n install-common install-i18n : all-i18n install-common
cd $(SRC); ../bld/install-mo $(DESTDIR)$(LOCALEDIR) $(INSTALL) cd $(BUILDDIR); ../bld/install-mo $(DESTDIR)$(LOCALEDIR) $(INSTALL)
cd $(MAN); ../bld/install-man $(DESTDIR)$(MANDIR) $(INSTALL) cd $(MAN); ../bld/install-man $(DESTDIR)$(MANDIR) $(INSTALL)
merge : merge : $(BUILDDIR)
@cd $(SRC) && $(MAKE) -f ../Makefile dnsmasq.pot @cd $(BUILDDIR) && $(MAKE) -f ../Makefile dnsmasq.pot
@cd $(PO); for f in *.po; do \ @cd $(PO); for f in *.po; do \
echo -n msgmerge $$f && $(MSGMERGE) --no-wrap -U $$f ../$(SRC)/dnsmasq.pot; \ echo -n msgmerge $$f && $(MSGMERGE) --no-wrap -U $$f ../$(BUILDDIR)/dnsmasq.pot; \
done done
$(BUILDDIR):
mkdir $(BUILDDIR)
# rules below are targets in recusive makes with cwd=$(SRC) # rules below are targets in recusive makes with cwd=$(SRC)
.c.o: $(OBJS:.o=.c) $(HDRS):
$(CC) $(CFLAGS) $(COPTS) $(I18N) $(BUILD_CFLAGS) $(RPM_OPT_FLAGS) -c $< ln -s ../$(SRC)/$@ .
dnsmasq : $(OBJS) .c.o:
$(CC) $(CFLAGS) $(COPTS) $(I18N) $(BUILD_CFLAGS) $(RPM_OPT_FLAGS) -c $<
dnsmasq : $(HDRS) $(OBJS)
$(CC) $(LDFLAGS) -o $@ $(OBJS) $(BUILD_LIBS) $(LIBS) $(CC) $(LDFLAGS) -o $@ $(OBJS) $(BUILD_LIBS) $(LIBS)
dnsmasq.pot : $(OBJS:.o=.c) dnsmasq.h config.h dnsmasq.pot : $(OBJS:.o=.c) $(HDRS)
$(XGETTEXT) -d dnsmasq --foreign-user --omit-header --keyword=_ -o $@ -i $(OBJS:.o=.c) $(XGETTEXT) -d dnsmasq --foreign-user --omit-header --keyword=_ -o $@ -i $(OBJS:.o=.c)
%.mo : ../po/%.po dnsmasq.pot %.mo : ../po/%.po dnsmasq.pot
+1
View File
@@ -0,0 +1 @@
$Format:%d$
+2 -1
View File
@@ -6,7 +6,8 @@ include $(CLEAR_VARS)
LOCAL_SRC_FILES := bpf.c cache.c dbus.c dhcp.c dnsmasq.c \ LOCAL_SRC_FILES := bpf.c cache.c dbus.c dhcp.c dnsmasq.c \
forward.c helper.c lease.c log.c \ forward.c helper.c lease.c log.c \
netlink.c network.c option.c rfc1035.c \ netlink.c network.c option.c rfc1035.c \
rfc2131.c tftp.c util.c rfc2131.c tftp.c util.c conntrack.c \
dhcp6.c rfc3315.c dhcp-common.c
LOCAL_MODULE := dnsmasq LOCAL_MODULE := dnsmasq
+31
View File
@@ -0,0 +1,31 @@
#!/bin/sh
# Determine the version string to build into a binary.
# When building in the git repository, we can use the output
# of "git describe" which gives an unequivocal answer.
#
# Failing that, we use the contents of the VERSION file
# which has a set of references substituted into it by git.
# If we can find one which matches $v[0-9].* then we assume it's
# a version-number tag, else we just use the whole string.
# we're called with pwd == TLD
cd ..
if which git >/dev/null 2>&1 && [ -d .git ]; then
git describe
elif grep '\$Format:%d\$' VERSION >/dev/null 2>&1; then
# unsubstituted VERSION, but no git available.
echo UNKNOWN
else
vers=`cat VERSION | sed 's/[(), ]/\n/ g' | grep -m 1 $v[0-9]`
if [ $? -eq 0 ]; then
echo ${vers#v}
else
cat VERSION
fi
fi
exit 0
+2 -2
View File
@@ -3,8 +3,8 @@
search=$1 search=$1
shift shift
if grep "^\#.*define.*$search" config.h 2>&1 >/dev/null || \ if grep "^\#[[:space:]]*define[[:space:]]*$search" config.h >/dev/null 2>&1 || \
grep $search 2>&1 >/dev/null ; then grep $search >/dev/null 2>&1; then
exec $* exec $*
fi fi
+54
View File
@@ -0,0 +1,54 @@
Linux iptables includes that ability to mark individual network packets
with a "firewall mark". Additionally there is a component called
"conntrack" which tries to string sequences of related packets together
into a "connection" (it even relates sequences of UDP and ICMP packets).
There is a related mark for a connection called a "connection mark".
Marks can be copied freely between the firewall and connection marks
Using these two features it become possible to tag all related traffic
in arbitrary ways, eg authenticated users, traffic from a particular IP,
port, etc. Unfortunately any kind of "proxy" breaks this relationship
because network packets go in one side of the proxy and a completely new
connection comes out of the other side. However, sometimes, we want to
maintain that relationship through the proxy and continue the connection
mark on packets upstream of our proxy
DNSMasq includes such a feature enabled by the --conntrack
option. This allows, for example, using iptables to mark traffic from
a particular IP, and that mark to be persisted to requests made *by*
DNSMasq. Such a feature could be useful for bandwidth accounting,
captive portals and the like. Note a similar feature has been
implemented in Squid 2.2
As an example consider the following iptables rules:
1) iptables -t mangle -A PREROUTING -j CONNMARK --restore-mark
2) iptables -t mangle -A PREROUTING -m mark --mark 0 -s 192.168.111.137
-j MARK --set-mark 137
3) iptables -t mangle -A PREROUTING -j CONNMARK --save-mark
4) iptables -t mangle -A OUTPUT -m mark ! --mark 0 -j CONNMARK --save-mark
1-3) are all applied to the PREROUTING table and affect all packets
entering the firewall.
1) copies any existing connection mark into the firewall mark. 2) Checks
the packet not already marked and if not applies an arbitrary mark based
on IP address. 3) Saves the firewall mark back to the connection mark
(which will persist it across related packets)
4) is applied to the OUTPUT table, which is where we first see packets
generated locally. DNSMasq will have already copied the firewall mark
from the request, across to the new packet, and so all that remains is
for iptables to copy it to the connection mark so it's persisted across
packets.
Note: iptables can be quite confusing to the beginner. The following
diagram is extremely helpful in understanding the flows
http://linux-ip.net/nf/nfk-traversal.png
Additionally the following URL contains a useful "starting guide" on
linux connection tracking/marking
http://home.regit.org/netfilter-en/netfilter-connmark/
+16
View File
@@ -0,0 +1,16 @@
Hello,
I created a systemd service file for dnsmasq.
systemd is a sysvinit replacement (see [1] for more information).
One of the goals of systemd is to encourage standardization between different
distributions. This means, while I also submitted a ticket in Debian GNU/Linux,
I would like to ask you to accept this service file as the upstream
distributor, so that other distributions can use the same service file and
don’t have to ship their own.
Please include this file in your next release (just like in init script).
[1] http://en.wikipedia.org/wiki/Systemd
+12
View File
@@ -0,0 +1,12 @@
[Unit]
Description=A lightweight DHCP and caching DNS server
[Service]
Type=dbus
BusName=uk.org.thekelleys.dnsmasq
ExecStartPre=/usr/sbin/dnsmasq --test
ExecStart=/usr/sbin/dnsmasq -k
ExecReload=/bin/kill -HUP $MAINPID
[Install]
WantedBy=multi-user.target
+23
View File
@@ -0,0 +1,23 @@
.TH DHCP_LEASE_TIME 1
.SH NAME
dhcp_lease_time \- Query remaining time of a lease on a the local dnsmasq DHCP server.
.SH SYNOPSIS
.B dhcp_lease_time <address>
.SH "DESCRIPTION"
Send a DHCPINFORM message to a dnsmasq server running on the local host
and print (to stdout) the time remaining in any lease for the given
address. The time is given as string printed to stdout.
If an error occurs or no lease exists for the given address,
nothing is sent to stdout a message is sent to stderr and a
non-zero error code is returned.
Requires dnsmasq 2.40 or later and may not work with other DHCP servers.
The address argument is a dotted-quad IP addresses and mandatory.
.SH SEE ALSO
.BR dnsmasq (8)
.SH AUTHOR
This manual page was written by Simon Kelley <simon@thekelleys.org.uk>.
+35
View File
@@ -0,0 +1,35 @@
.TH DHCP_RELEASE 1
.SH NAME
dhcp_release \- Release a DHCP lease on a the local dnsmasq DHCP server.
.SH SYNOPSIS
.B dhcp_release <interface> <address> <MAC address> <client_id>
.SH "DESCRIPTION"
A utility which forces the DHCP server running on this machine to release a
DHCP lease.
.PP
Send a DHCPRELEASE message via the specified interface to tell the
local DHCP server to delete a particular lease.
The interface argument is the interface in which a DHCP
request _would_ be received if it was coming from the client,
rather than being faked up here.
The address argument is a dotted-quad IP addresses and mandatory.
The MAC address is colon separated hex, and is mandatory. It may be
prefixed by an address-type byte followed by -, eg
10-11:22:33:44:55:66
but if the address-type byte is missing it is assumed to be 1, the type
for ethernet. This encoding is the one used in dnsmasq lease files.
The client-id is optional. If it is "*" then it treated as being missing.
.SH NOTES
MUST be run as root - will fail otherwise.
.SH SEE ALSO
.BR dnsmasq (8)
.SH AUTHOR
This manual page was written by Simon Kelley <simon@thekelleys.org.uk>.
+1 -1
View File
@@ -178,7 +178,7 @@ static int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask)
return (a.s_addr & mask.s_addr) == (b.s_addr & mask.s_addr); return (a.s_addr & mask.s_addr) == (b.s_addr & mask.s_addr);
} }
static struct in_addr find_interface(struct in_addr client, int fd, int index) static struct in_addr find_interface(struct in_addr client, int fd, unsigned int index)
{ {
struct sockaddr_nl addr; struct sockaddr_nl addr;
struct nlmsghdr *h; struct nlmsghdr *h;
+1011
View File
File diff suppressed because it is too large. Load diff
+6
View File
@@ -0,0 +1,6 @@
/etc/init.d/dnsmasq
/etc/default/dnsmasq
/etc/dnsmasq.conf
/etc/resolvconf/update.d/dnsmasq
/etc/dbus-1/system.d/dnsmasq.conf
/etc/insserv.conf.d/dnsmasq
+41
View File
@@ -0,0 +1,41 @@
Source: dnsmasq
Section: net
Priority: optional
Build-depends: gettext, libnetfilter-conntrack-dev [linux-any], libidn11-dev, libdbus-1-dev (>=0.61)
Maintainer: Simon Kelley <simon@thekelleys.org.uk>
Standards-Version: 3.9.2
Package: dnsmasq
Architecture: all
Depends: netbase, adduser, dnsmasq-base(>= ${source:Version})
Suggests: resolvconf
Conflicts: resolvconf (<<1.15)
Description: Small caching DNS proxy and DHCP/TFTP server
Dnsmasq is a 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/TFTP for network booting of diskless machines.
Package: dnsmasq-base
Architecture: any
Depends: ${shlibs:Depends}
Conflicts: dnsmasq (<<2.41)
Description: Small caching DNS proxy and DHCP/TFTP server
This package contains the dnsmasq executable and documentation, but
not the infrastructure required to run it as a system daemon. For
that, install the dnsmasq package.
Package: dnsmasq-utils
Architecture: linux-any
Depends: ${shlibs:Depends}
Conflicts: dnsmasq (<<2.40)
Description: Utilities for manipulating DHCP leases
Small utilities to query a DHCP server's lease database and
remove leases from it. These programs are distributed with dnsmasq
and may not work correctly with other DHCP servers.
+21
View File
@@ -0,0 +1,21 @@
dnsmasq is Copyright (c) 2000-2012 Simon Kelley
It was downloaded from: http://www.thekelleys.org.uk/dnsmasq/
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, or
(at your option) version 3 dated 29 June, 2007.
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.
On Debian GNU/Linux systems, the text of the GNU general public license is
available in the file /usr/share/common-licenses/GPL-2 or
/usr/share/common-licenses/GPL-3
The Debian package of dnsmasq was created by Simon Kelley with assistance
from Lars Bahner.
+18
View File
@@ -0,0 +1,18 @@
<!DOCTYPE busconfig PUBLIC
"-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<policy user="root">
<allow own="uk.org.thekelleys.dnsmasq"/>
<allow send_destination="uk.org.thekelleys.dnsmasq"/>
</policy>
<policy user="dnsmasq">
<allow own="uk.org.thekelleys.dnsmasq"/>
<allow send_destination="uk.org.thekelleys.dnsmasq"/>
</policy>
<policy context="default">
<deny own="uk.org.thekelleys.dnsmasq"/>
<deny send_destination="uk.org.thekelleys.dnsmasq"/>
</policy>
</busconfig>
+33
View File
@@ -0,0 +1,33 @@
# This file has five functions:
# 1) to completely disable starting dnsmasq,
# 2) to set DOMAIN_SUFFIX by running `dnsdomainname`
# 3) to select an alternative config file
# by setting DNSMASQ_OPTS to --conf-file=<file>
# 4) to tell dnsmasq to read the files in /etc/dnsmasq.d for
# more configuration variables.
# 5) to stop the resolvconf package from controlling dnsmasq's
# idea of which upstream nameservers to use.
# For upgraders from very old versions, all the shell variables set
# here in previous versions are still honored by the init script
# so if you just keep your old version of this file nothing will break.
#DOMAIN_SUFFIX=`dnsdomainname`
#DNSMASQ_OPTS="--conf-file=/etc/dnsmasq.alt"
# Whether or not to run the dnsmasq daemon; set to 0 to disable.
ENABLED=1
# By default search this drop directory for configuration options.
# Libvirt leaves a file here to make the system dnsmasq play nice.
# Comment out this line if you don't want this. The dpkg-* are file
# endings which cause dnsmasq to skip that file. This avoids pulling
# in backups made by dpkg.
CONFIG_DIR=/etc/dnsmasq.d,.dpkg-dist,.dpkg-old,.dpkg-new
# If the resolvconf package is installed, dnsmasq will use its output
# rather than the contents of /etc/resolv.conf to find upstream
# nameservers. Uncommenting this line inhibits this behaviour.
# Not that including a "resolv-file=<filename>" line in
# /etc/dnsmasq.conf is not enough to override resolvconf if it is
# installed: the line below must be uncommented.
#IGNORE_RESOLVCONF=yes
Vendored
+266
View File
@@ -0,0 +1,266 @@
#!/bin/sh
### BEGIN INIT INFO
# Provides: dnsmasq
# Required-Start: $network $remote_fs $syslog
# Required-Stop: $network $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Description: DHCP and DNS server
### END INIT INFO
set +e # Don't exit on error status
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DAEMON=/usr/sbin/dnsmasq
NAME=dnsmasq
DESC="DNS forwarder and DHCP server"
# Most configuration options in /etc/default/dnsmasq are deprecated
# but still honoured.
ENABLED=1
if [ -r /etc/default/$NAME ]; then
. /etc/default/$NAME
fi
# Get the system locale, so that messages are in the correct language, and the
# charset for IDN is correct
if [ -r /etc/default/locale ]; then
. /etc/default/locale
export LANG
fi
test -x $DAEMON || exit 0
# Provide skeleton LSB log functions for backports which don't have LSB functions.
if [ -f /lib/lsb/init-functions ]; then
. /lib/lsb/init-functions
else
log_warning_msg () {
echo "${@}."
}
log_success_msg () {
echo "${@}."
}
log_daemon_msg () {
echo -n "${1}: $2"
}
log_end_msg () {
if [ $1 -eq 0 ]; then
echo "."
elif [ $1 -eq 255 ]; then
/bin/echo -e " (warning)."
else
/bin/echo -e " failed!"
fi
}
fi
# RESOLV_CONF:
# If the resolvconf package is installed then use the resolv conf file
# that it provides as the default. Otherwise use /etc/resolv.conf as
# the default.
#
# If IGNORE_RESOLVCONF is set in /etc/default/dnsmasq or an explicit
# filename is set there then this inhibits the use of the resolvconf-provided
# information.
#
# Note that if the resolvconf package is installed it is not possible to
# override it just by configuration in /etc/dnsmasq.conf, it is necessary
# to set IGNORE_RESOLVCONF=yes in /etc/default/dnsmasq.
if [ ! "$RESOLV_CONF" ] &&
[ "$IGNORE_RESOLVCONF" != "yes" ] &&
[ -x /sbin/resolvconf ]
then
RESOLV_CONF=/var/run/dnsmasq/resolv.conf
fi
for INTERFACE in $DNSMASQ_INTERFACE; do
DNSMASQ_INTERFACES="$DNSMASQ_INTERFACES -i $INTERFACE"
done
for INTERFACE in $DNSMASQ_EXCEPT; do
DNSMASQ_INTERFACES="$DNSMASQ_INTERFACES -I $INTERFACE"
done
if [ ! "$DNSMASQ_USER" ]; then
DNSMASQ_USER="dnsmasq"
fi
start()
{
# Return
# 0 if daemon has been started
# 1 if daemon was already running
# 2 if daemon could not be started
# /var/run may be volatile, so we need to ensure that
# /var/run/dnsmasq exists here as well as in postinst
if [ ! -d /var/run/dnsmasq ]; then
mkdir /var/run/dnsmasq || return 2
chown dnsmasq:nogroup /var/run/dnsmasq || return 2
fi
start-stop-daemon --start --quiet --pidfile /var/run/dnsmasq/$NAME.pid --exec $DAEMON --test > /dev/null || return 1
start-stop-daemon --start --quiet --pidfile /var/run/dnsmasq/$NAME.pid --exec $DAEMON -- \
-x /var/run/dnsmasq/$NAME.pid \
${MAILHOSTNAME:+ -m $MAILHOSTNAME} \
${MAILTARGET:+ -t $MAILTARGET} \
${DNSMASQ_USER:+ -u $DNSMASQ_USER} \
${DNSMASQ_INTERFACES:+ $DNSMASQ_INTERFACES} \
${DHCP_LEASE:+ -l $DHCP_LEASE} \
${DOMAIN_SUFFIX:+ -s $DOMAIN_SUFFIX} \
${RESOLV_CONF:+ -r $RESOLV_CONF} \
${CACHESIZE:+ -c $CACHESIZE} \
${CONFIG_DIR:+ -7 $CONFIG_DIR} \
${DNSMASQ_OPTS:+ $DNSMASQ_OPTS} \
|| return 2
}
start_resolvconf()
{
# If interface "lo" is explicitly disabled in /etc/default/dnsmasq
# Then dnsmasq won't be providing local DNS, so don't add it to
# the resolvconf server set.
for interface in $DNSMASQ_EXCEPT
do
[ $interface = lo ] && return
done
if [ -x /sbin/resolvconf ] ; then
echo "nameserver 127.0.0.1" | /sbin/resolvconf -a lo.$NAME
fi
return 0
}
stop()
{
# Return
# 0 if daemon has been stopped
# 1 if daemon was already stopped
# 2 if daemon could not be stopped
# other if a failure occurred
start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile /var/run/dnsmasq/$NAME.pid --name $NAME
RETVAL="$?"
[ "$RETVAL" = 2 ] && return 2
return "$RETVAL"
}
stop_resolvconf()
{
if [ -x /sbin/resolvconf ] ; then
/sbin/resolvconf -d lo.$NAME
fi
return 0
}
status()
{
# Return
# 0 if daemon is running
# 1 if daemon is dead and pid file exists
# 3 if daemon is not running
# 4 if daemon status is unknown
start-stop-daemon --start --quiet --pidfile /var/run/dnsmasq/$NAME.pid --exec $DAEMON --test > /dev/null
case "$?" in
0) [ -e "/var/run/dnsmasq/$NAME.pid" ] && return 1 ; return 3 ;;
1) return 0 ;;
*) return 4 ;;
esac
}
case "$1" in
start)
test "$ENABLED" != "0" || exit 0
log_daemon_msg "Starting $DESC" "$NAME"
start
case "$?" in
0)
log_end_msg 0
start_resolvconf
exit 0
;;
1)
log_success_msg "(already running)"
exit 0
;;
*)
log_end_msg 1
exit 1
;;
esac
;;
stop)
stop_resolvconf
if [ "$ENABLED" != "0" ]; then
log_daemon_msg "Stopping $DESC" "$NAME"
fi
stop
RETVAL="$?"
if [ "$ENABLED" = "0" ]; then
case "$RETVAL" in
0) log_daemon_msg "Stopping $DESC" "$NAME"; log_end_msg 0 ;;
esac
exit 0
fi
case "$RETVAL" in
0) log_end_msg 0 ; exit 0 ;;
1) log_warning_msg "(not running)" ; exit 0 ;;
*) log_end_msg 1; exit 1 ;;
esac
;;
restart|force-reload)
test "$ENABLED" != "0" || exit 1
$DAEMON --test ${CONFIG_DIR:+ -7 $CONFIG_DIR} ${DNSMASQ_OPTS:+ $DNSMASQ_OPTS} >/dev/null 2>&1
if [ $? -ne 0 ]; then
NAME="configuration syntax check"
RETVAL="2"
else
stop_resolvconf
stop
RETVAL="$?"
fi
log_daemon_msg "Restarting $DESC" "$NAME"
case "$RETVAL" in
0|1)
sleep 2
start
case "$?" in
0)
log_end_msg 0
start_resolvconf
exit 0
;;
*)
log_end_msg 1
exit 1
;;
esac
;;
*)
log_end_msg 1
exit 1
;;
esac
;;
status)
log_daemon_msg "Checking $DESC" "$NAME"
status
case "$?" in
0) log_success_msg "(running)" ; exit 0 ;;
1) log_success_msg "(dead, pid file exists)" ; exit 1 ;;
3) log_success_msg "(not running)" ; exit 3 ;;
*) log_success_msg "(unknown)" ; exit 4 ;;
esac
;;
*)
echo "Usage: /etc/init.d/$NAME {start|stop|restart|force-reload|status}" >&2
exit 3
;;
esac
exit 0
+1
View File
@@ -0,0 +1 @@
$named dnsmasq
+49
View File
@@ -0,0 +1,49 @@
#!/bin/sh
set -e
# create a user to run as (code stolen from dovecot-common)
if [ "$1" = "configure" ]; then
if [ -z "`id -u dnsmasq 2> /dev/null`" ]; then
adduser --system --home /var/lib/misc --gecos "dnsmasq" \
--no-create-home --disabled-password \
--quiet dnsmasq || true
fi
# Make the directory where we keep the pid file - this
# has to be owned by "dnsmasq" do that the file can be unlinked.
if [ ! -d /var/run/dnsmasq ]; then
mkdir /var/run/dnsmasq
chown dnsmasq:nogroup /var/run/dnsmasq
fi
# handle new location of pidfile during an upgrade
if [ -e /var/run/dnsmasq.pid ]; then
mv /var/run/dnsmasq.pid /var/run/dnsmasq
fi
fi
if [ -x /etc/init.d/dnsmasq ]; then
update-rc.d dnsmasq defaults 15 85 >/dev/null
if [ "$1" = "configure" ] || [ "$1" = "abort-upgrade" ]; then
if [ -e /var/run/dnsmasq/dnsmasq.pid ]; then
ACTION=restart
else
ACTION=start
fi
if [ -x /usr/sbin/invoke-rc.d ] ; then
invoke-rc.d dnsmasq $ACTION || true
else
/etc/init.d/dnsmasq $ACTION || true
fi
fi
fi
# dpkg can botch the change of /usr/share/doc/dnsmasq from
# directory to symlink. Fix up here.
if [ ! -h /usr/share/doc/dnsmasq ] && { rmdir /usr/share/doc/dnsmasq; }; then
cd /usr/share/doc/
ln -s /usr/share/doc/dnsmasq-base dnsmasq
fi
+12
View File
@@ -0,0 +1,12 @@
#!/bin/sh
set -e
if [ purge = "$1" ]; then
update-rc.d dnsmasq remove >/dev/null
if [ -x "$(command -v deluser)" ]; then
deluser --quiet --system dnsmasq > /dev/null || true
else
echo >&2 "not removing dnsmasq system account because deluser command was not found"
fi
rm -rf /var/run/dnsmasq
fi
+14
View File
@@ -0,0 +1,14 @@
#!/bin/sh
set -e
if [ "$1" = "remove" ]; then
if [ -x /usr/sbin/invoke-rc.d ] ; then
invoke-rc.d dnsmasq stop || true
else
/etc/init.d/dnsmasq stop || true
fi
fi
exit 0
+76
View File
@@ -0,0 +1,76 @@
Notes on configuring dnsmasq as packaged for Debian.
(1) To configure dnsmasq edit /etc/dnsmasq.conf. The file is well
commented; see also the dnsmasq.8 man page for explanation of
the options. The file /etc/default/dnsmasq also exists but it
shouldn't need to be touched in most cases. To set up DHCP
options you might need to refer to a copy of RFC 2132. This is
available on Debian systems in the package doc-rfc-std as the file
/usr/share/doc/RFC/draft-standard/rfc2132.txt.gz .
(2) Installing the dnsmasq package also creates the directory
/etc/dnsmasq.d which is searched by dnsmasq for configuration file
fragments. This behaviour can be disabled by editing
/etc/default/dnsmasq.
(3) If the Debian resolvconf package is installed then, regardless
of what interface configuration daemons are employed, the list of
nameservers to which dnsmasq should forward queries can be found
in /var/run/dnsmasq/resolv.conf; also, 127.0.0.1 is listed as the
first nameserver address in /etc/resolv.conf. This works using the
default configurations of resolvconf and dnsmasq.
(4) In the absence of resolvconf, if you are using dhcpcd then
dnsmasq should read the list of nameservers from the automatically
generated file /etc/dhcpc/resolv.conf. You should list 127.0.0.1
as the first nameserver address in /etc/resolv.conf.
(5) In the absence of resolvconf, if you are using pppd then
dnsmasq should read the list of nameservers from the automatically
generated file /etc/ppp/resolv.conf. You should list 127.0.0.1
as the first nameserver address in /etc/resolv.conf.
(6) In the absence of resolvconf, dns-nameservers lines in
/etc/network/interfaces are ignored. If you do do not use
resolvconf, list 127.0.0.1 as the first nameserver address
in /etc/resolv.conf and configure your nameservers using
"server=<IP-address>" lines in /etc/dnsmasq.conf.
(7) If you run multiple DNS servers on a single machine, each
listening on a different interface, then it is necessary to use
the bind-interfaces option by uncommenting "bind-interfaces" in
/etc/dnsmasq.conf. This option stops dnsmasq from binding the
wildcard address and allows servers listening on port 53 on
interfaces not in use by dnsmasq to work. The Debian
libvirt package will add a configuration file in /etc/dnsmasq.d
which does this so that the "system" dnsmasq and "private" dnsmasq
instances started by libvirt do not clash.
(8) The following options are supported in DEB_BUILD_OPTIONS
noopt : compile without optimisation.
nostrip : don't remove symbols from binary.
nodocs : omit documentation.
notftp : omit TFTP support.
nodhcp : omit DHCP support.
nodhcp6 : omit DHCPv6 support.
noscript : omit lease-change script support.
use_lua : provide support for lease-change scripts written
in Lua.
noipv6 : omit IPv6 support.
nodbus : omit DBus support.
noconntrack : omit connection tracking support.
nortc : compile alternate mode suitable for systems without an RTC.
noi18n : omit translations and internationalisation support.
noidn : omit international domain name support, must be
combined with noi18n to be effective.
(9) Dnsmasq comes as three packages - dnsmasq-utils, dnsmasq-base and
dnsmasq. Dnsmasq-base provides the dnsmasq executable and
documentation (including this file). Dnsmasq, which depends on
dnsmasq-base, provides the init script and configuration
infrastructure. This file assumes that both are installed. It is
possible to install only dnsmasq-base and use dnsmasq as a
non-"system" daemon. Libvirt, for instance, does this.
Dnsmasq-utils provides the utilities dhcp_release and
dhcp_lease_time.
+7
View File
@@ -0,0 +1,7 @@
# All files in this directory will be read by dnsmasq as
# configuration files, except if their names end in
# ".dpkg-dist",".dpkg-old" or ".dpkg-new"
#
# This can be changed by editing /etc/default/dnsmasq
+70
View File
@@ -0,0 +1,70 @@
#!/bin/bash
#
# Script to update the resolver list for dnsmasq
#
# N.B. Resolvconf may run us even if dnsmasq is not running.
# If dnsmasq is installed then we go ahead and update
# the resolver list in case dnsmasq is started later.
#
# Assumption: On entry, PWD contains the resolv.conf-type files
#
# Requires bash because it uses a non-POSIX printf extension.
#
# Licensed under the GNU GPL. See /usr/share/common-licenses/GPL.
#
set -e
RUN_DIR="/var/run/dnsmasq"
RSLVRLIST_FILE="${RUN_DIR}/resolv.conf"
TMP_FILE="${RSLVRLIST_FILE}_new.$$"
[ -x /usr/sbin/dnsmasq ] || exit 0
[ -x /lib/resolvconf/list-records ] || exit 1
PATH=/bin:/sbin
report_err() { echo "$0: Error: $*" >&2 ; }
# Stores arguments (minus duplicates) in RSLT, separated by spaces
# Doesn't work properly if an argument itself contain whitespace
uniquify()
{
RSLT=""
while [ "$1" ] ; do
for E in $RSLT ; do
[ "$1" = "$E" ] && { shift ; continue 2 ; }
done
RSLT="${RSLT:+$RSLT }$1"
shift
done
}
if [ ! -d "$RUN_DIR" ] && ! mkdir --parents --mode=0755 "$RUN_DIR" ; then
report_err "Failed trying to create directory $RUN_DIR"
exit 1
fi
RSLVCNFFILES="$(/lib/resolvconf/list-records | sed -e '/^lo.dnsmasq$/d')"
NMSRVRS=""
if [ "$RSLVCNFFILES" ] ; then
uniquify $(sed -n -e 's/^[[:space:]]*nameserver[[:space:]]\+//p' $RSLVCNFFILES)
NMSRVRS="$RSLT"
fi
# Dnsmasq uses the mtime of $RSLVRLIST_FILE, with a resolution of one second,
# to detect changes in the file. This means that if a resolvconf update occurs
# within one second of the previous one then dnsmasq may fail to notice the
# more recent change. To work around this problem we sleep here to ensure
# that the new mtime is different.
if [ -f "$RSLVRLIST_FILE" ] && [ "$(ls -go --time-style='+%s' "$RSLVRLIST_FILE" | { read p h s t n ; echo "$t" ; })" = "$(date +%s)" ] ; then
sleep 1
fi
clean_up() { rm -f "$TMP_FILE" ; }
trap clean_up EXIT
: >| "$TMP_FILE"
for N in $NMSRVRS ; do echo "nameserver $N" >> "$TMP_FILE" ; done
mv -f "$TMP_FILE" "$RSLVRLIST_FILE"
+13
View File
@@ -0,0 +1,13 @@
#!/bin/sh
# Resolvconf packaging event hook script for the dnsmasq package
restart_dnsmasq() {
if which invoke-rc.d >/dev/null 2>&1 ; then
invoke-rc.d dnsmasq restart
elif [ -x /etc/init.d/dnsmasq ] ; then
/etc/init.d/dnsmasq restart
fi
}
case "$1" in
install) restart_dnsmasq ;;
esac
Vendored Executable
+194
View File
@@ -0,0 +1,194 @@
#!/usr/bin/make -f
# debian/rules file - for dnsmasq.
# Copyright 2001-2011 by Simon Kelley
# Based on the sample in the debian hello package which carries the following:
# Copyright 1994,1995 by Ian Jackson.
# I hereby give you perpetual unlimited permission to copy,
# modify and relicense this file, provided that you do not remove
# my name from the file itself. (I assert my moral right of
# paternity under the Copyright, Designs and Patents Act 1988.)
# This file may have to be extensively modified
package=dnsmasq-base
# policy manual, section 10.1
ifneq (,$(filter noopt,$(DEB_BUILD_OPTIONS)))
CFLAGS = -g -O0 -Wall -W
else
CFLAGS = -g -O2 -Wall -W
endif
COPTS =
TARGET = install-i18n
DEB_BUILD_ARCH_OS := $(shell dpkg-architecture -qDEB_BUILD_ARCH_OS)
ifeq (,$(filter nodbus,$(DEB_BUILD_OPTIONS)))
COPTS += -DHAVE_DBUS
endif
ifeq (,$(filter noconntrack,$(DEB_BUILD_OPTIONS)))
ifeq ($(DEB_BUILD_ARCH_OS),linux)
COPTS += -DHAVE_CONNTRACK
endif
endif
ifeq (,$(filter nodhcp6,$(DEB_BUILD_OPTIONS)))
COPTS += -DHAVE_DHCP6
endif
ifneq (,$(filter noipv6,$(DEB_BUILD_OPTIONS)))
COPTS += -DNO_IPV6
endif
ifneq (,$(filter notftp,$(DEB_BUILD_OPTIONS)))
COPTS += -DNO_TFTP
endif
ifneq (,$(filter nodhcp,$(DEB_BUILD_OPTIONS)))
COPTS += -DNO_DHCP
endif
ifneq (,$(filter noscript,$(DEB_BUILD_OPTIONS)))
COPTS += -DNO_SCRIPT
endif
ifneq (,$(filter nortc,$(DEB_BUILD_OPTIONS)))
COPTS += -DHAVE_BROKEN_RTC
endif
ifneq (,$(filter noi18n,$(DEB_BUILD_OPTIONS)))
TARGET = install
ifeq (,$(filter noidn, $(DEB_BUILD_OPTIONS)))
COPTS += -DHAVE_IDN
endif
endif
ifneq (,$(filter uselua,$(DEB_BUILD_OPTIONS)))
COPTS += -DHAVE_LUASCRIPT
endif
clean:
$(checkdir)
rm -rf debian/daemon debian/base debian/utils debian/*~ debian/files debian/substvars debian/utils-substvars
make clean
make -C contrib/wrt clean
binary-indep: checkroot
$(checkdir)
rm -rf debian/daemon
install -m 755 \
-d debian/daemon/DEBIAN \
-d debian/daemon/usr/share/doc \
-d debian/daemon/etc/init.d \
-d debian/daemon/etc/dnsmasq.d \
-d debian/daemon/etc/resolvconf/update.d \
-d debian/daemon/usr/lib/resolvconf/dpkg-event.d \
-d debian/daemon/etc/default \
-d debian/daemon/etc/dbus-1/system.d \
-d debian/daemon/lib/systemd/system \
-d debian/daemon/etc/insserv.conf.d
install -m 644 debian/conffiles debian/daemon/DEBIAN
install -m 755 debian/postinst debian/postrm debian/prerm debian/daemon/DEBIAN
install -m 755 debian/init debian/daemon/etc/init.d/dnsmasq
install -m 755 debian/resolvconf debian/daemon/etc/resolvconf/update.d/dnsmasq
install -m 755 debian/resolvconf-package debian/daemon/usr/lib/resolvconf/dpkg-event.d/dnsmasq
install -m 644 debian/default debian/daemon/etc/default/dnsmasq
install -m 644 dnsmasq.conf.example debian/daemon/etc/dnsmasq.conf
install -m 644 debian/readme.dnsmasq.d debian/daemon/etc/dnsmasq.d/README
install -m 644 debian/dbus.conf debian/daemon/etc/dbus-1/system.d/dnsmasq.conf
install -m 644 debian/systemd.service debian/daemon/lib/systemd/system/dnsmasq.service
install -m 644 debian/insserv debian/daemon/etc/insserv.conf.d/dnsmasq
ln -s $(package) debian/daemon/usr/share/doc/dnsmasq
cd debian/daemon && find . -type f ! -regex '.*DEBIAN/.*' -printf '%P\0' | xargs -r0 md5sum > DEBIAN/md5sums
dpkg-gencontrol -pdnsmasq -Pdebian/daemon
chown -R root.root debian/daemon
chmod -R g-ws debian/daemon
dpkg --build debian/daemon ..
binary-arch: checkroot
$(checkdir)
rm -rf debian/base
install -m 755 \
-d debian/base/DEBIAN \
-d debian/base/usr/share/doc/$(package) \
-d debian/base/usr/share/doc/$(package)/examples \
-d debian/base/var/run \
-d debian/base/var/lib/misc
make $(TARGET) PREFIX=/usr DESTDIR=`pwd`/debian/base CFLAGS="$(CFLAGS)" COPTS="$(COPTS)" CC=gcc
ifeq (,$(findstring nodocs,$(DEB_BUILD_OPTIONS)))
install -m 644 doc.html debian/base/usr/share/doc/$(package)/.
install -m 644 setup.html debian/base/usr/share/doc/$(package)/.
install -m 644 dnsmasq.conf.example debian/base/usr/share/doc/$(package)/examples/.
install -m 644 FAQ debian/base/usr/share/doc/$(package)/.
gzip -9 debian/base/usr/share/doc/$(package)/FAQ
install -m 644 CHANGELOG debian/base/usr/share/doc/$(package)/changelog
gzip -9 debian/base/usr/share/doc/$(package)/changelog
install -m 644 CHANGELOG.archive debian/base/usr/share/doc/$(package)/changelog.archive
gzip -9 debian/base/usr/share/doc/$(package)/changelog.archive
install -m 644 dbus/DBus-interface debian/base/usr/share/doc/$(package)/.
gzip -9 debian/base/usr/share/doc/$(package)/DBus-interface
endif
install -m 644 debian/changelog debian/base/usr/share/doc/$(package)/changelog.Debian
gzip -9 debian/base/usr/share/doc/$(package)/changelog.Debian
install -m 644 debian/readme debian/base/usr/share/doc/$(package)/README.Debian
install -m 644 debian/copyright debian/base/usr/share/doc/$(package)/copyright
gzip -9 debian/base/usr/share/man/man8/dnsmasq.8
for f in debian/base/usr/share/man/*; do \
if [ -f $$f/man8/dnsmasq.8 ]; then \
gzip -9 $$f/man8/dnsmasq.8 ; \
fi \
done
ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS)))
strip -R .note -R .comment debian/base/usr/sbin/dnsmasq
endif
cd debian/base && find . -type f ! -regex '.*DEBIAN/.*' -printf '%P\0' | xargs -r0 md5sum > DEBIAN/md5sums
dpkg-shlibdeps debian/base/usr/sbin/dnsmasq
dpkg-gencontrol -pdnsmasq-base -Pdebian/base
chown -R root.root debian/base
chmod -R g-ws debian/base
dpkg --build debian/base ..
ifeq ($(DEB_BUILD_ARCH_OS),linux)
rm -rf debian/utils
install -m 755 -d debian/utils/DEBIAN \
-d debian/utils/usr/share/man/man1 \
-d debian/utils/usr/bin \
-d debian/utils/usr/share/doc/dnsmasq-utils
make -C contrib/wrt PREFIX=/usr DESTDIR=`pwd`/debian/utils CFLAGS="$(CFLAGS)" COPTS="$(COPTS)" CC=gcc
install -m 755 contrib/wrt/dhcp_release debian/utils/usr/bin/dhcp_release
install -m 644 contrib/wrt/dhcp_release.1 debian/utils/usr/share/man/man1/dhcp_release.1
gzip -9 debian/utils/usr/share/man/man1/dhcp_release.1
install -m 755 contrib/wrt/dhcp_lease_time debian/utils/usr/bin/dhcp_lease_time
install -m 644 contrib/wrt/dhcp_lease_time.1 debian/utils/usr/share/man/man1/dhcp_lease_time.1
install -m 644 debian/copyright debian/utils/usr/share/doc/dnsmasq-utils/copyright
install -m 644 debian/changelog debian/utils/usr/share/doc/dnsmasq-utils/changelog.Debian
gzip -9 debian/utils/usr/share/doc/dnsmasq-utils/changelog.Debian
gzip -9 debian/utils/usr/share/man/man1/dhcp_lease_time.1
ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS)))
strip -R .note -R .comment debian/utils/usr/bin/dhcp_release
strip -R .note -R .comment debian/utils/usr/bin/dhcp_lease_time
endif
cd debian/utils && find . -type f ! -regex '.*DEBIAN/.*' -printf '%P\0' | xargs -r0 md5sum > DEBIAN/md5sums
dpkg-shlibdeps -Tdebian/utils-substvars debian/utils/usr/bin/dhcp_release debian/utils/usr/bin/dhcp_lease_time
dpkg-gencontrol -Tdebian/utils-substvars -pdnsmasq-utils -Pdebian/utils
chown -R root.root debian/utils
chmod -R g-ws debian/utils
dpkg --build debian/utils ..
endif
define checkdir
test -f Makefile -a -f debian/rules
endef
# Below here is fairly generic really
binary: binary-arch binary-indep
build:
checkroot:
test root = "`whoami`"
.PHONY: binary binary-arch binary-indep clean checkroot
+1
View File
@@ -0,0 +1 @@
1.0
+31
View File
@@ -0,0 +1,31 @@
[Unit]
Description=A lightweight DHCP and caching DNS server
[Service]
Type=dbus
BusName=uk.org.thekelleys.dnsmasq
# Test the config file and refuse starting if it is not valid.
ExecStartPre=/usr/sbin/dnsmasq --test
# Enable DBus by default because we use DBus activation.
#
# Drop privileges and become the 'dnsmasq' user. It is recommended by dnsmasq
# upstream to run dnsmasq as an isolated user that does not run any other
# processes, owns no files and has no shell. The default 'nobody' user has a
# shell and might be used for other processes.
#
# Debian-specific: add /etc/dnsmasq.d to config search path (with the exception
# of .dpkg-*). Packages such as libvirt leave config files there.
#
# --pid-file without argument disables writing a PIDfile, we don't need one.
ExecStart=/usr/sbin/dnsmasq -k \
--enable-dbus \
--user=dnsmasq \
-7 /etc/dnsmasq.d,.dpkg-dist,.dpkg-old,.dpkg-new \
--pid-file
ExecReload=/bin/kill -HUP $MAINPID
[Install]
WantedBy=multi-user.target
+15 -1
View File
@@ -219,7 +219,7 @@
# any machine with Ethernet address starting 11:22:33: # any machine with Ethernet address starting 11:22:33:
#dhcp-host=11:22:33:*:*:*,set:red #dhcp-host=11:22:33:*:*:*,set:red
# Ignore any clients which are specified in dhcp-host lines # Ignore any clients which are not specified in dhcp-host lines
# or /etc/ethers. Equivalent to ISC "deny unknown-clients". # or /etc/ethers. Equivalent to ISC "deny unknown-clients".
# This relies on the special "known" tag which is set when # This relies on the special "known" tag which is set when
# a host is matched. # a host is matched.
@@ -304,6 +304,9 @@
#dhcp-option=45,0.0.0.0 # netbios datagram distribution server #dhcp-option=45,0.0.0.0 # netbios datagram distribution server
#dhcp-option=46,8 # netbios node type #dhcp-option=46,8 # netbios node type
# Send an empty WPAD option. This may be REQUIRED to get windows 7 to behave.
#dhcp-option=252,"\n"
# Send RFC-3397 DNS domain search DHCP option. WARNING: Your DHCP client # Send RFC-3397 DNS domain search DHCP option. WARNING: Your DHCP client
# probably doesn't support this...... # probably doesn't support this......
#dhcp-option=option:domain-search,eng.apple.com,marketing.apple.com #dhcp-option=option:domain-search,eng.apple.com,marketing.apple.com
@@ -348,6 +351,9 @@
# external one. (See below for how to enable the TFTP server.) # external one. (See below for how to enable the TFTP server.)
#dhcp-boot=pxelinux.0 #dhcp-boot=pxelinux.0
# The same as above, but use custom tftp-server instead machine running dnsmasq
#dhcp-boot=pxelinux,server.name,192.168.1.100
# Boot for Etherboot gPXE. The idea is to send two different # Boot for Etherboot gPXE. The idea is to send two different
# filenames, the first loads gPXE, and the second tells gPXE what to # filenames, the first loads gPXE, and the second tells gPXE what to
# load. The dhcp-match sets the gpxe tag for requests from gPXE. # load. The dhcp-match sets the gpxe tag for requests from gPXE.
@@ -422,6 +428,14 @@
# Can fail with old PXE ROMS. Overridden by --pxe-service. # Can fail with old PXE ROMS. Overridden by --pxe-service.
#dhcp-boot=/var/ftpd/pxelinux.0,boothost,192.168.0.3 #dhcp-boot=/var/ftpd/pxelinux.0,boothost,192.168.0.3
# If there are multiple external tftp servers having a same name
# (using /etc/hosts) then that name can be specified as the
# tftp_servername (the third option to dhcp-boot) and in that
# case dnsmasq resolves this name and returns the resultant IP
# addresses in round robin fasion. This facility can be used to
# load balance the tftp load among a set of servers.
#dhcp-boot=/var/ftpd/pxelinux.0,boothost,tftp_server_name
# Set the limit on DHCP leases, the default is 150 # Set the limit on DHCP leases, the default is 150
#dhcp-lease-max=150 #dhcp-lease-max=150
+10 -15
View File
@@ -27,7 +27,7 @@ connection but would be a good choice for any smallish network (up to
1000 clients is known to work) where low 1000 clients is known to work) where low
resource use and ease of configuration are important. resource use and ease of configuration are important.
<P> <P>
Supported platforms include Linux (with glibc and uclibc), *BSD, Supported platforms include Linux (with glibc and uclibc), Android, *BSD,
Solaris and Mac OS X. Solaris and Mac OS X.
Dnsmasq is included in at least the following Linux distributions: Dnsmasq is included in at least the following Linux distributions:
Gentoo, Debian, Slackware, Suse, Fedora, Gentoo, Debian, Slackware, Suse, Fedora,
@@ -88,24 +88,19 @@ for any or all local machines.
</LI> </LI>
</DIR> </DIR>
<H2>Download.</H2> <H2>Get code.</H2>
<A HREF="http://www.thekelleys.org.uk/dnsmasq/"> Download</A> dnsmasq here. <A HREF="http://www.thekelleys.org.uk/dnsmasq/">Download</A> dnsmasq here.
The tarball includes this documentation, source, and manpage. The tarball includes this documentation, source, and manpage.
There is also a <A HREF="CHANGELOG"> CHANGELOG</A> and a <A HREF="FAQ">FAQ</A>. There is also a <A HREF="CHANGELOG"> CHANGELOG</A> and a <A HREF="FAQ">FAQ</A>.
Dnsmasq is part of the Debian distribution, it can be downloaded from
<A HREF="http://ftp.debian.org/debian/pool/main/d/dnsmasq/"> here</A> or installed using <TT>apt</TT>.
<H2>Links.</H2> Dnsmasq has a git repository which contains the complete release
Damien Raude-Morvan has an article in French at <A HREF="http://www.drazzib.com/docs-dnsmasq.html">http://www.drazzib.com/docs-dnsmasq.html</A> history of version 2 and development history from 2.60. You can
There is a good article about dnsmasq at <A <A HREF="http://thekelleys.org.uk/gitweb/?p=dnsmasq.git;a=summary">browse</A>
HREF="http://www.enterprisenetworkingplanet.com/netos/article.php/3377351">http://www.enterprisenetworkingplanet.com/netos/article.php/3377351</A> the repo, or get a copy using git protocol with the command
and another at <A
HREF="http://www.linux.com/articles/149040">http://www.linux.com/articles/149040</A> <PRE><TT>git clone git://thekelleys.org.uk/dnsmasq.git </TT></PRE>
and Ilya Evseev has an article in Russian about dnsmasq to be found at
<A HREF="http://ilya-evseev.narod.ru/articles/dnsmasq">
http://ilya-evseev.narod.ru/articles/dnsmasq</A>. Ismael Ull has an
article about dnsmasq in Spanish at <A HREF="http://www.mey-online.com.ar/blog/index.php/archives/guia-rapida-de-dnsmasq">http://www.mey-online.com.ar/blog/index.php/archives/guia-rapida-de-dnsmasq</A>
<H2>License.</H2> <H2>License.</H2>
Dnsmasq is distributed under the GPL. See the file COPYING in the distribution Dnsmasq is distributed under the GPL. See the file COPYING in the distribution
for details. for details.
+157 -48
View File
@@ -51,7 +51,7 @@ apply to domain names in cnames, PTR records, TXT records etc.
.B \-T, --local-ttl=<time> .B \-T, --local-ttl=<time>
When replying with information from /etc/hosts or the DHCP leases When replying with information from /etc/hosts or the DHCP leases
file dnsmasq by default sets the time-to-live field to zero, meaning file dnsmasq by default sets the time-to-live field to zero, meaning
that the requestor should not itself cache the information. This is that the requester should not itself cache the information. This is
the correct thing to do in almost all situations. This option allows a the correct thing to do in almost all situations. This option allows a
time-to-live (in seconds) to be given for these replies. This will time-to-live (in seconds) to be given for these replies. This will
reduce the load on the server at the expense of clients using stale reduce the load on the server at the expense of clients using stale
@@ -249,8 +249,8 @@ requested name has underscores, to catch LDAP requests.
.B \-r, --resolv-file=<file> .B \-r, --resolv-file=<file>
Read the IP addresses of the upstream nameservers from <file>, instead of Read the IP addresses of the upstream nameservers from <file>, instead of
/etc/resolv.conf. For the format of this file see /etc/resolv.conf. For the format of this file see
.BR resolv.conf (5) .BR resolv.conf (5).
the only lines relevant to dnsmasq are nameserver ones. Dnsmasq can The only lines relevant to dnsmasq are nameserver ones. Dnsmasq can
be told to poll more than one resolv.conf file, the first file name specified be told to poll more than one resolv.conf file, the first file name specified
overrides the default, subsequent ones add to the list. This is only overrides the default, subsequent ones add to the list. This is only
allowed when polling; the file with the currently latest modification allowed when polling; the file with the currently latest modification
@@ -276,7 +276,7 @@ server strictly in the order they appear in /etc/resolv.conf
By default, when dnsmasq has more than one upstream server available, By default, when dnsmasq has more than one upstream server available,
it will send queries to just one server. Setting this flag forces it will send queries to just one server. Setting this flag forces
dnsmasq to send all queries to all available servers. The reply from dnsmasq to send all queries to all available servers. The reply from
the server which answers first will be returned to the original requestor. the server which answers first will be returned to the original requester.
.TP .TP
.B --stop-dns-rebind .B --stop-dns-rebind
Reject (and log) addresses from upstream nameservers which are in the Reject (and log) addresses from upstream nameservers which are in the
@@ -303,7 +303,7 @@ This is useful when new nameservers may have different
data than that held in cache. data than that held in cache.
.TP .TP
.B \-D, --domain-needed .B \-D, --domain-needed
Tells dnsmasq to never forward queries for plain names, without dots Tells dnsmasq to never forward A or AAAA queries for plain names, without dots
or domain parts, to upstream nameservers. If the name is not known or domain parts, to upstream nameservers. If the name is not known
from /etc/hosts or DHCP then a "not found" answer is returned. from /etc/hosts or DHCP then a "not found" answer is returned.
.TP .TP
@@ -350,6 +350,9 @@ is a synonym for
.B server .B server
to make configuration files clearer in this case. to make configuration files clearer in this case.
IPv6 addresses may include a %interface scope-id, eg
fe80::202:a412:4512:7bbf%eth0.
The optional string after the @ character tells The optional string after the @ character tells
dnsmasq how to set the source of the queries to this dnsmasq how to set the source of the queries to this
nameserver. It should be an ip-address, which should belong to the machine on which nameserver. It should be an ip-address, which should belong to the machine on which
@@ -481,7 +484,20 @@ If you use the first DNSSEC mode, validating resolvers in clients,
this option is not required. Dnsmasq always returns all the data this option is not required. Dnsmasq always returns all the data
needed for a client to do validation itself. needed for a client to do validation itself.
.TP .TP
.B --conntrack
Read the Linux connection track mark associated with incoming DNS
queries and set the same mark value on upstream traffic used to answer
those queries. This allows traffic generated by dnsmasq to be
associated with the queries which cause it, useful for bandwidth
accounting and firewalling. Dnsmasq must have conntrack support
compiled in and the kernel must have conntrack support
included and configured. This option cannot be combined with
--query-port.
.TP
.B \-F, --dhcp-range=[interface:<interface>,][tag:<tag>[,tag:<tag>],][set:<tag],]<start-addr>,<end-addr>[,<netmask>[,<broadcast>]][,<lease time>] .B \-F, --dhcp-range=[interface:<interface>,][tag:<tag>[,tag:<tag>],][set:<tag],]<start-addr>,<end-addr>[,<netmask>[,<broadcast>]][,<lease time>]
.TP
.B \-F, --dhcp-range=[interface:<interface>,][tag:<tag>[,tag:<tag>],][set:<tag],]<start-IPv6addr>,<end-IPv6addr>[,<prefix-len>][,<lease time>]
Enable the DHCP server. Addresses will be given out from the range Enable the DHCP server. Addresses will be given out from the range
<start-addr> to <end-addr> and from statically defined addresses given <start-addr> to <end-addr> and from statically defined addresses given
in in
@@ -494,11 +510,20 @@ minimum lease time is two minutes. This
option may be repeated, with different addresses, to enable DHCP option may be repeated, with different addresses, to enable DHCP
service to more than one network. For directly connected networks (ie, service to more than one network. For directly connected networks (ie,
networks on which the machine running dnsmasq has an interface) the networks on which the machine running dnsmasq has an interface) the
netmask is optional. It is, however, required for networks which netmask is optional: dnsmasq will determine it from the interface
receive DHCP service via a relay agent. The broadcast address is configuration. For networks which receive DHCP service via a relay
agent, dnsmasq cannot determine the netmask itself, so it should be
specified, otherwise dnsmasq will have to guess, based on the class (A, B or
C) of the network address. The broadcast address is
always optional. It is always always optional. It is always
allowed to have more than one dhcp-range in a single subnet. allowed to have more than one dhcp-range in a single subnet.
For IPv6, the parameters are slightly different: instead of netmask
and broadcast address, there is an optional prefix length. If not
given, this defaults to 64. Unlike the IPv4 case, the prefix length is not
automatically derived from the interface configuration. The mimimum
size of the prefix length is 64.
The optional The optional
.B set:<tag> .B set:<tag>
sets an alphanumeric label which marks this network so that sets an alphanumeric label which marks this network so that
@@ -519,7 +544,7 @@ subnet. (See
.B pxe-prompt .B pxe-prompt
and and
.B pxe-service .B pxe-service
for details.) for details, applies to IPv4 only.)
The interface:<interface name> section is not normally used. See the The interface:<interface name> section is not normally used. See the
NOTES section for details of this. NOTES section for details of this.
@@ -529,7 +554,7 @@ Specify per host parameters for the DHCP server. This allows a machine
with a particular hardware address to be always allocated the same with a particular hardware address to be always allocated the same
hostname, IP address and lease time. A hostname specified like this hostname, IP address and lease time. A hostname specified like this
overrides any supplied by the DHCP client on the machine. It is also overrides any supplied by the DHCP client on the machine. It is also
allowable to ommit the hardware address and include the hostname, in allowable to omit the hardware address and include the hostname, in
which case the IP address and lease times will apply to any machine which case the IP address and lease times will apply to any machine
claiming that name. For example claiming that name. For example
.B --dhcp-host=00:20:e0:3b:13:af,wap,infinite .B --dhcp-host=00:20:e0:3b:13:af,wap,infinite
@@ -554,6 +579,12 @@ refers to the host with client identifier 01:02:03:04. It is also
allowed to specify the client ID as text, like this: allowed to specify the client ID as text, like this:
.B --dhcp-host=id:clientidastext,..... .B --dhcp-host=id:clientidastext,.....
A single
.B dhcp-host
may contain an IPv4 address or an IPv6 address, or both. IPv6 addresses must be bracketed by square brackets thus:
.B --dhcp-host=laptop,[1234::56]
Note that in IPv6 DHCP, the hardware address is not normally available, so a client must be identified by client-id (called client DUID) in IPv6-land) or hostname.
The special option id:* means "ignore any client-id The special option id:* means "ignore any client-id
and use MAC addresses only." This is useful when a client presents a client-id sometimes and use MAC addresses only." This is useful when a client presents a client-id sometimes
but not others. but not others.
@@ -601,7 +632,7 @@ will only match a
Token-Ring hardware address, since the ARP-address type for token ring Token-Ring hardware address, since the ARP-address type for token ring
is 6. is 6.
As a special case, it is possible to include more than one As a special case, in DHCPv4, it is possible to include more than one
hardware address. eg: hardware address. eg:
.B --dhcp-host=11:22:33:44:55:66,12:34:56:78:90:12,192.168.0.2 .B --dhcp-host=11:22:33:44:55:66,12:34:56:78:90:12,192.168.0.2
This allows an IP address to be associated with This allows an IP address to be associated with
@@ -639,14 +670,14 @@ hostname or dotted-quad IP address. When read by dnsmasq these lines
have exactly the same effect as have exactly the same effect as
.B --dhcp-host .B --dhcp-host
options containing the same information. /etc/ethers is re-read when options containing the same information. /etc/ethers is re-read when
dnsmasq receives SIGHUP. dnsmasq receives SIGHUP. IPv6 addresses are NOT read from /etc/ethers.
.TP .TP
.B \-O, --dhcp-option=[tag:<tag>,[tag:<tag>,]][encap:<opt>,][vi-encap:<enterprise>,][vendor:[<vendor-class>],][<opt>|option:<opt-name>],[<value>[,<value>]] .B \-O, --dhcp-option=[tag:<tag>,[tag:<tag>,]][encap:<opt>,][vi-encap:<enterprise>,][vendor:[<vendor-class>],][<opt>|option:<opt-name>|option6:<opt>|option6:<opt-name>],[<value>[,<value>]]
Specify different or extra options to DHCP clients. By default, Specify different or extra options to DHCP clients. By default,
dnsmasq sends some standard options to DHCP clients, the netmask and dnsmasq sends some standard options to DHCP clients, the netmask and
broadcast address are set to the same as the host running dnsmasq, and broadcast address are set to the same as the host running dnsmasq, and
the DNS server and default route are set to the address of the machine the DNS server and default route are set to the address of the machine
running dnsmasq. If the domain name option has been set, that is sent. running dnsmasq. (Equivalent rules apply for IPv6.) If the domain name option has been set, that is sent.
This configuration allows these defaults to be overridden, This configuration allows these defaults to be overridden,
or other options specified. The option, to be sent may be given as a or other options specified. The option, to be sent may be given as a
decimal number or as "option:<option-name>" The option numbers are decimal number or as "option:<option-name>" The option numbers are
@@ -673,6 +704,14 @@ to option 120 are handled as per RFC 3361. Dotted-quad IP addresses
which are followed by a slash and then a netmask size are encoded as which are followed by a slash and then a netmask size are encoded as
described in RFC 3442. described in RFC 3442.
IPv6 options are specified using the
.B option6:
keyword, followed by the option number or option name. The IPv6 option
name space is disjoint from the IPv4 option name space. IPv6 addresses
in options must be bracketed with square brackets, eg.
.B --dhcp-option=option6:ntp-server,[1234::56]
Be careful: no checking is done that the correct type of data for the Be careful: no checking is done that the correct type of data for the
option number is sent, it is quite possible to option number is sent, it is quite possible to
persuade dnsmasq to generate illegal DHCP packets with injudicious use persuade dnsmasq to generate illegal DHCP packets with injudicious use
@@ -688,7 +727,7 @@ literal string, use quotes. For instance when using option 66 to send
a literal IP address as TFTP server name, it is necessary to do a literal IP address as TFTP server name, it is necessary to do
.B --dhcp-option=66,"1.2.3.4" .B --dhcp-option=66,"1.2.3.4"
Encapsulated Vendor-class options may also be specified using Encapsulated Vendor-class options may also be specified (IPv4 only) using
--dhcp-option: for instance --dhcp-option: for instance
.B --dhcp-option=vendor:PXEClient,1,0.0.0.0 .B --dhcp-option=vendor:PXEClient,1,0.0.0.0
sends the encapsulated vendor sends the encapsulated vendor
@@ -700,9 +739,9 @@ for selecting encapsulated options in preference to any sent by the
client. It is client. It is
possible to omit the vendorclass completely; possible to omit the vendorclass completely;
.B --dhcp-option=vendor:,1,0.0.0.0 .B --dhcp-option=vendor:,1,0.0.0.0
in which case the encapsulated option is always sent. in which case the encapsulated option is always sent.
Options may be encapsulated within other options: for instance Options may be encapsulated (IPv4 only) within other options: for instance
.B --dhcp-option=encap:175, 190, "iscsi-client0" .B --dhcp-option=encap:175, 190, "iscsi-client0"
will send option 175, within which is the option 190. If multiple will send option 175, within which is the option 190. If multiple
options are given which are encapsulated with the same option number options are given which are encapsulated with the same option number
@@ -713,8 +752,9 @@ The final variant on encapsulated options is "Vendor-Identifying
Vendor Options" as specified by RFC3925. These are denoted like this: Vendor Options" as specified by RFC3925. These are denoted like this:
.B --dhcp-option=vi-encap:2, 10, "text" .B --dhcp-option=vi-encap:2, 10, "text"
The number in the vi-encap: section is the IANA enterprise number The number in the vi-encap: section is the IANA enterprise number
used to identify this option. used to identify this option. This form of encapsulation is supported
in IPv6.
The address 0.0.0.0 is not treated specially in The address 0.0.0.0 is not treated specially in
encapsulated options. encapsulated options.
.TP .TP
@@ -726,14 +766,14 @@ not ask for it in the parameter request list. This is sometimes
needed, for example when sending options to PXELinux. needed, for example when sending options to PXELinux.
.TP .TP
.B --dhcp-no-override .B --dhcp-no-override
Disable re-use of the DHCP servername and filename fields as extra (IPv4 only) Disable re-use of the DHCP servername and filename fields as extra
option space. If it can, dnsmasq moves the boot server and filename option space. If it can, dnsmasq moves the boot server and filename
information (from dhcp-boot) out of their dedicated fields into information (from dhcp-boot) out of their dedicated fields into
DHCP options. This make extra space available in the DHCP packet for DHCP options. This make extra space available in the DHCP packet for
options but can, rarely, confuse old or broken clients. This flag options but can, rarely, confuse old or broken clients. This flag
forces "simple and safe" behaviour to avoid problems in such a case. forces "simple and safe" behaviour to avoid problems in such a case.
.TP .TP
.B \-U, --dhcp-vendorclass=set:<tag>,<vendor-class> .B \-U, --dhcp-vendorclass=set:<tag>,[enterprise:<IANA-enterprise number>,]<vendor-class>
Map from a vendor-class string to a tag. Most DHCP clients provide a Map from a vendor-class string to a tag. Most DHCP clients provide a
"vendor class" which represents, in some sense, the type of host. This option "vendor class" which represents, in some sense, the type of host. This option
maps vendor classes to tags, so that DHCP options may be selectively delivered maps vendor classes to tags, so that DHCP options may be selectively delivered
@@ -743,7 +783,13 @@ will allow options to be set only for HP printers like so:
.B --dhcp-option=tag:printers,3,192.168.4.4 .B --dhcp-option=tag:printers,3,192.168.4.4
The vendor-class string is The vendor-class string is
substring matched against the vendor-class supplied by the client, to substring matched against the vendor-class supplied by the client, to
allow fuzzy matching. The set: prefix is optional but allowed for consistency. allow fuzzy matching. The set: prefix is optional but allowed for
consistency.
Note that in IPv6 only, vendorclasses are namespaced with an
IANA-allocated enterprise number. This is given with enterprise:
keyword and specifies that only vendorclasses matching the specified
number should be searched.
.TP .TP
.B \-j, --dhcp-userclass=set:<tag>,<user-class> .B \-j, --dhcp-userclass=set:<tag>,<user-class>
Map from a user-class string to a tag (with substring Map from a user-class string to a tag (with substring
@@ -755,7 +801,7 @@ this to set a different printer server for hosts in the class
"accounts" than for hosts in the class "engineering". "accounts" than for hosts in the class "engineering".
.TP .TP
.B \-4, --dhcp-mac=set:<tag>,<MAC address> .B \-4, --dhcp-mac=set:<tag>,<MAC address>
Map from a MAC address to a tag. The MAC address may include (IPv4 only) Map from a MAC address to a tag. The MAC address may include
wildcards. For example wildcards. For example
.B --dhcp-mac=set:3com,01:34:23:*:*:* .B --dhcp-mac=set:3com,01:34:23:*:*:*
will set the tag "3com" for any host whose MAC address matches the pattern. will set the tag "3com" for any host whose MAC address matches the pattern.
@@ -765,13 +811,16 @@ Map from RFC3046 relay agent options to tags. This data may
be provided by DHCP relay agents. The circuit-id or remote-id is be provided by DHCP relay agents. The circuit-id or remote-id is
normally given as colon-separated hex, but is also allowed to be a normally given as colon-separated hex, but is also allowed to be a
simple string. If an exact match is achieved between the circuit or simple string. If an exact match is achieved between the circuit or
agent ID and one provided by a relay agent, the tag is set. agent ID and one provided by a relay agent, the tag is set.
.B dhcp-remoteid
(but not dhcp-circuitid) is supported in IPv6.
.TP .TP
.B --dhcp-subscrid=set:<tag>,<subscriber-id> .B --dhcp-subscrid=set:<tag>,<subscriber-id>
Map from RFC3993 subscriber-id relay agent options to tags. (IPv4 and IPv6) Map from RFC3993 subscriber-id relay agent options to tags.
.TP .TP
.B --dhcp-proxy[=<ip addr>]...... .B --dhcp-proxy[=<ip addr>]......
A normal DHCP relay agent is only used to forward the initial parts of (IPv4 only) A normal DHCP relay agent is only used to forward the initial parts of
a DHCP interaction to the DHCP server. Once a client is configured, it a DHCP interaction to the DHCP server. Once a client is configured, it
communicates directly with the server. This is undesirable if the communicates directly with the server. This is undesirable if the
relay agent is addding extra information to the DHCP packets, such as relay agent is addding extra information to the DHCP packets, such as
@@ -831,7 +880,7 @@ dhcp-host configuration in dnsmasq and the contents of /etc/hosts and
/etc/ethers. /etc/ethers.
.TP .TP
.B --dhcp-generate-names=tag:<tag>[,tag:<tag>] .B --dhcp-generate-names=tag:<tag>[,tag:<tag>]
Generate a name for DHCP clients which do not otherwise have one, (IPv4 only) Generate a name for DHCP clients which do not otherwise have one,
using the MAC address expressed in hex, seperated by dashes. Note that using the MAC address expressed in hex, seperated by dashes. Note that
if a host provides a name, it will be used by preference to this, if a host provides a name, it will be used by preference to this,
unless unless
@@ -839,14 +888,14 @@ unless
is set. is set.
.TP .TP
.B --dhcp-broadcast[=tag:<tag>[,tag:<tag>]] .B --dhcp-broadcast[=tag:<tag>[,tag:<tag>]]
When all the given tags appear in the tag set, always use broadcast to (IPv4 only) When all the given tags appear in the tag set, always use broadcast to
communicate with the host when it is unconfigured. It is permissible communicate with the host when it is unconfigured. It is permissible
to supply no tags, in which case this is unconditional. Most DHCP clients which to supply no tags, in which case this is unconditional. Most DHCP clients which
need broadcast replies set a flag in their requests so that this need broadcast replies set a flag in their requests so that this
happens automatically, some old BOOTP clients do not. happens automatically, some old BOOTP clients do not.
.TP .TP
.B \-M, --dhcp-boot=[tag:<tag>,]<filename>,[<servername>[,<server address>]] .B \-M, --dhcp-boot=[tag:<tag>,]<filename>,[<servername>[,<server address>|<tftp_servername>]]
Set BOOTP options to be returned by the DHCP server. Server name and (IPv4 only) Set BOOTP options to be returned by the DHCP server. Server name and
address are optional: if not provided, the name is left empty, and the address are optional: if not provided, the name is left empty, and the
address set to the address of the machine running dnsmasq. If dnsmasq address set to the address of the machine running dnsmasq. If dnsmasq
is providing a TFTP service (see is providing a TFTP service (see
@@ -854,8 +903,25 @@ is providing a TFTP service (see
) then only the filename is required here to enable network booting. ) then only the filename is required here to enable network booting.
If the optional tag(s) are given, If the optional tag(s) are given,
they must match for this configuration to be sent. they must match for this configuration to be sent.
Instead of an IP address, the TFTP server address can be given as a domain
name which is looked up in /etc/hosts. This name can be associated in
/etc/hosts with multiple IP addresses, which are used round-robin.
This facility can be used to load balance the tftp load among a set of servers.
.TP .TP
.B --pxe-service=[tag:<tag>,]<CSA>,<menu text>[,<basename>|<bootservicetype>][,<server address>] .B --dhcp-sequential-ip
Dnsmasq is designed to choose IP addresses for DHCP clients using a
hash of the client's MAC address. This normally allows a client's
address to remain stable long-term, even if the client sometimes allows its DHCP
lease to expire. In this default mode IP addresses are distributed
pseudo-randomly over the entire available address range. There are
sometimes circumstances (typically server deployment) where it is more
convenient to have IP
addresses allocated sequentially, starting from the lowest available
address, and setting this flag enables this mode. Note that in the
sequential mode, clients which allow a lease to expire are much more
likely to move IP address; for this reason it should not be generally used.
.TP
.B --pxe-service=[tag:<tag>,]<CSA>,<menu text>[,<basename>|<bootservicetype>][,<server address>|<server_name>]
Most uses of PXE boot-ROMS simply allow the PXE Most uses of PXE boot-ROMS simply allow the PXE
system to obtain an IP address and then download the file specified by system to obtain an IP address and then download the file specified by
.B dhcp-boot .B dhcp-boot
@@ -871,17 +937,19 @@ parameter after the menu text may be a file name, in which case dnsmasq acts as
boot server and directs the PXE client to download the file by TFTP, boot server and directs the PXE client to download the file by TFTP,
either from itself ( either from itself (
.B enable-tftp .B enable-tftp
must be set for this to work) or another TFTP server if the final IP must be set for this to work) or another TFTP server if the final server
address is given. address/name is given.
Note that the "layer" Note that the "layer"
suffix (normally ".0") is supplied by PXE, and should not be added to suffix (normally ".0") is supplied by PXE, and should not be added to
the basename. If an integer boot service type, rather than a basename the basename. If an integer boot service type, rather than a basename
is given, then the PXE client will search for a is given, then the PXE client will search for a
suitable boot service for that type on the network. This search may be done suitable boot service for that type on the network. This search may be done
by broadcast, or direct to a server if its IP address is provided. by broadcast, or direct to a server if its IP address/name is provided.
If no boot service type or filename is provided (or a boot service type of 0 is specified) If no boot service type or filename is provided (or a boot service type of 0 is specified)
then the menu entry will abort the net boot procedure and then the menu entry will abort the net boot procedure and
continue booting from local media. continue booting from local media. The server address can be given as a domain
name which is looked up in /etc/hosts. This name can be associated in
/etc/hosts with multiple IP addresses, which are used round-robin.
.TP .TP
.B --pxe-prompt=[tag:<tag>,]<prompt>[,<timeout>] .B --pxe-prompt=[tag:<tag>,]<prompt>[,<timeout>]
Setting this provides a prompt to be displayed after PXE boot. If the Setting this provides a prompt to be displayed after PXE boot. If the
@@ -914,7 +982,7 @@ create thousands of leases and use lots of memory in the dnsmasq
process. process.
.TP .TP
.B \-K, --dhcp-authoritative .B \-K, --dhcp-authoritative
Should be set when dnsmasq is definitely the only DHCP server on a network. (IPv4 only) Should be set when dnsmasq is definitely the only DHCP server on a network.
It changes the behaviour from strict RFC compliance so that DHCP requests on It changes the behaviour from strict RFC compliance so that DHCP requests on
unknown leases from unknown hosts are not ignored. This allows new hosts unknown leases from unknown hosts are not ignored. This allows new hosts
to get a lease without a tedious timeout under all circumstances. It also to get a lease without a tedious timeout under all circumstances. It also
@@ -922,7 +990,7 @@ allows dnsmasq to rebuild its lease database without each client needing to
reacquire a lease, if the database is lost. reacquire a lease, if the database is lost.
.TP .TP
.B --dhcp-alternate-port[=<server port>[,<client port>]] .B --dhcp-alternate-port[=<server port>[,<client port>]]
Change the ports used for DHCP from the default. If this option is (IPv4 only) Change the ports used for DHCP from the default. If this option is
given alone, without arguments, it changes the ports used for DHCP given alone, without arguments, it changes the ports used for DHCP
from 67 and 68 to 1067 and 1068. If a single argument is given, that from 67 and 68 to 1067 and 1068. If a single argument is given, that
port number is used for the server and the port number plus one used port number is used for the server and the port number plus one used
@@ -930,7 +998,7 @@ for the client. Finally, two port numbers allows arbitrary
specification of both server and client ports for DHCP. specification of both server and client ports for DHCP.
.TP .TP
.B \-3, --bootp-dynamic[=<network-id>[,<network-id>]] .B \-3, --bootp-dynamic[=<network-id>[,<network-id>]]
Enable dynamic allocation of IP addresses to BOOTP clients. Use this (IPv4 only) Enable dynamic allocation of IP addresses to BOOTP clients. Use this
with care, since each address allocated to a BOOTP client is leased with care, since each address allocated to a BOOTP client is leased
forever, and therefore becomes permanently unavailable for re-use by forever, and therefore becomes permanently unavailable for re-use by
other hosts. if this is given without tags, then it unconditionally other hosts. if this is given without tags, then it unconditionally
@@ -938,7 +1006,7 @@ enables dynamic allocation. With tags, only when the tags are all
set. It may be repeated with different tag sets. set. It may be repeated with different tag sets.
.TP .TP
.B \-5, --no-ping .B \-5, --no-ping
By default, the DHCP server will attempt to ensure that an address in (IPv4 only) By default, the DHCP server will attempt to ensure that an address in
not in use before allocating it to a host. It does this by sending an not in use before allocating it to a host. It does this by sending an
ICMP echo request (aka "ping") to the address in question. If it gets ICMP echo request (aka "ping") to the address in question. If it gets
a reply, then the address must already be in use, and another is a reply, then the address must already be in use, and another is
@@ -957,7 +1025,7 @@ executable specified by this option is run. <path>
must be an absolute pathname, no PATH search occurs. must be an absolute pathname, no PATH search occurs.
The arguments to the process The arguments to the process
are "add", "old" or "del", the MAC are "add", "old" or "del", the MAC
address of the host, the IP address, and the hostname, address of the host (or DUID for IPv6) , the IP address, and the hostname,
if known. "add" means a lease has been created, "del" means it has if known. "add" means a lease has been created, "del" means it has
been destroyed, "old" is a notification of an existing lease when been destroyed, "old" is a notification of an existing lease when
dnsmasq starts or a change to MAC address or hostname of an existing dnsmasq starts or a change to MAC address or hostname of an existing
@@ -968,21 +1036,17 @@ token ring. The process is run as root (assuming that dnsmasq was originally run
root) even if dnsmasq is configured to change UID to an unprivileged user. root) even if dnsmasq is configured to change UID to an unprivileged user.
The environment is inherited from the invoker of dnsmasq, with some or The environment is inherited from the invoker of dnsmasq, with some or
all of the following variables added. all of the following variables added
DNSMASQ_CLIENT_ID if the host provided a client-id. For both IPv4 and IPv6:
DNSMASQ_DOMAIN if the fully-qualified domain name of the host is DNSMASQ_DOMAIN if the fully-qualified domain name of the host is
known, this is set to the domain part. (Note that the hostname passed known, this is set to the domain part. (Note that the hostname passed
to the script as an argument is never fully-qualified.) to the script as an argument is never fully-qualified.)
If the client provides vendor-class, hostname or user-class, If the client provides a hostname, DNSMASQ_SUPPLIED_HOSTNAME
these are provided in DNSMASQ_VENDOR_CLASS
DNSMASQ_SUPPLIED_HOSTNAME and If the client provides user-classes, DNSMASQ_USER_CLASS0..DNSMASQ_USER_CLASSn
DNSMASQ_USER_CLASS0..DNSMASQ_USER_CLASSn variables, but only for
"add" actions or "old" actions when a host resumes an existing lease,
since these data are not held in dnsmasq's lease
database.
If dnsmasq was compiled with HAVE_BROKEN_RTC, then If dnsmasq was compiled with HAVE_BROKEN_RTC, then
the length of the lease (in seconds) is stored in the length of the lease (in seconds) is stored in
@@ -1006,6 +1070,32 @@ is known.
DNSMASQ_TAGS contains all the tags set during the DNSMASQ_TAGS contains all the tags set during the
DHCP transaction, separated by spaces. DHCP transaction, separated by spaces.
For IPv4 only:
DNSMASQ_CLIENT_ID if the host provided a client-id.
If the client provides vendor-class, DNSMASQ_VENDOR_CLASS.
For IPv6 only:
If the client provides vendor-class, DNSMASQ_VENDOR_CLASS_ID,
containing the IANA enterprise id for the class, and
DNSMASQ_VENDOR_CLASS0..DNSMASQ_VENDOR_CLASSn for the data.
DNSMASQ_DUID containing the DUID of the server: this is the same for
every call to the script.
DNSMASQ_IAID containing the IAID for the lease. If the lease is a
temporary allocation, this is prefixed to 'T'.
Note that the supplied hostname, vendorclass and userclass data is
only supplied for
"add" actions or "old" actions when a host resumes an existing lease,
since these data are not held in dnsmasq's lease
database.
All file descriptors are All file descriptors are
closed except stdin, stdout and stderr which are open to /dev/null closed except stdin, stdout and stderr which are open to /dev/null
(except in debug mode). (except in debug mode).
@@ -1101,6 +1191,14 @@ without an address specified when
.B --dhcp-fqdn .B --dhcp-fqdn
is set. is set.
.TP .TP
.B --dhcp-client-update
Normally, when giving a DHCP lease, dnsmasq sets flags in the FQDN
option to tell the client not to attempt a DDNS update with its name
and IP address. This is because the name-IP pair is automatically
added into dnsmasq's DNS view. This flag suppresses that behaviour,
this is useful, for instance, to allow Windows clients to update
Active Directory servers. See RFC 4702 for details.
.TP
.B --enable-tftp[=<interface>] .B --enable-tftp[=<interface>]
Enable the TFTP server function. This is deliberately limited to that Enable the TFTP server function. This is deliberately limited to that
needed to net-boot a client. Only reading is allowed; the tsize and needed to net-boot a client. Only reading is allowed; the tsize and
@@ -1324,6 +1422,17 @@ so --dhcp=option=tag:!purple,3,1.2.3.4 sends the option when the
tag purple is not in the set of valid tags. (If using this in a tag purple is not in the set of valid tags. (If using this in a
command line rather than a configuration file, be sure to escape !, command line rather than a configuration file, be sure to escape !,
which is a shell metacharacter) which is a shell metacharacter)
When selecting dhcp-options, a tag from dhcp-range is second class
relative to other tags, to make it easy to override options for
individual hosts, so
.B dhcp-range=set:interface1,......
.B dhcp-host=set:myhost,.....
.B dhcp-option=tag:interface1,option:nis-domain,"domain1"
.B dhcp-option=tag:myhost,option:nis-domain,"domain2"
will set the NIS-domain to domain1 for hosts in the range, but
override that to domain2 for a particular host.
.PP .PP
Note that for Note that for
.B dhcp-range .B dhcp-range
+61 -9
View File
@@ -347,10 +347,10 @@ Cela est utile si les nouveaux serveurs sont susceptibles d'avoir des données
différentes de celles stockées dans le cache. différentes de celles stockées dans le cache.
.TP .TP
.B \-D, --domain-needed .B \-D, --domain-needed
Indique à Dnsmasq de ne jamais transmettre en amont de requêtes pour des noms Indique à Dnsmasq de ne jamais transmettre en amont de requêtes A ou AAAA pour
simples, ne comprenant donc ni points ni nom de domaine. Si un nom n'est pas des noms simples, c'est à dire ne comprenant ni points ni nom de domaine. Si un
dans /etc/hosts ou dans la liste des baux DHCP, alors une réponse de type nom n'est pas dans /etc/hosts ou dans la liste des baux DHCP, alors une réponse
"non trouvé" est renvoyée. de type "non trouvé" est renvoyée.
.TP .TP
.B \-S, --local, --server=[/[<domaine>]/[domaine/]][<Adresse IP>[#<port>][@<Adresse IP source>|<interface>[#<port>]]] .B \-S, --local, --server=[/[<domaine>]/[domaine/]][<Adresse IP>[#<port>][@<Adresse IP source>|<interface>[#<port>]]]
Spécifie directement l'adresse IP d'un serveur de nom amont. Cette option ne Spécifie directement l'adresse IP d'un serveur de nom amont. Cette option ne
@@ -402,6 +402,10 @@ est synonyme de
("serveur") afin de rendre plus claire l'utilisation de cette option pour cet ("serveur") afin de rendre plus claire l'utilisation de cette option pour cet
usage particulier. usage particulier.
Les adresses IPv6 peuvent inclure un identifiant de zone sous la forme
%interface tel que par exemple
fe80::202:a412:4512:7bbf%eth0.
La chaîne de caractères optionnelle suivant le caractère @ permet de définir La chaîne de caractères optionnelle suivant le caractère @ permet de définir
la source que Dnsmasq doit utiliser pour les réponses à ce la source que Dnsmasq doit utiliser pour les réponses à ce
serveur de nom. Il doit s'agir d'une des adresses IP appartenant à la machine sur serveur de nom. Il doit s'agir d'une des adresses IP appartenant à la machine sur
@@ -565,6 +569,16 @@ Si vous utilisez le premier mode DNSSEC, la validation par le resolveur des
clients, cette option n'est pas requise. Dnsmasq retourne toujours toutes les clients, cette option n'est pas requise. Dnsmasq retourne toujours toutes les
données nécessaires par un client pour effectuer la validation lui-même. données nécessaires par un client pour effectuer la validation lui-même.
.TP .TP
.B --conntrack
Lis le marquage de suivi de connexion Linux associé aux requêtes DNS entrantes
et positionne la même marque au trafic amont utilisé pour répondre à ces
requétes. Cela permet au trafic généré par Dnsmasq d'étre associé aux requêtes
l'ayant déclenché, ce qui est pratique pour la gestion de la bande passante
(accounting) et le filtrage (firewall). Dnsmasq doit pour cela être compilé
avec le support conntrack, le noyau doit également inclure conntrack et être
configuré pour cela. Cette option ne peut pas être combinée avec
--query-port.
.TP
.B \-F, --dhcp-range=[interface:<interface>,][tag:<label>[,tag:<label>],][set:<label],]<adresse de début>,<adresse de fin>[,<masque de réseau>[,<broadcast>]][,<durée de bail>] .B \-F, --dhcp-range=[interface:<interface>,][tag:<label>[,tag:<label>],][set:<label],]<adresse de début>,<adresse de fin>[,<masque de réseau>[,<broadcast>]][,<durée de bail>]
Active le serveur DHCP. Les adresses seront données dans la plage comprise entre Active le serveur DHCP. Les adresses seront données dans la plage comprise entre
<adresse de début> et <adresse de fin> et à partir des adresses définies <adresse de début> et <adresse de fin> et à partir des adresses définies
@@ -579,9 +593,14 @@ minutes.
Cette option peut être répétée, avec différentes adresses, Cette option peut être répétée, avec différentes adresses,
pour activer le service DHCP sur plus d'un réseau. Pour des réseaux directement pour activer le service DHCP sur plus d'un réseau. Pour des réseaux directement
connectés (c'est-à-dire des réseaux dans lesquels la machine sur laquelle tourne connectés (c'est-à-dire des réseaux dans lesquels la machine sur laquelle tourne
Dnsmasq possède une interface), le masque de réseau est optionnel. Il est par Dnsmasq possède une interface), le masque de réseau est optionnel : Dnsmasq la
contre requis pour les réseaux pour lesquels le service DHCP se fait via un déterminera à partir de la configuration des interfaces.
relais DHCP ("relay agent"). L'adresse de broadcast est toujours optionnelle.
Pour les réseaux pour lesquels le service DHCP se fait via un relais DHCP
("relay agent"), Dnsmasq est incapable de déterminer le masque par lui-même,
aussi il doit être spécifié, faute de quoi Dnsmasq essaiera de le deviner en
fonction de la classe (A, B ou C) de l'adresse réseau. L'adresse de broadcast
est toujours optionnelle.
Il est toujours possible d'avoir plus d'une plage DHCP pour un même Il est toujours possible d'avoir plus d'une plage DHCP pour un même
sous-réseau. sous-réseau.
@@ -969,7 +988,7 @@ option s'applique inconditionnellement. La plupart des clients DHCP nécessitant
d'un broadcast activent une option dans leur requête, ce qui fait que cela d'un broadcast activent une option dans leur requête, ce qui fait que cela
se fait automatiquement, mais ce n'est pas la cas de certains vieux clients BOOTP. se fait automatiquement, mais ce n'est pas la cas de certains vieux clients BOOTP.
.TP .TP
.B \-M, --dhcp-boot=[tag:<label>,]<nom de fichier>,[<nom de serveur>[,<adresse de serveur>]] .B \-M, --dhcp-boot=[tag:<label>,]<nom de fichier>,[<nom de serveur>[,<adresse de serveur>|<nom du serveur tftp>]]
Spécifie les options BOOTP devant être retournées par le serveur DHCP. Le nom de Spécifie les options BOOTP devant être retournées par le serveur DHCP. Le nom de
serveur ainsi que l'adresse sont optionnels : s'ils ne sont pas fournis, le nom serveur ainsi que l'adresse sont optionnels : s'ils ne sont pas fournis, le nom
est laissé vide et l'adresse fournie est celle de la machine sur laquelle est laissé vide et l'adresse fournie est celle de la machine sur laquelle
@@ -979,6 +998,25 @@ s'exécute Dnsmasq. Si Dnsmasq founit un service TFTP (voir
le réseau. le réseau.
Si d'éventuels labels sont fournis, ils doivent coïncider avec Si d'éventuels labels sont fournis, ils doivent coïncider avec
ceux du client pour que cet élement de configuration lui soit envoyé. ceux du client pour que cet élement de configuration lui soit envoyé.
Une adresse de serveur TFTP peut être spécifiée à la place de l'adresse IP,
sous la forme d'un nom de domaine qui sera cherché dans le fichier /etc/hosts.
Ce nom peut être associé dans /etc/hosts avec plusieurs adresses IP, auquel cas
celles-ci seront utilisées tour à tour (algorithme round-robin).
Cela peut-être utiliser pour équilibrer la charge tftp sur plusieurs serveurs.
.TP
.B --dhcp-sequential-ip
Dnsmasq est conçu pour choisir l'adresse IP des clients DHCP en utilisant
un hachage de l'adresse MAC du client. Cela permet en général à l'adresse
IP du client de rester stable au fil du temps, même lorsque le client laisse
expirer son bail DHCP de temps en temps. Dans ce mode de fonctionnement par
défaut, les adresses IP sont distribuées de façon pseudo-aléatoire dans la
totalité de la plage d'adresses utilisable. Il existe des circonstances (par
exemples pour du déploiement de serveur) où il est plus pratique d'allouer les
adresses IP de manière séquentielle, en commençant par la plus petite adresse
disponible, et c'est ce mode de fonctionnement qui est permis par cette option.
Veuillez noter que dans ce mode séquentiel, les clients qui laissent expirer
leur bail ont beaucoup plus de chance de voir leur adresse IP changer, aussi
cette option ne devrait pas être utilisée dans un cas général.
.TP .TP
.B --pxe-service=[tag:<label>,]<CSA>,<entrée de menu>[,<nom de fichier>|<type de service de démarrage>][,<adresse de serveur>] .B --pxe-service=[tag:<label>,]<CSA>,<entrée de menu>[,<nom de fichier>|<type de service de démarrage>][,<adresse de serveur>]
La plupart des ROMS de démarrage PXE ne permettent au système PXE que la simple La plupart des ROMS de démarrage PXE ne permettent au système PXE que la simple
@@ -1417,7 +1455,6 @@ Dans le cas de l'utilisation du logiciel logrotate, les options requises sont
.B create .B create
et et
.B delaycompress. .B delaycompress.
.PP .PP
Dnsmasq est un logiciel de transmission de requêtes DNS : il n'est pas capable Dnsmasq est un logiciel de transmission de requêtes DNS : il n'est pas capable
@@ -1509,6 +1546,21 @@ labels définis pour l'hôte considéré. (dans le cas de l'utilisation dans une
ligne de commande au lieu d'un fichier de configuration, ne pas oublier ligne de commande au lieu d'un fichier de configuration, ne pas oublier
d'échapper le caractère !, qui est un méta-caractère d'interpréteur de commande d'échapper le caractère !, qui est un méta-caractère d'interpréteur de commande
shell). shell).
+When selecting dhcp-options, a tag from dhcp-range is second class
+relative to other tags, to make it easy to override options for
+individual hosts, so
Lors de la sélection d'une option, une étiquette spécifiée par dhcp-range
passe après les autres étiquettes, ce qui permet de facilement remplacer des
option génériques pour des hôtes spécifiques, ainsi :
.B dhcp-range=set:interface1,......
.B dhcp-host=set:monhote,.....
.B dhcp-option=tag:interface1,option:nis-domain,"domaine1"
.B dhcp-option=tag:monhote,option:nis-domain,"domaine2"
va positionner l'option NIS-domain à domaine1 pour les hôtes dans la plage
d'adresse, sauf pour monhote pour lequel cette valeur sera domaine2.
.PP .PP
Veuillez noter que pour Veuillez noter que pour
.B dhcp-range .B dhcp-range
+510 -392
View File
File diff suppressed because it is too large. Load diff
+632 -518
View File
File diff suppressed because it is too large. Load diff
+662 -555
View File
File diff suppressed because it is too large. Load diff
+605 -490
View File
File diff suppressed because it is too large. Load diff
+629 -509
View File
File diff suppressed because it is too large. Load diff
+662 -555
View File
File diff suppressed because it is too large. Load diff
+626 -516
View File
File diff suppressed because it is too large. Load diff
+870 -755
View File
File diff suppressed because it is too large. Load diff
+662 -555
View File
File diff suppressed because it is too large. Load diff
+626 -516
View File
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -221,7 +221,7 @@ triggering dial-on-demand internet links.
Sending SIGHUP to the dnsmasq process will cause it to empty its cache and Sending SIGHUP to the dnsmasq process will cause it to empty its cache and
then re-load <TT>/etc/hosts</TT> and <TT>/etc/resolv.conf</TT>. then re-load <TT>/etc/hosts</TT> and <TT>/etc/resolv.conf</TT>.
<P> Sending SIGUSR1 (killall -10 dnsmasq) to the dnsmasq process will <P> Sending SIGUSR1 (killall -10 dnsmasq) to the dnsmasq process will
cause to to write cache usage statisticss to the log, typically cause to write cache usage statisticss to the log, typically
<TT>/var/log/syslog</TT> or <TT>/var/log/messages</TT>. <TT>/var/log/syslog</TT> or <TT>/var/log/messages</TT>.
<P> The <TT>log-queries</TT> option tells dnsmasq to verbosely log the queries <P> The <TT>log-queries</TT> option tells dnsmasq to verbosely log the queries
it is handling and causes SIGUSR1 to trigger a complete dump of the it is handling and causes SIGUSR1 to trigger a complete dump of the
+25 -4
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2011 Simon Kelley /* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -35,6 +35,13 @@ static struct iovec ifreq = {
#include <net/if_dl.h> #include <net/if_dl.h>
#include <netinet/if_ether.h> #include <netinet/if_ether.h>
#ifndef SA_SIZE
#define SA_SIZE(sa) \
( (!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ? \
sizeof(long) : \
1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(long) - 1) ) )
#endif
int arp_enumerate(void *parm, int (*callback)()) int arp_enumerate(void *parm, int (*callback)())
{ {
int mib[6]; int mib[6];
@@ -101,6 +108,10 @@ int iface_enumerate(int family, void *parm, int (*callback)())
return 0; /* need code for Solaris and MacOS*/ return 0; /* need code for Solaris and MacOS*/
#endif #endif
/* AF_LINK doesn't exist in Linux, so we can't use it in our API */
if (family == AF_LOCAL)
family = AF_LINK;
if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1) if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
return 0; return 0;
@@ -146,7 +157,7 @@ int iface_enumerate(int family, void *parm, int (*callback)())
ifr = (struct ifreq *)ifreq.iov_base; ifr = (struct ifreq *)ifreq.iov_base;
memcpy(ifr, ptr, len); memcpy(ifr, ptr, len);
if (ifr->ifr_addr.sa_family == family) if (ifr->ifr_addr.sa_family == family)
{ {
if (family == AF_INET) if (family == AF_INET)
@@ -175,13 +186,23 @@ int iface_enumerate(int family, void *parm, int (*callback)())
addr->s6_addr[2] = 0; addr->s6_addr[2] = 0;
addr->s6_addr[3] = 0; addr->s6_addr[3] = 0;
} }
if (!((*callback)(addr, /* We have no way to determine the prefix, so we assume it's 64 for now....... */
if (!((*callback)(addr, 64,
(int)((struct sockaddr_in6 *)&ifr->ifr_addr)->sin6_scope_id, (int)((struct sockaddr_in6 *)&ifr->ifr_addr)->sin6_scope_id,
(int)if_nametoindex(ifr->ifr_name), (int)if_nametoindex(ifr->ifr_name), 0,
parm))) parm)))
goto err; goto err;
} }
#endif #endif
#ifdef HAVE_DHCP6
else if (family == AF_LINK)
{
/* Assume ethernet again here */
struct sockaddr_dl *sdl = (struct sockaddr_dl *)&ifr->ifr_addr;
if (sdl->sdl_alen != 0 && !((*callback)(ARPHRD_ETHER, LLADDR(sdl), sdl->sdl_alen, parm)))
goto err;
}
#endif
} }
} }
+120 -76
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2011 Simon Kelley /* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -25,7 +25,6 @@ static int cache_inserted = 0, cache_live_freed = 0, insert_error;
static union bigname *big_free = NULL; static union bigname *big_free = NULL;
static int bignames_left, hash_size; static int bignames_left, hash_size;
static int uid = 0; static int uid = 0;
static char *addrbuff = NULL;
/* type->string mapping: this is also used by the name-hash function as a mixing table. */ /* type->string mapping: this is also used by the name-hash function as a mixing table. */
static const struct { static const struct {
@@ -75,9 +74,6 @@ void cache_init(void)
struct crec *crecp; struct crec *crecp;
int i; int i;
if (option_bool(OPT_LOG))
addrbuff = safe_malloc(ADDRSTRLEN);
bignames_left = daemon->cachesize/10; bignames_left = daemon->cachesize/10;
if (daemon->cachesize > 0) if (daemon->cachesize > 0)
@@ -639,11 +635,12 @@ struct crec *cache_find_by_addr(struct crec *crecp, struct all_addr *addr,
} }
static void add_hosts_entry(struct crec *cache, struct all_addr *addr, int addrlen, static void add_hosts_entry(struct crec *cache, struct all_addr *addr, int addrlen,
unsigned short flags, int index, int addr_dup) unsigned short flags, int index, struct crec **rhash, int hashsz)
{ {
struct crec *lookup = cache_find_by_name(NULL, cache->name.sname, 0, flags & (F_IPV4 | F_IPV6)); struct crec *lookup = cache_find_by_name(NULL, cache->name.sname, 0, flags & (F_IPV4 | F_IPV6));
int i, nameexists = 0; int i, nameexists = 0;
struct cname *a; struct cname *a;
unsigned int j;
/* Remove duplicates in hosts files. */ /* Remove duplicates in hosts files. */
if (lookup && (lookup->flags & F_HOSTS)) if (lookup && (lookup->flags & F_HOSTS))
@@ -657,33 +654,39 @@ static void add_hosts_entry(struct crec *cache, struct all_addr *addr, int addrl
} }
/* Ensure there is only one address -> name mapping (first one trumps) /* Ensure there is only one address -> name mapping (first one trumps)
We do this by steam here, first we see if the address is the same as We do this by steam here, The entries are kept in hash chains, linked
the last one we saw, which eliminates most in the case of an ad-block by ->next (which is unused at this point) held in hash buckets in
file with thousands of entries for the same address. the array rhash, hashed on address. Note that rhash and the values
Then we search and bail at the first matching address that came from in ->next are only valid whilst reading hosts files: the buckets are
a HOSTS file. Since the first host entry gets reverse, we know then freed, and the ->next pointer used for other things.
then that it must exist without searching exhaustively for it. */
Only insert each unique address once into this hashing structure.
This complexity avoids O(n^2) divergent CPU use whilst reading
large (10000 entry) hosts files. */
if (addr_dup) /* hash address */
flags &= ~F_REVERSE; for (j = 0, i = 0; i < addrlen; i++)
else j = (j*2 +((unsigned char *)addr)[i]) % hashsz;
for (i=0; i<hash_size; i++)
for (lookup = rhash[j]; lookup; lookup = lookup->next)
if ((lookup->flags & flags & (F_IPV4 | F_IPV6)) &&
memcmp(&lookup->addr.addr, addr, addrlen) == 0)
{ {
for (lookup = hash_table[i]; lookup; lookup = lookup->hash_next) flags &= ~F_REVERSE;
if ((lookup->flags & F_HOSTS) && break;
(lookup->flags & flags & (F_IPV4 | F_IPV6)) &&
memcmp(&lookup->addr.addr, addr, addrlen) == 0)
{
flags &= ~F_REVERSE;
break;
}
if (lookup)
break;
} }
/* maintain address hash chain, insert new unique address */
if (!lookup)
{
cache->next = rhash[j];
rhash[j] = cache;
}
cache->flags = flags; cache->flags = flags;
cache->uid = index; cache->uid = index;
memcpy(&cache->addr.addr, addr, addrlen); memcpy(&cache->addr.addr, addr, addrlen);
cache_hash(cache); cache_hash(cache);
/* don't need to do alias stuff for second and subsequent addresses. */ /* don't need to do alias stuff for second and subsequent addresses. */
@@ -747,14 +750,14 @@ static int gettok(FILE *f, char *token)
} }
} }
static int read_hostsfile(char *filename, int index, int cache_size) static int read_hostsfile(char *filename, int index, int cache_size, struct crec **rhash, int hashsz)
{ {
FILE *f = fopen(filename, "r"); FILE *f = fopen(filename, "r");
char *token = daemon->namebuff, *domain_suffix = NULL; char *token = daemon->namebuff, *domain_suffix = NULL;
int addr_count = 0, name_count = cache_size, lineno = 0; int addr_count = 0, name_count = cache_size, lineno = 0;
unsigned short flags = 0, saved_flags = 0; unsigned short flags = 0;
struct all_addr addr, saved_addr; struct all_addr addr;
int atnl, addrlen = 0, addr_dup; int atnl, addrlen = 0;
if (!f) if (!f)
{ {
@@ -766,7 +769,6 @@ static int read_hostsfile(char *filename, int index, int cache_size)
while ((atnl = gettok(f, token)) != EOF) while ((atnl = gettok(f, token)) != EOF)
{ {
addr_dup = 0;
lineno++; lineno++;
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
@@ -780,7 +782,7 @@ static int read_hostsfile(char *filename, int index, int cache_size)
{ {
flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6; flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6;
addrlen = IN6ADDRSZ; addrlen = IN6ADDRSZ;
domain_suffix = daemon->domain_suffix; domain_suffix = get_domain6(&addr.addr.addr6);
} }
#else #else
if ((addr.addr.addr4.s_addr = inet_addr(token)) != (in_addr_t) -1) if ((addr.addr.addr4.s_addr = inet_addr(token)) != (in_addr_t) -1)
@@ -798,14 +800,6 @@ static int read_hostsfile(char *filename, int index, int cache_size)
continue; continue;
} }
if (saved_flags == flags && memcmp(&addr, &saved_addr, addrlen) == 0)
addr_dup = 1;
else
{
saved_flags = flags;
saved_addr = addr;
}
addr_count++; addr_count++;
/* rehash every 1000 names. */ /* rehash every 1000 names. */
@@ -836,14 +830,13 @@ static int read_hostsfile(char *filename, int index, int cache_size)
strcpy(cache->name.sname, canon); strcpy(cache->name.sname, canon);
strcat(cache->name.sname, "."); strcat(cache->name.sname, ".");
strcat(cache->name.sname, domain_suffix); strcat(cache->name.sname, domain_suffix);
add_hosts_entry(cache, &addr, addrlen, flags, index, addr_dup); add_hosts_entry(cache, &addr, addrlen, flags, index, rhash, hashsz);
addr_dup = 1;
name_count++; name_count++;
} }
if ((cache = whine_malloc(sizeof(struct crec) + strlen(canon)+1-SMALLDNAME))) if ((cache = whine_malloc(sizeof(struct crec) + strlen(canon)+1-SMALLDNAME)))
{ {
strcpy(cache->name.sname, canon); strcpy(cache->name.sname, canon);
add_hosts_entry(cache, &addr, addrlen, flags, index, addr_dup); add_hosts_entry(cache, &addr, addrlen, flags, index, rhash, hashsz);
name_count++; name_count++;
} }
free(canon); free(canon);
@@ -865,7 +858,7 @@ static int read_hostsfile(char *filename, int index, int cache_size)
void cache_reload(void) void cache_reload(void)
{ {
struct crec *cache, **up, *tmp; struct crec *cache, **up, *tmp;
int i, total_size = daemon->cachesize; int revhashsz, i, total_size = daemon->cachesize;
struct hostsfile *ah; struct hostsfile *ah;
cache_inserted = cache_live_freed = 0; cache_inserted = cache_live_freed = 0;
@@ -899,14 +892,20 @@ void cache_reload(void)
my_syslog(LOG_INFO, _("cleared cache")); my_syslog(LOG_INFO, _("cleared cache"));
return; return;
} }
/* borrow the packet buffer for a temporary by-address hash */
memset(daemon->packet, 0, daemon->packet_buff_sz);
revhashsz = daemon->packet_buff_sz / sizeof(struct crec *);
/* we overwrote the buffer... */
daemon->srv_save = NULL;
if (!option_bool(OPT_NO_HOSTS)) if (!option_bool(OPT_NO_HOSTS))
total_size = read_hostsfile(HOSTSFILE, 0, total_size); total_size = read_hostsfile(HOSTSFILE, 0, total_size, (struct crec **)daemon->packet, revhashsz);
daemon->addn_hosts = expand_filelist(daemon->addn_hosts); daemon->addn_hosts = expand_filelist(daemon->addn_hosts);
for (ah = daemon->addn_hosts; ah; ah = ah->next) for (ah = daemon->addn_hosts; ah; ah = ah->next)
if (!(ah->flags & AH_INACTIVE)) if (!(ah->flags & AH_INACTIVE))
total_size = read_hostsfile(ah->fname, ah->index, total_size); total_size = read_hostsfile(ah->fname, ah->index, total_size, (struct crec **)daemon->packet, revhashsz);
} }
char *get_domain(struct in_addr addr) char *get_domain(struct in_addr addr)
@@ -914,14 +913,49 @@ char *get_domain(struct in_addr addr)
struct cond_domain *c; struct cond_domain *c;
for (c = daemon->cond_domain; c; c = c->next) for (c = daemon->cond_domain; c; c = c->next)
if (ntohl(addr.s_addr) >= ntohl(c->start.s_addr) && if (!c->is6 &&
ntohl(addr.s_addr) >= ntohl(c->start.s_addr) &&
ntohl(addr.s_addr) <= ntohl(c->end.s_addr)) ntohl(addr.s_addr) <= ntohl(c->end.s_addr))
return c->domain; return c->domain;
return daemon->domain_suffix; return daemon->domain_suffix;
} }
#ifdef HAVE_IPV6
char *get_domain6(struct in6_addr *addr)
{
struct cond_domain *c;
u64 addrpart = addr6part(addr);
for (c = daemon->cond_domain; c; c = c->next)
if (c->is6 &&
is_same_net6(addr, &c->start6, 64) &&
addrpart >= addr6part(&c->start6) &&
addrpart <= addr6part(&c->end6))
return c->domain;
return daemon->domain_suffix;
}
#endif
#ifdef HAVE_DHCP #ifdef HAVE_DHCP
struct in_addr a_record_from_hosts(char *name, time_t now)
{
struct crec *crecp = NULL;
struct in_addr ret;
while ((crecp = cache_find_by_name(crecp, name, now, F_IPV4)))
if (crecp->flags & F_HOSTS)
return *(struct in_addr *)&crecp->addr;
my_syslog(MS_DHCP | LOG_WARNING, _("No IPv4 address found for %s"), name);
ret.s_addr = 0;
return ret;
}
void cache_unhash_dhcp(void) void cache_unhash_dhcp(void)
{ {
struct crec *cache, **up; struct crec *cache, **up;
@@ -939,15 +973,24 @@ void cache_unhash_dhcp(void)
up = &cache->hash_next; up = &cache->hash_next;
} }
void cache_add_dhcp_entry(char *host_name, void cache_add_dhcp_entry(char *host_name, int prot,
struct in_addr *host_address, time_t ttd) struct all_addr *host_address, time_t ttd)
{ {
struct crec *crec = NULL, *aliasc; struct crec *crec = NULL, *aliasc;
unsigned short flags = F_NAMEP | F_DHCP | F_FORWARD | F_IPV4 | F_REVERSE; unsigned short flags = F_IPV4;
int in_hosts = 0; int in_hosts = 0;
struct cname *a; struct cname *a;
size_t addrlen = sizeof(struct in_addr);
#ifdef HAVE_IPV6
if (prot == AF_INET6)
{
flags = F_IPV6;
addrlen = sizeof(struct in6_addr);
}
#endif
while ((crec = cache_find_by_name(crec, host_name, 0, F_IPV4 | F_CNAME))) while ((crec = cache_find_by_name(crec, host_name, 0, flags | F_CNAME)))
{ {
/* check all addresses associated with name */ /* check all addresses associated with name */
if (crec->flags & F_HOSTS) if (crec->flags & F_HOSTS)
@@ -955,23 +998,25 @@ void cache_add_dhcp_entry(char *host_name,
/* if in hosts, don't need DHCP record */ /* if in hosts, don't need DHCP record */
in_hosts = 1; in_hosts = 1;
inet_ntop(prot, host_address, daemon->addrbuff, ADDRSTRLEN);
if (crec->flags & F_CNAME) if (crec->flags & F_CNAME)
my_syslog(MS_DHCP | LOG_WARNING, my_syslog(MS_DHCP | LOG_WARNING,
_("%s is a CNAME, not giving it to the DHCP lease of %s"), _("%s is a CNAME, not giving it to the DHCP lease of %s"),
host_name, inet_ntoa(*host_address)); host_name, daemon->addrbuff);
else if (crec->addr.addr.addr.addr4.s_addr != host_address->s_addr) else if (memcmp(&crec->addr.addr, host_address, addrlen) != 0)
{ {
strcpy(daemon->namebuff, inet_ntoa(crec->addr.addr.addr.addr4)); inet_ntop(prot, &crec->addr.addr, daemon->namebuff, MAXDNAME);
my_syslog(MS_DHCP | LOG_WARNING, my_syslog(MS_DHCP | LOG_WARNING,
_("not giving name %s to the DHCP lease of %s because " _("not giving name %s to the DHCP lease of %s because "
"the name exists in %s with address %s"), "the name exists in %s with address %s"),
host_name, inet_ntoa(*host_address), host_name, daemon->addrbuff,
record_source(crec->uid), daemon->namebuff); record_source(crec->uid), daemon->namebuff);
} }
} }
else if (!(crec->flags & F_DHCP)) else if (!(crec->flags & F_DHCP))
{ {
cache_scan_free(host_name, NULL, 0, crec->flags & (F_IPV4 | F_CNAME | F_FORWARD)); cache_scan_free(host_name, NULL, 0, crec->flags & (flags | F_CNAME | F_FORWARD));
/* scan_free deletes all addresses associated with name */ /* scan_free deletes all addresses associated with name */
break; break;
} }
@@ -980,14 +1025,16 @@ void cache_add_dhcp_entry(char *host_name,
if (in_hosts) if (in_hosts)
return; return;
if ((crec = cache_find_by_addr(NULL, (struct all_addr *)host_address, 0, F_IPV4))) if ((crec = cache_find_by_addr(NULL, (struct all_addr *)host_address, 0, flags)))
{ {
if (crec->flags & F_NEG) if (crec->flags & F_NEG)
cache_scan_free(NULL, (struct all_addr *)host_address, 0, F_IPV4 | F_REVERSE); {
else flags |= F_REVERSE;
/* avoid multiple reverse mappings */ cache_scan_free(NULL, (struct all_addr *)host_address, 0, flags);
flags &= ~F_REVERSE; }
} }
else
flags |= F_REVERSE;
if ((crec = dhcp_spare)) if ((crec = dhcp_spare))
dhcp_spare = dhcp_spare->next; dhcp_spare = dhcp_spare->next;
@@ -996,12 +1043,12 @@ void cache_add_dhcp_entry(char *host_name,
if (crec) /* malloc may fail */ if (crec) /* malloc may fail */
{ {
crec->flags = flags; crec->flags = flags | F_NAMEP | F_DHCP | F_FORWARD;
if (ttd == 0) if (ttd == 0)
crec->flags |= F_IMMORTAL; crec->flags |= F_IMMORTAL;
else else
crec->ttd = ttd; crec->ttd = ttd;
crec->addr.addr.addr.addr4 = *host_address; crec->addr.addr = *host_address;
crec->name.namep = host_name; crec->name.namep = host_name;
crec->uid = uid++; crec->uid = uid++;
cache_hash(crec); cache_hash(crec);
@@ -1042,9 +1089,6 @@ void dump_cache(time_t now)
my_syslog(LOG_INFO, _("queries forwarded %u, queries answered locally %u"), my_syslog(LOG_INFO, _("queries forwarded %u, queries answered locally %u"),
daemon->queries_forwarded, daemon->local_answer); daemon->queries_forwarded, daemon->local_answer);
if (!addrbuff && !(addrbuff = whine_malloc(ADDRSTRLEN)))
return;
/* sum counts from different records for same server */ /* sum counts from different records for same server */
for (serv = daemon->servers; serv; serv = serv->next) for (serv = daemon->servers; serv; serv = serv->next)
serv->flags &= ~SERV_COUNTED; serv->flags &= ~SERV_COUNTED;
@@ -1064,8 +1108,8 @@ void dump_cache(time_t now)
queries += serv1->queries; queries += serv1->queries;
failed_queries += serv1->failed_queries; failed_queries += serv1->failed_queries;
} }
port = prettyprint_addr(&serv->addr, addrbuff); port = prettyprint_addr(&serv->addr, daemon->addrbuff);
my_syslog(LOG_INFO, _("server %s#%d: queries sent %u, retried or failed %u"), addrbuff, port, queries, failed_queries); my_syslog(LOG_INFO, _("server %s#%d: queries sent %u, retried or failed %u"), daemon->addrbuff, port, queries, failed_queries);
} }
if (option_bool(OPT_DEBUG) || option_bool(OPT_LOG)) if (option_bool(OPT_DEBUG) || option_bool(OPT_LOG))
@@ -1090,11 +1134,11 @@ void dump_cache(time_t now)
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
else else
{ {
a = addrbuff; a = daemon->addrbuff;
if (cache->flags & F_IPV4) if (cache->flags & F_IPV4)
inet_ntop(AF_INET, &cache->addr.addr, addrbuff, ADDRSTRLEN); inet_ntop(AF_INET, &cache->addr.addr, a, ADDRSTRLEN);
else if (cache->flags & F_IPV6) else if (cache->flags & F_IPV6)
inet_ntop(AF_INET6, &cache->addr.addr, addrbuff, ADDRSTRLEN); inet_ntop(AF_INET6, &cache->addr.addr, a, ADDRSTRLEN);
} }
#else #else
else else
@@ -1149,7 +1193,7 @@ void querystr(char *str, unsigned short type)
void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg) void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg)
{ {
char *source, *dest = addrbuff; char *source, *dest = daemon->addrbuff;
char *verb = "is"; char *verb = "is";
if (!option_bool(OPT_LOG)) if (!option_bool(OPT_LOG))
@@ -1159,16 +1203,16 @@ void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg)
{ {
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6, inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6,
addr, addrbuff, ADDRSTRLEN); addr, daemon->addrbuff, ADDRSTRLEN);
#else #else
strncpy(addrbuff, inet_ntoa(addr->addr.addr4), ADDRSTRLEN); strncpy(daemon->addrbuff, inet_ntoa(addr->addr.addr4), ADDRSTRLEN);
#endif #endif
} }
if (flags & F_REVERSE) if (flags & F_REVERSE)
{ {
dest = name; dest = name;
name = addrbuff; name = daemon->addrbuff;
} }
if (flags & F_NEG) if (flags & F_NEG)
+207 -120
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2011 Simon Kelley /* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -14,8 +14,6 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#define VERSION "2.57"
#define FTABSIZ 150 /* max number of outstanding requests (default) */ #define FTABSIZ 150 /* max number of outstanding requests (default) */
#define MAX_PROCS 20 /* max no children for TCP requests */ #define MAX_PROCS 20 /* max no children for TCP requests */
#define CHILD_LIFETIME 150 /* secs 'till terminated (RFC1035 suggests > 120s) */ #define CHILD_LIFETIME 150 /* secs 'till terminated (RFC1035 suggests > 120s) */
@@ -34,12 +32,102 @@
#define SMALLDNAME 40 /* most domain names are smaller than this */ #define SMALLDNAME 40 /* most domain names are smaller than this */
#define HOSTSFILE "/etc/hosts" #define HOSTSFILE "/etc/hosts"
#define ETHERSFILE "/etc/ethers" #define ETHERSFILE "/etc/ethers"
#ifdef __uClinux__
# define RESOLVFILE "/etc/config/resolv.conf"
#else
# define RESOLVFILE "/etc/resolv.conf"
#endif
#define RUNFILE "/var/run/dnsmasq.pid" #define RUNFILE "/var/run/dnsmasq.pid"
#define DEFLEASE 3600 /* default lease time, 1 hour */
#define CHUSER "nobody"
#define CHGRP "dip"
#define TFTP_MAX_CONNECTIONS 50 /* max simultaneous connections */
#define LOG_MAX 5 /* log-queue length */
#define RANDFILE "/dev/urandom"
#define EDNS0_OPTION_MAC 5 /* dyndns.org temporary assignment */
#define DNSMASQ_SERVICE "uk.org.thekelleys.dnsmasq" /* DBUS interface specifics */
#define DNSMASQ_PATH "/uk/org/thekelleys/dnsmasq"
/* compile-time options: uncomment below to enable or do eg.
make COPTS=-DHAVE_BROKEN_RTC
HAVE_BROKEN_RTC
define this on embedded systems which don't have an RTC
which keeps time over reboots. Causes dnsmasq to use uptime
for timing, and keep lease lengths rather than expiry times
in its leases file. This also make dnsmasq "flash disk friendly".
Normally, dnsmasq tries very hard to keep the on-disk leases file
up-to-date: rewriting it after every renewal. When HAVE_BROKEN_RTC
is in effect, the lease file is only written when a new lease is
created, or an old one destroyed. (Because those are the only times
it changes.) This vastly reduces the number of file writes, and makes
it viable to keep the lease file on a flash filesystem.
NOTE: when enabling or disabling this, be sure to delete any old
leases file, otherwise dnsmasq may get very confused.
HAVE_TFTP
define this to get dnsmasq's built-in TFTP server.
HAVE_DHCP
define this to get dnsmasq's DHCPv4 server.
HAVE_DHCP6
define this to get dnsmasq's DHCPv6 server. (implies HAVE_DHCP).
HAVE_SCRIPT
define this to get the ability to call scripts on lease-change.
HAVE_LUASCRIPT
define this to get the ability to call Lua script on lease-change. (implies HAVE_SCRIPT)
HAVE_DBUS
define this if you want to link against libdbus, and have dnsmasq
support some methods to allow (re)configuration of the upstream DNS
servers via DBus.
HAVE_IDN
define this if you want international domain name support.
NOTE: for backwards compatibility, IDN support is automatically
included when internationalisation support is built, using the
*-i18n makefile targets, even if HAVE_IDN is not explicitly set.
HAVE_CONNTRACK
define this to include code which propogates conntrack marks from
incoming DNS queries to the corresponding upstream queries. This adds
a build-dependency on libnetfilter_conntrack, but the resulting binary will
still run happily on a kernel without conntrack support.
NO_IPV6
NO_TFTP
NO_DHCP
NO_DHCP6
NO_SCRIPT
NO_LARGEFILE
these are avilable to explictly disable compile time options which would
otherwise be enabled automatically (HAVE_IPV6, >2Gb file sizes) or
which are enabled by default in the distributed source tree. Building dnsmasq
with something like "make COPTS=-DNO_SCRIPT" will do the trick.
LEASEFILE
CONFFILE
RESOLVFILE
the default locations of these files are determined below, but may be overridden
in a build command line using COPTS.
*/
/* The default set of options to build. Built with these options, dnsmasq
has no library dependencies other than libc */
#define HAVE_DHCP
#define HAVE_DHCP6
#define HAVE_TFTP
#define HAVE_SCRIPT
/* #define HAVE_LUASCRIPT */
/* #define HAVE_BROKEN_RTC */
/* #define HAVE_DBUS */
/* #define HAVE_IDN */
/* #define HAVE_CONNTRACK */
/* Default locations for important system files. */
#ifndef LEASEFILE #ifndef LEASEFILE
# if defined(__FreeBSD__) || defined (__OpenBSD__) || defined(__DragonFly__) || defined(__NetBSD__) # if defined(__FreeBSD__) || defined (__OpenBSD__) || defined(__DragonFly__) || defined(__NetBSD__)
@@ -61,124 +149,34 @@
# endif # endif
#endif #endif
#define DEFLEASE 3600 /* default lease time, 1 hour */ #ifndef RESOLVFILE
#define CHUSER "nobody" # if defined(__uClinux__)
#define CHGRP "dip" # define RESOLVFILE "/etc/config/resolv.conf"
#define NAMESERVER_PORT 53 # else
#define DHCP_SERVER_PORT 67 # define RESOLVFILE "/etc/resolv.conf"
#define DHCP_CLIENT_PORT 68 # endif
#define DHCP_SERVER_ALTPORT 1067 #endif
#define DHCP_CLIENT_ALTPORT 1068
#define PXE_PORT 4011
#define TFTP_PORT 69
#define TFTP_MAX_CONNECTIONS 50 /* max simultaneous connections */
#define LOG_MAX 5 /* log-queue length */
#define RANDFILE "/dev/urandom"
#define DAD_WAIT 20 /* retry binding IPv6 sockets for this long */
#define EDNS0_OPTION_MAC 5 /* dyndns.org temporary assignment */
/* DBUS interface specifics */
#define DNSMASQ_SERVICE "uk.org.thekelleys.dnsmasq"
#define DNSMASQ_PATH "/uk/org/thekelleys/dnsmasq"
/* Follows system specific switches. If you run on a
new system, you may want to edit these. /* platform dependent options: these are determined automatically below
May replace this with Autoconf one day.
HAVE_LINUX_NETWORK HAVE_LINUX_NETWORK
HAVE_BSD_NETWORK HAVE_BSD_NETWORK
HAVE_SOLARIS_NETWORK HAVE_SOLARIS_NETWORK
define exactly one of these to alter interaction with kernel networking. define exactly one of these to alter interaction with kernel networking.
HAVE_BROKEN_RTC
define this on embedded systems which don't have an RTC
which keeps time over reboots. Causes dnsmasq to use uptime
for timing, and keep lease lengths rather than expiry times
in its leases file. This also make dnsmasq "flash disk friendly".
Normally, dnsmasq tries very hard to keep the on-disk leases file
up-to-date: rewriting it after every renewal. When HAVE_BROKEN_RTC
is in effect, the lease file is only written when a new lease is
created, or an old one destroyed. (Because those are the only times
it changes.) This vastly reduces the number of file writes, and makes
it viable to keep the lease file on a flash filesystem.
NOTE: when enabling or disabling this, be sure to delete any old
leases file, otherwise dnsmasq may get very confused.
HAVE_TFTP
define this to get dnsmasq's built-in TFTP server.
HAVE_DHCP
define this to get dnsmasq's DHCP server.
HAVE_SCRIPT
define this to get the ability to call scripts on lease-change
HAVE_GETOPT_LONG HAVE_GETOPT_LONG
define this if you have GNU libc or GNU getopt. defined when GNU-sty;e getopt_long available.
HAVE_ARC4RANDOM HAVE_ARC4RANDOM
define this if you have arc4random() to get better security from DNS spoofs defined if arc4random() available to get better security from DNS spoofs
by using really random ids (OpenBSD) by using really random ids (OpenBSD)
HAVE_SOCKADDR_SA_LEN HAVE_SOCKADDR_SA_LEN
define this if struct sockaddr has sa_len field (*BSD) defined if struct sockaddr has sa_len field (*BSD)
HAVE_DBUS
define this if you want to link against libdbus, and have dnsmasq
support some methods to allow (re)configuration of the upstream DNS
servers via DBus.
HAVE_IDN
define this if you want international domain name support.
NOTE: for backwards compatibility, IDN support is automatically
included when internationalisation support is built, using the
*-i18n makefile targets, even if HAVE_IDN is not explicitly set.
NOTES:
For Linux you should define
HAVE_LINUX_NETWORK
HAVE_GETOPT_LONG
you should NOT define
HAVE_ARC4RANDOM
HAVE_SOCKADDR_SA_LEN
For *BSD systems you should define
HAVE_BSD_NETWORK
HAVE_SOCKADDR_SA_LEN
and you MAY define
HAVE_ARC4RANDOM - OpenBSD and FreeBSD and NetBSD version 2.0 or later
HAVE_GETOPT_LONG - NetBSD, later FreeBSD
(FreeBSD and OpenBSD only if you link GNU getopt)
*/ */
/* platform independent options- uncomment to enable */
#define HAVE_DHCP
#define HAVE_TFTP
#define HAVE_SCRIPT
/* #define HAVE_BROKEN_RTC */
/* #define HAVE_DBUS */
/* #define HAVE_IDN */
/* Allow TFTP to be disabled with COPTS=-DNO_TFTP */
#ifdef NO_TFTP
#undef HAVE_TFTP
#endif
/* Allow DHCP to be disabled with COPTS=-DNO_DHCP */
#ifdef NO_DHCP
#undef HAVE_DHCP
#endif
/* Allow scripts to be disabled with COPTS=-DNO_SCRIPT */
#ifdef NO_SCRIPT
#undef HAVE_SCRIPT
#endif
/* platform dependent options. */
/* Must preceed __linux__ since uClinux defines __linux__ too. */ /* Must preceed __linux__ since uClinux defines __linux__ too. */
#if defined(__uClinux__) #if defined(__uClinux__)
#define HAVE_LINUX_NETWORK #define HAVE_LINUX_NETWORK
@@ -253,18 +251,12 @@ NOTES:
#endif #endif
/* Decide if we're going to support IPv6 */ /* Decide if we're going to support IPv6 */
/* IPv6 can be forced off with "make COPTS=-DNO_IPV6" */
/* We assume that systems which don't have IPv6 /* We assume that systems which don't have IPv6
headers don't have ntop and pton either */ headers don't have ntop and pton either */
#if defined(INET6_ADDRSTRLEN) && defined(IPV6_V6ONLY) && !defined(NO_IPV6) #if defined(INET6_ADDRSTRLEN) && defined(IPV6_V6ONLY)
# define HAVE_IPV6 # define HAVE_IPV6
# define ADDRSTRLEN INET6_ADDRSTRLEN # define ADDRSTRLEN INET6_ADDRSTRLEN
# if defined(SOL_IPV6)
# define IPV6_LEVEL SOL_IPV6
# else
# define IPV6_LEVEL IPPROTO_IPV6
# endif
#elif defined(INET_ADDRSTRLEN) #elif defined(INET_ADDRSTRLEN)
# undef HAVE_IPV6 # undef HAVE_IPV6
# define ADDRSTRLEN INET_ADDRSTRLEN # define ADDRSTRLEN INET_ADDRSTRLEN
@@ -273,8 +265,103 @@ NOTES:
# define ADDRSTRLEN 16 /* 4*3 + 3 dots + NULL */ # define ADDRSTRLEN 16 /* 4*3 + 3 dots + NULL */
#endif #endif
/* Can't do scripts without fork */
#ifdef NOFORK /* rules to implement compile-time option dependencies and
# undef HAVE_SCRIPT the NO_XXX flags */
#ifdef NO_IPV6
#undef HAVE_IPV6
#endif #endif
#ifdef NO_TFTP
#undef HAVE_TFTP
#endif
#ifdef NO_DHCP
#undef HAVE_DHCP
#undef HAVE_DHCP6
#endif
#if defined(NO_DHCP6) || !defined(HAVE_IPV6)
#undef HAVE_DHCP6
#endif
/* DHCP6 needs DHCP too */
#ifdef HAVE_DHCP6
#define HAVE_DHCP
#endif
#if defined(NO_SCRIPT) || !defined(HAVE_DHCP) || defined(NO_FORK)
#undef HAVE_SCRIPT
#undef HAVE_LUASCRIPT
#endif
/* Must HAVE_SCRIPT to HAVE_LUASCRIPT */
#ifdef HAVE_LUASCRIPT
#define HAVE_SCRIPT
#endif
/* Define a string indicating which options are in use.
DNSMASQP_COMPILE_OPTS is only defined in dnsmasq.c */
#ifdef DNSMASQ_COMPILE_OPTS
static char *compile_opts =
#ifndef HAVE_IPV6
"no-"
#endif
"IPv6 "
#ifndef HAVE_GETOPT_LONG
"no-"
#endif
"GNU-getopt "
#ifdef HAVE_BROKEN_RTC
"no-RTC "
#endif
#ifdef NO_FORK
"no-MMU "
#endif
#ifndef HAVE_DBUS
"no-"
#endif
"DBus "
#ifndef LOCALEDIR
"no-"
#endif
"i18n "
#if !defined(LOCALEDIR) && !defined(HAVE_IDN)
"no-"
#endif
"IDN "
#ifndef HAVE_DHCP
"no-"
#endif
"DHCP "
#if defined(HAVE_DHCP)
# if !defined (HAVE_DHCP6)
"no-"
# endif
"DHCPv6 "
# if !defined(HAVE_SCRIPT)
"no-scripts "
# else
# if !defined(HAVE_LUASCRIPT)
"no-"
# endif
"Lua "
# endif
#endif
#ifndef HAVE_TFTP
"no-"
#endif
"TFTP "
#ifndef HAVE_CONNTRACK
"no-"
#endif
"conntrack";
#endif
+90
View File
@@ -0,0 +1,90 @@
/* dnsmasq is Copyright (c) 2000-2012 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, or
(at your option) version 3 dated 29 June, 2007.
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.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dnsmasq.h"
#ifdef HAVE_CONNTRACK
#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
static int gotit = 0; /* yuck */
static int callback(enum nf_conntrack_msg_type type, struct nf_conntrack *ct, void *data);
int get_incoming_mark(union mysockaddr *peer_addr, struct all_addr *local_addr, int istcp, unsigned int *markp)
{
struct nf_conntrack *ct;
struct nfct_handle *h;
gotit = 0;
if ((ct = nfct_new()))
{
nfct_set_attr_u8(ct, ATTR_L4PROTO, istcp ? IPPROTO_TCP : IPPROTO_UDP);
nfct_set_attr_u16(ct, ATTR_PORT_DST, htons(daemon->port));
#ifdef HAVE_IPV6
if (peer_addr->sa.sa_family == AF_INET6)
{
nfct_set_attr_u8(ct, ATTR_L3PROTO, AF_INET6);
nfct_set_attr(ct, ATTR_IPV6_SRC, peer_addr->in6.sin6_addr.s6_addr);
nfct_set_attr_u16(ct, ATTR_PORT_SRC, peer_addr->in6.sin6_port);
nfct_set_attr(ct, ATTR_IPV6_DST, local_addr->addr.addr6.s6_addr);
}
else
#endif
{
nfct_set_attr_u8(ct, ATTR_L3PROTO, AF_INET);
nfct_set_attr_u32(ct, ATTR_IPV4_SRC, peer_addr->in.sin_addr.s_addr);
nfct_set_attr_u16(ct, ATTR_PORT_SRC, peer_addr->in.sin_port);
nfct_set_attr_u32(ct, ATTR_IPV4_DST, local_addr->addr.addr4.s_addr);
}
if ((h = nfct_open(CONNTRACK, 0)))
{
nfct_callback_register(h, NFCT_T_ALL, callback, (void *)markp);
if (nfct_query(h, NFCT_Q_GET, ct) == -1)
{
static int warned = 0;
if (!warned)
{
my_syslog(LOG_ERR, _("Conntrack connection mark retrieval failed: %s"), strerror(errno));
warned = 1;
}
}
nfct_close(h);
}
nfct_destroy(ct);
}
return gotit;
}
static int callback(enum nf_conntrack_msg_type type, struct nf_conntrack *ct, void *data)
{
unsigned int *ret = (unsigned int *)data;
*ret = nfct_get_attr_u32(ct, ATTR_MARK);
(void)type; /* eliminate warning */
gotit = 1;
return NFCT_CB_CONTINUE;
}
#endif
+1 -1
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2011 Simon Kelley /* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
+361
View File
@@ -0,0 +1,361 @@
/* dnsmasq is Copyright (c) 2000-2012 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, or
(at your option) version 3 dated 29 June, 2007.
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.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dnsmasq.h"
#ifdef HAVE_DHCP
void dhcp_common_init(void)
{
/* These each hold a DHCP option max size 255
and get a terminating zero added */
daemon->dhcp_buff = safe_malloc(256);
daemon->dhcp_buff2 = safe_malloc(256);
daemon->dhcp_buff3 = safe_malloc(256);
/* dhcp_packet is used by v4 and v6, outpacket only by v6
sizeof(struct dhcp_packet) is as good an initial size as any,
even for v6 */
expand_buf(&daemon->dhcp_packet, sizeof(struct dhcp_packet));
#ifdef HAVE_DHCP6
if (daemon->dhcp6)
expand_buf(&daemon->outpacket, sizeof(struct dhcp_packet));
#endif
}
ssize_t recv_dhcp_packet(int fd, struct msghdr *msg)
{
ssize_t sz;
while (1)
{
msg->msg_flags = 0;
while ((sz = recvmsg(fd, msg, MSG_PEEK | MSG_TRUNC)) == -1 && errno == EINTR);
if (sz == -1)
return -1;
if (!(msg->msg_flags & MSG_TRUNC))
break;
/* Very new Linux kernels return the actual size needed,
older ones always return truncated size */
if ((size_t)sz == daemon->dhcp_packet.iov_len)
{
if (!expand_buf(&daemon->dhcp_packet, sz + 100))
return -1;
}
else
{
expand_buf(&daemon->dhcp_packet, sz);
break;
}
}
while ((sz = recvmsg(fd, msg, 0)) == -1 && errno == EINTR);
return (msg->msg_flags & MSG_TRUNC) ? -1 : sz;
}
struct dhcp_netid *run_tag_if(struct dhcp_netid *tags)
{
struct tag_if *exprs;
struct dhcp_netid_list *list;
for (exprs = daemon->tag_if; exprs; exprs = exprs->next)
if (match_netid(exprs->tag, tags, 1))
for (list = exprs->set; list; list = list->next)
{
list->list->next = tags;
tags = list->list;
}
return tags;
}
struct dhcp_netid *option_filter(struct dhcp_netid *tags, struct dhcp_netid *context_tags, struct dhcp_opt *opts)
{
struct dhcp_netid *tagif = run_tag_if(tags);
struct dhcp_opt *opt;
/* flag options which are valid with the current tag set (sans context tags) */
for (opt = opts; opt; opt = opt->next)
{
opt->flags &= ~DHOPT_TAGOK;
if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)) &&
match_netid(opt->netid, tagif, 0))
opt->flags |= DHOPT_TAGOK;
}
/* now flag options which are valid, including the context tags,
otherwise valid options are inhibited if we found a higher priotity one above */
if (context_tags)
{
struct dhcp_netid *last_tag;
for (last_tag = context_tags; last_tag->next; last_tag = last_tag->next);
last_tag->next = tags;
tagif = run_tag_if(context_tags);
for (opt = opts; opt; opt = opt->next)
if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925 | DHOPT_TAGOK)) &&
match_netid(opt->netid, tagif, 0))
{
struct dhcp_opt *tmp;
for (tmp = opts; tmp; tmp = tmp->next)
if (tmp->opt == opt->opt && opt->netid && (tmp->flags & DHOPT_TAGOK))
break;
if (!tmp)
opt->flags |= DHOPT_TAGOK;
}
}
/* now flag untagged options which are not overridden by tagged ones */
for (opt = opts; opt; opt = opt->next)
if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925 | DHOPT_TAGOK)) && !opt->netid)
{
struct dhcp_opt *tmp;
for (tmp = opts; tmp; tmp = tmp->next)
if (tmp->opt == opt->opt && (tmp->flags & DHOPT_TAGOK))
break;
if (!tmp)
opt->flags |= DHOPT_TAGOK;
else if (!tmp->netid)
my_syslog(MS_DHCP | LOG_WARNING, _("Ignoring duplicate dhcp-option %d"), tmp->opt);
}
return tagif;
}
/* Is every member of check matched by a member of pool?
If tagnotneeded, untagged is OK */
int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool, int tagnotneeded)
{
struct dhcp_netid *tmp1;
if (!check && !tagnotneeded)
return 0;
for (; check; check = check->next)
{
/* '#' for not is for backwards compat. */
if (check->net[0] != '!' && check->net[0] != '#')
{
for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
if (strcmp(check->net, tmp1->net) == 0)
break;
if (!tmp1)
return 0;
}
else
for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
if (strcmp((check->net)+1, tmp1->net) == 0)
return 0;
}
return 1;
}
/* return domain or NULL if none. */
char *strip_hostname(char *hostname)
{
char *dot = strchr(hostname, '.');
if (!dot)
return NULL;
*dot = 0; /* truncate */
if (strlen(dot+1) != 0)
return dot+1;
return NULL;
}
void log_tags(struct dhcp_netid *netid, u32 xid)
{
if (netid && option_bool(OPT_LOG_OPTS))
{
char *s = daemon->namebuff;
for (*s = 0; netid; netid = netid->next)
{
/* kill dupes. */
struct dhcp_netid *n;
for (n = netid->next; n; n = n->next)
if (strcmp(netid->net, n->net) == 0)
break;
if (!n)
{
strncat (s, netid->net, (MAXDNAME-1) - strlen(s));
if (netid->next)
strncat (s, ", ", (MAXDNAME-1) - strlen(s));
}
}
my_syslog(MS_DHCP | LOG_INFO, _("%u tags: %s"), xid, s);
}
}
int match_bytes(struct dhcp_opt *o, unsigned char *p, int len)
{
int i;
if (o->len > len)
return 0;
if (o->len == 0)
return 1;
if (o->flags & DHOPT_HEX)
{
if (memcmp_masked(o->val, p, o->len, o->u.wildcard_mask))
return 1;
}
else
for (i = 0; i <= (len - o->len); )
{
if (memcmp(o->val, p + i, o->len) == 0)
return 1;
if (o->flags & DHOPT_STRING)
i++;
else
i += o->len;
}
return 0;
}
void check_dhcp_hosts(int fatal)
{
/* If the same IP appears in more than one host config, then DISCOVER
for one of the hosts will get the address, but REQUEST will be NAKed,
since the address is reserved by the other one -> protocol loop.
Also check that FQDNs match the domain we are using. */
struct dhcp_config *configs, *cp;
for (configs = daemon->dhcp_conf; configs; configs = configs->next)
{
char *domain;
if ((configs->flags & DHOPT_BANK) || fatal)
{
for (cp = configs->next; cp; cp = cp->next)
if ((configs->flags & cp->flags & CONFIG_ADDR) && configs->addr.s_addr == cp->addr.s_addr)
{
if (fatal)
die(_("duplicate IP address %s in dhcp-config directive."),
inet_ntoa(cp->addr), EC_BADCONF);
else
my_syslog(MS_DHCP | LOG_ERR, _("duplicate IP address %s in %s."),
inet_ntoa(cp->addr), daemon->dhcp_hosts_file);
configs->flags &= ~CONFIG_ADDR;
}
/* split off domain part */
if ((configs->flags & CONFIG_NAME) && (domain = strip_hostname(configs->hostname)))
configs->domain = domain;
}
}
}
void dhcp_update_configs(struct dhcp_config *configs)
{
/* Some people like to keep all static IP addresses in /etc/hosts.
This goes through /etc/hosts and sets static addresses for any DHCP config
records which don't have an address and whose name matches.
We take care to maintain the invariant that any IP address can appear
in at most one dhcp-host. Since /etc/hosts can be re-read by SIGHUP,
restore the status-quo ante first. */
struct dhcp_config *config;
struct crec *crec;
int prot = AF_INET;
for (config = configs; config; config = config->next)
if (config->flags & CONFIG_ADDR_HOSTS)
config->flags &= ~(CONFIG_ADDR | CONFIG_ADDR6 | CONFIG_ADDR_HOSTS);
#ifdef HAVE_DHCP6
again:
#endif
if (daemon->port != 0)
for (config = configs; config; config = config->next)
{
int conflags = CONFIG_ADDR;
int cacheflags = F_IPV4;
#ifdef HAVE_DHCP6
if (prot == AF_INET6)
{
conflags = CONFIG_ADDR6;
cacheflags = F_IPV6;
}
#endif
if (!(config->flags & conflags) &&
(config->flags & CONFIG_NAME) &&
(crec = cache_find_by_name(NULL, config->hostname, 0, cacheflags)) &&
(crec->flags & F_HOSTS))
{
if (cache_find_by_name(crec, config->hostname, 0, cacheflags))
{
/* use primary (first) address */
while (crec && !(crec->flags & F_REVERSE))
crec = cache_find_by_name(crec, config->hostname, 0, cacheflags);
if (!crec)
continue; /* should be never */
inet_ntop(prot, &crec->addr.addr, daemon->addrbuff, ADDRSTRLEN);
my_syslog(MS_DHCP | LOG_WARNING, _("%s has more than one address in hostsfile, using %s for DHCP"),
config->hostname, daemon->addrbuff);
}
if (prot == AF_INET && !config_find_by_address(configs, crec->addr.addr.addr.addr4))
{
config->addr = crec->addr.addr.addr.addr4;
config->flags |= CONFIG_ADDR | CONFIG_ADDR_HOSTS;
continue;
}
#ifdef HAVE_DHCP6
if (prot == AF_INET6 && !config_find_by_address6(configs, &crec->addr.addr.addr.addr6, 129, 0))
{
memcpy(config->hwaddr, &crec->addr.addr.addr.addr6, IN6ADDRSZ);
config->flags |= CONFIG_ADDR6 | CONFIG_ADDR_HOSTS;
continue;
}
#endif
inet_ntop(prot, &crec->addr.addr, daemon->addrbuff, ADDRSTRLEN);
my_syslog(MS_DHCP | LOG_WARNING, _("duplicate IP address %s (%s) in dhcp-config directive"),
daemon->addrbuff, config->hostname);
}
}
#ifdef HAVE_DHCP6
if (prot == AF_INET)
{
prot = AF_INET6;
goto again;
}
#endif
}
#endif
+179 -283
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2011 Simon Kelley /* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -19,13 +19,19 @@
#ifdef HAVE_DHCP #ifdef HAVE_DHCP
struct iface_param { struct iface_param {
struct in_addr relay, primary;
struct dhcp_context *current; struct dhcp_context *current;
int ind; int ind;
}; };
struct match_param {
int ind, matched;
struct in_addr netmask, broadcast, addr;
};
static int complete_context(struct in_addr local, int if_index, static int complete_context(struct in_addr local, int if_index,
struct in_addr netmask, struct in_addr broadcast, void *vparam); struct in_addr netmask, struct in_addr broadcast, void *vparam);
static int check_listen_addrs(struct in_addr local, int if_index,
struct in_addr netmask, struct in_addr broadcast, void *vparam);
static int make_fd(int port) static int make_fd(int port)
{ {
@@ -35,16 +41,22 @@ static int make_fd(int port)
#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
int mtu = IP_PMTUDISC_DONT; int mtu = IP_PMTUDISC_DONT;
#endif #endif
#if defined(IP_TOS) && defined(IPTOS_CLASS_CS6)
int tos = IPTOS_CLASS_CS6;
#endif
if (fd == -1) if (fd == -1)
die (_("cannot create DHCP socket: %s"), NULL, EC_BADNET); die (_("cannot create DHCP socket: %s"), NULL, EC_BADNET);
if (!fix_fd(fd) || if (!fix_fd(fd) ||
#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
setsockopt(fd, SOL_IP, IP_MTU_DISCOVER, &mtu, sizeof(mtu)) == -1 || setsockopt(fd, IPPROTO_IP, IP_MTU_DISCOVER, &mtu, sizeof(mtu)) == -1 ||
#endif
#if defined(IP_TOS) && defined(IPTOS_CLASS_CS6)
setsockopt(fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) == -1 ||
#endif #endif
#if defined(HAVE_LINUX_NETWORK) #if defined(HAVE_LINUX_NETWORK)
setsockopt(fd, SOL_IP, IP_PKTINFO, &oneopt, sizeof(oneopt)) == -1 || setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &oneopt, sizeof(oneopt)) == -1 ||
#else #else
setsockopt(fd, IPPROTO_IP, IP_RECVIF, &oneopt, sizeof(oneopt)) == -1 || setsockopt(fd, IPPROTO_IP, IP_RECVIF, &oneopt, sizeof(oneopt)) == -1 ||
#endif #endif
@@ -107,11 +119,8 @@ void dhcp_init(void)
#endif #endif
check_dhcp_hosts(1); check_dhcp_hosts(1);
daemon->dhcp_packet.iov_len = sizeof(struct dhcp_packet);
daemon->dhcp_packet.iov_base = safe_malloc(daemon->dhcp_packet.iov_len);
} }
void dhcp_packet(time_t now, int pxe_fd) void dhcp_packet(time_t now, int pxe_fd)
{ {
int fd = pxe_fd ? daemon->pxefd : daemon->dhcpfd; int fd = pxe_fd ? daemon->pxefd : daemon->dhcpfd;
@@ -125,7 +134,7 @@ void dhcp_packet(time_t now, int pxe_fd)
struct iovec iov; struct iovec iov;
ssize_t sz; ssize_t sz;
int iface_index = 0, unicast_dest = 0, is_inform = 0; int iface_index = 0, unicast_dest = 0, is_inform = 0;
struct in_addr iface_addr, *addrp = NULL; struct in_addr iface_addr;
struct iface_param parm; struct iface_param parm;
#ifdef HAVE_LINUX_NETWORK #ifdef HAVE_LINUX_NETWORK
struct arpreq arp_req; struct arpreq arp_req;
@@ -141,56 +150,23 @@ void dhcp_packet(time_t now, int pxe_fd)
char control[CMSG_SPACE(sizeof(struct sockaddr_dl))]; char control[CMSG_SPACE(sizeof(struct sockaddr_dl))];
#endif #endif
} control_u; } control_u;
struct dhcp_bridge *bridge, *alias;
msg.msg_control = NULL;
msg.msg_controllen = 0; msg.msg_controllen = sizeof(control_u);
msg.msg_name = NULL; msg.msg_control = control_u.control;
msg.msg_namelen = 0; msg.msg_name = &dest;
msg.msg_namelen = sizeof(dest);
msg.msg_iov = &daemon->dhcp_packet; msg.msg_iov = &daemon->dhcp_packet;
msg.msg_iovlen = 1; msg.msg_iovlen = 1;
while (1) if ((sz = recv_dhcp_packet(fd, &msg)) == -1 ||
{ (sz < (ssize_t)(sizeof(*mess) - sizeof(mess->options))))
msg.msg_flags = 0;
while ((sz = recvmsg(fd, &msg, MSG_PEEK | MSG_TRUNC)) == -1 && errno == EINTR);
if (sz == -1)
return;
if (!(msg.msg_flags & MSG_TRUNC))
break;
/* Very new Linux kernels return the actual size needed,
older ones always return truncated size */
if ((size_t)sz == daemon->dhcp_packet.iov_len)
{
if (!expand_buf(&daemon->dhcp_packet, sz + 100))
return;
}
else
{
expand_buf(&daemon->dhcp_packet, sz);
break;
}
}
/* expand_buf may have moved buffer */
mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
msg.msg_controllen = sizeof(control_u);
msg.msg_control = control_u.control;
msg.msg_flags = 0;
msg.msg_name = &dest;
msg.msg_namelen = sizeof(dest);
while ((sz = recvmsg(fd, &msg, 0)) == -1 && errno == EINTR);
if ((msg.msg_flags & MSG_TRUNC) || sz < (ssize_t)(sizeof(*mess) - sizeof(mess->options)))
return; return;
#if defined (HAVE_LINUX_NETWORK) #if defined (HAVE_LINUX_NETWORK)
if (msg.msg_controllen >= sizeof(struct cmsghdr)) if (msg.msg_controllen >= sizeof(struct cmsghdr))
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO) if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO)
{ {
union { union {
unsigned char *c; unsigned char *c;
@@ -237,26 +213,50 @@ void dhcp_packet(time_t now, int pxe_fd)
strncpy(arp_req.arp_dev, ifr.ifr_name, 16); strncpy(arp_req.arp_dev, ifr.ifr_name, 16);
#endif #endif
/* One form of bridging on BSD has the property that packets
can be recieved on bridge interfaces which do not have an IP address.
We allow these to be treated as aliases of another interface which does have
an IP address with --dhcp-bridge=interface,alias,alias */
for (bridge = daemon->bridges; bridge; bridge = bridge->next)
{
for (alias = bridge->alias; alias; alias = alias->next)
if (strncmp(ifr.ifr_name, alias->iface, IF_NAMESIZE) == 0)
{
if (!(iface_index = if_nametoindex(bridge->iface)))
{
my_syslog(LOG_WARNING, _("unknown interface %s in bridge-interface"), ifr.ifr_name);
return;
}
else
{
strncpy(ifr.ifr_name, bridge->iface, IF_NAMESIZE);
break;
}
}
if (alias)
break;
}
#ifdef MSG_BCAST #ifdef MSG_BCAST
/* OpenBSD tells us when a packet was broadcast */ /* OpenBSD tells us when a packet was broadcast */
if (!(msg.msg_flags & MSG_BCAST)) if (!(msg.msg_flags & MSG_BCAST))
unicast_dest = 1; unicast_dest = 1;
#endif #endif
ifr.ifr_addr.sa_family = AF_INET; ifr.ifr_addr.sa_family = AF_INET;
if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) != -1 ) if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) != -1 )
iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
else
{ {
addrp = &iface_addr; my_syslog(MS_DHCP | LOG_WARNING, _("DHCP packet received on %s which has no address"), ifr.ifr_name);
iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr; return;
} }
if (!iface_check(AF_INET, (struct all_addr *)addrp, ifr.ifr_name, &iface_index))
return;
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next) for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0)) if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
return; return;
/* weird libvirt-inspired access control */ /* weird libvirt-inspired access control */
for (context = daemon->dhcp; context; context = context->next) for (context = daemon->dhcp; context; context = context->next)
if (!context->interface || strcmp(context->interface, ifr.ifr_name) == 0) if (!context->interface || strcmp(context->interface, ifr.ifr_name) == 0)
@@ -264,42 +264,40 @@ void dhcp_packet(time_t now, int pxe_fd)
if (!context) if (!context)
return; return;
/* unlinked contexts are marked by context->current == context */ /* unlinked contexts are marked by context->current == context */
for (context = daemon->dhcp; context; context = context->next) for (context = daemon->dhcp; context; context = context->next)
context->current = context; context->current = context;
parm.relay = mess->giaddr;
parm.primary = iface_addr;
parm.current = NULL; parm.current = NULL;
parm.ind = iface_index; parm.ind = iface_index;
/* interface may have been changed by alias in iface_check, make sure it gets priority in case if (!iface_check(AF_INET, (struct all_addr *)&iface_addr, ifr.ifr_name))
there is more than one address on the interface in the same subnet */
if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) == -1)
{ {
my_syslog(MS_DHCP | LOG_WARNING, _("DHCP packet received on %s which has no address"), ifr.ifr_name); /* If we failed to match the primary address of the interface, see if we've got a --listen-address
return; for a secondary */
} struct match_param match;
else
{
iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
if (ioctl(daemon->dhcpfd, SIOCGIFNETMASK, &ifr) != -1)
{
struct in_addr netmask = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
if (ioctl(daemon->dhcpfd, SIOCGIFBRDADDR, &ifr) != -1)
{
struct in_addr broadcast = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
complete_context(iface_addr, iface_index, netmask, broadcast, &parm);
}
}
}
match.matched = 0;
match.ind = iface_index;
if (!daemon->if_addrs ||
!iface_enumerate(AF_INET, &match, check_listen_addrs) ||
!match.matched)
return;
iface_addr = match.addr;
/* make sure secondary address gets priority in case
there is more than one address on the interface in the same subnet */
complete_context(match.addr, iface_index, match.netmask, match.broadcast, &parm);
}
if (!iface_enumerate(AF_INET, &parm, complete_context)) if (!iface_enumerate(AF_INET, &parm, complete_context))
return; return;
lease_prune(NULL, now); /* lose any expired leases */ lease_prune(NULL, now); /* lose any expired leases */
iov.iov_len = dhcp_reply(parm.current, ifr.ifr_name, iface_index, (size_t)sz, iov.iov_len = dhcp_reply(parm.current, ifr.ifr_name, iface_index, (size_t)sz,
now, unicast_dest, &is_inform, pxe_fd); now, unicast_dest, &is_inform, pxe_fd, iface_addr);
lease_update_file(now); lease_update_file(now);
lease_update_dns(); lease_update_dns();
@@ -319,7 +317,7 @@ void dhcp_packet(time_t now, int pxe_fd)
#ifdef HAVE_SOCKADDR_SA_LEN #ifdef HAVE_SOCKADDR_SA_LEN
dest.sin_len = sizeof(struct sockaddr_in); dest.sin_len = sizeof(struct sockaddr_in);
#endif #endif
if (pxe_fd) if (pxe_fd)
{ {
if (mess->ciaddr.s_addr != 0) if (mess->ciaddr.s_addr != 0)
@@ -357,7 +355,7 @@ void dhcp_packet(time_t now, int pxe_fd)
pkt->ipi_ifindex = iface_index; pkt->ipi_ifindex = iface_index;
pkt->ipi_spec_dst.s_addr = 0; pkt->ipi_spec_dst.s_addr = 0;
msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
cmptr->cmsg_level = SOL_IP; cmptr->cmsg_level = IPPROTO_IP;
cmptr->cmsg_type = IP_PKTINFO; cmptr->cmsg_type = IP_PKTINFO;
dest.sin_addr.s_addr = INADDR_BROADCAST; dest.sin_addr.s_addr = INADDR_BROADCAST;
dest.sin_port = htons(daemon->dhcp_client_port); dest.sin_port = htons(daemon->dhcp_client_port);
@@ -414,6 +412,30 @@ void dhcp_packet(time_t now, int pxe_fd)
while(sendmsg(fd, &msg, 0) == -1 && retry_send()); while(sendmsg(fd, &msg, 0) == -1 && retry_send());
} }
/* check against secondary interface addresses */
static int check_listen_addrs(struct in_addr local, int if_index,
struct in_addr netmask, struct in_addr broadcast, void *vparam)
{
struct match_param *param = vparam;
struct iname *tmp;
if (if_index == param->ind)
{
for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
if ( tmp->addr.sa.sa_family == AF_INET &&
tmp->addr.in.sin_addr.s_addr == local.s_addr)
{
param->matched = 1;
param->addr = local;
param->netmask = netmask;
param->broadcast = broadcast;
break;
}
}
return 1;
}
/* This is a complex routine: it gets called with each (address,netmask,broadcast) triple /* This is a complex routine: it gets called with each (address,netmask,broadcast) triple
of each interface (and any relay address) and does the following things: of each interface (and any relay address) and does the following things:
@@ -448,38 +470,27 @@ static int complete_context(struct in_addr local, int if_index,
context->netmask = netmask; context->netmask = netmask;
} }
if (context->netmask.s_addr) if (context->netmask.s_addr != 0 &&
is_same_net(local, context->start, context->netmask) &&
is_same_net(local, context->end, context->netmask))
{ {
if (is_same_net(local, context->start, context->netmask) && /* link it onto the current chain if we've not seen it before */
is_same_net(local, context->end, context->netmask)) if (if_index == param->ind && context->current == context)
{ {
/* link it onto the current chain if we've not seen it before */ context->router = local;
if (if_index == param->ind && context->current == context) context->local = local;
{ context->current = param->current;
context->router = local; param->current = context;
context->local = local; }
context->current = param->current;
param->current = context; if (!(context->flags & CONTEXT_BRDCAST))
}
if (!(context->flags & CONTEXT_BRDCAST))
{
if (is_same_net(broadcast, context->start, context->netmask))
context->broadcast = broadcast;
else
context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr;
}
}
else if (param->relay.s_addr && is_same_net(param->relay, context->start, context->netmask))
{ {
context->router = param->relay; if (is_same_net(broadcast, context->start, context->netmask))
context->local = param->primary; context->broadcast = broadcast;
/* fill in missing broadcast addresses for relayed ranges */ else
if (!(context->flags & CONTEXT_BRDCAST))
context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr; context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr;
} }
}
}
} }
return 1; return 1;
@@ -505,7 +516,7 @@ struct dhcp_context *address_available(struct dhcp_context *context,
start = ntohl(tmp->start.s_addr); start = ntohl(tmp->start.s_addr);
end = ntohl(tmp->end.s_addr); end = ntohl(tmp->end.s_addr);
if (!(tmp->flags & CONTEXT_STATIC) && if (!(tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY)) &&
addr >= start && addr >= start &&
addr <= end && addr <= end &&
match_netid(tmp->filter, netids, 1)) match_netid(tmp->filter, netids, 1))
@@ -540,7 +551,8 @@ struct dhcp_context *narrow_context(struct dhcp_context *context,
if (!tmp) if (!tmp)
for (tmp = context; tmp; tmp = tmp->current) for (tmp = context; tmp; tmp = tmp->current)
if (match_netid(tmp->filter, netids, 1) && if (match_netid(tmp->filter, netids, 1) &&
is_same_net(taddr, tmp->start, tmp->netmask)) is_same_net(taddr, tmp->start, tmp->netmask) &&
!(tmp->flags & CONTEXT_PROXY))
break; break;
} }
@@ -562,50 +574,6 @@ struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct i
return NULL; return NULL;
} }
/* Is every member of check matched by a member of pool?
If tagnotneeded, untagged is OK */
int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool, int tagnotneeded)
{
struct dhcp_netid *tmp1;
if (!check && !tagnotneeded)
return 0;
for (; check; check = check->next)
{
/* '#' for not is for backwards compat. */
if (check->net[0] != '!' && check->net[0] != '#')
{
for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
if (strcmp(check->net, tmp1->net) == 0)
break;
if (!tmp1)
return 0;
}
else
for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
if (strcmp((check->net)+1, tmp1->net) == 0)
return 0;
}
return 1;
}
struct dhcp_netid *run_tag_if(struct dhcp_netid *tags)
{
struct tag_if *exprs;
struct dhcp_netid_list *list;
for (exprs = daemon->tag_if; exprs; exprs = exprs->next)
if (match_netid(exprs->tag, tags, 1))
for (list = exprs->set; list; list = list->next)
{
list->list->next = tags;
tags = list->list;
}
return tags;
}
int address_allocate(struct dhcp_context *context, int address_allocate(struct dhcp_context *context,
struct in_addr *addrp, unsigned char *hwaddr, int hw_len, struct in_addr *addrp, unsigned char *hwaddr, int hw_len,
struct dhcp_netid *netids, time_t now) struct dhcp_netid *netids, time_t now)
@@ -626,16 +594,22 @@ int address_allocate(struct dhcp_context *context,
for (pass = 0; pass <= 1; pass++) for (pass = 0; pass <= 1; pass++)
for (c = context; c; c = c->current) for (c = context; c; c = c->current)
if (c->flags & CONTEXT_STATIC) if (c->flags & (CONTEXT_STATIC | CONTEXT_PROXY))
continue; continue;
else if (!match_netid(c->filter, netids, pass)) else if (!match_netid(c->filter, netids, pass))
continue; continue;
else else
{ {
/* pick a seed based on hwaddr then iterate until we find a free address. */ if (option_bool(OPT_CONSEC_ADDR))
start.s_addr = addr.s_addr = /* seed is largest extant lease addr in this context */
htonl(ntohl(c->start.s_addr) + start = lease_find_max_addr(c);
((j + c->addr_epoch) % (1 + ntohl(c->end.s_addr) - ntohl(c->start.s_addr)))); else
/* pick a seed based on hwaddr */
start.s_addr = htonl(ntohl(c->start.s_addr) +
((j + c->addr_epoch) % (1 + ntohl(c->end.s_addr) - ntohl(c->start.s_addr))));
/* iterate until we find a free address. */
addr = start;
do { do {
/* eliminate addresses in use by the server. */ /* eliminate addresses in use by the server. */
@@ -660,9 +634,6 @@ int address_allocate(struct dhcp_context *context,
*addrp = addr; *addrp = addr;
if (option_bool(OPT_NO_PING))
return 1;
/* check if we failed to ping addr sometime in the last /* check if we failed to ping addr sometime in the last
PING_CACHE_TIME seconds. If so, assume the same situation still exists. PING_CACHE_TIME seconds. If so, assume the same situation still exists.
This avoids problems when a stupid client bangs This avoids problems when a stupid client bangs
@@ -672,33 +643,51 @@ int address_allocate(struct dhcp_context *context,
for (count = 0, r = daemon->ping_results; r; r = r->next) for (count = 0, r = daemon->ping_results; r; r = r->next)
if (difftime(now, r->time) > (float)PING_CACHE_TIME) if (difftime(now, r->time) > (float)PING_CACHE_TIME)
victim = r; /* old record */ victim = r; /* old record */
else if (++count == max || r->addr.s_addr == addr.s_addr) else
return 1; {
count++;
if (icmp_ping(addr)) if (r->addr.s_addr == addr.s_addr)
/* address in use: perturb address selection so that we are {
less likely to try this address again. */ /* consec-ip mode: we offered this address for another client
c->addr_epoch++; (different hash) recently, don't offer it to this one. */
else if (option_bool(OPT_CONSEC_ADDR) && r->hash != j)
break;
return 1;
}
}
if (!r)
{ {
/* at this point victim may hold an expired record */ if ((count < max) && !option_bool(OPT_NO_PING) && icmp_ping(addr))
if (!victim)
{ {
if ((victim = whine_malloc(sizeof(struct ping_result)))) /* address in use: perturb address selection so that we are
less likely to try this address again. */
if (!option_bool(OPT_CONSEC_ADDR))
c->addr_epoch++;
}
else
{
/* at this point victim may hold an expired record */
if (!victim)
{ {
victim->next = daemon->ping_results; if ((victim = whine_malloc(sizeof(struct ping_result))))
daemon->ping_results = victim; {
victim->next = daemon->ping_results;
daemon->ping_results = victim;
}
} }
/* record that this address is OK for 30s
without more ping checks */
if (victim)
{
victim->addr = addr;
victim->time = now;
victim->hash = j;
}
return 1;
} }
/* record that this address is OK for 30s
without more ping checks */
if (victim)
{
victim->addr = addr;
victim->time = now;
}
return 1;
} }
} }
@@ -709,6 +698,7 @@ int address_allocate(struct dhcp_context *context,
} while (addr.s_addr != start.s_addr); } while (addr.s_addr != start.s_addr);
} }
return 0; return 0;
} }
@@ -779,7 +769,7 @@ struct dhcp_config *find_config(struct dhcp_config *configs,
is_addr_in_context(context, config)) is_addr_in_context(context, config))
return config; return config;
/* use match with fewest wildcast octets */ /* use match with fewest wildcard octets */
for (candidate = NULL, count = 0, config = configs; config; config = config->next) for (candidate = NULL, count = 0, config = configs; config; config = config->next)
if (is_addr_in_context(context, config)) if (is_addr_in_context(context, config))
for (conf_addr = config->hwaddr; conf_addr; conf_addr = conf_addr->next) for (conf_addr = config->hwaddr; conf_addr; conf_addr = conf_addr->next)
@@ -956,85 +946,6 @@ void dhcp_read_ethers(void)
my_syslog(MS_DHCP | LOG_INFO, _("read %s - %d addresses"), ETHERSFILE, count); my_syslog(MS_DHCP | LOG_INFO, _("read %s - %d addresses"), ETHERSFILE, count);
} }
void check_dhcp_hosts(int fatal)
{
/* If the same IP appears in more than one host config, then DISCOVER
for one of the hosts will get the address, but REQUEST will be NAKed,
since the address is reserved by the other one -> protocol loop.
Also check that FQDNs match the domain we are using. */
struct dhcp_config *configs, *cp;
for (configs = daemon->dhcp_conf; configs; configs = configs->next)
{
char *domain;
if ((configs->flags & DHOPT_BANK) || fatal)
{
for (cp = configs->next; cp; cp = cp->next)
if ((configs->flags & cp->flags & CONFIG_ADDR) && configs->addr.s_addr == cp->addr.s_addr)
{
if (fatal)
die(_("duplicate IP address %s in dhcp-config directive."),
inet_ntoa(cp->addr), EC_BADCONF);
else
my_syslog(MS_DHCP | LOG_ERR, _("duplicate IP address %s in %s."),
inet_ntoa(cp->addr), daemon->dhcp_hosts_file);
configs->flags &= ~CONFIG_ADDR;
}
/* split off domain part */
if ((configs->flags & CONFIG_NAME) && (domain = strip_hostname(configs->hostname)))
configs->domain = domain;
}
}
}
void dhcp_update_configs(struct dhcp_config *configs)
{
/* Some people like to keep all static IP addresses in /etc/hosts.
This goes through /etc/hosts and sets static addresses for any DHCP config
records which don't have an address and whose name matches.
We take care to maintain the invariant that any IP address can appear
in at most one dhcp-host. Since /etc/hosts can be re-read by SIGHUP,
restore the status-quo ante first. */
struct dhcp_config *config;
struct crec *crec;
for (config = configs; config; config = config->next)
if (config->flags & CONFIG_ADDR_HOSTS)
config->flags &= ~(CONFIG_ADDR | CONFIG_ADDR_HOSTS);
if (daemon->port != 0)
for (config = configs; config; config = config->next)
if (!(config->flags & CONFIG_ADDR) &&
(config->flags & CONFIG_NAME) &&
(crec = cache_find_by_name(NULL, config->hostname, 0, F_IPV4)) &&
(crec->flags & F_HOSTS))
{
if (cache_find_by_name(crec, config->hostname, 0, F_IPV4))
{
/* use primary (first) address */
while (crec && !(crec->flags & F_REVERSE))
crec = cache_find_by_name(crec, config->hostname, 0, F_IPV4);
if (!crec)
continue; /* should be never */
my_syslog(MS_DHCP | LOG_WARNING, _("%s has more than one address in hostsfile, using %s for DHCP"),
config->hostname, inet_ntoa(crec->addr.addr.addr.addr4));
}
if (config_find_by_address(configs, crec->addr.addr.addr.addr4))
my_syslog(MS_DHCP | LOG_WARNING, _("duplicate IP address %s (%s) in dhcp-config directive"),
inet_ntoa(crec->addr.addr.addr.addr4), config->hostname);
else
{
config->addr = crec->addr.addr.addr.addr4;
config->flags |= CONFIG_ADDR | CONFIG_ADDR_HOSTS;
}
}
}
/* If we've not found a hostname any other way, try and see if there's one in /etc/hosts /* If we've not found a hostname any other way, try and see if there's one in /etc/hosts
for this address. If it has a domain part, that must match the set domain and for this address. If it has a domain part, that must match the set domain and
@@ -1075,20 +986,5 @@ char *host_from_dns(struct in_addr addr)
return NULL; return NULL;
} }
/* return domain or NULL if none. */
char *strip_hostname(char *hostname)
{
char *dot = strchr(hostname, '.');
if (!dot)
return NULL;
*dot = 0; /* truncate */
if (strlen(dot+1) != 0)
return dot+1;
return NULL;
}
#endif #endif
+518
View File
@@ -0,0 +1,518 @@
/* dnsmasq is Copyright (c) 2000-2012 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, or
(at your option) version 3 dated 29 June, 2007.
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.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dnsmasq.h"
#ifdef HAVE_DHCP6
struct iface_param {
struct dhcp_context *current;
int ind;
};
struct listen_param {
int fd_or_iface;
struct listen_param *next;
};
static int join_multicast(struct in6_addr *local, int prefix,
int scope, int if_index, int dad, void *vparam);
static int complete_context6(struct in6_addr *local, int prefix,
int scope, int if_index, int dad, void *vparam);
static int make_duid1(unsigned int type, char *mac, size_t maclen, void *parm);
void dhcp6_init(void)
{
int fd;
struct sockaddr_in6 saddr;
struct listen_param *listenp, listen;
#if defined(IP_TOS) && defined(IPTOS_CLASS_CS6)
int class = IPTOS_CLASS_CS6;
#endif
if ((fd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP)) == -1 ||
#if defined(IP_TOS) && defined(IPTOS_CLASS_CS6)
setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &class, sizeof(class)) == -1 ||
#endif
!fix_fd(fd) ||
!set_ipv6pktinfo(fd))
die (_("cannot create DHCPv6 socket: %s"), NULL, EC_BADNET);
memset(&saddr, 0, sizeof(saddr));
#ifdef HAVE_SOCKADDR_SA_LEN
saddr.sin6_len = sizeof(struct sockaddr_in6);
#endif
saddr.sin6_family = AF_INET6;
saddr.sin6_addr = in6addr_any;
saddr.sin6_port = htons(DHCPV6_SERVER_PORT);
if (bind(fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in6)))
die(_("failed to bind DHCPv6 server socket: %s"), NULL, EC_BADNET);
/* join multicast groups on each interface we're interested in */
listen.fd_or_iface = fd;
listen.next = NULL;
if (!iface_enumerate(AF_INET6, &listen, join_multicast))
die(_("failed to join DHCPv6 multicast group: %s"), NULL, EC_BADNET);
for (listenp = listen.next; listenp; )
{
struct listen_param *tmp = listenp->next;
free(listenp);
listenp = tmp;
}
daemon->dhcp6fd = fd;
}
static int join_multicast(struct in6_addr *local, int prefix,
int scope, int if_index, int dad, void *vparam)
{
char ifrn_name[IFNAMSIZ];
struct ipv6_mreq mreq;
struct listen_param *listenp, *param = vparam;
int fd = param->fd_or_iface;
struct dhcp_context *context;
struct iname *tmp;
(void)prefix;
(void)scope;
(void)dad;
/* record which interfaces we join on, so
that we do it at most one per interface, even when they
have multiple addresses */
for (listenp = param->next; listenp; listenp = listenp->next)
if (if_index == listenp->fd_or_iface)
return 1;
if (!indextoname(fd, if_index, ifrn_name))
return 0;
/* Are we doing DHCP on this interface? */
if (!iface_check(AF_INET6, (struct all_addr *)local, ifrn_name))
return 1;
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, ifrn_name) == 0))
return 1;
/* weird libvirt-inspired access control */
for (context = daemon->dhcp6; context; context = context->next)
if (!context->interface || strcmp(context->interface, ifrn_name) == 0)
break;
if (!context)
return 1;
mreq.ipv6mr_interface = if_index;
inet_pton(AF_INET6, ALL_RELAY_AGENTS_AND_SERVERS, &mreq.ipv6mr_multiaddr);
if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1)
return 0;
inet_pton(AF_INET6, ALL_SERVERS, &mreq.ipv6mr_multiaddr);
if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1)
return 0;
listenp = whine_malloc(sizeof(struct listen_param));
listenp->fd_or_iface = if_index;
listenp->next = param->next;
param->next = listenp;
return 1;
}
void dhcp6_packet(time_t now)
{
struct dhcp_context *context;
struct iface_param parm;
struct cmsghdr *cmptr;
struct msghdr msg;
int if_index = 0;
union {
struct cmsghdr align; /* this ensures alignment */
char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
} control_u;
union mysockaddr from;
struct all_addr dest;
ssize_t sz;
struct ifreq ifr;
struct iname *tmp;
msg.msg_control = control_u.control6;
msg.msg_controllen = sizeof(control_u);
msg.msg_flags = 0;
msg.msg_name = &from;
msg.msg_namelen = sizeof(from);
msg.msg_iov = &daemon->dhcp_packet;
msg.msg_iovlen = 1;
if ((sz = recv_dhcp_packet(daemon->dhcp6fd, &msg)) == -1 || sz <= 4)
return;
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
{
union {
unsigned char *c;
struct in6_pktinfo *p;
} p;
p.c = CMSG_DATA(cmptr);
if_index = p.p->ipi6_ifindex;
dest.addr.addr6 = p.p->ipi6_addr;
}
if (!indextoname(daemon->dhcp6fd, if_index, ifr.ifr_name))
return;
if (!iface_check(AF_INET6, (struct all_addr *)&dest, ifr.ifr_name))
return;
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
return;
/* weird libvirt-inspired access control */
for (context = daemon->dhcp6; context; context = context->next)
if (!context->interface || strcmp(context->interface, ifr.ifr_name) == 0)
break;
if (!context)
return;
/* unlinked contexts are marked by context->current == context */
for (context = daemon->dhcp6; context; context = context->next)
context->current = context;
parm.current = NULL;
parm.ind = if_index;
if (!iface_enumerate(AF_INET6, &parm, complete_context6))
return;
lease_prune(NULL, now); /* lose any expired leases */
msg.msg_iov = &daemon->dhcp_packet;
sz = dhcp6_reply(parm.current, if_index, ifr.ifr_name, sz, IN6_IS_ADDR_MULTICAST(&from.in6.sin6_addr), now);
lease_update_file(now);
lease_update_dns();
if (sz != 0)
while (sendto(daemon->dhcp6fd, daemon->outpacket.iov_base, sz, 0, (struct sockaddr *)&from, sizeof(from)) &&
retry_send());
}
static int complete_context6(struct in6_addr *local, int prefix,
int scope, int if_index, int dad, void *vparam)
{
struct dhcp_context *context;
struct iface_param *param = vparam;
(void)scope; /* warning */
(void)dad;
for (context = daemon->dhcp6; context; context = context->next)
{
if (prefix == context->prefix &&
!IN6_IS_ADDR_LOOPBACK(local) &&
!IN6_IS_ADDR_LINKLOCAL(local) &&
!IN6_IS_ADDR_MULTICAST(local) &&
is_same_net6(local, &context->start6, prefix) &&
is_same_net6(local, &context->end6, prefix))
{
/* link it onto the current chain if we've not seen it before */
if (if_index == param->ind && context->current == context)
{
context->current = param->current;
param->current = context;
context->local6 = *local;
}
}
}
return 1;
}
struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct in6_addr *net, int prefix, u64 addr)
{
struct dhcp_config *config;
for (config = configs; config; config = config->next)
if ((config->flags & CONFIG_ADDR6) &&
is_same_net6(&config->addr6, net, prefix) &&
(prefix == 128 || addr6part(&config->addr6) == addr))
return config;
return NULL;
}
int address6_allocate(struct dhcp_context *context, unsigned char *clid, int clid_len,
int serial, struct dhcp_netid *netids, struct in6_addr *ans)
{
/* Find a free address: exclude anything in use and anything allocated to
a particular hwaddr/clientid/hostname in our configuration.
Try to return from contexts which match netids first.
Note that we assume the address prefix lengths are 64 or greater, so we can
get by with 64 bit arithmetic.
*/
u64 start, addr;
struct dhcp_context *c, *d;
int i, pass;
u64 j;
/* hash hwaddr: use the SDBM hashing algorithm. This works
for MAC addresses, let's see how it manages with client-ids! */
for (j = 0, i = 0; i < clid_len; i++)
j += clid[i] + (j << 6) + (j << 16) - j;
for (pass = 0; pass <= 1; pass++)
for (c = context; c; c = c->current)
if (c->flags & (CONTEXT_STATIC | CONTEXT_PROXY))
continue;
else if (!match_netid(c->filter, netids, pass))
continue;
else
{
if (option_bool(OPT_CONSEC_ADDR))
/* seed is largest extant lease addr in this context */
start = lease_find_max_addr6(c) + serial;
else
start = addr6part(&c->start6) + ((j + c->addr_epoch + serial) % (1 + addr6part(&c->end6) - addr6part(&c->start6)));
/* iterate until we find a free address. */
addr = start;
do {
/* eliminate addresses in use by the server. */
for (d = context; d; d = d->current)
if (addr == addr6part(&d->router6))
break;
if (!d &&
!lease6_find_by_addr(&c->start6, c->prefix, addr) &&
!config_find_by_address6(daemon->dhcp_conf, &c->start6, c->prefix, addr))
{
*ans = c->start6;
setaddr6part (ans, addr);
return 1;
}
addr++;
if (addr == addr6part(&c->end6) + 1)
addr = addr6part(&c->start6);
} while (addr != start);
}
return 0;
}
struct dhcp_context *address6_available(struct dhcp_context *context,
struct in6_addr *taddr,
struct dhcp_netid *netids)
{
u64 start, end, addr = addr6part(taddr);
struct dhcp_context *tmp;
for (tmp = context; tmp; tmp = tmp->current)
{
start = addr6part(&tmp->start6);
end = addr6part(&tmp->end6);
if (!(tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY)) &&
is_same_net6(&context->start6, taddr, context->prefix) &&
is_same_net6(&context->end6, taddr, context->prefix) &&
addr >= start &&
addr <= end &&
match_netid(tmp->filter, netids, 1))
return tmp;
}
return NULL;
}
struct dhcp_context *narrow_context6(struct dhcp_context *context,
struct in6_addr *taddr,
struct dhcp_netid *netids)
{
/* We start of with a set of possible contexts, all on the current physical interface.
These are chained on ->current.
Here we have an address, and return the actual context correponding to that
address. Note that none may fit, if the address came a dhcp-host and is outside
any dhcp-range. In that case we return a static range if possible, or failing that,
any context on the correct subnet. (If there's more than one, this is a dodgy
configuration: maybe there should be a warning.) */
struct dhcp_context *tmp;
if (!(tmp = address6_available(context, taddr, netids)))
{
for (tmp = context; tmp; tmp = tmp->current)
if (match_netid(tmp->filter, netids, 1) &&
is_same_net6(taddr, &tmp->start6, tmp->prefix) &&
(tmp->flags & CONTEXT_STATIC))
break;
if (!tmp)
for (tmp = context; tmp; tmp = tmp->current)
if (match_netid(tmp->filter, netids, 1) &&
is_same_net6(taddr, &tmp->start6, tmp->prefix) &&
!(tmp->flags & CONTEXT_PROXY))
break;
}
/* Only one context allowed now */
if (tmp)
tmp->current = NULL;
return tmp;
}
static int is_addr_in_context6(struct dhcp_context *context, struct dhcp_config *config)
{
if (!context) /* called via find_config() from lease_update_from_configs() */
return 1;
if (!(config->flags & CONFIG_ADDR6))
return 1;
for (; context; context = context->current)
if (is_same_net6(&config->addr6, &context->start6, context->prefix))
return 1;
return 0;
}
struct dhcp_config *find_config6(struct dhcp_config *configs,
struct dhcp_context *context,
unsigned char *duid, int duid_len,
char *hostname)
{
int count, new;
struct dhcp_config *config;
struct hwaddr_config *conf_addr;
unsigned char *hwaddr = NULL;
int duid_type, hw_len = 0, hw_type = 0;
if (duid)
{
for (config = configs; config; config = config->next)
if (config->flags & CONFIG_CLID)
{
if (config->clid_len == duid_len &&
memcmp(config->clid, duid, duid_len) == 0 &&
is_addr_in_context6(context, config))
return config;
}
/* DHCPv6 doesn't deal in MAC addresses per-se, but some DUIDs do include
MAC addresses, so we try and parse them out here. Not that there is only one
DUID per host and it's created using any one of the MACs, so this is no
good no good for multihomed hosts. */
hwaddr = duid;
GETSHORT(duid_type, hwaddr);
if (duid_type == 1 || duid_type == 3)
{
GETSHORT(hw_type, hwaddr);
if (duid_type == 1)
hwaddr += 4; /* skip time */
hw_len = duid_len - 8;
}
if (hwaddr)
for (config = configs; config; config = config->next)
if (config_has_mac(config, hwaddr, hw_len, hw_type) &&
is_addr_in_context6(context, config))
return config;
}
if (hostname && context)
for (config = configs; config; config = config->next)
if ((config->flags & CONFIG_NAME) &&
hostname_isequal(config->hostname, hostname) &&
is_addr_in_context6(context, config))
return config;
/* use match with fewest wildcard octets */
if (hwaddr)
{
struct dhcp_config *candidate;
for (candidate = NULL, count = 0, config = configs; config; config = config->next)
if (is_addr_in_context6(context, config))
for (conf_addr = config->hwaddr; conf_addr; conf_addr = conf_addr->next)
if (conf_addr->wildcard_mask != 0 &&
conf_addr->hwaddr_len == hw_len &&
(conf_addr->hwaddr_type == hw_type || conf_addr->hwaddr_type == 0) &&
(new = memcmp_masked(conf_addr->hwaddr, hwaddr, hw_len, conf_addr->wildcard_mask)) > count)
{
count = new;
candidate = config;
}
return candidate;
}
return NULL;
}
void make_duid(time_t now)
{
/* rebase epoch to 1/1/2000 */
time_t newnow = now - 946684800;
iface_enumerate(AF_LOCAL, &newnow, make_duid1);
if(!daemon->duid)
die("Cannot create DHCPv6 server DUID: %s", NULL, EC_MISC);
}
static int make_duid1(unsigned int type, char *mac, size_t maclen, void *parm)
{
/* create DUID as specified in RFC3315. We use the MAC of the
first interface we find that isn't loopback or P-to-P */
unsigned char *p;
daemon->duid = p = safe_malloc(maclen + 8);
daemon->duid_len = maclen + 8;
#ifdef HAVE_BROKEN_RTC
PUTSHORT(3, p); /* DUID_LL */
#else
PUTSHORT(1, p); /* DUID_LLT */
#endif
PUTSHORT(type, p); /* address type */
#ifndef HAVE_BROKEN_RTC
PUTLONG(*((time_t *)parm), p); /* time */
#endif
memcpy(p, mac, maclen);
return 0;
}
#endif
+67
View File
@@ -0,0 +1,67 @@
/* dnsmasq is Copyright (c) 2000-2012 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, or
(at your option) version 3 dated 29 June, 2007.
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.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define DHCPV6_SERVER_PORT 547
#define DHCPV6_CLIENT_PORT 546
#define ALL_SERVERS "FF05::1:3"
#define ALL_RELAY_AGENTS_AND_SERVERS "FF02::1:2"
#define DHCP6SOLICIT 1
#define DHCP6ADVERTISE 2
#define DHCP6REQUEST 3
#define DHCP6CONFIRM 4
#define DHCP6RENEW 5
#define DHCP6REBIND 6
#define DHCP6REPLY 7
#define DHCP6RELEASE 8
#define DHCP6DECLINE 9
#define DHCP6RECONFIGURE 10
#define DHCP6IREQ 11
#define DHCP6RELAYFORW 12
#define DHCP6RELAYREPL 13
#define OPTION6_CLIENT_ID 1
#define OPTION6_SERVER_ID 2
#define OPTION6_IA_NA 3
#define OPTION6_IA_TA 4
#define OPTION6_IAADDR 5
#define OPTION6_ORO 6
#define OPTION6_PREFERENCE 7
#define OPTION6_ELAPSED_TIME 8
#define OPTION6_RELAY_MSG 9
#define OPTION6_AUTH 11
#define OPTION6_UNICAST 12
#define OPTION6_STATUS_CODE 13
#define OPTION6_RAPID_COMMIT 14
#define OPTION6_USER_CLASS 15
#define OPTION6_VENDOR_CLASS 16
#define OPTION6_VENDOR_OPTS 17
#define OPTION6_INTERFACE_ID 18
#define OPTION6_RECONFIGURE_MSG 19
#define OPTION6_RECONF_ACCEPT 20
#define OPTION6_DNS_SERVER 23
#define OPTION6_REMOTE_ID 37
#define OPTION6_SUBSCRIBER_ID 38
#define OPTION6_FQDN 39
#define DHCP6SUCCESS 0
#define DHCP6UNSPEC 1
#define DHCP6NOADDRS 2
#define DHCP6NOBINDING 3
#define DHCP6NOTONLINK 4
#define DHCP6USEMULTI 5
+6 -1
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2011 Simon Kelley /* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -13,6 +13,11 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#define DHCP_SERVER_PORT 67
#define DHCP_CLIENT_PORT 68
#define DHCP_SERVER_ALTPORT 1067
#define DHCP_CLIENT_ALTPORT 1068
#define PXE_PORT 4011
#define BOOTREQUEST 1 #define BOOTREQUEST 1
#define BOOTREPLY 2 #define BOOTREPLY 2
+5 -2
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2011 Simon Kelley /* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -14,6 +14,9 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#define NAMESERVER_PORT 53
#define TFTP_PORT 69
#define IN6ADDRSZ 16 #define IN6ADDRSZ 16
#define INADDRSZ 4 #define INADDRSZ 4
@@ -56,7 +59,7 @@ struct dns_header {
u16 id; u16 id;
u8 hb3,hb4; u8 hb3,hb4;
u16 qdcount,ancount,nscount,arcount; u16 qdcount,ancount,nscount,arcount;
} ; };
#define HB3_QR 0x80 #define HB3_QR 0x80
#define HB3_OPCODE 0x78 #define HB3_OPCODE 0x78
+195 -105
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2011 Simon Kelley /* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -14,50 +14,13 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
/* Declare static char *compiler_opts in config.h */
#define DNSMASQ_COMPILE_OPTS
#include "dnsmasq.h" #include "dnsmasq.h"
struct daemon *daemon; struct daemon *daemon;
static char *compile_opts =
#ifndef HAVE_IPV6
"no-"
#endif
"IPv6 "
#ifndef HAVE_GETOPT_LONG
"no-"
#endif
"GNU-getopt "
#ifdef HAVE_BROKEN_RTC
"no-RTC "
#endif
#ifdef NO_FORK
"no-MMU "
#endif
#ifndef HAVE_DBUS
"no-"
#endif
"DBus "
#ifndef LOCALEDIR
"no-"
#endif
"I18N "
#ifndef HAVE_DHCP
"no-"
#endif
"DHCP "
#if defined(HAVE_DHCP) && !defined(HAVE_SCRIPT)
"no-scripts "
#endif
#ifndef HAVE_TFTP
"no-"
#endif
"TFTP "
#if !defined(LOCALEDIR) && !defined(HAVE_IDN)
"no-"
#endif
"IDN";
static volatile pid_t pid = 0; static volatile pid_t pid = 0;
static volatile int pipewrite; static volatile int pipewrite;
@@ -65,7 +28,8 @@ static int set_dns_listeners(time_t now, fd_set *set, int *maxfdp);
static void check_dns_listeners(fd_set *set, time_t now); static void check_dns_listeners(fd_set *set, time_t now);
static void sig_handler(int sig); static void sig_handler(int sig);
static void async_event(int pipe, time_t now); static void async_event(int pipe, time_t now);
static void fatal_event(struct event_desc *ev); static void fatal_event(struct event_desc *ev, char *msg);
static int read_event(int fd, struct event_desc *evp, char **msg);
int main (int argc, char **argv) int main (int argc, char **argv)
{ {
@@ -75,7 +39,7 @@ int main (int argc, char **argv)
struct iname *if_tmp; struct iname *if_tmp;
int piperead, pipefd[2], err_pipe[2]; int piperead, pipefd[2], err_pipe[2];
struct passwd *ent_pw = NULL; struct passwd *ent_pw = NULL;
#if defined(HAVE_DHCP) && defined(HAVE_SCRIPT) #if defined(HAVE_SCRIPT)
uid_t script_uid = 0; uid_t script_uid = 0;
gid_t script_gid = 0; gid_t script_gid = 0;
#endif #endif
@@ -118,18 +82,29 @@ int main (int argc, char **argv)
daemon->edns_pktsz : DNSMASQ_PACKETSZ; daemon->edns_pktsz : DNSMASQ_PACKETSZ;
daemon->packet = safe_malloc(daemon->packet_buff_sz); daemon->packet = safe_malloc(daemon->packet_buff_sz);
daemon->addrbuff = safe_malloc(ADDRSTRLEN);
#ifdef HAVE_DHCP #ifdef HAVE_DHCP
if (!daemon->lease_file) if (!daemon->lease_file)
{ {
if (daemon->dhcp) if (daemon->dhcp || daemon->dhcp6)
daemon->lease_file = LEASEFILE; daemon->lease_file = LEASEFILE;
} }
#endif #endif
/* Close any file descriptors we inherited apart from std{in|out|err} */ /* Close any file descriptors we inherited apart from std{in|out|err}
Ensure that at least stdin, stdout and stderr (fd 0, 1, 2) exist,
otherwise file descriptors we create can end up being 0, 1, or 2
and then get accidentally closed later when we make 0, 1, and 2
open to /dev/null. Normally we'll be started with 0, 1 and 2 open,
but it's not guaranteed. By opening /dev/null three times, we
ensure that we're not using those fds for real stuff. */
for (i = 0; i < max_fd; i++) for (i = 0; i < max_fd; i++)
if (i != STDOUT_FILENO && i != STDERR_FILENO && i != STDIN_FILENO) if (i != STDOUT_FILENO && i != STDERR_FILENO && i != STDIN_FILENO)
close(i); close(i);
else
open("/dev/null", O_RDWR);
#ifdef HAVE_LINUX_NETWORK #ifdef HAVE_LINUX_NETWORK
netlink_init(); netlink_init();
@@ -148,6 +123,14 @@ int main (int argc, char **argv)
die(_("TFTP server not available: set HAVE_TFTP in src/config.h"), NULL, EC_BADCONF); die(_("TFTP server not available: set HAVE_TFTP in src/config.h"), NULL, EC_BADCONF);
#endif #endif
#ifdef HAVE_CONNTRACK
if (option_bool(OPT_CONNTRACK) && (daemon->query_port != 0 || daemon->osport))
die (_("Cannot use --conntrack AND --query-port"), NULL, EC_BADCONF);
#else
if (option_bool(OPT_CONNTRACK))
die(_("Conntrack support not available: set HAVE_CONNTRACK in src/config.h"), NULL, EC_BADCONF);
#endif
#ifdef HAVE_SOLARIS_NETWORK #ifdef HAVE_SOLARIS_NETWORK
if (daemon->max_logs != 0) if (daemon->max_logs != 0)
die(_("asychronous logging is not available under Solaris"), NULL, EC_BADCONF); die(_("asychronous logging is not available under Solaris"), NULL, EC_BADCONF);
@@ -163,13 +146,20 @@ int main (int argc, char **argv)
now = dnsmasq_time(); now = dnsmasq_time();
#ifdef HAVE_DHCP #ifdef HAVE_DHCP
if (daemon->dhcp) if (daemon->dhcp || daemon->dhcp6)
{ {
/* Note that order matters here, we must call lease_init before /* Note that order matters here, we must call lease_init before
creating any file descriptors which shouldn't be leaked creating any file descriptors which shouldn't be leaked
to the lease-script init process. */ to the lease-script init process. We need to call common_init
before lease_init to allocate buffers it uses.*/
dhcp_common_init();
lease_init(now); lease_init(now);
dhcp_init(); if (daemon->dhcp)
dhcp_init();
#ifdef HAVE_DHCP6
if (daemon->dhcp6)
dhcp6_init();
#endif
} }
#endif #endif
@@ -178,7 +168,7 @@ int main (int argc, char **argv)
if (option_bool(OPT_NOWILD)) if (option_bool(OPT_NOWILD))
{ {
daemon->listeners = create_bound_listeners(); create_bound_listeners(1);
for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next) for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next)
if (if_tmp->name && !if_tmp->used) if (if_tmp->name && !if_tmp->used)
@@ -192,7 +182,7 @@ int main (int argc, char **argv)
} }
} }
else else
daemon->listeners = create_wildcard_listeners(); create_wildcard_listeners();
if (daemon->port != 0) if (daemon->port != 0)
cache_init(); cache_init();
@@ -213,9 +203,10 @@ int main (int argc, char **argv)
if (daemon->port != 0) if (daemon->port != 0)
pre_allocate_sfds(); pre_allocate_sfds();
#if defined(HAVE_DHCP) && defined(HAVE_SCRIPT) #if defined(HAVE_SCRIPT)
/* Note getpwnam returns static storage */ /* Note getpwnam returns static storage */
if (daemon->dhcp && daemon->lease_change_command && daemon->scriptuser) if ((daemon->dhcp || daemon->dhcp6) && daemon->scriptuser &&
(daemon->lease_change_command || daemon->luascript))
{ {
if ((ent_pw = getpwnam(daemon->scriptuser))) if ((ent_pw = getpwnam(daemon->scriptuser)))
{ {
@@ -278,7 +269,7 @@ int main (int argc, char **argv)
piperead = pipefd[0]; piperead = pipefd[0];
pipewrite = pipefd[1]; pipewrite = pipefd[1];
/* prime the pipe to load stuff first time. */ /* prime the pipe to load stuff first time. */
send_event(pipewrite, EVENT_RELOAD, 0); send_event(pipewrite, EVENT_RELOAD, 0, NULL);
err_pipe[1] = -1; err_pipe[1] = -1;
@@ -301,18 +292,19 @@ int main (int argc, char **argv)
if ((pid = fork()) == -1) if ((pid = fork()) == -1)
/* fd == -1 since we've not forked, never returns. */ /* fd == -1 since we've not forked, never returns. */
send_event(-1, EVENT_FORK_ERR, errno); send_event(-1, EVENT_FORK_ERR, errno, NULL);
if (pid != 0) if (pid != 0)
{ {
struct event_desc ev; struct event_desc ev;
char *msg;
/* close our copy of write-end */ /* close our copy of write-end */
close(err_pipe[1]); close(err_pipe[1]);
/* check for errors after the fork */ /* check for errors after the fork */
if (read_write(err_pipe[0], (unsigned char *)&ev, sizeof(ev), 1)) if (read_event(err_pipe[0], &ev, &msg))
fatal_event(&ev); fatal_event(&ev, msg);
_exit(EC_GOOD); _exit(EC_GOOD);
} }
@@ -324,7 +316,7 @@ int main (int argc, char **argv)
setsid(); setsid();
if ((pid = fork()) == -1) if ((pid = fork()) == -1)
send_event(err_pipe[1], EVENT_FORK_ERR, errno); send_event(err_pipe[1], EVENT_FORK_ERR, errno, NULL);
if (pid != 0) if (pid != 0)
_exit(0); _exit(0);
@@ -344,7 +336,7 @@ int main (int argc, char **argv)
} }
else if (getuid() == 0) else if (getuid() == 0)
{ {
send_event(err_pipe[1], EVENT_PIDFILE, errno); send_event(err_pipe[1], EVENT_PIDFILE, errno, daemon->runfile);
_exit(0); _exit(0);
} }
} }
@@ -364,8 +356,8 @@ int main (int argc, char **argv)
/* if we are to run scripts, we need to fork a helper before dropping root. */ /* if we are to run scripts, we need to fork a helper before dropping root. */
daemon->helperfd = -1; daemon->helperfd = -1;
#if defined(HAVE_DHCP) && defined(HAVE_SCRIPT) #ifdef HAVE_SCRIPT
if (daemon->dhcp && daemon->lease_change_command) if ((daemon->dhcp || daemon->dhcp6) && (daemon->lease_change_command || daemon->luascript))
daemon->helperfd = create_helper(pipewrite, err_pipe[1], script_uid, script_gid, max_fd); daemon->helperfd = create_helper(pipewrite, err_pipe[1], script_uid, script_gid, max_fd);
#endif #endif
@@ -379,17 +371,23 @@ int main (int argc, char **argv)
(setgroups(0, &dummy) == -1 || (setgroups(0, &dummy) == -1 ||
setgid(gp->gr_gid) == -1)) setgid(gp->gr_gid) == -1))
{ {
send_event(err_pipe[1], EVENT_GROUP_ERR, errno); send_event(err_pipe[1], EVENT_GROUP_ERR, errno, daemon->groupname);
_exit(0); _exit(0);
} }
if (ent_pw && ent_pw->pw_uid != 0) if (ent_pw && ent_pw->pw_uid != 0)
{ {
#if defined(HAVE_LINUX_NETWORK) #if defined(HAVE_LINUX_NETWORK)
/* On linux, we keep CAP_NETADMIN (for ARP-injection) and /* On linux, we keep CAP_NETADMIN (for ARP-injection) and
CAP_NET_RAW (for icmp) if we're doing dhcp */ CAP_NET_RAW (for icmp) if we're doing dhcp. If we have yet to bind
data->effective = data->permitted = data->inheritable = ports because of DAD, we need CAP_NET_BIND_SERVICE too. */
(1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) | (1 << CAP_SETUID); if (is_dad_listeners())
data->effective = data->permitted = data->inheritable =
(1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) |
(1 << CAP_SETUID) | (1 << CAP_NET_BIND_SERVICE);
else
data->effective = data->permitted = data->inheritable =
(1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) | (1 << CAP_SETUID);
/* Tell kernel to not clear capabilities when dropping root */ /* Tell kernel to not clear capabilities when dropping root */
if (capset(hdr, data) == -1 || prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1) if (capset(hdr, data) == -1 || prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1)
@@ -419,26 +417,30 @@ int main (int argc, char **argv)
if (bad_capabilities != 0) if (bad_capabilities != 0)
{ {
send_event(err_pipe[1], EVENT_CAP_ERR, bad_capabilities); send_event(err_pipe[1], EVENT_CAP_ERR, bad_capabilities, NULL);
_exit(0); _exit(0);
} }
/* finally drop root */ /* finally drop root */
if (setuid(ent_pw->pw_uid) == -1) if (setuid(ent_pw->pw_uid) == -1)
{ {
send_event(err_pipe[1], EVENT_USER_ERR, errno); send_event(err_pipe[1], EVENT_USER_ERR, errno, daemon->username);
_exit(0); _exit(0);
} }
#ifdef HAVE_LINUX_NETWORK #ifdef HAVE_LINUX_NETWORK
data->effective = data->permitted = if (is_dad_listeners())
(1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW); data->effective = data->permitted =
(1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) | (1 << CAP_NET_BIND_SERVICE);
else
data->effective = data->permitted =
(1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW);
data->inheritable = 0; data->inheritable = 0;
/* lose the setuid and setgid capbilities */ /* lose the setuid and setgid capbilities */
if (capset(hdr, data) == -1) if (capset(hdr, data) == -1)
{ {
send_event(err_pipe[1], EVENT_CAP_ERR, errno); send_event(err_pipe[1], EVENT_CAP_ERR, errno, NULL);
_exit(0); _exit(0);
} }
#endif #endif
@@ -495,25 +497,53 @@ int main (int argc, char **argv)
my_syslog(LOG_INFO, _("asynchronous logging enabled, queue limit is %d messages"), daemon->max_logs); my_syslog(LOG_INFO, _("asynchronous logging enabled, queue limit is %d messages"), daemon->max_logs);
#ifdef HAVE_DHCP #ifdef HAVE_DHCP
if (daemon->dhcp) if (daemon->dhcp || daemon->dhcp6)
{ {
struct dhcp_context *dhcp_tmp; struct dhcp_context *dhcp_tmp;
int family = AF_INET;
for (dhcp_tmp = daemon->dhcp; dhcp_tmp; dhcp_tmp = dhcp_tmp->next) dhcp_tmp = daemon->dhcp;
#ifdef HAVE_DHCP6
again:
#endif
for (; dhcp_tmp; dhcp_tmp = dhcp_tmp->next)
{ {
void *start = &dhcp_tmp->start;
void *end = &dhcp_tmp->end;
#ifdef HAVE_DHCP6
if (family == AF_INET6)
{
start = &dhcp_tmp->start6;
end = &dhcp_tmp->end6;
}
#endif
prettyprint_time(daemon->dhcp_buff2, dhcp_tmp->lease_time); prettyprint_time(daemon->dhcp_buff2, dhcp_tmp->lease_time);
strcpy(daemon->dhcp_buff, inet_ntoa(dhcp_tmp->start)); inet_ntop(family, start, daemon->dhcp_buff, 256);
inet_ntop(family, end, daemon->dhcp_buff3, 256);
my_syslog(MS_DHCP | LOG_INFO, my_syslog(MS_DHCP | LOG_INFO,
(dhcp_tmp->flags & CONTEXT_STATIC) ? (dhcp_tmp->flags & CONTEXT_STATIC) ?
_("DHCP, static leases only on %.0s%s, lease time %s") : _("DHCP, static leases only on %.0s%s, lease time %s") :
(dhcp_tmp->flags & CONTEXT_PROXY) ? (dhcp_tmp->flags & CONTEXT_PROXY) ?
_("DHCP, proxy on subnet %.0s%s%.0s") : _("DHCP, proxy on subnet %.0s%s%.0s") :
_("DHCP, IP range %s -- %s, lease time %s"), _("DHCP, IP range %s -- %s, lease time %s"),
daemon->dhcp_buff, inet_ntoa(dhcp_tmp->end), daemon->dhcp_buff2); daemon->dhcp_buff, daemon->dhcp_buff3, daemon->dhcp_buff2);
} }
#ifdef HAVE_DHCP6
if (family == AF_INET)
{
family = AF_INET6;
dhcp_tmp = daemon->dhcp6;
goto again;
}
#endif
} }
#endif #endif
#ifdef HAVE_TFTP #ifdef HAVE_TFTP
if (daemon->tftp_unlimited || daemon->tftp_interfaces) if (daemon->tftp_unlimited || daemon->tftp_interfaces)
{ {
@@ -593,6 +623,13 @@ int main (int argc, char **argv)
t.tv_usec = 250000; t.tv_usec = 250000;
tp = &t; tp = &t;
} }
/* Wake every second whilst waiting for DAD to complete */
else if (is_dad_listeners())
{
t.tv_sec = 1;
t.tv_usec = 0;
tp = &t;
}
#ifdef HAVE_DBUS #ifdef HAVE_DBUS
set_dbus_listeners(&maxfd, &rset, &wset, &eset); set_dbus_listeners(&maxfd, &rset, &wset, &eset);
@@ -611,6 +648,14 @@ int main (int argc, char **argv)
} }
#endif #endif
#ifdef HAVE_DHCP6
if (daemon->dhcp6)
{
FD_SET(daemon->dhcp6fd, &rset);
bump_maxfd(daemon->dhcp6fd, &maxfd);
}
#endif
#ifdef HAVE_LINUX_NETWORK #ifdef HAVE_LINUX_NETWORK
FD_SET(daemon->netlinkfd, &rset); FD_SET(daemon->netlinkfd, &rset);
bump_maxfd(daemon->netlinkfd, &maxfd); bump_maxfd(daemon->netlinkfd, &maxfd);
@@ -647,6 +692,15 @@ int main (int argc, char **argv)
now = dnsmasq_time(); now = dnsmasq_time();
check_log_writer(&wset); check_log_writer(&wset);
/* Check the interfaces to see if any have exited DAD state
and if so, bind the address. */
if (is_dad_listeners())
{
enumerate_interfaces();
/* NB, is_dad_listeners() == 1 --> we're binding interfaces */
create_bound_listeners(0);
}
#ifdef HAVE_LINUX_NETWORK #ifdef HAVE_LINUX_NETWORK
if (FD_ISSET(daemon->netlinkfd, &rset)) if (FD_ISSET(daemon->netlinkfd, &rset))
@@ -697,6 +751,14 @@ int main (int argc, char **argv)
dhcp_packet(now, 1); dhcp_packet(now, 1);
} }
#ifdef HAVE_DHCP6
if (daemon->dhcp6)
{
if (FD_ISSET(daemon->dhcp6fd, &rset))
dhcp6_packet(now);
}
#endif
# ifdef HAVE_SCRIPT # ifdef HAVE_SCRIPT
if (daemon->helperfd != -1 && FD_ISSET(daemon->helperfd, &wset)) if (daemon->helperfd != -1 && FD_ISSET(daemon->helperfd, &wset))
helper_write(); helper_write();
@@ -741,28 +803,57 @@ static void sig_handler(int sig)
else else
return; return;
send_event(pipewrite, event, 0); send_event(pipewrite, event, 0, NULL);
errno = errsave; errno = errsave;
} }
} }
void send_event(int fd, int event, int data) void send_event(int fd, int event, int data, char *msg)
{ {
struct event_desc ev; struct event_desc ev;
struct iovec iov[2];
ev.event = event; ev.event = event;
ev.data = data; ev.data = data;
ev.msg_sz = msg ? strlen(msg) : 0;
iov[0].iov_base = &ev;
iov[0].iov_len = sizeof(ev);
iov[1].iov_base = msg;
iov[1].iov_len = ev.msg_sz;
/* error pipe, debug mode. */ /* error pipe, debug mode. */
if (fd == -1) if (fd == -1)
fatal_event(&ev); fatal_event(&ev, msg);
else else
/* pipe is non-blocking and struct event_desc is smaller than /* pipe is non-blocking and struct event_desc is smaller than
PIPE_BUF, so this either fails or writes everything */ PIPE_BUF, so this either fails or writes everything */
while (write(fd, &ev, sizeof(ev)) == -1 && errno == EINTR); while (writev(fd, iov, msg ? 2 : 1) == -1 && errno == EINTR);
} }
static void fatal_event(struct event_desc *ev) /* NOTE: the memory used to return msg is leaked: use msgs in events only
to describe fatal errors. */
static int read_event(int fd, struct event_desc *evp, char **msg)
{
char *buf;
if (!read_write(fd, (unsigned char *)evp, sizeof(struct event_desc), 1))
return 0;
*msg = NULL;
if (evp->msg_sz != 0 &&
(buf = malloc(evp->msg_sz + 1)) &&
read_write(fd, (unsigned char *)buf, evp->msg_sz, 1))
{
buf[evp->msg_sz] = 0;
*msg = buf;
}
return 1;
}
static void fatal_event(struct event_desc *ev, char *msg)
{ {
errno = ev->data; errno = ev->data;
@@ -781,19 +872,19 @@ static void fatal_event(struct event_desc *ev)
die(_("setting capabilities failed: %s"), NULL, EC_MISC); die(_("setting capabilities failed: %s"), NULL, EC_MISC);
case EVENT_USER_ERR: case EVENT_USER_ERR:
case EVENT_HUSER_ERR: die(_("failed to change user-id to %s: %s"), msg, EC_MISC);
die(_("failed to change user-id to %s: %s"),
ev->event == EVENT_USER_ERR ? daemon->username : daemon->scriptuser,
EC_MISC);
case EVENT_GROUP_ERR: case EVENT_GROUP_ERR:
die(_("failed to change group-id to %s: %s"), daemon->groupname, EC_MISC); die(_("failed to change group-id to %s: %s"), msg, EC_MISC);
case EVENT_PIDFILE: case EVENT_PIDFILE:
die(_("failed to open pidfile %s: %s"), daemon->runfile, EC_FILE); die(_("failed to open pidfile %s: %s"), msg, EC_FILE);
case EVENT_LOG_ERR: case EVENT_LOG_ERR:
die(_("cannot open %s: %s"), daemon->log_file ? daemon->log_file : "log", EC_FILE); die(_("cannot open log %s: %s"), msg, EC_FILE);
case EVENT_LUA_ERR:
die(_("failed to load Lua script: %s"), msg, EC_MISC);
} }
} }
@@ -802,8 +893,12 @@ static void async_event(int pipe, time_t now)
pid_t p; pid_t p;
struct event_desc ev; struct event_desc ev;
int i; int i;
char *msg;
if (read_write(pipe, (unsigned char *)&ev, sizeof(ev), 1))
/* NOTE: the memory used to return msg is leaked: use msgs in events only
to describe fatal errors. */
if (read_event(pipe, &ev, &msg))
switch (ev.event) switch (ev.event)
{ {
case EVENT_RELOAD: case EVENT_RELOAD:
@@ -825,7 +920,7 @@ static void async_event(int pipe, time_t now)
case EVENT_ALARM: case EVENT_ALARM:
#ifdef HAVE_DHCP #ifdef HAVE_DHCP
if (daemon->dhcp) if (daemon->dhcp || daemon->dhcp6)
{ {
lease_prune(NULL, now); lease_prune(NULL, now);
lease_update_file(now); lease_update_file(now);
@@ -848,11 +943,11 @@ static void async_event(int pipe, time_t now)
break; break;
case EVENT_KILLED: case EVENT_KILLED:
my_syslog(LOG_WARNING, _("child process killed by signal %d"), ev.data); my_syslog(LOG_WARNING, _("script process killed by signal %d"), ev.data);
break; break;
case EVENT_EXITED: case EVENT_EXITED:
my_syslog(LOG_WARNING, _("child process exited with status %d"), ev.data); my_syslog(LOG_WARNING, _("script process exited with status %d"), ev.data);
break; break;
case EVENT_EXEC_ERR: case EVENT_EXEC_ERR:
@@ -861,9 +956,10 @@ static void async_event(int pipe, time_t now)
break; break;
/* necessary for fatal errors in helper */ /* necessary for fatal errors in helper */
case EVENT_HUSER_ERR: case EVENT_USER_ERR:
case EVENT_DIE: case EVENT_DIE:
fatal_event(&ev); case EVENT_LUA_ERR:
fatal_event(&ev, msg);
break; break;
case EVENT_REOPEN: case EVENT_REOPEN:
@@ -880,7 +976,7 @@ static void async_event(int pipe, time_t now)
if (daemon->tcp_pids[i] != 0) if (daemon->tcp_pids[i] != 0)
kill(daemon->tcp_pids[i], SIGALRM); kill(daemon->tcp_pids[i], SIGALRM);
#if defined(HAVE_DHCP) && defined(HAVE_SCRIPT) #if defined(HAVE_SCRIPT)
/* handle pending lease transitions */ /* handle pending lease transitions */
if (daemon->helperfd != -1) if (daemon->helperfd != -1)
{ {
@@ -982,7 +1078,7 @@ void clear_cache_and_reload(time_t now)
cache_reload(); cache_reload();
#ifdef HAVE_DHCP #ifdef HAVE_DHCP
if (daemon->dhcp) if (daemon->dhcp || daemon->dhcp6)
{ {
if (option_bool(OPT_ETHERS)) if (option_bool(OPT_ETHERS))
dhcp_read_ethers(); dhcp_read_ethers();
@@ -1147,9 +1243,6 @@ static void check_dns_listeners(fd_set *set, time_t now)
unsigned char *buff; unsigned char *buff;
struct server *s; struct server *s;
int flags; int flags;
struct in_addr dst_addr_4;
dst_addr_4.s_addr = 0;
#ifndef NO_FORK #ifndef NO_FORK
/* Arrange for SIGALARM after CHILD_LIFETIME seconds to /* Arrange for SIGALARM after CHILD_LIFETIME seconds to
@@ -1168,10 +1261,7 @@ static void check_dns_listeners(fd_set *set, time_t now)
if ((flags = fcntl(confd, F_GETFL, 0)) != -1) if ((flags = fcntl(confd, F_GETFL, 0)) != -1)
fcntl(confd, F_SETFL, flags & ~O_NONBLOCK); fcntl(confd, F_SETFL, flags & ~O_NONBLOCK);
if (listener->family == AF_INET) buff = tcp_request(confd, now, &iface->addr, iface->netmask);
dst_addr_4 = iface->addr.in.sin_addr;
buff = tcp_request(confd, now, dst_addr_4, iface->netmask);
shutdown(confd, SHUT_RDWR); shutdown(confd, SHUT_RDWR);
close(confd); close(confd);
+150 -35
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2011 Simon Kelley /* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -14,7 +14,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#define COPYRIGHT "Copyright (c) 2000-2011 Simon Kelley" #define COPYRIGHT "Copyright (c) 2000-2012 Simon Kelley"
#ifndef NO_LARGEFILE #ifndef NO_LARGEFILE
/* Ensure we can use files >2GB (log files may grow this big) */ /* Ensure we can use files >2GB (log files may grow this big) */
@@ -39,9 +39,14 @@
/* get these before config.h for IPv6 stuff... */ /* get these before config.h for IPv6 stuff... */
#include <sys/types.h> #include <sys/types.h>
#include <sys/socket.h> #include <sys/socket.h>
#ifdef __APPLE__
/* Define before netinet/in.h to select API. OSX Lion onwards. */
# define __APPLE_USE_RFC_3542
#endif
#include <netinet/in.h> #include <netinet/in.h>
/* and this. */ /* Also needed before config.h. */
#include <getopt.h> #include <getopt.h>
#include "config.h" #include "config.h"
@@ -49,9 +54,13 @@
typedef unsigned char u8; typedef unsigned char u8;
typedef unsigned short u16; typedef unsigned short u16;
typedef unsigned int u32; typedef unsigned int u32;
typedef unsigned long long u64;
#include "dns_protocol.h" #include "dns_protocol.h"
#include "dhcp_protocol.h" #include "dhcp_protocol.h"
#ifdef HAVE_DHCP6
#include "dhcp6_protocol.h"
#endif
#define gettext_noop(S) (S) #define gettext_noop(S) (S)
#ifndef LOCALEDIR #ifndef LOCALEDIR
@@ -127,7 +136,7 @@ extern int capget(cap_user_header_t header, cap_user_data_t data);
/* Async event queue */ /* Async event queue */
struct event_desc { struct event_desc {
int event, data; int event, data, msg_sz;
}; };
#define EVENT_RELOAD 1 #define EVENT_RELOAD 1
@@ -148,6 +157,7 @@ struct event_desc {
#define EVENT_DIE 16 #define EVENT_DIE 16
#define EVENT_LOG_ERR 17 #define EVENT_LOG_ERR 17
#define EVENT_FORK_ERR 18 #define EVENT_FORK_ERR 18
#define EVENT_LUA_ERR 19
/* Exit codes. */ /* Exit codes. */
#define EC_GOOD 0 #define EC_GOOD 0
@@ -202,7 +212,10 @@ struct event_desc {
#define OPT_NO_REBIND 31 #define OPT_NO_REBIND 31
#define OPT_ADD_MAC 32 #define OPT_ADD_MAC 32
#define OPT_DNSSEC 33 #define OPT_DNSSEC 33
#define OPT_LAST 34 #define OPT_CONSEC_ADDR 34
#define OPT_CONNTRACK 35
#define OPT_FQDN_UPDATE 36
#define OPT_LAST 37
/* extra flags for my_syslog, we use a couple of facilities since they are known /* extra flags for my_syslog, we use a couple of facilities since they are known
not to occupy the same bits as priorities, no matter how syslog.h is set up. */ not to occupy the same bits as priorities, no matter how syslog.h is set up. */
@@ -309,7 +322,6 @@ struct crec {
#define F_RRNAME (1u<<17) #define F_RRNAME (1u<<17)
#define F_SERVER (1u<<18) #define F_SERVER (1u<<18)
#define F_QUERY (1u<<19) #define F_QUERY (1u<<19)
#define F_NSRR (1u<<20)
/* struct sockaddr is not large enough to hold any address, /* struct sockaddr is not large enough to hold any address,
@@ -362,7 +374,7 @@ struct server {
struct irec { struct irec {
union mysockaddr addr; union mysockaddr addr;
struct in_addr netmask; /* only valid for IPv4 */ struct in_addr netmask; /* only valid for IPv4 */
int tftp_ok, mtu; int tftp_ok, mtu, done, dad;
char *name; char *name;
struct irec *next; struct irec *next;
}; };
@@ -424,21 +436,26 @@ struct frec {
#define ACTION_OLD 3 #define ACTION_OLD 3
#define ACTION_ADD 4 #define ACTION_ADD 4
#define LEASE_NEW 1 /* newly created */
#define LEASE_CHANGED 2 /* modified */
#define LEASE_AUX_CHANGED 4 /* CLID or expiry changed */
#define LEASE_AUTH_NAME 8 /* hostname came from config, not from client */
#define LEASE_USED 16 /* used this DHCPv6 transaction */
#define LEASE_NA 32 /* IPv6 no-temporary lease */
#define LEASE_TA 64 /* IPv6 temporary lease */
struct dhcp_lease { struct dhcp_lease {
int clid_len; /* length of client identifier */ int clid_len; /* length of client identifier */
unsigned char *clid; /* clientid */ unsigned char *clid; /* clientid */
char *hostname, *fqdn; /* name from client-hostname option or config */ char *hostname, *fqdn; /* name from client-hostname option or config */
char *old_hostname; /* hostname before it moved to another lease */ char *old_hostname; /* hostname before it moved to another lease */
char auth_name; /* hostname came from config, not from client */ int flags;
char new; /* newly created */
char changed; /* modified */
char aux_changed; /* CLID or expiry changed */
time_t expires; /* lease expiry */ time_t expires; /* lease expiry */
#ifdef HAVE_BROKEN_RTC #ifdef HAVE_BROKEN_RTC
unsigned int length; unsigned int length;
#endif #endif
int hwaddr_len, hwaddr_type; int hwaddr_len, hwaddr_type; /* hw_type used for iaid in v6 */
unsigned char hwaddr[DHCP_CHADDR_MAX]; unsigned char hwaddr[DHCP_CHADDR_MAX]; /* also IPv6 address */
struct in_addr addr, override, giaddr; struct in_addr addr, override, giaddr;
unsigned char *extradata; unsigned char *extradata;
unsigned int extradata_len, extradata_size; unsigned int extradata_len, extradata_size;
@@ -475,6 +492,9 @@ struct dhcp_config {
unsigned char *clid; /* clientid */ unsigned char *clid; /* clientid */
char *hostname, *domain; char *hostname, *domain;
struct dhcp_netid_list *netid; struct dhcp_netid_list *netid;
#ifdef HAVE_DHCP6
struct in6_addr addr6;
#endif
struct in_addr addr; struct in_addr addr;
time_t decline_time; time_t decline_time;
unsigned int lease_time; unsigned int lease_time;
@@ -482,6 +502,8 @@ struct dhcp_config {
struct dhcp_config *next; struct dhcp_config *next;
}; };
#define have_config(config, mask) ((config) && ((config)->flags & (mask)))
#define CONFIG_DISABLE 1 #define CONFIG_DISABLE 1
#define CONFIG_CLID 2 #define CONFIG_CLID 2
#define CONFIG_TIME 8 #define CONFIG_TIME 8
@@ -492,6 +514,7 @@ struct dhcp_config {
#define CONFIG_ADDR_HOSTS 512 /* address added by from /etc/hosts */ #define CONFIG_ADDR_HOSTS 512 /* address added by from /etc/hosts */
#define CONFIG_DECLINED 1024 /* address declined by client */ #define CONFIG_DECLINED 1024 /* address declined by client */
#define CONFIG_BANK 2048 /* from dhcp hosts file */ #define CONFIG_BANK 2048 /* from dhcp hosts file */
#define CONFIG_ADDR6 4096
struct dhcp_opt { struct dhcp_opt {
int opt, len, flags; int opt, len, flags;
@@ -517,9 +540,11 @@ struct dhcp_opt {
#define DHOPT_HEX 512 #define DHOPT_HEX 512
#define DHOPT_VENDOR_MATCH 1024 #define DHOPT_VENDOR_MATCH 1024
#define DHOPT_RFC3925 2048 #define DHOPT_RFC3925 2048
#define DHOPT_TAGOK 4096
#define DHOPT_ADDR6 8192
struct dhcp_boot { struct dhcp_boot {
char *file, *sname; char *file, *sname, *tftp_sname;
struct in_addr next_server; struct in_addr next_server;
struct dhcp_netid *netid; struct dhcp_netid *netid;
struct dhcp_boot *next; struct dhcp_boot *next;
@@ -527,7 +552,7 @@ struct dhcp_boot {
struct pxe_service { struct pxe_service {
unsigned short CSA, type; unsigned short CSA, type;
char *menu, *basename; char *menu, *basename, *sname;
struct in_addr server; struct in_addr server;
struct dhcp_netid *netid; struct dhcp_netid *netid;
struct pxe_service *next; struct pxe_service *next;
@@ -541,7 +566,8 @@ struct pxe_service {
/* vendorclass, userclass, remote-id or cicuit-id */ /* vendorclass, userclass, remote-id or cicuit-id */
struct dhcp_vendor { struct dhcp_vendor {
int len, match_type, option; int len, match_type;
unsigned int enterprise;
char *data; char *data;
struct dhcp_netid netid; struct dhcp_netid netid;
struct dhcp_vendor *next; struct dhcp_vendor *next;
@@ -563,6 +589,10 @@ struct dhcp_bridge {
struct cond_domain { struct cond_domain {
char *domain; char *domain;
struct in_addr start, end; struct in_addr start, end;
#ifdef HAVE_IPV6
struct in6_addr start6, end6;
#endif
int is6;
struct cond_domain *next; struct cond_domain *next;
}; };
@@ -571,6 +601,11 @@ struct dhcp_context {
struct in_addr netmask, broadcast; struct in_addr netmask, broadcast;
struct in_addr local, router; struct in_addr local, router;
struct in_addr start, end; /* range of available addresses */ struct in_addr start, end; /* range of available addresses */
#ifdef HAVE_DHCP6
struct in6_addr start6, end6; /* range of available addresses */
struct in6_addr local6, router6;
int prefix;
#endif
int flags; int flags;
char *interface; char *interface;
struct dhcp_netid netid, *filter; struct dhcp_netid netid, *filter;
@@ -585,6 +620,7 @@ struct dhcp_context {
struct ping_result { struct ping_result {
struct in_addr addr; struct in_addr addr;
time_t time; time_t time;
unsigned int hash;
struct ping_result *next; struct ping_result *next;
}; };
@@ -642,6 +678,7 @@ extern struct daemon {
char *mxtarget; char *mxtarget;
char *lease_file; char *lease_file;
char *username, *groupname, *scriptuser; char *username, *groupname, *scriptuser;
char *luascript;
int group_set, osport; int group_set, osport;
char *domain_suffix; char *domain_suffix;
struct cond_domain *cond_domain; struct cond_domain *cond_domain;
@@ -657,9 +694,9 @@ extern struct daemon {
int port, query_port, min_port; int port, query_port, min_port;
unsigned long local_ttl, neg_ttl, max_ttl; unsigned long local_ttl, neg_ttl, max_ttl;
struct hostsfile *addn_hosts; struct hostsfile *addn_hosts;
struct dhcp_context *dhcp; struct dhcp_context *dhcp, *dhcp6;
struct dhcp_config *dhcp_conf; struct dhcp_config *dhcp_conf;
struct dhcp_opt *dhcp_opts, *dhcp_match; struct dhcp_opt *dhcp_opts, *dhcp_match, *dhcp_opts6, *dhcp_match6;
struct dhcp_vendor *dhcp_vendors; struct dhcp_vendor *dhcp_vendors;
struct dhcp_mac *dhcp_macs; struct dhcp_mac *dhcp_macs;
struct dhcp_boot *boot_config; struct dhcp_boot *boot_config;
@@ -713,7 +750,12 @@ extern struct daemon {
struct ping_result *ping_results; struct ping_result *ping_results;
FILE *lease_stream; FILE *lease_stream;
struct dhcp_bridge *bridges; struct dhcp_bridge *bridges;
#ifdef HAVE_DHCP6
int duid_len;
unsigned char *duid;
struct iovec outpacket;
int dhcp6fd;
#endif
/* DBus stuff */ /* DBus stuff */
/* void * here to avoid depending on dbus headers outside dbus.c */ /* void * here to avoid depending on dbus headers outside dbus.c */
void *dbus; void *dbus;
@@ -724,6 +766,9 @@ extern struct daemon {
/* TFTP stuff */ /* TFTP stuff */
struct tftp_transfer *tftp_trans; struct tftp_transfer *tftp_trans;
/* utility string buffer, hold max sized IP address as string */
char *addrbuff;
} *daemon; } *daemon;
/* cache.c */ /* cache.c */
@@ -741,11 +786,15 @@ void cache_start_insert(void);
struct crec *cache_insert(char *name, struct all_addr *addr, struct crec *cache_insert(char *name, struct all_addr *addr,
time_t now, unsigned long ttl, unsigned short flags); time_t now, unsigned long ttl, unsigned short flags);
void cache_reload(void); void cache_reload(void);
void cache_add_dhcp_entry(char *host_name, struct in_addr *host_address, time_t ttd); void cache_add_dhcp_entry(char *host_name, int prot, struct all_addr *host_address, time_t ttd);
struct in_addr a_record_from_hosts(char *name, time_t now);
void cache_unhash_dhcp(void); void cache_unhash_dhcp(void);
void dump_cache(time_t now); void dump_cache(time_t now);
char *cache_get_name(struct crec *crecp); char *cache_get_name(struct crec *crecp);
char *get_domain(struct in_addr addr); char *get_domain(struct in_addr addr);
#ifdef HAVE_IPV6
char *get_domain6(struct in6_addr *addr);
#endif
/* rfc1035.c */ /* rfc1035.c */
unsigned int extract_request(struct dns_header *header, size_t qlen, unsigned int extract_request(struct dns_header *header, size_t qlen,
@@ -781,6 +830,11 @@ int sockaddr_isequal(union mysockaddr *s1, union mysockaddr *s2);
int hostname_isequal(char *a, char *b); int hostname_isequal(char *a, char *b);
time_t dnsmasq_time(void); time_t dnsmasq_time(void);
int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask); int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask);
#ifdef HAVE_IPV6
int is_same_net6(struct in6_addr *a, struct in6_addr *b, int prefixlen);
u64 addr6part(struct in6_addr *addr);
void setaddr6part(struct in6_addr *addr, u64 host);
#endif
int retry_send(void); int retry_send(void);
void prettyprint_time(char *buf, unsigned int t); void prettyprint_time(char *buf, unsigned int t);
int prettyprint_addr(union mysockaddr *addr, char *buf); int prettyprint_addr(union mysockaddr *addr, char *buf);
@@ -804,7 +858,8 @@ void flush_log(void);
/* option.c */ /* option.c */
void read_opts (int argc, char **argv, char *compile_opts); void read_opts (int argc, char **argv, char *compile_opts);
char *option_string(unsigned char opt, int *is_ip, int *is_name); char *option_string(int prot, unsigned int opt, unsigned char *val,
int opt_len, char *buf, int buf_len);
void reread_dhcp(void); void reread_dhcp(void);
void set_option_bool(unsigned int opt); void set_option_bool(unsigned int opt);
struct hostsfile *expand_filelist(struct hostsfile *list); struct hostsfile *expand_filelist(struct hostsfile *list);
@@ -813,9 +868,12 @@ struct hostsfile *expand_filelist(struct hostsfile *list);
void reply_query(int fd, int family, time_t now); void reply_query(int fd, int family, time_t now);
void receive_query(struct listener *listen, time_t now); void receive_query(struct listener *listen, time_t now);
unsigned char *tcp_request(int confd, time_t now, unsigned char *tcp_request(int confd, time_t now,
struct in_addr local_addr, struct in_addr netmask); union mysockaddr *local_addr, struct in_addr netmask);
void server_gone(struct server *server); void server_gone(struct server *server);
struct frec *get_new_frec(time_t now, int *wait); struct frec *get_new_frec(time_t now, int *wait);
void send_from(int fd, int nowild, char *packet, size_t len,
union mysockaddr *to, struct all_addr *source,
unsigned int iface);
/* network.c */ /* network.c */
int indextoname(int fd, int index, char *name); int indextoname(int fd, int index, char *name);
@@ -825,11 +883,15 @@ void pre_allocate_sfds(void);
int reload_servers(char *fname); int reload_servers(char *fname);
void check_servers(void); void check_servers(void);
int enumerate_interfaces(); int enumerate_interfaces();
struct listener *create_wildcard_listeners(void); void create_wildcard_listeners(void);
struct listener *create_bound_listeners(void); void create_bound_listeners(int die);
int iface_check(int family, struct all_addr *addr, char *name, int *indexp); int is_dad_listeners(void);
int iface_check(int family, struct all_addr *addr, char *name);
int fix_fd(int fd); int fix_fd(int fd);
struct in_addr get_ifaddr(char *intr); struct in_addr get_ifaddr(char *intr);
#ifdef HAVE_IPV6
int set_ipv6pktinfo(int fd);
#endif
/* dhcp.c */ /* dhcp.c */
#ifdef HAVE_DHCP #ifdef HAVE_DHCP
@@ -841,24 +903,18 @@ struct dhcp_context *address_available(struct dhcp_context *context,
struct dhcp_context *narrow_context(struct dhcp_context *context, struct dhcp_context *narrow_context(struct dhcp_context *context,
struct in_addr taddr, struct in_addr taddr,
struct dhcp_netid *netids); struct dhcp_netid *netids);
int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool, int negonly);
int address_allocate(struct dhcp_context *context, int address_allocate(struct dhcp_context *context,
struct in_addr *addrp, unsigned char *hwaddr, int hw_len, struct in_addr *addrp, unsigned char *hwaddr, int hw_len,
struct dhcp_netid *netids, time_t now); struct dhcp_netid *netids, time_t now);
struct dhcp_netid *run_tag_if(struct dhcp_netid *input);
int config_has_mac(struct dhcp_config *config, unsigned char *hwaddr, int len, int type); int config_has_mac(struct dhcp_config *config, unsigned char *hwaddr, int len, int type);
struct dhcp_config *find_config(struct dhcp_config *configs, struct dhcp_config *find_config(struct dhcp_config *configs,
struct dhcp_context *context, struct dhcp_context *context,
unsigned char *clid, int clid_len, unsigned char *clid, int clid_len,
unsigned char *hwaddr, int hw_len, unsigned char *hwaddr, int hw_len,
int hw_type, char *hostname); int hw_type, char *hostname);
void dhcp_update_configs(struct dhcp_config *configs);
void dhcp_read_ethers(void); void dhcp_read_ethers(void);
void check_dhcp_hosts(int fatal);
struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr); struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr);
char *strip_hostname(char *hostname);
char *host_from_dns(struct in_addr addr); char *host_from_dns(struct in_addr addr);
char *get_domain(struct in_addr addr);
#endif #endif
/* lease.c */ /* lease.c */
@@ -866,25 +922,37 @@ char *get_domain(struct in_addr addr);
void lease_update_file(time_t now); void lease_update_file(time_t now);
void lease_update_dns(); void lease_update_dns();
void lease_init(time_t now); void lease_init(time_t now);
struct dhcp_lease *lease_allocate(struct in_addr addr); struct dhcp_lease *lease4_allocate(struct in_addr addr);
#ifdef HAVE_DHCP6
struct dhcp_lease *lease6_allocate(struct in6_addr *addrp, int lease_type);
struct dhcp_lease *lease6_find(unsigned char *clid, int clid_len,
int lease_type, int iaid, struct in6_addr *addr);
struct dhcp_lease *lease6_find_by_addr(struct in6_addr *net, int prefix, u64 addr);
u64 lease_find_max_addr6(struct dhcp_context *context);
#endif
void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr, void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr,
unsigned char *clid, int hw_len, int hw_type, int clid_len); unsigned char *clid, int hw_len, int hw_type, int clid_len);
void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth); void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth, char *domain, char *config_domain);
void lease_set_expires(struct dhcp_lease *lease, unsigned int len, time_t now); void lease_set_expires(struct dhcp_lease *lease, unsigned int len, time_t now);
void lease_set_interface(struct dhcp_lease *lease, int interface); void lease_set_interface(struct dhcp_lease *lease, int interface);
struct dhcp_lease *lease_find_by_client(unsigned char *hwaddr, int hw_len, int hw_type, struct dhcp_lease *lease_find_by_client(unsigned char *hwaddr, int hw_len, int hw_type,
unsigned char *clid, int clid_len); unsigned char *clid, int clid_len);
struct dhcp_lease *lease_find_by_addr(struct in_addr addr); struct dhcp_lease *lease_find_by_addr(struct in_addr addr);
struct in_addr lease_find_max_addr(struct dhcp_context *context);
void lease_prune(struct dhcp_lease *target, time_t now); void lease_prune(struct dhcp_lease *target, time_t now);
void lease_update_from_configs(void); void lease_update_from_configs(void);
int do_script_run(time_t now); int do_script_run(time_t now);
void rerun_scripts(void); void rerun_scripts(void);
#ifdef HAVE_SCRIPT
void lease_add_extradata(struct dhcp_lease *lease, unsigned char *data,
unsigned int len, int delim);
#endif
#endif #endif
/* rfc2131.c */ /* rfc2131.c */
#ifdef HAVE_DHCP #ifdef HAVE_DHCP
size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index, size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
size_t sz, time_t now, int unicast_dest, int *is_inform, int pxe_fd); size_t sz, time_t now, int unicast_dest, int *is_inform, int pxe_fd, struct in_addr fallback);
unsigned char *extended_hwaddr(int hwtype, int hwlen, unsigned char *hwaddr, unsigned char *extended_hwaddr(int hwtype, int hwlen, unsigned char *hwaddr,
int clid_len, unsigned char *clid, int *len_out); int clid_len, unsigned char *clid, int *len_out);
#endif #endif
@@ -894,7 +962,7 @@ unsigned char *extended_hwaddr(int hwtype, int hwlen, unsigned char *hwaddr,
int make_icmp_sock(void); int make_icmp_sock(void);
int icmp_ping(struct in_addr addr); int icmp_ping(struct in_addr addr);
#endif #endif
void send_event(int fd, int event, int data); void send_event(int fd, int event, int data, char *msg);
void clear_cache_and_reload(time_t now); void clear_cache_and_reload(time_t now);
void poll_resolv(int force, int do_reload, time_t now); void poll_resolv(int force, int do_reload, time_t now);
@@ -938,3 +1006,50 @@ int helper_buf_empty(void);
void tftp_request(struct listener *listen, time_t now); void tftp_request(struct listener *listen, time_t now);
void check_tftp_listeners(fd_set *rset, time_t now); void check_tftp_listeners(fd_set *rset, time_t now);
#endif #endif
/* conntrack.c */
#ifdef HAVE_CONNTRACK
int get_incoming_mark(union mysockaddr *peer_addr, struct all_addr *local_addr,
int istcp, unsigned int *markp);
#endif
/* dhcp6.c */
#ifdef HAVE_DHCP6
void dhcp6_init(void);
void dhcp6_packet(time_t now);
int address6_allocate(struct dhcp_context *context, unsigned char *clid, int clid_len,
int serial, struct dhcp_netid *netids, struct in6_addr *ans);
struct dhcp_context *address6_available(struct dhcp_context *context,
struct in6_addr *taddr,
struct dhcp_netid *netids);
struct dhcp_context *narrow_context6(struct dhcp_context *context,
struct in6_addr *taddr,
struct dhcp_netid *netids);
struct dhcp_config *find_config6(struct dhcp_config *configs,
struct dhcp_context *context,
unsigned char *duid, int duid_len,
char *hostname);
struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct in6_addr *net,
int prefix, u64 addr);
void make_duid(time_t now);
#endif
/* rfc3315.c */
#ifdef HAVE_DHCP6
size_t dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name, size_t sz, int is_multicast, time_t now);
#endif
/* dhcp-common.c */
#ifdef HAVE_DHCP
void dhcp_common_init(void);
ssize_t recv_dhcp_packet(int fd, struct msghdr *msg);
struct dhcp_netid *run_tag_if(struct dhcp_netid *input);
struct dhcp_netid *option_filter(struct dhcp_netid *tags, struct dhcp_netid *context_tags,
struct dhcp_opt *opts);
int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool, int negonly);
char *strip_hostname(char *hostname);
void log_tags(struct dhcp_netid *netid, u32 xid);
int match_bytes(struct dhcp_opt *o, unsigned char *p, int len);
void dhcp_update_configs(struct dhcp_config *configs);
void check_dhcp_hosts(int fatal);
#endif
+78 -49
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2011 Simon Kelley /* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -26,9 +26,9 @@ static struct randfd *allocate_rfd(int family);
/* Send a UDP packet with its source address set as "source" /* Send a UDP packet with its source address set as "source"
unless nowild is true, when we just send it with the kernel default */ unless nowild is true, when we just send it with the kernel default */
static void send_from(int fd, int nowild, char *packet, size_t len, void send_from(int fd, int nowild, char *packet, size_t len,
union mysockaddr *to, struct all_addr *source, union mysockaddr *to, struct all_addr *source,
unsigned int iface) unsigned int iface)
{ {
struct msghdr msg; struct msghdr msg;
struct iovec iov[1]; struct iovec iov[1];
@@ -70,7 +70,7 @@ static void send_from(int fd, int nowild, char *packet, size_t len,
p.ipi_spec_dst = source->addr.addr4; p.ipi_spec_dst = source->addr.addr4;
memcpy(CMSG_DATA(cmptr), &p, sizeof(p)); memcpy(CMSG_DATA(cmptr), &p, sizeof(p));
msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
cmptr->cmsg_level = SOL_IP; cmptr->cmsg_level = IPPROTO_IP;
cmptr->cmsg_type = IP_PKTINFO; cmptr->cmsg_type = IP_PKTINFO;
#elif defined(IP_SENDSRCADDR) #elif defined(IP_SENDSRCADDR)
memcpy(CMSG_DATA(cmptr), &(source->addr.addr4), sizeof(source->addr.addr4)); memcpy(CMSG_DATA(cmptr), &(source->addr.addr4), sizeof(source->addr.addr4));
@@ -88,10 +88,10 @@ static void send_from(int fd, int nowild, char *packet, size_t len,
memcpy(CMSG_DATA(cmptr), &p, sizeof(p)); memcpy(CMSG_DATA(cmptr), &p, sizeof(p));
msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
cmptr->cmsg_type = daemon->v6pktinfo; cmptr->cmsg_type = daemon->v6pktinfo;
cmptr->cmsg_level = IPV6_LEVEL; cmptr->cmsg_level = IPPROTO_IPV6;
} }
#else #else
iface = 0; /* eliminate warning */ (void)iface; /* eliminate warning */
#endif #endif
} }
@@ -207,10 +207,10 @@ static unsigned int search_servers(time_t now, struct all_addr **addrpp,
} }
} }
if (flags == 0 && !(qtype & F_NSRR) && if (flags == 0 && !(qtype & F_QUERY) &&
option_bool(OPT_NODOTS_LOCAL) && !strchr(qdomain, '.') && namelen != 0) option_bool(OPT_NODOTS_LOCAL) && !strchr(qdomain, '.') && namelen != 0)
/* don't forward simple names, make exception for NS queries and empty name. */ /* don't forward A or AAAA queries for simple names, except the empty name */
flags = F_NXDOMAIN; flags = F_NOERR;
if (flags == F_NXDOMAIN && check_for_local_domain(qdomain, now)) if (flags == F_NXDOMAIN && check_for_local_domain(qdomain, now))
flags = F_NOERR; flags = F_NOERR;
@@ -243,7 +243,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
unsigned int flags = 0; unsigned int flags = 0;
unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL); unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL);
struct server *start = NULL; struct server *start = NULL;
/* RFC 4035: sect 4.6 para 2 */ /* RFC 4035: sect 4.6 para 2 */
header->hb4 &= ~HB4_AD; header->hb4 &= ~HB4_AD;
@@ -366,6 +366,16 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
daemon->rfd_save = forward->rfd4; daemon->rfd_save = forward->rfd4;
fd = forward->rfd4->fd; fd = forward->rfd4->fd;
} }
#ifdef HAVE_CONNTRACK
/* Copy connection mark of incoming query to outgoing connection. */
if (option_bool(OPT_CONNTRACK))
{
unsigned int mark;
if (get_incoming_mark(udpaddr, dst_addr, 0, &mark))
setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
}
#endif
} }
if (sendto(fd, (char *)header, plen, 0, if (sendto(fd, (char *)header, plen, 0,
@@ -693,7 +703,7 @@ void receive_query(struct listener *listen, time_t now)
#if defined(HAVE_LINUX_NETWORK) #if defined(HAVE_LINUX_NETWORK)
if (listen->family == AF_INET) if (listen->family == AF_INET)
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO) if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO)
{ {
union { union {
unsigned char *c; unsigned char *c;
@@ -733,7 +743,7 @@ void receive_query(struct listener *listen, time_t now)
if (listen->family == AF_INET6) if (listen->family == AF_INET6)
{ {
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == IPV6_LEVEL && cmptr->cmsg_type == daemon->v6pktinfo) if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
{ {
union { union {
unsigned char *c; unsigned char *c;
@@ -750,7 +760,7 @@ void receive_query(struct listener *listen, time_t now)
/* enforce available interface configuration */ /* enforce available interface configuration */
if (!indextoname(listen->fd, if_index, ifr.ifr_name) || if (!indextoname(listen->fd, if_index, ifr.ifr_name) ||
!iface_check(listen->family, &dst_addr, ifr.ifr_name, &if_index)) !iface_check(listen->family, &dst_addr, ifr.ifr_name))
return; return;
if (listen->family == AF_INET && if (listen->family == AF_INET &&
@@ -797,7 +807,7 @@ void receive_query(struct listener *listen, time_t now)
about resources for debug mode, when the fork is suppressed: that's about resources for debug mode, when the fork is suppressed: that's
done by the caller. */ done by the caller. */
unsigned char *tcp_request(int confd, time_t now, unsigned char *tcp_request(int confd, time_t now,
struct in_addr local_addr, struct in_addr netmask) union mysockaddr *local_addr, struct in_addr netmask)
{ {
size_t size = 0; size_t size = 0;
int norebind = 0; int norebind = 0;
@@ -809,7 +819,13 @@ unsigned char *tcp_request(int confd, time_t now,
unsigned char *packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ); unsigned char *packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ);
struct dns_header *header; struct dns_header *header;
struct server *last_server; struct server *last_server;
struct in_addr dst_addr_4;
union mysockaddr peer_addr;
socklen_t peer_len = sizeof(union mysockaddr);
if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) == -1)
return packet;
while (1) while (1)
{ {
if (!packet || if (!packet ||
@@ -831,29 +847,28 @@ unsigned char *tcp_request(int confd, time_t now,
if ((gotname = extract_request(header, (unsigned int)size, daemon->namebuff, &qtype))) if ((gotname = extract_request(header, (unsigned int)size, daemon->namebuff, &qtype)))
{ {
union mysockaddr peer_addr; char types[20];
socklen_t peer_len = sizeof(union mysockaddr);
if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) != -1) querystr(types, qtype);
{
char types[20]; if (peer_addr.sa.sa_family == AF_INET)
log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff,
querystr(types, qtype); (struct all_addr *)&peer_addr.in.sin_addr, types);
if (peer_addr.sa.sa_family == AF_INET)
log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff,
(struct all_addr *)&peer_addr.in.sin_addr, types);
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
else else
log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff, log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff,
(struct all_addr *)&peer_addr.in6.sin6_addr, types); (struct all_addr *)&peer_addr.in6.sin6_addr, types);
#endif #endif
}
} }
if (local_addr->sa.sa_family == AF_INET)
dst_addr_4 = local_addr->in.sin_addr;
else
dst_addr_4.s_addr = 0;
/* m > 0 if answered from cache */ /* m > 0 if answered from cache */
m = answer_request(header, ((char *) header) + 65536, (unsigned int)size, m = answer_request(header, ((char *) header) + 65536, (unsigned int)size,
local_addr, netmask, now); dst_addr_4, netmask, now);
/* Do this by steam now we're not in the select() loop */ /* Do this by steam now we're not in the select() loop */
check_log_writer(NULL); check_log_writer(NULL);
@@ -866,14 +881,8 @@ unsigned char *tcp_request(int confd, time_t now,
char *domain = NULL; char *domain = NULL;
if (option_bool(OPT_ADD_MAC)) if (option_bool(OPT_ADD_MAC))
{ size = add_mac(header, size, ((char *) header) + 65536, &peer_addr);
union mysockaddr peer_addr;
socklen_t peer_len = sizeof(union mysockaddr);
if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) != -1)
size = add_mac(header, size, ((char *) header) + 65536, &peer_addr);
}
if (gotname) if (gotname)
flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind); flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind);
@@ -907,18 +916,38 @@ unsigned char *tcp_request(int confd, time_t now,
if (type != (last_server->flags & SERV_TYPE) || if (type != (last_server->flags & SERV_TYPE) ||
(type == SERV_HAS_DOMAIN && !hostname_isequal(domain, last_server->domain))) (type == SERV_HAS_DOMAIN && !hostname_isequal(domain, last_server->domain)))
continue; continue;
if ((last_server->tcpfd == -1) && if (last_server->tcpfd == -1)
(last_server->tcpfd = socket(last_server->addr.sa.sa_family, SOCK_STREAM, 0)) != -1 &&
(!local_bind(last_server->tcpfd, &last_server->source_addr, last_server->interface, 1) ||
connect(last_server->tcpfd, &last_server->addr.sa, sa_len(&last_server->addr)) == -1))
{ {
close(last_server->tcpfd); if ((last_server->tcpfd = socket(last_server->addr.sa.sa_family, SOCK_STREAM, 0)) == -1)
last_server->tcpfd = -1; continue;
if ((!local_bind(last_server->tcpfd, &last_server->source_addr, last_server->interface, 1) ||
connect(last_server->tcpfd, &last_server->addr.sa, sa_len(&last_server->addr)) == -1))
{
close(last_server->tcpfd);
last_server->tcpfd = -1;
continue;
}
#ifdef HAVE_CONNTRACK
/* Copy connection mark of incoming query to outgoing connection. */
if (option_bool(OPT_CONNTRACK))
{
unsigned int mark;
struct all_addr local;
#ifdef HAVE_IPV6
if (local_addr->sa.sa_family == AF_INET6)
local.addr.addr6 = local_addr->in6.sin6_addr;
else
#endif
local.addr.addr4 = local_addr->in.sin_addr;
if (get_incoming_mark(&peer_addr, &local, 1, &mark))
setsockopt(last_server->tcpfd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
}
#endif
} }
if (last_server->tcpfd == -1)
continue;
c1 = size >> 8; c1 = size >> 8;
c2 = size; c2 = size;
+364 -72
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2011 Simon Kelley /* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -16,6 +16,8 @@
#include "dnsmasq.h" #include "dnsmasq.h"
#ifdef HAVE_SCRIPT
/* This file has code to fork a helper process which recieves data via a pipe /* This file has code to fork a helper process which recieves data via a pipe
shared with the main process and which is responsible for calling a script when shared with the main process and which is responsible for calling a script when
DHCP leases change. DHCP leases change.
@@ -28,15 +30,25 @@
main process. main process.
*/ */
#if defined(HAVE_DHCP) && defined(HAVE_SCRIPT)
static void my_setenv(const char *name, const char *value, int *error); static void my_setenv(const char *name, const char *value, int *error);
static unsigned char *grab_extradata(unsigned char *buf, unsigned char *end, char *env, int *err); static unsigned char *grab_extradata(unsigned char *buf, unsigned char *end, char *env, int *err);
#ifdef HAVE_LUASCRIPT
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
lua_State *lua;
static unsigned char *grab_extradata_lua(unsigned char *buf, unsigned char *end, char *field);
#endif
struct script_data struct script_data
{ {
unsigned char action, hwaddr_len, hwaddr_type; int flags;
unsigned char clid_len, hostname_len, ed_len; int action, hwaddr_len, hwaddr_type;
int clid_len, hostname_len, ed_len;
struct in_addr addr, giaddr; struct in_addr addr, giaddr;
unsigned int remaining_time; unsigned int remaining_time;
#ifdef HAVE_BROKEN_RTC #ifdef HAVE_BROKEN_RTC
@@ -46,6 +58,7 @@ struct script_data
#endif #endif
unsigned char hwaddr[DHCP_CHADDR_MAX]; unsigned char hwaddr[DHCP_CHADDR_MAX];
char interface[IF_NAMESIZE]; char interface[IF_NAMESIZE];
}; };
static struct script_data *buf = NULL; static struct script_data *buf = NULL;
@@ -61,7 +74,7 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
then fork our process. */ then fork our process. */
if (pipe(pipefd) == -1 || !fix_fd(pipefd[1]) || (pid = fork()) == -1) if (pipe(pipefd) == -1 || !fix_fd(pipefd[1]) || (pid = fork()) == -1)
{ {
send_event(err_fd, EVENT_PIPE_ERR, errno); send_event(err_fd, EVENT_PIPE_ERR, errno, NULL);
_exit(0); _exit(0);
} }
@@ -88,38 +101,98 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
{ {
if (option_bool(OPT_NO_FORK)) if (option_bool(OPT_NO_FORK))
/* send error to daemon process if no-fork */ /* send error to daemon process if no-fork */
send_event(event_fd, EVENT_HUSER_ERR, errno); send_event(event_fd, EVENT_USER_ERR, errno, daemon->scriptuser);
else else
{ {
/* kill daemon */ /* kill daemon */
send_event(event_fd, EVENT_DIE, 0); send_event(event_fd, EVENT_DIE, 0, NULL);
/* return error */ /* return error */
send_event(err_fd, EVENT_HUSER_ERR, errno); send_event(err_fd, EVENT_USER_ERR, errno, daemon->scriptuser);
} }
_exit(0); _exit(0);
} }
} }
/* close all the sockets etc, we don't need them here. This closes err_fd, so that /* close all the sockets etc, we don't need them here.
main process can return. */ Don't close err_fd, in case the lua-init fails.
Note that we have to do this before lua init
so we don't close any lua fds. */
for (max_fd--; max_fd >= 0; max_fd--) for (max_fd--; max_fd >= 0; max_fd--)
if (max_fd != STDOUT_FILENO && max_fd != STDERR_FILENO && if (max_fd != STDOUT_FILENO && max_fd != STDERR_FILENO &&
max_fd != STDIN_FILENO && max_fd != pipefd[0] && max_fd != event_fd) max_fd != STDIN_FILENO && max_fd != pipefd[0] &&
max_fd != event_fd && max_fd != err_fd)
close(max_fd); close(max_fd);
#ifdef HAVE_LUASCRIPT
if (daemon->luascript)
{
const char *lua_err = NULL;
lua = lua_open();
luaL_openlibs(lua);
/* get Lua to load our script file */
if (luaL_dofile(lua, daemon->luascript) != 0)
lua_err = lua_tostring(lua, -1);
else
{
lua_getglobal(lua, "lease");
if (lua_type(lua, -1) != LUA_TFUNCTION)
lua_err = _("lease() function missing in Lua script");
}
if (lua_err)
{
if (option_bool(OPT_NO_FORK) || option_bool(OPT_DEBUG))
/* send error to daemon process if no-fork */
send_event(event_fd, EVENT_LUA_ERR, 0, (char *)lua_err);
else
{
/* kill daemon */
send_event(event_fd, EVENT_DIE, 0, NULL);
/* return error */
send_event(err_fd, EVENT_LUA_ERR, 0, (char *)lua_err);
}
_exit(0);
}
lua_pop(lua, 1); /* remove nil from stack */
lua_getglobal(lua, "init");
if (lua_type(lua, -1) == LUA_TFUNCTION)
lua_call(lua, 0, 0);
else
lua_pop(lua, 1); /* remove nil from stack */
}
#endif
/* All init done, close our copy of the error pipe, so that main process can return */
if (err_fd != -1)
close(err_fd);
/* loop here */ /* loop here */
while(1) while(1)
{ {
struct script_data data; struct script_data data;
char *p, *action_str, *hostname = NULL; char *p, *action_str, *hostname = NULL, *domain = NULL;
unsigned char *buf = (unsigned char *)daemon->namebuff; unsigned char *buf = (unsigned char *)daemon->namebuff;
unsigned char *end, *alloc_buff = NULL; unsigned char *end, *extradata, *alloc_buff = NULL;
int err = 0; int is6, err = 0;
free(alloc_buff);
/* we read zero bytes when pipe closed: this is our signal to exit */ /* we read zero bytes when pipe closed: this is our signal to exit */
if (!read_write(pipefd[0], (unsigned char *)&data, sizeof(data), 1)) if (!read_write(pipefd[0], (unsigned char *)&data, sizeof(data), 1))
_exit(0); {
#ifdef HAVE_LUASCRIPT
if (daemon->luascript)
{
lua_getglobal(lua, "shutdown");
if (lua_type(lua, -1) == LUA_TFUNCTION)
lua_call(lua, 0, 0);
}
#endif
_exit(0);
}
if (data.action == ACTION_DEL) if (data.action == ACTION_DEL)
action_str = "del"; action_str = "del";
else if (data.action == ACTION_ADD) else if (data.action == ACTION_ADD)
@@ -128,53 +201,218 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
action_str = "old"; action_str = "old";
else else
continue; continue;
is6 = !!(data.flags & (LEASE_TA | LEASE_NA));
/* stringify MAC into dhcp_buff */ if (!is6)
p = daemon->dhcp_buff;
if (data.hwaddr_type != ARPHRD_ETHER || data.hwaddr_len == 0)
p += sprintf(p, "%.2x-", data.hwaddr_type);
for (i = 0; (i < data.hwaddr_len) && (i < DHCP_CHADDR_MAX); i++)
{
p += sprintf(p, "%.2x", data.hwaddr[i]);
if (i != data.hwaddr_len - 1)
p += sprintf(p, ":");
}
/* and CLID into packet, avoid overwrite from bad data */
if ((data.clid_len > daemon->packet_buff_sz) || !read_write(pipefd[0], buf, data.clid_len, 1))
continue;
for (p = daemon->packet, i = 0; i < data.clid_len; i++)
{ {
p += sprintf(p, "%.2x", buf[i]); /* stringify MAC into dhcp_buff */
if (i != data.clid_len - 1) p = daemon->dhcp_buff;
p += sprintf(p, ":"); if (data.hwaddr_type != ARPHRD_ETHER || data.hwaddr_len == 0)
p += sprintf(p, "%.2x-", data.hwaddr_type);
for (i = 0; (i < data.hwaddr_len) && (i < DHCP_CHADDR_MAX); i++)
{
p += sprintf(p, "%.2x", data.hwaddr[i]);
if (i != data.hwaddr_len - 1)
p += sprintf(p, ":");
}
} }
#ifdef HAVE_DHCP6
/* and expiry or length into dhcp_buff2 */ else
{
/* duid not MAC for IPv6 */
for (p = daemon->dhcp_buff, i = 0; i < data.clid_len; i++)
{
p += sprintf(p, "%.2x", buf[i]);
if (i != data.clid_len - 1)
p += sprintf(p, ":");
}
}
#endif
/* expiry or length into dhcp_buff2 */
#ifdef HAVE_BROKEN_RTC #ifdef HAVE_BROKEN_RTC
sprintf(daemon->dhcp_buff2, "%u", data.length); sprintf(daemon->dhcp_buff2, "%u", data.length);
#else #else
sprintf(daemon->dhcp_buff2, "%lu", (unsigned long)data.expires); sprintf(daemon->dhcp_buff2, "%lu", (unsigned long)data.expires);
#endif #endif
/* supplied data may just exceed normal buffer (unlikely) */ /* supplied data may just exceed normal buffer (unlikely) */
if ((data.hostname_len + data.ed_len) > daemon->packet_buff_sz && if ((data.hostname_len + data.ed_len + data.clid_len) > MAXDNAME &&
!(alloc_buff = buf = malloc(data.hostname_len + data.ed_len))) !(alloc_buff = buf = malloc(data.hostname_len + data.ed_len + data.clid_len)))
continue; continue;
if (!read_write(pipefd[0], buf, if (!read_write(pipefd[0], buf,
data.hostname_len + data.ed_len, 1)) data.hostname_len + data.ed_len + data.clid_len, 1))
continue; continue;
/* CLID into packet */
if (!is6)
for (p = daemon->packet, i = 0; i < data.clid_len; i++)
{
p += sprintf(p, "%.2x", buf[i]);
if (i != data.clid_len - 1)
p += sprintf(p, ":");
}
#ifdef HAVE_DHCP6
else
{
/* or IAID and server DUID for IPv6 */
sprintf(daemon->dhcp_buff3, "%s%u", data.flags & LEASE_TA ? "T" : "", data.hwaddr_type);
for (p = daemon->packet, i = 0; i < daemon->duid_len; i++)
{
p += sprintf(p, "%.2x", daemon->duid[i]);
if (i != daemon->duid_len - 1)
p += sprintf(p, ":");
}
}
#endif
buf += data.clid_len;
if (data.hostname_len != 0)
{
char *dot;
hostname = (char *)buf;
hostname[data.hostname_len - 1] = 0;
if (!legal_hostname(hostname))
hostname = NULL;
else if ((dot = strchr(hostname, '.')))
{
domain = dot+1;
*dot = 0;
}
}
extradata = buf + data.hostname_len;
if (!is6)
inet_ntop(AF_INET, &data.addr, daemon->addrbuff, ADDRSTRLEN);
#ifdef HAVE_DHCP6
else
inet_ntop(AF_INET6, &data.hwaddr, daemon->addrbuff, ADDRSTRLEN);
#endif
#ifdef HAVE_LUASCRIPT
if (daemon->luascript)
{
lua_getglobal(lua, "lease"); /* function to call */
lua_pushstring(lua, action_str); /* arg1 - action */
lua_newtable(lua); /* arg2 - data table */
if (is6)
{
lua_pushstring(lua, daemon->packet);
lua_setfield(lua, -2, "duid");
lua_pushstring(lua, daemon->dhcp_buff3);
lua_setfield(lua, -2, "iaid");
}
if (!is6 && data.clid_len != 0)
{
lua_pushstring(lua, daemon->packet);
lua_setfield(lua, -2, "client_id");
}
if (strlen(data.interface) != 0)
{
lua_pushstring(lua, data.interface);
lua_setfield(lua, -2, "interface");
}
#ifdef HAVE_BROKEN_RTC
lua_pushnumber(lua, data.length);
lua_setfield(lua, -2, "lease_length");
#else
lua_pushnumber(lua, data.expires);
lua_setfield(lua, -2, "lease_expires");
#endif
if (hostname)
{
lua_pushstring(lua, hostname);
lua_setfield(lua, -2, "hostname");
}
if (domain)
{
lua_pushstring(lua, domain);
lua_setfield(lua, -2, "domain");
}
end = extradata + data.ed_len;
buf = extradata;
if (!is6)
buf = grab_extradata_lua(buf, end, "vendor_class");
#ifdef HAVE_DHCP6
else
for (i = 0; i < data.hwaddr_len; i++)
{
sprintf(daemon->dhcp_buff2, "vendor_class%i", i);
buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2);
}
#endif
buf = grab_extradata_lua(buf, end, "supplied_hostname");
if (!is6)
{
buf = grab_extradata_lua(buf, end, "cpewan_oui");
buf = grab_extradata_lua(buf, end, "cpewan_serial");
buf = grab_extradata_lua(buf, end, "cpewan_class");
}
buf = grab_extradata_lua(buf, end, "tags");
for (i = 0; buf; i++)
{
sprintf(daemon->dhcp_buff2, "user_class%i", i);
buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2);
}
if (!is6 && data.giaddr.s_addr != 0)
{
lua_pushstring(lua, inet_ntoa(data.giaddr));
lua_setfield(lua, -2, "relay_address");
}
if (data.action != ACTION_DEL && data.remaining_time != 0)
{
lua_pushnumber(lua, data.remaining_time);
lua_setfield(lua, -2, "time_remaining");
}
if (data.action == ACTION_OLD_HOSTNAME && hostname)
{
lua_pushstring(lua, hostname);
lua_setfield(lua, -2, "old_hostname");
}
if (!is6)
{
lua_pushstring(lua, daemon->dhcp_buff);
lua_setfield(lua, -2, "mac_address");
}
lua_pushstring(lua, daemon->addrbuff);
lua_setfield(lua, -2, "ip_address");
lua_call(lua, 2, 0); /* pass 2 values, expect 0 */
}
#endif
/* no script, just lua */
if (!daemon->lease_change_command)
continue;
/* possible fork errors are all temporary resource problems */ /* possible fork errors are all temporary resource problems */
while ((pid = fork()) == -1 && (errno == EAGAIN || errno == ENOMEM)) while ((pid = fork()) == -1 && (errno == EAGAIN || errno == ENOMEM))
sleep(2); sleep(2);
free(alloc_buff);
if (pid == -1) if (pid == -1)
continue; continue;
/* wait for child to complete */ /* wait for child to complete */
if (pid != 0) if (pid != 0)
{ {
@@ -188,9 +426,9 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
{ {
/* On error send event back to main process for logging */ /* On error send event back to main process for logging */
if (WIFSIGNALED(status)) if (WIFSIGNALED(status))
send_event(event_fd, EVENT_KILLED, WTERMSIG(status)); send_event(event_fd, EVENT_KILLED, WTERMSIG(status), NULL);
else if (WIFEXITED(status) && WEXITSTATUS(status) != 0) else if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
send_event(event_fd, EVENT_EXITED, WEXITSTATUS(status)); send_event(event_fd, EVENT_EXITED, WEXITSTATUS(status), NULL);
break; break;
} }
@@ -201,7 +439,13 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
continue; continue;
} }
if (data.clid_len != 0) if (is6)
{
my_setenv("DNSMASQ_IAID", daemon->dhcp_buff3, &err);
my_setenv("DNSMASQ_DUID", daemon->packet, &err);
}
if (!is6 && data.clid_len != 0)
my_setenv("DNSMASQ_CLIENT_ID", daemon->packet, &err); my_setenv("DNSMASQ_CLIENT_ID", daemon->packet, &err);
if (strlen(data.interface) != 0) if (strlen(data.interface) != 0)
@@ -213,28 +457,42 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
my_setenv("DNSMASQ_LEASE_EXPIRES", daemon->dhcp_buff2, &err); my_setenv("DNSMASQ_LEASE_EXPIRES", daemon->dhcp_buff2, &err);
#endif #endif
if (data.hostname_len != 0) if (domain)
my_setenv("DNSMASQ_DOMAIN", domain, &err);
end = extradata + data.ed_len;
buf = extradata;
if (!is6)
buf = grab_extradata(buf, end, "DNSMASQ_VENDOR_CLASS", &err);
#ifdef HAVE_DHCP6
else
{ {
char *dot; if (data.hwaddr_len != 0)
hostname = (char *)buf;
hostname[data.hostname_len - 1] = 0;
if (!legal_hostname(hostname))
hostname = NULL;
else if ((dot = strchr(hostname, '.')))
{ {
my_setenv("DNSMASQ_DOMAIN", dot+1, &err); buf = grab_extradata(buf, end, "DNSMASQ_VENDOR_CLASS_ID", &err);
*dot = 0; for (i = 0; i < data.hwaddr_len - 1; i++)
} {
buf += data.hostname_len; sprintf(daemon->dhcp_buff2, "DNSMASQ_VENDOR_CLASS%i", i);
buf = grab_extradata(buf, end, daemon->dhcp_buff2, &err);
}
}
} }
#endif
end = buf + data.ed_len;
buf = grab_extradata(buf, end, "DNSMASQ_VENDOR_CLASS", &err);
buf = grab_extradata(buf, end, "DNSMASQ_SUPPLIED_HOSTNAME", &err); buf = grab_extradata(buf, end, "DNSMASQ_SUPPLIED_HOSTNAME", &err);
buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_OUI", &err);
buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_SERIAL", &err); if (!is6)
buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_CLASS", &err); {
buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_OUI", &err);
buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_SERIAL", &err);
buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_CLASS", &err);
}
buf = grab_extradata(buf, end, "DNSMASQ_TAGS", &err); buf = grab_extradata(buf, end, "DNSMASQ_TAGS", &err);
if (is6)
buf = grab_extradata(buf, end, "DNSMASQ_RELAY_ADDRESS", &err);
for (i = 0; buf; i++) for (i = 0; buf; i++)
{ {
@@ -242,10 +500,10 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
buf = grab_extradata(buf, end, daemon->dhcp_buff2, &err); buf = grab_extradata(buf, end, daemon->dhcp_buff2, &err);
} }
if (data.giaddr.s_addr != 0) if (!is6 && data.giaddr.s_addr != 0)
my_setenv("DNSMASQ_RELAY_ADDRESS", inet_ntoa(data.giaddr), &err); my_setenv("DNSMASQ_RELAY_ADDRESS", inet_ntoa(data.giaddr), &err);
if (data.action != ACTION_DEL) if (data.action != ACTION_DEL && data.remaining_time != 0)
{ {
sprintf(daemon->dhcp_buff2, "%u", data.remaining_time); sprintf(daemon->dhcp_buff2, "%u", data.remaining_time);
my_setenv("DNSMASQ_TIME_REMAINING", daemon->dhcp_buff2, &err); my_setenv("DNSMASQ_TIME_REMAINING", daemon->dhcp_buff2, &err);
@@ -267,11 +525,11 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
{ {
execl(daemon->lease_change_command, execl(daemon->lease_change_command,
p ? p+1 : daemon->lease_change_command, p ? p+1 : daemon->lease_change_command,
action_str, daemon->dhcp_buff, inet_ntoa(data.addr), hostname, (char*)NULL); action_str, daemon->dhcp_buff, daemon->addrbuff, hostname, (char*)NULL);
err = errno; err = errno;
} }
/* failed, send event so the main process logs the problem */ /* failed, send event so the main process logs the problem */
send_event(event_fd, EVENT_EXEC_ERR, err); send_event(event_fd, EVENT_EXEC_ERR, err, NULL);
_exit(0); _exit(0);
} }
} }
@@ -305,13 +563,41 @@ static unsigned char *grab_extradata(unsigned char *buf, unsigned char *end, ch
return next + 1; return next + 1;
} }
#ifdef HAVE_LUASCRIPT
static unsigned char *grab_extradata_lua(unsigned char *buf, unsigned char *end, char *field)
{
unsigned char *next;
if (!buf || (buf == end))
return NULL;
for (next = buf; *next != 0; next++)
if (next == end)
return NULL;
if (next != buf)
{
lua_pushstring(lua, (char *)buf);
lua_setfield(lua, -2, field);
}
return next + 1;
}
#endif
/* pack up lease data into a buffer */ /* pack up lease data into a buffer */
void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t now) void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t now)
{ {
unsigned char *p; unsigned char *p;
size_t size; size_t size;
unsigned int hostname_len = 0, clid_len = 0, ed_len = 0; unsigned int hostname_len = 0, clid_len = 0, ed_len = 0;
int fd = daemon->dhcpfd;
#ifdef HAVE_DHCP6
if (!daemon->dhcp)
fd = daemon->dhcp6fd;
#endif
/* no script */ /* no script */
if (daemon->helperfd == -1) if (daemon->helperfd == -1)
return; return;
@@ -342,6 +628,7 @@ void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t n
} }
buf->action = action; buf->action = action;
buf->flags = lease->flags;
buf->hwaddr_len = lease->hwaddr_len; buf->hwaddr_len = lease->hwaddr_len;
buf->hwaddr_type = lease->hwaddr_type; buf->hwaddr_type = lease->hwaddr_type;
buf->clid_len = clid_len; buf->clid_len = clid_len;
@@ -349,8 +636,8 @@ void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t n
buf->hostname_len = hostname_len; buf->hostname_len = hostname_len;
buf->addr = lease->addr; buf->addr = lease->addr;
buf->giaddr = lease->giaddr; buf->giaddr = lease->giaddr;
memcpy(buf->hwaddr, lease->hwaddr, lease->hwaddr_len); memcpy(buf->hwaddr, lease->hwaddr, DHCP_CHADDR_MAX);
if (!indextoname(daemon->dhcpfd, lease->last_interface, buf->interface)) if (!indextoname(fd, lease->last_interface, buf->interface))
buf->interface[0] = 0; buf->interface[0] = 0;
#ifdef HAVE_BROKEN_RTC #ifdef HAVE_BROKEN_RTC
@@ -358,7 +645,11 @@ void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t n
#else #else
buf->expires = lease->expires; buf->expires = lease->expires;
#endif #endif
buf->remaining_time = (unsigned int)difftime(lease->expires, now);
if (lease->expires != 0)
buf->remaining_time = (unsigned int)difftime(lease->expires, now);
else
buf->remaining_time = 0;
p = (unsigned char *)(buf+1); p = (unsigned char *)(buf+1);
if (clid_len != 0) if (clid_len != 0)
@@ -408,3 +699,4 @@ void helper_write(void)
#endif #endif
+376 -73
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2011 Simon Kelley /* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -24,19 +24,13 @@ static int dns_dirty, file_dirty, leases_left;
void lease_init(time_t now) void lease_init(time_t now)
{ {
unsigned long ei; unsigned long ei;
struct in_addr addr; struct all_addr addr;
struct dhcp_lease *lease; struct dhcp_lease *lease;
int clid_len, hw_len, hw_type; int clid_len, hw_len, hw_type;
FILE *leasestream; FILE *leasestream;
/* These each hold a DHCP option max size 255
and get a terminating zero added */
daemon->dhcp_buff = safe_malloc(256);
daemon->dhcp_buff2 = safe_malloc(256);
daemon->dhcp_buff3 = safe_malloc(256);
leases_left = daemon->dhcp_max; leases_left = daemon->dhcp_max;
if (option_bool(OPT_LEASE_RO)) if (option_bool(OPT_LEASE_RO))
{ {
/* run "<lease_change_script> init" once to get the /* run "<lease_change_script> init" once to get the
@@ -73,23 +67,68 @@ void lease_init(time_t now)
/* client-id max length is 255 which is 255*2 digits + 254 colons /* client-id max length is 255 which is 255*2 digits + 254 colons
borrow DNS packet buffer which is always larger than 1000 bytes */ borrow DNS packet buffer which is always larger than 1000 bytes */
if (leasestream) if (leasestream)
while (fscanf(leasestream, "%lu %255s %16s %255s %764s", while (fscanf(leasestream, "%255s %255s", daemon->dhcp_buff3, daemon->dhcp_buff2) == 2)
&ei, daemon->dhcp_buff2, daemon->namebuff,
daemon->dhcp_buff, daemon->packet) == 5)
{ {
hw_len = parse_hex(daemon->dhcp_buff2, (unsigned char *)daemon->dhcp_buff2, DHCP_CHADDR_MAX, NULL, &hw_type); #ifdef HAVE_DHCP6
/* For backwards compatibility, no explict MAC address type means ether. */ if (strcmp(daemon->dhcp_buff3, "duid") == 0)
if (hw_type == 0 && hw_len != 0) {
hw_type = ARPHRD_ETHER; daemon->duid_len = parse_hex(daemon->dhcp_buff2, (unsigned char *)daemon->dhcp_buff2, 130, NULL, NULL);
daemon->duid = safe_malloc(daemon->duid_len);
memcpy(daemon->duid, daemon->dhcp_buff2, daemon->duid_len);
continue;
}
#endif
ei = atol(daemon->dhcp_buff3);
addr.s_addr = inet_addr(daemon->namebuff); if (fscanf(leasestream, " %64s %255s %764s",
daemon->namebuff, daemon->dhcp_buff, daemon->packet) != 3)
break;
/* decode hex in place */
clid_len = 0; clid_len = 0;
if (strcmp(daemon->packet, "*") != 0) if (strcmp(daemon->packet, "*") != 0)
clid_len = parse_hex(daemon->packet, (unsigned char *)daemon->packet, 255, NULL, NULL); clid_len = parse_hex(daemon->packet, (unsigned char *)daemon->packet, 255, NULL, NULL);
if (!(lease = lease_allocate(addr))) if (inet_pton(AF_INET, daemon->namebuff, &addr.addr.addr4) &&
(lease = lease4_allocate(addr.addr.addr4)))
{
hw_len = parse_hex(daemon->dhcp_buff2, (unsigned char *)daemon->dhcp_buff2, DHCP_CHADDR_MAX, NULL, &hw_type);
/* For backwards compatibility, no explict MAC address type means ether. */
if (hw_type == 0 && hw_len != 0)
hw_type = ARPHRD_ETHER;
lease_set_hwaddr(lease, (unsigned char *)daemon->dhcp_buff2, (unsigned char *)daemon->packet, hw_len, hw_type, clid_len);
if (strcmp(daemon->dhcp_buff, "*") != 0)
lease_set_hostname(lease, daemon->dhcp_buff, 0, get_domain(lease->addr), NULL);
}
#ifdef HAVE_DHCP6
else if (inet_pton(AF_INET6, daemon->namebuff, &addr.addr.addr6))
{
char *s = daemon->dhcp_buff2;
int lease_type = LEASE_NA;
if (s[0] == 'T')
{
lease_type = LEASE_TA;
s++;
}
hw_type = atoi(s);
if ((lease = lease6_allocate(&addr.addr.addr6, lease_type)))
{
lease_set_hwaddr(lease, NULL, (unsigned char *)daemon->packet, 0, hw_type, clid_len);
if (strcmp(daemon->dhcp_buff, "*") != 0)
lease_set_hostname(lease, daemon->dhcp_buff, 0, get_domain6((struct in6_addr *)lease->hwaddr), NULL);
}
}
#endif
else
break;
if (!lease)
die (_("too many stored leases"), NULL, EC_MISC); die (_("too many stored leases"), NULL, EC_MISC);
#ifdef HAVE_BROKEN_RTC #ifdef HAVE_BROKEN_RTC
@@ -104,14 +143,9 @@ void lease_init(time_t now)
lease->expires = (time_t)ei; lease->expires = (time_t)ei;
#endif #endif
lease_set_hwaddr(lease, (unsigned char *)daemon->dhcp_buff2, (unsigned char *)daemon->packet, hw_len, hw_type, clid_len);
if (strcmp(daemon->dhcp_buff, "*") != 0)
lease_set_hostname(lease, daemon->dhcp_buff, 0);
/* set these correctly: the "old" events are generated later from /* set these correctly: the "old" events are generated later from
the startup synthesised SIGHUP. */ the startup synthesised SIGHUP. */
lease->new = lease->changed = 0; lease->flags &= ~(LEASE_NEW | LEASE_CHANGED);
} }
#ifdef HAVE_SCRIPT #ifdef HAVE_SCRIPT
@@ -141,6 +175,16 @@ void lease_init(time_t now)
file_dirty = 0; file_dirty = 0;
lease_prune(NULL, now); lease_prune(NULL, now);
dns_dirty = 1; dns_dirty = 1;
#ifdef HAVE_DHCP6
/* If we're not doing DHCPv6, and there are not v6 leases, don't add the DUID to the database */
if (!daemon->duid && daemon->dhcp6)
{
file_dirty = 1;
make_duid(now);
}
#endif
} }
void lease_update_from_configs(void) void lease_update_from_configs(void)
@@ -150,17 +194,17 @@ void lease_update_from_configs(void)
struct dhcp_lease *lease; struct dhcp_lease *lease;
struct dhcp_config *config; struct dhcp_config *config;
char *name; char *name;
for (lease = leases; lease; lease = lease->next) for (lease = leases; lease; lease = lease->next)
if ((config = find_config(daemon->dhcp_conf, NULL, lease->clid, lease->clid_len, if ((config = find_config(daemon->dhcp_conf, NULL, lease->clid, lease->clid_len,
lease->hwaddr, lease->hwaddr_len, lease->hwaddr_type, NULL)) && lease->hwaddr, lease->hwaddr_len, lease->hwaddr_type, NULL)) &&
(config->flags & CONFIG_NAME) && (config->flags & CONFIG_NAME) &&
(!(config->flags & CONFIG_ADDR) || config->addr.s_addr == lease->addr.s_addr)) (!(config->flags & CONFIG_ADDR) || config->addr.s_addr == lease->addr.s_addr))
lease_set_hostname(lease, config->hostname, 1); lease_set_hostname(lease, config->hostname, 1, get_domain(lease->addr), NULL);
else if ((name = host_from_dns(lease->addr))) else if ((name = host_from_dns(lease->addr)))
lease_set_hostname(lease, name, 1); /* updates auth flag only */ lease_set_hostname(lease, name, 1, get_domain(lease->addr), NULL); /* updates auth flag only */
} }
static void ourprintf(int *errp, char *format, ...) static void ourprintf(int *errp, char *format, ...)
{ {
va_list ap; va_list ap;
@@ -186,11 +230,18 @@ void lease_update_file(time_t now)
for (lease = leases; lease; lease = lease->next) for (lease = leases; lease; lease = lease->next)
{ {
#ifdef HAVE_DHCP6
if (lease->flags & (LEASE_TA | LEASE_NA))
continue;
#endif
#ifdef HAVE_BROKEN_RTC #ifdef HAVE_BROKEN_RTC
ourprintf(&err, "%u ", lease->length); ourprintf(&err, "%u ", lease->length);
#else #else
ourprintf(&err, "%lu ", (unsigned long)lease->expires); ourprintf(&err, "%lu ", (unsigned long)lease->expires);
#endif #endif
if (lease->hwaddr_type != ARPHRD_ETHER || lease->hwaddr_len == 0) if (lease->hwaddr_type != ARPHRD_ETHER || lease->hwaddr_len == 0)
ourprintf(&err, "%.2x-", lease->hwaddr_type); ourprintf(&err, "%.2x-", lease->hwaddr_type);
for (i = 0; i < lease->hwaddr_len; i++) for (i = 0; i < lease->hwaddr_len; i++)
@@ -199,8 +250,10 @@ void lease_update_file(time_t now)
if (i != lease->hwaddr_len - 1) if (i != lease->hwaddr_len - 1)
ourprintf(&err, ":"); ourprintf(&err, ":");
} }
inet_ntop(AF_INET, &lease->addr, daemon->addrbuff, ADDRSTRLEN);
ourprintf(&err, " %s ", inet_ntoa(lease->addr)); ourprintf(&err, " %s ", daemon->addrbuff);
ourprintf(&err, "%s ", lease->hostname ? lease->hostname : "*"); ourprintf(&err, "%s ", lease->hostname ? lease->hostname : "*");
if (lease->clid && lease->clid_len != 0) if (lease->clid && lease->clid_len != 0)
@@ -213,6 +266,44 @@ void lease_update_file(time_t now)
ourprintf(&err, "*\n"); ourprintf(&err, "*\n");
} }
#ifdef HAVE_DHCP6
if (daemon->duid)
{
ourprintf(&err, "duid ");
for (i = 0; i < daemon->duid_len - 1; i++)
ourprintf(&err, "%.2x:", daemon->duid[i]);
ourprintf(&err, "%.2x\n", daemon->duid[i]);
for (lease = leases; lease; lease = lease->next)
{
if (!(lease->flags & (LEASE_TA | LEASE_NA)))
continue;
#ifdef HAVE_BROKEN_RTC
ourprintf(&err, "%u ", lease->length);
#else
ourprintf(&err, "%lu ", (unsigned long)lease->expires);
#endif
inet_ntop(AF_INET6, lease->hwaddr, daemon->addrbuff, ADDRSTRLEN);
ourprintf(&err, "%s%u %s ", (lease->flags & LEASE_TA) ? "T" : "",
lease->hwaddr_type, daemon->addrbuff);
ourprintf(&err, "%s ", lease->hostname ? lease->hostname : "*");
if (lease->clid && lease->clid_len != 0)
{
for (i = 0; i < lease->clid_len - 1; i++)
ourprintf(&err, "%.2x:", lease->clid[i]);
ourprintf(&err, "%.2x\n", lease->clid[i]);
}
else
ourprintf(&err, "*\n");
}
}
#endif
if (fflush(daemon->lease_stream) != 0 || if (fflush(daemon->lease_stream) != 0 ||
fsync(fileno(daemon->lease_stream)) < 0) fsync(fileno(daemon->lease_stream)) < 0)
err = errno; err = errno;
@@ -251,11 +342,21 @@ void lease_update_dns(void)
for (lease = leases; lease; lease = lease->next) for (lease = leases; lease; lease = lease->next)
{ {
int prot = AF_INET;
#ifdef HAVE_DHCP6
if (lease->flags & (LEASE_TA | LEASE_NA))
prot = AF_INET6;
#endif
if (lease->fqdn) if (lease->fqdn)
cache_add_dhcp_entry(lease->fqdn, &lease->addr, lease->expires); cache_add_dhcp_entry(lease->fqdn, prot,
prot == AF_INET ? (struct all_addr *)&lease->addr : (struct all_addr *)&lease->hwaddr,
lease->expires);
if (!option_bool(OPT_DHCP_FQDN) && lease->hostname) if (!option_bool(OPT_DHCP_FQDN) && lease->hostname)
cache_add_dhcp_entry(lease->hostname, &lease->addr, lease->expires); cache_add_dhcp_entry(lease->hostname, prot,
prot == AF_INET ? (struct all_addr *)&lease->addr : (struct all_addr *)&lease->hwaddr,
lease->expires);
} }
dns_dirty = 0; dns_dirty = 0;
@@ -275,7 +376,7 @@ void lease_prune(struct dhcp_lease *target, time_t now)
if (lease->hostname) if (lease->hostname)
dns_dirty = 1; dns_dirty = 1;
*up = lease->next; /* unlink */ *up = lease->next; /* unlink */
/* Put on old_leases list 'till we /* Put on old_leases list 'till we
can run the script */ can run the script */
@@ -297,18 +398,30 @@ struct dhcp_lease *lease_find_by_client(unsigned char *hwaddr, int hw_len, int h
if (clid) if (clid)
for (lease = leases; lease; lease = lease->next) for (lease = leases; lease; lease = lease->next)
if (lease->clid && clid_len == lease->clid_len && {
memcmp(clid, lease->clid, clid_len) == 0) #ifdef HAVE_DHCP6
return lease; if (lease->flags & (LEASE_TA | LEASE_NA))
continue;
#endif
if (lease->clid && clid_len == lease->clid_len &&
memcmp(clid, lease->clid, clid_len) == 0)
return lease;
}
for (lease = leases; lease; lease = lease->next) for (lease = leases; lease; lease = lease->next)
if ((!lease->clid || !clid) && {
hw_len != 0 && #ifdef HAVE_DHCP6
lease->hwaddr_len == hw_len && if (lease->flags & (LEASE_TA | LEASE_NA))
lease->hwaddr_type == hw_type && continue;
memcmp(hwaddr, lease->hwaddr, hw_len) == 0) #endif
return lease; if ((!lease->clid || !clid) &&
hw_len != 0 &&
lease->hwaddr_len == hw_len &&
lease->hwaddr_type == hw_type &&
memcmp(hwaddr, lease->hwaddr, hw_len) == 0)
return lease;
}
return NULL; return NULL;
} }
@@ -317,23 +430,126 @@ struct dhcp_lease *lease_find_by_addr(struct in_addr addr)
struct dhcp_lease *lease; struct dhcp_lease *lease;
for (lease = leases; lease; lease = lease->next) for (lease = leases; lease; lease = lease->next)
if (lease->addr.s_addr == addr.s_addr) {
return lease; #ifdef HAVE_DHCP6
if (lease->flags & (LEASE_TA | LEASE_NA))
continue;
#endif
if (lease->addr.s_addr == addr.s_addr)
return lease;
}
return NULL;
}
#ifdef HAVE_DHCP6
/* addr or clid may be NULL for "don't care, both NULL resets "USED" flags both
set activates USED check */
struct dhcp_lease *lease6_find(unsigned char *clid, int clid_len,
int lease_type, int iaid, struct in6_addr *addr)
{
struct dhcp_lease *lease;
for (lease = leases; lease; lease = lease->next)
{
if (!(lease->flags & lease_type) || lease->hwaddr_type != iaid)
continue;
if (clid && addr && (lease->flags & LEASE_USED))
continue;
if (addr && memcmp(lease->hwaddr, addr, IN6ADDRSZ) != 0)
continue;
if (clid &&
(clid_len != lease->clid_len ||
memcmp(clid, lease->clid, clid_len) != 0))
continue;
if (clid || addr)
{
lease->flags |= LEASE_USED;
return lease;
}
else
lease->flags &= ~LEASE_USED;
}
return NULL; return NULL;
} }
struct dhcp_lease *lease6_find_by_addr(struct in6_addr *net, int prefix, u64 addr)
{
struct dhcp_lease *lease;
for (lease = leases; lease; lease = lease->next)
{
if (!(lease->flags & (LEASE_TA | LEASE_NA)))
continue;
if (is_same_net6((struct in6_addr *)lease->hwaddr, net, prefix) &&
(prefix == 128 || addr6part((struct in6_addr *)lease->hwaddr) == addr))
return lease;
}
return NULL;
}
struct dhcp_lease *lease_allocate(struct in_addr addr) /* Find largest assigned address in context */
u64 lease_find_max_addr6(struct dhcp_context *context)
{
struct dhcp_lease *lease;
u64 addr = addr6part(&context->start6);
if (!(context->flags & (CONTEXT_STATIC | CONTEXT_PROXY)))
for (lease = leases; lease; lease = lease->next)
{
#ifdef HAVE_DHCP6
if (!(lease->flags & (LEASE_TA | LEASE_NA)))
continue;
#endif
if (is_same_net6((struct in6_addr *)lease->hwaddr, &context->start6, 64) &&
addr6part((struct in6_addr *)lease->hwaddr) > addr6part(&context->start6) &&
addr6part((struct in6_addr *)lease->hwaddr) <= addr6part(&context->end6) &&
addr6part((struct in6_addr *)lease->hwaddr) > addr)
addr = addr6part((struct in6_addr *)lease->hwaddr);
}
return addr;
}
#endif
/* Find largest assigned address in context */
struct in_addr lease_find_max_addr(struct dhcp_context *context)
{
struct dhcp_lease *lease;
struct in_addr addr = context->start;
if (!(context->flags & (CONTEXT_STATIC | CONTEXT_PROXY)))
for (lease = leases; lease; lease = lease->next)
{
#ifdef HAVE_DHCP6
if (lease->flags & (LEASE_TA | LEASE_NA))
continue;
#endif
if (((unsigned)ntohl(lease->addr.s_addr)) > ((unsigned)ntohl(context->start.s_addr)) &&
((unsigned)ntohl(lease->addr.s_addr)) <= ((unsigned)ntohl(context->end.s_addr)) &&
((unsigned)ntohl(lease->addr.s_addr)) > ((unsigned)ntohl(addr.s_addr)))
addr = lease->addr;
}
return addr;
}
static struct dhcp_lease *lease_allocate(void)
{ {
struct dhcp_lease *lease; struct dhcp_lease *lease;
if (!leases_left || !(lease = whine_malloc(sizeof(struct dhcp_lease)))) if (!leases_left || !(lease = whine_malloc(sizeof(struct dhcp_lease))))
return NULL; return NULL;
memset(lease, 0, sizeof(struct dhcp_lease)); memset(lease, 0, sizeof(struct dhcp_lease));
lease->new = 1; lease->flags = LEASE_NEW;
lease->addr = addr;
lease->hwaddr_len = 256; /* illegal value */
lease->expires = 1; lease->expires = 1;
#ifdef HAVE_BROKEN_RTC #ifdef HAVE_BROKEN_RTC
lease->length = 0xffffffff; /* illegal value */ lease->length = 0xffffffff; /* illegal value */
@@ -347,6 +563,26 @@ struct dhcp_lease *lease_allocate(struct in_addr addr)
return lease; return lease;
} }
struct dhcp_lease *lease4_allocate(struct in_addr addr)
{
struct dhcp_lease *lease = lease_allocate();
lease->addr = addr;
lease->hwaddr_len = 256; /* illegal value */
return lease;
}
#ifdef HAVE_DHCP6
struct dhcp_lease *lease6_allocate(struct in6_addr *addrp, int lease_type)
{
struct dhcp_lease *lease = lease_allocate();
memcpy(lease->hwaddr, addrp, sizeof(*addrp)) ;
lease->flags |= lease_type;
return lease;
}
#endif
void lease_set_expires(struct dhcp_lease *lease, unsigned int len, time_t now) void lease_set_expires(struct dhcp_lease *lease, unsigned int len, time_t now)
{ {
time_t exp = now + (time_t)len; time_t exp = now + (time_t)len;
@@ -362,7 +598,8 @@ void lease_set_expires(struct dhcp_lease *lease, unsigned int len, time_t now)
dns_dirty = 1; dns_dirty = 1;
lease->expires = exp; lease->expires = exp;
#ifndef HAVE_BROKEN_RTC #ifndef HAVE_BROKEN_RTC
lease->aux_changed = file_dirty = 1; lease->flags |= LEASE_AUX_CHANGED;
file_dirty = 1;
#endif #endif
} }
@@ -382,10 +619,12 @@ void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr,
hw_type != lease->hwaddr_type || hw_type != lease->hwaddr_type ||
(hw_len != 0 && memcmp(lease->hwaddr, hwaddr, hw_len) != 0)) (hw_len != 0 && memcmp(lease->hwaddr, hwaddr, hw_len) != 0))
{ {
memcpy(lease->hwaddr, hwaddr, hw_len); if (hw_len != 0)
memcpy(lease->hwaddr, hwaddr, hw_len);
lease->hwaddr_len = hw_len; lease->hwaddr_len = hw_len;
lease->hwaddr_type = hw_type; lease->hwaddr_type = hw_type;
lease->changed = file_dirty = 1; /* run script on change */ lease->flags |= LEASE_CHANGED;
file_dirty = 1; /* run script on change */
} }
/* only update clid when one is available, stops packets /* only update clid when one is available, stops packets
@@ -398,14 +637,18 @@ void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr,
if (lease->clid_len != clid_len) if (lease->clid_len != clid_len)
{ {
lease->aux_changed = file_dirty = 1; lease->flags |= LEASE_AUX_CHANGED;
file_dirty = 1;
free(lease->clid); free(lease->clid);
if (!(lease->clid = whine_malloc(clid_len))) if (!(lease->clid = whine_malloc(clid_len)))
return; return;
} }
else if (memcmp(lease->clid, clid, clid_len) != 0) else if (memcmp(lease->clid, clid, clid_len) != 0)
lease->aux_changed = file_dirty = 1; {
lease->flags |= LEASE_AUX_CHANGED;
file_dirty = 1;
}
lease->clid_len = clid_len; lease->clid_len = clid_len;
memcpy(lease->clid, clid, clid_len); memcpy(lease->clid, clid, clid_len);
} }
@@ -421,7 +664,7 @@ static void kill_name(struct dhcp_lease *lease)
free(lease->old_hostname); free(lease->old_hostname);
/* If we know the fqdn, pass that. The helper will derive the /* If we know the fqdn, pass that. The helper will derive the
unqualified name from it, free the unqulaified name here. */ unqualified name from it, free the unqualified name here. */
if (lease->fqdn) if (lease->fqdn)
{ {
@@ -434,14 +677,18 @@ static void kill_name(struct dhcp_lease *lease)
lease->hostname = lease->fqdn = NULL; lease->hostname = lease->fqdn = NULL;
} }
void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth) void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth, char *domain, char *config_domain)
{ {
struct dhcp_lease *lease_tmp; struct dhcp_lease *lease_tmp;
char *new_name = NULL, *new_fqdn = NULL; char *new_name = NULL, *new_fqdn = NULL;
if (config_domain && (!domain || !hostname_isequal(domain, config_domain)))
my_syslog(MS_DHCP | LOG_WARNING, _("Ignoring domain %s for DHCP host name %s"), config_domain, name);
if (lease->hostname && name && hostname_isequal(lease->hostname, name)) if (lease->hostname && name && hostname_isequal(lease->hostname, name))
{ {
lease->auth_name = auth; if (auth)
lease->flags |= LEASE_AUTH_NAME;
return; return;
} }
@@ -451,19 +698,21 @@ void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth)
/* If a machine turns up on a new net without dropping the old lease, /* If a machine turns up on a new net without dropping the old lease,
or two machines claim the same name, then we end up with two interfaces with or two machines claim the same name, then we end up with two interfaces with
the same name. Check for that here and remove the name from the old lease. the same name. Check for that here and remove the name from the old lease.
Note that IPv6 leases are different. All the leases to the same DUID are
allowed the same name.
Don't allow a name from the client to override a name from dnsmasq config. */ Don't allow a name from the client to override a name from dnsmasq config. */
if (name) if (name)
{ {
if ((new_name = whine_malloc(strlen(name) + 1))) if ((new_name = whine_malloc(strlen(name) + 1)))
{ {
char *suffix = get_domain(lease->addr);
strcpy(new_name, name); strcpy(new_name, name);
if (suffix && (new_fqdn = whine_malloc(strlen(new_name) + strlen(suffix) + 2))) if (domain && (new_fqdn = whine_malloc(strlen(new_name) + strlen(domain) + 2)))
{ {
strcpy(new_fqdn, name); strcpy(new_fqdn, name);
strcat(new_fqdn, "."); strcat(new_fqdn, ".");
strcat(new_fqdn, suffix); strcat(new_fqdn, domain);
} }
} }
@@ -472,7 +721,7 @@ void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth)
{ {
if (option_bool(OPT_DHCP_FQDN)) if (option_bool(OPT_DHCP_FQDN))
{ {
if (!new_fqdn || !lease_tmp->fqdn || !hostname_isequal(lease_tmp->fqdn, new_fqdn) ) if (!new_fqdn || !lease_tmp->fqdn || !hostname_isequal(lease_tmp->fqdn, new_fqdn))
continue; continue;
} }
else else
@@ -480,8 +729,22 @@ void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth)
if (!new_name || !lease_tmp->hostname || !hostname_isequal(lease_tmp->hostname, new_name) ) if (!new_name || !lease_tmp->hostname || !hostname_isequal(lease_tmp->hostname, new_name) )
continue; continue;
} }
if (lease_tmp->auth_name && !auth) if (lease->flags & (LEASE_TA | LEASE_NA))
{
if (!(lease_tmp->flags & (LEASE_TA | LEASE_NA)))
continue;
/* another lease for the same DUID is OK for IPv6 */
if (lease->clid_len == lease_tmp->clid_len &&
lease->clid && lease_tmp->clid &&
memcmp(lease->clid, lease_tmp->clid, lease->clid_len) == 0)
continue;
}
else if (lease_tmp->flags & (LEASE_TA | LEASE_NA))
continue;
if ((lease_tmp->flags & LEASE_AUTH_NAME) && !auth)
{ {
free(new_name); free(new_name);
free(new_fqdn); free(new_fqdn);
@@ -498,11 +761,13 @@ void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth)
lease->hostname = new_name; lease->hostname = new_name;
lease->fqdn = new_fqdn; lease->fqdn = new_fqdn;
lease->auth_name = auth;
if (auth)
lease->flags |= LEASE_AUTH_NAME;
file_dirty = 1; file_dirty = 1;
dns_dirty = 1; dns_dirty = 1;
lease->changed = 1; /* run script on change */ lease->flags |= LEASE_CHANGED; /* run script on change */
} }
void lease_set_interface(struct dhcp_lease *lease, int interface) void lease_set_interface(struct dhcp_lease *lease, int interface)
@@ -511,7 +776,7 @@ void lease_set_interface(struct dhcp_lease *lease, int interface)
return; return;
lease->last_interface = interface; lease->last_interface = interface;
lease->changed = 1; lease->flags |= LEASE_CHANGED;
} }
void rerun_scripts(void) void rerun_scripts(void)
@@ -519,7 +784,7 @@ void rerun_scripts(void)
struct dhcp_lease *lease; struct dhcp_lease *lease;
for (lease = leases; lease; lease = lease->next) for (lease = leases; lease; lease = lease->next)
lease->changed = 1; lease->flags |= LEASE_CHANGED;
} }
/* deleted leases get transferred to the old_leases list. /* deleted leases get transferred to the old_leases list.
@@ -585,18 +850,18 @@ int do_script_run(time_t now)
} }
for (lease = leases; lease; lease = lease->next) for (lease = leases; lease; lease = lease->next)
if (lease->new || lease->changed || if ((lease->flags & (LEASE_NEW | LEASE_CHANGED)) ||
(lease->aux_changed && option_bool(OPT_LEASE_RO))) ((lease->flags & LEASE_AUX_CHANGED) && option_bool(OPT_LEASE_RO)))
{ {
#ifdef HAVE_SCRIPT #ifdef HAVE_SCRIPT
queue_script(lease->new ? ACTION_ADD : ACTION_OLD, lease, queue_script((lease->flags & LEASE_NEW) ? ACTION_ADD : ACTION_OLD, lease,
lease->fqdn ? lease->fqdn : lease->hostname, now); lease->fqdn ? lease->fqdn : lease->hostname, now);
#endif #endif
#ifdef HAVE_DBUS #ifdef HAVE_DBUS
emit_dbus_signal(lease->new ? ACTION_ADD : ACTION_OLD, lease, emit_dbus_signal((lease->flags & LEASE_NEW) ? ACTION_ADD : ACTION_OLD, lease,
lease->fqdn ? lease->fqdn : lease->hostname); lease->fqdn ? lease->fqdn : lease->hostname);
#endif #endif
lease->new = lease->changed = lease->aux_changed = 0; lease->flags &= ~(LEASE_NEW | LEASE_CHANGED | LEASE_AUX_CHANGED);
/* this is used for the "add" call, then junked, since they're not in the database */ /* this is used for the "add" call, then junked, since they're not in the database */
free(lease->extradata); free(lease->extradata);
@@ -608,6 +873,44 @@ int do_script_run(time_t now)
return 0; /* nothing to do */ return 0; /* nothing to do */
} }
#ifdef HAVE_SCRIPT
void lease_add_extradata(struct dhcp_lease *lease, unsigned char *data, unsigned int len, int delim)
{
unsigned int i;
/* check for embeded NULLs */
for (i = 0; i < len; i++)
if (data[i] == 0)
{
len = i;
break;
}
if ((lease->extradata_size - lease->extradata_len) < (len + 1))
{
size_t newsz = lease->extradata_len + len + 100;
unsigned char *new = whine_malloc(newsz);
if (!new)
return;
if (lease->extradata)
{
memcpy(new, lease->extradata, lease->extradata_len);
free(lease->extradata);
}
lease->extradata = new;
lease->extradata_size = newsz;
}
if (len != 0)
memcpy(lease->extradata + lease->extradata_len, data, len);
lease->extradata[lease->extradata_len + len] = delim;
lease->extradata_len += len + 1;
}
#endif
#endif #endif
+20 -10
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2011 Simon Kelley /* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -84,7 +84,7 @@ int log_start(struct passwd *ent_pw, int errfd)
if (!log_reopen(daemon->log_file)) if (!log_reopen(daemon->log_file))
{ {
send_event(errfd, EVENT_LOG_ERR, errno); send_event(errfd, EVENT_LOG_ERR, errno, daemon->log_file ? daemon->log_file : "");
_exit(0); _exit(0);
} }
@@ -154,6 +154,19 @@ static void log_write(void)
while (entries) while (entries)
{ {
/* The data in the payoad is written with a terminating zero character
and the length reflects this. For a stream connection we need to
send the zero as a record terminator, but this isn't done for a
datagram connection, so treat the length as one less than reality
to elide the zero. If we're logging to a file, turn the zero into
a newline, and leave the length alone. */
int len_adjust = 0;
if (log_to_file)
entries->payload[entries->offset + entries->length - 1] = '\n';
else if (connection_type == SOCK_DGRAM)
len_adjust = 1;
/* Avoid duplicates over a fork() */ /* Avoid duplicates over a fork() */
if (entries->pid != getpid()) if (entries->pid != getpid())
{ {
@@ -163,11 +176,11 @@ static void log_write(void)
connection_good = 1; connection_good = 1;
if ((rc = write(log_fd, entries->payload + entries->offset, entries->length)) != -1) if ((rc = write(log_fd, entries->payload + entries->offset, entries->length - len_adjust)) != -1)
{ {
entries->length -= rc; entries->length -= rc;
entries->offset += rc; entries->offset += rc;
if (entries->length == 0) if (entries->length == len_adjust)
{ {
free_entry(); free_entry();
if (entries_lost != 0) if (entries_lost != 0)
@@ -183,7 +196,7 @@ static void log_write(void)
if (errno == EINTR) if (errno == EINTR)
continue; continue;
if (errno == EAGAIN) if (errno == EAGAIN || errno == EWOULDBLOCK)
return; /* syslogd busy, go again when select() or poll() says so */ return; /* syslogd busy, go again when select() or poll() says so */
if (errno == ENOBUFS) if (errno == ENOBUFS)
@@ -231,7 +244,8 @@ static void log_write(void)
errno == ECONNREFUSED || errno == ECONNREFUSED ||
errno == EISCONN || errno == EISCONN ||
errno == EINTR || errno == EINTR ||
errno == EAGAIN) errno == EAGAIN ||
errno == EWOULDBLOCK)
{ {
/* try again on next syslog() call */ /* try again on next syslog() call */
connection_good = 0; connection_good = 0;
@@ -366,10 +380,6 @@ void my_syslog(int priority, const char *format, ...)
entry->length = len > MAX_MESSAGE ? MAX_MESSAGE : len; entry->length = len > MAX_MESSAGE ? MAX_MESSAGE : len;
entry->offset = 0; entry->offset = 0;
entry->pid = pid; entry->pid = pid;
/* replace terminator with \n */
if (log_to_file)
entry->payload[entry->length - 1] = '\n';
} }
/* almost always, logging won't block, so try and write this now, /* almost always, logging won't block, so try and write this now,
+41 -7
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2011 Simon Kelley /* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -34,6 +34,7 @@
# define NDA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg)))) # define NDA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg))))
#endif #endif
static struct iovec iov; static struct iovec iov;
static u32 netlink_pid; static u32 netlink_pid;
@@ -127,7 +128,8 @@ static ssize_t netlink_recv(void)
} }
/* family = AF_UNSPEC finds ARP table entries. */ /* family = AF_UNSPEC finds ARP table entries.
family = AF_LOCAL finds MAC addresses. */
int iface_enumerate(int family, void *parm, int (*callback)()) int iface_enumerate(int family, void *parm, int (*callback)())
{ {
struct sockaddr_nl addr; struct sockaddr_nl addr;
@@ -144,10 +146,16 @@ int iface_enumerate(int family, void *parm, int (*callback)())
addr.nl_pad = 0; addr.nl_pad = 0;
addr.nl_groups = 0; addr.nl_groups = 0;
addr.nl_pid = 0; /* address to kernel */ addr.nl_pid = 0; /* address to kernel */
again:
if (family == AF_UNSPEC)
req.nlh.nlmsg_type = RTM_GETNEIGH;
else if (family == AF_LOCAL)
req.nlh.nlmsg_type = RTM_GETLINK;
else
req.nlh.nlmsg_type = RTM_GETADDR;
again:
req.nlh.nlmsg_len = sizeof(req); req.nlh.nlmsg_len = sizeof(req);
req.nlh.nlmsg_type = family == AF_UNSPEC ? RTM_GETNEIGH : RTM_GETADDR;
req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST | NLM_F_ACK; req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST | NLM_F_ACK;
req.nlh.nlmsg_pid = 0; req.nlh.nlmsg_pid = 0;
req.nlh.nlmsg_seq = ++seq; req.nlh.nlmsg_seq = ++seq;
@@ -179,7 +187,7 @@ int iface_enumerate(int family, void *parm, int (*callback)())
nl_err(h); nl_err(h);
else if (h->nlmsg_type == NLMSG_DONE) else if (h->nlmsg_type == NLMSG_DONE)
return 1; return 1;
else if (h->nlmsg_type == RTM_NEWADDR && family != AF_UNSPEC) else if (h->nlmsg_type == RTM_NEWADDR && family != AF_UNSPEC && family != AF_LOCAL)
{ {
struct ifaddrmsg *ifa = NLMSG_DATA(h); struct ifaddrmsg *ifa = NLMSG_DATA(h);
struct rtattr *rta = IFA_RTA(ifa); struct rtattr *rta = IFA_RTA(ifa);
@@ -222,7 +230,8 @@ int iface_enumerate(int family, void *parm, int (*callback)())
} }
if (addrp) if (addrp)
if (!((*callback)(addrp, ifa->ifa_index, ifa->ifa_index, parm))) if (!((*callback)(addrp, (int)(ifa->ifa_prefixlen), (int)(ifa->ifa_scope),
(int)(ifa->ifa_index), (int)(ifa->ifa_flags & IFA_F_TENTATIVE), parm)))
return 0; return 0;
} }
#endif #endif
@@ -252,7 +261,32 @@ int iface_enumerate(int family, void *parm, int (*callback)())
if (inaddr && mac) if (inaddr && mac)
if (!((*callback)(neigh->ndm_family, inaddr, mac, maclen, parm))) if (!((*callback)(neigh->ndm_family, inaddr, mac, maclen, parm)))
return 0; return 0;
} }
#ifdef HAVE_DHCP6
else if (h->nlmsg_type == RTM_NEWLINK && family == AF_LOCAL)
{
struct ifinfomsg *link = NLMSG_DATA(h);
struct rtattr *rta = IFLA_RTA(link);
unsigned int len1 = h->nlmsg_len - NLMSG_LENGTH(sizeof(*link));
char *mac = NULL;
size_t maclen = 0;
while (RTA_OK(rta, len1))
{
if (rta->rta_type == IFLA_ADDRESS)
{
maclen = rta->rta_len - sizeof(struct rtattr);
mac = (char *)(rta+1);
}
rta = RTA_NEXT(rta, len1);
}
if (mac && !((link->ifi_flags & (IFF_LOOPBACK | IFF_POINTOPOINT))) &&
!((*callback)((unsigned int)link->ifi_type, mac, maclen, parm)))
return 0;
}
#endif
} }
} }
+124 -132
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2011 Simon Kelley /* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -107,7 +107,7 @@ int indextoname(int fd, int index, char *name)
#endif #endif
int iface_check(int family, struct all_addr *addr, char *name, int *indexp) int iface_check(int family, struct all_addr *addr, char *name)
{ {
struct iname *tmp; struct iname *tmp;
int ret = 1; int ret = 1;
@@ -115,7 +115,7 @@ int iface_check(int family, struct all_addr *addr, char *name, int *indexp)
/* Note: have to check all and not bail out early, so that we set the /* Note: have to check all and not bail out early, so that we set the
"used" flags. */ "used" flags. */
if (daemon->if_names || (addr && daemon->if_addrs)) if (daemon->if_names || daemon->if_addrs)
{ {
#ifdef HAVE_DHCP #ifdef HAVE_DHCP
struct dhcp_context *range; struct dhcp_context *range;
@@ -134,7 +134,7 @@ int iface_check(int family, struct all_addr *addr, char *name, int *indexp)
ret = tmp->used = 1; ret = tmp->used = 1;
for (tmp = daemon->if_addrs; tmp; tmp = tmp->next) for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
if (addr && tmp->addr.sa.sa_family == family) if (tmp->addr.sa.sa_family == family)
{ {
if (family == AF_INET && if (family == AF_INET &&
tmp->addr.in.sin_addr.s_addr == addr->addr.addr4.s_addr) tmp->addr.in.sin_addr.s_addr == addr->addr.addr4.s_addr)
@@ -151,43 +151,12 @@ int iface_check(int family, struct all_addr *addr, char *name, int *indexp)
for (tmp = daemon->if_except; tmp; tmp = tmp->next) for (tmp = daemon->if_except; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, name) == 0)) if (tmp->name && (strcmp(tmp->name, name) == 0))
ret = 0; ret = 0;
if (indexp)
{
/* One form of bridging on BSD has the property that packets
can be recieved on bridge interfaces which do not have an IP address.
We allow these to be treated as aliases of another interface which does have
an IP address with --dhcp-bridge=interface,alias,alias */
struct dhcp_bridge *bridge, *alias;
for (bridge = daemon->bridges; bridge; bridge = bridge->next)
{
for (alias = bridge->alias; alias; alias = alias->next)
if (strncmp(name, alias->iface, IF_NAMESIZE) == 0)
{
int newindex;
if (!(newindex = if_nametoindex(bridge->iface)))
{
my_syslog(LOG_WARNING, _("unknown interface %s in bridge-interface"), name);
return 0;
}
else
{
*indexp = newindex;
strncpy(name, bridge->iface, IF_NAMESIZE);
break;
}
}
if (alias)
break;
}
}
return ret; return ret;
} }
static int iface_allowed(struct irec **irecp, int if_index, static int iface_allowed(struct irec **irecp, int if_index,
union mysockaddr *addr, struct in_addr netmask) union mysockaddr *addr, struct in_addr netmask, int dad)
{ {
struct irec *iface; struct irec *iface;
int fd, mtu = 0, loopback; int fd, mtu = 0, loopback;
@@ -202,8 +171,11 @@ static int iface_allowed(struct irec **irecp, int if_index,
we call this routine multiple times. */ we call this routine multiple times. */
for (iface = *irecp; iface; iface = iface->next) for (iface = *irecp; iface; iface = iface->next)
if (sockaddr_isequal(&iface->addr, addr)) if (sockaddr_isequal(&iface->addr, addr))
return 1; {
iface->dad = dad;
return 1;
}
if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1 || if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1 ||
!indextoname(fd, if_index, ifr.ifr_name) || !indextoname(fd, if_index, ifr.ifr_name) ||
ioctl(fd, SIOCGIFFLAGS, &ifr) == -1) ioctl(fd, SIOCGIFFLAGS, &ifr) == -1)
@@ -260,7 +232,7 @@ static int iface_allowed(struct irec **irecp, int if_index,
if (!ir) if (!ir)
{ {
if (addr->sa.sa_family == AF_INET && if (addr->sa.sa_family == AF_INET &&
!iface_check(AF_INET, (struct all_addr *)&addr->in.sin_addr, ifr.ifr_name, NULL)) !iface_check(AF_INET, (struct all_addr *)&addr->in.sin_addr, ifr.ifr_name))
return 1; return 1;
#ifdef HAVE_DHCP #ifdef HAVE_DHCP
@@ -271,7 +243,7 @@ static int iface_allowed(struct irec **irecp, int if_index,
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
if (addr->sa.sa_family == AF_INET6 && if (addr->sa.sa_family == AF_INET6 &&
!iface_check(AF_INET6, (struct all_addr *)&addr->in6.sin6_addr, ifr.ifr_name, NULL)) !iface_check(AF_INET6, (struct all_addr *)&addr->in6.sin6_addr, ifr.ifr_name))
return 1; return 1;
#endif #endif
} }
@@ -283,6 +255,8 @@ static int iface_allowed(struct irec **irecp, int if_index,
iface->netmask = netmask; iface->netmask = netmask;
iface->tftp_ok = tftp_ok; iface->tftp_ok = tftp_ok;
iface->mtu = mtu; iface->mtu = mtu;
iface->dad = dad;
iface->done = 0;
if ((iface->name = whine_malloc(strlen(ifr.ifr_name)+1))) if ((iface->name = whine_malloc(strlen(ifr.ifr_name)+1)))
strcpy(iface->name, ifr.ifr_name); strcpy(iface->name, ifr.ifr_name);
iface->next = *irecp; iface->next = *irecp;
@@ -295,13 +269,15 @@ static int iface_allowed(struct irec **irecp, int if_index,
} }
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
static int iface_allowed_v6(struct in6_addr *local, static int iface_allowed_v6(struct in6_addr *local, int prefix,
int scope, int if_index, void *vparam) int scope, int if_index, int dad, void *vparam)
{ {
union mysockaddr addr; union mysockaddr addr;
struct in_addr netmask; /* dummy */ struct in_addr netmask; /* dummy */
netmask.s_addr = 0; netmask.s_addr = 0;
(void)prefix; /* warning */
(void)scope; /* warning */
memset(&addr, 0, sizeof(addr)); memset(&addr, 0, sizeof(addr));
#ifdef HAVE_SOCKADDR_SA_LEN #ifdef HAVE_SOCKADDR_SA_LEN
@@ -310,9 +286,9 @@ static int iface_allowed_v6(struct in6_addr *local,
addr.in6.sin6_family = AF_INET6; addr.in6.sin6_family = AF_INET6;
addr.in6.sin6_addr = *local; addr.in6.sin6_addr = *local;
addr.in6.sin6_port = htons(daemon->port); addr.in6.sin6_port = htons(daemon->port);
addr.in6.sin6_scope_id = scope; addr.in6.sin6_scope_id = if_index;
return iface_allowed((struct irec **)vparam, if_index, &addr, netmask); return iface_allowed((struct irec **)vparam, if_index, &addr, netmask, dad);
} }
#endif #endif
@@ -330,7 +306,7 @@ static int iface_allowed_v4(struct in_addr local, int if_index,
addr.in.sin_addr = local; addr.in.sin_addr = local;
addr.in.sin_port = htons(daemon->port); addr.in.sin_port = htons(daemon->port);
return iface_allowed((struct irec **)vparam, if_index, &addr, netmask); return iface_allowed((struct irec **)vparam, if_index, &addr, netmask, 0);
} }
int enumerate_interfaces(void) int enumerate_interfaces(void)
@@ -355,13 +331,10 @@ int fix_fd(int fd)
return 1; return 1;
} }
static int make_sock(union mysockaddr *addr, int type) static int make_sock(union mysockaddr *addr, int type, int dienow)
{ {
int family = addr->sa.sa_family; int family = addr->sa.sa_family;
int fd, rc, opt = 1; int fd, rc, opt = 1;
#ifdef HAVE_IPV6
static int dad_count = 0;
#endif
if ((fd = socket(family, type, 0)) == -1) if ((fd = socket(family, type, 0)) == -1)
{ {
@@ -374,42 +347,27 @@ static int make_sock(union mysockaddr *addr, int type)
return -1; return -1;
err: err:
port = prettyprint_addr(addr, daemon->namebuff); if (dienow)
if (!option_bool(OPT_NOWILD)) {
sprintf(daemon->namebuff, "port %d", port); port = prettyprint_addr(addr, daemon->namebuff);
die(_("failed to create listening socket for %s: %s"), if (!option_bool(OPT_NOWILD))
daemon->namebuff, EC_BADNET); sprintf(daemon->namebuff, "port %d", port);
die(_("failed to create listening socket for %s: %s"),
daemon->namebuff, EC_BADNET);
}
return -1;
} }
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 || !fix_fd(fd)) if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 || !fix_fd(fd))
goto err; goto err;
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
if (family == AF_INET6 && setsockopt(fd, IPV6_LEVEL, IPV6_V6ONLY, &opt, sizeof(opt)) == -1) if (family == AF_INET6 && setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)) == -1)
goto err; goto err;
#endif #endif
while (1) if ((rc = bind(fd, (struct sockaddr *)addr, sa_len(addr))) == -1)
{
if ((rc = bind(fd, (struct sockaddr *)addr, sa_len(addr))) != -1)
break;
#ifdef HAVE_IPV6
/* An interface may have an IPv6 address which is still undergoing DAD.
If so, the bind will fail until the DAD completes, so we try over 20 seconds
before failing. */
if (family == AF_INET6 &&
(errno == ENODEV || errno == EADDRNOTAVAIL) &&
dad_count++ < DAD_WAIT)
{
sleep(1);
continue;
}
#endif
break;
}
if (rc == -1)
goto err; goto err;
if (type == SOCK_STREAM) if (type == SOCK_STREAM)
@@ -422,7 +380,7 @@ static int make_sock(union mysockaddr *addr, int type)
if (family == AF_INET) if (family == AF_INET)
{ {
#if defined(HAVE_LINUX_NETWORK) #if defined(HAVE_LINUX_NETWORK)
if (setsockopt(fd, SOL_IP, IP_PKTINFO, &opt, sizeof(opt)) == -1) if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt)) == -1)
goto err; goto err;
#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF) #elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt)) == -1 || if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt)) == -1 ||
@@ -431,46 +389,52 @@ static int make_sock(union mysockaddr *addr, int type)
#endif #endif
} }
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
else else if (!set_ipv6pktinfo(fd))
{ goto err;
/* The API changed around Linux 2.6.14 but the old ABI is still supported:
handle all combinations of headers and kernel.
OpenWrt note that this fixes the problem addressed by your very broken patch. */
daemon->v6pktinfo = IPV6_PKTINFO;
# ifdef IPV6_RECVPKTINFO
# ifdef IPV6_2292PKTINFO
if (setsockopt(fd, IPV6_LEVEL, IPV6_RECVPKTINFO, &opt, sizeof(opt)) == -1)
{
if (errno == ENOPROTOOPT && setsockopt(fd, IPV6_LEVEL, IPV6_2292PKTINFO, &opt, sizeof(opt)) != -1)
daemon->v6pktinfo = IPV6_2292PKTINFO;
else
goto err;
}
# else
if (setsockopt(fd, IPV6_LEVEL, IPV6_RECVPKTINFO, &opt, sizeof(opt)) == -1)
goto err;
# endif
# else
if (setsockopt(fd, IPV6_LEVEL, IPV6_PKTINFO, &opt, sizeof(opt)) == -1)
goto err;
# endif
}
#endif #endif
} }
return fd; return fd;
} }
#ifdef HAVE_IPV6
int set_ipv6pktinfo(int fd)
{
int opt = 1;
/* The API changed around Linux 2.6.14 but the old ABI is still supported:
handle all combinations of headers and kernel.
OpenWrt note that this fixes the problem addressed by your very broken patch. */
daemon->v6pktinfo = IPV6_PKTINFO;
#ifdef IPV6_RECVPKTINFO
if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &opt, sizeof(opt)) != -1)
return 1;
# ifdef IPV6_2292PKTINFO
else if (errno == ENOPROTOOPT && setsockopt(fd, IPPROTO_IPV6, IPV6_2292PKTINFO, &opt, sizeof(opt)) != -1)
{
daemon->v6pktinfo = IPV6_2292PKTINFO;
return 1;
}
# endif
#else
if (setsockopt(fd, IPPROTO_IPV6, IPV6_PKTINFO, &opt, sizeof(opt)) != -1)
return 1;
#endif
return 0;
}
#endif
static struct listener *create_listeners(union mysockaddr *addr, int do_tftp) static struct listener *create_listeners(union mysockaddr *addr, int do_tftp, int dienow)
{ {
struct listener *l = NULL; struct listener *l = NULL;
int fd = -1, tcpfd = -1, tftpfd = -1; int fd = -1, tcpfd = -1, tftpfd = -1;
if (daemon->port != 0) if (daemon->port != 0)
{ {
fd = make_sock(addr, SOCK_DGRAM); fd = make_sock(addr, SOCK_DGRAM, dienow);
tcpfd = make_sock(addr, SOCK_STREAM); tcpfd = make_sock(addr, SOCK_STREAM, dienow);
} }
#ifdef HAVE_TFTP #ifdef HAVE_TFTP
@@ -481,7 +445,7 @@ static struct listener *create_listeners(union mysockaddr *addr, int do_tftp)
/* port must be restored to DNS port for TCP code */ /* port must be restored to DNS port for TCP code */
short save = addr->in.sin_port; short save = addr->in.sin_port;
addr->in.sin_port = htons(TFTP_PORT); addr->in.sin_port = htons(TFTP_PORT);
tftpfd = make_sock(addr, SOCK_DGRAM); tftpfd = make_sock(addr, SOCK_DGRAM, dienow);
addr->in.sin_port = save; addr->in.sin_port = save;
} }
# ifdef HAVE_IPV6 # ifdef HAVE_IPV6
@@ -489,7 +453,7 @@ static struct listener *create_listeners(union mysockaddr *addr, int do_tftp)
{ {
short save = addr->in6.sin6_port; short save = addr->in6.sin6_port;
addr->in6.sin6_port = htons(TFTP_PORT); addr->in6.sin6_port = htons(TFTP_PORT);
tftpfd = make_sock(addr, SOCK_DGRAM); tftpfd = make_sock(addr, SOCK_DGRAM, dienow);
addr->in6.sin6_port = save; addr->in6.sin6_port = save;
} }
# endif # endif
@@ -509,7 +473,7 @@ static struct listener *create_listeners(union mysockaddr *addr, int do_tftp)
return l; return l;
} }
struct listener *create_wildcard_listeners(void) void create_wildcard_listeners(void)
{ {
union mysockaddr addr; union mysockaddr addr;
struct listener *l; struct listener *l;
@@ -523,7 +487,7 @@ struct listener *create_wildcard_listeners(void)
addr.in.sin_addr.s_addr = INADDR_ANY; addr.in.sin_addr.s_addr = INADDR_ANY;
addr.in.sin_port = htons(daemon->port); addr.in.sin_port = htons(daemon->port);
l = create_listeners(&addr, tftp_enabled); l = create_listeners(&addr, tftp_enabled, 1);
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
memset(&addr, 0, sizeof(addr)); memset(&addr, 0, sizeof(addr));
@@ -535,31 +499,41 @@ struct listener *create_wildcard_listeners(void)
addr.in6.sin6_port = htons(daemon->port); addr.in6.sin6_port = htons(daemon->port);
if (l) if (l)
l->next = create_listeners(&addr, tftp_enabled); l->next = create_listeners(&addr, tftp_enabled, 1);
else else
l = create_listeners(&addr, tftp_enabled); l = create_listeners(&addr, tftp_enabled, 1);
#endif #endif
return l; daemon->listeners = l;
} }
struct listener *create_bound_listeners(void) void create_bound_listeners(int dienow)
{ {
struct listener *new, *listeners = NULL; struct listener *new;
struct irec *iface; struct irec *iface;
for (iface = daemon->interfaces; iface; iface = iface->next) for (iface = daemon->interfaces; iface; iface = iface->next)
if ((new = create_listeners(&iface->addr, iface->tftp_ok))) if (!iface->done && !iface->dad &&
(new = create_listeners(&iface->addr, iface->tftp_ok, dienow)))
{ {
new->iface = iface; new->iface = iface;
new->next = listeners; new->next = daemon->listeners;
listeners = new; daemon->listeners = new;
iface->done = 1;
} }
return listeners;
} }
int is_dad_listeners(void)
{
struct irec *iface;
if (option_bool(OPT_NOWILD))
for (iface = daemon->interfaces; iface; iface = iface->next)
if (iface->dad && !iface->done)
return 1;
return 0;
}
/* return a UDP socket bound to a random port, have to cope with straying into /* return a UDP socket bound to a random port, have to cope with straying into
occupied port nos and reserved ones. */ occupied port nos and reserved ones. */
int random_sock(int family) int random_sock(int family)
@@ -896,20 +870,38 @@ int reload_servers(char *fname)
source_addr.in.sin_port = htons(daemon->query_port); source_addr.in.sin_port = htons(daemon->query_port);
} }
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
else if (inet_pton(AF_INET6, token, &addr.in6.sin6_addr) > 0) else
{ {
int scope_index = 0;
char *scope_id = strchr(token, '%');
if (scope_id)
{
*(scope_id++) = 0;
scope_index = if_nametoindex(scope_id);
}
if (inet_pton(AF_INET6, token, &addr.in6.sin6_addr) > 0)
{
#ifdef HAVE_SOCKADDR_SA_LEN #ifdef HAVE_SOCKADDR_SA_LEN
source_addr.in6.sin6_len = addr.in6.sin6_len = sizeof(source_addr.in6); source_addr.in6.sin6_len = addr.in6.sin6_len = sizeof(source_addr.in6);
#endif #endif
source_addr.in6.sin6_family = addr.in6.sin6_family = AF_INET6; source_addr.in6.sin6_family = addr.in6.sin6_family = AF_INET6;
addr.in6.sin6_port = htons(NAMESERVER_PORT); source_addr.in6.sin6_flowinfo = addr.in6.sin6_flowinfo = 0;
source_addr.in6.sin6_addr = in6addr_any; addr.in6.sin6_port = htons(NAMESERVER_PORT);
source_addr.in6.sin6_port = htons(daemon->query_port); addr.in6.sin6_scope_id = scope_index;
source_addr.in6.sin6_addr = in6addr_any;
source_addr.in6.sin6_port = htons(daemon->query_port);
source_addr.in6.sin6_scope_id = 0;
}
else
continue;
} }
#endif /* IPV6 */ #else /* IPV6 */
else else
continue; continue;
#endif
if (old_servers) if (old_servers)
{ {
serv = old_servers; serv = old_servers;
+648 -165
View File
File diff suppressed because it is too large. Load diff
+12 -10
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2011 Simon Kelley /* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -642,7 +642,6 @@ static int private_net(struct in_addr addr, int ban_localhost)
static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *header, size_t qlen, char *name) static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *header, size_t qlen, char *name)
{ {
int i, qtype, qclass, rdlen; int i, qtype, qclass, rdlen;
unsigned long ttl;
for (i = count; i != 0; i--) for (i = count; i != 0; i--)
{ {
@@ -656,7 +655,7 @@ static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *
GETSHORT(qtype, p); GETSHORT(qtype, p);
GETSHORT(qclass, p); GETSHORT(qclass, p);
GETLONG(ttl, p); p += 4; /* ttl */
GETSHORT(rdlen, p); GETSHORT(rdlen, p);
if (qclass == C_IN && qtype == T_A) if (qclass == C_IN && qtype == T_A)
@@ -1044,8 +1043,6 @@ unsigned int extract_request(struct dns_header *header, size_t qlen, char *name,
return F_IPV6; return F_IPV6;
if (qtype == T_ANY) if (qtype == T_ANY)
return F_IPV4 | F_IPV6; return F_IPV4 | F_IPV6;
if (qtype == T_NS || qtype == T_SOA)
return F_QUERY | F_NSRR;
} }
return F_QUERY; return F_QUERY;
@@ -1101,12 +1098,17 @@ int check_for_local_domain(char *name, time_t now)
struct txt_record *txt; struct txt_record *txt;
struct interface_name *intr; struct interface_name *intr;
struct ptr_record *ptr; struct ptr_record *ptr;
struct naptr *naptr;
if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6)) &&
if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6 | F_CNAME)) &&
(crecp->flags & (F_HOSTS | F_DHCP))) (crecp->flags & (F_HOSTS | F_DHCP)))
return 1; return 1;
for (mx = daemon->mxnames; mx; mx = mx->next) for (naptr = daemon->naptr; naptr; naptr = naptr->next)
if (hostname_isequal(name, naptr->name))
return 1;
for (mx = daemon->mxnames; mx; mx = mx->next)
if (hostname_isequal(name, mx->name)) if (hostname_isequal(name, mx->name))
return 1; return 1;
@@ -1309,11 +1311,11 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if (find_pseudoheader(header, qlen, NULL, &pheader, &is_sign)) if (find_pseudoheader(header, qlen, NULL, &pheader, &is_sign))
{ {
unsigned short udpsz, ext_rcode, flags; unsigned short udpsz, flags;
unsigned char *psave = pheader; unsigned char *psave = pheader;
GETSHORT(udpsz, pheader); GETSHORT(udpsz, pheader);
GETSHORT(ext_rcode, pheader); pheader += 2; /* ext_rcode */
GETSHORT(flags, pheader); GETSHORT(flags, pheader);
sec_reqd = flags & 0x8000; /* do bit */ sec_reqd = flags & 0x8000; /* do bit */
+184 -249
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2011 Simon Kelley /* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -18,16 +18,13 @@
#ifdef HAVE_DHCP #ifdef HAVE_DHCP
#define have_config(config, mask) ((config) && ((config)->flags & (mask)))
#define option_len(opt) ((int)(((unsigned char *)(opt))[1])) #define option_len(opt) ((int)(((unsigned char *)(opt))[1]))
#define option_ptr(opt, i) ((void *)&(((unsigned char *)(opt))[2u+(unsigned int)(i)])) #define option_ptr(opt, i) ((void *)&(((unsigned char *)(opt))[2u+(unsigned int)(i)]))
#ifdef HAVE_SCRIPT #ifdef HAVE_SCRIPT
static void add_extradata_data(struct dhcp_lease *lease, unsigned char *data, size_t len, int delim);
static void add_extradata_opt(struct dhcp_lease *lease, unsigned char *opt); static void add_extradata_opt(struct dhcp_lease *lease, unsigned char *opt);
#endif #endif
static int match_bytes(struct dhcp_opt *o, unsigned char *p, int len);
static int sanitise(unsigned char *opt, char *buf); static int sanitise(unsigned char *opt, char *buf);
static struct in_addr server_id(struct dhcp_context *context, struct in_addr override, struct in_addr fallback); static struct in_addr server_id(struct dhcp_context *context, struct in_addr override, struct in_addr fallback);
static unsigned int calc_time(struct dhcp_context *context, struct dhcp_config *config, unsigned char *opt); static unsigned int calc_time(struct dhcp_context *context, struct dhcp_config *config, unsigned char *opt);
@@ -35,39 +32,38 @@ static void option_put(struct dhcp_packet *mess, unsigned char *end, int opt, in
static void option_put_string(struct dhcp_packet *mess, unsigned char *end, static void option_put_string(struct dhcp_packet *mess, unsigned char *end,
int opt, char *string, int null_term); int opt, char *string, int null_term);
static struct in_addr option_addr(unsigned char *opt); static struct in_addr option_addr(unsigned char *opt);
static struct in_addr option_addr_arr(unsigned char *opt, int offset);
static unsigned int option_uint(unsigned char *opt, int i, int size); static unsigned int option_uint(unsigned char *opt, int i, int size);
static void log_packet(char *type, void *addr, unsigned char *ext_mac, static void log_packet(char *type, void *addr, unsigned char *ext_mac,
int mac_len, char *interface, char *string, u32 xid); int mac_len, char *interface, char *string, u32 xid);
static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize); static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize);
static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize); static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize);
static size_t dhcp_packet_size(struct dhcp_packet *mess, struct dhcp_netid *netid, static size_t dhcp_packet_size(struct dhcp_packet *mess, unsigned char *agent_id, unsigned char *real_end);
unsigned char *agent_id, unsigned char *real_end);
static void clear_packet(struct dhcp_packet *mess, unsigned char *end); static void clear_packet(struct dhcp_packet *mess, unsigned char *end);
static void do_options(struct dhcp_context *context, static void do_options(struct dhcp_context *context,
struct dhcp_packet *mess, struct dhcp_packet *mess,
unsigned char *real_end, unsigned char *real_end,
unsigned char *req_options, unsigned char *req_options,
char *hostname, char *hostname,
char *domain, char *config_domain, char *config_domain,
struct dhcp_netid *netid, struct dhcp_netid *netid,
struct in_addr subnet_addr, struct in_addr subnet_addr,
unsigned char fqdn_flags, unsigned char fqdn_flags,
int null_term, int pxearch, int null_term, int pxearch,
unsigned char *uuid, unsigned char *uuid,
int vendor_class_len); int vendor_class_len,
time_t now);
static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt); static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt);
static int do_encap_opts(struct dhcp_opt *opts, int encap, int flag, struct dhcp_packet *mess, unsigned char *end, int null_term); static int do_encap_opts(struct dhcp_opt *opts, int encap, int flag, struct dhcp_packet *mess, unsigned char *end, int null_term);
static void pxe_misc(struct dhcp_packet *mess, unsigned char *end, unsigned char *uuid); static void pxe_misc(struct dhcp_packet *mess, unsigned char *end, unsigned char *uuid);
static int prune_vendor_opts(struct dhcp_netid *netid); static int prune_vendor_opts(struct dhcp_netid *netid);
static struct dhcp_opt *pxe_opts(int pxe_arch, struct dhcp_netid *netid, struct in_addr local); static struct dhcp_opt *pxe_opts(int pxe_arch, struct dhcp_netid *netid, struct in_addr local, time_t now);
struct dhcp_boot *find_boot(struct dhcp_netid *netid); struct dhcp_boot *find_boot(struct dhcp_netid *netid);
size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index, size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
size_t sz, time_t now, int unicast_dest, int *is_inform, int pxe) size_t sz, time_t now, int unicast_dest, int *is_inform, int pxe, struct in_addr fallback)
{ {
unsigned char *opt, *clid = NULL; unsigned char *opt, *clid = NULL;
struct dhcp_lease *ltmp, *lease = NULL; struct dhcp_lease *ltmp, *lease = NULL;
@@ -85,7 +81,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
unsigned int time; unsigned int time;
struct dhcp_config *config; struct dhcp_config *config;
struct dhcp_netid *netid, *tagif_netid; struct dhcp_netid *netid, *tagif_netid;
struct in_addr subnet_addr, fallback, override; struct in_addr subnet_addr, override;
unsigned short fuzz = 0; unsigned short fuzz = 0;
unsigned int mess_type = 0; unsigned int mess_type = 0;
unsigned char fqdn_flags = 0; unsigned char fqdn_flags = 0;
@@ -297,17 +293,43 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
if (!context_new) if (!context_new)
for (context_tmp = daemon->dhcp; context_tmp; context_tmp = context_tmp->next) for (context_tmp = daemon->dhcp; context_tmp; context_tmp = context_tmp->next)
if (context_tmp->netmask.s_addr && {
is_same_net(addr, context_tmp->start, context_tmp->netmask) && struct in_addr netmask = context_tmp->netmask;
is_same_net(addr, context_tmp->end, context_tmp->netmask))
{ /* guess the netmask for relayed networks */
context_tmp->current = context_new; if (!(context_tmp->flags & CONTEXT_NETMASK) && context_tmp->netmask.s_addr == 0)
context_new = context_tmp; {
} if (IN_CLASSA(ntohl(context_tmp->start.s_addr)) && IN_CLASSA(ntohl(context_tmp->end.s_addr)))
netmask.s_addr = htonl(0xff000000);
else if (IN_CLASSB(ntohl(context_tmp->start.s_addr)) && IN_CLASSB(ntohl(context_tmp->end.s_addr)))
netmask.s_addr = htonl(0xffff0000);
else if (IN_CLASSC(ntohl(context_tmp->start.s_addr)) && IN_CLASSC(ntohl(context_tmp->end.s_addr)))
netmask.s_addr = htonl(0xffffff00);
}
/* This section fills in context mainly when a client which is on a remote (relayed)
network renews a lease without using the relay, after dnsmasq has restarted. */
if (netmask.s_addr != 0 &&
is_same_net(addr, context_tmp->start, netmask) &&
is_same_net(addr, context_tmp->end, netmask))
{
context_tmp->netmask = netmask;
if (context_tmp->local.s_addr == 0)
context_tmp->local = fallback;
if (context_tmp->router.s_addr == 0)
context_tmp->router = mess->giaddr;
/* fill in missing broadcast addresses for relayed ranges */
if (!(context_tmp->flags & CONTEXT_BRDCAST) && context_tmp->broadcast.s_addr == 0 )
context_tmp->broadcast.s_addr = context_tmp->start.s_addr | ~context_tmp->netmask.s_addr;
context_tmp->current = context_new;
context_new = context_tmp;
}
}
if (context_new || force) if (context_new || force)
context = context_new; context = context_new;
} }
if (!context) if (!context)
@@ -318,9 +340,6 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
return 0; return 0;
} }
/* keep _a_ local address available. */
fallback = context->local;
if (option_bool(OPT_LOG_OPTS)) if (option_bool(OPT_LOG_OPTS))
{ {
struct dhcp_context *context_tmp; struct dhcp_context *context_tmp;
@@ -441,9 +460,10 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
else if (context->netid.net) else if (context->netid.net)
{ {
context->netid.next = netid; context->netid.next = netid;
netid = &context->netid; tagif_netid = run_tag_if(&context->netid);
tagif_netid = run_tag_if(netid);
} }
log_tags(tagif_netid, ntohl(mess->xid));
if (!message && !nailed) if (!message && !nailed)
{ {
@@ -456,7 +476,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
if (!message && if (!message &&
!lease && !lease &&
(!(lease = lease_allocate(mess->yiaddr)))) (!(lease = lease4_allocate(mess->yiaddr))))
message = _("no leases left"); message = _("no leases left");
if (!message) if (!message)
@@ -465,7 +485,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
lease_set_hwaddr(lease, mess->chaddr, NULL, mess->hlen, mess->htype, 0); lease_set_hwaddr(lease, mess->chaddr, NULL, mess->hlen, mess->htype, 0);
if (hostname) if (hostname)
lease_set_hostname(lease, hostname, 1); lease_set_hostname(lease, hostname, 1, get_domain(lease->addr), domain);
/* infinite lease unless nailed in dhcp-host line. */ /* infinite lease unless nailed in dhcp-host line. */
lease_set_expires(lease, lease_set_expires(lease,
have_config(config, CONFIG_TIME) ? config->lease_time : 0xffffffff, have_config(config, CONFIG_TIME) ? config->lease_time : 0xffffffff,
@@ -474,13 +494,13 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
clear_packet(mess, end); clear_packet(mess, end);
do_options(context, mess, end, NULL, hostname, get_domain(mess->yiaddr), do_options(context, mess, end, NULL, hostname, get_domain(mess->yiaddr),
domain, tagif_netid, subnet_addr, 0, 0, 0, NULL, 0); netid, subnet_addr, 0, 0, 0, NULL, 0, now);
} }
} }
log_packet("BOOTP", logaddr, mess->chaddr, mess->hlen, iface_name, message, mess->xid); log_packet("BOOTP", logaddr, mess->chaddr, mess->hlen, iface_name, message, mess->xid);
return message ? 0 : dhcp_packet_size(mess, tagif_netid, agent_id, real_end); return message ? 0 : dhcp_packet_size(mess, agent_id, real_end);
} }
if ((opt = option_find(mess, sz, OPTION_CLIENT_FQDN, 4))) if ((opt = option_find(mess, sz, OPTION_CLIENT_FQDN, 4)))
@@ -496,11 +516,10 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
pp = op; pp = op;
/* Always force update, since the client has no way to do it itself. */ /* Always force update, since the client has no way to do it itself. */
if (!(fqdn_flags & 0x01)) if (!option_bool(OPT_FQDN_UPDATE) && !(fqdn_flags & 0x01))
fqdn_flags |= 0x02; fqdn_flags |= 0x03;
fqdn_flags &= ~0x08; fqdn_flags &= ~0x08;
fqdn_flags |= 0x01;
if (fqdn_flags & 0x04) if (fqdn_flags & 0x04)
while (*op != 0 && ((op + (*op) + 1) - pp) < len) while (*op != 0 && ((op + (*op) + 1) - pp) < len)
@@ -775,7 +794,9 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
mess->yiaddr = mess->ciaddr; mess->yiaddr = mess->ciaddr;
mess->ciaddr.s_addr = 0; mess->ciaddr.s_addr = 0;
if (service->server.s_addr != 0) if (service->sname)
mess->siaddr = a_record_from_hosts(service->sname, now);
else if (service->server.s_addr != 0)
mess->siaddr = service->server; mess->siaddr = service->server;
else else
mess->siaddr = context->local; mess->siaddr = context->local;
@@ -795,7 +816,8 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
do_encap_opts(&opt71, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0); do_encap_opts(&opt71, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);
log_packet("PXE", &mess->yiaddr, emac, emac_len, iface_name, (char *)mess->file, mess->xid); log_packet("PXE", &mess->yiaddr, emac, emac_len, iface_name, (char *)mess->file, mess->xid);
return dhcp_packet_size(mess, tagif_netid, agent_id, real_end); log_tags(tagif_netid, ntohl(mess->xid));
return dhcp_packet_size(mess, agent_id, real_end);
} }
if ((opt = option_find(mess, sz, OPTION_ARCH, 2))) if ((opt = option_find(mess, sz, OPTION_ARCH, 2)))
@@ -829,8 +851,10 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
and set discovery_control = 8 */ and set discovery_control = 8 */
if (boot) if (boot)
{ {
if (boot->next_server.s_addr) if (boot->next_server.s_addr)
mess->siaddr = boot->next_server; mess->siaddr = boot->next_server;
else if (boot->tftp_sname)
mess->siaddr = a_record_from_hosts(boot->tftp_sname, now);
if (boot->file) if (boot->file)
strncpy((char *)mess->file, boot->file, sizeof(mess->file)-1); strncpy((char *)mess->file, boot->file, sizeof(mess->file)-1);
@@ -841,10 +865,11 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(context->local.s_addr)); option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(context->local.s_addr));
pxe_misc(mess, end, uuid); pxe_misc(mess, end, uuid);
prune_vendor_opts(tagif_netid); prune_vendor_opts(tagif_netid);
do_encap_opts(pxe_opts(pxearch, tagif_netid, context->local), OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0); do_encap_opts(pxe_opts(pxearch, tagif_netid, context->local, now), OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);
log_packet("PXE", NULL, emac, emac_len, iface_name, ignore ? "proxy-ignored" : "proxy", mess->xid); log_packet("PXE", NULL, emac, emac_len, iface_name, ignore ? "proxy-ignored" : "proxy", mess->xid);
return ignore ? 0 : dhcp_packet_size(mess, tagif_netid, agent_id, real_end); log_tags(tagif_netid, ntohl(mess->xid));
return ignore ? 0 : dhcp_packet_size(mess, agent_id, real_end);
} }
} }
} }
@@ -975,15 +1000,16 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
if (message || !(context = narrow_context(context, mess->yiaddr, tagif_netid))) if (message || !(context = narrow_context(context, mess->yiaddr, tagif_netid)))
return 0; return 0;
log_packet("DHCPOFFER" , &mess->yiaddr, emac, emac_len, iface_name, NULL, mess->xid);
if (context->netid.net) if (context->netid.net)
{ {
context->netid.next = netid; context->netid.next = netid;
netid = &context->netid; tagif_netid = run_tag_if(&context->netid);
tagif_netid = run_tag_if(netid);
} }
log_tags(tagif_netid, ntohl(mess->xid));
log_packet("DHCPOFFER" , &mess->yiaddr, emac, emac_len, iface_name, NULL, mess->xid);
time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4)); time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
clear_packet(mess, end); clear_packet(mess, end);
option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPOFFER); option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPOFFER);
@@ -996,9 +1022,9 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
option_put(mess, end, OPTION_T2, 4, (time*7)/8); option_put(mess, end, OPTION_T2, 4, (time*7)/8);
} }
do_options(context, mess, end, req_options, offer_hostname, get_domain(mess->yiaddr), do_options(context, mess, end, req_options, offer_hostname, get_domain(mess->yiaddr),
domain, tagif_netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len); netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now);
return dhcp_packet_size(mess, tagif_netid, agent_id, real_end); return dhcp_packet_size(mess, agent_id, real_end);
case DHCPREQUEST: case DHCPREQUEST:
if (ignore || have_config(config, CONFIG_DISABLE)) if (ignore || have_config(config, CONFIG_DISABLE))
@@ -1029,12 +1055,30 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
if (!context) if (!context)
{ {
/* In auth mode, a REQUEST sent to the wrong server /* Handle very strange configs where clients have more than one route to the server.
should be faulted, so that the client establishes If a clients idea of its server-id matches any of our DHCP interfaces, we let it pass.
communication with us, otherwise, silently ignore. */ Have to set override to make sure we echo back the correct server-id */
if (!option_bool(OPT_AUTHORITATIVE)) struct irec *intr;
return 0;
message = _("wrong server-ID"); enumerate_interfaces();
for (intr = daemon->interfaces; intr; intr = intr->next)
if (intr->addr.sa.sa_family == AF_INET &&
intr->addr.in.sin_addr.s_addr == option_addr(opt).s_addr &&
intr->tftp_ok)
break;
if (intr)
override = intr->addr.in.sin_addr;
else
{
/* In auth mode, a REQUEST sent to the wrong server
should be faulted, so that the client establishes
communication with us, otherwise, silently ignore. */
if (!option_bool(OPT_AUTHORITATIVE))
return 0;
message = _("wrong server-ID");
}
} }
} }
@@ -1142,7 +1186,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
else if (!lease) else if (!lease)
{ {
if ((lease = lease_allocate(mess->yiaddr))) if ((lease = lease4_allocate(mess->yiaddr)))
do_classes = 1; do_classes = 1;
else else
message = _("no leases left"); message = _("no leases left");
@@ -1173,9 +1217,10 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
if (context->netid.net) if (context->netid.net)
{ {
context->netid.next = netid; context->netid.next = netid;
netid = &context->netid; tagif_netid = run_tag_if( &context->netid);
tagif_netid = run_tag_if(netid);
} }
log_tags(tagif_netid, ntohl(mess->xid));
#ifdef HAVE_SCRIPT #ifdef HAVE_SCRIPT
if (do_classes && daemon->lease_change_command) if (do_classes && daemon->lease_change_command)
@@ -1185,7 +1230,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
if (mess->giaddr.s_addr) if (mess->giaddr.s_addr)
lease->giaddr = mess->giaddr; lease->giaddr = mess->giaddr;
lease->changed = 1; lease->flags |= LEASE_CHANGED;
free(lease->extradata); free(lease->extradata);
lease->extradata = NULL; lease->extradata = NULL;
lease->extradata_size = lease->extradata_len = 0; lease->extradata_size = lease->extradata_len = 0;
@@ -1201,8 +1246,16 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
add_extradata_opt(lease, NULL); add_extradata_opt(lease, NULL);
else else
for (n = tagif_netid; n; n = n->next) for (n = tagif_netid; n; n = n->next)
add_extradata_data(lease, (unsigned char *)n->net, strlen(n->net), n->next ? ' ' : 0); {
struct dhcp_netid *n1;
/* kill dupes */
for (n1 = n->next; n1; n1 = n1->next)
if (strcmp(n->net, n1->net) == 0)
break;
if (!n1)
lease_add_extradata(lease, (unsigned char *)n->net, strlen(n->net), n->next ? ' ' : 0);
}
if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1))) if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
{ {
int len = option_len(opt); int len = option_len(opt);
@@ -1210,7 +1263,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
/* If the user-class option started as counted strings, the first byte will be zero. */ /* If the user-class option started as counted strings, the first byte will be zero. */
if (len != 0 && ucp[0] == 0) if (len != 0 && ucp[0] == 0)
ucp++, len--; ucp++, len--;
add_extradata_data(lease, ucp, len, 0); lease_add_extradata(lease, ucp, len, 0);
} }
} }
#endif #endif
@@ -1254,7 +1307,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
} }
if (hostname) if (hostname)
lease_set_hostname(lease, hostname, hostname_auth); lease_set_hostname(lease, hostname, hostname_auth, get_domain(lease->addr), domain);
lease_set_expires(lease, time, now); lease_set_expires(lease, time, now);
lease_set_interface(lease, int_index); lease_set_interface(lease, int_index);
@@ -1278,10 +1331,10 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
option_put(mess, end, OPTION_T2, 4, ((time/8)*7) - fuzz); option_put(mess, end, OPTION_T2, 4, ((time/8)*7) - fuzz);
} }
do_options(context, mess, end, req_options, hostname, get_domain(mess->yiaddr), do_options(context, mess, end, req_options, hostname, get_domain(mess->yiaddr),
domain, tagif_netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len); netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now);
} }
return dhcp_packet_size(mess, tagif_netid, agent_id, real_end); return dhcp_packet_size(mess, agent_id, real_end);
case DHCPINFORM: case DHCPINFORM:
if (ignore || have_config(config, CONFIG_DISABLE)) if (ignore || have_config(config, CONFIG_DISABLE))
@@ -1304,15 +1357,16 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
if (!hostname && (hostname = host_from_dns(mess->ciaddr))) if (!hostname && (hostname = host_from_dns(mess->ciaddr)))
domain = get_domain(mess->ciaddr); domain = get_domain(mess->ciaddr);
log_packet("DHCPACK", &mess->ciaddr, emac, emac_len, iface_name, hostname, mess->xid);
if (context && context->netid.net) if (context && context->netid.net)
{ {
context->netid.next = netid; context->netid.next = netid;
netid = &context->netid; tagif_netid = run_tag_if(&context->netid);
tagif_netid = run_tag_if(netid);
} }
log_tags(tagif_netid, ntohl(mess->xid));
log_packet("DHCPACK", &mess->ciaddr, emac, emac_len, iface_name, hostname, mess->xid);
if (lease) if (lease)
{ {
@@ -1337,46 +1391,15 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
} }
do_options(context, mess, end, req_options, hostname, get_domain(mess->ciaddr), do_options(context, mess, end, req_options, hostname, get_domain(mess->ciaddr),
domain, tagif_netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len); netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now);
*is_inform = 1; /* handle reply differently */ *is_inform = 1; /* handle reply differently */
return dhcp_packet_size(mess, tagif_netid, agent_id, real_end); return dhcp_packet_size(mess, agent_id, real_end);
} }
return 0; return 0;
} }
static int match_bytes(struct dhcp_opt *o, unsigned char *p, int len)
{
int i;
if (o->len > len)
return 0;
if (o->len == 0)
return 1;
if (o->flags & DHOPT_HEX)
{
if (memcmp_masked(o->val, p, o->len, o->u.wildcard_mask))
return 1;
}
else
for (i = 0; i <= (len - o->len); )
{
if (memcmp(o->val, p + i, o->len) == 0)
return 1;
if (o->flags & DHOPT_STRING)
i++;
else
i += o->len;
}
return 0;
}
/* find a good value to use as MAC address for logging and address-allocation hashing. /* find a good value to use as MAC address for logging and address-allocation hashing.
This is normally just the chaddr field from the DHCP packet, This is normally just the chaddr field from the DHCP packet,
but eg Firewire will have hlen == 0 and use the client-id instead. but eg Firewire will have hlen == 0 and use the client-id instead.
@@ -1432,7 +1455,7 @@ static struct in_addr server_id(struct dhcp_context *context, struct in_addr ove
{ {
if (override.s_addr != 0) if (override.s_addr != 0)
return override; return override;
else if (context) else if (context && context->local.s_addr != 0)
return context->local; return context->local;
else else
return fallback; return fallback;
@@ -1462,51 +1485,12 @@ static int sanitise(unsigned char *opt, char *buf)
} }
#ifdef HAVE_SCRIPT #ifdef HAVE_SCRIPT
static void add_extradata_data(struct dhcp_lease *lease, unsigned char *data, size_t len, int delim)
{
if ((lease->extradata_size - lease->extradata_len) < (len + 1))
{
size_t newsz = lease->extradata_len + len + 100;
unsigned char *new = whine_malloc(newsz);
if (!new)
return;
if (lease->extradata)
{
memcpy(new, lease->extradata, lease->extradata_len);
free(lease->extradata);
}
lease->extradata = new;
lease->extradata_size = newsz;
}
if (len != 0)
memcpy(lease->extradata + lease->extradata_len, data, len);
lease->extradata[lease->extradata_len + len] = delim;
lease->extradata_len += len + 1;
}
static void add_extradata_opt(struct dhcp_lease *lease, unsigned char *opt) static void add_extradata_opt(struct dhcp_lease *lease, unsigned char *opt)
{ {
if (!opt) if (!opt)
add_extradata_data(lease, NULL, 0, 0); lease_add_extradata(lease, NULL, 0, 0);
else else
{ lease_add_extradata(lease, option_ptr(opt, 0), option_len(opt), 0);
size_t i, len = option_len(opt);
unsigned char *ucp = option_ptr(opt, 0);
/* check for embeded NULLs */
for (i = 0; i < len; i++)
if (ucp[i] == 0)
{
len = i;
break;
}
add_extradata_data(lease, ucp, len, 0);
}
} }
#endif #endif
@@ -1544,30 +1528,10 @@ static void log_options(unsigned char *start, u32 xid)
{ {
while (*start != OPTION_END) while (*start != OPTION_END)
{ {
int is_ip, is_name, i; char *optname = option_string(AF_INET, start[0], option_ptr(start, 0), option_len(start), daemon->namebuff, MAXDNAME);
char *text = option_string(start[0], &is_ip, &is_name);
unsigned char trunc = option_len(start);
if (is_ip) my_syslog(MS_DHCP | LOG_INFO, "%u sent size:%3d option:%3d %s %s",
for (daemon->namebuff[0]= 0, i = 0; i <= trunc - INADDRSZ; i += INADDRSZ) ntohl(xid), option_len(start), start[0], optname, daemon->namebuff);
{
if (i != 0)
strncat(daemon->namebuff, ", ", 256 - strlen(daemon->namebuff));
strncat(daemon->namebuff, inet_ntoa(option_addr_arr(start, i)), 256 - strlen(daemon->namebuff));
}
else if (!is_name || !sanitise(start, daemon->namebuff))
{
if (trunc > 13)
trunc = 13;
print_mac(daemon->namebuff, option_ptr(start, 0), trunc);
}
my_syslog(MS_DHCP | LOG_INFO, "%u sent size:%3d option:%3d%s%s%s%s%s",
ntohl(xid), option_len(start), start[0],
text ? ":" : "", text ? text : "",
trunc == 0 ? "" : " ",
trunc == 0 ? "" : daemon->namebuff,
trunc == option_len(start) ? "" : "...");
start += start[1] + 2; start += start[1] + 2;
} }
} }
@@ -1622,22 +1586,17 @@ static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt
return NULL; return NULL;
} }
static struct in_addr option_addr_arr(unsigned char *opt, int offset) static struct in_addr option_addr(unsigned char *opt)
{ {
/* this worries about unaligned data in the option. */ /* this worries about unaligned data in the option. */
/* struct in_addr is network byte order */ /* struct in_addr is network byte order */
struct in_addr ret; struct in_addr ret;
memcpy(&ret, option_ptr(opt, offset), INADDRSZ); memcpy(&ret, option_ptr(opt, 0), INADDRSZ);
return ret; return ret;
} }
static struct in_addr option_addr(unsigned char *opt)
{
return option_addr_arr(opt, 0);
}
static unsigned int option_uint(unsigned char *opt, int offset, int size) static unsigned int option_uint(unsigned char *opt, int offset, int size)
{ {
/* this worries about unaligned data and byte order */ /* this worries about unaligned data and byte order */
@@ -1672,15 +1631,12 @@ static unsigned char *find_overload(struct dhcp_packet *mess)
return NULL; return NULL;
} }
static size_t dhcp_packet_size(struct dhcp_packet *mess, struct dhcp_netid *netid, static size_t dhcp_packet_size(struct dhcp_packet *mess, unsigned char *agent_id, unsigned char *real_end)
unsigned char *agent_id, unsigned char *real_end)
{ {
unsigned char *p = dhcp_skip_opts(&mess->options[0] + sizeof(u32)); unsigned char *p = dhcp_skip_opts(&mess->options[0] + sizeof(u32));
unsigned char *overload; unsigned char *overload;
size_t ret; size_t ret;
struct dhcp_netid_list *id_list;
struct dhcp_netid *n;
/* move agent_id back down to the end of the packet */ /* move agent_id back down to the end of the packet */
if (agent_id) if (agent_id)
{ {
@@ -1689,27 +1645,6 @@ static size_t dhcp_packet_size(struct dhcp_packet *mess, struct dhcp_netid *neti
memset(p, 0, real_end - p); /* in case of overlap */ memset(p, 0, real_end - p); /* in case of overlap */
} }
/* We do logging too */
if (netid && option_bool(OPT_LOG_OPTS))
{
char *s = daemon->namebuff;
for (*s = 0; netid; netid = netid->next)
{
/* kill dupes. */
for (n = netid->next; n; n = n->next)
if (strcmp(netid->net, n->net) == 0)
break;
if (!n)
{
strncat (s, netid->net, (MAXDNAME-1) - strlen(s));
if (netid->next)
strncat (s, ", ", (MAXDNAME-1) - strlen(s));
}
}
my_syslog(MS_DHCP | LOG_INFO, _("%u tags: %s"), ntohl(mess->xid), s);
}
/* add END options to the regions. */ /* add END options to the regions. */
overload = find_overload(mess); overload = find_overload(mess);
@@ -1734,12 +1669,6 @@ static size_t dhcp_packet_size(struct dhcp_packet *mess, struct dhcp_netid *neti
*p++ = OPTION_END; *p++ = OPTION_END;
for (id_list = daemon->force_broadcast; id_list; id_list = id_list->next)
if ((!id_list->list) || match_netid(id_list->list, netid, 0))
break;
if (id_list)
mess->flags |= htons(0x8000); /* force broadcast */
if (option_bool(OPT_LOG_OPTS)) if (option_bool(OPT_LOG_OPTS))
{ {
if (mess->siaddr.s_addr != 0) if (mess->siaddr.s_addr != 0)
@@ -1889,20 +1818,14 @@ static int in_list(unsigned char *list, int opt)
return 0; return 0;
} }
static struct dhcp_opt *option_find2(struct dhcp_netid *netid, struct dhcp_opt *opts, int opt) static struct dhcp_opt *option_find2(int opt)
{ {
struct dhcp_opt *tmp; struct dhcp_opt *opts;
for (tmp = opts; tmp; tmp = tmp->next)
if (tmp->opt == opt && !(tmp->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925))) for (opts = daemon->dhcp_opts; opts; opts = opts->next)
if (match_netid(tmp->netid, netid, 0)) if (opts->opt == opt && (opts->flags & DHOPT_TAGOK))
return tmp; return opts;
/* No match, look for one without a netid */
for (tmp = opts; tmp; tmp = tmp->next)
if (tmp->opt == opt && !(tmp->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)))
if (match_netid(tmp->netid, netid, 1))
return tmp;
return NULL; return NULL;
} }
@@ -2002,7 +1925,7 @@ static int prune_vendor_opts(struct dhcp_netid *netid)
return force; return force;
} }
static struct dhcp_opt *pxe_opts(int pxe_arch, struct dhcp_netid *netid, struct in_addr local) static struct dhcp_opt *pxe_opts(int pxe_arch, struct dhcp_netid *netid, struct in_addr local, time_t now)
{ {
#define NUM_OPTS 4 #define NUM_OPTS 4
@@ -2059,8 +1982,9 @@ static struct dhcp_opt *pxe_opts(int pxe_arch, struct dhcp_netid *netid, struct
return daemon->dhcp_opts; return daemon->dhcp_opts;
} }
boot_server = service->basename ? local : service->server; boot_server = service->basename ? local :
(service->sname ? a_record_from_hosts(service->sname, now) : service->server);
if (boot_server.s_addr != 0) if (boot_server.s_addr != 0)
{ {
if (q - (unsigned char *)daemon->dhcp_buff3 + 3 + INADDRSZ >= 253) if (q - (unsigned char *)daemon->dhcp_buff3 + 3 + INADDRSZ >= 253)
@@ -2151,13 +2075,14 @@ static void do_options(struct dhcp_context *context,
unsigned char *end, unsigned char *end,
unsigned char *req_options, unsigned char *req_options,
char *hostname, char *hostname,
char *domain, char *config_domain, char *domain,
struct dhcp_netid *netid, struct dhcp_netid *netid,
struct in_addr subnet_addr, struct in_addr subnet_addr,
unsigned char fqdn_flags, unsigned char fqdn_flags,
int null_term, int pxe_arch, int null_term, int pxe_arch,
unsigned char *uuid, unsigned char *uuid,
int vendor_class_len) int vendor_class_len,
time_t now)
{ {
struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts; struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts;
struct dhcp_boot *boot; struct dhcp_boot *boot;
@@ -2166,22 +2091,24 @@ static void do_options(struct dhcp_context *context,
unsigned char f0 = 0, s0 = 0; unsigned char f0 = 0, s0 = 0;
int done_file = 0, done_server = 0; int done_file = 0, done_server = 0;
int done_vendor_class = 0; int done_vendor_class = 0;
struct dhcp_netid *tagif;
struct dhcp_netid_list *id_list;
if (config_domain && (!domain || !hostname_isequal(domain, config_domain))) /* filter options based on tags, those we want get DHOPT_TAGOK bit set */
my_syslog(MS_DHCP | LOG_WARNING, _("Ignoring domain %s for DHCP host name %s"), config_domain, hostname); tagif = option_filter(netid, &context->netid, config_opts);
/* logging */ /* logging */
if (option_bool(OPT_LOG_OPTS) && req_options) if (option_bool(OPT_LOG_OPTS) && req_options)
{ {
char *q = daemon->namebuff; char *q = daemon->namebuff;
for (i = 0; req_options[i] != OPTION_END; i++) for (i = 0; req_options[i] != OPTION_END; i++)
{ {
char *s = option_string(req_options[i], NULL, NULL); char *s = option_string(AF_INET, req_options[i], NULL, 0, NULL, 0);
q += snprintf(q, MAXDNAME - (q - daemon->namebuff), q += snprintf(q, MAXDNAME - (q - daemon->namebuff),
"%d%s%s%s", "%d%s%s%s",
req_options[i], req_options[i],
s ? ":" : "", strlen(s) != 0 ? ":" : "",
s ? s : "", s,
req_options[i+1] == OPTION_END ? "" : ", "); req_options[i+1] == OPTION_END ? "" : ", ");
if (req_options[i+1] == OPTION_END || (q - daemon->namebuff) > 40) if (req_options[i+1] == OPTION_END || (q - daemon->namebuff) > 40)
{ {
@@ -2191,6 +2118,12 @@ static void do_options(struct dhcp_context *context,
} }
} }
for (id_list = daemon->force_broadcast; id_list; id_list = id_list->next)
if ((!id_list->list) || match_netid(id_list->list, netid, 0))
break;
if (id_list)
mess->flags |= htons(0x8000); /* force broadcast */
if (context) if (context)
mess->siaddr = context->local; mess->siaddr = context->local;
@@ -2200,7 +2133,7 @@ static void do_options(struct dhcp_context *context,
provide an manual option to disable it. provide an manual option to disable it.
Some PXE ROMs have bugs (surprise!) and need zero-terminated Some PXE ROMs have bugs (surprise!) and need zero-terminated
names, so we always send those. */ names, so we always send those. */
if ((boot = find_boot(netid))) if ((boot = find_boot(tagif)))
{ {
if (boot->sname) if (boot->sname)
{ {
@@ -2222,8 +2155,10 @@ static void do_options(struct dhcp_context *context,
strncpy((char *)mess->file, boot->file, sizeof(mess->file)-1); strncpy((char *)mess->file, boot->file, sizeof(mess->file)-1);
} }
if (boot->next_server.s_addr) if (boot->next_server.s_addr)
mess->siaddr = boot->next_server; mess->siaddr = boot->next_server;
else if (boot->tftp_sname)
mess->siaddr = a_record_from_hosts(boot->tftp_sname, now);
} }
else else
/* Use the values of the relevant options if no dhcp-boot given and /* Use the values of the relevant options if no dhcp-boot given and
@@ -2232,20 +2167,20 @@ static void do_options(struct dhcp_context *context,
dhcp-optsfile. */ dhcp-optsfile. */
{ {
if ((!req_options || !in_list(req_options, OPTION_FILENAME)) && if ((!req_options || !in_list(req_options, OPTION_FILENAME)) &&
(opt = option_find2(netid, config_opts, OPTION_FILENAME)) && !(opt->flags & DHOPT_FORCE)) (opt = option_find2(OPTION_FILENAME)) && !(opt->flags & DHOPT_FORCE))
{ {
strncpy((char *)mess->file, (char *)opt->val, sizeof(mess->file)-1); strncpy((char *)mess->file, (char *)opt->val, sizeof(mess->file)-1);
done_file = 1; done_file = 1;
} }
if ((!req_options || !in_list(req_options, OPTION_SNAME)) && if ((!req_options || !in_list(req_options, OPTION_SNAME)) &&
(opt = option_find2(netid, config_opts, OPTION_SNAME)) && !(opt->flags & DHOPT_FORCE)) (opt = option_find2(OPTION_SNAME)) && !(opt->flags & DHOPT_FORCE))
{ {
strncpy((char *)mess->sname, (char *)opt->val, sizeof(mess->sname)-1); strncpy((char *)mess->sname, (char *)opt->val, sizeof(mess->sname)-1);
done_server = 1; done_server = 1;
} }
if ((opt = option_find2(netid, config_opts, OPTION_END))) if ((opt = option_find2(OPTION_END)))
mess->siaddr.s_addr = ((struct in_addr *)opt->val)->s_addr; mess->siaddr.s_addr = ((struct in_addr *)opt->val)->s_addr;
} }
@@ -2273,36 +2208,36 @@ static void do_options(struct dhcp_context *context,
/* replies to DHCPINFORM may not have a valid context */ /* replies to DHCPINFORM may not have a valid context */
if (context) if (context)
{ {
if (!option_find2(netid, config_opts, OPTION_NETMASK)) if (!option_find2(OPTION_NETMASK))
option_put(mess, end, OPTION_NETMASK, INADDRSZ, ntohl(context->netmask.s_addr)); option_put(mess, end, OPTION_NETMASK, INADDRSZ, ntohl(context->netmask.s_addr));
/* May not have a "guessed" broadcast address if we got no packets via a relay /* May not have a "guessed" broadcast address if we got no packets via a relay
from this net yet (ie just unicast renewals after a restart */ from this net yet (ie just unicast renewals after a restart */
if (context->broadcast.s_addr && if (context->broadcast.s_addr &&
!option_find2(netid, config_opts, OPTION_BROADCAST)) !option_find2(OPTION_BROADCAST))
option_put(mess, end, OPTION_BROADCAST, INADDRSZ, ntohl(context->broadcast.s_addr)); option_put(mess, end, OPTION_BROADCAST, INADDRSZ, ntohl(context->broadcast.s_addr));
/* Same comments as broadcast apply, and also may not be able to get a sensible /* Same comments as broadcast apply, and also may not be able to get a sensible
default when using subnet select. User must configure by steam in that case. */ default when using subnet select. User must configure by steam in that case. */
if (context->router.s_addr && if (context->router.s_addr &&
in_list(req_options, OPTION_ROUTER) && in_list(req_options, OPTION_ROUTER) &&
!option_find2(netid, config_opts, OPTION_ROUTER)) !option_find2(OPTION_ROUTER))
option_put(mess, end, OPTION_ROUTER, INADDRSZ, ntohl(context->router.s_addr)); option_put(mess, end, OPTION_ROUTER, INADDRSZ, ntohl(context->router.s_addr));
if (in_list(req_options, OPTION_DNSSERVER) && if (in_list(req_options, OPTION_DNSSERVER) &&
!option_find2(netid, config_opts, OPTION_DNSSERVER)) !option_find2(OPTION_DNSSERVER))
option_put(mess, end, OPTION_DNSSERVER, INADDRSZ, ntohl(context->local.s_addr)); option_put(mess, end, OPTION_DNSSERVER, INADDRSZ, ntohl(context->local.s_addr));
} }
if (domain && in_list(req_options, OPTION_DOMAINNAME) && if (domain && in_list(req_options, OPTION_DOMAINNAME) &&
!option_find2(netid, config_opts, OPTION_DOMAINNAME)) !option_find2(OPTION_DOMAINNAME))
option_put_string(mess, end, OPTION_DOMAINNAME, domain, null_term); option_put_string(mess, end, OPTION_DOMAINNAME, domain, null_term);
/* Note that we ignore attempts to set the fqdn using --dhc-option=81,<name> */ /* Note that we ignore attempts to set the fqdn using --dhc-option=81,<name> */
if (hostname) if (hostname)
{ {
if (in_list(req_options, OPTION_HOSTNAME) && if (in_list(req_options, OPTION_HOSTNAME) &&
!option_find2(netid, config_opts, OPTION_HOSTNAME)) !option_find2(OPTION_HOSTNAME))
option_put_string(mess, end, OPTION_HOSTNAME, hostname, null_term); option_put_string(mess, end, OPTION_HOSTNAME, hostname, null_term);
if (fqdn_flags != 0) if (fqdn_flags != 0)
@@ -2351,6 +2286,10 @@ static void do_options(struct dhcp_context *context,
{ {
int optno = opt->opt; int optno = opt->opt;
/* netids match and not encapsulated? */
if (!(opt->flags & DHOPT_TAGOK))
continue;
/* was it asked for, or are we sending it anyway? */ /* was it asked for, or are we sending it anyway? */
if (!(opt->flags & DHOPT_FORCE) && !in_list(req_options, optno)) if (!(opt->flags & DHOPT_FORCE) && !in_list(req_options, optno))
continue; continue;
@@ -2369,10 +2308,6 @@ static void do_options(struct dhcp_context *context,
if (optno == OPTION_FILENAME && done_file) if (optno == OPTION_FILENAME && done_file)
continue; continue;
/* netids match and not encapsulated? */
if (opt != option_find2(netid, config_opts, optno))
continue;
/* For the options we have default values on /* For the options we have default values on
dhc-option=<optionno> means "don't include this option" dhc-option=<optionno> means "don't include this option"
not "include a zero-length option" */ not "include a zero-length option" */
@@ -2410,8 +2345,8 @@ static void do_options(struct dhcp_context *context,
/* Now send options to be encapsulated in arbitrary options, /* Now send options to be encapsulated in arbitrary options,
eg dhcp-option=encap:172,17,....... eg dhcp-option=encap:172,17,.......
Also hand vendor-identifying vendor-encapsulated options, Also handle vendor-identifying vendor-encapsulated options,
dhcp-option = rfc3925-encap:13,17,....... dhcp-option = vi-encap:13,17,.......
The may be more that one "outer" to do, so group The may be more that one "outer" to do, so group
all the options which match each outer in turn. */ all the options which match each outer in turn. */
for (opt = config_opts; opt; opt = opt->next) for (opt = config_opts; opt; opt = opt->next)
@@ -2439,7 +2374,7 @@ static void do_options(struct dhcp_context *context,
continue; continue;
o->flags |= DHOPT_ENCAP_DONE; o->flags |= DHOPT_ENCAP_DONE;
if (match_netid(o->netid, netid, 1) && if (match_netid(o->netid, tagif, 1) &&
((o->flags & DHOPT_FORCE) || in_list(req_options, outer))) ((o->flags & DHOPT_FORCE) || in_list(req_options, outer)))
{ {
o->flags |= DHOPT_ENCAP_MATCH; o->flags |= DHOPT_ENCAP_MATCH;
@@ -2473,12 +2408,12 @@ static void do_options(struct dhcp_context *context,
} }
} }
force_encap = prune_vendor_opts(netid); force_encap = prune_vendor_opts(tagif);
if (context && pxe_arch != -1) if (context && pxe_arch != -1)
{ {
pxe_misc(mess, end, uuid); pxe_misc(mess, end, uuid);
config_opts = pxe_opts(pxe_arch, netid, context->local); config_opts = pxe_opts(pxe_arch, tagif, context->local, now);
} }
if ((force_encap || in_list(req_options, OPTION_VENDOR_CLASS_OPT)) && if ((force_encap || in_list(req_options, OPTION_VENDOR_CLASS_OPT)) &&
+1461
View File
File diff suppressed because it is too large. Load diff
+30 -29
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2011 Simon Kelley /* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -57,7 +57,6 @@ void tftp_request(struct listener *listen, time_t now)
int mtuflag = IP_PMTUDISC_DONT; int mtuflag = IP_PMTUDISC_DONT;
#endif #endif
char namebuff[IF_NAMESIZE]; char namebuff[IF_NAMESIZE];
char pretty_addr[ADDRSTRLEN];
char *name; char *name;
char *prefix = daemon->tftp_prefix; char *prefix = daemon->tftp_prefix;
struct tftp_prefix *pref; struct tftp_prefix *pref;
@@ -114,7 +113,7 @@ void tftp_request(struct listener *listen, time_t now)
#if defined(HAVE_LINUX_NETWORK) #if defined(HAVE_LINUX_NETWORK)
if (listen->family == AF_INET) if (listen->family == AF_INET)
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO) if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO)
{ {
union { union {
unsigned char *c; unsigned char *c;
@@ -163,7 +162,7 @@ void tftp_request(struct listener *listen, time_t now)
if (listen->family == AF_INET6) if (listen->family == AF_INET6)
{ {
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == IPV6_LEVEL && cmptr->cmsg_type == daemon->v6pktinfo) if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
{ {
union { union {
unsigned char *c; unsigned char *c;
@@ -184,10 +183,10 @@ void tftp_request(struct listener *listen, time_t now)
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
if (listen->family == AF_INET6) if (listen->family == AF_INET6)
check = iface_check(AF_INET6, (struct all_addr *)&addr.in6.sin6_addr, name, &if_index); check = iface_check(AF_INET6, (struct all_addr *)&addr.in6.sin6_addr, name);
else else
#endif #endif
check = iface_check(AF_INET, (struct all_addr *)&addr.in.sin_addr, name, &if_index); check = iface_check(AF_INET, (struct all_addr *)&addr.in.sin_addr, name);
/* wierd TFTP service override */ /* wierd TFTP service override */
for (ir = daemon->tftp_interfaces; ir; ir = ir->next) for (ir = daemon->tftp_interfaces; ir; ir = ir->next)
@@ -222,17 +221,22 @@ void tftp_request(struct listener *listen, time_t now)
if (strcmp(ir->interface, name) == 0) if (strcmp(ir->interface, name) == 0)
special = 1; special = 1;
#ifdef HAVE_SOCKADDR_SA_LEN
addr.sa.sa_len = sa_len(&addr);
#endif
if (listen->family == AF_INET) if (listen->family == AF_INET)
addr.in.sin_port = htons(port); {
addr.in.sin_port = htons(port);
#ifdef HAVE_SOCKADDR_SA_LEN
addr.in.sin_len = sizeof(addr.in);
#endif
}
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
else else
{ {
addr.in6.sin6_port = htons(port); addr.in6.sin6_port = htons(port);
addr.in6.sin6_flowinfo = 0; addr.in6.sin6_flowinfo = 0;
addr.in6.sin6_scope_id = 0;
#ifdef HAVE_SOCKADDR_SA_LEN
addr.in6.sin6_len = sizeof(addr.in6);
#endif
} }
#endif #endif
@@ -255,14 +259,14 @@ void tftp_request(struct listener *listen, time_t now)
transfer->opt_blocksize = transfer->opt_transize = 0; transfer->opt_blocksize = transfer->opt_transize = 0;
transfer->netascii = transfer->carrylf = 0; transfer->netascii = transfer->carrylf = 0;
prettyprint_addr(&peer, pretty_addr); prettyprint_addr(&peer, daemon->addrbuff);
/* if we have a nailed-down range, iterate until we find a free one. */ /* if we have a nailed-down range, iterate until we find a free one. */
while (1) while (1)
{ {
if (bind(transfer->sockfd, &addr.sa, sizeof(addr)) == -1 || if (bind(transfer->sockfd, &addr.sa, sa_len(&addr)) == -1 ||
#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
setsockopt(transfer->sockfd, SOL_IP, IP_MTU_DISCOVER, &mtuflag, sizeof(mtuflag)) == -1 || setsockopt(transfer->sockfd, IPPROTO_IP, IP_MTU_DISCOVER, &mtuflag, sizeof(mtuflag)) == -1 ||
#endif #endif
!fix_fd(transfer->sockfd)) !fix_fd(transfer->sockfd))
{ {
@@ -293,7 +297,7 @@ void tftp_request(struct listener *listen, time_t now)
!(filename = next(&p, end)) || !(filename = next(&p, end)) ||
!(mode = next(&p, end)) || !(mode = next(&p, end)) ||
(strcasecmp(mode, "octet") != 0 && strcasecmp(mode, "netascii") != 0)) (strcasecmp(mode, "octet") != 0 && strcasecmp(mode, "netascii") != 0))
len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"), pretty_addr); len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"), daemon->addrbuff);
else else
{ {
if (strcasecmp(mode, "netascii") == 0) if (strcasecmp(mode, "netascii") == 0)
@@ -343,7 +347,7 @@ void tftp_request(struct listener *listen, time_t now)
size_t oldlen = strlen(daemon->namebuff); size_t oldlen = strlen(daemon->namebuff);
struct stat statbuf; struct stat statbuf;
strncat(daemon->namebuff, pretty_addr, (MAXDNAME-1) - strlen(daemon->namebuff)); strncat(daemon->namebuff, daemon->addrbuff, (MAXDNAME-1) - strlen(daemon->namebuff));
strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff)); strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff));
/* remove unique-directory if it doesn't exist */ /* remove unique-directory if it doesn't exist */
@@ -473,7 +477,6 @@ void check_tftp_listeners(fd_set *rset, time_t now)
{ {
struct tftp_transfer *transfer, *tmp, **up; struct tftp_transfer *transfer, *tmp, **up;
ssize_t len; ssize_t len;
char pretty_addr[ADDRSTRLEN];
struct ack { struct ack {
unsigned short op, block; unsigned short op, block;
@@ -489,7 +492,7 @@ void check_tftp_listeners(fd_set *rset, time_t now)
/* we overwrote the buffer... */ /* we overwrote the buffer... */
daemon->srv_save = NULL; daemon->srv_save = NULL;
prettyprint_addr(&transfer->peer, pretty_addr); prettyprint_addr(&transfer->peer, daemon->addrbuff);
if ((len = recv(transfer->sockfd, daemon->packet, daemon->packet_buff_sz, 0)) >= (ssize_t)sizeof(struct ack)) if ((len = recv(transfer->sockfd, daemon->packet, daemon->packet_buff_sz, 0)) >= (ssize_t)sizeof(struct ack))
{ {
@@ -521,7 +524,7 @@ void check_tftp_listeners(fd_set *rset, time_t now)
my_syslog(MS_TFTP | LOG_ERR, _("error %d %s received from %s"), my_syslog(MS_TFTP | LOG_ERR, _("error %d %s received from %s"),
(int)ntohs(mess->block), err, (int)ntohs(mess->block), err,
pretty_addr); daemon->addrbuff);
/* Got err, ensure we take abort */ /* Got err, ensure we take abort */
transfer->timeout = now; transfer->timeout = now;
@@ -552,7 +555,7 @@ void check_tftp_listeners(fd_set *rset, time_t now)
if (len != 0) if (len != 0)
{ {
my_syslog(MS_TFTP | LOG_ERR, _("failed sending %s to %s"), my_syslog(MS_TFTP | LOG_ERR, _("failed sending %s to %s"),
transfer->file->filename, pretty_addr); transfer->file->filename, daemon->addrbuff);
len = 0; len = 0;
endcon = 1; endcon = 1;
} }
@@ -565,7 +568,7 @@ void check_tftp_listeners(fd_set *rset, time_t now)
if (endcon || len == 0) if (endcon || len == 0)
{ {
if (!endcon) if (!endcon)
my_syslog(MS_TFTP | LOG_INFO, _("sent %s to %s"), transfer->file->filename, pretty_addr); my_syslog(MS_TFTP | LOG_INFO, _("sent %s to %s"), transfer->file->filename, daemon->addrbuff);
/* unlink */ /* unlink */
*up = tmp; *up = tmp;
free_transfer(transfer); free_transfer(transfer);
@@ -685,15 +688,13 @@ static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
for (i = 0, newcarrylf = 0; i < size; i++) for (i = 0, newcarrylf = 0; i < size; i++)
if (mess->data[i] == '\n' && ( i != 0 || !transfer->carrylf)) if (mess->data[i] == '\n' && ( i != 0 || !transfer->carrylf))
{ {
if (size == transfer->blocksize) transfer->expansion++;
{
transfer->expansion++; if (size != transfer->blocksize)
if (i == size - 1)
newcarrylf = 1; /* don't expand LF again if it moves to the next block */
}
else
size++; /* room in this block */ size++; /* room in this block */
else if (i == size - 1)
newcarrylf = 1; /* don't expand LF again if it moves to the next block */
/* make space and insert CR */ /* make space and insert CR */
memmove(&mess->data[i+1], &mess->data[i], size - (i + 1)); memmove(&mess->data[i+1], &mess->data[i], size - (i + 1));
mess->data[i] = '\r'; mess->data[i] = '\r';
+52 -2
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2011 Simon Kelley /* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -320,6 +320,48 @@ int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask)
return (a.s_addr & mask.s_addr) == (b.s_addr & mask.s_addr); return (a.s_addr & mask.s_addr) == (b.s_addr & mask.s_addr);
} }
#ifdef HAVE_IPV6
int is_same_net6(struct in6_addr *a, struct in6_addr *b, int prefixlen)
{
int pfbytes = prefixlen >> 3;
int pfbits = prefixlen & 7;
if (memcmp(&a->s6_addr, &b->s6_addr, pfbytes) != 0)
return 0;
if (pfbits == 0 ||
(a->s6_addr[pfbytes] >> (8 - pfbits) != b->s6_addr[pfbytes] >> (8 - pfbits)))
return 1;
return 0;
}
/* return least signigicant 64 bits if IPv6 address */
u64 addr6part(struct in6_addr *addr)
{
int i;
u64 ret = 0;
for (i = 8; i < 16; i++)
ret = (ret << 8) + addr->s6_addr[i];
return ret;
}
void setaddr6part(struct in6_addr *addr, u64 host)
{
int i;
for (i = 15; i >= 8; i--)
{
addr->s6_addr[i] = host;
host = host >> 8;
}
}
#endif
/* returns port number from address */ /* returns port number from address */
int prettyprint_addr(union mysockaddr *addr, char *buf) int prettyprint_addr(union mysockaddr *addr, char *buf)
{ {
@@ -333,7 +375,15 @@ int prettyprint_addr(union mysockaddr *addr, char *buf)
} }
else if (addr->sa.sa_family == AF_INET6) else if (addr->sa.sa_family == AF_INET6)
{ {
char name[IF_NAMESIZE];
inet_ntop(AF_INET6, &addr->in6.sin6_addr, buf, ADDRSTRLEN); inet_ntop(AF_INET6, &addr->in6.sin6_addr, buf, ADDRSTRLEN);
if (addr->in6.sin6_scope_id != 0 &&
if_indextoname(addr->in6.sin6_scope_id, name) &&
strlen(buf) + strlen(name) + 2 <= ADDRSTRLEN)
{
strcat(buf, "%");
strcat(buf, name);
}
port = ntohs(addr->in6.sin6_port); port = ntohs(addr->in6.sin6_port);
} }
#else #else
@@ -475,7 +525,7 @@ void bump_maxfd(int fd, int *max)
int retry_send(void) int retry_send(void)
{ {
struct timespec waiter; struct timespec waiter;
if (errno == EAGAIN) if (errno == EAGAIN || errno == EWOULDBLOCK)
{ {
waiter.tv_sec = 0; waiter.tv_sec = 0;
waiter.tv_nsec = 10000; waiter.tv_nsec = 10000;