1 #include <u.h> 2 #include <libc.h> 3 #include <ip.h> 4 #include "dhcp.h" 5 6 void usage(void); 7 void bootpdump(uchar *p, int n); 8 int openlisten(char*); 9 void dhcpinit(void); 10 uchar *optadd(uchar*, int, void*, int); 11 uchar *optaddbyte(uchar*, int, int); 12 uchar *optaddulong(uchar*, int, ulong); 13 uchar *optaddaddr(uchar*, int, uchar*); 14 uchar *optget(Bootp*, int, int); 15 int optgetbyte(Bootp*, int); 16 ulong optgetulong(Bootp*, int); 17 int optgetaddr(Bootp*, int, uchar*); 18 void dhcpsend(int); 19 void dhcprecv(void); 20 void timerthread(void*); 21 void stdinthread(void*); 22 Bootp *parse(uchar*, int); 23 ulong thread(void(*f)(void*), void *a); 24 void myfatal(char *fmt, ...); 25 26 struct { 27 QLock lk; 28 int state; 29 int fd; 30 ulong xid; 31 ulong starttime; 32 char cid[100]; 33 char sname[64]; 34 uchar server[IPaddrlen]; /* server IP address */ 35 uchar client[IPaddrlen]; /* client IP address */ 36 uchar mask[IPaddrlen]; /* client mask */ 37 ulong lease; /* lease time */ 38 ulong resend; /* number of resends for current state */ 39 ulong timeout; /* time to timeout - seconds */ 40 } dhcp; 41 42 char net[64]; 43 44 char optmagic[4] = { 0x63, 0x82, 0x53, 0x63 }; 45 46 void 47 main(int argc, char *argv[]) 48 { 49 char *p; 50 51 setnetmtpt(net, sizeof(net), nil); 52 53 ARGBEGIN{ 54 case 'x': 55 p = ARGF(); 56 if(p == nil) 57 usage(); 58 setnetmtpt(net, sizeof(net), p); 59 }ARGEND; 60 61 fmtinstall('E', eipfmt); 62 fmtinstall('I', eipfmt); 63 fmtinstall('V', eipfmt); 64 65 dhcpinit(); 66 67 rfork(RFNOTEG|RFREND); 68 69 thread(timerthread, 0); 70 thread(stdinthread, 0); 71 72 qlock(&dhcp.lk); 73 dhcp.starttime = time(0); 74 dhcp.fd = openlisten(net); 75 dhcpsend(Discover); 76 dhcp.state = Sselecting; 77 dhcp.resend = 0; 78 dhcp.timeout = 4; 79 80 while(dhcp.state != Sbound) 81 dhcprecv(); 82 83 /* allows other clients on this machine */ 84 close(dhcp.fd); 85 dhcp.fd = -1; 86 87 print("ip=%I\n", dhcp.client); 88 print("mask=%I\n", dhcp.mask); 89 print("end\n"); 90 91 /* keep lease alive */ 92 for(;;) { 93 //fprint(2, "got lease for %d\n", dhcp.lease); 94 qunlock(&dhcp.lk); 95 sleep(dhcp.lease*500); // wait half of lease time 96 qlock(&dhcp.lk); 97 98 //fprint(2, "try renue\n", dhcp.lease); 99 dhcp.starttime = time(0); 100 dhcp.fd = openlisten(net); 101 dhcp.xid = time(0)*getpid(); 102 dhcpsend(Request); 103 dhcp.state = Srenewing; 104 dhcp.resend = 0; 105 dhcp.timeout = 1; 106 107 while(dhcp.state != Sbound) 108 dhcprecv(); 109 110 /* allows other clients on this machine */ 111 close(dhcp.fd); 112 dhcp.fd = -1; 113 } 114 } 115 116 void 117 usage(void) 118 { 119 fprint(2, "usage: %s [-x netextension]\n", argv0); 120 exits("usage"); 121 } 122 123 void 124 timerthread(void*) 125 { 126 for(;;) { 127 sleep(1000); 128 qlock(&dhcp.lk); 129 if(--dhcp.timeout > 0) { 130 qunlock(&dhcp.lk); 131 continue; 132 } 133 134 switch(dhcp.state) { 135 default: 136 myfatal("timerthread: unknown state %d", dhcp.state); 137 case Sinit: 138 break; 139 case Sselecting: 140 dhcpsend(Discover); 141 dhcp.timeout = 4; 142 dhcp.resend++; 143 if(dhcp.resend>5) 144 myfatal("dhcp: giving up: selecting"); 145 break; 146 case Srequesting: 147 dhcpsend(Request); 148 dhcp.timeout = 4; 149 dhcp.resend++; 150 if(dhcp.resend>5) 151 myfatal("dhcp: giving up: requesting"); 152 break; 153 case Srenewing: 154 dhcpsend(Request); 155 dhcp.timeout = 1; 156 dhcp.resend++; 157 if(dhcp.resend>3) { 158 dhcp.state = Srebinding; 159 dhcp.resend = 0; 160 } 161 break; 162 case Srebinding: 163 dhcpsend(Request); 164 dhcp.timeout = 4; 165 dhcp.resend++; 166 if(dhcp.resend>5) 167 myfatal("dhcp: giving up: rebinding"); 168 break; 169 case Sbound: 170 break; 171 } 172 qunlock(&dhcp.lk); 173 } 174 } 175 176 void 177 stdinthread(void*) 178 { 179 uchar buf[100]; 180 int n; 181 182 for(;;) { 183 n = read(0, buf, sizeof(buf)); 184 if(n <= 0) 185 break; 186 } 187 /* shutdown cleanly */ 188 qlock(&dhcp.lk); 189 if(dhcp.client) { 190 if(dhcp.fd < 0) 191 dhcp.fd = openlisten(net); 192 dhcpsend(Release); 193 } 194 qunlock(&dhcp.lk); 195 196 postnote(PNGROUP, getpid(), "die"); 197 exits(0); 198 } 199 200 void 201 dhcpinit(void) 202 { 203 int fd; 204 205 dhcp.state = Sinit; 206 dhcp.timeout = 4; 207 208 fd = open("/dev/random", 0); 209 if(fd >= 0) { 210 read(fd, &dhcp.xid, sizeof(dhcp.xid)); 211 close(fd); 212 } else 213 dhcp.xid = time(0)*getpid(); 214 srand(dhcp.xid); 215 216 sprint(dhcp.cid, "%s.%d", getenv("sysname"), getpid()); 217 } 218 219 void 220 dhcpsend(int type) 221 { 222 Bootp bp; 223 OUdphdr *up; 224 uchar *p; 225 int n; 226 227 memset(&bp, 0, sizeof(bp)); 228 up = (OUdphdr*)bp.udphdr; 229 230 hnputs(up->rport, 67); 231 bp.op = Bootrequest; 232 hnputl(bp.xid, dhcp.xid); 233 hnputs(bp.secs, time(0)-dhcp.starttime); 234 hnputs(bp.flags, Fbroadcast); // reply must be broadcast 235 memmove(bp.optmagic, optmagic, 4); 236 p = bp.optdata; 237 p = optaddbyte(p, ODtype, type); 238 p = optadd(p, ODclientid, dhcp.cid, strlen(dhcp.cid)); 239 switch(type) { 240 default: 241 myfatal("dhcpsend: unknown message type: %d", type); 242 case Discover: 243 ipmove(up->raddr, IPv4bcast); // broadcast 244 break; 245 case Request: 246 if(dhcp.state == Sbound || dhcp.state == Srenewing) 247 ipmove(up->raddr, dhcp.server); 248 else 249 ipmove(up->raddr, IPv4bcast); // broadcast 250 p = optaddulong(p, ODlease, dhcp.lease); 251 if(dhcp.state == Sselecting || dhcp.state == Srequesting) { 252 p = optaddaddr(p, ODipaddr, dhcp.client); // mistake?? 253 p = optaddaddr(p, ODserverid, dhcp.server); 254 } else 255 v6tov4(bp.ciaddr, dhcp.client); 256 break; 257 case Release: 258 ipmove(up->raddr, dhcp.server); 259 v6tov4(bp.ciaddr, dhcp.client); 260 p = optaddaddr(p, ODipaddr, dhcp.client); 261 p = optaddaddr(p, ODserverid, dhcp.server); 262 break; 263 } 264 265 *p++ = OBend; 266 267 n = p - (uchar*)&bp; 268 269 if(write(dhcp.fd, &bp, n) != n) 270 myfatal("dhcpsend: write failed: %r"); 271 } 272 273 void 274 dhcprecv(void) 275 { 276 uchar buf[2000]; 277 Bootp *bp; 278 int n, type; 279 ulong lease; 280 uchar mask[IPaddrlen]; 281 282 qunlock(&dhcp.lk); 283 n = read(dhcp.fd, buf, sizeof(buf)); 284 qlock(&dhcp.lk); 285 286 if(n <= 0) 287 myfatal("dhcprecv: bad read: %r"); 288 289 bp = parse(buf, n); 290 if(bp == 0) 291 return; 292 293 if(0) { 294 fprint(2, "recved\n"); 295 bootpdump(buf, n); 296 } 297 298 type = optgetbyte(bp, ODtype); 299 switch(type) { 300 default: 301 fprint(2, "dhcprecv: unknown type: %d\n", type); 302 break; 303 case Offer: 304 if(dhcp.state != Sselecting) 305 break; 306 lease = optgetulong(bp, ODlease); 307 if(lease == 0) 308 myfatal("bad lease"); 309 if(!optgetaddr(bp, OBmask, mask)) 310 memset(mask, 0xff, sizeof(mask)); 311 v4tov6(dhcp.client, bp->yiaddr); 312 if(!optgetaddr(bp, ODserverid, dhcp.server)) { 313 fprint(2, "dhcprecv: Offer from server with invalid serverid\n"); 314 break; 315 } 316 317 dhcp.lease = lease; 318 ipmove(dhcp.mask, mask); 319 memmove(dhcp.sname, bp->sname, sizeof(dhcp.sname)); 320 dhcp.sname[sizeof(dhcp.sname)-1] = 0; 321 322 dhcpsend(Request); 323 dhcp.state = Srequesting; 324 dhcp.resend = 0; 325 dhcp.timeout = 4; 326 break; 327 case Ack: 328 if(dhcp.state != Srequesting) 329 if(dhcp.state != Srenewing) 330 if(dhcp.state != Srebinding) 331 break; 332 lease = optgetulong(bp, ODlease); 333 if(lease == 0) 334 myfatal("bad lease"); 335 if(!optgetaddr(bp, OBmask, mask)) 336 memset(mask, 0xff, sizeof(mask)); 337 v4tov6(dhcp.client, bp->yiaddr); 338 dhcp.lease = lease; 339 ipmove(dhcp.mask, mask); 340 dhcp.state = Sbound; 341 break; 342 case Nak: 343 myfatal("recved nak"); 344 break; 345 } 346 347 } 348 349 int 350 openlisten(char *net) 351 { 352 int fd, cfd; 353 char data[128]; 354 char devdir[40]; 355 int n; 356 357 // sprint(data, "%s/udp!*!bootpc", net); 358 sprint(data, "%s/udp!*!68", net); 359 for(n=0;;n++) { 360 cfd = announce(data, devdir); 361 if(cfd >= 0) 362 break; 363 /* might be another client - wait and try again */ 364 fprint(2, "dhcpclient: can't announce: %r"); 365 sleep(1000); 366 if(n>10) 367 myfatal("can't announce: giving up: %r"); 368 } 369 370 if(fprint(cfd, "headers") < 0) 371 myfatal("can't set header mode: %r"); 372 fprint(cfd, "oldheaders"); 373 374 sprint(data, "%s/data", devdir); 375 376 fd = open(data, ORDWR); 377 if(fd < 0) 378 myfatal("open udp data: %r"); 379 close(cfd); 380 381 return fd; 382 } 383 384 uchar* 385 optadd(uchar *p, int op, void *d, int n) 386 { 387 p[0] = op; 388 p[1] = n; 389 memmove(p+2, d, n); 390 return p+n+2; 391 } 392 393 uchar* 394 optaddbyte(uchar *p, int op, int b) 395 { 396 p[0] = op; 397 p[1] = 1; 398 p[2] = b; 399 return p+3; 400 } 401 402 uchar* 403 optaddulong(uchar *p, int op, ulong x) 404 { 405 p[0] = op; 406 p[1] = 4; 407 hnputl(p+2, x); 408 return p+6; 409 } 410 411 uchar * 412 optaddaddr(uchar *p, int op, uchar *ip) 413 { 414 p[0] = op; 415 p[1] = 4; 416 v6tov4(p+2, ip); 417 return p+6; 418 } 419 420 uchar* 421 optget(Bootp *bp, int op, int n) 422 { 423 int len, code; 424 uchar *p; 425 426 p = bp->optdata; 427 for(;;) { 428 code = *p++; 429 if(code == OBpad) 430 continue; 431 if(code == OBend) 432 return 0; 433 len = *p++; 434 if(code != op) { 435 p += len; 436 continue; 437 } 438 if(n && n != len) 439 return 0; 440 return p; 441 } 442 } 443 444 445 int 446 optgetbyte(Bootp *bp, int op) 447 { 448 uchar *p; 449 450 p = optget(bp, op, 1); 451 if(p == 0) 452 return 0; 453 return *p; 454 } 455 456 ulong 457 optgetulong(Bootp *bp, int op) 458 { 459 uchar *p; 460 461 p = optget(bp, op, 4); 462 if(p == 0) 463 return 0; 464 return nhgetl(p); 465 } 466 467 int 468 optgetaddr(Bootp *bp, int op, uchar *ip) 469 { 470 uchar *p; 471 472 p = optget(bp, op, 4); 473 if(p == 0) 474 return 0; 475 v4tov6(ip, p); 476 return 1; 477 } 478 479 /* make sure packet looks ok */ 480 Bootp * 481 parse(uchar *p, int n) 482 { 483 int len, code; 484 Bootp *bp; 485 486 bp = (Bootp*)p; 487 if(n < bp->optmagic - p) { 488 fprint(2, "dhcpclient: parse: short bootp packet"); 489 return 0; 490 } 491 492 if(dhcp.xid != nhgetl(bp->xid)) { 493 fprint(2, "dhcpclient: parse: bad xid: got %ux expected %lux\n", nhgetl(bp->xid), dhcp.xid); 494 return 0; 495 } 496 497 if(bp->op != Bootreply) { 498 fprint(2, "dhcpclient: parse: bad op\n"); 499 return 0; 500 } 501 502 n -= bp->optmagic - p; 503 p = bp->optmagic; 504 505 if(n < 4) { 506 fprint(2, "dhcpclient: parse: not option data"); 507 return 0; 508 } 509 if(memcmp(optmagic, p, 4) != 0) { 510 fprint(2, "dhcpclient: parse: bad opt magic %ux %ux %ux %ux\n", p[0], p[1], p[2], p[3]); 511 return 0; 512 } 513 p += 4; 514 n -= 4; 515 while(n>0) { 516 code = *p++; 517 n--; 518 if(code == OBpad) 519 continue; 520 if(code == OBend) 521 return bp; 522 if(n == 0) { 523 fprint(2, "dhcpclient: parse: bad option: %d", code); 524 return 0; 525 } 526 len = *p++; 527 n--; 528 if(len > n) { 529 fprint(2, "dhcpclient: parse: bad option: %d", code); 530 return 0; 531 } 532 p += len; 533 n -= len; 534 } 535 536 /* fix up nonstandard packets */ 537 /* assume there is space */ 538 *p = OBend; 539 540 return bp; 541 } 542 543 void 544 bootpdump(uchar *p, int n) 545 { 546 Bootp *bp; 547 OUdphdr *up; 548 int len, i, code; 549 550 bp = (Bootp*)p; 551 up = (OUdphdr*)bp->udphdr; 552 553 if(n < bp->optmagic - p) { 554 fprint(2, "dhcpclient: short bootp packet"); 555 return; 556 } 557 558 fprint(2, "laddr=%I lport=%d raddr=%I rport=%d\n", up->laddr, nhgets(up->lport), 559 up->raddr, nhgets(up->rport)); 560 fprint(2, "op=%d htype=%d hlen=%d hops=%d\n", bp->op, bp->htype, bp->hlen, bp->hops); 561 fprint(2, "xid=%ux secs=%d flags=%ux\n", nhgetl(bp->xid), nhgets(bp->secs), nhgets(bp->flags)); 562 fprint(2, "ciaddr=%V yiaddr=%V siaddr=%V giaddr=%V\n", 563 bp->ciaddr, bp->yiaddr, bp->siaddr, bp->giaddr); 564 fprint(2, "chaddr="); 565 for(i=0; i<16; i++) 566 fprint(2, "%ux ", bp->chaddr[i]); 567 fprint(2, "\n"); 568 fprint(2, "sname=%s\n", bp->sname); 569 fprint(2, "file = %s\n", bp->file); 570 571 n -= bp->optmagic - p; 572 p = bp->optmagic; 573 574 if(n < 4) 575 return; 576 if(memcmp(optmagic, p, 4) != 0) 577 fprint(2, "dhcpclient: bad opt magic %ux %ux %ux %ux\n", p[0], p[1], p[2], p[3]); 578 p += 4; 579 n -= 4; 580 581 while(n>0) { 582 code = *p++; 583 n--; 584 if(code == OBpad) 585 continue; 586 if(code == OBend) 587 break; 588 if(n == 0) { 589 fprint(2, " bad option: %d", code); 590 return; 591 } 592 len = *p++; 593 n--; 594 if(len > n) { 595 fprint(2, " bad option: %d", code); 596 return; 597 } 598 switch(code) { 599 default: 600 fprint(2, "unknown option %d\n", code); 601 for(i = 0; i<len; i++) 602 fprint(2, "%ux ", p[i]); 603 case ODtype: 604 fprint(2, "DHCP type %d\n", p[0]); 605 break; 606 case ODclientid: 607 fprint(2, "client id="); 608 for(i = 0; i<len; i++) 609 fprint(2, "%ux ", p[i]); 610 fprint(2, "\n"); 611 break; 612 case ODlease: 613 fprint(2, "lease=%d\n", nhgetl(p)); 614 break; 615 case ODserverid: 616 fprint(2, "server id=%V\n", p); 617 break; 618 case OBmask: 619 fprint(2, "mask=%V\n", p); 620 break; 621 case OBrouter: 622 fprint(2, "router=%V\n", p); 623 break; 624 } 625 p += len; 626 n -= len; 627 } 628 } 629 630 ulong 631 thread(void(*f)(void*), void *a) 632 { 633 int pid; 634 pid=rfork(RFNOWAIT|RFMEM|RFPROC); 635 if(pid < 0) 636 myfatal("rfork failed: %r"); 637 if(pid != 0) 638 return pid; 639 (*f)(a); 640 return 0; // never reaches here 641 } 642 643 void 644 myfatal(char *fmt, ...) 645 { 646 char buf[1024]; 647 va_list arg; 648 649 va_start(arg, fmt); 650 vseprint(buf, buf+sizeof(buf), fmt, arg); 651 va_end(arg); 652 fprint(2, "%s: %s\n", argv0, buf); 653 postnote(PNGROUP, getpid(), "die"); 654 exits(buf); 655 } 656