xref: /onnv-gate/usr/src/uts/common/io/mac/mac_protect.c (revision 12816:2ad74dc35297)
110734SEric Cheng /*
210734SEric Cheng  * CDDL HEADER START
310734SEric Cheng  *
410734SEric Cheng  * The contents of this file are subject to the terms of the
510734SEric Cheng  * Common Development and Distribution License (the "License").
610734SEric Cheng  * You may not use this file except in compliance with the License.
710734SEric Cheng  *
810734SEric Cheng  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
910734SEric Cheng  * or http://www.opensolaris.org/os/licensing.
1010734SEric Cheng  * See the License for the specific language governing permissions
1110734SEric Cheng  * and limitations under the License.
1210734SEric Cheng  *
1310734SEric Cheng  * When distributing Covered Code, include this CDDL HEADER in each
1410734SEric Cheng  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1510734SEric Cheng  * If applicable, add the following below this CDDL HEADER, with the
1610734SEric Cheng  * fields enclosed by brackets "[]" replaced with your own identifying
1710734SEric Cheng  * information: Portions Copyright [yyyy] [name of copyright owner]
1810734SEric Cheng  *
1910734SEric Cheng  * CDDL HEADER END
2010734SEric Cheng  */
2110734SEric Cheng 
2210734SEric Cheng /*
2312748SSowmini.Varadhan@oracle.COM  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
2410734SEric Cheng  */
2510734SEric Cheng 
2610734SEric Cheng #include <sys/strsun.h>
2710734SEric Cheng #include <sys/sdt.h>
2810734SEric Cheng #include <sys/mac.h>
2910734SEric Cheng #include <sys/mac_impl.h>
3010734SEric Cheng #include <sys/mac_client_impl.h>
3110734SEric Cheng #include <sys/mac_client_priv.h>
3210734SEric Cheng #include <sys/ethernet.h>
3310734SEric Cheng #include <sys/vlan.h>
3410734SEric Cheng #include <sys/dlpi.h>
3511878SVenu.Iyer@Sun.COM #include <sys/avl.h>
3610734SEric Cheng #include <inet/ip.h>
3710734SEric Cheng #include <inet/ip6.h>
3810734SEric Cheng #include <inet/arp.h>
3911878SVenu.Iyer@Sun.COM #include <netinet/arp.h>
4011878SVenu.Iyer@Sun.COM #include <netinet/udp.h>
4111878SVenu.Iyer@Sun.COM #include <netinet/dhcp.h>
4211878SVenu.Iyer@Sun.COM #include <netinet/dhcp6.h>
4311878SVenu.Iyer@Sun.COM 
4411878SVenu.Iyer@Sun.COM /*
4511878SVenu.Iyer@Sun.COM  * Implementation overview for DHCP address detection
4611878SVenu.Iyer@Sun.COM  *
4711878SVenu.Iyer@Sun.COM  * The purpose of DHCP address detection is to relieve the user of having to
4811878SVenu.Iyer@Sun.COM  * manually configure static IP addresses when ip-nospoof protection is turned
4911878SVenu.Iyer@Sun.COM  * on. To achieve this, the mac layer needs to intercept DHCP packets to
5011878SVenu.Iyer@Sun.COM  * determine the assigned IP addresses.
5111878SVenu.Iyer@Sun.COM  *
5211878SVenu.Iyer@Sun.COM  * A DHCP handshake between client and server typically requires at least
5311878SVenu.Iyer@Sun.COM  * 4 messages:
5411878SVenu.Iyer@Sun.COM  *
5511878SVenu.Iyer@Sun.COM  * 1. DISCOVER - client attempts to locate DHCP servers via a
5611878SVenu.Iyer@Sun.COM  *               broadcast message to its subnet.
5711878SVenu.Iyer@Sun.COM  * 2. OFFER    - server responds to client with an IP address and
5811878SVenu.Iyer@Sun.COM  *               other parameters.
5911878SVenu.Iyer@Sun.COM  * 3. REQUEST  - client requests the offered address.
6011878SVenu.Iyer@Sun.COM  * 4. ACK      - server verifies that the requested address matches
6111878SVenu.Iyer@Sun.COM  *               the one it offered.
6211878SVenu.Iyer@Sun.COM  *
6311878SVenu.Iyer@Sun.COM  * DHCPv6 behaves pretty much the same way aside from different message names.
6411878SVenu.Iyer@Sun.COM  *
6511878SVenu.Iyer@Sun.COM  * Address information is embedded in either the OFFER or REQUEST message.
6611878SVenu.Iyer@Sun.COM  * We chose to intercept REQUEST because this is at the last part of the
6711878SVenu.Iyer@Sun.COM  * handshake and it indicates that the client intends to keep the address.
6811878SVenu.Iyer@Sun.COM  * Intercepting OFFERs is unreliable because the client may receive multiple
6911878SVenu.Iyer@Sun.COM  * offers from different servers, and we can't tell which address the client
7011878SVenu.Iyer@Sun.COM  * will keep.
7111878SVenu.Iyer@Sun.COM  *
7211878SVenu.Iyer@Sun.COM  * Each DHCP message has a transaction ID. We use this transaction ID to match
7311878SVenu.Iyer@Sun.COM  * REQUESTs with ACKs received from servers.
7411878SVenu.Iyer@Sun.COM  *
7511878SVenu.Iyer@Sun.COM  * For IPv4, the process to acquire a DHCP-assigned address is as follows:
7611878SVenu.Iyer@Sun.COM  *
7711878SVenu.Iyer@Sun.COM  * 1. Client sends REQUEST. a new dhcpv4_txn_t object is created and inserted
7811878SVenu.Iyer@Sun.COM  *    in the the mci_v4_pending_txn table (keyed by xid). This object represents
7911878SVenu.Iyer@Sun.COM  *    a new transaction. It contains the xid, the client ID and requested IP
8011878SVenu.Iyer@Sun.COM  *    address.
8111878SVenu.Iyer@Sun.COM  *
8211878SVenu.Iyer@Sun.COM  * 2. Server responds with an ACK. The xid from this ACK is used to lookup the
8311878SVenu.Iyer@Sun.COM  *    pending transaction from the mci_v4_pending_txn table. Once the object is
8411878SVenu.Iyer@Sun.COM  *    found, it is removed from the pending table and inserted into the
8511878SVenu.Iyer@Sun.COM  *    completed table (mci_v4_completed_txn, keyed by client ID) and the dynamic
8611878SVenu.Iyer@Sun.COM  *    IP table (mci_v4_dyn_ip, keyed by IP address).
8711878SVenu.Iyer@Sun.COM  *
8811878SVenu.Iyer@Sun.COM  * 3. An outgoing packet that goes through the ip-nospoof path will be checked
8911878SVenu.Iyer@Sun.COM  *    against the dynamic IP table. Packets that have the assigned DHCP address
9011878SVenu.Iyer@Sun.COM  *    as the source IP address will pass the check and be admitted onto the
9111878SVenu.Iyer@Sun.COM  *    network.
9211878SVenu.Iyer@Sun.COM  *
9311878SVenu.Iyer@Sun.COM  * IPv4 notes:
9411878SVenu.Iyer@Sun.COM  *
9511878SVenu.Iyer@Sun.COM  * If the server never responds with an ACK, there is a timer that is set after
9611878SVenu.Iyer@Sun.COM  * the insertion of the transaction into the pending table. When the timer
9711878SVenu.Iyer@Sun.COM  * fires, it will check whether the transaction is old (by comparing current
9811878SVenu.Iyer@Sun.COM  * time and the txn's timestamp), if so the transaction will be freed. along
9911878SVenu.Iyer@Sun.COM  * with this, any transaction in the completed/dyn-ip tables matching the client
10011878SVenu.Iyer@Sun.COM  * ID of this stale transaction will also be freed. If the client fails to
10111878SVenu.Iyer@Sun.COM  * extend a lease, we want to stop the client from using any IP addresses that
10211878SVenu.Iyer@Sun.COM  * were granted previously.
10311878SVenu.Iyer@Sun.COM  *
10411878SVenu.Iyer@Sun.COM  * A RELEASE message from the client will not cause a transaction to be created.
10511878SVenu.Iyer@Sun.COM  * The client ID in the RELEASE message will be used for finding and removing
10611878SVenu.Iyer@Sun.COM  * transactions in the completed and dyn-ip tables.
10711878SVenu.Iyer@Sun.COM  *
10811878SVenu.Iyer@Sun.COM  *
10911878SVenu.Iyer@Sun.COM  * For IPv6, the process to acquire a DHCPv6-assigned address is as follows:
11011878SVenu.Iyer@Sun.COM  *
11111878SVenu.Iyer@Sun.COM  * 1. Client sends REQUEST. The DUID is extracted and stored into a dhcpv6_cid_t
11211878SVenu.Iyer@Sun.COM  *    structure. A new transaction structure (dhcpv6_txn_t) is also created and
11311878SVenu.Iyer@Sun.COM  *    it will point to the dhcpv6_cid_t. If an existing transaction with a
11411878SVenu.Iyer@Sun.COM  *    matching xid is not found, this dhcpv6_txn_t will be inserted into the
11511878SVenu.Iyer@Sun.COM  *    mci_v6_pending_txn table (keyed by xid).
11611878SVenu.Iyer@Sun.COM  *
11711878SVenu.Iyer@Sun.COM  * 2. Server responds with a REPLY. If a pending transaction is found, the
11811878SVenu.Iyer@Sun.COM  *    addresses in the reply will be placed into the dhcpv6_cid_t pointed to by
11911878SVenu.Iyer@Sun.COM  *    the transaction. The dhcpv6_cid_t will then be moved to the mci_v6_cid
12011878SVenu.Iyer@Sun.COM  *    table (keyed by cid). The associated addresses will be added to the
12111878SVenu.Iyer@Sun.COM  *    mci_v6_dyn_ip table (while still being pointed to by the dhcpv6_cid_t).
12211878SVenu.Iyer@Sun.COM  *
12311878SVenu.Iyer@Sun.COM  * 3. IPv6 ip-nospoof will now check mci_v6_dyn_ip for matching packets.
12411878SVenu.Iyer@Sun.COM  *    Packets with a source address matching one of the DHCPv6-assigned
12511878SVenu.Iyer@Sun.COM  *    addresses will be allowed through.
12611878SVenu.Iyer@Sun.COM  *
12711878SVenu.Iyer@Sun.COM  * IPv6 notes:
12811878SVenu.Iyer@Sun.COM  *
12911878SVenu.Iyer@Sun.COM  * The v6 code shares the same timer as v4 for scrubbing stale transactions.
13011878SVenu.Iyer@Sun.COM  * Just like v4, as part of removing an expired transaction, a RELEASE will be
13111878SVenu.Iyer@Sun.COM  * be triggered on the cid associated with the expired transaction.
13211878SVenu.Iyer@Sun.COM  *
13311878SVenu.Iyer@Sun.COM  * The data structures used for v6 are slightly different because a v6 client
13411878SVenu.Iyer@Sun.COM  * may have multiple addresses associated with it.
13511878SVenu.Iyer@Sun.COM  */
13611878SVenu.Iyer@Sun.COM 
13711878SVenu.Iyer@Sun.COM /*
13811878SVenu.Iyer@Sun.COM  * These are just arbitrary limits meant for preventing abuse (e.g. a user
13911878SVenu.Iyer@Sun.COM  * flooding the network with bogus transactions). They are not meant to be
14011878SVenu.Iyer@Sun.COM  * user-modifiable so they are not exposed as linkprops.
14111878SVenu.Iyer@Sun.COM  */
14211878SVenu.Iyer@Sun.COM static ulong_t	dhcp_max_pending_txn = 512;
14311878SVenu.Iyer@Sun.COM static ulong_t	dhcp_max_completed_txn = 512;
14411878SVenu.Iyer@Sun.COM static time_t	txn_cleanup_interval = 60;
14511878SVenu.Iyer@Sun.COM 
14611878SVenu.Iyer@Sun.COM /*
14711878SVenu.Iyer@Sun.COM  * DHCPv4 transaction. It may be added to three different tables
14811878SVenu.Iyer@Sun.COM  * (keyed by different fields).
14911878SVenu.Iyer@Sun.COM  */
15011878SVenu.Iyer@Sun.COM typedef struct dhcpv4_txn {
15111878SVenu.Iyer@Sun.COM 	uint32_t		dt_xid;
15211878SVenu.Iyer@Sun.COM 	time_t			dt_timestamp;
15311878SVenu.Iyer@Sun.COM 	uint8_t			dt_cid[DHCP_MAX_OPT_SIZE];
15411878SVenu.Iyer@Sun.COM 	uint8_t			dt_cid_len;
15511878SVenu.Iyer@Sun.COM 	ipaddr_t		dt_ipaddr;
15611878SVenu.Iyer@Sun.COM 	avl_node_t		dt_node;
15711878SVenu.Iyer@Sun.COM 	avl_node_t		dt_ipnode;
15811878SVenu.Iyer@Sun.COM 	struct dhcpv4_txn	*dt_next;
15911878SVenu.Iyer@Sun.COM } dhcpv4_txn_t;
16011878SVenu.Iyer@Sun.COM 
16111878SVenu.Iyer@Sun.COM /*
16211878SVenu.Iyer@Sun.COM  * DHCPv6 address. May be added to mci_v6_dyn_ip.
16311878SVenu.Iyer@Sun.COM  * It is always pointed to by its parent dhcpv6_cid_t structure.
16411878SVenu.Iyer@Sun.COM  */
16511878SVenu.Iyer@Sun.COM typedef struct dhcpv6_addr {
16611878SVenu.Iyer@Sun.COM 	in6_addr_t		da_addr;
16711878SVenu.Iyer@Sun.COM 	avl_node_t		da_node;
16811878SVenu.Iyer@Sun.COM 	struct dhcpv6_addr	*da_next;
16911878SVenu.Iyer@Sun.COM } dhcpv6_addr_t;
17011878SVenu.Iyer@Sun.COM 
17111878SVenu.Iyer@Sun.COM /*
17211878SVenu.Iyer@Sun.COM  * DHCPv6 client ID. May be added to mci_v6_cid.
17311878SVenu.Iyer@Sun.COM  * No dhcpv6_txn_t should be pointing to it after it is added to mci_v6_cid.
17411878SVenu.Iyer@Sun.COM  */
17511878SVenu.Iyer@Sun.COM typedef struct dhcpv6_cid {
17611878SVenu.Iyer@Sun.COM 	uchar_t			*dc_cid;
17711878SVenu.Iyer@Sun.COM 	uint_t			dc_cid_len;
17811878SVenu.Iyer@Sun.COM 	dhcpv6_addr_t		*dc_addr;
17911878SVenu.Iyer@Sun.COM 	uint_t			dc_addrcnt;
18011878SVenu.Iyer@Sun.COM 	avl_node_t		dc_node;
18111878SVenu.Iyer@Sun.COM } dhcpv6_cid_t;
18211878SVenu.Iyer@Sun.COM 
18311878SVenu.Iyer@Sun.COM /*
18411878SVenu.Iyer@Sun.COM  * DHCPv6 transaction. Unlike its v4 counterpart, this object gets freed up
18511878SVenu.Iyer@Sun.COM  * as soon as the transaction completes or expires.
18611878SVenu.Iyer@Sun.COM  */
18711878SVenu.Iyer@Sun.COM typedef struct dhcpv6_txn {
18811878SVenu.Iyer@Sun.COM 	uint32_t		dt_xid;
18911878SVenu.Iyer@Sun.COM 	time_t			dt_timestamp;
19011878SVenu.Iyer@Sun.COM 	dhcpv6_cid_t		*dt_cid;
19111878SVenu.Iyer@Sun.COM 	avl_node_t		dt_node;
19211878SVenu.Iyer@Sun.COM 	struct dhcpv6_txn	*dt_next;
19311878SVenu.Iyer@Sun.COM } dhcpv6_txn_t;
19411878SVenu.Iyer@Sun.COM 
19511878SVenu.Iyer@Sun.COM static void	start_txn_cleanup_timer(mac_client_impl_t *);
19612748SSowmini.Varadhan@oracle.COM static boolean_t allowed_ips_set(mac_resource_props_t *, uint32_t);
19711878SVenu.Iyer@Sun.COM 
19811878SVenu.Iyer@Sun.COM #define	BUMP_STAT(m, s)	(m)->mci_misc_stat.mms_##s++
19910734SEric Cheng 
20010734SEric Cheng /*
20111878SVenu.Iyer@Sun.COM  * Comparison functions for the 3 AVL trees used:
20211878SVenu.Iyer@Sun.COM  * mci_v4_pending_txn, mci_v4_completed_txn, mci_v4_dyn_ip
20311878SVenu.Iyer@Sun.COM  */
20411878SVenu.Iyer@Sun.COM static int
compare_dhcpv4_xid(const void * arg1,const void * arg2)20511878SVenu.Iyer@Sun.COM compare_dhcpv4_xid(const void *arg1, const void *arg2)
20611878SVenu.Iyer@Sun.COM {
20711878SVenu.Iyer@Sun.COM 	const dhcpv4_txn_t	*txn1 = arg1, *txn2 = arg2;
20811878SVenu.Iyer@Sun.COM 
20911878SVenu.Iyer@Sun.COM 	if (txn1->dt_xid < txn2->dt_xid)
21011878SVenu.Iyer@Sun.COM 		return (-1);
21111878SVenu.Iyer@Sun.COM 	else if (txn1->dt_xid > txn2->dt_xid)
21211878SVenu.Iyer@Sun.COM 		return (1);
21311878SVenu.Iyer@Sun.COM 	else
21411878SVenu.Iyer@Sun.COM 		return (0);
21511878SVenu.Iyer@Sun.COM }
21611878SVenu.Iyer@Sun.COM 
21711878SVenu.Iyer@Sun.COM static int
compare_dhcpv4_cid(const void * arg1,const void * arg2)21811878SVenu.Iyer@Sun.COM compare_dhcpv4_cid(const void *arg1, const void *arg2)
21911878SVenu.Iyer@Sun.COM {
22011878SVenu.Iyer@Sun.COM 	const dhcpv4_txn_t	*txn1 = arg1, *txn2 = arg2;
22111878SVenu.Iyer@Sun.COM 	int			ret;
22211878SVenu.Iyer@Sun.COM 
22311878SVenu.Iyer@Sun.COM 	if (txn1->dt_cid_len < txn2->dt_cid_len)
22411878SVenu.Iyer@Sun.COM 		return (-1);
22511878SVenu.Iyer@Sun.COM 	else if (txn1->dt_cid_len > txn2->dt_cid_len)
22611878SVenu.Iyer@Sun.COM 		return (1);
22711878SVenu.Iyer@Sun.COM 
22811878SVenu.Iyer@Sun.COM 	if (txn1->dt_cid_len == 0)
22911878SVenu.Iyer@Sun.COM 		return (0);
23011878SVenu.Iyer@Sun.COM 
23111878SVenu.Iyer@Sun.COM 	ret = memcmp(txn1->dt_cid, txn2->dt_cid, txn1->dt_cid_len);
23211878SVenu.Iyer@Sun.COM 	if (ret < 0)
23311878SVenu.Iyer@Sun.COM 		return (-1);
23411878SVenu.Iyer@Sun.COM 	else if (ret > 0)
23511878SVenu.Iyer@Sun.COM 		return (1);
23611878SVenu.Iyer@Sun.COM 	else
23711878SVenu.Iyer@Sun.COM 		return (0);
23811878SVenu.Iyer@Sun.COM }
23911878SVenu.Iyer@Sun.COM 
24011878SVenu.Iyer@Sun.COM static int
compare_dhcpv4_ip(const void * arg1,const void * arg2)24111878SVenu.Iyer@Sun.COM compare_dhcpv4_ip(const void *arg1, const void *arg2)
24211878SVenu.Iyer@Sun.COM {
24311878SVenu.Iyer@Sun.COM 	const dhcpv4_txn_t	*txn1 = arg1, *txn2 = arg2;
24411878SVenu.Iyer@Sun.COM 
24511878SVenu.Iyer@Sun.COM 	if (txn1->dt_ipaddr < txn2->dt_ipaddr)
24611878SVenu.Iyer@Sun.COM 		return (-1);
24711878SVenu.Iyer@Sun.COM 	else if (txn1->dt_ipaddr > txn2->dt_ipaddr)
24811878SVenu.Iyer@Sun.COM 		return (1);
24911878SVenu.Iyer@Sun.COM 	else
25011878SVenu.Iyer@Sun.COM 		return (0);
25111878SVenu.Iyer@Sun.COM }
25211878SVenu.Iyer@Sun.COM 
25311878SVenu.Iyer@Sun.COM /*
25411878SVenu.Iyer@Sun.COM  * Find the specified DHCPv4 option.
25511878SVenu.Iyer@Sun.COM  */
25611878SVenu.Iyer@Sun.COM static int
get_dhcpv4_option(struct dhcp * dh4,uchar_t * end,uint8_t type,uchar_t ** opt,uint8_t * opt_len)25711878SVenu.Iyer@Sun.COM get_dhcpv4_option(struct dhcp *dh4, uchar_t *end, uint8_t type,
25811878SVenu.Iyer@Sun.COM     uchar_t **opt, uint8_t *opt_len)
25911878SVenu.Iyer@Sun.COM {
26011878SVenu.Iyer@Sun.COM 	uchar_t		*start = (uchar_t *)dh4->options;
26111878SVenu.Iyer@Sun.COM 	uint8_t		otype, olen;
26211878SVenu.Iyer@Sun.COM 
26311878SVenu.Iyer@Sun.COM 	while (start < end) {
26411878SVenu.Iyer@Sun.COM 		if (*start == CD_PAD) {
26511878SVenu.Iyer@Sun.COM 			start++;
26611878SVenu.Iyer@Sun.COM 			continue;
26711878SVenu.Iyer@Sun.COM 		}
26811878SVenu.Iyer@Sun.COM 		if (*start == CD_END)
26911878SVenu.Iyer@Sun.COM 			break;
27011878SVenu.Iyer@Sun.COM 
27111878SVenu.Iyer@Sun.COM 		otype = *start++;
27211878SVenu.Iyer@Sun.COM 		olen = *start++;
27311878SVenu.Iyer@Sun.COM 		if (otype == type && olen > 0) {
27411878SVenu.Iyer@Sun.COM 			*opt = start;
27511878SVenu.Iyer@Sun.COM 			*opt_len = olen;
27611878SVenu.Iyer@Sun.COM 			return (0);
27711878SVenu.Iyer@Sun.COM 		}
27811878SVenu.Iyer@Sun.COM 		start += olen;
27911878SVenu.Iyer@Sun.COM 	}
28011878SVenu.Iyer@Sun.COM 	return (ENOENT);
28111878SVenu.Iyer@Sun.COM }
28211878SVenu.Iyer@Sun.COM 
28311878SVenu.Iyer@Sun.COM /*
28411878SVenu.Iyer@Sun.COM  * Locate the start of a DHCPv4 header.
28511878SVenu.Iyer@Sun.COM  * The possible return values and associated meanings are:
28611878SVenu.Iyer@Sun.COM  * 0      - packet is DHCP and has a DHCP header.
28711878SVenu.Iyer@Sun.COM  * EINVAL - packet is not DHCP. the recommended action is to let it pass.
28811878SVenu.Iyer@Sun.COM  * ENOSPC - packet is a initial fragment that is DHCP or is unidentifiable.
28911878SVenu.Iyer@Sun.COM  *          the recommended action is to drop it.
29011878SVenu.Iyer@Sun.COM  */
29111878SVenu.Iyer@Sun.COM static int
get_dhcpv4_info(ipha_t * ipha,uchar_t * end,struct dhcp ** dh4)29211878SVenu.Iyer@Sun.COM get_dhcpv4_info(ipha_t *ipha, uchar_t *end, struct dhcp **dh4)
29311878SVenu.Iyer@Sun.COM {
29411878SVenu.Iyer@Sun.COM 	uint16_t	offset_and_flags, client, server;
29511878SVenu.Iyer@Sun.COM 	boolean_t	first_frag = B_FALSE;
29611878SVenu.Iyer@Sun.COM 	struct udphdr	*udph;
29711878SVenu.Iyer@Sun.COM 	uchar_t		*dh;
29811878SVenu.Iyer@Sun.COM 
29911878SVenu.Iyer@Sun.COM 	if (ipha->ipha_protocol != IPPROTO_UDP)
30011878SVenu.Iyer@Sun.COM 		return (EINVAL);
30111878SVenu.Iyer@Sun.COM 
30211878SVenu.Iyer@Sun.COM 	offset_and_flags = ntohs(ipha->ipha_fragment_offset_and_flags);
30311878SVenu.Iyer@Sun.COM 	if ((offset_and_flags & (IPH_MF | IPH_OFFSET)) != 0) {
30411878SVenu.Iyer@Sun.COM 		/*
30511878SVenu.Iyer@Sun.COM 		 * All non-initial fragments may pass because we cannot
30611878SVenu.Iyer@Sun.COM 		 * identify their type. It's safe to let them through
30711878SVenu.Iyer@Sun.COM 		 * because reassembly will fail if we decide to drop the
30811878SVenu.Iyer@Sun.COM 		 * initial fragment.
30911878SVenu.Iyer@Sun.COM 		 */
31011878SVenu.Iyer@Sun.COM 		if (((offset_and_flags << 3) & 0xffff) != 0)
31111878SVenu.Iyer@Sun.COM 			return (EINVAL);
31211878SVenu.Iyer@Sun.COM 		first_frag = B_TRUE;
31311878SVenu.Iyer@Sun.COM 	}
31411878SVenu.Iyer@Sun.COM 	/* drop packets without a udp header */
31511878SVenu.Iyer@Sun.COM 	udph = (struct udphdr *)((uchar_t *)ipha + IPH_HDR_LENGTH(ipha));
31611878SVenu.Iyer@Sun.COM 	if ((uchar_t *)&udph[1] > end)
31711878SVenu.Iyer@Sun.COM 		return (ENOSPC);
31811878SVenu.Iyer@Sun.COM 
31911878SVenu.Iyer@Sun.COM 	client = htons(IPPORT_BOOTPC);
32011878SVenu.Iyer@Sun.COM 	server = htons(IPPORT_BOOTPS);
32111878SVenu.Iyer@Sun.COM 	if (udph->uh_sport != client && udph->uh_sport != server &&
32211878SVenu.Iyer@Sun.COM 	    udph->uh_dport != client && udph->uh_dport != server)
32311878SVenu.Iyer@Sun.COM 		return (EINVAL);
32411878SVenu.Iyer@Sun.COM 
32511878SVenu.Iyer@Sun.COM 	/* drop dhcp fragments */
32611878SVenu.Iyer@Sun.COM 	if (first_frag)
32711878SVenu.Iyer@Sun.COM 		return (ENOSPC);
32811878SVenu.Iyer@Sun.COM 
32911878SVenu.Iyer@Sun.COM 	dh = (uchar_t *)&udph[1];
33011878SVenu.Iyer@Sun.COM 	if (dh + BASE_PKT_SIZE > end)
33111878SVenu.Iyer@Sun.COM 		return (EINVAL);
33211878SVenu.Iyer@Sun.COM 
33311878SVenu.Iyer@Sun.COM 	*dh4 = (struct dhcp *)dh;
33411878SVenu.Iyer@Sun.COM 	return (0);
33511878SVenu.Iyer@Sun.COM }
33611878SVenu.Iyer@Sun.COM 
33711878SVenu.Iyer@Sun.COM /*
33811878SVenu.Iyer@Sun.COM  * Wrappers for accesses to avl trees to improve readability.
33911878SVenu.Iyer@Sun.COM  * Their purposes are fairly self-explanatory.
34011878SVenu.Iyer@Sun.COM  */
34111878SVenu.Iyer@Sun.COM static dhcpv4_txn_t *
find_dhcpv4_pending_txn(mac_client_impl_t * mcip,uint32_t xid)34211878SVenu.Iyer@Sun.COM find_dhcpv4_pending_txn(mac_client_impl_t *mcip, uint32_t xid)
34311878SVenu.Iyer@Sun.COM {
34411878SVenu.Iyer@Sun.COM 	dhcpv4_txn_t	tmp_txn;
34511878SVenu.Iyer@Sun.COM 
34611878SVenu.Iyer@Sun.COM 	ASSERT(MUTEX_HELD(&mcip->mci_protect_lock));
34711878SVenu.Iyer@Sun.COM 	tmp_txn.dt_xid = xid;
34811878SVenu.Iyer@Sun.COM 	return (avl_find(&mcip->mci_v4_pending_txn, &tmp_txn, NULL));
34911878SVenu.Iyer@Sun.COM }
35011878SVenu.Iyer@Sun.COM 
35111878SVenu.Iyer@Sun.COM static int
insert_dhcpv4_pending_txn(mac_client_impl_t * mcip,dhcpv4_txn_t * txn)35211878SVenu.Iyer@Sun.COM insert_dhcpv4_pending_txn(mac_client_impl_t *mcip, dhcpv4_txn_t *txn)
35311878SVenu.Iyer@Sun.COM {
35411878SVenu.Iyer@Sun.COM 	avl_index_t	where;
35511878SVenu.Iyer@Sun.COM 
35611878SVenu.Iyer@Sun.COM 	ASSERT(MUTEX_HELD(&mcip->mci_protect_lock));
35711878SVenu.Iyer@Sun.COM 	if (avl_find(&mcip->mci_v4_pending_txn, txn, &where) != NULL)
35811878SVenu.Iyer@Sun.COM 		return (EEXIST);
35911878SVenu.Iyer@Sun.COM 
36011878SVenu.Iyer@Sun.COM 	if (avl_numnodes(&mcip->mci_v4_pending_txn) >= dhcp_max_pending_txn) {
36111878SVenu.Iyer@Sun.COM 		BUMP_STAT(mcip, dhcpdropped);
36211878SVenu.Iyer@Sun.COM 		return (EAGAIN);
36311878SVenu.Iyer@Sun.COM 	}
36411878SVenu.Iyer@Sun.COM 	avl_insert(&mcip->mci_v4_pending_txn, txn, where);
36511878SVenu.Iyer@Sun.COM 	return (0);
36611878SVenu.Iyer@Sun.COM }
36711878SVenu.Iyer@Sun.COM 
36811878SVenu.Iyer@Sun.COM static void
remove_dhcpv4_pending_txn(mac_client_impl_t * mcip,dhcpv4_txn_t * txn)36911878SVenu.Iyer@Sun.COM remove_dhcpv4_pending_txn(mac_client_impl_t *mcip, dhcpv4_txn_t *txn)
37011878SVenu.Iyer@Sun.COM {
37111878SVenu.Iyer@Sun.COM 	ASSERT(MUTEX_HELD(&mcip->mci_protect_lock));
37211878SVenu.Iyer@Sun.COM 	avl_remove(&mcip->mci_v4_pending_txn, txn);
37311878SVenu.Iyer@Sun.COM }
37411878SVenu.Iyer@Sun.COM 
37511878SVenu.Iyer@Sun.COM static dhcpv4_txn_t *
find_dhcpv4_completed_txn(mac_client_impl_t * mcip,uint8_t * cid,uint8_t cid_len)37611878SVenu.Iyer@Sun.COM find_dhcpv4_completed_txn(mac_client_impl_t *mcip, uint8_t *cid,
37711878SVenu.Iyer@Sun.COM     uint8_t cid_len)
37811878SVenu.Iyer@Sun.COM {
37911878SVenu.Iyer@Sun.COM 	dhcpv4_txn_t	tmp_txn;
38011878SVenu.Iyer@Sun.COM 
38111878SVenu.Iyer@Sun.COM 	ASSERT(MUTEX_HELD(&mcip->mci_protect_lock));
38211878SVenu.Iyer@Sun.COM 	if (cid_len > 0)
38311878SVenu.Iyer@Sun.COM 		bcopy(cid, tmp_txn.dt_cid, cid_len);
38411878SVenu.Iyer@Sun.COM 	tmp_txn.dt_cid_len = cid_len;
38511878SVenu.Iyer@Sun.COM 	return (avl_find(&mcip->mci_v4_completed_txn, &tmp_txn, NULL));
38611878SVenu.Iyer@Sun.COM }
38711878SVenu.Iyer@Sun.COM 
38811878SVenu.Iyer@Sun.COM /*
38911878SVenu.Iyer@Sun.COM  * After a pending txn is removed from the pending table, it is inserted
39011878SVenu.Iyer@Sun.COM  * into both the completed and dyn-ip tables. These two insertions are
39111878SVenu.Iyer@Sun.COM  * done together because a client ID must have 1:1 correspondence with
39211878SVenu.Iyer@Sun.COM  * an IP address and IP addresses must be unique in the dyn-ip table.
39311878SVenu.Iyer@Sun.COM  */
39411878SVenu.Iyer@Sun.COM static int
insert_dhcpv4_completed_txn(mac_client_impl_t * mcip,dhcpv4_txn_t * txn)39511878SVenu.Iyer@Sun.COM insert_dhcpv4_completed_txn(mac_client_impl_t *mcip, dhcpv4_txn_t *txn)
39611878SVenu.Iyer@Sun.COM {
39711878SVenu.Iyer@Sun.COM 	avl_index_t	where;
39811878SVenu.Iyer@Sun.COM 
39911878SVenu.Iyer@Sun.COM 	ASSERT(MUTEX_HELD(&mcip->mci_protect_lock));
40011878SVenu.Iyer@Sun.COM 	if (avl_find(&mcip->mci_v4_completed_txn, txn, &where) != NULL)
40111878SVenu.Iyer@Sun.COM 		return (EEXIST);
40211878SVenu.Iyer@Sun.COM 
40311878SVenu.Iyer@Sun.COM 	if (avl_numnodes(&mcip->mci_v4_completed_txn) >=
40411878SVenu.Iyer@Sun.COM 	    dhcp_max_completed_txn) {
40511878SVenu.Iyer@Sun.COM 		BUMP_STAT(mcip, dhcpdropped);
40611878SVenu.Iyer@Sun.COM 		return (EAGAIN);
40711878SVenu.Iyer@Sun.COM 	}
40811878SVenu.Iyer@Sun.COM 
40911878SVenu.Iyer@Sun.COM 	avl_insert(&mcip->mci_v4_completed_txn, txn, where);
41011878SVenu.Iyer@Sun.COM 	if (avl_find(&mcip->mci_v4_dyn_ip, txn, &where) != NULL) {
41111878SVenu.Iyer@Sun.COM 		avl_remove(&mcip->mci_v4_completed_txn, txn);
41211878SVenu.Iyer@Sun.COM 		return (EEXIST);
41311878SVenu.Iyer@Sun.COM 	}
41411878SVenu.Iyer@Sun.COM 	avl_insert(&mcip->mci_v4_dyn_ip, txn, where);
41511878SVenu.Iyer@Sun.COM 	return (0);
41611878SVenu.Iyer@Sun.COM }
41711878SVenu.Iyer@Sun.COM 
41811878SVenu.Iyer@Sun.COM static void
remove_dhcpv4_completed_txn(mac_client_impl_t * mcip,dhcpv4_txn_t * txn)41911878SVenu.Iyer@Sun.COM remove_dhcpv4_completed_txn(mac_client_impl_t *mcip, dhcpv4_txn_t *txn)
42011878SVenu.Iyer@Sun.COM {
42111878SVenu.Iyer@Sun.COM 	dhcpv4_txn_t	*ctxn;
42211878SVenu.Iyer@Sun.COM 
42311878SVenu.Iyer@Sun.COM 	ASSERT(MUTEX_HELD(&mcip->mci_protect_lock));
42411878SVenu.Iyer@Sun.COM 	if ((ctxn = avl_find(&mcip->mci_v4_dyn_ip, txn, NULL)) != NULL &&
42511878SVenu.Iyer@Sun.COM 	    ctxn == txn)
42611878SVenu.Iyer@Sun.COM 		avl_remove(&mcip->mci_v4_dyn_ip, txn);
42711878SVenu.Iyer@Sun.COM 
42811878SVenu.Iyer@Sun.COM 	avl_remove(&mcip->mci_v4_completed_txn, txn);
42911878SVenu.Iyer@Sun.COM }
43011878SVenu.Iyer@Sun.COM 
43111878SVenu.Iyer@Sun.COM /*
43211878SVenu.Iyer@Sun.COM  * Check whether an IP address is in the dyn-ip table.
43310734SEric Cheng  */
43410734SEric Cheng static boolean_t
check_dhcpv4_dyn_ip(mac_client_impl_t * mcip,ipaddr_t ipaddr)43511878SVenu.Iyer@Sun.COM check_dhcpv4_dyn_ip(mac_client_impl_t *mcip, ipaddr_t ipaddr)
43611878SVenu.Iyer@Sun.COM {
43711878SVenu.Iyer@Sun.COM 	dhcpv4_txn_t	tmp_txn, *txn;
43811878SVenu.Iyer@Sun.COM 
43911878SVenu.Iyer@Sun.COM 	mutex_enter(&mcip->mci_protect_lock);
44011878SVenu.Iyer@Sun.COM 	tmp_txn.dt_ipaddr = ipaddr;
44111878SVenu.Iyer@Sun.COM 	txn = avl_find(&mcip->mci_v4_dyn_ip, &tmp_txn, NULL);
44211878SVenu.Iyer@Sun.COM 	mutex_exit(&mcip->mci_protect_lock);
44311878SVenu.Iyer@Sun.COM 	return (txn != NULL);
44411878SVenu.Iyer@Sun.COM }
44511878SVenu.Iyer@Sun.COM 
44611878SVenu.Iyer@Sun.COM /*
44711878SVenu.Iyer@Sun.COM  * Create/destroy a DHCPv4 transaction.
44811878SVenu.Iyer@Sun.COM  */
44911878SVenu.Iyer@Sun.COM static dhcpv4_txn_t *
create_dhcpv4_txn(uint32_t xid,uint8_t * cid,uint8_t cid_len,ipaddr_t ipaddr)45011878SVenu.Iyer@Sun.COM create_dhcpv4_txn(uint32_t xid, uint8_t *cid, uint8_t cid_len, ipaddr_t ipaddr)
45111878SVenu.Iyer@Sun.COM {
45211878SVenu.Iyer@Sun.COM 	dhcpv4_txn_t	*txn;
45311878SVenu.Iyer@Sun.COM 
45411878SVenu.Iyer@Sun.COM 	if ((txn = kmem_zalloc(sizeof (*txn), KM_NOSLEEP)) == NULL)
45511878SVenu.Iyer@Sun.COM 		return (NULL);
45611878SVenu.Iyer@Sun.COM 
45711878SVenu.Iyer@Sun.COM 	txn->dt_xid = xid;
45811878SVenu.Iyer@Sun.COM 	txn->dt_timestamp = ddi_get_time();
45911878SVenu.Iyer@Sun.COM 	if (cid_len > 0)
46011878SVenu.Iyer@Sun.COM 		bcopy(cid, &txn->dt_cid, cid_len);
46111878SVenu.Iyer@Sun.COM 	txn->dt_cid_len = cid_len;
46211878SVenu.Iyer@Sun.COM 	txn->dt_ipaddr = ipaddr;
46311878SVenu.Iyer@Sun.COM 	return (txn);
46411878SVenu.Iyer@Sun.COM }
46511878SVenu.Iyer@Sun.COM 
46611878SVenu.Iyer@Sun.COM static void
free_dhcpv4_txn(dhcpv4_txn_t * txn)46711878SVenu.Iyer@Sun.COM free_dhcpv4_txn(dhcpv4_txn_t *txn)
46811878SVenu.Iyer@Sun.COM {
46911878SVenu.Iyer@Sun.COM 	kmem_free(txn, sizeof (*txn));
47011878SVenu.Iyer@Sun.COM }
47111878SVenu.Iyer@Sun.COM 
47211878SVenu.Iyer@Sun.COM /*
47311878SVenu.Iyer@Sun.COM  * Clean up all v4 tables.
47411878SVenu.Iyer@Sun.COM  */
47511878SVenu.Iyer@Sun.COM static void
flush_dhcpv4(mac_client_impl_t * mcip)47611878SVenu.Iyer@Sun.COM flush_dhcpv4(mac_client_impl_t *mcip)
47711878SVenu.Iyer@Sun.COM {
47811878SVenu.Iyer@Sun.COM 	void		*cookie = NULL;
47911878SVenu.Iyer@Sun.COM 	dhcpv4_txn_t	*txn;
48011878SVenu.Iyer@Sun.COM 
48111878SVenu.Iyer@Sun.COM 	ASSERT(MUTEX_HELD(&mcip->mci_protect_lock));
48211878SVenu.Iyer@Sun.COM 	while ((txn = avl_destroy_nodes(&mcip->mci_v4_dyn_ip,
48311878SVenu.Iyer@Sun.COM 	    &cookie)) != NULL) {
48411878SVenu.Iyer@Sun.COM 		/*
48511878SVenu.Iyer@Sun.COM 		 * No freeing needed here because the same txn exists
48611878SVenu.Iyer@Sun.COM 		 * in the mci_v4_completed_txn table as well.
48711878SVenu.Iyer@Sun.COM 		 */
48811878SVenu.Iyer@Sun.COM 	}
48911878SVenu.Iyer@Sun.COM 	cookie = NULL;
49011878SVenu.Iyer@Sun.COM 	while ((txn = avl_destroy_nodes(&mcip->mci_v4_completed_txn,
49111878SVenu.Iyer@Sun.COM 	    &cookie)) != NULL) {
49211878SVenu.Iyer@Sun.COM 		free_dhcpv4_txn(txn);
49311878SVenu.Iyer@Sun.COM 	}
49411878SVenu.Iyer@Sun.COM 	cookie = NULL;
49511878SVenu.Iyer@Sun.COM 	while ((txn = avl_destroy_nodes(&mcip->mci_v4_pending_txn,
49611878SVenu.Iyer@Sun.COM 	    &cookie)) != NULL) {
49711878SVenu.Iyer@Sun.COM 		free_dhcpv4_txn(txn);
49811878SVenu.Iyer@Sun.COM 	}
49911878SVenu.Iyer@Sun.COM }
50011878SVenu.Iyer@Sun.COM 
50111878SVenu.Iyer@Sun.COM /*
50211878SVenu.Iyer@Sun.COM  * Cleanup stale DHCPv4 transactions.
50311878SVenu.Iyer@Sun.COM  */
50411878SVenu.Iyer@Sun.COM static void
txn_cleanup_v4(mac_client_impl_t * mcip)50511878SVenu.Iyer@Sun.COM txn_cleanup_v4(mac_client_impl_t *mcip)
50610734SEric Cheng {
50711878SVenu.Iyer@Sun.COM 	dhcpv4_txn_t		*txn, *ctxn, *next, *txn_list = NULL;
50811878SVenu.Iyer@Sun.COM 
50911878SVenu.Iyer@Sun.COM 	/*
51011878SVenu.Iyer@Sun.COM 	 * Find stale pending transactions and place them on a list
51111878SVenu.Iyer@Sun.COM 	 * to be removed.
51211878SVenu.Iyer@Sun.COM 	 */
51311878SVenu.Iyer@Sun.COM 	for (txn = avl_first(&mcip->mci_v4_pending_txn); txn != NULL;
51411878SVenu.Iyer@Sun.COM 	    txn = avl_walk(&mcip->mci_v4_pending_txn, txn, AVL_AFTER)) {
51511878SVenu.Iyer@Sun.COM 		if (ddi_get_time() - txn->dt_timestamp >
51611878SVenu.Iyer@Sun.COM 		    txn_cleanup_interval) {
51711878SVenu.Iyer@Sun.COM 			DTRACE_PROBE2(found__expired__txn,
51811878SVenu.Iyer@Sun.COM 			    mac_client_impl_t *, mcip,
51911878SVenu.Iyer@Sun.COM 			    dhcpv4_txn_t *, txn);
52011878SVenu.Iyer@Sun.COM 
52111878SVenu.Iyer@Sun.COM 			txn->dt_next = txn_list;
52211878SVenu.Iyer@Sun.COM 			txn_list = txn;
52311878SVenu.Iyer@Sun.COM 		}
52411878SVenu.Iyer@Sun.COM 	}
52511878SVenu.Iyer@Sun.COM 
52611878SVenu.Iyer@Sun.COM 	/*
52711878SVenu.Iyer@Sun.COM 	 * Remove and free stale pending transactions and completed
52811878SVenu.Iyer@Sun.COM 	 * transactions with the same client IDs as the stale transactions.
52911878SVenu.Iyer@Sun.COM 	 */
53011878SVenu.Iyer@Sun.COM 	for (txn = txn_list; txn != NULL; txn = next) {
53111878SVenu.Iyer@Sun.COM 		avl_remove(&mcip->mci_v4_pending_txn, txn);
53211878SVenu.Iyer@Sun.COM 
53311878SVenu.Iyer@Sun.COM 		ctxn = find_dhcpv4_completed_txn(mcip, txn->dt_cid,
53411878SVenu.Iyer@Sun.COM 		    txn->dt_cid_len);
53511878SVenu.Iyer@Sun.COM 		if (ctxn != NULL) {
53611878SVenu.Iyer@Sun.COM 			DTRACE_PROBE2(removing__completed__txn,
53711878SVenu.Iyer@Sun.COM 			    mac_client_impl_t *, mcip,
53811878SVenu.Iyer@Sun.COM 			    dhcpv4_txn_t *, ctxn);
53911878SVenu.Iyer@Sun.COM 
54011878SVenu.Iyer@Sun.COM 			remove_dhcpv4_completed_txn(mcip, ctxn);
54111878SVenu.Iyer@Sun.COM 			free_dhcpv4_txn(ctxn);
54211878SVenu.Iyer@Sun.COM 		}
54311878SVenu.Iyer@Sun.COM 		next = txn->dt_next;
54411878SVenu.Iyer@Sun.COM 		txn->dt_next = NULL;
54511878SVenu.Iyer@Sun.COM 
54611878SVenu.Iyer@Sun.COM 		DTRACE_PROBE2(freeing__txn, mac_client_impl_t *, mcip,
54711878SVenu.Iyer@Sun.COM 		    dhcpv4_txn_t *, txn);
54811878SVenu.Iyer@Sun.COM 		free_dhcpv4_txn(txn);
54911878SVenu.Iyer@Sun.COM 	}
55011878SVenu.Iyer@Sun.COM }
55111878SVenu.Iyer@Sun.COM 
55211878SVenu.Iyer@Sun.COM /*
55311878SVenu.Iyer@Sun.COM  * Core logic for intercepting outbound DHCPv4 packets.
55411878SVenu.Iyer@Sun.COM  */
55512748SSowmini.Varadhan@oracle.COM static boolean_t
intercept_dhcpv4_outbound(mac_client_impl_t * mcip,ipha_t * ipha,uchar_t * end)55611878SVenu.Iyer@Sun.COM intercept_dhcpv4_outbound(mac_client_impl_t *mcip, ipha_t *ipha, uchar_t *end)
55711878SVenu.Iyer@Sun.COM {
55812748SSowmini.Varadhan@oracle.COM 	struct dhcp		*dh4;
55912748SSowmini.Varadhan@oracle.COM 	uchar_t			*opt;
56012748SSowmini.Varadhan@oracle.COM 	dhcpv4_txn_t		*txn, *ctxn;
56112748SSowmini.Varadhan@oracle.COM 	ipaddr_t		ipaddr;
56212748SSowmini.Varadhan@oracle.COM 	uint8_t			opt_len, mtype, cid[DHCP_MAX_OPT_SIZE], cid_len;
56312748SSowmini.Varadhan@oracle.COM 	mac_resource_props_t	*mrp = MCIP_RESOURCE_PROPS(mcip);
56411878SVenu.Iyer@Sun.COM 
56511878SVenu.Iyer@Sun.COM 	if (get_dhcpv4_info(ipha, end, &dh4) != 0)
56612748SSowmini.Varadhan@oracle.COM 		return (B_TRUE);
56712748SSowmini.Varadhan@oracle.COM 
56812748SSowmini.Varadhan@oracle.COM 	/* ip_nospoof/allowed-ips and DHCP are mutually exclusive by default */
56912748SSowmini.Varadhan@oracle.COM 	if (allowed_ips_set(mrp, IPV4_VERSION))
57012748SSowmini.Varadhan@oracle.COM 		return (B_FALSE);
57111878SVenu.Iyer@Sun.COM 
57211878SVenu.Iyer@Sun.COM 	if (get_dhcpv4_option(dh4, end, CD_DHCP_TYPE, &opt, &opt_len) != 0 ||
57311878SVenu.Iyer@Sun.COM 	    opt_len != 1) {
57411878SVenu.Iyer@Sun.COM 		DTRACE_PROBE2(mtype__not__found, mac_client_impl_t *, mcip,
57511878SVenu.Iyer@Sun.COM 		    struct dhcp *, dh4);
57612748SSowmini.Varadhan@oracle.COM 		return (B_TRUE);
57711878SVenu.Iyer@Sun.COM 	}
57811878SVenu.Iyer@Sun.COM 	mtype = *opt;
57911878SVenu.Iyer@Sun.COM 	if (mtype != REQUEST && mtype != RELEASE) {
58011878SVenu.Iyer@Sun.COM 		DTRACE_PROBE3(ignored__mtype, mac_client_impl_t *, mcip,
58111878SVenu.Iyer@Sun.COM 		    struct dhcp *, dh4, uint8_t, mtype);
58212748SSowmini.Varadhan@oracle.COM 		return (B_TRUE);
58311878SVenu.Iyer@Sun.COM 	}
58411878SVenu.Iyer@Sun.COM 
58511878SVenu.Iyer@Sun.COM 	/* client ID is optional for IPv4 */
58611878SVenu.Iyer@Sun.COM 	if (get_dhcpv4_option(dh4, end, CD_CLIENT_ID, &opt, &opt_len) == 0 &&
58711878SVenu.Iyer@Sun.COM 	    opt_len >= 2) {
58811878SVenu.Iyer@Sun.COM 		bcopy(opt, cid, opt_len);
58911878SVenu.Iyer@Sun.COM 		cid_len = opt_len;
59011878SVenu.Iyer@Sun.COM 	} else {
59111878SVenu.Iyer@Sun.COM 		bzero(cid, DHCP_MAX_OPT_SIZE);
59211878SVenu.Iyer@Sun.COM 		cid_len = 0;
59311878SVenu.Iyer@Sun.COM 	}
59411878SVenu.Iyer@Sun.COM 
59511878SVenu.Iyer@Sun.COM 	mutex_enter(&mcip->mci_protect_lock);
59611878SVenu.Iyer@Sun.COM 	if (mtype == RELEASE) {
59711878SVenu.Iyer@Sun.COM 		DTRACE_PROBE2(release, mac_client_impl_t *, mcip,
59811878SVenu.Iyer@Sun.COM 		    struct dhcp *, dh4);
59911878SVenu.Iyer@Sun.COM 
60011878SVenu.Iyer@Sun.COM 		/* flush any completed txn with this cid */
60111878SVenu.Iyer@Sun.COM 		ctxn = find_dhcpv4_completed_txn(mcip, cid, cid_len);
60211878SVenu.Iyer@Sun.COM 		if (ctxn != NULL) {
60311878SVenu.Iyer@Sun.COM 			DTRACE_PROBE2(release__successful, mac_client_impl_t *,
60411878SVenu.Iyer@Sun.COM 			    mcip, struct dhcp *, dh4);
60511878SVenu.Iyer@Sun.COM 
60611878SVenu.Iyer@Sun.COM 			remove_dhcpv4_completed_txn(mcip, ctxn);
60711878SVenu.Iyer@Sun.COM 			free_dhcpv4_txn(ctxn);
60811878SVenu.Iyer@Sun.COM 		}
60911878SVenu.Iyer@Sun.COM 		goto done;
61011878SVenu.Iyer@Sun.COM 	}
61111878SVenu.Iyer@Sun.COM 
61211878SVenu.Iyer@Sun.COM 	/*
61311878SVenu.Iyer@Sun.COM 	 * If a pending txn already exists, we'll update its timestamp so
61411878SVenu.Iyer@Sun.COM 	 * it won't get flushed by the timer. We don't need to create new
61511878SVenu.Iyer@Sun.COM 	 * txns for retransmissions.
61611878SVenu.Iyer@Sun.COM 	 */
61711878SVenu.Iyer@Sun.COM 	if ((txn = find_dhcpv4_pending_txn(mcip, dh4->xid)) != NULL) {
61811878SVenu.Iyer@Sun.COM 		DTRACE_PROBE2(update, mac_client_impl_t *, mcip,
61911878SVenu.Iyer@Sun.COM 		    dhcpv4_txn_t *, txn);
62011878SVenu.Iyer@Sun.COM 		txn->dt_timestamp = ddi_get_time();
62111878SVenu.Iyer@Sun.COM 		goto done;
62211878SVenu.Iyer@Sun.COM 	}
62311878SVenu.Iyer@Sun.COM 
62411878SVenu.Iyer@Sun.COM 	if (get_dhcpv4_option(dh4, end, CD_REQUESTED_IP_ADDR,
62511878SVenu.Iyer@Sun.COM 	    &opt, &opt_len) != 0 || opt_len != sizeof (ipaddr)) {
62611878SVenu.Iyer@Sun.COM 		DTRACE_PROBE2(ipaddr__not__found, mac_client_impl_t *, mcip,
62711878SVenu.Iyer@Sun.COM 		    struct dhcp *, dh4);
62811878SVenu.Iyer@Sun.COM 		goto done;
62911878SVenu.Iyer@Sun.COM 	}
63011878SVenu.Iyer@Sun.COM 	bcopy(opt, &ipaddr, sizeof (ipaddr));
63111878SVenu.Iyer@Sun.COM 	if ((txn = create_dhcpv4_txn(dh4->xid, cid, cid_len, ipaddr)) == NULL)
63211878SVenu.Iyer@Sun.COM 		goto done;
63311878SVenu.Iyer@Sun.COM 
63411878SVenu.Iyer@Sun.COM 	if (insert_dhcpv4_pending_txn(mcip, txn) != 0) {
63511878SVenu.Iyer@Sun.COM 		DTRACE_PROBE2(insert__failed, mac_client_impl_t *, mcip,
63611878SVenu.Iyer@Sun.COM 		    dhcpv4_txn_t *, txn);
63711878SVenu.Iyer@Sun.COM 		free_dhcpv4_txn(txn);
63811878SVenu.Iyer@Sun.COM 		goto done;
63911878SVenu.Iyer@Sun.COM 	}
64011878SVenu.Iyer@Sun.COM 	start_txn_cleanup_timer(mcip);
64111878SVenu.Iyer@Sun.COM 
64211878SVenu.Iyer@Sun.COM 	DTRACE_PROBE2(txn__pending, mac_client_impl_t *, mcip,
64311878SVenu.Iyer@Sun.COM 	    dhcpv4_txn_t *, txn);
64411878SVenu.Iyer@Sun.COM 
64511878SVenu.Iyer@Sun.COM done:
64611878SVenu.Iyer@Sun.COM 	mutex_exit(&mcip->mci_protect_lock);
64712748SSowmini.Varadhan@oracle.COM 	return (B_TRUE);
64811878SVenu.Iyer@Sun.COM }
64911878SVenu.Iyer@Sun.COM 
65011878SVenu.Iyer@Sun.COM /*
65111878SVenu.Iyer@Sun.COM  * Core logic for intercepting inbound DHCPv4 packets.
65211878SVenu.Iyer@Sun.COM  */
65311878SVenu.Iyer@Sun.COM static void
intercept_dhcpv4_inbound(mac_client_impl_t * mcip,ipha_t * ipha,uchar_t * end)65411878SVenu.Iyer@Sun.COM intercept_dhcpv4_inbound(mac_client_impl_t *mcip, ipha_t *ipha, uchar_t *end)
65511878SVenu.Iyer@Sun.COM {
65611878SVenu.Iyer@Sun.COM 	uchar_t		*opt;
65711878SVenu.Iyer@Sun.COM 	struct dhcp	*dh4;
65811878SVenu.Iyer@Sun.COM 	dhcpv4_txn_t	*txn, *ctxn;
65911878SVenu.Iyer@Sun.COM 	uint8_t		opt_len, mtype;
66011878SVenu.Iyer@Sun.COM 
66111878SVenu.Iyer@Sun.COM 	if (get_dhcpv4_info(ipha, end, &dh4) != 0)
66211878SVenu.Iyer@Sun.COM 		return;
66311878SVenu.Iyer@Sun.COM 
66411878SVenu.Iyer@Sun.COM 	if (get_dhcpv4_option(dh4, end, CD_DHCP_TYPE, &opt, &opt_len) != 0 ||
66511878SVenu.Iyer@Sun.COM 	    opt_len != 1) {
66611878SVenu.Iyer@Sun.COM 		DTRACE_PROBE2(mtype__not__found, mac_client_impl_t *, mcip,
66711878SVenu.Iyer@Sun.COM 		    struct dhcp *, dh4);
66811878SVenu.Iyer@Sun.COM 		return;
66911878SVenu.Iyer@Sun.COM 	}
67011878SVenu.Iyer@Sun.COM 	mtype = *opt;
67111878SVenu.Iyer@Sun.COM 	if (mtype != ACK && mtype != NAK) {
67211878SVenu.Iyer@Sun.COM 		DTRACE_PROBE3(ignored__mtype, mac_client_impl_t *, mcip,
67311878SVenu.Iyer@Sun.COM 		    struct dhcp *, dh4, uint8_t, mtype);
67411878SVenu.Iyer@Sun.COM 		return;
67511878SVenu.Iyer@Sun.COM 	}
67611878SVenu.Iyer@Sun.COM 
67711878SVenu.Iyer@Sun.COM 	mutex_enter(&mcip->mci_protect_lock);
67811878SVenu.Iyer@Sun.COM 	if ((txn = find_dhcpv4_pending_txn(mcip, dh4->xid)) == NULL) {
67911878SVenu.Iyer@Sun.COM 		DTRACE_PROBE2(txn__not__found, mac_client_impl_t *, mcip,
68011878SVenu.Iyer@Sun.COM 		    struct dhcp *, dh4);
68111878SVenu.Iyer@Sun.COM 		goto done;
68211878SVenu.Iyer@Sun.COM 	}
68311878SVenu.Iyer@Sun.COM 	remove_dhcpv4_pending_txn(mcip, txn);
68410734SEric Cheng 
68510734SEric Cheng 	/*
68611878SVenu.Iyer@Sun.COM 	 * We're about to move a txn from the pending table to the completed/
68711878SVenu.Iyer@Sun.COM 	 * dyn-ip tables. If there is an existing completed txn with the
68811878SVenu.Iyer@Sun.COM 	 * same cid as our txn, we need to remove and free it.
68911878SVenu.Iyer@Sun.COM 	 */
69011878SVenu.Iyer@Sun.COM 	ctxn = find_dhcpv4_completed_txn(mcip, txn->dt_cid, txn->dt_cid_len);
69111878SVenu.Iyer@Sun.COM 	if (ctxn != NULL) {
69211878SVenu.Iyer@Sun.COM 		DTRACE_PROBE2(replacing__old__txn, mac_client_impl_t *, mcip,
69311878SVenu.Iyer@Sun.COM 		    dhcpv4_txn_t *, ctxn);
69411878SVenu.Iyer@Sun.COM 		remove_dhcpv4_completed_txn(mcip, ctxn);
69511878SVenu.Iyer@Sun.COM 		free_dhcpv4_txn(ctxn);
69611878SVenu.Iyer@Sun.COM 	}
69711878SVenu.Iyer@Sun.COM 	if (mtype == NAK) {
69811878SVenu.Iyer@Sun.COM 		DTRACE_PROBE2(nak__received, mac_client_impl_t *, mcip,
69911878SVenu.Iyer@Sun.COM 		    dhcpv4_txn_t *, txn);
70011878SVenu.Iyer@Sun.COM 		free_dhcpv4_txn(txn);
70111878SVenu.Iyer@Sun.COM 		goto done;
70211878SVenu.Iyer@Sun.COM 	}
70311878SVenu.Iyer@Sun.COM 	if (insert_dhcpv4_completed_txn(mcip, txn) != 0) {
70411878SVenu.Iyer@Sun.COM 		DTRACE_PROBE2(insert__failed, mac_client_impl_t *, mcip,
70511878SVenu.Iyer@Sun.COM 		    dhcpv4_txn_t *, txn);
70611878SVenu.Iyer@Sun.COM 		free_dhcpv4_txn(txn);
70711878SVenu.Iyer@Sun.COM 		goto done;
70811878SVenu.Iyer@Sun.COM 	}
70911878SVenu.Iyer@Sun.COM 	DTRACE_PROBE2(txn__completed, mac_client_impl_t *, mcip,
71011878SVenu.Iyer@Sun.COM 	    dhcpv4_txn_t *, txn);
71111878SVenu.Iyer@Sun.COM 
71211878SVenu.Iyer@Sun.COM done:
71311878SVenu.Iyer@Sun.COM 	mutex_exit(&mcip->mci_protect_lock);
71411878SVenu.Iyer@Sun.COM }
71511878SVenu.Iyer@Sun.COM 
71611878SVenu.Iyer@Sun.COM 
71711878SVenu.Iyer@Sun.COM /*
71811878SVenu.Iyer@Sun.COM  * Comparison functions for the DHCPv6 AVL trees.
71911878SVenu.Iyer@Sun.COM  */
72011878SVenu.Iyer@Sun.COM static int
compare_dhcpv6_xid(const void * arg1,const void * arg2)72111878SVenu.Iyer@Sun.COM compare_dhcpv6_xid(const void *arg1, const void *arg2)
72211878SVenu.Iyer@Sun.COM {
72311878SVenu.Iyer@Sun.COM 	const dhcpv6_txn_t	*txn1 = arg1, *txn2 = arg2;
72411878SVenu.Iyer@Sun.COM 
72511878SVenu.Iyer@Sun.COM 	if (txn1->dt_xid < txn2->dt_xid)
72611878SVenu.Iyer@Sun.COM 		return (-1);
72711878SVenu.Iyer@Sun.COM 	else if (txn1->dt_xid > txn2->dt_xid)
72811878SVenu.Iyer@Sun.COM 		return (1);
72911878SVenu.Iyer@Sun.COM 	else
73011878SVenu.Iyer@Sun.COM 		return (0);
73111878SVenu.Iyer@Sun.COM }
73211878SVenu.Iyer@Sun.COM 
73311878SVenu.Iyer@Sun.COM static int
compare_dhcpv6_ip(const void * arg1,const void * arg2)73411878SVenu.Iyer@Sun.COM compare_dhcpv6_ip(const void *arg1, const void *arg2)
73511878SVenu.Iyer@Sun.COM {
73611878SVenu.Iyer@Sun.COM 	const dhcpv6_addr_t	*ip1 = arg1, *ip2 = arg2;
73711878SVenu.Iyer@Sun.COM 	int			ret;
73811878SVenu.Iyer@Sun.COM 
73911878SVenu.Iyer@Sun.COM 	ret = memcmp(&ip1->da_addr, &ip2->da_addr, sizeof (in6_addr_t));
74011878SVenu.Iyer@Sun.COM 	if (ret < 0)
74111878SVenu.Iyer@Sun.COM 		return (-1);
74211878SVenu.Iyer@Sun.COM 	else if (ret > 0)
74311878SVenu.Iyer@Sun.COM 		return (1);
74411878SVenu.Iyer@Sun.COM 	else
74511878SVenu.Iyer@Sun.COM 		return (0);
74611878SVenu.Iyer@Sun.COM }
74711878SVenu.Iyer@Sun.COM 
74811878SVenu.Iyer@Sun.COM static int
compare_dhcpv6_cid(const void * arg1,const void * arg2)74911878SVenu.Iyer@Sun.COM compare_dhcpv6_cid(const void *arg1, const void *arg2)
75011878SVenu.Iyer@Sun.COM {
75111878SVenu.Iyer@Sun.COM 	const dhcpv6_cid_t	*cid1 = arg1, *cid2 = arg2;
75211878SVenu.Iyer@Sun.COM 	int			ret;
75311878SVenu.Iyer@Sun.COM 
75411878SVenu.Iyer@Sun.COM 	if (cid1->dc_cid_len < cid2->dc_cid_len)
75511878SVenu.Iyer@Sun.COM 		return (-1);
75611878SVenu.Iyer@Sun.COM 	else if (cid1->dc_cid_len > cid2->dc_cid_len)
75711878SVenu.Iyer@Sun.COM 		return (1);
75811878SVenu.Iyer@Sun.COM 
75911878SVenu.Iyer@Sun.COM 	if (cid1->dc_cid_len == 0)
76011878SVenu.Iyer@Sun.COM 		return (0);
76111878SVenu.Iyer@Sun.COM 
76211878SVenu.Iyer@Sun.COM 	ret = memcmp(cid1->dc_cid, cid2->dc_cid, cid1->dc_cid_len);
76311878SVenu.Iyer@Sun.COM 	if (ret < 0)
76411878SVenu.Iyer@Sun.COM 		return (-1);
76511878SVenu.Iyer@Sun.COM 	else if (ret > 0)
76611878SVenu.Iyer@Sun.COM 		return (1);
76711878SVenu.Iyer@Sun.COM 	else
76811878SVenu.Iyer@Sun.COM 		return (0);
76911878SVenu.Iyer@Sun.COM }
77011878SVenu.Iyer@Sun.COM 
77111878SVenu.Iyer@Sun.COM /*
77211878SVenu.Iyer@Sun.COM  * Locate the start of a DHCPv6 header.
77311878SVenu.Iyer@Sun.COM  * The possible return values and associated meanings are:
77411878SVenu.Iyer@Sun.COM  * 0      - packet is DHCP and has a DHCP header.
77511878SVenu.Iyer@Sun.COM  * EINVAL - packet is not DHCP. the recommended action is to let it pass.
77611878SVenu.Iyer@Sun.COM  * ENOSPC - packet is a initial fragment that is DHCP or is unidentifiable.
77711878SVenu.Iyer@Sun.COM  *          the recommended action is to drop it.
77811878SVenu.Iyer@Sun.COM  */
77911878SVenu.Iyer@Sun.COM static int
get_dhcpv6_info(ip6_t * ip6h,uchar_t * end,dhcpv6_message_t ** dh6)78011878SVenu.Iyer@Sun.COM get_dhcpv6_info(ip6_t *ip6h, uchar_t *end, dhcpv6_message_t **dh6)
78111878SVenu.Iyer@Sun.COM {
78211878SVenu.Iyer@Sun.COM 	uint16_t	hdrlen, client, server;
78311878SVenu.Iyer@Sun.COM 	boolean_t	first_frag = B_FALSE;
78411878SVenu.Iyer@Sun.COM 	ip6_frag_t	*frag = NULL;
78511878SVenu.Iyer@Sun.COM 	uint8_t		proto;
78611878SVenu.Iyer@Sun.COM 	struct udphdr	*udph;
78711878SVenu.Iyer@Sun.COM 	uchar_t		*dh;
78811878SVenu.Iyer@Sun.COM 
78911878SVenu.Iyer@Sun.COM 	if (!mac_ip_hdr_length_v6(ip6h, end, &hdrlen, &proto, &frag))
79011878SVenu.Iyer@Sun.COM 		return (ENOSPC);
79111878SVenu.Iyer@Sun.COM 
79211878SVenu.Iyer@Sun.COM 	if (proto != IPPROTO_UDP)
79311878SVenu.Iyer@Sun.COM 		return (EINVAL);
79411878SVenu.Iyer@Sun.COM 
79511878SVenu.Iyer@Sun.COM 	if (frag != NULL) {
79611878SVenu.Iyer@Sun.COM 		/*
79711878SVenu.Iyer@Sun.COM 		 * All non-initial fragments may pass because we cannot
79811878SVenu.Iyer@Sun.COM 		 * identify their type. It's safe to let them through
79911878SVenu.Iyer@Sun.COM 		 * because reassembly will fail if we decide to drop the
80011878SVenu.Iyer@Sun.COM 		 * initial fragment.
80111878SVenu.Iyer@Sun.COM 		 */
80211878SVenu.Iyer@Sun.COM 		if ((ntohs(frag->ip6f_offlg) & ~7) != 0)
80311878SVenu.Iyer@Sun.COM 			return (EINVAL);
80411878SVenu.Iyer@Sun.COM 		first_frag = B_TRUE;
80511878SVenu.Iyer@Sun.COM 	}
80611878SVenu.Iyer@Sun.COM 	/* drop packets without a udp header */
80711878SVenu.Iyer@Sun.COM 	udph = (struct udphdr *)((uchar_t *)ip6h + hdrlen);
80811878SVenu.Iyer@Sun.COM 	if ((uchar_t *)&udph[1] > end)
80911878SVenu.Iyer@Sun.COM 		return (ENOSPC);
81011878SVenu.Iyer@Sun.COM 
81111878SVenu.Iyer@Sun.COM 	client = htons(IPPORT_DHCPV6C);
81211878SVenu.Iyer@Sun.COM 	server = htons(IPPORT_DHCPV6S);
81311878SVenu.Iyer@Sun.COM 	if (udph->uh_sport != client && udph->uh_sport != server &&
81411878SVenu.Iyer@Sun.COM 	    udph->uh_dport != client && udph->uh_dport != server)
81511878SVenu.Iyer@Sun.COM 		return (EINVAL);
81611878SVenu.Iyer@Sun.COM 
81711878SVenu.Iyer@Sun.COM 	/* drop dhcp fragments */
81811878SVenu.Iyer@Sun.COM 	if (first_frag)
81911878SVenu.Iyer@Sun.COM 		return (ENOSPC);
82011878SVenu.Iyer@Sun.COM 
82111878SVenu.Iyer@Sun.COM 	dh = (uchar_t *)&udph[1];
82211878SVenu.Iyer@Sun.COM 	if (dh + sizeof (dhcpv6_message_t) > end)
82311878SVenu.Iyer@Sun.COM 		return (EINVAL);
82411878SVenu.Iyer@Sun.COM 
82511878SVenu.Iyer@Sun.COM 	*dh6 = (dhcpv6_message_t *)dh;
82611878SVenu.Iyer@Sun.COM 	return (0);
82711878SVenu.Iyer@Sun.COM }
82811878SVenu.Iyer@Sun.COM 
82911878SVenu.Iyer@Sun.COM /*
83011878SVenu.Iyer@Sun.COM  * Find the specified DHCPv6 option.
83111878SVenu.Iyer@Sun.COM  */
83211878SVenu.Iyer@Sun.COM static dhcpv6_option_t *
get_dhcpv6_option(void * buf,size_t buflen,dhcpv6_option_t * oldopt,uint16_t codenum,uint_t * retlenp)83311878SVenu.Iyer@Sun.COM get_dhcpv6_option(void *buf, size_t buflen, dhcpv6_option_t *oldopt,
83411878SVenu.Iyer@Sun.COM     uint16_t codenum, uint_t *retlenp)
83511878SVenu.Iyer@Sun.COM {
83611878SVenu.Iyer@Sun.COM 	uchar_t		*bp;
83711878SVenu.Iyer@Sun.COM 	dhcpv6_option_t	d6o;
83811878SVenu.Iyer@Sun.COM 	uint_t		olen;
83911878SVenu.Iyer@Sun.COM 
84011878SVenu.Iyer@Sun.COM 	codenum = htons(codenum);
84111878SVenu.Iyer@Sun.COM 	bp = buf;
84211878SVenu.Iyer@Sun.COM 	while (buflen >= sizeof (dhcpv6_option_t)) {
84311878SVenu.Iyer@Sun.COM 		bcopy(bp, &d6o, sizeof (d6o));
84411878SVenu.Iyer@Sun.COM 		olen = ntohs(d6o.d6o_len) + sizeof (d6o);
84511878SVenu.Iyer@Sun.COM 		if (olen > buflen)
84611878SVenu.Iyer@Sun.COM 			break;
84711878SVenu.Iyer@Sun.COM 		if (d6o.d6o_code != codenum || d6o.d6o_len == 0 ||
84811878SVenu.Iyer@Sun.COM 		    (oldopt != NULL && bp <= (uchar_t *)oldopt)) {
84911878SVenu.Iyer@Sun.COM 			bp += olen;
85011878SVenu.Iyer@Sun.COM 			buflen -= olen;
85111878SVenu.Iyer@Sun.COM 			continue;
85211878SVenu.Iyer@Sun.COM 		}
85311878SVenu.Iyer@Sun.COM 		if (retlenp != NULL)
85411878SVenu.Iyer@Sun.COM 			*retlenp = olen;
85511878SVenu.Iyer@Sun.COM 		/* LINTED : alignment */
85611878SVenu.Iyer@Sun.COM 		return ((dhcpv6_option_t *)bp);
85711878SVenu.Iyer@Sun.COM 	}
85811878SVenu.Iyer@Sun.COM 	return (NULL);
85911878SVenu.Iyer@Sun.COM }
86011878SVenu.Iyer@Sun.COM 
86111878SVenu.Iyer@Sun.COM /*
86211878SVenu.Iyer@Sun.COM  * Get the status code from a reply message.
86311878SVenu.Iyer@Sun.COM  */
86411878SVenu.Iyer@Sun.COM static int
get_dhcpv6_status(dhcpv6_message_t * dh6,uchar_t * end,uint16_t * status)86511878SVenu.Iyer@Sun.COM get_dhcpv6_status(dhcpv6_message_t *dh6, uchar_t *end, uint16_t *status)
86611878SVenu.Iyer@Sun.COM {
86711878SVenu.Iyer@Sun.COM 	dhcpv6_option_t	*d6o;
86811878SVenu.Iyer@Sun.COM 	uint_t		olen;
86911878SVenu.Iyer@Sun.COM 	uint16_t	s;
87011878SVenu.Iyer@Sun.COM 
87111878SVenu.Iyer@Sun.COM 	d6o = get_dhcpv6_option(&dh6[1], end - (uchar_t *)&dh6[1], NULL,
87211878SVenu.Iyer@Sun.COM 	    DHCPV6_OPT_STATUS_CODE, &olen);
87311878SVenu.Iyer@Sun.COM 
87411878SVenu.Iyer@Sun.COM 	/* Success is implied if status code is missing */
87511878SVenu.Iyer@Sun.COM 	if (d6o == NULL) {
87611878SVenu.Iyer@Sun.COM 		*status = DHCPV6_STAT_SUCCESS;
87711878SVenu.Iyer@Sun.COM 		return (0);
87811878SVenu.Iyer@Sun.COM 	}
87911878SVenu.Iyer@Sun.COM 	if ((uchar_t *)d6o + olen > end)
88011878SVenu.Iyer@Sun.COM 		return (EINVAL);
88111878SVenu.Iyer@Sun.COM 
88211878SVenu.Iyer@Sun.COM 	olen -= sizeof (*d6o);
88311878SVenu.Iyer@Sun.COM 	if (olen < sizeof (s))
88411878SVenu.Iyer@Sun.COM 		return (EINVAL);
88511878SVenu.Iyer@Sun.COM 
88611878SVenu.Iyer@Sun.COM 	bcopy(&d6o[1], &s, sizeof (s));
88711878SVenu.Iyer@Sun.COM 	*status = ntohs(s);
88811878SVenu.Iyer@Sun.COM 	return (0);
88911878SVenu.Iyer@Sun.COM }
89011878SVenu.Iyer@Sun.COM 
89111878SVenu.Iyer@Sun.COM /*
89211878SVenu.Iyer@Sun.COM  * Get the addresses from a reply message.
89311878SVenu.Iyer@Sun.COM  */
89411878SVenu.Iyer@Sun.COM static int
get_dhcpv6_addrs(dhcpv6_message_t * dh6,uchar_t * end,dhcpv6_cid_t * cid)89511878SVenu.Iyer@Sun.COM get_dhcpv6_addrs(dhcpv6_message_t *dh6, uchar_t *end, dhcpv6_cid_t *cid)
89611878SVenu.Iyer@Sun.COM {
89711878SVenu.Iyer@Sun.COM 	dhcpv6_option_t		*d6o;
89811878SVenu.Iyer@Sun.COM 	dhcpv6_addr_t		*next;
89911878SVenu.Iyer@Sun.COM 	uint_t			olen;
90011878SVenu.Iyer@Sun.COM 
90111878SVenu.Iyer@Sun.COM 	d6o = NULL;
90211878SVenu.Iyer@Sun.COM 	while ((d6o = get_dhcpv6_option(&dh6[1], end - (uchar_t *)&dh6[1],
90311878SVenu.Iyer@Sun.COM 	    d6o, DHCPV6_OPT_IA_NA, &olen)) != NULL) {
90411878SVenu.Iyer@Sun.COM 		dhcpv6_option_t		*d6so;
90511878SVenu.Iyer@Sun.COM 		dhcpv6_iaaddr_t		d6ia;
90611878SVenu.Iyer@Sun.COM 		dhcpv6_addr_t		**addrp;
90711878SVenu.Iyer@Sun.COM 		uchar_t			*obase;
90811878SVenu.Iyer@Sun.COM 		uint_t			solen;
90911878SVenu.Iyer@Sun.COM 
91011878SVenu.Iyer@Sun.COM 		if (olen < sizeof (dhcpv6_ia_na_t) ||
91111878SVenu.Iyer@Sun.COM 		    (uchar_t *)d6o + olen > end)
91211878SVenu.Iyer@Sun.COM 			goto fail;
91311878SVenu.Iyer@Sun.COM 
91411878SVenu.Iyer@Sun.COM 		obase = (uchar_t *)d6o + sizeof (dhcpv6_ia_na_t);
91511878SVenu.Iyer@Sun.COM 		olen -= sizeof (dhcpv6_ia_na_t);
91611878SVenu.Iyer@Sun.COM 		d6so = NULL;
91711878SVenu.Iyer@Sun.COM 		while ((d6so = get_dhcpv6_option(obase, olen, d6so,
91811878SVenu.Iyer@Sun.COM 		    DHCPV6_OPT_IAADDR, &solen)) != NULL) {
91911878SVenu.Iyer@Sun.COM 			if (solen < sizeof (dhcpv6_iaaddr_t) ||
92011878SVenu.Iyer@Sun.COM 			    (uchar_t *)d6so + solen > end)
92111878SVenu.Iyer@Sun.COM 				goto fail;
92211878SVenu.Iyer@Sun.COM 
92311878SVenu.Iyer@Sun.COM 			bcopy(d6so, &d6ia, sizeof (d6ia));
92411878SVenu.Iyer@Sun.COM 			for (addrp = &cid->dc_addr; *addrp != NULL;
92511878SVenu.Iyer@Sun.COM 			    addrp = &(*addrp)->da_next) {
92611878SVenu.Iyer@Sun.COM 				if (bcmp(&(*addrp)->da_addr, &d6ia.d6ia_addr,
92711878SVenu.Iyer@Sun.COM 				    sizeof (in6_addr_t)) == 0)
92811878SVenu.Iyer@Sun.COM 					goto fail;
92911878SVenu.Iyer@Sun.COM 			}
93011878SVenu.Iyer@Sun.COM 			if ((*addrp = kmem_zalloc(sizeof (dhcpv6_addr_t),
93111878SVenu.Iyer@Sun.COM 			    KM_NOSLEEP)) == NULL)
93211878SVenu.Iyer@Sun.COM 				goto fail;
93311878SVenu.Iyer@Sun.COM 
93411878SVenu.Iyer@Sun.COM 			bcopy(&d6ia.d6ia_addr, &(*addrp)->da_addr,
93511878SVenu.Iyer@Sun.COM 			    sizeof (in6_addr_t));
93611878SVenu.Iyer@Sun.COM 			cid->dc_addrcnt++;
93711878SVenu.Iyer@Sun.COM 		}
93811878SVenu.Iyer@Sun.COM 	}
93911878SVenu.Iyer@Sun.COM 	if (cid->dc_addrcnt == 0)
94011878SVenu.Iyer@Sun.COM 		return (ENOENT);
94111878SVenu.Iyer@Sun.COM 
94211878SVenu.Iyer@Sun.COM 	return (0);
94311878SVenu.Iyer@Sun.COM 
94411878SVenu.Iyer@Sun.COM fail:
94511878SVenu.Iyer@Sun.COM 	for (; cid->dc_addr != NULL; cid->dc_addr = next) {
94611878SVenu.Iyer@Sun.COM 		next = cid->dc_addr->da_next;
94711878SVenu.Iyer@Sun.COM 		kmem_free(cid->dc_addr, sizeof (dhcpv6_addr_t));
94811878SVenu.Iyer@Sun.COM 		cid->dc_addrcnt--;
94911878SVenu.Iyer@Sun.COM 	}
95011878SVenu.Iyer@Sun.COM 	ASSERT(cid->dc_addrcnt == 0);
95111878SVenu.Iyer@Sun.COM 	return (EINVAL);
95211878SVenu.Iyer@Sun.COM }
95311878SVenu.Iyer@Sun.COM 
95411878SVenu.Iyer@Sun.COM /*
95511878SVenu.Iyer@Sun.COM  * Free a cid.
95611878SVenu.Iyer@Sun.COM  * Before this gets called the caller must ensure that all the
95711878SVenu.Iyer@Sun.COM  * addresses are removed from the mci_v6_dyn_ip table.
95811878SVenu.Iyer@Sun.COM  */
95911878SVenu.Iyer@Sun.COM static void
free_dhcpv6_cid(dhcpv6_cid_t * cid)96011878SVenu.Iyer@Sun.COM free_dhcpv6_cid(dhcpv6_cid_t *cid)
96111878SVenu.Iyer@Sun.COM {
96211878SVenu.Iyer@Sun.COM 	dhcpv6_addr_t	*addr, *next;
96311878SVenu.Iyer@Sun.COM 	uint_t		cnt = 0;
96411878SVenu.Iyer@Sun.COM 
96511878SVenu.Iyer@Sun.COM 	kmem_free(cid->dc_cid, cid->dc_cid_len);
96611878SVenu.Iyer@Sun.COM 	for (addr = cid->dc_addr; addr != NULL; addr = next) {
96711878SVenu.Iyer@Sun.COM 		next = addr->da_next;
96811878SVenu.Iyer@Sun.COM 		kmem_free(addr, sizeof (*addr));
96911878SVenu.Iyer@Sun.COM 		cnt++;
97011878SVenu.Iyer@Sun.COM 	}
97111878SVenu.Iyer@Sun.COM 	ASSERT(cnt == cid->dc_addrcnt);
97211878SVenu.Iyer@Sun.COM 	kmem_free(cid, sizeof (*cid));
97311878SVenu.Iyer@Sun.COM }
97411878SVenu.Iyer@Sun.COM 
97511878SVenu.Iyer@Sun.COM /*
97611878SVenu.Iyer@Sun.COM  * Extract the DUID from a message. The associated addresses will be
97711878SVenu.Iyer@Sun.COM  * extracted later from the reply message.
97811878SVenu.Iyer@Sun.COM  */
97911878SVenu.Iyer@Sun.COM static dhcpv6_cid_t *
create_dhcpv6_cid(dhcpv6_message_t * dh6,uchar_t * end)98011878SVenu.Iyer@Sun.COM create_dhcpv6_cid(dhcpv6_message_t *dh6, uchar_t *end)
98111878SVenu.Iyer@Sun.COM {
98211878SVenu.Iyer@Sun.COM 	dhcpv6_option_t		*d6o;
98311878SVenu.Iyer@Sun.COM 	dhcpv6_cid_t		*cid;
98411878SVenu.Iyer@Sun.COM 	uchar_t			*rawcid;
98511878SVenu.Iyer@Sun.COM 	uint_t			olen, rawcidlen;
98611878SVenu.Iyer@Sun.COM 
98711878SVenu.Iyer@Sun.COM 	d6o = get_dhcpv6_option(&dh6[1], end - (uchar_t *)&dh6[1], NULL,
98811878SVenu.Iyer@Sun.COM 	    DHCPV6_OPT_CLIENTID, &olen);
98911878SVenu.Iyer@Sun.COM 	if (d6o == NULL || (uchar_t *)d6o + olen > end)
99011878SVenu.Iyer@Sun.COM 		return (NULL);
99111878SVenu.Iyer@Sun.COM 
99211878SVenu.Iyer@Sun.COM 	rawcidlen = olen - sizeof (*d6o);
99311878SVenu.Iyer@Sun.COM 	if ((rawcid = kmem_zalloc(rawcidlen, KM_NOSLEEP)) == NULL)
99411878SVenu.Iyer@Sun.COM 		return (NULL);
99511878SVenu.Iyer@Sun.COM 	bcopy(d6o + 1, rawcid, rawcidlen);
99611878SVenu.Iyer@Sun.COM 
99711878SVenu.Iyer@Sun.COM 	if ((cid = kmem_zalloc(sizeof (*cid), KM_NOSLEEP)) == NULL) {
99811878SVenu.Iyer@Sun.COM 		kmem_free(rawcid, rawcidlen);
99911878SVenu.Iyer@Sun.COM 		return (NULL);
100011878SVenu.Iyer@Sun.COM 	}
100111878SVenu.Iyer@Sun.COM 	cid->dc_cid = rawcid;
100211878SVenu.Iyer@Sun.COM 	cid->dc_cid_len = rawcidlen;
100311878SVenu.Iyer@Sun.COM 	return (cid);
100411878SVenu.Iyer@Sun.COM }
100511878SVenu.Iyer@Sun.COM 
100611878SVenu.Iyer@Sun.COM /*
100711878SVenu.Iyer@Sun.COM  * Remove a cid from mci_v6_cid. The addresses owned by the cid
100811878SVenu.Iyer@Sun.COM  * are also removed from mci_v6_dyn_ip.
100911878SVenu.Iyer@Sun.COM  */
101011878SVenu.Iyer@Sun.COM static void
remove_dhcpv6_cid(mac_client_impl_t * mcip,dhcpv6_cid_t * cid)101111878SVenu.Iyer@Sun.COM remove_dhcpv6_cid(mac_client_impl_t *mcip, dhcpv6_cid_t *cid)
101211878SVenu.Iyer@Sun.COM {
101311878SVenu.Iyer@Sun.COM 	dhcpv6_addr_t	*addr, *tmp_addr;
101411878SVenu.Iyer@Sun.COM 
101511878SVenu.Iyer@Sun.COM 	ASSERT(MUTEX_HELD(&mcip->mci_protect_lock));
101611878SVenu.Iyer@Sun.COM 	avl_remove(&mcip->mci_v6_cid, cid);
101711878SVenu.Iyer@Sun.COM 	for (addr = cid->dc_addr; addr != NULL; addr = addr->da_next) {
101811878SVenu.Iyer@Sun.COM 		tmp_addr = avl_find(&mcip->mci_v6_dyn_ip, addr, NULL);
101911878SVenu.Iyer@Sun.COM 		if (tmp_addr == addr)
102011878SVenu.Iyer@Sun.COM 			avl_remove(&mcip->mci_v6_dyn_ip, addr);
102111878SVenu.Iyer@Sun.COM 	}
102211878SVenu.Iyer@Sun.COM }
102311878SVenu.Iyer@Sun.COM 
102411878SVenu.Iyer@Sun.COM /*
102511878SVenu.Iyer@Sun.COM  * Find and remove a matching cid and associated addresses from
102611878SVenu.Iyer@Sun.COM  * their respective tables.
102711878SVenu.Iyer@Sun.COM  */
102811878SVenu.Iyer@Sun.COM static void
release_dhcpv6_cid(mac_client_impl_t * mcip,dhcpv6_cid_t * cid)102911878SVenu.Iyer@Sun.COM release_dhcpv6_cid(mac_client_impl_t *mcip, dhcpv6_cid_t *cid)
103011878SVenu.Iyer@Sun.COM {
103111878SVenu.Iyer@Sun.COM 	dhcpv6_cid_t	*oldcid;
103211878SVenu.Iyer@Sun.COM 
103311878SVenu.Iyer@Sun.COM 	ASSERT(MUTEX_HELD(&mcip->mci_protect_lock));
103411878SVenu.Iyer@Sun.COM 	if ((oldcid = avl_find(&mcip->mci_v6_cid, cid, NULL)) == NULL)
103511878SVenu.Iyer@Sun.COM 		return;
103611878SVenu.Iyer@Sun.COM 
103711878SVenu.Iyer@Sun.COM 	/*
103811878SVenu.Iyer@Sun.COM 	 * Since cid belongs to a pending txn, it can't possibly be in
103911878SVenu.Iyer@Sun.COM 	 * mci_v6_cid. Anything that's found must be an existing cid.
104010734SEric Cheng 	 */
104111878SVenu.Iyer@Sun.COM 	ASSERT(oldcid != cid);
104211878SVenu.Iyer@Sun.COM 	remove_dhcpv6_cid(mcip, oldcid);
104311878SVenu.Iyer@Sun.COM 	free_dhcpv6_cid(oldcid);
104411878SVenu.Iyer@Sun.COM }
104511878SVenu.Iyer@Sun.COM 
104611878SVenu.Iyer@Sun.COM /*
104711878SVenu.Iyer@Sun.COM  * Insert cid into mci_v6_cid.
104811878SVenu.Iyer@Sun.COM  */
104911878SVenu.Iyer@Sun.COM static int
insert_dhcpv6_cid(mac_client_impl_t * mcip,dhcpv6_cid_t * cid)105011878SVenu.Iyer@Sun.COM insert_dhcpv6_cid(mac_client_impl_t *mcip, dhcpv6_cid_t *cid)
105111878SVenu.Iyer@Sun.COM {
105211878SVenu.Iyer@Sun.COM 	avl_index_t	where;
105311878SVenu.Iyer@Sun.COM 	dhcpv6_addr_t	*addr;
105411878SVenu.Iyer@Sun.COM 
105511878SVenu.Iyer@Sun.COM 	ASSERT(MUTEX_HELD(&mcip->mci_protect_lock));
105611878SVenu.Iyer@Sun.COM 	if (avl_find(&mcip->mci_v6_cid, cid, &where) != NULL)
105711878SVenu.Iyer@Sun.COM 		return (EEXIST);
105811878SVenu.Iyer@Sun.COM 
105911878SVenu.Iyer@Sun.COM 	if (avl_numnodes(&mcip->mci_v6_cid) >= dhcp_max_completed_txn) {
106011878SVenu.Iyer@Sun.COM 		BUMP_STAT(mcip, dhcpdropped);
106111878SVenu.Iyer@Sun.COM 		return (EAGAIN);
106211878SVenu.Iyer@Sun.COM 	}
106311878SVenu.Iyer@Sun.COM 	avl_insert(&mcip->mci_v6_cid, cid, where);
106411878SVenu.Iyer@Sun.COM 	for (addr = cid->dc_addr; addr != NULL; addr = addr->da_next) {
106511878SVenu.Iyer@Sun.COM 		if (avl_find(&mcip->mci_v6_dyn_ip, addr, &where) != NULL)
106611878SVenu.Iyer@Sun.COM 			goto fail;
106711878SVenu.Iyer@Sun.COM 
106811878SVenu.Iyer@Sun.COM 		avl_insert(&mcip->mci_v6_dyn_ip, addr, where);
106911878SVenu.Iyer@Sun.COM 	}
107011878SVenu.Iyer@Sun.COM 	return (0);
107111878SVenu.Iyer@Sun.COM 
107211878SVenu.Iyer@Sun.COM fail:
107311878SVenu.Iyer@Sun.COM 	remove_dhcpv6_cid(mcip, cid);
107411878SVenu.Iyer@Sun.COM 	return (EEXIST);
107511878SVenu.Iyer@Sun.COM }
107611878SVenu.Iyer@Sun.COM 
107711878SVenu.Iyer@Sun.COM /*
107811878SVenu.Iyer@Sun.COM  * Check whether an IP address is in the dyn-ip table.
107911878SVenu.Iyer@Sun.COM  */
108011878SVenu.Iyer@Sun.COM static boolean_t
check_dhcpv6_dyn_ip(mac_client_impl_t * mcip,in6_addr_t * addr)108111878SVenu.Iyer@Sun.COM check_dhcpv6_dyn_ip(mac_client_impl_t *mcip, in6_addr_t *addr)
108211878SVenu.Iyer@Sun.COM {
108311878SVenu.Iyer@Sun.COM 	dhcpv6_addr_t	tmp_addr, *a;
108411878SVenu.Iyer@Sun.COM 
108511878SVenu.Iyer@Sun.COM 	mutex_enter(&mcip->mci_protect_lock);
108611878SVenu.Iyer@Sun.COM 	bcopy(addr, &tmp_addr.da_addr, sizeof (in6_addr_t));
108711878SVenu.Iyer@Sun.COM 	a = avl_find(&mcip->mci_v6_dyn_ip, &tmp_addr, NULL);
108811878SVenu.Iyer@Sun.COM 	mutex_exit(&mcip->mci_protect_lock);
108911878SVenu.Iyer@Sun.COM 	return (a != NULL);
109011878SVenu.Iyer@Sun.COM }
109111878SVenu.Iyer@Sun.COM 
109211878SVenu.Iyer@Sun.COM static dhcpv6_txn_t *
find_dhcpv6_pending_txn(mac_client_impl_t * mcip,uint32_t xid)109311878SVenu.Iyer@Sun.COM find_dhcpv6_pending_txn(mac_client_impl_t *mcip, uint32_t xid)
109411878SVenu.Iyer@Sun.COM {
109511878SVenu.Iyer@Sun.COM 	dhcpv6_txn_t	tmp_txn;
109611878SVenu.Iyer@Sun.COM 
109711878SVenu.Iyer@Sun.COM 	ASSERT(MUTEX_HELD(&mcip->mci_protect_lock));
109811878SVenu.Iyer@Sun.COM 	tmp_txn.dt_xid = xid;
109911878SVenu.Iyer@Sun.COM 	return (avl_find(&mcip->mci_v6_pending_txn, &tmp_txn, NULL));
110011878SVenu.Iyer@Sun.COM }
110111878SVenu.Iyer@Sun.COM 
110211878SVenu.Iyer@Sun.COM static void
remove_dhcpv6_pending_txn(mac_client_impl_t * mcip,dhcpv6_txn_t * txn)110311878SVenu.Iyer@Sun.COM remove_dhcpv6_pending_txn(mac_client_impl_t *mcip, dhcpv6_txn_t *txn)
110411878SVenu.Iyer@Sun.COM {
110511878SVenu.Iyer@Sun.COM 	ASSERT(MUTEX_HELD(&mcip->mci_protect_lock));
110611878SVenu.Iyer@Sun.COM 	avl_remove(&mcip->mci_v6_pending_txn, txn);
110711878SVenu.Iyer@Sun.COM }
110811878SVenu.Iyer@Sun.COM 
110911878SVenu.Iyer@Sun.COM static dhcpv6_txn_t *
create_dhcpv6_txn(uint32_t xid,dhcpv6_cid_t * cid)111011878SVenu.Iyer@Sun.COM create_dhcpv6_txn(uint32_t xid, dhcpv6_cid_t *cid)
111111878SVenu.Iyer@Sun.COM {
111211878SVenu.Iyer@Sun.COM 	dhcpv6_txn_t	*txn;
111311878SVenu.Iyer@Sun.COM 
111411878SVenu.Iyer@Sun.COM 	if ((txn = kmem_zalloc(sizeof (dhcpv6_txn_t), KM_NOSLEEP)) == NULL)
111511878SVenu.Iyer@Sun.COM 		return (NULL);
111611878SVenu.Iyer@Sun.COM 
111711878SVenu.Iyer@Sun.COM 	txn->dt_xid = xid;
111811878SVenu.Iyer@Sun.COM 	txn->dt_cid = cid;
111911878SVenu.Iyer@Sun.COM 	txn->dt_timestamp = ddi_get_time();
112011878SVenu.Iyer@Sun.COM 	return (txn);
112111878SVenu.Iyer@Sun.COM }
112211878SVenu.Iyer@Sun.COM 
112311878SVenu.Iyer@Sun.COM static void
free_dhcpv6_txn(dhcpv6_txn_t * txn)112411878SVenu.Iyer@Sun.COM free_dhcpv6_txn(dhcpv6_txn_t *txn)
112511878SVenu.Iyer@Sun.COM {
112611878SVenu.Iyer@Sun.COM 	if (txn->dt_cid != NULL)
112711878SVenu.Iyer@Sun.COM 		free_dhcpv6_cid(txn->dt_cid);
112811878SVenu.Iyer@Sun.COM 	kmem_free(txn, sizeof (dhcpv6_txn_t));
112911878SVenu.Iyer@Sun.COM }
113011878SVenu.Iyer@Sun.COM 
113111878SVenu.Iyer@Sun.COM static int
insert_dhcpv6_pending_txn(mac_client_impl_t * mcip,dhcpv6_txn_t * txn)113211878SVenu.Iyer@Sun.COM insert_dhcpv6_pending_txn(mac_client_impl_t *mcip, dhcpv6_txn_t *txn)
113311878SVenu.Iyer@Sun.COM {
113411878SVenu.Iyer@Sun.COM 	avl_index_t	where;
113511878SVenu.Iyer@Sun.COM 
113611878SVenu.Iyer@Sun.COM 	ASSERT(MUTEX_HELD(&mcip->mci_protect_lock));
113711878SVenu.Iyer@Sun.COM 	if (avl_find(&mcip->mci_v6_pending_txn, txn, &where) != NULL)
113811878SVenu.Iyer@Sun.COM 		return (EEXIST);
113911878SVenu.Iyer@Sun.COM 
114011878SVenu.Iyer@Sun.COM 	if (avl_numnodes(&mcip->mci_v6_pending_txn) >= dhcp_max_pending_txn) {
114111878SVenu.Iyer@Sun.COM 		BUMP_STAT(mcip, dhcpdropped);
114211878SVenu.Iyer@Sun.COM 		return (EAGAIN);
114311878SVenu.Iyer@Sun.COM 	}
114411878SVenu.Iyer@Sun.COM 	avl_insert(&mcip->mci_v6_pending_txn, txn, where);
114511878SVenu.Iyer@Sun.COM 	return (0);
114611878SVenu.Iyer@Sun.COM }
114711878SVenu.Iyer@Sun.COM 
114811878SVenu.Iyer@Sun.COM /*
114911878SVenu.Iyer@Sun.COM  * Clean up all v6 tables.
115011878SVenu.Iyer@Sun.COM  */
115111878SVenu.Iyer@Sun.COM static void
flush_dhcpv6(mac_client_impl_t * mcip)115211878SVenu.Iyer@Sun.COM flush_dhcpv6(mac_client_impl_t *mcip)
115311878SVenu.Iyer@Sun.COM {
115411878SVenu.Iyer@Sun.COM 	void		*cookie = NULL;
115511878SVenu.Iyer@Sun.COM 	dhcpv6_cid_t	*cid;
115611878SVenu.Iyer@Sun.COM 	dhcpv6_txn_t	*txn;
115711878SVenu.Iyer@Sun.COM 
115811878SVenu.Iyer@Sun.COM 	ASSERT(MUTEX_HELD(&mcip->mci_protect_lock));
115911878SVenu.Iyer@Sun.COM 	while (avl_destroy_nodes(&mcip->mci_v6_dyn_ip, &cookie) != NULL) {
116011878SVenu.Iyer@Sun.COM 	}
116111878SVenu.Iyer@Sun.COM 	cookie = NULL;
116211878SVenu.Iyer@Sun.COM 	while ((cid = avl_destroy_nodes(&mcip->mci_v6_cid, &cookie)) != NULL) {
116311878SVenu.Iyer@Sun.COM 		free_dhcpv6_cid(cid);
116411878SVenu.Iyer@Sun.COM 	}
116511878SVenu.Iyer@Sun.COM 	cookie = NULL;
116611878SVenu.Iyer@Sun.COM 	while ((txn = avl_destroy_nodes(&mcip->mci_v6_pending_txn,
116711878SVenu.Iyer@Sun.COM 	    &cookie)) != NULL) {
116811878SVenu.Iyer@Sun.COM 		free_dhcpv6_txn(txn);
116911878SVenu.Iyer@Sun.COM 	}
117011878SVenu.Iyer@Sun.COM }
117111878SVenu.Iyer@Sun.COM 
117211878SVenu.Iyer@Sun.COM /*
117311878SVenu.Iyer@Sun.COM  * Cleanup stale DHCPv6 transactions.
117411878SVenu.Iyer@Sun.COM  */
117511878SVenu.Iyer@Sun.COM static void
txn_cleanup_v6(mac_client_impl_t * mcip)117611878SVenu.Iyer@Sun.COM txn_cleanup_v6(mac_client_impl_t *mcip)
117711878SVenu.Iyer@Sun.COM {
117811878SVenu.Iyer@Sun.COM 	dhcpv6_txn_t		*txn, *next, *txn_list = NULL;
117911878SVenu.Iyer@Sun.COM 
118011878SVenu.Iyer@Sun.COM 	/*
118111878SVenu.Iyer@Sun.COM 	 * Find stale pending transactions and place them on a list
118211878SVenu.Iyer@Sun.COM 	 * to be removed.
118311878SVenu.Iyer@Sun.COM 	 */
118411878SVenu.Iyer@Sun.COM 	for (txn = avl_first(&mcip->mci_v6_pending_txn); txn != NULL;
118511878SVenu.Iyer@Sun.COM 	    txn = avl_walk(&mcip->mci_v6_pending_txn, txn, AVL_AFTER)) {
118611878SVenu.Iyer@Sun.COM 		if (ddi_get_time() - txn->dt_timestamp >
118711878SVenu.Iyer@Sun.COM 		    txn_cleanup_interval) {
118811878SVenu.Iyer@Sun.COM 			DTRACE_PROBE2(found__expired__txn,
118911878SVenu.Iyer@Sun.COM 			    mac_client_impl_t *, mcip,
119011878SVenu.Iyer@Sun.COM 			    dhcpv6_txn_t *, txn);
119111878SVenu.Iyer@Sun.COM 
119211878SVenu.Iyer@Sun.COM 			txn->dt_next = txn_list;
119311878SVenu.Iyer@Sun.COM 			txn_list = txn;
119411878SVenu.Iyer@Sun.COM 		}
119511878SVenu.Iyer@Sun.COM 	}
119611878SVenu.Iyer@Sun.COM 
119711878SVenu.Iyer@Sun.COM 	/*
119811878SVenu.Iyer@Sun.COM 	 * Remove and free stale pending transactions.
119911878SVenu.Iyer@Sun.COM 	 * Release any existing cids matching the stale transactions.
120011878SVenu.Iyer@Sun.COM 	 */
120111878SVenu.Iyer@Sun.COM 	for (txn = txn_list; txn != NULL; txn = next) {
120211878SVenu.Iyer@Sun.COM 		avl_remove(&mcip->mci_v6_pending_txn, txn);
120311878SVenu.Iyer@Sun.COM 		release_dhcpv6_cid(mcip, txn->dt_cid);
120411878SVenu.Iyer@Sun.COM 		next = txn->dt_next;
120511878SVenu.Iyer@Sun.COM 		txn->dt_next = NULL;
120611878SVenu.Iyer@Sun.COM 
120711878SVenu.Iyer@Sun.COM 		DTRACE_PROBE2(freeing__txn, mac_client_impl_t *, mcip,
120811878SVenu.Iyer@Sun.COM 		    dhcpv6_txn_t *, txn);
120911878SVenu.Iyer@Sun.COM 		free_dhcpv6_txn(txn);
121011878SVenu.Iyer@Sun.COM 	}
121111878SVenu.Iyer@Sun.COM 
121211878SVenu.Iyer@Sun.COM }
121311878SVenu.Iyer@Sun.COM 
121411878SVenu.Iyer@Sun.COM /*
121511878SVenu.Iyer@Sun.COM  * Core logic for intercepting outbound DHCPv6 packets.
121611878SVenu.Iyer@Sun.COM  */
121712748SSowmini.Varadhan@oracle.COM static boolean_t
intercept_dhcpv6_outbound(mac_client_impl_t * mcip,ip6_t * ip6h,uchar_t * end)121811878SVenu.Iyer@Sun.COM intercept_dhcpv6_outbound(mac_client_impl_t *mcip, ip6_t *ip6h, uchar_t *end)
121911878SVenu.Iyer@Sun.COM {
122011878SVenu.Iyer@Sun.COM 	dhcpv6_message_t	*dh6;
122111878SVenu.Iyer@Sun.COM 	dhcpv6_txn_t		*txn;
122211878SVenu.Iyer@Sun.COM 	dhcpv6_cid_t		*cid = NULL;
122311878SVenu.Iyer@Sun.COM 	uint32_t		xid;
122411878SVenu.Iyer@Sun.COM 	uint8_t			mtype;
122512748SSowmini.Varadhan@oracle.COM 	mac_resource_props_t *mrp = MCIP_RESOURCE_PROPS(mcip);
122611878SVenu.Iyer@Sun.COM 
122711878SVenu.Iyer@Sun.COM 	if (get_dhcpv6_info(ip6h, end, &dh6) != 0)
122812748SSowmini.Varadhan@oracle.COM 		return (B_TRUE);
122912748SSowmini.Varadhan@oracle.COM 
123012748SSowmini.Varadhan@oracle.COM 	/* ip_nospoof/allowed-ips and DHCP are mutually exclusive by default */
123112748SSowmini.Varadhan@oracle.COM 	if (allowed_ips_set(mrp, IPV6_VERSION))
123212748SSowmini.Varadhan@oracle.COM 		return (B_FALSE);
123311878SVenu.Iyer@Sun.COM 
123411878SVenu.Iyer@Sun.COM 	mtype = dh6->d6m_msg_type;
123511878SVenu.Iyer@Sun.COM 	if (mtype != DHCPV6_MSG_REQUEST && mtype != DHCPV6_MSG_RENEW &&
123611878SVenu.Iyer@Sun.COM 	    mtype != DHCPV6_MSG_REBIND && mtype != DHCPV6_MSG_RELEASE)
123712748SSowmini.Varadhan@oracle.COM 		return (B_TRUE);
123811878SVenu.Iyer@Sun.COM 
123911878SVenu.Iyer@Sun.COM 	if ((cid = create_dhcpv6_cid(dh6, end)) == NULL)
124012748SSowmini.Varadhan@oracle.COM 		return (B_TRUE);
124111878SVenu.Iyer@Sun.COM 
124211878SVenu.Iyer@Sun.COM 	mutex_enter(&mcip->mci_protect_lock);
124311878SVenu.Iyer@Sun.COM 	if (mtype == DHCPV6_MSG_RELEASE) {
124411878SVenu.Iyer@Sun.COM 		release_dhcpv6_cid(mcip, cid);
124511878SVenu.Iyer@Sun.COM 		goto done;
124611878SVenu.Iyer@Sun.COM 	}
124711878SVenu.Iyer@Sun.COM 	xid = DHCPV6_GET_TRANSID(dh6);
124811878SVenu.Iyer@Sun.COM 	if ((txn = find_dhcpv6_pending_txn(mcip, xid)) != NULL) {
124911878SVenu.Iyer@Sun.COM 		DTRACE_PROBE2(update, mac_client_impl_t *, mcip,
125011878SVenu.Iyer@Sun.COM 		    dhcpv6_txn_t *, txn);
125111878SVenu.Iyer@Sun.COM 		txn->dt_timestamp = ddi_get_time();
125211878SVenu.Iyer@Sun.COM 		goto done;
125311878SVenu.Iyer@Sun.COM 	}
125411878SVenu.Iyer@Sun.COM 	if ((txn = create_dhcpv6_txn(xid, cid)) == NULL)
125511878SVenu.Iyer@Sun.COM 		goto done;
125611878SVenu.Iyer@Sun.COM 
125711878SVenu.Iyer@Sun.COM 	cid = NULL;
125811878SVenu.Iyer@Sun.COM 	if (insert_dhcpv6_pending_txn(mcip, txn) != 0) {
125911878SVenu.Iyer@Sun.COM 		DTRACE_PROBE2(insert__failed, mac_client_impl_t *, mcip,
126011878SVenu.Iyer@Sun.COM 		    dhcpv6_txn_t *, txn);
126111878SVenu.Iyer@Sun.COM 		free_dhcpv6_txn(txn);
126211878SVenu.Iyer@Sun.COM 		goto done;
126311878SVenu.Iyer@Sun.COM 	}
126411878SVenu.Iyer@Sun.COM 	start_txn_cleanup_timer(mcip);
126511878SVenu.Iyer@Sun.COM 
126611878SVenu.Iyer@Sun.COM 	DTRACE_PROBE2(txn__pending, mac_client_impl_t *, mcip,
126711878SVenu.Iyer@Sun.COM 	    dhcpv6_txn_t *, txn);
126811878SVenu.Iyer@Sun.COM 
126911878SVenu.Iyer@Sun.COM done:
127011878SVenu.Iyer@Sun.COM 	if (cid != NULL)
127111878SVenu.Iyer@Sun.COM 		free_dhcpv6_cid(cid);
127211878SVenu.Iyer@Sun.COM 
127311878SVenu.Iyer@Sun.COM 	mutex_exit(&mcip->mci_protect_lock);
127412748SSowmini.Varadhan@oracle.COM 	return (B_TRUE);
127511878SVenu.Iyer@Sun.COM }
127611878SVenu.Iyer@Sun.COM 
127711878SVenu.Iyer@Sun.COM /*
127811878SVenu.Iyer@Sun.COM  * Core logic for intercepting inbound DHCPv6 packets.
127911878SVenu.Iyer@Sun.COM  */
128011878SVenu.Iyer@Sun.COM static void
intercept_dhcpv6_inbound(mac_client_impl_t * mcip,ip6_t * ip6h,uchar_t * end)128111878SVenu.Iyer@Sun.COM intercept_dhcpv6_inbound(mac_client_impl_t *mcip, ip6_t *ip6h, uchar_t *end)
128211878SVenu.Iyer@Sun.COM {
128311878SVenu.Iyer@Sun.COM 	dhcpv6_message_t	*dh6;
128411878SVenu.Iyer@Sun.COM 	dhcpv6_txn_t		*txn;
128511878SVenu.Iyer@Sun.COM 	uint32_t		xid;
128611878SVenu.Iyer@Sun.COM 	uint8_t			mtype;
128711878SVenu.Iyer@Sun.COM 	uint16_t		status;
128811878SVenu.Iyer@Sun.COM 
128911878SVenu.Iyer@Sun.COM 	if (get_dhcpv6_info(ip6h, end, &dh6) != 0)
129011878SVenu.Iyer@Sun.COM 		return;
129111878SVenu.Iyer@Sun.COM 
129211878SVenu.Iyer@Sun.COM 	mtype = dh6->d6m_msg_type;
129311878SVenu.Iyer@Sun.COM 	if (mtype != DHCPV6_MSG_REPLY)
129411878SVenu.Iyer@Sun.COM 		return;
129511878SVenu.Iyer@Sun.COM 
129611878SVenu.Iyer@Sun.COM 	mutex_enter(&mcip->mci_protect_lock);
129711878SVenu.Iyer@Sun.COM 	xid = DHCPV6_GET_TRANSID(dh6);
129811878SVenu.Iyer@Sun.COM 	if ((txn = find_dhcpv6_pending_txn(mcip, xid)) == NULL) {
129911878SVenu.Iyer@Sun.COM 		DTRACE_PROBE2(txn__not__found, mac_client_impl_t *, mcip,
130011878SVenu.Iyer@Sun.COM 		    dhcpv6_message_t *, dh6);
130111878SVenu.Iyer@Sun.COM 		goto done;
130211878SVenu.Iyer@Sun.COM 	}
130311878SVenu.Iyer@Sun.COM 	remove_dhcpv6_pending_txn(mcip, txn);
130411878SVenu.Iyer@Sun.COM 	release_dhcpv6_cid(mcip, txn->dt_cid);
130511878SVenu.Iyer@Sun.COM 
130611878SVenu.Iyer@Sun.COM 	if (get_dhcpv6_status(dh6, end, &status) != 0 ||
130711878SVenu.Iyer@Sun.COM 	    status != DHCPV6_STAT_SUCCESS) {
130811878SVenu.Iyer@Sun.COM 		DTRACE_PROBE2(error__status, mac_client_impl_t *, mcip,
130911878SVenu.Iyer@Sun.COM 		    dhcpv6_txn_t *, txn);
131011878SVenu.Iyer@Sun.COM 		goto done;
131111878SVenu.Iyer@Sun.COM 	}
131211878SVenu.Iyer@Sun.COM 	if (get_dhcpv6_addrs(dh6, end, txn->dt_cid) != 0) {
131311878SVenu.Iyer@Sun.COM 		DTRACE_PROBE2(no__addrs, mac_client_impl_t *, mcip,
131411878SVenu.Iyer@Sun.COM 		    dhcpv6_txn_t *, txn);
131511878SVenu.Iyer@Sun.COM 		goto done;
131611878SVenu.Iyer@Sun.COM 	}
131711878SVenu.Iyer@Sun.COM 	if (insert_dhcpv6_cid(mcip, txn->dt_cid) != 0) {
131811878SVenu.Iyer@Sun.COM 		DTRACE_PROBE2(insert__failed, mac_client_impl_t *, mcip,
131911878SVenu.Iyer@Sun.COM 		    dhcpv6_txn_t *, txn);
132011878SVenu.Iyer@Sun.COM 		goto done;
132111878SVenu.Iyer@Sun.COM 	}
132211878SVenu.Iyer@Sun.COM 	DTRACE_PROBE2(txn__completed, mac_client_impl_t *, mcip,
132311878SVenu.Iyer@Sun.COM 	    dhcpv6_txn_t *, txn);
132411878SVenu.Iyer@Sun.COM 
132511878SVenu.Iyer@Sun.COM 	txn->dt_cid = NULL;
132611878SVenu.Iyer@Sun.COM 
132711878SVenu.Iyer@Sun.COM done:
132811878SVenu.Iyer@Sun.COM 	if (txn != NULL)
132911878SVenu.Iyer@Sun.COM 		free_dhcpv6_txn(txn);
133011878SVenu.Iyer@Sun.COM 	mutex_exit(&mcip->mci_protect_lock);
133111878SVenu.Iyer@Sun.COM }
133211878SVenu.Iyer@Sun.COM 
133311878SVenu.Iyer@Sun.COM /*
133411878SVenu.Iyer@Sun.COM  * Timer for cleaning up stale transactions.
133511878SVenu.Iyer@Sun.COM  */
133611878SVenu.Iyer@Sun.COM static void
txn_cleanup_timer(void * arg)133711878SVenu.Iyer@Sun.COM txn_cleanup_timer(void *arg)
133811878SVenu.Iyer@Sun.COM {
133911878SVenu.Iyer@Sun.COM 	mac_client_impl_t	*mcip = arg;
134011878SVenu.Iyer@Sun.COM 
134111878SVenu.Iyer@Sun.COM 	mutex_enter(&mcip->mci_protect_lock);
134211878SVenu.Iyer@Sun.COM 	if (mcip->mci_txn_cleanup_tid == 0) {
134311878SVenu.Iyer@Sun.COM 		/* do nothing if timer got cancelled */
134411878SVenu.Iyer@Sun.COM 		mutex_exit(&mcip->mci_protect_lock);
134511878SVenu.Iyer@Sun.COM 		return;
134611878SVenu.Iyer@Sun.COM 	}
134711878SVenu.Iyer@Sun.COM 	mcip->mci_txn_cleanup_tid = 0;
134811878SVenu.Iyer@Sun.COM 
134911878SVenu.Iyer@Sun.COM 	txn_cleanup_v4(mcip);
135011878SVenu.Iyer@Sun.COM 	txn_cleanup_v6(mcip);
135111878SVenu.Iyer@Sun.COM 
135211878SVenu.Iyer@Sun.COM 	/*
135311878SVenu.Iyer@Sun.COM 	 * Restart timer if pending transactions still exist.
135411878SVenu.Iyer@Sun.COM 	 */
135511878SVenu.Iyer@Sun.COM 	if (!avl_is_empty(&mcip->mci_v4_pending_txn) ||
135611878SVenu.Iyer@Sun.COM 	    !avl_is_empty(&mcip->mci_v6_pending_txn)) {
135711878SVenu.Iyer@Sun.COM 		DTRACE_PROBE1(restarting__timer, mac_client_impl_t *, mcip);
135811878SVenu.Iyer@Sun.COM 
135911878SVenu.Iyer@Sun.COM 		mcip->mci_txn_cleanup_tid = timeout(txn_cleanup_timer, mcip,
136011878SVenu.Iyer@Sun.COM 		    drv_usectohz(txn_cleanup_interval * 1000000));
136111878SVenu.Iyer@Sun.COM 	}
136211878SVenu.Iyer@Sun.COM 	mutex_exit(&mcip->mci_protect_lock);
136311878SVenu.Iyer@Sun.COM }
136411878SVenu.Iyer@Sun.COM 
136511878SVenu.Iyer@Sun.COM static void
start_txn_cleanup_timer(mac_client_impl_t * mcip)136611878SVenu.Iyer@Sun.COM start_txn_cleanup_timer(mac_client_impl_t *mcip)
136711878SVenu.Iyer@Sun.COM {
136811878SVenu.Iyer@Sun.COM 	ASSERT(MUTEX_HELD(&mcip->mci_protect_lock));
136911878SVenu.Iyer@Sun.COM 	if (mcip->mci_txn_cleanup_tid == 0) {
137011878SVenu.Iyer@Sun.COM 		mcip->mci_txn_cleanup_tid = timeout(txn_cleanup_timer, mcip,
137111878SVenu.Iyer@Sun.COM 		    drv_usectohz(txn_cleanup_interval * 1000000));
137211878SVenu.Iyer@Sun.COM 	}
137311878SVenu.Iyer@Sun.COM }
137411878SVenu.Iyer@Sun.COM 
137511878SVenu.Iyer@Sun.COM static void
cancel_txn_cleanup_timer(mac_client_impl_t * mcip)137611878SVenu.Iyer@Sun.COM cancel_txn_cleanup_timer(mac_client_impl_t *mcip)
137711878SVenu.Iyer@Sun.COM {
137811878SVenu.Iyer@Sun.COM 	timeout_id_t	tid;
137911878SVenu.Iyer@Sun.COM 
138011878SVenu.Iyer@Sun.COM 	ASSERT(MUTEX_HELD(&mcip->mci_protect_lock));
138111878SVenu.Iyer@Sun.COM 
138211878SVenu.Iyer@Sun.COM 	/*
138311878SVenu.Iyer@Sun.COM 	 * This needs to be a while loop because the timer could get
138411878SVenu.Iyer@Sun.COM 	 * rearmed during untimeout().
138511878SVenu.Iyer@Sun.COM 	 */
138611878SVenu.Iyer@Sun.COM 	while ((tid = mcip->mci_txn_cleanup_tid) != 0) {
138711878SVenu.Iyer@Sun.COM 		mcip->mci_txn_cleanup_tid = 0;
138811878SVenu.Iyer@Sun.COM 		mutex_exit(&mcip->mci_protect_lock);
138911878SVenu.Iyer@Sun.COM 		(void) untimeout(tid);
139011878SVenu.Iyer@Sun.COM 		mutex_enter(&mcip->mci_protect_lock);
139111878SVenu.Iyer@Sun.COM 	}
139211878SVenu.Iyer@Sun.COM }
139311878SVenu.Iyer@Sun.COM 
139411878SVenu.Iyer@Sun.COM /*
139511878SVenu.Iyer@Sun.COM  * Get the start/end pointers of an L3 packet and also do pullup if needed.
139611878SVenu.Iyer@Sun.COM  * pulled-up packet needs to be freed by the caller.
139711878SVenu.Iyer@Sun.COM  */
139811878SVenu.Iyer@Sun.COM static int
get_l3_info(mblk_t * mp,size_t hdrsize,uchar_t ** start,uchar_t ** end,mblk_t ** nmp)139911878SVenu.Iyer@Sun.COM get_l3_info(mblk_t *mp, size_t hdrsize, uchar_t **start, uchar_t **end,
140011878SVenu.Iyer@Sun.COM     mblk_t **nmp)
140111878SVenu.Iyer@Sun.COM {
140211878SVenu.Iyer@Sun.COM 	uchar_t	*s, *e;
140311878SVenu.Iyer@Sun.COM 	mblk_t	*newmp = NULL;
140411878SVenu.Iyer@Sun.COM 
140511878SVenu.Iyer@Sun.COM 	/*
140611878SVenu.Iyer@Sun.COM 	 * Pullup if necessary but reject packets that do not have
140711878SVenu.Iyer@Sun.COM 	 * a proper mac header.
140811878SVenu.Iyer@Sun.COM 	 */
140911878SVenu.Iyer@Sun.COM 	s = mp->b_rptr + hdrsize;
141011878SVenu.Iyer@Sun.COM 	e = mp->b_wptr;
141111878SVenu.Iyer@Sun.COM 
141211878SVenu.Iyer@Sun.COM 	if (s > mp->b_wptr)
141311878SVenu.Iyer@Sun.COM 		return (EINVAL);
141411878SVenu.Iyer@Sun.COM 
141511878SVenu.Iyer@Sun.COM 	if (!OK_32PTR(s) || mp->b_cont != NULL) {
141611878SVenu.Iyer@Sun.COM 		/*
141711878SVenu.Iyer@Sun.COM 		 * Temporarily adjust mp->b_rptr to ensure proper
141811878SVenu.Iyer@Sun.COM 		 * alignment of IP header in newmp.
141911878SVenu.Iyer@Sun.COM 		 */
142011878SVenu.Iyer@Sun.COM 		DTRACE_PROBE1(pullup__needed, mblk_t *, mp);
142111878SVenu.Iyer@Sun.COM 
142211878SVenu.Iyer@Sun.COM 		mp->b_rptr += hdrsize;
142311878SVenu.Iyer@Sun.COM 		newmp = msgpullup(mp, -1);
142411878SVenu.Iyer@Sun.COM 		mp->b_rptr -= hdrsize;
142511878SVenu.Iyer@Sun.COM 
142611878SVenu.Iyer@Sun.COM 		if (newmp == NULL)
142711878SVenu.Iyer@Sun.COM 			return (ENOMEM);
142811878SVenu.Iyer@Sun.COM 
142911878SVenu.Iyer@Sun.COM 		s = newmp->b_rptr;
143011878SVenu.Iyer@Sun.COM 		e = newmp->b_wptr;
143111878SVenu.Iyer@Sun.COM 	}
143211878SVenu.Iyer@Sun.COM 
143311878SVenu.Iyer@Sun.COM 	*start = s;
143411878SVenu.Iyer@Sun.COM 	*end = e;
143511878SVenu.Iyer@Sun.COM 	*nmp = newmp;
143611878SVenu.Iyer@Sun.COM 	return (0);
143711878SVenu.Iyer@Sun.COM }
143811878SVenu.Iyer@Sun.COM 
143911878SVenu.Iyer@Sun.COM void
mac_protect_intercept_dhcp_one(mac_client_impl_t * mcip,mblk_t * mp)144011878SVenu.Iyer@Sun.COM mac_protect_intercept_dhcp_one(mac_client_impl_t *mcip, mblk_t *mp)
144111878SVenu.Iyer@Sun.COM {
144211878SVenu.Iyer@Sun.COM 	mac_impl_t		*mip = mcip->mci_mip;
144311878SVenu.Iyer@Sun.COM 	uchar_t			*start, *end;
144411878SVenu.Iyer@Sun.COM 	mblk_t			*nmp = NULL;
144511878SVenu.Iyer@Sun.COM 	mac_header_info_t	mhi;
144611878SVenu.Iyer@Sun.COM 	int			err;
144711878SVenu.Iyer@Sun.COM 
144811878SVenu.Iyer@Sun.COM 	err = mac_vlan_header_info((mac_handle_t)mip, mp, &mhi);
144911878SVenu.Iyer@Sun.COM 	if (err != 0) {
145011878SVenu.Iyer@Sun.COM 		DTRACE_PROBE2(invalid__header, mac_client_impl_t *, mcip,
145111878SVenu.Iyer@Sun.COM 		    mblk_t *, mp);
145211878SVenu.Iyer@Sun.COM 		return;
145311878SVenu.Iyer@Sun.COM 	}
145411878SVenu.Iyer@Sun.COM 
145511878SVenu.Iyer@Sun.COM 	err = get_l3_info(mp, mhi.mhi_hdrsize, &start, &end, &nmp);
145611878SVenu.Iyer@Sun.COM 	if (err != 0) {
145711878SVenu.Iyer@Sun.COM 		DTRACE_PROBE2(invalid__l3, mac_client_impl_t *, mcip,
145811878SVenu.Iyer@Sun.COM 		    mblk_t *, mp);
145911878SVenu.Iyer@Sun.COM 		return;
146011878SVenu.Iyer@Sun.COM 	}
146111878SVenu.Iyer@Sun.COM 
146211878SVenu.Iyer@Sun.COM 	switch (mhi.mhi_bindsap) {
146311878SVenu.Iyer@Sun.COM 	case ETHERTYPE_IP: {
146411878SVenu.Iyer@Sun.COM 		ipha_t	*ipha = (ipha_t *)start;
146511878SVenu.Iyer@Sun.COM 
146611878SVenu.Iyer@Sun.COM 		if (start + sizeof (ipha_t) > end)
146711878SVenu.Iyer@Sun.COM 			return;
146811878SVenu.Iyer@Sun.COM 
146911878SVenu.Iyer@Sun.COM 		intercept_dhcpv4_inbound(mcip, ipha, end);
147011878SVenu.Iyer@Sun.COM 		break;
147111878SVenu.Iyer@Sun.COM 	}
147211878SVenu.Iyer@Sun.COM 	case ETHERTYPE_IPV6: {
147311878SVenu.Iyer@Sun.COM 		ip6_t		*ip6h = (ip6_t *)start;
147411878SVenu.Iyer@Sun.COM 
147511878SVenu.Iyer@Sun.COM 		if (start + sizeof (ip6_t) > end)
147611878SVenu.Iyer@Sun.COM 			return;
147711878SVenu.Iyer@Sun.COM 
147811878SVenu.Iyer@Sun.COM 		intercept_dhcpv6_inbound(mcip, ip6h, end);
147911878SVenu.Iyer@Sun.COM 		break;
148011878SVenu.Iyer@Sun.COM 	}
148111878SVenu.Iyer@Sun.COM 	}
148211878SVenu.Iyer@Sun.COM 	freemsg(nmp);
148311878SVenu.Iyer@Sun.COM }
148411878SVenu.Iyer@Sun.COM 
148511878SVenu.Iyer@Sun.COM void
mac_protect_intercept_dhcp(mac_client_impl_t * mcip,mblk_t * mp)148611878SVenu.Iyer@Sun.COM mac_protect_intercept_dhcp(mac_client_impl_t *mcip, mblk_t *mp)
148711878SVenu.Iyer@Sun.COM {
148811878SVenu.Iyer@Sun.COM 	/*
148911878SVenu.Iyer@Sun.COM 	 * Skip checks if we are part of an aggr.
149011878SVenu.Iyer@Sun.COM 	 */
149111878SVenu.Iyer@Sun.COM 	if ((mcip->mci_state_flags & MCIS_IS_AGGR_PORT) != 0)
149211878SVenu.Iyer@Sun.COM 		return;
149311878SVenu.Iyer@Sun.COM 
149411878SVenu.Iyer@Sun.COM 	for (; mp != NULL; mp = mp->b_next)
149511878SVenu.Iyer@Sun.COM 		mac_protect_intercept_dhcp_one(mcip, mp);
149611878SVenu.Iyer@Sun.COM }
149711878SVenu.Iyer@Sun.COM 
149811878SVenu.Iyer@Sun.COM void
mac_protect_flush_dhcp(mac_client_impl_t * mcip)149911878SVenu.Iyer@Sun.COM mac_protect_flush_dhcp(mac_client_impl_t *mcip)
150011878SVenu.Iyer@Sun.COM {
150111878SVenu.Iyer@Sun.COM 	mutex_enter(&mcip->mci_protect_lock);
150211878SVenu.Iyer@Sun.COM 	flush_dhcpv4(mcip);
150311878SVenu.Iyer@Sun.COM 	flush_dhcpv6(mcip);
150411878SVenu.Iyer@Sun.COM 	mutex_exit(&mcip->mci_protect_lock);
150511878SVenu.Iyer@Sun.COM }
150611878SVenu.Iyer@Sun.COM 
150711878SVenu.Iyer@Sun.COM void
mac_protect_cancel_timer(mac_client_impl_t * mcip)150811878SVenu.Iyer@Sun.COM mac_protect_cancel_timer(mac_client_impl_t *mcip)
150911878SVenu.Iyer@Sun.COM {
151011878SVenu.Iyer@Sun.COM 	mutex_enter(&mcip->mci_protect_lock);
151111878SVenu.Iyer@Sun.COM 	cancel_txn_cleanup_timer(mcip);
151211878SVenu.Iyer@Sun.COM 	mutex_exit(&mcip->mci_protect_lock);
151311878SVenu.Iyer@Sun.COM }
151411878SVenu.Iyer@Sun.COM 
151511878SVenu.Iyer@Sun.COM /*
151611878SVenu.Iyer@Sun.COM  * Check if addr is in the 'allowed-ips' list.
151711878SVenu.Iyer@Sun.COM  */
151811878SVenu.Iyer@Sun.COM 
151911878SVenu.Iyer@Sun.COM /* ARGSUSED */
152011878SVenu.Iyer@Sun.COM static boolean_t
ipnospoof_check_v4(mac_client_impl_t * mcip,mac_protect_t * protect,ipaddr_t * addr)152111878SVenu.Iyer@Sun.COM ipnospoof_check_v4(mac_client_impl_t *mcip, mac_protect_t *protect,
152211878SVenu.Iyer@Sun.COM     ipaddr_t *addr)
152311878SVenu.Iyer@Sun.COM {
152411878SVenu.Iyer@Sun.COM 	uint_t	i;
152511878SVenu.Iyer@Sun.COM 
152611878SVenu.Iyer@Sun.COM 	/*
152711878SVenu.Iyer@Sun.COM 	 * The unspecified address is allowed.
152811878SVenu.Iyer@Sun.COM 	 */
152911878SVenu.Iyer@Sun.COM 	if (*addr == INADDR_ANY)
153010734SEric Cheng 		return (B_TRUE);
153110734SEric Cheng 
153210734SEric Cheng 	for (i = 0; i < protect->mp_ipaddrcnt; i++) {
153311878SVenu.Iyer@Sun.COM 		mac_ipaddr_t	*v4addr = &protect->mp_ipaddrs[i];
153411878SVenu.Iyer@Sun.COM 
153511878SVenu.Iyer@Sun.COM 		if (v4addr->ip_version == IPV4_VERSION &&
153611878SVenu.Iyer@Sun.COM 		    V4_PART_OF_V6(v4addr->ip_addr) == *addr)
153710734SEric Cheng 			return (B_TRUE);
153810734SEric Cheng 	}
153912748SSowmini.Varadhan@oracle.COM 	return (protect->mp_ipaddrcnt == 0 ?
154012748SSowmini.Varadhan@oracle.COM 	    check_dhcpv4_dyn_ip(mcip, *addr) : B_FALSE);
154111878SVenu.Iyer@Sun.COM }
154211878SVenu.Iyer@Sun.COM 
154311878SVenu.Iyer@Sun.COM static boolean_t
ipnospoof_check_v6(mac_client_impl_t * mcip,mac_protect_t * protect,in6_addr_t * addr)154411878SVenu.Iyer@Sun.COM ipnospoof_check_v6(mac_client_impl_t *mcip, mac_protect_t *protect,
154511878SVenu.Iyer@Sun.COM     in6_addr_t *addr)
154611878SVenu.Iyer@Sun.COM {
154711878SVenu.Iyer@Sun.COM 	uint_t	i;
154811878SVenu.Iyer@Sun.COM 
154911878SVenu.Iyer@Sun.COM 	/*
155011878SVenu.Iyer@Sun.COM 	 * The unspecified address and the v6 link local address are allowed.
155111878SVenu.Iyer@Sun.COM 	 */
155211878SVenu.Iyer@Sun.COM 	if (IN6_IS_ADDR_UNSPECIFIED(addr) ||
155311878SVenu.Iyer@Sun.COM 	    ((mcip->mci_protect_flags & MPT_FLAG_V6_LOCAL_ADDR_SET) != 0 &&
155411878SVenu.Iyer@Sun.COM 	    IN6_ARE_ADDR_EQUAL(&mcip->mci_v6_local_addr, addr)))
155511878SVenu.Iyer@Sun.COM 		return (B_TRUE);
155611878SVenu.Iyer@Sun.COM 
155711878SVenu.Iyer@Sun.COM 
155811878SVenu.Iyer@Sun.COM 	for (i = 0; i < protect->mp_ipaddrcnt; i++) {
155911878SVenu.Iyer@Sun.COM 		mac_ipaddr_t	*v6addr = &protect->mp_ipaddrs[i];
156011878SVenu.Iyer@Sun.COM 
156111878SVenu.Iyer@Sun.COM 		if (v6addr->ip_version == IPV6_VERSION &&
156211878SVenu.Iyer@Sun.COM 		    IN6_ARE_ADDR_EQUAL(&v6addr->ip_addr, addr))
156311878SVenu.Iyer@Sun.COM 			return (B_TRUE);
156411878SVenu.Iyer@Sun.COM 	}
156512748SSowmini.Varadhan@oracle.COM 	return (protect->mp_ipaddrcnt == 0 ?
156612748SSowmini.Varadhan@oracle.COM 	    check_dhcpv6_dyn_ip(mcip, addr) : B_FALSE);
156710734SEric Cheng }
156810734SEric Cheng 
156910734SEric Cheng /*
157011878SVenu.Iyer@Sun.COM  * Checks various fields within an IPv6 NDP packet.
157111878SVenu.Iyer@Sun.COM  */
157211878SVenu.Iyer@Sun.COM static boolean_t
ipnospoof_check_ndp(mac_client_impl_t * mcip,mac_protect_t * protect,ip6_t * ip6h,uchar_t * end)157311878SVenu.Iyer@Sun.COM ipnospoof_check_ndp(mac_client_impl_t *mcip, mac_protect_t *protect,
157411878SVenu.Iyer@Sun.COM     ip6_t *ip6h, uchar_t *end)
157511878SVenu.Iyer@Sun.COM {
157611878SVenu.Iyer@Sun.COM 	icmp6_t			*icmp_nd = (icmp6_t *)&ip6h[1];
157711878SVenu.Iyer@Sun.COM 	int			hdrlen, optlen, opttype, len;
157811878SVenu.Iyer@Sun.COM 	uint_t			addrlen, maclen;
157911878SVenu.Iyer@Sun.COM 	uint8_t			type;
158011878SVenu.Iyer@Sun.COM 	nd_opt_hdr_t		*opt;
158111878SVenu.Iyer@Sun.COM 	struct nd_opt_lla	*lla = NULL;
158211878SVenu.Iyer@Sun.COM 
158311878SVenu.Iyer@Sun.COM 	/*
158411878SVenu.Iyer@Sun.COM 	 * NDP packets do not have extension headers so the ICMPv6 header
158511878SVenu.Iyer@Sun.COM 	 * must immediately follow the IPv6 header.
158611878SVenu.Iyer@Sun.COM 	 */
158711878SVenu.Iyer@Sun.COM 	if (ip6h->ip6_nxt != IPPROTO_ICMPV6)
158811878SVenu.Iyer@Sun.COM 		return (B_TRUE);
158911878SVenu.Iyer@Sun.COM 
159011878SVenu.Iyer@Sun.COM 	/* ICMPv6 header missing */
159111878SVenu.Iyer@Sun.COM 	if ((uchar_t *)&icmp_nd[1] > end)
159211878SVenu.Iyer@Sun.COM 		return (B_FALSE);
159311878SVenu.Iyer@Sun.COM 
159411878SVenu.Iyer@Sun.COM 	len = end - (uchar_t *)icmp_nd;
159511878SVenu.Iyer@Sun.COM 	type = icmp_nd->icmp6_type;
159611878SVenu.Iyer@Sun.COM 
159711878SVenu.Iyer@Sun.COM 	switch (type) {
159811878SVenu.Iyer@Sun.COM 	case ND_ROUTER_SOLICIT:
159911878SVenu.Iyer@Sun.COM 		hdrlen = sizeof (nd_router_solicit_t);
160011878SVenu.Iyer@Sun.COM 		break;
160111878SVenu.Iyer@Sun.COM 	case ND_ROUTER_ADVERT:
160211878SVenu.Iyer@Sun.COM 		hdrlen = sizeof (nd_router_advert_t);
160311878SVenu.Iyer@Sun.COM 		break;
160411878SVenu.Iyer@Sun.COM 	case ND_NEIGHBOR_SOLICIT:
160511878SVenu.Iyer@Sun.COM 		hdrlen = sizeof (nd_neighbor_solicit_t);
160611878SVenu.Iyer@Sun.COM 		break;
160711878SVenu.Iyer@Sun.COM 	case ND_NEIGHBOR_ADVERT:
160811878SVenu.Iyer@Sun.COM 		hdrlen = sizeof (nd_neighbor_advert_t);
160911878SVenu.Iyer@Sun.COM 		break;
161011878SVenu.Iyer@Sun.COM 	case ND_REDIRECT:
161111878SVenu.Iyer@Sun.COM 		hdrlen = sizeof (nd_redirect_t);
161211878SVenu.Iyer@Sun.COM 		break;
161311878SVenu.Iyer@Sun.COM 	default:
161411878SVenu.Iyer@Sun.COM 		return (B_TRUE);
161511878SVenu.Iyer@Sun.COM 	}
161611878SVenu.Iyer@Sun.COM 
161711878SVenu.Iyer@Sun.COM 	if (len < hdrlen)
161811878SVenu.Iyer@Sun.COM 		return (B_FALSE);
161911878SVenu.Iyer@Sun.COM 
162011878SVenu.Iyer@Sun.COM 	/* SLLA option checking is needed for RS/RA/NS */
162111878SVenu.Iyer@Sun.COM 	opttype = ND_OPT_SOURCE_LINKADDR;
162211878SVenu.Iyer@Sun.COM 
162311878SVenu.Iyer@Sun.COM 	switch (type) {
162411878SVenu.Iyer@Sun.COM 	case ND_NEIGHBOR_ADVERT: {
162511878SVenu.Iyer@Sun.COM 		nd_neighbor_advert_t	*na = (nd_neighbor_advert_t *)icmp_nd;
162611878SVenu.Iyer@Sun.COM 
162711878SVenu.Iyer@Sun.COM 		if (!ipnospoof_check_v6(mcip, protect, &na->nd_na_target)) {
162811878SVenu.Iyer@Sun.COM 			DTRACE_PROBE2(ndp__na__fail,
162911878SVenu.Iyer@Sun.COM 			    mac_client_impl_t *, mcip, ip6_t *, ip6h);
163011878SVenu.Iyer@Sun.COM 			return (B_FALSE);
163111878SVenu.Iyer@Sun.COM 		}
163211878SVenu.Iyer@Sun.COM 
163311878SVenu.Iyer@Sun.COM 		/* TLLA option for NA */
163411878SVenu.Iyer@Sun.COM 		opttype = ND_OPT_TARGET_LINKADDR;
163511878SVenu.Iyer@Sun.COM 		break;
163611878SVenu.Iyer@Sun.COM 	}
163711878SVenu.Iyer@Sun.COM 	case ND_REDIRECT: {
163811878SVenu.Iyer@Sun.COM 		/* option checking not needed for RD */
163911878SVenu.Iyer@Sun.COM 		return (B_TRUE);
164011878SVenu.Iyer@Sun.COM 	}
164111878SVenu.Iyer@Sun.COM 	default:
164211878SVenu.Iyer@Sun.COM 		break;
164311878SVenu.Iyer@Sun.COM 	}
164411878SVenu.Iyer@Sun.COM 
164511878SVenu.Iyer@Sun.COM 	if (len == hdrlen) {
164611878SVenu.Iyer@Sun.COM 		/* no options, we're done */
164711878SVenu.Iyer@Sun.COM 		return (B_TRUE);
164811878SVenu.Iyer@Sun.COM 	}
164911878SVenu.Iyer@Sun.COM 	opt = (nd_opt_hdr_t *)((uchar_t *)icmp_nd + hdrlen);
165011878SVenu.Iyer@Sun.COM 	optlen = len - hdrlen;
165111878SVenu.Iyer@Sun.COM 
165211878SVenu.Iyer@Sun.COM 	/* find the option header we need */
165311878SVenu.Iyer@Sun.COM 	while (optlen > sizeof (nd_opt_hdr_t)) {
165411878SVenu.Iyer@Sun.COM 		if (opt->nd_opt_type == opttype) {
165511878SVenu.Iyer@Sun.COM 			lla = (struct nd_opt_lla *)opt;
165611878SVenu.Iyer@Sun.COM 			break;
165711878SVenu.Iyer@Sun.COM 		}
165811878SVenu.Iyer@Sun.COM 		optlen -= 8 * opt->nd_opt_len;
165911878SVenu.Iyer@Sun.COM 		opt = (nd_opt_hdr_t *)
166011878SVenu.Iyer@Sun.COM 		    ((uchar_t *)opt + 8 * opt->nd_opt_len);
166111878SVenu.Iyer@Sun.COM 	}
166211878SVenu.Iyer@Sun.COM 	if (lla == NULL)
166311878SVenu.Iyer@Sun.COM 		return (B_TRUE);
166411878SVenu.Iyer@Sun.COM 
166511878SVenu.Iyer@Sun.COM 	addrlen = lla->nd_opt_lla_len * 8 - sizeof (nd_opt_hdr_t);
166611878SVenu.Iyer@Sun.COM 	maclen = mcip->mci_mip->mi_info.mi_addr_length;
166711878SVenu.Iyer@Sun.COM 
166811878SVenu.Iyer@Sun.COM 	if (addrlen != maclen ||
166911878SVenu.Iyer@Sun.COM 	    bcmp(mcip->mci_unicast->ma_addr,
167011878SVenu.Iyer@Sun.COM 	    lla->nd_opt_lla_hdw_addr, maclen) != 0) {
167111878SVenu.Iyer@Sun.COM 		DTRACE_PROBE2(ndp__lla__fail,
167211878SVenu.Iyer@Sun.COM 		    mac_client_impl_t *, mcip, ip6_t *, ip6h);
167311878SVenu.Iyer@Sun.COM 		return (B_FALSE);
167411878SVenu.Iyer@Sun.COM 	}
167511878SVenu.Iyer@Sun.COM 
167611878SVenu.Iyer@Sun.COM 	DTRACE_PROBE2(ndp__lla__ok, mac_client_impl_t *, mcip, ip6_t *, ip6h);
167711878SVenu.Iyer@Sun.COM 	return (B_TRUE);
167811878SVenu.Iyer@Sun.COM }
167911878SVenu.Iyer@Sun.COM 
168011878SVenu.Iyer@Sun.COM /*
168111878SVenu.Iyer@Sun.COM  * Enforce ip-nospoof protection.
168210734SEric Cheng  */
168310734SEric Cheng static int
ipnospoof_check(mac_client_impl_t * mcip,mac_protect_t * protect,mblk_t * mp,mac_header_info_t * mhip)168410734SEric Cheng ipnospoof_check(mac_client_impl_t *mcip, mac_protect_t *protect,
168510734SEric Cheng     mblk_t *mp, mac_header_info_t *mhip)
168610734SEric Cheng {
168711878SVenu.Iyer@Sun.COM 	size_t		hdrsize = mhip->mhi_hdrsize;
168810734SEric Cheng 	uint32_t	sap = mhip->mhi_bindsap;
168911878SVenu.Iyer@Sun.COM 	uchar_t		*start, *end;
169011878SVenu.Iyer@Sun.COM 	mblk_t		*nmp = NULL;
169111878SVenu.Iyer@Sun.COM 	int		err;
169210734SEric Cheng 
169311878SVenu.Iyer@Sun.COM 	err = get_l3_info(mp, hdrsize, &start, &end, &nmp);
169411878SVenu.Iyer@Sun.COM 	if (err != 0) {
169511878SVenu.Iyer@Sun.COM 		DTRACE_PROBE2(invalid__l3, mac_client_impl_t *, mcip,
169611878SVenu.Iyer@Sun.COM 		    mblk_t *, mp);
169711878SVenu.Iyer@Sun.COM 		return (err);
169810734SEric Cheng 	}
169911878SVenu.Iyer@Sun.COM 	err = EINVAL;
170010734SEric Cheng 
170110734SEric Cheng 	switch (sap) {
170210734SEric Cheng 	case ETHERTYPE_IP: {
170310734SEric Cheng 		ipha_t	*ipha = (ipha_t *)start;
170410734SEric Cheng 
170511878SVenu.Iyer@Sun.COM 		if (start + sizeof (ipha_t) > end)
170610734SEric Cheng 			goto fail;
170710734SEric Cheng 
170811878SVenu.Iyer@Sun.COM 		if (!ipnospoof_check_v4(mcip, protect, &ipha->ipha_src))
170910734SEric Cheng 			goto fail;
171010734SEric Cheng 
171112748SSowmini.Varadhan@oracle.COM 		if (!intercept_dhcpv4_outbound(mcip, ipha, end))
171212748SSowmini.Varadhan@oracle.COM 			goto fail;
171310734SEric Cheng 		break;
171410734SEric Cheng 	}
171510734SEric Cheng 	case ETHERTYPE_ARP: {
171610734SEric Cheng 		arh_t		*arh = (arh_t *)start;
171710734SEric Cheng 		uint32_t	maclen, hlen, plen, arplen;
171810734SEric Cheng 		ipaddr_t	spaddr;
171910734SEric Cheng 		uchar_t		*shaddr;
172010734SEric Cheng 
172111878SVenu.Iyer@Sun.COM 		if (start + sizeof (arh_t) > end)
172210734SEric Cheng 			goto fail;
172310734SEric Cheng 
172410734SEric Cheng 		maclen = mcip->mci_mip->mi_info.mi_addr_length;
172510734SEric Cheng 		hlen = arh->arh_hlen;
172610734SEric Cheng 		plen = arh->arh_plen;
172710734SEric Cheng 		if ((hlen != 0 && hlen != maclen) ||
172810734SEric Cheng 		    plen != sizeof (ipaddr_t))
172910734SEric Cheng 			goto fail;
173010734SEric Cheng 
173110734SEric Cheng 		arplen = sizeof (arh_t) + 2 * hlen + 2 * plen;
173211878SVenu.Iyer@Sun.COM 		if (start + arplen > end)
173310734SEric Cheng 			goto fail;
173410734SEric Cheng 
173510734SEric Cheng 		shaddr = start + sizeof (arh_t);
173610734SEric Cheng 		if (hlen != 0 &&
173710734SEric Cheng 		    bcmp(mcip->mci_unicast->ma_addr, shaddr, maclen) != 0)
173810734SEric Cheng 			goto fail;
173910734SEric Cheng 
174010734SEric Cheng 		bcopy(shaddr + hlen, &spaddr, sizeof (spaddr));
174111878SVenu.Iyer@Sun.COM 		if (!ipnospoof_check_v4(mcip, protect, &spaddr))
174210734SEric Cheng 			goto fail;
174310734SEric Cheng 		break;
174410734SEric Cheng 	}
174511878SVenu.Iyer@Sun.COM 	case ETHERTYPE_IPV6: {
174611878SVenu.Iyer@Sun.COM 		ip6_t		*ip6h = (ip6_t *)start;
174711878SVenu.Iyer@Sun.COM 
174811878SVenu.Iyer@Sun.COM 		if (start + sizeof (ip6_t) > end)
174911878SVenu.Iyer@Sun.COM 			goto fail;
175011878SVenu.Iyer@Sun.COM 
175111878SVenu.Iyer@Sun.COM 		if (!ipnospoof_check_v6(mcip, protect, &ip6h->ip6_src))
175211878SVenu.Iyer@Sun.COM 			goto fail;
175311878SVenu.Iyer@Sun.COM 
175411878SVenu.Iyer@Sun.COM 		if (!ipnospoof_check_ndp(mcip, protect, ip6h, end))
175511878SVenu.Iyer@Sun.COM 			goto fail;
175611878SVenu.Iyer@Sun.COM 
175712748SSowmini.Varadhan@oracle.COM 		if (!intercept_dhcpv6_outbound(mcip, ip6h, end))
175812748SSowmini.Varadhan@oracle.COM 			goto fail;
175910734SEric Cheng 		break;
176010734SEric Cheng 	}
176111878SVenu.Iyer@Sun.COM 	}
176211878SVenu.Iyer@Sun.COM 	freemsg(nmp);
176310734SEric Cheng 	return (0);
176410734SEric Cheng 
176510734SEric Cheng fail:
176611878SVenu.Iyer@Sun.COM 	freemsg(nmp);
176710734SEric Cheng 	return (err);
176810734SEric Cheng }
176910734SEric Cheng 
177011878SVenu.Iyer@Sun.COM static boolean_t
dhcpnospoof_check_cid(mac_protect_t * p,uchar_t * cid,uint_t cidlen)177111878SVenu.Iyer@Sun.COM dhcpnospoof_check_cid(mac_protect_t *p, uchar_t *cid, uint_t cidlen)
177211878SVenu.Iyer@Sun.COM {
177311878SVenu.Iyer@Sun.COM 	int	i;
177411878SVenu.Iyer@Sun.COM 
177511878SVenu.Iyer@Sun.COM 	for (i = 0; i < p->mp_cidcnt; i++) {
177611878SVenu.Iyer@Sun.COM 		mac_dhcpcid_t	*dcid = &p->mp_cids[i];
177711878SVenu.Iyer@Sun.COM 
177811878SVenu.Iyer@Sun.COM 		if (dcid->dc_len == cidlen &&
177911878SVenu.Iyer@Sun.COM 		    bcmp(dcid->dc_id, cid, cidlen) == 0)
178011878SVenu.Iyer@Sun.COM 			return (B_TRUE);
178111878SVenu.Iyer@Sun.COM 	}
178211878SVenu.Iyer@Sun.COM 	return (B_FALSE);
178311878SVenu.Iyer@Sun.COM }
178411878SVenu.Iyer@Sun.COM 
178511878SVenu.Iyer@Sun.COM static boolean_t
dhcpnospoof_check_v4(mac_client_impl_t * mcip,mac_protect_t * p,ipha_t * ipha,uchar_t * end)178611878SVenu.Iyer@Sun.COM dhcpnospoof_check_v4(mac_client_impl_t *mcip, mac_protect_t *p,
178711878SVenu.Iyer@Sun.COM     ipha_t *ipha, uchar_t *end)
178811878SVenu.Iyer@Sun.COM {
178911878SVenu.Iyer@Sun.COM 	struct dhcp	*dh4;
179011878SVenu.Iyer@Sun.COM 	uchar_t		*cid;
179111878SVenu.Iyer@Sun.COM 	uint_t		maclen, cidlen = 0;
179211878SVenu.Iyer@Sun.COM 	uint8_t		optlen;
179311878SVenu.Iyer@Sun.COM 	int		err;
179411878SVenu.Iyer@Sun.COM 
179511878SVenu.Iyer@Sun.COM 	if ((err = get_dhcpv4_info(ipha, end, &dh4)) != 0)
179611878SVenu.Iyer@Sun.COM 		return (err == EINVAL);
179711878SVenu.Iyer@Sun.COM 
179811878SVenu.Iyer@Sun.COM 	maclen = mcip->mci_mip->mi_info.mi_addr_length;
179911878SVenu.Iyer@Sun.COM 	if (dh4->hlen == maclen &&
180011878SVenu.Iyer@Sun.COM 	    bcmp(mcip->mci_unicast->ma_addr, dh4->chaddr, maclen) != 0) {
180111878SVenu.Iyer@Sun.COM 		return (B_FALSE);
180211878SVenu.Iyer@Sun.COM 	}
180311878SVenu.Iyer@Sun.COM 	if (get_dhcpv4_option(dh4, end, CD_CLIENT_ID, &cid, &optlen) == 0)
180411878SVenu.Iyer@Sun.COM 		cidlen = optlen;
180511878SVenu.Iyer@Sun.COM 
180611878SVenu.Iyer@Sun.COM 	if (cidlen == 0)
180711878SVenu.Iyer@Sun.COM 		return (B_TRUE);
180811878SVenu.Iyer@Sun.COM 
180911878SVenu.Iyer@Sun.COM 	if (*cid == ARPHRD_ETHER && cidlen - 1 == maclen &&
181011878SVenu.Iyer@Sun.COM 	    bcmp(mcip->mci_unicast->ma_addr, cid + 1, maclen) == 0)
181111878SVenu.Iyer@Sun.COM 		return (B_TRUE);
181211878SVenu.Iyer@Sun.COM 
181311878SVenu.Iyer@Sun.COM 	return (dhcpnospoof_check_cid(p, cid, cidlen));
181411878SVenu.Iyer@Sun.COM }
181511878SVenu.Iyer@Sun.COM 
181611878SVenu.Iyer@Sun.COM static boolean_t
dhcpnospoof_check_v6(mac_client_impl_t * mcip,mac_protect_t * p,ip6_t * ip6h,uchar_t * end)181711878SVenu.Iyer@Sun.COM dhcpnospoof_check_v6(mac_client_impl_t *mcip, mac_protect_t *p,
181811878SVenu.Iyer@Sun.COM     ip6_t *ip6h, uchar_t *end)
181911878SVenu.Iyer@Sun.COM {
182011878SVenu.Iyer@Sun.COM 	dhcpv6_message_t	*dh6;
182111878SVenu.Iyer@Sun.COM 	dhcpv6_option_t		*d6o;
182211878SVenu.Iyer@Sun.COM 	uint8_t			mtype;
182311878SVenu.Iyer@Sun.COM 	uchar_t			*cid, *lladdr = NULL;
182411878SVenu.Iyer@Sun.COM 	uint_t			cidlen, maclen, addrlen = 0;
182511878SVenu.Iyer@Sun.COM 	uint16_t		cidtype;
182611878SVenu.Iyer@Sun.COM 	int			err;
182711878SVenu.Iyer@Sun.COM 
182811878SVenu.Iyer@Sun.COM 	if ((err = get_dhcpv6_info(ip6h, end, &dh6)) != 0)
182911878SVenu.Iyer@Sun.COM 		return (err == EINVAL);
183011878SVenu.Iyer@Sun.COM 
183111878SVenu.Iyer@Sun.COM 	/*
183211878SVenu.Iyer@Sun.COM 	 * We only check client-generated messages.
183311878SVenu.Iyer@Sun.COM 	 */
183411878SVenu.Iyer@Sun.COM 	mtype = dh6->d6m_msg_type;
183511878SVenu.Iyer@Sun.COM 	if (mtype == DHCPV6_MSG_ADVERTISE || mtype == DHCPV6_MSG_REPLY ||
183611878SVenu.Iyer@Sun.COM 	    mtype == DHCPV6_MSG_RECONFIGURE)
183711878SVenu.Iyer@Sun.COM 		return (B_TRUE);
183811878SVenu.Iyer@Sun.COM 
183911878SVenu.Iyer@Sun.COM 	d6o = get_dhcpv6_option(&dh6[1], end - (uchar_t *)&dh6[1], NULL,
184011878SVenu.Iyer@Sun.COM 	    DHCPV6_OPT_CLIENTID, &cidlen);
184111878SVenu.Iyer@Sun.COM 	if (d6o == NULL || (uchar_t *)d6o + cidlen > end)
184211878SVenu.Iyer@Sun.COM 		return (B_TRUE);
184311878SVenu.Iyer@Sun.COM 
184411878SVenu.Iyer@Sun.COM 	cid = (uchar_t *)&d6o[1];
184511878SVenu.Iyer@Sun.COM 	cidlen -= sizeof (*d6o);
184611878SVenu.Iyer@Sun.COM 	if (cidlen < sizeof (cidtype))
184711878SVenu.Iyer@Sun.COM 		return (B_TRUE);
184811878SVenu.Iyer@Sun.COM 
184911878SVenu.Iyer@Sun.COM 	bcopy(cid, &cidtype, sizeof (cidtype));
185011878SVenu.Iyer@Sun.COM 	cidtype = ntohs(cidtype);
185111878SVenu.Iyer@Sun.COM 	if (cidtype == DHCPV6_DUID_LLT && cidlen >= sizeof (duid_llt_t)) {
185211878SVenu.Iyer@Sun.COM 		lladdr = cid + sizeof (duid_llt_t);
185311878SVenu.Iyer@Sun.COM 		addrlen = cidlen - sizeof (duid_llt_t);
185411878SVenu.Iyer@Sun.COM 	}
185511878SVenu.Iyer@Sun.COM 	if (cidtype == DHCPV6_DUID_LL && cidlen >= sizeof (duid_ll_t)) {
185611878SVenu.Iyer@Sun.COM 		lladdr = cid + sizeof (duid_ll_t);
185711878SVenu.Iyer@Sun.COM 		addrlen = cidlen - sizeof (duid_ll_t);
185811878SVenu.Iyer@Sun.COM 	}
185911878SVenu.Iyer@Sun.COM 	maclen = mcip->mci_mip->mi_info.mi_addr_length;
186011878SVenu.Iyer@Sun.COM 	if (lladdr != NULL && addrlen == maclen &&
186111878SVenu.Iyer@Sun.COM 	    bcmp(mcip->mci_unicast->ma_addr, lladdr, maclen) == 0) {
186211878SVenu.Iyer@Sun.COM 		return (B_TRUE);
186311878SVenu.Iyer@Sun.COM 	}
186411878SVenu.Iyer@Sun.COM 	return (dhcpnospoof_check_cid(p, cid, cidlen));
186511878SVenu.Iyer@Sun.COM }
186611878SVenu.Iyer@Sun.COM 
186711878SVenu.Iyer@Sun.COM /*
186811878SVenu.Iyer@Sun.COM  * Enforce dhcp-nospoof protection.
186911878SVenu.Iyer@Sun.COM  */
187011878SVenu.Iyer@Sun.COM static int
dhcpnospoof_check(mac_client_impl_t * mcip,mac_protect_t * protect,mblk_t * mp,mac_header_info_t * mhip)187111878SVenu.Iyer@Sun.COM dhcpnospoof_check(mac_client_impl_t *mcip, mac_protect_t *protect,
187211878SVenu.Iyer@Sun.COM     mblk_t *mp, mac_header_info_t *mhip)
187311878SVenu.Iyer@Sun.COM {
187411878SVenu.Iyer@Sun.COM 	size_t		hdrsize = mhip->mhi_hdrsize;
187511878SVenu.Iyer@Sun.COM 	uint32_t	sap = mhip->mhi_bindsap;
187611878SVenu.Iyer@Sun.COM 	uchar_t		*start, *end;
187711878SVenu.Iyer@Sun.COM 	mblk_t		*nmp = NULL;
187811878SVenu.Iyer@Sun.COM 	int		err;
187911878SVenu.Iyer@Sun.COM 
188011878SVenu.Iyer@Sun.COM 	err = get_l3_info(mp, hdrsize, &start, &end, &nmp);
188111878SVenu.Iyer@Sun.COM 	if (err != 0) {
188211878SVenu.Iyer@Sun.COM 		DTRACE_PROBE2(invalid__l3, mac_client_impl_t *, mcip,
188311878SVenu.Iyer@Sun.COM 		    mblk_t *, mp);
188411878SVenu.Iyer@Sun.COM 		return (err);
188511878SVenu.Iyer@Sun.COM 	}
188611878SVenu.Iyer@Sun.COM 	err = EINVAL;
188711878SVenu.Iyer@Sun.COM 
188811878SVenu.Iyer@Sun.COM 	switch (sap) {
188911878SVenu.Iyer@Sun.COM 	case ETHERTYPE_IP: {
189011878SVenu.Iyer@Sun.COM 		ipha_t	*ipha = (ipha_t *)start;
189111878SVenu.Iyer@Sun.COM 
189211878SVenu.Iyer@Sun.COM 		if (start + sizeof (ipha_t) > end)
189311878SVenu.Iyer@Sun.COM 			goto fail;
189411878SVenu.Iyer@Sun.COM 
189511878SVenu.Iyer@Sun.COM 		if (!dhcpnospoof_check_v4(mcip, protect, ipha, end))
189611878SVenu.Iyer@Sun.COM 			goto fail;
189711878SVenu.Iyer@Sun.COM 
189811878SVenu.Iyer@Sun.COM 		break;
189911878SVenu.Iyer@Sun.COM 	}
190011878SVenu.Iyer@Sun.COM 	case ETHERTYPE_IPV6: {
190111878SVenu.Iyer@Sun.COM 		ip6_t		*ip6h = (ip6_t *)start;
190211878SVenu.Iyer@Sun.COM 
190311878SVenu.Iyer@Sun.COM 		if (start + sizeof (ip6_t) > end)
190411878SVenu.Iyer@Sun.COM 			goto fail;
190511878SVenu.Iyer@Sun.COM 
190611878SVenu.Iyer@Sun.COM 		if (!dhcpnospoof_check_v6(mcip, protect, ip6h, end))
190711878SVenu.Iyer@Sun.COM 			goto fail;
190811878SVenu.Iyer@Sun.COM 
190911878SVenu.Iyer@Sun.COM 		break;
191011878SVenu.Iyer@Sun.COM 	}
191111878SVenu.Iyer@Sun.COM 	}
191211878SVenu.Iyer@Sun.COM 	freemsg(nmp);
191311878SVenu.Iyer@Sun.COM 	return (0);
191411878SVenu.Iyer@Sun.COM 
191511878SVenu.Iyer@Sun.COM fail:
191611878SVenu.Iyer@Sun.COM 	/* increment dhcpnospoof stat here */
191711878SVenu.Iyer@Sun.COM 	freemsg(nmp);
191811878SVenu.Iyer@Sun.COM 	return (err);
191911878SVenu.Iyer@Sun.COM }
192011878SVenu.Iyer@Sun.COM 
192111878SVenu.Iyer@Sun.COM /*
192211878SVenu.Iyer@Sun.COM  * This needs to be called whenever the mac client's mac address changes.
192311878SVenu.Iyer@Sun.COM  */
192411878SVenu.Iyer@Sun.COM void
mac_protect_update_v6_local_addr(mac_client_impl_t * mcip)192511878SVenu.Iyer@Sun.COM mac_protect_update_v6_local_addr(mac_client_impl_t *mcip)
192611878SVenu.Iyer@Sun.COM {
192711878SVenu.Iyer@Sun.COM 	uint8_t		*p, *macaddr = mcip->mci_unicast->ma_addr;
192811878SVenu.Iyer@Sun.COM 	uint_t		i, media = mcip->mci_mip->mi_info.mi_media;
192911878SVenu.Iyer@Sun.COM 	in6_addr_t	token, *v6addr = &mcip->mci_v6_local_addr;
193011878SVenu.Iyer@Sun.COM 	in6_addr_t	ll_template = {(uint32_t)V6_LINKLOCAL, 0x0, 0x0, 0x0};
193111878SVenu.Iyer@Sun.COM 
193211878SVenu.Iyer@Sun.COM 
193311878SVenu.Iyer@Sun.COM 	bzero(&token, sizeof (token));
193411878SVenu.Iyer@Sun.COM 	p = (uint8_t *)&token.s6_addr32[2];
193511878SVenu.Iyer@Sun.COM 
193611878SVenu.Iyer@Sun.COM 	switch (media) {
193711878SVenu.Iyer@Sun.COM 	case DL_ETHER:
193811878SVenu.Iyer@Sun.COM 		bcopy(macaddr, p, 3);
193911878SVenu.Iyer@Sun.COM 		p[0] ^= 0x2;
194011878SVenu.Iyer@Sun.COM 		p[3] = 0xff;
194111878SVenu.Iyer@Sun.COM 		p[4] = 0xfe;
194211878SVenu.Iyer@Sun.COM 		bcopy(macaddr + 3, p + 5, 3);
194311878SVenu.Iyer@Sun.COM 		break;
194411878SVenu.Iyer@Sun.COM 	case DL_IB:
194511878SVenu.Iyer@Sun.COM 		ASSERT(mcip->mci_mip->mi_info.mi_addr_length == 20);
194611878SVenu.Iyer@Sun.COM 		bcopy(macaddr + 12, p, 8);
194711878SVenu.Iyer@Sun.COM 		p[0] |= 2;
194811878SVenu.Iyer@Sun.COM 		break;
194911878SVenu.Iyer@Sun.COM 	default:
195011878SVenu.Iyer@Sun.COM 		/*
195111878SVenu.Iyer@Sun.COM 		 * We do not need to generate the local address for link types
195211878SVenu.Iyer@Sun.COM 		 * that do not support link protection. Wifi pretends to be
195311878SVenu.Iyer@Sun.COM 		 * ethernet so it is covered by the DL_ETHER case (note the
195411878SVenu.Iyer@Sun.COM 		 * use of mi_media instead of mi_nativemedia).
195511878SVenu.Iyer@Sun.COM 		 */
195611878SVenu.Iyer@Sun.COM 		return;
195711878SVenu.Iyer@Sun.COM 	}
195811878SVenu.Iyer@Sun.COM 
195911878SVenu.Iyer@Sun.COM 	for (i = 0; i < 4; i++) {
196011878SVenu.Iyer@Sun.COM 		v6addr->s6_addr32[i] = token.s6_addr32[i] |
196111878SVenu.Iyer@Sun.COM 		    ll_template.s6_addr32[i];
196211878SVenu.Iyer@Sun.COM 	}
196311878SVenu.Iyer@Sun.COM 	mcip->mci_protect_flags |= MPT_FLAG_V6_LOCAL_ADDR_SET;
196411878SVenu.Iyer@Sun.COM }
196511878SVenu.Iyer@Sun.COM 
196610734SEric Cheng /*
196710734SEric Cheng  * Enforce link protection on one packet.
196810734SEric Cheng  */
196910734SEric Cheng static int
mac_protect_check_one(mac_client_impl_t * mcip,mblk_t * mp)197010734SEric Cheng mac_protect_check_one(mac_client_impl_t *mcip, mblk_t *mp)
197110734SEric Cheng {
197210734SEric Cheng 	mac_impl_t		*mip = mcip->mci_mip;
197310734SEric Cheng 	mac_resource_props_t	*mrp = MCIP_RESOURCE_PROPS(mcip);
197410734SEric Cheng 	mac_protect_t		*protect;
197510734SEric Cheng 	mac_header_info_t	mhi;
197610734SEric Cheng 	uint32_t		types;
197710734SEric Cheng 	int			err;
197810734SEric Cheng 
197910734SEric Cheng 	ASSERT(mp->b_next == NULL);
198010734SEric Cheng 	ASSERT(mrp != NULL);
198110734SEric Cheng 
198210734SEric Cheng 	err = mac_vlan_header_info((mac_handle_t)mip, mp, &mhi);
198310734SEric Cheng 	if (err != 0) {
198410734SEric Cheng 		DTRACE_PROBE2(invalid__header, mac_client_impl_t *, mcip,
198510734SEric Cheng 		    mblk_t *, mp);
198610734SEric Cheng 		return (err);
198710734SEric Cheng 	}
198810734SEric Cheng 	protect = &mrp->mrp_protect;
198910734SEric Cheng 	types = protect->mp_types;
199010734SEric Cheng 
199110734SEric Cheng 	if ((types & MPT_MACNOSPOOF) != 0) {
199210734SEric Cheng 		if (mhi.mhi_saddr != NULL &&
199310734SEric Cheng 		    bcmp(mcip->mci_unicast->ma_addr, mhi.mhi_saddr,
199410734SEric Cheng 		    mip->mi_info.mi_addr_length) != 0) {
199511878SVenu.Iyer@Sun.COM 			BUMP_STAT(mcip, macspoofed);
199610734SEric Cheng 			DTRACE_PROBE2(mac__nospoof__fail,
199710734SEric Cheng 			    mac_client_impl_t *, mcip, mblk_t *, mp);
199810734SEric Cheng 			return (EINVAL);
199910734SEric Cheng 		}
200010734SEric Cheng 	}
200110734SEric Cheng 	if ((types & MPT_RESTRICTED) != 0) {
200210734SEric Cheng 		uint32_t	vid = VLAN_ID(mhi.mhi_tci);
200310734SEric Cheng 		uint32_t	sap = mhi.mhi_bindsap;
200410734SEric Cheng 
200510734SEric Cheng 		/*
200610734SEric Cheng 		 * ETHERTYPE_VLAN packets are allowed through, provided that
200710734SEric Cheng 		 * the vid is not spoofed.
200810734SEric Cheng 		 */
200910734SEric Cheng 		if (vid != 0 && !mac_client_check_flow_vid(mcip, vid)) {
201011878SVenu.Iyer@Sun.COM 			BUMP_STAT(mcip, restricted);
201110734SEric Cheng 			DTRACE_PROBE2(restricted__vid__invalid,
201210734SEric Cheng 			    mac_client_impl_t *, mcip, mblk_t *, mp);
201310734SEric Cheng 			return (EINVAL);
201410734SEric Cheng 		}
201510734SEric Cheng 
201610734SEric Cheng 		if (sap != ETHERTYPE_IP && sap != ETHERTYPE_IPV6 &&
201710734SEric Cheng 		    sap != ETHERTYPE_ARP) {
201811878SVenu.Iyer@Sun.COM 			BUMP_STAT(mcip, restricted);
201910734SEric Cheng 			DTRACE_PROBE2(restricted__fail,
202010734SEric Cheng 			    mac_client_impl_t *, mcip, mblk_t *, mp);
202110734SEric Cheng 			return (EINVAL);
202210734SEric Cheng 		}
202310734SEric Cheng 	}
202410734SEric Cheng 	if ((types & MPT_IPNOSPOOF) != 0) {
202511878SVenu.Iyer@Sun.COM 		if ((err = ipnospoof_check(mcip, protect, mp, &mhi)) != 0) {
202611878SVenu.Iyer@Sun.COM 			BUMP_STAT(mcip, ipspoofed);
202710734SEric Cheng 			DTRACE_PROBE2(ip__nospoof__fail,
202810734SEric Cheng 			    mac_client_impl_t *, mcip, mblk_t *, mp);
202910734SEric Cheng 			return (err);
203010734SEric Cheng 		}
203110734SEric Cheng 	}
203211878SVenu.Iyer@Sun.COM 	if ((types & MPT_DHCPNOSPOOF) != 0) {
203311878SVenu.Iyer@Sun.COM 		if ((err = dhcpnospoof_check(mcip, protect, mp, &mhi)) != 0) {
203411878SVenu.Iyer@Sun.COM 			BUMP_STAT(mcip, dhcpspoofed);
203511878SVenu.Iyer@Sun.COM 			DTRACE_PROBE2(dhcp__nospoof__fail,
203611878SVenu.Iyer@Sun.COM 			    mac_client_impl_t *, mcip, mblk_t *, mp);
203711878SVenu.Iyer@Sun.COM 			return (err);
203811878SVenu.Iyer@Sun.COM 		}
203911878SVenu.Iyer@Sun.COM 	}
204010734SEric Cheng 	return (0);
204110734SEric Cheng }
204210734SEric Cheng 
204310734SEric Cheng /*
204410734SEric Cheng  * Enforce link protection on a packet chain.
204510734SEric Cheng  * Packets that pass the checks are returned back to the caller.
204610734SEric Cheng  */
204710734SEric Cheng mblk_t *
mac_protect_check(mac_client_handle_t mch,mblk_t * mp)204810734SEric Cheng mac_protect_check(mac_client_handle_t mch, mblk_t *mp)
204910734SEric Cheng {
205010734SEric Cheng 	mac_client_impl_t	*mcip = (mac_client_impl_t *)mch;
205110734SEric Cheng 	mblk_t			*ret_mp = NULL, **tailp = &ret_mp, *next;
205210734SEric Cheng 
205310734SEric Cheng 	/*
205410734SEric Cheng 	 * Skip checks if we are part of an aggr.
205510734SEric Cheng 	 */
205610734SEric Cheng 	if ((mcip->mci_state_flags & MCIS_IS_AGGR_PORT) != 0)
205710734SEric Cheng 		return (mp);
205810734SEric Cheng 
205910734SEric Cheng 	for (; mp != NULL; mp = next) {
206010734SEric Cheng 		next = mp->b_next;
206110734SEric Cheng 		mp->b_next = NULL;
206210734SEric Cheng 
206310734SEric Cheng 		if (mac_protect_check_one(mcip, mp) == 0) {
206410734SEric Cheng 			*tailp = mp;
206510734SEric Cheng 			tailp = &mp->b_next;
206610734SEric Cheng 		} else {
206710734SEric Cheng 			freemsg(mp);
206810734SEric Cheng 		}
206910734SEric Cheng 	}
207010734SEric Cheng 	return (ret_mp);
207110734SEric Cheng }
207210734SEric Cheng 
207310734SEric Cheng /*
207410734SEric Cheng  * Check if a particular protection type is enabled.
207510734SEric Cheng  */
207610734SEric Cheng boolean_t
mac_protect_enabled(mac_client_handle_t mch,uint32_t type)207710734SEric Cheng mac_protect_enabled(mac_client_handle_t mch, uint32_t type)
207810734SEric Cheng {
207911878SVenu.Iyer@Sun.COM 	return (MAC_PROTECT_ENABLED((mac_client_impl_t *)mch, type));
208011878SVenu.Iyer@Sun.COM }
208111878SVenu.Iyer@Sun.COM 
208211878SVenu.Iyer@Sun.COM static int
validate_ips(mac_protect_t * p)208311878SVenu.Iyer@Sun.COM validate_ips(mac_protect_t *p)
208411878SVenu.Iyer@Sun.COM {
208511878SVenu.Iyer@Sun.COM 	uint_t		i, j;
208611878SVenu.Iyer@Sun.COM 
208711878SVenu.Iyer@Sun.COM 	if (p->mp_ipaddrcnt == MPT_RESET)
208811878SVenu.Iyer@Sun.COM 		return (0);
208911878SVenu.Iyer@Sun.COM 
209011878SVenu.Iyer@Sun.COM 	if (p->mp_ipaddrcnt > MPT_MAXIPADDR)
209111878SVenu.Iyer@Sun.COM 		return (EINVAL);
209211878SVenu.Iyer@Sun.COM 
209311878SVenu.Iyer@Sun.COM 	for (i = 0; i < p->mp_ipaddrcnt; i++) {
209411878SVenu.Iyer@Sun.COM 		mac_ipaddr_t	*addr = &p->mp_ipaddrs[i];
209511878SVenu.Iyer@Sun.COM 
209611878SVenu.Iyer@Sun.COM 		/*
209711878SVenu.Iyer@Sun.COM 		 * The unspecified address is implicitly allowed
209811878SVenu.Iyer@Sun.COM 		 * so there's no need to add it to the list.
209911878SVenu.Iyer@Sun.COM 		 */
210011878SVenu.Iyer@Sun.COM 		if (addr->ip_version == IPV4_VERSION) {
210111878SVenu.Iyer@Sun.COM 			if (V4_PART_OF_V6(addr->ip_addr) == INADDR_ANY)
210211878SVenu.Iyer@Sun.COM 				return (EINVAL);
210311878SVenu.Iyer@Sun.COM 		} else if (addr->ip_version == IPV6_VERSION) {
210411878SVenu.Iyer@Sun.COM 			if (IN6_IS_ADDR_UNSPECIFIED(&addr->ip_addr))
210511878SVenu.Iyer@Sun.COM 				return (EINVAL);
210611878SVenu.Iyer@Sun.COM 		} else {
210711878SVenu.Iyer@Sun.COM 			/* invalid ip version */
210811878SVenu.Iyer@Sun.COM 			return (EINVAL);
210911878SVenu.Iyer@Sun.COM 		}
211011878SVenu.Iyer@Sun.COM 
211111878SVenu.Iyer@Sun.COM 		for (j = 0; j < p->mp_ipaddrcnt; j++) {
211211878SVenu.Iyer@Sun.COM 			mac_ipaddr_t	*addr1 = &p->mp_ipaddrs[j];
211311878SVenu.Iyer@Sun.COM 
211411878SVenu.Iyer@Sun.COM 			if (i == j || addr->ip_version != addr1->ip_version)
211511878SVenu.Iyer@Sun.COM 				continue;
211610734SEric Cheng 
211711878SVenu.Iyer@Sun.COM 			/* found a duplicate */
211811878SVenu.Iyer@Sun.COM 			if ((addr->ip_version == IPV4_VERSION &&
211911878SVenu.Iyer@Sun.COM 			    V4_PART_OF_V6(addr->ip_addr) ==
212011878SVenu.Iyer@Sun.COM 			    V4_PART_OF_V6(addr1->ip_addr)) ||
212111878SVenu.Iyer@Sun.COM 			    IN6_ARE_ADDR_EQUAL(&addr->ip_addr,
212211878SVenu.Iyer@Sun.COM 			    &addr1->ip_addr))
212311878SVenu.Iyer@Sun.COM 				return (EINVAL);
212411878SVenu.Iyer@Sun.COM 		}
212511878SVenu.Iyer@Sun.COM 	}
212611878SVenu.Iyer@Sun.COM 	return (0);
212711878SVenu.Iyer@Sun.COM }
212811878SVenu.Iyer@Sun.COM 
212911878SVenu.Iyer@Sun.COM /* ARGSUSED */
213011878SVenu.Iyer@Sun.COM static int
validate_cids(mac_protect_t * p)213111878SVenu.Iyer@Sun.COM validate_cids(mac_protect_t *p)
213211878SVenu.Iyer@Sun.COM {
213311878SVenu.Iyer@Sun.COM 	uint_t		i, j;
213411878SVenu.Iyer@Sun.COM 
213511878SVenu.Iyer@Sun.COM 	if (p->mp_cidcnt == MPT_RESET)
213611878SVenu.Iyer@Sun.COM 		return (0);
213711878SVenu.Iyer@Sun.COM 
213811878SVenu.Iyer@Sun.COM 	if (p->mp_cidcnt > MPT_MAXCID)
213911878SVenu.Iyer@Sun.COM 		return (EINVAL);
214011878SVenu.Iyer@Sun.COM 
214111878SVenu.Iyer@Sun.COM 	for (i = 0; i < p->mp_cidcnt; i++) {
214211878SVenu.Iyer@Sun.COM 		mac_dhcpcid_t	*cid = &p->mp_cids[i];
214311878SVenu.Iyer@Sun.COM 
214411878SVenu.Iyer@Sun.COM 		if (cid->dc_len > MPT_MAXCIDLEN ||
214511878SVenu.Iyer@Sun.COM 		    (cid->dc_form != CIDFORM_TYPED &&
214611878SVenu.Iyer@Sun.COM 		    cid->dc_form != CIDFORM_HEX &&
214711878SVenu.Iyer@Sun.COM 		    cid->dc_form != CIDFORM_STR))
214811878SVenu.Iyer@Sun.COM 			return (EINVAL);
214911878SVenu.Iyer@Sun.COM 
215011878SVenu.Iyer@Sun.COM 		for (j = 0; j < p->mp_cidcnt; j++) {
215111878SVenu.Iyer@Sun.COM 			mac_dhcpcid_t	*cid1 = &p->mp_cids[j];
215211878SVenu.Iyer@Sun.COM 
215311878SVenu.Iyer@Sun.COM 			if (i == j || cid->dc_len != cid1->dc_len)
215411878SVenu.Iyer@Sun.COM 				continue;
215511878SVenu.Iyer@Sun.COM 
215611878SVenu.Iyer@Sun.COM 			/* found a duplicate */
215711878SVenu.Iyer@Sun.COM 			if (bcmp(cid->dc_id, cid1->dc_id, cid->dc_len) == 0)
215811878SVenu.Iyer@Sun.COM 				return (EINVAL);
215911878SVenu.Iyer@Sun.COM 		}
216011878SVenu.Iyer@Sun.COM 	}
216111878SVenu.Iyer@Sun.COM 	return (0);
216210734SEric Cheng }
216310734SEric Cheng 
216410734SEric Cheng /*
216510734SEric Cheng  * Sanity-checks parameters given by userland.
216610734SEric Cheng  */
216710734SEric Cheng int
mac_protect_validate(mac_resource_props_t * mrp)216810734SEric Cheng mac_protect_validate(mac_resource_props_t *mrp)
216910734SEric Cheng {
217010734SEric Cheng 	mac_protect_t	*p = &mrp->mrp_protect;
217111878SVenu.Iyer@Sun.COM 	int		err;
217210734SEric Cheng 
217310734SEric Cheng 	/* check for invalid types */
217410734SEric Cheng 	if (p->mp_types != MPT_RESET && (p->mp_types & ~MPT_ALL) != 0)
217510734SEric Cheng 		return (EINVAL);
217610734SEric Cheng 
217711878SVenu.Iyer@Sun.COM 	if ((err = validate_ips(p)) != 0)
217811878SVenu.Iyer@Sun.COM 		return (err);
217910734SEric Cheng 
218011878SVenu.Iyer@Sun.COM 	if ((err = validate_cids(p)) != 0)
218111878SVenu.Iyer@Sun.COM 		return (err);
218210734SEric Cheng 
218310734SEric Cheng 	return (0);
218410734SEric Cheng }
218510734SEric Cheng 
218610734SEric Cheng /*
218710734SEric Cheng  * Enable/disable link protection.
218810734SEric Cheng  */
218910734SEric Cheng int
mac_protect_set(mac_client_handle_t mch,mac_resource_props_t * mrp)219010734SEric Cheng mac_protect_set(mac_client_handle_t mch, mac_resource_props_t *mrp)
219110734SEric Cheng {
219210734SEric Cheng 	mac_client_impl_t	*mcip = (mac_client_impl_t *)mch;
219310734SEric Cheng 	mac_impl_t		*mip = mcip->mci_mip;
219410734SEric Cheng 	uint_t			media = mip->mi_info.mi_nativemedia;
219510734SEric Cheng 	int			err;
219610734SEric Cheng 
219710734SEric Cheng 	ASSERT(MAC_PERIM_HELD((mac_handle_t)mip));
219810734SEric Cheng 
219910734SEric Cheng 	/* tunnels are not supported */
220010734SEric Cheng 	if (media == DL_IPV4 || media == DL_IPV6 || media == DL_6TO4)
220110734SEric Cheng 		return (ENOTSUP);
220210734SEric Cheng 
220310734SEric Cheng 	if ((err = mac_protect_validate(mrp)) != 0)
220410734SEric Cheng 		return (err);
220510734SEric Cheng 
220612748SSowmini.Varadhan@oracle.COM 	if (err != 0)
220712748SSowmini.Varadhan@oracle.COM 		return (err);
220812748SSowmini.Varadhan@oracle.COM 
220910734SEric Cheng 	mac_update_resources(mrp, MCIP_RESOURCE_PROPS(mcip), B_FALSE);
221012748SSowmini.Varadhan@oracle.COM 	i_mac_notify(((mcip->mci_state_flags & MCIS_IS_VNIC) != 0 ?
221112748SSowmini.Varadhan@oracle.COM 	    mcip->mci_upper_mip : mip), MAC_NOTE_ALLOWED_IPS);
221210734SEric Cheng 	return (0);
221310734SEric Cheng }
221410734SEric Cheng 
221510734SEric Cheng void
mac_protect_update(mac_resource_props_t * new,mac_resource_props_t * curr)221610734SEric Cheng mac_protect_update(mac_resource_props_t *new, mac_resource_props_t *curr)
221710734SEric Cheng {
221810734SEric Cheng 	mac_protect_t	*np = &new->mrp_protect;
221910734SEric Cheng 	mac_protect_t	*cp = &curr->mrp_protect;
222010734SEric Cheng 	uint32_t	types = np->mp_types;
222110734SEric Cheng 
222210734SEric Cheng 	if (types == MPT_RESET) {
222310734SEric Cheng 		cp->mp_types = 0;
222410734SEric Cheng 		curr->mrp_mask &= ~MRP_PROTECT;
222510734SEric Cheng 	} else {
222610734SEric Cheng 		if (types != 0) {
222710734SEric Cheng 			cp->mp_types = types;
222810734SEric Cheng 			curr->mrp_mask |= MRP_PROTECT;
222910734SEric Cheng 		}
223010734SEric Cheng 	}
223110734SEric Cheng 	if (np->mp_ipaddrcnt != 0) {
223211878SVenu.Iyer@Sun.COM 		if (np->mp_ipaddrcnt <= MPT_MAXIPADDR) {
223310734SEric Cheng 			bcopy(np->mp_ipaddrs, cp->mp_ipaddrs,
223410734SEric Cheng 			    sizeof (cp->mp_ipaddrs));
223510734SEric Cheng 			cp->mp_ipaddrcnt = np->mp_ipaddrcnt;
223610734SEric Cheng 		} else if (np->mp_ipaddrcnt == MPT_RESET) {
223710734SEric Cheng 			bzero(cp->mp_ipaddrs, sizeof (cp->mp_ipaddrs));
223810734SEric Cheng 			cp->mp_ipaddrcnt = 0;
223910734SEric Cheng 		}
224010734SEric Cheng 	}
224111878SVenu.Iyer@Sun.COM 	if (np->mp_cidcnt != 0) {
224211878SVenu.Iyer@Sun.COM 		if (np->mp_cidcnt <= MPT_MAXCID) {
224311878SVenu.Iyer@Sun.COM 			bcopy(np->mp_cids, cp->mp_cids, sizeof (cp->mp_cids));
224411878SVenu.Iyer@Sun.COM 			cp->mp_cidcnt = np->mp_cidcnt;
224511878SVenu.Iyer@Sun.COM 		} else if (np->mp_cidcnt == MPT_RESET) {
224611878SVenu.Iyer@Sun.COM 			bzero(cp->mp_cids, sizeof (cp->mp_cids));
224711878SVenu.Iyer@Sun.COM 			cp->mp_cidcnt = 0;
224811878SVenu.Iyer@Sun.COM 		}
224911878SVenu.Iyer@Sun.COM 	}
225010734SEric Cheng }
225111878SVenu.Iyer@Sun.COM 
225211878SVenu.Iyer@Sun.COM void
mac_protect_init(mac_client_impl_t * mcip)225311878SVenu.Iyer@Sun.COM mac_protect_init(mac_client_impl_t *mcip)
225411878SVenu.Iyer@Sun.COM {
225511878SVenu.Iyer@Sun.COM 	mutex_init(&mcip->mci_protect_lock, NULL, MUTEX_DRIVER, NULL);
225611878SVenu.Iyer@Sun.COM 	mcip->mci_protect_flags = 0;
225711878SVenu.Iyer@Sun.COM 	mcip->mci_txn_cleanup_tid = 0;
225811878SVenu.Iyer@Sun.COM 	avl_create(&mcip->mci_v4_pending_txn, compare_dhcpv4_xid,
225911878SVenu.Iyer@Sun.COM 	    sizeof (dhcpv4_txn_t), offsetof(dhcpv4_txn_t, dt_node));
226011878SVenu.Iyer@Sun.COM 	avl_create(&mcip->mci_v4_completed_txn, compare_dhcpv4_cid,
226111878SVenu.Iyer@Sun.COM 	    sizeof (dhcpv4_txn_t), offsetof(dhcpv4_txn_t, dt_node));
226211878SVenu.Iyer@Sun.COM 	avl_create(&mcip->mci_v4_dyn_ip, compare_dhcpv4_ip,
226311878SVenu.Iyer@Sun.COM 	    sizeof (dhcpv4_txn_t), offsetof(dhcpv4_txn_t, dt_ipnode));
226411878SVenu.Iyer@Sun.COM 	avl_create(&mcip->mci_v6_pending_txn, compare_dhcpv6_xid,
226511878SVenu.Iyer@Sun.COM 	    sizeof (dhcpv6_txn_t), offsetof(dhcpv6_txn_t, dt_node));
226611878SVenu.Iyer@Sun.COM 	avl_create(&mcip->mci_v6_cid, compare_dhcpv6_cid,
226711878SVenu.Iyer@Sun.COM 	    sizeof (dhcpv6_cid_t), offsetof(dhcpv6_cid_t, dc_node));
226811878SVenu.Iyer@Sun.COM 	avl_create(&mcip->mci_v6_dyn_ip, compare_dhcpv6_ip,
226911878SVenu.Iyer@Sun.COM 	    sizeof (dhcpv6_addr_t), offsetof(dhcpv6_addr_t, da_node));
227011878SVenu.Iyer@Sun.COM }
227111878SVenu.Iyer@Sun.COM 
227211878SVenu.Iyer@Sun.COM void
mac_protect_fini(mac_client_impl_t * mcip)227311878SVenu.Iyer@Sun.COM mac_protect_fini(mac_client_impl_t *mcip)
227411878SVenu.Iyer@Sun.COM {
227511878SVenu.Iyer@Sun.COM 	avl_destroy(&mcip->mci_v6_dyn_ip);
227611878SVenu.Iyer@Sun.COM 	avl_destroy(&mcip->mci_v6_cid);
227711878SVenu.Iyer@Sun.COM 	avl_destroy(&mcip->mci_v6_pending_txn);
227811878SVenu.Iyer@Sun.COM 	avl_destroy(&mcip->mci_v4_dyn_ip);
227911878SVenu.Iyer@Sun.COM 	avl_destroy(&mcip->mci_v4_completed_txn);
228011878SVenu.Iyer@Sun.COM 	avl_destroy(&mcip->mci_v4_pending_txn);
228111878SVenu.Iyer@Sun.COM 	mcip->mci_txn_cleanup_tid = 0;
228211878SVenu.Iyer@Sun.COM 	mcip->mci_protect_flags = 0;
228311878SVenu.Iyer@Sun.COM 	mutex_destroy(&mcip->mci_protect_lock);
228411878SVenu.Iyer@Sun.COM }
228512748SSowmini.Varadhan@oracle.COM 
228612748SSowmini.Varadhan@oracle.COM static boolean_t
allowed_ips_set(mac_resource_props_t * mrp,uint32_t af)228712748SSowmini.Varadhan@oracle.COM allowed_ips_set(mac_resource_props_t *mrp, uint32_t af)
228812748SSowmini.Varadhan@oracle.COM {
228912748SSowmini.Varadhan@oracle.COM 	int i;
229012748SSowmini.Varadhan@oracle.COM 
229112748SSowmini.Varadhan@oracle.COM 	for (i = 0; i < mrp->mrp_protect.mp_ipaddrcnt; i++) {
229212748SSowmini.Varadhan@oracle.COM 		if (mrp->mrp_protect.mp_ipaddrs[i].ip_version == af)
229312748SSowmini.Varadhan@oracle.COM 			return (B_TRUE);
229412748SSowmini.Varadhan@oracle.COM 	}
229512748SSowmini.Varadhan@oracle.COM 	return (B_FALSE);
229612748SSowmini.Varadhan@oracle.COM }
229712748SSowmini.Varadhan@oracle.COM 
2298*12816SSowmini.Varadhan@oracle.COM mac_protect_t *
mac_protect_get(mac_handle_t mh)2299*12816SSowmini.Varadhan@oracle.COM mac_protect_get(mac_handle_t mh)
230012748SSowmini.Varadhan@oracle.COM {
230112748SSowmini.Varadhan@oracle.COM 	mac_impl_t *mip = (mac_impl_t *)mh;
230212748SSowmini.Varadhan@oracle.COM 
2303*12816SSowmini.Varadhan@oracle.COM 	return (&mip->mi_resource_props.mrp_protect);
230412748SSowmini.Varadhan@oracle.COM }
2305