125ec3e3dSEric Cheng /* 225ec3e3dSEric Cheng * CDDL HEADER START 325ec3e3dSEric Cheng * 425ec3e3dSEric Cheng * The contents of this file are subject to the terms of the 525ec3e3dSEric Cheng * Common Development and Distribution License (the "License"). 625ec3e3dSEric Cheng * You may not use this file except in compliance with the License. 725ec3e3dSEric Cheng * 825ec3e3dSEric Cheng * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 925ec3e3dSEric Cheng * or http://www.opensolaris.org/os/licensing. 1025ec3e3dSEric Cheng * See the License for the specific language governing permissions 1125ec3e3dSEric Cheng * and limitations under the License. 1225ec3e3dSEric Cheng * 1325ec3e3dSEric Cheng * When distributing Covered Code, include this CDDL HEADER in each 1425ec3e3dSEric Cheng * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1525ec3e3dSEric Cheng * If applicable, add the following below this CDDL HEADER, with the 1625ec3e3dSEric Cheng * fields enclosed by brackets "[]" replaced with your own identifying 1725ec3e3dSEric Cheng * information: Portions Copyright [yyyy] [name of copyright owner] 1825ec3e3dSEric Cheng * 1925ec3e3dSEric Cheng * CDDL HEADER END 2025ec3e3dSEric Cheng */ 2125ec3e3dSEric Cheng 2225ec3e3dSEric Cheng /* 23550b6e40SSowmini Varadhan * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 24146e34b6SCody Peter Mello * Copyright (c) 2015, Joyent, Inc. All rights reserved. 2525ec3e3dSEric Cheng */ 264d6a58d3SJosef 'Jeff' Sipek /* 274d6a58d3SJosef 'Jeff' Sipek * Copyright 2014 Nexenta Systems, Inc. All rights reserved. 284d6a58d3SJosef 'Jeff' Sipek */ 2925ec3e3dSEric Cheng 30*4e6f6c83SCody Peter Mello #include <sys/cmn_err.h> 3125ec3e3dSEric Cheng #include <sys/strsun.h> 3225ec3e3dSEric Cheng #include <sys/sdt.h> 3325ec3e3dSEric Cheng #include <sys/mac.h> 3425ec3e3dSEric Cheng #include <sys/mac_impl.h> 3525ec3e3dSEric Cheng #include <sys/mac_client_impl.h> 3625ec3e3dSEric Cheng #include <sys/mac_client_priv.h> 3725ec3e3dSEric Cheng #include <sys/ethernet.h> 3825ec3e3dSEric Cheng #include <sys/vlan.h> 3925ec3e3dSEric Cheng #include <sys/dlpi.h> 400dc2366fSVenugopal Iyer #include <sys/avl.h> 4125ec3e3dSEric Cheng #include <inet/ip.h> 4225ec3e3dSEric Cheng #include <inet/ip6.h> 4325ec3e3dSEric Cheng #include <inet/arp.h> 440dc2366fSVenugopal Iyer #include <netinet/arp.h> 450dc2366fSVenugopal Iyer #include <netinet/udp.h> 460dc2366fSVenugopal Iyer #include <netinet/dhcp.h> 470dc2366fSVenugopal Iyer #include <netinet/dhcp6.h> 4825ec3e3dSEric Cheng 4925ec3e3dSEric Cheng /* 500dc2366fSVenugopal Iyer * Implementation overview for DHCP address detection 510dc2366fSVenugopal Iyer * 520dc2366fSVenugopal Iyer * The purpose of DHCP address detection is to relieve the user of having to 530dc2366fSVenugopal Iyer * manually configure static IP addresses when ip-nospoof protection is turned 540dc2366fSVenugopal Iyer * on. To achieve this, the mac layer needs to intercept DHCP packets to 550dc2366fSVenugopal Iyer * determine the assigned IP addresses. 560dc2366fSVenugopal Iyer * 570dc2366fSVenugopal Iyer * A DHCP handshake between client and server typically requires at least 580dc2366fSVenugopal Iyer * 4 messages: 590dc2366fSVenugopal Iyer * 600dc2366fSVenugopal Iyer * 1. DISCOVER - client attempts to locate DHCP servers via a 610dc2366fSVenugopal Iyer * broadcast message to its subnet. 620dc2366fSVenugopal Iyer * 2. OFFER - server responds to client with an IP address and 630dc2366fSVenugopal Iyer * other parameters. 640dc2366fSVenugopal Iyer * 3. REQUEST - client requests the offered address. 650dc2366fSVenugopal Iyer * 4. ACK - server verifies that the requested address matches 660dc2366fSVenugopal Iyer * the one it offered. 670dc2366fSVenugopal Iyer * 680dc2366fSVenugopal Iyer * DHCPv6 behaves pretty much the same way aside from different message names. 690dc2366fSVenugopal Iyer * 700dc2366fSVenugopal Iyer * Address information is embedded in either the OFFER or REQUEST message. 710dc2366fSVenugopal Iyer * We chose to intercept REQUEST because this is at the last part of the 720dc2366fSVenugopal Iyer * handshake and it indicates that the client intends to keep the address. 730dc2366fSVenugopal Iyer * Intercepting OFFERs is unreliable because the client may receive multiple 740dc2366fSVenugopal Iyer * offers from different servers, and we can't tell which address the client 750dc2366fSVenugopal Iyer * will keep. 760dc2366fSVenugopal Iyer * 770dc2366fSVenugopal Iyer * Each DHCP message has a transaction ID. We use this transaction ID to match 780dc2366fSVenugopal Iyer * REQUESTs with ACKs received from servers. 790dc2366fSVenugopal Iyer * 800dc2366fSVenugopal Iyer * For IPv4, the process to acquire a DHCP-assigned address is as follows: 810dc2366fSVenugopal Iyer * 820dc2366fSVenugopal Iyer * 1. Client sends REQUEST. a new dhcpv4_txn_t object is created and inserted 830dc2366fSVenugopal Iyer * in the the mci_v4_pending_txn table (keyed by xid). This object represents 840dc2366fSVenugopal Iyer * a new transaction. It contains the xid, the client ID and requested IP 850dc2366fSVenugopal Iyer * address. 860dc2366fSVenugopal Iyer * 870dc2366fSVenugopal Iyer * 2. Server responds with an ACK. The xid from this ACK is used to lookup the 880dc2366fSVenugopal Iyer * pending transaction from the mci_v4_pending_txn table. Once the object is 890dc2366fSVenugopal Iyer * found, it is removed from the pending table and inserted into the 900dc2366fSVenugopal Iyer * completed table (mci_v4_completed_txn, keyed by client ID) and the dynamic 910dc2366fSVenugopal Iyer * IP table (mci_v4_dyn_ip, keyed by IP address). 920dc2366fSVenugopal Iyer * 930dc2366fSVenugopal Iyer * 3. An outgoing packet that goes through the ip-nospoof path will be checked 940dc2366fSVenugopal Iyer * against the dynamic IP table. Packets that have the assigned DHCP address 950dc2366fSVenugopal Iyer * as the source IP address will pass the check and be admitted onto the 960dc2366fSVenugopal Iyer * network. 970dc2366fSVenugopal Iyer * 980dc2366fSVenugopal Iyer * IPv4 notes: 990dc2366fSVenugopal Iyer * 1000dc2366fSVenugopal Iyer * If the server never responds with an ACK, there is a timer that is set after 1010dc2366fSVenugopal Iyer * the insertion of the transaction into the pending table. When the timer 1020dc2366fSVenugopal Iyer * fires, it will check whether the transaction is old (by comparing current 1030dc2366fSVenugopal Iyer * time and the txn's timestamp), if so the transaction will be freed. along 1040dc2366fSVenugopal Iyer * with this, any transaction in the completed/dyn-ip tables matching the client 1050dc2366fSVenugopal Iyer * ID of this stale transaction will also be freed. If the client fails to 1060dc2366fSVenugopal Iyer * extend a lease, we want to stop the client from using any IP addresses that 1070dc2366fSVenugopal Iyer * were granted previously. 1080dc2366fSVenugopal Iyer * 1090dc2366fSVenugopal Iyer * A RELEASE message from the client will not cause a transaction to be created. 1100dc2366fSVenugopal Iyer * The client ID in the RELEASE message will be used for finding and removing 1110dc2366fSVenugopal Iyer * transactions in the completed and dyn-ip tables. 1120dc2366fSVenugopal Iyer * 1130dc2366fSVenugopal Iyer * 1140dc2366fSVenugopal Iyer * For IPv6, the process to acquire a DHCPv6-assigned address is as follows: 1150dc2366fSVenugopal Iyer * 1160dc2366fSVenugopal Iyer * 1. Client sends REQUEST. The DUID is extracted and stored into a dhcpv6_cid_t 1170dc2366fSVenugopal Iyer * structure. A new transaction structure (dhcpv6_txn_t) is also created and 1180dc2366fSVenugopal Iyer * it will point to the dhcpv6_cid_t. If an existing transaction with a 1190dc2366fSVenugopal Iyer * matching xid is not found, this dhcpv6_txn_t will be inserted into the 1200dc2366fSVenugopal Iyer * mci_v6_pending_txn table (keyed by xid). 1210dc2366fSVenugopal Iyer * 1220dc2366fSVenugopal Iyer * 2. Server responds with a REPLY. If a pending transaction is found, the 1230dc2366fSVenugopal Iyer * addresses in the reply will be placed into the dhcpv6_cid_t pointed to by 1240dc2366fSVenugopal Iyer * the transaction. The dhcpv6_cid_t will then be moved to the mci_v6_cid 1250dc2366fSVenugopal Iyer * table (keyed by cid). The associated addresses will be added to the 1260dc2366fSVenugopal Iyer * mci_v6_dyn_ip table (while still being pointed to by the dhcpv6_cid_t). 1270dc2366fSVenugopal Iyer * 1280dc2366fSVenugopal Iyer * 3. IPv6 ip-nospoof will now check mci_v6_dyn_ip for matching packets. 1290dc2366fSVenugopal Iyer * Packets with a source address matching one of the DHCPv6-assigned 1300dc2366fSVenugopal Iyer * addresses will be allowed through. 1310dc2366fSVenugopal Iyer * 1320dc2366fSVenugopal Iyer * IPv6 notes: 1330dc2366fSVenugopal Iyer * 1340dc2366fSVenugopal Iyer * The v6 code shares the same timer as v4 for scrubbing stale transactions. 1350dc2366fSVenugopal Iyer * Just like v4, as part of removing an expired transaction, a RELEASE will be 1360dc2366fSVenugopal Iyer * be triggered on the cid associated with the expired transaction. 1370dc2366fSVenugopal Iyer * 1380dc2366fSVenugopal Iyer * The data structures used for v6 are slightly different because a v6 client 1390dc2366fSVenugopal Iyer * may have multiple addresses associated with it. 1400dc2366fSVenugopal Iyer */ 1410dc2366fSVenugopal Iyer 1420dc2366fSVenugopal Iyer /* 1430dc2366fSVenugopal Iyer * These are just arbitrary limits meant for preventing abuse (e.g. a user 1440dc2366fSVenugopal Iyer * flooding the network with bogus transactions). They are not meant to be 1450dc2366fSVenugopal Iyer * user-modifiable so they are not exposed as linkprops. 1460dc2366fSVenugopal Iyer */ 1470dc2366fSVenugopal Iyer static ulong_t dhcp_max_pending_txn = 512; 1480dc2366fSVenugopal Iyer static ulong_t dhcp_max_completed_txn = 512; 149*4e6f6c83SCody Peter Mello static ulong_t slaac_max_allowed = 512; 1504d6a58d3SJosef 'Jeff' Sipek static hrtime_t txn_cleanup_interval = 60 * NANOSEC; 1510dc2366fSVenugopal Iyer 1520dc2366fSVenugopal Iyer /* 1530dc2366fSVenugopal Iyer * DHCPv4 transaction. It may be added to three different tables 1540dc2366fSVenugopal Iyer * (keyed by different fields). 1550dc2366fSVenugopal Iyer */ 1560dc2366fSVenugopal Iyer typedef struct dhcpv4_txn { 1570dc2366fSVenugopal Iyer uint32_t dt_xid; 1584d6a58d3SJosef 'Jeff' Sipek hrtime_t dt_timestamp; 1590dc2366fSVenugopal Iyer uint8_t dt_cid[DHCP_MAX_OPT_SIZE]; 1600dc2366fSVenugopal Iyer uint8_t dt_cid_len; 1610dc2366fSVenugopal Iyer ipaddr_t dt_ipaddr; 1620dc2366fSVenugopal Iyer avl_node_t dt_node; 1630dc2366fSVenugopal Iyer avl_node_t dt_ipnode; 1640dc2366fSVenugopal Iyer struct dhcpv4_txn *dt_next; 1650dc2366fSVenugopal Iyer } dhcpv4_txn_t; 1660dc2366fSVenugopal Iyer 1670dc2366fSVenugopal Iyer /* 1680dc2366fSVenugopal Iyer * DHCPv6 address. May be added to mci_v6_dyn_ip. 1690dc2366fSVenugopal Iyer * It is always pointed to by its parent dhcpv6_cid_t structure. 1700dc2366fSVenugopal Iyer */ 1710dc2366fSVenugopal Iyer typedef struct dhcpv6_addr { 1720dc2366fSVenugopal Iyer in6_addr_t da_addr; 1730dc2366fSVenugopal Iyer avl_node_t da_node; 1740dc2366fSVenugopal Iyer struct dhcpv6_addr *da_next; 1750dc2366fSVenugopal Iyer } dhcpv6_addr_t; 1760dc2366fSVenugopal Iyer 1770dc2366fSVenugopal Iyer /* 1780dc2366fSVenugopal Iyer * DHCPv6 client ID. May be added to mci_v6_cid. 1790dc2366fSVenugopal Iyer * No dhcpv6_txn_t should be pointing to it after it is added to mci_v6_cid. 1800dc2366fSVenugopal Iyer */ 1810dc2366fSVenugopal Iyer typedef struct dhcpv6_cid { 1820dc2366fSVenugopal Iyer uchar_t *dc_cid; 1830dc2366fSVenugopal Iyer uint_t dc_cid_len; 1840dc2366fSVenugopal Iyer dhcpv6_addr_t *dc_addr; 1850dc2366fSVenugopal Iyer uint_t dc_addrcnt; 1860dc2366fSVenugopal Iyer avl_node_t dc_node; 1870dc2366fSVenugopal Iyer } dhcpv6_cid_t; 1880dc2366fSVenugopal Iyer 1890dc2366fSVenugopal Iyer /* 1900dc2366fSVenugopal Iyer * DHCPv6 transaction. Unlike its v4 counterpart, this object gets freed up 1910dc2366fSVenugopal Iyer * as soon as the transaction completes or expires. 1920dc2366fSVenugopal Iyer */ 1930dc2366fSVenugopal Iyer typedef struct dhcpv6_txn { 1940dc2366fSVenugopal Iyer uint32_t dt_xid; 1954d6a58d3SJosef 'Jeff' Sipek hrtime_t dt_timestamp; 1960dc2366fSVenugopal Iyer dhcpv6_cid_t *dt_cid; 1970dc2366fSVenugopal Iyer avl_node_t dt_node; 1980dc2366fSVenugopal Iyer struct dhcpv6_txn *dt_next; 1990dc2366fSVenugopal Iyer } dhcpv6_txn_t; 2000dc2366fSVenugopal Iyer 201*4e6f6c83SCody Peter Mello /* 202*4e6f6c83SCody Peter Mello * Stateless address autoconfiguration (SLAAC) address. May be added to 203*4e6f6c83SCody Peter Mello * mci_v6_slaac_ip. 204*4e6f6c83SCody Peter Mello */ 205*4e6f6c83SCody Peter Mello typedef struct slaac_addr { 206*4e6f6c83SCody Peter Mello in6_addr_t sla_prefix; 207*4e6f6c83SCody Peter Mello in6_addr_t sla_addr; 208*4e6f6c83SCody Peter Mello avl_node_t sla_node; 209*4e6f6c83SCody Peter Mello } slaac_addr_t; 210*4e6f6c83SCody Peter Mello 2110dc2366fSVenugopal Iyer static void start_txn_cleanup_timer(mac_client_impl_t *); 212550b6e40SSowmini Varadhan static boolean_t allowed_ips_set(mac_resource_props_t *, uint32_t); 2130dc2366fSVenugopal Iyer 2140dc2366fSVenugopal Iyer #define BUMP_STAT(m, s) (m)->mci_misc_stat.mms_##s++ 2150dc2366fSVenugopal Iyer 2160dc2366fSVenugopal Iyer /* 2170dc2366fSVenugopal Iyer * Comparison functions for the 3 AVL trees used: 2180dc2366fSVenugopal Iyer * mci_v4_pending_txn, mci_v4_completed_txn, mci_v4_dyn_ip 2190dc2366fSVenugopal Iyer */ 2200dc2366fSVenugopal Iyer static int 2210dc2366fSVenugopal Iyer compare_dhcpv4_xid(const void *arg1, const void *arg2) 2220dc2366fSVenugopal Iyer { 2230dc2366fSVenugopal Iyer const dhcpv4_txn_t *txn1 = arg1, *txn2 = arg2; 2240dc2366fSVenugopal Iyer 2250dc2366fSVenugopal Iyer if (txn1->dt_xid < txn2->dt_xid) 2260dc2366fSVenugopal Iyer return (-1); 2270dc2366fSVenugopal Iyer else if (txn1->dt_xid > txn2->dt_xid) 2280dc2366fSVenugopal Iyer return (1); 2290dc2366fSVenugopal Iyer else 2300dc2366fSVenugopal Iyer return (0); 2310dc2366fSVenugopal Iyer } 2320dc2366fSVenugopal Iyer 2330dc2366fSVenugopal Iyer static int 2340dc2366fSVenugopal Iyer compare_dhcpv4_cid(const void *arg1, const void *arg2) 2350dc2366fSVenugopal Iyer { 2360dc2366fSVenugopal Iyer const dhcpv4_txn_t *txn1 = arg1, *txn2 = arg2; 2370dc2366fSVenugopal Iyer int ret; 2380dc2366fSVenugopal Iyer 2390dc2366fSVenugopal Iyer if (txn1->dt_cid_len < txn2->dt_cid_len) 2400dc2366fSVenugopal Iyer return (-1); 2410dc2366fSVenugopal Iyer else if (txn1->dt_cid_len > txn2->dt_cid_len) 2420dc2366fSVenugopal Iyer return (1); 2430dc2366fSVenugopal Iyer 2440dc2366fSVenugopal Iyer if (txn1->dt_cid_len == 0) 2450dc2366fSVenugopal Iyer return (0); 2460dc2366fSVenugopal Iyer 2470dc2366fSVenugopal Iyer ret = memcmp(txn1->dt_cid, txn2->dt_cid, txn1->dt_cid_len); 2480dc2366fSVenugopal Iyer if (ret < 0) 2490dc2366fSVenugopal Iyer return (-1); 2500dc2366fSVenugopal Iyer else if (ret > 0) 2510dc2366fSVenugopal Iyer return (1); 2520dc2366fSVenugopal Iyer else 2530dc2366fSVenugopal Iyer return (0); 2540dc2366fSVenugopal Iyer } 2550dc2366fSVenugopal Iyer 2560dc2366fSVenugopal Iyer static int 2570dc2366fSVenugopal Iyer compare_dhcpv4_ip(const void *arg1, const void *arg2) 2580dc2366fSVenugopal Iyer { 2590dc2366fSVenugopal Iyer const dhcpv4_txn_t *txn1 = arg1, *txn2 = arg2; 2600dc2366fSVenugopal Iyer 2610dc2366fSVenugopal Iyer if (txn1->dt_ipaddr < txn2->dt_ipaddr) 2620dc2366fSVenugopal Iyer return (-1); 2630dc2366fSVenugopal Iyer else if (txn1->dt_ipaddr > txn2->dt_ipaddr) 2640dc2366fSVenugopal Iyer return (1); 2650dc2366fSVenugopal Iyer else 2660dc2366fSVenugopal Iyer return (0); 2670dc2366fSVenugopal Iyer } 2680dc2366fSVenugopal Iyer 2690dc2366fSVenugopal Iyer /* 2700dc2366fSVenugopal Iyer * Find the specified DHCPv4 option. 2710dc2366fSVenugopal Iyer */ 2720dc2366fSVenugopal Iyer static int 2730dc2366fSVenugopal Iyer get_dhcpv4_option(struct dhcp *dh4, uchar_t *end, uint8_t type, 2740dc2366fSVenugopal Iyer uchar_t **opt, uint8_t *opt_len) 2750dc2366fSVenugopal Iyer { 2760dc2366fSVenugopal Iyer uchar_t *start = (uchar_t *)dh4->options; 2770dc2366fSVenugopal Iyer uint8_t otype, olen; 2780dc2366fSVenugopal Iyer 2790dc2366fSVenugopal Iyer while (start < end) { 2800dc2366fSVenugopal Iyer if (*start == CD_PAD) { 2810dc2366fSVenugopal Iyer start++; 2820dc2366fSVenugopal Iyer continue; 2830dc2366fSVenugopal Iyer } 2840dc2366fSVenugopal Iyer if (*start == CD_END) 2850dc2366fSVenugopal Iyer break; 2860dc2366fSVenugopal Iyer 2870dc2366fSVenugopal Iyer otype = *start++; 2880dc2366fSVenugopal Iyer olen = *start++; 2890dc2366fSVenugopal Iyer if (otype == type && olen > 0) { 2900dc2366fSVenugopal Iyer *opt = start; 2910dc2366fSVenugopal Iyer *opt_len = olen; 2920dc2366fSVenugopal Iyer return (0); 2930dc2366fSVenugopal Iyer } 2940dc2366fSVenugopal Iyer start += olen; 2950dc2366fSVenugopal Iyer } 2960dc2366fSVenugopal Iyer return (ENOENT); 2970dc2366fSVenugopal Iyer } 2980dc2366fSVenugopal Iyer 2990dc2366fSVenugopal Iyer /* 3000dc2366fSVenugopal Iyer * Locate the start of a DHCPv4 header. 3010dc2366fSVenugopal Iyer * The possible return values and associated meanings are: 3020dc2366fSVenugopal Iyer * 0 - packet is DHCP and has a DHCP header. 3030dc2366fSVenugopal Iyer * EINVAL - packet is not DHCP. the recommended action is to let it pass. 3040dc2366fSVenugopal Iyer * ENOSPC - packet is a initial fragment that is DHCP or is unidentifiable. 3050dc2366fSVenugopal Iyer * the recommended action is to drop it. 3060dc2366fSVenugopal Iyer */ 3070dc2366fSVenugopal Iyer static int 3080dc2366fSVenugopal Iyer get_dhcpv4_info(ipha_t *ipha, uchar_t *end, struct dhcp **dh4) 3090dc2366fSVenugopal Iyer { 3100dc2366fSVenugopal Iyer uint16_t offset_and_flags, client, server; 3110dc2366fSVenugopal Iyer boolean_t first_frag = B_FALSE; 3120dc2366fSVenugopal Iyer struct udphdr *udph; 3130dc2366fSVenugopal Iyer uchar_t *dh; 3140dc2366fSVenugopal Iyer 3150dc2366fSVenugopal Iyer if (ipha->ipha_protocol != IPPROTO_UDP) 3160dc2366fSVenugopal Iyer return (EINVAL); 3170dc2366fSVenugopal Iyer 3180dc2366fSVenugopal Iyer offset_and_flags = ntohs(ipha->ipha_fragment_offset_and_flags); 3190dc2366fSVenugopal Iyer if ((offset_and_flags & (IPH_MF | IPH_OFFSET)) != 0) { 3200dc2366fSVenugopal Iyer /* 3210dc2366fSVenugopal Iyer * All non-initial fragments may pass because we cannot 3220dc2366fSVenugopal Iyer * identify their type. It's safe to let them through 3230dc2366fSVenugopal Iyer * because reassembly will fail if we decide to drop the 3240dc2366fSVenugopal Iyer * initial fragment. 3250dc2366fSVenugopal Iyer */ 3260dc2366fSVenugopal Iyer if (((offset_and_flags << 3) & 0xffff) != 0) 3270dc2366fSVenugopal Iyer return (EINVAL); 3280dc2366fSVenugopal Iyer first_frag = B_TRUE; 3290dc2366fSVenugopal Iyer } 3300dc2366fSVenugopal Iyer /* drop packets without a udp header */ 3310dc2366fSVenugopal Iyer udph = (struct udphdr *)((uchar_t *)ipha + IPH_HDR_LENGTH(ipha)); 3320dc2366fSVenugopal Iyer if ((uchar_t *)&udph[1] > end) 3330dc2366fSVenugopal Iyer return (ENOSPC); 3340dc2366fSVenugopal Iyer 3350dc2366fSVenugopal Iyer client = htons(IPPORT_BOOTPC); 3360dc2366fSVenugopal Iyer server = htons(IPPORT_BOOTPS); 3370dc2366fSVenugopal Iyer if (udph->uh_sport != client && udph->uh_sport != server && 3380dc2366fSVenugopal Iyer udph->uh_dport != client && udph->uh_dport != server) 3390dc2366fSVenugopal Iyer return (EINVAL); 3400dc2366fSVenugopal Iyer 3410dc2366fSVenugopal Iyer /* drop dhcp fragments */ 3420dc2366fSVenugopal Iyer if (first_frag) 3430dc2366fSVenugopal Iyer return (ENOSPC); 3440dc2366fSVenugopal Iyer 3450dc2366fSVenugopal Iyer dh = (uchar_t *)&udph[1]; 3460dc2366fSVenugopal Iyer if (dh + BASE_PKT_SIZE > end) 3470dc2366fSVenugopal Iyer return (EINVAL); 3480dc2366fSVenugopal Iyer 3490dc2366fSVenugopal Iyer *dh4 = (struct dhcp *)dh; 3500dc2366fSVenugopal Iyer return (0); 3510dc2366fSVenugopal Iyer } 3520dc2366fSVenugopal Iyer 3530dc2366fSVenugopal Iyer /* 3540dc2366fSVenugopal Iyer * Wrappers for accesses to avl trees to improve readability. 3550dc2366fSVenugopal Iyer * Their purposes are fairly self-explanatory. 3560dc2366fSVenugopal Iyer */ 3570dc2366fSVenugopal Iyer static dhcpv4_txn_t * 3580dc2366fSVenugopal Iyer find_dhcpv4_pending_txn(mac_client_impl_t *mcip, uint32_t xid) 3590dc2366fSVenugopal Iyer { 3600dc2366fSVenugopal Iyer dhcpv4_txn_t tmp_txn; 3610dc2366fSVenugopal Iyer 3620dc2366fSVenugopal Iyer ASSERT(MUTEX_HELD(&mcip->mci_protect_lock)); 3630dc2366fSVenugopal Iyer tmp_txn.dt_xid = xid; 3640dc2366fSVenugopal Iyer return (avl_find(&mcip->mci_v4_pending_txn, &tmp_txn, NULL)); 3650dc2366fSVenugopal Iyer } 3660dc2366fSVenugopal Iyer 3670dc2366fSVenugopal Iyer static int 3680dc2366fSVenugopal Iyer insert_dhcpv4_pending_txn(mac_client_impl_t *mcip, dhcpv4_txn_t *txn) 3690dc2366fSVenugopal Iyer { 3700dc2366fSVenugopal Iyer avl_index_t where; 3710dc2366fSVenugopal Iyer 3720dc2366fSVenugopal Iyer ASSERT(MUTEX_HELD(&mcip->mci_protect_lock)); 3730dc2366fSVenugopal Iyer if (avl_find(&mcip->mci_v4_pending_txn, txn, &where) != NULL) 3740dc2366fSVenugopal Iyer return (EEXIST); 3750dc2366fSVenugopal Iyer 3760dc2366fSVenugopal Iyer if (avl_numnodes(&mcip->mci_v4_pending_txn) >= dhcp_max_pending_txn) { 3770dc2366fSVenugopal Iyer BUMP_STAT(mcip, dhcpdropped); 3780dc2366fSVenugopal Iyer return (EAGAIN); 3790dc2366fSVenugopal Iyer } 3800dc2366fSVenugopal Iyer avl_insert(&mcip->mci_v4_pending_txn, txn, where); 3810dc2366fSVenugopal Iyer return (0); 3820dc2366fSVenugopal Iyer } 3830dc2366fSVenugopal Iyer 3840dc2366fSVenugopal Iyer static void 3850dc2366fSVenugopal Iyer remove_dhcpv4_pending_txn(mac_client_impl_t *mcip, dhcpv4_txn_t *txn) 3860dc2366fSVenugopal Iyer { 3870dc2366fSVenugopal Iyer ASSERT(MUTEX_HELD(&mcip->mci_protect_lock)); 3880dc2366fSVenugopal Iyer avl_remove(&mcip->mci_v4_pending_txn, txn); 3890dc2366fSVenugopal Iyer } 3900dc2366fSVenugopal Iyer 3910dc2366fSVenugopal Iyer static dhcpv4_txn_t * 3920dc2366fSVenugopal Iyer find_dhcpv4_completed_txn(mac_client_impl_t *mcip, uint8_t *cid, 3930dc2366fSVenugopal Iyer uint8_t cid_len) 3940dc2366fSVenugopal Iyer { 3950dc2366fSVenugopal Iyer dhcpv4_txn_t tmp_txn; 3960dc2366fSVenugopal Iyer 3970dc2366fSVenugopal Iyer ASSERT(MUTEX_HELD(&mcip->mci_protect_lock)); 3980dc2366fSVenugopal Iyer if (cid_len > 0) 3990dc2366fSVenugopal Iyer bcopy(cid, tmp_txn.dt_cid, cid_len); 4000dc2366fSVenugopal Iyer tmp_txn.dt_cid_len = cid_len; 4010dc2366fSVenugopal Iyer return (avl_find(&mcip->mci_v4_completed_txn, &tmp_txn, NULL)); 4020dc2366fSVenugopal Iyer } 4030dc2366fSVenugopal Iyer 4040dc2366fSVenugopal Iyer /* 4050dc2366fSVenugopal Iyer * After a pending txn is removed from the pending table, it is inserted 4060dc2366fSVenugopal Iyer * into both the completed and dyn-ip tables. These two insertions are 4070dc2366fSVenugopal Iyer * done together because a client ID must have 1:1 correspondence with 4080dc2366fSVenugopal Iyer * an IP address and IP addresses must be unique in the dyn-ip table. 4090dc2366fSVenugopal Iyer */ 4100dc2366fSVenugopal Iyer static int 4110dc2366fSVenugopal Iyer insert_dhcpv4_completed_txn(mac_client_impl_t *mcip, dhcpv4_txn_t *txn) 4120dc2366fSVenugopal Iyer { 4130dc2366fSVenugopal Iyer avl_index_t where; 4140dc2366fSVenugopal Iyer 4150dc2366fSVenugopal Iyer ASSERT(MUTEX_HELD(&mcip->mci_protect_lock)); 4160dc2366fSVenugopal Iyer if (avl_find(&mcip->mci_v4_completed_txn, txn, &where) != NULL) 4170dc2366fSVenugopal Iyer return (EEXIST); 4180dc2366fSVenugopal Iyer 4190dc2366fSVenugopal Iyer if (avl_numnodes(&mcip->mci_v4_completed_txn) >= 4200dc2366fSVenugopal Iyer dhcp_max_completed_txn) { 4210dc2366fSVenugopal Iyer BUMP_STAT(mcip, dhcpdropped); 4220dc2366fSVenugopal Iyer return (EAGAIN); 4230dc2366fSVenugopal Iyer } 4240dc2366fSVenugopal Iyer 4250dc2366fSVenugopal Iyer avl_insert(&mcip->mci_v4_completed_txn, txn, where); 4260dc2366fSVenugopal Iyer if (avl_find(&mcip->mci_v4_dyn_ip, txn, &where) != NULL) { 4270dc2366fSVenugopal Iyer avl_remove(&mcip->mci_v4_completed_txn, txn); 4280dc2366fSVenugopal Iyer return (EEXIST); 4290dc2366fSVenugopal Iyer } 4300dc2366fSVenugopal Iyer avl_insert(&mcip->mci_v4_dyn_ip, txn, where); 4310dc2366fSVenugopal Iyer return (0); 4320dc2366fSVenugopal Iyer } 4330dc2366fSVenugopal Iyer 4340dc2366fSVenugopal Iyer static void 4350dc2366fSVenugopal Iyer remove_dhcpv4_completed_txn(mac_client_impl_t *mcip, dhcpv4_txn_t *txn) 4360dc2366fSVenugopal Iyer { 4370dc2366fSVenugopal Iyer dhcpv4_txn_t *ctxn; 4380dc2366fSVenugopal Iyer 4390dc2366fSVenugopal Iyer ASSERT(MUTEX_HELD(&mcip->mci_protect_lock)); 4400dc2366fSVenugopal Iyer if ((ctxn = avl_find(&mcip->mci_v4_dyn_ip, txn, NULL)) != NULL && 4410dc2366fSVenugopal Iyer ctxn == txn) 4420dc2366fSVenugopal Iyer avl_remove(&mcip->mci_v4_dyn_ip, txn); 4430dc2366fSVenugopal Iyer 4440dc2366fSVenugopal Iyer avl_remove(&mcip->mci_v4_completed_txn, txn); 4450dc2366fSVenugopal Iyer } 4460dc2366fSVenugopal Iyer 4470dc2366fSVenugopal Iyer /* 4480dc2366fSVenugopal Iyer * Check whether an IP address is in the dyn-ip table. 44925ec3e3dSEric Cheng */ 45025ec3e3dSEric Cheng static boolean_t 4510dc2366fSVenugopal Iyer check_dhcpv4_dyn_ip(mac_client_impl_t *mcip, ipaddr_t ipaddr) 4520dc2366fSVenugopal Iyer { 4530dc2366fSVenugopal Iyer dhcpv4_txn_t tmp_txn, *txn; 4540dc2366fSVenugopal Iyer 4550dc2366fSVenugopal Iyer mutex_enter(&mcip->mci_protect_lock); 4560dc2366fSVenugopal Iyer tmp_txn.dt_ipaddr = ipaddr; 4570dc2366fSVenugopal Iyer txn = avl_find(&mcip->mci_v4_dyn_ip, &tmp_txn, NULL); 4580dc2366fSVenugopal Iyer mutex_exit(&mcip->mci_protect_lock); 4590dc2366fSVenugopal Iyer return (txn != NULL); 4600dc2366fSVenugopal Iyer } 4610dc2366fSVenugopal Iyer 4620dc2366fSVenugopal Iyer /* 4630dc2366fSVenugopal Iyer * Create/destroy a DHCPv4 transaction. 4640dc2366fSVenugopal Iyer */ 4650dc2366fSVenugopal Iyer static dhcpv4_txn_t * 4660dc2366fSVenugopal Iyer create_dhcpv4_txn(uint32_t xid, uint8_t *cid, uint8_t cid_len, ipaddr_t ipaddr) 4670dc2366fSVenugopal Iyer { 4680dc2366fSVenugopal Iyer dhcpv4_txn_t *txn; 4690dc2366fSVenugopal Iyer 4700dc2366fSVenugopal Iyer if ((txn = kmem_zalloc(sizeof (*txn), KM_NOSLEEP)) == NULL) 4710dc2366fSVenugopal Iyer return (NULL); 4720dc2366fSVenugopal Iyer 4730dc2366fSVenugopal Iyer txn->dt_xid = xid; 4744d6a58d3SJosef 'Jeff' Sipek txn->dt_timestamp = gethrtime(); 4750dc2366fSVenugopal Iyer if (cid_len > 0) 4760dc2366fSVenugopal Iyer bcopy(cid, &txn->dt_cid, cid_len); 4770dc2366fSVenugopal Iyer txn->dt_cid_len = cid_len; 4780dc2366fSVenugopal Iyer txn->dt_ipaddr = ipaddr; 4790dc2366fSVenugopal Iyer return (txn); 4800dc2366fSVenugopal Iyer } 4810dc2366fSVenugopal Iyer 4820dc2366fSVenugopal Iyer static void 4830dc2366fSVenugopal Iyer free_dhcpv4_txn(dhcpv4_txn_t *txn) 4840dc2366fSVenugopal Iyer { 4850dc2366fSVenugopal Iyer kmem_free(txn, sizeof (*txn)); 4860dc2366fSVenugopal Iyer } 4870dc2366fSVenugopal Iyer 4880dc2366fSVenugopal Iyer /* 4890dc2366fSVenugopal Iyer * Clean up all v4 tables. 4900dc2366fSVenugopal Iyer */ 4910dc2366fSVenugopal Iyer static void 4920dc2366fSVenugopal Iyer flush_dhcpv4(mac_client_impl_t *mcip) 4930dc2366fSVenugopal Iyer { 4940dc2366fSVenugopal Iyer void *cookie = NULL; 4950dc2366fSVenugopal Iyer dhcpv4_txn_t *txn; 4960dc2366fSVenugopal Iyer 4970dc2366fSVenugopal Iyer ASSERT(MUTEX_HELD(&mcip->mci_protect_lock)); 4980dc2366fSVenugopal Iyer while ((txn = avl_destroy_nodes(&mcip->mci_v4_dyn_ip, 4990dc2366fSVenugopal Iyer &cookie)) != NULL) { 5000dc2366fSVenugopal Iyer /* 5010dc2366fSVenugopal Iyer * No freeing needed here because the same txn exists 5020dc2366fSVenugopal Iyer * in the mci_v4_completed_txn table as well. 5030dc2366fSVenugopal Iyer */ 5040dc2366fSVenugopal Iyer } 5050dc2366fSVenugopal Iyer cookie = NULL; 5060dc2366fSVenugopal Iyer while ((txn = avl_destroy_nodes(&mcip->mci_v4_completed_txn, 5070dc2366fSVenugopal Iyer &cookie)) != NULL) { 5080dc2366fSVenugopal Iyer free_dhcpv4_txn(txn); 5090dc2366fSVenugopal Iyer } 5100dc2366fSVenugopal Iyer cookie = NULL; 5110dc2366fSVenugopal Iyer while ((txn = avl_destroy_nodes(&mcip->mci_v4_pending_txn, 5120dc2366fSVenugopal Iyer &cookie)) != NULL) { 5130dc2366fSVenugopal Iyer free_dhcpv4_txn(txn); 5140dc2366fSVenugopal Iyer } 5150dc2366fSVenugopal Iyer } 5160dc2366fSVenugopal Iyer 5170dc2366fSVenugopal Iyer /* 5180dc2366fSVenugopal Iyer * Cleanup stale DHCPv4 transactions. 5190dc2366fSVenugopal Iyer */ 5200dc2366fSVenugopal Iyer static void 5210dc2366fSVenugopal Iyer txn_cleanup_v4(mac_client_impl_t *mcip) 5220dc2366fSVenugopal Iyer { 5230dc2366fSVenugopal Iyer dhcpv4_txn_t *txn, *ctxn, *next, *txn_list = NULL; 5240dc2366fSVenugopal Iyer 5250dc2366fSVenugopal Iyer /* 5260dc2366fSVenugopal Iyer * Find stale pending transactions and place them on a list 5270dc2366fSVenugopal Iyer * to be removed. 5280dc2366fSVenugopal Iyer */ 5290dc2366fSVenugopal Iyer for (txn = avl_first(&mcip->mci_v4_pending_txn); txn != NULL; 5300dc2366fSVenugopal Iyer txn = avl_walk(&mcip->mci_v4_pending_txn, txn, AVL_AFTER)) { 5314d6a58d3SJosef 'Jeff' Sipek if (gethrtime() - txn->dt_timestamp > txn_cleanup_interval) { 5320dc2366fSVenugopal Iyer DTRACE_PROBE2(found__expired__txn, 5330dc2366fSVenugopal Iyer mac_client_impl_t *, mcip, 5340dc2366fSVenugopal Iyer dhcpv4_txn_t *, txn); 5350dc2366fSVenugopal Iyer 5360dc2366fSVenugopal Iyer txn->dt_next = txn_list; 5370dc2366fSVenugopal Iyer txn_list = txn; 5380dc2366fSVenugopal Iyer } 5390dc2366fSVenugopal Iyer } 5400dc2366fSVenugopal Iyer 5410dc2366fSVenugopal Iyer /* 5420dc2366fSVenugopal Iyer * Remove and free stale pending transactions and completed 5430dc2366fSVenugopal Iyer * transactions with the same client IDs as the stale transactions. 5440dc2366fSVenugopal Iyer */ 5450dc2366fSVenugopal Iyer for (txn = txn_list; txn != NULL; txn = next) { 5460dc2366fSVenugopal Iyer avl_remove(&mcip->mci_v4_pending_txn, txn); 5470dc2366fSVenugopal Iyer 5480dc2366fSVenugopal Iyer ctxn = find_dhcpv4_completed_txn(mcip, txn->dt_cid, 5490dc2366fSVenugopal Iyer txn->dt_cid_len); 5500dc2366fSVenugopal Iyer if (ctxn != NULL) { 5510dc2366fSVenugopal Iyer DTRACE_PROBE2(removing__completed__txn, 5520dc2366fSVenugopal Iyer mac_client_impl_t *, mcip, 5530dc2366fSVenugopal Iyer dhcpv4_txn_t *, ctxn); 5540dc2366fSVenugopal Iyer 5550dc2366fSVenugopal Iyer remove_dhcpv4_completed_txn(mcip, ctxn); 5560dc2366fSVenugopal Iyer free_dhcpv4_txn(ctxn); 5570dc2366fSVenugopal Iyer } 5580dc2366fSVenugopal Iyer next = txn->dt_next; 5590dc2366fSVenugopal Iyer txn->dt_next = NULL; 5600dc2366fSVenugopal Iyer 5610dc2366fSVenugopal Iyer DTRACE_PROBE2(freeing__txn, mac_client_impl_t *, mcip, 5620dc2366fSVenugopal Iyer dhcpv4_txn_t *, txn); 5630dc2366fSVenugopal Iyer free_dhcpv4_txn(txn); 5640dc2366fSVenugopal Iyer } 5650dc2366fSVenugopal Iyer } 5660dc2366fSVenugopal Iyer 5670dc2366fSVenugopal Iyer /* 5680dc2366fSVenugopal Iyer * Core logic for intercepting outbound DHCPv4 packets. 5690dc2366fSVenugopal Iyer */ 570550b6e40SSowmini Varadhan static boolean_t 5710dc2366fSVenugopal Iyer intercept_dhcpv4_outbound(mac_client_impl_t *mcip, ipha_t *ipha, uchar_t *end) 5720dc2366fSVenugopal Iyer { 5730dc2366fSVenugopal Iyer struct dhcp *dh4; 5740dc2366fSVenugopal Iyer uchar_t *opt; 5750dc2366fSVenugopal Iyer dhcpv4_txn_t *txn, *ctxn; 5760dc2366fSVenugopal Iyer ipaddr_t ipaddr; 5770dc2366fSVenugopal Iyer uint8_t opt_len, mtype, cid[DHCP_MAX_OPT_SIZE], cid_len; 578550b6e40SSowmini Varadhan mac_resource_props_t *mrp = MCIP_RESOURCE_PROPS(mcip); 5790dc2366fSVenugopal Iyer 5800dc2366fSVenugopal Iyer if (get_dhcpv4_info(ipha, end, &dh4) != 0) 581550b6e40SSowmini Varadhan return (B_TRUE); 582550b6e40SSowmini Varadhan 583550b6e40SSowmini Varadhan /* ip_nospoof/allowed-ips and DHCP are mutually exclusive by default */ 584550b6e40SSowmini Varadhan if (allowed_ips_set(mrp, IPV4_VERSION)) 585550b6e40SSowmini Varadhan return (B_FALSE); 5860dc2366fSVenugopal Iyer 5870dc2366fSVenugopal Iyer if (get_dhcpv4_option(dh4, end, CD_DHCP_TYPE, &opt, &opt_len) != 0 || 5880dc2366fSVenugopal Iyer opt_len != 1) { 5890dc2366fSVenugopal Iyer DTRACE_PROBE2(mtype__not__found, mac_client_impl_t *, mcip, 5900dc2366fSVenugopal Iyer struct dhcp *, dh4); 591550b6e40SSowmini Varadhan return (B_TRUE); 5920dc2366fSVenugopal Iyer } 5930dc2366fSVenugopal Iyer mtype = *opt; 5940dc2366fSVenugopal Iyer if (mtype != REQUEST && mtype != RELEASE) { 5950dc2366fSVenugopal Iyer DTRACE_PROBE3(ignored__mtype, mac_client_impl_t *, mcip, 5960dc2366fSVenugopal Iyer struct dhcp *, dh4, uint8_t, mtype); 597550b6e40SSowmini Varadhan return (B_TRUE); 5980dc2366fSVenugopal Iyer } 5990dc2366fSVenugopal Iyer 6000dc2366fSVenugopal Iyer /* client ID is optional for IPv4 */ 6010dc2366fSVenugopal Iyer if (get_dhcpv4_option(dh4, end, CD_CLIENT_ID, &opt, &opt_len) == 0 && 6020dc2366fSVenugopal Iyer opt_len >= 2) { 6030dc2366fSVenugopal Iyer bcopy(opt, cid, opt_len); 6040dc2366fSVenugopal Iyer cid_len = opt_len; 6050dc2366fSVenugopal Iyer } else { 6060dc2366fSVenugopal Iyer bzero(cid, DHCP_MAX_OPT_SIZE); 6070dc2366fSVenugopal Iyer cid_len = 0; 6080dc2366fSVenugopal Iyer } 6090dc2366fSVenugopal Iyer 6100dc2366fSVenugopal Iyer mutex_enter(&mcip->mci_protect_lock); 6110dc2366fSVenugopal Iyer if (mtype == RELEASE) { 6120dc2366fSVenugopal Iyer DTRACE_PROBE2(release, mac_client_impl_t *, mcip, 6130dc2366fSVenugopal Iyer struct dhcp *, dh4); 6140dc2366fSVenugopal Iyer 6150dc2366fSVenugopal Iyer /* flush any completed txn with this cid */ 6160dc2366fSVenugopal Iyer ctxn = find_dhcpv4_completed_txn(mcip, cid, cid_len); 6170dc2366fSVenugopal Iyer if (ctxn != NULL) { 6180dc2366fSVenugopal Iyer DTRACE_PROBE2(release__successful, mac_client_impl_t *, 6190dc2366fSVenugopal Iyer mcip, struct dhcp *, dh4); 6200dc2366fSVenugopal Iyer 6210dc2366fSVenugopal Iyer remove_dhcpv4_completed_txn(mcip, ctxn); 6220dc2366fSVenugopal Iyer free_dhcpv4_txn(ctxn); 6230dc2366fSVenugopal Iyer } 6240dc2366fSVenugopal Iyer goto done; 6250dc2366fSVenugopal Iyer } 6260dc2366fSVenugopal Iyer 6270dc2366fSVenugopal Iyer /* 6280dc2366fSVenugopal Iyer * If a pending txn already exists, we'll update its timestamp so 6290dc2366fSVenugopal Iyer * it won't get flushed by the timer. We don't need to create new 6300dc2366fSVenugopal Iyer * txns for retransmissions. 6310dc2366fSVenugopal Iyer */ 6320dc2366fSVenugopal Iyer if ((txn = find_dhcpv4_pending_txn(mcip, dh4->xid)) != NULL) { 6330dc2366fSVenugopal Iyer DTRACE_PROBE2(update, mac_client_impl_t *, mcip, 6340dc2366fSVenugopal Iyer dhcpv4_txn_t *, txn); 6354d6a58d3SJosef 'Jeff' Sipek txn->dt_timestamp = gethrtime(); 6360dc2366fSVenugopal Iyer goto done; 6370dc2366fSVenugopal Iyer } 6380dc2366fSVenugopal Iyer 6390dc2366fSVenugopal Iyer if (get_dhcpv4_option(dh4, end, CD_REQUESTED_IP_ADDR, 6400dc2366fSVenugopal Iyer &opt, &opt_len) != 0 || opt_len != sizeof (ipaddr)) { 6410dc2366fSVenugopal Iyer DTRACE_PROBE2(ipaddr__not__found, mac_client_impl_t *, mcip, 6420dc2366fSVenugopal Iyer struct dhcp *, dh4); 6430dc2366fSVenugopal Iyer goto done; 6440dc2366fSVenugopal Iyer } 6450dc2366fSVenugopal Iyer bcopy(opt, &ipaddr, sizeof (ipaddr)); 6460dc2366fSVenugopal Iyer if ((txn = create_dhcpv4_txn(dh4->xid, cid, cid_len, ipaddr)) == NULL) 6470dc2366fSVenugopal Iyer goto done; 6480dc2366fSVenugopal Iyer 6490dc2366fSVenugopal Iyer if (insert_dhcpv4_pending_txn(mcip, txn) != 0) { 6500dc2366fSVenugopal Iyer DTRACE_PROBE2(insert__failed, mac_client_impl_t *, mcip, 6510dc2366fSVenugopal Iyer dhcpv4_txn_t *, txn); 6520dc2366fSVenugopal Iyer free_dhcpv4_txn(txn); 6530dc2366fSVenugopal Iyer goto done; 6540dc2366fSVenugopal Iyer } 6550dc2366fSVenugopal Iyer start_txn_cleanup_timer(mcip); 6560dc2366fSVenugopal Iyer 6570dc2366fSVenugopal Iyer DTRACE_PROBE2(txn__pending, mac_client_impl_t *, mcip, 6580dc2366fSVenugopal Iyer dhcpv4_txn_t *, txn); 6590dc2366fSVenugopal Iyer 6600dc2366fSVenugopal Iyer done: 6610dc2366fSVenugopal Iyer mutex_exit(&mcip->mci_protect_lock); 662550b6e40SSowmini Varadhan return (B_TRUE); 6630dc2366fSVenugopal Iyer } 6640dc2366fSVenugopal Iyer 6650dc2366fSVenugopal Iyer /* 6660dc2366fSVenugopal Iyer * Core logic for intercepting inbound DHCPv4 packets. 6670dc2366fSVenugopal Iyer */ 6680dc2366fSVenugopal Iyer static void 669*4e6f6c83SCody Peter Mello intercept_dhcpv4_inbound(mac_client_impl_t *mcip, uchar_t *end, 670*4e6f6c83SCody Peter Mello struct dhcp *dh4) 6710dc2366fSVenugopal Iyer { 6720dc2366fSVenugopal Iyer uchar_t *opt; 6730dc2366fSVenugopal Iyer dhcpv4_txn_t *txn, *ctxn; 6740dc2366fSVenugopal Iyer uint8_t opt_len, mtype; 6750dc2366fSVenugopal Iyer 6760dc2366fSVenugopal Iyer if (get_dhcpv4_option(dh4, end, CD_DHCP_TYPE, &opt, &opt_len) != 0 || 6770dc2366fSVenugopal Iyer opt_len != 1) { 6780dc2366fSVenugopal Iyer DTRACE_PROBE2(mtype__not__found, mac_client_impl_t *, mcip, 6790dc2366fSVenugopal Iyer struct dhcp *, dh4); 6800dc2366fSVenugopal Iyer return; 6810dc2366fSVenugopal Iyer } 6820dc2366fSVenugopal Iyer mtype = *opt; 6830dc2366fSVenugopal Iyer if (mtype != ACK && mtype != NAK) { 6840dc2366fSVenugopal Iyer DTRACE_PROBE3(ignored__mtype, mac_client_impl_t *, mcip, 6850dc2366fSVenugopal Iyer struct dhcp *, dh4, uint8_t, mtype); 6860dc2366fSVenugopal Iyer return; 6870dc2366fSVenugopal Iyer } 6880dc2366fSVenugopal Iyer 6890dc2366fSVenugopal Iyer mutex_enter(&mcip->mci_protect_lock); 6900dc2366fSVenugopal Iyer if ((txn = find_dhcpv4_pending_txn(mcip, dh4->xid)) == NULL) { 6910dc2366fSVenugopal Iyer DTRACE_PROBE2(txn__not__found, mac_client_impl_t *, mcip, 6920dc2366fSVenugopal Iyer struct dhcp *, dh4); 6930dc2366fSVenugopal Iyer goto done; 6940dc2366fSVenugopal Iyer } 6950dc2366fSVenugopal Iyer remove_dhcpv4_pending_txn(mcip, txn); 6960dc2366fSVenugopal Iyer 6970dc2366fSVenugopal Iyer /* 6980dc2366fSVenugopal Iyer * We're about to move a txn from the pending table to the completed/ 6990dc2366fSVenugopal Iyer * dyn-ip tables. If there is an existing completed txn with the 7000dc2366fSVenugopal Iyer * same cid as our txn, we need to remove and free it. 7010dc2366fSVenugopal Iyer */ 7020dc2366fSVenugopal Iyer ctxn = find_dhcpv4_completed_txn(mcip, txn->dt_cid, txn->dt_cid_len); 7030dc2366fSVenugopal Iyer if (ctxn != NULL) { 7040dc2366fSVenugopal Iyer DTRACE_PROBE2(replacing__old__txn, mac_client_impl_t *, mcip, 7050dc2366fSVenugopal Iyer dhcpv4_txn_t *, ctxn); 7060dc2366fSVenugopal Iyer remove_dhcpv4_completed_txn(mcip, ctxn); 7070dc2366fSVenugopal Iyer free_dhcpv4_txn(ctxn); 7080dc2366fSVenugopal Iyer } 7090dc2366fSVenugopal Iyer if (mtype == NAK) { 7100dc2366fSVenugopal Iyer DTRACE_PROBE2(nak__received, mac_client_impl_t *, mcip, 7110dc2366fSVenugopal Iyer dhcpv4_txn_t *, txn); 7120dc2366fSVenugopal Iyer free_dhcpv4_txn(txn); 7130dc2366fSVenugopal Iyer goto done; 7140dc2366fSVenugopal Iyer } 7150dc2366fSVenugopal Iyer if (insert_dhcpv4_completed_txn(mcip, txn) != 0) { 7160dc2366fSVenugopal Iyer DTRACE_PROBE2(insert__failed, mac_client_impl_t *, mcip, 7170dc2366fSVenugopal Iyer dhcpv4_txn_t *, txn); 7180dc2366fSVenugopal Iyer free_dhcpv4_txn(txn); 7190dc2366fSVenugopal Iyer goto done; 7200dc2366fSVenugopal Iyer } 7210dc2366fSVenugopal Iyer DTRACE_PROBE2(txn__completed, mac_client_impl_t *, mcip, 7220dc2366fSVenugopal Iyer dhcpv4_txn_t *, txn); 7230dc2366fSVenugopal Iyer 7240dc2366fSVenugopal Iyer done: 7250dc2366fSVenugopal Iyer mutex_exit(&mcip->mci_protect_lock); 7260dc2366fSVenugopal Iyer } 7270dc2366fSVenugopal Iyer 7280dc2366fSVenugopal Iyer 7290dc2366fSVenugopal Iyer /* 7300dc2366fSVenugopal Iyer * Comparison functions for the DHCPv6 AVL trees. 7310dc2366fSVenugopal Iyer */ 7320dc2366fSVenugopal Iyer static int 7330dc2366fSVenugopal Iyer compare_dhcpv6_xid(const void *arg1, const void *arg2) 7340dc2366fSVenugopal Iyer { 7350dc2366fSVenugopal Iyer const dhcpv6_txn_t *txn1 = arg1, *txn2 = arg2; 7360dc2366fSVenugopal Iyer 7370dc2366fSVenugopal Iyer if (txn1->dt_xid < txn2->dt_xid) 7380dc2366fSVenugopal Iyer return (-1); 7390dc2366fSVenugopal Iyer else if (txn1->dt_xid > txn2->dt_xid) 7400dc2366fSVenugopal Iyer return (1); 7410dc2366fSVenugopal Iyer else 7420dc2366fSVenugopal Iyer return (0); 7430dc2366fSVenugopal Iyer } 7440dc2366fSVenugopal Iyer 7450dc2366fSVenugopal Iyer static int 7460dc2366fSVenugopal Iyer compare_dhcpv6_ip(const void *arg1, const void *arg2) 7470dc2366fSVenugopal Iyer { 7480dc2366fSVenugopal Iyer const dhcpv6_addr_t *ip1 = arg1, *ip2 = arg2; 7490dc2366fSVenugopal Iyer int ret; 7500dc2366fSVenugopal Iyer 7510dc2366fSVenugopal Iyer ret = memcmp(&ip1->da_addr, &ip2->da_addr, sizeof (in6_addr_t)); 7520dc2366fSVenugopal Iyer if (ret < 0) 7530dc2366fSVenugopal Iyer return (-1); 7540dc2366fSVenugopal Iyer else if (ret > 0) 7550dc2366fSVenugopal Iyer return (1); 7560dc2366fSVenugopal Iyer else 7570dc2366fSVenugopal Iyer return (0); 7580dc2366fSVenugopal Iyer } 7590dc2366fSVenugopal Iyer 7600dc2366fSVenugopal Iyer static int 7610dc2366fSVenugopal Iyer compare_dhcpv6_cid(const void *arg1, const void *arg2) 7620dc2366fSVenugopal Iyer { 7630dc2366fSVenugopal Iyer const dhcpv6_cid_t *cid1 = arg1, *cid2 = arg2; 7640dc2366fSVenugopal Iyer int ret; 7650dc2366fSVenugopal Iyer 7660dc2366fSVenugopal Iyer if (cid1->dc_cid_len < cid2->dc_cid_len) 7670dc2366fSVenugopal Iyer return (-1); 7680dc2366fSVenugopal Iyer else if (cid1->dc_cid_len > cid2->dc_cid_len) 7690dc2366fSVenugopal Iyer return (1); 7700dc2366fSVenugopal Iyer 7710dc2366fSVenugopal Iyer if (cid1->dc_cid_len == 0) 7720dc2366fSVenugopal Iyer return (0); 7730dc2366fSVenugopal Iyer 7740dc2366fSVenugopal Iyer ret = memcmp(cid1->dc_cid, cid2->dc_cid, cid1->dc_cid_len); 7750dc2366fSVenugopal Iyer if (ret < 0) 7760dc2366fSVenugopal Iyer return (-1); 7770dc2366fSVenugopal Iyer else if (ret > 0) 7780dc2366fSVenugopal Iyer return (1); 7790dc2366fSVenugopal Iyer else 7800dc2366fSVenugopal Iyer return (0); 7810dc2366fSVenugopal Iyer } 7820dc2366fSVenugopal Iyer 783*4e6f6c83SCody Peter Mello static int 784*4e6f6c83SCody Peter Mello compare_slaac_ip(const void *arg1, const void *arg2) 785*4e6f6c83SCody Peter Mello { 786*4e6f6c83SCody Peter Mello const slaac_addr_t *ip1 = arg1, *ip2 = arg2; 787*4e6f6c83SCody Peter Mello int ret; 788*4e6f6c83SCody Peter Mello 789*4e6f6c83SCody Peter Mello ret = memcmp(&ip1->sla_addr, &ip2->sla_addr, sizeof (in6_addr_t)); 790*4e6f6c83SCody Peter Mello if (ret < 0) 791*4e6f6c83SCody Peter Mello return (-1); 792*4e6f6c83SCody Peter Mello else if (ret > 0) 793*4e6f6c83SCody Peter Mello return (1); 794*4e6f6c83SCody Peter Mello else 795*4e6f6c83SCody Peter Mello return (0); 796*4e6f6c83SCody Peter Mello } 797*4e6f6c83SCody Peter Mello 7980dc2366fSVenugopal Iyer /* 7990dc2366fSVenugopal Iyer * Locate the start of a DHCPv6 header. 8000dc2366fSVenugopal Iyer * The possible return values and associated meanings are: 8010dc2366fSVenugopal Iyer * 0 - packet is DHCP and has a DHCP header. 8020dc2366fSVenugopal Iyer * EINVAL - packet is not DHCP. the recommended action is to let it pass. 8030dc2366fSVenugopal Iyer * ENOSPC - packet is a initial fragment that is DHCP or is unidentifiable. 8040dc2366fSVenugopal Iyer * the recommended action is to drop it. 8050dc2366fSVenugopal Iyer */ 8060dc2366fSVenugopal Iyer static int 8070dc2366fSVenugopal Iyer get_dhcpv6_info(ip6_t *ip6h, uchar_t *end, dhcpv6_message_t **dh6) 8080dc2366fSVenugopal Iyer { 8090dc2366fSVenugopal Iyer uint16_t hdrlen, client, server; 8100dc2366fSVenugopal Iyer boolean_t first_frag = B_FALSE; 8110dc2366fSVenugopal Iyer ip6_frag_t *frag = NULL; 8120dc2366fSVenugopal Iyer uint8_t proto; 8130dc2366fSVenugopal Iyer struct udphdr *udph; 8140dc2366fSVenugopal Iyer uchar_t *dh; 8150dc2366fSVenugopal Iyer 8160dc2366fSVenugopal Iyer if (!mac_ip_hdr_length_v6(ip6h, end, &hdrlen, &proto, &frag)) 8170dc2366fSVenugopal Iyer return (ENOSPC); 8180dc2366fSVenugopal Iyer 8190dc2366fSVenugopal Iyer if (proto != IPPROTO_UDP) 8200dc2366fSVenugopal Iyer return (EINVAL); 8210dc2366fSVenugopal Iyer 8220dc2366fSVenugopal Iyer if (frag != NULL) { 8230dc2366fSVenugopal Iyer /* 8240dc2366fSVenugopal Iyer * All non-initial fragments may pass because we cannot 8250dc2366fSVenugopal Iyer * identify their type. It's safe to let them through 8260dc2366fSVenugopal Iyer * because reassembly will fail if we decide to drop the 8270dc2366fSVenugopal Iyer * initial fragment. 8280dc2366fSVenugopal Iyer */ 8290dc2366fSVenugopal Iyer if ((ntohs(frag->ip6f_offlg) & ~7) != 0) 8300dc2366fSVenugopal Iyer return (EINVAL); 8310dc2366fSVenugopal Iyer first_frag = B_TRUE; 8320dc2366fSVenugopal Iyer } 8330dc2366fSVenugopal Iyer /* drop packets without a udp header */ 8340dc2366fSVenugopal Iyer udph = (struct udphdr *)((uchar_t *)ip6h + hdrlen); 8350dc2366fSVenugopal Iyer if ((uchar_t *)&udph[1] > end) 8360dc2366fSVenugopal Iyer return (ENOSPC); 8370dc2366fSVenugopal Iyer 8380dc2366fSVenugopal Iyer client = htons(IPPORT_DHCPV6C); 8390dc2366fSVenugopal Iyer server = htons(IPPORT_DHCPV6S); 8400dc2366fSVenugopal Iyer if (udph->uh_sport != client && udph->uh_sport != server && 8410dc2366fSVenugopal Iyer udph->uh_dport != client && udph->uh_dport != server) 8420dc2366fSVenugopal Iyer return (EINVAL); 8430dc2366fSVenugopal Iyer 8440dc2366fSVenugopal Iyer /* drop dhcp fragments */ 8450dc2366fSVenugopal Iyer if (first_frag) 8460dc2366fSVenugopal Iyer return (ENOSPC); 8470dc2366fSVenugopal Iyer 8480dc2366fSVenugopal Iyer dh = (uchar_t *)&udph[1]; 8490dc2366fSVenugopal Iyer if (dh + sizeof (dhcpv6_message_t) > end) 8500dc2366fSVenugopal Iyer return (EINVAL); 8510dc2366fSVenugopal Iyer 8520dc2366fSVenugopal Iyer *dh6 = (dhcpv6_message_t *)dh; 8530dc2366fSVenugopal Iyer return (0); 8540dc2366fSVenugopal Iyer } 8550dc2366fSVenugopal Iyer 856*4e6f6c83SCody Peter Mello static int 857*4e6f6c83SCody Peter Mello get_ra_info(ip6_t *ip6h, uchar_t *end, nd_router_advert_t **ra) 858*4e6f6c83SCody Peter Mello { 859*4e6f6c83SCody Peter Mello uint16_t hdrlen; 860*4e6f6c83SCody Peter Mello ip6_frag_t *frag = NULL; 861*4e6f6c83SCody Peter Mello uint8_t proto; 862*4e6f6c83SCody Peter Mello uchar_t *hdrp; 863*4e6f6c83SCody Peter Mello struct icmp6_hdr *icmp; 864*4e6f6c83SCody Peter Mello 865*4e6f6c83SCody Peter Mello if (!mac_ip_hdr_length_v6(ip6h, end, &hdrlen, &proto, &frag)) 866*4e6f6c83SCody Peter Mello return (ENOSPC); 867*4e6f6c83SCody Peter Mello 868*4e6f6c83SCody Peter Mello if (proto != IPPROTO_ICMPV6) 869*4e6f6c83SCody Peter Mello return (EINVAL); 870*4e6f6c83SCody Peter Mello 871*4e6f6c83SCody Peter Mello if (frag != NULL) { 872*4e6f6c83SCody Peter Mello /* 873*4e6f6c83SCody Peter Mello * All non-initial fragments may pass because we cannot 874*4e6f6c83SCody Peter Mello * identify their type. It's safe to let them through 875*4e6f6c83SCody Peter Mello * because reassembly will fail if we decide to drop the 876*4e6f6c83SCody Peter Mello * initial fragment. 877*4e6f6c83SCody Peter Mello */ 878*4e6f6c83SCody Peter Mello if ((ntohs(frag->ip6f_offlg) & ~7) != 0) 879*4e6f6c83SCody Peter Mello return (EINVAL); 880*4e6f6c83SCody Peter Mello return (ENOSPC); 881*4e6f6c83SCody Peter Mello } 882*4e6f6c83SCody Peter Mello 883*4e6f6c83SCody Peter Mello /* 884*4e6f6c83SCody Peter Mello * Ensure that the ICMP header falls w/in packet boundaries, in case 885*4e6f6c83SCody Peter Mello * we've received a malicious packet that reports incorrect lengths. 886*4e6f6c83SCody Peter Mello */ 887*4e6f6c83SCody Peter Mello hdrp = (uchar_t *)ip6h + hdrlen; 888*4e6f6c83SCody Peter Mello if ((hdrp + sizeof (struct icmp6_hdr)) > end) { 889*4e6f6c83SCody Peter Mello return (EINVAL); 890*4e6f6c83SCody Peter Mello } 891*4e6f6c83SCody Peter Mello icmp = (struct icmp6_hdr *)hdrp; 892*4e6f6c83SCody Peter Mello 893*4e6f6c83SCody Peter Mello if (icmp->icmp6_type != ND_ROUTER_ADVERT || 894*4e6f6c83SCody Peter Mello icmp->icmp6_code != 0) 895*4e6f6c83SCody Peter Mello return (EINVAL); 896*4e6f6c83SCody Peter Mello 897*4e6f6c83SCody Peter Mello *ra = (nd_router_advert_t *)icmp; 898*4e6f6c83SCody Peter Mello return (0); 899*4e6f6c83SCody Peter Mello } 900*4e6f6c83SCody Peter Mello 9010dc2366fSVenugopal Iyer /* 9020dc2366fSVenugopal Iyer * Find the specified DHCPv6 option. 9030dc2366fSVenugopal Iyer */ 9040dc2366fSVenugopal Iyer static dhcpv6_option_t * 9050dc2366fSVenugopal Iyer get_dhcpv6_option(void *buf, size_t buflen, dhcpv6_option_t *oldopt, 9060dc2366fSVenugopal Iyer uint16_t codenum, uint_t *retlenp) 9070dc2366fSVenugopal Iyer { 9080dc2366fSVenugopal Iyer uchar_t *bp; 9090dc2366fSVenugopal Iyer dhcpv6_option_t d6o; 9100dc2366fSVenugopal Iyer uint_t olen; 9110dc2366fSVenugopal Iyer 9120dc2366fSVenugopal Iyer codenum = htons(codenum); 9130dc2366fSVenugopal Iyer bp = buf; 9140dc2366fSVenugopal Iyer while (buflen >= sizeof (dhcpv6_option_t)) { 9150dc2366fSVenugopal Iyer bcopy(bp, &d6o, sizeof (d6o)); 9160dc2366fSVenugopal Iyer olen = ntohs(d6o.d6o_len) + sizeof (d6o); 9170dc2366fSVenugopal Iyer if (olen > buflen) 9180dc2366fSVenugopal Iyer break; 9190dc2366fSVenugopal Iyer if (d6o.d6o_code != codenum || d6o.d6o_len == 0 || 9200dc2366fSVenugopal Iyer (oldopt != NULL && bp <= (uchar_t *)oldopt)) { 9210dc2366fSVenugopal Iyer bp += olen; 9220dc2366fSVenugopal Iyer buflen -= olen; 9230dc2366fSVenugopal Iyer continue; 9240dc2366fSVenugopal Iyer } 9250dc2366fSVenugopal Iyer if (retlenp != NULL) 9260dc2366fSVenugopal Iyer *retlenp = olen; 9270dc2366fSVenugopal Iyer /* LINTED : alignment */ 9280dc2366fSVenugopal Iyer return ((dhcpv6_option_t *)bp); 9290dc2366fSVenugopal Iyer } 9300dc2366fSVenugopal Iyer return (NULL); 9310dc2366fSVenugopal Iyer } 9320dc2366fSVenugopal Iyer 9330dc2366fSVenugopal Iyer /* 9340dc2366fSVenugopal Iyer * Get the status code from a reply message. 9350dc2366fSVenugopal Iyer */ 9360dc2366fSVenugopal Iyer static int 9370dc2366fSVenugopal Iyer get_dhcpv6_status(dhcpv6_message_t *dh6, uchar_t *end, uint16_t *status) 9380dc2366fSVenugopal Iyer { 9390dc2366fSVenugopal Iyer dhcpv6_option_t *d6o; 9400dc2366fSVenugopal Iyer uint_t olen; 9410dc2366fSVenugopal Iyer uint16_t s; 9420dc2366fSVenugopal Iyer 9430dc2366fSVenugopal Iyer d6o = get_dhcpv6_option(&dh6[1], end - (uchar_t *)&dh6[1], NULL, 9440dc2366fSVenugopal Iyer DHCPV6_OPT_STATUS_CODE, &olen); 9450dc2366fSVenugopal Iyer 9460dc2366fSVenugopal Iyer /* Success is implied if status code is missing */ 9470dc2366fSVenugopal Iyer if (d6o == NULL) { 9480dc2366fSVenugopal Iyer *status = DHCPV6_STAT_SUCCESS; 9490dc2366fSVenugopal Iyer return (0); 9500dc2366fSVenugopal Iyer } 9510dc2366fSVenugopal Iyer if ((uchar_t *)d6o + olen > end) 9520dc2366fSVenugopal Iyer return (EINVAL); 9530dc2366fSVenugopal Iyer 9540dc2366fSVenugopal Iyer olen -= sizeof (*d6o); 9550dc2366fSVenugopal Iyer if (olen < sizeof (s)) 9560dc2366fSVenugopal Iyer return (EINVAL); 9570dc2366fSVenugopal Iyer 9580dc2366fSVenugopal Iyer bcopy(&d6o[1], &s, sizeof (s)); 9590dc2366fSVenugopal Iyer *status = ntohs(s); 9600dc2366fSVenugopal Iyer return (0); 9610dc2366fSVenugopal Iyer } 9620dc2366fSVenugopal Iyer 9630dc2366fSVenugopal Iyer /* 9640dc2366fSVenugopal Iyer * Get the addresses from a reply message. 9650dc2366fSVenugopal Iyer */ 9660dc2366fSVenugopal Iyer static int 9670dc2366fSVenugopal Iyer get_dhcpv6_addrs(dhcpv6_message_t *dh6, uchar_t *end, dhcpv6_cid_t *cid) 9680dc2366fSVenugopal Iyer { 9690dc2366fSVenugopal Iyer dhcpv6_option_t *d6o; 9700dc2366fSVenugopal Iyer dhcpv6_addr_t *next; 9710dc2366fSVenugopal Iyer uint_t olen; 9720dc2366fSVenugopal Iyer 9730dc2366fSVenugopal Iyer d6o = NULL; 9740dc2366fSVenugopal Iyer while ((d6o = get_dhcpv6_option(&dh6[1], end - (uchar_t *)&dh6[1], 9750dc2366fSVenugopal Iyer d6o, DHCPV6_OPT_IA_NA, &olen)) != NULL) { 9760dc2366fSVenugopal Iyer dhcpv6_option_t *d6so; 9770dc2366fSVenugopal Iyer dhcpv6_iaaddr_t d6ia; 9780dc2366fSVenugopal Iyer dhcpv6_addr_t **addrp; 9790dc2366fSVenugopal Iyer uchar_t *obase; 9800dc2366fSVenugopal Iyer uint_t solen; 9810dc2366fSVenugopal Iyer 9820dc2366fSVenugopal Iyer if (olen < sizeof (dhcpv6_ia_na_t) || 9830dc2366fSVenugopal Iyer (uchar_t *)d6o + olen > end) 9840dc2366fSVenugopal Iyer goto fail; 9850dc2366fSVenugopal Iyer 9860dc2366fSVenugopal Iyer obase = (uchar_t *)d6o + sizeof (dhcpv6_ia_na_t); 9870dc2366fSVenugopal Iyer olen -= sizeof (dhcpv6_ia_na_t); 9880dc2366fSVenugopal Iyer d6so = NULL; 9890dc2366fSVenugopal Iyer while ((d6so = get_dhcpv6_option(obase, olen, d6so, 9900dc2366fSVenugopal Iyer DHCPV6_OPT_IAADDR, &solen)) != NULL) { 9910dc2366fSVenugopal Iyer if (solen < sizeof (dhcpv6_iaaddr_t) || 9920dc2366fSVenugopal Iyer (uchar_t *)d6so + solen > end) 9930dc2366fSVenugopal Iyer goto fail; 9940dc2366fSVenugopal Iyer 9950dc2366fSVenugopal Iyer bcopy(d6so, &d6ia, sizeof (d6ia)); 9960dc2366fSVenugopal Iyer for (addrp = &cid->dc_addr; *addrp != NULL; 9970dc2366fSVenugopal Iyer addrp = &(*addrp)->da_next) { 9980dc2366fSVenugopal Iyer if (bcmp(&(*addrp)->da_addr, &d6ia.d6ia_addr, 9990dc2366fSVenugopal Iyer sizeof (in6_addr_t)) == 0) 10000dc2366fSVenugopal Iyer goto fail; 10010dc2366fSVenugopal Iyer } 10020dc2366fSVenugopal Iyer if ((*addrp = kmem_zalloc(sizeof (dhcpv6_addr_t), 10030dc2366fSVenugopal Iyer KM_NOSLEEP)) == NULL) 10040dc2366fSVenugopal Iyer goto fail; 10050dc2366fSVenugopal Iyer 10060dc2366fSVenugopal Iyer bcopy(&d6ia.d6ia_addr, &(*addrp)->da_addr, 10070dc2366fSVenugopal Iyer sizeof (in6_addr_t)); 10080dc2366fSVenugopal Iyer cid->dc_addrcnt++; 10090dc2366fSVenugopal Iyer } 10100dc2366fSVenugopal Iyer } 10110dc2366fSVenugopal Iyer if (cid->dc_addrcnt == 0) 10120dc2366fSVenugopal Iyer return (ENOENT); 10130dc2366fSVenugopal Iyer 10140dc2366fSVenugopal Iyer return (0); 10150dc2366fSVenugopal Iyer 10160dc2366fSVenugopal Iyer fail: 10170dc2366fSVenugopal Iyer for (; cid->dc_addr != NULL; cid->dc_addr = next) { 10180dc2366fSVenugopal Iyer next = cid->dc_addr->da_next; 10190dc2366fSVenugopal Iyer kmem_free(cid->dc_addr, sizeof (dhcpv6_addr_t)); 10200dc2366fSVenugopal Iyer cid->dc_addrcnt--; 10210dc2366fSVenugopal Iyer } 10220dc2366fSVenugopal Iyer ASSERT(cid->dc_addrcnt == 0); 10230dc2366fSVenugopal Iyer return (EINVAL); 10240dc2366fSVenugopal Iyer } 10250dc2366fSVenugopal Iyer 10260dc2366fSVenugopal Iyer /* 10270dc2366fSVenugopal Iyer * Free a cid. 10280dc2366fSVenugopal Iyer * Before this gets called the caller must ensure that all the 10290dc2366fSVenugopal Iyer * addresses are removed from the mci_v6_dyn_ip table. 10300dc2366fSVenugopal Iyer */ 10310dc2366fSVenugopal Iyer static void 10320dc2366fSVenugopal Iyer free_dhcpv6_cid(dhcpv6_cid_t *cid) 10330dc2366fSVenugopal Iyer { 10340dc2366fSVenugopal Iyer dhcpv6_addr_t *addr, *next; 10350dc2366fSVenugopal Iyer uint_t cnt = 0; 10360dc2366fSVenugopal Iyer 10370dc2366fSVenugopal Iyer kmem_free(cid->dc_cid, cid->dc_cid_len); 10380dc2366fSVenugopal Iyer for (addr = cid->dc_addr; addr != NULL; addr = next) { 10390dc2366fSVenugopal Iyer next = addr->da_next; 10400dc2366fSVenugopal Iyer kmem_free(addr, sizeof (*addr)); 10410dc2366fSVenugopal Iyer cnt++; 10420dc2366fSVenugopal Iyer } 10430dc2366fSVenugopal Iyer ASSERT(cnt == cid->dc_addrcnt); 10440dc2366fSVenugopal Iyer kmem_free(cid, sizeof (*cid)); 10450dc2366fSVenugopal Iyer } 10460dc2366fSVenugopal Iyer 10470dc2366fSVenugopal Iyer /* 10480dc2366fSVenugopal Iyer * Extract the DUID from a message. The associated addresses will be 10490dc2366fSVenugopal Iyer * extracted later from the reply message. 10500dc2366fSVenugopal Iyer */ 10510dc2366fSVenugopal Iyer static dhcpv6_cid_t * 10520dc2366fSVenugopal Iyer create_dhcpv6_cid(dhcpv6_message_t *dh6, uchar_t *end) 10530dc2366fSVenugopal Iyer { 10540dc2366fSVenugopal Iyer dhcpv6_option_t *d6o; 10550dc2366fSVenugopal Iyer dhcpv6_cid_t *cid; 10560dc2366fSVenugopal Iyer uchar_t *rawcid; 10570dc2366fSVenugopal Iyer uint_t olen, rawcidlen; 10580dc2366fSVenugopal Iyer 10590dc2366fSVenugopal Iyer d6o = get_dhcpv6_option(&dh6[1], end - (uchar_t *)&dh6[1], NULL, 10600dc2366fSVenugopal Iyer DHCPV6_OPT_CLIENTID, &olen); 10610dc2366fSVenugopal Iyer if (d6o == NULL || (uchar_t *)d6o + olen > end) 10620dc2366fSVenugopal Iyer return (NULL); 10630dc2366fSVenugopal Iyer 10640dc2366fSVenugopal Iyer rawcidlen = olen - sizeof (*d6o); 10650dc2366fSVenugopal Iyer if ((rawcid = kmem_zalloc(rawcidlen, KM_NOSLEEP)) == NULL) 10660dc2366fSVenugopal Iyer return (NULL); 10670dc2366fSVenugopal Iyer bcopy(d6o + 1, rawcid, rawcidlen); 10680dc2366fSVenugopal Iyer 10690dc2366fSVenugopal Iyer if ((cid = kmem_zalloc(sizeof (*cid), KM_NOSLEEP)) == NULL) { 10700dc2366fSVenugopal Iyer kmem_free(rawcid, rawcidlen); 10710dc2366fSVenugopal Iyer return (NULL); 10720dc2366fSVenugopal Iyer } 10730dc2366fSVenugopal Iyer cid->dc_cid = rawcid; 10740dc2366fSVenugopal Iyer cid->dc_cid_len = rawcidlen; 10750dc2366fSVenugopal Iyer return (cid); 10760dc2366fSVenugopal Iyer } 10770dc2366fSVenugopal Iyer 10780dc2366fSVenugopal Iyer /* 10790dc2366fSVenugopal Iyer * Remove a cid from mci_v6_cid. The addresses owned by the cid 10800dc2366fSVenugopal Iyer * are also removed from mci_v6_dyn_ip. 10810dc2366fSVenugopal Iyer */ 10820dc2366fSVenugopal Iyer static void 10830dc2366fSVenugopal Iyer remove_dhcpv6_cid(mac_client_impl_t *mcip, dhcpv6_cid_t *cid) 10840dc2366fSVenugopal Iyer { 10850dc2366fSVenugopal Iyer dhcpv6_addr_t *addr, *tmp_addr; 10860dc2366fSVenugopal Iyer 10870dc2366fSVenugopal Iyer ASSERT(MUTEX_HELD(&mcip->mci_protect_lock)); 10880dc2366fSVenugopal Iyer avl_remove(&mcip->mci_v6_cid, cid); 10890dc2366fSVenugopal Iyer for (addr = cid->dc_addr; addr != NULL; addr = addr->da_next) { 10900dc2366fSVenugopal Iyer tmp_addr = avl_find(&mcip->mci_v6_dyn_ip, addr, NULL); 10910dc2366fSVenugopal Iyer if (tmp_addr == addr) 10920dc2366fSVenugopal Iyer avl_remove(&mcip->mci_v6_dyn_ip, addr); 10930dc2366fSVenugopal Iyer } 10940dc2366fSVenugopal Iyer } 10950dc2366fSVenugopal Iyer 10960dc2366fSVenugopal Iyer /* 10970dc2366fSVenugopal Iyer * Find and remove a matching cid and associated addresses from 10980dc2366fSVenugopal Iyer * their respective tables. 10990dc2366fSVenugopal Iyer */ 11000dc2366fSVenugopal Iyer static void 11010dc2366fSVenugopal Iyer release_dhcpv6_cid(mac_client_impl_t *mcip, dhcpv6_cid_t *cid) 11020dc2366fSVenugopal Iyer { 11030dc2366fSVenugopal Iyer dhcpv6_cid_t *oldcid; 11040dc2366fSVenugopal Iyer 11050dc2366fSVenugopal Iyer ASSERT(MUTEX_HELD(&mcip->mci_protect_lock)); 11060dc2366fSVenugopal Iyer if ((oldcid = avl_find(&mcip->mci_v6_cid, cid, NULL)) == NULL) 11070dc2366fSVenugopal Iyer return; 11080dc2366fSVenugopal Iyer 11090dc2366fSVenugopal Iyer /* 11100dc2366fSVenugopal Iyer * Since cid belongs to a pending txn, it can't possibly be in 11110dc2366fSVenugopal Iyer * mci_v6_cid. Anything that's found must be an existing cid. 11120dc2366fSVenugopal Iyer */ 11130dc2366fSVenugopal Iyer ASSERT(oldcid != cid); 11140dc2366fSVenugopal Iyer remove_dhcpv6_cid(mcip, oldcid); 11150dc2366fSVenugopal Iyer free_dhcpv6_cid(oldcid); 11160dc2366fSVenugopal Iyer } 11170dc2366fSVenugopal Iyer 11180dc2366fSVenugopal Iyer /* 11190dc2366fSVenugopal Iyer * Insert cid into mci_v6_cid. 11200dc2366fSVenugopal Iyer */ 11210dc2366fSVenugopal Iyer static int 11220dc2366fSVenugopal Iyer insert_dhcpv6_cid(mac_client_impl_t *mcip, dhcpv6_cid_t *cid) 11230dc2366fSVenugopal Iyer { 11240dc2366fSVenugopal Iyer avl_index_t where; 11250dc2366fSVenugopal Iyer dhcpv6_addr_t *addr; 11260dc2366fSVenugopal Iyer 11270dc2366fSVenugopal Iyer ASSERT(MUTEX_HELD(&mcip->mci_protect_lock)); 11280dc2366fSVenugopal Iyer if (avl_find(&mcip->mci_v6_cid, cid, &where) != NULL) 11290dc2366fSVenugopal Iyer return (EEXIST); 11300dc2366fSVenugopal Iyer 11310dc2366fSVenugopal Iyer if (avl_numnodes(&mcip->mci_v6_cid) >= dhcp_max_completed_txn) { 11320dc2366fSVenugopal Iyer BUMP_STAT(mcip, dhcpdropped); 11330dc2366fSVenugopal Iyer return (EAGAIN); 11340dc2366fSVenugopal Iyer } 11350dc2366fSVenugopal Iyer avl_insert(&mcip->mci_v6_cid, cid, where); 11360dc2366fSVenugopal Iyer for (addr = cid->dc_addr; addr != NULL; addr = addr->da_next) { 11370dc2366fSVenugopal Iyer if (avl_find(&mcip->mci_v6_dyn_ip, addr, &where) != NULL) 11380dc2366fSVenugopal Iyer goto fail; 11390dc2366fSVenugopal Iyer 11400dc2366fSVenugopal Iyer avl_insert(&mcip->mci_v6_dyn_ip, addr, where); 11410dc2366fSVenugopal Iyer } 11420dc2366fSVenugopal Iyer return (0); 11430dc2366fSVenugopal Iyer 11440dc2366fSVenugopal Iyer fail: 11450dc2366fSVenugopal Iyer remove_dhcpv6_cid(mcip, cid); 11460dc2366fSVenugopal Iyer return (EEXIST); 11470dc2366fSVenugopal Iyer } 11480dc2366fSVenugopal Iyer 11490dc2366fSVenugopal Iyer /* 11500dc2366fSVenugopal Iyer * Check whether an IP address is in the dyn-ip table. 11510dc2366fSVenugopal Iyer */ 11520dc2366fSVenugopal Iyer static boolean_t 11530dc2366fSVenugopal Iyer check_dhcpv6_dyn_ip(mac_client_impl_t *mcip, in6_addr_t *addr) 11540dc2366fSVenugopal Iyer { 11550dc2366fSVenugopal Iyer dhcpv6_addr_t tmp_addr, *a; 11560dc2366fSVenugopal Iyer 11570dc2366fSVenugopal Iyer mutex_enter(&mcip->mci_protect_lock); 11580dc2366fSVenugopal Iyer bcopy(addr, &tmp_addr.da_addr, sizeof (in6_addr_t)); 11590dc2366fSVenugopal Iyer a = avl_find(&mcip->mci_v6_dyn_ip, &tmp_addr, NULL); 11600dc2366fSVenugopal Iyer mutex_exit(&mcip->mci_protect_lock); 11610dc2366fSVenugopal Iyer return (a != NULL); 11620dc2366fSVenugopal Iyer } 11630dc2366fSVenugopal Iyer 11640dc2366fSVenugopal Iyer static dhcpv6_txn_t * 11650dc2366fSVenugopal Iyer find_dhcpv6_pending_txn(mac_client_impl_t *mcip, uint32_t xid) 11660dc2366fSVenugopal Iyer { 11670dc2366fSVenugopal Iyer dhcpv6_txn_t tmp_txn; 11680dc2366fSVenugopal Iyer 11690dc2366fSVenugopal Iyer ASSERT(MUTEX_HELD(&mcip->mci_protect_lock)); 11700dc2366fSVenugopal Iyer tmp_txn.dt_xid = xid; 11710dc2366fSVenugopal Iyer return (avl_find(&mcip->mci_v6_pending_txn, &tmp_txn, NULL)); 11720dc2366fSVenugopal Iyer } 11730dc2366fSVenugopal Iyer 11740dc2366fSVenugopal Iyer static void 11750dc2366fSVenugopal Iyer remove_dhcpv6_pending_txn(mac_client_impl_t *mcip, dhcpv6_txn_t *txn) 11760dc2366fSVenugopal Iyer { 11770dc2366fSVenugopal Iyer ASSERT(MUTEX_HELD(&mcip->mci_protect_lock)); 11780dc2366fSVenugopal Iyer avl_remove(&mcip->mci_v6_pending_txn, txn); 11790dc2366fSVenugopal Iyer } 11800dc2366fSVenugopal Iyer 11810dc2366fSVenugopal Iyer static dhcpv6_txn_t * 11820dc2366fSVenugopal Iyer create_dhcpv6_txn(uint32_t xid, dhcpv6_cid_t *cid) 11830dc2366fSVenugopal Iyer { 11840dc2366fSVenugopal Iyer dhcpv6_txn_t *txn; 11850dc2366fSVenugopal Iyer 11860dc2366fSVenugopal Iyer if ((txn = kmem_zalloc(sizeof (dhcpv6_txn_t), KM_NOSLEEP)) == NULL) 11870dc2366fSVenugopal Iyer return (NULL); 11880dc2366fSVenugopal Iyer 11890dc2366fSVenugopal Iyer txn->dt_xid = xid; 11900dc2366fSVenugopal Iyer txn->dt_cid = cid; 11914d6a58d3SJosef 'Jeff' Sipek txn->dt_timestamp = gethrtime(); 11920dc2366fSVenugopal Iyer return (txn); 11930dc2366fSVenugopal Iyer } 11940dc2366fSVenugopal Iyer 11950dc2366fSVenugopal Iyer static void 11960dc2366fSVenugopal Iyer free_dhcpv6_txn(dhcpv6_txn_t *txn) 11970dc2366fSVenugopal Iyer { 11980dc2366fSVenugopal Iyer if (txn->dt_cid != NULL) 11990dc2366fSVenugopal Iyer free_dhcpv6_cid(txn->dt_cid); 12000dc2366fSVenugopal Iyer kmem_free(txn, sizeof (dhcpv6_txn_t)); 12010dc2366fSVenugopal Iyer } 12020dc2366fSVenugopal Iyer 12030dc2366fSVenugopal Iyer static int 12040dc2366fSVenugopal Iyer insert_dhcpv6_pending_txn(mac_client_impl_t *mcip, dhcpv6_txn_t *txn) 12050dc2366fSVenugopal Iyer { 12060dc2366fSVenugopal Iyer avl_index_t where; 12070dc2366fSVenugopal Iyer 12080dc2366fSVenugopal Iyer ASSERT(MUTEX_HELD(&mcip->mci_protect_lock)); 12090dc2366fSVenugopal Iyer if (avl_find(&mcip->mci_v6_pending_txn, txn, &where) != NULL) 12100dc2366fSVenugopal Iyer return (EEXIST); 12110dc2366fSVenugopal Iyer 12120dc2366fSVenugopal Iyer if (avl_numnodes(&mcip->mci_v6_pending_txn) >= dhcp_max_pending_txn) { 12130dc2366fSVenugopal Iyer BUMP_STAT(mcip, dhcpdropped); 12140dc2366fSVenugopal Iyer return (EAGAIN); 12150dc2366fSVenugopal Iyer } 12160dc2366fSVenugopal Iyer avl_insert(&mcip->mci_v6_pending_txn, txn, where); 12170dc2366fSVenugopal Iyer return (0); 12180dc2366fSVenugopal Iyer } 12190dc2366fSVenugopal Iyer 12200dc2366fSVenugopal Iyer /* 12210dc2366fSVenugopal Iyer * Clean up all v6 tables. 12220dc2366fSVenugopal Iyer */ 12230dc2366fSVenugopal Iyer static void 12240dc2366fSVenugopal Iyer flush_dhcpv6(mac_client_impl_t *mcip) 12250dc2366fSVenugopal Iyer { 12260dc2366fSVenugopal Iyer void *cookie = NULL; 12270dc2366fSVenugopal Iyer dhcpv6_cid_t *cid; 12280dc2366fSVenugopal Iyer dhcpv6_txn_t *txn; 12290dc2366fSVenugopal Iyer 12300dc2366fSVenugopal Iyer ASSERT(MUTEX_HELD(&mcip->mci_protect_lock)); 12310dc2366fSVenugopal Iyer while (avl_destroy_nodes(&mcip->mci_v6_dyn_ip, &cookie) != NULL) { 12320dc2366fSVenugopal Iyer } 12330dc2366fSVenugopal Iyer cookie = NULL; 12340dc2366fSVenugopal Iyer while ((cid = avl_destroy_nodes(&mcip->mci_v6_cid, &cookie)) != NULL) { 12350dc2366fSVenugopal Iyer free_dhcpv6_cid(cid); 12360dc2366fSVenugopal Iyer } 12370dc2366fSVenugopal Iyer cookie = NULL; 12380dc2366fSVenugopal Iyer while ((txn = avl_destroy_nodes(&mcip->mci_v6_pending_txn, 12390dc2366fSVenugopal Iyer &cookie)) != NULL) { 12400dc2366fSVenugopal Iyer free_dhcpv6_txn(txn); 12410dc2366fSVenugopal Iyer } 12420dc2366fSVenugopal Iyer } 12430dc2366fSVenugopal Iyer 1244*4e6f6c83SCody Peter Mello void 1245*4e6f6c83SCody Peter Mello flush_slaac(mac_client_impl_t *mcip) 1246*4e6f6c83SCody Peter Mello { 1247*4e6f6c83SCody Peter Mello void *cookie = NULL; 1248*4e6f6c83SCody Peter Mello slaac_addr_t *addr = NULL; 1249*4e6f6c83SCody Peter Mello 1250*4e6f6c83SCody Peter Mello while ((addr = avl_destroy_nodes(&mcip->mci_v6_slaac_ip, &cookie)) != 1251*4e6f6c83SCody Peter Mello NULL) { 1252*4e6f6c83SCody Peter Mello kmem_free(addr, sizeof (slaac_addr_t)); 1253*4e6f6c83SCody Peter Mello } 1254*4e6f6c83SCody Peter Mello } 1255*4e6f6c83SCody Peter Mello 12560dc2366fSVenugopal Iyer /* 12570dc2366fSVenugopal Iyer * Cleanup stale DHCPv6 transactions. 12580dc2366fSVenugopal Iyer */ 12590dc2366fSVenugopal Iyer static void 12600dc2366fSVenugopal Iyer txn_cleanup_v6(mac_client_impl_t *mcip) 12610dc2366fSVenugopal Iyer { 12620dc2366fSVenugopal Iyer dhcpv6_txn_t *txn, *next, *txn_list = NULL; 12630dc2366fSVenugopal Iyer 12640dc2366fSVenugopal Iyer /* 12650dc2366fSVenugopal Iyer * Find stale pending transactions and place them on a list 12660dc2366fSVenugopal Iyer * to be removed. 12670dc2366fSVenugopal Iyer */ 12680dc2366fSVenugopal Iyer for (txn = avl_first(&mcip->mci_v6_pending_txn); txn != NULL; 12690dc2366fSVenugopal Iyer txn = avl_walk(&mcip->mci_v6_pending_txn, txn, AVL_AFTER)) { 12704d6a58d3SJosef 'Jeff' Sipek if (gethrtime() - txn->dt_timestamp > txn_cleanup_interval) { 12710dc2366fSVenugopal Iyer DTRACE_PROBE2(found__expired__txn, 12720dc2366fSVenugopal Iyer mac_client_impl_t *, mcip, 12730dc2366fSVenugopal Iyer dhcpv6_txn_t *, txn); 12740dc2366fSVenugopal Iyer 12750dc2366fSVenugopal Iyer txn->dt_next = txn_list; 12760dc2366fSVenugopal Iyer txn_list = txn; 12770dc2366fSVenugopal Iyer } 12780dc2366fSVenugopal Iyer } 12790dc2366fSVenugopal Iyer 12800dc2366fSVenugopal Iyer /* 12810dc2366fSVenugopal Iyer * Remove and free stale pending transactions. 12820dc2366fSVenugopal Iyer * Release any existing cids matching the stale transactions. 12830dc2366fSVenugopal Iyer */ 12840dc2366fSVenugopal Iyer for (txn = txn_list; txn != NULL; txn = next) { 12850dc2366fSVenugopal Iyer avl_remove(&mcip->mci_v6_pending_txn, txn); 12860dc2366fSVenugopal Iyer release_dhcpv6_cid(mcip, txn->dt_cid); 12870dc2366fSVenugopal Iyer next = txn->dt_next; 12880dc2366fSVenugopal Iyer txn->dt_next = NULL; 12890dc2366fSVenugopal Iyer 12900dc2366fSVenugopal Iyer DTRACE_PROBE2(freeing__txn, mac_client_impl_t *, mcip, 12910dc2366fSVenugopal Iyer dhcpv6_txn_t *, txn); 12920dc2366fSVenugopal Iyer free_dhcpv6_txn(txn); 12930dc2366fSVenugopal Iyer } 12940dc2366fSVenugopal Iyer 12950dc2366fSVenugopal Iyer } 12960dc2366fSVenugopal Iyer 12970dc2366fSVenugopal Iyer /* 12980dc2366fSVenugopal Iyer * Core logic for intercepting outbound DHCPv6 packets. 12990dc2366fSVenugopal Iyer */ 1300550b6e40SSowmini Varadhan static boolean_t 13010dc2366fSVenugopal Iyer intercept_dhcpv6_outbound(mac_client_impl_t *mcip, ip6_t *ip6h, uchar_t *end) 13020dc2366fSVenugopal Iyer { 13030dc2366fSVenugopal Iyer dhcpv6_message_t *dh6; 13040dc2366fSVenugopal Iyer dhcpv6_txn_t *txn; 13050dc2366fSVenugopal Iyer dhcpv6_cid_t *cid = NULL; 13060dc2366fSVenugopal Iyer uint32_t xid; 13070dc2366fSVenugopal Iyer uint8_t mtype; 1308550b6e40SSowmini Varadhan mac_resource_props_t *mrp = MCIP_RESOURCE_PROPS(mcip); 13090dc2366fSVenugopal Iyer 13100dc2366fSVenugopal Iyer if (get_dhcpv6_info(ip6h, end, &dh6) != 0) 1311550b6e40SSowmini Varadhan return (B_TRUE); 1312550b6e40SSowmini Varadhan 1313550b6e40SSowmini Varadhan /* ip_nospoof/allowed-ips and DHCP are mutually exclusive by default */ 1314550b6e40SSowmini Varadhan if (allowed_ips_set(mrp, IPV6_VERSION)) 1315550b6e40SSowmini Varadhan return (B_FALSE); 13160dc2366fSVenugopal Iyer 1317146e34b6SCody Peter Mello /* 1318146e34b6SCody Peter Mello * We want to act on packets that result in DHCPv6 Reply messages, or 1319146e34b6SCody Peter Mello * on packets that give up an IPv6 address. For example, a Request or 1320146e34b6SCody Peter Mello * Solicit (w/ the Rapid Commit option) will cause the server to send a 1321146e34b6SCody Peter Mello * Reply, ending the transaction. 1322146e34b6SCody Peter Mello */ 13230dc2366fSVenugopal Iyer mtype = dh6->d6m_msg_type; 1324146e34b6SCody Peter Mello if (mtype != DHCPV6_MSG_SOLICIT && mtype != DHCPV6_MSG_REQUEST && 1325146e34b6SCody Peter Mello mtype != DHCPV6_MSG_RENEW && mtype != DHCPV6_MSG_REBIND && 1326146e34b6SCody Peter Mello mtype != DHCPV6_MSG_RELEASE) 1327550b6e40SSowmini Varadhan return (B_TRUE); 13280dc2366fSVenugopal Iyer 13290dc2366fSVenugopal Iyer if ((cid = create_dhcpv6_cid(dh6, end)) == NULL) 1330550b6e40SSowmini Varadhan return (B_TRUE); 13310dc2366fSVenugopal Iyer 13320dc2366fSVenugopal Iyer mutex_enter(&mcip->mci_protect_lock); 13330dc2366fSVenugopal Iyer if (mtype == DHCPV6_MSG_RELEASE) { 13340dc2366fSVenugopal Iyer release_dhcpv6_cid(mcip, cid); 13350dc2366fSVenugopal Iyer goto done; 13360dc2366fSVenugopal Iyer } 13370dc2366fSVenugopal Iyer xid = DHCPV6_GET_TRANSID(dh6); 13380dc2366fSVenugopal Iyer if ((txn = find_dhcpv6_pending_txn(mcip, xid)) != NULL) { 13390dc2366fSVenugopal Iyer DTRACE_PROBE2(update, mac_client_impl_t *, mcip, 13400dc2366fSVenugopal Iyer dhcpv6_txn_t *, txn); 13414d6a58d3SJosef 'Jeff' Sipek txn->dt_timestamp = gethrtime(); 13420dc2366fSVenugopal Iyer goto done; 13430dc2366fSVenugopal Iyer } 13440dc2366fSVenugopal Iyer if ((txn = create_dhcpv6_txn(xid, cid)) == NULL) 13450dc2366fSVenugopal Iyer goto done; 13460dc2366fSVenugopal Iyer 13470dc2366fSVenugopal Iyer cid = NULL; 13480dc2366fSVenugopal Iyer if (insert_dhcpv6_pending_txn(mcip, txn) != 0) { 13490dc2366fSVenugopal Iyer DTRACE_PROBE2(insert__failed, mac_client_impl_t *, mcip, 13500dc2366fSVenugopal Iyer dhcpv6_txn_t *, txn); 13510dc2366fSVenugopal Iyer free_dhcpv6_txn(txn); 13520dc2366fSVenugopal Iyer goto done; 13530dc2366fSVenugopal Iyer } 13540dc2366fSVenugopal Iyer start_txn_cleanup_timer(mcip); 13550dc2366fSVenugopal Iyer 13560dc2366fSVenugopal Iyer DTRACE_PROBE2(txn__pending, mac_client_impl_t *, mcip, 13570dc2366fSVenugopal Iyer dhcpv6_txn_t *, txn); 13580dc2366fSVenugopal Iyer 13590dc2366fSVenugopal Iyer done: 13600dc2366fSVenugopal Iyer if (cid != NULL) 13610dc2366fSVenugopal Iyer free_dhcpv6_cid(cid); 13620dc2366fSVenugopal Iyer 13630dc2366fSVenugopal Iyer mutex_exit(&mcip->mci_protect_lock); 1364550b6e40SSowmini Varadhan return (B_TRUE); 13650dc2366fSVenugopal Iyer } 13660dc2366fSVenugopal Iyer 13670dc2366fSVenugopal Iyer /* 13680dc2366fSVenugopal Iyer * Core logic for intercepting inbound DHCPv6 packets. 13690dc2366fSVenugopal Iyer */ 13700dc2366fSVenugopal Iyer static void 1371*4e6f6c83SCody Peter Mello intercept_dhcpv6_inbound(mac_client_impl_t *mcip, uchar_t *end, 1372*4e6f6c83SCody Peter Mello dhcpv6_message_t *dh6) 13730dc2366fSVenugopal Iyer { 13740dc2366fSVenugopal Iyer dhcpv6_txn_t *txn; 13750dc2366fSVenugopal Iyer uint32_t xid; 13760dc2366fSVenugopal Iyer uint8_t mtype; 13770dc2366fSVenugopal Iyer uint16_t status; 13780dc2366fSVenugopal Iyer 13790dc2366fSVenugopal Iyer mtype = dh6->d6m_msg_type; 13800dc2366fSVenugopal Iyer if (mtype != DHCPV6_MSG_REPLY) 13810dc2366fSVenugopal Iyer return; 13820dc2366fSVenugopal Iyer 13830dc2366fSVenugopal Iyer mutex_enter(&mcip->mci_protect_lock); 13840dc2366fSVenugopal Iyer xid = DHCPV6_GET_TRANSID(dh6); 13850dc2366fSVenugopal Iyer if ((txn = find_dhcpv6_pending_txn(mcip, xid)) == NULL) { 13860dc2366fSVenugopal Iyer DTRACE_PROBE2(txn__not__found, mac_client_impl_t *, mcip, 13870dc2366fSVenugopal Iyer dhcpv6_message_t *, dh6); 13880dc2366fSVenugopal Iyer goto done; 13890dc2366fSVenugopal Iyer } 13900dc2366fSVenugopal Iyer remove_dhcpv6_pending_txn(mcip, txn); 13910dc2366fSVenugopal Iyer release_dhcpv6_cid(mcip, txn->dt_cid); 13920dc2366fSVenugopal Iyer 13930dc2366fSVenugopal Iyer if (get_dhcpv6_status(dh6, end, &status) != 0 || 13940dc2366fSVenugopal Iyer status != DHCPV6_STAT_SUCCESS) { 13950dc2366fSVenugopal Iyer DTRACE_PROBE2(error__status, mac_client_impl_t *, mcip, 13960dc2366fSVenugopal Iyer dhcpv6_txn_t *, txn); 13970dc2366fSVenugopal Iyer goto done; 13980dc2366fSVenugopal Iyer } 13990dc2366fSVenugopal Iyer if (get_dhcpv6_addrs(dh6, end, txn->dt_cid) != 0) { 14000dc2366fSVenugopal Iyer DTRACE_PROBE2(no__addrs, mac_client_impl_t *, mcip, 14010dc2366fSVenugopal Iyer dhcpv6_txn_t *, txn); 14020dc2366fSVenugopal Iyer goto done; 14030dc2366fSVenugopal Iyer } 14040dc2366fSVenugopal Iyer if (insert_dhcpv6_cid(mcip, txn->dt_cid) != 0) { 14050dc2366fSVenugopal Iyer DTRACE_PROBE2(insert__failed, mac_client_impl_t *, mcip, 14060dc2366fSVenugopal Iyer dhcpv6_txn_t *, txn); 14070dc2366fSVenugopal Iyer goto done; 14080dc2366fSVenugopal Iyer } 14090dc2366fSVenugopal Iyer DTRACE_PROBE2(txn__completed, mac_client_impl_t *, mcip, 14100dc2366fSVenugopal Iyer dhcpv6_txn_t *, txn); 14110dc2366fSVenugopal Iyer 14120dc2366fSVenugopal Iyer txn->dt_cid = NULL; 14130dc2366fSVenugopal Iyer 14140dc2366fSVenugopal Iyer done: 14150dc2366fSVenugopal Iyer if (txn != NULL) 14160dc2366fSVenugopal Iyer free_dhcpv6_txn(txn); 14170dc2366fSVenugopal Iyer mutex_exit(&mcip->mci_protect_lock); 14180dc2366fSVenugopal Iyer } 14190dc2366fSVenugopal Iyer 14200dc2366fSVenugopal Iyer /* 1421*4e6f6c83SCody Peter Mello * Check whether an IP address is in the SLAAC table. 1422*4e6f6c83SCody Peter Mello */ 1423*4e6f6c83SCody Peter Mello static boolean_t 1424*4e6f6c83SCody Peter Mello check_slaac_ip(mac_client_impl_t *mcip, in6_addr_t *addr) 1425*4e6f6c83SCody Peter Mello { 1426*4e6f6c83SCody Peter Mello slaac_addr_t tmp_addr, *a; 1427*4e6f6c83SCody Peter Mello 1428*4e6f6c83SCody Peter Mello mutex_enter(&mcip->mci_protect_lock); 1429*4e6f6c83SCody Peter Mello bcopy(addr, &tmp_addr.sla_addr, sizeof (in6_addr_t)); 1430*4e6f6c83SCody Peter Mello a = avl_find(&mcip->mci_v6_slaac_ip, &tmp_addr, NULL); 1431*4e6f6c83SCody Peter Mello mutex_exit(&mcip->mci_protect_lock); 1432*4e6f6c83SCody Peter Mello return (a != NULL); 1433*4e6f6c83SCody Peter Mello } 1434*4e6f6c83SCody Peter Mello 1435*4e6f6c83SCody Peter Mello static boolean_t 1436*4e6f6c83SCody Peter Mello insert_slaac_ip(avl_tree_t *tree, in6_addr_t *token, slaac_addr_t *addr) 1437*4e6f6c83SCody Peter Mello { 1438*4e6f6c83SCody Peter Mello uint_t i; 1439*4e6f6c83SCody Peter Mello avl_index_t where; 1440*4e6f6c83SCody Peter Mello in6_addr_t *prefix = &addr->sla_prefix; 1441*4e6f6c83SCody Peter Mello in6_addr_t *in6p = &addr->sla_addr; 1442*4e6f6c83SCody Peter Mello 1443*4e6f6c83SCody Peter Mello bcopy(prefix, in6p, sizeof (struct in6_addr)); 1444*4e6f6c83SCody Peter Mello 1445*4e6f6c83SCody Peter Mello for (i = 0; i < 4; i++) { 1446*4e6f6c83SCody Peter Mello in6p->s6_addr32[i] = token->s6_addr32[i] | 1447*4e6f6c83SCody Peter Mello in6p->s6_addr32[i]; 1448*4e6f6c83SCody Peter Mello } 1449*4e6f6c83SCody Peter Mello 1450*4e6f6c83SCody Peter Mello DTRACE_PROBE1(generated__addr, in6_addr_t *, in6p); 1451*4e6f6c83SCody Peter Mello 1452*4e6f6c83SCody Peter Mello if (avl_find(tree, addr, &where) != NULL) 1453*4e6f6c83SCody Peter Mello return (B_FALSE); 1454*4e6f6c83SCody Peter Mello 1455*4e6f6c83SCody Peter Mello avl_insert(tree, addr, where); 1456*4e6f6c83SCody Peter Mello return (B_TRUE); 1457*4e6f6c83SCody Peter Mello } 1458*4e6f6c83SCody Peter Mello 1459*4e6f6c83SCody Peter Mello static void 1460*4e6f6c83SCody Peter Mello insert_slaac_prefix(mac_client_impl_t *mcip, nd_opt_prefix_info_t *po) 1461*4e6f6c83SCody Peter Mello { 1462*4e6f6c83SCody Peter Mello slaac_addr_t *addr = NULL; 1463*4e6f6c83SCody Peter Mello in6_addr_t *token = &mcip->mci_v6_mac_token; 1464*4e6f6c83SCody Peter Mello 1465*4e6f6c83SCody Peter Mello ASSERT(MUTEX_HELD(&mcip->mci_protect_lock)); 1466*4e6f6c83SCody Peter Mello 1467*4e6f6c83SCody Peter Mello if (avl_numnodes(&mcip->mci_v6_slaac_ip) >= slaac_max_allowed) { 1468*4e6f6c83SCody Peter Mello DTRACE_PROBE(limit__reached); 1469*4e6f6c83SCody Peter Mello return; 1470*4e6f6c83SCody Peter Mello } 1471*4e6f6c83SCody Peter Mello 1472*4e6f6c83SCody Peter Mello if ((addr = kmem_zalloc(sizeof (slaac_addr_t), 1473*4e6f6c83SCody Peter Mello KM_NOSLEEP | KM_NORMALPRI)) == NULL) 1474*4e6f6c83SCody Peter Mello return; 1475*4e6f6c83SCody Peter Mello 1476*4e6f6c83SCody Peter Mello bcopy(&po->nd_opt_pi_prefix, &addr->sla_prefix, 1477*4e6f6c83SCody Peter Mello sizeof (struct in6_addr)); 1478*4e6f6c83SCody Peter Mello 1479*4e6f6c83SCody Peter Mello if (!insert_slaac_ip(&mcip->mci_v6_slaac_ip, token, addr)) { 1480*4e6f6c83SCody Peter Mello kmem_free(addr, sizeof (slaac_addr_t)); 1481*4e6f6c83SCody Peter Mello } 1482*4e6f6c83SCody Peter Mello } 1483*4e6f6c83SCody Peter Mello 1484*4e6f6c83SCody Peter Mello static void 1485*4e6f6c83SCody Peter Mello intercept_prefix_info(mac_client_impl_t *mcip, nd_opt_prefix_info_t *po) 1486*4e6f6c83SCody Peter Mello { 1487*4e6f6c83SCody Peter Mello if (8 * po->nd_opt_pi_len != sizeof (nd_opt_prefix_info_t)) { 1488*4e6f6c83SCody Peter Mello DTRACE_PROBE(invalid__length); 1489*4e6f6c83SCody Peter Mello return; 1490*4e6f6c83SCody Peter Mello } 1491*4e6f6c83SCody Peter Mello 1492*4e6f6c83SCody Peter Mello if (po->nd_opt_pi_prefix_len > 128) { 1493*4e6f6c83SCody Peter Mello DTRACE_PROBE(invalid__plen); 1494*4e6f6c83SCody Peter Mello return; 1495*4e6f6c83SCody Peter Mello } 1496*4e6f6c83SCody Peter Mello 1497*4e6f6c83SCody Peter Mello if (IN6_IS_ADDR_LINKLOCAL(&po->nd_opt_pi_prefix)) { 1498*4e6f6c83SCody Peter Mello DTRACE_PROBE(link__local); 1499*4e6f6c83SCody Peter Mello return; 1500*4e6f6c83SCody Peter Mello } 1501*4e6f6c83SCody Peter Mello 1502*4e6f6c83SCody Peter Mello if ((po->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_AUTO) == 0) 1503*4e6f6c83SCody Peter Mello return; 1504*4e6f6c83SCody Peter Mello 1505*4e6f6c83SCody Peter Mello mutex_enter(&mcip->mci_protect_lock); 1506*4e6f6c83SCody Peter Mello insert_slaac_prefix(mcip, po); 1507*4e6f6c83SCody Peter Mello mutex_exit(&mcip->mci_protect_lock); 1508*4e6f6c83SCody Peter Mello } 1509*4e6f6c83SCody Peter Mello 1510*4e6f6c83SCody Peter Mello /* 1511*4e6f6c83SCody Peter Mello * If we receive a Router Advertisement carrying prefix information and 1512*4e6f6c83SCody Peter Mello * indicating that SLAAC should be performed, then track the prefix. 1513*4e6f6c83SCody Peter Mello */ 1514*4e6f6c83SCody Peter Mello static void 1515*4e6f6c83SCody Peter Mello intercept_ra_inbound(mac_client_impl_t *mcip, ip6_t *ip6h, uchar_t *end, 1516*4e6f6c83SCody Peter Mello nd_router_advert_t *ra) 1517*4e6f6c83SCody Peter Mello { 1518*4e6f6c83SCody Peter Mello struct nd_opt_hdr *opt; 1519*4e6f6c83SCody Peter Mello int len, optlen; 1520*4e6f6c83SCody Peter Mello 1521*4e6f6c83SCody Peter Mello if (ip6h->ip6_hlim != 255) { 1522*4e6f6c83SCody Peter Mello DTRACE_PROBE1(invalid__hoplimit, uint8_t, ip6h->ip6_hlim); 1523*4e6f6c83SCody Peter Mello return; 1524*4e6f6c83SCody Peter Mello } 1525*4e6f6c83SCody Peter Mello 1526*4e6f6c83SCody Peter Mello len = ip6h->ip6_plen - sizeof (nd_router_advert_t); 1527*4e6f6c83SCody Peter Mello opt = (struct nd_opt_hdr *)&ra[1]; 1528*4e6f6c83SCody Peter Mello while (len >= sizeof (struct nd_opt_hdr) && 1529*4e6f6c83SCody Peter Mello ((uchar_t *)opt + sizeof (struct nd_opt_hdr)) <= end) { 1530*4e6f6c83SCody Peter Mello optlen = opt->nd_opt_len * 8; 1531*4e6f6c83SCody Peter Mello 1532*4e6f6c83SCody Peter Mello if (optlen < sizeof (struct nd_opt_hdr) || 1533*4e6f6c83SCody Peter Mello ((uchar_t *)opt + optlen) > end) { 1534*4e6f6c83SCody Peter Mello DTRACE_PROBE(invalid__length); 1535*4e6f6c83SCody Peter Mello return; 1536*4e6f6c83SCody Peter Mello } 1537*4e6f6c83SCody Peter Mello 1538*4e6f6c83SCody Peter Mello if (opt->nd_opt_type == ND_OPT_PREFIX_INFORMATION) { 1539*4e6f6c83SCody Peter Mello intercept_prefix_info(mcip, 1540*4e6f6c83SCody Peter Mello (nd_opt_prefix_info_t *)opt); 1541*4e6f6c83SCody Peter Mello } 1542*4e6f6c83SCody Peter Mello 1543*4e6f6c83SCody Peter Mello opt = (struct nd_opt_hdr *)((char *)opt + optlen); 1544*4e6f6c83SCody Peter Mello len -= optlen; 1545*4e6f6c83SCody Peter Mello } 1546*4e6f6c83SCody Peter Mello } 1547*4e6f6c83SCody Peter Mello 1548*4e6f6c83SCody Peter Mello /* 15490dc2366fSVenugopal Iyer * Timer for cleaning up stale transactions. 15500dc2366fSVenugopal Iyer */ 15510dc2366fSVenugopal Iyer static void 15520dc2366fSVenugopal Iyer txn_cleanup_timer(void *arg) 15530dc2366fSVenugopal Iyer { 15540dc2366fSVenugopal Iyer mac_client_impl_t *mcip = arg; 15550dc2366fSVenugopal Iyer 15560dc2366fSVenugopal Iyer mutex_enter(&mcip->mci_protect_lock); 15570dc2366fSVenugopal Iyer if (mcip->mci_txn_cleanup_tid == 0) { 15580dc2366fSVenugopal Iyer /* do nothing if timer got cancelled */ 15590dc2366fSVenugopal Iyer mutex_exit(&mcip->mci_protect_lock); 15600dc2366fSVenugopal Iyer return; 15610dc2366fSVenugopal Iyer } 15620dc2366fSVenugopal Iyer mcip->mci_txn_cleanup_tid = 0; 15630dc2366fSVenugopal Iyer 15640dc2366fSVenugopal Iyer txn_cleanup_v4(mcip); 15650dc2366fSVenugopal Iyer txn_cleanup_v6(mcip); 15660dc2366fSVenugopal Iyer 15670dc2366fSVenugopal Iyer /* 15680dc2366fSVenugopal Iyer * Restart timer if pending transactions still exist. 15690dc2366fSVenugopal Iyer */ 15700dc2366fSVenugopal Iyer if (!avl_is_empty(&mcip->mci_v4_pending_txn) || 15710dc2366fSVenugopal Iyer !avl_is_empty(&mcip->mci_v6_pending_txn)) { 15720dc2366fSVenugopal Iyer DTRACE_PROBE1(restarting__timer, mac_client_impl_t *, mcip); 15730dc2366fSVenugopal Iyer 15740dc2366fSVenugopal Iyer mcip->mci_txn_cleanup_tid = timeout(txn_cleanup_timer, mcip, 15754d6a58d3SJosef 'Jeff' Sipek drv_usectohz(txn_cleanup_interval / (NANOSEC / MICROSEC))); 15760dc2366fSVenugopal Iyer } 15770dc2366fSVenugopal Iyer mutex_exit(&mcip->mci_protect_lock); 15780dc2366fSVenugopal Iyer } 15790dc2366fSVenugopal Iyer 15800dc2366fSVenugopal Iyer static void 15810dc2366fSVenugopal Iyer start_txn_cleanup_timer(mac_client_impl_t *mcip) 15820dc2366fSVenugopal Iyer { 15830dc2366fSVenugopal Iyer ASSERT(MUTEX_HELD(&mcip->mci_protect_lock)); 15840dc2366fSVenugopal Iyer if (mcip->mci_txn_cleanup_tid == 0) { 15850dc2366fSVenugopal Iyer mcip->mci_txn_cleanup_tid = timeout(txn_cleanup_timer, mcip, 15864d6a58d3SJosef 'Jeff' Sipek drv_usectohz(txn_cleanup_interval / (NANOSEC / MICROSEC))); 15870dc2366fSVenugopal Iyer } 15880dc2366fSVenugopal Iyer } 15890dc2366fSVenugopal Iyer 15900dc2366fSVenugopal Iyer static void 15910dc2366fSVenugopal Iyer cancel_txn_cleanup_timer(mac_client_impl_t *mcip) 15920dc2366fSVenugopal Iyer { 15930dc2366fSVenugopal Iyer timeout_id_t tid; 15940dc2366fSVenugopal Iyer 15950dc2366fSVenugopal Iyer ASSERT(MUTEX_HELD(&mcip->mci_protect_lock)); 15960dc2366fSVenugopal Iyer 15970dc2366fSVenugopal Iyer /* 15980dc2366fSVenugopal Iyer * This needs to be a while loop because the timer could get 15990dc2366fSVenugopal Iyer * rearmed during untimeout(). 16000dc2366fSVenugopal Iyer */ 16010dc2366fSVenugopal Iyer while ((tid = mcip->mci_txn_cleanup_tid) != 0) { 16020dc2366fSVenugopal Iyer mcip->mci_txn_cleanup_tid = 0; 16030dc2366fSVenugopal Iyer mutex_exit(&mcip->mci_protect_lock); 16040dc2366fSVenugopal Iyer (void) untimeout(tid); 16050dc2366fSVenugopal Iyer mutex_enter(&mcip->mci_protect_lock); 16060dc2366fSVenugopal Iyer } 16070dc2366fSVenugopal Iyer } 16080dc2366fSVenugopal Iyer 16090dc2366fSVenugopal Iyer /* 16100dc2366fSVenugopal Iyer * Get the start/end pointers of an L3 packet and also do pullup if needed. 16110dc2366fSVenugopal Iyer * pulled-up packet needs to be freed by the caller. 16120dc2366fSVenugopal Iyer */ 16130dc2366fSVenugopal Iyer static int 16140dc2366fSVenugopal Iyer get_l3_info(mblk_t *mp, size_t hdrsize, uchar_t **start, uchar_t **end, 16150dc2366fSVenugopal Iyer mblk_t **nmp) 16160dc2366fSVenugopal Iyer { 16170dc2366fSVenugopal Iyer uchar_t *s, *e; 16180dc2366fSVenugopal Iyer mblk_t *newmp = NULL; 16190dc2366fSVenugopal Iyer 16200dc2366fSVenugopal Iyer /* 16210dc2366fSVenugopal Iyer * Pullup if necessary but reject packets that do not have 16220dc2366fSVenugopal Iyer * a proper mac header. 16230dc2366fSVenugopal Iyer */ 16240dc2366fSVenugopal Iyer s = mp->b_rptr + hdrsize; 16250dc2366fSVenugopal Iyer e = mp->b_wptr; 16260dc2366fSVenugopal Iyer 16270dc2366fSVenugopal Iyer if (s > mp->b_wptr) 16280dc2366fSVenugopal Iyer return (EINVAL); 16290dc2366fSVenugopal Iyer 16300dc2366fSVenugopal Iyer if (!OK_32PTR(s) || mp->b_cont != NULL) { 16310dc2366fSVenugopal Iyer /* 16320dc2366fSVenugopal Iyer * Temporarily adjust mp->b_rptr to ensure proper 16330dc2366fSVenugopal Iyer * alignment of IP header in newmp. 16340dc2366fSVenugopal Iyer */ 16350dc2366fSVenugopal Iyer DTRACE_PROBE1(pullup__needed, mblk_t *, mp); 16360dc2366fSVenugopal Iyer 16370dc2366fSVenugopal Iyer mp->b_rptr += hdrsize; 16380dc2366fSVenugopal Iyer newmp = msgpullup(mp, -1); 16390dc2366fSVenugopal Iyer mp->b_rptr -= hdrsize; 16400dc2366fSVenugopal Iyer 16410dc2366fSVenugopal Iyer if (newmp == NULL) 16420dc2366fSVenugopal Iyer return (ENOMEM); 16430dc2366fSVenugopal Iyer 16440dc2366fSVenugopal Iyer s = newmp->b_rptr; 16450dc2366fSVenugopal Iyer e = newmp->b_wptr; 16460dc2366fSVenugopal Iyer } 16470dc2366fSVenugopal Iyer 16480dc2366fSVenugopal Iyer *start = s; 16490dc2366fSVenugopal Iyer *end = e; 16500dc2366fSVenugopal Iyer *nmp = newmp; 16510dc2366fSVenugopal Iyer return (0); 16520dc2366fSVenugopal Iyer } 16530dc2366fSVenugopal Iyer 16540dc2366fSVenugopal Iyer void 1655*4e6f6c83SCody Peter Mello mac_protect_intercept_dynamic_one(mac_client_impl_t *mcip, mblk_t *mp) 16560dc2366fSVenugopal Iyer { 16570dc2366fSVenugopal Iyer mac_impl_t *mip = mcip->mci_mip; 16580dc2366fSVenugopal Iyer uchar_t *start, *end; 16590dc2366fSVenugopal Iyer mblk_t *nmp = NULL; 16600dc2366fSVenugopal Iyer mac_header_info_t mhi; 16610dc2366fSVenugopal Iyer int err; 16620dc2366fSVenugopal Iyer 16630dc2366fSVenugopal Iyer err = mac_vlan_header_info((mac_handle_t)mip, mp, &mhi); 16640dc2366fSVenugopal Iyer if (err != 0) { 16650dc2366fSVenugopal Iyer DTRACE_PROBE2(invalid__header, mac_client_impl_t *, mcip, 16660dc2366fSVenugopal Iyer mblk_t *, mp); 16670dc2366fSVenugopal Iyer return; 16680dc2366fSVenugopal Iyer } 16690dc2366fSVenugopal Iyer 16700dc2366fSVenugopal Iyer err = get_l3_info(mp, mhi.mhi_hdrsize, &start, &end, &nmp); 16710dc2366fSVenugopal Iyer if (err != 0) { 16720dc2366fSVenugopal Iyer DTRACE_PROBE2(invalid__l3, mac_client_impl_t *, mcip, 16730dc2366fSVenugopal Iyer mblk_t *, mp); 16740dc2366fSVenugopal Iyer return; 16750dc2366fSVenugopal Iyer } 16760dc2366fSVenugopal Iyer 16770dc2366fSVenugopal Iyer switch (mhi.mhi_bindsap) { 16780dc2366fSVenugopal Iyer case ETHERTYPE_IP: { 1679*4e6f6c83SCody Peter Mello struct dhcp *dh4; 16800dc2366fSVenugopal Iyer ipha_t *ipha = (ipha_t *)start; 16810dc2366fSVenugopal Iyer 16820dc2366fSVenugopal Iyer if (start + sizeof (ipha_t) > end) 16830dc2366fSVenugopal Iyer return; 16840dc2366fSVenugopal Iyer 1685*4e6f6c83SCody Peter Mello if (get_dhcpv4_info(ipha, end, &dh4) == 0) { 1686*4e6f6c83SCody Peter Mello intercept_dhcpv4_inbound(mcip, end, dh4); 1687*4e6f6c83SCody Peter Mello } 16880dc2366fSVenugopal Iyer break; 16890dc2366fSVenugopal Iyer } 16900dc2366fSVenugopal Iyer case ETHERTYPE_IPV6: { 1691*4e6f6c83SCody Peter Mello dhcpv6_message_t *dh6; 1692*4e6f6c83SCody Peter Mello nd_router_advert_t *ra; 16930dc2366fSVenugopal Iyer ip6_t *ip6h = (ip6_t *)start; 16940dc2366fSVenugopal Iyer 16950dc2366fSVenugopal Iyer if (start + sizeof (ip6_t) > end) 16960dc2366fSVenugopal Iyer return; 16970dc2366fSVenugopal Iyer 1698*4e6f6c83SCody Peter Mello if (get_dhcpv6_info(ip6h, end, &dh6) == 0) { 1699*4e6f6c83SCody Peter Mello intercept_dhcpv6_inbound(mcip, end, dh6); 1700*4e6f6c83SCody Peter Mello } else if (get_ra_info(ip6h, end, &ra) == 0) { 1701*4e6f6c83SCody Peter Mello intercept_ra_inbound(mcip, ip6h, end, ra); 1702*4e6f6c83SCody Peter Mello } 1703*4e6f6c83SCody Peter Mello 17040dc2366fSVenugopal Iyer break; 17050dc2366fSVenugopal Iyer } 17060dc2366fSVenugopal Iyer } 17070dc2366fSVenugopal Iyer freemsg(nmp); 17080dc2366fSVenugopal Iyer } 17090dc2366fSVenugopal Iyer 17100dc2366fSVenugopal Iyer void 1711*4e6f6c83SCody Peter Mello mac_protect_intercept_dynamic(mac_client_impl_t *mcip, mblk_t *mp) 17120dc2366fSVenugopal Iyer { 17130dc2366fSVenugopal Iyer /* 17140dc2366fSVenugopal Iyer * Skip checks if we are part of an aggr. 17150dc2366fSVenugopal Iyer */ 17160dc2366fSVenugopal Iyer if ((mcip->mci_state_flags & MCIS_IS_AGGR_PORT) != 0) 17170dc2366fSVenugopal Iyer return; 17180dc2366fSVenugopal Iyer 17190dc2366fSVenugopal Iyer for (; mp != NULL; mp = mp->b_next) 1720*4e6f6c83SCody Peter Mello mac_protect_intercept_dynamic_one(mcip, mp); 17210dc2366fSVenugopal Iyer } 17220dc2366fSVenugopal Iyer 17230dc2366fSVenugopal Iyer void 1724*4e6f6c83SCody Peter Mello mac_protect_flush_dynamic(mac_client_impl_t *mcip) 17250dc2366fSVenugopal Iyer { 17260dc2366fSVenugopal Iyer mutex_enter(&mcip->mci_protect_lock); 17270dc2366fSVenugopal Iyer flush_dhcpv4(mcip); 17280dc2366fSVenugopal Iyer flush_dhcpv6(mcip); 1729*4e6f6c83SCody Peter Mello flush_slaac(mcip); 17300dc2366fSVenugopal Iyer mutex_exit(&mcip->mci_protect_lock); 17310dc2366fSVenugopal Iyer } 17320dc2366fSVenugopal Iyer 17330dc2366fSVenugopal Iyer void 17340dc2366fSVenugopal Iyer mac_protect_cancel_timer(mac_client_impl_t *mcip) 17350dc2366fSVenugopal Iyer { 17360dc2366fSVenugopal Iyer mutex_enter(&mcip->mci_protect_lock); 17370dc2366fSVenugopal Iyer cancel_txn_cleanup_timer(mcip); 17380dc2366fSVenugopal Iyer mutex_exit(&mcip->mci_protect_lock); 17390dc2366fSVenugopal Iyer } 17400dc2366fSVenugopal Iyer 17410dc2366fSVenugopal Iyer /* 17420dc2366fSVenugopal Iyer * Check if addr is in the 'allowed-ips' list. 17430dc2366fSVenugopal Iyer */ 17440dc2366fSVenugopal Iyer 17450dc2366fSVenugopal Iyer /* ARGSUSED */ 17460dc2366fSVenugopal Iyer static boolean_t 17470dc2366fSVenugopal Iyer ipnospoof_check_v4(mac_client_impl_t *mcip, mac_protect_t *protect, 17480dc2366fSVenugopal Iyer ipaddr_t *addr) 174925ec3e3dSEric Cheng { 175025ec3e3dSEric Cheng uint_t i; 175125ec3e3dSEric Cheng 175225ec3e3dSEric Cheng /* 17530dc2366fSVenugopal Iyer * The unspecified address is allowed. 175425ec3e3dSEric Cheng */ 17550dc2366fSVenugopal Iyer if (*addr == INADDR_ANY) 175625ec3e3dSEric Cheng return (B_TRUE); 175725ec3e3dSEric Cheng 175825ec3e3dSEric Cheng for (i = 0; i < protect->mp_ipaddrcnt; i++) { 17590dc2366fSVenugopal Iyer mac_ipaddr_t *v4addr = &protect->mp_ipaddrs[i]; 17600dc2366fSVenugopal Iyer 1761e03914f9SRobert Mustacchi if (v4addr->ip_version == IPV4_VERSION) { 1762e03914f9SRobert Mustacchi uint32_t mask; 1763e03914f9SRobert Mustacchi 1764e03914f9SRobert Mustacchi /* LINTED E_SUSPICIOUS_COMPARISON */ 1765e03914f9SRobert Mustacchi ASSERT(v4addr->ip_netmask >= 0 && 1766e03914f9SRobert Mustacchi v4addr->ip_netmask <= 32); 1767e03914f9SRobert Mustacchi mask = 0xFFFFFFFFu << (32 - v4addr->ip_netmask); 1768e03914f9SRobert Mustacchi /* 1769e03914f9SRobert Mustacchi * Since we have a netmask we know this entry 1770e03914f9SRobert Mustacchi * signifies the entire subnet. Check if the 1771e03914f9SRobert Mustacchi * given address is on the subnet. 1772e03914f9SRobert Mustacchi */ 1773e03914f9SRobert Mustacchi if (htonl(V4_PART_OF_V6(v4addr->ip_addr)) == 1774e03914f9SRobert Mustacchi (htonl(*addr) & mask)) 177525ec3e3dSEric Cheng return (B_TRUE); 177625ec3e3dSEric Cheng } 1777e03914f9SRobert Mustacchi } 1778550b6e40SSowmini Varadhan return (protect->mp_ipaddrcnt == 0 ? 1779550b6e40SSowmini Varadhan check_dhcpv4_dyn_ip(mcip, *addr) : B_FALSE); 17800dc2366fSVenugopal Iyer } 17810dc2366fSVenugopal Iyer 17820dc2366fSVenugopal Iyer static boolean_t 17830dc2366fSVenugopal Iyer ipnospoof_check_v6(mac_client_impl_t *mcip, mac_protect_t *protect, 17840dc2366fSVenugopal Iyer in6_addr_t *addr) 17850dc2366fSVenugopal Iyer { 17860dc2366fSVenugopal Iyer uint_t i; 17870dc2366fSVenugopal Iyer 17880dc2366fSVenugopal Iyer /* 17890dc2366fSVenugopal Iyer * The unspecified address and the v6 link local address are allowed. 17900dc2366fSVenugopal Iyer */ 17910dc2366fSVenugopal Iyer if (IN6_IS_ADDR_UNSPECIFIED(addr) || 17920dc2366fSVenugopal Iyer ((mcip->mci_protect_flags & MPT_FLAG_V6_LOCAL_ADDR_SET) != 0 && 17930dc2366fSVenugopal Iyer IN6_ARE_ADDR_EQUAL(&mcip->mci_v6_local_addr, addr))) 17940dc2366fSVenugopal Iyer return (B_TRUE); 17950dc2366fSVenugopal Iyer 17960dc2366fSVenugopal Iyer 17970dc2366fSVenugopal Iyer for (i = 0; i < protect->mp_ipaddrcnt; i++) { 17980dc2366fSVenugopal Iyer mac_ipaddr_t *v6addr = &protect->mp_ipaddrs[i]; 17990dc2366fSVenugopal Iyer 18000dc2366fSVenugopal Iyer if (v6addr->ip_version == IPV6_VERSION && 1801e03914f9SRobert Mustacchi /* LINTED E_SUSPICIOUS_COMPARISON */ 1802e03914f9SRobert Mustacchi IN6_ARE_PREFIXEDADDR_EQUAL(&v6addr->ip_addr, addr, 1803e03914f9SRobert Mustacchi v6addr->ip_netmask)) 18040dc2366fSVenugopal Iyer return (B_TRUE); 18050dc2366fSVenugopal Iyer } 1806*4e6f6c83SCody Peter Mello 1807*4e6f6c83SCody Peter Mello if (protect->mp_ipaddrcnt == 0) { 1808*4e6f6c83SCody Peter Mello return (check_slaac_ip(mcip, addr) || 1809*4e6f6c83SCody Peter Mello check_dhcpv6_dyn_ip(mcip, addr)); 1810*4e6f6c83SCody Peter Mello } else { 1811*4e6f6c83SCody Peter Mello return (B_FALSE); 1812*4e6f6c83SCody Peter Mello } 181325ec3e3dSEric Cheng } 181425ec3e3dSEric Cheng 181525ec3e3dSEric Cheng /* 18160dc2366fSVenugopal Iyer * Checks various fields within an IPv6 NDP packet. 18170dc2366fSVenugopal Iyer */ 18180dc2366fSVenugopal Iyer static boolean_t 18190dc2366fSVenugopal Iyer ipnospoof_check_ndp(mac_client_impl_t *mcip, mac_protect_t *protect, 18200dc2366fSVenugopal Iyer ip6_t *ip6h, uchar_t *end) 18210dc2366fSVenugopal Iyer { 18220dc2366fSVenugopal Iyer icmp6_t *icmp_nd = (icmp6_t *)&ip6h[1]; 18230dc2366fSVenugopal Iyer int hdrlen, optlen, opttype, len; 18240dc2366fSVenugopal Iyer uint_t addrlen, maclen; 18250dc2366fSVenugopal Iyer uint8_t type; 18260dc2366fSVenugopal Iyer nd_opt_hdr_t *opt; 18270dc2366fSVenugopal Iyer struct nd_opt_lla *lla = NULL; 18280dc2366fSVenugopal Iyer 18290dc2366fSVenugopal Iyer /* 18300dc2366fSVenugopal Iyer * NDP packets do not have extension headers so the ICMPv6 header 18310dc2366fSVenugopal Iyer * must immediately follow the IPv6 header. 18320dc2366fSVenugopal Iyer */ 18330dc2366fSVenugopal Iyer if (ip6h->ip6_nxt != IPPROTO_ICMPV6) 18340dc2366fSVenugopal Iyer return (B_TRUE); 18350dc2366fSVenugopal Iyer 18360dc2366fSVenugopal Iyer /* ICMPv6 header missing */ 18370dc2366fSVenugopal Iyer if ((uchar_t *)&icmp_nd[1] > end) 18380dc2366fSVenugopal Iyer return (B_FALSE); 18390dc2366fSVenugopal Iyer 18400dc2366fSVenugopal Iyer len = end - (uchar_t *)icmp_nd; 18410dc2366fSVenugopal Iyer type = icmp_nd->icmp6_type; 18420dc2366fSVenugopal Iyer 18430dc2366fSVenugopal Iyer switch (type) { 18440dc2366fSVenugopal Iyer case ND_ROUTER_SOLICIT: 18450dc2366fSVenugopal Iyer hdrlen = sizeof (nd_router_solicit_t); 18460dc2366fSVenugopal Iyer break; 18470dc2366fSVenugopal Iyer case ND_ROUTER_ADVERT: 18480dc2366fSVenugopal Iyer hdrlen = sizeof (nd_router_advert_t); 18490dc2366fSVenugopal Iyer break; 18500dc2366fSVenugopal Iyer case ND_NEIGHBOR_SOLICIT: 18510dc2366fSVenugopal Iyer hdrlen = sizeof (nd_neighbor_solicit_t); 18520dc2366fSVenugopal Iyer break; 18530dc2366fSVenugopal Iyer case ND_NEIGHBOR_ADVERT: 18540dc2366fSVenugopal Iyer hdrlen = sizeof (nd_neighbor_advert_t); 18550dc2366fSVenugopal Iyer break; 18560dc2366fSVenugopal Iyer case ND_REDIRECT: 18570dc2366fSVenugopal Iyer hdrlen = sizeof (nd_redirect_t); 18580dc2366fSVenugopal Iyer break; 18590dc2366fSVenugopal Iyer default: 18600dc2366fSVenugopal Iyer return (B_TRUE); 18610dc2366fSVenugopal Iyer } 18620dc2366fSVenugopal Iyer 18630dc2366fSVenugopal Iyer if (len < hdrlen) 18640dc2366fSVenugopal Iyer return (B_FALSE); 18650dc2366fSVenugopal Iyer 18660dc2366fSVenugopal Iyer /* SLLA option checking is needed for RS/RA/NS */ 18670dc2366fSVenugopal Iyer opttype = ND_OPT_SOURCE_LINKADDR; 18680dc2366fSVenugopal Iyer 18690dc2366fSVenugopal Iyer switch (type) { 18700dc2366fSVenugopal Iyer case ND_NEIGHBOR_ADVERT: { 18710dc2366fSVenugopal Iyer nd_neighbor_advert_t *na = (nd_neighbor_advert_t *)icmp_nd; 18720dc2366fSVenugopal Iyer 18730dc2366fSVenugopal Iyer if (!ipnospoof_check_v6(mcip, protect, &na->nd_na_target)) { 18740dc2366fSVenugopal Iyer DTRACE_PROBE2(ndp__na__fail, 18750dc2366fSVenugopal Iyer mac_client_impl_t *, mcip, ip6_t *, ip6h); 18760dc2366fSVenugopal Iyer return (B_FALSE); 18770dc2366fSVenugopal Iyer } 18780dc2366fSVenugopal Iyer 18790dc2366fSVenugopal Iyer /* TLLA option for NA */ 18800dc2366fSVenugopal Iyer opttype = ND_OPT_TARGET_LINKADDR; 18810dc2366fSVenugopal Iyer break; 18820dc2366fSVenugopal Iyer } 18830dc2366fSVenugopal Iyer case ND_REDIRECT: { 18840dc2366fSVenugopal Iyer /* option checking not needed for RD */ 18850dc2366fSVenugopal Iyer return (B_TRUE); 18860dc2366fSVenugopal Iyer } 18870dc2366fSVenugopal Iyer default: 18880dc2366fSVenugopal Iyer break; 18890dc2366fSVenugopal Iyer } 18900dc2366fSVenugopal Iyer 18910dc2366fSVenugopal Iyer if (len == hdrlen) { 18920dc2366fSVenugopal Iyer /* no options, we're done */ 18930dc2366fSVenugopal Iyer return (B_TRUE); 18940dc2366fSVenugopal Iyer } 18950dc2366fSVenugopal Iyer opt = (nd_opt_hdr_t *)((uchar_t *)icmp_nd + hdrlen); 18960dc2366fSVenugopal Iyer optlen = len - hdrlen; 18970dc2366fSVenugopal Iyer 18980dc2366fSVenugopal Iyer /* find the option header we need */ 18990dc2366fSVenugopal Iyer while (optlen > sizeof (nd_opt_hdr_t)) { 19000dc2366fSVenugopal Iyer if (opt->nd_opt_type == opttype) { 19010dc2366fSVenugopal Iyer lla = (struct nd_opt_lla *)opt; 19020dc2366fSVenugopal Iyer break; 19030dc2366fSVenugopal Iyer } 19040dc2366fSVenugopal Iyer optlen -= 8 * opt->nd_opt_len; 19050dc2366fSVenugopal Iyer opt = (nd_opt_hdr_t *) 19060dc2366fSVenugopal Iyer ((uchar_t *)opt + 8 * opt->nd_opt_len); 19070dc2366fSVenugopal Iyer } 19080dc2366fSVenugopal Iyer if (lla == NULL) 19090dc2366fSVenugopal Iyer return (B_TRUE); 19100dc2366fSVenugopal Iyer 19110dc2366fSVenugopal Iyer addrlen = lla->nd_opt_lla_len * 8 - sizeof (nd_opt_hdr_t); 19120dc2366fSVenugopal Iyer maclen = mcip->mci_mip->mi_info.mi_addr_length; 19130dc2366fSVenugopal Iyer 19140dc2366fSVenugopal Iyer if (addrlen != maclen || 19150dc2366fSVenugopal Iyer bcmp(mcip->mci_unicast->ma_addr, 19160dc2366fSVenugopal Iyer lla->nd_opt_lla_hdw_addr, maclen) != 0) { 19170dc2366fSVenugopal Iyer DTRACE_PROBE2(ndp__lla__fail, 19180dc2366fSVenugopal Iyer mac_client_impl_t *, mcip, ip6_t *, ip6h); 19190dc2366fSVenugopal Iyer return (B_FALSE); 19200dc2366fSVenugopal Iyer } 19210dc2366fSVenugopal Iyer 19220dc2366fSVenugopal Iyer DTRACE_PROBE2(ndp__lla__ok, mac_client_impl_t *, mcip, ip6_t *, ip6h); 19230dc2366fSVenugopal Iyer return (B_TRUE); 19240dc2366fSVenugopal Iyer } 19250dc2366fSVenugopal Iyer 19260dc2366fSVenugopal Iyer /* 19270dc2366fSVenugopal Iyer * Enforce ip-nospoof protection. 192825ec3e3dSEric Cheng */ 192925ec3e3dSEric Cheng static int 193025ec3e3dSEric Cheng ipnospoof_check(mac_client_impl_t *mcip, mac_protect_t *protect, 193125ec3e3dSEric Cheng mblk_t *mp, mac_header_info_t *mhip) 193225ec3e3dSEric Cheng { 19330dc2366fSVenugopal Iyer size_t hdrsize = mhip->mhi_hdrsize; 193425ec3e3dSEric Cheng uint32_t sap = mhip->mhi_bindsap; 19350dc2366fSVenugopal Iyer uchar_t *start, *end; 19360dc2366fSVenugopal Iyer mblk_t *nmp = NULL; 19370dc2366fSVenugopal Iyer int err; 193825ec3e3dSEric Cheng 19390dc2366fSVenugopal Iyer err = get_l3_info(mp, hdrsize, &start, &end, &nmp); 19400dc2366fSVenugopal Iyer if (err != 0) { 19410dc2366fSVenugopal Iyer DTRACE_PROBE2(invalid__l3, mac_client_impl_t *, mcip, 19420dc2366fSVenugopal Iyer mblk_t *, mp); 19430dc2366fSVenugopal Iyer return (err); 194425ec3e3dSEric Cheng } 19450dc2366fSVenugopal Iyer err = EINVAL; 194625ec3e3dSEric Cheng 194725ec3e3dSEric Cheng switch (sap) { 194825ec3e3dSEric Cheng case ETHERTYPE_IP: { 194925ec3e3dSEric Cheng ipha_t *ipha = (ipha_t *)start; 195025ec3e3dSEric Cheng 19510dc2366fSVenugopal Iyer if (start + sizeof (ipha_t) > end) 195225ec3e3dSEric Cheng goto fail; 195325ec3e3dSEric Cheng 19540dc2366fSVenugopal Iyer if (!ipnospoof_check_v4(mcip, protect, &ipha->ipha_src)) 195525ec3e3dSEric Cheng goto fail; 195625ec3e3dSEric Cheng 1957550b6e40SSowmini Varadhan if (!intercept_dhcpv4_outbound(mcip, ipha, end)) 1958550b6e40SSowmini Varadhan goto fail; 195925ec3e3dSEric Cheng break; 196025ec3e3dSEric Cheng } 196125ec3e3dSEric Cheng case ETHERTYPE_ARP: { 196225ec3e3dSEric Cheng arh_t *arh = (arh_t *)start; 196325ec3e3dSEric Cheng uint32_t maclen, hlen, plen, arplen; 196425ec3e3dSEric Cheng ipaddr_t spaddr; 196525ec3e3dSEric Cheng uchar_t *shaddr; 196625ec3e3dSEric Cheng 19670dc2366fSVenugopal Iyer if (start + sizeof (arh_t) > end) 196825ec3e3dSEric Cheng goto fail; 196925ec3e3dSEric Cheng 197025ec3e3dSEric Cheng maclen = mcip->mci_mip->mi_info.mi_addr_length; 197125ec3e3dSEric Cheng hlen = arh->arh_hlen; 197225ec3e3dSEric Cheng plen = arh->arh_plen; 197325ec3e3dSEric Cheng if ((hlen != 0 && hlen != maclen) || 197425ec3e3dSEric Cheng plen != sizeof (ipaddr_t)) 197525ec3e3dSEric Cheng goto fail; 197625ec3e3dSEric Cheng 197725ec3e3dSEric Cheng arplen = sizeof (arh_t) + 2 * hlen + 2 * plen; 19780dc2366fSVenugopal Iyer if (start + arplen > end) 197925ec3e3dSEric Cheng goto fail; 198025ec3e3dSEric Cheng 198125ec3e3dSEric Cheng shaddr = start + sizeof (arh_t); 198225ec3e3dSEric Cheng if (hlen != 0 && 198325ec3e3dSEric Cheng bcmp(mcip->mci_unicast->ma_addr, shaddr, maclen) != 0) 198425ec3e3dSEric Cheng goto fail; 198525ec3e3dSEric Cheng 198625ec3e3dSEric Cheng bcopy(shaddr + hlen, &spaddr, sizeof (spaddr)); 19870dc2366fSVenugopal Iyer if (!ipnospoof_check_v4(mcip, protect, &spaddr)) 198825ec3e3dSEric Cheng goto fail; 198925ec3e3dSEric Cheng break; 199025ec3e3dSEric Cheng } 19910dc2366fSVenugopal Iyer case ETHERTYPE_IPV6: { 19920dc2366fSVenugopal Iyer ip6_t *ip6h = (ip6_t *)start; 19930dc2366fSVenugopal Iyer 19940dc2366fSVenugopal Iyer if (start + sizeof (ip6_t) > end) 19950dc2366fSVenugopal Iyer goto fail; 19960dc2366fSVenugopal Iyer 19970dc2366fSVenugopal Iyer if (!ipnospoof_check_v6(mcip, protect, &ip6h->ip6_src)) 19980dc2366fSVenugopal Iyer goto fail; 19990dc2366fSVenugopal Iyer 20000dc2366fSVenugopal Iyer if (!ipnospoof_check_ndp(mcip, protect, ip6h, end)) 20010dc2366fSVenugopal Iyer goto fail; 20020dc2366fSVenugopal Iyer 2003550b6e40SSowmini Varadhan if (!intercept_dhcpv6_outbound(mcip, ip6h, end)) 2004550b6e40SSowmini Varadhan goto fail; 200525ec3e3dSEric Cheng break; 200625ec3e3dSEric Cheng } 20070dc2366fSVenugopal Iyer } 20080dc2366fSVenugopal Iyer freemsg(nmp); 200925ec3e3dSEric Cheng return (0); 201025ec3e3dSEric Cheng 201125ec3e3dSEric Cheng fail: 20120dc2366fSVenugopal Iyer freemsg(nmp); 201325ec3e3dSEric Cheng return (err); 201425ec3e3dSEric Cheng } 201525ec3e3dSEric Cheng 20160dc2366fSVenugopal Iyer static boolean_t 20170dc2366fSVenugopal Iyer dhcpnospoof_check_cid(mac_protect_t *p, uchar_t *cid, uint_t cidlen) 20180dc2366fSVenugopal Iyer { 20190dc2366fSVenugopal Iyer int i; 20200dc2366fSVenugopal Iyer 20210dc2366fSVenugopal Iyer for (i = 0; i < p->mp_cidcnt; i++) { 20220dc2366fSVenugopal Iyer mac_dhcpcid_t *dcid = &p->mp_cids[i]; 20230dc2366fSVenugopal Iyer 20240dc2366fSVenugopal Iyer if (dcid->dc_len == cidlen && 20250dc2366fSVenugopal Iyer bcmp(dcid->dc_id, cid, cidlen) == 0) 20260dc2366fSVenugopal Iyer return (B_TRUE); 20270dc2366fSVenugopal Iyer } 20280dc2366fSVenugopal Iyer return (B_FALSE); 20290dc2366fSVenugopal Iyer } 20300dc2366fSVenugopal Iyer 20310dc2366fSVenugopal Iyer static boolean_t 20320dc2366fSVenugopal Iyer dhcpnospoof_check_v4(mac_client_impl_t *mcip, mac_protect_t *p, 20330dc2366fSVenugopal Iyer ipha_t *ipha, uchar_t *end) 20340dc2366fSVenugopal Iyer { 20350dc2366fSVenugopal Iyer struct dhcp *dh4; 20360dc2366fSVenugopal Iyer uchar_t *cid; 20370dc2366fSVenugopal Iyer uint_t maclen, cidlen = 0; 20380dc2366fSVenugopal Iyer uint8_t optlen; 20390dc2366fSVenugopal Iyer int err; 20400dc2366fSVenugopal Iyer 20410dc2366fSVenugopal Iyer if ((err = get_dhcpv4_info(ipha, end, &dh4)) != 0) 20420dc2366fSVenugopal Iyer return (err == EINVAL); 20430dc2366fSVenugopal Iyer 20440dc2366fSVenugopal Iyer maclen = mcip->mci_mip->mi_info.mi_addr_length; 20450dc2366fSVenugopal Iyer if (dh4->hlen == maclen && 20460dc2366fSVenugopal Iyer bcmp(mcip->mci_unicast->ma_addr, dh4->chaddr, maclen) != 0) { 20470dc2366fSVenugopal Iyer return (B_FALSE); 20480dc2366fSVenugopal Iyer } 20490dc2366fSVenugopal Iyer if (get_dhcpv4_option(dh4, end, CD_CLIENT_ID, &cid, &optlen) == 0) 20500dc2366fSVenugopal Iyer cidlen = optlen; 20510dc2366fSVenugopal Iyer 20520dc2366fSVenugopal Iyer if (cidlen == 0) 20530dc2366fSVenugopal Iyer return (B_TRUE); 20540dc2366fSVenugopal Iyer 20550dc2366fSVenugopal Iyer if (*cid == ARPHRD_ETHER && cidlen - 1 == maclen && 20560dc2366fSVenugopal Iyer bcmp(mcip->mci_unicast->ma_addr, cid + 1, maclen) == 0) 20570dc2366fSVenugopal Iyer return (B_TRUE); 20580dc2366fSVenugopal Iyer 20590dc2366fSVenugopal Iyer return (dhcpnospoof_check_cid(p, cid, cidlen)); 20600dc2366fSVenugopal Iyer } 20610dc2366fSVenugopal Iyer 20620dc2366fSVenugopal Iyer static boolean_t 20630dc2366fSVenugopal Iyer dhcpnospoof_check_v6(mac_client_impl_t *mcip, mac_protect_t *p, 20640dc2366fSVenugopal Iyer ip6_t *ip6h, uchar_t *end) 20650dc2366fSVenugopal Iyer { 20660dc2366fSVenugopal Iyer dhcpv6_message_t *dh6; 20670dc2366fSVenugopal Iyer dhcpv6_option_t *d6o; 20680dc2366fSVenugopal Iyer uint8_t mtype; 20690dc2366fSVenugopal Iyer uchar_t *cid, *lladdr = NULL; 20700dc2366fSVenugopal Iyer uint_t cidlen, maclen, addrlen = 0; 20710dc2366fSVenugopal Iyer uint16_t cidtype; 20720dc2366fSVenugopal Iyer int err; 20730dc2366fSVenugopal Iyer 20740dc2366fSVenugopal Iyer if ((err = get_dhcpv6_info(ip6h, end, &dh6)) != 0) 20750dc2366fSVenugopal Iyer return (err == EINVAL); 20760dc2366fSVenugopal Iyer 20770dc2366fSVenugopal Iyer /* 20780dc2366fSVenugopal Iyer * We only check client-generated messages. 20790dc2366fSVenugopal Iyer */ 20800dc2366fSVenugopal Iyer mtype = dh6->d6m_msg_type; 20810dc2366fSVenugopal Iyer if (mtype == DHCPV6_MSG_ADVERTISE || mtype == DHCPV6_MSG_REPLY || 20820dc2366fSVenugopal Iyer mtype == DHCPV6_MSG_RECONFIGURE) 20830dc2366fSVenugopal Iyer return (B_TRUE); 20840dc2366fSVenugopal Iyer 20850dc2366fSVenugopal Iyer d6o = get_dhcpv6_option(&dh6[1], end - (uchar_t *)&dh6[1], NULL, 20860dc2366fSVenugopal Iyer DHCPV6_OPT_CLIENTID, &cidlen); 20870dc2366fSVenugopal Iyer if (d6o == NULL || (uchar_t *)d6o + cidlen > end) 20880dc2366fSVenugopal Iyer return (B_TRUE); 20890dc2366fSVenugopal Iyer 20900dc2366fSVenugopal Iyer cid = (uchar_t *)&d6o[1]; 20910dc2366fSVenugopal Iyer cidlen -= sizeof (*d6o); 20920dc2366fSVenugopal Iyer if (cidlen < sizeof (cidtype)) 20930dc2366fSVenugopal Iyer return (B_TRUE); 20940dc2366fSVenugopal Iyer 20950dc2366fSVenugopal Iyer bcopy(cid, &cidtype, sizeof (cidtype)); 20960dc2366fSVenugopal Iyer cidtype = ntohs(cidtype); 20970dc2366fSVenugopal Iyer if (cidtype == DHCPV6_DUID_LLT && cidlen >= sizeof (duid_llt_t)) { 20980dc2366fSVenugopal Iyer lladdr = cid + sizeof (duid_llt_t); 20990dc2366fSVenugopal Iyer addrlen = cidlen - sizeof (duid_llt_t); 21000dc2366fSVenugopal Iyer } 21010dc2366fSVenugopal Iyer if (cidtype == DHCPV6_DUID_LL && cidlen >= sizeof (duid_ll_t)) { 21020dc2366fSVenugopal Iyer lladdr = cid + sizeof (duid_ll_t); 21030dc2366fSVenugopal Iyer addrlen = cidlen - sizeof (duid_ll_t); 21040dc2366fSVenugopal Iyer } 21050dc2366fSVenugopal Iyer maclen = mcip->mci_mip->mi_info.mi_addr_length; 21060dc2366fSVenugopal Iyer if (lladdr != NULL && addrlen == maclen && 21070dc2366fSVenugopal Iyer bcmp(mcip->mci_unicast->ma_addr, lladdr, maclen) == 0) { 21080dc2366fSVenugopal Iyer return (B_TRUE); 21090dc2366fSVenugopal Iyer } 21100dc2366fSVenugopal Iyer return (dhcpnospoof_check_cid(p, cid, cidlen)); 21110dc2366fSVenugopal Iyer } 21120dc2366fSVenugopal Iyer 21130dc2366fSVenugopal Iyer /* 21140dc2366fSVenugopal Iyer * Enforce dhcp-nospoof protection. 21150dc2366fSVenugopal Iyer */ 21160dc2366fSVenugopal Iyer static int 21170dc2366fSVenugopal Iyer dhcpnospoof_check(mac_client_impl_t *mcip, mac_protect_t *protect, 21180dc2366fSVenugopal Iyer mblk_t *mp, mac_header_info_t *mhip) 21190dc2366fSVenugopal Iyer { 21200dc2366fSVenugopal Iyer size_t hdrsize = mhip->mhi_hdrsize; 21210dc2366fSVenugopal Iyer uint32_t sap = mhip->mhi_bindsap; 21220dc2366fSVenugopal Iyer uchar_t *start, *end; 21230dc2366fSVenugopal Iyer mblk_t *nmp = NULL; 21240dc2366fSVenugopal Iyer int err; 21250dc2366fSVenugopal Iyer 21260dc2366fSVenugopal Iyer err = get_l3_info(mp, hdrsize, &start, &end, &nmp); 21270dc2366fSVenugopal Iyer if (err != 0) { 21280dc2366fSVenugopal Iyer DTRACE_PROBE2(invalid__l3, mac_client_impl_t *, mcip, 21290dc2366fSVenugopal Iyer mblk_t *, mp); 21300dc2366fSVenugopal Iyer return (err); 21310dc2366fSVenugopal Iyer } 21320dc2366fSVenugopal Iyer err = EINVAL; 21330dc2366fSVenugopal Iyer 21340dc2366fSVenugopal Iyer switch (sap) { 21350dc2366fSVenugopal Iyer case ETHERTYPE_IP: { 21360dc2366fSVenugopal Iyer ipha_t *ipha = (ipha_t *)start; 21370dc2366fSVenugopal Iyer 21380dc2366fSVenugopal Iyer if (start + sizeof (ipha_t) > end) 21390dc2366fSVenugopal Iyer goto fail; 21400dc2366fSVenugopal Iyer 21410dc2366fSVenugopal Iyer if (!dhcpnospoof_check_v4(mcip, protect, ipha, end)) 21420dc2366fSVenugopal Iyer goto fail; 21430dc2366fSVenugopal Iyer 21440dc2366fSVenugopal Iyer break; 21450dc2366fSVenugopal Iyer } 21460dc2366fSVenugopal Iyer case ETHERTYPE_IPV6: { 21470dc2366fSVenugopal Iyer ip6_t *ip6h = (ip6_t *)start; 21480dc2366fSVenugopal Iyer 21490dc2366fSVenugopal Iyer if (start + sizeof (ip6_t) > end) 21500dc2366fSVenugopal Iyer goto fail; 21510dc2366fSVenugopal Iyer 21520dc2366fSVenugopal Iyer if (!dhcpnospoof_check_v6(mcip, protect, ip6h, end)) 21530dc2366fSVenugopal Iyer goto fail; 21540dc2366fSVenugopal Iyer 21550dc2366fSVenugopal Iyer break; 21560dc2366fSVenugopal Iyer } 21570dc2366fSVenugopal Iyer } 21580dc2366fSVenugopal Iyer freemsg(nmp); 21590dc2366fSVenugopal Iyer return (0); 21600dc2366fSVenugopal Iyer 21610dc2366fSVenugopal Iyer fail: 21620dc2366fSVenugopal Iyer /* increment dhcpnospoof stat here */ 21630dc2366fSVenugopal Iyer freemsg(nmp); 21640dc2366fSVenugopal Iyer return (err); 21650dc2366fSVenugopal Iyer } 21660dc2366fSVenugopal Iyer 21670dc2366fSVenugopal Iyer /* 2168*4e6f6c83SCody Peter Mello * This is called whenever the mac client's mac address changes, to make sure 2169*4e6f6c83SCody Peter Mello * we allow use of the new link-local address. 21700dc2366fSVenugopal Iyer */ 2171*4e6f6c83SCody Peter Mello static void 21720dc2366fSVenugopal Iyer mac_protect_update_v6_local_addr(mac_client_impl_t *mcip) 21730dc2366fSVenugopal Iyer { 2174*4e6f6c83SCody Peter Mello uint_t i; 2175*4e6f6c83SCody Peter Mello in6_addr_t *token = &mcip->mci_v6_mac_token; 2176*4e6f6c83SCody Peter Mello in6_addr_t *v6addr = &mcip->mci_v6_local_addr; 21770dc2366fSVenugopal Iyer in6_addr_t ll_template = {(uint32_t)V6_LINKLOCAL, 0x0, 0x0, 0x0}; 21780dc2366fSVenugopal Iyer 2179*4e6f6c83SCody Peter Mello for (i = 0; i < 4; i++) { 2180*4e6f6c83SCody Peter Mello v6addr->s6_addr32[i] = token->s6_addr32[i] | 2181*4e6f6c83SCody Peter Mello ll_template.s6_addr32[i]; 2182*4e6f6c83SCody Peter Mello } 2183*4e6f6c83SCody Peter Mello mcip->mci_protect_flags |= MPT_FLAG_V6_LOCAL_ADDR_SET; 2184*4e6f6c83SCody Peter Mello } 21850dc2366fSVenugopal Iyer 2186*4e6f6c83SCody Peter Mello /* 2187*4e6f6c83SCody Peter Mello * This is called whenever the mac client's mac address changes, to make sure 2188*4e6f6c83SCody Peter Mello * that any existing addresses gained via SLAAC are appropriately updated. 2189*4e6f6c83SCody Peter Mello */ 2190*4e6f6c83SCody Peter Mello static void 2191*4e6f6c83SCody Peter Mello mac_protect_update_v6_slaac_addr(mac_client_impl_t *mcip) 2192*4e6f6c83SCody Peter Mello { 2193*4e6f6c83SCody Peter Mello void *cookie = NULL; 2194*4e6f6c83SCody Peter Mello avl_tree_t temp_tree; 2195*4e6f6c83SCody Peter Mello avl_tree_t *ttp = &temp_tree, *sip = &mcip->mci_v6_slaac_ip; 2196*4e6f6c83SCody Peter Mello in6_addr_t *token = &mcip->mci_v6_mac_token; 2197*4e6f6c83SCody Peter Mello slaac_addr_t *addr = NULL; 2198*4e6f6c83SCody Peter Mello 2199*4e6f6c83SCody Peter Mello avl_create(ttp, compare_slaac_ip, sizeof (slaac_addr_t), 2200*4e6f6c83SCody Peter Mello offsetof(slaac_addr_t, sla_node)); 2201*4e6f6c83SCody Peter Mello 2202*4e6f6c83SCody Peter Mello /* Copy everything over to the temporary tree, and fix the IP address */ 2203*4e6f6c83SCody Peter Mello while ((addr = avl_destroy_nodes(sip, &cookie)) != NULL) { 2204*4e6f6c83SCody Peter Mello VERIFY(insert_slaac_ip(ttp, token, addr) == B_TRUE); 2205*4e6f6c83SCody Peter Mello } 2206*4e6f6c83SCody Peter Mello 2207*4e6f6c83SCody Peter Mello /* 2208*4e6f6c83SCody Peter Mello * Now that the tempory tree has all of the modified addresses, we can 2209*4e6f6c83SCody Peter Mello * swap them over to the original tree once it's reset. 2210*4e6f6c83SCody Peter Mello */ 2211*4e6f6c83SCody Peter Mello avl_destroy(sip); 2212*4e6f6c83SCody Peter Mello avl_create(sip, compare_slaac_ip, sizeof (slaac_addr_t), 2213*4e6f6c83SCody Peter Mello offsetof(slaac_addr_t, sla_node)); 2214*4e6f6c83SCody Peter Mello avl_swap(ttp, sip); 2215*4e6f6c83SCody Peter Mello } 2216*4e6f6c83SCody Peter Mello 2217*4e6f6c83SCody Peter Mello /* 2218*4e6f6c83SCody Peter Mello * After the unicast MAC address changes, we need to update the derived token, 2219*4e6f6c83SCody Peter Mello * and update the IPv6 addresses that use the token. 2220*4e6f6c83SCody Peter Mello */ 2221*4e6f6c83SCody Peter Mello void 2222*4e6f6c83SCody Peter Mello mac_protect_update_mac_token(mac_client_impl_t *mcip) 2223*4e6f6c83SCody Peter Mello { 2224*4e6f6c83SCody Peter Mello uint_t media = mcip->mci_mip->mi_info.mi_media; 2225*4e6f6c83SCody Peter Mello uint8_t *p, *macaddr = mcip->mci_unicast->ma_addr; 2226*4e6f6c83SCody Peter Mello in6_addr_t *token = &mcip->mci_v6_mac_token; 2227*4e6f6c83SCody Peter Mello 2228*4e6f6c83SCody Peter Mello bzero(token, sizeof (in6_addr_t)); 2229*4e6f6c83SCody Peter Mello p = (uint8_t *)&token->s6_addr32[2]; 22300dc2366fSVenugopal Iyer 22310dc2366fSVenugopal Iyer switch (media) { 22320dc2366fSVenugopal Iyer case DL_ETHER: 22330dc2366fSVenugopal Iyer bcopy(macaddr, p, 3); 22340dc2366fSVenugopal Iyer p[0] ^= 0x2; 22350dc2366fSVenugopal Iyer p[3] = 0xff; 22360dc2366fSVenugopal Iyer p[4] = 0xfe; 22370dc2366fSVenugopal Iyer bcopy(macaddr + 3, p + 5, 3); 22380dc2366fSVenugopal Iyer break; 22390dc2366fSVenugopal Iyer case DL_IB: 22400dc2366fSVenugopal Iyer ASSERT(mcip->mci_mip->mi_info.mi_addr_length == 20); 22410dc2366fSVenugopal Iyer bcopy(macaddr + 12, p, 8); 22420dc2366fSVenugopal Iyer p[0] |= 2; 22430dc2366fSVenugopal Iyer break; 22440dc2366fSVenugopal Iyer default: 22450dc2366fSVenugopal Iyer /* 22460dc2366fSVenugopal Iyer * We do not need to generate the local address for link types 22470dc2366fSVenugopal Iyer * that do not support link protection. Wifi pretends to be 2248*4e6f6c83SCody Peter Mello * Ethernet so it is covered by the DL_ETHER case (note the 22490dc2366fSVenugopal Iyer * use of mi_media instead of mi_nativemedia). 22500dc2366fSVenugopal Iyer */ 22510dc2366fSVenugopal Iyer return; 22520dc2366fSVenugopal Iyer } 22530dc2366fSVenugopal Iyer 2254*4e6f6c83SCody Peter Mello mac_protect_update_v6_local_addr(mcip); 2255*4e6f6c83SCody Peter Mello mac_protect_update_v6_slaac_addr(mcip); 22560dc2366fSVenugopal Iyer } 2257*4e6f6c83SCody Peter Mello 2258*4e6f6c83SCody Peter Mello 22590dc2366fSVenugopal Iyer 226025ec3e3dSEric Cheng /* 226125ec3e3dSEric Cheng * Enforce link protection on one packet. 226225ec3e3dSEric Cheng */ 226325ec3e3dSEric Cheng static int 226425ec3e3dSEric Cheng mac_protect_check_one(mac_client_impl_t *mcip, mblk_t *mp) 226525ec3e3dSEric Cheng { 226625ec3e3dSEric Cheng mac_impl_t *mip = mcip->mci_mip; 226725ec3e3dSEric Cheng mac_resource_props_t *mrp = MCIP_RESOURCE_PROPS(mcip); 226825ec3e3dSEric Cheng mac_protect_t *protect; 226925ec3e3dSEric Cheng mac_header_info_t mhi; 227025ec3e3dSEric Cheng uint32_t types; 227125ec3e3dSEric Cheng int err; 227225ec3e3dSEric Cheng 227325ec3e3dSEric Cheng ASSERT(mp->b_next == NULL); 227425ec3e3dSEric Cheng ASSERT(mrp != NULL); 227525ec3e3dSEric Cheng 227625ec3e3dSEric Cheng err = mac_vlan_header_info((mac_handle_t)mip, mp, &mhi); 227725ec3e3dSEric Cheng if (err != 0) { 227825ec3e3dSEric Cheng DTRACE_PROBE2(invalid__header, mac_client_impl_t *, mcip, 227925ec3e3dSEric Cheng mblk_t *, mp); 228025ec3e3dSEric Cheng return (err); 228125ec3e3dSEric Cheng } 228225ec3e3dSEric Cheng protect = &mrp->mrp_protect; 228325ec3e3dSEric Cheng types = protect->mp_types; 228425ec3e3dSEric Cheng 228525ec3e3dSEric Cheng if ((types & MPT_MACNOSPOOF) != 0) { 228625ec3e3dSEric Cheng if (mhi.mhi_saddr != NULL && 228725ec3e3dSEric Cheng bcmp(mcip->mci_unicast->ma_addr, mhi.mhi_saddr, 228825ec3e3dSEric Cheng mip->mi_info.mi_addr_length) != 0) { 22890dc2366fSVenugopal Iyer BUMP_STAT(mcip, macspoofed); 229025ec3e3dSEric Cheng DTRACE_PROBE2(mac__nospoof__fail, 229125ec3e3dSEric Cheng mac_client_impl_t *, mcip, mblk_t *, mp); 229225ec3e3dSEric Cheng return (EINVAL); 229325ec3e3dSEric Cheng } 229425ec3e3dSEric Cheng } 229525ec3e3dSEric Cheng if ((types & MPT_RESTRICTED) != 0) { 229625ec3e3dSEric Cheng uint32_t vid = VLAN_ID(mhi.mhi_tci); 229725ec3e3dSEric Cheng uint32_t sap = mhi.mhi_bindsap; 229825ec3e3dSEric Cheng 229925ec3e3dSEric Cheng /* 230025ec3e3dSEric Cheng * ETHERTYPE_VLAN packets are allowed through, provided that 230125ec3e3dSEric Cheng * the vid is not spoofed. 230225ec3e3dSEric Cheng */ 230325ec3e3dSEric Cheng if (vid != 0 && !mac_client_check_flow_vid(mcip, vid)) { 23040dc2366fSVenugopal Iyer BUMP_STAT(mcip, restricted); 230525ec3e3dSEric Cheng DTRACE_PROBE2(restricted__vid__invalid, 230625ec3e3dSEric Cheng mac_client_impl_t *, mcip, mblk_t *, mp); 230725ec3e3dSEric Cheng return (EINVAL); 230825ec3e3dSEric Cheng } 230925ec3e3dSEric Cheng 231025ec3e3dSEric Cheng if (sap != ETHERTYPE_IP && sap != ETHERTYPE_IPV6 && 231125ec3e3dSEric Cheng sap != ETHERTYPE_ARP) { 23120dc2366fSVenugopal Iyer BUMP_STAT(mcip, restricted); 231325ec3e3dSEric Cheng DTRACE_PROBE2(restricted__fail, 231425ec3e3dSEric Cheng mac_client_impl_t *, mcip, mblk_t *, mp); 231525ec3e3dSEric Cheng return (EINVAL); 231625ec3e3dSEric Cheng } 231725ec3e3dSEric Cheng } 231825ec3e3dSEric Cheng if ((types & MPT_IPNOSPOOF) != 0) { 23190dc2366fSVenugopal Iyer if ((err = ipnospoof_check(mcip, protect, mp, &mhi)) != 0) { 23200dc2366fSVenugopal Iyer BUMP_STAT(mcip, ipspoofed); 232125ec3e3dSEric Cheng DTRACE_PROBE2(ip__nospoof__fail, 232225ec3e3dSEric Cheng mac_client_impl_t *, mcip, mblk_t *, mp); 232325ec3e3dSEric Cheng return (err); 232425ec3e3dSEric Cheng } 232525ec3e3dSEric Cheng } 23260dc2366fSVenugopal Iyer if ((types & MPT_DHCPNOSPOOF) != 0) { 23270dc2366fSVenugopal Iyer if ((err = dhcpnospoof_check(mcip, protect, mp, &mhi)) != 0) { 23280dc2366fSVenugopal Iyer BUMP_STAT(mcip, dhcpspoofed); 23290dc2366fSVenugopal Iyer DTRACE_PROBE2(dhcp__nospoof__fail, 23300dc2366fSVenugopal Iyer mac_client_impl_t *, mcip, mblk_t *, mp); 23310dc2366fSVenugopal Iyer return (err); 23320dc2366fSVenugopal Iyer } 23330dc2366fSVenugopal Iyer } 233425ec3e3dSEric Cheng return (0); 233525ec3e3dSEric Cheng } 233625ec3e3dSEric Cheng 233725ec3e3dSEric Cheng /* 233825ec3e3dSEric Cheng * Enforce link protection on a packet chain. 233925ec3e3dSEric Cheng * Packets that pass the checks are returned back to the caller. 234025ec3e3dSEric Cheng */ 234125ec3e3dSEric Cheng mblk_t * 234225ec3e3dSEric Cheng mac_protect_check(mac_client_handle_t mch, mblk_t *mp) 234325ec3e3dSEric Cheng { 234425ec3e3dSEric Cheng mac_client_impl_t *mcip = (mac_client_impl_t *)mch; 234525ec3e3dSEric Cheng mblk_t *ret_mp = NULL, **tailp = &ret_mp, *next; 234625ec3e3dSEric Cheng 234725ec3e3dSEric Cheng /* 234825ec3e3dSEric Cheng * Skip checks if we are part of an aggr. 234925ec3e3dSEric Cheng */ 235025ec3e3dSEric Cheng if ((mcip->mci_state_flags & MCIS_IS_AGGR_PORT) != 0) 235125ec3e3dSEric Cheng return (mp); 235225ec3e3dSEric Cheng 235325ec3e3dSEric Cheng for (; mp != NULL; mp = next) { 235425ec3e3dSEric Cheng next = mp->b_next; 235525ec3e3dSEric Cheng mp->b_next = NULL; 235625ec3e3dSEric Cheng 235725ec3e3dSEric Cheng if (mac_protect_check_one(mcip, mp) == 0) { 235825ec3e3dSEric Cheng *tailp = mp; 235925ec3e3dSEric Cheng tailp = &mp->b_next; 236025ec3e3dSEric Cheng } else { 236125ec3e3dSEric Cheng freemsg(mp); 236225ec3e3dSEric Cheng } 236325ec3e3dSEric Cheng } 236425ec3e3dSEric Cheng return (ret_mp); 236525ec3e3dSEric Cheng } 236625ec3e3dSEric Cheng 236725ec3e3dSEric Cheng /* 236825ec3e3dSEric Cheng * Check if a particular protection type is enabled. 236925ec3e3dSEric Cheng */ 237025ec3e3dSEric Cheng boolean_t 237125ec3e3dSEric Cheng mac_protect_enabled(mac_client_handle_t mch, uint32_t type) 237225ec3e3dSEric Cheng { 23730dc2366fSVenugopal Iyer return (MAC_PROTECT_ENABLED((mac_client_impl_t *)mch, type)); 23740dc2366fSVenugopal Iyer } 237525ec3e3dSEric Cheng 23760dc2366fSVenugopal Iyer static int 23770dc2366fSVenugopal Iyer validate_ips(mac_protect_t *p) 23780dc2366fSVenugopal Iyer { 23790dc2366fSVenugopal Iyer uint_t i, j; 23800dc2366fSVenugopal Iyer 23810dc2366fSVenugopal Iyer if (p->mp_ipaddrcnt == MPT_RESET) 23820dc2366fSVenugopal Iyer return (0); 23830dc2366fSVenugopal Iyer 23840dc2366fSVenugopal Iyer if (p->mp_ipaddrcnt > MPT_MAXIPADDR) 23850dc2366fSVenugopal Iyer return (EINVAL); 23860dc2366fSVenugopal Iyer 23870dc2366fSVenugopal Iyer for (i = 0; i < p->mp_ipaddrcnt; i++) { 23880dc2366fSVenugopal Iyer mac_ipaddr_t *addr = &p->mp_ipaddrs[i]; 23890dc2366fSVenugopal Iyer 23900dc2366fSVenugopal Iyer /* 2391e03914f9SRobert Mustacchi * The unspecified address is implicitly allowed so there's no 2392e03914f9SRobert Mustacchi * need to add it to the list. Also, validate that the netmask, 2393e03914f9SRobert Mustacchi * if any, is sane for the specific version of IP. A mask of 2394e03914f9SRobert Mustacchi * some kind is always required. 23950dc2366fSVenugopal Iyer */ 2396e03914f9SRobert Mustacchi if (addr->ip_netmask == 0) 2397e03914f9SRobert Mustacchi return (EINVAL); 2398e03914f9SRobert Mustacchi 23990dc2366fSVenugopal Iyer if (addr->ip_version == IPV4_VERSION) { 24000dc2366fSVenugopal Iyer if (V4_PART_OF_V6(addr->ip_addr) == INADDR_ANY) 24010dc2366fSVenugopal Iyer return (EINVAL); 2402e03914f9SRobert Mustacchi if (addr->ip_netmask > 32) 2403e03914f9SRobert Mustacchi return (EINVAL); 24040dc2366fSVenugopal Iyer } else if (addr->ip_version == IPV6_VERSION) { 24050dc2366fSVenugopal Iyer if (IN6_IS_ADDR_UNSPECIFIED(&addr->ip_addr)) 24060dc2366fSVenugopal Iyer return (EINVAL); 2407e03914f9SRobert Mustacchi 2408e03914f9SRobert Mustacchi if (IN6_IS_ADDR_V4MAPPED_ANY(&addr->ip_addr)) 2409e03914f9SRobert Mustacchi return (EINVAL); 2410e03914f9SRobert Mustacchi 2411e03914f9SRobert Mustacchi if (addr->ip_netmask > 128) 2412e03914f9SRobert Mustacchi return (EINVAL); 24130dc2366fSVenugopal Iyer } else { 24140dc2366fSVenugopal Iyer /* invalid ip version */ 24150dc2366fSVenugopal Iyer return (EINVAL); 24160dc2366fSVenugopal Iyer } 24170dc2366fSVenugopal Iyer 24180dc2366fSVenugopal Iyer for (j = 0; j < p->mp_ipaddrcnt; j++) { 24190dc2366fSVenugopal Iyer mac_ipaddr_t *addr1 = &p->mp_ipaddrs[j]; 24200dc2366fSVenugopal Iyer 24210dc2366fSVenugopal Iyer if (i == j || addr->ip_version != addr1->ip_version) 24220dc2366fSVenugopal Iyer continue; 24230dc2366fSVenugopal Iyer 24240dc2366fSVenugopal Iyer /* found a duplicate */ 24250dc2366fSVenugopal Iyer if ((addr->ip_version == IPV4_VERSION && 24260dc2366fSVenugopal Iyer V4_PART_OF_V6(addr->ip_addr) == 24270dc2366fSVenugopal Iyer V4_PART_OF_V6(addr1->ip_addr)) || 24280dc2366fSVenugopal Iyer IN6_ARE_ADDR_EQUAL(&addr->ip_addr, 24290dc2366fSVenugopal Iyer &addr1->ip_addr)) 24300dc2366fSVenugopal Iyer return (EINVAL); 24310dc2366fSVenugopal Iyer } 24320dc2366fSVenugopal Iyer } 24330dc2366fSVenugopal Iyer return (0); 24340dc2366fSVenugopal Iyer } 24350dc2366fSVenugopal Iyer 24360dc2366fSVenugopal Iyer /* ARGSUSED */ 24370dc2366fSVenugopal Iyer static int 24380dc2366fSVenugopal Iyer validate_cids(mac_protect_t *p) 24390dc2366fSVenugopal Iyer { 24400dc2366fSVenugopal Iyer uint_t i, j; 24410dc2366fSVenugopal Iyer 24420dc2366fSVenugopal Iyer if (p->mp_cidcnt == MPT_RESET) 24430dc2366fSVenugopal Iyer return (0); 24440dc2366fSVenugopal Iyer 24450dc2366fSVenugopal Iyer if (p->mp_cidcnt > MPT_MAXCID) 24460dc2366fSVenugopal Iyer return (EINVAL); 24470dc2366fSVenugopal Iyer 24480dc2366fSVenugopal Iyer for (i = 0; i < p->mp_cidcnt; i++) { 24490dc2366fSVenugopal Iyer mac_dhcpcid_t *cid = &p->mp_cids[i]; 24500dc2366fSVenugopal Iyer 24510dc2366fSVenugopal Iyer if (cid->dc_len > MPT_MAXCIDLEN || 24520dc2366fSVenugopal Iyer (cid->dc_form != CIDFORM_TYPED && 24530dc2366fSVenugopal Iyer cid->dc_form != CIDFORM_HEX && 24540dc2366fSVenugopal Iyer cid->dc_form != CIDFORM_STR)) 24550dc2366fSVenugopal Iyer return (EINVAL); 24560dc2366fSVenugopal Iyer 24570dc2366fSVenugopal Iyer for (j = 0; j < p->mp_cidcnt; j++) { 24580dc2366fSVenugopal Iyer mac_dhcpcid_t *cid1 = &p->mp_cids[j]; 24590dc2366fSVenugopal Iyer 24600dc2366fSVenugopal Iyer if (i == j || cid->dc_len != cid1->dc_len) 24610dc2366fSVenugopal Iyer continue; 24620dc2366fSVenugopal Iyer 24630dc2366fSVenugopal Iyer /* found a duplicate */ 24640dc2366fSVenugopal Iyer if (bcmp(cid->dc_id, cid1->dc_id, cid->dc_len) == 0) 24650dc2366fSVenugopal Iyer return (EINVAL); 24660dc2366fSVenugopal Iyer } 24670dc2366fSVenugopal Iyer } 24680dc2366fSVenugopal Iyer return (0); 246925ec3e3dSEric Cheng } 247025ec3e3dSEric Cheng 247125ec3e3dSEric Cheng /* 247225ec3e3dSEric Cheng * Sanity-checks parameters given by userland. 247325ec3e3dSEric Cheng */ 247425ec3e3dSEric Cheng int 247525ec3e3dSEric Cheng mac_protect_validate(mac_resource_props_t *mrp) 247625ec3e3dSEric Cheng { 247725ec3e3dSEric Cheng mac_protect_t *p = &mrp->mrp_protect; 24780dc2366fSVenugopal Iyer int err; 247925ec3e3dSEric Cheng 248025ec3e3dSEric Cheng /* check for invalid types */ 248125ec3e3dSEric Cheng if (p->mp_types != MPT_RESET && (p->mp_types & ~MPT_ALL) != 0) 248225ec3e3dSEric Cheng return (EINVAL); 248325ec3e3dSEric Cheng 24840dc2366fSVenugopal Iyer if ((err = validate_ips(p)) != 0) 24850dc2366fSVenugopal Iyer return (err); 248625ec3e3dSEric Cheng 24870dc2366fSVenugopal Iyer if ((err = validate_cids(p)) != 0) 24880dc2366fSVenugopal Iyer return (err); 248925ec3e3dSEric Cheng 249025ec3e3dSEric Cheng return (0); 249125ec3e3dSEric Cheng } 249225ec3e3dSEric Cheng 249325ec3e3dSEric Cheng /* 249425ec3e3dSEric Cheng * Enable/disable link protection. 249525ec3e3dSEric Cheng */ 249625ec3e3dSEric Cheng int 249725ec3e3dSEric Cheng mac_protect_set(mac_client_handle_t mch, mac_resource_props_t *mrp) 249825ec3e3dSEric Cheng { 249925ec3e3dSEric Cheng mac_client_impl_t *mcip = (mac_client_impl_t *)mch; 250025ec3e3dSEric Cheng mac_impl_t *mip = mcip->mci_mip; 250125ec3e3dSEric Cheng uint_t media = mip->mi_info.mi_nativemedia; 250225ec3e3dSEric Cheng int err; 250325ec3e3dSEric Cheng 250425ec3e3dSEric Cheng ASSERT(MAC_PERIM_HELD((mac_handle_t)mip)); 250525ec3e3dSEric Cheng 250625ec3e3dSEric Cheng /* tunnels are not supported */ 250725ec3e3dSEric Cheng if (media == DL_IPV4 || media == DL_IPV6 || media == DL_6TO4) 250825ec3e3dSEric Cheng return (ENOTSUP); 250925ec3e3dSEric Cheng 251025ec3e3dSEric Cheng if ((err = mac_protect_validate(mrp)) != 0) 251125ec3e3dSEric Cheng return (err); 251225ec3e3dSEric Cheng 2513550b6e40SSowmini Varadhan if (err != 0) 2514550b6e40SSowmini Varadhan return (err); 2515550b6e40SSowmini Varadhan 251625ec3e3dSEric Cheng mac_update_resources(mrp, MCIP_RESOURCE_PROPS(mcip), B_FALSE); 2517550b6e40SSowmini Varadhan i_mac_notify(((mcip->mci_state_flags & MCIS_IS_VNIC) != 0 ? 2518550b6e40SSowmini Varadhan mcip->mci_upper_mip : mip), MAC_NOTE_ALLOWED_IPS); 251925ec3e3dSEric Cheng return (0); 252025ec3e3dSEric Cheng } 252125ec3e3dSEric Cheng 252225ec3e3dSEric Cheng void 252325ec3e3dSEric Cheng mac_protect_update(mac_resource_props_t *new, mac_resource_props_t *curr) 252425ec3e3dSEric Cheng { 252525ec3e3dSEric Cheng mac_protect_t *np = &new->mrp_protect; 252625ec3e3dSEric Cheng mac_protect_t *cp = &curr->mrp_protect; 252725ec3e3dSEric Cheng uint32_t types = np->mp_types; 252825ec3e3dSEric Cheng 252925ec3e3dSEric Cheng if (types == MPT_RESET) { 253025ec3e3dSEric Cheng cp->mp_types = 0; 253125ec3e3dSEric Cheng curr->mrp_mask &= ~MRP_PROTECT; 253225ec3e3dSEric Cheng } else { 253325ec3e3dSEric Cheng if (types != 0) { 253425ec3e3dSEric Cheng cp->mp_types = types; 253525ec3e3dSEric Cheng curr->mrp_mask |= MRP_PROTECT; 253625ec3e3dSEric Cheng } 253725ec3e3dSEric Cheng } 253825ec3e3dSEric Cheng if (np->mp_ipaddrcnt != 0) { 25390dc2366fSVenugopal Iyer if (np->mp_ipaddrcnt <= MPT_MAXIPADDR) { 254025ec3e3dSEric Cheng bcopy(np->mp_ipaddrs, cp->mp_ipaddrs, 254125ec3e3dSEric Cheng sizeof (cp->mp_ipaddrs)); 254225ec3e3dSEric Cheng cp->mp_ipaddrcnt = np->mp_ipaddrcnt; 254325ec3e3dSEric Cheng } else if (np->mp_ipaddrcnt == MPT_RESET) { 254425ec3e3dSEric Cheng bzero(cp->mp_ipaddrs, sizeof (cp->mp_ipaddrs)); 254525ec3e3dSEric Cheng cp->mp_ipaddrcnt = 0; 254625ec3e3dSEric Cheng } 254725ec3e3dSEric Cheng } 25480dc2366fSVenugopal Iyer if (np->mp_cidcnt != 0) { 25490dc2366fSVenugopal Iyer if (np->mp_cidcnt <= MPT_MAXCID) { 25500dc2366fSVenugopal Iyer bcopy(np->mp_cids, cp->mp_cids, sizeof (cp->mp_cids)); 25510dc2366fSVenugopal Iyer cp->mp_cidcnt = np->mp_cidcnt; 25520dc2366fSVenugopal Iyer } else if (np->mp_cidcnt == MPT_RESET) { 25530dc2366fSVenugopal Iyer bzero(cp->mp_cids, sizeof (cp->mp_cids)); 25540dc2366fSVenugopal Iyer cp->mp_cidcnt = 0; 25550dc2366fSVenugopal Iyer } 25560dc2366fSVenugopal Iyer } 25570dc2366fSVenugopal Iyer } 25580dc2366fSVenugopal Iyer 25590dc2366fSVenugopal Iyer void 25600dc2366fSVenugopal Iyer mac_protect_init(mac_client_impl_t *mcip) 25610dc2366fSVenugopal Iyer { 25620dc2366fSVenugopal Iyer mutex_init(&mcip->mci_protect_lock, NULL, MUTEX_DRIVER, NULL); 25630dc2366fSVenugopal Iyer mcip->mci_protect_flags = 0; 25640dc2366fSVenugopal Iyer mcip->mci_txn_cleanup_tid = 0; 25650dc2366fSVenugopal Iyer avl_create(&mcip->mci_v4_pending_txn, compare_dhcpv4_xid, 25660dc2366fSVenugopal Iyer sizeof (dhcpv4_txn_t), offsetof(dhcpv4_txn_t, dt_node)); 25670dc2366fSVenugopal Iyer avl_create(&mcip->mci_v4_completed_txn, compare_dhcpv4_cid, 25680dc2366fSVenugopal Iyer sizeof (dhcpv4_txn_t), offsetof(dhcpv4_txn_t, dt_node)); 25690dc2366fSVenugopal Iyer avl_create(&mcip->mci_v4_dyn_ip, compare_dhcpv4_ip, 25700dc2366fSVenugopal Iyer sizeof (dhcpv4_txn_t), offsetof(dhcpv4_txn_t, dt_ipnode)); 25710dc2366fSVenugopal Iyer avl_create(&mcip->mci_v6_pending_txn, compare_dhcpv6_xid, 25720dc2366fSVenugopal Iyer sizeof (dhcpv6_txn_t), offsetof(dhcpv6_txn_t, dt_node)); 25730dc2366fSVenugopal Iyer avl_create(&mcip->mci_v6_cid, compare_dhcpv6_cid, 25740dc2366fSVenugopal Iyer sizeof (dhcpv6_cid_t), offsetof(dhcpv6_cid_t, dc_node)); 25750dc2366fSVenugopal Iyer avl_create(&mcip->mci_v6_dyn_ip, compare_dhcpv6_ip, 25760dc2366fSVenugopal Iyer sizeof (dhcpv6_addr_t), offsetof(dhcpv6_addr_t, da_node)); 2577*4e6f6c83SCody Peter Mello avl_create(&mcip->mci_v6_slaac_ip, compare_slaac_ip, 2578*4e6f6c83SCody Peter Mello sizeof (slaac_addr_t), offsetof(slaac_addr_t, sla_node)); 2579098d2c75SRobert Mustacchi 2580098d2c75SRobert Mustacchi if (mcip->mci_state_flags & MCIS_IS_VNIC) 2581098d2c75SRobert Mustacchi mcip->mci_protect_flags |= MPT_FLAG_PROMISC_FILTERED; 25820dc2366fSVenugopal Iyer } 25830dc2366fSVenugopal Iyer 25840dc2366fSVenugopal Iyer void 25850dc2366fSVenugopal Iyer mac_protect_fini(mac_client_impl_t *mcip) 25860dc2366fSVenugopal Iyer { 25870dc2366fSVenugopal Iyer avl_destroy(&mcip->mci_v6_dyn_ip); 25880dc2366fSVenugopal Iyer avl_destroy(&mcip->mci_v6_cid); 25890dc2366fSVenugopal Iyer avl_destroy(&mcip->mci_v6_pending_txn); 25900dc2366fSVenugopal Iyer avl_destroy(&mcip->mci_v4_dyn_ip); 25910dc2366fSVenugopal Iyer avl_destroy(&mcip->mci_v4_completed_txn); 25920dc2366fSVenugopal Iyer avl_destroy(&mcip->mci_v4_pending_txn); 2593*4e6f6c83SCody Peter Mello avl_destroy(&mcip->mci_v6_slaac_ip); 25940dc2366fSVenugopal Iyer mcip->mci_txn_cleanup_tid = 0; 25950dc2366fSVenugopal Iyer mcip->mci_protect_flags = 0; 25960dc2366fSVenugopal Iyer mutex_destroy(&mcip->mci_protect_lock); 259725ec3e3dSEric Cheng } 2598550b6e40SSowmini Varadhan 2599550b6e40SSowmini Varadhan static boolean_t 2600550b6e40SSowmini Varadhan allowed_ips_set(mac_resource_props_t *mrp, uint32_t af) 2601550b6e40SSowmini Varadhan { 2602550b6e40SSowmini Varadhan int i; 2603550b6e40SSowmini Varadhan 2604550b6e40SSowmini Varadhan for (i = 0; i < mrp->mrp_protect.mp_ipaddrcnt; i++) { 2605550b6e40SSowmini Varadhan if (mrp->mrp_protect.mp_ipaddrs[i].ip_version == af) 2606550b6e40SSowmini Varadhan return (B_TRUE); 2607550b6e40SSowmini Varadhan } 2608550b6e40SSowmini Varadhan return (B_FALSE); 2609550b6e40SSowmini Varadhan } 2610550b6e40SSowmini Varadhan 261189c6130dSSowmini Varadhan mac_protect_t * 261289c6130dSSowmini Varadhan mac_protect_get(mac_handle_t mh) 2613550b6e40SSowmini Varadhan { 2614550b6e40SSowmini Varadhan mac_impl_t *mip = (mac_impl_t *)mh; 2615550b6e40SSowmini Varadhan 261689c6130dSSowmini Varadhan return (&mip->mi_resource_props.mrp_protect); 2617550b6e40SSowmini Varadhan } 2618