xref: /plan9/sys/src/9/pcboot/pxeload.c (revision e4575fb150652789294d651c24a881396f70018d)
125210b06SDavid du Colombier /*
225210b06SDavid du Colombier  * 9boot - load next kernel via pxe (bootp, tftp) and start it
325210b06SDavid du Colombier  *
425210b06SDavid du Colombier  * intel says that pxe can only load into the bottom 640K,
525210b06SDavid du Colombier  * and intel's boot agent takes 128K, leaving only 512K for 9boot.
625210b06SDavid du Colombier  *
725210b06SDavid du Colombier  * some of this code is from the old 9load's bootp.c.
825210b06SDavid du Colombier  */
925210b06SDavid du Colombier #include	"u.h"
1025210b06SDavid du Colombier #include	"../port/lib.h"
1125210b06SDavid du Colombier #include	"mem.h"
1225210b06SDavid du Colombier #include	"dat.h"
1325210b06SDavid du Colombier #include	"fns.h"
1425210b06SDavid du Colombier #include	"io.h"
1525210b06SDavid du Colombier #include	"ureg.h"
1625210b06SDavid du Colombier #include	"pool.h"
1725210b06SDavid du Colombier #include	"../port/netif.h"
1825210b06SDavid du Colombier #include	"../ip/ip.h"
1925210b06SDavid du Colombier #include	"pxe.h"
2025210b06SDavid du Colombier 
2125210b06SDavid du Colombier #define TFTPDEF "135.104.9.6"	/* IP of default tftp server */
2225210b06SDavid du Colombier 
2325210b06SDavid du Colombier enum {
2425210b06SDavid du Colombier 	Tftpusehdrs =	0,	/* flag: use announce+headers for tftp? */
2525210b06SDavid du Colombier 	Debug =		0,
2625210b06SDavid du Colombier 
2725210b06SDavid du Colombier 	Tftphdrsz =	4,
2825210b06SDavid du Colombier 	/*
2925210b06SDavid du Colombier 	 * this can be bigger than the ether mtu and
3025210b06SDavid du Colombier 	 * will work due to ip fragmentation, at least on v4.
3125210b06SDavid du Colombier 	 */
3225210b06SDavid du Colombier 	Prefsegsize =	1400,
3325210b06SDavid du Colombier 	Maxsegsize =	2048,
3425210b06SDavid du Colombier 	Bufsz =		Maxsegsize + 2,
3525210b06SDavid du Colombier };
3625210b06SDavid du Colombier 
3725210b06SDavid du Colombier typedef struct Ethaddr Ethaddr;
3825210b06SDavid du Colombier typedef struct Kernname Kernname;
3925210b06SDavid du Colombier typedef struct Openeth Openeth;
4025210b06SDavid du Colombier typedef struct Tftp Tftp;
4125210b06SDavid du Colombier 
4225210b06SDavid du Colombier struct Tftp {
4325210b06SDavid du Colombier 	uchar	header[Tftphdrsz];
4425210b06SDavid du Colombier 	uchar	data[Maxsegsize];
4525210b06SDavid du Colombier };
4625210b06SDavid du Colombier 
4725210b06SDavid du Colombier struct Kernname {
4825210b06SDavid du Colombier 	char	*edev;
4925210b06SDavid du Colombier 	char	*bootfile;
5025210b06SDavid du Colombier };
5125210b06SDavid du Colombier 
5225210b06SDavid du Colombier struct Openeth {
5325210b06SDavid du Colombier 	/* names */
5425210b06SDavid du Colombier 	int	ctlrno;
5525210b06SDavid du Colombier 	char	ethname[16];	/* ether%d */
5625210b06SDavid du Colombier 	char	netethname[32];	/* /net/ether%d */
5725210b06SDavid du Colombier 	char	filename[128];	/* from bootp, for tftp */
5825210b06SDavid du Colombier 
5925210b06SDavid du Colombier 	Chan	*ifcctl;	/* /net/ipifc/clone */
6025210b06SDavid du Colombier 	Chan	*ethctl;	/* /net/etherN/0/ctl, for promiscuous mode */
6125210b06SDavid du Colombier 
6225210b06SDavid du Colombier 	/* udp connection */
6325210b06SDavid du Colombier 	Chan	*udpctl;
6425210b06SDavid du Colombier 	Chan	*udpdata;
6525210b06SDavid du Colombier 	Pxenetaddr *netaddr;
6625210b06SDavid du Colombier 	int	rxactive;
6725210b06SDavid du Colombier };
6825210b06SDavid du Colombier 
6925210b06SDavid du Colombier struct Ethaddr {		/* communication with sleep procs */
7025210b06SDavid du Colombier 	Openeth	*oe;
7125210b06SDavid du Colombier 	Pxenetaddr *a;
7225210b06SDavid du Colombier };
7325210b06SDavid du Colombier 
7425210b06SDavid du Colombier static char ethernm[] = "ether";
7525210b06SDavid du Colombier 
7625210b06SDavid du Colombier /*
7725210b06SDavid du Colombier  * there can be at most one concurrent tftp session until we move these
7825210b06SDavid du Colombier  * variables into Openeth or some other struct.
7925210b06SDavid du Colombier  */
8025210b06SDavid du Colombier static ushort tftpport;
8125210b06SDavid du Colombier static int tftpblockno;
8225210b06SDavid du Colombier static int tftpphase;
8325210b06SDavid du Colombier static int progress;
8425210b06SDavid du Colombier static int segsize;
8525210b06SDavid du Colombier static Tftp *tftpb;
8625210b06SDavid du Colombier 
8725210b06SDavid du Colombier static uchar myea[Eaddrlen];
8825210b06SDavid du Colombier static Pxenetaddr myaddr;		/* actually, local ip addr & port */
8925210b06SDavid du Colombier static Pxenetaddr tftpserv;		/* actually, remote ip addr & port */
9025210b06SDavid du Colombier static Pxenetaddr bootpserv;
9125210b06SDavid du Colombier 
9225210b06SDavid du Colombier uchar *
9325210b06SDavid du Colombier etheraddr(Openeth *oe)
9425210b06SDavid du Colombier {
9525210b06SDavid du Colombier 	int n;
9625210b06SDavid du Colombier 	char name[32], buf[32];
9725210b06SDavid du Colombier 	uchar ea[Eaddrlen];
9825210b06SDavid du Colombier 
9925210b06SDavid du Colombier 	memset(ea, 0, sizeof ea);
10025210b06SDavid du Colombier 	snprint(name, sizeof name, "#l%d/ether%d/addr", oe->ctlrno, oe->ctlrno);
10125210b06SDavid du Colombier 	n = readfile(name, buf, sizeof buf - 1);
10225210b06SDavid du Colombier 	if (n < 0)
10325210b06SDavid du Colombier 		return ea;
10425210b06SDavid du Colombier 	buf[n] = '\0';
10525210b06SDavid du Colombier 	parseether(ea, buf);
10625210b06SDavid du Colombier 	return ea;
10725210b06SDavid du Colombier }
10825210b06SDavid du Colombier 
10925210b06SDavid du Colombier static void
11025210b06SDavid du Colombier udpsend(Openeth *oe, Pxenetaddr *a, void *data, int dlen)
11125210b06SDavid du Colombier {
11225210b06SDavid du Colombier 	int n;
11325210b06SDavid du Colombier 	uchar *buf;
11425210b06SDavid du Colombier 	Chan *c;
11525210b06SDavid du Colombier 	Etherpkt pkt;
11625210b06SDavid du Colombier 	Udphdr *uh;
11725210b06SDavid du Colombier 
11825210b06SDavid du Colombier 	buf = data;
11925210b06SDavid du Colombier 	if (dlen > sizeof pkt)
12025210b06SDavid du Colombier 		panic("udpsend: packet too big");
12125210b06SDavid du Colombier 
12225210b06SDavid du Colombier 	oe->netaddr = a;
12325210b06SDavid du Colombier 	/*
12425210b06SDavid du Colombier 	 * add Plan 9 UDP pseudo-headers
12525210b06SDavid du Colombier 	 */
12625210b06SDavid du Colombier 	if (!tftpphase || Tftpusehdrs) {
12725210b06SDavid du Colombier 		memset(&pkt, 0, sizeof pkt);
12825210b06SDavid du Colombier 		uh = (Udphdr*)&pkt;
12925210b06SDavid du Colombier 		memmove(uh + 1, data, dlen);
13025210b06SDavid du Colombier 		USED(buf);
13125210b06SDavid du Colombier 		buf = (uchar *)uh;
13225210b06SDavid du Colombier 		dlen += sizeof *uh;
13325210b06SDavid du Colombier 		if (dlen > sizeof pkt)
13425210b06SDavid du Colombier 			panic("udpsend: packet too big");
13525210b06SDavid du Colombier 
13625210b06SDavid du Colombier 		ipmove(uh->laddr, myaddr.ip);
13725210b06SDavid du Colombier 		hnputs(uh->lport, myaddr.port);
13825210b06SDavid du Colombier 		ipmove(uh->raddr, a->ip);
13925210b06SDavid du Colombier 		hnputs(uh->rport, a->port);
14025210b06SDavid du Colombier 		if(Debug)
14125210b06SDavid du Colombier 			print("udpsend %I!%d -> %I!%d ", uh->laddr,
14225210b06SDavid du Colombier 				nhgets(uh->lport), uh->raddr, nhgets(uh->rport));
14325210b06SDavid du Colombier 	}
14425210b06SDavid du Colombier 	if (waserror()) {
14525210b06SDavid du Colombier 		iprint("udp write error\n");
14625210b06SDavid du Colombier 		return;			/* send another req later */
14725210b06SDavid du Colombier 	}
14825210b06SDavid du Colombier 	c = oe->udpdata;
14925210b06SDavid du Colombier 	assert(oe->udpdata != nil);
15025210b06SDavid du Colombier 	n = devtab[c->type]->write(c, buf, dlen, c->offset);
15125210b06SDavid du Colombier 	poperror();
15225210b06SDavid du Colombier 	c->offset += n;
15325210b06SDavid du Colombier 	if (n != dlen)
15425210b06SDavid du Colombier 		print("udpsend: wrote %d/%d\n", n, dlen);
15525210b06SDavid du Colombier 	else if (progress)
15625210b06SDavid du Colombier 		print(".");
15725210b06SDavid du Colombier }
15825210b06SDavid du Colombier 
15925210b06SDavid du Colombier static void
16025210b06SDavid du Colombier nak(Openeth *oe, Pxenetaddr *a, int code, char *msg, int report)
16125210b06SDavid du Colombier {
16225210b06SDavid du Colombier 	char buf[4 + 32];
16325210b06SDavid du Colombier 
16425210b06SDavid du Colombier 	buf[0] = 0;
16525210b06SDavid du Colombier 	buf[1] = Tftp_ERROR;
16625210b06SDavid du Colombier 	buf[2] = 0;
16725210b06SDavid du Colombier 	buf[3] = code;
16825210b06SDavid du Colombier 	strncpy(buf+4, msg, sizeof buf - 4 - 1);
16925210b06SDavid du Colombier 	udpsend(oe, a, buf, 4 + strlen(buf+4) + 1);
17025210b06SDavid du Colombier 	if(report)
17125210b06SDavid du Colombier 		print("\ntftp: error(%d): %s\n", code, msg);
17225210b06SDavid du Colombier }
17325210b06SDavid du Colombier 
17425210b06SDavid du Colombier /* a is the source address we're looking for */
17525210b06SDavid du Colombier static int
17625210b06SDavid du Colombier tuplematch(Pxenetaddr *a, Udphdr *h)
17725210b06SDavid du Colombier {
17825210b06SDavid du Colombier 	int port;
17925210b06SDavid du Colombier 	uchar *ip;
18025210b06SDavid du Colombier 
18125210b06SDavid du Colombier 	if (tftpphase && !Tftpusehdrs)
18225210b06SDavid du Colombier 		return 1;
18325210b06SDavid du Colombier 	/*
18425210b06SDavid du Colombier 	 * we're using udp headers mode, because we're still doing bootp,
18525210b06SDavid du Colombier 	 * or we are doing tftp and we chose to use headers mode.
18625210b06SDavid du Colombier 	 */
18725210b06SDavid du Colombier 	port = a->port;
18825210b06SDavid du Colombier 	ip = a->ip;
18925210b06SDavid du Colombier 	/*
19025210b06SDavid du Colombier 	 * we're accepting any src port or it's from the port we want, and
19125210b06SDavid du Colombier 	 * it's from the ip we want or we sent to a broadcast address, and
19225210b06SDavid du Colombier 	 * it's for us or it's a broadcast.
19325210b06SDavid du Colombier 	 */
19425210b06SDavid du Colombier 	return (port == 0 || nhgets(h->rport) == port) &&
19525210b06SDavid du Colombier 		(equivip6(h->raddr, ip) || equivip6(ip, IPv4bcast)) &&
19625210b06SDavid du Colombier 		(equivip6(h->laddr, myaddr.ip) || equivip6(h->laddr, IPv4bcast));
19725210b06SDavid du Colombier }
19825210b06SDavid du Colombier 
19925210b06SDavid du Colombier /* extract UDP payload into data and set a */
20025210b06SDavid du Colombier static int
20125210b06SDavid du Colombier udppayload(Udphdr *h, int len, Pxenetaddr *a, uchar *data, int dlen)
20225210b06SDavid du Colombier {
20325210b06SDavid du Colombier 	if(Debug)
20425210b06SDavid du Colombier 		print("udprecv %I!%d to %I!%d...\n",
20525210b06SDavid du Colombier 			h->raddr, nhgets(h->rport), h->laddr, nhgets(h->lport));
20625210b06SDavid du Colombier 
20725210b06SDavid du Colombier 	if(a->port != 0 && nhgets(h->rport) != a->port) {
20825210b06SDavid du Colombier 		if(Debug)
20925210b06SDavid du Colombier 			print("udpport %ux not %ux\n", nhgets(h->rport), a->port);
21025210b06SDavid du Colombier 		return -1;
21125210b06SDavid du Colombier 	}
21225210b06SDavid du Colombier 
21325210b06SDavid du Colombier 	if(!equivip6(a->ip, IPv4bcast) && !equivip6(a->ip, h->raddr)) {
21425210b06SDavid du Colombier 		if(Debug)
21525210b06SDavid du Colombier 			print("bad ip %I not %I\n", h->raddr, a->ip);
21625210b06SDavid du Colombier 		return -1;
21725210b06SDavid du Colombier 	}
21825210b06SDavid du Colombier 
21925210b06SDavid du Colombier 	len -= sizeof *h;		/* don't count pseudo-headers */
22025210b06SDavid du Colombier 	if(len > dlen) {
22125210b06SDavid du Colombier 		print("udp packet too big: %d > %d; from addr %I\n",
22225210b06SDavid du Colombier 			len, dlen, h->raddr);
22325210b06SDavid du Colombier 		return -1;
22425210b06SDavid du Colombier 	}
22525210b06SDavid du Colombier 	memmove(data, h + 1, len);	/* skip pseudo-headers */
22625210b06SDavid du Colombier 
22725210b06SDavid du Colombier 	/* set a from remote address */
22825210b06SDavid du Colombier 	ipmove(a->ip, h->raddr);
22925210b06SDavid du Colombier 	a->port = nhgets(h->rport);
23025210b06SDavid du Colombier 	return len;
23125210b06SDavid du Colombier }
23225210b06SDavid du Colombier 
23325210b06SDavid du Colombier static int
23425210b06SDavid du Colombier chanlen(Chan *ch)
23525210b06SDavid du Colombier {
23625210b06SDavid du Colombier 	int len;
23725210b06SDavid du Colombier 	Dir *dp;
23825210b06SDavid du Colombier 
23925210b06SDavid du Colombier 	dp = dirchstat(ch);
24025210b06SDavid du Colombier 	if (dp == nil)
24125210b06SDavid du Colombier 		return -1;
24225210b06SDavid du Colombier 	len = dp->length;		/* qlen(cv->rq) in devip */
24325210b06SDavid du Colombier 	free(dp);
24425210b06SDavid du Colombier 	return len;
24525210b06SDavid du Colombier }
24625210b06SDavid du Colombier 
24725210b06SDavid du Colombier static int
24825210b06SDavid du Colombier udprecv(Openeth *oe, Pxenetaddr *a, void *data, int dlen)
24925210b06SDavid du Colombier {
25025210b06SDavid du Colombier 	int len, buflen, chlen;
25125210b06SDavid du Colombier 	ulong timo, now;
25225210b06SDavid du Colombier 	char *buf;
25325210b06SDavid du Colombier 	Chan *c;
25425210b06SDavid du Colombier 	Etherpkt pkt;
25525210b06SDavid du Colombier 
25625210b06SDavid du Colombier 	oe->netaddr = a;
25725210b06SDavid du Colombier 	/* timo is frequency of tftp ack and broadcast bootp retransmission */
25825210b06SDavid du Colombier 	if(oe->rxactive == 0)
25925210b06SDavid du Colombier 		timo = 1000;
26025210b06SDavid du Colombier 	else
26125210b06SDavid du Colombier 		timo = Timeout;
26225210b06SDavid du Colombier 	now = TK2MS(m->ticks);
26325210b06SDavid du Colombier 	timo += now;			/* deadline */
26425210b06SDavid du Colombier 
26525210b06SDavid du Colombier 	c = oe->udpdata;
26625210b06SDavid du Colombier 	spllo();			/* paranoia */
26725210b06SDavid du Colombier 	do {
26825210b06SDavid du Colombier 		/*
26925210b06SDavid du Colombier 		 * wait for data to arrive or time-out.
27025210b06SDavid du Colombier 		 * alarms only work for user procs, so we poll to avoid getting
27125210b06SDavid du Colombier 		 * stuck in ipread.
27225210b06SDavid du Colombier 		 */
27325210b06SDavid du Colombier 		for (chlen = chanlen(c); chlen == 0 && now < timo;
27425210b06SDavid du Colombier 		     chlen = chanlen(c)) {
27525210b06SDavid du Colombier 			/* briefly give somebody else a chance to run */
27625210b06SDavid du Colombier 			tsleep(&up->sleep, return0, 0, 0);
27725210b06SDavid du Colombier 			now = TK2MS(m->ticks);
27825210b06SDavid du Colombier 		}
27925210b06SDavid du Colombier 		if (chlen <= 0) {
28025210b06SDavid du Colombier 			print("T");
28125210b06SDavid du Colombier 			return -1;		/* timed out */
28225210b06SDavid du Colombier 		}
28325210b06SDavid du Colombier 
28425210b06SDavid du Colombier 		while (waserror()) {
28525210b06SDavid du Colombier 			print("read err: %s\n", up->errstr);
28625210b06SDavid du Colombier 			tsleep(&up->sleep, return0, 0, 1000);
28725210b06SDavid du Colombier 		}
28825210b06SDavid du Colombier 
28925210b06SDavid du Colombier 		/*
29025210b06SDavid du Colombier 		 * using Plan 9 UDP pseudo-headers?
29125210b06SDavid du Colombier 		 */
29225210b06SDavid du Colombier 		if (tftpphase && !Tftpusehdrs) {
29325210b06SDavid du Colombier 			buf = data;	/* read directly in caller's buffer */
29425210b06SDavid du Colombier 			buflen = dlen;
29525210b06SDavid du Colombier 		} else {
29625210b06SDavid du Colombier 			buf = (char *)&pkt;  /* read pkt with hdrs */
29725210b06SDavid du Colombier 			buflen = sizeof pkt;
29825210b06SDavid du Colombier 		}
29925210b06SDavid du Colombier 		/* devtab[c->type]->read calls ipread */
30025210b06SDavid du Colombier 		len = devtab[c->type]->read(c, buf, buflen, c->offset);
30125210b06SDavid du Colombier 		poperror();
30225210b06SDavid du Colombier 
30325210b06SDavid du Colombier 		if (len <= 0)
30425210b06SDavid du Colombier 			return len;
30525210b06SDavid du Colombier 		c->offset += len;
30625210b06SDavid du Colombier 	} while (!tuplematch(oe->netaddr, (Udphdr *)buf));
30725210b06SDavid du Colombier 
30825210b06SDavid du Colombier 	/*
30925210b06SDavid du Colombier 	 * using Plan 9 UDP pseudo-headers? extract payload into caller's buf.
31025210b06SDavid du Colombier 	 */
31125210b06SDavid du Colombier 	if (!tftpphase || Tftpusehdrs)
31225210b06SDavid du Colombier 		len = udppayload((Udphdr *)&pkt, len, a, data, dlen);
31325210b06SDavid du Colombier 	if (len >= 0)
31425210b06SDavid du Colombier 		oe->rxactive = 1;
31525210b06SDavid du Colombier 	return len;
31625210b06SDavid du Colombier }
31725210b06SDavid du Colombier 
31825210b06SDavid du Colombier static void
31925210b06SDavid du Colombier ack(Openeth *oe, Pxenetaddr *a, int blkno)
32025210b06SDavid du Colombier {
32125210b06SDavid du Colombier 	char buf[4];
32225210b06SDavid du Colombier 
32325210b06SDavid du Colombier 	buf[0] = 0;
32425210b06SDavid du Colombier 	buf[1] = Tftp_ACK;
32525210b06SDavid du Colombier 	buf[2] = blkno>>8;
32625210b06SDavid du Colombier 	buf[3] = blkno;
32725210b06SDavid du Colombier 	udpsend(oe, a, buf, sizeof buf);
32825210b06SDavid du Colombier }
32925210b06SDavid du Colombier 
33025210b06SDavid du Colombier static char *
33125210b06SDavid du Colombier skipwd(char *wd)
33225210b06SDavid du Colombier {
33325210b06SDavid du Colombier 	while (*wd != '\0')
33425210b06SDavid du Colombier 		wd++;
33525210b06SDavid du Colombier 	return wd + 1;		/* skip terminating NUL */
33625210b06SDavid du Colombier }
33725210b06SDavid du Colombier 
33825210b06SDavid du Colombier static int
33925210b06SDavid du Colombier optval(char *opt, char *pkt, int len)
34025210b06SDavid du Colombier {
34125210b06SDavid du Colombier 	char *wd, *ep, *p;
34225210b06SDavid du Colombier 
34325210b06SDavid du Colombier 	ep = pkt + len;
34425210b06SDavid du Colombier 	for (p = pkt; p < ep && *p != '\0'; p = skipwd(wd)) {
34525210b06SDavid du Colombier 		wd = skipwd(p);
34625210b06SDavid du Colombier 		if (cistrcmp(p, opt) == 0)
34725210b06SDavid du Colombier 			return strtol(wd, 0, 10);
34825210b06SDavid du Colombier 	}
34925210b06SDavid du Colombier 	return -1;
35025210b06SDavid du Colombier }
35125210b06SDavid du Colombier 
35225210b06SDavid du Colombier /*
35325210b06SDavid du Colombier  * send a tftp read request to `a' for name.  if we get a data packet back,
35425210b06SDavid du Colombier  * ack it and stash it in tftp for later.
35525210b06SDavid du Colombier  *
35625210b06SDavid du Colombier  * format of a request packet, from the RFC:
35725210b06SDavid du Colombier  *
35825210b06SDavid du Colombier  *          2 bytes     string    1 byte     string   1 byte
35925210b06SDavid du Colombier  *          ------------------------------------------------
36025210b06SDavid du Colombier  *         | Opcode |  Filename  |   0  |    Mode    |   0  |
36125210b06SDavid du Colombier  *          ------------------------------------------------
36225210b06SDavid du Colombier  */
36325210b06SDavid du Colombier static int
36425210b06SDavid du Colombier tftpread1st(Openeth *oe, Pxenetaddr *a, char *name, Tftp *tftp)
36525210b06SDavid du Colombier {
36625210b06SDavid du Colombier 	int i, n, len, rlen, oport, sendack;
36725210b06SDavid du Colombier 	static char *buf;
36825210b06SDavid du Colombier 
36925210b06SDavid du Colombier 	if (buf == nil)
37025210b06SDavid du Colombier 		buf = malloc(Bufsz);
37125210b06SDavid du Colombier 	buf[0] = 0;
37225210b06SDavid du Colombier 	buf[1] = Tftp_READ;
37325210b06SDavid du Colombier 	len = 2 + snprint(buf+2, Bufsz - 2, "%s", name) + 1;
37425210b06SDavid du Colombier 	len += snprint(buf+len, Bufsz - len, "octet") + 1;
37525210b06SDavid du Colombier 	len += snprint(buf+len, Bufsz - len, "blksize") + 1; /* option */
37625210b06SDavid du Colombier 	len += snprint(buf+len, Bufsz - len, "%d", Prefsegsize) + 1;
37725210b06SDavid du Colombier 
37825210b06SDavid du Colombier 	/*
37925210b06SDavid du Colombier 	 * keep sending the same packet until we get an answer.
38025210b06SDavid du Colombier 	 */
38125210b06SDavid du Colombier 	if (Debug)
38225210b06SDavid du Colombier 		print("tftpread1st %s\n", name);
38325210b06SDavid du Colombier 	oe->netaddr = a;
38425210b06SDavid du Colombier 	/*
38525210b06SDavid du Colombier 	 * the first packet or two sent seem to get dropped,
38625210b06SDavid du Colombier 	 * so use a shorter time-out on the first packet.
38725210b06SDavid du Colombier 	 */
38825210b06SDavid du Colombier 	oe->rxactive = 0;
38925210b06SDavid du Colombier 	oport = a->port;
39025210b06SDavid du Colombier 	tftpblockno = 0;
39125210b06SDavid du Colombier 	segsize = Defsegsize;
39225210b06SDavid du Colombier 	sendack = 0;
39325210b06SDavid du Colombier 	for(i = 0; i < 10; i++){
39425210b06SDavid du Colombier 		a->port = oport;
39525210b06SDavid du Colombier 		if (sendack)
39625210b06SDavid du Colombier 			ack(oe, a, tftpblockno);
39725210b06SDavid du Colombier 		else
39825210b06SDavid du Colombier 			udpsend(oe, a, buf, len);	/* tftp read name */
39925210b06SDavid du Colombier 
40025210b06SDavid du Colombier 		if((rlen = udprecv(oe, a, tftp, sizeof(Tftp))) < Tftphdrsz)
40125210b06SDavid du Colombier 			continue;		/* runt or time-out */
40225210b06SDavid du Colombier 
40325210b06SDavid du Colombier 		switch((tftp->header[0]<<8)|tftp->header[1]){
40425210b06SDavid du Colombier 
40525210b06SDavid du Colombier 		case Tftp_ERROR:
40625210b06SDavid du Colombier 			print("tftpread1st: error (%d): %s\n",
40725210b06SDavid du Colombier 				(tftp->header[2]<<8)|tftp->header[3], (char*)tftp->data);
40825210b06SDavid du Colombier 			return -1;
40925210b06SDavid du Colombier 
41025210b06SDavid du Colombier 		case Tftp_OACK:
41125210b06SDavid du Colombier 			n = optval("blksize", (char *)tftp->header+2, rlen-2);
41225210b06SDavid du Colombier 			if (n <= 0) {
41325210b06SDavid du Colombier 				nak(oe, a, 0, "bad blksize option value", 0);
41425210b06SDavid du Colombier 				return -1;
41525210b06SDavid du Colombier 			}
41625210b06SDavid du Colombier 			segsize = n;
41725210b06SDavid du Colombier 			/* no bytes stashed in tftp.data */
41825210b06SDavid du Colombier 			i = 0;
41925210b06SDavid du Colombier 			sendack = 1;
42025210b06SDavid du Colombier 			break;
42125210b06SDavid du Colombier 
42225210b06SDavid du Colombier 		case Tftp_DATA:
42325210b06SDavid du Colombier 			tftpblockno = 1;
42425210b06SDavid du Colombier 			len = (tftp->header[2]<<8)|tftp->header[3];
42525210b06SDavid du Colombier 			if(len != tftpblockno){
42625210b06SDavid du Colombier 				print("tftpread1st: block error: %d\n", len);
42725210b06SDavid du Colombier 				nak(oe, a, 1, "block error", 0);
42825210b06SDavid du Colombier 				return -1;
42925210b06SDavid du Colombier 			}
43025210b06SDavid du Colombier 			rlen -= Tftphdrsz;
43125210b06SDavid du Colombier 			if(rlen < segsize)
43225210b06SDavid du Colombier 				/* ACK now, in case we don't later */
43325210b06SDavid du Colombier 				ack(oe, a, tftpblockno);
43425210b06SDavid du Colombier 			return rlen;
43525210b06SDavid du Colombier 
43625210b06SDavid du Colombier 		default:
43725210b06SDavid du Colombier 			print("tftpread1st: unexpected pkt type recv'd\n");
43825210b06SDavid du Colombier 			nak(oe, a, 0, "unexpected pkt type recv'd", 0);
43925210b06SDavid du Colombier 			return -1;
44025210b06SDavid du Colombier 		}
44125210b06SDavid du Colombier 	}
44225210b06SDavid du Colombier 
44325210b06SDavid du Colombier 	print("tftpread1st: failed to connect to server (%I!%d)\n", a->ip, oport);
44425210b06SDavid du Colombier 	return -1;
44525210b06SDavid du Colombier }
44625210b06SDavid du Colombier 
44725210b06SDavid du Colombier static int
44825210b06SDavid du Colombier tftpread(Openeth *oe, Pxenetaddr *a, Tftp *tftp, int dlen)
44925210b06SDavid du Colombier {
45025210b06SDavid du Colombier 	int try, blockno, len;
45125210b06SDavid du Colombier 
45225210b06SDavid du Colombier 	dlen += Tftphdrsz;
45325210b06SDavid du Colombier 
45425210b06SDavid du Colombier 	/*
45525210b06SDavid du Colombier 	 * keep sending ACKs until we get an answer.
45625210b06SDavid du Colombier 	 */
45725210b06SDavid du Colombier 	for(try = 0; try < 10; try++) {
45825210b06SDavid du Colombier 		ack(oe, a, tftpblockno);
45925210b06SDavid du Colombier 
46025210b06SDavid du Colombier 		len = udprecv(oe, a, tftp, dlen);
46125210b06SDavid du Colombier 		/*
46225210b06SDavid du Colombier 		 * NB: not `<='; just a header is legal and happens when
46325210b06SDavid du Colombier 		 * file being read is a multiple of segsize bytes long.
46425210b06SDavid du Colombier 		 */
46525210b06SDavid du Colombier 		if(len < Tftphdrsz){
46625210b06SDavid du Colombier 			if(Debug)
46725210b06SDavid du Colombier 				print("tftpread: too short %d <= %d\n",
46825210b06SDavid du Colombier 					len, Tftphdrsz);
46925210b06SDavid du Colombier 			continue;
47025210b06SDavid du Colombier 		}
47125210b06SDavid du Colombier 		switch((tftp->header[0]<<8)|tftp->header[1]){
47225210b06SDavid du Colombier 		case Tftp_ERROR:
47325210b06SDavid du Colombier 			print("tftpread: error (blk %d): %s\n",
47425210b06SDavid du Colombier 				(tftp->header[2]<<8)|tftp->header[3],
47525210b06SDavid du Colombier 				(char*)tftp->data);
47625210b06SDavid du Colombier 			nak(oe, a, 0, "error pkt recv'd", 0);
47725210b06SDavid du Colombier 			return -1;
47825210b06SDavid du Colombier 		case Tftp_OACK:
47925210b06SDavid du Colombier 			print("tftpread: oack pkt recv'd too late\n");
48025210b06SDavid du Colombier 			nak(oe, a, 0, "oack pkt recv'd too late", 0);
48125210b06SDavid du Colombier 			return -1;
48225210b06SDavid du Colombier 		default:
48325210b06SDavid du Colombier 			print("tftpread: unexpected pkt type recv'd\n");
48425210b06SDavid du Colombier 			nak(oe, a, 0, "unexpected pkt type recv'd", 0);
48525210b06SDavid du Colombier 			return -1;
48625210b06SDavid du Colombier 		case Tftp_DATA:
48725210b06SDavid du Colombier 			break;
48825210b06SDavid du Colombier 		}
48925210b06SDavid du Colombier 		blockno = (tftp->header[2]<<8)|tftp->header[3];
49025210b06SDavid du Colombier 		if(blockno <= tftpblockno){
49125210b06SDavid du Colombier 			if(Debug)
49225210b06SDavid du Colombier 				print("tftpread: blkno %d <= %d\n",
49325210b06SDavid du Colombier 					blockno, tftpblockno);
49425210b06SDavid du Colombier 			continue;
49525210b06SDavid du Colombier 		}
49625210b06SDavid du Colombier 
49725210b06SDavid du Colombier 		if(blockno == tftpblockno+1) {
49825210b06SDavid du Colombier 			tftpblockno++;
49925210b06SDavid du Colombier 			if(len < dlen)	/* last packet? send final ack */
50025210b06SDavid du Colombier 				ack(oe, a, tftpblockno);
50125210b06SDavid du Colombier 			return len-Tftphdrsz;
50225210b06SDavid du Colombier 		}
50325210b06SDavid du Colombier 		print("tftpread: block error: %d, expected %d\n",
50425210b06SDavid du Colombier 			blockno, tftpblockno+1);
50525210b06SDavid du Colombier 	}
50625210b06SDavid du Colombier 
50725210b06SDavid du Colombier 	return -1;
50825210b06SDavid du Colombier }
50925210b06SDavid du Colombier 
51025210b06SDavid du Colombier /*
51125210b06SDavid du Colombier  * broadcast a bootp request for file.  stash any answer in rep.
51225210b06SDavid du Colombier  */
51325210b06SDavid du Colombier static int
51425210b06SDavid du Colombier bootpbcast(Openeth *oe, char *file, Bootp *rep)
51525210b06SDavid du Colombier {
51625210b06SDavid du Colombier 	Bootp req;
51725210b06SDavid du Colombier 	int i;
51825210b06SDavid du Colombier 	uchar *ea;
51925210b06SDavid du Colombier 	char name[128], *filename, *sysname;
52025210b06SDavid du Colombier 	static char zeroes[IPaddrlen];
52125210b06SDavid du Colombier 
52225210b06SDavid du Colombier 	oe->filename[0] = '\0';
52325210b06SDavid du Colombier 	if (Debug)
52425210b06SDavid du Colombier 		if (file == nil)
52525210b06SDavid du Colombier 			print("bootpopen: %s...", oe->ethname);
52625210b06SDavid du Colombier 		else
52725210b06SDavid du Colombier 			print("bootpopen: %s!%s...", oe->ethname, file);
52825210b06SDavid du Colombier 	if((ea = etheraddr(oe)) == nil){
52925210b06SDavid du Colombier 		print("bad ether %s\n", oe->ethname);
53025210b06SDavid du Colombier 		return -1;
53125210b06SDavid du Colombier 	}
53225210b06SDavid du Colombier 
53325210b06SDavid du Colombier 	filename = nil;
53425210b06SDavid du Colombier 	sysname = 0;
53525210b06SDavid du Colombier 	if(file && *file){
53625210b06SDavid du Colombier 		strncpy(name, file, sizeof name);
53725210b06SDavid du Colombier 		if(filename = strchr(name, '!')){
53825210b06SDavid du Colombier 			sysname = name;
53925210b06SDavid du Colombier 			*filename++ = 0;
54025210b06SDavid du Colombier 		}
54125210b06SDavid du Colombier 		else
54225210b06SDavid du Colombier 			filename = name;
54325210b06SDavid du Colombier 	}
54425210b06SDavid du Colombier 
54525210b06SDavid du Colombier 	/*
54625210b06SDavid du Colombier 	 * form a bootp request packet
54725210b06SDavid du Colombier 	 */
54825210b06SDavid du Colombier 	memset(&req, 0, sizeof(req));
54925210b06SDavid du Colombier 	req.op = Bootrequest;
55025210b06SDavid du Colombier 	req.htype = 1;			/* ethernet */
55125210b06SDavid du Colombier 	req.hlen = Eaddrlen;		/* ethernet */
55225210b06SDavid du Colombier 	memmove(req.chaddr, ea, Eaddrlen);
55325210b06SDavid du Colombier 	req.flags[0] = 0x80;		/* request broadcast reply */
55425210b06SDavid du Colombier 	if(filename != nil) {
55525210b06SDavid du Colombier 		strncpy(req.file, filename, sizeof(req.file));
55625210b06SDavid du Colombier 		strncpy(oe->filename, filename, sizeof oe->filename);
55725210b06SDavid du Colombier 	}
55825210b06SDavid du Colombier 	if(sysname != nil)		/* if server name given, supply it */
55925210b06SDavid du Colombier 		strncpy(req.sname, sysname, sizeof(req.sname));
56025210b06SDavid du Colombier 
56125210b06SDavid du Colombier 	if (memcmp(myaddr.ip, zeroes, sizeof myaddr.ip) == 0)
56225210b06SDavid du Colombier 		ipmove(myaddr.ip, IPv4bcast);	/* didn't know my ip yet */
56325210b06SDavid du Colombier 	myaddr.port = BPportsrc;
56425210b06SDavid du Colombier 	memmove(myea, ea, Eaddrlen);
56525210b06SDavid du Colombier 
56625210b06SDavid du Colombier 	/* send to 255.255.255.255!67 */
56725210b06SDavid du Colombier 	ipmove(bootpserv.ip, IPv4bcast);
56825210b06SDavid du Colombier 	bootpserv.port = BPportdst;
56925210b06SDavid du Colombier 
57025210b06SDavid du Colombier 	/*
57125210b06SDavid du Colombier 	 * send it until we get a matching answer
57225210b06SDavid du Colombier 	 */
57325210b06SDavid du Colombier 	memset(rep, 0, sizeof *rep);
57425210b06SDavid du Colombier 	for(i = 10; i > 0; i--) {
57525210b06SDavid du Colombier 		req.xid[0] = i;			/* try different xids */
57625210b06SDavid du Colombier 		udpsend(oe, &bootpserv, &req, sizeof(req));
57725210b06SDavid du Colombier 
57825210b06SDavid du Colombier 		if(udprecv(oe, &bootpserv, rep, sizeof(*rep)) <= 0)
57925210b06SDavid du Colombier 			continue;
58025210b06SDavid du Colombier 		if(memcmp(req.chaddr, rep->chaddr, Eaddrlen) != 0)
58125210b06SDavid du Colombier 			continue;
58225210b06SDavid du Colombier 		if(rep->htype != 1 || rep->hlen != Eaddrlen)
58325210b06SDavid du Colombier 			continue;
58425210b06SDavid du Colombier 		if(sysname == 0 || strcmp(sysname, rep->sname) == 0)
58525210b06SDavid du Colombier 			break;
58625210b06SDavid du Colombier 	}
58725210b06SDavid du Colombier 	if(i <= 0) {
58825210b06SDavid du Colombier 		if (file == nil)
58925210b06SDavid du Colombier 			print("bootp on %s timed out\n", oe->ethname);
59025210b06SDavid du Colombier 		else
59125210b06SDavid du Colombier 			print("bootp on %s for %s timed out\n", oe->ethname, file);
59225210b06SDavid du Colombier 		return -1;
59325210b06SDavid du Colombier 	}
59425210b06SDavid du Colombier 	return 0;
59525210b06SDavid du Colombier }
59625210b06SDavid du Colombier 
59725210b06SDavid du Colombier /*
59825210b06SDavid du Colombier  * request file via tftp from server named in rep.
59925210b06SDavid du Colombier  * initial data packet will be stashed in tftpb.
60025210b06SDavid du Colombier  */
60125210b06SDavid du Colombier static int
60225210b06SDavid du Colombier tftpopen(Openeth *oe, char *file, Bootp *rep)
60325210b06SDavid du Colombier {
60425210b06SDavid du Colombier 	char *filename;
60525210b06SDavid du Colombier 	char buf[128];
60625210b06SDavid du Colombier 	static uchar ipv4noaddr[IPv4addrlen];
60725210b06SDavid du Colombier 
60825210b06SDavid du Colombier 	/*
60925210b06SDavid du Colombier 	 * read file from tftp server in bootp answer
61025210b06SDavid du Colombier 	 */
61125210b06SDavid du Colombier 	filename = oe->filename;
61225210b06SDavid du Colombier 	if (file)
61325210b06SDavid du Colombier 		filename = file;
61425210b06SDavid du Colombier 	if(filename == 0 || *filename == 0){
61525210b06SDavid du Colombier 		if(strcmp(rep->file, "/386/9boot") == 0 ||
61625210b06SDavid du Colombier 		   strcmp(rep->file, "/386/9pxeload") == 0) {
61725210b06SDavid du Colombier 			print("won't load another boot loader (%s)\n", rep->file);
61825210b06SDavid du Colombier 			return -1;		/* avoid infinite loop */
61925210b06SDavid du Colombier 		}
62025210b06SDavid du Colombier 		filename = rep->file;
62125210b06SDavid du Colombier 	}
62225210b06SDavid du Colombier 
62325210b06SDavid du Colombier 	print("\n");
62425210b06SDavid du Colombier 	if(rep->sname[0] != '\0')
62525210b06SDavid du Colombier 		print("%s ", rep->sname);
62625210b06SDavid du Colombier 
62725210b06SDavid du Colombier 	v4tov6(myaddr.ip, rep->yiaddr);
62825210b06SDavid du Colombier 	myaddr.port = tftpport;
62925210b06SDavid du Colombier 	if (equivip4(rep->siaddr, ipv4noaddr)) { /* no server address? */
63025210b06SDavid du Colombier 		getstr("tftp server IP address", buf, sizeof buf, TFTPDEF, 0);
63125210b06SDavid du Colombier 		v4parseip(rep->siaddr, buf);
63225210b06SDavid du Colombier 	}
63325210b06SDavid du Colombier 	v4tov6(tftpserv.ip, rep->siaddr);
63425210b06SDavid du Colombier 	tftpserv.port = TFTPport;
63525210b06SDavid du Colombier 	if (tftpb == nil)
63625210b06SDavid du Colombier 		tftpb = malloc(sizeof *tftpb);
63725210b06SDavid du Colombier 
63825210b06SDavid du Colombier 	print("(%V!%d): %s ", rep->siaddr, tftpserv.port, filename);
63925210b06SDavid du Colombier 
64025210b06SDavid du Colombier 	return tftpread1st(oe, &tftpserv, filename, tftpb);
64125210b06SDavid du Colombier }
64225210b06SDavid du Colombier 
64325210b06SDavid du Colombier int
64425210b06SDavid du Colombier tftpboot(Openeth *oe, char *file, Bootp *rep, Boot *b)
64525210b06SDavid du Colombier {
64625210b06SDavid du Colombier 	int n;
64725210b06SDavid du Colombier 
64825210b06SDavid du Colombier 	if((n = tftpopen(oe, file, rep)) < 0)
64925210b06SDavid du Colombier 		return -1;
65025210b06SDavid du Colombier 
65125210b06SDavid du Colombier 	progress = 0;			/* no more dots; we're on a roll now */
65225210b06SDavid du Colombier 	print(" ");			/* after "sys (ip!port): kernel ..." */
65325210b06SDavid du Colombier 	while(bootpass(b, tftpb->data, n) == MORE){
65425210b06SDavid du Colombier 		n = tftpread(oe, &tftpserv, tftpb, segsize);
65525210b06SDavid du Colombier 		if(n < segsize)
65625210b06SDavid du Colombier 			break;
65725210b06SDavid du Colombier 	}
65825210b06SDavid du Colombier 	if(0 < n && n < segsize)	/* got to end of file */
65925210b06SDavid du Colombier 		bootpass(b, tftpb->data, n);
66025210b06SDavid du Colombier 	else
66125210b06SDavid du Colombier 		nak(oe, &tftpserv, 3, "ok", 0);	/* tftpclose to abort transfer */
66225210b06SDavid du Colombier 	bootpass(b, nil, 0);	/* boot if possible */
66325210b06SDavid du Colombier 	return -1;
66425210b06SDavid du Colombier }
66525210b06SDavid du Colombier 
66625210b06SDavid du Colombier /* leave the channel to /net/ipifc/clone open */
66725210b06SDavid du Colombier static int
66825210b06SDavid du Colombier binddevip(Openeth *oe)
66925210b06SDavid du Colombier {
67025210b06SDavid du Colombier 	Chan *icc;
67125210b06SDavid du Colombier 	char buf[32];
67225210b06SDavid du Colombier 
67325210b06SDavid du Colombier 	if (waserror()) {
67425210b06SDavid du Colombier 		print("binddevip: can't bind ether %s: %s\n",
67525210b06SDavid du Colombier 			oe->netethname, up->errstr);
67625210b06SDavid du Colombier 		nexterror();
67725210b06SDavid du Colombier 	}
67825210b06SDavid du Colombier 	/* get a new ip interface */
67925210b06SDavid du Colombier 	oe->ifcctl = icc = namecopen("/net/ipifc/clone", ORDWR);
68025210b06SDavid du Colombier 	if(icc == nil)
68125210b06SDavid du Colombier 		error("can't open /net/ipifc/clone");
68225210b06SDavid du Colombier 
68325210b06SDavid du Colombier 	/*
68425210b06SDavid du Colombier 	 * specify medium as ethernet, bind the interface to it.
68525210b06SDavid du Colombier 	 * this should trigger chandial of types 0x800, 0x806 and 0x86dd.
68625210b06SDavid du Colombier 	 */
68725210b06SDavid du Colombier 	snprint(buf, sizeof buf, "bind ether %s", oe->netethname);
68825210b06SDavid du Colombier 	devtab[icc->type]->write(icc, buf, strlen(buf), 0);  /* bind ether %s */
68925210b06SDavid du Colombier 	poperror();
69025210b06SDavid du Colombier 	return 0;
69125210b06SDavid du Colombier }
69225210b06SDavid du Colombier 
69325210b06SDavid du Colombier /* set the default route */
69425210b06SDavid du Colombier static int
69525210b06SDavid du Colombier adddefroute(char *, uchar *gaddr)
69625210b06SDavid du Colombier {
69725210b06SDavid du Colombier 	char buf[64];
69825210b06SDavid du Colombier 	Chan *rc;
69925210b06SDavid du Colombier 
70025210b06SDavid du Colombier 	rc = nil;
70125210b06SDavid du Colombier 	if (waserror()) {
70225210b06SDavid du Colombier 		if (rc)
70325210b06SDavid du Colombier 			cclose(rc);
70425210b06SDavid du Colombier 		return -1;
70525210b06SDavid du Colombier 	}
70625210b06SDavid du Colombier 	rc = enamecopen("/net/iproute", ORDWR);
70725210b06SDavid du Colombier 
70825210b06SDavid du Colombier 	if(isv4(gaddr))
70925210b06SDavid du Colombier 		snprint(buf, sizeof buf, "add 0 0 %I", gaddr);
71025210b06SDavid du Colombier 	else
71125210b06SDavid du Colombier 		snprint(buf, sizeof buf, "add :: /0 %I", gaddr);
71225210b06SDavid du Colombier 	devtab[rc->type]->write(rc, buf, strlen(buf), 0);
71325210b06SDavid du Colombier 	poperror();
71425210b06SDavid du Colombier 	cclose(rc);
71525210b06SDavid du Colombier 	return 0;
71625210b06SDavid du Colombier }
71725210b06SDavid du Colombier 
71825210b06SDavid du Colombier static int
71925210b06SDavid du Colombier validip(uchar *ip)
72025210b06SDavid du Colombier {
72125210b06SDavid du Colombier 	return ipcmp(ip, IPnoaddr) != 0 && ipcmp(ip, v4prefix) != 0;
72225210b06SDavid du Colombier }
72325210b06SDavid du Colombier 
72425210b06SDavid du Colombier static int
72525210b06SDavid du Colombier openetherdev(Openeth *oe)
72625210b06SDavid du Colombier {
72725210b06SDavid du Colombier 	int n;
72825210b06SDavid du Colombier 	char num[16];
72925210b06SDavid du Colombier 	Chan *c;
73025210b06SDavid du Colombier 	static char promisc[] = "promiscuous";
73125210b06SDavid du Colombier 
73225210b06SDavid du Colombier 	if (chdir(oe->netethname) < 0)
73325210b06SDavid du Colombier 		return -1;			/* out of ethers */
73425210b06SDavid du Colombier 
73525210b06SDavid du Colombier 	oe->ethctl = nil;
73625210b06SDavid du Colombier 	if (waserror()) {
73725210b06SDavid du Colombier 		print("error opening /net/ether%d/0/ctl: %s\n",
73825210b06SDavid du Colombier 			oe->ctlrno, up->errstr);
73925210b06SDavid du Colombier 		if (oe->ethctl) {
74025210b06SDavid du Colombier 			cclose(oe->ethctl);
74125210b06SDavid du Colombier 			oe->ethctl = nil;
74225210b06SDavid du Colombier 		}
74325210b06SDavid du Colombier 		chdir("/");			/* don't hold conv. open */
74425210b06SDavid du Colombier 		return -1;
74525210b06SDavid du Colombier 	}
74625210b06SDavid du Colombier 	oe->ethctl = c = namecopen("0/ctl", ORDWR);	/* should be ipv4 */
74725210b06SDavid du Colombier 	if (c == nil) {
74825210b06SDavid du Colombier 		/* read clone file to make conversation 0 since not present */
74925210b06SDavid du Colombier 		oe->ethctl = c = enamecopen("clone", ORDWR);
75025210b06SDavid du Colombier 		n = devtab[c->type]->read(c, num, sizeof num - 1, 0);
75125210b06SDavid du Colombier 		if (n < 0)
75225210b06SDavid du Colombier 			print("no %s/clone: %s\n", oe->netethname, up->errstr);
75325210b06SDavid du Colombier 		else {
75425210b06SDavid du Colombier 			num[n] = 0;
75525210b06SDavid du Colombier 			print("%s/clone returned %s\n", oe->netethname, num);
75625210b06SDavid du Colombier 		}
75725210b06SDavid du Colombier 	}
75825210b06SDavid du Colombier 	/* shouldn't be needed to read bootp (broadcast) reply */
75925210b06SDavid du Colombier 	devtab[c->type]->write(c, promisc, sizeof promisc-1, 0);
76025210b06SDavid du Colombier 	poperror();
76125210b06SDavid du Colombier 	chdir("/");
76225210b06SDavid du Colombier 	/* leave oe->ethctl open to keep promiscuous mode on */
76325210b06SDavid du Colombier 	return 0;
76425210b06SDavid du Colombier }
76525210b06SDavid du Colombier 
76625210b06SDavid du Colombier /* add a logical interface to the ip stack */
76725210b06SDavid du Colombier int
76825210b06SDavid du Colombier minip4cfg(Openeth *oe)
76925210b06SDavid du Colombier {
77025210b06SDavid du Colombier 	int n;
77125210b06SDavid du Colombier 	char buf[64];
77225210b06SDavid du Colombier 
77325210b06SDavid du Colombier 	n = snprint(buf, sizeof buf, "add %I", IPnoaddr);
77425210b06SDavid du Colombier 	devtab[oe->ifcctl->type]->write(oe->ifcctl, buf, n, 0);	/* add %I */
77525210b06SDavid du Colombier 
77625210b06SDavid du Colombier 	openetherdev(oe);
77725210b06SDavid du Colombier 	return 0;
77825210b06SDavid du Colombier }
77925210b06SDavid du Colombier 
78025210b06SDavid du Colombier /* remove the :: address added by minip4cfg */
78125210b06SDavid du Colombier int
78225210b06SDavid du Colombier unminip4cfg(Openeth *oe)
78325210b06SDavid du Colombier {
78425210b06SDavid du Colombier 	int n;
78525210b06SDavid du Colombier 	char buf[64];
78625210b06SDavid du Colombier 
78725210b06SDavid du Colombier 	n = snprint(buf, sizeof buf, "remove %I /128", IPnoaddr);
78825210b06SDavid du Colombier 	if (waserror()) {
78925210b06SDavid du Colombier 		print("failed write to ifc: %s: %s\n", buf, up->errstr);
79025210b06SDavid du Colombier 		return -1;
79125210b06SDavid du Colombier 	}
79225210b06SDavid du Colombier 	devtab[oe->ifcctl->type]->write(oe->ifcctl, buf, n, 0);	/* remove %I */
79325210b06SDavid du Colombier 	cclose(oe->ethctl);		/* turn promiscuous mode off */
79425210b06SDavid du Colombier 	oe->ethctl = nil;
79525210b06SDavid du Colombier 	poperror();
79625210b06SDavid du Colombier 	return 0;
79725210b06SDavid du Colombier }
79825210b06SDavid du Colombier 
79925210b06SDavid du Colombier /*
80025210b06SDavid du Colombier  * parse p, looking for option `op'.  if non-nil, np points to minimum length.
80125210b06SDavid du Colombier  * return nil if option is too small, else ptr to opt, and
80225210b06SDavid du Colombier  * store actual length via np if non-nil.
80325210b06SDavid du Colombier  */
80425210b06SDavid du Colombier uchar*
80525210b06SDavid du Colombier optget(uchar *p, int op, int *np)
80625210b06SDavid du Colombier {
80725210b06SDavid du Colombier 	int len, code;
80825210b06SDavid du Colombier 
80925210b06SDavid du Colombier 	while ((code = *p++) != OBend) {
81025210b06SDavid du Colombier 		if(code == OBpad)
81125210b06SDavid du Colombier 			continue;
81225210b06SDavid du Colombier 		len = *p++;
81325210b06SDavid du Colombier 		if(code != op) {
81425210b06SDavid du Colombier 			p += len;
81525210b06SDavid du Colombier 			continue;
81625210b06SDavid du Colombier 		}
81725210b06SDavid du Colombier 		if(np != nil){
81825210b06SDavid du Colombier 			if(*np > len) {
81925210b06SDavid du Colombier 				return 0;
82025210b06SDavid du Colombier 			}
82125210b06SDavid du Colombier 			*np = len;
82225210b06SDavid du Colombier 		}
82325210b06SDavid du Colombier 		return p;
82425210b06SDavid du Colombier 	}
82525210b06SDavid du Colombier 	return 0;
82625210b06SDavid du Colombier }
82725210b06SDavid du Colombier 
82825210b06SDavid du Colombier int
82925210b06SDavid du Colombier optgetaddr(uchar *p, int op, uchar *ip)
83025210b06SDavid du Colombier {
83125210b06SDavid du Colombier 	int len;
83225210b06SDavid du Colombier 
83325210b06SDavid du Colombier 	len = 4;
83425210b06SDavid du Colombier 	p = optget(p, op, &len);
83525210b06SDavid du Colombier 	if(p == nil)
83625210b06SDavid du Colombier 		return 0;
83725210b06SDavid du Colombier 	v4tov6(ip, p);
83825210b06SDavid du Colombier 	return 1;
83925210b06SDavid du Colombier }
84025210b06SDavid du Colombier 
84125210b06SDavid du Colombier int beprimary = 1;
84225210b06SDavid du Colombier 
84325210b06SDavid du Colombier /* add a logical interface to the ip stack */
84425210b06SDavid du Colombier int
84525210b06SDavid du Colombier ip4cfg(Openeth *oe, Bootp *rep)
84625210b06SDavid du Colombier {
84725210b06SDavid du Colombier 	int n;
84825210b06SDavid du Colombier 	uchar gaddr[IPaddrlen], v6mask[IPaddrlen];
84925210b06SDavid du Colombier 	uchar v4mask[IPv4addrlen];
85025210b06SDavid du Colombier 	char buf[64];
85125210b06SDavid du Colombier 	static uchar zeroes[4];
85225210b06SDavid du Colombier 
85325210b06SDavid du Colombier 	v4tov6(gaddr, rep->yiaddr);
85425210b06SDavid du Colombier 	if(!validip(gaddr))
85525210b06SDavid du Colombier 		return -1;
85625210b06SDavid du Colombier 
85725210b06SDavid du Colombier 	/* dig subnet mask, if any, out of options.  if none, guess. */
85825210b06SDavid du Colombier 	if(optgetaddr(rep->optdata, OBmask, v6mask)) {
85925210b06SDavid du Colombier 		v6tov4(v4mask, v6mask);
86025210b06SDavid du Colombier 		n = snprint(buf, sizeof buf, "add %V %M", rep->yiaddr, v4mask);
86125210b06SDavid du Colombier 	} else
86225210b06SDavid du Colombier 		n = snprint(buf, sizeof buf, "add %V 255.255.255.0", rep->yiaddr);
86325210b06SDavid du Colombier 
86425210b06SDavid du Colombier 	devtab[oe->ifcctl->type]->write(oe->ifcctl, buf, n, 0);
86525210b06SDavid du Colombier 
86625210b06SDavid du Colombier 	v4tov6(gaddr, rep->giaddr);
86725210b06SDavid du Colombier 	if(beprimary==1 && validip(gaddr) && !equivip4(rep->giaddr, zeroes))
86825210b06SDavid du Colombier 		adddefroute("/net", gaddr);
86925210b06SDavid du Colombier 	return 0;
87025210b06SDavid du Colombier }
87125210b06SDavid du Colombier 
87225210b06SDavid du Colombier static int
87325210b06SDavid du Colombier openudp(Openeth *oe)
87425210b06SDavid du Colombier {
87525210b06SDavid du Colombier 	int n;
87625210b06SDavid du Colombier 	char buf[16];
87725210b06SDavid du Colombier 	Chan *cc;
87825210b06SDavid du Colombier 
87925210b06SDavid du Colombier 	/* read clone file for conversation number */
88025210b06SDavid du Colombier 	if (waserror())
88125210b06SDavid du Colombier 		panic("openudp: can't open /net/udp/clone");
88225210b06SDavid du Colombier 	cc = enamecopen("/net/udp/clone", ORDWR);
88325210b06SDavid du Colombier 	oe->udpctl = cc;
88425210b06SDavid du Colombier 	n = devtab[cc->type]->read(cc, buf, sizeof buf - 1, 0);
88525210b06SDavid du Colombier 	poperror();
88625210b06SDavid du Colombier 	buf[n] = '\0';
88725210b06SDavid du Colombier 	return atoi(buf);
88825210b06SDavid du Colombier }
88925210b06SDavid du Colombier 
89025210b06SDavid du Colombier static void
89125210b06SDavid du Colombier initbind(Openeth *oe)
89225210b06SDavid du Colombier {
89325210b06SDavid du Colombier 	char buf[8];
89425210b06SDavid du Colombier 
89525210b06SDavid du Colombier 	if (waserror()) {
89625210b06SDavid du Colombier 		print("error while binding: %s\n", up->errstr);
89725210b06SDavid du Colombier 		return;
89825210b06SDavid du Colombier 	}
89925210b06SDavid du Colombier 	snprint(buf, sizeof buf, "#I%d", oe->ctlrno);
90025210b06SDavid du Colombier 	bind(buf, "/net", MAFTER);
90125210b06SDavid du Colombier 	snprint(buf, sizeof buf, "#l%d", oe->ctlrno);
90225210b06SDavid du Colombier 	bind(buf, "/net", MAFTER);
90325210b06SDavid du Colombier 	binddevip(oe);
90425210b06SDavid du Colombier 	poperror();
90525210b06SDavid du Colombier }
90625210b06SDavid du Colombier 
90725210b06SDavid du Colombier static void
90825210b06SDavid du Colombier closeudp(Openeth *oe)
90925210b06SDavid du Colombier {
91025210b06SDavid du Colombier 	if (oe->udpctl) {
91125210b06SDavid du Colombier 		cclose(oe->udpctl);
91225210b06SDavid du Colombier 		oe->udpctl = nil;
91325210b06SDavid du Colombier 	}
91425210b06SDavid du Colombier 	if (oe->udpdata) {
91525210b06SDavid du Colombier 		cclose(oe->udpdata);
91625210b06SDavid du Colombier 		oe->udpdata = nil;
91725210b06SDavid du Colombier 	}
91825210b06SDavid du Colombier }
91925210b06SDavid du Colombier 
92025210b06SDavid du Colombier static int
92125210b06SDavid du Colombier announce(Openeth *oe, char *port)
92225210b06SDavid du Colombier {
92325210b06SDavid du Colombier 	int udpconv;
92425210b06SDavid du Colombier 	char buf[32];
92525210b06SDavid du Colombier 	static char hdrs[] = "headers";
92625210b06SDavid du Colombier 
92725210b06SDavid du Colombier 	while (waserror()) {
92825210b06SDavid du Colombier 		print("can't announce udp!*!%s: %s\n", port, up->errstr);
92925210b06SDavid du Colombier 		closeudp(oe);
93025210b06SDavid du Colombier 		nexterror();
93125210b06SDavid du Colombier 	}
93225210b06SDavid du Colombier 	udpconv = openudp(oe);
93325210b06SDavid du Colombier 	if (udpconv < 0)
93425210b06SDavid du Colombier 		panic("can't open udp conversation: %s", up->errstr);
93525210b06SDavid du Colombier 
93625210b06SDavid du Colombier 	/* headers is only effective after a udp announce */
93725210b06SDavid du Colombier 	snprint(buf, sizeof buf, "announce %s", port);
93825210b06SDavid du Colombier 	devtab[oe->udpctl->type]->write(oe->udpctl, buf, strlen(buf), 0);
93925210b06SDavid du Colombier 	devtab[oe->udpctl->type]->write(oe->udpctl, hdrs, sizeof hdrs - 1, 0);
94025210b06SDavid du Colombier 	poperror();
94125210b06SDavid du Colombier 
94225210b06SDavid du Colombier 	/* now okay to open the data file */
94325210b06SDavid du Colombier 	snprint(buf, sizeof buf, "/net/udp/%d/data", udpconv);
94425210b06SDavid du Colombier 	/*
94525210b06SDavid du Colombier 	 * we must use create, not open, to get Conv->rq and ->wq
94625210b06SDavid du Colombier 	 * allocated by udpcreate.
94725210b06SDavid du Colombier 	 */
94825210b06SDavid du Colombier 	oe->udpdata = enameccreate(buf, ORDWR);
94925210b06SDavid du Colombier 	cclose(oe->udpctl);
95025210b06SDavid du Colombier 	oe->udpctl = nil;
95125210b06SDavid du Colombier 	return udpconv;
95225210b06SDavid du Colombier }
95325210b06SDavid du Colombier 
95425210b06SDavid du Colombier static long
95525210b06SDavid du Colombier tftprdfile(Openeth *oe, int openread, void* va, long len)
95625210b06SDavid du Colombier {
95725210b06SDavid du Colombier 	int n;
95825210b06SDavid du Colombier 	char *p, *v;
95925210b06SDavid du Colombier 
96025210b06SDavid du Colombier 	n = openread;	/* have read this many bytes already into tftpb->data */
96125210b06SDavid du Colombier 	p = v = va;
96225210b06SDavid du Colombier 	len--;				/* leave room for NUL */
96325210b06SDavid du Colombier 	while(n > 0) {
96425210b06SDavid du Colombier 		if((p-v)+n > len)
96525210b06SDavid du Colombier 			n = len - (p-v);
96625210b06SDavid du Colombier 		memmove(p, tftpb->data, n);
96725210b06SDavid du Colombier 		p += n;
96825210b06SDavid du Colombier 		*p = 0;
96925210b06SDavid du Colombier 		if(n != segsize)
97025210b06SDavid du Colombier 			break;
97125210b06SDavid du Colombier 
97225210b06SDavid du Colombier 		if((n = tftpread(oe, &tftpserv, tftpb, segsize)) < 0)
97325210b06SDavid du Colombier 			return -1;
97425210b06SDavid du Colombier 	}
97525210b06SDavid du Colombier 	return p-v;
97625210b06SDavid du Colombier }
97725210b06SDavid du Colombier 
97825210b06SDavid du Colombier static int
97925210b06SDavid du Colombier newtftpconn(Openeth *oe, Bootp *rep)
98025210b06SDavid du Colombier {
98125210b06SDavid du Colombier 	char num[16], dialstr[64];
98225210b06SDavid du Colombier 
98325210b06SDavid du Colombier 	if (waserror()) {
98425210b06SDavid du Colombier 		print("can't dial: %s\n", up->errstr);
98525210b06SDavid du Colombier 		return -1;
98625210b06SDavid du Colombier 	}
98725210b06SDavid du Colombier 	closeudp(oe);
98825210b06SDavid du Colombier 
98925210b06SDavid du Colombier 	tftpphase = 1;
99025210b06SDavid du Colombier 	tftpport = 5000 + nrand(20480);
99125210b06SDavid du Colombier 	snprint(num, sizeof num, "%d", tftpport);
99225210b06SDavid du Colombier 	if (Tftpusehdrs)
99325210b06SDavid du Colombier 		announce(oe, num);
99425210b06SDavid du Colombier 	else {
99525210b06SDavid du Colombier 		snprint(dialstr, sizeof dialstr, "/net/udp!%V!%d",
99625210b06SDavid du Colombier 			rep->siaddr, TFTPport);
99725210b06SDavid du Colombier 		oe->udpdata = chandial(dialstr, num, nil, nil);
99825210b06SDavid du Colombier 		oe->udpctl = nil;
99925210b06SDavid du Colombier 	}
100025210b06SDavid du Colombier 	poperror();
100125210b06SDavid du Colombier 	return 0;
100225210b06SDavid du Colombier }
100325210b06SDavid du Colombier 
100425210b06SDavid du Colombier static int
100525210b06SDavid du Colombier setipcfg(Openeth *oe, Bootp *rep)
100625210b06SDavid du Colombier {
100725210b06SDavid du Colombier 	int r;
100825210b06SDavid du Colombier 
100925210b06SDavid du Colombier 	tftpphase = 0;
101025210b06SDavid du Colombier 	progress = 1;
101125210b06SDavid du Colombier 
101225210b06SDavid du Colombier 	/* /net/iproute is unpopulated here; add at least broadcast */
101325210b06SDavid du Colombier 	minip4cfg(oe);
101425210b06SDavid du Colombier 	announce(oe, "68");
101525210b06SDavid du Colombier 	r = bootpbcast(oe, nil, rep);
101625210b06SDavid du Colombier 	closeudp(oe);
101725210b06SDavid du Colombier 	unminip4cfg(oe);
101825210b06SDavid du Colombier 	if(r < 0)
101925210b06SDavid du Colombier 		return -1;
102025210b06SDavid du Colombier 
102125210b06SDavid du Colombier 	ip4cfg(oe, rep);
102225210b06SDavid du Colombier 	if (Debug)
102325210b06SDavid du Colombier 		print("got & set ip config\n");
102425210b06SDavid du Colombier 	return 0;
102525210b06SDavid du Colombier }
102625210b06SDavid du Colombier 
102725210b06SDavid du Colombier static int
102825210b06SDavid du Colombier getkernname(Openeth *oe, Bootp *rep, Kernname *kp)
102925210b06SDavid du Colombier {
103025210b06SDavid du Colombier 	int n;
103125210b06SDavid du Colombier 	char *ini, *p;
103225210b06SDavid du Colombier 	char cfgpxe[32], buf[64];
103325210b06SDavid du Colombier 
103425210b06SDavid du Colombier 	if (kp->bootfile) {
103525210b06SDavid du Colombier 		print("getkernname: already have bootfile %s\n", kp->bootfile);
103625210b06SDavid du Colombier 		return 0;
103725210b06SDavid du Colombier 	}
103825210b06SDavid du Colombier 	if (newtftpconn(oe, rep) < 0)
103925210b06SDavid du Colombier 		return -1;
104025210b06SDavid du Colombier 
104125210b06SDavid du Colombier 	/* use our mac address instead of relying on a bootp answer */
104225210b06SDavid du Colombier 	snprint(cfgpxe, sizeof cfgpxe, "/cfg/pxe/%E", myea);
104325210b06SDavid du Colombier 	/*
104425210b06SDavid du Colombier 	 * use bootp answer (rep) to open cfgpxe.
104525210b06SDavid du Colombier 	 * reads first pkt of cfgpxe into tftpb->data.
104625210b06SDavid du Colombier 	 */
104725210b06SDavid du Colombier 	n = tftpopen(oe, cfgpxe, rep);
104825210b06SDavid du Colombier 	if (n < 0) {
104925210b06SDavid du Colombier 		print("\nfailed.\n");
105025210b06SDavid du Colombier 		return -1;
105125210b06SDavid du Colombier 	}
105225210b06SDavid du Colombier 	if (Debug)
105325210b06SDavid du Colombier 		print("\opened %s\n", cfgpxe);
105425210b06SDavid du Colombier 
105525210b06SDavid du Colombier 	ini = smalloc(2*BOOTARGSLEN);
105625210b06SDavid du Colombier 	/* starts by copying data from tftpb->data into ini */
105725210b06SDavid du Colombier 	n = tftprdfile(oe, n, ini, 2*BOOTARGSLEN);
105825210b06SDavid du Colombier 	if (n < 0) {
105925210b06SDavid du Colombier 		print("error reading %s\n", cfgpxe);
106025210b06SDavid du Colombier 		free(ini);
106125210b06SDavid du Colombier 		return -1;
106225210b06SDavid du Colombier 	}
106325210b06SDavid du Colombier 	print(" read %d bytes", n);
106425210b06SDavid du Colombier 
106525210b06SDavid du Colombier 	/*
106625210b06SDavid du Colombier 	 * take note of plan9.ini contents.  consumes ini to make config vars,
106725210b06SDavid du Colombier 	 * thus we can't free ini.
106825210b06SDavid du Colombier 	 */
106925210b06SDavid du Colombier 	dotini(ini);
107025210b06SDavid du Colombier 	i8250console();		/* configure serial port with defaults */
1071*e4575fb1SDavid du Colombier 
1072*e4575fb1SDavid du Colombier 	kp->edev = kp->bootfile = nil;
1073*e4575fb1SDavid du Colombier 	p = getconf("bootfile");
1074*e4575fb1SDavid du Colombier 	if (p)
1075*e4575fb1SDavid du Colombier 		kstrdup(&kp->bootfile, p);
1076*e4575fb1SDavid du Colombier 	if (kp->bootfile == nil)
1077*e4575fb1SDavid du Colombier 		askbootfile(buf, sizeof buf, &kp->bootfile, Promptsecs,
1078*e4575fb1SDavid du Colombier 			"ether0!/386/9pccpu");
1079*e4575fb1SDavid du Colombier 	if (strcmp(kp->bootfile, "manual") == 0)
1080*e4575fb1SDavid du Colombier 		askbootfile(buf, sizeof buf, &kp->bootfile, 0, "");
1081*e4575fb1SDavid du Colombier 
108225210b06SDavid du Colombier 	p = strchr(kp->bootfile, '!');
108325210b06SDavid du Colombier 	if (p != nil) {
108425210b06SDavid du Colombier 		*p++ = '\0';
1085*e4575fb1SDavid du Colombier 		kp->edev = kp->bootfile;
108625210b06SDavid du Colombier 		kp->bootfile = nil;
108725210b06SDavid du Colombier 		kstrdup(&kp->bootfile, p);
108825210b06SDavid du Colombier 		if (strncmp(kp->edev, ethernm, sizeof ethernm - 1) != 0) {
108925210b06SDavid du Colombier 			print("bad ether device %s\n", kp->edev);
109025210b06SDavid du Colombier 			return -1;
109125210b06SDavid du Colombier 		}
109225210b06SDavid du Colombier 	}
109325210b06SDavid du Colombier 
109425210b06SDavid du Colombier 	/* pass arguments to kernels that can use them */
109525210b06SDavid du Colombier 	strecpy(BOOTLINE, BOOTLINE+BOOTLINELEN, kp->bootfile);
109625210b06SDavid du Colombier 	p = strchr(kp->bootfile, ' ');
109725210b06SDavid du Colombier 	if(p != nil)
109825210b06SDavid du Colombier 		*p = '\0';
109925210b06SDavid du Colombier 	return 0;
110025210b06SDavid du Colombier }
110125210b06SDavid du Colombier 
110225210b06SDavid du Colombier static void
110325210b06SDavid du Colombier unbinddevip(Openeth *oe)
110425210b06SDavid du Colombier {
110525210b06SDavid du Colombier 	Chan *icc;
110625210b06SDavid du Colombier 	static char unbind[] = "unbind";
110725210b06SDavid du Colombier 
110825210b06SDavid du Colombier 	icc = oe->ifcctl;
110925210b06SDavid du Colombier 	if (icc) {
111025210b06SDavid du Colombier 		devtab[icc->type]->write(icc, unbind, sizeof unbind - 1, 0);
111125210b06SDavid du Colombier 		cclose(icc);
111225210b06SDavid du Colombier 		oe->ifcctl = nil;
111325210b06SDavid du Colombier 	}
111425210b06SDavid du Colombier }
111525210b06SDavid du Colombier 
111625210b06SDavid du Colombier /*
111725210b06SDavid du Colombier  * phase 1: get our ip (v4) configuration via bootp, set new ip configuration.
111825210b06SDavid du Colombier  * phase 2: load /cfg/pxe, parse it, extract kernel filename.
111925210b06SDavid du Colombier  * phase 3: load kernel and jump to it.
112025210b06SDavid du Colombier  */
112125210b06SDavid du Colombier static void
112225210b06SDavid du Colombier tftpload(Openeth *oe, Kernname *kp)
112325210b06SDavid du Colombier {
112425210b06SDavid du Colombier 	Bootp rep;
112525210b06SDavid du Colombier 	Boot boot;
112625210b06SDavid du Colombier 
112725210b06SDavid du Colombier 	if(waserror()) {
112825210b06SDavid du Colombier 		print("tftpload: %s\n", up->errstr);
112925210b06SDavid du Colombier 		closeudp(oe);
113025210b06SDavid du Colombier 		unbinddevip(oe);
113125210b06SDavid du Colombier 		return;
113225210b06SDavid du Colombier 	}
113325210b06SDavid du Colombier 
113425210b06SDavid du Colombier 	memset(&rep, 0, sizeof rep);
113525210b06SDavid du Colombier 	if (setipcfg(oe, &rep) >= 0 &&
113625210b06SDavid du Colombier 	    getkernname(oe, &rep, kp) >= 0 &&
113725210b06SDavid du Colombier 	    (!kp->edev ||
113825210b06SDavid du Colombier 	     oe->ctlrno == strtol(kp->edev + sizeof ethernm - 1, 0, 10)) &&
113925210b06SDavid du Colombier 	    newtftpconn(oe, &rep) >= 0) {
114025210b06SDavid du Colombier 		memset(&boot, 0, sizeof boot);
114125210b06SDavid du Colombier 		boot.state = INITKERNEL;
114225210b06SDavid du Colombier 		tftpboot(oe, kp->bootfile, &rep, &boot);
114325210b06SDavid du Colombier 	}
114425210b06SDavid du Colombier 
114525210b06SDavid du Colombier 	/* we failed or bootfile asked for another ether */
114625210b06SDavid du Colombier 	poperror();
114725210b06SDavid du Colombier 	closeudp(oe);
114825210b06SDavid du Colombier 	unbinddevip(oe);
114925210b06SDavid du Colombier }
115025210b06SDavid du Colombier 
115125210b06SDavid du Colombier static int
115225210b06SDavid du Colombier etherload(int eth, Kernname *kp)
115325210b06SDavid du Colombier {
115425210b06SDavid du Colombier 	Openeth *oe;
115525210b06SDavid du Colombier 
115625210b06SDavid du Colombier 	print("pxe on ether%d ", eth);
115725210b06SDavid du Colombier 	oe = smalloc(sizeof *oe);
115825210b06SDavid du Colombier 	memset(oe, 0, sizeof *oe);
115925210b06SDavid du Colombier 	oe->ctlrno = eth;
116025210b06SDavid du Colombier 	snprint(oe->ethname, sizeof oe->ethname, "ether%d", oe->ctlrno);
116125210b06SDavid du Colombier 	snprint(oe->netethname, sizeof oe->netethname, "/net/ether%d",
116225210b06SDavid du Colombier 		oe->ctlrno);
116325210b06SDavid du Colombier 	initbind(oe);
116425210b06SDavid du Colombier 
116525210b06SDavid du Colombier 	tftpload(oe, kp);
116625210b06SDavid du Colombier 
116725210b06SDavid du Colombier 	/* failed to boot; keep going */
116825210b06SDavid du Colombier 	unmount(nil, "/net");
116925210b06SDavid du Colombier 	return 0;
117025210b06SDavid du Colombier }
117125210b06SDavid du Colombier 
117225210b06SDavid du Colombier static int
117325210b06SDavid du Colombier nethers(void)
117425210b06SDavid du Colombier {
117525210b06SDavid du Colombier 	int neth;
117625210b06SDavid du Colombier 	char num[4];
117725210b06SDavid du Colombier 	Chan *cc;
117825210b06SDavid du Colombier 
117925210b06SDavid du Colombier 	/* count interfaces */
118025210b06SDavid du Colombier 	print("attaching ethers:");
118125210b06SDavid du Colombier 	for (neth = 0; ; neth++) {
118225210b06SDavid du Colombier 		cc = nil;
118325210b06SDavid du Colombier 		if (waserror()) {		/* no more interfaces */
118425210b06SDavid du Colombier 			if (cc)
118525210b06SDavid du Colombier 				cclose(cc);
118625210b06SDavid du Colombier 			break;
118725210b06SDavid du Colombier 		}
118825210b06SDavid du Colombier 
118925210b06SDavid du Colombier 		snprint(num, sizeof num, "%d", neth);
119025210b06SDavid du Colombier 		cc = etherattach(num);
119125210b06SDavid du Colombier 		if (cc)
119225210b06SDavid du Colombier 			cclose(cc);
119325210b06SDavid du Colombier 		poperror();
119425210b06SDavid du Colombier 		if (cc == nil)
119525210b06SDavid du Colombier 			break;			/* no more interfaces */
119625210b06SDavid du Colombier 		print(" %d", neth);
119725210b06SDavid du Colombier 	}
119825210b06SDavid du Colombier 	print("\n");
119925210b06SDavid du Colombier 	return neth;
120025210b06SDavid du Colombier }
120125210b06SDavid du Colombier 
120225210b06SDavid du Colombier void
120325210b06SDavid du Colombier bootloadproc(void *)
120425210b06SDavid du Colombier {
120525210b06SDavid du Colombier 	int eth, neth;
120625210b06SDavid du Colombier 	Kernname kernnm;
120725210b06SDavid du Colombier 
120825210b06SDavid du Colombier 	neth = nethers();
120925210b06SDavid du Colombier 	if(neth <= 0) {
121025210b06SDavid du Colombier 		print("error counting interfaces, assuming 1\n");
121125210b06SDavid du Colombier 		neth = 1;
121225210b06SDavid du Colombier 	}
121325210b06SDavid du Colombier 
121425210b06SDavid du Colombier 	srand(TK2MS(m->ticks));			/* for local port numbers */
121525210b06SDavid du Colombier 	nrand(20480);				/* 1st # is always 0; toss it */
121625210b06SDavid du Colombier 	kernnm.edev = kernnm.bootfile = nil;
121725210b06SDavid du Colombier 
121825210b06SDavid du Colombier 	while(waserror()) {
121925210b06SDavid du Colombier 		print("%s\n", up->errstr);
122025210b06SDavid du Colombier 		tsleep(&up->sleep, return0, 0, 30*1000);
122125210b06SDavid du Colombier 	}
122225210b06SDavid du Colombier 	for (;;) {
122325210b06SDavid du Colombier 		/* try each interface in turn: first get /cfg/pxe file */
122425210b06SDavid du Colombier 		for (eth = 0; eth < neth && kernnm.edev == nil; eth++)
122525210b06SDavid du Colombier 			etherload(eth, &kernnm);
122625210b06SDavid du Colombier 		if (kernnm.edev != nil) {
122725210b06SDavid du Colombier 			eth = strtol(kernnm.edev + sizeof ethernm - 1, 0, 10);
122825210b06SDavid du Colombier 			etherload(eth, &kernnm);
122925210b06SDavid du Colombier 		}
123025210b06SDavid du Colombier 		/*
123125210b06SDavid du Colombier 		 * couldn't boot on any ether.  don't give up;
123225210b06SDavid du Colombier 		 * perhaps the boot servers are down, so try again later.
123325210b06SDavid du Colombier 		 */
123425210b06SDavid du Colombier 		print("failed to boot via pxe; will try again.\n");
123525210b06SDavid du Colombier 		tsleep(&up->sleep, return0, 0, 15*1000);
123625210b06SDavid du Colombier 	}
123725210b06SDavid du Colombier }
1238