Use timerfd in order to correctly handle various renewal and rebinding timeouts… | |
git clone git://git.codemadness.org/sdhcp | |
Log | |
Files | |
Refs | |
LICENSE | |
--- | |
commit 80bdf55532fadbf9398ce7e12fe494282f45f394 | |
parent a5616cecc1506731f1ca20ae0d31325f9b0526b4 | |
Author: Michael Forney <[email protected]> | |
Date: Wed, 14 Nov 2018 18:40:38 -0800 | |
Use timerfd in order to correctly handle various renewal and rebinding timeouts | |
Diffstat: | |
M sdhcp.c | 139 +++++++++++++++++++++++------… | |
M 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 secon… | |
+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(renewa… | |
+ optget(&bp, &rebindingtime, ODrebindingtime, sizeof(re… | |
+ 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 |