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