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 53431Scarlsonj * Common Development and Distribution License (the "License"). 63431Scarlsonj * 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 /* 223431Scarlsonj * 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> 343431Scarlsonj #include <search.h> 353431Scarlsonj #include <alloca.h> 363431Scarlsonj #include <limits.h> 373431Scarlsonj #include <stropts.h> 383431Scarlsonj #include <netinet/dhcp6.h> 393431Scarlsonj #include <arpa/inet.h> 403431Scarlsonj #include <sys/sysmacros.h> 413431Scarlsonj #include <sys/sockio.h> 423431Scarlsonj #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" 493431Scarlsonj #include "dlpi_io.h" 500Sstevel@tonic-gate 513431Scarlsonj int v6_sock_fd = -1; 523431Scarlsonj int v4_sock_fd = -1; 533431Scarlsonj 543431Scarlsonj const in6_addr_t ipv6_all_dhcp_relay_and_servers = { 553431Scarlsonj 0xff, 0x02, 0x00, 0x00, 563431Scarlsonj 0x00, 0x00, 0x00, 0x00, 573431Scarlsonj 0x00, 0x00, 0x00, 0x00, 583431Scarlsonj 0x00, 0x01, 0x00, 0x02 593431Scarlsonj }; 600Sstevel@tonic-gate 610Sstevel@tonic-gate /* 623431Scarlsonj * We have our own version of this constant because dhcpagent is compiled with 633431Scarlsonj * -lxnet. 640Sstevel@tonic-gate */ 653431Scarlsonj const in6_addr_t my_in6addr_any = IN6ADDR_ANY_INIT; 660Sstevel@tonic-gate 673431Scarlsonj static void retransmit(iu_tq_t *, void *); 683431Scarlsonj static void next_retransmission(dhcp_smach_t *, boolean_t, boolean_t); 693431Scarlsonj static boolean_t send_pkt_internal(dhcp_smach_t *); 700Sstevel@tonic-gate 710Sstevel@tonic-gate /* 723431Scarlsonj * pkt_send_type(): returns an integer representing the packet's type; only 733431Scarlsonj * for use with outbound packets. 740Sstevel@tonic-gate * 753431Scarlsonj * 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 803431Scarlsonj pkt_send_type(const dhcp_pkt_t *dpkt) 810Sstevel@tonic-gate { 823431Scarlsonj const uchar_t *option; 833431Scarlsonj 843431Scarlsonj if (dpkt->pkt_isv6) 853431Scarlsonj 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 933431Scarlsonj option = dpkt->pkt->options; 943431Scarlsonj for (;;) { 953431Scarlsonj if (*option == CD_PAD) { 963431Scarlsonj option++; 973431Scarlsonj continue; 983431Scarlsonj } 993431Scarlsonj if (*option == CD_END || 1003431Scarlsonj option + 2 - dpkt->pkt->options >= 1013431Scarlsonj sizeof (dpkt->pkt->options)) 1020Sstevel@tonic-gate return (0); 1033431Scarlsonj if (*option == CD_DHCP_TYPE) 1043431Scarlsonj break; 1050Sstevel@tonic-gate option++; 1063431Scarlsonj option += *option + 1; 1070Sstevel@tonic-gate } 1080Sstevel@tonic-gate 1090Sstevel@tonic-gate return (option[2]); 1100Sstevel@tonic-gate } 1110Sstevel@tonic-gate 1120Sstevel@tonic-gate /* 1133431Scarlsonj * pkt_recv_type(): returns an integer representing the packet's type; only 1143431Scarlsonj * for use with inbound packets. 1153431Scarlsonj * 1163431Scarlsonj * input: dhcp_pkt_t *: the packet to examine 1173431Scarlsonj * output: uchar_t: the packet type (0 if unknown) 1183431Scarlsonj */ 1193431Scarlsonj 1203431Scarlsonj uchar_t 1213431Scarlsonj pkt_recv_type(const PKT_LIST *plp) 1223431Scarlsonj { 1233431Scarlsonj if (plp->isv6) 1243431Scarlsonj return (((const dhcpv6_message_t *)plp->pkt)->d6m_msg_type); 1253431Scarlsonj else if (plp->opts[CD_DHCP_TYPE] != NULL) 1263431Scarlsonj return (plp->opts[CD_DHCP_TYPE]->value[0]); 1273431Scarlsonj else 1283431Scarlsonj return (0); 1293431Scarlsonj } 1303431Scarlsonj 1313431Scarlsonj /* 1323431Scarlsonj * pkt_get_xid(): returns transaction ID from a DHCP packet. 1333431Scarlsonj * 1343431Scarlsonj * input: const PKT *: the packet to examine 1353431Scarlsonj * output: uint_t: the transaction ID (0 if unknown) 1363431Scarlsonj */ 1373431Scarlsonj 1383431Scarlsonj uint_t 1393431Scarlsonj pkt_get_xid(const PKT *pkt, boolean_t isv6) 1403431Scarlsonj { 1413431Scarlsonj if (pkt == NULL) 1423431Scarlsonj return (0); 1433431Scarlsonj if (isv6) 1443431Scarlsonj return (DHCPV6_GET_TRANSID((const dhcpv6_message_t *)pkt)); 1453431Scarlsonj else 1463431Scarlsonj return (pkt->xid); 1473431Scarlsonj } 1483431Scarlsonj 1493431Scarlsonj /* 1500Sstevel@tonic-gate * init_pkt(): initializes and returns a packet of a given type 1510Sstevel@tonic-gate * 1523431Scarlsonj * input: dhcp_smach_t *: the state machine that will send the packet 1530Sstevel@tonic-gate * uchar_t: the packet type (DHCP message type) 1543431Scarlsonj * 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 * 1583431Scarlsonj init_pkt(dhcp_smach_t *dsmp, uchar_t type) 1590Sstevel@tonic-gate { 1603431Scarlsonj uint_t mtu; 1613431Scarlsonj dhcp_pkt_t *dpkt = &dsmp->dsm_send_pkt; 1623431Scarlsonj dhcp_lif_t *lif = dsmp->dsm_lif; 1633431Scarlsonj dhcp_pif_t *pif = lif->lif_pif; 1640Sstevel@tonic-gate uint32_t xid; 1653431Scarlsonj boolean_t isv6; 1660Sstevel@tonic-gate 1673431Scarlsonj mtu = dsmp->dsm_using_dlpi ? pif->pif_max : lif->lif_max; 1683431Scarlsonj 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. 1733431Scarlsonj * 1743431Scarlsonj * Note that transaction ID zero is intentionally never assigned. 1753431Scarlsonj * That's used to represent "no ID." Also note that transaction IDs 1763431Scarlsonj * are only 24 bits long in DHCPv6. 1770Sstevel@tonic-gate */ 1780Sstevel@tonic-gate 1790Sstevel@tonic-gate do { 1800Sstevel@tonic-gate xid = mrand48(); 1813431Scarlsonj if (isv6) 1823431Scarlsonj xid &= 0xFFFFFF; 1833431Scarlsonj } while (xid == 0 || 1843431Scarlsonj lookup_smach_by_xid(xid, NULL, dpkt->pkt_isv6) != NULL); 1853431Scarlsonj 1863431Scarlsonj if (isv6) { 1873431Scarlsonj dhcpv6_message_t *v6; 1883431Scarlsonj 1893431Scarlsonj if (mtu != dpkt->pkt_max_len && 1903431Scarlsonj (v6 = realloc(dpkt->pkt, mtu)) != NULL) { 1913431Scarlsonj /* LINTED: alignment known to be correct */ 1923431Scarlsonj dpkt->pkt = (PKT *)v6; 1933431Scarlsonj dpkt->pkt_max_len = mtu; 1943431Scarlsonj } 1953431Scarlsonj 1963431Scarlsonj if (sizeof (*v6) > dpkt->pkt_max_len) { 1973431Scarlsonj dhcpmsg(MSG_ERR, "init_pkt: cannot allocate v6 pkt: %u", 1983431Scarlsonj mtu); 1993431Scarlsonj return (NULL); 2003431Scarlsonj } 2013431Scarlsonj 2023431Scarlsonj v6 = (dhcpv6_message_t *)dpkt->pkt; 2033431Scarlsonj dpkt->pkt_cur_len = sizeof (*v6); 2043431Scarlsonj 2053431Scarlsonj (void) memset(v6, 0, dpkt->pkt_max_len); 2063431Scarlsonj 2073431Scarlsonj v6->d6m_msg_type = type; 2083431Scarlsonj DHCPV6_SET_TRANSID(v6, xid); 2093431Scarlsonj 2103431Scarlsonj if (dsmp->dsm_cidlen > 0 && 2113431Scarlsonj add_pkt_opt(dpkt, DHCPV6_OPT_CLIENTID, dsmp->dsm_cid, 2123431Scarlsonj dsmp->dsm_cidlen) == NULL) { 2133431Scarlsonj dhcpmsg(MSG_WARNING, 2143431Scarlsonj "init_pkt: cannot insert client ID"); 2153431Scarlsonj return (NULL); 2163431Scarlsonj } 2173431Scarlsonj 2183431Scarlsonj /* For v6, time starts with the creation of a transaction */ 2193431Scarlsonj dsmp->dsm_neg_hrtime = gethrtime(); 2203431Scarlsonj dsmp->dsm_newstart_monosec = monosec(); 2213431Scarlsonj } else { 2223431Scarlsonj static uint8_t bootmagic[] = BOOTMAGIC; 2233431Scarlsonj PKT *v4; 2243431Scarlsonj 2253431Scarlsonj if (mtu != dpkt->pkt_max_len && 2263431Scarlsonj (v4 = realloc(dpkt->pkt, mtu)) != NULL) { 2273431Scarlsonj dpkt->pkt = v4; 2283431Scarlsonj dpkt->pkt_max_len = mtu; 2293431Scarlsonj } 2300Sstevel@tonic-gate 2313431Scarlsonj if (offsetof(PKT, options) > dpkt->pkt_max_len) { 2323431Scarlsonj dhcpmsg(MSG_ERR, "init_pkt: cannot allocate v4 pkt: %u", 2333431Scarlsonj mtu); 2343431Scarlsonj return (NULL); 2353431Scarlsonj } 2363431Scarlsonj 2373431Scarlsonj v4 = dpkt->pkt; 2383431Scarlsonj dpkt->pkt_cur_len = offsetof(PKT, options); 2390Sstevel@tonic-gate 2403431Scarlsonj (void) memset(v4, 0, dpkt->pkt_max_len); 2413431Scarlsonj (void) memcpy(v4->cookie, bootmagic, sizeof (bootmagic)); 2423431Scarlsonj if (pif->pif_hwlen <= sizeof (v4->chaddr)) { 2433431Scarlsonj v4->hlen = pif->pif_hwlen; 2443431Scarlsonj (void) memcpy(v4->chaddr, pif->pif_hwaddr, 2453431Scarlsonj pif->pif_hwlen); 2463431Scarlsonj } else { 2473431Scarlsonj /* 2483431Scarlsonj * The mac address does not fit in the chaddr 2493431Scarlsonj * field, thus it can not be sent to the server, 2503431Scarlsonj * thus server can not unicast the reply. Per 2513431Scarlsonj * RFC 2131 4.4.1, client can set this bit in 2523431Scarlsonj * DISCOVER/REQUEST. If the client is already 2533431Scarlsonj * in BOUND/REBINDING/RENEWING state, do not set 2543431Scarlsonj * this bit, as it can respond to unicast responses 2553431Scarlsonj * from server using the 'ciaddr' address. 2563431Scarlsonj */ 2573431Scarlsonj if (type == DISCOVER || 2583431Scarlsonj (type == REQUEST && dsmp->dsm_state != RENEWING && 2593431Scarlsonj dsmp->dsm_state != REBINDING && 2603431Scarlsonj dsmp->dsm_state != BOUND)) 2613431Scarlsonj v4->flags = htons(BCAST_MASK); 2623431Scarlsonj } 2633431Scarlsonj 2643431Scarlsonj v4->xid = xid; 2653431Scarlsonj v4->op = BOOTREQUEST; 2663431Scarlsonj v4->htype = pif->pif_hwtype; 2673431Scarlsonj 2683431Scarlsonj if (add_pkt_opt(dpkt, CD_DHCP_TYPE, &type, 1) == NULL) { 2693431Scarlsonj dhcpmsg(MSG_WARNING, 2703431Scarlsonj "init_pkt: cannot set DHCP packet type"); 2713431Scarlsonj return (NULL); 2723431Scarlsonj } 2733431Scarlsonj 2743431Scarlsonj if (dsmp->dsm_cidlen > 0 && 2753431Scarlsonj add_pkt_opt(dpkt, CD_CLIENT_ID, dsmp->dsm_cid, 2763431Scarlsonj dsmp->dsm_cidlen) == NULL) { 2773431Scarlsonj dhcpmsg(MSG_WARNING, 2783431Scarlsonj "init_pkt: cannot insert client ID"); 2793431Scarlsonj return (NULL); 2803431Scarlsonj } 2813431Scarlsonj } 2820Sstevel@tonic-gate 2830Sstevel@tonic-gate return (dpkt); 2840Sstevel@tonic-gate } 2850Sstevel@tonic-gate 2860Sstevel@tonic-gate /* 2873431Scarlsonj * remove_pkt_opt(): removes the first instance of an option from a dhcp_pkt_t 2883431Scarlsonj * 2893431Scarlsonj * input: dhcp_pkt_t *: the packet to remove the option from 2903431Scarlsonj * uint_t: the type of option being added 2913431Scarlsonj * output: boolean_t: B_TRUE on success, B_FALSE on failure 2923431Scarlsonj * note: currently does not work with DHCPv6 suboptions, or to remove 2933431Scarlsonj * arbitrary option instances. 2943431Scarlsonj */ 2953431Scarlsonj 2963431Scarlsonj boolean_t 2973431Scarlsonj remove_pkt_opt(dhcp_pkt_t *dpkt, uint_t opt_type) 2983431Scarlsonj { 2993431Scarlsonj uchar_t *raw_pkt, *raw_end, *next; 3003431Scarlsonj uint_t len; 3013431Scarlsonj 3023431Scarlsonj raw_pkt = (uchar_t *)dpkt->pkt; 3033431Scarlsonj raw_end = raw_pkt + dpkt->pkt_cur_len; 3043431Scarlsonj if (dpkt->pkt_isv6) { 3053431Scarlsonj dhcpv6_option_t d6o; 3063431Scarlsonj 3073431Scarlsonj raw_pkt += sizeof (dhcpv6_message_t); 3083431Scarlsonj 3093431Scarlsonj opt_type = htons(opt_type); 3103431Scarlsonj while (raw_pkt + sizeof (d6o) <= raw_end) { 3113431Scarlsonj (void) memcpy(&d6o, raw_pkt, sizeof (d6o)); 3123431Scarlsonj len = ntohs(d6o.d6o_len) + sizeof (d6o); 3133431Scarlsonj if (len > raw_end - raw_pkt) 3143431Scarlsonj break; 3153431Scarlsonj next = raw_pkt + len; 3163431Scarlsonj if (d6o.d6o_code == opt_type) { 3173431Scarlsonj if (next < raw_end) { 3183431Scarlsonj (void) memmove(raw_pkt, next, 3193431Scarlsonj raw_end - next); 3203431Scarlsonj } 3213431Scarlsonj dpkt->pkt_cur_len -= len; 3223431Scarlsonj return (B_TRUE); 3233431Scarlsonj } 3243431Scarlsonj raw_pkt = next; 3253431Scarlsonj } 3263431Scarlsonj } else { 3273431Scarlsonj uchar_t *pstart, *padrun; 3283431Scarlsonj 3293431Scarlsonj raw_pkt += offsetof(PKT, options); 3303431Scarlsonj pstart = raw_pkt; 3313431Scarlsonj 3323431Scarlsonj if (opt_type == CD_END || opt_type == CD_PAD) 3333431Scarlsonj return (B_FALSE); 3343431Scarlsonj 3353431Scarlsonj padrun = NULL; 3363431Scarlsonj while (raw_pkt + 1 <= raw_end) { 3373431Scarlsonj if (*raw_pkt == CD_END) 3383431Scarlsonj break; 3393431Scarlsonj if (*raw_pkt == CD_PAD) { 3403431Scarlsonj if (padrun == NULL) 3413431Scarlsonj padrun = raw_pkt; 3423431Scarlsonj raw_pkt++; 3433431Scarlsonj continue; 3443431Scarlsonj } 3453431Scarlsonj if (raw_pkt + 2 > raw_end) 3463431Scarlsonj break; 3473431Scarlsonj len = raw_pkt[1]; 3483431Scarlsonj if (len > raw_end - raw_pkt || len < 2) 3493431Scarlsonj break; 3503431Scarlsonj next = raw_pkt + len; 3513431Scarlsonj if (*raw_pkt == opt_type) { 3523431Scarlsonj if (next < raw_end) { 3533431Scarlsonj int toadd = (4 + ((next-pstart)&3) - 3543431Scarlsonj ((raw_pkt-pstart)&3)) & 3; 3553431Scarlsonj int torem = 4 - toadd; 3563431Scarlsonj 3573431Scarlsonj if (torem != 4 && padrun != NULL && 3583431Scarlsonj (raw_pkt - padrun) >= torem) { 3593431Scarlsonj raw_pkt -= torem; 3603431Scarlsonj dpkt->pkt_cur_len -= torem; 3613431Scarlsonj } else if (toadd > 0) { 3623431Scarlsonj (void) memset(raw_pkt, CD_PAD, 3633431Scarlsonj toadd); 3643431Scarlsonj raw_pkt += toadd; 3653431Scarlsonj /* max is not an issue here */ 3663431Scarlsonj dpkt->pkt_cur_len += toadd; 3673431Scarlsonj } 3683431Scarlsonj if (raw_pkt != next) { 3693431Scarlsonj (void) memmove(raw_pkt, next, 3703431Scarlsonj raw_end - next); 3713431Scarlsonj } 3723431Scarlsonj } 3733431Scarlsonj dpkt->pkt_cur_len -= len; 3743431Scarlsonj return (B_TRUE); 3753431Scarlsonj } 3763431Scarlsonj padrun = NULL; 3773431Scarlsonj raw_pkt = next; 3783431Scarlsonj } 3793431Scarlsonj } 3803431Scarlsonj return (B_FALSE); 3813431Scarlsonj } 3823431Scarlsonj 3833431Scarlsonj /* 3843431Scarlsonj * update_v6opt_len(): updates the length field of a DHCPv6 option. 3853431Scarlsonj * 3863431Scarlsonj * input: dhcpv6_option_t *: option to be updated 3873431Scarlsonj * int: number of octets to add or subtract 3883431Scarlsonj * output: boolean_t: B_TRUE on success, B_FALSE on failure 3893431Scarlsonj */ 3903431Scarlsonj 3913431Scarlsonj boolean_t 3923431Scarlsonj update_v6opt_len(dhcpv6_option_t *opt, int adjust) 3933431Scarlsonj { 3943431Scarlsonj dhcpv6_option_t optval; 3953431Scarlsonj 3963431Scarlsonj (void) memcpy(&optval, opt, sizeof (optval)); 3973431Scarlsonj adjust += ntohs(optval.d6o_len); 3983431Scarlsonj if (adjust < 0 || adjust > UINT16_MAX) { 3993431Scarlsonj return (B_FALSE); 4003431Scarlsonj } else { 4013431Scarlsonj optval.d6o_len = htons(adjust); 4023431Scarlsonj (void) memcpy(opt, &optval, sizeof (optval)); 4033431Scarlsonj return (B_TRUE); 4043431Scarlsonj } 4053431Scarlsonj } 4063431Scarlsonj 4073431Scarlsonj /* 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 4113431Scarlsonj * uint_t: the type of option being added 4120Sstevel@tonic-gate * const void *: the value of that option 4133431Scarlsonj * uint_t: the length of the value of the option 4143431Scarlsonj * output: void *: pointer to the option that was added, or NULL on failure. 4150Sstevel@tonic-gate */ 4160Sstevel@tonic-gate 4173431Scarlsonj void * 4183431Scarlsonj add_pkt_opt(dhcp_pkt_t *dpkt, uint_t opt_type, const void *opt_val, 4193431Scarlsonj uint_t opt_len) 4200Sstevel@tonic-gate { 4213431Scarlsonj uchar_t *raw_pkt; 4223431Scarlsonj int req_len; 4233431Scarlsonj void *optr; 4243431Scarlsonj 4253431Scarlsonj raw_pkt = (uchar_t *)dpkt->pkt; 4263431Scarlsonj optr = raw_pkt + dpkt->pkt_cur_len; 4273431Scarlsonj if (dpkt->pkt_isv6) { 4283431Scarlsonj dhcpv6_option_t d6o; 4293431Scarlsonj 4303431Scarlsonj req_len = opt_len + sizeof (d6o); 4313431Scarlsonj 4323431Scarlsonj if (dpkt->pkt_cur_len + req_len > dpkt->pkt_max_len) { 4333431Scarlsonj dhcpmsg(MSG_WARNING, 4343431Scarlsonj "add_pkt_opt: not enough room for v6 option %u in " 4353431Scarlsonj "packet (%u + %u > %u)", opt_type, 4363431Scarlsonj dpkt->pkt_cur_len, req_len, dpkt->pkt_max_len); 4373431Scarlsonj return (NULL); 4383431Scarlsonj } 4393431Scarlsonj d6o.d6o_code = htons(opt_type); 4403431Scarlsonj d6o.d6o_len = htons(opt_len); 4413431Scarlsonj (void) memcpy(&raw_pkt[dpkt->pkt_cur_len], &d6o, sizeof (d6o)); 4423431Scarlsonj dpkt->pkt_cur_len += sizeof (d6o); 4433431Scarlsonj if (opt_len > 0) { 4443431Scarlsonj (void) memcpy(&raw_pkt[dpkt->pkt_cur_len], opt_val, 4453431Scarlsonj opt_len); 4463431Scarlsonj dpkt->pkt_cur_len += opt_len; 4473431Scarlsonj } 4483431Scarlsonj } else { 4493431Scarlsonj req_len = opt_len + 2; /* + 2 for code & length bytes */ 4503431Scarlsonj 4513431Scarlsonj /* CD_END and CD_PAD options don't have a length field */ 4523431Scarlsonj if (opt_type == CD_END || opt_type == CD_PAD) { 4533431Scarlsonj req_len = 1; 4543431Scarlsonj } else if (opt_val == NULL) { 4553431Scarlsonj dhcpmsg(MSG_ERROR, "add_pkt_opt: option type %d is " 4563431Scarlsonj "missing required value", opt_type); 4573431Scarlsonj return (NULL); 4583431Scarlsonj } 4593431Scarlsonj 4603431Scarlsonj if ((dpkt->pkt_cur_len + req_len) > dpkt->pkt_max_len) { 4613431Scarlsonj dhcpmsg(MSG_WARNING, 4623431Scarlsonj "add_pkt_opt: not enough room for v4 option %u in " 4633431Scarlsonj "packet", opt_type); 4643431Scarlsonj return (NULL); 4653431Scarlsonj } 4663431Scarlsonj 4673431Scarlsonj raw_pkt[dpkt->pkt_cur_len++] = opt_type; 4680Sstevel@tonic-gate 4693431Scarlsonj if (req_len > 1) { 4703431Scarlsonj raw_pkt[dpkt->pkt_cur_len++] = opt_len; 4713431Scarlsonj if (opt_len > 0) { 4723431Scarlsonj (void) memcpy(&raw_pkt[dpkt->pkt_cur_len], 4733431Scarlsonj opt_val, opt_len); 4743431Scarlsonj dpkt->pkt_cur_len += opt_len; 4753431Scarlsonj } 4763431Scarlsonj } 4773431Scarlsonj } 4783431Scarlsonj return (optr); 4793431Scarlsonj } 4800Sstevel@tonic-gate 4813431Scarlsonj /* 4823431Scarlsonj * add_pkt_subopt(): adds an option to a dhcp_pkt_t option. DHCPv6-specific, 4833431Scarlsonj * but could be extended to IPv4 DHCP if necessary. Assumes 4843431Scarlsonj * that if the parent isn't a top-level option, the caller 4853431Scarlsonj * will adjust any upper-level options recursively using 4863431Scarlsonj * update_v6opt_len. 4873431Scarlsonj * 4883431Scarlsonj * input: dhcp_pkt_t *: the packet to add the suboption to 4893431Scarlsonj * dhcpv6_option_t *: the start of the option to that should contain 4903431Scarlsonj * it (parent) 4913431Scarlsonj * uint_t: the type of suboption being added 4923431Scarlsonj * const void *: the value of that option 4933431Scarlsonj * uint_t: the length of the value of the option 4943431Scarlsonj * output: void *: pointer to the suboption that was added, or NULL on 4953431Scarlsonj * failure. 4963431Scarlsonj */ 4973431Scarlsonj 4983431Scarlsonj void * 4993431Scarlsonj add_pkt_subopt(dhcp_pkt_t *dpkt, dhcpv6_option_t *parentopt, uint_t opt_type, 5003431Scarlsonj const void *opt_val, uint_t opt_len) 5013431Scarlsonj { 5023431Scarlsonj uchar_t *raw_pkt; 5033431Scarlsonj int req_len; 5043431Scarlsonj void *optr; 5053431Scarlsonj dhcpv6_option_t d6o; 5063431Scarlsonj uchar_t *optend; 5073431Scarlsonj int olen; 5083431Scarlsonj 5093431Scarlsonj if (!dpkt->pkt_isv6) 5103431Scarlsonj return (NULL); 5113431Scarlsonj 5123431Scarlsonj raw_pkt = (uchar_t *)dpkt->pkt; 5133431Scarlsonj req_len = opt_len + sizeof (d6o); 5143431Scarlsonj 5153431Scarlsonj if (dpkt->pkt_cur_len + req_len > dpkt->pkt_max_len) { 5163431Scarlsonj dhcpmsg(MSG_WARNING, 5173431Scarlsonj "add_pkt_subopt: not enough room for v6 suboption %u in " 5183431Scarlsonj "packet (%u + %u > %u)", opt_type, 5193431Scarlsonj dpkt->pkt_cur_len, req_len, dpkt->pkt_max_len); 5203431Scarlsonj return (NULL); 5210Sstevel@tonic-gate } 5220Sstevel@tonic-gate 5233431Scarlsonj /* 5243431Scarlsonj * Update the parent option to include room for this option, 5253431Scarlsonj * and compute the insertion point. 5263431Scarlsonj */ 5273431Scarlsonj (void) memcpy(&d6o, parentopt, sizeof (d6o)); 5283431Scarlsonj olen = ntohs(d6o.d6o_len); 5293431Scarlsonj optend = (uchar_t *)(parentopt + 1) + olen; 5303431Scarlsonj olen += req_len; 5313431Scarlsonj d6o.d6o_len = htons(olen); 5323431Scarlsonj (void) memcpy(parentopt, &d6o, sizeof (d6o)); 5330Sstevel@tonic-gate 5343431Scarlsonj /* 5353431Scarlsonj * If there's anything at the end to move, then move it. Also bump up 5363431Scarlsonj * the packet size. 5373431Scarlsonj */ 5383431Scarlsonj if (optend < raw_pkt + dpkt->pkt_cur_len) { 5393431Scarlsonj (void) memmove(optend + req_len, optend, 5403431Scarlsonj (raw_pkt + dpkt->pkt_cur_len) - optend); 5410Sstevel@tonic-gate } 5423431Scarlsonj dpkt->pkt_cur_len += req_len; 5433431Scarlsonj 5443431Scarlsonj /* 5453431Scarlsonj * Now format the suboption and add it in. 5463431Scarlsonj */ 5473431Scarlsonj optr = optend; 5483431Scarlsonj d6o.d6o_code = htons(opt_type); 5493431Scarlsonj d6o.d6o_len = htons(opt_len); 5503431Scarlsonj (void) memcpy(optend, &d6o, sizeof (d6o)); 5513431Scarlsonj if (opt_len > 0) 5523431Scarlsonj (void) memcpy(optend + sizeof (d6o), opt_val, opt_len); 5533431Scarlsonj 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 5603431Scarlsonj * uint_t: the type of option being added 5610Sstevel@tonic-gate * uint16_t: the value of that option 5623431Scarlsonj * output: void *: pointer to the option that was added, or NULL on failure. 5630Sstevel@tonic-gate */ 5640Sstevel@tonic-gate 5653431Scarlsonj void * 5663431Scarlsonj add_pkt_opt16(dhcp_pkt_t *dpkt, uint_t opt_type, uint16_t opt_value) 5670Sstevel@tonic-gate { 5683431Scarlsonj 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 5753431Scarlsonj * uint_t: the type of option being added 5760Sstevel@tonic-gate * uint32_t: the value of that option 5773431Scarlsonj * output: void *: pointer to the option that was added, or NULL on failure. 5783431Scarlsonj */ 5793431Scarlsonj 5803431Scarlsonj void * 5813431Scarlsonj add_pkt_opt32(dhcp_pkt_t *dpkt, uint_t opt_type, uint32_t opt_value) 5823431Scarlsonj { 5833431Scarlsonj return (add_pkt_opt(dpkt, opt_type, &opt_value, 4)); 5843431Scarlsonj } 5853431Scarlsonj 5863431Scarlsonj /* 5873431Scarlsonj * add_pkt_prl(): adds the parameter request option to the packet 5883431Scarlsonj * 5893431Scarlsonj * input: dhcp_pkt_t *: the packet to add the option to 5903431Scarlsonj * dhcp_smach_t *: state machine with request option 5913431Scarlsonj * output: void *: pointer to the option that was added, or NULL on failure. 5920Sstevel@tonic-gate */ 5930Sstevel@tonic-gate 5943431Scarlsonj void * 5953431Scarlsonj add_pkt_prl(dhcp_pkt_t *dpkt, dhcp_smach_t *dsmp) 5960Sstevel@tonic-gate { 5973431Scarlsonj uint_t len; 5983431Scarlsonj 5993431Scarlsonj if (dsmp->dsm_prllen == 0) 6003431Scarlsonj return (0); 6013431Scarlsonj 6023431Scarlsonj if (dpkt->pkt_isv6) { 6033431Scarlsonj uint16_t *prl; 6043431Scarlsonj 6053431Scarlsonj /* 6063431Scarlsonj * RFC 3315 requires that we include the option, even if we 6073431Scarlsonj * have nothing to request. 6083431Scarlsonj */ 6093431Scarlsonj if (dsmp->dsm_prllen == 0) 6103431Scarlsonj prl = NULL; 6113431Scarlsonj else 6123431Scarlsonj prl = alloca(dsmp->dsm_prllen * sizeof (uint16_t)); 6133431Scarlsonj 6143431Scarlsonj for (len = 0; len < dsmp->dsm_prllen; len++) 6153431Scarlsonj prl[len] = htons(dsmp->dsm_prl[len]); 6163431Scarlsonj return (add_pkt_opt(dpkt, DHCPV6_OPT_ORO, prl, 6173431Scarlsonj len * sizeof (uint16_t))); 6183431Scarlsonj } else { 6193431Scarlsonj uint8_t *prl = alloca(dsmp->dsm_prllen); 6203431Scarlsonj 6213431Scarlsonj for (len = 0; len < dsmp->dsm_prllen; len++) 6223431Scarlsonj prl[len] = dsmp->dsm_prl[len]; 6233431Scarlsonj return (add_pkt_opt(dpkt, CD_REQUEST_LIST, prl, len)); 6243431Scarlsonj } 6250Sstevel@tonic-gate } 6260Sstevel@tonic-gate 6270Sstevel@tonic-gate /* 6283431Scarlsonj * add_pkt_lif(): Adds CD_REQUESTED_IP_ADDR (IPv4 DHCP) or IA_NA and IAADDR 6293431Scarlsonj * (DHCPv6) options to the packet to represent the given LIF. 6300Sstevel@tonic-gate * 6313431Scarlsonj * input: dhcp_pkt_t *: the packet to add the options to 6323431Scarlsonj * dhcp_lif_t *: the logical interface to represent 6333431Scarlsonj * int: status code (unused for IPv4 DHCP) 6343431Scarlsonj * const char *: message to include with status option, or NULL 6353431Scarlsonj * output: boolean_t: B_TRUE on success, B_FALSE on failure 6360Sstevel@tonic-gate */ 6370Sstevel@tonic-gate 6383431Scarlsonj boolean_t 6393431Scarlsonj add_pkt_lif(dhcp_pkt_t *dpkt, dhcp_lif_t *lif, int status, const char *msg) 6400Sstevel@tonic-gate { 6413431Scarlsonj if (lif->lif_pif->pif_isv6) { 6423431Scarlsonj dhcp_smach_t *dsmp; 6433431Scarlsonj dhcpv6_message_t *d6m; 6443431Scarlsonj dhcpv6_ia_na_t d6in; 6453431Scarlsonj dhcpv6_iaaddr_t d6ia; 6463431Scarlsonj uint32_t iaid; 6473431Scarlsonj uint16_t *statusopt; 6483431Scarlsonj dhcpv6_option_t *d6o, *d6so; 6493431Scarlsonj uint_t olen; 6503431Scarlsonj 6513431Scarlsonj /* 6523431Scarlsonj * Currently, we support just one IAID related to the primary 6533431Scarlsonj * LIF on the state machine. 6543431Scarlsonj */ 6553431Scarlsonj dsmp = lif->lif_lease->dl_smach; 6563431Scarlsonj iaid = dsmp->dsm_lif->lif_iaid; 6573431Scarlsonj iaid = htonl(iaid); 6583431Scarlsonj 6593431Scarlsonj d6m = (dhcpv6_message_t *)dpkt->pkt; 6600Sstevel@tonic-gate 6613431Scarlsonj /* 6623431Scarlsonj * Find or create the IA_NA needed for this LIF. If we 6633431Scarlsonj * supported IA_TA, we'd check the IFF_TEMPORARY bit here. 6643431Scarlsonj */ 6653431Scarlsonj d6o = NULL; 6663431Scarlsonj while ((d6o = dhcpv6_find_option(d6m + 1, 6673431Scarlsonj dpkt->pkt_cur_len - sizeof (*d6m), d6o, DHCPV6_OPT_IA_NA, 6683431Scarlsonj &olen)) != NULL) { 6693431Scarlsonj if (olen < sizeof (d6in)) 6703431Scarlsonj continue; 6713431Scarlsonj (void) memcpy(&d6in, d6o, sizeof (d6in)); 6723431Scarlsonj if (d6in.d6in_iaid == iaid) 6733431Scarlsonj break; 6743431Scarlsonj } 6753431Scarlsonj if (d6o == NULL) { 6763431Scarlsonj d6in.d6in_iaid = iaid; 6773431Scarlsonj d6in.d6in_t1 = 0; 6783431Scarlsonj d6in.d6in_t2 = 0; 6793431Scarlsonj d6o = add_pkt_opt(dpkt, DHCPV6_OPT_IA_NA, 6803431Scarlsonj (dhcpv6_option_t *)&d6in + 1, 6813431Scarlsonj sizeof (d6in) - sizeof (*d6o)); 6823431Scarlsonj if (d6o == NULL) 6833431Scarlsonj return (B_FALSE); 6843431Scarlsonj } 6850Sstevel@tonic-gate 6863431Scarlsonj /* 6873431Scarlsonj * Now add the IAADDR suboption for this LIF. No need to 6883431Scarlsonj * search here, as we know that this is unique. 6893431Scarlsonj */ 6903431Scarlsonj d6ia.d6ia_addr = lif->lif_v6addr; 6910Sstevel@tonic-gate 6923431Scarlsonj /* 6933431Scarlsonj * For Release and Decline, we zero out the lifetime. For 6943431Scarlsonj * Renew and Rebind, we report the original time as the 6953431Scarlsonj * preferred and valid lifetimes. 6963431Scarlsonj */ 6973431Scarlsonj if (d6m->d6m_msg_type == DHCPV6_MSG_RELEASE || 6983431Scarlsonj d6m->d6m_msg_type == DHCPV6_MSG_DECLINE) { 6993431Scarlsonj d6ia.d6ia_preflife = 0; 7003431Scarlsonj d6ia.d6ia_vallife = 0; 7013431Scarlsonj } else { 7023431Scarlsonj d6ia.d6ia_preflife = htonl(lif->lif_preferred.dt_start); 7033431Scarlsonj d6ia.d6ia_vallife = htonl(lif->lif_expire.dt_start); 7043431Scarlsonj } 7053431Scarlsonj d6so = add_pkt_subopt(dpkt, d6o, DHCPV6_OPT_IAADDR, 7063431Scarlsonj (dhcpv6_option_t *)&d6ia + 1, 7073431Scarlsonj sizeof (d6ia) - sizeof (*d6o)); 7083431Scarlsonj if (d6so == NULL) 7093431Scarlsonj return (B_FALSE); 7100Sstevel@tonic-gate 7113431Scarlsonj /* 7123431Scarlsonj * Add a status code suboption to the IAADDR to tell the server 7133431Scarlsonj * why we're declining the address. Note that we must manually 7143431Scarlsonj * update the enclosing IA_NA, as add_pkt_subopt doesn't know 7153431Scarlsonj * how to do that. 7163431Scarlsonj */ 7173431Scarlsonj if (status != DHCPV6_STAT_SUCCESS || msg != NULL) { 7183431Scarlsonj olen = sizeof (*statusopt) + 7193431Scarlsonj (msg == NULL ? 0 : strlen(msg)); 7203431Scarlsonj statusopt = alloca(olen); 7213431Scarlsonj *statusopt = htons(status); 7223431Scarlsonj if (msg != NULL) { 7233431Scarlsonj (void) memcpy((char *)(statusopt + 1), msg, 7243431Scarlsonj olen - sizeof (*statusopt)); 7253431Scarlsonj } 7263431Scarlsonj d6so = add_pkt_subopt(dpkt, d6so, 7273431Scarlsonj DHCPV6_OPT_STATUS_CODE, statusopt, olen); 7283431Scarlsonj if (d6so != NULL) { 7293431Scarlsonj /* 7303431Scarlsonj * Update for length of suboption header and 7313431Scarlsonj * suboption contents. 7323431Scarlsonj */ 7333431Scarlsonj (void) update_v6opt_len(d6o, sizeof (*d6so) + 7343431Scarlsonj olen); 7353431Scarlsonj } 7363431Scarlsonj } 7373431Scarlsonj } else { 7383431Scarlsonj /* 7393431Scarlsonj * For DECLINE, we need to add the CD_REQUESTED_IP_ADDR option. 7403431Scarlsonj * In all other cases (RELEASE and REQUEST), we need to set 7413431Scarlsonj * ciadr. 7423431Scarlsonj */ 7433431Scarlsonj if (pkt_send_type(dpkt) == DECLINE) { 7443431Scarlsonj if (!add_pkt_opt32(dpkt, CD_REQUESTED_IP_ADDR, 7453431Scarlsonj lif->lif_addr)) 7463431Scarlsonj return (B_FALSE); 7473431Scarlsonj } else { 7483431Scarlsonj dpkt->pkt->ciaddr.s_addr = lif->lif_addr; 7493431Scarlsonj } 7503431Scarlsonj 7513431Scarlsonj /* 7523431Scarlsonj * It's not too worrisome if the message fails to fit in the 7533431Scarlsonj * packet. The result will still be valid. 7543431Scarlsonj */ 7553431Scarlsonj if (msg != NULL) 7563431Scarlsonj (void) add_pkt_opt(dpkt, CD_MESSAGE, msg, 7573431Scarlsonj strlen(msg) + 1); 7580Sstevel@tonic-gate } 7593431Scarlsonj return (B_TRUE); 7600Sstevel@tonic-gate } 7610Sstevel@tonic-gate 7620Sstevel@tonic-gate /* 7633431Scarlsonj * free_pkt_entry(): frees a packet list list entry 7640Sstevel@tonic-gate * 7653431Scarlsonj * input: PKT_LIST *: the packet list entry to free 7663431Scarlsonj * output: void 7670Sstevel@tonic-gate */ 7683431Scarlsonj void 7693431Scarlsonj free_pkt_entry(PKT_LIST *plp) 7700Sstevel@tonic-gate { 7713431Scarlsonj if (plp != NULL) { 7723431Scarlsonj free(plp->pkt); 7733431Scarlsonj free(plp); 7743431Scarlsonj } 7750Sstevel@tonic-gate } 7760Sstevel@tonic-gate 7770Sstevel@tonic-gate /* 7783431Scarlsonj * 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 7853431Scarlsonj free_pkt_list(PKT_LIST **head) 7860Sstevel@tonic-gate { 7873431Scarlsonj PKT_LIST *plp; 7880Sstevel@tonic-gate 7893431Scarlsonj while ((plp = *head) != NULL) { 7903431Scarlsonj remque(plp); 7913431Scarlsonj 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 * 7983431Scarlsonj * input: dhcp_smach_t *: the state machine with a packet to send 7993431Scarlsonj * output: boolean_t: B_TRUE if the packet is sent, B_FALSE otherwise 8000Sstevel@tonic-gate */ 8010Sstevel@tonic-gate 8023431Scarlsonj static boolean_t 8033431Scarlsonj send_pkt_internal(dhcp_smach_t *dsmp) 8040Sstevel@tonic-gate { 8050Sstevel@tonic-gate ssize_t n_bytes; 8063431Scarlsonj dhcp_lif_t *lif = dsmp->dsm_lif; 8073431Scarlsonj dhcp_pif_t *pif = lif->lif_pif; 8083431Scarlsonj dhcp_pkt_t *dpkt = &dsmp->dsm_send_pkt; 8093431Scarlsonj uchar_t ptype = pkt_send_type(dpkt); 8103431Scarlsonj const char *pkt_name; 8113431Scarlsonj struct iovec iov; 8123431Scarlsonj struct msghdr msg; 8133431Scarlsonj struct cmsghdr *cmsg; 8143431Scarlsonj struct in6_pktinfo *ipi6; 8153431Scarlsonj boolean_t ismcast; 8163431Scarlsonj 8173431Scarlsonj /* 8183431Scarlsonj * Timer should not be running at the point we go to send a packet. 8193431Scarlsonj */ 8203431Scarlsonj if (dsmp->dsm_retrans_timer != -1) { 8213431Scarlsonj dhcpmsg(MSG_CRIT, "send_pkt_internal: unexpected retransmit " 8223431Scarlsonj "timer on %s", dsmp->dsm_name); 8233431Scarlsonj stop_pkt_retransmission(dsmp); 8243431Scarlsonj } 8253431Scarlsonj 8263431Scarlsonj 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 8363431Scarlsonj if (dsmp->dsm_send_timeout != 0) { 8373431Scarlsonj if ((dsmp->dsm_retrans_timer = iu_schedule_timer_ms(tq, 8383431Scarlsonj 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 8433431Scarlsonj hold_smach(dsmp); 8443431Scarlsonj } 8453431Scarlsonj 8463431Scarlsonj if (dpkt->pkt_isv6) { 8473431Scarlsonj hrtime_t delta; 8483431Scarlsonj 8493431Scarlsonj /* 8503431Scarlsonj * Convert current time into centiseconds since transaction 8513431Scarlsonj * started. This is what DHCPv6 expects to see in the Elapsed 8523431Scarlsonj * Time option. 8533431Scarlsonj */ 8543431Scarlsonj delta = (gethrtime() - dsmp->dsm_neg_hrtime) / 8553431Scarlsonj (NANOSEC / 100); 8563431Scarlsonj if (delta > DHCPV6_FOREVER) 8573431Scarlsonj delta = DHCPV6_FOREVER; 8583431Scarlsonj (void) remove_pkt_opt(dpkt, DHCPV6_OPT_ELAPSED_TIME); 8593431Scarlsonj (void) add_pkt_opt16(dpkt, DHCPV6_OPT_ELAPSED_TIME, 8603431Scarlsonj htons(delta)); 8613431Scarlsonj } else { 8623431Scarlsonj /* 8633431Scarlsonj * set the `pkt->secs' field depending on the type of packet. 8643431Scarlsonj * it should be zero, except in the following cases: 8653431Scarlsonj * 8663431Scarlsonj * DISCOVER: set to the number of seconds since we started 8673431Scarlsonj * trying to obtain a lease. 8683431Scarlsonj * 8693431Scarlsonj * INFORM: set to the number of seconds since we started 8703431Scarlsonj * trying to get configuration parameters. 8713431Scarlsonj * 8723431Scarlsonj * REQUEST: if in the REQUESTING state, then same value as 8733431Scarlsonj * DISCOVER, otherwise the number of seconds 8743431Scarlsonj * since we started trying to obtain a lease. 8753431Scarlsonj * 8763431Scarlsonj * we also set `dsm_newstart_monosec', to the time we sent a 8773431Scarlsonj * REQUEST or DISCOVER packet, so we know the lease start 8783431Scarlsonj * time (the DISCOVER case is for handling BOOTP servers). 8793431Scarlsonj */ 8803431Scarlsonj 8813431Scarlsonj switch (ptype) { 8823431Scarlsonj 8833431Scarlsonj case DISCOVER: 8843431Scarlsonj dsmp->dsm_newstart_monosec = monosec(); 8853431Scarlsonj dsmp->dsm_disc_secs = dsmp->dsm_newstart_monosec - 8863431Scarlsonj hrtime_to_monosec(dsmp->dsm_neg_hrtime); 8873431Scarlsonj dpkt->pkt->secs = htons(dsmp->dsm_disc_secs); 8883431Scarlsonj break; 8893431Scarlsonj 8903431Scarlsonj case INFORM: 8913431Scarlsonj dpkt->pkt->secs = htons(monosec() - 8923431Scarlsonj hrtime_to_monosec(dsmp->dsm_neg_hrtime)); 8933431Scarlsonj break; 8943431Scarlsonj 8953431Scarlsonj case REQUEST: 8963431Scarlsonj dsmp->dsm_newstart_monosec = monosec(); 8973431Scarlsonj 8983431Scarlsonj if (dsmp->dsm_state == REQUESTING) { 8993431Scarlsonj dpkt->pkt->secs = htons(dsmp->dsm_disc_secs); 9003431Scarlsonj break; 9013431Scarlsonj } 9023431Scarlsonj 9033431Scarlsonj dpkt->pkt->secs = htons(monosec() - 9043431Scarlsonj hrtime_to_monosec(dsmp->dsm_neg_hrtime)); 9053431Scarlsonj break; 9063431Scarlsonj 9073431Scarlsonj default: 9083431Scarlsonj dpkt->pkt->secs = htons(0); 9093431Scarlsonj break; 9103431Scarlsonj } 9110Sstevel@tonic-gate } 9120Sstevel@tonic-gate 9133431Scarlsonj if (dpkt->pkt_isv6) { 9143431Scarlsonj struct sockaddr_in6 sin6; 9153431Scarlsonj 9163431Scarlsonj (void) memset(&iov, 0, sizeof (iov)); 9173431Scarlsonj iov.iov_base = dpkt->pkt; 9183431Scarlsonj iov.iov_len = dpkt->pkt_cur_len; 9193431Scarlsonj 9203431Scarlsonj (void) memset(&msg, 0, sizeof (msg)); 9213431Scarlsonj msg.msg_name = &dsmp->dsm_send_dest.v6; 9223431Scarlsonj msg.msg_namelen = sizeof (struct sockaddr_in6); 9233431Scarlsonj msg.msg_iov = &iov; 9243431Scarlsonj msg.msg_iovlen = 1; 9253431Scarlsonj 9263431Scarlsonj /* 9273431Scarlsonj * If the address that's requested cannot be reached, then fall 9283431Scarlsonj * back to the multcast address. 9293431Scarlsonj */ 9303431Scarlsonj if (IN6_IS_ADDR_MULTICAST(&dsmp->dsm_send_dest.v6.sin6_addr)) { 9313431Scarlsonj ismcast = B_TRUE; 9323431Scarlsonj } else { 9333431Scarlsonj struct dstinforeq dinfo; 9343431Scarlsonj struct strioctl str; 9350Sstevel@tonic-gate 9363431Scarlsonj ismcast = B_FALSE; 9373431Scarlsonj (void) memset(&dinfo, 0, sizeof (dinfo)); 9383431Scarlsonj dinfo.dir_daddr = dsmp->dsm_send_dest.v6.sin6_addr; 9393431Scarlsonj str.ic_cmd = SIOCGDSTINFO; 9403431Scarlsonj str.ic_timout = 0; 9413431Scarlsonj str.ic_len = sizeof (dinfo); 9423431Scarlsonj str.ic_dp = (char *)&dinfo; 9433431Scarlsonj if (ioctl(v6_sock_fd, I_STR, &str) == -1) { 9443431Scarlsonj dhcpmsg(MSG_ERR, 9453431Scarlsonj "send_pkt_internal: ioctl SIOCGDSTINFO"); 9463431Scarlsonj } else if (!dinfo.dir_dreachable) { 9473431Scarlsonj char abuf[INET6_ADDRSTRLEN]; 9480Sstevel@tonic-gate 9493431Scarlsonj dhcpmsg(MSG_DEBUG, "send_pkt_internal: %s is " 9503431Scarlsonj "not reachable; using multicast instead", 9513431Scarlsonj inet_ntop(AF_INET6, &dinfo.dir_daddr, abuf, 9523431Scarlsonj sizeof (abuf))); 9533431Scarlsonj sin6 = dsmp->dsm_send_dest.v6; 9543431Scarlsonj sin6.sin6_addr = 9553431Scarlsonj ipv6_all_dhcp_relay_and_servers; 9563431Scarlsonj msg.msg_name = &sin6; 9573431Scarlsonj ismcast = B_TRUE; 9583431Scarlsonj } 9590Sstevel@tonic-gate } 9600Sstevel@tonic-gate 9613431Scarlsonj /* 9623431Scarlsonj * Make room for our ancillary data option as well as a dummy 9633431Scarlsonj * option used by CMSG_NXTHDR. 9643431Scarlsonj */ 9653431Scarlsonj msg.msg_controllen = sizeof (*cmsg) + _MAX_ALIGNMENT + 9663431Scarlsonj sizeof (*ipi6) + _MAX_ALIGNMENT + sizeof (*cmsg); 9673431Scarlsonj msg.msg_control = alloca(msg.msg_controllen); 9683431Scarlsonj cmsg = CMSG_FIRSTHDR(&msg); 9693431Scarlsonj cmsg->cmsg_level = IPPROTO_IPV6; 9703431Scarlsonj cmsg->cmsg_type = IPV6_PKTINFO; 9713431Scarlsonj /* LINTED: alignment */ 9723431Scarlsonj ipi6 = (struct in6_pktinfo *)CMSG_DATA(cmsg); 9733431Scarlsonj if (ismcast) 9743431Scarlsonj ipi6->ipi6_addr = lif->lif_v6addr; 9753431Scarlsonj else 9763431Scarlsonj ipi6->ipi6_addr = my_in6addr_any; 9773431Scarlsonj ipi6->ipi6_ifindex = lif->lif_pif->pif_index; 9783431Scarlsonj cmsg->cmsg_len = (char *)(ipi6 + 1) - (char *)cmsg; 9790Sstevel@tonic-gate 9803431Scarlsonj /* 9813431Scarlsonj * Now correct the control message length. 9823431Scarlsonj */ 9833431Scarlsonj cmsg = CMSG_NXTHDR(&msg, cmsg); 9843431Scarlsonj msg.msg_controllen = (char *)cmsg - (char *)msg.msg_control; 9850Sstevel@tonic-gate 9863431Scarlsonj n_bytes = sendmsg(v6_sock_fd, &msg, 0); 9873431Scarlsonj } else { 9883431Scarlsonj if (dsmp->dsm_using_dlpi) { 9894456Sss150715 n_bytes = dlpi_sendto(pif->pif_dlpi_hd, dpkt->pkt, 9903431Scarlsonj dpkt->pkt_cur_len, &dsmp->dsm_send_dest.v4, 9913431Scarlsonj pif->pif_daddr, pif->pif_dlen); 9923431Scarlsonj /* dlpi_sendto calls putmsg */ 9933431Scarlsonj if (n_bytes == 0) 9943431Scarlsonj n_bytes = dpkt->pkt_cur_len; 9953431Scarlsonj } else { 9963431Scarlsonj n_bytes = sendto(lif->lif_sock_ip_fd, dpkt->pkt, 9973431Scarlsonj dpkt->pkt_cur_len, 0, 9983431Scarlsonj (struct sockaddr *)&dsmp->dsm_send_dest.v4, 9993431Scarlsonj sizeof (struct sockaddr_in)); 10003431Scarlsonj } 10010Sstevel@tonic-gate } 10020Sstevel@tonic-gate 10030Sstevel@tonic-gate if (n_bytes != dpkt->pkt_cur_len) { 10043431Scarlsonj 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)", 10103431Scarlsonj pkt_name, dsmp->dsm_send_timeout / MILLISEC); 10113431Scarlsonj return (B_FALSE); 10120Sstevel@tonic-gate } 10130Sstevel@tonic-gate 10143431Scarlsonj dhcpmsg(MSG_VERBOSE, "sent %s xid %x packet out %s", pkt_name, 10153431Scarlsonj pkt_get_xid(dpkt->pkt, dpkt->pkt_isv6), dsmp->dsm_name); 10160Sstevel@tonic-gate 10173431Scarlsonj dsmp->dsm_packet_sent++; 10183431Scarlsonj dsmp->dsm_sent++; 10193431Scarlsonj return (B_TRUE); 10200Sstevel@tonic-gate } 10210Sstevel@tonic-gate 10220Sstevel@tonic-gate /* 10233431Scarlsonj * send_pkt(): sends a packet out 10240Sstevel@tonic-gate * 10253431Scarlsonj * 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) 10313431Scarlsonj * output: boolean_t: B_TRUE if the packet was sent, B_FALSE otherwise 10320Sstevel@tonic-gate */ 10330Sstevel@tonic-gate 10343431Scarlsonj boolean_t 10353431Scarlsonj 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 10453431Scarlsonj dsmp->dsm_packet_sent = 0; 10460Sstevel@tonic-gate 10473431Scarlsonj (void) memset(&dsmp->dsm_send_dest.v4, 0, 10483431Scarlsonj sizeof (dsmp->dsm_send_dest.v4)); 10493431Scarlsonj dsmp->dsm_send_dest.v4.sin_addr.s_addr = dest; 10503431Scarlsonj dsmp->dsm_send_dest.v4.sin_family = AF_INET; 10513431Scarlsonj dsmp->dsm_send_dest.v4.sin_port = htons(IPPORT_BOOTPS); 10523431Scarlsonj 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 10593431Scarlsonj 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 10663431Scarlsonj free_pkt_list(&dsmp->dsm_recv_pkt_list); 10673431Scarlsonj 10683431Scarlsonj if (stop == NULL) 10693431Scarlsonj dsmp->dsm_send_timeout = 0; /* prevents retransmissions */ 10703431Scarlsonj else 10713431Scarlsonj next_retransmission(dsmp, B_TRUE, B_FALSE); 10723431Scarlsonj 10733431Scarlsonj return (send_pkt_internal(dsmp)); 10743431Scarlsonj } 10753431Scarlsonj 10763431Scarlsonj /* 10773431Scarlsonj * send_pkt_v6(): sends a DHCPv6 packet out 10783431Scarlsonj * 10793431Scarlsonj * input: dhcp_smach_t *: the state machine sending the packet 10803431Scarlsonj * dhcp_pkt_t *: the packet to send out 10813431Scarlsonj * in6_addr_t: the destination IPv6 address for the packet 10823431Scarlsonj * stop_func_t *: a pointer to function to indicate when to stop 10833431Scarlsonj * retransmitting the packet (if NULL, packet is 10843431Scarlsonj * not retransmitted) 10853431Scarlsonj * uint_t: Initial Retransmit Timer value 10863431Scarlsonj * uint_t: Maximum Retransmit Timer value, zero if none 10873431Scarlsonj * output: boolean_t: B_TRUE if the packet was sent, B_FALSE otherwise 10883431Scarlsonj */ 10893431Scarlsonj 10903431Scarlsonj boolean_t 10913431Scarlsonj send_pkt_v6(dhcp_smach_t *dsmp, dhcp_pkt_t *dpkt, in6_addr_t dest, 10923431Scarlsonj stop_func_t *stop, uint_t irt, uint_t mrt) 10933431Scarlsonj { 10943431Scarlsonj dsmp->dsm_packet_sent = 0; 10953431Scarlsonj 10963431Scarlsonj (void) memset(&dsmp->dsm_send_dest.v6, 0, 10973431Scarlsonj sizeof (dsmp->dsm_send_dest.v6)); 10983431Scarlsonj dsmp->dsm_send_dest.v6.sin6_addr = dest; 10993431Scarlsonj dsmp->dsm_send_dest.v6.sin6_family = AF_INET6; 11003431Scarlsonj dsmp->dsm_send_dest.v6.sin6_port = htons(IPPORT_DHCPV6S); 11013431Scarlsonj dsmp->dsm_send_stop_func = stop; 11023431Scarlsonj 11033431Scarlsonj /* 11043431Scarlsonj * TODO: dispose of this gruesome assumption (there's no real 11053431Scarlsonj * technical gain from doing so, but it would be cleaner) 11063431Scarlsonj */ 11073431Scarlsonj 11083431Scarlsonj assert(dpkt == &dsmp->dsm_send_pkt); 11093431Scarlsonj 11103431Scarlsonj /* 11113431Scarlsonj * clear out any packets which had been previously received 11123431Scarlsonj * but not pulled off of the recv_packet queue. 11133431Scarlsonj */ 11143431Scarlsonj 11153431Scarlsonj free_pkt_list(&dsmp->dsm_recv_pkt_list); 11160Sstevel@tonic-gate 11170Sstevel@tonic-gate if (stop == NULL) { 11183431Scarlsonj dsmp->dsm_send_timeout = 0; /* prevents retransmissions */ 11193431Scarlsonj } else { 11203431Scarlsonj dsmp->dsm_send_timeout = irt; 11213431Scarlsonj dsmp->dsm_send_tcenter = mrt; 11223431Scarlsonj /* 11233431Scarlsonj * This is quite ugly, but RFC 3315 section 17.1.2 requires 11243431Scarlsonj * that the RAND value for the very first retransmission of a 11253431Scarlsonj * Solicit message is strictly greater than zero. 11263431Scarlsonj */ 11273431Scarlsonj next_retransmission(dsmp, B_TRUE, 11283431Scarlsonj pkt_send_type(dpkt) == DHCPV6_MSG_SOLICIT); 11293431Scarlsonj } 11300Sstevel@tonic-gate 11313431Scarlsonj 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 11383431Scarlsonj * 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 { 11463431Scarlsonj dhcp_smach_t *dsmp = arg; 11470Sstevel@tonic-gate 11483431Scarlsonj dsmp->dsm_retrans_timer = -1; 11493431Scarlsonj 11503431Scarlsonj if (!verify_smach(dsmp)) 11510Sstevel@tonic-gate return; 11520Sstevel@tonic-gate 11530Sstevel@tonic-gate /* 11543431Scarlsonj * Check the callback to see if we should keep sending retransmissions. 11553431Scarlsonj * Compute the next retransmission time first, so that the callback can 11563431Scarlsonj * cap the value if need be. (Required for DHCPv6 Confirm messages.) 11573431Scarlsonj * 11583431Scarlsonj * Hold the state machine across the callback so that the called 11593431Scarlsonj * function can remove the state machine from the system without 11603431Scarlsonj * disturbing the string used subsequently for verbose logging. The 11613431Scarlsonj * Release function destroys the state machine when the retry count 11623431Scarlsonj * expires. 11630Sstevel@tonic-gate */ 11640Sstevel@tonic-gate 11653431Scarlsonj next_retransmission(dsmp, B_FALSE, B_FALSE); 11663431Scarlsonj hold_smach(dsmp); 11673431Scarlsonj if (dsmp->dsm_send_stop_func(dsmp, dsmp->dsm_packet_sent)) { 11683431Scarlsonj dhcpmsg(MSG_VERBOSE, "retransmit: time to stop on %s", 11693431Scarlsonj dsmp->dsm_name); 11703431Scarlsonj } else { 11713431Scarlsonj dhcpmsg(MSG_VERBOSE, "retransmit: sending another on %s", 11723431Scarlsonj dsmp->dsm_name); 11733431Scarlsonj (void) send_pkt_internal(dsmp); 11743431Scarlsonj } 11753431Scarlsonj 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 * 11813431Scarlsonj * 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 11863431Scarlsonj stop_pkt_retransmission(dhcp_smach_t *dsmp) 11870Sstevel@tonic-gate { 11883431Scarlsonj if (dsmp->dsm_retrans_timer != -1 && 11893431Scarlsonj iu_cancel_timer(tq, dsmp->dsm_retrans_timer, NULL) == 1) { 11903431Scarlsonj dhcpmsg(MSG_VERBOSE, "stop_pkt_retransmission: stopped on %s", 11913431Scarlsonj dsmp->dsm_name); 11923431Scarlsonj dsmp->dsm_retrans_timer = -1; 11933431Scarlsonj release_smach(dsmp); 11940Sstevel@tonic-gate } 11950Sstevel@tonic-gate } 11960Sstevel@tonic-gate 11970Sstevel@tonic-gate /* 11983431Scarlsonj * retransmit_now(): force a packet retransmission right now. Used only with 11993431Scarlsonj * the DHCPv6 UseMulticast status code. Use with caution; 12003431Scarlsonj * triggered retransmissions can cause packet storms. 12010Sstevel@tonic-gate * 12023431Scarlsonj * input: dhcp_smach_t *: the state machine to force retransmission on 12033431Scarlsonj * output: void 12040Sstevel@tonic-gate */ 12050Sstevel@tonic-gate 12063431Scarlsonj void 12073431Scarlsonj retransmit_now(dhcp_smach_t *dsmp) 12083431Scarlsonj { 12093431Scarlsonj stop_pkt_retransmission(dsmp); 12103431Scarlsonj (void) send_pkt_internal(dsmp); 12113431Scarlsonj } 12123431Scarlsonj 12133431Scarlsonj /* 12143431Scarlsonj * alloc_pkt_entry(): Allocates a packet list entry with a given data area 12153431Scarlsonj * size. 12163431Scarlsonj * 12173431Scarlsonj * input: size_t: size of data area for packet 12183431Scarlsonj * boolean_t: B_TRUE for IPv6 12193431Scarlsonj * output: PKT_LIST *: allocated packet list entry 12203431Scarlsonj */ 12213431Scarlsonj 12223431Scarlsonj PKT_LIST * 12233431Scarlsonj alloc_pkt_entry(size_t psize, boolean_t isv6) 12240Sstevel@tonic-gate { 12250Sstevel@tonic-gate PKT_LIST *plp; 12260Sstevel@tonic-gate 12273431Scarlsonj if ((plp = calloc(1, sizeof (*plp))) == NULL || 12283431Scarlsonj (plp->pkt = malloc(psize)) == NULL) { 12293431Scarlsonj free(plp); 12303431Scarlsonj plp = NULL; 12313431Scarlsonj } else { 12323431Scarlsonj plp->len = psize; 12333431Scarlsonj plp->isv6 = isv6; 12340Sstevel@tonic-gate } 12350Sstevel@tonic-gate 12363431Scarlsonj return (plp); 12373431Scarlsonj } 12380Sstevel@tonic-gate 12393431Scarlsonj /* 12403431Scarlsonj * sock_recvpkt(): read from the given socket into an allocated buffer and 12413431Scarlsonj * handles any ancillary data options. 12423431Scarlsonj * 12433431Scarlsonj * input: int: file descriptor to read 12443431Scarlsonj * PKT_LIST *: allocated buffer 12453431Scarlsonj * output: ssize_t: number of bytes read, or -1 on error 12463431Scarlsonj */ 12473431Scarlsonj 12483431Scarlsonj static ssize_t 12493431Scarlsonj sock_recvpkt(int fd, PKT_LIST *plp) 12503431Scarlsonj { 12513431Scarlsonj struct iovec iov; 12523431Scarlsonj struct msghdr msg; 12533476Scarlsonj int64_t ctrl[8192 / sizeof (int64_t)]; 12543431Scarlsonj ssize_t msglen; 12550Sstevel@tonic-gate 12563431Scarlsonj (void) memset(&iov, 0, sizeof (iov)); 12573431Scarlsonj iov.iov_base = (caddr_t)plp->pkt; 12583431Scarlsonj iov.iov_len = plp->len; 12593431Scarlsonj 12603431Scarlsonj (void) memset(&msg, 0, sizeof (msg)); 12613431Scarlsonj msg.msg_name = &plp->pktfrom; 12623431Scarlsonj msg.msg_namelen = sizeof (plp->pktfrom); 12633431Scarlsonj msg.msg_iov = &iov; 12643431Scarlsonj msg.msg_iovlen = 1; 12653431Scarlsonj msg.msg_control = ctrl; 12663431Scarlsonj msg.msg_controllen = sizeof (ctrl); 12673431Scarlsonj 12683431Scarlsonj if ((msglen = recvmsg(fd, &msg, 0)) != -1) { 12693431Scarlsonj struct cmsghdr *cmsg; 12703431Scarlsonj 12713431Scarlsonj for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; 12723431Scarlsonj cmsg = CMSG_NXTHDR(&msg, cmsg)) { 12733431Scarlsonj struct sockaddr_in *sinp; 12743431Scarlsonj struct sockaddr_in6 *sin6; 12753431Scarlsonj struct in6_pktinfo *ipi6; 12760Sstevel@tonic-gate 12773431Scarlsonj switch (cmsg->cmsg_level) { 12783431Scarlsonj case IPPROTO_IP: 12793431Scarlsonj switch (cmsg->cmsg_type) { 12803431Scarlsonj case IP_RECVDSTADDR: 12813431Scarlsonj sinp = (struct sockaddr_in *) 12823431Scarlsonj &plp->pktto; 12833431Scarlsonj sinp->sin_family = AF_INET; 12843431Scarlsonj (void) memcpy(&sinp->sin_addr.s_addr, 12853431Scarlsonj CMSG_DATA(cmsg), 12863431Scarlsonj sizeof (ipaddr_t)); 12873431Scarlsonj break; 12883431Scarlsonj 12893431Scarlsonj case IP_RECVIF: 12903431Scarlsonj (void) memcpy(&plp->ifindex, 12913431Scarlsonj CMSG_DATA(cmsg), sizeof (uint_t)); 12923431Scarlsonj break; 12933431Scarlsonj } 12943431Scarlsonj break; 12950Sstevel@tonic-gate 12963431Scarlsonj case IPPROTO_IPV6: 12973431Scarlsonj switch (cmsg->cmsg_type) { 12983431Scarlsonj case IPV6_PKTINFO: 12993431Scarlsonj /* LINTED: alignment */ 13003431Scarlsonj ipi6 = (struct in6_pktinfo *) 13013431Scarlsonj CMSG_DATA(cmsg); 13023431Scarlsonj sin6 = (struct sockaddr_in6 *) 13033431Scarlsonj &plp->pktto; 13043431Scarlsonj sin6->sin6_family = AF_INET6; 13053431Scarlsonj (void) memcpy(&sin6->sin6_addr, 13063431Scarlsonj &ipi6->ipi6_addr, 13073431Scarlsonj sizeof (ipi6->ipi6_addr)); 13083431Scarlsonj (void) memcpy(&plp->ifindex, 13093431Scarlsonj &ipi6->ipi6_ifindex, 13103431Scarlsonj sizeof (uint_t)); 13113431Scarlsonj break; 13123431Scarlsonj } 13133431Scarlsonj } 13143431Scarlsonj } 13153431Scarlsonj } 13163431Scarlsonj return (msglen); 13173431Scarlsonj } 13180Sstevel@tonic-gate 13193431Scarlsonj /* 13203431Scarlsonj * recv_pkt(): receives a single DHCP packet on a given file descriptor. 13213431Scarlsonj * 13224456Sss150715 * input: int: if not using dlpi, the file descriptor to receive the packet 13233431Scarlsonj * int: the maximum packet size to allow 13243431Scarlsonj * boolean_t: B_TRUE for IPv6 13253431Scarlsonj * boolean_t: B_TRUE if using DLPI 13264456Sss150715 * void *: if using DLPI, structure that has DLPI handle 13273431Scarlsonj * output: PKT_LIST *: the received packet 13283431Scarlsonj */ 13290Sstevel@tonic-gate 13303431Scarlsonj PKT_LIST * 13314456Sss150715 recv_pkt(int fd, int mtu, boolean_t isv6, boolean_t isdlpi, dhcp_pif_t *arg) 13323431Scarlsonj { 13333431Scarlsonj PKT_LIST *plp; 13343431Scarlsonj ssize_t retval; 13353431Scarlsonj 13363431Scarlsonj if ((plp = alloc_pkt_entry(mtu, isv6)) == NULL) { 13373431Scarlsonj dhcpmsg(MSG_ERROR, 13383431Scarlsonj "recv_pkt: allocation failure; dropped packet"); 13393431Scarlsonj return (NULL); 13400Sstevel@tonic-gate } 13410Sstevel@tonic-gate 13423431Scarlsonj if (isv6) { 13433431Scarlsonj retval = sock_recvpkt(fd, plp); 13443431Scarlsonj 13453431Scarlsonj if (retval == -1) { 13463431Scarlsonj dhcpmsg(MSG_ERR, 13473431Scarlsonj "recv_pkt: recvfrom v6 failed, dropped"); 13483431Scarlsonj goto failure; 13493431Scarlsonj } 13503431Scarlsonj 13513431Scarlsonj plp->len = retval; 13520Sstevel@tonic-gate 13533431Scarlsonj if (retval < sizeof (dhcpv6_message_t)) { 13543431Scarlsonj dhcpmsg(MSG_WARNING, "recv_pkt: runt message"); 13553431Scarlsonj goto failure; 13563431Scarlsonj } 13573431Scarlsonj } else { 13583431Scarlsonj if (isdlpi) { 13594456Sss150715 dhcp_pif_t *pif = arg; 13604456Sss150715 13614456Sss150715 retval = dlpi_recvfrom(pif->pif_dlpi_hd, plp->pkt, mtu, 13623431Scarlsonj (struct sockaddr_in *)&plp->pktfrom, 13633431Scarlsonj (struct sockaddr_in *)&plp->pktto); 13643431Scarlsonj } else { 13653431Scarlsonj retval = sock_recvpkt(fd, plp); 13663431Scarlsonj } 13670Sstevel@tonic-gate 13683431Scarlsonj if (retval == -1) { 13693431Scarlsonj dhcpmsg(MSG_ERR, 13703431Scarlsonj "recv_pkt: %srecvfrom v4 failed, dropped", 13713431Scarlsonj isdlpi ? "dlpi_" : ""); 13723431Scarlsonj goto failure; 13733431Scarlsonj } 13743431Scarlsonj 13753431Scarlsonj plp->len = retval; 13763431Scarlsonj 13773431Scarlsonj switch (dhcp_options_scan(plp, B_TRUE)) { 13783431Scarlsonj 13793431Scarlsonj case DHCP_WRONG_MSG_TYPE: 13803431Scarlsonj dhcpmsg(MSG_WARNING, 13813431Scarlsonj "recv_pkt: unexpected DHCP message"); 13823431Scarlsonj goto failure; 13830Sstevel@tonic-gate 13843431Scarlsonj case DHCP_GARBLED_MSG_TYPE: 13853431Scarlsonj dhcpmsg(MSG_WARNING, 13863431Scarlsonj "recv_pkt: garbled DHCP message type"); 13873431Scarlsonj goto failure; 13880Sstevel@tonic-gate 13893431Scarlsonj case DHCP_BAD_OPT_OVLD: 13903431Scarlsonj dhcpmsg(MSG_WARNING, "recv_pkt: bad option overload"); 13913431Scarlsonj goto failure; 13923431Scarlsonj 13933431Scarlsonj case 0: 13943431Scarlsonj break; 13953431Scarlsonj 13963431Scarlsonj default: 13973431Scarlsonj dhcpmsg(MSG_WARNING, 13983431Scarlsonj "recv_pkt: packet corrupted, dropped"); 13990Sstevel@tonic-gate goto failure; 14000Sstevel@tonic-gate } 14010Sstevel@tonic-gate } 14023431Scarlsonj return (plp); 14030Sstevel@tonic-gate 14040Sstevel@tonic-gate failure: 14053431Scarlsonj free_pkt_entry(plp); 14063431Scarlsonj return (NULL); 14073431Scarlsonj } 14083431Scarlsonj 14093431Scarlsonj /* 14103431Scarlsonj * pkt_v4_match(): check if a given DHCPv4 message type is in a given set 14113431Scarlsonj * 14123431Scarlsonj * input: uchar_t: packet type 14133431Scarlsonj * dhcp_message_type_t: bit-wise OR of DHCP_P* values. 14143431Scarlsonj * output: boolean_t: B_TRUE if packet type is in the set 14153431Scarlsonj */ 14163431Scarlsonj 14173431Scarlsonj boolean_t 14183431Scarlsonj pkt_v4_match(uchar_t type, dhcp_message_type_t match_type) 14193431Scarlsonj { 14203431Scarlsonj /* 14213431Scarlsonj * note: the ordering here allows direct indexing of the table 14223431Scarlsonj * based on the RFC2131 packet type value passed in. 14233431Scarlsonj */ 14243431Scarlsonj 14253431Scarlsonj static dhcp_message_type_t type_map[] = { 14263431Scarlsonj DHCP_PUNTYPED, DHCP_PDISCOVER, DHCP_POFFER, DHCP_PREQUEST, 14273431Scarlsonj DHCP_PDECLINE, DHCP_PACK, DHCP_PNAK, DHCP_PRELEASE, 14283431Scarlsonj DHCP_PINFORM 14293431Scarlsonj }; 14303431Scarlsonj 14313431Scarlsonj if (type < (sizeof (type_map) / sizeof (*type_map))) 14323431Scarlsonj return ((type_map[type] & match_type) ? B_TRUE : B_FALSE); 14333431Scarlsonj else 14343431Scarlsonj return (B_FALSE); 14353431Scarlsonj } 14363431Scarlsonj 14373431Scarlsonj /* 14383431Scarlsonj * pkt_smach_enqueue(): enqueue a packet on a given state machine 14393431Scarlsonj * 14403431Scarlsonj * input: dhcp_smach_t: state machine 14413431Scarlsonj * PKT_LIST *: packet to enqueue 14423431Scarlsonj * output: none 14433431Scarlsonj */ 14443431Scarlsonj 14453431Scarlsonj void 14463431Scarlsonj pkt_smach_enqueue(dhcp_smach_t *dsmp, PKT_LIST *plp) 14473431Scarlsonj { 14483431Scarlsonj dhcpmsg(MSG_VERBOSE, "pkt_smach_enqueue: received %s %s packet on %s", 14493431Scarlsonj pkt_type_to_string(pkt_recv_type(plp), dsmp->dsm_isv6), 14503431Scarlsonj dsmp->dsm_isv6 ? "v6" : "v4", dsmp->dsm_name); 14513431Scarlsonj 14523431Scarlsonj /* add to front of list */ 14533431Scarlsonj insque(plp, &dsmp->dsm_recv_pkt_list); 14540Sstevel@tonic-gate } 14550Sstevel@tonic-gate 14560Sstevel@tonic-gate /* 14573431Scarlsonj * next_retransmission(): computes the number of seconds until the next 14583431Scarlsonj * retransmission, based on the algorithms in RFCs 2131 14593431Scarlsonj * 3315. 14600Sstevel@tonic-gate * 14613431Scarlsonj * input: dhcp_smach_t *: state machine that needs a new timer 14623431Scarlsonj * boolean_t: B_TRUE if this is the first time sending the message 14633431Scarlsonj * boolean_t: B_TRUE for positive RAND values only (RFC 3315 17.1.2) 14643431Scarlsonj * output: none 14650Sstevel@tonic-gate */ 14660Sstevel@tonic-gate 14673431Scarlsonj static void 14683431Scarlsonj next_retransmission(dhcp_smach_t *dsmp, boolean_t first_send, 14693431Scarlsonj boolean_t positive_only) 14700Sstevel@tonic-gate { 14713431Scarlsonj uint32_t timeout_ms; 14723431Scarlsonj 14733431Scarlsonj if (dsmp->dsm_isv6) { 14743431Scarlsonj double randval; 14753431Scarlsonj 14763431Scarlsonj /* 14773431Scarlsonj * The RFC specifies 0 to 10% jitter for the initial 14783431Scarlsonj * solicitation, and plus or minus 10% jitter for all others. 14793431Scarlsonj * This works out to 100 milliseconds on the shortest timer we 14803431Scarlsonj * use. 14813431Scarlsonj */ 14823431Scarlsonj if (positive_only) 14833431Scarlsonj randval = drand48() / 10.0; 14843431Scarlsonj else 14853431Scarlsonj randval = (drand48() - 0.5) / 5.0; 14863431Scarlsonj 14873431Scarlsonj /* The RFC specifies doubling *after* the first transmission */ 14883431Scarlsonj timeout_ms = dsmp->dsm_send_timeout; 14893431Scarlsonj if (!first_send) 14903431Scarlsonj timeout_ms *= 2; 14913431Scarlsonj timeout_ms += (int)(randval * dsmp->dsm_send_timeout); 14923431Scarlsonj 14933431Scarlsonj /* This checks the MRT (maximum retransmission time) */ 14943431Scarlsonj if (dsmp->dsm_send_tcenter != 0 && 14953431Scarlsonj timeout_ms > dsmp->dsm_send_tcenter) { 14963431Scarlsonj timeout_ms = dsmp->dsm_send_tcenter + 14973431Scarlsonj (uint_t)(randval * dsmp->dsm_send_tcenter); 14983431Scarlsonj } 14993431Scarlsonj 15003431Scarlsonj dsmp->dsm_send_timeout = timeout_ms; 15013431Scarlsonj } else { 15023431Scarlsonj if (dsmp->dsm_state == RENEWING || 15033431Scarlsonj dsmp->dsm_state == REBINDING) { 15043431Scarlsonj monosec_t mono; 15053431Scarlsonj 15063431Scarlsonj timeout_ms = dsmp->dsm_state == RENEWING ? 15073431Scarlsonj dsmp->dsm_leases->dl_t2.dt_start : 15083431Scarlsonj dsmp->dsm_leases->dl_lifs->lif_expire.dt_start; 15093431Scarlsonj timeout_ms += dsmp->dsm_curstart_monosec; 15103431Scarlsonj mono = monosec(); 15113431Scarlsonj if (mono > timeout_ms) 15123431Scarlsonj timeout_ms = 0; 15133431Scarlsonj else 15143431Scarlsonj timeout_ms -= mono; 15153431Scarlsonj timeout_ms *= MILLISEC / 2; 15163431Scarlsonj } else { 15173431Scarlsonj /* 15183431Scarlsonj * Start at 4, and increase by a factor of 2 up to 64. 15193431Scarlsonj */ 15203431Scarlsonj if (first_send) { 15213431Scarlsonj timeout_ms = 4 * MILLISEC; 15223431Scarlsonj } else { 15233431Scarlsonj timeout_ms = MIN(dsmp->dsm_send_tcenter << 1, 15243431Scarlsonj 64 * MILLISEC); 15253431Scarlsonj } 15263431Scarlsonj } 15273431Scarlsonj 15283431Scarlsonj dsmp->dsm_send_tcenter = timeout_ms; 15293431Scarlsonj 15303431Scarlsonj /* 15313431Scarlsonj * At each iteration, jitter the timeout by some fraction of a 15323431Scarlsonj * second. 15333431Scarlsonj */ 15343431Scarlsonj dsmp->dsm_send_timeout = timeout_ms + 15353431Scarlsonj ((lrand48() % (2 * MILLISEC)) - MILLISEC); 15363431Scarlsonj } 15373431Scarlsonj } 15380Sstevel@tonic-gate 15393431Scarlsonj /* 15403431Scarlsonj * dhcp_ip_default(): open and bind the default IP sockets used for I/O and 15413431Scarlsonj * interface control. 15423431Scarlsonj * 15433431Scarlsonj * input: none 15443431Scarlsonj * output: B_TRUE on success 15453431Scarlsonj */ 15463431Scarlsonj 15473431Scarlsonj boolean_t 15483431Scarlsonj dhcp_ip_default(void) 15493431Scarlsonj { 1550*5123Scarlsonj int on = 1; 15513431Scarlsonj 15523431Scarlsonj if ((v4_sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { 15533431Scarlsonj dhcpmsg(MSG_ERR, 15543431Scarlsonj "dhcp_ip_default: unable to create IPv4 socket"); 15553431Scarlsonj return (B_FALSE); 15563431Scarlsonj } 15573431Scarlsonj 15583431Scarlsonj if (setsockopt(v4_sock_fd, IPPROTO_IP, IP_RECVDSTADDR, &on, 15593431Scarlsonj sizeof (on)) == -1) { 15603431Scarlsonj dhcpmsg(MSG_ERR, 15613431Scarlsonj "dhcp_ip_default: unable to enable IP_RECVDSTADDR"); 15623431Scarlsonj return (B_FALSE); 15633431Scarlsonj } 15643431Scarlsonj 15653431Scarlsonj if (setsockopt(v4_sock_fd, IPPROTO_IP, IP_RECVIF, &on, sizeof (on)) == 15663431Scarlsonj -1) { 15673431Scarlsonj dhcpmsg(MSG_ERR, 15683431Scarlsonj "dhcp_ip_default: unable to enable IP_RECVIF"); 15693431Scarlsonj return (B_FALSE); 15703431Scarlsonj } 15710Sstevel@tonic-gate 15723431Scarlsonj if (!bind_sock(v4_sock_fd, IPPORT_BOOTPC, INADDR_ANY)) { 15733431Scarlsonj dhcpmsg(MSG_ERROR, 15743431Scarlsonj "dhcp_ip_default: unable to bind IPv4 socket to port %d", 15753431Scarlsonj IPPORT_BOOTPC); 15763431Scarlsonj return (B_FALSE); 15773431Scarlsonj } 15783431Scarlsonj 15793431Scarlsonj if (iu_register_event(eh, v4_sock_fd, POLLIN, dhcp_acknak_common, 15803431Scarlsonj NULL) == -1) { 15813431Scarlsonj dhcpmsg(MSG_WARNING, "dhcp_ip_default: cannot register to " 15823431Scarlsonj "receive IPv4 broadcasts"); 15833431Scarlsonj return (B_FALSE); 15843431Scarlsonj } 15853431Scarlsonj 15863431Scarlsonj if ((v6_sock_fd = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) { 15873431Scarlsonj dhcpmsg(MSG_ERR, 15883431Scarlsonj "dhcp_ip_default: unable to create IPv6 socket"); 15893431Scarlsonj return (B_FALSE); 15903431Scarlsonj } 15913431Scarlsonj 15923431Scarlsonj if (setsockopt(v6_sock_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, 15933431Scarlsonj sizeof (on)) == -1) { 15943431Scarlsonj dhcpmsg(MSG_ERR, 15953431Scarlsonj "dhcp_ip_default: unable to enable IPV6_RECVPKTINFO"); 15963431Scarlsonj return (B_FALSE); 15973431Scarlsonj } 15983431Scarlsonj 15993431Scarlsonj if (!bind_sock_v6(v6_sock_fd, IPPORT_DHCPV6C, NULL)) { 16003431Scarlsonj dhcpmsg(MSG_ERROR, 16013431Scarlsonj "dhcp_ip_default: unable to bind IPv6 socket to port %d", 16023431Scarlsonj IPPORT_DHCPV6C); 16033431Scarlsonj return (B_FALSE); 16043431Scarlsonj } 16053431Scarlsonj 16063431Scarlsonj if (iu_register_event(eh, v6_sock_fd, POLLIN, dhcp_acknak_common, 16073431Scarlsonj NULL) == -1) { 16083431Scarlsonj dhcpmsg(MSG_WARNING, "dhcp_ip_default: cannot register to " 16093431Scarlsonj "receive IPv6 packets"); 16103431Scarlsonj return (B_FALSE); 16113431Scarlsonj } 16123431Scarlsonj 16133431Scarlsonj return (B_TRUE); 16140Sstevel@tonic-gate } 1615