123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439 |
- #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 = "/lib/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, ORDWR);
- if(fd >= 0)
- return fd;
- 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")){
- /* no file, create an exclusive access file */
- fd = create(file, ORDWR, DMEXCL|0664);
- 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;
- 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;
- }
- }
- /*
- * look for oldest binding that we think is unused
- */
- for(;;){
- oldest = nil;
- oldesttime = 0;
- for(b = bcache; b; b = b->next){
- 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;
- oldesttime = b->lasttouched;
- }
- }
- }
- if(oldest == nil)
- break;
- /* make sure noone is still using it */
- oldest->tried = now;
- 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){
- if(b->offeredto && strcmp(b->offeredto, id) == 0 && samenet(b->ip, iip)){
- /* make sure some other system hasn't stolen it */
- syncbinding(b, 0);
- 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;
- }
|