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 "dhcp.h"
5
 
6
void	bootpdump(uchar *p, int n);
7
void	dhcpinit(void);
8
void	dhcprecv(void);
9
void	dhcpsend(int);
10
void	myfatal(char *fmt, ...);
11
int	openlisten(char*);
12
uchar	*optaddaddr(uchar*, int, uchar*);
13
uchar	*optaddbyte(uchar*, int, int);
14
uchar	*optadd(uchar*, int, void*, int);
15
uchar	*optaddulong(uchar*, int, ulong);
16
uchar	*optget(Bootp*, int, int);
17
int	optgetaddr(Bootp*, int, uchar*);
18
int	optgetbyte(Bootp*, int);
19
ulong	optgetulong(Bootp*, int);
20
Bootp	*parse(uchar*, int);
21
void	stdinthread(void*);
22
ulong	thread(void(*f)(void*), void *a);
23
void	timerthread(void*);
24
void	usage(void);
25
 
26
struct {
27
	QLock	lk;
28
	int	state;
29
	int	fd;
30
	ulong	xid;
31
	ulong	starttime;
32
	char	cid[100];
33
	char	sname[64];
34
	uchar	server[IPaddrlen];		/* server IP address */
35
	uchar	client[IPaddrlen];		/* client IP address */
36
	uchar	mask[IPaddrlen];		/* client mask */
37
	ulong	lease;		/* lease time */
38
	ulong	resend;		/* number of resends for current state */
39
	ulong	timeout;	/* time to timeout - seconds */
40
} dhcp;
41
 
42
char	net[64];
43
 
44
char optmagic[4] = { 0x63, 0x82, 0x53, 0x63 };
45
 
46
void
47
main(int argc, char *argv[])
48
{
49
	char *p;
50
 
51
	setnetmtpt(net, sizeof(net), nil);
52
 
53
	ARGBEGIN{
54
	case 'x':
55
		p = ARGF();
56
		if(p == nil)
57
			usage();
58
		setnetmtpt(net, sizeof(net), p);
59
	}ARGEND;
60
 
61
	fmtinstall('E', eipfmt);
62
	fmtinstall('I', eipfmt);
63
	fmtinstall('V', eipfmt);
64
 
65
	dhcpinit();
66
 
67
	rfork(RFNOTEG|RFREND);
68
 
69
	thread(timerthread, 0);
70
	thread(stdinthread, 0);
71
 
72
	qlock(&dhcp.lk);
73
	dhcp.starttime = time(0);
74
	dhcp.fd = openlisten(net);
75
	dhcpsend(Discover);
76
	dhcp.state = Sselecting;
77
	dhcp.resend = 0;
78
	dhcp.timeout = 4;
79
 
80
	while(dhcp.state != Sbound)
81
		dhcprecv();
82
 
83
	/* allows other clients on this machine */
84
	close(dhcp.fd);
85
	dhcp.fd = -1;
86
 
87
	print("ip=%I\n", dhcp.client);
88
	print("mask=%I\n", dhcp.mask);
89
	print("end\n");
90
 
91
	/* keep lease alive */
92
	for(;;) {
93
//fprint(2, "got lease for %d\n", dhcp.lease);
94
		qunlock(&dhcp.lk);
95
		sleep(dhcp.lease*500);	/* wait half of lease time */
96
		qlock(&dhcp.lk);
97
 
98
//fprint(2, "try renue\n", dhcp.lease);
99
		dhcp.starttime = time(0);
100
		dhcp.fd = openlisten(net);
101
		dhcp.xid = time(0)*getpid();
102
		dhcpsend(Request);
103
		dhcp.state = Srenewing;
104
		dhcp.resend = 0;
105
		dhcp.timeout = 1;
106
 
107
		while(dhcp.state != Sbound)
108
			dhcprecv();
109
 
110
		/* allows other clients on this machine */
111
		close(dhcp.fd);
112
		dhcp.fd = -1;
113
	}
114
}
115
 
116
void
117
usage(void)
118
{
119
	fprint(2, "usage: %s [-x netextension]\n", argv0);
120
	exits("usage");
121
}
122
 
123
void
124
timerthread(void*)
125
{
126
	for(;;) {
127
		sleep(1000);
128
		qlock(&dhcp.lk);
129
		if(--dhcp.timeout > 0) {
130
			qunlock(&dhcp.lk);
131
			continue;
132
		}
133
 
134
		switch(dhcp.state) {
135
		default:
136
			myfatal("timerthread: unknown state %d", dhcp.state);
137
		case Sinit:
138
			break;
139
		case Sselecting:
140
			dhcpsend(Discover);
141
			dhcp.timeout = 4;
142
			dhcp.resend++;
143
			if(dhcp.resend>5)
144
				myfatal("dhcp: giving up: selecting");
145
			break;
146
		case Srequesting:
147
			dhcpsend(Request);
148
			dhcp.timeout = 4;
149
			dhcp.resend++;
150
			if(dhcp.resend>5)
151
				myfatal("dhcp: giving up: requesting");
152
			break;
153
		case Srenewing:
154
			dhcpsend(Request);
155
			dhcp.timeout = 1;
156
			dhcp.resend++;
157
			if(dhcp.resend>3) {
158
				dhcp.state = Srebinding;
159
				dhcp.resend = 0;
160
			}
161
			break;
162
		case Srebinding:
163
			dhcpsend(Request);
164
			dhcp.timeout = 4;
165
			dhcp.resend++;
166
			if(dhcp.resend>5)
167
				myfatal("dhcp: giving up: rebinding");
168
			break;
169
		case Sbound:
170
			break;
171
		}
172
		qunlock(&dhcp.lk);
173
	}
174
}
175
 
176
void
177
stdinthread(void*)
178
{
179
	uchar buf[100];
180
	int n;
181
 
182
	for(;;) {
183
		n = read(0, buf, sizeof(buf));
184
		if(n <= 0)
185
			break;
186
	}
187
	/* shutdown cleanly */
188
	qlock(&dhcp.lk);
189
	if(dhcp.client) {
190
		if(dhcp.fd < 0)
191
			dhcp.fd = openlisten(net);
192
		dhcpsend(Release);
193
	}
194
	qunlock(&dhcp.lk);
195
 
196
	postnote(PNGROUP, getpid(), "die");
197
	exits(0);
198
}
199
 
200
void
201
dhcpinit(void)
202
{
203
	int fd;
204
 
205
	dhcp.state = Sinit;
206
	dhcp.timeout = 4;
207
 
208
	fd = open("/dev/random", 0);
209
	if(fd >= 0) {
210
		read(fd, &dhcp.xid, sizeof(dhcp.xid));
211
		close(fd);
212
	} else
213
		dhcp.xid = time(0)*getpid();
214
	srand(dhcp.xid);
215
 
216
	sprint(dhcp.cid, "%s.%d", getenv("sysname"), getpid());
217
}
218
 
219
void
220
dhcpsend(int type)
221
{
222
	int n;
223
	uchar *p;
224
	Bootp bp;
225
	Udphdr *up;
226
 
227
	memset(&bp, 0, sizeof bp);
228
	up = (Udphdr*)bp.udphdr;
229
 
230
	hnputs(up->rport, 67);
231
	bp.op = Bootrequest;
232
	hnputl(bp.xid, dhcp.xid);
233
	hnputs(bp.secs, time(0) - dhcp.starttime);
234
	hnputs(bp.flags, Fbroadcast);		/* reply must be broadcast */
235
	memmove(bp.optmagic, optmagic, 4);
236
	p = bp.optdata;
237
	p = optaddbyte(p, ODtype, type);
238
	p = optadd(p, ODclientid, dhcp.cid, strlen(dhcp.cid));
239
	switch(type) {
240
	default:
241
		myfatal("dhcpsend: unknown message type: %d", type);
242
	case Discover:
243
		ipmove(up->raddr, IPv4bcast);	/* broadcast */
244
		break;
245
	case Request:
246
		if(dhcp.state == Sbound || dhcp.state == Srenewing)
247
			ipmove(up->raddr, dhcp.server);
248
		else
249
			ipmove(up->raddr, IPv4bcast);	/* broadcast */
250
		p = optaddulong(p, ODlease, dhcp.lease);
251
		if(dhcp.state == Sselecting || dhcp.state == Srequesting) {
252
			p = optaddaddr(p, ODipaddr, dhcp.client);	/* mistake?? */
253
			p = optaddaddr(p, ODserverid, dhcp.server);
254
		} else
255
			v6tov4(bp.ciaddr, dhcp.client);
256
		break;
257
	case Release:
258
		ipmove(up->raddr, dhcp.server);
259
		v6tov4(bp.ciaddr, dhcp.client);
260
		p = optaddaddr(p, ODipaddr, dhcp.client);
261
		p = optaddaddr(p, ODserverid, dhcp.server);
262
		break;
263
	}
264
 
265
	*p++ = OBend;
266
 
267
	n = p - (uchar*)&bp;
268
 
269
	if(write(dhcp.fd, &bp, n) != n)
270
		myfatal("dhcpsend: write failed: %r");
271
}
272
 
273
void
274
dhcprecv(void)
275
{
276
	uchar buf[2000];
277
	Bootp *bp;
278
	int n, type;
279
	ulong lease;
280
	uchar mask[IPaddrlen];
281
 
282
	qunlock(&dhcp.lk);
283
	n = read(dhcp.fd, buf, sizeof(buf));
284
	qlock(&dhcp.lk);
285
 
286
	if(n <= 0)
287
		myfatal("dhcprecv: bad read: %r");
288
 
289
	bp = parse(buf, n);
290
	if(bp == 0)
291
		return;
292
 
293
if(1) {
294
fprint(2, "recved\n");
295
bootpdump(buf, n);
296
}
297
 
298
	type = optgetbyte(bp, ODtype);
299
	switch(type) {
300
	default:
301
		fprint(2, "dhcprecv: unknown type: %d\n", type);
302
		break;
303
	case Offer:
304
		if(dhcp.state != Sselecting)
305
			break;
306
		lease = optgetulong(bp, ODlease);
307
		if(lease == 0)
308
			myfatal("bad lease");
309
		if(!optgetaddr(bp, OBmask, mask))
310
			memset(mask, 0xff, sizeof(mask));
311
		v4tov6(dhcp.client, bp->yiaddr);
312
		if(!optgetaddr(bp, ODserverid, dhcp.server)) {
313
			fprint(2, "dhcprecv: Offer from server with invalid serverid\n");
314
			break;
315
		}
316
 
317
		dhcp.lease = lease;
318
		ipmove(dhcp.mask, mask);
319
		memmove(dhcp.sname, bp->sname, sizeof(dhcp.sname));
320
		dhcp.sname[sizeof(dhcp.sname)-1] = 0;
321
 
322
		dhcpsend(Request);
323
		dhcp.state = Srequesting;
324
		dhcp.resend = 0;
325
		dhcp.timeout = 4;
326
		break;
327
	case Ack:
328
		if(dhcp.state != Srequesting)
329
		if(dhcp.state != Srenewing)
330
		if(dhcp.state != Srebinding)
331
			break;
332
		lease = optgetulong(bp, ODlease);
333
		if(lease == 0)
334
			myfatal("bad lease");
335
		if(!optgetaddr(bp, OBmask, mask))
336
			memset(mask, 0xff, sizeof(mask));
337
		v4tov6(dhcp.client, bp->yiaddr);
338
		dhcp.lease = lease;
339
		ipmove(dhcp.mask, mask);
340
		dhcp.state = Sbound;
341
		break;
342
	case Nak:
343
		myfatal("recved nak");
344
		break;
345
	}
346
 
347
}
348
 
349
int
350
openlisten(char *net)
351
{
352
	int n, fd, cfd;
353
	char data[128], devdir[40];
354
 
355
//	sprint(data, "%s/udp!*!bootpc", net);
356
	sprint(data, "%s/udp!*!68", net);
357
	for(n = 0; ; n++) {
358
		cfd = announce(data, devdir);
359
		if(cfd >= 0)
360
			break;
361
		/* might be another client - wait and try again */
362
		fprint(2, "dhcpclient: can't announce %s: %r", data);
363
		sleep(1000);
364
		if(n > 10)
365
			myfatal("can't announce: giving up: %r");
366
	}
367
 
368
	if(fprint(cfd, "headers") < 0)
369
		myfatal("can't set header mode: %r");
370
 
371
	sprint(data, "%s/data", devdir);
372
	fd = open(data, ORDWR);
373
	if(fd < 0)
374
		myfatal("open %s: %r", data);
375
	close(cfd);
376
	return fd;
377
}
378
 
379
uchar*
380
optadd(uchar *p, int op, void *d, int n)
381
{
382
	p[0] = op;
383
	p[1] = n;
384
	memmove(p+2, d, n);
385
	return p+n+2;
386
}
387
 
388
uchar*
389
optaddbyte(uchar *p, int op, int b)
390
{
391
	p[0] = op;
392
	p[1] = 1;
393
	p[2] = b;
394
	return p+3;
395
}
396
 
397
uchar*
398
optaddulong(uchar *p, int op, ulong x)
399
{
400
	p[0] = op;
401
	p[1] = 4;
402
	hnputl(p+2, x);
403
	return p+6;
404
}
405
 
406
uchar *
407
optaddaddr(uchar *p, int op, uchar *ip)
408
{
409
	p[0] = op;
410
	p[1] = 4;
411
	v6tov4(p+2, ip);
412
	return p+6;
413
}
414
 
415
uchar*
416
optget(Bootp *bp, int op, int n)
417
{
418
	int len, code;
419
	uchar *p;
420
 
421
	p = bp->optdata;
422
	for(;;) {
423
		code = *p++;
424
		if(code == OBpad)
425
			continue;
426
		if(code == OBend)
427
			return 0;
428
		len = *p++;
429
		if(code != op) {
430
			p += len;
431
			continue;
432
		}
433
		if(n && n != len)
434
			return 0;
435
		return p;
436
	}
437
}
438
 
439
 
440
int
441
optgetbyte(Bootp *bp, int op)
442
{
443
	uchar *p;
444
 
445
	p = optget(bp, op, 1);
446
	if(p == 0)
447
		return 0;
448
	return *p;
449
}
450
 
451
ulong
452
optgetulong(Bootp *bp, int op)
453
{
454
	uchar *p;
455
 
456
	p = optget(bp, op, 4);
457
	if(p == 0)
458
		return 0;
459
	return nhgetl(p);
460
}
461
 
462
int
463
optgetaddr(Bootp *bp, int op, uchar *ip)
464
{
465
	uchar *p;
466
 
467
	p = optget(bp, op, 4);
468
	if(p == 0)
469
		return 0;
470
	v4tov6(ip, p);
471
	return 1;
472
}
473
 
474
/* make sure packet looks ok */
475
Bootp *
476
parse(uchar *p, int n)
477
{
478
	int len, code;
479
	Bootp *bp;
480
 
481
	bp = (Bootp*)p;
482
	if(n < bp->optmagic - p) {
483
		fprint(2, "dhcpclient: parse: short bootp packet");
484
		return 0;
485
	}
486
 
487
	if(dhcp.xid != nhgetl(bp->xid)) {
488
		fprint(2, "dhcpclient: parse: bad xid: got %ux expected %lux\n",
489
			nhgetl(bp->xid), dhcp.xid);
490
		return 0;
491
	}
492
 
493
	if(bp->op != Bootreply) {
494
		fprint(2, "dhcpclient: parse: bad op\n");
495
		return 0;
496
	}
497
 
498
	n -= bp->optmagic - p;
499
	p = bp->optmagic;
500
 
501
	if(n < 4) {
502
		fprint(2, "dhcpclient: parse: not option data");
503
		return 0;
504
	}
505
	if(memcmp(optmagic, p, 4) != 0) {
506
		fprint(2, "dhcpclient: parse: bad opt magic %ux %ux %ux %ux\n",
507
			p[0], p[1], p[2], p[3]);
508
		return 0;
509
	}
510
	p += 4;
511
	n -= 4;
512
	while(n>0) {
513
		code = *p++;
514
		n--;
515
		if(code == OBpad)
516
			continue;
517
		if(code == OBend)
518
			return bp;
519
		if(n == 0) {
520
			fprint(2, "dhcpclient: parse: bad option: %d", code);
521
			return 0;
522
		}
523
		len = *p++;
524
		n--;
525
		if(len > n) {
526
			fprint(2, "dhcpclient: parse: bad option: %d", code);
527
			return 0;
528
		}
529
		p += len;
530
		n -= len;
531
	}
532
 
533
	/* fix up nonstandard packets */
534
	/* assume there is space */
535
	*p = OBend;
536
 
537
	return bp;
538
}
539
 
540
void
541
bootpdump(uchar *p, int n)
542
{
543
	int len, i, code;
544
	Bootp *bp;
545
	Udphdr *up;
546
 
547
	bp = (Bootp*)p;
548
	up = (Udphdr*)bp->udphdr;
549
 
550
	if(n < bp->optmagic - p) {
551
		fprint(2, "dhcpclient: short bootp packet");
552
		return;
553
	}
554
 
555
	fprint(2, "laddr=%I lport=%d raddr=%I rport=%d\n", up->laddr,
556
		nhgets(up->lport), up->raddr, nhgets(up->rport));
557
	fprint(2, "op=%d htype=%d hlen=%d hops=%d\n", bp->op, bp->htype,
558
		bp->hlen, bp->hops);
559
	fprint(2, "xid=%ux secs=%d flags=%ux\n", nhgetl(bp->xid),
560
		nhgets(bp->secs), nhgets(bp->flags));
561
	fprint(2, "ciaddr=%V yiaddr=%V siaddr=%V giaddr=%V\n",
562
		bp->ciaddr, bp->yiaddr, bp->siaddr, bp->giaddr);
563
	fprint(2, "chaddr=");
564
	for(i=0; i<16; i++)
565
		fprint(2, "%ux ", bp->chaddr[i]);
566
	fprint(2, "\n");
567
	fprint(2, "sname=%s\n", bp->sname);
568
	fprint(2, "file = %s\n", bp->file);
569
 
570
	n -= bp->optmagic - p;
571
	p = bp->optmagic;
572
 
573
	if(n < 4)
574
		return;
575
	if(memcmp(optmagic, p, 4) != 0)
576
		fprint(2, "dhcpclient: bad opt magic %ux %ux %ux %ux\n",
577
			p[0], p[1], p[2], p[3]);
578
	p += 4;
579
	n -= 4;
580
 
581
	while(n>0) {
582
		code = *p++;
583
		n--;
584
		if(code == OBpad)
585
			continue;
586
		if(code == OBend)
587
			break;
588
		if(n == 0) {
589
			fprint(2, " bad option: %d", code);
590
			return;
591
		}
592
		len = *p++;
593
		n--;
594
		if(len > n) {
595
			fprint(2, " bad option: %d", code);
596
			return;
597
		}
598
		switch(code) {
599
		default:
600
			fprint(2, "unknown option %d\n", code);
601
			for(i = 0; i<len; i++)
602
				fprint(2, "%ux ", p[i]);
603
		case ODtype:
604
			fprint(2, "DHCP type %d\n", p[0]);
605
			break;
606
		case ODclientid:
607
			fprint(2, "client id=");
608
			for(i = 0; i<len; i++)
609
				fprint(2, "%ux ", p[i]);
610
			fprint(2, "\n");
611
			break;
612
		case ODlease:
613
			fprint(2, "lease=%d\n", nhgetl(p));
614
			break;
615
		case ODserverid:
616
			fprint(2, "server id=%V\n", p);
617
			break;
618
		case OBmask:
619
			fprint(2, "mask=%V\n", p);
620
			break;
621
		case OBrouter:
622
			fprint(2, "router=%V\n", p);
623
			break;
624
		}
625
		p += len;
626
		n -= len;
627
	}
628
}
629
 
630
ulong
631
thread(void(*f)(void*), void *a)
632
{
633
	int pid;
634
 
635
	pid = rfork(RFNOWAIT|RFMEM|RFPROC);
636
	if(pid < 0)
637
		myfatal("rfork failed: %r");
638
	if(pid != 0)
639
		return pid;
640
	(*f)(a);
641
	return 0;	/* never reaches here */
642
}
643
 
644
void
645
myfatal(char *fmt, ...)
646
{
647
	char buf[1024];
648
	va_list arg;
649
 
650
	va_start(arg, fmt);
651
	vseprint(buf, buf+sizeof(buf), fmt, arg);
652
	va_end(arg);
653
	fprint(2, "%s: %s\n", argv0, buf);
654
	postnote(PNGROUP, getpid(), "die");
655
	exits(buf);
656
}