xref: /onnv-gate/usr/src/cmd/cmd-inet/sbin/dhcpagent/packet.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate #include <string.h>
30*0Sstevel@tonic-gate #include <sys/types.h>
31*0Sstevel@tonic-gate #include <stdlib.h>
32*0Sstevel@tonic-gate #include <arpa/inet.h>
33*0Sstevel@tonic-gate #include <dhcpmsg.h>
34*0Sstevel@tonic-gate #include <stddef.h>
35*0Sstevel@tonic-gate #include <assert.h>
36*0Sstevel@tonic-gate 
37*0Sstevel@tonic-gate #include "states.h"
38*0Sstevel@tonic-gate #include "interface.h"
39*0Sstevel@tonic-gate #include "agent.h"
40*0Sstevel@tonic-gate #include "packet.h"
41*0Sstevel@tonic-gate #include "util.h"
42*0Sstevel@tonic-gate 
43*0Sstevel@tonic-gate static double	fuzzify(uint32_t, double);
44*0Sstevel@tonic-gate static void 	retransmit(iu_tq_t *, void *);
45*0Sstevel@tonic-gate static uint32_t	next_retransmission(uint32_t);
46*0Sstevel@tonic-gate static int	send_pkt_internal(struct ifslist *);
47*0Sstevel@tonic-gate static uchar_t	pkt_type(PKT *);
48*0Sstevel@tonic-gate 
49*0Sstevel@tonic-gate /*
50*0Sstevel@tonic-gate  * dhcp_type_ptob(): converts the DHCP packet type values in RFC2131 into
51*0Sstevel@tonic-gate  *		     values which can be used for recv_pkt()
52*0Sstevel@tonic-gate  *
53*0Sstevel@tonic-gate  *   input: uchar_t: a DHCP packet type value, as defined in RFC2131
54*0Sstevel@tonic-gate  *  output: dhcp_message_type_t: a packet type value for use with recv_pkt()
55*0Sstevel@tonic-gate  */
56*0Sstevel@tonic-gate 
57*0Sstevel@tonic-gate static dhcp_message_type_t
58*0Sstevel@tonic-gate dhcp_type_ptob(uchar_t type)
59*0Sstevel@tonic-gate {
60*0Sstevel@tonic-gate 	/*
61*0Sstevel@tonic-gate 	 * note: the ordering here allows direct indexing of the table
62*0Sstevel@tonic-gate 	 *	 based on the RFC2131 packet type value passed in.
63*0Sstevel@tonic-gate 	 */
64*0Sstevel@tonic-gate 
65*0Sstevel@tonic-gate 	static dhcp_message_type_t type_map[] = {
66*0Sstevel@tonic-gate 		DHCP_PUNTYPED, DHCP_PDISCOVER, DHCP_POFFER, DHCP_PREQUEST,
67*0Sstevel@tonic-gate 		DHCP_PDECLINE, DHCP_PACK, DHCP_PNAK, DHCP_PRELEASE, DHCP_PINFORM
68*0Sstevel@tonic-gate 	};
69*0Sstevel@tonic-gate 
70*0Sstevel@tonic-gate 	if (type < (sizeof (type_map) / sizeof (*type_map)))
71*0Sstevel@tonic-gate 		return (type_map[type]);
72*0Sstevel@tonic-gate 
73*0Sstevel@tonic-gate 	return (0);
74*0Sstevel@tonic-gate }
75*0Sstevel@tonic-gate 
76*0Sstevel@tonic-gate /*
77*0Sstevel@tonic-gate  * pkt_type(): returns an integer representing the packet's type; only
78*0Sstevel@tonic-gate  *	       for use with outbound packets.
79*0Sstevel@tonic-gate  *
80*0Sstevel@tonic-gate  *   input: PKT *: the packet to examine
81*0Sstevel@tonic-gate  *  output: uchar_t: the packet type (0 if unknown)
82*0Sstevel@tonic-gate  */
83*0Sstevel@tonic-gate 
84*0Sstevel@tonic-gate static uchar_t
85*0Sstevel@tonic-gate pkt_type(PKT *pkt)
86*0Sstevel@tonic-gate {
87*0Sstevel@tonic-gate 	uchar_t	*option = pkt->options;
88*0Sstevel@tonic-gate 
89*0Sstevel@tonic-gate 	/*
90*0Sstevel@tonic-gate 	 * this is a little dirty but it should get the job done.
91*0Sstevel@tonic-gate 	 * assumes that the type is in the statically allocated part
92*0Sstevel@tonic-gate 	 * of the options field.
93*0Sstevel@tonic-gate 	 */
94*0Sstevel@tonic-gate 
95*0Sstevel@tonic-gate 	while (*option != CD_DHCP_TYPE) {
96*0Sstevel@tonic-gate 		if (option + 2 - pkt->options >= sizeof (pkt->options))
97*0Sstevel@tonic-gate 			return (0);
98*0Sstevel@tonic-gate 
99*0Sstevel@tonic-gate 		option++;
100*0Sstevel@tonic-gate 		option += *option;
101*0Sstevel@tonic-gate 	}
102*0Sstevel@tonic-gate 
103*0Sstevel@tonic-gate 	return (option[2]);
104*0Sstevel@tonic-gate }
105*0Sstevel@tonic-gate 
106*0Sstevel@tonic-gate /*
107*0Sstevel@tonic-gate  * init_pkt(): initializes and returns a packet of a given type
108*0Sstevel@tonic-gate  *
109*0Sstevel@tonic-gate  *   input: struct ifslist *: the interface the packet will be going out
110*0Sstevel@tonic-gate  *	    uchar_t: the packet type (DHCP message type)
111*0Sstevel@tonic-gate  *  output: dhcp_pkt_t *: a pointer to the initialized packet
112*0Sstevel@tonic-gate  */
113*0Sstevel@tonic-gate 
114*0Sstevel@tonic-gate dhcp_pkt_t *
115*0Sstevel@tonic-gate init_pkt(struct ifslist *ifsp, uchar_t type)
116*0Sstevel@tonic-gate {
117*0Sstevel@tonic-gate 	uint8_t		bootmagic[] = BOOTMAGIC;
118*0Sstevel@tonic-gate 	dhcp_pkt_t	*dpkt = &ifsp->if_send_pkt;
119*0Sstevel@tonic-gate 	uint32_t	xid;
120*0Sstevel@tonic-gate 
121*0Sstevel@tonic-gate 	dpkt->pkt_max_len = ifsp->if_max;
122*0Sstevel@tonic-gate 	dpkt->pkt_cur_len = offsetof(PKT, options);
123*0Sstevel@tonic-gate 
124*0Sstevel@tonic-gate 	(void) memset(dpkt->pkt, 0, ifsp->if_max);
125*0Sstevel@tonic-gate 	(void) memcpy(dpkt->pkt->cookie, bootmagic, sizeof (bootmagic));
126*0Sstevel@tonic-gate 	if (ifsp->if_hwlen <= sizeof (dpkt->pkt->chaddr)) {
127*0Sstevel@tonic-gate 		dpkt->pkt->hlen  = ifsp->if_hwlen;
128*0Sstevel@tonic-gate 		(void) memcpy(dpkt->pkt->chaddr, ifsp->if_hwaddr,
129*0Sstevel@tonic-gate 		    ifsp->if_hwlen);
130*0Sstevel@tonic-gate 	} else {
131*0Sstevel@tonic-gate 		/*
132*0Sstevel@tonic-gate 		 * The mac address does not fit in the chaddr
133*0Sstevel@tonic-gate 		 * field, thus it can not be sent to the server,
134*0Sstevel@tonic-gate 		 * thus server can not unicast the reply. Per
135*0Sstevel@tonic-gate 		 * RFC 2131 4.4.1, client can set this bit in
136*0Sstevel@tonic-gate 		 * DISCOVER/REQUEST. If the client is already
137*0Sstevel@tonic-gate 		 * in BOUND/REBINDING/RENEWING state, do not set
138*0Sstevel@tonic-gate 		 * this bit, as it can respond to unicast responses
139*0Sstevel@tonic-gate 		 * from server using the 'ciaddr' address.
140*0Sstevel@tonic-gate 		 */
141*0Sstevel@tonic-gate 		if ((type == DISCOVER) || ((type == REQUEST) &&
142*0Sstevel@tonic-gate 		    (ifsp->if_state != RENEWING) &&
143*0Sstevel@tonic-gate 		    (ifsp->if_state != REBINDING) &&
144*0Sstevel@tonic-gate 		    (ifsp->if_state != BOUND)))
145*0Sstevel@tonic-gate 			dpkt->pkt->flags = htons(BCAST_MASK);
146*0Sstevel@tonic-gate 	}
147*0Sstevel@tonic-gate 
148*0Sstevel@tonic-gate 	/*
149*0Sstevel@tonic-gate 	 * since multiple dhcp leases may be maintained over the same dlpi
150*0Sstevel@tonic-gate 	 * device (e.g. "hme0" and "hme0:1"), make sure the xid is unique.
151*0Sstevel@tonic-gate 	 */
152*0Sstevel@tonic-gate 
153*0Sstevel@tonic-gate 	do {
154*0Sstevel@tonic-gate 		xid = mrand48();
155*0Sstevel@tonic-gate 	} while (lookup_ifs_by_xid(xid) != NULL);
156*0Sstevel@tonic-gate 
157*0Sstevel@tonic-gate 	dpkt->pkt->xid	 = xid;
158*0Sstevel@tonic-gate 	dpkt->pkt->op    = BOOTREQUEST;
159*0Sstevel@tonic-gate 	dpkt->pkt->htype = ifsp->if_hwtype;
160*0Sstevel@tonic-gate 
161*0Sstevel@tonic-gate 	add_pkt_opt(dpkt, CD_DHCP_TYPE, &type, 1);
162*0Sstevel@tonic-gate 	add_pkt_opt(dpkt, CD_CLIENT_ID, ifsp->if_cid, ifsp->if_cidlen);
163*0Sstevel@tonic-gate 
164*0Sstevel@tonic-gate 	return (dpkt);
165*0Sstevel@tonic-gate }
166*0Sstevel@tonic-gate 
167*0Sstevel@tonic-gate /*
168*0Sstevel@tonic-gate  * add_pkt_opt(): adds an option to a dhcp_pkt_t
169*0Sstevel@tonic-gate  *
170*0Sstevel@tonic-gate  *   input: dhcp_pkt_t *: the packet to add the option to
171*0Sstevel@tonic-gate  *	    uchar_t: the type of option being added
172*0Sstevel@tonic-gate  *	    const void *: the value of that option
173*0Sstevel@tonic-gate  *	    uchar_t: the length of the value of the option
174*0Sstevel@tonic-gate  *  output: void
175*0Sstevel@tonic-gate  */
176*0Sstevel@tonic-gate 
177*0Sstevel@tonic-gate void
178*0Sstevel@tonic-gate add_pkt_opt(dhcp_pkt_t *dpkt, uchar_t opt_type, const void *opt_val,
179*0Sstevel@tonic-gate     uchar_t opt_len)
180*0Sstevel@tonic-gate {
181*0Sstevel@tonic-gate 	caddr_t		raw_pkt = (caddr_t)dpkt->pkt;
182*0Sstevel@tonic-gate 	int16_t		req_len = opt_len + 2; /* + 2 for code & length bytes */
183*0Sstevel@tonic-gate 
184*0Sstevel@tonic-gate 	/* CD_END and CD_PAD options don't have a length field */
185*0Sstevel@tonic-gate 	if (opt_type == CD_END || opt_type == CD_PAD)
186*0Sstevel@tonic-gate 		req_len--;
187*0Sstevel@tonic-gate 	else if (opt_val == NULL)
188*0Sstevel@tonic-gate 		return;
189*0Sstevel@tonic-gate 
190*0Sstevel@tonic-gate 	if ((dpkt->pkt_cur_len + req_len) > dpkt->pkt_max_len) {
191*0Sstevel@tonic-gate 		dhcpmsg(MSG_WARNING, "add_pkt_opt: not enough room for option "
192*0Sstevel@tonic-gate 		    "%d in packet", opt_type);
193*0Sstevel@tonic-gate 		return;
194*0Sstevel@tonic-gate 	}
195*0Sstevel@tonic-gate 
196*0Sstevel@tonic-gate 	raw_pkt[dpkt->pkt_cur_len++] = opt_type;
197*0Sstevel@tonic-gate 
198*0Sstevel@tonic-gate 	if (opt_len > 0) {
199*0Sstevel@tonic-gate 		raw_pkt[dpkt->pkt_cur_len++] = opt_len;
200*0Sstevel@tonic-gate 		(void) memcpy(&raw_pkt[dpkt->pkt_cur_len], opt_val, opt_len);
201*0Sstevel@tonic-gate 		dpkt->pkt_cur_len += opt_len;
202*0Sstevel@tonic-gate 	}
203*0Sstevel@tonic-gate }
204*0Sstevel@tonic-gate 
205*0Sstevel@tonic-gate /*
206*0Sstevel@tonic-gate  * add_pkt_opt16(): adds an option with a 16-bit value to a dhcp_pkt_t
207*0Sstevel@tonic-gate  *
208*0Sstevel@tonic-gate  *   input: dhcp_pkt_t *: the packet to add the option to
209*0Sstevel@tonic-gate  *	    uchar_t: the type of option being added
210*0Sstevel@tonic-gate  *	    uint16_t: the value of that option
211*0Sstevel@tonic-gate  *  output: void
212*0Sstevel@tonic-gate  */
213*0Sstevel@tonic-gate 
214*0Sstevel@tonic-gate void
215*0Sstevel@tonic-gate add_pkt_opt16(dhcp_pkt_t *dpkt, uchar_t opt_type, uint16_t opt_value)
216*0Sstevel@tonic-gate {
217*0Sstevel@tonic-gate 	add_pkt_opt(dpkt, opt_type, &opt_value, 2);
218*0Sstevel@tonic-gate }
219*0Sstevel@tonic-gate 
220*0Sstevel@tonic-gate /*
221*0Sstevel@tonic-gate  * add_pkt_opt32(): adds an option with a 32-bit value to a dhcp_pkt_t
222*0Sstevel@tonic-gate  *
223*0Sstevel@tonic-gate  *   input: dhcp_pkt_t *: the packet to add the option to
224*0Sstevel@tonic-gate  *	    uchar_t: the type of option being added
225*0Sstevel@tonic-gate  *	    uint32_t: the value of that option
226*0Sstevel@tonic-gate  *  output: void
227*0Sstevel@tonic-gate  */
228*0Sstevel@tonic-gate 
229*0Sstevel@tonic-gate void
230*0Sstevel@tonic-gate add_pkt_opt32(dhcp_pkt_t *dpkt, uchar_t opt_type, uint32_t opt_value)
231*0Sstevel@tonic-gate {
232*0Sstevel@tonic-gate 	add_pkt_opt(dpkt, opt_type, &opt_value, 4);
233*0Sstevel@tonic-gate }
234*0Sstevel@tonic-gate 
235*0Sstevel@tonic-gate /*
236*0Sstevel@tonic-gate  * get_pkt_times(): pulls the lease times out of a packet and stores them as
237*0Sstevel@tonic-gate  *		    host-byteorder relative times in the passed in parameters
238*0Sstevel@tonic-gate  *
239*0Sstevel@tonic-gate  *   input: PKT_LIST *: the packet to pull the packet times from
240*0Sstevel@tonic-gate  *	    lease_t *: where to store the relative lease time in hbo
241*0Sstevel@tonic-gate  *	    lease_t *: where to store the relative t1 time in hbo
242*0Sstevel@tonic-gate  *	    lease_t *: where to store the relative t2 time in hbo
243*0Sstevel@tonic-gate  *  output: void
244*0Sstevel@tonic-gate  */
245*0Sstevel@tonic-gate 
246*0Sstevel@tonic-gate void
247*0Sstevel@tonic-gate get_pkt_times(PKT_LIST *ack, lease_t *lease, lease_t *t1, lease_t *t2)
248*0Sstevel@tonic-gate {
249*0Sstevel@tonic-gate 	*lease	= DHCP_PERM;
250*0Sstevel@tonic-gate 	*t1	= DHCP_PERM;
251*0Sstevel@tonic-gate 	*t2	= DHCP_PERM;
252*0Sstevel@tonic-gate 
253*0Sstevel@tonic-gate 	if (ack->opts[CD_DHCP_TYPE]  == NULL ||
254*0Sstevel@tonic-gate 	    ack->opts[CD_LEASE_TIME] == NULL ||
255*0Sstevel@tonic-gate 	    ack->opts[CD_LEASE_TIME]->len != sizeof (lease_t))
256*0Sstevel@tonic-gate 		return;
257*0Sstevel@tonic-gate 
258*0Sstevel@tonic-gate 	(void) memcpy(lease, ack->opts[CD_LEASE_TIME]->value, sizeof (lease_t));
259*0Sstevel@tonic-gate 	*lease = ntohl(*lease);
260*0Sstevel@tonic-gate 
261*0Sstevel@tonic-gate 	if (*lease == DHCP_PERM)
262*0Sstevel@tonic-gate 		return;
263*0Sstevel@tonic-gate 
264*0Sstevel@tonic-gate 	if (ack->opts[CD_T1_TIME] != NULL &&
265*0Sstevel@tonic-gate 	    ack->opts[CD_T1_TIME]->len == sizeof (lease_t)) {
266*0Sstevel@tonic-gate 		(void) memcpy(t1, ack->opts[CD_T1_TIME]->value, sizeof (*t1));
267*0Sstevel@tonic-gate 		*t1 = ntohl(*t1);
268*0Sstevel@tonic-gate 	}
269*0Sstevel@tonic-gate 
270*0Sstevel@tonic-gate 	if ((*t1 == DHCP_PERM) || (*t1 >= *lease))
271*0Sstevel@tonic-gate 		*t1 = (lease_t)fuzzify(*lease, DHCP_T1_FACT);
272*0Sstevel@tonic-gate 
273*0Sstevel@tonic-gate 	if (ack->opts[CD_T2_TIME] != NULL &&
274*0Sstevel@tonic-gate 	    ack->opts[CD_T2_TIME]->len == sizeof (lease_t)) {
275*0Sstevel@tonic-gate 		(void) memcpy(t2, ack->opts[CD_T2_TIME]->value, sizeof (*t2));
276*0Sstevel@tonic-gate 		*t2 = ntohl(*t2);
277*0Sstevel@tonic-gate 	}
278*0Sstevel@tonic-gate 
279*0Sstevel@tonic-gate 	if ((*t2 == DHCP_PERM) || (*t2 > *lease) || (*t2 <= *t1))
280*0Sstevel@tonic-gate 		*t2 = (lease_t)fuzzify(*lease, DHCP_T2_FACT);
281*0Sstevel@tonic-gate }
282*0Sstevel@tonic-gate 
283*0Sstevel@tonic-gate /*
284*0Sstevel@tonic-gate  * fuzzify(): adds some "fuzz" to a t1/t2 time, in accordance with RFC2131
285*0Sstevel@tonic-gate  *
286*0Sstevel@tonic-gate  *   input: uint32_t: the number of seconds until lease expiration
287*0Sstevel@tonic-gate  *	    double: the approximate percentage of that time to return
288*0Sstevel@tonic-gate  *  output: double: a number approximating (sec * pct)
289*0Sstevel@tonic-gate  */
290*0Sstevel@tonic-gate 
291*0Sstevel@tonic-gate static double
292*0Sstevel@tonic-gate fuzzify(uint32_t sec, double pct)
293*0Sstevel@tonic-gate {
294*0Sstevel@tonic-gate 	return (sec * (pct + (drand48() - 0.5) / 25.0));
295*0Sstevel@tonic-gate }
296*0Sstevel@tonic-gate 
297*0Sstevel@tonic-gate /*
298*0Sstevel@tonic-gate  * free_pkt_list(): frees a packet list
299*0Sstevel@tonic-gate  *
300*0Sstevel@tonic-gate  *   input: PKT_LIST **: the packet list to free
301*0Sstevel@tonic-gate  *  output: void
302*0Sstevel@tonic-gate  */
303*0Sstevel@tonic-gate 
304*0Sstevel@tonic-gate void
305*0Sstevel@tonic-gate free_pkt_list(PKT_LIST **plp)
306*0Sstevel@tonic-gate {
307*0Sstevel@tonic-gate 	PKT_LIST	*plp_next;
308*0Sstevel@tonic-gate 
309*0Sstevel@tonic-gate 	for (; *plp != NULL; *plp = plp_next) {
310*0Sstevel@tonic-gate 		plp_next = (*plp)->next;
311*0Sstevel@tonic-gate 		free((*plp)->pkt);
312*0Sstevel@tonic-gate 		free(*plp);
313*0Sstevel@tonic-gate 	}
314*0Sstevel@tonic-gate }
315*0Sstevel@tonic-gate 
316*0Sstevel@tonic-gate /*
317*0Sstevel@tonic-gate  * prepend_to_pkt_list(): prepends a packet to a packet list
318*0Sstevel@tonic-gate  *
319*0Sstevel@tonic-gate  *   input: PKT_LIST **: the packet list
320*0Sstevel@tonic-gate  *	    PKT_LIST *: the packet to prepend
321*0Sstevel@tonic-gate  *  output: void
322*0Sstevel@tonic-gate  */
323*0Sstevel@tonic-gate 
324*0Sstevel@tonic-gate static void
325*0Sstevel@tonic-gate prepend_to_pkt_list(PKT_LIST **list_head, PKT_LIST *new_entry)
326*0Sstevel@tonic-gate {
327*0Sstevel@tonic-gate 	new_entry->next = *list_head;
328*0Sstevel@tonic-gate 	new_entry->prev = NULL;
329*0Sstevel@tonic-gate 
330*0Sstevel@tonic-gate 	if (*list_head != NULL)
331*0Sstevel@tonic-gate 		(*list_head)->prev = new_entry;
332*0Sstevel@tonic-gate 
333*0Sstevel@tonic-gate 	*list_head = new_entry;
334*0Sstevel@tonic-gate }
335*0Sstevel@tonic-gate 
336*0Sstevel@tonic-gate /*
337*0Sstevel@tonic-gate  * remove_from_pkt_list(): removes a given packet from a packet list
338*0Sstevel@tonic-gate  *
339*0Sstevel@tonic-gate  *   input: PKT_LIST **: the packet list
340*0Sstevel@tonic-gate  *	    PKT_LIST *: the packet to remove
341*0Sstevel@tonic-gate  *  output: void
342*0Sstevel@tonic-gate  */
343*0Sstevel@tonic-gate 
344*0Sstevel@tonic-gate void
345*0Sstevel@tonic-gate remove_from_pkt_list(PKT_LIST **list_head, PKT_LIST *remove)
346*0Sstevel@tonic-gate {
347*0Sstevel@tonic-gate 	if (*list_head == NULL)
348*0Sstevel@tonic-gate 		return;
349*0Sstevel@tonic-gate 
350*0Sstevel@tonic-gate 	if (*list_head == remove) {
351*0Sstevel@tonic-gate 		*list_head = remove->next;
352*0Sstevel@tonic-gate 		if (*list_head != NULL)
353*0Sstevel@tonic-gate 			(*list_head)->prev = NULL;
354*0Sstevel@tonic-gate 	} else {
355*0Sstevel@tonic-gate 		remove->prev->next = remove->next;
356*0Sstevel@tonic-gate 		if (remove->next != NULL)
357*0Sstevel@tonic-gate 			remove->next->prev = remove->prev;
358*0Sstevel@tonic-gate 	}
359*0Sstevel@tonic-gate 
360*0Sstevel@tonic-gate 	remove->next = NULL;
361*0Sstevel@tonic-gate 	remove->prev = NULL;
362*0Sstevel@tonic-gate }
363*0Sstevel@tonic-gate 
364*0Sstevel@tonic-gate /*
365*0Sstevel@tonic-gate  * send_pkt_internal(): sends a packet out on an interface
366*0Sstevel@tonic-gate  *
367*0Sstevel@tonic-gate  *   input: struct ifslist *: the interface to send the packet out on
368*0Sstevel@tonic-gate  *  output: int: 1 if the packet is sent, 0 otherwise
369*0Sstevel@tonic-gate  */
370*0Sstevel@tonic-gate 
371*0Sstevel@tonic-gate static int
372*0Sstevel@tonic-gate send_pkt_internal(struct ifslist *ifsp)
373*0Sstevel@tonic-gate {
374*0Sstevel@tonic-gate 	ssize_t		n_bytes;
375*0Sstevel@tonic-gate 	dhcp_pkt_t	*dpkt = &ifsp->if_send_pkt;
376*0Sstevel@tonic-gate 	const char	*pkt_name = pkt_type_to_string(pkt_type(dpkt->pkt));
377*0Sstevel@tonic-gate 
378*0Sstevel@tonic-gate 	/*
379*0Sstevel@tonic-gate 	 * if needed, schedule a retransmission timer, then attempt to
380*0Sstevel@tonic-gate 	 * send the packet.  if we fail, then log the error.  our
381*0Sstevel@tonic-gate 	 * return value should indicate whether or not we were
382*0Sstevel@tonic-gate 	 * successful in sending the request, independent of whether
383*0Sstevel@tonic-gate 	 * we could schedule a timer.
384*0Sstevel@tonic-gate 	 */
385*0Sstevel@tonic-gate 
386*0Sstevel@tonic-gate 	if (ifsp->if_send_timeout != 0) {
387*0Sstevel@tonic-gate 		if ((ifsp->if_retrans_timer = iu_schedule_timer_ms(tq,
388*0Sstevel@tonic-gate 		    ifsp->if_send_timeout, retransmit, ifsp)) == -1)
389*0Sstevel@tonic-gate 			dhcpmsg(MSG_WARNING, "send_pkt_internal: cannot "
390*0Sstevel@tonic-gate 			    "schedule retransmit timer for %s packet",
391*0Sstevel@tonic-gate 			    pkt_name);
392*0Sstevel@tonic-gate 		else
393*0Sstevel@tonic-gate 			hold_ifs(ifsp);
394*0Sstevel@tonic-gate 	}
395*0Sstevel@tonic-gate 
396*0Sstevel@tonic-gate 	/*
397*0Sstevel@tonic-gate 	 * set the `pkt->secs' field depending on the type of packet.
398*0Sstevel@tonic-gate 	 * it should be zero, except in the following cases:
399*0Sstevel@tonic-gate 	 *
400*0Sstevel@tonic-gate 	 * DISCOVER:	set to the number of seconds since we started
401*0Sstevel@tonic-gate 	 *		trying to obtain a lease.
402*0Sstevel@tonic-gate 	 *
403*0Sstevel@tonic-gate 	 * INFORM:	set to the number of seconds since we started
404*0Sstevel@tonic-gate 	 *		trying to get configuration parameters.
405*0Sstevel@tonic-gate 	 *
406*0Sstevel@tonic-gate 	 * REQUEST:	if in the REQUESTING state, then same value as
407*0Sstevel@tonic-gate 	 *		DISCOVER, otherwise the number of seconds
408*0Sstevel@tonic-gate 	 *		since we started trying to obtain a lease.
409*0Sstevel@tonic-gate 	 *
410*0Sstevel@tonic-gate 	 * we also set `if_newstart_monosec', to the time we sent a
411*0Sstevel@tonic-gate 	 * REQUEST or DISCOVER packet, so we know the lease start
412*0Sstevel@tonic-gate 	 * time (the DISCOVER case is for handling BOOTP servers).
413*0Sstevel@tonic-gate 	 */
414*0Sstevel@tonic-gate 
415*0Sstevel@tonic-gate 	switch (pkt_type(dpkt->pkt)) {
416*0Sstevel@tonic-gate 
417*0Sstevel@tonic-gate 	case DISCOVER:
418*0Sstevel@tonic-gate 		ifsp->if_newstart_monosec = monosec();
419*0Sstevel@tonic-gate 		ifsp->if_disc_secs = monosec() - ifsp->if_neg_monosec;
420*0Sstevel@tonic-gate 		dpkt->pkt->secs = htons(ifsp->if_disc_secs);
421*0Sstevel@tonic-gate 		break;
422*0Sstevel@tonic-gate 
423*0Sstevel@tonic-gate 	case INFORM:
424*0Sstevel@tonic-gate 		dpkt->pkt->secs = htons(monosec() - ifsp->if_neg_monosec);
425*0Sstevel@tonic-gate 		break;
426*0Sstevel@tonic-gate 
427*0Sstevel@tonic-gate 	case REQUEST:
428*0Sstevel@tonic-gate 		ifsp->if_newstart_monosec = monosec();
429*0Sstevel@tonic-gate 
430*0Sstevel@tonic-gate 		if (ifsp->if_state == REQUESTING) {
431*0Sstevel@tonic-gate 			dpkt->pkt->secs = htons(ifsp->if_disc_secs);
432*0Sstevel@tonic-gate 			break;
433*0Sstevel@tonic-gate 		}
434*0Sstevel@tonic-gate 
435*0Sstevel@tonic-gate 		dpkt->pkt->secs = htons(monosec() - ifsp->if_neg_monosec);
436*0Sstevel@tonic-gate 		break;
437*0Sstevel@tonic-gate 
438*0Sstevel@tonic-gate 	default:
439*0Sstevel@tonic-gate 		dpkt->pkt->secs = htons(0);
440*0Sstevel@tonic-gate 	}
441*0Sstevel@tonic-gate 
442*0Sstevel@tonic-gate 	switch (ifsp->if_state) {
443*0Sstevel@tonic-gate 
444*0Sstevel@tonic-gate 	case BOUND:
445*0Sstevel@tonic-gate 	case RENEWING:
446*0Sstevel@tonic-gate 	case REBINDING:
447*0Sstevel@tonic-gate 		n_bytes = sendto(ifsp->if_sock_ip_fd, dpkt->pkt,
448*0Sstevel@tonic-gate 		    dpkt->pkt_cur_len, 0,
449*0Sstevel@tonic-gate 		    (struct sockaddr *)&ifsp->if_send_dest,
450*0Sstevel@tonic-gate 		    sizeof (struct sockaddr_in));
451*0Sstevel@tonic-gate 		break;
452*0Sstevel@tonic-gate 
453*0Sstevel@tonic-gate 	default:
454*0Sstevel@tonic-gate 		n_bytes = dlpi_sendto(ifsp->if_dlpi_fd, dpkt->pkt,
455*0Sstevel@tonic-gate 		    dpkt->pkt_cur_len, &ifsp->if_send_dest,
456*0Sstevel@tonic-gate 		    ifsp->if_daddr, ifsp->if_dlen);
457*0Sstevel@tonic-gate 		break;
458*0Sstevel@tonic-gate 	}
459*0Sstevel@tonic-gate 
460*0Sstevel@tonic-gate 	if (n_bytes != dpkt->pkt_cur_len) {
461*0Sstevel@tonic-gate 		if (ifsp->if_retrans_timer == -1)
462*0Sstevel@tonic-gate 			dhcpmsg(MSG_WARNING, "send_pkt_internal: cannot send "
463*0Sstevel@tonic-gate 			    "%s packet to server", pkt_name);
464*0Sstevel@tonic-gate 		else
465*0Sstevel@tonic-gate 			dhcpmsg(MSG_WARNING, "send_pkt_internal: cannot send "
466*0Sstevel@tonic-gate 			    "%s packet to server (will retry in %u seconds)",
467*0Sstevel@tonic-gate 			    pkt_name, ifsp->if_send_timeout / MILLISEC);
468*0Sstevel@tonic-gate 		return (0);
469*0Sstevel@tonic-gate 	}
470*0Sstevel@tonic-gate 
471*0Sstevel@tonic-gate 	dhcpmsg(MSG_VERBOSE, "sent %s packet out %s", pkt_name,
472*0Sstevel@tonic-gate 	    ifsp->if_name);
473*0Sstevel@tonic-gate 
474*0Sstevel@tonic-gate 	ifsp->if_packet_sent++;
475*0Sstevel@tonic-gate 	ifsp->if_sent++;
476*0Sstevel@tonic-gate 	return (1);
477*0Sstevel@tonic-gate }
478*0Sstevel@tonic-gate 
479*0Sstevel@tonic-gate /*
480*0Sstevel@tonic-gate  * send_pkt(): sends a packet out on an interface
481*0Sstevel@tonic-gate  *
482*0Sstevel@tonic-gate  *   input: struct ifslist *: the interface to send the packet out on
483*0Sstevel@tonic-gate  *	    dhcp_pkt_t *: the packet to send out
484*0Sstevel@tonic-gate  *	    in_addr_t: the destination IP address for the packet
485*0Sstevel@tonic-gate  *	    stop_func_t *: a pointer to function to indicate when to stop
486*0Sstevel@tonic-gate  *			   retransmitting the packet (if NULL, packet is
487*0Sstevel@tonic-gate  *			   not retransmitted)
488*0Sstevel@tonic-gate  *  output: int: 1 if the packet was sent, 0 otherwise
489*0Sstevel@tonic-gate  */
490*0Sstevel@tonic-gate 
491*0Sstevel@tonic-gate int
492*0Sstevel@tonic-gate send_pkt(struct ifslist *ifsp, dhcp_pkt_t *dpkt, in_addr_t dest,
493*0Sstevel@tonic-gate     stop_func_t *stop)
494*0Sstevel@tonic-gate {
495*0Sstevel@tonic-gate 	/*
496*0Sstevel@tonic-gate 	 * packets must be at least sizeof (PKT) or they may be dropped
497*0Sstevel@tonic-gate 	 * by routers.  pad out the packet in this case.
498*0Sstevel@tonic-gate 	 */
499*0Sstevel@tonic-gate 
500*0Sstevel@tonic-gate 	dpkt->pkt_cur_len = MAX(dpkt->pkt_cur_len, sizeof (PKT));
501*0Sstevel@tonic-gate 
502*0Sstevel@tonic-gate 	ifsp->if_packet_sent = 0;
503*0Sstevel@tonic-gate 
504*0Sstevel@tonic-gate 	(void) memset(&ifsp->if_send_dest, 0, sizeof (ifsp->if_send_dest));
505*0Sstevel@tonic-gate 	ifsp->if_send_dest.sin_addr.s_addr = dest;
506*0Sstevel@tonic-gate 	ifsp->if_send_dest.sin_family	   = AF_INET;
507*0Sstevel@tonic-gate 	ifsp->if_send_dest.sin_port	   = htons(IPPORT_BOOTPS);
508*0Sstevel@tonic-gate 	ifsp->if_send_stop_func		   = stop;
509*0Sstevel@tonic-gate 
510*0Sstevel@tonic-gate 	/*
511*0Sstevel@tonic-gate 	 * TODO: dispose of this gruesome assumption (there's no real
512*0Sstevel@tonic-gate 	 * technical gain from doing so, but it would be cleaner)
513*0Sstevel@tonic-gate 	 */
514*0Sstevel@tonic-gate 
515*0Sstevel@tonic-gate 	assert(dpkt == &ifsp->if_send_pkt);
516*0Sstevel@tonic-gate 
517*0Sstevel@tonic-gate 	/*
518*0Sstevel@tonic-gate 	 * clear out any packets which had been previously received
519*0Sstevel@tonic-gate 	 * but not pulled off of the recv_packet queue.
520*0Sstevel@tonic-gate 	 */
521*0Sstevel@tonic-gate 
522*0Sstevel@tonic-gate 	free_pkt_list(&ifsp->if_recv_pkt_list);
523*0Sstevel@tonic-gate 
524*0Sstevel@tonic-gate 	if (stop == NULL) {
525*0Sstevel@tonic-gate 		ifsp->if_retrans_timer = -1;
526*0Sstevel@tonic-gate 		ifsp->if_send_timeout = 0;	/* prevents retransmissions */
527*0Sstevel@tonic-gate 	} else
528*0Sstevel@tonic-gate 		ifsp->if_send_timeout = next_retransmission(0);
529*0Sstevel@tonic-gate 
530*0Sstevel@tonic-gate 	return (send_pkt_internal(ifsp));
531*0Sstevel@tonic-gate }
532*0Sstevel@tonic-gate 
533*0Sstevel@tonic-gate /*
534*0Sstevel@tonic-gate  * retransmit(): retransmits the current packet on an interface
535*0Sstevel@tonic-gate  *
536*0Sstevel@tonic-gate  *   input: iu_tq_t *: unused
537*0Sstevel@tonic-gate  *	    void *: the struct ifslist * to send the packet on
538*0Sstevel@tonic-gate  *  output: void
539*0Sstevel@tonic-gate  */
540*0Sstevel@tonic-gate 
541*0Sstevel@tonic-gate /* ARGSUSED */
542*0Sstevel@tonic-gate static void
543*0Sstevel@tonic-gate retransmit(iu_tq_t *tqp, void *arg)
544*0Sstevel@tonic-gate {
545*0Sstevel@tonic-gate 	struct ifslist		*ifsp = (struct ifslist *)arg;
546*0Sstevel@tonic-gate 
547*0Sstevel@tonic-gate 	if (check_ifs(ifsp) == 0) {
548*0Sstevel@tonic-gate 		(void) release_ifs(ifsp);
549*0Sstevel@tonic-gate 		return;
550*0Sstevel@tonic-gate 	}
551*0Sstevel@tonic-gate 
552*0Sstevel@tonic-gate 	/*
553*0Sstevel@tonic-gate 	 * check the callback to see if we should keep sending retransmissions
554*0Sstevel@tonic-gate 	 */
555*0Sstevel@tonic-gate 
556*0Sstevel@tonic-gate 	if (ifsp->if_send_stop_func(ifsp, ifsp->if_packet_sent))
557*0Sstevel@tonic-gate 		return;
558*0Sstevel@tonic-gate 
559*0Sstevel@tonic-gate 	ifsp->if_send_timeout = next_retransmission(ifsp->if_send_timeout);
560*0Sstevel@tonic-gate 	(void) send_pkt_internal(ifsp);
561*0Sstevel@tonic-gate }
562*0Sstevel@tonic-gate 
563*0Sstevel@tonic-gate /*
564*0Sstevel@tonic-gate  * stop_pkt_retransmission(): stops retransmission of last sent packet
565*0Sstevel@tonic-gate  *
566*0Sstevel@tonic-gate  *   input: struct ifslist *: the interface to stop retransmission on
567*0Sstevel@tonic-gate  *  output: void
568*0Sstevel@tonic-gate  */
569*0Sstevel@tonic-gate 
570*0Sstevel@tonic-gate void
571*0Sstevel@tonic-gate stop_pkt_retransmission(struct ifslist *ifsp)
572*0Sstevel@tonic-gate {
573*0Sstevel@tonic-gate 	if (ifsp->if_retrans_timer != -1) {
574*0Sstevel@tonic-gate 		if (iu_cancel_timer(tq, ifsp->if_retrans_timer, NULL) == 1) {
575*0Sstevel@tonic-gate 			(void) release_ifs(ifsp);
576*0Sstevel@tonic-gate 			ifsp->if_retrans_timer = -1;
577*0Sstevel@tonic-gate 		}
578*0Sstevel@tonic-gate 	}
579*0Sstevel@tonic-gate }
580*0Sstevel@tonic-gate 
581*0Sstevel@tonic-gate /*
582*0Sstevel@tonic-gate  * recv_pkt(): receives packets on an interface (put on ifsp->if_recv_pkt_list)
583*0Sstevel@tonic-gate  *
584*0Sstevel@tonic-gate  *   input: struct ifslist *: the interface to receive packets on
585*0Sstevel@tonic-gate  *	    int: the file descriptor to receive the packet on
586*0Sstevel@tonic-gate  *	    dhcp_message_type_t: the types of packets to receive
587*0Sstevel@tonic-gate  *	    boolean_t: if B_TRUE, more than one packet can be received
588*0Sstevel@tonic-gate  *  output: int: 1 if a packet was received successfully, 0 otherwise
589*0Sstevel@tonic-gate  */
590*0Sstevel@tonic-gate 
591*0Sstevel@tonic-gate int
592*0Sstevel@tonic-gate recv_pkt(struct ifslist *ifsp, int fd, dhcp_message_type_t type,
593*0Sstevel@tonic-gate     boolean_t chain)
594*0Sstevel@tonic-gate {
595*0Sstevel@tonic-gate 	PKT_LIST	*plp;
596*0Sstevel@tonic-gate 	PKT		*pkt;
597*0Sstevel@tonic-gate 	ssize_t		retval;
598*0Sstevel@tonic-gate 	uchar_t		recv_pkt_type;
599*0Sstevel@tonic-gate 	const char	*recv_pkt_name;
600*0Sstevel@tonic-gate 
601*0Sstevel@tonic-gate 	/*
602*0Sstevel@tonic-gate 	 * collect replies.  chain them up if the chain flag is set
603*0Sstevel@tonic-gate 	 * and we've already got one, otherwise drop the packet.
604*0Sstevel@tonic-gate 	 * calloc the PKT_LIST since dhcp_options_scan() relies on it
605*0Sstevel@tonic-gate 	 * being zeroed.
606*0Sstevel@tonic-gate 	 */
607*0Sstevel@tonic-gate 
608*0Sstevel@tonic-gate 	pkt = calloc(1, ifsp->if_max);
609*0Sstevel@tonic-gate 	plp = calloc(1, sizeof (PKT_LIST));
610*0Sstevel@tonic-gate 	if (pkt == NULL || plp == NULL) {
611*0Sstevel@tonic-gate 		dhcpmsg(MSG_ERR, "recv_pkt: dropped packet");
612*0Sstevel@tonic-gate 		goto failure;
613*0Sstevel@tonic-gate 	}
614*0Sstevel@tonic-gate 
615*0Sstevel@tonic-gate 	plp->pkt = pkt;
616*0Sstevel@tonic-gate 
617*0Sstevel@tonic-gate 	switch (ifsp->if_state) {
618*0Sstevel@tonic-gate 
619*0Sstevel@tonic-gate 	case BOUND:
620*0Sstevel@tonic-gate 	case RENEWING:
621*0Sstevel@tonic-gate 	case REBINDING:
622*0Sstevel@tonic-gate 		retval = recvfrom(fd, pkt, ifsp->if_max, 0, NULL, 0);
623*0Sstevel@tonic-gate 		break;
624*0Sstevel@tonic-gate 
625*0Sstevel@tonic-gate 	default:
626*0Sstevel@tonic-gate 		retval = dlpi_recvfrom(fd, pkt, ifsp->if_max, 0);
627*0Sstevel@tonic-gate 		break;
628*0Sstevel@tonic-gate 	}
629*0Sstevel@tonic-gate 
630*0Sstevel@tonic-gate 	if (retval == -1) {
631*0Sstevel@tonic-gate 		dhcpmsg(MSG_ERR, "recv_pkt: recvfrom failed, dropped");
632*0Sstevel@tonic-gate 		goto failure;
633*0Sstevel@tonic-gate 	}
634*0Sstevel@tonic-gate 
635*0Sstevel@tonic-gate 	plp->len = retval;
636*0Sstevel@tonic-gate 
637*0Sstevel@tonic-gate 	switch (dhcp_options_scan(plp, B_TRUE)) {
638*0Sstevel@tonic-gate 
639*0Sstevel@tonic-gate 	case DHCP_WRONG_MSG_TYPE:
640*0Sstevel@tonic-gate 		dhcpmsg(MSG_WARNING, "recv_pkt: unexpected DHCP message");
641*0Sstevel@tonic-gate 		goto failure;
642*0Sstevel@tonic-gate 
643*0Sstevel@tonic-gate 	case DHCP_GARBLED_MSG_TYPE:
644*0Sstevel@tonic-gate 		dhcpmsg(MSG_WARNING, "recv_pkt: garbled DHCP message type");
645*0Sstevel@tonic-gate 		goto failure;
646*0Sstevel@tonic-gate 
647*0Sstevel@tonic-gate 	case DHCP_BAD_OPT_OVLD:
648*0Sstevel@tonic-gate 		dhcpmsg(MSG_WARNING, "recv_pkt: bad option overload");
649*0Sstevel@tonic-gate 		goto failure;
650*0Sstevel@tonic-gate 
651*0Sstevel@tonic-gate 	case 0:
652*0Sstevel@tonic-gate 		break;
653*0Sstevel@tonic-gate 
654*0Sstevel@tonic-gate 	default:
655*0Sstevel@tonic-gate 		dhcpmsg(MSG_WARNING, "recv_pkt: packet corrupted, dropped");
656*0Sstevel@tonic-gate 		goto failure;
657*0Sstevel@tonic-gate 	}
658*0Sstevel@tonic-gate 
659*0Sstevel@tonic-gate 	/*
660*0Sstevel@tonic-gate 	 * make sure the packet we got in was one we were expecting --
661*0Sstevel@tonic-gate 	 * it needs to have the right type and to have the same xid.
662*0Sstevel@tonic-gate 	 */
663*0Sstevel@tonic-gate 
664*0Sstevel@tonic-gate 	if (plp->opts[CD_DHCP_TYPE] != NULL)
665*0Sstevel@tonic-gate 		recv_pkt_type = *plp->opts[CD_DHCP_TYPE]->value;
666*0Sstevel@tonic-gate 	else
667*0Sstevel@tonic-gate 		recv_pkt_type = 0;
668*0Sstevel@tonic-gate 
669*0Sstevel@tonic-gate 	recv_pkt_name = pkt_type_to_string(recv_pkt_type);
670*0Sstevel@tonic-gate 
671*0Sstevel@tonic-gate 	if ((dhcp_type_ptob(recv_pkt_type) & type) == 0) {
672*0Sstevel@tonic-gate 		dhcpmsg(MSG_VERBOSE, "received unexpected %s packet on "
673*0Sstevel@tonic-gate 		    "%s, dropped", recv_pkt_name, ifsp->if_name);
674*0Sstevel@tonic-gate 		goto failure;
675*0Sstevel@tonic-gate 	}
676*0Sstevel@tonic-gate 
677*0Sstevel@tonic-gate 	/* the xid is opaque -- no byteorder work */
678*0Sstevel@tonic-gate 	if (plp->pkt->xid != ifsp->if_send_pkt.pkt->xid) {
679*0Sstevel@tonic-gate 		dhcpmsg(MSG_VERBOSE, "received unexpected packet xid (%#x "
680*0Sstevel@tonic-gate 		    "instead of %#x) on %s, dropped", plp->pkt->xid,
681*0Sstevel@tonic-gate 		    ifsp->if_send_pkt.pkt->xid, ifsp->if_name);
682*0Sstevel@tonic-gate 		goto failure;
683*0Sstevel@tonic-gate 	}
684*0Sstevel@tonic-gate 
685*0Sstevel@tonic-gate 	if (ifsp->if_recv_pkt_list != NULL) {
686*0Sstevel@tonic-gate 		if (chain == B_FALSE) {
687*0Sstevel@tonic-gate 			dhcpmsg(MSG_WARNING, "recv_pkt: unexpected additional "
688*0Sstevel@tonic-gate 			    "%s packet, dropped", recv_pkt_name);
689*0Sstevel@tonic-gate 			goto failure;
690*0Sstevel@tonic-gate 		}
691*0Sstevel@tonic-gate 	}
692*0Sstevel@tonic-gate 
693*0Sstevel@tonic-gate 	dhcpmsg(MSG_VERBOSE, "received %s packet on %s", recv_pkt_name,
694*0Sstevel@tonic-gate 	    ifsp->if_name);
695*0Sstevel@tonic-gate 
696*0Sstevel@tonic-gate 	prepend_to_pkt_list(&ifsp->if_recv_pkt_list, plp);
697*0Sstevel@tonic-gate 	ifsp->if_received++;
698*0Sstevel@tonic-gate 	return (1);
699*0Sstevel@tonic-gate 
700*0Sstevel@tonic-gate failure:
701*0Sstevel@tonic-gate 	free(pkt);
702*0Sstevel@tonic-gate 	free(plp);
703*0Sstevel@tonic-gate 	return (0);
704*0Sstevel@tonic-gate }
705*0Sstevel@tonic-gate 
706*0Sstevel@tonic-gate /*
707*0Sstevel@tonic-gate  * next_retransmission(): returns the number of seconds until the next
708*0Sstevel@tonic-gate  *			  retransmission, based on the algorithm in RFC2131
709*0Sstevel@tonic-gate  *
710*0Sstevel@tonic-gate  *   input: uint32_t: the number of milliseconds for the last retransmission
711*0Sstevel@tonic-gate  *  output: uint32_t: the number of milliseconds until the next retransmission
712*0Sstevel@tonic-gate  */
713*0Sstevel@tonic-gate 
714*0Sstevel@tonic-gate static uint32_t
715*0Sstevel@tonic-gate next_retransmission(uint32_t last_timeout_ms)
716*0Sstevel@tonic-gate {
717*0Sstevel@tonic-gate 	uint32_t	timeout_ms;
718*0Sstevel@tonic-gate 
719*0Sstevel@tonic-gate 	/*
720*0Sstevel@tonic-gate 	 * start at 4, and increase by a factor of 2 up to 64.  at each
721*0Sstevel@tonic-gate 	 * iteration, jitter the timeout by some fraction of a second.
722*0Sstevel@tonic-gate 	 */
723*0Sstevel@tonic-gate 	if (last_timeout_ms == 0)
724*0Sstevel@tonic-gate 		timeout_ms = 4 * MILLISEC;
725*0Sstevel@tonic-gate 	else
726*0Sstevel@tonic-gate 		timeout_ms = MIN(last_timeout_ms << 1, 64 * MILLISEC);
727*0Sstevel@tonic-gate 
728*0Sstevel@tonic-gate 	return (timeout_ms + ((lrand48() % (2 * MILLISEC)) - MILLISEC));
729*0Sstevel@tonic-gate }
730