2 * Copyright (c) 1988-2013 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
);
539 request
->ip
.ip_id
= ip_randomid();
540 error
= send_packet(context
->ifp
, request
, request_size
);
542 printf("dhcp: send_packet failed with %d\n", error
);
546 wait_ticks
+= random_range(-RAND_TICKS
, RAND_TICKS
);
547 dprintf(("dhcp: waiting %d ticks\n", wait_ticks
));
548 error
= dhcp_get_ack(context
, wait_ticks
);
554 printf("dhcp: server " IP_FORMAT
" send us a NAK\n",
555 IP_LIST(&context
->server_id
));
560 dprintf(("dhcp: failed to receive packets: %d\n", error
));
564 if (wait_ticks
> (MAX_WAIT_SECS
* hz
))
565 wait_ticks
= MAX_WAIT_SECS
* hz
;
566 microtime(¤t_time
);
579 dhcp_get_offer(struct dhcp_context
* context
, int wait_ticks
)
582 int gather_count
= 0;
583 const struct in_addr
* ip
;
589 struct in_addr server_id
;
590 struct socket
* timer_arg
;
592 timer_arg
= context
->so
;
593 reply
= dhcp_context_reply(context
);
594 timeout((timeout_fcn_t
)dhcp_timeout
, &timer_arg
, wait_ticks
);
596 error
= receive_packet(context
->so
, context
->reply
,
597 sizeof(context
->reply
), &n
);
601 dprintf(("\ndhcp: received packet length %d\n", n
));
602 if (n
< (int)sizeof(struct dhcp
)) {
603 dprintf(("dhcp: packet is too short %d < %d\n",
604 n
, (int)sizeof(struct dhcp
)));
607 if (ntohl(reply
->dp_xid
) != context
->xid
608 || reply
->dp_yiaddr
.s_addr
== 0
609 || reply
->dp_yiaddr
.s_addr
== INADDR_BROADCAST
610 || bcmp(reply
->dp_chaddr
,
611 link_address(context
->dl_p
),
612 link_address_length(context
->dl_p
)) != 0) {
616 (void)dhcpol_parse_packet(&options
, reply
, n
);
617 if (get_dhcp_msgtype(&options
) != dhcp_msgtype_offer_e
) {
621 ip
= (const struct in_addr
*)
622 dhcpol_find(&options
,
623 dhcptag_server_identifier_e
, &len
, NULL
);
624 if (ip
== NULL
|| len
< (int)sizeof(*ip
)) {
625 /* missing/invalid server identifier */
628 printf("dhcp: received OFFER: server " IP_FORMAT
629 " IP address " IP_FORMAT
"\n",
630 IP_LIST(ip
), IP_LIST(&reply
->dp_yiaddr
));
632 rating
= rate_packet(&options
);
633 if (rating
> last_rating
) {
634 context
->iaddr
= reply
->dp_yiaddr
;
635 ip
= (const struct in_addr
*)
636 dhcpol_find(&options
,
637 dhcptag_subnet_mask_e
, &len
, NULL
);
638 if (ip
!= NULL
&& len
>= (int)sizeof(*ip
)) {
639 context
->netmask
= *ip
;
641 ip
= (const struct in_addr
*)
642 dhcpol_find(&options
, dhcptag_router_e
, &len
, NULL
);
643 if (ip
!= NULL
&& len
>= (int)sizeof(*ip
)) {
644 context
->router
= *ip
;
646 context
->server_id
= server_id
;
648 if (rating
>= GOOD_RATING
) {
649 dhcpol_free(&options
);
650 /* packet is good enough */
651 untimeout((timeout_fcn_t
)dhcp_timeout
, &timer_arg
);
654 if (gather_count
== 0) {
655 untimeout((timeout_fcn_t
)dhcp_timeout
, &timer_arg
);
656 timer_arg
= context
->so
;
657 timeout((timeout_fcn_t
)dhcp_timeout
, &timer_arg
,
658 hz
* GATHER_TIME_SECS
);
662 dhcpol_free(&options
);
664 else if ((error
!= EWOULDBLOCK
)) {
665 untimeout((timeout_fcn_t
)dhcp_timeout
, &timer_arg
);
668 else if (timer_arg
== NULL
) { /* timed out */
669 if (gather_count
!= 0) {
670 dprintf(("dhcp: gathering time has expired\n"));
676 socket_lock(context
->so
, 1);
677 error
= sbwait(&context
->so
->so_rcv
);
678 socket_unlock(context
->so
, 1);
685 * Function: dhcp_init
687 * Start in the DHCP INIT state sending DISCOVER's. When we get OFFER's,
688 * try to select one of them by sending a REQUEST and waiting for an ACK.
691 dhcp_init(struct dhcp_context
* context
)
693 struct timeval current_time
;
695 uint32_t lease_option
= htonl(SUGGESTED_LEASE_LENGTH
);
696 dhcpoa_t
* options_p
;
697 struct dhcp_packet
* request
;
702 /* remember the time we started */
703 microtime(&context
->start_time
);
704 current_time
= context
->start_time
;
706 request
= dhcp_context_request(context
);
707 options_p
= &context
->request_options
;
710 /* format a DHCP DISCOVER packet */
711 make_dhcp_request(&request
->dhcp
, DHCP_PAYLOAD_MIN
,
712 dhcp_msgtype_discover_e
,
713 link_address(context
->dl_p
), ARPHRD_ETHER
,
714 link_address_length(context
->dl_p
),
716 /* add the requested lease time */
717 dhcpoa_add(options_p
, dhcptag_lease_time_e
,
718 sizeof(lease_option
), &lease_option
);
719 dhcpoa_add(options_p
, dhcptag_end_e
, 0, 0);
720 request_size
= sizeof(*request
) + RFC_MAGIC_SIZE
721 + dhcpoa_used(options_p
);
722 if (request_size
< (int)sizeof(struct bootp_packet
)) {
723 /* pad out to BOOTP-sized packet */
724 request_size
= sizeof(struct bootp_packet
);
726 init_dhcp_packet_header(request
, request_size
);
728 wait_ticks
= INITIAL_WAIT_SECS
* hz
;
729 for (retry
= 0; retry
< context
->max_try
; retry
++) {
730 /* Send the request */
731 printf("dhcp: sending DISCOVER\n");
732 request
->dhcp
.dp_secs
733 = htons((u_short
)(current_time
.tv_sec
734 - context
->start_time
.tv_sec
));
735 request
->dhcp
.dp_xid
= htonl(context
->xid
);
736 request
->ip
.ip_id
= ip_randomid();
737 error
= send_packet(context
->ifp
, request
, request_size
);
739 printf("dhcp: send_packet failed with %d\n", error
);
742 wait_ticks
+= random_range(-RAND_TICKS
, RAND_TICKS
);
743 dprintf(("dhcp: waiting %d ticks\n", wait_ticks
));
744 error
= dhcp_get_offer(context
, wait_ticks
);
747 error
= dhcp_select(context
);
752 if (error
!= EPROTO
&& error
!= ETIMEDOUT
) {
754 dprintf(("dhcp: dhcp_select failed %d\n", error
));
757 /* wait 10 seconds, and try again */
758 printf("dhcp: trying again in 10 seconds\n");
759 tsleep(&error
, PRIBIO
, "dhcp_init", 10 * hz
);
763 else if (error
!= EWOULDBLOCK
) {
764 dprintf(("dhcp: failed to receive packets: %d\n", error
));
768 if (wait_ticks
> (MAX_WAIT_SECS
* hz
))
769 wait_ticks
= MAX_WAIT_SECS
* hz
;
770 microtime(¤t_time
);
783 dhcp_context_free(struct dhcp_context
* context
, struct proc
* procp
)
785 if (context
== NULL
) {
788 if (context
->so
!= NULL
) {
791 /* disable reception of DHCP packets before address assignment */
792 context
->ifr
.ifr_intval
= 0;
793 error
= ifioctl(context
->so
, SIOCAUTOADDR
,
794 (caddr_t
)&context
->ifr
, procp
);
796 printf("dhcp: SIOCAUTOADDR failed: %d\n", error
);
798 soclose(context
->so
);
800 kfree(context
, sizeof(*context
));
804 static struct dhcp_context
*
805 dhcp_context_create(struct ifnet
* ifp
, int max_try
,
806 struct proc
* procp
, int * error_p
)
808 struct dhcp_context
* context
= NULL
;
809 struct sockaddr_dl
* dl_p
;
810 struct in_addr lo_addr
;
811 struct in_addr lo_mask
;
813 struct sockaddr_in sin
;
815 /* get the hardware address from the interface */
816 dl_p
= link_from_ifnet(ifp
);
818 printf("dhcp: can't get link address\n");
823 printf("dhcp: h/w addr ");
825 if (dl_p
->sdl_type
!= IFT_ETHER
) {
826 printf("dhcp: hardware type %d not supported\n",
832 context
= (struct dhcp_context
*)kalloc(sizeof(*context
));
833 if (context
== NULL
) {
834 printf("dhcp: failed to allocate context\n");
838 bzero(context
, sizeof(*context
));
841 error
= socreate(AF_INET
, &context
->so
, SOCK_DGRAM
, 0);
843 printf("dhcp: socreate failed %d\n", error
);
847 /* assign 127.0.0.1 to lo0 so that the bind will succeed */
848 lo_addr
.s_addr
= htonl(INADDR_LOOPBACK
);
849 lo_mask
.s_addr
= htonl(IN_CLASSA_NET
);
850 error
= inet_aifaddr(context
->so
, "lo0", &lo_addr
, &lo_mask
, NULL
);
852 printf("dhcp: assigning loopback address failed %d\n", error
);
855 /* enable reception of DHCP packets before an address is assigned */
856 snprintf(context
->ifr
.ifr_name
,
857 sizeof(context
->ifr
.ifr_name
), "%s", if_name(ifp
));
858 context
->ifr
.ifr_intval
= 1;
860 error
= ifioctl(context
->so
, SIOCAUTOADDR
, (caddr_t
)&context
->ifr
, procp
);
862 printf("dhcp: SIOCAUTOADDR failed: %d\n", error
);
865 dprintf(("dhcp: SIOCAUTOADDR done\n"));
867 error
= ifioctl(context
->so
, SIOCPROTOATTACH
, (caddr_t
)&context
->ifr
,
870 printf("dhcp: SIOCPROTOATTACH failed: %d\n", error
);
873 dprintf(("dhcp: SIOCPROTOATTACH done\n"));
875 /* bind the socket */
876 sin
.sin_len
= sizeof(sin
);
877 sin
.sin_family
= AF_INET
;
878 sin
.sin_port
= htons(IPPORT_BOOTPC
);
879 sin
.sin_addr
.s_addr
= INADDR_ANY
;
880 error
= sobindlock(context
->so
, (struct sockaddr
*)&sin
, 1);
882 printf("dhcp: sobind failed, %d\n", error
);
886 /* make it non-blocking I/O */
887 socket_lock(context
->so
, 1);
888 context
->so
->so_state
|= SS_NBIO
;
889 socket_unlock(context
->so
, 1);
891 /* save passed-in information */
892 context
->max_try
= max_try
;
893 context
->dl_p
= dl_p
;
896 /* get a random transaction id */
897 context
->xid
= random();
902 dhcp_context_free(context
, procp
);
910 * Do DHCP over the specified interface to retrieve the IP address,
911 * subnet mask, and router.
914 dhcp(struct ifnet
* ifp
, struct in_addr
* iaddr_p
, int max_try
,
915 struct in_addr
* netmask_p
, struct in_addr
* router_p
,
919 struct dhcp_context
* context
;
921 context
= dhcp_context_create(ifp
, max_try
, procp
, &error
);
922 if (context
== NULL
) {
926 /* start DHCP in the INIT state */
927 error
= dhcp_init(context
);
929 *iaddr_p
= context
->iaddr
;
930 *netmask_p
= context
->netmask
;
931 *router_p
= context
->router
;
933 dhcp_context_free(context
, procp
);