]> git.ipfire.org Git - thirdparty/dhcp.git/commitdiff
Merged rt35711c (DHCPv4-over-DHCPv6 support)
authorFrancis Dupont <fdupont@isc.org>
Tue, 23 Feb 2016 09:40:10 +0000 (10:40 +0100)
committerFrancis Dupont <fdupont@isc.org>
Tue, 23 Feb 2016 09:40:10 +0000 (10:40 +0100)
27 files changed:
RELNOTES
client/clparse.c
client/dhc6.c
client/dhclient.8
client/dhclient.c
common/Makefile.am
common/Makefile.in
common/discover.c
common/inet.c
common/options.c
common/tables.c
configure
configure.ac
includes/config.h.in
includes/dhcp6.h
includes/dhcpd.h
includes/site.h
relay/dhcrelay.c
server/bootp.c
server/confpars.c
server/dhcp.c
server/dhcpd.8
server/dhcpd.c
server/dhcpleasequery.c
server/dhcpv6.c
server/mdb.c
server/stables.c

index aef9a85ac31cc336620bc46fa2ab29e313fc1d09..192a08b82f64ef6e8584a56569c749c1d46592ff 100644 (file)
--- 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
index 320c42f5728eacddb746238207c32451efd52128..643b3faed32a0b9bbdee0f427ded99b08b383400 100644 (file)
@@ -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);
 
index aacd4aea604e7980ac497ff086179c8700f3e34a..c8d16e8a5fa6fd0e7045909d4ebcf558f2a20ae1 100644 (file)
@@ -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) {
index 83260e61ab65b107dcb5e444f293872c0044b088..1946d9bb3a26c0159a38794774cf9e3f4e6c7d9f 100644 (file)
@@ -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
index df7f8836e930d2451afba0d864d9af2698345bc0..d1d83a0134b45629813ee4849cf59e1fa56e718c 100644 (file)
@@ -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 */
index c579719a187c2af84973f637cbb10f251abbb5e2..113aee842b07b85f39e79faf88c6379183915633 100644 (file)
@@ -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)
 
index a8d3c651f54a2d8134bb90fc40e74959f228035a..e78e4f7ca76fde4f81f9d27a58c86726d925d470 100644 (file)
@@ -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@
index 3cd64a75c1becd4430ed96ddbfe53deac9127de8..b5f297e7580a27a8feb802cdb6afc5be37c93c38 100644 (file)
@@ -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();
        }
index 0cff19d0dc61bdc071c41e3ea00b36ac22001b45..52852d8300a39eb5130218fb26e8c80f400b8f46 100644 (file)
@@ -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);
+}
index 5abccf89cd688a4838d23b7b887316e8f71bd721..50271f22007facbe1b02df9604a953c592081387 100644 (file)
@@ -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;
index 1c9360cbcf5b1eb4e163a37faedeaff89bb1c971..7617b7ef762dd1c0dbb16920f83e32367d7da53d 100644 (file)
@@ -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 }
 };
 
index edf0de5a0f21450c04430c045d14faaecb37c312..5c980452be964cc8a23192b7d66f98171f8bb9d6 100755 (executable)
--- 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
index 9351150a63781155b49c6c384007fe0629e67515..7f05b7d6ae27674e3a4941fed2d9773708119965 100644 (file)
@@ -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
index 80f3b083be8571a551df1d71a86414768a1f673f..403a28f1a8cfe60537b857f8ad39c825477b4eef 100644 (file)
@@ -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
index 03fedfa8f7b34cab06f09b878da3d118d7299fc8..4d7a9e34fa8cf3c9ef426a49888fa6d4f7cf9a28 100644 (file)
 #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
 #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 
 #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.
index bdd00c6db2ea608021123d86d35033d72075e3d8..4b3001a847c4fbd685c131d34b3b4e80ec155cfe 100644 (file)
@@ -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);
index 0586717eddd2c1fd76e95e5c84b54eb982e41026..944dbefc7b6994ec71e929e4840871b5b5e9d116 100644 (file)
 #define RFC6939_OPTIONS
 #define RFC6977_OPTIONS
 #define RFC7083_OPTIONS
+#define RFC7341_OPTIONS
 #define RFC7618_OPTIONS
 #define RFC7710_OPTIONS
index e06eb4c2aa07d475f5d38f16e5b3725e7c38d23c..9ec39cd1dd122dc855f23829a54b62f90365ef2e 100644 (file)
@@ -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),
index ca54be6465bcc30df9beb8e32623123ce9008172..2e752c5b6832bd01b399e98b01e37ec516a0169e 100644 (file)
@@ -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;
index bc85b3b6ba8b081278fed19e98ced4b0c72a5d0c..1b37a22eb5a3200957284329f788bbffc9622d5d 100644 (file)
@@ -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);
index dcbadc39b90524836cce733d9ed1ac7f060450bb..a823a4e834b75d5720f52470eea6f31abb664a25 100644 (file)
@@ -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;
index bfda6397400f793a6538b10ba92bc66ef130dde6..5546676669ded9ebdd2693aa79e2c42c7549a28a 100644 (file)
@@ -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
index abc63df9bb4a6736f9ee900607425f150f9721ac..145561c06040c6f568e3b32f41ea0cfd05a6b53d 100644 (file)
@@ -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
index 0766b849983dedcafbb03f7848ae8fa33a74bfc2..91ca870ccf88e84e76ff77635debdf88d106e402 100644 (file)
@@ -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));
index d85d0b070cfe7e16c2ab2583d9d8dca793c39583..18d8bb8875703f7cbc55da4d2b5b83d01dc7caa9 100644 (file)
 
 #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,176 +6644,727 @@ 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
-dhcpv6_discard(struct packet *packet) {
-       /* INSIST(packet->msg_type > 0); */
-       /* INSIST(packet->msg_type < dhcpv6_type_name_max); */
-
-       log_debug("Discarding %s from %s; message type not handled by server",
-                 dhcpv6_type_names[packet->dhcpv6_msg_type],
-                 piaddr(packet->client_addr));
-}
-
-static void
-build_dhcpv6_reply(struct data_string *reply, struct packet *packet) {
-       memset(reply, 0, sizeof(*reply));
+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;
 
-       /* I would like to classify the client once here, but
-        * as I don't want to classify all of the incoming packets
-        * I need to do it before handling specific types.
-        * We don't need to classify if we are tossing the packet
-        * or if it is a relay - the classification step will get
-        * done when we process the inner client packet.
+       /*
+        * 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;
 
-       switch (packet->dhcpv6_msg_type) {
-               case DHCPV6_SOLICIT:
-                       classify_client(packet);
-                       dhcpv6_solicit(reply, packet);
-                       break;
-               case DHCPV6_ADVERTISE:
-                       dhcpv6_discard(packet);
-                       break;
-               case DHCPV6_REQUEST:
-                       classify_client(packet);
-                       dhcpv6_request(reply, packet);
-                       break;
-               case DHCPV6_CONFIRM:
-                       classify_client(packet);
-                       dhcpv6_confirm(reply, packet);
-                       break;
-               case DHCPV6_RENEW:
-                       classify_client(packet);
-                       dhcpv6_renew(reply, packet);
-                       break;
-               case DHCPV6_REBIND:
-                       classify_client(packet);
-                       dhcpv6_rebind(reply, packet);
-                       break;
-               case DHCPV6_REPLY:
-                       dhcpv6_discard(packet);
-                       break;
-               case DHCPV6_RELEASE:
-                       classify_client(packet);
-                       dhcpv6_release(reply, packet);
-                       break;
-               case DHCPV6_DECLINE:
-                       classify_client(packet);
-                       dhcpv6_decline(reply, packet);
-                       break;
-               case DHCPV6_RECONFIGURE:
-                       dhcpv6_discard(packet);
-                       break;
-               case DHCPV6_INFORMATION_REQUEST:
-                       classify_client(packet);
-                       dhcpv6_information_request(reply, packet);
-                       break;
-               case DHCPV6_RELAY_FORW:
-                       dhcpv6_relay_forw(reply, packet);
-                       break;
-               case DHCPV6_RELAY_REPL:
-                       dhcpv6_discard(packet);
-                       break;
-               case DHCPV6_LEASEQUERY:
-                       classify_client(packet);
-                       dhcpv6_leasequery(reply, packet);
-                       break;
-               case DHCPV6_LEASEQUERY_REPLY:
-                       dhcpv6_discard(packet);
-                       break;
-               default:
-                       /* XXX: would be nice if we had "notice" level,
-                               as syslog, for this */
-                       log_info("Discarding unknown DHCPv6 message type %d "
-                                "from %s", packet->dhcpv6_msg_type,
-                                piaddr(packet->client_addr));
+       /*
+        * 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;
        }
-}
-
-static void
-log_packet_in(const struct packet *packet) {
-       struct data_string s;
-       u_int32_t tid;
-       char tmp_addr[INET6_ADDRSTRLEN];
-       const void *addr;
 
-       memset(&s, 0, sizeof(s));
-
-       if (packet->dhcpv6_msg_type < dhcpv6_type_name_max) {
-               data_string_sprintfa(&s, "%s message from %s port %d",
-                                    dhcpv6_type_names[packet->dhcpv6_msg_type],
-                                    piaddr(packet->client_addr),
-                                    ntohs(packet->client_port));
-       } else {
-               data_string_sprintfa(&s,
-                                    "Unknown message type %d from %s port %d",
-                                    packet->dhcpv6_msg_type,
-                                    piaddr(packet->client_addr),
-                                    ntohs(packet->client_port));
+       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 ((packet->dhcpv6_msg_type == DHCPV6_RELAY_FORW) ||
-           (packet->dhcpv6_msg_type == DHCPV6_RELAY_REPL)) {
-               addr = &packet->dhcpv6_link_address;
-               data_string_sprintfa(&s, ", link address %s",
-                                    inet_ntop(AF_INET6, addr,
-                                              tmp_addr, sizeof(tmp_addr)));
-               addr = &packet->dhcpv6_peer_address;
-               data_string_sprintfa(&s, ", peer address %s",
-                                    inet_ntop(AF_INET6, addr,
-                                              tmp_addr, sizeof(tmp_addr)));
-       } else {
-               tid = 0;
-               memcpy(((char *)&tid)+1, packet->dhcpv6_transaction_id, 3);
-               data_string_sprintfa(&s, ", transaction ID 0x%06X", tid);
-
-/*
-               oc = lookup_option(&dhcpv6_universe, packet->options,
-                                  D6O_CLIENTID);
-               if (oc != NULL) {
-                       memset(&tmp_ds, 0, sizeof(tmp_ds_));
-                       if (!evaluate_option_cache(&tmp_ds, packet, NULL, NULL,
-                                                  packet->options, NULL,
-                                                  &global_scope, oc, MDL)) {
-                               log_error("Error evaluating Client Identifier");
-                       } else {
-                               data_strint_sprintf(&s, ", client ID %s",
-
-                               data_string_forget(&tmp_ds, MDL);
-                       }
-               }
-*/
 
+       if (!packet6_len_okay((char *)enc_opt_data.data, enc_opt_data.len)) {
+               log_error("dhcp4o6_relay_forw: "
+                         "encapsulated packet too short.");
+               goto exit;
        }
-       log_info("%s", s.data);
-
-       data_string_forget(&s, MDL);
-}
-
-void
-dhcpv6(struct packet *packet) {
-       struct data_string reply;
-       struct sockaddr_in6 to_addr;
-       int send_ret;
 
        /*
-        * Log a message that we received this packet.
+        * Build a packet structure from this encapsulated packet.
         */
-       log_packet_in(packet);
+       if (!packet_allocate(&enc_packet, MDL)) {
+               log_error("dhcp4o6_relay_forw: "
+                         "no memory for encapsulated packet.");
+               goto exit;
+       }
 
-       /*
-        * Build our reply packet.
-        */
-       build_dhcpv6_reply(&reply, packet);
+       if (!option_state_allocate(&enc_packet->options, MDL)) {
+               log_error("dhcp4o6_relay_forw: "
+                         "no memory for encapsulated packet's options.");
+               goto exit;
+       }
 
-       if (reply.data != NULL) {
-               /*
-                * Send our reply, if we have one.
-                */
-               memset(&to_addr, 0, sizeof(to_addr));
-               to_addr.sin6_family = AF_INET6;
-               if ((packet->dhcpv6_msg_type == DHCPV6_RELAY_FORW) ||
-                   (packet->dhcpv6_msg_type == DHCPV6_RELAY_REPL)) {
-                       to_addr.sin6_port = local_port;
+       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); */
+       /* INSIST(packet->msg_type < dhcpv6_type_name_max); */
+
+       log_debug("Discarding %s from %s; message type not handled by server",
+                 dhcpv6_type_names[packet->dhcpv6_msg_type],
+                 piaddr(packet->client_addr));
+}
+
+static void
+build_dhcpv6_reply(struct data_string *reply, struct packet *packet) {
+       memset(reply, 0, sizeof(*reply));
+
+       /* I would like to classify the client once here, but
+        * as I don't want to classify all of the incoming packets
+        * I need to do it before handling specific types.
+        * We don't need to classify if we are tossing the packet
+        * or if it is a relay - the classification step will get
+        * done when we process the inner client packet.
+        */
+
+       switch (packet->dhcpv6_msg_type) {
+               case DHCPV6_SOLICIT:
+                       classify_client(packet);
+                       dhcpv6_solicit(reply, packet);
+                       break;
+               case DHCPV6_ADVERTISE:
+                       dhcpv6_discard(packet);
+                       break;
+               case DHCPV6_REQUEST:
+                       classify_client(packet);
+                       dhcpv6_request(reply, packet);
+                       break;
+               case DHCPV6_CONFIRM:
+                       classify_client(packet);
+                       dhcpv6_confirm(reply, packet);
+                       break;
+               case DHCPV6_RENEW:
+                       classify_client(packet);
+                       dhcpv6_renew(reply, packet);
+                       break;
+               case DHCPV6_REBIND:
+                       classify_client(packet);
+                       dhcpv6_rebind(reply, packet);
+                       break;
+               case DHCPV6_REPLY:
+                       dhcpv6_discard(packet);
+                       break;
+               case DHCPV6_RELEASE:
+                       classify_client(packet);
+                       dhcpv6_release(reply, packet);
+                       break;
+               case DHCPV6_DECLINE:
+                       classify_client(packet);
+                       dhcpv6_decline(reply, packet);
+                       break;
+               case DHCPV6_RECONFIGURE:
+                       dhcpv6_discard(packet);
+                       break;
+               case DHCPV6_INFORMATION_REQUEST:
+                       classify_client(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:
+                       dhcpv6_discard(packet);
+                       break;
+               case DHCPV6_LEASEQUERY:
+                       classify_client(packet);
+                       dhcpv6_leasequery(reply, packet);
+                       break;
+               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 */
+                       log_info("Discarding unknown DHCPv6 message type %d "
+                                "from %s", packet->dhcpv6_msg_type,
+                                piaddr(packet->client_addr));
+       }
+}
+
+static void
+log_packet_in(const struct packet *packet) {
+       struct data_string s;
+       u_int32_t tid;
+       char tmp_addr[INET6_ADDRSTRLEN];
+       const void *addr;
+
+       memset(&s, 0, sizeof(s));
+
+       if (packet->dhcpv6_msg_type < dhcpv6_type_name_max) {
+               data_string_sprintfa(&s, "%s message from %s port %d",
+                                    dhcpv6_type_names[packet->dhcpv6_msg_type],
+                                    piaddr(packet->client_addr),
+                                    ntohs(packet->client_port));
+       } else {
+               data_string_sprintfa(&s,
+                                    "Unknown message type %d from %s port %d",
+                                    packet->dhcpv6_msg_type,
+                                    piaddr(packet->client_addr),
+                                    ntohs(packet->client_port));
+       }
+       if ((packet->dhcpv6_msg_type == DHCPV6_RELAY_FORW) ||
+           (packet->dhcpv6_msg_type == DHCPV6_RELAY_REPL)) {
+               addr = &packet->dhcpv6_link_address;
+               data_string_sprintfa(&s, ", link address %s",
+                                    inet_ntop(AF_INET6, addr,
+                                              tmp_addr, sizeof(tmp_addr)));
+               addr = &packet->dhcpv6_peer_address;
+               data_string_sprintfa(&s, ", 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)) {
+               tid = 0;
+               memcpy(((char *)&tid)+1, packet->dhcpv6_transaction_id, 3);
+               data_string_sprintfa(&s, ", transaction ID 0x%06X", tid);
+
+/*
+               oc = lookup_option(&dhcpv6_universe, packet->options,
+                                  D6O_CLIENTID);
+               if (oc != NULL) {
+                       memset(&tmp_ds, 0, sizeof(tmp_ds_));
+                       if (!evaluate_option_cache(&tmp_ds, packet, NULL, NULL,
+                                                  packet->options, NULL,
+                                                  &global_scope, oc, MDL)) {
+                               log_error("Error evaluating Client Identifier");
+                       } else {
+                               data_strint_sprintf(&s, ", client ID %s",
+
+                               data_string_forget(&tmp_ds, MDL);
+                       }
+               }
+*/
+
+       }
+       log_info("%s", s.data);
+
+       data_string_forget(&s, MDL);
+}
+
+void
+dhcpv6(struct packet *packet) {
+       struct data_string reply;
+       struct sockaddr_in6 to_addr;
+       int send_ret;
+
+       /*
+        * Log a message that we received this packet.
+        */
+       log_packet_in(packet);
+
+       /*
+        * Build our reply packet.
+        */
+       build_dhcpv6_reply(&reply, packet);
+
+       if (reply.data != NULL) {
+               /*
+                * Send our reply, if we have one.
+                */
+               memset(&to_addr, 0, sizeof(to_addr));
+               to_addr.sin6_family = AF_INET6;
+               if ((packet->dhcpv6_msg_type == DHCPV6_RELAY_FORW) ||
+                   (packet->dhcpv6_msg_type == DHCPV6_RELAY_REPL)) {
+                       to_addr.sin6_port = local_port;
                } else {
                        to_addr.sin6_port = remote_port;
                }
@@ -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.");
index ed89b5b30a34cf855daa68961af6d7056344cad7..148659fb5a458de448e7b77c708d826309798542 100644 (file)
@@ -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")];
index d375bad604fbe4d2a54d3233948c981a983bd7f5..f34a6721ca1be46e5a7f048c955e0d874de93973 100644 (file)
@@ -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