Currently IPv6 addresses are assigned to tuple (IAID, DUID). When system changes IAID/DUID then old assigned IPv6 address cannot be reused, even when in config file was DHCPv6 assignment based on MAC address (and not on DUID).
IAID/DUID is changed when rebooting from one operating system to another; or after reinstalling system. In reality it is normal that DUID of some machine is changed, so people rather assign also IPv6 addresses based on MAC address. So assigning IPv6 based on MAC address in dnsmasq is currently semi-broken. This patch tries to fix it and honors IPv6 config rules with MAC address, to always assign particular IPv6 address to specific MAC address (when configured). And ignores the fact if IAID/DUID was changed. Normally IPv6 address should be assigned by IAID/DUID (which also state DHCPv6 RFCs), but dnsmasq has already some support for assigning IPv6 address based on MAC address, when users configured in config file. So this patch just tries to fix above problem for user configuration with MAC addresses. It does not change assignment based on DUID. Also this patch adds support for allowing IPv6 address to be associated with multiple hardware addresses, and gives dnsmasq permission to abandon a lease. This is similar functionality as already supported for IPv4 address. --- man/dnsmasq.8 | 9 ++++++--- src/rfc3315.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 59 insertions(+), 12 deletions(-) diff --git a/man/dnsmasq.8 b/man/dnsmasq.8 index f01a5ba..8614f08 100644 --- a/man/dnsmasq.8 +++ b/man/dnsmasq.8 @@ -1068,10 +1068,13 @@ will only match a Token-Ring hardware address, since the ARP-address type for token ring is 6. -As a special case, in DHCPv4, it is possible to include more than one -hardware address. eg: +It is possible to include more than one hardware address. eg for IPv4: .B --dhcp-host=11:22:33:44:55:66,12:34:56:78:90:12,192.168.0.2 -This allows an IP address to be associated with +or for IPv6: +.B --dhcp-host=11:22:33:44:55:66,12:34:56:78:90:12,[::2] +or for both: +.B --dhcp-host=11:22:33:44:55:66,12:34:56:78:90:12,192.168.0.2,[::2] +This allows an IPv4 and/or IPv6 address to be associated with multiple hardware addresses, and gives dnsmasq permission to abandon a DHCP lease to one of the hardware addresses when another one asks for a lease. Beware that this is a dangerous thing to do, it will only diff --git a/src/rfc3315.c b/src/rfc3315.c index a20776d..c83cf2d 100644 --- a/src/rfc3315.c +++ b/src/rfc3315.c @@ -54,7 +54,7 @@ static struct prefix_class *prefix_class_from_context(struct dhcp_context *conte #endif static void mark_context_used(struct state *state, struct in6_addr *addr); static void mark_config_used(struct dhcp_context *context, struct in6_addr *addr); -static int check_address(struct state *state, struct in6_addr *addr); +static int check_address(struct state *state, struct dhcp_config *config, struct in6_addr *addr); static void add_address(struct state *state, struct dhcp_context *context, unsigned int lease_time, void *ia_option, unsigned int *min_time, struct in6_addr *addr, time_t now); static void update_leases(struct state *state, struct dhcp_context *context, struct in6_addr *addr, unsigned int lease_time, time_t now); @@ -746,7 +746,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ /* If the client asks for an address on the same network as a configured address, offer the configured address instead, to make moving to newly-configured addresses automatic. */ - if (!(c->flags & CONTEXT_CONF_USED) && config_valid(config, c, &addr) && check_address(state, &addr)) + if (!(c->flags & CONTEXT_CONF_USED) && config_valid(config, c, &addr) && check_address(state, config, &addr)) { req_addr = addr; mark_config_used(c, &addr); @@ -755,8 +755,14 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ } else if (!(c = address6_available(state->context, &req_addr, solicit_tags, plain_range))) continue; /* not an address we're allowed */ - else if (!check_address(state, &req_addr)) + else if (!check_address(state, config, &req_addr)) continue; /* address leased elsewhere */ + else if (state->mac_len && config && + config_has_mac(config, state->mac, state->mac_len, state->mac_type) && + match_netid(c->filter, solicit_tags, plain_range) && + config_valid(config, c, &addr) && + !IN6_ARE_ADDR_EQUAL(&req_addr, &addr)) + continue; /* another static address is configured */ /* add address to output packet */ #ifdef OPTION6_PREFIX_CLASS @@ -772,10 +778,13 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ /* Suggest configured address(es) */ for (c = state->context; c; c = c->current) - if (!(c->flags & CONTEXT_CONF_USED) && + if ((!(c->flags & CONTEXT_CONF_USED) || + (state->mac_len && config && + config_has_mac(config, state->mac, state->mac_len, state->mac_type) + )) && match_netid(c->filter, solicit_tags, plain_range) && config_valid(config, c, &addr) && - check_address(state, &addr)) + check_address(state, config, &addr)) { mark_config_used(state->context, &addr); if (have_config(config, CONFIG_TIME)) @@ -800,6 +809,12 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ req_addr = ltmp->addr6; if ((c = address6_available(state->context, &req_addr, solicit_tags, plain_range))) { + if (state->mac_len && config && + config_has_mac(config, state->mac, state->mac_len, state->mac_type) && + match_netid(c->filter, solicit_tags, plain_range) && + config_valid(config, c, &addr) && + !IN6_ARE_ADDR_EQUAL(&req_addr, &addr)) + continue; /* skip this lease because another static address is configured */ #ifdef OPTION6_PREFIX_CLASS if (dump_all_prefix_classes && state->ia_type == OPTION6_IA_NA) state->send_prefix_class = prefix_class_from_context(c); @@ -943,7 +958,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ put_opt6_string(_("address unavailable")); end_opt6(o1); } - else if (!check_address(state, &req_addr)) + else if (!check_address(state, config, &req_addr)) { /* Address leased to another DUID/IAID */ o1 = new_opt6(OPTION6_STATUS_CODE); @@ -1064,6 +1079,17 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ struct in6_addr addr; unsigned int lease_time; + /* check if another static address is preferred */ + if (state->mac_len && config && + config_has_mac(config, state->mac, state->mac_len, state->mac_type) && + config_valid(config, this_context, &addr) && + !IN6_ARE_ADDR_EQUAL(&req_addr, &addr)) + { + preferred_time = valid_time = 0; + message = _("deprecated"); + } + else + { get_context_tag(state, this_context); if (config_valid(config, this_context, &addr) && IN6_ARE_ADDR_EQUAL(&addr, &req_addr) && have_config(config, CONFIG_TIME)) @@ -1089,6 +1115,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ if (preferred_time == 0) message = _("deprecated"); + } } else { @@ -1135,11 +1162,24 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24)) { struct in6_addr req_addr; + struct in6_addr addr; + struct dhcp_context *c; + int config_addr_ok = 1; /* alignment */ memcpy(&req_addr, opt6_ptr(ia_option, 0), IN6ADDRSZ); + + c = address6_valid(state->context, &req_addr, tagif, 1); + + if (c && state->mac_len && config && + config_has_mac(config, state->mac, state->mac_len, state->mac_type) && + config_valid(config, c, &addr) && + !IN6_ARE_ADDR_EQUAL(&req_addr, &addr)) + { + config_addr_ok = 0; + } - if (!address6_valid(state->context, &req_addr, tagif, 1)) + if (!c || !config_addr_ok) { o1 = new_opt6(OPTION6_STATUS_CODE); put_opt6_short(DHCP6NOTONLINK); @@ -1772,11 +1812,15 @@ static void mark_config_used(struct dhcp_context *context, struct in6_addr *addr context->flags |= CONTEXT_CONF_USED; } -/* make sure address not leased to another CLID/IAID */ -static int check_address(struct state *state, struct in6_addr *addr) +/* check that ipv6 address belongs to config with same mac address as in state or ipv6 address is not leased to another CLID/IAID */ +static int check_address(struct state *state, struct dhcp_config *config, struct in6_addr *addr) { struct dhcp_lease *lease; + if (state->mac_len && config && + config_has_mac(config, state->mac, state->mac_len, state->mac_type)) + return 1; + if (!(lease = lease6_find_by_addr(addr, 128, 0))) return 1; -- 2.11.0 _______________________________________________ Dnsmasq-discuss mailing list Dnsmasq-discuss@lists.thekelleys.org.uk http://lists.thekelleys.org.uk/mailman/listinfo/dnsmasq-discuss