summaryrefslogtreecommitdiff
path: root/client/dhclient.c
diff options
context:
space:
mode:
Diffstat (limited to 'client/dhclient.c')
-rw-r--r--client/dhclient.c205
1 files changed, 177 insertions, 28 deletions
diff --git a/client/dhclient.c b/client/dhclient.c
index 57d53adc..46dc3a71 100644
--- a/client/dhclient.c
+++ b/client/dhclient.c
@@ -3,7 +3,7 @@
DHCP Client. */
/*
- * Copyright (c) 2004-2021 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2004-2022 by Internet Systems Consortium, Inc. ("ISC")
* Copyright (c) 1995-2003 by Internet Software Consortium
*
* This Source Code Form is subject to the terms of the Mozilla Public
@@ -19,8 +19,8 @@
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Internet Systems Consortium, Inc.
- * 950 Charter Street
- * Redwood City, CA 94063
+ * PO Box 360
+ * Newmarket, NH 03857 USA
* <info@isc.org>
* https://www.isc.org/
*
@@ -77,14 +77,15 @@ int decline_wait_time = 10; /* Default to 10 secs per, RFC 2131, 3.1.5 */
#define ASSERT_STATE(state_is, state_shouldbe) {}
#ifndef UNIT_TEST
-static const char copyright[] = "Copyright 2004-2021 Internet Systems Consortium.";
+static const char copyright[] = "Copyright 2004-2022 Internet Systems Consortium.";
static const char arr [] = "All rights reserved.";
static const char message [] = "Internet Systems Consortium DHCP Client";
static const char url [] = "For info, please visit https://www.isc.org/software/dhcp/";
#endif /* UNIT_TEST */
-u_int16_t local_port = 0;
-u_int16_t remote_port = 0;
+extern u_int16_t local_port;
+extern u_int16_t remote_port;
+
#if defined(DHCPv6) && defined(DHCP4o6)
int dhcp4o6_state = -1; /* -1 = stopped, 0 = polling, 1 = started */
#endif
@@ -136,10 +137,10 @@ static void dhclient_ddns_cb_free(dhcp_ddns_cb_t *ddns_cb,
* the description of the command line. The arguments provide
* a way for the caller to request more specific information about
* the error be printed as well. Mostly this will be that some
- * comamnd doesn't include its argument.
+ * command doesn't include its argument.
*
* \param sfmt - The basic string and format for the specific error
- * \param sarg - Generally the offending argument from the comamnd line.
+ * \param sarg - Generally the offending argument from the command line.
*
* \return Nothing
*/
@@ -566,7 +567,7 @@ main(int argc, char **argv) {
} else if (argv[i][0] == '-') {
usage("Unknown command: %s", argv[i]);
} else if (interfaces_requested < 0) {
- usage("No interfaces comamnd -n and "
+ usage("No interfaces command -n and "
" requested interface %s", argv[i]);
} else {
struct interface_info *tmp = NULL;
@@ -828,21 +829,36 @@ main(int argc, char **argv) {
? DISCOVER_REQUESTED
: DISCOVER_RUNNING);
- /* Make up a seed for the random number generator from current
- time plus the sum of the last four bytes of each
- interface's hardware address interpreted as an integer.
- Not much entropy, but we're booting, so we're not likely to
- find anything better. */
+ /* PLEASE PREFER the random device: not all systems use random
+ * process identifiers so the alternative can be predictable. */
seed = 0;
- for (ip = interfaces; ip; ip = ip->next) {
- int junk;
- memcpy(&junk,
- &ip->hw_address.hbuf[ip->hw_address.hlen -
- sizeof seed], sizeof seed);
- seed += junk;
+ size_t nrnd = 0;
+#ifdef ISC_PATH_RANDOMDEV
+ FILE *frnd = fopen(ISC_PATH_RANDOMDEV, "r");
+ if (frnd) {
+ nrnd = fread(&seed, sizeof(seed), 1, frnd);
+ fclose(frnd);
}
- srandom(seed + cur_time + (unsigned)getpid());
+#endif
+ /* Please leave the compiler to emit a warning about a constant
+ * condition in the if test. */
+ if (!nrnd) {
+ /* Make up a seed for the random number generator from current
+ time plus the sum of the last four bytes of each
+ interface's hardware address interpreted as an integer.
+ Not much entropy, but we're booting, so we're not likely to
+ find anything better. */
+ for (ip = interfaces; ip; ip = ip->next) {
+ int junk;
+ memcpy(&junk,
+ &ip->hw_address.hbuf[ip->hw_address.hlen -
+ sizeof seed], sizeof seed);
+ seed += junk;
+ }
+ seed += cur_time + (unsigned)getpid();
+ }
+ srandom(seed);
/*
* Establish a default DUID. We always do so for v6 and
@@ -1241,6 +1257,105 @@ void state_init (cpp)
send_discover (client);
}
+/* check_v6only is called by dhcpoffer and dhcpack. It checks if a
+ * requested v6-only-preferred option is present and returned the
+ * V6ONLY_WAIT delay to suspend DHCPv4. */
+
+uint32_t check_v6only(packet, client)
+ struct packet *packet;
+ struct client_state *client;
+{
+ int i;
+ struct option **req;
+ isc_boolean_t found = ISC_FALSE;
+ struct option_cache *oc;
+ struct data_string data;
+ uint32_t v6only_wait = 0;
+
+ /* Check if the v6-only-preferred was requested. */
+ req = client->config->requested_options;
+
+ if (req == NULL)
+ return 0;
+
+ for (i = 0 ; req[i] != NULL ; i++) {
+ if ((req[i]->universe == &dhcp_universe) &&
+ (req[i]->code == DHO_V6_ONLY_PREFERRED)) {
+ found = ISC_TRUE;
+ break;
+ }
+ }
+
+ if (found == ISC_FALSE)
+ return 0;
+
+ /* Get the V6ONLY_WAIT timer. */
+ oc = lookup_option(&dhcp_universe, packet->options,
+ DHO_V6_ONLY_PREFERRED);
+ if (!oc)
+ return 0;
+
+ memset(&data, 0, sizeof(data));
+
+ if (evaluate_option_cache(&data, packet, (struct lease *)0, client,
+ packet->options, (struct option_state *)0,
+ &global_scope, oc, MDL)) {
+ if (data.len == 4) {
+ v6only_wait = getULong(data.data);
+ if (v6only_wait < MIN_V6ONLY_WAIT)
+ v6only_wait = MIN_V6ONLY_WAIT;
+ }
+ data_string_forget(&data, MDL);
+ }
+
+ return (v6only_wait);
+}
+
+/* finish_v6only is called when the V6ONLY_WAIT timer expired. */
+
+void finish_v6only(cpp)
+ void *cpp;
+{
+ struct client_state *client = cpp;
+
+ cancel_timeout(finish_v6only, client);
+ client->state = S_INIT;
+ state_init(cpp);
+}
+
+/*
+ * start_v6only is called when a requested v6-only-preferred option was
+ * returned by the server. */
+
+void start_v6only(client, v6only_wait)
+ struct client_state *client;
+ uint32_t v6only_wait;
+{
+ struct timeval tv;
+
+ /* Enter V6ONLY state. */
+
+ client->state = S_V6ONLY;
+
+ /* Run the client script. */
+ script_init(client, "V6ONLY", NULL);
+ if (client->active) {
+ script_write_params(client, "old_", client->active);
+ destroy_client_lease(client->active);
+ client->active = NULL;
+ }
+ script_write_requested(client);
+ client_envadd(client, "", "v6-only-preferred", "%lu",
+ (long unsigned)v6only_wait);
+ script_go(client);
+
+ /* Trigger finish_v6only after V6ONLY_WAIT seconds. */
+ tv.tv_sec = cur_tv.tv_sec + v6only_wait;
+ tv.tv_usec = cur_tv.tv_usec;
+
+ add_timeout(&tv, finish_v6only, client, 0, 0);
+}
+
/*
* state_selecting is called when one or more DHCPOFFER packets have been
* received and a configurable period of time has passed.
@@ -1336,6 +1451,7 @@ void dhcpack (packet)
{
struct interface_info *ip = packet -> interface;
struct client_state *client;
+ uint32_t v6only_wait;
struct client_lease *lease;
struct option_cache *oc;
struct data_string ds;
@@ -1371,6 +1487,16 @@ void dhcpack (packet)
inet_ntoa(packet->raw->yiaddr),
piaddr (packet->client_addr));
+ /* Check v6only first. */
+ v6only_wait = check_v6only(packet, client);
+ if (v6only_wait > 0) {
+ log_info("v6 only preferred for %lu seconds.",
+ (long unsigned)v6only_wait);
+ cancel_timeout(send_request, client);
+ start_v6only(client, v6only_wait);
+ return;
+ }
+
lease = packet_to_lease (packet, client);
if (!lease) {
log_info ("packet_to_lease failed.");
@@ -1524,9 +1650,12 @@ void bind_lease (client)
script_write_params(client, "alias_", client->alias);
/* If the BOUND/RENEW code detects another machine using the
- offered address, it exits nonzero. We need to send a
- DHCPDECLINE and toss the lease. */
- if (script_go(client)) {
+ offered address, then per our man page it should exit with
+ a non-zero status, to which we send a DHCPDECLINE and toss
+ the lease. A return value of less than zero indicates
+ the script crashed (e.g. segfault) which script_go will log
+ but we will ignore here. */
+ if (script_go(client) > 0) {
make_decline(client, client->new);
send_decline(client);
destroy_client_lease(client->new);
@@ -1649,6 +1778,7 @@ void state_stop (cpp)
cancel_timeout(send_discover, client);
cancel_timeout(send_request, client);
cancel_timeout(state_bound, client);
+ cancel_timeout(finish_v6only, client);
/* If we have an address, unconfigure it. */
if (client->active) {
@@ -2008,6 +2138,7 @@ void dhcpoffer (packet)
{
struct interface_info *ip = packet -> interface;
struct client_state *client;
+ uint32_t v6only_wait;
struct client_lease *lease, *lp;
struct option **req;
int i;
@@ -2043,6 +2174,16 @@ void dhcpoffer (packet)
inet_ntoa(packet->raw->yiaddr),
piaddr(packet->client_addr));
+ /* Check v6only first. */
+ v6only_wait = check_v6only(packet, client);
+ if (v6only_wait > 0) {
+ log_info("%s: v6 only preferred for %lu.", obuf,
+ (long unsigned)v6only_wait);
+ cancel_timeout(send_discover, client);
+ start_v6only(client, v6only_wait);
+ return;
+ }
+
/* If this lease doesn't supply the minimum required DHCPv4 parameters,
* ignore it.
*/
@@ -3258,12 +3399,12 @@ make_client_options(struct client_state *client, struct client_lease *lease,
hw_idx = 0;
hw_len = client->interface->hw_address.hlen;
}
- memcpy(&client_identifier.buffer->data + 5 - hw_len,
+ memcpy(client_identifier.buffer->data + 5 - hw_len,
client->interface->hw_address.hbuf + hw_idx,
hw_len);
/* Add the default duid */
- memcpy(&client_identifier.buffer->data+(1+4),
+ memcpy(client_identifier.buffer->data + (1 + 4),
default_duid.data, default_duid.len);
/* And save the option */
@@ -4434,8 +4575,14 @@ int script_go(struct client_state *client)
}
dfree (envp, MDL);
gettimeofday(&cur_tv, NULL);
- return (WIFEXITED (wstatus) ?
- WEXITSTATUS (wstatus) : -WTERMSIG (wstatus));
+
+ if (!WIFEXITED(wstatus)) {
+ int sigval = WTERMSIG(wstatus);
+ log_error ("script_go script: %s was terminated by signal %d", scriptName, sigval);
+ return (-sigval);
+ }
+
+ return (WEXITSTATUS(wstatus));
}
void client_envadd (struct client_state *client,
@@ -4622,6 +4769,7 @@ void client_location_changed ()
case S_REBINDING:
case S_STOPPED:
case S_DECLINING:
+ case S_V6ONLY:
break;
}
client -> state = S_INIT;
@@ -4700,6 +4848,7 @@ void do_release(client)
cancel_timeout (state_init, client);
cancel_timeout (send_request, client);
cancel_timeout (state_reboot, client);
+ cancel_timeout (finish_v6only, client);
client -> state = S_STOPPED;
#if defined(DHCPv6) && defined(DHCP4o6)