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