diff options
Diffstat (limited to 'src/dhcp6.c')
-rw-r--r-- | src/dhcp6.c | 87 |
1 files changed, 32 insertions, 55 deletions
diff --git a/src/dhcp6.c b/src/dhcp6.c index 8286ff4..0853664 100644 --- a/src/dhcp6.c +++ b/src/dhcp6.c @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2018 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -27,17 +27,10 @@ struct iface_param { int ind, addr_match; }; -struct mac_param { - struct in6_addr *target; - unsigned char *mac; - unsigned int maclen; -}; - static int complete_context6(struct in6_addr *local, int prefix, int scope, int if_index, int flags, unsigned int preferred, unsigned int valid, void *vparam); -static int find_mac(int family, char *addrp, char *mac, size_t maclen, void *parmv); static int make_duid1(int index, unsigned int type, char *mac, size_t maclen, void *parm); void dhcp6_init(void) @@ -58,9 +51,9 @@ void dhcp6_init(void) !set_ipv6pktinfo(fd)) die (_("cannot create DHCPv6 socket: %s"), NULL, EC_BADNET); - /* When bind-interfaces is set, there might be more than one dnmsasq + /* When bind-interfaces is set, there might be more than one dnsmasq instance binding port 547. That's OK if they serve different networks. - Need to set REUSEADDR|REUSEPORT to make this posible. + Need to set REUSEADDR|REUSEPORT to make this possible. Handle the case that REUSEPORT is defined, but the kernel doesn't support it. This handles the introduction of REUSEPORT on Linux. */ if (option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND)) @@ -227,7 +220,7 @@ void dhcp6_packet(time_t now) inet_pton(AF_INET6, ALL_SERVERS, &all_servers); if (!IN6_ARE_ADDR_EQUAL(&dst_addr, &all_servers)) - relay_upstream6(parm.relay, sz, &from.sin6_addr, from.sin6_scope_id); + relay_upstream6(parm.relay, sz, &from.sin6_addr, from.sin6_scope_id, now); return; } @@ -257,16 +250,15 @@ void dhcp6_packet(time_t now) } } -void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac, unsigned int *maclenp, unsigned int *mactypep) +void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac, unsigned int *maclenp, unsigned int *mactypep, time_t now) { - /* Recieving a packet from a host does not populate the neighbour + /* Receiving a packet from a host does not populate the neighbour cache, so we send a neighbour discovery request if we can't find the sender. Repeat a few times in case of packet loss. */ struct neigh_packet neigh; - struct sockaddr_in6 addr; - struct mac_param mac_param; - int i; + union mysockaddr addr; + int i, maclen; neigh.type = ND_NEIGHBOR_SOLICIT; neigh.code = 0; @@ -277,55 +269,31 @@ void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac, unsi memset(&addr, 0, sizeof(addr)); #ifdef HAVE_SOCKADDR_SA_LEN - addr.sin6_len = sizeof(struct sockaddr_in6); + addr.in6.sin6_len = sizeof(struct sockaddr_in6); #endif - addr.sin6_family = AF_INET6; - addr.sin6_port = htons(IPPROTO_ICMPV6); - addr.sin6_addr = *client; - addr.sin6_scope_id = iface; - - mac_param.target = client; - mac_param.maclen = 0; - mac_param.mac = mac; + addr.in6.sin6_family = AF_INET6; + addr.in6.sin6_port = htons(IPPROTO_ICMPV6); + addr.in6.sin6_addr = *client; + addr.in6.sin6_scope_id = iface; for (i = 0; i < 5; i++) { struct timespec ts; - iface_enumerate(AF_UNSPEC, &mac_param, find_mac); - - if (mac_param.maclen != 0) + if ((maclen = find_mac(&addr, mac, 0, now)) != 0) break; - - sendto(daemon->icmp6fd, &neigh, sizeof(neigh), 0, (struct sockaddr *)&addr, sizeof(addr)); + + sendto(daemon->icmp6fd, &neigh, sizeof(neigh), 0, &addr.sa, sizeof(addr)); ts.tv_sec = 0; ts.tv_nsec = 100000000; /* 100ms */ nanosleep(&ts, NULL); } - *maclenp = mac_param.maclen; + *maclenp = maclen; *mactypep = ARPHRD_ETHER; } -static int find_mac(int family, char *addrp, char *mac, size_t maclen, void *parmv) -{ - struct mac_param *parm = parmv; - - if (family == AF_INET6 && IN6_ARE_ADDR_EQUAL(parm->target, (struct in6_addr *)addrp)) - { - if (maclen <= DHCP_CHADDR_MAX) - { - parm->maclen = maclen; - memcpy(parm->mac, mac, maclen); - } - - return 0; /* found, abort */ - } - - return 1; -} - static int complete_context6(struct in6_addr *local, int prefix, int scope, int if_index, int flags, unsigned int preferred, unsigned int valid, void *vparam) @@ -376,7 +344,7 @@ static int complete_context6(struct in6_addr *local, int prefix, { struct dhcp_context *tmp, **up; - /* use interface values only for contructed contexts */ + /* use interface values only for constructed contexts */ if (!(context->flags & CONTEXT_CONSTRUCTED)) preferred = valid = 0xffffffff; else if (flags & IFACE_DEPRECATED) @@ -452,7 +420,7 @@ struct dhcp_context *address6_allocate(struct dhcp_context *context, unsigned c j = rand64(); else for (j = iaid, i = 0; i < clid_len; i++) - j += clid[i] + (j << 6) + (j << 16) - j; + j = clid[i] + (j << 6) + (j << 16) - j; for (pass = 0; pass <= plain_range ? 1 : 0; pass++) for (c = context; c; c = c->current) @@ -466,7 +434,16 @@ struct dhcp_context *address6_allocate(struct dhcp_context *context, unsigned c /* seed is largest extant lease addr in this context */ start = lease_find_max_addr6(c) + serial; else - start = addr6part(&c->start6) + ((j + c->addr_epoch) % (1 + addr6part(&c->end6) - addr6part(&c->start6))); + { + u64 range = 1 + addr6part(&c->end6) - addr6part(&c->start6); + u64 offset = j + c->addr_epoch; + + /* don't divide by zero if range is whole 2^64 */ + if (range != 0) + offset = offset % range; + + start = addr6part(&c->start6) + offset; + } /* iterate until we find a free address. */ addr = start; @@ -695,7 +672,7 @@ static int construct_worker(struct in6_addr *local, int prefix, /* address went, now it's back */ log_context(AF_INET6, context); /* fast RAs for a while */ - ra_start_unsolicted(param->now, context); + ra_start_unsolicited(param->now, context); param->newone = 1; /* Add address to name again */ if (context->flags & CONTEXT_RA_NAME) @@ -718,7 +695,7 @@ static int construct_worker(struct in6_addr *local, int prefix, context->next = daemon->dhcp6; daemon->dhcp6 = context; - ra_start_unsolicted(param->now, context); + ra_start_unsolicited(param->now, context); /* we created a new one, need to call lease_update_file to get periodic functions called */ param->newone = 1; @@ -766,7 +743,7 @@ void dhcp_construct_contexts(time_t now) /* maximum time is 2 hours, from RFC */ if (context->saved_valid > 7200) /* 2 hours */ context->saved_valid = 7200; - ra_start_unsolicted(now, context); + ra_start_unsolicited(now, context); param.newone = 1; /* include deletion */ if (context->flags & CONTEXT_RA_NAME) |