diff --git a/src/bpf.c b/src/bpf.c index b6fbd90..378817a 100644 --- a/src/bpf.c +++ b/src/bpf.c @@ -147,7 +147,7 @@ int iface_enumerate(int family, void *parm, int (*callback)()) addr->s6_addr[3] = 0; } - if (!((*callback)(addr, prefix, scope_id, iface_index, 0, parm))) + if (!((*callback)(addr, prefix, scope_id, iface_index, 0, 0, 0, parm))) goto err; } #endif diff --git a/src/dhcp-common.c b/src/dhcp-common.c index 30aad56..0da1bd3 100644 --- a/src/dhcp-common.c +++ b/src/dhcp-common.c @@ -333,83 +333,6 @@ 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 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, 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->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 - #ifdef HAVE_LINUX_NETWORK void bindtodevice(int fd) { @@ -750,4 +673,75 @@ char *option_string(int prot, unsigned int opt, unsigned char *val, int opt_len, } +void log_context(int family, struct dhcp_context *context) +{ + /* Cannot use dhcp_buff* for RA contexts */ + + void *start = &context->start; + void *end = &context->end; + +#ifdef HAVE_DHCP6 + if (family == AF_INET6) + { + struct in6_addr subnet = context->start6; + if (!(context->flags & CONTEXT_TEMPLATE)) + setaddr6part(&subnet, 0); + inet_ntop(AF_INET6, &subnet, daemon->addrbuff, ADDRSTRLEN); + start = &context->start6; + end = &context->end6; + } +#endif + + if (context->flags & CONTEXT_CONSTRUCTED) + sprintf(daemon->namebuff, "constructed for %s", context->template_interface); + else if (context->flags & CONTEXT_TEMPLATE) + sprintf(daemon->namebuff, "template for %s", context->template_interface); + else + { + if (family != AF_INET && (context->flags & CONTEXT_DEPRECATE)) + strcpy(daemon->namebuff, _("prefix deprecated")); + else + { + char *p = daemon->namebuff; + + p += sprintf(p, _("lease time ")); + prettyprint_time(p, context->lease_time); + } + } + + 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") : + (context->flags & CONTEXT_STATIC) ? + _("%s, static leases only on %.0s%s, %s") : + (context->flags & CONTEXT_PROXY) ? + _("%s, proxy on subnet %.0s%s%.0s") : + _("%s, IP range %s -- %s, %s"), + (family != AF_INET) ? "DHCPv6" : "DHCP", + daemon->dhcp_buff, daemon->dhcp_buff3, daemon->namebuff); + } + + if (context->flags & CONTEXT_RA_NAME) + my_syslog(MS_DHCP | LOG_INFO, _("DHCPv4-derived IPv6 names on %s"), + daemon->addrbuff); + + + if (context->flags & (CONTEXT_RA_ONLY | CONTEXT_RA_NAME | CONTEXT_RA_STATELESS)) + { + if (!(context->flags & (CONTEXT_DEPRECATE | CONTEXT_CONSTRUCTED | CONTEXT_TEMPLATE))) + { + char *p = daemon->namebuff; + p += sprintf(p, _("prefix valid ")); + prettyprint_time(p, context->lease_time > 7200 ? context->lease_time : 7200); + } + my_syslog(MS_DHCP | LOG_INFO, _("router advertisement on %s %s"), + daemon->addrbuff, daemon->namebuff); + } +} + + #endif diff --git a/src/dhcp6.c b/src/dhcp6.c index ba53f0f..48d0d50 100644 --- a/src/dhcp6.c +++ b/src/dhcp6.c @@ -25,7 +25,8 @@ struct iface_param { }; static int complete_context6(struct in6_addr *local, int prefix, - int scope, int if_index, int dad, void *vparam); + int scope, int if_index, int dad, + int preferred, int valid, void *vparam); static int make_duid1(int index, unsigned int type, char *mac, size_t maclen, void *parm); @@ -181,7 +182,8 @@ void dhcp6_packet(time_t now) } static int complete_context6(struct in6_addr *local, int prefix, - int scope, int if_index, int dad, void *vparam) + int scope, int if_index, int dad, int preferred, + int valid, void *vparam) { struct dhcp_context *context; struct iface_param *param = vparam; @@ -189,6 +191,8 @@ static int complete_context6(struct in6_addr *local, int prefix, (void)scope; /* warning */ (void)dad; + (void)preferred; + (void)valid; if (if_index == param->ind && !IN6_IS_ADDR_LOOPBACK(local) && @@ -467,6 +471,212 @@ static int make_duid1(int index, unsigned int type, char *mac, size_t maclen, vo return 0; } + +struct cparam { + time_t now; + int newone; +}; + +static int construct_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 in6_addr start6, end6; + struct dhcp_context *template, *context; + + (void)scope; + (void)dad; + + struct cparam *param = vparam; + + if (IN6_IS_ADDR_LOOPBACK(local) || + IN6_IS_ADDR_LINKLOCAL(local) || + IN6_IS_ADDR_MULTICAST(local)) + return 1; + + if (!indextoname(daemon->doing_dhcp6 ? daemon->dhcp6fd : daemon->icmp6fd, if_index, ifrn_name)) + return 0; + + for (template = daemon->dhcp6; template; template = template->next) + if (!(template->flags & CONTEXT_TEMPLATE)) + { + /* non-template entries, just fill in interface and local addresses */ + if (prefix == template->prefix && + is_same_net6(local, &template->start6, prefix) && + is_same_net6(local, &template->end6, prefix)) + { + template->if_index = if_index; + template->local6 = *local; + } + + } + else if (strcmp(ifrn_name, template->template_interface) == 0 && + addr6part(local) == addr6part(&template->start6)) + { + start6 = *local; + setaddr6part(&start6, addr6part(&template->start6)); + end6 = *local; + setaddr6part(&end6, addr6part(&template->end6)); + + for (context = daemon->dhcp6; context; context = context->next) + if ((context->flags & CONTEXT_CONSTRUCTED) && + IN6_ARE_ADDR_EQUAL(&start6, &context->start6) && + IN6_ARE_ADDR_EQUAL(&end6, &context->end6)) + { + context->flags &= ~CONTEXT_GC; + break; + } + + if (!context && (context = whine_malloc(sizeof (struct dhcp_context)))) + { + *context = *template; + context->start6 = start6; + context->end6 = end6; + context->flags &= ~CONTEXT_TEMPLATE; + context->flags |= CONTEXT_CONSTRUCTED; + context->if_index = if_index; + context->local6 = *local; + context->lease_time = param->now + valid; + + context->next = daemon->dhcp6; + daemon->dhcp6 = context; + + ra_start_unsolicted(dnsmasq_time(), context); + /* we created a new one, need to call + lease_update_file to get periodic functions called */ + param->newone = 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; +} + +void dhcp_construct_contexts(time_t now) +{ + struct dhcp_context *tmp, *context, **up; + struct cparam param; + param.newone = 0; + param.now = now; + + for (context = daemon->dhcp6; context; context = context->next) + if (context->flags & CONTEXT_CONSTRUCTED) + context->flags |= CONTEXT_GC; + + iface_enumerate(AF_INET6, ¶m, construct_worker); + + for (up = &daemon->dhcp6, context = daemon->dhcp6; context; context = tmp) + { + tmp = context->next; + + if (context->flags & CONTEXT_GC) + { + if (daemon->dhcp6 == context) + daemon->dhcp6 = context->next; + *up = context->next; + free(context); + } + else + up = &context->next; + } + + if (param.newone) + lease_update_file(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 9d90b4d..7a9d340 100644 --- a/src/dnsmasq.c +++ b/src/dnsmasq.c @@ -51,6 +51,7 @@ int main (int argc, char **argv) cap_user_header_t hdr = NULL; cap_user_data_t data = NULL; #endif + struct dhcp_context *context; #ifdef LOCALEDIR setlocale(LC_ALL, ""); @@ -167,40 +168,44 @@ int main (int argc, char **argv) #ifdef HAVE_DHCP if (daemon->dhcp || daemon->dhcp6) - { + { + +# ifdef HAVE_DHCP6 + if (daemon->dhcp6) + { + daemon->doing_ra = option_bool(OPT_RA); + + for (context = daemon->dhcp6; context; context = context->next) + { + if (context->flags & CONTEXT_DHCP) + daemon->doing_dhcp6 = 1; + if (context->flags & (CONTEXT_RA_ONLY | CONTEXT_RA_NAME | CONTEXT_RA_STATELESS)) + daemon->doing_ra = 1; + } + } +# endif + /* Note that order matters here, we must call lease_init before creating any file descriptors which shouldn't be leaked to the lease-script init process. We need to call common_init before lease_init to allocate buffers it uses.*/ - dhcp_common_init(); - lease_init(now); - + if (daemon->dhcp || daemon->doing_dhcp6) + { + dhcp_common_init(); + lease_init(now); + } + if (daemon->dhcp) dhcp_init(); - } - + # ifdef HAVE_DHCP6 - /* Start RA subsystem if --enable-ra OR dhcp-range=, 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->doing_ra) + ra_init(now); - 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->doing_dhcp6) + dhcp6_init(); # endif + } #endif @@ -214,13 +219,13 @@ int main (int argc, char **argv) #ifdef HAVE_DHCP6 /* after netlink_init */ - if (daemon->ra_contexts || daemon->dhcp6) + if (daemon->doing_dhcp6 || daemon->doing_ra) join_multicast(); #endif #ifdef HAVE_DHCP /* after netlink_init */ - if (daemon->dhcp || daemon->dhcp6) + if (daemon->dhcp || daemon->doing_dhcp6) lease_find_interfaces(now); #endif @@ -628,90 +633,24 @@ 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 (daemon->ra_contexts) - my_syslog(MS_DHCP | LOG_INFO, _("IPv6 router advertisement enabled")); + #ifdef HAVE_DHCP - if (daemon->dhcp || daemon->dhcp6 || daemon->ra_contexts) - { - struct dhcp_context *dhcp_tmp; - int family = AF_INET; - dhcp_tmp = daemon->dhcp; - -#ifdef HAVE_DHCP6 - again: -#endif - for (; dhcp_tmp; dhcp_tmp = dhcp_tmp->next) - { - void *start = &dhcp_tmp->start; - void *end = &dhcp_tmp->end; - -#ifdef HAVE_DHCP6 - if (family == AF_INET6) - { - start = &dhcp_tmp->start6; - end = &dhcp_tmp->end6; - struct in6_addr subnet = dhcp_tmp->start6; - setaddr6part(&subnet, 0); - inet_ntop(AF_INET6, &subnet, daemon->dhcp_buff2, 256); - } -#endif - - if (family != AF_INET && (dhcp_tmp->flags & CONTEXT_DEPRECATE)) - strcpy(daemon->namebuff, _("prefix deprecated")); - else - { - char *p = daemon->namebuff; - p += sprintf(p, _("lease time ")); - prettyprint_time(p, dhcp_tmp->lease_time); - } - - inet_ntop(family, start, daemon->dhcp_buff, 256); - inet_ntop(family, end, daemon->dhcp_buff3, 256); - if ((dhcp_tmp->flags & CONTEXT_DHCP) || family == AF_INET) - my_syslog(MS_DHCP | LOG_INFO, - (dhcp_tmp->flags & CONTEXT_RA_STATELESS) ? - _("%s stateless on %s%.0s%.0s") : - (dhcp_tmp->flags & CONTEXT_STATIC) ? - _("%s, static leases only on %.0s%s, %s") : - (dhcp_tmp->flags & CONTEXT_PROXY) ? - _("%s, proxy on subnet %.0s%s%.0s") : - _("%s, IP range %s -- %s, %s"), - (family != AF_INET) ? "DHCPv6" : "DHCP", - daemon->dhcp_buff, daemon->dhcp_buff3, daemon->namebuff); + for (context = daemon->dhcp; context; context = context->next) + log_context(AF_INET, context); - if (dhcp_tmp->flags & CONTEXT_RA_NAME) - my_syslog(MS_DHCP | LOG_INFO, _("DHCPv4-derived IPv6 names on %s"), - daemon->dhcp_buff2); - if (dhcp_tmp->flags & (CONTEXT_RA_ONLY | CONTEXT_RA_NAME | CONTEXT_RA_STATELESS)) - { - if (!(dhcp_tmp->flags & CONTEXT_DEPRECATE)) - { - char *p = daemon->namebuff; - p += sprintf(p, _("prefix valid ")); - prettyprint_time(p, dhcp_tmp->lease_time > 7200 ? dhcp_tmp->lease_time : 7200); - } - my_syslog(MS_DHCP | LOG_INFO, _("SLAAC on %s %s"), - daemon->dhcp_buff2, daemon->namebuff); - } - } - -#ifdef HAVE_DHCP6 - if (family == AF_INET) - { - family = AF_INET6; - if (daemon->ra_contexts) - dhcp_tmp = daemon->ra_contexts; - else - dhcp_tmp = daemon->dhcp6; - goto again; - } -#endif +# ifdef HAVE_DHCP6 + for (context = daemon->dhcp6; context; context = context->next) + log_context(AF_INET6, context); - } -#endif + if (daemon->doing_dhcp6 || daemon->doing_ra) + dhcp_construct_contexts(now); + + if (option_bool(OPT_RA)) + my_syslog(MS_DHCP | LOG_INFO, _("IPv6 router advertisement enabled")); +# endif +#endif #ifdef HAVE_TFTP if (option_bool(OPT_TFTP)) @@ -818,13 +757,13 @@ int main (int argc, char **argv) #endif #ifdef HAVE_DHCP6 - if (daemon->dhcp6) + if (daemon->doing_dhcp6) { FD_SET(daemon->dhcp6fd, &rset); bump_maxfd(daemon->dhcp6fd, &maxfd); } - if (daemon->ra_contexts) + if (daemon->doing_ra) { FD_SET(daemon->icmp6fd, &rset); bump_maxfd(daemon->icmp6fd, &maxfd); @@ -888,7 +827,7 @@ int main (int argc, char **argv) #ifdef HAVE_LINUX_NETWORK if (FD_ISSET(daemon->netlinkfd, &rset)) - netlink_multicast(); + netlink_multicast(now); #endif /* Check for changes to resolv files once per second max. */ @@ -936,11 +875,11 @@ int main (int argc, char **argv) } #ifdef HAVE_DHCP6 - if (daemon->dhcp6 && FD_ISSET(daemon->dhcp6fd, &rset)) + if (daemon->doing_dhcp6 && FD_ISSET(daemon->dhcp6fd, &rset)) dhcp6_packet(now); - if (daemon->ra_contexts && FD_ISSET(daemon->icmp6fd, &rset)) - icmp6_packet(); + if (daemon->doing_ra && FD_ISSET(daemon->icmp6fd, &rset)) + icmp6_packet(now); #endif # ifdef HAVE_SCRIPT @@ -1120,13 +1059,13 @@ static void async_event(int pipe, time_t now) case EVENT_ALARM: #ifdef HAVE_DHCP - if (daemon->dhcp || daemon->dhcp6) + if (daemon->dhcp || daemon->doing_dhcp6) { lease_prune(NULL, now); lease_update_file(now); } #ifdef HAVE_DHCP6 - else if (daemon->ra_contexts) + else if (daemon->doing_ra) /* Not doing DHCP, so no lease system, manage alarms for ra only */ send_alarm(periodic_ra(now), now); #endif @@ -1283,7 +1222,7 @@ void clear_cache_and_reload(time_t now) cache_reload(); #ifdef HAVE_DHCP - if (daemon->dhcp || daemon->dhcp6) + if (daemon->dhcp || daemon->doing_dhcp6) { if (option_bool(OPT_ETHERS)) dhcp_read_ethers(); @@ -1294,7 +1233,7 @@ void clear_cache_and_reload(time_t now) lease_update_dns(1); } #ifdef HAVE_DHCP6 - else if (daemon->ra_contexts) + else if (daemon->doing_ra) /* Not doing DHCP, so no lease system, manage alarms for ra only */ send_alarm(periodic_ra(now), now); @@ -1597,7 +1536,7 @@ int icmp_ping(struct in_addr addr) set_log_writer(&wset, &maxfd); #ifdef HAVE_DHCP6 - if (daemon->ra_contexts) + if (daemon->doing_ra) { FD_SET(daemon->icmp6fd, &rset); bump_maxfd(daemon->icmp6fd, &maxfd); @@ -1616,8 +1555,8 @@ int icmp_ping(struct in_addr addr) check_dns_listeners(&rset, now); #ifdef HAVE_DHCP6 - if (daemon->ra_contexts && FD_ISSET(daemon->icmp6fd, &rset)) - icmp6_packet(); + if (daemon->doing_ra && FD_ISSET(daemon->icmp6fd, &rset)) + icmp6_packet(now); #endif #ifdef HAVE_TFTP diff --git a/src/dnsmasq.h b/src/dnsmasq.h index a2b0131..385fd3a 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -667,7 +667,7 @@ struct cond_domain { #endif int is6; struct cond_domain *next; -}; +}; struct dhcp_context { unsigned int lease_time, addr_epoch; @@ -679,6 +679,8 @@ struct dhcp_context { struct in6_addr local6; int prefix, if_index; time_t ra_time; + char *template_interface; + int valid, preferred; /* times from address for constructed contexts */ #endif int flags; struct dhcp_netid netid, *filter; @@ -695,6 +697,11 @@ struct dhcp_context { #define CONTEXT_RA_STATELESS 128 #define CONTEXT_DHCP 256 #define CONTEXT_DEPRECATE 512 +#define CONTEXT_TEMPLATE 1024 /* create contexts using addresses */ +#define CONTEXT_CONSTRUCTED 2048 +#define CONTEXT_GC 4096 +#define CONTEXT_RA 8192 + struct ping_result { struct in_addr addr; @@ -773,7 +780,7 @@ extern struct daemon { int port, query_port, min_port; unsigned long local_ttl, neg_ttl, max_ttl, max_cache_ttl, auth_ttl; struct hostsfile *addn_hosts; - struct dhcp_context *dhcp, *dhcp6, *ra_contexts; + struct dhcp_context *dhcp, *dhcp6; struct dhcp_config *dhcp_conf; struct dhcp_opt *dhcp_opts, *dhcp_match, *dhcp_opts6, *dhcp_match6; struct dhcp_vendor *dhcp_vendors; @@ -784,6 +791,7 @@ extern struct daemon { struct addr_list *override_relays; int override; int enable_pxe; + int doing_ra, doing_dhcp6; struct dhcp_netid_list *dhcp_ignore, *dhcp_ignore_names, *dhcp_gen_names; struct dhcp_netid_list *force_broadcast, *bootp_dynamic; struct hostsfile *dhcp_hosts_file, *dhcp_opts_file; @@ -1074,7 +1082,7 @@ void poll_resolv(int force, int do_reload, time_t now); /* netlink.c */ #ifdef HAVE_LINUX_NETWORK void netlink_init(void); -void netlink_multicast(void); +void netlink_multicast(time_t now); #endif /* bpf.c */ @@ -1142,6 +1150,8 @@ struct dhcp_config *find_config6(struct dhcp_config *configs, struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct in6_addr *net, int prefix, u64 addr); void make_duid(time_t now); +void dhcp_construct_contexts(time_t now); +void join_multicast(void); #endif /* rfc3315.c */ @@ -1172,8 +1182,8 @@ void bindtodevice(int fd); #endif # ifdef HAVE_DHCP6 void display_opts6(void); -void join_multicast(void); # endif +void log_context(int family, struct dhcp_context *context); #endif /* outpacket.c */ @@ -1192,16 +1202,14 @@ void put_opt6_string(char *s); /* radv.c */ #ifdef HAVE_DHCP6 void ra_init(time_t now); -void icmp6_packet(void); +void icmp6_packet(time_t now); time_t periodic_ra(time_t now); void ra_start_unsolicted(time_t now, struct dhcp_context *context); #endif /* slaac.c */ #ifdef HAVE_DHCP6 -void build_subnet_map(void); void slaac_add_addrs(struct dhcp_lease *lease, time_t now, int force); time_t periodic_slaac(time_t now, struct dhcp_lease *leases); void slaac_ping_reply(struct in6_addr *sender, unsigned char *packet, char *interface, struct dhcp_lease *leases); -void schedule_subnet_map(void); #endif diff --git a/src/lease.c b/src/lease.c index ad4ee84..079a767 100644 --- a/src/lease.c +++ b/src/lease.c @@ -308,7 +308,7 @@ void lease_update_file(time_t now) #ifdef HAVE_DHCP6 /* do timed RAs and determine when the next is, also pings to potential SLAAC addresses */ - if (daemon->ra_contexts) + if (daemon->doing_ra) { time_t event; @@ -363,12 +363,15 @@ 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, void *vparam) + int scope, int if_index, int dad, + int preferred, int valid, void *vparam) { struct dhcp_lease *lease; (void)scope; (void)dad; + (void)preferred; + (void)valid; for (lease = leases; lease; lease = lease->next) if ((lease->flags & (LEASE_TA | LEASE_NA))) @@ -395,10 +398,6 @@ void lease_ping_reply(struct in6_addr *sender, unsigned char *packet, char *inte start-time. */ void lease_find_interfaces(time_t now) { -#ifdef HAVE_DHCP6 - build_subnet_map(); -#endif - iface_enumerate(AF_INET, &now, find_interface_v4); #ifdef HAVE_DHCP6 iface_enumerate(AF_INET6, &now, find_interface_v6); diff --git a/src/netlink.c b/src/netlink.c index 9951ed5..9090034 100644 --- a/src/netlink.c +++ b/src/netlink.c @@ -50,10 +50,14 @@ void netlink_init(void) addr.nl_pid = 0; /* autobind */ addr.nl_groups = RTMGRP_IPV4_ROUTE; if (option_bool(OPT_CLEVERBIND)) - addr.nl_groups |= RTMGRP_IPV4_IFADDR; + addr.nl_groups |= RTMGRP_IPV4_IFADDR; #ifdef HAVE_IPV6 addr.nl_groups |= RTMGRP_IPV6_ROUTE; - if (daemon->ra_contexts || option_bool(OPT_CLEVERBIND)) + if (option_bool(OPT_CLEVERBIND)) + addr.nl_groups |= RTMGRP_IPV6_IFADDR; +#endif +#ifdef HAVE_DHCP6 + if (daemon->doing_ra || daemon->doing_dhcp6) addr.nl_groups |= RTMGRP_IPV6_IFADDR; #endif @@ -187,7 +191,7 @@ int iface_enumerate(int family, void *parm, int (*callback)()) if (h->nlmsg_seq != seq || h->nlmsg_pid != netlink_pid || h->nlmsg_type == NLMSG_ERROR) { /* May be multicast arriving async */ - if (nl_async(h) && option_bool(OPT_CLEVERBIND)) + if (nl_async(h)) newaddr = 1; } else if (h->nlmsg_type == NLMSG_DONE) @@ -196,10 +200,15 @@ int iface_enumerate(int family, void *parm, int (*callback)()) after we complete as we're not re-entrant */ if (newaddr) { - enumerate_interfaces(); - create_bound_listeners(0); + if (option_bool(OPT_CLEVERBIND)) + { + enumerate_interfaces(); + create_bound_listeners(0); + } +#ifdef HAVE_DHCP6 + dhcp_construct_contexts(dnsmasq_time()); +#endif } - return callback_ok; } else if (h->nlmsg_type == RTM_NEWADDR && family != AF_UNSPEC && family != AF_LOCAL) @@ -236,17 +245,24 @@ int iface_enumerate(int family, void *parm, int (*callback)()) else if (ifa->ifa_family == AF_INET6) { struct in6_addr *addrp = NULL; + u32 valid = 0, preferred = 0; while (RTA_OK(rta, len1)) { if (rta->rta_type == IFA_ADDRESS) addrp = ((struct in6_addr *)(rta+1)); - + else if (rta->rta_type == IFA_CACHEINFO) + { + struct ifa_cacheinfo *ifc = (struct ifa_cacheinfo *)(rta+1); + preferred = ifc->ifa_prefered; + valid = ifc->ifa_valid; + } rta = RTA_NEXT(rta, len1); } 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), parm))) + (int)(ifa->ifa_index), (int)(ifa->ifa_flags & IFA_F_TENTATIVE), + (int) preferred, (int)valid, parm))) callback_ok = 0; } #endif @@ -305,7 +321,7 @@ int iface_enumerate(int family, void *parm, int (*callback)()) } } -void netlink_multicast(void) +void netlink_multicast(time_t now) { ssize_t len; struct nlmsghdr *h; @@ -318,7 +334,7 @@ void netlink_multicast(void) if ((len = netlink_recv()) != -1) for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len)) - if (nl_async(h) && option_bool(OPT_CLEVERBIND)) + if (nl_async(h)) newaddr = 1; /* restore non-blocking status */ @@ -326,8 +342,14 @@ void netlink_multicast(void) if (newaddr) { - enumerate_interfaces(); - create_bound_listeners(0); + if (option_bool(OPT_CLEVERBIND)) + { + enumerate_interfaces(); + create_bound_listeners(0); + } +#ifdef HAVE_DHCP6 + dhcp_construct_contexts(now); +#endif } } @@ -371,21 +393,8 @@ static int nl_async(struct nlmsghdr *h) } return 0; } - else if (h->nlmsg_type == RTM_NEWADDR) - { -#ifdef HAVE_DHCP6 - /* force RAs to sync new network and pick up new interfaces. */ - if (daemon->ra_contexts) - { - schedule_subnet_map(); - ra_start_unsolicted(dnsmasq_time(), NULL); - /* cause lease_update_file to run after we return, in case we were called from - iface_enumerate and can't re-enter it now */ - send_alarm(0, 0); - } -#endif - return 1; /* clever bind mode - rescan */ - } + else if (h->nlmsg_type == RTM_NEWADDR || h->nlmsg_type == RTM_DELADDR) + return 1; /* clever bind mode - rescan */ return 0; } diff --git a/src/network.c b/src/network.c index 3adba86..c534092 100644 --- a/src/network.c +++ b/src/network.c @@ -289,7 +289,8 @@ 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, void *vparam) + int scope, int if_index, int dad, + int preferred, int valid, void *vparam) { union mysockaddr addr; struct in_addr netmask; /* dummy */ @@ -297,6 +298,8 @@ static int iface_allowed_v6(struct in6_addr *local, int prefix, (void)prefix; /* warning */ (void)scope; /* warning */ + (void)preferred; + (void)valid; memset(&addr, 0, sizeof(addr)); #ifdef HAVE_SOCKADDR_SA_LEN diff --git a/src/option.c b/src/option.c index 0ecc0f5..b765f90 100644 --- a/src/option.c +++ b/src/option.c @@ -2180,7 +2180,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma case 'F': /* --dhcp-range */ { int k, leasepos = 2; - char *cp, *a[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL }; + char *cp, *a[8] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL }; struct dhcp_context *new = opt_malloc(sizeof(struct dhcp_context)); memset (new, 0, sizeof(*new)); @@ -2227,7 +2227,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma } } - for (k = 1; k < 7; k++) + for (k = 1; k < 8; k++) if (!(a[k] = split(a[k-1]))) break; @@ -2284,28 +2284,25 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma if (strcmp(a[leasepos], "static") == 0) new->flags |= CONTEXT_STATIC | CONTEXT_DHCP; else if (strcmp(a[leasepos], "ra-only") == 0 || strcmp(a[leasepos], "slaac") == 0 ) - new->flags |= CONTEXT_RA_ONLY; + new->flags |= CONTEXT_RA_ONLY | CONTEXT_RA; else if (strcmp(a[leasepos], "ra-names") == 0) - new->flags |= CONTEXT_RA_NAME; + new->flags |= CONTEXT_RA_NAME | CONTEXT_RA; else if (strcmp(a[leasepos], "ra-stateless") == 0) - new->flags |= CONTEXT_RA_STATELESS | CONTEXT_DHCP; + new->flags |= CONTEXT_RA_STATELESS | CONTEXT_DHCP | CONTEXT_RA; else if (leasepos == 1 && inet_pton(AF_INET6, a[leasepos], &new->end6)) new->flags |= CONTEXT_DHCP; + else if (strstr(a[leasepos], "constructor:") == a[leasepos]) + { + new->template_interface = opt_string_alloc(a[leasepos] + 12); + new->flags |= CONTEXT_TEMPLATE; + } else break; } - if (new->flags & CONTEXT_DHCP) - { - new->next = daemon->dhcp6; - daemon->dhcp6 = new; - } - else - { - new->next = daemon->ra_contexts; - daemon->ra_contexts = new; - } - + new->next = daemon->dhcp6; + daemon->dhcp6 = new; + /* bare integer < 128 is prefix value */ if (leasepos < k) { @@ -2317,10 +2314,14 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma { new->prefix = pref; leasepos++; - if ((new->flags & (CONTEXT_RA_ONLY | CONTEXT_RA_NAME | CONTEXT_RA_STATELESS)) && - new->prefix != 64) - ret_err(_("prefix must be exactly 64 for RA subnets")); - else if (new->prefix < 64) + if (new->prefix != 64) + { + if ((new->flags & (CONTEXT_RA_ONLY | CONTEXT_RA_NAME | CONTEXT_RA_STATELESS))) + ret_err(_("prefix must be exactly 64 for RA subnets")); + else if (new->template_interface) + ret_err(_("prefix must be exactly 64 for subnet constructors")); + } + if (new->prefix < 64) ret_err(_("prefix must be at least 64")); } } diff --git a/src/radv.c b/src/radv.c index 54884a2..b74c398 100644 --- a/src/radv.c +++ b/src/radv.c @@ -27,6 +27,7 @@ #include struct ra_param { + time_t now; int ind, managed, other, found_context, first; char *if_name; struct dhcp_netid *tags; @@ -37,11 +38,13 @@ struct search_param { time_t now; int iface; }; -static void send_ra(int iface, char *iface_name, struct in6_addr *dest); +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, void *vparam); + int scope, int if_index, int dad, + int preferred, int valid, void *vparam); static int iface_search(struct in6_addr *local, int prefix, - int scope, int if_index, int dad, void *vparam); + int scope, int if_index, int dad, + int prefered, int valid, void *vparam); static int add_lla(int index, unsigned int type, char *mac, size_t maclen, void *parm); static int hop_limit; @@ -62,7 +65,7 @@ void ra_init(time_t now) expand_buf(&daemon->outpacket, sizeof(struct dhcp_packet)); /* See if we're guessing SLAAC addresses, if so we need to recieve ping replies */ - for (context = daemon->ra_contexts; context; context = context->next) + for (context = daemon->dhcp6; context; context = context->next) if ((context->flags & CONTEXT_RA_NAME)) break; @@ -98,14 +101,17 @@ void ra_start_unsolicted(time_t now, struct dhcp_context *context) if (context) context->ra_time = now; else - for (context = daemon->ra_contexts; context; context = context->next) - context->ra_time = now + (rand16()/13000); /* range 0 - 5 */ - + for (context = daemon->dhcp6; context; context = context->next) + if (context->flags & CONTEXT_RA) + context->ra_time = now + (rand16()/13000); /* range 0 - 5 */ + else + context->ra_time = 0; + /* re-do frequently for a minute or so, in case the first gets lost. */ ra_short_period_start = now; } -void icmp6_packet(void) +void icmp6_packet(time_t now) { char interface[IF_NAMESIZE+1]; ssize_t sz; @@ -174,11 +180,11 @@ void icmp6_packet(void) my_syslog(MS_DHCP | LOG_INFO, "RTR-SOLICIT(%s) %s", interface, mac); /* source address may not be valid in solicit request. */ - send_ra(if_index, interface, !IN6_IS_ADDR_UNSPECIFIED(&from.sin6_addr) ? &from.sin6_addr : NULL); + send_ra(now, if_index, interface, !IN6_IS_ADDR_UNSPECIFIED(&from.sin6_addr) ? &from.sin6_addr : NULL); } } -static void send_ra(int iface, char *iface_name, struct in6_addr *dest) +static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *dest) { struct ra_packet *ra; struct ra_param parm; @@ -206,13 +212,14 @@ static void send_ra(int iface, char *iface_name, struct in6_addr *dest) parm.found_context = 0; parm.if_name = iface_name; parm.first = 1; - + parm.now = now; + /* set tag with name == interface */ iface_id.net = iface_name; iface_id.next = NULL; parm.tags = &iface_id; - for (context = daemon->ra_contexts; context; context = context->next) + for (context = daemon->dhcp6; context; context = context->next) { context->flags &= ~CONTEXT_RA_DONE; context->netid.next = &context->netid; @@ -320,28 +327,32 @@ static void send_ra(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, void *vparam) + int scope, int if_index, int dad, + int preferred, 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)) param->link_local = *local; else if (!IN6_IS_ADDR_LOOPBACK(local) && - !IN6_IS_ADDR_LINKLOCAL(local) && !IN6_IS_ADDR_MULTICAST(local)) { int do_prefix = 0; int do_slaac = 0; int deprecate = 0; + int found_constructed = 0; unsigned int time = 0xffffffff; + int calc_valid = 0, calc_preferred = 0; struct dhcp_context *context; - for (context = daemon->ra_contexts; context; context = context->next) + for (context = daemon->dhcp6; context; context = context->next) if (prefix == context->prefix && is_same_net6(local, &context->start6, prefix) && is_same_net6(local, &context->end6, prefix)) @@ -365,7 +376,18 @@ static int add_prefixes(struct in6_addr *local, int prefix, param->managed = 1; 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 */ if (time > context->lease_time) time = context->lease_time; @@ -395,6 +417,12 @@ static int add_prefixes(struct in6_addr *local, int prefix, param->first = 0; param->found_context = 1; } + + if (!found_constructed) + { + calc_valid = time; + calc_preferred = deprecate ? 0 : time; + } if (do_prefix) { @@ -414,8 +442,8 @@ static int add_prefixes(struct in6_addr *local, int prefix, 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(time); - opt->preferred_lifetime = htonl(deprecate ? 0 : time); + opt->valid_lifetime = htonl(calc_valid); + opt->preferred_lifetime = htonl(calc_preferred); opt->reserved = 0; opt->prefix = *local; @@ -462,7 +490,7 @@ time_t periodic_ra(time_t now) while (1) { /* find overdue events, and time of first future event */ - for (next_event = 0, context = daemon->ra_contexts; context; context = context->next) + for (next_event = 0, context = daemon->dhcp6; context; context = context->next) if (context->ra_time != 0) { if (difftime(context->ra_time, now) <= 0.0) @@ -492,22 +520,25 @@ time_t periodic_ra(time_t now) if (tmp->name && (strcmp(tmp->name, interface) == 0)) break; if (!tmp) - send_ra(param.iface, interface, NULL); + send_ra(now, param.iface, interface, NULL); } } return next_event; } static int iface_search(struct in6_addr *local, int prefix, - int scope, int if_index, int dad, void *vparam) + int scope, int if_index, int dad, + int preferred, int valid, void *vparam) { struct search_param *param = vparam; struct dhcp_context *context; (void)scope; (void)dad; + (void)preferred; + (void)valid; - for (context = daemon->ra_contexts; context; context = context->next) + for (context = daemon->dhcp6; context; context = context->next) if (prefix == context->prefix && is_same_net6(local, &context->start6, prefix) && is_same_net6(local, &context->end6, prefix)) diff --git a/src/slaac.c b/src/slaac.c index 7713ac4..f959c72 100644 --- a/src/slaac.c +++ b/src/slaac.c @@ -20,7 +20,6 @@ #include -static int map_rebuild = 0; static int ping_id = 0; void slaac_add_addrs(struct dhcp_lease *lease, time_t now, int force) @@ -38,7 +37,7 @@ void slaac_add_addrs(struct dhcp_lease *lease, time_t now, int force) old = lease->slaac_address; lease->slaac_address = NULL; - for (context = daemon->ra_contexts; context; context = context->next) + for (context = daemon->dhcp6; context; context = context->next) if ((context->flags & CONTEXT_RA_NAME) && lease->last_interface == context->if_index) { struct in6_addr addr = context->start6; @@ -123,7 +122,7 @@ time_t periodic_slaac(time_t now, struct dhcp_lease *leases) struct slaac_address *slaac; time_t next_event = 0; - for (context = daemon->ra_contexts; context; context = context->next) + for (context = daemon->dhcp6; context; context = context->next) if ((context->flags & CONTEXT_RA_NAME)) break; @@ -134,12 +133,6 @@ time_t periodic_slaac(time_t now, struct dhcp_lease *leases) while (ping_id == 0) ping_id = rand16(); - if (map_rebuild) - { - map_rebuild = 0; - build_subnet_map(); - } - for (lease = leases; lease; lease = lease->next) for (slaac = lease->slaac_address; slaac; slaac = slaac->next) { @@ -211,51 +204,4 @@ void slaac_ping_reply(struct in6_addr *sender, unsigned char *packet, char *inte lease_update_dns(gotone); } -/* Build a map from ra-names subnets to corresponding interfaces. This - is used to go from DHCPv4 leases to SLAAC addresses, - interface->IPv6-subnet, IPv6-subnet + MAC address -> SLAAC. -*/ -static int add_subnet(struct in6_addr *local, int prefix, - int scope, int if_index, int dad, void *vparam) -{ - struct dhcp_context *context; - - (void)scope; - (void)dad; - (void)vparam; - - for (context = daemon->ra_contexts; context; context = context->next) - if ((context->flags & CONTEXT_RA_NAME) && - prefix == context->prefix && - is_same_net6(local, &context->start6, prefix) && - is_same_net6(local, &context->end6, prefix)) - { - context->if_index = if_index; - context->local6 = *local; - } - - return 1; -} - -void build_subnet_map(void) -{ - struct dhcp_context *context; - int ok = 0; - - for (context = daemon->ra_contexts; context; context = context->next) - { - context->if_index = 0; - if ((context->flags & CONTEXT_RA_NAME)) - ok = 1; - } - - /* ra-names configured */ - if (ok) - iface_enumerate(AF_INET6, NULL, add_subnet); -} - -void schedule_subnet_map(void) -{ - map_rebuild = 1; -} #endif