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