Compare commits

...

81 Commits
v2.60 ... v2.61

Author SHA1 Message Date
Simon Kelley
7f61b3ad59 Small SLAAC optimisation. 2012-04-29 16:01:28 +01:00
Simon Kelley
a9ab732e35 reconfirm SLAAC addresses when DHCPv4 leases go though INIT_REBOOT state. 2012-04-29 16:01:28 +01:00
Simon Kelley
11263a462c isprint cast. 2012-04-29 16:01:28 +01:00
Simon Kelley
231d061b45 Tidy TXT record sanitising 2012-04-29 16:01:28 +01:00
Simon Kelley
cdbee9a40b Find room to store key-id and digest-type in DS records.
->uid is now overloaded to store key length
2012-04-27 10:30:49 +01:00
Simon Kelley
7b4ad2eb34 Teach cache to store DS and DNSKEY records 2012-04-27 10:30:49 +01:00
Simon Kelley
19d69be220 CHANGELOG update. 2012-04-27 10:14:34 +01:00
Simon Kelley
04363607aa Fix tftp-over-IPv4 regression on OpenBSD. 2012-04-27 10:11:51 +01:00
Simon Kelley
dcffad2a86 Ensure that the DBus DhcpLeaseUpdated events are generated. 2012-04-24 15:25:18 +01:00
Simon Kelley
6a69ab5ebd Fix error-handling problem in TFTP server. 2012-04-24 14:42:26 +01:00
Simon Kelley
fc92ead0dd CHANGELOG typo. 2012-04-22 21:28:24 +01:00
Simon Kelley
61ce600b20 --tftp-lowercase option. 2012-04-20 21:28:49 +01:00
Simon Kelley
7a14dfebbb Tidy previous commit. 2012-04-20 20:50:42 +01:00
Simon Kelley
42fb8153ba Sanitise filenames logged by TFTP 2012-04-20 17:15:01 +01:00
Simon Kelley
6f13e53886 Tidy up malloc-failure handling. 2012-04-17 14:25:06 +01:00
Simon Kelley
d1c759c5c1 Answer CNAME queries correctly. 2012-04-16 17:26:19 +01:00
Simon Kelley
e46164e0bd Updated French translation. 2012-04-16 16:39:38 +01:00
Simon Kelley
7389ce7ff5 substitue non-portable tail command with sed. 2012-04-16 15:07:48 +01:00
Simon Kelley
2f77797b17 Add port option to example dnsmasq.conf 2012-04-16 14:58:53 +01:00
Simon Kelley
9380ba70d6 Set SO_BINDTODEVICE on DHCP sockets when doing DHCP on one interface
only. Fixes OpenSTack use-case.
2012-04-16 14:41:56 +01:00
Simon Kelley
1023dcbc9e Don't cache DNS data from non-recursive nameservers. 2012-04-09 18:00:08 +01:00
Simon Kelley
83e854e359 Typo. 2012-04-05 13:21:58 +01:00
Simon Kelley
50303b19d8 Remove redundant send_from logging code. 2012-04-04 22:13:17 +01:00
Simon Kelley
89382bacaa Tweak sending ICMP6 echo requests for slaac. 2012-04-04 20:48:16 +01:00
Simon Kelley
6c559c34df tweak favicon 2012-04-02 20:40:34 +01:00
Simon Kelley
adaa6888dd Move FIXME message to comment - having it emitted by the code is just confusing. 2012-04-02 10:02:12 +01:00
Simon Kelley
a813111379 Fix bug in tag-matching logic with negated tags. 2012-03-31 21:35:12 +01:00
Simon Kelley
18f0fb050b RDNSS and DNSSL data in router advertisements. 2012-03-31 21:18:55 +01:00
Simon Kelley
05e92e5afe More RA flag evolution. 2012-03-30 22:24:15 +01:00
Simon Kelley
4723d49dad Set managed RA flag always when doing DHCP. 2012-03-30 21:04:17 +01:00
Simon Kelley
fbbc14541a Fix off-by-one in DHCPv6 FQDN option decoding. 2012-03-30 20:48:20 +01:00
Simon Kelley
5ef33279f2 Tidying radv 2012-03-30 15:10:28 +01:00
Simon Kelley
1e02a85970 radv.c tidying. 2012-03-29 11:07:25 +01:00
Simon Kelley
0e88d53faa Fix preprocessor checks, IP_TOS -> IPV6_TCLASS 2012-03-28 22:22:05 +01:00
Simon Kelley
01d1b8ddf2 Changelog update. 2012-03-28 21:37:25 +01:00
Simon Kelley
c8257540bc "deprecated" lease-time keyword for IPv6 2012-03-28 21:15:41 +01:00
Simon Kelley
2240704863 DHCP start-up logging tweak 2012-03-27 14:42:48 +01:00
Simon Kelley
e8ca69ea16 Doc updates for latest RA changes. 2012-03-26 21:23:26 +01:00
Simon Kelley
da632e7cc1 Comment typo. 2012-03-26 11:14:05 +01:00
Simon Kelley
30cd96663f More flexible RA configuration. 2012-03-25 20:44:38 +01:00
Simon Kelley
7dbe98147d tweak ra timer code to avoid missing events. 2012-03-25 14:49:54 +01:00
Simon Kelley
5d71d83420 Listen on ICMP6 file decriptor even when on ra-only only in use. 2012-03-24 14:40:42 +00:00
Simon Kelley
38a59a9ff7 debian changelog untangle. 2012-03-23 10:08:12 +00:00
Simon Kelley
4b028ad612 Merge branch 'bind' 2012-03-23 10:02:30 +00:00
Simon Kelley
442560beb4 Debian changelog for preivious fix. 2012-03-23 10:01:13 +00:00
Simon Kelley
7d2b5c9583 Fix crash in DHCPINFORM without valid --dhcp-range. 2012-03-23 10:00:02 +00:00
Simon Kelley
29689cfa5a Handle errors when sending ICMP6 pings better. 2012-03-22 14:01:00 +00:00
Simon Kelley
52d4abf2f9 Make --listen-address work for all 127.0.0.0/8 addresses. 2012-03-21 21:39:48 +00:00
Simon Kelley
a953096485 Send "FTP transfer complete" events to the DHCP lease script. 2012-03-20 22:07:35 +00:00
Simon Kelley
884a6dfe6d RA managed-bit and use-SLAAC bit tweaks. 2012-03-20 16:20:22 +00:00
Simon Kelley
0068301d24 Conditional compilation tweak. 2012-03-19 20:29:55 +00:00
Simon Kelley
353ae4d270 Check assumed SLAAC addresses by pinging them. 2012-03-19 20:07:51 +00:00
Simon Kelley
e759d426fa --host-record support 2012-03-16 13:18:57 +00:00
Simon Kelley
40ef23b547 Move DHCP option stuff to dhcp-common.c 2012-03-13 21:59:28 +00:00
Simon Kelley
f5e8562f96 More DHCP-option logging tweaks. 2012-03-13 14:22:30 +00:00
Simon Kelley
1567feae3c Log vendor class for DHCPv6 2012-03-12 22:15:35 +00:00
Simon Kelley
daf061c9de randomise DHCPv6 lease renewal intervals 2012-03-12 21:57:18 +00:00
Simon Kelley
d0e2c6c9ab decode DHCPv4 T1, T2 and lease-time options better. 2012-03-12 21:44:14 +00:00
Simon Kelley
8643ec7fea Update CHANGLEOG 2012-03-12 20:04:14 +00:00
Simon Kelley
5cfea3d402 Tidy last commit. 2012-03-12 17:28:27 +00:00
Simon Kelley
6c8f21e4a4 More useful DHCPv6 packet logging. 2012-03-12 15:06:55 +00:00
Simon Kelley
1d0f91c4a9 Don't trust the port in the source address of requests.
At least one client gets it wrong: always send to the client port for
clients, and the server port for relays.
2012-03-12 11:56:22 +00:00
Simon Kelley
2a82db4caf Supply zero preference in advertise and reply messages 2012-03-10 21:40:10 +00:00
Simon Kelley
dd88c17f15 Add status code containing "success" to all IA_TA and IA_NA
which have IAADDR options. This communicates zero information and
RFC3315 is unclear that it's needed, but at least one client seems
to require it.
2012-03-10 20:46:57 +00:00
Simon Kelley
8b37270410 Implement --dhcp-duid 2012-03-09 17:45:10 +00:00
Simon Kelley
760169fc43 Debian updates. 2012-03-09 14:27:49 +00:00
Simon Kelley
7023e38294 Docs changes for ra-names. 2012-03-09 12:05:49 +00:00
Simon Kelley
a7cf58cc47 Merge branch 'ra-names' 2012-03-09 11:37:42 +00:00
Simon Kelley
e25d1a2ea2 Fix prefix-map build code logic. 2012-03-08 13:24:17 +00:00
Simon Kelley
70969c1757 move #include for Solaris and Apple. 2012-03-07 20:46:28 +00:00
Simon Kelley
3803437dcc tidying 2012-03-07 20:39:40 +00:00
Simon Kelley
eabc6dd76a Use getifaddrs on *BSD. 2012-03-07 20:28:20 +00:00
Simon Kelley
e28d2e2b77 Merge branch 'getifaddrs' 2012-03-07 20:26:23 +00:00
Simon Kelley
96fafe2ed6 Fixed typos and tested. 2012-03-07 20:25:39 +00:00
Simon Kelley
c81d390f84 Update man page to reflect the existance of DHCPv6 and RA. 2012-03-07 19:10:19 +00:00
Simon Kelley
08456c61f6 Use getifaddrs to find interfaces on *BSD 2012-03-07 19:08:11 +00:00
Simon Kelley
bc26f9a03f Handle firewire and EUI-64 addresses in the SLAAC code. 2012-03-07 13:13:56 +00:00
Simon Kelley
6ffeff86be Teach emit_dbus_signal() about IPv6 DHCPv6 leases. 2012-03-07 10:32:35 +00:00
Simon Kelley
f444cddbaf Don't waste time calculating EUI-64 when a lease doesn't have a name. 2012-03-07 10:15:57 +00:00
Simon Kelley
d13191a46c Bump Debian version number. 2012-03-06 19:57:39 +00:00
Simon Kelley
801ca9a7b7 Add ra-names SLAAC-hostnames from DHCPv4 option. 2012-03-06 19:30:17 +00:00
37 changed files with 2625 additions and 1139 deletions

107
CHANGELOG
View File

@@ -1,3 +1,110 @@
version 2.61
Re-write interface discovery code on *BSD to use
getifaddrs. This is more portable, more straightforward,
and allows us to find the prefix length for IPv6
addresses.
Add ra-names, ra-stateless and slaac keywords for DHCPv6.
Dnsmasq can now synthesise AAAA records for dual-stack
hosts which get IPv6 addresses via SLAAC. It is also now
possible to use SLAAC and stateless DHCPv6, and to
tell clients to use SLAAC addresses as well as DHCP ones.
Thanks to Dave Taht for help with this.
Add --dhcp-duid to allow DUID-EN uids to be used.
Explicity send DHCPv6 replies to the correct port, instead
of relying on clients to send requests with the correct
source address, since at least one client in the wild gets
this wrong. Thanks to Conrda Kostecki for help tracking
this down.
Send a preference value of 255 in DHCPv6 replies when
--dhcp-authoritative is in effect. This tells clients not
to wait around for other DHCP servers.
Better logging of DHCPv6 options.
Add --host-record. Thanks to Rob Zwissler for the
suggestion.
Invoke the DHCP script with action "tftp" when a TFTP file
transfer completes. The size of the file, address to which
it was sent and complete pathname are supplied. Note that
version 2.60 introduced some script incompatibilties
associated with DHCPv6, and this is a further change. To
be safe, scripts should ignore unknown actions, and if
not IPv6-aware, should exit if the environment
variable DNSMASQ_IAID is set. The use-case for this is
to track netboot/install. Suggestion from Shantanu
Gadgil.
Update contrib/port-forward/dnsmasq-portforward to reflect
the above.
Set the environment variable DNSMASQ_LOG_DHCP when running
the script id --log-dhcp is in effect, so that script can
taylor their logging verbosity. Suggestion from Malte
Forkel.
Arrange that addresses specified with --listen-address
work even if there is no interface carrying the
address. This is chiefly useful for IPv4 loopback
addresses, where any address in 127.0.0.0/8 is a valid
loopback address, but normally only 127.0.0.1 appears on
the lo interface. Thanks to Mathieu Trudel-Lapierre for
the idea and initial patch.
Fix crash, introduced in 2.60, when a DHCPINFORM is
received from a network which has no valid dhcp-range.
Thanks to Stephane Glondu for the bug report.
Add a new DHCP lease time keyword, "deprecated" for
--dhcp-range. This is only valid for IPv6, and sets the
preffered lease time for both DHCP and RA to zero. The
effect is that clients can continue to use the address
for existing connections, but new connections will use
other addresses, if they exist. This makes hitless
renumbering at least possible.
Fix bug in address6_available() which caused DHCPv6 lease
aquistion to fail if more than one dhcp-range in use.
Provide RDNSS and DNSSL data in router advertisements,
using the settings provided for DHCP options
option6:domain-search and option6:dns-server.
Tweak logo/favicon.ico to add some transparency. Thanks to
SamLT for work on this.
Don't cache data from non-recursive nameservers, since it
may erroneously look like a valid CNAME to a non-exitant
name. Thanks to Ben Winslow for finding this.
Call SO_BINDTODEVICE on the DHCP socket(s) when doing DHCP
on exacly one interface and --bind-interfaces is set. This
makes the OpenStack use-case of one dnsmasq per virtual
interface work. This is only available on Linux; it's not
supported on other platforms. Thanks to Vishvananda Ishaya
and the OpenStack team for the suggestion.
Updated French translation. Thanks to Gildas Le Nadan.
Give correct from-cache answers to explict CNAME queries.
Thanks to Rob Zwissler for spotting this.
Add --tftp-lowercase option. Thanks to Oliver Rath for the
patch.
Ensure that the DBus DhcpLeaseUpdated events are generated
when a lease goes through INIT_REBOOT state, even if the
dhcp-script is not in use. thanks to Antoaneta-Ecaterina
Ene for the patch.
Fix failure of TFTP over IPv4 on OpenBSD platform. Thanks
to Brad Smith for spotting this.
version 2.60
Fix compilation problem in Mac OS X Lion. Thanks to Olaf
Flebbe for the patch.

View File

@@ -65,7 +65,7 @@ version = -DVERSION='\"`$(top)/bld/get-version $(top)`\"'
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 \
helper.o tftp.o log.o conntrack.o dhcp6.o rfc3315.o \
dhcp-common.o outpacket.o radv.o
dhcp-common.o outpacket.o radv.o slaac.o
hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \
dns-protocol.h radv-protocol.h

View File

@@ -8,7 +8,7 @@ LOCAL_SRC_FILES := bpf.c cache.c dbus.c dhcp.c dnsmasq.c \
netlink.c network.c option.c rfc1035.c \
rfc2131.c tftp.c util.c conntrack.c \
dhcp6.c rfc3315.c dhcp-common.c outpacket.c \
radv.c
radv.c slaac.c
LOCAL_MODULE := dnsmasq

View File

@@ -18,7 +18,7 @@ else
vers=`cat $1/VERSION | sed 's/[(), ]/,/ g' | tr ',' '\n' | grep $v[0-9]`
if [ $? -eq 0 ]; then
echo "${vers}" | head -n 1 | tail -c +2
echo "${vers}" | head -n 1 | sed 's/^v//'
else
cat $1/VERSION
fi

View File

@@ -34,11 +34,21 @@ if [ ${DNSMASQ_OLD_HOSTNAME} ] && [ ${action} = old ] ; then
hostname=${DNSMASQ_OLD_HOSTNAME}
fi
# IPv6 leases are not our concern. no NAT there!
if [ ${DNSMASQ_IAID} ] ; then
exit 0
fi
# action init is not relevant, and will only be seen when leasefile-ro is set.
if [ ${action} = init ] ; then
exit 0
fi
# action tftp is not relevant.
if [ ${action} = tftp ] ; then
exit 0
fi
if [ ${hostname} ]; then
ports=$(sed -n -e "/^${hostname}\ .*/ s/^.* //p" ${PORTSFILE})

16
debian/changelog vendored
View File

@@ -1,3 +1,19 @@
dnsmasq (2.61-1) unstable; urgency=low
* New upstream.
* Provide "dump-stats" initscript method. (closes: #654656)
* Add (empty) build-indep and build-arch rules targets.
* Bump standards-version to 3.9.3
* Add port option to example dnsmasq.conf (closes: #668386)
-- Simon Kelley <simon@thekelleys.org.uk> Tue, 6 Mar 2012 19:45:43 +0000
dnsmasq (2.60-2) unstable; urgency=high
* Fix DHCPv4 segfault. (closes: #665008)
-- Simon Kelley <simon@thekelleys.org.uk> Fri, 23 Mar 2012 09:37:23 +0000
dnsmasq (2.60-1) unstable; urgency=low
* New upstream.

2
debian/control vendored
View File

@@ -3,7 +3,7 @@ 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
Standards-Version: 3.9.3
Package: dnsmasq
Architecture: all

5
debian/init vendored
View File

@@ -256,8 +256,11 @@ case "$1" in
*) log_success_msg "(unknown)" ; exit 4 ;;
esac
;;
dump-stats)
kill -s USR1 `cat /var/run/dnsmasq/$NAME.pid`
;;
*)
echo "Usage: /etc/init.d/$NAME {start|stop|restart|force-reload|status}" >&2
echo "Usage: /etc/init.d/$NAME {start|stop|restart|force-reload|dump-stats|status}" >&2
exit 3
;;
esac

3
debian/rules vendored
View File

@@ -184,7 +184,10 @@ endef
# Below here is fairly generic really
binary: binary-arch binary-indep
build:
build-arch:
build-indep:
checkroot:
test root = "`whoami`"

View File

@@ -4,6 +4,11 @@
# as the long options legal on the command line. See
# "/usr/sbin/dnsmasq --help" or "man 8 dnsmasq" for details.
# Listen on this specific port instead of the standard DNS port
# (53). Setting this to zero completely disables DNS function,
# leaving only DHCP and/or TFTP.
#port=5353
# The following two options make you a better netizen, since they
# tell dnsmasq to filter out queries which the public DNS cannot
# answer, and which load the servers (especially the root servers)
@@ -164,10 +169,37 @@
# Do Router Advertisements, BUT NOT DHCP for this subnet.
#dhcp-range=1234::, ra-only
# Do Router Advertisements, BUT NOT DHCP for this subnet, also try and
# add names to the DNS for the IPv6 address of SLAAC-configured dual-stack
# hosts. Use the DHCPv4 lease to derive the name, network segment and
# MAC address and assume that the host will also have an
# IPv6 address calculated using the SLAAC alogrithm.
#dhcp-range=1234::, ra-names
# Do Router Advertisements, BUT NOT DHCP for this subnet.
# Set the lifetime to 46 hours. (Note: minimum lifetime is 2 hours.)
#dhcp-range=1234::, ra-only, 48h
# Do DHCP and Router Advertisements for this subnet. Set the A bit in the RA
# so that clients can use SLAAC addresses as well as DHCP ones.
#dhcp-range=1234::2, 1234::500, slaac
# Do Router Advertisements and stateless DHCP for this subnet. Clients will
# not get addresses from DHCP, but they will get other configuration information.
# They will use SLAAC for addresses.
#dhcp-range=1234::, ra-stateless
# Do stateless DHCP, SLAAC, and generate DNS names for SLAAC addresses
# from DHCPv4 leases.
#dhcp-range=1234::, ra-stateless, ra-names
# Do router advertisements for all subnets where we're doing DHCPv6
# Unless overriden by ra-stateless, ra-names, et al, the router
# advertisements will have the M and O bits set, so that the clients
# get addresses and configuration from DHCPv6, and the A bit reset, so the
# clients don't use SLAAC addresses.
#enable-ra
# Supply parameters for specified hosts using DHCP. There are lots
# of valid alternatives, so we will give examples of each. Note that
# IP addresses DO NOT have to be in the range given above, they just

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -23,7 +23,7 @@ options. It includes a secure, read-only,
TFTP server to allow net/PXE boot of DHCP hosts and also supports BOOTP.
.PP
Dnsmasq
supports IPv6 for DNS and TFTP, but not DHCP.
supports IPv6 for all functions and a minimal router-advertisement daemon.
.SH OPTIONS
Note that in general missing parameters are allowed and switch off
functions, for instance "--pid-file" disables writing a PID file. On
@@ -416,6 +416,24 @@ zone files: the port, weight and priority numbers are in a different
order. More than one SRV record for a given service/domain is allowed,
all that match are returned.
.TP
.B --host-record=<name>[,<name>....][<IPv4-address>],[<IPv6-address>]
Add A, AAAA and PTR records to the DNS. This adds one or more names to
the DNS with associated IPv4 (A) and IPv6 (AAAA) records. A name may
appear in more than one
.B host-record
and therefore be assigned more than one address. Only the first
address creates a PTR record linking the address to the name. This is
the same rule as is used reading hosts-files.
.B host-record
options are considered to be read before host-files, so a name
appearing there inhibits PTR-record creation if it appears in
hosts-file also. Unlike hosts-files, names are not expanded, even when
.B expand-hosts
is in effect. Short and long names may appear in the same
.B host-record,
eg.
.B --host-record=laptop,laptop.thekelleys.org,192.168.0.1,1234::100
.TP
.B \-Y, --txt-record=<name>[[,<text>],<text>]
Return a TXT DNS record. The value of TXT record is a set of strings,
so any number may be included, delimited by commas; use quotes to put
@@ -494,9 +512,9 @@ 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>][,<mode>][,<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>]
.B \-F, --dhcp-range=[interface:<interface>,][tag:<tag>[,tag:<tag>],][set:<tag],]<start-IPv6addr>[,<end-IPv6addr>][,<mode>][,<prefix-len>][,<lease time>]
Enable the DHCP server. Addresses will be given out from the range
<start-addr> to <end-addr> and from statically defined addresses given
@@ -506,8 +524,12 @@ options. If the lease time is given, then leases
will be given for that length of time. The lease time is in seconds,
or minutes (eg 45m) or hours (eg 1h) or "infinite". If not given,
the default lease time is one hour. The
minimum lease time is two minutes. This
option may be repeated, with different addresses, to enable DHCP
minimum lease time is two minutes. For IPv6 ranges, the lease time
maybe "deprecated"; this sets the preferred lifetime sent in a DHCP
lease or router advertisement to zero, which causes clients to use
other addresses, if available, for new connections as a prelude to renumbering.
This option may be repeated, with different addresses, to enable DHCP
service to more than one network. For directly connected networks (ie,
networks on which the machine running dnsmasq has an interface) the
netmask is optional: dnsmasq will determine it from the interface
@@ -532,7 +554,7 @@ When it is prefixed with 'tag:' instead, then its meaning changes from setting
a tag to matching it. Only one tag may be set, but more than one tag
may be matched.
The end address may be replaced by the keyword
The optional <mode> keyword may be
.B static
which tells dnsmasq to enable DHCP for the network specified, but not
to dynamically allocate IP addresses: only hosts which have static
@@ -540,23 +562,50 @@ addresses given via
.B dhcp-host
or from /etc/ethers will be served.
The end address may be replaced by
the keyword
For IPv4, the <mode> may be
.B proxy
in which case dnsmasq will provide proxy-DHCP on the specified
subnet. (See
.B pxe-prompt
and
.B pxe-service
for details, applies to IPv4 only.)
for details.)
For IPv6, the mode may be some combination of
.B ra-only, slaac, ra-names, ra-stateless.
The end address may be replaced by
the keyword
.B ra-only
which tells dnsmasq to offer Router Advertisement only on this subnet,
and not DHCP. This applies to IPv6 only, see
.B enable-ra
for details.
tells dnsmasq to offer Router Advertisement only on this subnet,
and not DHCP.
.B slaac
tells dnsmasq to offer Router Advertisement on this subnet and to set
the A bit in the router advertisement, so that the client will use
SLAAC addresses. When used with a DHCP range or static DHCP address
this results in the client having both a DHCP-assigned and a SLAAC
address.
.B ra-stateless
sends router advertisements with the O and A bits set, and provides a
stateless DHCP service. The client will use a SLAAC address, and use
DHCP for other configuration information.
.B ra-names
enables a mode
which gives DNS names to dual-stack hosts which do SLAAC for
IPv6. Dnsmasq uses the host's IPv4 lease to derive the name, network
segment and MAC address and assumes that the host will also have an
IPv6 address calculated using the SLAAC algorithm, on the same network
segment. The address is pinged, and if a reply is received, an AAAA
record is added to the DNS for this IPv6
address. Note that this is only happens for directly-connected
networks, (not one doing DHCP via a relay) and it will not work
if a host is using privacy extensions.
.B ra-names
can be combined with
.B ra-stateless
and
.B slaac.
The interface:<interface name> section is not normally used. See the
NOTES section for details of this.
@@ -1031,8 +1080,19 @@ the tags used to determine them.
.B \-l, --dhcp-leasefile=<path>
Use the specified file to store DHCP lease information.
.TP
.B --dhcp-duid=<enterprise-id>,<uid>
(IPv6 only) Specify the server persistent UID which the DHCPv6 server
will use. This option is not normally required as dnsmasq creates a
DUID automatically when it is first needed. When given, this option
provides dnsmasq the data required to create a DUID-EN type DUID. Note
that once set, the DUID is stored in the lease database, so to change between DUID-EN and
automatically created DUIDs or vice-versa, the lease database must be
re-intialised. The enterprise-id is assigned by IANA, and the uid is a
string of hex octets unique to a particular device.
.TP
.B \-6 --dhcp-script=<path>
Whenever a new DHCP lease is created, or an old one destroyed, the
Whenever a new DHCP lease is created, or an old one destroyed, or a
TFTP file transfer completes, the
executable specified by this option is run. <path>
must be an absolute pathname, no PATH search occurs.
The arguments to the process
@@ -1082,6 +1142,10 @@ is known.
DNSMASQ_TAGS contains all the tags set during the
DHCP transaction, separated by spaces.
DNSMASQ_LOG_DHCP is set if
.B --log-dhcp
is in effect.
For IPv4 only:
DNSMASQ_CLIENT_ID if the host provided a client-id.
@@ -1108,6 +1172,8 @@ only supplied for
since these data are not held in dnsmasq's lease
database.
All file descriptors are
closed except stdin, stdout and stderr which are open to /dev/null
(except in debug mode).
@@ -1126,6 +1192,17 @@ all existing leases as they are read from the lease file. Expired
leases will be called with "del" and others with "old". When dnsmasq
receives a HUP signal, the script will be invoked for existing leases
with an "old " event.
There are two further actions which may appear as the first argument
to the script, "init" and "tftp". More may be added in the future, so
scripts should be written to ignore unknown actions. "init" is
described below in
.B --leasefile-ro
The "tftp" action is invoked when a TFTP file transfer completes: the
arguments are the file size in bytes, the address to which the file
was sent, and the complete pathname of the file.
.TP
.B --dhcp-luascript=<path>
Specify a script written in Lua, to be run when leases are created,
@@ -1139,11 +1216,13 @@ function, and may provide
and
.B shutdown
functions, which are called, without arguments when dnsmasq starts up
and terminates.
and terminates. It may also provide a
.B tftp
function.
The
.B lease
method receives the information detailed in
function receives the information detailed in
.B --dhcp-script.
It gets two arguments, firstly the action, which is a string
containing, "add", "old" or "del", and secondly a table of tag value
@@ -1160,7 +1239,16 @@ for IPv4, and
.B client_duid, ip_address
and
.B hostname
for IPv6.
for IPv6.
The
.B tftp
function is called in the same way as the lease function, and the
table holds the tags
.B destination_address,
.B file_name
and
.B file_size.
.TP
.B --dhcp-scriptuser
Specify the user as which to run the lease-change script or Lua script. This defaults to root, but can be changed to another user using this flag.
@@ -1255,16 +1343,14 @@ only a subset of this is needed, and dnsmasq can handle it, using
existing DHCP configuration to provide most data. When RA is enabled,
dnsmasq will advertise a prefix for each dhcp-range, with default
router and recursive DNS server as the relevant link-local address on
the machine running dnsmasq. The "managed address" bits are set,
except for a dhcp-range which is marked as "ra-only", in which case RA
is provided but no DHCPv6 service and the managed address bits are
cleared.
.B enable-ra
enables router advertisement for prefixes where dnsmasq is doing
DHCPv6. It is not needed to "ra-only" prefixes. Creating an "ra-only"
prefix and not setting
.B enable-ra
sends advertisements only to "ra-only" prefixes.
the machine running dnsmasq. By default, he "managed address" bits are set, and
the "use SLAAC" bit is reset. This can be changed for individual
subnets with the mode keywords described in
.B --dhcp-range.
RFC6106 DNS parameters are included in the advertisements. By default,
the relevant link-local address of the machine running dnsmasq is sent
as recursive DNS server. If provided, the DHCPv6 options dns-server and
domain-search are used for RDNSS and DNSSL.
.TP
.B --enable-tftp[=<interface>]
Enable the TFTP server function. This is deliberately limited to that
@@ -1299,6 +1385,12 @@ are accessible. It is not recommended to run dnsmasq as root with TFTP
enabled, and certainly not without specifying --tftp-root. Doing so
can expose any world-readable file on the server to any host on the net.
.TP
.B --tftp-lowercase
Convert filenames in TFTP requests to all lowercase. This is useful
for requests from Windows machines, which have case-insensitive
filesystems and tend to play fast-and-loose with case in filenames.
Note that dnsmasq's tftp server always converts "\\" to "/" in filenames.
.TP
.B --tftp-max=<connections>
Set the maximum number of concurrent TFTP connections allowed. This
defaults to 50. When serving a large number of TFTP connections,

View File

@@ -22,7 +22,8 @@ peut être configuré pour envoyer n'importe quel option DHCP.
Il inclut un serveur TFTP sécurisé en lecture seule permettant le démarrage via
le réseau/PXE de clients DHCP et supporte également le protocole BOOTP.
.PP
Dnsmasq supporte IPv6 pour le DNS et TFTP mais pas pour le DHCP.
Dnsmasq supporte IPv6 et contient un démon minimaliste capable de faire des
annonces routeurs ("router-advertisements").
.SH OPTIONS
Notes : Il est possible d'utiliser des options sans leur donner de paramètre.
Dans ce cas, la fonction correspondante sera désactivée. Par exemple
@@ -492,6 +493,27 @@ dans un ordre différents. Pour un service/domaine donné, plus d'un
enregistrement SRV est autorisé et tous les enregistrements qui coïncident sont
retournés dans la réponse.
.TP
.B --host-record=<nom>[,<nom>....][<adresse IPv4>],[<adresse IPv6>]
Ajoute des enregistrements A, AAAA et PTR dans le DNS. Ceci permet d'ajouter
un ou plusieurs noms dans le DNS et de les associer à des enregistrements IPv4
(A) ou IPv6 (AAAA). Un nom peut apparaître dans plus d'une entrée
.B host-record
et de fait être associé à plus d'une adresse. Seule la première entrée créée
l'enregistrement PTR associée au nom. Ceci correspond à la même règle que celle
utilisée lors de la lecture du fichier hosts.
Les options
.B host-record
sont considérées lues avant le fichier hosts, ainsi un nom apparaissant dans
une option host-record et dans le fichier hosts n'aura pas d'enregistrement
PTR associé à l'entrée dans le fichier hosts. A l'inverse du fichier hosts, les
noms ne sont pas étendus, même lorsque l'option
.B expand-hosts
est activée. Les noms longs et les noms courts peuvent apparaitre dans la même
entrée
.B host-record,
c-à-d
.B --host-record=laptop,laptop.thekelleys.org,192.168.0.1,1234::100
.TP
.B \-Y, --txt-record=<nom>[[,<texte>],<texte>]
Définit un enregistrement DNS de type TXT. La valeur de l'enregistrement TXT est
un ensemble de chaînes de caractères, donc un nombre variable de chaînes de
@@ -577,11 +599,11 @@ 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.
--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>][,<mode>][,<masque de réseau>[,<broadcast>]][,<durée de bail>]
.TP
.B \-F, --dhcp-range=[interface:<interface>,][tag:<label>[,tag:<label>],][set:<label>],]<addresse IPv6 de début>,<adresse IPv6 de fin>[,<longueur de préfixe>][,<durée de bail>]
.B \-F, --dhcp-range=[interface:<interface>,][tag:<label>[,tag:<label>],][set:<label>],]<addresse IPv6 de début>[,<adresse IPv6 de fin>][,<mode>][,<longueur de préfixe>][,<durée de bail>]
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
statiquement dans l'option
@@ -592,6 +614,13 @@ en heures (exemple : 1h) ou être la chaine de caractère "infinite" pour une
durée indéterminée. Si aucune valeur n'est donnée, une durée de bail par défaut
de une heure est appliquée. La valeur minimum pour un bail DHCP est de 2
minutes.
Pour les plages IPv6, la durée de bail peut-être égale au mot-clef "deprecated"
(obsolète); Cela positionne la durée de vie préférée envoyée dans les baux DHCP
ou les annonces routeurs à zéro, ce qui incite les clients à utiliser d'autres
adresses autant que possible, pour toute nouvelle connexion, en préalable à
la renumérotation.
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
connectés (c'est-à-dire des réseaux dans lesquels la machine sur laquelle tourne
@@ -622,30 +651,59 @@ Lorsque préfixé par 'tag:', la signification change, et au lieu de définir un
label, il définit le label pour laquelle la règle s'applique. Un seul label peut-
être défini mais plusieurs labels peuvent coïncider.
L'adresse de fin peut être remplacée par le mot-clef
Le mot clef optionnel <mode> peut être égal à
.B static
("statique") qui indique à Dnsmasq d'activer le service DHCP pour le réseau
("statique") ce qui indique à Dnsmasq d'activer le service DHCP pour le réseau
spécifié, mais de ne pas activer l'allocation dynamique d'adresses IP : Seuls
les hôtes possédant des adresses IP statiques fournies via
.B dhcp-host
ou présentes dans le fichier /etc/ethers seront alors servis par le DHCP.
L'adresse de fin peut-être remplacée par le mot-clef
Pour IPv4, le <mode> peut est égal à
.B proxy
, auquel cas Dnsmasq fournira un service de DHCP proxy pour le sous-réseau
spécifié. (voir
.B pxe-prompt
et
.B pxe-service
pour plus de détails, s'applique à IPv4 seulement).
pour plus de détails).
Pour IPv6, le mode peut-être une combinaison des valeurs
.B ra-only, slaac, ra-names, ra-stateless.
L'adresse de fin peut être remplacée par le mot-clef
.B ra-only
qui indique à dnsmasq de n'effectuer que des annonces de routeur (Router
Advertisement, RA) sur ce sous-réseau, et de ne pas faire de DHCP. Ceci
s'applique uniquement à IPv6, voir
.B enable-ra
pour plus de détails.
indique à dnsmasq de n'effectuer que des annonces de routeur (Router
Advertisement, RA) sur ce sous-réseau, et de ne pas faire de DHCP.
.B slaac
indique à dnsmasq d'effectuer des annonces de routeur sur ce sous-réseau
et de positionner dans celles-ci le bit A, afin que les clients utilisent
des adresses SLAAC. Lorsqu'utilisé conjointement avec une plage DHCP ou des
affectations statiques d'adresses DHCP, les clients disposeront à la fois
d'adresses DHCP assignées et d'adresses SLAAC.
.B ra-stateless
indique à dnsmasq d'effectuer des annonces de routeur avec les bits 0 et A
positionnés, et de fournir un service DHCP sans état ("stateless"). Les clients
utiliseront des adresses SLAAC, et utiliseront DHCP pour toutes les autres
informations de configuration.
.B ra-names
active un mode qui fourni des noms DNS aux hôtes fonctionnant en double pile
("dual stack") et configurés pour faire du SLAAC en IPv6. Dnsmasq utilise le
bail IPv4 de l'hôte afin de dériver le nom, le segment de réseau et l'adresse
MAC et assume que l'hôte disposera d'une adresse IPv6 calculée via l'algorithme
SLAAC, sur le même segment de réseau. Un ping est envoyé à l'adresse, et si une
réponse est obtenue, un enregistrement AAAA est rajouté dans le DNS pour cette
adresse IPv6. Veuillez-noter que cela n'arrive que pour les réseaux directement
connectés (et non ceux pour lesquels DHCP se fait via relai), et ne
fonctionnera pas si un hôte utilise les "extensions de vie privée"
("privacy extensions").
.B ra-names
peut-être combiné avec
.B ra-stateless
et
.B slaac.
La section interface:<nom d'interface> n'est normalement pas utilisée. Se
référer aux indications de la section NOTES pour plus de détail à ce sujet.
@@ -1170,9 +1228,21 @@ détermination de celles-ci.
.B \-l, --dhcp-leasefile=<chemin de fichier>
Utilise le fichier dont le chemin est fourni pour stocker les informations de
baux DHCP.
.TP
.TP
.B --dhcp-duid=<ID d'entreprise>,<uid>
(IPv6 seulement) Spécifie le numéro d'UID de serveur persistant que le serveur
DHCPv6 doit utiliser. Cette option n'est normalement pas requise, Dnsmasq
créant un DUID automatiquement lorsque cela est nécessaire. Lorsque cette
option est positionnée, elle fournit à Dnsmasq les données nécessaires à la
création d'un DUID de type DUID-EN. Veuillez noter qu'une fois créé, le DUID
est stocké dans la base des baux, aussi changer entre un DUID créé
automatiquement et un DUID-EN et vice-versa impose de réinitialiser la base de
baux. Le numéro d'ID d'entreprise est assigné par l'IANA, et l'uid est une
chaine hexadécimale unique à chaque serveur.
.TP
.B \-6 --dhcp-script=<chemin de fichier>
Lorsqu'un bail DHCP est créé, ou qu'un ancien est supprimé, le fichier dont le
Lorsqu'un bail DHCP est créé, qu'un ancien est supprimé, ou qu'un transfert
TFTP est terminé, le fichier dont le
chemin est spécifié est exécuté. Le <chemin de fichier> doit être un chemin
absolu, aucune recherche n'est effectuée via la variable d'environnement PATH.
Les arguments fournis à celui-ci sont soit
@@ -1223,6 +1293,10 @@ relai DHCP pour contacter Dnsmasq, si l'adresse IP du relai est connue.
DNSMASQ_TAGS contient tous les labels fournis pendant la transaction DHCP,
séparés par des espaces.
DNSMASQ_LOG_DHCP est positionné si
.B --log-dhcp
est activé.
Pour IPv4 seulement :
DNSMASQ_CLIENT_ID, si l'hôte a fourni un identifiant de client.
@@ -1262,6 +1336,16 @@ Au démarrage de Dnsmasq, le script sera invoqué pour chacun des baux existants
dans le fichier des baux. Le script sera lancé avec l'action "del" pour les
baux expirés, et "old" pour les autres. Lorsque Dnsmasq reçoit un signal HUP,
le script sera invoqué avec une action "old" pour tous les baux existants.
Il existe deux autres actions pouvant apparaître comme argument au script :
"init" et "tftp". D'autres sont susceptibles d'être rajoutées dans le futur,
aussi les scripts devraient-être écrits de sorte à ignorer les actions
inconnues. "init" est décrite ci-dessous dans
.B --leasefile-ro.
L'action "tftp" est invoquée lorsqu'un transfert de fichier TFTP s'est
terminé. Ses arguments sont la taille du fichier en octets, l'adresse à
laquelle le fichier a été envoyé, ainsi que le chemin complet du fichier.
.TP
.B --dhcp-luascript=<chemin>
Spécifie un script écrit en Lua, devant être exécuté lorsque des baux sont
@@ -1276,8 +1360,10 @@ et peut fournir des fonctions
et
.B shutdown
qui sont appellées, sans arguments, lorsque dnsmasq démarre ou s'arrête.
Il peut également fournir une fonction
.B tftp.
La méthode
La fonction
.B lease
reçoit les informations détaillées dans
.B --dhcp-script.
@@ -1301,6 +1387,16 @@ et
ainsi que
.B hostname
(le nom d'hôte) dans le cas d'IPv6.
La fonction
.B tftp
est appelée de la même façon que la fonction "lease", et la table contient les
labels
.B destination_address,
.B file_name
et
.B file_size
(respectivement "adresse de destination", "nom de fichier" et "taille de fichier").
.TP
.B --dhcp-scriptuser
Spécifie l'utilisateur sous lequel le script shell lease-change ou le script
@@ -1367,7 +1463,7 @@ Si la gamme d'adresse est fournie sous la forme
qui a pour effect d'ajouter --local-declarations aux requêtes DNS directes et
inverses. C-à-d
.B --domain=thekelleys.org.uk,192.168.0.0/24,local
est indentique à
est identique à
.B --domain=thekelleys.org.uk,192.168.0.0/24
--local=/thekelleys.org.uk/ --local=/0.168.192.in-addr.arpa/
La taille de réseau doit-être de 8, 16 ou 24 pour être valide.
@@ -1411,10 +1507,15 @@ Advertisement") sont activées, dnsmasq va annoncer un préfixe pour chaque
dhcp-range et, par défaut, fournir comme valeur de routeur et de DNS récursif
la valeur d'adresse link-local appropriée parmi celles de la machine sur
laquelle tourne dnsmasq.
Les bits "managed address" sont positionnés, sauf pour un dhcp-range marqué
comme "ra-only" (annonce routeur uniquement). Dans ce cas le service d'annonce
routeur est rendu mais aucun service DHCPv6 n'est assuré, et les bits "managed
address" ne sont pas positionnés.
Par défaut, les bits "managed address" sont positionnés, et le bit "use SLAAC"
("utiliser SLAAC") est réinitialisé. Cela peut-être changé pour des
sous-réseaux donnés par le biais du mot clef de mode décris dans
.B --dhcp-range.
Les paramètres DNS du RFC6106 sont inclus dans les annonces. Par défaut,
l'adresse link-local appropriée parmi celles de la machine sur laquelle tourne
dnsmasq est spécifiée comme DNS récursif. Si elles sont fournies, les
options dns-server et domain-search sont utilisées respectivement pour RDNSS et
DNSSL.
.TP
.B --enable-tftp[=<interface>]
Active la fonction serveur TFTP. Celui-ci est de manière délibérée limité aux

View File

@@ -1128,9 +1128,10 @@ msgstr "DHCP, Proxy im Subnetz %.0s%s%.0s"
msgid "DHCP, IP range %s -- %s, lease time %s"
msgstr "DHCP, IP-Bereich %s - %s, Lease-Zeit %s "
# FIXME: this and the next few must be full strings to be translatable - do not assemble in code"
#: dnsmasq.c:588
msgid "root is "
msgstr "FIXME: this and the next few must be full strings to be translatable - do not assemble in code"
msgstr ""
#: dnsmasq.c:588
msgid "enabled"

147
src/bpf.c
View File

@@ -17,19 +17,9 @@
#include "dnsmasq.h"
#if defined(HAVE_BSD_NETWORK) || defined(HAVE_SOLARIS_NETWORK)
static struct iovec ifconf = {
.iov_base = NULL,
.iov_len = 0
};
static struct iovec ifreq = {
.iov_base = NULL,
.iov_len = 0
};
#include <ifaddrs.h>
#if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
#include <sys/sysctl.h>
#include <net/route.h>
#include <net/if_dl.h>
@@ -50,8 +40,12 @@ int arp_enumerate(void *parm, int (*callback)())
struct rt_msghdr *rtm;
struct sockaddr_inarp *sin2;
struct sockaddr_dl *sdl;
struct iovec buff;
int rc;
buff.iov_base = NULL;
buff.iov_len = 0;
mib[0] = CTL_NET;
mib[1] = PF_ROUTE;
mib[2] = 0;
@@ -67,9 +61,9 @@ int arp_enumerate(void *parm, int (*callback)())
while (1)
{
if (!expand_buf(&ifconf, needed))
if (!expand_buf(&buff, needed))
return 0;
if ((rc = sysctl(mib, 6, ifconf.iov_base, &needed, NULL, 0)) == 0 ||
if ((rc = sysctl(mib, 6, buff.iov_base, &needed, NULL, 0)) == 0 ||
errno != ENOMEM)
break;
needed += needed / 8;
@@ -77,7 +71,7 @@ int arp_enumerate(void *parm, int (*callback)())
if (rc == -1)
return 0;
for (next = ifconf.iov_base ; next < (char *)ifconf.iov_base + needed; next += rtm->rtm_msglen)
for (next = buff.iov_base ; next < (char *)buff.iov_base + needed; next += rtm->rtm_msglen)
{
rtm = (struct rt_msghdr *)next;
sin2 = (struct sockaddr_inarp *)(rtm + 1);
@@ -88,19 +82,14 @@ int arp_enumerate(void *parm, int (*callback)())
return 1;
}
#endif
int iface_enumerate(int family, void *parm, int (*callback)())
{
char *ptr;
struct ifreq *ifr;
struct ifconf ifc;
int fd, errsav, ret = 0;
int lastlen = 0;
size_t len = 0;
struct ifaddrs *head, *addrs;
int errsav, ret = 0;
if (family == AF_UNSPEC)
#if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
return arp_enumerate(parm, callback);
@@ -112,95 +101,62 @@ int iface_enumerate(int family, void *parm, int (*callback)())
if (family == AF_LOCAL)
family = AF_LINK;
if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
if (getifaddrs(&head) == -1)
return 0;
while(1)
{
len += 10*sizeof(struct ifreq);
if (!expand_buf(&ifconf, len))
goto err;
ifc.ifc_len = len;
ifc.ifc_buf = ifconf.iov_base;
if (ioctl(fd, SIOCGIFCONF, &ifc) == -1)
{
if (errno != EINVAL || lastlen != 0)
goto err;
}
else
{
if (ifc.ifc_len == lastlen)
break; /* got a big enough buffer now */
lastlen = ifc.ifc_len;
}
}
for (ptr = ifc.ifc_buf; ptr < (char *)(ifc.ifc_buf + ifc.ifc_len); ptr += len)
{
/* subsequent entries may not be aligned, so copy into
an aligned buffer to avoid nasty complaints about
unaligned accesses. */
len = sizeof(struct ifreq);
#ifdef HAVE_SOCKADDR_SA_LEN
ifr = (struct ifreq *)ptr;
if (ifr->ifr_addr.sa_len > sizeof(ifr->ifr_ifru))
len = ifr->ifr_addr.sa_len + offsetof(struct ifreq, ifr_ifru);
#endif
if (!expand_buf(&ifreq, len))
goto err;
ifr = (struct ifreq *)ifreq.iov_base;
memcpy(ifr, ptr, len);
if (ifr->ifr_addr.sa_family == family)
for (addrs = head; addrs; addrs = addrs->ifa_next)
{
if (addrs->ifa_addr->sa_family == family)
{
int iface_index = if_nametoindex(addrs->ifa_name);
if (iface_index == 0)
continue;
if (family == AF_INET)
{
struct in_addr addr, netmask, broadcast;
broadcast.s_addr = 0;
addr = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr;
if (ioctl(fd, SIOCGIFNETMASK, ifr) == -1)
continue;
netmask = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr;
if (ioctl(fd, SIOCGIFBRDADDR, ifr) != -1)
broadcast = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr;
if (!((*callback)(addr,
(int)if_nametoindex(ifr->ifr_name),
netmask, broadcast,
parm)))
addr = ((struct sockaddr_in *) addrs->ifa_addr)->sin_addr;
netmask = ((struct sockaddr_in *) addrs->ifa_netmask)->sin_addr;
broadcast = ((struct sockaddr_in *) addrs->ifa_broadaddr)->sin_addr;
if (!((*callback)(addr, iface_index, netmask, broadcast, parm)))
goto err;
}
#ifdef HAVE_IPV6
else if (family == AF_INET6)
{
struct in6_addr *addr = &((struct sockaddr_in6 *)&ifr->ifr_addr)->sin6_addr;
struct in6_addr *addr = &((struct sockaddr_in6 *) addrs->ifa_addr)->sin6_addr;
unsigned char *netmask = (unsigned char *) &((struct sockaddr_in6 *) addrs->ifa_netmask)->sin6_addr;
int scope_id = ((struct sockaddr_in6 *) addrs->ifa_addr)->sin6_scope_id;
int i, j, prefix = 0;
for (i = 0; i < IN6ADDRSZ; i++, prefix += 8)
if (netmask[i] != 0xff)
break;
if (i != IN6ADDRSZ && netmask[i])
for (j = 7; j > 0; j--, prefix++)
if ((netmask[i] & (1 << j)) == 0)
break;
/* voodoo to clear interface field in address */
if (!option_bool(OPT_NOWILD) && IN6_IS_ADDR_LINKLOCAL(addr))
{
addr->s6_addr[2] = 0;
addr->s6_addr[3] = 0;
}
/* 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)if_nametoindex(ifr->ifr_name), 0,
parm)))
if (!((*callback)(addr, prefix, scope_id, iface_index, 0, parm)))
goto err;
}
}
#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)((int)if_nametoindex(ifr->ifr_name),
ARPHRD_ETHER, LLADDR(sdl), sdl->sdl_alen, parm)))
struct sockaddr_dl *sdl = (struct sockaddr_dl *) addrs->ifa_addr;
if (sdl->sdl_alen != 0 &&
!((*callback)(iface_index, ARPHRD_ETHER, LLADDR(sdl), sdl->sdl_alen, parm)))
goto err;
}
#endif
@@ -211,7 +167,7 @@ int iface_enumerate(int family, void *parm, int (*callback)())
err:
errsav = errno;
close(fd);
freeifaddrs(head);
errno = errsav;
return ret;
@@ -228,13 +184,10 @@ void init_bpf(void)
while (1)
{
/* useful size which happens to be sufficient */
if (expand_buf(&ifreq, sizeof(struct ifreq)))
{
sprintf(ifreq.iov_base, "/dev/bpf%d", i++);
if ((daemon->dhcp_raw_fd = open(ifreq.iov_base, O_RDWR, 0)) != -1)
return;
}
sprintf(daemon->dhcp_buff, "/dev/bpf%d", i++);
if ((daemon->dhcp_raw_fd = open(daemon->dhcp_buff, O_RDWR, 0)) != -1)
return;
if (errno != EBUSY)
die(_("cannot create DHCP BPF socket: %s"), NULL, EC_BADNET);
}

View File

@@ -25,6 +25,9 @@ static int cache_inserted = 0, cache_live_freed = 0, insert_error;
static union bigname *big_free = NULL;
static int bignames_left, hash_size;
static int uid = 0;
#ifdef HAVE_DNSSEC
static struct keydata *keyblock_free = NULL;
#endif
/* type->string mapping: this is also used by the name-hash function as a mixing table. */
static const struct {
@@ -190,6 +193,10 @@ static void cache_free(struct crec *crecp)
big_free = crecp->name.bname;
crecp->flags &= ~F_BIGNAME;
}
#ifdef HAVE_DNSSEC
else if (crecp->flags & (F_DNSKEY | F_DS))
keydata_free(crecp->addr.key.keydata);
#endif
}
/* insert a new cache entry at the head of the list (youngest entry) */
@@ -233,7 +240,11 @@ static int is_outdated_cname_pointer(struct crec *crecp)
if (!(crecp->flags & F_CNAME))
return 0;
if (crecp->addr.cname.cache && crecp->addr.cname.uid == crecp->addr.cname.cache->uid)
/* NB. record may be reused as DS or DNSKEY, where uid is
overloaded for something completely different */
if (crecp->addr.cname.cache &&
(crecp->addr.cname.cache->flags & (F_IPV4 | F_IPV6)) &&
crecp->addr.cname.uid == crecp->addr.cname.cache->uid)
return 0;
return 1;
@@ -280,7 +291,7 @@ static int cache_scan_free(char *name, struct all_addr *addr, time_t now, unsign
}
}
else if ((crecp->flags & F_FORWARD) &&
((flags & crecp->flags & (F_IPV4 | F_IPV6)) || ((crecp->flags | flags) & F_CNAME)) &&
((flags & crecp->flags & F_TYPE) || ((crecp->flags | flags) & F_CNAME)) &&
hostname_isequal(cache_get_name(crecp), name))
{
if (crecp->flags & (F_HOSTS | F_DHCP))
@@ -360,7 +371,9 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
int freed_all = flags & F_REVERSE;
int free_avail = 0;
log_query(flags | F_UPSTREAM, name, addr, NULL);
/* Don't log keys */
if (flags & (F_IPV4 | F_IPV6))
log_query(flags | F_UPSTREAM, name, addr, NULL);
/* if previous insertion failed give up now. */
if (insert_error)
@@ -452,9 +465,7 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
if (addr)
new->addr.addr = *addr;
else
new->addr.cname.cache = NULL;
new->ttd = now + (time_t)ttl;
new->next = new_chain;
new_chain = new;
@@ -634,10 +645,11 @@ struct crec *cache_find_by_addr(struct crec *crecp, struct all_addr *addr,
return NULL;
}
static void add_hosts_entry(struct crec *cache, struct all_addr *addr, int addrlen,
unsigned short flags, int index, struct crec **rhash, int hashsz)
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_get_name(cache), 0, cache->flags & (F_IPV4 | F_IPV6));
int i, nameexists = 0;
struct cname *a;
unsigned int j;
@@ -670,10 +682,10 @@ static void add_hosts_entry(struct crec *cache, struct all_addr *addr, int addrl
j = (j*2 +((unsigned char *)addr)[i]) % hashsz;
for (lookup = rhash[j]; lookup; lookup = lookup->next)
if ((lookup->flags & flags & (F_IPV4 | F_IPV6)) &&
if ((lookup->flags & cache->flags & (F_IPV4 | F_IPV6)) &&
memcmp(&lookup->addr.addr, addr, addrlen) == 0)
{
flags &= ~F_REVERSE;
cache->flags &= ~F_REVERSE;
break;
}
@@ -684,7 +696,6 @@ static void add_hosts_entry(struct crec *cache, struct all_addr *addr, int addrl
rhash[j] = cache;
}
cache->flags = flags;
cache->uid = index;
memcpy(&cache->addr.addr, addr, addrlen);
cache_hash(cache);
@@ -692,7 +703,7 @@ static void add_hosts_entry(struct crec *cache, struct all_addr *addr, int addrl
/* don't need to do alias stuff for second and subsequent addresses. */
if (!nameexists)
for (a = daemon->cnames; a; a = a->next)
if (hostname_isequal(cache->name.sname, a->target) &&
if (hostname_isequal(cache_get_name(cache), a->target) &&
(lookup = whine_malloc(sizeof(struct crec))))
{
lookup->flags = F_FORWARD | F_IMMORTAL | F_NAMEP | F_HOSTS | F_CNAME;
@@ -771,26 +782,19 @@ static int read_hostsfile(char *filename, int index, int cache_size, struct crec
{
lineno++;
#ifdef HAVE_IPV6
if (inet_pton(AF_INET, token, &addr) > 0)
{
flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4;
addrlen = INADDRSZ;
domain_suffix = get_domain(addr.addr.addr4);
}
#ifdef HAVE_IPV6
else if (inet_pton(AF_INET6, token, &addr) > 0)
{
flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6;
addrlen = IN6ADDRSZ;
domain_suffix = get_domain6(&addr.addr.addr6);
}
#else
if ((addr.addr.addr4.s_addr = inet_addr(token)) != (in_addr_t) -1)
{
flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4;
addrlen = INADDRSZ;
domain_suffix = get_domain(addr.addr.addr4);
}
#endif
else
{
@@ -830,13 +834,15 @@ static int read_hostsfile(char *filename, int index, int cache_size, struct crec
strcpy(cache->name.sname, canon);
strcat(cache->name.sname, ".");
strcat(cache->name.sname, domain_suffix);
add_hosts_entry(cache, &addr, addrlen, flags, index, rhash, hashsz);
cache->flags = flags;
add_hosts_entry(cache, &addr, addrlen, index, rhash, hashsz);
name_count++;
}
if ((cache = whine_malloc(sizeof(struct crec) + strlen(canon)+1-SMALLDNAME)))
{
strcpy(cache->name.sname, canon);
add_hosts_entry(cache, &addr, addrlen, flags, index, rhash, hashsz);
cache->flags = flags;
add_hosts_entry(cache, &addr, addrlen, index, rhash, hashsz);
name_count++;
}
free(canon);
@@ -860,6 +866,8 @@ void cache_reload(void)
struct crec *cache, **up, *tmp;
int revhashsz, i, total_size = daemon->cachesize;
struct hostsfile *ah;
struct host_record *hr;
struct name_list *nl;
cache_inserted = cache_live_freed = 0;
@@ -886,6 +894,34 @@ void cache_reload(void)
up = &cache->hash_next;
}
/* 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;
/* Do host_records in config. */
for (hr = daemon->host_records; hr; hr = hr->next)
for (nl = hr->names; nl; nl = nl->next)
{
if (hr->addr.s_addr != 0 &&
(cache = whine_malloc(sizeof(struct crec))))
{
cache->name.namep = nl->name;
cache->flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4 | F_NAMEP | F_CONFIG;
add_hosts_entry(cache, (struct all_addr *)&hr->addr, INADDRSZ, 0, (struct crec **)daemon->packet, revhashsz);
}
#ifdef HAVE_IPV6
if (!IN6_IS_ADDR_UNSPECIFIED(&hr->addr6) &&
(cache = whine_malloc(sizeof(struct crec))))
{
cache->name.namep = nl->name;
cache->flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6 | F_NAMEP | F_CONFIG;
add_hosts_entry(cache, (struct all_addr *)&hr->addr6, IN6ADDRSZ, 0, (struct crec **)daemon->packet, revhashsz);
}
#endif
}
if (option_bool(OPT_NO_HOSTS) && !daemon->addn_hosts)
{
if (daemon->cachesize > 0)
@@ -893,12 +929,6 @@ void cache_reload(void)
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))
total_size = read_hostsfile(HOSTSFILE, 0, total_size, (struct crec **)daemon->packet, revhashsz);
@@ -1131,22 +1161,35 @@ void dump_cache(time_t now)
if (!is_outdated_cname_pointer(cache))
a = cache_get_name(cache->addr.cname.cache);
}
#ifdef HAVE_IPV6
#ifdef HAVE_DNSSEC
else if (cache->flags & F_DNSKEY)
{
a = daemon->addrbuff;
sprintf(a, "%3u %u", cache->addr.key.algo, cache->uid);
}
else if (cache->flags & F_DS)
{
a = daemon->addrbuff;
sprintf(a, "%5u %3u %3u %u", cache->addr.key.flags_or_keyid,
cache->addr.key.algo, cache->addr.key.digest, cache->uid);
}
#endif
else
{
a = daemon->addrbuff;
if (cache->flags & F_IPV4)
inet_ntop(AF_INET, &cache->addr.addr, a, ADDRSTRLEN);
#ifdef HAVE_IPV6
else if (cache->flags & F_IPV6)
inet_ntop(AF_INET6, &cache->addr.addr, a, ADDRSTRLEN);
}
#else
else
a = inet_ntoa(cache->addr.addr.addr.addr4);
#endif
p += sprintf(p, "%-30.30s %s%s%s%s%s%s%s%s%s%s ", a,
}
p += sprintf(p, "%-30.30s %s%s%s%s%s%s%s%s%s%s%s%s%s ", a,
cache->flags & F_IPV4 ? "4" : "",
cache->flags & F_IPV6 ? "6" : "",
cache->flags & F_DNSKEY ? "K" : "",
cache->flags & F_DS ? "S" : "",
cache->flags & F_CNAME ? "C" : "",
cache->flags & F_FORWARD ? "F" : " ",
cache->flags & F_REVERSE ? "R" : " ",
@@ -1154,7 +1197,8 @@ void dump_cache(time_t now)
cache->flags & F_DHCP ? "D" : " ",
cache->flags & F_NEG ? "N" : " ",
cache->flags & F_NXDOMAIN ? "X" : " ",
cache->flags & F_HOSTS ? "H" : " ");
cache->flags & F_HOSTS ? "H" : " ",
cache->flags & F_DNSSECOK ? "V" : " ");
#ifdef HAVE_BROKEN_RTC
p += sprintf(p, "%lu", cache->flags & F_IMMORTAL ? 0: (unsigned long)(cache->ttd - now));
#else
@@ -1268,3 +1312,50 @@ void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg)
my_syslog(LOG_INFO, "%s %s %s %s", source, name, verb, dest);
}
#ifdef HAVE_DNSSEC
struct keydata *keydata_alloc(char *data, size_t len)
{
struct keydata *block, *ret = NULL;
struct keydata **prev = &ret;
while (len > 0)
{
if (keyblock_free)
{
block = keyblock_free;
keyblock_free = block->next;
}
else
block = whine_malloc(sizeof(struct keydata));
if (!block)
{
/* failed to alloc, free partial chain */
keydata_free(ret);
return NULL;
}
memcpy(block->key, data, len > KEYBLOCK_LEN ? KEYBLOCK_LEN : len);
data += KEYBLOCK_LEN;
len -= KEYBLOCK_LEN;
*prev = block;
prev = &block->next;
block->next = NULL;
}
return ret;
}
void keydata_free(struct keydata *blocks)
{
struct keydata *tmp;
if (blocks)
{
for (tmp = blocks; tmp->next; tmp = tmp->next);
tmp->next = keyblock_free;
keyblock_free = blocks;
}
}
#endif

View File

@@ -18,6 +18,7 @@
#define MAX_PROCS 20 /* max no children for TCP requests */
#define CHILD_LIFETIME 150 /* secs 'till terminated (RFC1035 suggests > 120s) */
#define EDNS_PKTSZ 4096 /* default max EDNS.0 UDP packet from RFC5625 */
#define KEYBLOCK_LEN 140 /* choose to mininise fragmentation when storing DNSSEC keys */
#define TIMEOUT 10 /* drop UDP queries after TIMEOUT seconds */
#define FORWARD_TEST 50 /* try all servers every 50 queries */
#define FORWARD_TIME 20 /* or 20 seconds */
@@ -167,7 +168,7 @@ HAVE_SOLARIS_NETWORK
define exactly one of these to alter interaction with kernel networking.
HAVE_GETOPT_LONG
defined when GNU-sty;e getopt_long available.
defined when GNU-style getopt_long available.
HAVE_ARC4RANDOM
defined if arc4random() available to get better security from DNS spoofs

View File

@@ -396,7 +396,7 @@ void emit_dbus_signal(int action, struct dhcp_lease *lease, char *hostname)
DBusConnection *connection = (DBusConnection *)daemon->dbus;
DBusMessage* message = NULL;
DBusMessageIter args;
char *action_str, *addr, *mac = daemon->namebuff;
char *action_str, *mac = daemon->namebuff;
unsigned char *p;
int i;
@@ -406,10 +406,21 @@ void emit_dbus_signal(int action, struct dhcp_lease *lease, char *hostname)
if (!hostname)
hostname = "";
p = extended_hwaddr(lease->hwaddr_type, lease->hwaddr_len,
lease->hwaddr, lease->clid_len, lease->clid, &i);
print_mac(mac, p, i);
#ifdef HAVE_DHCP6
if (lease->flags & (LEASE_TA | LEASE_NA))
{
print_mac(mac, lease->clid, lease->clid_len);
inet_ntop(AF_INET6, lease->hwaddr, daemon->addrbuff, ADDRSTRLEN);
}
else
#endif
{
p = extended_hwaddr(lease->hwaddr_type, lease->hwaddr_len,
lease->hwaddr, lease->clid_len, lease->clid, &i);
print_mac(mac, p, i);
inet_ntop(AF_INET, &lease->addr, daemon->addrbuff, ADDRSTRLEN);
}
if (action == ACTION_DEL)
action_str = "DhcpLeaseDeleted";
else if (action == ACTION_ADD)
@@ -419,14 +430,12 @@ void emit_dbus_signal(int action, struct dhcp_lease *lease, char *hostname)
else
return;
addr = inet_ntoa(lease->addr);
if (!(message = dbus_message_new_signal(DNSMASQ_PATH, DNSMASQ_SERVICE, action_str)))
return;
dbus_message_iter_init_append(message, &args);
if (dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &addr) &&
if (dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &daemon->addrbuff) &&
dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &mac) &&
dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &hostname))
dbus_connection_send(connection, message, NULL);

View File

@@ -111,6 +111,13 @@ struct dhcp_netid *option_filter(struct dhcp_netid *tags, struct dhcp_netid *con
last_tag->next = tags;
tagif = run_tag_if(context_tags);
/* reset stuff with tag:!<tag> which now matches. */
for (opt = opts; opt; opt = opt->next)
if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)) &&
(opt->flags & DHOPT_TAGOK) &&
!match_netid(opt->netid, tagif, 0))
opt->flags &= ~DHOPT_TAGOK;
for (opt = opts; opt; opt = opt->next)
if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925 | DHOPT_TAGOK)) &&
match_netid(opt->netid, tagif, 0))
@@ -444,4 +451,340 @@ void join_multicast(void)
}
#endif
#ifdef HAVE_LINUX_NETWORK
void bindtodevice(int fd)
{
/* If we are doing DHCP on exactly one interface, and running linux, do SO_BINDTODEVICE
to that device. This is for the use case of (eg) OpenStack, which runs a new
dnsmasq instance for each VLAN interface it creates. Without the BINDTODEVICE,
individual processes don't always see the packets they should.
SO_BINDTODEVICE is only available Linux. */
struct irec *iface, *found;
for (found = NULL, iface = daemon->interfaces; iface; iface = iface->next)
if (iface->dhcp_ok)
{
if (!found)
found = iface;
else if (strcmp(found->name, iface->name) != 0)
{
/* more than one. */
found = NULL;
break;
}
}
if (found)
{
struct ifreq ifr;
strcpy(ifr.ifr_name, found->name);
/* only allowed by root. */
if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr)) == -1 &&
errno != EPERM)
die(_("failed to set SO_BINDTODEVICE on DHCP socket: %s"), NULL, EC_BADNET);
}
}
#endif
static const struct opttab_t {
char *name;
u16 val, size;
} opttab[] = {
{ "netmask", 1, OT_ADDR_LIST },
{ "time-offset", 2, 4 },
{ "router", 3, OT_ADDR_LIST },
{ "dns-server", 6, OT_ADDR_LIST },
{ "log-server", 7, OT_ADDR_LIST },
{ "lpr-server", 9, OT_ADDR_LIST },
{ "hostname", 12, OT_INTERNAL | OT_NAME },
{ "boot-file-size", 13, 2 | OT_DEC },
{ "domain-name", 15, OT_NAME },
{ "swap-server", 16, OT_ADDR_LIST },
{ "root-path", 17, OT_NAME },
{ "extension-path", 18, OT_NAME },
{ "ip-forward-enable", 19, 1 },
{ "non-local-source-routing", 20, 1 },
{ "policy-filter", 21, OT_ADDR_LIST },
{ "max-datagram-reassembly", 22, 2 | OT_DEC },
{ "default-ttl", 23, 1 | OT_DEC },
{ "mtu", 26, 2 | OT_DEC },
{ "all-subnets-local", 27, 1 },
{ "broadcast", 28, OT_INTERNAL | OT_ADDR_LIST },
{ "router-discovery", 31, 1 },
{ "router-solicitation", 32, OT_ADDR_LIST },
{ "static-route", 33, OT_ADDR_LIST },
{ "trailer-encapsulation", 34, 1 },
{ "arp-timeout", 35, 4 | OT_DEC },
{ "ethernet-encap", 36, 1 },
{ "tcp-ttl", 37, 1 },
{ "tcp-keepalive", 38, 4 | OT_DEC },
{ "nis-domain", 40, OT_NAME },
{ "nis-server", 41, OT_ADDR_LIST },
{ "ntp-server", 42, OT_ADDR_LIST },
{ "vendor-encap", 43, OT_INTERNAL },
{ "netbios-ns", 44, OT_ADDR_LIST },
{ "netbios-dd", 45, OT_ADDR_LIST },
{ "netbios-nodetype", 46, 1 },
{ "netbios-scope", 47, 0 },
{ "x-windows-fs", 48, OT_ADDR_LIST },
{ "x-windows-dm", 49, OT_ADDR_LIST },
{ "requested-address", 50, OT_INTERNAL | OT_ADDR_LIST },
{ "lease-time", 51, OT_INTERNAL | OT_DEC },
{ "option-overload", 52, OT_INTERNAL },
{ "message-type", 53, OT_INTERNAL | OT_DEC },
{ "server-identifier", 54, OT_INTERNAL | OT_ADDR_LIST },
{ "parameter-request", 55, OT_INTERNAL },
{ "message", 56, OT_INTERNAL },
{ "max-message-size", 57, OT_INTERNAL },
{ "T1", 58, OT_INTERNAL | OT_DEC},
{ "T2", 59, OT_INTERNAL | OT_DEC},
{ "vendor-class", 60, 0 },
{ "client-id", 61, OT_INTERNAL },
{ "nis+-domain", 64, OT_NAME },
{ "nis+-server", 65, OT_ADDR_LIST },
{ "tftp-server", 66, OT_NAME },
{ "bootfile-name", 67, OT_NAME },
{ "mobile-ip-home", 68, OT_ADDR_LIST },
{ "smtp-server", 69, OT_ADDR_LIST },
{ "pop3-server", 70, OT_ADDR_LIST },
{ "nntp-server", 71, OT_ADDR_LIST },
{ "irc-server", 74, OT_ADDR_LIST },
{ "user-class", 77, 0 },
{ "FQDN", 81, OT_INTERNAL },
{ "agent-id", 82, OT_INTERNAL },
{ "client-arch", 93, 2 | OT_DEC },
{ "client-interface-id", 94, 0 },
{ "client-machine-id", 97, 0 },
{ "subnet-select", 118, OT_INTERNAL },
{ "domain-search", 119, OT_RFC1035_NAME },
{ "sip-server", 120, 0 },
{ "classless-static-route", 121, 0 },
{ "vendor-id-encap", 125, 0 },
{ "server-ip-address", 255, OT_ADDR_LIST }, /* special, internal only, sets siaddr */
{ NULL, 0, 0 }
};
#ifdef HAVE_DHCP6
static const struct opttab_t opttab6[] = {
{ "client-id", 1, OT_INTERNAL },
{ "server-id", 2, OT_INTERNAL },
{ "ia-na", 3, OT_INTERNAL },
{ "ia-ta", 4, OT_INTERNAL },
{ "iaaddr", 5, OT_INTERNAL },
{ "oro", 6, OT_INTERNAL },
{ "preference", 7, OT_INTERNAL | OT_DEC },
{ "unicast", 12, OT_INTERNAL },
{ "status", 13, OT_INTERNAL },
{ "rapid-commit", 14, OT_INTERNAL },
{ "user-class", 15, OT_INTERNAL | OT_CSTRING },
{ "vendor-class", 16, OT_INTERNAL | OT_CSTRING },
{ "vendor-opts", 17, OT_INTERNAL },
{ "sip-server-domain", 21, OT_RFC1035_NAME },
{ "sip-server", 22, OT_ADDR_LIST },
{ "dns-server", 23, OT_ADDR_LIST },
{ "domain-search", 24, OT_RFC1035_NAME },
{ "nis-server", 27, OT_ADDR_LIST },
{ "nis+-server", 28, OT_ADDR_LIST },
{ "nis-domain", 29, OT_RFC1035_NAME },
{ "nis+-domain", 30, OT_RFC1035_NAME },
{ "sntp-server", 31, OT_ADDR_LIST },
{ "FQDN", 39, OT_INTERNAL | OT_RFC1035_NAME },
{ "ntp-server", 56, OT_ADDR_LIST },
{ "bootfile-url", 59, OT_NAME },
{ "bootfile-param", 60, OT_CSTRING },
{ NULL, 0, 0 }
};
#endif
void display_opts(void)
{
int i;
printf(_("Known DHCP options:\n"));
for (i = 0; opttab[i].name; i++)
if (!(opttab[i].size & OT_INTERNAL))
printf("%3d %s\n", opttab[i].val, opttab[i].name);
}
#ifdef HAVE_DHCP6
void display_opts6(void)
{
int i;
printf(_("Known DHCPv6 options:\n"));
for (i = 0; opttab6[i].name; i++)
if (!(opttab6[i].size & OT_INTERNAL))
printf("%3d %s\n", opttab6[i].val, opttab6[i].name);
}
#endif
u16 lookup_dhcp_opt(int prot, char *name)
{
const struct opttab_t *t;
int i;
#ifdef HAVE_DHCP6
if (prot == AF_INET6)
t = opttab6;
else
#endif
t = opttab;
for (i = 0; t[i].name; i++)
if (!(t[i].size & OT_INTERNAL) &&
strcasecmp(t[i].name, name) == 0)
return t[i].val;
return 0;
}
u16 lookup_dhcp_len(int prot, u16 val)
{
const struct opttab_t *t;
int i;
#ifdef HAVE_DHCP6
if (prot == AF_INET6)
t = opttab6;
else
#endif
t = opttab;
for (i = 0; t[i].name; i++)
if (val == t[i].val)
{
if (t[i].size & OT_INTERNAL)
return 0;
return t[i].size & ~OT_DEC;
}
return 0;
}
char *option_string(int prot, unsigned int opt, unsigned char *val, int opt_len, char *buf, int buf_len)
{
int o, i, j, nodecode = 0;
const struct opttab_t *ot = opttab;
#ifdef HAVE_DHCP6
if (prot == AF_INET6)
ot = opttab6;
#endif
for (o = 0; ot[o].name; o++)
if (ot[o].val == opt)
{
if (buf)
{
memset(buf, 0, buf_len);
if (ot[o].size & OT_ADDR_LIST)
{
struct all_addr addr;
int addr_len = INADDRSZ;
#ifdef HAVE_DHCP6
if (prot == AF_INET6)
addr_len = IN6ADDRSZ;
#endif
for (buf[0]= 0, i = 0; i <= opt_len - addr_len; i += addr_len)
{
if (i != 0)
strncat(buf, ", ", buf_len - strlen(buf));
/* align */
memcpy(&addr, &val[i], addr_len);
inet_ntop(prot, &val[i], daemon->addrbuff, ADDRSTRLEN);
strncat(buf, daemon->addrbuff, buf_len - strlen(buf));
}
}
else if (ot[o].size & OT_NAME)
for (i = 0, j = 0; i < opt_len && j < buf_len ; i++)
{
char c = val[i];
if (isprint((int)c))
buf[j++] = c;
}
#ifdef HAVE_DHCP6
/* We don't handle compressed rfc1035 names, so no good in IPv4 land */
else if ((ot[o].size & OT_RFC1035_NAME) && prot == AF_INET6)
{
i = 0, j = 0;
while (i < opt_len && val[i] != 0)
{
int k, l = i + val[i] + 1;
for (k = i + 1; k < opt_len && k < l && j < buf_len ; k++)
{
char c = val[k];
if (isprint((int)c))
buf[j++] = c;
}
i = l;
if (val[i] != 0 && j < buf_len)
buf[j++] = '.';
}
}
else if ((ot[o].size & OT_CSTRING))
{
int k, len;
unsigned char *p;
i = 0, j = 0;
while (1)
{
p = &val[i];
GETSHORT(len, p);
for (k = 0; k < len && j < buf_len; k++)
{
char c = *p++;
if (isprint((int)c))
buf[j++] = c;
}
i += len +2;
if (i >= opt_len)
break;
if (j < buf_len)
buf[j++] = ',';
}
}
#endif
else if ((ot[o].size & OT_DEC) && opt_len != 0)
{
unsigned int dec = 0;
for (i = 0; i < opt_len; i++)
dec = (dec << 8) | val[i];
sprintf(buf, "%u", dec);
}
else
nodecode = 1;
}
break;
}
if (opt_len != 0 && buf && (!ot[o].name || nodecode))
{
int trunc = 0;
if (opt_len > 14)
{
trunc = 1;
opt_len = 14;
}
print_mac(buf, val, opt_len);
if (trunc)
strncat(buf, "...", buf_len - strlen(buf));
}
return ot[o].name ? ot[o].name : "";
}
#endif

View File

@@ -299,7 +299,7 @@ void dhcp_packet(time_t now, int pxe_fd)
iov.iov_len = dhcp_reply(parm.current, ifr.ifr_name, iface_index, (size_t)sz,
now, unicast_dest, &is_inform, pxe_fd, iface_addr);
lease_update_file(now);
lease_update_dns();
lease_update_dns(0);
if (iov.iov_len == 0)
return;

View File

@@ -54,6 +54,7 @@
#define OPTION6_RECONFIGURE_MSG 19
#define OPTION6_RECONF_ACCEPT 20
#define OPTION6_DNS_SERVER 23
#define OPTION6_DOMAIN_SEARCH 24
#define OPTION6_REMOTE_ID 37
#define OPTION6_SUBSCRIBER_ID 38
#define OPTION6_FQDN 39

View File

@@ -33,12 +33,12 @@ void dhcp6_init(void)
{
int fd;
struct sockaddr_in6 saddr;
#if defined(IP_TOS) && defined(IPTOS_CLASS_CS6)
#if defined(IPV6_TCLASS) && 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)
#if defined(IPV6_TCLASS) && defined(IPTOS_CLASS_CS6)
setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &class, sizeof(class)) == -1 ||
#endif
!fix_fd(fd) ||
@@ -75,6 +75,7 @@ void dhcp6_packet(time_t now)
ssize_t sz;
struct ifreq ifr;
struct iname *tmp;
unsigned short port;
msg.msg_control = control_u.control6;
msg.msg_controllen = sizeof(control_u);
@@ -84,7 +85,7 @@ void dhcp6_packet(time_t now)
msg.msg_iov = &daemon->dhcp_packet;
msg.msg_iovlen = 1;
if ((sz = recv_dhcp_packet(daemon->dhcp6fd, &msg)) == -1 || sz <= 4)
if ((sz = recv_dhcp_packet(daemon->dhcp6fd, &msg)) == -1)
return;
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
@@ -134,15 +135,23 @@ void dhcp6_packet(time_t now)
lease_prune(NULL, now); /* lose any expired leases */
sz = dhcp6_reply(parm.current, if_index, ifr.ifr_name, &parm.fallback,
sz, IN6_IS_ADDR_MULTICAST(&from.sin6_addr), now);
port = dhcp6_reply(parm.current, if_index, ifr.ifr_name, &parm.fallback,
sz, IN6_IS_ADDR_MULTICAST(&from.sin6_addr), now);
lease_update_file(now);
lease_update_dns();
lease_update_dns(0);
if (sz != 0)
while (sendto(daemon->dhcp6fd, daemon->outpacket.iov_base, sz, 0, (struct sockaddr *)&from, sizeof(from)) == -1 &&
/* The port in the source address of the original request should
be correct, but at least once client sends from the server port,
so we explicitly send to the client port to a client, and the
server port to a relay. */
if (port != 0)
{
from.sin6_port = htons(port);
while (sendto(daemon->dhcp6fd, daemon->outpacket.iov_base, save_counter(0),
0, (struct sockaddr *)&from, sizeof(from)) == -1 &&
retry_send());
}
}
static int complete_context6(struct in6_addr *local, int prefix,
@@ -220,7 +229,7 @@ int address6_allocate(struct dhcp_context *context, unsigned char *clid, int cl
for (pass = 0; pass <= 1; pass++)
for (c = context; c; c = c->current)
if (c->flags & (CONTEXT_STATIC | CONTEXT_PROXY))
if (c->flags & (CONTEXT_DEPRECATE | CONTEXT_STATIC | CONTEXT_RA_STATELESS))
continue;
else if (!match_netid(c->filter, netids, pass))
continue;
@@ -273,9 +282,9 @@ struct dhcp_context *address6_available(struct dhcp_context *context,
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) &&
if (!(tmp->flags & (CONTEXT_STATIC | CONTEXT_RA_STATELESS)) &&
is_same_net6(&tmp->start6, taddr, tmp->prefix) &&
is_same_net6(&tmp->end6, taddr, tmp->prefix) &&
addr >= start &&
addr <= end &&
match_netid(tmp->filter, netids, 1))
@@ -365,13 +374,26 @@ struct dhcp_config *find_config6(struct dhcp_config *configs,
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);
if (daemon->duid_config)
{
unsigned char *p;
daemon->duid = p = safe_malloc(daemon->duid_config_len + 6);
daemon->duid_len = daemon->duid_config_len + 6;
PUTSHORT(2, p); /* DUID_EN */
PUTLONG(daemon->duid_enterprise, p);
memcpy(p, daemon->duid_config, daemon->duid_config_len);
}
else
{
/* 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(int index, unsigned int type, char *mac, size_t maclen, void *parm)

View File

@@ -106,16 +106,14 @@ int main (int argc, char **argv)
else
open("/dev/null", O_RDWR);
#ifdef HAVE_LINUX_NETWORK
netlink_init();
#elif !(defined(IP_RECVDSTADDR) && \
defined(IP_RECVIF) && \
defined(IP_SENDSRCADDR))
#ifndef HAVE_LINUX_NETWORK
# if !(defined(IP_RECVDSTADDR) && defined(IP_RECVIF) && defined(IP_SENDSRCADDR))
if (!option_bool(OPT_NOWILD))
{
bind_fallback = 1;
set_option_bool(OPT_NOWILD);
}
# endif
#endif
#ifndef HAVE_TFTP
@@ -180,10 +178,25 @@ int main (int argc, char **argv)
if (daemon->dhcp6)
dhcp6_init();
if (daemon->ra_contexts || daemon->dhcp6)
join_multicast();
# endif
#endif
#ifdef HAVE_LINUX_NETWORK
/* After lease_init */
netlink_init();
#endif
#ifdef HAVE_DHCP6
/* after netlink_init */
if (daemon->ra_contexts || daemon->dhcp6)
join_multicast();
#endif
#ifdef HAVE_DHCP
/* after netlink_init */
if (daemon->dhcp || daemon->dhcp6)
lease_find_interfaces(now);
#endif
if (!enumerate_interfaces())
@@ -196,13 +209,21 @@ int main (int argc, char **argv)
for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next)
if (if_tmp->name && !if_tmp->used)
die(_("unknown interface %s"), if_tmp->name, EC_BADNET);
for (if_tmp = daemon->if_addrs; if_tmp; if_tmp = if_tmp->next)
if (!if_tmp->used)
{
prettyprint_addr(&if_tmp->addr, daemon->namebuff);
die(_("no interface with address %s"), daemon->namebuff, EC_BADNET);
}
#if defined(HAVE_LINUX_NETWORK) && defined(HAVE_DHCP)
/* after enumerate_interfaces() */
if (daemon->dhcp)
{
bindtodevice(daemon->dhcpfd);
if (daemon->enable_pxe)
bindtodevice(daemon->pxefd);
}
#endif
#if defined(HAVE_LINUX_NETWORK) && defined(HAVE_DHCP6)
if (daemon->dhcp6)
bindtodevice(daemon->dhcp6fd);
#endif
}
else
create_wildcard_listeners();
@@ -524,7 +545,7 @@ int main (int argc, char **argv)
my_syslog(MS_DHCP | LOG_INFO, _("IPv6 router advertisement enabled"));
#ifdef HAVE_DHCP
if (daemon->dhcp || daemon->dhcp6)
if (daemon->dhcp || daemon->dhcp6 || daemon->ra_contexts)
{
struct dhcp_context *dhcp_tmp;
int family = AF_INET;
@@ -537,27 +558,56 @@ int main (int argc, char **argv)
{
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;
struct in6_addr subnet = dhcp_tmp->start6;
setaddr6part(&subnet, 0);
inet_ntop(AF_INET6, &subnet, daemon->addrbuff, 256);
}
#endif
prettyprint_time(daemon->dhcp_buff2, dhcp_tmp->lease_time);
inet_ntop(family, start, daemon->dhcp_buff, 256);
inet_ntop(family, end, daemon->dhcp_buff3, 256);
my_syslog(MS_DHCP | LOG_INFO,
(dhcp_tmp->flags & CONTEXT_STATIC) ?
_("DHCP, static leases only on %.0s%s, lease time %s") :
(dhcp_tmp->flags & CONTEXT_RA_ONLY) ?
_("router advertisement only on %.0s%s, lifetime %s") :
(dhcp_tmp->flags & CONTEXT_PROXY) ?
_("DHCP, proxy on subnet %.0s%s%.0s") :
_("DHCP, IP range %s -- %s, lease time %s"),
daemon->dhcp_buff, daemon->dhcp_buff3, daemon->dhcp_buff2);
if (family != AF_INET && (dhcp_tmp->flags & CONTEXT_DEPRECATE))
strcpy(daemon->namebuff, _("prefix deprecated"));
else
{
char *p = daemon->namebuff;
p += sprintf(p, _("lease time "));
prettyprint_time(p, dhcp_tmp->lease_time);
}
if (daemon->dhcp_buff)
inet_ntop(family, start, daemon->dhcp_buff, 256);
if (daemon->dhcp_buff3)
inet_ntop(family, end, daemon->dhcp_buff3, 256);
if ((dhcp_tmp->flags & CONTEXT_DHCP) || family == AF_INET)
my_syslog(MS_DHCP | LOG_INFO,
(dhcp_tmp->flags & CONTEXT_RA_STATELESS) ?
_("stateless DHCPv6 on %s%.0s%.0s") :
(dhcp_tmp->flags & CONTEXT_STATIC) ?
_("DHCP, static leases only on %.0s%s, %s") :
(dhcp_tmp->flags & CONTEXT_PROXY) ?
_("DHCP, proxy on subnet %.0s%s%.0s") :
_("DHCP, IP range %s -- %s, %s"),
daemon->dhcp_buff, daemon->dhcp_buff3, daemon->namebuff);
if (dhcp_tmp->flags & CONTEXT_RA_NAME)
my_syslog(MS_DHCP | LOG_INFO, _("DHCPv4-derived IPv6 names on %s"),
daemon->addrbuff);
if (dhcp_tmp->flags & (CONTEXT_RA_ONLY | CONTEXT_RA_NAME | CONTEXT_RA_STATELESS))
{
if (!(dhcp_tmp->flags & CONTEXT_DEPRECATE))
{
char *p = daemon->namebuff;
p += sprintf(p, _("prefix valid "));
prettyprint_time(p, dhcp_tmp->lease_time > 7200 ? dhcp_tmp->lease_time : 7200);
}
my_syslog(MS_DHCP | LOG_INFO, _("SLAAC on %s %s"),
daemon->addrbuff, daemon->namebuff);
}
}
#ifdef HAVE_DHCP6
@@ -685,12 +735,12 @@ int main (int argc, char **argv)
{
FD_SET(daemon->dhcp6fd, &rset);
bump_maxfd(daemon->dhcp6fd, &maxfd);
if (daemon->ra_contexts)
{
FD_SET(daemon->icmp6fd, &rset);
bump_maxfd(daemon->icmp6fd, &maxfd);
}
}
if (daemon->ra_contexts)
{
FD_SET(daemon->icmp6fd, &rset);
bump_maxfd(daemon->icmp6fd, &maxfd);
}
#endif
@@ -706,6 +756,10 @@ int main (int argc, char **argv)
# ifdef HAVE_SCRIPT
while (helper_buf_empty() && do_script_run(now));
# ifdef HAVE_TFTP
while (helper_buf_empty() && do_tftp_script_run());
# endif
if (!helper_buf_empty())
{
FD_SET(daemon->helperfd, &wset);
@@ -714,6 +768,11 @@ int main (int argc, char **argv)
# else
/* need this for other side-effects */
while (do_script_run(now));
# ifdef HAVE_TFTP
while (do_tftp_script_run());
# endif
# endif
#endif
@@ -849,9 +908,17 @@ static void sig_handler(int sig)
}
}
void send_alarm(void)
/* now == 0 -> queue immediate callback */
void send_alarm(time_t event, time_t now)
{
send_event(pipewrite, EVENT_ALARM, 0, NULL);
if (now == 0 || event != 0)
{
/* alarm(0) or alarm(-ve) doesn't do what we want.... */
if ((now == 0 || difftime(event, now) <= 0.0))
send_event(pipewrite, EVENT_ALARM, 0, NULL);
else
alarm((unsigned)difftime(event, now));
}
}
void send_event(int fd, int event, int data, char *msg)
@@ -973,13 +1040,8 @@ static void async_event(int pipe, time_t now)
}
#ifdef HAVE_DHCP6
else if (daemon->ra_contexts)
{
/* Not doing DHCP, so no lease system, manage
alarms for ra only */
time_t next_event = periodic_ra(now);
if (next_event != 0)
alarm((unsigned)difftime(next_event, now));
}
/* Not doing DHCP, so no lease system, manage alarms for ra only */
send_alarm(periodic_ra(now), now);
#endif
#endif
break;
@@ -1143,7 +1205,7 @@ void clear_cache_and_reload(time_t now)
check_dhcp_hosts(0);
lease_update_from_configs();
lease_update_file(now);
lease_update_dns();
lease_update_dns(1);
}
#ifdef HAVE_DHCP6
else if (daemon->ra_contexts)
@@ -1257,18 +1319,19 @@ static void check_dns_listeners(fd_set *set, time_t now)
int confd;
struct irec *iface = NULL;
pid_t p;
union mysockaddr tcp_addr;
socklen_t tcp_len = sizeof(union mysockaddr);
while ((confd = accept(listener->tcpfd, NULL, NULL)) == -1 && errno == EINTR);
while((confd = accept(listener->tcpfd, NULL, NULL)) == -1 && errno == EINTR);
if (confd == -1)
if (confd == -1 ||
getsockname(confd, (struct sockaddr *)&tcp_addr, &tcp_len) == -1)
continue;
if (option_bool(OPT_NOWILD))
iface = listener->iface;
iface = listener->iface; /* May be NULL */
else
{
union mysockaddr tcp_addr;
socklen_t tcp_len = sizeof(union mysockaddr);
/* Check for allowed interfaces when binding the wildcard address:
we do this by looking for an interface with the same address as
the local address of the TCP connection, then looking to see if that's
@@ -1276,14 +1339,13 @@ static void check_dns_listeners(fd_set *set, time_t now)
interface too, for localisation. */
/* interface may be new since startup */
if (enumerate_interfaces() &&
getsockname(confd, (struct sockaddr *)&tcp_addr, &tcp_len) != -1)
if (enumerate_interfaces())
for (iface = daemon->interfaces; iface; iface = iface->next)
if (sockaddr_isequal(&iface->addr, &tcp_addr))
break;
}
if (!iface)
if (!iface && !option_bool(OPT_NOWILD))
{
shutdown(confd, SHUT_RDWR);
close(confd);
@@ -1309,7 +1371,13 @@ static void check_dns_listeners(fd_set *set, time_t now)
unsigned char *buff;
struct server *s;
int flags;
struct in_addr netmask;
if (iface)
netmask = iface->netmask;
else
netmask.s_addr = 0;
#ifndef NO_FORK
/* Arrange for SIGALARM after CHILD_LIFETIME seconds to
terminate the process. */
@@ -1327,7 +1395,7 @@ static void check_dns_listeners(fd_set *set, time_t now)
if ((flags = fcntl(confd, F_GETFL, 0)) != -1)
fcntl(confd, F_SETFL, flags & ~O_NONBLOCK);
buff = tcp_request(confd, now, &iface->addr, iface->netmask);
buff = tcp_request(confd, now, &tcp_addr, netmask);
shutdown(confd, SHUT_RDWR);
close(confd);

View File

@@ -217,7 +217,8 @@ struct event_desc {
#define OPT_CONNTRACK 35
#define OPT_FQDN_UPDATE 36
#define OPT_RA 37
#define OPT_LAST 38
#define OPT_TFTP_LC 38
#define OPT_LAST 39
/* 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. */
@@ -274,6 +275,18 @@ struct cname {
struct cname *next;
};
struct host_record {
struct name_list {
char *name;
struct name_list *next;
} *names;
struct in_addr addr;
#ifdef HAVE_IPV6
struct in6_addr addr6;
#endif
struct host_record *next;
};
struct interface_name {
char *name; /* domain name */
char *intr; /* interface name */
@@ -285,17 +298,30 @@ union bigname {
union bigname *next; /* freelist */
};
struct keydata {
struct keydata *next;
unsigned char key[KEYBLOCK_LEN];
};
struct crec {
struct crec *next, *prev, *hash_next;
time_t ttd; /* time to die */
int uid;
/* union is 16 bytes when doing IPv6, 8 bytes on 32 bit machines without IPv6 */
union {
struct all_addr addr;
struct {
struct crec *cache;
int uid;
} cname;
struct {
struct keydata *keydata;
unsigned char algo;
unsigned char digest; /* DS only */
unsigned short flags_or_keyid; /* flags for DNSKEY, keyid for DS */
} key;
} addr;
time_t ttd; /* time to die */
/* used as keylen if F_DS or F_DNSKEY, index to source for F_HOSTS */
int uid;
unsigned short flags;
union {
char sname[SMALLDNAME];
@@ -316,14 +342,21 @@ struct crec {
#define F_BIGNAME (1u<<9)
#define F_NXDOMAIN (1u<<10)
#define F_CNAME (1u<<11)
#define F_NOERR (1u<<12)
#define F_DNSKEY (1u<<12)
#define F_CONFIG (1u<<13)
#define F_DS (1u<<14)
#define F_DNSSECOK (1u<<15)
/* below here are only valid as args to log_query: cache
entries are limited to 16 bits */
#define F_UPSTREAM (1u<<16)
#define F_RRNAME (1u<<17)
#define F_SERVER (1u<<18)
#define F_QUERY (1u<<19)
#define F_NOERR (1u<<20)
/* composites */
#define F_TYPE (F_IPV4 | F_IPV6 | F_DNSKEY | F_DS) /* Only one may be set */
/* struct sockaddr is not large enough to hold any address,
@@ -376,14 +409,14 @@ struct server {
struct irec {
union mysockaddr addr;
struct in_addr netmask; /* only valid for IPv4 */
int tftp_ok, mtu, done, dad;
int tftp_ok, dhcp_ok, mtu, done, dad;
char *name;
struct irec *next;
};
struct listener {
int fd, tcpfd, tftpfd, family;
struct irec *iface; /* only valid for non-wildcard */
struct irec *iface; /* only sometimes valid for non-wildcard */
struct listener *next;
};
@@ -432,11 +465,21 @@ struct frec {
struct frec *next;
};
/* flags in top of length field for DHCP-option tables */
#define OT_ADDR_LIST 0x8000
#define OT_RFC1035_NAME 0x4000
#define OT_INTERNAL 0x2000
#define OT_NAME 0x1000
#define OT_CSTRING 0x0800
#define OT_DEC 0x0400
/* actions in the daemon->helper RPC */
#define ACTION_DEL 1
#define ACTION_OLD_HOSTNAME 2
#define ACTION_OLD 3
#define ACTION_ADD 4
#define ACTION_TFTP 5
#define LEASE_NEW 1 /* newly created */
#define LEASE_CHANGED 2 /* modified */
@@ -445,6 +488,7 @@ struct frec {
#define LEASE_USED 16 /* used this DHCPv6 transaction */
#define LEASE_NA 32 /* IPv6 no-temporary lease */
#define LEASE_TA 64 /* IPv6 temporary lease */
#define LEASE_HAVE_HWADDR 128 /* Have set hwaddress */
struct dhcp_lease {
int clid_len; /* length of client identifier */
@@ -462,6 +506,14 @@ struct dhcp_lease {
unsigned char *extradata;
unsigned int extradata_len, extradata_size;
int last_interface;
#ifdef HAVE_DHCP6
struct slaac_address {
struct in6_addr addr, local;
time_t ping_time;
int backoff; /* zero -> confirmed */
struct slaac_address *next;
} *slaac_address;
#endif
struct dhcp_lease *next;
};
@@ -606,7 +658,7 @@ struct dhcp_context {
#ifdef HAVE_DHCP6
struct in6_addr start6, end6; /* range of available addresses */
struct in6_addr local6;
int prefix;
int prefix, if_index;
time_t ra_time;
#endif
int flags;
@@ -615,12 +667,16 @@ struct dhcp_context {
struct dhcp_context *next, *current;
};
#define CONTEXT_STATIC 1
#define CONTEXT_NETMASK 2
#define CONTEXT_BRDCAST 4
#define CONTEXT_PROXY 8
#define CONTEXT_RA_ONLY 16
#define CONTEXT_RA_DONE 32
#define CONTEXT_STATIC 1
#define CONTEXT_NETMASK 2
#define CONTEXT_BRDCAST 4
#define CONTEXT_PROXY 8
#define CONTEXT_RA_ONLY 16
#define CONTEXT_RA_DONE 32
#define CONTEXT_RA_NAME 64
#define CONTEXT_RA_STATELESS 128
#define CONTEXT_DHCP 256
#define CONTEXT_DEPRECATE 512
struct ping_result {
struct in_addr addr;
@@ -678,6 +734,7 @@ extern struct daemon {
struct naptr *naptr;
struct txt_record *txt;
struct ptr_record *ptr;
struct host_record *host_records, *host_records_tail;
struct cname *cnames;
struct interface_name *int_names;
char *mxtarget;
@@ -723,6 +780,8 @@ extern struct daemon {
struct tftp_prefix *if_prefix; /* per-interface TFTP prefixes */
struct interface_list *tftp_interfaces; /* interfaces for limited TFTP service */
int tftp_unlimited;
unsigned int duid_enterprise, duid_config_len;
unsigned char *duid_config;
/* globally used stuff for DNS */
char *packet; /* packet buffer */
@@ -769,7 +828,7 @@ extern struct daemon {
#endif
/* TFTP stuff */
struct tftp_transfer *tftp_trans;
struct tftp_transfer *tftp_trans, *tftp_done_trans;
/* utility string buffer, hold max sized IP address as string */
char *addrbuff;
@@ -800,6 +859,10 @@ char *get_domain(struct in_addr addr);
#ifdef HAVE_IPV6
char *get_domain6(struct in6_addr *addr);
#endif
#ifdef HAVE_DNSSEC
struct keydata *keydata_alloc(char *data, size_t len);
void keydata_free(struct keydata *blocks);
#endif
/* rfc1035.c */
unsigned int extract_request(struct dns_header *header, size_t qlen,
@@ -876,7 +939,7 @@ unsigned char *tcp_request(int confd, time_t now,
union mysockaddr *local_addr, struct in_addr netmask);
void server_gone(struct server *server);
struct frec *get_new_frec(time_t now, int *wait);
void send_from(int fd, int nowild, char *packet, size_t len,
int send_from(int fd, int nowild, char *packet, size_t len,
union mysockaddr *to, struct all_addr *source,
unsigned int iface);
@@ -925,7 +988,7 @@ char *host_from_dns(struct in_addr addr);
/* lease.c */
#ifdef HAVE_DHCP
void lease_update_file(time_t now);
void lease_update_dns();
void lease_update_dns(int force);
void lease_init(time_t now);
struct dhcp_lease *lease4_allocate(struct in_addr addr);
#ifdef HAVE_DHCP6
@@ -934,12 +997,13 @@ 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);
void lease_ping_reply(struct in6_addr *sender, unsigned char *packet, char *interface);
#endif
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, time_t now, int force);
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_interface(struct dhcp_lease *lease, int interface);
void lease_set_interface(struct dhcp_lease *lease, int interface, time_t now);
struct dhcp_lease *lease_find_by_client(unsigned char *hwaddr, int hw_len, int hw_type,
unsigned char *clid, int clid_len);
struct dhcp_lease *lease_find_by_addr(struct in_addr addr);
@@ -948,6 +1012,7 @@ void lease_prune(struct dhcp_lease *target, time_t now);
void lease_update_from_configs(void);
int do_script_run(time_t now);
void rerun_scripts(void);
void lease_find_interfaces(time_t now);
#ifdef HAVE_SCRIPT
void lease_add_extradata(struct dhcp_lease *lease, unsigned char *data,
unsigned int len, int delim);
@@ -967,7 +1032,7 @@ unsigned char *extended_hwaddr(int hwtype, int hwlen, unsigned char *hwaddr,
int make_icmp_sock(void);
int icmp_ping(struct in_addr addr);
#endif
void send_alarm(void);
void send_alarm(time_t event, time_t now);
void send_event(int fd, int event, int data, char *msg);
void clear_cache_and_reload(time_t now);
void poll_resolv(int force, int do_reload, time_t now);
@@ -999,11 +1064,14 @@ void emit_dbus_signal(int action, struct dhcp_lease *lease, char *hostname);
#endif
/* helper.c */
#if defined(HAVE_DHCP) && !defined(NO_FORK)
#if defined(HAVE_SCRIPT)
int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd);
void helper_write(void);
void queue_script(int action, struct dhcp_lease *lease,
char *hostname, time_t now);
#ifdef HAVE_TFTP
void queue_tftp(off_t file_len, char *filename, union mysockaddr *peer);
#endif
int helper_buf_empty(void);
#endif
@@ -1011,6 +1079,7 @@ int helper_buf_empty(void);
#ifdef HAVE_TFTP
void tftp_request(struct listener *listen, time_t now);
void check_tftp_listeners(fd_set *rset, time_t now);
int do_tftp_script_run(void);
#endif
/* conntrack.c */
@@ -1042,8 +1111,8 @@ void make_duid(time_t now);
/* rfc3315.c */
#ifdef HAVE_DHCP6
size_t dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name,
struct in6_addr *fallback, size_t sz, int is_multicast, time_t now);
unsigned short dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name,
struct in6_addr *fallback, size_t sz, int is_multicast, time_t now);
#endif
/* dhcp-common.c */
@@ -1059,7 +1128,16 @@ 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);
void display_opts(void);
u16 lookup_dhcp_opt(int prot, char *name);
u16 lookup_dhcp_len(int prot, u16 val);
char *option_string(int prot, unsigned int opt, unsigned char *val,
int opt_len, char *buf, int buf_len);
#ifdef HAVE_LINUX_NETWORK
void bindtodevice(int fd);
#endif
# ifdef HAVE_DHCP6
void display_opts6(void);
void join_multicast(void);
# endif
#endif
@@ -1082,5 +1160,14 @@ void put_opt6_string(char *s);
void ra_init(time_t now);
void icmp6_packet(void);
time_t periodic_ra(time_t now);
void ra_start_unsolicted(time_t now);
void ra_start_unsolicted(time_t now, struct dhcp_context *context);
#endif
/* slaac.c */
#ifdef HAVE_DHCP6
void build_subnet_map(void);
void slaac_add_addrs(struct dhcp_lease *lease, time_t now, int force);
time_t periodic_slaac(time_t now, struct dhcp_lease *leases);
void slaac_ping_reply(struct in6_addr *sender, unsigned char *packet, char *interface, struct dhcp_lease *leases);
void schedule_subnet_map(void);
#endif

View File

@@ -26,9 +26,9 @@ static struct randfd *allocate_rfd(int family);
/* Send a UDP packet with its source address set as "source"
unless nowild is true, when we just send it with the kernel default */
void send_from(int fd, int nowild, char *packet, size_t len,
union mysockaddr *to, struct all_addr *source,
unsigned int iface)
int send_from(int fd, int nowild, char *packet, size_t len,
union mysockaddr *to, struct all_addr *source,
unsigned int iface)
{
struct msghdr msg;
struct iovec iov[1];
@@ -111,7 +111,11 @@ void send_from(int fd, int nowild, char *packet, size_t len,
goto retry;
my_syslog(LOG_ERR, _("failed to send packet: %s"), strerror(errno));
return 0;
}
return 1;
}
static unsigned int search_servers(time_t now, struct all_addr **addrpp,
@@ -660,7 +664,7 @@ void receive_query(struct listener *listen, time_t now)
/* packet buffer overwritten */
daemon->srv_save = NULL;
if (listen->family == AF_INET && option_bool(OPT_NOWILD))
if (listen->iface && listen->family == AF_INET && option_bool(OPT_NOWILD))
{
dst_addr_4 = listen->iface->addr.in.sin_addr;
netmask = listen->iface->netmask;

View File

@@ -192,18 +192,24 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
#endif
_exit(0);
}
is6 = !!(data.flags & (LEASE_TA | LEASE_NA));
if (data.action == ACTION_DEL)
action_str = "del";
else if (data.action == ACTION_ADD)
action_str = "add";
else if (data.action == ACTION_OLD || data.action == ACTION_OLD_HOSTNAME)
action_str = "old";
else if (data.action == ACTION_TFTP)
{
action_str = "tftp";
is6 = (data.flags != AF_INET);
}
else
continue;
is6 = !!(data.flags & (LEASE_TA | LEASE_NA));
if (!is6)
{
/* stringify MAC into dhcp_buff */
@@ -271,13 +277,16 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
char *dot;
hostname = (char *)buf;
hostname[data.hostname_len - 1] = 0;
if (!legal_hostname(hostname))
hostname = NULL;
else if ((dot = strchr(hostname, '.')))
if (data.action != ACTION_TFTP)
{
domain = dot+1;
*dot = 0;
}
if (!legal_hostname(hostname))
hostname = NULL;
else if ((dot = strchr(hostname, '.')))
{
domain = dot+1;
*dot = 0;
}
}
}
extradata = buf + data.hostname_len;
@@ -289,116 +298,141 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
inet_ntop(AF_INET6, &data.hwaddr, daemon->addrbuff, ADDRSTRLEN);
#endif
/* file length */
if (data.action == ACTION_TFTP)
sprintf(daemon->dhcp_buff, "%u", data.hwaddr_len);
#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)
if (data.action == ACTION_TFTP)
{
lua_pushstring(lua, daemon->dhcp_buff);
lua_setfield(lua, -2, "client_duid");
lua_pushstring(lua, daemon->packet);
lua_setfield(lua, -2, "server_duid");
lua_pushstring(lua, daemon->dhcp_buff3);
lua_setfield(lua, -2, "iaid");
lua_getglobal(lua, "tftp");
if (lua_type(lua, -1) != LUA_TFUNCTION)
lua_pop(lua, 1); /* tftp function optional */
else
{
lua_pushstring(lua, action_str); /* arg1 - action */
lua_newtable(lua); /* arg2 - data table */
lua_pushstring(lua, daemon->addrbuff);
lua_setfield(lua, -2, "destination_address");
lua_pushstring(lua, hostname);
lua_setfield(lua, -2, "file_name");
lua_pushstring(lua, daemon->dhcp_buff);
lua_setfield(lua, -2, "file_size");
lua_call(lua, 2, 0); /* pass 2 values, expect 0 */
}
}
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);
}
{
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->dhcp_buff);
lua_setfield(lua, -2, "client_duid");
lua_pushstring(lua, daemon->packet);
lua_setfield(lua, -2, "server_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
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");
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");
if (is6)
buf = grab_extradata_lua(buf, end, "relay_address");
else if (data.giaddr.s_addr != 0)
{
lua_pushstring(lua, inet_ntoa(data.giaddr));
lua_setfield(lua, -2, "relay_address");
}
for (i = 0; buf; i++)
{
sprintf(daemon->dhcp_buff2, "user_class%i", i);
buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2);
}
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 */
}
buf = grab_extradata_lua(buf, end, "tags");
if (is6)
buf = grab_extradata_lua(buf, end, "relay_address");
else if (data.giaddr.s_addr != 0)
{
lua_pushstring(lua, inet_ntoa(data.giaddr));
lua_setfield(lua, -2, "relay_address");
}
for (i = 0; buf; i++)
{
sprintf(daemon->dhcp_buff2, "user_class%i", i);
buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2);
}
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
@@ -439,81 +473,87 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
continue;
}
if (is6)
if (data.action != ACTION_TFTP)
{
my_setenv("DNSMASQ_IAID", daemon->dhcp_buff3, &err);
my_setenv("DNSMASQ_SERVER_DUID", daemon->packet, &err);
}
if (!is6 && data.clid_len != 0)
my_setenv("DNSMASQ_CLIENT_ID", daemon->packet, &err);
if (strlen(data.interface) != 0)
my_setenv("DNSMASQ_INTERFACE", data.interface, &err);
#ifdef HAVE_BROKEN_RTC
my_setenv("DNSMASQ_LEASE_LENGTH", daemon->dhcp_buff2, &err);
#else
my_setenv("DNSMASQ_LEASE_EXPIRES", daemon->dhcp_buff2, &err);
#endif
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
{
if (data.hwaddr_len != 0)
if (is6)
{
buf = grab_extradata(buf, end, "DNSMASQ_VENDOR_CLASS_ID", &err);
for (i = 0; i < data.hwaddr_len - 1; i++)
my_setenv("DNSMASQ_IAID", daemon->dhcp_buff3, &err);
my_setenv("DNSMASQ_SERVER_DUID", daemon->packet, &err);
}
if (!is6 && data.clid_len != 0)
my_setenv("DNSMASQ_CLIENT_ID", daemon->packet, &err);
if (strlen(data.interface) != 0)
my_setenv("DNSMASQ_INTERFACE", data.interface, &err);
#ifdef HAVE_BROKEN_RTC
my_setenv("DNSMASQ_LEASE_LENGTH", daemon->dhcp_buff2, &err);
#else
my_setenv("DNSMASQ_LEASE_EXPIRES", daemon->dhcp_buff2, &err);
#endif
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
{
if (data.hwaddr_len != 0)
{
sprintf(daemon->dhcp_buff2, "DNSMASQ_VENDOR_CLASS%i", i);
buf = grab_extradata(buf, end, daemon->dhcp_buff2, &err);
buf = grab_extradata(buf, end, "DNSMASQ_VENDOR_CLASS_ID", &err);
for (i = 0; i < data.hwaddr_len - 1; i++)
{
sprintf(daemon->dhcp_buff2, "DNSMASQ_VENDOR_CLASS%i", i);
buf = grab_extradata(buf, end, daemon->dhcp_buff2, &err);
}
}
}
}
#endif
buf = grab_extradata(buf, end, "DNSMASQ_SUPPLIED_HOSTNAME", &err);
if (!is6)
{
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);
if (is6)
buf = grab_extradata(buf, end, "DNSMASQ_RELAY_ADDRESS", &err);
else if (data.giaddr.s_addr != 0)
my_setenv("DNSMASQ_RELAY_ADDRESS", inet_ntoa(data.giaddr), &err);
for (i = 0; buf; i++)
{
sprintf(daemon->dhcp_buff2, "DNSMASQ_USER_CLASS%i", i);
buf = grab_extradata(buf, end, daemon->dhcp_buff2, &err);
}
if (data.action != ACTION_DEL && data.remaining_time != 0)
{
sprintf(daemon->dhcp_buff2, "%u", data.remaining_time);
my_setenv("DNSMASQ_TIME_REMAINING", daemon->dhcp_buff2, &err);
}
if (data.action == ACTION_OLD_HOSTNAME && hostname)
{
my_setenv("DNSMASQ_OLD_HOSTNAME", hostname, &err);
hostname = NULL;
buf = grab_extradata(buf, end, "DNSMASQ_SUPPLIED_HOSTNAME", &err);
if (!is6)
{
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);
if (is6)
buf = grab_extradata(buf, end, "DNSMASQ_RELAY_ADDRESS", &err);
else if (data.giaddr.s_addr != 0)
my_setenv("DNSMASQ_RELAY_ADDRESS", inet_ntoa(data.giaddr), &err);
for (i = 0; buf; i++)
{
sprintf(daemon->dhcp_buff2, "DNSMASQ_USER_CLASS%i", i);
buf = grab_extradata(buf, end, daemon->dhcp_buff2, &err);
}
if (data.action != ACTION_DEL && data.remaining_time != 0)
{
sprintf(daemon->dhcp_buff2, "%u", data.remaining_time);
my_setenv("DNSMASQ_TIME_REMAINING", daemon->dhcp_buff2, &err);
}
if (data.action == ACTION_OLD_HOSTNAME && hostname)
{
my_setenv("DNSMASQ_OLD_HOSTNAME", hostname, &err);
hostname = NULL;
}
}
if (option_bool(OPT_LOG_OPTS))
my_setenv("DNSMASQ_LOG_DHCP", "1", &err);
/* we need to have the event_fd around if exec fails */
if ((i = fcntl(event_fd, F_GETFD)) != -1)
fcntl(event_fd, F_SETFD, i | FD_CLOEXEC);
@@ -584,11 +624,29 @@ static unsigned char *grab_extradata_lua(unsigned char *buf, unsigned char *end,
}
#endif
static void buff_alloc(size_t size)
{
if (size > buf_size)
{
struct script_data *new;
/* start with reasonable size, will almost never need extending. */
if (size < sizeof(struct script_data) + 200)
size = sizeof(struct script_data) + 200;
if (!(new = whine_malloc(size)))
return;
if (buf)
free(buf);
buf = new;
buf_size = size;
}
}
/* pack up lease data into a buffer */
void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t now)
{
unsigned char *p;
size_t size;
unsigned int hostname_len = 0, clid_len = 0, ed_len = 0;
int fd = daemon->dhcpfd;
@@ -608,23 +666,7 @@ void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t n
if (hostname)
hostname_len = strlen(hostname) + 1;
size = sizeof(struct script_data) + clid_len + ed_len + hostname_len;
if (size > buf_size)
{
struct script_data *new;
/* start with reasonable size, will almost never need extending. */
if (size < sizeof(struct script_data) + 200)
size = sizeof(struct script_data) + 200;
if (!(new = whine_malloc(size)))
return;
if (buf)
free(buf);
buf = new;
buf_size = size;
}
buff_alloc(sizeof(struct script_data) + clid_len + ed_len + hostname_len);
buf->action = action;
buf->flags = lease->flags;
@@ -669,6 +711,37 @@ void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t n
bytes_in_buf = p - (unsigned char *)buf;
}
#ifdef HAVE_TFTP
/* This nastily re-uses DHCP-fields for TFTP stuff */
void queue_tftp(off_t file_len, char *filename, union mysockaddr *peer)
{
unsigned int filename_len;
/* no script */
if (daemon->helperfd == -1)
return;
filename_len = strlen(filename) + 1;
buff_alloc(sizeof(struct script_data) + filename_len);
memset(buf, 0, sizeof(struct script_data));
buf->action = ACTION_TFTP;
buf->hostname_len = filename_len;
buf->hwaddr_len = file_len;
if ((buf->flags = peer->sa.sa_family) == AF_INET)
buf->addr = peer->in.sin_addr;
#ifdef HAVE_IPV6
else
memcpy(buf->hwaddr, &peer->in6.sin6_addr, IN6ADDRSZ);
#endif
memcpy((unsigned char *)(buf+1), filename, filename_len);
bytes_in_buf = sizeof(struct script_data) + filename_len;
}
#endif
int helper_buf_empty(void)
{
return bytes_in_buf == 0;

View File

@@ -97,7 +97,8 @@ void lease_init(time_t now)
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);
lease_set_hwaddr(lease, (unsigned char *)daemon->dhcp_buff2, (unsigned char *)daemon->packet,
hw_len, hw_type, clid_len, now, 0);
if (strcmp(daemon->dhcp_buff, "*") != 0)
lease_set_hostname(lease, daemon->dhcp_buff, 0, get_domain(lease->addr), NULL);
@@ -118,7 +119,7 @@ void lease_init(time_t now)
if ((lease = lease6_allocate(&addr.addr.addr6, lease_type)))
{
lease_set_hwaddr(lease, NULL, (unsigned char *)daemon->packet, 0, hw_type, clid_len);
lease_set_hwaddr(lease, NULL, (unsigned char *)daemon->packet, 0, hw_type, clid_len, now, 0);
if (strcmp(daemon->dhcp_buff, "*") != 0)
lease_set_hostname(lease, daemon->dhcp_buff, 0, get_domain6((struct in6_addr *)lease->hwaddr), NULL);
@@ -175,16 +176,6 @@ void lease_init(time_t now)
file_dirty = 0;
lease_prune(NULL, now);
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)
@@ -316,9 +307,16 @@ void lease_update_file(time_t now)
next_event = 0;
#ifdef HAVE_DHCP6
/* do timed RAs and determine when the next is */
/* do timed RAs and determine when the next is, also pings to potential SLAAC addresses */
if (daemon->ra_contexts)
next_event = periodic_ra(now);
{
time_t ra_event = periodic_slaac(now, leases);
next_event = periodic_ra(now);
if (next_event == 0 || difftime(next_event, ra_event) > 0.0)
next_event = ra_event;
}
#endif
for (lease = leases; lease; lease = lease->next)
@@ -336,24 +334,107 @@ void lease_update_file(time_t now)
(unsigned int)difftime(next_event, now));
}
if (next_event != 0)
alarm((unsigned)difftime(next_event, now));
send_alarm(next_event, now);
}
void lease_update_dns(void)
static int find_interface_v4(struct in_addr local, int if_index,
struct in_addr netmask, struct in_addr broadcast, void *vparam)
{
struct dhcp_lease *lease;
if (daemon->port != 0 && dns_dirty)
(void) broadcast;
(void) vparam;
for (lease = leases; lease; lease = lease->next)
if (!(lease->flags & (LEASE_TA | LEASE_NA)))
if (is_same_net(local, lease->addr, netmask))
lease_set_interface(lease, if_index, *((time_t *)vparam));
return 1;
}
#ifdef HAVE_DHCP6
static int find_interface_v6(struct in6_addr *local, int prefix,
int scope, int if_index, int dad, void *vparam)
{
struct dhcp_lease *lease;
(void)scope;
(void)dad;
for (lease = leases; lease; lease = lease->next)
if ((lease->flags & (LEASE_TA | LEASE_NA)))
if (is_same_net6(local, (struct in6_addr *)&lease->hwaddr, prefix))
lease_set_interface(lease, if_index, *((time_t *)vparam));
return 1;
}
void lease_ping_reply(struct in6_addr *sender, unsigned char *packet, char *interface)
{
/* We may be doing RA but not DHCPv4, in which case the lease
database may not exist and we have nothing to do anyway */
if (daemon->dhcp)
slaac_ping_reply(sender, packet, interface, leases);
}
#endif
/* Find interfaces associated with leases at start-up. This gets updated as
we do DHCP transactions, but information about directly-connected subnets
is useful from scrips and necessary for determining SLAAC addresses from
start-time. */
void lease_find_interfaces(time_t now)
{
#ifdef HAVE_DHCP6
build_subnet_map();
#endif
iface_enumerate(AF_INET, &now, find_interface_v4);
#ifdef HAVE_DHCP6
iface_enumerate(AF_INET6, &now, find_interface_v6);
/* 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_dns(int force)
{
struct dhcp_lease *lease;
if (daemon->port != 0 && (dns_dirty || force))
{
cache_unhash_dhcp();
for (lease = leases; lease; lease = lease->next)
{
int prot = AF_INET;
#ifdef HAVE_DHCP6
if (lease->flags & (LEASE_TA | LEASE_NA))
prot = AF_INET6;
else if (lease->hostname || lease->fqdn)
{
struct slaac_address *slaac;
for (slaac = lease->slaac_address; slaac; slaac = slaac->next)
if (slaac->backoff == 0)
{
if (lease->fqdn)
cache_add_dhcp_entry(lease->fqdn, AF_INET6, (struct all_addr *)&slaac->addr, lease->expires);
if (!option_bool(OPT_DHCP_FQDN) && lease->hostname)
cache_add_dhcp_entry(lease->hostname, AF_INET6, (struct all_addr *)&slaac->addr, lease->expires);
}
}
#endif
if (lease->fqdn)
@@ -621,8 +702,16 @@ void lease_set_expires(struct dhcp_lease *lease, unsigned int len, time_t now)
}
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,
time_t now, int force)
{
#ifdef HAVE_DHCP6
int change = force;
lease->flags |= LEASE_HAVE_HWADDR;
#endif
(void)force;
if (hw_len != lease->hwaddr_len ||
hw_type != lease->hwaddr_type ||
(hw_len != 0 && memcmp(lease->hwaddr, hwaddr, hw_len) != 0))
@@ -633,6 +722,9 @@ void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr,
lease->hwaddr_type = hw_type;
lease->flags |= LEASE_CHANGED;
file_dirty = 1; /* run script on change */
#ifdef HAVE_DHCP6
change = 1;
#endif
}
/* only update clid when one is available, stops packets
@@ -650,17 +742,27 @@ void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr,
free(lease->clid);
if (!(lease->clid = whine_malloc(clid_len)))
return;
#ifdef HAVE_DHCP6
change = 1;
#endif
}
else if (memcmp(lease->clid, clid, clid_len) != 0)
{
lease->flags |= LEASE_AUX_CHANGED;
file_dirty = 1;
#ifdef HAVE_DHCP6
change = 1;
#endif
}
lease->clid_len = clid_len;
memcpy(lease->clid, clid, clid_len);
}
#ifdef HAVE_DHCP6
if (change)
slaac_add_addrs(lease, now, force);
#endif
}
static void kill_name(struct dhcp_lease *lease)
@@ -778,13 +880,17 @@ void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth, char *do
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, time_t now)
{
if (lease->last_interface == interface)
return;
lease->last_interface = interface;
lease->flags |= LEASE_CHANGED;
#ifdef HAVE_DHCP6
slaac_add_addrs(lease, now, 0);
#endif
}
void rerun_scripts(void)
@@ -827,6 +933,14 @@ int do_script_run(time_t now)
}
else
{
#ifdef HAVE_DHCP6
struct slaac_address *slaac, *tmp;
for (slaac = lease->slaac_address; slaac; slaac = tmp)
{
tmp = slaac->next;
free(slaac);
}
#endif
kill_name(lease);
#ifdef HAVE_SCRIPT
queue_script(ACTION_DEL, lease, lease->old_hostname, now);

View File

@@ -345,10 +345,11 @@ static void nl_routechange(struct nlmsghdr *h)
/* force RAs to sync new network and pick up new interfaces. */
if (daemon->ra_contexts)
{
ra_start_unsolicted(dnsmasq_time());
schedule_subnet_map();
ra_start_unsolicted(dnsmasq_time(), NULL);
/* cause lease_update_file to run after we return, in case we were called from
iface_enumerate and can't re-enter it now */
send_alarm();
send_alarm(0, 0);
}
#endif

View File

@@ -162,6 +162,7 @@ static int iface_allowed(struct irec **irecp, int if_index,
int fd, mtu = 0, loopback;
struct ifreq ifr;
int tftp_ok = daemon->tftp_unlimited;
int dhcp_ok = 1;
#ifdef HAVE_DHCP
struct iname *tmp;
#endif
@@ -190,6 +191,9 @@ static int iface_allowed(struct irec **irecp, int if_index,
}
loopback = ifr.ifr_flags & IFF_LOOPBACK;
if (loopback)
dhcp_ok = 0;
if (ioctl(fd, SIOCGIFMTU, &ifr) != -1)
mtu = ifr.ifr_mtu;
@@ -238,7 +242,10 @@ static int iface_allowed(struct irec **irecp, int if_index,
#ifdef HAVE_DHCP
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
tftp_ok = 0;
{
tftp_ok = 0;
dhcp_ok = 0;
}
#endif
#ifdef HAVE_IPV6
@@ -254,14 +261,18 @@ static int iface_allowed(struct irec **irecp, int if_index,
iface->addr = *addr;
iface->netmask = netmask;
iface->tftp_ok = tftp_ok;
iface->dhcp_ok = dhcp_ok;
iface->mtu = mtu;
iface->dad = dad;
iface->done = 0;
if ((iface->name = whine_malloc(strlen(ifr.ifr_name)+1)))
strcpy(iface->name, ifr.ifr_name);
iface->next = *irecp;
*irecp = iface;
return 1;
{
strcpy(iface->name, ifr.ifr_name);
iface->next = *irecp;
*irecp = iface;
return 1;
}
free(iface);
}
errno = ENOMEM;
@@ -511,6 +522,7 @@ void create_bound_listeners(int dienow)
{
struct listener *new;
struct irec *iface;
struct iname *if_tmp;
for (iface = daemon->interfaces; iface; iface = iface->next)
if (!iface->done && !iface->dad &&
@@ -521,6 +533,25 @@ void create_bound_listeners(int dienow)
daemon->listeners = new;
iface->done = 1;
}
/* Check for --listen-address options that haven't been used because there's
no interface with a matching address. These may be valid: eg it's possible
to listen on 127.0.1.1 even if the loopback interface is 127.0.0.1
If the address isn't valid the bind() will fail and we'll die().
The resulting listeners have the ->iface field NULL, and this has to be
handled by the DNS and TFTP code. It disables --localise-queries processing
(no netmask) and some MTU login the tftp code. */
for (if_tmp = daemon->if_addrs; if_tmp; if_tmp = if_tmp->next)
if (!if_tmp->used &&
(new = create_listeners(&if_tmp->addr, 1, dienow)))
{
new->iface = NULL;
new->next = daemon->listeners;
daemon->listeners = new;
}
}
int is_dad_listeners(void)

View File

@@ -115,6 +115,9 @@ struct myoption {
#define LOPT_FQDN 304
#define LOPT_LUASCRIPT 305
#define LOPT_RA 306
#define LOPT_DUID 307
#define LOPT_HOST_REC 308
#define LOPT_TFTP_LC 309
#ifdef HAVE_GETOPT_LONG
static const struct option opts[] =
@@ -194,6 +197,7 @@ static const struct myoption opts[] =
{ "tftp-unique-root", 0, 0, LOPT_APREF },
{ "tftp-root", 1, 0, LOPT_PREFIX },
{ "tftp-max", 1, 0, LOPT_TFTP_MAX },
{ "tftp-lowercase", 0, 0, LOPT_TFTP_LC },
{ "ptr-record", 1, 0, LOPT_PTR },
{ "naptr-record", 1, 0, LOPT_NAPTR },
{ "bridge-interface", 1, 0 , LOPT_BRIDGE },
@@ -235,6 +239,8 @@ static const struct myoption opts[] =
{ "dhcp-client-update", 0, 0, LOPT_FQDN },
{ "dhcp-luascript", 1, 0, LOPT_LUASCRIPT },
{ "enable-ra", 0, 0, LOPT_RA },
{ "dhcp-duid", 1, 0, LOPT_DUID },
{ "host-record", 1, 0, LOPT_HOST_REC },
{ NULL, 0, 0, 0 }
};
@@ -338,6 +344,7 @@ static struct {
{ LOPT_SECURE, OPT_TFTP_SECURE, NULL, gettext_noop("Allow access only to files owned by the user running dnsmasq."), NULL },
{ LOPT_TFTP_MAX, ARG_ONE, "<integer>", gettext_noop("Maximum number of conncurrent TFTP transfers (defaults to %s)."), "#" },
{ LOPT_NOBLOCK, OPT_TFTP_NOBLOCK, NULL, gettext_noop("Disable the TFTP blocksize extension."), NULL },
{ LOPT_TFTP_LC, OPT_TFTP_LC, NULL, gettext_noop("Convert TFTP filenames to lowercase"), NULL },
{ LOPT_TFTPPORTS, ARG_ONE, "<start>,<end>", gettext_noop("Ephemeral port range for use by TFTP transfers."), NULL },
{ LOPT_LOG_OPTS, OPT_LOG_OPTS, NULL, gettext_noop("Extra logging for DHCP."), NULL },
{ LOPT_MAX_LOGS, ARG_ONE, "[=<integer>]", gettext_noop("Enable async. logging; optionally set queue length."), NULL },
@@ -362,242 +369,11 @@ static struct {
{ LOPT_CONNTRACK, OPT_CONNTRACK, NULL, gettext_noop("Copy connection-track mark from queries to upstream connections."), NULL },
{ LOPT_FQDN, OPT_FQDN_UPDATE, NULL, gettext_noop("Allow DHCP clients to do their own DDNS updates."), NULL },
{ LOPT_RA, OPT_RA, NULL, gettext_noop("Send router-advertisements for interfaces doing DHCPv6"), NULL },
{ LOPT_DUID, ARG_ONE, "<enterprise>,<duid>", gettext_noop("Specify DUID_EN-type DHCPv6 server DUID"), NULL },
{ LOPT_HOST_REC, ARG_DUP, "<name>,<address>", gettext_noop("Specify host (A/AAAA and PTR) records"), NULL },
{ 0, 0, NULL, NULL, NULL }
};
#ifdef HAVE_DHCP
#define OT_ADDR_LIST 0x8000
#define OT_RFC1035_NAME 0x4000
#define OT_INTERNAL 0x2000
#define OT_NAME 0x1000
#define OT_CSTRING 0x0800
static const struct opttab_t {
char *name;
u16 val, size;
} opttab[] = {
{ "netmask", 1, OT_ADDR_LIST },
{ "time-offset", 2, 4 },
{ "router", 3, OT_ADDR_LIST },
{ "dns-server", 6, OT_ADDR_LIST },
{ "log-server", 7, OT_ADDR_LIST },
{ "lpr-server", 9, OT_ADDR_LIST },
{ "hostname", 12, OT_INTERNAL | OT_NAME },
{ "boot-file-size", 13, 2 },
{ "domain-name", 15, OT_NAME },
{ "swap-server", 16, OT_ADDR_LIST },
{ "root-path", 17, OT_NAME },
{ "extension-path", 18, OT_NAME },
{ "ip-forward-enable", 19, 1 },
{ "non-local-source-routing", 20, 1 },
{ "policy-filter", 21, OT_ADDR_LIST },
{ "max-datagram-reassembly", 22, 2 },
{ "default-ttl", 23, 1 },
{ "mtu", 26, 2 },
{ "all-subnets-local", 27, 1 },
{ "broadcast", 28, OT_INTERNAL | OT_ADDR_LIST },
{ "router-discovery", 31, 1 },
{ "router-solicitation", 32, OT_ADDR_LIST },
{ "static-route", 33, OT_ADDR_LIST },
{ "trailer-encapsulation", 34, 1 },
{ "arp-timeout", 35, 4 },
{ "ethernet-encap", 36, 1 },
{ "tcp-ttl", 37, 1 },
{ "tcp-keepalive", 38, 4 },
{ "nis-domain", 40, OT_NAME },
{ "nis-server", 41, OT_ADDR_LIST },
{ "ntp-server", 42, OT_ADDR_LIST },
{ "vendor-encap", 43, OT_INTERNAL },
{ "netbios-ns", 44, OT_ADDR_LIST },
{ "netbios-dd", 45, OT_ADDR_LIST },
{ "netbios-nodetype", 46, 1 },
{ "netbios-scope", 47, 0 },
{ "x-windows-fs", 48, OT_ADDR_LIST },
{ "x-windows-dm", 49, OT_ADDR_LIST },
{ "requested-address", 50, OT_INTERNAL | OT_ADDR_LIST },
{ "lease-time", 51, OT_INTERNAL },
{ "option-overload", 52, OT_INTERNAL },
{ "message-type", 53, OT_INTERNAL, },
{ "server-identifier", 54, OT_INTERNAL | OT_ADDR_LIST },
{ "parameter-request", 55, OT_INTERNAL },
{ "message", 56, OT_INTERNAL },
{ "max-message-size", 57, OT_INTERNAL },
{ "T1", 58, OT_INTERNAL },
{ "T2", 59, OT_INTERNAL },
{ "vendor-class", 60, 0 },
{ "client-id", 61, OT_INTERNAL },
{ "nis+-domain", 64, OT_NAME },
{ "nis+-server", 65, OT_ADDR_LIST },
{ "tftp-server", 66, OT_NAME },
{ "bootfile-name", 67, OT_NAME },
{ "mobile-ip-home", 68, OT_ADDR_LIST },
{ "smtp-server", 69, OT_ADDR_LIST },
{ "pop3-server", 70, OT_ADDR_LIST },
{ "nntp-server", 71, OT_ADDR_LIST },
{ "irc-server", 74, OT_ADDR_LIST },
{ "user-class", 77, 0 },
{ "FQDN", 81, OT_INTERNAL },
{ "agent-id", 82, OT_INTERNAL },
{ "client-arch", 93, 2 },
{ "client-interface-id", 94, 0 },
{ "client-machine-id", 97, 0 },
{ "subnet-select", 118, OT_INTERNAL },
{ "domain-search", 119, OT_RFC1035_NAME },
{ "sip-server", 120, 0 },
{ "classless-static-route", 121, 0 },
{ "vendor-id-encap", 125, 0 },
{ "server-ip-address", 255, OT_ADDR_LIST }, /* special, internal only, sets siaddr */
{ NULL, 0, 0 }
};
#ifdef HAVE_DHCP6
static const struct opttab_t opttab6[] = {
{ "client-id", 1, OT_INTERNAL },
{ "server-id", 2, OT_INTERNAL },
{ "ia-na", 3, OT_INTERNAL },
{ "ia-ta", 4, OT_INTERNAL },
{ "iaaddr", 5, OT_INTERNAL },
{ "oro", 6, OT_INTERNAL },
{ "preference", 7, OT_INTERNAL },
{ "unicast", 12, OT_INTERNAL },
{ "status-code", 13, OT_INTERNAL },
{ "rapid-commit", 14, OT_INTERNAL },
{ "user-class", 15, OT_INTERNAL | OT_CSTRING },
{ "vendor-class", 16, OT_INTERNAL | OT_CSTRING },
{ "vendor-opts", 17, OT_INTERNAL },
{ "sip-server-domain", 21, OT_RFC1035_NAME },
{ "sip-server", 22, OT_ADDR_LIST },
{ "dns-server", 23, OT_ADDR_LIST },
{ "domain-search", 24, OT_RFC1035_NAME },
{ "nis-server", 27, OT_ADDR_LIST },
{ "nis+-server", 28, OT_ADDR_LIST },
{ "nis-domain", 29, OT_RFC1035_NAME },
{ "nis+-domain", 30, OT_RFC1035_NAME },
{ "sntp-server", 31, OT_ADDR_LIST },
{ "FQDN", 39, OT_INTERNAL | OT_RFC1035_NAME },
{ "ntp-server", 56, OT_ADDR_LIST },
{ "bootfile-url", 59, OT_NAME },
{ "bootfile-param", 60, OT_CSTRING },
{ NULL, 0, 0 }
};
#endif
char *option_string(int prot, unsigned int opt, unsigned char *val, int opt_len, char *buf, int buf_len)
{
int o, i, j, nodecode = 0;
const struct opttab_t *ot = opttab;
#ifdef HAVE_DHCP6
if (prot == AF_INET6)
ot = opttab6;
#endif
for (o = 0; ot[o].name; o++)
if (ot[o].val == opt)
{
if (buf)
{
memset(buf, 0, buf_len);
if (ot[o].size & OT_ADDR_LIST)
{
struct all_addr addr;
int addr_len = INADDRSZ;
#ifdef HAVE_DHCP6
if (prot == AF_INET6)
addr_len = IN6ADDRSZ;
#endif
for (buf[0]= 0, i = 0; i <= opt_len - addr_len; i += addr_len)
{
if (i != 0)
strncat(buf, ", ", buf_len - strlen(buf));
/* align */
memcpy(&addr, &val[i], addr_len);
inet_ntop(prot, &val[i], daemon->addrbuff, ADDRSTRLEN);
strncat(buf, daemon->addrbuff, buf_len - strlen(buf));
}
}
else if (ot[o].size & OT_NAME)
for (i = 0, j = 0; i < opt_len && j < buf_len ; i++)
{
char c = val[i];
if (isprint((int)c))
buf[j++] = c;
}
#ifdef HAVE_DHCP6
/* We don't handle compressed rfc1035 names, so no good in IPv4 land */
else if ((ot[o].size & OT_RFC1035_NAME) && prot == AF_INET6)
{
i = 0, j = 0;
while (i < opt_len && val[i] != 0)
{
int k, l = i + val[i] + 1;
for (k = i + 1; k < opt_len && k < l && j < buf_len ; k++)
{
char c = val[k];
if (isprint((int)c))
buf[j++] = c;
}
i = l;
if (val[i] != 0 && j < buf_len)
buf[j++] = '.';
}
}
else if ((ot[o].size & OT_CSTRING))
{
int k, len;
unsigned char *p;
i = 0, j = 0;
while (1)
{
p = &val[i];
GETSHORT(len, p);
for (k = 0; k < len && j < buf_len; k++)
{
char c = *p++;
if (isprint((int)c))
buf[j++] = c;
}
i += len +2;
if (i >= opt_len)
break;
if (j < buf_len)
buf[j++] = ',';
}
}
#endif
else
nodecode = 1;
}
break;
}
if (buf && (!ot[o].name || nodecode))
{
int trunc = 0;
if (opt_len > 13)
{
trunc = 1;
opt_len = 13;
}
print_mac(buf, val, opt_len);
if (trunc)
strncat(buf, "...", buf_len - strlen(buf));
}
return ot[o].name ? ot[o].name : "";
}
#endif
/* We hide metacharaters in quoted strings by mapping them into the ASCII control
character space. Note that the \0, \t \b \r \033 and \n characters are carefully placed in the
following sequence so that they map to themselves: it is therefore possible to call
@@ -820,29 +596,6 @@ static void do_usage(void)
}
#ifdef HAVE_DHCP
static void display_opts(void)
{
int i;
printf(_("Known DHCP options:\n"));
for (i = 0; opttab[i].name; i++)
if (!(opttab[i].size & OT_INTERNAL))
printf("%3d %s\n", opttab[i].val, opttab[i].name);
}
#ifdef HAVE_DHCP6
static void display_opts6(void)
{
int i;
printf(_("Known DHCPv6 options:\n"));
for (i = 0; opttab6[i].name; i++)
if (!(opttab6[i].size & OT_INTERNAL))
printf("%3d %s\n", opttab6[i].val, opttab6[i].name);
}
#endif
static int is_tag_prefix(char *arg)
{
@@ -865,7 +618,7 @@ static char *parse_dhcp_opt(char *arg, int flags)
{
struct dhcp_opt *new = opt_malloc(sizeof(struct dhcp_opt));
char lenchar = 0, *cp;
int i, addrs, digs, is_addr, is_addr6, is_hex, is_dec, is_string, dots;
int addrs, digs, is_addr, is_addr6, is_hex, is_dec, is_string, dots;
char *comma = NULL, *problem = NULL;
struct dhcp_netid *np = NULL;
u16 opt_len = 0;
@@ -894,14 +647,8 @@ static char *parse_dhcp_opt(char *arg, int flags)
if (strstr(arg, "option:") == arg)
{
for (i = 0; opttab[i].name; i++)
if (!(opttab[i].size & OT_INTERNAL) &&
strcasecmp(opttab[i].name, arg+7) == 0)
{
new->opt = opttab[i].val;
opt_len = opttab[i].size;
break;
}
new->opt = lookup_dhcp_opt(AF_INET, arg+7);
opt_len = lookup_dhcp_len(AF_INET, new->opt);
/* option:<optname> must follow tag and vendor string. */
break;
}
@@ -918,14 +665,10 @@ static char *parse_dhcp_opt(char *arg, int flags)
opt_len = 0;
}
else
for (i = 0; opttab6[i].name; i++)
if (!(opttab6[i].size & OT_INTERNAL) &&
strcasecmp(opttab6[i].name, arg+8) == 0)
{
new->opt = opttab6[i].val;
opt_len = opttab6[i].size;
break;
}
{
new->opt = lookup_dhcp_opt(AF_INET6, arg+8);
opt_len = lookup_dhcp_len(AF_INET6, new->opt);
}
/* option6:<opt>|<optname> must follow tag and vendor string. */
is6 = 1;
break;
@@ -974,28 +717,14 @@ static char *parse_dhcp_opt(char *arg, int flags)
if (opt_len == 0 &&
!(new->flags & DHOPT_RFC3925))
for (i = 0; opttab6[i].name; i++)
if (new->opt == opttab6[i].val)
{
opt_len = opttab6[i].size;
if (opt_len & OT_INTERNAL)
opt_len = 0;
break;
}
opt_len = lookup_dhcp_len(AF_INET6 ,new->opt);
}
else
#endif
if (opt_len == 0 &&
!(new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE | DHOPT_RFC3925)))
for (i = 0; opttab[i].name; i++)
if (new->opt == opttab[i].val)
{
opt_len = opttab[i].size;
if (opt_len & OT_INTERNAL)
opt_len = 0;
break;
}
opt_len = lookup_dhcp_len(AF_INET ,new->opt);
/* option may be missing with rfc3925 match */
if (new->opt == 0)
problem = _("bad dhcp-option");
@@ -1259,10 +988,10 @@ static char *parse_dhcp_opt(char *arg, int flags)
else if (comma && (opt_len & OT_CSTRING))
{
/* length fields are two bytes so need 16 bits for each string */
int commas = 1;
int i, commas = 1;
unsigned char *p, *newp;
for(i = 0; comma[i]; i++)
for (i = 0; comma[i]; i++)
if (comma[i] == ',')
commas++;
@@ -1274,6 +1003,7 @@ static char *parse_dhcp_opt(char *arg, int flags)
while (arg && *arg)
{
u16 len = strlen(arg);
unhide_metas(arg);
PUTSHORT(len, p);
memcpy(p, arg, len);
p += len;
@@ -1287,29 +1017,40 @@ static char *parse_dhcp_opt(char *arg, int flags)
}
else if (comma && (opt_len & OT_RFC1035_NAME))
{
int commas = 1;
unsigned char *p, *newp;
for(i = 0; comma[i]; i++)
if (comma[i] == ',')
commas++;
newp = opt_malloc(strlen(comma)+(2*commas));
p = newp;
unsigned char *p = NULL, *newp, *end;
int len = 0;
arg = comma;
comma = split(arg);
while (arg && *arg)
{
p = do_rfc1035_name(p, arg);
*p++ = 0;
char *dom = canonicalise_opt(arg);
if (!dom)
{
problem = _("bad domain in dhcp-option");
break;
}
newp = opt_malloc(len + strlen(dom) + 2);
if (p)
{
memcpy(newp, p, len);
free(p);
}
p = newp;
end = do_rfc1035_name(p + len, dom);
*end++ = 0;
len = end - p;
free(dom);
arg = comma;
comma = split(arg);
}
new->val = newp;
new->len = p - newp;
new->val = p;
new->len = len;
}
#endif
else
@@ -2233,7 +1974,7 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
case 'F': /* --dhcp-range */
{
int k, leasepos = 2;
char *cp, *a[5] = { NULL, NULL, NULL, NULL, NULL };
char *cp, *a[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
struct dhcp_context *new = opt_malloc(sizeof(struct dhcp_context));
memset (new, 0, sizeof(*new));
@@ -2284,7 +2025,7 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
}
}
for (k = 1; k < 5; k++)
for (k = 1; k < 7; k++)
if (!(a[k] = split(a[k-1])))
break;
@@ -2294,17 +2035,12 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
{
new->next = daemon->dhcp;
daemon->dhcp = new;
new->end = new->start;
if (strcmp(a[1], "static") == 0)
{
new->end = new->start;
new->flags |= CONTEXT_STATIC;
}
new->flags |= CONTEXT_STATIC;
else if (strcmp(a[1], "proxy") == 0)
{
new->end = new->start;
new->flags |= CONTEXT_PROXY;
}
else if ((new->end.s_addr = inet_addr(a[1])) == (in_addr_t)-1)
new->flags |= CONTEXT_PROXY;
else if (!inet_pton(AF_INET, a[1], &new->end))
option = '?';
if (ntohl(new->start.s_addr) > ntohl(new->end.s_addr))
@@ -2334,46 +2070,54 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
else if (inet_pton(AF_INET6, a[0], &new->start6))
{
new->prefix = 64; /* default */
new->end6 = new->start6;
for (leasepos = 1; leasepos < k; leasepos++)
{
if (strcmp(a[leasepos], "static") == 0)
new->flags |= CONTEXT_STATIC | CONTEXT_DHCP;
else if (strcmp(a[leasepos], "ra-only") == 0 || strcmp(a[leasepos], "slaac") == 0 )
new->flags |= CONTEXT_RA_ONLY;
else if (strcmp(a[leasepos], "ra-names") == 0)
new->flags |= CONTEXT_RA_NAME;
else if (strcmp(a[leasepos], "ra-stateless") == 0)
new->flags |= CONTEXT_RA_STATELESS | CONTEXT_DHCP;
else if (leasepos == 1 && inet_pton(AF_INET6, a[leasepos], &new->end6))
new->flags |= CONTEXT_DHCP;
else
break;
}
if (strcmp(a[1], "static") == 0)
{
memcpy(&new->end6, &new->start6, IN6ADDRSZ);
new->flags |= CONTEXT_STATIC;
}
else if (strcmp(a[1], "ra-only") == 0)
{
memcpy(&new->end6, &new->start6, IN6ADDRSZ);
new->flags |= CONTEXT_RA_ONLY;
}
else if (!inet_pton(AF_INET6, a[1], &new->end6))
option = '?';
if (new->flags & CONTEXT_RA_ONLY)
{
new->next = daemon->ra_contexts;
daemon->ra_contexts = new;
}
else
if (new->flags & CONTEXT_DHCP)
{
new->next = daemon->dhcp6;
daemon->dhcp6 = new;
}
else
{
new->next = daemon->ra_contexts;
daemon->ra_contexts = new;
}
/* bare integer < 128 is prefix value */
if (option != '?' && k >= 3)
if (option != '?' && leasepos < k)
{
int pref;
for (cp = a[2]; *cp; cp++)
for (cp = a[leasepos]; *cp; cp++)
if (!(*cp >= '0' && *cp <= '9'))
break;
if (!*cp && (pref = atoi(a[2])) <= 128)
if (!*cp && (pref = atoi(a[leasepos])) <= 128)
{
new->prefix = pref;
leasepos = 3;
if (new->prefix < 64)
leasepos++;
if ((new->flags & (CONTEXT_RA_ONLY | CONTEXT_RA_NAME | CONTEXT_RA_STATELESS)) &&
new->prefix != 64)
problem = _("prefix must be exactly 64 for RA subnets");
else if (new->prefix < 64)
problem = _("prefix must be at least 64");
}
}
if (!problem && !is_same_net6(&new->start6, &new->end6, new->prefix))
problem = _("inconsistent DHCPv6 range");
else if (addr6part(&new->start6) > addr6part(&new->end6))
@@ -2385,10 +2129,12 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
}
#endif
if (k >= leasepos+1)
if (leasepos < k)
{
if (strcmp(a[leasepos], "infinite") == 0)
new->lease_time = 0xffffffff;
else if (strcmp(a[leasepos], "deprecated") == 0)
new->flags |= CONTEXT_DEPRECATE;
else
{
int fac = 1;
@@ -2421,16 +2167,6 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
}
}
}
#ifdef HAVE_DHCP6
/* lifetimes must be min 2 hrs, by RFC 2462.
This gets enforced in radv.c for DHCP ranges
which are legitimately less. */
if ((new->flags & CONTEXT_RA_ONLY) &&
new->lease_time < 7200)
new->lease_time = 7200;
#endif
break;
}
@@ -3040,6 +2776,19 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
break;
#endif
#ifdef HAVE_DHCP6
case LOPT_DUID: /* --dhcp-duid */
if (!(comma = split(arg)) || !atoi_check(arg, (int *)&daemon->duid_enterprise))
problem = _("bad DUID");
else
{
daemon->duid_config_len = parse_hex(comma,(unsigned char *)comma, strlen(comma), NULL, NULL);
daemon->duid_config = opt_malloc(daemon->duid_config_len);
memcpy(daemon->duid_config, comma, daemon->duid_config_len);
}
break;
#endif
case 'V': /* --alias */
{
char *dash, *a[3] = { NULL, NULL, NULL };
@@ -3287,6 +3036,59 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
break;
}
case LOPT_HOST_REC: /* --host-record */
{
struct host_record *new = opt_malloc(sizeof(struct host_record));
memset(new, 0, sizeof(struct host_record));
if (!arg || !(comma = split(arg)))
problem = _("Bad host-record");
else
while (arg)
{
struct all_addr addr;
if (inet_pton(AF_INET, arg, &addr))
new->addr = addr.addr.addr4;
#ifdef HAVE_IPV6
else if (inet_pton(AF_INET6, arg, &addr))
new->addr6 = addr.addr.addr6;
#endif
else
{
int nomem;
char *canon = canonicalise(arg, &nomem);
struct name_list *nl = opt_malloc(sizeof(struct name_list));
if (!canon)
{
problem = _("Bad name in host-record");
break;
}
nl->name = canon;
/* keep order, so that PTR record goes to first name */
nl->next = NULL;
if (!new->names)
new->names = nl;
else
{
struct name_list *tmp;
for (tmp = new->names; tmp->next; tmp = tmp->next);
tmp->next = nl;
}
}
arg = comma;
comma = split(arg);
}
/* Keep list order */
if (!daemon->host_records_tail)
daemon->host_records = new;
else
daemon->host_records_tail->next = new;
new->next = NULL;
daemon->host_records_tail = new;
break;
}
default:
return _("unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DBus support)");

View File

@@ -17,6 +17,13 @@
#define ALL_HOSTS "FF02::1"
#define ALL_ROUTERS "FF02::2"
struct ping_packet {
u8 type, code;
u16 checksum;
u16 identifier;
u16 sequence_no;
};
struct ra_packet {
u8 type, code;
u16 checksum;
@@ -32,13 +39,11 @@ struct prefix_opt {
struct in6_addr prefix;
};
#define ICMP6_ROUTER_SOLICIT 133
#define ICMP6_ROUTER_ADVERT 134
#define ICMP6_OPT_SOURCE_MAC 1
#define ICMP6_OPT_PREFIX 3
#define ICMP6_OPT_MTU 5
#define ICMP6_OPT_RDNSS 25
#define ICMP6_OPT_DNSSL 31

View File

@@ -27,8 +27,9 @@
#include <netinet/icmp6.h>
struct ra_param {
int ind, managed, found_context, first;
int ind, managed, other, found_context, first;
char *if_name;
struct dhcp_netid *tags;
struct in6_addr link_local;
};
@@ -50,22 +51,29 @@ void ra_init(time_t now)
{
struct icmp6_filter filter;
int fd;
#if defined(IP_TOS) && defined(IPTOS_CLASS_CS6)
#if defined(IPV6_TCLASS) && defined(IPTOS_CLASS_CS6)
int class = IPTOS_CLASS_CS6;
#endif
int val = 255; /* radvd uses this value */
socklen_t len = sizeof(int);
struct dhcp_context *context;
/* ensure this is around even if we're not doing DHCPv6 */
expand_buf(&daemon->outpacket, sizeof(struct dhcp_packet));
/* See if we're guessing SLAAC addresses, if so we need to recieve ping replies */
for (context = daemon->ra_contexts; context; context = context->next)
if ((context->flags & CONTEXT_RA_NAME))
break;
ICMP6_FILTER_SETBLOCKALL(&filter);
ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filter);
ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filter);
if (context)
ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filter);
if ((fd = socket(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) == -1 ||
getsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &hop_limit, &len) ||
#if defined(IP_TOS) && defined(IPTOS_CLASS_CS6)
#if defined(IPV6_TCLASS) && defined(IPTOS_CLASS_CS6)
setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &class, sizeof(class)) == -1 ||
#endif
!fix_fd(fd) ||
@@ -77,21 +85,21 @@ void ra_init(time_t now)
daemon->icmp6fd = fd;
ra_start_unsolicted(now);
ra_start_unsolicted(now, NULL);
}
void ra_start_unsolicted(time_t now)
void ra_start_unsolicted(time_t now, struct dhcp_context *context)
{
struct dhcp_context *context;
/* init timers so that we do ra's for all soon. some ra_times will end up zeroed
/* init timers so that we do ra's for some/all soon. some ra_times will end up zeroed
if it's not appropriate to advertise those contexts.
This gets re-called on a netlink route-change to re-do the advertisement
and pick up new interfaces */
/* range 0 - 5 */
for (context = daemon->ra_contexts; context; context = context->next)
context->ra_time = now + (rand16()/13000);
if (context)
context->ra_time = now;
else
for (context = daemon->ra_contexts; context; context = context->next)
context->ra_time = now + (rand16()/13000); /* range 0 - 5 */
/* re-do frequently for a minute or so, in case the first gets lost. */
ra_short_period_start = now;
@@ -109,8 +117,7 @@ void icmp6_packet(void)
char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
} control_u;
struct sockaddr_in6 from;
unsigned char *p;
char *mac = "";
unsigned char *packet;
struct iname *tmp;
struct dhcp_context *context;
@@ -125,6 +132,8 @@ void icmp6_packet(void)
if ((sz = recv_dhcp_packet(daemon->icmp6fd, &msg)) == -1 || sz < 8)
return;
packet = (unsigned char *)daemon->outpacket.iov_base;
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
@@ -153,24 +162,25 @@ void icmp6_packet(void)
if (!context->interface || strcmp(context->interface, interface) == 0)
break;
if (!context)
if (!context || packet[1] != 0)
return;
p = (unsigned char *)daemon->outpacket.iov_base;
if (p[0] != ICMP6_ROUTER_SOLICIT || p[1] != 0)
return;
/* look for link-layer address option for logging */
if (sz >= 16 && p[8] == ICMP6_OPT_SOURCE_MAC && (p[9] * 8) + 8 <= sz)
if (packet[0] == ICMP6_ECHO_REPLY)
lease_ping_reply(&from.sin6_addr, packet, interface);
else if (packet[0] == ND_ROUTER_SOLICIT)
{
print_mac(daemon->namebuff, &p[10], (p[9] * 8) - 2);
mac = daemon->namebuff;
char *mac = "";
/* look for link-layer address option for logging */
if (sz >= 16 && packet[8] == ICMP6_OPT_SOURCE_MAC && (packet[9] * 8) + 8 <= sz)
{
print_mac(daemon->namebuff, &packet[10], (packet[9] * 8) - 2);
mac = daemon->namebuff;
}
my_syslog(MS_DHCP | LOG_INFO, "RTR-SOLICIT(%s) %s", interface, mac);
send_ra(if_index, interface, &from.sin6_addr);
}
my_syslog(MS_DHCP | LOG_INFO, "RTR-SOLICIT(%s) %s", interface, mac);
send_ra(if_index, interface, &from.sin6_addr);
}
static void send_ra(int iface, char *iface_name, struct in6_addr *dest)
@@ -180,27 +190,39 @@ static void send_ra(int iface, char *iface_name, struct in6_addr *dest)
struct ifreq ifr;
struct sockaddr_in6 addr;
struct dhcp_context *context;
struct dhcp_netid iface_id;
struct dhcp_opt *opt_cfg;
int done_dns = 0;
save_counter(0);
ra = expand(sizeof(struct ra_packet));
ra->type = ICMP6_ROUTER_ADVERT;
ra->type = ND_ROUTER_ADVERT;
ra->code = 0;
ra->hop_limit = hop_limit;
ra->flags = 0;
ra->flags = 0x00;
ra->lifetime = htons(1800); /* AdvDefaultLifetime*/
ra->reachable_time = 0;
ra->retrans_time = 0;
parm.ind = iface;
parm.managed = 0;
parm.other = 0;
parm.found_context = 0;
parm.if_name = iface_name;
parm.first = 1;
for (context = daemon->ra_contexts; context; context = context->next)
context->flags &= ~CONTEXT_RA_DONE;
/* set tag with name == interface */
iface_id.net = iface_name;
iface_id.next = NULL;
parm.tags = &iface_id;
for (context = daemon->ra_contexts; context; context = context->next)
{
context->flags &= ~CONTEXT_RA_DONE;
context->netid.next = &context->netid;
}
if (!iface_enumerate(AF_INET6, &parm, add_prefixes) ||
!parm.found_context)
return;
@@ -216,19 +238,70 @@ static void send_ra(int iface, char *iface_name, struct in6_addr *dest)
}
iface_enumerate(AF_LOCAL, &iface, add_lla);
/* RDNSS, RFC 6106, use relevant DHCP6 options */
(void)option_filter(parm.tags, NULL, daemon->dhcp_opts6);
/* RDNSS, RFC 6106 */
put_opt6_char(ICMP6_OPT_RDNSS);
put_opt6_char(3);
put_opt6_short(0);
put_opt6_long(1800); /* lifetime - twice RA retransmit */
put_opt6(&parm.link_local, IN6ADDRSZ);
for (opt_cfg = daemon->dhcp_opts6; opt_cfg; opt_cfg = opt_cfg->next)
{
int i;
/* netids match and not encapsulated? */
if (!(opt_cfg->flags & DHOPT_TAGOK))
continue;
if (opt_cfg->opt == OPTION6_DNS_SERVER)
{
struct in6_addr *a = (struct in6_addr *)opt_cfg->val;
done_dns = 1;
if (opt_cfg->len == 0)
continue;
put_opt6_char(ICMP6_OPT_RDNSS);
put_opt6_char((opt_cfg->len/8) + 1);
put_opt6_short(0);
put_opt6_long(1800); /* lifetime - twice RA retransmit */
/* zero means "self" */
for (i = 0; i < opt_cfg->len; i += IN6ADDRSZ, a++)
if (IN6_IS_ADDR_UNSPECIFIED(a))
put_opt6(&parm.link_local, IN6ADDRSZ);
else
put_opt6(a, IN6ADDRSZ);
}
if (opt_cfg->opt == OPTION6_DOMAIN_SEARCH && opt_cfg->len != 0)
{
int len = ((opt_cfg->len+7)/8);
put_opt6_char(ICMP6_OPT_DNSSL);
put_opt6_char(len + 1);
put_opt6_short(0);
put_opt6_long(1800); /* lifetime - twice RA retransmit */
put_opt6(opt_cfg->val, opt_cfg->len);
/* pad */
for (i = opt_cfg->len; i < len * 8; i++)
put_opt6_char(0);
}
}
if (!done_dns)
{
/* default == us. */
put_opt6_char(ICMP6_OPT_RDNSS);
put_opt6_char(3);
put_opt6_short(0);
put_opt6_long(1800); /* lifetime - twice RA retransmit */
put_opt6(&parm.link_local, IN6ADDRSZ);
}
/* set managed bits unless we're providing only RA on this link */
if (parm.managed)
ra->flags = 0xc0;
ra->flags |= 0x80; /* M flag, managed, */
if (parm.other)
ra->flags |= 0x40; /* O flag, other */
/* decide where we're sending */
memset(&addr, 0, sizeof(addr));
#ifdef HAVE_SOCKADDR_SA_LEN
@@ -238,7 +311,7 @@ static void send_ra(int iface, char *iface_name, struct in6_addr *dest)
addr.sin6_port = htons(IPPROTO_ICMPV6);
if (dest)
{
memcpy(&addr.sin6_addr, dest, sizeof(struct in6_addr));
addr.sin6_addr = *dest;
if (IN6_IS_ADDR_LINKLOCAL(dest) ||
IN6_IS_ADDR_MC_LINKLOCAL(dest))
addr.sin6_scope_id = iface;
@@ -254,9 +327,7 @@ static void send_ra(int iface, char *iface_name, struct in6_addr *dest)
static int add_prefixes(struct in6_addr *local, int prefix,
int scope, int if_index, int dad, void *vparam)
{
struct dhcp_context *context, *tmp;
struct ra_param *param = vparam;
struct prefix_opt *opt;
(void)scope; /* warning */
(void)dad;
@@ -269,64 +340,92 @@ static int add_prefixes(struct in6_addr *local, int prefix,
!IN6_IS_ADDR_LINKLOCAL(local) &&
!IN6_IS_ADDR_MULTICAST(local))
{
int do_prefix = 0;
int do_slaac = 0;
int deprecate = 0;
unsigned int time = 0xffffffff;
struct dhcp_context *context;
for (context = daemon->ra_contexts; context; context = context->next)
if (prefix == context->prefix &&
is_same_net6(local, &context->start6, prefix) &&
is_same_net6(local, &context->end6, prefix))
{
if (!(context->flags & CONTEXT_RA_ONLY))
if ((context->flags &
(CONTEXT_RA_ONLY | CONTEXT_RA_NAME | CONTEXT_RA_STATELESS)))
{
do_slaac = 1;
if (context->flags & CONTEXT_DHCP)
{
param->other = 1;
if (!(context->flags & CONTEXT_RA_STATELESS))
param->managed = 1;
}
}
else
{
/* don't do RA for non-ra-only unless --enable-ra is set */
if (!option_bool(OPT_RA))
continue;
param->managed = 1;
param->other = 1;
}
if (context->flags & CONTEXT_RA_DONE)
continue;
/* find floor time */
if (time > context->lease_time)
time = context->lease_time;
/* subsequent prefixes on the same interface don't need timers */
if (context->flags & CONTEXT_DEPRECATE)
deprecate = 1;
/* subsequent prefixes on the same interface
and subsequent instances of this prefix don't need timers */
if (!param->first)
context->ra_time = 0;
param->first = 0;
param->found_context = 1;
context->flags |= CONTEXT_RA_DONE;
/* mark this subnet and duplicates: as done. */
for (tmp = context->next; tmp; tmp = tmp->next)
if (tmp->prefix == prefix &&
is_same_net6(local, &tmp->start6, prefix) &&
is_same_net6(local, &tmp->end6, prefix))
{
tmp->flags |= CONTEXT_RA_DONE;
context->ra_time = 0;
}
if ((opt = expand(sizeof(struct prefix_opt))))
/* collect dhcp-range tags */
if (context->netid.next == &context->netid && context->netid.net)
{
u64 addrpart = addr6part(&context->start6);
u64 mask = (prefix == 64) ? (u64)-1LL : (1LLU << (128 - prefix)) - 1LLU;
unsigned int time = context->lease_time;
/* lifetimes must be min 2 hrs, by RFC 2462 */
if (time < 7200)
time = 7200;
opt->type = ICMP6_OPT_PREFIX;
opt->len = 4;
opt->prefix_len = prefix;
/* autonomous only is we're not doing dhcp */
opt->flags = (context->flags & CONTEXT_RA_ONLY) ? 0xc0 : 0x00;
opt->valid_lifetime = opt->preferred_lifetime = htonl(time);
opt->reserved = 0;
opt->prefix = context->start6;
setaddr6part(&opt->prefix, addrpart & ~mask);
inet_ntop(AF_INET6, &opt->prefix, daemon->addrbuff, ADDRSTRLEN);
my_syslog(MS_DHCP | LOG_INFO, "RTR-ADVERT(%s) %s", param->if_name, daemon->addrbuff);
context->netid.next = param->tags;
param->tags = &context->netid;
}
if (!(context->flags & CONTEXT_RA_DONE))
{
context->flags |= CONTEXT_RA_DONE;
do_prefix = 1;
}
}
if (do_prefix)
{
struct prefix_opt *opt;
if ((opt = expand(sizeof(struct prefix_opt))))
{
/* zero net part of address */
setaddr6part(local, addr6part(local) & ~((prefix == 64) ? (u64)-1LL : (1LLU << (128 - prefix)) - 1LLU));
/* lifetimes must be min 2 hrs, by RFC 2462 */
if (time < 7200)
time = 7200;
opt->type = ICMP6_OPT_PREFIX;
opt->len = 4;
opt->prefix_len = prefix;
/* autonomous only if we're not doing dhcp */
opt->flags = do_slaac ? 0x40 : 0x00;
opt->valid_lifetime = htonl(time);
opt->preferred_lifetime = htonl(deprecate ? 0 : time);
opt->reserved = 0;
opt->prefix = *local;
inet_ntop(AF_INET6, local, daemon->addrbuff, ADDRSTRLEN);
my_syslog(MS_DHCP | LOG_INFO, "RTR-ADVERT(%s) %s", param->if_name, daemon->addrbuff);
}
}
}
}
return 1;
@@ -368,11 +467,11 @@ time_t periodic_ra(time_t now)
for (next_event = 0, context = daemon->ra_contexts; context; context = context->next)
if (context->ra_time != 0)
{
if (difftime(context->ra_time, now) < 0.0)
if (difftime(context->ra_time, now) <= 0.0)
break; /* overdue */
if (next_event == 0 || difftime(next_event, context->ra_time + 2) > 0.0)
next_event = context->ra_time + 2;
if (next_event == 0 || difftime(next_event, context->ra_time) > 0.0)
next_event = context->ra_time;
}
/* none overdue */
@@ -406,7 +505,7 @@ static int iface_search(struct in6_addr *local, int prefix,
if (prefix == context->prefix &&
is_same_net6(local, &context->start6, prefix) &&
is_same_net6(local, &context->end6, prefix))
if (context->ra_time != 0 && difftime(context->ra_time, param->now) < 0.0)
if (context->ra_time != 0 && difftime(context->ra_time, param->now) <= 0.0)
{
/* found an interface that's overdue for RA determine new
timeout value and zap other contexts on the same interface
@@ -426,4 +525,5 @@ static int iface_search(struct in6_addr *local, int prefix,
return 1; /* keep searching */
}
#endif

View File

@@ -699,15 +699,17 @@ static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *
unsigned char *p2 = p1;
/* make counted string zero-term and sanitise */
for (i = 0; i < len; i++)
if (isprint(*(p2+1)))
{
*p2 = *(p2+1);
p2++;
}
{
if (!isprint((int)*(p2+1)))
break;
*p2 = *(p2+1);
p2++;
}
*p2 = 0;
my_syslog(LOG_INFO, "reply %s is %s", name, p1);
/* restore */
memmove(p1 + 1, p1, len);
memmove(p1 + 1, p1, i);
*p1 = len;
p1 += len+1;
}
@@ -936,10 +938,14 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
if (!cname_count--)
return 0; /* looped CNAMES */
newc = cache_insert(name, NULL, now, attl, F_CNAME | F_FORWARD);
if (newc && cpp)
if (newc)
{
cpp->addr.cname.cache = newc;
cpp->addr.cname.uid = newc->uid;
newc->addr.cname.cache = NULL;
if (cpp)
{
cpp->addr.cname.cache = newc;
cpp->addr.cname.uid = newc->uid;
}
}
cpp = newc;
@@ -1003,10 +1009,16 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
}
}
/* Don't put stuff from a truncated packet into the cache,
also don't cache replies where DNSSEC validation was turned off, either
the upstream server told us so, or the original query specified it. */
if (!(header->hb3 & HB3_TC) && !(header->hb4 & HB4_CD) && !checking_disabled)
/* Don't put stuff from a truncated packet into the cache.
Don't cache replies where DNSSEC validation was turned off, either
the upstream server told us so, or the original query specified it.
Don't cache replies from non-recursive nameservers, since we may get a
reply containing a CNAME but not its target, even though the target
does exist. */
if (!(header->hb3 & HB3_TC) &&
!(header->hb4 & HB4_CD) &&
(header->hb4 & HB4_RA) &&
!checking_disabled)
cache_end_insert();
return 0;
@@ -1633,6 +1645,23 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
} while ((crecp = cache_find_by_name(crecp, name, now, flag | F_CNAME)));
}
}
if (qtype == T_CNAME || qtype == T_ANY)
{
if ((crecp = cache_find_by_name(NULL, name, now, F_CNAME)) &&
(qtype == T_CNAME || (crecp->flags & (F_HOSTS | F_DHCP))))
{
ans = 1;
if (!dryrun)
{
log_query(crecp->flags, name, NULL, record_source(crecp->uid));
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
crec_ttl(crecp, now), &nameoffset,
T_CNAME, C_IN, "d", cache_get_name(crecp->addr.cname.cache)))
anscount++;
}
}
}
if (qtype == T_MX || qtype == T_ANY)
{

View File

@@ -483,14 +483,14 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
{
logaddr = &mess->yiaddr;
lease_set_hwaddr(lease, mess->chaddr, NULL, mess->hlen, mess->htype, 0);
lease_set_hwaddr(lease, mess->chaddr, NULL, mess->hlen, mess->htype, 0, now, 1);
if (hostname)
lease_set_hostname(lease, hostname, 1, get_domain(lease->addr), domain);
/* infinite lease unless nailed in dhcp-host line. */
lease_set_expires(lease,
have_config(config, CONFIG_TIME) ? config->lease_time : 0xffffffff,
now);
lease_set_interface(lease, int_index);
lease_set_interface(lease, int_index, now);
clear_packet(mess, end);
do_options(context, mess, end, NULL, hostname, get_domain(mess->yiaddr),
@@ -1222,51 +1222,56 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
log_tags(tagif_netid, ntohl(mess->xid));
#ifdef HAVE_SCRIPT
if (do_classes && daemon->lease_change_command)
if (do_classes)
{
struct dhcp_netid *n;
if (mess->giaddr.s_addr)
lease->giaddr = mess->giaddr;
/* pick up INIT-REBOOT events. */
lease->flags |= LEASE_CHANGED;
free(lease->extradata);
lease->extradata = NULL;
lease->extradata_size = lease->extradata_len = 0;
add_extradata_opt(lease, option_find(mess, sz, OPTION_VENDOR_ID, 1));
add_extradata_opt(lease, option_find(mess, sz, OPTION_HOSTNAME, 1));
add_extradata_opt(lease, oui);
add_extradata_opt(lease, serial);
add_extradata_opt(lease, class);
/* space-concat tag set */
if (!tagif_netid)
add_extradata_opt(lease, NULL);
else
for (n = tagif_netid; n; n = n->next)
{
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)))
#ifdef HAVE_SCRIPT
if (daemon->lease_change_command)
{
int len = option_len(opt);
unsigned char *ucp = option_ptr(opt, 0);
/* If the user-class option started as counted strings, the first byte will be zero. */
if (len != 0 && ucp[0] == 0)
ucp++, len--;
lease_add_extradata(lease, ucp, len, 0);
struct dhcp_netid *n;
if (mess->giaddr.s_addr)
lease->giaddr = mess->giaddr;
free(lease->extradata);
lease->extradata = NULL;
lease->extradata_size = lease->extradata_len = 0;
add_extradata_opt(lease, option_find(mess, sz, OPTION_VENDOR_ID, 1));
add_extradata_opt(lease, option_find(mess, sz, OPTION_HOSTNAME, 1));
add_extradata_opt(lease, oui);
add_extradata_opt(lease, serial);
add_extradata_opt(lease, class);
/* space-concat tag set */
if (!tagif_netid)
add_extradata_opt(lease, NULL);
else
for (n = tagif_netid; n; n = n->next)
{
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)))
{
int len = option_len(opt);
unsigned char *ucp = option_ptr(opt, 0);
/* If the user-class option started as counted strings, the first byte will be zero. */
if (len != 0 && ucp[0] == 0)
ucp++, len--;
lease_add_extradata(lease, ucp, len, 0);
}
}
}
#endif
}
if (!hostname_auth && (client_hostname = host_from_dns(mess->yiaddr)))
{
@@ -1276,7 +1281,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
}
time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
lease_set_hwaddr(lease, mess->chaddr, clid, mess->hlen, mess->htype, clid_len);
lease_set_hwaddr(lease, mess->chaddr, clid, mess->hlen, mess->htype, clid_len, now, do_classes);
/* if all the netids in the ignore_name list are present, ignore client-supplied name */
if (!hostname_auth)
@@ -1310,7 +1315,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
lease_set_hostname(lease, hostname, hostname_auth, get_domain(lease->addr), domain);
lease_set_expires(lease, time, now);
lease_set_interface(lease, int_index);
lease_set_interface(lease, int_index, now);
if (override.s_addr != 0)
lease->override = override;
@@ -1387,7 +1392,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
else
time = (unsigned int)difftime(lease->expires, now);
option_put(mess, end, OPTION_LEASE_TIME, 4, time);
lease_set_interface(lease, int_index);
lease_set_interface(lease, int_index, now);
}
do_options(context, mess, end, req_options, hostname, get_domain(mess->ciaddr),
@@ -2095,7 +2100,8 @@ static void do_options(struct dhcp_context *context,
struct dhcp_netid_list *id_list;
/* filter options based on tags, those we want get DHOPT_TAGOK bit set */
context->netid.next = NULL;
if (context)
context->netid.next = NULL;
tagif = option_filter(netid, context && context->netid.net ? &context->netid : NULL, config_opts);
/* logging */

View File

@@ -23,6 +23,7 @@ static int dhcp6_maybe_relay(struct in6_addr *link_address, struct dhcp_netid **
int interface, char *iface_name, struct in6_addr *fallback, void *inbuff, size_t sz, int is_unicast, time_t now);
static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dhcp_netid *tags, struct dhcp_context *context,
int interface, char *iface_name, struct in6_addr *fallback, void *inbuff, size_t sz, int is_unicast, time_t now);
static void log6_opts(int nest, unsigned int xid, void *start_opts, void *end_opts);
static void log6_packet(char *type, unsigned char *clid, int clid_len, struct in6_addr *addr, int xid, char *iface, char *string);
static void *opt6_find (void *opts, void *end, unsigned int search, unsigned int minsize);
@@ -34,12 +35,18 @@ static unsigned int opt6_uint(unsigned char *opt, int offset, int size);
#define opt6_ptr(opt, i) ((void *)&(((unsigned char *)(opt))[4+(i)]))
size_t dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name,
struct in6_addr *fallback, size_t sz, int is_unicast, time_t now)
unsigned short dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name,
struct in6_addr *fallback, size_t sz, int is_unicast, time_t now)
{
struct dhcp_netid *relay_tags = NULL;
struct dhcp_vendor *vendor;
int msg_type;
if (sz <= 4)
return 0;
msg_type = *((unsigned char *)daemon->dhcp_packet.iov_base);
/* Mark these so we only match each at most once, to avoid tangled linked lists */
for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
vendor->netid.next = &vendor->netid;
@@ -47,7 +54,7 @@ size_t dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name
save_counter(0);
if (dhcp6_maybe_relay(NULL, &relay_tags, context, interface, iface_name, fallback, daemon->dhcp_packet.iov_base, sz, is_unicast, now))
return save_counter(0);
return msg_type == DHCP6RELAYFORW ? DHCPV6_SERVER_PORT : DHCPV6_CLIENT_PORT;
return 0;
}
@@ -283,6 +290,9 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
}
}
if (option_bool(OPT_LOG_OPTS) && (opt = opt6_find(packet_options, end, OPTION6_VENDOR_CLASS, 4)))
my_syslog(MS_DHCP | LOG_INFO, _("%u vendor class: %u"), xid, opt6_uint(opt, 0, 4));
/* dhcp-match. If we have hex-and-wildcards, look for a left-anchored match.
Otherwise assume the option is an array, and look for a matching element.
If no data given, existance of the option is enough. This code handles
@@ -339,28 +349,28 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
if (len != 0 && len < 255)
{
unsigned char *pp, *op = opt6_ptr(opt, 1);
char *pq = daemon->dhcp_buff;
pp = op;
while (*op != 0 && ((op + (*op) + 1) - pp) < len)
{
memcpy(pq, op+1, *op);
pq += *op;
op += (*op)+1;
*(pq++) = '.';
}
if (pq != daemon->dhcp_buff)
pq--;
*pq = 0;
if (legal_hostname(daemon->dhcp_buff))
{
client_hostname = daemon->dhcp_buff;
if (option_bool(OPT_LOG_OPTS))
my_syslog(MS_DHCP | LOG_INFO, _("%u client provides name: %s"), xid, client_hostname);
}
unsigned char *pp, *op = opt6_ptr(opt, 1);
char *pq = daemon->dhcp_buff;
pp = op;
while (*op != 0 && ((op + (*op)) - pp) < len)
{
memcpy(pq, op+1, *op);
pq += *op;
op += (*op)+1;
*(pq++) = '.';
}
if (pq != daemon->dhcp_buff)
pq--;
*pq = 0;
if (legal_hostname(daemon->dhcp_buff))
{
client_hostname = daemon->dhcp_buff;
if (option_bool(OPT_LOG_OPTS))
my_syslog(MS_DHCP | LOG_INFO, _("%u client provides name: %s"), xid, client_hostname);
}
}
}
@@ -503,13 +513,13 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
while (1)
{
struct in6_addr alloced_addr, *addrp = NULL;
u32 preferred_time = 0;
u32 requested_time = 0;
struct dhcp_lease *lease = NULL;
if (ia_option)
{
struct in6_addr *req_addr = opt6_ptr(ia_option, 0);
preferred_time = opt6_uint(ia_option, 16, 4);
requested_time = opt6_uint(ia_option, 16, 4);
if (!address6_available(context, req_addr, tags) &&
(!have_config(config, CONFIG_ADDR6) || memcmp(&config->addr6, req_addr, IN6ADDRSZ) != 0))
@@ -611,10 +621,10 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
if (ia_option)
{
if (preferred_time < 120u )
preferred_time = 120u; /* sanity */
if (lease_time == 0xffffffff || (preferred_time != 0xffffffff && preferred_time < lease_time))
lease_time = preferred_time;
if (requested_time < 120u )
requested_time = 120u; /* sanity */
if (lease_time == 0xffffffff || (requested_time != 0xffffffff && requested_time < lease_time))
lease_time = requested_time;
}
if (lease_time < min_time)
@@ -627,8 +637,8 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
if (lease)
{
lease_set_expires(lease, lease_time, now);
lease_set_hwaddr(lease, NULL, clid, 0, iaid, clid_len);
lease_set_interface(lease, interface);
lease_set_hwaddr(lease, NULL, clid, 0, iaid, clid_len, now, 0);
lease_set_interface(lease, interface, now);
if (hostname && ia_type == OPTION6_IA_NA)
{
char *addr_domain = get_domain6(addrp);
@@ -720,8 +730,9 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
{
o1 = new_opt6(OPTION6_IAADDR);
put_opt6(addrp, sizeof(*addrp));
put_opt6_long(lease_time);
put_opt6_long(lease_time);
/* preferred lifetime */
put_opt6_long(this_context && (this_context->flags & CONTEXT_DEPRECATE) ? 0 : lease_time);
put_opt6_long(lease_time); /* valid lifetime */
end_opt6(o1);
log6_packet( make_lease ? "DHCPREPLY" : "DHCPADVERTISE",
@@ -737,6 +748,11 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
{
if (address_assigned)
{
o1 = new_opt6(OPTION6_STATUS_CODE);
put_opt6_short(DHCP6SUCCESS);
put_opt6_string("Oh hai from dnsmasq");
end_opt6(o1);
if (ia_type == OPTION6_IA_NA)
{
/* go back an fill in fields in IA_NA option */
@@ -750,7 +766,7 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
}
else
{
/* no address, return erro */
/* no address, return error */
o1 = new_opt6(OPTION6_STATUS_CODE);
put_opt6_short(DHCP6NOADDRS);
put_opt6_string("No addresses available");
@@ -758,6 +774,15 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
}
end_opt6(o);
if (address_assigned)
{
/* If --dhcp-authoritative is set, we can tell client not to wait for
other possible servers */
o = new_opt6(OPTION6_PREFERENCE);
put_opt6_char(option_bool(OPT_AUTHORITATIVE) ? 255 : 0);
end_opt6(o);
}
break;
}
@@ -818,7 +843,7 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
{
struct dhcp_lease *lease = NULL;
struct in6_addr *req_addr = opt6_ptr(ia_option, 0);
u32 preferred_time = opt6_uint(ia_option, 16, 4);
u32 requested_time = opt6_uint(ia_option, 16, 4);
unsigned int lease_time;
struct dhcp_context *this_context;
struct dhcp_config *valid_config = config;
@@ -849,7 +874,10 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
if (!address6_available(context, req_addr, tagif) ||
!(this_context = narrow_context6(context, req_addr, tagif)))
lease_time = 0;
{
lease_time = 0;
this_context = NULL;
}
else
{
/* get tags from context if we've not used it before */
@@ -871,10 +899,10 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
lease_time = have_config(valid_config, CONFIG_TIME) ? valid_config->lease_time : this_context->lease_time;
if (preferred_time < 120u )
preferred_time = 120u; /* sanity */
if (lease_time == 0xffffffff || (preferred_time != 0xffffffff && preferred_time < lease_time))
lease_time = preferred_time;
if (requested_time < 120u )
requested_time = 120u; /* sanity */
if (lease_time == 0xffffffff || (requested_time != 0xffffffff && requested_time < lease_time))
lease_time = requested_time;
lease_set_expires(lease, lease_time, now);
if (ia_type == OPTION6_IA_NA && hostname)
@@ -893,17 +921,23 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
o1 = new_opt6(OPTION6_IAADDR);
put_opt6(req_addr, sizeof(*req_addr));
put_opt6_long(lease_time);
put_opt6_long(lease_time);
/* preferred lifetime */
put_opt6_long(this_context && (this_context->flags & CONTEXT_DEPRECATE) ? 0 : lease_time);
put_opt6_long(lease_time); /* valid lifetime */
end_opt6(o1);
}
if (t1cntr != 0)
{
/* go back an fill in fields in IA_NA option */
unsigned int t1 = min_time == 0xffffffff ? 0xffffffff : min_time/2;
unsigned int t2 = min_time == 0xffffffff ? 0xffffffff : (min_time/8) * 7;
int sav = save_counter(t1cntr);
unsigned int t1, t2, fuzz = rand16();
while (fuzz > (min_time/16))
fuzz = fuzz/2;
t1 = min_time == 0xffffffff ? 0xffffffff : min_time/2 - fuzz;
t2 = min_time == 0xffffffff ? 0xffffffff : ((min_time/8)*7) - fuzz;
put_opt6_long(t1);
put_opt6_long(t2);
save_counter(sav);
@@ -1314,31 +1348,68 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
log_tags(tagif, xid);
if (option_bool(OPT_LOG_OPTS))
{
int end_opts = save_counter(-1);
/* must have created at least one option */
if (start_opts != end_opts)
for (opt = daemon->outpacket.iov_base + start_opts; opt; opt = opt6_next(opt, daemon->outpacket.iov_base + end_opts))
{
int offset = 0;
char *optname;
/* account for flag byte on FQDN */
if (opt6_type(opt) == OPTION6_FQDN)
offset = 1;
optname = option_string(AF_INET6, opt6_type(opt), opt6_ptr(opt, offset), opt6_len(opt) - offset, daemon->namebuff, MAXDNAME);
my_syslog(MS_DHCP | LOG_INFO, "%u sent size:%3d option:%3d %s %s",
xid, opt6_len(opt), opt6_type(opt), optname, daemon->namebuff);
}
}
log6_opts(0, xid, daemon->outpacket.iov_base + start_opts, daemon->outpacket.iov_base + save_counter(-1));
return 1;
}
static void log6_opts(int nest, unsigned int xid, void *start_opts, void *end_opts)
{
void *opt;
char *desc = nest ? "nest" : "sent";
if (start_opts == end_opts)
return;
for (opt = start_opts; opt; opt = opt6_next(opt, end_opts))
{
int type = opt6_type(opt);
void *ia_options = NULL;
char *optname;
if (type == OPTION6_IA_NA)
{
sprintf(daemon->namebuff, "IAID=%u T1=%u T2=%u",
opt6_uint(opt, 0, 4), opt6_uint(opt, 4, 4), opt6_uint(opt, 8, 4));
optname = "ia-na";
ia_options = opt6_ptr(opt, 12);
}
else if (type == OPTION6_IA_TA)
{
sprintf(daemon->namebuff, "IAID=%u", opt6_uint(opt, 0, 4));
optname = "ia-ta";
ia_options = opt6_ptr(opt, 4);
}
else if (type == OPTION6_IAADDR)
{
inet_ntop(AF_INET6, opt6_ptr(opt, 0), daemon->addrbuff, ADDRSTRLEN);
sprintf(daemon->namebuff, "%s PL=%u VL=%u",
daemon->addrbuff, opt6_uint(opt, 16, 4), opt6_uint(opt, 20, 4));
optname = "iaaddr";
ia_options = opt6_ptr(opt, 24);
}
else if (type == OPTION6_STATUS_CODE)
{
int len = sprintf(daemon->namebuff, "%u ", opt6_uint(opt, 0, 2));
memcpy(daemon->namebuff + len, opt6_ptr(opt, 2), opt6_len(opt)-2);
daemon->namebuff[len + opt6_len(opt) - 2] = 0;
optname = "status";
}
else
{
/* account for flag byte on FQDN */
int offset = type == OPTION6_FQDN ? 1 : 0;
optname = option_string(AF_INET6, type, opt6_ptr(opt, offset), opt6_len(opt) - offset, daemon->namebuff, MAXDNAME);
}
my_syslog(MS_DHCP | LOG_INFO, "%u %s size:%3d option:%3d %s %s",
xid, desc, opt6_len(opt), type, optname, daemon->namebuff);
if (ia_options)
log6_opts(1, xid, ia_options, opt6_ptr(opt, opt6_len(opt)));
}
}
static void log6_packet(char *type, unsigned char *clid, int clid_len, struct in6_addr *addr, int xid, char *iface, char *string)
{
/* avoid buffer overflow */

261
src/slaac.c Normal file
View File

@@ -0,0 +1,261 @@
/* 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
#include <netinet/icmp6.h>
static int map_rebuild = 0;
static int ping_id = 0;
void slaac_add_addrs(struct dhcp_lease *lease, time_t now, int force)
{
struct slaac_address *slaac, *old, **up;
struct dhcp_context *context;
int dns_dirty = 0;
if (!(lease->flags & LEASE_HAVE_HWADDR) ||
(lease->flags & (LEASE_TA | LEASE_NA)) ||
lease->last_interface == 0 ||
!lease->hostname)
return ;
old = lease->slaac_address;
lease->slaac_address = NULL;
for (context = daemon->ra_contexts; context; context = context->next)
if ((context->flags & CONTEXT_RA_NAME) && lease->last_interface == context->if_index)
{
struct in6_addr addr = context->start6;
if (lease->hwaddr_len == 6 &&
(lease->hwaddr_type == ARPHRD_ETHER || lease->hwaddr_type == ARPHRD_IEEE802))
{
/* convert MAC address to EUI-64 */
memcpy(&addr.s6_addr[8], lease->hwaddr, 3);
memcpy(&addr.s6_addr[13], &lease->hwaddr[3], 3);
addr.s6_addr[11] = 0xff;
addr.s6_addr[12] = 0xfe;
}
#if defined(ARPHRD_EUI64)
else if (lease->hwaddr_len == 8 &&
lease->hwaddr_type == ARPHRD_EUI64)
memcpy(&addr.s6_addr[8], lease->hwaddr, 8);
#endif
#if defined(ARPHRD_IEEE1394) && defined(ARPHRD_EUI64)
else if (lease->clid_len == 9 &&
lease->clid[0] == ARPHRD_EUI64 &&
lease->hwaddr_type == ARPHRD_IEEE1394)
/* firewire has EUI-64 identifier as clid */
memcpy(&addr.s6_addr[8], &lease->clid[1], 8);
#endif
else
continue;
addr.s6_addr[8] ^= 0x02;
/* check if we already have this one */
for (up = &old, slaac = old; slaac; slaac = slaac->next)
{
if (IN6_ARE_ADDR_EQUAL(&addr, &slaac->addr))
{
*up = slaac->next;
/* recheck when DHCPv4 goes through init-reboot */
if (force)
{
slaac->ping_time = now;
slaac->backoff = 1;
dns_dirty = 1;
}
break;
}
up = &slaac->next;
}
/* No, make new one */
if (!slaac && (slaac = whine_malloc(sizeof(struct slaac_address))))
{
slaac->ping_time = now;
slaac->backoff = 1;
slaac->addr = addr;
slaac->local = context->local6;
/* Do RA's to prod it */
ra_start_unsolicted(now, context);
}
if (slaac)
{
slaac->next = lease->slaac_address;
lease->slaac_address = slaac;
}
}
if (old || dns_dirty)
lease_update_dns(1);
/* Free any no reused */
for (; old; old = slaac)
{
slaac = old->next;
free(old);
}
}
time_t periodic_slaac(time_t now, struct dhcp_lease *leases)
{
struct dhcp_context *context;
struct dhcp_lease *lease;
struct slaac_address *slaac;
time_t next_event = 0;
for (context = daemon->ra_contexts; context; context = context->next)
if ((context->flags & CONTEXT_RA_NAME))
break;
/* nothing configured */
if (!context)
return 0;
while (ping_id == 0)
ping_id = rand16();
if (map_rebuild)
{
map_rebuild = 0;
build_subnet_map();
}
for (lease = leases; lease; lease = lease->next)
for (slaac = lease->slaac_address; slaac; slaac = slaac->next)
{
/* confirmed or given up? */
if (slaac->backoff == 0 || slaac->ping_time == 0)
continue;
if (difftime(slaac->ping_time, now) <= 0.0)
{
struct ping_packet *ping;
struct sockaddr_in6 addr;
save_counter(0);
ping = expand(sizeof(struct ping_packet));
ping->type = ICMP6_ECHO_REQUEST;
ping->code = 0;
ping->identifier = ping_id;
ping->sequence_no = slaac->backoff;
memset(&addr, 0, sizeof(addr));
#ifdef HAVE_SOCKADDR_SA_LEN
addr.sin6_len = sizeof(struct sockaddr_in6);
#endif
addr.sin6_family = AF_INET6;
addr.sin6_port = htons(IPPROTO_ICMPV6);
addr.sin6_addr = slaac->addr;
if (sendto(daemon->icmp6fd, daemon->outpacket.iov_base, save_counter(0), 0,
(struct sockaddr *)&addr, sizeof(addr)) == -1 &&
errno == EHOSTUNREACH)
slaac->ping_time = 0; /* Give up */
else
{
slaac->ping_time += (1 << (slaac->backoff - 1)) + (rand16()/21785); /* 0 - 3 */
if (slaac->backoff > 4)
slaac->ping_time += rand16()/4000; /* 0 - 15 */
if (slaac->backoff < 12)
slaac->backoff++;
}
}
if (slaac->ping_time != 0 &&
(next_event == 0 || difftime(next_event, slaac->ping_time) >= 0.0))
next_event = slaac->ping_time;
}
return next_event;
}
void slaac_ping_reply(struct in6_addr *sender, unsigned char *packet, char *interface, struct dhcp_lease *leases)
{
struct dhcp_lease *lease;
struct slaac_address *slaac;
struct ping_packet *ping = (struct ping_packet *)packet;
int gotone = 0;
if (ping->identifier == ping_id)
for (lease = leases; lease; lease = lease->next)
for (slaac = lease->slaac_address; slaac; slaac = slaac->next)
if (slaac->backoff != 0 && IN6_ARE_ADDR_EQUAL(sender, &slaac->addr))
{
slaac->backoff = 0;
gotone = 1;
inet_ntop(AF_INET6, sender, daemon->addrbuff, ADDRSTRLEN);
my_syslog(MS_DHCP | LOG_INFO, "SLAAC-CONFIRM(%s) %s %s", interface, daemon->addrbuff, lease->hostname);
}
lease_update_dns(gotone);
}
/* Build a map from ra-names subnets to corresponding interfaces. This
is used to go from DHCPv4 leases to SLAAC addresses,
interface->IPv6-subnet, IPv6-subnet + MAC address -> SLAAC.
*/
static int add_subnet(struct in6_addr *local, int prefix,
int scope, int if_index, int dad, void *vparam)
{
struct dhcp_context *context;
(void)scope;
(void)dad;
(void)vparam;
for (context = daemon->ra_contexts; context; context = context->next)
if ((context->flags & CONTEXT_RA_NAME) &&
prefix == context->prefix &&
is_same_net6(local, &context->start6, prefix) &&
is_same_net6(local, &context->end6, prefix))
{
context->if_index = if_index;
context->local6 = *local;
}
return 1;
}
void build_subnet_map(void)
{
struct dhcp_context *context;
int ok = 0;
for (context = daemon->ra_contexts; context; context = context->next)
{
context->if_index = 0;
if ((context->flags & CONTEXT_RA_NAME))
ok = 1;
}
/* ra-names configured */
if (ok)
iface_enumerate(AF_INET6, NULL, add_subnet);
}
void schedule_subnet_map(void)
{
map_rebuild = 1;
}
#endif

View File

@@ -24,6 +24,7 @@ static ssize_t tftp_err(int err, char *packet, char *mess, char *file);
static ssize_t tftp_err_oops(char *packet, char *file);
static ssize_t get_block(char *packet, struct tftp_transfer *transfer);
static char *next(char **p, char *end);
static void sanitise(char *buf);
#define OP_RRQ 1
#define OP_WRQ 2
@@ -57,7 +58,7 @@ void tftp_request(struct listener *listen, time_t now)
int mtuflag = IP_PMTUDISC_DONT;
#endif
char namebuff[IF_NAMESIZE];
char *name;
char *name = NULL;
char *prefix = daemon->tftp_prefix;
struct tftp_prefix *pref;
struct interface_list *ir;
@@ -95,9 +96,20 @@ void tftp_request(struct listener *listen, time_t now)
if (option_bool(OPT_NOWILD))
{
addr = listen->iface->addr;
mtu = listen->iface->mtu;
name = listen->iface->name;
if (listen->iface)
{
addr = listen->iface->addr;
mtu = listen->iface->mtu;
name = listen->iface->name;
}
else
{
/* we're listening on an address that doesn't appear on an interface,
ask the kernel what the socket is bound to */
socklen_t tcp_len = sizeof(union mysockaddr);
if (getsockname(listen->tftpfd, (struct sockaddr *)&addr, &tcp_len) == -1)
return;
}
}
else
{
@@ -211,15 +223,18 @@ void tftp_request(struct listener *listen, time_t now)
mtu = ifr.ifr_mtu;
}
/* check for per-interface prefix */
for (pref = daemon->if_prefix; pref; pref = pref->next)
if (strcmp(pref->interface, name) == 0)
prefix = pref->prefix;
/* wierd TFTP interfaces disable special options. */
for (ir = daemon->tftp_interfaces; ir; ir = ir->next)
if (strcmp(ir->interface, name) == 0)
special = 1;
if (name)
{
/* check for per-interface prefix */
for (pref = daemon->if_prefix; pref; pref = pref->next)
if (strcmp(pref->interface, name) == 0)
prefix = pref->prefix;
/* wierd TFTP interfaces disable special options. */
for (ir = daemon->tftp_interfaces; ir; ir = ir->next)
if (strcmp(ir->interface, name) == 0)
special = 1;
}
if (listen->family == AF_INET)
{
@@ -297,7 +312,10 @@ void tftp_request(struct listener *listen, time_t now)
!(filename = next(&p, end)) ||
!(mode = next(&p, end)) ||
(strcasecmp(mode, "octet") != 0 && strcasecmp(mode, "netascii") != 0))
len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"), daemon->addrbuff);
{
len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"), daemon->addrbuff);
is_err = 1;
}
else
{
if (strcasecmp(mode, "netascii") == 0)
@@ -330,9 +348,12 @@ void tftp_request(struct listener *listen, time_t now)
}
/* cope with backslashes from windows boxen. */
while ((p = strchr(filename, '\\')))
*p = '/';
for (p = filename; *p; p++)
if (*p == '\\')
*p = '/';
else if (option_bool(OPT_TFTP_LC))
*p = tolower(*p);
strcpy(daemon->namebuff, "/");
if (prefix)
{
@@ -379,7 +400,7 @@ void tftp_request(struct listener *listen, time_t now)
}
while (sendto(transfer->sockfd, packet, len, 0,
(struct sockaddr *)&peer, sizeof(peer)) == -1 && errno == EINTR);
(struct sockaddr *)&peer, sa_len(&peer)) == -1 && errno == EINTR);
if (is_err)
free_transfer(transfer);
@@ -487,12 +508,12 @@ void check_tftp_listeners(fd_set *rset, time_t now)
{
tmp = transfer->next;
prettyprint_addr(&transfer->peer, daemon->addrbuff);
if (FD_ISSET(transfer->sockfd, rset))
{
/* we overwrote the buffer... */
daemon->srv_save = NULL;
prettyprint_addr(&transfer->peer, daemon->addrbuff);
if ((len = recv(transfer->sockfd, daemon->packet, daemon->packet_buff_sz, 0)) >= (ssize_t)sizeof(struct ack))
{
@@ -514,14 +535,8 @@ void check_tftp_listeners(fd_set *rset, time_t now)
if (!err)
err = "";
else
{
unsigned char *q, *r;
for (q = r = (unsigned char *)err; *r; r++)
if (isprint(*r))
*(q++) = *r;
*q = 0;
}
sanitise(err);
my_syslog(MS_TFTP | LOG_ERR, _("error %d %s received from %s"),
(int)ntohs(mess->block), err,
daemon->addrbuff);
@@ -548,30 +563,33 @@ void check_tftp_listeners(fd_set *rset, time_t now)
len = tftp_err_oops(daemon->packet, transfer->file->filename);
endcon = 1;
}
else if (++transfer->backoff > 5)
/* don't complain about timeout when we're awaiting the last
ACK, some clients never send it */
else if (++transfer->backoff > 5 && len != 0)
{
/* don't complain about timeout when we're awaiting the last
ACK, some clients never send it */
if (len != 0)
{
my_syslog(MS_TFTP | LOG_ERR, _("failed sending %s to %s"),
transfer->file->filename, daemon->addrbuff);
len = 0;
endcon = 1;
}
endcon = 1;
len = 0;
}
if (len != 0)
while(sendto(transfer->sockfd, daemon->packet, len, 0,
(struct sockaddr *)&transfer->peer, sizeof(transfer->peer)) == -1 && errno == EINTR);
(struct sockaddr *)&transfer->peer, sa_len(&transfer->peer)) == -1 && errno == EINTR);
if (endcon || len == 0)
{
if (!endcon)
my_syslog(MS_TFTP | LOG_INFO, _("sent %s to %s"), transfer->file->filename, daemon->addrbuff);
strcpy(daemon->namebuff, transfer->file->filename);
sanitise(daemon->namebuff);
my_syslog(MS_TFTP | LOG_INFO, endcon ? _("failed sending %s to %s") : _("sent %s to %s"), daemon->namebuff, daemon->addrbuff);
/* unlink */
*up = tmp;
free_transfer(transfer);
if (endcon)
free_transfer(transfer);
else
{
/* put on queue to be sent to script and deleted */
transfer->next = daemon->tftp_done_trans;
daemon->tftp_done_trans = transfer;
}
continue;
}
}
@@ -605,6 +623,16 @@ static char *next(char **p, char *end)
return ret;
}
static void sanitise(char *buf)
{
unsigned char *q, *r;
for (q = r = (unsigned char *)buf; *r; r++)
if (isprint((int)*r))
*(q++) = *r;
*q = 0;
}
static ssize_t tftp_err(int err, char *packet, char *message, char *file)
{
struct errmess {
@@ -613,7 +641,9 @@ static ssize_t tftp_err(int err, char *packet, char *message, char *file)
} *mess = (struct errmess *)packet;
ssize_t ret = 4;
char *errstr = strerror(errno);
sanitise(file);
mess->op = htons(OP_ERR);
mess->err = htons(err);
ret += (snprintf(mess->message, 500, message, file, errstr) + 1);
@@ -624,7 +654,9 @@ static ssize_t tftp_err(int err, char *packet, char *message, char *file)
static ssize_t tftp_err_oops(char *packet, char *file)
{
return tftp_err(ERR_NOTDEF, packet, _("cannot read %s: %s"), file);
/* May have >1 refs to file, so potentially mangle a copy of the name */
strcpy(daemon->namebuff, file);
return tftp_err(ERR_NOTDEF, packet, _("cannot read %s: %s"), daemon->namebuff);
}
/* return -1 for error, zero for done. */
@@ -709,4 +741,21 @@ static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
}
}
int do_tftp_script_run(void)
{
struct tftp_transfer *transfer;
if ((transfer = daemon->tftp_done_trans))
{
daemon->tftp_done_trans = transfer->next;
#ifdef HAVE_SCRIPT
queue_tftp(transfer->file->size, transfer->file->filename, &transfer->peer);
#endif
free_transfer(transfer);
return 1;
}
return 0;
}
#endif