diff options
author | rsc <devnull@localhost> | 2005-12-26 04:48:52 +0000 |
---|---|---|
committer | rsc <devnull@localhost> | 2005-12-26 04:48:52 +0000 |
commit | 87a52e0485d3281ebea6bf4b725aa8023690e96f (patch) | |
tree | 0abc2d2ddb875196177231639d3cb4519e814b9d /src/cmd/ip/dhcpd | |
parent | 35d26aa32167e84326cdb745c0e906393b8de71d (diff) | |
download | plan9port-87a52e0485d3281ebea6bf4b725aa8023690e96f.tar.gz plan9port-87a52e0485d3281ebea6bf4b725aa8023690e96f.tar.bz2 plan9port-87a52e0485d3281ebea6bf4b725aa8023690e96f.zip |
new goodies
Diffstat (limited to 'src/cmd/ip/dhcpd')
-rwxr-xr-x | src/cmd/ip/dhcpd/dat.h | 85 | ||||
-rwxr-xr-x | src/cmd/ip/dhcpd/db.c | 452 | ||||
-rwxr-xr-x | src/cmd/ip/dhcpd/dhcpd.c | 1632 | ||||
-rwxr-xr-x | src/cmd/ip/dhcpd/dhcpleases.c | 43 | ||||
-rwxr-xr-x | src/cmd/ip/dhcpd/mkfile | 22 | ||||
-rwxr-xr-x | src/cmd/ip/dhcpd/ndb.c | 318 | ||||
-rwxr-xr-x | src/cmd/ip/dhcpd/ping.c | 112 | ||||
-rwxr-xr-x | src/cmd/ip/dhcpd/testlook.c | 222 | ||||
-rwxr-xr-x | src/cmd/ip/dhcpd/testlookup.c | 168 | ||||
-rwxr-xr-x | src/cmd/ip/dhcpd/testping.c | 22 |
10 files changed, 3076 insertions, 0 deletions
diff --git a/src/cmd/ip/dhcpd/dat.h b/src/cmd/ip/dhcpd/dat.h new file mode 100755 index 00000000..28604c1b --- /dev/null +++ b/src/cmd/ip/dhcpd/dat.h @@ -0,0 +1,85 @@ +#include "../dhcp.h" + +enum +{ + Maxstr= 256, +}; + +typedef struct Binding Binding; +struct Binding +{ + Binding *next; + uchar ip[IPaddrlen]; + + char *boundto; /* id last bound to */ + char *offeredto; /* id we've offered this to */ + + long lease; /* absolute time at which binding expires */ + long expoffer; /* absolute time at which offer times out */ + long offer; /* lease offered */ + long lasttouched; /* time this entry last assigned/unassigned */ + long lastcomplained; /* last time we complained about a used but not leased */ + long tried; /* last time we tried this entry */ + + Qid q; /* qid at the last syncbinding */ +}; + +typedef struct Info Info; +struct Info +{ + int indb; /* true if found in database */ + char domain[Maxstr]; /* system domain name */ + char bootf[Maxstr]; /* boot file */ + char bootf2[Maxstr]; /* alternative boot file */ + uchar tftp[NDB_IPlen]; /* ip addr of tftp server */ + uchar tftp2[NDB_IPlen]; /* ip addr of alternate server */ + uchar ipaddr[NDB_IPlen]; /* ip address of system */ + uchar ipmask[NDB_IPlen]; /* ip network mask */ + uchar ipnet[NDB_IPlen]; /* ip network address (ipaddr & ipmask) */ + uchar etheraddr[6]; /* ethernet address */ + uchar gwip[NDB_IPlen]; /* gateway ip address */ + uchar fsip[NDB_IPlen]; /* file system ip address */ + uchar auip[NDB_IPlen]; /* authentication server ip address */ + char rootpath[Maxstr]; /* rootfs for diskless nfs clients */ + char dhcpgroup[Maxstr]; + char vendor[Maxstr]; /* vendor info */ +}; + + +/* from dhcp.c */ +extern int validip(uchar*); +extern void warning(int, char*, ...); +extern int minlease; + +/* from db.c */ +extern char* tohex(char*, uchar*, int); +extern char* toid(uchar*, int); +extern void initbinding(uchar*, int); +extern Binding* iptobinding(uchar*, int); +extern Binding* idtobinding(char*, Info*, int); +extern Binding* idtooffer(char*, Info*); +extern int commitbinding(Binding*); +extern int releasebinding(Binding*, char*); +extern int samenet(uchar *ip, Info *iip); +extern void mkoffer(Binding*, char*, long); +extern int syncbinding(Binding*, int); + +/* from ndb.c */ +extern int lookup(Bootp*, Info*, Info*); +extern int lookupip(uchar*, Info*, int); +extern void lookupname(char*, Ndbtuple*); +extern Iplifc* findlifc(uchar*); +extern int forme(uchar*); +extern int lookupserver(char*, uchar**, Ndbtuple *t); +extern Ndbtuple* lookupinfo(uchar *ipaddr, char **attr, int n); + +/* from icmp.c */ +extern int icmpecho(uchar*); + +extern char *binddir; +extern int debug; +extern char *blog; +extern Ipifc *ipifcs; +extern long now; +extern char *ndbfile; + diff --git a/src/cmd/ip/dhcpd/db.c b/src/cmd/ip/dhcpd/db.c new file mode 100755 index 00000000..414d85b9 --- /dev/null +++ b/src/cmd/ip/dhcpd/db.c @@ -0,0 +1,452 @@ +#include <u.h> +#include <libc.h> +#include <ip.h> +#include <bio.h> +#include <ndb.h> +#include <ctype.h> +#include "dat.h" + +/* + * format of a binding entry: + * char ipaddr[32]; + * char id[32]; + * char hwa[32]; + * char otime[10]; + */ +Binding *bcache; +uchar bfirst[IPaddrlen]; +char *binddir = nil; +char *xbinddir = "#9/ndb/dhcp"; + +/* + * convert a byte array to hex + */ +static char +hex(int x) +{ + if(x < 10) + return x + '0'; + return x - 10 + 'a'; +} +extern char* +tohex(char *hdr, uchar *p, int len) +{ + char *s, *sp; + int hlen; + + hlen = strlen(hdr); + s = malloc(hlen + 2*len + 1); + sp = s; + strcpy(sp, hdr); + sp += hlen; + for(; len > 0; len--){ + *sp++ = hex(*p>>4); + *sp++ = hex(*p & 0xf); + p++; + } + *sp = 0; + return s; +} + +/* + * convert a client id to a string. If it's already + * ascii, leave it be. Otherwise, convert it to hex. + */ +extern char* +toid(uchar *p, int n) +{ + int i; + char *s; + + for(i = 0; i < n; i++) + if(!isprint(p[i])) + return tohex("id", p, n); + s = malloc(n + 1); + memmove(s, p, n); + s[n] = 0; + return s; +} + +/* + * increment an ip address + */ +static void +incip(uchar *ip) +{ + int i, x; + + for(i = IPaddrlen-1; i >= 0; i--){ + x = ip[i]; + x++; + ip[i] = x; + if((x & 0x100) == 0) + break; + } +} + +/* + * find a binding for an id or hardware address + */ +static int +lockopen(char *file) +{ + char err[ERRMAX]; + int fd, tries; + + for(tries = 0; tries < 5; tries++){ + fd = open(file, OLOCK|ORDWR); + if(fd >= 0) + return fd; +print("open %s: %r\n", file); + errstr(err, sizeof err); + if(strstr(err, "lock")){ + /* wait for other process to let go of lock */ + sleep(250); + + /* try again */ + continue; + } + if(strstr(err, "exist") || strstr(err, "No such")){ + /* no file, create an exclusive access file */ + fd = create(file, ORDWR, DMEXCL|0666); + chmod(file, 0666); + if(fd >= 0) + return fd; + } + } + return -1; +} + +void +setbinding(Binding *b, char *id, long t) +{ + if(b->boundto) + free(b->boundto); + + b->boundto = strdup(id); + b->lease = t; +} + +static void +parsebinding(Binding *b, char *buf) +{ + long t; + char *id, *p; + + /* parse */ + t = atoi(buf); + id = strchr(buf, '\n'); + if(id){ + *id++ = 0; + p = strchr(id, '\n'); + if(p) + *p = 0; + } else + id = ""; + + /* replace any past info */ + setbinding(b, id, t); +} + +static int +writebinding(int fd, Binding *b) +{ + Dir *d; + + seek(fd, 0, 0); + if(fprint(fd, "%ld\n%s\n", b->lease, b->boundto) < 0) + return -1; + d = dirfstat(fd); + if(d == nil) + return -1; + b->q.type = d->qid.type; + b->q.path = d->qid.path; + b->q.vers = d->qid.vers; + free(d); + return 0; +} + +/* + * synchronize cached binding with file. the file always wins. + */ +int +syncbinding(Binding *b, int returnfd) +{ + char buf[512]; + int i, fd; + Dir *d; + + if(binddir == nil) + binddir = unsharp(xbinddir); + + snprint(buf, sizeof(buf), "%s/%I", binddir, b->ip); + fd = lockopen(buf); + if(fd < 0){ + /* assume someone else is using it */ + b->lease = time(0) + OfferTimeout; + return -1; + } + + /* reread if changed */ + d = dirfstat(fd); + if(d != nil) /* BUG? */ + if(d->qid.type != b->q.type || d->qid.path != b->q.path || d->qid.vers != b->q.vers){ + i = read(fd, buf, sizeof(buf)-1); + if(i < 0) + i = 0; + buf[i] = 0; + parsebinding(b, buf); + b->lasttouched = d->mtime; + b->q.path = d->qid.path; + b->q.vers = d->qid.vers; + } + + free(d); + + if(returnfd) + return fd; + + close(fd); + return 0; +} + +extern int +samenet(uchar *ip, Info *iip) +{ + uchar x[IPaddrlen]; + + maskip(iip->ipmask, ip, x); + return ipcmp(x, iip->ipnet) == 0; +} + +/* + * create a record for each binding + */ +extern void +initbinding(uchar *first, int n) +{ + while(n-- > 0){ + iptobinding(first, 1); + incip(first); + } +} + +/* + * find a binding for a specific ip address + */ +extern Binding* +iptobinding(uchar *ip, int mk) +{ + Binding *b; + + for(b = bcache; b; b = b->next){ + if(ipcmp(b->ip, ip) == 0){ + syncbinding(b, 0); + return b; + } + } + + if(mk == 0) + return 0; + b = malloc(sizeof(*b)); + memset(b, 0, sizeof(*b)); + ipmove(b->ip, ip); + b->next = bcache; + bcache = b; + syncbinding(b, 0); + return b; +} + +static void +lognolease(Binding *b) +{ + /* renew the old binding, and hope it eventually goes away */ + b->offer = 5*60; + commitbinding(b); + + /* complain if we haven't in the last 5 minutes */ + if(now - b->lastcomplained < 5*60) + return; + syslog(0, blog, "dhcp: lease for %I to %s ended at %ld but still in use\n", + b->ip, b->boundto != nil ? b->boundto : "?", b->lease); + b->lastcomplained = now; +} + +/* + * find a free binding for a hw addr or id on the same network as iip + */ +extern Binding* +idtobinding(char *id, Info *iip, int ping) +{ + Binding *b, *oldest; + int oldesttime; + + /* + * first look for an old binding that matches. that way + * clients will tend to keep the same ip addresses. + */ + for(b = bcache; b; b = b->next){ + if(b->boundto && strcmp(b->boundto, id) == 0){ + if(!samenet(b->ip, iip)) + continue; + + /* check with the other servers */ + syncbinding(b, 0); + if(strcmp(b->boundto, id) == 0) + return b; + } + } + +print("looking for old for %I\n", iip->ipnet); + + /* + * look for oldest binding that we think is unused + */ + for(;;){ + oldest = nil; + oldesttime = 0; + for(b = bcache; b; b = b->next){ +print("tried %d now %d lease %d exp %d %I\n", b->tried, now, b->lease, b->expoffer, b->ip); + if(b->tried != now) + if(b->lease < now && b->expoffer < now && samenet(b->ip, iip)) + if(oldest == nil || b->lasttouched < oldesttime){ + /* sync and check again */ + syncbinding(b, 0); + if(b->lease < now && b->expoffer < now && samenet(b->ip, iip)) + if(oldest == nil || b->lasttouched < oldesttime){ + oldest = b; +print("have oldest\n"); + oldesttime = b->lasttouched; + } + } + } + if(oldest == nil) + break; + + /* make sure noone is still using it */ + oldest->tried = now; +print("return oldest\n"); + if(ping == 0 || icmpecho(oldest->ip) == 0) + return oldest; + + lognolease(oldest); /* sets lastcomplained */ + } + + /* try all bindings */ + for(b = bcache; b; b = b->next){ + syncbinding(b, 0); + if(b->tried != now) + if(b->lease < now && b->expoffer < now && samenet(b->ip, iip)){ + b->tried = now; + if(ping == 0 || icmpecho(b->ip) == 0) + return b; + + lognolease(b); + } + } + + /* nothing worked, give up */ + return 0; +} + +/* + * create an offer + */ +extern void +mkoffer(Binding *b, char *id, long leasetime) +{ + if(leasetime <= 0){ + if(b->lease > now + minlease) + leasetime = b->lease - now; + else + leasetime = minlease; + } + if(b->offeredto) + free(b->offeredto); + b->offeredto = strdup(id); + b->offer = leasetime; + b->expoffer = now + OfferTimeout; +} + +/* + * find an offer for this id + */ +extern Binding* +idtooffer(char *id, Info *iip) +{ + Binding *b; + + /* look for an offer to this id */ + for(b = bcache; b; b = b->next){ +print("%I %I ? offeredto %s id %s\n", b->ip, iip->ipnet, b->offeredto, id); + if(b->offeredto && strcmp(b->offeredto, id) == 0 && samenet(b->ip, iip)){ + /* make sure some other system hasn't stolen it */ + syncbinding(b, 0); +print("b->lease %d now %d boundto %s offered %s\n", b->lease, now, b->boundto, b->offeredto); + if(b->lease < now + || (b->boundto && strcmp(b->boundto, b->offeredto) == 0)) + return b; + } + } + return 0; +} + +/* + * commit a lease, this could fail + */ +extern int +commitbinding(Binding *b) +{ + int fd; + long now; + + now = time(0); + + if(b->offeredto == 0) + return -1; + fd = syncbinding(b, 1); + if(fd < 0) + return -1; + if(b->lease > now && b->boundto && strcmp(b->boundto, b->offeredto) != 0){ + close(fd); + return -1; + } + setbinding(b, b->offeredto, now + b->offer); + b->lasttouched = now; + + if(writebinding(fd, b) < 0){ + close(fd); + return -1; + } + close(fd); + return 0; +} + +/* + * commit a lease, this could fail + */ +extern int +releasebinding(Binding *b, char *id) +{ + int fd; + long now; + + now = time(0); + + fd = syncbinding(b, 1); + if(fd < 0) + return -1; + if(b->lease > now && b->boundto && strcmp(b->boundto, id) != 0){ + close(fd); + return -1; + } + b->lease = 0; + b->expoffer = 0; + + if(writebinding(fd, b) < 0){ + close(fd); + return -1; + } + close(fd); + return 0; +} diff --git a/src/cmd/ip/dhcpd/dhcpd.c b/src/cmd/ip/dhcpd/dhcpd.c new file mode 100755 index 00000000..3b0873c2 --- /dev/null +++ b/src/cmd/ip/dhcpd/dhcpd.c @@ -0,0 +1,1632 @@ +#include <u.h> +#include <sys/socket.h> +#include <net/if_arp.h> +#include <netinet/ip.h> +#include <sys/ioctl.h> +#include <libc.h> +#include <ip.h> +#include <bio.h> +#include <ndb.h> +#include "dat.h" + +int bwfd; +int wfd; + +// +// ala rfc2131 +// + +typedef struct Req Req; +struct Req +{ + int fd; /* for reply */ + Bootp *bp; + Udphdr uh; + Udphdr *up; + uchar *e; /* end of received message */ + uchar *p; /* options pointer */ + uchar *max; /* max end of reply */ + + /* expanded to v6 */ + uchar ciaddr[IPaddrlen]; + uchar giaddr[IPaddrlen]; + + /* parsed options */ + int p9request; /* true if this is a bootp with plan9 options */ + int genrequest; /* true if this is a bootp with generic options */ + int dhcptype; /* dhcp message type */ + int leasetime; /* dhcp lease */ + uchar ip[IPaddrlen]; /* requested address */ + uchar server[IPaddrlen]; /* server address */ + char msg[ERRMAX]; /* error message */ + char vci[32]; /* vendor class id */ + char *id; /* client id */ + uchar requested[32]; /* requested params */ + uchar vendorclass[32]; + char cputype[32-3]; + + Info gii; /* about target network */ + Info ii; /* about target system */ + int staticbinding; + + uchar buf[2*1024]; /* message buffer */ +}; + +#define TFTP "/lib/tftpd" +char *blog = "ipboot"; +char mysysname[64]; +Ipifc *ipifcs; +int debug; +int nobootp; +long now; +int slow; +char net[256]; +uchar xmyipaddr[IPaddrlen]; + +int pptponly; // only answer request that came from the pptp server +int mute; +int minlease = MinLease; + +ulong start; + +/* option magic */ +char plan9opt[4] = { 'p', '9', ' ', ' ' }; +char genericopt[4] = { 0x63, 0x82, 0x53, 0x63 }; + +/* well known addresses */ +uchar zeros[Maxhwlen]; + +/* option debug buffer */ +char optbuf[1024]; +char *op; +char *oe = optbuf + sizeof(optbuf); + +char *optname[256] = +{ +[OBend] "end", +[OBpad] "pad", +[OBmask] "mask", +[OBtimeoff] "timeoff", +[OBrouter] "router", +[OBtimeserver] "time", +[OBnameserver] "name", +[OBdnserver] "dns", +[OBlogserver] "log", +[OBcookieserver] "cookie", +[OBlprserver] "lpr", +[OBimpressserver] "impress", +[OBrlserver] "rl", +[OBhostname] "host", +[OBbflen] "bflen", +[OBdumpfile] "dumpfile", +[OBdomainname] "dom", +[OBswapserver] "swap", +[OBrootpath] "rootpath", +[OBextpath] "extpath", +[OBipforward] "ipforward", +[OBnonlocal] "nonlocal", +[OBpolicyfilter] "policyfilter", +[OBmaxdatagram] "maxdatagram", +[OBttl] "ttl", +[OBpathtimeout] "pathtimeout", +[OBpathplateau] "pathplateau", +[OBmtu] "mtu", +[OBsubnetslocal] "subnetslocal", +[OBbaddr] "baddr", +[OBdiscovermask] "discovermask", +[OBsupplymask] "supplymask", +[OBdiscoverrouter] "discoverrouter", +[OBrsserver] "rsserver", +[OBstaticroutes] "staticroutes", +[OBtrailerencap] "trailerencap", +[OBarptimeout] "arptimeout", +[OBetherencap] "etherencap", +[OBtcpttl] "tcpttl", +[OBtcpka] "tcpka", +[OBtcpkag] "tcpkag", +[OBnisdomain] "nisdomain", +[OBniserver] "niserver", +[OBntpserver] "ntpserver", +[OBvendorinfo] "vendorinfo", +[OBnetbiosns] "NBns", +[OBnetbiosdds] "NBdds", +[OBnetbiostype] "NBtype", +[OBnetbiosscope] "NBscope", +[OBxfontserver] "xfont", +[OBxdispmanager] "xdisp", +[OBnisplusdomain] "NPdomain", +[OBnisplusserver] "NP", +[OBhomeagent] "homeagent", +[OBsmtpserver] "smtp", +[OBpop3server] "pop3", +[OBnntpserver] "nntp", +[OBwwwserver] "www", +[OBfingerserver] "finger", +[OBircserver] "ircserver", +[OBstserver] "stserver", +[OBstdaserver] "stdaserver", + +/* dhcp options */ +[ODipaddr] "ip", +[ODlease] "leas", +[ODoverload] "overload", +[ODtype] "typ", +[ODserverid] "sid", +[ODparams] "params", +[ODmessage] "message", +[ODmaxmsg] "maxmsg", +[ODrenewaltime] "renewaltime", +[ODrebindingtime] "rebindingtime", +[ODvendorclass] "vendorclass", +[ODclientid] "cid", +[ODtftpserver] "tftpserver", +[ODbootfile] "bf", +}; + +void addropt(Req*, int, uchar*); +void addrsopt(Req*, int, uchar**, int); +void arpenter(uchar*, uchar*); +void bootp(Req*); +void byteopt(Req*, int, uchar); +void dhcp(Req*); +void fatal(int, char*, ...); +void hexopt(Req*, int, char*); +void longopt(Req*, int, long); +void maskopt(Req*, int, uchar*); +void miscoptions(Req*, uchar*); +int openlisten(char *net); +void parseoptions(Req*); +void proto(Req*, int); +void rcvdecline(Req*); +void rcvdiscover(Req*); +void rcvinform(Req*); +void rcvrelease(Req*); +void rcvrequest(Req*); +char* readsysname(void); +void remrequested(Req*, int); +void sendack(Req*, uchar*, int, int); +void sendnak(Req*, char*); +void sendoffer(Req*, uchar*, int); +void stringopt(Req*, int, char*); +void termopt(Req*); +int validip(uchar*); +void vectoropt(Req*, int, uchar*, int); +void warning(int, char*, ...); +void logdhcp(Req*); +void logdhcpout(Req *, char *); +int readlast(int, Udphdr*, uchar*, int); + +void +timestamp(char *tag) +{ + ulong t; + + t = nsec()/1000; + syslog(0, blog, "%s %lud", tag, t - start); +} + +void +usage(void) +{ + fprint(2, "usage: dhcp [-dmsnp] [-f directory] [-x netmtpt] [-M minlease] addr n [addr n ...]\n"); + exits("usage"); +} + +void +main(int argc, char **argv) +{ + int i, n, fd; + char *p; + uchar ip[IPaddrlen]; + Req r; + + fmtinstall('E', eipfmt); + fmtinstall('I', eipfmt); + fmtinstall('V', eipfmt); + fmtinstall('M', eipfmt); + ARGBEGIN { + case 'm': + mute = 1; + break; + case 'd': + debug = 1; + break; + case 'f': + p = ARGF(); + if(p == nil) + usage(); + ndbfile = p; + break; + case 's': + slow = 1; + break; + case 'n': + nobootp = 1; + break; + case 'i': + parseip(xmyipaddr,EARGF(usage())); + break; + case 'p': + pptponly = 1; + break; + case 'M': + p = ARGF(); + if(p == nil) + usage(); + minlease = atoi(p); + if(minlease <= 0) + minlease = MinLease; + break; + } ARGEND; + + while(argc > 1){ + parseip(ip, argv[0]); + if(!validip(ip)) + usage(); + n = atoi(argv[1]); + if(n <= 0) + usage(); + initbinding(ip, n); + argc -= 2; + argv += 2; + } + + /* for debugging */ + for(i = 0; i < 256; i++) + if(optname[i] == 0) + optname[i] = smprint("%d", i); + + /* what is my name? */ + p = readsysname(); + strcpy(mysysname, p); + + /* put process in background */ + if(!debug) switch(rfork(RFNOTEG|RFPROC|RFFDG)) { + case -1: + fatal(1, "fork"); + case 0: + break; + default: + exits(0); + } + + chdir(TFTP); + fd = openlisten(net); + wfd = fd; + bwfd = fd; + if(setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, "eth0", 5) < 0) + print("setsockopt: %r\n"); + + for(;;){ + memset(&r, 0, sizeof(r)); + r.fd = fd; + n = readlast(fd, &r.uh, r.buf, sizeof(r.buf)); + if(n < 0) + fatal(1, "error reading requests"); + start = nsec()/1000; + op = optbuf; + *op = 0; + proto(&r, n); + if(r.id != nil) + free(r.id); + } +} + +void +proto(Req *rp, int n) +{ + uchar relip[IPaddrlen]; + char buf[64]; + + now = time(0); + + rp->e = rp->buf + n; + rp->bp = (Bootp*)rp->buf; + rp->up = &rp->uh; + rp->max = rp->buf + MINSUPPORTED - IPUDPHDRSIZE; + rp->p = rp->bp->optdata; + v4tov6(rp->giaddr, rp->bp->giaddr); + v4tov6(rp->ciaddr, rp->bp->ciaddr); + + if(pptponly && rp->bp->htype != 0) + return; + + ipifcs = readipifc(net, ipifcs, -1); + if(validip(rp->giaddr)) + ipmove(relip, rp->giaddr); + else if(validip(rp->up->raddr)) + ipmove(relip, rp->up->raddr); + else + ipmove(relip, xmyipaddr); + ipmove(rp->up->laddr, xmyipaddr); + if(rp->e < (uchar*)rp->bp->sname){ + warning(0, "packet too short"); + return; + } + if(rp->bp->op != Bootrequest){ + warning(0, "not bootrequest"); + return; + } + + if(rp->e >= rp->bp->optdata){ + if(memcmp(rp->bp->optmagic, plan9opt, sizeof(rp->bp->optmagic)) == 0) + rp->p9request = 1; + if(memcmp(rp->bp->optmagic, genericopt, sizeof(rp->bp->optmagic)) == 0) { + rp->genrequest = 1; + parseoptions(rp); + } + } + rp->p = rp->bp->optdata; + + /* If no id is specified, make one from the hardware address + * of the target. We assume all zeros is not a hardware address + * which could be a mistake. + */ + if(rp->id == nil){ + if(rp->bp->hlen > Maxhwlen){ + warning(0, "hlen %d", rp->bp->hlen); + return; + } + if(memcmp(zeros, rp->bp->chaddr, rp->bp->hlen) == 0){ + warning(0, "no chaddr"); + return; + } + sprint(buf, "hwa%2.2ux_", rp->bp->htype); + rp->id = tohex(buf, rp->bp->chaddr, rp->bp->hlen); + } + + /* info about gateway */ + if(lookupip(relip, &rp->gii, 1) < 0){ + warning(0, "lookupip failed"); + return; + } + + /* info about target system */ + if(lookup(rp->bp, &rp->ii, &rp->gii) == 0) + if(rp->ii.indb && rp->ii.dhcpgroup[0] == 0) + rp->staticbinding = 1; + + if(rp->dhcptype) + dhcp(rp); + else + bootp(rp); +timestamp("done"); +} + +void +dhcp(Req *rp) +{ + logdhcp(rp); + + switch(rp->dhcptype){ + case Discover: + if(slow) + sleep(500); + rcvdiscover(rp); + break; + case Request: + rcvrequest(rp); + break; + case Decline: + rcvdecline(rp); + break; + case Release: + rcvrelease(rp); + break; + case Inform: + rcvinform(rp); + break; + } +} + +void +rcvdiscover(Req *rp) +{ + Binding *b, *nb; + + if(rp->staticbinding){ + sendoffer(rp, rp->ii.ipaddr, StaticLease); + return; + } + + /* + * first look for an outstanding offer + */ + b = idtooffer(rp->id, &rp->gii); + + /* + * rfc2131 says: + * If an address is available, the new address + * SHOULD be chosen as follows: + * + * o The client's current address as recorded in the client's current + * binding, ELSE + * + * o The client's previous address as recorded in the client's (now + * expired or released) binding, if that address is in the server's + * pool of available addresses and not already allocated, ELSE + * + * o The address requested in the 'Requested IP Address' option, if that + * address is valid and not already allocated, ELSE + * + * o A new address allocated from the server's pool of available + * addresses; the address is selected based on the subnet from which + * the message was received (if 'giaddr' is 0) or on the address of + * the relay agent that forwarded the message ('giaddr' when not 0). + */ + if(b == nil){ + b = idtobinding(rp->id, &rp->gii, 1); + if(b && b->boundto && strcmp(b->boundto, rp->id) != 0) + if(validip(rp->ip) && samenet(rp->ip, &rp->gii)){ + nb = iptobinding(rp->ip, 0); + if(nb && nb->lease < now) + b = nb; + } + } + if(b == nil){ + warning(0, "!Discover(%s via %I): no binding %I", + rp->id, rp->gii.ipaddr, rp->ip); + return; + } + mkoffer(b, rp->id, rp->leasetime); + sendoffer(rp, b->ip, b->offer); +} + +void +rcvrequest(Req *rp) +{ + Binding *b; + + if(validip(rp->server)){ + /* this is a reply to an offer - SELECTING */ + + /* check for hard assignment */ + if(rp->staticbinding){ + if(forme(rp->server)) + sendack(rp, rp->ii.ipaddr, StaticLease, 1); + else + warning(0, "!Request(%s via %I): for server %I not me", + rp->id, rp->gii.ipaddr, rp->server); + return; + } + + b = idtooffer(rp->id, &rp->gii); + + /* if we don't have an offer, nak */ + if(b == nil){ + warning(0, "!Request(%s via %I): no offer", + rp->id, rp->gii.ipaddr); + if(forme(rp->server)) + sendnak(rp, "no offer for you"); + return; + } + + /* if not for me, retract offer */ + if(!forme(rp->server)){ + b->expoffer = 0; + warning(0, "!Request(%s via %I): for server %I not me", + rp->id, rp->gii.ipaddr, rp->server); + return; + } + + /* + * if the client is confused about what we offered, nak. + * client really shouldn't be specifying this when selecting + */ + if(validip(rp->ip) && ipcmp(rp->ip, b->ip) != 0){ + warning(0, "!Request(%s via %I): requests %I, not %I", + rp->id, rp->gii.ipaddr, rp->ip, b->ip); + sendnak(rp, "bad ip address option"); + return; + } + if(commitbinding(b) < 0){ + warning(0, "!Request(%s via %I): can't commit %I", + rp->id, rp->gii.ipaddr, b->ip); + sendnak(rp, "can't commit binding"); + return; + } + sendack(rp, b->ip, b->offer, 1); + } else if(validip(rp->ip)){ + /* + * checking address/net - INIT-REBOOT + * + * This is a rebooting client that remembers its old + * address. + */ + /* check for hard assignment */ + if(rp->staticbinding){ + if(memcmp(rp->ip, rp->ii.ipaddr, IPaddrlen) != 0){ + warning(0, "!Request(%s via %I): %I not valid for %E", + rp->id, rp->gii.ipaddr, rp->ip, rp->bp->chaddr); + sendnak(rp, "not valid"); + } + sendack(rp, rp->ii.ipaddr, StaticLease, 1); + return; + } + + /* make sure the network makes sense */ + if(!samenet(rp->ip, &rp->gii)){ + warning(0, "!Request(%s via %I): bad forward of %I", + rp->id, rp->gii.ipaddr, rp->ip); + sendnak(rp, "wrong network"); + return; + } + b = iptobinding(rp->ip, 0); + if(b == nil){ + warning(0, "!Request(%s via %I): no binding for %I for", + rp->id, rp->gii.ipaddr, rp->ip); + return; + } + if(memcmp(rp->ip, b->ip, IPaddrlen) != 0 || now > b->lease){ + warning(0, "!Request(%s via %I): %I not valid", + rp->id, rp->gii.ipaddr, rp->ip); + sendnak(rp, "not valid"); + return; + } + b->offer = b->lease - now; + sendack(rp, b->ip, b->offer, 1); + } else if(validip(rp->ciaddr)){ + /* + * checking address - RENEWING or REBINDING + * + * these states are indistinguishable in our action. The only + * difference is how close to lease expiration the client is. + * If it is really close, it broadcasts the request hoping that + * some server will answer. + */ + + /* check for hard assignment */ + if(rp->staticbinding){ + if(ipcmp(rp->ciaddr, rp->ii.ipaddr) != 0){ + warning(0, "!Request(%s via %I): %I not valid", + rp->id, rp->gii.ipaddr, rp->ciaddr); + sendnak(rp, "not valid"); + } + sendack(rp, rp->ii.ipaddr, StaticLease, 1); + return; + } + + /* make sure the network makes sense */ + if(!samenet(rp->ciaddr, &rp->gii)){ + warning(0, "!Request(%s via %I): bad forward of %I", + rp->id, rp->gii.ipaddr, rp->ip); + sendnak(rp, "wrong network"); + return; + } + b = iptobinding(rp->ciaddr, 0); + if(b == nil){ + warning(0, "!Request(%s via %I): no binding for %I", + rp->id, rp->gii.ipaddr, rp->ciaddr); + return; + } + if(ipcmp(rp->ciaddr, b->ip) != 0){ + warning(0, "!Request(%I via %s): %I not valid", + rp->id, rp->gii.ipaddr, rp->ciaddr); + sendnak(rp, "invalid ip address"); + return; + } + mkoffer(b, rp->id, rp->leasetime); + if(commitbinding(b) < 0){ + warning(0, "!Request(%s via %I): can't commit %I", + rp->id, rp->gii.ipaddr, b->ip); + sendnak(rp, "can't commit binding"); + return; + } + sendack(rp, b->ip, b->offer, 1); + } +} + +void +rcvdecline(Req *rp) +{ + Binding *b; + char buf[64]; + + if(rp->staticbinding) + return; + + b = idtooffer(rp->id, &rp->gii); + if(b == nil){ + warning(0, "!Decline(%s via %I): no binding", + rp->id, rp->gii.ipaddr); + return; + } + + /* mark ip address as in use */ + snprint(buf, sizeof(buf), "declined by %s", rp->id); + mkoffer(b, buf, 0x7fffffff); + commitbinding(b); +} + +void +rcvrelease(Req *rp) +{ + Binding *b; + + if(rp->staticbinding) + return; + + b = idtobinding(rp->id, &rp->gii, 0); + if(b == nil){ + warning(0, "!Release(%s via %I): no binding", + rp->id, rp->gii.ipaddr); + return; + } + if(strcmp(rp->id, b->boundto) != 0){ + warning(0, "!Release(%s via %I): invalid release of %I", + rp->id, rp->gii.ipaddr, rp->ip); + return; + } + warning(0, "Release(%s via %I): releasing %I", b->boundto, rp->gii.ipaddr, b->ip); + if(releasebinding(b, rp->id) < 0) + warning(0, "release: couldn't release"); +} + +void +rcvinform(Req *rp) +{ + Binding *b; + + if(rp->staticbinding){ + sendack(rp, rp->ii.ipaddr, 0, 0); + return; + } + + b = iptobinding(rp->ciaddr, 0); + if(b == nil){ + warning(0, "!Inform(%s via %I): no binding for %I", + rp->id, rp->gii.ipaddr, rp->ip); + return; + } + sendack(rp, b->ip, 0, 0); +} + +int +setsiaddr(uchar *siaddr, uchar *saddr, uchar *laddr) +{ + if(ipcmp(saddr, IPnoaddr) != 0){ + v6tov4(siaddr, saddr); + return 0; + } else { + v6tov4(siaddr, laddr); + return 1; + } +} + +void +sendoffer(Req *rp, uchar *ip, int offer) +{ + int n; + int fd; + ushort flags; + Bootp *bp; + Udphdr *up; + + bp = rp->bp; + up = rp->up; + + /* + * set destination + */ + flags = nhgets(bp->flags); + fd = wfd; + if(validip(rp->giaddr)){ + ipmove(up->raddr, rp->giaddr); + hnputs(up->rport, 67); + } else if(flags & Fbroadcast){ + fd = bwfd; + ipmove(up->raddr, IPv4bcast); + hnputs(up->rport, 68); + } else { + ipmove(up->raddr, ip); + if(bp->htype == 1) + arpenter(up->raddr, bp->chaddr); + hnputs(up->rport, 68); + } + + /* + * fill in standard bootp part + */ + bp->op = Bootreply; + bp->hops = 0; + hnputs(bp->secs, 0); + memset(bp->ciaddr, 0, sizeof(bp->ciaddr)); + v6tov4(bp->giaddr, rp->giaddr); + v6tov4(bp->yiaddr, ip); + setsiaddr(bp->siaddr, rp->ii.tftp, up->laddr); + strncpy(bp->sname, mysysname, sizeof(bp->sname)); + strncpy(bp->file, rp->ii.bootf, sizeof(bp->file)); + + /* + * set options + */ + byteopt(rp, ODtype, Offer); + longopt(rp, ODlease, offer); + addropt(rp, ODserverid, up->laddr); + miscoptions(rp, ip); + termopt(rp); + + logdhcpout(rp, "Offer"); + + /* + * send + */ + n = rp->p - rp->buf; +print("OFFER: %I %I %d %d\n", rp->up->laddr, rp->up->raddr, nhgets(rp->up->lport), nhgets(rp->up->rport)); + if(!mute && udpwrite(fd, rp->up, rp->buf, n) != n) + warning(0, "offer: write failed: %r"); +} + +void +sendack(Req *rp, uchar *ip, int offer, int sendlease) +{ + int n, fd; + ushort flags; + Bootp *bp; + Udphdr *up; + + bp = rp->bp; + up = rp->up; + + /* + * set destination + */ + fd = wfd; + flags = nhgets(bp->flags); + if(validip(rp->giaddr)){ + ipmove(up->raddr, rp->giaddr); + hnputs(up->rport, 67); + } else if(flags & Fbroadcast){ + fd = bwfd; + ipmove(up->raddr, IPv4bcast); + hnputs(up->rport, 68); + } else { + ipmove(up->raddr, ip); + if(bp->htype == 1) + arpenter(up->raddr, bp->chaddr); + hnputs(up->rport, 68); + } + + /* + * fill in standard bootp part + */ + bp->op = Bootreply; + bp->hops = 0; + hnputs(bp->secs, 0); + v6tov4(bp->giaddr, rp->giaddr); + v6tov4(bp->yiaddr, ip); + setsiaddr(bp->siaddr, rp->ii.tftp, up->laddr); + strncpy(bp->sname, mysysname, sizeof(bp->sname)); + strncpy(bp->file, rp->ii.bootf, sizeof(bp->file)); + + /* + * set options + */ + byteopt(rp, ODtype, Ack); + if(sendlease){ + longopt(rp, ODlease, offer); + } + addropt(rp, ODserverid, up->laddr); + miscoptions(rp, ip); + termopt(rp); + + logdhcpout(rp, "Ack"); + + /* + * send + */ + n = rp->p - rp->buf; + if(!mute && udpwrite(fd, rp->up, rp->buf, n) != n) + warning(0, "ack: write failed: %r"); +} + +void +sendnak(Req *rp, char *msg) +{ + int n, fd; + Bootp *bp; + Udphdr *up; + + bp = rp->bp; + up = rp->up; + + /* + * set destination (always broadcast) + */ + fd = wfd; + if(validip(rp->giaddr)){ + ipmove(up->raddr, rp->giaddr); + hnputs(up->rport, 67); + } else { + fd = bwfd; + ipmove(up->raddr, IPv4bcast); + hnputs(up->rport, 68); + } + + /* + * fill in standard bootp part + */ + bp->op = Bootreply; + bp->hops = 0; + hnputs(bp->secs, 0); + v6tov4(bp->giaddr, rp->giaddr); + memset(bp->ciaddr, 0, sizeof(bp->ciaddr)); + memset(bp->yiaddr, 0, sizeof(bp->yiaddr)); + memset(bp->siaddr, 0, sizeof(bp->siaddr)); + + /* + * set options + */ + byteopt(rp, ODtype, Nak); + addropt(rp, ODserverid, up->laddr); + if(msg) + stringopt(rp, ODmessage, msg); + if(strncmp(rp->id, "id", 2) == 0) + hexopt(rp, ODclientid, rp->id+2); + termopt(rp); + + logdhcpout(rp, "Nak"); + + /* + * send nak + */ + n = rp->p - rp->buf; + if(!mute && udpwrite(fd, rp->up, rp->buf, n) != n) + warning(0, "nak: write failed: %r"); +} + +void +bootp(Req *rp) +{ + int n, fd; + Bootp *bp; + Udphdr *up; + ushort flags; + Iplifc *lifc; + Info *iip; + + warning(0, "bootp %s %I->%I from %s via %I, file %s", + rp->genrequest ? "generic" : (rp->p9request ? "p9" : ""), + rp->up->raddr, rp->up->laddr, + rp->id, rp->gii.ipaddr, + rp->bp->file); + + if(nobootp) + return; + + bp = rp->bp; + up = rp->up; + iip = &rp->ii; + + if(rp->staticbinding == 0){ + warning(0, "bootp from unknown %s via %I", rp->id, rp->gii.ipaddr); + return; + } + + /* ignore if not for us */ + if(*bp->sname){ + if(strcmp(bp->sname, mysysname) != 0){ + bp->sname[20] = 0; + warning(0, "bootp for server %s", bp->sname); + return; + } + } else if(slow) + sleep(500); + + /* ignore if we don't know what file to load */ + if(*bp->file == 0){ + if(rp->genrequest && *iip->bootf2) /* if not plan 9 and we have an alternate file... */ + strncpy(bp->file, iip->bootf2, sizeof(bp->file)); + else if(*iip->bootf) + strncpy(bp->file, iip->bootf, sizeof(bp->file)); + else if(*bp->sname) /* if we were asked, respond no matter what */ + bp->file[0] = '\0'; + else { + warning(0, "no bootfile for %I", iip->ipaddr); + return; + } + } + + /* ignore if the file is unreadable */ + if((!rp->genrequest) && bp->file[0] && access(bp->file, 4) < 0){ + warning(0, "inaccessible bootfile1 %s", bp->file); + return; + } + + bp->op = Bootreply; + v6tov4(bp->yiaddr, iip->ipaddr); + if(rp->p9request){ + warning(0, "p9bootp: %I", iip->ipaddr); + memmove(bp->optmagic, plan9opt, 4); + if(iip->gwip == 0) + v4tov6(iip->gwip, bp->giaddr); + rp->p += sprint((char*)rp->p, "%V %I %I %I", iip->ipmask+IPv4off, iip->fsip, + iip->auip, iip->gwip); + sprint(optbuf, "%s", (char*)(bp->optmagic)); + } else if(rp->genrequest){ + warning(0, "genericbootp: %I", iip->ipaddr); + memmove(bp->optmagic, genericopt, 4); + miscoptions(rp, iip->ipaddr); + termopt(rp); + } else if(iip->vendor[0] != 0) { + warning(0, "bootp vendor field: %s", iip->vendor); + memset(rp->p, 0, 128-4); + rp->p += sprint((char*)bp->optmagic, "%s", iip->vendor); + } else { + memset(rp->p, 0, 128-4); + rp->p += 128-4; + } + + /* + * set destination + */ + fd = wfd; + flags = nhgets(bp->flags); + if(validip(rp->giaddr)){ + ipmove(up->raddr, rp->giaddr); + hnputs(up->rport, 67); + } else if(flags & Fbroadcast){ + fd = bwfd; + ipmove(up->raddr, IPv4bcast); + hnputs(up->rport, 68); + } else { + v4tov6(up->raddr, bp->yiaddr); + if(bp->htype == 1) + arpenter(up->raddr, bp->chaddr); + hnputs(up->rport, 68); + } + + /* + * select best local address if destination is directly connected + */ + lifc = findlifc(up->raddr); + if(lifc) + ipmove(up->laddr, lifc->ip); + + /* + * our identity + */ + strncpy(bp->sname, mysysname, sizeof(bp->sname)); + + /* + * set tftp server + */ + setsiaddr(bp->siaddr, iip->tftp, up->laddr); + if(rp->genrequest && *iip->bootf2) + setsiaddr(bp->siaddr, iip->tftp2, up->laddr); + + /* + * RFC 1048 says that we must pad vendor field with + * zeros until we have a 64 byte field. + */ + n = rp->p - rp->bp->optdata; + if(n < 64-4) { + memset(rp->p, 0, (64-4)-n); + rp->p += (64-4)-n; + } + + /* + * send + */ + n = rp->p - rp->buf; + if(!mute && udpwrite(fd, rp->up, rp->buf, n) != n) + warning(0, "bootp: write failed: %r"); + + warning(0, "bootp via %I: file %s xid(%ux)flag(%ux)ci(%V)gi(%V)yi(%V)si(%V) %s", + up->raddr, bp->file, nhgetl(bp->xid), nhgets(bp->flags), + bp->ciaddr, bp->giaddr, bp->yiaddr, bp->siaddr, + optbuf); +} + +void +parseoptions(Req *rp) +{ + int n, c, code; + uchar *o, *p; + + p = rp->p; + + while(p < rp->e){ + code = *p++; + if(code == 255) + break; + if(code == 0) + continue; + + /* ignore anything that's too long */ + n = *p++; + o = p; + p += n; + if(p > rp->e) + return; + + switch(code){ + case ODipaddr: /* requested ip address */ + if(n == IPv4addrlen) + v4tov6(rp->ip, o); + break; + case ODlease: /* requested lease time */ + rp->leasetime = nhgetl(o); + if(rp->leasetime > MaxLease || rp->leasetime < 0) + rp->leasetime = MaxLease; + break; + case ODtype: + c = *o; + if(c < 10 && c > 0) + rp->dhcptype = c; + break; + case ODserverid: + if(n == IPv4addrlen) + v4tov6(rp->server, o); + break; + case ODmessage: + if(n > sizeof rp->msg-1) + n = sizeof rp->msg-1; + memmove(rp->msg, o, n); + rp->msg[n] = 0; + break; + case ODmaxmsg: + c = nhgets(o); + c -= 28; + if(c > 0) + rp->max = rp->buf + c; + break; + case ODclientid: + if(n <= 1) + break; + rp->id = toid( o, n); + break; + case ODparams: + if(n > sizeof(rp->requested)) + n = sizeof(rp->requested); + memmove(rp->requested, o, n); + break; + case ODvendorclass: + if(n >= sizeof(rp->vendorclass)) + n = sizeof(rp->vendorclass)-1; + memmove(rp->vendorclass, o, n); + rp->vendorclass[n] = 0; + if(strncmp((char*)rp->vendorclass, "p9-", 3) == 0) + strcpy(rp->cputype, (char*)rp->vendorclass+3); + break; + case OBend: + return; + } + } +} + +void +remrequested(Req *rp, int opt) +{ + uchar *p; + + p = memchr(rp->requested, opt, sizeof(rp->requested)); + if(p != nil) + *p = OBpad; +} + +void +miscoptions(Req *rp, uchar *ip) +{ + char *p; + int i, j; + uchar *addrs[2]; + uchar x[2*IPaddrlen]; + uchar vopts[64]; + uchar *op, *omax; + char *attr[100], **a; + int na; + Ndbtuple *t; + + addrs[0] = x; + addrs[1] = x+IPaddrlen; + + /* always supply these */ + maskopt(rp, OBmask, rp->gii.ipmask); + if(validip(rp->gii.gwip)){ + remrequested(rp, OBrouter); + addropt(rp, OBrouter, rp->gii.gwip); + } else if(validip(rp->giaddr)){ + remrequested(rp, OBrouter); + addropt(rp, OBrouter, rp->giaddr); + } + + // OBhostname for the HP4000M switches + // (this causes NT to log infinite errors - tough shit ) + if(*rp->ii.domain){ + remrequested(rp, OBhostname); + stringopt(rp, OBhostname, rp->ii.domain); + } + if(*rp->ii.rootpath) + stringopt(rp, OBrootpath, rp->ii.rootpath); + + /* figure out what we need to lookup */ + na = 0; + a = attr; + if(*rp->ii.domain == 0) + a[na++] = "dom"; + for(i = 0; i < sizeof(rp->requested); i++) + switch(rp->requested[i]){ + case OBrouter: + a[na++] = "@ipgw"; + break; + case OBdnserver: + a[na++] = "@dns"; + break; + case OBnetbiosns: + a[na++] = "@wins"; + break; + case OBsmtpserver: + a[na++] = "@smtp"; + break; + case OBpop3server: + a[na++] = "@pop3"; + break; + case OBwwwserver: + a[na++] = "@www"; + break; + case OBntpserver: + a[na++] = "@ntp"; + break; + case OBtimeserver: + a[na++] = "@time"; + break; + } + if(strncmp((char*)rp->vendorclass, "plan9_", 6) == 0 + || strncmp((char*)rp->vendorclass, "p9-", 3) == 0){ + a[na++] = "@fs"; + a[na++] = "@auth"; + } + t = lookupinfo(ip, a, na); + + /* lookup anything we might be missing */ + if(*rp->ii.domain == 0) + lookupname(rp->ii.domain, t); + + /* add any requested ones that we know about */ + for(i = 0; i < sizeof(rp->requested); i++) + switch(rp->requested[i]){ + case OBrouter: + j = lookupserver("ipgw", addrs, t); + addrsopt(rp, OBrouter, addrs, j); + break; + case OBdnserver: + j = lookupserver("dns", addrs, t); + addrsopt(rp, OBdnserver, addrs, j); + break; + case OBhostname: + if(*rp->ii.domain) + stringopt(rp, OBhostname, rp->ii.domain); + break; + case OBdomainname: + p = strchr(rp->ii.domain, '.'); + if(p) + stringopt(rp, OBdomainname, p+1); + break; + case OBnetbiosns: + j = lookupserver("wins", addrs, t); + addrsopt(rp, OBnetbiosns, addrs, j); + break; + case OBnetbiostype: + /* p-node: peer to peer WINS queries */ + byteopt(rp, OBnetbiostype, 0x2); + break; + case OBsmtpserver: + j = lookupserver("smtp", addrs, t); + addrsopt(rp, OBsmtpserver, addrs, j); + break; + case OBpop3server: + j = lookupserver("pop3", addrs, t); + addrsopt(rp, OBpop3server, addrs, j); + break; + case OBwwwserver: + j = lookupserver("www", addrs, t); + addrsopt(rp, OBwwwserver, addrs, j); + break; + case OBntpserver: + j = lookupserver("ntp", addrs, t); + addrsopt(rp, OBntpserver, addrs, j); + break; + case OBtimeserver: + j = lookupserver("time", addrs, t); + addrsopt(rp, OBtimeserver, addrs, j); + break; + case OBttl: + byteopt(rp, OBttl, 255); + break; + } + + // add plan9 specific options + if(strncmp((char*)rp->vendorclass, "plan9_", 6) == 0 + || strncmp((char*)rp->vendorclass, "p9-", 3) == 0){ + // point to temporary area + op = rp->p; + omax = rp->max; + rp->p = vopts; + rp->max = vopts + sizeof(vopts) - 1; + + j = lookupserver("fs", addrs, t); + addrsopt(rp, OP9fs, addrs, j); + j = lookupserver("auth", addrs, t); + addrsopt(rp, OP9auth, addrs, j); + + // point back + j = rp->p - vopts; + rp->p = op; + rp->max = omax; + vectoropt(rp, OBvendorinfo, vopts, j); + } + + ndbfree(t); +} + +int +openlisten(char *net) +{ + int fd; + char data[128]; + char devdir[40]; + int yes; + + sprint(data, "udp!*!bootps"); + fd = announce(data, devdir); + if(fd < 0) + fatal(1, "can't announce"); + yes = 1; + if(setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &yes, sizeof yes) < 0) + fatal(1, "can't broadcast"); + return fd; +} + +void +fatal(int syserr, char *fmt, ...) +{ + char buf[ERRMAX]; + va_list arg; + + va_start(arg, fmt); + vseprint(buf, buf+sizeof(buf), fmt, arg); + va_end(arg); + if(syserr) + syslog(1, blog, "%s: %r", buf); + else + syslog(1, blog, "%s", buf); + exits(buf); +} + +extern void +warning(int syserr, char *fmt, ...) +{ + char buf[256]; + va_list arg; + + va_start(arg, fmt); + vseprint(buf, buf+sizeof(buf), fmt, arg); + va_end(arg); + if(syserr){ + syslog(0, blog, "%s: %r", buf); + if(debug) + fprint(2, "%s: %r\n", buf); + } else { + syslog(0, blog, "%s", buf); + if(debug) + fprint(2, "%s\n", buf); + } +} + +char* +readsysname(void) +{ + static char name[128]; + char *p; + int n, fd; + + fd = open("/dev/sysname", OREAD); + if(fd >= 0){ + n = read(fd, name, sizeof(name)-1); + close(fd); + if(n > 0){ + name[n] = 0; + return name; + } + } + p = getenv("sysname"); + if(p == nil || *p == 0) + return "unknown"; + return p; +} + +extern int +validip(uchar *ip) +{ + if(ipcmp(ip, IPnoaddr) == 0) + return 0; + if(ipcmp(ip, v4prefix) == 0) + return 0; + return 1; +} + +void +longopt(Req *rp, int t, long v) +{ + if(rp->p + 6 > rp->max) + return; + *rp->p++ = t; + *rp->p++ = 4; + hnputl(rp->p, v); + rp->p += 4; + + op = seprint(op, oe, "%s(%ld)", optname[t], v); +} + +void +addropt(Req *rp, int t, uchar *ip) +{ + if(rp->p + 6 > rp->max) + return; + *rp->p++ = t; + *rp->p++ = 4; + memmove(rp->p, ip+IPv4off, 4); + rp->p += 4; + + op = seprint(op, oe, "%s(%I)", optname[t], ip); +} + +void +maskopt(Req *rp, int t, uchar *ip) +{ + if(rp->p + 6 > rp->max) + return; + *rp->p++ = t; + *rp->p++ = 4; + memmove(rp->p, ip+IPv4off, 4); + rp->p += 4; + + op = seprint(op, oe, "%s(%M)", optname[t], ip); +} + +void +addrsopt(Req *rp, int t, uchar **ip, int i) +{ + if(i <= 0) + return; + if(rp->p + 2 + 4*i > rp->max) + return; + *rp->p++ = t; + *rp->p++ = 4*i; + op = seprint(op, oe, "%s(", optname[t]); + while(i-- > 0){ + v6tov4(rp->p, *ip); + rp->p += 4; + op = seprint(op, oe, "%I", *ip); + ip++; + if(i > 0) + op = seprint(op, oe, " "); + } + op = seprint(op, oe, ")"); +} + +void +byteopt(Req *rp, int t, uchar v) +{ + if(rp->p + 3 > rp->max) + return; + *rp->p++ = t; + *rp->p++ = 1; + *rp->p++ = v; + + op = seprint(op, oe, "%s(%d)", optname[t], v); +} + +void +termopt(Req *rp) +{ + if(rp->p + 1 > rp->max) + return; + *rp->p++ = OBend; +} + +void +stringopt(Req *rp, int t, char *str) +{ + int n; + + n = strlen(str); + if(n > 255) + n = 255; + if(rp->p+n+2 > rp->max) + return; + *rp->p++ = t; + *rp->p++ = n; + memmove(rp->p, str, n); + rp->p += n; + + op = seprint(op, oe, "%s(%s)", optname[t], str); +} + +void +vectoropt(Req *rp, int t, uchar *v, int n) +{ + int i; + + if(n > 255) + n = 255; + if(rp->p+n+2 > rp->max) + return; + *rp->p++ = t; + *rp->p++ = n; + memmove(rp->p, v, n); + rp->p += n; + + op = seprint(op, oe, "%s(", optname[t]); + if(n > 0) + op = seprint(op, oe, "%ud", 0); + for(i = 1; i < n; i++) + op = seprint(op, oe, " %ud", v[i]); +} + +int +fromhex(int x) +{ + if(x >= '0' && x <= '9') + return x - '0'; + return x - 'a'; +} + +void +hexopt(Req *rp, int t, char *str) +{ + int n; + + n = strlen(str); + n /= 2; + if(n > 255) + n = 255; + if(rp->p+n+2 > rp->max) + return; + *rp->p++ = t; + *rp->p++ = n; + while(n-- > 0){ + *rp->p++ = (fromhex(str[0])<<4)|fromhex(str[1]); + str += 2; + } + + op = seprint(op, oe, "%s(%s)", optname[t], str); +} + +/* + * What a crock it is to do this for real. + * A giant hairy mess of ioctls that differ from + * system to system. Don't get sucked in. + * This need not be fast. + */ +void +arpenter(uchar *ip, uchar *ether) +{ + int pid; + char xip[100], xether[100]; + + switch(pid=fork()){ + case -1: + break; + default: + waitpid(); + break; + case 0: + snprint(xip, sizeof xip, "%I", ip); + snprint(xether, sizeof xether, "%#E", ether); + execl("arp", "arp", "-s", xip, xether, "temp", nil); + _exits("execl"); + } +/* + for comfort - ah, the good old days + + int f; + char buf[256]; + + sprint(buf, "%s/arp", net); + f = open(buf, OWRITE); + if(f < 0){ + syslog(debug, blog, "open %s: %r", buf); + return; + } + fprint(f, "add ether %I %E", ip, ether); + close(f); +*/ +} + +char *dhcpmsgname[] = +{ + [Discover] "Discover", + [Offer] "Offer", + [Request] "Request", + [Decline] "Decline", + [Ack] "Ack", + [Nak] "Nak", + [Release] "Release", + [Inform] "Inform", +}; + +void +logdhcp(Req *rp) +{ + char buf[4096]; + char *p, *e; + int i; + + p = buf; + e = buf + sizeof(buf); + if(rp->dhcptype > 0 && rp->dhcptype <= Inform) + p = seprint(p, e, "%s(", dhcpmsgname[rp->dhcptype]); + else + p = seprint(p, e, "%d(", rp->dhcptype); + p = seprint(p, e, "%I->%I) xid(%ux)flag(%ux)", rp->up->raddr, rp->up->laddr, + nhgetl(rp->bp->xid), nhgets(rp->bp->flags)); + if(rp->bp->htype == 1) + p = seprint(p, e, "ea(%E)", rp->bp->chaddr); + if(validip(rp->ciaddr)) + p = seprint(p, e, "ci(%I)", rp->ciaddr); + if(validip(rp->giaddr)) + p = seprint(p, e, "gi(%I)", rp->giaddr); + if(validip(rp->ip)) + p = seprint(p, e, "ip(%I)", rp->ip); + if(rp->id != nil) + p = seprint(p, e, "id(%s)", rp->id); + if(rp->leasetime) + p = seprint(p, e, "leas(%d)", rp->leasetime); + if(validip(rp->server)) + p = seprint(p, e, "sid(%I)", rp->server); + p = seprint(p, e, "need("); + for(i = 0; i < sizeof(rp->requested); i++) + if(rp->requested[i] != 0) + p = seprint(p, e, "%s ", optname[rp->requested[i]]); + p = seprint(p, e, ")"); + + USED(p); + syslog(0, blog, "%s", buf); +} + +void +logdhcpout(Req *rp, char *type) +{ + syslog(0, blog, "%s(%I->%I)id(%s)ci(%V)gi(%V)yi(%V)si(%V) %s", + type, rp->up->laddr, rp->up->raddr, rp->id, + rp->bp->ciaddr, rp->bp->giaddr, rp->bp->yiaddr, rp->bp->siaddr, optbuf); +} + +/* + * if we get behind, it's useless to try answering since the sender + * will probably have retransmitted with a differnt sequence number. + * So dump all the last message in the queue. + */ +void ding(void *x, char *msg) +{ + USED(x); + + if(strstr(msg, "alarm")) + noted(NCONT); + else + noted(NDFLT); +} + +int +readlast(int fd, Udphdr *hdr, uchar *buf, int len) +{ + int lastn, n; + + notify(ding); + + lastn = 0; + for(;;){ + alarm(20); + n = udpread(fd, hdr, buf, len); + alarm(0); + if(n < 0){ + if(lastn > 0) + return lastn; + break; + } + lastn = n; + } + return udpread(fd, hdr, buf, len); +} diff --git a/src/cmd/ip/dhcpd/dhcpleases.c b/src/cmd/ip/dhcpd/dhcpleases.c new file mode 100755 index 00000000..71719649 --- /dev/null +++ b/src/cmd/ip/dhcpd/dhcpleases.c @@ -0,0 +1,43 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <ndb.h> +#include <ip.h> +#include "dat.h" + +extern char *binddir; + long now; + char *blog = "ipboot"; + int minlease = MinLease; + +void +main(void) +{ + Dir *all; + int i, nall, fd; + Binding b; + + fmtinstall('E', eipfmt); + fmtinstall('I', eipfmt); + fmtinstall('V', eipfmt); + fmtinstall('M', eipfmt); + + fd = open(binddir, OREAD); + if(fd < 0) + sysfatal("opening %s: %r", binddir); + nall = dirreadall(fd, &all); + if(nall < 0) + sysfatal("reading %s: %r", binddir); + close(fd); + + b.boundto = 0; + b.lease = b.offer = 0; + now = time(0); + for(i = 0; i < nall; i++){ + parseip(b.ip, all[i].name); + if(syncbinding(&b, 0) < 0) + continue; + if(b.lease > now) + print("%I leased by %s until %s", b.ip, b.boundto, ctime(b.lease)); + } +} diff --git a/src/cmd/ip/dhcpd/mkfile b/src/cmd/ip/dhcpd/mkfile new file mode 100755 index 00000000..90e40a6d --- /dev/null +++ b/src/cmd/ip/dhcpd/mkfile @@ -0,0 +1,22 @@ +<$PLAN9/src/mkhdr + +TARG=dhcpd\ + dhcpleases\ + +DOFILES=\ + db.$O\ + ndb.$O\ + ping.$O\ + +IOFILES=\ + db.$O\ + ping.$O\ + +HFILES=dat.h ../dhcp.h + +<$PLAN9/src/mkmany + +$O.dhcpd: $DOFILES +$O.dhcpleases: $IOFILES +$O.testping: ping.$O + diff --git a/src/cmd/ip/dhcpd/ndb.c b/src/cmd/ip/dhcpd/ndb.c new file mode 100755 index 00000000..53ffb58f --- /dev/null +++ b/src/cmd/ip/dhcpd/ndb.c @@ -0,0 +1,318 @@ +/* + * this currently only works for ethernet bootp's -- presotto + */ +#include <u.h> +#include <libc.h> +#include <ip.h> +#include <bio.h> +#include <ndb.h> +#include "dat.h" + +static void check72(Info *iip); + +Ndb *db; +char *ndbfile; + +Iplifc* +findlifc(uchar *ip) +{ + uchar x[IPaddrlen]; + Ipifc *ifc; + Iplifc *lifc; + + for(ifc = ipifcs; ifc; ifc = ifc->next){ + for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){ + if(lifc->net[0] == 0) + continue; + maskip(ip, lifc->mask, x); + if(memcmp(x, lifc->net, IPaddrlen) == 0) + return lifc; + } + } + return nil; +} + +int +forme(uchar *ip) +{ + Ipifc *ifc; + Iplifc *lifc; + +extern uchar xmyipaddr[IPaddrlen]; + +if(memcmp(ip, xmyipaddr, IPaddrlen) == 0) + return 1; + + for(ifc = ipifcs; ifc; ifc = ifc->next){ + for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next) + if(memcmp(ip, lifc->ip, IPaddrlen) == 0) + return 1; + } + return 0; +} + +uchar noetheraddr[6]; + +static void +setipaddr(uchar *addr, char *ip) +{ + if(ipcmp(addr, IPnoaddr) == 0) + parseip(addr, ip); +} + +static void +setipmask(uchar *mask, char *ip) +{ + if(ipcmp(mask, IPnoaddr) == 0) + parseipmask(mask, ip); +} + +/* + * do an ipinfo with defaults + */ +int +lookupip(uchar *ipaddr, Info *iip, int gate) +{ + char ip[32]; + Ndbtuple *t, *nt; + char *attrs[32], **p; + + if(db == 0) + db = ndbopen(ndbfile); + if(db == 0){ + fprint(2, "can't open db\n"); + return -1; + } + + p = attrs; + *p++ = "ip"; + *p++ = "ipmask"; + *p++ = "@ipgw"; + if(!gate){ + *p++ = "bootf"; + *p++ = "bootf2"; + *p++ = "@tftp"; + *p++ = "@tftp2"; + *p++ = "rootpath"; + *p++ = "dhcp"; + *p++ = "vendorclass"; + *p++ = "ether"; + *p++ = "dom"; + *p++ = "@fs"; + *p++ = "@auth"; + } + *p = 0; + + memset(iip, 0, sizeof(*iip)); + snprint(ip, sizeof(ip), "%I", ipaddr); + t = ndbipinfo(db, "ip", ip, attrs, p - attrs); + if(t == nil) + return -1; + + for(nt = t; nt != nil; nt = nt->entry){ + if(strcmp(nt->attr, "ip") == 0) + setipaddr(iip->ipaddr, nt->val); + else + if(strcmp(nt->attr, "ipmask") == 0) + setipmask(iip->ipmask, nt->val); + else + if(strcmp(nt->attr, "fs") == 0) + setipaddr(iip->fsip, nt->val); + else + if(strcmp(nt->attr, "auth") == 0) + setipaddr(iip->auip, nt->val); + else + if(strcmp(nt->attr, "tftp") == 0) + setipaddr(iip->tftp, nt->val); + else + if(strcmp(nt->attr, "tftp2") == 0) + setipaddr(iip->tftp2, nt->val); + else + if(strcmp(nt->attr, "ipgw") == 0) + setipaddr(iip->gwip, nt->val); + else + if(strcmp(nt->attr, "ether") == 0){ + if(memcmp(iip->etheraddr, noetheraddr, 6) == 0) + parseether(iip->etheraddr, nt->val); + iip->indb = 1; + } + else + if(strcmp(nt->attr, "dhcp") == 0){ + if(iip->dhcpgroup[0] == 0) + strcpy(iip->dhcpgroup, nt->val); + } + else + if(strcmp(nt->attr, "bootf") == 0){ + if(iip->bootf[0] == 0) + strcpy(iip->bootf, nt->val); + } + else + if(strcmp(nt->attr, "bootf2") == 0){ + if(iip->bootf2[0] == 0) + strcpy(iip->bootf2, nt->val); + } + else + if(strcmp(nt->attr, "vendor") == 0){ + if(iip->vendor[0] == 0) + strcpy(iip->vendor, nt->val); + } + else + if(strcmp(nt->attr, "dom") == 0){ + if(iip->domain[0] == 0) + strcpy(iip->domain, nt->val); + } + else + if(strcmp(nt->attr, "rootpath") == 0){ + if(iip->rootpath[0] == 0) + strcpy(iip->rootpath, nt->val); + } + } + ndbfree(t); + maskip(iip->ipaddr, iip->ipmask, iip->ipnet); + return 0; +} + +static uchar zeroes[6]; + +/* + * lookup info about a client in the database. Find an address on the + * same net as riip. + */ +int +lookup(Bootp *bp, Info *iip, Info *riip) +{ + Ndbtuple *t, *nt; + Ndbs s; + char *hwattr; + char *hwval, hwbuf[33]; + uchar ciaddr[IPaddrlen]; + + if(db == 0) + db = ndbopen(ndbfile); + if(db == 0){ + fprint(2, "can't open db\n"); + return -1; + } + + memset(iip, 0, sizeof(*iip)); + + /* client knows its address? */ + v4tov6(ciaddr, bp->ciaddr); + if(validip(ciaddr)){ + if(lookupip(ciaddr, iip, 0) < 0) + return -1; /* don't know anything about it */ + +check72(iip); + + if(!samenet(riip->ipaddr, iip)){ + warning(0, "%I not on %I", ciaddr, riip->ipnet); + return -1; + } + + /* + * see if this is a masquerade, i.e., if the ether + * address doesn't match what we expected it to be. + */ + if(memcmp(iip->etheraddr, zeroes, 6) != 0) + if(memcmp(bp->chaddr, iip->etheraddr, 6) != 0) + warning(0, "ciaddr %I rcvd from %E instead of %E", + ciaddr, bp->chaddr, iip->etheraddr); + + return 0; + } + + if(bp->hlen > Maxhwlen) + return -1; + switch(bp->htype){ + case 1: + hwattr = "ether"; + hwval = hwbuf; + snprint(hwbuf, sizeof(hwbuf), "%E", bp->chaddr); + break; + default: + syslog(0, blog, "not ethernet %E, htype %d, hlen %d", + bp->chaddr, bp->htype, bp->hlen); + return -1; + } + + /* + * use hardware address to find an ip address on + * same net as riip + */ + t = ndbsearch(db, &s, hwattr, hwval); + while(t){ + for(nt = t; nt; nt = nt->entry){ + if(strcmp(nt->attr, "ip") != 0) + continue; + parseip(ciaddr, nt->val); + if(lookupip(ciaddr, iip, 0) < 0) + continue; + if(samenet(riip->ipaddr, iip)){ + ndbfree(t); + return 0; + } + } + ndbfree(t); + t = ndbsnext(&s, hwattr, hwval); + } + return -1; +} + +/* + * interface to ndbipinfo + */ +Ndbtuple* +lookupinfo(uchar *ipaddr, char **attr, int n) +{ + char ip[32]; + + sprint(ip, "%I", ipaddr); + return ndbipinfo(db, "ip", ip, attr, n); +} + +/* + * return the ip addresses for a type of server for system ip + */ +int +lookupserver(char *attr, uchar **ipaddrs, Ndbtuple *t) +{ + Ndbtuple *nt; + int rv = 0; + + for(nt = t; rv < 2 && nt != nil; nt = nt->entry) + if(strcmp(nt->attr, attr) == 0){ + parseip(ipaddrs[rv], nt->val); + rv++; + } + return rv; +} + +/* + * just lookup the name + */ +void +lookupname(char *val, Ndbtuple *t) +{ + Ndbtuple *nt; + + for(nt = t; nt != nil; nt = nt->entry) + if(strcmp(nt->attr, "dom") == 0){ + strcpy(val, nt->val); + break; + } +} + +uchar slash120[IPaddrlen] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0 }; +uchar net72[IPaddrlen] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xff, 0xff, 135, 104, 72, 0 }; + +static void +check72(Info *iip) +{ + uchar net[IPaddrlen]; + + maskip(iip->ipaddr, slash120, net); + if(ipcmp(net, net72) == 0) + syslog(0, blog, "check72 %I %M gw %I", iip->ipaddr, iip->ipmask, iip->gwip); +} diff --git a/src/cmd/ip/dhcpd/ping.c b/src/cmd/ip/dhcpd/ping.c new file mode 100755 index 00000000..7fa7a424 --- /dev/null +++ b/src/cmd/ip/dhcpd/ping.c @@ -0,0 +1,112 @@ +#include <u.h> +#include <libc.h> +#include <ip.h> + + +typedef struct Icmp Icmp; +struct Icmp +{ + uchar vihl; /* Version and header length */ + uchar tos; /* Type of service */ + uchar length[2]; /* packet length */ + uchar id[2]; /* Identification */ + uchar frag[2]; /* Fragment information */ + uchar ttl; /* Time to live */ + uchar proto; /* Protocol */ + uchar ipcksum[2]; /* Header checksum */ + uchar src[4]; /* Ip source */ + uchar dst[4]; /* Ip destination */ + uchar type; + uchar code; + uchar cksum[2]; + uchar icmpid[2]; + uchar seq[2]; + uchar data[1]; +}; + +enum +{ /* Packet Types */ + EchoReply = 0, + Unreachable = 3, + SrcQuench = 4, + EchoRequest = 8, + TimeExceed = 11, + Timestamp = 13, + TimestampReply = 14, + InfoRequest = 15, + InfoReply = 16, + + ICMP_IPSIZE = 20, + ICMP_HDRSIZE = 8, +}; + +static void +catch(void *a, char *msg) +{ + USED(a); + if(strstr(msg, "alarm")) + noted(NCONT); + else + noted(NDFLT); +} + +#define MSG "dhcp probe" + +/* + * make sure noone is using the address + */ +int +icmpecho(uchar *a) +{ + int fd; + char buf[512]; + Icmp *ip; + int i, n, len; + ushort sseq, x; + int rv; + +return 0; + rv = 0; + + sprint(buf, "%I", a); + fd = dial(netmkaddr(buf, "icmp", "1"), 0, 0, 0); + if(fd < 0){ + return 0; + } + + sseq = getpid()*time(0); + + ip = (Icmp*)buf; + notify(catch); + for(i = 0; i < 3; i++){ + ip->type = EchoRequest; + ip->code = 0; + strcpy((char*)ip->data, MSG); + ip->seq[0] = sseq; + ip->seq[1] = sseq>>8; + len = ICMP_IPSIZE+ICMP_HDRSIZE+sizeof(MSG); + + /* send a request */ + if(write(fd, buf, len) < len) + break; + + /* wait 1/10th second for a reply and try again */ + alarm(100); + n = read(fd, buf, sizeof(buf)); + alarm(0); + if(n <= 0) + continue; + + /* an answer to our echo request? */ + x = (ip->seq[1]<<8)|ip->seq[0]; + if(n >= len) + if(ip->type == EchoReply) + if(x == sseq) + if(strcmp((char*)ip->data, MSG) == 0){ + rv = 1; + break; + } + } + close(fd); + return rv; +} diff --git a/src/cmd/ip/dhcpd/testlook.c b/src/cmd/ip/dhcpd/testlook.c new file mode 100755 index 00000000..4937e55b --- /dev/null +++ b/src/cmd/ip/dhcpd/testlook.c @@ -0,0 +1,222 @@ +#include <u.h> +#include <libc.h> +#include <ip.h> +#include <bio.h> +#include <ndb.h> + +static uchar noether[6]; + +/* + * Look for a pair with the given attribute. look first on the same line, + * then in the whole entry. + */ +static Ndbtuple* +lookval(Ndbtuple *entry, Ndbtuple *line, char *attr, char *to) +{ + Ndbtuple *nt; + + /* first look on same line (closer binding) */ + for(nt = line;;){ + if(strcmp(attr, nt->attr) == 0){ + strncpy(to, nt->val, Ndbvlen); + return nt; + } + nt = nt->line; + if(nt == line) + break; + } + /* search whole tuple */ + for(nt = entry; nt; nt = nt->entry) + if(strcmp(attr, nt->attr) == 0){ + strncpy(to, nt->val, Ndbvlen); + return nt; + } + return 0; +} + +/* + * lookup an ip address + */ +static uchar* +lookupip(Ndb *db, char *name, uchar *to, Ipinfo *iip) +{ + Ndbtuple *t, *nt; + char buf[Ndbvlen]; + uchar subnet[IPaddrlen]; + Ndbs s; + char *attr; + + attr = ipattr(name); + if(strcmp(attr, "ip") == 0){ + parseip(to, name); + return to; + } + + t = ndbgetval(db, &s, attr, name, "ip", buf); + if(t){ + /* first look for match on same subnet */ + for(nt = t; nt; nt = nt->entry){ + if(strcmp(nt->attr, "ip") != 0) + continue; + parseip(to, nt->val); + maskip(to, iip->ipmask, subnet); + if(memcmp(subnet, iip->ipnet, sizeof(subnet)) == 0) + return to; + } + + /* otherwise, just take what we have */ + ndbfree(t); + parseip(to, buf); + return to; + } + return 0; +} + +/* + * lookup a subnet and fill in anything we can + */ +static void +recursesubnet(Ndb *db, uchar *mask, Ipinfo *iip, char *fs, char *gw, char *au) +{ + Ndbs s; + Ndbtuple *t; + uchar submask[IPaddrlen]; + char ip[Ndbvlen]; + + memmove(iip->ipmask, mask, 4); + maskip(iip->ipaddr, iip->ipmask, iip->ipnet); + sprint(ip, "%I", iip->ipnet); + t = ndbsearch(db, &s, "ip", ip); +print("%s->", ip); + if(t){ + /* look for a further subnet */ + if(lookval(t, s.t, "ipmask", ip)){ + parseip(submask, ip); + + /* recurse only if it has changed */ + if(!equivip(submask, mask)) + recursesubnet(db, submask, iip, fs, gw, au); + + } + + /* fill in what we don't have */ + if(gw[0] == 0) + lookval(t, s.t, "ipgw", gw); + if(fs[0] == 0) + lookval(t, s.t, "fs", fs); + if(au[0] == 0) + lookval(t, s.t, "auth", au); + + ndbfree(t); + } +} +#ifdef foo +/* + * find out everything we can about a system from what has been + * specified. + */ +int +ipinfo(Ndb *db, char *etherin, char *ipin, char *name, Ipinfo *iip) +{ + Ndbtuple *t; + Ndbs s; + char ether[Ndbvlen]; + char ip[Ndbvlen]; + char fsname[Ndbvlen]; + char gwname[Ndbvlen]; + char auname[Ndbvlen]; + + memset(iip, 0, sizeof(Ipinfo)); + fsname[0] = 0; + gwname[0] = 0; + auname[0] = 0; + + /* + * look for a matching entry + */ + t = 0; + if(etherin) + t = ndbgetval(db, &s, "ether", etherin, "ip", ip); + if(t == 0 && ipin) + t = ndbsearch(db, &s, "ip", ipin); + if(t == 0 && name) + t = ndbgetval(db, &s, ipattr(name), name, "ip", ip); + if(t){ + /* + * copy in addresses and name + */ + if(lookval(t, s.t, "ip", ip)) + parseip(iip->ipaddr, ip); + if(lookval(t, s.t, "ether", ether)) + parseether(iip->etheraddr, ether); + lookval(t, s.t, "dom", iip->domain); + + /* + * Look for bootfile, fs, and gateway. + * If necessary, search through all entries for + * this ip address. + */ + while(t){ + if(iip->bootf[0] == 0) + lookval(t, s.t, "bootf", iip->bootf); + if(fsname[0] == 0) + lookval(t, s.t, "fs", fsname); + if(gwname[0] == 0) + lookval(t, s.t, "ipgw", gwname); + if(auname[0] == 0) + lookval(t, s.t, "auth", auname); + ndbfree(t); + if(iip->bootf[0] && fsname[0] && gwname[0] && auname[0]) + break; + t = ndbsnext(&s, "ether", ether); + } + } else if(ipin) { + /* + * copy in addresses (all we know) + */ + parseip(iip->ipaddr, ipin); + if(etherin) + parseether(iip->etheraddr, etherin); + } else + return -1; + + /* + * Look up the client's network and find a subnet mask for it. + * Fill in from the subnet (or net) entry anything we can't figure + * out from the client record. + */ + recursesubnet(db, classmask[CLASS(iip->ipaddr)], iip, fsname, gwname, auname); + + /* lookup fs's and gw's ip addresses */ + + if(fsname[0]) + lookupip(db, fsname, iip->fsip, iip); + if(gwname[0]) + lookupip(db, gwname, iip->gwip, iip); + if(auname[0]) + lookupip(db, auname, iip->auip, iip); + return 0; +} +#endif +void +main(int argc, char **argv) +{ + Ipinfo ii; + Ndb *db; + + db = ndbopen(0); + + fmtinstall('E', eipconv); + fmtinstall('I', eipconv); + if(argc < 2) + exits(0); + if(strchr(argv[1], '.')){ + if(ipinfo(db, 0, argv[1], 0, &ii) < 0) + exits(0); + } else { + if(ipinfo(db, argv[1], 0, 0, &ii) < 0) + exits(0); + } + fprint(2, "a %I m %I n %I f %s e %E\n", ii.ipaddr, + ii.ipmask, ii.ipnet, ii.bootf, ii.etheraddr); +} diff --git a/src/cmd/ip/dhcpd/testlookup.c b/src/cmd/ip/dhcpd/testlookup.c new file mode 100755 index 00000000..c69624c5 --- /dev/null +++ b/src/cmd/ip/dhcpd/testlookup.c @@ -0,0 +1,168 @@ +#include <u.h> +#include <libc.h> +#include <ip.h> +#include <bio.h> +#include <ndb.h> + +static uchar noether[6]; + Ndb *db; + +static void +recursesubnet(Ndb *db, uchar *addr, uchar *mask, char *attr, char *name, char *name1) +{ + Ndbs s; + Ndbtuple *t, *nt; + uchar submask[IPaddrlen], net[IPaddrlen]; + char ip[Ndbvlen]; + int found; + + maskip(addr, mask, net); + sprint(ip, "%I", net); + t = ndbsearch(db, &s, "ip", ip); + if(t == 0) + return; + + for(nt = t; nt; nt = nt->entry){ + if(strcmp(nt->attr, "ipmask") == 0){ + parseip(submask, nt->val); + if(memcmp(submask, mask, IPaddrlen) != 0) + recursesubnet(db, addr, submask, attr, name, name1); + break; + } + } + + if(name[0] == 0){ + found = 0; + for(nt = t; nt; nt = nt->entry){ + if(strcmp(nt->attr, attr) == 0){ + if(found){ + strcpy(name, nt->val); + name1[0] = 0; + found = 1; + } else { + strcpy(name1, nt->val); + break; + } + } + } + } + + ndbfree(t); +} + +/* + * lookup an ip address + */ +static int +getipaddr(Ndb *db, char *name, uchar *to, Ipinfo *iip) +{ + Ndbtuple *t, *nt; + char buf[Ndbvlen]; + uchar subnet[IPaddrlen]; + Ndbs s; + char *attr; + + attr = ipattr(name); + if(strcmp(attr, "ip") == 0){ + parseip(to, name); + return 1; + } + + t = ndbgetval(db, &s, attr, name, "ip", buf); + if(t){ + /* first look for match on same subnet */ + for(nt = t; nt; nt = nt->entry){ + if(strcmp(nt->attr, "ip") != 0) + continue; + parseip(to, nt->val); + maskip(to, iip->ipmask, subnet); + if(memcmp(subnet, iip->ipnet, sizeof(subnet)) == 0) + return 1; + } + + /* otherwise, just take what we have */ + ndbfree(t); + parseip(to, buf); + return 1; + } + return 0; +} + +/* + * return the ip addresses for a type of server for system ip + */ +int +lookupserver(char *attr, uchar ipaddrs[2][IPaddrlen], Ipinfo *iip) +{ + Ndbtuple *t, *nt; + Ndbs s; + char ip[32]; + char name[Ndbvlen]; + char name1[Ndbvlen]; + int i; + + name[0] = name1[0] = 0; + + snprint(ip, sizeof(ip), "%I", iip->ipaddr); + t = ndbsearch(db, &s, "ip", ip); + while(t){ + for(nt = t; nt; nt = nt->entry){ + if(strcmp(attr, nt->attr) == 0){ + if(*name == 0) + strcpy(name, nt->val); + else { + strcpy(name1, nt->val); + break; + } + } + } + if(name[0]) + break; + t = ndbsnext(&s, "ip", ip); + } + + if(name[0] == 0) + recursesubnet(db, iip->ipaddr, classmask[CLASS(iip->ipaddr)], attr, name, name1); + + i = 0; + if(name[0] && getipaddr(db, name, *ipaddrs, iip) == 1){ + ipaddrs++; + i++; + } + if(name1[0] && getipaddr(db, name1, *ipaddrs, iip) == 1) + i++; + return i; +} + +void +main(int argc, char **argv) +{ + Ipinfo ii; + uchar addrs[2][IPaddrlen]; + int i, j; + + db = ndbopen(0); + + fmtinstall('E', eipconv); + fmtinstall('I', eipconv); + if(argc < 2) + exits(0); + if(strchr(argv[1], '.')){ + if(ipinfo(db, 0, argv[1], 0, &ii) < 0) + exits(0); + } else { + if(ipinfo(db, argv[1], 0, 0, &ii) < 0) + exits(0); + } + print("a %I m %I n %I f %s e %E a %I\n", ii.ipaddr, + ii.ipmask, ii.ipnet, ii.bootf, ii.etheraddr, ii.auip); + + i = lookupserver("auth", addrs, &ii); + print("lookupserver returns %d\n", i); + for(j = 0; j < i; j++) + print("%I\n", addrs[j]); + i = lookupserver("dns", addrs, &ii); + print("lookupserver returns %d\n", i); + for(j = 0; j < i; j++) + print("%I\n", addrs[j]); +} diff --git a/src/cmd/ip/dhcpd/testping.c b/src/cmd/ip/dhcpd/testping.c new file mode 100755 index 00000000..b5ca90c5 --- /dev/null +++ b/src/cmd/ip/dhcpd/testping.c @@ -0,0 +1,22 @@ +#include <u.h> +#include <libc.h> +#include <ip.h> +#include <bio.h> +#include <ndb.h> +#include "dat.h" + +char *blog = "ipboot"; + +void +main(int argc, char **argv) +{ + fmtinstall('E', eipconv); + fmtinstall('I', eipconv); + + if(argc < 2) + exits(0); + if(icmpecho(argv[1])) + fprint(2, "%s live\n", argv[1]); + else + fprint(2, "%s doesn't answer\n", argv[1]); +} |