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