1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * 25 * Standalone dhcp client. 26 */ 27 28 #pragma ident "%Z%%M% %I% %E% SMI" 29 30 #include <sys/types.h> 31 #include <sys/salib.h> 32 #include <sys/bootconf.h> 33 #include <sys/bootcmn.h> 34 #include <sys/socket.h> 35 #include <sys/isa_defs.h> 36 #include <netinet/in.h> 37 #include <netinet/in_systm.h> 38 #include <netinet/inetutil.h> 39 #include <netinet/dhcp.h> 40 #include <netinet/ip.h> 41 #include <netinet/udp.h> 42 #include <dhcp_impl.h> 43 #include <net/if_types.h> 44 #include <sys/promif.h> 45 #include <sys/platnames.h> 46 #include <socket_inet.h> 47 48 #include "ipv4.h" 49 #include "mac.h" 50 #include <sys/bootdebug.h> 51 #include "dhcpv4.h" 52 53 static char *s_n = "INIT"; 54 static enum DHCPSTATE dhcp_state = INIT; 55 56 static PKT *dhcp_snd_bufp, *dhcp_rcv_bufp; 57 static int dhcp_buf_size; 58 59 static const uint8_t magic[] = BOOTMAGIC; /* RFC1048 */ 60 static uint8_t opt_discover[] = { CD_DHCP_TYPE, 1, DISCOVER }; 61 static uint8_t opt_request[] = { CD_DHCP_TYPE, 1, REQUEST }; 62 static uint8_t opt_decline[] = { CD_DHCP_TYPE, 1, DECLINE }; 63 64 static uint8_t dhcp_classid[DHCP_MAX_OPT_SIZE + 3]; 65 static uint8_t dhcp_clientid[DHCP_MAX_CID_LEN]; 66 static uint8_t dhcp_clientid_len = 0; 67 68 static uint32_t dhcp_start_time; /* start time (msecs */ 69 static time_t dhcp_secs; 70 static uint32_t timeout; /* timeout in milliseconds */ 71 72 static int pkt_counter; 73 PKT_LIST *list_tl, *list_hd; 74 PKT_LIST *state_pl = NULL; 75 76 #define PROM_BOOT_CACHED "bootp-response" 77 extern char *bootp_response; /* bootprop.c */ 78 extern int pagesize; 79 80 /* 81 * Do whatever reset actions/initialization actions are generic for every 82 * DHCP/bootp message. Set the message type. 83 * 84 * Returns: the updated options ptr. 85 */ 86 static uint8_t * 87 init_msg(PKT *pkt, uint8_t *pkttype) 88 { 89 static uint32_t xid; 90 91 bzero(pkt, dhcp_buf_size); 92 bcopy(magic, pkt->cookie, sizeof (pkt->cookie)); 93 pkt->op = BOOTREQUEST; 94 if (xid == 0) 95 bcopy(mac_get_addr_buf()+2, &xid, 4); 96 else 97 xid++; 98 pkt->xid = xid; 99 bcopy(pkttype, pkt->options, 3); 100 return ((uint8_t *)(pkt->options + 3)); 101 } 102 103 /* 104 * Parameter request list. 105 */ 106 static void 107 parameter_request_list(uint8_t **opt) 108 { 109 /* 110 * This parameter request list is used in the normal 4-packet 111 * DHCPDISCOVER/OFFER/REQUEST/ACK exchange; it must not contain 112 * CD_REQUESTED_IP_ADDR or CD_LEASE_TIME. 113 */ 114 static uint8_t prlist[] = { 115 CD_REQUEST_LIST, /* parameter request list option number */ 116 4, /* number of options requested */ 117 CD_SUBNETMASK, 118 CD_ROUTER, 119 CD_HOSTNAME, 120 CD_VENDOR_SPEC 121 }; 122 if (opt && *opt) { 123 bcopy(prlist, *opt, sizeof (prlist)); 124 *opt += sizeof (prlist); 125 } 126 } 127 128 /* 129 * Set hardware specific fields 130 */ 131 static void 132 set_hw_spec_data(PKT *p, uint8_t **opt, uint8_t *pkttype) 133 { 134 char mfg[DHCP_MAX_OPT_SIZE + 1], cbuf[DHCP_MAX_OPT_SIZE + 1]; 135 uint8_t *tp, *dp; 136 int adjust_len, len, i; 137 138 p->htype = mac_arp_type(mac_get_type()); 139 len = (uchar_t)mac_get_addr_len(); 140 if (len <= sizeof (p->chaddr)) { 141 p->hlen = len; 142 bcopy(mac_get_addr_buf(), p->chaddr, len); 143 } else { 144 uint8_t type = *(pkttype + 2); 145 /* 146 * The mac address does not fit in the chaddr 147 * field, thus it can not be sent to the server, 148 * thus server can not unicast the reply. Per 149 * RFC 2131 4.4.1, client can set this bit in 150 * DISCOVER/REQUEST. 151 */ 152 if ((type == DISCOVER) || (type == REQUEST)) 153 p->flags = htons(BCAST_MASK); 154 } 155 156 if (opt && *opt) { 157 if (dhcp_classid[0] == '\0') { 158 /* 159 * Classids based on mfg name: Commas (,) are 160 * converted to periods (.), and spaces ( ) are removed. 161 */ 162 dhcp_classid[0] = CD_CLASS_ID; 163 164 (void) strncpy(mfg, get_mfg_name(), sizeof (mfg)); 165 if (strncmp(mfg, "SUNW", strlen("SUNW")) != 0) { 166 len = strlen("SUNW."); 167 (void) strcpy(cbuf, "SUNW."); 168 } else { 169 len = 0; 170 cbuf[0] = '\0'; 171 } 172 len += strlen(mfg); 173 174 if ((len + 2) < DHCP_MAX_OPT_SIZE) { 175 tp = (uint8_t *)strcat(cbuf, mfg); 176 dp = &dhcp_classid[2]; 177 adjust_len = 0; 178 for (i = 0; i < len; i++, tp++) { 179 if (*tp == ',') { 180 *dp++ = '.'; 181 } else if (*tp == ' ') { 182 adjust_len++; 183 } else { 184 *dp++ = *tp; 185 } 186 } 187 len -= adjust_len; 188 dhcp_classid[1] = (uint8_t)len; 189 } else 190 prom_panic("Not enough space for class id"); 191 #ifdef DHCP_DEBUG 192 printf("%s: Classid: %s\n", s_n, &dhcp_classid[2]); 193 #endif /* DHCP_DEBUG */ 194 } 195 bcopy(dhcp_classid, *opt, dhcp_classid[1] + 2); 196 *opt += dhcp_classid[1] + 2; 197 } 198 } 199 200 static void 201 flush_list(void) 202 { 203 PKT_LIST *wk, *tmp; 204 205 wk = list_hd; 206 while (wk != NULL) { 207 tmp = wk; 208 wk = wk->next; 209 bkmem_free((char *)tmp->pkt, tmp->len); 210 bkmem_free((char *)tmp, sizeof (PKT_LIST)); 211 } 212 list_hd = list_tl = NULL; 213 pkt_counter = 0; 214 } 215 216 static void 217 remove_list(PKT_LIST *pl, int flag) 218 { 219 if (list_hd == NULL) 220 return; 221 222 if (list_hd == list_tl) { 223 list_hd = list_tl = NULL; 224 } else if (list_hd == pl) { 225 list_hd = pl->next; 226 list_hd->prev = NULL; 227 } else if (list_tl == pl) { 228 list_tl = list_tl->prev; 229 list_tl->next = NULL; 230 } else { 231 pl->prev->next = pl->next; 232 pl->next->prev = pl->prev; 233 } 234 pkt_counter--; 235 if (flag) { 236 bkmem_free((char *)pl->pkt, pl->len); 237 bkmem_free((char *)pl, sizeof (PKT_LIST)); 238 } 239 } 240 241 /* 242 * Collects BOOTP responses. Length has to be right, it has to be 243 * a BOOTP reply pkt, with the same XID and HW address as ours. Adds 244 * them to the pkt list. 245 * 246 * Returns 0 if no error processing packet, 1 if an error occurred and/or 247 * collection of replies should stop. Used in inet() calls. 248 */ 249 static int 250 bootp_collect(int len) 251 { 252 PKT *s = (PKT *)dhcp_snd_bufp; 253 PKT *r = (PKT *)dhcp_rcv_bufp; 254 PKT_LIST *pl; 255 256 if (len < sizeof (PKT)) { 257 dprintf("%s: BOOTP reply too small: %d\n", s_n, len); 258 return (1); 259 } 260 if (r->op == BOOTREPLY && r->xid == s->xid && 261 bcmp((caddr_t)s->chaddr, (caddr_t)r->chaddr, s->hlen) == 0) { 262 /* Add a packet to the pkt list */ 263 if (pkt_counter > (MAX_PKT_LIST - 1)) 264 return (1); /* got enough packets already */ 265 if (((pl = (PKT_LIST *)bkmem_zalloc(sizeof (PKT_LIST))) == 266 NULL) || ((pl->pkt = (PKT *)bkmem_zalloc(len)) == NULL)) { 267 errno = ENOMEM; 268 if (pl != NULL) 269 bkmem_free((char *)pl, sizeof (PKT_LIST)); 270 return (1); 271 } 272 bcopy(dhcp_rcv_bufp, pl->pkt, len); 273 pl->len = len; 274 if (list_hd == NULL) { 275 list_hd = list_tl = pl; 276 pl->prev = NULL; 277 } else { 278 list_tl->next = pl; 279 pl->prev = list_tl; 280 list_tl = pl; 281 } 282 pkt_counter++; 283 pl->next = NULL; 284 } 285 return (0); 286 } 287 288 /* 289 * Checks if BOOTP exchange(s) were successful. Returns 1 if they 290 * were, 0 otherwise. Used in inet() calls. 291 */ 292 static int 293 bootp_success(void) 294 { 295 PKT *s = (PKT *)dhcp_snd_bufp; 296 297 if (list_hd != NULL) { 298 /* remember the secs - we may need them later */ 299 dhcp_secs = ntohs(s->secs); 300 return (1); 301 } 302 s->secs = htons((uint16_t)((prom_gettime() - dhcp_start_time)/1000)); 303 return (0); 304 } 305 306 /* 307 * This function accesses the network. Opens a connection, and binds to 308 * it if a client binding doesn't already exist. If 'tries' is 0, then 309 * no reply is expected/returned. If 'tries' is non-zero, then 'tries' 310 * attempts are made to get a valid response. If 'tol' is not zero, 311 * then this function will wait for 'tol' milliseconds for more than one 312 * response to a transmit. 313 * 314 * Returns 0 for success, errno otherwise. 315 */ 316 static int 317 inet(uint32_t size, struct in_addr *src, struct in_addr *dest, uint32_t tries, 318 uint32_t tol) 319 { 320 int done = B_FALSE, flags, len; 321 uint32_t attempts = 0; 322 int sd; 323 uint32_t wait_time; /* Max time collect replies */ 324 uint32_t init_timeout; /* Max time wait ANY reply */ 325 uint32_t now; 326 struct sockaddr_in saddr, daddr; 327 328 if ((sd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 329 dprintf("%s: Can't open a socket.\n", s_n); 330 return (errno); 331 } 332 333 flags = 0; 334 335 bzero(&saddr, sizeof (struct sockaddr_in)); 336 saddr.sin_family = AF_INET; 337 saddr.sin_port = htons(IPPORT_BOOTPC); 338 saddr.sin_addr.s_addr = htonl(src->s_addr); 339 340 if (bind(sd, (struct sockaddr *)&saddr, sizeof (saddr)) < 0) { 341 dprintf("%s: Cannot bind to port %d, errno: %d\n", 342 s_n, IPPORT_BOOTPC, errno); 343 (void) socket_close(sd); 344 return (errno); 345 } 346 347 if (ntohl(dest->s_addr) == INADDR_BROADCAST) { 348 int dontroute = B_TRUE; 349 (void) setsockopt(sd, SOL_SOCKET, SO_DONTROUTE, 350 (const void *)&dontroute, sizeof (dontroute)); 351 } 352 353 bzero(&daddr, sizeof (struct sockaddr_in)); 354 daddr.sin_family = AF_INET; 355 daddr.sin_port = htons(IPPORT_BOOTPS); 356 daddr.sin_addr.s_addr = htonl(dest->s_addr); 357 wait_time = prom_gettime() + tol; 358 359 do { 360 if (sendto(sd, (char *)dhcp_snd_bufp, size, flags, 361 (struct sockaddr *)&daddr, sizeof (daddr)) < 0) { 362 dprintf("%s: sendto failed with errno: %d\n", 363 s_n, errno); 364 (void) socket_close(sd); 365 return (errno); 366 } 367 if (!tries) 368 break; /* don't bother to check for reply */ 369 370 now = prom_gettime(); 371 if (timeout == 0) 372 timeout = 4000; 373 else { 374 timeout <<= 1; 375 if (timeout > 64000) 376 timeout = 64000; 377 } 378 init_timeout = now + timeout; 379 wait_time = now + tol; 380 do { 381 if ((len = recvfrom(sd, (char *)dhcp_rcv_bufp, 382 (int)dhcp_buf_size, MSG_DONTWAIT, NULL, 383 NULL)) < 0) { 384 if (errno == EWOULDBLOCK) 385 continue; /* DONT WAIT */ 386 (void) socket_close(sd); 387 flush_list(); 388 return (errno); 389 } 390 391 if (bootp_collect(len)) 392 break; /* Stop collecting */ 393 394 if (tol != 0) { 395 if (wait_time < prom_gettime()) 396 break; /* collection timeout */ 397 } 398 } while (prom_gettime() < init_timeout); 399 400 if (bootp_success()) { 401 done = B_TRUE; 402 break; /* got the goods */ 403 } 404 } while (++attempts < tries); 405 406 (void) socket_close(sd); 407 408 return (done ? 0 : DHCP_NO_DATA); 409 } 410 411 /* 412 * Print the message from the server. 413 */ 414 static void 415 prt_server_msg(DHCP_OPT *p) 416 { 417 int len = p->len; 418 char scratch[DHCP_MAX_OPT_SIZE + 1]; 419 420 if (len > DHCP_MAX_OPT_SIZE) 421 len = DHCP_MAX_OPT_SIZE; 422 bcopy(p->value, scratch, len); 423 scratch[len] = '\0'; 424 printf("%s: Message from server: '%s'\n", s_n, scratch); 425 } 426 427 /* 428 * This function scans the list of OFFERS, and returns the "best" offer. 429 * The criteria used for determining this is: 430 * 431 * The best: 432 * DHCP OFFER (not BOOTP), same client_id as ours, same class_id, 433 * Longest lease, all the options we need. 434 * 435 * Not quite as good: 436 * DHCP OFFER, no class_id, short lease, only some of the options we need. 437 * 438 * We're really reach'in 439 * BOOTP reply. 440 * 441 * DON'T select an offer from a server that gave us a configuration we 442 * couldn't use. Take this server off the "bad" list when this is done. 443 * Next time, we could potentially retry this server's configuration. 444 * 445 * NOTE: perhaps this bad server should have a counter associated with it. 446 */ 447 static PKT_LIST * 448 select_best(void) 449 { 450 PKT_LIST *wk, *tk, *best; 451 int err = 0; 452 453 /* Pass one. Scan for options, set appropriate opt field. */ 454 wk = list_hd; 455 while (wk != NULL) { 456 if ((err = dhcp_options_scan(wk, B_TRUE)) != 0) { 457 /* Garbled Options. Nuke this pkt. */ 458 if (boothowto & RB_DEBUG) { 459 switch (err) { 460 case DHCP_WRONG_MSG_TYPE: 461 printf("%s: Unexpected DHCP message.\n", 462 s_n); 463 break; 464 case DHCP_GARBLED_MSG_TYPE: 465 printf( 466 "%s: Garbled DHCP message type.\n", 467 s_n); 468 break; 469 case DHCP_BAD_OPT_OVLD: 470 printf("%s: Bad option overload.\n", 471 s_n); 472 break; 473 } 474 } 475 tk = wk; 476 wk = wk->next; 477 remove_list(tk, B_TRUE); 478 continue; 479 } 480 wk = wk->next; 481 } 482 483 /* 484 * Pass two. Pick out the best offer. Point system. 485 * What's important? 486 * 0) DHCP 487 * 1) No option overload 488 * 2) Encapsulated vendor option 489 * 3) Non-null sname and siaddr fields 490 * 4) Non-null file field 491 * 5) Hostname 492 * 6) Subnetmask 493 * 7) Router 494 */ 495 best = NULL; 496 for (wk = list_hd; wk != NULL; wk = wk->next) { 497 wk->offset = 0; 498 if (wk->opts[CD_DHCP_TYPE] && 499 wk->opts[CD_DHCP_TYPE]->len == 1) { 500 if (*wk->opts[CD_DHCP_TYPE]->value != OFFER) { 501 dprintf("%s: Unexpected DHCP message." 502 " Expected OFFER message.\n", s_n); 503 continue; 504 } 505 if (!wk->opts[CD_LEASE_TIME]) { 506 dprintf("%s: DHCP OFFER message without lease " 507 "time parameter.\n", s_n); 508 continue; 509 } else { 510 if (wk->opts[CD_LEASE_TIME]->len != 4) { 511 dprintf("%s: Lease expiration time is " 512 "garbled.\n", s_n); 513 continue; 514 } 515 } 516 if (!wk->opts[CD_SERVER_ID]) { 517 dprintf("%s: DHCP OFFER message without server " 518 "id parameter.\n", s_n); 519 continue; 520 } else { 521 if (wk->opts[CD_SERVER_ID]->len != 4) { 522 dprintf("%s: Server identifier " 523 "parameter is garbled.\n", s_n); 524 continue; 525 } 526 } 527 /* Valid DHCP OFFER. See if we got our parameters. */ 528 dprintf("%s: Found valid DHCP OFFER message.\n", s_n); 529 530 wk->offset += 30; 531 532 /* 533 * Also could be faked, though more difficult 534 * because the encapsulation is hard to encode 535 * on a BOOTP server; plus there's not as much 536 * real estate in the packet for options, so 537 * it's likely this option would get dropped. 538 */ 539 if (wk->opts[CD_VENDOR_SPEC]) 540 wk->offset += 80; 541 } else 542 dprintf("%s: Found valid BOOTP reply.\n", s_n); 543 544 /* 545 * RFC1048 BOOTP? 546 */ 547 if (bcmp((caddr_t)wk->pkt->cookie, (caddr_t)magic, 548 sizeof (magic)) == 0) { 549 wk->offset += 5; 550 if (wk->opts[CD_SUBNETMASK]) 551 wk->offset++; 552 if (wk->opts[CD_ROUTER]) 553 wk->offset++; 554 if (wk->opts[CD_HOSTNAME]) 555 wk->offset += 5; 556 557 /* 558 * Prefer options that have diskless boot significance 559 */ 560 if (ntohl(wk->pkt->siaddr.s_addr) != INADDR_ANY) 561 wk->offset += 10; /* server ip */ 562 if (wk->opts[CD_OPTION_OVERLOAD] == NULL) { 563 if (wk->pkt->sname[0] != '\0') 564 wk->offset += 10; /* server name */ 565 if (wk->pkt->file[0] != '\0') 566 wk->offset += 5; /* File to load */ 567 } 568 } 569 #ifdef DHCP_DEBUG 570 printf("%s: This server configuration has '%d' points.\n", s_n, 571 wk->offset); 572 #endif /* DHCP_DEBUG */ 573 if (!best) 574 best = wk; 575 else { 576 if (best->offset < wk->offset) 577 best = wk; 578 } 579 } 580 if (best) { 581 #ifdef DHCP_DEBUG 582 printf("%s: Found best: points: %d\n", s_n, best->offset); 583 #endif /* DHCP_DEBUG */ 584 remove_list(best, B_FALSE); 585 } else { 586 dprintf("%s: No valid BOOTP reply or DHCP OFFER was found.\n", 587 s_n); 588 } 589 flush_list(); /* toss the remaining list */ 590 return (best); 591 } 592 593 /* 594 * Send a decline message to the generator of the DHCPACK. 595 */ 596 static void 597 dhcp_decline(char *msg, PKT_LIST *pl) 598 { 599 PKT *pkt; 600 uint8_t *opt, ulen; 601 int pkt_size; 602 struct in_addr nets, ours, t_server, t_yiaddr; 603 604 if (pl != NULL) { 605 if (pl->opts[CD_SERVER_ID] != NULL && 606 pl->opts[CD_SERVER_ID]->len == sizeof (struct in_addr)) { 607 bcopy(pl->opts[CD_SERVER_ID]->value, 608 &t_server, pl->opts[CD_SERVER_ID]->len); 609 } else 610 t_server.s_addr = htonl(INADDR_BROADCAST); 611 t_yiaddr.s_addr = pl->pkt->yiaddr.s_addr; 612 } 613 pkt = (PKT *)dhcp_snd_bufp; 614 opt = init_msg(pkt, opt_decline); 615 set_hw_spec_data(pkt, &opt, opt_decline); 616 617 *opt++ = CD_SERVER_ID; 618 *opt++ = sizeof (struct in_addr); 619 bcopy(&t_server, opt, sizeof (struct in_addr)); 620 opt += sizeof (struct in_addr); 621 nets.s_addr = htonl(INADDR_BROADCAST); 622 623 /* 624 * Use the given yiaddr in our ciaddr field so server can identify us. 625 */ 626 pkt->ciaddr.s_addr = t_yiaddr.s_addr; 627 628 ipv4_getipaddr(&ours); /* Could be 0, could be yiaddr */ 629 ours.s_addr = htonl(ours.s_addr); 630 631 ulen = (uint8_t)strlen(msg); 632 *opt++ = CD_MESSAGE; 633 *opt++ = ulen; 634 bcopy(msg, opt, ulen); 635 opt += ulen; 636 *opt++ = CD_END; 637 638 pkt_size = (uint8_t *)opt - (uint8_t *)pkt; 639 if (pkt_size < sizeof (PKT)) 640 pkt_size = sizeof (PKT); 641 642 timeout = 0; /* reset timeout */ 643 (void) inet(pkt_size, &ours, &nets, 0, 0L); 644 } 645 646 /* 647 * Implementings SELECTING state of DHCP client state machine. 648 */ 649 static int 650 dhcp_selecting(void) 651 { 652 int pkt_size; 653 PKT *pkt; 654 uint8_t *opt; 655 uint16_t size; 656 uint32_t lease; 657 struct in_addr nets, ours; 658 659 pkt = (PKT *)dhcp_snd_bufp; 660 opt = init_msg(pkt, opt_discover); 661 pkt->secs = htons((uint16_t)((prom_gettime() - dhcp_start_time)/1000)); 662 663 *opt++ = CD_MAX_DHCP_SIZE; 664 *opt++ = sizeof (size); 665 size = (uint16_t)(dhcp_buf_size - sizeof (struct ip) - 666 sizeof (struct udphdr)); 667 size = htons(size); 668 bcopy(&size, opt, sizeof (size)); 669 opt += sizeof (size); 670 671 set_hw_spec_data(pkt, &opt, opt_discover); 672 673 *opt++ = CD_LEASE_TIME; 674 *opt++ = sizeof (lease); 675 lease = htonl(DHCP_PERM); /* ask for a permanent lease */ 676 bcopy(&lease, opt, sizeof (lease)); 677 opt += sizeof (lease); 678 679 /* 680 * If a clientid was set using dhcp_set_client_id(), add this 681 * to the options. 682 */ 683 if (dhcp_clientid_len > 0) { 684 *opt++ = CD_CLIENT_ID; 685 *opt++ = dhcp_clientid_len; 686 bcopy(dhcp_clientid, opt, dhcp_clientid_len); 687 opt += dhcp_clientid_len; 688 } 689 690 parameter_request_list(&opt); 691 692 *opt++ = CD_END; 693 694 pkt_size = (uint8_t *)opt - (uint8_t *)pkt; 695 if (pkt_size < sizeof (PKT)) 696 pkt_size = sizeof (PKT); 697 698 nets.s_addr = INADDR_BROADCAST; 699 ours.s_addr = INADDR_ANY; 700 timeout = 0; /* reset timeout */ 701 702 return (inet(pkt_size, &ours, &nets, DHCP_RETRIES, DHCP_WAIT)); 703 } 704 705 /* 706 * implements the REQUESTING state of the DHCP client state machine. 707 */ 708 static int 709 dhcp_requesting(void) 710 { 711 PKT_LIST *pl, *wk; 712 PKT *pkt, *pl_pkt; 713 uint8_t *opt; 714 int pkt_size, err; 715 uint32_t t_time; 716 struct in_addr nets, ours; 717 DHCP_OPT *doptp; 718 uint16_t size; 719 720 /* here we scan the list of OFFERS, select the best one. */ 721 state_pl = NULL; 722 723 if ((pl = select_best()) == NULL) { 724 dprintf("%s: No valid BOOTP reply or DHCP OFFER was found.\n", 725 s_n); 726 return (1); 727 } 728 729 pl_pkt = pl->pkt; 730 731 /* 732 * Check to see if we got an OFFER pkt(s). If not, then We only got 733 * a response from a BOOTP server. We'll go to the bound state and 734 * try to use that configuration. 735 */ 736 if (pl->opts[CD_DHCP_TYPE] == NULL) { 737 if (mac_call_arp(&pl_pkt->yiaddr, NULL, DHCP_ARP_TIMEOUT)) { 738 /* Someone responded! go back to SELECTING state. */ 739 printf("%s: Some host already using BOOTP %s.\n", s_n, 740 inet_ntoa(pl_pkt->yiaddr)); 741 bkmem_free((char *)pl->pkt, pl->len); 742 bkmem_free((char *)pl, sizeof (PKT_LIST)); 743 return (1); 744 } 745 state_pl = pl; 746 return (0); 747 } 748 749 /* 750 * if we got a message from the server, display it. 751 */ 752 if (pl->opts[CD_MESSAGE]) 753 prt_server_msg(pl->opts[CD_MESSAGE]); 754 /* 755 * Assemble a DHCPREQUEST, with the ciaddr field set to 0, since we 756 * got here from DISCOVER state. Keep secs field the same for relay 757 * agents. We start with the DHCPOFFER packet we got, and the 758 * options contained in it to make a requested option list. 759 */ 760 pkt = (PKT *)dhcp_snd_bufp; 761 opt = init_msg(pkt, opt_request); 762 763 /* Time from Successful DISCOVER message. */ 764 pkt->secs = htons((uint16_t)dhcp_secs); 765 766 size = (uint16_t)(dhcp_buf_size - sizeof (struct ip) - 767 sizeof (struct udphdr)); 768 size = htons(size); 769 *opt++ = CD_MAX_DHCP_SIZE; 770 *opt++ = sizeof (size); 771 bcopy(&size, opt, sizeof (size)); 772 opt += sizeof (size); 773 774 set_hw_spec_data(pkt, &opt, opt_request); 775 *opt++ = CD_SERVER_ID; 776 *opt++ = pl->opts[CD_SERVER_ID]->len; 777 bcopy(pl->opts[CD_SERVER_ID]->value, opt, 778 pl->opts[CD_SERVER_ID]->len); 779 opt += pl->opts[CD_SERVER_ID]->len; 780 781 /* our offered lease minus boot time */ 782 *opt++ = CD_LEASE_TIME; 783 *opt++ = 4; 784 bcopy(pl->opts[CD_LEASE_TIME]->value, &t_time, 785 sizeof (t_time)); 786 t_time = ntohl(t_time); 787 if ((uint32_t)t_time != DHCP_PERM) 788 t_time -= (uint32_t)dhcp_secs; 789 t_time = htonl(t_time); 790 bcopy(&t_time, opt, sizeof (t_time)); 791 opt += sizeof (t_time); 792 793 /* our offered IP address, as required. */ 794 *opt++ = CD_REQUESTED_IP_ADDR; 795 *opt++ = sizeof (struct in_addr); 796 bcopy(&pl_pkt->yiaddr, opt, sizeof (struct in_addr)); 797 opt += sizeof (struct in_addr); 798 799 /* 800 * If a clientid was set using dhcp_set_client_id(), add this 801 * to the options. 802 */ 803 if (dhcp_clientid_len > 0) { 804 *opt++ = CD_CLIENT_ID; 805 *opt++ = dhcp_clientid_len; 806 bcopy(dhcp_clientid, opt, dhcp_clientid_len); 807 opt += dhcp_clientid_len; 808 } 809 810 parameter_request_list(&opt); 811 812 *opt++ = CD_END; 813 814 /* Done with the OFFER pkt. */ 815 bkmem_free((char *)pl->pkt, pl->len); 816 bkmem_free((char *)pl, sizeof (PKT_LIST)); 817 818 /* 819 * We make 4 attempts here. We wait for 2 seconds to accumulate 820 * requests, just in case a BOOTP server is too fast! 821 */ 822 pkt_size = (uint8_t *)opt - (uint8_t *)pkt; 823 if (pkt_size < sizeof (PKT)) 824 pkt_size = sizeof (PKT); 825 826 nets.s_addr = INADDR_BROADCAST; 827 ours.s_addr = INADDR_ANY; 828 timeout = 0; /* reset timeout */ 829 830 if ((err = inet(pkt_size, &ours, &nets, 4, (time_t)2L)) != 0) 831 return (err); 832 for (wk = list_hd; wk != NULL && state_pl == NULL; wk = wk->next) { 833 if (dhcp_options_scan(wk, B_TRUE) != 0 || 834 !wk->opts[CD_DHCP_TYPE]) 835 continue; /* garbled options */ 836 switch (*wk->opts[CD_DHCP_TYPE]->value) { 837 case ACK: 838 remove_list(wk, B_FALSE); 839 state_pl = wk; 840 break; 841 case NAK: 842 printf("%s: rejected by DHCP server: %s\n", 843 s_n, inet_ntoa(*((struct in_addr *)wk-> 844 opts[CD_SERVER_ID]->value))); 845 if (wk->opts[CD_MESSAGE]) 846 prt_server_msg(wk->opts[CD_MESSAGE]); 847 break; 848 default: 849 dprintf("%s: Unexpected DHCP message type.\n", s_n); 850 break; 851 } 852 } 853 flush_list(); 854 if (state_pl != NULL) { 855 if (state_pl->opts[CD_MESSAGE]) 856 prt_server_msg(state_pl->opts[CD_MESSAGE]); 857 pl_pkt = state_pl->pkt; 858 /* arp our new address, just to make sure */ 859 if (!mac_call_arp(&pl_pkt->yiaddr, NULL, DHCP_ARP_TIMEOUT)) { 860 /* 861 * No response. Check if lease is ok. 862 */ 863 doptp = state_pl->opts[CD_LEASE_TIME]; 864 if (state_pl->opts[CD_DHCP_TYPE] && (!doptp || 865 (doptp->len % 4) != 0)) { 866 dhcp_decline("Missing or corrupted lease time", 867 state_pl); 868 err++; 869 } 870 } else { 871 dhcp_decline("IP Address already being used!", 872 state_pl); 873 err++; 874 } 875 if (err) { 876 bkmem_free((char *)state_pl->pkt, state_pl->len); 877 bkmem_free((char *)state_pl, sizeof (PKT_LIST)); 878 state_pl = NULL; 879 } else 880 return (0); /* passes the tests! */ 881 } 882 dprintf("%s: No valid DHCP acknowledge messages received.\n", s_n); 883 return (1); 884 } 885 886 /* 887 * Implements BOUND state of DHCP client state machine. 888 */ 889 static int 890 dhcp_bound(void) 891 { 892 PKT *pl_pkt = state_pl->pkt; 893 DHCP_OPT *doptp; 894 uint8_t *tp, *hp; 895 int items, i, fnd, k; 896 char hostname[MAXHOSTNAMELEN+1]; 897 struct in_addr subnet, defr, savr, *ipp, xip; 898 899 #ifdef DHCP_DEBUG 900 if (dhcp_classid[0] != 0) { 901 char scratch[DHCP_MAX_OPT_SIZE + 1]; 902 903 bcopy(&dhcp_classid[2], scratch, dhcp_classid[1]); 904 scratch[dhcp_classid[1]] = '\0'; 905 printf("Your machine is of the class: '%s'.\n", scratch); 906 } 907 #endif /* DHCP_DEBUG */ 908 909 /* First, set the bare essentials. (IP layer parameters) */ 910 911 ipv4_getipaddr(&xip); 912 if (xip.s_addr != INADDR_ANY) 913 ipp = &xip; 914 else { 915 ipp = &pl_pkt->yiaddr; 916 ipp->s_addr = ntohl(ipp->s_addr); 917 ipv4_setipaddr(ipp); 918 } 919 920 ipp->s_addr = htonl(ipp->s_addr); 921 922 if (boothowto & RB_VERBOSE) 923 printf("%s: IP address is: %s\n", s_n, inet_ntoa(*ipp)); 924 925 /* 926 * Ensure that the Boot NFS READ size, if given, is an int16_t. 927 */ 928 if (state_pl->vs[VS_BOOT_NFS_READSIZE] != NULL) { 929 doptp = state_pl->vs[VS_BOOT_NFS_READSIZE]; 930 if (doptp->len != sizeof (int16_t)) 931 state_pl->vs[VS_BOOT_NFS_READSIZE] = NULL; 932 } 933 934 /* 935 * Set subnetmask. Nice, but not required. 936 */ 937 if (state_pl->opts[CD_SUBNETMASK] != NULL) { 938 doptp = state_pl->opts[CD_SUBNETMASK]; 939 if (doptp->len != 4) 940 state_pl->opts[CD_SUBNETMASK] = NULL; 941 else { 942 bcopy(doptp->value, &subnet, 943 sizeof (struct in_addr)); 944 dprintf("%s: Setting netmask to: %s\n", s_n, 945 inet_ntoa(subnet)); 946 subnet.s_addr = ntohl(subnet.s_addr); 947 ipv4_setnetmask(&subnet); 948 } 949 } 950 951 /* 952 * Set default IP TTL. Nice, but not required. 953 */ 954 if (state_pl->opts[CD_IPTTL] != NULL) { 955 doptp = state_pl->opts[CD_IPTTL]; 956 if (doptp->len > 1) 957 state_pl->opts[CD_IPTTL] = NULL; 958 else { 959 doptp = state_pl->opts[CD_IPTTL]; 960 ipv4_setmaxttl(*(uint8_t *)doptp->value); 961 dprintf("%s: Setting IP TTL to: %d\n", s_n, 962 *(uint8_t *)doptp->value); 963 } 964 } 965 966 /* 967 * Set default router. Not required, although we'll know soon 968 * enough... 969 */ 970 if (state_pl->opts[CD_ROUTER] != NULL) { 971 doptp = state_pl->opts[CD_ROUTER]; 972 if ((doptp->len % 4) != 0) { 973 state_pl->opts[CD_ROUTER] = NULL; 974 } else { 975 if ((hp = (uint8_t *)bkmem_alloc( 976 mac_get_hdr_len())) == NULL) { 977 errno = ENOMEM; 978 return (-1); 979 } 980 items = doptp->len / sizeof (struct in_addr); 981 tp = doptp->value; 982 bcopy(tp, &savr, sizeof (struct in_addr)); 983 for (i = 0, fnd = 0; i < items; i++) { 984 bcopy(tp, &defr, sizeof (struct in_addr)); 985 for (k = 0, fnd = 0; k < 2 && fnd == 0; k++) { 986 fnd = mac_get_arp(&defr, hp, 987 mac_get_hdr_len(), 988 mac_get_arp_timeout()); 989 } 990 if (fnd) 991 break; 992 dprintf( 993 "%s: Warning: Router %s is unreachable.\n", 994 s_n, inet_ntoa(defr)); 995 tp += sizeof (struct in_addr); 996 } 997 bkmem_free((char *)hp, mac_get_hdr_len()); 998 999 /* 1000 * If fnd is 0, we didn't find a working router. We'll 1001 * still try to use first default router. If we got 1002 * a bad router address (like not on the same net), 1003 * we're hosed anyway. 1004 */ 1005 if (!fnd) { 1006 dprintf( 1007 "%s: Warning: Using default router: %s\n", 1008 s_n, inet_ntoa(savr)); 1009 defr.s_addr = savr.s_addr; 1010 } 1011 /* ipv4_route expects network order IP addresses */ 1012 (void) ipv4_route(IPV4_ADD_ROUTE, RT_DEFAULT, NULL, 1013 &defr); 1014 } 1015 } 1016 1017 /* 1018 * Set hostname. Not required. 1019 */ 1020 if (state_pl->opts[CD_HOSTNAME] != NULL) { 1021 doptp = state_pl->opts[CD_HOSTNAME]; 1022 i = doptp->len; 1023 if (i > MAXHOSTNAMELEN) 1024 i = MAXHOSTNAMELEN; 1025 bcopy(doptp->value, hostname, i); 1026 hostname[i] = '\0'; 1027 (void) sethostname(hostname, i); 1028 if (boothowto & RB_VERBOSE) 1029 printf("%s: Hostname is %s\n", s_n, hostname); 1030 } 1031 1032 /* 1033 * We don't care about the lease time.... We can't enforce it anyway. 1034 */ 1035 return (0); 1036 } 1037 1038 /* 1039 * Convert the DHCPACK into a pure ASCII boot property for use by the kernel 1040 * later in the boot process. 1041 */ 1042 static void 1043 create_bootpresponse_bprop(PKT_LIST *pl) 1044 { 1045 uint_t buflen; 1046 1047 if (pl == NULL || bootp_response != NULL) 1048 return; 1049 1050 buflen = (pl->len * 2) + 1; /* extra space for null (1) */ 1051 if ((bootp_response = bkmem_alloc(buflen)) == NULL) 1052 return; 1053 1054 if (octet_to_hexascii((uint8_t *)pl->pkt, pl->len, bootp_response, 1055 &buflen) != 0) { 1056 bkmem_free(bootp_response, (pl->len * 2) + 1); 1057 bootp_response = NULL; 1058 } 1059 1060 #if defined(__sparc) 1061 prom_create_encoded_prop("bootp-response", pl->pkt, pl->len, 1062 ENCODE_BYTES); 1063 #endif /* __sparc */ 1064 } 1065 1066 /* 1067 * Examines /chosen node for "bootp-response" property. If it exists, this 1068 * property is the DHCPACK cached there by the PROM's DHCP implementation. 1069 * 1070 * If cache_present is B_TRUE, we simply return B_TRUE if the property exists 1071 * w/o decoding it. If cache_present is B_FALSE, we decode the packet and 1072 * use it as our state packet for the jump to BOUND mode. Note that it's good 1073 * enough that the cache exists w/o validation for determining if that's what 1074 * the user intended. 1075 * 1076 * We'll short-circuit the DHCP exchange by accepting this packet. We build a 1077 * PKT_LIST structure, and copy the bootp-response property value into a 1078 * PKT buffer we allocated. We then scan the PKT for options, and then 1079 * set state_pl to point to it. 1080 * 1081 * Returns B_TRUE if a packet was cached (and was processed correctly), false 1082 * otherwise. The caller needs to make the state change from SELECTING to 1083 * BOUND upon a B_TRUE return from this function. 1084 */ 1085 int 1086 prom_cached_reply(int cache_present) 1087 { 1088 PKT_LIST *pl; 1089 int len; 1090 pnode_t chosen; 1091 char *prop = PROM_BOOT_CACHED; 1092 1093 chosen = prom_finddevice("/chosen"); 1094 if (chosen == OBP_NONODE || chosen == OBP_BADNODE) 1095 chosen = prom_nextnode((pnode_t)0); /* root node */ 1096 1097 if ((len = prom_getproplen(chosen, prop)) <= 0) 1098 return (B_FALSE); 1099 1100 if (cache_present) 1101 return (B_TRUE); 1102 1103 if (((pl = (PKT_LIST *)bkmem_zalloc(sizeof (PKT_LIST))) == NULL) || 1104 ((pl->pkt = (PKT *)bkmem_zalloc(len)) == NULL)) { 1105 errno = ENOMEM; 1106 if (pl != NULL) 1107 bkmem_free((char *)pl, sizeof (PKT_LIST)); 1108 return (B_FALSE); 1109 } 1110 1111 (void) prom_getprop(chosen, prop, (caddr_t)pl->pkt); 1112 1113 pl->len = len; 1114 1115 if (dhcp_options_scan(pl, B_TRUE) != 0) { 1116 /* garbled packet */ 1117 bkmem_free((char *)pl->pkt, pl->len); 1118 bkmem_free((char *)pl, sizeof (PKT_LIST)); 1119 return (B_FALSE); 1120 } 1121 1122 state_pl = pl; 1123 return (B_TRUE); 1124 } 1125 1126 /* 1127 * Perform DHCP to acquire what we used to get using rarp/bootparams. 1128 * Returns 0 for success, nonzero otherwise. 1129 * 1130 * DHCP client state machine. 1131 */ 1132 int 1133 dhcp(void) 1134 { 1135 int err = 0; 1136 int done = B_FALSE; 1137 1138 dhcp_buf_size = mac_get_mtu(); 1139 dhcp_snd_bufp = (PKT *)bkmem_alloc(dhcp_buf_size); 1140 dhcp_rcv_bufp = (PKT *)bkmem_alloc(dhcp_buf_size); 1141 1142 if (dhcp_snd_bufp == NULL || dhcp_rcv_bufp == NULL) { 1143 if (dhcp_snd_bufp != NULL) 1144 bkmem_free((char *)dhcp_snd_bufp, dhcp_buf_size); 1145 if (dhcp_rcv_bufp != NULL) 1146 bkmem_free((char *)dhcp_rcv_bufp, dhcp_buf_size); 1147 errno = ENOMEM; 1148 return (-1); 1149 } 1150 1151 while (err == 0 && !done) { 1152 switch (dhcp_state) { 1153 case INIT: 1154 1155 s_n = "INIT"; 1156 if (prom_cached_reply(B_FALSE)) { 1157 dprintf("%s: Using PROM cached BOOT reply...\n", 1158 s_n); 1159 dhcp_state = BOUND; 1160 } else { 1161 dhcp_state = SELECTING; 1162 dhcp_start_time = prom_gettime(); 1163 } 1164 break; 1165 1166 case SELECTING: 1167 1168 s_n = "SELECTING"; 1169 err = dhcp_selecting(); 1170 switch (err) { 1171 case 0: 1172 dhcp_state = REQUESTING; 1173 break; 1174 case DHCP_NO_DATA: 1175 dprintf( 1176 "%s: No DHCP response after %d tries.\n", 1177 s_n, DHCP_RETRIES); 1178 break; 1179 default: 1180 /* major network problems */ 1181 dprintf("%s: Network transaction failed: %d\n", 1182 s_n, err); 1183 break; 1184 } 1185 break; 1186 1187 case REQUESTING: 1188 1189 s_n = "REQUESTING"; 1190 err = dhcp_requesting(); 1191 switch (err) { 1192 case 0: 1193 dhcp_state = BOUND; 1194 break; 1195 case DHCP_NO_DATA: 1196 dprintf("%s: Request timed out.\n", s_n); 1197 dhcp_state = SELECTING; 1198 err = 0; 1199 (void) sleep(10); 1200 break; 1201 default: 1202 /* major network problems */ 1203 dprintf("%s: Network transaction failed: %d\n", 1204 s_n, err); 1205 break; 1206 } 1207 break; 1208 1209 case BOUND: 1210 1211 /* 1212 * We just "give up" if bound state fails. 1213 */ 1214 s_n = "BOUND"; 1215 if ((err = dhcp_bound()) == 0) { 1216 dhcp_state = CONFIGURED; 1217 } 1218 break; 1219 1220 case CONFIGURED: 1221 s_n = "CONFIGURED"; 1222 create_bootpresponse_bprop(state_pl); 1223 done = B_TRUE; 1224 break; 1225 } 1226 } 1227 bkmem_free((char *)dhcp_snd_bufp, dhcp_buf_size); 1228 bkmem_free((char *)dhcp_rcv_bufp, dhcp_buf_size); 1229 1230 return (err); 1231 } 1232 1233 /* 1234 * Returns a copy of the DHCP-supplied value of the parameter requested 1235 * by code. 1236 */ 1237 boolean_t 1238 dhcp_getinfo(uchar_t optcat, uint16_t code, uint16_t optsize, void *value, 1239 size_t *vallenp) 1240 { 1241 size_t len = *vallenp; 1242 1243 if (dhcp_getinfo_pl(state_pl, optcat, code, 1244 optsize, value, &len)) { 1245 1246 if (len <= *vallenp) { 1247 *vallenp = len; 1248 return (B_TRUE); 1249 } 1250 } 1251 1252 return (B_FALSE); 1253 } 1254 1255 /* 1256 * Sets the clientid option. 1257 */ 1258 void 1259 dhcp_set_client_id(uint8_t *clientid, uint8_t clientid_len) 1260 { 1261 bcopy(clientid, dhcp_clientid, clientid_len); 1262 dhcp_clientid_len = clientid_len; 1263 } 1264