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