/* * Copyright (c) 1988-2013 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. The rights granted to you under the License * may not be used to create, or enable the creation or redistribution of, * unlawful or unlicensed copies of an Apple operating system, or to * circumvent, violate, or enable the circumvention or violation of, any * terms of an Apple operating system software license agreement. * * Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ /* * in_dhcp.c * - use DHCP to allocate an IP address and get the subnet mask and router */ /* * Modification History * * April 17, 2007 Dieter Siegmund (dieter@apple.com) * - created based on in_bootp.c */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DHCP_DEBUG #define dprintf(x) printf x; #else /* !DHCP_DEBUG */ #define dprintf(x) #endif /* DHCP_DEBUG */ #define INITIAL_WAIT_SECS 2 #define MAX_WAIT_SECS 64 #define GATHER_TIME_SECS 4 #define RAND_TICKS (hz) /* one second */ const struct sockaddr_in blank_sin = { sizeof(struct sockaddr_in), AF_INET, 0, { 0 }, { 0, 0, 0, 0, 0, 0, 0, 0 } }; __private_extern__ int inet_aifaddr(struct socket * so, const char * name, const struct in_addr * addr, const struct in_addr * mask, const struct in_addr * broadcast) { struct ifaliasreq ifra; bzero(&ifra, sizeof(ifra)); strlcpy(ifra.ifra_name, name, sizeof(ifra.ifra_name)); if (addr) { *((struct sockaddr_in *)(void *)&ifra.ifra_addr) = blank_sin; ((struct sockaddr_in *)(void *)&ifra.ifra_addr)->sin_addr = *addr; } if (mask) { *((struct sockaddr_in *)(void *)&ifra.ifra_mask) = blank_sin; ((struct sockaddr_in *)(void *)&ifra.ifra_mask)->sin_addr = *mask; } if (broadcast) { *((struct sockaddr_in *)(void *)&ifra.ifra_broadaddr) = blank_sin; ((struct sockaddr_in *)(void *)&ifra.ifra_broadaddr)->sin_addr = *broadcast; } return (ifioctl(so, SIOCAIFADDR, (caddr_t)&ifra, current_proc())); } struct dhcp_context { struct ifnet * ifp; struct sockaddr_dl * dl_p; struct ifreq ifr; struct socket * so; uint8_t request[DHCP_PACKET_MIN]; dhcpoa_t request_options; uint8_t reply[DHCP_PAYLOAD_MIN]; struct timeval start_time; uint32_t xid; int max_try; struct in_addr iaddr; struct in_addr netmask; struct in_addr router; struct in_addr server_id; }; static __inline__ struct dhcp_packet * dhcp_context_request(struct dhcp_context * context) { return ((struct dhcp_packet *)(void *)context->request); } static __inline__ struct dhcp * dhcp_context_reply(struct dhcp_context * context) { return ((struct dhcp *)(void *)context->reply); } struct mbuf * ip_pkt_to_mbuf(caddr_t pkt, int pktsize); static int receive_packet(struct socket * so, void * pp, int psize, int * actual_size); /* ip address formatting macros */ #define IP_FORMAT "%d.%d.%d.%d" #define IP_CH(ip) ((const uint8_t *)ip) #define IP_LIST(ip) IP_CH(ip)[0],IP_CH(ip)[1],IP_CH(ip)[2],IP_CH(ip)[3] #define SUGGESTED_LEASE_LENGTH (60 * 60 * 24 * 30 * 3) /* 3 months */ static const uint8_t dhcp_params[] = { dhcptag_subnet_mask_e, dhcptag_router_e, }; #define N_DHCP_PARAMS (sizeof(dhcp_params) / sizeof(dhcp_params[0])) static __inline__ long random_range(long bottom, long top) { long number = top - bottom + 1; long range_size = LONG_MAX / number; return (((long)random()) / range_size + bottom); } static void init_dhcp_packet_header(struct dhcp_packet * pkt, int pkt_size) { bzero(&pkt->ip, sizeof(pkt->ip)); bzero(&pkt->udp, sizeof(pkt->udp)); pkt->ip.ip_v = IPVERSION; pkt->ip.ip_hl = sizeof(struct ip) >> 2; pkt->ip.ip_ttl = MAXTTL; pkt->ip.ip_p = IPPROTO_UDP; pkt->ip.ip_src.s_addr = 0; pkt->ip.ip_dst.s_addr = htonl(INADDR_BROADCAST); pkt->ip.ip_len = htons(pkt_size); pkt->ip.ip_sum = 0; pkt->udp.uh_sport = htons(IPPORT_BOOTPC); pkt->udp.uh_dport = htons(IPPORT_BOOTPS); pkt->udp.uh_sum = 0; pkt->udp.uh_ulen = htons(pkt_size - sizeof(pkt->ip)); return; } /* * Function: make_dhcp_request * Purpose: * Initialize the DHCP-specific parts of the message. */ static void make_dhcp_request(struct dhcp * request, int request_size, dhcp_msgtype_t msg, const uint8_t * hwaddr, uint8_t hwtype, int hwlen, dhcpoa_t * options_p) { uint8_t cid[ETHER_ADDR_LEN + 1]; uint8_t rfc_magic[RFC_MAGIC_SIZE] = RFC_OPTIONS_MAGIC; if (hwlen >= (int)sizeof(cid)) { printf("dhcp: hwlen is %d (> %d), truncating\n", hwlen, (int)sizeof(cid)); hwlen = sizeof(cid) - 1; } bzero(request, request_size); request->dp_op = BOOTREQUEST; request->dp_htype = hwtype; request->dp_hlen = hwlen; bcopy(hwaddr, request->dp_chaddr, hwlen); bcopy(rfc_magic, request->dp_options, RFC_MAGIC_SIZE); dhcpoa_init(options_p, request->dp_options + RFC_MAGIC_SIZE, request_size - sizeof(struct dhcp) - RFC_MAGIC_SIZE); /* make the request a dhcp packet */ dhcpoa_add_dhcpmsg(options_p, msg); /* add the list of required parameters */ dhcpoa_add(options_p, dhcptag_parameter_request_list_e, N_DHCP_PARAMS, dhcp_params); /* add the DHCP client identifier */ cid[0] = hwtype; bcopy(hwaddr, cid + 1, hwlen); dhcpoa_add(options_p, dhcptag_client_identifier_e, hwlen + 1, cid); return; } /* * Function: ip_pkt_to_mbuf * Purpose: * Put the given IP packet into an mbuf, calculate the * IP checksum. */ struct mbuf * ip_pkt_to_mbuf(caddr_t pkt, int pktsize) { struct ip * ip; struct mbuf * m; m = (struct mbuf *)m_devget(pkt, pktsize, 0, NULL, NULL); if (m == 0) { printf("dhcp: ip_pkt_to_mbuf: m_devget failed\n"); return NULL; } m->m_flags |= M_BCAST; /* Compute the checksum */ ip = mtod(m, struct ip *); ip->ip_sum = 0; ip->ip_sum = in_cksum(m, sizeof(struct ip)); return (m); } static __inline__ u_char * link_address(struct sockaddr_dl * dl_p) { return (u_char *)(dl_p->sdl_data + dl_p->sdl_nlen); } static __inline__ int link_address_length(struct sockaddr_dl * dl_p) { return (dl_p->sdl_alen); } static __inline__ void link_print(struct sockaddr_dl * dl_p) { int i; for (i = 0; i < dl_p->sdl_alen; i++) printf("%s%x", i ? ":" : "", (link_address(dl_p))[i]); printf("\n"); return; } static struct sockaddr_dl * link_from_ifnet(struct ifnet * ifp) { return ((struct sockaddr_dl *)(void *)ifp->if_lladdr->ifa_addr); } /* * Function: send_packet * Purpose: * Send the request directly on the interface, bypassing the routing code. */ static int send_packet(struct ifnet * ifp, struct dhcp_packet * pkt, int pkt_size) { struct mbuf * m; struct sockaddr_in dest; dest = blank_sin; dest.sin_port = htons(IPPORT_BOOTPS); dest.sin_addr.s_addr = INADDR_BROADCAST; m = ip_pkt_to_mbuf((caddr_t)pkt, pkt_size); return dlil_output(ifp, PF_INET, m, 0, (struct sockaddr *)&dest, 0, NULL); } /* * Function: receive_packet * Purpose: * Return a received packet or an error if none available. */ static int receive_packet(struct socket * so, void * pp, int psize, int * actual_size) { uio_t auio; int error; int rcvflg; char uio_buf[ UIO_SIZEOF(1) ]; auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ, &uio_buf[0], sizeof(uio_buf)); uio_addiov(auio, CAST_USER_ADDR_T(pp), psize); rcvflg = MSG_WAITALL; error = soreceive(so, (struct sockaddr **) 0, auio, 0, 0, &rcvflg); *actual_size = psize - uio_resid(auio); return (error); } /* * Function: dhcp_timeout * Purpose: * Wakeup the process waiting for something on a socket. */ static void dhcp_timeout(void * arg) { struct socket * * timer_arg = (struct socket * *)arg; struct socket * so = *timer_arg; dprintf(("dhcp: timeout\n")); *timer_arg = NULL; socket_lock(so, 1); sowakeup(so, &so->so_rcv); socket_unlock(so, 1); return; } /* * Function: rate_packet * Purpose: * Return an integer point rating value for the given dhcp packet. * If yiaddr non-zero, the packet gets a rating of 1. * Another point is given if the packet contains the subnet mask, * and another if the router is present. */ #define GOOD_RATING 3 static __inline__ int rate_packet(dhcpol_t * options_p) { int len; int rating = 1; if (dhcpol_find(options_p, dhcptag_subnet_mask_e, &len, NULL) != NULL) { rating++; } if (dhcpol_find(options_p, dhcptag_router_e, &len, NULL) != NULL) { rating++; } return (rating); } static dhcp_msgtype_t get_dhcp_msgtype(dhcpol_t * options_p) { int len; const uint8_t * opt; opt = dhcpol_find(options_p, dhcptag_dhcp_message_type_e, &len, NULL); if (opt != NULL && len == 1) { return (*opt); } return (dhcp_msgtype_none_e); } static int dhcp_get_ack(struct dhcp_context * context, int wait_ticks) { int error = 0; const struct in_addr * ip; int len; int n; struct dhcp * reply; struct in_addr server_id; struct socket * timer_arg; timer_arg = context->so; reply = dhcp_context_reply(context); timeout((timeout_fcn_t)dhcp_timeout, &timer_arg, wait_ticks); while (1) { error = receive_packet(context->so, context->reply, sizeof(context->reply), &n); if (error == 0) { dhcp_msgtype_t msg; dhcpol_t options; dprintf(("\ndhcp: received packet length %d\n", n)); if (n < (int)sizeof(struct dhcp)) { dprintf(("dhcp: packet is too short %d < %d\n", n, (int)sizeof(struct dhcp))); continue; } if (ntohl(reply->dp_xid) != context->xid || bcmp(reply->dp_chaddr, link_address(context->dl_p), link_address_length(context->dl_p)) != 0) { /* not for us */ continue; } (void)dhcpol_parse_packet(&options, reply, n); server_id.s_addr = 0; ip = (const struct in_addr *) dhcpol_find(&options, dhcptag_server_identifier_e, &len, NULL); if (ip != NULL && len >= (int)sizeof(*ip)) { server_id = *ip; } msg = get_dhcp_msgtype(&options); if (msg == dhcp_msgtype_nak_e && server_id.s_addr == context->server_id.s_addr) { /* server NAK'd us, start over */ dhcpol_free(&options); error = EPROTO; untimeout((timeout_fcn_t)dhcp_timeout, &timer_arg); break; } if (msg != dhcp_msgtype_ack_e || reply->dp_yiaddr.s_addr == 0 || reply->dp_yiaddr.s_addr == INADDR_BROADCAST) { /* ignore the packet */ goto next_packet; } printf("dhcp: received ACK: server " IP_FORMAT " IP address " IP_FORMAT "\n", IP_LIST(&server_id), IP_LIST(&reply->dp_yiaddr)); context->iaddr = reply->dp_yiaddr; ip = (const struct in_addr *) dhcpol_find(&options, dhcptag_subnet_mask_e, &len, NULL); if (ip != NULL && len >= (int)sizeof(*ip)) { context->netmask = *ip; } ip = (const struct in_addr *) dhcpol_find(&options, dhcptag_router_e, &len, NULL); if (ip != NULL && len >= (int)sizeof(*ip)) { context->router = *ip; } dhcpol_free(&options); untimeout((timeout_fcn_t)dhcp_timeout, &timer_arg); break; next_packet: dhcpol_free(&options); } else if ((error != EWOULDBLOCK)) { /* if some other error occurred, we're done */ untimeout((timeout_fcn_t)dhcp_timeout, &timer_arg); break; } else if (timer_arg == NULL) { /* timed out */ break; } else { /* wait for a wait to arrive, or a timeout to occur */ socket_lock(context->so, 1); error = sbwait(&context->so->so_rcv); socket_unlock(context->so, 1); } } return (error); } static int dhcp_select(struct dhcp_context * context) { struct timeval current_time; int error = 0; dhcpoa_t * options_p; struct dhcp_packet * request; int request_size; int retry; int wait_ticks; /* format a DHCP Request packet */ request = dhcp_context_request(context); options_p = &context->request_options; make_dhcp_request(&request->dhcp, DHCP_PAYLOAD_MIN, dhcp_msgtype_request_e, link_address(context->dl_p), ARPHRD_ETHER, link_address_length(context->dl_p), options_p); /* insert server identifier and requested ip address */ dhcpoa_add(options_p, dhcptag_requested_ip_address_e, sizeof(context->iaddr), &context->iaddr); dhcpoa_add(options_p, dhcptag_server_identifier_e, sizeof(context->server_id), &context->server_id); dhcpoa_add(options_p, dhcptag_end_e, 0, 0); request_size = sizeof(*request) + RFC_MAGIC_SIZE + dhcpoa_used(options_p); if (request_size < (int)sizeof(struct bootp_packet)) { /* pad out to BOOTP-sized packet */ request_size = sizeof(struct bootp_packet); } init_dhcp_packet_header(request, request_size); wait_ticks = INITIAL_WAIT_SECS * hz; #define SELECT_RETRY_COUNT 3 for (retry = 0; retry < SELECT_RETRY_COUNT; retry++) { /* Send the request */ printf("dhcp: sending REQUEST: server " IP_FORMAT " IP address " IP_FORMAT "\n", IP_LIST(&context->server_id), IP_LIST(&context->iaddr)); microtime(¤t_time); request->dhcp.dp_secs = htons((u_short) (current_time.tv_sec - context->start_time.tv_sec)); request->dhcp.dp_xid = htonl(context->xid); request->ip.ip_id = ip_randomid(); error = send_packet(context->ifp, request, request_size); if (error != 0) { printf("dhcp: send_packet failed with %d\n", error); goto failed; } wait_ticks += random_range(-RAND_TICKS, RAND_TICKS); dprintf(("dhcp: waiting %d ticks\n", wait_ticks)); error = dhcp_get_ack(context, wait_ticks); switch (error) { case 0: /* we're done */ goto done; case EPROTO: printf("dhcp: server " IP_FORMAT " send us a NAK\n", IP_LIST(&context->server_id)); goto failed; case EWOULDBLOCK: break; default: dprintf(("dhcp: failed to receive packets: %d\n", error)); goto failed; } wait_ticks *= 2; if (wait_ticks > (MAX_WAIT_SECS * hz)) wait_ticks = MAX_WAIT_SECS * hz; microtime(¤t_time); } error = ETIMEDOUT; goto failed; done: error = 0; failed: return (error); } static int dhcp_get_offer(struct dhcp_context * context, int wait_ticks) { int error = 0; int gather_count = 0; const struct in_addr * ip; int last_rating = 0; int len; int n; int rating; struct dhcp * reply; struct in_addr server_id; struct socket * timer_arg; timer_arg = context->so; reply = dhcp_context_reply(context); timeout((timeout_fcn_t)dhcp_timeout, &timer_arg, wait_ticks); while (1) { error = receive_packet(context->so, context->reply, sizeof(context->reply), &n); if (error == 0) { dhcpol_t options; dprintf(("\ndhcp: received packet length %d\n", n)); if (n < (int)sizeof(struct dhcp)) { dprintf(("dhcp: packet is too short %d < %d\n", n, (int)sizeof(struct dhcp))); continue; } if (ntohl(reply->dp_xid) != context->xid || reply->dp_yiaddr.s_addr == 0 || reply->dp_yiaddr.s_addr == INADDR_BROADCAST || bcmp(reply->dp_chaddr, link_address(context->dl_p), link_address_length(context->dl_p)) != 0) { /* not for us */ continue; } (void)dhcpol_parse_packet(&options, reply, n); if (get_dhcp_msgtype(&options) != dhcp_msgtype_offer_e) { /* not an offer */ goto next_packet; } ip = (const struct in_addr *) dhcpol_find(&options, dhcptag_server_identifier_e, &len, NULL); if (ip == NULL || len < (int)sizeof(*ip)) { /* missing/invalid server identifier */ goto next_packet; } printf("dhcp: received OFFER: server " IP_FORMAT " IP address " IP_FORMAT "\n", IP_LIST(ip), IP_LIST(&reply->dp_yiaddr)); server_id = *ip; rating = rate_packet(&options); if (rating > last_rating) { context->iaddr = reply->dp_yiaddr; ip = (const struct in_addr *) dhcpol_find(&options, dhcptag_subnet_mask_e, &len, NULL); if (ip != NULL && len >= (int)sizeof(*ip)) { context->netmask = *ip; } ip = (const struct in_addr *) dhcpol_find(&options, dhcptag_router_e, &len, NULL); if (ip != NULL && len >= (int)sizeof(*ip)) { context->router = *ip; } context->server_id = server_id; } if (rating >= GOOD_RATING) { dhcpol_free(&options); /* packet is good enough */ untimeout((timeout_fcn_t)dhcp_timeout, &timer_arg); break; } if (gather_count == 0) { untimeout((timeout_fcn_t)dhcp_timeout, &timer_arg); timer_arg = context->so; timeout((timeout_fcn_t)dhcp_timeout, &timer_arg, hz * GATHER_TIME_SECS); } gather_count = 1; next_packet: dhcpol_free(&options); } else if ((error != EWOULDBLOCK)) { untimeout((timeout_fcn_t)dhcp_timeout, &timer_arg); break; } else if (timer_arg == NULL) { /* timed out */ if (gather_count != 0) { dprintf(("dhcp: gathering time has expired\n")); error = 0; } break; } else { socket_lock(context->so, 1); error = sbwait(&context->so->so_rcv); socket_unlock(context->so, 1); } } return (error); } /* * Function: dhcp_init * Purpose: * Start in the DHCP INIT state sending DISCOVER's. When we get OFFER's, * try to select one of them by sending a REQUEST and waiting for an ACK. */ static int dhcp_init(struct dhcp_context * context) { struct timeval current_time; int error = 0; uint32_t lease_option = htonl(SUGGESTED_LEASE_LENGTH); dhcpoa_t * options_p; struct dhcp_packet * request; int request_size; int retry; int wait_ticks; /* remember the time we started */ microtime(&context->start_time); current_time = context->start_time; request = dhcp_context_request(context); options_p = &context->request_options; retry: /* format a DHCP DISCOVER packet */ make_dhcp_request(&request->dhcp, DHCP_PAYLOAD_MIN, dhcp_msgtype_discover_e, link_address(context->dl_p), ARPHRD_ETHER, link_address_length(context->dl_p), options_p); /* add the requested lease time */ dhcpoa_add(options_p, dhcptag_lease_time_e, sizeof(lease_option), &lease_option); dhcpoa_add(options_p, dhcptag_end_e, 0, 0); request_size = sizeof(*request) + RFC_MAGIC_SIZE + dhcpoa_used(options_p); if (request_size < (int)sizeof(struct bootp_packet)) { /* pad out to BOOTP-sized packet */ request_size = sizeof(struct bootp_packet); } init_dhcp_packet_header(request, request_size); wait_ticks = INITIAL_WAIT_SECS * hz; for (retry = 0; retry < context->max_try; retry++) { /* Send the request */ printf("dhcp: sending DISCOVER\n"); request->dhcp.dp_secs = htons((u_short)(current_time.tv_sec - context->start_time.tv_sec)); request->dhcp.dp_xid = htonl(context->xid); request->ip.ip_id = ip_randomid(); error = send_packet(context->ifp, request, request_size); if (error != 0) { printf("dhcp: send_packet failed with %d\n", error); goto failed; } wait_ticks += random_range(-RAND_TICKS, RAND_TICKS); dprintf(("dhcp: waiting %d ticks\n", wait_ticks)); error = dhcp_get_offer(context, wait_ticks); if (error == 0) { /* send a REQUEST */ error = dhcp_select(context); if (error == 0) { /* we're done !*/ goto done; } if (error != EPROTO && error != ETIMEDOUT) { /* fatal error */ dprintf(("dhcp: dhcp_select failed %d\n", error)); goto failed; } /* wait 10 seconds, and try again */ printf("dhcp: trying again in 10 seconds\n"); tsleep(&error, PRIBIO, "dhcp_init", 10 * hz); context->xid++; goto retry; } else if (error != EWOULDBLOCK) { dprintf(("dhcp: failed to receive packets: %d\n", error)); goto failed; } wait_ticks *= 2; if (wait_ticks > (MAX_WAIT_SECS * hz)) wait_ticks = MAX_WAIT_SECS * hz; microtime(¤t_time); } error = ETIMEDOUT; goto failed; done: error = 0; failed: return (error); } static void dhcp_context_free(struct dhcp_context * context, struct proc * procp) { if (context == NULL) { return; } if (context->so != NULL) { int error; /* disable reception of DHCP packets before address assignment */ context->ifr.ifr_intval = 0; error = ifioctl(context->so, SIOCAUTOADDR, (caddr_t)&context->ifr, procp); if (error) { printf("dhcp: SIOCAUTOADDR failed: %d\n", error); } soclose(context->so); } kfree(context, sizeof(*context)); return; } static struct dhcp_context * dhcp_context_create(struct ifnet * ifp, int max_try, struct proc * procp, int * error_p) { struct dhcp_context * context = NULL; struct sockaddr_dl * dl_p; struct in_addr lo_addr; struct in_addr lo_mask; int error; struct sockaddr_in sin; /* get the hardware address from the interface */ dl_p = link_from_ifnet(ifp); if (dl_p == NULL) { printf("dhcp: can't get link address\n"); error = ENXIO; goto failed; } printf("dhcp: h/w addr "); link_print(dl_p); if (dl_p->sdl_type != IFT_ETHER) { printf("dhcp: hardware type %d not supported\n", dl_p->sdl_type); error = ENXIO; goto failed; } context = (struct dhcp_context *)kalloc(sizeof(*context)); if (context == NULL) { printf("dhcp: failed to allocate context\n"); error = ENOMEM; goto failed; } bzero(context, sizeof(*context)); /* get a socket */ error = socreate(AF_INET, &context->so, SOCK_DGRAM, 0); if (error != 0) { printf("dhcp: socreate failed %d\n", error); goto failed; } /* assign 127.0.0.1 to lo0 so that the bind will succeed */ lo_addr.s_addr = htonl(INADDR_LOOPBACK); lo_mask.s_addr = htonl(IN_CLASSA_NET); error = inet_aifaddr(context->so, "lo0", &lo_addr, &lo_mask, NULL); if (error != 0) { printf("dhcp: assigning loopback address failed %d\n", error); } /* enable reception of DHCP packets before an address is assigned */ snprintf(context->ifr.ifr_name, sizeof(context->ifr.ifr_name), "%s", if_name(ifp)); context->ifr.ifr_intval = 1; error = ifioctl(context->so, SIOCAUTOADDR, (caddr_t)&context->ifr, procp); if (error) { printf("dhcp: SIOCAUTOADDR failed: %d\n", error); goto failed; } dprintf(("dhcp: SIOCAUTOADDR done\n")); error = ifioctl(context->so, SIOCPROTOATTACH, (caddr_t)&context->ifr, procp); if (error) { printf("dhcp: SIOCPROTOATTACH failed: %d\n", error); goto failed; } dprintf(("dhcp: SIOCPROTOATTACH done\n")); /* bind the socket */ sin.sin_len = sizeof(sin); sin.sin_family = AF_INET; sin.sin_port = htons(IPPORT_BOOTPC); sin.sin_addr.s_addr = INADDR_ANY; error = sobindlock(context->so, (struct sockaddr *)&sin, 1); if (error) { printf("dhcp: sobind failed, %d\n", error); goto failed; } /* make it non-blocking I/O */ socket_lock(context->so, 1); context->so->so_state |= SS_NBIO; socket_unlock(context->so, 1); /* save passed-in information */ context->max_try = max_try; context->dl_p = dl_p; context->ifp = ifp; /* get a random transaction id */ context->xid = random(); return (context); failed: dhcp_context_free(context, procp); *error_p = error; return (NULL); } /* * Routine: dhcp * Function: * Do DHCP over the specified interface to retrieve the IP address, * subnet mask, and router. */ int dhcp(struct ifnet * ifp, struct in_addr * iaddr_p, int max_try, struct in_addr * netmask_p, struct in_addr * router_p, struct proc * procp) { int error = 0; struct dhcp_context * context; context = dhcp_context_create(ifp, max_try, procp, &error); if (context == NULL) { return (error); } /* start DHCP in the INIT state */ error = dhcp_init(context); if (error == 0) { *iaddr_p = context->iaddr; *netmask_p = context->netmask; *router_p = context->router; } dhcp_context_free(context, procp); return (error); }