1# 2# Generalized boot Inferno 3# 4 5implement Init; 6 7include "sys.m"; 8 sys: Sys; 9 10include "draw.m"; 11 12include "keyring.m"; 13 kr: Keyring; 14 15include "security.m"; 16 auth: Auth; 17 random: Random; 18 19include "tftp.m"; 20 21Bootpreadlen: con 128; 22 23Init: module 24{ 25 init: fn(); 26}; 27 28ip: string; 29mask: string; 30fsip: string; 31bootprotocol: string; 32bootserver: string; 33bootfile: string; 34 35debug: con 0; 36 37init() 38{ 39 ipavailable: int; 40 sys = load Sys Sys->PATH; 41 42 kexecfd := sys->open("#B/kexec", Sys->OWRITE); 43 if (kexecfd == nil) 44 fatal(sys->sprint("opening #B/kexec: %r")); 45 46 ipavailable = 0; 47 if (dobind("#l", "/net", sys->MREPL) && dobind("#I", "/net", sys->MAFTER)) 48 ipavailable = 1; 49 50 dobind("#c", "/dev", sys->MAFTER); # console device 51 52 if (!ipavailable) 53 fatal("no IP stack available"); 54 cfd := sys->open("/net/ipifc/clone", sys->ORDWR); 55 if(cfd == nil) 56 fatal(sys->sprint("open /net/ipifc/clone: %r")); 57 58 if (sys->fprint(cfd, "bind ether ether0") < 0) 59 fatal(sys->sprint("binding ether0: %r")); 60 61 fsready := 0; 62 63 fsip = ipconfig(cfd); 64 65 bootstring := getenvdefault("bootpath", "tftp"); 66 67 (bootprotocol, bootserver, bootfile) = parsebootstring(bootstring); 68 69 if (bootprotocol == nil) 70 fatal(bootstring + ": unrecognised syntax"); 71 72 # Run dhcp if necessary 73 if (bootprotocol == "tftp" && (bootserver == nil || bootfile == nil)) 74 dhcp(); 75 76 # determine server 77 if (bootprotocol == "net" && bootserver == nil) 78 bootserver = fsip; 79 80 if (bootserver == nil) 81 fatal("couldn't determine boot server"); 82 83 if (bootfile == nil) 84 fatal("couldn't determine boot file"); 85 86 if (bootprotocol == nil) 87 fatal("couldn't determine boot protocol"); 88 89 sys->print("loading %s!%s!%s\n", bootprotocol, bootserver, bootfile); 90 91 if (bootprotocol == "net") { 92 sys->print("Attempting remote mount\n"); 93 if (netfs(bootserver) == 0) 94 sys->print("Remote mount successful\n"); 95 else 96 fatal(sys->sprint("Remote mount failed: %r")); 97 fd := sys->open("/n/remote" + bootfile, Sys->OREAD); 98 if (fd == nil) 99 fatal(sys->sprint("%s:/n/remote%s: %r", bootserver, bootfile)); 100 if (sys->stream(fd, kexecfd, 4096) < 0) 101 fatal(sys->sprint("copying %s: %r", bootfile)); 102 } 103 else if (bootprotocol == "tftp") { 104 tftp := load Tftp Tftp->PATH; 105 if (tftp == nil) 106 fatal("can't load tftp module"); 107 tftp->init(1); 108 errstr := tftp->receive(bootserver, bootfile, kexecfd); 109 if (errstr != nil) 110 fatal("tftp: " + errstr); 111 } 112 else 113 fatal("protocol " + bootprotocol + " not supported"); 114 sys->print("Launching new kernel\n"); 115 kexecfd = nil; 116} 117 118parsebootstring(s: string): (string, string, string) 119{ 120 proto, server, file: string; 121 (n, l) := sys->tokenize(s, "!"); 122 if (n > 3) 123 return (nil, nil, nil); 124 proto = hd l; 125 l = tl l; 126 if (l != nil) { 127 server = hd l; 128 l = tl l; 129 } 130 if (l != nil) 131 file = hd l; 132 case proto { 133 "tftp" => 134 ; 135 "net" => 136 # can't have a default file, so n must be 3 137 if (n != 3) 138 return (nil, nil, nil); 139 * => 140 return (nil, nil, nil); 141 } 142 return (proto, server, file); 143} 144 145dobind(f, t: string, flags: int): int 146{ 147 if(sys->bind(f, t, flags) < 0) { 148 err(sys->sprint("can't bind %s on %s: %r", f, t)); 149 return 0; 150 } 151 return 1; 152} 153 154err(s: string) 155{ 156 sys->fprint(sys->fildes(2), "bootinit: %s\n", s); 157} 158 159hang() 160{ 161 <-(chan of int); 162} 163 164fatal(s: string) 165{ 166 err(s); 167 hang(); 168} 169 170envlist: list of string; 171 172getenv(name: string): string 173{ 174 if (envlist == nil) { 175 fd := sys->open("/dev/sysenv", Sys->OREAD); 176 if (fd != nil) { 177 ntok: int; 178 buf := array[1024] of byte; 179 nr := sys->read(fd, buf, len buf); 180 if(nr > 0) 181 (ntok, envlist) = sys->tokenize(string buf, "\n"); 182 } 183 } 184 ls := envlist; 185 while(ls != nil) { 186 (ntok2, ls2) := sys->tokenize(hd ls, "="); 187 if(hd ls2 == name) 188 return hd tl ls2; 189 ls = tl ls; 190 } 191 return nil; 192} 193 194getenvdefault(name: string, default: string): string 195{ 196 rv := getenv(name); 197 if (rv == nil) 198 return default; 199 return rv; 200} 201 202ipconfig(cfd: ref sys->FD): string 203{ 204 ip = getenv("wireip"); 205 if (ip == nil) 206 ip = getenv("ip"); 207 mask = getenv("ipmask"); 208 fsip = getenv("fsip"); 209 if (ip != nil && mask != nil) { 210 sys->print("ip %s %s\n", ip, mask); 211 sys->fprint(cfd, "add %s %s", ip, mask); 212 gwip := getenv("gwip"); 213 if (gwip != nil) { 214 sys->print("gwip %s\n", gwip); 215 rfd := sys->open("/net/iproute", Sys->ORDWR); 216 if (rfd == nil || sys->fprint(rfd, "add 0.0.0.0 0.0.0.0 %s", gwip) < 0) 217 err(sys->sprint("failed to add default route: %r")); 218 } 219 } 220 if (ip == nil || mask == nil) 221 return bootp(cfd); 222 return fsip; 223} 224 225bootpdone: int; 226 227bootp(cfd: ref sys->FD): string 228{ 229 if (bootpdone == 1) 230 return fsip; 231 232 bootpdone = 1; 233 234 sys->print("bootp ..."); 235 236 if (sys->fprint(cfd, "bootp") < 0) { 237 sys->print("init: bootp: %r"); 238 return nil; 239 } 240 241 fd := sys->open("/net/bootp", sys->OREAD); 242 if(fd == nil) { 243 err(sys->sprint("open /net/bootp: %r")); 244 return nil; 245 } 246 247 buf := array[Bootpreadlen] of byte; 248 nr := sys->read(fd, buf, len buf); 249 fd = nil; 250 if(nr <= 0) { 251 err(sys->sprint("read /net/bootp: %r")); 252 return nil; 253 } 254 (ntok, ls) := sys->tokenize(string buf, " \t\n"); 255 while(ls != nil) { 256 name := hd ls; 257 ls = tl ls; 258 if (ls == nil) 259 break; 260 value := hd ls; 261 ls = tl ls; 262 if (name == "fsip") 263 fsip = value; 264 else if (name == "ipaddr") 265 ip = value; 266 else if (name == "ipmask") 267 mask = value; 268 } 269 return fsip; 270} 271 272netfs(server: string): int 273{ 274 auth = load Auth Auth->PATH; 275 if (auth != nil) 276 auth->init(); 277 278 kr = load Keyring Keyring->PATH; 279 sys->print("dial..."); 280 (ok, c) := sys->dial("tcp!" + server + "!6666", nil); 281 if(ok < 0) 282 return -1; 283 284 if(kr != nil && auth != nil){ 285 err: string; 286 sys->print("Authenticate ..."); 287 ai := kr->readauthinfo("/nvfs/default"); 288 if(ai == nil){ 289 sys->print("readauthinfo /nvfs/default failed: %r\n"); 290 sys->print("trying mount as `nobody'\n"); 291 } 292 (c.dfd, err) = auth->client("none", ai, c.dfd); 293 if(c.dfd == nil){ 294 sys->print("authentication failed: %s\n", err); 295 return -1; 296 } 297 } 298 299 sys->print("mount ..."); 300 301 c.cfd = nil; 302 n := sys->mount(c.dfd, nil, "/n/remote", sys->MREPL, ""); 303 if(n > 0) 304 return 0; 305 return -1; 306} 307 308# 309# 310# DHCP 311# 312# 313 314Dhcp: adt { 315 op: int; 316 htype: int; 317 hops: int; 318 xid: int; 319 secs: int; 320 flags: int; 321 ciaddr: int; 322 yiaddr: int; 323 siaddr: int; 324 giaddr: int; 325 chaddr: array of byte; 326 sname: string; 327 file: string; 328}; 329 330nboputl(buf: array of byte, val: int) 331{ 332 buf[0] = byte (val >> 24); 333 buf[1] = byte (val >> 16); 334 buf[2] = byte (val >> 8); 335 buf[3] = byte val; 336} 337 338nboputs(buf: array of byte, val: int) 339{ 340 buf[0] = byte (val >> 8); 341 buf[1] = byte val; 342} 343 344nbogets(buf: array of byte): int 345{ 346 return (int buf[0] << 8) | int buf[1]; 347} 348 349nbogetl(buf: array of byte): int 350{ 351 return (int buf[0] << 24) | (int buf[1] << 16) | (int buf[2] << 8) | int buf[3]; 352} 353 354stringget(buf: array of byte): string 355{ 356 for (x := 0; x < len buf; x++) 357 if (buf[x] == byte 0) 358 break; 359 if (x == 0) 360 return nil; 361 return string buf[0 : x]; 362} 363 364memcmp(b1: array of byte, b2: array of byte): int 365{ 366 l := len b1; 367 if (l < len b2) 368 return int -b2[l]; 369 if (l > len b2) 370 return int b1[l]; 371 for (i := 0; i < l; i++) { 372 d := int b1[i] - int b2[i]; 373 if (d != 0) 374 return d; 375 } 376 return 0; 377} 378 379memncpy(out: array of byte, in: array of byte) 380{ 381 if (in == nil) 382 return; 383 l := len in; 384 if (l > len out) 385 l = len out; 386 out[0 :] = in[0 : l]; 387} 388 389memset(out: array of byte, val: byte) 390{ 391 for (l := 0; l < len out; l++) 392 out[l] = val; 393} 394 395dhcpsend(dfd: ref Sys->FD, dhcp: ref Dhcp) 396{ 397 buf := array[576] of byte; 398 buf[0] = byte dhcp.op; 399 buf[1] = byte dhcp.htype; 400 buf[2] = byte len dhcp.chaddr; 401 buf[3] = byte dhcp.hops; 402 nboputl(buf[4 : 8], dhcp.xid); 403 nboputs(buf[8 : 10], dhcp.secs); 404 nboputs(buf[10 : 12], dhcp.flags); 405 nboputl(buf[12 : 16], dhcp.ciaddr); 406 nboputl(buf[16 : 20], dhcp.yiaddr); 407 nboputl(buf[20 : 24], dhcp.siaddr); 408 nboputl(buf[24 : 28], dhcp.giaddr); 409 memset(buf[28 :], byte 0); 410 memncpy(buf[28 : 44], dhcp.chaddr); 411 memncpy(buf[44 : 108], array of byte dhcp.sname); 412 memncpy(buf[108 : 236], array of byte dhcp.file); 413 sys->write(dfd, buf, len buf); 414} 415 416kill(pid: int) 417{ 418 fd := sys->open("#p/" + string pid + "/ctl", sys->OWRITE); 419 if (fd == nil) 420 return; 421 422 msg := array of byte "kill"; 423 sys->write(fd, msg, len msg); 424} 425 426ipfmt(ipaddr: int): string 427{ 428 return sys->sprint("%ud.%ud.%ud.%ud", 429 (ipaddr >> 24) & 16rff, 430 (ipaddr >> 16) & 16rff, 431 (ipaddr >> 8) & 16rff, 432 ipaddr & 16rff); 433} 434 435dumpdhcp(dhcp: ref Dhcp) 436{ 437 sys->print("op %d htype %d hops %d xid %ud\n", dhcp.op, dhcp.htype, dhcp.hops, dhcp.xid); 438 sys->print("secs %d flags 0x%.4ux\n", dhcp.secs, dhcp.flags); 439 sys->print("ciaddr %s\n", ipfmt(dhcp.ciaddr)); 440 sys->print("yiaddr %s\n", ipfmt(dhcp.yiaddr)); 441 sys->print("siaddr %s\n", ipfmt(dhcp.siaddr)); 442 sys->print("giaddr %s\n", ipfmt(dhcp.giaddr)); 443 sys->print("chaddr "); 444 for (x := 0; x < len dhcp.chaddr; x++) 445 sys->print("%.2ux", int dhcp.chaddr[x]); 446 sys->print("\n"); 447 if (dhcp.sname != nil) 448 sys->print("sname %s\n", dhcp.sname); 449 if (dhcp.file != nil) 450 sys->print("file %s\n", dhcp.file); 451} 452 453dhcplisten(pidc: chan of int, fd: ref Sys->FD, dc: chan of ref Dhcp) 454{ 455 pid := sys->pctl(0, nil); 456 pidc <-= pid; 457 buf := array [576] of byte; 458 while (1) { 459 n := sys->read(fd, buf, len buf); 460 dhcp := ref Dhcp; 461 dhcp.op = int buf[0]; 462 dhcp.htype = int buf[1]; 463 hlen := int buf[2]; 464 dhcp.hops = int buf[3]; 465 dhcp.xid = nbogetl(buf[4 : 8]); 466 dhcp.secs = nbogets(buf[8 : 10]); 467 dhcp.flags = nbogets(buf[10 : 12]); 468 dhcp.ciaddr = nbogetl(buf[12 : 16]); 469 dhcp.yiaddr = nbogetl(buf[16 : 20]); 470 dhcp.siaddr = nbogetl(buf[20 : 24]); 471 dhcp.giaddr = nbogetl(buf[24: 28]); 472 dhcp.chaddr = buf[28 : 28 + hlen]; 473 dhcp.sname = stringget(buf[44 : 108]); 474 dhcp.file = stringget(buf[108 : 236]); 475 dc <-= dhcp; 476 } 477} 478 479timeoutproc(pid: chan of int, howlong: int, c: chan of string) 480{ 481 pid <-= sys->pctl(0, nil); 482 483 sys->sleep(howlong); 484 485 # send timeout 486 c <-= "timed out"; 487} 488 489tpid := -1; 490tc: chan of string; 491 492timeoutcancel() 493{ 494 if (tpid >= 0) { 495 kill(tpid); 496 tpid = -1; 497 } 498} 499 500timeoutstart(howlong: int): (chan of string) 501{ 502 timeoutcancel(); 503 pidc := chan of int; 504 tc = chan of string; 505 spawn timeoutproc(pidc, howlong, tc); 506 tpid = <- pidc; 507 return tc; 508} 509 510atohn(b: byte): int 511{ 512 if (b >= byte '0' && b <= byte '9') 513 return int (b - byte '0'); 514 if (b >= byte 'A' && b <= byte 'F') 515 return int b - 'A' + 10; 516 if (b >= byte 'a' && b <= byte 'f') 517 return int b - 'a' + 10; 518 return -1; 519} 520 521atohb(buf: array of byte): int 522{ 523 tn := atohn(buf[0]); 524 bn := atohn(buf[1]); 525 if (tn < 0 || bn < 0) 526 return -1; 527 return tn * 16 + bn; 528} 529 530gethaddr(dhcp: ref Dhcp): int 531{ 532 fd := sys->open("#l/ether0/addr", Sys->OREAD); 533 if (fd == nil) 534 return 0; 535 buf := array [100] of byte; 536 n := sys->read(fd, buf, len buf); 537 if (n < 0) 538 return 0; 539 dhcp.htype = 1; 540 hlen := n / 2; 541 dhcp.chaddr = array [hlen] of byte; 542 for (i := 0; i < hlen; i++) 543 dhcp.chaddr[i] = byte atohb(buf[i * 2 : i * 2 + 2]); 544 return 1; 545} 546 547parsedq(dq: string): (int, int) 548{ 549 (c, l) := sys->tokenize(dq, "."); 550 if (c != 4) 551 return (0, 0); 552 a := hd l; 553 l = tl l; 554 b := hd l; 555 l = tl l; 556 d := hd l; 557 l = tl l; 558 addr := (int a << 24) | (int b << 16) | (int d << 8) | int hd l; 559 return (1, addr); 560} 561 562dhcp() 563{ 564 ok: int; 565 conn: Sys->Connection; 566 rdhcp: ref Dhcp; 567 568 if (random == nil) 569 random = load Random Random->PATH; 570 571 (ok, conn) = sys->dial("udp!255.255.255.255!67", "68"); 572 if (!ok) 573 fatal(sys->sprint("failed to dial udp broadcast: %r")); 574 575 pidc := chan of int; 576 dc := chan of ref Dhcp; 577 spawn dhcplisten(pidc, conn.dfd, dc); 578 dhcppid := <- pidc; 579 dhcp := ref Dhcp; 580 dhcp.op = 1; 581 dhcp.htype = 1; 582 gethaddr(dhcp); 583 dhcp.hops = 0; 584 dhcp.xid = random->randomint(Random->NotQuiteRandom); 585 dhcp.secs = 0; 586 dhcp.flags = 0; 587 (ok, dhcp.ciaddr) = parsedq(ip); 588 dhcp.yiaddr = 0; 589 dhcp.siaddr = 0; 590 dhcp.giaddr = 0; 591 if (bootfile != "bootp") 592 dhcp.file = bootfile; 593 else 594 dhcp.file = nil; 595 ok = 0; 596 for (count := 0; !ok && count < 5; count++) { 597 mtc := timeoutstart(3000); 598 dhcpsend(conn.dfd, dhcp); 599 timedout := 0; 600 do { 601 alt { 602 <- mtc => 603 timedout = 1; 604 rdhcp = <- dc => 605 if (debug) 606 dumpdhcp(rdhcp); 607 if (rdhcp.ciaddr != dhcp.ciaddr || rdhcp.xid != dhcp.xid 608 || memcmp(rdhcp.chaddr, dhcp.chaddr) != 0) { 609 break; 610 } 611 if (rdhcp.file != nil) { 612 ok = 1; 613 timeoutcancel(); 614 } 615 } 616 } while (!timedout && !ok); 617 dhcp.xid++; 618 } 619 if (ok) { 620 if (bootfile == nil) 621 bootfile = rdhcp.file; 622 if (bootserver == nil) 623 bootserver = ipfmt(rdhcp.siaddr); 624 } 625 else 626 err("bootp timed out"); 627 kill(dhcppid); 628} 629