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