diff --git a/includes/dhcp6.h b/includes/dhcp6.h index bf8205c..e2258f1 100644 --- a/includes/dhcp6.h +++ b/includes/dhcp6.h @@ -115,6 +115,7 @@ #define D6O_V6_PCP_SERVER 86 /* RFC7291 */ #define D6O_DHCPV4_MSG 87 /* RFC7341 */ #define D6O_DHCP4_O_DHCP6_SERVER 88 /* RFC7341 */ +#define D6O_RELAY_PORT 90 /* XXX draft relay-port */ /* * Status Codes, from RFC 3315 section 24.4, and RFC 3633, 5007, 5460. diff --git a/includes/dhcpd.h b/includes/dhcpd.h index 261714d..6f2737e 100644 --- a/includes/dhcpd.h +++ b/includes/dhcpd.h @@ -472,6 +472,9 @@ struct packet { /* Propogates server value SV_ECHO_CLIENT_ID so it is available * in cons_options() */ int sv_echo_client_id; + + /* Relay port check */ + isc_boolean_t relay_source_port; }; /* diff --git a/server/dhcpv6.c b/server/dhcpv6.c index 18d8bb8..45f15d3 100644 --- a/server/dhcpv6.c +++ b/server/dhcpv6.c @@ -27,6 +27,13 @@ static void send_dhcpv4_response(struct data_string *raw); static void recv_dhcpv4_query(struct data_string *raw); static void dhcp4o6_dhcpv4_query(struct data_string *reply_ret, struct packet *packet); + +struct udp_data4o6 { + u_int16_t src_port; + u_int8_t rp_opt_exist; + u_int8_t reserved; +}; +static int offset_data4o6 = 32 + sizeof(struct udp_data4o6); #endif /* @@ -198,7 +205,7 @@ isc_result_t dhcpv4o6_handler(omapi_object_t *h) { cc = recv(dhcp4o6_fd, buf, sizeof(buf), 0); - if (cc < DHCP_FIXED_NON_UDP + 32) + if (cc < DHCP_FIXED_NON_UDP + offset_data4o6) return ISC_R_UNEXPECTED; memset(&raw, 0, sizeof(raw)); if (!buffer_allocate(&raw.buffer, cc, MDL)) { @@ -224,7 +231,7 @@ isc_result_t dhcpv4o6_handler(omapi_object_t *h) { * \brief Send the DHCPv4-response back to the DHCPv6 side * (DHCPv6 server function) * - * Format: interface:16 + address:16 + DHCPv6 DHCPv4-response message + * Format: interface:16 + address:16 + udp:4 + DHCPv6 DHCPv4-response message * * \param raw the IPC message content */ @@ -234,6 +241,7 @@ static void send_dhcpv4_response(struct data_string *raw) { struct sockaddr_in6 to_addr; char pbuf[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")]; int send_ret; + struct udp_data4o6 udp_data; memset(name, 0, sizeof(name)); memcpy(name, raw->data, 16); @@ -250,26 +258,32 @@ static void send_dhcpv4_response(struct data_string *raw) { memset(&to_addr, 0, sizeof(to_addr)); to_addr.sin6_family = AF_INET6; memcpy(&to_addr.sin6_addr, raw->data + 16, 16); - if ((raw->data[32] == DHCPV6_RELAY_FORW) || - (raw->data[32] == DHCPV6_RELAY_REPL)) { - to_addr.sin6_port = local_port; + + memset(&udp_data, 0, sizeof(udp_data)); + memcpy(&udp_data, raw->data + 32, sizeof(udp_data)); + + if ((raw->data[offset_data4o6] == DHCPV6_RELAY_FORW) || + (raw->data[offset_data4o6] == DHCPV6_RELAY_REPL)) { + to_addr.sin6_port = (udp_data.rp_opt_exist) ? + udp_data.src_port : local_port; } else { to_addr.sin6_port = remote_port; } log_info("send_dhcpv4_response(): sending %s on %s to %s port %d", - dhcpv6_type_names[raw->data[32]], + dhcpv6_type_names[raw->data[offset_data4o6]], name, inet_ntop(AF_INET6, raw->data + 16, pbuf, sizeof(pbuf)), ntohs(to_addr.sin6_port)); - send_ret = send_packet6(ip, raw->data + 32, raw->len - 32, &to_addr); + send_ret = send_packet6(ip, raw->data + offset_data4o6, + raw->len - offset_data4o6, &to_addr); if (send_ret < 0) { log_error("send_dhcpv4_response: send_packet6(): %m"); - } else if (send_ret != raw->len - 32) { + } else if (send_ret != raw->len - offset_data4o6) { log_error("send_dhcpv4_response: send_packet6() " "sent %d of %d bytes", - send_ret, raw->len - 32); + send_ret, raw->len - offset_data4o6); } } #endif /* DHCP4o6 */ @@ -844,6 +858,7 @@ static const int required_opts_solicit[] = { }; static const int required_opts_agent[] = { D6O_INTERFACE_ID, + D6O_RELAY_PORT, D6O_RELAY_MSG, 0 }; @@ -6838,6 +6853,33 @@ dhcp4o6_relay_forw(struct data_string *reply_ret, struct packet *packet) { } /* + * Append the relay_port option if present. + */ + oc = lookup_option(&dhcpv6_universe, packet->options, + D6O_RELAY_PORT); + if (oc != NULL) { + if (!evaluate_option_cache(&a_opt, packet, + NULL, NULL, + packet->options, NULL, + &global_scope, oc, MDL)) { + log_error("dhcpv6_relay_forw: error evaluating " + "Relay Port."); + goto exit; + } + if (!save_option_buffer(&dhcpv6_universe, opt_state, NULL, + (unsigned char *)a_opt.data, + a_opt.len, + D6O_RELAY_PORT, 0)) { + log_error("dhcpv6_relay_forw: error saving " + "Relay Port."); + goto exit; + } + data_string_forget(&a_opt, MDL); + + packet->relay_source_port = ISC_TRUE; + } + + /* * Append our encapsulated stuff for caller. */ if (!save_option_buffer(&dhcpv6_universe, opt_state, NULL, @@ -7126,7 +7168,7 @@ exit: * \brief Forward a DHCPv4-query message to the DHCPv4 side * (DHCPv6 server function) * - * Format: interface:16 + address:16 + DHCPv6 DHCPv4-query message + * Format: interface:16 + address:16 + udp:4 + DHCPv6 DHCPv4-query message * * \brief packet the DHCPv6 DHCPv4-query message */ @@ -7134,6 +7176,7 @@ static void forw_dhcpv4_query(struct packet *packet) { struct data_string ds; unsigned len; int cc; + struct udp_data4o6 udp_data; /* Get the initial message. */ while (packet->dhcpv6_container_packet != NULL) @@ -7148,7 +7191,7 @@ static void forw_dhcpv4_query(struct packet *packet) { } /* Get a buffer. */ - len = packet->packet_length + 32; + len = packet->packet_length + offset_data4o6; memset(&ds, 0, sizeof(ds)); if (!buffer_allocate(&ds.buffer, len, MDL)) { log_error("forw_dhcpv4_query: " @@ -7162,7 +7205,10 @@ static void forw_dhcpv4_query(struct packet *packet) { strncpy((char *)ds.buffer->data, packet->interface->name, 16); memcpy(ds.buffer->data + 16, packet->client_addr.iabuf, 16); - memcpy(ds.buffer->data + 32, + memset(&udp_data, 0, sizeof(udp_data)); + udp_data.src_port = packet->client_port; + memcpy(ds.buffer->data + 32, &udp_data, sizeof(udp_data)); + memcpy(ds.buffer->data + offset_data4o6, (unsigned char *)packet->raw, packet->packet_length); @@ -7406,7 +7452,7 @@ dhcpv6(struct packet *packet) { * Receive a message with a DHCPv4-query inside from the DHCPv6 server. * (code copied from \ref do_packet6() \ref and dhcpv6()) * - * Format: interface:16 + address:16 + DHCPv6 DHCPv4-query message + * Format: interface:16 + address:16 + +udp:4 + DHCPv6 DHCPv4-query message * * \param raw the DHCPv6 DHCPv4-query message raw content */ @@ -7422,6 +7468,7 @@ static void recv_dhcpv4_query(struct data_string *raw) { struct data_string ds; unsigned len; int cc; + struct udp_data4o6 udp_data; memset(name, 0, sizeof(name)); memcpy(name, raw->data, 16); @@ -7438,14 +7485,18 @@ static void recv_dhcpv4_query(struct data_string *raw) { iaddr.len = 16; memcpy(iaddr.iabuf, raw->data + 16, 16); + memset(&udp_data, 0, sizeof(udp_data)); + memcpy(&udp_data, raw->data + 32, sizeof(udp_data)); + /* * From do_packet6(). */ - if (!packet6_len_okay((char *)raw->data + 32, raw->len - 32)) { + if (!packet6_len_okay((char *)raw->data + offset_data4o6, + raw->len - offset_data4o6)) { log_error("recv_dhcpv4_query: " "short packet from %s, len %d, dropped", - piaddr(iaddr), raw->len - 32); + piaddr(iaddr), raw->len - offset_data4o6); return; } @@ -7464,18 +7515,19 @@ static void recv_dhcpv4_query(struct data_string *raw) { return; } - packet->raw = (struct dhcp_packet *)(raw->data + 32); - packet->packet_length = raw->len - 32; - packet->client_port = remote_port; + packet->raw = (struct dhcp_packet *)(raw->data + offset_data4o6); + packet->packet_length = raw->len - offset_data4o6; + packet->client_port = udp_data.src_port; packet->client_addr = iaddr; interface_reference(&packet->interface, ip, MDL); - msg_type = raw->data[32]; + msg_type = raw->data[offset_data4o6]; if ((msg_type == DHCPV6_RELAY_FORW) || (msg_type == DHCPV6_RELAY_REPL)) { int relaylen = (int)(offsetof(struct dhcpv6_relay_packet, options)); - relay = (const struct dhcpv6_relay_packet *)(raw->data + 32); + relay = (const struct dhcpv6_relay_packet *)(raw->data + + offset_data4o6); packet->dhcpv6_msg_type = relay->msg_type; /* relay-specific data */ @@ -7487,7 +7539,7 @@ static void recv_dhcpv4_query(struct data_string *raw) { if (!parse_option_buffer(packet->options, relay->options, - raw->len - 32 - relaylen, + raw->len - offset_data4o6 - relaylen, &dhcpv6_universe)) { /* no logging here, as parse_option_buffer() logs all cases where it fails */ @@ -7498,7 +7550,8 @@ static void recv_dhcpv4_query(struct data_string *raw) { (msg_type == DHCPV6_DHCPV4_RESPONSE)) { int msglen = (int)(offsetof(struct dhcpv4_over_dhcpv6_packet, options)); - msg = (struct dhcpv4_over_dhcpv6_packet *)(raw->data + 32); + msg = (struct dhcpv4_over_dhcpv6_packet *)(raw->data + + offset_data4o6); packet->dhcpv6_msg_type = msg->msg_type; /* message-specific data */ @@ -7507,7 +7560,7 @@ static void recv_dhcpv4_query(struct data_string *raw) { if (!parse_option_buffer(packet->options, msg->options, - raw->len - 32 - msglen, + raw->len - offset_data4o6 - msglen, &dhcpv6_universe)) { /* no logging here, as parse_option_buffer() logs all cases where it fails */ @@ -7576,7 +7629,7 @@ static void recv_dhcpv4_query(struct data_string *raw) { /* * Forward the response. */ - len = reply.len + 32; + len = reply.len + offset_data4o6; memset(&ds, 0, sizeof(ds)); if (!buffer_allocate(&ds.buffer, len, MDL)) { log_error("recv_dhcpv4_query: no memory."); @@ -7587,7 +7640,11 @@ static void recv_dhcpv4_query(struct data_string *raw) { memcpy(ds.buffer->data, name, 16); memcpy(ds.buffer->data + 16, iaddr.iabuf, 16); - memcpy(ds.buffer->data + 32, reply.data, reply.len); + + udp_data.rp_opt_exist = (packet->relay_source_port) ? 1 : 0; + memcpy(ds.buffer->data + 32, &udp_data, sizeof(udp_data)); + + memcpy(ds.buffer->data + offset_data4o6, reply.data, reply.len); cc = send(dhcp4o6_fd, ds.data, ds.len, 0); if (cc < 0) log_error("recv_dhcpv4_query: send(): %m");