The DHCP server at GUADEC apparently gives out infinite leases, which have a value of 0xFFFFFFFF (in seconds). Leaving aside the question of whether infinite leases are actually a good idea at a conference where many people come and go (hint: they usually aren’t), why won’t it just @#%@#%!&&* connect?
By default NetworkManager uses dhclient for DHCP, and that usually works fairly well. It’s a well-understood program developed by the ISC that’s used by millions every day. But dhclient apparently fails with infinite leases. Here’s the dhclient code:
#define DHCP_SEC_MAX 0xFFFFFFFF
/*
* The ISC timer library doesn’t seem to like negative values
* and can’t accept any values above 4G-1 seconds so we limit
* the values to 0 <= value < 4G-1. We do it before
* checking the trace option so that both the trace code and
* the working code use the same values.
*/sec = when->tv_sec – cur_tv.tv_sec;
usec = when->tv_usec – cur_tv.tv_usec;<…>
} else if (sec > DHCP_SEC_MAX) {
log_error(“Timeout requested too large ”
“reducing to 2^^32-1”);
sec = DHCP_SEC_MAX;
<…>
}isc_interval_set(&interval, sec & DHCP_SEC_MAX, usec * 1000);
status = isc_time_nowplusinterval(&expires, &interval);
if (status != ISC_R_SUCCESS) {
log_fatal(“Unable to set up timer: %s”,
isc_result_totext(status));
}
The code attempts to add a timeout that triggers when the lease expires; “when” is the lease interval coming from the DHCP server (which is 0xFFFFFFFF, remember). “cur_tv” is just gettimeofday(). Let’s enumerate the fail purely for pedagogic purposes:
- Despite the comment and logged error, the code makes no attempt to limit the value to UINT_MAX – 1. Even if it did, this wouldn’t help; see below. So now we’re passing UINT_MAX into isc_time_nowplusinterval() as interval->seconds. Condensed code for that function:
isc_result_t isc_time_nowplusinterval(isc_time_t *t, const isc_interval_t *i) {
struct timeval tv;if (gettimeofday(&tv, NULL) == -1)
return (ISC_R_UNEXPECTED);/* Ensure the resulting seconds value fits in the size of an
* unsigned int. (It is written this way as a slight optimization;
* note that even if both values == INT_MAX, then when added
* and getting another 1 added below the result is UINT_MAX.)
*/
if ((tv.tv_sec > INT_MAX || i->seconds > INT_MAX) &&
((long long)tv.tv_sec + i->seconds > UINT_MAX))
return (ISC_R_RANGE);<…>
- tv.tv_sec could be greater than INT_MAX since time_t is often 8 bytes wide. So this code is much more likely to fail in early 2038.
- Oops! i->seconds is UINT_MAX, and clearly that’s larger than INT_MAX. So onward to the next bit.
- Oops I did it again! I played with your timer! The second check passes (unless you have a time machine and you like 1970) because tv.tv_sec is the current time (clearly a large value) and i->seconds is already UINT_MAX.
- An error is returned, and your lease fails.
Looks like this code just doesn’t expect to deal with large values of Unix time; hopefully that’ll get corrected soon. The Red Hat bug report for this is bug 662254.
What can I do?
NetworkManager supports two different DHCP clients, selectable at runtime. Install dhcpcd, set dhcp=dhcpcd in the [main] section of /etc/NetworkManager/NetworkManager.conf, restart NM, and voila, DHCP.
Hey Dan,
what package is that ?
$ sudo yum install dhcpcd
No package dhcpcd available.
How do I install dhcpcd on a current fedora 17 release?
[spider@clarus]~/Documents/gnome-testing% yum provides ‘*/dhcpcd’
logcheck-1.3.14-6.fc17.noarch : Analyzes log files and sends noticeable events
: as email
Repo : fedora
Matched from:
Filename : /etc/logcheck/ignore.d.workstation/dhcpcd
rear-1.13.0-1.fc17.noarch : Relax and Recover (Rear) is a Linux Disaster
: Recovery framework
Repo : updates
Matched from:
Filename : /usr/share/rear/skel/default/var/lib/dhcpcd
No binary for it that I can find..