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