123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665 |
- /*
- * This file is part of the UCB release of Plan 9. It is subject to the license
- * terms in the LICENSE file found in the top-level directory of this
- * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
- * part of the UCB release of Plan 9, including this file, may be copied,
- * modified, propagated, or distributed except according to the terms contained
- * in the LICENSE file.
- */
- #include <u.h>
- #include <libc.h>
- #include <ip.h>
- #include "dhcp.h"
- void bootpdump(uint8_t *p, int n);
- void dhcpinit(void);
- void dhcprecv(void);
- void dhcpsend(int);
- void myfatal(char *fmt, ...);
- int openlisten(char*);
- uint8_t *optaddaddr(uint8_t*, int, uint8_t*);
- uint8_t *optaddbyte(uint8_t*, int, int);
- uint8_t *optadd(uint8_t*, int, void*, int);
- uint8_t *optaddulong(uint8_t*, int, uint32_t);
- uint8_t *optget(Bootp*, int, int);
- int optgetaddr(Bootp*, int, uint8_t*);
- int optgetbyte(Bootp*, int);
- uint32_t optgetulong(Bootp*, int);
- Bootp *parse(uint8_t*, int);
- void stdinthread(void*);
- uint32_t thread(void(*f)(void*), void *a);
- void timerthread(void*);
- void usage(void);
- struct {
- QLock lk;
- int state;
- int fd;
- uint32_t xid;
- uint32_t starttime;
- char cid[100];
- char sname[64];
- uint8_t server[IPaddrlen]; /* server IP address */
- uint8_t client[IPaddrlen]; /* client IP address */
- uint8_t mask[IPaddrlen]; /* client mask */
- uint32_t lease; /* lease time */
- uint32_t resend; /* number of resends for current state */
- uint32_t 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* v)
- {
- 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* v)
- {
- uint8_t 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;
- uint8_t *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 - (uint8_t*)&bp;
- if(write(dhcp.fd, &bp, n) != n)
- myfatal("dhcpsend: write failed: %r");
- }
- void
- dhcprecv(void)
- {
- uint8_t buf[2000];
- Bootp *bp;
- int n, type;
- uint32_t lease;
- uint8_t 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;
- }
- uint8_t*
- optadd(uint8_t *p, int op, void *d, int n)
- {
- p[0] = op;
- p[1] = n;
- memmove(p+2, d, n);
- return p+n+2;
- }
- uint8_t*
- optaddbyte(uint8_t *p, int op, int b)
- {
- p[0] = op;
- p[1] = 1;
- p[2] = b;
- return p+3;
- }
- uint8_t*
- optaddulong(uint8_t *p, int op, uint32_t x)
- {
- p[0] = op;
- p[1] = 4;
- hnputl(p+2, x);
- return p+6;
- }
- uint8_t *
- optaddaddr(uint8_t *p, int op, uint8_t *ip)
- {
- p[0] = op;
- p[1] = 4;
- v6tov4(p+2, ip);
- return p+6;
- }
- uint8_t*
- optget(Bootp *bp, int op, int n)
- {
- int len, code;
- uint8_t *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)
- {
- uint8_t *p;
- p = optget(bp, op, 1);
- if(p == 0)
- return 0;
- return *p;
- }
- uint32_t
- optgetulong(Bootp *bp, int op)
- {
- uint8_t *p;
- p = optget(bp, op, 4);
- if(p == 0)
- return 0;
- return nhgetl(p);
- }
- int
- optgetaddr(Bootp *bp, int op, uint8_t *ip)
- {
- uint8_t *p;
- p = optget(bp, op, 4);
- if(p == 0)
- return 0;
- v4tov6(ip, p);
- return 1;
- }
- /* make sure packet looks ok */
- Bootp *
- parse(uint8_t *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(uint8_t *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;
- }
- }
- uint32_t
- 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);
- }
|