Compare commits

...
13 Commits
13 changed files with 536 additions and 151 deletions

No files matched your search

+30
View File
@@ -1,3 +1,33 @@
version 2.66
Add the ability to act as an authoritative DNS
server. Dnsmasq can now answer queries from the wider 'net
with local data, as long as the correct NS records are set
up. Only local data is provided, to avoid creating an open
DNS relay. Zone transfer is supported, to allow secondary
servers to be configured.
Add "constructed DHCP ranges" for DHCPv6. This is intended
for IPv6 routers which get prefixes dynamically via prefix
delegation. With suitable configuration, stateful DHCPv6
and RA can happen automatically as prefixes are delegated
and then deprecated, without having to re-write the
dnsmasq configuration file or restart the daemon. Thanks to
Steven Barth for extensive testing and development work on
this idea.
Fix crash on startup on Solaris 11. Regression probably
introduced in 2.61. Thanks to Geoff Johnstone for the
patch.
Add code to make behaviour for TCP DNS requests that same
as for UDP requests, when a request arrives for an allowed
address, but via a banned interface. This change is only
active on Linux, since the relevant API is missing (AFAIK)
on other platforms. Many thanks to Tomas Hozza for
spotting the problem, and doing invaluable discovery of
the obscure and undocumented API required for the solution.
version 2.65
Fix regression which broke forwarding of queries sent via
TCP which are not for A and AAAA and which were directed to
+178 -3
View File
@@ -75,6 +75,9 @@ the upstream DNS servers.
.B --max-cache-ttl=<time>
Set a maximum TTL value for entries in the cache.
.TP
.B --auth-ttl=<time>
Set the TTL value returned in answers from the authoritative server.
.TP
.B \-k, --keep-in-foreground
Do not go into the background at startup but otherwise run as
normal. This is intended for use when dnsmasq is run under daemontools
@@ -541,8 +544,27 @@ needed for a client to do validation itself.
.TP
.B --auth-zone=<domain>[,<subnet>[,<subnet>.....]]
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 specified subnets, or in a subnet corresponding to a contructed DHCP range. The subnet(s) are also used to define in-addr.arpa and ipv6.arpa domains which are served for reverse-DNS queries.
.TP
will be served, except that A and AAAA records must be in one of the
specified subnets, or in a subnet corresponding to a contructed DHCP
range. The subnet(s) are also used to define in-addr.arpa and
ipv6.arpa domains which are served for reverse-DNS queries.
.TP
.B --auth-soa=<serial>[,<hostmaster>[,<refresh>[,<retry>[,<expiry>]]]]
Specify fields in the SOA record associated with authoritative
zones. Note that this is optional, all the values are set to sane defaults.
.TP
.B --auth-sec-servers=<domain>[,<domain>[,<domain>...]]
Specify any secondary servers for a zone for which dnsmasq is
authoritative. These servers must be configured to get zone data from
dnsmasq by zone transfer, and answer queries for the same
authoritative zones and dnsmasq.
.TP
.B --auth-peer=<ip-address>[,<ip-address>[,<ip-address>...]]
Specify the addresses of secondary servers which are allowed to
initiate zone transfer (AXFR) requests for zones for which dnsmasq is
authoritative. If this option is not given, then AXFR requests wil be
accepted from any secondary.
.TP
.B --conntrack
Read the Linux connection track mark associated with incoming DNS
queries and set the same mark value on upstream traffic used to answer
@@ -695,7 +717,13 @@ A single
.B dhcp-host
may contain an IPv4 address or an IPv6 address, or both. IPv6 addresses must be bracketed by square brackets thus:
.B --dhcp-host=laptop,[1234::56]
Note that in IPv6 DHCP, the hardware address is not normally available, so a client must be identified by client-id (called client DUID in IPv6-land) or hostname.
IPv6 addresses may contain only the host-identifier part:
.B --dhcp-host=laptop,[::56]
in which case thay act as wildcards in constructed dhcp ranges, with
the appropriate network part inserted.
Note that in IPv6 DHCP, the hardware address is not normally
available, so a client must be identified by client-id (called client
DUID in IPv6-land) or hostname.
The special option id:* means "ignore any client-id
and use MAC addresses only." This is useful when a client presents a client-id sometimes
@@ -1671,6 +1699,153 @@ parameter in a BOOTP request is used as a tag,
as is the tag "bootp", allowing some control over the options returned to
different classes of hosts.
.SH AUTHORITATIVE CONFIGURATION
.PP
Configuring dnsmasq to act as an authoritative DNS server is
complicated by the fact that it involves configuration of external DNS
servers to provide delegation. We will walk through three scenarios of
increasing complexity. Prerequisites for all of these scenarios
are a globally accesible IP address, an A or AAAA record pointing to that address,
and an external DNS server capable of doing delegation of the zone in
question. For the first part of this explanation, we will call the A (or AAAA) record
for the globally accessible address server.example.com, and the zone
for which dnsmasq is authoritative our.zone.com.
The simplest configuration consists of two lines of dnsmasq configuration; something like
.nf
.B auth-server=server.example.com,eth0
.B auth-zone=our.zone.com,1.2.3.0/24
.fi
and two records in the external DNS
.nf
server.example.com A 192.0.43.10
our.zone.com NS server.example.com
.fi
eth0 is the external network interface on which dnsmasq is listening,
and has (globally accessible) address 192.0.43.10.
Note that the external IP address may well be dynamic (ie assigned
from an ISP by DHCP or PPP) If so, the A record must be linked to this
dynamic assignment by one of the usual dynamic-DNS systems.
A more complex, but practically useful configuration has the address
record for the globally accessible IP address residing in the
authoritative zone which dnsmasq is serving, typically at the root. Now
we have
.nf
.B auth-server=our.zone.com,eth0
.B auth-zone=our.zone.com,1.2.3.0/24
.fi
.nf
our.zone.com A 192.0.43.10
our.zone.com NS our.zone.com
.fi
The A record for our.zone.com has now become a glue record, it solves
the chicken-and-egg problem of finding the IP address of the
nameserver for our.zone.com when the A record is within that
zone. Note that this is the only role of this record: as dnsmasq is
now authoritative from our.zone.com it too must provide this
record. If the external address is static, this can be done with an
.B /etc/hosts
entry or
.B --host-record.
If the external address is dynamic,
then it must be done using something like
.nf
.B --interface-name=our.zone.com,eth0
.fi
Our final configuration builds on that above, but also adds a
secondary DNS server. This is another DNS server which learns the DNS data
for the zone by doing zones transfer, and acts as a backup should
the primary server become inaccessible. The configuration of the
secondary is beyond the scope of this man-page, but the extra
configuration of dnsmasq is simple:
.nf
.B auth-sec-servers=secondary.myisp.com
.fi
and
.nf
our.zone.com NS secondary.myisp.com
.fi
Adding auth-sec-servers enables zone transfer in dnsmasq, to allow the
secondary to collect the DNS data. If you wish to restrict this data
to particular hosts then
.nf
.B auth-peer=<IP address of secondary>
.fi
will do so.
Dnsmasq acts as an authoritative server for in-addr.arpa and
ipv6.arpa domains associated with the subnets given in auth-zone
declarations, so reverse (address to name) lookups can be simply
configured with a suitable NS record, for instance in this example,
where we allow 1.2.3.0/24 addresses.
.nf
3.2.1.in-addr.arpa NS our.zone.com
.fi
Note that at present, reverse (in-addr.arpa and ip6.arpa) zones are
not available in zone transfers, so there is no point arranging
secondary servers for reverse lookups.
.PP
When dnsmasq is configured to act as an authoritative server, the
following data is used to populate the authoritative zone.
.PP
.B --mx-host, --srv-host, --dns-rr, --txt-record, --naptr-record
, as long as the record names are in the authoritative domain.
.PP
.B --cname
as long as the record name is in the authoritative domain. If the
target of the CNAME is unqualified, then it is qualified with the
authoritative zone name.
.PP
IPv4 and IPv6 addresses from /etc/hosts (and
.B --addn-hosts
) and
.B --host-record
provided the address falls into one of the subnets specified in the
.B --auth-zone.
.PP
Addresses specified by
.B --interface-name.
In this case, the address is not contrained to a subnet from
.B --auth-zone.
.PP
Addresses of DHCP leases, provided the address falls into one of the subnets specified in the
.B --auth-zone
OR a constructed DHCP range. In the default mode, where a DHCP lease
has an unqualified name, and possibly a qualified name constructed
using
.B --domain
then the name in the authoritative zone is constructed from the
unqualified name and the zone's domain. This may or may not equal
that specified by
.B --domain.
If
.B --dhcp-fqdn
is set, then the fully qualified names associated with DHCP leases are
used, and must match the zone's domain.
.SH EXIT CODES
.PP
0 - Dnsmasq successfully forked into the background, or terminated
+20 -22
View File
@@ -376,35 +376,33 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
}
else if (qtype == T_AXFR)
{
if (daemon->auth_peers)
struct iname *peers;
if (peer_addr->sa.sa_family == AF_INET)
peer_addr->in.sin_port = 0;
#ifdef HAVE_IPV6
else
peer_addr->in6.sin6_port = 0;
#endif
for (peers = daemon->auth_peers; peers; peers = peers->next)
if (sockaddr_isequal(peer_addr, &peers->addr))
break;
/* Refuse all AXFR unless --auth-sec-servers is set */
if ((!peers && daemon->auth_peers) || !daemon->secondary_forward_server)
{
struct iname *peers;
if (peer_addr->sa.sa_family == AF_INET)
peer_addr->in.sin_port = 0;
inet_ntop(AF_INET, &peer_addr->in.sin_addr, daemon->addrbuff, ADDRSTRLEN);
#ifdef HAVE_IPV6
else
peer_addr->in6.sin6_port = 0;
inet_ntop(AF_INET6, &peer_addr->in6.sin6_addr, daemon->addrbuff, ADDRSTRLEN);
#endif
for (peers = daemon->auth_peers; peers; peers = peers->next)
if (sockaddr_isequal(peer_addr, &peers->addr))
break;
if (!peers)
{
if (peer_addr->sa.sa_family == AF_INET)
inet_ntop(AF_INET, &peer_addr->in.sin_addr, daemon->addrbuff, ADDRSTRLEN);
#ifdef HAVE_IPV6
else
inet_ntop(AF_INET6, &peer_addr->in6.sin6_addr, daemon->addrbuff, ADDRSTRLEN);
#endif
my_syslog(LOG_WARNING, _("ignoring zone transfer request from %s"), daemon->addrbuff);
return 0;
}
my_syslog(LOG_WARNING, _("ignoring zone transfer request from %s"), daemon->addrbuff);
return 0;
}
soa = 1; /* inhibits auth section */
ns = 1; /* ensure we include NS records! */
axfr = 1;
+5 -2
View File
@@ -111,7 +111,7 @@ int iface_enumerate(int family, void *parm, int (*callback)())
{
int iface_index = if_nametoindex(addrs->ifa_name);
if (iface_index == 0)
if (iface_index == 0 || !addrs->ifa_addr || !addrs->ifa_netmask)
continue;
if (family == AF_INET)
@@ -119,7 +119,10 @@ int iface_enumerate(int family, void *parm, int (*callback)())
struct in_addr addr, netmask, broadcast;
addr = ((struct sockaddr_in *) addrs->ifa_addr)->sin_addr;
netmask = ((struct sockaddr_in *) addrs->ifa_netmask)->sin_addr;
broadcast = ((struct sockaddr_in *) addrs->ifa_broadaddr)->sin_addr;
if (addrs->ifa_broadaddr)
broadcast = ((struct sockaddr_in *) addrs->ifa_broadaddr)->sin_addr;
else
broadcast.s_addr = 0;
if (!((*callback)(addr, iface_index, netmask, broadcast, parm)))
goto err;
}
+36 -44
View File
@@ -679,7 +679,7 @@ void log_context(int family, struct dhcp_context *context)
void *start = &context->start;
void *end = &context->end;
char *n = "", *m = "", *p = daemon->namebuff;
char *template = "", *p = daemon->namebuff;
*p = 0;
@@ -694,67 +694,59 @@ void log_context(int family, struct dhcp_context *context)
end = &context->end6;
}
#endif
if ((context->flags & CONTEXT_DHCP) ||
!(context->flags & (CONTEXT_CONSTRUCTED | CONTEXT_TEMPLATE)))
if (family != AF_INET && (context->flags & CONTEXT_DEPRECATE))
strcpy(daemon->namebuff, _(", prefix deprecated"));
else
{
if (family != AF_INET && (context->flags & CONTEXT_DEPRECATE))
strcpy(daemon->namebuff, _(", prefix deprecated"));
else
{
p += sprintf(p, _(", lease time "));
m = p;
prettyprint_time(p, context->lease_time);
p += strlen(p);
}
}
p += sprintf(p, _(", lease time "));
prettyprint_time(p, context->lease_time);
p += strlen(p);
}
#ifdef HAVE_DHCP6
if (context->flags & CONTEXT_CONSTRUCTED)
{
char ifrn_name[IFNAMSIZ];
if (indextoname(daemon->doing_dhcp6 ? daemon->dhcp6fd : daemon->icmp6fd, context->if_index, ifrn_name))
{
n = p;
p += sprintf(p, ", constructed for %s", ifrn_name);
}
}
if (context->flags & CONTEXT_TEMPLATE)
{
n = p;
p += sprintf(p, ", template for %s%s", context->template_interface,
(context->flags & CONTEXT_WILDCARD) ? "*" : "");
}
{
char ifrn_name[IFNAMSIZ];
template = p;
p += sprintf(p, ", ");
if (indextoname(daemon->doing_dhcp6 ? daemon->dhcp6fd : daemon->icmp6fd, context->if_index, ifrn_name))
sprintf(p, "constructed for %s", ifrn_name);
}
else if (context->flags & CONTEXT_TEMPLATE)
{
template = p;
p += sprintf(p, ", ");
sprintf(p, "template for %s%s", context->template_interface,
(context->flags & CONTEXT_WILDCARD) ? "*" : "");
}
#endif
if ((context->flags & CONTEXT_DHCP) || family == AF_INET)
{
inet_ntop(family, start, daemon->dhcp_buff, 256);
inet_ntop(family, end, daemon->dhcp_buff3, 256);
my_syslog(MS_DHCP | LOG_INFO,
(context->flags & CONTEXT_RA_STATELESS) ?
_("%s stateless on %s%.0s%.0s") :
_("%s stateless on %s%.0s%.0s%s") :
(context->flags & CONTEXT_STATIC) ?
_("%s, static leases only on %.0s%s%s") :
_("%s, static leases only on %.0s%s%s%.0s") :
(context->flags & CONTEXT_PROXY) ?
_("%s, proxy on subnet %.0s%s%.0s") :
_("%s, IP range %s -- %s%s"),
_("%s, proxy on subnet %.0s%s%.0s%.0s") :
_("%s, IP range %s -- %s%s%.0s"),
(family != AF_INET) ? "DHCPv6" : "DHCP",
daemon->dhcp_buff, daemon->dhcp_buff3, daemon->namebuff);
daemon->dhcp_buff, daemon->dhcp_buff3, daemon->namebuff, template);
}
#ifdef HAVE_DHCP6
if (context->flags & CONTEXT_RA_NAME)
my_syslog(MS_DHCP | LOG_INFO, _("DHCPv4-derived IPv6 names on %s%s"),
daemon->addrbuff, n);
my_syslog(MS_DHCP | LOG_INFO, _("DHCPv4-derived IPv6 names on %s%s"), daemon->addrbuff, template);
if (context->flags & CONTEXT_RA)
my_syslog(MS_DHCP | LOG_INFO, _("router advertisement on %s%s%s"),
daemon->addrbuff,
(context->flags & (CONTEXT_CONSTRUCTED | CONTEXT_TEMPLATE)) ? "" : ", prefix valid ",
(context->flags & (CONTEXT_CONSTRUCTED | CONTEXT_TEMPLATE)) ? n : m);
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
}
+35 -19
View File
@@ -211,7 +211,8 @@ static int complete_context6(struct in6_addr *local, int prefix,
for (context = daemon->dhcp6; context; context = context->next)
{
if (!(context->flags & CONTEXT_TEMPLATE) &&
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))
@@ -328,6 +329,7 @@ int address6_allocate(struct dhcp_context *context, unsigned char *clid, int cl
return 0;
}
/* can dynamically allocate addr */
struct dhcp_context *address6_available(struct dhcp_context *context,
struct in6_addr *taddr,
struct dhcp_netid *netids)
@@ -352,6 +354,21 @@ struct dhcp_context *address6_available(struct dhcp_context *context,
return NULL;
}
/* address OK if configured */
struct dhcp_context *address6_valid(struct dhcp_context *context,
struct in6_addr *taddr,
struct dhcp_netid *netids)
{
struct dhcp_context *tmp;
for (tmp = context; tmp; tmp = tmp->current)
if (is_same_net6(&tmp->start6, taddr, tmp->prefix) &&
match_netid(tmp->filter, netids, 1))
return tmp;
return NULL;
}
struct dhcp_context *narrow_context6(struct dhcp_context *context,
struct in6_addr *taddr,
struct dhcp_netid *netids)
@@ -366,21 +383,12 @@ struct dhcp_context *narrow_context6(struct dhcp_context *context,
struct dhcp_context *tmp;
if (!(tmp = address6_available(context, taddr, netids)))
{
for (tmp = context; tmp; tmp = tmp->current)
if (match_netid(tmp->filter, netids, 1) &&
is_same_net6(taddr, &tmp->start6, tmp->prefix) &&
(tmp->flags & CONTEXT_STATIC))
break;
if (!tmp)
for (tmp = context; tmp; tmp = tmp->current)
if (match_netid(tmp->filter, netids, 1) &&
is_same_net6(taddr, &tmp->start6, tmp->prefix) &&
!(tmp->flags & CONTEXT_PROXY))
break;
}
if (!(tmp = address6_available(context, taddr, netids)) &&
!(tmp = address6_valid(context, taddr, netids)))
for (tmp = context; tmp; tmp = tmp->current)
if (match_netid(tmp->filter, netids, 1) &&
is_same_net6(taddr, &tmp->start6, tmp->prefix))
break;
/* Only one context allowed now */
if (tmp)
@@ -391,11 +399,19 @@ struct dhcp_context *narrow_context6(struct dhcp_context *context,
static int is_config_in_context6(struct dhcp_context *context, struct dhcp_config *config)
{
if (!context) /* called via find_config() from lease_update_from_configs() */
return 1;
/* expand wildcard on contructed contexts */
if ((config->flags & CONFIG_WILDCARD) &&
(context->flags & CONTEXT_CONSTRUCTED))
{
u64 addrpart = addr6part(&config->addr6);
config->addr6 = context->start6;
setaddr6part(&config->addr6, addrpart);
return 1;
}
if (!(config->flags & CONFIG_ADDR6) || is_addr_in_context6(context, &config->addr6))
return 1;
return 0;
}
+52 -19
View File
@@ -1337,7 +1337,7 @@ static void check_dns_listeners(fd_set *set, time_t now)
if (listener->tcpfd != -1 && FD_ISSET(listener->tcpfd, set))
{
int confd;
int confd, client_ok = 1;
struct irec *iface = NULL;
pid_t p;
union mysockaddr tcp_addr;
@@ -1348,25 +1348,58 @@ static void check_dns_listeners(fd_set *set, time_t now)
if (confd == -1 ||
getsockname(confd, (struct sockaddr *)&tcp_addr, &tcp_len) == -1)
continue;
if (option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND))
if (option_bool(OPT_NOWILD))
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. */
/* interface may be new since startup */
if (enumerate_interfaces())
for (iface = daemon->interfaces; iface; iface = iface->next)
if (sockaddr_isequal(&iface->addr, &tcp_addr))
break;
}
if (!iface && !(option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND)))
else
{
int if_index;
char intr_name[IF_NAMESIZE];
/* 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;
#ifdef HAVE_IPV6
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;
}
}
if (!client_ok)
{
shutdown(confd, SHUT_RDWR);
close(confd);
+6
View File
@@ -593,6 +593,7 @@ struct dhcp_config {
#define CONFIG_DECLINED 1024 /* address declined by client */
#define CONFIG_BANK 2048 /* from dhcp hosts file */
#define CONFIG_ADDR6 4096
#define CONFIG_WILDCARD 8192
struct dhcp_opt {
int opt, len, flags;
@@ -1001,7 +1002,9 @@ void create_wildcard_listeners(void);
void create_bound_listeners(int die);
int is_dad_listeners(void);
int iface_check(int family, struct all_addr *addr, char *name, int *auth_dns);
int loopback_exception(int fd, int family, struct all_addr *addr, char *name);
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);
@@ -1149,6 +1152,9 @@ int is_addr_in_context6(struct dhcp_context *context, struct in6_addr *addr);
struct dhcp_context *address6_available(struct dhcp_context *context,
struct in6_addr *taddr,
struct dhcp_netid *netids);
struct dhcp_context *address6_valid(struct dhcp_context *context,
struct in6_addr *taddr,
struct dhcp_netid *netids);
struct dhcp_context *narrow_context6(struct dhcp_context *context,
struct in6_addr *taddr,
struct dhcp_netid *netids);
+10 -3
View File
@@ -763,10 +763,17 @@ void receive_query(struct listener *listen, time_t now)
/* enforce available interface configuration */
if (!indextoname(listen->fd, if_index, ifr.ifr_name) ||
!iface_check(listen->family, &dst_addr, ifr.ifr_name, &auth_dns))
if (!indextoname(listen->fd, if_index, ifr.ifr_name))
return;
if (!iface_check(listen->family, &dst_addr, ifr.ifr_name, &auth_dns))
{
if (!option_bool(OPT_CLEVERBIND))
enumerate_interfaces();
if (!loopback_exception(listen->fd, listen->family, &dst_addr, ifr.ifr_name))
return;
}
if (listen->family == AF_INET && option_bool(OPT_LOCALISE))
{
struct irec *iface;
@@ -780,7 +787,7 @@ void receive_query(struct listener *listen, time_t now)
break;
/* interface may be new */
if (!iface)
if (!iface && !option_bool(OPT_CLEVERBIND))
enumerate_interfaces();
for (iface = daemon->interfaces; iface; iface = iface->next)
+105 -1
View File
@@ -170,7 +170,40 @@ int iface_check(int family, struct all_addr *addr, char *name, int *auth)
return ret;
}
/* Fix for problem that the kernel sometimes reports the loopback inerface as the
arrival interface when a packet originates locally, even when sent to address of
an interface other than the loopback. Accept packet if it arrived via a loopback
interface, even when we're not accepting packets that way, as long as the destination
address is one we're believing. Interface list must be up-to-date before calling. */
int loopback_exception(int fd, int family, struct all_addr *addr, char *name)
{
struct ifreq ifr;
struct irec *iface;
strncpy(ifr.ifr_name, name, IF_NAMESIZE);
if (ioctl(fd, SIOCGIFFLAGS, &ifr) != -1 &&
ifr.ifr_flags & IFF_LOOPBACK)
{
for (iface = daemon->interfaces; iface; iface = iface->next)
if (iface->addr.sa.sa_family == family)
{
if (family == AF_INET)
{
if (iface->addr.in.sin_addr.s_addr == addr->addr.addr4.s_addr)
return 1;
}
#ifdef HAVE_IPV6
else if (IN6_ARE_ADDR_EQUAL(&iface->addr.in6.sin6_addr, &addr->addr.addr6))
return 1;
#endif
}
}
return 0;
}
static int iface_allowed(struct irec **irecp, int if_index,
union mysockaddr *addr, struct in_addr netmask, int dad)
{
@@ -459,6 +492,77 @@ int set_ipv6pktinfo(int fd)
return 0;
}
#endif
/* Find the interface on which a TCP connection arrived, if possible, or zero otherwise. */
int tcp_interface(int fd, int af)
{
int if_index = 0;
#ifdef HAVE_LINUX_NETWORK
int opt = 1;
struct cmsghdr *cmptr;
struct msghdr msg;
/* use mshdr do that the CMSDG_* macros are available */
msg.msg_control = daemon->packet;
msg.msg_controllen = daemon->packet_buff_sz;
/* we overwrote the buffer... */
daemon->srv_save = NULL;
if (af == AF_INET)
{
if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt)) != -1 &&
getsockopt(fd, IPPROTO_IP, IP_PKTOPTIONS, msg.msg_control, (socklen_t *)&msg.msg_controllen) != -1)
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO)
{
union {
unsigned char *c;
struct in_pktinfo *p;
} p;
p.c = CMSG_DATA(cmptr);
if_index = p.p->ipi_ifindex;
}
}
#ifdef HAVE_IPV6
else
{
/* Only the RFC-2292 API has the ability to find the interface for TCP connections,
it was removed in RFC-3542 !!!!
Fortunately, Linux kept the 2292 ABI when it moved to 3542. The following code always
uses the old ABI, and should work with pre- and post-3542 kernel headers */
#ifdef IPV6_2292PKTOPTIONS
# define PKTOPTIONS IPV6_2292PKTOPTIONS
#else
# define PKTOPTIONS IPV6_PKTOPTIONS
#endif
if (set_ipv6pktinfo(fd) &&
getsockopt(fd, IPPROTO_IPV6, PKTOPTIONS, msg.msg_control, (socklen_t *)&msg.msg_controllen) != -1)
{
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
{
union {
unsigned char *c;
struct in6_pktinfo *p;
} p;
p.c = CMSG_DATA(cmptr);
if_index = p.p->ipi6_ifindex;
}
}
}
#endif /* IPV6 */
#endif /* Linux */
return if_index;
}
static struct listener *create_listeners(union mysockaddr *addr, int do_tftp, int dienow)
{
+9 -1
View File
@@ -392,7 +392,7 @@ static struct {
{ LOPT_RR, ARG_DUP, "<name>,<RR-number>,[<data>]", gettext_noop("Specify arbitrary DNS resource record"), NULL },
{ LOPT_CLVERBIND, OPT_CLEVERBIND, NULL, gettext_noop("Bind to interfaces in use - check for new interfaces"), NULL },
{ LOPT_AUTHSERV, ARG_ONE, "<NS>,<interface>", gettext_noop("Export local names to global DNS"), NULL },
{ LOPT_AUTHZONE, ARG_DUP, "<domain>,<subnet>[,<subnet>]", gettext_noop("Domain to export to global DNS"), NULL },
{ LOPT_AUTHZONE, ARG_DUP, "<domain>,[<subnet>...]", gettext_noop("Domain to export to global DNS"), NULL },
{ LOPT_AUTHTTL, ARG_ONE, "<integer>", gettext_noop("Set TTL for authoritative replies"), NULL },
{ LOPT_AUTHSOA, ARG_ONE, "<serial>[,...]", gettext_noop("Set authoritive zone information"), NULL },
{ LOPT_AUTHSFS, ARG_DUP, "<NS>[,<NS>...]", gettext_noop("Secondary authoritative nameservers for forward domains"), NULL },
@@ -2468,6 +2468,14 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
if (!inet_pton(AF_INET6, arg, &new->addr6))
ret_err(_("bad IPv6 address"));
for (i= 0; i < 8; i++)
if (new->addr6.s6_addr[i] != 0)
break;
/* set WILDCARD if network part all zeros */
if (i == 8)
new->flags |= CONFIG_WILDCARD;
new->flags |= CONFIG_ADDR6;
}
+39 -31
View File
@@ -82,18 +82,20 @@ static int dhcp6_maybe_relay(struct in6_addr *link_address, struct dhcp_netid **
{
struct dhcp_context *c;
context = NULL;
if (!IN6_IS_ADDR_LOOPBACK(link_address) &&
!IN6_IS_ADDR_LINKLOCAL(link_address) &&
!IN6_IS_ADDR_MULTICAST(link_address))
for (c = daemon->dhcp6; c; c = c->next)
if ((c->flags & CONTEXT_DHCP) &&
!(c->flags & CONTEXT_TEMPLATE) &&
is_same_net6(link_address, &c->start6, c->prefix) &&
is_same_net6(link_address, &c->end6, c->prefix))
{
c->current = context;
context = c;
}
for (c = daemon->dhcp6; c; c = c->next)
if (!IN6_IS_ADDR_LOOPBACK(link_address) &&
!IN6_IS_ADDR_LINKLOCAL(link_address) &&
!IN6_IS_ADDR_MULTICAST(link_address) &&
is_same_net6(link_address, &c->start6, c->prefix) &&
is_same_net6(link_address, &c->end6, c->prefix))
{
c->current = context;
context = c;
}
if (!context)
{
inet_ntop(AF_INET6, link_address, daemon->addrbuff, ADDRSTRLEN);
@@ -525,32 +527,38 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
{
struct in6_addr *req_addr = opt6_ptr(ia_option, 0);
requested_time = opt6_uint(ia_option, 16, 4);
if (!address6_available(context, req_addr, tags) &&
(!have_config(config, CONFIG_ADDR6) || memcmp(&config->addr6, req_addr, IN6ADDRSZ) != 0))
struct dhcp_context *dynamic;
if ((dynamic = address6_available(context, req_addr, tags)) || address6_valid(context, req_addr, tags))
{
if (msg_type == DHCP6REQUEST)
if (!dynamic && !(have_config(config, CONFIG_ADDR6) && memcmp(&config->addr6, req_addr, IN6ADDRSZ) == 0))
{
/* host has a lease, but it's not on the correct link */
/* Static range, not configured. */
o1 = new_opt6(OPTION6_STATUS_CODE);
put_opt6_short(DHCP6NOTONLINK);
put_opt6_string("Not on link");
put_opt6_short(DHCP6UNSPEC);
put_opt6_string("Address unavailable");
end_opt6(o1);
}
else if (lease6_find_by_addr(req_addr, 128, 0) &&
!(lease = lease6_find(clid, clid_len, ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, iaid, req_addr)))
{
/* Address leased to another DUID/IAID */
o1 = new_opt6(OPTION6_STATUS_CODE);
put_opt6_short(DHCP6UNSPEC);
put_opt6_string("Address in use");
end_opt6(o1);
}
else
addrp = req_addr;
}
else if ((lease = lease6_find(NULL, 0, ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA,
iaid, req_addr)) &&
(clid_len != lease->clid_len ||
memcmp(clid, lease->clid, clid_len) != 0))
else if (msg_type == DHCP6REQUEST)
{
/* Address leased to another DUID */
/* requested address not on the correct link */
o1 = new_opt6(OPTION6_STATUS_CODE);
put_opt6_short(DHCP6UNSPEC);
put_opt6_string("Address in use");
put_opt6_short(DHCP6NOTONLINK);
put_opt6_string("Not on link");
end_opt6(o1);
}
else
addrp = req_addr;
}
}
else
{
@@ -567,9 +575,9 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
inet_ntop(AF_INET6, &config->addr6, daemon->addrbuff, ADDRSTRLEN);
if (ltmp && ltmp->clid &&
(ltmp->clid_len != clid_len || memcmp(ltmp->clid, clid, clid_len) != 0))
my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it is leased to %s"),
daemon->addrbuff, print_mac(daemon->namebuff, ltmp->clid, ltmp->clid_len));
(ltmp->clid_len != clid_len || memcmp(ltmp->clid, clid, clid_len) != 0 || ltmp->hwaddr_type != iaid))
my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it is leased to %s#%d"),
daemon->addrbuff, print_mac(daemon->namebuff, ltmp->clid, ltmp->clid_len), ltmp->hwaddr_type);
else if (have_config(config, CONFIG_DECLINED) &&
difftime(now, config->decline_time) < (float)DECLINE_BACKOFF)
my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it was previously declined"),
+11 -6
View File
@@ -61,6 +61,7 @@ void tftp_request(struct listener *listen, time_t now)
char *name = NULL;
char *prefix = daemon->tftp_prefix;
struct tftp_prefix *pref;
struct all_addr addra;
union {
struct cmsghdr align; /* this ensures alignment */
@@ -189,18 +190,22 @@ void tftp_request(struct listener *listen, time_t now)
return;
name = namebuff;
addra.addr.addr4 = addr.in.sin_addr;
#ifdef HAVE_IPV6
if (listen->family == AF_INET6)
addra.addr.addr6 = addr.in6.sin6_addr;
#endif
if (!iface_check(listen->family, &addra, name, NULL))
{
if (!iface_check(AF_INET6, (struct all_addr *)&addr.in6.sin6_addr, name, NULL))
if (!option_bool(OPT_CLEVERBIND))
enumerate_interfaces();
if (!loopback_exception(listen->tftpfd, listen->family, &addra, name))
return;
}
else
#endif
if (!iface_check(AF_INET, (struct all_addr *)&addr.in.sin_addr, name, NULL))
return;
#ifdef HAVE_DHCP
/* allowed interfaces are the same as for DHCP */
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)