sdhcp

simple dhcp client
git clone git://git.2f30.org/sdhcp.git
Log | Files | Refs | LICENSE

commit 80bdf55532fadbf9398ce7e12fe494282f45f394
parent a5616cecc1506731f1ca20ae0d31325f9b0526b4
Author: Michael Forney <mforney@mforney.org>
Date:   Wed, 14 Nov 2018 18:40:38 -0800

Use timerfd in order to correctly handle various renewal and rebinding timeouts

Diffstat:
sdhcp.c | 139++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
util.h | 1+
2 files changed, 103 insertions(+), 37 deletions(-)

diff --git a/sdhcp.c b/sdhcp.c @@ -1,5 +1,6 @@ #include <sys/ioctl.h> #include <sys/socket.h> +#include <sys/timerfd.h> #include <netinet/in.h> #include <net/if.h> @@ -47,7 +48,9 @@ enum { DHCPnak, DHCPrelease, DHCPinform, - Timeout = 200, + Timeout0 = 200, + Timeout1, + Timeout2, Bootrequest = 1, Bootreply = 2, @@ -89,14 +92,14 @@ static time_t starttime; static char *ifname = "eth0"; static unsigned char cid[16]; static char *program = ""; -static int sock; +static int sock, timers[3]; /* sav */ static unsigned char server[4]; static unsigned char client[4]; static unsigned char mask[4]; static unsigned char router[4]; static unsigned char dns[4]; -static uint32_t t1; +static uint32_t renewaltime, rebindingtime, lease; static int dflag = 1; /* change DNS in /etc/resolv.conf ? */ static int iflag = 1; /* set IP ? */ @@ -282,7 +285,7 @@ dhcpsend(int type, int how) break; case DHCPrequest: /* memcpy(bp.ciaddr, client, sizeof bp.ciaddr); */ - p = hnoptput(p, ODlease, t1, sizeof(t1)); + p = hnoptput(p, ODlease, lease, sizeof(lease)); p = optput(p, ODipaddr, client, sizeof(client)); p = optput(p, ODserverid, server, sizeof(server)); break; @@ -302,22 +305,34 @@ static int dhcprecv(void) { unsigned char type; - struct pollfd pfd; - - memset(&pfd, 0, sizeof(pfd)); - pfd.fd = sock; - pfd.events = POLLIN; - - memset(&bp, 0, sizeof(bp)); - if (poll(&pfd, 1, -1) == -1) { - if (errno != EINTR) - eprintf("poll:"); - else - return Timeout; + struct pollfd pfd[] = { + { .fd = sock, .events = POLLIN }, + { .fd = timers[0], .events = POLLIN }, + { .fd = timers[1], .events = POLLIN }, + { .fd = timers[2], .events = POLLIN }, + }; + uint64_t n; + + if (poll(pfd, LEN(pfd), -1) == -1) + eprintf("poll:"); + if (pfd[0].revents) { + memset(&bp, 0, sizeof(bp)); + udprecv(IP(255, 255, 255, 255), sock, &bp, sizeof(bp)); + optget(&bp, &type, ODtype, sizeof(type)); + return type; + } + if (pfd[1].revents) { + type = Timeout0; + read(timers[0], &n, sizeof(n)); + } + if (pfd[2].revents) { + type = Timeout1; + read(timers[1], &n, sizeof(n)); + } + if (pfd[3].revents) { + type = Timeout2; + read(timers[2], &n, sizeof(n)); } - udprecv(IP(255, 255, 255, 255), sock, &bp, sizeof(bp)); - optget(&bp, &type, ODtype, sizeof(type)); - return type; } @@ -343,32 +358,62 @@ acceptlease(void) setenv("DNS", buf, 1); system(program); } - alarm(t1); +} + +static void +settimeout(int n, const struct itimerspec *ts) +{ + if (timerfd_settime(timers[n], 0, ts, NULL) < 0) + eprintf("timerfd_settime:"); +} + +/* sets ts to expire halfway to the expiration of timer n, minimum of 60 seconds */ +static void +calctimeout(int n, struct itimerspec *ts) +{ + if (timerfd_gettime(timers[n], ts) < 0) + eprintf("timerfd_gettime:"); + ts->it_value.tv_nsec /= 2; + if (ts->it_value.tv_sec % 2) + ts->it_value.tv_nsec += 500000000; + ts->it_value.tv_sec /= 2; + if (ts->it_value.tv_sec < 60) { + ts->it_value.tv_sec = 60; + ts->it_value.tv_nsec = 0; + } } static void run(void) { int forked = 0; + struct itimerspec timeout = { 0 }; Init: dhcpsend(DHCPdiscover, Broadcast); - alarm(1); + timeout.it_value.tv_sec = 1; + timeout.it_value.tv_nsec = 0; + settimeout(0, &timeout); goto Selecting; Selecting: for (;;) { switch (dhcprecv()) { case DHCPoffer: - alarm(0); + timeout.it_value.tv_sec = 0; + settimeout(0, &timeout); memcpy(client, bp.yiaddr, sizeof(client)); optget(&bp, server, ODserverid, sizeof(server)); optget(&bp, mask, OBmask, sizeof(mask)); optget(&bp, router, OBrouter, sizeof(router)); optget(&bp, dns, OBdnsserver, sizeof(dns)); - optget(&bp, &t1, ODlease, sizeof(t1)); - t1 = ntohl(t1); + optget(&bp, &renewaltime, ODrenewaltime, sizeof(renewaltime)); + optget(&bp, &rebindingtime, ODrebindingtime, sizeof(rebindingtime)); + optget(&bp, &lease, ODlease, sizeof(lease)); + renewaltime = ntohl(renewaltime); + rebindingtime = ntohl(rebindingtime); + lease = ntohl(lease); goto Requesting; - case Timeout: + case Timeout0: goto Init; } } @@ -390,43 +435,57 @@ Bound: exit(0); forked = 1; } + timeout.it_value.tv_sec = renewaltime; + settimeout(0, &timeout); + timeout.it_value.tv_sec = rebindingtime; + settimeout(1, &timeout); + timeout.it_value.tv_sec = lease;; + settimeout(2, &timeout); for (;;) { switch (dhcprecv()) { - case Timeout: + case Timeout0: /* t1 elapsed */ goto Renewing; + case Timeout1: /* t2 elapsed */ + goto Rebinding; + case Timeout2: /* lease expired */ + goto Init; } } Renewing: dhcpsend(DHCPrequest, Unicast); + calctimeout(1, &timeout); + settimeout(0, &timeout); for (;;) { switch (dhcprecv()) { case DHCPack: goto Bound; + case Timeout0: /* resend request */ + goto Renewing; + case Timeout1: /* t2 elapsed */ + goto Rebinding; + case Timeout2: case DHCPnak: goto Init; - case Timeout: - goto Rebinding; } } Rebinding: + calctimeout(2, &timeout); + settimeout(0, &timeout); dhcpsend(DHCPrequest, Broadcast); for (;;) { switch (dhcprecv()) { - case DHCPnak: /* lease expired */ - goto Init; case DHCPack: goto Bound; + case Timeout0: /* resend request */ + goto Rebinding; + case Timeout2: /* lease expired */ + case DHCPnak: + goto Init; } } } static void -nop(int unused) -{ - (void)unused; -} - -static void cleanexit(int unused) { (void)unused; @@ -447,6 +506,7 @@ main(int argc, char *argv[]) struct ifreq ifreq; struct sockaddr addr; int rnd; + size_t i; ARGBEGIN { case 'd': /* don't update DNS in /etc/resolv.conf */ @@ -472,7 +532,6 @@ main(int argc, char *argv[]) strlcpy((char *)cid, argv[1], sizeof(cid)); /* client-id */ memset(&ifreq, 0, sizeof(ifreq)); - signal(SIGALRM, nop); signal(SIGTERM, cleanexit); if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) @@ -497,6 +556,12 @@ main(int argc, char *argv[]) read(rnd, xid, sizeof(xid)); close(rnd); + for (i = 0; i < LEN(timers); ++i) { + timers[i] = timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC); + if (timers[i] == -1) + eprintf("timerfd_create:"); + } + starttime = time(NULL); run(); diff --git a/util.h b/util.h @@ -1,4 +1,5 @@ #define MIN(a,b) (((a)<(b))?(a):(b)) +#define LEN(a) (sizeof(a) / sizeof((a)[0])) #define bpdump(p,n) 1 #undef strlcpy