[PATCH RFC v2 1/3] dhclient: IPv6: factor common code in dhc6.c

H. Peter Anvin hpa at zytor.com
Wed Dec 2 19:21:15 UTC 2015


Factor a number of common code idioms into functions, rather than
replicating the code in several places.

Signed-off-by: H. Peter Anvin <hpa at zytor.com>
---
 client/dhc6.c | 1054 +++++++++++++++++++++------------------------------------
 1 file changed, 394 insertions(+), 660 deletions(-)

diff --git a/client/dhc6.c b/client/dhc6.c
index 37ce739460ff..8f77fafd7281 100644
--- a/client/dhc6.c
+++ b/client/dhc6.c
@@ -74,22 +74,39 @@ static struct dhc6_addr *find_pref(struct dhc6_addr *head,
 void init_handler(struct packet *packet, struct client_state *client);
 void info_request_handler(struct packet *packet, struct client_state *client);
 void rapid_commit_handler(struct packet *packet, struct client_state *client);
+static void dhc6_new_iaid(unsigned char iaid[4],
+			  const struct client_state *client, int i);
 void do_init6(void *input);
 void do_info_request6(void *input);
 void do_confirm6(void *input);
 void reply_handler(struct packet *packet, struct client_state *client);
-static isc_result_t dhc6_add_ia_na(struct client_state *client,
-				   struct data_string *packet,
-				   struct dhc6_lease *lease,
-				   u_int8_t message);
-static isc_result_t dhc6_add_ia_ta(struct client_state *client,
-				   struct data_string *packet,
-				   struct dhc6_lease *lease,
-				   u_int8_t message);
+static isc_result_t dhc6_add_ia_na_ta(struct client_state *client,
+				      struct data_string *packet,
+				      struct option *option,
+				      struct dhc6_lease *lease,
+				      u_int8_t message);
+static isc_result_t dhc6_make_ia_na_ta(const struct client_state *client,
+				       struct data_string *packet,
+				       struct option *option,
+				       const struct dhc6_addr *addr,
+				       const unsigned char iaid[4],
+				       u_int8_t message);
+static isc_result_t dhc6_make_iaaddr(struct data_string *iads,
+				     const struct dhc6_addr *old_addr,
+				     TIME t1, TIME t2);
 static isc_result_t dhc6_add_ia_pd(struct client_state *client,
 				   struct data_string *packet,
 				   struct dhc6_lease *lease,
 				   u_int8_t message);
+static isc_result_t dhc6_make_ia_pd(const struct client_state *client,
+				    struct data_string *packet,
+				    const struct dhc6_addr *pref,
+				    const unsigned char iaid[4],
+				    u_int8_t message);
+static isc_result_t dhc6_make_iaprefix(struct data_string *ia,
+				       const struct dhc6_addr *old_addr,
+				       TIME t1, TIME t2);
+static void putTimes(unsigned char *, TIME t1, TIME t2);
 static isc_boolean_t stopping_finished(void);
 static void dhc6_merge_lease(struct dhc6_lease *src, struct dhc6_lease *dst);
 void do_select6(void *input);
@@ -1496,6 +1513,26 @@ check_timing6 (struct client_state *client, u_int8_t msg_type,
 	return(CHK_TIM_SUCCESS);
 }
 
+static void
+dhc6_new_iaid(unsigned char iaid[4], const struct client_state *client, int i)
+{
+	int idx, len;
+
+	/*
+	 * A simple IAID is the last 4 bytes of the hardware address.
+	 */
+	if (client->interface->hw_address.hlen > 4) {
+		idx = client->interface->hw_address.hlen - 4;
+		len = 4;
+	} else {
+		idx = 0;
+		len = client->interface->hw_address.hlen;
+	}
+	memset(iaid, 0, 4);
+	memcpy(iaid, client->interface->hw_address.hbuf + idx, len);
+	iaid[3] += i;
+}
+
 /*
  * do_init6() marshals and transmits a solicit.
  */
@@ -1506,11 +1543,9 @@ do_init6(void *input)
 	struct dhc6_ia *old_ia;
 	struct dhc6_addr *old_addr;
 	struct data_string ds;
-	struct data_string ia;
-	struct data_string addr;
 	struct timeval tv;
-	u_int32_t t1, t2;
-	int i, idx, len, send_ret;
+	unsigned char iaid[4];
+	int i, send_ret;
 
 	client = input;
 
@@ -1559,100 +1594,20 @@ do_init6(void *input)
 		 * cache.  They'd have to be pulled down as they also contain
 		 * different option caches in the same universe...
 		 */
-		memset(&ia, 0, sizeof(ia));
-		if (!buffer_allocate(&ia.buffer, 12, MDL)) {
-			log_error("Unable to allocate memory for IA_NA.");
-			data_string_forget(&ds, MDL);
-			return;
-		}
-		ia.data = ia.buffer->data;
-		ia.len = 12;
-
-		/*
-		 * A simple IAID is the last 4 bytes
-		 * of the hardware address.
-		 */
-		if (client->interface->hw_address.hlen > 4) {
-			idx = client->interface->hw_address.hlen - 4;
-			len = 4;
-		} else {
-			idx = 0;
-			len = client->interface->hw_address.hlen;
-		}
-		memcpy(ia.buffer->data,
-		       client->interface->hw_address.hbuf + idx,
-		       len);
-		if (i)
-			ia.buffer->data[3] += i;
-
-		t1 = client->config->requested_lease / 2;
-		t2 = t1 + (t1 / 2);
-		putULong(ia.buffer->data + 4, t1);
-		putULong(ia.buffer->data + 8, t2);
-
-		log_debug("XMT:  X-- IA_NA %s",
-			  print_hex_1(4, ia.buffer->data, 55));
-		log_debug("XMT:  | X-- Request renew in  +%u", (unsigned)t1);
-		log_debug("XMT:  | X-- Request rebind in +%u", (unsigned)t2);
+		dhc6_new_iaid(iaid, client, i);
 
+		old_addr = NULL;
 		if ((client->active_lease != NULL) &&
 		    ((old_ia = find_ia(client->active_lease->bindings,
-				       D6O_IA_NA,
-				       (char *)ia.buffer->data)) != NULL)) {
-			/*
-			 * For each address in the old IA_NA,
-			 * request a binding.
-			 */
-			memset(&addr, 0, sizeof(addr));
-			for (old_addr = old_ia->addrs ; old_addr != NULL ;
-			     old_addr = old_addr->next) {
-				if (old_addr->address.len != 16) {
-					log_error("Invalid IPv6 address "
-						  "length %d.  "
-						  "Ignoring.  (%s:%d)",
-						  old_addr->address.len,
-						  MDL);
-					continue;
-				}
+				       D6O_IA_NA, (char *)iaid)) != NULL))
+			old_addr = old_ia->addrs;
 
-				if (!buffer_allocate(&addr.buffer, 24, MDL)) {
-					log_error("Unable to allocate memory "
-						  "for IAADDR.");
-					data_string_forget(&ia, MDL);
-					data_string_forget(&ds, MDL);
-					return;
-				}
-				addr.data = addr.buffer->data;
-				addr.len = 24;
-
-				memcpy(addr.buffer->data,
-				       old_addr->address.iabuf,
-				       16);
-
-				t1 = client->config->requested_lease;
-				t2 = t1 + (t1 / 2);
-				putULong(addr.buffer->data + 16, t1);
-				putULong(addr.buffer->data + 20, t2);
-
-				log_debug("XMT:  | X-- Request address %s.",
-					  piaddr(old_addr->address));
-				log_debug("XMT:  | | X-- Request "
-					  "preferred in +%u",
-					  (unsigned)t1);
-				log_debug("XMT:  | | X-- Request valid "
-					  "in     +%u",
-					  (unsigned)t2);
-
-				append_option(&ia, &dhcpv6_universe,
-					      iaaddr_option,
-					      &addr);
-
-				data_string_forget(&addr, MDL);
-			}
+		if (dhc6_make_ia_na_ta(client, &ds, ia_na_option,
+				       old_addr, iaid,
+				       DHCPV6_SOLICIT) != ISC_R_SUCCESS) {
+			data_string_forget(&ds, MDL);
+			return;
 		}
-
-		append_option(&ds, &dhcpv6_universe, ia_na_option, &ia);
-		data_string_forget(&ia, MDL);
 	}
 
 	/* Append IA_TA. */
@@ -1662,93 +1617,20 @@ do_init6(void *input)
 		 * cache.  They'd have to be pulled down as they also contain
 		 * different option caches in the same universe...
 		 */
-		memset(&ia, 0, sizeof(ia));
-		if (!buffer_allocate(&ia.buffer, 4, MDL)) {
-			log_error("Unable to allocate memory for IA_TA.");
-			data_string_forget(&ds, MDL);
-			return;
-		}
-		ia.data = ia.buffer->data;
-		ia.len = 4;
-
-		/*
-		 * A simple IAID is the last 4 bytes
-		 * of the hardware address.
-		 */
-		if (client->interface->hw_address.hlen > 4) {
-			idx = client->interface->hw_address.hlen - 4;
-			len = 4;
-		} else {
-			idx = 0;
-			len = client->interface->hw_address.hlen;
-		}
-		memcpy(ia.buffer->data,
-		       client->interface->hw_address.hbuf + idx,
-		       len);
-		if (i)
-			ia.buffer->data[3] += i;
-
-		log_debug("XMT:  X-- IA_TA %s",
-			  print_hex_1(4, ia.buffer->data, 55));
+		dhc6_new_iaid(iaid, client, i);
 
+		old_addr = NULL;
 		if ((client->active_lease != NULL) &&
 		    ((old_ia = find_ia(client->active_lease->bindings,
-				       D6O_IA_TA,
-				       (char *)ia.buffer->data)) != NULL)) {
-			/*
-			 * For each address in the old IA_TA,
-			 * request a binding.
-			 */
-			memset(&addr, 0, sizeof(addr));
-			for (old_addr = old_ia->addrs ; old_addr != NULL ;
-			     old_addr = old_addr->next) {
-				if (old_addr->address.len != 16) {
-					log_error("Invalid IPv6 address "
-						  "length %d.  "
-						  "Ignoring.  (%s:%d)",
-						  old_addr->address.len,
-						  MDL);
-					continue;
-				}
+				       D6O_IA_TA, (char *)iaid)) != NULL))
+			old_addr = old_ia->addrs;
 
-				if (!buffer_allocate(&addr.buffer, 24, MDL)) {
-					log_error("Unable to allocate memory "
-						  "for IAADDR.");
-					data_string_forget(&ia, MDL);
-					data_string_forget(&ds, MDL);
-					return;
-				}
-				addr.data = addr.buffer->data;
-				addr.len = 24;
-
-				memcpy(addr.buffer->data,
-				       old_addr->address.iabuf,
-				       16);
-
-				t1 = client->config->requested_lease;
-				t2 = t1 + (t1 / 2);
-				putULong(addr.buffer->data + 16, t1);
-				putULong(addr.buffer->data + 20, t2);
-
-				log_debug("XMT:  | X-- Request address %s.",
-					  piaddr(old_addr->address));
-				log_debug("XMT:  | | X-- Request "
-					  "preferred in +%u",
-					  (unsigned)t1);
-				log_debug("XMT:  | | X-- Request valid "
-					  "in     +%u",
-					  (unsigned)t2);
-
-				append_option(&ia, &dhcpv6_universe,
-					      iaaddr_option,
-					      &addr);
-
-				data_string_forget(&addr, MDL);
-			}
+		if (dhc6_make_ia_na_ta(client, &ds, ia_ta_option,
+				       old_addr, iaid,
+				       DHCPV6_SOLICIT) != ISC_R_SUCCESS) {
+			data_string_forget(&ds, MDL);
+			return;
 		}
-
-		append_option(&ds, &dhcpv6_universe, ia_ta_option, &ia);
-		data_string_forget(&ia, MDL);
 	}
 
 	/* Append IA_PD. */
@@ -1758,101 +1640,20 @@ do_init6(void *input)
 		 * cache.  They'd have to be pulled down as they also contain
 		 * different option caches in the same universe...
 		 */
-		memset(&ia, 0, sizeof(ia));
-		if (!buffer_allocate(&ia.buffer, 12, MDL)) {
-			log_error("Unable to allocate memory for IA_PD.");
-			data_string_forget(&ds, MDL);
-			return;
-		}
-		ia.data = ia.buffer->data;
-		ia.len = 12;
-
-		/*
-		 * A simple IAID is the last 4 bytes
-		 * of the hardware address.
-		 */
-		if (client->interface->hw_address.hlen > 4) {
-			idx = client->interface->hw_address.hlen - 4;
-			len = 4;
-		} else {
-			idx = 0;
-			len = client->interface->hw_address.hlen;
-		}
-		memcpy(ia.buffer->data,
-		       client->interface->hw_address.hbuf + idx,
-		       len);
-		if (i)
-			ia.buffer->data[3] += i;
 
-		t1 = client->config->requested_lease / 2;
-		t2 = t1 + (t1 / 2);
-		putULong(ia.buffer->data + 4, t1);
-		putULong(ia.buffer->data + 8, t2);
-
-		log_debug("XMT:  X-- IA_PD %s",
-			  print_hex_1(4, ia.buffer->data, 55));
-		log_debug("XMT:  | X-- Request renew in  +%u", (unsigned)t1);
-		log_debug("XMT:  | X-- Request rebind in +%u", (unsigned)t2);
+		dhc6_new_iaid(iaid, client, i);
 
+		old_addr = NULL;
 		if ((client->active_lease != NULL) &&
 		    ((old_ia = find_ia(client->active_lease->bindings,
-				       D6O_IA_PD,
-				       (char *)ia.buffer->data)) != NULL)) {
-			/*
-			 * For each prefix in the old IA_PD,
-			 * request a binding.
-			 */
-			memset(&addr, 0, sizeof(addr));
-			for (old_addr = old_ia->addrs ; old_addr != NULL ;
-			     old_addr = old_addr->next) {
-				if (old_addr->address.len != 16) {
-					log_error("Invalid IPv6 prefix, "
-						  "Ignoring.  (%s:%d)",
-						  MDL);
-					continue;
-				}
+				       D6O_IA_PD, (char *)iaid)) != NULL))
+			old_addr = old_ia->addrs;
 
-				if (!buffer_allocate(&addr.buffer, 25, MDL)) {
-					log_error("Unable to allocate memory "
-						  "for IAPREFIX.");
-					data_string_forget(&ia, MDL);
-					data_string_forget(&ds, MDL);
-					return;
-				}
-				addr.data = addr.buffer->data;
-				addr.len = 25;
-
-				t1 = client->config->requested_lease;
-				t2 = t1 + (t1 / 2);
-				putULong(addr.buffer->data, t1);
-				putULong(addr.buffer->data + 4, t2);
-
-				putUChar(addr.buffer->data + 8,
-					 old_addr->plen);
-				memcpy(addr.buffer->data + 9,
-				       old_addr->address.iabuf,
-				       16);
-
-				log_debug("XMT:  | X-- Request prefix %s/%u.",
-					  piaddr(old_addr->address),
-					  (unsigned) old_addr->plen);
-				log_debug("XMT:  | | X-- Request "
-					  "preferred in +%u",
-					  (unsigned)t1);
-				log_debug("XMT:  | | X-- Request valid "
-					  "in     +%u",
-					  (unsigned)t2);
-
-				append_option(&ia, &dhcpv6_universe,
-					      iaprefix_option,
-					      &addr);
-
-				data_string_forget(&addr, MDL);
-			}
+		if (dhc6_make_ia_pd(client, &ds, old_addr, iaid,
+				    DHCPV6_SOLICIT) != ISC_R_SUCCESS) {
+			data_string_forget(&ds, MDL);
+			return;
 		}
-
-		append_option(&ds, &dhcpv6_universe, ia_pd_option, &ia);
-		data_string_forget(&ia, MDL);
 	}
 
 	/* Transmit and wait. */
@@ -1987,13 +1788,13 @@ do_confirm6(void *input)
 
 	/* Append IA's. */
 	if (wanted_ia_na &&
-	    dhc6_add_ia_na(client, &ds, client->active_lease,
-			   DHCPV6_CONFIRM) != ISC_R_SUCCESS) {
+	    dhc6_add_ia_na_ta(client, &ds, ia_na_option, client->active_lease,
+			      DHCPV6_CONFIRM) != ISC_R_SUCCESS) {
 		data_string_forget(&ds, MDL);
 		return;
 	}
 	if (wanted_ia_ta &&
-	    dhc6_add_ia_ta(client, &ds, client->active_lease,
+	    dhc6_add_ia_na_ta(client, &ds, ia_ta_option, client->active_lease,
 			   DHCPV6_CONFIRM) != ISC_R_SUCCESS) {
 		data_string_forget(&ds, MDL);
 		return;
@@ -2100,8 +1901,9 @@ do_release6(void *input)
 
 	/* Append IA's (but don't release temporary addresses). */
 	if (wanted_ia_na &&
-	    dhc6_add_ia_na(client, &ds, client->active_lease,
-			   DHCPV6_RELEASE) != ISC_R_SUCCESS) {
+	    dhc6_add_ia_na_ta(client, &ds, ia_na_option,
+			      client->active_lease,
+			      DHCPV6_RELEASE) != ISC_R_SUCCESS) {
 		data_string_forget(&ds, MDL);
 		goto release_done;
 	}
@@ -3195,14 +2997,14 @@ do_select6(void *input)
 
 	/* Now append any IA's, and within them any IAADDR/IAPREFIXs. */
 	if (wanted_ia_na &&
-	    dhc6_add_ia_na(client, &ds, lease,
-			   DHCPV6_REQUEST) != ISC_R_SUCCESS) {
+	    dhc6_add_ia_na_ta(client, &ds, ia_na_option, lease,
+			      DHCPV6_REQUEST) != ISC_R_SUCCESS) {
 		data_string_forget(&ds, MDL);
 		return;
 	}
 	if (wanted_ia_ta &&
-	    dhc6_add_ia_ta(client, &ds, lease,
-			   DHCPV6_REQUEST) != ISC_R_SUCCESS) {
+	    dhc6_add_ia_na_ta(client, &ds, ia_ta_option, lease,
+			      DHCPV6_REQUEST) != ISC_R_SUCCESS) {
 		data_string_forget(&ds, MDL);
 		return;
 	}
@@ -3238,78 +3040,124 @@ do_select6(void *input)
 	dhc6_retrans_advance(client);
 }
 
-/* For each IA_NA in the lease, for each address in the IA_NA,
+/* For each IA_NA/IA_TA in the lease, for each address in the IA_NA/IA_TA,
  * append that information onto the packet-so-far.
  */
 static isc_result_t
-dhc6_add_ia_na(struct client_state *client, struct data_string *packet,
-	       struct dhc6_lease *lease, u_int8_t message)
+dhc6_add_ia_na_ta(struct client_state *client, struct data_string *packet,
+		  struct option *option,
+		  struct dhc6_lease *lease, u_int8_t message)
 {
-	struct data_string iads;
-	struct data_string addrds;
-	struct dhc6_addr *addr;
 	struct dhc6_ia *ia;
 	isc_result_t rval = ISC_R_SUCCESS;
-	TIME t1, t2;
 
-	memset(&iads, 0, sizeof(iads));
-	memset(&addrds, 0, sizeof(addrds));
-	for (ia = lease->bindings;
-	     ia != NULL && rval == ISC_R_SUCCESS;
-	     ia = ia->next) {
-		if (ia->ia_type != D6O_IA_NA)
+	for (ia = lease->bindings; ia != NULL; ia = ia->next) {
+		if (ia->ia_type != option->code)
 			continue;
 
-		if (!buffer_allocate(&iads.buffer, 12, MDL)) {
-			log_error("Unable to allocate memory for IA_NA.");
-			rval = ISC_R_NOMEMORY;
+		rval = dhc6_make_ia_na_ta(client, packet, option,
+					  ia->addrs, ia->iaid, message);
+		if (rval != ISC_R_SUCCESS)
 			break;
-		}
+	}
 
-		/* Copy the IAID into the packet buffer. */
-		memcpy(iads.buffer->data, ia->iaid, 4);
-		iads.data = iads.buffer->data;
-		iads.len = 12;
+	return rval;
+}
 
-		switch (message) {
-		      case DHCPV6_REQUEST:
-		      case DHCPV6_RENEW:
-		      case DHCPV6_REBIND:
+/* Build an IA_NA or IA_TA option to add to a packet */
+static isc_result_t
+dhc6_make_ia_na_ta(const struct client_state *client,
+		   struct data_string *packet,
+		   struct option *option,
+		   const struct dhc6_addr *addr,
+		   const unsigned char iaid[4],
+		   u_int8_t message)
+{
+	TIME t1, t2;
+	struct data_string iads;
+	int addr_count;
+	isc_result_t rval = ISC_R_SUCCESS;
+	const char *name;
 
-			t1 = client->config->requested_lease / 2;
-			t2 = t1 + (t1 / 2);
-#if MAX_TIME > 0xffffffff
-			if (t1 > 0xffffffff)
-				t1 = 0xffffffff;
-			if (t2 > 0xffffffff)
-				t2 = 0xffffffff;
-#endif
-			putULong(iads.buffer->data + 4, t1);
-			putULong(iads.buffer->data + 8, t2);
-
-			log_debug("XMT:  X-- IA_NA %s",
-				  print_hex_1(4, iads.data, 59));
-			log_debug("XMT:  | X-- Requested renew  +%u",
-				  (unsigned) t1);
-			log_debug("XMT:  | X-- Requested rebind +%u",
-				  (unsigned) t2);
-			break;
+	switch (option->code) {
+	case D6O_IA_NA:
+		name = "IA_NA";
+		break;
+	case D6O_IA_TA:
+		name = "IA_TA";
+		break;
+	default:
+		log_fatal("Impossible condition at %s:%d.", MDL);
+	}
+
+	memset(&iads, 0, sizeof(iads));
+	if (!buffer_allocate(&iads.buffer, 12, MDL)) {
+		log_error("Unable to allocate memory for %s.", name);
+		return ISC_R_NOMEMORY;
+	}
+
+	/* Copy the IAID into the packet buffer. */
+	memcpy(&iads.buffer->data, iaid, 4);
+	iads.data = iads.buffer->data;
+	iads.len = 12;
+
+	/* Times for the IA_NA/IA_TA options */
+	switch (message) {
+	case DHCPV6_SOLICIT:
+	case DHCPV6_REQUEST:
+	case DHCPV6_RENEW:
+	case DHCPV6_REBIND:
+		t1 = client->config->requested_lease / 2;
+		t2 = t1 + (t1 / 2);
+		break;
 
-		      case DHCPV6_CONFIRM:
-		      case DHCPV6_RELEASE:
-		      case DHCPV6_DECLINE:
-			/* Set t1 and t2 to zero; server will ignore them */
-			memset(iads.buffer->data + 4, 0, 8);
-			log_debug("XMT:  X-- IA_NA %s",
-				  print_hex_1(4, iads.buffer->data, 55));
+	case DHCPV6_CONFIRM:
+	case DHCPV6_RELEASE:
+	case DHCPV6_DECLINE:
+		/* Set t1 and t2 to zero; server will ignore them */
+		t1 = t2 = 0;
+		break;
 
-			break;
+	default:
+		log_fatal("Impossible condition at %s:%d.", MDL);
+	}
+	putTimes(iads.buffer->data + 4, t1, t2);
 
-		      default:
-			log_fatal("Impossible condition at %s:%d.", MDL);
-		}
+	log_debug("XMT:  X-- %s %s", option->name,
+		  print_hex_1(4, iads.buffer->data, 55));
+	if (t1 || t2) {
+		log_debug("XMT:  | X-- Request renew in  +%u", (unsigned)t1);
+		log_debug("XMT:  | X-- Request rebind in +%u", (unsigned)t2);
+	}
 
-		for (addr = ia->addrs ; addr != NULL ; addr = addr->next) {
+	/* Times for the IAADDR options */
+	switch (message) {
+	case DHCPV6_SOLICIT:
+		t1 = client->config->requested_lease;
+		t2 = t1 + (t1 / 2);
+		break;
+
+	case DHCPV6_REQUEST:
+	case DHCPV6_RENEW:
+	case DHCPV6_REBIND:
+		t1 = client->config->requested_lease;
+		t2 = t1 + 300;
+		break;
+
+	case DHCPV6_CONFIRM:
+	case DHCPV6_RELEASE:
+	case DHCPV6_DECLINE:
+		/* Set t1 and t2 to zero; server will ignore them */
+		t1 = t2 = 0;
+		break;
+
+	default:
+		log_fatal("Impossible condition at %s:%d.", MDL);
+	}
+
+	addr_count = 0;
+	for (; addr; addr = addr->next) {
+		if (message != DHCPV6_SOLICIT) {
 			/*
 			 * Do not confirm expired addresses, do not request
 			 * expired addresses (but we keep them around for
@@ -3317,299 +3165,178 @@ dhc6_add_ia_na(struct client_state *client, struct data_string *packet,
 			 */
 			if (addr->flags & DHC6_ADDR_EXPIRED)
 				continue;
+		}
 
-			if (addr->address.len != 16) {
-				log_error("Illegal IPv6 address length (%d), "
-					  "ignoring.  (%s:%d)",
-					  addr->address.len, MDL);
-				continue;
-			}
+		if (addr->address.len != 16) {
+			log_error("Illegal IPv6 address length (%d), "
+				  "ignoring.  (%s:%d)",
+				  addr->address.len, MDL);
+			continue;
+		}
 
-			if (!buffer_allocate(&addrds.buffer, 24, MDL)) {
-				log_error("Unable to allocate memory for "
-					  "IAADDR.");
-				rval = ISC_R_NOMEMORY;
-				break;
-			}
+		rval = dhc6_make_iaaddr(&iads, addr, t1, t2);
+		if (rval != ISC_R_SUCCESS)
+			goto fail;
 
-			addrds.data = addrds.buffer->data;
-			addrds.len = 24;
-
-			/* Copy the address into the packet buffer. */
-			memcpy(addrds.buffer->data, addr->address.iabuf, 16);
-
-			/* Copy in additional information as appropriate */
-			switch (message) {
-			      case DHCPV6_REQUEST:
-			      case DHCPV6_RENEW:
-			      case DHCPV6_REBIND:
-				t1 = client->config->requested_lease;
-				t2 = t1 + 300;
-				putULong(addrds.buffer->data + 16, t1);
-				putULong(addrds.buffer->data + 20, t2);
-
-				log_debug("XMT:  | | X-- IAADDR %s",
-					  piaddr(addr->address));
-				log_debug("XMT:  | | | X-- Preferred "
-					  "lifetime +%u", (unsigned)t1);
-				log_debug("XMT:  | | | X-- Max lifetime +%u",
-					  (unsigned)t2);
+		addr_count++;
+	}
 
-				break;
+	if (message != DHCPV6_SOLICIT && !addr_count) {
+		log_debug("!!!:  V %s has no IAADDRs - removed.", name);
+		rval = ISC_R_FAILURE;
+		goto fail;
+	}
 
-			      case DHCPV6_CONFIRM:
-				/*
-				 * Set preferred and max life to zero,
-				 * per 17.1.3.
-				 */
-				memset(addrds.buffer->data + 16, 0, 8);
-				log_debug("XMT:  | X-- Confirm Address %s",
-					  piaddr(addr->address));
-				break;
+	append_option(packet, &dhcpv6_universe, option, &iads);
 
-			      case DHCPV6_RELEASE:
-				/* Preferred and max life are irrelevant */
-				memset(addrds.buffer->data + 16, 0, 8);
-				log_debug("XMT:  | X-- Release Address %s",
-					  piaddr(addr->address));
-				break;
+fail:
+	data_string_forget(&iads, MDL);
+	return rval;
+}
 
-			      case DHCPV6_DECLINE:
-				/* Preferred and max life are irrelevant */
-				memset(addrds.buffer->data + 16, 0, 8);
-				log_debug("XMT:  | X-- Decline Address %s",
-					  piaddr(addr->address));
-				break;
+/* Build an IAPREFIX suboption to add to an IA_NA or IA_TA option
+ */
+static isc_result_t
+dhc6_make_iaaddr(struct data_string *iads,
+		 const struct dhc6_addr *old_addr,
+		 TIME t1, TIME t2)
+{
+	struct data_string addr;
 
-			      default:
-				log_fatal("Impossible condition at %s:%d.",
-					  MDL);
-			}
+	memset(&addr, 0, sizeof addr);
+	if (!buffer_allocate(&addr.buffer, 24, MDL)) {
+		log_error("Unable to allocate memory "
+			  "for IAADDR.");
+		return ISC_R_NOMEMORY;
+	}
 
-			append_option(&iads, &dhcpv6_universe, iaaddr_option,
-				      &addrds);
-			data_string_forget(&addrds, MDL);
-		}
+	addr.data = addr.buffer->data;
+	addr.len = 24;
 
-		/*
-		 * It doesn't make sense to make a request without an
-		 * address.
-		 */
-		if (ia->addrs == NULL) {
-			log_debug("!!!:  V IA_NA has no IAADDRs - removed.");
-			rval = ISC_R_FAILURE;
-		} else if (rval == ISC_R_SUCCESS) {
-			log_debug("XMT:  V IA_NA appended.");
-			append_option(packet, &dhcpv6_universe, ia_na_option,
-				      &iads);
-		}
+	memcpy(addr.buffer->data,
+	       old_addr->address.iabuf,
+	       16);
 
-		data_string_forget(&iads, MDL);
+	putTimes(addr.buffer->data + 16, t1, t2);
+
+	log_debug("XMT:  | X-- %s address %s.",
+		  t1 ? "Request" : "Release",
+		  piaddr(old_addr->address));
+	if (t1 || t2) {
+		log_debug("XMT:  | | X-- Request preferred in +%u",
+			  (unsigned)t1);
+		log_debug("XMT:  | | X-- Request valid in     +%u",
+			  (unsigned)t2);
 	}
 
-	return rval;
+	append_option(iads, &dhcpv6_universe, iaaddr_option, &addr);
+	data_string_forget(&addr, MDL);
+
+	return ISC_R_SUCCESS;
 }
 
-/* For each IA_TA in the lease, for each address in the IA_TA,
+/* For each IA_PD in the lease, for each prefix in the IA_PD,
  * append that information onto the packet-so-far.
  */
 static isc_result_t
-dhc6_add_ia_ta(struct client_state *client, struct data_string *packet,
+dhc6_add_ia_pd(struct client_state *client, struct data_string *packet,
 	       struct dhc6_lease *lease, u_int8_t message)
 {
-	struct data_string iads;
-	struct data_string addrds;
-	struct dhc6_addr *addr;
-	struct dhc6_ia *ia;
+	const struct dhc6_ia *ia;
 	isc_result_t rval = ISC_R_SUCCESS;
-	TIME t1, t2;
 
-	memset(&iads, 0, sizeof(iads));
-	memset(&addrds, 0, sizeof(addrds));
-	for (ia = lease->bindings;
-	     ia != NULL && rval == ISC_R_SUCCESS;
-	     ia = ia->next) {
-		if (ia->ia_type != D6O_IA_TA)
+	for (ia = lease->bindings; ia != NULL; ia = ia->next) {
+		if (ia->ia_type != D6O_IA_PD)
 			continue;
 
-		if (!buffer_allocate(&iads.buffer, 4, MDL)) {
-			log_error("Unable to allocate memory for IA_TA.");
-			rval = ISC_R_NOMEMORY;
+		rval = dhc6_make_ia_pd(client, packet,
+				       ia->addrs, ia->iaid, message);
+		if (rval != ISC_R_SUCCESS)
 			break;
-		}
-
-		/* Copy the IAID into the packet buffer. */
-		memcpy(iads.buffer->data, ia->iaid, 4);
-		iads.data = iads.buffer->data;
-		iads.len = 4;
-
-		log_debug("XMT:  X-- IA_TA %s",
-			  print_hex_1(4, iads.buffer->data, 55));
-
-		for (addr = ia->addrs ; addr != NULL ; addr = addr->next) {
-			/*
-			 * Do not confirm expired addresses, do not request
-			 * expired addresses (but we keep them around for
-			 * solicit).
-			 */
-			if (addr->flags & DHC6_ADDR_EXPIRED)
-				continue;
-
-			if (addr->address.len != 16) {
-				log_error("Illegal IPv6 address length (%d), "
-					  "ignoring.  (%s:%d)",
-					  addr->address.len, MDL);
-				continue;
-			}
-
-			if (!buffer_allocate(&addrds.buffer, 24, MDL)) {
-				log_error("Unable to allocate memory for "
-					  "IAADDR.");
-				rval = ISC_R_NOMEMORY;
-				break;
-			}
-
-			addrds.data = addrds.buffer->data;
-			addrds.len = 24;
-
-			/* Copy the address into the packet buffer. */
-			memcpy(addrds.buffer->data, addr->address.iabuf, 16);
-
-			/* Copy in additional information as appropriate */
-			switch (message) {
-			      case DHCPV6_REQUEST:
-			      case DHCPV6_RENEW:
-			      case DHCPV6_REBIND:
-				t1 = client->config->requested_lease;
-				t2 = t1 + 300;
-				putULong(addrds.buffer->data + 16, t1);
-				putULong(addrds.buffer->data + 20, t2);
-
-				log_debug("XMT:  | | X-- IAADDR %s",
-					  piaddr(addr->address));
-				log_debug("XMT:  | | | X-- Preferred "
-					  "lifetime +%u", (unsigned)t1);
-				log_debug("XMT:  | | | X-- Max lifetime +%u",
-					  (unsigned)t2);
-
-				break;
-
-			      case DHCPV6_CONFIRM:
-				/*
-				 * Set preferred and max life to zero,
-				 * per 17.1.3.
-				 */
-				memset(addrds.buffer->data + 16, 0, 8);
-				log_debug("XMT:  | X-- Confirm Address %s",
-					  piaddr(addr->address));
-				break;
-
-			      case DHCPV6_RELEASE:
-				/* Preferred and max life are irrelevant */
-				memset(addrds.buffer->data + 16, 0, 8);
-				log_debug("XMT:  | X-- Release Address %s",
-					  piaddr(addr->address));
-				break;
-
-			      default:
-				log_fatal("Impossible condition at %s:%d.",
-					  MDL);
-			}
-
-			append_option(&iads, &dhcpv6_universe, iaaddr_option,
-				      &addrds);
-			data_string_forget(&addrds, MDL);
-		}
-
-		/*
-		 * It doesn't make sense to make a request without an
-		 * address.
-		 */
-		if (ia->addrs == NULL) {
-			log_debug("!!!:  V IA_TA has no IAADDRs - removed.");
-			rval = ISC_R_FAILURE;
-		} else if (rval == ISC_R_SUCCESS) {
-			log_debug("XMT:  V IA_TA appended.");
-			append_option(packet, &dhcpv6_universe, ia_ta_option,
-				      &iads);
-		}
-
-		data_string_forget(&iads, MDL);
 	}
 
 	return rval;
 }
 
-/* For each IA_PD in the lease, for each prefix in the IA_PD,
- * append that information onto the packet-so-far.
+/* Build an IA_PD option to add to a packet
  */
 static isc_result_t
-dhc6_add_ia_pd(struct client_state *client, struct data_string *packet,
-	       struct dhc6_lease *lease, u_int8_t message)
+dhc6_make_ia_pd(const struct client_state *client,
+		struct data_string *packet,
+		const struct dhc6_addr *pref,
+		const unsigned char iaid[4],
+		u_int8_t message)
 {
+	TIME t1, t2;
 	struct data_string iads;
-	struct data_string prefds;
-	struct dhc6_addr *pref;
-	struct dhc6_ia *ia;
+	int pfx_count;
 	isc_result_t rval = ISC_R_SUCCESS;
-	TIME t1, t2;
 
-	memset(&iads, 0, sizeof(iads));
-	memset(&prefds, 0, sizeof(prefds));
-	for (ia = lease->bindings;
-	     ia != NULL && rval == ISC_R_SUCCESS;
-	     ia = ia->next) {
-		if (ia->ia_type != D6O_IA_PD)
-			continue;
+	memset(&iads, 0, sizeof iads);
+	if (!buffer_allocate(&iads.buffer, 12, MDL)) {
+		log_error("Unable to allocate memory for IA_PD.");
+		return ISC_R_NOMEMORY;
+	}
 
-		if (!buffer_allocate(&iads.buffer, 12, MDL)) {
-			log_error("Unable to allocate memory for IA_PD.");
-			rval = ISC_R_NOMEMORY;
-			break;
-		}
+	/* Copy the IAID into the packet buffer. */
+	memcpy(iads.buffer->data, iaid, 4);
+	iads.data = iads.buffer->data;
+	iads.len = 12;
 
-		/* Copy the IAID into the packet buffer. */
-		memcpy(iads.buffer->data, ia->iaid, 4);
-		iads.data = iads.buffer->data;
-		iads.len = 12;
+	/* Times for the IA_PD option */
+	switch (message) {
+	case DHCPV6_SOLICIT:
+	case DHCPV6_REQUEST:
+	case DHCPV6_RENEW:
+	case DHCPV6_REBIND:
+		t1 = client->config->requested_lease / 2;
+		t2 = t1 + (t1 / 2);
+		break;
 
-		switch (message) {
-		      case DHCPV6_REQUEST:
-		      case DHCPV6_RENEW:
-		      case DHCPV6_REBIND:
+	case DHCPV6_RELEASE:
+		/* Set t1 and t2 to zero; server will ignore them */
+		t1 = t2 = 0;
+		break;
 
-			t1 = client->config->requested_lease / 2;
-			t2 = t1 + (t1 / 2);
-#if MAX_TIME > 0xffffffff
-			if (t1 > 0xffffffff)
-				t1 = 0xffffffff;
-			if (t2 > 0xffffffff)
-				t2 = 0xffffffff;
-#endif
-			putULong(iads.buffer->data + 4, t1);
-			putULong(iads.buffer->data + 8, t2);
-
-			log_debug("XMT:  X-- IA_PD %s",
-				  print_hex_1(4, iads.data, 59));
-			log_debug("XMT:  | X-- Requested renew  +%u",
-				  (unsigned) t1);
-			log_debug("XMT:  | X-- Requested rebind +%u",
-				  (unsigned) t2);
-			break;
+	default:
+		log_fatal("Impossible condition at %s:%d.", MDL);
+	}
 
-		      case DHCPV6_RELEASE:
-			/* Set t1 and t2 to zero; server will ignore them */
-			memset(iads.buffer->data + 4, 0, 8);
-			log_debug("XMT:  X-- IA_PD %s",
-				  print_hex_1(4, iads.buffer->data, 55));
+	putTimes(iads.buffer->data + 4, t1, t2);
 
-			break;
+	log_debug("XMT:  X-- IA_PD %s", print_hex_1(4, iads.data, 55));
+	if (t1 || t2) {
+		log_debug("XMT:  | X-- Requested renew  +%u", (unsigned) t1);
+		log_debug("XMT:  | X-- Requested rebind +%u", (unsigned) t2);
+	}
 
-		      default:
-			log_fatal("Impossible condition at %s:%d.", MDL);
-		}
+	/* Times for the IAPREFIX options */
+	switch (message) {
+	case DHCPV6_SOLICIT:
+		t1 = client->config->requested_lease;
+		t2 = t1 + (t1 / 2);
+		break;
+
+	case DHCPV6_REQUEST:
+	case DHCPV6_RENEW:
+	case DHCPV6_REBIND:
+		t1 = client->config->requested_lease;
+		t2 = t1 + 300;
+		break;
+
+	case DHCPV6_RELEASE:
+		/* Preferred and max life are irrelevant */
+		t1 = t2 = 0;
+		break;
+
+	default:
+		log_fatal("Impossible condition at %s:%d.", MDL);
+	}
 
-		for (pref = ia->addrs ; pref != NULL ; pref = pref->next) {
+	pfx_count = 0;
+
+	for (; pref; pref = pref->next) {
+		if (message != DHCPV6_SOLICIT) {
 			/*
 			 * Do not confirm expired prefixes, do not request
 			 * expired prefixes (but we keep them around for
@@ -3617,93 +3344,100 @@ dhc6_add_ia_pd(struct client_state *client, struct data_string *packet,
 			 */
 			if (pref->flags & DHC6_ADDR_EXPIRED)
 				continue;
+		}
 
-			if (pref->address.len != 16) {
-				log_error("Illegal IPv6 prefix "
-					  "ignoring.  (%s:%d)",
-					  MDL);
-				continue;
-			}
+		if (pref->address.len != 16) {
+			log_error("Illegal IPv6 prefix, ignoring. (%s:%d)",
+				  MDL);
+			continue;
+		}
 
-			if (pref->plen == 0) {
-				log_info("Null IPv6 prefix, "
-					 "ignoring. (%s:%d)",
-					 MDL);
-			}
+		if (pref->plen == 0) {
+			log_info("Null IPv6 prefix, ignoring. (%s:%d)",
+				 MDL);
+			continue;
+		}
 
-			if (!buffer_allocate(&prefds.buffer, 25, MDL)) {
-				log_error("Unable to allocate memory for "
-					  "IAPREFIX.");
-				rval = ISC_R_NOMEMORY;
-				break;
-			}
+		rval = dhc6_make_iaprefix(&iads, pref, t1, t2);
+		if (rval != ISC_R_SUCCESS)
+			goto fail;
 
-			prefds.data = prefds.buffer->data;
-			prefds.len = 25;
-
-			/* Copy the prefix into the packet buffer. */
-			putUChar(prefds.buffer->data + 8, pref->plen);
-			memcpy(prefds.buffer->data + 9,
-			       pref->address.iabuf,
-			       16);
-
-			/* Copy in additional information as appropriate */
-			switch (message) {
-			      case DHCPV6_REQUEST:
-			      case DHCPV6_RENEW:
-			      case DHCPV6_REBIND:
-				t1 = client->config->requested_lease;
-				t2 = t1 + 300;
-				putULong(prefds.buffer->data, t1);
-				putULong(prefds.buffer->data + 4, t2);
-
-				log_debug("XMT:  | | X-- IAPREFIX %s/%u",
-					  piaddr(pref->address),
-					  (unsigned) pref->plen);
-				log_debug("XMT:  | | | X-- Preferred "
-					  "lifetime +%u", (unsigned)t1);
-				log_debug("XMT:  | | | X-- Max lifetime +%u",
-					  (unsigned)t2);
+		pfx_count++;
+	}
 
-				break;
+	/*
+	 * It doesn't make sense to make a request without any
+	 * address, except in a solicitation.
+	 */
+	if (message != DHCPV6_SOLICIT && !pfx_count) {
+		log_debug("!!!:  V IA_PD has no IAPREFIXs - removed.");
+		rval = ISC_R_FAILURE;
+		goto fail;
+	}
 
-			      case DHCPV6_RELEASE:
-				/* Preferred and max life are irrelevant */
-				memset(prefds.buffer->data, 0, 8);
-				log_debug("XMT:  | X-- Release Prefix %s/%u",
-					  piaddr(pref->address),
-					  (unsigned) pref->plen);
-				break;
+	log_debug("XMT:  V IA_PD appended.");
+	append_option(packet, &dhcpv6_universe, ia_pd_option, &iads);
 
-			      default:
-				log_fatal("Impossible condition at %s:%d.",
-					  MDL);
-			}
+fail:
+	data_string_forget(&iads, MDL);
+	return rval;
+}
 
-			append_option(&iads, &dhcpv6_universe,
-				      iaprefix_option, &prefds);
-			data_string_forget(&prefds, MDL);
-		}
+/* Build an IAPREFIX suboption to add to an IA_PD option
+ */
+static isc_result_t
+dhc6_make_iaprefix(struct data_string *iads,
+		   const struct dhc6_addr *old_addr,
+		   TIME t1, TIME t2)
+{
+	struct data_string addr;
 
-		/*
-		 * It doesn't make sense to make a request without an
-		 * address.
-		 */
-		if (ia->addrs == NULL) {
-			log_debug("!!!:  V IA_PD has no IAPREFIXs - removed.");
-			rval = ISC_R_FAILURE;
-		} else if (rval == ISC_R_SUCCESS) {
-			log_debug("XMT:  V IA_PD appended.");
-			append_option(packet, &dhcpv6_universe,
-				      ia_pd_option, &iads);
-		}
+	memset(&addr, 0, sizeof addr);
+	if (!buffer_allocate(&addr.buffer, 25, MDL)) {
+		log_error("Unable to allocate memory for IAPREFIX.");
+		return ISC_R_NOMEMORY;
+	}
+
+	addr.data = addr.buffer->data;
+	addr.len = 25;
+
+	putTimes(addr.buffer->data, t1, t2);
 
-		data_string_forget(&iads, MDL);
+	putUChar(addr.buffer->data + 8, old_addr->plen);
+	memcpy(addr.buffer->data + 9, old_addr->address.iabuf, 16);
+
+	log_debug("XMT:  | X-- %s prefix %s/%u.",
+		  t1 ? "Request" : "Release",
+		  piaddr(old_addr->address), (unsigned) old_addr->plen);
+	if (t1 || t2) {
+		log_debug("XMT:  | | X-- Request preferred in +%u",
+			  (unsigned)t1);
+		log_debug("XMT:  | | X-- Request valid in     +%u",
+			  (unsigned)t2);
 	}
 
-	return rval;
+	append_option(iads, &dhcpv6_universe, iaprefix_option, &addr);
+
+	data_string_forget(&addr, MDL);
+	return ISC_R_SUCCESS;
 }
 
+/* Write a pair of times, with capping.
+ */
+static void
+putTimes(unsigned char *buf, TIME t1, TIME t2)
+{
+#if MAX_TIME > 0xffffffff
+	if (t1 > 0xffffffff)
+		t1 = 0xffffffff;
+	if (t2 > 0xffffffff)
+		t2 = 0xffffffff;
+#endif
+	putULong(buf, t1);
+	putULong(buf + 4, t2);
+}
+
+
 /* stopping_finished() checks if there is a remaining work to do.
  */
 static isc_boolean_t
@@ -4517,8 +4251,8 @@ do_refresh6(void *input)
 
 	/* Append IA's */
 	if (wanted_ia_na &&
-	    dhc6_add_ia_na(client, &ds, lease,
-			   client->refresh_type) != ISC_R_SUCCESS) {
+	    dhc6_add_ia_na_ta(client, &ds, ia_na_option, lease,
+			      client->refresh_type) != ISC_R_SUCCESS) {
 		data_string_forget(&ds, MDL);
 		return;
 	}
-- 
2.4.3



More information about the dhcp-workers mailing list