2 * Copyright (c) 1988-2011 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
31 * - use DHCP to allocate an IP address and get the subnet mask and router
35 * Modification History
37 * April 17, 2007 Dieter Siegmund (dieter@apple.com)
38 * - created based on in_bootp.c
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>
48 #include <sys/ioctl.h>
51 #include <sys/vnode.h>
52 #include <sys/socket.h>
53 #include <sys/socketvar.h>
54 #include <sys/uio_internal.h>
56 #include <net/if_dl.h>
57 #include <net/if_types.h>
58 #include <net/route.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>
75 #include <kern/kern_types.h>
76 #include <kern/kalloc.h>
79 #define dprintf(x) printf x;
80 #else /* !DHCP_DEBUG */
82 #endif /* DHCP_DEBUG */
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 */
89 const struct sockaddr_in blank_sin
= {
90 sizeof(struct sockaddr_in
),
94 { 0, 0, 0, 0, 0, 0, 0, 0 }
97 __private_extern__
int
98 inet_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
)
103 struct ifaliasreq ifra
;
105 bzero(&ifra
, sizeof(ifra
));
106 strlcpy(ifra
.ifra_name
, name
, sizeof(ifra
.ifra_name
));
108 *((struct sockaddr_in
*)(void *)&ifra
.ifra_addr
) = blank_sin
;
109 ((struct sockaddr_in
*)(void *)&ifra
.ifra_addr
)->sin_addr
= *addr
;
112 *((struct sockaddr_in
*)(void *)&ifra
.ifra_mask
) = blank_sin
;
113 ((struct sockaddr_in
*)(void *)&ifra
.ifra_mask
)->sin_addr
= *mask
;
116 *((struct sockaddr_in
*)(void *)&ifra
.ifra_broadaddr
) = blank_sin
;
117 ((struct sockaddr_in
*)(void *)&ifra
.ifra_broadaddr
)->sin_addr
= *broadcast
;
119 return (ifioctl(so
, SIOCAIFADDR
, (caddr_t
)&ifra
, current_proc()));
123 struct dhcp_context
{
125 struct sockaddr_dl
* dl_p
;
128 uint8_t request
[DHCP_PACKET_MIN
];
129 dhcpoa_t request_options
;
130 uint8_t reply
[DHCP_PAYLOAD_MIN
];
131 struct timeval start_time
;
134 struct in_addr iaddr
;
135 struct in_addr netmask
;
136 struct in_addr router
;
137 struct in_addr server_id
;
140 static __inline__
struct dhcp_packet
*
141 dhcp_context_request(struct dhcp_context
* context
)
143 return ((struct dhcp_packet
*)(void *)context
->request
);
146 static __inline__
struct dhcp
*
147 dhcp_context_reply(struct dhcp_context
* context
)
149 return ((struct dhcp
*)(void *)context
->reply
);
152 struct mbuf
* ip_pkt_to_mbuf(caddr_t pkt
, int pktsize
);
155 receive_packet(struct socket
* so
, void * pp
, int psize
,
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]
163 #define SUGGESTED_LEASE_LENGTH (60 * 60 * 24 * 30 * 3) /* 3 months */
165 static const uint8_t dhcp_params
[] = {
166 dhcptag_subnet_mask_e
,
170 #define N_DHCP_PARAMS (sizeof(dhcp_params) / sizeof(dhcp_params[0]))
172 static __inline__
long
173 random_range(long bottom
, long top
)
175 long number
= top
- bottom
+ 1;
176 long range_size
= LONG_MAX
/ number
;
177 return (((long)random()) / range_size
+ bottom
);
181 init_dhcp_packet_header(struct dhcp_packet
* pkt
, int pkt_size
)
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
);
193 pkt
->udp
.uh_sport
= htons(IPPORT_BOOTPC
);
194 pkt
->udp
.uh_dport
= htons(IPPORT_BOOTPS
);
196 pkt
->udp
.uh_ulen
= htons(pkt_size
- sizeof(pkt
->ip
));
201 * Function: make_dhcp_request
203 * Initialize the DHCP-specific parts of the message.
206 make_dhcp_request(struct dhcp
* request
, int request_size
,
208 const uint8_t * hwaddr
, uint8_t hwtype
, int hwlen
,
209 dhcpoa_t
* options_p
)
211 uint8_t cid
[ETHER_ADDR_LEN
+ 1];
212 uint8_t rfc_magic
[RFC_MAGIC_SIZE
] = RFC_OPTIONS_MAGIC
;
214 if (hwlen
> (int)sizeof(cid
)) {
215 printf("dhcp: hwlen is %d (> %d), truncating\n", hwlen
,
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
);
230 /* add the list of required parameters */
231 dhcpoa_add(options_p
, dhcptag_parameter_request_list_e
,
232 N_DHCP_PARAMS
, dhcp_params
);
234 /* add the DHCP client identifier */
236 bcopy(hwaddr
, cid
+ 1, hwlen
);
237 dhcpoa_add(options_p
, dhcptag_client_identifier_e
, hwlen
+ 1, cid
);
243 * Function: ip_pkt_to_mbuf
245 * Put the given IP packet into an mbuf, calculate the
249 ip_pkt_to_mbuf(caddr_t pkt
, int pktsize
)
254 m
= (struct mbuf
*)m_devget(pkt
, pktsize
, 0, NULL
, NULL
);
256 printf("dhcp: ip_pkt_to_mbuf: m_devget failed\n");
259 m
->m_flags
|= M_BCAST
;
260 /* Compute the checksum */
261 ip
= mtod(m
, struct ip
*);
263 ip
->ip_sum
= in_cksum(m
, sizeof(struct ip
));
267 static __inline__ u_char
*
268 link_address(struct sockaddr_dl
* dl_p
)
270 return (u_char
*)(dl_p
->sdl_data
+ dl_p
->sdl_nlen
);
273 static __inline__
int
274 link_address_length(struct sockaddr_dl
* dl_p
)
276 return (dl_p
->sdl_alen
);
279 static __inline__
void
280 link_print(struct sockaddr_dl
* dl_p
)
284 for (i
= 0; i
< dl_p
->sdl_alen
; i
++)
285 printf("%s%x", i
? ":" : "",
286 (link_address(dl_p
))[i
]);
291 static struct sockaddr_dl
*
292 link_from_ifnet(struct ifnet
* ifp
)
294 return ((struct sockaddr_dl
*)(void *)ifp
->if_lladdr
->ifa_addr
);
298 * Function: send_packet
300 * Send the request directly on the interface, bypassing the routing code.
303 send_packet(struct ifnet
* ifp
, struct dhcp_packet
* pkt
, int pkt_size
)
306 struct sockaddr_in dest
;
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
);
316 * Function: receive_packet
318 * Return a received packet or an error if none available.
321 receive_packet(struct socket
* so
, void * pp
, int psize
, int * actual_size
)
326 char uio_buf
[ UIO_SIZEOF(1) ];
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
;
333 error
= soreceive(so
, (struct sockaddr
**) 0, auio
, 0, 0, &rcvflg
);
334 *actual_size
= psize
- uio_resid(auio
);
339 * Function: dhcp_timeout
341 * Wakeup the process waiting for something on a socket.
344 dhcp_timeout(void * arg
)
346 struct socket
* * timer_arg
= (struct socket
* *)arg
;
347 struct socket
* so
= *timer_arg
;
349 dprintf(("dhcp: timeout\n"));
353 sowakeup(so
, &so
->so_rcv
);
354 socket_unlock(so
, 1);
359 * Function: rate_packet
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.
366 #define GOOD_RATING 3
367 static __inline__
int
368 rate_packet(dhcpol_t
* options_p
)
373 if (dhcpol_find(options_p
, dhcptag_subnet_mask_e
, &len
, NULL
) != NULL
) {
376 if (dhcpol_find(options_p
, dhcptag_router_e
, &len
, NULL
) != NULL
) {
382 static dhcp_msgtype_t
383 get_dhcp_msgtype(dhcpol_t
* options_p
)
388 opt
= dhcpol_find(options_p
, dhcptag_dhcp_message_type_e
, &len
, NULL
);
389 if (opt
!= NULL
&& len
== 1) {
392 return (dhcp_msgtype_none_e
);
396 dhcp_get_ack(struct dhcp_context
* context
, int wait_ticks
)
399 const struct in_addr
* ip
;
403 struct in_addr server_id
;
404 struct socket
* timer_arg
;
406 timer_arg
= context
->so
;
407 reply
= dhcp_context_reply(context
);
408 timeout((timeout_fcn_t
)dhcp_timeout
, &timer_arg
, wait_ticks
);
410 error
= receive_packet(context
->so
, context
->reply
,
411 sizeof(context
->reply
), &n
);
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
)));
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) {
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
)) {
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
);
442 untimeout((timeout_fcn_t
)dhcp_timeout
, &timer_arg
);
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 */
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
;
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
;
466 dhcpol_free(&options
);
467 untimeout((timeout_fcn_t
)dhcp_timeout
, &timer_arg
);
471 dhcpol_free(&options
);
473 else if ((error
!= EWOULDBLOCK
)) {
474 /* if some other error occurred, we're done */
475 untimeout((timeout_fcn_t
)dhcp_timeout
, &timer_arg
);
478 else if (timer_arg
== NULL
) {
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);
493 dhcp_select(struct dhcp_context
* context
)
495 struct timeval current_time
;
497 dhcpoa_t
* options_p
;
498 struct dhcp_packet
* request
;
503 /* format a DHCP Request packet */
504 request
= dhcp_context_request(context
);
505 options_p
= &context
->request_options
;
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
),
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
);
524 init_dhcp_packet_header(request
, request_size
);
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
537 (current_time
.tv_sec
- context
->start_time
.tv_sec
));
538 request
->dhcp
.dp_xid
= htonl(context
->xid
);
540 request
->ip
.ip_id
= ip_randomid();
542 request
->ip
.ip_id
= htons(ip_id
++);
544 error
= send_packet(context
->ifp
, request
, request_size
);
546 printf("dhcp: send_packet failed with %d\n", error
);
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
);
558 printf("dhcp: server " IP_FORMAT
" send us a NAK\n",
559 IP_LIST(&context
->server_id
));
564 dprintf(("dhcp: failed to receive packets: %d\n", error
));
568 if (wait_ticks
> (MAX_WAIT_SECS
* hz
))
569 wait_ticks
= MAX_WAIT_SECS
* hz
;
570 microtime(¤t_time
);
583 dhcp_get_offer(struct dhcp_context
* context
, int wait_ticks
)
586 int gather_count
= 0;
587 const struct in_addr
* ip
;
593 struct in_addr server_id
;
594 struct socket
* timer_arg
;
596 timer_arg
= context
->so
;
597 reply
= dhcp_context_reply(context
);
598 timeout((timeout_fcn_t
)dhcp_timeout
, &timer_arg
, wait_ticks
);
600 error
= receive_packet(context
->so
, context
->reply
,
601 sizeof(context
->reply
), &n
);
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
)));
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) {
620 (void)dhcpol_parse_packet(&options
, reply
, n
);
621 if (get_dhcp_msgtype(&options
) != dhcp_msgtype_offer_e
) {
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 */
632 printf("dhcp: received OFFER: server " IP_FORMAT
633 " IP address " IP_FORMAT
"\n",
634 IP_LIST(ip
), IP_LIST(&reply
->dp_yiaddr
));
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
;
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
;
650 context
->server_id
= server_id
;
652 if (rating
>= GOOD_RATING
) {
653 dhcpol_free(&options
);
654 /* packet is good enough */
655 untimeout((timeout_fcn_t
)dhcp_timeout
, &timer_arg
);
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
);
666 dhcpol_free(&options
);
668 else if ((error
!= EWOULDBLOCK
)) {
669 untimeout((timeout_fcn_t
)dhcp_timeout
, &timer_arg
);
672 else if (timer_arg
== NULL
) { /* timed out */
673 if (gather_count
!= 0) {
674 dprintf(("dhcp: gathering time has expired\n"));
680 socket_lock(context
->so
, 1);
681 error
= sbwait(&context
->so
->so_rcv
);
682 socket_unlock(context
->so
, 1);
689 * Function: dhcp_init
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.
695 dhcp_init(struct dhcp_context
* context
)
697 struct timeval current_time
;
699 uint32_t lease_option
= htonl(SUGGESTED_LEASE_LENGTH
);
700 dhcpoa_t
* options_p
;
701 struct dhcp_packet
* request
;
706 /* remember the time we started */
707 microtime(&context
->start_time
);
708 current_time
= context
->start_time
;
710 request
= dhcp_context_request(context
);
711 options_p
= &context
->request_options
;
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
),
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
);
730 init_dhcp_packet_header(request
, request_size
);
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
);
741 request
->ip
.ip_id
= ip_randomid();
743 request
->ip
.ip_id
= htons(ip_id
++);
745 error
= send_packet(context
->ifp
, request
, request_size
);
747 printf("dhcp: send_packet failed with %d\n", error
);
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
);
755 error
= dhcp_select(context
);
760 if (error
!= EPROTO
&& error
!= ETIMEDOUT
) {
762 dprintf(("dhcp: dhcp_select failed %d\n", error
));
765 /* wait 10 seconds, and try again */
766 printf("dhcp: trying again in 10 seconds\n");
767 tsleep(&error
, PRIBIO
, "dhcp_init", 10 * hz
);
771 else if (error
!= EWOULDBLOCK
) {
772 dprintf(("dhcp: failed to receive packets: %d\n", error
));
776 if (wait_ticks
> (MAX_WAIT_SECS
* hz
))
777 wait_ticks
= MAX_WAIT_SECS
* hz
;
778 microtime(¤t_time
);
791 dhcp_context_free(struct dhcp_context
* context
, struct proc
* procp
)
793 if (context
== NULL
) {
796 if (context
->so
!= NULL
) {
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
);
804 printf("dhcp: SIOCAUTOADDR failed: %d\n", error
);
806 soclose(context
->so
);
808 kfree(context
, sizeof(*context
));
812 static struct dhcp_context
*
813 dhcp_context_create(struct ifnet
* ifp
, int max_try
,
814 struct proc
* procp
, int * error_p
)
816 struct dhcp_context
* context
= NULL
;
817 struct sockaddr_dl
* dl_p
;
818 struct in_addr lo_addr
;
819 struct in_addr lo_mask
;
821 struct sockaddr_in sin
;
823 /* get the hardware address from the interface */
824 dl_p
= link_from_ifnet(ifp
);
826 printf("dhcp: can't get link address\n");
831 printf("dhcp: h/w addr ");
833 if (dl_p
->sdl_type
!= IFT_ETHER
) {
834 printf("dhcp: hardware type %d not supported\n",
840 context
= (struct dhcp_context
*)kalloc(sizeof(*context
));
841 if (context
== NULL
) {
842 printf("dhcp: failed to allocate context\n");
846 bzero(context
, sizeof(*context
));
849 error
= socreate(AF_INET
, &context
->so
, SOCK_DGRAM
, 0);
851 printf("dhcp: socreate failed %d\n", error
);
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
);
860 printf("dhcp: assigning loopback address failed %d\n", error
);
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
,
867 context
->ifr
.ifr_intval
= 1;
869 error
= ifioctl(context
->so
, SIOCAUTOADDR
, (caddr_t
)&context
->ifr
, procp
);
871 printf("dhcp: SIOCAUTOADDR failed: %d\n", error
);
874 dprintf(("dhcp: SIOCAUTOADDR done\n"));
876 error
= ifioctl(context
->so
, SIOCPROTOATTACH
, (caddr_t
)&context
->ifr
,
879 printf("dhcp: SIOCPROTOATTACH failed: %d\n", error
);
882 dprintf(("dhcp: SIOCPROTOATTACH done\n"));
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
);
891 printf("dhcp: sobind failed, %d\n", error
);
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);
900 /* save passed-in information */
901 context
->max_try
= max_try
;
902 context
->dl_p
= dl_p
;
905 /* get a random transaction id */
906 context
->xid
= random();
911 dhcp_context_free(context
, procp
);
919 * Do DHCP over the specified interface to retrieve the IP address,
920 * subnet mask, and router.
923 dhcp(struct ifnet
* ifp
, struct in_addr
* iaddr_p
, int max_try
,
924 struct in_addr
* netmask_p
, struct in_addr
* router_p
,
928 struct dhcp_context
* context
;
930 context
= dhcp_context_create(ifp
, max_try
, procp
, &error
);
931 if (context
== NULL
) {
935 /* start DHCP in the INIT state */
936 error
= dhcp_init(context
);
938 *iaddr_p
= context
->iaddr
;
939 *netmask_p
= context
->netmask
;
940 *router_p
= context
->router
;
942 dhcp_context_free(context
, procp
);