--- /dev/null
+diff -up dhcp-4.3.1b1/server/dhcpv6.c.UseMulticast dhcp-4.3.1b1/server/dhcpv6.c
+--- dhcp-4.3.1b1/server/dhcpv6.c.UseMulticast 2014-07-02 19:58:40.000000000 +0200
++++ dhcp-4.3.1b1/server/dhcpv6.c 2014-07-10 18:20:03.066256219 +0200
+@@ -376,6 +376,48 @@ generate_new_server_duid(void) {
+ }
+
+ /*
++ * Is the D6O_UNICAST option defined in dhcpd.conf ?
++ */
++static isc_boolean_t unicast_option_defined;
++
++/*
++ * Did we already search dhcpd.conf for D6O_UNICAST option ?
++ * We need to store it here to not parse dhcpd.conf repeatedly.
++ */
++static isc_boolean_t unicast_option_parsed = ISC_FALSE;
++
++
++/*
++ * Is the D6O_UNICAST option defined in dhcpd.conf ?
++ */
++isc_boolean_t
++is_unicast_option_defined(void) {
++ struct option_state *opt_state;
++ struct option_cache *oc;
++
++ /*
++ * If we are looking for the unicast option for the first time
++ */
++ if (unicast_option_parsed == ISC_FALSE) {
++ unicast_option_parsed = ISC_TRUE;
++ opt_state = NULL;
++ if (!option_state_allocate(&opt_state, MDL)) {
++ log_fatal("No memory for option state.");
++ }
++
++ execute_statements_in_scope(NULL, NULL, NULL, NULL, NULL,
++ opt_state, &global_scope, root_group, NULL, NULL);
++
++ oc = lookup_option(&dhcpv6_universe, opt_state, D6O_UNICAST);
++ unicast_option_defined = (oc != NULL);
++
++ option_state_dereference(&opt_state, MDL);
++ }
++
++ return (unicast_option_defined);
++}
++
++/*
+ * Get the client identifier from the packet.
+ */
+ isc_result_t
+@@ -706,6 +748,12 @@ static const int required_opts[] = {
+ D6O_PREFERENCE,
+ 0
+ };
++static const int required_opts_NAA[] = {
++ D6O_CLIENTID,
++ D6O_SERVERID,
++ D6O_STATUS_CODE,
++ 0
++};
+ static const int required_opts_solicit[] = {
+ D6O_CLIENTID,
+ D6O_SERVERID,
+@@ -1587,6 +1635,56 @@ lease_to_client(struct data_string *repl
+ reply.shared->group, NULL);
+ }
+
++ /* reject unicast message, unless we set unicast option */
++ if ((packet->unicast == ISC_TRUE) && !is_unicast_option_defined())
++ /*
++ * RFC3315 section 18.2.1 (Request):
++ *
++ * When the server receives a Request message via unicast from a client
++ * to which the server has not sent a unicast option, the server
++ * discards the Request message and responds with a Reply message
++ * containing a Status Code option with the value UseMulticast, a Server
++ * Identifier option containing the server's DUID, the Client Identifier
++ * option from the client message, and no other options.
++ *
++ * Section 18.2.3 (Renew):
++ *
++ * When the server receives a Renew message via unicast from a client to
++ * which the server has not sent a unicast option, the server discards
++ * the Renew message and responds with a Reply message containing a
++ * Status Code option with the value UseMulticast, a Server Identifier
++ * option containing the server's DUID, the Client Identifier option
++ * from the client message, and no other options.
++ */
++ {
++ /* Set the UseMulticast status code. */
++ if (!set_status_code(STATUS_UseMulticast,
++ "Unicast not allowed by server.",
++ reply.opt_state)) {
++ log_error("lease_to_client: Unable to set "
++ "UseMulticast status code.");
++ goto exit;
++ }
++
++ /* Rewind the cursor to the start. */
++ reply.cursor = REPLY_OPTIONS_INDEX;
++
++ /*
++ * Produce an reply that includes only:
++ *
++ * Status code.
++ * Server DUID.
++ * Client DUID.
++ */
++ reply.cursor += store_options6((char *)reply.buf.data +
++ reply.cursor,
++ sizeof(reply.buf) -
++ reply.cursor,
++ reply.opt_state, reply.packet,
++ required_opts_NAA,
++ NULL);
++ }
++
+ /*
+ * RFC3315 section 17.2.2 (Solicit):
+ *
+@@ -1619,6 +1717,7 @@ lease_to_client(struct data_string *repl
+ * Having stored the client's IA's, store any options that
+ * will fit in the remaining space.
+ */
++ else
+ reply.cursor += store_options6((char *)reply.buf.data + reply.cursor,
+ sizeof(reply.buf) - reply.cursor,
+ reply.opt_state, reply.packet,
+@@ -4748,7 +4847,6 @@ dhcpv6_solicit(struct data_string *reply
+ * Very similar to Solicit handling, except the server DUID is required.
+ */
+
+-/* TODO: reject unicast messages, unless we set unicast option */
+ static void
+ dhcpv6_request(struct data_string *reply_ret, struct packet *packet) {
+ struct data_string client_id;
+@@ -5078,7 +5176,6 @@ exit:
+ * except for the error code of when addresses don't match.
+ */
+
+-/* TODO: reject unicast messages, unless we set unicast option */
+ static void
+ dhcpv6_renew(struct data_string *reply, struct packet *packet) {
+ struct data_string client_id;
+@@ -5322,18 +5419,60 @@ iterate_over_ia_na(struct data_string *r
+ goto exit;
+ }
+
+- snprintf(status_msg, sizeof(status_msg), "%s received.", packet_type);
+- if (!set_status_code(STATUS_Success, status_msg, opt_state)) {
+- goto exit;
+- }
++ /* reject unicast message, unless we set unicast option */
++ if ((packet->unicast == ISC_TRUE) && !is_unicast_option_defined()) {
++ /*
++ * RFC3315 section 18.2.6 (Release):
++ *
++ * When the server receives a Release message via unicast from a client
++ * to which the server has not sent a unicast option, the server
++ * discards the Release message and responds with a Reply message
++ * containing a Status Code option with value UseMulticast, a Server
++ * Identifier option containing the server's DUID, the Client Identifier
++ * option from the client message, and no other options.
++ *
++ * Section 18.2.7 (Decline):
++ *
++ * When the server receives a Decline message via unicast from a client
++ * to which the server has not sent a unicast option, the server
++ * discards the Decline message and responds with a Reply message
++ * containing a Status Code option with the value UseMulticast, a Server
++ * Identifier option containing the server's DUID, the Client Identifier
++ * option from the client message, and no other options.
++ */
++ snprintf(status_msg, sizeof(status_msg),
++ "%s received unicast.", packet_type);
++ if (!set_status_code(STATUS_UseMulticast, status_msg, opt_state)) {
++ goto exit;
++ }
+
+- /*
+- * Add our options that are not associated with any IA_NA or IA_TA.
+- */
+- reply_ofs += store_options6(reply_data+reply_ofs,
+- sizeof(reply_data)-reply_ofs,
++ /*
++ * Produce an reply that includes only:
++ *
++ * Status code.
++ * Server DUID.
++ * Client DUID.
++ */
++ reply_ofs += store_options6(reply_data+reply_ofs,
++ sizeof(reply_data)-reply_ofs,
+ opt_state, packet,
+- required_opts, NULL);
++ required_opts_NAA, NULL);
++
++ goto return_reply;
++ } else {
++ snprintf(status_msg, sizeof(status_msg), "%s received.", packet_type);
++ if (!set_status_code(STATUS_Success, status_msg, opt_state)) {
++ goto exit;
++ }
++
++ /*
++ * Add our options that are not associated with any IA_NA or IA_TA.
++ */
++ reply_ofs += store_options6(reply_data+reply_ofs,
++ sizeof(reply_data)-reply_ofs,
++ opt_state, packet,
++ required_opts, NULL);
++ }
+
+ /*
+ * Loop through the IA_NA reported by the client, and deal with
+@@ -5471,6 +5610,7 @@ iterate_over_ia_na(struct data_string *r
+ /*
+ * Return our reply to the caller.
+ */
++return_reply:
+ reply_ret->len = reply_ofs;
+ reply_ret->buffer = NULL;
+ if (!buffer_allocate(&reply_ret->buffer, reply_ofs, MDL)) {
+@@ -5516,7 +5656,6 @@ exit:
+ * we still need to be aware of this possibility.
+ */
+
+-/* TODO: reject unicast messages, unless we set unicast option */
+ /* TODO: IA_TA */
+ static void
+ dhcpv6_decline(struct data_string *reply, struct packet *packet) {
+@@ -5986,7 +6125,6 @@ exit:
+ * Release means a client is done with the leases.
+ */
+
+-/* TODO: reject unicast messages, unless we set unicast option */
+ static void
+ dhcpv6_release(struct data_string *reply, struct packet *packet) {
+ struct data_string client_id;