Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
/*
2
 * User-level PPP over Ethernet (PPPoE) client.
3
 * See RFC 2516
4
 */
5
 
6
#include <u.h>
7
#include <libc.h>
8
#include <ip.h>
9
 
10
void dumppkt(uchar*);
11
uchar *findtag(uchar*, int, int*, int);
12
void hexdump(uchar*, int);
13
int malformed(uchar*, int, int);
14
int pppoe(char*);
15
void execppp(int);
16
 
17
int alarmed;
18
int debug;
19
int sessid;
20
char *keyspec;
21
int primary;
22
char *pppnetmtpt;
23
char *acname;
24
char *pppname = "/bin/ip/ppp";
25
char *srvname = "";
26
char *wantac;
27
uchar *cookie;
28
int cookielen;
29
uchar etherdst[6];
30
int mtu = 1492;
31
 
32
void
33
usage(void)
34
{
35
	fprint(2, "usage: pppoe [-Pd] [-A acname] [-S srvname] [-k keyspec] [-m mtu] [-x pppnet] [ether0]\n");
36
	exits("usage");
37
}
38
 
39
int
40
catchalarm(void *a, char *msg)
41
{
42
	USED(a);
43
 
44
	if(strstr(msg, "alarm")){
45
		alarmed = 1;
46
		return 1;
47
	}
48
	if(debug)
49
		fprint(2, "note rcved: %s\n", msg);
50
	return 0;
51
}
52
 
53
void
54
main(int argc, char **argv)
55
{
56
	int fd;
57
	char *dev;
58
 
59
	ARGBEGIN{
60
	case 'A':
61
		wantac = EARGF(usage());
62
		break;
63
	case 'P':
64
		primary = 1;
65
		break;
66
	case 'S':
67
		srvname = EARGF(usage());
68
		break;
69
	case 'd':
70
		debug++;
71
		break;
72
	case 'm':
73
		mtu = atoi(EARGF(usage()));
74
		break;
75
	case 'k':
76
		keyspec = EARGF(usage());
77
		break;
78
	case 'x':
79
		pppnetmtpt = EARGF(usage());
80
		break;
81
	default:
82
		usage();
83
	}ARGEND
84
 
85
	switch(argc){
86
	default:
87
		usage();
88
	case 0:
89
		dev = "ether0";
90
		break;
91
	case 1:
92
		dev = argv[0];
93
		break;
94
	}
95
 
96
	fmtinstall('E', eipfmt);
97
 
98
	atnotify(catchalarm, 1);
99
	fd = pppoe(dev);
100
	execppp(fd);
101
}
102
 
103
typedef struct Etherhdr Etherhdr;
104
struct Etherhdr {
105
	uchar dst[6];
106
	uchar src[6];
107
	uchar type[2];
108
};
109
 
110
enum {
111
	EtherHdrSz = 6+6+2,
112
	EtherMintu = 60,
113
 
114
	EtherPppoeDiscovery = 0x8863,
115
	EtherPppoeSession = 0x8864,
116
};
117
 
118
uchar etherbcast[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
119
 
120
int
121
etherhdr(uchar *pkt, uchar *dst, int type)
122
{
123
	Etherhdr *eh;
124
 
125
	eh = (Etherhdr*)pkt;
126
	memmove(eh->dst, dst, sizeof(eh->dst));
127
	hnputs(eh->type, type);
128
	return EtherHdrSz;
129
}
130
 
131
typedef struct Pppoehdr Pppoehdr;
132
struct Pppoehdr {
133
	uchar verstype;
134
	uchar code;
135
	uchar sessid[2];
136
	uchar length[2];	/* of payload */
137
};
138
 
139
enum {
140
	PppoeHdrSz = 1+1+2+2,
141
	Hdr = EtherHdrSz+PppoeHdrSz,
142
};
143
 
144
enum {
145
	VersType = 0x11,
146
 
147
	/* Discovery codes */
148
	CodeDiscInit = 0x09,	/* discovery init */
149
	CodeDiscOffer = 0x07,	/* discovery offer */
150
	CodeDiscReq = 0x19,	/* discovery request */
151
	CodeDiscSess = 0x65,	/* session confirmation */
152
 
153
	/* Session codes */
154
	CodeSession = 0x00,
155
};
156
 
157
int
158
pppoehdr(uchar *pkt, int code, int sessid)
159
{
160
	Pppoehdr *ph;
161
 
162
	ph = (Pppoehdr*)pkt;
163
	ph->verstype = VersType;
164
	ph->code = code;
165
	hnputs(ph->sessid, sessid);
166
	return PppoeHdrSz;
167
}
168
 
169
typedef struct Taghdr Taghdr;
170
struct Taghdr {
171
	uchar type[2];
172
	uchar length[2];	/* of value */
173
};
174
 
175
enum {
176
	TagEnd = 0x0000,		/* end of tag list */
177
	TagSrvName = 0x0101,	/* service name */
178
	TagAcName = 0x0102,	/* access concentrator name */
179
	TagHostUniq = 0x0103,	/* nonce */
180
	TagAcCookie = 0x0104,	/* a.c. cookie */
181
	TagVendSpec = 0x0105,	/* vendor specific */
182
	TagRelaySessId = 0x0110,	/* relay session id */
183
	TagSrvNameErr = 0x0201,	/* service name error (ascii) */
184
	TagAcSysErr = 0x0202,	/* a.c. system error */
185
};
186
 
187
int
188
tag(uchar *pkt, int type, void *value, int nvalue)
189
{
190
	Taghdr *h;
191
 
192
	h = (Taghdr*)pkt;
193
	hnputs(h->type, type);
194
	hnputs(h->length, nvalue);
195
	memmove(pkt+4, value, nvalue);
196
	return 4+nvalue;
197
}
198
 
199
/* PPPoE Active Discovery Initiation */
200
int
201
padi(uchar *pkt)
202
{
203
	int sz, tagoff;
204
	uchar *length;
205
 
206
	sz = 0;
207
	sz += etherhdr(pkt+sz, etherbcast, EtherPppoeDiscovery);
208
	sz += pppoehdr(pkt+sz, CodeDiscInit, 0x0000);
209
	length = pkt+sz-2;
210
	tagoff = sz;
211
	sz += tag(pkt+sz, TagSrvName, srvname, strlen(srvname));
212
	hnputs(length, sz-tagoff);
213
	return sz;
214
}
215
 
216
/* PPPoE Active Discovery Request */
217
int
218
padr(uchar *pkt)
219
{
220
	int sz, tagoff;
221
	uchar *length;
222
 
223
	sz = 0;
224
	sz += etherhdr(pkt+sz, etherdst, EtherPppoeDiscovery);
225
	sz += pppoehdr(pkt+sz, CodeDiscReq, 0x0000);
226
	length = pkt+sz-2;
227
	tagoff = sz;
228
	sz += tag(pkt+sz, TagSrvName, srvname, strlen(srvname));
229
	sz += tag(pkt+sz, TagAcName, acname, strlen(acname));
230
	if(cookie)
231
		sz += tag(pkt+sz, TagAcCookie, cookie, cookielen);
232
	hnputs(length, sz-tagoff);
233
	return sz;
234
}
235
 
236
void
237
ewrite(int fd, void *buf, int nbuf)
238
{
239
	char e[ERRMAX], path[64];
240
 
241
	if(write(fd, buf, nbuf) != nbuf){
242
		rerrstr(e, sizeof e);
243
		strcpy(path, "unknown");
244
		fd2path(fd, path, sizeof path);
245
		sysfatal("write %d to %s: %s", nbuf, path, e);
246
	}
247
}
248
 
249
void*
250
emalloc(long n)
251
{
252
	void *v;
253
 
254
	v = malloc(n);
255
	if(v == nil)
256
		sysfatal("out of memory");
257
	return v;
258
}
259
 
260
int
261
aread(int timeout, int fd, void *buf, int nbuf)
262
{
263
	int n;
264
 
265
	alarmed = 0;
266
	alarm(timeout);
267
	n = read(fd, buf, nbuf);
268
	alarm(0);
269
	if(alarmed)
270
		return -1;
271
	if(n < 0)
272
		sysfatal("read: %r");
273
	if(n == 0)
274
		sysfatal("short read");
275
	return n;
276
}
277
 
278
int
279
pktread(int timeout, int fd, void *buf, int nbuf, int (*want)(uchar*))
280
{
281
	int n, t2;
282
	n = -1;
283
	for(t2=timeout; t2<16000; t2*=2){
284
		while((n = aread(t2, fd, buf, nbuf)) > 0){
285
			if(malformed(buf, n, EtherPppoeDiscovery)){
286
				if(debug)
287
					fprint(2, "dropping pkt: %r\n");
288
				continue;
289
			}
290
			if(debug)
291
				dumppkt(buf);
292
			if(!want(buf)){
293
				if(debug)
294
					fprint(2, "dropping unwanted pkt: %r\n");
295
				continue;
296
			}
297
			break;
298
		}
299
		if(n > 0)
300
			break;
301
	}
302
	return n;
303
}
304
 
305
int
306
bad(char *reason)
307
{
308
	werrstr(reason);
309
	return 0;
310
}
311
 
312
void*
313
copy(uchar *s, int len)
314
{
315
	uchar *v;
316
 
317
	v = emalloc(len+1);
318
	memmove(v, s, len);
319
	v[len] = '\0';
320
	return v;
321
}
322
 
323
void
324
clearstate(void)
325
{
326
	sessid = -1;
327
	free(acname);
328
	acname = nil;
329
	free(cookie);
330
	cookie = nil;
331
}
332
 
333
int
334
wantoffer(uchar *pkt)
335
{
336
	int i, len;
337
	uchar *s;
338
	Etherhdr *eh;
339
	Pppoehdr *ph;
340
 
341
	eh = (Etherhdr*)pkt;
342
	ph = (Pppoehdr*)(pkt+EtherHdrSz);
343
 
344
	if(ph->code != CodeDiscOffer)
345
		return bad("not an offer");
346
	if(nhgets(ph->sessid) != 0x0000)
347
		return bad("bad session id");
348
 
349
	for(i=0;; i++){
350
		if((s = findtag(pkt, TagSrvName, &len, i)) == nil)
351
			return bad("no matching service name");
352
		if(len == strlen(srvname) && memcmp(s, srvname, len) == 0)
353
			break;
354
	}
355
 
356
	if((s = findtag(pkt, TagAcName, &len, 0)) == nil)
357
		return bad("no ac name");
358
	acname = copy(s, len);
359
	if(wantac && strcmp(acname, wantac) != 0){
360
		free(acname);
361
		return bad("wrong ac name");
362
	}
363
 
364
	if(s = findtag(pkt, TagAcCookie, &len, 0)){
365
		cookie = copy(s, len);
366
		cookielen = len;
367
	}
368
	memmove(etherdst, eh->src, sizeof etherdst);
369
	return 1;
370
}
371
 
372
int
373
wantsession(uchar *pkt)
374
{
375
	int len;
376
	uchar *s;
377
	Pppoehdr *ph;
378
 
379
	ph = (Pppoehdr*)(pkt+EtherHdrSz);
380
 
381
	if(ph->code != CodeDiscSess)
382
		return bad("not a session confirmation");
383
	if(nhgets(ph->sessid) == 0x0000)
384
		return bad("bad session id");
385
	if(findtag(pkt, TagSrvName, &len, 0) == nil)
386
		return bad("no service name");
387
	if(findtag(pkt, TagSrvNameErr, &len, 0))
388
		return bad("service name error");
389
	if(findtag(pkt, TagAcSysErr, &len, 0))
390
		return bad("ac system error");
391
 
392
	/*
393
	 * rsc said: ``if there is no -S option given, the current code
394
	 * waits for an offer with service name == "".
395
	 * that's silly.  it should take the first one it gets.''
396
	 */
397
	if(srvname[0] != '\0') {
398
		if((s = findtag(pkt, TagSrvName, &len, 0)) == nil)
399
			return bad("no matching service name");
400
		if(len != strlen(srvname) || memcmp(s, srvname, len) != 0)
401
			return bad("no matching service name");
402
	}
403
	sessid = nhgets(ph->sessid);
404
	return 1;
405
}
406
 
407
int
408
pppoe(char *ether)
409
{
410
	char buf[64];
411
	uchar pkt[1520];
412
	int dfd, p[2], n, sfd, sz, timeout;
413
	Pppoehdr *ph;
414
 
415
	ph = (Pppoehdr*)(pkt+EtherHdrSz);
416
	snprint(buf, sizeof buf, "%s!%d", ether, EtherPppoeDiscovery);
417
	if((dfd = dial(buf, nil, nil, nil)) < 0)
418
		sysfatal("dial %s: %r", buf);
419
 
420
	snprint(buf, sizeof buf, "%s!%d", ether, EtherPppoeSession);
421
	if((sfd = dial(buf, nil, nil, nil)) < 0)
422
		sysfatal("dial %s: %r", buf);
423
 
424
	for(timeout=250; timeout<16000; timeout*=2){
425
		clearstate();
426
		memset(pkt, 0, sizeof pkt);
427
		sz = padi(pkt);
428
		if(debug)
429
			dumppkt(pkt);
430
		if(sz < EtherMintu)
431
			sz = EtherMintu;
432
		ewrite(dfd, pkt, sz);
433
 
434
		if(pktread(timeout, dfd, pkt, sizeof pkt, wantoffer) < 0)
435
			continue;
436
 
437
		memset(pkt, 0, sizeof pkt);
438
		sz = padr(pkt);
439
		if(debug)
440
			dumppkt(pkt);
441
		if(sz < EtherMintu)
442
			sz = EtherMintu;
443
		ewrite(dfd, pkt, sz);
444
 
445
		if(pktread(timeout, dfd, pkt, sizeof pkt, wantsession) < 0)
446
			continue;
447
 
448
		break;
449
	}
450
	if(sessid < 0)
451
		sysfatal("could not establish session");
452
 
453
	rfork(RFNOTEG);
454
	if(pipe(p) < 0)
455
		sysfatal("pipe: %r");
456
 
457
	switch(fork()){
458
	case -1:
459
		sysfatal("fork: %r");
460
	default:
461
		break;
462
	case 0:
463
		close(p[1]);
464
		while((n = read(p[0], pkt+Hdr, sizeof pkt-Hdr)) > 0){
465
			etherhdr(pkt, etherdst, EtherPppoeSession);
466
			pppoehdr(pkt+EtherHdrSz, 0x00, sessid);
467
			hnputs(pkt+Hdr-2, n);
468
			sz = Hdr+n;
469
			if(debug > 1){
470
				dumppkt(pkt);
471
				hexdump(pkt, sz);
472
			}
473
			if(sz < EtherMintu)
474
				sz = EtherMintu;
475
			if(write(sfd, pkt, sz) < 0){
476
				if(debug)
477
					fprint(2, "write to ether failed: %r");
478
				_exits(nil);
479
			}
480
		}
481
		_exits(nil);
482
	}
483
 
484
	switch(fork()){
485
	case -1:
486
		sysfatal("fork: %r");
487
	default:
488
		break;
489
	case 0:
490
		close(p[1]);
491
		while((n = read(sfd, pkt, sizeof pkt)) > 0){
492
			if(malformed(pkt, n, EtherPppoeSession)
493
			|| ph->code != 0x00 || nhgets(ph->sessid) != sessid){
494
				if(debug)
495
					fprint(2, "malformed session pkt: %r\n");
496
				if(debug)
497
					dumppkt(pkt);
498
				continue;
499
			}
500
			if(write(p[0], pkt+Hdr, nhgets(ph->length)) < 0){
501
				if(debug)
502
					fprint(2, "write to ppp failed: %r\n");
503
				_exits(nil);
504
			}
505
		}
506
		_exits(nil);
507
	}
508
	close(p[0]);
509
	return p[1];
510
}
511
 
512
void
513
execppp(int fd)
514
{
515
	char *argv[16];
516
	int argc;
517
	char smtu[10];
518
 
519
	argc = 0;
520
	argv[argc++] = pppname;
521
	snprint(smtu, sizeof(smtu), "-m%d", mtu);
522
	argv[argc++] = smtu;
523
	argv[argc++] = "-F";
524
	if(debug)
525
		argv[argc++] = "-d";
526
	if(primary)
527
		argv[argc++] = "-P";
528
	if(pppnetmtpt){
529
		argv[argc++] = "-x";
530
		argv[argc++] = pppnetmtpt;
531
	}
532
	if(keyspec){
533
		argv[argc++] = "-k";
534
		argv[argc++] = keyspec;
535
	}
536
	argv[argc] = nil;
537
 
538
	dup(fd, 0);
539
	dup(fd, 1);
540
	exec(pppname, argv);
541
	sysfatal("exec: %r");
542
}
543
 
544
uchar*
545
findtag(uchar *pkt, int tagtype, int *plen, int skip)
546
{
547
	int len, sz, totlen;
548
	uchar *tagdat, *v;
549
	Etherhdr *eh;
550
	Pppoehdr *ph;
551
	Taghdr *t;
552
 
553
	eh = (Etherhdr*)pkt;
554
	ph = (Pppoehdr*)(pkt+EtherHdrSz);
555
	tagdat = pkt+Hdr;
556
 
557
	if(nhgets(eh->type) != EtherPppoeDiscovery)
558
		return nil;
559
	totlen = nhgets(ph->length);
560
 
561
	sz = 0;
562
	while(sz+4 <= totlen){
563
		t = (Taghdr*)(tagdat+sz);
564
		v = tagdat+sz+4;
565
		len = nhgets(t->length);
566
		if(sz+4+len > totlen)
567
			break;
568
		if(nhgets(t->type) == tagtype && skip-- == 0){
569
			*plen = len;
570
			return v;
571
		}
572
		sz += 2+2+len;
573
	}
574
	return nil;	
575
}
576
 
577
void
578
dumptags(uchar *tagdat, int ntagdat)
579
{
580
	int i,len, sz;
581
	uchar *v;
582
	Taghdr *t;
583
 
584
	sz = 0;
585
	while(sz+4 <= ntagdat){
586
		t = (Taghdr*)(tagdat+sz);
587
		v = tagdat+sz+2+2;
588
		len = nhgets(t->length);
589
		if(sz+4+len > ntagdat)
590
			break;
591
		fprint(2, "\t0x%x %d: ", nhgets(t->type), len);
592
		switch(nhgets(t->type)){
593
		case TagEnd:
594
			fprint(2, "end of tag list\n");
595
			break;
596
		case TagSrvName:
597
			fprint(2, "service '%.*s'\n", len, (char*)v);
598
			break;
599
		case TagAcName:
600
			fprint(2, "ac '%.*s'\n", len, (char*)v);
601
			break;
602
		case TagHostUniq:
603
			fprint(2, "nonce ");
604
		Hex:
605
			for(i=0; i<len; i++)
606
				fprint(2, "%.2ux", v[i]);
607
			fprint(2, "\n");
608
			break;
609
		case TagAcCookie:
610
			fprint(2, "ac cookie ");
611
			goto Hex;
612
		case TagVendSpec:
613
			fprint(2, "vend spec ");
614
			goto Hex;
615
		case TagRelaySessId:
616
			fprint(2, "relay ");
617
			goto Hex;
618
		case TagSrvNameErr:
619
			fprint(2, "srverr '%.*s'\n", len, (char*)v);
620
			break;
621
		case TagAcSysErr:
622
			fprint(2, "syserr '%.*s'\n", len, (char*)v);
623
			break;
624
		}
625
		sz += 2+2+len;
626
	}
627
	if(sz != ntagdat)
628
		fprint(2, "warning: only dumped %d of %d bytes\n", sz, ntagdat);
629
}
630
 
631
void
632
dumppkt(uchar *pkt)
633
{
634
	int et;
635
	Etherhdr *eh;
636
	Pppoehdr *ph;
637
 
638
	eh = (Etherhdr*)pkt;
639
	ph = (Pppoehdr*)(pkt+EtherHdrSz);
640
	et = nhgets(eh->type);
641
 
642
	fprint(2, "%E -> %E type 0x%x\n", 
643
		eh->src, eh->dst, et);
644
	switch(et){
645
	case EtherPppoeDiscovery:
646
	case EtherPppoeSession:
647
		fprint(2, "\tvers %d type %d code 0x%x sessid 0x%x length %d\n",
648
			ph->verstype>>4, ph->verstype&15,
649
			ph->code, nhgets(ph->sessid), nhgets(ph->length));
650
		if(et == EtherPppoeDiscovery)
651
			dumptags(pkt+Hdr, nhgets(ph->length));
652
	}
653
}
654
 
655
int
656
malformed(uchar *pkt, int n, int wantet)
657
{
658
	int et;
659
	Etherhdr *eh;
660
	Pppoehdr *ph;
661
 
662
	eh = (Etherhdr*)pkt;
663
	ph = (Pppoehdr*)(pkt+EtherHdrSz);
664
 
665
	if(n < Hdr || n < Hdr+nhgets(ph->length)){
666
		werrstr("packet too short %d != %d", n, Hdr+nhgets(ph->length));
667
		return 1;
668
	}
669
 
670
	et = nhgets(eh->type);
671
	if(et != wantet){
672
		werrstr("wrong ethernet packet type 0x%x != 0x%x", et, wantet);
673
		return 1;
674
	}
675
 
676
	return 0;
677
}
678
 
679
void
680
hexdump(uchar *a, int na)
681
{
682
	int i;
683
	char buf[80];
684
 
685
	buf[0] = '\0';
686
	for(i=0; i<na; i++){
687
		sprint(buf+strlen(buf), " %.2ux", a[i]);
688
		if(i%16 == 7)
689
			sprint(buf+strlen(buf), " --");
690
		if(i%16==15){
691
			sprint(buf+strlen(buf), "\n");
692
			write(2, buf, strlen(buf));
693
			buf[0] = 0;
694
		}
695
	}
696
	if(i%16){
697
		sprint(buf+strlen(buf), "\n");
698
		write(2, buf, strlen(buf));
699
	}
700
}