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 
30797f979dSCody 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;
149797f979dSCody 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 
201797f979dSCody Peter Mello /*
202797f979dSCody Peter Mello  * Stateless address autoconfiguration (SLAAC) address. May be added to
203797f979dSCody Peter Mello  * mci_v6_slaac_ip.
204797f979dSCody Peter Mello  */
205797f979dSCody Peter Mello typedef struct slaac_addr {
206797f979dSCody Peter Mello 	in6_addr_t		sla_prefix;
207797f979dSCody Peter Mello 	in6_addr_t		sla_addr;
208797f979dSCody Peter Mello 	avl_node_t		sla_node;
209797f979dSCody Peter Mello } slaac_addr_t;
210797f979dSCody 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
compare_dhcpv4_xid(const void * arg1,const void * arg2)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
compare_dhcpv4_cid(const void * arg1,const void * arg2)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
compare_dhcpv4_ip(const void * arg1,const void * arg2)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
get_dhcpv4_option(struct dhcp * dh4,uchar_t * end,uint8_t type,uchar_t ** opt,uint8_t * opt_len)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
get_dhcpv4_info(ipha_t * ipha,uchar_t * end,struct dhcp ** dh4)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 *
find_dhcpv4_pending_txn(mac_client_impl_t * mcip,uint32_t xid)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
insert_dhcpv4_pending_txn(mac_client_impl_t * mcip,dhcpv4_txn_t * txn)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
remove_dhcpv4_pending_txn(mac_client_impl_t * mcip,dhcpv4_txn_t * txn)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 *
find_dhcpv4_completed_txn(mac_client_impl_t * mcip,uint8_t * cid,uint8_t cid_len)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
insert_dhcpv4_completed_txn(mac_client_impl_t * mcip,dhcpv4_txn_t * txn)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
remove_dhcpv4_completed_txn(mac_client_impl_t * mcip,dhcpv4_txn_t * txn)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.
4490dc2366fSVenugopal Iyer  */
4500dc2366fSVenugopal Iyer static boolean_t
check_dhcpv4_dyn_ip(mac_client_impl_t * mcip,ipaddr_t ipaddr)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 *
create_dhcpv4_txn(uint32_t xid,uint8_t * cid,uint8_t cid_len,ipaddr_t ipaddr)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
free_dhcpv4_txn(dhcpv4_txn_t * txn)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
flush_dhcpv4(mac_client_impl_t * mcip)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
txn_cleanup_v4(mac_client_impl_t * mcip)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
intercept_dhcpv4_outbound(mac_client_impl_t * mcip,ipha_t * ipha,uchar_t * end)5710dc2366fSVenugopal Iyer intercept_dhcpv4_outbound(mac_client_impl_t *mcip, ipha_t *ipha, uchar_t *end)
5720dc2366fSVenugopal Iyer {
573550b6e40SSowmini Varadhan 	struct dhcp		*dh4;
574550b6e40SSowmini Varadhan 	uchar_t			*opt;
575550b6e40SSowmini Varadhan 	dhcpv4_txn_t		*txn, *ctxn;
576550b6e40SSowmini Varadhan 	ipaddr_t		ipaddr;
577550b6e40SSowmini Varadhan 	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
intercept_dhcpv4_inbound(mac_client_impl_t * mcip,uchar_t * end,struct dhcp * dh4)669797f979dSCody Peter Mello intercept_dhcpv4_inbound(mac_client_impl_t *mcip, uchar_t *end,
670797f979dSCody 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
compare_dhcpv6_xid(const void * arg1,const void * arg2)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
compare_dhcpv6_ip(const void * arg1,const void * arg2)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
compare_dhcpv6_cid(const void * arg1,const void * arg2)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 
783797f979dSCody Peter Mello static int
compare_slaac_ip(const void * arg1,const void * arg2)784797f979dSCody Peter Mello compare_slaac_ip(const void *arg1, const void *arg2)
785797f979dSCody Peter Mello {
786797f979dSCody Peter Mello 	const slaac_addr_t	*ip1 = arg1, *ip2 = arg2;
787797f979dSCody Peter Mello 	int			ret;
788797f979dSCody Peter Mello 
789797f979dSCody Peter Mello 	ret = memcmp(&ip1->sla_addr, &ip2->sla_addr, sizeof (in6_addr_t));
790797f979dSCody Peter Mello 	if (ret < 0)
791797f979dSCody Peter Mello 		return (-1);
792797f979dSCody Peter Mello 	else if (ret > 0)
793797f979dSCody Peter Mello 		return (1);
794797f979dSCody Peter Mello 	else
795797f979dSCody Peter Mello 		return (0);
796797f979dSCody Peter Mello }
797797f979dSCody 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
get_dhcpv6_info(ip6_t * ip6h,uchar_t * end,dhcpv6_message_t ** dh6)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 
856797f979dSCody Peter Mello static int
get_ra_info(ip6_t * ip6h,uchar_t * end,nd_router_advert_t ** ra)857797f979dSCody Peter Mello get_ra_info(ip6_t *ip6h, uchar_t *end, nd_router_advert_t **ra)
858797f979dSCody Peter Mello {
859797f979dSCody Peter Mello 	uint16_t		hdrlen;
860797f979dSCody Peter Mello 	ip6_frag_t		*frag = NULL;
861797f979dSCody Peter Mello 	uint8_t			proto;
862797f979dSCody Peter Mello 	uchar_t			*hdrp;
863797f979dSCody Peter Mello 	struct icmp6_hdr	*icmp;
864797f979dSCody Peter Mello 
865797f979dSCody Peter Mello 	if (!mac_ip_hdr_length_v6(ip6h, end, &hdrlen, &proto, &frag))
866797f979dSCody Peter Mello 		return (ENOSPC);
867797f979dSCody Peter Mello 
868797f979dSCody Peter Mello 	if (proto != IPPROTO_ICMPV6)
869797f979dSCody Peter Mello 		return (EINVAL);
870797f979dSCody Peter Mello 
871797f979dSCody Peter Mello 	if (frag != NULL) {
872797f979dSCody Peter Mello 		/*
873797f979dSCody Peter Mello 		 * All non-initial fragments may pass because we cannot
874797f979dSCody Peter Mello 		 * identify their type. It's safe to let them through
875797f979dSCody Peter Mello 		 * because reassembly will fail if we decide to drop the
876797f979dSCody Peter Mello 		 * initial fragment.
877797f979dSCody Peter Mello 		 */
878797f979dSCody Peter Mello 		if ((ntohs(frag->ip6f_offlg) & ~7) != 0)
879797f979dSCody Peter Mello 			return (EINVAL);
880797f979dSCody Peter Mello 		return (ENOSPC);
881797f979dSCody Peter Mello 	}
882797f979dSCody Peter Mello 
883797f979dSCody Peter Mello 	/*
884797f979dSCody Peter Mello 	 * Ensure that the ICMP header falls w/in packet boundaries, in case
885797f979dSCody Peter Mello 	 * we've received a malicious packet that reports incorrect lengths.
886797f979dSCody Peter Mello 	 */
887797f979dSCody Peter Mello 	hdrp = (uchar_t *)ip6h + hdrlen;
888797f979dSCody Peter Mello 	if ((hdrp + sizeof (struct icmp6_hdr)) > end) {
889797f979dSCody Peter Mello 		return (EINVAL);
890797f979dSCody Peter Mello 	}
891797f979dSCody Peter Mello 	icmp = (struct icmp6_hdr *)hdrp;
892797f979dSCody Peter Mello 
893797f979dSCody Peter Mello 	if (icmp->icmp6_type != ND_ROUTER_ADVERT ||
894797f979dSCody Peter Mello 	    icmp->icmp6_code != 0)
895797f979dSCody Peter Mello 		return (EINVAL);
896797f979dSCody Peter Mello 
897797f979dSCody Peter Mello 	*ra = (nd_router_advert_t *)icmp;
898797f979dSCody Peter Mello 	return (0);
899797f979dSCody Peter Mello }
900797f979dSCody Peter Mello 
9010dc2366fSVenugopal Iyer /*
9020dc2366fSVenugopal Iyer  * Find the specified DHCPv6 option.
9030dc2366fSVenugopal Iyer  */
9040dc2366fSVenugopal Iyer static dhcpv6_option_t *
get_dhcpv6_option(void * buf,size_t buflen,dhcpv6_option_t * oldopt,uint16_t codenum,uint_t * retlenp)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
get_dhcpv6_status(dhcpv6_message_t * dh6,uchar_t * end,uint16_t * status)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
get_dhcpv6_addrs(dhcpv6_message_t * dh6,uchar_t * end,dhcpv6_cid_t * cid)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
free_dhcpv6_cid(dhcpv6_cid_t * cid)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 *
create_dhcpv6_cid(dhcpv6_message_t * dh6,uchar_t * end)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
remove_dhcpv6_cid(mac_client_impl_t * mcip,dhcpv6_cid_t * cid)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
release_dhcpv6_cid(mac_client_impl_t * mcip,dhcpv6_cid_t * cid)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
insert_dhcpv6_cid(mac_client_impl_t * mcip,dhcpv6_cid_t * cid)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
check_dhcpv6_dyn_ip(mac_client_impl_t * mcip,in6_addr_t * addr)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 *
find_dhcpv6_pending_txn(mac_client_impl_t * mcip,uint32_t xid)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
remove_dhcpv6_pending_txn(mac_client_impl_t * mcip,dhcpv6_txn_t * txn)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 *
create_dhcpv6_txn(uint32_t xid,dhcpv6_cid_t * cid)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
free_dhcpv6_txn(dhcpv6_txn_t * txn)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
insert_dhcpv6_pending_txn(mac_client_impl_t * mcip,dhcpv6_txn_t * txn)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
flush_dhcpv6(mac_client_impl_t * mcip)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 
1244797f979dSCody Peter Mello void
flush_slaac(mac_client_impl_t * mcip)1245797f979dSCody Peter Mello flush_slaac(mac_client_impl_t *mcip)
1246797f979dSCody Peter Mello {
1247797f979dSCody Peter Mello 	void		*cookie = NULL;
1248797f979dSCody Peter Mello 	slaac_addr_t	*addr = NULL;
1249797f979dSCody Peter Mello 
1250797f979dSCody Peter Mello 	while ((addr = avl_destroy_nodes(&mcip->mci_v6_slaac_ip, &cookie)) !=
1251797f979dSCody Peter Mello 	    NULL) {
1252797f979dSCody Peter Mello 		kmem_free(addr, sizeof (slaac_addr_t));
1253797f979dSCody Peter Mello 	}
1254797f979dSCody Peter Mello }
1255797f979dSCody Peter Mello 
12560dc2366fSVenugopal Iyer /*
12570dc2366fSVenugopal Iyer  * Cleanup stale DHCPv6 transactions.
12580dc2366fSVenugopal Iyer  */
12590dc2366fSVenugopal Iyer static void
txn_cleanup_v6(mac_client_impl_t * mcip)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
intercept_dhcpv6_outbound(mac_client_impl_t * mcip,ip6_t * ip6h,uchar_t * end)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
intercept_dhcpv6_inbound(mac_client_impl_t * mcip,uchar_t * end,dhcpv6_message_t * dh6)1371797f979dSCody Peter Mello intercept_dhcpv6_inbound(mac_client_impl_t *mcip, uchar_t *end,
1372797f979dSCody 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 
1420797f979dSCody Peter Mello /*
1421797f979dSCody Peter Mello  * Check whether an IP address is in the SLAAC table.
1422797f979dSCody Peter Mello  */
1423797f979dSCody Peter Mello static boolean_t
check_slaac_ip(mac_client_impl_t * mcip,in6_addr_t * addr)1424797f979dSCody Peter Mello check_slaac_ip(mac_client_impl_t *mcip, in6_addr_t *addr)
1425797f979dSCody Peter Mello {
1426797f979dSCody Peter Mello 	slaac_addr_t	tmp_addr, *a;
1427797f979dSCody Peter Mello 
1428797f979dSCody Peter Mello 	mutex_enter(&mcip->mci_protect_lock);
1429797f979dSCody Peter Mello 	bcopy(addr, &tmp_addr.sla_addr, sizeof (in6_addr_t));
1430797f979dSCody Peter Mello 	a = avl_find(&mcip->mci_v6_slaac_ip, &tmp_addr, NULL);
1431797f979dSCody Peter Mello 	mutex_exit(&mcip->mci_protect_lock);
1432797f979dSCody Peter Mello 	return (a != NULL);
1433797f979dSCody Peter Mello }
1434797f979dSCody Peter Mello 
1435797f979dSCody Peter Mello static boolean_t
insert_slaac_ip(avl_tree_t * tree,in6_addr_t * token,slaac_addr_t * addr)1436797f979dSCody Peter Mello insert_slaac_ip(avl_tree_t *tree, in6_addr_t *token, slaac_addr_t *addr)
1437797f979dSCody Peter Mello {
1438797f979dSCody Peter Mello 	uint_t		i;
1439797f979dSCody Peter Mello 	avl_index_t	where;
1440797f979dSCody Peter Mello 	in6_addr_t	*prefix = &addr->sla_prefix;
1441797f979dSCody Peter Mello 	in6_addr_t	*in6p = &addr->sla_addr;
1442797f979dSCody Peter Mello 
1443797f979dSCody Peter Mello 	bcopy(prefix, in6p, sizeof (struct in6_addr));
1444797f979dSCody Peter Mello 
1445797f979dSCody Peter Mello 	for (i = 0; i < 4; i++) {
1446797f979dSCody Peter Mello 		in6p->s6_addr32[i] = token->s6_addr32[i] |
1447797f979dSCody Peter Mello 		    in6p->s6_addr32[i];
1448797f979dSCody Peter Mello 	}
1449797f979dSCody Peter Mello 
1450797f979dSCody Peter Mello 	DTRACE_PROBE1(generated__addr, in6_addr_t *, in6p);
1451797f979dSCody Peter Mello 
1452797f979dSCody Peter Mello 	if (avl_find(tree, addr, &where) != NULL)
1453797f979dSCody Peter Mello 		return (B_FALSE);
1454797f979dSCody Peter Mello 
1455797f979dSCody Peter Mello 	avl_insert(tree, addr, where);
1456797f979dSCody Peter Mello 	return (B_TRUE);
1457797f979dSCody Peter Mello }
1458797f979dSCody Peter Mello 
1459797f979dSCody Peter Mello static void
insert_slaac_prefix(mac_client_impl_t * mcip,nd_opt_prefix_info_t * po)1460797f979dSCody Peter Mello insert_slaac_prefix(mac_client_impl_t *mcip, nd_opt_prefix_info_t *po)
1461797f979dSCody Peter Mello {
1462797f979dSCody Peter Mello 	slaac_addr_t	*addr = NULL;
1463797f979dSCody Peter Mello 	in6_addr_t	*token = &mcip->mci_v6_mac_token;
1464797f979dSCody Peter Mello 
1465797f979dSCody Peter Mello 	ASSERT(MUTEX_HELD(&mcip->mci_protect_lock));
1466797f979dSCody Peter Mello 
1467797f979dSCody Peter Mello 	if (avl_numnodes(&mcip->mci_v6_slaac_ip) >= slaac_max_allowed) {
1468797f979dSCody Peter Mello 		DTRACE_PROBE(limit__reached);
1469797f979dSCody Peter Mello 		return;
1470797f979dSCody Peter Mello 	}
1471797f979dSCody Peter Mello 
1472*ca783257SDan McDonald 	if ((addr = kmem_zalloc(sizeof (slaac_addr_t), KM_NOSLEEP_LAZY)) ==
1473*ca783257SDan McDonald 	    NULL)
1474797f979dSCody Peter Mello 		return;
1475797f979dSCody Peter Mello 
1476797f979dSCody Peter Mello 	bcopy(&po->nd_opt_pi_prefix, &addr->sla_prefix,
1477797f979dSCody Peter Mello 	    sizeof (struct in6_addr));
1478797f979dSCody Peter Mello 
1479797f979dSCody Peter Mello 	if (!insert_slaac_ip(&mcip->mci_v6_slaac_ip, token, addr)) {
1480797f979dSCody Peter Mello 		kmem_free(addr, sizeof (slaac_addr_t));
1481797f979dSCody Peter Mello 	}
1482797f979dSCody Peter Mello }
1483797f979dSCody Peter Mello 
1484797f979dSCody Peter Mello static void
intercept_prefix_info(mac_client_impl_t * mcip,nd_opt_prefix_info_t * po)1485797f979dSCody Peter Mello intercept_prefix_info(mac_client_impl_t *mcip, nd_opt_prefix_info_t *po)
1486797f979dSCody Peter Mello {
1487797f979dSCody Peter Mello 	if (8 * po->nd_opt_pi_len != sizeof (nd_opt_prefix_info_t)) {
1488797f979dSCody Peter Mello 		DTRACE_PROBE(invalid__length);
1489797f979dSCody Peter Mello 		return;
1490797f979dSCody Peter Mello 	}
1491797f979dSCody Peter Mello 
1492797f979dSCody Peter Mello 	if (po->nd_opt_pi_prefix_len > 128) {
1493797f979dSCody Peter Mello 		DTRACE_PROBE(invalid__plen);
1494797f979dSCody Peter Mello 		return;
1495797f979dSCody Peter Mello 	}
1496797f979dSCody Peter Mello 
1497797f979dSCody Peter Mello 	if (IN6_IS_ADDR_LINKLOCAL(&po->nd_opt_pi_prefix)) {
1498797f979dSCody Peter Mello 		DTRACE_PROBE(link__local);
1499797f979dSCody Peter Mello 		return;
1500797f979dSCody Peter Mello 	}
1501797f979dSCody Peter Mello 
1502797f979dSCody Peter Mello 	if ((po->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_AUTO) == 0)
1503797f979dSCody Peter Mello 		return;
1504797f979dSCody Peter Mello 
1505797f979dSCody Peter Mello 	mutex_enter(&mcip->mci_protect_lock);
1506797f979dSCody Peter Mello 	insert_slaac_prefix(mcip, po);
1507797f979dSCody Peter Mello 	mutex_exit(&mcip->mci_protect_lock);
1508797f979dSCody Peter Mello }
1509797f979dSCody Peter Mello 
1510797f979dSCody Peter Mello /*
1511797f979dSCody Peter Mello  * If we receive a Router Advertisement carrying prefix information and
1512797f979dSCody Peter Mello  * indicating that SLAAC should be performed, then track the prefix.
1513797f979dSCody Peter Mello  */
1514797f979dSCody Peter Mello static void
intercept_ra_inbound(mac_client_impl_t * mcip,ip6_t * ip6h,uchar_t * end,nd_router_advert_t * ra)1515797f979dSCody Peter Mello intercept_ra_inbound(mac_client_impl_t *mcip, ip6_t *ip6h, uchar_t *end,
1516797f979dSCody Peter Mello     nd_router_advert_t *ra)
1517797f979dSCody Peter Mello {
1518797f979dSCody Peter Mello 	struct nd_opt_hdr *opt;
1519797f979dSCody Peter Mello 	int len, optlen;
1520797f979dSCody Peter Mello 
1521797f979dSCody Peter Mello 	if (ip6h->ip6_hlim != 255) {
1522797f979dSCody Peter Mello 		DTRACE_PROBE1(invalid__hoplimit, uint8_t, ip6h->ip6_hlim);
1523797f979dSCody Peter Mello 		return;
1524797f979dSCody Peter Mello 	}
1525797f979dSCody Peter Mello 
1526797f979dSCody Peter Mello 	len = ip6h->ip6_plen - sizeof (nd_router_advert_t);
1527797f979dSCody Peter Mello 	opt = (struct nd_opt_hdr *)&ra[1];
1528797f979dSCody Peter Mello 	while (len >= sizeof (struct nd_opt_hdr) &&
1529797f979dSCody Peter Mello 	    ((uchar_t *)opt + sizeof (struct nd_opt_hdr)) <= end) {
1530797f979dSCody Peter Mello 		optlen = opt->nd_opt_len * 8;
1531797f979dSCody Peter Mello 
1532797f979dSCody Peter Mello 		if (optlen < sizeof (struct nd_opt_hdr) ||
1533797f979dSCody Peter Mello 		    ((uchar_t *)opt + optlen) > end) {
1534797f979dSCody Peter Mello 			DTRACE_PROBE(invalid__length);
1535797f979dSCody Peter Mello 			return;
1536797f979dSCody Peter Mello 		}
1537797f979dSCody Peter Mello 
1538797f979dSCody Peter Mello 		if (opt->nd_opt_type == ND_OPT_PREFIX_INFORMATION) {
1539797f979dSCody Peter Mello 			intercept_prefix_info(mcip,
1540797f979dSCody Peter Mello 			    (nd_opt_prefix_info_t *)opt);
1541797f979dSCody Peter Mello 		}
1542797f979dSCody Peter Mello 
1543797f979dSCody Peter Mello 		opt = (struct nd_opt_hdr *)((char *)opt + optlen);
1544797f979dSCody Peter Mello 		len -= optlen;
1545797f979dSCody Peter Mello 	}
1546797f979dSCody Peter Mello }
1547797f979dSCody Peter Mello 
15480dc2366fSVenugopal Iyer /*
15490dc2366fSVenugopal Iyer  * Timer for cleaning up stale transactions.
15500dc2366fSVenugopal Iyer  */
15510dc2366fSVenugopal Iyer static void
txn_cleanup_timer(void * arg)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
start_txn_cleanup_timer(mac_client_impl_t * mcip)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
cancel_txn_cleanup_timer(mac_client_impl_t * mcip)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
get_l3_info(mblk_t * mp,size_t hdrsize,uchar_t ** start,uchar_t ** end,mblk_t ** nmp)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
mac_protect_intercept_dynamic_one(mac_client_impl_t * mcip,mblk_t * mp)1655797f979dSCody 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: {
1679797f979dSCody Peter Mello 		struct dhcp	*dh4;
1680797f979dSCody Peter Mello 		ipha_t		*ipha = (ipha_t *)start;
16810dc2366fSVenugopal Iyer 
16820dc2366fSVenugopal Iyer 		if (start + sizeof (ipha_t) > end)
16830dc2366fSVenugopal Iyer 			return;
16840dc2366fSVenugopal Iyer 
1685797f979dSCody Peter Mello 		if (get_dhcpv4_info(ipha, end, &dh4) == 0) {
1686797f979dSCody Peter Mello 			intercept_dhcpv4_inbound(mcip, end, dh4);
1687797f979dSCody Peter Mello 		}
16880dc2366fSVenugopal Iyer 		break;
16890dc2366fSVenugopal Iyer 	}
16900dc2366fSVenugopal Iyer 	case ETHERTYPE_IPV6: {
1691797f979dSCody Peter Mello 		dhcpv6_message_t	*dh6;
1692797f979dSCody Peter Mello 		nd_router_advert_t	*ra;
1693797f979dSCody Peter Mello 		ip6_t			*ip6h = (ip6_t *)start;
16940dc2366fSVenugopal Iyer 
16950dc2366fSVenugopal Iyer 		if (start + sizeof (ip6_t) > end)
16960dc2366fSVenugopal Iyer 			return;
16970dc2366fSVenugopal Iyer 
1698797f979dSCody Peter Mello 		if (get_dhcpv6_info(ip6h, end, &dh6) == 0) {
1699797f979dSCody Peter Mello 			intercept_dhcpv6_inbound(mcip, end, dh6);
1700797f979dSCody Peter Mello 		} else if (get_ra_info(ip6h, end, &ra) == 0) {
1701797f979dSCody Peter Mello 			intercept_ra_inbound(mcip, ip6h, end, ra);
1702797f979dSCody Peter Mello 		}
1703797f979dSCody Peter Mello 
17040dc2366fSVenugopal Iyer 		break;
17050dc2366fSVenugopal Iyer 	}
17060dc2366fSVenugopal Iyer 	}
17070dc2366fSVenugopal Iyer 	freemsg(nmp);
17080dc2366fSVenugopal Iyer }
17090dc2366fSVenugopal Iyer 
17100dc2366fSVenugopal Iyer void
mac_protect_intercept_dynamic(mac_client_impl_t * mcip,mblk_t * mp)1711797f979dSCody 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)
1720797f979dSCody Peter Mello 		mac_protect_intercept_dynamic_one(mcip, mp);
17210dc2366fSVenugopal Iyer }
17220dc2366fSVenugopal Iyer 
17230dc2366fSVenugopal Iyer void
mac_protect_flush_dynamic(mac_client_impl_t * mcip)1724797f979dSCody 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);
1729797f979dSCody Peter Mello 	flush_slaac(mcip);
17300dc2366fSVenugopal Iyer 	mutex_exit(&mcip->mci_protect_lock);
17310dc2366fSVenugopal Iyer }
17320dc2366fSVenugopal Iyer 
17330dc2366fSVenugopal Iyer void
mac_protect_cancel_timer(mac_client_impl_t * mcip)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.
174325ec3e3dSEric Cheng  */
17440dc2366fSVenugopal Iyer 
17450dc2366fSVenugopal Iyer /* ARGSUSED */
17460dc2366fSVenugopal Iyer static boolean_t
ipnospoof_check_v4(mac_client_impl_t * mcip,mac_protect_t * protect,ipaddr_t * addr)17470dc2366fSVenugopal Iyer ipnospoof_check_v4(mac_client_impl_t *mcip, mac_protect_t *protect,
17480dc2366fSVenugopal Iyer     ipaddr_t *addr)
17490dc2366fSVenugopal Iyer {
17500dc2366fSVenugopal Iyer 	uint_t	i;
17510dc2366fSVenugopal Iyer 
17520dc2366fSVenugopal Iyer 	/*
17530dc2366fSVenugopal Iyer 	 * The unspecified address is allowed.
17540dc2366fSVenugopal Iyer 	 */
17550dc2366fSVenugopal Iyer 	if (*addr == INADDR_ANY)
17560dc2366fSVenugopal Iyer 		return (B_TRUE);
17570dc2366fSVenugopal Iyer 
17580dc2366fSVenugopal Iyer 	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))
1775e03914f9SRobert Mustacchi 				return (B_TRUE);
1776e03914f9SRobert Mustacchi 		}
17770dc2366fSVenugopal Iyer 	}
1778550b6e40SSowmini Varadhan 	return (protect->mp_ipaddrcnt == 0 ?
1779550b6e40SSowmini Varadhan 	    check_dhcpv4_dyn_ip(mcip, *addr) : B_FALSE);
17800dc2366fSVenugopal Iyer }
17810dc2366fSVenugopal Iyer 
178225ec3e3dSEric Cheng static boolean_t
ipnospoof_check_v6(mac_client_impl_t * mcip,mac_protect_t * protect,in6_addr_t * addr)17830dc2366fSVenugopal Iyer ipnospoof_check_v6(mac_client_impl_t *mcip, mac_protect_t *protect,
17840dc2366fSVenugopal Iyer     in6_addr_t *addr)
178525ec3e3dSEric Cheng {
17860dc2366fSVenugopal Iyer 	uint_t	i;
178725ec3e3dSEric Cheng 
178825ec3e3dSEric Cheng 	/*
17890dc2366fSVenugopal Iyer 	 * The unspecified address and the v6 link local address are allowed.
179025ec3e3dSEric Cheng 	 */
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)))
179425ec3e3dSEric Cheng 		return (B_TRUE);
179525ec3e3dSEric Cheng 
17960dc2366fSVenugopal Iyer 
179725ec3e3dSEric Cheng 	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))
180425ec3e3dSEric Cheng 			return (B_TRUE);
180525ec3e3dSEric Cheng 	}
1806797f979dSCody Peter Mello 
1807797f979dSCody Peter Mello 	if (protect->mp_ipaddrcnt == 0) {
1808797f979dSCody Peter Mello 		return (check_slaac_ip(mcip, addr) ||
1809797f979dSCody Peter Mello 		    check_dhcpv6_dyn_ip(mcip, addr));
1810797f979dSCody Peter Mello 	} else {
1811797f979dSCody Peter Mello 		return (B_FALSE);
1812797f979dSCody Peter Mello 	}
181325ec3e3dSEric Cheng }
181425ec3e3dSEric Cheng 
181525ec3e3dSEric Cheng /*
18160dc2366fSVenugopal Iyer  * Checks various fields within an IPv6 NDP packet.
181725ec3e3dSEric Cheng  */
18180dc2366fSVenugopal Iyer static boolean_t
ipnospoof_check_ndp(mac_client_impl_t * mcip,mac_protect_t * protect,ip6_t * ip6h,uchar_t * end)18190dc2366fSVenugopal Iyer ipnospoof_check_ndp(mac_client_impl_t *mcip, mac_protect_t *protect,
18200dc2366fSVenugopal Iyer     ip6_t *ip6h, uchar_t *end)
182125ec3e3dSEric Cheng {
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;
182825ec3e3dSEric Cheng 
182925ec3e3dSEric Cheng 	/*
18300dc2366fSVenugopal Iyer 	 * NDP packets do not have extension headers so the ICMPv6 header
18310dc2366fSVenugopal Iyer 	 * must immediately follow the IPv6 header.
183225ec3e3dSEric Cheng 	 */
18330dc2366fSVenugopal Iyer 	if (ip6h->ip6_nxt != IPPROTO_ICMPV6)
18340dc2366fSVenugopal Iyer 		return (B_TRUE);
183525ec3e3dSEric Cheng 
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.
19280dc2366fSVenugopal Iyer  */
19290dc2366fSVenugopal Iyer static int
ipnospoof_check(mac_client_impl_t * mcip,mac_protect_t * protect,mblk_t * mp,mac_header_info_t * mhip)19300dc2366fSVenugopal Iyer ipnospoof_check(mac_client_impl_t *mcip, mac_protect_t *protect,
19310dc2366fSVenugopal Iyer     mblk_t *mp, mac_header_info_t *mhip)
19320dc2366fSVenugopal Iyer {
19330dc2366fSVenugopal Iyer 	size_t		hdrsize = mhip->mhi_hdrsize;
19340dc2366fSVenugopal Iyer 	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;
20050dc2366fSVenugopal Iyer 		break;
20060dc2366fSVenugopal Iyer 	}
20070dc2366fSVenugopal Iyer 	}
20080dc2366fSVenugopal Iyer 	freemsg(nmp);
20090dc2366fSVenugopal Iyer 	return (0);
20100dc2366fSVenugopal Iyer 
20110dc2366fSVenugopal Iyer fail:
20120dc2366fSVenugopal Iyer 	freemsg(nmp);
20130dc2366fSVenugopal Iyer 	return (err);
20140dc2366fSVenugopal Iyer }
20150dc2366fSVenugopal Iyer 
20160dc2366fSVenugopal Iyer static boolean_t
dhcpnospoof_check_cid(mac_protect_t * p,uchar_t * cid,uint_t cidlen)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
dhcpnospoof_check_v4(mac_client_impl_t * mcip,mac_protect_t * p,ipha_t * ipha,uchar_t * end)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
dhcpnospoof_check_v6(mac_client_impl_t * mcip,mac_protect_t * p,ip6_t * ip6h,uchar_t * end)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
dhcpnospoof_check(mac_client_impl_t * mcip,mac_protect_t * protect,mblk_t * mp,mac_header_info_t * mhip)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 
215525ec3e3dSEric Cheng 		break;
215625ec3e3dSEric Cheng 	}
21570dc2366fSVenugopal Iyer 	}
21580dc2366fSVenugopal Iyer 	freemsg(nmp);
215925ec3e3dSEric Cheng 	return (0);
216025ec3e3dSEric Cheng 
216125ec3e3dSEric Cheng fail:
21620dc2366fSVenugopal Iyer 	/* increment dhcpnospoof stat here */
21630dc2366fSVenugopal Iyer 	freemsg(nmp);
216425ec3e3dSEric Cheng 	return (err);
216525ec3e3dSEric Cheng }
216625ec3e3dSEric Cheng 
21670dc2366fSVenugopal Iyer /*
2168797f979dSCody Peter Mello  * This is called whenever the mac client's mac address changes, to make sure
2169797f979dSCody Peter Mello  * we allow use of the new link-local address.
21700dc2366fSVenugopal Iyer  */
2171797f979dSCody Peter Mello static void
mac_protect_update_v6_local_addr(mac_client_impl_t * mcip)21720dc2366fSVenugopal Iyer mac_protect_update_v6_local_addr(mac_client_impl_t *mcip)
21730dc2366fSVenugopal Iyer {
2174797f979dSCody Peter Mello 	uint_t		i;
2175797f979dSCody Peter Mello 	in6_addr_t	*token = &mcip->mci_v6_mac_token;
2176797f979dSCody 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 
2179797f979dSCody Peter Mello 	for (i = 0; i < 4; i++) {
2180797f979dSCody Peter Mello 		v6addr->s6_addr32[i] = token->s6_addr32[i] |
2181797f979dSCody Peter Mello 		    ll_template.s6_addr32[i];
2182797f979dSCody Peter Mello 	}
2183797f979dSCody Peter Mello 	mcip->mci_protect_flags |= MPT_FLAG_V6_LOCAL_ADDR_SET;
2184797f979dSCody Peter Mello }
21850dc2366fSVenugopal Iyer 
2186797f979dSCody Peter Mello /*
2187797f979dSCody Peter Mello  * This is called whenever the mac client's mac address changes, to make sure
2188797f979dSCody Peter Mello  * that any existing addresses gained via SLAAC are appropriately updated.
2189797f979dSCody Peter Mello  */
2190797f979dSCody Peter Mello static void
mac_protect_update_v6_slaac_addr(mac_client_impl_t * mcip)2191797f979dSCody Peter Mello mac_protect_update_v6_slaac_addr(mac_client_impl_t *mcip)
2192797f979dSCody Peter Mello {
2193797f979dSCody Peter Mello 	void		*cookie = NULL;
2194797f979dSCody Peter Mello 	avl_tree_t	temp_tree;
2195797f979dSCody Peter Mello 	avl_tree_t	*ttp = &temp_tree, *sip = &mcip->mci_v6_slaac_ip;
2196797f979dSCody Peter Mello 	in6_addr_t	*token = &mcip->mci_v6_mac_token;
2197797f979dSCody Peter Mello 	slaac_addr_t	*addr = NULL;
2198797f979dSCody Peter Mello 
2199797f979dSCody Peter Mello 	avl_create(ttp, compare_slaac_ip, sizeof (slaac_addr_t),
2200797f979dSCody Peter Mello 	    offsetof(slaac_addr_t, sla_node));
2201797f979dSCody Peter Mello 
2202797f979dSCody Peter Mello 	/* Copy everything over to the temporary tree, and fix the IP address */
2203797f979dSCody Peter Mello 	while ((addr = avl_destroy_nodes(sip, &cookie)) != NULL) {
2204797f979dSCody Peter Mello 		VERIFY(insert_slaac_ip(ttp, token, addr) == B_TRUE);
2205797f979dSCody Peter Mello 	}
2206797f979dSCody Peter Mello 
2207797f979dSCody Peter Mello 	/*
2208797f979dSCody Peter Mello 	 * Now that the tempory tree has all of the modified addresses, we can
2209797f979dSCody Peter Mello 	 * swap them over to the original tree once it's reset.
2210797f979dSCody Peter Mello 	 */
2211797f979dSCody Peter Mello 	avl_destroy(sip);
2212797f979dSCody Peter Mello 	avl_create(sip, compare_slaac_ip, sizeof (slaac_addr_t),
2213797f979dSCody Peter Mello 	    offsetof(slaac_addr_t, sla_node));
2214797f979dSCody Peter Mello 	avl_swap(ttp, sip);
2215797f979dSCody Peter Mello }
2216797f979dSCody Peter Mello 
2217797f979dSCody Peter Mello /*
2218797f979dSCody Peter Mello  * After the unicast MAC address changes, we need to update the derived token,
2219797f979dSCody Peter Mello  * and update the IPv6 addresses that use the token.
2220797f979dSCody Peter Mello  */
2221797f979dSCody Peter Mello void
mac_protect_update_mac_token(mac_client_impl_t * mcip)2222797f979dSCody Peter Mello mac_protect_update_mac_token(mac_client_impl_t *mcip)
2223797f979dSCody Peter Mello {
2224797f979dSCody Peter Mello 	uint_t		media = mcip->mci_mip->mi_info.mi_media;
2225797f979dSCody Peter Mello 	uint8_t		*p, *macaddr = mcip->mci_unicast->ma_addr;
2226797f979dSCody Peter Mello 	in6_addr_t	*token = &mcip->mci_v6_mac_token;
2227797f979dSCody Peter Mello 
2228797f979dSCody Peter Mello 	bzero(token, sizeof (in6_addr_t));
2229797f979dSCody 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
2248797f979dSCody 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 
2254797f979dSCody Peter Mello 	mac_protect_update_v6_local_addr(mcip);
2255797f979dSCody Peter Mello 	mac_protect_update_v6_slaac_addr(mcip);
22560dc2366fSVenugopal Iyer }
22570dc2366fSVenugopal Iyer 
2258797f979dSCody Peter Mello 
2259797f979dSCody Peter Mello 
226025ec3e3dSEric Cheng /*
226125ec3e3dSEric Cheng  * Enforce link protection on one packet.
226225ec3e3dSEric Cheng  */
226325ec3e3dSEric Cheng static int
mac_protect_check_one(mac_client_impl_t * mcip,mblk_t * mp)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 *
mac_protect_check(mac_client_handle_t mch,mblk_t * mp)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
mac_protect_enabled(mac_client_handle_t mch,uint32_t type)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
validate_ips(mac_protect_t * p)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
validate_cids(mac_protect_t * p)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
mac_protect_validate(mac_resource_props_t * mrp)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
mac_protect_set(mac_client_handle_t mch,mac_resource_props_t * mrp)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
mac_protect_update(mac_resource_props_t * new,mac_resource_props_t * curr)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
mac_protect_init(mac_client_impl_t * mcip)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));
2577797f979dSCody Peter Mello 	avl_create(&mcip->mci_v6_slaac_ip, compare_slaac_ip,
2578797f979dSCody Peter Mello 	    sizeof (slaac_addr_t), offsetof(slaac_addr_t, sla_node));
257910a40492SRobert Mustacchi 
258010a40492SRobert Mustacchi 	if (mcip->mci_state_flags & MCIS_IS_VNIC)
258110a40492SRobert Mustacchi 		mcip->mci_protect_flags |= MPT_FLAG_PROMISC_FILTERED;
25820dc2366fSVenugopal Iyer }
25830dc2366fSVenugopal Iyer 
25840dc2366fSVenugopal Iyer void
mac_protect_fini(mac_client_impl_t * mcip)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);
2593797f979dSCody 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
allowed_ips_set(mac_resource_props_t * mrp,uint32_t af)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 *
mac_protect_get(mac_handle_t mh)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