[Dnsmasq-discuss] [PATCH] DHCPv6: Add support for more than one hardware address per IPv6 address

Pali Rohár pali.rohar at gmail.com
Wed Dec 23 21:10:44 GMT 2015


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 = &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);
@@ -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




More information about the Dnsmasq-discuss mailing list