Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions src/dhcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -1823,15 +1823,23 @@ send_message(struct interface *ifp, uint8_t type, void (*callback)(void *))
state->xid);
RT = 0; /* bogus gcc warning */
} else {
unsigned int jitter = ifo->backoff_jitter;

if (state->interval == 0)
state->interval = 4;
state->interval = ifo->initial_interval;
else {
unsigned int cutoff = ifo->backoff_cutoff;

state->interval *= 2;
if (state->interval > 64)
state->interval = 64;
if (state->interval > cutoff)
state->interval = cutoff;
}
RT = (state->interval * MSEC_PER_SEC) +
(arc4random_uniform(MSEC_PER_SEC * 2) - MSEC_PER_SEC);

/* Jitter is bounded at config time to the smallest possible
* interval, so the result can never underflow. */
RT = state->interval * MSEC_PER_SEC +
arc4random_uniform(jitter * 2) - jitter;

/* No carrier? Don't bother sending the packet.
* However, we do need to advance the timeout. */
if (!if_is_link_up(ifp))
Expand Down
46 changes: 46 additions & 0 deletions src/dhcpcd.conf.5.in
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,52 @@ You can use this option to stop this from happening.
.It Ic fallback Ar profile
Fall back to using this profile if DHCP fails.
This allows you to configure a static profile instead of using ZeroConf.
.It Ic initial_interval Ar seconds
Set the initial DHCPv4 retransmission interval to
.Ar seconds .
The minimum value is 1 and the maximum is 4.
The default is 4 seconds as per RFC 2131.
This option only affects DHCPv4;
DHCPv6 retransmission is governed by RFC 8415.
See also
.Ic backoff_cutoff
and
.Ic backoff_jitter .
.It Ic backoff_cutoff Ar seconds
Cap the DHCPv4 exponential backoff interval at
.Ar seconds .
The minimum value is 1 and the maximum is 64.
The default is 64 seconds as per RFC 2131.
If
.Ar seconds
is less than
.Ic initial_interval
it will be raised to match it, since the cutoff is a cap on the
exponential growth and must not shrink the initial interval.
Setting both
.Ic initial_interval
and
.Ic backoff_cutoff
to 1 effectively disables exponential growth, so
retransmissions use only the initial interval plus jitter.
This option only affects DHCPv4;
DHCPv6 retransmission is governed by RFC 8415.
See also
.Ic initial_interval
and
.Ic backoff_jitter .
.It Ic backoff_jitter Ar milliseconds
Set the random jitter applied to each DHCPv4 retransmission interval.
The jitter is applied as \(+-
.Ar milliseconds .
The default is 1000 (\(+-1 second) as per RFC 2131, which is also the maximum.
A value of 0 disables jitter, producing deterministic retransmission timing.
This option only affects DHCPv4;
DHCPv6 retransmission is governed by RFC 8415.
See also
.Ic backoff_cutoff
and
.Ic initial_interval .
.It Ic fallback_time Ar seconds
Start fallback after
.Ar seconds .
Expand Down
46 changes: 46 additions & 0 deletions src/if-options.c
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,9 @@ const struct option cf_options[] = { { "background", no_argument, NULL, 'b' },
{ "fallback_time", required_argument, NULL, O_FALLBACK_TIME },
{ "ipv4ll_time", required_argument, NULL, O_IPV4LL_TIME },
{ "nosyslog", no_argument, NULL, O_NOSYSLOG },
{ "initial_interval", required_argument, NULL, O_INITIAL_INTERVAL },
{ "backoff_cutoff", required_argument, NULL, O_BACKOFF_CUTOFF },
{ "backoff_jitter", required_argument, NULL, O_BACKOFF_JITTER },
{ NULL, 0, NULL, '\0' } };

static char *
Expand Down Expand Up @@ -2552,6 +2555,33 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo,
logopts &= ~LOGERR_LOG;
logsetopts(logopts);
} break;
case O_INITIAL_INTERVAL:
ARG_REQUIRED;
ifo->initial_interval = (uint32_t)strtou(arg, NULL, 0, 1,
MAX_INITIAL_INTERVAL, &e);
if (e) {
logerrx("invalid initial interval: %s", arg);
return -1;
}
break;
case O_BACKOFF_CUTOFF:
ARG_REQUIRED;
ifo->backoff_cutoff = (uint32_t)strtou(arg, NULL, 0, 1,
MAX_BACKOFF_CUTOFF, &e);
if (e) {
logerrx("invalid backoff cutoff: %s", arg);
return -1;
}
break;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
case O_BACKOFF_JITTER:
ARG_REQUIRED;
ifo->backoff_jitter = (uint32_t)strtou(arg, NULL, 0, 0,
MAX_BACKOFF_JITTER, &e);
if (e) {
logerrx("invalid backoff jitter: %s", arg);
return -1;
}
break;
default:
return 0;
}
Expand Down Expand Up @@ -2618,6 +2648,19 @@ finish_config(struct if_options *ifo)
if (!(ifo->options & DHCPCD_IPV6RS))
ifo->options &= ~(
DHCPCD_IPV6RA_AUTOCONF | DHCPCD_IPV6RA_REQRDNSS);

#ifdef INET
/* The exponential backoff cutoff must not be lower than the initial
* interval, otherwise the retransmission sequence would shrink rather
* than grow up to the cap. Clamp the cutoff up to the initial
* interval to preserve the documented "cap" semantics. */
if (ifo->backoff_cutoff < ifo->initial_interval) {
logwarnx("backoff_cutoff (%u) is less than initial_interval "
"(%u); raising backoff_cutoff to match",
ifo->backoff_cutoff, ifo->initial_interval);
ifo->backoff_cutoff = ifo->initial_interval;
}
#endif
}

static struct if_options *
Expand All @@ -2637,6 +2680,9 @@ default_config(struct dhcpcd_ctx *ctx)
#ifdef INET
ifo->fallback_time = DEFAULT_FALLBACK;
ifo->ipv4ll_time = DEFAULT_IPV4LL;
ifo->initial_interval = DEFAULT_INITIAL_INTERVAL;
ifo->backoff_cutoff = DEFAULT_BACKOFF_CUTOFF;
ifo->backoff_jitter = DEFAULT_BACKOFF_JITTER;
#endif
ifo->metric = -1;
ifo->auth.options |= DHCPCD_AUTH_REQUIRE;
Expand Down
15 changes: 15 additions & 0 deletions src/if-options.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,15 @@
#define DEFAULT_REQUEST 180 /* secs to request, mirror DHCP6 */
#define DEFAULT_FALLBACK 5 /* secs until fallback */
#define DEFAULT_IPV4LL 5 /* secs until ipv4ll */
/* DHCPv4 retransmission backoff defaults (RFC 2131); DHCPv4-only. */
#define DEFAULT_INITIAL_INTERVAL 4 /* DHCP_BASE per RFC 2131 */
#define DEFAULT_BACKOFF_CUTOFF 64 /* DHCP_MAX per RFC 2131 */
#define DEFAULT_BACKOFF_JITTER 1000 /* +/- milliseconds */

/* Upper bounds to keep the retransmit timeout arithmetic well within range. */
#define MAX_INITIAL_INTERVAL 4 /* DHCP_BASE per RFC 2131 */
#define MAX_BACKOFF_CUTOFF 64 /* DHCP_MAX per RFC 2131 */
#define MAX_BACKOFF_JITTER 1000 /* +/- milliseconds, per RFC 2131 */

#ifndef HOSTNAME_MAX_LEN
#define HOSTNAME_MAX_LEN 250 /* 255 - 3 (FQDN) - 2 (DNS enc) */
Expand Down Expand Up @@ -191,6 +200,9 @@
#define O_VSIO O_BASE + 57
#define O_VSIO6 O_BASE + 58
#define O_NOSYSLOG O_BASE + 59
#define O_INITIAL_INTERVAL O_BASE + 60
#define O_BACKOFF_CUTOFF O_BASE + 61
#define O_BACKOFF_JITTER O_BASE + 62

extern const struct option cf_options[];

Expand Down Expand Up @@ -258,6 +270,9 @@ struct if_options {
uint32_t request_time;
uint32_t fallback_time;
uint32_t ipv4ll_time;
uint32_t initial_interval;
uint32_t backoff_cutoff;
uint32_t backoff_jitter;
unsigned long long options;
bool randomise_hwaddr;

Expand Down