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