summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrancis Dupont <fdupont@isc.org>2016-02-23 10:40:10 +0100
committerFrancis Dupont <fdupont@isc.org>2016-02-23 10:40:10 +0100
commit785c1a519e88bfebb70bd3384589de36bee02dd2 (patch)
tree04f794b247068983b2df432fb20f229de1e8bf03
parent08ad1e3c9089b51888c73837cf41de49112de29c (diff)
downloadisc-dhcp-785c1a519e88bfebb70bd3384589de36bee02dd2.tar.gz
Merged rt35711c (DHCPv4-over-DHCPv6 support)
-rw-r--r--RELNOTES9
-rw-r--r--client/clparse.c42
-rw-r--r--client/dhc6.c16
-rw-r--r--client/dhclient.814
-rw-r--r--client/dhclient.c746
-rw-r--r--common/Makefile.am10
-rw-r--r--common/Makefile.in28
-rw-r--r--common/discover.c49
-rw-r--r--common/inet.c28
-rw-r--r--common/options.c25
-rw-r--r--common/tables.c37
-rwxr-xr-xconfigure34
-rw-r--r--configure.ac27
-rw-r--r--includes/config.h.in3
-rw-r--r--includes/dhcp6.h72
-rw-r--r--includes/dhcpd.h32
-rw-r--r--includes/site.h1
-rw-r--r--relay/dhcrelay.c4
-rw-r--r--server/bootp.c30
-rw-r--r--server/confpars.c11
-rw-r--r--server/dhcp.c374
-rw-r--r--server/dhcpd.816
-rw-r--r--server/dhcpd.c60
-rw-r--r--server/dhcpleasequery.c7
-rw-r--r--server/dhcpv6.c889
-rw-r--r--server/mdb.c12
-rw-r--r--server/stables.c2
27 files changed, 2494 insertions, 84 deletions
diff --git a/RELNOTES b/RELNOTES
index aef9a85a..192a08b8 100644
--- a/RELNOTES
+++ b/RELNOTES
@@ -227,6 +227,15 @@ by Eric Young (eay@cryptsoft.com).
- Corrected minor Coverity issues.
[ISC-Bugs #35144]
+- Add support for RFC 7341 DHCPv4 over DHCPv6 with a new configuration
+ option "--enable-dhcpv4o6". Note this feature requires DHCPv6 support
+ and is not compatible with delayed-ack. Both client and server use 2
+ processes which communicate over UDP on a pair of sockets. The new
+ "-4o6 <port>" command line argment enables DHCPv4 over DHCPv6 support
+ and specifies the consecutive ports to use for inter-process communication.
+ Please look at doc/DHCPv4-over-DHCPv6 for more details.
+ [ISC-Bugs #35711]
+
Changes since 4.3.3b1
- None
diff --git a/client/clparse.c b/client/clparse.c
index 320c42f5..643b3fae 100644
--- a/client/clparse.c
+++ b/client/clparse.c
@@ -3,7 +3,7 @@
Parser for dhclient config and lease files... */
/*
- * Copyright (c) 2004-2014 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2004-2014,2016 by Internet Systems Consortium, Inc. ("ISC")
* Copyright (c) 1996-2003 by Internet Software Consortium
*
* Permission to use, copy, modify, and distribute this software for any
@@ -32,7 +32,8 @@
struct client_config top_level_config;
#define NUM_DEFAULT_REQUESTED_OPTS 9
-struct option *default_requested_options[NUM_DEFAULT_REQUESTED_OPTS + 1];
+/* There can be 2 extra requested options for DHCPv4-over-DHCPv6. */
+struct option *default_requested_options[NUM_DEFAULT_REQUESTED_OPTS + 2 + 1];
static void parse_client_default_duid(struct parse *cfile);
static void parse_client6_lease_statement(struct parse *cfile);
@@ -120,6 +121,43 @@ isc_result_t read_client_conf ()
"assembly.", code);
}
+#ifdef DHCP4o6
+ /* DHCPv4-over-DHCPv6 extra requested options in code order */
+ if (dhcpv4_over_dhcpv6 == 1) {
+ /* The DHCP4o6 server option should be requested */
+ code = D6O_DHCP4_O_DHCP6_SERVER;
+ option_code_hash_lookup(&default_requested_options[9],
+ dhcpv6_universe.code_hash,
+ &code, 0, MDL);
+ if (default_requested_options[9] == NULL) {
+ log_fatal("Unable to find option definition for "
+ "index %u during default parameter request "
+ "assembly.", code);
+ }
+ } else if (dhcpv4_over_dhcpv6 > 1) {
+ /* Called from run_stateless so the IRT should
+ be requested too */
+ code = D6O_INFORMATION_REFRESH_TIME;
+ option_code_hash_lookup(&default_requested_options[9],
+ dhcpv6_universe.code_hash,
+ &code, 0, MDL);
+ if (default_requested_options[9] == NULL) {
+ log_fatal("Unable to find option definition for "
+ "index %u during default parameter request "
+ "assembly.", code);
+ }
+ code = D6O_DHCP4_O_DHCP6_SERVER;
+ option_code_hash_lookup(&default_requested_options[10],
+ dhcpv6_universe.code_hash,
+ &code, 0, MDL);
+ if (default_requested_options[10] == NULL) {
+ log_fatal("Unable to find option definition for "
+ "index %u during default parameter request "
+ "assembly.", code);
+ }
+ }
+#endif
+
/* Initialize the top level client configuration. */
memset (&top_level_config, 0, sizeof top_level_config);
diff --git a/client/dhc6.c b/client/dhc6.c
index aacd4aea..c8d16e8a 100644
--- a/client/dhc6.c
+++ b/client/dhc6.c
@@ -4835,6 +4835,11 @@ start_bound(struct client_state *client)
script_go(client);
}
+#ifdef DHCP4o6
+ if (dhcpv4_over_dhcpv6)
+ dhcp4o6_start();
+#endif
+
go_daemon();
if (client->old_lease != NULL) {
@@ -5314,8 +5319,12 @@ dhc6_check_irt(struct client_state *client)
}
}
/* Simply return gives a endless loop waiting for nothing. */
- if (!found)
+ if (!found) {
+#ifdef DHCP4o6
+ if (!dhcpv4_over_dhcpv6)
+#endif
exit(0);
+ }
oc = lookup_option(&dhcpv6_universe, client->active_lease->options,
D6O_INFORMATION_REFRESH_TIME);
@@ -5368,6 +5377,11 @@ start_informed(struct client_state *client)
script_write_requested6(client);
script_go(client);
+#ifdef DHCP4o6
+ if (dhcpv4_over_dhcpv6)
+ dhcp4o6_start();
+#endif
+
go_daemon();
if (client->old_lease != NULL) {
diff --git a/client/dhclient.8 b/client/dhclient.8
index 83260e61..1946d9bb 100644
--- a/client/dhclient.8
+++ b/client/dhclient.8
@@ -1,6 +1,6 @@
.\" $Id: dhclient.8,v 1.36 2011/04/15 21:58:12 sar Exp $
.\"
-.\" Copyright (c) 2004,2007-2015 by Internet Systems Consortium, Inc. ("ISC")
+.\" Copyright (c) 2004,2007-2016 by Internet Systems Consortium, Inc. ("ISC")
.\" Copyright (c) 1996-2003 by Internet Software Consortium
.\"
.\" Permission to use, copy, modify, and distribute this software for any
@@ -64,6 +64,10 @@ dhclient - Dynamic Host Configuration Protocol Client
.B -I
]
[
+.B -4o6
+.I port
+]
+[
.B -D
.I LL|LLT
]
@@ -239,6 +243,14 @@ along with configuration parameters. It cannot be combined with
processing. Note: it is not recommended to mix queries of different
types together or even to share the lease file between them.
.TP
+.BI \-4o6 \ port
+Participate in the DHCPv4 over DHCPv6 protocol specified by RFC 7341.
+This associates a DHCPv4 and a DHCPv6 client to allow the v4 client to
+send v4 requests encapuslated in a v6 packet. Communication
+between the two clients is done on a pair of UDP sockets bound
+to ::1 \fIport\fR and \fIport + 1\fR. Both clients must
+be launched using the same \fIport\fR argument.
+.TP
.BI \-1
Try to get a lease once. On failure exit with code 2. In DHCPv6 this
sets the maximum duration of the initial exchange to
diff --git a/client/dhclient.c b/client/dhclient.c
index df7f8836..d1d83a01 100644
--- a/client/dhclient.c
+++ b/client/dhclient.c
@@ -80,6 +80,9 @@ static const char url [] = "For info, please visit https://www.isc.org/software/
u_int16_t local_port = 0;
u_int16_t remote_port = 0;
+#if defined(DHCPv6) && defined(DHCP4o6)
+int dhcp4o6_state = -1; /* -1 = stopped, 0 = polling, 1 = started */
+#endif
int no_daemon = 0;
struct string_list *client_env = NULL;
int client_env_count = 0;
@@ -97,7 +100,7 @@ char *mockup_relay = NULL;
char *progname = NULL;
-void run_stateless(int exit_mode);
+void run_stateless(int exit_mode, u_int16_t port);
static isc_result_t write_duid(struct data_string *duid);
static void add_reject(struct packet *packet);
@@ -126,6 +129,17 @@ static void dhclient_ddns_cb_free(dhcp_ddns_cb_t *ddns_cb,
* \return Nothing
*/
+#if defined(DHCPv6) && defined(DHCP4o6)
+static void dhcp4o6_poll(void *dummy);
+static void dhcp4o6_resume(void);
+static void recv_dhcpv4_response(struct data_string *raw);
+static int send_dhcpv4_query(struct client_state *client, int broadcast);
+
+static void dhcp4o6_stop(void);
+static void forw_dhcpv4_response(struct packet *packet);
+static void forw_dhcpv4_query(struct data_string *raw);
+#endif
+
#ifndef UNIT_TEST
/* These are only used when we call usage() from the main routine
* which isn't compiled when building for unit tests
@@ -151,14 +165,19 @@ usage(const char *sfmt, const char *sarg)
log_fatal("Usage: %s "
#ifdef DHCPv6
+#ifdef DHCP4o6
+ "[-4|-6] [-SNTPRI1dvrxi] [-nw] -4o6 <port>]\n"
+ " [-p <port>] [-D LL|LLT] \n"
+#else /* DHCP4o6 */
"[-4|-6] [-SNTPRI1dvrxi] [-nw] [-p <port>] [-D LL|LLT] \n"
+#endif
#else /* DHCPv6 */
"[-I1dvrxi] [-nw] [-p <port>] [-D LL|LLT] \n"
#endif /* DHCPv6 */
" [-s server-addr] [-cf config-file]\n"
" [-df duid-file] [-lf lease-file]\n"
" [-pf pid-file] [--no-pid] [-e VAR=val]\n"
- " [-sf script-file] [interface]",
+ " [-sf script-file] [interface]*",
isc_file_basename(progname));
}
@@ -183,6 +202,9 @@ main(int argc, char **argv) {
int no_dhclient_script = 0;
#ifdef DHCPv6
int local_family_set = 0;
+#ifdef DHCP4o6
+ u_int16_t dhcp4o6_port = 0;
+#endif /* DHCP4o6 */
#endif /* DHCPv6 */
char *s;
@@ -252,6 +274,17 @@ main(int argc, char **argv) {
"both.");
local_family_set = 1;
local_family = AF_INET6;
+#ifdef DHCP4o6
+ } else if (!strcmp(argv[i], "-4o6")) {
+ if (++i == argc)
+ usage(use_noarg, argv[i-1]);
+ dhcp4o6_port = validate_port_pair(argv[i]);
+
+ log_debug("DHCPv4 over DHCPv6 over ::1 port %d and %d",
+ ntohs(dhcp4o6_port),
+ ntohs(dhcp4o6_port) + 1);
+ dhcpv4_over_dhcpv6 = 1;
+#endif /* DHCP4o6 */
#endif /* DHCPv6 */
} else if (!strcmp(argv[i], "-x")) { /* eXit, no release */
release_mode = 0;
@@ -434,6 +467,17 @@ main(int argc, char **argv) {
usage("PD %s only supports one requested interface", "-P");
}
+#if defined(DHCPv6) && defined(DHCP4o6)
+ if ((local_family == AF_INET6) && dhcpv4_over_dhcpv6 &&
+ (exit_mode || release_mode))
+ log_error("Can't relay DHCPv4-over-DHCPv6 "
+ "without a persistent DHCPv6 client");
+ if ((local_family == AF_INET) && dhcpv4_over_dhcpv6 &&
+ (interfaces_requested != 1))
+ log_fatal("DHCPv4-over-DHCPv6 requires an explicit "
+ "interface on which to be applied");
+#endif
+
if (!no_dhclient_conf && (s = getenv("PATH_DHCLIENT_CONF"))) {
path_dhclient_conf = s;
}
@@ -572,7 +616,11 @@ main(int argc, char **argv) {
usage("Stateless commnad: %s incompatibile with "
"other commands", "-S");
}
- run_stateless(exit_mode);
+#if defined(DHCPv6) && defined(DHCP4o6)
+ run_stateless(exit_mode, dhcp4o6_port);
+#else
+ run_stateless(exit_mode, 0);
+#endif
return 0;
}
@@ -682,6 +730,11 @@ main(int argc, char **argv) {
}
}
+#if defined(DHCPv6) && defined(DHCP4o6)
+ if (dhcpv4_over_dhcpv6 && !exit_mode)
+ dhcp4o6_setup(dhcp4o6_port);
+#endif
+
/* Start a configuration state machine for each interface. */
#ifdef DHCPv6
if (local_family == AF_INET6) {
@@ -715,7 +768,7 @@ main(int argc, char **argv) {
client = client->next) {
if (exit_mode)
state_stop(client);
- else if (release_mode)
+ if (release_mode)
do_release(client);
else {
client->state = S_INIT;
@@ -752,7 +805,7 @@ main(int argc, char **argv) {
#ifndef DHCPv6
return 0;
#else
- if (local_family == AF_INET6) {
+ if ((local_family == AF_INET6) || dhcpv4_over_dhcpv6) {
if (onetry)
return 0;
} else
@@ -813,13 +866,25 @@ main(int argc, char **argv) {
return 0;
}
-void run_stateless(int exit_mode)
+/*
+ * \brief Run the DHCPv6 stateless client (dhclient -6 -S)
+ *
+ * \param exist_mode set to 1 when dhclient was called with -x
+ * \param port DHCPv4-over-DHCPv6 client inter-process communication
+ * UDP port pair (port,port+1 with port in network byte order)
+ */
+
+void run_stateless(int exit_mode, u_int16_t port)
{
#ifdef DHCPv6
struct client_state *client;
omapi_object_t *listener;
isc_result_t result;
+#ifndef DHCP4o6
+ IGNORE_UNUSED(port);
+#endif
+
/* Discover the network interface. */
discover_interfaces(DISCOVER_REQUESTED);
@@ -827,6 +892,12 @@ void run_stateless(int exit_mode)
usage("No interfaces available for stateless command: %s", "-S");
/* Parse the dhclient.conf file. */
+#ifdef DHCP4o6
+ if (dhcpv4_over_dhcpv6) {
+ /* Mark we want to request IRT too! */
+ dhcpv4_over_dhcpv6++;
+ }
+#endif
read_client_conf();
/* Parse the lease database. */
@@ -845,6 +916,11 @@ void run_stateless(int exit_mode)
form_duid(&default_duid, MDL);
}
+#ifdef DHCP4o6
+ if (dhcpv4_over_dhcpv6 && !exit_mode)
+ dhcp4o6_setup(port);
+#endif
+
/* Start a configuration state machine. */
for (client = interfaces->client ;
client != NULL ;
@@ -967,6 +1043,17 @@ void state_reboot (cpp)
{
struct client_state *client = cpp;
+#if defined(DHCPv6) && defined(DHCP4o6)
+ if (dhcpv4_over_dhcpv6 && (dhcp4o6_state <= 0)) {
+ if (dhcp4o6_state < 0)
+ dhcp4o6_poll(NULL);
+ client->pending = P_REBOOT;
+ return;
+ }
+#endif
+
+ client->pending= P_NONE;
+
/* If we don't remember an active lease, go straight to INIT. */
if (!client -> active ||
client -> active -> is_bootp ||
@@ -1405,6 +1492,8 @@ void state_stop (cpp)
{
struct client_state *client = cpp;
+ client->pending = P_NONE;
+
/* Cancel all timeouts. */
cancel_timeout(state_selecting, client);
cancel_timeout(send_discover, client);
@@ -1553,6 +1642,17 @@ dhcpv6(struct packet *packet) {
/* Screen out nonsensical messages. */
switch(packet->dhcpv6_msg_type) {
+#ifdef DHCP4o6
+ case DHCPV6_DHCPV4_RESPONSE:
+ if (dhcpv4_over_dhcpv6) {
+ log_info("RCV: %s message on %s from %s.",
+ dhcpv6_type_names[packet->dhcpv6_msg_type],
+ packet->interface->name,
+ piaddr(packet->client_addr));
+ forw_dhcpv4_response(packet);
+ }
+ return;
+#endif
case DHCPV6_ADVERTISE:
case DHCPV6_RECONFIGURE:
if (stateless)
@@ -1581,6 +1681,176 @@ dhcpv6(struct packet *packet) {
/* XXX: temporary log for debugging */
log_info("Packet received, but nothing done with it.");
}
+
+#ifdef DHCP4o6
+/*
+ * \brief Forward a DHCPv4-response to the DHCPv4 client.
+ * (DHCPv6 client function)
+ *
+ * The DHCPv6 client receives a DHCPv4-response which is forwarded
+ * to the DHCPv4 client.
+ * Format: address:16 + DHCPv4 message content
+ * (we have no state to keep the address so it is transported in
+ * DHCPv6 <-> DHCPv6 inter-process messages)
+ *
+ * \param packet the DHCPv4-response packet
+ */
+static void forw_dhcpv4_response(struct packet *packet)
+{
+ struct option_cache *oc;
+ struct data_string enc_opt_data;
+ struct data_string ds;
+ int cc;
+
+ /*
+ * Discard if relay is not ready.
+ */
+ if (dhcp4o6_state == -1) {
+ log_info("forw_dhcpv4_response: not ready.");
+ return;
+ }
+
+ if (packet->client_addr.len != 16) {
+ log_error("forw_dhcpv4_response: bad address");
+ return;
+ }
+
+ /*
+ * Get our encapsulated DHCPv4 message.
+ */
+ oc = lookup_option(&dhcpv6_universe, packet->options, D6O_DHCPV4_MSG);
+ if (oc == NULL) {
+ log_info("DHCPv4-response from %s missing "
+ "DHCPv4 Message option.",
+ piaddr(packet->client_addr));
+ return;
+ }
+
+ memset(&enc_opt_data, 0, sizeof(enc_opt_data));
+ if (!evaluate_option_cache(&enc_opt_data, NULL, NULL, NULL,
+ NULL, NULL, &global_scope, oc, MDL)) {
+ log_error("forw_dhcpv4_response: error evaluating "
+ "DHCPv4 message.");
+ data_string_forget(&enc_opt_data, MDL);
+ return;
+ }
+
+ if (enc_opt_data.len < DHCP_FIXED_NON_UDP) {
+ log_error("forw_dhcpv4_response: "
+ "no memory for encapsulated packet.");
+ data_string_forget(&enc_opt_data, MDL);
+ return;
+ }
+
+ /*
+ * Append address.
+ */
+ memset(&ds, 0, sizeof(ds));
+ if (!buffer_allocate(&ds.buffer, enc_opt_data.len + 16, MDL)) {
+ log_error("forw_dhcpv4_response: no memory buffer.");
+ data_string_forget(&enc_opt_data, MDL);
+ return;
+ }
+ ds.data = ds.buffer->data;
+ ds.len = enc_opt_data.len + 16;
+ memcpy(ds.buffer->data, enc_opt_data.data, enc_opt_data.len);
+ memcpy(ds.buffer->data + enc_opt_data.len,
+ packet->client_addr.iabuf, 16);
+ data_string_forget(&enc_opt_data, MDL);
+
+ /*
+ * Forward them.
+ */
+ cc = send(dhcp4o6_fd, ds.data, ds.len, 0);
+ if (cc < 0)
+ log_error("forw_dhcpv4_response: send(): %m");
+
+ data_string_forget(&ds, MDL);
+}
+
+/*
+ * \brief Receive a DHCPv4-response from the DHCPv6 client.
+ * (DHCPv4 client function)
+ *
+ * The DHCPv4 client receives a DHCPv4-response forwarded
+ * by the DHCPv6 client (using \ref forw_dhcpv4_response())
+ *
+ * \param raw the DHCPv4-response raw packet
+ */
+static void recv_dhcpv4_response(struct data_string *raw)
+{
+ struct packet *packet;
+ struct iaddr from;
+
+ if (interfaces == NULL) {
+ log_error("recv_dhcpv4_response: no interfaces.");
+ return;
+ }
+
+ from.len = 16;
+ memcpy(from.iabuf, raw->data + (raw->len - 16), 16);
+
+ /*
+ * Build a packet structure.
+ */
+ packet = NULL;
+ if (!packet_allocate(&packet, MDL)) {
+ log_error("recv_dhcpv4_response: no memory for packet.");
+ return;
+ }
+
+ packet->raw = (struct dhcp_packet *) raw->data;
+ packet->packet_length = raw->len - 16;
+ packet->client_port = remote_port;
+ packet->client_addr = from;
+ interface_reference(&packet->interface, interfaces, MDL);
+
+ /* Allocate packet->options now so it is non-null for all packets */
+ if (!option_state_allocate (&packet->options, MDL)) {
+ log_error("recv_dhcpv4_response: no memory for options.");
+ packet_dereference (&packet, MDL);
+ return;
+ }
+
+ /* If there's an option buffer, try to parse it. */
+ if (packet->packet_length >= DHCP_FIXED_NON_UDP + 4) {
+ struct option_cache *op;
+ if (!parse_options(packet)) {
+ if (packet->options)
+ option_state_dereference
+ (&packet->options, MDL);
+ packet_dereference (&packet, MDL);
+ return;
+ }
+
+ if (packet->options_valid &&
+ (op = lookup_option(&dhcp_universe,
+ packet->options,
+ DHO_DHCP_MESSAGE_TYPE))) {
+ struct data_string dp;
+ memset(&dp, 0, sizeof dp);
+ evaluate_option_cache(&dp, packet, NULL, NULL,
+ packet->options, NULL,
+ NULL, op, MDL);
+ if (dp.len > 0)
+ packet->packet_type = dp.data[0];
+ else
+ packet->packet_type = 0;
+ data_string_forget(&dp, MDL);
+ }
+ }
+
+ if (validate_packet(packet) != 0) {
+ if (packet->packet_type)
+ dhcp(packet);
+ else
+ bootp(packet);
+ }
+
+ /* If the caller kept the packet, they'll have upped the refcnt. */
+ packet_dereference(&packet, MDL);
+}
+#endif /* DHCP4o6 */
#endif /* DHCPv6 */
void dhcpoffer (packet)
@@ -2002,16 +2272,33 @@ void send_discover (cpp)
client -> packet.secs = htons (65535);
client -> secs = client -> packet.secs;
+#if defined(DHCPv6) && defined(DHCP4o6)
+ if (dhcpv4_over_dhcpv6) {
+ log_info ("DHCPDISCOVER interval %ld",
+ (long)(client -> interval));
+ } else
+#endif
log_info ("DHCPDISCOVER on %s to %s port %d interval %ld",
client -> name ? client -> name : client -> interface -> name,
inet_ntoa (sockaddr_broadcast.sin_addr),
ntohs (sockaddr_broadcast.sin_port), (long)(client -> interval));
/* Send out a packet. */
+#if defined(DHCPv6) && defined(DHCP4o6)
+ if (dhcpv4_over_dhcpv6) {
+ result = send_dhcpv4_query(client, 1);
+ } else
+#endif
result = send_packet(client->interface, NULL, &client->packet,
client->packet_length, inaddr_any,
&sockaddr_broadcast, NULL);
if (result < 0) {
+#if defined(DHCPv6) && defined(DHCP4o6)
+ if (dhcpv4_over_dhcpv6) {
+ log_error("%s:%d: Failed to send %d byte long packet.",
+ MDL, client->packet_length);
+ } else
+#endif
log_error("%s:%d: Failed to send %d byte long packet over %s "
"interface.", MDL, client->packet_length,
client->interface->name);
@@ -2274,11 +2561,28 @@ void send_request (cpp)
client -> packet.secs = htons (65535);
}
+#if defined(DHCPv6) && defined(DHCP4o6)
+ if (dhcpv4_over_dhcpv6) {
+ log_info ("DHCPREQUEST");
+ } else
+#endif
log_info ("DHCPREQUEST on %s to %s port %d",
client -> name ? client -> name : client -> interface -> name,
inet_ntoa (destination.sin_addr),
ntohs (destination.sin_port));
+#if defined(DHCPv6) && defined(DHCP4o6)
+ if (dhcpv4_over_dhcpv6) {
+ int broadcast = 0;
+ if (destination.sin_addr.s_addr == INADDR_BROADCAST)
+ broadcast = 1;
+ result = send_dhcpv4_query(client, broadcast);
+ if (result < 0) {
+ log_error("%s:%d: Failed to send %d byte long packet.",
+ MDL, client->packet_length);
+ }
+ } else
+#endif
if (destination.sin_addr.s_addr != INADDR_BROADCAST &&
fallback_interface) {
result = send_packet(fallback_interface, NULL, &client->packet,
@@ -2317,16 +2621,32 @@ void send_decline (cpp)
int result;
+#if defined(DHCPv6) && defined(DHCP4o6)
+ if (dhcpv4_over_dhcpv6) {
+ log_info ("DHCPDECLINE");
+ } else
+#endif
log_info ("DHCPDECLINE on %s to %s port %d",
client->name ? client->name : client->interface->name,
inet_ntoa(sockaddr_broadcast.sin_addr),
ntohs(sockaddr_broadcast.sin_port));
/* Send out a packet. */
+#if defined(DHCPv6) && defined(DHCP4o6)
+ if (dhcpv4_over_dhcpv6) {
+ result = send_dhcpv4_query(client, 1);
+ } else
+#endif
result = send_packet(client->interface, NULL, &client->packet,
client->packet_length, inaddr_any,
&sockaddr_broadcast, NULL);
if (result < 0) {
+#if defined(DHCPv6) && defined(DHCP4o6)
+ if (dhcpv4_over_dhcpv6) {
+ log_error("%s:%d: Failed to send %d byte long packet.",
+ MDL, client->packet_length);
+ } else
+#endif
log_error("%s:%d: Failed to send %d byte long packet over %s"
" interface.", MDL, client->packet_length,
client->interface->name);
@@ -2363,11 +2683,28 @@ void send_release (cpp)
return;
}
+#if defined(DHCPv6) && defined(DHCP4o6)
+ if (dhcpv4_over_dhcpv6) {
+ log_info ("DHCPRELEASE");
+ } else
+#endif
log_info ("DHCPRELEASE on %s to %s port %d",
client -> name ? client -> name : client -> interface -> name,
inet_ntoa (destination.sin_addr),
ntohs (destination.sin_port));
+#if defined(DHCPv6) && defined(DHCP4o6)
+ if (dhcpv4_over_dhcpv6) {
+ int broadcast = 0;
+ if (destination.sin_addr.s_addr == INADDR_BROADCAST)
+ broadcast = 1;
+ result = send_dhcpv4_query(client, broadcast);
+ if (result < 0) {
+ log_error("%s:%d: Failed to send %d byte long packet.",
+ MDL, client->packet_length);
+ }
+ } else
+#endif
if (fallback_interface) {
result = send_packet(fallback_interface, NULL, &client->packet,
client->packet_length, from, &destination,
@@ -2393,6 +2730,151 @@ void send_release (cpp)
}
}
+#if defined(DHCPv6) && defined(DHCP4o6)
+/*
+ * \brief Send a DHCPv4-query to the DHCPv6 client
+ * (DHCPv4 client function)
+ *
+ * The DHCPv4 client sends a DHCPv4-query to the DHCPv6 client over
+ * the inter-process communication socket.
+ *
+ * \param client the DHCPv4 client state
+ * \param broadcast the broadcast flag
+ * \return the sent byte count (-1 on error)
+ */
+static int send_dhcpv4_query(struct client_state *client, int broadcast) {
+ struct data_string ds;
+ struct dhcpv4_over_dhcpv6_packet *query;
+ int ofs, len, cc;
+
+ if (dhcp4o6_state <= 0) {
+ log_info("send_dhcpv4_query: not ready.");
+ return -1;
+ }
+
+ /*
+ * Compute buffer length and allocate it.
+ */
+ len = ofs = (int)(offsetof(struct dhcpv4_over_dhcpv6_packet, options));
+ len += dhcpv6_universe.tag_size + dhcpv6_universe.length_size;
+ len += client->packet_length;
+ memset(&ds, 0, sizeof(ds));
+ if (!buffer_allocate(&ds.buffer, len, MDL)) {
+ log_error("Unable to allocate memory for DHCPv4-query.");
+ return -1;
+ }
+ ds.data = ds.buffer->data;
+ ds.len = len;
+
+ /*
+ * Fill header.
+ */
+ query = (struct dhcpv4_over_dhcpv6_packet *)ds.data;
+ query->msg_type = DHCPV6_DHCPV4_QUERY;
+ query->flags[0] = query->flags[1] = query->flags[2] = 0;
+ if (!broadcast)
+ query->flags[0] |= DHCP4O6_QUERY_UNICAST;
+
+ /*
+ * Append DHCPv4 message.
+ */
+ dhcpv6_universe.store_tag(ds.buffer->data + ofs, D6O_DHCPV4_MSG);
+ ofs += dhcpv6_universe.tag_size;
+ dhcpv6_universe.store_length(ds.buffer->data + ofs,
+ client->packet_length);
+ ofs += dhcpv6_universe.length_size;
+ memcpy(ds.buffer->data + ofs, &client->packet, client->packet_length);
+
+ /*
+ * Send DHCPv6 message.
+ */
+ cc = send(dhcp4o6_fd, ds.data, ds.len, 0);
+ if (cc < 0)
+ log_error("send_dhcpv4_query: send(): %m");
+
+ data_string_forget(&ds, MDL);
+
+ return cc;
+}
+
+/*
+ * \brief Forward a DHCPv4-query to all DHCPv4 over DHCPv6 server addresses.
+ * (DHCPv6 client function)
+ *
+ * \param raw the DHCPv6 DHCPv4-query message raw content
+ */
+static void forw_dhcpv4_query(struct data_string *raw) {
+ struct interface_info *ip;
+ struct client_state *client;
+ struct dhc6_lease *lease;
+ struct option_cache *oc;
+ struct data_string addrs;
+ struct sockaddr_in6 sin6;
+ int i, send_ret, attempt, success;
+
+ attempt = success = 0;
+ memset(&sin6, 0, sizeof(sin6));
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_port = remote_port;
+#ifdef HAVE_SA_LEN
+ sin6.sin6_len = sizeof(sin6);
+#endif
+ memset(&addrs, 0, sizeof(addrs));
+ for (ip = interfaces; ip != NULL; ip = ip->next) {
+ for (client = ip->client; client != NULL;
+ client = client->next) {
+ if ((client->state != S_BOUND) &&
+ (client->state != S_RENEWING) &&
+ (client->state != S_REBINDING))
+ continue;
+ lease = client->active_lease;
+ if ((lease == NULL) || lease->released)
+ continue;
+ oc = lookup_option(&dhcpv6_universe,
+ lease->options,
+ D6O_DHCP4_O_DHCP6_SERVER);
+ if ((oc == NULL) ||
+ !evaluate_option_cache(&addrs, NULL, NULL, NULL,
+ lease->options, NULL,
+ &global_scope, oc, MDL) ||
+ ((addrs.len % sizeof(sin6.sin6_addr)) != 0)) {
+ data_string_forget(&addrs, MDL);
+ continue;
+ }
+ if (addrs.len == 0) {
+ /* note there is nothing to forget */
+ inet_pton(AF_INET6,
+ All_DHCP_Relay_Agents_and_Servers,
+ &sin6.sin6_addr);
+ attempt++;
+ send_ret = send_packet6(ip, raw->data,
+ raw->len, &sin6);
+ if (send_ret == raw->len)
+ success++;
+ continue;
+ }
+ for (i = 0; i < addrs.len;
+ i += sizeof(sin6.sin6_addr)) {
+ memcpy(&sin6.sin6_addr, addrs.data + i,
+ sizeof(sin6.sin6_addr));
+ attempt++;
+ send_ret = send_packet6(ip, raw->data,
+ raw->len, &sin6);
+ if (send_ret == raw->len)
+ success++;
+ }
+ data_string_forget(&addrs, MDL);
+ }
+ }
+
+ log_info("forw_dhcpv4_query: sent(%d): %d/%d",
+ raw->len, success, attempt);
+
+ if (attempt == 0)
+ dhcp4o6_stop();
+}
+#endif
+
void
make_client_options(struct client_state *client, struct client_lease *lease,
u_int8_t *type, struct option_cache *sid,
@@ -2579,6 +3061,7 @@ void make_discover (client, lease)
client -> packet.op = BOOTREQUEST;
client -> packet.htype = client -> interface -> hw_address.hbuf [0];
+ /* Assumes hw_address is known, otherwise a random value may result */
client -> packet.hlen = client -> interface -> hw_address.hlen - 1;
client -> packet.hops = 0;
client -> packet.xid = random ();
@@ -2652,6 +3135,7 @@ void make_request (client, lease)
client -> packet.op = BOOTREQUEST;
client -> packet.htype = client -> interface -> hw_address.hbuf [0];
+ /* Assumes hw_address is known, otherwise a random value may result */
client -> packet.hlen = client -> interface -> hw_address.hlen - 1;
client -> packet.hops = 0;
client -> packet.xid = client -> xid;
@@ -2726,6 +3210,7 @@ void make_decline (client, lease)
client -> packet.op = BOOTREQUEST;
client -> packet.htype = client -> interface -> hw_address.hbuf [0];
+ /* Assumes hw_address is known, otherwise a random value may result */
client -> packet.hlen = client -> interface -> hw_address.hlen - 1;
client -> packet.hops = 0;
client -> packet.xid = client -> xid;
@@ -2787,6 +3272,7 @@ void make_release (client, lease)
client -> packet.op = BOOTREQUEST;
client -> packet.htype = client -> interface -> hw_address.hbuf [0];
+ /* Assumes hw_address is known, otherwise a random value may result */
client -> packet.hlen = client -> interface -> hw_address.hlen - 1;
client -> packet.hops = 0;
client -> packet.xid = random ();
@@ -3796,6 +4282,15 @@ void do_release(client)
struct data_string ds;
struct option_cache *oc;
+#if defined(DHCPv6) && defined(DHCP4o6)
+ if (dhcpv4_over_dhcpv6 && (dhcp4o6_state <= 0)) {
+ if (dhcp4o6_state < 0)
+ dhcp4o6_poll(NULL);
+ client->pending = P_RELEASE;
+ return;
+ }
+#endif
+
/* Pick a random xid. */
client -> xid = random ();
@@ -3852,6 +4347,11 @@ void do_release(client)
cancel_timeout (send_request, client);
cancel_timeout (state_reboot, client);
client -> state = S_STOPPED;
+
+#if defined(DHCPv6) && defined(DHCP4o6)
+ if (dhcpv4_over_dhcpv6)
+ exit(0);
+#endif
}
int dhclient_interface_shutdown_hook (struct interface_info *interface)
@@ -4647,3 +5147,237 @@ dhclient_ddns_cb_free(dhcp_ddns_cb_t *ddns_cb, char* file, int line) {
ddns_cb_free(ddns_cb, file, line);
}
}
+
+#if defined(DHCPv6) && defined(DHCP4o6)
+/*
+ * \brief Omapi I/O handler
+ *
+ * The inter-process communication receive handler.
+ *
+ * On the DHCPv6 side, the message is either a POLL (which is answered
+ * by a START or a STOP) or a DHCPv4-QUERY (which is forwarded to
+ * DHCPv4 over DHCPv6 servers by forw_dhcpv4_query()).
+ *
+ * On the DHCPv4 side, the message is either a START, a STOP
+ * (both for the DHCP4 over DHCPv6 state machine) or a DHCPv4-RESPONSE
+ * (which is processed by recv_dhcpv4_response()).
+ *
+ * \param h the OMAPI object
+ * \return a result for I/O success or error (used by the I/O subsystem)
+ */
+isc_result_t dhcpv4o6_handler(omapi_object_t *h) {
+ char buf[65536];
+ char start_msg[5] = { 'S', 'T', 'A', 'R', 'T' };
+ char stop_msg[4] = { 'S', 'T', 'O', 'P' };
+ char poll_msg[4] = { 'P', 'O', 'L', 'L' };
+ struct data_string raw;
+ int cc;
+
+ if (h->type != dhcp4o6_type)
+ return DHCP_R_INVALIDARG;
+
+ cc = recv(dhcp4o6_fd, buf, sizeof(buf), 0);
+ if (cc <= 0)
+ return ISC_R_UNEXPECTED;
+
+ if (local_family == AF_INET6) {
+ if ((cc == 4) &&
+ (memcmp(buf, poll_msg, sizeof(poll_msg)) == 0)) {
+ log_info("RCV: POLL");
+ if (dhcp4o6_state < 0)
+ cc = send(dhcp4o6_fd, stop_msg,
+ sizeof(stop_msg), 0);
+ else
+ cc = send(dhcp4o6_fd, start_msg,
+ sizeof(start_msg), 0);
+ if (cc < 0) {
+ log_error("dhcpv4o6_handler: send(): %m");
+ return ISC_R_IOERROR;
+ }
+ } else {
+ if (cc < DHCP_FIXED_NON_UDP + 8)
+ return ISC_R_UNEXPECTED;
+ memset(&raw, 0, sizeof(raw));
+ if (!buffer_allocate(&raw.buffer, cc, MDL)) {
+ log_error("dhcpv4o6_handler: "
+ "no memory buffer.");
+ return ISC_R_NOMEMORY;
+ }
+ raw.data = raw.buffer->data;
+ raw.len = cc;
+ memcpy(raw.buffer->data, buf, cc);
+
+ forw_dhcpv4_query(&raw);
+
+ data_string_forget(&raw, MDL);
+ }
+ } else {
+ if ((cc == 4) &&
+ (memcmp(buf, stop_msg, sizeof(stop_msg)) == 0)) {
+ log_info("RCV: STOP");
+ if (dhcp4o6_state > 0) {
+ dhcp4o6_state = 0;
+ dhcp4o6_poll(NULL);
+ }
+ } else if ((cc == 5) &&
+ (memcmp(buf, start_msg, sizeof(start_msg)) == 0)) {
+ log_info("RCV: START");
+ if (dhcp4o6_state == 0)
+ cancel_timeout(dhcp4o6_poll, NULL);
+ dhcp4o6_state = 1;
+ dhcp4o6_resume();
+ } else {
+ if (cc < DHCP_FIXED_NON_UDP + 16)
+ return ISC_R_UNEXPECTED;
+ memset(&raw, 0, sizeof(raw));
+ if (!buffer_allocate(&raw.buffer, cc, MDL)) {
+ log_error("dhcpv4o6_handler: "
+ "no memory buffer.");
+ return ISC_R_NOMEMORY;
+ }
+ raw.data = raw.buffer->data;
+ raw.len = cc;
+ memcpy(raw.buffer->data, buf, cc);
+
+ recv_dhcpv4_response(&raw);
+
+ data_string_forget(&raw, MDL);
+ }
+ }
+
+ return ISC_R_SUCCESS;
+}
+
+/*
+ * \brief Poll the DHCPv6 client
+ * (DHCPv4 client function)
+ *
+ * A POLL message is sent to the DHCPv6 client periodically to check
+ * if the DHCPv6 is ready (i.e., has a valid DHCPv4-over-DHCPv6 server
+ * address option).
+ */
+static void dhcp4o6_poll(void *dummy) {
+ char msg[4] = { 'P', 'O', 'L', 'L' };
+ struct timeval tv;
+ int cc;
+
+ IGNORE_UNUSED(dummy);
+
+ if (dhcp4o6_state < 0)
+ dhcp4o6_state = 0;
+
+ log_info("POLL");
+
+ cc = send(dhcp4o6_fd, msg, sizeof(msg), 0);
+ if (cc < 0)
+ log_error("dhcp4o6_poll: send(): %m");
+
+ tv.tv_sec = cur_time + 60;
+ tv.tv_usec = random() % 1000000;
+
+ add_timeout(&tv, dhcp4o6_poll, NULL, 0, 0);
+}
+
+/*
+ * \brief Resume pending operations
+ * (DHCPv4 client function)
+ *
+ * A START message was received from the DHCPv6 client so pending
+ * operations (RELEASE or REBOOT) must be resumed.
+ */
+static void dhcp4o6_resume() {
+ struct interface_info *ip;
+ struct client_state *client;
+
+ for (ip = interfaces; ip != NULL; ip = ip->next) {
+ for (client = ip->client; client != NULL;
+ client = client->next) {
+ if (client->pending == P_RELEASE)
+ do_release(client);
+ else if (client->pending == P_REBOOT)
+ state_reboot(client);
+ }
+ }
+}
+
+/*
+ * \brief Send a START to the DHCPv4 client
+ * (DHCPv6 client function)
+ *
+ * First check if there is a valid DHCPv4-over-DHCPv6 server address option,
+ * and when found go UP and on a transition from another state send
+ * a START message to the DHCPv4 client.
+ */
+void dhcp4o6_start() {
+ struct interface_info *ip;
+ struct client_state *client;
+ struct dhc6_lease *lease;
+ struct option_cache *oc;
+ struct data_string addrs;
+ char msg[5] = { 'S', 'T', 'A', 'R', 'T' };
+ int cc;
+
+ memset(&addrs, 0, sizeof(addrs));
+ for (ip = interfaces; ip != NULL; ip = ip->next) {
+ for (client = ip->client; client != NULL;
+ client = client->next) {
+ if ((client->state != S_BOUND) &&
+ (client->state != S_RENEWING) &&
+ (client->state != S_REBINDING))
+ continue;
+ lease = client->active_lease;
+ if ((lease == NULL) || lease->released)
+ continue;
+ oc = lookup_option(&dhcpv6_universe,
+ lease->options,
+ D6O_DHCP4_O_DHCP6_SERVER);
+ if ((oc == NULL) ||
+ !evaluate_option_cache(&addrs, NULL, NULL, NULL,
+ lease->options, NULL,
+ &global_scope, oc, MDL))
+ continue;
+ if ((addrs.len % 16) != 0) {
+ data_string_forget(&addrs, MDL);
+ continue;
+ }
+ data_string_forget(&addrs, MDL);
+ goto found;
+ }
+ }
+ log_info("dhcp4o6_start: failed");
+ dhcp4o6_stop();
+ return;
+
+found:
+ if (dhcp4o6_state == 1)
+ return;
+ log_info("dhcp4o6_start: go to UP");
+ dhcp4o6_state = 1;
+
+ cc = send(dhcp4o6_fd, msg, sizeof(msg), 0);
+ if (cc < 0)
+ log_info("dhcp4o6_start: send(): %m");
+}
+
+/*
+ * Send a STOP to the DHCPv4 client
+ * (DHCPv6 client function)
+ *
+ * Go DOWN and on a transition from another state send a STOP message
+ * to the DHCPv4 client.
+ */
+static void dhcp4o6_stop() {
+ char msg[4] = { 'S', 'T', 'O', 'P' };
+ int cc;
+
+ if (dhcp4o6_state == -1)
+ return;
+
+ log_info("dhcp4o6_stop: go to DOWN");
+ dhcp4o6_state = -1;
+
+ cc = send(dhcp4o6_fd, msg, sizeof(msg), 0);
+ if (cc < 0)
+ log_error("dhcp4o6_stop: send(): %m");
+}
+#endif /* DHCPv6 && DHCP4o6 */
diff --git a/common/Makefile.am b/common/Makefile.am
index c579719a..113aee84 100644
--- a/common/Makefile.am
+++ b/common/Makefile.am
@@ -2,11 +2,11 @@ AM_CPPFLAGS = -I$(top_srcdir) -DLOCALSTATEDIR='"@localstatedir@"'
AM_CFLAGS = $(LDAP_CFLAGS)
noinst_LIBRARIES = libdhcp.a
-libdhcp_a_SOURCES = alloc.c bpf.c comapi.c conflex.c ctrace.c discover.c \
- dispatch.c dlpi.c dns.c ethernet.c execute.c fddi.c \
- icmp.c inet.c lpf.c memory.c nit.c ns_name.c options.c \
- packet.c parse.c print.c raw.c resolv.c socket.c \
- tables.c tr.c tree.c upf.c
+libdhcp_a_SOURCES = alloc.c bpf.c comapi.c conflex.c ctrace.c dhcp4o6.c \
+ discover.c dispatch.c dlpi.c dns.c ethernet.c execute.c \
+ fddi.c icmp.c inet.c lpf.c memory.c nit.c ns_name.c \
+ options.c packet.c parse.c print.c raw.c resolv.c \
+ socket.c tables.c tr.c tree.c upf.c
man_MANS = dhcp-eval.5 dhcp-options.5
EXTRA_DIST = $(man_MANS)
diff --git a/common/Makefile.in b/common/Makefile.in
index a8d3c651..e78e4f7c 100644
--- a/common/Makefile.in
+++ b/common/Makefile.in
@@ -107,14 +107,15 @@ am__v_AR_1 =
libdhcp_a_AR = $(AR) $(ARFLAGS)
libdhcp_a_LIBADD =
am_libdhcp_a_OBJECTS = alloc.$(OBJEXT) bpf.$(OBJEXT) comapi.$(OBJEXT) \
- conflex.$(OBJEXT) ctrace.$(OBJEXT) discover.$(OBJEXT) \
- dispatch.$(OBJEXT) dlpi.$(OBJEXT) dns.$(OBJEXT) \
- ethernet.$(OBJEXT) execute.$(OBJEXT) fddi.$(OBJEXT) \
- icmp.$(OBJEXT) inet.$(OBJEXT) lpf.$(OBJEXT) memory.$(OBJEXT) \
- nit.$(OBJEXT) ns_name.$(OBJEXT) options.$(OBJEXT) \
- packet.$(OBJEXT) parse.$(OBJEXT) print.$(OBJEXT) raw.$(OBJEXT) \
- resolv.$(OBJEXT) socket.$(OBJEXT) tables.$(OBJEXT) \
- tr.$(OBJEXT) tree.$(OBJEXT) upf.$(OBJEXT)
+ conflex.$(OBJEXT) ctrace.$(OBJEXT) dhcp4o6.$(OBJEXT) \
+ discover.$(OBJEXT) dispatch.$(OBJEXT) dlpi.$(OBJEXT) \
+ dns.$(OBJEXT) ethernet.$(OBJEXT) execute.$(OBJEXT) \
+ fddi.$(OBJEXT) icmp.$(OBJEXT) inet.$(OBJEXT) lpf.$(OBJEXT) \
+ memory.$(OBJEXT) nit.$(OBJEXT) ns_name.$(OBJEXT) \
+ options.$(OBJEXT) packet.$(OBJEXT) parse.$(OBJEXT) \
+ print.$(OBJEXT) raw.$(OBJEXT) resolv.$(OBJEXT) \
+ socket.$(OBJEXT) tables.$(OBJEXT) tr.$(OBJEXT) tree.$(OBJEXT) \
+ upf.$(OBJEXT)
libdhcp_a_OBJECTS = $(am_libdhcp_a_OBJECTS)
AM_V_P = $(am__v_P_@AM_V@)
am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
@@ -358,11 +359,11 @@ top_srcdir = @top_srcdir@
AM_CPPFLAGS = -I$(top_srcdir) -DLOCALSTATEDIR='"@localstatedir@"'
AM_CFLAGS = $(LDAP_CFLAGS)
noinst_LIBRARIES = libdhcp.a
-libdhcp_a_SOURCES = alloc.c bpf.c comapi.c conflex.c ctrace.c discover.c \
- dispatch.c dlpi.c dns.c ethernet.c execute.c fddi.c \
- icmp.c inet.c lpf.c memory.c nit.c ns_name.c options.c \
- packet.c parse.c print.c raw.c resolv.c socket.c \
- tables.c tr.c tree.c upf.c
+libdhcp_a_SOURCES = alloc.c bpf.c comapi.c conflex.c ctrace.c dhcp4o6.c \
+ discover.c dispatch.c dlpi.c dns.c ethernet.c execute.c \
+ fddi.c icmp.c inet.c lpf.c memory.c nit.c ns_name.c \
+ options.c packet.c parse.c print.c raw.c resolv.c \
+ socket.c tables.c tr.c tree.c upf.c
man_MANS = dhcp-eval.5 dhcp-options.5
EXTRA_DIST = $(man_MANS)
@@ -425,6 +426,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/comapi.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/conflex.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ctrace.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcp4o6.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/discover.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dispatch.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dlpi.Po@am__quote@
diff --git a/common/discover.c b/common/discover.c
index 3cd64a75..b5f297e7 100644
--- a/common/discover.c
+++ b/common/discover.c
@@ -3,7 +3,7 @@
Find and identify the network interfaces. */
/*
- * Copyright (c) 2013-2014 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2013-2014,2016 by Internet Systems Consortium, Inc. ("ISC")
* Copyright (c) 2004-2009,2011 by Internet Systems Consortium, Inc. ("ISC")
* Copyright (c) 1995-2003 by Internet Software Consortium
*
@@ -45,6 +45,7 @@ int interfaces_invalidated;
int quiet_interface_discovery;
u_int16_t local_port;
u_int16_t remote_port;
+int dhcpv4_over_dhcpv6 = 0;
int (*dhcp_interface_setup_hook) (struct interface_info *, struct iaddr *);
int (*dhcp_interface_discovery_hook) (struct interface_info *);
isc_result_t (*dhcp_interface_startup_hook) (struct interface_info *);
@@ -1002,7 +1003,8 @@ discover_interfaces(int state) {
/* We don't want the loopback interface. */
if (a->sin_addr.s_addr == htonl(INADDR_LOOPBACK) &&
((tmp->flags & INTERFACE_AUTOMATIC) &&
- state == DISCOVER_SERVER))
+ ((state == DISCOVER_SERVER) ||
+ (state == DISCOVER_SERVER46))))
continue;
/* If the only address we have is 0.0.0.0, we
@@ -1029,7 +1031,8 @@ discover_interfaces(int state) {
/* We don't want the loopback interface. */
if (IN6_IS_ADDR_LOOPBACK(&a->sin6_addr) &&
((tmp->flags & INTERFACE_AUTOMATIC) &&
- state == DISCOVER_SERVER))
+ ((state == DISCOVER_SERVER) ||
+ (state == DISCOVER_SERVER46))))
continue;
/* If the only address we have is 0.0.0.0, we
@@ -1226,31 +1229,48 @@ discover_interfaces(int state) {
tmp -> index = -1;
/* Register the interface... */
- if (local_family == AF_INET) {
- if_register_receive(tmp);
- if_register_send(tmp);
+ switch (local_family) {
+ case AF_INET:
+ if (!dhcpv4_over_dhcpv6) {
+ if_register_receive(tmp);
+ if_register_send(tmp);
+ } else {
+ /* get_hw_addr() was called by register. */
+ get_hw_addr(tmp->name, &tmp->hw_address);
+ }
+ break;
#ifdef DHCPv6
- } else {
+ case AF_INET6:
if ((state == DISCOVER_SERVER) ||
(state == DISCOVER_RELAY)) {
if_register6(tmp, 1);
+ } else if (state == DISCOVER_SERVER46) {
+ /* get_hw_addr() was called by if_register*6
+ so now we have to call it explicitly
+ to not leave the hardware address unknown
+ (some code expects it cannot be. */
+ get_hw_addr(tmp->name, &tmp->hw_address);
} else {
if_register_linklocal6(tmp);
}
+ break;
#endif /* DHCPv6 */
}
interface_stash (tmp);
wifcount++;
#if defined (F_SETFD)
- if (fcntl (tmp -> rfdesc, F_SETFD, 1) < 0)
+ /* if_register*() are no longer always called so
+ descriptors must be checked. */
+ if ((tmp -> rfdesc >= 0) &&
+ (fcntl (tmp -> rfdesc, F_SETFD, 1) < 0))
+ log_error ("Can't set close-on-exec on %s: %m",
+ tmp -> name);
+ if ((tmp -> wfdesc != tmp -> rfdesc) &&
+ (tmp -> wfdesc >= 0) &&
+ (fcntl (tmp -> wfdesc, F_SETFD, 1) < 0))
log_error ("Can't set close-on-exec on %s: %m",
tmp -> name);
- if (tmp -> rfdesc != tmp -> wfdesc) {
- if (fcntl (tmp -> wfdesc, F_SETFD, 1) < 0)
- log_error ("Can't set close-on-exec on %s: %m",
- tmp -> name);
- }
#endif
next:
interface_dereference (&tmp, MDL);
@@ -1308,7 +1328,8 @@ discover_interfaces(int state) {
log_fatal ("Not configured to listen on any interfaces!");
}
- if ((local_family == AF_INET) && !setup_fallback) {
+ if ((local_family == AF_INET) &&
+ !setup_fallback && !dhcpv4_over_dhcpv6) {
setup_fallback = 1;
maybe_setup_fallback();
}
diff --git a/common/inet.c b/common/inet.c
index 0cff19d0..52852d83 100644
--- a/common/inet.c
+++ b/common/inet.c
@@ -4,7 +4,7 @@
way... */
/*
- * Copyright (c) 2011,2013,2014 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2011,2013,2014,2016 by Internet Systems Consortium, Inc. ("ISC")
* Copyright (c) 2007-2009 by Internet Systems Consortium, Inc. ("ISC")
* Copyright (c) 2004,2005 by Internet Systems Consortium, Inc. ("ISC")
* Copyright (c) 1995-2003 by Internet Software Consortium
@@ -622,3 +622,29 @@ validate_port(char *port) {
return htons((u_int16_t)local_port);
}
+
+/* \brief Validate that the string represents a valid port pair (i.e. n,n+1)
+ *
+ * \param the string to validate
+ * \return the first port number in network byte order
+ */
+
+u_int16_t
+validate_port_pair(char *port) {
+ long local_port = 0;
+ long lower = 1;
+ long upper = 65534;
+ char *endptr;
+
+ errno = 0;
+ local_port = strtol(port, &endptr, 10);
+
+ if ((*endptr != '\0') || (errno == ERANGE) || (errno == EINVAL))
+ log_fatal ("Invalid port pair specification: %s", port);
+
+ if (local_port < lower || local_port > upper)
+ log_fatal("Port pair specified is out of range (%ld-%ld).",
+ lower, upper);
+
+ return htons((u_int16_t)local_port);
+}
diff --git a/common/options.c b/common/options.c
index 5abccf89..50271f22 100644
--- a/common/options.c
+++ b/common/options.c
@@ -3955,6 +3955,9 @@ do_packet6(struct interface_info *interface, const char *packet,
unsigned char msg_type;
const struct dhcpv6_packet *msg;
const struct dhcpv6_relay_packet *relay;
+#ifdef DHCP4o6
+ const struct dhcpv4_over_dhcpv6_packet *msg46;
+#endif
struct packet *decoded_packet;
#if defined (DEBUG_MEMORY_LEAKAGE)
unsigned long previous_outstanding = dmalloc_outstanding;
@@ -4016,6 +4019,28 @@ do_packet6(struct interface_info *interface, const char *packet,
packet_dereference(&decoded_packet, MDL);
return;
}
+#ifdef DHCP4o6
+ } else if ((msg_type == DHCPV6_DHCPV4_QUERY) ||
+ (msg_type == DHCPV6_DHCPV4_RESPONSE)) {
+ int msglen =
+ (int)(offsetof(struct dhcpv4_over_dhcpv6_packet, options));
+ msg46 = (struct dhcpv4_over_dhcpv6_packet *)packet;
+ decoded_packet->dhcpv6_msg_type = msg46->msg_type;
+
+ /* message-specific data */
+ memcpy(decoded_packet->dhcp4o6_flags,
+ msg46->flags,
+ sizeof(decoded_packet->dhcp4o6_flags));
+
+ if (!parse_option_buffer(decoded_packet->options,
+ msg46->options, len - msglen,
+ &dhcpv6_universe)) {
+ /* no logging here, as parse_option_buffer() logs all
+ cases where it fails */
+ packet_dereference(&decoded_packet, MDL);
+ return;
+ }
+#endif
} else {
int msglen = (int)(offsetof(struct dhcpv6_packet, options));
msg = (const struct dhcpv6_packet *)packet;
diff --git a/common/tables.c b/common/tables.c
index 1c9360cb..7617b7ef 100644
--- a/common/tables.c
+++ b/common/tables.c
@@ -3,7 +3,7 @@
Tables of information... */
/*
- * Copyright (c) 2011-2014 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2011-2014, 2016 by Internet Systems Consortium, Inc. ("ISC")
* Copyright (c) 2004-2009 by Internet Systems Consortium, Inc. ("ISC")
* Copyright (c) 1995-2003 by Internet Software Consortium
*
@@ -565,13 +565,20 @@ static struct option dhcpv6_options[] = {
{ "ipv6-address-andsf", "6A", &dhcpv6_universe, 143, 1 },
#endif
+ /* RFC7341 OPTIONS */
+#if defined(RFC7341_OPTIONS)
+ { "dhcpv4-msg", "X", &dhcpv6_universe, 87, 1 },
+ { "dhcp4-o-dhcp6-server", "6A", &dhcpv6_universe, 88, 1 },
+#endif
+
{ NULL, NULL, NULL, 0, 0 }
};
struct enumeration_value dhcpv6_duid_type_values[] = {
- { "duid-llt", DUID_LLT }, /* Link-Local Plus Time */
- { "duid-en", DUID_EN }, /* DUID based upon enterprise-ID. */
- { "duid-ll", DUID_LL }, /* DUID from Link Local address only. */
+ { "duid-llt", DUID_LLT }, /* Link-Local Plus Time */
+ { "duid-en", DUID_EN }, /* DUID based upon enterprise-ID. */
+ { "duid-ll", DUID_LL }, /* DUID from Link Local address only. */
+ { "duid-uuid", DUID_UUID }, /* DUID based upon UUID */
{ NULL, 0 }
};
@@ -593,6 +600,7 @@ struct enumeration_value dhcpv6_status_code_values[] = {
{ "MalformedQuery", 8 }, /* Leasequery not valid. */
{ "NotConfigured", 9 }, /* The target address is not in config. */
{ "NotAllowed", 10 }, /* Server doesn't allow the leasequery. */
+ { "QueryTerminated", 11 }, /* Leasequery terminated. */
{ NULL, 0 }
};
@@ -605,6 +613,9 @@ struct enumeration dhcpv6_status_codes = {
struct enumeration_value lq6_query_type_values[] = {
{ "query-by-address", 1 },
{ "query-by-clientid", 2 },
+ { "query-by-relay-id", 3 },
+ { "query-by-link-address", 4 },
+ { "query-by-remote-id", 5 },
{ NULL, 0 }
};
@@ -630,6 +641,12 @@ struct enumeration_value dhcpv6_message_values[] = {
{ "RELAY-REPL", 13 },
{ "LEASEQUERY", 14 },
{ "LEASEQUERY-REPLY", 15 },
+ { "LEASEQUERY-DONE", 16 },
+ { "LEASEQUERY-DATA", 17 },
+ { "RECONFIGURE-REQUEST", 18 },
+ { "RECONFIGURE-REPLY", 19 },
+ { "DHCPV4-QUERY", 20 },
+ { "DHCPV4-RESPONSE", 21 },
{ NULL, 0 }
};
@@ -650,7 +667,13 @@ const char *dhcpv6_type_names[] = {
"Relay-forward",
"Relay-reply",
"Leasequery",
- "Leasequery-reply"
+ "Leasequery-reply",
+ "Leasequery-done",
+ "Leasequery-data",
+ "Reconfigure-request",
+ "Reconfigure-reply",
+ "Dhcpv4-query",
+ "Dhcpv4-response"
};
const int dhcpv6_type_name_max =
(sizeof(dhcpv6_type_names) / sizeof(dhcpv6_type_names[0]));
@@ -670,7 +693,9 @@ static struct option vsio_options[] = {
struct universe isc6_universe;
static struct option isc6_options[] = {
{ "media", "t", &isc6_universe, 1, 1 },
- { "update-assist", "X", &isc6_universe, 2, 1 },
+ { "update-assist", "X", &isc6_universe, 2, 1 },
+ { "4o6-interface", "t", &isc6_universe, 60000, 1 },
+ { "4o6-source-address", "6", &isc6_universe, 60001, 1 },
{ NULL, NULL, NULL, 0, 0 }
};
diff --git a/configure b/configure
index edf0de5a..5c980452 100755
--- a/configure
+++ b/configure
@@ -753,6 +753,7 @@ enable_execute
enable_tracing
enable_delayed_ack
enable_dhcpv6
+enable_dhcpv4o6
enable_paranoia
enable_early_chroot
enable_ipv4_pktinfo
@@ -1423,6 +1424,8 @@ Optional Features:
is yes)
--enable-delayed-ack queues multiple DHCPACK replies (default is no)
--enable-dhcpv6 enable support for DHCPv6 (default is yes)
+ --enable-dhcpv4o6 enable support for DHCPv4-over-DHCPv6 (default is
+ no)
--enable-paranoia enable support for chroot/setuid (default is no)
--enable-early-chroot enable chrooting prior to configuration (default is
no)
@@ -5430,6 +5433,27 @@ $as_echo "#define DHCPv6 1" >>confdefs.h
fi
+# DHCPv4o6 optional compile-time feature.
+# Check whether --enable-dhcpv4o6 was given.
+if test "${enable_dhcpv4o6+set}" = set; then :
+ enableval=$enable_dhcpv4o6;
+fi
+
+# DHCPv4o6 is off by default, so define if it is explicitly enabled.
+if test "$enable_dhcpv4o6" = "yes"; then
+ # DHCPv4o6 requires DHCPv6
+ if test "$enable_dhcpv6" = "no"; then
+ as_fn_error $? "dhcpv4o6 requires dhcpv6" "$LINENO" 5
+ fi
+ # DHCPv4o6 is not yet compatible with delayed-ack
+ if test "$enable_delayed_ack" = "yes"; then
+ as_fn_error $? "dhcpv4o6 is not compatible with delayed-ack" "$LINENO" 5
+ fi
+
+$as_echo "#define DHCP4o6 1" >>confdefs.h
+
+fi
+
# PARANOIA is off by default (until we can test it with all features)
# Check whether --enable-paranoia was given.
if test "${enable_paranoia+set}" = set; then :
@@ -8641,6 +8665,14 @@ $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
fi
+if test "$enable_dhcpv4o6" = "yes"; then
+ DHCP_VERSIONS="DHCPv4, DHCPv6 and DHCPv4-over-DHCPv6"
+elif test "$enable_dhcpv6" != "no"; then
+ DHCP_VERSIONS="DHCPv4 and DHCPv6"
+else
+ DHCP_VERSIONS="DHCPv4"
+fi
+
(cd $srcdir
sh util/bindvar.sh
if test $? -ne 0; then
@@ -8663,6 +8695,8 @@ Flags:
DEFS: $DEFS
CFLAGS: $CFLAGS
+DHCP versions: $DHCP_VERSIONS
+
Features:
debug: $enable_debug
failover: $enable_failover
diff --git a/configure.ac b/configure.ac
index 9351150a..7f05b7d6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -149,6 +149,23 @@ if test "$enable_dhcpv6" != "no"; then
[Define to 1 to include DHCPv6 support.])
fi
+# DHCPv4o6 optional compile-time feature.
+AC_ARG_ENABLE(dhcpv4o6,
+ AS_HELP_STRING([--enable-dhcpv4o6],[enable support for DHCPv4-over-DHCPv6 (default is no)]))
+# DHCPv4o6 is off by default, so define if it is explicitly enabled.
+if test "$enable_dhcpv4o6" = "yes"; then
+ # DHCPv4o6 requires DHCPv6
+ if test "$enable_dhcpv6" = "no"; then
+ AC_MSG_ERROR([dhcpv4o6 requires dhcpv6])
+ fi
+ # DHCPv4o6 is not yet compatible with delayed-ack
+ if test "$enable_delayed_ack" = "yes"; then
+ AC_MSG_ERROR([dhcpv4o6 is not compatible with delayed-ack])
+ fi
+ AC_DEFINE([DHCP4o6], [1],
+ [Define to 1 to include DHCPv4 over DHCPv6 support.])
+fi
+
# PARANOIA is off by default (until we can test it with all features)
AC_ARG_ENABLE(paranoia,
AS_HELP_STRING([--enable-paranoia],[enable support for chroot/setuid (default is no)]))
@@ -799,6 +816,14 @@ AC_CONFIG_FILES([
])
AC_OUTPUT
+if test "$enable_dhcpv4o6" = "yes"; then
+ DHCP_VERSIONS="DHCPv4, DHCPv6 and DHCPv4-over-DHCPv6"
+elif test "$enable_dhcpv6" != "no"; then
+ DHCP_VERSIONS="DHCPv4 and DHCPv6"
+else
+ DHCP_VERSIONS="DHCPv4"
+fi
+
(cd $srcdir
sh util/bindvar.sh
if test $? -ne 0; then
@@ -821,6 +846,8 @@ Flags:
DEFS: $DEFS
CFLAGS: $CFLAGS
+DHCP versions: $DHCP_VERSIONS
+
Features:
debug: $enable_debug
failover: $enable_failover
diff --git a/includes/config.h.in b/includes/config.h.in
index 80f3b083..403a28f1 100644
--- a/includes/config.h.in
+++ b/includes/config.h.in
@@ -12,6 +12,9 @@
/* Define to queue multiple DHCPACK replies per fsync. */
#undef DELAYED_ACK
+/* Define to 1 to include DHCPv4 over DHCPv6 support. */
+#undef DHCP4o6
+
/* Define to BIG_ENDIAN for MSB (Motorola or SPARC CPUs) or LITTLE_ENDIAN for
LSB (Intel CPUs). */
#undef DHCP_BYTE_ORDER
diff --git a/includes/dhcp6.h b/includes/dhcp6.h
index 03fedfa8..4d7a9e34 100644
--- a/includes/dhcp6.h
+++ b/includes/dhcp6.h
@@ -76,10 +76,49 @@
#define D6O_CLT_TIME 46 /* RFC5007 */
#define D6O_LQ_RELAY_DATA 47 /* RFC5007 */
#define D6O_LQ_CLIENT_LINK 48 /* RFC5007 */
+#define D6O_MIP6_HNIDF 49 /* RFC6610 */
+#define D6O_MIP6_VDINF 50 /* RFC6610 */
+#define D6O_V6_LOST 51 /* RFC5223 */
+#define D6O_CAPWAP_AC_V6 52 /* RFC5417 */
+#define D6O_RELAY_ID 53 /* RFC5460 */
+#define D6O_IPV6_ADDRESS_MOS 54 /* RFC5678 */
+#define D6O_IPV6_FQDN_MOS 55 /* RFC5678 */
+#define D6O_NTP_SERVER 56 /* RFC5908 */
+#define D6O_V6_ACCESS_DOMAIN 57 /* RFC5986 */
+#define D6O_SIP_UA_CS_LIST 58 /* RFC6011 */
+#define D6O_BOOTFILE_URL 59 /* RFC5970 */
+#define D6O_BOOTFILE_PARAM 60 /* RFC5970 */
+#define D6O_CLIENT_ARCH_TYPE 61 /* RFC5970 */
+#define D6O_NII 62 /* RFC5970 */
+#define D6O_GEOLOCATION 63 /* RFC6225 */
+#define D6O_AFTR_NAME 64 /* RFC6334 */
+#define D6O_ERP_LOCAL_DOMAIN_NAME 65 /* RFC6440 */
+#define D6O_RSOO 66 /* RFC6422 */
+#define D6O_PD_EXCLUDE 67 /* RFC6603 */
+#define D6O_VSS 68 /* RFC6607 */
+#define D6O_MIP6_IDINF 69 /* RFC6610 */
+#define D6O_MIP6_UDINF 70 /* RFC6610 */
+#define D6O_MIP6_HNP 71 /* RFC6610 */
+#define D6O_MIP6_HAA 72 /* RFC6610 */
+#define D6O_MIP6_HAF 73 /* RFC6610 */
+#define D6O_RDNSS_SELECTION 74 /* RFC6731 */
+#define D6O_KRB_PRINCIPAL_NAME 75 /* RFC6784 */
+#define D6O_KRB_REALM_NAME 76 /* RFC6784 */
+#define D6O_KRB_DEFAULT_REALM_NAME 77 /* RFC6784 */
+#define D6O_KRB_KDC 78 /* RFC6784 */
#define D6O_CLIENT_LINKLAYER_ADDR 79 /* RFC6939 */
+#define D6O_LINK_ADDRESS 80 /* RFC6977 */
+#define D6O_RADIUS 81 /* RFC7037 */
+#define D6O_SOL_MAX_RT 82 /* RFC7083 */
+#define D6O_INF_MAX_RT 83 /* RFC7083 */
+#define D6O_ADDRSEL 84 /* RFC7078 */
+#define D6O_ADDRSEL_TABLE 85 /* RFC7078 */
+#define D6O_V6_PCP_SERVER 86 /* RFC7291 */
+#define D6O_DHCPV4_MSG 87 /* RFC7341 */
+#define D6O_DHCP4_O_DHCP6_SERVER 88 /* RFC7341 */
/*
- * Status Codes, from RFC 3315 section 24.4, and RFC 3633, 5007.
+ * Status Codes, from RFC 3315 section 24.4, and RFC 3633, 5007, 5460.
*/
#define STATUS_Success 0
#define STATUS_UnspecFail 1
@@ -92,6 +131,7 @@
#define STATUS_MalformedQuery 8
#define STATUS_NotConfigured 9
#define STATUS_NotAllowed 10
+#define STATUS_QueryTerminated 11
/*
* DHCPv6 message types, defined in section 5.3 of RFC 3315
@@ -109,8 +149,14 @@
#define DHCPV6_INFORMATION_REQUEST 11
#define DHCPV6_RELAY_FORW 12
#define DHCPV6_RELAY_REPL 13
-#define DHCPV6_LEASEQUERY 14
-#define DHCPV6_LEASEQUERY_REPLY 15
+#define DHCPV6_LEASEQUERY 14 /* RFC5007 */
+#define DHCPV6_LEASEQUERY_REPLY 15 /* RFC5007 */
+#define DHCPV6_LEASEQUERY_DONE 16 /* RFC5460 */
+#define DHCPV6_LEASEQUERY_DATA 17 /* RFC5460 */
+#define DHCPV6_RECONFIGURE_REQUEST 18 /* RFC6977 */
+#define DHCPV6_RECONFIGURE_REPLY 19 /* RFC6977 */
+#define DHCPV6_DHCPV4_QUERY 20 /* RFC7341 */
+#define DHCPV6_DHCPV4_RESPONSE 21 /* RFC7341 */
extern const char *dhcpv6_type_names[];
extern const int dhcpv6_type_name_max;
@@ -120,6 +166,7 @@ extern const int dhcpv6_type_name_max;
#define DUID_LLT 1
#define DUID_EN 2
#define DUID_LL 3
+#define DUID_UUID 4 /* RFC6355 */
/* Offsets into IA_*'s where Option spaces commence. */
#define IA_NA_OFFSET 12 /* IAID, T1, T2, all 4 octets each */
@@ -197,10 +244,27 @@ struct dhcpv6_relay_packet {
};
#define MAX_V6RELAY_HOPS 32
-/* Leasequery query-types (RFC 5007) */
+/*
+ * DHCPv4-over-DHCPv6 packet format, defined in RFC 4731
+ */
+struct dhcpv4_over_dhcpv6_packet {
+ unsigned char msg_type;
+ unsigned char flags[3];
+ unsigned char options[FLEXIBLE_ARRAY_MEMBER];
+};
+#define DHCP4O6_QUERY_UNICAST 128
+
+/* DHCPv4-over-DHCPv6 ISC vendor suboptions */
+#define D4O6_INTERFACE 60000
+#define D4O6_SRC_ADDRESS 60001
+
+/* Leasequery query-types (RFC 5007, 5460) */
#define LQ6QT_BY_ADDRESS 1
#define LQ6QT_BY_CLIENTID 2
+#define LQ6QT_BY_RELAY_ID 3
+#define LQ6QT_BY_LINK_ADDRESS 4
+#define LQ6QT_BY_REMOTE_ID 5
/*
* DUID time starts 2000-01-01.
diff --git a/includes/dhcpd.h b/includes/dhcpd.h
index bdd00c6d..4b3001a8 100644
--- a/includes/dhcpd.h
+++ b/includes/dhcpd.h
@@ -421,6 +421,12 @@ struct packet {
/* DHCPv6 packet containing this one, or NULL if none */
struct packet *dhcpv6_container_packet;
+ /* DHCPv4-over-DHCPv6 flags */
+ unsigned char dhcp4o6_flags[3];
+
+ /* DHCPv4-over-DHCPv6 response, or NULL */
+ struct data_string *dhcp4o6_response;
+
int options_valid;
int client_port;
struct iaddr client_addr;
@@ -684,7 +690,8 @@ struct lease_state {
#define DISCOVER_SERVER 1
#define DISCOVER_UNCONFIGURED 2
#define DISCOVER_RELAY 3
-#define DISCOVER_REQUESTED 4
+#define DISCOVER_SERVER46 4
+#define DISCOVER_REQUESTED 5
/* DDNS_UPDATE_STYLE enumerations. */
#define DDNS_UPDATE_STYLE_NONE 0
@@ -1167,6 +1174,13 @@ enum dhcp_state {
S_STOPPED = 8
};
+/* Possible pending client operations. */
+enum dhcp_pending {
+ P_NONE = 0,
+ P_REBOOT = 1,
+ P_RELEASE = 2
+};
+
/* Authentication and BOOTP policy possibilities (not all values work
for each). */
enum policy { P_IGNORE, P_ACCEPT, P_PREFER, P_REQUIRE, P_DONT };
@@ -1245,6 +1259,7 @@ struct client_state {
struct option_state *sent_options; /* Options we sent. */
enum dhcp_state state; /* Current state for this interface. */
TIME last_write; /* Last time this state was written. */
+ enum dhcp_pending pending; /* Current pending operation. */
/* DHCPv4 values. */
struct client_lease *active; /* Currently active lease. */
@@ -2029,6 +2044,17 @@ void parse_vendor_option(struct packet *packet,
struct option_state *out_options,
struct binding_scope **scope);
+/* dhcp4o6.c */
+#if defined(DHCP4o6)
+extern int dhcp4o6_fd;
+extern omapi_object_t *dhcp4o6_object;
+extern omapi_object_type_t *dhcp4o6_type;
+extern void dhcp4o6_setup(u_int16_t);
+
+/* dependency */
+extern isc_result_t dhcpv4o6_handler(omapi_object_t *);
+
+#endif
/* dhcpd.c */
extern struct timeval cur_tv;
#define cur_time cur_tv.tv_sec
@@ -2748,6 +2774,7 @@ extern struct in_addr local_address;
extern u_int16_t local_port;
extern u_int16_t remote_port;
+extern int dhcpv4_over_dhcpv6;
extern int (*dhcp_interface_setup_hook) (struct interface_info *,
struct iaddr *);
extern int (*dhcp_interface_discovery_hook) (struct interface_info *);
@@ -2858,6 +2885,7 @@ const char *piaddr (struct iaddr);
char *piaddrmask(struct iaddr *, struct iaddr *);
char *piaddrcidr(const struct iaddr *, unsigned int);
u_int16_t validate_port(char *);
+u_int16_t validate_port_pair(char *);
/* dhclient.c */
extern int nowait;
@@ -2951,6 +2979,8 @@ void dhcpv4_client_assignments(void);
void dhcpv6_client_assignments(void);
void form_duid(struct data_string *duid, const char *file, int line);
+void dhcp4o6_start(void);
+
/* dhc6.c */
void dhc6_lease_destroy(struct dhc6_lease **src, const char *file, int line);
void start_init6(struct client_state *client);
diff --git a/includes/site.h b/includes/site.h
index 0586717e..944dbefc 100644
--- a/includes/site.h
+++ b/includes/site.h
@@ -337,5 +337,6 @@
#define RFC6939_OPTIONS
#define RFC6977_OPTIONS
#define RFC7083_OPTIONS
+#define RFC7341_OPTIONS
#define RFC7618_OPTIONS
#define RFC7710_OPTIONS
diff --git a/relay/dhcrelay.c b/relay/dhcrelay.c
index e06eb4c2..9ec39cd1 100644
--- a/relay/dhcrelay.c
+++ b/relay/dhcrelay.c
@@ -1498,6 +1498,7 @@ process_up6(struct packet *packet, struct stream_list *dp) {
case DHCPV6_INFORMATION_REQUEST:
case DHCPV6_RELAY_FORW:
case DHCPV6_LEASEQUERY:
+ case DHCPV6_DHCPV4_QUERY:
log_info("Relaying %s from %s port %d going up.",
dhcpv6_type_names[packet->dhcpv6_msg_type],
piaddr(packet->client_addr),
@@ -1509,6 +1510,7 @@ process_up6(struct packet *packet, struct stream_list *dp) {
case DHCPV6_RECONFIGURE:
case DHCPV6_RELAY_REPL:
case DHCPV6_LEASEQUERY_REPLY:
+ case DHCPV6_DHCPV4_RESPONSE:
log_info("Discarding %s from %s port %d going up.",
dhcpv6_type_names[packet->dhcpv6_msg_type],
piaddr(packet->client_addr),
@@ -1727,6 +1729,7 @@ process_down6(struct packet *packet) {
case DHCPV6_RECONFIGURE:
case DHCPV6_RELAY_FORW:
case DHCPV6_LEASEQUERY_REPLY:
+ case DHCPV6_DHCPV4_RESPONSE:
log_info("Relaying %s to %s port %d down.",
dhcpv6_type_names[msg->msg_type],
piaddr(peer),
@@ -1742,6 +1745,7 @@ process_down6(struct packet *packet) {
case DHCPV6_DECLINE:
case DHCPV6_INFORMATION_REQUEST:
case DHCPV6_LEASEQUERY:
+ case DHCPV6_DHCPV4_QUERY:
log_info("Discarding %s to %s port %d down.",
dhcpv6_type_names[msg->msg_type],
piaddr(peer),
diff --git a/server/bootp.c b/server/bootp.c
index ca54be64..2e752c5b 100644
--- a/server/bootp.c
+++ b/server/bootp.c
@@ -3,7 +3,7 @@
BOOTP Protocol support. */
/*
- * Copyright (c) 2009,2012-2014 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2009,2012-2014,2016 by Internet Systems Consortium, Inc. ("ISC")
* Copyright (c) 2004,2005,2007 by Internet Systems Consortium, Inc. ("ISC")
* Copyright (c) 1995-2003 by Internet Software Consortium
*
@@ -350,6 +350,34 @@ void bootp (packet)
/* We're done with the option state. */
option_state_dereference (&options, MDL);
+#if defined(DHCPv6) && defined(DHCP4o6)
+ if (dhcpv4_over_dhcpv6 && (packet->dhcp4o6_response != NULL)) {
+ /* Report what we're doing... */
+ log_info("%s", msgbuf);
+ log_info("DHCP4o6 BOOTREPLY for %s to %s (%s) via %s",
+ piaddr(lease->ip_addr),
+ ((hp != NULL) && (hp->name != NULL)) ?
+ hp -> name : "unknown",
+ print_hw_addr (packet->raw->htype,
+ packet->raw->hlen,
+ packet->raw->chaddr),
+ piaddr(packet->client_addr));
+
+ /* fill dhcp4o6_response */
+ packet->dhcp4o6_response->len = outgoing.packet_length;
+ packet->dhcp4o6_response->buffer = NULL;
+ if (!buffer_allocate(&packet->dhcp4o6_response->buffer,
+ outgoing.packet_length, MDL)) {
+ log_fatal("No memory to store DHCP4o6 reply.");
+ }
+ packet->dhcp4o6_response->data =
+ packet->dhcp4o6_response->buffer->data;
+ memcpy(packet->dhcp4o6_response->buffer->data,
+ outgoing.raw, outgoing.packet_length);
+ goto out;
+ }
+#endif
+
/* Set up the hardware destination address... */
hto.hbuf [0] = packet -> raw -> htype;
hto.hlen = packet -> raw -> hlen + 1;
diff --git a/server/confpars.c b/server/confpars.c
index bc85b3b6..1b37a22e 100644
--- a/server/confpars.c
+++ b/server/confpars.c
@@ -2786,12 +2786,21 @@ parse_subnet6_declaration(struct parse *cfile, struct shared_network *share) {
0xF0, 0xF8, 0xFC, 0xFE };
struct iaddr iaddr;
- if (local_family != AF_INET6) {
+#if defined(DHCP4o6)
+ if ((local_family != AF_INET6) && !dhcpv4_over_dhcpv6) {
+ parse_warn(cfile, "subnet6 statement is only supported "
+ "in DHCPv6 and DHCPv4o6 modes.");
+ skip_to_semi(cfile);
+ return;
+ }
+#else /* defined(DHCP4o6) */
+ if (local_family != AF_INET6) {
parse_warn(cfile, "subnet6 statement is only supported "
"in DHCPv6 mode.");
skip_to_semi(cfile);
return;
}
+#endif /* !defined(DHCP4o6) */
subnet = NULL;
status = subnet_allocate(&subnet, MDL);
diff --git a/server/dhcp.c b/server/dhcp.c
index dcbadc39..a823a4e8 100644
--- a/server/dhcp.c
+++ b/server/dhcp.c
@@ -36,6 +36,9 @@ static void maybe_return_agent_options(struct packet *packet,
static int reuse_lease (struct packet* packet, struct lease* new_lease,
struct lease* lease, struct lease_state *state,
int offer);
+#if defined(DHCPv6) && defined(DHCP4o6)
+static int locate_network6(struct packet *packet);
+#endif
int outstanding_pings;
@@ -108,6 +111,20 @@ dhcp (struct packet *packet) {
s = typebuf;
}
+#if defined(DHCPv6) && defined(DHCP4o6)
+ if (dhcpv4_over_dhcpv6 && (packet->dhcp4o6_response != NULL)) {
+ log_info("DHCP4o6 %s from %s via %s: %s", s,
+ (packet->raw->htype
+ ? print_hw_addr(packet->raw->htype,
+ packet->raw->hlen,
+ packet->raw->chaddr)
+ : "<no identifier>"),
+ piaddr(packet->client_addr),
+ errmsg);
+ goto out;
+ }
+#endif
+
log_info("%s from %s via %s: %s", s,
(packet->raw->htype
? print_hw_addr(packet->raw->htype,
@@ -292,6 +309,21 @@ void dhcpdiscover (packet, ms_nulltp)
/* %Audit% This is log output. %2004.06.17,Safe%
* If we truncate we hope the user can get a hint from the log.
*/
+#if defined(DHCPv6) && defined(DHCP4o6)
+ if (dhcpv4_over_dhcpv6 && (packet->dhcp4o6_response != NULL)) {
+ snprintf (msgbuf, sizeof msgbuf,
+ "DHCP4o6 DHCPDISCOVER from %s %s%s%svia %s",
+ (packet -> raw -> htype
+ ? print_hw_addr (packet -> raw -> htype,
+ packet -> raw -> hlen,
+ packet -> raw -> chaddr)
+ : (lease
+ ? print_hex_1(lease->uid_len, lease->uid, 60)
+ : "<no identifier>")),
+ s ? "(" : "", s ? s : "", s ? ") " : "",
+ piaddr(packet->client_addr));
+ } else
+#endif
snprintf (msgbuf, sizeof msgbuf, "DHCPDISCOVER from %s %s%s%svia %s",
(packet -> raw -> htype
? print_hw_addr (packet -> raw -> htype,
@@ -307,6 +339,12 @@ void dhcpdiscover (packet, ms_nulltp)
/* Sourceless packets don't make sense here. */
if (!packet -> shared_network) {
+#if defined(DHCPv6) && defined(DHCP4o6)
+ if (dhcpv4_over_dhcpv6 && (packet->dhcp4o6_response != NULL)) {
+ log_info ("DHCP4o6 packet from unknown subnet: %s",
+ piaddr(packet->client_addr));
+ } else
+#endif
log_info ("Packet from unknown subnet: %s",
inet_ntoa (packet -> raw -> giaddr));
goto out;
@@ -482,6 +520,22 @@ void dhcprequest (packet, ms_nulltp, ip_lease)
/* %Audit% This is log output. %2004.06.17,Safe%
* If we truncate we hope the user can get a hint from the log.
*/
+#if defined(DHCPv6) && defined(DHCP4o6)
+ if (dhcpv4_over_dhcpv6 && (packet->dhcp4o6_response != NULL)) {
+ snprintf (msgbuf, sizeof msgbuf,
+ "DHCP4o6 DHCPREQUEST for %s%s from %s %s%s%svia %s",
+ piaddr (cip), smbuf,
+ (packet -> raw -> htype
+ ? print_hw_addr (packet -> raw -> htype,
+ packet -> raw -> hlen,
+ packet -> raw -> chaddr)
+ : (lease
+ ? print_hex_1(lease->uid_len, lease->uid, 60)
+ : "<no identifier>")),
+ s ? "(" : "", s ? s : "", s ? ") " : "",
+ piaddr(packet->client_addr));
+ } else
+#endif
snprintf (msgbuf, sizeof msgbuf,
"DHCPREQUEST for %s%s from %s %s%s%svia %s",
piaddr (cip), smbuf,
@@ -801,6 +855,24 @@ void dhcprelease (packet, ms_nulltp)
/* %Audit% This is log output. %2004.06.17,Safe%
* If we truncate we hope the user can get a hint from the log.
*/
+#if defined(DHCPv6) && defined(DHCP4o6)
+ if (dhcpv4_over_dhcpv6 && (packet->dhcp4o6_response != NULL)) {
+ snprintf (msgbuf, sizeof msgbuf,
+ "DHCP4o6 DHCPRELEASE of %s from %s %s%s%svia "
+ "%s (%sfound)",
+ cstr,
+ (packet -> raw -> htype
+ ? print_hw_addr (packet -> raw -> htype,
+ packet -> raw -> hlen,
+ packet -> raw -> chaddr)
+ : (lease
+ ? print_hex_1(lease->uid_len, lease->uid, 60)
+ : "<no identifier>")),
+ s ? "(" : "", s ? s : "", s ? ") " : "",
+ piaddr(packet->client_addr),
+ lease ? "" : "not ");
+ } else
+#endif
snprintf (msgbuf, sizeof msgbuf,
"DHCPRELEASE of %s from %s %s%s%svia %s (%sfound)",
cstr,
@@ -892,6 +964,22 @@ void dhcpdecline (packet, ms_nulltp)
/* %Audit% This is log output. %2004.06.17,Safe%
* If we truncate we hope the user can get a hint from the log.
*/
+#if defined(DHCPv6) && defined(DHCP4o6)
+ if (dhcpv4_over_dhcpv6 && (packet->dhcp4o6_response != NULL)) {
+ snprintf (msgbuf, sizeof msgbuf,
+ "DHCP4o6 DHCPDECLINE of %s from %s %s%s%svia %s",
+ piaddr (cip),
+ (packet -> raw -> htype
+ ? print_hw_addr (packet -> raw -> htype,
+ packet -> raw -> hlen,
+ packet -> raw -> chaddr)
+ : (lease
+ ? print_hex_1(lease->uid_len, lease->uid, 60)
+ : "<no identifier>")),
+ s ? "(" : "", s ? s : "", s ? ") " : "",
+ piaddr(packet->client_addr));
+ } else
+#endif
snprintf (msgbuf, sizeof msgbuf,
"DHCPDECLINE of %s from %s %s%s%svia %s",
piaddr (cip),
@@ -1003,9 +1091,17 @@ void dhcpinform (packet, ms_nulltp)
source address if they didn't set ciaddr. */
if (!packet->raw->ciaddr.s_addr) {
zeroed_ciaddr = ISC_TRUE;
- cip.len = 4;
- memcpy(cip.iabuf, &packet->client_addr.iabuf, 4);
- addr_type = "source";
+ /* With DHCPv4-over-DHCPv6 it can be an IPv6 address
+ so we check its length. */
+ if (packet->client_addr.len == 4) {
+ cip.len = 4;
+ memcpy(cip.iabuf, &packet->client_addr.iabuf, 4);
+ addr_type = "source";
+ } else {
+ cip.len = 0;
+ memset(cip.iabuf, 0, 4);
+ addr_type = "v4o6";
+ }
} else {
zeroed_ciaddr = ISC_FALSE;
cip.len = 4;
@@ -1028,6 +1124,14 @@ void dhcpinform (packet, ms_nulltp)
/* %Audit% This is log output. %2004.06.17,Safe%
* If we truncate we hope the user can get a hint from the log.
*/
+#if defined(DHCPv6) && defined(DHCP4o6)
+ if (dhcpv4_over_dhcpv6 && (packet->dhcp4o6_response != NULL)) {
+ snprintf(msgbuf, sizeof(msgbuf),
+ "DHCP4o6 DHCPINFORM from %s via %s",
+ piaddr(cip),
+ piaddr(packet->client_addr));
+ } else
+#endif
snprintf(msgbuf, sizeof(msgbuf), "DHCPINFORM from %s via %s",
piaddr(cip),
packet->raw->giaddr.s_addr ?
@@ -1511,6 +1615,36 @@ void dhcpinform (packet, ms_nulltp)
dump_raw ((unsigned char *)&raw, outgoing.packet_length);
#endif
+#if defined(DHCPv6) && defined(DHCP4o6)
+ if (dhcpv4_over_dhcpv6 && (packet->dhcp4o6_response != NULL)) {
+ /* Report what we're sending. */
+ snprintf(msgbuf, sizeof msgbuf,
+ "DHCP4o6 DHCPACK to %s (%s) via", piaddr(cip),
+ (packet->raw->htype && packet->raw->hlen) ?
+ print_hw_addr(packet->raw->htype, packet->raw->hlen,
+ packet->raw->chaddr) :
+ "<no client hardware address>");
+ log_info("%s %s", msgbuf, piaddr(packet->client_addr));
+
+ /* fill dhcp4o6_response */
+ packet->dhcp4o6_response->len = outgoing.packet_length;
+ packet->dhcp4o6_response->buffer = NULL;
+ if (!buffer_allocate(&packet->dhcp4o6_response->buffer,
+ outgoing.packet_length, MDL)) {
+ log_fatal("No memory to store DHCP4o6 reply.");
+ }
+ packet->dhcp4o6_response->data =
+ packet->dhcp4o6_response->buffer->data;
+ memcpy(packet->dhcp4o6_response->buffer->data,
+ outgoing.raw, outgoing.packet_length);
+
+ /* done */
+ if (subnet)
+ subnet_dereference (&subnet, MDL);
+ return;
+ }
+#endif
+
/* Set up the common stuff... */
to.sin_family = AF_INET;
#ifdef HAVE_SA_LEN
@@ -1711,7 +1845,21 @@ void nak_lease (packet, cip, network_group)
raw.hops = packet -> raw -> hops;
raw.op = BOOTREPLY;
+ /* Make sure that the packet is at least as big as a BOOTP packet. */
+ if (outgoing.packet_length < BOOTP_MIN_LEN)
+ outgoing.packet_length = BOOTP_MIN_LEN;
+
/* Report what we're sending... */
+#if defined(DHCPv6) && defined(DHCP4o6)
+ if (dhcpv4_over_dhcpv6 && (packet->dhcp4o6_response != NULL)) {
+ log_info ("DHCP4o6 DHCPNAK on %s to %s via %s",
+ piaddr (*cip),
+ print_hw_addr (packet -> raw -> htype,
+ packet -> raw -> hlen,
+ packet -> raw -> chaddr),
+ piaddr(packet->client_addr));
+ } else
+#endif
log_info ("DHCPNAK on %s to %s via %s",
piaddr (*cip),
print_hw_addr (packet -> raw -> htype,
@@ -1728,6 +1876,23 @@ void nak_lease (packet, cip, network_group)
dump_raw ((unsigned char *)&raw, outgoing.packet_length);
#endif
+#if defined(DHCPv6) && defined(DHCP4o6)
+ if (dhcpv4_over_dhcpv6 && (packet->dhcp4o6_response != NULL)) {
+ /* fill dhcp4o6_response */
+ packet->dhcp4o6_response->len = outgoing.packet_length;
+ packet->dhcp4o6_response->buffer = NULL;
+ if (!buffer_allocate(&packet->dhcp4o6_response->buffer,
+ outgoing.packet_length, MDL)) {
+ log_fatal("No memory to store DHCP4o6 reply.");
+ }
+ packet->dhcp4o6_response->data =
+ packet->dhcp4o6_response->buffer->data;
+ memcpy(packet->dhcp4o6_response->buffer->data,
+ outgoing.raw, outgoing.packet_length);
+ return;
+ }
+#endif
+
/* Set up the common stuff... */
to.sin_family = AF_INET;
#ifdef HAVE_SA_LEN
@@ -1735,10 +1900,6 @@ void nak_lease (packet, cip, network_group)
#endif
memset (to.sin_zero, 0, sizeof to.sin_zero);
- /* Make sure that the packet is at least as big as a BOOTP packet. */
- if (outgoing.packet_length < BOOTP_MIN_LEN)
- outgoing.packet_length = BOOTP_MIN_LEN;
-
/* If this was gatewayed, send it back to the gateway.
Otherwise, broadcast it on the local network. */
if (raw.giaddr.s_addr) {
@@ -1964,7 +2125,7 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp)
struct in_addr from;
TIME remaining_time;
struct iaddr cip;
-#if defined(DELAYED_ACK)
+#if defined(DELAYED_ACK) && !defined(DHCP4o6)
/* By default we don't do the enqueue */
isc_boolean_t enqueue = ISC_FALSE;
#endif
@@ -2965,7 +3126,7 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp)
commit = 0;
}
-#if !defined(DELAYED_ACK)
+#if !defined(DELAYED_ACK) || defined(DHCP4o6)
/* Install the new information on 'lt' onto the lease at
* 'lease'. If this is a DHCPOFFER, it is a 'soft' promise,
* if it is a DHCPACK, it is a 'hard' binding, so it needs
@@ -2977,7 +3138,7 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp)
if ((use_old_lease == 0) &&
!supersede_lease(lease, lt, commit,
offer == DHCPACK, offer == DHCPACK, 0)) {
-#else /* defined(DELAYED_ACK) */
+#else /* defined(DELAYED_ACK) && !defined(DHCP4o6) */
/*
* If there already isn't a need for a lease commit, and we
* can just answer right away, set a flag to indicate this.
@@ -3374,7 +3535,7 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp)
++outstanding_pings;
} else {
lease->cltt = cur_time;
-#if defined(DELAYED_ACK)
+#if defined(DELAYED_ACK) && !defined(DHCP4o6)
if (enqueue)
delayed_ack_enqueue(lease);
else
@@ -3650,6 +3811,48 @@ void dhcp_reply (lease)
} else
s = (char *)0;
+ /* Make sure outgoing packets are at least as big
+ as a BOOTP packet. */
+ if (packet_length < BOOTP_MIN_LEN)
+ packet_length = BOOTP_MIN_LEN;
+
+#if defined(DHCPv6) && defined(DHCP4o6)
+ if (dhcpv4_over_dhcpv6 && (state->packet->dhcp4o6_response != NULL)) {
+ /* Say what we're doing... */
+ log_info ("DHCP4o6 %s on %s to %s %s%s%svia %s",
+ (state -> offer
+ ? (state -> offer == DHCPACK
+ ? "DHCPACK" : "DHCPOFFER")
+ : "BOOTREPLY"),
+ piaddr (lease -> ip_addr),
+ (lease -> hardware_addr.hlen
+ ? print_hw_addr (lease -> hardware_addr.hbuf [0],
+ lease -> hardware_addr.hlen - 1,
+ &lease -> hardware_addr.hbuf [1])
+ : print_hex_1(lease->uid_len, lease->uid, 60)),
+ s ? "(" : "", s ? s : "", s ? ") " : "",
+ piaddr(state->packet->client_addr));
+
+ /* fill dhcp4o6_response */
+ state->packet->dhcp4o6_response->len = packet_length;
+ state->packet->dhcp4o6_response->buffer = NULL;
+ if (!buffer_allocate(&state->packet->dhcp4o6_response->buffer,
+ packet_length, MDL)) {
+ log_fatal("No memory to store DHCP4o6 reply.");
+ }
+ state->packet->dhcp4o6_response->data =
+ state->packet->dhcp4o6_response->buffer->data;
+ memcpy(state->packet->dhcp4o6_response->buffer->data,
+ &raw, packet_length);
+
+ /* done */
+ free_lease_state (state, MDL);
+ lease -> state = (struct lease_state *)0;
+
+ return;
+ }
+#endif
+
/* Say what we're doing... */
log_info ("%s on %s to %s %s%s%svia %s",
(state -> offer
@@ -3666,6 +3869,10 @@ void dhcp_reply (lease)
? inet_ntoa (state -> giaddr)
: state -> ip -> name));
+#ifdef DEBUG_PACKET
+ dump_raw ((unsigned char *)&raw, packet_length);
+#endif
+
/* Set up the hardware address... */
hto.hlen = lease -> hardware_addr.hlen;
memcpy (hto.hbuf, lease -> hardware_addr.hbuf, hto.hlen);
@@ -3676,15 +3883,6 @@ void dhcp_reply (lease)
#endif
memset (to.sin_zero, 0, sizeof to.sin_zero);
-#ifdef DEBUG_PACKET
- dump_raw ((unsigned char *)&raw, packet_length);
-#endif
-
- /* Make sure outgoing packets are at least as big
- as a BOOTP packet. */
- if (packet_length < BOOTP_MIN_LEN)
- packet_length = BOOTP_MIN_LEN;
-
/* If this was gatewayed, send it back to the gateway... */
if (raw.giaddr.s_addr) {
to.sin_addr = raw.giaddr;
@@ -4801,6 +4999,132 @@ int permitted (packet, permit_list)
return 0;
}
+#if defined(DHCPv6) && defined(DHCP4o6)
+static int locate_network6 (packet)
+ struct packet *packet;
+{
+ const struct packet *chk_packet;
+ const struct in6_addr *link_addr, *first_link_addr;
+ struct iaddr ia;
+ struct data_string data;
+ struct subnet *subnet = NULL;
+ struct option_cache *oc;
+
+ /* from locate_network() */
+
+ /* See if there's a Relay Agent Link Selection Option, or a
+ * Subnet Selection Option. The Link-Select and Subnet-Select
+ * are formatted and used precisely the same, but we must prefer
+ * the link-select over the subnet-select.
+ * BTW in DHCPv4 over DHCPv6 no cross version relay was specified
+ * so it is unlikely to see a link-select.
+ */
+ if ((oc = lookup_option(&agent_universe, packet->options,
+ RAI_LINK_SELECT)) == NULL)
+ oc = lookup_option(&dhcp_universe, packet->options,
+ DHO_SUBNET_SELECTION);
+
+ /* If there's an option indicating link connection or subnet
+ * selection, and it's valid, use it to figure out the subnet.
+ * If it's not valid, fail.
+ */
+ if (oc) {
+ memset(&data, 0, sizeof data);
+ if (!evaluate_option_cache(&data, packet, NULL, NULL,
+ packet->options, NULL,
+ &global_scope, oc, MDL)) {
+ return (0);
+ }
+ if (data.len == 0) {
+ return (0);
+ }
+ if (data.len != 4) {
+ data_string_forget(&data, MDL);
+ return (0);
+ }
+ ia.len = 4;
+ memcpy(ia.iabuf, data.data, 4);
+ data_string_forget(&data, MDL);
+
+ if (find_subnet(&subnet, ia, MDL)) {
+ shared_network_reference(&packet->shared_network,
+ subnet->shared_network, MDL);
+ subnet_dereference(&subnet, MDL);
+ return (1);
+ }
+ return (0);
+ }
+
+ /* See if there is a giaddr (still unlikely), if there is one
+ * use it to figure out the subnet. If it's not valid, fail.
+ */
+ if (packet->raw->giaddr.s_addr) {
+ ia.len = 4;
+ memcpy(ia.iabuf, &packet->raw->giaddr, 4);
+
+ if (find_subnet(&subnet, ia, MDL)) {
+ shared_network_reference(&packet->shared_network,
+ subnet->shared_network, MDL);
+ subnet_dereference(&subnet, MDL);
+ return (1);
+ }
+ return (0);
+ }
+
+ /* from shared_network_from_packet6() */
+
+ /* First, find the link address where the packet from the client
+ * first appeared (if this packet was relayed).
+ */
+ first_link_addr = NULL;
+ chk_packet = packet->dhcpv6_container_packet;
+ while (chk_packet != NULL) {
+ link_addr = &chk_packet->dhcpv6_link_address;
+ if (!IN6_IS_ADDR_UNSPECIFIED(link_addr) &&
+ !IN6_IS_ADDR_LINKLOCAL(link_addr)) {
+ first_link_addr = link_addr;
+ break;
+ }
+ chk_packet = chk_packet->dhcpv6_container_packet;
+ }
+
+ /* If there is a relayed link address, find the subnet associated
+ * with that, and use that to get the appropriate shared_network.
+ */
+ if (first_link_addr != NULL) {
+ ia.len = sizeof(*first_link_addr);
+ memcpy(ia.iabuf, first_link_addr, sizeof(*first_link_addr));
+ if (find_subnet (&subnet, ia, MDL)) {
+ shared_network_reference(&packet->shared_network,
+ subnet->shared_network, MDL);
+ subnet_dereference(&subnet, MDL);
+ return (1);
+ }
+ return (0);
+ }
+
+ /* If there is no link address, we will use the interface
+ * that this packet came in on to pick the shared_network.
+ */
+ if (packet->interface != NULL) {
+ if (packet->interface->shared_network == NULL)
+ return (0);
+ shared_network_reference(&packet->shared_network,
+ packet->interface->shared_network,
+ MDL);
+ return (1);
+ }
+
+ /* We shouldn't be able to get here but if there is no link
+ * address and no interface we don't know where to get the
+ * shared_network from, log an error and return an error.
+ */
+ log_error("No interface and no link address "
+ "can't determine DHCP4o6 shared network");
+ return (0);
+}
+#endif
+
int locate_network (packet)
struct packet *packet;
{
@@ -4809,6 +5133,12 @@ int locate_network (packet)
struct subnet *subnet = (struct subnet *)0;
struct option_cache *oc;
+#if defined(DHCPv6) && defined(DHCP4o6)
+ if (dhcpv4_over_dhcpv6 && (packet->dhcp4o6_response != NULL)) {
+ return (locate_network6 (packet));
+ }
+#endif
+
/* See if there's a Relay Agent Link Selection Option, or a
* Subnet Selection Option. The Link-Select and Subnet-Select
* are formatted and used precisely the same, but we must prefer
@@ -4843,7 +5173,11 @@ int locate_network (packet)
&global_scope, oc, MDL)) {
return 0;
}
+ if (data.len == 0) {
+ return 0;
+ }
if (data.len != 4) {
+ data_string_forget (&data, MDL);
return 0;
}
ia.len = 4;
diff --git a/server/dhcpd.8 b/server/dhcpd.8
index bfda6397..55466766 100644
--- a/server/dhcpd.8
+++ b/server/dhcpd.8
@@ -1,6 +1,6 @@
.\" dhcpd.8
.\"
-.\" Copyright (c) 2009-2012,2015 by Internet Systems Consortium, Inc. ("ISC")
+.\" Copyright (c) 2009-2012,2015-2016 by Internet Systems Consortium, Inc. ("ISC")
.\" Copyright (c) 2004-2007 by Internet Systems Consortium, Inc. ("ISC")
.\" Copyright (c) 1996-2003 by Internet Software Consortium
.\"
@@ -59,6 +59,10 @@ dhcpd - Dynamic Host Configuration Protocol Server
.B -6
]
[
+.B -4o6
+.I port
+]
+[
.B -s
.I server
]
@@ -208,8 +212,16 @@ Run as a DHCP server. This is the default and cannot be combined with
.BI \-6
Run as a DHCPv6 server. This cannot be combined with \fB\-4\fR.
.TP
+.BI \-4o6 \ port
+Participate in the DHCPv4 over DHCPv6 protocol specified by RFC 7341.
+This associates a DHCPv4 and a DHCPv6 server to allow the v4 server to
+receive v4 requests that were encapsualted in a v6 packet. Communication
+between the two servers is done on a pair of UDP sockets bound
+to ::1 \fIport\fR and \fIport + 1\fR. Both servers must
+be launched using the same \fIport\fR argument.
+.TP
.BI \-p \ port
-The udp port number on which
+The UDP port number on which
.B dhcpd
should listen. If unspecified
.B dhcpd
diff --git a/server/dhcpd.c b/server/dhcpd.c
index abc63df9..145561c0 100644
--- a/server/dhcpd.c
+++ b/server/dhcpd.c
@@ -164,7 +164,12 @@ usage(const char *sfmt, const char *sarg) {
log_fatal("Usage: %s [-p <UDP port #>] [-f] [-d] [-q] [-t|-T]\n"
#ifdef DHCPv6
+#ifdef DHCP4o6
+ " [-4|-6] [-4o6 <port>]\n"
+ " [-cf config-file] [-lf lease-file]\n"
+#else /* DHCP4o6 */
" [-4|-6] [-cf config-file] [-lf lease-file]\n"
+#endif /* DHCP4o6 */
#else /* !DHCPv6 */
" [-cf config-file] [-lf lease-file]\n"
#endif /* DHCPv6 */
@@ -228,6 +233,9 @@ main(int argc, char **argv) {
int no_dhcpd_pid = 0;
#ifdef DHCPv6
int local_family_set = 0;
+#ifdef DHCP4o6
+ u_int16_t dhcp4o6_port = 0;
+#endif /* DHCP4o6 */
#endif /* DHCPv6 */
#if defined (TRACING)
char *traceinfile = (char *)0;
@@ -369,6 +377,17 @@ main(int argc, char **argv) {
}
local_family = AF_INET6;
local_family_set = 1;
+#ifdef DHCP4o6
+ } else if (!strcmp(argv[i], "-4o6")) {
+ if (++i == argc)
+ usage(use_noarg, argv[i-1]);
+ dhcp4o6_port = validate_port_pair(argv[i]);
+
+ log_debug("DHCPv4 over DHCPv6 over ::1 port %d and %d",
+ ntohs(dhcp4o6_port),
+ ntohs(dhcp4o6_port) + 1);
+ dhcpv4_over_dhcpv6 = 1;
+#endif /* DHCP4o6 */
#endif /* DHCPv6 */
} else if (!strcmp (argv [i], "--version")) {
const char vstring[] = "isc-dhcpd-";
@@ -415,6 +434,18 @@ main(int argc, char **argv) {
}
}
+#if defined(DHCPv6) && defined(DHCP4o6)
+ if (dhcpv4_over_dhcpv6) {
+ if (!local_family_set)
+ log_error("please specify the address family "
+ "with DHPv4 over DHCPv6 [-4|-6].");
+ if ((local_family == AF_INET) && (interfaces != NULL))
+ log_fatal("DHCPv4 server in DHPv4 over DHCPv6 "
+ "mode with command line specified "
+ "interfaces.");
+ }
+#endif /* DHCPv6 && DHCP4o6 */
+
if (!no_dhcpd_conf && (s = getenv ("PATH_DHCPD_CONF"))) {
path_dhcpd_conf = s;
}
@@ -677,6 +708,15 @@ main(int argc, char **argv) {
postconf_initialization (quiet);
+#if defined(DHCPv6) && defined(DHCP4o6)
+ if (dhcpv4_over_dhcpv6) {
+ if ((local_family == AF_INET) && (interfaces != NULL))
+ log_fatal("DHCPv4 server in DHPv4 over DHCPv6 "
+ "mode with config file specified "
+ "interfaces.");
+ }
+#endif /* DHCPv6 && DHCP4o6 */
+
#if defined (PARANOIA) && !defined (EARLY_CHROOT)
if (set_chroot) setup_chroot (set_chroot);
#endif /* PARANOIA && !EARLY_CHROOT */
@@ -727,6 +767,20 @@ main(int argc, char **argv) {
exit (0);
/* Discover all the network interfaces and initialize them. */
+#if defined(DHCPv6) && defined(DHCP4o6)
+ if (dhcpv4_over_dhcpv6) {
+ int real_family = local_family;
+ local_family = AF_INET6;
+ /* The DHCPv4 side of DHCPv4-over-DHCPv6 service
+ uses a specific discovery which doesn't register
+ DHCPv6 sockets. */
+ if (real_family == AF_INET)
+ discover_interfaces(DISCOVER_SERVER46);
+ else
+ discover_interfaces(DISCOVER_SERVER);
+ local_family = real_family;
+ } else
+#endif /* DHCPv6 && DHCP4o6 */
discover_interfaces(DISCOVER_SERVER);
#ifdef DHCPv6
@@ -772,7 +826,7 @@ main(int argc, char **argv) {
* server-duid from the lease file
* server-duid from the config file (the config file is read first
* and the lease file overwrites the config file information)
- * genrate a new one
+ * generate a new one from the interface hardware addresses.
* In all cases we write it out to the lease file.
* See dhcpv6.c for discussion of setting DUID.
*/
@@ -782,6 +836,10 @@ main(int argc, char **argv) {
log_fatal("Unable to set server identifier.");
}
write_server_duid();
+#ifdef DHCP4o6
+ if (dhcpv4_over_dhcpv6)
+ dhcp4o6_setup(dhcp4o6_port);
+#endif /* DHCP4o6 */
#endif /* DHCPv6 */
#ifndef DEBUG
diff --git a/server/dhcpleasequery.c b/server/dhcpleasequery.c
index 0766b849..91ca870c 100644
--- a/server/dhcpleasequery.c
+++ b/server/dhcpleasequery.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011-2013 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2011-2013,2016 by Internet Systems Consortium, Inc. ("ISC")
* Copyright (C) 2006-2007,2009 by Internet Systems Consortium, Inc. ("ISC")
*
* Permission to use, copy, modify, and distribute this software for any
@@ -172,6 +172,11 @@ dhcpleasequery(struct packet *packet, int ms_nulltp) {
/*
* We can't reply if there is no giaddr field.
*/
+ /*
+ * Note: this makes DHCPv4-over-DHCPv6 always fail but it should not
+ * really be a problem because it is not a specified use case
+ * (or even one that makes sense).
+ */
if (!packet->raw->giaddr.s_addr) {
log_info("%s: missing giaddr, ciaddr is %s, no reply sent",
msgbuf, inet_ntoa(packet->raw->ciaddr));
diff --git a/server/dhcpv6.c b/server/dhcpv6.c
index d85d0b07..18d8bb88 100644
--- a/server/dhcpv6.c
+++ b/server/dhcpv6.c
@@ -20,6 +20,15 @@
#ifdef DHCPv6
+#ifdef DHCP4o6
+static void forw_dhcpv4_query(struct packet *packet);
+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);
+#endif
+
/*
* We use print_hex_1() to output DUID values. We could actually output
* the DUID with more information... MAC address if using type 1 or 3,
@@ -167,6 +176,104 @@ static isc_result_t get_first_ia_addr_val (struct packet* packet, int addr_type,
static void
set_reply_tee_times(struct reply_state* reply, unsigned ia_cursor);
+#ifdef DHCP4o6
+/*
+ * \brief Omapi I/O handler
+ *
+ * The inter-process communication receive handler.
+ * Get the message, put it into the raw data_string
+ * and call \ref send_dhcpv4_response() (DHCPv6 side) or
+ * \ref recv_dhcpv4_query() (DHCPv4 side)
+ *
+ * \param h the OMAPI object
+ * \return a result for I/O success or error (used by the I/O subsystem)
+ */
+isc_result_t dhcpv4o6_handler(omapi_object_t *h) {
+ char buf[65536];
+ struct data_string raw;
+ int cc;
+
+ if (h->type != dhcp4o6_type)
+ return DHCP_R_INVALIDARG;
+
+ cc = recv(dhcp4o6_fd, buf, sizeof(buf), 0);
+
+ if (cc < DHCP_FIXED_NON_UDP + 32)
+ return ISC_R_UNEXPECTED;
+ memset(&raw, 0, sizeof(raw));
+ if (!buffer_allocate(&raw.buffer, cc, MDL)) {
+ log_error("dhcpv4o6_handler: no memory buffer.");
+ return ISC_R_NOMEMORY;
+ }
+ raw.data = raw.buffer->data;
+ raw.len = cc;
+ memcpy(raw.buffer->data, buf, cc);
+
+ if (local_family == AF_INET6) {
+ send_dhcpv4_response(&raw);
+ } else {
+ recv_dhcpv4_query(&raw);
+ }
+
+ data_string_forget(&raw, MDL);
+
+ return ISC_R_SUCCESS;
+}
+
+/*
+ * \brief Send the DHCPv4-response back to the DHCPv6 side
+ * (DHCPv6 server function)
+ *
+ * Format: interface:16 + address:16 + DHCPv6 DHCPv4-response message
+ *
+ * \param raw the IPC message content
+ */
+static void send_dhcpv4_response(struct data_string *raw) {
+ struct interface_info *ip;
+ char name[16 + 1];
+ struct sockaddr_in6 to_addr;
+ char pbuf[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
+ int send_ret;
+
+ memset(name, 0, sizeof(name));
+ memcpy(name, raw->data, 16);
+ for (ip = interfaces; ip != NULL; ip = ip->next) {
+ if (!strcmp(name, ip->name))
+ break;
+ }
+ if (ip == NULL) {
+ log_error("send_dhcpv4_response: can't find interface %s.",
+ name);
+ return;
+ }
+
+ 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;
+ } 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]],
+ 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);
+ if (send_ret < 0) {
+ log_error("send_dhcpv4_response: send_packet6(): %m");
+ } else if (send_ret != raw->len - 32) {
+ log_error("send_dhcpv4_response: send_packet6() "
+ "sent %d of %d bytes",
+ send_ret, raw->len - 32);
+ }
+}
+#endif /* DHCP4o6 */
+
/*
* Schedule lease timeouts for all of the iasubopts in the reply.
* This is currently used to schedule timeouts for soft leases.
@@ -754,6 +861,12 @@ static const int required_opts_STATUS_CODE[] = {
D6O_STATUS_CODE,
0
};
+#ifdef DHCP4o6
+static const int required_opts_4o6[] = {
+ D6O_DHCPV4_MSG,
+ 0
+};
+#endif
static const int unicast_reject_opts[] = {
D6O_CLIENTID,
@@ -1379,7 +1492,7 @@ try_client_v6_prefix(struct iasubopt **pref,
* hash the address. After a number of failures we
* conclude the pool is basically full.
*/
-static isc_result_t
+static isc_result_t
pick_v6_prefix(struct reply_state *reply) {
struct ipv6_pool *p = NULL;
struct ipv6_pond *pond;
@@ -6231,6 +6344,7 @@ dhcpv6_information_request(struct data_string *reply, struct packet *packet) {
/* XXX: this is very, very similar to do_packet6(), and should probably
be combined in a clever way */
+/* DHCPv6 server side */
static void
dhcpv6_relay_forw(struct data_string *reply_ret, struct packet *packet) {
struct option_cache *oc;
@@ -6275,12 +6389,14 @@ dhcpv6_relay_forw(struct data_string *reply_ret, struct packet *packet) {
if (!evaluate_option_cache(&enc_opt_data, NULL, NULL, NULL,
NULL, NULL, &global_scope, oc, MDL)) {
+ /* should be dhcpv6_relay_forw */
log_error("dhcpv6_forw_relay: error evaluating "
"relayed message.");
goto exit;
}
if (!packet6_len_okay((char *)enc_opt_data.data, enc_opt_data.len)) {
+ /* should be dhcpv6_relay_forw */
log_error("dhcpv6_forw_relay: encapsulated packet too short.");
goto exit;
}
@@ -6290,12 +6406,14 @@ dhcpv6_relay_forw(struct data_string *reply_ret, struct packet *packet) {
*/
enc_packet = NULL;
if (!packet_allocate(&enc_packet, MDL)) {
+ /* should be dhcpv6_relay_forw */
log_error("dhcpv6_forw_relay: "
"no memory for encapsulated packet.");
goto exit;
}
if (!option_state_allocate(&enc_packet->options, MDL)) {
+ /* should be dhcpv6_relay_forw */
log_error("dhcpv6_forw_relay: "
"no memory for encapsulated packet's options.");
goto exit;
@@ -6328,6 +6446,23 @@ dhcpv6_relay_forw(struct data_string *reply_ret, struct packet *packet) {
cases where it fails */
goto exit;
}
+ } else if ((msg_type == DHCPV6_DHCPV4_QUERY) ||
+ (msg_type == DHCPV6_DHCPV4_RESPONSE)) {
+#ifdef DHCP4o6
+ if (!dhcpv4_over_dhcpv6 ||
+ (msg_type == DHCPV6_DHCPV4_RESPONSE)) {
+ log_error("dhcpv6_relay_forw: "
+ "unsupported %s message type.",
+ dhcpv6_type_names[msg_type]);
+ goto exit;
+ }
+ forw_dhcpv4_query(packet);
+ goto exit;
+#else /* DHCP4o6 */
+ log_error("dhcpv6_relay_forw: unsupported %s message type.",
+ dhcpv6_type_names[msg_type]);
+ goto exit;
+#endif /* DHCP4o6 */
} else {
int msglen = (int)(offsetof(struct dhcpv6_packet, options));
msg = (struct dhcpv6_packet *)enc_opt_data.data;
@@ -6509,6 +6644,536 @@ exit:
}
}
+#ifdef DHCP4o6
+/* \brief Internal processing of a relayed DHCPv4-query
+ * (DHCPv4 server side)
+ *
+ * Code copied from \ref dhcpv6_relay_forw() which itself is
+ * from \ref do_packet6().
+ *
+ * \param reply_ret pointer to the response
+ * \param packet the query
+ */
+static void
+dhcp4o6_relay_forw(struct data_string *reply_ret, struct packet *packet) {
+ struct option_cache *oc;
+ struct data_string enc_opt_data;
+ struct packet *enc_packet;
+ unsigned char msg_type;
+ const struct dhcpv6_relay_packet *relay;
+ const struct dhcpv4_over_dhcpv6_packet *msg;
+ struct data_string enc_reply;
+ 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")];
+ 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.
+ */
+ opt_state = NULL;
+ memset(&a_opt, 0, sizeof(a_opt));
+ memset(&packet_ero, 0, sizeof(packet_ero));
+ memset(&enc_reply, 0, sizeof(enc_reply));
+ memset(&enc_opt_data, 0, sizeof(enc_opt_data));
+ enc_packet = NULL;
+
+ /*
+ * Get our encapsulated relay message.
+ */
+ oc = lookup_option(&dhcpv6_universe, packet->options, D6O_RELAY_MSG);
+ if (oc == NULL) {
+ inet_ntop(AF_INET6, &packet->dhcpv6_link_address,
+ link_addr, sizeof(link_addr));
+ inet_ntop(AF_INET6, &packet->dhcpv6_peer_address,
+ peer_addr, sizeof(peer_addr));
+ log_info("Relay-forward from %s with link address=%s and "
+ "peer address=%s missing Relay Message option.",
+ piaddr(packet->client_addr), link_addr, peer_addr);
+ goto exit;
+ }
+
+ if (!evaluate_option_cache(&enc_opt_data, NULL, NULL, NULL,
+ NULL, NULL, &global_scope, oc, MDL)) {
+ log_error("dhcp4o6_relay_forw: error evaluating "
+ "relayed message.");
+ goto exit;
+ }
+
+ if (!packet6_len_okay((char *)enc_opt_data.data, enc_opt_data.len)) {
+ log_error("dhcp4o6_relay_forw: "
+ "encapsulated packet too short.");
+ goto exit;
+ }
+
+ /*
+ * Build a packet structure from this encapsulated packet.
+ */
+ if (!packet_allocate(&enc_packet, MDL)) {
+ log_error("dhcp4o6_relay_forw: "
+ "no memory for encapsulated packet.");
+ goto exit;
+ }
+
+ if (!option_state_allocate(&enc_packet->options, MDL)) {
+ log_error("dhcp4o6_relay_forw: "
+ "no memory for encapsulated packet's options.");
+ goto exit;
+ }
+
+ enc_packet->client_port = packet->client_port;
+ enc_packet->client_addr = packet->client_addr;
+ interface_reference(&enc_packet->interface, packet->interface, MDL);
+ enc_packet->dhcpv6_container_packet = packet;
+
+ msg_type = enc_opt_data.data[0];
+ if ((msg_type == DHCPV6_RELAY_FORW) ||
+ (msg_type == DHCPV6_RELAY_REPL)) {
+ int relaylen = (int)(offsetof(struct dhcpv6_relay_packet, options));
+ relay = (struct dhcpv6_relay_packet *)enc_opt_data.data;
+ enc_packet->dhcpv6_msg_type = relay->msg_type;
+
+ /* relay-specific data */
+ enc_packet->dhcpv6_hop_count = relay->hop_count;
+ memcpy(&enc_packet->dhcpv6_link_address,
+ relay->link_address, sizeof(relay->link_address));
+ memcpy(&enc_packet->dhcpv6_peer_address,
+ relay->peer_address, sizeof(relay->peer_address));
+
+ if (!parse_option_buffer(enc_packet->options,
+ relay->options,
+ enc_opt_data.len - relaylen,
+ &dhcpv6_universe)) {
+ /* no logging here, as parse_option_buffer() logs all
+ cases where it fails */
+ goto exit;
+ }
+ } else if ((msg_type == DHCPV6_DHCPV4_QUERY) ||
+ (msg_type == DHCPV6_DHCPV4_RESPONSE)) {
+ int msglen =
+ (int)(offsetof(struct dhcpv4_over_dhcpv6_packet, options));
+ msg = (struct dhcpv4_over_dhcpv6_packet *)enc_opt_data.data;
+ enc_packet->dhcpv6_msg_type = msg->msg_type;
+
+ /* message-specific data */
+ memcpy(enc_packet->dhcp4o6_flags,
+ msg->flags,
+ sizeof(enc_packet->dhcp4o6_flags));
+
+ if (!parse_option_buffer(enc_packet->options,
+ msg->options,
+ enc_opt_data.len - msglen,
+ &dhcpv6_universe)) {
+ /* no logging here, as parse_option_buffer() logs all
+ cases where it fails */
+ goto exit;
+ }
+ } else {
+ log_error("dhcp4o6_relay_forw: unexpected message of type %d.",
+ (int)msg_type);
+ goto exit;
+ }
+
+ /*
+ * This is recursive. It is possible to exceed maximum packet size.
+ * XXX: This will cause the packet send to fail.
+ */
+ build_dhcpv6_reply(&enc_reply, enc_packet);
+
+ /*
+ * If we got no encapsulated data, then it is discarded, and
+ * our reply-forw is also discarded.
+ */
+ if (enc_reply.data == NULL) {
+ goto exit;
+ }
+
+ /*
+ * Now we can use the reply_data buffer.
+ * Packet header stuff all comes from the forward message.
+ */
+ reply = (struct dhcpv6_relay_packet *)reply_data;
+ reply->msg_type = DHCPV6_RELAY_REPL;
+ reply->hop_count = packet->dhcpv6_hop_count;
+ memcpy(reply->link_address, &packet->dhcpv6_link_address,
+ sizeof(reply->link_address));
+ memcpy(reply->peer_address, &packet->dhcpv6_peer_address,
+ sizeof(reply->peer_address));
+ reply_ofs = (int)(offsetof(struct dhcpv6_relay_packet, options));
+
+ /*
+ * Get the reply option state.
+ */
+ if (!option_state_allocate(&opt_state, MDL)) {
+ log_error("dhcp4o6_relay_forw: no memory for option state.");
+ goto exit;
+ }
+
+ /*
+ * Append the interface-id if present.
+ */
+ oc = lookup_option(&dhcpv6_universe, packet->options,
+ D6O_INTERFACE_ID);
+ if (oc != NULL) {
+ if (!evaluate_option_cache(&a_opt, packet,
+ NULL, NULL,
+ packet->options, NULL,
+ &global_scope, oc, MDL)) {
+ log_error("dhcp4o6_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("dhcp4o6_relay_forw: error saving "
+ "Interface ID.");
+ goto exit;
+ }
+ data_string_forget(&a_opt, MDL);
+ }
+
+ /*
+ * Append our encapsulated stuff for caller.
+ */
+ if (!save_option_buffer(&dhcpv6_universe, opt_state, NULL,
+ (unsigned char *)enc_reply.data,
+ enc_reply.len,
+ D6O_RELAY_MSG, 0)) {
+ log_error("dhcp4o6_relay_forw: error saving Relay MSG.");
+ 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("dhcp4o6_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("dhcp4o6_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("dhcp4o6_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;
+ if (!buffer_allocate(&reply_ret->buffer, reply_ret->len, MDL)) {
+ log_fatal("No memory to store reply.");
+ }
+ reply_ret->data = reply_ret->buffer->data;
+ memcpy(reply_ret->buffer->data, reply_data, reply_ofs);
+
+exit:
+ if (opt_state != NULL)
+ 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) {
+ data_string_forget(&enc_reply, MDL);
+ }
+ if (enc_opt_data.data != NULL) {
+ data_string_forget(&enc_opt_data, MDL);
+ }
+ if (enc_packet != NULL) {
+ packet_dereference(&enc_packet, MDL);
+ }
+}
+
+/*
+ * \brief Internal processing of a DHCPv4-query
+ * (DHCPv4 server function)
+ *
+ * Code copied from \ref do_packet().
+ *
+ * \param reply_ret pointer to the response
+ * \param packet the query
+ */
+static void
+dhcp4o6_dhcpv4_query(struct data_string *reply_ret, struct packet *packet) {
+ struct option_cache *oc;
+ struct data_string enc_opt_data;
+ struct packet *enc_packet;
+ struct data_string enc_response;
+ struct option_state *opt_state;
+ static char response_data[65536];
+ struct dhcpv4_over_dhcpv6_packet *response;
+ int response_ofs;
+
+ /*
+ * Initialize variables for early exit.
+ */
+ opt_state = NULL;
+ memset(&enc_response, 0, sizeof(enc_response));
+ memset(&enc_opt_data, 0, sizeof(enc_opt_data));
+ enc_packet = NULL;
+
+ /*
+ * Get our encapsulated relay message.
+ */
+ oc = lookup_option(&dhcpv6_universe, packet->options, D6O_DHCPV4_MSG);
+ if (oc == NULL) {
+ log_info("DHCPv4-query from %s missing DHCPv4 Message option.",
+ piaddr(packet->client_addr));
+ goto exit;
+ }
+
+ if (!evaluate_option_cache(&enc_opt_data, NULL, NULL, NULL,
+ NULL, NULL, &global_scope, oc, MDL)) {
+ log_error("dhcp4o6_dhcpv4_query: error evaluating "
+ "DHCPv4 message.");
+ goto exit;
+ }
+
+ if (enc_opt_data.len < DHCP_FIXED_NON_UDP) {
+ log_error("dhcp4o6_dhcpv4_query: DHCPv4 packet too short.");
+ goto exit;
+ }
+
+ /*
+ * Build a packet structure from this encapsulated packet.
+ */
+ if (!packet_allocate(&enc_packet, MDL)) {
+ log_error("dhcp4o6_dhcpv4_query: "
+ "no memory for encapsulated packet.");
+ goto exit;
+ }
+
+ enc_packet->raw = (struct dhcp_packet *)enc_opt_data.data;
+ enc_packet->packet_length = enc_opt_data.len;
+ enc_packet->dhcp4o6_response = &enc_response;
+ enc_packet->client_port = packet->client_port;
+ enc_packet->client_addr = packet->client_addr;
+ interface_reference(&enc_packet->interface, packet->interface, MDL);
+ enc_packet->dhcpv6_container_packet = packet;
+ if (packet->dhcp4o6_flags[0] & DHCP4O6_QUERY_UNICAST)
+ enc_packet->unicast = 1;
+
+ if (enc_packet->raw->hlen > sizeof(enc_packet->raw->chaddr)) {
+ log_info("dhcp4o6_dhcpv4_query: "
+ "discarding packet with bogus hlen.");
+ goto exit;
+ }
+
+ /* Allocate packet->options now so it is non-null for all packets */
+ if (!option_state_allocate (&enc_packet->options, MDL)) {
+ log_error("dhcp4o6_dhcpv4_query: no memory for options.");
+ goto exit;
+ }
+
+ /* If there's an option buffer, try to parse it. */
+ if (enc_packet->packet_length >= DHCP_FIXED_NON_UDP + 4) {
+ struct option_cache *op;
+ if (!parse_options(enc_packet)) {
+ if (enc_packet->options)
+ option_state_dereference
+ (&enc_packet->options, MDL);
+ packet_dereference (&enc_packet, MDL);
+ goto exit;
+ }
+
+ if (enc_packet->options_valid &&
+ (op = lookup_option(&dhcp_universe,
+ enc_packet->options,
+ DHO_DHCP_MESSAGE_TYPE))) {
+ struct data_string dp;
+ memset(&dp, 0, sizeof dp);
+ evaluate_option_cache(&dp, enc_packet, NULL, NULL,
+ enc_packet->options, NULL,
+ NULL, op, MDL);
+ if (dp.len > 0)
+ enc_packet->packet_type = dp.data[0];
+ else
+ enc_packet->packet_type = 0;
+ data_string_forget(&dp, MDL);
+ }
+ }
+
+ if (validate_packet(enc_packet) != 0) {
+ if (enc_packet->packet_type)
+ dhcp(enc_packet);
+ else
+ bootp(enc_packet);
+ }
+
+ /* If the caller kept the packet, they'll have upped the refcnt. */
+ packet_dereference(&enc_packet, MDL);
+
+ /*
+ * If we got no response data, then it is discarded, and
+ * our DHCPv4-response is also discarded.
+ */
+ if (enc_response.data == NULL) {
+ goto exit;
+ }
+
+ /*
+ * Now we can use the response_data buffer.
+ */
+ response = (struct dhcpv4_over_dhcpv6_packet *)response_data;
+ response->msg_type = DHCPV6_DHCPV4_RESPONSE;
+ response->flags[0] = response->flags[1] = response->flags[2] = 0;
+ response_ofs =
+ (int)(offsetof(struct dhcpv4_over_dhcpv6_packet, options));
+
+ /*
+ * Get the response option state.
+ */
+ if (!option_state_allocate(&opt_state, MDL)) {
+ log_error("dhcp4o6_dhcpv4_query: no memory for option state.");
+ goto exit;
+ }
+
+ /*
+ * Append our encapsulated stuff for caller.
+ */
+ if (!save_option_buffer(&dhcpv6_universe, opt_state, NULL,
+ (unsigned char *)enc_response.data,
+ enc_response.len,
+ D6O_DHCPV4_MSG, 0)) {
+ log_error("dhcp4o6_dhcpv4_query: error saving DHCPv4 MSG.");
+ goto exit;
+ }
+
+ response_ofs += store_options6(response_data + response_ofs,
+ sizeof(response_data) - response_ofs,
+ opt_state, packet,
+ required_opts_4o6, NULL);
+
+ /*
+ * Return our response to the caller.
+ */
+ reply_ret->len = response_ofs;
+ reply_ret->buffer = NULL;
+ if (!buffer_allocate(&reply_ret->buffer, reply_ret->len, MDL)) {
+ log_fatal("dhcp4o6_dhcpv4_query: no memory to store reply.");
+ }
+ reply_ret->data = reply_ret->buffer->data;
+ memcpy(reply_ret->buffer->data, response_data, response_ofs);
+
+exit:
+ if (opt_state != NULL)
+ option_state_dereference(&opt_state, MDL);
+ if (enc_response.data != NULL) {
+ data_string_forget(&enc_response, MDL);
+ }
+ if (enc_opt_data.data != NULL) {
+ data_string_forget(&enc_opt_data, MDL);
+ }
+ if (enc_packet != NULL) {
+ packet_dereference(&enc_packet, MDL);
+ }
+}
+
+/*
+ * \brief Forward a DHCPv4-query message to the DHCPv4 side
+ * (DHCPv6 server function)
+ *
+ * Format: interface:16 + address:16 + DHCPv6 DHCPv4-query message
+ *
+ * \brief packet the DHCPv6 DHCPv4-query message
+ */
+static void forw_dhcpv4_query(struct packet *packet) {
+ struct data_string ds;
+ unsigned len;
+ int cc;
+
+ /* Get the initial message. */
+ while (packet->dhcpv6_container_packet != NULL)
+ packet = packet->dhcpv6_container_packet;
+
+ /* Check the initial message. */
+ if ((packet->raw == NULL) ||
+ (packet->client_addr.len != 16) ||
+ (packet->interface == NULL)) {
+ log_error("forw_dhcpv4_query: can't find initial message.");
+ return;
+ }
+
+ /* Get a buffer. */
+ len = packet->packet_length + 32;
+ memset(&ds, 0, sizeof(ds));
+ if (!buffer_allocate(&ds.buffer, len, MDL)) {
+ log_error("forw_dhcpv4_query: "
+ "no memory for encapsulating packet.");
+ return;
+ }
+ ds.data = ds.buffer->data;
+ ds.len = len;
+
+ /* Fill the buffer. */
+ strncpy((char *)ds.buffer->data, packet->interface->name, 16);
+ memcpy(ds.buffer->data + 16,
+ packet->client_addr.iabuf, 16);
+ memcpy(ds.buffer->data + 32,
+ (unsigned char *)packet->raw,
+ packet->packet_length);
+
+ /* Forward to the DHCPv4 server. */
+ cc = send(dhcp4o6_fd, ds.data, ds.len, 0);
+ if (cc < 0)
+ log_error("forw_dhcpv4_query: send(): %m");
+ data_string_forget(&ds, MDL);
+}
+#endif
+
static void
dhcpv6_discard(struct packet *packet) {
/* INSIST(packet->msg_type > 0); */
@@ -6574,6 +7239,11 @@ build_dhcpv6_reply(struct data_string *reply, struct packet *packet) {
dhcpv6_information_request(reply, packet);
break;
case DHCPV6_RELAY_FORW:
+#ifdef DHCP4o6
+ if (dhcpv4_over_dhcpv6 && (local_family == AF_INET))
+ dhcp4o6_relay_forw(reply, packet);
+ else
+#endif /* DHCP4o6 */
dhcpv6_relay_forw(reply, packet);
break;
case DHCPV6_RELAY_REPL:
@@ -6586,6 +7256,21 @@ build_dhcpv6_reply(struct data_string *reply, struct packet *packet) {
case DHCPV6_LEASEQUERY_REPLY:
dhcpv6_discard(packet);
break;
+ case DHCPV6_DHCPV4_QUERY:
+#ifdef DHCP4o6
+ if (dhcpv4_over_dhcpv6) {
+ if (local_family == AF_INET6) {
+ forw_dhcpv4_query(packet);
+ } else {
+ dhcp4o6_dhcpv4_query(reply, packet);
+ }
+ } else
+#endif /* DHCP4o6 */
+ dhcpv6_discard(packet);
+ break;
+ case DHCPV6_DHCPV4_RESPONSE:
+ dhcpv6_discard(packet);
+ break;
default:
/* XXX: would be nice if we had "notice" level,
as syslog, for this */
@@ -6626,7 +7311,8 @@ log_packet_in(const struct packet *packet) {
data_string_sprintfa(&s, ", peer address %s",
inet_ntop(AF_INET6, addr,
tmp_addr, sizeof(tmp_addr)));
- } else {
+ } else if ((packet->dhcpv6_msg_type != DHCPV6_DHCPV4_QUERY) &&
+ (packet->dhcpv6_msg_type != DHCPV6_DHCPV4_RESPONSE)) {
tid = 0;
memcpy(((char *)&tid)+1, packet->dhcpv6_transaction_id, 3);
data_string_sprintfa(&s, ", transaction ID 0x%06X", tid);
@@ -6712,6 +7398,203 @@ dhcpv6(struct packet *packet) {
}
}
+#ifdef DHCP4o6
+/*
+ * \brief Receive a DHCPv4-query message from the DHCPv6 side
+ * (DHCPv4 server function)
+ *
+ * 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
+ *
+ * \param raw the DHCPv6 DHCPv4-query message raw content
+ */
+static void recv_dhcpv4_query(struct data_string *raw) {
+ struct interface_info *ip;
+ char name[16 + 1];
+ struct iaddr iaddr;
+ struct packet *packet;
+ unsigned char msg_type;
+ const struct dhcpv6_relay_packet *relay;
+ const struct dhcpv4_over_dhcpv6_packet *msg;
+ struct data_string reply;
+ struct data_string ds;
+ unsigned len;
+ int cc;
+
+ memset(name, 0, sizeof(name));
+ memcpy(name, raw->data, 16);
+ for (ip = interfaces; ip != NULL; ip = ip->next) {
+ if (!strcmp(name, ip->name))
+ break;
+ }
+ if (ip == NULL) {
+ log_error("recv_dhcpv4_query: can't find interface %s.",
+ name);
+ return;
+ }
+
+ iaddr.len = 16;
+ memcpy(iaddr.iabuf, raw->data + 16, 16);
+
+ /*
+ * From do_packet6().
+ */
+
+ if (!packet6_len_okay((char *)raw->data + 32, raw->len - 32)) {
+ log_error("recv_dhcpv4_query: "
+ "short packet from %s, len %d, dropped",
+ piaddr(iaddr), raw->len - 32);
+ return;
+ }
+
+ /*
+ * Build a packet structure.
+ */
+ packet = NULL;
+ if (!packet_allocate(&packet, MDL)) {
+ log_error("recv_dhcpv4_query: no memory for packet.");
+ return;
+ }
+
+ if (!option_state_allocate(&packet->options, MDL)) {
+ log_error("recv_dhcpv4_query: no memory for options.");
+ packet_dereference(&packet, MDL);
+ return;
+ }
+
+ packet->raw = (struct dhcp_packet *)(raw->data + 32);
+ packet->packet_length = raw->len - 32;
+ packet->client_port = remote_port;
+ packet->client_addr = iaddr;
+ interface_reference(&packet->interface, ip, MDL);
+
+ msg_type = raw->data[32];
+ 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);
+ packet->dhcpv6_msg_type = relay->msg_type;
+
+ /* relay-specific data */
+ packet->dhcpv6_hop_count = relay->hop_count;
+ memcpy(&packet->dhcpv6_link_address,
+ relay->link_address, sizeof(relay->link_address));
+ memcpy(&packet->dhcpv6_peer_address,
+ relay->peer_address, sizeof(relay->peer_address));
+
+ if (!parse_option_buffer(packet->options,
+ relay->options,
+ raw->len - 32 - relaylen,
+ &dhcpv6_universe)) {
+ /* no logging here, as parse_option_buffer() logs all
+ cases where it fails */
+ packet_dereference(&packet, MDL);
+ return;
+ }
+ } else if ((msg_type == DHCPV6_DHCPV4_QUERY) ||
+ (msg_type == DHCPV6_DHCPV4_RESPONSE)) {
+ int msglen =
+ (int)(offsetof(struct dhcpv4_over_dhcpv6_packet, options));
+ msg = (struct dhcpv4_over_dhcpv6_packet *)(raw->data + 32);
+ packet->dhcpv6_msg_type = msg->msg_type;
+
+ /* message-specific data */
+ memcpy(packet->dhcp4o6_flags, msg->flags,
+ sizeof(packet->dhcp4o6_flags));
+
+ if (!parse_option_buffer(packet->options,
+ msg->options,
+ raw->len - 32 - msglen,
+ &dhcpv6_universe)) {
+ /* no logging here, as parse_option_buffer() logs all
+ cases where it fails */
+ packet_dereference(&packet, MDL);
+ return;
+ }
+ } else {
+ log_error("recv_dhcpv4_query: unexpected message of type %d.",
+ (int)msg_type);
+ packet_dereference(&packet, MDL);
+ return;
+ }
+
+ /*
+ * From dhcpv6().
+ */
+
+ /*
+ * Log a message that we received this packet.
+ */
+ /* log_packet_in(packet); */
+ memset(&ds, 0, sizeof(ds));
+ if (packet->dhcpv6_msg_type < dhcpv6_type_name_max) {
+ data_string_sprintfa(&ds, "%s message from %s",
+ dhcpv6_type_names[packet->dhcpv6_msg_type],
+ piaddr(packet->client_addr));
+ } else {
+ data_string_sprintfa(&ds,
+ "Unknown message type %d from %s",
+ packet->dhcpv6_msg_type,
+ piaddr(packet->client_addr));
+ }
+ if ((packet->dhcpv6_msg_type == DHCPV6_RELAY_FORW) ||
+ (packet->dhcpv6_msg_type == DHCPV6_RELAY_REPL)) {
+ char tmp_addr[INET6_ADDRSTRLEN];
+ const void *addr;
+
+ addr = &packet->dhcpv6_link_address;
+ data_string_sprintfa(&ds, ", link address %s",
+ inet_ntop(AF_INET6, addr,
+ tmp_addr, sizeof(tmp_addr)));
+ addr = &packet->dhcpv6_peer_address;
+ data_string_sprintfa(&ds, ", peer address %s",
+ inet_ntop(AF_INET6, addr,
+ tmp_addr, sizeof(tmp_addr)));
+ } else if ((packet->dhcpv6_msg_type != DHCPV6_DHCPV4_QUERY) &&
+ (packet->dhcpv6_msg_type != DHCPV6_DHCPV4_RESPONSE)) {
+ u_int32_t tid = 0;
+
+ memcpy(((char *)&tid)+1, packet->dhcpv6_transaction_id, 3);
+ data_string_sprintfa(&ds, ", transaction ID 0x%06X", tid);
+ }
+ log_info("%s", ds.data);
+ data_string_forget(&ds, MDL);
+
+ /*
+ * Build our reply packet.
+ */
+ build_dhcpv6_reply(&reply, packet);
+
+ packet_dereference(&packet, MDL);
+
+ if (reply.data == NULL)
+ return;
+
+ /*
+ * Forward the response.
+ */
+ len = reply.len + 32;
+ memset(&ds, 0, sizeof(ds));
+ if (!buffer_allocate(&ds.buffer, len, MDL)) {
+ log_error("recv_dhcpv4_query: no memory.");
+ return;
+ }
+ ds.data = ds.buffer->data;
+ ds.len = len;
+
+ memcpy(ds.buffer->data, name, 16);
+ memcpy(ds.buffer->data + 16, iaddr.iabuf, 16);
+ memcpy(ds.buffer->data + 32, reply.data, reply.len);
+ cc = send(dhcp4o6_fd, ds.data, ds.len, 0);
+ if (cc < 0)
+ log_error("recv_dhcpv4_query: send(): %m");
+ data_string_forget(&ds, MDL);
+}
+#endif /* DHCP4o6 */
+
static void
seek_shared_host(struct host_decl **hp, struct shared_network *shared) {
struct host_decl *nofixed = NULL;
@@ -6803,7 +7686,7 @@ unicast_reject(struct data_string *reply_ret,
struct reply_state reply;
memset(&reply, 0x0, sizeof(struct reply_state));
- /* Locate the client. */
+ /* Locate the client. */
if (shared_network_from_packet6(&reply.shared, packet)
!= ISC_R_SUCCESS) {
log_error("unicast_reject: could not locate client.");
diff --git a/server/mdb.c b/server/mdb.c
index ed89b5b3..148659fb 100644
--- a/server/mdb.c
+++ b/server/mdb.c
@@ -914,6 +914,10 @@ int find_subnet (struct subnet **sp,
struct subnet *rv;
for (rv = subnets; rv; rv = rv -> next_subnet) {
+#if defined(DHCP4o6)
+ if (addr.len != rv->netmask.len)
+ continue;
+#endif
if (addr_eq (subnet_number (addr, rv -> netmask), rv -> net)) {
if (subnet_reference (sp, rv,
file, line) != ISC_R_SUCCESS)
@@ -931,6 +935,10 @@ int find_grouped_subnet (struct subnet **sp,
struct subnet *rv;
for (rv = share -> subnets; rv; rv = rv -> next_sibling) {
+#if defined(DHCP4o6)
+ if (addr.len != rv->netmask.len)
+ continue;
+#endif
if (addr_eq (subnet_number (addr, rv -> netmask), rv -> net)) {
if (subnet_reference (sp, rv,
file, line) != ISC_R_SUCCESS)
@@ -946,6 +954,10 @@ int
subnet_inner_than(const struct subnet *subnet,
const struct subnet *scan,
int warnp) {
+#if defined(DHCP4o6)
+ if (subnet->net.len != scan->net.len)
+ return 0;
+#endif
if (addr_eq(subnet_number(subnet->net, scan->netmask), scan->net) ||
addr_eq(subnet_number(scan->net, subnet->netmask), subnet->net)) {
char n1buf[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255")];
diff --git a/server/stables.c b/server/stables.c
index d375bad6..f34a6721 100644
--- a/server/stables.c
+++ b/server/stables.c
@@ -234,7 +234,7 @@ static struct option server_options[] = {
{ "limit-addrs-per-ia", "L", &server_universe, 56, 1 },
{ "limit-prefs-per-ia", "L", &server_universe, 57, 1 },
/* Assert a configuration parsing error if delayed-ack isn't compiled in. */
-#if defined(DELAYED_ACK)
+#if defined(DELAYED_ACK) && !defined(DHCP4o6)
{ "delayed-ack", "S", &server_universe, 58, 1 },
{ "max-ack-delay", "L", &server_universe, 59, 1 },
#endif