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