diff options
| author | Charles.Forsyth <devnull@localhost> | 2006-12-22 21:39:35 +0000 |
|---|---|---|
| committer | Charles.Forsyth <devnull@localhost> | 2006-12-22 21:39:35 +0000 |
| commit | 74a4d8c26dd3c1e9febcb717cfd6cb6512991a7a (patch) | |
| tree | c6e220ba61db3a6ea4052e6841296d829654e664 /os/ip/bootp.c | |
| parent | 46439007cf417cbd9ac8049bb4122c890097a0fa (diff) | |
20060303
Diffstat (limited to 'os/ip/bootp.c')
| -rw-r--r-- | os/ip/bootp.c | 231 |
1 files changed, 231 insertions, 0 deletions
diff --git a/os/ip/bootp.c b/os/ip/bootp.c new file mode 100644 index 00000000..b7d3fcda --- /dev/null +++ b/os/ip/bootp.c @@ -0,0 +1,231 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "kernel.h" +#include "ip.h" + +static ulong fsip; +static ulong auip; +static ulong gwip; +static ulong ipmask; +static ulong ipaddr; + +enum +{ + Bootrequest = 1, + Bootreply = 2, +}; + +typedef struct Bootp +{ + /* udp.c oldheader */ + uchar raddr[IPaddrlen]; + uchar laddr[IPaddrlen]; + uchar rport[2]; + uchar lport[2]; + /* bootp itself */ + uchar op; /* opcode */ + uchar htype; /* hardware type */ + uchar hlen; /* hardware address len */ + uchar hops; /* hops */ + uchar xid[4]; /* a random number */ + uchar secs[2]; /* elapsed snce client started booting */ + uchar pad[2]; + uchar ciaddr[4]; /* client IP address (client tells server) */ + uchar yiaddr[4]; /* client IP address (server tells client) */ + uchar siaddr[4]; /* server IP address */ + uchar giaddr[4]; /* gateway IP address */ + uchar chaddr[16]; /* client hardware address */ + uchar sname[64]; /* server host name (optional) */ + uchar file[128]; /* boot file name */ + uchar vend[128]; /* vendor-specific goo */ +} Bootp; + +/* + * bootp returns: + * + * "fsip d.d.d.d + * auip d.d.d.d + * gwip d.d.d.d + * ipmask d.d.d.d + * ipaddr d.d.d.d" + * + * where d.d.d.d is the IP address in dotted decimal notation, and each + * address is followed by a newline. + */ + +static Bootp req; +static Proc* rcvprocp; +static int recv; +static int done; +static Rendez bootpr; +static char rcvbuf[512+2*IPaddrlen+2*2]; + +static void +rcvbootp(void *a) +{ + int n, fd; + Bootp *rp; + char *field[4]; + uchar ip[IPaddrlen]; + + if(waserror()) + pexit("", 0); + rcvprocp = up; /* store for postnote below */ + fd = (int)a; + while(done == 0) { + n = kread(fd, rcvbuf, sizeof(rcvbuf)); + if(n <= 0) + break; + rp = (Bootp*)rcvbuf; + /* currently ignore udp's header */ + if(memcmp(req.chaddr, rp->chaddr, 6) == 0 + && rp->htype == 1 && rp->hlen == 6 + && getfields((char*)rp->vend+4, field, 4, 1, " ") == 4 + && strncmp((char*)rp->vend, "p9 ", 4) == 0){ + if(ipaddr == 0) + ipaddr = nhgetl(rp->yiaddr); + if(ipmask == 0) + ipmask = parseip(ip, field[0]); + if(fsip == 0) + fsip = parseip(ip, field[1]); + if(auip == 0) + auip = parseip(ip, field[2]); + if(gwip == 0) + gwip = parseip(ip, field[3]); + break; + } + } + poperror(); + rcvprocp = nil; + + recv = 1; + wakeup(&bootpr); + pexit("", 0); +} + +static char* +rbootp(Ipifc *ifc) +{ + int cfd, dfd, tries, n; + char ia[5+3*24], im[16], *av[3]; + uchar nipaddr[4], ngwip[4], nipmask[4]; + char dir[Maxpath]; + + av[1] = "0.0.0.0"; + av[2] = "0.0.0.0"; + ipifcadd(ifc, av, 3, 0, nil); + + cfd = kannounce("udp!*!68", dir); + if(cfd < 0) + return "bootp announce failed"; + strcat(dir, "/data"); + if(kwrite(cfd, "headers", 7) < 0){ + kclose(cfd); + return "bootp ctl headers failed"; + } + kwrite(cfd, "oldheaders", 10); + dfd = kopen(dir, ORDWR); + if(dfd < 0){ + kclose(cfd); + return "bootp open data failed"; + } + kclose(cfd); + + + /* create request */ + memset(&req, 0, sizeof(req)); + ipmove(req.raddr, IPv4bcast); + hnputs(req.rport, 67); + req.op = Bootrequest; + req.htype = 1; /* ethernet (all we know) */ + req.hlen = 6; /* ethernet (all we know) */ + + /* Hardware MAC address */ + memmove(req.chaddr, ifc->mac, 6); + /* Fill in the local IP address if we know it */ + ipv4local(ifc, req.ciaddr); + memset(req.file, 0, sizeof(req.file)); + strcpy((char*)req.vend, "p9 "); + + done = 0; + recv = 0; + + kproc("rcvbootp", rcvbootp, (void*)dfd, KPDUPFDG); + + /* + * broadcast bootp's till we get a reply, + * or fixed number of tries + */ + tries = 0; + while(recv == 0) { + if(kwrite(dfd, &req, sizeof(req)) < 0) + print("bootp: write: %s\n", commonerror()); + + tsleep(&bootpr, return0, 0, 1000); + if(++tries > 10) { + print("bootp: timed out\n"); + break; + } + } + kclose(dfd); + done = 1; + if(rcvprocp != nil){ + postnote(rcvprocp, 1, "timeout", 0); + rcvprocp = nil; + } + + av[1] = "0.0.0.0"; + av[2] = "0.0.0.0"; + ipifcrem(ifc, av, 3); + + hnputl(nipaddr, ipaddr); + sprint(ia, "%V", nipaddr); + hnputl(nipmask, ipmask); + sprint(im, "%V", nipmask); + av[1] = ia; + av[2] = im; + ipifcadd(ifc, av, 3, 0, nil); + + if(gwip != 0) { + hnputl(ngwip, gwip); + n = snprint(ia, sizeof(ia), "add 0.0.0.0 0.0.0.0 %V", ngwip); + routewrite(ifc->conv->p->f, nil, ia, n); + } + return nil; +} + +static int +rbootpread(char *bp, ulong offset, int len) +{ + int n; + char *buf; + uchar a[4]; + + buf = smalloc(READSTR); + if(waserror()){ + free(buf); + nexterror(); + } + hnputl(a, fsip); + n = snprint(buf, READSTR, "fsip %15V\n", a); + hnputl(a, auip); + n += snprint(buf + n, READSTR-n, "auip %15V\n", a); + hnputl(a, gwip); + n += snprint(buf + n, READSTR-n, "gwip %15V\n", a); + hnputl(a, ipmask); + n += snprint(buf + n, READSTR-n, "ipmask %15V\n", a); + hnputl(a, ipaddr); + snprint(buf + n, READSTR-n, "ipaddr %15V\n", a); + + len = readstr(offset, bp, len, buf); + poperror(); + free(buf); + return len; +} + +char* (*bootp)(Ipifc*) = rbootp; +int (*bootpread)(char*, ulong, int) = rbootpread; |
