diff options
Diffstat (limited to 'sbin/dhclient')
-rw-r--r-- | sbin/dhclient/dhclient.8 | 6 | ||||
-rw-r--r-- | sbin/dhclient/dhclient.c | 67 | ||||
-rw-r--r-- | sbin/dhclient/dhcp.h | 3 | ||||
-rw-r--r-- | sbin/dhclient/dhcpd.h | 7 | ||||
-rw-r--r-- | sbin/dhclient/dispatch.c | 44 | ||||
-rw-r--r-- | sbin/dhclient/packet.c | 53 | ||||
-rw-r--r-- | sbin/dhclient/tests/Makefile | 1 |
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, ×pec_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 |