Blame SOURCES/dhcp-4.2.4-UseMulticast.patch

c8bb8f
diff -up dhcp-4.2.4b1/server/dhcpv6.c.UseMulticast dhcp-4.2.4b1/server/dhcpv6.c
c8bb8f
--- dhcp-4.2.4b1/server/dhcpv6.c.UseMulticast	2012-04-11 00:14:04.000000000 +0200
c8bb8f
+++ dhcp-4.2.4b1/server/dhcpv6.c	2012-04-16 19:21:43.575923732 +0200
c8bb8f
@@ -346,6 +346,48 @@ generate_new_server_duid(void) {
c8bb8f
 }
c8bb8f
 
c8bb8f
 /*
c8bb8f
+ * Is the D6O_UNICAST option defined in dhcpd.conf ?
c8bb8f
+ */
c8bb8f
+static isc_boolean_t unicast_option_defined;
c8bb8f
+
c8bb8f
+/*
c8bb8f
+ * Did we already search dhcpd.conf for D6O_UNICAST option ?
c8bb8f
+ * We need to store it here to not parse dhcpd.conf repeatedly.
c8bb8f
+ */
c8bb8f
+static isc_boolean_t unicast_option_parsed = ISC_FALSE;
c8bb8f
+
c8bb8f
+
c8bb8f
+/*
c8bb8f
+ * Is the D6O_UNICAST option defined in dhcpd.conf ?
c8bb8f
+ */
c8bb8f
+isc_boolean_t
c8bb8f
+is_unicast_option_defined(void) {
c8bb8f
+	struct option_state *opt_state;
c8bb8f
+	struct option_cache *oc;
c8bb8f
+
c8bb8f
+	/*
c8bb8f
+	 * If we are looking for the unicast option for the first time
c8bb8f
+	 */
c8bb8f
+	if (unicast_option_parsed == ISC_FALSE) {
c8bb8f
+		unicast_option_parsed = ISC_TRUE;
c8bb8f
+		opt_state = NULL;
c8bb8f
+		if (!option_state_allocate(&opt_state, MDL)) {
c8bb8f
+			log_fatal("No memory for option state.");
c8bb8f
+		}
c8bb8f
+
c8bb8f
+		execute_statements_in_scope(NULL, NULL, NULL, NULL, NULL,
c8bb8f
+				opt_state, &global_scope, root_group, NULL);
c8bb8f
+
c8bb8f
+		oc = lookup_option(&dhcpv6_universe, opt_state, D6O_UNICAST);
c8bb8f
+		unicast_option_defined = (oc != NULL);
c8bb8f
+
c8bb8f
+		option_state_dereference(&opt_state, MDL);
c8bb8f
+	}
c8bb8f
+
c8bb8f
+	return (unicast_option_defined);
c8bb8f
+}
c8bb8f
+
c8bb8f
+/*
c8bb8f
  * Get the client identifier from the packet.
c8bb8f
  */
c8bb8f
 isc_result_t
c8bb8f
@@ -1404,6 +1446,56 @@ lease_to_client(struct data_string *repl
c8bb8f
 						    reply.shared->group);
c8bb8f
 	}
c8bb8f
 
c8bb8f
+	/* reject unicast message, unless we set unicast option */
c8bb8f
+	if ((packet->unicast == ISC_TRUE) && !is_unicast_option_defined())
c8bb8f
+	/*
c8bb8f
+	 * RFC3315 section 18.2.1 (Request):
c8bb8f
+	 *
c8bb8f
+	 * When the server receives a Request message via unicast from a client
c8bb8f
+	 * to which the server has not sent a unicast option, the server
c8bb8f
+	 * discards the Request message and responds with a Reply message
c8bb8f
+	 * containing a Status Code option with the value UseMulticast, a Server
c8bb8f
+	 * Identifier option containing the server's DUID, the Client Identifier
c8bb8f
+	 * option from the client message, and no other options.
c8bb8f
+	 *
c8bb8f
+	 * Section 18.2.3 (Renew):
c8bb8f
+	 *
c8bb8f
+	 * When the server receives a Renew message via unicast from a client to
c8bb8f
+	 * which the server has not sent a unicast option, the server discards
c8bb8f
+	 * the Renew message and responds with a Reply message containing a
c8bb8f
+	 * Status Code option with the value UseMulticast, a Server Identifier
c8bb8f
+	 * option containing the server's DUID, the Client Identifier option
c8bb8f
+	 * from the client message, and no other options.
c8bb8f
+	 */
c8bb8f
+	{
c8bb8f
+		/* Set the UseMulticast status code. */
c8bb8f
+		if (!set_status_code(STATUS_UseMulticast,
c8bb8f
+					"Unicast not allowed by server.",
c8bb8f
+					reply.opt_state)) {
c8bb8f
+			log_error("lease_to_client: Unable to set "
c8bb8f
+					"UseMulticast status code.");
c8bb8f
+			goto exit;
c8bb8f
+		}
c8bb8f
+
c8bb8f
+		/* Rewind the cursor to the start. */
c8bb8f
+		reply.cursor = REPLY_OPTIONS_INDEX;
c8bb8f
+
c8bb8f
+		/*
c8bb8f
+		 * Produce an reply that includes only:
c8bb8f
+		 *
c8bb8f
+		 * Status code.
c8bb8f
+		 * Server DUID.
c8bb8f
+		 * Client DUID.
c8bb8f
+		 */
c8bb8f
+		reply.cursor += store_options6((char *)reply.buf.data +
c8bb8f
+					reply.cursor,
c8bb8f
+					sizeof(reply.buf) -
c8bb8f
+					reply.cursor,
c8bb8f
+					reply.opt_state, reply.packet,
c8bb8f
+					required_opts_NAA,
c8bb8f
+					NULL);
c8bb8f
+	}
c8bb8f
+
c8bb8f
 	/*
c8bb8f
 	 * RFC3315 section 17.2.2 (Solicit):
c8bb8f
 	 *
c8bb8f
@@ -1429,8 +1521,8 @@ lease_to_client(struct data_string *repl
c8bb8f
 	 * Sends a Renew/Rebind if the IA is not in the Reply message.
c8bb8f
 	 */
c8bb8f
 #if defined (RFC3315_PRE_ERRATA_2010_08)
c8bb8f
-	if (no_resources_avail && (reply.ia_count != 0) &&
c8bb8f
-	    (reply.packet->dhcpv6_msg_type == DHCPV6_SOLICIT))
c8bb8f
+	else if (no_resources_avail && (reply.ia_count != 0) &&
c8bb8f
+		 (reply.packet->dhcpv6_msg_type == DHCPV6_SOLICIT))
c8bb8f
 	{
c8bb8f
 		/* Set the NoAddrsAvail status code. */
c8bb8f
 		if (!set_status_code(STATUS_NoAddrsAvail,
c8bb8f
@@ -1477,6 +1569,7 @@ lease_to_client(struct data_string *repl
c8bb8f
 	 * Having stored the client's IA's, store any options that
c8bb8f
 	 * will fit in the remaining space.
c8bb8f
 	 */
c8bb8f
+	else 
c8bb8f
 	reply.cursor += store_options6((char *)reply.buf.data + reply.cursor,
c8bb8f
 				       sizeof(reply.buf) - reply.cursor,
c8bb8f
 				       reply.opt_state, reply.packet,
c8bb8f
@@ -4126,7 +4219,6 @@ dhcpv6_solicit(struct data_string *reply
c8bb8f
  * Very similar to Solicit handling, except the server DUID is required.
c8bb8f
  */
c8bb8f
 
c8bb8f
-/* TODO: reject unicast messages, unless we set unicast option */
c8bb8f
 static void
c8bb8f
 dhcpv6_request(struct data_string *reply_ret, struct packet *packet) {
c8bb8f
 	struct data_string client_id;
c8bb8f
@@ -4456,7 +4548,6 @@ exit:
c8bb8f
  * except for the error code of when addresses don't match.
c8bb8f
  */
c8bb8f
 
c8bb8f
-/* TODO: reject unicast messages, unless we set unicast option */
c8bb8f
 static void
c8bb8f
 dhcpv6_renew(struct data_string *reply, struct packet *packet) {
c8bb8f
 	struct data_string client_id;
c8bb8f
@@ -4700,18 +4791,60 @@ iterate_over_ia_na(struct data_string *r
c8bb8f
 		goto exit;
c8bb8f
 	}
c8bb8f
 
c8bb8f
-	snprintf(status_msg, sizeof(status_msg), "%s received.", packet_type);
c8bb8f
-	if (!set_status_code(STATUS_Success, status_msg, opt_state)) {
c8bb8f
-		goto exit;
c8bb8f
-	}
c8bb8f
+	/* reject unicast message, unless we set unicast option */
c8bb8f
+	if ((packet->unicast == ISC_TRUE) && !is_unicast_option_defined()) {
c8bb8f
+		/*
c8bb8f
+		 * RFC3315 section 18.2.6 (Release):
c8bb8f
+		 *
c8bb8f
+		 * When the server receives a Release message via unicast from a client
c8bb8f
+		 * to which the server has not sent a unicast option, the server
c8bb8f
+		 * discards the Release message and responds with a Reply message
c8bb8f
+		 * containing a Status Code option with value UseMulticast, a Server
c8bb8f
+		 * Identifier option containing the server's DUID, the Client Identifier
c8bb8f
+		 * option from the client message, and no other options.
c8bb8f
+		 *
c8bb8f
+		 * Section 18.2.7 (Decline):
c8bb8f
+		 *
c8bb8f
+		 * When the server receives a Decline message via unicast from a client
c8bb8f
+		 * to which the server has not sent a unicast option, the server
c8bb8f
+		 * discards the Decline message and responds with a Reply message
c8bb8f
+		 * containing a Status Code option with the value UseMulticast, a Server
c8bb8f
+		 * Identifier option containing the server's DUID, the Client Identifier
c8bb8f
+		 * option from the client message, and no other options.
c8bb8f
+		 */
c8bb8f
+		snprintf(status_msg, sizeof(status_msg),
c8bb8f
+				 "%s received unicast.", packet_type);
c8bb8f
+		if (!set_status_code(STATUS_UseMulticast, status_msg, opt_state)) {
c8bb8f
+			goto exit;
c8bb8f
+		}
c8bb8f
 
c8bb8f
-	/* 
c8bb8f
-	 * Add our options that are not associated with any IA_NA or IA_TA. 
c8bb8f
-	 */
c8bb8f
-	reply_ofs += store_options6(reply_data+reply_ofs,
c8bb8f
-				    sizeof(reply_data)-reply_ofs, 
c8bb8f
+		/*
c8bb8f
+		 * Produce an reply that includes only:
c8bb8f
+		 *
c8bb8f
+		 * Status code.
c8bb8f
+		 * Server DUID.
c8bb8f
+		 * Client DUID.
c8bb8f
+		 */
c8bb8f
+		reply_ofs += store_options6(reply_data+reply_ofs,
c8bb8f
+				    sizeof(reply_data)-reply_ofs,
c8bb8f
 				    opt_state, packet,
c8bb8f
-				    required_opts, NULL);
c8bb8f
+				    required_opts_NAA, NULL);
c8bb8f
+
c8bb8f
+		goto return_reply;
c8bb8f
+	} else {
c8bb8f
+		snprintf(status_msg, sizeof(status_msg), "%s received.", packet_type);
c8bb8f
+		if (!set_status_code(STATUS_Success, status_msg, opt_state)) {
c8bb8f
+			goto exit;
c8bb8f
+		}
c8bb8f
+
c8bb8f
+		/*
c8bb8f
+		 * Add our options that are not associated with any IA_NA or IA_TA.
c8bb8f
+		 */
c8bb8f
+		reply_ofs += store_options6(reply_data+reply_ofs,
c8bb8f
+					    sizeof(reply_data)-reply_ofs,
c8bb8f
+					    opt_state, packet,
c8bb8f
+					    required_opts, NULL);
c8bb8f
+	}
c8bb8f
 
c8bb8f
 	/*
c8bb8f
 	 * Loop through the IA_NA reported by the client, and deal with
c8bb8f
@@ -4849,6 +4982,7 @@ iterate_over_ia_na(struct data_string *r
c8bb8f
 	/* 
c8bb8f
 	 * Return our reply to the caller.
c8bb8f
 	 */
c8bb8f
+return_reply:
c8bb8f
 	reply_ret->len = reply_ofs;
c8bb8f
 	reply_ret->buffer = NULL;
c8bb8f
 	if (!buffer_allocate(&reply_ret->buffer, reply_ofs, MDL)) {
c8bb8f
@@ -4894,7 +5028,6 @@ exit:
c8bb8f
  * we still need to be aware of this possibility.
c8bb8f
  */
c8bb8f
 
c8bb8f
-/* TODO: reject unicast messages, unless we set unicast option */
c8bb8f
 /* TODO: IA_TA */
c8bb8f
 static void
c8bb8f
 dhcpv6_decline(struct data_string *reply, struct packet *packet) {
c8bb8f
@@ -5364,7 +5497,6 @@ exit:
c8bb8f
  * Release means a client is done with the leases.
c8bb8f
  */
c8bb8f
 
c8bb8f
-/* TODO: reject unicast messages, unless we set unicast option */
c8bb8f
 static void
c8bb8f
 dhcpv6_release(struct data_string *reply, struct packet *packet) {
c8bb8f
 	struct data_string client_id;