Make RA without DHCPv6 possible.

This commit is contained in:
Simon Kelley
2012-02-27 17:42:38 +00:00
parent 58dc02ebf2
commit 843c96b4b3
8 changed files with 168 additions and 123 deletions

View File

@@ -358,4 +358,90 @@ void dhcp_update_configs(struct dhcp_config *configs)
}
#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

View File

@@ -24,14 +24,6 @@ struct iface_param {
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);
@@ -41,7 +33,6 @@ void dhcp6_init(void)
{
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
@@ -65,88 +56,9 @@ void dhcp6_init(void)
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;

View File

@@ -154,24 +154,41 @@ int main (int argc, char **argv)
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)
{
/* ra_init before dhcp6_init, so dhcp6_init can setup multicast listening */
if (option_bool(OPT_RA))
ra_init(now);
dhcp6_init();
}
#endif
}
# 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
{
for (context = daemon->ra_contexts; context->next; context = context->next);
context->next = daemon->dhcp6;
}
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);
@@ -211,7 +228,8 @@ int main (int argc, char **argv)
#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)))
@@ -502,7 +520,7 @@ int main (int argc, char **argv)
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
@@ -668,7 +686,7 @@ int main (int argc, char **argv)
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);
@@ -777,7 +795,7 @@ int main (int argc, char **argv)
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
@@ -953,6 +971,16 @@ static void async_event(int pipe, time_t now)
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;
@@ -1117,6 +1145,16 @@ void clear_cache_and_reload(time_t now)
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
}
@@ -1402,7 +1440,7 @@ int icmp_ping(struct in_addr addr)
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);
@@ -1421,7 +1459,7 @@ int icmp_ping(struct in_addr addr)
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

View File

@@ -1059,6 +1059,9 @@ void log_tags(struct dhcp_netid *netid, u32 xid);
int match_bytes(struct dhcp_opt *o, unsigned char *p, int len);
void dhcp_update_configs(struct dhcp_config *configs);
void check_dhcp_hosts(int fatal);
# ifdef HAVE_DHCP6
void join_multicast(void);
# endif
#endif
/* outpacket.c */

View File

@@ -317,7 +317,7 @@ void lease_update_file(time_t now)
#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

View File

@@ -343,7 +343,7 @@ static void nl_routechange(struct nlmsghdr *h)
#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

View File

@@ -15,9 +15,10 @@
*/
/* 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"
@@ -47,7 +48,6 @@ static time_t ra_short_period_start;
void ra_init(time_t now)
{
struct dhcp_context *context;
struct icmp6_filter filter;
int fd;
#if defined(IP_TOS) && defined(IPTOS_CLASS_CS6)
@@ -56,6 +56,9 @@ void ra_init(time_t now)
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);
@@ -74,19 +77,6 @@ void ra_init(time_t now)
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);
}