diff -up dhcp-4.3.0a1/server/dhcpv6.c.UseMulticast dhcp-4.3.0a1/server/dhcpv6.c --- dhcp-4.3.0a1/server/dhcpv6.c.UseMulticast 2013-12-11 01:25:12.000000000 +0100 +++ dhcp-4.3.0a1/server/dhcpv6.c 2013-12-19 15:54:46.934932371 +0100 @@ -353,6 +353,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); + + 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 @@ -1498,6 +1540,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): * @@ -1523,8 +1615,8 @@ lease_to_client(struct data_string *repl * Sends a Renew/Rebind if the IA is not in the Reply message. */ #if defined (RFC3315_PRE_ERRATA_2010_08) - if (no_resources_avail && (reply.ia_count != 0) && - (reply.packet->dhcpv6_msg_type == DHCPV6_SOLICIT)) + else if (no_resources_avail && (reply.ia_count != 0) && + (reply.packet->dhcpv6_msg_type == DHCPV6_SOLICIT)) { /* Set the NoAddrsAvail status code. */ if (!set_status_code(STATUS_NoAddrsAvail, @@ -1571,6 +1663,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, @@ -4688,7 +4781,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; @@ -5018,7 +5110,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; @@ -5262,18 +5353,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 @@ -5411,6 +5544,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)) { @@ -5456,7 +5590,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) { @@ -5926,7 +6059,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;