Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
#include <u.h>
2
#include <libc.h>
3
#include <ip.h>
4
#include <bio.h>
5
#include <ndb.h>
6
#include <ctype.h>
7
#include "dat.h"
8
 
9
/*
10
 *  format of a binding entry:
11
 *	char ipaddr[32];
12
 *	char id[32];
13
 *	char hwa[32];
14
 *	char otime[10];
15
 */
16
Binding *bcache;
17
uchar bfirst[IPaddrlen];
18
char *binddir = "/lib/ndb/dhcp";
19
 
20
/*
21
 *  convert a byte array to hex
22
 */
23
static char
24
hex(int x)
25
{
26
	if(x < 10)
27
		return x + '0';
28
	return x - 10 + 'a';
29
}
30
extern char*
31
tohex(char *hdr, uchar *p, int len)
32
{
33
	char *s, *sp;
34
	int hlen;
35
 
36
	hlen = strlen(hdr);
37
	s = malloc(hlen + 2*len + 1);
38
	sp = s;
39
	strcpy(sp, hdr);
40
	sp += hlen;
41
	for(; len > 0; len--){
42
		*sp++ = hex(*p>>4);
43
		*sp++ = hex(*p & 0xf);
44
		p++;
45
	}
46
	*sp = 0;
47
	return s;
48
}
49
 
50
/*
51
 *  convert a client id to a string.  If it's already
52
 *  ascii, leave it be.  Otherwise, convert it to hex.
53
 */
54
extern char*
55
toid(uchar *p, int n)
56
{
57
	int i;
58
	char *s;
59
 
60
	for(i = 0; i < n; i++)
61
		if(!isprint(p[i]))
62
			return tohex("id", p, n);
63
	s = malloc(n + 1);
64
	memmove(s, p, n);
65
	s[n] = 0;
66
	return s;
67
}
68
 
69
/*
70
 *  increment an ip address
71
 */
72
static void
73
incip(uchar *ip)
74
{
75
	int i, x;
76
 
77
	for(i = IPaddrlen-1; i >= 0; i--){
78
		x = ip[i];
79
		x++;
80
		ip[i] = x;
81
		if((x & 0x100) == 0)
82
			break;
83
	}
84
}
85
 
86
/*
87
 *  find a binding for an id or hardware address
88
 */
89
static int
90
lockopen(char *file)
91
{
92
	char err[ERRMAX];
93
	int fd, tries;
94
 
95
	for(tries = 0; tries < 5; tries++){
96
		fd = open(file, ORDWR);
97
		if(fd >= 0)
98
			return fd;
99
		errstr(err, sizeof err);
100
		if(strstr(err, "lock")){
101
			/* wait for other process to let go of lock */
102
			sleep(250);
103
 
104
			/* try again */
105
			continue;
106
		}
107
		if(strstr(err, "exist")){
108
			/* no file, create an exclusive access file */
109
			fd = create(file, ORDWR, DMEXCL|0664);
110
			if(fd >= 0)
111
				return fd;
112
		}
113
	}
114
	return -1;
115
}
116
 
117
void
118
setbinding(Binding *b, char *id, long t)
119
{
120
	if(b->boundto)
121
		free(b->boundto);
122
 
123
	b->boundto = strdup(id);
124
	b->lease = t;
125
}
126
 
127
static void
128
parsebinding(Binding *b, char *buf)
129
{
130
	long t;
131
	char *id, *p;
132
 
133
	/* parse */
134
	t = atoi(buf);
135
	id = strchr(buf, '\n');
136
	if(id){
137
		*id++ = 0;
138
		p = strchr(id, '\n');
139
		if(p)
140
			*p = 0;
141
	} else
142
		id = "";
143
 
144
	/* replace any past info */
145
	setbinding(b, id, t);
146
}
147
 
148
static int
149
writebinding(int fd, Binding *b)
150
{
151
	Dir *d;
152
 
153
	seek(fd, 0, 0);
154
	if(fprint(fd, "%ld\n%s\n", b->lease, b->boundto) < 0)
155
		return -1;
156
	d = dirfstat(fd);
157
	if(d == nil)
158
		return -1;
159
	b->q.type = d->qid.type;
160
	b->q.path = d->qid.path;
161
	b->q.vers = d->qid.vers;
162
	free(d);
163
	return 0;
164
}
165
 
166
/*
167
 *  synchronize cached binding with file.  the file always wins.
168
 */
169
int
170
syncbinding(Binding *b, int returnfd)
171
{
172
	char buf[512];
173
	int i, fd;
174
	Dir *d;
175
 
176
	snprint(buf, sizeof(buf), "%s/%I", binddir, b->ip);
177
	fd = lockopen(buf);
178
	if(fd < 0){
179
		/* assume someone else is using it */
180
		b->lease = time(0) + OfferTimeout;
181
		return -1;
182
	}
183
 
184
	/* reread if changed */
185
	d = dirfstat(fd);
186
	if(d != nil)	/* BUG? */
187
	if(d->qid.type != b->q.type || d->qid.path != b->q.path || d->qid.vers != b->q.vers){
188
		i = read(fd, buf, sizeof(buf)-1);
189
		if(i < 0)
190
			i = 0;
191
		buf[i] = 0;
192
		parsebinding(b, buf);
193
		b->lasttouched = d->mtime;
194
		b->q.path = d->qid.path;
195
		b->q.vers = d->qid.vers;
196
	}
197
 
198
	free(d);
199
 
200
	if(returnfd)
201
		return fd;
202
 
203
	close(fd);
204
	return 0;
205
}
206
 
207
extern int
208
samenet(uchar *ip, Info *iip)
209
{
210
	uchar x[IPaddrlen];
211
 
212
	maskip(iip->ipmask, ip, x);
213
	return ipcmp(x, iip->ipnet) == 0;
214
}
215
 
216
/*
217
 *  create a record for each binding
218
 */
219
extern void
220
initbinding(uchar *first, int n)
221
{
222
	while(n-- > 0){
223
		iptobinding(first, 1);
224
		incip(first);
225
	}
226
}
227
 
228
/*
229
 *  find a binding for a specific ip address
230
 */
231
extern Binding*
232
iptobinding(uchar *ip, int mk)
233
{
234
	Binding *b;
235
 
236
	for(b = bcache; b; b = b->next){
237
		if(ipcmp(b->ip, ip) == 0){
238
			syncbinding(b, 0);
239
			return b;
240
		}
241
	}
242
 
243
	if(mk == 0)
244
		return 0;
245
	b = malloc(sizeof(*b));
246
	memset(b, 0, sizeof(*b));
247
	ipmove(b->ip, ip);
248
	b->next = bcache;
249
	bcache = b;
250
	syncbinding(b, 0);
251
	return b;
252
}
253
 
254
static void
255
lognolease(Binding *b)
256
{
257
	/* renew the old binding, and hope it eventually goes away */
258
	b->offer = 5*60;
259
	commitbinding(b);
260
 
261
	/* complain if we haven't in the last 5 minutes */
262
	if(now - b->lastcomplained < 5*60)
263
		return;
264
	syslog(0, blog, "dhcp: lease for %I to %s ended at %ld but still in use\n",
265
		b->ip, b->boundto != nil ? b->boundto : "?", b->lease);
266
	b->lastcomplained = now;
267
}
268
 
269
/*
270
 *  find a free binding for a hw addr or id on the same network as iip
271
 */
272
extern Binding*
273
idtobinding(char *id, Info *iip, int ping)
274
{
275
	Binding *b, *oldest;
276
	int oldesttime;
277
 
278
	/*
279
	 *  first look for an old binding that matches.  that way
280
	 *  clients will tend to keep the same ip addresses.
281
	 */
282
	for(b = bcache; b; b = b->next){
283
		if(b->boundto && strcmp(b->boundto, id) == 0){
284
			if(!samenet(b->ip, iip))
285
				continue;
286
 
287
			/* check with the other servers */
288
			syncbinding(b, 0);
289
			if(strcmp(b->boundto, id) == 0)
290
				return b;
291
		}
292
	}
293
 
294
	/*
295
	 *  look for oldest binding that we think is unused
296
	 */
297
	for(;;){
298
		oldest = nil;
299
		oldesttime = 0;
300
		for(b = bcache; b; b = b->next){
301
			if(b->tried != now)
302
			if(b->lease < now && b->expoffer < now && samenet(b->ip, iip))
303
			if(oldest == nil || b->lasttouched < oldesttime){
304
				/* sync and check again */
305
				syncbinding(b, 0);
306
				if(b->lease < now && b->expoffer < now && samenet(b->ip, iip))
307
				if(oldest == nil || b->lasttouched < oldesttime){
308
					oldest = b;
309
					oldesttime = b->lasttouched;
310
				}
311
			}
312
		}
313
		if(oldest == nil)
314
			break;
315
 
316
		/* make sure noone is still using it */
317
		oldest->tried = now;
318
		if(ping == 0 || icmpecho(oldest->ip) == 0)
319
			return oldest;
320
 
321
		lognolease(oldest);	/* sets lastcomplained */
322
	}
323
 
324
	/* try all bindings */
325
	for(b = bcache; b; b = b->next){
326
		syncbinding(b, 0);
327
		if(b->tried != now)
328
		if(b->lease < now && b->expoffer < now && samenet(b->ip, iip)){
329
			b->tried = now;
330
			if(ping == 0 || icmpecho(b->ip) == 0)
331
				return b;
332
 
333
			lognolease(b);
334
		}
335
	}
336
 
337
	/* nothing worked, give up */
338
	return 0;
339
}
340
 
341
/*
342
 *  create an offer
343
 */
344
extern void
345
mkoffer(Binding *b, char *id, long leasetime)
346
{
347
	if(leasetime <= 0){
348
		if(b->lease > now + minlease)
349
			leasetime = b->lease - now;
350
		else
351
			leasetime = minlease;
352
	}
353
	if(b->offeredto)
354
		free(b->offeredto);
355
	b->offeredto = strdup(id);
356
	b->offer = leasetime;
357
	b->expoffer = now + OfferTimeout;
358
}
359
 
360
/*
361
 *  find an offer for this id
362
 */
363
extern Binding*
364
idtooffer(char *id, Info *iip)
365
{
366
	Binding *b;
367
 
368
	/* look for an offer to this id */
369
	for(b = bcache; b; b = b->next){
370
		if(b->offeredto && strcmp(b->offeredto, id) == 0 && samenet(b->ip, iip)){
371
			/* make sure some other system hasn't stolen it */
372
			syncbinding(b, 0);
373
			if(b->lease < now
374
			|| (b->boundto && strcmp(b->boundto, b->offeredto) == 0))
375
				return b;
376
		}
377
	}
378
	return 0;
379
}
380
 
381
/*
382
 *  commit a lease, this could fail
383
 */
384
extern int
385
commitbinding(Binding *b)
386
{
387
	int fd;
388
	long now;
389
 
390
	now = time(0);
391
 
392
	if(b->offeredto == 0)
393
		return -1;
394
	fd = syncbinding(b, 1);
395
	if(fd < 0)
396
		return -1;
397
	if(b->lease > now && b->boundto && strcmp(b->boundto, b->offeredto) != 0){
398
		close(fd);
399
		return -1;
400
	}
401
	setbinding(b, b->offeredto, now + b->offer);
402
	b->lasttouched = now;
403
 
404
	if(writebinding(fd, b) < 0){
405
		close(fd);
406
		return -1;
407
	}
408
	close(fd);
409
	return 0;
410
}
411
 
412
/*
413
 *  commit a lease, this could fail
414
 */
415
extern int
416
releasebinding(Binding *b, char *id)
417
{
418
	int fd;
419
	long now;
420
 
421
	now = time(0);
422
 
423
	fd = syncbinding(b, 1);
424
	if(fd < 0)
425
		return -1;
426
	if(b->lease > now && b->boundto && strcmp(b->boundto, id) != 0){
427
		close(fd);
428
		return -1;
429
	}
430
	b->lease = 0;
431
	b->expoffer = 0;
432
 
433
	if(writebinding(fd, b) < 0){
434
		close(fd);
435
		return -1;
436
	}
437
	close(fd);
438
	return 0;
439
}