lede/package/network/services/dnsmasq/patches/300-DHCPv6-Honor-assigning-IPv6-address-based-on-MAC-add.patch

167 lines
7.0 KiB
Diff

From 93ac8f9d469ff08d41170eb6934842b3626d5fdd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
Date: Wed, 23 Dec 2015 22:10:44 +0100
Subject: [PATCH] DHCPv6: Honor assigning IPv6 address based on MAC address
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.
---
src/rfc3315.c | 55 +++++++++++++++++++++++++++++++++++++++++++--------
1 file changed, 47 insertions(+), 8 deletions(-)
--- a/src/rfc3315.c
+++ b/src/rfc3315.c
@@ -48,7 +48,7 @@ static int build_ia(struct state *state,
static void end_ia(int t1cntr, unsigned int min_time, int do_fuzz);
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 int config_valid(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr, struct state *state, time_t now);
static struct addrlist *config_implies(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr);
static void add_address(struct state *state, struct dhcp_context *context, unsigned int lease_time, void *ia_option,
@@ -688,8 +688,13 @@ static int dhcp6_no_relay(struct state *
}
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_implies(config, c, &req_addr))
+ continue; /* another static address is configured */
/* add address to output packet */
add_address(state, c, lease_time, ia_option, &min_time, &req_addr, now);
@@ -701,7 +706,10 @@ static int dhcp6_no_relay(struct state *
/* 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, state, now))
{
@@ -725,6 +733,11 @@ static int dhcp6_no_relay(struct state *
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_implies(config, c, &req_addr))
+ continue; /* skip this lease because another static address is configured */
add_address(state, c, c->lease_time, NULL, &min_time, &req_addr, now);
mark_context_used(state, &req_addr);
get_context_tag(state, c);
@@ -859,7 +872,7 @@ static int dhcp6_no_relay(struct state *
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);
@@ -989,6 +1002,16 @@ static int dhcp6_no_relay(struct state *
{
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_implies(config, this_context, &req_addr))
+ {
+ preferred_time = valid_time = 0;
+ message = _("deprecated");
+ }
+ else
+ {
get_context_tag(state, this_context);
if (config_implies(config, this_context, &req_addr) && have_config(config, CONFIG_TIME))
@@ -1014,6 +1037,7 @@ static int dhcp6_no_relay(struct state *
if (preferred_time == 0)
message = _("deprecated");
+ }
address_assigned = 1;
}
@@ -1070,11 +1094,22 @@ static int dhcp6_no_relay(struct state *
ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))
{
struct in6_addr req_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_implies(config, c, &req_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);
@@ -1692,11 +1727,15 @@ static void mark_config_used(struct dhcp
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;
@@ -1773,7 +1812,7 @@ static int config_valid(struct dhcp_conf
{
setaddr6part(addr, addrpart+i);
- if (check_address(state, addr))
+ if (check_address(state, config, addr))
return 1;
}
}