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