aboutsummaryrefslogtreecommitdiff
path: root/sbin/dhclient
diff options
context:
space:
mode:
Diffstat (limited to 'sbin/dhclient')
-rw-r--r--sbin/dhclient/dhclient.86
-rw-r--r--sbin/dhclient/dhclient.c67
-rw-r--r--sbin/dhclient/dhcp.h3
-rw-r--r--sbin/dhclient/dhcpd.h7
-rw-r--r--sbin/dhclient/dispatch.c44
-rw-r--r--sbin/dhclient/packet.c53
-rw-r--r--sbin/dhclient/tests/Makefile1
7 files changed, 121 insertions, 60 deletions
diff --git a/sbin/dhclient/dhclient.8 b/sbin/dhclient/dhclient.8
index a7328304fa68..7f0b418128ca 100644
--- a/sbin/dhclient/dhclient.8
+++ b/sbin/dhclient/dhclient.8
@@ -36,7 +36,7 @@
.\" see ``http://www.isc.org/isc''. To learn more about Vixie
.\" Enterprises, see ``http://www.vix.com''.
.\"
-.Dd August 4, 2018
+.Dd August 1, 2024
.Dt DHCLIENT 8
.Os
.Sh NAME
@@ -82,6 +82,10 @@ will revert to running in the background.
Specify an alternate location,
.Ar file ,
for the leases file.
+.It Fl n
+Make
+.Nm
+not wait for ARP resolution.
.It Fl p Ar file
Specify an alternate location for the PID file.
The default is
diff --git a/sbin/dhclient/dhclient.c b/sbin/dhclient/dhclient.c
index 8179a5c34209..5d2a7453578b 100644
--- a/sbin/dhclient/dhclient.c
+++ b/sbin/dhclient/dhclient.c
@@ -90,7 +90,8 @@
cap_channel_t *capsyslog;
-time_t cur_time;
+time_t cur_time; /* Seconds since epoch. */
+struct timespec time_now; /* CLOCK_MONOTONIC. */
static time_t default_lease_time = 43200; /* 12 hours... */
const char *path_dhclient_conf = _PATH_DHCLIENT_CONF;
@@ -120,6 +121,8 @@ struct pidfh *pidfile;
*/
#define TIME_MAX ((((time_t) 1 << (sizeof(time_t) * CHAR_BIT - 2)) - 1) * 2 + 1)
+static struct timespec arp_timeout = { .tv_sec = 0, .tv_nsec = 250 * 1000 * 1000 };
+static const struct timespec zero_timespec = { .tv_sec = 0, .tv_nsec = 0 };
int log_priority;
static int no_daemon;
static int unknown_ok = 1;
@@ -383,7 +386,7 @@ main(int argc, char *argv[])
cap_openlog(capsyslog, getprogname(), LOG_PID | LOG_NDELAY, DHCPD_LOG_FACILITY);
cap_setlogmask(capsyslog, LOG_UPTO(LOG_DEBUG));
- while ((ch = getopt(argc, argv, "bc:dl:p:qu")) != -1)
+ while ((ch = getopt(argc, argv, "bc:dl:np:qu")) != -1)
switch (ch) {
case 'b':
immediate_daemon = 1;
@@ -397,6 +400,9 @@ main(int argc, char *argv[])
case 'l':
path_dhclient_db = optarg;
break;
+ case 'n':
+ arp_timeout = zero_timespec;
+ break;
case 'p':
path_dhclient_pidfile = optarg;
break;
@@ -443,7 +449,8 @@ main(int argc, char *argv[])
log_perror = 0;
tzset();
- time(&cur_time);
+ clock_gettime(CLOCK_MONOTONIC, &time_now);
+ cur_time = time(NULL);
inaddr_broadcast.s_addr = INADDR_BROADCAST;
inaddr_any.s_addr = INADDR_ANY;
@@ -532,7 +539,7 @@ main(int argc, char *argv[])
setproctitle("%s", ifi->name);
/* setgroups(2) is not permitted in capability mode. */
- if (setgroups(1, &pw->pw_gid) != 0)
+ if (setgroups(0, NULL) != 0)
error("can't restrict groups: %m");
if (caph_enter_casper() < 0)
@@ -572,7 +579,7 @@ void
usage(void)
{
- fprintf(stderr, "usage: %s [-bdqu] ", getprogname());
+ fprintf(stderr, "usage: %s [-bdnqu] ", getprogname());
fprintf(stderr, "[-c conffile] [-l leasefile] interface\n");
exit(1);
}
@@ -1022,10 +1029,14 @@ dhcpoffer(struct packet *packet)
struct interface_info *ip = packet->interface;
struct client_lease *lease, *lp;
int i;
- int arp_timeout_needed, stop_selecting;
+ struct timespec arp_timeout_needed;
+ time_t stop_selecting;
+ struct timespec stop_time;
const char *name = packet->options[DHO_DHCP_MESSAGE_TYPE].len ?
"DHCPOFFER" : "BOOTREPLY";
+ clock_gettime(CLOCK_MONOTONIC, &time_now);
+
/* If we're not receptive to an offer right now, or if the offer
has an unrecognizable transaction id, then just drop it. */
if (ip->client->state != S_SELECTING ||
@@ -1078,9 +1089,10 @@ dhcpoffer(struct packet *packet)
/* If the script can't send an ARP request without waiting,
we'll be waiting when we do the ARPCHECK, so don't wait now. */
if (script_go())
- arp_timeout_needed = 0;
+ arp_timeout_needed = zero_timespec;
+
else
- arp_timeout_needed = 2;
+ arp_timeout_needed = arp_timeout;
/* Figure out when we're supposed to stop selecting. */
stop_selecting =
@@ -1099,9 +1111,13 @@ dhcpoffer(struct packet *packet)
offer would take us past the selection timeout,
then don't extend the timeout - just hope for the
best. */
+
+ struct timespec interm_struct;
+ timespecadd(&time_now, &arp_timeout_needed, &interm_struct);
+
if (ip->client->offered_leases &&
- (cur_time + arp_timeout_needed) > stop_selecting)
- arp_timeout_needed = 0;
+ interm_struct.tv_sec >= stop_selecting)
+ arp_timeout_needed = zero_timespec;
/* Put the lease at the end of the list. */
lease->next = NULL;
@@ -1115,21 +1131,21 @@ dhcpoffer(struct packet *packet)
}
}
- /* If we're supposed to stop selecting before we've had time
- to wait for the ARPREPLY, add some delay to wait for
- the ARPREPLY. */
- if (stop_selecting - cur_time < arp_timeout_needed)
- stop_selecting = cur_time + arp_timeout_needed;
-
- /* 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)
- state_selecting(ip);
- else {
- add_timeout(stop_selecting, state_selecting, ip);
- cancel_timeout(send_discover, ip);
+ /*
+ * Wait until stop_selecting seconds past the epoch, or until
+ * arp_timeout_needed past now, whichever is longer. Note that
+ * the first case only occurs if select-timeout is set to nonzero
+ * in dhclient.conf.
+ */
+ struct timespec time_left =
+ {.tv_sec = stop_selecting - cur_time, .tv_nsec = 0};
+ if (timespeccmp(&time_left, &arp_timeout_needed, <)) {
+ timespecadd(&time_now, &arp_timeout_needed, &stop_time);
+ } else {
+ timespecadd(&time_now, &time_left, &stop_time);
}
+ add_timeout_timespec(stop_time, state_selecting, ip);
+ cancel_timeout(send_discover, ip);
}
/* Allocate a client_lease structure and initialize it from the parameters
@@ -2618,6 +2634,9 @@ check_option(struct client_lease *l, int option)
case DHO_BOOTFILE_NAME:
case DHO_DHCP_USER_CLASS_ID:
case DHO_URL:
+ case DHO_SIP_SERVERS:
+ case DHO_V_I_VENDOR_CLASS:
+ case DHO_V_I_VENDOR_OPTS:
case DHO_END:
return (1);
case DHO_CLASSLESS_ROUTES:
diff --git a/sbin/dhclient/dhcp.h b/sbin/dhclient/dhcp.h
index 300deb464238..02ea42a66079 100644
--- a/sbin/dhclient/dhcp.h
+++ b/sbin/dhclient/dhcp.h
@@ -172,7 +172,10 @@ struct dhcp_packet {
#define DHO_DHCP_USER_CLASS_ID 77
#define DHO_URL 114
#define DHO_DOMAIN_SEARCH 119
+#define DHO_SIP_SERVERS 120
#define DHO_CLASSLESS_ROUTES 121
+#define DHO_V_I_VENDOR_CLASS 124
+#define DHO_V_I_VENDOR_OPTS 125
#define DHO_END 255
/* DHCP message types. */
diff --git a/sbin/dhclient/dhcpd.h b/sbin/dhclient/dhcpd.h
index 399b5c1ecf6c..c61564067598 100644
--- a/sbin/dhclient/dhcpd.h
+++ b/sbin/dhclient/dhcpd.h
@@ -199,7 +199,6 @@ struct client_state {
struct interface_info {
struct interface_info *next;
struct hardware hw_address;
- struct in_addr primary_address;
char name[IFNAMSIZ];
int rfdesc;
int wfdesc;
@@ -219,7 +218,7 @@ struct interface_info {
struct timeout {
struct timeout *next;
- time_t when;
+ struct timespec when;
void (*func)(void *);
void *what;
};
@@ -321,6 +320,7 @@ void reinitialize_interfaces(void);
void dispatch(void);
void got_one(struct protocol *);
void add_timeout(time_t, void (*)(void *), void *);
+void add_timeout_timespec(struct timespec, void (*)(void *), void *);
void cancel_timeout(void (*)(void *), void *);
void add_protocol(const char *, int, void (*)(struct protocol *), void *);
void remove_protocol(struct protocol *);
@@ -361,7 +361,8 @@ char *piaddr(struct iaddr);
extern cap_channel_t *capsyslog;
extern const char *path_dhclient_conf;
extern char *path_dhclient_db;
-extern time_t cur_time;
+extern struct timespec time_now; /* CLOCK_MONOTONIC */
+extern time_t cur_time; /* Seconds since epoch */
extern int log_priority;
extern int log_perror;
diff --git a/sbin/dhclient/dispatch.c b/sbin/dhclient/dispatch.c
index 310f477f8a4f..06bd31e5ec82 100644
--- a/sbin/dhclient/dispatch.c
+++ b/sbin/dhclient/dispatch.c
@@ -56,6 +56,10 @@
#define assert_aligned(p, align) assert((((uintptr_t)p) & ((align) - 1)) == 0)
static struct protocol *protocols;
+static const struct timespec timespec_intmax_ms = {
+ .tv_sec = INT_MAX / 1000,
+ .tv_nsec = (INT_MAX % 1000) * 1000000
+};
static struct timeout *timeouts;
static struct timeout *free_timeouts;
static int interfaces_invalidated;
@@ -76,7 +80,6 @@ discover_interfaces(struct interface_info *iface)
{
struct ifaddrs *ifap, *ifa;
struct ifreq *tif;
- int len = IFNAMSIZ + sizeof(struct sockaddr_storage);
if (getifaddrs(&ifap) != 0)
error("getifaddrs failed");
@@ -119,7 +122,7 @@ discover_interfaces(struct interface_info *iface)
LLADDR(foo), foo->sdl_alen);
}
if (!iface->ifp) {
- if ((tif = calloc(1, len)) == NULL)
+ if ((tif = calloc(1, sizeof(struct ifreq))) == NULL)
error("no space to remember ifp");
strlcpy(tif->ifr_name, ifa->ifa_name, IFNAMSIZ);
iface->ifp = tif;
@@ -155,7 +158,9 @@ dispatch(void)
int count, live_interfaces, i, to_msec, nfds = 0;
struct protocol *l;
struct pollfd *fds;
- time_t howlong;
+ struct timespec howlong;
+
+ clock_gettime(CLOCK_MONOTONIC, &time_now);
for (l = protocols; l; l = l->next)
nfds++;
@@ -173,7 +178,7 @@ another:
if (timeouts) {
struct timeout *t;
- if (timeouts->when <= cur_time) {
+ if (timespeccmp(&timeouts->when, &time_now, <=)) {
t = timeouts;
timeouts = timeouts->next;
(*(t->func))(t->what);
@@ -188,10 +193,10 @@ another:
* int for poll, while not polling with a
* negative timeout and blocking indefinitely.
*/
- howlong = timeouts->when - cur_time;
- if (howlong > INT_MAX / 1000)
- howlong = INT_MAX / 1000;
- to_msec = howlong * 1000;
+ timespecsub(&timeouts->when, &time_now, &howlong);
+ if (timespeccmp(&howlong, &timespec_intmax_ms, >))
+ howlong = timespec_intmax_ms;
+ to_msec = howlong.tv_sec * 1000 + howlong.tv_nsec / 1000000;
} else
to_msec = -1;
@@ -218,14 +223,16 @@ another:
/* Not likely to be transitory... */
if (count == -1) {
if (errno == EAGAIN || errno == EINTR) {
- time(&cur_time);
+ clock_gettime(CLOCK_MONOTONIC, &time_now);
+ cur_time = time(NULL);
continue;
} else
error("poll: %m");
}
/* Get the current time... */
- time(&cur_time);
+ clock_gettime(CLOCK_MONOTONIC, &time_now);
+ cur_time = time(NULL);
i = 0;
for (l = protocols; l; l = l->next) {
@@ -356,7 +363,18 @@ active:
}
void
-add_timeout(time_t when, void (*where)(void *), void *what)
+add_timeout(time_t when_s, void (*where)(void *), void *what)
+{
+ struct timespec when;
+
+ cur_time = time(NULL);
+ clock_gettime(CLOCK_MONOTONIC, &when);
+ when.tv_sec += when_s - cur_time;
+ add_timeout_timespec(when, where, what);
+}
+
+void
+add_timeout_timespec(struct timespec when, void (*where)(void *), void *what)
{
struct timeout *t, *q;
@@ -395,7 +413,7 @@ add_timeout(time_t when, void (*where)(void *), void *what)
/* Now sort this timeout into the timeout list. */
/* Beginning of list? */
- if (!timeouts || timeouts->when > q->when) {
+ if (!timeouts || timespeccmp(&timeouts->when, &q->when, >)) {
q->next = timeouts;
timeouts = q;
return;
@@ -403,7 +421,7 @@ add_timeout(time_t when, void (*where)(void *), void *what)
/* Middle of list? */
for (t = timeouts; t->next; t = t->next) {
- if (t->next->when > q->when) {
+ if (timespeccmp(&t->next->when, &q->when, >)) {
q->next = t->next;
t->next = q;
return;
diff --git a/sbin/dhclient/packet.c b/sbin/dhclient/packet.c
index 3d7390c06ee0..fc0305a8cb0c 100644
--- a/sbin/dhclient/packet.c
+++ b/sbin/dhclient/packet.c
@@ -135,11 +135,14 @@ assemble_udp_ip_header(unsigned char *buf, int *bufix, u_int32_t from,
udp.uh_ulen = htons(sizeof(udp) + len);
memset(&udp.uh_sum, 0, sizeof(udp.uh_sum));
- udp.uh_sum = wrapsum(checksum((unsigned char *)&udp, sizeof(udp),
- checksum(data, len, checksum((unsigned char *)&ip.ip_src,
+ udp.uh_sum = wrapsum(checksum(data, len, checksum((unsigned char *)&udp,
+ sizeof(udp), checksum((unsigned char *)&ip.ip_src,
2 * sizeof(ip.ip_src),
IPPROTO_UDP + (u_int32_t)ntohs(udp.uh_ulen)))));
+ if (udp.uh_sum == htons(0))
+ udp.uh_sum = htons(0xffff);
+
memcpy(&buf[*bufix], &udp, sizeof(udp));
*bufix += sizeof(udp);
}
@@ -166,7 +169,7 @@ decode_udp_ip_header(unsigned char *buf, int bufix, struct sockaddr_in *from,
struct ip *ip;
struct udphdr *udp;
u_int32_t ip_len = (buf[bufix] & 0xf) << 2;
- u_int32_t sum, usum;
+ u_int32_t sum, usum, pseudo_sum;
static int ip_packets_seen;
static int ip_packets_bad_checksum;
static int udp_packets_seen;
@@ -224,23 +227,37 @@ decode_udp_ip_header(unsigned char *buf, int bufix, struct sockaddr_in *from,
}
usum = udp->uh_sum;
- udp->uh_sum = 0;
-
- sum = wrapsum(checksum((unsigned char *)udp, sizeof(*udp),
- checksum(data, len, checksum((unsigned char *)&ip->ip_src,
- 2 * sizeof(ip->ip_src),
- IPPROTO_UDP + (u_int32_t)ntohs(udp->uh_ulen)))));
-
udp_packets_seen++;
- if (usum && usum != sum) {
- udp_packets_bad_checksum++;
- if (udp_packets_seen > 4 && udp_packets_bad_checksum != 0 &&
- (udp_packets_seen / udp_packets_bad_checksum) < 2) {
- note("%d bad udp checksums in %d packets",
- udp_packets_bad_checksum, udp_packets_seen);
- udp_packets_seen = udp_packets_bad_checksum = 0;
+
+ if (usum != htons(0)) {
+ udp->uh_sum = 0;
+
+ pseudo_sum = checksum((unsigned char *)&ip->ip_src,
+ 2 * sizeof(ip->ip_src),
+ IPPROTO_UDP + (u_int32_t)ntohs(udp->uh_ulen));
+ sum = wrapsum(checksum(data, len,
+ checksum((unsigned char *)udp, sizeof(*udp), pseudo_sum)));
+ if (sum == htons(0))
+ sum = htons(0xffff);
+
+ /*
+ * In addition to accepting UDP packets with the correct
+ * checksum in the checksum field, accept also the ones which
+ * have the correct pseudo header checksum in the checksum
+ * field. This allows to process UDP packets, which have been
+ * marked for transmit checksum offloading by the sender side.
+ */
+ if (usum != sum && usum != htons(pseudo_sum & 0x0000ffff)) {
+ udp_packets_bad_checksum++;
+ if (udp_packets_seen > 4 &&
+ udp_packets_bad_checksum != 0 &&
+ (udp_packets_seen / udp_packets_bad_checksum) < 2) {
+ note("%d bad udp checksums in %d packets",
+ udp_packets_bad_checksum, udp_packets_seen);
+ udp_packets_seen = udp_packets_bad_checksum = 0;
+ }
+ return (-1);
}
- return (-1);
}
memcpy(&from->sin_port, &udp->uh_sport, sizeof(udp->uh_sport));
diff --git a/sbin/dhclient/tests/Makefile b/sbin/dhclient/tests/Makefile
index 7d4359d4b762..2cca8635fa13 100644
--- a/sbin/dhclient/tests/Makefile
+++ b/sbin/dhclient/tests/Makefile
@@ -1,4 +1,3 @@
-
.PATH: ${.CURDIR:H}
ATF_TESTS_SH= pcp