1/* 2 * Copyright (c) 1988-2011 Apple Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28 29/* 30 * in_dhcp.c 31 * - use DHCP to allocate an IP address and get the subnet mask and router 32 */ 33 34/* 35 * Modification History 36 * 37 * April 17, 2007 Dieter Siegmund (dieter@apple.com) 38 * - created based on in_bootp.c 39 */ 40 41#include <sys/param.h> 42#include <sys/types.h> 43#include <mach/boolean.h> 44#include <sys/kernel.h> 45#include <sys/errno.h> 46#include <sys/file.h> 47#include <sys/uio.h> 48#include <sys/ioctl.h> 49#include <sys/time.h> 50#include <sys/mbuf.h> 51#include <sys/vnode.h> 52#include <sys/socket.h> 53#include <sys/socketvar.h> 54#include <sys/uio_internal.h> 55#include <net/if.h> 56#include <net/if_dl.h> 57#include <net/if_types.h> 58#include <net/route.h> 59#include <net/dlil.h> 60#include <netinet/in.h> 61#include <netinet/in_systm.h> 62#include <netinet/if_ether.h> 63#include <netinet/ip.h> 64#include <netinet/ip_var.h> 65#include <netinet/udp.h> 66#include <netinet/udp_var.h> 67#include <netinet/ip_icmp.h> 68#include <netinet/bootp.h> 69#include <netinet/dhcp.h> 70#include <netinet/in_dhcp.h> 71#include <sys/systm.h> 72#include <sys/malloc.h> 73#include <netinet/dhcp_options.h> 74 75#include <kern/kern_types.h> 76#include <kern/kalloc.h> 77 78#ifdef DHCP_DEBUG 79#define dprintf(x) printf x; 80#else /* !DHCP_DEBUG */ 81#define dprintf(x) 82#endif /* DHCP_DEBUG */ 83 84#define INITIAL_WAIT_SECS 2 85#define MAX_WAIT_SECS 64 86#define GATHER_TIME_SECS 4 87#define RAND_TICKS (hz) /* one second */ 88 89const struct sockaddr_in blank_sin = { 90 sizeof(struct sockaddr_in), 91 AF_INET, 92 0, 93 { 0 }, 94 { 0, 0, 0, 0, 0, 0, 0, 0 } 95}; 96 97__private_extern__ int 98inet_aifaddr(struct socket * so, const char * name, 99 const struct in_addr * addr, 100 const struct in_addr * mask, 101 const struct in_addr * broadcast) 102{ 103 struct ifaliasreq ifra; 104 105 bzero(&ifra, sizeof(ifra)); 106 strlcpy(ifra.ifra_name, name, sizeof(ifra.ifra_name)); 107 if (addr) { 108 *((struct sockaddr_in *)(void *)&ifra.ifra_addr) = blank_sin; 109 ((struct sockaddr_in *)(void *)&ifra.ifra_addr)->sin_addr = *addr; 110 } 111 if (mask) { 112 *((struct sockaddr_in *)(void *)&ifra.ifra_mask) = blank_sin; 113 ((struct sockaddr_in *)(void *)&ifra.ifra_mask)->sin_addr = *mask; 114 } 115 if (broadcast) { 116 *((struct sockaddr_in *)(void *)&ifra.ifra_broadaddr) = blank_sin; 117 ((struct sockaddr_in *)(void *)&ifra.ifra_broadaddr)->sin_addr = *broadcast; 118 } 119 return (ifioctl(so, SIOCAIFADDR, (caddr_t)&ifra, current_proc())); 120} 121 122 123struct dhcp_context { 124 struct ifnet * ifp; 125 struct sockaddr_dl * dl_p; 126 struct ifreq ifr; 127 struct socket * so; 128 uint8_t request[DHCP_PACKET_MIN]; 129 dhcpoa_t request_options; 130 uint8_t reply[DHCP_PAYLOAD_MIN]; 131 struct timeval start_time; 132 uint32_t xid; 133 int max_try; 134 struct in_addr iaddr; 135 struct in_addr netmask; 136 struct in_addr router; 137 struct in_addr server_id; 138}; 139 140static __inline__ struct dhcp_packet * 141dhcp_context_request(struct dhcp_context * context) 142{ 143 return ((struct dhcp_packet *)(void *)context->request); 144} 145 146static __inline__ struct dhcp * 147dhcp_context_reply(struct dhcp_context * context) 148{ 149 return ((struct dhcp *)(void *)context->reply); 150} 151 152struct mbuf * ip_pkt_to_mbuf(caddr_t pkt, int pktsize); 153 154static int 155receive_packet(struct socket * so, void * pp, int psize, 156 int * actual_size); 157 158/* ip address formatting macros */ 159#define IP_FORMAT "%d.%d.%d.%d" 160#define IP_CH(ip) ((const uint8_t *)ip) 161#define IP_LIST(ip) IP_CH(ip)[0],IP_CH(ip)[1],IP_CH(ip)[2],IP_CH(ip)[3] 162 163#define SUGGESTED_LEASE_LENGTH (60 * 60 * 24 * 30 * 3) /* 3 months */ 164 165static const uint8_t dhcp_params[] = { 166 dhcptag_subnet_mask_e, 167 dhcptag_router_e, 168}; 169 170#define N_DHCP_PARAMS (sizeof(dhcp_params) / sizeof(dhcp_params[0])) 171 172static __inline__ long 173random_range(long bottom, long top) 174{ 175 long number = top - bottom + 1; 176 long range_size = LONG_MAX / number; 177 return (((long)random()) / range_size + bottom); 178} 179 180static void 181init_dhcp_packet_header(struct dhcp_packet * pkt, int pkt_size) 182{ 183 bzero(&pkt->ip, sizeof(pkt->ip)); 184 bzero(&pkt->udp, sizeof(pkt->udp)); 185 pkt->ip.ip_v = IPVERSION; 186 pkt->ip.ip_hl = sizeof(struct ip) >> 2; 187 pkt->ip.ip_ttl = MAXTTL; 188 pkt->ip.ip_p = IPPROTO_UDP; 189 pkt->ip.ip_src.s_addr = 0; 190 pkt->ip.ip_dst.s_addr = htonl(INADDR_BROADCAST); 191 pkt->ip.ip_len = htons(pkt_size); 192 pkt->ip.ip_sum = 0; 193 pkt->udp.uh_sport = htons(IPPORT_BOOTPC); 194 pkt->udp.uh_dport = htons(IPPORT_BOOTPS); 195 pkt->udp.uh_sum = 0; 196 pkt->udp.uh_ulen = htons(pkt_size - sizeof(pkt->ip)); 197 return; 198} 199 200/* 201 * Function: make_dhcp_request 202 * Purpose: 203 * Initialize the DHCP-specific parts of the message. 204 */ 205static void 206make_dhcp_request(struct dhcp * request, int request_size, 207 dhcp_msgtype_t msg, 208 const uint8_t * hwaddr, uint8_t hwtype, int hwlen, 209 dhcpoa_t * options_p) 210{ 211 uint8_t cid[ETHER_ADDR_LEN + 1]; 212 uint8_t rfc_magic[RFC_MAGIC_SIZE] = RFC_OPTIONS_MAGIC; 213 214 if (hwlen > (int)sizeof(cid)) { 215 printf("dhcp: hwlen is %d (> %d), truncating\n", hwlen, 216 (int)sizeof(cid)); 217 hwlen = sizeof(cid); 218 } 219 bzero(request, request_size); 220 request->dp_op = BOOTREQUEST; 221 request->dp_htype = hwtype; 222 request->dp_hlen = hwlen; 223 bcopy(hwaddr, request->dp_chaddr, hwlen); 224 bcopy(rfc_magic, request->dp_options, RFC_MAGIC_SIZE); 225 dhcpoa_init(options_p, request->dp_options + RFC_MAGIC_SIZE, 226 request_size - sizeof(struct dhcp) - RFC_MAGIC_SIZE); 227 /* make the request a dhcp packet */ 228 dhcpoa_add_dhcpmsg(options_p, msg); 229 230 /* add the list of required parameters */ 231 dhcpoa_add(options_p, dhcptag_parameter_request_list_e, 232 N_DHCP_PARAMS, dhcp_params); 233 234 /* add the DHCP client identifier */ 235 cid[0] = hwtype; 236 bcopy(hwaddr, cid + 1, hwlen); 237 dhcpoa_add(options_p, dhcptag_client_identifier_e, hwlen + 1, cid); 238 239 return; 240} 241 242/* 243 * Function: ip_pkt_to_mbuf 244 * Purpose: 245 * Put the given IP packet into an mbuf, calculate the 246 * IP checksum. 247 */ 248struct mbuf * 249ip_pkt_to_mbuf(caddr_t pkt, int pktsize) 250{ 251 struct ip * ip; 252 struct mbuf * m; 253 254 m = (struct mbuf *)m_devget(pkt, pktsize, 0, NULL, NULL); 255 if (m == 0) { 256 printf("dhcp: ip_pkt_to_mbuf: m_devget failed\n"); 257 return NULL; 258 } 259 m->m_flags |= M_BCAST; 260 /* Compute the checksum */ 261 ip = mtod(m, struct ip *); 262 ip->ip_sum = 0; 263 ip->ip_sum = in_cksum(m, sizeof(struct ip)); 264 return (m); 265} 266 267static __inline__ u_char * 268link_address(struct sockaddr_dl * dl_p) 269{ 270 return (u_char *)(dl_p->sdl_data + dl_p->sdl_nlen); 271} 272 273static __inline__ int 274link_address_length(struct sockaddr_dl * dl_p) 275{ 276 return (dl_p->sdl_alen); 277} 278 279static __inline__ void 280link_print(struct sockaddr_dl * dl_p) 281{ 282 int i; 283 284 for (i = 0; i < dl_p->sdl_alen; i++) 285 printf("%s%x", i ? ":" : "", 286 (link_address(dl_p))[i]); 287 printf("\n"); 288 return; 289} 290 291static struct sockaddr_dl * 292link_from_ifnet(struct ifnet * ifp) 293{ 294 return ((struct sockaddr_dl *)(void *)ifp->if_lladdr->ifa_addr); 295} 296 297/* 298 * Function: send_packet 299 * Purpose: 300 * Send the request directly on the interface, bypassing the routing code. 301 */ 302static int 303send_packet(struct ifnet * ifp, struct dhcp_packet * pkt, int pkt_size) 304{ 305 struct mbuf * m; 306 struct sockaddr_in dest; 307 308 dest = blank_sin; 309 dest.sin_port = htons(IPPORT_BOOTPS); 310 dest.sin_addr.s_addr = INADDR_BROADCAST; 311 m = ip_pkt_to_mbuf((caddr_t)pkt, pkt_size); 312 return dlil_output(ifp, PF_INET, m, 0, (struct sockaddr *)&dest, 0, NULL); 313} 314 315/* 316 * Function: receive_packet 317 * Purpose: 318 * Return a received packet or an error if none available. 319 */ 320static int 321receive_packet(struct socket * so, void * pp, int psize, int * actual_size) 322{ 323 uio_t auio; 324 int error; 325 int rcvflg; 326 char uio_buf[ UIO_SIZEOF(1) ]; 327 328 auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ, 329 &uio_buf[0], sizeof(uio_buf)); 330 uio_addiov(auio, CAST_USER_ADDR_T(pp), psize); 331 rcvflg = MSG_WAITALL; 332 333 error = soreceive(so, (struct sockaddr **) 0, auio, 0, 0, &rcvflg); 334 *actual_size = psize - uio_resid(auio); 335 return (error); 336} 337 338/* 339 * Function: dhcp_timeout 340 * Purpose: 341 * Wakeup the process waiting for something on a socket. 342 */ 343static void 344dhcp_timeout(void * arg) 345{ 346 struct socket * * timer_arg = (struct socket * *)arg; 347 struct socket * so = *timer_arg; 348 349 dprintf(("dhcp: timeout\n")); 350 351 *timer_arg = NULL; 352 socket_lock(so, 1); 353 sowakeup(so, &so->so_rcv); 354 socket_unlock(so, 1); 355 return; 356} 357 358/* 359 * Function: rate_packet 360 * Purpose: 361 * Return an integer point rating value for the given dhcp packet. 362 * If yiaddr non-zero, the packet gets a rating of 1. 363 * Another point is given if the packet contains the subnet mask, 364 * and another if the router is present. 365 */ 366#define GOOD_RATING 3 367static __inline__ int 368rate_packet(dhcpol_t * options_p) 369{ 370 int len; 371 int rating = 1; 372 373 if (dhcpol_find(options_p, dhcptag_subnet_mask_e, &len, NULL) != NULL) { 374 rating++; 375 } 376 if (dhcpol_find(options_p, dhcptag_router_e, &len, NULL) != NULL) { 377 rating++; 378 } 379 return (rating); 380} 381 382static dhcp_msgtype_t 383get_dhcp_msgtype(dhcpol_t * options_p) 384{ 385 int len; 386 const uint8_t * opt; 387 388 opt = dhcpol_find(options_p, dhcptag_dhcp_message_type_e, &len, NULL); 389 if (opt != NULL && len == 1) { 390 return (*opt); 391 } 392 return (dhcp_msgtype_none_e); 393} 394 395static int 396dhcp_get_ack(struct dhcp_context * context, int wait_ticks) 397{ 398 int error = 0; 399 const struct in_addr * ip; 400 int len; 401 int n; 402 struct dhcp * reply; 403 struct in_addr server_id; 404 struct socket * timer_arg; 405 406 timer_arg = context->so; 407 reply = dhcp_context_reply(context); 408 timeout((timeout_fcn_t)dhcp_timeout, &timer_arg, wait_ticks); 409 while (1) { 410 error = receive_packet(context->so, context->reply, 411 sizeof(context->reply), &n); 412 if (error == 0) { 413 dhcp_msgtype_t msg; 414 dhcpol_t options; 415 416 dprintf(("\ndhcp: received packet length %d\n", n)); 417 if (n < (int)sizeof(struct dhcp)) { 418 dprintf(("dhcp: packet is too short %d < %d\n", 419 n, (int)sizeof(struct dhcp))); 420 continue; 421 } 422 if (ntohl(reply->dp_xid) != context->xid 423 || bcmp(reply->dp_chaddr, link_address(context->dl_p), 424 link_address_length(context->dl_p)) != 0) { 425 /* not for us */ 426 continue; 427 } 428 (void)dhcpol_parse_packet(&options, reply, n); 429 server_id.s_addr = 0; 430 ip = (const struct in_addr *) 431 dhcpol_find(&options, 432 dhcptag_server_identifier_e, &len, NULL); 433 if (ip != NULL && len >= (int)sizeof(*ip)) { 434 server_id = *ip; 435 } 436 msg = get_dhcp_msgtype(&options); 437 if (msg == dhcp_msgtype_nak_e 438 && server_id.s_addr == context->server_id.s_addr) { 439 /* server NAK'd us, start over */ 440 dhcpol_free(&options); 441 error = EPROTO; 442 untimeout((timeout_fcn_t)dhcp_timeout, &timer_arg); 443 break; 444 } 445 if (msg != dhcp_msgtype_ack_e 446 || reply->dp_yiaddr.s_addr == 0 447 || reply->dp_yiaddr.s_addr == INADDR_BROADCAST) { 448 /* ignore the packet */ 449 goto next_packet; 450 } 451 printf("dhcp: received ACK: server " IP_FORMAT 452 " IP address " IP_FORMAT "\n", 453 IP_LIST(&server_id), IP_LIST(&reply->dp_yiaddr)); 454 context->iaddr = reply->dp_yiaddr; 455 ip = (const struct in_addr *) 456 dhcpol_find(&options, 457 dhcptag_subnet_mask_e, &len, NULL); 458 if (ip != NULL && len >= (int)sizeof(*ip)) { 459 context->netmask = *ip; 460 } 461 ip = (const struct in_addr *) 462 dhcpol_find(&options, dhcptag_router_e, &len, NULL); 463 if (ip != NULL && len >= (int)sizeof(*ip)) { 464 context->router = *ip; 465 } 466 dhcpol_free(&options); 467 untimeout((timeout_fcn_t)dhcp_timeout, &timer_arg); 468 break; 469 470 next_packet: 471 dhcpol_free(&options); 472 } 473 else if ((error != EWOULDBLOCK)) { 474 /* if some other error occurred, we're done */ 475 untimeout((timeout_fcn_t)dhcp_timeout, &timer_arg); 476 break; 477 } 478 else if (timer_arg == NULL) { 479 /* timed out */ 480 break; 481 } 482 else { 483 /* wait for a wait to arrive, or a timeout to occur */ 484 socket_lock(context->so, 1); 485 error = sbwait(&context->so->so_rcv); 486 socket_unlock(context->so, 1); 487 } 488 } 489 return (error); 490} 491 492static int 493dhcp_select(struct dhcp_context * context) 494{ 495 struct timeval current_time; 496 int error = 0; 497 dhcpoa_t * options_p; 498 struct dhcp_packet * request; 499 int request_size; 500 int retry; 501 int wait_ticks; 502 503 /* format a DHCP Request packet */ 504 request = dhcp_context_request(context); 505 options_p = &context->request_options; 506 507 make_dhcp_request(&request->dhcp, DHCP_PAYLOAD_MIN, 508 dhcp_msgtype_request_e, 509 link_address(context->dl_p), ARPHRD_ETHER, 510 link_address_length(context->dl_p), 511 options_p); 512 /* insert server identifier and requested ip address */ 513 dhcpoa_add(options_p, dhcptag_requested_ip_address_e, 514 sizeof(context->iaddr), &context->iaddr); 515 dhcpoa_add(options_p, dhcptag_server_identifier_e, 516 sizeof(context->server_id), &context->server_id); 517 dhcpoa_add(options_p, dhcptag_end_e, 0, 0); 518 request_size = sizeof(*request) + RFC_MAGIC_SIZE 519 + dhcpoa_used(options_p); 520 if (request_size < (int)sizeof(struct bootp_packet)) { 521 /* pad out to BOOTP-sized packet */ 522 request_size = sizeof(struct bootp_packet); 523 } 524 init_dhcp_packet_header(request, request_size); 525 526 wait_ticks = INITIAL_WAIT_SECS * hz; 527#define SELECT_RETRY_COUNT 3 528 for (retry = 0; retry < SELECT_RETRY_COUNT; retry++) { 529 /* Send the request */ 530 printf("dhcp: sending REQUEST: server " IP_FORMAT 531 " IP address " IP_FORMAT "\n", 532 IP_LIST(&context->server_id), 533 IP_LIST(&context->iaddr)); 534 microtime(¤t_time); 535 request->dhcp.dp_secs 536 = htons((u_short) 537 (current_time.tv_sec - context->start_time.tv_sec)); 538 request->dhcp.dp_xid = htonl(context->xid); 539#ifdef RANDOM_IP_ID 540 request->ip.ip_id = ip_randomid(); 541#else 542 request->ip.ip_id = htons(ip_id++); 543#endif 544 error = send_packet(context->ifp, request, request_size); 545 if (error != 0) { 546 printf("dhcp: send_packet failed with %d\n", error); 547 goto failed; 548 } 549 550 wait_ticks += random_range(-RAND_TICKS, RAND_TICKS); 551 dprintf(("dhcp: waiting %d ticks\n", wait_ticks)); 552 error = dhcp_get_ack(context, wait_ticks); 553 switch (error) { 554 case 0: 555 /* we're done */ 556 goto done; 557 case EPROTO: 558 printf("dhcp: server " IP_FORMAT " send us a NAK\n", 559 IP_LIST(&context->server_id)); 560 goto failed; 561 case EWOULDBLOCK: 562 break; 563 default: 564 dprintf(("dhcp: failed to receive packets: %d\n", error)); 565 goto failed; 566 } 567 wait_ticks *= 2; 568 if (wait_ticks > (MAX_WAIT_SECS * hz)) 569 wait_ticks = MAX_WAIT_SECS * hz; 570 microtime(¤t_time); 571 } 572 error = ETIMEDOUT; 573 goto failed; 574 575 done: 576 error = 0; 577 578 failed: 579 return (error); 580} 581 582static int 583dhcp_get_offer(struct dhcp_context * context, int wait_ticks) 584{ 585 int error = 0; 586 int gather_count = 0; 587 const struct in_addr * ip; 588 int last_rating = 0; 589 int len; 590 int n; 591 int rating; 592 struct dhcp * reply; 593 struct in_addr server_id; 594 struct socket * timer_arg; 595 596 timer_arg = context->so; 597 reply = dhcp_context_reply(context); 598 timeout((timeout_fcn_t)dhcp_timeout, &timer_arg, wait_ticks); 599 while (1) { 600 error = receive_packet(context->so, context->reply, 601 sizeof(context->reply), &n); 602 if (error == 0) { 603 dhcpol_t options; 604 605 dprintf(("\ndhcp: received packet length %d\n", n)); 606 if (n < (int)sizeof(struct dhcp)) { 607 dprintf(("dhcp: packet is too short %d < %d\n", 608 n, (int)sizeof(struct dhcp))); 609 continue; 610 } 611 if (ntohl(reply->dp_xid) != context->xid 612 || reply->dp_yiaddr.s_addr == 0 613 || reply->dp_yiaddr.s_addr == INADDR_BROADCAST 614 || bcmp(reply->dp_chaddr, 615 link_address(context->dl_p), 616 link_address_length(context->dl_p)) != 0) { 617 /* not for us */ 618 continue; 619 } 620 (void)dhcpol_parse_packet(&options, reply, n); 621 if (get_dhcp_msgtype(&options) != dhcp_msgtype_offer_e) { 622 /* not an offer */ 623 goto next_packet; 624 } 625 ip = (const struct in_addr *) 626 dhcpol_find(&options, 627 dhcptag_server_identifier_e, &len, NULL); 628 if (ip == NULL || len < (int)sizeof(*ip)) { 629 /* missing/invalid server identifier */ 630 goto next_packet; 631 } 632 printf("dhcp: received OFFER: server " IP_FORMAT 633 " IP address " IP_FORMAT "\n", 634 IP_LIST(ip), IP_LIST(&reply->dp_yiaddr)); 635 server_id = *ip; 636 rating = rate_packet(&options); 637 if (rating > last_rating) { 638 context->iaddr = reply->dp_yiaddr; 639 ip = (const struct in_addr *) 640 dhcpol_find(&options, 641 dhcptag_subnet_mask_e, &len, NULL); 642 if (ip != NULL && len >= (int)sizeof(*ip)) { 643 context->netmask = *ip; 644 } 645 ip = (const struct in_addr *) 646 dhcpol_find(&options, dhcptag_router_e, &len, NULL); 647 if (ip != NULL && len >= (int)sizeof(*ip)) { 648 context->router = *ip; 649 } 650 context->server_id = server_id; 651 } 652 if (rating >= GOOD_RATING) { 653 dhcpol_free(&options); 654 /* packet is good enough */ 655 untimeout((timeout_fcn_t)dhcp_timeout, &timer_arg); 656 break; 657 } 658 if (gather_count == 0) { 659 untimeout((timeout_fcn_t)dhcp_timeout, &timer_arg); 660 timer_arg = context->so; 661 timeout((timeout_fcn_t)dhcp_timeout, &timer_arg, 662 hz * GATHER_TIME_SECS); 663 } 664 gather_count = 1; 665 next_packet: 666 dhcpol_free(&options); 667 } 668 else if ((error != EWOULDBLOCK)) { 669 untimeout((timeout_fcn_t)dhcp_timeout, &timer_arg); 670 break; 671 } 672 else if (timer_arg == NULL) { /* timed out */ 673 if (gather_count != 0) { 674 dprintf(("dhcp: gathering time has expired\n")); 675 error = 0; 676 } 677 break; 678 } 679 else { 680 socket_lock(context->so, 1); 681 error = sbwait(&context->so->so_rcv); 682 socket_unlock(context->so, 1); 683 } 684 } 685 return (error); 686} 687 688/* 689 * Function: dhcp_init 690 * Purpose: 691 * Start in the DHCP INIT state sending DISCOVER's. When we get OFFER's, 692 * try to select one of them by sending a REQUEST and waiting for an ACK. 693 */ 694static int 695dhcp_init(struct dhcp_context * context) 696{ 697 struct timeval current_time; 698 int error = 0; 699 uint32_t lease_option = htonl(SUGGESTED_LEASE_LENGTH); 700 dhcpoa_t * options_p; 701 struct dhcp_packet * request; 702 int request_size; 703 int retry; 704 int wait_ticks; 705 706 /* remember the time we started */ 707 microtime(&context->start_time); 708 current_time = context->start_time; 709 710 request = dhcp_context_request(context); 711 options_p = &context->request_options; 712 713 retry: 714 /* format a DHCP DISCOVER packet */ 715 make_dhcp_request(&request->dhcp, DHCP_PAYLOAD_MIN, 716 dhcp_msgtype_discover_e, 717 link_address(context->dl_p), ARPHRD_ETHER, 718 link_address_length(context->dl_p), 719 options_p); 720 /* add the requested lease time */ 721 dhcpoa_add(options_p, dhcptag_lease_time_e, 722 sizeof(lease_option), &lease_option); 723 dhcpoa_add(options_p, dhcptag_end_e, 0, 0); 724 request_size = sizeof(*request) + RFC_MAGIC_SIZE 725 + dhcpoa_used(options_p); 726 if (request_size < (int)sizeof(struct bootp_packet)) { 727 /* pad out to BOOTP-sized packet */ 728 request_size = sizeof(struct bootp_packet); 729 } 730 init_dhcp_packet_header(request, request_size); 731 732 wait_ticks = INITIAL_WAIT_SECS * hz; 733 for (retry = 0; retry < context->max_try; retry++) { 734 /* Send the request */ 735 printf("dhcp: sending DISCOVER\n"); 736 request->dhcp.dp_secs 737 = htons((u_short)(current_time.tv_sec 738 - context->start_time.tv_sec)); 739 request->dhcp.dp_xid = htonl(context->xid); 740#ifdef RANDOM_IP_ID 741 request->ip.ip_id = ip_randomid(); 742#else 743 request->ip.ip_id = htons(ip_id++); 744#endif 745 error = send_packet(context->ifp, request, request_size); 746 if (error != 0) { 747 printf("dhcp: send_packet failed with %d\n", error); 748 goto failed; 749 } 750 wait_ticks += random_range(-RAND_TICKS, RAND_TICKS); 751 dprintf(("dhcp: waiting %d ticks\n", wait_ticks)); 752 error = dhcp_get_offer(context, wait_ticks); 753 if (error == 0) { 754 /* send a REQUEST */ 755 error = dhcp_select(context); 756 if (error == 0) { 757 /* we're done !*/ 758 goto done; 759 } 760 if (error != EPROTO && error != ETIMEDOUT) { 761 /* fatal error */ 762 dprintf(("dhcp: dhcp_select failed %d\n", error)); 763 goto failed; 764 } 765 /* wait 10 seconds, and try again */ 766 printf("dhcp: trying again in 10 seconds\n"); 767 tsleep(&error, PRIBIO, "dhcp_init", 10 * hz); 768 context->xid++; 769 goto retry; 770 } 771 else if (error != EWOULDBLOCK) { 772 dprintf(("dhcp: failed to receive packets: %d\n", error)); 773 goto failed; 774 } 775 wait_ticks *= 2; 776 if (wait_ticks > (MAX_WAIT_SECS * hz)) 777 wait_ticks = MAX_WAIT_SECS * hz; 778 microtime(¤t_time); 779 } 780 error = ETIMEDOUT; 781 goto failed; 782 783 done: 784 error = 0; 785 786 failed: 787 return (error); 788} 789 790static void 791dhcp_context_free(struct dhcp_context * context, struct proc * procp) 792{ 793 if (context == NULL) { 794 return; 795 } 796 if (context->so != NULL) { 797 int error; 798 799 /* disable reception of DHCP packets before address assignment */ 800 context->ifr.ifr_intval = 0; 801 error = ifioctl(context->so, SIOCAUTOADDR, 802 (caddr_t)&context->ifr, procp); 803 if (error) { 804 printf("dhcp: SIOCAUTOADDR failed: %d\n", error); 805 } 806 soclose(context->so); 807 } 808 kfree(context, sizeof(*context)); 809 return; 810} 811 812static struct dhcp_context * 813dhcp_context_create(struct ifnet * ifp, int max_try, 814 struct proc * procp, int * error_p) 815{ 816 struct dhcp_context * context = NULL; 817 struct sockaddr_dl * dl_p; 818 struct in_addr lo_addr; 819 struct in_addr lo_mask; 820 int error; 821 struct sockaddr_in sin; 822 823 /* get the hardware address from the interface */ 824 dl_p = link_from_ifnet(ifp); 825 if (dl_p == NULL) { 826 printf("dhcp: can't get link address\n"); 827 error = ENXIO; 828 goto failed; 829 } 830 831 printf("dhcp: h/w addr "); 832 link_print(dl_p); 833 if (dl_p->sdl_type != IFT_ETHER) { 834 printf("dhcp: hardware type %d not supported\n", 835 dl_p->sdl_type); 836 error = ENXIO; 837 goto failed; 838 } 839 840 context = (struct dhcp_context *)kalloc(sizeof(*context)); 841 if (context == NULL) { 842 printf("dhcp: failed to allocate context\n"); 843 error = ENOMEM; 844 goto failed; 845 } 846 bzero(context, sizeof(*context)); 847 848 /* get a socket */ 849 error = socreate(AF_INET, &context->so, SOCK_DGRAM, 0); 850 if (error != 0) { 851 printf("dhcp: socreate failed %d\n", error); 852 goto failed; 853 } 854 855 /* assign 127.0.0.1 to lo0 so that the bind will succeed */ 856 lo_addr.s_addr = htonl(INADDR_LOOPBACK); 857 lo_mask.s_addr = htonl(IN_CLASSA_NET); 858 error = inet_aifaddr(context->so, "lo0", &lo_addr, &lo_mask, NULL); 859 if (error != 0) { 860 printf("dhcp: assigning loopback address failed %d\n", error); 861 } 862 863 /* enable reception of DHCP packets before an address is assigned */ 864 snprintf(context->ifr.ifr_name, 865 sizeof(context->ifr.ifr_name), "%s%d", ifp->if_name, 866 ifp->if_unit); 867 context->ifr.ifr_intval = 1; 868 869 error = ifioctl(context->so, SIOCAUTOADDR, (caddr_t)&context->ifr, procp); 870 if (error) { 871 printf("dhcp: SIOCAUTOADDR failed: %d\n", error); 872 goto failed; 873 } 874 dprintf(("dhcp: SIOCAUTOADDR done\n")); 875 876 error = ifioctl(context->so, SIOCPROTOATTACH, (caddr_t)&context->ifr, 877 procp); 878 if (error) { 879 printf("dhcp: SIOCPROTOATTACH failed: %d\n", error); 880 goto failed; 881 } 882 dprintf(("dhcp: SIOCPROTOATTACH done\n")); 883 884 /* bind the socket */ 885 sin.sin_len = sizeof(sin); 886 sin.sin_family = AF_INET; 887 sin.sin_port = htons(IPPORT_BOOTPC); 888 sin.sin_addr.s_addr = INADDR_ANY; 889 error = sobind(context->so, (struct sockaddr *)&sin); 890 if (error) { 891 printf("dhcp: sobind failed, %d\n", error); 892 goto failed; 893 } 894 895 /* make it non-blocking I/O */ 896 socket_lock(context->so, 1); 897 context->so->so_state |= SS_NBIO; 898 socket_unlock(context->so, 1); 899 900 /* save passed-in information */ 901 context->max_try = max_try; 902 context->dl_p = dl_p; 903 context->ifp = ifp; 904 905 /* get a random transaction id */ 906 context->xid = random(); 907 908 return (context); 909 910 failed: 911 dhcp_context_free(context, procp); 912 *error_p = error; 913 return (NULL); 914} 915 916/* 917 * Routine: dhcp 918 * Function: 919 * Do DHCP over the specified interface to retrieve the IP address, 920 * subnet mask, and router. 921 */ 922int 923dhcp(struct ifnet * ifp, struct in_addr * iaddr_p, int max_try, 924 struct in_addr * netmask_p, struct in_addr * router_p, 925 struct proc * procp) 926{ 927 int error = 0; 928 struct dhcp_context * context; 929 930 context = dhcp_context_create(ifp, max_try, procp, &error); 931 if (context == NULL) { 932 return (error); 933 } 934 935 /* start DHCP in the INIT state */ 936 error = dhcp_init(context); 937 if (error == 0) { 938 *iaddr_p = context->iaddr; 939 *netmask_p = context->netmask; 940 *router_p = context->router; 941 } 942 dhcp_context_free(context, procp); 943 return (error); 944} 945