4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
25 * Standalone dhcp client.
28 #pragma ident "%Z%%M% %I% %E% SMI"
30 #include <sys/types.h>
31 #include <sys/salib.h>
32 #include <sys/bootconf.h>
33 #include <sys/bootcmn.h>
34 #include <sys/socket.h>
35 #include <sys/isa_defs.h>
36 #include <netinet/in.h>
37 #include <netinet/in_systm.h>
38 #include <netinet/inetutil.h>
39 #include <netinet/dhcp.h>
40 #include <netinet/ip.h>
41 #include <netinet/udp.h>
42 #include <dhcp_impl.h>
43 #include <net/if_types.h>
44 #include <sys/promif.h>
45 #include <sys/platnames.h>
46 #include <socket_inet.h>
50 #include <sys/bootdebug.h>
53 static char *s_n
= "INIT";
54 static enum DHCPSTATE dhcp_state
= INIT
;
56 static PKT
*dhcp_snd_bufp
, *dhcp_rcv_bufp
;
57 static int dhcp_buf_size
;
59 static const uint8_t magic
[] = BOOTMAGIC
; /* RFC1048 */
60 static uint8_t opt_discover
[] = { CD_DHCP_TYPE
, 1, DISCOVER
};
61 static uint8_t opt_request
[] = { CD_DHCP_TYPE
, 1, REQUEST
};
62 static uint8_t opt_decline
[] = { CD_DHCP_TYPE
, 1, DECLINE
};
64 static uint8_t dhcp_classid
[DHCP_MAX_OPT_SIZE
+ 3];
65 static uint8_t dhcp_clientid
[DHCP_MAX_CID_LEN
];
66 static uint8_t dhcp_clientid_len
= 0;
68 static uint32_t dhcp_start_time
; /* start time (msecs */
69 static time_t dhcp_secs
;
70 static uint32_t timeout
; /* timeout in milliseconds */
72 static int pkt_counter
;
73 PKT_LIST
*list_tl
, *list_hd
;
74 PKT_LIST
*state_pl
= NULL
;
76 #define PROM_BOOT_CACHED "bootp-response"
77 extern char *bootp_response
; /* bootprop.c */
81 * Do whatever reset actions/initialization actions are generic for every
82 * DHCP/bootp message. Set the message type.
84 * Returns: the updated options ptr.
87 init_msg(PKT
*pkt
, uint8_t *pkttype
)
91 bzero(pkt
, dhcp_buf_size
);
92 bcopy(magic
, pkt
->cookie
, sizeof (pkt
->cookie
));
93 pkt
->op
= BOOTREQUEST
;
95 bcopy(mac_get_addr_buf()+2, &xid
, 4);
99 bcopy(pkttype
, pkt
->options
, 3);
100 return ((uint8_t *)(pkt
->options
+ 3));
104 * Parameter request list.
107 parameter_request_list(uint8_t **opt
)
110 * This parameter request list is used in the normal 4-packet
111 * DHCPDISCOVER/OFFER/REQUEST/ACK exchange; it must not contain
112 * CD_REQUESTED_IP_ADDR or CD_LEASE_TIME.
114 static uint8_t prlist
[] = {
115 CD_REQUEST_LIST
, /* parameter request list option number */
116 4, /* number of options requested */
123 bcopy(prlist
, *opt
, sizeof (prlist
));
124 *opt
+= sizeof (prlist
);
129 * Set hardware specific fields
132 set_hw_spec_data(PKT
*p
, uint8_t **opt
, uint8_t *pkttype
)
134 char mfg
[DHCP_MAX_OPT_SIZE
+ 1], cbuf
[DHCP_MAX_OPT_SIZE
+ 1];
136 int adjust_len
, len
, i
;
138 p
->htype
= mac_arp_type(mac_get_type());
139 len
= (uchar_t
)mac_get_addr_len();
140 if (len
<= sizeof (p
->chaddr
)) {
142 bcopy(mac_get_addr_buf(), p
->chaddr
, len
);
144 uint8_t type
= *(pkttype
+ 2);
146 * The mac address does not fit in the chaddr
147 * field, thus it can not be sent to the server,
148 * thus server can not unicast the reply. Per
149 * RFC 2131 4.4.1, client can set this bit in
152 if ((type
== DISCOVER
) || (type
== REQUEST
))
153 p
->flags
= htons(BCAST_MASK
);
157 if (dhcp_classid
[0] == '\0') {
159 * Classids based on mfg name: Commas (,) are
160 * converted to periods (.), and spaces ( ) are removed.
162 dhcp_classid
[0] = CD_CLASS_ID
;
164 (void) strncpy(mfg
, get_mfg_name(), sizeof (mfg
));
165 if (strncmp(mfg
, "SUNW", strlen("SUNW")) != 0) {
166 len
= strlen("SUNW.");
167 (void) strcpy(cbuf
, "SUNW.");
174 if ((len
+ 2) < DHCP_MAX_OPT_SIZE
) {
175 tp
= (uint8_t *)strcat(cbuf
, mfg
);
176 dp
= &dhcp_classid
[2];
178 for (i
= 0; i
< len
; i
++, tp
++) {
181 } else if (*tp
== ' ') {
188 dhcp_classid
[1] = (uint8_t)len
;
190 prom_panic("Not enough space for class id");
192 printf("%s: Classid: %s\n", s_n
, &dhcp_classid
[2]);
193 #endif /* DHCP_DEBUG */
195 bcopy(dhcp_classid
, *opt
, dhcp_classid
[1] + 2);
196 *opt
+= dhcp_classid
[1] + 2;
209 bkmem_free((char *)tmp
->pkt
, tmp
->len
);
210 bkmem_free((char *)tmp
, sizeof (PKT_LIST
));
212 list_hd
= list_tl
= NULL
;
217 remove_list(PKT_LIST
*pl
, int flag
)
222 if (list_hd
== list_tl
) {
223 list_hd
= list_tl
= NULL
;
224 } else if (list_hd
== pl
) {
226 list_hd
->prev
= NULL
;
227 } else if (list_tl
== pl
) {
228 list_tl
= list_tl
->prev
;
229 list_tl
->next
= NULL
;
231 pl
->prev
->next
= pl
->next
;
232 pl
->next
->prev
= pl
->prev
;
236 bkmem_free((char *)pl
->pkt
, pl
->len
);
237 bkmem_free((char *)pl
, sizeof (PKT_LIST
));
242 * Collects BOOTP responses. Length has to be right, it has to be
243 * a BOOTP reply pkt, with the same XID and HW address as ours. Adds
244 * them to the pkt list.
246 * Returns 0 if no error processing packet, 1 if an error occurred and/or
247 * collection of replies should stop. Used in inet() calls.
250 bootp_collect(int len
)
252 PKT
*s
= (PKT
*)dhcp_snd_bufp
;
253 PKT
*r
= (PKT
*)dhcp_rcv_bufp
;
256 if (len
< sizeof (PKT
)) {
257 dprintf("%s: BOOTP reply too small: %d\n", s_n
, len
);
260 if (r
->op
== BOOTREPLY
&& r
->xid
== s
->xid
&&
261 bcmp((caddr_t
)s
->chaddr
, (caddr_t
)r
->chaddr
, s
->hlen
) == 0) {
262 /* Add a packet to the pkt list */
263 if (pkt_counter
> (MAX_PKT_LIST
- 1))
264 return (1); /* got enough packets already */
265 if (((pl
= (PKT_LIST
*)bkmem_zalloc(sizeof (PKT_LIST
))) ==
266 NULL
) || ((pl
->pkt
= (PKT
*)bkmem_zalloc(len
)) == NULL
)) {
269 bkmem_free((char *)pl
, sizeof (PKT_LIST
));
272 bcopy(dhcp_rcv_bufp
, pl
->pkt
, len
);
274 if (list_hd
== NULL
) {
275 list_hd
= list_tl
= pl
;
289 * Checks if BOOTP exchange(s) were successful. Returns 1 if they
290 * were, 0 otherwise. Used in inet() calls.
295 PKT
*s
= (PKT
*)dhcp_snd_bufp
;
297 if (list_hd
!= NULL
) {
298 /* remember the secs - we may need them later */
299 dhcp_secs
= ntohs(s
->secs
);
302 s
->secs
= htons((uint16_t)((prom_gettime() - dhcp_start_time
)/1000));
307 * This function accesses the network. Opens a connection, and binds to
308 * it if a client binding doesn't already exist. If 'tries' is 0, then
309 * no reply is expected/returned. If 'tries' is non-zero, then 'tries'
310 * attempts are made to get a valid response. If 'tol' is not zero,
311 * then this function will wait for 'tol' milliseconds for more than one
312 * response to a transmit.
314 * Returns 0 for success, errno otherwise.
317 inet(uint32_t size
, struct in_addr
*src
, struct in_addr
*dest
, uint32_t tries
,
320 int done
= B_FALSE
, flags
, len
;
321 uint32_t attempts
= 0;
323 uint32_t wait_time
; /* Max time collect replies */
324 uint32_t init_timeout
; /* Max time wait ANY reply */
326 struct sockaddr_in saddr
, daddr
;
328 if ((sd
= socket(AF_INET
, SOCK_DGRAM
, 0)) < 0) {
329 dprintf("%s: Can't open a socket.\n", s_n
);
335 bzero(&saddr
, sizeof (struct sockaddr_in
));
336 saddr
.sin_family
= AF_INET
;
337 saddr
.sin_port
= htons(IPPORT_BOOTPC
);
338 saddr
.sin_addr
.s_addr
= htonl(src
->s_addr
);
340 if (bind(sd
, (struct sockaddr
*)&saddr
, sizeof (saddr
)) < 0) {
341 dprintf("%s: Cannot bind to port %d, errno: %d\n",
342 s_n
, IPPORT_BOOTPC
, errno
);
343 (void) socket_close(sd
);
347 if (ntohl(dest
->s_addr
) == INADDR_BROADCAST
) {
348 int dontroute
= B_TRUE
;
349 (void) setsockopt(sd
, SOL_SOCKET
, SO_DONTROUTE
,
350 (const void *)&dontroute
, sizeof (dontroute
));
353 bzero(&daddr
, sizeof (struct sockaddr_in
));
354 daddr
.sin_family
= AF_INET
;
355 daddr
.sin_port
= htons(IPPORT_BOOTPS
);
356 daddr
.sin_addr
.s_addr
= htonl(dest
->s_addr
);
357 wait_time
= prom_gettime() + tol
;
360 if (sendto(sd
, (char *)dhcp_snd_bufp
, size
, flags
,
361 (struct sockaddr
*)&daddr
, sizeof (daddr
)) < 0) {
362 dprintf("%s: sendto failed with errno: %d\n",
364 (void) socket_close(sd
);
368 break; /* don't bother to check for reply */
370 now
= prom_gettime();
378 init_timeout
= now
+ timeout
;
379 wait_time
= now
+ tol
;
381 if ((len
= recvfrom(sd
, (char *)dhcp_rcv_bufp
,
382 (int)dhcp_buf_size
, MSG_DONTWAIT
, NULL
,
384 if (errno
== EWOULDBLOCK
)
385 continue; /* DONT WAIT */
386 (void) socket_close(sd
);
391 if (bootp_collect(len
))
392 break; /* Stop collecting */
395 if (wait_time
< prom_gettime())
396 break; /* collection timeout */
398 } while (prom_gettime() < init_timeout
);
400 if (bootp_success()) {
402 break; /* got the goods */
404 } while (++attempts
< tries
);
406 (void) socket_close(sd
);
408 return (done
? 0 : DHCP_NO_DATA
);
412 * Print the message from the server.
415 prt_server_msg(DHCP_OPT
*p
)
418 char scratch
[DHCP_MAX_OPT_SIZE
+ 1];
420 if (len
> DHCP_MAX_OPT_SIZE
)
421 len
= DHCP_MAX_OPT_SIZE
;
422 bcopy(p
->value
, scratch
, len
);
424 printf("%s: Message from server: '%s'\n", s_n
, scratch
);
428 * This function scans the list of OFFERS, and returns the "best" offer.
429 * The criteria used for determining this is:
432 * DHCP OFFER (not BOOTP), same client_id as ours, same class_id,
433 * Longest lease, all the options we need.
436 * DHCP OFFER, no class_id, short lease, only some of the options we need.
438 * We're really reach'in
441 * DON'T select an offer from a server that gave us a configuration we
442 * couldn't use. Take this server off the "bad" list when this is done.
443 * Next time, we could potentially retry this server's configuration.
445 * NOTE: perhaps this bad server should have a counter associated with it.
450 PKT_LIST
*wk
, *tk
, *best
;
453 /* Pass one. Scan for options, set appropriate opt field. */
456 if ((err
= dhcp_options_scan(wk
, B_TRUE
)) != 0) {
457 /* Garbled Options. Nuke this pkt. */
458 if (boothowto
& RB_DEBUG
) {
460 case DHCP_WRONG_MSG_TYPE
:
461 printf("%s: Unexpected DHCP message.\n",
464 case DHCP_GARBLED_MSG_TYPE
:
466 "%s: Garbled DHCP message type.\n",
469 case DHCP_BAD_OPT_OVLD
:
470 printf("%s: Bad option overload.\n",
477 remove_list(tk
, B_TRUE
);
484 * Pass two. Pick out the best offer. Point system.
487 * 1) No option overload
488 * 2) Encapsulated vendor option
489 * 3) Non-null sname and siaddr fields
490 * 4) Non-null file field
496 for (wk
= list_hd
; wk
!= NULL
; wk
= wk
->next
) {
498 if (wk
->opts
[CD_DHCP_TYPE
] &&
499 wk
->opts
[CD_DHCP_TYPE
]->len
== 1) {
500 if (*wk
->opts
[CD_DHCP_TYPE
]->value
!= OFFER
) {
501 dprintf("%s: Unexpected DHCP message."
502 " Expected OFFER message.\n", s_n
);
505 if (!wk
->opts
[CD_LEASE_TIME
]) {
506 dprintf("%s: DHCP OFFER message without lease "
507 "time parameter.\n", s_n
);
510 if (wk
->opts
[CD_LEASE_TIME
]->len
!= 4) {
511 dprintf("%s: Lease expiration time is "
516 if (!wk
->opts
[CD_SERVER_ID
]) {
517 dprintf("%s: DHCP OFFER message without server "
518 "id parameter.\n", s_n
);
521 if (wk
->opts
[CD_SERVER_ID
]->len
!= 4) {
522 dprintf("%s: Server identifier "
523 "parameter is garbled.\n", s_n
);
527 /* Valid DHCP OFFER. See if we got our parameters. */
528 dprintf("%s: Found valid DHCP OFFER message.\n", s_n
);
533 * Also could be faked, though more difficult
534 * because the encapsulation is hard to encode
535 * on a BOOTP server; plus there's not as much
536 * real estate in the packet for options, so
537 * it's likely this option would get dropped.
539 if (wk
->opts
[CD_VENDOR_SPEC
])
542 dprintf("%s: Found valid BOOTP reply.\n", s_n
);
547 if (bcmp((caddr_t
)wk
->pkt
->cookie
, (caddr_t
)magic
,
548 sizeof (magic
)) == 0) {
550 if (wk
->opts
[CD_SUBNETMASK
])
552 if (wk
->opts
[CD_ROUTER
])
554 if (wk
->opts
[CD_HOSTNAME
])
558 * Prefer options that have diskless boot significance
560 if (ntohl(wk
->pkt
->siaddr
.s_addr
) != INADDR_ANY
)
561 wk
->offset
+= 10; /* server ip */
562 if (wk
->opts
[CD_OPTION_OVERLOAD
] == NULL
) {
563 if (wk
->pkt
->sname
[0] != '\0')
564 wk
->offset
+= 10; /* server name */
565 if (wk
->pkt
->file
[0] != '\0')
566 wk
->offset
+= 5; /* File to load */
570 printf("%s: This server configuration has '%d' points.\n", s_n
,
572 #endif /* DHCP_DEBUG */
576 if (best
->offset
< wk
->offset
)
582 printf("%s: Found best: points: %d\n", s_n
, best
->offset
);
583 #endif /* DHCP_DEBUG */
584 remove_list(best
, B_FALSE
);
586 dprintf("%s: No valid BOOTP reply or DHCP OFFER was found.\n",
589 flush_list(); /* toss the remaining list */
594 * Send a decline message to the generator of the DHCPACK.
597 dhcp_decline(char *msg
, PKT_LIST
*pl
)
602 struct in_addr nets
, ours
, t_server
, t_yiaddr
;
605 if (pl
->opts
[CD_SERVER_ID
] != NULL
&&
606 pl
->opts
[CD_SERVER_ID
]->len
== sizeof (struct in_addr
)) {
607 bcopy(pl
->opts
[CD_SERVER_ID
]->value
,
608 &t_server
, pl
->opts
[CD_SERVER_ID
]->len
);
610 t_server
.s_addr
= htonl(INADDR_BROADCAST
);
611 t_yiaddr
.s_addr
= pl
->pkt
->yiaddr
.s_addr
;
613 pkt
= (PKT
*)dhcp_snd_bufp
;
614 opt
= init_msg(pkt
, opt_decline
);
615 set_hw_spec_data(pkt
, &opt
, opt_decline
);
617 *opt
++ = CD_SERVER_ID
;
618 *opt
++ = sizeof (struct in_addr
);
619 bcopy(&t_server
, opt
, sizeof (struct in_addr
));
620 opt
+= sizeof (struct in_addr
);
621 nets
.s_addr
= htonl(INADDR_BROADCAST
);
624 * Use the given yiaddr in our ciaddr field so server can identify us.
626 pkt
->ciaddr
.s_addr
= t_yiaddr
.s_addr
;
628 ipv4_getipaddr(&ours
); /* Could be 0, could be yiaddr */
629 ours
.s_addr
= htonl(ours
.s_addr
);
631 ulen
= (uint8_t)strlen(msg
);
634 bcopy(msg
, opt
, ulen
);
638 pkt_size
= (uint8_t *)opt
- (uint8_t *)pkt
;
639 if (pkt_size
< sizeof (PKT
))
640 pkt_size
= sizeof (PKT
);
642 timeout
= 0; /* reset timeout */
643 (void) inet(pkt_size
, &ours
, &nets
, 0, 0L);
647 * Implementings SELECTING state of DHCP client state machine.
657 struct in_addr nets
, ours
;
659 pkt
= (PKT
*)dhcp_snd_bufp
;
660 opt
= init_msg(pkt
, opt_discover
);
661 pkt
->secs
= htons((uint16_t)((prom_gettime() - dhcp_start_time
)/1000));
663 *opt
++ = CD_MAX_DHCP_SIZE
;
664 *opt
++ = sizeof (size
);
665 size
= (uint16_t)(dhcp_buf_size
- sizeof (struct ip
) -
666 sizeof (struct udphdr
));
668 bcopy(&size
, opt
, sizeof (size
));
669 opt
+= sizeof (size
);
671 set_hw_spec_data(pkt
, &opt
, opt_discover
);
673 *opt
++ = CD_LEASE_TIME
;
674 *opt
++ = sizeof (lease
);
675 lease
= htonl(DHCP_PERM
); /* ask for a permanent lease */
676 bcopy(&lease
, opt
, sizeof (lease
));
677 opt
+= sizeof (lease
);
680 * If a clientid was set using dhcp_set_client_id(), add this
683 if (dhcp_clientid_len
> 0) {
684 *opt
++ = CD_CLIENT_ID
;
685 *opt
++ = dhcp_clientid_len
;
686 bcopy(dhcp_clientid
, opt
, dhcp_clientid_len
);
687 opt
+= dhcp_clientid_len
;
690 parameter_request_list(&opt
);
694 pkt_size
= (uint8_t *)opt
- (uint8_t *)pkt
;
695 if (pkt_size
< sizeof (PKT
))
696 pkt_size
= sizeof (PKT
);
698 nets
.s_addr
= INADDR_BROADCAST
;
699 ours
.s_addr
= INADDR_ANY
;
700 timeout
= 0; /* reset timeout */
702 return (inet(pkt_size
, &ours
, &nets
, DHCP_RETRIES
, DHCP_WAIT
));
706 * implements the REQUESTING state of the DHCP client state machine.
709 dhcp_requesting(void)
716 struct in_addr nets
, ours
;
720 /* here we scan the list of OFFERS, select the best one. */
723 if ((pl
= select_best()) == NULL
) {
724 dprintf("%s: No valid BOOTP reply or DHCP OFFER was found.\n",
732 * Check to see if we got an OFFER pkt(s). If not, then We only got
733 * a response from a BOOTP server. We'll go to the bound state and
734 * try to use that configuration.
736 if (pl
->opts
[CD_DHCP_TYPE
] == NULL
) {
737 if (mac_call_arp(&pl_pkt
->yiaddr
, NULL
, DHCP_ARP_TIMEOUT
)) {
738 /* Someone responded! go back to SELECTING state. */
739 printf("%s: Some host already using BOOTP %s.\n", s_n
,
740 inet_ntoa(pl_pkt
->yiaddr
));
741 bkmem_free((char *)pl
->pkt
, pl
->len
);
742 bkmem_free((char *)pl
, sizeof (PKT_LIST
));
750 * if we got a message from the server, display it.
752 if (pl
->opts
[CD_MESSAGE
])
753 prt_server_msg(pl
->opts
[CD_MESSAGE
]);
755 * Assemble a DHCPREQUEST, with the ciaddr field set to 0, since we
756 * got here from DISCOVER state. Keep secs field the same for relay
757 * agents. We start with the DHCPOFFER packet we got, and the
758 * options contained in it to make a requested option list.
760 pkt
= (PKT
*)dhcp_snd_bufp
;
761 opt
= init_msg(pkt
, opt_request
);
763 /* Time from Successful DISCOVER message. */
764 pkt
->secs
= htons((uint16_t)dhcp_secs
);
766 size
= (uint16_t)(dhcp_buf_size
- sizeof (struct ip
) -
767 sizeof (struct udphdr
));
769 *opt
++ = CD_MAX_DHCP_SIZE
;
770 *opt
++ = sizeof (size
);
771 bcopy(&size
, opt
, sizeof (size
));
772 opt
+= sizeof (size
);
774 set_hw_spec_data(pkt
, &opt
, opt_request
);
775 *opt
++ = CD_SERVER_ID
;
776 *opt
++ = pl
->opts
[CD_SERVER_ID
]->len
;
777 bcopy(pl
->opts
[CD_SERVER_ID
]->value
, opt
,
778 pl
->opts
[CD_SERVER_ID
]->len
);
779 opt
+= pl
->opts
[CD_SERVER_ID
]->len
;
781 /* our offered lease minus boot time */
782 *opt
++ = CD_LEASE_TIME
;
784 bcopy(pl
->opts
[CD_LEASE_TIME
]->value
, &t_time
,
786 t_time
= ntohl(t_time
);
787 if ((uint32_t)t_time
!= DHCP_PERM
)
788 t_time
-= (uint32_t)dhcp_secs
;
789 t_time
= htonl(t_time
);
790 bcopy(&t_time
, opt
, sizeof (t_time
));
791 opt
+= sizeof (t_time
);
793 /* our offered IP address, as required. */
794 *opt
++ = CD_REQUESTED_IP_ADDR
;
795 *opt
++ = sizeof (struct in_addr
);
796 bcopy(&pl_pkt
->yiaddr
, opt
, sizeof (struct in_addr
));
797 opt
+= sizeof (struct in_addr
);
800 * If a clientid was set using dhcp_set_client_id(), add this
803 if (dhcp_clientid_len
> 0) {
804 *opt
++ = CD_CLIENT_ID
;
805 *opt
++ = dhcp_clientid_len
;
806 bcopy(dhcp_clientid
, opt
, dhcp_clientid_len
);
807 opt
+= dhcp_clientid_len
;
810 parameter_request_list(&opt
);
814 /* Done with the OFFER pkt. */
815 bkmem_free((char *)pl
->pkt
, pl
->len
);
816 bkmem_free((char *)pl
, sizeof (PKT_LIST
));
819 * We make 4 attempts here. We wait for 2 seconds to accumulate
820 * requests, just in case a BOOTP server is too fast!
822 pkt_size
= (uint8_t *)opt
- (uint8_t *)pkt
;
823 if (pkt_size
< sizeof (PKT
))
824 pkt_size
= sizeof (PKT
);
826 nets
.s_addr
= INADDR_BROADCAST
;
827 ours
.s_addr
= INADDR_ANY
;
828 timeout
= 0; /* reset timeout */
830 if ((err
= inet(pkt_size
, &ours
, &nets
, 4, (time_t)2L)) != 0)
832 for (wk
= list_hd
; wk
!= NULL
&& state_pl
== NULL
; wk
= wk
->next
) {
833 if (dhcp_options_scan(wk
, B_TRUE
) != 0 ||
834 !wk
->opts
[CD_DHCP_TYPE
])
835 continue; /* garbled options */
836 switch (*wk
->opts
[CD_DHCP_TYPE
]->value
) {
838 remove_list(wk
, B_FALSE
);
842 printf("%s: rejected by DHCP server: %s\n",
843 s_n
, inet_ntoa(*((struct in_addr
*)wk
->
844 opts
[CD_SERVER_ID
]->value
)));
845 if (wk
->opts
[CD_MESSAGE
])
846 prt_server_msg(wk
->opts
[CD_MESSAGE
]);
849 dprintf("%s: Unexpected DHCP message type.\n", s_n
);
854 if (state_pl
!= NULL
) {
855 if (state_pl
->opts
[CD_MESSAGE
])
856 prt_server_msg(state_pl
->opts
[CD_MESSAGE
]);
857 pl_pkt
= state_pl
->pkt
;
858 /* arp our new address, just to make sure */
859 if (!mac_call_arp(&pl_pkt
->yiaddr
, NULL
, DHCP_ARP_TIMEOUT
)) {
861 * No response. Check if lease is ok.
863 doptp
= state_pl
->opts
[CD_LEASE_TIME
];
864 if (state_pl
->opts
[CD_DHCP_TYPE
] && (!doptp
||
865 (doptp
->len
% 4) != 0)) {
866 dhcp_decline("Missing or corrupted lease time",
871 dhcp_decline("IP Address already being used!",
876 bkmem_free((char *)state_pl
->pkt
, state_pl
->len
);
877 bkmem_free((char *)state_pl
, sizeof (PKT_LIST
));
880 return (0); /* passes the tests! */
882 dprintf("%s: No valid DHCP acknowledge messages received.\n", s_n
);
887 * Implements BOUND state of DHCP client state machine.
892 PKT
*pl_pkt
= state_pl
->pkt
;
895 int items
, i
, fnd
, k
;
896 char hostname
[MAXHOSTNAMELEN
+1];
897 struct in_addr subnet
, defr
, savr
, *ipp
, xip
;
900 if (dhcp_classid
[0] != 0) {
901 char scratch
[DHCP_MAX_OPT_SIZE
+ 1];
903 bcopy(&dhcp_classid
[2], scratch
, dhcp_classid
[1]);
904 scratch
[dhcp_classid
[1]] = '\0';
905 printf("Your machine is of the class: '%s'.\n", scratch
);
907 #endif /* DHCP_DEBUG */
909 /* First, set the bare essentials. (IP layer parameters) */
911 ipv4_getipaddr(&xip
);
912 if (xip
.s_addr
!= INADDR_ANY
)
915 ipp
= &pl_pkt
->yiaddr
;
916 ipp
->s_addr
= ntohl(ipp
->s_addr
);
920 ipp
->s_addr
= htonl(ipp
->s_addr
);
922 if (boothowto
& RB_VERBOSE
)
923 printf("%s: IP address is: %s\n", s_n
, inet_ntoa(*ipp
));
926 * Ensure that the Boot NFS READ size, if given, is an int16_t.
928 if (state_pl
->vs
[VS_BOOT_NFS_READSIZE
] != NULL
) {
929 doptp
= state_pl
->vs
[VS_BOOT_NFS_READSIZE
];
930 if (doptp
->len
!= sizeof (int16_t))
931 state_pl
->vs
[VS_BOOT_NFS_READSIZE
] = NULL
;
935 * Set subnetmask. Nice, but not required.
937 if (state_pl
->opts
[CD_SUBNETMASK
] != NULL
) {
938 doptp
= state_pl
->opts
[CD_SUBNETMASK
];
940 state_pl
->opts
[CD_SUBNETMASK
] = NULL
;
942 bcopy(doptp
->value
, &subnet
,
943 sizeof (struct in_addr
));
944 dprintf("%s: Setting netmask to: %s\n", s_n
,
946 subnet
.s_addr
= ntohl(subnet
.s_addr
);
947 ipv4_setnetmask(&subnet
);
952 * Set default IP TTL. Nice, but not required.
954 if (state_pl
->opts
[CD_IPTTL
] != NULL
) {
955 doptp
= state_pl
->opts
[CD_IPTTL
];
957 state_pl
->opts
[CD_IPTTL
] = NULL
;
959 doptp
= state_pl
->opts
[CD_IPTTL
];
960 ipv4_setmaxttl(*(uint8_t *)doptp
->value
);
961 dprintf("%s: Setting IP TTL to: %d\n", s_n
,
962 *(uint8_t *)doptp
->value
);
967 * Set default router. Not required, although we'll know soon
970 if (state_pl
->opts
[CD_ROUTER
] != NULL
) {
971 doptp
= state_pl
->opts
[CD_ROUTER
];
972 if ((doptp
->len
% 4) != 0) {
973 state_pl
->opts
[CD_ROUTER
] = NULL
;
975 if ((hp
= (uint8_t *)bkmem_alloc(
976 mac_get_hdr_len())) == NULL
) {
980 items
= doptp
->len
/ sizeof (struct in_addr
);
982 bcopy(tp
, &savr
, sizeof (struct in_addr
));
983 for (i
= 0, fnd
= 0; i
< items
; i
++) {
984 bcopy(tp
, &defr
, sizeof (struct in_addr
));
985 for (k
= 0, fnd
= 0; k
< 2 && fnd
== 0; k
++) {
986 fnd
= mac_get_arp(&defr
, hp
,
988 mac_get_arp_timeout());
993 "%s: Warning: Router %s is unreachable.\n",
994 s_n
, inet_ntoa(defr
));
995 tp
+= sizeof (struct in_addr
);
997 bkmem_free((char *)hp
, mac_get_hdr_len());
1000 * If fnd is 0, we didn't find a working router. We'll
1001 * still try to use first default router. If we got
1002 * a bad router address (like not on the same net),
1003 * we're hosed anyway.
1007 "%s: Warning: Using default router: %s\n",
1008 s_n
, inet_ntoa(savr
));
1009 defr
.s_addr
= savr
.s_addr
;
1011 /* ipv4_route expects network order IP addresses */
1012 (void) ipv4_route(IPV4_ADD_ROUTE
, RT_DEFAULT
, NULL
,
1018 * Set hostname. Not required.
1020 if (state_pl
->opts
[CD_HOSTNAME
] != NULL
) {
1021 doptp
= state_pl
->opts
[CD_HOSTNAME
];
1023 if (i
> MAXHOSTNAMELEN
)
1025 bcopy(doptp
->value
, hostname
, i
);
1027 (void) sethostname(hostname
, i
);
1028 if (boothowto
& RB_VERBOSE
)
1029 printf("%s: Hostname is %s\n", s_n
, hostname
);
1033 * We don't care about the lease time.... We can't enforce it anyway.
1039 * Convert the DHCPACK into a pure ASCII boot property for use by the kernel
1040 * later in the boot process.
1043 create_bootpresponse_bprop(PKT_LIST
*pl
)
1047 if (pl
== NULL
|| bootp_response
!= NULL
)
1050 buflen
= (pl
->len
* 2) + 1; /* extra space for null (1) */
1051 if ((bootp_response
= bkmem_alloc(buflen
)) == NULL
)
1054 if (octet_to_hexascii((uint8_t *)pl
->pkt
, pl
->len
, bootp_response
,
1056 bkmem_free(bootp_response
, (pl
->len
* 2) + 1);
1057 bootp_response
= NULL
;
1063 * Examines /chosen node for "bootp-response" property. If it exists, this
1064 * property is the DHCPACK cached there by the PROM's DHCP implementation.
1066 * If cache_present is B_TRUE, we simply return B_TRUE if the property exists
1067 * w/o decoding it. If cache_present is B_FALSE, we decode the packet and
1068 * use it as our state packet for the jump to BOUND mode. Note that it's good
1069 * enough that the cache exists w/o validation for determining if that's what
1070 * the user intended.
1072 * We'll short-circuit the DHCP exchange by accepting this packet. We build a
1073 * PKT_LIST structure, and copy the bootp-response property value into a
1074 * PKT buffer we allocated. We then scan the PKT for options, and then
1075 * set state_pl to point to it.
1077 * Returns B_TRUE if a packet was cached (and was processed correctly), false
1078 * otherwise. The caller needs to make the state change from SELECTING to
1079 * BOUND upon a B_TRUE return from this function.
1082 prom_cached_reply(int cache_present
)
1087 char *prop
= PROM_BOOT_CACHED
;
1089 chosen
= prom_finddevice("/chosen");
1090 if (chosen
== OBP_NONODE
|| chosen
== OBP_BADNODE
)
1091 chosen
= prom_nextnode((pnode_t
)0); /* root node */
1093 if ((len
= prom_getproplen(chosen
, prop
)) <= 0)
1099 if (((pl
= (PKT_LIST
*)bkmem_zalloc(sizeof (PKT_LIST
))) == NULL
) ||
1100 ((pl
->pkt
= (PKT
*)bkmem_zalloc(len
)) == NULL
)) {
1103 bkmem_free((char *)pl
, sizeof (PKT_LIST
));
1107 (void) prom_getprop(chosen
, prop
, (caddr_t
)pl
->pkt
);
1111 if (dhcp_options_scan(pl
, B_TRUE
) != 0) {
1112 /* garbled packet */
1113 bkmem_free((char *)pl
->pkt
, pl
->len
);
1114 bkmem_free((char *)pl
, sizeof (PKT_LIST
));
1123 * Perform DHCP to acquire what we used to get using rarp/bootparams.
1124 * Returns 0 for success, nonzero otherwise.
1126 * DHCP client state machine.
1134 dhcp_buf_size
= mac_get_mtu();
1135 dhcp_snd_bufp
= (PKT
*)bkmem_alloc(dhcp_buf_size
);
1136 dhcp_rcv_bufp
= (PKT
*)bkmem_alloc(dhcp_buf_size
);
1138 if (dhcp_snd_bufp
== NULL
|| dhcp_rcv_bufp
== NULL
) {
1139 if (dhcp_snd_bufp
!= NULL
)
1140 bkmem_free((char *)dhcp_snd_bufp
, dhcp_buf_size
);
1141 if (dhcp_rcv_bufp
!= NULL
)
1142 bkmem_free((char *)dhcp_rcv_bufp
, dhcp_buf_size
);
1147 while (err
== 0 && !done
) {
1148 switch (dhcp_state
) {
1152 if (prom_cached_reply(B_FALSE
)) {
1153 dprintf("%s: Using PROM cached BOOT reply...\n",
1157 dhcp_state
= SELECTING
;
1158 dhcp_start_time
= prom_gettime();
1165 err
= dhcp_selecting();
1168 dhcp_state
= REQUESTING
;
1172 "%s: No DHCP response after %d tries.\n",
1176 /* major network problems */
1177 dprintf("%s: Network transaction failed: %d\n",
1186 err
= dhcp_requesting();
1192 dprintf("%s: Request timed out.\n", s_n
);
1193 dhcp_state
= SELECTING
;
1198 /* major network problems */
1199 dprintf("%s: Network transaction failed: %d\n",
1208 * We just "give up" if bound state fails.
1211 if ((err
= dhcp_bound()) == 0) {
1212 dhcp_state
= CONFIGURED
;
1218 create_bootpresponse_bprop(state_pl
);
1223 bkmem_free((char *)dhcp_snd_bufp
, dhcp_buf_size
);
1224 bkmem_free((char *)dhcp_rcv_bufp
, dhcp_buf_size
);
1230 * Returns a copy of the DHCP-supplied value of the parameter requested
1234 dhcp_getinfo(uchar_t optcat
, uint16_t code
, uint16_t optsize
, void *value
,
1237 size_t len
= *vallenp
;
1239 if (dhcp_getinfo_pl(state_pl
, optcat
, code
,
1240 optsize
, value
, &len
)) {
1242 if (len
<= *vallenp
) {
1252 * Sets the clientid option.
1255 dhcp_set_client_id(uint8_t *clientid
, uint8_t clientid_len
)
1257 bcopy(clientid
, dhcp_clientid
, clientid_len
);
1258 dhcp_clientid_len
= clientid_len
;