1 /*
2 * CDDL HEADER START
3 *
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.
7 *
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.
12 *
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]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 *
25 * Standalone dhcp client.
26 */
27
28 #pragma ident "%Z%%M% %I% %E% SMI"
29
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>
47
48 #include "ipv4.h"
49 #include "mac.h"
50 #include <sys/bootdebug.h>
51 #include "dhcpv4.h"
52
53 static char *s_n = "INIT";
54 static enum DHCPSTATE dhcp_state = INIT;
55
56 static PKT *dhcp_snd_bufp, *dhcp_rcv_bufp;
57 static int dhcp_buf_size;
58
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 };
63
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;
67
68 static uint32_t dhcp_start_time; /* start time (msecs */
69 static time_t dhcp_secs;
70 static uint32_t timeout; /* timeout in milliseconds */
71
72 static int pkt_counter;
73 PKT_LIST *list_tl, *list_hd;
74 PKT_LIST *state_pl = NULL;
75
76 #define PROM_BOOT_CACHED "bootp-response"
77 extern char *bootp_response; /* bootprop.c */
78 extern int pagesize;
79
80 /*
81 * Do whatever reset actions/initialization actions are generic for every
82 * DHCP/bootp message. Set the message type.
83 *
84 * Returns: the updated options ptr.
85 */
86 static uint8_t *
init_msg(PKT * pkt,uint8_t * pkttype)87 init_msg(PKT *pkt, uint8_t *pkttype)
88 {
89 static uint32_t xid;
90
91 bzero(pkt, dhcp_buf_size);
92 bcopy(magic, pkt->cookie, sizeof (pkt->cookie));
93 pkt->op = BOOTREQUEST;
94 if (xid == 0)
95 bcopy(mac_get_addr_buf()+2, &xid, 4);
96 else
97 xid++;
98 pkt->xid = xid;
99 bcopy(pkttype, pkt->options, 3);
100 return ((uint8_t *)(pkt->options + 3));
101 }
102
103 /*
104 * Parameter request list.
105 */
106 static void
parameter_request_list(uint8_t ** opt)107 parameter_request_list(uint8_t **opt)
108 {
109 /*
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.
113 */
114 static uint8_t prlist[] = {
115 CD_REQUEST_LIST, /* parameter request list option number */
116 4, /* number of options requested */
117 CD_SUBNETMASK,
118 CD_ROUTER,
119 CD_HOSTNAME,
120 CD_VENDOR_SPEC
121 };
122 if (opt && *opt) {
123 bcopy(prlist, *opt, sizeof (prlist));
124 *opt += sizeof (prlist);
125 }
126 }
127
128 /*
129 * Set hardware specific fields
130 */
131 static void
set_hw_spec_data(PKT * p,uint8_t ** opt,uint8_t * pkttype)132 set_hw_spec_data(PKT *p, uint8_t **opt, uint8_t *pkttype)
133 {
134 char mfg[DHCP_MAX_OPT_SIZE + 1], cbuf[DHCP_MAX_OPT_SIZE + 1];
135 uint8_t *tp, *dp;
136 int adjust_len, len, i;
137
138 p->htype = mac_arp_type(mac_get_type());
139 len = (uchar_t)mac_get_addr_len();
140 if (len <= sizeof (p->chaddr)) {
141 p->hlen = len;
142 bcopy(mac_get_addr_buf(), p->chaddr, len);
143 } else {
144 uint8_t type = *(pkttype + 2);
145 /*
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
150 * DISCOVER/REQUEST.
151 */
152 if ((type == DISCOVER) || (type == REQUEST))
153 p->flags = htons(BCAST_MASK);
154 }
155
156 if (opt && *opt) {
157 if (dhcp_classid[0] == '\0') {
158 /*
159 * Classids based on mfg name: Commas (,) are
160 * converted to periods (.), and spaces ( ) are removed.
161 */
162 dhcp_classid[0] = CD_CLASS_ID;
163
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.");
168 } else {
169 len = 0;
170 cbuf[0] = '\0';
171 }
172 len += strlen(mfg);
173
174 if ((len + 2) < DHCP_MAX_OPT_SIZE) {
175 tp = (uint8_t *)strcat(cbuf, mfg);
176 dp = &dhcp_classid[2];
177 adjust_len = 0;
178 for (i = 0; i < len; i++, tp++) {
179 if (*tp == ',') {
180 *dp++ = '.';
181 } else if (*tp == ' ') {
182 adjust_len++;
183 } else {
184 *dp++ = *tp;
185 }
186 }
187 len -= adjust_len;
188 dhcp_classid[1] = (uint8_t)len;
189 } else
190 prom_panic("Not enough space for class id");
191 #ifdef DHCP_DEBUG
192 printf("%s: Classid: %s\n", s_n, &dhcp_classid[2]);
193 #endif /* DHCP_DEBUG */
194 }
195 bcopy(dhcp_classid, *opt, dhcp_classid[1] + 2);
196 *opt += dhcp_classid[1] + 2;
197 }
198 }
199
200 static void
flush_list(void)201 flush_list(void)
202 {
203 PKT_LIST *wk, *tmp;
204
205 wk = list_hd;
206 while (wk != NULL) {
207 tmp = wk;
208 wk = wk->next;
209 bkmem_free((char *)tmp->pkt, tmp->len);
210 bkmem_free((char *)tmp, sizeof (PKT_LIST));
211 }
212 list_hd = list_tl = NULL;
213 pkt_counter = 0;
214 }
215
216 static void
remove_list(PKT_LIST * pl,int flag)217 remove_list(PKT_LIST *pl, int flag)
218 {
219 if (list_hd == NULL)
220 return;
221
222 if (list_hd == list_tl) {
223 list_hd = list_tl = NULL;
224 } else if (list_hd == pl) {
225 list_hd = pl->next;
226 list_hd->prev = NULL;
227 } else if (list_tl == pl) {
228 list_tl = list_tl->prev;
229 list_tl->next = NULL;
230 } else {
231 pl->prev->next = pl->next;
232 pl->next->prev = pl->prev;
233 }
234 pkt_counter--;
235 if (flag) {
236 bkmem_free((char *)pl->pkt, pl->len);
237 bkmem_free((char *)pl, sizeof (PKT_LIST));
238 }
239 }
240
241 /*
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.
245 *
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.
248 */
249 static int
bootp_collect(int len)250 bootp_collect(int len)
251 {
252 PKT *s = (PKT *)dhcp_snd_bufp;
253 PKT *r = (PKT *)dhcp_rcv_bufp;
254 PKT_LIST *pl;
255
256 if (len < sizeof (PKT)) {
257 dprintf("%s: BOOTP reply too small: %d\n", s_n, len);
258 return (1);
259 }
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)) {
267 errno = ENOMEM;
268 if (pl != NULL)
269 bkmem_free((char *)pl, sizeof (PKT_LIST));
270 return (1);
271 }
272 bcopy(dhcp_rcv_bufp, pl->pkt, len);
273 pl->len = len;
274 if (list_hd == NULL) {
275 list_hd = list_tl = pl;
276 pl->prev = NULL;
277 } else {
278 list_tl->next = pl;
279 pl->prev = list_tl;
280 list_tl = pl;
281 }
282 pkt_counter++;
283 pl->next = NULL;
284 }
285 return (0);
286 }
287
288 /*
289 * Checks if BOOTP exchange(s) were successful. Returns 1 if they
290 * were, 0 otherwise. Used in inet() calls.
291 */
292 static int
bootp_success(void)293 bootp_success(void)
294 {
295 PKT *s = (PKT *)dhcp_snd_bufp;
296
297 if (list_hd != NULL) {
298 /* remember the secs - we may need them later */
299 dhcp_secs = ntohs(s->secs);
300 return (1);
301 }
302 s->secs = htons((uint16_t)((prom_gettime() - dhcp_start_time)/1000));
303 return (0);
304 }
305
306 /*
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.
313 *
314 * Returns 0 for success, errno otherwise.
315 */
316 static int
inet(uint32_t size,struct in_addr * src,struct in_addr * dest,uint32_t tries,uint32_t tol)317 inet(uint32_t size, struct in_addr *src, struct in_addr *dest, uint32_t tries,
318 uint32_t tol)
319 {
320 int done = B_FALSE, flags, len;
321 uint32_t attempts = 0;
322 int sd;
323 uint32_t wait_time; /* Max time collect replies */
324 uint32_t init_timeout; /* Max time wait ANY reply */
325 uint32_t now;
326 struct sockaddr_in saddr, daddr;
327
328 if ((sd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
329 dprintf("%s: Can't open a socket.\n", s_n);
330 return (errno);
331 }
332
333 flags = 0;
334
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);
339
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);
344 return (errno);
345 }
346
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));
351 }
352
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;
358
359 do {
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",
363 s_n, errno);
364 (void) socket_close(sd);
365 return (errno);
366 }
367 if (!tries)
368 break; /* don't bother to check for reply */
369
370 now = prom_gettime();
371 if (timeout == 0)
372 timeout = 4000;
373 else {
374 timeout <<= 1;
375 if (timeout > 64000)
376 timeout = 64000;
377 }
378 init_timeout = now + timeout;
379 wait_time = now + tol;
380 do {
381 if ((len = recvfrom(sd, (char *)dhcp_rcv_bufp,
382 (int)dhcp_buf_size, MSG_DONTWAIT, NULL,
383 NULL)) < 0) {
384 if (errno == EWOULDBLOCK)
385 continue; /* DONT WAIT */
386 (void) socket_close(sd);
387 flush_list();
388 return (errno);
389 }
390
391 if (bootp_collect(len))
392 break; /* Stop collecting */
393
394 if (tol != 0) {
395 if (wait_time < prom_gettime())
396 break; /* collection timeout */
397 }
398 } while (prom_gettime() < init_timeout);
399
400 if (bootp_success()) {
401 done = B_TRUE;
402 break; /* got the goods */
403 }
404 } while (++attempts < tries);
405
406 (void) socket_close(sd);
407
408 return (done ? 0 : DHCP_NO_DATA);
409 }
410
411 /*
412 * Print the message from the server.
413 */
414 static void
prt_server_msg(DHCP_OPT * p)415 prt_server_msg(DHCP_OPT *p)
416 {
417 int len = p->len;
418 char scratch[DHCP_MAX_OPT_SIZE + 1];
419
420 if (len > DHCP_MAX_OPT_SIZE)
421 len = DHCP_MAX_OPT_SIZE;
422 bcopy(p->value, scratch, len);
423 scratch[len] = '\0';
424 printf("%s: Message from server: '%s'\n", s_n, scratch);
425 }
426
427 /*
428 * This function scans the list of OFFERS, and returns the "best" offer.
429 * The criteria used for determining this is:
430 *
431 * The best:
432 * DHCP OFFER (not BOOTP), same client_id as ours, same class_id,
433 * Longest lease, all the options we need.
434 *
435 * Not quite as good:
436 * DHCP OFFER, no class_id, short lease, only some of the options we need.
437 *
438 * We're really reach'in
439 * BOOTP reply.
440 *
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.
444 *
445 * NOTE: perhaps this bad server should have a counter associated with it.
446 */
447 static PKT_LIST *
select_best(void)448 select_best(void)
449 {
450 PKT_LIST *wk, *tk, *best;
451 int err = 0;
452
453 /* Pass one. Scan for options, set appropriate opt field. */
454 wk = list_hd;
455 while (wk != NULL) {
456 if ((err = dhcp_options_scan(wk, B_TRUE)) != 0) {
457 /* Garbled Options. Nuke this pkt. */
458 if (boothowto & RB_DEBUG) {
459 switch (err) {
460 case DHCP_WRONG_MSG_TYPE:
461 printf("%s: Unexpected DHCP message.\n",
462 s_n);
463 break;
464 case DHCP_GARBLED_MSG_TYPE:
465 printf(
466 "%s: Garbled DHCP message type.\n",
467 s_n);
468 break;
469 case DHCP_BAD_OPT_OVLD:
470 printf("%s: Bad option overload.\n",
471 s_n);
472 break;
473 }
474 }
475 tk = wk;
476 wk = wk->next;
477 remove_list(tk, B_TRUE);
478 continue;
479 }
480 wk = wk->next;
481 }
482
483 /*
484 * Pass two. Pick out the best offer. Point system.
485 * What's important?
486 * 0) DHCP
487 * 1) No option overload
488 * 2) Encapsulated vendor option
489 * 3) Non-null sname and siaddr fields
490 * 4) Non-null file field
491 * 5) Hostname
492 * 6) Subnetmask
493 * 7) Router
494 */
495 best = NULL;
496 for (wk = list_hd; wk != NULL; wk = wk->next) {
497 wk->offset = 0;
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);
503 continue;
504 }
505 if (!wk->opts[CD_LEASE_TIME]) {
506 dprintf("%s: DHCP OFFER message without lease "
507 "time parameter.\n", s_n);
508 continue;
509 } else {
510 if (wk->opts[CD_LEASE_TIME]->len != 4) {
511 dprintf("%s: Lease expiration time is "
512 "garbled.\n", s_n);
513 continue;
514 }
515 }
516 if (!wk->opts[CD_SERVER_ID]) {
517 dprintf("%s: DHCP OFFER message without server "
518 "id parameter.\n", s_n);
519 continue;
520 } else {
521 if (wk->opts[CD_SERVER_ID]->len != 4) {
522 dprintf("%s: Server identifier "
523 "parameter is garbled.\n", s_n);
524 continue;
525 }
526 }
527 /* Valid DHCP OFFER. See if we got our parameters. */
528 dprintf("%s: Found valid DHCP OFFER message.\n", s_n);
529
530 wk->offset += 30;
531
532 /*
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.
538 */
539 if (wk->opts[CD_VENDOR_SPEC])
540 wk->offset += 80;
541 } else
542 dprintf("%s: Found valid BOOTP reply.\n", s_n);
543
544 /*
545 * RFC1048 BOOTP?
546 */
547 if (bcmp((caddr_t)wk->pkt->cookie, (caddr_t)magic,
548 sizeof (magic)) == 0) {
549 wk->offset += 5;
550 if (wk->opts[CD_SUBNETMASK])
551 wk->offset++;
552 if (wk->opts[CD_ROUTER])
553 wk->offset++;
554 if (wk->opts[CD_HOSTNAME])
555 wk->offset += 5;
556
557 /*
558 * Prefer options that have diskless boot significance
559 */
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 */
567 }
568 }
569 #ifdef DHCP_DEBUG
570 printf("%s: This server configuration has '%d' points.\n", s_n,
571 wk->offset);
572 #endif /* DHCP_DEBUG */
573 if (!best)
574 best = wk;
575 else {
576 if (best->offset < wk->offset)
577 best = wk;
578 }
579 }
580 if (best) {
581 #ifdef DHCP_DEBUG
582 printf("%s: Found best: points: %d\n", s_n, best->offset);
583 #endif /* DHCP_DEBUG */
584 remove_list(best, B_FALSE);
585 } else {
586 dprintf("%s: No valid BOOTP reply or DHCP OFFER was found.\n",
587 s_n);
588 }
589 flush_list(); /* toss the remaining list */
590 return (best);
591 }
592
593 /*
594 * Send a decline message to the generator of the DHCPACK.
595 */
596 static void
dhcp_decline(char * msg,PKT_LIST * pl)597 dhcp_decline(char *msg, PKT_LIST *pl)
598 {
599 PKT *pkt;
600 uint8_t *opt, ulen;
601 int pkt_size;
602 struct in_addr nets, ours, t_server, t_yiaddr;
603
604 if (pl != NULL) {
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);
609 } else
610 t_server.s_addr = htonl(INADDR_BROADCAST);
611 t_yiaddr.s_addr = pl->pkt->yiaddr.s_addr;
612 }
613 pkt = (PKT *)dhcp_snd_bufp;
614 opt = init_msg(pkt, opt_decline);
615 set_hw_spec_data(pkt, &opt, opt_decline);
616
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);
622
623 /*
624 * Use the given yiaddr in our ciaddr field so server can identify us.
625 */
626 pkt->ciaddr.s_addr = t_yiaddr.s_addr;
627
628 ipv4_getipaddr(&ours); /* Could be 0, could be yiaddr */
629 ours.s_addr = htonl(ours.s_addr);
630
631 ulen = (uint8_t)strlen(msg);
632 *opt++ = CD_MESSAGE;
633 *opt++ = ulen;
634 bcopy(msg, opt, ulen);
635 opt += ulen;
636 *opt++ = CD_END;
637
638 pkt_size = (uint8_t *)opt - (uint8_t *)pkt;
639 if (pkt_size < sizeof (PKT))
640 pkt_size = sizeof (PKT);
641
642 timeout = 0; /* reset timeout */
643 (void) inet(pkt_size, &ours, &nets, 0, 0L);
644 }
645
646 /*
647 * Implementings SELECTING state of DHCP client state machine.
648 */
649 static int
dhcp_selecting(void)650 dhcp_selecting(void)
651 {
652 int pkt_size;
653 PKT *pkt;
654 uint8_t *opt;
655 uint16_t size;
656 uint32_t lease;
657 struct in_addr nets, ours;
658
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));
662
663 *opt++ = CD_MAX_DHCP_SIZE;
664 *opt++ = sizeof (size);
665 size = (uint16_t)(dhcp_buf_size - sizeof (struct ip) -
666 sizeof (struct udphdr));
667 size = htons(size);
668 bcopy(&size, opt, sizeof (size));
669 opt += sizeof (size);
670
671 set_hw_spec_data(pkt, &opt, opt_discover);
672
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);
678
679 /*
680 * If a clientid was set using dhcp_set_client_id(), add this
681 * to the options.
682 */
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;
688 }
689
690 parameter_request_list(&opt);
691
692 *opt++ = CD_END;
693
694 pkt_size = (uint8_t *)opt - (uint8_t *)pkt;
695 if (pkt_size < sizeof (PKT))
696 pkt_size = sizeof (PKT);
697
698 nets.s_addr = INADDR_BROADCAST;
699 ours.s_addr = INADDR_ANY;
700 timeout = 0; /* reset timeout */
701
702 return (inet(pkt_size, &ours, &nets, DHCP_RETRIES, DHCP_WAIT));
703 }
704
705 /*
706 * implements the REQUESTING state of the DHCP client state machine.
707 */
708 static int
dhcp_requesting(void)709 dhcp_requesting(void)
710 {
711 PKT_LIST *pl, *wk;
712 PKT *pkt, *pl_pkt;
713 uint8_t *opt;
714 int pkt_size, err;
715 uint32_t t_time;
716 struct in_addr nets, ours;
717 DHCP_OPT *doptp;
718 uint16_t size;
719
720 /* here we scan the list of OFFERS, select the best one. */
721 state_pl = NULL;
722
723 if ((pl = select_best()) == NULL) {
724 dprintf("%s: No valid BOOTP reply or DHCP OFFER was found.\n",
725 s_n);
726 return (1);
727 }
728
729 pl_pkt = pl->pkt;
730
731 /*
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.
735 */
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));
743 return (1);
744 }
745 state_pl = pl;
746 return (0);
747 }
748
749 /*
750 * if we got a message from the server, display it.
751 */
752 if (pl->opts[CD_MESSAGE])
753 prt_server_msg(pl->opts[CD_MESSAGE]);
754 /*
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.
759 */
760 pkt = (PKT *)dhcp_snd_bufp;
761 opt = init_msg(pkt, opt_request);
762
763 /* Time from Successful DISCOVER message. */
764 pkt->secs = htons((uint16_t)dhcp_secs);
765
766 size = (uint16_t)(dhcp_buf_size - sizeof (struct ip) -
767 sizeof (struct udphdr));
768 size = htons(size);
769 *opt++ = CD_MAX_DHCP_SIZE;
770 *opt++ = sizeof (size);
771 bcopy(&size, opt, sizeof (size));
772 opt += sizeof (size);
773
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;
780
781 /* our offered lease minus boot time */
782 *opt++ = CD_LEASE_TIME;
783 *opt++ = 4;
784 bcopy(pl->opts[CD_LEASE_TIME]->value, &t_time,
785 sizeof (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);
792
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);
798
799 /*
800 * If a clientid was set using dhcp_set_client_id(), add this
801 * to the options.
802 */
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;
808 }
809
810 parameter_request_list(&opt);
811
812 *opt++ = CD_END;
813
814 /* Done with the OFFER pkt. */
815 bkmem_free((char *)pl->pkt, pl->len);
816 bkmem_free((char *)pl, sizeof (PKT_LIST));
817
818 /*
819 * We make 4 attempts here. We wait for 2 seconds to accumulate
820 * requests, just in case a BOOTP server is too fast!
821 */
822 pkt_size = (uint8_t *)opt - (uint8_t *)pkt;
823 if (pkt_size < sizeof (PKT))
824 pkt_size = sizeof (PKT);
825
826 nets.s_addr = INADDR_BROADCAST;
827 ours.s_addr = INADDR_ANY;
828 timeout = 0; /* reset timeout */
829
830 if ((err = inet(pkt_size, &ours, &nets, 4, (time_t)2L)) != 0)
831 return (err);
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) {
837 case ACK:
838 remove_list(wk, B_FALSE);
839 state_pl = wk;
840 break;
841 case NAK:
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]);
847 break;
848 default:
849 dprintf("%s: Unexpected DHCP message type.\n", s_n);
850 break;
851 }
852 }
853 flush_list();
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)) {
860 /*
861 * No response. Check if lease is ok.
862 */
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",
867 state_pl);
868 err++;
869 }
870 } else {
871 dhcp_decline("IP Address already being used!",
872 state_pl);
873 err++;
874 }
875 if (err) {
876 bkmem_free((char *)state_pl->pkt, state_pl->len);
877 bkmem_free((char *)state_pl, sizeof (PKT_LIST));
878 state_pl = NULL;
879 } else
880 return (0); /* passes the tests! */
881 }
882 dprintf("%s: No valid DHCP acknowledge messages received.\n", s_n);
883 return (1);
884 }
885
886 /*
887 * Implements BOUND state of DHCP client state machine.
888 */
889 static int
dhcp_bound(void)890 dhcp_bound(void)
891 {
892 PKT *pl_pkt = state_pl->pkt;
893 DHCP_OPT *doptp;
894 uint8_t *tp, *hp;
895 int items, i, fnd, k;
896 char hostname[MAXHOSTNAMELEN+1];
897 struct in_addr subnet, defr, savr, *ipp, xip;
898
899 #ifdef DHCP_DEBUG
900 if (dhcp_classid[0] != 0) {
901 char scratch[DHCP_MAX_OPT_SIZE + 1];
902
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);
906 }
907 #endif /* DHCP_DEBUG */
908
909 /* First, set the bare essentials. (IP layer parameters) */
910
911 ipv4_getipaddr(&xip);
912 if (xip.s_addr != INADDR_ANY)
913 ipp = &xip;
914 else {
915 ipp = &pl_pkt->yiaddr;
916 ipp->s_addr = ntohl(ipp->s_addr);
917 ipv4_setipaddr(ipp);
918 }
919
920 ipp->s_addr = htonl(ipp->s_addr);
921
922 if (boothowto & RB_VERBOSE)
923 printf("%s: IP address is: %s\n", s_n, inet_ntoa(*ipp));
924
925 /*
926 * Ensure that the Boot NFS READ size, if given, is an int16_t.
927 */
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;
932 }
933
934 /*
935 * Set subnetmask. Nice, but not required.
936 */
937 if (state_pl->opts[CD_SUBNETMASK] != NULL) {
938 doptp = state_pl->opts[CD_SUBNETMASK];
939 if (doptp->len != 4)
940 state_pl->opts[CD_SUBNETMASK] = NULL;
941 else {
942 bcopy(doptp->value, &subnet,
943 sizeof (struct in_addr));
944 dprintf("%s: Setting netmask to: %s\n", s_n,
945 inet_ntoa(subnet));
946 subnet.s_addr = ntohl(subnet.s_addr);
947 ipv4_setnetmask(&subnet);
948 }
949 }
950
951 /*
952 * Set default IP TTL. Nice, but not required.
953 */
954 if (state_pl->opts[CD_IPTTL] != NULL) {
955 doptp = state_pl->opts[CD_IPTTL];
956 if (doptp->len > 1)
957 state_pl->opts[CD_IPTTL] = NULL;
958 else {
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);
963 }
964 }
965
966 /*
967 * Set default router. Not required, although we'll know soon
968 * enough...
969 */
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;
974 } else {
975 if ((hp = (uint8_t *)bkmem_alloc(
976 mac_get_hdr_len())) == NULL) {
977 errno = ENOMEM;
978 return (-1);
979 }
980 items = doptp->len / sizeof (struct in_addr);
981 tp = doptp->value;
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,
987 mac_get_hdr_len(),
988 mac_get_arp_timeout());
989 }
990 if (fnd)
991 break;
992 dprintf(
993 "%s: Warning: Router %s is unreachable.\n",
994 s_n, inet_ntoa(defr));
995 tp += sizeof (struct in_addr);
996 }
997 bkmem_free((char *)hp, mac_get_hdr_len());
998
999 /*
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.
1004 */
1005 if (!fnd) {
1006 dprintf(
1007 "%s: Warning: Using default router: %s\n",
1008 s_n, inet_ntoa(savr));
1009 defr.s_addr = savr.s_addr;
1010 }
1011 /* ipv4_route expects network order IP addresses */
1012 (void) ipv4_route(IPV4_ADD_ROUTE, RT_DEFAULT, NULL,
1013 &defr);
1014 }
1015 }
1016
1017 /*
1018 * Set hostname. Not required.
1019 */
1020 if (state_pl->opts[CD_HOSTNAME] != NULL) {
1021 doptp = state_pl->opts[CD_HOSTNAME];
1022 i = doptp->len;
1023 if (i > MAXHOSTNAMELEN)
1024 i = MAXHOSTNAMELEN;
1025 bcopy(doptp->value, hostname, i);
1026 hostname[i] = '\0';
1027 (void) sethostname(hostname, i);
1028 if (boothowto & RB_VERBOSE)
1029 printf("%s: Hostname is %s\n", s_n, hostname);
1030 }
1031
1032 /*
1033 * We don't care about the lease time.... We can't enforce it anyway.
1034 */
1035 return (0);
1036 }
1037
1038 /*
1039 * Convert the DHCPACK into a pure ASCII boot property for use by the kernel
1040 * later in the boot process.
1041 */
1042 static void
create_bootpresponse_bprop(PKT_LIST * pl)1043 create_bootpresponse_bprop(PKT_LIST *pl)
1044 {
1045 uint_t buflen;
1046
1047 if (pl == NULL || bootp_response != NULL)
1048 return;
1049
1050 buflen = (pl->len * 2) + 1; /* extra space for null (1) */
1051 if ((bootp_response = bkmem_alloc(buflen)) == NULL)
1052 return;
1053
1054 if (octet_to_hexascii((uint8_t *)pl->pkt, pl->len, bootp_response,
1055 &buflen) != 0) {
1056 bkmem_free(bootp_response, (pl->len * 2) + 1);
1057 bootp_response = NULL;
1058 }
1059
1060 #if defined(__sparc)
1061 prom_create_encoded_prop("bootp-response", pl->pkt, pl->len,
1062 ENCODE_BYTES);
1063 #endif /* __sparc */
1064 }
1065
1066 /*
1067 * Examines /chosen node for "bootp-response" property. If it exists, this
1068 * property is the DHCPACK cached there by the PROM's DHCP implementation.
1069 *
1070 * If cache_present is B_TRUE, we simply return B_TRUE if the property exists
1071 * w/o decoding it. If cache_present is B_FALSE, we decode the packet and
1072 * use it as our state packet for the jump to BOUND mode. Note that it's good
1073 * enough that the cache exists w/o validation for determining if that's what
1074 * the user intended.
1075 *
1076 * We'll short-circuit the DHCP exchange by accepting this packet. We build a
1077 * PKT_LIST structure, and copy the bootp-response property value into a
1078 * PKT buffer we allocated. We then scan the PKT for options, and then
1079 * set state_pl to point to it.
1080 *
1081 * Returns B_TRUE if a packet was cached (and was processed correctly), false
1082 * otherwise. The caller needs to make the state change from SELECTING to
1083 * BOUND upon a B_TRUE return from this function.
1084 */
1085 int
prom_cached_reply(int cache_present)1086 prom_cached_reply(int cache_present)
1087 {
1088 PKT_LIST *pl;
1089 int len;
1090 pnode_t chosen;
1091 char *prop = PROM_BOOT_CACHED;
1092
1093 chosen = prom_finddevice("/chosen");
1094 if (chosen == OBP_NONODE || chosen == OBP_BADNODE)
1095 chosen = prom_nextnode((pnode_t)0); /* root node */
1096
1097 if ((len = prom_getproplen(chosen, prop)) <= 0)
1098 return (B_FALSE);
1099
1100 if (cache_present)
1101 return (B_TRUE);
1102
1103 if (((pl = (PKT_LIST *)bkmem_zalloc(sizeof (PKT_LIST))) == NULL) ||
1104 ((pl->pkt = (PKT *)bkmem_zalloc(len)) == NULL)) {
1105 errno = ENOMEM;
1106 if (pl != NULL)
1107 bkmem_free((char *)pl, sizeof (PKT_LIST));
1108 return (B_FALSE);
1109 }
1110
1111 (void) prom_getprop(chosen, prop, (caddr_t)pl->pkt);
1112
1113 pl->len = len;
1114
1115 if (dhcp_options_scan(pl, B_TRUE) != 0) {
1116 /* garbled packet */
1117 bkmem_free((char *)pl->pkt, pl->len);
1118 bkmem_free((char *)pl, sizeof (PKT_LIST));
1119 return (B_FALSE);
1120 }
1121
1122 state_pl = pl;
1123 return (B_TRUE);
1124 }
1125
1126 /*
1127 * Perform DHCP to acquire what we used to get using rarp/bootparams.
1128 * Returns 0 for success, nonzero otherwise.
1129 *
1130 * DHCP client state machine.
1131 */
1132 int
dhcp(void)1133 dhcp(void)
1134 {
1135 int err = 0;
1136 int done = B_FALSE;
1137
1138 dhcp_buf_size = mac_get_mtu();
1139 dhcp_snd_bufp = (PKT *)bkmem_alloc(dhcp_buf_size);
1140 dhcp_rcv_bufp = (PKT *)bkmem_alloc(dhcp_buf_size);
1141
1142 if (dhcp_snd_bufp == NULL || dhcp_rcv_bufp == NULL) {
1143 if (dhcp_snd_bufp != NULL)
1144 bkmem_free((char *)dhcp_snd_bufp, dhcp_buf_size);
1145 if (dhcp_rcv_bufp != NULL)
1146 bkmem_free((char *)dhcp_rcv_bufp, dhcp_buf_size);
1147 errno = ENOMEM;
1148 return (-1);
1149 }
1150
1151 while (err == 0 && !done) {
1152 switch (dhcp_state) {
1153 case INIT:
1154
1155 s_n = "INIT";
1156 if (prom_cached_reply(B_FALSE)) {
1157 dprintf("%s: Using PROM cached BOOT reply...\n",
1158 s_n);
1159 dhcp_state = BOUND;
1160 } else {
1161 dhcp_state = SELECTING;
1162 dhcp_start_time = prom_gettime();
1163 }
1164 break;
1165
1166 case SELECTING:
1167
1168 s_n = "SELECTING";
1169 err = dhcp_selecting();
1170 switch (err) {
1171 case 0:
1172 dhcp_state = REQUESTING;
1173 break;
1174 case DHCP_NO_DATA:
1175 dprintf(
1176 "%s: No DHCP response after %d tries.\n",
1177 s_n, DHCP_RETRIES);
1178 break;
1179 default:
1180 /* major network problems */
1181 dprintf("%s: Network transaction failed: %d\n",
1182 s_n, err);
1183 break;
1184 }
1185 break;
1186
1187 case REQUESTING:
1188
1189 s_n = "REQUESTING";
1190 err = dhcp_requesting();
1191 switch (err) {
1192 case 0:
1193 dhcp_state = BOUND;
1194 break;
1195 case DHCP_NO_DATA:
1196 dprintf("%s: Request timed out.\n", s_n);
1197 dhcp_state = SELECTING;
1198 err = 0;
1199 (void) sleep(10);
1200 break;
1201 default:
1202 /* major network problems */
1203 dprintf("%s: Network transaction failed: %d\n",
1204 s_n, err);
1205 break;
1206 }
1207 break;
1208
1209 case BOUND:
1210
1211 /*
1212 * We just "give up" if bound state fails.
1213 */
1214 s_n = "BOUND";
1215 if ((err = dhcp_bound()) == 0) {
1216 dhcp_state = CONFIGURED;
1217 }
1218 break;
1219
1220 case CONFIGURED:
1221 s_n = "CONFIGURED";
1222 create_bootpresponse_bprop(state_pl);
1223 done = B_TRUE;
1224 break;
1225 }
1226 }
1227 bkmem_free((char *)dhcp_snd_bufp, dhcp_buf_size);
1228 bkmem_free((char *)dhcp_rcv_bufp, dhcp_buf_size);
1229
1230 return (err);
1231 }
1232
1233 /*
1234 * Returns a copy of the DHCP-supplied value of the parameter requested
1235 * by code.
1236 */
1237 boolean_t
dhcp_getinfo(uchar_t optcat,uint16_t code,uint16_t optsize,void * value,size_t * vallenp)1238 dhcp_getinfo(uchar_t optcat, uint16_t code, uint16_t optsize, void *value,
1239 size_t *vallenp)
1240 {
1241 size_t len = *vallenp;
1242
1243 if (dhcp_getinfo_pl(state_pl, optcat, code,
1244 optsize, value, &len)) {
1245
1246 if (len <= *vallenp) {
1247 *vallenp = len;
1248 return (B_TRUE);
1249 }
1250 }
1251
1252 return (B_FALSE);
1253 }
1254
1255 /*
1256 * Sets the clientid option.
1257 */
1258 void
dhcp_set_client_id(uint8_t * clientid,uint8_t clientid_len)1259 dhcp_set_client_id(uint8_t *clientid, uint8_t clientid_len)
1260 {
1261 bcopy(clientid, dhcp_clientid, clientid_len);
1262 dhcp_clientid_len = clientid_len;
1263 }
1264