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