Compare commits

...
40 Commits
Author SHA1 Message Date
Simon Kelley ff7eea27e7 Add --dhcp-relay config option. 2013-09-04 18:01:38 +01:00
Simon Kelley 6692a1a53f Fix dhcp-range sanity checking. 2013-08-20 14:41:31 +01:00
Simon Kelley a37cd7aaf5 Use tags from pxe-proxy dhcp-range statements. 2013-08-20 10:33:32 +01:00
Vladislav Grishenko e4cdbbf521 Allow prefix :: in constructed dhcp-range. 2013-08-19 16:20:31 +01:00
Vladislav Grishenko 4568a6f842 IFACE_PERMANENT interface flag in enumeration. 2013-08-19 16:07:07 +01:00
Simon Kelley 5c72bb9e33 Silence compiler warning. 2013-08-19 14:12:59 +01:00
Vladislav Grishenko 8c3bdb4ffc Add code to get IPv6 address lifetimes and flags for *BSD. 2013-08-19 14:04:38 +01:00
Simon Kelley ffbad34b31 Set SOREUSEADDR as well as SOREUSEPORT on DHCP sockets when both available. 2013-08-14 15:53:57 +01:00
Simon Kelley f086d39641 Debian package change: update resolvconf script. 2013-08-14 14:54:23 +01:00
Simon Kelley cc4baaab0d Fix DHCPv6 lease time calculation when client sends VL==0 or PL==0 2013-08-05 15:03:44 +01:00
Simon Kelley 66409193dc Sanity check for dhcp-range template. 2013-08-01 20:19:32 +01:00
Simon Kelley 2937f8a040 Provide independent control over which interfaces get TFTP. 2013-07-29 19:49:07 +01:00
Simon Kelley edf0bde0c6 Make --listen-address higher priority than --except-interface. 2013-07-29 17:21:48 +01:00
Simon Kelley 8d03046269 Add --force-fast-ra option. 2013-07-29 15:41:26 +01:00
Simon Kelley 9f48ffa1e8 Apply ceiling of configured dhcp-range leasetime to deprecated prefix adverts. 2013-07-28 15:47:04 +01:00
Simon Kelley 871d4562f1 Support RFC-4242 information-refresh-time. 2013-07-28 09:37:38 +01:00
Simon Kelley 0f371f9e1a Remove dead code. 2013-07-27 15:15:38 +01:00
Simon Kelley 6bd109aa2f Better job with domain for DHCPv6 information-request. 2013-07-27 15:11:44 +01:00
Simon Kelley f7a40ec650 Tweak when old prefix returns. 2013-07-27 13:36:08 +01:00
Simon Kelley ff1b41dc57 Merge branch 'master' of ssh://thekelleys.org.uk/var/cache/git/dnsmasq
Conflicts:
	CHANGELOG
2013-07-27 12:32:29 +01:00
Simon Kelley fc4c4fda05 Fix MAC address enumeration on *BSD. 2013-07-26 15:38:59 +01:00
Simon Kelley ef1a94abaa Advertise lost prefixes with pref_time == 0 for 2 hours. 2013-07-26 13:59:03 +01:00
Simon Kelley d9fb0be8c7 Make --clear-on-reload apply to DBus API too. 2013-07-25 21:47:17 +01:00
Roy Marples 3f3adae6bc DHCP FQDN option tweaks. 2013-07-25 16:22:46 +01:00
Simon Kelley 1ecbaaa382 Tweak parameter lifetimes in Router advertisements. 2013-07-25 14:19:27 +01:00
Kyle Mestery d859ca2f9b Allow hostnames to start with a number. 2013-07-24 13:17:54 +01:00
Simon Kelley 3953dcc7f2 Typo in name in CHANGELOG. 2013-07-03 20:40:45 +01:00
Simon Kelley 625ac28c61 Fix crash with empty DHCP string options. 2013-07-02 21:19:32 +01:00
Simon Kelley b4b9308079 Increase timeout/number of retries in TFTP. 2013-06-19 10:31:23 +01:00
Simon Kelley e2ba0df2d4 Don't BIND DHCP socket if more interfaces may come along later. 2013-05-31 17:04:25 +01:00
Simon Kelley 921360ce62 Add constructor-noauth: keyword 2013-05-31 14:07:22 +01:00
Simon Kelley 429805dbbc Allow constructed ranges from interface address at end of range.
Also make man page on this clearer, as it's been confusing many.
2013-05-31 13:47:26 +01:00
Marcelo Salhab Brogliato 0da5e8979b Log forwarding table overflows. 2013-05-31 11:49:06 +01:00
Simon Kelley baa80ae512 Remove limit in prefix length in --auth-zone. 2013-05-29 16:32:07 +01:00
Simon Kelley 3e8ed78bf1 Fix option parsing for --dhcp-host. 2013-05-29 14:31:33 +01:00
Simon Kelley 48493329d6 Update Debian resolvconf script for dnscrypt-proxy integration. 2013-05-28 14:49:54 +01:00
Simon Kelley 76dd75de77 Fix hang from new interface-name code, when using TCP. 2013-05-23 10:04:25 +01:00
Simon Kelley 63fd27e35f Debian changelog update. 2013-05-20 11:50:33 +01:00
Simon Kelley 115ac3e4d7 Generalise --interface-name to cope with IPv6 addresses. 2013-05-20 11:28:32 +01:00
Simon Kelley cfcad42ff1 Fix failure to start with ENOTSOCK 2013-05-17 11:32:03 +01:00
26 changed files with 1601 additions and 601 deletions

No files matched your search

+72
View File
@@ -31,6 +31,78 @@ version 2.67
want to continue to bind the aliases too, you need to add
eg. --interface=eth0:0 to the config.
Fix "failed to set SO_BINDTODEVICE on DHCP socket: Socket
operation on non-socket" error on startup with
configurations which have exactly one --interface option
and do RA but _not_ DHCPv6. Thanks to Trever Adams for the
bug report.
Generalise --interface-name to cope with IPv6 addresses
and multiple addresses per interface per address family.
Fix option parsing for --dhcp-host, which was generating a
spurious error when all seven possible items were
included. Thanks to Zhiqiang Wang for the bug report.
Remove restriction on prefix-length in --auth-zone. Thanks
to Toke Hoiland-Jorgensen for suggesting this.
Log when the maximum number of concurrent DNS queries is
reached. Thanks to Marcelo Salhab Brogliato for the patch.
If wildcards are used in --interface, don't assume that
there will only ever be one available interface for DHCP
just because there is one at start-up. More may appear, so
we can't use SO_BINDTODEVICE. Thanks to Natrio for the bug
report.
Increase timeout/number of retries in TFTP to accomodate
AudioCodes Voice Gateways doing streaming writes to flash.
Thanks to Damian Kaczkowski for spotting the problem.
Fix crash with empty DHCP string options when adding zero
terminator. Thanks to Patrick McLean for the bug report.
Allow hostnames to start with a number, as allowed in
RFC-1123. Thanks to Kyle Mestery for the patch.
Fixes to DHCP FQDN option handling: don't terminate FQDN
if domain not known and allow a FQDN option with blank
name to request that a FQDN option is returned in the
reply. Thanks to Roy Marples for the patch.
Make --clear-on-reload apply to setting upstream servers
via DBus too.
When the address which triggered the construction of an
advertised IPv6 prefix disappears, continue to advertise
the prefix for up to 2 hours, with the preferred lifetime
set to zero. This satisfies RFC 6204 4.3 L-13 and makes
things work better if a prefix disappears without being
deprecated first. Thanks to Uwe Schindler for persuasively
arguing for this.
Fix MAC address enumeration on *BSD. Thanks to Brad Smith
for the bug report.
Support RFC-4242 information-refresh-time options in the
reply to DHCPv6 information-request. The lease time of the
smallest valid dhcp-range is sent. Thanks to Uwe Schindler
for suggesting this.
Add --force-fast-ra option. Another thanks to Uwe Schindler.
Make --listen-address higher priority than --except-interface
in all circumstances. Thanks to Thomas Hood for the bugreport.
Provide independent control over which interfaces get TFTP
service. If enable-tftp is given a list of interfaces, then TFTP
is provided on those. Without the list, the previous behaviour
(provide TFTP to the same interfaces we provide DHCP to)
is retained. Thanks to Lonnie Abelbeck for the suggestion.
Add --dhcp-relay config option.
version 2.66
Add the ability to act as an authoritative DNS
+19 -1
View File
@@ -2,7 +2,25 @@ dnsmasq (2.67-1) unstable; urgency=low
* New upstream.
-- Simon Kelley <simon@thekelleys.org.uk> Fri, 19 Apr 2013 10:23:31 +0000
-- Simon Kelley <simon@thekelleys.org.uk> Wed, 4 Aug 2013 14:53:22 +0000
dnsmasq (2.66-4) unstable; urgency=low
* Update resolvconf script. (closes: #716908)
-- Simon Kelley <simon@thekelleys.org.uk> Wed, 4 Aug 2013 14:48:21 +0000
dnsmasq (2.66-3) unstable; urgency=low
* Update resolvconf script for dnscrypt-proxy integration. (closes: #709179)
-- Simon Kelley <simon@thekelleys.org.uk> Tue, 28 May 2013 14:39:51 +0000
dnsmasq (2.66-2) unstable; urgency=low
* Fix error on startup with some configs. (closes: #709010)
-- Simon Kelley <simon@thekelleys.org.uk> Mon, 20 May 2013 11:46:11 +0000
dnsmasq (2.66-1) unstable; urgency=low
+27 -12
View File
@@ -1,16 +1,14 @@
#!/bin/bash
#!/bin/sh
#
# Script to update the resolver list for dnsmasq
#
# N.B. Resolvconf may run us even if dnsmasq is not running.
# If dnsmasq is installed then we go ahead and update
# the resolver list in case dnsmasq is started later.
# N.B. Resolvconf may run us even if dnsmasq is not (yet) running.
# If dnsmasq is installed then we go ahead and update the resolver list
# in case dnsmasq is started later.
#
# Assumption: On entry, PWD contains the resolv.conf-type files
# Assumption: On entry, PWD contains the resolv.conf-type files.
#
# Requires bash because it uses a non-POSIX printf extension.
#
# Licensed under the GNU GPL. See /usr/share/common-licenses/GPL.
# This file is part of the dnsmasq package.
#
set -e
@@ -18,6 +16,8 @@ set -e
RUN_DIR="/var/run/dnsmasq"
RSLVRLIST_FILE="${RUN_DIR}/resolv.conf"
TMP_FILE="${RSLVRLIST_FILE}_new.$$"
MY_RECORD_NAME="lo.dnsmasq"
DNSCRYPT_RECORD_NAME="lo.dnscrypt"
[ -x /usr/sbin/dnsmasq ] || exit 0
[ -x /lib/resolvconf/list-records ] || exit 1
@@ -27,7 +27,7 @@ PATH=/bin:/sbin
report_err() { echo "$0: Error: $*" >&2 ; }
# Stores arguments (minus duplicates) in RSLT, separated by spaces
# Doesn't work properly if an argument itself contain whitespace
# Doesn't work properly if an argument itself contains whitespace
uniquify()
{
RSLT=""
@@ -45,7 +45,22 @@ if [ ! -d "$RUN_DIR" ] && ! mkdir --parents --mode=0755 "$RUN_DIR" ; then
exit 1
fi
RSLVCNFFILES="$(/lib/resolvconf/list-records | sed -e '/^lo.dnsmasq$/d')"
RSLVCNFFILES=""
for F in $(/lib/resolvconf/list-records --after "$MY_RECORD_NAME") ; do
case "$F" in
"$MY_RECORD_NAME")
# Omit
;;
"$DNSCRYPT_RECORD_NAME")
# Dnscrypt, I only have eyes for you
RSLVCNFFILES="$DNSCRYPT_RECORD_NAME"
break
;;
*)
RSLVCNFFILES="${RSLVCNFFILES:+$RSLVCNFFILES }$F"
;;
esac
done
NMSRVRS=""
if [ "$RSLVCNFFILES" ] ; then
@@ -56,8 +71,8 @@ fi
# Dnsmasq uses the mtime of $RSLVRLIST_FILE, with a resolution of one second,
# to detect changes in the file. This means that if a resolvconf update occurs
# within one second of the previous one then dnsmasq may fail to notice the
# more recent change. To work around this problem we sleep here to ensure
# that the new mtime is different.
# more recent change. To work around this problem we sleep one second here
# if necessary in order to ensure that the new mtime is different.
if [ -f "$RSLVRLIST_FILE" ] && [ "$(ls -go --time-style='+%s' "$RSLVRLIST_FILE" | { read p h s t n ; echo "$t" ; })" = "$(date +%s)" ] ; then
sleep 1
fi
+77 -9
View File
@@ -338,7 +338,8 @@ by '/', like the --server syntax, eg.
Don't poll /etc/resolv.conf for changes.
.TP
.B --clear-on-reload
Whenever /etc/resolv.conf is re-read, clear the DNS cache.
Whenever /etc/resolv.conf is re-read or the upstream servers are set
via DBus, clear the DNS cache.
This is useful when new nameservers may have different
data than that held in cache.
.TP
@@ -572,13 +573,18 @@ If you use the first DNSSEC mode, validating resolvers in clients,
this option is not required. Dnsmasq always returns all the data
needed for a client to do validation itself.
.TP
.B --auth-zone=<domain>[,<subnet>[,<subnet>.....]]
.B --auth-zone=<domain>[,<subnet>[/<prefix length>][,<subnet>[/<prefix length>].....]]
Define a DNS zone for which dnsmasq acts as authoritative server. Locally defined DNS records which are in the domain
will be served, except that A and AAAA records must be in one of the
will be served. A and AAAA records must be in one of the
specified subnets, or in a subnet corresponding to a constructed DHCP
range. The subnet(s) are also used to define in-addr.arpa and
ipv6.arpa domains which are served for reverse-DNS queries. For IPv4
subnets, the prefix length is limited to the values 8, 16 or 24.
range. (This can be overridden with
.B constructor-noauth:
) The subnet(s) are also used to define in-addr.arpa and
ipv6.arpa domains which are served for reverse-DNS queries. If not
specified, the prefix length defaults to 24 for IPv4 and 64 for IPv6.
For IPv4 subnets, the prefix length should be have the value 8, 16 or 24
unless you are familiar with RFC 2317 and have arranged the
in-addr.arpa delegation accordingly.
.TP
.B --auth-soa=<serial>[,<hostmaster>[,<refresh>[,<retry>[,<expiry>]]]]
Specify fields in the SOA record associated with authoritative
@@ -646,7 +652,30 @@ This forms a template which describes how to create ranges, based on the address
.B --dhcp-range=::1,::400,constructor:eth0
will look for addresses of the form <network>::1 on eth0 and then create a range from <network>::1 to <network>::400. If the interface is assigned more than one network, then the corresponding ranges will be automatically created, and then deprecated and finally removed again as the address is deprecated and then deleted. The interface name may have a final "*" wildcard.
will look for addresses of the form <network>::1 or <network>:400 on
eth0 and then create a range from <network>::1 to <network>::400. If
the interface is assigned more than one network, then the
corresponding ranges will be automatically created, and then
deprecated and finally removed again as the address is deprecated and
then deleted. The interface name may have a final "*" wildcard. Note
that just any address on eth0 will not do: the non-prefix part must be
equal either the start or end address given in the dhcp-range. This is
to prevent prefixes becoming perpetual if the interface
gains a SLAAC address for the prefix when it is advertised by dnsmasq.
If a dhcp-range is only being used for stateless DHCP and/or SLAAC,
then the address can be simply ::
.B --dhcp-range=::,constructor:eth0
This removes the condition above, and will pick up the prefix from any address on eth0 which is NOT
autoconfigured, slaac, temporary or deprecated.
There is a variant of the constructor: syntax using the keyword
.B constructor-noauth.
See
.B --auth-zone
for an explanation of this.
The optional
.B set:<tag>
@@ -944,6 +973,38 @@ DHCP options. This make extra space available in the DHCP packet for
options but can, rarely, confuse old or broken clients. This flag
forces "simple and safe" behaviour to avoid problems in such a case.
.TP
.B --dhcp-relay=<local address>,<server address>[,<interface]
Configure dnsmasq to do DHCP relay. The local address is an address
allocated to an interface on the host running dnsmasq. All DHCP
requests arriving on that interface will we relayed to a remote DHCP
server at the server address. It is possible to relay from a single local
address to multiple remote servers by using multiple dhcp-relay
configs with the same local address and different server
addresses. A server address must be an IP literal address, not a
domain name. In the case of DHCPv6, the server address may be the
ALL_SERVERS multicast address, ff05::1:3. In this case the interface
must be given, not be wildcard, and is used to direct the multicast to the
correct interface to reach the DHCP server.
Access control for DHCP clients has the same rules as for the DHCP
server, see --interface, --except-interface, etc. The optional
interface name in the dhcp-relay config has a different function: it
controls on which interface DHCP replies from the server will be
accepted. This is intended for configurations which have three
interfaces: one being relayed from, a second connecting the DHCP
server, and a third untrusted network, typically the wider
internet. It avoids the possibility of spoof replies arriving via this
third interface.
It is allowed to have dnsmasq act as a DHCP server on one set of
interfaces and relay from a disjoint set of interfaces. Note that
whilst it is quite possible to write configurations which appear to
act as a server and a relay on the same interface, this is not
supported: the relay function will take precedence.
Both DHCPv4 and DHCPv6 relay is supported. It's not possible to relay
DHCPv4 to a DHCPv6 server or vice-versa.
.TP
.B \-U, --dhcp-vendorclass=set:<tag>,[enterprise:<IANA-enterprise number>,]<vendor-class>
Map from a vendor-class string to a tag. Most DHCP clients provide a
"vendor class" which represents, in some sense, the type of host. This option
@@ -1466,11 +1527,18 @@ 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
.B --force-fast-ra
Normally, dnsmasq advertises a new IPv6 prefix frequently (every 10 seconds or so) for the first minute, and then
drops back to sending "maintenance" advertisements every 10 minutes or so. This option forces dnsmasq to be always in
frequent RA mode. It's a bug workaround for mobile devices which go deaf to RAs during sleep and therefore
lose conectivity; with frequent RAs they recover in a reasonable time after wakeup.
.TP
.B --enable-tftp[=<interface>[,<interface>]]
Enable the TFTP server function. This is deliberately limited to that
needed to net-boot a client. Only reading is allowed; the tsize and
blksize extensions are supported (tsize is only supported in octet
mode).
mode). Without an argument, the TFTP service is provided to the same set of interfaces as DHCP service.
If the list of interfaces is provided, that defines which interfaces recieve TFTP service.
.TP
.B --tftp-root=<directory>[,<interface>]
Look for files to transfer using TFTP relative to the given
+93 -48
View File
@@ -54,6 +54,7 @@ static int filter_constructed_dhcp(struct auth_zone *zone, int flag, struct all_
if (flag & F_IPV6)
for (context = daemon->dhcp6; context; context = context->next)
if ((context->flags & CONTEXT_CONSTRUCTED) &&
!(context->flags & CONTEXT_NOAUTH) &&
is_same_net6(&(addr_u->addr.addr6), &context->start6, context->prefix))
return 1;
#endif
@@ -152,32 +153,55 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
auth = 0;
continue;
}
if (flag == F_IPV4)
{
for (intr = daemon->int_names; intr; intr = intr->next)
{
if (addr.addr.addr4.s_addr == get_ifaddr(intr->intr).s_addr)
break;
else
while (intr->next && strcmp(intr->intr, intr->next->intr) == 0)
intr = intr->next;
}
intr = NULL;
if (intr)
{
if (in_zone(zone, intr->name, NULL))
{
found = 1;
log_query(F_IPV4 | F_REVERSE | F_CONFIG, intr->name, &addr, NULL);
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
daemon->auth_ttl, NULL,
T_PTR, C_IN, "d", intr->name))
anscount++;
}
if (flag == F_IPV4)
for (intr = daemon->int_names; intr; intr = intr->next)
{
struct addrlist *addrlist;
for (addrlist = intr->addr4; addrlist; addrlist = addrlist->next)
if (addr.addr.addr4.s_addr == addrlist->addr.addr.addr4.s_addr)
break;
if (addrlist)
break;
else
while (intr->next && strcmp(intr->intr, intr->next->intr) == 0)
intr = intr->next;
}
#ifdef HAVE_IPV6
else if (flag == F_IPV6)
for (intr = daemon->int_names; intr; intr = intr->next)
{
struct addrlist *addrlist;
for (addrlist = intr->addr6; addrlist; addrlist = addrlist->next)
if (IN6_ARE_ADDR_EQUAL(&addr.addr.addr6, &addrlist->addr.addr.addr6))
break;
if (addrlist)
break;
else
while (intr->next && strcmp(intr->intr, intr->next->intr) == 0)
intr = intr->next;
}
#endif
if (intr)
{
if (in_zone(zone, intr->name, NULL))
{
found = 1;
log_query(flag| F_REVERSE | F_CONFIG, intr->name, &addr, NULL);
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
daemon->auth_ttl, NULL,
T_PTR, C_IN, "d", intr->name))
anscount++;
}
}
if ((crecp = cache_find_by_addr(NULL, &addr, now, flag)))
do {
strcpy(name, cache_get_name(crecp));
@@ -321,20 +345,37 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
anscount++;
}
}
if (qtype == T_A)
flag = F_IPV4;
#ifdef HAVE_IPV6
if (qtype == T_AAAA)
flag = F_IPV6;
#endif
for (intr = daemon->int_names; intr; intr = intr->next)
if (hostname_isequal(name, intr->name))
{
struct addrlist *addrlist;
addrlist = intr->addr4;
#ifdef HAVE_IPV6
if (qtype == T_AAAA)
addrlist = intr->addr6;
#endif
nxdomain = 0;
if (qtype == T_A && (addr.addr.addr4 = get_ifaddr(intr->intr)).s_addr != (in_addr_t) -1)
{
found = 1;
log_query(F_FORWARD | F_CONFIG | F_IPV4, name, &addr, NULL);
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
daemon->auth_ttl, NULL, T_A, C_IN, "4", &addr))
anscount++;
}
for (; addrlist; addrlist = addrlist->next)
if (filter_constructed_dhcp(zone, flag, &addrlist->addr))
{
found = 1;
log_query(F_FORWARD | F_CONFIG | flag, name, &addrlist->addr, NULL);
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
daemon->auth_ttl, NULL, qtype, C_IN,
qtype == T_A ? "4" : "6", &addrlist->addr))
anscount++;
}
}
for (a = daemon->cnames; a; a = a->next)
@@ -356,14 +397,6 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
goto cname_restart;
}
if (qtype == T_A)
flag = F_IPV4;
#ifdef HAVE_IPV6
if (qtype == T_AAAA)
flag = F_IPV6;
#endif
if (!cut)
{
nxdomain = 0;
@@ -488,10 +521,10 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
in_addr_t a = ntohl(subnet->addr4.s_addr) >> 8;
char *p = name;
if (subnet->prefixlen == 24)
if (subnet->prefixlen >= 24)
p += sprintf(p, "%d.", a & 0xff);
a = a >> 8;
if (subnet->prefixlen != 8)
if (subnet->prefixlen >= 16 )
p += sprintf(p, "%d.", a & 0xff);
a = a >> 8;
p += sprintf(p, "%d.in-addr.arpa", a & 0xff);
@@ -634,20 +667,32 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
}
for (intr = daemon->int_names; intr; intr = intr->next)
if (in_zone(zone, intr->name, &cut) && (addr.addr.addr4 = get_ifaddr(intr->intr)).s_addr != (in_addr_t) -1)
if (in_zone(zone, intr->name, &cut))
{
struct addrlist *addrlist;
if (cut)
*cut = 0;
if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp,
daemon->auth_ttl, NULL, T_A, C_IN, "4", cut ? intr->name : NULL, &addr))
anscount++;
for (addrlist = intr->addr4; addrlist; addrlist = addrlist->next)
if (filter_constructed_dhcp(zone, F_IPV4, &addrlist->addr) &&
add_resource_record(header, limit, &trunc, -axfroffset, &ansp,
daemon->auth_ttl, NULL, T_A, C_IN, "4", cut ? intr->name : NULL, &addrlist->addr))
anscount++;
#ifdef HAVE_IPV6
for (addrlist = intr->addr6; addrlist; addrlist = addrlist->next)
if (filter_constructed_dhcp(zone, F_IPV6, &addrlist->addr) &&
add_resource_record(header, limit, &trunc, -axfroffset, &ansp,
daemon->auth_ttl, NULL, T_AAAA, C_IN, "6", cut ? intr->name : NULL, &addrlist->addr))
anscount++;
#endif
/* restore config data */
if (cut)
*cut = '.';
}
for (a = daemon->cnames; a; a = a->next)
if (in_zone(zone, a->alias, &cut))
{
+53 -11
View File
@@ -25,6 +25,10 @@
#include <net/route.h>
#include <net/if_dl.h>
#include <netinet/if_ether.h>
#if defined(__FreeBSD__)
# include <net/if_var.h>
#endif
#include <netinet/in_var.h>
#ifndef SA_SIZE
#define SA_SIZE(sa) \
@@ -89,7 +93,7 @@ int arp_enumerate(void *parm, int (*callback)())
int iface_enumerate(int family, void *parm, int (*callback)())
{
struct ifaddrs *head, *addrs;
int errsav, ret = 0;
int errsav, fd = -1, ret = 0;
if (family == AF_UNSPEC)
#if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
@@ -105,13 +109,19 @@ int iface_enumerate(int family, void *parm, int (*callback)())
if (getifaddrs(&head) == -1)
return 0;
#if defined(HAVE_BSD_NETWORK) && defined(HAVE_IPV6)
if (family == AF_INET6)
fd = socket(PF_INET6, SOCK_DGRAM, 0);
#endif
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 || !addrs->ifa_addr || !addrs->ifa_netmask)
if (iface_index == 0 || !addrs->ifa_addr ||
(!addrs->ifa_netmask && family != AF_LINK))
continue;
if (family == AF_INET)
@@ -133,11 +143,40 @@ int iface_enumerate(int family, void *parm, int (*callback)())
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;
u32 valid = 0xffffffff, preferred = 0xffffffff;
int flags = 0;
#ifdef HAVE_BSD_NETWORK
struct in6_ifreq ifr6;
memset(&ifr6, 0, sizeof(ifr6));
strncpy(ifr6.ifr_name, addrs->ifa_name, sizeof(ifr6.ifr_name));
ifr6.ifr_addr = *((struct sockaddr_in6 *) addrs->ifa_addr);
if (fd != -1 && ioctl(fd, SIOCGIFAFLAG_IN6, &ifr6) != -1)
{
if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_TENTATIVE)
flags |= IFACE_TENTATIVE;
if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_DEPRECATED)
flags |= IFACE_DEPRECATED;
if (!(ifr6.ifr_ifru.ifru_flags6 & (IN6_IFF_AUTOCONF | IN6_IFF_TEMPORARY)))
flags |= IFACE_PERMANENT;
}
ifr6.ifr_addr = *((struct sockaddr_in6 *) addrs->ifa_addr);
if (fd != -1 && ioctl(fd, SIOCGIFALIFETIME_IN6, &ifr6) != -1)
{
valid = ifr6.ifr_ifru.ifru_lifetime.ia6t_vltime;
preferred = ifr6.ifr_ifru.ifru_lifetime.ia6t_pltime;
}
#endif
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)
@@ -148,13 +187,14 @@ int iface_enumerate(int family, void *parm, int (*callback)())
{
addr->s6_addr[2] = 0;
addr->s6_addr[3] = 0;
}
/* preferred and valid times == forever until we known how to dtermine them. */
if (!((*callback)(addr, prefix, scope_id, iface_index, 0, -1, -1, parm)))
goto err;
}
#endif
}
if (!((*callback)(addr, prefix, scope_id, iface_index, flags,
(int) preferred, (int)valid, parm)))
goto err;
}
#endif /* HAVE_IPV6 */
#ifdef HAVE_DHCP6
else if (family == AF_LINK)
{
@@ -172,7 +212,9 @@ int iface_enumerate(int family, void *parm, int (*callback)())
err:
errsav = errno;
freeifaddrs(head);
freeifaddrs(head);
if (fd != -1)
close(fd);
errno = errsav;
return ret;
+16 -7
View File
@@ -305,8 +305,6 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
const char *addr_err;
char *dup = NULL;
my_syslog(LOG_INFO, _("setting upstream servers from DBus"));
if (!dbus_message_iter_init(message, &iter))
{
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
@@ -478,6 +476,7 @@ DBusHandlerResult message_handler(DBusConnection *connection,
{
char *method = (char *)dbus_message_get_member(message);
DBusMessage *reply = NULL;
int clear_cache = 0, new_servers = 0;
if (dbus_message_is_method_call(message, DBUS_INTERFACE_INTROSPECTABLE, "Introspect"))
{
@@ -501,24 +500,34 @@ DBusHandlerResult message_handler(DBusConnection *connection,
}
else if (strcmp(method, "SetServers") == 0)
{
my_syslog(LOG_INFO, _("setting upstream servers from DBus"));
dbus_read_servers(message);
check_servers();
new_servers = 1;
}
else if (strcmp(method, "SetServersEx") == 0)
{
reply = dbus_read_servers_ex(message, 0);
check_servers();
new_servers = 1;
}
else if (strcmp(method, "SetDomainServers") == 0)
{
reply = dbus_read_servers_ex(message, 1);
check_servers();
new_servers = 1;
}
else if (strcmp(method, "ClearCache") == 0)
clear_cache_and_reload(dnsmasq_time());
clear_cache = 1;
else
return (DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
if (new_servers)
{
my_syslog(LOG_INFO, _("setting upstream servers from DBus"));
check_servers();
if (option_bool(OPT_RELOAD))
clear_cache = 1;
}
if (clear_cache)
clear_cache_and_reload(dnsmasq_time());
method = user_data; /* no warning */
+29 -12
View File
@@ -347,21 +347,27 @@ void bindtodevice(int fd)
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. */
SO_BINDTODEVICE is only available Linux.
Note that if wildcards are used in --interface, or a configured interface doesn't
yet exist, then more interfaces may arrive later, so we can't safely assert there
is only one interface and proceed.
*/
struct irec *iface, *found;
struct iname *if_tmp;
for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next)
if (if_tmp->name && (!if_tmp->used || strchr(if_tmp->name, '*')))
return;
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;
}
return; /* more than one. */
}
if (found)
@@ -714,7 +720,7 @@ void log_context(int family, struct dhcp_context *context)
p += sprintf(p, ", ");
if (indextoname(daemon->doing_dhcp6 ? daemon->dhcp6fd : daemon->icmp6fd, context->if_index, ifrn_name))
sprintf(p, "constructed for %s", ifrn_name);
sprintf(p, "%s for %s", (context->flags & CONTEXT_OLD) ? "old prefix" : "constructed", ifrn_name);
}
else if (context->flags & CONTEXT_TEMPLATE)
{
@@ -725,7 +731,8 @@ void log_context(int family, struct dhcp_context *context)
}
#endif
if ((context->flags & CONTEXT_DHCP) || family == AF_INET)
if (!(context->flags & CONTEXT_OLD) &&
((context->flags & CONTEXT_DHCP) || family == AF_INET))
{
inet_ntop(family, start, daemon->dhcp_buff, 256);
inet_ntop(family, end, daemon->dhcp_buff3, 256);
@@ -742,14 +749,24 @@ void log_context(int family, struct dhcp_context *context)
}
#ifdef HAVE_DHCP6
if (context->flags & CONTEXT_RA_NAME)
if ((context->flags & CONTEXT_RA_NAME) && !(context->flags & CONTEXT_OLD))
my_syslog(MS_DHCP | LOG_INFO, _("DHCPv4-derived IPv6 names on %s%s"), daemon->addrbuff, template);
if ((context->flags & CONTEXT_RA) || (option_bool(OPT_RA) && (context->flags & CONTEXT_DHCP) && family == AF_INET6))
my_syslog(MS_DHCP | LOG_INFO, _("router advertisement on %s%s"), daemon->addrbuff, template);
#endif
}
void log_relay(int family, struct dhcp_relay *relay)
{
inet_ntop(family, &relay->local, daemon->addrbuff, ADDRSTRLEN);
inet_ntop(family, &relay->server, daemon->namebuff, ADDRSTRLEN);
if (relay->interface)
my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay from %s to %s via %s"), daemon->addrbuff, daemon->namebuff, relay->interface);
else
my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay from %s to %s"), daemon->addrbuff, daemon->namebuff);
}
#endif
+165 -51
View File
@@ -20,6 +20,8 @@
struct iface_param {
struct dhcp_context *current;
struct dhcp_relay *relay;
struct in_addr relay_local;
int ind;
};
@@ -32,6 +34,8 @@ static int complete_context(struct in_addr local, int if_index, char *label,
struct in_addr netmask, struct in_addr broadcast, void *vparam);
static int check_listen_addrs(struct in_addr local, int if_index, char *label,
struct in_addr netmask, struct in_addr broadcast, void *vparam);
static int relay_upstream4(struct dhcp_relay *relay, struct dhcp_packet *mess, size_t sz, int iface_index);
static struct dhcp_relay *relay_reply4(struct dhcp_packet *mess, char *arrival_interface);
static int make_fd(int port)
{
@@ -70,15 +74,15 @@ static int make_fd(int port)
support it. This handles the introduction of REUSEPORT on Linux. */
if (option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND))
{
int rc = -1, porterr = 0;
int rc = 0;
#ifdef SO_REUSEPORT
if ((rc = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &oneopt, sizeof(oneopt))) == -1 &&
errno != ENOPROTOOPT)
porterr = 1;
errno == ENOPROTOOPT)
rc = 0;
#endif
if (rc == -1 && !porterr)
if (rc != -1)
rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &oneopt, sizeof(oneopt));
if (rc == -1)
@@ -132,6 +136,8 @@ void dhcp_packet(time_t now, int pxe_fd)
int fd = pxe_fd ? daemon->pxefd : daemon->dhcpfd;
struct dhcp_packet *mess;
struct dhcp_context *context;
struct dhcp_relay *relay;
int is_relay_reply = 0;
struct iname *tmp;
struct ifreq ifr;
struct msghdr msg;
@@ -250,57 +256,86 @@ void dhcp_packet(time_t now, int pxe_fd)
unicast_dest = 1;
#endif
ifr.ifr_addr.sa_family = AF_INET;
if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) != -1 )
iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
if ((relay = relay_reply4((struct dhcp_packet *)daemon->dhcp_packet.iov_base, ifr.ifr_name)))
{
/* Reply from server, using us as relay. */
iface_index = relay->iface_index;
if (!indextoname(daemon->dhcpfd, iface_index, ifr.ifr_name))
return;
is_relay_reply = 1;
iov.iov_len = sz;
#ifdef HAVE_LINUX_NETWORK
strncpy(arp_req.arp_dev, ifr.ifr_name, 16);
#endif
}
else
{
my_syslog(MS_DHCP | LOG_WARNING, _("DHCP packet received on %s which has no address"), ifr.ifr_name);
return;
}
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
return;
/* unlinked contexts are marked by context->current == context */
for (context = daemon->dhcp; context; context = context->next)
context->current = context;
parm.current = NULL;
parm.ind = iface_index;
if (!iface_check(AF_INET, (struct all_addr *)&iface_addr, ifr.ifr_name, NULL))
{
/* If we failed to match the primary address of the interface, see if we've got a --listen-address
for a secondary */
struct match_param match;
ifr.ifr_addr.sa_family = AF_INET;
if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) != -1 )
iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
else
{
my_syslog(MS_DHCP | LOG_WARNING, _("DHCP packet received on %s which has no address"), ifr.ifr_name);
return;
}
match.matched = 0;
match.ind = iface_index;
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
return;
if (!daemon->if_addrs ||
!iface_enumerate(AF_INET, &match, check_listen_addrs) ||
!match.matched)
/* unlinked contexts/relays are marked by context->current == context */
for (context = daemon->dhcp; context; context = context->next)
context->current = context;
for (relay = daemon->relay4; relay; relay = relay->next)
relay->current = relay;
parm.current = NULL;
parm.relay = NULL;
parm.relay_local.s_addr = 0;
parm.ind = iface_index;
if (!iface_check(AF_INET, (struct all_addr *)&iface_addr, ifr.ifr_name, NULL))
{
/* If we failed to match the primary address of the interface, see if we've got a --listen-address
for a secondary */
struct match_param match;
match.matched = 0;
match.ind = iface_index;
if (!daemon->if_addrs ||
!iface_enumerate(AF_INET, &match, check_listen_addrs) ||
!match.matched)
return;
iface_addr = match.addr;
/* make sure secondary address gets priority in case
there is more than one address on the interface in the same subnet */
complete_context(match.addr, iface_index, NULL, match.netmask, match.broadcast, &parm);
}
if (!iface_enumerate(AF_INET, &parm, complete_context))
return;
iface_addr = match.addr;
/* make sure secondary address gets priority in case
there is more than one address on the interface in the same subnet */
complete_context(match.addr, iface_index, NULL, match.netmask, match.broadcast, &parm);
}
/* We're relaying this request */
if (parm.relay_local.s_addr != 0 &&
relay_upstream4(parm.relay, (struct dhcp_packet *)daemon->dhcp_packet.iov_base, (size_t)sz, iface_index))
return;
/* May have configured relay, but not DHCP server */
if (!daemon->dhcp)
return;
lease_prune(NULL, now); /* lose any expired leases */
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(0);
if (!iface_enumerate(AF_INET, &parm, complete_context))
return;
lease_prune(NULL, now); /* lose any expired leases */
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(0);
if (iov.iov_len == 0)
return;
if (iov.iov_len == 0)
return;
}
msg.msg_name = &dest;
msg.msg_namelen = sizeof(dest);
@@ -321,7 +356,7 @@ void dhcp_packet(time_t now, int pxe_fd)
if (mess->ciaddr.s_addr != 0)
dest.sin_addr = mess->ciaddr;
}
else if (mess->giaddr.s_addr)
else if (mess->giaddr.s_addr && !is_relay_reply)
{
/* Send to BOOTP relay */
dest.sin_port = htons(daemon->dhcp_server_port);
@@ -334,7 +369,7 @@ void dhcp_packet(time_t now, int pxe_fd)
source port too, and send back to that. If we're replying
to a DHCPINFORM, trust the source address always. */
if ((!is_inform && dest.sin_addr.s_addr != mess->ciaddr.s_addr) ||
dest.sin_port == 0 || dest.sin_addr.s_addr == 0)
dest.sin_port == 0 || dest.sin_addr.s_addr == 0 || is_relay_reply)
{
dest.sin_port = htons(daemon->dhcp_client_port);
dest.sin_addr = mess->ciaddr;
@@ -450,6 +485,7 @@ static int complete_context(struct in_addr local, int if_index, char *label,
struct in_addr netmask, struct in_addr broadcast, void *vparam)
{
struct dhcp_context *context;
struct dhcp_relay *relay;
struct iface_param *param = vparam;
(void)label;
@@ -495,6 +531,15 @@ static int complete_context(struct in_addr local, int if_index, char *label,
}
}
for (relay = daemon->relay4; relay; relay = relay->next)
if (if_index == param->ind && relay->local.addr.addr4.s_addr == local.s_addr && relay->current == relay &&
(param->relay_local.s_addr == 0 || param->relay_local.s_addr == local.s_addr))
{
relay->current = param->relay;
param->relay = relay;
param->relay_local = local;
}
return 1;
}
@@ -988,5 +1033,74 @@ char *host_from_dns(struct in_addr addr)
return NULL;
}
#endif
static int relay_upstream4(struct dhcp_relay *relay, struct dhcp_packet *mess, size_t sz, int iface_index)
{
/* ->local is same value for all relays on ->current chain */
struct all_addr from;
if (mess->op != BOOTREQUEST)
return 0;
/* source address == relay address */
from.addr.addr4 = relay->local.addr.addr4;
/* already gatewayed ? */
if (mess->giaddr.s_addr)
{
/* if so check if by us, to stomp on loops. */
if (mess->giaddr.s_addr == relay->local.addr.addr4.s_addr)
return 1;
}
else
{
/* plug in our address */
mess->giaddr.s_addr = relay->local.addr.addr4.s_addr;
}
if ((mess->hops++) > 20)
return 1;
for (; relay; relay = relay->current)
{
union mysockaddr to;
to.sa.sa_family = AF_INET;
to.in.sin_addr = relay->server.addr.addr4;
to.in.sin_port = htons(daemon->dhcp_server_port);
send_from(daemon->dhcpfd, 0, (char *)mess, sz, &to, &from, 0);
if (option_bool(OPT_LOG_OPTS))
{
inet_ntop(AF_INET, &relay->local, daemon->addrbuff, ADDRSTRLEN);
my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay %s -> %s"), daemon->addrbuff, inet_ntoa(relay->server.addr.addr4));
}
/* Save this for replies */
relay->iface_index = iface_index;
}
return 1;
}
static struct dhcp_relay *relay_reply4(struct dhcp_packet *mess, char *arrival_interface)
{
struct dhcp_relay *relay;
if (mess->giaddr.s_addr == 0 || mess->op != BOOTREPLY)
return NULL;
for (relay = daemon->relay4; relay; relay = relay->next)
{
if (mess->giaddr.s_addr == relay->local.addr.addr4.s_addr)
{
if (!relay->interface || wildcard_match(relay->interface, arrival_interface))
return relay->iface_index != 0 ? relay : NULL;
}
}
return NULL;
}
#endif
+1
View File
@@ -55,6 +55,7 @@
#define OPTION6_RECONF_ACCEPT 20
#define OPTION6_DNS_SERVER 23
#define OPTION6_DOMAIN_SEARCH 24
#define OPTION6_REFRESH_TIME 32
#define OPTION6_REMOTE_ID 37
#define OPTION6_SUBSCRIBER_ID 38
#define OPTION6_FQDN 39
+187 -114
View File
@@ -20,7 +20,8 @@
struct iface_param {
struct dhcp_context *current;
struct in6_addr fallback;
struct dhcp_relay *relay;
struct in6_addr fallback, relay_local;
int ind, addr_match;
};
@@ -55,15 +56,15 @@ void dhcp6_init(void)
support it. This handles the introduction of REUSEPORT on Linux. */
if (option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND))
{
int rc = -1, porterr = 0;
int rc = 0;
#ifdef SO_REUSEPORT
if ((rc = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &oneopt, sizeof(oneopt))) == -1 &&
errno != ENOPROTOOPT)
porterr = 1;
errno == ENOPROTOOPT)
rc = 0;
#endif
if (rc == -1 && !porterr)
if (rc != -1)
rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &oneopt, sizeof(oneopt));
if (rc == -1)
@@ -87,6 +88,7 @@ void dhcp6_init(void)
void dhcp6_packet(time_t now)
{
struct dhcp_context *context;
struct dhcp_relay *relay;
struct iface_param parm;
struct cmsghdr *cmptr;
struct msghdr msg;
@@ -126,56 +128,75 @@ void dhcp6_packet(time_t now)
if (!indextoname(daemon->dhcp6fd, if_index, ifr.ifr_name))
return;
for (tmp = daemon->if_except; tmp; tmp = tmp->next)
if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
return;
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
return;
parm.current = NULL;
parm.ind = if_index;
parm.addr_match = 0;
memset(&parm.fallback, 0, IN6ADDRSZ);
for (context = daemon->dhcp6; context; context = context->next)
if (IN6_IS_ADDR_UNSPECIFIED(&context->start6) && context->prefix == 0)
{
/* wildcard context for DHCP-stateless only */
parm.current = context;
context->current = NULL;
}
else
{
/* unlinked contexts are marked by context->current == context */
context->current = context;
memset(&context->local6, 0, IN6ADDRSZ);
}
if (!iface_enumerate(AF_INET6, &parm, complete_context6))
return;
if (daemon->if_names || daemon->if_addrs)
if ((port = relay_reply6(&from, sz, ifr.ifr_name)) == 0)
{
for (tmp = daemon->if_names; tmp; tmp = tmp->next)
for (tmp = daemon->if_except; tmp; tmp = tmp->next)
if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
break;
return;
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
return;
parm.current = NULL;
parm.relay = NULL;
memset(&parm.relay_local, 0, IN6ADDRSZ);
parm.ind = if_index;
parm.addr_match = 0;
memset(&parm.fallback, 0, IN6ADDRSZ);
for (context = daemon->dhcp6; context; context = context->next)
if (IN6_IS_ADDR_UNSPECIFIED(&context->start6) && context->prefix == 0)
{
/* wildcard context for DHCP-stateless only */
parm.current = context;
context->current = NULL;
}
else
{
/* unlinked contexts are marked by context->current == context */
context->current = context;
memset(&context->local6, 0, IN6ADDRSZ);
}
if (!tmp && !parm.addr_match)
for (relay = daemon->relay6; relay; relay = relay->next)
relay->current = relay;
if (!iface_enumerate(AF_INET6, &parm, complete_context6))
return;
if (daemon->if_names || daemon->if_addrs)
{
for (tmp = daemon->if_names; tmp; tmp = tmp->next)
if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
break;
if (!tmp && !parm.addr_match)
return;
}
if (parm.relay)
{
relay_upstream6(parm.relay, sz, &from.sin6_addr, from.sin6_scope_id);
return;
}
/* May have configured relay, but not DHCP server */
if (!daemon->doing_dhcp6)
return;
lease_prune(NULL, now); /* lose any expired leases */
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(0);
}
lease_prune(NULL, now); /* lose any expired leases */
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(0);
/* 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
@@ -194,70 +215,84 @@ static int complete_context6(struct in6_addr *local, int prefix,
unsigned int valid, void *vparam)
{
struct dhcp_context *context;
struct dhcp_relay *relay;
struct iface_param *param = vparam;
struct iname *tmp;
(void)scope; /* warning */
if (if_index == param->ind &&
!IN6_IS_ADDR_LOOPBACK(local) &&
!IN6_IS_ADDR_LINKLOCAL(local) &&
!IN6_IS_ADDR_MULTICAST(local))
if (if_index == param->ind)
{
/* if we have --listen-address config, see if the
arrival interface has a matching address. */
for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
if (tmp->addr.sa.sa_family == AF_INET6 &&
IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr, local))
param->addr_match = 1;
/* Determine a globally address on the arrival interface, even
if we have no matching dhcp-context, because we're only
allocating on remote subnets via relays. This
is used as a default for the DNS server option. */
param->fallback = *local;
for (context = daemon->dhcp6; context; context = context->next)
if (!IN6_IS_ADDR_LOOPBACK(local) &&
!IN6_IS_ADDR_LINKLOCAL(local) &&
!IN6_IS_ADDR_MULTICAST(local))
{
if ((context->flags & CONTEXT_DHCP) &&
!(context->flags & CONTEXT_TEMPLATE) &&
prefix == context->prefix &&
is_same_net6(local, &context->start6, prefix) &&
is_same_net6(local, &context->end6, prefix))
/* if we have --listen-address config, see if the
arrival interface has a matching address. */
for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
if (tmp->addr.sa.sa_family == AF_INET6 &&
IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr, local))
param->addr_match = 1;
/* Determine a globally address on the arrival interface, even
if we have no matching dhcp-context, because we're only
allocating on remote subnets via relays. This
is used as a default for the DNS server option. */
param->fallback = *local;
for (context = daemon->dhcp6; context; context = context->next)
{
/* link it onto the current chain if we've not seen it before */
if (context->current == context)
if ((context->flags & CONTEXT_DHCP) &&
!(context->flags & (CONTEXT_TEMPLATE | CONTEXT_OLD)) &&
prefix == context->prefix &&
is_same_net6(local, &context->start6, prefix) &&
is_same_net6(local, &context->end6, prefix))
{
struct dhcp_context *tmp, **up;
/* use interface values only for contructed contexts */
if (!(context->flags & CONTEXT_CONSTRUCTED))
preferred = valid = 0xffffffff;
else if (flags & IFACE_DEPRECATED)
preferred = 0;
if (context->flags & CONTEXT_DEPRECATE)
preferred = 0;
/* order chain, longest preferred time first */
for (up = &param->current, tmp = param->current; tmp; tmp = tmp->current)
if (tmp->preferred <= preferred)
break;
else
up = &tmp->current;
context->current = *up;
*up = context;
context->local6 = *local;
context->preferred = preferred;
context->valid = valid;
/* link it onto the current chain if we've not seen it before */
if (context->current == context)
{
struct dhcp_context *tmp, **up;
/* use interface values only for contructed contexts */
if (!(context->flags & CONTEXT_CONSTRUCTED))
preferred = valid = 0xffffffff;
else if (flags & IFACE_DEPRECATED)
preferred = 0;
if (context->flags & CONTEXT_DEPRECATE)
preferred = 0;
/* order chain, longest preferred time first */
for (up = &param->current, tmp = param->current; tmp; tmp = tmp->current)
if (tmp->preferred <= preferred)
break;
else
up = &tmp->current;
context->current = *up;
*up = context;
context->local6 = *local;
context->preferred = preferred;
context->valid = valid;
}
}
}
}
for (relay = daemon->relay6; relay; relay = relay->next)
if (IN6_ARE_ADDR_EQUAL(local, &relay->local.addr.addr6) && relay->current == relay &&
(IN6_IS_ADDR_UNSPECIFIED(&param->relay_local) || IN6_ARE_ADDR_EQUAL(local, &param->relay_local)))
{
relay->current = param->relay;
param->relay = relay;
param->relay_local = *local;
}
}
return 1;
return 1;
}
struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct in6_addr *net, int prefix, u64 addr)
@@ -538,7 +573,11 @@ static int construct_worker(struct in6_addr *local, int prefix,
}
}
else if (addr6part(local) == addr6part(&template->start6) && wildcard_match(template->template_interface, ifrn_name))
else if ((addr6part(local) == addr6part(&template->start6) ||
addr6part(local) == addr6part(&template->end6) ||
(IN6_IS_ADDR_UNSPECIFIED(&template->start6) &&
IFACE_PERMANENT == (flags & (IFACE_PERMANENT | IFACE_DEPRECATED)))) &&
wildcard_match(template->template_interface, ifrn_name))
{
start6 = *local;
setaddr6part(&start6, addr6part(&template->start6));
@@ -550,7 +589,18 @@ static int construct_worker(struct in6_addr *local, int prefix,
IN6_ARE_ADDR_EQUAL(&start6, &context->start6) &&
IN6_ARE_ADDR_EQUAL(&end6, &context->end6))
{
context->flags &= ~CONTEXT_GC;
int flags = context->flags;
context->flags &= ~(CONTEXT_GC | CONTEXT_OLD);
if (flags & CONTEXT_OLD)
{
/* address went, now it's back */
log_context(AF_INET6, context);
/* fast RAs for a while */
ra_start_unsolicted(param->now, context);
/* Add address to name again */
if (context->flags & CONTEXT_RA_NAME)
param->newname = 1;
}
break;
}
@@ -563,6 +613,7 @@ static int construct_worker(struct in6_addr *local, int prefix,
context->flags |= CONTEXT_CONSTRUCTED;
context->if_index = if_index;
context->local6 = *local;
context->saved_valid = 0;
context->next = daemon->dhcp6;
daemon->dhcp6 = context;
@@ -585,35 +636,57 @@ static int construct_worker(struct in6_addr *local, int prefix,
void dhcp_construct_contexts(time_t now)
{
struct dhcp_context *tmp, *context, **up;
struct dhcp_context *context, *tmp, **up;
struct cparam param;
param.newone = 0;
param.newname = 0;
param.now = now;
for (context = daemon->dhcp6; context; context = context->next)
{
context->if_index = 0;
if (context->flags & CONTEXT_CONSTRUCTED)
context->flags |= CONTEXT_GC;
}
if (context->flags & CONTEXT_CONSTRUCTED)
context->flags |= CONTEXT_GC;
iface_enumerate(AF_INET6, &param, construct_worker);
for (up = &daemon->dhcp6, context = daemon->dhcp6; context; context = tmp)
{
tmp = context->next;
if (context->flags & CONTEXT_GC)
tmp = context->next;
if (context->flags & CONTEXT_GC && !(context->flags & CONTEXT_OLD))
{
*up = context->next;
param.newone = 1; /* include deletion */
if (context->flags & CONTEXT_RA_NAME)
param.newname = 1;
free(context);
if ((context->flags & (CONTEXT_RA_ONLY | CONTEXT_RA_NAME | CONTEXT_RA_STATELESS)) ||
option_bool(OPT_RA))
{
/* previously constructed context has gone. advertise it's demise */
context->flags |= CONTEXT_OLD;
context->address_lost_time = now;
/* Apply same ceiling of configured lease time as in radv.c */
if (context->saved_valid > context->lease_time)
context->saved_valid = context->lease_time;
/* maximum time is 2 hours, from RFC */
if (context->saved_valid > 7200) /* 2 hours */
context->saved_valid = 7200;
ra_start_unsolicted(now, context);
param.newone = 1; /* include deletion */
if (context->flags & CONTEXT_RA_NAME)
param.newname = 1;
log_context(AF_INET6, context);
up = &context->next;
}
else
{
/* we were never doing RA for this, so free now */
*up = context->next;
free(context);
}
}
else
up = &context->next;
up = &context->next;
}
if (param.newone)
+111 -96
View File
@@ -52,6 +52,7 @@ int main (int argc, char **argv)
cap_user_data_t data = NULL;
#endif
struct dhcp_context *context;
struct dhcp_relay *relay;
#ifdef LOCALEDIR
setlocale(LC_ALL, "");
@@ -166,50 +167,47 @@ int main (int argc, char **argv)
daemon->soa_sn = now;
#endif
#ifdef HAVE_DHCP
if (daemon->dhcp || daemon->dhcp6)
{
#ifdef HAVE_DHCP6
if (daemon->dhcp6)
{
daemon->doing_ra = option_bool(OPT_RA);
# ifdef HAVE_DHCP6
if (daemon->dhcp6)
for (context = daemon->dhcp6; context; context = context->next)
{
daemon->doing_ra = option_bool(OPT_RA);
for (context = daemon->dhcp6; context; context = context->next)
{
if (context->flags & CONTEXT_DHCP)
daemon->doing_dhcp6 = 1;
if (context->flags & CONTEXT_RA)
daemon->doing_ra = 1;
if (context->flags & CONTEXT_DHCP)
daemon->doing_dhcp6 = 1;
if (context->flags & CONTEXT_RA)
daemon->doing_ra = 1;
#ifndef HAVE_LINUX_NETWORK
if (context->flags & CONTEXT_TEMPLATE)
die (_("dhcp-range constructor not available on this platform"), NULL, EC_BADCONF);
if (context->flags & CONTEXT_TEMPLATE)
die (_("dhcp-range constructor not available on this platform"), NULL, EC_BADCONF);
#endif
}
}
# endif
/* Note that order matters here, we must call lease_init before
creating any file descriptors which shouldn't be leaked
to the lease-script init process. We need to call common_init
before lease_init to allocate buffers it uses.*/
if (daemon->dhcp || daemon->doing_dhcp6)
{
dhcp_common_init();
lease_init(now);
}
if (daemon->dhcp)
dhcp_init();
# ifdef HAVE_DHCP6
if (daemon->doing_ra)
ra_init(now);
if (daemon->doing_dhcp6)
dhcp6_init();
# endif
}
#endif
#ifdef HAVE_DHCP
/* Note that order matters here, we must call lease_init before
creating any file descriptors which shouldn't be leaked
to the lease-script init process. We need to call common_init
before lease_init to allocate buffers it uses.*/
if (daemon->dhcp || daemon->doing_dhcp6 || daemon->relay4 || daemon->relay6)
{
dhcp_common_init();
if (daemon->dhcp || daemon->doing_dhcp6)
lease_init(now);
}
if (daemon->dhcp || daemon->relay4)
dhcp_init();
# ifdef HAVE_DHCP6
if (daemon->doing_ra)
ra_init(now);
if (daemon->doing_dhcp6 || daemon->relay6)
dhcp6_init();
# endif
#endif
@@ -225,7 +223,7 @@ int main (int argc, char **argv)
die(_("cannot set --bind-interfaces and --bind-dynamic"), NULL, EC_BADCONF);
#endif
if (!enumerate_interfaces())
if (!enumerate_interfaces(1) || !enumerate_interfaces(0))
die(_("failed to find list of interfaces: %s"), NULL, EC_MISC);
if (option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND))
@@ -239,7 +237,7 @@ int main (int argc, char **argv)
#if defined(HAVE_LINUX_NETWORK) && defined(HAVE_DHCP)
/* after enumerate_interfaces() */
if (daemon->dhcp)
if (daemon->dhcp || daemon->relay4)
{
bindtodevice(daemon->dhcpfd);
if (daemon->enable_pxe)
@@ -248,7 +246,7 @@ int main (int argc, char **argv)
#endif
#if defined(HAVE_LINUX_NETWORK) && defined(HAVE_DHCP6)
if (daemon->dhcp6)
if (daemon->doing_dhcp6 || daemon->relay6)
bindtodevice(daemon->dhcp6fd);
#endif
}
@@ -257,7 +255,7 @@ int main (int argc, char **argv)
#ifdef HAVE_DHCP6
/* after enumerate_interfaces() */
if (daemon->doing_dhcp6 || daemon->doing_ra)
if (daemon->doing_dhcp6 || daemon->relay6 || daemon->doing_ra)
join_multicast(1);
#endif
@@ -641,10 +639,16 @@ int main (int argc, char **argv)
for (context = daemon->dhcp; context; context = context->next)
log_context(AF_INET, context);
for (relay = daemon->relay4; relay; relay = relay->next)
log_relay(AF_INET, relay);
# ifdef HAVE_DHCP6
for (context = daemon->dhcp6; context; context = context->next)
log_context(AF_INET6, context);
for (relay = daemon->relay6; relay; relay = relay->next)
log_relay(AF_INET6, relay);
if (daemon->doing_dhcp6 || daemon->doing_ra)
dhcp_construct_contexts(now);
@@ -749,7 +753,7 @@ int main (int argc, char **argv)
#endif
#ifdef HAVE_DHCP
if (daemon->dhcp)
if (daemon->dhcp || daemon->relay4)
{
FD_SET(daemon->dhcpfd, &rset);
bump_maxfd(daemon->dhcpfd, &maxfd);
@@ -762,7 +766,7 @@ int main (int argc, char **argv)
#endif
#ifdef HAVE_DHCP6
if (daemon->doing_dhcp6)
if (daemon->doing_dhcp6 || daemon->relay6)
{
FD_SET(daemon->dhcp6fd, &rset);
bump_maxfd(daemon->dhcp6fd, &maxfd);
@@ -820,12 +824,15 @@ int main (int argc, char **argv)
now = dnsmasq_time();
check_log_writer(&wset);
/* prime. */
enumerate_interfaces(1);
/* Check the interfaces to see if any have exited DAD state
and if so, bind the address. */
if (is_dad_listeners())
{
enumerate_interfaces();
enumerate_interfaces(0);
/* NB, is_dad_listeners() == 1 --> we're binding interfaces */
create_bound_listeners(0);
}
@@ -871,7 +878,7 @@ int main (int argc, char **argv)
#endif
#ifdef HAVE_DHCP
if (daemon->dhcp)
if (daemon->dhcp || daemon->relay4)
{
if (FD_ISSET(daemon->dhcpfd, &rset))
dhcp_packet(now, 0);
@@ -880,7 +887,7 @@ int main (int argc, char **argv)
}
#ifdef HAVE_DHCP6
if (daemon->doing_dhcp6 && FD_ISSET(daemon->dhcp6fd, &rset))
if ((daemon->doing_dhcp6 || daemon->relay6) && FD_ISSET(daemon->dhcp6fd, &rset))
dhcp6_packet(now);
if (daemon->doing_ra && FD_ISSET(daemon->icmp6fd, &rset))
@@ -1352,63 +1359,71 @@ static void check_dns_listeners(fd_set *set, time_t now)
if (confd == -1)
continue;
if (getsockname(confd, (struct sockaddr *)&tcp_addr, &tcp_len) == -1)
{
close(confd);
continue;
}
/* Make sure that the interface list is up-to-date.
We do this here as we may need the results below, and
the DNS code needs them for --interface-name stuff.
if (option_bool(OPT_NOWILD))
iface = listener->iface; /* May be NULL */
else
{
int if_index;
char intr_name[IF_NAMESIZE];
Multiple calls to enumerate_interfaces() per select loop are
inhibited, so calls to it in the child process (which doesn't select())
have no effect. This avoids two processes reading from the same
netlink fd and screwing the pooch entirely.
*/
/* In full wildcard mode, need to refresh interface list.
This happens automagically in CLEVERBIND */
if (!option_bool(OPT_CLEVERBIND))
enumerate_interfaces();
/* if we can find the arrival interface, check it's one that's allowed */
if ((if_index = tcp_interface(confd, tcp_addr.sa.sa_family)) != 0 &&
indextoname(listener->tcpfd, if_index, intr_name))
{
struct all_addr addr;
addr.addr.addr4 = tcp_addr.in.sin_addr;
enumerate_interfaces(0);
if (option_bool(OPT_NOWILD))
iface = listener->iface; /* May be NULL */
else
{
int if_index;
char intr_name[IF_NAMESIZE];
/* if we can find the arrival interface, check it's one that's allowed */
if ((if_index = tcp_interface(confd, tcp_addr.sa.sa_family)) != 0 &&
indextoname(listener->tcpfd, if_index, intr_name))
{
struct all_addr addr;
addr.addr.addr4 = tcp_addr.in.sin_addr;
#ifdef HAVE_IPV6
if (tcp_addr.sa.sa_family == AF_INET6)
addr.addr.addr6 = tcp_addr.in6.sin6_addr;
if (tcp_addr.sa.sa_family == AF_INET6)
addr.addr.addr6 = tcp_addr.in6.sin6_addr;
#endif
for (iface = daemon->interfaces; iface; iface = iface->next)
if (iface->index == if_index)
break;
if (!iface && !loopback_exception(listener->tcpfd, tcp_addr.sa.sa_family, &addr, intr_name))
client_ok = 0;
}
if (option_bool(OPT_CLEVERBIND))
iface = listener->iface; /* May be NULL */
else
{
/* 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
an allowed interface. As a side effect, we get the netmask of the
interface too, for localisation. */
for (iface = daemon->interfaces; iface; iface = iface->next)
if (sockaddr_isequal(&iface->addr, &tcp_addr))
break;
if (!iface)
client_ok = 0;
}
}
for (iface = daemon->interfaces; iface; iface = iface->next)
if (iface->index == if_index)
break;
if (!iface && !loopback_exception(listener->tcpfd, tcp_addr.sa.sa_family, &addr, intr_name))
client_ok = 0;
}
if (option_bool(OPT_CLEVERBIND))
iface = listener->iface; /* May be NULL */
else
{
/* 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
an allowed interface. As a side effect, we get the netmask of the
interface too, for localisation. */
for (iface = daemon->interfaces; iface; iface = iface->next)
if (sockaddr_isequal(&iface->addr, &tcp_addr))
break;
if (!iface)
client_ok = 0;
}
}
if (!client_ok)
{
shutdown(confd, SHUT_RDWR);
+28 -6
View File
@@ -221,7 +221,8 @@ struct event_desc {
#define OPT_TFTP_LC 38
#define OPT_CLEVERBIND 39
#define OPT_TFTP 40
#define OPT_LAST 41
#define OPT_FAST_RA 41
#define OPT_LAST 42
/* 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. */
@@ -307,6 +308,13 @@ struct host_record {
struct interface_name {
char *name; /* domain name */
char *intr; /* interface name */
struct addrlist {
struct all_addr addr;
struct addrlist *next;
} *addr4;
#ifdef HAVE_IPV6
struct addrlist *addr6;
#endif
struct interface_name *next;
};
@@ -392,6 +400,7 @@ union mysockaddr {
/* bits in flag param to IPv6 callbacks from iface_enumerate() */
#define IFACE_TENTATIVE 1
#define IFACE_DEPRECATED 2
#define IFACE_PERMANENT 4
#define SERV_FROM_RESOLV 1 /* 1 for servers from resolv, 0 for command line. */
@@ -699,8 +708,8 @@ struct dhcp_context {
struct in6_addr start6, end6; /* range of available addresses */
struct in6_addr local6;
int prefix, if_index;
unsigned int valid, preferred;
time_t ra_time, ra_short_period_start;
unsigned int valid, preferred, saved_valid;
time_t ra_time, ra_short_period_start, address_lost_time;
char *template_interface;
#endif
int flags;
@@ -724,6 +733,9 @@ struct dhcp_context {
#define CONTEXT_RA 8192
#define CONTEXT_CONF_USED 16384
#define CONTEXT_USED 32768
#define CONTEXT_NOAUTH 65536
#define CONTEXT_OLD 131072
struct ping_result {
struct in_addr addr;
@@ -763,6 +775,12 @@ struct tftp_prefix {
struct tftp_prefix *next;
};
struct dhcp_relay {
struct all_addr local, server;
char *interface; /* Allowable interface for replies from server, and dest for IPv6 multicast */
int iface_index; /* working - interface in which requests arrived, for return */
struct dhcp_relay *current, *next;
};
extern struct daemon {
/* datastuctures representing the command-line and
@@ -792,7 +810,7 @@ extern struct daemon {
struct cond_domain *cond_domain, *synth_domains;
char *runfile;
char *lease_change_command;
struct iname *if_names, *if_addrs, *if_except, *dhcp_except, *auth_peers;
struct iname *if_names, *if_addrs, *if_except, *dhcp_except, *auth_peers, *tftp_interfaces;
struct bogus_addr *bogus_addr;
struct server *servers;
struct ipsets *ipsets;
@@ -812,6 +830,7 @@ extern struct daemon {
struct pxe_service *pxe_services;
struct tag_if *tag_if;
struct addr_list *override_relays;
struct dhcp_relay *relay4, *relay6;
int override;
int enable_pxe;
int doing_ra, doing_dhcp6;
@@ -1024,7 +1043,7 @@ int random_sock(int family);
void pre_allocate_sfds(void);
int reload_servers(char *fname);
void check_servers(void);
int enumerate_interfaces();
int enumerate_interfaces(int reset);
void create_wildcard_listeners(void);
void create_bound_listeners(int die);
int is_dad_listeners(void);
@@ -1033,7 +1052,6 @@ int loopback_exception(int fd, int family, struct all_addr *addr, char *name);
int label_exception(int index, int family, struct all_addr *addr);
int fix_fd(int fd);
int tcp_interface(int fd, int af);
struct in_addr get_ifaddr(char *intr);
#ifdef HAVE_IPV6
int set_ipv6pktinfo(int fd);
#endif
@@ -1206,6 +1224,9 @@ void dhcp_construct_contexts(time_t now);
#ifdef HAVE_DHCP6
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);
void relay_upstream6(struct dhcp_relay *relay, ssize_t sz, struct in6_addr *peer_address, u32 scope_id);
unsigned short relay_reply6( struct sockaddr_in6 *peer, ssize_t sz, char *arrival_interface);
#endif
/* dhcp-common.c */
@@ -1232,6 +1253,7 @@ void bindtodevice(int fd);
void display_opts6(void);
# endif
void log_context(int family, struct dhcp_context *context);
void log_relay(int family, struct dhcp_relay *relay);
#endif
/* outpacket.c */
+1 -1
View File
@@ -224,7 +224,7 @@ char *get_domain6(struct in6_addr *addr)
{
struct cond_domain *c;
if ((c = search_domain6(addr, daemon->cond_domain)))
if (addr && (c = search_domain6(addr, daemon->cond_domain)))
return c->domain;
return daemon->domain_suffix;
+11 -2
View File
@@ -788,7 +788,7 @@ void receive_query(struct listener *listen, time_t now)
if (!iface_check(listen->family, &dst_addr, ifr.ifr_name, &auth_dns))
{
if (!option_bool(OPT_CLEVERBIND))
enumerate_interfaces();
enumerate_interfaces(0);
if (!loopback_exception(listen->fd, listen->family, &dst_addr, ifr.ifr_name) &&
!label_exception(if_index, listen->family, &dst_addr))
return;
@@ -808,7 +808,7 @@ void receive_query(struct listener *listen, time_t now)
/* interface may be new */
if (!iface && !option_bool(OPT_CLEVERBIND))
enumerate_interfaces();
enumerate_interfaces(0);
for (iface = daemon->interfaces; iface; iface = iface->next)
if (iface->addr.sa.sa_family == AF_INET &&
@@ -1205,8 +1205,17 @@ struct frec *get_new_frec(time_t now, int *wait)
/* none available, calculate time 'till oldest record expires */
if (count > daemon->ftabsize)
{
static time_t last_log = 0;
if (oldest && wait)
*wait = oldest->time + (time_t)TIMEOUT - now;
if ((int)difftime(now, last_log) > 5)
{
last_log = now;
my_syslog(LOG_WARNING, _("Maximum number of concurrent DNS queries reached (max: %d)"), daemon->ftabsize);
}
return NULL;
}
+13 -7
View File
@@ -193,7 +193,10 @@ int iface_enumerate(int family, void *parm, int (*callback)())
{
/* May be multicast arriving async */
if (nl_async(h))
newaddr = 1;
{
newaddr = 1;
enumerate_interfaces(1); /* reset */
}
}
else if (h->nlmsg_type == NLMSG_DONE)
{
@@ -262,6 +265,9 @@ int iface_enumerate(int family, void *parm, int (*callback)())
if (ifa->ifa_flags & IFA_F_DEPRECATED)
flags |= IFACE_DEPRECATED;
if (ifa->ifa_flags & IFA_F_PERMANENT)
flags |= IFACE_PERMANENT;
if (addrp && callback_ok)
if (!((*callback)(addrp, (int)(ifa->ifa_prefixlen), (int)(ifa->ifa_scope),
@@ -396,18 +402,18 @@ static int nl_async(struct nlmsghdr *h)
static void nl_newaddress(time_t now)
{
if (option_bool(OPT_CLEVERBIND) || daemon->doing_dhcp6 || daemon->doing_ra)
enumerate_interfaces();
if (option_bool(OPT_CLEVERBIND) || daemon->doing_dhcp6 || daemon->relay6 || daemon->doing_ra)
enumerate_interfaces(0);
if (option_bool(OPT_CLEVERBIND))
create_bound_listeners(0);
#ifdef HAVE_DHCP6
if (daemon->doing_dhcp6 || daemon->relay6 || daemon->doing_ra)
join_multicast(0);
if (daemon->doing_dhcp6 || daemon->doing_ra)
{
join_multicast(0);
dhcp_construct_contexts(now);
}
dhcp_construct_contexts(now);
if (daemon->doing_dhcp6)
lease_find_interfaces(now);
+151 -65
View File
@@ -99,6 +99,8 @@ int indextoname(int fd, int index, char *name)
int indextoname(int fd, int index, char *name)
{
(void)fd;
if (index == 0 || !if_indextoname(index, name))
return 0;
@@ -110,7 +112,7 @@ int indextoname(int fd, int index, char *name)
int iface_check(int family, struct all_addr *addr, char *name, int *auth)
{
struct iname *tmp;
int ret = 1;
int ret = 1, match_addr = 0;
/* Note: have to check all and not bail out early, so that we set the
"used" flags. */
@@ -132,19 +134,20 @@ int iface_check(int family, struct all_addr *addr, char *name, int *auth)
{
if (family == AF_INET &&
tmp->addr.in.sin_addr.s_addr == addr->addr.addr4.s_addr)
ret = tmp->used = 1;
ret = match_addr = tmp->used = 1;
#ifdef HAVE_IPV6
else if (family == AF_INET6 &&
IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr,
&addr->addr.addr6))
ret = tmp->used = 1;
ret = match_addr = tmp->used = 1;
#endif
}
}
for (tmp = daemon->if_except; tmp; tmp = tmp->next)
if (tmp->name && wildcard_match(tmp->name, name))
ret = 0;
if (!match_addr)
for (tmp = daemon->if_except; tmp; tmp = tmp->next)
if (tmp->name && wildcard_match(tmp->name, name))
ret = 0;
for (tmp = daemon->authinterface; tmp; tmp = tmp->next)
@@ -224,11 +227,16 @@ int label_exception(int index, int family, struct all_addr *addr)
return 0;
}
static int iface_allowed(struct irec **irecp, int if_index, char *label,
struct iface_param {
struct addrlist *spare;
int fd;
};
static int iface_allowed(struct iface_param *param, int if_index, char *label,
union mysockaddr *addr, struct in_addr netmask, int dad)
{
struct irec *iface;
int fd, mtu = 0, loopback;
int mtu = 0, loopback;
struct ifreq ifr;
int tftp_ok = !!option_bool(OPT_TFTP);
int dhcp_ok = 1;
@@ -237,39 +245,72 @@ static int iface_allowed(struct irec **irecp, int if_index, char *label,
struct iname *tmp;
#endif
/* check whether the interface IP has been added already
we call this routine multiple times. */
for (iface = *irecp; iface; iface = iface->next)
if (sockaddr_isequal(&iface->addr, addr))
{
iface->dad = dad;
return 1;
}
if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1 ||
!indextoname(fd, if_index, ifr.ifr_name) ||
ioctl(fd, SIOCGIFFLAGS, &ifr) == -1)
{
if (fd != -1)
{
int errsave = errno;
close(fd);
errno = errsave;
}
return 0;
}
if (!indextoname(param->fd, if_index, ifr.ifr_name) ||
ioctl(param->fd, SIOCGIFFLAGS, &ifr) == -1)
return 0;
loopback = ifr.ifr_flags & IFF_LOOPBACK;
if (loopback)
dhcp_ok = 0;
if (ioctl(fd, SIOCGIFMTU, &ifr) != -1)
if (ioctl(param->fd, SIOCGIFMTU, &ifr) != -1)
mtu = ifr.ifr_mtu;
close(fd);
if (!label)
label = ifr.ifr_name;
/* If we are restricting the set of interfaces to use, make
/* Update addresses from interface_names. These are a set independent
of the set we're listening on. */
#ifdef HAVE_IPV6
if (addr->sa.sa_family != AF_INET6 || !IN6_IS_ADDR_LINKLOCAL(&addr->in6.sin6_addr))
#endif
{
struct interface_name *int_name;
struct addrlist *al;
for (int_name = daemon->int_names; int_name; int_name = int_name->next)
if (strncmp(label, int_name->intr, IF_NAMESIZE) == 0)
{
if (param->spare)
{
al = param->spare;
param->spare = al->next;
}
else
al = whine_malloc(sizeof(struct addrlist));
if (al)
{
if (addr->sa.sa_family == AF_INET)
{
al->addr.addr.addr4 = addr->in.sin_addr;
al->next = int_name->addr4;
int_name->addr4 = al;
}
#ifdef HAVE_IPV6
else
{
al->addr.addr.addr6 = addr->in6.sin6_addr;
al->next = int_name->addr6;
int_name->addr6 = al;
}
#endif
}
}
}
/* check whether the interface IP has been added already
we call this routine multiple times. */
for (iface = daemon->interfaces; iface; iface = iface->next)
if (sockaddr_isequal(&iface->addr, addr))
{
iface->dad = dad;
return 1;
}
/* If we are restricting the set of interfaces to use, make
sure that loopback interfaces are in that set. */
if (daemon->if_names && loopback)
{
@@ -292,9 +333,6 @@ static int iface_allowed(struct irec **irecp, int if_index, char *label,
}
}
if (!label)
label = ifr.ifr_name;
if (addr->sa.sa_family == AF_INET &&
!iface_check(AF_INET, (struct all_addr *)&addr->in.sin_addr, label, &auth_dns))
return 1;
@@ -321,6 +359,16 @@ static int iface_allowed(struct irec **irecp, int if_index, char *label,
}
#endif
if (daemon->tftp_interfaces)
{
/* dedicated tftp interface list */
tftp_ok = 0;
for (tmp = daemon->tftp_interfaces; tmp; tmp = tmp->next)
if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
tftp_ok = 1;
}
/* add to list */
if ((iface = whine_malloc(sizeof(struct irec))))
{
@@ -336,8 +384,8 @@ static int iface_allowed(struct irec **irecp, int if_index, char *label,
if ((iface->name = whine_malloc(strlen(ifr.ifr_name)+1)))
{
strcpy(iface->name, ifr.ifr_name);
iface->next = *irecp;
*irecp = iface;
iface->next = daemon->interfaces;
daemon->interfaces = iface;
return 1;
}
free(iface);
@@ -371,7 +419,7 @@ static int iface_allowed_v6(struct in6_addr *local, int prefix,
addr.in6.sin6_port = htons(daemon->port);
addr.in6.sin6_scope_id = if_index;
return iface_allowed((struct irec **)vparam, if_index, NULL, &addr, netmask, !!(flags & IFACE_TENTATIVE));
return iface_allowed((struct iface_param *)vparam, if_index, NULL, &addr, netmask, !!(flags & IFACE_TENTATIVE));
}
#endif
@@ -389,17 +437,75 @@ static int iface_allowed_v4(struct in_addr local, int if_index, char *label,
addr.in.sin_addr = local;
addr.in.sin_port = htons(daemon->port);
return iface_allowed((struct irec **)vparam, if_index, label, &addr, netmask, 0);
return iface_allowed((struct iface_param *)vparam, if_index, label, &addr, netmask, 0);
}
int enumerate_interfaces(void)
int enumerate_interfaces(int reset)
{
static struct addrlist *spare = NULL;
static int done = 0;
struct iface_param param;
int errsave, ret = 1;
struct addrlist *addr, *tmp;
struct interface_name *intname;
/* Do this max once per select cycle - also inhibits netlink socket use
in TCP child processes. */
if (reset)
{
done = 0;
return 1;
}
if (done)
return 1;
done = 1;
if ((param.fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
return 0;
/* remove addresses stored against interface_names */
for (intname = daemon->int_names; intname; intname = intname->next)
{
for (addr = intname->addr4; addr; addr = tmp)
{
tmp = addr->next;
addr->next = spare;
spare = addr;
}
intname->addr4 = NULL;
#ifdef HAVE_IPV6
if (!iface_enumerate(AF_INET6, &daemon->interfaces, iface_allowed_v6))
return 0;
for (addr = intname->addr6; addr; addr = tmp)
{
tmp = addr->next;
addr->next = spare;
spare = addr;
}
intname->addr6 = NULL;
#endif
}
param.spare = spare;
#ifdef HAVE_IPV6
ret = iface_enumerate(AF_INET6, &param, iface_allowed_v6);
#endif
return iface_enumerate(AF_INET, &daemon->interfaces, iface_allowed_v4);
if (ret)
ret = iface_enumerate(AF_INET, &param, iface_allowed_v4);
errsave = errno;
close(param.fd);
errno = errsave;
spare = param.spare;
return ret;
}
/* set NONBLOCK bit on fd: See Stevens 16.6 */
@@ -745,7 +851,7 @@ void join_multicast(int dienow)
inet_pton(AF_INET6, ALL_RELAY_AGENTS_AND_SERVERS, &mreq.ipv6mr_multiaddr);
if (daemon->doing_dhcp6 &&
if ((daemon->doing_dhcp6 || daemon->relay6) &&
setsockopt(daemon->dhcp6fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1)
err = 1;
@@ -971,7 +1077,7 @@ void check_servers(void)
/* interface may be new since startup */
if (!option_bool(OPT_NOWILD))
enumerate_interfaces();
enumerate_interfaces(0);
for (new = daemon->servers; new; new = tmp)
{
@@ -1179,27 +1285,7 @@ int reload_servers(char *fname)
}
/* Use an IPv4 listener socket for ioctling */
struct in_addr get_ifaddr(char *intr)
{
struct listener *l;
struct ifreq ifr;
struct sockaddr_in ret;
ret.sin_addr.s_addr = -1;
for (l = daemon->listeners;
l && (l->family != AF_INET || l->fd == -1);
l = l->next);
strncpy(ifr.ifr_name, intr, IF_NAMESIZE);
ifr.ifr_addr.sa_family = AF_INET;
if (l && ioctl(l->fd, SIOCGIFADDR, &ifr) != -1)
memcpy(&ret, &ifr.ifr_addr, sizeof(ret));
return ret.sin_addr;
}
+87 -28
View File
@@ -132,6 +132,8 @@ struct myoption {
#ifdef OPTION6_PREFIX_CLASS
#define LOPT_PREF_CLSS 321
#endif
#define LOPT_FAST_RA 322
#define LOPT_RELAY 323
#ifdef HAVE_GETOPT_LONG
static const struct option opts[] =
@@ -207,7 +209,7 @@ static const struct myoption opts[] =
{ "dns-forward-max", 1, 0, '0' },
{ "clear-on-reload", 0, 0, LOPT_RELOAD },
{ "dhcp-ignore-names", 2, 0, LOPT_NO_NAMES },
{ "enable-tftp", 0, 0, LOPT_TFTP },
{ "enable-tftp", 2, 0, LOPT_TFTP },
{ "tftp-secure", 0, 0, LOPT_SECURE },
{ "tftp-unique-root", 0, 0, LOPT_APREF },
{ "tftp-root", 1, 0, LOPT_PREFIX },
@@ -269,6 +271,8 @@ static const struct myoption opts[] =
#ifdef OPTION6_PREFIX_CLASS
{ "dhcp-prefix-class", 1, 0, LOPT_PREF_CLSS },
#endif
{ "force-fast-ra", 0, 0, LOPT_FAST_RA },
{ "dhcp-relay", 1, 0, LOPT_RELAY },
{ NULL, 0, 0, 0 }
};
@@ -366,7 +370,7 @@ static struct {
{ LOPT_RELOAD, OPT_RELOAD, NULL, gettext_noop("Clear DNS cache when reloading %s."), RESOLVFILE },
{ LOPT_NO_NAMES, ARG_DUP, "[=tag:<tag>]...", gettext_noop("Ignore hostnames provided by DHCP clients."), NULL },
{ LOPT_OVERRIDE, OPT_NO_OVERRIDE, NULL, gettext_noop("Do NOT reuse filename and server fields for extra DHCP options."), NULL },
{ LOPT_TFTP, OPT_TFTP, NULL, gettext_noop("Enable integrated read-only TFTP server."), NULL },
{ LOPT_TFTP, ARG_DUP, "[=<intr>[,<intr>]]", gettext_noop("Enable integrated read-only TFTP server."), NULL },
{ LOPT_PREFIX, ARG_DUP, "<dir>[,<iface>]", gettext_noop("Export files by TFTP only from the specified subtree."), NULL },
{ LOPT_APREF, OPT_TFTP_APREF, NULL, gettext_noop("Add client IP address to tftp-root."), NULL },
{ LOPT_SECURE, OPT_TFTP_SECURE, NULL, gettext_noop("Allow access only to files owned by the user running dnsmasq."), NULL },
@@ -387,6 +391,7 @@ static struct {
{ LOPT_DHCP_FQDN, OPT_DHCP_FQDN, NULL, gettext_noop("Use only fully qualified domain names for DHCP clients."), NULL },
{ LOPT_GEN_NAMES, ARG_DUP, "[=tag:<tag>]", gettext_noop("Generate hostnames based on MAC address for nameless clients."), NULL},
{ LOPT_PROXY, ARG_DUP, "[=<ipaddr>]...", gettext_noop("Use these DHCP relays as full proxies."), NULL },
{ LOPT_RELAY, ARG_DUP, "<local-addr>,<server>[,<interface>]", gettext_noop("Relay DHCP requests to a remote server"), NULL},
{ LOPT_CNAME, ARG_DUP, "<alias>,<target>", gettext_noop("Specify alias name for LOCAL DNS name."), NULL },
{ LOPT_PXE_PROMT, ARG_DUP, "<prompt>,[<timeout>]", gettext_noop("Prompt to send to PXE clients."), NULL },
{ LOPT_PXE_SERV, ARG_DUP, "<service>", gettext_noop("Boot service for PXE menu."), NULL },
@@ -397,6 +402,7 @@ 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_FAST_RA, OPT_FAST_RA, NULL, gettext_noop("Always send frequent router-advertisements"), 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 },
{ LOPT_RR, ARG_DUP, "<name>,<RR-number>,[<data>]", gettext_noop("Specify arbitrary DNS resource record"), NULL },
@@ -1637,8 +1643,6 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
if (inet_pton(AF_INET, arg, &subnet->addr4))
{
if ((prefixlen & 0x07) != 0 || prefixlen > 24)
ret_err(_("bad prefix"));
subnet->prefixlen = (prefixlen == 0) ? 24 : prefixlen;
subnet->is6 = 0;
}
@@ -1657,7 +1661,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
case LOPT_AUTHSOA: /* --auth-soa */
comma = split(arg);
atoi_check(arg, (int *)&daemon->soa_sn);
daemon->soa_sn = (u32)atoi(arg);
if (comma)
{
char *cp;
@@ -1672,17 +1676,17 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
{
arg = comma;
comma = split(arg);
atoi_check(arg, (int *)&daemon->soa_refresh);
daemon->soa_refresh = (u32)atoi(arg);
if (comma)
{
arg = comma;
comma = split(arg);
atoi_check(arg, (int *)&daemon->soa_retry);
daemon->soa_retry = (u32)atoi(arg);
if (comma)
{
arg = comma;
comma = split(arg);
atoi_check(arg, (int *)&daemon->soa_expiry);
daemon->soa_expiry = (u32)atoi(arg);
}
}
}
@@ -1903,6 +1907,12 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
} while (arg);
break;
case LOPT_TFTP: /* --enable-tftp */
set_option_bool(OPT_TFTP);
if (!arg)
break;
/* fall through */
case 'I': /* --except-interface */
case '2': /* --no-dhcp-interface */
do {
@@ -1914,6 +1924,11 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
new->next = daemon->if_except;
daemon->if_except = new;
}
else if (option == LOPT_TFTP)
{
new->next = daemon->tftp_interfaces;
daemon->tftp_interfaces = new;
}
else
{
new->next = daemon->dhcp_except;
@@ -2389,11 +2404,9 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
{
new->prefix = 64; /* default */
new->end6 = new->start6;
/* dhcp-range=:: enables DHCP stateless on any interface */
if (IN6_IS_ADDR_UNSPECIFIED(&new->start6))
new->prefix = 0;
new->next = daemon->dhcp6;
daemon->dhcp6 = new;
for (leasepos = 1; leasepos < k; leasepos++)
{
if (strcmp(a[leasepos], "static") == 0)
@@ -2411,13 +2424,15 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
new->template_interface = opt_string_alloc(a[leasepos] + 12);
new->flags |= CONTEXT_TEMPLATE;
}
else if (strstr(a[leasepos], "constructor-noauth:") == a[leasepos])
{
new->template_interface = opt_string_alloc(a[leasepos] + 19);
new->flags |= CONTEXT_TEMPLATE | CONTEXT_NOAUTH;
}
else
break;
}
new->next = daemon->dhcp6;
daemon->dhcp6 = new;
/* bare integer < 128 is prefix value */
if (leasepos < k)
{
@@ -2429,20 +2444,35 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
{
new->prefix = pref;
leasepos++;
if (new->prefix != 64)
{
if ((new->flags & (CONTEXT_RA_ONLY | CONTEXT_RA_NAME | CONTEXT_RA_STATELESS)))
ret_err(_("prefix must be exactly 64 for RA subnets"));
else if (new->template_interface)
ret_err(_("prefix must be exactly 64 for subnet constructors"));
}
if (new->prefix < 64)
ret_err(_("prefix must be at least 64"));
}
}
if (new->prefix != 64)
{
if ((new->flags & (CONTEXT_RA_ONLY | CONTEXT_RA_NAME | CONTEXT_RA_STATELESS)))
ret_err(_("prefix length must be exactly 64 for RA subnets"));
else if (new->flags & CONTEXT_TEMPLATE)
ret_err(_("prefix length must be exactly 64 for subnet constructors"));
}
if (new->prefix < 64)
ret_err(_("prefix length must be at least 64"));
if (!is_same_net6(&new->start6, &new->end6, new->prefix))
ret_err(_("inconsistent DHCPv6 range"));
/* dhcp-range=:: enables DHCP stateless on any interface */
if (IN6_IS_ADDR_UNSPECIFIED(&new->start6) && !(new->flags & CONTEXT_TEMPLATE))
new->prefix = 0;
if (new->flags & CONTEXT_TEMPLATE)
{
struct in6_addr zero;
memset(&zero, 0, sizeof(zero));
if (!is_same_net6(&zero, &new->start6, new->prefix))
ret_err(_("prefix must be zero with \"constructor:\" argument"));
}
if (addr6part(&new->start6) > addr6part(&new->end6))
{
struct in6_addr tmp = new->start6;
@@ -2510,7 +2540,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
case 'G': /* --dhcp-host */
{
int j, k = 0;
char *a[6] = { NULL, NULL, NULL, NULL, NULL, NULL };
char *a[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
struct dhcp_config *new;
struct in_addr in;
@@ -2522,7 +2552,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
new->netid = NULL;
if ((a[0] = arg))
for (k = 1; k < 6; k++)
for (k = 1; k < 7; k++)
if (!(a[k] = split(a[k-1])))
break;
@@ -3151,6 +3181,31 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
arg = comma;
}
break;
case LOPT_RELAY: /* --dhcp-relay */
{
struct dhcp_relay *new = opt_malloc(sizeof(struct dhcp_relay));
comma = split(arg);
new->interface = opt_string_alloc(split(comma));
new->iface_index = 0;
if (inet_pton(AF_INET, arg, &new->local) && inet_pton(AF_INET, comma, &new->server))
{
new->next = daemon->relay4;
daemon->relay4 = new;
}
#ifdef HAVE_DHCP6
else if (inet_pton(AF_INET6, arg, &new->local) && inet_pton(AF_INET6, comma, &new->server))
{
new->next = daemon->relay6;
daemon->relay6 = new;
}
#endif
else
ret_err(_("Bad dhcp-relay"));
break;
}
#endif
#ifdef HAVE_DHCP6
@@ -3215,6 +3270,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
new = opt_malloc(sizeof(struct interface_name));
new->next = NULL;
new->addr4 = NULL;
#ifdef HAVE_IPV6
new->addr6 = NULL;
#endif
/* Add to the end of the list, so that first name
of an interface is used for PTR lookups. */
for (up = &daemon->int_names; *up; up = &((*up)->next));
+2 -2
View File
@@ -70,9 +70,9 @@ void *put_opt6(void *data, size_t len)
{
void *p;
if ((p = expand(len)))
if ((p = expand(len)) && data)
memcpy(p, data, len);
return p;
}
+97 -27
View File
@@ -47,6 +47,7 @@ static int iface_search(struct in6_addr *local, int prefix,
int scope, int if_index, int flags,
int prefered, int valid, void *vparam);
static int add_lla(int index, unsigned int type, char *mac, size_t maclen, void *parm);
static void new_timeout(struct dhcp_context *context, time_t now);
static int hop_limit;
@@ -187,9 +188,8 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de
{
struct ra_packet *ra;
struct ra_param parm;
struct ifreq ifr;
struct sockaddr_in6 addr;
struct dhcp_context *context;
struct dhcp_context *context, *tmp, **up;
struct dhcp_netid iface_id;
struct dhcp_opt *opt_cfg;
int done_dns = 0;
@@ -228,12 +228,66 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de
context->netid.next = &context->netid;
}
if (!iface_enumerate(AF_INET6, &parm, add_prefixes) ||
!parm.found_context)
if (!iface_enumerate(AF_INET6, &parm, add_prefixes))
return;
strncpy(ifr.ifr_name, iface_name, IF_NAMESIZE);
/* Look for constructed contexts associated with addresses which have gone,
and advertise them with preferred_time == 0 RFC 6204 4.3 L-13 */
for (up = &daemon->dhcp6, context = daemon->dhcp6; context; context = tmp)
{
tmp = context->next;
if (context->if_index == iface && (context->flags & CONTEXT_OLD))
{
unsigned int old = difftime(now, context->address_lost_time);
if (old > context->saved_valid)
{
/* We've advertised this enough, time to go */
*up = context->next;
free(context);
}
else
{
struct prefix_opt *opt;
struct in6_addr local = context->start6;
int do_slaac = 0;
parm.found_context = 1;
/* zero net part of address */
setaddr6part(&local, addr6part(&local) & ~((context->prefix == 64) ? (u64)-1LL : (1LLU << (128 - context->prefix)) - 1LLU));
if ((context->flags &
(CONTEXT_RA_ONLY | CONTEXT_RA_NAME | CONTEXT_RA_STATELESS)))
do_slaac = 1;
if ((opt = expand(sizeof(struct prefix_opt))))
{
opt->type = ICMP6_OPT_PREFIX;
opt->len = 4;
opt->prefix_len = context->prefix;
/* autonomous only if we're not doing dhcp, always set "on-link" */
opt->flags = do_slaac ? 0xC0 : 0x80;
opt->valid_lifetime = htonl(context->saved_valid - old);
opt->preferred_lifetime = htonl(0);
opt->reserved = 0;
opt->prefix = local;
inet_ntop(AF_INET6, &local, daemon->addrbuff, ADDRSTRLEN);
my_syslog(MS_DHCP | LOG_INFO, "RTR-ADVERT(%s) %s old prefix", iface_name, daemon->addrbuff);
}
up = &context->next;
}
}
else
up = &context->next;
}
if (!parm.found_context)
return;
#ifdef HAVE_LINUX_NETWORK
/* Note that IPv6 MTU is not necessarilly the same as the IPv4 MTU
available from SIOCGIFMTU */
@@ -291,7 +345,7 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de
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_long(RA_INTERVAL * 2); /* lifetime - twice RA retransmit */
put_opt6(opt_cfg->val, opt_cfg->len);
/* pad */
@@ -361,11 +415,13 @@ static int add_prefixes(struct in6_addr *local, int prefix,
struct dhcp_context *context;
for (context = daemon->dhcp6; context; context = context->next)
if (!(context->flags & CONTEXT_TEMPLATE) &&
if (!(context->flags & (CONTEXT_TEMPLATE | CONTEXT_OLD)) &&
prefix == context->prefix &&
is_same_net6(local, &context->start6, prefix) &&
is_same_net6(local, &context->end6, prefix))
{
context->saved_valid = valid;
if ((context->flags &
(CONTEXT_RA_ONLY | CONTEXT_RA_NAME | CONTEXT_RA_STATELESS)))
{
@@ -386,12 +442,12 @@ static int add_prefixes(struct in6_addr *local, int prefix,
param->other = 1;
}
/* find floor time, don't reduce below RA interval. */
/* find floor time, don't reduce below 3 * RA interval. */
if (time > context->lease_time)
{
time = context->lease_time;
if (time < ((unsigned int)RA_INTERVAL))
time = RA_INTERVAL;
if (time < ((unsigned int)(3 * RA_INTERVAL)))
time = 3 * RA_INTERVAL;
}
if (context->flags & CONTEXT_DEPRECATE)
@@ -466,6 +522,7 @@ static int add_prefixes(struct in6_addr *local, int prefix,
inet_ntop(AF_INET6, local, daemon->addrbuff, ADDRSTRLEN);
my_syslog(MS_DHCP | LOG_INFO, "RTR-ADVERT(%s) %s", param->if_name, daemon->addrbuff);
}
}
}
}
@@ -520,16 +577,25 @@ time_t periodic_ra(time_t now)
if (!context)
break;
/* There's a context overdue, but we can't find an interface
associated with it, because it's for a subnet we dont
have an interface on. Probably we're doing DHCP on
a remote subnet via a relay. Zero the timer, since we won't
ever be able to send ra's and satistfy it. */
if (iface_enumerate(AF_INET6, &param, iface_search))
if ((context->flags & CONTEXT_OLD) && context->if_index != 0)
{
/* A context for an old address. We'll not find the interface by
looking for addresses, but we know it anyway, as long as we
sent at least one RA whilst the address was current. */
param.iface = context->if_index;
new_timeout(context, now);
}
else if (iface_enumerate(AF_INET6, &param, iface_search))
/* There's a context overdue, but we can't find an interface
associated with it, because it's for a subnet we dont
have an interface on. Probably we're doing DHCP on
a remote subnet via a relay. Zero the timer, since we won't
ever be able to send ra's and satistfy it. */
context->ra_time = 0;
else if (param.iface != 0 &&
indextoname(daemon->icmp6fd, param.iface, interface) &&
iface_check(AF_LOCAL, NULL, interface, NULL))
if (param.iface != 0 &&
indextoname(daemon->icmp6fd, param.iface, interface) &&
iface_check(AF_LOCAL, NULL, interface, NULL))
{
struct iname *tmp;
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
@@ -554,7 +620,7 @@ static int iface_search(struct in6_addr *local, int prefix,
(void)valid;
for (context = daemon->dhcp6; context; context = context->next)
if (!(context->flags & CONTEXT_TEMPLATE) &&
if (!(context->flags & (CONTEXT_TEMPLATE | CONTEXT_OLD)) &&
prefix == context->prefix &&
is_same_net6(local, &context->start6, prefix) &&
is_same_net6(local, &context->end6, prefix) &&
@@ -568,12 +634,7 @@ static int iface_search(struct in6_addr *local, int prefix,
if (!(flags & IFACE_TENTATIVE))
param->iface = if_index;
if (difftime(param->now, context->ra_short_period_start) < 60.0)
/* range 5 - 20 */
context->ra_time = param->now + 5 + (rand16()/4400);
else
/* range 3/4 - 1 times RA_INTERVAL */
context->ra_time = param->now + (3 * RA_INTERVAL)/4 + ((RA_INTERVAL * (unsigned int)rand16()) >> 18);
new_timeout(context, param->now);
/* zero timers for other contexts on the same subnet, so they don't timeout
independently */
@@ -588,6 +649,15 @@ static int iface_search(struct in6_addr *local, int prefix,
return 1; /* keep searching */
}
static void new_timeout(struct dhcp_context *context, time_t now)
{
if (difftime(now, context->ra_short_period_start) < 60.0 || option_bool(OPT_FAST_RA))
/* range 5 - 20 */
context->ra_time = now + 5 + (rand16()/4400);
else
/* range 3/4 - 1 times RA_INTERVAL */
context->ra_time = now + (3 * RA_INTERVAL)/4 + ((RA_INTERVAL * (unsigned int)rand16()) >> 18);
}
#endif
+54 -23
View File
@@ -1450,19 +1450,42 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if (is_arpa == F_IPV4)
for (intr = daemon->int_names; intr; intr = intr->next)
{
if (addr.addr.addr4.s_addr == get_ifaddr(intr->intr).s_addr)
struct addrlist *addrlist;
for (addrlist = intr->addr4; addrlist; addrlist = addrlist->next)
if (addr.addr.addr4.s_addr == addrlist->addr.addr.addr4.s_addr)
break;
if (addrlist)
break;
else
while (intr->next && strcmp(intr->intr, intr->next->intr) == 0)
intr = intr->next;
}
#ifdef HAVE_IPV6
else if (is_arpa == F_IPV6)
for (intr = daemon->int_names; intr; intr = intr->next)
{
struct addrlist *addrlist;
for (addrlist = intr->addr6; addrlist; addrlist = addrlist->next)
if (IN6_ARE_ADDR_EQUAL(&addr.addr.addr6, &addrlist->addr.addr.addr6))
break;
if (addrlist)
break;
else
while (intr->next && strcmp(intr->intr, intr->next->intr) == 0)
intr = intr->next;
}
#endif
if (intr)
{
ans = 1;
if (!dryrun)
{
log_query(F_IPV4 | F_REVERSE | F_CONFIG, intr->name, &addr, NULL);
log_query(is_arpa | F_REVERSE | F_CONFIG, intr->name, &addr, NULL);
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
daemon->local_ttl, NULL,
T_PTR, C_IN, "d", intr->name))
@@ -1546,7 +1569,8 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
for (flag = F_IPV4; flag; flag = (flag == F_IPV4) ? F_IPV6 : 0)
{
unsigned short type = T_A;
struct interface_name *intr;
if (flag == F_IPV6)
#ifdef HAVE_IPV6
type = T_AAAA;
@@ -1595,31 +1619,38 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
}
/* interface name stuff */
if (qtype == T_A)
for (intr = daemon->int_names; intr; intr = intr->next)
if (hostname_isequal(name, intr->name))
break;
if (intr)
{
struct interface_name *intr;
struct addrlist *addrlist;
for (intr = daemon->int_names; intr; intr = intr->next)
if (hostname_isequal(name, intr->name))
break;
enumerate_interfaces(0);
if (intr)
addrlist = intr->addr4;
#ifdef HAVE_IPV6
if (type == T_AAAA)
addrlist = intr->addr6;
#endif
ans = 1;
if (!dryrun)
{
ans = 1;
if (!dryrun)
{
if ((addr.addr.addr4 = get_ifaddr(intr->intr)).s_addr == (in_addr_t) -1)
log_query(F_FORWARD | F_CONFIG | F_IPV4 | F_NEG, name, NULL, NULL);
else
{
log_query(F_FORWARD | F_CONFIG | F_IPV4, name, &addr, NULL);
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
daemon->local_ttl, NULL, type, C_IN, "4", &addr))
anscount++;
}
}
continue;
if (!addrlist)
log_query(F_FORWARD | F_CONFIG | flag | F_NEG, name, NULL, NULL);
else
for (; addrlist; addrlist = addrlist->next)
{
log_query(F_FORWARD | F_CONFIG | flag, name, &addrlist->addr, NULL);
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
daemon->local_ttl, NULL, type, C_IN,
type == T_A ? "4" : "6", &addrlist->addr))
anscount++;
}
}
continue;
}
cname_restart:
+24 -11
View File
@@ -615,7 +615,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
return message ? 0 : dhcp_packet_size(mess, agent_id, real_end);
}
if ((opt = option_find(mess, sz, OPTION_CLIENT_FQDN, 4)))
if ((opt = option_find(mess, sz, OPTION_CLIENT_FQDN, 3)))
{
/* http://tools.ietf.org/wg/dhc/draft-ietf-dhc-fqdn-option/draft-ietf-dhc-fqdn-option-10.txt */
int len = option_len(opt);
@@ -645,7 +645,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
}
if (fqdn_flags & 0x04)
while (*op != 0 && ((op + (*op) + 1) - pp) < len)
while (*op != 0 && ((op + (*op)) - pp) < len)
{
memcpy(pq, op+1, *op);
pq += *op;
@@ -848,8 +848,16 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
if (tmp)
{
struct dhcp_boot *boot = find_boot(tagif_netid);
struct dhcp_boot *boot;
if (tmp->netid.net)
{
tmp->netid.next = netid;
tagif_netid = run_tag_if(&tmp->netid);
}
boot = find_boot(tagif_netid);
mess->yiaddr.s_addr = 0;
if (mess_type == DHCPDISCOVER || mess->ciaddr.s_addr == 0)
{
@@ -1072,7 +1080,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
Have to set override to make sure we echo back the correct server-id */
struct irec *intr;
enumerate_interfaces();
enumerate_interfaces(0);
for (intr = daemon->interfaces; intr; intr = intr->next)
if (intr->addr.sa.sa_family == AF_INET &&
@@ -1385,8 +1393,8 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
lease->hostname)
hostname = lease->hostname;
if (!hostname && (hostname = host_from_dns(mess->ciaddr)))
domain = get_domain(mess->ciaddr);
if (!hostname)
hostname = host_from_dns(mess->ciaddr);
if (context && context->netid.net)
{
@@ -1833,7 +1841,8 @@ static int do_opt(struct dhcp_opt *opt, unsigned char *p, struct dhcp_context *c
}
}
else
memcpy(p, opt->val, len);
/* empty string may be extended to "\0" by null_term */
memcpy(p, opt->val ? opt->val : (unsigned char *)"", len);
}
return len;
}
@@ -2289,7 +2298,9 @@ static void do_options(struct dhcp_context *context,
if (domain)
len += strlen(domain) + 1;
else if (fqdn_flags & 0x04)
len--;
if ((p = free_space(mess, end, OPTION_CLIENT_FQDN, len)))
{
*(p++) = fqdn_flags & 0x0f; /* MBZ bits to zero */
@@ -2300,8 +2311,10 @@ static void do_options(struct dhcp_context *context,
{
p = do_rfc1035_name(p, hostname);
if (domain)
p = do_rfc1035_name(p, domain);
*p++ = 0;
{
p = do_rfc1035_name(p, domain);
*p++ = 0;
}
}
else
{
+249 -49
View File
@@ -54,13 +54,13 @@ static struct prefix_class *prefix_class_from_context(struct dhcp_context *conte
static void mark_context_used(struct state *state, struct dhcp_context *context, struct in6_addr *addr);
static void mark_config_used(struct dhcp_context *context, struct in6_addr *addr);
static int check_address(struct state *state, struct in6_addr *addr);
static void add_address(struct state *state, struct dhcp_context *context, unsigned int lease_time, unsigned int requested_time,
static void add_address(struct state *state, struct dhcp_context *context, unsigned int lease_time, void *ia_option,
unsigned int *min_time, struct in6_addr *addr, int update_lease, time_t now);
static void update_leases(struct state *state, struct dhcp_context *context, struct in6_addr *addr, unsigned int lease_time, time_t now);
static int add_local_addrs(struct dhcp_context *context);
struct dhcp_netid *add_options(struct state *state, struct in6_addr *fallback, struct dhcp_context *context);
static struct dhcp_netid *add_options(struct state *state, struct in6_addr *fallback, struct dhcp_context *context, int do_refresh);
static void calculate_times(struct dhcp_context *context, unsigned int *min_time, unsigned int *valid_timep,
unsigned int *preferred_timep, unsigned int lease_time, unsigned int requested_time);
unsigned int *preferred_timep, unsigned int lease_time);
#define opt6_len(opt) ((int)(opt6_uint(opt, -2, 2)))
#define opt6_type(opt) (opt6_uint(opt, -4, 2))
@@ -120,7 +120,7 @@ static int dhcp6_maybe_relay(struct in6_addr *link_address, struct dhcp_netid **
!IN6_IS_ADDR_MULTICAST(link_address))
for (c = daemon->dhcp6; c; c = c->next)
if ((c->flags & CONTEXT_DHCP) &&
!(c->flags & CONTEXT_TEMPLATE) &&
!(c->flags & (CONTEXT_TEMPLATE | CONTEXT_OLD)) &&
is_same_net6(link_address, &c->start6, c->prefix) &&
is_same_net6(link_address, &c->end6, c->prefix))
{
@@ -155,7 +155,8 @@ static int dhcp6_maybe_relay(struct in6_addr *link_address, struct dhcp_netid **
return 0;
/* copy header stuff into reply message and set type to reply */
outmsgtypep = put_opt6(inbuff, 34);
if (!(outmsgtypep = put_opt6(inbuff, 34)))
return 0;
*outmsgtypep = DHCP6RELAYREPL;
/* look for relay options and set tags if found. */
@@ -252,7 +253,8 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
state.tags = &v6_id;
/* copy over transaction-id, and save pointer to message type */
outmsgtypep = put_opt6(inbuff, 4);
if (!(outmsgtypep = put_opt6(inbuff, 4)))
return 0;
start_opts = save_counter(-1);
state.xid = outmsgtypep[3] | outmsgtypep[2] << 8 | outmsgtypep[1] << 16;
@@ -574,7 +576,7 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
/* set unless we're sending a particular prefix-class, when we
want only dhcp-ranges with the correct tags set and not those without any tags. */
int plain_range = 1;
u32 lease_time, requested_time;
u32 lease_time;
struct dhcp_lease *ltmp;
struct in6_addr *req_addr;
struct in6_addr addr;
@@ -645,8 +647,7 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
for (ia_counter = 0; ia_option; ia_counter++, ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))
{
req_addr = opt6_ptr(ia_option, 0);
requested_time = opt6_uint(ia_option, 16, 4);
if ((c = address6_valid(context, req_addr, solicit_tags, plain_range)))
{
lease_time = c->lease_time;
@@ -670,7 +671,7 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
if (dump_all_prefix_classes && state.ia_type == OPTION6_IA_NA)
state.send_prefix_class = prefix_class_from_context(c);
#endif
add_address(&state, c, lease_time, requested_time, &min_time, req_addr, rapid_commit != NULL, now);
add_address(&state, c, lease_time, ia_option, &min_time, req_addr, rapid_commit != NULL, now);
mark_context_used(&state, context, req_addr);
get_context_tag(&state, c);
address_assigned = 1;
@@ -694,7 +695,7 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
if (dump_all_prefix_classes && state.ia_type == OPTION6_IA_NA)
state.send_prefix_class = prefix_class_from_context(c);
#endif
add_address(&state, c, lease_time, lease_time, &min_time, &addr, rapid_commit != NULL, now);
add_address(&state, c, lease_time, NULL, &min_time, &addr, rapid_commit != NULL, now);
mark_context_used(&state, context, &addr);
get_context_tag(&state, c);
address_assigned = 1;
@@ -711,7 +712,7 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
if (dump_all_prefix_classes && state.ia_type == OPTION6_IA_NA)
state.send_prefix_class = prefix_class_from_context(c);
#endif
add_address(&state, c, c->lease_time, c->lease_time, &min_time, req_addr, rapid_commit != NULL, now);
add_address(&state, c, c->lease_time, NULL, &min_time, req_addr, rapid_commit != NULL, now);
mark_context_used(&state, context, req_addr);
get_context_tag(&state, c);
address_assigned = 1;
@@ -725,7 +726,7 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
if (dump_all_prefix_classes && state.ia_type == OPTION6_IA_NA)
state.send_prefix_class = prefix_class_from_context(c);
#endif
add_address(&state, c, c->lease_time, c->lease_time, &min_time, &addr, rapid_commit != NULL, now);
add_address(&state, c, c->lease_time, NULL, &min_time, &addr, rapid_commit != NULL, now);
mark_context_used(&state, context, &addr);
get_context_tag(&state, c);
address_assigned = 1;
@@ -747,7 +748,7 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
o = new_opt6(OPTION6_PREFERENCE);
put_opt6_char(option_bool(OPT_AUTHORITATIVE) ? 255 : 0);
end_opt6(o);
tagif = add_options(&state, fallback, context);
tagif = add_options(&state, fallback, context, 0);
}
else
{
@@ -788,7 +789,6 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
for (; ia_option; ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))
{
struct in6_addr *req_addr = opt6_ptr(ia_option, 0);
u32 requested_time = opt6_uint(ia_option, 16, 4);
struct dhcp_context *dynamic, *c;
unsigned int lease_time;
struct in6_addr addr;
@@ -829,7 +829,7 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
if (dump_all_prefix_classes && state.ia_type == OPTION6_IA_NA)
state.send_prefix_class = prefix_class_from_context(c);
#endif
add_address(&state, dynamic, lease_time, requested_time, &min_time, req_addr, 1, now);
add_address(&state, dynamic, lease_time, ia_option, &min_time, req_addr, 1, now);
get_context_tag(&state, dynamic);
address_assigned = 1;
}
@@ -865,7 +865,7 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
log6_packet(&state, "DHCPREPLY", NULL, _("no addresses available"));
}
tagif = add_options(&state, fallback, context);
tagif = add_options(&state, fallback, context, 0);
break;
}
@@ -893,9 +893,8 @@ 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 requested_time = opt6_uint(ia_option, 16, 4);
unsigned int preferred_time = 0; /* in case renewal inappropriate */
unsigned int valid_time = 0;
unsigned int preferred_time = opt6_uint(ia_option, 16, 4);
unsigned int valid_time = opt6_uint(ia_option, 20, 4);
char *message = NULL;
struct dhcp_context *this_context;
@@ -915,6 +914,8 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
put_opt6_short(DHCP6NOBINDING);
put_opt6_string(_("no binding found"));
end_opt6(o1);
preferred_time = valid_time = 0;
break;
}
@@ -932,7 +933,7 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
else
lease_time = this_context->lease_time;
calculate_times(this_context, &min_time, &valid_time, &preferred_time, lease_time, requested_time);
calculate_times(this_context, &min_time, &valid_time, &preferred_time, lease_time);
lease_set_expires(lease, valid_time, now);
if (state.ia_type == OPTION6_IA_NA && state.hostname)
@@ -949,8 +950,11 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
message = _("deprecated");
}
else
message = _("address invalid");
{
preferred_time = valid_time = 0;
message = _("address invalid");
}
log6_packet(&state, "DHCPREPLY", req_addr, message);
o1 = new_opt6(OPTION6_IAADDR);
@@ -964,7 +968,7 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
end_opt6(o);
}
tagif = add_options(&state, fallback, context);
tagif = add_options(&state, fallback, context, 0);
break;
}
@@ -1015,11 +1019,20 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
context->netid.next = NULL;
state.context_tags = &context->netid;
}
/* Similarly, we can't determine domain from address, but if the FQDN is
given in --dhcp-host, we can use that, and failing that we can use the
unqualified configured domain, if any. */
if (state.hostname_auth)
state.send_domain = state.domain;
else
state.send_domain = get_domain6(NULL);
log6_packet(&state, "DHCPINFORMATION-REQUEST", NULL, ignore ? _("ignored") : state.hostname);
if (ignore)
return 0;
*outmsgtypep = DHCP6REPLY;
tagif = add_options(&state, fallback, context);
tagif = add_options(&state, fallback, context, 1);
break;
}
@@ -1169,13 +1182,16 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
}
struct dhcp_netid *add_options(struct state *state, struct in6_addr *fallback, struct dhcp_context *context)
static struct dhcp_netid *add_options(struct state *state,
struct in6_addr *fallback,
struct dhcp_context *context,
int do_refresh)
{
void *oro;
/* filter options based on tags, those we want get DHOPT_TAGOK bit set */
struct dhcp_netid *tagif = option_filter(state->tags, state->context_tags, daemon->dhcp_opts6);
struct dhcp_opt *opt_cfg;
int done_dns = 0, do_encap = 0;
int done_dns = 0, done_refresh = !do_refresh, do_encap = 0;
int i, o, o1;
oro = opt6_find(state->packet_options, state->end, OPTION6_ORO, 0);
@@ -1203,6 +1219,9 @@ struct dhcp_netid *add_options(struct state *state, struct in6_addr *fallback, s
if (opt_cfg->len == 0)
continue;
}
if (opt_cfg->opt == OPTION6_REFRESH_TIME)
done_refresh = 1;
o = new_opt6(opt_cfg->opt);
if (opt_cfg->flags & DHOPT_ADDR6)
@@ -1226,15 +1245,35 @@ struct dhcp_netid *add_options(struct state *state, struct in6_addr *fallback, s
end_opt6(o);
}
if (daemon->port == NAMESERVER_PORT && !done_dns &&
(!IN6_IS_ADDR_UNSPECIFIED(&context->local6) ||
!IN6_IS_ADDR_UNSPECIFIED(fallback)))
if (daemon->port == NAMESERVER_PORT && !done_dns)
{
o = new_opt6(OPTION6_DNS_SERVER);
if (!add_local_addrs(context))
put_opt6(fallback, IN6ADDRSZ);
end_opt6(o);
}
if (context && !done_refresh)
{
struct dhcp_context *c;
unsigned int lease_time = 0xffffffff;
/* Find the smallest lease tie of all contexts,
subjext to the RFC-4242 stipulation that this must not
be less than 600. */
for (c = context; c; c = c->next)
if (c->lease_time < lease_time)
{
if (c->lease_time < 600)
lease_time = 600;
else
lease_time = c->lease_time;
}
o = new_opt6(OPTION6_REFRESH_TIME);
put_opt6_long(lease_time);
end_opt6(o);
}
/* handle vendor-identifying vendor-encapsulated options,
dhcp-option = vi-encap:13,17,....... */
@@ -1299,16 +1338,18 @@ struct dhcp_netid *add_options(struct state *state, struct in6_addr *fallback, s
size_t len = strlen(state->hostname);
if (state->send_domain)
len += strlen(state->send_domain) + 1;
len += strlen(state->send_domain) + 2;
o = new_opt6(OPTION6_FQDN);
if ((p = expand(len + 3)))
if ((p = expand(len + 2)))
{
*(p++) = state->fqdn_flags;
p = do_rfc1035_name(p, state->hostname);
if (state->send_domain)
p = do_rfc1035_name(p, state->send_domain);
*p = 0;
{
p = do_rfc1035_name(p, state->send_domain);
*p = 0;
}
}
end_opt6(o);
}
@@ -1463,14 +1504,21 @@ static void end_ia(int t1cntr, unsigned int min_time, int do_fuzz)
}
}
static void add_address(struct state *state, struct dhcp_context *context, unsigned int lease_time, unsigned int requested_time,
static void add_address(struct state *state, struct dhcp_context *context, unsigned int lease_time, void *ia_option,
unsigned int *min_time, struct in6_addr *addr, int do_update, time_t now)
{
unsigned int valid_time, preferred_time;
unsigned int valid_time = 0, preferred_time = 0;
int o = new_opt6(OPTION6_IAADDR);
struct dhcp_lease *lease;
calculate_times(context, min_time, &valid_time, &preferred_time, lease_time, requested_time);
/* get client requested times */
if (ia_option)
{
preferred_time = opt6_uint(ia_option, 16, 4);
valid_time = opt6_uint(ia_option, 20, 4);
}
calculate_times(context, min_time, &valid_time, &preferred_time, lease_time);
put_opt6(addr, sizeof(*addr));
put_opt6_long(preferred_time);
@@ -1554,22 +1602,60 @@ static int check_address(struct state *state, struct in6_addr *addr)
return 1;
}
/* Calculate valid and preferred times to send in leases/renewals.
Inputs are:
*valid_timep, *preferred_timep - requested times from IAADDR options.
context->valid, context->preferred - times associated with subnet address on local interface.
context->flags | CONTEXT_DEPRECATE - "deprecated" flag in dhcp-range.
lease_time - configured time for context for individual client.
*min_time - smallest valid time sent so far.
Outputs are :
*valid_timep, *preferred_timep - times to be send in IAADDR option.
*min_time - smallest valid time sent so far, to calculate T1 and T2.
*/
static void calculate_times(struct dhcp_context *context, unsigned int *min_time, unsigned int *valid_timep,
unsigned int *preferred_timep, unsigned int lease_time, unsigned int requested_time)
unsigned int *preferred_timep, unsigned int lease_time)
{
unsigned int preferred_time, valid_time;
unsigned int req_preferred = *preferred_timep, req_valid = *valid_timep;
unsigned int valid_time = lease_time, preferred_time = lease_time;
/* RFC 3315: "A server ignores the lifetimes set
by the client if the preferred lifetime is greater than the valid
lifetime. */
if (req_preferred <= req_valid)
{
if (req_preferred != 0)
{
/* 0 == "no preference from client" */
if (req_preferred < 120u)
req_preferred = 120u; /* sanity */
if (req_preferred < preferred_time)
preferred_time = req_preferred;
}
if (req_valid != 0)
/* 0 == "no preference from client" */
{
if (req_valid < 120u)
req_valid = 120u; /* sanity */
if (req_valid < valid_time)
valid_time = req_valid;
}
}
if (requested_time < 120u )
requested_time = 120u; /* sanity */
if (lease_time == 0xffffffff || (requested_time != 0xffffffff && requested_time < lease_time))
lease_time = requested_time;
valid_time = (context->valid < lease_time) ? context->valid : lease_time;
preferred_time = (context->preferred < lease_time) ? context->preferred : lease_time;
if (context->flags & CONTEXT_DEPRECATE)
/* deprecate (preferred == 0) which configured, or when local address
is deprecated */
if ((context->flags & CONTEXT_DEPRECATE) || context->preferred == 0)
preferred_time = 0;
if (preferred_time != 0 && preferred_time < *min_time)
*min_time = preferred_time;
@@ -1828,4 +1914,118 @@ static unsigned int opt6_uint(unsigned char *opt, int offset, int size)
return ret;
}
void relay_upstream6(struct dhcp_relay *relay, ssize_t sz, struct in6_addr *peer_address, u32 scope_id)
{
/* ->local is same value for all relays on ->current chain */
struct all_addr from;
unsigned char *header;
unsigned char *inbuff = daemon->dhcp_packet.iov_base;
int msg_type = *inbuff;
int hopcount;
struct in6_addr multicast;
inet_pton(AF_INET6, ALL_SERVERS, &multicast);
/* source address == relay address */
from.addr.addr6 = relay->local.addr.addr6;
/* Get hop count from nested relayed message */
if (msg_type == DHCP6RELAYFORW)
hopcount = *((unsigned char *)inbuff+1) + 1;
else
hopcount = 0;
/* RFC 3315 HOP_COUNT_LIMIT */
if (hopcount > 32)
return;
save_counter(0);
if ((header = put_opt6(NULL, 34)))
{
int o;
header[0] = DHCP6RELAYFORW;
header[1] = hopcount;
memcpy(&header[2], &relay->local.addr.addr6, IN6ADDRSZ);
memcpy(&header[18], peer_address, IN6ADDRSZ);
o = new_opt6(OPTION6_RELAY_MSG);
put_opt6(inbuff, sz);
end_opt6(o);
for (; relay; relay = relay->current)
{
union mysockaddr to;
to.sa.sa_family = AF_INET6;
to.in6.sin6_addr = relay->server.addr.addr6;
to.in6.sin6_port = htons(DHCPV6_SERVER_PORT);
to.in6.sin6_flowinfo = 0;
to.in6.sin6_scope_id = 0;
if (IN6_ARE_ADDR_EQUAL(&relay->server.addr.addr6, &multicast))
{
int multicast_iface;
if (!relay->interface || strchr(relay->interface, '*') ||
(multicast_iface = if_nametoindex(relay->interface)) == 0 ||
setsockopt(daemon->dhcp6fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &multicast_iface, sizeof(multicast_iface)) == -1)
my_syslog(MS_DHCP | LOG_ERR, _("Cannot multicast to DHCPv6 server without correct interface"));
}
send_from(daemon->dhcp6fd, 0, daemon->outpacket.iov_base, save_counter(0), &to, &from, 0);
if (option_bool(OPT_LOG_OPTS))
{
inet_ntop(AF_INET6, &relay->local, daemon->addrbuff, ADDRSTRLEN);
inet_ntop(AF_INET6, &relay->server, daemon->namebuff, ADDRSTRLEN);
my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay %s -> %s"), daemon->addrbuff, daemon->namebuff);
}
/* Save this for replies */
relay->iface_index = scope_id;
}
}
}
unsigned short relay_reply6(struct sockaddr_in6 *peer, ssize_t sz, char *arrival_interface)
{
struct dhcp_relay *relay;
struct in6_addr link;
unsigned char *inbuff = daemon->dhcp_packet.iov_base;
/* must have at least msg_type+hopcount+link_address+peer_address+minimal size option
which is 1 + 1 + 16 + 16 + 2 + 2 = 38 */
if (sz < 38 || *inbuff != DHCP6RELAYREPL)
return 0;
memcpy(&link, &inbuff[2], IN6ADDRSZ);
for (relay = daemon->relay6; relay; relay = relay->next)
if (IN6_ARE_ADDR_EQUAL(&link, &relay->local.addr.addr6) &&
(!relay->interface || wildcard_match(relay->interface, arrival_interface)))
break;
save_counter(0);
if (relay)
{
void *opt, *opts = inbuff + 34;
void *end = inbuff + sz;
for (opt = opts; opt; opt = opt6_next(opt, end))
if (opt6_type(opt) == OPTION6_RELAY_MSG && opt6_len(opt) > 0)
{
int encap_type = *((unsigned char *)opt6_ptr(opt, 0));
put_opt6(opt6_ptr(opt, 0), opt6_len(opt));
memcpy(&peer->sin6_addr, &inbuff[18], IN6ADDRSZ);
peer->sin6_scope_id = relay->iface_index;
return encap_type == DHCP6RELAYREPL ? DHCPV6_SERVER_PORT : DHCPV6_CLIENT_PORT;
}
}
return 0;
}
#endif
+4 -2
View File
@@ -38,7 +38,9 @@ void slaac_add_addrs(struct dhcp_lease *lease, time_t now, int force)
lease->slaac_address = NULL;
for (context = daemon->dhcp6; context; context = context->next)
if ((context->flags & CONTEXT_RA_NAME) && lease->last_interface == context->if_index)
if ((context->flags & CONTEXT_RA_NAME) &&
!(context->flags & CONTEXT_OLD) &&
lease->last_interface == context->if_index)
{
struct in6_addr addr = context->start6;
if (lease->hwaddr_len == 6 &&
@@ -123,7 +125,7 @@ time_t periodic_slaac(time_t now, struct dhcp_lease *leases)
time_t next_event = 0;
for (context = daemon->dhcp6; context; context = context->next)
if ((context->flags & CONTEXT_RA_NAME))
if ((context->flags & CONTEXT_RA_NAME) && !(context->flags & CONTEXT_OLD))
break;
/* nothing configured */
+26 -12
View File
@@ -198,22 +198,36 @@ void tftp_request(struct listener *listen, time_t now)
addra.addr.addr6 = addr.in6.sin6_addr;
#endif
if (!iface_check(listen->family, &addra, name, NULL))
if (daemon->tftp_interfaces)
{
if (!option_bool(OPT_CLEVERBIND))
enumerate_interfaces();
if (!loopback_exception(listen->tftpfd, listen->family, &addra, name) &&
!label_exception(if_index, listen->family, &addra) )
/* dedicated tftp interface list */
for (tmp = daemon->tftp_interfaces; tmp; tmp = tmp->next)
if (tmp->name && wildcard_match(tmp->name, name))
break;
if (!tmp)
return;
}
else
{
/* Do the same as DHCP */
if (!iface_check(listen->family, &addra, name, NULL))
{
if (!option_bool(OPT_CLEVERBIND))
enumerate_interfaces(0);
if (!loopback_exception(listen->tftpfd, listen->family, &addra, name) &&
!label_exception(if_index, listen->family, &addra) )
return;
}
#ifdef HAVE_DHCP
/* allowed interfaces are the same as for DHCP */
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
if (tmp->name && wildcard_match(tmp->name, name))
return;
/* allowed interfaces are the same as for DHCP */
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
if (tmp->name && wildcard_match(tmp->name, name))
return;
#endif
}
strncpy(ifr.ifr_name, name, IF_NAMESIZE);
if (ioctl(listen->tftpfd, SIOCGIFMTU, &ifr) != -1)
mtu = ifr.ifr_mtu;
@@ -555,7 +569,7 @@ void check_tftp_listeners(fd_set *rset, time_t now)
}
/* don't complain about timeout when we're awaiting the last
ACK, some clients never send it */
else if (++transfer->backoff > 5 && len != 0)
else if (++transfer->backoff > 7 && len != 0)
{
endcon = 1;
len = 0;
+4 -5
View File
@@ -151,14 +151,13 @@ int legal_hostname(char *name)
/* check for legal char a-z A-Z 0-9 - _ . */
{
if ((c >= 'A' && c <= 'Z') ||
(c >= 'a' && c <= 'z'))
(c >= 'a' && c <= 'z') ||
(c >= '0' && c <= '9'))
continue;
if (!first &&
((c >= '0' && c <= '9') ||
c == '-' || c == '_'))
if (!first && (c == '-' || c == '_'))
continue;
/* end of hostname part */
if (c == '.')
return 1;