10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*3431Scarlsonj * Common Development and Distribution License (the "License"). 6*3431Scarlsonj * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 22*3431Scarlsonj * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 270Sstevel@tonic-gate 280Sstevel@tonic-gate #include <string.h> 290Sstevel@tonic-gate #include <sys/types.h> 300Sstevel@tonic-gate #include <stdlib.h> 310Sstevel@tonic-gate #include <dhcpmsg.h> 320Sstevel@tonic-gate #include <stddef.h> 330Sstevel@tonic-gate #include <assert.h> 34*3431Scarlsonj #include <search.h> 35*3431Scarlsonj #include <alloca.h> 36*3431Scarlsonj #include <limits.h> 37*3431Scarlsonj #include <stropts.h> 38*3431Scarlsonj #include <netinet/dhcp6.h> 39*3431Scarlsonj #include <arpa/inet.h> 40*3431Scarlsonj #include <sys/sysmacros.h> 41*3431Scarlsonj #include <sys/sockio.h> 42*3431Scarlsonj #include <inet/ip6_asp.h> 430Sstevel@tonic-gate 440Sstevel@tonic-gate #include "states.h" 450Sstevel@tonic-gate #include "interface.h" 460Sstevel@tonic-gate #include "agent.h" 470Sstevel@tonic-gate #include "packet.h" 480Sstevel@tonic-gate #include "util.h" 49*3431Scarlsonj #include "dlpi_io.h" 500Sstevel@tonic-gate 51*3431Scarlsonj int v6_sock_fd = -1; 52*3431Scarlsonj int v4_sock_fd = -1; 53*3431Scarlsonj 54*3431Scarlsonj const in6_addr_t ipv6_all_dhcp_relay_and_servers = { 55*3431Scarlsonj 0xff, 0x02, 0x00, 0x00, 56*3431Scarlsonj 0x00, 0x00, 0x00, 0x00, 57*3431Scarlsonj 0x00, 0x00, 0x00, 0x00, 58*3431Scarlsonj 0x00, 0x01, 0x00, 0x02 59*3431Scarlsonj }; 600Sstevel@tonic-gate 610Sstevel@tonic-gate /* 62*3431Scarlsonj * We have our own version of this constant because dhcpagent is compiled with 63*3431Scarlsonj * -lxnet. 640Sstevel@tonic-gate */ 65*3431Scarlsonj const in6_addr_t my_in6addr_any = IN6ADDR_ANY_INIT; 660Sstevel@tonic-gate 67*3431Scarlsonj static void retransmit(iu_tq_t *, void *); 68*3431Scarlsonj static void next_retransmission(dhcp_smach_t *, boolean_t, boolean_t); 69*3431Scarlsonj static boolean_t send_pkt_internal(dhcp_smach_t *); 700Sstevel@tonic-gate 710Sstevel@tonic-gate /* 72*3431Scarlsonj * pkt_send_type(): returns an integer representing the packet's type; only 73*3431Scarlsonj * for use with outbound packets. 740Sstevel@tonic-gate * 75*3431Scarlsonj * input: dhcp_pkt_t *: the packet to examine 760Sstevel@tonic-gate * output: uchar_t: the packet type (0 if unknown) 770Sstevel@tonic-gate */ 780Sstevel@tonic-gate 790Sstevel@tonic-gate static uchar_t 80*3431Scarlsonj pkt_send_type(const dhcp_pkt_t *dpkt) 810Sstevel@tonic-gate { 82*3431Scarlsonj const uchar_t *option; 83*3431Scarlsonj 84*3431Scarlsonj if (dpkt->pkt_isv6) 85*3431Scarlsonj return (((const dhcpv6_message_t *)dpkt->pkt)->d6m_msg_type); 860Sstevel@tonic-gate 870Sstevel@tonic-gate /* 880Sstevel@tonic-gate * this is a little dirty but it should get the job done. 890Sstevel@tonic-gate * assumes that the type is in the statically allocated part 900Sstevel@tonic-gate * of the options field. 910Sstevel@tonic-gate */ 920Sstevel@tonic-gate 93*3431Scarlsonj option = dpkt->pkt->options; 94*3431Scarlsonj for (;;) { 95*3431Scarlsonj if (*option == CD_PAD) { 96*3431Scarlsonj option++; 97*3431Scarlsonj continue; 98*3431Scarlsonj } 99*3431Scarlsonj if (*option == CD_END || 100*3431Scarlsonj option + 2 - dpkt->pkt->options >= 101*3431Scarlsonj sizeof (dpkt->pkt->options)) 1020Sstevel@tonic-gate return (0); 103*3431Scarlsonj if (*option == CD_DHCP_TYPE) 104*3431Scarlsonj break; 1050Sstevel@tonic-gate option++; 106*3431Scarlsonj option += *option + 1; 1070Sstevel@tonic-gate } 1080Sstevel@tonic-gate 1090Sstevel@tonic-gate return (option[2]); 1100Sstevel@tonic-gate } 1110Sstevel@tonic-gate 1120Sstevel@tonic-gate /* 113*3431Scarlsonj * pkt_recv_type(): returns an integer representing the packet's type; only 114*3431Scarlsonj * for use with inbound packets. 115*3431Scarlsonj * 116*3431Scarlsonj * input: dhcp_pkt_t *: the packet to examine 117*3431Scarlsonj * output: uchar_t: the packet type (0 if unknown) 118*3431Scarlsonj */ 119*3431Scarlsonj 120*3431Scarlsonj uchar_t 121*3431Scarlsonj pkt_recv_type(const PKT_LIST *plp) 122*3431Scarlsonj { 123*3431Scarlsonj if (plp->isv6) 124*3431Scarlsonj return (((const dhcpv6_message_t *)plp->pkt)->d6m_msg_type); 125*3431Scarlsonj else if (plp->opts[CD_DHCP_TYPE] != NULL) 126*3431Scarlsonj return (plp->opts[CD_DHCP_TYPE]->value[0]); 127*3431Scarlsonj else 128*3431Scarlsonj return (0); 129*3431Scarlsonj } 130*3431Scarlsonj 131*3431Scarlsonj /* 132*3431Scarlsonj * pkt_get_xid(): returns transaction ID from a DHCP packet. 133*3431Scarlsonj * 134*3431Scarlsonj * input: const PKT *: the packet to examine 135*3431Scarlsonj * output: uint_t: the transaction ID (0 if unknown) 136*3431Scarlsonj */ 137*3431Scarlsonj 138*3431Scarlsonj uint_t 139*3431Scarlsonj pkt_get_xid(const PKT *pkt, boolean_t isv6) 140*3431Scarlsonj { 141*3431Scarlsonj if (pkt == NULL) 142*3431Scarlsonj return (0); 143*3431Scarlsonj if (isv6) 144*3431Scarlsonj return (DHCPV6_GET_TRANSID((const dhcpv6_message_t *)pkt)); 145*3431Scarlsonj else 146*3431Scarlsonj return (pkt->xid); 147*3431Scarlsonj } 148*3431Scarlsonj 149*3431Scarlsonj /* 1500Sstevel@tonic-gate * init_pkt(): initializes and returns a packet of a given type 1510Sstevel@tonic-gate * 152*3431Scarlsonj * input: dhcp_smach_t *: the state machine that will send the packet 1530Sstevel@tonic-gate * uchar_t: the packet type (DHCP message type) 154*3431Scarlsonj * output: dhcp_pkt_t *: a pointer to the initialized packet; may be NULL 1550Sstevel@tonic-gate */ 1560Sstevel@tonic-gate 1570Sstevel@tonic-gate dhcp_pkt_t * 158*3431Scarlsonj init_pkt(dhcp_smach_t *dsmp, uchar_t type) 1590Sstevel@tonic-gate { 160*3431Scarlsonj uint_t mtu; 161*3431Scarlsonj dhcp_pkt_t *dpkt = &dsmp->dsm_send_pkt; 162*3431Scarlsonj dhcp_lif_t *lif = dsmp->dsm_lif; 163*3431Scarlsonj dhcp_pif_t *pif = lif->lif_pif; 1640Sstevel@tonic-gate uint32_t xid; 165*3431Scarlsonj boolean_t isv6; 1660Sstevel@tonic-gate 167*3431Scarlsonj mtu = dsmp->dsm_using_dlpi ? pif->pif_max : lif->lif_max; 168*3431Scarlsonj dpkt->pkt_isv6 = isv6 = pif->pif_isv6; 1690Sstevel@tonic-gate 1700Sstevel@tonic-gate /* 1710Sstevel@tonic-gate * since multiple dhcp leases may be maintained over the same dlpi 1720Sstevel@tonic-gate * device (e.g. "hme0" and "hme0:1"), make sure the xid is unique. 173*3431Scarlsonj * 174*3431Scarlsonj * Note that transaction ID zero is intentionally never assigned. 175*3431Scarlsonj * That's used to represent "no ID." Also note that transaction IDs 176*3431Scarlsonj * are only 24 bits long in DHCPv6. 1770Sstevel@tonic-gate */ 1780Sstevel@tonic-gate 1790Sstevel@tonic-gate do { 1800Sstevel@tonic-gate xid = mrand48(); 181*3431Scarlsonj if (isv6) 182*3431Scarlsonj xid &= 0xFFFFFF; 183*3431Scarlsonj } while (xid == 0 || 184*3431Scarlsonj lookup_smach_by_xid(xid, NULL, dpkt->pkt_isv6) != NULL); 185*3431Scarlsonj 186*3431Scarlsonj if (isv6) { 187*3431Scarlsonj dhcpv6_message_t *v6; 188*3431Scarlsonj 189*3431Scarlsonj if (mtu != dpkt->pkt_max_len && 190*3431Scarlsonj (v6 = realloc(dpkt->pkt, mtu)) != NULL) { 191*3431Scarlsonj /* LINTED: alignment known to be correct */ 192*3431Scarlsonj dpkt->pkt = (PKT *)v6; 193*3431Scarlsonj dpkt->pkt_max_len = mtu; 194*3431Scarlsonj } 195*3431Scarlsonj 196*3431Scarlsonj if (sizeof (*v6) > dpkt->pkt_max_len) { 197*3431Scarlsonj dhcpmsg(MSG_ERR, "init_pkt: cannot allocate v6 pkt: %u", 198*3431Scarlsonj mtu); 199*3431Scarlsonj return (NULL); 200*3431Scarlsonj } 201*3431Scarlsonj 202*3431Scarlsonj v6 = (dhcpv6_message_t *)dpkt->pkt; 203*3431Scarlsonj dpkt->pkt_cur_len = sizeof (*v6); 204*3431Scarlsonj 205*3431Scarlsonj (void) memset(v6, 0, dpkt->pkt_max_len); 206*3431Scarlsonj 207*3431Scarlsonj v6->d6m_msg_type = type; 208*3431Scarlsonj DHCPV6_SET_TRANSID(v6, xid); 209*3431Scarlsonj 210*3431Scarlsonj if (dsmp->dsm_cidlen > 0 && 211*3431Scarlsonj add_pkt_opt(dpkt, DHCPV6_OPT_CLIENTID, dsmp->dsm_cid, 212*3431Scarlsonj dsmp->dsm_cidlen) == NULL) { 213*3431Scarlsonj dhcpmsg(MSG_WARNING, 214*3431Scarlsonj "init_pkt: cannot insert client ID"); 215*3431Scarlsonj return (NULL); 216*3431Scarlsonj } 217*3431Scarlsonj 218*3431Scarlsonj /* For v6, time starts with the creation of a transaction */ 219*3431Scarlsonj dsmp->dsm_neg_hrtime = gethrtime(); 220*3431Scarlsonj dsmp->dsm_newstart_monosec = monosec(); 221*3431Scarlsonj } else { 222*3431Scarlsonj static uint8_t bootmagic[] = BOOTMAGIC; 223*3431Scarlsonj PKT *v4; 224*3431Scarlsonj 225*3431Scarlsonj if (mtu != dpkt->pkt_max_len && 226*3431Scarlsonj (v4 = realloc(dpkt->pkt, mtu)) != NULL) { 227*3431Scarlsonj dpkt->pkt = v4; 228*3431Scarlsonj dpkt->pkt_max_len = mtu; 229*3431Scarlsonj } 2300Sstevel@tonic-gate 231*3431Scarlsonj if (offsetof(PKT, options) > dpkt->pkt_max_len) { 232*3431Scarlsonj dhcpmsg(MSG_ERR, "init_pkt: cannot allocate v4 pkt: %u", 233*3431Scarlsonj mtu); 234*3431Scarlsonj return (NULL); 235*3431Scarlsonj } 236*3431Scarlsonj 237*3431Scarlsonj v4 = dpkt->pkt; 238*3431Scarlsonj dpkt->pkt_cur_len = offsetof(PKT, options); 2390Sstevel@tonic-gate 240*3431Scarlsonj (void) memset(v4, 0, dpkt->pkt_max_len); 241*3431Scarlsonj (void) memcpy(v4->cookie, bootmagic, sizeof (bootmagic)); 242*3431Scarlsonj if (pif->pif_hwlen <= sizeof (v4->chaddr)) { 243*3431Scarlsonj v4->hlen = pif->pif_hwlen; 244*3431Scarlsonj (void) memcpy(v4->chaddr, pif->pif_hwaddr, 245*3431Scarlsonj pif->pif_hwlen); 246*3431Scarlsonj } else { 247*3431Scarlsonj /* 248*3431Scarlsonj * The mac address does not fit in the chaddr 249*3431Scarlsonj * field, thus it can not be sent to the server, 250*3431Scarlsonj * thus server can not unicast the reply. Per 251*3431Scarlsonj * RFC 2131 4.4.1, client can set this bit in 252*3431Scarlsonj * DISCOVER/REQUEST. If the client is already 253*3431Scarlsonj * in BOUND/REBINDING/RENEWING state, do not set 254*3431Scarlsonj * this bit, as it can respond to unicast responses 255*3431Scarlsonj * from server using the 'ciaddr' address. 256*3431Scarlsonj */ 257*3431Scarlsonj if (type == DISCOVER || 258*3431Scarlsonj (type == REQUEST && dsmp->dsm_state != RENEWING && 259*3431Scarlsonj dsmp->dsm_state != REBINDING && 260*3431Scarlsonj dsmp->dsm_state != BOUND)) 261*3431Scarlsonj v4->flags = htons(BCAST_MASK); 262*3431Scarlsonj } 263*3431Scarlsonj 264*3431Scarlsonj v4->xid = xid; 265*3431Scarlsonj v4->op = BOOTREQUEST; 266*3431Scarlsonj v4->htype = pif->pif_hwtype; 267*3431Scarlsonj 268*3431Scarlsonj if (add_pkt_opt(dpkt, CD_DHCP_TYPE, &type, 1) == NULL) { 269*3431Scarlsonj dhcpmsg(MSG_WARNING, 270*3431Scarlsonj "init_pkt: cannot set DHCP packet type"); 271*3431Scarlsonj return (NULL); 272*3431Scarlsonj } 273*3431Scarlsonj 274*3431Scarlsonj if (dsmp->dsm_cidlen > 0 && 275*3431Scarlsonj add_pkt_opt(dpkt, CD_CLIENT_ID, dsmp->dsm_cid, 276*3431Scarlsonj dsmp->dsm_cidlen) == NULL) { 277*3431Scarlsonj dhcpmsg(MSG_WARNING, 278*3431Scarlsonj "init_pkt: cannot insert client ID"); 279*3431Scarlsonj return (NULL); 280*3431Scarlsonj } 281*3431Scarlsonj } 2820Sstevel@tonic-gate 2830Sstevel@tonic-gate return (dpkt); 2840Sstevel@tonic-gate } 2850Sstevel@tonic-gate 2860Sstevel@tonic-gate /* 287*3431Scarlsonj * remove_pkt_opt(): removes the first instance of an option from a dhcp_pkt_t 288*3431Scarlsonj * 289*3431Scarlsonj * input: dhcp_pkt_t *: the packet to remove the option from 290*3431Scarlsonj * uint_t: the type of option being added 291*3431Scarlsonj * output: boolean_t: B_TRUE on success, B_FALSE on failure 292*3431Scarlsonj * note: currently does not work with DHCPv6 suboptions, or to remove 293*3431Scarlsonj * arbitrary option instances. 294*3431Scarlsonj */ 295*3431Scarlsonj 296*3431Scarlsonj boolean_t 297*3431Scarlsonj remove_pkt_opt(dhcp_pkt_t *dpkt, uint_t opt_type) 298*3431Scarlsonj { 299*3431Scarlsonj uchar_t *raw_pkt, *raw_end, *next; 300*3431Scarlsonj uint_t len; 301*3431Scarlsonj 302*3431Scarlsonj raw_pkt = (uchar_t *)dpkt->pkt; 303*3431Scarlsonj raw_end = raw_pkt + dpkt->pkt_cur_len; 304*3431Scarlsonj if (dpkt->pkt_isv6) { 305*3431Scarlsonj dhcpv6_option_t d6o; 306*3431Scarlsonj 307*3431Scarlsonj raw_pkt += sizeof (dhcpv6_message_t); 308*3431Scarlsonj 309*3431Scarlsonj opt_type = htons(opt_type); 310*3431Scarlsonj while (raw_pkt + sizeof (d6o) <= raw_end) { 311*3431Scarlsonj (void) memcpy(&d6o, raw_pkt, sizeof (d6o)); 312*3431Scarlsonj len = ntohs(d6o.d6o_len) + sizeof (d6o); 313*3431Scarlsonj if (len > raw_end - raw_pkt) 314*3431Scarlsonj break; 315*3431Scarlsonj next = raw_pkt + len; 316*3431Scarlsonj if (d6o.d6o_code == opt_type) { 317*3431Scarlsonj if (next < raw_end) { 318*3431Scarlsonj (void) memmove(raw_pkt, next, 319*3431Scarlsonj raw_end - next); 320*3431Scarlsonj } 321*3431Scarlsonj dpkt->pkt_cur_len -= len; 322*3431Scarlsonj return (B_TRUE); 323*3431Scarlsonj } 324*3431Scarlsonj raw_pkt = next; 325*3431Scarlsonj } 326*3431Scarlsonj } else { 327*3431Scarlsonj uchar_t *pstart, *padrun; 328*3431Scarlsonj 329*3431Scarlsonj raw_pkt += offsetof(PKT, options); 330*3431Scarlsonj pstart = raw_pkt; 331*3431Scarlsonj 332*3431Scarlsonj if (opt_type == CD_END || opt_type == CD_PAD) 333*3431Scarlsonj return (B_FALSE); 334*3431Scarlsonj 335*3431Scarlsonj padrun = NULL; 336*3431Scarlsonj while (raw_pkt + 1 <= raw_end) { 337*3431Scarlsonj if (*raw_pkt == CD_END) 338*3431Scarlsonj break; 339*3431Scarlsonj if (*raw_pkt == CD_PAD) { 340*3431Scarlsonj if (padrun == NULL) 341*3431Scarlsonj padrun = raw_pkt; 342*3431Scarlsonj raw_pkt++; 343*3431Scarlsonj continue; 344*3431Scarlsonj } 345*3431Scarlsonj if (raw_pkt + 2 > raw_end) 346*3431Scarlsonj break; 347*3431Scarlsonj len = raw_pkt[1]; 348*3431Scarlsonj if (len > raw_end - raw_pkt || len < 2) 349*3431Scarlsonj break; 350*3431Scarlsonj next = raw_pkt + len; 351*3431Scarlsonj if (*raw_pkt == opt_type) { 352*3431Scarlsonj if (next < raw_end) { 353*3431Scarlsonj int toadd = (4 + ((next-pstart)&3) - 354*3431Scarlsonj ((raw_pkt-pstart)&3)) & 3; 355*3431Scarlsonj int torem = 4 - toadd; 356*3431Scarlsonj 357*3431Scarlsonj if (torem != 4 && padrun != NULL && 358*3431Scarlsonj (raw_pkt - padrun) >= torem) { 359*3431Scarlsonj raw_pkt -= torem; 360*3431Scarlsonj dpkt->pkt_cur_len -= torem; 361*3431Scarlsonj } else if (toadd > 0) { 362*3431Scarlsonj (void) memset(raw_pkt, CD_PAD, 363*3431Scarlsonj toadd); 364*3431Scarlsonj raw_pkt += toadd; 365*3431Scarlsonj /* max is not an issue here */ 366*3431Scarlsonj dpkt->pkt_cur_len += toadd; 367*3431Scarlsonj } 368*3431Scarlsonj if (raw_pkt != next) { 369*3431Scarlsonj (void) memmove(raw_pkt, next, 370*3431Scarlsonj raw_end - next); 371*3431Scarlsonj } 372*3431Scarlsonj } 373*3431Scarlsonj dpkt->pkt_cur_len -= len; 374*3431Scarlsonj return (B_TRUE); 375*3431Scarlsonj } 376*3431Scarlsonj padrun = NULL; 377*3431Scarlsonj raw_pkt = next; 378*3431Scarlsonj } 379*3431Scarlsonj } 380*3431Scarlsonj return (B_FALSE); 381*3431Scarlsonj } 382*3431Scarlsonj 383*3431Scarlsonj /* 384*3431Scarlsonj * update_v6opt_len(): updates the length field of a DHCPv6 option. 385*3431Scarlsonj * 386*3431Scarlsonj * input: dhcpv6_option_t *: option to be updated 387*3431Scarlsonj * int: number of octets to add or subtract 388*3431Scarlsonj * output: boolean_t: B_TRUE on success, B_FALSE on failure 389*3431Scarlsonj */ 390*3431Scarlsonj 391*3431Scarlsonj boolean_t 392*3431Scarlsonj update_v6opt_len(dhcpv6_option_t *opt, int adjust) 393*3431Scarlsonj { 394*3431Scarlsonj dhcpv6_option_t optval; 395*3431Scarlsonj 396*3431Scarlsonj (void) memcpy(&optval, opt, sizeof (optval)); 397*3431Scarlsonj adjust += ntohs(optval.d6o_len); 398*3431Scarlsonj if (adjust < 0 || adjust > UINT16_MAX) { 399*3431Scarlsonj return (B_FALSE); 400*3431Scarlsonj } else { 401*3431Scarlsonj optval.d6o_len = htons(adjust); 402*3431Scarlsonj (void) memcpy(opt, &optval, sizeof (optval)); 403*3431Scarlsonj return (B_TRUE); 404*3431Scarlsonj } 405*3431Scarlsonj } 406*3431Scarlsonj 407*3431Scarlsonj /* 4080Sstevel@tonic-gate * add_pkt_opt(): adds an option to a dhcp_pkt_t 4090Sstevel@tonic-gate * 4100Sstevel@tonic-gate * input: dhcp_pkt_t *: the packet to add the option to 411*3431Scarlsonj * uint_t: the type of option being added 4120Sstevel@tonic-gate * const void *: the value of that option 413*3431Scarlsonj * uint_t: the length of the value of the option 414*3431Scarlsonj * output: void *: pointer to the option that was added, or NULL on failure. 4150Sstevel@tonic-gate */ 4160Sstevel@tonic-gate 417*3431Scarlsonj void * 418*3431Scarlsonj add_pkt_opt(dhcp_pkt_t *dpkt, uint_t opt_type, const void *opt_val, 419*3431Scarlsonj uint_t opt_len) 4200Sstevel@tonic-gate { 421*3431Scarlsonj uchar_t *raw_pkt; 422*3431Scarlsonj int req_len; 423*3431Scarlsonj void *optr; 424*3431Scarlsonj 425*3431Scarlsonj raw_pkt = (uchar_t *)dpkt->pkt; 426*3431Scarlsonj optr = raw_pkt + dpkt->pkt_cur_len; 427*3431Scarlsonj if (dpkt->pkt_isv6) { 428*3431Scarlsonj dhcpv6_option_t d6o; 429*3431Scarlsonj 430*3431Scarlsonj req_len = opt_len + sizeof (d6o); 431*3431Scarlsonj 432*3431Scarlsonj if (dpkt->pkt_cur_len + req_len > dpkt->pkt_max_len) { 433*3431Scarlsonj dhcpmsg(MSG_WARNING, 434*3431Scarlsonj "add_pkt_opt: not enough room for v6 option %u in " 435*3431Scarlsonj "packet (%u + %u > %u)", opt_type, 436*3431Scarlsonj dpkt->pkt_cur_len, req_len, dpkt->pkt_max_len); 437*3431Scarlsonj return (NULL); 438*3431Scarlsonj } 439*3431Scarlsonj d6o.d6o_code = htons(opt_type); 440*3431Scarlsonj d6o.d6o_len = htons(opt_len); 441*3431Scarlsonj (void) memcpy(&raw_pkt[dpkt->pkt_cur_len], &d6o, sizeof (d6o)); 442*3431Scarlsonj dpkt->pkt_cur_len += sizeof (d6o); 443*3431Scarlsonj if (opt_len > 0) { 444*3431Scarlsonj (void) memcpy(&raw_pkt[dpkt->pkt_cur_len], opt_val, 445*3431Scarlsonj opt_len); 446*3431Scarlsonj dpkt->pkt_cur_len += opt_len; 447*3431Scarlsonj } 448*3431Scarlsonj } else { 449*3431Scarlsonj req_len = opt_len + 2; /* + 2 for code & length bytes */ 450*3431Scarlsonj 451*3431Scarlsonj /* CD_END and CD_PAD options don't have a length field */ 452*3431Scarlsonj if (opt_type == CD_END || opt_type == CD_PAD) { 453*3431Scarlsonj req_len = 1; 454*3431Scarlsonj } else if (opt_val == NULL) { 455*3431Scarlsonj dhcpmsg(MSG_ERROR, "add_pkt_opt: option type %d is " 456*3431Scarlsonj "missing required value", opt_type); 457*3431Scarlsonj return (NULL); 458*3431Scarlsonj } 459*3431Scarlsonj 460*3431Scarlsonj if ((dpkt->pkt_cur_len + req_len) > dpkt->pkt_max_len) { 461*3431Scarlsonj dhcpmsg(MSG_WARNING, 462*3431Scarlsonj "add_pkt_opt: not enough room for v4 option %u in " 463*3431Scarlsonj "packet", opt_type); 464*3431Scarlsonj return (NULL); 465*3431Scarlsonj } 466*3431Scarlsonj 467*3431Scarlsonj raw_pkt[dpkt->pkt_cur_len++] = opt_type; 4680Sstevel@tonic-gate 469*3431Scarlsonj if (req_len > 1) { 470*3431Scarlsonj raw_pkt[dpkt->pkt_cur_len++] = opt_len; 471*3431Scarlsonj if (opt_len > 0) { 472*3431Scarlsonj (void) memcpy(&raw_pkt[dpkt->pkt_cur_len], 473*3431Scarlsonj opt_val, opt_len); 474*3431Scarlsonj dpkt->pkt_cur_len += opt_len; 475*3431Scarlsonj } 476*3431Scarlsonj } 477*3431Scarlsonj } 478*3431Scarlsonj return (optr); 479*3431Scarlsonj } 4800Sstevel@tonic-gate 481*3431Scarlsonj /* 482*3431Scarlsonj * add_pkt_subopt(): adds an option to a dhcp_pkt_t option. DHCPv6-specific, 483*3431Scarlsonj * but could be extended to IPv4 DHCP if necessary. Assumes 484*3431Scarlsonj * that if the parent isn't a top-level option, the caller 485*3431Scarlsonj * will adjust any upper-level options recursively using 486*3431Scarlsonj * update_v6opt_len. 487*3431Scarlsonj * 488*3431Scarlsonj * input: dhcp_pkt_t *: the packet to add the suboption to 489*3431Scarlsonj * dhcpv6_option_t *: the start of the option to that should contain 490*3431Scarlsonj * it (parent) 491*3431Scarlsonj * uint_t: the type of suboption being added 492*3431Scarlsonj * const void *: the value of that option 493*3431Scarlsonj * uint_t: the length of the value of the option 494*3431Scarlsonj * output: void *: pointer to the suboption that was added, or NULL on 495*3431Scarlsonj * failure. 496*3431Scarlsonj */ 497*3431Scarlsonj 498*3431Scarlsonj void * 499*3431Scarlsonj add_pkt_subopt(dhcp_pkt_t *dpkt, dhcpv6_option_t *parentopt, uint_t opt_type, 500*3431Scarlsonj const void *opt_val, uint_t opt_len) 501*3431Scarlsonj { 502*3431Scarlsonj uchar_t *raw_pkt; 503*3431Scarlsonj int req_len; 504*3431Scarlsonj void *optr; 505*3431Scarlsonj dhcpv6_option_t d6o; 506*3431Scarlsonj uchar_t *optend; 507*3431Scarlsonj int olen; 508*3431Scarlsonj 509*3431Scarlsonj if (!dpkt->pkt_isv6) 510*3431Scarlsonj return (NULL); 511*3431Scarlsonj 512*3431Scarlsonj raw_pkt = (uchar_t *)dpkt->pkt; 513*3431Scarlsonj req_len = opt_len + sizeof (d6o); 514*3431Scarlsonj 515*3431Scarlsonj if (dpkt->pkt_cur_len + req_len > dpkt->pkt_max_len) { 516*3431Scarlsonj dhcpmsg(MSG_WARNING, 517*3431Scarlsonj "add_pkt_subopt: not enough room for v6 suboption %u in " 518*3431Scarlsonj "packet (%u + %u > %u)", opt_type, 519*3431Scarlsonj dpkt->pkt_cur_len, req_len, dpkt->pkt_max_len); 520*3431Scarlsonj return (NULL); 5210Sstevel@tonic-gate } 5220Sstevel@tonic-gate 523*3431Scarlsonj /* 524*3431Scarlsonj * Update the parent option to include room for this option, 525*3431Scarlsonj * and compute the insertion point. 526*3431Scarlsonj */ 527*3431Scarlsonj (void) memcpy(&d6o, parentopt, sizeof (d6o)); 528*3431Scarlsonj olen = ntohs(d6o.d6o_len); 529*3431Scarlsonj optend = (uchar_t *)(parentopt + 1) + olen; 530*3431Scarlsonj olen += req_len; 531*3431Scarlsonj d6o.d6o_len = htons(olen); 532*3431Scarlsonj (void) memcpy(parentopt, &d6o, sizeof (d6o)); 5330Sstevel@tonic-gate 534*3431Scarlsonj /* 535*3431Scarlsonj * If there's anything at the end to move, then move it. Also bump up 536*3431Scarlsonj * the packet size. 537*3431Scarlsonj */ 538*3431Scarlsonj if (optend < raw_pkt + dpkt->pkt_cur_len) { 539*3431Scarlsonj (void) memmove(optend + req_len, optend, 540*3431Scarlsonj (raw_pkt + dpkt->pkt_cur_len) - optend); 5410Sstevel@tonic-gate } 542*3431Scarlsonj dpkt->pkt_cur_len += req_len; 543*3431Scarlsonj 544*3431Scarlsonj /* 545*3431Scarlsonj * Now format the suboption and add it in. 546*3431Scarlsonj */ 547*3431Scarlsonj optr = optend; 548*3431Scarlsonj d6o.d6o_code = htons(opt_type); 549*3431Scarlsonj d6o.d6o_len = htons(opt_len); 550*3431Scarlsonj (void) memcpy(optend, &d6o, sizeof (d6o)); 551*3431Scarlsonj if (opt_len > 0) 552*3431Scarlsonj (void) memcpy(optend + sizeof (d6o), opt_val, opt_len); 553*3431Scarlsonj return (optr); 5540Sstevel@tonic-gate } 5550Sstevel@tonic-gate 5560Sstevel@tonic-gate /* 5570Sstevel@tonic-gate * add_pkt_opt16(): adds an option with a 16-bit value to a dhcp_pkt_t 5580Sstevel@tonic-gate * 5590Sstevel@tonic-gate * input: dhcp_pkt_t *: the packet to add the option to 560*3431Scarlsonj * uint_t: the type of option being added 5610Sstevel@tonic-gate * uint16_t: the value of that option 562*3431Scarlsonj * output: void *: pointer to the option that was added, or NULL on failure. 5630Sstevel@tonic-gate */ 5640Sstevel@tonic-gate 565*3431Scarlsonj void * 566*3431Scarlsonj add_pkt_opt16(dhcp_pkt_t *dpkt, uint_t opt_type, uint16_t opt_value) 5670Sstevel@tonic-gate { 568*3431Scarlsonj return (add_pkt_opt(dpkt, opt_type, &opt_value, 2)); 5690Sstevel@tonic-gate } 5700Sstevel@tonic-gate 5710Sstevel@tonic-gate /* 5720Sstevel@tonic-gate * add_pkt_opt32(): adds an option with a 32-bit value to a dhcp_pkt_t 5730Sstevel@tonic-gate * 5740Sstevel@tonic-gate * input: dhcp_pkt_t *: the packet to add the option to 575*3431Scarlsonj * uint_t: the type of option being added 5760Sstevel@tonic-gate * uint32_t: the value of that option 577*3431Scarlsonj * output: void *: pointer to the option that was added, or NULL on failure. 578*3431Scarlsonj */ 579*3431Scarlsonj 580*3431Scarlsonj void * 581*3431Scarlsonj add_pkt_opt32(dhcp_pkt_t *dpkt, uint_t opt_type, uint32_t opt_value) 582*3431Scarlsonj { 583*3431Scarlsonj return (add_pkt_opt(dpkt, opt_type, &opt_value, 4)); 584*3431Scarlsonj } 585*3431Scarlsonj 586*3431Scarlsonj /* 587*3431Scarlsonj * add_pkt_prl(): adds the parameter request option to the packet 588*3431Scarlsonj * 589*3431Scarlsonj * input: dhcp_pkt_t *: the packet to add the option to 590*3431Scarlsonj * dhcp_smach_t *: state machine with request option 591*3431Scarlsonj * output: void *: pointer to the option that was added, or NULL on failure. 5920Sstevel@tonic-gate */ 5930Sstevel@tonic-gate 594*3431Scarlsonj void * 595*3431Scarlsonj add_pkt_prl(dhcp_pkt_t *dpkt, dhcp_smach_t *dsmp) 5960Sstevel@tonic-gate { 597*3431Scarlsonj uint_t len; 598*3431Scarlsonj 599*3431Scarlsonj if (dsmp->dsm_prllen == 0) 600*3431Scarlsonj return (0); 601*3431Scarlsonj 602*3431Scarlsonj if (dpkt->pkt_isv6) { 603*3431Scarlsonj uint16_t *prl; 604*3431Scarlsonj 605*3431Scarlsonj /* 606*3431Scarlsonj * RFC 3315 requires that we include the option, even if we 607*3431Scarlsonj * have nothing to request. 608*3431Scarlsonj */ 609*3431Scarlsonj if (dsmp->dsm_prllen == 0) 610*3431Scarlsonj prl = NULL; 611*3431Scarlsonj else 612*3431Scarlsonj prl = alloca(dsmp->dsm_prllen * sizeof (uint16_t)); 613*3431Scarlsonj 614*3431Scarlsonj for (len = 0; len < dsmp->dsm_prllen; len++) 615*3431Scarlsonj prl[len] = htons(dsmp->dsm_prl[len]); 616*3431Scarlsonj return (add_pkt_opt(dpkt, DHCPV6_OPT_ORO, prl, 617*3431Scarlsonj len * sizeof (uint16_t))); 618*3431Scarlsonj } else { 619*3431Scarlsonj uint8_t *prl = alloca(dsmp->dsm_prllen); 620*3431Scarlsonj 621*3431Scarlsonj for (len = 0; len < dsmp->dsm_prllen; len++) 622*3431Scarlsonj prl[len] = dsmp->dsm_prl[len]; 623*3431Scarlsonj return (add_pkt_opt(dpkt, CD_REQUEST_LIST, prl, len)); 624*3431Scarlsonj } 6250Sstevel@tonic-gate } 6260Sstevel@tonic-gate 6270Sstevel@tonic-gate /* 628*3431Scarlsonj * add_pkt_lif(): Adds CD_REQUESTED_IP_ADDR (IPv4 DHCP) or IA_NA and IAADDR 629*3431Scarlsonj * (DHCPv6) options to the packet to represent the given LIF. 6300Sstevel@tonic-gate * 631*3431Scarlsonj * input: dhcp_pkt_t *: the packet to add the options to 632*3431Scarlsonj * dhcp_lif_t *: the logical interface to represent 633*3431Scarlsonj * int: status code (unused for IPv4 DHCP) 634*3431Scarlsonj * const char *: message to include with status option, or NULL 635*3431Scarlsonj * output: boolean_t: B_TRUE on success, B_FALSE on failure 6360Sstevel@tonic-gate */ 6370Sstevel@tonic-gate 638*3431Scarlsonj boolean_t 639*3431Scarlsonj add_pkt_lif(dhcp_pkt_t *dpkt, dhcp_lif_t *lif, int status, const char *msg) 6400Sstevel@tonic-gate { 641*3431Scarlsonj if (lif->lif_pif->pif_isv6) { 642*3431Scarlsonj dhcp_smach_t *dsmp; 643*3431Scarlsonj dhcpv6_message_t *d6m; 644*3431Scarlsonj dhcpv6_ia_na_t d6in; 645*3431Scarlsonj dhcpv6_iaaddr_t d6ia; 646*3431Scarlsonj uint32_t iaid; 647*3431Scarlsonj uint16_t *statusopt; 648*3431Scarlsonj dhcpv6_option_t *d6o, *d6so; 649*3431Scarlsonj uint_t olen; 650*3431Scarlsonj 651*3431Scarlsonj /* 652*3431Scarlsonj * Currently, we support just one IAID related to the primary 653*3431Scarlsonj * LIF on the state machine. 654*3431Scarlsonj */ 655*3431Scarlsonj dsmp = lif->lif_lease->dl_smach; 656*3431Scarlsonj iaid = dsmp->dsm_lif->lif_iaid; 657*3431Scarlsonj iaid = htonl(iaid); 658*3431Scarlsonj 659*3431Scarlsonj d6m = (dhcpv6_message_t *)dpkt->pkt; 6600Sstevel@tonic-gate 661*3431Scarlsonj /* 662*3431Scarlsonj * Find or create the IA_NA needed for this LIF. If we 663*3431Scarlsonj * supported IA_TA, we'd check the IFF_TEMPORARY bit here. 664*3431Scarlsonj */ 665*3431Scarlsonj d6o = NULL; 666*3431Scarlsonj while ((d6o = dhcpv6_find_option(d6m + 1, 667*3431Scarlsonj dpkt->pkt_cur_len - sizeof (*d6m), d6o, DHCPV6_OPT_IA_NA, 668*3431Scarlsonj &olen)) != NULL) { 669*3431Scarlsonj if (olen < sizeof (d6in)) 670*3431Scarlsonj continue; 671*3431Scarlsonj (void) memcpy(&d6in, d6o, sizeof (d6in)); 672*3431Scarlsonj if (d6in.d6in_iaid == iaid) 673*3431Scarlsonj break; 674*3431Scarlsonj } 675*3431Scarlsonj if (d6o == NULL) { 676*3431Scarlsonj d6in.d6in_iaid = iaid; 677*3431Scarlsonj d6in.d6in_t1 = 0; 678*3431Scarlsonj d6in.d6in_t2 = 0; 679*3431Scarlsonj d6o = add_pkt_opt(dpkt, DHCPV6_OPT_IA_NA, 680*3431Scarlsonj (dhcpv6_option_t *)&d6in + 1, 681*3431Scarlsonj sizeof (d6in) - sizeof (*d6o)); 682*3431Scarlsonj if (d6o == NULL) 683*3431Scarlsonj return (B_FALSE); 684*3431Scarlsonj } 6850Sstevel@tonic-gate 686*3431Scarlsonj /* 687*3431Scarlsonj * Now add the IAADDR suboption for this LIF. No need to 688*3431Scarlsonj * search here, as we know that this is unique. 689*3431Scarlsonj */ 690*3431Scarlsonj d6ia.d6ia_addr = lif->lif_v6addr; 6910Sstevel@tonic-gate 692*3431Scarlsonj /* 693*3431Scarlsonj * For Release and Decline, we zero out the lifetime. For 694*3431Scarlsonj * Renew and Rebind, we report the original time as the 695*3431Scarlsonj * preferred and valid lifetimes. 696*3431Scarlsonj */ 697*3431Scarlsonj if (d6m->d6m_msg_type == DHCPV6_MSG_RELEASE || 698*3431Scarlsonj d6m->d6m_msg_type == DHCPV6_MSG_DECLINE) { 699*3431Scarlsonj d6ia.d6ia_preflife = 0; 700*3431Scarlsonj d6ia.d6ia_vallife = 0; 701*3431Scarlsonj } else { 702*3431Scarlsonj d6ia.d6ia_preflife = htonl(lif->lif_preferred.dt_start); 703*3431Scarlsonj d6ia.d6ia_vallife = htonl(lif->lif_expire.dt_start); 704*3431Scarlsonj } 705*3431Scarlsonj d6so = add_pkt_subopt(dpkt, d6o, DHCPV6_OPT_IAADDR, 706*3431Scarlsonj (dhcpv6_option_t *)&d6ia + 1, 707*3431Scarlsonj sizeof (d6ia) - sizeof (*d6o)); 708*3431Scarlsonj if (d6so == NULL) 709*3431Scarlsonj return (B_FALSE); 7100Sstevel@tonic-gate 711*3431Scarlsonj /* 712*3431Scarlsonj * Add a status code suboption to the IAADDR to tell the server 713*3431Scarlsonj * why we're declining the address. Note that we must manually 714*3431Scarlsonj * update the enclosing IA_NA, as add_pkt_subopt doesn't know 715*3431Scarlsonj * how to do that. 716*3431Scarlsonj */ 717*3431Scarlsonj if (status != DHCPV6_STAT_SUCCESS || msg != NULL) { 718*3431Scarlsonj olen = sizeof (*statusopt) + 719*3431Scarlsonj (msg == NULL ? 0 : strlen(msg)); 720*3431Scarlsonj statusopt = alloca(olen); 721*3431Scarlsonj *statusopt = htons(status); 722*3431Scarlsonj if (msg != NULL) { 723*3431Scarlsonj (void) memcpy((char *)(statusopt + 1), msg, 724*3431Scarlsonj olen - sizeof (*statusopt)); 725*3431Scarlsonj } 726*3431Scarlsonj d6so = add_pkt_subopt(dpkt, d6so, 727*3431Scarlsonj DHCPV6_OPT_STATUS_CODE, statusopt, olen); 728*3431Scarlsonj if (d6so != NULL) { 729*3431Scarlsonj /* 730*3431Scarlsonj * Update for length of suboption header and 731*3431Scarlsonj * suboption contents. 732*3431Scarlsonj */ 733*3431Scarlsonj (void) update_v6opt_len(d6o, sizeof (*d6so) + 734*3431Scarlsonj olen); 735*3431Scarlsonj } 736*3431Scarlsonj } 737*3431Scarlsonj } else { 738*3431Scarlsonj /* 739*3431Scarlsonj * For DECLINE, we need to add the CD_REQUESTED_IP_ADDR option. 740*3431Scarlsonj * In all other cases (RELEASE and REQUEST), we need to set 741*3431Scarlsonj * ciadr. 742*3431Scarlsonj */ 743*3431Scarlsonj if (pkt_send_type(dpkt) == DECLINE) { 744*3431Scarlsonj if (!add_pkt_opt32(dpkt, CD_REQUESTED_IP_ADDR, 745*3431Scarlsonj lif->lif_addr)) 746*3431Scarlsonj return (B_FALSE); 747*3431Scarlsonj } else { 748*3431Scarlsonj dpkt->pkt->ciaddr.s_addr = lif->lif_addr; 749*3431Scarlsonj } 750*3431Scarlsonj 751*3431Scarlsonj /* 752*3431Scarlsonj * It's not too worrisome if the message fails to fit in the 753*3431Scarlsonj * packet. The result will still be valid. 754*3431Scarlsonj */ 755*3431Scarlsonj if (msg != NULL) 756*3431Scarlsonj (void) add_pkt_opt(dpkt, CD_MESSAGE, msg, 757*3431Scarlsonj strlen(msg) + 1); 7580Sstevel@tonic-gate } 759*3431Scarlsonj return (B_TRUE); 7600Sstevel@tonic-gate } 7610Sstevel@tonic-gate 7620Sstevel@tonic-gate /* 763*3431Scarlsonj * free_pkt_entry(): frees a packet list list entry 7640Sstevel@tonic-gate * 765*3431Scarlsonj * input: PKT_LIST *: the packet list entry to free 766*3431Scarlsonj * output: void 7670Sstevel@tonic-gate */ 768*3431Scarlsonj void 769*3431Scarlsonj free_pkt_entry(PKT_LIST *plp) 7700Sstevel@tonic-gate { 771*3431Scarlsonj if (plp != NULL) { 772*3431Scarlsonj free(plp->pkt); 773*3431Scarlsonj free(plp); 774*3431Scarlsonj } 7750Sstevel@tonic-gate } 7760Sstevel@tonic-gate 7770Sstevel@tonic-gate /* 778*3431Scarlsonj * free_pkt_list(): frees an entire packet list 7790Sstevel@tonic-gate * 7800Sstevel@tonic-gate * input: PKT_LIST **: the packet list to free 7810Sstevel@tonic-gate * output: void 7820Sstevel@tonic-gate */ 7830Sstevel@tonic-gate 7840Sstevel@tonic-gate void 785*3431Scarlsonj free_pkt_list(PKT_LIST **head) 7860Sstevel@tonic-gate { 787*3431Scarlsonj PKT_LIST *plp; 7880Sstevel@tonic-gate 789*3431Scarlsonj while ((plp = *head) != NULL) { 790*3431Scarlsonj remque(plp); 791*3431Scarlsonj free_pkt_entry(plp); 7920Sstevel@tonic-gate } 7930Sstevel@tonic-gate } 7940Sstevel@tonic-gate 7950Sstevel@tonic-gate /* 7960Sstevel@tonic-gate * send_pkt_internal(): sends a packet out on an interface 7970Sstevel@tonic-gate * 798*3431Scarlsonj * input: dhcp_smach_t *: the state machine with a packet to send 799*3431Scarlsonj * output: boolean_t: B_TRUE if the packet is sent, B_FALSE otherwise 8000Sstevel@tonic-gate */ 8010Sstevel@tonic-gate 802*3431Scarlsonj static boolean_t 803*3431Scarlsonj send_pkt_internal(dhcp_smach_t *dsmp) 8040Sstevel@tonic-gate { 8050Sstevel@tonic-gate ssize_t n_bytes; 806*3431Scarlsonj dhcp_lif_t *lif = dsmp->dsm_lif; 807*3431Scarlsonj dhcp_pif_t *pif = lif->lif_pif; 808*3431Scarlsonj dhcp_pkt_t *dpkt = &dsmp->dsm_send_pkt; 809*3431Scarlsonj uchar_t ptype = pkt_send_type(dpkt); 810*3431Scarlsonj const char *pkt_name; 811*3431Scarlsonj struct iovec iov; 812*3431Scarlsonj struct msghdr msg; 813*3431Scarlsonj struct cmsghdr *cmsg; 814*3431Scarlsonj struct in6_pktinfo *ipi6; 815*3431Scarlsonj boolean_t ismcast; 816*3431Scarlsonj 817*3431Scarlsonj /* 818*3431Scarlsonj * Timer should not be running at the point we go to send a packet. 819*3431Scarlsonj */ 820*3431Scarlsonj if (dsmp->dsm_retrans_timer != -1) { 821*3431Scarlsonj dhcpmsg(MSG_CRIT, "send_pkt_internal: unexpected retransmit " 822*3431Scarlsonj "timer on %s", dsmp->dsm_name); 823*3431Scarlsonj stop_pkt_retransmission(dsmp); 824*3431Scarlsonj } 825*3431Scarlsonj 826*3431Scarlsonj pkt_name = pkt_type_to_string(ptype, dpkt->pkt_isv6); 8270Sstevel@tonic-gate 8280Sstevel@tonic-gate /* 8290Sstevel@tonic-gate * if needed, schedule a retransmission timer, then attempt to 8300Sstevel@tonic-gate * send the packet. if we fail, then log the error. our 8310Sstevel@tonic-gate * return value should indicate whether or not we were 8320Sstevel@tonic-gate * successful in sending the request, independent of whether 8330Sstevel@tonic-gate * we could schedule a timer. 8340Sstevel@tonic-gate */ 8350Sstevel@tonic-gate 836*3431Scarlsonj if (dsmp->dsm_send_timeout != 0) { 837*3431Scarlsonj if ((dsmp->dsm_retrans_timer = iu_schedule_timer_ms(tq, 838*3431Scarlsonj dsmp->dsm_send_timeout, retransmit, dsmp)) == -1) 8390Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "send_pkt_internal: cannot " 8400Sstevel@tonic-gate "schedule retransmit timer for %s packet", 8410Sstevel@tonic-gate pkt_name); 8420Sstevel@tonic-gate else 843*3431Scarlsonj hold_smach(dsmp); 844*3431Scarlsonj } 845*3431Scarlsonj 846*3431Scarlsonj if (dpkt->pkt_isv6) { 847*3431Scarlsonj hrtime_t delta; 848*3431Scarlsonj 849*3431Scarlsonj /* 850*3431Scarlsonj * Convert current time into centiseconds since transaction 851*3431Scarlsonj * started. This is what DHCPv6 expects to see in the Elapsed 852*3431Scarlsonj * Time option. 853*3431Scarlsonj */ 854*3431Scarlsonj delta = (gethrtime() - dsmp->dsm_neg_hrtime) / 855*3431Scarlsonj (NANOSEC / 100); 856*3431Scarlsonj if (delta > DHCPV6_FOREVER) 857*3431Scarlsonj delta = DHCPV6_FOREVER; 858*3431Scarlsonj (void) remove_pkt_opt(dpkt, DHCPV6_OPT_ELAPSED_TIME); 859*3431Scarlsonj (void) add_pkt_opt16(dpkt, DHCPV6_OPT_ELAPSED_TIME, 860*3431Scarlsonj htons(delta)); 861*3431Scarlsonj } else { 862*3431Scarlsonj /* 863*3431Scarlsonj * set the `pkt->secs' field depending on the type of packet. 864*3431Scarlsonj * it should be zero, except in the following cases: 865*3431Scarlsonj * 866*3431Scarlsonj * DISCOVER: set to the number of seconds since we started 867*3431Scarlsonj * trying to obtain a lease. 868*3431Scarlsonj * 869*3431Scarlsonj * INFORM: set to the number of seconds since we started 870*3431Scarlsonj * trying to get configuration parameters. 871*3431Scarlsonj * 872*3431Scarlsonj * REQUEST: if in the REQUESTING state, then same value as 873*3431Scarlsonj * DISCOVER, otherwise the number of seconds 874*3431Scarlsonj * since we started trying to obtain a lease. 875*3431Scarlsonj * 876*3431Scarlsonj * we also set `dsm_newstart_monosec', to the time we sent a 877*3431Scarlsonj * REQUEST or DISCOVER packet, so we know the lease start 878*3431Scarlsonj * time (the DISCOVER case is for handling BOOTP servers). 879*3431Scarlsonj */ 880*3431Scarlsonj 881*3431Scarlsonj switch (ptype) { 882*3431Scarlsonj 883*3431Scarlsonj case DISCOVER: 884*3431Scarlsonj dsmp->dsm_newstart_monosec = monosec(); 885*3431Scarlsonj dsmp->dsm_disc_secs = dsmp->dsm_newstart_monosec - 886*3431Scarlsonj hrtime_to_monosec(dsmp->dsm_neg_hrtime); 887*3431Scarlsonj dpkt->pkt->secs = htons(dsmp->dsm_disc_secs); 888*3431Scarlsonj break; 889*3431Scarlsonj 890*3431Scarlsonj case INFORM: 891*3431Scarlsonj dpkt->pkt->secs = htons(monosec() - 892*3431Scarlsonj hrtime_to_monosec(dsmp->dsm_neg_hrtime)); 893*3431Scarlsonj break; 894*3431Scarlsonj 895*3431Scarlsonj case REQUEST: 896*3431Scarlsonj dsmp->dsm_newstart_monosec = monosec(); 897*3431Scarlsonj 898*3431Scarlsonj if (dsmp->dsm_state == REQUESTING) { 899*3431Scarlsonj dpkt->pkt->secs = htons(dsmp->dsm_disc_secs); 900*3431Scarlsonj break; 901*3431Scarlsonj } 902*3431Scarlsonj 903*3431Scarlsonj dpkt->pkt->secs = htons(monosec() - 904*3431Scarlsonj hrtime_to_monosec(dsmp->dsm_neg_hrtime)); 905*3431Scarlsonj break; 906*3431Scarlsonj 907*3431Scarlsonj default: 908*3431Scarlsonj dpkt->pkt->secs = htons(0); 909*3431Scarlsonj break; 910*3431Scarlsonj } 9110Sstevel@tonic-gate } 9120Sstevel@tonic-gate 913*3431Scarlsonj if (dpkt->pkt_isv6) { 914*3431Scarlsonj struct sockaddr_in6 sin6; 915*3431Scarlsonj 916*3431Scarlsonj (void) memset(&iov, 0, sizeof (iov)); 917*3431Scarlsonj iov.iov_base = dpkt->pkt; 918*3431Scarlsonj iov.iov_len = dpkt->pkt_cur_len; 919*3431Scarlsonj 920*3431Scarlsonj (void) memset(&msg, 0, sizeof (msg)); 921*3431Scarlsonj msg.msg_name = &dsmp->dsm_send_dest.v6; 922*3431Scarlsonj msg.msg_namelen = sizeof (struct sockaddr_in6); 923*3431Scarlsonj msg.msg_iov = &iov; 924*3431Scarlsonj msg.msg_iovlen = 1; 925*3431Scarlsonj 926*3431Scarlsonj /* 927*3431Scarlsonj * If the address that's requested cannot be reached, then fall 928*3431Scarlsonj * back to the multcast address. 929*3431Scarlsonj */ 930*3431Scarlsonj if (IN6_IS_ADDR_MULTICAST(&dsmp->dsm_send_dest.v6.sin6_addr)) { 931*3431Scarlsonj ismcast = B_TRUE; 932*3431Scarlsonj } else { 933*3431Scarlsonj struct dstinforeq dinfo; 934*3431Scarlsonj struct strioctl str; 9350Sstevel@tonic-gate 936*3431Scarlsonj ismcast = B_FALSE; 937*3431Scarlsonj (void) memset(&dinfo, 0, sizeof (dinfo)); 938*3431Scarlsonj dinfo.dir_daddr = dsmp->dsm_send_dest.v6.sin6_addr; 939*3431Scarlsonj str.ic_cmd = SIOCGDSTINFO; 940*3431Scarlsonj str.ic_timout = 0; 941*3431Scarlsonj str.ic_len = sizeof (dinfo); 942*3431Scarlsonj str.ic_dp = (char *)&dinfo; 943*3431Scarlsonj if (ioctl(v6_sock_fd, I_STR, &str) == -1) { 944*3431Scarlsonj dhcpmsg(MSG_ERR, 945*3431Scarlsonj "send_pkt_internal: ioctl SIOCGDSTINFO"); 946*3431Scarlsonj } else if (!dinfo.dir_dreachable) { 947*3431Scarlsonj char abuf[INET6_ADDRSTRLEN]; 9480Sstevel@tonic-gate 949*3431Scarlsonj dhcpmsg(MSG_DEBUG, "send_pkt_internal: %s is " 950*3431Scarlsonj "not reachable; using multicast instead", 951*3431Scarlsonj inet_ntop(AF_INET6, &dinfo.dir_daddr, abuf, 952*3431Scarlsonj sizeof (abuf))); 953*3431Scarlsonj sin6 = dsmp->dsm_send_dest.v6; 954*3431Scarlsonj sin6.sin6_addr = 955*3431Scarlsonj ipv6_all_dhcp_relay_and_servers; 956*3431Scarlsonj msg.msg_name = &sin6; 957*3431Scarlsonj ismcast = B_TRUE; 958*3431Scarlsonj } 9590Sstevel@tonic-gate } 9600Sstevel@tonic-gate 961*3431Scarlsonj /* 962*3431Scarlsonj * Make room for our ancillary data option as well as a dummy 963*3431Scarlsonj * option used by CMSG_NXTHDR. 964*3431Scarlsonj */ 965*3431Scarlsonj msg.msg_controllen = sizeof (*cmsg) + _MAX_ALIGNMENT + 966*3431Scarlsonj sizeof (*ipi6) + _MAX_ALIGNMENT + sizeof (*cmsg); 967*3431Scarlsonj msg.msg_control = alloca(msg.msg_controllen); 968*3431Scarlsonj cmsg = CMSG_FIRSTHDR(&msg); 969*3431Scarlsonj cmsg->cmsg_level = IPPROTO_IPV6; 970*3431Scarlsonj cmsg->cmsg_type = IPV6_PKTINFO; 971*3431Scarlsonj /* LINTED: alignment */ 972*3431Scarlsonj ipi6 = (struct in6_pktinfo *)CMSG_DATA(cmsg); 973*3431Scarlsonj if (ismcast) 974*3431Scarlsonj ipi6->ipi6_addr = lif->lif_v6addr; 975*3431Scarlsonj else 976*3431Scarlsonj ipi6->ipi6_addr = my_in6addr_any; 977*3431Scarlsonj ipi6->ipi6_ifindex = lif->lif_pif->pif_index; 978*3431Scarlsonj cmsg->cmsg_len = (char *)(ipi6 + 1) - (char *)cmsg; 9790Sstevel@tonic-gate 980*3431Scarlsonj /* 981*3431Scarlsonj * Now correct the control message length. 982*3431Scarlsonj */ 983*3431Scarlsonj cmsg = CMSG_NXTHDR(&msg, cmsg); 984*3431Scarlsonj msg.msg_controllen = (char *)cmsg - (char *)msg.msg_control; 9850Sstevel@tonic-gate 986*3431Scarlsonj n_bytes = sendmsg(v6_sock_fd, &msg, 0); 987*3431Scarlsonj } else { 988*3431Scarlsonj if (dsmp->dsm_using_dlpi) { 989*3431Scarlsonj n_bytes = dlpi_sendto(pif->pif_dlpi_fd, dpkt->pkt, 990*3431Scarlsonj dpkt->pkt_cur_len, &dsmp->dsm_send_dest.v4, 991*3431Scarlsonj pif->pif_daddr, pif->pif_dlen); 992*3431Scarlsonj /* dlpi_sendto calls putmsg */ 993*3431Scarlsonj if (n_bytes == 0) 994*3431Scarlsonj n_bytes = dpkt->pkt_cur_len; 995*3431Scarlsonj } else { 996*3431Scarlsonj n_bytes = sendto(lif->lif_sock_ip_fd, dpkt->pkt, 997*3431Scarlsonj dpkt->pkt_cur_len, 0, 998*3431Scarlsonj (struct sockaddr *)&dsmp->dsm_send_dest.v4, 999*3431Scarlsonj sizeof (struct sockaddr_in)); 1000*3431Scarlsonj } 10010Sstevel@tonic-gate } 10020Sstevel@tonic-gate 10030Sstevel@tonic-gate if (n_bytes != dpkt->pkt_cur_len) { 1004*3431Scarlsonj if (dsmp->dsm_retrans_timer == -1) 10050Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "send_pkt_internal: cannot send " 10060Sstevel@tonic-gate "%s packet to server", pkt_name); 10070Sstevel@tonic-gate else 10080Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "send_pkt_internal: cannot send " 10090Sstevel@tonic-gate "%s packet to server (will retry in %u seconds)", 1010*3431Scarlsonj pkt_name, dsmp->dsm_send_timeout / MILLISEC); 1011*3431Scarlsonj return (B_FALSE); 10120Sstevel@tonic-gate } 10130Sstevel@tonic-gate 1014*3431Scarlsonj dhcpmsg(MSG_VERBOSE, "sent %s xid %x packet out %s", pkt_name, 1015*3431Scarlsonj pkt_get_xid(dpkt->pkt, dpkt->pkt_isv6), dsmp->dsm_name); 10160Sstevel@tonic-gate 1017*3431Scarlsonj dsmp->dsm_packet_sent++; 1018*3431Scarlsonj dsmp->dsm_sent++; 1019*3431Scarlsonj return (B_TRUE); 10200Sstevel@tonic-gate } 10210Sstevel@tonic-gate 10220Sstevel@tonic-gate /* 1023*3431Scarlsonj * send_pkt(): sends a packet out 10240Sstevel@tonic-gate * 1025*3431Scarlsonj * input: dhcp_smach_t *: the state machine sending the packet 10260Sstevel@tonic-gate * dhcp_pkt_t *: the packet to send out 10270Sstevel@tonic-gate * in_addr_t: the destination IP address for the packet 10280Sstevel@tonic-gate * stop_func_t *: a pointer to function to indicate when to stop 10290Sstevel@tonic-gate * retransmitting the packet (if NULL, packet is 10300Sstevel@tonic-gate * not retransmitted) 1031*3431Scarlsonj * output: boolean_t: B_TRUE if the packet was sent, B_FALSE otherwise 10320Sstevel@tonic-gate */ 10330Sstevel@tonic-gate 1034*3431Scarlsonj boolean_t 1035*3431Scarlsonj send_pkt(dhcp_smach_t *dsmp, dhcp_pkt_t *dpkt, in_addr_t dest, 10360Sstevel@tonic-gate stop_func_t *stop) 10370Sstevel@tonic-gate { 10380Sstevel@tonic-gate /* 10390Sstevel@tonic-gate * packets must be at least sizeof (PKT) or they may be dropped 10400Sstevel@tonic-gate * by routers. pad out the packet in this case. 10410Sstevel@tonic-gate */ 10420Sstevel@tonic-gate 10430Sstevel@tonic-gate dpkt->pkt_cur_len = MAX(dpkt->pkt_cur_len, sizeof (PKT)); 10440Sstevel@tonic-gate 1045*3431Scarlsonj dsmp->dsm_packet_sent = 0; 10460Sstevel@tonic-gate 1047*3431Scarlsonj (void) memset(&dsmp->dsm_send_dest.v4, 0, 1048*3431Scarlsonj sizeof (dsmp->dsm_send_dest.v4)); 1049*3431Scarlsonj dsmp->dsm_send_dest.v4.sin_addr.s_addr = dest; 1050*3431Scarlsonj dsmp->dsm_send_dest.v4.sin_family = AF_INET; 1051*3431Scarlsonj dsmp->dsm_send_dest.v4.sin_port = htons(IPPORT_BOOTPS); 1052*3431Scarlsonj dsmp->dsm_send_stop_func = stop; 10530Sstevel@tonic-gate 10540Sstevel@tonic-gate /* 10550Sstevel@tonic-gate * TODO: dispose of this gruesome assumption (there's no real 10560Sstevel@tonic-gate * technical gain from doing so, but it would be cleaner) 10570Sstevel@tonic-gate */ 10580Sstevel@tonic-gate 1059*3431Scarlsonj assert(dpkt == &dsmp->dsm_send_pkt); 10600Sstevel@tonic-gate 10610Sstevel@tonic-gate /* 10620Sstevel@tonic-gate * clear out any packets which had been previously received 10630Sstevel@tonic-gate * but not pulled off of the recv_packet queue. 10640Sstevel@tonic-gate */ 10650Sstevel@tonic-gate 1066*3431Scarlsonj free_pkt_list(&dsmp->dsm_recv_pkt_list); 1067*3431Scarlsonj 1068*3431Scarlsonj if (stop == NULL) 1069*3431Scarlsonj dsmp->dsm_send_timeout = 0; /* prevents retransmissions */ 1070*3431Scarlsonj else 1071*3431Scarlsonj next_retransmission(dsmp, B_TRUE, B_FALSE); 1072*3431Scarlsonj 1073*3431Scarlsonj return (send_pkt_internal(dsmp)); 1074*3431Scarlsonj } 1075*3431Scarlsonj 1076*3431Scarlsonj /* 1077*3431Scarlsonj * send_pkt_v6(): sends a DHCPv6 packet out 1078*3431Scarlsonj * 1079*3431Scarlsonj * input: dhcp_smach_t *: the state machine sending the packet 1080*3431Scarlsonj * dhcp_pkt_t *: the packet to send out 1081*3431Scarlsonj * in6_addr_t: the destination IPv6 address for the packet 1082*3431Scarlsonj * stop_func_t *: a pointer to function to indicate when to stop 1083*3431Scarlsonj * retransmitting the packet (if NULL, packet is 1084*3431Scarlsonj * not retransmitted) 1085*3431Scarlsonj * uint_t: Initial Retransmit Timer value 1086*3431Scarlsonj * uint_t: Maximum Retransmit Timer value, zero if none 1087*3431Scarlsonj * output: boolean_t: B_TRUE if the packet was sent, B_FALSE otherwise 1088*3431Scarlsonj */ 1089*3431Scarlsonj 1090*3431Scarlsonj boolean_t 1091*3431Scarlsonj send_pkt_v6(dhcp_smach_t *dsmp, dhcp_pkt_t *dpkt, in6_addr_t dest, 1092*3431Scarlsonj stop_func_t *stop, uint_t irt, uint_t mrt) 1093*3431Scarlsonj { 1094*3431Scarlsonj dsmp->dsm_packet_sent = 0; 1095*3431Scarlsonj 1096*3431Scarlsonj (void) memset(&dsmp->dsm_send_dest.v6, 0, 1097*3431Scarlsonj sizeof (dsmp->dsm_send_dest.v6)); 1098*3431Scarlsonj dsmp->dsm_send_dest.v6.sin6_addr = dest; 1099*3431Scarlsonj dsmp->dsm_send_dest.v6.sin6_family = AF_INET6; 1100*3431Scarlsonj dsmp->dsm_send_dest.v6.sin6_port = htons(IPPORT_DHCPV6S); 1101*3431Scarlsonj dsmp->dsm_send_stop_func = stop; 1102*3431Scarlsonj 1103*3431Scarlsonj /* 1104*3431Scarlsonj * TODO: dispose of this gruesome assumption (there's no real 1105*3431Scarlsonj * technical gain from doing so, but it would be cleaner) 1106*3431Scarlsonj */ 1107*3431Scarlsonj 1108*3431Scarlsonj assert(dpkt == &dsmp->dsm_send_pkt); 1109*3431Scarlsonj 1110*3431Scarlsonj /* 1111*3431Scarlsonj * clear out any packets which had been previously received 1112*3431Scarlsonj * but not pulled off of the recv_packet queue. 1113*3431Scarlsonj */ 1114*3431Scarlsonj 1115*3431Scarlsonj free_pkt_list(&dsmp->dsm_recv_pkt_list); 11160Sstevel@tonic-gate 11170Sstevel@tonic-gate if (stop == NULL) { 1118*3431Scarlsonj dsmp->dsm_send_timeout = 0; /* prevents retransmissions */ 1119*3431Scarlsonj } else { 1120*3431Scarlsonj dsmp->dsm_send_timeout = irt; 1121*3431Scarlsonj dsmp->dsm_send_tcenter = mrt; 1122*3431Scarlsonj /* 1123*3431Scarlsonj * This is quite ugly, but RFC 3315 section 17.1.2 requires 1124*3431Scarlsonj * that the RAND value for the very first retransmission of a 1125*3431Scarlsonj * Solicit message is strictly greater than zero. 1126*3431Scarlsonj */ 1127*3431Scarlsonj next_retransmission(dsmp, B_TRUE, 1128*3431Scarlsonj pkt_send_type(dpkt) == DHCPV6_MSG_SOLICIT); 1129*3431Scarlsonj } 11300Sstevel@tonic-gate 1131*3431Scarlsonj return (send_pkt_internal(dsmp)); 11320Sstevel@tonic-gate } 11330Sstevel@tonic-gate 11340Sstevel@tonic-gate /* 11350Sstevel@tonic-gate * retransmit(): retransmits the current packet on an interface 11360Sstevel@tonic-gate * 11370Sstevel@tonic-gate * input: iu_tq_t *: unused 1138*3431Scarlsonj * void *: the dhcp_smach_t * (state machine) sending a packet 11390Sstevel@tonic-gate * output: void 11400Sstevel@tonic-gate */ 11410Sstevel@tonic-gate 11420Sstevel@tonic-gate /* ARGSUSED */ 11430Sstevel@tonic-gate static void 11440Sstevel@tonic-gate retransmit(iu_tq_t *tqp, void *arg) 11450Sstevel@tonic-gate { 1146*3431Scarlsonj dhcp_smach_t *dsmp = arg; 11470Sstevel@tonic-gate 1148*3431Scarlsonj dsmp->dsm_retrans_timer = -1; 1149*3431Scarlsonj 1150*3431Scarlsonj if (!verify_smach(dsmp)) 11510Sstevel@tonic-gate return; 11520Sstevel@tonic-gate 11530Sstevel@tonic-gate /* 1154*3431Scarlsonj * Check the callback to see if we should keep sending retransmissions. 1155*3431Scarlsonj * Compute the next retransmission time first, so that the callback can 1156*3431Scarlsonj * cap the value if need be. (Required for DHCPv6 Confirm messages.) 1157*3431Scarlsonj * 1158*3431Scarlsonj * Hold the state machine across the callback so that the called 1159*3431Scarlsonj * function can remove the state machine from the system without 1160*3431Scarlsonj * disturbing the string used subsequently for verbose logging. The 1161*3431Scarlsonj * Release function destroys the state machine when the retry count 1162*3431Scarlsonj * expires. 11630Sstevel@tonic-gate */ 11640Sstevel@tonic-gate 1165*3431Scarlsonj next_retransmission(dsmp, B_FALSE, B_FALSE); 1166*3431Scarlsonj hold_smach(dsmp); 1167*3431Scarlsonj if (dsmp->dsm_send_stop_func(dsmp, dsmp->dsm_packet_sent)) { 1168*3431Scarlsonj dhcpmsg(MSG_VERBOSE, "retransmit: time to stop on %s", 1169*3431Scarlsonj dsmp->dsm_name); 1170*3431Scarlsonj } else { 1171*3431Scarlsonj dhcpmsg(MSG_VERBOSE, "retransmit: sending another on %s", 1172*3431Scarlsonj dsmp->dsm_name); 1173*3431Scarlsonj (void) send_pkt_internal(dsmp); 1174*3431Scarlsonj } 1175*3431Scarlsonj release_smach(dsmp); 11760Sstevel@tonic-gate } 11770Sstevel@tonic-gate 11780Sstevel@tonic-gate /* 11790Sstevel@tonic-gate * stop_pkt_retransmission(): stops retransmission of last sent packet 11800Sstevel@tonic-gate * 1181*3431Scarlsonj * input: dhcp_smach_t *: the state machine to stop retransmission on 11820Sstevel@tonic-gate * output: void 11830Sstevel@tonic-gate */ 11840Sstevel@tonic-gate 11850Sstevel@tonic-gate void 1186*3431Scarlsonj stop_pkt_retransmission(dhcp_smach_t *dsmp) 11870Sstevel@tonic-gate { 1188*3431Scarlsonj if (dsmp->dsm_retrans_timer != -1 && 1189*3431Scarlsonj iu_cancel_timer(tq, dsmp->dsm_retrans_timer, NULL) == 1) { 1190*3431Scarlsonj dhcpmsg(MSG_VERBOSE, "stop_pkt_retransmission: stopped on %s", 1191*3431Scarlsonj dsmp->dsm_name); 1192*3431Scarlsonj dsmp->dsm_retrans_timer = -1; 1193*3431Scarlsonj release_smach(dsmp); 11940Sstevel@tonic-gate } 11950Sstevel@tonic-gate } 11960Sstevel@tonic-gate 11970Sstevel@tonic-gate /* 1198*3431Scarlsonj * retransmit_now(): force a packet retransmission right now. Used only with 1199*3431Scarlsonj * the DHCPv6 UseMulticast status code. Use with caution; 1200*3431Scarlsonj * triggered retransmissions can cause packet storms. 12010Sstevel@tonic-gate * 1202*3431Scarlsonj * input: dhcp_smach_t *: the state machine to force retransmission on 1203*3431Scarlsonj * output: void 12040Sstevel@tonic-gate */ 12050Sstevel@tonic-gate 1206*3431Scarlsonj void 1207*3431Scarlsonj retransmit_now(dhcp_smach_t *dsmp) 1208*3431Scarlsonj { 1209*3431Scarlsonj stop_pkt_retransmission(dsmp); 1210*3431Scarlsonj (void) send_pkt_internal(dsmp); 1211*3431Scarlsonj } 1212*3431Scarlsonj 1213*3431Scarlsonj /* 1214*3431Scarlsonj * alloc_pkt_entry(): Allocates a packet list entry with a given data area 1215*3431Scarlsonj * size. 1216*3431Scarlsonj * 1217*3431Scarlsonj * input: size_t: size of data area for packet 1218*3431Scarlsonj * boolean_t: B_TRUE for IPv6 1219*3431Scarlsonj * output: PKT_LIST *: allocated packet list entry 1220*3431Scarlsonj */ 1221*3431Scarlsonj 1222*3431Scarlsonj PKT_LIST * 1223*3431Scarlsonj alloc_pkt_entry(size_t psize, boolean_t isv6) 12240Sstevel@tonic-gate { 12250Sstevel@tonic-gate PKT_LIST *plp; 12260Sstevel@tonic-gate 1227*3431Scarlsonj if ((plp = calloc(1, sizeof (*plp))) == NULL || 1228*3431Scarlsonj (plp->pkt = malloc(psize)) == NULL) { 1229*3431Scarlsonj free(plp); 1230*3431Scarlsonj plp = NULL; 1231*3431Scarlsonj } else { 1232*3431Scarlsonj plp->len = psize; 1233*3431Scarlsonj plp->isv6 = isv6; 12340Sstevel@tonic-gate } 12350Sstevel@tonic-gate 1236*3431Scarlsonj return (plp); 1237*3431Scarlsonj } 12380Sstevel@tonic-gate 1239*3431Scarlsonj /* 1240*3431Scarlsonj * sock_recvpkt(): read from the given socket into an allocated buffer and 1241*3431Scarlsonj * handles any ancillary data options. 1242*3431Scarlsonj * 1243*3431Scarlsonj * input: int: file descriptor to read 1244*3431Scarlsonj * PKT_LIST *: allocated buffer 1245*3431Scarlsonj * output: ssize_t: number of bytes read, or -1 on error 1246*3431Scarlsonj */ 1247*3431Scarlsonj 1248*3431Scarlsonj static ssize_t 1249*3431Scarlsonj sock_recvpkt(int fd, PKT_LIST *plp) 1250*3431Scarlsonj { 1251*3431Scarlsonj struct iovec iov; 1252*3431Scarlsonj struct msghdr msg; 1253*3431Scarlsonj long ctrl[8192 / sizeof (long)]; 1254*3431Scarlsonj ssize_t msglen; 12550Sstevel@tonic-gate 1256*3431Scarlsonj (void) memset(&iov, 0, sizeof (iov)); 1257*3431Scarlsonj iov.iov_base = (caddr_t)plp->pkt; 1258*3431Scarlsonj iov.iov_len = plp->len; 1259*3431Scarlsonj 1260*3431Scarlsonj (void) memset(&msg, 0, sizeof (msg)); 1261*3431Scarlsonj msg.msg_name = &plp->pktfrom; 1262*3431Scarlsonj msg.msg_namelen = sizeof (plp->pktfrom); 1263*3431Scarlsonj msg.msg_iov = &iov; 1264*3431Scarlsonj msg.msg_iovlen = 1; 1265*3431Scarlsonj msg.msg_control = ctrl; 1266*3431Scarlsonj msg.msg_controllen = sizeof (ctrl); 1267*3431Scarlsonj 1268*3431Scarlsonj if ((msglen = recvmsg(fd, &msg, 0)) != -1) { 1269*3431Scarlsonj struct cmsghdr *cmsg; 1270*3431Scarlsonj 1271*3431Scarlsonj for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; 1272*3431Scarlsonj cmsg = CMSG_NXTHDR(&msg, cmsg)) { 1273*3431Scarlsonj struct sockaddr_in *sinp; 1274*3431Scarlsonj struct sockaddr_in6 *sin6; 1275*3431Scarlsonj struct in6_pktinfo *ipi6; 12760Sstevel@tonic-gate 1277*3431Scarlsonj switch (cmsg->cmsg_level) { 1278*3431Scarlsonj case IPPROTO_IP: 1279*3431Scarlsonj switch (cmsg->cmsg_type) { 1280*3431Scarlsonj case IP_RECVDSTADDR: 1281*3431Scarlsonj sinp = (struct sockaddr_in *) 1282*3431Scarlsonj &plp->pktto; 1283*3431Scarlsonj sinp->sin_family = AF_INET; 1284*3431Scarlsonj (void) memcpy(&sinp->sin_addr.s_addr, 1285*3431Scarlsonj CMSG_DATA(cmsg), 1286*3431Scarlsonj sizeof (ipaddr_t)); 1287*3431Scarlsonj break; 1288*3431Scarlsonj 1289*3431Scarlsonj case IP_RECVIF: 1290*3431Scarlsonj (void) memcpy(&plp->ifindex, 1291*3431Scarlsonj CMSG_DATA(cmsg), sizeof (uint_t)); 1292*3431Scarlsonj break; 1293*3431Scarlsonj } 1294*3431Scarlsonj break; 12950Sstevel@tonic-gate 1296*3431Scarlsonj case IPPROTO_IPV6: 1297*3431Scarlsonj switch (cmsg->cmsg_type) { 1298*3431Scarlsonj case IPV6_PKTINFO: 1299*3431Scarlsonj /* LINTED: alignment */ 1300*3431Scarlsonj ipi6 = (struct in6_pktinfo *) 1301*3431Scarlsonj CMSG_DATA(cmsg); 1302*3431Scarlsonj sin6 = (struct sockaddr_in6 *) 1303*3431Scarlsonj &plp->pktto; 1304*3431Scarlsonj sin6->sin6_family = AF_INET6; 1305*3431Scarlsonj (void) memcpy(&sin6->sin6_addr, 1306*3431Scarlsonj &ipi6->ipi6_addr, 1307*3431Scarlsonj sizeof (ipi6->ipi6_addr)); 1308*3431Scarlsonj (void) memcpy(&plp->ifindex, 1309*3431Scarlsonj &ipi6->ipi6_ifindex, 1310*3431Scarlsonj sizeof (uint_t)); 1311*3431Scarlsonj break; 1312*3431Scarlsonj } 1313*3431Scarlsonj } 1314*3431Scarlsonj } 1315*3431Scarlsonj } 1316*3431Scarlsonj return (msglen); 1317*3431Scarlsonj } 13180Sstevel@tonic-gate 1319*3431Scarlsonj /* 1320*3431Scarlsonj * recv_pkt(): receives a single DHCP packet on a given file descriptor. 1321*3431Scarlsonj * 1322*3431Scarlsonj * input: int: the file descriptor to receive the packet 1323*3431Scarlsonj * int: the maximum packet size to allow 1324*3431Scarlsonj * boolean_t: B_TRUE for IPv6 1325*3431Scarlsonj * boolean_t: B_TRUE if using DLPI 1326*3431Scarlsonj * output: PKT_LIST *: the received packet 1327*3431Scarlsonj */ 13280Sstevel@tonic-gate 1329*3431Scarlsonj PKT_LIST * 1330*3431Scarlsonj recv_pkt(int fd, int mtu, boolean_t isv6, boolean_t isdlpi) 1331*3431Scarlsonj { 1332*3431Scarlsonj PKT_LIST *plp; 1333*3431Scarlsonj ssize_t retval; 1334*3431Scarlsonj 1335*3431Scarlsonj if ((plp = alloc_pkt_entry(mtu, isv6)) == NULL) { 1336*3431Scarlsonj dhcpmsg(MSG_ERROR, 1337*3431Scarlsonj "recv_pkt: allocation failure; dropped packet"); 1338*3431Scarlsonj return (NULL); 13390Sstevel@tonic-gate } 13400Sstevel@tonic-gate 1341*3431Scarlsonj if (isv6) { 1342*3431Scarlsonj retval = sock_recvpkt(fd, plp); 1343*3431Scarlsonj 1344*3431Scarlsonj if (retval == -1) { 1345*3431Scarlsonj dhcpmsg(MSG_ERR, 1346*3431Scarlsonj "recv_pkt: recvfrom v6 failed, dropped"); 1347*3431Scarlsonj goto failure; 1348*3431Scarlsonj } 1349*3431Scarlsonj 1350*3431Scarlsonj plp->len = retval; 13510Sstevel@tonic-gate 1352*3431Scarlsonj if (retval < sizeof (dhcpv6_message_t)) { 1353*3431Scarlsonj dhcpmsg(MSG_WARNING, "recv_pkt: runt message"); 1354*3431Scarlsonj goto failure; 1355*3431Scarlsonj } 1356*3431Scarlsonj } else { 1357*3431Scarlsonj if (isdlpi) { 1358*3431Scarlsonj retval = dlpi_recvfrom(fd, plp->pkt, mtu, 1359*3431Scarlsonj (struct sockaddr_in *)&plp->pktfrom, 1360*3431Scarlsonj (struct sockaddr_in *)&plp->pktto); 1361*3431Scarlsonj } else { 1362*3431Scarlsonj retval = sock_recvpkt(fd, plp); 1363*3431Scarlsonj } 13640Sstevel@tonic-gate 1365*3431Scarlsonj if (retval == -1) { 1366*3431Scarlsonj dhcpmsg(MSG_ERR, 1367*3431Scarlsonj "recv_pkt: %srecvfrom v4 failed, dropped", 1368*3431Scarlsonj isdlpi ? "dlpi_" : ""); 1369*3431Scarlsonj goto failure; 1370*3431Scarlsonj } 1371*3431Scarlsonj 1372*3431Scarlsonj plp->len = retval; 1373*3431Scarlsonj 1374*3431Scarlsonj switch (dhcp_options_scan(plp, B_TRUE)) { 1375*3431Scarlsonj 1376*3431Scarlsonj case DHCP_WRONG_MSG_TYPE: 1377*3431Scarlsonj dhcpmsg(MSG_WARNING, 1378*3431Scarlsonj "recv_pkt: unexpected DHCP message"); 1379*3431Scarlsonj goto failure; 13800Sstevel@tonic-gate 1381*3431Scarlsonj case DHCP_GARBLED_MSG_TYPE: 1382*3431Scarlsonj dhcpmsg(MSG_WARNING, 1383*3431Scarlsonj "recv_pkt: garbled DHCP message type"); 1384*3431Scarlsonj goto failure; 13850Sstevel@tonic-gate 1386*3431Scarlsonj case DHCP_BAD_OPT_OVLD: 1387*3431Scarlsonj dhcpmsg(MSG_WARNING, "recv_pkt: bad option overload"); 1388*3431Scarlsonj goto failure; 1389*3431Scarlsonj 1390*3431Scarlsonj case 0: 1391*3431Scarlsonj break; 1392*3431Scarlsonj 1393*3431Scarlsonj default: 1394*3431Scarlsonj dhcpmsg(MSG_WARNING, 1395*3431Scarlsonj "recv_pkt: packet corrupted, dropped"); 13960Sstevel@tonic-gate goto failure; 13970Sstevel@tonic-gate } 13980Sstevel@tonic-gate } 1399*3431Scarlsonj return (plp); 14000Sstevel@tonic-gate 14010Sstevel@tonic-gate failure: 1402*3431Scarlsonj free_pkt_entry(plp); 1403*3431Scarlsonj return (NULL); 1404*3431Scarlsonj } 1405*3431Scarlsonj 1406*3431Scarlsonj /* 1407*3431Scarlsonj * pkt_v4_match(): check if a given DHCPv4 message type is in a given set 1408*3431Scarlsonj * 1409*3431Scarlsonj * input: uchar_t: packet type 1410*3431Scarlsonj * dhcp_message_type_t: bit-wise OR of DHCP_P* values. 1411*3431Scarlsonj * output: boolean_t: B_TRUE if packet type is in the set 1412*3431Scarlsonj */ 1413*3431Scarlsonj 1414*3431Scarlsonj boolean_t 1415*3431Scarlsonj pkt_v4_match(uchar_t type, dhcp_message_type_t match_type) 1416*3431Scarlsonj { 1417*3431Scarlsonj /* 1418*3431Scarlsonj * note: the ordering here allows direct indexing of the table 1419*3431Scarlsonj * based on the RFC2131 packet type value passed in. 1420*3431Scarlsonj */ 1421*3431Scarlsonj 1422*3431Scarlsonj static dhcp_message_type_t type_map[] = { 1423*3431Scarlsonj DHCP_PUNTYPED, DHCP_PDISCOVER, DHCP_POFFER, DHCP_PREQUEST, 1424*3431Scarlsonj DHCP_PDECLINE, DHCP_PACK, DHCP_PNAK, DHCP_PRELEASE, 1425*3431Scarlsonj DHCP_PINFORM 1426*3431Scarlsonj }; 1427*3431Scarlsonj 1428*3431Scarlsonj if (type < (sizeof (type_map) / sizeof (*type_map))) 1429*3431Scarlsonj return ((type_map[type] & match_type) ? B_TRUE : B_FALSE); 1430*3431Scarlsonj else 1431*3431Scarlsonj return (B_FALSE); 1432*3431Scarlsonj } 1433*3431Scarlsonj 1434*3431Scarlsonj /* 1435*3431Scarlsonj * pkt_smach_enqueue(): enqueue a packet on a given state machine 1436*3431Scarlsonj * 1437*3431Scarlsonj * input: dhcp_smach_t: state machine 1438*3431Scarlsonj * PKT_LIST *: packet to enqueue 1439*3431Scarlsonj * output: none 1440*3431Scarlsonj */ 1441*3431Scarlsonj 1442*3431Scarlsonj void 1443*3431Scarlsonj pkt_smach_enqueue(dhcp_smach_t *dsmp, PKT_LIST *plp) 1444*3431Scarlsonj { 1445*3431Scarlsonj dhcpmsg(MSG_VERBOSE, "pkt_smach_enqueue: received %s %s packet on %s", 1446*3431Scarlsonj pkt_type_to_string(pkt_recv_type(plp), dsmp->dsm_isv6), 1447*3431Scarlsonj dsmp->dsm_isv6 ? "v6" : "v4", dsmp->dsm_name); 1448*3431Scarlsonj 1449*3431Scarlsonj /* add to front of list */ 1450*3431Scarlsonj insque(plp, &dsmp->dsm_recv_pkt_list); 1451*3431Scarlsonj dsmp->dsm_received++; 14520Sstevel@tonic-gate } 14530Sstevel@tonic-gate 14540Sstevel@tonic-gate /* 1455*3431Scarlsonj * next_retransmission(): computes the number of seconds until the next 1456*3431Scarlsonj * retransmission, based on the algorithms in RFCs 2131 1457*3431Scarlsonj * 3315. 14580Sstevel@tonic-gate * 1459*3431Scarlsonj * input: dhcp_smach_t *: state machine that needs a new timer 1460*3431Scarlsonj * boolean_t: B_TRUE if this is the first time sending the message 1461*3431Scarlsonj * boolean_t: B_TRUE for positive RAND values only (RFC 3315 17.1.2) 1462*3431Scarlsonj * output: none 14630Sstevel@tonic-gate */ 14640Sstevel@tonic-gate 1465*3431Scarlsonj static void 1466*3431Scarlsonj next_retransmission(dhcp_smach_t *dsmp, boolean_t first_send, 1467*3431Scarlsonj boolean_t positive_only) 14680Sstevel@tonic-gate { 1469*3431Scarlsonj uint32_t timeout_ms; 1470*3431Scarlsonj 1471*3431Scarlsonj if (dsmp->dsm_isv6) { 1472*3431Scarlsonj double randval; 1473*3431Scarlsonj 1474*3431Scarlsonj /* 1475*3431Scarlsonj * The RFC specifies 0 to 10% jitter for the initial 1476*3431Scarlsonj * solicitation, and plus or minus 10% jitter for all others. 1477*3431Scarlsonj * This works out to 100 milliseconds on the shortest timer we 1478*3431Scarlsonj * use. 1479*3431Scarlsonj */ 1480*3431Scarlsonj if (positive_only) 1481*3431Scarlsonj randval = drand48() / 10.0; 1482*3431Scarlsonj else 1483*3431Scarlsonj randval = (drand48() - 0.5) / 5.0; 1484*3431Scarlsonj 1485*3431Scarlsonj /* The RFC specifies doubling *after* the first transmission */ 1486*3431Scarlsonj timeout_ms = dsmp->dsm_send_timeout; 1487*3431Scarlsonj if (!first_send) 1488*3431Scarlsonj timeout_ms *= 2; 1489*3431Scarlsonj timeout_ms += (int)(randval * dsmp->dsm_send_timeout); 1490*3431Scarlsonj 1491*3431Scarlsonj /* This checks the MRT (maximum retransmission time) */ 1492*3431Scarlsonj if (dsmp->dsm_send_tcenter != 0 && 1493*3431Scarlsonj timeout_ms > dsmp->dsm_send_tcenter) { 1494*3431Scarlsonj timeout_ms = dsmp->dsm_send_tcenter + 1495*3431Scarlsonj (uint_t)(randval * dsmp->dsm_send_tcenter); 1496*3431Scarlsonj } 1497*3431Scarlsonj 1498*3431Scarlsonj dsmp->dsm_send_timeout = timeout_ms; 1499*3431Scarlsonj } else { 1500*3431Scarlsonj if (dsmp->dsm_state == RENEWING || 1501*3431Scarlsonj dsmp->dsm_state == REBINDING) { 1502*3431Scarlsonj monosec_t mono; 1503*3431Scarlsonj 1504*3431Scarlsonj timeout_ms = dsmp->dsm_state == RENEWING ? 1505*3431Scarlsonj dsmp->dsm_leases->dl_t2.dt_start : 1506*3431Scarlsonj dsmp->dsm_leases->dl_lifs->lif_expire.dt_start; 1507*3431Scarlsonj timeout_ms += dsmp->dsm_curstart_monosec; 1508*3431Scarlsonj mono = monosec(); 1509*3431Scarlsonj if (mono > timeout_ms) 1510*3431Scarlsonj timeout_ms = 0; 1511*3431Scarlsonj else 1512*3431Scarlsonj timeout_ms -= mono; 1513*3431Scarlsonj timeout_ms *= MILLISEC / 2; 1514*3431Scarlsonj } else { 1515*3431Scarlsonj /* 1516*3431Scarlsonj * Start at 4, and increase by a factor of 2 up to 64. 1517*3431Scarlsonj */ 1518*3431Scarlsonj if (first_send) { 1519*3431Scarlsonj timeout_ms = 4 * MILLISEC; 1520*3431Scarlsonj } else { 1521*3431Scarlsonj timeout_ms = MIN(dsmp->dsm_send_tcenter << 1, 1522*3431Scarlsonj 64 * MILLISEC); 1523*3431Scarlsonj } 1524*3431Scarlsonj } 1525*3431Scarlsonj 1526*3431Scarlsonj dsmp->dsm_send_tcenter = timeout_ms; 1527*3431Scarlsonj 1528*3431Scarlsonj /* 1529*3431Scarlsonj * At each iteration, jitter the timeout by some fraction of a 1530*3431Scarlsonj * second. 1531*3431Scarlsonj */ 1532*3431Scarlsonj dsmp->dsm_send_timeout = timeout_ms + 1533*3431Scarlsonj ((lrand48() % (2 * MILLISEC)) - MILLISEC); 1534*3431Scarlsonj } 1535*3431Scarlsonj } 15360Sstevel@tonic-gate 1537*3431Scarlsonj /* 1538*3431Scarlsonj * dhcp_ip_default(): open and bind the default IP sockets used for I/O and 1539*3431Scarlsonj * interface control. 1540*3431Scarlsonj * 1541*3431Scarlsonj * input: none 1542*3431Scarlsonj * output: B_TRUE on success 1543*3431Scarlsonj */ 1544*3431Scarlsonj 1545*3431Scarlsonj boolean_t 1546*3431Scarlsonj dhcp_ip_default(void) 1547*3431Scarlsonj { 1548*3431Scarlsonj int on; 1549*3431Scarlsonj 1550*3431Scarlsonj if ((v4_sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { 1551*3431Scarlsonj dhcpmsg(MSG_ERR, 1552*3431Scarlsonj "dhcp_ip_default: unable to create IPv4 socket"); 1553*3431Scarlsonj return (B_FALSE); 1554*3431Scarlsonj } 1555*3431Scarlsonj 1556*3431Scarlsonj if (setsockopt(v4_sock_fd, IPPROTO_IP, IP_RECVDSTADDR, &on, 1557*3431Scarlsonj sizeof (on)) == -1) { 1558*3431Scarlsonj dhcpmsg(MSG_ERR, 1559*3431Scarlsonj "dhcp_ip_default: unable to enable IP_RECVDSTADDR"); 1560*3431Scarlsonj return (B_FALSE); 1561*3431Scarlsonj } 1562*3431Scarlsonj 1563*3431Scarlsonj if (setsockopt(v4_sock_fd, IPPROTO_IP, IP_RECVIF, &on, sizeof (on)) == 1564*3431Scarlsonj -1) { 1565*3431Scarlsonj dhcpmsg(MSG_ERR, 1566*3431Scarlsonj "dhcp_ip_default: unable to enable IP_RECVIF"); 1567*3431Scarlsonj return (B_FALSE); 1568*3431Scarlsonj } 15690Sstevel@tonic-gate 1570*3431Scarlsonj if (!bind_sock(v4_sock_fd, IPPORT_BOOTPC, INADDR_ANY)) { 1571*3431Scarlsonj dhcpmsg(MSG_ERROR, 1572*3431Scarlsonj "dhcp_ip_default: unable to bind IPv4 socket to port %d", 1573*3431Scarlsonj IPPORT_BOOTPC); 1574*3431Scarlsonj return (B_FALSE); 1575*3431Scarlsonj } 1576*3431Scarlsonj 1577*3431Scarlsonj if (iu_register_event(eh, v4_sock_fd, POLLIN, dhcp_acknak_common, 1578*3431Scarlsonj NULL) == -1) { 1579*3431Scarlsonj dhcpmsg(MSG_WARNING, "dhcp_ip_default: cannot register to " 1580*3431Scarlsonj "receive IPv4 broadcasts"); 1581*3431Scarlsonj return (B_FALSE); 1582*3431Scarlsonj } 1583*3431Scarlsonj 1584*3431Scarlsonj if ((v6_sock_fd = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) { 1585*3431Scarlsonj dhcpmsg(MSG_ERR, 1586*3431Scarlsonj "dhcp_ip_default: unable to create IPv6 socket"); 1587*3431Scarlsonj return (B_FALSE); 1588*3431Scarlsonj } 1589*3431Scarlsonj 1590*3431Scarlsonj if (setsockopt(v6_sock_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, 1591*3431Scarlsonj sizeof (on)) == -1) { 1592*3431Scarlsonj dhcpmsg(MSG_ERR, 1593*3431Scarlsonj "dhcp_ip_default: unable to enable IPV6_RECVPKTINFO"); 1594*3431Scarlsonj return (B_FALSE); 1595*3431Scarlsonj } 1596*3431Scarlsonj 1597*3431Scarlsonj if (!bind_sock_v6(v6_sock_fd, IPPORT_DHCPV6C, NULL)) { 1598*3431Scarlsonj dhcpmsg(MSG_ERROR, 1599*3431Scarlsonj "dhcp_ip_default: unable to bind IPv6 socket to port %d", 1600*3431Scarlsonj IPPORT_DHCPV6C); 1601*3431Scarlsonj return (B_FALSE); 1602*3431Scarlsonj } 1603*3431Scarlsonj 1604*3431Scarlsonj if (iu_register_event(eh, v6_sock_fd, POLLIN, dhcp_acknak_common, 1605*3431Scarlsonj NULL) == -1) { 1606*3431Scarlsonj dhcpmsg(MSG_WARNING, "dhcp_ip_default: cannot register to " 1607*3431Scarlsonj "receive IPv6 packets"); 1608*3431Scarlsonj return (B_FALSE); 1609*3431Scarlsonj } 1610*3431Scarlsonj 1611*3431Scarlsonj return (B_TRUE); 16120Sstevel@tonic-gate } 1613