Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
/*
2
 * 9boot - load next kernel via pxe (bootp, tftp) and start it
3
 *
4
 * intel says that pxe can only load into the bottom 640K,
5
 * and intel's boot agent takes 128K, leaving only 512K for 9boot.
6
 *
7
 * some of this code is from the old 9load's bootp.c.
8
 */
9
#include	"u.h"
10
#include	"../port/lib.h"
11
#include	"mem.h"
12
#include	"dat.h"
13
#include	"fns.h"
14
#include	"io.h"
15
#include	"ureg.h"
16
#include	"pool.h"
17
#include	"../port/netif.h"
18
#include	"etherif.h"
19
#include	"../ip/ip.h"
20
#include	"pxe.h"
21
 
22
#define TFTPDEF "135.104.9.6"	/* IP of default tftp server */
23
 
24
enum {
25
	Tftpusehdrs =	0,	/* flag: use announce+headers for tftp? */
26
	Debug =		0,
27
 
28
	Tftphdrsz =	4,
29
	/*
30
	 * this can be bigger than the ether mtu and
31
	 * will work due to ip fragmentation, at least on v4.
32
	 */
33
	Prefsegsize =	1400,
34
	Maxsegsize =	2048,
35
	Bufsz =		Maxsegsize + 2,
36
 
37
	Ok =		0,
38
	Err =		-1,
39
	Nonexist =	-2,
40
};
41
 
42
typedef struct Ethaddr Ethaddr;
43
typedef struct Kernname Kernname;
44
typedef struct Openeth Openeth;
45
typedef struct Tftp Tftp;
46
 
47
struct Tftp {
48
	uchar	header[Tftphdrsz];
49
	uchar	data[Maxsegsize];
50
};
51
 
52
struct Kernname {
53
	char	*edev;
54
	char	*bootfile;
55
};
56
 
57
struct Openeth {
58
	/* names */
59
	int	ctlrno;
60
	char	ethname[16];	/* ether%d */
61
	char	netethname[32];	/* /net/ether%d */
62
	char	filename[128];	/* from bootp, for tftp */
63
 
64
	Chan	*ifcctl;	/* /net/ipifc/clone */
65
	Chan	*ethctl;	/* /net/etherN/0/ctl, for promiscuous mode */
66
 
67
	/* udp connection */
68
	Chan	*udpctl;
69
	Chan	*udpdata;
70
	Pxenetaddr *netaddr;
71
	int	rxactive;
72
};
73
 
74
struct Ethaddr {		/* communication with sleep procs */
75
	Openeth	*oe;
76
	Pxenetaddr *a;
77
};
78
 
79
static char ethernm[] = "ether";
80
static uchar myea[Eaddrlen];
81
static Pxenetaddr myaddr;		/* actually, local ip addr & port */
82
 
83
/*
84
 * there can be at most one concurrent tftp session until we move these
85
 * variables into Openeth or some other struct (Tftpstate).
86
 */
87
static ushort tftpport;
88
static int tftpblockno;
89
static int tftpphase;
90
static int progress;
91
static int segsize;
92
static Tftp *tftpb;
93
static Pxenetaddr tftpserv;		/* actually, remote ip addr & port */
94
static Pxenetaddr bootpserv;
95
 
96
static int	tftpconnect(Openeth *, Bootp *);
97
 
98
uchar *
99
etheraddr(Openeth *oe)
100
{
101
	int n;
102
	char name[32], buf[32];
103
	static uchar ea[Eaddrlen];
104
 
105
	memset(ea, 0, sizeof ea);
106
	snprint(name, sizeof name, "#l%d/ether%d/addr", oe->ctlrno, oe->ctlrno);
107
	n = readfile(name, buf, sizeof buf - 1);
108
	if (n < 0)
109
		return ea;
110
	buf[n] = '\0';
111
	parseether(ea, buf);
112
	return ea;
113
}
114
 
115
static void
116
udpsend(Openeth *oe, Pxenetaddr *a, void *data, int dlen)
117
{
118
	int n;
119
	uchar *buf;
120
	Chan *c;
121
	Etherpkt pkt;
122
	Udphdr *uh;
123
 
124
	buf = data;
125
	if (dlen > sizeof pkt)
126
		panic("udpsend: packet too big");
127
 
128
	oe->netaddr = a;
129
	/*
130
	 * add Plan 9 UDP pseudo-headers
131
	 */
132
	if (!tftpphase || Tftpusehdrs) {
133
		memset(&pkt, 0, sizeof pkt);
134
		uh = (Udphdr*)&pkt;
135
		memmove(uh + 1, data, dlen);
136
		USED(buf);
137
		buf = (uchar *)uh;
138
		dlen += sizeof *uh;
139
		if (dlen > sizeof pkt)
140
			panic("udpsend: packet too big");
141
 
142
		ipmove(uh->laddr, myaddr.ip);
143
		hnputs(uh->lport, myaddr.port);
144
		ipmove(uh->raddr, a->ip);
145
		hnputs(uh->rport, a->port);
146
		if(Debug)
147
			print("udpsend %I!%d -> %I!%d ", uh->laddr,
148
				nhgets(uh->lport), uh->raddr, nhgets(uh->rport));
149
	}
150
	if (waserror()) {
151
		iprint("udp write error\n");
152
		return;			/* send another req later */
153
	}
154
	c = oe->udpdata;
155
	assert(oe->udpdata != nil);
156
	n = devtab[c->type]->write(c, buf, dlen, c->offset);
157
	poperror();
158
	c->offset += n;
159
	if (n != dlen)
160
		print("udpsend: wrote %d/%d\n", n, dlen);
161
	else if (progress)
162
		print(".");
163
}
164
 
165
static void
166
nak(Openeth *oe, Pxenetaddr *a, int code, char *msg, int report)
167
{
168
	char buf[4 + 32];
169
 
170
	buf[0] = 0;
171
	buf[1] = Tftp_ERROR;
172
	buf[2] = 0;
173
	buf[3] = code;
174
	strncpy(buf+4, msg, sizeof buf - 4 - 1);
175
	udpsend(oe, a, buf, 4 + strlen(buf+4) + 1);
176
	if(report)
177
		print("\ntftp: error(%d): %s\n", code, msg);
178
}
179
 
180
/* a is the source address we're looking for */
181
static int
182
tuplematch(Pxenetaddr *a, Udphdr *h)
183
{
184
	int port;
185
	uchar *ip;
186
 
187
	if (tftpphase && !Tftpusehdrs)
188
		return 1;
189
	/*
190
	 * we're using udp headers mode, because we're still doing bootp,
191
	 * or we are doing tftp and we chose to use headers mode.
192
	 */
193
	port = a->port;
194
	ip = a->ip;
195
	/*
196
	 * we're accepting any src port or it's from the port we want, and
197
	 * it's from the ip we want or we sent to a broadcast address, and
198
	 * it's for us or it's a broadcast.
199
	 */
200
	return (port == 0 || nhgets(h->rport) == port) &&
201
		(equivip6(h->raddr, ip) || equivip6(ip, IPv4bcast)) &&
202
		(equivip6(h->laddr, myaddr.ip) || equivip6(h->laddr, IPv4bcast));
203
}
204
 
205
/* extract UDP payload into data and set a */
206
static int
207
udppayload(Udphdr *h, int len, Pxenetaddr *a, uchar *data, int dlen)
208
{
209
	if(Debug)
210
		print("udprecv %I!%d to %I!%d...\n",
211
			h->raddr, nhgets(h->rport), h->laddr, nhgets(h->lport));
212
 
213
	if(a->port != 0 && nhgets(h->rport) != a->port) {
214
		if(Debug)
215
			print("udpport %ux not %ux\n", nhgets(h->rport), a->port);
216
		return -1;
217
	}
218
 
219
	if(!equivip6(a->ip, IPv4bcast) && !equivip6(a->ip, h->raddr)) {
220
		if(Debug)
221
			print("bad ip %I not %I\n", h->raddr, a->ip);
222
		return -1;
223
	}
224
 
225
	len -= sizeof *h;		/* don't count pseudo-headers */
226
	if(len > dlen) {
227
		print("udp packet too big: %d > %d; from addr %I\n",
228
			len, dlen, h->raddr);
229
		return -1;
230
	}
231
	memmove(data, h + 1, len);	/* skip pseudo-headers */
232
 
233
	/* set a from remote address */
234
	ipmove(a->ip, h->raddr);
235
	a->port = nhgets(h->rport);
236
	return len;
237
}
238
 
239
static int
240
chanlen(Chan *ch)
241
{
242
	int len;
243
	Dir *dp;
244
 
245
	dp = dirchstat(ch);
246
	if (dp == nil)
247
		return -1;
248
	len = dp->length;		/* qlen(cv->rq) in devip */
249
	free(dp);
250
	return len;
251
}
252
 
253
static int
254
udprecv(Openeth *oe, Pxenetaddr *a, void *data, int dlen)
255
{
256
	int len, buflen, chlen;
257
	ulong timo, now;
258
	char *buf;
259
	Chan *c;
260
	Etherpkt pkt;
261
 
262
	oe->netaddr = a;
263
	/* timo is frequency of tftp ack and broadcast bootp retransmission */
264
	if(oe->rxactive == 0)
265
		timo = 1000;
266
	else
267
		timo = Timeout;
268
	now = TK2MS(m->ticks);
269
	timo += now;			/* deadline */
270
 
271
	c = oe->udpdata;
272
	spllo();			/* paranoia */
273
	do {
274
		/*
275
		 * wait for data to arrive or time-out.
276
		 * alarms only work for user procs, so we poll to avoid getting
277
		 * stuck in ipread.
278
		 */
279
		for (chlen = chanlen(c); chlen == 0 && now < timo;
280
		     chlen = chanlen(c)) {
281
			/* briefly give somebody else a chance to run */
282
			tsleep(&up->sleep, return0, 0, 0);
283
			now = TK2MS(m->ticks);
284
		}
285
		if (chlen <= 0) {
286
			print("T");
287
			return -1;		/* timed out */
288
		}
289
 
290
		while (waserror()) {
291
			print("read err: %s\n", up->errstr);
292
			tsleep(&up->sleep, return0, 0, 1000);
293
		}
294
 
295
		/*
296
		 * using Plan 9 UDP pseudo-headers?
297
		 */
298
		if (tftpphase && !Tftpusehdrs) {
299
			buf = data;	/* read directly in caller's buffer */
300
			buflen = dlen;
301
		} else {
302
			buf = (char *)&pkt;  /* read pkt with hdrs */
303
			buflen = sizeof pkt;
304
		}
305
		/* devtab[c->type]->read calls ipread */
306
		len = devtab[c->type]->read(c, buf, buflen, c->offset);
307
		poperror();
308
 
309
		if (len <= 0)
310
			return len;
311
		c->offset += len;
312
	} while (!tuplematch(oe->netaddr, (Udphdr *)buf));
313
 
314
	/*
315
	 * using Plan 9 UDP pseudo-headers? extract payload into caller's buf.
316
	 */
317
	if (!tftpphase || Tftpusehdrs)
318
		len = udppayload((Udphdr *)&pkt, len, a, data, dlen);
319
	if (len >= 0)
320
		oe->rxactive = 1;
321
	return len;
322
}
323
 
324
static void
325
ack(Openeth *oe, Pxenetaddr *a, int blkno)
326
{
327
	char buf[4];
328
 
329
	buf[0] = 0;
330
	buf[1] = Tftp_ACK;
331
	buf[2] = blkno>>8;
332
	buf[3] = blkno;
333
	udpsend(oe, a, buf, sizeof buf);
334
}
335
 
336
static char *
337
skipwd(char *wd)
338
{
339
	while (*wd != '\0')
340
		wd++;
341
	return wd + 1;		/* skip terminating NUL */
342
}
343
 
344
static int
345
optval(char *opt, char *pkt, int len)
346
{
347
	char *wd, *ep, *p;
348
 
349
	ep = pkt + len;
350
	for (p = pkt; p < ep && *p != '\0'; p = skipwd(wd)) {
351
		wd = skipwd(p);
352
		if (cistrcmp(p, opt) == 0)
353
			return strtol(wd, 0, 10);
354
	}
355
	return -1;
356
}
357
 
358
/*
359
 * send a tftp read request to `a' for name.  if we get a data packet back,
360
 * ack it and stash it in tftp for later.
361
 *
362
 * format of a request packet, from the RFC:
363
 *
364
 *          2 bytes     string    1 byte     string   1 byte
365
 *          ------------------------------------------------
366
 *         | Opcode |  Filename  |   0  |    Mode    |   0  |
367
 *          ------------------------------------------------
368
 */
369
static int
370
tftpread1st(Openeth *oe, Pxenetaddr *a, char *name, Tftp *tftp)
371
{
372
	int i, n, len, rlen, oport, sendack;
373
	static char *buf;
374
 
375
	if (buf == nil)
376
		buf = malloc(Bufsz);
377
	buf[0] = 0;
378
	buf[1] = Tftp_READ;
379
	len = 2 + snprint(buf+2, Bufsz - 2, "%s", name) + 1;
380
	len += snprint(buf+len, Bufsz - len, "octet") + 1;
381
	len += snprint(buf+len, Bufsz - len, "blksize") + 1; /* option */
382
	len += snprint(buf+len, Bufsz - len, "%d", Prefsegsize) + 1;
383
 
384
	/*
385
	 * keep sending the same packet until we get an answer.
386
	 */
387
	if (Debug)
388
		print("tftpread1st %s\n", name);
389
	oe->netaddr = a;
390
	/*
391
	 * the first packet or two sent seem to get dropped,
392
	 * so use a shorter time-out on the first packet.
393
	 */
394
	oe->rxactive = 0;
395
	oport = a->port;
396
	tftpblockno = 0;
397
	segsize = Defsegsize;
398
	sendack = 0;
399
	for(i = 0; i < 10; i++){
400
		a->port = oport;
401
		if (sendack)
402
			ack(oe, a, tftpblockno);
403
		else
404
			udpsend(oe, a, buf, len);	/* tftp read name */
405
 
406
		if((rlen = udprecv(oe, a, tftp, sizeof(Tftp))) < Tftphdrsz)
407
			continue;		/* runt or time-out */
408
 
409
		switch((tftp->header[0]<<8)|tftp->header[1]){
410
 
411
		case Tftp_ERROR:
412
			if(strstr((char *)tftp->data, "does not exist") != nil){
413
				print("%s\n", (char*)tftp->data);
414
				return Nonexist;
415
			}
416
			print("tftpread1st: error (%d): %s\n",
417
				(tftp->header[2]<<8)|tftp->header[3], (char*)tftp->data);
418
			return Err;
419
 
420
		case Tftp_OACK:
421
			n = optval("blksize", (char *)tftp->header+2, rlen-2);
422
			if (n <= 0) {
423
				nak(oe, a, 0, "bad blksize option value", 0);
424
				return Err;
425
			}
426
			segsize = n;
427
			/* no bytes stashed in tftp.data */
428
			i = 0;
429
			sendack = 1;
430
			break;
431
 
432
		case Tftp_DATA:
433
			tftpblockno = 1;
434
			len = (tftp->header[2]<<8)|tftp->header[3];
435
			if(len != tftpblockno){
436
				print("tftpread1st: block error: %d\n", len);
437
				nak(oe, a, 1, "block error", 0);
438
				return Err;
439
			}
440
			rlen -= Tftphdrsz;
441
			if(rlen < segsize)
442
				/* ACK now, in case we don't later */
443
				ack(oe, a, tftpblockno);
444
			return rlen;
445
 
446
		default:
447
			print("tftpread1st: unexpected pkt type recv'd\n");
448
			nak(oe, a, 0, "unexpected pkt type recv'd", 0);
449
			return Err;
450
		}
451
	}
452
 
453
	print("tftpread1st: failed to connect to server (%I!%d)\n", a->ip, oport);
454
	return Err;
455
}
456
 
457
static int
458
tftpread(Openeth *oe, Pxenetaddr *a, Tftp *tftp, int dlen)
459
{
460
	int try, blockno, len;
461
 
462
	dlen += Tftphdrsz;
463
 
464
	/*
465
	 * keep sending ACKs until we get an answer.
466
	 */
467
	for(try = 0; try < 10; try++) {
468
		ack(oe, a, tftpblockno);
469
 
470
		len = udprecv(oe, a, tftp, dlen);
471
		/*
472
		 * NB: not `<='; just a header is legal and happens when
473
		 * file being read is a multiple of segsize bytes long.
474
		 */
475
		if(len < Tftphdrsz){
476
			if(Debug)
477
				print("tftpread: too short %d <= %d\n",
478
					len, Tftphdrsz);
479
			continue;
480
		}
481
		switch((tftp->header[0]<<8)|tftp->header[1]){
482
		case Tftp_ERROR:
483
			print("tftpread: error (blk %d): %s\n",
484
				(tftp->header[2]<<8)|tftp->header[3],
485
				(char*)tftp->data);
486
			nak(oe, a, 0, "error pkt recv'd", 0);
487
			return -1;
488
		case Tftp_OACK:
489
			print("tftpread: oack pkt recv'd too late\n");
490
			nak(oe, a, 0, "oack pkt recv'd too late", 0);
491
			return -1;
492
		default:
493
			print("tftpread: unexpected pkt type recv'd\n");
494
			nak(oe, a, 0, "unexpected pkt type recv'd", 0);
495
			return -1;
496
		case Tftp_DATA:
497
			break;
498
		}
499
		blockno = (tftp->header[2]<<8)|tftp->header[3];
500
		if(blockno <= tftpblockno){
501
			if(Debug)
502
				print("tftpread: blkno %d <= %d\n",
503
					blockno, tftpblockno);
504
			continue;
505
		}
506
 
507
		if(blockno == tftpblockno+1) {
508
			tftpblockno++;
509
			if(len < dlen)	/* last packet? send final ack */
510
				ack(oe, a, tftpblockno);
511
			return len-Tftphdrsz;
512
		}
513
		print("tftpread: block error: %d, expected %d\n",
514
			blockno, tftpblockno+1);
515
	}
516
 
517
	return -1;
518
}
519
 
520
/*
521
 * broadcast a bootp request for file.  stash any answer in rep.
522
 */
523
static int
524
bootpbcast(Openeth *oe, char *file, Bootp *rep)
525
{
526
	Bootp req;
527
	int i;
528
	uchar *ea;
529
	char name[128], *filename, *sysname;
530
	static char zeroes[IPaddrlen];
531
 
532
	oe->filename[0] = '\0';
533
	if (Debug)
534
		if (file == nil)
535
			print("bootpopen: %s...", oe->ethname);
536
		else
537
			print("bootpopen: %s!%s...", oe->ethname, file);
538
	if((ea = etheraddr(oe)) == nil){
539
		print("bad ether %s\n", oe->ethname);
540
		return -1;
541
	}
542
 
543
	filename = nil;
544
	sysname = 0;
545
	if(file && *file){
546
		strncpy(name, file, sizeof name);
547
		if(filename = strchr(name, '!')){
548
			sysname = name;
549
			*filename++ = 0;
550
		}
551
		else
552
			filename = name;
553
	}
554
 
555
	/*
556
	 * form a bootp request packet
557
	 */
558
	memset(&req, 0, sizeof(req));
559
	req.op = Bootrequest;
560
	req.htype = 1;			/* ethernet */
561
	req.hlen = Eaddrlen;		/* ethernet */
562
	memmove(req.chaddr, ea, Eaddrlen);
563
	req.flags[0] = 0x80;		/* request broadcast reply */
564
	if(filename != nil) {
565
		strncpy(req.file, filename, sizeof(req.file));
566
		strncpy(oe->filename, filename, sizeof oe->filename);
567
	}
568
	if(sysname != nil)		/* if server name given, supply it */
569
		strncpy(req.sname, sysname, sizeof(req.sname));
570
 
571
	if (memcmp(myaddr.ip, zeroes, sizeof myaddr.ip) == 0)
572
		ipmove(myaddr.ip, IPv4bcast);	/* didn't know my ip yet */
573
	myaddr.port = BPportsrc;
574
	memmove(myea, ea, Eaddrlen);
575
 
576
	/* send to 255.255.255.255!67 */
577
	ipmove(bootpserv.ip, IPv4bcast);
578
	bootpserv.port = BPportdst;
579
 
580
	/*
581
	 * send it until we get a matching answer
582
	 */
583
	memset(rep, 0, sizeof *rep);
584
	for(i = 10; i > 0; i--) {
585
		req.xid[0] = i;			/* try different xids */
586
		udpsend(oe, &bootpserv, &req, sizeof(req));
587
 
588
		if(udprecv(oe, &bootpserv, rep, sizeof(*rep)) <= 0)
589
			continue;
590
		if(memcmp(req.chaddr, rep->chaddr, Eaddrlen) != 0)
591
			continue;
592
		if(rep->htype != 1 || rep->hlen != Eaddrlen)
593
			continue;
594
		if(sysname == 0 || strcmp(sysname, rep->sname) == 0)
595
			break;
596
	}
597
	if(i <= 0) {
598
		if (file == nil)
599
			print("bootp on %s timed out\n", oe->ethname);
600
		else
601
			print("bootp on %s for %s timed out\n", oe->ethname, file);
602
		return -1;
603
	}
604
	return 0;
605
}
606
 
607
/*
608
 * request file via tftp from server named in rep.
609
 * initial data packet will be stashed in tftpb.
610
 */
611
static int
612
tftpopen(Openeth *oe, char *file, Bootp *rep)
613
{
614
	char *filename;
615
	char buf[128];
616
	static uchar ipv4noaddr[IPv4addrlen];
617
 
618
	if (tftpconnect(oe, rep) < 0)
619
		return Err;
620
 
621
	/*
622
	 * read file from tftp server in bootp answer
623
	 */
624
	filename = oe->filename;
625
	if (file)
626
		filename = file;
627
	if(filename == 0 || *filename == 0){
628
		if(strcmp(rep->file, "/386/9boot") == 0 ||
629
		   strcmp(rep->file, "/386/9pxeload") == 0) {
630
			print("won't load another boot loader (%s)\n", rep->file);
631
			return -1;		/* avoid infinite loop */
632
		}
633
		filename = rep->file;
634
	}
635
 
636
	print("\n");
637
	if(rep->sname[0] != '\0')
638
		print("%s ", rep->sname);
639
 
640
	v4tov6(myaddr.ip, rep->yiaddr);
641
	myaddr.port = tftpport;
642
	if (equivip4(rep->siaddr, ipv4noaddr)) { /* no server address? */
643
		getstr("tftp server IP address", buf, sizeof buf, TFTPDEF, 0);
644
		v4parseip(rep->siaddr, buf);
645
	}
646
	v4tov6(tftpserv.ip, rep->siaddr);
647
	tftpserv.port = TFTPport;
648
	if (tftpb == nil)
649
		tftpb = malloc(sizeof *tftpb);
650
 
651
	print("(%V!%d): %s ", rep->siaddr, tftpserv.port, filename);
652
 
653
	return tftpread1st(oe, &tftpserv, filename, tftpb);
654
}
655
 
656
/* load the kernel in file via tftp on oe */
657
int
658
tftpboot(Openeth *oe, char *file, Bootp *rep, Boot *b)
659
{
660
	int n;
661
 
662
	/* file must exist, else it's an error */
663
	if((n = tftpopen(oe, file, rep)) < 0)
664
		return n;
665
 
666
	progress = 0;			/* no more dots; we're on a roll now */
667
	print(" ");			/* after "sys (ip!port): kernel ..." */
668
	while(bootpass(b, tftpb->data, n) == MORE){
669
		n = tftpread(oe, &tftpserv, tftpb, segsize);
670
		if(n < segsize)
671
			break;
672
	}
673
	if(0 < n && n < segsize)	/* got to end of file */
674
		bootpass(b, tftpb->data, n);
675
	else
676
		nak(oe, &tftpserv, 3, "ok", 0);	/* tftpclose to abort transfer */
677
	bootpass(b, nil, 0);	/* boot if possible */
678
	return Err;
679
}
680
 
681
/* leave the channel to /net/ipifc/clone open */
682
static int
683
binddevip(Openeth *oe)
684
{
685
	Chan *icc;
686
	char buf[32];
687
 
688
	if (waserror()) {
689
		print("binddevip: can't bind ether %s: %s\n",
690
			oe->netethname, up->errstr);
691
		nexterror();
692
	}
693
	/* get a new ip interface */
694
	oe->ifcctl = icc = namecopen("/net/ipifc/clone", ORDWR);
695
	if(icc == nil)
696
		error("can't open /net/ipifc/clone");
697
 
698
	/*
699
	 * specify medium as ethernet, bind the interface to it.
700
	 * this should trigger chandial of types 0x800, 0x806 and 0x86dd.
701
	 */
702
	snprint(buf, sizeof buf, "bind ether %s", oe->netethname);
703
	devtab[icc->type]->write(icc, buf, strlen(buf), 0);  /* bind ether %s */
704
	poperror();
705
	return 0;
706
}
707
 
708
/* set the default route */
709
static int
710
adddefroute(char *, uchar *gaddr)
711
{
712
	char buf[64];
713
	Chan *rc;
714
 
715
	rc = nil;
716
	if (waserror()) {
717
		if (rc)
718
			cclose(rc);
719
		return -1;
720
	}
721
	rc = enamecopen("/net/iproute", ORDWR);
722
 
723
	if(isv4(gaddr))
724
		snprint(buf, sizeof buf, "add 0 0 %I", gaddr);
725
	else
726
		snprint(buf, sizeof buf, "add :: /0 %I", gaddr);
727
	devtab[rc->type]->write(rc, buf, strlen(buf), 0);
728
	poperror();
729
	cclose(rc);
730
	return 0;
731
}
732
 
733
static int
734
validip(uchar *ip)
735
{
736
	return ipcmp(ip, IPnoaddr) != 0 && ipcmp(ip, v4prefix) != 0;
737
}
738
 
739
static int
740
openetherdev(Openeth *oe)
741
{
742
	int n;
743
	char num[16];
744
	Chan *c;
745
	static char promisc[] = "promiscuous";
746
 
747
	if (chdir(oe->netethname) < 0)
748
		return -1;			/* out of ethers */
749
 
750
	oe->ethctl = nil;
751
	if (waserror()) {
752
		print("error opening /net/ether%d/0/ctl: %s\n",
753
			oe->ctlrno, up->errstr);
754
		if (oe->ethctl) {
755
			cclose(oe->ethctl);
756
			oe->ethctl = nil;
757
		}
758
		chdir("/");			/* don't hold conv. open */
759
		return -1;
760
	}
761
	oe->ethctl = c = namecopen("0/ctl", ORDWR);	/* should be ipv4 */
762
	if (c == nil) {
763
		/* read clone file to make conversation 0 since not present */
764
		oe->ethctl = c = enamecopen("clone", ORDWR);
765
		n = devtab[c->type]->read(c, num, sizeof num - 1, 0);
766
		if (n < 0)
767
			print("no %s/clone: %s\n", oe->netethname, up->errstr);
768
		else {
769
			num[n] = 0;
770
			print("%s/clone returned %s\n", oe->netethname, num);
771
		}
772
	}
773
	/* shouldn't be needed to read bootp (broadcast) reply */
774
	devtab[c->type]->write(c, promisc, sizeof promisc-1, 0);
775
	poperror();
776
	chdir("/");
777
	/* leave oe->ethctl open to keep promiscuous mode on */
778
	return 0;
779
}
780
 
781
/* add a logical interface to the ip stack */
782
int
783
minip4cfg(Openeth *oe)
784
{
785
	int n;
786
	char buf[64];
787
 
788
	n = snprint(buf, sizeof buf, "add %I", IPnoaddr);
789
	devtab[oe->ifcctl->type]->write(oe->ifcctl, buf, n, 0);	/* add %I */
790
 
791
	openetherdev(oe);
792
	return 0;
793
}
794
 
795
/* remove the :: address added by minip4cfg */
796
int
797
unminip4cfg(Openeth *oe)
798
{
799
	int n;
800
	char buf[64];
801
 
802
	n = snprint(buf, sizeof buf, "remove %I /128", IPnoaddr);
803
	if (waserror()) {
804
		print("failed write to ifc: %s: %s\n", buf, up->errstr);
805
		return -1;
806
	}
807
	devtab[oe->ifcctl->type]->write(oe->ifcctl, buf, n, 0);	/* remove %I */
808
	cclose(oe->ethctl);		/* turn promiscuous mode off */
809
	oe->ethctl = nil;
810
	poperror();
811
	return 0;
812
}
813
 
814
/*
815
 * parse p, looking for option `op'.  if non-nil, np points to minimum length.
816
 * return nil if option is too small, else ptr to opt, and
817
 * store actual length via np if non-nil.
818
 */
819
uchar*
820
optget(uchar *p, int op, int *np)
821
{
822
	int len, code;
823
 
824
	while ((code = *p++) != OBend) {
825
		if(code == OBpad)
826
			continue;
827
		len = *p++;
828
		if(code != op) {
829
			p += len;
830
			continue;
831
		}
832
		if(np != nil){
833
			if(*np > len)
834
				return 0;
835
			*np = len;
836
		}
837
		return p;
838
	}
839
	return 0;
840
}
841
 
842
int
843
optgetaddr(uchar *p, int op, uchar *ip)
844
{
845
	int len;
846
 
847
	len = 4;
848
	p = optget(p, op, &len);
849
	if(p == nil)
850
		return 0;
851
	v4tov6(ip, p);
852
	return 1;
853
}
854
 
855
int beprimary = 1;
856
 
857
/* add a logical interface to the ip stack */
858
int
859
ip4cfg(Openeth *oe, Bootp *rep)
860
{
861
	int n;
862
	uchar gaddr[IPaddrlen], v6mask[IPaddrlen];
863
	uchar v4mask[IPv4addrlen];
864
	char buf[64];
865
	static uchar zeroes[4];
866
 
867
	v4tov6(gaddr, rep->yiaddr);
868
	if(!validip(gaddr))
869
		return -1;
870
 
871
	/* dig subnet mask, if any, out of options.  if none, guess. */
872
	if(optgetaddr(rep->optdata, OBmask, v6mask)) {
873
		v6tov4(v4mask, v6mask);
874
		n = snprint(buf, sizeof buf, "add %V %M", rep->yiaddr, v4mask);
875
	} else
876
		n = snprint(buf, sizeof buf, "add %V 255.255.255.0", rep->yiaddr);
877
 
878
	devtab[oe->ifcctl->type]->write(oe->ifcctl, buf, n, 0);
879
 
880
	v4tov6(gaddr, rep->giaddr);
881
	if(beprimary==1 && validip(gaddr) && !equivip4(rep->giaddr, zeroes))
882
		adddefroute("/net", gaddr);
883
	return 0;
884
}
885
 
886
static int
887
openudp(Openeth *oe)
888
{
889
	int n;
890
	char buf[16];
891
	Chan *cc;
892
 
893
	/* read clone file for conversation number */
894
	if (waserror())
895
		panic("openudp: can't open /net/udp/clone");
896
	cc = enamecopen("/net/udp/clone", ORDWR);
897
	oe->udpctl = cc;
898
	n = devtab[cc->type]->read(cc, buf, sizeof buf - 1, 0);
899
	poperror();
900
	buf[n] = '\0';
901
	return atoi(buf);
902
}
903
 
904
static void
905
initbind(Openeth *oe)
906
{
907
	char buf[8];
908
 
909
	if (waserror()) {
910
		print("error while binding: %s\n", up->errstr);
911
		return;
912
	}
913
	snprint(buf, sizeof buf, "#I%d", oe->ctlrno);
914
	bind(buf, "/net", MAFTER);
915
	snprint(buf, sizeof buf, "#l%d", oe->ctlrno);
916
	bind(buf, "/net", MAFTER);
917
	binddevip(oe);
918
	poperror();
919
}
920
 
921
static void
922
closeudp(Openeth *oe)
923
{
924
	if (oe->udpctl) {
925
		cclose(oe->udpctl);
926
		oe->udpctl = nil;
927
	}
928
	if (oe->udpdata) {
929
		cclose(oe->udpdata);
930
		oe->udpdata = nil;
931
	}
932
}
933
 
934
static int
935
announce(Openeth *oe, char *port)
936
{
937
	int udpconv;
938
	char buf[32];
939
	static char hdrs[] = "headers";
940
 
941
	while (waserror()) {
942
		print("can't announce udp!*!%s: %s\n", port, up->errstr);
943
		closeudp(oe);
944
		nexterror();
945
	}
946
	udpconv = openudp(oe);
947
	if (udpconv < 0)
948
		panic("can't open udp conversation: %s", up->errstr);
949
 
950
	/* headers is only effective after a udp announce */
951
	snprint(buf, sizeof buf, "announce %s", port);
952
	devtab[oe->udpctl->type]->write(oe->udpctl, buf, strlen(buf), 0);
953
	devtab[oe->udpctl->type]->write(oe->udpctl, hdrs, sizeof hdrs - 1, 0);
954
	poperror();
955
 
956
	/* now okay to open the data file */
957
	snprint(buf, sizeof buf, "/net/udp/%d/data", udpconv);
958
	/*
959
	 * we must use create, not open, to get Conv->rq and ->wq
960
	 * allocated by udpcreate.
961
	 */
962
	oe->udpdata = enameccreate(buf, ORDWR);
963
	cclose(oe->udpctl);
964
	oe->udpctl = nil;
965
	return udpconv;
966
}
967
 
968
static long
969
tftprdfile(Openeth *oe, int openread, void* va, long len)
970
{
971
	int n;
972
	char *p, *v;
973
 
974
	n = openread;	/* have read this many bytes already into tftpb->data */
975
	p = v = va;
976
	len--;				/* leave room for NUL */
977
	while(n > 0) {
978
		if((p-v)+n > len)
979
			n = len - (p-v);
980
		memmove(p, tftpb->data, n);
981
		p += n;
982
		*p = 0;
983
		if(n != segsize)
984
			break;
985
 
986
		if((n = tftpread(oe, &tftpserv, tftpb, segsize)) < 0)
987
			return n;
988
	}
989
	return p-v;
990
}
991
 
992
static int
993
tftpconnect(Openeth *oe, Bootp *rep)
994
{
995
	char num[16], dialstr[64];
996
 
997
	if (waserror()) {
998
		print("can't dial: %s\n", up->errstr);
999
		return -1;
1000
	}
1001
	closeudp(oe);
1002
 
1003
	tftpphase = 1;
1004
	tftpport = 5000 + nrand(20480);
1005
	snprint(num, sizeof num, "%d", tftpport);
1006
	if (Tftpusehdrs)
1007
		announce(oe, num);
1008
	else {
1009
		snprint(dialstr, sizeof dialstr, "/net/udp!%V!%d",
1010
			rep->siaddr, TFTPport);
1011
		oe->udpdata = chandial(dialstr, num, nil, nil);
1012
		oe->udpctl = nil;
1013
	}
1014
	poperror();
1015
	return 0;
1016
}
1017
 
1018
static int
1019
setipcfg(Openeth *oe, Bootp *rep)
1020
{
1021
	int r;
1022
 
1023
	tftpphase = 0;
1024
	progress = 1;
1025
 
1026
	/* /net/iproute is unpopulated here; add at least broadcast */
1027
	minip4cfg(oe);
1028
	announce(oe, "68");
1029
	r = bootpbcast(oe, nil, rep);
1030
	closeudp(oe);
1031
	unminip4cfg(oe);
1032
	if(r < 0)
1033
		return -1;
1034
 
1035
	ip4cfg(oe, rep);
1036
	if (Debug)
1037
		print("got & set ip config\n");
1038
	return 0;
1039
}
1040
 
1041
/*
1042
 * use bootp answer (rep) to open cfgpxe.
1043
 * reads first pkt of cfgpxe into tftpb->data.
1044
 */
1045
static int
1046
rdcfgpxe(Openeth *oe, Bootp *rep, char *cfgpxe)
1047
{
1048
	int n;
1049
	char *ini;
1050
 
1051
	/* cfgpxe is optional */
1052
	n = tftpopen(oe, cfgpxe, rep);
1053
	if (n < 0)
1054
		return n;
1055
	if (Debug)
1056
		print("\opened %s\n", cfgpxe);
1057
 
1058
	ini = smalloc(2*BOOTARGSLEN);
1059
	/* starts by copying data from tftpb->data into ini */
1060
	n = tftprdfile(oe, n, ini, 2*BOOTARGSLEN);
1061
	if (n < 0) {
1062
		print("error reading %s\n", cfgpxe);
1063
		free(ini);
1064
		return n;
1065
	}
1066
	print(" read %d bytes", n);
1067
 
1068
	/*
1069
	 * take note of plan9.ini contents.  consumes ini to make config vars,
1070
	 * thus we can't free ini.
1071
	 */
1072
	dotini(ini);
1073
	return Ok;
1074
}
1075
 
1076
/*
1077
 * break kp->bootfile into kp->edev & kp->bootfile,
1078
 * copy any args for new kernel to low memory.
1079
 */
1080
static int
1081
parsebootfile(Kernname *kp)
1082
{
1083
	char *p;
1084
 
1085
	p = strchr(kp->bootfile, '!');
1086
	if (p != nil) {
1087
		*p++ = '\0';
1088
		kp->edev = kp->bootfile;
1089
		kp->bootfile = nil;
1090
		kstrdup(&kp->bootfile, p);
1091
		if (strncmp(kp->edev, ethernm, sizeof ethernm - 1) != 0) {
1092
			print("bad ether device %s\n", kp->edev);
1093
			return Err;
1094
		}
1095
	}
1096
 
1097
	/* pass any arguments to kernels that expect them */
1098
	strecpy(BOOTLINE, BOOTLINE+BOOTLINELEN, kp->bootfile);
1099
	p = strchr(kp->bootfile, ' ');
1100
	if(p != nil)
1101
		*p = '\0';
1102
	return Ok;
1103
}
1104
 
1105
static int
1106
getkernname(Openeth *oe, Bootp *rep, Kernname *kp)
1107
{
1108
	int n;
1109
	char *p;
1110
	char cfgpxe[32], buf[64];
1111
 
1112
	if (kp->bootfile) {
1113
		/* i think returning here is a bad idea */
1114
		// print("getkernname: already have bootfile %s\n",
1115
		//	kp->bootfile);
1116
		free(kp->bootfile);
1117
		// return Ok;
1118
	}
1119
	kp->edev = kp->bootfile = nil;
1120
	i8250console();		/* configure serial port with defaults */
1121
 
1122
	/* use our mac address instead of relying on a bootp answer. */
1123
	snprint(cfgpxe, sizeof cfgpxe, "/cfg/pxe/%E", myea);
1124
	n = rdcfgpxe(oe, rep, cfgpxe);
1125
	switch (n) {
1126
	case Ok:
1127
		p = getconf("bootfile");
1128
		if (p)
1129
			kstrdup(&kp->bootfile, p);
1130
		if (kp->bootfile == nil)
1131
			askbootfile(buf, sizeof buf, &kp->bootfile, Promptsecs,
1132
				"ether0!/386/9pccpu");
1133
		if (strcmp(kp->bootfile, "manual") == 0)
1134
			askbootfile(buf, sizeof buf, &kp->bootfile, 0, "");
1135
		break;
1136
	case Err:
1137
		print("\nfailed.\n");
1138
		return n;
1139
	case Nonexist:
1140
		askbootfile(buf, sizeof buf, &kp->bootfile, 0, "");
1141
		break;
1142
	}
1143
	return parsebootfile(kp);
1144
}
1145
 
1146
static void
1147
unbinddevip(Openeth *oe)
1148
{
1149
	Chan *icc;
1150
	static char unbind[] = "unbind";
1151
 
1152
	icc = oe->ifcctl;
1153
	if (icc) {
1154
		devtab[icc->type]->write(icc, unbind, sizeof unbind - 1, 0);
1155
		cclose(icc);
1156
		oe->ifcctl = nil;
1157
	}
1158
}
1159
 
1160
/*
1161
 * phase 1: get our ip (v4) configuration via bootp, set new ip configuration.
1162
 * phase 2: load /cfg/pxe, parse it, extract kernel filename.
1163
 * phase 3: load kernel and jump to it.
1164
 */
1165
static int
1166
tftpload(Openeth *oe, Kernname *kp)
1167
{
1168
	int r, n;
1169
	char buf[64];
1170
	Bootp rep;
1171
	Boot boot;
1172
 
1173
	r = -1;
1174
	if(waserror()) {
1175
		print("tftpload: %s\n", up->errstr);
1176
		closeudp(oe);
1177
		unbinddevip(oe);
1178
		return r;
1179
	}
1180
 
1181
	memset(&rep, 0, sizeof rep);
1182
	if (setipcfg(oe, &rep) < 0)
1183
		error("can't set ip config");
1184
 
1185
	n = getkernname(oe, &rep, kp);
1186
	if (n < 0) {
1187
		r = n;			/* pass reason back to caller */
1188
		USED(r);
1189
		nexterror();
1190
	}
1191
	do {
1192
		if (kp->edev &&
1193
		    oe->ctlrno != strtol(kp->edev + sizeof ethernm - 1, 0, 10)){
1194
			/* user specified an ether & it's not this one; next! */
1195
			r = Ok;
1196
			USED(r);
1197
			nexterror();
1198
		}
1199
 
1200
		memset(&boot, 0, sizeof boot);
1201
		boot.state = INITKERNEL;
1202
		r = tftpboot(oe, kp->bootfile, &rep, &boot);
1203
 
1204
		/* we failed or bootfile asked for another ether */
1205
		if (r == Nonexist)
1206
			do {
1207
				askbootfile(buf, sizeof buf, &kp->bootfile, 0, "");
1208
			} while (parsebootfile(kp) != Ok);
1209
	} while (r == Nonexist);
1210
 
1211
	poperror();
1212
	closeudp(oe);
1213
	unbinddevip(oe);
1214
	return r;
1215
}
1216
 
1217
static int
1218
etherload(int eth, Kernname *kp)
1219
{
1220
	int r;
1221
	Openeth *oe;
1222
 
1223
	print("pxe on ether%d ", eth);
1224
	oe = smalloc(sizeof *oe);
1225
	memset(oe, 0, sizeof *oe);
1226
	oe->ctlrno = eth;
1227
	snprint(oe->ethname, sizeof oe->ethname, "ether%d", oe->ctlrno);
1228
	snprint(oe->netethname, sizeof oe->netethname, "/net/ether%d",
1229
		oe->ctlrno);
1230
	initbind(oe);
1231
 
1232
	r = tftpload(oe, kp);
1233
 
1234
	/* failed to boot; keep going */
1235
	unmount(nil, "/net");
1236
	return r;
1237
}
1238
 
1239
static int
1240
attacheth(int neth)
1241
{
1242
	char num[4];
1243
	Chan *cc;
1244
 
1245
	cc = nil;
1246
	if (waserror()) {		/* no more interfaces */
1247
		if (cc)
1248
			cclose(cc);
1249
		return -1;
1250
	}
1251
	snprint(num, sizeof num, "%d", neth);
1252
	cc = etherattach(num);
1253
	if (cc)
1254
		cclose(cc);
1255
	poperror();
1256
	return cc == nil? -1: 0;
1257
}
1258
 
1259
void
1260
bootloadproc(void *)
1261
{
1262
	int eth, neth, needattach;
1263
	Kernname kernnm;
1264
 
1265
	srand(TK2MS(m->ticks));			/* for local port numbers */
1266
	nrand(20480);				/* 1st # is always 0; toss it */
1267
	kernnm.edev = kernnm.bootfile = nil;
1268
 
1269
	while(waserror()) {
1270
		print("%s\n", up->errstr);
1271
		tsleep(&up->sleep, return0, 0, 30*1000);
1272
	}
1273
	neth = MaxEther;
1274
	needattach = 1;
1275
	for (;;) {
1276
		/* try each interface in turn: first get /cfg/pxe file */
1277
		for (eth = 0; eth < neth && kernnm.edev == nil; eth++) {
1278
			if (needattach && attacheth(eth) < 0)
1279
				break;
1280
			etherload(eth, &kernnm);
1281
		}
1282
		if (needattach) {
1283
			neth = eth;
1284
			needattach = 0;
1285
			if (neth == 0)
1286
				print("no ethernet interfaces found\n");
1287
		}
1288
		if (kernnm.edev != nil) {
1289
			eth = strtol(kernnm.edev + sizeof ethernm - 1, 0, 10);
1290
			etherload(eth, &kernnm);
1291
		}
1292
		/*
1293
		 * couldn't boot on any ether.  don't give up;
1294
		 * perhaps the boot servers are down, so try again later.
1295
		 */
1296
		print("failed to boot via pxe; will try again.\n");
1297
		tsleep(&up->sleep, return0, 0, 15*1000);
1298
	}
1299
}