commit a91f359dd69354befd8c70a89e0effd8891e1644
Author: sin <sin@2f30.org>
Date: Wed, 25 Sep 2013 12:14:00 +0100
Initial import of http://galos.no-ip.org/sdhcp
Diffstat:
A | Makefile | | | 14 | ++++++++++++++ |
A | debug.c | | | 139 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | sdhcp.8 | | | 16 | ++++++++++++++++ |
A | sdhcp.c | | | 437 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
4 files changed, 606 insertions(+), 0 deletions(-)
diff --git a/Makefile b/Makefile
@@ -0,0 +1,14 @@
+DESTDIR=
+sdhcp: sdhcp.c debug.c
+ $(CC) -O2 -o $@ sdhcp.c -static
+debug: sdhcp.c debug.c
+ $(CC) -DDEBUG -o sdhcp sdhcp.c -static
+
+all: sdhcp
+
+install: all
+ install -s sdhcp $(DESTDIR)/sbin
+ gzip -c sdhcp.8 > /usr/share/man/man8/sdhcp.8.gz
+
+clean:
+ rm -f sdhcp ?*~
diff --git a/debug.c b/debug.c
@@ -0,0 +1,139 @@
+#include<stdarg.h>
+#ifdef DEBUG
+unsigned short nhgets(uchar c[2]){
+ return ((c[0]<<8) + c[1])&0xffff;
+}
+unsigned long nhgetl(uchar c[4]){
+ return (nhgets(c)<<16) + nhgets(c+2);
+}
+char *ipstr(uchar *ip){
+ char * ch = malloc(3*4+3+10);
+ sprintf(ch, "%d.%d.%d.%d", ip[0],ip[1],ip[2],ip[3]);
+ return ch;
+}
+#endif
+
+void
+dbgprintf(char *str, ...)
+{
+#ifdef DEBUG
+ va_list ap;
+ va_start(ap, str);
+ vfprintf(stderr, str, ap);
+ va_end(ap);
+#endif
+}
+
+void
+bpdump(uchar *p, int n)
+{
+#ifdef DEBUG
+ int len, i, code;
+ Bootp *bp;
+// Udphdr *up;
+
+ bp = (Bootp*)p;
+ // up = (Udphdr*)bp->udphdr;
+
+ if(n < bp->magic - p) {
+ fprintf(stderr, "dhcpclient: short bootp packet");
+ return;
+ }
+ char *types[] = {"discover", "offer", "request",
+ "decline", "ack", "nak", "release", "inform"};
+ uchar type;
+ optget(bp, &type, ODtype, sizeof type);
+ fprintf(stderr, "DHCP%s\n", types[type-1]);
+ // fprintf(stderr, "laddr=%I lport=%d raddr=%I rport=%d\n", up->laddr,
+ // nhgets(up->lport), up->raddr, nhgets(up->rport));
+ fprintf(stderr, "op=%d htype=%d hlen=%d hops=%d\n", *bp->op, *bp->htype,
+ * bp->hlen, *bp->hops);
+ fprintf(stderr, "xid=%x secs=%d flags=%x\n", nhgetl(bp->xid),
+ nhgets(bp->secs), nhgets(bp->flags));
+ fprintf(stderr, "ciaddr=%s yiaddr=%s siaddr=%s giaddr=%s\n",
+ ipstr(bp->ciaddr), ipstr(bp->yiaddr), ipstr(bp->siaddr), ipstr(bp->giaddr));
+ fprintf(stderr, "chaddr=");
+ for(i=0; i<15; i++)
+ fprintf(stderr, "%.2x:", bp->chaddr[i]);
+ fprintf(stderr, "%.2x\n", bp->chaddr[15]);
+ fprintf(stderr, "sname=%s\n", bp->sname);
+ fprintf(stderr, "file = %s\n", bp->file);
+
+ n -= bp->magic - p;
+ p = bp->magic;
+
+ if(n < 4)
+ return;
+ if(memcmp(magic, p, 4) != 0)
+ fprintf(stderr, "dhcpclient: bad opt magic %#x %#x %#x %#x\n",
+ p[0], p[1], p[2], p[3]);
+ p += 4;
+ n -= 4;
+
+ while(n>0) {
+ code = *p++;
+ n--;
+ if(code == OBpad)
+ continue;
+ if(code == OBend)
+ break;
+ if(n == 0) {
+ fprintf(stderr, " bad option: %d", code);
+ return;
+ }
+ len = *p++;
+ n--;
+ if(len > n) {
+ fprintf(stderr, " bad option: %d", code);
+ return;
+ }
+ switch(code) {
+ default:
+ fprintf(stderr, "unknown option %d\n", code);
+ for(i = 0; i<len; i++)
+ fprintf(stderr, "%x ", p[i]);
+ fprintf(stderr, "\n");
+ break;
+ case ODtype:
+ fprintf(stderr, "DHCP type %d\n", p[0]);
+ break;
+ case ODclientid:
+ fprintf(stderr, "client id=");
+ for(i = 0; i<len; i++)
+ fprintf(stderr, "%x ", p[i]);
+ fprintf(stderr, "\n");
+ break;
+ case ODlease:
+ fprintf(stderr, "lease=%d sec\n", nhgetl(p));
+ break;
+ case ODserverid:
+ fprintf(stderr, "server id=%s\n", ipstr(p));
+ break;
+ case OBmask:
+ fprintf(stderr, "mask=%s\n", ipstr(p));
+ break;
+ case OBrouter:
+ fprintf(stderr, "router=%s\n", ipstr(p));
+ break;
+ case ODipaddr:
+ fprintf(stderr, "ip addr=%s\n", ipstr(p));
+ break;
+ case OBdnsserver:
+ fprintf(stderr, "dns=%s\n", ipstr(p));
+ break;
+ case OBbaddr:
+ fprintf(stderr, "broadcast=%s\n", ipstr(p));
+ break;
+ case ODrenewaltime:
+ fprintf(stderr, "renew time=%d sec\n", nhgetl(p));
+ break;
+ case ODrebindingtime:
+ fprintf(stderr, "rebind time=%d sec\n", nhgetl(p));
+ break;
+ }
+ p += len;
+ n -= len;
+ }
+#endif
+ }
+
diff --git a/sdhcp.8 b/sdhcp.8
@@ -0,0 +1,16 @@
+.TH SDHCP 1
+.SH NAME
+sdhcp \- a simple dhcp client
+.SH SYNOPSIS
+.B sdhcp
+.RB [interface]
+.SH DESCRIPTION
+sdchp 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 BUGS
+I'm sure there's plenty. It only currently supports a small subset of
+dhcp options, and has been untested on larger networks. It ignores most of
+the dchp options it understands. Send bug reports to me!
+.SH AUTHOR
+David Galos (galosd83 (at) students.rowan.edu)
diff --git a/sdhcp.c b/sdhcp.c
@@ -0,0 +1,437 @@
+#include<sys/socket.h>
+#include<sys/ioctl.h>
+#include<netinet/in.h>
+#include<net/if.h>
+#include<net/route.h>
+#include<signal.h>
+#include<sys/poll.h>
+#include<errno.h>
+#include<fcntl.h>
+#include<stdio.h>
+#include<stdlib.h>
+#include<string.h>
+
+#define MIN(a,b) (((a)<(b))?(a):(b))
+
+typedef unsigned char uchar;
+typedef struct Bootp Bootp;
+struct Bootp {
+ uchar op [1];
+ uchar htype [1];
+ uchar hlen [1];
+ uchar hops [1];
+ uchar xid [4];
+ uchar secs [2];
+ uchar flags [2];
+ uchar ciaddr [4];
+ uchar yiaddr [4];
+ uchar siaddr [4];
+ uchar giaddr [4];
+ uchar chaddr [16];
+ uchar sname [64];
+ uchar file [128];
+ uchar magic [4];
+ uchar optdata [312-4];
+};
+
+void bpdump(uchar *p, int n);
+enum {
+ DHCPdiscover = 1, DHCPoffer, DHCPrequest,
+ DHCPdecline, DHCPack, DHCPnak, DHCPrelease,
+ DHCPinform, Timeout=200,
+
+ Bootrequest= 1,
+ Bootreply= 2,
+ /* bootp flags */
+ Fbroadcast= 1<<15,
+
+ OBpad= 0,
+ OBmask= 1,
+ OBrouter= 3,
+ OBnameserver= 5,
+ OBdnsserver= 6,
+ 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};
+
+Bootp bp;
+uchar magic[] = {99, 130, 83, 99};
+
+//struct conf{
+ uchar xid[sizeof bp.xid];
+ uchar hwaddr[16];
+ time_t starttime;
+ char *ifname = "eth0";
+ char *cid = "vaio.12340";
+ int sock;
+//} var;
+//struct sav{
+ uchar server[4];
+ uchar client[4];
+ uchar mask[4];
+ uchar router[4];
+ uchar dns[4];
+ unsigned long t1;
+ unsigned long t2;
+//} sav;
+
+#define IP(...) (uchar[4]){__VA_ARGS__}
+
+static void
+die(char *str)
+{
+ perror(str);
+ exit(EXIT_FAILURE);
+}
+
+static void
+hnput(uchar *dst, unsigned long long src, int n)
+{
+ int x;
+ for(x=0; n--; x++)
+ dst[x] = (src>>(n*8))&0xff;
+}
+
+static struct sockaddr
+iptoaddr(uchar ip[4], int port)
+{
+ struct sockaddr_in ifaddr;
+ ifaddr.sin_family=AF_INET;
+ ifaddr.sin_port = htons(port);
+ memcpy(&ifaddr.sin_addr, ip, sizeof ifaddr.sin_addr);
+ return *(struct sockaddr*)&ifaddr;
+}
+
+#define UDPWRAPPER(name, func, port, hack) \
+static int name(uchar ip[4], int fd, void *data, size_t n){\
+ struct sockaddr addr = iptoaddr(ip, port);\
+ int x, y = sizeof addr;\
+ if((x=func(fd, data, n, 0, &addr, hack y))==-1)\
+ die(#func);\
+ return x;\
+}
+UDPWRAPPER(udpsend, sendto, 67, )
+UDPWRAPPER(udprecv, recvfrom, 68, &)
+
+static void
+setip(uchar ip[4], uchar mask[4], uchar gateway[4])
+{
+ int fd, x;
+ struct ifreq ifreq = {0,};
+ struct rtentry rtreq = {0,};
+
+ fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
+ strcpy(ifreq.ifr_name, ifname);
+ ifreq.ifr_addr = iptoaddr(ip, 0);
+ ioctl(fd, SIOCSIFADDR , &ifreq);
+ ifreq.ifr_netmask = iptoaddr(mask, 0);
+ ioctl(fd, SIOCSIFNETMASK , &ifreq);
+ ifreq.ifr_flags=IFF_UP|IFF_RUNNING|IFF_BROADCAST|IFF_MULTICAST;
+ ioctl(fd, SIOCSIFFLAGS , &ifreq);
+
+ rtreq.rt_flags = (RTF_UP | RTF_GATEWAY);
+ rtreq.rt_gateway = iptoaddr(gateway, 0);
+ rtreq.rt_genmask = iptoaddr(IP(0,0,0,0), 0);
+ rtreq.rt_dst = iptoaddr(IP(0,0,0,0), 0);
+ ioctl(fd, SIOCADDRT , &rtreq);
+
+ close(fd);
+}
+static void
+cat(int dfd, char *src)
+{
+ char buf[4096];
+ int n, sfd = open(src, O_RDONLY);
+ while((n=read(sfd, buf, sizeof buf))>0)
+ write(dfd, buf, n);
+ close(sfd);
+
+}
+//use itoa not sprintf to make dietlibc happy.
+char* itoa(char * str, int x)
+{
+ if(x==0){
+ *str='0';
+ return str+1;
+ }
+ int k = 1;
+ char *ep = str;
+ while(x/k > 0)
+ k*=10;
+ while((k/=10)>=1)
+ *ep++ = '0'+((x/k)%10);
+ *ep = '\0';
+ return str+strlen(str);
+}
+static void
+setdns(uchar dns[4])
+{
+ char buf[128], *bp = buf;
+ int fd = creat("/etc/resolv.conf", 0644);
+ cat(fd, "/etc/resolv.conf.head");
+ memcpy(buf, "\nnameserver ", 12), bp+=11;
+ *(bp=itoa(bp+1, dns[0])) = '.';
+ *(bp=itoa(bp+1, dns[1])) = '.';
+ *(bp=itoa(bp+1, dns[2])) = '.';
+ *(bp=itoa(bp+1, dns[3])) = '\n';
+ *++bp = '\0';
+ write(fd, buf, strlen(buf));
+ cat(fd, "/etc/resolv.conf.tail");
+ close(fd);
+}
+
+static uchar *
+optget(Bootp *bp, void *data, int opt, int n)
+{
+ uchar *p = bp->optdata;
+ uchar *top = ((uchar*)bp)+sizeof *bp;
+ while(p<top){
+ int code = *p++;
+ if(code==OBpad)
+ continue;
+ if(code==OBend || p==top)
+ break;
+ int len = *p++;
+ if(len > top-p)
+ break;
+ if(code==opt){
+ memcpy(data, p, MIN(len, n));
+ return p;
+ }
+ p+=len;
+ }
+}
+
+static uchar *
+optput(uchar *p, int opt, uchar *data, int len)
+{
+ *p++ = opt;
+ *p++ = (uchar)len;
+ memcpy(p, data, len);
+ return p+len;
+}
+static uchar*
+hnoptput(uchar *p, int opt, long long data, int len)
+{
+ *p++=opt;
+ *p++ = (uchar)len;
+ hnput(p, data, len);
+ return p+len;
+}
+#include "debug.c"
+
+static void
+dhcpsend(int type, int how)
+{
+ dbgprintf("\nSending ");
+ 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);
+ uchar *p = bp.optdata;
+ p = hnoptput(p, ODtype, type, 1);
+ p = optput(p, ODclientid, cid, strlen(cid));
+
+ switch(type){
+ case DHCPdiscover:
+ break;
+ case DHCPrequest:
+// memcpy(bp.ciaddr, client, sizeof bp.ciaddr);
+ p = hnoptput(p, ODlease, t1, sizeof t1);
+ 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;
+ bpdump((void*)&bp, p-(uchar*)&bp);
+ uchar *ip = (how==Broadcast)?IP(255,255,255,255):server;
+ udpsend(ip, sock, &bp, p-(uchar*)&bp);
+}
+
+static int
+dhcprecv()
+{
+ dbgprintf("\nReceiving ");
+ memset(&bp, 0, sizeof bp);
+ struct pollfd pfd = {sock, POLLIN};
+ if(poll(&pfd, 1, -1)==-1){
+ if(errno!=EINTR)
+ die("poll");
+ else
+ return Timeout;
+ }
+ int x = udprecv(IP(255,255,255,255), sock, &bp, sizeof bp);
+ bpdump((void*)&bp, x);
+ uchar type;
+ optget(&bp, &type, ODtype, sizeof type);
+ return type;
+}
+
+static void
+acceptlease()
+{
+ setip(client, mask, router);
+ setdns(dns);
+ alarm(t1);
+}
+
+static void
+run()
+{
+#if 0
+InitReboot:
+ //send DHCPrequest to old server
+ dhcpsend(DHCPrequest, Broadcasr);
+ goto Rebooting;
+Rebooting:
+ switch (dhcprecv()){
+ case DHCPnak:
+ goto Init;
+ case DHCPack:
+ acceptoffer();
+ goto Bound;
+ }
+#endif
+Init:
+ dbgprintf("\n\n------- Init ------\n");
+ dhcpsend(DHCPdiscover, Broadcast);
+ alarm(1);
+ goto Selecting;
+Selecting:
+ dbgprintf("\n\n------- Selecting ------\n");
+ switch(dhcprecv()){
+ case DHCPoffer:
+ alarm(0);
+ 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);
+ dhcpsend(DHCPrequest, Broadcast);
+ goto Requesting;
+ case Timeout:
+ goto Init;
+ default:
+ goto Selecting;
+ }
+Requesting:
+ dbgprintf("\n\n------- Requesting ------\n");
+ switch(dhcprecv()){
+ case DHCPoffer:
+ goto Requesting; //ignore other offers.
+// case DHCPack: //(and you don't want it)?
+// dhcpsend(DHCPdecline, Unicast);
+// goto Init;
+ case DHCPack:
+ acceptlease();
+ goto Bound;
+ }
+Bound:
+ dbgprintf("\n\n------- Bound ------\n");
+ write(1, "Congrats! You should be on the 'net.\n", 37);
+ if(fork())
+ exit(0);
+ switch (dhcprecv()){
+ case DHCPoffer:
+ case DHCPack:
+ case DHCPnak:
+ goto Bound; //discard offer, ack, or nak
+ case Timeout:
+ dhcpsend(DHCPrequest, Unicast);
+ goto Renewing;
+ }
+Renewing:
+ dbgprintf("\n\n------- Renewing ------\n");
+ switch(dhcprecv()){
+ case DHCPack:
+ acceptlease();
+ goto Bound;
+ case DHCPnak:
+ //halt network;
+ goto Init;
+ case Timeout: //t2 expires:
+ dhcpsend(DHCPrequest, Broadcast);
+ goto Rebinding;
+ }
+Rebinding:
+ dbgprintf("\n\n------- Rebinding ------\n");
+ switch(dhcprecv()){
+ case DHCPnak: //lease expired
+ //halt network;
+ goto Init;
+ case DHCPack:
+ acceptlease();
+ goto Bound;
+ }
+}
+
+static void nop(int unused){ }
+static void cleanexit(int unused){
+ dhcpsend(DHCPrelease, Unicast);
+ exit(0);
+}
+
+int
+main(int argc, char *argv[])
+{
+ if(argc>2){
+ write(2, "usage: sdhcp [inferface]\n",25);
+ exit(EXIT_FAILURE);
+ }if(argc==2)
+ ifname = argv[1];
+
+ signal(SIGALRM, nop);
+ signal(SIGTERM, cleanexit);
+
+ if((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+ die("socket");
+ int bcast = 1;
+ if(setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &bcast, sizeof bcast)==-1)
+ die("setsockopt");
+ struct ifreq ifreq = {0,};
+ strcpy(ifreq.ifr_name, ifname);
+ ioctl(sock, SIOCGIFINDEX, &ifreq);
+ if(setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, &ifreq, sizeof ifreq)==-1)
+ die("setsockopt");
+ struct sockaddr addr = iptoaddr(IP(255,255,255,255), 68);
+ if(bind(sock, (void*)&addr, sizeof addr)!=0)
+ die("bind");
+ ioctl(sock, SIOCGIFHWADDR, &ifreq);
+ memcpy(hwaddr, ifreq.ifr_hwaddr.sa_data, sizeof ifreq.ifr_hwaddr.sa_data);
+ int rnd = open("/dev/urandom", O_RDONLY);
+ read(rnd, xid, sizeof xid);
+ close(rnd);
+
+ starttime = time(NULL);
+ run();
+ return 0;
+}
+