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 Udphdr *up; 224 uchar *p; 225 int n; 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(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 373 sprint(data, "%s/data", devdir); 374 375 fd = open(data, ORDWR); 376 if(fd < 0) 377 myfatal("open udp data: %r"); 378 close(cfd); 379 380 return fd; 381 } 382 383 uchar* 384 optadd(uchar *p, int op, void *d, int n) 385 { 386 p[0] = op; 387 p[1] = n; 388 memmove(p+2, d, n); 389 return p+n+2; 390 } 391 392 uchar* 393 optaddbyte(uchar *p, int op, int b) 394 { 395 p[0] = op; 396 p[1] = 1; 397 p[2] = b; 398 return p+3; 399 } 400 401 uchar* 402 optaddulong(uchar *p, int op, ulong x) 403 { 404 p[0] = op; 405 p[1] = 4; 406 hnputl(p+2, x); 407 return p+6; 408 } 409 410 uchar * 411 optaddaddr(uchar *p, int op, uchar *ip) 412 { 413 p[0] = op; 414 p[1] = 4; 415 v6tov4(p+2, ip); 416 return p+6; 417 } 418 419 uchar* 420 optget(Bootp *bp, int op, int n) 421 { 422 int len, code; 423 uchar *p; 424 425 p = bp->optdata; 426 for(;;) { 427 code = *p++; 428 if(code == OBpad) 429 continue; 430 if(code == OBend) 431 return 0; 432 len = *p++; 433 if(code != op) { 434 p += len; 435 continue; 436 } 437 if(n && n != len) 438 return 0; 439 return p; 440 } 441 } 442 443 444 int 445 optgetbyte(Bootp *bp, int op) 446 { 447 uchar *p; 448 449 p = optget(bp, op, 1); 450 if(p == 0) 451 return 0; 452 return *p; 453 } 454 455 ulong 456 optgetulong(Bootp *bp, int op) 457 { 458 uchar *p; 459 460 p = optget(bp, op, 4); 461 if(p == 0) 462 return 0; 463 return nhgetl(p); 464 } 465 466 int 467 optgetaddr(Bootp *bp, int op, uchar *ip) 468 { 469 uchar *p; 470 471 p = optget(bp, op, 4); 472 if(p == 0) 473 return 0; 474 v4tov6(ip, p); 475 return 1; 476 } 477 478 /* make sure packet looks ok */ 479 Bootp * 480 parse(uchar *p, int n) 481 { 482 int len, code; 483 Bootp *bp; 484 485 bp = (Bootp*)p; 486 if(n < bp->optmagic - p) { 487 fprint(2, "dhcpclient: parse: short bootp packet"); 488 return 0; 489 } 490 491 if(dhcp.xid != nhgetl(bp->xid)) { 492 fprint(2, "dhcpclient: parse: bad xid: got %ux expected %lux\n", nhgetl(bp->xid), dhcp.xid); 493 return 0; 494 } 495 496 if(bp->op != Bootreply) { 497 fprint(2, "dhcpclient: parse: bad op\n"); 498 return 0; 499 } 500 501 n -= bp->optmagic - p; 502 p = bp->optmagic; 503 504 if(n < 4) { 505 fprint(2, "dhcpclient: parse: not option data"); 506 return 0; 507 } 508 if(memcmp(optmagic, p, 4) != 0) { 509 fprint(2, "dhcpclient: parse: bad opt magic %ux %ux %ux %ux\n", p[0], p[1], p[2], p[3]); 510 return 0; 511 } 512 p += 4; 513 n -= 4; 514 while(n>0) { 515 code = *p++; 516 n--; 517 if(code == OBpad) 518 continue; 519 if(code == OBend) 520 return bp; 521 if(n == 0) { 522 fprint(2, "dhcpclient: parse: bad option: %d", code); 523 return 0; 524 } 525 len = *p++; 526 n--; 527 if(len > n) { 528 fprint(2, "dhcpclient: parse: bad option: %d", code); 529 return 0; 530 } 531 p += len; 532 n -= len; 533 } 534 535 /* fix up nonstandard packets */ 536 /* assume there is space */ 537 *p = OBend; 538 539 return bp; 540 } 541 542 void 543 bootpdump(uchar *p, int n) 544 { 545 Bootp *bp; 546 Udphdr *up; 547 int len, i, code; 548 549 bp = (Bootp*)p; 550 up = (Udphdr*)bp->udphdr; 551 552 if(n < bp->optmagic - p) { 553 fprint(2, "dhcpclient: short bootp packet"); 554 return; 555 } 556 557 fprint(2, "laddr=%I lport=%d raddr=%I rport=%d\n", up->laddr, nhgets(up->lport), 558 up->raddr, nhgets(up->rport)); 559 fprint(2, "op=%d htype=%d hlen=%d hops=%d\n", bp->op, bp->htype, bp->hlen, bp->hops); 560 fprint(2, "xid=%ux secs=%d flags=%ux\n", nhgetl(bp->xid), 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", p[0], p[1], p[2], p[3]); 577 p += 4; 578 n -= 4; 579 580 while(n>0) { 581 code = *p++; 582 n--; 583 if(code == OBpad) 584 continue; 585 if(code == OBend) 586 break; 587 if(n == 0) { 588 fprint(2, " bad option: %d", code); 589 return; 590 } 591 len = *p++; 592 n--; 593 if(len > n) { 594 fprint(2, " bad option: %d", code); 595 return; 596 } 597 switch(code) { 598 default: 599 fprint(2, "unknown option %d\n", code); 600 for(i = 0; i<len; i++) 601 fprint(2, "%ux ", p[i]); 602 case ODtype: 603 fprint(2, "DHCP type %d\n", p[0]); 604 break; 605 case ODclientid: 606 fprint(2, "client id="); 607 for(i = 0; i<len; i++) 608 fprint(2, "%ux ", p[i]); 609 fprint(2, "\n"); 610 break; 611 case ODlease: 612 fprint(2, "lease=%d\n", nhgetl(p)); 613 break; 614 case ODserverid: 615 fprint(2, "server id=%V\n", p); 616 break; 617 case OBmask: 618 fprint(2, "mask=%V\n", p); 619 break; 620 case OBrouter: 621 fprint(2, "router=%V\n", p); 622 break; 623 } 624 p += len; 625 n -= len; 626 } 627 } 628 629 ulong 630 thread(void(*f)(void*), void *a) 631 { 632 int pid; 633 pid=rfork(RFNOWAIT|RFMEM|RFPROC); 634 if(pid < 0) 635 myfatal("rfork failed: %r"); 636 if(pid != 0) 637 return pid; 638 (*f)(a); 639 return 0; // never reaches here 640 } 641 642 void 643 myfatal(char *fmt, ...) 644 { 645 char buf[1024]; 646 va_list arg; 647 648 va_start(arg, fmt); 649 vseprint(buf, buf+sizeof(buf), fmt, arg); 650 va_end(arg); 651 fprint(2, "%s: %s\n", argv0, buf); 652 postnote(PNGROUP, getpid(), "die"); 653 exits(buf); 654 } 655