Pali Rohár
2015-12-23 21:10:44 UTC
This patch allows to assign one IPv6 address for more config entries
specified by MAC address. This is similar function as for IPv4 addresses
in DHCPv4 server code part.
---
man/dnsmasq.8 | 9 ++++++---
src/rfc3315.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++---------
2 files changed, 60 insertions(+), 12 deletions(-)
diff --git a/man/dnsmasq.8 b/man/dnsmasq.8
index d51b10f..6a121fe 100644
--- a/man/dnsmasq.8
+++ b/man/dnsmasq.8
@@ -978,10 +978,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 3ed8623..19738b4 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);
@@ -704,7 +704,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);
@@ -713,8 +713,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
@@ -730,10 +736,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))
@@ -758,6 +767,12 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
req_addr = <mp->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);
@@ -898,7 +913,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);
@@ -1017,6 +1032,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))
@@ -1042,6 +1068,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
if (preferred_time == 0)
message = _("deprecated");
+ }
}
else
{
@@ -1088,13 +1115,27 @@ 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 = opt6_ptr(ia_option, 0);
+ struct in6_addr addr;
+ struct dhcp_context *c;
+ int config_addr_ok = 1;
+
+ 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);
put_opt6_string(_("confirm failed"));
end_opt6(o1);
+ log6_quiet(state, "DHCPCONFIRM", req_addr, "(confirm failed)");
return 1;
}
@@ -1715,11 +1756,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;
specified by MAC address. This is similar function as for IPv4 addresses
in DHCPv4 server code part.
---
man/dnsmasq.8 | 9 ++++++---
src/rfc3315.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++---------
2 files changed, 60 insertions(+), 12 deletions(-)
diff --git a/man/dnsmasq.8 b/man/dnsmasq.8
index d51b10f..6a121fe 100644
--- a/man/dnsmasq.8
+++ b/man/dnsmasq.8
@@ -978,10 +978,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 3ed8623..19738b4 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);
@@ -704,7 +704,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);
@@ -713,8 +713,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
@@ -730,10 +736,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))
@@ -758,6 +767,12 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
req_addr = <mp->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);
@@ -898,7 +913,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);
@@ -1017,6 +1032,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))
@@ -1042,6 +1068,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
if (preferred_time == 0)
message = _("deprecated");
+ }
}
else
{
@@ -1088,13 +1115,27 @@ 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 = opt6_ptr(ia_option, 0);
+ struct in6_addr addr;
+ struct dhcp_context *c;
+ int config_addr_ok = 1;
+
+ 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);
put_opt6_string(_("confirm failed"));
end_opt6(o1);
+ log6_quiet(state, "DHCPCONFIRM", req_addr, "(confirm failed)");
return 1;
}
@@ -1715,11 +1756,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;
--
1.7.9.5
1.7.9.5