1 #include <u.h> 2 #include <libc.h> 3 #include <ip.h> 4 #include "dhcp.h" 5 6 void bootpdump(uchar *p, int n); 7 void dhcpinit(void); 8 void dhcprecv(void); 9 void dhcpsend(int); 10 void myfatal(char *fmt, ...); 11 int openlisten(char*); 12 uchar *optaddaddr(uchar*, int, uchar*); 13 uchar *optaddbyte(uchar*, int, int); 14 uchar *optadd(uchar*, int, void*, int); 15 uchar *optaddulong(uchar*, int, ulong); 16 uchar *optget(Bootp*, int, int); 17 int optgetaddr(Bootp*, int, uchar*); 18 int optgetbyte(Bootp*, int); 19 ulong optgetulong(Bootp*, int); 20 Bootp *parse(uchar*, int); 21 void stdinthread(void*); 22 ulong thread(void(*f)(void*), void *a); 23 void timerthread(void*); 24 void usage(void); 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 int n; 223 uchar *p; 224 Bootp bp; 225 Udphdr *up; 226 227 memset(&bp, 0, sizeof bp); 228 up = (Udphdr*)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(1) { 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 n, fd, cfd; 353 char data[128], devdir[40]; 354 355 // sprint(data, "%s/udp!*!bootpc", net); 356 sprint(data, "%s/udp!*!68", net); 357 for(n = 0; ; n++) { 358 cfd = announce(data, devdir); 359 if(cfd >= 0) 360 break; 361 /* might be another client - wait and try again */ 362 fprint(2, "dhcpclient: can't announce %s: %r", data); 363 sleep(1000); 364 if(n > 10) 365 myfatal("can't announce: giving up: %r"); 366 } 367 368 if(fprint(cfd, "headers") < 0) 369 myfatal("can't set header mode: %r"); 370 371 sprint(data, "%s/data", devdir); 372 fd = open(data, ORDWR); 373 if(fd < 0) 374 myfatal("open %s: %r", data); 375 close(cfd); 376 return fd; 377 } 378 379 uchar* 380 optadd(uchar *p, int op, void *d, int n) 381 { 382 p[0] = op; 383 p[1] = n; 384 memmove(p+2, d, n); 385 return p+n+2; 386 } 387 388 uchar* 389 optaddbyte(uchar *p, int op, int b) 390 { 391 p[0] = op; 392 p[1] = 1; 393 p[2] = b; 394 return p+3; 395 } 396 397 uchar* 398 optaddulong(uchar *p, int op, ulong x) 399 { 400 p[0] = op; 401 p[1] = 4; 402 hnputl(p+2, x); 403 return p+6; 404 } 405 406 uchar * 407 optaddaddr(uchar *p, int op, uchar *ip) 408 { 409 p[0] = op; 410 p[1] = 4; 411 v6tov4(p+2, ip); 412 return p+6; 413 } 414 415 uchar* 416 optget(Bootp *bp, int op, int n) 417 { 418 int len, code; 419 uchar *p; 420 421 p = bp->optdata; 422 for(;;) { 423 code = *p++; 424 if(code == OBpad) 425 continue; 426 if(code == OBend) 427 return 0; 428 len = *p++; 429 if(code != op) { 430 p += len; 431 continue; 432 } 433 if(n && n != len) 434 return 0; 435 return p; 436 } 437 } 438 439 440 int 441 optgetbyte(Bootp *bp, int op) 442 { 443 uchar *p; 444 445 p = optget(bp, op, 1); 446 if(p == 0) 447 return 0; 448 return *p; 449 } 450 451 ulong 452 optgetulong(Bootp *bp, int op) 453 { 454 uchar *p; 455 456 p = optget(bp, op, 4); 457 if(p == 0) 458 return 0; 459 return nhgetl(p); 460 } 461 462 int 463 optgetaddr(Bootp *bp, int op, uchar *ip) 464 { 465 uchar *p; 466 467 p = optget(bp, op, 4); 468 if(p == 0) 469 return 0; 470 v4tov6(ip, p); 471 return 1; 472 } 473 474 /* make sure packet looks ok */ 475 Bootp * 476 parse(uchar *p, int n) 477 { 478 int len, code; 479 Bootp *bp; 480 481 bp = (Bootp*)p; 482 if(n < bp->optmagic - p) { 483 fprint(2, "dhcpclient: parse: short bootp packet"); 484 return 0; 485 } 486 487 if(dhcp.xid != nhgetl(bp->xid)) { 488 fprint(2, "dhcpclient: parse: bad xid: got %ux expected %lux\n", 489 nhgetl(bp->xid), dhcp.xid); 490 return 0; 491 } 492 493 if(bp->op != Bootreply) { 494 fprint(2, "dhcpclient: parse: bad op\n"); 495 return 0; 496 } 497 498 n -= bp->optmagic - p; 499 p = bp->optmagic; 500 501 if(n < 4) { 502 fprint(2, "dhcpclient: parse: not option data"); 503 return 0; 504 } 505 if(memcmp(optmagic, p, 4) != 0) { 506 fprint(2, "dhcpclient: parse: bad opt magic %ux %ux %ux %ux\n", 507 p[0], p[1], p[2], p[3]); 508 return 0; 509 } 510 p += 4; 511 n -= 4; 512 while(n>0) { 513 code = *p++; 514 n--; 515 if(code == OBpad) 516 continue; 517 if(code == OBend) 518 return bp; 519 if(n == 0) { 520 fprint(2, "dhcpclient: parse: bad option: %d", code); 521 return 0; 522 } 523 len = *p++; 524 n--; 525 if(len > n) { 526 fprint(2, "dhcpclient: parse: bad option: %d", code); 527 return 0; 528 } 529 p += len; 530 n -= len; 531 } 532 533 /* fix up nonstandard packets */ 534 /* assume there is space */ 535 *p = OBend; 536 537 return bp; 538 } 539 540 void 541 bootpdump(uchar *p, int n) 542 { 543 int len, i, code; 544 Bootp *bp; 545 Udphdr *up; 546 547 bp = (Bootp*)p; 548 up = (Udphdr*)bp->udphdr; 549 550 if(n < bp->optmagic - p) { 551 fprint(2, "dhcpclient: short bootp packet"); 552 return; 553 } 554 555 fprint(2, "laddr=%I lport=%d raddr=%I rport=%d\n", up->laddr, 556 nhgets(up->lport), up->raddr, nhgets(up->rport)); 557 fprint(2, "op=%d htype=%d hlen=%d hops=%d\n", bp->op, bp->htype, 558 bp->hlen, bp->hops); 559 fprint(2, "xid=%ux secs=%d flags=%ux\n", nhgetl(bp->xid), 560 nhgets(bp->secs), nhgets(bp->flags)); 561 fprint(2, "ciaddr=%V yiaddr=%V siaddr=%V giaddr=%V\n", 562 bp->ciaddr, bp->yiaddr, bp->siaddr, bp->giaddr); 563 fprint(2, "chaddr="); 564 for(i=0; i<16; i++) 565 fprint(2, "%ux ", bp->chaddr[i]); 566 fprint(2, "\n"); 567 fprint(2, "sname=%s\n", bp->sname); 568 fprint(2, "file = %s\n", bp->file); 569 570 n -= bp->optmagic - p; 571 p = bp->optmagic; 572 573 if(n < 4) 574 return; 575 if(memcmp(optmagic, p, 4) != 0) 576 fprint(2, "dhcpclient: bad opt magic %ux %ux %ux %ux\n", 577 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 635 pid = rfork(RFNOWAIT|RFMEM|RFPROC); 636 if(pid < 0) 637 myfatal("rfork failed: %r"); 638 if(pid != 0) 639 return pid; 640 (*f)(a); 641 return 0; /* never reaches here */ 642 } 643 644 void 645 myfatal(char *fmt, ...) 646 { 647 char buf[1024]; 648 va_list arg; 649 650 va_start(arg, fmt); 651 vseprint(buf, buf+sizeof(buf), fmt, arg); 652 va_end(arg); 653 fprint(2, "%s: %s\n", argv0, buf); 654 postnote(PNGROUP, getpid(), "die"); 655 exits(buf); 656 } 657