17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate * CDDL HEADER START
37c478bd9Sstevel@tonic-gate *
47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the
5d04ccbb3Scarlsonj * Common Development and Distribution License (the "License").
6d04ccbb3Scarlsonj * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate *
87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate * and limitations under the License.
127c478bd9Sstevel@tonic-gate *
137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate *
197c478bd9Sstevel@tonic-gate * CDDL HEADER END
207c478bd9Sstevel@tonic-gate */
217c478bd9Sstevel@tonic-gate /*
22*e11c3f44Smeem * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
237c478bd9Sstevel@tonic-gate * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate */
257c478bd9Sstevel@tonic-gate
267c478bd9Sstevel@tonic-gate #include <string.h>
277c478bd9Sstevel@tonic-gate #include <sys/types.h>
287c478bd9Sstevel@tonic-gate #include <stdlib.h>
297c478bd9Sstevel@tonic-gate #include <dhcpmsg.h>
307c478bd9Sstevel@tonic-gate #include <stddef.h>
317c478bd9Sstevel@tonic-gate #include <assert.h>
32d04ccbb3Scarlsonj #include <search.h>
33d04ccbb3Scarlsonj #include <alloca.h>
34d04ccbb3Scarlsonj #include <limits.h>
35d04ccbb3Scarlsonj #include <stropts.h>
36d04ccbb3Scarlsonj #include <netinet/dhcp6.h>
37d04ccbb3Scarlsonj #include <arpa/inet.h>
38d04ccbb3Scarlsonj #include <sys/sysmacros.h>
39d04ccbb3Scarlsonj #include <sys/sockio.h>
40d04ccbb3Scarlsonj #include <inet/ip6_asp.h>
417c478bd9Sstevel@tonic-gate
427c478bd9Sstevel@tonic-gate #include "states.h"
437c478bd9Sstevel@tonic-gate #include "interface.h"
447c478bd9Sstevel@tonic-gate #include "agent.h"
457c478bd9Sstevel@tonic-gate #include "packet.h"
467c478bd9Sstevel@tonic-gate #include "util.h"
477c478bd9Sstevel@tonic-gate
48d04ccbb3Scarlsonj int v6_sock_fd = -1;
49d04ccbb3Scarlsonj int v4_sock_fd = -1;
507c478bd9Sstevel@tonic-gate
51d04ccbb3Scarlsonj const in6_addr_t ipv6_all_dhcp_relay_and_servers = {
52d04ccbb3Scarlsonj 0xff, 0x02, 0x00, 0x00,
53d04ccbb3Scarlsonj 0x00, 0x00, 0x00, 0x00,
54d04ccbb3Scarlsonj 0x00, 0x00, 0x00, 0x00,
55d04ccbb3Scarlsonj 0x00, 0x01, 0x00, 0x02
567c478bd9Sstevel@tonic-gate };
577c478bd9Sstevel@tonic-gate
58d04ccbb3Scarlsonj /*
59d04ccbb3Scarlsonj * We have our own version of this constant because dhcpagent is compiled with
60d04ccbb3Scarlsonj * -lxnet.
61d04ccbb3Scarlsonj */
62d04ccbb3Scarlsonj const in6_addr_t my_in6addr_any = IN6ADDR_ANY_INIT;
637c478bd9Sstevel@tonic-gate
64d04ccbb3Scarlsonj static void retransmit(iu_tq_t *, void *);
65d04ccbb3Scarlsonj static void next_retransmission(dhcp_smach_t *, boolean_t, boolean_t);
66d04ccbb3Scarlsonj static boolean_t send_pkt_internal(dhcp_smach_t *);
677c478bd9Sstevel@tonic-gate
687c478bd9Sstevel@tonic-gate /*
69d04ccbb3Scarlsonj * pkt_send_type(): returns an integer representing the packet's type; only
707c478bd9Sstevel@tonic-gate * for use with outbound packets.
717c478bd9Sstevel@tonic-gate *
72d04ccbb3Scarlsonj * input: dhcp_pkt_t *: the packet to examine
737c478bd9Sstevel@tonic-gate * output: uchar_t: the packet type (0 if unknown)
747c478bd9Sstevel@tonic-gate */
757c478bd9Sstevel@tonic-gate
767c478bd9Sstevel@tonic-gate static uchar_t
pkt_send_type(const dhcp_pkt_t * dpkt)77d04ccbb3Scarlsonj pkt_send_type(const dhcp_pkt_t *dpkt)
787c478bd9Sstevel@tonic-gate {
79d04ccbb3Scarlsonj const uchar_t *option;
80d04ccbb3Scarlsonj
81d04ccbb3Scarlsonj if (dpkt->pkt_isv6)
82d04ccbb3Scarlsonj return (((const dhcpv6_message_t *)dpkt->pkt)->d6m_msg_type);
837c478bd9Sstevel@tonic-gate
847c478bd9Sstevel@tonic-gate /*
857c478bd9Sstevel@tonic-gate * this is a little dirty but it should get the job done.
867c478bd9Sstevel@tonic-gate * assumes that the type is in the statically allocated part
877c478bd9Sstevel@tonic-gate * of the options field.
887c478bd9Sstevel@tonic-gate */
897c478bd9Sstevel@tonic-gate
90d04ccbb3Scarlsonj option = dpkt->pkt->options;
91d04ccbb3Scarlsonj for (;;) {
92d04ccbb3Scarlsonj if (*option == CD_PAD) {
937c478bd9Sstevel@tonic-gate option++;
94d04ccbb3Scarlsonj continue;
95d04ccbb3Scarlsonj }
96d04ccbb3Scarlsonj if (*option == CD_END ||
97d04ccbb3Scarlsonj option + 2 - dpkt->pkt->options >=
98d04ccbb3Scarlsonj sizeof (dpkt->pkt->options))
99d04ccbb3Scarlsonj return (0);
100d04ccbb3Scarlsonj if (*option == CD_DHCP_TYPE)
101d04ccbb3Scarlsonj break;
102d04ccbb3Scarlsonj option++;
103d04ccbb3Scarlsonj option += *option + 1;
1047c478bd9Sstevel@tonic-gate }
1057c478bd9Sstevel@tonic-gate
1067c478bd9Sstevel@tonic-gate return (option[2]);
1077c478bd9Sstevel@tonic-gate }
1087c478bd9Sstevel@tonic-gate
1097c478bd9Sstevel@tonic-gate /*
110d04ccbb3Scarlsonj * pkt_recv_type(): returns an integer representing the packet's type; only
111d04ccbb3Scarlsonj * for use with inbound packets.
112d04ccbb3Scarlsonj *
113d04ccbb3Scarlsonj * input: dhcp_pkt_t *: the packet to examine
114d04ccbb3Scarlsonj * output: uchar_t: the packet type (0 if unknown)
115d04ccbb3Scarlsonj */
116d04ccbb3Scarlsonj
117d04ccbb3Scarlsonj uchar_t
pkt_recv_type(const PKT_LIST * plp)118d04ccbb3Scarlsonj pkt_recv_type(const PKT_LIST *plp)
119d04ccbb3Scarlsonj {
120d04ccbb3Scarlsonj if (plp->isv6)
121d04ccbb3Scarlsonj return (((const dhcpv6_message_t *)plp->pkt)->d6m_msg_type);
122d04ccbb3Scarlsonj else if (plp->opts[CD_DHCP_TYPE] != NULL)
123d04ccbb3Scarlsonj return (plp->opts[CD_DHCP_TYPE]->value[0]);
124d04ccbb3Scarlsonj else
125d04ccbb3Scarlsonj return (0);
126d04ccbb3Scarlsonj }
127d04ccbb3Scarlsonj
128d04ccbb3Scarlsonj /*
129d04ccbb3Scarlsonj * pkt_get_xid(): returns transaction ID from a DHCP packet.
130d04ccbb3Scarlsonj *
131d04ccbb3Scarlsonj * input: const PKT *: the packet to examine
132d04ccbb3Scarlsonj * output: uint_t: the transaction ID (0 if unknown)
133d04ccbb3Scarlsonj */
134d04ccbb3Scarlsonj
135d04ccbb3Scarlsonj uint_t
pkt_get_xid(const PKT * pkt,boolean_t isv6)136d04ccbb3Scarlsonj pkt_get_xid(const PKT *pkt, boolean_t isv6)
137d04ccbb3Scarlsonj {
138d04ccbb3Scarlsonj if (pkt == NULL)
139d04ccbb3Scarlsonj return (0);
140d04ccbb3Scarlsonj if (isv6)
141d04ccbb3Scarlsonj return (DHCPV6_GET_TRANSID((const dhcpv6_message_t *)pkt));
142d04ccbb3Scarlsonj else
143d04ccbb3Scarlsonj return (pkt->xid);
144d04ccbb3Scarlsonj }
145d04ccbb3Scarlsonj
146d04ccbb3Scarlsonj /*
1477c478bd9Sstevel@tonic-gate * init_pkt(): initializes and returns a packet of a given type
1487c478bd9Sstevel@tonic-gate *
149d04ccbb3Scarlsonj * input: dhcp_smach_t *: the state machine that will send the packet
1507c478bd9Sstevel@tonic-gate * uchar_t: the packet type (DHCP message type)
151d04ccbb3Scarlsonj * output: dhcp_pkt_t *: a pointer to the initialized packet; may be NULL
1527c478bd9Sstevel@tonic-gate */
1537c478bd9Sstevel@tonic-gate
1547c478bd9Sstevel@tonic-gate dhcp_pkt_t *
init_pkt(dhcp_smach_t * dsmp,uchar_t type)155d04ccbb3Scarlsonj init_pkt(dhcp_smach_t *dsmp, uchar_t type)
1567c478bd9Sstevel@tonic-gate {
157d04ccbb3Scarlsonj dhcp_pkt_t *dpkt = &dsmp->dsm_send_pkt;
158d04ccbb3Scarlsonj dhcp_lif_t *lif = dsmp->dsm_lif;
159d04ccbb3Scarlsonj dhcp_pif_t *pif = lif->lif_pif;
160e704a8f2Smeem uint_t mtu = lif->lif_max;
1617c478bd9Sstevel@tonic-gate uint32_t xid;
162d04ccbb3Scarlsonj boolean_t isv6;
1637c478bd9Sstevel@tonic-gate
164d04ccbb3Scarlsonj dpkt->pkt_isv6 = isv6 = pif->pif_isv6;
165d04ccbb3Scarlsonj
166d04ccbb3Scarlsonj /*
167e704a8f2Smeem * Since multiple dhcp leases may be maintained over the same pif
168e704a8f2Smeem * (e.g. "hme0" and "hme0:1"), make sure the xid is unique.
169d04ccbb3Scarlsonj *
170d04ccbb3Scarlsonj * Note that transaction ID zero is intentionally never assigned.
171d04ccbb3Scarlsonj * That's used to represent "no ID." Also note that transaction IDs
172d04ccbb3Scarlsonj * are only 24 bits long in DHCPv6.
173d04ccbb3Scarlsonj */
174d04ccbb3Scarlsonj
175d04ccbb3Scarlsonj do {
176d04ccbb3Scarlsonj xid = mrand48();
177d04ccbb3Scarlsonj if (isv6)
178d04ccbb3Scarlsonj xid &= 0xFFFFFF;
179d04ccbb3Scarlsonj } while (xid == 0 ||
180d04ccbb3Scarlsonj lookup_smach_by_xid(xid, NULL, dpkt->pkt_isv6) != NULL);
181d04ccbb3Scarlsonj
182d04ccbb3Scarlsonj if (isv6) {
183d04ccbb3Scarlsonj dhcpv6_message_t *v6;
184d04ccbb3Scarlsonj
185d04ccbb3Scarlsonj if (mtu != dpkt->pkt_max_len &&
186d04ccbb3Scarlsonj (v6 = realloc(dpkt->pkt, mtu)) != NULL) {
187d04ccbb3Scarlsonj /* LINTED: alignment known to be correct */
188d04ccbb3Scarlsonj dpkt->pkt = (PKT *)v6;
189d04ccbb3Scarlsonj dpkt->pkt_max_len = mtu;
190d04ccbb3Scarlsonj }
191d04ccbb3Scarlsonj
192d04ccbb3Scarlsonj if (sizeof (*v6) > dpkt->pkt_max_len) {
193d04ccbb3Scarlsonj dhcpmsg(MSG_ERR, "init_pkt: cannot allocate v6 pkt: %u",
194d04ccbb3Scarlsonj mtu);
195d04ccbb3Scarlsonj return (NULL);
196d04ccbb3Scarlsonj }
197d04ccbb3Scarlsonj
198d04ccbb3Scarlsonj v6 = (dhcpv6_message_t *)dpkt->pkt;
199d04ccbb3Scarlsonj dpkt->pkt_cur_len = sizeof (*v6);
200d04ccbb3Scarlsonj
201d04ccbb3Scarlsonj (void) memset(v6, 0, dpkt->pkt_max_len);
202d04ccbb3Scarlsonj
203d04ccbb3Scarlsonj v6->d6m_msg_type = type;
204d04ccbb3Scarlsonj DHCPV6_SET_TRANSID(v6, xid);
205d04ccbb3Scarlsonj
206d04ccbb3Scarlsonj if (dsmp->dsm_cidlen > 0 &&
207d04ccbb3Scarlsonj add_pkt_opt(dpkt, DHCPV6_OPT_CLIENTID, dsmp->dsm_cid,
208d04ccbb3Scarlsonj dsmp->dsm_cidlen) == NULL) {
209d04ccbb3Scarlsonj dhcpmsg(MSG_WARNING,
210d04ccbb3Scarlsonj "init_pkt: cannot insert client ID");
211d04ccbb3Scarlsonj return (NULL);
212d04ccbb3Scarlsonj }
213d04ccbb3Scarlsonj
214d04ccbb3Scarlsonj /* For v6, time starts with the creation of a transaction */
215d04ccbb3Scarlsonj dsmp->dsm_neg_hrtime = gethrtime();
216d04ccbb3Scarlsonj dsmp->dsm_newstart_monosec = monosec();
217d04ccbb3Scarlsonj } else {
218d04ccbb3Scarlsonj static uint8_t bootmagic[] = BOOTMAGIC;
219d04ccbb3Scarlsonj PKT *v4;
220d04ccbb3Scarlsonj
221d04ccbb3Scarlsonj if (mtu != dpkt->pkt_max_len &&
222d04ccbb3Scarlsonj (v4 = realloc(dpkt->pkt, mtu)) != NULL) {
223d04ccbb3Scarlsonj dpkt->pkt = v4;
224d04ccbb3Scarlsonj dpkt->pkt_max_len = mtu;
225d04ccbb3Scarlsonj }
226d04ccbb3Scarlsonj
227d04ccbb3Scarlsonj if (offsetof(PKT, options) > dpkt->pkt_max_len) {
228d04ccbb3Scarlsonj dhcpmsg(MSG_ERR, "init_pkt: cannot allocate v4 pkt: %u",
229d04ccbb3Scarlsonj mtu);
230d04ccbb3Scarlsonj return (NULL);
231d04ccbb3Scarlsonj }
232d04ccbb3Scarlsonj
233d04ccbb3Scarlsonj v4 = dpkt->pkt;
2347c478bd9Sstevel@tonic-gate dpkt->pkt_cur_len = offsetof(PKT, options);
2357c478bd9Sstevel@tonic-gate
236d04ccbb3Scarlsonj (void) memset(v4, 0, dpkt->pkt_max_len);
237d04ccbb3Scarlsonj (void) memcpy(v4->cookie, bootmagic, sizeof (bootmagic));
238d04ccbb3Scarlsonj if (pif->pif_hwlen <= sizeof (v4->chaddr)) {
239d04ccbb3Scarlsonj v4->hlen = pif->pif_hwlen;
240d04ccbb3Scarlsonj (void) memcpy(v4->chaddr, pif->pif_hwaddr,
241d04ccbb3Scarlsonj pif->pif_hwlen);
2427c478bd9Sstevel@tonic-gate } else {
2437c478bd9Sstevel@tonic-gate /*
2447c478bd9Sstevel@tonic-gate * The mac address does not fit in the chaddr
2457c478bd9Sstevel@tonic-gate * field, thus it can not be sent to the server,
2467c478bd9Sstevel@tonic-gate * thus server can not unicast the reply. Per
2477c478bd9Sstevel@tonic-gate * RFC 2131 4.4.1, client can set this bit in
2487c478bd9Sstevel@tonic-gate * DISCOVER/REQUEST. If the client is already
249e704a8f2Smeem * in a bound state, do not set this bit, as it
250e704a8f2Smeem * can respond to unicast responses from server
251e704a8f2Smeem * using the 'ciaddr' address.
2527c478bd9Sstevel@tonic-gate */
253e704a8f2Smeem if (type == DISCOVER || (type == REQUEST &&
254e704a8f2Smeem !is_bound_state(dsmp->dsm_state)))
255d04ccbb3Scarlsonj v4->flags = htons(BCAST_MASK);
256d04ccbb3Scarlsonj }
257d04ccbb3Scarlsonj
258d04ccbb3Scarlsonj v4->xid = xid;
259d04ccbb3Scarlsonj v4->op = BOOTREQUEST;
260d04ccbb3Scarlsonj v4->htype = pif->pif_hwtype;
261d04ccbb3Scarlsonj
262d04ccbb3Scarlsonj if (add_pkt_opt(dpkt, CD_DHCP_TYPE, &type, 1) == NULL) {
263d04ccbb3Scarlsonj dhcpmsg(MSG_WARNING,
264d04ccbb3Scarlsonj "init_pkt: cannot set DHCP packet type");
265d04ccbb3Scarlsonj return (NULL);
266d04ccbb3Scarlsonj }
267d04ccbb3Scarlsonj
268d04ccbb3Scarlsonj if (dsmp->dsm_cidlen > 0 &&
269d04ccbb3Scarlsonj add_pkt_opt(dpkt, CD_CLIENT_ID, dsmp->dsm_cid,
270d04ccbb3Scarlsonj dsmp->dsm_cidlen) == NULL) {
271d04ccbb3Scarlsonj dhcpmsg(MSG_WARNING,
272d04ccbb3Scarlsonj "init_pkt: cannot insert client ID");
273d04ccbb3Scarlsonj return (NULL);
274d04ccbb3Scarlsonj }
275d04ccbb3Scarlsonj }
276d04ccbb3Scarlsonj
277d04ccbb3Scarlsonj return (dpkt);
2787c478bd9Sstevel@tonic-gate }
2797c478bd9Sstevel@tonic-gate
2807c478bd9Sstevel@tonic-gate /*
281d04ccbb3Scarlsonj * remove_pkt_opt(): removes the first instance of an option from a dhcp_pkt_t
282d04ccbb3Scarlsonj *
283d04ccbb3Scarlsonj * input: dhcp_pkt_t *: the packet to remove the option from
284d04ccbb3Scarlsonj * uint_t: the type of option being added
285d04ccbb3Scarlsonj * output: boolean_t: B_TRUE on success, B_FALSE on failure
286d04ccbb3Scarlsonj * note: currently does not work with DHCPv6 suboptions, or to remove
287d04ccbb3Scarlsonj * arbitrary option instances.
2887c478bd9Sstevel@tonic-gate */
2897c478bd9Sstevel@tonic-gate
290d04ccbb3Scarlsonj boolean_t
remove_pkt_opt(dhcp_pkt_t * dpkt,uint_t opt_type)291d04ccbb3Scarlsonj remove_pkt_opt(dhcp_pkt_t *dpkt, uint_t opt_type)
292d04ccbb3Scarlsonj {
293d04ccbb3Scarlsonj uchar_t *raw_pkt, *raw_end, *next;
294d04ccbb3Scarlsonj uint_t len;
2957c478bd9Sstevel@tonic-gate
296d04ccbb3Scarlsonj raw_pkt = (uchar_t *)dpkt->pkt;
297d04ccbb3Scarlsonj raw_end = raw_pkt + dpkt->pkt_cur_len;
298d04ccbb3Scarlsonj if (dpkt->pkt_isv6) {
299d04ccbb3Scarlsonj dhcpv6_option_t d6o;
3007c478bd9Sstevel@tonic-gate
301d04ccbb3Scarlsonj raw_pkt += sizeof (dhcpv6_message_t);
3027c478bd9Sstevel@tonic-gate
303d04ccbb3Scarlsonj opt_type = htons(opt_type);
304d04ccbb3Scarlsonj while (raw_pkt + sizeof (d6o) <= raw_end) {
305d04ccbb3Scarlsonj (void) memcpy(&d6o, raw_pkt, sizeof (d6o));
306d04ccbb3Scarlsonj len = ntohs(d6o.d6o_len) + sizeof (d6o);
307d04ccbb3Scarlsonj if (len > raw_end - raw_pkt)
308d04ccbb3Scarlsonj break;
309d04ccbb3Scarlsonj next = raw_pkt + len;
310d04ccbb3Scarlsonj if (d6o.d6o_code == opt_type) {
311d04ccbb3Scarlsonj if (next < raw_end) {
312d04ccbb3Scarlsonj (void) memmove(raw_pkt, next,
313d04ccbb3Scarlsonj raw_end - next);
314d04ccbb3Scarlsonj }
315d04ccbb3Scarlsonj dpkt->pkt_cur_len -= len;
316d04ccbb3Scarlsonj return (B_TRUE);
317d04ccbb3Scarlsonj }
318d04ccbb3Scarlsonj raw_pkt = next;
319d04ccbb3Scarlsonj }
320d04ccbb3Scarlsonj } else {
321d04ccbb3Scarlsonj uchar_t *pstart, *padrun;
322d04ccbb3Scarlsonj
323d04ccbb3Scarlsonj raw_pkt += offsetof(PKT, options);
324d04ccbb3Scarlsonj pstart = raw_pkt;
325d04ccbb3Scarlsonj
326d04ccbb3Scarlsonj if (opt_type == CD_END || opt_type == CD_PAD)
327d04ccbb3Scarlsonj return (B_FALSE);
328d04ccbb3Scarlsonj
329d04ccbb3Scarlsonj padrun = NULL;
330d04ccbb3Scarlsonj while (raw_pkt + 1 <= raw_end) {
331d04ccbb3Scarlsonj if (*raw_pkt == CD_END)
332d04ccbb3Scarlsonj break;
333d04ccbb3Scarlsonj if (*raw_pkt == CD_PAD) {
334d04ccbb3Scarlsonj if (padrun == NULL)
335d04ccbb3Scarlsonj padrun = raw_pkt;
336d04ccbb3Scarlsonj raw_pkt++;
337d04ccbb3Scarlsonj continue;
338d04ccbb3Scarlsonj }
339d04ccbb3Scarlsonj if (raw_pkt + 2 > raw_end)
340d04ccbb3Scarlsonj break;
341d04ccbb3Scarlsonj len = raw_pkt[1];
342d04ccbb3Scarlsonj if (len > raw_end - raw_pkt || len < 2)
343d04ccbb3Scarlsonj break;
344d04ccbb3Scarlsonj next = raw_pkt + len;
345d04ccbb3Scarlsonj if (*raw_pkt == opt_type) {
346d04ccbb3Scarlsonj if (next < raw_end) {
347d04ccbb3Scarlsonj int toadd = (4 + ((next-pstart)&3) -
348d04ccbb3Scarlsonj ((raw_pkt-pstart)&3)) & 3;
349d04ccbb3Scarlsonj int torem = 4 - toadd;
350d04ccbb3Scarlsonj
351d04ccbb3Scarlsonj if (torem != 4 && padrun != NULL &&
352d04ccbb3Scarlsonj (raw_pkt - padrun) >= torem) {
353d04ccbb3Scarlsonj raw_pkt -= torem;
354d04ccbb3Scarlsonj dpkt->pkt_cur_len -= torem;
355d04ccbb3Scarlsonj } else if (toadd > 0) {
356d04ccbb3Scarlsonj (void) memset(raw_pkt, CD_PAD,
357d04ccbb3Scarlsonj toadd);
358d04ccbb3Scarlsonj raw_pkt += toadd;
359d04ccbb3Scarlsonj /* max is not an issue here */
360d04ccbb3Scarlsonj dpkt->pkt_cur_len += toadd;
361d04ccbb3Scarlsonj }
362d04ccbb3Scarlsonj if (raw_pkt != next) {
363d04ccbb3Scarlsonj (void) memmove(raw_pkt, next,
364d04ccbb3Scarlsonj raw_end - next);
365d04ccbb3Scarlsonj }
366d04ccbb3Scarlsonj }
367d04ccbb3Scarlsonj dpkt->pkt_cur_len -= len;
368d04ccbb3Scarlsonj return (B_TRUE);
369d04ccbb3Scarlsonj }
370d04ccbb3Scarlsonj padrun = NULL;
371d04ccbb3Scarlsonj raw_pkt = next;
372d04ccbb3Scarlsonj }
373d04ccbb3Scarlsonj }
374d04ccbb3Scarlsonj return (B_FALSE);
375d04ccbb3Scarlsonj }
376d04ccbb3Scarlsonj
377d04ccbb3Scarlsonj /*
378d04ccbb3Scarlsonj * update_v6opt_len(): updates the length field of a DHCPv6 option.
379d04ccbb3Scarlsonj *
380d04ccbb3Scarlsonj * input: dhcpv6_option_t *: option to be updated
381d04ccbb3Scarlsonj * int: number of octets to add or subtract
382d04ccbb3Scarlsonj * output: boolean_t: B_TRUE on success, B_FALSE on failure
383d04ccbb3Scarlsonj */
384d04ccbb3Scarlsonj
385d04ccbb3Scarlsonj boolean_t
update_v6opt_len(dhcpv6_option_t * opt,int adjust)386d04ccbb3Scarlsonj update_v6opt_len(dhcpv6_option_t *opt, int adjust)
387d04ccbb3Scarlsonj {
388d04ccbb3Scarlsonj dhcpv6_option_t optval;
389d04ccbb3Scarlsonj
390d04ccbb3Scarlsonj (void) memcpy(&optval, opt, sizeof (optval));
391d04ccbb3Scarlsonj adjust += ntohs(optval.d6o_len);
392d04ccbb3Scarlsonj if (adjust < 0 || adjust > UINT16_MAX) {
393d04ccbb3Scarlsonj return (B_FALSE);
394d04ccbb3Scarlsonj } else {
395d04ccbb3Scarlsonj optval.d6o_len = htons(adjust);
396d04ccbb3Scarlsonj (void) memcpy(opt, &optval, sizeof (optval));
397d04ccbb3Scarlsonj return (B_TRUE);
398d04ccbb3Scarlsonj }
3997c478bd9Sstevel@tonic-gate }
4007c478bd9Sstevel@tonic-gate
4017c478bd9Sstevel@tonic-gate /*
4027c478bd9Sstevel@tonic-gate * add_pkt_opt(): adds an option to a dhcp_pkt_t
4037c478bd9Sstevel@tonic-gate *
4047c478bd9Sstevel@tonic-gate * input: dhcp_pkt_t *: the packet to add the option to
405d04ccbb3Scarlsonj * uint_t: the type of option being added
4067c478bd9Sstevel@tonic-gate * const void *: the value of that option
407d04ccbb3Scarlsonj * uint_t: the length of the value of the option
408d04ccbb3Scarlsonj * output: void *: pointer to the option that was added, or NULL on failure.
4097c478bd9Sstevel@tonic-gate */
4107c478bd9Sstevel@tonic-gate
411d04ccbb3Scarlsonj void *
add_pkt_opt(dhcp_pkt_t * dpkt,uint_t opt_type,const void * opt_val,uint_t opt_len)412d04ccbb3Scarlsonj add_pkt_opt(dhcp_pkt_t *dpkt, uint_t opt_type, const void *opt_val,
413d04ccbb3Scarlsonj uint_t opt_len)
4147c478bd9Sstevel@tonic-gate {
415d04ccbb3Scarlsonj uchar_t *raw_pkt;
416d04ccbb3Scarlsonj int req_len;
417d04ccbb3Scarlsonj void *optr;
418d04ccbb3Scarlsonj
419d04ccbb3Scarlsonj raw_pkt = (uchar_t *)dpkt->pkt;
420d04ccbb3Scarlsonj optr = raw_pkt + dpkt->pkt_cur_len;
421d04ccbb3Scarlsonj if (dpkt->pkt_isv6) {
422d04ccbb3Scarlsonj dhcpv6_option_t d6o;
423d04ccbb3Scarlsonj
424d04ccbb3Scarlsonj req_len = opt_len + sizeof (d6o);
425d04ccbb3Scarlsonj
426d04ccbb3Scarlsonj if (dpkt->pkt_cur_len + req_len > dpkt->pkt_max_len) {
427d04ccbb3Scarlsonj dhcpmsg(MSG_WARNING,
428d04ccbb3Scarlsonj "add_pkt_opt: not enough room for v6 option %u in "
429d04ccbb3Scarlsonj "packet (%u + %u > %u)", opt_type,
430d04ccbb3Scarlsonj dpkt->pkt_cur_len, req_len, dpkt->pkt_max_len);
431d04ccbb3Scarlsonj return (NULL);
432d04ccbb3Scarlsonj }
433d04ccbb3Scarlsonj d6o.d6o_code = htons(opt_type);
434d04ccbb3Scarlsonj d6o.d6o_len = htons(opt_len);
435d04ccbb3Scarlsonj (void) memcpy(&raw_pkt[dpkt->pkt_cur_len], &d6o, sizeof (d6o));
436d04ccbb3Scarlsonj dpkt->pkt_cur_len += sizeof (d6o);
437d04ccbb3Scarlsonj if (opt_len > 0) {
438d04ccbb3Scarlsonj (void) memcpy(&raw_pkt[dpkt->pkt_cur_len], opt_val,
439d04ccbb3Scarlsonj opt_len);
440d04ccbb3Scarlsonj dpkt->pkt_cur_len += opt_len;
441d04ccbb3Scarlsonj }
442d04ccbb3Scarlsonj } else {
443d04ccbb3Scarlsonj req_len = opt_len + 2; /* + 2 for code & length bytes */
4447c478bd9Sstevel@tonic-gate
4457c478bd9Sstevel@tonic-gate /* CD_END and CD_PAD options don't have a length field */
446d04ccbb3Scarlsonj if (opt_type == CD_END || opt_type == CD_PAD) {
447d04ccbb3Scarlsonj req_len = 1;
448d04ccbb3Scarlsonj } else if (opt_val == NULL) {
449d04ccbb3Scarlsonj dhcpmsg(MSG_ERROR, "add_pkt_opt: option type %d is "
450d04ccbb3Scarlsonj "missing required value", opt_type);
451d04ccbb3Scarlsonj return (NULL);
452d04ccbb3Scarlsonj }
4537c478bd9Sstevel@tonic-gate
4547c478bd9Sstevel@tonic-gate if ((dpkt->pkt_cur_len + req_len) > dpkt->pkt_max_len) {
455d04ccbb3Scarlsonj dhcpmsg(MSG_WARNING,
456d04ccbb3Scarlsonj "add_pkt_opt: not enough room for v4 option %u in "
457d04ccbb3Scarlsonj "packet", opt_type);
458d04ccbb3Scarlsonj return (NULL);
4597c478bd9Sstevel@tonic-gate }
4607c478bd9Sstevel@tonic-gate
4617c478bd9Sstevel@tonic-gate raw_pkt[dpkt->pkt_cur_len++] = opt_type;
4627c478bd9Sstevel@tonic-gate
463d04ccbb3Scarlsonj if (req_len > 1) {
4647c478bd9Sstevel@tonic-gate raw_pkt[dpkt->pkt_cur_len++] = opt_len;
465d04ccbb3Scarlsonj if (opt_len > 0) {
466d04ccbb3Scarlsonj (void) memcpy(&raw_pkt[dpkt->pkt_cur_len],
467d04ccbb3Scarlsonj opt_val, opt_len);
4687c478bd9Sstevel@tonic-gate dpkt->pkt_cur_len += opt_len;
4697c478bd9Sstevel@tonic-gate }
4707c478bd9Sstevel@tonic-gate }
471d04ccbb3Scarlsonj }
472d04ccbb3Scarlsonj return (optr);
473d04ccbb3Scarlsonj }
474d04ccbb3Scarlsonj
475d04ccbb3Scarlsonj /*
476d04ccbb3Scarlsonj * add_pkt_subopt(): adds an option to a dhcp_pkt_t option. DHCPv6-specific,
477d04ccbb3Scarlsonj * but could be extended to IPv4 DHCP if necessary. Assumes
478d04ccbb3Scarlsonj * that if the parent isn't a top-level option, the caller
479d04ccbb3Scarlsonj * will adjust any upper-level options recursively using
480d04ccbb3Scarlsonj * update_v6opt_len.
481d04ccbb3Scarlsonj *
482d04ccbb3Scarlsonj * input: dhcp_pkt_t *: the packet to add the suboption to
483d04ccbb3Scarlsonj * dhcpv6_option_t *: the start of the option to that should contain
484d04ccbb3Scarlsonj * it (parent)
485d04ccbb3Scarlsonj * uint_t: the type of suboption being added
486d04ccbb3Scarlsonj * const void *: the value of that option
487d04ccbb3Scarlsonj * uint_t: the length of the value of the option
488d04ccbb3Scarlsonj * output: void *: pointer to the suboption that was added, or NULL on
489d04ccbb3Scarlsonj * failure.
490d04ccbb3Scarlsonj */
491d04ccbb3Scarlsonj
492d04ccbb3Scarlsonj void *
add_pkt_subopt(dhcp_pkt_t * dpkt,dhcpv6_option_t * parentopt,uint_t opt_type,const void * opt_val,uint_t opt_len)493d04ccbb3Scarlsonj add_pkt_subopt(dhcp_pkt_t *dpkt, dhcpv6_option_t *parentopt, uint_t opt_type,
494d04ccbb3Scarlsonj const void *opt_val, uint_t opt_len)
495d04ccbb3Scarlsonj {
496d04ccbb3Scarlsonj uchar_t *raw_pkt;
497d04ccbb3Scarlsonj int req_len;
498d04ccbb3Scarlsonj void *optr;
499d04ccbb3Scarlsonj dhcpv6_option_t d6o;
500d04ccbb3Scarlsonj uchar_t *optend;
501d04ccbb3Scarlsonj int olen;
502d04ccbb3Scarlsonj
503d04ccbb3Scarlsonj if (!dpkt->pkt_isv6)
504d04ccbb3Scarlsonj return (NULL);
505d04ccbb3Scarlsonj
506d04ccbb3Scarlsonj raw_pkt = (uchar_t *)dpkt->pkt;
507d04ccbb3Scarlsonj req_len = opt_len + sizeof (d6o);
508d04ccbb3Scarlsonj
509d04ccbb3Scarlsonj if (dpkt->pkt_cur_len + req_len > dpkt->pkt_max_len) {
510d04ccbb3Scarlsonj dhcpmsg(MSG_WARNING,
511d04ccbb3Scarlsonj "add_pkt_subopt: not enough room for v6 suboption %u in "
512d04ccbb3Scarlsonj "packet (%u + %u > %u)", opt_type,
513d04ccbb3Scarlsonj dpkt->pkt_cur_len, req_len, dpkt->pkt_max_len);
514d04ccbb3Scarlsonj return (NULL);
515d04ccbb3Scarlsonj }
516d04ccbb3Scarlsonj
517d04ccbb3Scarlsonj /*
518d04ccbb3Scarlsonj * Update the parent option to include room for this option,
519d04ccbb3Scarlsonj * and compute the insertion point.
520d04ccbb3Scarlsonj */
521d04ccbb3Scarlsonj (void) memcpy(&d6o, parentopt, sizeof (d6o));
522d04ccbb3Scarlsonj olen = ntohs(d6o.d6o_len);
523d04ccbb3Scarlsonj optend = (uchar_t *)(parentopt + 1) + olen;
524d04ccbb3Scarlsonj olen += req_len;
525d04ccbb3Scarlsonj d6o.d6o_len = htons(olen);
526d04ccbb3Scarlsonj (void) memcpy(parentopt, &d6o, sizeof (d6o));
527d04ccbb3Scarlsonj
528d04ccbb3Scarlsonj /*
529d04ccbb3Scarlsonj * If there's anything at the end to move, then move it. Also bump up
530d04ccbb3Scarlsonj * the packet size.
531d04ccbb3Scarlsonj */
532d04ccbb3Scarlsonj if (optend < raw_pkt + dpkt->pkt_cur_len) {
533d04ccbb3Scarlsonj (void) memmove(optend + req_len, optend,
534d04ccbb3Scarlsonj (raw_pkt + dpkt->pkt_cur_len) - optend);
535d04ccbb3Scarlsonj }
536d04ccbb3Scarlsonj dpkt->pkt_cur_len += req_len;
537d04ccbb3Scarlsonj
538d04ccbb3Scarlsonj /*
539d04ccbb3Scarlsonj * Now format the suboption and add it in.
540d04ccbb3Scarlsonj */
541d04ccbb3Scarlsonj optr = optend;
542d04ccbb3Scarlsonj d6o.d6o_code = htons(opt_type);
543d04ccbb3Scarlsonj d6o.d6o_len = htons(opt_len);
544d04ccbb3Scarlsonj (void) memcpy(optend, &d6o, sizeof (d6o));
545d04ccbb3Scarlsonj if (opt_len > 0)
546d04ccbb3Scarlsonj (void) memcpy(optend + sizeof (d6o), opt_val, opt_len);
547d04ccbb3Scarlsonj return (optr);
548d04ccbb3Scarlsonj }
5497c478bd9Sstevel@tonic-gate
5507c478bd9Sstevel@tonic-gate /*
5517c478bd9Sstevel@tonic-gate * add_pkt_opt16(): adds an option with a 16-bit value to a dhcp_pkt_t
5527c478bd9Sstevel@tonic-gate *
5537c478bd9Sstevel@tonic-gate * input: dhcp_pkt_t *: the packet to add the option to
554d04ccbb3Scarlsonj * uint_t: the type of option being added
5557c478bd9Sstevel@tonic-gate * uint16_t: the value of that option
556d04ccbb3Scarlsonj * output: void *: pointer to the option that was added, or NULL on failure.
5577c478bd9Sstevel@tonic-gate */
5587c478bd9Sstevel@tonic-gate
559d04ccbb3Scarlsonj void *
add_pkt_opt16(dhcp_pkt_t * dpkt,uint_t opt_type,uint16_t opt_value)560d04ccbb3Scarlsonj add_pkt_opt16(dhcp_pkt_t *dpkt, uint_t opt_type, uint16_t opt_value)
5617c478bd9Sstevel@tonic-gate {
562d04ccbb3Scarlsonj return (add_pkt_opt(dpkt, opt_type, &opt_value, 2));
5637c478bd9Sstevel@tonic-gate }
5647c478bd9Sstevel@tonic-gate
5657c478bd9Sstevel@tonic-gate /*
5667c478bd9Sstevel@tonic-gate * add_pkt_opt32(): adds an option with a 32-bit value to a dhcp_pkt_t
5677c478bd9Sstevel@tonic-gate *
5687c478bd9Sstevel@tonic-gate * input: dhcp_pkt_t *: the packet to add the option to
569d04ccbb3Scarlsonj * uint_t: the type of option being added
5707c478bd9Sstevel@tonic-gate * uint32_t: the value of that option
571d04ccbb3Scarlsonj * output: void *: pointer to the option that was added, or NULL on failure.
572d04ccbb3Scarlsonj */
573d04ccbb3Scarlsonj
574d04ccbb3Scarlsonj void *
add_pkt_opt32(dhcp_pkt_t * dpkt,uint_t opt_type,uint32_t opt_value)575d04ccbb3Scarlsonj add_pkt_opt32(dhcp_pkt_t *dpkt, uint_t opt_type, uint32_t opt_value)
576d04ccbb3Scarlsonj {
577d04ccbb3Scarlsonj return (add_pkt_opt(dpkt, opt_type, &opt_value, 4));
578d04ccbb3Scarlsonj }
579d04ccbb3Scarlsonj
580d04ccbb3Scarlsonj /*
581d04ccbb3Scarlsonj * add_pkt_prl(): adds the parameter request option to the packet
582d04ccbb3Scarlsonj *
583d04ccbb3Scarlsonj * input: dhcp_pkt_t *: the packet to add the option to
584d04ccbb3Scarlsonj * dhcp_smach_t *: state machine with request option
585d04ccbb3Scarlsonj * output: void *: pointer to the option that was added, or NULL on failure.
586d04ccbb3Scarlsonj */
587d04ccbb3Scarlsonj
588d04ccbb3Scarlsonj void *
add_pkt_prl(dhcp_pkt_t * dpkt,dhcp_smach_t * dsmp)589d04ccbb3Scarlsonj add_pkt_prl(dhcp_pkt_t *dpkt, dhcp_smach_t *dsmp)
590d04ccbb3Scarlsonj {
591d04ccbb3Scarlsonj uint_t len;
592d04ccbb3Scarlsonj
593d04ccbb3Scarlsonj if (dsmp->dsm_prllen == 0)
594d04ccbb3Scarlsonj return (0);
595d04ccbb3Scarlsonj
596d04ccbb3Scarlsonj if (dpkt->pkt_isv6) {
597d04ccbb3Scarlsonj uint16_t *prl;
598d04ccbb3Scarlsonj
599d04ccbb3Scarlsonj /*
600d04ccbb3Scarlsonj * RFC 3315 requires that we include the option, even if we
601d04ccbb3Scarlsonj * have nothing to request.
602d04ccbb3Scarlsonj */
603d04ccbb3Scarlsonj if (dsmp->dsm_prllen == 0)
604d04ccbb3Scarlsonj prl = NULL;
605d04ccbb3Scarlsonj else
606d04ccbb3Scarlsonj prl = alloca(dsmp->dsm_prllen * sizeof (uint16_t));
607d04ccbb3Scarlsonj
608d04ccbb3Scarlsonj for (len = 0; len < dsmp->dsm_prllen; len++)
609d04ccbb3Scarlsonj prl[len] = htons(dsmp->dsm_prl[len]);
610d04ccbb3Scarlsonj return (add_pkt_opt(dpkt, DHCPV6_OPT_ORO, prl,
611d04ccbb3Scarlsonj len * sizeof (uint16_t)));
612d04ccbb3Scarlsonj } else {
613d04ccbb3Scarlsonj uint8_t *prl = alloca(dsmp->dsm_prllen);
614d04ccbb3Scarlsonj
615d04ccbb3Scarlsonj for (len = 0; len < dsmp->dsm_prllen; len++)
616d04ccbb3Scarlsonj prl[len] = dsmp->dsm_prl[len];
617d04ccbb3Scarlsonj return (add_pkt_opt(dpkt, CD_REQUEST_LIST, prl, len));
618d04ccbb3Scarlsonj }
619d04ccbb3Scarlsonj }
620d04ccbb3Scarlsonj
621d04ccbb3Scarlsonj /*
622d04ccbb3Scarlsonj * add_pkt_lif(): Adds CD_REQUESTED_IP_ADDR (IPv4 DHCP) or IA_NA and IAADDR
623d04ccbb3Scarlsonj * (DHCPv6) options to the packet to represent the given LIF.
624d04ccbb3Scarlsonj *
625d04ccbb3Scarlsonj * input: dhcp_pkt_t *: the packet to add the options to
626d04ccbb3Scarlsonj * dhcp_lif_t *: the logical interface to represent
627d04ccbb3Scarlsonj * int: status code (unused for IPv4 DHCP)
628d04ccbb3Scarlsonj * const char *: message to include with status option, or NULL
629d04ccbb3Scarlsonj * output: boolean_t: B_TRUE on success, B_FALSE on failure
630d04ccbb3Scarlsonj */
631d04ccbb3Scarlsonj
632d04ccbb3Scarlsonj boolean_t
add_pkt_lif(dhcp_pkt_t * dpkt,dhcp_lif_t * lif,int status,const char * msg)633d04ccbb3Scarlsonj add_pkt_lif(dhcp_pkt_t *dpkt, dhcp_lif_t *lif, int status, const char *msg)
634d04ccbb3Scarlsonj {
635d04ccbb3Scarlsonj if (lif->lif_pif->pif_isv6) {
636d04ccbb3Scarlsonj dhcp_smach_t *dsmp;
637d04ccbb3Scarlsonj dhcpv6_message_t *d6m;
638d04ccbb3Scarlsonj dhcpv6_ia_na_t d6in;
639d04ccbb3Scarlsonj dhcpv6_iaaddr_t d6ia;
640d04ccbb3Scarlsonj uint32_t iaid;
641d04ccbb3Scarlsonj uint16_t *statusopt;
642d04ccbb3Scarlsonj dhcpv6_option_t *d6o, *d6so;
643d04ccbb3Scarlsonj uint_t olen;
644d04ccbb3Scarlsonj
645d04ccbb3Scarlsonj /*
646d04ccbb3Scarlsonj * Currently, we support just one IAID related to the primary
647d04ccbb3Scarlsonj * LIF on the state machine.
648d04ccbb3Scarlsonj */
649d04ccbb3Scarlsonj dsmp = lif->lif_lease->dl_smach;
650d04ccbb3Scarlsonj iaid = dsmp->dsm_lif->lif_iaid;
651d04ccbb3Scarlsonj iaid = htonl(iaid);
652d04ccbb3Scarlsonj
653d04ccbb3Scarlsonj d6m = (dhcpv6_message_t *)dpkt->pkt;
654d04ccbb3Scarlsonj
655d04ccbb3Scarlsonj /*
656d04ccbb3Scarlsonj * Find or create the IA_NA needed for this LIF. If we
657d04ccbb3Scarlsonj * supported IA_TA, we'd check the IFF_TEMPORARY bit here.
658d04ccbb3Scarlsonj */
659d04ccbb3Scarlsonj d6o = NULL;
660d04ccbb3Scarlsonj while ((d6o = dhcpv6_find_option(d6m + 1,
661d04ccbb3Scarlsonj dpkt->pkt_cur_len - sizeof (*d6m), d6o, DHCPV6_OPT_IA_NA,
662d04ccbb3Scarlsonj &olen)) != NULL) {
663d04ccbb3Scarlsonj if (olen < sizeof (d6in))
664d04ccbb3Scarlsonj continue;
665d04ccbb3Scarlsonj (void) memcpy(&d6in, d6o, sizeof (d6in));
666d04ccbb3Scarlsonj if (d6in.d6in_iaid == iaid)
667d04ccbb3Scarlsonj break;
668d04ccbb3Scarlsonj }
669d04ccbb3Scarlsonj if (d6o == NULL) {
670d04ccbb3Scarlsonj d6in.d6in_iaid = iaid;
671d04ccbb3Scarlsonj d6in.d6in_t1 = 0;
672d04ccbb3Scarlsonj d6in.d6in_t2 = 0;
673d04ccbb3Scarlsonj d6o = add_pkt_opt(dpkt, DHCPV6_OPT_IA_NA,
674d04ccbb3Scarlsonj (dhcpv6_option_t *)&d6in + 1,
675d04ccbb3Scarlsonj sizeof (d6in) - sizeof (*d6o));
676d04ccbb3Scarlsonj if (d6o == NULL)
677d04ccbb3Scarlsonj return (B_FALSE);
678d04ccbb3Scarlsonj }
679d04ccbb3Scarlsonj
680d04ccbb3Scarlsonj /*
681d04ccbb3Scarlsonj * Now add the IAADDR suboption for this LIF. No need to
682d04ccbb3Scarlsonj * search here, as we know that this is unique.
683d04ccbb3Scarlsonj */
684d04ccbb3Scarlsonj d6ia.d6ia_addr = lif->lif_v6addr;
685d04ccbb3Scarlsonj
686d04ccbb3Scarlsonj /*
687d04ccbb3Scarlsonj * For Release and Decline, we zero out the lifetime. For
688d04ccbb3Scarlsonj * Renew and Rebind, we report the original time as the
689d04ccbb3Scarlsonj * preferred and valid lifetimes.
690d04ccbb3Scarlsonj */
691d04ccbb3Scarlsonj if (d6m->d6m_msg_type == DHCPV6_MSG_RELEASE ||
692d04ccbb3Scarlsonj d6m->d6m_msg_type == DHCPV6_MSG_DECLINE) {
693d04ccbb3Scarlsonj d6ia.d6ia_preflife = 0;
694d04ccbb3Scarlsonj d6ia.d6ia_vallife = 0;
695d04ccbb3Scarlsonj } else {
696d04ccbb3Scarlsonj d6ia.d6ia_preflife = htonl(lif->lif_preferred.dt_start);
697d04ccbb3Scarlsonj d6ia.d6ia_vallife = htonl(lif->lif_expire.dt_start);
698d04ccbb3Scarlsonj }
699d04ccbb3Scarlsonj d6so = add_pkt_subopt(dpkt, d6o, DHCPV6_OPT_IAADDR,
700d04ccbb3Scarlsonj (dhcpv6_option_t *)&d6ia + 1,
701d04ccbb3Scarlsonj sizeof (d6ia) - sizeof (*d6o));
702d04ccbb3Scarlsonj if (d6so == NULL)
703d04ccbb3Scarlsonj return (B_FALSE);
704d04ccbb3Scarlsonj
705d04ccbb3Scarlsonj /*
706d04ccbb3Scarlsonj * Add a status code suboption to the IAADDR to tell the server
707d04ccbb3Scarlsonj * why we're declining the address. Note that we must manually
708d04ccbb3Scarlsonj * update the enclosing IA_NA, as add_pkt_subopt doesn't know
709d04ccbb3Scarlsonj * how to do that.
710d04ccbb3Scarlsonj */
711d04ccbb3Scarlsonj if (status != DHCPV6_STAT_SUCCESS || msg != NULL) {
712d04ccbb3Scarlsonj olen = sizeof (*statusopt) +
713d04ccbb3Scarlsonj (msg == NULL ? 0 : strlen(msg));
714d04ccbb3Scarlsonj statusopt = alloca(olen);
715d04ccbb3Scarlsonj *statusopt = htons(status);
716d04ccbb3Scarlsonj if (msg != NULL) {
717d04ccbb3Scarlsonj (void) memcpy((char *)(statusopt + 1), msg,
718d04ccbb3Scarlsonj olen - sizeof (*statusopt));
719d04ccbb3Scarlsonj }
720d04ccbb3Scarlsonj d6so = add_pkt_subopt(dpkt, d6so,
721d04ccbb3Scarlsonj DHCPV6_OPT_STATUS_CODE, statusopt, olen);
722d04ccbb3Scarlsonj if (d6so != NULL) {
723d04ccbb3Scarlsonj /*
724d04ccbb3Scarlsonj * Update for length of suboption header and
725d04ccbb3Scarlsonj * suboption contents.
726d04ccbb3Scarlsonj */
727d04ccbb3Scarlsonj (void) update_v6opt_len(d6o, sizeof (*d6so) +
728d04ccbb3Scarlsonj olen);
729d04ccbb3Scarlsonj }
730d04ccbb3Scarlsonj }
731d04ccbb3Scarlsonj } else {
732d04ccbb3Scarlsonj /*
733d04ccbb3Scarlsonj * For DECLINE, we need to add the CD_REQUESTED_IP_ADDR option.
734d04ccbb3Scarlsonj * In all other cases (RELEASE and REQUEST), we need to set
735d04ccbb3Scarlsonj * ciadr.
736d04ccbb3Scarlsonj */
737d04ccbb3Scarlsonj if (pkt_send_type(dpkt) == DECLINE) {
738d04ccbb3Scarlsonj if (!add_pkt_opt32(dpkt, CD_REQUESTED_IP_ADDR,
739d04ccbb3Scarlsonj lif->lif_addr))
740d04ccbb3Scarlsonj return (B_FALSE);
741d04ccbb3Scarlsonj } else {
742d04ccbb3Scarlsonj dpkt->pkt->ciaddr.s_addr = lif->lif_addr;
743d04ccbb3Scarlsonj }
744d04ccbb3Scarlsonj
745d04ccbb3Scarlsonj /*
746d04ccbb3Scarlsonj * It's not too worrisome if the message fails to fit in the
747d04ccbb3Scarlsonj * packet. The result will still be valid.
748d04ccbb3Scarlsonj */
749d04ccbb3Scarlsonj if (msg != NULL)
750d04ccbb3Scarlsonj (void) add_pkt_opt(dpkt, CD_MESSAGE, msg,
751d04ccbb3Scarlsonj strlen(msg) + 1);
752d04ccbb3Scarlsonj }
753d04ccbb3Scarlsonj return (B_TRUE);
754d04ccbb3Scarlsonj }
755d04ccbb3Scarlsonj
756d04ccbb3Scarlsonj /*
757d04ccbb3Scarlsonj * free_pkt_entry(): frees a packet list list entry
758d04ccbb3Scarlsonj *
759d04ccbb3Scarlsonj * input: PKT_LIST *: the packet list entry to free
7607c478bd9Sstevel@tonic-gate * output: void
7617c478bd9Sstevel@tonic-gate */
7627c478bd9Sstevel@tonic-gate void
free_pkt_entry(PKT_LIST * plp)763d04ccbb3Scarlsonj free_pkt_entry(PKT_LIST *plp)
7647c478bd9Sstevel@tonic-gate {
765d04ccbb3Scarlsonj if (plp != NULL) {
766d04ccbb3Scarlsonj free(plp->pkt);
767d04ccbb3Scarlsonj free(plp);
768d04ccbb3Scarlsonj }
7697c478bd9Sstevel@tonic-gate }
7707c478bd9Sstevel@tonic-gate
7717c478bd9Sstevel@tonic-gate /*
772d04ccbb3Scarlsonj * free_pkt_list(): frees an entire packet list
7737c478bd9Sstevel@tonic-gate *
7747c478bd9Sstevel@tonic-gate * input: PKT_LIST **: the packet list to free
7757c478bd9Sstevel@tonic-gate * output: void
7767c478bd9Sstevel@tonic-gate */
7777c478bd9Sstevel@tonic-gate
7787c478bd9Sstevel@tonic-gate void
free_pkt_list(PKT_LIST ** head)779d04ccbb3Scarlsonj free_pkt_list(PKT_LIST **head)
7807c478bd9Sstevel@tonic-gate {
781d04ccbb3Scarlsonj PKT_LIST *plp;
7827c478bd9Sstevel@tonic-gate
783d04ccbb3Scarlsonj while ((plp = *head) != NULL) {
784d04ccbb3Scarlsonj remque(plp);
785d04ccbb3Scarlsonj free_pkt_entry(plp);
7867c478bd9Sstevel@tonic-gate }
7877c478bd9Sstevel@tonic-gate }
7887c478bd9Sstevel@tonic-gate
7897c478bd9Sstevel@tonic-gate /*
7907c478bd9Sstevel@tonic-gate * send_pkt_internal(): sends a packet out on an interface
7917c478bd9Sstevel@tonic-gate *
792d04ccbb3Scarlsonj * input: dhcp_smach_t *: the state machine with a packet to send
793d04ccbb3Scarlsonj * output: boolean_t: B_TRUE if the packet is sent, B_FALSE otherwise
7947c478bd9Sstevel@tonic-gate */
7957c478bd9Sstevel@tonic-gate
796d04ccbb3Scarlsonj static boolean_t
send_pkt_internal(dhcp_smach_t * dsmp)797d04ccbb3Scarlsonj send_pkt_internal(dhcp_smach_t *dsmp)
7987c478bd9Sstevel@tonic-gate {
7997c478bd9Sstevel@tonic-gate ssize_t n_bytes;
800d04ccbb3Scarlsonj dhcp_lif_t *lif = dsmp->dsm_lif;
801d04ccbb3Scarlsonj dhcp_pkt_t *dpkt = &dsmp->dsm_send_pkt;
802d04ccbb3Scarlsonj uchar_t ptype = pkt_send_type(dpkt);
803d04ccbb3Scarlsonj const char *pkt_name;
804d04ccbb3Scarlsonj struct iovec iov;
805d04ccbb3Scarlsonj struct msghdr msg;
806d04ccbb3Scarlsonj struct cmsghdr *cmsg;
807d04ccbb3Scarlsonj struct in6_pktinfo *ipi6;
808d04ccbb3Scarlsonj boolean_t ismcast;
809e704a8f2Smeem int msgtype;
810d04ccbb3Scarlsonj
811d04ccbb3Scarlsonj /*
812d04ccbb3Scarlsonj * Timer should not be running at the point we go to send a packet.
813d04ccbb3Scarlsonj */
814d04ccbb3Scarlsonj if (dsmp->dsm_retrans_timer != -1) {
815d04ccbb3Scarlsonj dhcpmsg(MSG_CRIT, "send_pkt_internal: unexpected retransmit "
816d04ccbb3Scarlsonj "timer on %s", dsmp->dsm_name);
817d04ccbb3Scarlsonj stop_pkt_retransmission(dsmp);
818d04ccbb3Scarlsonj }
819d04ccbb3Scarlsonj
820d04ccbb3Scarlsonj pkt_name = pkt_type_to_string(ptype, dpkt->pkt_isv6);
8217c478bd9Sstevel@tonic-gate
8227c478bd9Sstevel@tonic-gate /*
8237c478bd9Sstevel@tonic-gate * if needed, schedule a retransmission timer, then attempt to
8247c478bd9Sstevel@tonic-gate * send the packet. if we fail, then log the error. our
8257c478bd9Sstevel@tonic-gate * return value should indicate whether or not we were
8267c478bd9Sstevel@tonic-gate * successful in sending the request, independent of whether
8277c478bd9Sstevel@tonic-gate * we could schedule a timer.
8287c478bd9Sstevel@tonic-gate */
8297c478bd9Sstevel@tonic-gate
830d04ccbb3Scarlsonj if (dsmp->dsm_send_timeout != 0) {
831d04ccbb3Scarlsonj if ((dsmp->dsm_retrans_timer = iu_schedule_timer_ms(tq,
832d04ccbb3Scarlsonj dsmp->dsm_send_timeout, retransmit, dsmp)) == -1)
8337c478bd9Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "send_pkt_internal: cannot "
8347c478bd9Sstevel@tonic-gate "schedule retransmit timer for %s packet",
8357c478bd9Sstevel@tonic-gate pkt_name);
8367c478bd9Sstevel@tonic-gate else
837d04ccbb3Scarlsonj hold_smach(dsmp);
8387c478bd9Sstevel@tonic-gate }
8397c478bd9Sstevel@tonic-gate
840d04ccbb3Scarlsonj if (dpkt->pkt_isv6) {
841d04ccbb3Scarlsonj hrtime_t delta;
842d04ccbb3Scarlsonj
843d04ccbb3Scarlsonj /*
844d04ccbb3Scarlsonj * Convert current time into centiseconds since transaction
845d04ccbb3Scarlsonj * started. This is what DHCPv6 expects to see in the Elapsed
846d04ccbb3Scarlsonj * Time option.
847d04ccbb3Scarlsonj */
848d04ccbb3Scarlsonj delta = (gethrtime() - dsmp->dsm_neg_hrtime) /
849d04ccbb3Scarlsonj (NANOSEC / 100);
850d04ccbb3Scarlsonj if (delta > DHCPV6_FOREVER)
851d04ccbb3Scarlsonj delta = DHCPV6_FOREVER;
852d04ccbb3Scarlsonj (void) remove_pkt_opt(dpkt, DHCPV6_OPT_ELAPSED_TIME);
853d04ccbb3Scarlsonj (void) add_pkt_opt16(dpkt, DHCPV6_OPT_ELAPSED_TIME,
854d04ccbb3Scarlsonj htons(delta));
855d04ccbb3Scarlsonj } else {
8567c478bd9Sstevel@tonic-gate /*
8577c478bd9Sstevel@tonic-gate * set the `pkt->secs' field depending on the type of packet.
8587c478bd9Sstevel@tonic-gate * it should be zero, except in the following cases:
8597c478bd9Sstevel@tonic-gate *
8607c478bd9Sstevel@tonic-gate * DISCOVER: set to the number of seconds since we started
8617c478bd9Sstevel@tonic-gate * trying to obtain a lease.
8627c478bd9Sstevel@tonic-gate *
8637c478bd9Sstevel@tonic-gate * INFORM: set to the number of seconds since we started
8647c478bd9Sstevel@tonic-gate * trying to get configuration parameters.
8657c478bd9Sstevel@tonic-gate *
8667c478bd9Sstevel@tonic-gate * REQUEST: if in the REQUESTING state, then same value as
8677c478bd9Sstevel@tonic-gate * DISCOVER, otherwise the number of seconds
8687c478bd9Sstevel@tonic-gate * since we started trying to obtain a lease.
8697c478bd9Sstevel@tonic-gate *
870d04ccbb3Scarlsonj * we also set `dsm_newstart_monosec', to the time we sent a
8717c478bd9Sstevel@tonic-gate * REQUEST or DISCOVER packet, so we know the lease start
8727c478bd9Sstevel@tonic-gate * time (the DISCOVER case is for handling BOOTP servers).
8737c478bd9Sstevel@tonic-gate */
8747c478bd9Sstevel@tonic-gate
875d04ccbb3Scarlsonj switch (ptype) {
8767c478bd9Sstevel@tonic-gate
8777c478bd9Sstevel@tonic-gate case DISCOVER:
878d04ccbb3Scarlsonj dsmp->dsm_newstart_monosec = monosec();
879d04ccbb3Scarlsonj dsmp->dsm_disc_secs = dsmp->dsm_newstart_monosec -
880d04ccbb3Scarlsonj hrtime_to_monosec(dsmp->dsm_neg_hrtime);
881d04ccbb3Scarlsonj dpkt->pkt->secs = htons(dsmp->dsm_disc_secs);
8827c478bd9Sstevel@tonic-gate break;
8837c478bd9Sstevel@tonic-gate
8847c478bd9Sstevel@tonic-gate case INFORM:
885d04ccbb3Scarlsonj dpkt->pkt->secs = htons(monosec() -
886d04ccbb3Scarlsonj hrtime_to_monosec(dsmp->dsm_neg_hrtime));
8877c478bd9Sstevel@tonic-gate break;
8887c478bd9Sstevel@tonic-gate
8897c478bd9Sstevel@tonic-gate case REQUEST:
890d04ccbb3Scarlsonj dsmp->dsm_newstart_monosec = monosec();
8917c478bd9Sstevel@tonic-gate
892d04ccbb3Scarlsonj if (dsmp->dsm_state == REQUESTING) {
893d04ccbb3Scarlsonj dpkt->pkt->secs = htons(dsmp->dsm_disc_secs);
8947c478bd9Sstevel@tonic-gate break;
8957c478bd9Sstevel@tonic-gate }
8967c478bd9Sstevel@tonic-gate
897d04ccbb3Scarlsonj dpkt->pkt->secs = htons(monosec() -
898d04ccbb3Scarlsonj hrtime_to_monosec(dsmp->dsm_neg_hrtime));
8997c478bd9Sstevel@tonic-gate break;
9007c478bd9Sstevel@tonic-gate
9017c478bd9Sstevel@tonic-gate default:
9027c478bd9Sstevel@tonic-gate dpkt->pkt->secs = htons(0);
903d04ccbb3Scarlsonj break;
904d04ccbb3Scarlsonj }
9057c478bd9Sstevel@tonic-gate }
9067c478bd9Sstevel@tonic-gate
907d04ccbb3Scarlsonj if (dpkt->pkt_isv6) {
908d04ccbb3Scarlsonj struct sockaddr_in6 sin6;
9097c478bd9Sstevel@tonic-gate
910d04ccbb3Scarlsonj (void) memset(&iov, 0, sizeof (iov));
911d04ccbb3Scarlsonj iov.iov_base = dpkt->pkt;
912d04ccbb3Scarlsonj iov.iov_len = dpkt->pkt_cur_len;
913d04ccbb3Scarlsonj
914d04ccbb3Scarlsonj (void) memset(&msg, 0, sizeof (msg));
915d04ccbb3Scarlsonj msg.msg_name = &dsmp->dsm_send_dest.v6;
916d04ccbb3Scarlsonj msg.msg_namelen = sizeof (struct sockaddr_in6);
917d04ccbb3Scarlsonj msg.msg_iov = &iov;
918d04ccbb3Scarlsonj msg.msg_iovlen = 1;
919d04ccbb3Scarlsonj
920d04ccbb3Scarlsonj /*
921d04ccbb3Scarlsonj * If the address that's requested cannot be reached, then fall
922d04ccbb3Scarlsonj * back to the multcast address.
923d04ccbb3Scarlsonj */
924d04ccbb3Scarlsonj if (IN6_IS_ADDR_MULTICAST(&dsmp->dsm_send_dest.v6.sin6_addr)) {
925d04ccbb3Scarlsonj ismcast = B_TRUE;
926d04ccbb3Scarlsonj } else {
927d04ccbb3Scarlsonj struct dstinforeq dinfo;
928d04ccbb3Scarlsonj struct strioctl str;
929d04ccbb3Scarlsonj
930d04ccbb3Scarlsonj ismcast = B_FALSE;
931d04ccbb3Scarlsonj (void) memset(&dinfo, 0, sizeof (dinfo));
932d04ccbb3Scarlsonj dinfo.dir_daddr = dsmp->dsm_send_dest.v6.sin6_addr;
933d04ccbb3Scarlsonj str.ic_cmd = SIOCGDSTINFO;
934d04ccbb3Scarlsonj str.ic_timout = 0;
935d04ccbb3Scarlsonj str.ic_len = sizeof (dinfo);
936d04ccbb3Scarlsonj str.ic_dp = (char *)&dinfo;
937d04ccbb3Scarlsonj if (ioctl(v6_sock_fd, I_STR, &str) == -1) {
938d04ccbb3Scarlsonj dhcpmsg(MSG_ERR,
939d04ccbb3Scarlsonj "send_pkt_internal: ioctl SIOCGDSTINFO");
940d04ccbb3Scarlsonj } else if (!dinfo.dir_dreachable) {
941d04ccbb3Scarlsonj char abuf[INET6_ADDRSTRLEN];
942d04ccbb3Scarlsonj
943d04ccbb3Scarlsonj dhcpmsg(MSG_DEBUG, "send_pkt_internal: %s is "
944d04ccbb3Scarlsonj "not reachable; using multicast instead",
945d04ccbb3Scarlsonj inet_ntop(AF_INET6, &dinfo.dir_daddr, abuf,
946d04ccbb3Scarlsonj sizeof (abuf)));
947d04ccbb3Scarlsonj sin6 = dsmp->dsm_send_dest.v6;
948d04ccbb3Scarlsonj sin6.sin6_addr =
949d04ccbb3Scarlsonj ipv6_all_dhcp_relay_and_servers;
950d04ccbb3Scarlsonj msg.msg_name = &sin6;
951d04ccbb3Scarlsonj ismcast = B_TRUE;
952d04ccbb3Scarlsonj }
953d04ccbb3Scarlsonj }
954d04ccbb3Scarlsonj
955d04ccbb3Scarlsonj /*
956d04ccbb3Scarlsonj * Make room for our ancillary data option as well as a dummy
957d04ccbb3Scarlsonj * option used by CMSG_NXTHDR.
958d04ccbb3Scarlsonj */
959d04ccbb3Scarlsonj msg.msg_controllen = sizeof (*cmsg) + _MAX_ALIGNMENT +
960d04ccbb3Scarlsonj sizeof (*ipi6) + _MAX_ALIGNMENT + sizeof (*cmsg);
961d04ccbb3Scarlsonj msg.msg_control = alloca(msg.msg_controllen);
962d04ccbb3Scarlsonj cmsg = CMSG_FIRSTHDR(&msg);
963d04ccbb3Scarlsonj cmsg->cmsg_level = IPPROTO_IPV6;
964d04ccbb3Scarlsonj cmsg->cmsg_type = IPV6_PKTINFO;
965d04ccbb3Scarlsonj /* LINTED: alignment */
966d04ccbb3Scarlsonj ipi6 = (struct in6_pktinfo *)CMSG_DATA(cmsg);
967d04ccbb3Scarlsonj if (ismcast)
968d04ccbb3Scarlsonj ipi6->ipi6_addr = lif->lif_v6addr;
969d04ccbb3Scarlsonj else
970d04ccbb3Scarlsonj ipi6->ipi6_addr = my_in6addr_any;
971*e11c3f44Smeem if (lif->lif_pif->pif_under_ipmp)
972*e11c3f44Smeem ipi6->ipi6_ifindex = lif->lif_pif->pif_grindex;
973*e11c3f44Smeem else
974d04ccbb3Scarlsonj ipi6->ipi6_ifindex = lif->lif_pif->pif_index;
975d04ccbb3Scarlsonj cmsg->cmsg_len = (char *)(ipi6 + 1) - (char *)cmsg;
976d04ccbb3Scarlsonj
977d04ccbb3Scarlsonj /*
978d04ccbb3Scarlsonj * Now correct the control message length.
979d04ccbb3Scarlsonj */
980d04ccbb3Scarlsonj cmsg = CMSG_NXTHDR(&msg, cmsg);
981d04ccbb3Scarlsonj msg.msg_controllen = (char *)cmsg - (char *)msg.msg_control;
982d04ccbb3Scarlsonj
983d04ccbb3Scarlsonj n_bytes = sendmsg(v6_sock_fd, &msg, 0);
984d04ccbb3Scarlsonj } else {
985d04ccbb3Scarlsonj n_bytes = sendto(lif->lif_sock_ip_fd, dpkt->pkt,
9867c478bd9Sstevel@tonic-gate dpkt->pkt_cur_len, 0,
987d04ccbb3Scarlsonj (struct sockaddr *)&dsmp->dsm_send_dest.v4,
9887c478bd9Sstevel@tonic-gate sizeof (struct sockaddr_in));
989d04ccbb3Scarlsonj }
9907c478bd9Sstevel@tonic-gate
9917c478bd9Sstevel@tonic-gate if (n_bytes != dpkt->pkt_cur_len) {
992e704a8f2Smeem msgtype = (n_bytes == -1) ? MSG_ERR : MSG_WARNING;
993d04ccbb3Scarlsonj if (dsmp->dsm_retrans_timer == -1)
994e704a8f2Smeem dhcpmsg(msgtype, "send_pkt_internal: cannot send "
9957c478bd9Sstevel@tonic-gate "%s packet to server", pkt_name);
9967c478bd9Sstevel@tonic-gate else
997e704a8f2Smeem dhcpmsg(msgtype, "send_pkt_internal: cannot send "
9987c478bd9Sstevel@tonic-gate "%s packet to server (will retry in %u seconds)",
999d04ccbb3Scarlsonj pkt_name, dsmp->dsm_send_timeout / MILLISEC);
1000d04ccbb3Scarlsonj return (B_FALSE);
10017c478bd9Sstevel@tonic-gate }
10027c478bd9Sstevel@tonic-gate
1003d04ccbb3Scarlsonj dhcpmsg(MSG_VERBOSE, "sent %s xid %x packet out %s", pkt_name,
1004d04ccbb3Scarlsonj pkt_get_xid(dpkt->pkt, dpkt->pkt_isv6), dsmp->dsm_name);
10057c478bd9Sstevel@tonic-gate
1006d04ccbb3Scarlsonj dsmp->dsm_packet_sent++;
1007d04ccbb3Scarlsonj dsmp->dsm_sent++;
1008d04ccbb3Scarlsonj return (B_TRUE);
10097c478bd9Sstevel@tonic-gate }
10107c478bd9Sstevel@tonic-gate
10117c478bd9Sstevel@tonic-gate /*
1012d04ccbb3Scarlsonj * send_pkt(): sends a packet out
10137c478bd9Sstevel@tonic-gate *
1014d04ccbb3Scarlsonj * input: dhcp_smach_t *: the state machine sending the packet
10157c478bd9Sstevel@tonic-gate * dhcp_pkt_t *: the packet to send out
10167c478bd9Sstevel@tonic-gate * in_addr_t: the destination IP address for the packet
10177c478bd9Sstevel@tonic-gate * stop_func_t *: a pointer to function to indicate when to stop
10187c478bd9Sstevel@tonic-gate * retransmitting the packet (if NULL, packet is
10197c478bd9Sstevel@tonic-gate * not retransmitted)
1020d04ccbb3Scarlsonj * output: boolean_t: B_TRUE if the packet was sent, B_FALSE otherwise
10217c478bd9Sstevel@tonic-gate */
10227c478bd9Sstevel@tonic-gate
1023d04ccbb3Scarlsonj boolean_t
send_pkt(dhcp_smach_t * dsmp,dhcp_pkt_t * dpkt,in_addr_t dest,stop_func_t * stop)1024d04ccbb3Scarlsonj send_pkt(dhcp_smach_t *dsmp, dhcp_pkt_t *dpkt, in_addr_t dest,
10257c478bd9Sstevel@tonic-gate stop_func_t *stop)
10267c478bd9Sstevel@tonic-gate {
10277c478bd9Sstevel@tonic-gate /*
10287c478bd9Sstevel@tonic-gate * packets must be at least sizeof (PKT) or they may be dropped
10297c478bd9Sstevel@tonic-gate * by routers. pad out the packet in this case.
10307c478bd9Sstevel@tonic-gate */
10317c478bd9Sstevel@tonic-gate
10327c478bd9Sstevel@tonic-gate dpkt->pkt_cur_len = MAX(dpkt->pkt_cur_len, sizeof (PKT));
10337c478bd9Sstevel@tonic-gate
1034d04ccbb3Scarlsonj dsmp->dsm_packet_sent = 0;
10357c478bd9Sstevel@tonic-gate
1036d04ccbb3Scarlsonj (void) memset(&dsmp->dsm_send_dest.v4, 0,
1037d04ccbb3Scarlsonj sizeof (dsmp->dsm_send_dest.v4));
1038d04ccbb3Scarlsonj dsmp->dsm_send_dest.v4.sin_addr.s_addr = dest;
1039d04ccbb3Scarlsonj dsmp->dsm_send_dest.v4.sin_family = AF_INET;
1040d04ccbb3Scarlsonj dsmp->dsm_send_dest.v4.sin_port = htons(IPPORT_BOOTPS);
1041d04ccbb3Scarlsonj dsmp->dsm_send_stop_func = stop;
10427c478bd9Sstevel@tonic-gate
10437c478bd9Sstevel@tonic-gate /*
10447c478bd9Sstevel@tonic-gate * TODO: dispose of this gruesome assumption (there's no real
10457c478bd9Sstevel@tonic-gate * technical gain from doing so, but it would be cleaner)
10467c478bd9Sstevel@tonic-gate */
10477c478bd9Sstevel@tonic-gate
1048d04ccbb3Scarlsonj assert(dpkt == &dsmp->dsm_send_pkt);
10497c478bd9Sstevel@tonic-gate
10507c478bd9Sstevel@tonic-gate /*
10517c478bd9Sstevel@tonic-gate * clear out any packets which had been previously received
10527c478bd9Sstevel@tonic-gate * but not pulled off of the recv_packet queue.
10537c478bd9Sstevel@tonic-gate */
10547c478bd9Sstevel@tonic-gate
1055d04ccbb3Scarlsonj free_pkt_list(&dsmp->dsm_recv_pkt_list);
1056d04ccbb3Scarlsonj
1057d04ccbb3Scarlsonj if (stop == NULL)
1058d04ccbb3Scarlsonj dsmp->dsm_send_timeout = 0; /* prevents retransmissions */
1059d04ccbb3Scarlsonj else
1060d04ccbb3Scarlsonj next_retransmission(dsmp, B_TRUE, B_FALSE);
1061d04ccbb3Scarlsonj
1062d04ccbb3Scarlsonj return (send_pkt_internal(dsmp));
1063d04ccbb3Scarlsonj }
1064d04ccbb3Scarlsonj
1065d04ccbb3Scarlsonj /*
1066d04ccbb3Scarlsonj * send_pkt_v6(): sends a DHCPv6 packet out
1067d04ccbb3Scarlsonj *
1068d04ccbb3Scarlsonj * input: dhcp_smach_t *: the state machine sending the packet
1069d04ccbb3Scarlsonj * dhcp_pkt_t *: the packet to send out
1070d04ccbb3Scarlsonj * in6_addr_t: the destination IPv6 address for the packet
1071d04ccbb3Scarlsonj * stop_func_t *: a pointer to function to indicate when to stop
1072d04ccbb3Scarlsonj * retransmitting the packet (if NULL, packet is
1073d04ccbb3Scarlsonj * not retransmitted)
1074d04ccbb3Scarlsonj * uint_t: Initial Retransmit Timer value
1075d04ccbb3Scarlsonj * uint_t: Maximum Retransmit Timer value, zero if none
1076d04ccbb3Scarlsonj * output: boolean_t: B_TRUE if the packet was sent, B_FALSE otherwise
1077d04ccbb3Scarlsonj */
1078d04ccbb3Scarlsonj
1079d04ccbb3Scarlsonj boolean_t
send_pkt_v6(dhcp_smach_t * dsmp,dhcp_pkt_t * dpkt,in6_addr_t dest,stop_func_t * stop,uint_t irt,uint_t mrt)1080d04ccbb3Scarlsonj send_pkt_v6(dhcp_smach_t *dsmp, dhcp_pkt_t *dpkt, in6_addr_t dest,
1081d04ccbb3Scarlsonj stop_func_t *stop, uint_t irt, uint_t mrt)
1082d04ccbb3Scarlsonj {
1083d04ccbb3Scarlsonj dsmp->dsm_packet_sent = 0;
1084d04ccbb3Scarlsonj
1085d04ccbb3Scarlsonj (void) memset(&dsmp->dsm_send_dest.v6, 0,
1086d04ccbb3Scarlsonj sizeof (dsmp->dsm_send_dest.v6));
1087d04ccbb3Scarlsonj dsmp->dsm_send_dest.v6.sin6_addr = dest;
1088d04ccbb3Scarlsonj dsmp->dsm_send_dest.v6.sin6_family = AF_INET6;
1089d04ccbb3Scarlsonj dsmp->dsm_send_dest.v6.sin6_port = htons(IPPORT_DHCPV6S);
1090d04ccbb3Scarlsonj dsmp->dsm_send_stop_func = stop;
1091d04ccbb3Scarlsonj
1092d04ccbb3Scarlsonj /*
1093d04ccbb3Scarlsonj * TODO: dispose of this gruesome assumption (there's no real
1094d04ccbb3Scarlsonj * technical gain from doing so, but it would be cleaner)
1095d04ccbb3Scarlsonj */
1096d04ccbb3Scarlsonj
1097d04ccbb3Scarlsonj assert(dpkt == &dsmp->dsm_send_pkt);
1098d04ccbb3Scarlsonj
1099d04ccbb3Scarlsonj /*
1100d04ccbb3Scarlsonj * clear out any packets which had been previously received
1101d04ccbb3Scarlsonj * but not pulled off of the recv_packet queue.
1102d04ccbb3Scarlsonj */
1103d04ccbb3Scarlsonj
1104d04ccbb3Scarlsonj free_pkt_list(&dsmp->dsm_recv_pkt_list);
11057c478bd9Sstevel@tonic-gate
11067c478bd9Sstevel@tonic-gate if (stop == NULL) {
1107d04ccbb3Scarlsonj dsmp->dsm_send_timeout = 0; /* prevents retransmissions */
1108d04ccbb3Scarlsonj } else {
1109d04ccbb3Scarlsonj dsmp->dsm_send_timeout = irt;
1110d04ccbb3Scarlsonj dsmp->dsm_send_tcenter = mrt;
1111d04ccbb3Scarlsonj /*
1112d04ccbb3Scarlsonj * This is quite ugly, but RFC 3315 section 17.1.2 requires
1113d04ccbb3Scarlsonj * that the RAND value for the very first retransmission of a
1114d04ccbb3Scarlsonj * Solicit message is strictly greater than zero.
1115d04ccbb3Scarlsonj */
1116d04ccbb3Scarlsonj next_retransmission(dsmp, B_TRUE,
1117d04ccbb3Scarlsonj pkt_send_type(dpkt) == DHCPV6_MSG_SOLICIT);
1118d04ccbb3Scarlsonj }
11197c478bd9Sstevel@tonic-gate
1120d04ccbb3Scarlsonj return (send_pkt_internal(dsmp));
11217c478bd9Sstevel@tonic-gate }
11227c478bd9Sstevel@tonic-gate
11237c478bd9Sstevel@tonic-gate /*
11247c478bd9Sstevel@tonic-gate * retransmit(): retransmits the current packet on an interface
11257c478bd9Sstevel@tonic-gate *
11267c478bd9Sstevel@tonic-gate * input: iu_tq_t *: unused
1127d04ccbb3Scarlsonj * void *: the dhcp_smach_t * (state machine) sending a packet
11287c478bd9Sstevel@tonic-gate * output: void
11297c478bd9Sstevel@tonic-gate */
11307c478bd9Sstevel@tonic-gate
11317c478bd9Sstevel@tonic-gate /* ARGSUSED */
11327c478bd9Sstevel@tonic-gate static void
retransmit(iu_tq_t * tqp,void * arg)11337c478bd9Sstevel@tonic-gate retransmit(iu_tq_t *tqp, void *arg)
11347c478bd9Sstevel@tonic-gate {
1135d04ccbb3Scarlsonj dhcp_smach_t *dsmp = arg;
11367c478bd9Sstevel@tonic-gate
1137d04ccbb3Scarlsonj dsmp->dsm_retrans_timer = -1;
1138d04ccbb3Scarlsonj
1139d04ccbb3Scarlsonj if (!verify_smach(dsmp))
11407c478bd9Sstevel@tonic-gate return;
11417c478bd9Sstevel@tonic-gate
11427c478bd9Sstevel@tonic-gate /*
1143d04ccbb3Scarlsonj * Check the callback to see if we should keep sending retransmissions.
1144d04ccbb3Scarlsonj * Compute the next retransmission time first, so that the callback can
1145d04ccbb3Scarlsonj * cap the value if need be. (Required for DHCPv6 Confirm messages.)
1146d04ccbb3Scarlsonj *
1147d04ccbb3Scarlsonj * Hold the state machine across the callback so that the called
1148d04ccbb3Scarlsonj * function can remove the state machine from the system without
1149d04ccbb3Scarlsonj * disturbing the string used subsequently for verbose logging. The
1150d04ccbb3Scarlsonj * Release function destroys the state machine when the retry count
1151d04ccbb3Scarlsonj * expires.
11527c478bd9Sstevel@tonic-gate */
11537c478bd9Sstevel@tonic-gate
1154d04ccbb3Scarlsonj next_retransmission(dsmp, B_FALSE, B_FALSE);
1155d04ccbb3Scarlsonj hold_smach(dsmp);
1156d04ccbb3Scarlsonj if (dsmp->dsm_send_stop_func(dsmp, dsmp->dsm_packet_sent)) {
1157d04ccbb3Scarlsonj dhcpmsg(MSG_VERBOSE, "retransmit: time to stop on %s",
1158d04ccbb3Scarlsonj dsmp->dsm_name);
1159d04ccbb3Scarlsonj } else {
1160d04ccbb3Scarlsonj dhcpmsg(MSG_VERBOSE, "retransmit: sending another on %s",
1161d04ccbb3Scarlsonj dsmp->dsm_name);
1162d04ccbb3Scarlsonj (void) send_pkt_internal(dsmp);
1163d04ccbb3Scarlsonj }
1164d04ccbb3Scarlsonj release_smach(dsmp);
11657c478bd9Sstevel@tonic-gate }
11667c478bd9Sstevel@tonic-gate
11677c478bd9Sstevel@tonic-gate /*
11687c478bd9Sstevel@tonic-gate * stop_pkt_retransmission(): stops retransmission of last sent packet
11697c478bd9Sstevel@tonic-gate *
1170d04ccbb3Scarlsonj * input: dhcp_smach_t *: the state machine to stop retransmission on
11717c478bd9Sstevel@tonic-gate * output: void
11727c478bd9Sstevel@tonic-gate */
11737c478bd9Sstevel@tonic-gate
11747c478bd9Sstevel@tonic-gate void
stop_pkt_retransmission(dhcp_smach_t * dsmp)1175d04ccbb3Scarlsonj stop_pkt_retransmission(dhcp_smach_t *dsmp)
11767c478bd9Sstevel@tonic-gate {
1177d04ccbb3Scarlsonj if (dsmp->dsm_retrans_timer != -1 &&
1178d04ccbb3Scarlsonj iu_cancel_timer(tq, dsmp->dsm_retrans_timer, NULL) == 1) {
1179d04ccbb3Scarlsonj dhcpmsg(MSG_VERBOSE, "stop_pkt_retransmission: stopped on %s",
1180d04ccbb3Scarlsonj dsmp->dsm_name);
1181d04ccbb3Scarlsonj dsmp->dsm_retrans_timer = -1;
1182d04ccbb3Scarlsonj release_smach(dsmp);
11837c478bd9Sstevel@tonic-gate }
11847c478bd9Sstevel@tonic-gate }
11857c478bd9Sstevel@tonic-gate
11867c478bd9Sstevel@tonic-gate /*
1187d04ccbb3Scarlsonj * retransmit_now(): force a packet retransmission right now. Used only with
1188d04ccbb3Scarlsonj * the DHCPv6 UseMulticast status code. Use with caution;
1189d04ccbb3Scarlsonj * triggered retransmissions can cause packet storms.
11907c478bd9Sstevel@tonic-gate *
1191d04ccbb3Scarlsonj * input: dhcp_smach_t *: the state machine to force retransmission on
1192d04ccbb3Scarlsonj * output: void
11937c478bd9Sstevel@tonic-gate */
11947c478bd9Sstevel@tonic-gate
1195d04ccbb3Scarlsonj void
retransmit_now(dhcp_smach_t * dsmp)1196d04ccbb3Scarlsonj retransmit_now(dhcp_smach_t *dsmp)
1197d04ccbb3Scarlsonj {
1198d04ccbb3Scarlsonj stop_pkt_retransmission(dsmp);
1199d04ccbb3Scarlsonj (void) send_pkt_internal(dsmp);
1200d04ccbb3Scarlsonj }
1201d04ccbb3Scarlsonj
1202d04ccbb3Scarlsonj /*
1203d04ccbb3Scarlsonj * alloc_pkt_entry(): Allocates a packet list entry with a given data area
1204d04ccbb3Scarlsonj * size.
1205d04ccbb3Scarlsonj *
1206d04ccbb3Scarlsonj * input: size_t: size of data area for packet
1207d04ccbb3Scarlsonj * boolean_t: B_TRUE for IPv6
1208d04ccbb3Scarlsonj * output: PKT_LIST *: allocated packet list entry
1209d04ccbb3Scarlsonj */
1210d04ccbb3Scarlsonj
1211d04ccbb3Scarlsonj PKT_LIST *
alloc_pkt_entry(size_t psize,boolean_t isv6)1212d04ccbb3Scarlsonj alloc_pkt_entry(size_t psize, boolean_t isv6)
12137c478bd9Sstevel@tonic-gate {
12147c478bd9Sstevel@tonic-gate PKT_LIST *plp;
1215d04ccbb3Scarlsonj
1216d04ccbb3Scarlsonj if ((plp = calloc(1, sizeof (*plp))) == NULL ||
1217d04ccbb3Scarlsonj (plp->pkt = malloc(psize)) == NULL) {
1218d04ccbb3Scarlsonj free(plp);
1219d04ccbb3Scarlsonj plp = NULL;
1220d04ccbb3Scarlsonj } else {
1221d04ccbb3Scarlsonj plp->len = psize;
1222d04ccbb3Scarlsonj plp->isv6 = isv6;
1223d04ccbb3Scarlsonj }
1224d04ccbb3Scarlsonj
1225d04ccbb3Scarlsonj return (plp);
1226d04ccbb3Scarlsonj }
12277c478bd9Sstevel@tonic-gate
12287c478bd9Sstevel@tonic-gate /*
1229d04ccbb3Scarlsonj * sock_recvpkt(): read from the given socket into an allocated buffer and
1230d04ccbb3Scarlsonj * handles any ancillary data options.
1231d04ccbb3Scarlsonj *
1232d04ccbb3Scarlsonj * input: int: file descriptor to read
1233d04ccbb3Scarlsonj * PKT_LIST *: allocated buffer
1234d04ccbb3Scarlsonj * output: ssize_t: number of bytes read, or -1 on error
12357c478bd9Sstevel@tonic-gate */
12367c478bd9Sstevel@tonic-gate
1237d04ccbb3Scarlsonj static ssize_t
sock_recvpkt(int fd,PKT_LIST * plp)1238d04ccbb3Scarlsonj sock_recvpkt(int fd, PKT_LIST *plp)
1239d04ccbb3Scarlsonj {
1240d04ccbb3Scarlsonj struct iovec iov;
1241d04ccbb3Scarlsonj struct msghdr msg;
12424ee71a50Scarlsonj int64_t ctrl[8192 / sizeof (int64_t)];
1243d04ccbb3Scarlsonj ssize_t msglen;
1244d04ccbb3Scarlsonj
1245d04ccbb3Scarlsonj (void) memset(&iov, 0, sizeof (iov));
1246d04ccbb3Scarlsonj iov.iov_base = (caddr_t)plp->pkt;
1247d04ccbb3Scarlsonj iov.iov_len = plp->len;
1248d04ccbb3Scarlsonj
1249d04ccbb3Scarlsonj (void) memset(&msg, 0, sizeof (msg));
1250d04ccbb3Scarlsonj msg.msg_name = &plp->pktfrom;
1251d04ccbb3Scarlsonj msg.msg_namelen = sizeof (plp->pktfrom);
1252d04ccbb3Scarlsonj msg.msg_iov = &iov;
1253d04ccbb3Scarlsonj msg.msg_iovlen = 1;
1254d04ccbb3Scarlsonj msg.msg_control = ctrl;
1255d04ccbb3Scarlsonj msg.msg_controllen = sizeof (ctrl);
1256d04ccbb3Scarlsonj
1257d04ccbb3Scarlsonj if ((msglen = recvmsg(fd, &msg, 0)) != -1) {
1258d04ccbb3Scarlsonj struct cmsghdr *cmsg;
1259d04ccbb3Scarlsonj
1260d04ccbb3Scarlsonj for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
1261d04ccbb3Scarlsonj cmsg = CMSG_NXTHDR(&msg, cmsg)) {
1262d04ccbb3Scarlsonj struct sockaddr_in *sinp;
1263d04ccbb3Scarlsonj struct sockaddr_in6 *sin6;
1264d04ccbb3Scarlsonj struct in6_pktinfo *ipi6;
1265d04ccbb3Scarlsonj
1266d04ccbb3Scarlsonj switch (cmsg->cmsg_level) {
1267d04ccbb3Scarlsonj case IPPROTO_IP:
1268d04ccbb3Scarlsonj switch (cmsg->cmsg_type) {
1269d04ccbb3Scarlsonj case IP_RECVDSTADDR:
1270d04ccbb3Scarlsonj sinp = (struct sockaddr_in *)
1271d04ccbb3Scarlsonj &plp->pktto;
1272d04ccbb3Scarlsonj sinp->sin_family = AF_INET;
1273d04ccbb3Scarlsonj (void) memcpy(&sinp->sin_addr.s_addr,
1274d04ccbb3Scarlsonj CMSG_DATA(cmsg),
1275d04ccbb3Scarlsonj sizeof (ipaddr_t));
1276d04ccbb3Scarlsonj break;
1277d04ccbb3Scarlsonj
1278d04ccbb3Scarlsonj case IP_RECVIF:
1279d04ccbb3Scarlsonj (void) memcpy(&plp->ifindex,
1280d04ccbb3Scarlsonj CMSG_DATA(cmsg), sizeof (uint_t));
1281d04ccbb3Scarlsonj break;
1282d04ccbb3Scarlsonj }
1283d04ccbb3Scarlsonj break;
1284d04ccbb3Scarlsonj
1285d04ccbb3Scarlsonj case IPPROTO_IPV6:
1286d04ccbb3Scarlsonj switch (cmsg->cmsg_type) {
1287d04ccbb3Scarlsonj case IPV6_PKTINFO:
1288d04ccbb3Scarlsonj /* LINTED: alignment */
1289d04ccbb3Scarlsonj ipi6 = (struct in6_pktinfo *)
1290d04ccbb3Scarlsonj CMSG_DATA(cmsg);
1291d04ccbb3Scarlsonj sin6 = (struct sockaddr_in6 *)
1292d04ccbb3Scarlsonj &plp->pktto;
1293d04ccbb3Scarlsonj sin6->sin6_family = AF_INET6;
1294d04ccbb3Scarlsonj (void) memcpy(&sin6->sin6_addr,
1295d04ccbb3Scarlsonj &ipi6->ipi6_addr,
1296d04ccbb3Scarlsonj sizeof (ipi6->ipi6_addr));
1297d04ccbb3Scarlsonj (void) memcpy(&plp->ifindex,
1298d04ccbb3Scarlsonj &ipi6->ipi6_ifindex,
1299d04ccbb3Scarlsonj sizeof (uint_t));
1300d04ccbb3Scarlsonj break;
1301d04ccbb3Scarlsonj }
1302d04ccbb3Scarlsonj }
1303d04ccbb3Scarlsonj }
1304d04ccbb3Scarlsonj }
1305d04ccbb3Scarlsonj return (msglen);
1306d04ccbb3Scarlsonj }
1307d04ccbb3Scarlsonj
1308d04ccbb3Scarlsonj /*
1309d04ccbb3Scarlsonj * recv_pkt(): receives a single DHCP packet on a given file descriptor.
1310d04ccbb3Scarlsonj *
1311e704a8f2Smeem * input: int: the file descriptor to receive the packet from
1312d04ccbb3Scarlsonj * int: the maximum packet size to allow
1313d04ccbb3Scarlsonj * boolean_t: B_TRUE for IPv6
1314d04ccbb3Scarlsonj * output: PKT_LIST *: the received packet
1315d04ccbb3Scarlsonj */
1316d04ccbb3Scarlsonj
1317d04ccbb3Scarlsonj PKT_LIST *
recv_pkt(int fd,int mtu,boolean_t isv6)1318e704a8f2Smeem recv_pkt(int fd, int mtu, boolean_t isv6)
1319d04ccbb3Scarlsonj {
1320d04ccbb3Scarlsonj PKT_LIST *plp;
1321d04ccbb3Scarlsonj ssize_t retval;
1322d04ccbb3Scarlsonj
1323d04ccbb3Scarlsonj if ((plp = alloc_pkt_entry(mtu, isv6)) == NULL) {
1324d04ccbb3Scarlsonj dhcpmsg(MSG_ERROR,
1325d04ccbb3Scarlsonj "recv_pkt: allocation failure; dropped packet");
1326d04ccbb3Scarlsonj return (NULL);
1327d04ccbb3Scarlsonj }
1328d04ccbb3Scarlsonj
1329d04ccbb3Scarlsonj retval = sock_recvpkt(fd, plp);
1330d04ccbb3Scarlsonj if (retval == -1) {
1331e704a8f2Smeem dhcpmsg(MSG_ERR, "recv_pkt: recvfrom v%d failed, dropped",
1332e704a8f2Smeem isv6 ? 6 : 4);
13337c478bd9Sstevel@tonic-gate goto failure;
13347c478bd9Sstevel@tonic-gate }
13357c478bd9Sstevel@tonic-gate
1336d04ccbb3Scarlsonj plp->len = retval;
13377c478bd9Sstevel@tonic-gate
1338e704a8f2Smeem if (isv6) {
1339d04ccbb3Scarlsonj if (retval < sizeof (dhcpv6_message_t)) {
1340d04ccbb3Scarlsonj dhcpmsg(MSG_WARNING, "recv_pkt: runt message");
1341d04ccbb3Scarlsonj goto failure;
1342d04ccbb3Scarlsonj }
1343d04ccbb3Scarlsonj } else {
13447c478bd9Sstevel@tonic-gate switch (dhcp_options_scan(plp, B_TRUE)) {
13457c478bd9Sstevel@tonic-gate
13467c478bd9Sstevel@tonic-gate case DHCP_WRONG_MSG_TYPE:
1347d04ccbb3Scarlsonj dhcpmsg(MSG_WARNING,
1348d04ccbb3Scarlsonj "recv_pkt: unexpected DHCP message");
13497c478bd9Sstevel@tonic-gate goto failure;
13507c478bd9Sstevel@tonic-gate
13517c478bd9Sstevel@tonic-gate case DHCP_GARBLED_MSG_TYPE:
1352d04ccbb3Scarlsonj dhcpmsg(MSG_WARNING,
1353d04ccbb3Scarlsonj "recv_pkt: garbled DHCP message type");
13547c478bd9Sstevel@tonic-gate goto failure;
13557c478bd9Sstevel@tonic-gate
13567c478bd9Sstevel@tonic-gate case DHCP_BAD_OPT_OVLD:
13577c478bd9Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "recv_pkt: bad option overload");
13587c478bd9Sstevel@tonic-gate goto failure;
13597c478bd9Sstevel@tonic-gate
13607c478bd9Sstevel@tonic-gate case 0:
13617c478bd9Sstevel@tonic-gate break;
13627c478bd9Sstevel@tonic-gate
13637c478bd9Sstevel@tonic-gate default:
1364d04ccbb3Scarlsonj dhcpmsg(MSG_WARNING,
1365d04ccbb3Scarlsonj "recv_pkt: packet corrupted, dropped");
13667c478bd9Sstevel@tonic-gate goto failure;
13677c478bd9Sstevel@tonic-gate }
13687c478bd9Sstevel@tonic-gate }
1369d04ccbb3Scarlsonj return (plp);
13707c478bd9Sstevel@tonic-gate
13717c478bd9Sstevel@tonic-gate failure:
1372d04ccbb3Scarlsonj free_pkt_entry(plp);
1373d04ccbb3Scarlsonj return (NULL);
13747c478bd9Sstevel@tonic-gate }
13757c478bd9Sstevel@tonic-gate
13767c478bd9Sstevel@tonic-gate /*
1377d04ccbb3Scarlsonj * pkt_v4_match(): check if a given DHCPv4 message type is in a given set
13787c478bd9Sstevel@tonic-gate *
1379d04ccbb3Scarlsonj * input: uchar_t: packet type
1380d04ccbb3Scarlsonj * dhcp_message_type_t: bit-wise OR of DHCP_P* values.
1381d04ccbb3Scarlsonj * output: boolean_t: B_TRUE if packet type is in the set
13827c478bd9Sstevel@tonic-gate */
13837c478bd9Sstevel@tonic-gate
1384d04ccbb3Scarlsonj boolean_t
pkt_v4_match(uchar_t type,dhcp_message_type_t match_type)1385d04ccbb3Scarlsonj pkt_v4_match(uchar_t type, dhcp_message_type_t match_type)
1386d04ccbb3Scarlsonj {
1387d04ccbb3Scarlsonj /*
1388d04ccbb3Scarlsonj * note: the ordering here allows direct indexing of the table
1389d04ccbb3Scarlsonj * based on the RFC2131 packet type value passed in.
1390d04ccbb3Scarlsonj */
1391d04ccbb3Scarlsonj
1392d04ccbb3Scarlsonj static dhcp_message_type_t type_map[] = {
1393d04ccbb3Scarlsonj DHCP_PUNTYPED, DHCP_PDISCOVER, DHCP_POFFER, DHCP_PREQUEST,
1394d04ccbb3Scarlsonj DHCP_PDECLINE, DHCP_PACK, DHCP_PNAK, DHCP_PRELEASE,
1395d04ccbb3Scarlsonj DHCP_PINFORM
1396d04ccbb3Scarlsonj };
1397d04ccbb3Scarlsonj
1398d04ccbb3Scarlsonj if (type < (sizeof (type_map) / sizeof (*type_map)))
1399d04ccbb3Scarlsonj return ((type_map[type] & match_type) ? B_TRUE : B_FALSE);
1400d04ccbb3Scarlsonj else
1401d04ccbb3Scarlsonj return (B_FALSE);
1402d04ccbb3Scarlsonj }
1403d04ccbb3Scarlsonj
1404d04ccbb3Scarlsonj /*
1405d04ccbb3Scarlsonj * pkt_smach_enqueue(): enqueue a packet on a given state machine
1406d04ccbb3Scarlsonj *
1407d04ccbb3Scarlsonj * input: dhcp_smach_t: state machine
1408d04ccbb3Scarlsonj * PKT_LIST *: packet to enqueue
1409d04ccbb3Scarlsonj * output: none
1410d04ccbb3Scarlsonj */
1411d04ccbb3Scarlsonj
1412d04ccbb3Scarlsonj void
pkt_smach_enqueue(dhcp_smach_t * dsmp,PKT_LIST * plp)1413d04ccbb3Scarlsonj pkt_smach_enqueue(dhcp_smach_t *dsmp, PKT_LIST *plp)
1414d04ccbb3Scarlsonj {
1415d04ccbb3Scarlsonj dhcpmsg(MSG_VERBOSE, "pkt_smach_enqueue: received %s %s packet on %s",
1416d04ccbb3Scarlsonj pkt_type_to_string(pkt_recv_type(plp), dsmp->dsm_isv6),
1417d04ccbb3Scarlsonj dsmp->dsm_isv6 ? "v6" : "v4", dsmp->dsm_name);
1418d04ccbb3Scarlsonj
1419d04ccbb3Scarlsonj /* add to front of list */
1420d04ccbb3Scarlsonj insque(plp, &dsmp->dsm_recv_pkt_list);
1421d04ccbb3Scarlsonj }
1422d04ccbb3Scarlsonj
1423d04ccbb3Scarlsonj /*
1424d04ccbb3Scarlsonj * next_retransmission(): computes the number of seconds until the next
1425d04ccbb3Scarlsonj * retransmission, based on the algorithms in RFCs 2131
1426d04ccbb3Scarlsonj * 3315.
1427d04ccbb3Scarlsonj *
1428d04ccbb3Scarlsonj * input: dhcp_smach_t *: state machine that needs a new timer
1429d04ccbb3Scarlsonj * boolean_t: B_TRUE if this is the first time sending the message
1430d04ccbb3Scarlsonj * boolean_t: B_TRUE for positive RAND values only (RFC 3315 17.1.2)
1431d04ccbb3Scarlsonj * output: none
1432d04ccbb3Scarlsonj */
1433d04ccbb3Scarlsonj
1434d04ccbb3Scarlsonj static void
next_retransmission(dhcp_smach_t * dsmp,boolean_t first_send,boolean_t positive_only)1435d04ccbb3Scarlsonj next_retransmission(dhcp_smach_t *dsmp, boolean_t first_send,
1436d04ccbb3Scarlsonj boolean_t positive_only)
14377c478bd9Sstevel@tonic-gate {
14387c478bd9Sstevel@tonic-gate uint32_t timeout_ms;
14397c478bd9Sstevel@tonic-gate
1440d04ccbb3Scarlsonj if (dsmp->dsm_isv6) {
1441d04ccbb3Scarlsonj double randval;
14427c478bd9Sstevel@tonic-gate
1443d04ccbb3Scarlsonj /*
1444d04ccbb3Scarlsonj * The RFC specifies 0 to 10% jitter for the initial
1445d04ccbb3Scarlsonj * solicitation, and plus or minus 10% jitter for all others.
1446d04ccbb3Scarlsonj * This works out to 100 milliseconds on the shortest timer we
1447d04ccbb3Scarlsonj * use.
1448d04ccbb3Scarlsonj */
1449d04ccbb3Scarlsonj if (positive_only)
1450d04ccbb3Scarlsonj randval = drand48() / 10.0;
1451d04ccbb3Scarlsonj else
1452d04ccbb3Scarlsonj randval = (drand48() - 0.5) / 5.0;
1453d04ccbb3Scarlsonj
1454d04ccbb3Scarlsonj /* The RFC specifies doubling *after* the first transmission */
1455d04ccbb3Scarlsonj timeout_ms = dsmp->dsm_send_timeout;
1456d04ccbb3Scarlsonj if (!first_send)
1457d04ccbb3Scarlsonj timeout_ms *= 2;
1458d04ccbb3Scarlsonj timeout_ms += (int)(randval * dsmp->dsm_send_timeout);
1459d04ccbb3Scarlsonj
1460d04ccbb3Scarlsonj /* This checks the MRT (maximum retransmission time) */
1461d04ccbb3Scarlsonj if (dsmp->dsm_send_tcenter != 0 &&
1462d04ccbb3Scarlsonj timeout_ms > dsmp->dsm_send_tcenter) {
1463d04ccbb3Scarlsonj timeout_ms = dsmp->dsm_send_tcenter +
1464d04ccbb3Scarlsonj (uint_t)(randval * dsmp->dsm_send_tcenter);
1465d04ccbb3Scarlsonj }
1466d04ccbb3Scarlsonj
1467d04ccbb3Scarlsonj dsmp->dsm_send_timeout = timeout_ms;
1468d04ccbb3Scarlsonj } else {
1469d04ccbb3Scarlsonj if (dsmp->dsm_state == RENEWING ||
1470d04ccbb3Scarlsonj dsmp->dsm_state == REBINDING) {
1471d04ccbb3Scarlsonj monosec_t mono;
1472d04ccbb3Scarlsonj
1473d04ccbb3Scarlsonj timeout_ms = dsmp->dsm_state == RENEWING ?
1474d04ccbb3Scarlsonj dsmp->dsm_leases->dl_t2.dt_start :
1475d04ccbb3Scarlsonj dsmp->dsm_leases->dl_lifs->lif_expire.dt_start;
1476d04ccbb3Scarlsonj timeout_ms += dsmp->dsm_curstart_monosec;
1477d04ccbb3Scarlsonj mono = monosec();
1478d04ccbb3Scarlsonj if (mono > timeout_ms)
1479d04ccbb3Scarlsonj timeout_ms = 0;
1480d04ccbb3Scarlsonj else
1481d04ccbb3Scarlsonj timeout_ms -= mono;
1482d04ccbb3Scarlsonj timeout_ms *= MILLISEC / 2;
1483d04ccbb3Scarlsonj } else {
1484d04ccbb3Scarlsonj /*
1485d04ccbb3Scarlsonj * Start at 4, and increase by a factor of 2 up to 64.
1486d04ccbb3Scarlsonj */
1487d04ccbb3Scarlsonj if (first_send) {
1488d04ccbb3Scarlsonj timeout_ms = 4 * MILLISEC;
1489d04ccbb3Scarlsonj } else {
1490d04ccbb3Scarlsonj timeout_ms = MIN(dsmp->dsm_send_tcenter << 1,
1491d04ccbb3Scarlsonj 64 * MILLISEC);
1492d04ccbb3Scarlsonj }
1493d04ccbb3Scarlsonj }
1494d04ccbb3Scarlsonj
1495d04ccbb3Scarlsonj dsmp->dsm_send_tcenter = timeout_ms;
1496d04ccbb3Scarlsonj
1497d04ccbb3Scarlsonj /*
1498d04ccbb3Scarlsonj * At each iteration, jitter the timeout by some fraction of a
1499d04ccbb3Scarlsonj * second.
1500d04ccbb3Scarlsonj */
1501d04ccbb3Scarlsonj dsmp->dsm_send_timeout = timeout_ms +
1502d04ccbb3Scarlsonj ((lrand48() % (2 * MILLISEC)) - MILLISEC);
1503d04ccbb3Scarlsonj }
1504d04ccbb3Scarlsonj }
1505d04ccbb3Scarlsonj
1506d04ccbb3Scarlsonj /*
1507d04ccbb3Scarlsonj * dhcp_ip_default(): open and bind the default IP sockets used for I/O and
1508d04ccbb3Scarlsonj * interface control.
1509d04ccbb3Scarlsonj *
1510d04ccbb3Scarlsonj * input: none
1511d04ccbb3Scarlsonj * output: B_TRUE on success
1512d04ccbb3Scarlsonj */
1513d04ccbb3Scarlsonj
1514d04ccbb3Scarlsonj boolean_t
dhcp_ip_default(void)1515d04ccbb3Scarlsonj dhcp_ip_default(void)
1516d04ccbb3Scarlsonj {
1517b5e45829Scarlsonj int on = 1;
1518d04ccbb3Scarlsonj
1519d04ccbb3Scarlsonj if ((v4_sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
1520d04ccbb3Scarlsonj dhcpmsg(MSG_ERR,
1521d04ccbb3Scarlsonj "dhcp_ip_default: unable to create IPv4 socket");
1522d04ccbb3Scarlsonj return (B_FALSE);
1523d04ccbb3Scarlsonj }
1524d04ccbb3Scarlsonj
1525d04ccbb3Scarlsonj if (setsockopt(v4_sock_fd, IPPROTO_IP, IP_RECVDSTADDR, &on,
1526d04ccbb3Scarlsonj sizeof (on)) == -1) {
1527d04ccbb3Scarlsonj dhcpmsg(MSG_ERR,
1528d04ccbb3Scarlsonj "dhcp_ip_default: unable to enable IP_RECVDSTADDR");
1529d04ccbb3Scarlsonj return (B_FALSE);
1530d04ccbb3Scarlsonj }
1531d04ccbb3Scarlsonj
1532d04ccbb3Scarlsonj if (setsockopt(v4_sock_fd, IPPROTO_IP, IP_RECVIF, &on, sizeof (on)) ==
1533d04ccbb3Scarlsonj -1) {
1534d04ccbb3Scarlsonj dhcpmsg(MSG_ERR,
1535d04ccbb3Scarlsonj "dhcp_ip_default: unable to enable IP_RECVIF");
1536d04ccbb3Scarlsonj return (B_FALSE);
1537d04ccbb3Scarlsonj }
1538d04ccbb3Scarlsonj
1539d04ccbb3Scarlsonj if (!bind_sock(v4_sock_fd, IPPORT_BOOTPC, INADDR_ANY)) {
1540d04ccbb3Scarlsonj dhcpmsg(MSG_ERROR,
1541d04ccbb3Scarlsonj "dhcp_ip_default: unable to bind IPv4 socket to port %d",
1542d04ccbb3Scarlsonj IPPORT_BOOTPC);
1543d04ccbb3Scarlsonj return (B_FALSE);
1544d04ccbb3Scarlsonj }
1545d04ccbb3Scarlsonj
1546e704a8f2Smeem if (iu_register_event(eh, v4_sock_fd, POLLIN, dhcp_acknak_global,
1547d04ccbb3Scarlsonj NULL) == -1) {
1548d04ccbb3Scarlsonj dhcpmsg(MSG_WARNING, "dhcp_ip_default: cannot register to "
1549d04ccbb3Scarlsonj "receive IPv4 broadcasts");
1550d04ccbb3Scarlsonj return (B_FALSE);
1551d04ccbb3Scarlsonj }
1552d04ccbb3Scarlsonj
1553d04ccbb3Scarlsonj if ((v6_sock_fd = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) {
1554d04ccbb3Scarlsonj dhcpmsg(MSG_ERR,
1555d04ccbb3Scarlsonj "dhcp_ip_default: unable to create IPv6 socket");
1556d04ccbb3Scarlsonj return (B_FALSE);
1557d04ccbb3Scarlsonj }
1558d04ccbb3Scarlsonj
1559d04ccbb3Scarlsonj if (setsockopt(v6_sock_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on,
1560d04ccbb3Scarlsonj sizeof (on)) == -1) {
1561d04ccbb3Scarlsonj dhcpmsg(MSG_ERR,
1562d04ccbb3Scarlsonj "dhcp_ip_default: unable to enable IPV6_RECVPKTINFO");
1563d04ccbb3Scarlsonj return (B_FALSE);
1564d04ccbb3Scarlsonj }
1565d04ccbb3Scarlsonj
1566d04ccbb3Scarlsonj if (!bind_sock_v6(v6_sock_fd, IPPORT_DHCPV6C, NULL)) {
1567d04ccbb3Scarlsonj dhcpmsg(MSG_ERROR,
1568d04ccbb3Scarlsonj "dhcp_ip_default: unable to bind IPv6 socket to port %d",
1569d04ccbb3Scarlsonj IPPORT_DHCPV6C);
1570d04ccbb3Scarlsonj return (B_FALSE);
1571d04ccbb3Scarlsonj }
1572d04ccbb3Scarlsonj
1573e704a8f2Smeem if (iu_register_event(eh, v6_sock_fd, POLLIN, dhcp_acknak_global,
1574d04ccbb3Scarlsonj NULL) == -1) {
1575d04ccbb3Scarlsonj dhcpmsg(MSG_WARNING, "dhcp_ip_default: cannot register to "
1576d04ccbb3Scarlsonj "receive IPv6 packets");
1577d04ccbb3Scarlsonj return (B_FALSE);
1578d04ccbb3Scarlsonj }
1579d04ccbb3Scarlsonj
1580d04ccbb3Scarlsonj return (B_TRUE);
15817c478bd9Sstevel@tonic-gate }
1582