commit 640ff93b4819f63a909d29fc21f0cdf00033034f
Author: Naveen Narayanan <zerous@nocebo.space>
Date: Thu, 23 Sep 2021 21:22:20 +0200
Import from http://git.2f30.org/sdhcp/
Diffstat:
A | Makefile | | | 65 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | arg.h | | | 63 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | config.mk | | | 12 | ++++++++++++ |
A | sdhcp.1 | | | 51 | +++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | sdhcp.c | | | 584 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | util.h | | | 10 | ++++++++++ |
A | util/eprintf.c | | | 65 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | util/strlcpy.c | | | 32 | ++++++++++++++++++++++++++++++++ |
8 files changed, 882 insertions(+), 0 deletions(-)
diff --git a/Makefile b/Makefile
@@ -0,0 +1,65 @@
+include config.mk
+
+.POSIX:
+.SUFFIXES: .c .o
+
+HDR = util.h arg.h
+LIB = \
+ util/strlcpy.o \
+ util/eprintf.o
+
+SRC = sdhcp.c
+
+OBJ = $(SRC:.c=.o) $(LIB)
+BIN = $(SRC:.c=)
+MAN = $(SRC:.c=.1)
+
+all: options binlib
+
+options:
+ @echo sdhcp build options:
+ @echo "CFLAGS = ${CFLAGS}"
+ @echo "LDFLAGS = ${LDFLAGS}"
+ @echo "CC = ${CC}"
+
+binlib: util.a
+ $(MAKE) bin
+
+bin: $(BIN)
+
+$(OBJ): $(HDR) config.mk
+
+.o:
+ @echo LD $@
+ @$(LD) -o $@ $< util.a $(LDFLAGS)
+
+.c.o:
+ @echo CC $<
+ @$(CC) -c -o $@ $< $(CFLAGS)
+
+util.a: $(LIB)
+ @echo AR $@
+ @$(AR) -r -c $@ $(LIB)
+ @ranlib $@
+
+install: all
+ @echo installing executables to $(DESTDIR)$(PREFIX)/sbin
+ @mkdir -p $(DESTDIR)$(PREFIX)/sbin
+ @cp -f $(BIN) $(DESTDIR)$(PREFIX)/sbin
+ @cd $(DESTDIR)$(PREFIX)/sbin && chmod 755 $(BIN)
+ @echo installing manual pages to $(DESTDIR)$(MANPREFIX)/man1
+ @mkdir -p $(DESTDIR)$(MANPREFIX)/man1
+ @for m in $(MAN); do sed "s/VERSION/$(VERSION)/g" < "$$m" > $(DESTDIR)$(MANPREFIX)/man1/"$$m"; done
+ @cd $(DESTDIR)$(MANPREFIX)/man1 && chmod 644 $(MAN)
+
+uninstall:
+ @echo removing executables from $(DESTDIR)$(PREFIX)/sbin
+ @cd $(DESTDIR)$(PREFIX)/sbin && rm -f $(BIN)
+ @echo removing manual pages from $(DESTDIR)$(MANPREFIX)/man1
+ @cd $(DESTDIR)$(MANPREFIX)/man1 && rm -f $(MAN)
+
+clean:
+ @echo cleaning
+ @rm -f $(BIN) $(OBJ) util.a
+
+.PHONY: all options clean install uninstall
diff --git a/arg.h b/arg.h
@@ -0,0 +1,63 @@
+/*
+ * Copy me if you can.
+ * by 20h
+ */
+
+#ifndef ARG_H__
+#define ARG_H__
+
+extern char *argv0;
+
+/* use main(int argc, char *argv[]) */
+#define ARGBEGIN for (argv0 = *argv, argv++, argc--;\
+ argv[0] && argv[0][1]\
+ && argv[0][0] == '-';\
+ argc--, argv++) {\
+ char argc_;\
+ char **argv_;\
+ int brk_;\
+ if (argv[0][1] == '-' && argv[0][2] == '\0') {\
+ argv++;\
+ argc--;\
+ break;\
+ }\
+ for (brk_ = 0, argv[0]++, argv_ = argv;\
+ argv[0][0] && !brk_;\
+ argv[0]++) {\
+ if (argv_ != argv)\
+ break;\
+ argc_ = argv[0][0];\
+ switch (argc_)
+
+/* Handles obsolete -NUM syntax */
+#define ARGNUM case '0':\
+ case '1':\
+ case '2':\
+ case '3':\
+ case '4':\
+ case '5':\
+ case '6':\
+ case '7':\
+ case '8':\
+ case '9'
+
+#define ARGEND }\
+ }
+
+#define ARGC() argc_
+
+#define ARGNUMF(base) (brk_ = 1, estrtol(argv[0], (base)))
+
+#define EARGF(x) ((argv[0][1] == '\0' && argv[1] == NULL)?\
+ ((x), abort(), (char *)0) :\
+ (brk_ = 1, (argv[0][1] != '\0')?\
+ (&argv[0][1]) :\
+ (argc--, argv++, argv[0])))
+
+#define ARGF() ((argv[0][1] == '\0' && argv[1] == NULL)?\
+ (char *)0 :\
+ (brk_ = 1, (argv[0][1] != '\0')?\
+ (&argv[0][1]) :\
+ (argc--, argv++, argv[0])))
+
+#endif
diff --git a/config.mk b/config.mk
@@ -0,0 +1,12 @@
+# sdhcp version
+VERSION = 0.1
+
+PREFIX = /usr/local
+DESTDIR =
+MANPREFIX = $(PREFIX)/share/man
+
+CC = cc
+LD = $(CC)
+CPPFLAGS = -D_DEFAULT_SOURCE
+CFLAGS = -Wall -Wextra -pedantic -std=c99 $(CPPFLAGS)
+LDFLAGS = -s
diff --git a/sdhcp.1 b/sdhcp.1
@@ -0,0 +1,51 @@
+.Dd April 27, 2015
+.Dt SDHCP 1
+.Os
+.Sh NAME
+.Nm sdhcp
+.Nd a simple DHCP client
+.Sh SYNOPSIS
+.Nm
+.Op Fl d
+.Op Fl e Ar program
+.Op Fl f
+.Op Fl i
+.Op Ar interface
+.Op Ar client-id
+.Sh DESCRIPTION
+.Nm
+is a simple, tiny DHCP client. It runs until it enters the "Bound"
+state, then forks to the background and runs as a daemon to keep
+the lease alive.
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Fl d
+don't change DNS in
+.Pa /etc/resolv.conf .
+.It Fl e Ar program
+run
+.Ar program .
+Variables will be set, see VARIABLES.
+.It Fl f
+run in foreground.
+.It Fl i
+don't change interface information such as an IP address.
+.El
+.Sh VARIABLES
+The following variables are set:
+.Bl -tag -width Ds
+.It Ev SERVER
+DHCP IP.
+.It Ev DNS
+DNS IP.
+.It Ev ROUTER
+router IP.
+.It Ev MASK
+network mask.
+.It Ev CLIENT
+your client IP.
+.El
+.Sh BUGS
+I'm sure there are plenty. It only currently supports a small subset of
+DHCP options, and has been untested on larger networks. It ignores most of
+the DHCP options it understands.
diff --git a/sdhcp.c b/sdhcp.c
@@ -0,0 +1,584 @@
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/timerfd.h>
+
+#include <netinet/in.h>
+#include <net/if.h>
+#include <net/route.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "arg.h"
+#include "util.h"
+
+typedef struct bootp {
+ unsigned char op [1];
+ unsigned char htype [1];
+ unsigned char hlen [1];
+ unsigned char hops [1];
+ unsigned char xid [4];
+ unsigned char secs [2];
+ unsigned char flags [2];
+ unsigned char ciaddr [4];
+ unsigned char yiaddr [4];
+ unsigned char siaddr [4];
+ unsigned char giaddr [4];
+ unsigned char chaddr [16];
+ unsigned char sname [64];
+ unsigned char file [128];
+ unsigned char magic [4];
+ unsigned char optdata [312-4];
+} Bootp;
+
+enum {
+ DHCPdiscover = 1,
+ DHCPoffer,
+ DHCPrequest,
+ DHCPdecline,
+ DHCPack,
+ DHCPnak,
+ DHCPrelease,
+ DHCPinform,
+ Timeout0 = 200,
+ Timeout1,
+ Timeout2,
+
+ Bootrequest = 1,
+ Bootreply = 2,
+ /* bootp flags */
+ Fbroadcast = 1 << 15,
+
+ OBpad = 0,
+ OBmask = 1,
+ OBrouter = 3,
+ OBnameserver = 5,
+ OBdnsserver = 6,
+ OBhostname = 12,
+ OBbaddr = 28,
+ ODipaddr = 50, /* 0x32 */
+ ODlease = 51,
+ ODoverload = 52,
+ ODtype = 53, /* 0x35 */
+ ODserverid = 54, /* 0x36 */
+ ODparams = 55, /* 0x37 */
+ ODmessage = 56,
+ ODmaxmsg = 57,
+ ODrenewaltime = 58,
+ ODrebindingtime = 59,
+ ODvendorclass = 60,
+ ODclientid = 61, /* 0x3d */
+ ODtftpserver = 66,
+ ODbootfile = 67,
+ OBend = 255,
+};
+
+enum { Broadcast, Unicast };
+
+static Bootp bp;
+static unsigned char magic[] = { 99, 130, 83, 99 };
+
+/* conf */
+static unsigned char xid[sizeof(bp.xid)];
+static unsigned char hwaddr[16];
+static char hostname[HOST_NAME_MAX + 1];
+static time_t starttime;
+static char *ifname = "eth0";
+static unsigned char cid[16];
+static char *program = "";
+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 int dflag = 1; /* change DNS in /etc/resolv.conf ? */
+static int iflag = 1; /* set IP ? */
+static int fflag = 0; /* run in foreground */
+
+#define IP(a, b, c, d) (unsigned char[4]){ a, b, c, d }
+
+static void
+hnput(unsigned char *dst, uint32_t src, size_t n)
+{
+ unsigned int i;
+
+ for (i = 0; n--; i++)
+ dst[i] = (src >> (n * 8)) & 0xff;
+}
+
+static struct sockaddr *
+iptoaddr(struct sockaddr *ifaddr, unsigned char ip[4], int port)
+{
+ struct sockaddr_in *in = (struct sockaddr_in *)ifaddr;
+
+ in->sin_family = AF_INET;
+ in->sin_port = htons(port);
+ memcpy(&(in->sin_addr), ip, sizeof(in->sin_addr));
+
+ return ifaddr;
+}
+
+/* sendto UDP wrapper */
+static ssize_t
+udpsend(unsigned char ip[4], int fd, void *data, size_t n)
+{
+ struct sockaddr addr;
+ socklen_t addrlen = sizeof(addr);
+ ssize_t sent;
+
+ iptoaddr(&addr, ip, 67); /* bootp server */
+ if ((sent = sendto(fd, data, n, 0, &addr, addrlen)) == -1)
+ eprintf("sendto:");
+
+ return sent;
+}
+
+/* recvfrom UDP wrapper */
+static ssize_t
+udprecv(unsigned char ip[4], int fd, void *data, size_t n)
+{
+ struct sockaddr addr;
+ socklen_t addrlen = sizeof(addr);
+ ssize_t r;
+
+ iptoaddr(&addr, ip, 68); /* bootp client */
+ if ((r = recvfrom(fd, data, n, 0, &addr, &addrlen)) == -1)
+ eprintf("recvfrom:");
+
+ return r;
+}
+
+static void
+setip(unsigned char ip[4], unsigned char mask[4], unsigned char gateway[4])
+{
+ struct ifreq ifreq;
+ struct rtentry rtreq;
+ int fd;
+
+ memset(&ifreq, 0, sizeof(ifreq));
+ memset(&rtreq, 0, sizeof(rtreq));
+
+ strlcpy(ifreq.ifr_name, ifname, IF_NAMESIZE);
+ iptoaddr(&(ifreq.ifr_addr), ip, 0);
+ if ((fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP)) == -1)
+ eprintf("can't set ip, socket:");
+ ioctl(fd, SIOCSIFADDR, &ifreq);
+ iptoaddr(&(ifreq.ifr_netmask), mask, 0);
+ ioctl(fd, SIOCSIFNETMASK, &ifreq);
+ ifreq.ifr_flags = IFF_UP | IFF_RUNNING | IFF_BROADCAST | IFF_MULTICAST;
+ ioctl(fd, SIOCSIFFLAGS, &ifreq);
+ /* gw */
+ rtreq.rt_flags = (RTF_UP | RTF_GATEWAY);
+ iptoaddr(&(rtreq.rt_gateway), gateway, 0);
+ iptoaddr(&(rtreq.rt_genmask), IP(0, 0, 0, 0), 0);
+ iptoaddr(&(rtreq.rt_dst), IP(0, 0, 0, 0), 0);
+ ioctl(fd, SIOCADDRT, &rtreq);
+
+ close(fd);
+}
+
+static void
+cat(int dfd, char *src)
+{
+ char buf[BUFSIZ];
+ int n, fd;
+
+ if ((fd = open(src, O_RDONLY)) == -1)
+ return; /* can't read, but don't error out */
+ while ((n = read(fd, buf, sizeof(buf))) > 0)
+ write(dfd, buf, n);
+ close(fd);
+}
+
+static void
+setdns(unsigned char dns[4])
+{
+ char buf[128];
+ int fd;
+
+ if ((fd = creat("/etc/resolv.conf", 0644)) == -1) {
+ weprintf("can't change /etc/resolv.conf:");
+ return;
+ }
+ cat(fd, "/etc/resolv.conf.head");
+ if (snprintf(buf, sizeof(buf) - 1, "\nnameserver %d.%d.%d.%d\n",
+ dns[0], dns[1], dns[2], dns[3]) > 0)
+ write(fd, buf, strlen(buf));
+ cat(fd, "/etc/resolv.conf.tail");
+ close(fd);
+}
+
+static void
+optget(Bootp *bp, void *data, int opt, int n)
+{
+ unsigned char *p = bp->optdata;
+ unsigned char *top = ((unsigned char *)bp) + sizeof(*bp);
+ int code, len;
+
+ while (p < top) {
+ code = *p++;
+ if (code == OBpad)
+ continue;
+ if (code == OBend || p == top)
+ break;
+ len = *p++;
+ if (len > top - p)
+ break;
+ if (code == opt) {
+ memcpy(data, p, MIN(len, n));
+ break;
+ }
+ p += len;
+ }
+}
+
+static unsigned char *
+optput(unsigned char *p, int opt, unsigned char *data, size_t len)
+{
+ *p++ = opt;
+ *p++ = (unsigned char)len;
+ memcpy(p, data, len);
+
+ return p + len;
+}
+
+static unsigned char *
+hnoptput(unsigned char *p, int opt, uint32_t data, size_t len)
+{
+ *p++ = opt;
+ *p++ = (unsigned char)len;
+ hnput(p, data, len);
+
+ return p + len;
+}
+
+static void
+dhcpsend(int type, int how)
+{
+ unsigned char *ip, *p;
+
+ memset(&bp, 0, sizeof(bp));
+ hnput(bp.op, Bootrequest, 1);
+ hnput(bp.htype, 1, 1);
+ hnput(bp.hlen, 6, 1);
+ memcpy(bp.xid, xid, sizeof(xid));
+ hnput(bp.flags, Fbroadcast, sizeof(bp.flags));
+ hnput(bp.secs, time(NULL) - starttime, sizeof(bp.secs));
+ memcpy(bp.magic, magic, sizeof(bp.magic));
+ memcpy(bp.chaddr, hwaddr, sizeof(bp.chaddr));
+ p = bp.optdata;
+ p = hnoptput(p, ODtype, type, 1);
+ p = optput(p, ODclientid, cid, sizeof(cid));
+ p = optput(p, OBhostname, (unsigned char *)hostname, strlen(hostname));
+
+ switch (type) {
+ case DHCPdiscover:
+ break;
+ case DHCPrequest:
+ /* memcpy(bp.ciaddr, client, sizeof bp.ciaddr); */
+ p = optput(p, ODipaddr, client, sizeof(client));
+ p = optput(p, ODserverid, server, sizeof(server));
+ break;
+ case DHCPrelease:
+ memcpy(bp.ciaddr, client, sizeof(client));
+ p = optput(p, ODipaddr, client, sizeof(client));
+ p = optput(p, ODserverid, server, sizeof(server));
+ break;
+ }
+ *p++ = OBend;
+
+ ip = (how == Broadcast) ? IP(255, 255, 255, 255) : server;
+ udpsend(ip, sock, &bp, p - (unsigned char *)&bp);
+}
+
+static int
+dhcprecv(void)
+{
+ unsigned char type;
+ 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));
+ }
+ return type;
+}
+
+static void
+acceptlease(void)
+{
+ char buf[128];
+
+ if (iflag)
+ setip(client, mask, router);
+ if (dflag)
+ setdns(dns);
+ if (*program) {
+ snprintf(buf, sizeof(buf), "%d.%d.%d.%d", server[0], server[1], server[2], server[3]);
+ setenv("SERVER", buf, 1);
+ snprintf(buf, sizeof(buf), "%d.%d.%d.%d", client[0], client[1], client[2], client[3]);
+ setenv("CLIENT", buf, 1);
+ snprintf(buf, sizeof(buf), "%d.%d.%d.%d", mask[0], mask[1], mask[2], mask[3]);
+ setenv("MASK", buf, 1);
+ snprintf(buf, sizeof(buf), "%d.%d.%d.%d", router[0], router[1], router[2], router[3]);
+ setenv("ROUTER", buf, 1);
+ snprintf(buf, sizeof(buf), "%d.%d.%d.%d", dns[0], dns[1], dns[2], dns[3]);
+ setenv("DNS", buf, 1);
+ system(program);
+ }
+}
+
+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, t;
+ struct itimerspec timeout = { 0 };
+ uint32_t renewaltime, rebindingtime, lease;
+
+Init:
+ dhcpsend(DHCPdiscover, Broadcast);
+ timeout.it_value.tv_sec = 1;
+ timeout.it_value.tv_nsec = 0;
+ settimeout(0, &timeout);
+ goto Selecting;
+Selecting:
+ for (;;) {
+ switch (dhcprecv()) {
+ case DHCPoffer:
+ memcpy(client, bp.yiaddr, sizeof(client));
+ optget(&bp, server, ODserverid, sizeof(server));
+ goto Requesting;
+ case Timeout0:
+ goto Init;
+ }
+ }
+Requesting:
+ for (t = 4; t <= 64; t *= 2) {
+ dhcpsend(DHCPrequest, Broadcast);
+ timeout.it_value.tv_sec = t;
+ settimeout(0, &timeout);
+ for (;;) {
+ switch (dhcprecv()) {
+ case DHCPack:
+ goto Bound;
+ case DHCPnak:
+ goto Init;
+ case Timeout0:
+ break;
+ default:
+ continue;
+ }
+ break;
+ }
+ }
+ /* no response from DHCPREQUEST after several attempts, go to INIT */
+ goto Init;
+Bound:
+ optget(&bp, mask, OBmask, sizeof(mask));
+ optget(&bp, router, OBrouter, sizeof(router));
+ optget(&bp, dns, OBdnsserver, sizeof(dns));
+ 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);
+ acceptlease();
+ fputs("Congrats! You should be on the 'net.\n", stdout);
+ if (!fflag && !forked) {
+ if (fork())
+ 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 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;
+ }
+ }
+Rebinding:
+ calctimeout(2, &timeout);
+ settimeout(0, &timeout);
+ dhcpsend(DHCPrequest, Broadcast);
+ for (;;) {
+ switch (dhcprecv()) {
+ case DHCPack:
+ goto Bound;
+ case Timeout0: /* resend request */
+ goto Rebinding;
+ case Timeout2: /* lease expired */
+ case DHCPnak:
+ goto Init;
+ }
+ }
+}
+
+static void
+cleanexit(int unused)
+{
+ (void)unused;
+ dhcpsend(DHCPrelease, Unicast);
+ _exit(0);
+}
+
+static void
+usage(void)
+{
+ eprintf("usage: %s [-d] [-e program] [-f] [-i] [ifname] [clientid]\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int bcast = 1;
+ struct ifreq ifreq;
+ struct sockaddr addr;
+ int rnd;
+ size_t i;
+
+ ARGBEGIN {
+ case 'd': /* don't update DNS in /etc/resolv.conf */
+ dflag = 0;
+ break;
+ case 'e': /* run program */
+ program = EARGF(usage());
+ break;
+ case 'f': /* run in foreground */
+ fflag = 1;
+ break;
+ case 'i': /* don't set ip */
+ iflag = 0;
+ break;
+ default:
+ usage();
+ break;
+ } ARGEND;
+
+ if (argc)
+ ifname = argv[0]; /* interface name */
+ if (argc >= 2)
+ strlcpy((char *)cid, argv[1], sizeof(cid)); /* client-id */
+
+ memset(&ifreq, 0, sizeof(ifreq));
+ signal(SIGTERM, cleanexit);
+
+ if (gethostname(hostname, sizeof(hostname)) == -1)
+ eprintf("gethostname:");
+
+ if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+ eprintf("socket:");
+ if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &bcast, sizeof(bcast)) == -1)
+ eprintf("setsockopt:");
+
+ strlcpy(ifreq.ifr_name, ifname, IF_NAMESIZE);
+ ioctl(sock, SIOCGIFINDEX, &ifreq);
+ if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, &ifreq, sizeof(ifreq)) == -1)
+ eprintf("setsockopt:");
+ iptoaddr(&addr, IP(255, 255, 255, 255), 68);
+ if (bind(sock, (void*)&addr, sizeof(addr)) != 0)
+ eprintf("bind:");
+ ioctl(sock, SIOCGIFHWADDR, &ifreq);
+ memcpy(hwaddr, ifreq.ifr_hwaddr.sa_data, sizeof(ifreq.ifr_hwaddr.sa_data));
+ if (!cid[0])
+ memcpy(cid, hwaddr, sizeof(cid));
+
+ if ((rnd = open("/dev/urandom", O_RDONLY)) == -1)
+ eprintf("can't open /dev/urandom tidentifier:");
+ 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();
+
+ return 0;
+}
diff --git a/util.h b/util.h
@@ -0,0 +1,10 @@
+#define MIN(a,b) (((a)<(b))?(a):(b))
+#define LEN(a) (sizeof(a) / sizeof((a)[0]))
+#define bpdump(p,n) 1
+
+#undef strlcpy
+size_t strlcpy(char *, const char *, size_t);
+
+void weprintf(const char *, ...);
+void eprintf(const char *, ...);
+void enprintf(int, const char *, ...);
diff --git a/util/eprintf.c b/util/eprintf.c
@@ -0,0 +1,65 @@
+/* See LICENSE file for copyright and license details. */
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../util.h"
+
+char *argv0;
+
+static void venprintf(int, const char *, va_list);
+
+void
+eprintf(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ venprintf(1, fmt, ap);
+ va_end(ap);
+}
+
+void
+enprintf(int status, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ venprintf(status, fmt, ap);
+ va_end(ap);
+}
+
+void
+venprintf(int status, const char *fmt, va_list ap)
+{
+ if (strncmp(fmt, "usage", strlen("usage")))
+ fprintf(stderr, "%s: ", argv0);
+
+ vfprintf(stderr, fmt, ap);
+
+ if (fmt[0] && fmt[strlen(fmt)-1] == ':') {
+ fputc(' ', stderr);
+ perror(NULL);
+ }
+
+ exit(status);
+}
+
+void
+weprintf(const char *fmt, ...)
+{
+ va_list ap;
+
+ if (strncmp(fmt, "usage", strlen("usage")))
+ fprintf(stderr, "%s: ", argv0);
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+
+ if (fmt[0] && fmt[strlen(fmt)-1] == ':') {
+ fputc(' ', stderr);
+ perror(NULL);
+ }
+}
diff --git a/util/strlcpy.c b/util/strlcpy.c
@@ -0,0 +1,32 @@
+/* Taken from OpenBSD */
+#include <sys/types.h>
+#include <string.h>
+#include "../util.h"
+
+/*
+ * Copy src to string dst of size siz. At most siz-1 characters
+ * will be copied. Always NUL terminates (unless siz == 0).
+ * Returns strlen(src); if retval >= siz, truncation occurred.
+ */
+size_t
+strlcpy(char *dst, const char *src, size_t siz)
+{
+ char *d = dst;
+ const char *s = src;
+ size_t n = siz;
+ /* Copy as many bytes as will fit */
+ if (n != 0) {
+ while (--n != 0) {
+ if ((*d++ = *s++) == '\0')
+ break;
+ }
+ }
+ /* Not enough room in dst, add NUL and traverse rest of src */
+ if (n == 0) {
+ if (siz != 0)
+ *d = '\0'; /* NUL-terminate dst */
+ while (*s++)
+ ;
+ }
+ return(s - src - 1); /* count does not include NUL */
+}