2 * Copyright (c) 1988-2007 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
*)&ifra
.ifra_addr
) = blank_sin
;
109 ((struct sockaddr_in
*)&ifra
.ifra_addr
)->sin_addr
= *addr
;
112 *((struct sockaddr_in
*)&ifra
.ifra_mask
) = blank_sin
;
113 ((struct sockaddr_in
*)&ifra
.ifra_mask
)->sin_addr
= *mask
;
116 *((struct sockaddr_in
*)&ifra
.ifra_broadaddr
) = blank_sin
;
117 ((struct sockaddr_in
*)&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
*)context
->request
);
146 static __inline__
struct dhcp
*
147 dhcp_context_reply(struct dhcp_context
* context
)
149 return ((struct dhcp
*)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
)
285 printf("len %d index %d family %d type 0x%x nlen %d alen %d"
286 " slen %d addr ", dl_p
->sdl_len
,
287 dl_p
->sdl_index
, dl_p
->sdl_family
, dl_p
->sdl_type
,
288 dl_p
->sdl_nlen
, dl_p
->sdl_alen
, dl_p
->sdl_slen
);
290 for (i
= 0; i
< dl_p
->sdl_alen
; i
++)
291 printf("%s%x", i
? ":" : "",
292 (link_address(dl_p
))[i
]);
297 static struct sockaddr_dl
*
298 link_from_ifnet(struct ifnet
* ifp
)
300 struct ifaddr
* addr
;
302 ifnet_lock_shared(ifp
);
303 TAILQ_FOREACH(addr
, &ifp
->if_addrhead
, ifa_link
) {
304 if (addr
->ifa_addr
->sa_family
== AF_LINK
) {
305 struct sockaddr_dl
* dl_p
= (struct sockaddr_dl
*)(addr
->ifa_addr
);
307 ifnet_lock_done(ifp
);
311 ifnet_lock_done(ifp
);
316 * Function: send_packet
318 * Send the request directly on the interface, bypassing the routing code.
321 send_packet(struct ifnet
* ifp
, struct dhcp_packet
* pkt
, int pkt_size
)
324 struct sockaddr_in dest
;
327 dest
.sin_port
= htons(IPPORT_BOOTPS
);
328 dest
.sin_addr
.s_addr
= INADDR_BROADCAST
;
329 m
= ip_pkt_to_mbuf((caddr_t
)pkt
, pkt_size
);
330 return dlil_output(ifp
, PF_INET
, m
, 0, (struct sockaddr
*)&dest
, 0);
334 * Function: receive_packet
336 * Return a received packet or an error if none available.
339 receive_packet(struct socket
* so
, void * pp
, int psize
, int * actual_size
)
344 char uio_buf
[ UIO_SIZEOF(1) ];
346 auio
= uio_createwithbuffer(1, 0, UIO_SYSSPACE
, UIO_READ
,
347 &uio_buf
[0], sizeof(uio_buf
));
348 uio_addiov(auio
, CAST_USER_ADDR_T(pp
), psize
);
349 rcvflg
= MSG_WAITALL
;
351 error
= soreceive(so
, (struct sockaddr
**) 0, auio
, 0, 0, &rcvflg
);
352 *actual_size
= psize
- uio_resid(auio
);
357 * Function: dhcp_timeout
359 * Wakeup the process waiting for something on a socket.
362 dhcp_timeout(void * arg
)
364 struct socket
* * timer_arg
= (struct socket
* *)arg
;
365 struct socket
* so
= *timer_arg
;
367 dprintf(("dhcp: timeout\n"));
371 sowakeup(so
, &so
->so_rcv
);
372 socket_unlock(so
, 1);
377 * Function: rate_packet
379 * Return an integer point rating value for the given dhcp packet.
380 * If yiaddr non-zero, the packet gets a rating of 1.
381 * Another point is given if the packet contains the subnet mask,
382 * and another if the router is present.
384 #define GOOD_RATING 3
385 static __inline__
int
386 rate_packet(dhcpol_t
* options_p
)
391 if (dhcpol_find(options_p
, dhcptag_subnet_mask_e
, &len
, NULL
) != NULL
) {
394 if (dhcpol_find(options_p
, dhcptag_router_e
, &len
, NULL
) != NULL
) {
400 static dhcp_msgtype_t
401 get_dhcp_msgtype(dhcpol_t
* options_p
)
406 opt
= dhcpol_find(options_p
, dhcptag_dhcp_message_type_e
, &len
, NULL
);
407 if (opt
!= NULL
&& len
== 1) {
410 return (dhcp_msgtype_none_e
);
414 dhcp_get_ack(struct dhcp_context
* context
, int wait_ticks
)
417 const struct in_addr
* ip
;
421 struct in_addr server_id
;
422 struct socket
* timer_arg
;
424 timer_arg
= context
->so
;
425 reply
= dhcp_context_reply(context
);
426 timeout((timeout_fcn_t
)dhcp_timeout
, &timer_arg
, wait_ticks
);
428 error
= receive_packet(context
->so
, context
->reply
,
429 sizeof(context
->reply
), &n
);
434 dprintf(("\ndhcp: received packet length %d\n", n
));
435 if (n
< (int)sizeof(struct dhcp
)) {
436 dprintf(("dhcp: packet is too short %d < %d\n",
437 n
, (int)sizeof(struct dhcp
)));
440 if (ntohl(reply
->dp_xid
) != context
->xid
441 || bcmp(reply
->dp_chaddr
, link_address(context
->dl_p
),
442 link_address_length(context
->dl_p
)) != 0) {
446 (void)dhcpol_parse_packet(&options
, reply
, n
);
447 server_id
.s_addr
= 0;
448 ip
= (const struct in_addr
*)
449 dhcpol_find(&options
,
450 dhcptag_server_identifier_e
, &len
, NULL
);
451 if (ip
!= NULL
&& len
>= (int)sizeof(*ip
)) {
454 msg
= get_dhcp_msgtype(&options
);
455 if (msg
== dhcp_msgtype_nak_e
456 && server_id
.s_addr
== context
->server_id
.s_addr
) {
457 /* server NAK'd us, start over */
458 dhcpol_free(&options
);
460 untimeout((timeout_fcn_t
)dhcp_timeout
, &timer_arg
);
463 if (msg
!= dhcp_msgtype_ack_e
464 || reply
->dp_yiaddr
.s_addr
== 0
465 || reply
->dp_yiaddr
.s_addr
== INADDR_BROADCAST
) {
466 /* ignore the packet */
469 printf("dhcp: received ACK: server " IP_FORMAT
470 " IP address " IP_FORMAT
"\n",
471 IP_LIST(&server_id
), IP_LIST(&reply
->dp_yiaddr
));
472 context
->iaddr
= reply
->dp_yiaddr
;
473 ip
= (const struct in_addr
*)
474 dhcpol_find(&options
,
475 dhcptag_subnet_mask_e
, &len
, NULL
);
476 if (ip
!= NULL
&& len
>= (int)sizeof(*ip
)) {
477 context
->netmask
= *ip
;
479 ip
= (const struct in_addr
*)
480 dhcpol_find(&options
, dhcptag_router_e
, &len
, NULL
);
481 if (ip
!= NULL
&& len
>= (int)sizeof(*ip
)) {
482 context
->router
= *ip
;
484 dhcpol_free(&options
);
485 untimeout((timeout_fcn_t
)dhcp_timeout
, &timer_arg
);
489 dhcpol_free(&options
);
491 else if ((error
!= EWOULDBLOCK
)) {
492 /* if some other error occurred, we're done */
493 untimeout((timeout_fcn_t
)dhcp_timeout
, &timer_arg
);
496 else if (timer_arg
== NULL
) {
501 /* wait for a wait to arrive, or a timeout to occur */
502 socket_lock(context
->so
, 1);
503 error
= sbwait(&context
->so
->so_rcv
);
504 socket_unlock(context
->so
, 1);
511 dhcp_select(struct dhcp_context
* context
)
513 struct timeval current_time
;
515 dhcpoa_t
* options_p
;
516 struct dhcp_packet
* request
;
521 /* format a DHCP Request packet */
522 request
= dhcp_context_request(context
);
523 options_p
= &context
->request_options
;
525 make_dhcp_request(&request
->dhcp
, DHCP_PAYLOAD_MIN
,
526 dhcp_msgtype_request_e
,
527 link_address(context
->dl_p
), ARPHRD_ETHER
,
528 link_address_length(context
->dl_p
),
530 /* insert server identifier and requested ip address */
531 dhcpoa_add(options_p
, dhcptag_requested_ip_address_e
,
532 sizeof(context
->iaddr
), &context
->iaddr
);
533 dhcpoa_add(options_p
, dhcptag_server_identifier_e
,
534 sizeof(context
->server_id
), &context
->server_id
);
535 dhcpoa_add(options_p
, dhcptag_end_e
, 0, 0);
536 request_size
= sizeof(*request
) + RFC_MAGIC_SIZE
537 + dhcpoa_used(options_p
);
538 if (request_size
< (int)sizeof(struct bootp_packet
)) {
539 /* pad out to BOOTP-sized packet */
540 request_size
= sizeof(struct bootp_packet
);
542 init_dhcp_packet_header(request
, request_size
);
544 wait_ticks
= INITIAL_WAIT_SECS
* hz
;
545 #define SELECT_RETRY_COUNT 3
546 for (retry
= 0; retry
< SELECT_RETRY_COUNT
; retry
++) {
547 /* Send the request */
548 printf("dhcp: sending REQUEST: server " IP_FORMAT
549 " IP address " IP_FORMAT
"\n",
550 IP_LIST(&context
->server_id
),
551 IP_LIST(&context
->iaddr
));
552 microtime(¤t_time
);
553 request
->dhcp
.dp_secs
555 (current_time
.tv_sec
- context
->start_time
.tv_sec
));
556 request
->dhcp
.dp_xid
= htonl(context
->xid
);
558 request
->ip
.ip_id
= ip_randomid();
560 request
->ip
.ip_id
= htons(ip_id
++);
562 error
= send_packet(context
->ifp
, request
, request_size
);
564 printf("dhcp: send_packet failed with %d\n", error
);
568 wait_ticks
+= random_range(-RAND_TICKS
, RAND_TICKS
);
569 dprintf(("dhcp: waiting %d ticks\n", wait_ticks
));
570 error
= dhcp_get_ack(context
, wait_ticks
);
576 printf("dhcp: server " IP_FORMAT
" send us a NAK\n",
577 IP_LIST(&context
->server_id
));
582 dprintf(("dhcp: failed to receive packets: %d\n", error
));
586 if (wait_ticks
> (MAX_WAIT_SECS
* hz
))
587 wait_ticks
= MAX_WAIT_SECS
* hz
;
588 microtime(¤t_time
);
601 dhcp_get_offer(struct dhcp_context
* context
, int wait_ticks
)
604 int gather_count
= 0;
605 const struct in_addr
* ip
;
611 struct in_addr server_id
;
612 struct socket
* timer_arg
;
614 timer_arg
= context
->so
;
615 reply
= dhcp_context_reply(context
);
616 timeout((timeout_fcn_t
)dhcp_timeout
, &timer_arg
, wait_ticks
);
618 error
= receive_packet(context
->so
, context
->reply
,
619 sizeof(context
->reply
), &n
);
623 dprintf(("\ndhcp: received packet length %d\n", n
));
624 if (n
< (int)sizeof(struct dhcp
)) {
625 dprintf(("dhcp: packet is too short %d < %d\n",
626 n
, (int)sizeof(struct dhcp
)));
629 if (ntohl(reply
->dp_xid
) != context
->xid
630 || reply
->dp_yiaddr
.s_addr
== 0
631 || reply
->dp_yiaddr
.s_addr
== INADDR_BROADCAST
632 || bcmp(reply
->dp_chaddr
,
633 link_address(context
->dl_p
),
634 link_address_length(context
->dl_p
)) != 0) {
638 (void)dhcpol_parse_packet(&options
, reply
, n
);
639 if (get_dhcp_msgtype(&options
) != dhcp_msgtype_offer_e
) {
643 ip
= (const struct in_addr
*)
644 dhcpol_find(&options
,
645 dhcptag_server_identifier_e
, &len
, NULL
);
646 if (ip
== NULL
|| len
< (int)sizeof(*ip
)) {
647 /* missing/invalid server identifier */
650 printf("dhcp: received OFFER: server " IP_FORMAT
651 " IP address " IP_FORMAT
"\n",
652 IP_LIST(ip
), IP_LIST(&reply
->dp_yiaddr
));
654 rating
= rate_packet(&options
);
655 if (rating
> last_rating
) {
656 context
->iaddr
= reply
->dp_yiaddr
;
657 ip
= (const struct in_addr
*)
658 dhcpol_find(&options
,
659 dhcptag_subnet_mask_e
, &len
, NULL
);
660 if (ip
!= NULL
&& len
>= (int)sizeof(*ip
)) {
661 context
->netmask
= *ip
;
663 ip
= (const struct in_addr
*)
664 dhcpol_find(&options
, dhcptag_router_e
, &len
, NULL
);
665 if (ip
!= NULL
&& len
>= (int)sizeof(*ip
)) {
666 context
->router
= *ip
;
668 context
->server_id
= server_id
;
670 if (rating
>= GOOD_RATING
) {
671 dhcpol_free(&options
);
672 /* packet is good enough */
673 untimeout((timeout_fcn_t
)dhcp_timeout
, &timer_arg
);
676 if (gather_count
== 0) {
677 untimeout((timeout_fcn_t
)dhcp_timeout
, &timer_arg
);
678 timer_arg
= context
->so
;
679 timeout((timeout_fcn_t
)dhcp_timeout
, &timer_arg
,
680 hz
* GATHER_TIME_SECS
);
684 dhcpol_free(&options
);
686 else if ((error
!= EWOULDBLOCK
)) {
687 untimeout((timeout_fcn_t
)dhcp_timeout
, &timer_arg
);
690 else if (timer_arg
== NULL
) { /* timed out */
691 if (gather_count
!= 0) {
692 dprintf(("dhcp: gathering time has expired\n"));
698 socket_lock(context
->so
, 1);
699 error
= sbwait(&context
->so
->so_rcv
);
700 socket_unlock(context
->so
, 1);
707 * Function: dhcp_init
709 * Start in the DHCP INIT state sending DISCOVER's. When we get OFFER's,
710 * try to select one of them by sending a REQUEST and waiting for an ACK.
713 dhcp_init(struct dhcp_context
* context
)
715 struct timeval current_time
;
717 uint32_t lease_option
= htonl(SUGGESTED_LEASE_LENGTH
);
718 dhcpoa_t
* options_p
;
719 struct dhcp_packet
* request
;
724 /* remember the time we started */
725 microtime(&context
->start_time
);
726 current_time
= context
->start_time
;
728 request
= dhcp_context_request(context
);
729 options_p
= &context
->request_options
;
732 /* format a DHCP DISCOVER packet */
733 make_dhcp_request(&request
->dhcp
, DHCP_PAYLOAD_MIN
,
734 dhcp_msgtype_discover_e
,
735 link_address(context
->dl_p
), ARPHRD_ETHER
,
736 link_address_length(context
->dl_p
),
738 /* add the requested lease time */
739 dhcpoa_add(options_p
, dhcptag_lease_time_e
,
740 sizeof(lease_option
), &lease_option
);
741 dhcpoa_add(options_p
, dhcptag_end_e
, 0, 0);
742 request_size
= sizeof(*request
) + RFC_MAGIC_SIZE
743 + dhcpoa_used(options_p
);
744 if (request_size
< (int)sizeof(struct bootp_packet
)) {
745 /* pad out to BOOTP-sized packet */
746 request_size
= sizeof(struct bootp_packet
);
748 init_dhcp_packet_header(request
, request_size
);
750 wait_ticks
= INITIAL_WAIT_SECS
* hz
;
751 for (retry
= 0; retry
< context
->max_try
; retry
++) {
752 /* Send the request */
753 printf("dhcp: sending DISCOVER\n");
754 request
->dhcp
.dp_secs
755 = htons((u_short
)(current_time
.tv_sec
756 - context
->start_time
.tv_sec
));
757 request
->dhcp
.dp_xid
= htonl(context
->xid
);
759 request
->ip
.ip_id
= ip_randomid();
761 request
->ip
.ip_id
= htons(ip_id
++);
763 error
= send_packet(context
->ifp
, request
, request_size
);
765 printf("dhcp: send_packet failed with %d\n", error
);
768 wait_ticks
+= random_range(-RAND_TICKS
, RAND_TICKS
);
769 dprintf(("dhcp: waiting %d ticks\n", wait_ticks
));
770 error
= dhcp_get_offer(context
, wait_ticks
);
773 error
= dhcp_select(context
);
778 if (error
!= EPROTO
&& error
!= ETIMEDOUT
) {
780 dprintf(("dhcp: dhcp_select failed %d\n", error
));
783 /* wait 10 seconds, and try again */
784 printf("dhcp: trying again in 10 seconds\n");
785 tsleep(&error
, PRIBIO
, "dhcp_init", 10 * hz
);
789 else if (error
!= EWOULDBLOCK
) {
790 dprintf(("dhcp: failed to receive packets: %d\n", error
));
794 if (wait_ticks
> (MAX_WAIT_SECS
* hz
))
795 wait_ticks
= MAX_WAIT_SECS
* hz
;
796 microtime(¤t_time
);
809 dhcp_context_free(struct dhcp_context
* context
, struct proc
* procp
)
811 if (context
== NULL
) {
814 if (context
->so
!= NULL
) {
817 /* disable reception of DHCP packets before address assignment */
818 context
->ifr
.ifr_intval
= 0;
819 error
= ifioctl(context
->so
, SIOCAUTOADDR
,
820 (caddr_t
)&context
->ifr
, procp
);
822 printf("dhcp: SIOCAUTOADDR failed: %d\n", error
);
824 soclose(context
->so
);
826 kfree(context
, sizeof(*context
));
830 static struct dhcp_context
*
831 dhcp_context_create(struct ifnet
* ifp
, int max_try
,
832 struct proc
* procp
, int * error_p
)
834 struct dhcp_context
* context
= NULL
;
835 struct sockaddr_dl
* dl_p
;
836 struct in_addr lo_addr
;
837 struct in_addr lo_mask
;
839 struct sockaddr_in sin
;
841 /* get the hardware address from the interface */
842 dl_p
= link_from_ifnet(ifp
);
844 printf("dhcp: can't get link address\n");
849 printf("dhcp: h/w addr ");
851 if (dl_p
->sdl_type
!= IFT_ETHER
) {
852 printf("dhcp: hardware type %d not supported\n",
858 context
= (struct dhcp_context
*)kalloc(sizeof(*context
));
859 if (context
== NULL
) {
860 printf("dhcp: failed to allocate context\n");
864 bzero(context
, sizeof(*context
));
867 error
= socreate(AF_INET
, &context
->so
, SOCK_DGRAM
, 0);
869 printf("dhcp: socreate failed %d\n", error
);
873 /* assign 127.0.0.1 to lo0 so that the bind will succeed */
874 lo_addr
.s_addr
= htonl(INADDR_LOOPBACK
);
875 lo_mask
.s_addr
= htonl(IN_CLASSA_NET
);
876 error
= inet_aifaddr(context
->so
, "lo0", &lo_addr
, &lo_mask
, NULL
);
878 printf("dhcp: assigning loopback address failed %d\n", error
);
881 /* enable reception of DHCP packets before an address is assigned */
882 snprintf(context
->ifr
.ifr_name
,
883 sizeof(context
->ifr
.ifr_name
), "%s%d", ifp
->if_name
,
885 context
->ifr
.ifr_intval
= 1;
887 error
= ifioctl(context
->so
, SIOCAUTOADDR
, (caddr_t
)&context
->ifr
, procp
);
889 printf("dhcp: SIOCAUTOADDR failed: %d\n", error
);
892 dprintf(("dhcp: SIOCAUTOADDR done\n"));
894 error
= ifioctl(context
->so
, SIOCPROTOATTACH
, (caddr_t
)&context
->ifr
,
897 printf("dhcp: SIOCPROTOATTACH failed: %d\n", error
);
900 dprintf(("dhcp: SIOCPROTOATTACH done\n"));
902 /* bind the socket */
903 sin
.sin_len
= sizeof(sin
);
904 sin
.sin_family
= AF_INET
;
905 sin
.sin_port
= htons(IPPORT_BOOTPC
);
906 sin
.sin_addr
.s_addr
= INADDR_ANY
;
907 error
= sobind(context
->so
, (struct sockaddr
*)&sin
);
909 printf("dhcp: sobind failed, %d\n", error
);
913 /* make it non-blocking I/O */
914 socket_lock(context
->so
, 1);
915 context
->so
->so_state
|= SS_NBIO
;
916 socket_unlock(context
->so
, 1);
918 /* save passed-in information */
919 context
->max_try
= max_try
;
920 context
->dl_p
= dl_p
;
923 /* get a random transaction id */
924 context
->xid
= random();
929 dhcp_context_free(context
, procp
);
937 * Do DHCP over the specified interface to retrieve the IP address,
938 * subnet mask, and router.
941 dhcp(struct ifnet
* ifp
, struct in_addr
* iaddr_p
, int max_try
,
942 struct in_addr
* netmask_p
, struct in_addr
* router_p
,
946 struct dhcp_context
* context
;
948 context
= dhcp_context_create(ifp
, max_try
, procp
, &error
);
949 if (context
== NULL
) {
953 /* start DHCP in the INIT state */
954 error
= dhcp_init(context
);
956 *iaddr_p
= context
->iaddr
;
957 *netmask_p
= context
->netmask
;
958 *router_p
= context
->router
;
960 dhcp_context_free(context
, procp
);