8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / cmd-inet / sbin / dhcpagent / packet.c
bloba763530436049789bfcee174c11ff0c5faf2301a
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #include <string.h>
27 #include <sys/types.h>
28 #include <stdlib.h>
29 #include <dhcpmsg.h>
30 #include <stddef.h>
31 #include <assert.h>
32 #include <search.h>
33 #include <alloca.h>
34 #include <limits.h>
35 #include <stropts.h>
36 #include <netinet/dhcp6.h>
37 #include <arpa/inet.h>
38 #include <sys/sysmacros.h>
39 #include <sys/sockio.h>
40 #include <inet/ip6_asp.h>
42 #include "states.h"
43 #include "interface.h"
44 #include "agent.h"
45 #include "packet.h"
46 #include "util.h"
48 int v6_sock_fd = -1;
49 int v4_sock_fd = -1;
51 const in6_addr_t ipv6_all_dhcp_relay_and_servers = {
52 0xff, 0x02, 0x00, 0x00,
53 0x00, 0x00, 0x00, 0x00,
54 0x00, 0x00, 0x00, 0x00,
55 0x00, 0x01, 0x00, 0x02
59 * We have our own version of this constant because dhcpagent is compiled with
60 * -lxnet.
62 const in6_addr_t my_in6addr_any = IN6ADDR_ANY_INIT;
64 static void retransmit(iu_tq_t *, void *);
65 static void next_retransmission(dhcp_smach_t *, boolean_t, boolean_t);
66 static boolean_t send_pkt_internal(dhcp_smach_t *);
69 * pkt_send_type(): returns an integer representing the packet's type; only
70 * for use with outbound packets.
72 * input: dhcp_pkt_t *: the packet to examine
73 * output: uchar_t: the packet type (0 if unknown)
76 static uchar_t
77 pkt_send_type(const dhcp_pkt_t *dpkt)
79 const uchar_t *option;
81 if (dpkt->pkt_isv6)
82 return (((const dhcpv6_message_t *)dpkt->pkt)->d6m_msg_type);
85 * this is a little dirty but it should get the job done.
86 * assumes that the type is in the statically allocated part
87 * of the options field.
90 option = dpkt->pkt->options;
91 for (;;) {
92 if (*option == CD_PAD) {
93 option++;
94 continue;
96 if (*option == CD_END ||
97 option + 2 - dpkt->pkt->options >=
98 sizeof (dpkt->pkt->options))
99 return (0);
100 if (*option == CD_DHCP_TYPE)
101 break;
102 option++;
103 option += *option + 1;
106 return (option[2]);
110 * pkt_recv_type(): returns an integer representing the packet's type; only
111 * for use with inbound packets.
113 * input: dhcp_pkt_t *: the packet to examine
114 * output: uchar_t: the packet type (0 if unknown)
117 uchar_t
118 pkt_recv_type(const PKT_LIST *plp)
120 if (plp->isv6)
121 return (((const dhcpv6_message_t *)plp->pkt)->d6m_msg_type);
122 else if (plp->opts[CD_DHCP_TYPE] != NULL)
123 return (plp->opts[CD_DHCP_TYPE]->value[0]);
124 else
125 return (0);
129 * pkt_get_xid(): returns transaction ID from a DHCP packet.
131 * input: const PKT *: the packet to examine
132 * output: uint_t: the transaction ID (0 if unknown)
135 uint_t
136 pkt_get_xid(const PKT *pkt, boolean_t isv6)
138 if (pkt == NULL)
139 return (0);
140 if (isv6)
141 return (DHCPV6_GET_TRANSID((const dhcpv6_message_t *)pkt));
142 else
143 return (pkt->xid);
147 * init_pkt(): initializes and returns a packet of a given type
149 * input: dhcp_smach_t *: the state machine that will send the packet
150 * uchar_t: the packet type (DHCP message type)
151 * output: dhcp_pkt_t *: a pointer to the initialized packet; may be NULL
154 dhcp_pkt_t *
155 init_pkt(dhcp_smach_t *dsmp, uchar_t type)
157 dhcp_pkt_t *dpkt = &dsmp->dsm_send_pkt;
158 dhcp_lif_t *lif = dsmp->dsm_lif;
159 dhcp_pif_t *pif = lif->lif_pif;
160 uint_t mtu = lif->lif_max;
161 uint32_t xid;
162 boolean_t isv6;
164 dpkt->pkt_isv6 = isv6 = pif->pif_isv6;
167 * Since multiple dhcp leases may be maintained over the same pif
168 * (e.g. "hme0" and "hme0:1"), make sure the xid is unique.
170 * Note that transaction ID zero is intentionally never assigned.
171 * That's used to represent "no ID." Also note that transaction IDs
172 * are only 24 bits long in DHCPv6.
175 do {
176 xid = mrand48();
177 if (isv6)
178 xid &= 0xFFFFFF;
179 } while (xid == 0 ||
180 lookup_smach_by_xid(xid, NULL, dpkt->pkt_isv6) != NULL);
182 if (isv6) {
183 dhcpv6_message_t *v6;
185 if (mtu != dpkt->pkt_max_len &&
186 (v6 = realloc(dpkt->pkt, mtu)) != NULL) {
187 /* LINTED: alignment known to be correct */
188 dpkt->pkt = (PKT *)v6;
189 dpkt->pkt_max_len = mtu;
192 if (sizeof (*v6) > dpkt->pkt_max_len) {
193 dhcpmsg(MSG_ERR, "init_pkt: cannot allocate v6 pkt: %u",
194 mtu);
195 return (NULL);
198 v6 = (dhcpv6_message_t *)dpkt->pkt;
199 dpkt->pkt_cur_len = sizeof (*v6);
201 (void) memset(v6, 0, dpkt->pkt_max_len);
203 v6->d6m_msg_type = type;
204 DHCPV6_SET_TRANSID(v6, xid);
206 if (dsmp->dsm_cidlen > 0 &&
207 add_pkt_opt(dpkt, DHCPV6_OPT_CLIENTID, dsmp->dsm_cid,
208 dsmp->dsm_cidlen) == NULL) {
209 dhcpmsg(MSG_WARNING,
210 "init_pkt: cannot insert client ID");
211 return (NULL);
214 /* For v6, time starts with the creation of a transaction */
215 dsmp->dsm_neg_hrtime = gethrtime();
216 dsmp->dsm_newstart_monosec = monosec();
217 } else {
218 static uint8_t bootmagic[] = BOOTMAGIC;
219 PKT *v4;
221 if (mtu != dpkt->pkt_max_len &&
222 (v4 = realloc(dpkt->pkt, mtu)) != NULL) {
223 dpkt->pkt = v4;
224 dpkt->pkt_max_len = mtu;
227 if (offsetof(PKT, options) > dpkt->pkt_max_len) {
228 dhcpmsg(MSG_ERR, "init_pkt: cannot allocate v4 pkt: %u",
229 mtu);
230 return (NULL);
233 v4 = dpkt->pkt;
234 dpkt->pkt_cur_len = offsetof(PKT, options);
236 (void) memset(v4, 0, dpkt->pkt_max_len);
237 (void) memcpy(v4->cookie, bootmagic, sizeof (bootmagic));
238 if (pif->pif_hwlen <= sizeof (v4->chaddr)) {
239 v4->hlen = pif->pif_hwlen;
240 (void) memcpy(v4->chaddr, pif->pif_hwaddr,
241 pif->pif_hwlen);
242 } else {
244 * The mac address does not fit in the chaddr
245 * field, thus it can not be sent to the server,
246 * thus server can not unicast the reply. Per
247 * RFC 2131 4.4.1, client can set this bit in
248 * DISCOVER/REQUEST. If the client is already
249 * in a bound state, do not set this bit, as it
250 * can respond to unicast responses from server
251 * using the 'ciaddr' address.
253 if (type == DISCOVER || (type == REQUEST &&
254 !is_bound_state(dsmp->dsm_state)))
255 v4->flags = htons(BCAST_MASK);
258 v4->xid = xid;
259 v4->op = BOOTREQUEST;
260 v4->htype = pif->pif_hwtype;
262 if (add_pkt_opt(dpkt, CD_DHCP_TYPE, &type, 1) == NULL) {
263 dhcpmsg(MSG_WARNING,
264 "init_pkt: cannot set DHCP packet type");
265 return (NULL);
268 if (dsmp->dsm_cidlen > 0 &&
269 add_pkt_opt(dpkt, CD_CLIENT_ID, dsmp->dsm_cid,
270 dsmp->dsm_cidlen) == NULL) {
271 dhcpmsg(MSG_WARNING,
272 "init_pkt: cannot insert client ID");
273 return (NULL);
277 return (dpkt);
281 * remove_pkt_opt(): removes the first instance of an option from a dhcp_pkt_t
283 * input: dhcp_pkt_t *: the packet to remove the option from
284 * uint_t: the type of option being added
285 * output: boolean_t: B_TRUE on success, B_FALSE on failure
286 * note: currently does not work with DHCPv6 suboptions, or to remove
287 * arbitrary option instances.
290 boolean_t
291 remove_pkt_opt(dhcp_pkt_t *dpkt, uint_t opt_type)
293 uchar_t *raw_pkt, *raw_end, *next;
294 uint_t len;
296 raw_pkt = (uchar_t *)dpkt->pkt;
297 raw_end = raw_pkt + dpkt->pkt_cur_len;
298 if (dpkt->pkt_isv6) {
299 dhcpv6_option_t d6o;
301 raw_pkt += sizeof (dhcpv6_message_t);
303 opt_type = htons(opt_type);
304 while (raw_pkt + sizeof (d6o) <= raw_end) {
305 (void) memcpy(&d6o, raw_pkt, sizeof (d6o));
306 len = ntohs(d6o.d6o_len) + sizeof (d6o);
307 if (len > raw_end - raw_pkt)
308 break;
309 next = raw_pkt + len;
310 if (d6o.d6o_code == opt_type) {
311 if (next < raw_end) {
312 (void) memmove(raw_pkt, next,
313 raw_end - next);
315 dpkt->pkt_cur_len -= len;
316 return (B_TRUE);
318 raw_pkt = next;
320 } else {
321 uchar_t *pstart, *padrun;
323 raw_pkt += offsetof(PKT, options);
324 pstart = raw_pkt;
326 if (opt_type == CD_END || opt_type == CD_PAD)
327 return (B_FALSE);
329 padrun = NULL;
330 while (raw_pkt + 1 <= raw_end) {
331 if (*raw_pkt == CD_END)
332 break;
333 if (*raw_pkt == CD_PAD) {
334 if (padrun == NULL)
335 padrun = raw_pkt;
336 raw_pkt++;
337 continue;
339 if (raw_pkt + 2 > raw_end)
340 break;
341 len = raw_pkt[1];
342 if (len > raw_end - raw_pkt || len < 2)
343 break;
344 next = raw_pkt + len;
345 if (*raw_pkt == opt_type) {
346 if (next < raw_end) {
347 int toadd = (4 + ((next-pstart)&3) -
348 ((raw_pkt-pstart)&3)) & 3;
349 int torem = 4 - toadd;
351 if (torem != 4 && padrun != NULL &&
352 (raw_pkt - padrun) >= torem) {
353 raw_pkt -= torem;
354 dpkt->pkt_cur_len -= torem;
355 } else if (toadd > 0) {
356 (void) memset(raw_pkt, CD_PAD,
357 toadd);
358 raw_pkt += toadd;
359 /* max is not an issue here */
360 dpkt->pkt_cur_len += toadd;
362 if (raw_pkt != next) {
363 (void) memmove(raw_pkt, next,
364 raw_end - next);
367 dpkt->pkt_cur_len -= len;
368 return (B_TRUE);
370 padrun = NULL;
371 raw_pkt = next;
374 return (B_FALSE);
378 * update_v6opt_len(): updates the length field of a DHCPv6 option.
380 * input: dhcpv6_option_t *: option to be updated
381 * int: number of octets to add or subtract
382 * output: boolean_t: B_TRUE on success, B_FALSE on failure
385 boolean_t
386 update_v6opt_len(dhcpv6_option_t *opt, int adjust)
388 dhcpv6_option_t optval;
390 (void) memcpy(&optval, opt, sizeof (optval));
391 adjust += ntohs(optval.d6o_len);
392 if (adjust < 0 || adjust > UINT16_MAX) {
393 return (B_FALSE);
394 } else {
395 optval.d6o_len = htons(adjust);
396 (void) memcpy(opt, &optval, sizeof (optval));
397 return (B_TRUE);
402 * add_pkt_opt(): adds an option to a dhcp_pkt_t
404 * input: dhcp_pkt_t *: the packet to add the option to
405 * uint_t: the type of option being added
406 * const void *: the value of that option
407 * uint_t: the length of the value of the option
408 * output: void *: pointer to the option that was added, or NULL on failure.
411 void *
412 add_pkt_opt(dhcp_pkt_t *dpkt, uint_t opt_type, const void *opt_val,
413 uint_t opt_len)
415 uchar_t *raw_pkt;
416 int req_len;
417 void *optr;
419 raw_pkt = (uchar_t *)dpkt->pkt;
420 optr = raw_pkt + dpkt->pkt_cur_len;
421 if (dpkt->pkt_isv6) {
422 dhcpv6_option_t d6o;
424 req_len = opt_len + sizeof (d6o);
426 if (dpkt->pkt_cur_len + req_len > dpkt->pkt_max_len) {
427 dhcpmsg(MSG_WARNING,
428 "add_pkt_opt: not enough room for v6 option %u in "
429 "packet (%u + %u > %u)", opt_type,
430 dpkt->pkt_cur_len, req_len, dpkt->pkt_max_len);
431 return (NULL);
433 d6o.d6o_code = htons(opt_type);
434 d6o.d6o_len = htons(opt_len);
435 (void) memcpy(&raw_pkt[dpkt->pkt_cur_len], &d6o, sizeof (d6o));
436 dpkt->pkt_cur_len += sizeof (d6o);
437 if (opt_len > 0) {
438 (void) memcpy(&raw_pkt[dpkt->pkt_cur_len], opt_val,
439 opt_len);
440 dpkt->pkt_cur_len += opt_len;
442 } else {
443 req_len = opt_len + 2; /* + 2 for code & length bytes */
445 /* CD_END and CD_PAD options don't have a length field */
446 if (opt_type == CD_END || opt_type == CD_PAD) {
447 req_len = 1;
448 } else if (opt_val == NULL) {
449 dhcpmsg(MSG_ERROR, "add_pkt_opt: option type %d is "
450 "missing required value", opt_type);
451 return (NULL);
454 if ((dpkt->pkt_cur_len + req_len) > dpkt->pkt_max_len) {
455 dhcpmsg(MSG_WARNING,
456 "add_pkt_opt: not enough room for v4 option %u in "
457 "packet", opt_type);
458 return (NULL);
461 raw_pkt[dpkt->pkt_cur_len++] = opt_type;
463 if (req_len > 1) {
464 raw_pkt[dpkt->pkt_cur_len++] = opt_len;
465 if (opt_len > 0) {
466 (void) memcpy(&raw_pkt[dpkt->pkt_cur_len],
467 opt_val, opt_len);
468 dpkt->pkt_cur_len += opt_len;
472 return (optr);
476 * add_pkt_subopt(): adds an option to a dhcp_pkt_t option. DHCPv6-specific,
477 * but could be extended to IPv4 DHCP if necessary. Assumes
478 * that if the parent isn't a top-level option, the caller
479 * will adjust any upper-level options recursively using
480 * update_v6opt_len.
482 * input: dhcp_pkt_t *: the packet to add the suboption to
483 * dhcpv6_option_t *: the start of the option to that should contain
484 * it (parent)
485 * uint_t: the type of suboption being added
486 * const void *: the value of that option
487 * uint_t: the length of the value of the option
488 * output: void *: pointer to the suboption that was added, or NULL on
489 * failure.
492 void *
493 add_pkt_subopt(dhcp_pkt_t *dpkt, dhcpv6_option_t *parentopt, uint_t opt_type,
494 const void *opt_val, uint_t opt_len)
496 uchar_t *raw_pkt;
497 int req_len;
498 void *optr;
499 dhcpv6_option_t d6o;
500 uchar_t *optend;
501 int olen;
503 if (!dpkt->pkt_isv6)
504 return (NULL);
506 raw_pkt = (uchar_t *)dpkt->pkt;
507 req_len = opt_len + sizeof (d6o);
509 if (dpkt->pkt_cur_len + req_len > dpkt->pkt_max_len) {
510 dhcpmsg(MSG_WARNING,
511 "add_pkt_subopt: not enough room for v6 suboption %u in "
512 "packet (%u + %u > %u)", opt_type,
513 dpkt->pkt_cur_len, req_len, dpkt->pkt_max_len);
514 return (NULL);
518 * Update the parent option to include room for this option,
519 * and compute the insertion point.
521 (void) memcpy(&d6o, parentopt, sizeof (d6o));
522 olen = ntohs(d6o.d6o_len);
523 optend = (uchar_t *)(parentopt + 1) + olen;
524 olen += req_len;
525 d6o.d6o_len = htons(olen);
526 (void) memcpy(parentopt, &d6o, sizeof (d6o));
529 * If there's anything at the end to move, then move it. Also bump up
530 * the packet size.
532 if (optend < raw_pkt + dpkt->pkt_cur_len) {
533 (void) memmove(optend + req_len, optend,
534 (raw_pkt + dpkt->pkt_cur_len) - optend);
536 dpkt->pkt_cur_len += req_len;
539 * Now format the suboption and add it in.
541 optr = optend;
542 d6o.d6o_code = htons(opt_type);
543 d6o.d6o_len = htons(opt_len);
544 (void) memcpy(optend, &d6o, sizeof (d6o));
545 if (opt_len > 0)
546 (void) memcpy(optend + sizeof (d6o), opt_val, opt_len);
547 return (optr);
551 * add_pkt_opt16(): adds an option with a 16-bit value to a dhcp_pkt_t
553 * input: dhcp_pkt_t *: the packet to add the option to
554 * uint_t: the type of option being added
555 * uint16_t: the value of that option
556 * output: void *: pointer to the option that was added, or NULL on failure.
559 void *
560 add_pkt_opt16(dhcp_pkt_t *dpkt, uint_t opt_type, uint16_t opt_value)
562 return (add_pkt_opt(dpkt, opt_type, &opt_value, 2));
566 * add_pkt_opt32(): adds an option with a 32-bit value to a dhcp_pkt_t
568 * input: dhcp_pkt_t *: the packet to add the option to
569 * uint_t: the type of option being added
570 * uint32_t: the value of that option
571 * output: void *: pointer to the option that was added, or NULL on failure.
574 void *
575 add_pkt_opt32(dhcp_pkt_t *dpkt, uint_t opt_type, uint32_t opt_value)
577 return (add_pkt_opt(dpkt, opt_type, &opt_value, 4));
581 * add_pkt_prl(): adds the parameter request option to the packet
583 * input: dhcp_pkt_t *: the packet to add the option to
584 * dhcp_smach_t *: state machine with request option
585 * output: void *: pointer to the option that was added, or NULL on failure.
588 void *
589 add_pkt_prl(dhcp_pkt_t *dpkt, dhcp_smach_t *dsmp)
591 uint_t len;
593 if (dsmp->dsm_prllen == 0)
594 return (0);
596 if (dpkt->pkt_isv6) {
597 uint16_t *prl;
600 * RFC 3315 requires that we include the option, even if we
601 * have nothing to request.
603 if (dsmp->dsm_prllen == 0)
604 prl = NULL;
605 else
606 prl = alloca(dsmp->dsm_prllen * sizeof (uint16_t));
608 for (len = 0; len < dsmp->dsm_prllen; len++)
609 prl[len] = htons(dsmp->dsm_prl[len]);
610 return (add_pkt_opt(dpkt, DHCPV6_OPT_ORO, prl,
611 len * sizeof (uint16_t)));
612 } else {
613 uint8_t *prl = alloca(dsmp->dsm_prllen);
615 for (len = 0; len < dsmp->dsm_prllen; len++)
616 prl[len] = dsmp->dsm_prl[len];
617 return (add_pkt_opt(dpkt, CD_REQUEST_LIST, prl, len));
622 * add_pkt_lif(): Adds CD_REQUESTED_IP_ADDR (IPv4 DHCP) or IA_NA and IAADDR
623 * (DHCPv6) options to the packet to represent the given LIF.
625 * input: dhcp_pkt_t *: the packet to add the options to
626 * dhcp_lif_t *: the logical interface to represent
627 * int: status code (unused for IPv4 DHCP)
628 * const char *: message to include with status option, or NULL
629 * output: boolean_t: B_TRUE on success, B_FALSE on failure
632 boolean_t
633 add_pkt_lif(dhcp_pkt_t *dpkt, dhcp_lif_t *lif, int status, const char *msg)
635 if (lif->lif_pif->pif_isv6) {
636 dhcp_smach_t *dsmp;
637 dhcpv6_message_t *d6m;
638 dhcpv6_ia_na_t d6in;
639 dhcpv6_iaaddr_t d6ia;
640 uint32_t iaid;
641 uint16_t *statusopt;
642 dhcpv6_option_t *d6o, *d6so;
643 uint_t olen;
646 * Currently, we support just one IAID related to the primary
647 * LIF on the state machine.
649 dsmp = lif->lif_lease->dl_smach;
650 iaid = dsmp->dsm_lif->lif_iaid;
651 iaid = htonl(iaid);
653 d6m = (dhcpv6_message_t *)dpkt->pkt;
656 * Find or create the IA_NA needed for this LIF. If we
657 * supported IA_TA, we'd check the IFF_TEMPORARY bit here.
659 d6o = NULL;
660 while ((d6o = dhcpv6_find_option(d6m + 1,
661 dpkt->pkt_cur_len - sizeof (*d6m), d6o, DHCPV6_OPT_IA_NA,
662 &olen)) != NULL) {
663 if (olen < sizeof (d6in))
664 continue;
665 (void) memcpy(&d6in, d6o, sizeof (d6in));
666 if (d6in.d6in_iaid == iaid)
667 break;
669 if (d6o == NULL) {
670 d6in.d6in_iaid = iaid;
671 d6in.d6in_t1 = 0;
672 d6in.d6in_t2 = 0;
673 d6o = add_pkt_opt(dpkt, DHCPV6_OPT_IA_NA,
674 (dhcpv6_option_t *)&d6in + 1,
675 sizeof (d6in) - sizeof (*d6o));
676 if (d6o == NULL)
677 return (B_FALSE);
681 * Now add the IAADDR suboption for this LIF. No need to
682 * search here, as we know that this is unique.
684 d6ia.d6ia_addr = lif->lif_v6addr;
687 * For Release and Decline, we zero out the lifetime. For
688 * Renew and Rebind, we report the original time as the
689 * preferred and valid lifetimes.
691 if (d6m->d6m_msg_type == DHCPV6_MSG_RELEASE ||
692 d6m->d6m_msg_type == DHCPV6_MSG_DECLINE) {
693 d6ia.d6ia_preflife = 0;
694 d6ia.d6ia_vallife = 0;
695 } else {
696 d6ia.d6ia_preflife = htonl(lif->lif_preferred.dt_start);
697 d6ia.d6ia_vallife = htonl(lif->lif_expire.dt_start);
699 d6so = add_pkt_subopt(dpkt, d6o, DHCPV6_OPT_IAADDR,
700 (dhcpv6_option_t *)&d6ia + 1,
701 sizeof (d6ia) - sizeof (*d6o));
702 if (d6so == NULL)
703 return (B_FALSE);
706 * Add a status code suboption to the IAADDR to tell the server
707 * why we're declining the address. Note that we must manually
708 * update the enclosing IA_NA, as add_pkt_subopt doesn't know
709 * how to do that.
711 if (status != DHCPV6_STAT_SUCCESS || msg != NULL) {
712 olen = sizeof (*statusopt) +
713 (msg == NULL ? 0 : strlen(msg));
714 statusopt = alloca(olen);
715 *statusopt = htons(status);
716 if (msg != NULL) {
717 (void) memcpy((char *)(statusopt + 1), msg,
718 olen - sizeof (*statusopt));
720 d6so = add_pkt_subopt(dpkt, d6so,
721 DHCPV6_OPT_STATUS_CODE, statusopt, olen);
722 if (d6so != NULL) {
724 * Update for length of suboption header and
725 * suboption contents.
727 (void) update_v6opt_len(d6o, sizeof (*d6so) +
728 olen);
731 } else {
733 * For DECLINE, we need to add the CD_REQUESTED_IP_ADDR option.
734 * In all other cases (RELEASE and REQUEST), we need to set
735 * ciadr.
737 if (pkt_send_type(dpkt) == DECLINE) {
738 if (!add_pkt_opt32(dpkt, CD_REQUESTED_IP_ADDR,
739 lif->lif_addr))
740 return (B_FALSE);
741 } else {
742 dpkt->pkt->ciaddr.s_addr = lif->lif_addr;
746 * It's not too worrisome if the message fails to fit in the
747 * packet. The result will still be valid.
749 if (msg != NULL)
750 (void) add_pkt_opt(dpkt, CD_MESSAGE, msg,
751 strlen(msg) + 1);
753 return (B_TRUE);
757 * free_pkt_entry(): frees a packet list list entry
759 * input: PKT_LIST *: the packet list entry to free
760 * output: void
762 void
763 free_pkt_entry(PKT_LIST *plp)
765 if (plp != NULL) {
766 free(plp->pkt);
767 free(plp);
772 * free_pkt_list(): frees an entire packet list
774 * input: PKT_LIST **: the packet list to free
775 * output: void
778 void
779 free_pkt_list(PKT_LIST **head)
781 PKT_LIST *plp;
783 while ((plp = *head) != NULL) {
784 remque(plp);
785 free_pkt_entry(plp);
790 * send_pkt_internal(): sends a packet out on an interface
792 * input: dhcp_smach_t *: the state machine with a packet to send
793 * output: boolean_t: B_TRUE if the packet is sent, B_FALSE otherwise
796 static boolean_t
797 send_pkt_internal(dhcp_smach_t *dsmp)
799 ssize_t n_bytes;
800 dhcp_lif_t *lif = dsmp->dsm_lif;
801 dhcp_pkt_t *dpkt = &dsmp->dsm_send_pkt;
802 uchar_t ptype = pkt_send_type(dpkt);
803 const char *pkt_name;
804 struct iovec iov;
805 struct msghdr msg;
806 struct cmsghdr *cmsg;
807 struct in6_pktinfo *ipi6;
808 boolean_t ismcast;
809 int msgtype;
812 * Timer should not be running at the point we go to send a packet.
814 if (dsmp->dsm_retrans_timer != -1) {
815 dhcpmsg(MSG_CRIT, "send_pkt_internal: unexpected retransmit "
816 "timer on %s", dsmp->dsm_name);
817 stop_pkt_retransmission(dsmp);
820 pkt_name = pkt_type_to_string(ptype, dpkt->pkt_isv6);
823 * if needed, schedule a retransmission timer, then attempt to
824 * send the packet. if we fail, then log the error. our
825 * return value should indicate whether or not we were
826 * successful in sending the request, independent of whether
827 * we could schedule a timer.
830 if (dsmp->dsm_send_timeout != 0) {
831 if ((dsmp->dsm_retrans_timer = iu_schedule_timer_ms(tq,
832 dsmp->dsm_send_timeout, retransmit, dsmp)) == -1)
833 dhcpmsg(MSG_WARNING, "send_pkt_internal: cannot "
834 "schedule retransmit timer for %s packet",
835 pkt_name);
836 else
837 hold_smach(dsmp);
840 if (dpkt->pkt_isv6) {
841 hrtime_t delta;
844 * Convert current time into centiseconds since transaction
845 * started. This is what DHCPv6 expects to see in the Elapsed
846 * Time option.
848 delta = (gethrtime() - dsmp->dsm_neg_hrtime) /
849 (NANOSEC / 100);
850 if (delta > DHCPV6_FOREVER)
851 delta = DHCPV6_FOREVER;
852 (void) remove_pkt_opt(dpkt, DHCPV6_OPT_ELAPSED_TIME);
853 (void) add_pkt_opt16(dpkt, DHCPV6_OPT_ELAPSED_TIME,
854 htons(delta));
855 } else {
857 * set the `pkt->secs' field depending on the type of packet.
858 * it should be zero, except in the following cases:
860 * DISCOVER: set to the number of seconds since we started
861 * trying to obtain a lease.
863 * INFORM: set to the number of seconds since we started
864 * trying to get configuration parameters.
866 * REQUEST: if in the REQUESTING state, then same value as
867 * DISCOVER, otherwise the number of seconds
868 * since we started trying to obtain a lease.
870 * we also set `dsm_newstart_monosec', to the time we sent a
871 * REQUEST or DISCOVER packet, so we know the lease start
872 * time (the DISCOVER case is for handling BOOTP servers).
875 switch (ptype) {
877 case DISCOVER:
878 dsmp->dsm_newstart_monosec = monosec();
879 dsmp->dsm_disc_secs = dsmp->dsm_newstart_monosec -
880 hrtime_to_monosec(dsmp->dsm_neg_hrtime);
881 dpkt->pkt->secs = htons(dsmp->dsm_disc_secs);
882 break;
884 case INFORM:
885 dpkt->pkt->secs = htons(monosec() -
886 hrtime_to_monosec(dsmp->dsm_neg_hrtime));
887 break;
889 case REQUEST:
890 dsmp->dsm_newstart_monosec = monosec();
892 if (dsmp->dsm_state == REQUESTING) {
893 dpkt->pkt->secs = htons(dsmp->dsm_disc_secs);
894 break;
897 dpkt->pkt->secs = htons(monosec() -
898 hrtime_to_monosec(dsmp->dsm_neg_hrtime));
899 break;
901 default:
902 dpkt->pkt->secs = htons(0);
903 break;
907 if (dpkt->pkt_isv6) {
908 struct sockaddr_in6 sin6;
910 (void) memset(&iov, 0, sizeof (iov));
911 iov.iov_base = dpkt->pkt;
912 iov.iov_len = dpkt->pkt_cur_len;
914 (void) memset(&msg, 0, sizeof (msg));
915 msg.msg_name = &dsmp->dsm_send_dest.v6;
916 msg.msg_namelen = sizeof (struct sockaddr_in6);
917 msg.msg_iov = &iov;
918 msg.msg_iovlen = 1;
921 * If the address that's requested cannot be reached, then fall
922 * back to the multcast address.
924 if (IN6_IS_ADDR_MULTICAST(&dsmp->dsm_send_dest.v6.sin6_addr)) {
925 ismcast = B_TRUE;
926 } else {
927 struct dstinforeq dinfo;
928 struct strioctl str;
930 ismcast = B_FALSE;
931 (void) memset(&dinfo, 0, sizeof (dinfo));
932 dinfo.dir_daddr = dsmp->dsm_send_dest.v6.sin6_addr;
933 str.ic_cmd = SIOCGDSTINFO;
934 str.ic_timout = 0;
935 str.ic_len = sizeof (dinfo);
936 str.ic_dp = (char *)&dinfo;
937 if (ioctl(v6_sock_fd, I_STR, &str) == -1) {
938 dhcpmsg(MSG_ERR,
939 "send_pkt_internal: ioctl SIOCGDSTINFO");
940 } else if (!dinfo.dir_dreachable) {
941 char abuf[INET6_ADDRSTRLEN];
943 dhcpmsg(MSG_DEBUG, "send_pkt_internal: %s is "
944 "not reachable; using multicast instead",
945 inet_ntop(AF_INET6, &dinfo.dir_daddr, abuf,
946 sizeof (abuf)));
947 sin6 = dsmp->dsm_send_dest.v6;
948 sin6.sin6_addr =
949 ipv6_all_dhcp_relay_and_servers;
950 msg.msg_name = &sin6;
951 ismcast = B_TRUE;
956 * Make room for our ancillary data option as well as a dummy
957 * option used by CMSG_NXTHDR.
959 msg.msg_controllen = sizeof (*cmsg) + _MAX_ALIGNMENT +
960 sizeof (*ipi6) + _MAX_ALIGNMENT + sizeof (*cmsg);
961 msg.msg_control = alloca(msg.msg_controllen);
962 cmsg = CMSG_FIRSTHDR(&msg);
963 cmsg->cmsg_level = IPPROTO_IPV6;
964 cmsg->cmsg_type = IPV6_PKTINFO;
965 /* LINTED: alignment */
966 ipi6 = (struct in6_pktinfo *)CMSG_DATA(cmsg);
967 if (ismcast)
968 ipi6->ipi6_addr = lif->lif_v6addr;
969 else
970 ipi6->ipi6_addr = my_in6addr_any;
971 if (lif->lif_pif->pif_under_ipmp)
972 ipi6->ipi6_ifindex = lif->lif_pif->pif_grindex;
973 else
974 ipi6->ipi6_ifindex = lif->lif_pif->pif_index;
975 cmsg->cmsg_len = (char *)(ipi6 + 1) - (char *)cmsg;
978 * Now correct the control message length.
980 cmsg = CMSG_NXTHDR(&msg, cmsg);
981 msg.msg_controllen = (char *)cmsg - (char *)msg.msg_control;
983 n_bytes = sendmsg(v6_sock_fd, &msg, 0);
984 } else {
985 n_bytes = sendto(lif->lif_sock_ip_fd, dpkt->pkt,
986 dpkt->pkt_cur_len, 0,
987 (struct sockaddr *)&dsmp->dsm_send_dest.v4,
988 sizeof (struct sockaddr_in));
991 if (n_bytes != dpkt->pkt_cur_len) {
992 msgtype = (n_bytes == -1) ? MSG_ERR : MSG_WARNING;
993 if (dsmp->dsm_retrans_timer == -1)
994 dhcpmsg(msgtype, "send_pkt_internal: cannot send "
995 "%s packet to server", pkt_name);
996 else
997 dhcpmsg(msgtype, "send_pkt_internal: cannot send "
998 "%s packet to server (will retry in %u seconds)",
999 pkt_name, dsmp->dsm_send_timeout / MILLISEC);
1000 return (B_FALSE);
1003 dhcpmsg(MSG_VERBOSE, "sent %s xid %x packet out %s", pkt_name,
1004 pkt_get_xid(dpkt->pkt, dpkt->pkt_isv6), dsmp->dsm_name);
1006 dsmp->dsm_packet_sent++;
1007 dsmp->dsm_sent++;
1008 return (B_TRUE);
1012 * send_pkt(): sends a packet out
1014 * input: dhcp_smach_t *: the state machine sending the packet
1015 * dhcp_pkt_t *: the packet to send out
1016 * in_addr_t: the destination IP address for the packet
1017 * stop_func_t *: a pointer to function to indicate when to stop
1018 * retransmitting the packet (if NULL, packet is
1019 * not retransmitted)
1020 * output: boolean_t: B_TRUE if the packet was sent, B_FALSE otherwise
1023 boolean_t
1024 send_pkt(dhcp_smach_t *dsmp, dhcp_pkt_t *dpkt, in_addr_t dest,
1025 stop_func_t *stop)
1028 * packets must be at least sizeof (PKT) or they may be dropped
1029 * by routers. pad out the packet in this case.
1032 dpkt->pkt_cur_len = MAX(dpkt->pkt_cur_len, sizeof (PKT));
1034 dsmp->dsm_packet_sent = 0;
1036 (void) memset(&dsmp->dsm_send_dest.v4, 0,
1037 sizeof (dsmp->dsm_send_dest.v4));
1038 dsmp->dsm_send_dest.v4.sin_addr.s_addr = dest;
1039 dsmp->dsm_send_dest.v4.sin_family = AF_INET;
1040 dsmp->dsm_send_dest.v4.sin_port = htons(IPPORT_BOOTPS);
1041 dsmp->dsm_send_stop_func = stop;
1044 * TODO: dispose of this gruesome assumption (there's no real
1045 * technical gain from doing so, but it would be cleaner)
1048 assert(dpkt == &dsmp->dsm_send_pkt);
1051 * clear out any packets which had been previously received
1052 * but not pulled off of the recv_packet queue.
1055 free_pkt_list(&dsmp->dsm_recv_pkt_list);
1057 if (stop == NULL)
1058 dsmp->dsm_send_timeout = 0; /* prevents retransmissions */
1059 else
1060 next_retransmission(dsmp, B_TRUE, B_FALSE);
1062 return (send_pkt_internal(dsmp));
1066 * send_pkt_v6(): sends a DHCPv6 packet out
1068 * input: dhcp_smach_t *: the state machine sending the packet
1069 * dhcp_pkt_t *: the packet to send out
1070 * in6_addr_t: the destination IPv6 address for the packet
1071 * stop_func_t *: a pointer to function to indicate when to stop
1072 * retransmitting the packet (if NULL, packet is
1073 * not retransmitted)
1074 * uint_t: Initial Retransmit Timer value
1075 * uint_t: Maximum Retransmit Timer value, zero if none
1076 * output: boolean_t: B_TRUE if the packet was sent, B_FALSE otherwise
1079 boolean_t
1080 send_pkt_v6(dhcp_smach_t *dsmp, dhcp_pkt_t *dpkt, in6_addr_t dest,
1081 stop_func_t *stop, uint_t irt, uint_t mrt)
1083 dsmp->dsm_packet_sent = 0;
1085 (void) memset(&dsmp->dsm_send_dest.v6, 0,
1086 sizeof (dsmp->dsm_send_dest.v6));
1087 dsmp->dsm_send_dest.v6.sin6_addr = dest;
1088 dsmp->dsm_send_dest.v6.sin6_family = AF_INET6;
1089 dsmp->dsm_send_dest.v6.sin6_port = htons(IPPORT_DHCPV6S);
1090 dsmp->dsm_send_stop_func = stop;
1093 * TODO: dispose of this gruesome assumption (there's no real
1094 * technical gain from doing so, but it would be cleaner)
1097 assert(dpkt == &dsmp->dsm_send_pkt);
1100 * clear out any packets which had been previously received
1101 * but not pulled off of the recv_packet queue.
1104 free_pkt_list(&dsmp->dsm_recv_pkt_list);
1106 if (stop == NULL) {
1107 dsmp->dsm_send_timeout = 0; /* prevents retransmissions */
1108 } else {
1109 dsmp->dsm_send_timeout = irt;
1110 dsmp->dsm_send_tcenter = mrt;
1112 * This is quite ugly, but RFC 3315 section 17.1.2 requires
1113 * that the RAND value for the very first retransmission of a
1114 * Solicit message is strictly greater than zero.
1116 next_retransmission(dsmp, B_TRUE,
1117 pkt_send_type(dpkt) == DHCPV6_MSG_SOLICIT);
1120 return (send_pkt_internal(dsmp));
1124 * retransmit(): retransmits the current packet on an interface
1126 * input: iu_tq_t *: unused
1127 * void *: the dhcp_smach_t * (state machine) sending a packet
1128 * output: void
1131 /* ARGSUSED */
1132 static void
1133 retransmit(iu_tq_t *tqp, void *arg)
1135 dhcp_smach_t *dsmp = arg;
1137 dsmp->dsm_retrans_timer = -1;
1139 if (!verify_smach(dsmp))
1140 return;
1143 * Check the callback to see if we should keep sending retransmissions.
1144 * Compute the next retransmission time first, so that the callback can
1145 * cap the value if need be. (Required for DHCPv6 Confirm messages.)
1147 * Hold the state machine across the callback so that the called
1148 * function can remove the state machine from the system without
1149 * disturbing the string used subsequently for verbose logging. The
1150 * Release function destroys the state machine when the retry count
1151 * expires.
1154 next_retransmission(dsmp, B_FALSE, B_FALSE);
1155 hold_smach(dsmp);
1156 if (dsmp->dsm_send_stop_func(dsmp, dsmp->dsm_packet_sent)) {
1157 dhcpmsg(MSG_VERBOSE, "retransmit: time to stop on %s",
1158 dsmp->dsm_name);
1159 } else {
1160 dhcpmsg(MSG_VERBOSE, "retransmit: sending another on %s",
1161 dsmp->dsm_name);
1162 (void) send_pkt_internal(dsmp);
1164 release_smach(dsmp);
1168 * stop_pkt_retransmission(): stops retransmission of last sent packet
1170 * input: dhcp_smach_t *: the state machine to stop retransmission on
1171 * output: void
1174 void
1175 stop_pkt_retransmission(dhcp_smach_t *dsmp)
1177 if (dsmp->dsm_retrans_timer != -1 &&
1178 iu_cancel_timer(tq, dsmp->dsm_retrans_timer, NULL) == 1) {
1179 dhcpmsg(MSG_VERBOSE, "stop_pkt_retransmission: stopped on %s",
1180 dsmp->dsm_name);
1181 dsmp->dsm_retrans_timer = -1;
1182 release_smach(dsmp);
1187 * retransmit_now(): force a packet retransmission right now. Used only with
1188 * the DHCPv6 UseMulticast status code. Use with caution;
1189 * triggered retransmissions can cause packet storms.
1191 * input: dhcp_smach_t *: the state machine to force retransmission on
1192 * output: void
1195 void
1196 retransmit_now(dhcp_smach_t *dsmp)
1198 stop_pkt_retransmission(dsmp);
1199 (void) send_pkt_internal(dsmp);
1203 * alloc_pkt_entry(): Allocates a packet list entry with a given data area
1204 * size.
1206 * input: size_t: size of data area for packet
1207 * boolean_t: B_TRUE for IPv6
1208 * output: PKT_LIST *: allocated packet list entry
1211 PKT_LIST *
1212 alloc_pkt_entry(size_t psize, boolean_t isv6)
1214 PKT_LIST *plp;
1216 if ((plp = calloc(1, sizeof (*plp))) == NULL ||
1217 (plp->pkt = malloc(psize)) == NULL) {
1218 free(plp);
1219 plp = NULL;
1220 } else {
1221 plp->len = psize;
1222 plp->isv6 = isv6;
1225 return (plp);
1229 * sock_recvpkt(): read from the given socket into an allocated buffer and
1230 * handles any ancillary data options.
1232 * input: int: file descriptor to read
1233 * PKT_LIST *: allocated buffer
1234 * output: ssize_t: number of bytes read, or -1 on error
1237 static ssize_t
1238 sock_recvpkt(int fd, PKT_LIST *plp)
1240 struct iovec iov;
1241 struct msghdr msg;
1242 int64_t ctrl[8192 / sizeof (int64_t)];
1243 ssize_t msglen;
1245 (void) memset(&iov, 0, sizeof (iov));
1246 iov.iov_base = (caddr_t)plp->pkt;
1247 iov.iov_len = plp->len;
1249 (void) memset(&msg, 0, sizeof (msg));
1250 msg.msg_name = &plp->pktfrom;
1251 msg.msg_namelen = sizeof (plp->pktfrom);
1252 msg.msg_iov = &iov;
1253 msg.msg_iovlen = 1;
1254 msg.msg_control = ctrl;
1255 msg.msg_controllen = sizeof (ctrl);
1257 if ((msglen = recvmsg(fd, &msg, 0)) != -1) {
1258 struct cmsghdr *cmsg;
1260 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
1261 cmsg = CMSG_NXTHDR(&msg, cmsg)) {
1262 struct sockaddr_in *sinp;
1263 struct sockaddr_in6 *sin6;
1264 struct in6_pktinfo *ipi6;
1266 switch (cmsg->cmsg_level) {
1267 case IPPROTO_IP:
1268 switch (cmsg->cmsg_type) {
1269 case IP_RECVDSTADDR:
1270 sinp = (struct sockaddr_in *)
1271 &plp->pktto;
1272 sinp->sin_family = AF_INET;
1273 (void) memcpy(&sinp->sin_addr.s_addr,
1274 CMSG_DATA(cmsg),
1275 sizeof (ipaddr_t));
1276 break;
1278 case IP_RECVIF:
1279 (void) memcpy(&plp->ifindex,
1280 CMSG_DATA(cmsg), sizeof (uint_t));
1281 break;
1283 break;
1285 case IPPROTO_IPV6:
1286 switch (cmsg->cmsg_type) {
1287 case IPV6_PKTINFO:
1288 /* LINTED: alignment */
1289 ipi6 = (struct in6_pktinfo *)
1290 CMSG_DATA(cmsg);
1291 sin6 = (struct sockaddr_in6 *)
1292 &plp->pktto;
1293 sin6->sin6_family = AF_INET6;
1294 (void) memcpy(&sin6->sin6_addr,
1295 &ipi6->ipi6_addr,
1296 sizeof (ipi6->ipi6_addr));
1297 (void) memcpy(&plp->ifindex,
1298 &ipi6->ipi6_ifindex,
1299 sizeof (uint_t));
1300 break;
1305 return (msglen);
1309 * recv_pkt(): receives a single DHCP packet on a given file descriptor.
1311 * input: int: the file descriptor to receive the packet from
1312 * int: the maximum packet size to allow
1313 * boolean_t: B_TRUE for IPv6
1314 * output: PKT_LIST *: the received packet
1317 PKT_LIST *
1318 recv_pkt(int fd, int mtu, boolean_t isv6)
1320 PKT_LIST *plp;
1321 ssize_t retval;
1323 if ((plp = alloc_pkt_entry(mtu, isv6)) == NULL) {
1324 dhcpmsg(MSG_ERROR,
1325 "recv_pkt: allocation failure; dropped packet");
1326 return (NULL);
1329 retval = sock_recvpkt(fd, plp);
1330 if (retval == -1) {
1331 dhcpmsg(MSG_ERR, "recv_pkt: recvfrom v%d failed, dropped",
1332 isv6 ? 6 : 4);
1333 goto failure;
1336 plp->len = retval;
1338 if (isv6) {
1339 if (retval < sizeof (dhcpv6_message_t)) {
1340 dhcpmsg(MSG_WARNING, "recv_pkt: runt message");
1341 goto failure;
1343 } else {
1344 switch (dhcp_options_scan(plp, B_TRUE)) {
1346 case DHCP_WRONG_MSG_TYPE:
1347 dhcpmsg(MSG_WARNING,
1348 "recv_pkt: unexpected DHCP message");
1349 goto failure;
1351 case DHCP_GARBLED_MSG_TYPE:
1352 dhcpmsg(MSG_WARNING,
1353 "recv_pkt: garbled DHCP message type");
1354 goto failure;
1356 case DHCP_BAD_OPT_OVLD:
1357 dhcpmsg(MSG_WARNING, "recv_pkt: bad option overload");
1358 goto failure;
1360 case 0:
1361 break;
1363 default:
1364 dhcpmsg(MSG_WARNING,
1365 "recv_pkt: packet corrupted, dropped");
1366 goto failure;
1369 return (plp);
1371 failure:
1372 free_pkt_entry(plp);
1373 return (NULL);
1377 * pkt_v4_match(): check if a given DHCPv4 message type is in a given set
1379 * input: uchar_t: packet type
1380 * dhcp_message_type_t: bit-wise OR of DHCP_P* values.
1381 * output: boolean_t: B_TRUE if packet type is in the set
1384 boolean_t
1385 pkt_v4_match(uchar_t type, dhcp_message_type_t match_type)
1388 * note: the ordering here allows direct indexing of the table
1389 * based on the RFC2131 packet type value passed in.
1392 static dhcp_message_type_t type_map[] = {
1393 DHCP_PUNTYPED, DHCP_PDISCOVER, DHCP_POFFER, DHCP_PREQUEST,
1394 DHCP_PDECLINE, DHCP_PACK, DHCP_PNAK, DHCP_PRELEASE,
1395 DHCP_PINFORM
1398 if (type < (sizeof (type_map) / sizeof (*type_map)))
1399 return ((type_map[type] & match_type) ? B_TRUE : B_FALSE);
1400 else
1401 return (B_FALSE);
1405 * pkt_smach_enqueue(): enqueue a packet on a given state machine
1407 * input: dhcp_smach_t: state machine
1408 * PKT_LIST *: packet to enqueue
1409 * output: none
1412 void
1413 pkt_smach_enqueue(dhcp_smach_t *dsmp, PKT_LIST *plp)
1415 dhcpmsg(MSG_VERBOSE, "pkt_smach_enqueue: received %s %s packet on %s",
1416 pkt_type_to_string(pkt_recv_type(plp), dsmp->dsm_isv6),
1417 dsmp->dsm_isv6 ? "v6" : "v4", dsmp->dsm_name);
1419 /* add to front of list */
1420 insque(plp, &dsmp->dsm_recv_pkt_list);
1424 * next_retransmission(): computes the number of seconds until the next
1425 * retransmission, based on the algorithms in RFCs 2131
1426 * 3315.
1428 * input: dhcp_smach_t *: state machine that needs a new timer
1429 * boolean_t: B_TRUE if this is the first time sending the message
1430 * boolean_t: B_TRUE for positive RAND values only (RFC 3315 17.1.2)
1431 * output: none
1434 static void
1435 next_retransmission(dhcp_smach_t *dsmp, boolean_t first_send,
1436 boolean_t positive_only)
1438 uint32_t timeout_ms;
1440 if (dsmp->dsm_isv6) {
1441 double randval;
1444 * The RFC specifies 0 to 10% jitter for the initial
1445 * solicitation, and plus or minus 10% jitter for all others.
1446 * This works out to 100 milliseconds on the shortest timer we
1447 * use.
1449 if (positive_only)
1450 randval = drand48() / 10.0;
1451 else
1452 randval = (drand48() - 0.5) / 5.0;
1454 /* The RFC specifies doubling *after* the first transmission */
1455 timeout_ms = dsmp->dsm_send_timeout;
1456 if (!first_send)
1457 timeout_ms *= 2;
1458 timeout_ms += (int)(randval * dsmp->dsm_send_timeout);
1460 /* This checks the MRT (maximum retransmission time) */
1461 if (dsmp->dsm_send_tcenter != 0 &&
1462 timeout_ms > dsmp->dsm_send_tcenter) {
1463 timeout_ms = dsmp->dsm_send_tcenter +
1464 (uint_t)(randval * dsmp->dsm_send_tcenter);
1467 dsmp->dsm_send_timeout = timeout_ms;
1468 } else {
1469 if (dsmp->dsm_state == RENEWING ||
1470 dsmp->dsm_state == REBINDING) {
1471 monosec_t mono;
1473 timeout_ms = dsmp->dsm_state == RENEWING ?
1474 dsmp->dsm_leases->dl_t2.dt_start :
1475 dsmp->dsm_leases->dl_lifs->lif_expire.dt_start;
1476 timeout_ms += dsmp->dsm_curstart_monosec;
1477 mono = monosec();
1478 if (mono > timeout_ms)
1479 timeout_ms = 0;
1480 else
1481 timeout_ms -= mono;
1482 timeout_ms *= MILLISEC / 2;
1483 } else {
1485 * Start at 4, and increase by a factor of 2 up to 64.
1487 if (first_send) {
1488 timeout_ms = 4 * MILLISEC;
1489 } else {
1490 timeout_ms = MIN(dsmp->dsm_send_tcenter << 1,
1491 64 * MILLISEC);
1495 dsmp->dsm_send_tcenter = timeout_ms;
1498 * At each iteration, jitter the timeout by some fraction of a
1499 * second.
1501 dsmp->dsm_send_timeout = timeout_ms +
1502 ((lrand48() % (2 * MILLISEC)) - MILLISEC);
1507 * dhcp_ip_default(): open and bind the default IP sockets used for I/O and
1508 * interface control.
1510 * input: none
1511 * output: B_TRUE on success
1514 boolean_t
1515 dhcp_ip_default(void)
1517 int on = 1;
1519 if ((v4_sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
1520 dhcpmsg(MSG_ERR,
1521 "dhcp_ip_default: unable to create IPv4 socket");
1522 return (B_FALSE);
1525 if (setsockopt(v4_sock_fd, IPPROTO_IP, IP_RECVDSTADDR, &on,
1526 sizeof (on)) == -1) {
1527 dhcpmsg(MSG_ERR,
1528 "dhcp_ip_default: unable to enable IP_RECVDSTADDR");
1529 return (B_FALSE);
1532 if (setsockopt(v4_sock_fd, IPPROTO_IP, IP_RECVIF, &on, sizeof (on)) ==
1533 -1) {
1534 dhcpmsg(MSG_ERR,
1535 "dhcp_ip_default: unable to enable IP_RECVIF");
1536 return (B_FALSE);
1539 if (!bind_sock(v4_sock_fd, IPPORT_BOOTPC, INADDR_ANY)) {
1540 dhcpmsg(MSG_ERROR,
1541 "dhcp_ip_default: unable to bind IPv4 socket to port %d",
1542 IPPORT_BOOTPC);
1543 return (B_FALSE);
1546 if (iu_register_event(eh, v4_sock_fd, POLLIN, dhcp_acknak_global,
1547 NULL) == -1) {
1548 dhcpmsg(MSG_WARNING, "dhcp_ip_default: cannot register to "
1549 "receive IPv4 broadcasts");
1550 return (B_FALSE);
1553 if ((v6_sock_fd = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) {
1554 dhcpmsg(MSG_ERR,
1555 "dhcp_ip_default: unable to create IPv6 socket");
1556 return (B_FALSE);
1559 if (setsockopt(v6_sock_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on,
1560 sizeof (on)) == -1) {
1561 dhcpmsg(MSG_ERR,
1562 "dhcp_ip_default: unable to enable IPV6_RECVPKTINFO");
1563 return (B_FALSE);
1566 if (!bind_sock_v6(v6_sock_fd, IPPORT_DHCPV6C, NULL)) {
1567 dhcpmsg(MSG_ERROR,
1568 "dhcp_ip_default: unable to bind IPv6 socket to port %d",
1569 IPPORT_DHCPV6C);
1570 return (B_FALSE);
1573 if (iu_register_event(eh, v6_sock_fd, POLLIN, dhcp_acknak_global,
1574 NULL) == -1) {
1575 dhcpmsg(MSG_WARNING, "dhcp_ip_default: cannot register to "
1576 "receive IPv6 packets");
1577 return (B_FALSE);
1580 return (B_TRUE);