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