new goodies
This commit is contained in:
parent
35d26aa321
commit
87a52e0485
44 changed files with 8723 additions and 0 deletions
452
src/cmd/ip/dhcpd/db.c
Executable file
452
src/cmd/ip/dhcpd/db.c
Executable file
|
|
@ -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;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue