aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/ip/dhcpd/db.c
diff options
context:
space:
mode:
authorrsc <devnull@localhost>2005-12-26 04:48:52 +0000
committerrsc <devnull@localhost>2005-12-26 04:48:52 +0000
commit87a52e0485d3281ebea6bf4b725aa8023690e96f (patch)
tree0abc2d2ddb875196177231639d3cb4519e814b9d /src/cmd/ip/dhcpd/db.c
parent35d26aa32167e84326cdb745c0e906393b8de71d (diff)
downloadplan9port-87a52e0485d3281ebea6bf4b725aa8023690e96f.tar.gz
plan9port-87a52e0485d3281ebea6bf4b725aa8023690e96f.tar.bz2
plan9port-87a52e0485d3281ebea6bf4b725aa8023690e96f.zip
new goodies
Diffstat (limited to 'src/cmd/ip/dhcpd/db.c')
-rwxr-xr-xsrc/cmd/ip/dhcpd/db.c452
1 files changed, 452 insertions, 0 deletions
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;
+}