6fa8a9
From 268080fc19990711a1d1e1acd68a50aa2f6cb5fb Mon Sep 17 00:00:00 2001
6fa8a9
From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= <pemensik@redhat.com>
6fa8a9
Date: Fri, 17 Sep 2021 20:12:21 +0200
6fa8a9
Subject: [PATCH] Offer alternative DHCPv6 address if requested is taken
6fa8a9
6fa8a9
In some cases multiple requests might arrive from single DUID. It may
6fa8a9
happen just one address is offered to different IAID requests. When
6fa8a9
the first request confirms lease, another would be offered alternative
6fa8a9
address instead of address in use error.
6fa8a9
6fa8a9
Includes check on such Rapid commit equivalents and returns NotOnLink
6fa8a9
error, required by RFC 8145, if requested address were not on any
6fa8a9
supported prefix.
6fa8a9
---
6fa8a9
 src/rfc3315.c | 39 ++++++++++++++++++++++++++++-----------
6fa8a9
 1 file changed, 28 insertions(+), 11 deletions(-)
6fa8a9
6fa8a9
diff --git a/src/rfc3315.c b/src/rfc3315.c
6fa8a9
index 5c2ff97..d1534ad 100644
6fa8a9
--- a/src/rfc3315.c
6fa8a9
+++ b/src/rfc3315.c
6fa8a9
@@ -614,7 +614,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
6fa8a9
       
6fa8a9
     case DHCP6SOLICIT:
6fa8a9
       {
6fa8a9
-      	int address_assigned = 0;
6fa8a9
+	int address_assigned = 0, ia_invalid = 0;
6fa8a9
 	/* tags without all prefix-class tags */
6fa8a9
 	struct dhcp_netid *solicit_tags;
6fa8a9
 	struct dhcp_context *c;
6fa8a9
@@ -697,6 +697,8 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
6fa8a9
 		    get_context_tag(state, c);
6fa8a9
 		    address_assigned = 1;
6fa8a9
 		  }
6fa8a9
+		else
6fa8a9
+		  ia_invalid++;
6fa8a9
 	      }
6fa8a9
 	    
6fa8a9
 	    /* Suggest configured address(es) */
6fa8a9
@@ -782,11 +784,26 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
6fa8a9
 	    tagif = add_options(state, 0);
6fa8a9
 	  }
6fa8a9
 	else
6fa8a9
-	  { 
6fa8a9
+	  {
6fa8a9
+	    char *errmsg;
6fa8a9
 	    /* no address, return error */
6fa8a9
 	    o1 = new_opt6(OPTION6_STATUS_CODE);
6fa8a9
-	    put_opt6_short(DHCP6NOADDRS);
6fa8a9
-	    put_opt6_string(_("no addresses available"));
6fa8a9
+	    if (state->lease_allocate && ia_invalid)
6fa8a9
+	      {
6fa8a9
+		/* RFC 8415, Section 18.3.2:
6fa8a9
+		   If any of the prefixes of the included addresses are not
6fa8a9
+		   appropriate for the link to which the client is connected,
6fa8a9
+		   the server MUST return the IA to the client with a Status
6fa8a9
+		   Code option with the value NotOnLink. */
6fa8a9
+		put_opt6_short(DHCP6NOTONLINK);
6fa8a9
+		errmsg = _("not on link");
6fa8a9
+	      }
6fa8a9
+	    else
6fa8a9
+	      {
6fa8a9
+		put_opt6_short(DHCP6NOADDRS);
6fa8a9
+		errmsg = _("no addresses available");
6fa8a9
+	      }
6fa8a9
+	    put_opt6_string(errmsg);
6fa8a9
 	    end_opt6(o1);
6fa8a9
 
6fa8a9
 	    /* Some clients will ask repeatedly when we're not giving
6fa8a9
@@ -795,7 +812,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
6fa8a9
 	    for (c = state->context; c; c = c->current)
6fa8a9
 	      if (!(c->flags & CONTEXT_RA_STATELESS))
6fa8a9
 		{
6fa8a9
-		  log6_packet(state, state->lease_allocate ? "DHCPREPLY" : "DHCPADVERTISE", NULL, _("no addresses available"));
6fa8a9
+		  log6_packet(state, state->lease_allocate ? "DHCPREPLY" : "DHCPADVERTISE", NULL, errmsg);
6fa8a9
 		  break;
6fa8a9
 		}
6fa8a9
 	  }
6fa8a9
@@ -831,7 +848,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
6fa8a9
 		 /* If we get a request with an IA_*A without addresses, treat it exactly like
6fa8a9
 		    a SOLICT with rapid commit set. */
6fa8a9
 		 save_counter(start);
6fa8a9
-		 goto request_no_address; 
6fa8a9
+		 goto request_no_address;
6fa8a9
 	       }
6fa8a9
 
6fa8a9
 	    o = build_ia(state, &t1cntr);
6fa8a9
@@ -861,11 +878,11 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
6fa8a9
 		      }
6fa8a9
 		    else if (!check_address(state, &req_addr))
6fa8a9
 		      {
6fa8a9
-			/* Address leased to another DUID/IAID */
6fa8a9
-			o1 = new_opt6(OPTION6_STATUS_CODE);
6fa8a9
-			put_opt6_short(DHCP6UNSPEC);
6fa8a9
-			put_opt6_string(_("address in use"));
6fa8a9
-			end_opt6(o1);
6fa8a9
+			/* Address leased to another DUID/IAID.
6fa8a9
+			   Find another address for the client, treat it exactly like
6fa8a9
+			   a SOLICT with rapid commit set. */
6fa8a9
+			save_counter(start);
6fa8a9
+			goto request_no_address;
6fa8a9
 		      } 
6fa8a9
 		    else 
6fa8a9
 		      {
6fa8a9
-- 
6fa8a9
2.31.1
6fa8a9