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