diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/ip/dhcpclient.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/ip/dhcpclient.c')
-rwxr-xr-x | sys/src/cmd/ip/dhcpclient.c | 656 |
1 files changed, 656 insertions, 0 deletions
diff --git a/sys/src/cmd/ip/dhcpclient.c b/sys/src/cmd/ip/dhcpclient.c new file mode 100755 index 000000000..3e84b3e7a --- /dev/null +++ b/sys/src/cmd/ip/dhcpclient.c @@ -0,0 +1,656 @@ +#include <u.h> +#include <libc.h> +#include <ip.h> +#include "dhcp.h" + +void bootpdump(uchar *p, int n); +void dhcpinit(void); +void dhcprecv(void); +void dhcpsend(int); +void myfatal(char *fmt, ...); +int openlisten(char*); +uchar *optaddaddr(uchar*, int, uchar*); +uchar *optaddbyte(uchar*, int, int); +uchar *optadd(uchar*, int, void*, int); +uchar *optaddulong(uchar*, int, ulong); +uchar *optget(Bootp*, int, int); +int optgetaddr(Bootp*, int, uchar*); +int optgetbyte(Bootp*, int); +ulong optgetulong(Bootp*, int); +Bootp *parse(uchar*, int); +void stdinthread(void*); +ulong thread(void(*f)(void*), void *a); +void timerthread(void*); +void usage(void); + +struct { + QLock lk; + int state; + int fd; + ulong xid; + ulong starttime; + char cid[100]; + char sname[64]; + uchar server[IPaddrlen]; /* server IP address */ + uchar client[IPaddrlen]; /* client IP address */ + uchar mask[IPaddrlen]; /* client mask */ + ulong lease; /* lease time */ + ulong resend; /* number of resends for current state */ + ulong timeout; /* time to timeout - seconds */ +} dhcp; + +char net[64]; + +char optmagic[4] = { 0x63, 0x82, 0x53, 0x63 }; + +void +main(int argc, char *argv[]) +{ + char *p; + + setnetmtpt(net, sizeof(net), nil); + + ARGBEGIN{ + case 'x': + p = ARGF(); + if(p == nil) + usage(); + setnetmtpt(net, sizeof(net), p); + }ARGEND; + + fmtinstall('E', eipfmt); + fmtinstall('I', eipfmt); + fmtinstall('V', eipfmt); + + dhcpinit(); + + rfork(RFNOTEG|RFREND); + + thread(timerthread, 0); + thread(stdinthread, 0); + + qlock(&dhcp.lk); + dhcp.starttime = time(0); + dhcp.fd = openlisten(net); + dhcpsend(Discover); + dhcp.state = Sselecting; + dhcp.resend = 0; + dhcp.timeout = 4; + + while(dhcp.state != Sbound) + dhcprecv(); + + /* allows other clients on this machine */ + close(dhcp.fd); + dhcp.fd = -1; + + print("ip=%I\n", dhcp.client); + print("mask=%I\n", dhcp.mask); + print("end\n"); + + /* keep lease alive */ + for(;;) { +//fprint(2, "got lease for %d\n", dhcp.lease); + qunlock(&dhcp.lk); + sleep(dhcp.lease*500); /* wait half of lease time */ + qlock(&dhcp.lk); + +//fprint(2, "try renue\n", dhcp.lease); + dhcp.starttime = time(0); + dhcp.fd = openlisten(net); + dhcp.xid = time(0)*getpid(); + dhcpsend(Request); + dhcp.state = Srenewing; + dhcp.resend = 0; + dhcp.timeout = 1; + + while(dhcp.state != Sbound) + dhcprecv(); + + /* allows other clients on this machine */ + close(dhcp.fd); + dhcp.fd = -1; + } +} + +void +usage(void) +{ + fprint(2, "usage: %s [-x netextension]\n", argv0); + exits("usage"); +} + +void +timerthread(void*) +{ + for(;;) { + sleep(1000); + qlock(&dhcp.lk); + if(--dhcp.timeout > 0) { + qunlock(&dhcp.lk); + continue; + } + + switch(dhcp.state) { + default: + myfatal("timerthread: unknown state %d", dhcp.state); + case Sinit: + break; + case Sselecting: + dhcpsend(Discover); + dhcp.timeout = 4; + dhcp.resend++; + if(dhcp.resend>5) + myfatal("dhcp: giving up: selecting"); + break; + case Srequesting: + dhcpsend(Request); + dhcp.timeout = 4; + dhcp.resend++; + if(dhcp.resend>5) + myfatal("dhcp: giving up: requesting"); + break; + case Srenewing: + dhcpsend(Request); + dhcp.timeout = 1; + dhcp.resend++; + if(dhcp.resend>3) { + dhcp.state = Srebinding; + dhcp.resend = 0; + } + break; + case Srebinding: + dhcpsend(Request); + dhcp.timeout = 4; + dhcp.resend++; + if(dhcp.resend>5) + myfatal("dhcp: giving up: rebinding"); + break; + case Sbound: + break; + } + qunlock(&dhcp.lk); + } +} + +void +stdinthread(void*) +{ + uchar buf[100]; + int n; + + for(;;) { + n = read(0, buf, sizeof(buf)); + if(n <= 0) + break; + } + /* shutdown cleanly */ + qlock(&dhcp.lk); + if(dhcp.client) { + if(dhcp.fd < 0) + dhcp.fd = openlisten(net); + dhcpsend(Release); + } + qunlock(&dhcp.lk); + + postnote(PNGROUP, getpid(), "die"); + exits(0); +} + +void +dhcpinit(void) +{ + int fd; + + dhcp.state = Sinit; + dhcp.timeout = 4; + + fd = open("/dev/random", 0); + if(fd >= 0) { + read(fd, &dhcp.xid, sizeof(dhcp.xid)); + close(fd); + } else + dhcp.xid = time(0)*getpid(); + srand(dhcp.xid); + + sprint(dhcp.cid, "%s.%d", getenv("sysname"), getpid()); +} + +void +dhcpsend(int type) +{ + int n; + uchar *p; + Bootp bp; + Udphdr *up; + + memset(&bp, 0, sizeof bp); + up = (Udphdr*)bp.udphdr; + + hnputs(up->rport, 67); + bp.op = Bootrequest; + hnputl(bp.xid, dhcp.xid); + hnputs(bp.secs, time(0) - dhcp.starttime); + hnputs(bp.flags, Fbroadcast); /* reply must be broadcast */ + memmove(bp.optmagic, optmagic, 4); + p = bp.optdata; + p = optaddbyte(p, ODtype, type); + p = optadd(p, ODclientid, dhcp.cid, strlen(dhcp.cid)); + switch(type) { + default: + myfatal("dhcpsend: unknown message type: %d", type); + case Discover: + ipmove(up->raddr, IPv4bcast); /* broadcast */ + break; + case Request: + if(dhcp.state == Sbound || dhcp.state == Srenewing) + ipmove(up->raddr, dhcp.server); + else + ipmove(up->raddr, IPv4bcast); /* broadcast */ + p = optaddulong(p, ODlease, dhcp.lease); + if(dhcp.state == Sselecting || dhcp.state == Srequesting) { + p = optaddaddr(p, ODipaddr, dhcp.client); /* mistake?? */ + p = optaddaddr(p, ODserverid, dhcp.server); + } else + v6tov4(bp.ciaddr, dhcp.client); + break; + case Release: + ipmove(up->raddr, dhcp.server); + v6tov4(bp.ciaddr, dhcp.client); + p = optaddaddr(p, ODipaddr, dhcp.client); + p = optaddaddr(p, ODserverid, dhcp.server); + break; + } + + *p++ = OBend; + + n = p - (uchar*)&bp; + + if(write(dhcp.fd, &bp, n) != n) + myfatal("dhcpsend: write failed: %r"); +} + +void +dhcprecv(void) +{ + uchar buf[2000]; + Bootp *bp; + int n, type; + ulong lease; + uchar mask[IPaddrlen]; + + qunlock(&dhcp.lk); + n = read(dhcp.fd, buf, sizeof(buf)); + qlock(&dhcp.lk); + + if(n <= 0) + myfatal("dhcprecv: bad read: %r"); + + bp = parse(buf, n); + if(bp == 0) + return; + +if(1) { +fprint(2, "recved\n"); +bootpdump(buf, n); +} + + type = optgetbyte(bp, ODtype); + switch(type) { + default: + fprint(2, "dhcprecv: unknown type: %d\n", type); + break; + case Offer: + if(dhcp.state != Sselecting) + break; + lease = optgetulong(bp, ODlease); + if(lease == 0) + myfatal("bad lease"); + if(!optgetaddr(bp, OBmask, mask)) + memset(mask, 0xff, sizeof(mask)); + v4tov6(dhcp.client, bp->yiaddr); + if(!optgetaddr(bp, ODserverid, dhcp.server)) { + fprint(2, "dhcprecv: Offer from server with invalid serverid\n"); + break; + } + + dhcp.lease = lease; + ipmove(dhcp.mask, mask); + memmove(dhcp.sname, bp->sname, sizeof(dhcp.sname)); + dhcp.sname[sizeof(dhcp.sname)-1] = 0; + + dhcpsend(Request); + dhcp.state = Srequesting; + dhcp.resend = 0; + dhcp.timeout = 4; + break; + case Ack: + if(dhcp.state != Srequesting) + if(dhcp.state != Srenewing) + if(dhcp.state != Srebinding) + break; + lease = optgetulong(bp, ODlease); + if(lease == 0) + myfatal("bad lease"); + if(!optgetaddr(bp, OBmask, mask)) + memset(mask, 0xff, sizeof(mask)); + v4tov6(dhcp.client, bp->yiaddr); + dhcp.lease = lease; + ipmove(dhcp.mask, mask); + dhcp.state = Sbound; + break; + case Nak: + myfatal("recved nak"); + break; + } + +} + +int +openlisten(char *net) +{ + int n, fd, cfd; + char data[128], devdir[40]; + +// sprint(data, "%s/udp!*!bootpc", net); + sprint(data, "%s/udp!*!68", net); + for(n = 0; ; n++) { + cfd = announce(data, devdir); + if(cfd >= 0) + break; + /* might be another client - wait and try again */ + fprint(2, "dhcpclient: can't announce %s: %r", data); + sleep(1000); + if(n > 10) + myfatal("can't announce: giving up: %r"); + } + + if(fprint(cfd, "headers") < 0) + myfatal("can't set header mode: %r"); + + sprint(data, "%s/data", devdir); + fd = open(data, ORDWR); + if(fd < 0) + myfatal("open %s: %r", data); + close(cfd); + return fd; +} + +uchar* +optadd(uchar *p, int op, void *d, int n) +{ + p[0] = op; + p[1] = n; + memmove(p+2, d, n); + return p+n+2; +} + +uchar* +optaddbyte(uchar *p, int op, int b) +{ + p[0] = op; + p[1] = 1; + p[2] = b; + return p+3; +} + +uchar* +optaddulong(uchar *p, int op, ulong x) +{ + p[0] = op; + p[1] = 4; + hnputl(p+2, x); + return p+6; +} + +uchar * +optaddaddr(uchar *p, int op, uchar *ip) +{ + p[0] = op; + p[1] = 4; + v6tov4(p+2, ip); + return p+6; +} + +uchar* +optget(Bootp *bp, int op, int n) +{ + int len, code; + uchar *p; + + p = bp->optdata; + for(;;) { + code = *p++; + if(code == OBpad) + continue; + if(code == OBend) + return 0; + len = *p++; + if(code != op) { + p += len; + continue; + } + if(n && n != len) + return 0; + return p; + } +} + + +int +optgetbyte(Bootp *bp, int op) +{ + uchar *p; + + p = optget(bp, op, 1); + if(p == 0) + return 0; + return *p; +} + +ulong +optgetulong(Bootp *bp, int op) +{ + uchar *p; + + p = optget(bp, op, 4); + if(p == 0) + return 0; + return nhgetl(p); +} + +int +optgetaddr(Bootp *bp, int op, uchar *ip) +{ + uchar *p; + + p = optget(bp, op, 4); + if(p == 0) + return 0; + v4tov6(ip, p); + return 1; +} + +/* make sure packet looks ok */ +Bootp * +parse(uchar *p, int n) +{ + int len, code; + Bootp *bp; + + bp = (Bootp*)p; + if(n < bp->optmagic - p) { + fprint(2, "dhcpclient: parse: short bootp packet"); + return 0; + } + + if(dhcp.xid != nhgetl(bp->xid)) { + fprint(2, "dhcpclient: parse: bad xid: got %ux expected %lux\n", + nhgetl(bp->xid), dhcp.xid); + return 0; + } + + if(bp->op != Bootreply) { + fprint(2, "dhcpclient: parse: bad op\n"); + return 0; + } + + n -= bp->optmagic - p; + p = bp->optmagic; + + if(n < 4) { + fprint(2, "dhcpclient: parse: not option data"); + return 0; + } + if(memcmp(optmagic, p, 4) != 0) { + fprint(2, "dhcpclient: parse: bad opt magic %ux %ux %ux %ux\n", + p[0], p[1], p[2], p[3]); + return 0; + } + p += 4; + n -= 4; + while(n>0) { + code = *p++; + n--; + if(code == OBpad) + continue; + if(code == OBend) + return bp; + if(n == 0) { + fprint(2, "dhcpclient: parse: bad option: %d", code); + return 0; + } + len = *p++; + n--; + if(len > n) { + fprint(2, "dhcpclient: parse: bad option: %d", code); + return 0; + } + p += len; + n -= len; + } + + /* fix up nonstandard packets */ + /* assume there is space */ + *p = OBend; + + return bp; +} + +void +bootpdump(uchar *p, int n) +{ + int len, i, code; + Bootp *bp; + Udphdr *up; + + bp = (Bootp*)p; + up = (Udphdr*)bp->udphdr; + + if(n < bp->optmagic - p) { + fprint(2, "dhcpclient: short bootp packet"); + return; + } + + fprint(2, "laddr=%I lport=%d raddr=%I rport=%d\n", up->laddr, + nhgets(up->lport), up->raddr, nhgets(up->rport)); + fprint(2, "op=%d htype=%d hlen=%d hops=%d\n", bp->op, bp->htype, + bp->hlen, bp->hops); + fprint(2, "xid=%ux secs=%d flags=%ux\n", nhgetl(bp->xid), + nhgets(bp->secs), nhgets(bp->flags)); + fprint(2, "ciaddr=%V yiaddr=%V siaddr=%V giaddr=%V\n", + bp->ciaddr, bp->yiaddr, bp->siaddr, bp->giaddr); + fprint(2, "chaddr="); + for(i=0; i<16; i++) + fprint(2, "%ux ", bp->chaddr[i]); + fprint(2, "\n"); + fprint(2, "sname=%s\n", bp->sname); + fprint(2, "file = %s\n", bp->file); + + n -= bp->optmagic - p; + p = bp->optmagic; + + if(n < 4) + return; + if(memcmp(optmagic, p, 4) != 0) + fprint(2, "dhcpclient: bad opt magic %ux %ux %ux %ux\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) { + fprint(2, " bad option: %d", code); + return; + } + len = *p++; + n--; + if(len > n) { + fprint(2, " bad option: %d", code); + return; + } + switch(code) { + default: + fprint(2, "unknown option %d\n", code); + for(i = 0; i<len; i++) + fprint(2, "%ux ", p[i]); + case ODtype: + fprint(2, "DHCP type %d\n", p[0]); + break; + case ODclientid: + fprint(2, "client id="); + for(i = 0; i<len; i++) + fprint(2, "%ux ", p[i]); + fprint(2, "\n"); + break; + case ODlease: + fprint(2, "lease=%d\n", nhgetl(p)); + break; + case ODserverid: + fprint(2, "server id=%V\n", p); + break; + case OBmask: + fprint(2, "mask=%V\n", p); + break; + case OBrouter: + fprint(2, "router=%V\n", p); + break; + } + p += len; + n -= len; + } +} + +ulong +thread(void(*f)(void*), void *a) +{ + int pid; + + pid = rfork(RFNOWAIT|RFMEM|RFPROC); + if(pid < 0) + myfatal("rfork failed: %r"); + if(pid != 0) + return pid; + (*f)(a); + return 0; /* never reaches here */ +} + +void +myfatal(char *fmt, ...) +{ + char buf[1024]; + va_list arg; + + va_start(arg, fmt); + vseprint(buf, buf+sizeof(buf), fmt, arg); + va_end(arg); + fprint(2, "%s: %s\n", argv0, buf); + postnote(PNGROUP, getpid(), "die"); + exits(buf); +} |