Compare commits

..
23 Commits
Author SHA1 Message Date
Simon Kelley b456b9fdfe Linked-list bug in new "use longest prefixes first" code. 2013-01-02 17:59:28 +00:00
Simon Kelley 34d0a36a1d Man page updates 2013-01-02 11:40:56 +00:00
Simon Kelley 355736f36f Fix auth-DNS filtering problems with contructed ranges. 2012-12-30 17:54:04 +00:00
Simon Kelley 771287be11 Wildcards in dhcp-range constructors 2012-12-30 17:38:09 +00:00
Simon Kelley dc9476b670 Use RA_INTERVAL for lifetimes. 2012-12-29 22:08:26 +00:00
Simon Kelley 1e14cc0f48 Make it legal to have no subnet in --auth-zone, may be contructed instead. 2012-12-29 17:27:59 +00:00
Simon Kelley 55b548ae2b Add RA_INTERVAL parameter in config.h 2012-12-29 17:13:04 +00:00
Simon Kelley 3b43646a08 Use /proc/sys/net/ipv6/conf/<iface>/mtu for RA advertised MTU. 2012-12-28 11:55:45 +00:00
Simon Kelley 3bc0d932d0 More work on lease and router lifetime calculation. 2012-12-28 11:31:44 +00:00
Simon Kelley 60225f4e75 Allow constructed prefixes in auth zones. 2012-12-28 11:29:01 +00:00
Simon Kelley 1962446269 Join multicast groups only on IPv6 addresses! 2012-12-28 11:18:09 +00:00
Simon Kelley be37986a0f Better error checking in DHCPv6 dhcp-range option parsing. 2012-12-23 12:01:39 +00:00
Simon Kelley d7346a1e8c Tweak context-construct logic. 2012-12-22 22:45:54 +00:00
Simon Kelley 87d346f6a7 saner function name 2012-12-22 22:35:11 +00:00
Simon Kelley f0dd7f807d Fix new-address logic and ordering for first address on new interface. 2012-12-22 22:31:58 +00:00
Simon Kelley 0c0502426f Check for new SLAAC addresses when we add new prefixes. 2012-12-22 22:13:19 +00:00
Simon Kelley 7f035f58c6 Don't cap prefx lifetimes below RA retransmit interval. 2012-12-22 21:27:08 +00:00
Simon Kelley 81e84f8dac preferred and valid times in bpf.c 2012-12-21 20:54:00 +00:00
Simon Kelley 55b42f6de3 Default to global, not link-local address in RA DNS field. 2012-12-21 16:53:15 +00:00
Simon Kelley ed8b68ad06 Simplify and fix RA lifetime calculation. 2012-12-21 16:23:26 +00:00
Simon Kelley bad7b875eb add general flag param to iface_enumerate IPv6 callback 2012-12-20 22:00:39 +00:00
Simon Kelley 5d162f20a9 Rationalise join_multicast() 2012-12-20 14:55:46 +00:00
Simon Kelley 9d29949440 typo 2012-12-18 21:48:15 +00:00
14 changed files with 334 additions and 236 deletions

No files matched your search

+29 -6
View File
@@ -6,24 +6,25 @@ dnsmasq \- A lightweight DHCP and caching DNS server.
.I [OPTION]...
.SH "DESCRIPTION"
.BR dnsmasq
is a lightweight DNS, TFTP and DHCP server. It is intended to provide
is a lightweight DNS, TFTP, PXE, router advertisement and DHCP server. It is intended to provide
coupled DNS and DHCP service to a LAN.
.PP
Dnsmasq accepts DNS queries and either answers them from a small, local,
cache or forwards them to a real, recursive, DNS server. It loads the
contents of /etc/hosts so that local hostnames
which do not appear in the global DNS can be resolved and also answers
DNS queries for DHCP configured hosts.
DNS queries for DHCP configured hosts. It can also act as the authoritative DNS server for one or more domains, allowing local names to appear in the global DNS.
.PP
The dnsmasq DHCP server supports static address assignments and multiple
networks. It automatically
sends a sensible default set of DHCP options, and can be configured to
send any desired set of DHCP options, including vendor-encapsulated
options. It includes a secure, read-only,
TFTP server to allow net/PXE boot of DHCP hosts and also supports BOOTP.
TFTP server to allow net/PXE boot of DHCP hosts and also supports BOOTP. The PXE support is full featured, and includes a proxy mode which supplies PXE information to clients whilst DHCP address allocation is done by another server.
.PP
Dnsmasq
supports IPv6 for all functions and a minimal router-advertisement daemon.
The dnsmasq DHCPv6 server provides the same set of features as the DHCPv4 server, and in addition, it includes router advertisements and a neat feature which allows nameing for clients which use DHCPv4 and RA only for IPv6 configuration. There is support for doing address allocation (both DHCPv6 and RA) from subnets which are dynamically delegated via DHCPv6 prefix delegation.
.PP
Dnsmasq is coded with small embedded systems in mind. It aims for the smallest possible memory footprint compatible with the supported functions, and allows uneeded functions to be omitted from the compiled binary.
.SH OPTIONS
Note that in general missing parameters are allowed and switch off
functions, for instance "--pid-file" disables writing a PID file. On
@@ -178,6 +179,16 @@ and
options does not matter and that
.B --except-interface
options always override the others.
.TP
.B --auth-server=<domain>,<interface>|<ip-address>
Enable DNS authoritative mode for queries arriving at an interface or address. Note that the the interface or address
need not be mentioned in
.B --interface
or
.B --listen-address
configuration, indeed
.B --auth-server
will overide these and provide a different DNS service on the specified interface. The <domain> is the "glue record". It should resolve in the global DNS to a A and/or AAAA record which points to the address dnsmasq is listening on.
.TP
.B \-2, --no-dhcp-interface=<interface name>
Do not provide DHCP or TFTP on the specified interface, but do provide DNS service.
@@ -528,6 +539,10 @@ 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>.....]]
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
.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
@@ -540,7 +555,7 @@ included and configured. This option cannot be combined with
.TP
.B \-F, --dhcp-range=[tag:<tag>[,tag:<tag>],][set:<tag],]<start-addr>[,<end-addr>][,<mode>][,<netmask>[,<broadcast>]][,<lease time>]
.TP
.B \-F, --dhcp-range=[tag:<tag>[,tag:<tag>],][set:<tag],]<start-IPv6addr>[,<end-IPv6addr>][,<mode>][,<prefix-len>][,<lease time>]
.B \-F, --dhcp-range=[tag:<tag>[,tag:<tag>],][set:<tag],]<start-IPv6addr>[,<end-IPv6addr>|constuctor:<interface>][,<mode>][,<prefix-len>][,<lease time>]
Enable the DHCP server. Addresses will be given out from the range
<start-addr> to <end-addr> and from statically defined addresses given
@@ -572,6 +587,14 @@ given, this defaults to 64. Unlike the IPv4 case, the prefix length is not
automatically derived from the interface configuration. The mimimum
size of the prefix length is 64.
IPv6 (only) supports another type of range. In this, the start address and optional end address contain only the network part (ie ::1) and they are followed by
.B constructor:<interface>.
This forms a template which describes how to create ranges, based on the addresses assigned to the interface. For instance
.B --dhcp-range=::1,::4,constructor:eth0
will look for addreses 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.
The optional
.B set:<tag>
sets an alphanumeric label which marks this network so that
+20 -4
View File
@@ -46,6 +46,21 @@ static struct subnet *filter_zone(struct auth_zone *zone, int flag, struct all_a
return NULL;
}
static int filter_constructed_dhcp(struct auth_zone *zone, int flag, struct all_addr *addr_u)
{
#ifdef HAVE_DHCP6
struct dhcp_context *context;
if (flag | F_IPV6)
for (context = daemon->dhcp6; context; context = context->next)
if ((context->flags & CONTEXT_CONSTRUCTED) &&
is_same_net6(&(addr_u->addr.addr6), &context->start6, context->prefix))
return 1;
#endif
return filter_zone(zone, flag, addr_u) != NULL;
}
static int in_zone(struct auth_zone *zone, char *name, char **cut)
{
size_t namelen = strlen(name);
@@ -415,7 +430,8 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
do
{
nxdomain = 0;
if ((crecp->flags & flag) && filter_zone(zone, flag, &(crecp->addr.addr)))
if ((crecp->flags & flag) &&
(filter_constructed_dhcp(zone, flag, &(crecp->addr.addr))))
{
*cut = '.'; /* restore domain part */
log_query(crecp->flags, name, &crecp->addr.addr, record_source(crecp->uid));
@@ -438,7 +454,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
do
{
nxdomain = 0;
if ((crecp->flags & flag) && filter_zone(zone, flag, &(crecp->addr.addr)))
if ((crecp->flags & flag) && filter_constructed_dhcp(zone, flag, &(crecp->addr.addr)))
{
log_query(crecp->flags, name, &crecp->addr.addr, record_source(crecp->uid));
found = 1;
@@ -663,7 +679,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
if ((crecp->flags & F_DHCP) && !option_bool(OPT_DHCP_FQDN))
{
char *cache_name = cache_get_name(crecp);
if (!strchr(cache_name, '.') && filter_zone(zone, (crecp->flags & (F_IPV6 | F_IPV4)), &(crecp->addr.addr)))
if (!strchr(cache_name, '.') && filter_constructed_dhcp(zone, (crecp->flags & (F_IPV6 | F_IPV4)), &(crecp->addr.addr)))
{
qtype = T_A;
#ifdef HAVE_IPV6
@@ -680,7 +696,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
if ((crecp->flags & F_HOSTS) || (((crecp->flags & F_DHCP) && option_bool(OPT_DHCP_FQDN))))
{
strcpy(name, cache_get_name(crecp));
if (in_zone(zone, name, &cut) && filter_zone(zone, (crecp->flags & (F_IPV6 | F_IPV4)), &(crecp->addr.addr)))
if (in_zone(zone, name, &cut) && filter_constructed_dhcp(zone, (crecp->flags & (F_IPV6 | F_IPV4)), &(crecp->addr.addr)))
{
qtype = T_A;
#ifdef HAVE_IPV6
+2 -1
View File
@@ -147,7 +147,8 @@ int iface_enumerate(int family, void *parm, int (*callback)())
addr->s6_addr[3] = 0;
}
if (!((*callback)(addr, prefix, scope_id, iface_index, 0, 0, 0, parm)))
/* 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
+1
View File
@@ -46,6 +46,7 @@
#define SOA_REFRESH 1200 /* SOA refresh default */
#define SOA_RETRY 180 /* SOA retry default */
#define SOA_EXPIRY 1209600 /* SOA expiry default */
#define RA_INTERVAL 600 /* Send unsolicited RA's this often when not provoked. */
/* compile-time options: uncomment below to enable or do eg.
make COPTS=-DHAVE_BROKEN_RTC
+8 -3
View File
@@ -712,14 +712,19 @@ void log_context(int family, struct dhcp_context *context)
#ifdef HAVE_DHCP6
if (context->flags & CONTEXT_CONSTRUCTED)
{
n = p;
p += sprintf(p, ", constructed for %s", context->template_interface);
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", context->template_interface);
p += sprintf(p, ", template for %s%s", context->template_interface,
(context->flags & CONTEXT_WILDCARD) ? "*" : "");
}
#endif
+55 -114
View File
@@ -25,8 +25,8 @@ struct iface_param {
};
static int complete_context6(struct in6_addr *local, int prefix,
int scope, int if_index, int dad,
int preferred, int valid, void *vparam);
int scope, int if_index, int flags,
unsigned int preferred, unsigned int valid, void *vparam);
static int make_duid1(int index, unsigned int type, char *mac, size_t maclen, void *parm);
@@ -182,18 +182,15 @@ void dhcp6_packet(time_t now)
}
static int complete_context6(struct in6_addr *local, int prefix,
int scope, int if_index, int dad, int preferred,
int valid, void *vparam)
int scope, int if_index, int flags, unsigned int preferred,
unsigned int valid, void *vparam)
{
struct dhcp_context *context;
struct iface_param *param = vparam;
struct iname *tmp;
(void)scope; /* warning */
(void)dad;
(void)preferred;
(void)valid;
if (if_index == param->ind &&
!IN6_IS_ADDR_LOOPBACK(local) &&
!IN6_IS_ADDR_LINKLOCAL(local) &&
@@ -219,12 +216,34 @@ static int complete_context6(struct in6_addr *local, int prefix,
is_same_net6(local, &context->start6, prefix) &&
is_same_net6(local, &context->end6, prefix))
{
/* link it onto the current chain if we've not seen it before */
if (context->current == context)
{
context->current = param->current;
param->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;
}
}
}
@@ -475,11 +494,11 @@ static int make_duid1(int index, unsigned int type, char *mac, size_t maclen, vo
struct cparam {
time_t now;
int newone;
int newone, newname;
};
static int construct_worker(struct in6_addr *local, int prefix,
int scope, int if_index, int dad,
int scope, int if_index, int flags,
int preferred, int valid, void *vparam)
{
char ifrn_name[IFNAMSIZ];
@@ -487,7 +506,9 @@ static int construct_worker(struct in6_addr *local, int prefix,
struct dhcp_context *template, *context;
(void)scope;
(void)dad;
(void)flags;
(void)valid;
(void)preferred;
struct cparam *param = vparam;
@@ -512,8 +533,9 @@ static int construct_worker(struct in6_addr *local, int prefix,
}
}
else if (strcmp(ifrn_name, template->template_interface) == 0 &&
addr6part(local) == addr6part(&template->start6))
else if (addr6part(local) == addr6part(&template->start6) &&
strncmp(template->template_interface, ifrn_name, strlen(template->template_interface)) == 0 &&
(strlen(template->template_interface) == strlen(ifrn_name) || (template->flags & CONTEXT_WILDCARD)))
{
start6 = *local;
setaddr6part(&start6, addr6part(&template->start6));
@@ -546,22 +568,13 @@ static int construct_worker(struct in6_addr *local, int prefix,
/* we created a new one, need to call
lease_update_file to get periodic functions called */
param->newone = 1;
/* Will need to add new putative SLAAC addresses to existing leases */
if (context->flags & CONTEXT_RA_NAME)
param->newname = 1;
log_context(AF_INET6, context);
}
if (context)
{
if (valid == -1)
context->valid = valid;
else
context->valid = valid + param->now;
if (preferred == -1)
context->preferred = preferred;
else
context->preferred = preferred + param->now;
}
}
return 1;
@@ -572,14 +585,15 @@ void dhcp_construct_contexts(time_t now)
struct dhcp_context *tmp, *context, **up;
struct cparam param;
param.newone = 0;
param.newname = 0;
param.now = now;
for (context = daemon->dhcp6; context; context = context->next)
if (context->flags & CONTEXT_CONSTRUCTED)
{
context->flags |= CONTEXT_GC;
context->if_index = 0;
}
{
context->if_index = 0;
if (context->flags & CONTEXT_CONSTRUCTED)
context->flags |= CONTEXT_GC;
}
iface_enumerate(AF_INET6, &param, construct_worker);
@@ -589,10 +603,11 @@ void dhcp_construct_contexts(time_t now)
if (context->flags & CONTEXT_GC)
{
if (daemon->dhcp6 == context)
daemon->dhcp6 = context->next;
*up = context->next;
free(context);
param.newone = 1; /* include deletion */
if (context->flags & CONTEXT_RA_NAME)
param.newname = 1;
}
else
up = &context->next;
@@ -601,90 +616,16 @@ void dhcp_construct_contexts(time_t now)
if (param.newone)
{
if (daemon->dhcp || daemon->doing_dhcp6)
lease_update_file(now);
{
if (param.newname)
lease_update_slaac(now);
lease_update_file(now);
}
else
/* Not doing DHCP, so no lease system, manage alarms for ra only */
send_alarm(periodic_ra(now), now);
}
}
static int join_multicast_worker(struct in6_addr *local, int prefix,
int scope, int if_index, int dad,
int preferred, int valid, void *vparam)
{
char ifrn_name[IFNAMSIZ];
struct ipv6_mreq mreq;
int fd, i, max = *((int *)vparam);
struct iname *tmp;
(void)prefix;
(void)scope;
(void)dad;
(void)preferred;
(void)valid;
/* record which interfaces we join on, so that we do it at most one per
interface, even when they have multiple addresses. Use outpacket
as an array of int, since it's always allocated here and easy
to expand for theoretical vast numbers of interfaces. */
for (i = 0; i < max; i++)
if (if_index == ((int *)daemon->outpacket.iov_base)[i])
return 1;
if ((fd = socket(PF_INET6, SOCK_DGRAM, 0)) == -1)
return 0;
if (!indextoname(fd, if_index, ifrn_name))
{
close(fd);
return 0;
}
close(fd);
/* Are we doing DHCP on this interface? */
if (!iface_check(AF_INET6, (struct all_addr *)local, ifrn_name, NULL))
return 1;
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, ifrn_name) == 0))
return 1;
mreq.ipv6mr_interface = if_index;
inet_pton(AF_INET6, ALL_RELAY_AGENTS_AND_SERVERS, &mreq.ipv6mr_multiaddr);
if (daemon->doing_dhcp6 &&
setsockopt(daemon->dhcp6fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1)
return 0;
inet_pton(AF_INET6, ALL_SERVERS, &mreq.ipv6mr_multiaddr);
if (daemon->doing_dhcp6 &&
setsockopt(daemon->dhcp6fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1)
return 0;
inet_pton(AF_INET6, ALL_ROUTERS, &mreq.ipv6mr_multiaddr);
if (daemon->doing_ra &&
setsockopt(daemon->icmp6fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1)
return 0;
expand_buf(&daemon->outpacket, (max+1) * sizeof(int));
((int *)daemon->outpacket.iov_base)[max++] = if_index;
*((int *)vparam) = max;
return 1;
}
void join_multicast(void)
{
int count = 0;
if (!iface_enumerate(AF_INET6, &count, join_multicast_worker))
die(_("failed to join DHCPv6 multicast group: %s"), NULL, EC_BADNET);
}
#endif
+7 -7
View File
@@ -183,7 +183,7 @@ int main (int argc, char **argv)
daemon->doing_ra = 1;
#ifndef HAVE_LINUX_NETWORK
if (context->flags & CONTEXT_TEMPLATE)
die (_("dhcp-range contructor not available on this platform"), NULL, EC_BADCONF);
die (_("dhcp-range constructor not available on this platform"), NULL, EC_BADCONF);
#endif
}
}
@@ -220,12 +220,6 @@ int main (int argc, char **argv)
die(_("cannot set --bind-interfaces and --bind-dynamic"), NULL, EC_BADCONF);
#endif
#ifdef HAVE_DHCP6
/* after netlink_init */
if (daemon->doing_dhcp6 || daemon->doing_ra)
join_multicast();
#endif
if (!enumerate_interfaces())
die(_("failed to find list of interfaces: %s"), NULL, EC_MISC);
@@ -255,6 +249,12 @@ int main (int argc, char **argv)
}
else
create_wildcard_listeners();
#ifdef HAVE_DHCP6
/* after enumerate_interfaces() */
if (daemon->doing_dhcp6 || daemon->doing_ra)
join_multicast(1);
#endif
if (daemon->port != 0)
cache_init();
+13 -5
View File
@@ -389,6 +389,11 @@ union mysockaddr {
#endif
};
/* bits in flag param to IPv6 callbacks from iface_enumerate() */
#define IFACE_TENTATIVE 1
#define IFACE_DEPRECATED 2
#define SERV_FROM_RESOLV 1 /* 1 for servers from resolv, 0 for command line. */
#define SERV_NO_ADDR 2 /* no server, this domain is local only */
#define SERV_LITERAL_ADDRESS 4 /* addr is the answer, not the server */
@@ -428,8 +433,8 @@ struct server {
struct irec {
union mysockaddr addr;
struct in_addr netmask; /* only valid for IPv4 */
int tftp_ok, dhcp_ok, mtu, done, dad, dns_auth;
char *name;
int tftp_ok, dhcp_ok, mtu, done, dad, dns_auth, index, multicast_done;
char *name;
struct irec *next;
};
@@ -678,9 +683,9 @@ 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;
char *template_interface;
int valid, preferred; /* times from address for constructed contexts */
#endif
int flags;
struct dhcp_netid netid, *filter;
@@ -701,7 +706,7 @@ struct dhcp_context {
#define CONTEXT_CONSTRUCTED 2048
#define CONTEXT_GC 4096
#define CONTEXT_RA 8192
#define CONTEXT_WILDCARD 16384
struct ping_result {
struct in_addr addr;
@@ -1001,6 +1006,9 @@ struct in_addr get_ifaddr(char *intr);
#ifdef HAVE_IPV6
int set_ipv6pktinfo(int fd);
#endif
#ifdef HAVE_DHCP6
void join_multicast(int dienow);
#endif
/* dhcp.c */
#ifdef HAVE_DHCP
@@ -1040,6 +1048,7 @@ void lease6_filter(int lease_type, int iaid, struct dhcp_context *context);
struct dhcp_lease *lease6_find_by_addr(struct in6_addr *net, int prefix, u64 addr);
u64 lease_find_max_addr6(struct dhcp_context *context);
void lease_ping_reply(struct in6_addr *sender, unsigned char *packet, char *interface);
void lease_update_slaac(time_t now);
#endif
void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr,
unsigned char *clid, int hw_len, int hw_type, int clid_len, time_t now, int force);
@@ -1151,7 +1160,6 @@ struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct
int prefix, u64 addr);
void make_duid(time_t now);
void dhcp_construct_contexts(time_t now);
void join_multicast(void);
#endif
/* rfc3315.c */
+14 -2
View File
@@ -363,13 +363,13 @@ static int find_interface_v4(struct in_addr local, int if_index,
#ifdef HAVE_DHCP6
static int find_interface_v6(struct in6_addr *local, int prefix,
int scope, int if_index, int dad,
int scope, int if_index, int flags,
int preferred, int valid, void *vparam)
{
struct dhcp_lease *lease;
(void)scope;
(void)dad;
(void)flags;
(void)preferred;
(void)valid;
@@ -389,6 +389,18 @@ void lease_ping_reply(struct in6_addr *sender, unsigned char *packet, char *inte
slaac_ping_reply(sender, packet, interface, leases);
}
void lease_update_slaac(time_t now)
{
/* Called when we contruct a new RA-names context, to add putative
new SLAAC addresses to existing leases. */
struct dhcp_lease *lease;
if (daemon->dhcp)
for (lease = leases; lease; lease = lease->next)
slaac_add_addrs(lease, now, 0);
}
#endif
+34 -33
View File
@@ -39,6 +39,7 @@ static struct iovec iov;
static u32 netlink_pid;
static int nl_async(struct nlmsghdr *h);
static void nl_newaddress(time_t now);
void netlink_init(void)
{
@@ -199,23 +200,8 @@ int iface_enumerate(int family, void *parm, int (*callback)())
/* handle async new interface address arrivals, these have to be done
after we complete as we're not re-entrant */
if (newaddr)
{
time_t now = dnsmasq_time();
nl_newaddress(dnsmasq_time());
if (option_bool(OPT_CLEVERBIND))
{
enumerate_interfaces();
create_bound_listeners(0);
}
#ifdef HAVE_DHCP6
if (daemon->doing_dhcp6 || daemon->doing_ra)
dhcp_construct_contexts(now);
if (daemon->doing_dhcp6)
lease_find_interfaces(now);
#endif
}
return callback_ok;
}
else if (h->nlmsg_type == RTM_NEWADDR && family != AF_UNSPEC && family != AF_LOCAL)
@@ -253,6 +239,8 @@ int iface_enumerate(int family, void *parm, int (*callback)())
{
struct in6_addr *addrp = NULL;
u32 valid = 0, preferred = 0;
int flags = 0;
while (RTA_OK(rta, len1))
{
if (rta->rta_type == IFA_ADDRESS)
@@ -266,9 +254,15 @@ int iface_enumerate(int family, void *parm, int (*callback)())
rta = RTA_NEXT(rta, len1);
}
if (ifa->ifa_flags & IFA_F_TENTATIVE)
flags |= IFACE_TENTATIVE;
if (ifa->ifa_flags & IFA_F_DEPRECATED)
flags |= IFACE_DEPRECATED;
if (addrp && callback_ok)
if (!((*callback)(addrp, (int)(ifa->ifa_prefixlen), (int)(ifa->ifa_scope),
(int)(ifa->ifa_index), (int)(ifa->ifa_flags & IFA_F_TENTATIVE),
(int)(ifa->ifa_index), flags,
(int) preferred, (int)valid, parm)))
callback_ok = 0;
}
@@ -346,23 +340,9 @@ void netlink_multicast(time_t now)
/* restore non-blocking status */
fcntl(daemon->netlinkfd, F_SETFL, flags);
if (newaddr)
{
if (option_bool(OPT_CLEVERBIND))
{
enumerate_interfaces();
create_bound_listeners(0);
}
#ifdef HAVE_DHCP6
if (daemon->doing_dhcp6 || daemon->doing_ra)
dhcp_construct_contexts(now);
if (daemon->doing_dhcp6)
lease_find_interfaces(now);
#endif
}
nl_newaddress(now);
}
static int nl_async(struct nlmsghdr *h)
@@ -410,7 +390,28 @@ static int nl_async(struct nlmsghdr *h)
return 0;
}
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))
create_bound_listeners(0);
#ifdef HAVE_DHCP6
if (daemon->doing_dhcp6 || daemon->doing_ra)
{
join_multicast(0);
dhcp_construct_contexts(now);
}
if (daemon->doing_dhcp6)
lease_find_interfaces(now);
#endif
}
#endif
+60 -3
View File
@@ -272,7 +272,8 @@ static int iface_allowed(struct irec **irecp, int if_index,
iface->dns_auth = auth_dns;
iface->mtu = mtu;
iface->dad = dad;
iface->done = 0;
iface->done = iface->multicast_done = 0;
iface->index = if_index;
if ((iface->name = whine_malloc(strlen(ifr.ifr_name)+1)))
{
strcpy(iface->name, ifr.ifr_name);
@@ -281,6 +282,7 @@ static int iface_allowed(struct irec **irecp, int if_index,
return 1;
}
free(iface);
}
errno = ENOMEM;
@@ -289,7 +291,7 @@ static int iface_allowed(struct irec **irecp, int if_index,
#ifdef HAVE_IPV6
static int iface_allowed_v6(struct in6_addr *local, int prefix,
int scope, int if_index, int dad,
int scope, int if_index, int flags,
int preferred, int valid, void *vparam)
{
union mysockaddr addr;
@@ -310,7 +312,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, &addr, netmask, dad);
return iface_allowed((struct irec **)vparam, if_index, &addr, netmask, !!(flags & IFACE_TENTATIVE));
}
#endif
@@ -587,6 +589,61 @@ int is_dad_listeners(void)
return 0;
}
#ifdef HAVE_DHCP6
void join_multicast(int dienow)
{
struct irec *iface, *tmp;
for (iface = daemon->interfaces; iface; iface = iface->next)
if (iface->addr.sa.sa_family == AF_INET6 && iface->dhcp_ok && !iface->multicast_done)
{
/* There's an irec per address but we only want to join for multicast
once per interface. Weed out duplicates. */
for (tmp = daemon->interfaces; tmp; tmp = tmp->next)
if (tmp->multicast_done && tmp->index == iface->index)
break;
iface->multicast_done = 1;
if (!tmp)
{
struct ipv6_mreq mreq;
int err = 0;
mreq.ipv6mr_interface = iface->index;
inet_pton(AF_INET6, ALL_RELAY_AGENTS_AND_SERVERS, &mreq.ipv6mr_multiaddr);
if (daemon->doing_dhcp6 &&
setsockopt(daemon->dhcp6fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1)
err = 1;
inet_pton(AF_INET6, ALL_SERVERS, &mreq.ipv6mr_multiaddr);
if (daemon->doing_dhcp6 &&
setsockopt(daemon->dhcp6fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1)
err = 1;
inet_pton(AF_INET6, ALL_ROUTERS, &mreq.ipv6mr_multiaddr);
if (daemon->doing_ra &&
setsockopt(daemon->icmp6fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1)
err = 1;
if (err)
{
char *s = _("interface %s failed to join DHCPv6 multicast group: %s");
if (dienow)
die(s, iface->name, EC_BADNET);
else
my_syslog(LOG_ERR, s, iface->name, strerror(errno));
}
}
}
}
#endif
/* return a UDP socket bound to a random port, have to cope with straying into
occupied port nos and reserved ones. */
int random_sock(int family)
+13 -3
View File
@@ -1587,9 +1587,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
struct auth_zone *new;
comma = split(arg);
if (!comma)
ret_err(gen_err);
new = opt_malloc(sizeof(struct auth_zone));
new->domain = opt_string_alloc(arg);
new->subnet = NULL;
@@ -2293,6 +2291,11 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
new->flags |= CONTEXT_DHCP;
else if (strstr(a[leasepos], "constructor:") == a[leasepos])
{
if (a[leasepos][strlen(a[leasepos])-1] == '*')
{
a[leasepos][strlen(a[leasepos])-1] = 0;
new->flags |= CONTEXT_WILDCARD;
}
new->template_interface = opt_string_alloc(a[leasepos] + 12);
new->flags |= CONTEXT_TEMPLATE;
}
@@ -2371,6 +2374,13 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
a[leasepos][strlen(a[leasepos]) - 1] = 0;
}
for (cp = a[leasepos]; *cp; cp++)
if (!(*cp >= '0' && *cp <= '9'))
break;
if (!cp || (leasepos+1 < k))
ret_err(_("bad dhcp-range"));
new->lease_time = atoi(a[leasepos]) * fac;
/* Leases of a minute or less confuse
some clients, notably Apple's */
+68 -52
View File
@@ -31,7 +31,8 @@ struct ra_param {
int ind, managed, other, found_context, first;
char *if_name;
struct dhcp_netid *tags;
struct in6_addr link_local;
struct in6_addr link_local, link_global;
unsigned int pref_time;
};
struct search_param {
@@ -40,10 +41,10 @@ struct search_param {
static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *dest);
static int add_prefixes(struct in6_addr *local, int prefix,
int scope, int if_index, int dad,
int preferred, int valid, void *vparam);
int scope, int if_index, int flags,
unsigned int preferred, unsigned int valid, void *vparam);
static int iface_search(struct in6_addr *local, int prefix,
int scope, int if_index, int dad,
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);
@@ -192,7 +193,10 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de
struct dhcp_netid iface_id;
struct dhcp_opt *opt_cfg;
int done_dns = 0;
#ifdef HAVE_LINUX_NETWORK
FILE *f;
#endif
save_counter(0);
ra = expand(sizeof(struct ra_packet));
@@ -200,7 +204,7 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de
ra->code = 0;
ra->hop_limit = hop_limit;
ra->flags = 0x00;
ra->lifetime = htons(1800); /* AdvDefaultLifetime*/
ra->lifetime = htons(RA_INTERVAL * 3); /* AdvDefaultLifetime * 3 */
ra->reachable_time = 0;
ra->retrans_time = 0;
@@ -211,6 +215,7 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de
parm.if_name = iface_name;
parm.first = 1;
parm.now = now;
parm.pref_time = 0;
/* set tag with name == interface */
iface_id.net = iface_name;
@@ -228,14 +233,23 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de
return;
strncpy(ifr.ifr_name, iface_name, IF_NAMESIZE);
if (ioctl(daemon->icmp6fd, SIOCGIFMTU, &ifr) != -1)
#ifdef HAVE_LINUX_NETWORK
/* Note that IPv6 MTU is not necessarilly the same as the IPv4 MTU
available from SIOCGIFMTU */
sprintf(daemon->namebuff, "/proc/sys/net/ipv6/conf/%s/mtu", iface_name);
if ((f = fopen(daemon->namebuff, "r")))
{
put_opt6_char(ICMP6_OPT_MTU);
put_opt6_char(1);
put_opt6_short(0);
put_opt6_long(ifr.ifr_mtu);
if (fgets(daemon->namebuff, MAXDNAME, f))
{
put_opt6_char(ICMP6_OPT_MTU);
put_opt6_char(1);
put_opt6_short(0);
put_opt6_long(atoi(daemon->namebuff));
}
fclose(f);
}
#endif
iface_enumerate(AF_LOCAL, &iface, add_lla);
@@ -261,11 +275,11 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de
put_opt6_char(ICMP6_OPT_RDNSS);
put_opt6_char((opt_cfg->len/8) + 1);
put_opt6_short(0);
put_opt6_long(1800); /* lifetime - twice RA retransmit */
put_opt6_long(RA_INTERVAL * 2); /* lifetime - twice RA retransmit */
/* zero means "self" */
for (i = 0; i < opt_cfg->len; i += IN6ADDRSZ, a++)
if (IN6_IS_ADDR_UNSPECIFIED(a))
put_opt6(&parm.link_local, IN6ADDRSZ);
put_opt6(&parm.link_global, IN6ADDRSZ);
else
put_opt6(a, IN6ADDRSZ);
}
@@ -292,8 +306,8 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de
put_opt6_char(ICMP6_OPT_RDNSS);
put_opt6_char(3);
put_opt6_short(0);
put_opt6_long(1800); /* lifetime - twice RA retransmit */
put_opt6(&parm.link_local, IN6ADDRSZ);
put_opt6_long(RA_INTERVAL * 2); /* lifetime - twice RA retransmit */
put_opt6(&parm.link_global, IN6ADDRSZ);
}
/* set managed bits unless we're providing only RA on this link */
@@ -325,16 +339,13 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de
}
static int add_prefixes(struct in6_addr *local, int prefix,
int scope, int if_index, int dad,
int preferred, int valid, void *vparam)
int scope, int if_index, int flags,
unsigned int preferred, unsigned int valid, void *vparam)
{
struct ra_param *param = vparam;
(void)scope; /* warning */
(void)dad;
(void)preferred;
(void)valid;
if (if_index == param->ind)
{
if (IN6_IS_ADDR_LINKLOCAL(local))
@@ -345,9 +356,8 @@ static int add_prefixes(struct in6_addr *local, int prefix,
int do_prefix = 0;
int do_slaac = 0;
int deprecate = 0;
int found_constructed = 0;
int constructed = 0;
unsigned int time = 0xffffffff;
int calc_valid = 0, calc_preferred = 0;
struct dhcp_context *context;
for (context = daemon->dhcp6; context; context = context->next)
@@ -376,23 +386,20 @@ static int add_prefixes(struct in6_addr *local, int prefix,
param->other = 1;
}
if (context->flags & CONTEXT_CONSTRUCTED)
{
found_constructed = 1;
calc_valid = context->valid;
calc_preferred = context->preferred;
if (context->valid != -1)
calc_valid -= (int)param->now;
if (context->preferred != -1)
calc_preferred -= (int)param->now;
}
/* find floor time */
/* find floor time, don't reduce below RA interval. */
if (time > context->lease_time)
time = context->lease_time;
{
time = context->lease_time;
if (time < ((unsigned int)RA_INTERVAL))
time = RA_INTERVAL;
}
if (context->flags & CONTEXT_DEPRECATE)
deprecate = 1;
if (context->flags & CONTEXT_CONSTRUCTED)
constructed = 1;
/* collect dhcp-range tags */
if (context->netid.next == &context->netid && context->netid.net)
@@ -417,10 +424,24 @@ static int add_prefixes(struct in6_addr *local, int prefix,
param->found_context = 1;
}
if (!found_constructed)
/* configured time is ceiling */
if (!constructed || valid > time)
valid = time;
if (flags & IFACE_DEPRECATED)
preferred = 0;
if (deprecate)
time = 0;
/* configured time is ceiling */
if (!constructed || preferred > time)
preferred = time;
if (preferred > param->pref_time)
{
calc_valid = time;
calc_preferred = deprecate ? 0 : time;
param->pref_time = preferred;
param->link_global = *local;
}
if (do_prefix)
@@ -432,17 +453,13 @@ static int add_prefixes(struct in6_addr *local, int prefix,
/* zero net part of address */
setaddr6part(local, addr6part(local) & ~((prefix == 64) ? (u64)-1LL : (1LLU << (128 - prefix)) - 1LLU));
/* lifetimes must be min 2 hrs, by RFC 2462 */
if (time < 7200)
time = 7200;
opt->type = ICMP6_OPT_PREFIX;
opt->len = 4;
opt->prefix_len = prefix;
/* autonomous only if we're not doing dhcp, always set "on-link" */
opt->flags = do_slaac ? 0xC0 : 0x80;
opt->valid_lifetime = htonl(calc_valid);
opt->preferred_lifetime = htonl(calc_preferred);
opt->valid_lifetime = htonl(valid);
opt->preferred_lifetime = htonl(preferred);
opt->reserved = 0;
opt->prefix = *local;
@@ -526,14 +543,13 @@ time_t periodic_ra(time_t now)
}
static int iface_search(struct in6_addr *local, int prefix,
int scope, int if_index, int dad,
int scope, int if_index, int flags,
int preferred, int valid, void *vparam)
{
struct search_param *param = vparam;
struct dhcp_context *context;
(void)scope;
(void)dad;
(void)preferred;
(void)valid;
@@ -549,15 +565,15 @@ static int iface_search(struct in6_addr *local, int prefix,
timeout value and arrange for RA to be sent unless interface is
still doing DAD.*/
if (!dad)
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 450 - 600 */
context->ra_time = param->now + 450 + (rand16()/440);
/* range 3/4 - 1 times RA_INTERVAL */
context->ra_time = param->now + (3 * RA_INTERVAL)/4 + ((RA_INTERVAL * (unsigned int)rand16()) >> 18);
/* zero timers for other contexts on the same subnet, so they don't timeout
independently */
+10 -3
View File
@@ -625,10 +625,16 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
hostname = NULL;
}
}
if (have_config(valid_config, CONFIG_TIME))
lease_time = valid_config->lease_time;
else
lease_time = this_context->lease_time;
lease_time = have_config(valid_config, CONFIG_TIME) ? valid_config->lease_time : this_context->lease_time;
if (this_context->valid < lease_time)
lease_time = this_context->valid;
if (ia_option)
if (ia_option)
{
if (requested_time < 120u )
requested_time = 120u; /* sanity */
@@ -740,7 +746,8 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
o1 = new_opt6(OPTION6_IAADDR);
put_opt6(addrp, sizeof(*addrp));
/* preferred lifetime */
put_opt6_long(this_context && (this_context->flags & CONTEXT_DEPRECATE) ? 0 : lease_time);
put_opt6_long(this_context && (this_context->preferred < lease_time) ?
this_context->preferred : lease_time);
put_opt6_long(lease_time); /* valid lifetime */
end_opt6(o1);