# an explicit netmask instead.
#dhcp-range=192.168.0.0,static
+# Enable DHCPv6. Note that the prefix-length does not need to be specified
+# and defaults to 64 if missing/
+#dhcp-range=1234::2, 1234::500, 64, 12h
+
+# Not Router Advertisements, BUT NOT DHCP for this subnet.
+#dhcp-range=1234::, ra-only
+
# Supply parameters for specified hosts using DHCP. There are lots
# of valid alternatives, so we will give examples of each. Note that
# IP addresses DO NOT have to be in the range given above, they just
# any machine with Ethernet address starting 11:22:33:
#dhcp-host=11:22:33:*:*:*,set:red
+# Give a fixed IPv6 address and name to client with
+# DUID 00:01:00:01:16:d2:83:fc:92:d4:19:e2:d8:b2
+# Note the MAC addresses CANNOT be used to identify DHCPv6 clients.
+# Note also the they [] around the IPv6 address are obilgatory.
+#dhcp-host=id:00:01:00:01:16:d2:83:fc:92:d4:19:e2:d8:b2, fred, [1234::5]
+
# Ignore any clients which are not specified in dhcp-host lines
# or /etc/ethers. Equivalent to ISC "deny unknown-clients".
# This relies on the special "known" tag which is set when
# Set the NTP time server addresses to 192.168.0.4 and 10.10.0.5
#dhcp-option=option:ntp-server,192.168.0.4,10.10.0.5
+# Send DHCPv6 option. Note [] around IPv6 addresses.
+dhcp-option=option6:dns-server,[1234::77],[1234::88]
+
# Set the NTP time server address to be the same machine as
# is running dnsmasq
#dhcp-option=42,0.0.0.0
}
+#ifdef HAVE_DHCP6
+static int join_multicast_worker(struct in6_addr *local, int prefix,
+ int scope, int if_index, int dad, void *vparam)
+{
+ char ifrn_name[IFNAMSIZ];
+ struct ipv6_mreq mreq;
+ int fd, i, max = *((int *)vparam);
+ struct dhcp_context *context;
+ struct iname *tmp;
+
+ (void)prefix;
+ (void)scope;
+ (void)dad;
+
+ /* record which interfaces we join on, so that we do it at most one per
+ interface, even when they have multiple addresses. 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))
+ return 1;
+
+ for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
+ if (tmp->name && (strcmp(tmp->name, ifrn_name) == 0))
+ return 1;
+
+ /* weird libvirt-inspired access control */
+ for (context = daemon->dhcp6; context; context = context->next)
+ if (!context->interface || strcmp(context->interface, ifrn_name) == 0)
+ break;
+
+ if (!context)
+ return 1;
+
+ mreq.ipv6mr_interface = if_index;
+
+ inet_pton(AF_INET6, ALL_RELAY_AGENTS_AND_SERVERS, &mreq.ipv6mr_multiaddr);
+
+ if (daemon->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->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->ra_contexts &&
+ 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
+
#endif
int ind;
};
-struct listen_param {
- int fd_or_iface;
- struct listen_param *next;
-};
-
-static int join_multicast(struct in6_addr *local, int prefix,
- int scope, int if_index, int dad, void *vparam);
-
static int complete_context6(struct in6_addr *local, int prefix,
int scope, int if_index, int dad, void *vparam);
{
int fd;
struct sockaddr_in6 saddr;
- struct listen_param *listenp, listen;
#if defined(IP_TOS) && defined(IPTOS_CLASS_CS6)
int class = IPTOS_CLASS_CS6;
#endif
if (bind(fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in6)))
die(_("failed to bind DHCPv6 server socket: %s"), NULL, EC_BADNET);
- /* join multicast groups on each interface we're interested in */
- listen.fd_or_iface = fd;
- listen.next = NULL;
- if (!iface_enumerate(AF_INET6, &listen, join_multicast))
- die(_("failed to join DHCPv6 multicast group: %s"), NULL, EC_BADNET);
- for (listenp = listen.next; listenp; )
- {
- struct listen_param *tmp = listenp->next;
- free(listenp);
- listenp = tmp;
- }
-
daemon->dhcp6fd = fd;
}
-static int join_multicast(struct in6_addr *local, int prefix,
- int scope, int if_index, int dad, void *vparam)
-{
- char ifrn_name[IFNAMSIZ];
- struct ipv6_mreq mreq;
- struct listen_param *listenp, *param = vparam;
- int fd = param->fd_or_iface;
- struct dhcp_context *context;
- struct iname *tmp;
-
- (void)prefix;
- (void)scope;
- (void)dad;
-
- /* record which interfaces we join on, so
- that we do it at most one per interface, even when they
- have multiple addresses */
- for (listenp = param->next; listenp; listenp = listenp->next)
- if (if_index == listenp->fd_or_iface)
- return 1;
-
- if (!indextoname(fd, if_index, ifrn_name))
- return 0;
-
- /* Are we doing DHCP on this interface? */
- if (!iface_check(AF_INET6, (struct all_addr *)local, ifrn_name))
- return 1;
-
- for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
- if (tmp->name && (strcmp(tmp->name, ifrn_name) == 0))
- return 1;
-
- /* weird libvirt-inspired access control */
- for (context = daemon->dhcp6; context; context = context->next)
- if (!context->interface || strcmp(context->interface, ifrn_name) == 0)
- break;
-
- if (!context)
- return 1;
-
- mreq.ipv6mr_interface = if_index;
-
- inet_pton(AF_INET6, ALL_RELAY_AGENTS_AND_SERVERS, &mreq.ipv6mr_multiaddr);
-
- if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1)
- return 0;
-
- inet_pton(AF_INET6, ALL_SERVERS, &mreq.ipv6mr_multiaddr);
-
- if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1)
- return 0;
-
- inet_pton(AF_INET6, ALL_ROUTERS, &mreq.ipv6mr_multiaddr);
-
- if (daemon->icmp6fd != -1 &&
- setsockopt(daemon->icmp6fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1)
- return 0;
-
- listenp = whine_malloc(sizeof(struct listen_param));
- listenp->fd_or_iface = if_index;
- listenp->next = param->next;
- param->next = listenp;
-
- return 1;
-}
-
-
void dhcp6_packet(time_t now)
{
struct dhcp_context *context;
before lease_init to allocate buffers it uses.*/
dhcp_common_init();
lease_init(now);
+
if (daemon->dhcp)
dhcp_init();
-#ifdef HAVE_DHCP6
- daemon->icmp6fd = -1;
- if (daemon->dhcp6)
+ }
+
+# ifdef HAVE_DHCP6
+ /* Start RA subsystem if --enable-ra OR dhcp-range=<subnet>, ra-only */
+ if (daemon->ra_contexts || option_bool(OPT_RA))
+ {
+ /* link the DHCP6 contexts to the ra-only ones so we can traverse them all
+ from ->ra_contexts, but only the non-ra-onlies from ->dhcp6 */
+ struct dhcp_context *context;
+
+ if (!daemon->ra_contexts)
+ daemon->ra_contexts = daemon->dhcp6;
+ else
{
- /* ra_init before dhcp6_init, so dhcp6_init can setup multicast listening */
- if (option_bool(OPT_RA))
- ra_init(now);
- dhcp6_init();
+ for (context = daemon->ra_contexts; context->next; context = context->next);
+ context->next = daemon->dhcp6;
}
-#endif
+ ra_init(now);
}
+
+ if (daemon->dhcp6)
+ dhcp6_init();
+
+ if (daemon->ra_contexts || daemon->dhcp6)
+ join_multicast();
+# endif
+
#endif
if (!enumerate_interfaces())
die(_("failed to find list of interfaces: %s"), NULL, EC_MISC);
-
+
if (option_bool(OPT_NOWILD))
{
create_bound_listeners(1);
#if defined(HAVE_SCRIPT)
/* Note getpwnam returns static storage */
- if ((daemon->dhcp || daemon->dhcp6) && daemon->scriptuser &&
+ if ((daemon->dhcp || daemon->dhcp6) &&
+ daemon->scriptuser &&
(daemon->lease_change_command || daemon->luascript))
{
if ((ent_pw = getpwnam(daemon->scriptuser)))
if (daemon->max_logs != 0)
my_syslog(LOG_INFO, _("asynchronous logging enabled, queue limit is %d messages"), daemon->max_logs);
- if (option_bool(OPT_RA))
+ if (daemon->ra_contexts)
my_syslog(MS_DHCP | LOG_INFO, _("IPv6 router advertisement enabled"));
#ifdef HAVE_DHCP
FD_SET(daemon->dhcp6fd, &rset);
bump_maxfd(daemon->dhcp6fd, &maxfd);
- if (daemon->icmp6fd != -1)
+ if (daemon->ra_contexts)
{
FD_SET(daemon->icmp6fd, &rset);
bump_maxfd(daemon->icmp6fd, &maxfd);
if (FD_ISSET(daemon->dhcp6fd, &rset))
dhcp6_packet(now);
- if (daemon->icmp6fd != -1 && FD_ISSET(daemon->icmp6fd, &rset))
+ if (daemon->ra_contexts && FD_ISSET(daemon->icmp6fd, &rset))
icmp6_packet();
}
#endif
lease_prune(NULL, now);
lease_update_file(now);
}
+#ifdef HAVE_DHCP6
+ else if (daemon->ra_contexts)
+ {
+ /* Not doing DHCP, so no lease system, manage
+ alarms for ra only */
+ time_t next_event = periodic_ra(now);
+ if (next_event != 0)
+ alarm((unsigned)difftime(next_event, now));
+ }
+#endif
#endif
break;
lease_update_file(now);
lease_update_dns();
}
+#ifdef HAVE_DHCP6
+ else if (daemon->ra_contexts)
+ {
+ /* Not doing DHCP, so no lease system, manage
+ alarms for ra only */
+ time_t next_event = periodic_ra(now);
+ if (next_event != 0)
+ alarm((unsigned)difftime(next_event, now));
+ }
+#endif
#endif
}
set_log_writer(&wset, &maxfd);
#ifdef HAVE_DHCP6
- if (daemon->icmp6fd != -1)
+ if (daemon->ra_contexts)
{
FD_SET(daemon->icmp6fd, &rset);
bump_maxfd(daemon->icmp6fd, &maxfd);
check_dns_listeners(&rset, now);
#ifdef HAVE_DHCP6
- if (daemon->icmp6fd != -1 && FD_ISSET(daemon->icmp6fd, &rset))
+ if (daemon->ra_contexts && FD_ISSET(daemon->icmp6fd, &rset))
icmp6_packet();
#endif
int match_bytes(struct dhcp_opt *o, unsigned char *p, int len);
void dhcp_update_configs(struct dhcp_config *configs);
void check_dhcp_hosts(int fatal);
+# ifdef HAVE_DHCP6
+void join_multicast(void);
+# endif
#endif
/* outpacket.c */
#ifdef HAVE_DHCP6
/* do timed RAs and determine when the next is */
- if (option_bool(OPT_RA))
+ if (daemon->ra_contexts)
next_event = periodic_ra(now);
#endif
#ifdef HAVE_DHCP6
/* force RAs to sync new network and pick up new interfaces. */
- if (option_bool(OPT_RA))
+ if (daemon->ra_contexts)
{
ra_start_unsolicted(dnsmasq_time());
/* cause lease_update_file to run after we return, in case we were called from
*/
-/* NB. This code may be called during a DHCPv4 transaction which is in ping-wait
+/* NB. This code may be called during a DHCPv4 or transaction which is in ping-wait
It therefore cannot use any DHCP buffer resources except outpacket, which is
- not used by DHCPv4 code. */
+ not used by DHCPv4 code. This code may also be called when DHCP 4 or 6 isn't
+ active, so we ensure that outpacket is allocated here too */
#include "dnsmasq.h"
void ra_init(time_t now)
{
- struct dhcp_context *context;
struct icmp6_filter filter;
int fd;
#if defined(IP_TOS) && defined(IPTOS_CLASS_CS6)
int val = 255; /* radvd uses this value */
size_t len = sizeof(int);
+ /* ensure this is around even if we're not doing DHCPv6 */
+ expand_buf(&daemon->outpacket, sizeof(struct dhcp_packet));
+
ICMP6_FILTER_SETBLOCKALL(&filter);
ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filter);
ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filter);
daemon->icmp6fd = fd;
- /* link the DHCP6 contexts to the ra-only ones so we can traverse them all
- from ->ra_contexts, but only the non-ra-onlies from ->dhcp6 */
- if (!daemon->ra_contexts)
- daemon->ra_contexts = daemon->dhcp6;
- else
- {
- for (context = daemon->ra_contexts; context->next; context = context->next);
- context->next = daemon->dhcp6;
- }
-
- if (!daemon->dhcp6)
- die(_("cannot do router advertisement unless DHCPv6 is enabled"), NULL, EC_BADCONF);
-
ra_start_unsolicted(now);
}