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 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 #include <sys/types.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>
43 #include "interface.h"
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
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)
77 pkt_send_type(const dhcp_pkt_t
*dpkt
)
79 const uchar_t
*option
;
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
;
92 if (*option
== CD_PAD
) {
96 if (*option
== CD_END
||
97 option
+ 2 - dpkt
->pkt
->options
>=
98 sizeof (dpkt
->pkt
->options
))
100 if (*option
== CD_DHCP_TYPE
)
103 option
+= *option
+ 1;
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)
118 pkt_recv_type(const PKT_LIST
*plp
)
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]);
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)
136 pkt_get_xid(const PKT
*pkt
, boolean_t isv6
)
141 return (DHCPV6_GET_TRANSID((const dhcpv6_message_t
*)pkt
));
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
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
;
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.
180 lookup_smach_by_xid(xid
, NULL
, dpkt
->pkt_isv6
) != NULL
);
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",
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
) {
210 "init_pkt: cannot insert client ID");
214 /* For v6, time starts with the creation of a transaction */
215 dsmp
->dsm_neg_hrtime
= gethrtime();
216 dsmp
->dsm_newstart_monosec
= monosec();
218 static uint8_t bootmagic
[] = BOOTMAGIC
;
221 if (mtu
!= dpkt
->pkt_max_len
&&
222 (v4
= realloc(dpkt
->pkt
, mtu
)) != NULL
) {
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",
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
,
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
);
259 v4
->op
= BOOTREQUEST
;
260 v4
->htype
= pif
->pif_hwtype
;
262 if (add_pkt_opt(dpkt
, CD_DHCP_TYPE
, &type
, 1) == NULL
) {
264 "init_pkt: cannot set DHCP packet type");
268 if (dsmp
->dsm_cidlen
> 0 &&
269 add_pkt_opt(dpkt
, CD_CLIENT_ID
, dsmp
->dsm_cid
,
270 dsmp
->dsm_cidlen
) == NULL
) {
272 "init_pkt: cannot insert client ID");
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.
291 remove_pkt_opt(dhcp_pkt_t
*dpkt
, uint_t opt_type
)
293 uchar_t
*raw_pkt
, *raw_end
, *next
;
296 raw_pkt
= (uchar_t
*)dpkt
->pkt
;
297 raw_end
= raw_pkt
+ dpkt
->pkt_cur_len
;
298 if (dpkt
->pkt_isv6
) {
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
)
309 next
= raw_pkt
+ len
;
310 if (d6o
.d6o_code
== opt_type
) {
311 if (next
< raw_end
) {
312 (void) memmove(raw_pkt
, next
,
315 dpkt
->pkt_cur_len
-= len
;
321 uchar_t
*pstart
, *padrun
;
323 raw_pkt
+= offsetof(PKT
, options
);
326 if (opt_type
== CD_END
|| opt_type
== CD_PAD
)
330 while (raw_pkt
+ 1 <= raw_end
) {
331 if (*raw_pkt
== CD_END
)
333 if (*raw_pkt
== CD_PAD
) {
339 if (raw_pkt
+ 2 > raw_end
)
342 if (len
> raw_end
- raw_pkt
|| len
< 2)
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
) {
354 dpkt
->pkt_cur_len
-= torem
;
355 } else if (toadd
> 0) {
356 (void) memset(raw_pkt
, CD_PAD
,
359 /* max is not an issue here */
360 dpkt
->pkt_cur_len
+= toadd
;
362 if (raw_pkt
!= next
) {
363 (void) memmove(raw_pkt
, next
,
367 dpkt
->pkt_cur_len
-= len
;
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
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
) {
395 optval
.d6o_len
= htons(adjust
);
396 (void) memcpy(opt
, &optval
, sizeof (optval
));
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.
412 add_pkt_opt(dhcp_pkt_t
*dpkt
, uint_t opt_type
, const void *opt_val
,
419 raw_pkt
= (uchar_t
*)dpkt
->pkt
;
420 optr
= raw_pkt
+ dpkt
->pkt_cur_len
;
421 if (dpkt
->pkt_isv6
) {
424 req_len
= opt_len
+ sizeof (d6o
);
426 if (dpkt
->pkt_cur_len
+ req_len
> dpkt
->pkt_max_len
) {
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
);
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
);
438 (void) memcpy(&raw_pkt
[dpkt
->pkt_cur_len
], opt_val
,
440 dpkt
->pkt_cur_len
+= opt_len
;
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
) {
448 } else if (opt_val
== NULL
) {
449 dhcpmsg(MSG_ERROR
, "add_pkt_opt: option type %d is "
450 "missing required value", opt_type
);
454 if ((dpkt
->pkt_cur_len
+ req_len
) > dpkt
->pkt_max_len
) {
456 "add_pkt_opt: not enough room for v4 option %u in "
461 raw_pkt
[dpkt
->pkt_cur_len
++] = opt_type
;
464 raw_pkt
[dpkt
->pkt_cur_len
++] = opt_len
;
466 (void) memcpy(&raw_pkt
[dpkt
->pkt_cur_len
],
468 dpkt
->pkt_cur_len
+= opt_len
;
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
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
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
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
)
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
) {
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
);
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
;
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
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.
542 d6o
.d6o_code
= htons(opt_type
);
543 d6o
.d6o_len
= htons(opt_len
);
544 (void) memcpy(optend
, &d6o
, sizeof (d6o
));
546 (void) memcpy(optend
+ sizeof (d6o
), opt_val
, opt_len
);
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.
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.
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.
589 add_pkt_prl(dhcp_pkt_t
*dpkt
, dhcp_smach_t
*dsmp
)
593 if (dsmp
->dsm_prllen
== 0)
596 if (dpkt
->pkt_isv6
) {
600 * RFC 3315 requires that we include the option, even if we
601 * have nothing to request.
603 if (dsmp
->dsm_prllen
== 0)
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)));
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
633 add_pkt_lif(dhcp_pkt_t
*dpkt
, dhcp_lif_t
*lif
, int status
, const char *msg
)
635 if (lif
->lif_pif
->pif_isv6
) {
637 dhcpv6_message_t
*d6m
;
639 dhcpv6_iaaddr_t d6ia
;
642 dhcpv6_option_t
*d6o
, *d6so
;
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
;
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.
660 while ((d6o
= dhcpv6_find_option(d6m
+ 1,
661 dpkt
->pkt_cur_len
- sizeof (*d6m
), d6o
, DHCPV6_OPT_IA_NA
,
663 if (olen
< sizeof (d6in
))
665 (void) memcpy(&d6in
, d6o
, sizeof (d6in
));
666 if (d6in
.d6in_iaid
== iaid
)
670 d6in
.d6in_iaid
= iaid
;
673 d6o
= add_pkt_opt(dpkt
, DHCPV6_OPT_IA_NA
,
674 (dhcpv6_option_t
*)&d6in
+ 1,
675 sizeof (d6in
) - sizeof (*d6o
));
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;
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
));
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
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
);
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
);
724 * Update for length of suboption header and
725 * suboption contents.
727 (void) update_v6opt_len(d6o
, sizeof (*d6so
) +
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
737 if (pkt_send_type(dpkt
) == DECLINE
) {
738 if (!add_pkt_opt32(dpkt
, CD_REQUESTED_IP_ADDR
,
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.
750 (void) add_pkt_opt(dpkt
, CD_MESSAGE
, msg
,
757 * free_pkt_entry(): frees a packet list list entry
759 * input: PKT_LIST *: the packet list entry to free
763 free_pkt_entry(PKT_LIST
*plp
)
772 * free_pkt_list(): frees an entire packet list
774 * input: PKT_LIST **: the packet list to free
779 free_pkt_list(PKT_LIST
**head
)
783 while ((plp
= *head
) != NULL
) {
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
797 send_pkt_internal(dhcp_smach_t
*dsmp
)
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
;
806 struct cmsghdr
*cmsg
;
807 struct in6_pktinfo
*ipi6
;
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",
840 if (dpkt
->pkt_isv6
) {
844 * Convert current time into centiseconds since transaction
845 * started. This is what DHCPv6 expects to see in the Elapsed
848 delta
= (gethrtime() - dsmp
->dsm_neg_hrtime
) /
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
,
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).
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
);
885 dpkt
->pkt
->secs
= htons(monosec() -
886 hrtime_to_monosec(dsmp
->dsm_neg_hrtime
));
890 dsmp
->dsm_newstart_monosec
= monosec();
892 if (dsmp
->dsm_state
== REQUESTING
) {
893 dpkt
->pkt
->secs
= htons(dsmp
->dsm_disc_secs
);
897 dpkt
->pkt
->secs
= htons(monosec() -
898 hrtime_to_monosec(dsmp
->dsm_neg_hrtime
));
902 dpkt
->pkt
->secs
= htons(0);
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
);
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
)) {
927 struct dstinforeq dinfo
;
931 (void) memset(&dinfo
, 0, sizeof (dinfo
));
932 dinfo
.dir_daddr
= dsmp
->dsm_send_dest
.v6
.sin6_addr
;
933 str
.ic_cmd
= SIOCGDSTINFO
;
935 str
.ic_len
= sizeof (dinfo
);
936 str
.ic_dp
= (char *)&dinfo
;
937 if (ioctl(v6_sock_fd
, I_STR
, &str
) == -1) {
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
,
947 sin6
= dsmp
->dsm_send_dest
.v6
;
949 ipv6_all_dhcp_relay_and_servers
;
950 msg
.msg_name
= &sin6
;
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
);
968 ipi6
->ipi6_addr
= lif
->lif_v6addr
;
970 ipi6
->ipi6_addr
= my_in6addr_any
;
971 if (lif
->lif_pif
->pif_under_ipmp
)
972 ipi6
->ipi6_ifindex
= lif
->lif_pif
->pif_grindex
;
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);
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
);
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
);
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
++;
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
1024 send_pkt(dhcp_smach_t
*dsmp
, dhcp_pkt_t
*dpkt
, in_addr_t dest
,
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
);
1058 dsmp
->dsm_send_timeout
= 0; /* prevents retransmissions */
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
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
);
1107 dsmp
->dsm_send_timeout
= 0; /* prevents retransmissions */
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
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
))
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
1154 next_retransmission(dsmp
, B_FALSE
, B_FALSE
);
1156 if (dsmp
->dsm_send_stop_func(dsmp
, dsmp
->dsm_packet_sent
)) {
1157 dhcpmsg(MSG_VERBOSE
, "retransmit: time to stop on %s",
1160 dhcpmsg(MSG_VERBOSE
, "retransmit: sending another on %s",
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
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",
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
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
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
1212 alloc_pkt_entry(size_t psize
, boolean_t isv6
)
1216 if ((plp
= calloc(1, sizeof (*plp
))) == NULL
||
1217 (plp
->pkt
= malloc(psize
)) == NULL
) {
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
1238 sock_recvpkt(int fd
, PKT_LIST
*plp
)
1242 int64_t ctrl
[8192 / sizeof (int64_t)];
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
);
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
) {
1268 switch (cmsg
->cmsg_type
) {
1269 case IP_RECVDSTADDR
:
1270 sinp
= (struct sockaddr_in
*)
1272 sinp
->sin_family
= AF_INET
;
1273 (void) memcpy(&sinp
->sin_addr
.s_addr
,
1279 (void) memcpy(&plp
->ifindex
,
1280 CMSG_DATA(cmsg
), sizeof (uint_t
));
1286 switch (cmsg
->cmsg_type
) {
1288 /* LINTED: alignment */
1289 ipi6
= (struct in6_pktinfo
*)
1291 sin6
= (struct sockaddr_in6
*)
1293 sin6
->sin6_family
= AF_INET6
;
1294 (void) memcpy(&sin6
->sin6_addr
,
1296 sizeof (ipi6
->ipi6_addr
));
1297 (void) memcpy(&plp
->ifindex
,
1298 &ipi6
->ipi6_ifindex
,
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
1318 recv_pkt(int fd
, int mtu
, boolean_t isv6
)
1323 if ((plp
= alloc_pkt_entry(mtu
, isv6
)) == NULL
) {
1325 "recv_pkt: allocation failure; dropped packet");
1329 retval
= sock_recvpkt(fd
, plp
);
1331 dhcpmsg(MSG_ERR
, "recv_pkt: recvfrom v%d failed, dropped",
1339 if (retval
< sizeof (dhcpv6_message_t
)) {
1340 dhcpmsg(MSG_WARNING
, "recv_pkt: runt message");
1344 switch (dhcp_options_scan(plp
, B_TRUE
)) {
1346 case DHCP_WRONG_MSG_TYPE
:
1347 dhcpmsg(MSG_WARNING
,
1348 "recv_pkt: unexpected DHCP message");
1351 case DHCP_GARBLED_MSG_TYPE
:
1352 dhcpmsg(MSG_WARNING
,
1353 "recv_pkt: garbled DHCP message type");
1356 case DHCP_BAD_OPT_OVLD
:
1357 dhcpmsg(MSG_WARNING
, "recv_pkt: bad option overload");
1364 dhcpmsg(MSG_WARNING
,
1365 "recv_pkt: packet corrupted, dropped");
1372 free_pkt_entry(plp
);
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
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
,
1398 if (type
< (sizeof (type_map
) / sizeof (*type_map
)))
1399 return ((type_map
[type
] & match_type
) ? B_TRUE
: 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
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
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)
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
) {
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
1450 randval
= drand48() / 10.0;
1452 randval
= (drand48() - 0.5) / 5.0;
1454 /* The RFC specifies doubling *after* the first transmission */
1455 timeout_ms
= dsmp
->dsm_send_timeout
;
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
;
1469 if (dsmp
->dsm_state
== RENEWING
||
1470 dsmp
->dsm_state
== REBINDING
) {
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
;
1478 if (mono
> timeout_ms
)
1482 timeout_ms
*= MILLISEC
/ 2;
1485 * Start at 4, and increase by a factor of 2 up to 64.
1488 timeout_ms
= 4 * MILLISEC
;
1490 timeout_ms
= MIN(dsmp
->dsm_send_tcenter
<< 1,
1495 dsmp
->dsm_send_tcenter
= timeout_ms
;
1498 * At each iteration, jitter the timeout by some fraction of a
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.
1511 * output: B_TRUE on success
1515 dhcp_ip_default(void)
1519 if ((v4_sock_fd
= socket(AF_INET
, SOCK_DGRAM
, 0)) == -1) {
1521 "dhcp_ip_default: unable to create IPv4 socket");
1525 if (setsockopt(v4_sock_fd
, IPPROTO_IP
, IP_RECVDSTADDR
, &on
,
1526 sizeof (on
)) == -1) {
1528 "dhcp_ip_default: unable to enable IP_RECVDSTADDR");
1532 if (setsockopt(v4_sock_fd
, IPPROTO_IP
, IP_RECVIF
, &on
, sizeof (on
)) ==
1535 "dhcp_ip_default: unable to enable IP_RECVIF");
1539 if (!bind_sock(v4_sock_fd
, IPPORT_BOOTPC
, INADDR_ANY
)) {
1541 "dhcp_ip_default: unable to bind IPv4 socket to port %d",
1546 if (iu_register_event(eh
, v4_sock_fd
, POLLIN
, dhcp_acknak_global
,
1548 dhcpmsg(MSG_WARNING
, "dhcp_ip_default: cannot register to "
1549 "receive IPv4 broadcasts");
1553 if ((v6_sock_fd
= socket(AF_INET6
, SOCK_DGRAM
, 0)) == -1) {
1555 "dhcp_ip_default: unable to create IPv6 socket");
1559 if (setsockopt(v6_sock_fd
, IPPROTO_IPV6
, IPV6_RECVPKTINFO
, &on
,
1560 sizeof (on
)) == -1) {
1562 "dhcp_ip_default: unable to enable IPV6_RECVPKTINFO");
1566 if (!bind_sock_v6(v6_sock_fd
, IPPORT_DHCPV6C
, NULL
)) {
1568 "dhcp_ip_default: unable to bind IPv6 socket to port %d",
1573 if (iu_register_event(eh
, v6_sock_fd
, POLLIN
, dhcp_acknak_global
,
1575 dhcpmsg(MSG_WARNING
, "dhcp_ip_default: cannot register to "
1576 "receive IPv6 packets");