2
0
mirror of https://gitlab.isc.org/isc-projects/dhcp synced 2025-08-31 06:15:55 +00:00

Several time related improvements:

- set initial delay to 0 to speed up client start
- added 'initial-delay' parameter to possibly revert to old behavior
- better handling of very short (1 or 2s) leases
- client lease records are recorded at most once every 15 seconds
- ICMP ping-check is now timed more precisely
- Servers that don't offer lease-time are now black-listed
[ISC-Bugs #19660]
This commit is contained in:
Tomek Mrugalski
2011-04-21 13:24:24 +00:00
parent 5ce8d6e749
commit 5d082abd92
9 changed files with 219 additions and 71 deletions

View File

@@ -91,6 +91,7 @@ void run_stateless(int exit_mode);
static void usage(void);
static isc_result_t write_duid(struct data_string *duid);
static void add_reject(struct packet *packet);
static int check_domain_name(const char *ptr, size_t len, int dots);
static int check_domain_name_list(const char *ptr, size_t len, int dots);
@@ -608,13 +609,28 @@ main(int argc, char **argv) {
do_release(client);
else {
client->state = S_INIT;
/* Set up a timeout to start the
* initialization process.
*/
tv.tv_sec = cur_time + random() % 5;
tv.tv_usec = 0;
add_timeout(&tv, state_reboot,
client, 0, 0);
if (top_level_config.initial_delay>0)
{
tv.tv_sec = 0;
if (top_level_config.
initial_delay>1)
tv.tv_sec = cur_time
+ random()
% (top_level_config.
initial_delay-1);
tv.tv_usec = random()
% 1000000;
/*
* this gives better
* distribution than just
*whole seconds
*/
add_timeout(&tv, state_reboot,
client, 0, 0);
} else {
state_reboot(client);
}
}
}
}
@@ -1064,21 +1080,34 @@ void dhcpack (packet)
} else
client -> new -> expiry = 0;
if (!client -> new -> expiry) {
if (client->new->expiry == 0) {
struct timeval tv;
log_error ("no expiry time on offered lease.");
/* XXX this is going to be bad - if this _does_
XXX happen, we should probably dynamically
XXX disqualify the DHCP server that gave us the
XXX bad packet from future selections and
XXX then go back into the init state. */
state_init (client);
/* Quench this (broken) server. Return to INIT to reselect. */
add_reject(packet);
/* 1/2 second delay to restart at INIT. */
tv.tv_sec = cur_tv.tv_sec;
tv.tv_usec = cur_tv.tv_usec + 500000;
if (tv.tv_usec >= 1000000) {
tv.tv_sec++;
tv.tv_usec -= 1000000;
}
add_timeout(&tv, state_init, client, 0, 0);
return;
}
/* A number that looks negative here is really just very large,
because the lease expiry offset is unsigned. */
if (client -> new -> expiry < 0)
client -> new -> expiry = TIME_MAX;
/*
* A number that looks negative here is really just very large,
* because the lease expiry offset is unsigned.
*/
if (client->new->expiry < 0)
client->new->expiry = TIME_MAX;
/* Take the server-provided renewal time if there is one. */
oc = lookup_option (&dhcp_universe, client -> new -> options,
DHO_DHCP_RENEWAL_TIME);
@@ -1189,8 +1218,10 @@ void bind_lease (client)
return;
}
/* Write out the new lease. */
write_client_lease (client, client -> new, 0, 0);
/* Write out the new lease if it has been long enough. */
if (!client->last_write ||
(cur_time - client->last_write) >= MIN_LEASE_WRITE)
write_client_lease(client, client->new, 0, 0);
/* Replace the old active lease with the new one. */
if (client -> active)
@@ -1199,9 +1230,10 @@ void bind_lease (client)
client -> new = (struct client_lease *)0;
/* Set up a timeout to start the renewal process. */
tv . tv_sec = client -> active -> renewal;
tv . tv_usec = 0;
add_timeout (&tv, state_bound, client, 0, 0);
tv.tv_sec = client->active->renewal;
tv.tv_usec = ((client->active->renewal - cur_tv.tv_sec) > 1) ?
random() % 1000000 : cur_tv.tv_usec;
add_timeout(&tv, state_bound, client, 0, 0);
log_info ("bound to %s -- renewal in %ld seconds.",
piaddr (client -> active -> address),
@@ -1565,15 +1597,15 @@ void dhcpoffer (packet)
/* If the selecting interval has expired, go immediately to
state_selecting(). Otherwise, time out into
state_selecting at the select interval. */
if (stop_selecting <= 0)
if (stop_selecting <= cur_tv.tv_sec)
state_selecting (client);
else {
tv . tv_sec = stop_selecting;
tv . tv_usec = 0;
add_timeout (&tv, state_selecting, client, 0, 0);
cancel_timeout (send_discover, client);
tv.tv_sec = stop_selecting;
tv.tv_usec = cur_tv.tv_usec;
add_timeout(&tv, state_selecting, client, 0, 0);
cancel_timeout(send_discover, client);
}
log_info ("%s", obuf);
log_info("%s", obuf);
}
/* Allocate a client_lease structure and initialize it from the parameters
@@ -1873,9 +1905,15 @@ void send_discover (cpp)
inaddr_any, &sockaddr_broadcast,
(struct hardware *)0);
tv . tv_sec = cur_time + client -> interval;
tv . tv_usec = 0;
add_timeout (&tv, send_discover, client, 0, 0);
/*
* If we used 0 microseconds here, and there were other clients on the
* same network with a synchronized local clock (ntp), and a similar
* zero-microsecond-scheduler behavior, then we could be participating
* in a sub-second DOS ttck.
*/
tv.tv_sec = cur_tv.tv_sec + client->interval;
tv.tv_usec = client->interval > 1 ? random() % 1000000 : cur_tv.tv_usec;
add_timeout(&tv, send_discover, client, 0, 0);
}
/* state_panic gets called if we haven't received any offers in a preset
@@ -1923,9 +1961,12 @@ void state_panic (cpp)
log_info ("bound: renewal in %ld %s.",
(long)(client -> active -> renewal -
cur_time), "seconds");
tv . tv_sec = client -> active -> renewal;
tv . tv_usec = 0;
add_timeout (&tv, state_bound, client, 0, 0);
tv.tv_sec = client->active->renewal;
tv.tv_usec = ((client->active->renewal -
cur_time) > 1) ?
random() % 1000000 :
cur_tv.tv_usec;
add_timeout(&tv, state_bound, client, 0, 0);
} else {
client -> state = S_BOUND;
log_info ("bound: immediate renewal.");
@@ -1981,11 +2022,11 @@ void state_panic (cpp)
script_write_params (client, "alias_", client -> alias);
script_go (client);
client -> state = S_INIT;
tv . tv_sec = cur_time +
((client -> config -> retry_interval + 1) / 2 +
(random () % client -> config -> retry_interval));
tv . tv_usec = 0;
add_timeout (&tv, state_init, client, 0, 0);
tv.tv_sec = cur_tv.tv_sec + ((client->config->retry_interval + 1) / 2 +
(random() % client->config->retry_interval));
tv.tv_usec = ((tv.tv_sec - cur_tv.tv_sec) > 1) ?
random() % 1000000 : cur_tv.tv_usec;
add_timeout(&tv, state_init, client, 0, 0);
go_daemon ();
}
@@ -2140,9 +2181,10 @@ void send_request (cpp)
from, &destination,
(struct hardware *)0);
tv . tv_sec = cur_time + client -> interval;
tv . tv_usec = 0;
add_timeout (&tv, send_request, client, 0, 0);
tv.tv_sec = cur_tv.tv_sec + client->interval;
tv.tv_usec = ((tv.tv_sec - cur_tv.tv_sec) > 1) ?
random() % 1000000 : cur_tv.tv_usec;
add_timeout(&tv, send_request, client, 0, 0);
}
void send_decline (cpp)
@@ -2624,6 +2666,9 @@ void rewrite_client_leases ()
write_client6_lease(client,
client->active_lease,
1, 0);
/* Reset last_write after rewrites. */
client->last_write = 0;
}
}
@@ -2642,6 +2687,9 @@ void rewrite_client_leases ()
write_client6_lease(client,
client->active_lease,
1, 0);
/* Reset last_write after rewrites. */
client->last_write = 0;
}
}
fflush (leaseFile);
@@ -2985,12 +3033,15 @@ int write_client_lease (client, lease, rewrite, makesure)
if (fflush(leaseFile) != 0)
errors++;
client->last_write = cur_time;
if (!errors && makesure) {
if (fsync (fileno (leaseFile)) < 0) {
log_info ("write_client_lease: %m");
return 0;
}
}
return errors ? 0 : 1;
}
@@ -3552,7 +3603,6 @@ isc_result_t dhclient_interface_startup_hook (struct interface_info *interface)
{
struct interface_info *ip;
struct client_state *client;
struct timeval tv;
/* This code needs some rethinking. It doesn't test against
a signal name, and it just kind of bulls into doing something
@@ -3590,13 +3640,9 @@ isc_result_t dhclient_interface_startup_hook (struct interface_info *interface)
if (ip -> flags & INTERFACE_RUNNING)
continue;
ip -> flags |= INTERFACE_RUNNING;
for (client = ip -> client; client; client = client -> next) {
client -> state = S_INIT;
/* Set up a timeout to start the initialization
process. */
tv . tv_sec = cur_time + random () % 5;
tv . tv_usec = 0;
add_timeout (&tv, state_reboot, client, 0, 0);
for (client = ip->client ; client ; client = client->next) {
client->state = S_INIT;
state_reboot(client);
}
}
return ISC_R_SUCCESS;
@@ -3741,9 +3787,9 @@ isc_result_t dhcp_set_control_state (control_object_state_t oldstate,
}
if (newstate == server_shutdown) {
tv . tv_sec = cur_tv . tv_sec + 1;
tv . tv_usec = cur_tv . tv_usec;
add_timeout (&tv, shutdown_exit, 0, 0, 0);
tv.tv_sec = cur_tv.tv_sec;
tv.tv_usec = cur_tv.tv_usec + 1;
add_timeout(&tv, shutdown_exit, 0, 0, 0);
}
return ISC_R_SUCCESS;
}
@@ -3861,8 +3907,8 @@ client_dns_update_action(dhcp_ddns_cb_t *ddns_cb,
/* and update our timer */
if (ddns_cb->timeout < 3600)
ddns_cb->timeout *= 10;
tv.tv_sec = cur_time + ddns_cb->timeout;
tv.tv_usec = 0;
tv.tv_sec = cur_tv.tv_sec + ddns_cb->timeout;
tv.tv_usec = cur_tv.tv_usec;
add_timeout(&tv, client_dns_update_timeout,
ddns_cb, NULL, NULL);
return;
@@ -4030,8 +4076,8 @@ dhclient_schedule_updates(struct client_state *client,
client->ddns_cb = ddns_cb;
tv.tv_sec = cur_time + offset;
tv.tv_usec = 0;
tv.tv_sec = cur_tv.tv_sec + offset;
tv.tv_usec = cur_tv.tv_usec;
add_timeout(&tv, client_dns_update_timeout,
ddns_cb, NULL, NULL);
} else {
@@ -4198,6 +4244,31 @@ static int check_option_values(struct universe *universe,
return(0);
}
static void
add_reject(struct packet *packet) {
struct iaddrmatchlist *list;
list = dmalloc(sizeof(struct iaddrmatchlist), MDL);
if (!list)
log_fatal ("no memory for reject list!");
/*
* client_addr is misleading - it is set to source address in common
* code.
*/
list->match.addr = packet->client_addr;
/* Set mask to indicate host address. */
list->match.mask.len = list->match.addr.len;
memset(list->match.mask.iabuf, 0xff, sizeof(list->match.mask.iabuf));
/* Append to reject list for the source interface. */
list->next = packet->interface->client->config->reject_list;
packet->interface->client->config->reject_list = list;
/*
* We should inform user that we won't be accepting this server
* anymore.
*/
log_info("Server added to list of rejected servers.");
}