mirror of
https://gitlab.isc.org/isc-projects/dhcp
synced 2025-08-29 05:17:57 +00:00
ERO(RFC 4994) server support
This commit is contained in:
parent
80c9fdb0e7
commit
2b964ac0c7
2
RELNOTES
2
RELNOTES
@ -89,6 +89,8 @@ work on other platforms. Please report any problems and suggested fixes to
|
|||||||
The default delayed ack limit is 28. Thanks entirely to a patch from
|
The default delayed ack limit is 28. Thanks entirely to a patch from
|
||||||
Christof Chen.
|
Christof Chen.
|
||||||
|
|
||||||
|
- ERO (RFC 4994) server support.
|
||||||
|
|
||||||
Changes since 4.0.0 (bug fixes)
|
Changes since 4.0.0 (bug fixes)
|
||||||
|
|
||||||
- DHCP now builds on AIX.
|
- DHCP now builds on AIX.
|
||||||
|
178
server/dhcpv6.c
178
server/dhcpv6.c
@ -699,6 +699,11 @@ static const int required_opts_solicit[] = {
|
|||||||
D6O_PREFERENCE,
|
D6O_PREFERENCE,
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
static const int required_opts_agent[] = {
|
||||||
|
D6O_INTERFACE_ID,
|
||||||
|
D6O_RELAY_MSG,
|
||||||
|
0
|
||||||
|
};
|
||||||
static const int required_opts_IA[] = {
|
static const int required_opts_IA[] = {
|
||||||
D6O_IAADDR,
|
D6O_IAADDR,
|
||||||
D6O_STATUS_CODE,
|
D6O_STATUS_CODE,
|
||||||
@ -5224,7 +5229,6 @@ dhcpv6_information_request(struct data_string *reply, struct packet *packet) {
|
|||||||
be combined in a clever way */
|
be combined in a clever way */
|
||||||
static void
|
static void
|
||||||
dhcpv6_relay_forw(struct data_string *reply_ret, struct packet *packet) {
|
dhcpv6_relay_forw(struct data_string *reply_ret, struct packet *packet) {
|
||||||
struct dhcpv6_relay_packet reply;
|
|
||||||
struct option_cache *oc;
|
struct option_cache *oc;
|
||||||
struct data_string enc_opt_data;
|
struct data_string enc_opt_data;
|
||||||
struct packet *enc_packet;
|
struct packet *enc_packet;
|
||||||
@ -5234,7 +5238,11 @@ dhcpv6_relay_forw(struct data_string *reply_ret, struct packet *packet) {
|
|||||||
struct data_string enc_reply;
|
struct data_string enc_reply;
|
||||||
char link_addr[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
|
char link_addr[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
|
||||||
char peer_addr[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
|
char peer_addr[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
|
||||||
struct data_string interface_id;
|
struct data_string a_opt, packet_ero;
|
||||||
|
struct option_state *opt_state;
|
||||||
|
static char reply_data[65536];
|
||||||
|
struct dhcpv6_relay_packet *reply;
|
||||||
|
int reply_ofs;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize variables for early exit.
|
* Initialize variables for early exit.
|
||||||
@ -5242,7 +5250,8 @@ dhcpv6_relay_forw(struct data_string *reply_ret, struct packet *packet) {
|
|||||||
memset(&enc_opt_data, 0, sizeof(enc_opt_data));
|
memset(&enc_opt_data, 0, sizeof(enc_opt_data));
|
||||||
enc_packet = NULL;
|
enc_packet = NULL;
|
||||||
memset(&enc_reply, 0, sizeof(enc_reply));
|
memset(&enc_reply, 0, sizeof(enc_reply));
|
||||||
memset(&interface_id, 0, sizeof(interface_id));
|
memset(&a_opt, 0, sizeof(a_opt));
|
||||||
|
memset(&packet_ero, 0, sizeof(packet_ero));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get our encapsulated relay message.
|
* Get our encapsulated relay message.
|
||||||
@ -5259,7 +5268,6 @@ dhcpv6_relay_forw(struct data_string *reply_ret, struct packet *packet) {
|
|||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(&enc_opt_data, 0, sizeof(enc_opt_data));
|
|
||||||
if (!evaluate_option_cache(&enc_opt_data, NULL, NULL, NULL,
|
if (!evaluate_option_cache(&enc_opt_data, NULL, NULL, NULL,
|
||||||
NULL, NULL, &global_scope, oc, MDL)) {
|
NULL, NULL, &global_scope, oc, MDL)) {
|
||||||
log_error("dhcpv6_forw_relay: error evaluating "
|
log_error("dhcpv6_forw_relay: error evaluating "
|
||||||
@ -5347,68 +5355,140 @@ dhcpv6_relay_forw(struct data_string *reply_ret, struct packet *packet) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Append the interface-id if present
|
* Now we can use the reply_data buffer.
|
||||||
|
* Packet header stuff all comes from the forward message.
|
||||||
*/
|
*/
|
||||||
oc = lookup_option(&dhcpv6_universe, packet->options, D6O_INTERFACE_ID);
|
reply = (struct dhcpv6_relay_packet *)reply_data;
|
||||||
if (oc != NULL) {
|
reply->msg_type = DHCPV6_RELAY_REPL;
|
||||||
memset(&interface_id, 0, sizeof(interface_id));
|
reply->hop_count = packet->dhcpv6_hop_count;
|
||||||
if (!evaluate_option_cache(&interface_id, NULL, NULL, NULL,
|
memcpy(reply->link_address, &packet->dhcpv6_link_address,
|
||||||
NULL, NULL, &global_scope,
|
sizeof(reply->link_address));
|
||||||
oc, MDL)) {
|
memcpy(reply->peer_address, &packet->dhcpv6_peer_address,
|
||||||
log_error("dhcpv6_forw_relay: error evaluating "
|
sizeof(reply->peer_address));
|
||||||
"Interface ID.");
|
reply_ofs = (int)((char *)reply->options - (char *)reply);
|
||||||
goto exit;
|
|
||||||
}
|
/*
|
||||||
|
* Get the reply option state.
|
||||||
|
*/
|
||||||
|
opt_state = NULL;
|
||||||
|
if (!option_state_allocate(&opt_state, MDL)) {
|
||||||
|
log_error("dhcpv6_relay_forw: no memory for option state.");
|
||||||
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Packet header stuff all comes from the forward message.
|
* Append the interface-id if present.
|
||||||
*/
|
*/
|
||||||
reply.msg_type = DHCPV6_RELAY_REPL;
|
oc = lookup_option(&dhcpv6_universe, packet->options,
|
||||||
reply.hop_count = packet->dhcpv6_hop_count;
|
D6O_INTERFACE_ID);
|
||||||
memcpy(reply.link_address, &packet->dhcpv6_link_address,
|
if (oc != NULL) {
|
||||||
sizeof(reply.link_address));
|
if (!evaluate_option_cache(&a_opt, packet,
|
||||||
memcpy(reply.peer_address, &packet->dhcpv6_peer_address,
|
NULL, NULL,
|
||||||
sizeof(reply.peer_address));
|
packet->options, NULL,
|
||||||
|
&global_scope, oc, MDL)) {
|
||||||
|
log_error("dhcpv6_relay_forw: error evaluating "
|
||||||
|
"Interface ID.");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
if (!save_option_buffer(&dhcpv6_universe, opt_state, NULL,
|
||||||
|
(unsigned char *)a_opt.data,
|
||||||
|
a_opt.len,
|
||||||
|
D6O_INTERFACE_ID, 0)) {
|
||||||
|
log_error("dhcpv6_relay_forw: error saving "
|
||||||
|
"Interface ID.");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
data_string_forget(&a_opt, MDL);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copy our encapsulated stuff for caller.
|
* Append our encapsulated stuff for caller.
|
||||||
*/
|
*/
|
||||||
reply_ret->len = sizeof(reply) + 4 + enc_reply.len;
|
if (!save_option_buffer(&dhcpv6_universe, opt_state, NULL,
|
||||||
if (interface_id.data != NULL) {
|
(unsigned char *)enc_reply.data,
|
||||||
reply_ret->len += 4 + interface_id.len;
|
enc_reply.len,
|
||||||
}
|
D6O_RELAY_MSG, 0)) {
|
||||||
/*
|
log_error("dhcpv6_relay_forw: error saving Relay MSG.");
|
||||||
* XXX: We should not allow this to happen, perhaps by letting
|
|
||||||
* build_dhcp_reply() know our space remaining.
|
|
||||||
*/
|
|
||||||
if (reply_ret->len >= 65536) {
|
|
||||||
log_error("dhcpv6_forw_relay: RELAY-REPL too big (%d bytes)",
|
|
||||||
reply_ret->len);
|
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the ERO if any.
|
||||||
|
*/
|
||||||
|
oc = lookup_option(&dhcpv6_universe, packet->options, D6O_ERO);
|
||||||
|
if (oc != NULL) {
|
||||||
|
unsigned req;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!evaluate_option_cache(&packet_ero, packet,
|
||||||
|
NULL, NULL,
|
||||||
|
packet->options, NULL,
|
||||||
|
&global_scope, oc, MDL) ||
|
||||||
|
(packet_ero.len & 1)) {
|
||||||
|
log_error("dhcpv6_relay_forw: error evaluating ERO.");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Decode and apply the ERO. */
|
||||||
|
for (i = 0; i < packet_ero.len; i += 2) {
|
||||||
|
req = getUShort(packet_ero.data + i);
|
||||||
|
/* Already in the reply? */
|
||||||
|
oc = lookup_option(&dhcpv6_universe, opt_state, req);
|
||||||
|
if (oc != NULL)
|
||||||
|
continue;
|
||||||
|
/* Get it from the packet if present. */
|
||||||
|
oc = lookup_option(&dhcpv6_universe,
|
||||||
|
packet->options,
|
||||||
|
req);
|
||||||
|
if (oc == NULL)
|
||||||
|
continue;
|
||||||
|
if (!evaluate_option_cache(&a_opt, packet,
|
||||||
|
NULL, NULL,
|
||||||
|
packet->options, NULL,
|
||||||
|
&global_scope, oc, MDL)) {
|
||||||
|
log_error("dhcpv6_relay_forw: error "
|
||||||
|
"evaluating option %u.", req);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
if (!save_option_buffer(&dhcpv6_universe,
|
||||||
|
opt_state,
|
||||||
|
NULL,
|
||||||
|
(unsigned char *)a_opt.data,
|
||||||
|
a_opt.len,
|
||||||
|
req,
|
||||||
|
0)) {
|
||||||
|
log_error("dhcpv6_relay_forw: error saving "
|
||||||
|
"option %u.", req);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
data_string_forget(&a_opt, MDL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reply_ofs += store_options6(reply_data + reply_ofs,
|
||||||
|
sizeof(reply_data) - reply_ofs,
|
||||||
|
opt_state, packet,
|
||||||
|
required_opts_agent, &packet_ero);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return our reply to the caller.
|
||||||
|
*/
|
||||||
|
reply_ret->len = reply_ofs;
|
||||||
reply_ret->buffer = NULL;
|
reply_ret->buffer = NULL;
|
||||||
if (!buffer_allocate(&reply_ret->buffer, reply_ret->len, MDL)) {
|
if (!buffer_allocate(&reply_ret->buffer, reply_ret->len, MDL)) {
|
||||||
log_fatal("No memory to store reply.");
|
log_fatal("No memory to store reply.");
|
||||||
}
|
}
|
||||||
reply_ret->data = reply_ret->buffer->data;
|
reply_ret->data = reply_ret->buffer->data;
|
||||||
memcpy(reply_ret->buffer->data, &reply, sizeof(reply));
|
memcpy(reply_ret->buffer->data, reply_data, reply_ofs);
|
||||||
putShort(reply_ret->buffer->data+sizeof(reply), D6O_RELAY_MSG);
|
|
||||||
putShort(reply_ret->buffer->data+sizeof(reply)+2, enc_reply.len);
|
|
||||||
memcpy(reply_ret->buffer->data+sizeof(reply)+4,
|
|
||||||
enc_reply.data, enc_reply.len);
|
|
||||||
if (interface_id.data != NULL) {
|
|
||||||
putShort(reply_ret->buffer->data+sizeof(reply)+4+enc_reply.len,
|
|
||||||
D6O_INTERFACE_ID);
|
|
||||||
putShort(reply_ret->buffer->data+sizeof(reply)+6+enc_reply.len,
|
|
||||||
interface_id.len);
|
|
||||||
memcpy(reply_ret->buffer->data+sizeof(reply)+8+enc_reply.len,
|
|
||||||
interface_id.data, interface_id.len);
|
|
||||||
}
|
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
if (interface_id.data != NULL) {
|
if (opt_state != NULL)
|
||||||
data_string_forget(&interface_id, MDL);
|
option_state_dereference(&opt_state, MDL);
|
||||||
|
if (a_opt.data != NULL) {
|
||||||
|
data_string_forget(&a_opt, MDL);
|
||||||
|
}
|
||||||
|
if (packet_ero.data != NULL) {
|
||||||
|
data_string_forget(&packet_ero, MDL);
|
||||||
}
|
}
|
||||||
if (enc_reply.data != NULL) {
|
if (enc_reply.data != NULL) {
|
||||||
data_string_forget(&enc_reply, MDL);
|
data_string_forget(&enc_reply, MDL);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user