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