diff --git a/src/dnsmasq.c b/src/dnsmasq.c index 82e4cad..9ed6f3a 100644 --- a/src/dnsmasq.c +++ b/src/dnsmasq.c @@ -106,16 +106,14 @@ int main (int argc, char **argv) else open("/dev/null", O_RDWR); -#ifdef HAVE_LINUX_NETWORK - netlink_init(); -#elif !(defined(IP_RECVDSTADDR) && \ - defined(IP_RECVIF) && \ - defined(IP_SENDSRCADDR)) +#ifndef HAVE_LINUX_NETWORK +# if !(defined(IP_RECVDSTADDR) && defined(IP_RECVIF) && defined(IP_SENDSRCADDR)) if (!option_bool(OPT_NOWILD)) { bind_fallback = 1; set_option_bool(OPT_NOWILD); } +# endif #endif #ifndef HAVE_TFTP @@ -180,10 +178,25 @@ int main (int argc, char **argv) if (daemon->dhcp6) dhcp6_init(); - if (daemon->ra_contexts || daemon->dhcp6) - join_multicast(); # endif +#endif + +#ifdef HAVE_LINUX_NETWORK + /* After lease_init */ + netlink_init(); +#endif + +#ifdef HAVE_DHCP6 + /* after netlink_init */ + if (daemon->ra_contexts || daemon->dhcp6) + join_multicast(); +#endif + +#ifdef HAVE_DHCP + /* after netlink_init */ + if (daemon->dhcp || daemon->dhcp6) + lease_find_interfaces(); #endif if (!enumerate_interfaces()) @@ -552,6 +565,8 @@ int main (int argc, char **argv) my_syslog(MS_DHCP | LOG_INFO, (dhcp_tmp->flags & CONTEXT_STATIC) ? _("DHCP, static leases only on %.0s%s, lease time %s") : + (dhcp_tmp->flags & CONTEXT_RA_NAME) ? + _("router advertisement with DHCPv4-derived names on %.0s%s, lifetime %s") : (dhcp_tmp->flags & CONTEXT_RA_ONLY) ? _("router advertisement only on %.0s%s, lifetime %s") : (dhcp_tmp->flags & CONTEXT_PROXY) ? diff --git a/src/dnsmasq.h b/src/dnsmasq.h index e72e53c..2ce22a7 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -621,6 +621,7 @@ struct dhcp_context { #define CONTEXT_PROXY 8 #define CONTEXT_RA_ONLY 16 #define CONTEXT_RA_DONE 32 +#define CONTEXT_RA_NAME 64 struct ping_result { struct in_addr addr; @@ -629,6 +630,13 @@ struct ping_result { struct ping_result *next; }; +struct subnet_map { + int iface; + struct in6_addr subnet; + struct subnet_map *next; +}; + + struct tftp_file { int refcount, fd; off_t size; @@ -948,6 +956,7 @@ void lease_prune(struct dhcp_lease *target, time_t now); void lease_update_from_configs(void); int do_script_run(time_t now); void rerun_scripts(void); +void lease_find_interfaces(void); #ifdef HAVE_SCRIPT void lease_add_extradata(struct dhcp_lease *lease, unsigned char *data, unsigned int len, int delim); @@ -1083,4 +1092,5 @@ void ra_init(time_t now); void icmp6_packet(void); time_t periodic_ra(time_t now); void ra_start_unsolicted(time_t now); +struct subnet_map *build_subnet_map(void); #endif diff --git a/src/lease.c b/src/lease.c index fa2005f..e79b0db 100644 --- a/src/lease.c +++ b/src/lease.c @@ -340,20 +340,115 @@ void lease_update_file(time_t now) alarm((unsigned)difftime(next_event, now)); } -void lease_update_dns(void) + +static int find_interface_v4(struct in_addr local, int if_index, + struct in_addr netmask, struct in_addr broadcast, void *vparam) { struct dhcp_lease *lease; + (void) broadcast; + (void) vparam; + + for (lease = leases; lease; lease = lease->next) + if (!(lease->flags & (LEASE_TA | LEASE_NA))) + if (is_same_net(local, lease->addr, netmask)) + lease->last_interface = if_index; + + return 1; +} + +#ifdef HAVE_DHCP6 +static int find_interface_v6(struct in6_addr *local, int prefix, + int scope, int if_index, int dad, void *vparam) +{ + struct dhcp_lease *lease; + + (void) scope; + (void) vparam; + (void)dad; + + for (lease = leases; lease; lease = lease->next) + if ((lease->flags & (LEASE_TA | LEASE_NA))) + if (is_same_net6(local, (struct in6_addr *)&lease->hwaddr, prefix)) + lease->last_interface = if_index; + + return 1; +} +#endif + + +/* Find interfaces associated with leases at start-up. This gets updated as + we do DHCP transactions, but information about directly-connected subnets + is useful from scrips and necessary for determining SLAAC addresses from + start-time. */ +void lease_find_interfaces(void) +{ + iface_enumerate(AF_INET, NULL, find_interface_v4); +#ifdef HAVE_DHCP6 + iface_enumerate(AF_INET6, NULL, find_interface_v6); +#endif +} + + + +void lease_update_dns(void) +{ + struct dhcp_lease *lease; + if (daemon->port != 0 && dns_dirty) { - cache_unhash_dhcp(); +#ifdef HAVE_DHCP6 + struct subnet_map *subnets = build_subnet_map(); +#endif + cache_unhash_dhcp(); + for (lease = leases; lease; lease = lease->next) { int prot = AF_INET; + #ifdef HAVE_DHCP6 if (lease->flags & (LEASE_TA | LEASE_NA)) prot = AF_INET6; + else if (lease->hostname || lease->fqdn) + { + struct subnet_map *map; + for (map = subnets; map; map = map->next) + if (lease->last_interface == map->iface) + { + struct in6_addr addr = map->subnet; + if (lease->hwaddr_len == 6 && + (lease->hwaddr_type == ARPHRD_ETHER || lease->hwaddr_type == ARPHRD_IEEE802)) + { + /* convert MAC address to EUI-64 */ + memcpy(&addr.s6_addr[8], lease->hwaddr, 3); + memcpy(&addr.s6_addr[13], &lease->hwaddr[3], 3); + addr.s6_addr[11] = 0xff; + addr.s6_addr[12] = 0xfe; + } +#if defined(ARPHRD_EUI64) + else if (lease->hwaddr_len == 8 && + lease->hwaddr_type == ARPHRD_EUI64) + memcpy(&addr.s6_addr[8], lease->hwaddr, 8); +#endif +#if defined(ARPHRD_IEEE1394) && defined(ARPHRD_EUI64) + else if (lease->clid_len == 9 && + lease->clid[0] == ARPHRD_EUI64 && + lease->hwaddr_type == ARPHRD_IEEE1394) + /* firewire has EUI-64 identifier as clid */ + memcpy(&addr.s6_addr[8], &lease->clid[1], 8); +#endif + else + continue; + + addr.s6_addr[8] ^= 0x02; + + if (lease->fqdn) + cache_add_dhcp_entry(lease->fqdn, AF_INET6, (struct all_addr *)&addr, lease->expires); + if (!option_bool(OPT_DHCP_FQDN) && lease->hostname) + cache_add_dhcp_entry(lease->hostname, AF_INET6, (struct all_addr *)&addr, lease->expires); + } + } #endif if (lease->fqdn) diff --git a/src/option.c b/src/option.c index ee7989a..13dc8bf 100644 --- a/src/option.c +++ b/src/option.c @@ -2345,6 +2345,11 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line) memcpy(&new->end6, &new->start6, IN6ADDRSZ); new->flags |= CONTEXT_RA_ONLY; } + else if (strcmp(a[1], "ra-names") == 0) + { + memcpy(&new->end6, &new->start6, IN6ADDRSZ); + new->flags |= CONTEXT_RA_NAME | CONTEXT_RA_ONLY; + } else if (!inet_pton(AF_INET6, a[1], &new->end6)) option = '?'; @@ -2370,7 +2375,9 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line) { new->prefix = pref; leasepos = 3; - if (new->prefix < 64) + if ((new->flags & CONTEXT_RA_ONLY) && new->prefix != 64) + problem = _("prefix must be exactly 64 for RA subnets"); + else if (new->prefix < 64) problem = _("prefix must be at least 64"); } } diff --git a/src/radv.c b/src/radv.c index 81d853f..c8e2f01 100644 --- a/src/radv.c +++ b/src/radv.c @@ -426,4 +426,73 @@ static int iface_search(struct in6_addr *local, int prefix, return 1; /* keep searching */ } +static int add_subnet(struct in6_addr *local, int prefix, + int scope, int if_index, int dad, void *vparam) +{ + struct dhcp_context *context; + struct subnet_map **subnets = vparam; + struct subnet_map *map; + + (void)scope; + (void)dad; + + 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)) + { + for (map = *subnets; map; map = map->next) + if (map->iface == 0 || + (map->iface == if_index && is_same_net6(local, &map->subnet, prefix))) + break; + + /* It's there already */ + if (map && map->iface != 0) + continue; + + if (!map && (map = whine_malloc(sizeof(struct subnet_map)))) + { + map->next = *subnets; + *subnets = map; + } + + if (map) + { + map->iface = if_index; + map->subnet = *local; + } + } + + return 1; +} + +/* 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. +*/ +struct subnet_map *build_subnet_map(void) +{ + struct subnet_map *map; + struct dhcp_context *context; + static struct subnet_map *subnets = NULL; + + for (context = daemon->ra_contexts; context; context = context->next) + if ((context->flags & CONTEXT_RA_NAME)) + break; + + /* no ra-names, no need to go further. */ + if (!context) + return NULL; + + /* mark unused */ + for (map = subnets; map; map = map->next) + map->iface = 0; + + if (iface_enumerate(AF_INET6, &subnets, add_subnet)) + return subnets; + + return NULL; +} + #endif