sdhcp

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

commit a91f359dd69354befd8c70a89e0effd8891e1644
Author: sin <sin@2f30.org>
Date:   Wed Sep 25 12:14:00 +0100

Initial import of http://galos.no-ip.org/sdhcp

Diffstat:
Makefile | 14++++++++++++++
debug.c | 139+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sdhcp.8 | 16++++++++++++++++
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; +} +