From 5d162f20a95fc4216cb07906ed2f73fc7829ede0 Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Thu, 20 Dec 2012 14:55:46 +0000 Subject: [PATCH] Rationalise join_multicast() --- src/dhcp6.c | 78 --------------------------------------------------- src/dnsmasq.c | 12 ++++---- src/dnsmasq.h | 8 ++++-- src/netlink.c | 57 +++++++++++++++++-------------------- src/network.c | 59 +++++++++++++++++++++++++++++++++++++- 5 files changed, 94 insertions(+), 120 deletions(-) diff --git a/src/dhcp6.c b/src/dhcp6.c index bf8e960..af6b404 100644 --- a/src/dhcp6.c +++ b/src/dhcp6.c @@ -607,84 +607,6 @@ void dhcp_construct_contexts(time_t now) 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 diff --git a/src/dnsmasq.c b/src/dnsmasq.c index 4c7cd39..17dffa4 100644 --- a/src/dnsmasq.c +++ b/src/dnsmasq.c @@ -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(); diff --git a/src/dnsmasq.h b/src/dnsmasq.h index 0ae3621..43f5968 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -428,8 +428,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; }; @@ -1001,6 +1001,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 @@ -1151,7 +1154,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 */ diff --git a/src/netlink.c b/src/netlink.c index 1f1c658..4b9ad14 100644 --- a/src/netlink.c +++ b/src/netlink.c @@ -39,6 +39,7 @@ static struct iovec iov; static u32 netlink_pid; static int nl_async(struct nlmsghdr *h); +static void nl_newinterface(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_newinterface(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) @@ -346,23 +332,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_newinterface(now); } static int nl_async(struct nlmsghdr *h) @@ -410,7 +382,28 @@ static int nl_async(struct nlmsghdr *h) return 0; } + +static void nl_newinterface(time_t now) +{ + 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); + join_multicast(0); + } + + if (daemon->doing_dhcp6) + lease_find_interfaces(now); +#endif +} + + #endif diff --git a/src/network.c b/src/network.c index c534092..266edd5 100644 --- a/src/network.c +++ b/src/network.c @@ -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; @@ -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->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)