diff options
Diffstat (limited to 'src/lease.c')
-rw-r--r-- | src/lease.c | 725 |
1 files changed, 632 insertions, 93 deletions
diff --git a/src/lease.c b/src/lease.c index cfa7543..8adb605 100644 --- a/src/lease.c +++ b/src/lease.c @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2011 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2015 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 @@ -24,19 +24,13 @@ static int dns_dirty, file_dirty, leases_left; void lease_init(time_t now) { unsigned long ei; - struct in_addr addr; + struct all_addr addr; struct dhcp_lease *lease; int clid_len, hw_len, hw_type; FILE *leasestream; - /* These each hold a DHCP option max size 255 - and get a terminating zero added */ - daemon->dhcp_buff = safe_malloc(256); - daemon->dhcp_buff2 = safe_malloc(256); - daemon->dhcp_buff3 = safe_malloc(256); - leases_left = daemon->dhcp_max; - + if (option_bool(OPT_LEASE_RO)) { /* run "<lease_change_script> init" once to get the @@ -73,23 +67,70 @@ void lease_init(time_t now) /* client-id max length is 255 which is 255*2 digits + 254 colons borrow DNS packet buffer which is always larger than 1000 bytes */ if (leasestream) - while (fscanf(leasestream, "%lu %255s %16s %255s %764s", - &ei, daemon->dhcp_buff2, daemon->namebuff, - daemon->dhcp_buff, daemon->packet) == 5) + while (fscanf(leasestream, "%255s %255s", daemon->dhcp_buff3, daemon->dhcp_buff2) == 2) { - hw_len = parse_hex(daemon->dhcp_buff2, (unsigned char *)daemon->dhcp_buff2, DHCP_CHADDR_MAX, NULL, &hw_type); - /* For backwards compatibility, no explict MAC address type means ether. */ - if (hw_type == 0 && hw_len != 0) - hw_type = ARPHRD_ETHER; +#ifdef HAVE_DHCP6 + if (strcmp(daemon->dhcp_buff3, "duid") == 0) + { + daemon->duid_len = parse_hex(daemon->dhcp_buff2, (unsigned char *)daemon->dhcp_buff2, 130, NULL, NULL); + daemon->duid = safe_malloc(daemon->duid_len); + memcpy(daemon->duid, daemon->dhcp_buff2, daemon->duid_len); + continue; + } +#endif + + ei = atol(daemon->dhcp_buff3); - addr.s_addr = inet_addr(daemon->namebuff); + if (fscanf(leasestream, " %64s %255s %764s", + daemon->namebuff, daemon->dhcp_buff, daemon->packet) != 3) + break; - /* decode hex in place */ clid_len = 0; if (strcmp(daemon->packet, "*") != 0) clid_len = parse_hex(daemon->packet, (unsigned char *)daemon->packet, 255, NULL, NULL); - if (!(lease = lease_allocate(addr))) + if (inet_pton(AF_INET, daemon->namebuff, &addr.addr.addr4) && + (lease = lease4_allocate(addr.addr.addr4))) + { + hw_len = parse_hex(daemon->dhcp_buff2, (unsigned char *)daemon->dhcp_buff2, DHCP_CHADDR_MAX, NULL, &hw_type); + /* For backwards compatibility, no explict MAC address type means ether. */ + if (hw_type == 0 && hw_len != 0) + hw_type = ARPHRD_ETHER; + + lease_set_hwaddr(lease, (unsigned char *)daemon->dhcp_buff2, (unsigned char *)daemon->packet, + hw_len, hw_type, clid_len, now, 0); + + if (strcmp(daemon->dhcp_buff, "*") != 0) + lease_set_hostname(lease, daemon->dhcp_buff, 0, get_domain(lease->addr), NULL); + } +#ifdef HAVE_DHCP6 + else if (inet_pton(AF_INET6, daemon->namebuff, &addr.addr.addr6)) + { + char *s = daemon->dhcp_buff2; + int lease_type = LEASE_NA; + int iaid; + + if (s[0] == 'T') + { + lease_type = LEASE_TA; + s++; + } + + iaid = strtoul(s, NULL, 10); + + if ((lease = lease6_allocate(&addr.addr.addr6, lease_type))) + { + lease_set_hwaddr(lease, NULL, (unsigned char *)daemon->packet, 0, 0, clid_len, now, 0); + lease_set_iaid(lease, iaid); + if (strcmp(daemon->dhcp_buff, "*") != 0) + lease_set_hostname(lease, daemon->dhcp_buff, 0, get_domain6((struct in6_addr *)lease->hwaddr), NULL); + } + } +#endif + else + break; + + if (!lease) die (_("too many stored leases"), NULL, EC_MISC); #ifdef HAVE_BROKEN_RTC @@ -104,14 +145,9 @@ void lease_init(time_t now) lease->expires = (time_t)ei; #endif - lease_set_hwaddr(lease, (unsigned char *)daemon->dhcp_buff2, (unsigned char *)daemon->packet, hw_len, hw_type, clid_len); - - if (strcmp(daemon->dhcp_buff, "*") != 0) - lease_set_hostname(lease, daemon->dhcp_buff, 0); - /* set these correctly: the "old" events are generated later from the startup synthesised SIGHUP. */ - lease->new = lease->changed = 0; + lease->flags &= ~(LEASE_NEW | LEASE_CHANGED); } #ifdef HAVE_SCRIPT @@ -150,17 +186,19 @@ void lease_update_from_configs(void) struct dhcp_lease *lease; struct dhcp_config *config; char *name; - + for (lease = leases; lease; lease = lease->next) - if ((config = find_config(daemon->dhcp_conf, NULL, lease->clid, lease->clid_len, - lease->hwaddr, lease->hwaddr_len, lease->hwaddr_type, NULL)) && - (config->flags & CONFIG_NAME) && - (!(config->flags & CONFIG_ADDR) || config->addr.s_addr == lease->addr.s_addr)) - lease_set_hostname(lease, config->hostname, 1); + if (lease->flags & (LEASE_TA | LEASE_NA)) + continue; + else if ((config = find_config(daemon->dhcp_conf, NULL, lease->clid, lease->clid_len, + lease->hwaddr, lease->hwaddr_len, lease->hwaddr_type, NULL)) && + (config->flags & CONFIG_NAME) && + (!(config->flags & CONFIG_ADDR) || config->addr.s_addr == lease->addr.s_addr)) + lease_set_hostname(lease, config->hostname, 1, get_domain(lease->addr), NULL); else if ((name = host_from_dns(lease->addr))) - lease_set_hostname(lease, name, 1); /* updates auth flag only */ + lease_set_hostname(lease, name, 1, get_domain(lease->addr), NULL); /* updates auth flag only */ } - + static void ourprintf(int *errp, char *format, ...) { va_list ap; @@ -186,11 +224,18 @@ void lease_update_file(time_t now) for (lease = leases; lease; lease = lease->next) { + +#ifdef HAVE_DHCP6 + if (lease->flags & (LEASE_TA | LEASE_NA)) + continue; +#endif + #ifdef HAVE_BROKEN_RTC ourprintf(&err, "%u ", lease->length); #else ourprintf(&err, "%lu ", (unsigned long)lease->expires); #endif + if (lease->hwaddr_type != ARPHRD_ETHER || lease->hwaddr_len == 0) ourprintf(&err, "%.2x-", lease->hwaddr_type); for (i = 0; i < lease->hwaddr_len; i++) @@ -199,8 +244,10 @@ void lease_update_file(time_t now) if (i != lease->hwaddr_len - 1) ourprintf(&err, ":"); } + + inet_ntop(AF_INET, &lease->addr, daemon->addrbuff, ADDRSTRLEN); - ourprintf(&err, " %s ", inet_ntoa(lease->addr)); + ourprintf(&err, " %s ", daemon->addrbuff); ourprintf(&err, "%s ", lease->hostname ? lease->hostname : "*"); if (lease->clid && lease->clid_len != 0) @@ -213,6 +260,44 @@ void lease_update_file(time_t now) ourprintf(&err, "*\n"); } +#ifdef HAVE_DHCP6 + if (daemon->duid) + { + ourprintf(&err, "duid "); + for (i = 0; i < daemon->duid_len - 1; i++) + ourprintf(&err, "%.2x:", daemon->duid[i]); + ourprintf(&err, "%.2x\n", daemon->duid[i]); + + for (lease = leases; lease; lease = lease->next) + { + + if (!(lease->flags & (LEASE_TA | LEASE_NA))) + continue; + +#ifdef HAVE_BROKEN_RTC + ourprintf(&err, "%u ", lease->length); +#else + ourprintf(&err, "%lu ", (unsigned long)lease->expires); +#endif + + inet_ntop(AF_INET6, &lease->addr6, daemon->addrbuff, ADDRSTRLEN); + + ourprintf(&err, "%s%u %s ", (lease->flags & LEASE_TA) ? "T" : "", + lease->iaid, daemon->addrbuff); + ourprintf(&err, "%s ", lease->hostname ? lease->hostname : "*"); + + if (lease->clid && lease->clid_len != 0) + { + for (i = 0; i < lease->clid_len - 1; i++) + ourprintf(&err, "%.2x:", lease->clid[i]); + ourprintf(&err, "%.2x\n", lease->clid[i]); + } + else + ourprintf(&err, "*\n"); + } + } +#endif + if (fflush(daemon->lease_stream) != 0 || fsync(fileno(daemon->lease_stream)) < 0) err = errno; @@ -221,11 +306,33 @@ void lease_update_file(time_t now) file_dirty = 0; } - /* Set alarm for when the first lease expires + slop. */ - for (next_event = 0, lease = leases; lease; lease = lease->next) + /* Set alarm for when the first lease expires. */ + next_event = 0; + +#ifdef HAVE_DHCP6 + /* do timed RAs and determine when the next is, also pings to potential SLAAC addresses */ + if (daemon->doing_ra) + { + time_t event; + + if ((event = periodic_slaac(now, leases)) != 0) + { + if (next_event == 0 || difftime(next_event, event) > 0.0) + next_event = event; + } + + if ((event = periodic_ra(now)) != 0) + { + if (next_event == 0 || difftime(next_event, event) > 0.0) + next_event = event; + } + } +#endif + + for (lease = leases; lease; lease = lease->next) if (lease->expires != 0 && - (next_event == 0 || difftime(next_event, lease->expires + 10) > 0.0)) - next_event = lease->expires + 10; + (next_event == 0 || difftime(next_event, lease->expires) > 0.0)) + next_event = lease->expires; if (err) { @@ -237,25 +344,168 @@ void lease_update_file(time_t now) (unsigned int)difftime(next_event, now)); } - if (next_event != 0) - alarm((unsigned)difftime(next_event, now)); + send_alarm(next_event, now); +} + + +static int find_interface_v4(struct in_addr local, int if_index, char *label, + struct in_addr netmask, struct in_addr broadcast, void *vparam) +{ + struct dhcp_lease *lease; + int prefix = netmask_length(netmask); + + (void) label; + (void) broadcast; + (void) vparam; + + for (lease = leases; lease; lease = lease->next) + if (!(lease->flags & (LEASE_TA | LEASE_NA)) && + is_same_net(local, lease->addr, netmask) && + prefix > lease->new_prefixlen) + { + lease->new_interface = if_index; + lease->new_prefixlen = prefix; + } + + return 1; } -void lease_update_dns(void) +#ifdef HAVE_DHCP6 +static int find_interface_v6(struct in6_addr *local, int prefix, + int scope, int if_index, int flags, + int preferred, int valid, void *vparam) { struct dhcp_lease *lease; + + (void)scope; + (void)flags; + (void)preferred; + (void)valid; + (void)vparam; + + for (lease = leases; lease; lease = lease->next) + if ((lease->flags & (LEASE_TA | LEASE_NA))) + if (is_same_net6(local, &lease->addr6, prefix) && prefix > lease->new_prefixlen) { + /* save prefix length for comparison, as we might get shorter matching + * prefix in upcoming netlink GETADDR responses + * */ + lease->new_interface = if_index; + lease->new_prefixlen = prefix; + } + + return 1; +} + +void lease_ping_reply(struct in6_addr *sender, unsigned char *packet, char *interface) +{ + /* We may be doing RA but not DHCPv4, in which case the lease + database may not exist and we have nothing to do anyway */ + if (daemon->dhcp) + slaac_ping_reply(sender, packet, interface, leases); +} + +void lease_update_slaac(time_t now) +{ + /* Called when we contruct a new RA-names context, to add putative + new SLAAC addresses to existing leases. */ + + struct dhcp_lease *lease; - if (daemon->port != 0 && dns_dirty) + if (daemon->dhcp) + for (lease = leases; lease; lease = lease->next) + slaac_add_addrs(lease, now, 0); +} + +#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(time_t now) +{ + struct dhcp_lease *lease; + + for (lease = leases; lease; lease = lease->next) + lease->new_prefixlen = lease->new_interface = 0; + + iface_enumerate(AF_INET, &now, find_interface_v4); +#ifdef HAVE_DHCP6 + iface_enumerate(AF_INET6, &now, find_interface_v6); +#endif + + for (lease = leases; lease; lease = lease->next) + if (lease->new_interface != 0) + lease_set_interface(lease, lease->new_interface, now); +} + +#ifdef HAVE_DHCP6 +void lease_make_duid(time_t now) +{ + /* If we're not doing DHCPv6, and there are not v6 leases, don't add the DUID to the database */ + if (!daemon->duid && daemon->doing_dhcp6) { - cache_unhash_dhcp(); + file_dirty = 1; + make_duid(now); + } +} +#endif + + + + +void lease_update_dns(int force) +{ + struct dhcp_lease *lease; + + if (daemon->port != 0 && (dns_dirty || force)) + { +#ifndef HAVE_BROKEN_RTC + /* force transfer to authoritative secondaries */ + daemon->soa_sn++; +#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 slaac_address *slaac; + + for (slaac = lease->slaac_address; slaac; slaac = slaac->next) + if (slaac->backoff == 0) + { + if (lease->fqdn) + cache_add_dhcp_entry(lease->fqdn, AF_INET6, (struct all_addr *)&slaac->addr, lease->expires); + if (!option_bool(OPT_DHCP_FQDN) && lease->hostname) + cache_add_dhcp_entry(lease->hostname, AF_INET6, (struct all_addr *)&slaac->addr, lease->expires); + } + } + if (lease->fqdn) - cache_add_dhcp_entry(lease->fqdn, &lease->addr, lease->expires); + cache_add_dhcp_entry(lease->fqdn, prot, + prot == AF_INET ? (struct all_addr *)&lease->addr : (struct all_addr *)&lease->addr6, + lease->expires); if (!option_bool(OPT_DHCP_FQDN) && lease->hostname) - cache_add_dhcp_entry(lease->hostname, &lease->addr, lease->expires); + cache_add_dhcp_entry(lease->hostname, prot, + prot == AF_INET ? (struct all_addr *)&lease->addr : (struct all_addr *)&lease->addr6, + lease->expires); + +#else + if (lease->fqdn) + cache_add_dhcp_entry(lease->fqdn, prot, (struct all_addr *)&lease->addr, lease->expires); + + if (!option_bool(OPT_DHCP_FQDN) && lease->hostname) + cache_add_dhcp_entry(lease->hostname, prot, (struct all_addr *)&lease->addr, lease->expires); +#endif } dns_dirty = 0; @@ -275,7 +525,7 @@ void lease_prune(struct dhcp_lease *target, time_t now) if (lease->hostname) dns_dirty = 1; - *up = lease->next; /* unlink */ + *up = lease->next; /* unlink */ /* Put on old_leases list 'till we can run the script */ @@ -297,18 +547,30 @@ struct dhcp_lease *lease_find_by_client(unsigned char *hwaddr, int hw_len, int h if (clid) for (lease = leases; lease; lease = lease->next) - if (lease->clid && clid_len == lease->clid_len && - memcmp(clid, lease->clid, clid_len) == 0) - return lease; + { +#ifdef HAVE_DHCP6 + if (lease->flags & (LEASE_TA | LEASE_NA)) + continue; +#endif + if (lease->clid && clid_len == lease->clid_len && + memcmp(clid, lease->clid, clid_len) == 0) + return lease; + } for (lease = leases; lease; lease = lease->next) - if ((!lease->clid || !clid) && - hw_len != 0 && - lease->hwaddr_len == hw_len && - lease->hwaddr_type == hw_type && - memcmp(hwaddr, lease->hwaddr, hw_len) == 0) - return lease; - + { +#ifdef HAVE_DHCP6 + if (lease->flags & (LEASE_TA | LEASE_NA)) + continue; +#endif + if ((!lease->clid || !clid) && + hw_len != 0 && + lease->hwaddr_len == hw_len && + lease->hwaddr_type == hw_type && + memcmp(hwaddr, lease->hwaddr, hw_len) == 0) + return lease; + } + return NULL; } @@ -317,27 +579,156 @@ struct dhcp_lease *lease_find_by_addr(struct in_addr addr) struct dhcp_lease *lease; for (lease = leases; lease; lease = lease->next) - if (lease->addr.s_addr == addr.s_addr) + { +#ifdef HAVE_DHCP6 + if (lease->flags & (LEASE_TA | LEASE_NA)) + continue; +#endif + if (lease->addr.s_addr == addr.s_addr) + return lease; + } + + return NULL; +} + +#ifdef HAVE_DHCP6 +/* find address for {CLID, IAID, address} */ +struct dhcp_lease *lease6_find(unsigned char *clid, int clid_len, + int lease_type, int iaid, struct in6_addr *addr) +{ + struct dhcp_lease *lease; + + for (lease = leases; lease; lease = lease->next) + { + if (!(lease->flags & lease_type) || lease->iaid != iaid) + continue; + + if (!IN6_ARE_ADDR_EQUAL(&lease->addr6, addr)) + continue; + + if ((clid_len != lease->clid_len || + memcmp(clid, lease->clid, clid_len) != 0)) + continue; + + return lease; + } + + return NULL; +} + +/* reset "USED flags */ +void lease6_reset(void) +{ + struct dhcp_lease *lease; + + for (lease = leases; lease; lease = lease->next) + lease->flags &= ~LEASE_USED; +} + +/* enumerate all leases belonging to {CLID, IAID} */ +struct dhcp_lease *lease6_find_by_client(struct dhcp_lease *first, int lease_type, unsigned char *clid, int clid_len, int iaid) +{ + struct dhcp_lease *lease; + + if (!first) + first = leases; + else + first = first->next; + + for (lease = first; lease; lease = lease->next) + { + if (lease->flags & LEASE_USED) + continue; + + if (!(lease->flags & lease_type) || lease->iaid != iaid) + continue; + + if ((clid_len != lease->clid_len || + memcmp(clid, lease->clid, clid_len) != 0)) + continue; + return lease; + } + + return NULL; +} + +struct dhcp_lease *lease6_find_by_addr(struct in6_addr *net, int prefix, u64 addr) +{ + struct dhcp_lease *lease; + + for (lease = leases; lease; lease = lease->next) + { + if (!(lease->flags & (LEASE_TA | LEASE_NA))) + continue; + + if (is_same_net6(&lease->addr6, net, prefix) && + (prefix == 128 || addr6part(&lease->addr6) == addr)) + return lease; + } return NULL; +} + +/* Find largest assigned address in context */ +u64 lease_find_max_addr6(struct dhcp_context *context) +{ + struct dhcp_lease *lease; + u64 addr = addr6part(&context->start6); + + if (!(context->flags & (CONTEXT_STATIC | CONTEXT_PROXY))) + for (lease = leases; lease; lease = lease->next) + { + if (!(lease->flags & (LEASE_TA | LEASE_NA))) + continue; + + if (is_same_net6(&lease->addr6, &context->start6, 64) && + addr6part(&lease->addr6) > addr6part(&context->start6) && + addr6part(&lease->addr6) <= addr6part(&context->end6) && + addr6part(&lease->addr6) > addr) + addr = addr6part(&lease->addr6); + } + + return addr; } +#endif + +/* Find largest assigned address in context */ +struct in_addr lease_find_max_addr(struct dhcp_context *context) +{ + struct dhcp_lease *lease; + struct in_addr addr = context->start; + + if (!(context->flags & (CONTEXT_STATIC | CONTEXT_PROXY))) + for (lease = leases; lease; lease = lease->next) + { +#ifdef HAVE_DHCP6 + if (lease->flags & (LEASE_TA | LEASE_NA)) + continue; +#endif + if (((unsigned)ntohl(lease->addr.s_addr)) > ((unsigned)ntohl(context->start.s_addr)) && + ((unsigned)ntohl(lease->addr.s_addr)) <= ((unsigned)ntohl(context->end.s_addr)) && + ((unsigned)ntohl(lease->addr.s_addr)) > ((unsigned)ntohl(addr.s_addr))) + addr = lease->addr; + } + + return addr; +} -struct dhcp_lease *lease_allocate(struct in_addr addr) +static struct dhcp_lease *lease_allocate(void) { struct dhcp_lease *lease; if (!leases_left || !(lease = whine_malloc(sizeof(struct dhcp_lease)))) return NULL; memset(lease, 0, sizeof(struct dhcp_lease)); - lease->new = 1; - lease->addr = addr; - lease->hwaddr_len = 256; /* illegal value */ + lease->flags = LEASE_NEW; lease->expires = 1; #ifdef HAVE_BROKEN_RTC lease->length = 0xffffffff; /* illegal value */ #endif + lease->hwaddr_len = 256; /* illegal value */ lease->next = leases; leases = lease; @@ -347,22 +738,57 @@ struct dhcp_lease *lease_allocate(struct in_addr addr) return lease; } -void lease_set_expires(struct dhcp_lease *lease, unsigned int len, time_t now) +struct dhcp_lease *lease4_allocate(struct in_addr addr) { - time_t exp = now + (time_t)len; + struct dhcp_lease *lease = lease_allocate(); + if (lease) + lease->addr = addr; + return lease; +} + +#ifdef HAVE_DHCP6 +struct dhcp_lease *lease6_allocate(struct in6_addr *addrp, int lease_type) +{ + struct dhcp_lease *lease = lease_allocate(); + + if (lease) + { + lease->addr6 = *addrp; + lease->flags |= lease_type; + lease->iaid = 0; + } + + return lease; +} +#endif + +void lease_set_expires(struct dhcp_lease *lease, unsigned int len, time_t now) +{ + time_t exp; + if (len == 0xffffffff) { exp = 0; len = 0; } - + else + { + exp = now + (time_t)len; + /* Check for 2038 overflow. Make the lease + inifinite in that case, as the least disruptive + thing we can do. */ + if (difftime(exp, now) <= 0.0) + exp = 0; + } + if (exp != lease->expires) { dns_dirty = 1; lease->expires = exp; #ifndef HAVE_BROKEN_RTC - lease->aux_changed = file_dirty = 1; + lease->flags |= LEASE_AUX_CHANGED; + file_dirty = 1; #endif } @@ -370,22 +796,45 @@ void lease_set_expires(struct dhcp_lease *lease, unsigned int len, time_t now) if (len != lease->length) { lease->length = len; - lease->aux_changed = file_dirty = 1; + lease->flags |= LEASE_AUX_CHANGED; + file_dirty = 1; } #endif } -void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr, - unsigned char *clid, int hw_len, int hw_type, int clid_len) +#ifdef HAVE_DHCP6 +void lease_set_iaid(struct dhcp_lease *lease, int iaid) +{ + if (lease->iaid != iaid) + { + lease->iaid = iaid; + lease->flags |= LEASE_CHANGED; + } +} +#endif + +void lease_set_hwaddr(struct dhcp_lease *lease, const unsigned char *hwaddr, + const unsigned char *clid, int hw_len, int hw_type, + int clid_len, time_t now, int force) { +#ifdef HAVE_DHCP6 + int change = force; + lease->flags |= LEASE_HAVE_HWADDR; +#endif + + (void)force; + (void)now; + if (hw_len != lease->hwaddr_len || hw_type != lease->hwaddr_type || (hw_len != 0 && memcmp(lease->hwaddr, hwaddr, hw_len) != 0)) { - memcpy(lease->hwaddr, hwaddr, hw_len); + if (hw_len != 0) + memcpy(lease->hwaddr, hwaddr, hw_len); lease->hwaddr_len = hw_len; lease->hwaddr_type = hw_type; - lease->changed = file_dirty = 1; /* run script on change */ + lease->flags |= LEASE_CHANGED; + file_dirty = 1; /* run script on change */ } /* only update clid when one is available, stops packets @@ -398,18 +847,32 @@ void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr, if (lease->clid_len != clid_len) { - lease->aux_changed = file_dirty = 1; + lease->flags |= LEASE_AUX_CHANGED; + file_dirty = 1; free(lease->clid); if (!(lease->clid = whine_malloc(clid_len))) return; +#ifdef HAVE_DHCP6 + change = 1; +#endif } else if (memcmp(lease->clid, clid, clid_len) != 0) - lease->aux_changed = file_dirty = 1; - + { + lease->flags |= LEASE_AUX_CHANGED; + file_dirty = 1; +#ifdef HAVE_DHCP6 + change = 1; +#endif + } + lease->clid_len = clid_len; memcpy(lease->clid, clid, clid_len); } - + +#ifdef HAVE_DHCP6 + if (change) + slaac_add_addrs(lease, now, force); +#endif } static void kill_name(struct dhcp_lease *lease) @@ -421,7 +884,7 @@ static void kill_name(struct dhcp_lease *lease) free(lease->old_hostname); /* If we know the fqdn, pass that. The helper will derive the - unqualified name from it, free the unqulaified name here. */ + unqualified name from it, free the unqualified name here. */ if (lease->fqdn) { @@ -434,14 +897,18 @@ static void kill_name(struct dhcp_lease *lease) lease->hostname = lease->fqdn = NULL; } -void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth) +void lease_set_hostname(struct dhcp_lease *lease, const char *name, int auth, char *domain, char *config_domain) { struct dhcp_lease *lease_tmp; char *new_name = NULL, *new_fqdn = NULL; + + if (config_domain && (!domain || !hostname_isequal(domain, config_domain))) + my_syslog(MS_DHCP | LOG_WARNING, _("Ignoring domain %s for DHCP host name %s"), config_domain, name); if (lease->hostname && name && hostname_isequal(lease->hostname, name)) { - lease->auth_name = auth; + if (auth) + lease->flags |= LEASE_AUTH_NAME; return; } @@ -451,19 +918,21 @@ void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth) /* If a machine turns up on a new net without dropping the old lease, or two machines claim the same name, then we end up with two interfaces with the same name. Check for that here and remove the name from the old lease. + Note that IPv6 leases are different. All the leases to the same DUID are + allowed the same name. + Don't allow a name from the client to override a name from dnsmasq config. */ if (name) { if ((new_name = whine_malloc(strlen(name) + 1))) { - char *suffix = get_domain(lease->addr); strcpy(new_name, name); - if (suffix && (new_fqdn = whine_malloc(strlen(new_name) + strlen(suffix) + 2))) + if (domain && (new_fqdn = whine_malloc(strlen(new_name) + strlen(domain) + 2))) { strcpy(new_fqdn, name); strcat(new_fqdn, "."); - strcat(new_fqdn, suffix); + strcat(new_fqdn, domain); } } @@ -472,7 +941,7 @@ void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth) { if (option_bool(OPT_DHCP_FQDN)) { - if (!new_fqdn || !lease_tmp->fqdn || !hostname_isequal(lease_tmp->fqdn, new_fqdn) ) + if (!new_fqdn || !lease_tmp->fqdn || !hostname_isequal(lease_tmp->fqdn, new_fqdn)) continue; } else @@ -480,8 +949,22 @@ void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth) if (!new_name || !lease_tmp->hostname || !hostname_isequal(lease_tmp->hostname, new_name) ) continue; } - - if (lease_tmp->auth_name && !auth) + + if (lease->flags & (LEASE_TA | LEASE_NA)) + { + if (!(lease_tmp->flags & (LEASE_TA | LEASE_NA))) + continue; + + /* another lease for the same DUID is OK for IPv6 */ + if (lease->clid_len == lease_tmp->clid_len && + lease->clid && lease_tmp->clid && + memcmp(lease->clid, lease_tmp->clid, lease->clid_len) == 0) + continue; + } + else if (lease_tmp->flags & (LEASE_TA | LEASE_NA)) + continue; + + if ((lease_tmp->flags & LEASE_AUTH_NAME) && !auth) { free(new_name); free(new_fqdn); @@ -498,20 +981,28 @@ void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth) lease->hostname = new_name; lease->fqdn = new_fqdn; - lease->auth_name = auth; + + if (auth) + lease->flags |= LEASE_AUTH_NAME; file_dirty = 1; dns_dirty = 1; - lease->changed = 1; /* run script on change */ + lease->flags |= LEASE_CHANGED; /* run script on change */ } -void lease_set_interface(struct dhcp_lease *lease, int interface) +void lease_set_interface(struct dhcp_lease *lease, int interface, time_t now) { + (void)now; + if (lease->last_interface == interface) return; lease->last_interface = interface; - lease->changed = 1; + lease->flags |= LEASE_CHANGED; + +#ifdef HAVE_DHCP6 + slaac_add_addrs(lease, now, 0); +#endif } void rerun_scripts(void) @@ -519,7 +1010,7 @@ void rerun_scripts(void) struct dhcp_lease *lease; for (lease = leases; lease; lease = lease->next) - lease->changed = 1; + lease->flags |= LEASE_CHANGED; } /* deleted leases get transferred to the old_leases list. @@ -531,6 +1022,8 @@ int do_script_run(time_t now) { struct dhcp_lease *lease; + (void)now; + #ifdef HAVE_DBUS /* If we're going to be sending DBus signals, but the connection is not yet up, delay everything until it is. */ @@ -554,6 +1047,14 @@ int do_script_run(time_t now) } else { +#ifdef HAVE_DHCP6 + struct slaac_address *slaac, *tmp; + for (slaac = lease->slaac_address; slaac; slaac = tmp) + { + tmp = slaac->next; + free(slaac); + } +#endif kill_name(lease); #ifdef HAVE_SCRIPT queue_script(ACTION_DEL, lease, lease->old_hostname, now); @@ -585,18 +1086,18 @@ int do_script_run(time_t now) } for (lease = leases; lease; lease = lease->next) - if (lease->new || lease->changed || - (lease->aux_changed && option_bool(OPT_LEASE_RO))) + if ((lease->flags & (LEASE_NEW | LEASE_CHANGED)) || + ((lease->flags & LEASE_AUX_CHANGED) && option_bool(OPT_LEASE_RO))) { #ifdef HAVE_SCRIPT - queue_script(lease->new ? ACTION_ADD : ACTION_OLD, lease, + queue_script((lease->flags & LEASE_NEW) ? ACTION_ADD : ACTION_OLD, lease, lease->fqdn ? lease->fqdn : lease->hostname, now); #endif #ifdef HAVE_DBUS - emit_dbus_signal(lease->new ? ACTION_ADD : ACTION_OLD, lease, + emit_dbus_signal((lease->flags & LEASE_NEW) ? ACTION_ADD : ACTION_OLD, lease, lease->fqdn ? lease->fqdn : lease->hostname); #endif - lease->new = lease->changed = lease->aux_changed = 0; + lease->flags &= ~(LEASE_NEW | LEASE_CHANGED | LEASE_AUX_CHANGED); /* this is used for the "add" call, then junked, since they're not in the database */ free(lease->extradata); @@ -608,6 +1109,44 @@ int do_script_run(time_t now) return 0; /* nothing to do */ } +#ifdef HAVE_SCRIPT +void lease_add_extradata(struct dhcp_lease *lease, unsigned char *data, unsigned int len, int delim) +{ + unsigned int i; + + /* check for embeded NULLs */ + for (i = 0; i < len; i++) + if (data[i] == 0) + { + len = i; + break; + } + + if ((lease->extradata_size - lease->extradata_len) < (len + 1)) + { + size_t newsz = lease->extradata_len + len + 100; + unsigned char *new = whine_malloc(newsz); + + if (!new) + return; + + if (lease->extradata) + { + memcpy(new, lease->extradata, lease->extradata_len); + free(lease->extradata); + } + + lease->extradata = new; + lease->extradata_size = newsz; + } + + if (len != 0) + memcpy(lease->extradata + lease->extradata_len, data, len); + lease->extradata[lease->extradata_len + len] = delim; + lease->extradata_len += len + 1; +} +#endif + #endif |