2018-01-06 01:08:25 +01:00
|
|
|
/*
|
|
|
|
* This file is part of the UCB release of Plan 9. It is subject to the license
|
|
|
|
* terms in the LICENSE file found in the top-level directory of this
|
|
|
|
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
|
|
|
|
* part of the UCB release of Plan 9, including this file, may be copied,
|
|
|
|
* modified, propagated, or distributed except according to the terms contained
|
|
|
|
* in the LICENSE file.
|
|
|
|
*/
|
|
|
|
/* Portions of this file are Copyright (C) 2015-2018 Giacomo Tesio <giacomo@tesio.it>
|
|
|
|
* See /doc/license/gpl-2.0.txt for details about the licensing.
|
|
|
|
*/
|
|
|
|
/* Portions of this file are Copyright (C) 9front's team.
|
|
|
|
* See /doc/license/9front-mit for details about the licensing.
|
2021-12-06 17:39:58 +01:00
|
|
|
* See http://git.9front.org/plan9front/plan9front/HEAD/info.html for a list of authors.
|
2018-01-06 01:08:25 +01:00
|
|
|
*/
|
2016-11-25 17:18:40 +01:00
|
|
|
#include <u.h>
|
2017-04-19 23:33:14 +02:00
|
|
|
#include <lib9.h>
|
2016-11-25 17:18:40 +01:00
|
|
|
#include <ip.h>
|
|
|
|
#include <bio.h>
|
|
|
|
#include <ndb.h>
|
2020-09-13 02:21:36 +02:00
|
|
|
#include <chartypes.h>
|
2016-11-25 17:18:40 +01:00
|
|
|
#include "dat.h"
|
|
|
|
|
|
|
|
/*
|
|
|
|
* format of a binding entry:
|
|
|
|
* char ipaddr[32];
|
|
|
|
* char id[32];
|
|
|
|
* char hwa[32];
|
|
|
|
* char otime[10];
|
|
|
|
*/
|
|
|
|
Binding *bcache;
|
|
|
|
uint8_t 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, uint8_t *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(uint8_t *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(uint8_t *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++){
|
2019-11-26 02:25:23 +01:00
|
|
|
fd = sys_open(file, ORDWR);
|
2016-11-25 17:18:40 +01:00
|
|
|
if(fd >= 0)
|
|
|
|
return fd;
|
2019-11-26 02:25:23 +01:00
|
|
|
sys_errstr(err, sizeof err);
|
2016-11-25 17:18:40 +01:00
|
|
|
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 */
|
2016-12-24 21:25:20 +01:00
|
|
|
fd = ocreate(file, ORDWR, DMEXCL|0664);
|
2016-11-25 17:18:40 +01:00
|
|
|
if(fd >= 0)
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
setbinding(Binding *b, char *id, int32_t t)
|
|
|
|
{
|
|
|
|
if(b->boundto)
|
|
|
|
free(b->boundto);
|
|
|
|
|
|
|
|
b->boundto = strdup(id);
|
|
|
|
b->lease = t;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
parsebinding(Binding *b, char *buf)
|
|
|
|
{
|
|
|
|
int32_t 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;
|
|
|
|
|
2019-11-26 02:25:23 +01:00
|
|
|
sys_seek(fd, 0, 0);
|
2016-11-25 17:18:40 +01:00
|
|
|
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){
|
2019-11-26 02:25:23 +01:00
|
|
|
i = jehanne_read(fd, buf, sizeof(buf)-1);
|
2016-11-25 17:18:40 +01:00
|
|
|
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;
|
|
|
|
|
2019-11-26 02:25:23 +01:00
|
|
|
sys_close(fd);
|
2016-11-25 17:18:40 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
extern int
|
|
|
|
samenet(uint8_t *ip, Info *iip)
|
|
|
|
{
|
|
|
|
uint8_t x[IPaddrlen];
|
|
|
|
|
|
|
|
maskip(iip->ipmask, ip, x);
|
|
|
|
return ipcmp(x, iip->ipnet) == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* create a record for each binding
|
|
|
|
*/
|
|
|
|
extern void
|
|
|
|
initbinding(uint8_t *first, int n)
|
|
|
|
{
|
|
|
|
while(n-- > 0){
|
|
|
|
iptobinding(first, 1);
|
|
|
|
incip(first);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* find a binding for a specific ip address
|
|
|
|
*/
|
|
|
|
extern Binding*
|
|
|
|
iptobinding(uint8_t *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",
|
|
|
|
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, int32_t 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;
|
|
|
|
int32_t 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){
|
2019-11-26 02:25:23 +01:00
|
|
|
sys_close(fd);
|
2016-11-25 17:18:40 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
setbinding(b, b->offeredto, now + b->offer);
|
|
|
|
b->lasttouched = now;
|
2016-12-24 21:25:20 +01:00
|
|
|
|
2016-11-25 17:18:40 +01:00
|
|
|
if(writebinding(fd, b) < 0){
|
2019-11-26 02:25:23 +01:00
|
|
|
sys_close(fd);
|
2016-11-25 17:18:40 +01:00
|
|
|
return -1;
|
|
|
|
}
|
2019-11-26 02:25:23 +01:00
|
|
|
sys_close(fd);
|
2016-11-25 17:18:40 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* commit a lease, this could fail
|
|
|
|
*/
|
|
|
|
extern int
|
|
|
|
releasebinding(Binding *b, char *id)
|
|
|
|
{
|
|
|
|
int fd;
|
|
|
|
int32_t now;
|
|
|
|
|
|
|
|
now = time(0);
|
|
|
|
|
|
|
|
fd = syncbinding(b, 1);
|
|
|
|
if(fd < 0)
|
|
|
|
return -1;
|
|
|
|
if(b->lease > now && b->boundto && strcmp(b->boundto, id) != 0){
|
2019-11-26 02:25:23 +01:00
|
|
|
sys_close(fd);
|
2016-11-25 17:18:40 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
b->lease = 0;
|
|
|
|
b->expoffer = 0;
|
2016-12-24 21:25:20 +01:00
|
|
|
|
2016-11-25 17:18:40 +01:00
|
|
|
if(writebinding(fd, b) < 0){
|
2019-11-26 02:25:23 +01:00
|
|
|
sys_close(fd);
|
2016-11-25 17:18:40 +01:00
|
|
|
return -1;
|
|
|
|
}
|
2019-11-26 02:25:23 +01:00
|
|
|
sys_close(fd);
|
2016-11-25 17:18:40 +01:00
|
|
|
return 0;
|
|
|
|
}
|