xref: /onnv-gate/usr/src/uts/common/io/vnic/vnic_dev.c (revision 10616:3be00c4a6835)
15084Sjohnlev /*
25084Sjohnlev  * CDDL HEADER START
35084Sjohnlev  *
45084Sjohnlev  * The contents of this file are subject to the terms of the
55084Sjohnlev  * Common Development and Distribution License (the "License").
65084Sjohnlev  * You may not use this file except in compliance with the License.
75084Sjohnlev  *
85084Sjohnlev  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95084Sjohnlev  * or http://www.opensolaris.org/os/licensing.
105084Sjohnlev  * See the License for the specific language governing permissions
115084Sjohnlev  * and limitations under the License.
125084Sjohnlev  *
135084Sjohnlev  * When distributing Covered Code, include this CDDL HEADER in each
145084Sjohnlev  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155084Sjohnlev  * If applicable, add the following below this CDDL HEADER, with the
165084Sjohnlev  * fields enclosed by brackets "[]" replaced with your own identifying
175084Sjohnlev  * information: Portions Copyright [yyyy] [name of copyright owner]
185084Sjohnlev  *
195084Sjohnlev  * CDDL HEADER END
205084Sjohnlev  */
215084Sjohnlev /*
228927SGirish.Moodalbail@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
235084Sjohnlev  * Use is subject to license terms.
245084Sjohnlev  */
255084Sjohnlev 
265084Sjohnlev #include <sys/types.h>
27*10616SSebastien.Roy@Sun.COM #include <sys/cred.h>
285084Sjohnlev #include <sys/sysmacros.h>
295084Sjohnlev #include <sys/conf.h>
305084Sjohnlev #include <sys/cmn_err.h>
315084Sjohnlev #include <sys/list.h>
325084Sjohnlev #include <sys/ksynch.h>
335084Sjohnlev #include <sys/kmem.h>
345084Sjohnlev #include <sys/stream.h>
355084Sjohnlev #include <sys/modctl.h>
365084Sjohnlev #include <sys/ddi.h>
375084Sjohnlev #include <sys/sunddi.h>
385084Sjohnlev #include <sys/atomic.h>
395084Sjohnlev #include <sys/stat.h>
405084Sjohnlev #include <sys/modhash.h>
415084Sjohnlev #include <sys/strsubr.h>
425084Sjohnlev #include <sys/strsun.h>
435084Sjohnlev #include <sys/dlpi.h>
445084Sjohnlev #include <sys/mac.h>
458275SEric Cheng #include <sys/mac_provider.h>
468275SEric Cheng #include <sys/mac_client.h>
478275SEric Cheng #include <sys/mac_client_priv.h>
485084Sjohnlev #include <sys/mac_ether.h>
495895Syz147064 #include <sys/dls.h>
505084Sjohnlev #include <sys/pattr.h>
518275SEric Cheng #include <sys/time.h>
528275SEric Cheng #include <sys/vlan.h>
535084Sjohnlev #include <sys/vnic.h>
545084Sjohnlev #include <sys/vnic_impl.h>
558275SEric Cheng #include <sys/mac_flow_impl.h>
565084Sjohnlev #include <inet/ip_impl.h>
575084Sjohnlev 
588275SEric Cheng /*
598275SEric Cheng  * Note that for best performance, the VNIC is a passthrough design.
608275SEric Cheng  * For each VNIC corresponds a MAC client of the underlying MAC (lower MAC).
618275SEric Cheng  * This MAC client is opened by the VNIC driver at VNIC creation,
628275SEric Cheng  * and closed when the VNIC is deleted.
638275SEric Cheng  * When a MAC client of the VNIC itself opens a VNIC, the MAC layer
648275SEric Cheng  * (upper MAC) detects that the MAC being opened is a VNIC. Instead
658275SEric Cheng  * of allocating a new MAC client, it asks the VNIC driver to return
668275SEric Cheng  * the lower MAC client handle associated with the VNIC, and that handle
678275SEric Cheng  * is returned to the upper MAC client directly. This allows access
688275SEric Cheng  * by upper MAC clients of the VNIC to have direct access to the lower
698275SEric Cheng  * MAC client for the control path and data path.
708275SEric Cheng  *
718275SEric Cheng  * Due to this passthrough, some of the entry points exported by the
728275SEric Cheng  * VNIC driver are never directly invoked. These entry points include
738275SEric Cheng  * vnic_m_start, vnic_m_stop, vnic_m_promisc, vnic_m_multicst, etc.
748275SEric Cheng  */
758275SEric Cheng 
765084Sjohnlev static int vnic_m_start(void *);
775084Sjohnlev static void vnic_m_stop(void *);
785084Sjohnlev static int vnic_m_promisc(void *, boolean_t);
795084Sjohnlev static int vnic_m_multicst(void *, boolean_t, const uint8_t *);
805084Sjohnlev static int vnic_m_unicst(void *, const uint8_t *);
815084Sjohnlev static int vnic_m_stat(void *, uint_t, uint64_t *);
828275SEric Cheng static void vnic_m_ioctl(void *, queue_t *, mblk_t *);
838927SGirish.Moodalbail@Sun.COM static int vnic_m_setprop(void *, const char *, mac_prop_id_t, uint_t,
848927SGirish.Moodalbail@Sun.COM     const void *);
858927SGirish.Moodalbail@Sun.COM static int vnic_m_getprop(void *, const char *, mac_prop_id_t, uint_t,
868927SGirish.Moodalbail@Sun.COM     uint_t, void *, uint_t *);
875084Sjohnlev static mblk_t *vnic_m_tx(void *, mblk_t *);
885084Sjohnlev static boolean_t vnic_m_capab_get(void *, mac_capab_t, void *);
895084Sjohnlev static void vnic_notify_cb(void *, mac_notify_type_t);
905084Sjohnlev 
915084Sjohnlev static kmem_cache_t	*vnic_cache;
925084Sjohnlev static krwlock_t	vnic_lock;
935084Sjohnlev static uint_t		vnic_count;
945084Sjohnlev 
959514SGirish.Moodalbail@Sun.COM #define	ANCHOR_VNIC_MIN_MTU	576
969514SGirish.Moodalbail@Sun.COM #define	ANCHOR_VNIC_MAX_MTU	9000
979514SGirish.Moodalbail@Sun.COM 
985084Sjohnlev /* hash of VNICs (vnic_t's), keyed by VNIC id */
995084Sjohnlev static mod_hash_t	*vnic_hash;
1005084Sjohnlev #define	VNIC_HASHSZ	64
1015084Sjohnlev #define	VNIC_HASH_KEY(vnic_id)	((mod_hash_key_t)(uintptr_t)vnic_id)
1025084Sjohnlev 
1038927SGirish.Moodalbail@Sun.COM #define	VNIC_M_CALLBACK_FLAGS	\
1048927SGirish.Moodalbail@Sun.COM 	(MC_IOCTL | MC_GETCAPAB | MC_SETPROP | MC_GETPROP)
1055084Sjohnlev 
1065084Sjohnlev static mac_callbacks_t vnic_m_callbacks = {
1075084Sjohnlev 	VNIC_M_CALLBACK_FLAGS,
1085084Sjohnlev 	vnic_m_stat,
1095084Sjohnlev 	vnic_m_start,
1105084Sjohnlev 	vnic_m_stop,
1115084Sjohnlev 	vnic_m_promisc,
1125084Sjohnlev 	vnic_m_multicst,
1135084Sjohnlev 	vnic_m_unicst,
1145084Sjohnlev 	vnic_m_tx,
1158275SEric Cheng 	vnic_m_ioctl,
1168927SGirish.Moodalbail@Sun.COM 	vnic_m_capab_get,
1178927SGirish.Moodalbail@Sun.COM 	NULL,
1188927SGirish.Moodalbail@Sun.COM 	NULL,
1198927SGirish.Moodalbail@Sun.COM 	vnic_m_setprop,
1208927SGirish.Moodalbail@Sun.COM 	vnic_m_getprop
1215084Sjohnlev };
1225084Sjohnlev 
1235084Sjohnlev void
1245084Sjohnlev vnic_dev_init(void)
1255084Sjohnlev {
1265084Sjohnlev 	vnic_cache = kmem_cache_create("vnic_cache",
1275084Sjohnlev 	    sizeof (vnic_t), 0, NULL, NULL, NULL, NULL, NULL, 0);
1285084Sjohnlev 
1295084Sjohnlev 	vnic_hash = mod_hash_create_idhash("vnic_hash",
1305084Sjohnlev 	    VNIC_HASHSZ, mod_hash_null_valdtor);
1315084Sjohnlev 
1325084Sjohnlev 	rw_init(&vnic_lock, NULL, RW_DEFAULT, NULL);
1335084Sjohnlev 
1345084Sjohnlev 	vnic_count = 0;
1355084Sjohnlev }
1365084Sjohnlev 
1375084Sjohnlev void
1385084Sjohnlev vnic_dev_fini(void)
1395084Sjohnlev {
1405084Sjohnlev 	ASSERT(vnic_count == 0);
1415084Sjohnlev 
1425084Sjohnlev 	rw_destroy(&vnic_lock);
1435084Sjohnlev 	mod_hash_destroy_idhash(vnic_hash);
1445084Sjohnlev 	kmem_cache_destroy(vnic_cache);
1455084Sjohnlev }
1465084Sjohnlev 
1475084Sjohnlev uint_t
1485084Sjohnlev vnic_dev_count(void)
1495084Sjohnlev {
1505084Sjohnlev 	return (vnic_count);
1515084Sjohnlev }
1525084Sjohnlev 
1538275SEric Cheng static vnic_ioc_diag_t
1548275SEric Cheng vnic_mac2vnic_diag(mac_diag_t diag)
1555084Sjohnlev {
1568275SEric Cheng 	switch (diag) {
1578275SEric Cheng 	case MAC_DIAG_MACADDR_NIC:
1588275SEric Cheng 		return (VNIC_IOC_DIAG_MACADDR_NIC);
1598275SEric Cheng 	case MAC_DIAG_MACADDR_INUSE:
1608275SEric Cheng 		return (VNIC_IOC_DIAG_MACADDR_INUSE);
1618275SEric Cheng 	case MAC_DIAG_MACADDR_INVALID:
1628275SEric Cheng 		return (VNIC_IOC_DIAG_MACADDR_INVALID);
1638275SEric Cheng 	case MAC_DIAG_MACADDRLEN_INVALID:
1648275SEric Cheng 		return (VNIC_IOC_DIAG_MACADDRLEN_INVALID);
1658275SEric Cheng 	case MAC_DIAG_MACFACTORYSLOTINVALID:
1668275SEric Cheng 		return (VNIC_IOC_DIAG_MACFACTORYSLOTINVALID);
1678275SEric Cheng 	case MAC_DIAG_MACFACTORYSLOTUSED:
1688275SEric Cheng 		return (VNIC_IOC_DIAG_MACFACTORYSLOTUSED);
1698275SEric Cheng 	case MAC_DIAG_MACFACTORYSLOTALLUSED:
1708275SEric Cheng 		return (VNIC_IOC_DIAG_MACFACTORYSLOTALLUSED);
1718275SEric Cheng 	case MAC_DIAG_MACFACTORYNOTSUP:
1728275SEric Cheng 		return (VNIC_IOC_DIAG_MACFACTORYNOTSUP);
1738275SEric Cheng 	case MAC_DIAG_MACPREFIX_INVALID:
1748275SEric Cheng 		return (VNIC_IOC_DIAG_MACPREFIX_INVALID);
1758275SEric Cheng 	case MAC_DIAG_MACPREFIXLEN_INVALID:
1768275SEric Cheng 		return (VNIC_IOC_DIAG_MACPREFIXLEN_INVALID);
1778275SEric Cheng 	case MAC_DIAG_MACNO_HWRINGS:
1788275SEric Cheng 		return (VNIC_IOC_DIAG_NO_HWRINGS);
1798275SEric Cheng 	default:
1808275SEric Cheng 		return (VNIC_IOC_DIAG_NONE);
1815084Sjohnlev 	}
1825084Sjohnlev }
1835084Sjohnlev 
1845084Sjohnlev static int
1858275SEric Cheng vnic_unicast_add(vnic_t *vnic, vnic_mac_addr_type_t vnic_addr_type,
1868275SEric Cheng     int *addr_slot, uint_t prefix_len, int *addr_len_ptr_arg,
1878275SEric Cheng     uint8_t *mac_addr_arg, uint16_t flags, vnic_ioc_diag_t *diag,
1888275SEric Cheng     uint16_t vid)
1895084Sjohnlev {
1908275SEric Cheng 	mac_diag_t mac_diag;
1918275SEric Cheng 	uint16_t mac_flags = 0;
1928275SEric Cheng 	int err;
1938275SEric Cheng 	uint_t addr_len;
1945084Sjohnlev 
1958275SEric Cheng 	if (flags & VNIC_IOC_CREATE_NODUPCHECK)
1968275SEric Cheng 		mac_flags |= MAC_UNICAST_NODUPCHECK;
1975084Sjohnlev 
1988275SEric Cheng 	switch (vnic_addr_type) {
1998275SEric Cheng 	case VNIC_MAC_ADDR_TYPE_FIXED:
2008275SEric Cheng 		/*
2018275SEric Cheng 		 * The MAC address value to assign to the VNIC
2028275SEric Cheng 		 * is already provided in mac_addr_arg. addr_len_ptr_arg
2038275SEric Cheng 		 * already contains the MAC address length.
2048275SEric Cheng 		 */
2058275SEric Cheng 		break;
2065084Sjohnlev 
2078275SEric Cheng 	case VNIC_MAC_ADDR_TYPE_RANDOM:
2088275SEric Cheng 		/*
2098275SEric Cheng 		 * Random MAC address. There are two sub-cases:
2108275SEric Cheng 		 *
2118275SEric Cheng 		 * 1 - If mac_len == 0, a new MAC address is generated.
2128275SEric Cheng 		 *	The length of the MAC address to generated depends
2138275SEric Cheng 		 *	on the type of MAC used. The prefix to use for the MAC
2148275SEric Cheng 		 *	address is stored in the most significant bytes
2158275SEric Cheng 		 *	of the mac_addr argument, and its length is specified
2168275SEric Cheng 		 *	by the mac_prefix_len argument. This prefix can
2178275SEric Cheng 		 *	correspond to a IEEE OUI in the case of Ethernet,
2188275SEric Cheng 		 *	for example.
2198275SEric Cheng 		 *
2208275SEric Cheng 		 * 2 - If mac_len > 0, the address was already picked
2218275SEric Cheng 		 *	randomly, and is now passed back during VNIC
2228275SEric Cheng 		 *	re-creation. The mac_addr argument contains the MAC
2238275SEric Cheng 		 *	address that was generated. We distinguish this
2248275SEric Cheng 		 *	case from the fixed MAC address case, since we
2258275SEric Cheng 		 *	want the user consumers to know, when they query
2268275SEric Cheng 		 *	the list of VNICs, that a VNIC was assigned a
2278275SEric Cheng 		 *	random MAC address vs assigned a fixed address
2288275SEric Cheng 		 *	specified by the user.
2298275SEric Cheng 		 */
2305084Sjohnlev 
2318275SEric Cheng 		/*
2328275SEric Cheng 		 * If it's a pre-generated address, we're done. mac_addr_arg
2338275SEric Cheng 		 * and addr_len_ptr_arg already contain the MAC address
2348275SEric Cheng 		 * value and length.
2358275SEric Cheng 		 */
2368275SEric Cheng 		if (*addr_len_ptr_arg > 0)
2378275SEric Cheng 			break;
2385084Sjohnlev 
2398275SEric Cheng 		/* generate a new random MAC address */
2408275SEric Cheng 		if ((err = mac_addr_random(vnic->vn_mch,
2418275SEric Cheng 		    prefix_len, mac_addr_arg, &mac_diag)) != 0) {
2428275SEric Cheng 			*diag = vnic_mac2vnic_diag(mac_diag);
2438275SEric Cheng 			return (err);
2448275SEric Cheng 		}
2458275SEric Cheng 		*addr_len_ptr_arg = mac_addr_len(vnic->vn_lower_mh);
2468275SEric Cheng 		break;
2478275SEric Cheng 
2488275SEric Cheng 	case VNIC_MAC_ADDR_TYPE_FACTORY:
2498275SEric Cheng 		err = mac_addr_factory_reserve(vnic->vn_mch, addr_slot);
2508275SEric Cheng 		if (err != 0) {
2518275SEric Cheng 			if (err == EINVAL)
2528275SEric Cheng 				*diag = VNIC_IOC_DIAG_MACFACTORYSLOTINVALID;
2538275SEric Cheng 			if (err == EBUSY)
2548275SEric Cheng 				*diag = VNIC_IOC_DIAG_MACFACTORYSLOTUSED;
2558275SEric Cheng 			if (err == ENOSPC)
2568275SEric Cheng 				*diag = VNIC_IOC_DIAG_MACFACTORYSLOTALLUSED;
2578275SEric Cheng 			return (err);
2585084Sjohnlev 		}
2595084Sjohnlev 
2608275SEric Cheng 		mac_addr_factory_value(vnic->vn_lower_mh, *addr_slot,
2618275SEric Cheng 		    mac_addr_arg, &addr_len, NULL, NULL);
2628275SEric Cheng 		*addr_len_ptr_arg = addr_len;
2638275SEric Cheng 		break;
2645084Sjohnlev 
2658275SEric Cheng 	case VNIC_MAC_ADDR_TYPE_AUTO:
2668275SEric Cheng 		/* first try to allocate a factory MAC address */
2678275SEric Cheng 		err = mac_addr_factory_reserve(vnic->vn_mch, addr_slot);
2688275SEric Cheng 		if (err == 0) {
2698275SEric Cheng 			mac_addr_factory_value(vnic->vn_lower_mh, *addr_slot,
2708275SEric Cheng 			    mac_addr_arg, &addr_len, NULL, NULL);
2718275SEric Cheng 			vnic_addr_type = VNIC_MAC_ADDR_TYPE_FACTORY;
2728275SEric Cheng 			*addr_len_ptr_arg = addr_len;
2738275SEric Cheng 			break;
2745084Sjohnlev 		}
2755084Sjohnlev 
2765084Sjohnlev 		/*
2778275SEric Cheng 		 * Allocating a factory MAC address failed, generate a
2788275SEric Cheng 		 * random MAC address instead.
2795084Sjohnlev 		 */
2808275SEric Cheng 		if ((err = mac_addr_random(vnic->vn_mch,
2818275SEric Cheng 		    prefix_len, mac_addr_arg, &mac_diag)) != 0) {
2828275SEric Cheng 			*diag = vnic_mac2vnic_diag(mac_diag);
2838275SEric Cheng 			return (err);
2845084Sjohnlev 		}
2858275SEric Cheng 		*addr_len_ptr_arg = mac_addr_len(vnic->vn_lower_mh);
2868275SEric Cheng 		vnic_addr_type = VNIC_MAC_ADDR_TYPE_RANDOM;
2878275SEric Cheng 		break;
2888275SEric Cheng 	case VNIC_MAC_ADDR_TYPE_PRIMARY:
2898275SEric Cheng 		/*
2908275SEric Cheng 		 * We get the address here since we copy it in the
2918275SEric Cheng 		 * vnic's vn_addr.
2928275SEric Cheng 		 */
2938275SEric Cheng 		mac_unicast_primary_get(vnic->vn_lower_mh, mac_addr_arg);
2948275SEric Cheng 		*addr_len_ptr_arg = mac_addr_len(vnic->vn_lower_mh);
2958275SEric Cheng 		mac_flags |= MAC_UNICAST_VNIC_PRIMARY;
2968275SEric Cheng 		break;
2975084Sjohnlev 	}
2985084Sjohnlev 
2998275SEric Cheng 	vnic->vn_addr_type = vnic_addr_type;
3005084Sjohnlev 
3018275SEric Cheng 	err = mac_unicast_add(vnic->vn_mch, mac_addr_arg, mac_flags,
3028275SEric Cheng 	    &vnic->vn_muh, vid, &mac_diag);
3038275SEric Cheng 	if (err != 0) {
3048275SEric Cheng 		if (vnic_addr_type == VNIC_MAC_ADDR_TYPE_FACTORY) {
3058275SEric Cheng 			/* release factory MAC address */
3068275SEric Cheng 			mac_addr_factory_release(vnic->vn_mch, *addr_slot);
3075084Sjohnlev 		}
3088275SEric Cheng 		*diag = vnic_mac2vnic_diag(mac_diag);
3095084Sjohnlev 	}
3105084Sjohnlev 
3115084Sjohnlev 	return (err);
3125084Sjohnlev }
3135084Sjohnlev 
3145084Sjohnlev /*
3155084Sjohnlev  * Create a new VNIC upon request from administrator.
3165084Sjohnlev  * Returns 0 on success, an errno on failure.
3175084Sjohnlev  */
3188275SEric Cheng /* ARGSUSED */
3195084Sjohnlev int
3208275SEric Cheng vnic_dev_create(datalink_id_t vnic_id, datalink_id_t linkid,
3218275SEric Cheng     vnic_mac_addr_type_t *vnic_addr_type, int *mac_len, uchar_t *mac_addr,
3228275SEric Cheng     int *mac_slot, uint_t mac_prefix_len, uint16_t vid,
323*10616SSebastien.Roy@Sun.COM     mac_resource_props_t *mrp, uint32_t flags, vnic_ioc_diag_t *diag,
324*10616SSebastien.Roy@Sun.COM     cred_t *credp)
3255084Sjohnlev {
3268275SEric Cheng 	vnic_t *vnic;
3275084Sjohnlev 	mac_register_t *mac;
3285084Sjohnlev 	int err;
3298275SEric Cheng 	boolean_t is_anchor = ((flags & VNIC_IOC_CREATE_ANCHOR) != 0);
3308275SEric Cheng 	char vnic_name[MAXNAMELEN];
3318275SEric Cheng 	const mac_info_t *minfop;
3328275SEric Cheng 	uint32_t req_hwgrp_flag = ((flags & VNIC_IOC_CREATE_REQ_HWRINGS) != 0) ?
3338275SEric Cheng 	    MAC_OPEN_FLAGS_REQ_HWRINGS : 0;
3345084Sjohnlev 
3358275SEric Cheng 	*diag = VNIC_IOC_DIAG_NONE;
3365084Sjohnlev 
3375084Sjohnlev 	rw_enter(&vnic_lock, RW_WRITER);
3385084Sjohnlev 
3395084Sjohnlev 	/* does a VNIC with the same id already exist? */
3405084Sjohnlev 	err = mod_hash_find(vnic_hash, VNIC_HASH_KEY(vnic_id),
3415084Sjohnlev 	    (mod_hash_val_t *)&vnic);
3425084Sjohnlev 	if (err == 0) {
3435084Sjohnlev 		rw_exit(&vnic_lock);
3445084Sjohnlev 		return (EEXIST);
3455084Sjohnlev 	}
3465084Sjohnlev 
3475084Sjohnlev 	vnic = kmem_cache_alloc(vnic_cache, KM_NOSLEEP);
3485084Sjohnlev 	if (vnic == NULL) {
3495084Sjohnlev 		rw_exit(&vnic_lock);
3505084Sjohnlev 		return (ENOMEM);
3515084Sjohnlev 	}
3525084Sjohnlev 
3538275SEric Cheng 	bzero(vnic, sizeof (*vnic));
3545084Sjohnlev 
3555084Sjohnlev 	vnic->vn_id = vnic_id;
3568275SEric Cheng 	vnic->vn_link_id = linkid;
3578275SEric Cheng 
3588275SEric Cheng 	if (!is_anchor) {
3598275SEric Cheng 		if (linkid == DATALINK_INVALID_LINKID) {
3608275SEric Cheng 			err = EINVAL;
3618275SEric Cheng 			goto bail;
3628275SEric Cheng 		}
3638275SEric Cheng 
3648275SEric Cheng 		/*
3658275SEric Cheng 		 * Open the lower MAC and assign its initial bandwidth and
3668275SEric Cheng 		 * MAC address. We do this here during VNIC creation and
3678275SEric Cheng 		 * do not wait until the upper MAC client open so that we
3688275SEric Cheng 		 * can validate the VNIC creation parameters (bandwidth,
3698275SEric Cheng 		 * MAC address, etc) and reserve a factory MAC address if
3708275SEric Cheng 		 * one was requested.
3718275SEric Cheng 		 */
3728275SEric Cheng 		err = mac_open_by_linkid(linkid, &vnic->vn_lower_mh);
3738275SEric Cheng 		if (err != 0)
3748275SEric Cheng 			goto bail;
3758275SEric Cheng 
3768275SEric Cheng 		/*
3778275SEric Cheng 		 * VNIC(vlan) over VNICs(vlans) is not supported.
3788275SEric Cheng 		 */
3798275SEric Cheng 		if (mac_is_vnic(vnic->vn_lower_mh)) {
3808275SEric Cheng 			err = EINVAL;
3818275SEric Cheng 			goto bail;
3828275SEric Cheng 		}
3838275SEric Cheng 
3848275SEric Cheng 		/* only ethernet support for now */
3858275SEric Cheng 		minfop = mac_info(vnic->vn_lower_mh);
3868275SEric Cheng 		if (minfop->mi_nativemedia != DL_ETHER) {
3878275SEric Cheng 			err = ENOTSUP;
3888275SEric Cheng 			goto bail;
3898275SEric Cheng 		}
3905084Sjohnlev 
3918275SEric Cheng 		(void) dls_mgmt_get_linkinfo(vnic_id, vnic_name, NULL, NULL,
3928275SEric Cheng 		    NULL);
3938275SEric Cheng 		err = mac_client_open(vnic->vn_lower_mh, &vnic->vn_mch,
3948275SEric Cheng 		    vnic_name, MAC_OPEN_FLAGS_IS_VNIC | req_hwgrp_flag);
3958275SEric Cheng 		if (err != 0)
3968275SEric Cheng 			goto bail;
3978275SEric Cheng 
3988275SEric Cheng 		if (mrp != NULL) {
3998275SEric Cheng 			err = mac_client_set_resources(vnic->vn_mch, mrp);
4008275SEric Cheng 			if (err != 0)
4018275SEric Cheng 				goto bail;
4028275SEric Cheng 		}
4038275SEric Cheng 		/* assign a MAC address to the VNIC */
4045084Sjohnlev 
4058275SEric Cheng 		err = vnic_unicast_add(vnic, *vnic_addr_type, mac_slot,
4068275SEric Cheng 		    mac_prefix_len, mac_len, mac_addr, flags, diag, vid);
4078275SEric Cheng 		if (err != 0) {
4088275SEric Cheng 			vnic->vn_muh = NULL;
4098275SEric Cheng 			if (diag != NULL && req_hwgrp_flag != 0)
4108275SEric Cheng 				*diag = VNIC_IOC_DIAG_NO_HWRINGS;
4118275SEric Cheng 			goto bail;
4128275SEric Cheng 		}
4138275SEric Cheng 
4148275SEric Cheng 		/* register to receive notification from underlying MAC */
4158275SEric Cheng 		vnic->vn_mnh = mac_notify_add(vnic->vn_lower_mh, vnic_notify_cb,
4168275SEric Cheng 		    vnic);
4178275SEric Cheng 
4188275SEric Cheng 		*vnic_addr_type = vnic->vn_addr_type;
4198275SEric Cheng 		vnic->vn_addr_len = *mac_len;
4208275SEric Cheng 		vnic->vn_vid = vid;
4218275SEric Cheng 
4228275SEric Cheng 		bcopy(mac_addr, vnic->vn_addr, vnic->vn_addr_len);
4238275SEric Cheng 
4248275SEric Cheng 		if (vnic->vn_addr_type == VNIC_MAC_ADDR_TYPE_FACTORY)
4258275SEric Cheng 			vnic->vn_slot_id = *mac_slot;
4268275SEric Cheng 
4278275SEric Cheng 		/* set the initial VNIC capabilities */
4288275SEric Cheng 		if (!mac_capab_get(vnic->vn_lower_mh, MAC_CAPAB_HCKSUM,
4298275SEric Cheng 		    &vnic->vn_hcksum_txflags))
4308275SEric Cheng 			vnic->vn_hcksum_txflags = 0;
4318275SEric Cheng 	}
4325084Sjohnlev 
4335084Sjohnlev 	/* register with the MAC module */
4345084Sjohnlev 	if ((mac = mac_alloc(MAC_VERSION)) == NULL)
4355084Sjohnlev 		goto bail;
4365084Sjohnlev 
4375084Sjohnlev 	mac->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
4385084Sjohnlev 	mac->m_driver = vnic;
4395084Sjohnlev 	mac->m_dip = vnic_get_dip();
4405895Syz147064 	mac->m_instance = (uint_t)-1;
4415084Sjohnlev 	mac->m_src_addr = vnic->vn_addr;
4425084Sjohnlev 	mac->m_callbacks = &vnic_m_callbacks;
4435084Sjohnlev 
4448275SEric Cheng 	if (!is_anchor) {
4458275SEric Cheng 		/*
4468275SEric Cheng 		 * If this is a VNIC based VLAN, then we check for the
4478275SEric Cheng 		 * margin unless it has been created with the force
4488275SEric Cheng 		 * flag. If we are configuring a VLAN over an etherstub,
4498275SEric Cheng 		 * we don't check the margin even if force is not set.
4508275SEric Cheng 		 */
4518275SEric Cheng 		if (vid == 0 || (flags & VNIC_IOC_CREATE_FORCE) != 0) {
4528275SEric Cheng 			if (vid != VLAN_ID_NONE)
4538275SEric Cheng 				vnic->vn_force = B_TRUE;
4548275SEric Cheng 			/*
4558275SEric Cheng 			 * As the current margin size of the underlying mac is
4568275SEric Cheng 			 * used to determine the margin size of the VNIC
4578275SEric Cheng 			 * itself, request the underlying mac not to change
4588275SEric Cheng 			 * to a smaller margin size.
4598275SEric Cheng 			 */
4608275SEric Cheng 			err = mac_margin_add(vnic->vn_lower_mh,
4618275SEric Cheng 			    &vnic->vn_margin, B_TRUE);
4628275SEric Cheng 			ASSERT(err == 0);
4638275SEric Cheng 		} else {
4648275SEric Cheng 			vnic->vn_margin = VLAN_TAGSZ;
4658275SEric Cheng 			err = mac_margin_add(vnic->vn_lower_mh,
4668275SEric Cheng 			    &vnic->vn_margin, B_FALSE);
4678275SEric Cheng 			if (err != 0) {
4688275SEric Cheng 				mac_free(mac);
4698275SEric Cheng 				if (diag != NULL)
4708275SEric Cheng 					*diag = VNIC_IOC_DIAG_MACMARGIN_INVALID;
4718275SEric Cheng 				goto bail;
4728275SEric Cheng 			}
4738275SEric Cheng 		}
4745084Sjohnlev 
4758275SEric Cheng 		mac_sdu_get(vnic->vn_lower_mh, &mac->m_min_sdu,
4768275SEric Cheng 		    &mac->m_max_sdu);
4778275SEric Cheng 	} else {
4788275SEric Cheng 		vnic->vn_margin = VLAN_TAGSZ;
4799514SGirish.Moodalbail@Sun.COM 		mac->m_min_sdu = ANCHOR_VNIC_MIN_MTU;
4809514SGirish.Moodalbail@Sun.COM 		mac->m_max_sdu = ANCHOR_VNIC_MAX_MTU;
4818275SEric Cheng 	}
4828275SEric Cheng 
4835895Syz147064 	mac->m_margin = vnic->vn_margin;
4848275SEric Cheng 
4855084Sjohnlev 	err = mac_register(mac, &vnic->vn_mh);
4865084Sjohnlev 	mac_free(mac);
4875895Syz147064 	if (err != 0) {
4888275SEric Cheng 		VERIFY(is_anchor || mac_margin_remove(vnic->vn_lower_mh,
4895895Syz147064 		    vnic->vn_margin) == 0);
4905084Sjohnlev 		goto bail;
4915895Syz147064 	}
4925895Syz147064 
4938275SEric Cheng 	/* Set the VNIC's MAC in the client */
4948275SEric Cheng 	if (!is_anchor)
4958275SEric Cheng 		mac_set_upper_mac(vnic->vn_mch, vnic->vn_mh);
4968275SEric Cheng 
497*10616SSebastien.Roy@Sun.COM 	err = dls_devnet_create(vnic->vn_mh, vnic->vn_id, crgetzoneid(credp));
498*10616SSebastien.Roy@Sun.COM 	if (err != 0) {
4998275SEric Cheng 		VERIFY(is_anchor || mac_margin_remove(vnic->vn_lower_mh,
5005895Syz147064 		    vnic->vn_margin) == 0);
5015895Syz147064 		(void) mac_unregister(vnic->vn_mh);
5025895Syz147064 		goto bail;
5035895Syz147064 	}
5045084Sjohnlev 
5055084Sjohnlev 	/* add new VNIC to hash table */
5065084Sjohnlev 	err = mod_hash_insert(vnic_hash, VNIC_HASH_KEY(vnic_id),
5075084Sjohnlev 	    (mod_hash_val_t)vnic);
5085084Sjohnlev 	ASSERT(err == 0);
5095084Sjohnlev 	vnic_count++;
5105084Sjohnlev 
5119052SEric Cheng 	vnic->vn_enabled = B_TRUE;
5125084Sjohnlev 	rw_exit(&vnic_lock);
5135084Sjohnlev 
5145084Sjohnlev 	return (0);
5155084Sjohnlev 
5165084Sjohnlev bail:
5175084Sjohnlev 	rw_exit(&vnic_lock);
5188275SEric Cheng 	if (!is_anchor) {
5198275SEric Cheng 		if (vnic->vn_mnh != NULL)
5208275SEric Cheng 			(void) mac_notify_remove(vnic->vn_mnh, B_TRUE);
5218275SEric Cheng 		if (vnic->vn_muh != NULL)
5228275SEric Cheng 			(void) mac_unicast_remove(vnic->vn_mch, vnic->vn_muh);
5238275SEric Cheng 		if (vnic->vn_mch != NULL)
5248275SEric Cheng 			mac_client_close(vnic->vn_mch, MAC_CLOSE_FLAGS_IS_VNIC);
5258275SEric Cheng 		if (vnic->vn_lower_mh != NULL)
5268275SEric Cheng 			mac_close(vnic->vn_lower_mh);
5275084Sjohnlev 	}
5285084Sjohnlev 
5298275SEric Cheng 	kmem_cache_free(vnic_cache, vnic);
5305084Sjohnlev 	return (err);
5315084Sjohnlev }
5325084Sjohnlev 
5335084Sjohnlev /*
5345084Sjohnlev  * Modify the properties of an existing VNIC.
5355084Sjohnlev  */
5365084Sjohnlev /* ARGSUSED */
5375084Sjohnlev int
5385895Syz147064 vnic_dev_modify(datalink_id_t vnic_id, uint_t modify_mask,
5398275SEric Cheng     vnic_mac_addr_type_t mac_addr_type, uint_t mac_len, uchar_t *mac_addr,
5408275SEric Cheng     uint_t mac_slot, mac_resource_props_t *mrp)
5415084Sjohnlev {
5425084Sjohnlev 	vnic_t *vnic = NULL;
5435084Sjohnlev 
5445084Sjohnlev 	rw_enter(&vnic_lock, RW_WRITER);
5455084Sjohnlev 
5465084Sjohnlev 	if (mod_hash_find(vnic_hash, VNIC_HASH_KEY(vnic_id),
5475084Sjohnlev 	    (mod_hash_val_t *)&vnic) != 0) {
5485084Sjohnlev 		rw_exit(&vnic_lock);
5495084Sjohnlev 		return (ENOENT);
5505084Sjohnlev 	}
5515084Sjohnlev 
5525084Sjohnlev 	rw_exit(&vnic_lock);
5535084Sjohnlev 
5548275SEric Cheng 	return (0);
5555084Sjohnlev }
5565084Sjohnlev 
5578275SEric Cheng /* ARGSUSED */
5585084Sjohnlev int
559*10616SSebastien.Roy@Sun.COM vnic_dev_delete(datalink_id_t vnic_id, uint32_t flags, cred_t *credp)
5605084Sjohnlev {
5615084Sjohnlev 	vnic_t *vnic = NULL;
5625084Sjohnlev 	mod_hash_val_t val;
5635895Syz147064 	datalink_id_t tmpid;
5645084Sjohnlev 	int rc;
5655084Sjohnlev 
5665084Sjohnlev 	rw_enter(&vnic_lock, RW_WRITER);
5675084Sjohnlev 
5685084Sjohnlev 	if (mod_hash_find(vnic_hash, VNIC_HASH_KEY(vnic_id),
5695084Sjohnlev 	    (mod_hash_val_t *)&vnic) != 0) {
5705084Sjohnlev 		rw_exit(&vnic_lock);
5715084Sjohnlev 		return (ENOENT);
5725084Sjohnlev 	}
5735084Sjohnlev 
5748275SEric Cheng 	if ((rc = dls_devnet_destroy(vnic->vn_mh, &tmpid, B_TRUE)) != 0) {
5755895Syz147064 		rw_exit(&vnic_lock);
5765895Syz147064 		return (rc);
5775895Syz147064 	}
5785895Syz147064 
5795895Syz147064 	ASSERT(vnic_id == tmpid);
5805895Syz147064 
5815084Sjohnlev 	/*
5825084Sjohnlev 	 * We cannot unregister the MAC yet. Unregistering would
5835084Sjohnlev 	 * free up mac_impl_t which should not happen at this time.
5848275SEric Cheng 	 * So disable mac_impl_t by calling mac_disable(). This will prevent
5858275SEric Cheng 	 * any new claims on mac_impl_t.
5865084Sjohnlev 	 */
5878275SEric Cheng 	if ((rc = mac_disable(vnic->vn_mh)) != 0) {
588*10616SSebastien.Roy@Sun.COM 		(void) dls_devnet_create(vnic->vn_mh, vnic_id,
589*10616SSebastien.Roy@Sun.COM 		    crgetzoneid(credp));
5905084Sjohnlev 		rw_exit(&vnic_lock);
5918275SEric Cheng 		return (rc);
5925084Sjohnlev 	}
5935084Sjohnlev 
5949052SEric Cheng 	vnic->vn_enabled = B_FALSE;
5955084Sjohnlev 	(void) mod_hash_remove(vnic_hash, VNIC_HASH_KEY(vnic_id), &val);
5965084Sjohnlev 	ASSERT(vnic == (vnic_t *)val);
5978275SEric Cheng 	vnic_count--;
5988275SEric Cheng 	rw_exit(&vnic_lock);
5995084Sjohnlev 
6008275SEric Cheng 	/*
6018275SEric Cheng 	 * XXX-nicolas shouldn't have a void cast here, if it's
6028275SEric Cheng 	 * expected that the function will never fail, then we should
6038275SEric Cheng 	 * have an ASSERT().
6048275SEric Cheng 	 */
6058275SEric Cheng 	(void) mac_unregister(vnic->vn_mh);
6065084Sjohnlev 
6078275SEric Cheng 	if (vnic->vn_lower_mh != NULL) {
6085084Sjohnlev 		/*
6098275SEric Cheng 		 * Check if MAC address for the vnic was obtained from the
6108275SEric Cheng 		 * factory MAC addresses. If yes, release it.
6115084Sjohnlev 		 */
6128275SEric Cheng 		if (vnic->vn_addr_type == VNIC_MAC_ADDR_TYPE_FACTORY) {
6138275SEric Cheng 			(void) mac_addr_factory_release(vnic->vn_mch,
6148275SEric Cheng 			    vnic->vn_slot_id);
6158275SEric Cheng 		}
6168275SEric Cheng 		(void) mac_margin_remove(vnic->vn_lower_mh, vnic->vn_margin);
6178275SEric Cheng 		(void) mac_notify_remove(vnic->vn_mnh, B_TRUE);
6188275SEric Cheng 		(void) mac_unicast_remove(vnic->vn_mch, vnic->vn_muh);
6198275SEric Cheng 		mac_client_close(vnic->vn_mch, MAC_CLOSE_FLAGS_IS_VNIC);
6208275SEric Cheng 		mac_close(vnic->vn_lower_mh);
6215084Sjohnlev 	}
6225084Sjohnlev 
6235084Sjohnlev 	kmem_cache_free(vnic_cache, vnic);
6245084Sjohnlev 	return (0);
6255084Sjohnlev }
6265084Sjohnlev 
6278275SEric Cheng /* ARGSUSED */
6288275SEric Cheng mblk_t *
6298275SEric Cheng vnic_m_tx(void *arg, mblk_t *mp_chain)
6305084Sjohnlev {
6318275SEric Cheng 	/*
6328275SEric Cheng 	 * This function could be invoked for an anchor VNIC when sending
6338275SEric Cheng 	 * broadcast and multicast packets, and unicast packets which did
6348275SEric Cheng 	 * not match any local known destination.
6358275SEric Cheng 	 */
6368275SEric Cheng 	freemsgchain(mp_chain);
6378275SEric Cheng 	return (NULL);
6385084Sjohnlev }
6395084Sjohnlev 
6408275SEric Cheng /*ARGSUSED*/
6415084Sjohnlev static void
6428275SEric Cheng vnic_m_ioctl(void *arg, queue_t *q, mblk_t *mp)
6435084Sjohnlev {
6448275SEric Cheng 	miocnak(q, mp, 0, ENOTSUP);
6455084Sjohnlev }
6465084Sjohnlev 
6475084Sjohnlev /*
6488275SEric Cheng  * This entry point cannot be passed-through, since it is invoked
6498275SEric Cheng  * for the per-VNIC kstats which must be exported independently
6508275SEric Cheng  * of the existence of VNIC MAC clients.
6515084Sjohnlev  */
6525084Sjohnlev static int
6535084Sjohnlev vnic_m_stat(void *arg, uint_t stat, uint64_t *val)
6545084Sjohnlev {
6555084Sjohnlev 	vnic_t *vnic = arg;
6565084Sjohnlev 	int rval = 0;
6575084Sjohnlev 
6588275SEric Cheng 	if (vnic->vn_lower_mh == NULL) {
6598275SEric Cheng 		/*
6608275SEric Cheng 		 * It's an anchor VNIC, which does not have any
6618275SEric Cheng 		 * statistics in itself.
6628275SEric Cheng 		 */
6638275SEric Cheng 		return (ENOTSUP);
6648275SEric Cheng 	}
6658275SEric Cheng 
6668275SEric Cheng 	/*
6678275SEric Cheng 	 * ENOTSUP must be reported for unsupported stats, the VNIC
6688275SEric Cheng 	 * driver reports a subset of the stats that would
6698275SEric Cheng 	 * be returned by a real piece of hardware.
6708275SEric Cheng 	 */
6715084Sjohnlev 
6725084Sjohnlev 	switch (stat) {
6738275SEric Cheng 	case MAC_STAT_LINK_STATE:
6748275SEric Cheng 	case MAC_STAT_LINK_UP:
6758275SEric Cheng 	case MAC_STAT_PROMISC:
6765084Sjohnlev 	case MAC_STAT_IFSPEED:
6775084Sjohnlev 	case MAC_STAT_MULTIRCV:
6788275SEric Cheng 	case MAC_STAT_MULTIXMT:
6795084Sjohnlev 	case MAC_STAT_BRDCSTRCV:
6805084Sjohnlev 	case MAC_STAT_BRDCSTXMT:
6818275SEric Cheng 	case MAC_STAT_OPACKETS:
6828275SEric Cheng 	case MAC_STAT_OBYTES:
6835084Sjohnlev 	case MAC_STAT_IERRORS:
6845084Sjohnlev 	case MAC_STAT_OERRORS:
6855084Sjohnlev 	case MAC_STAT_RBYTES:
6865084Sjohnlev 	case MAC_STAT_IPACKETS:
6878275SEric Cheng 		*val = mac_client_stat_get(vnic->vn_mch, stat);
6885084Sjohnlev 		break;
6895084Sjohnlev 	default:
6905084Sjohnlev 		rval = ENOTSUP;
6915084Sjohnlev 	}
6925084Sjohnlev 
6935084Sjohnlev 	return (rval);
6945084Sjohnlev }
6955084Sjohnlev 
6965084Sjohnlev /*
6978275SEric Cheng  * Invoked by the upper MAC to retrieve the lower MAC client handle
6988275SEric Cheng  * corresponding to a VNIC. A pointer to this function is obtained
6998275SEric Cheng  * by the upper MAC via capability query.
7008275SEric Cheng  *
7018275SEric Cheng  * XXX-nicolas Note: this currently causes all VNIC MAC clients to
7028275SEric Cheng  * receive the same MAC client handle for the same VNIC. This is ok
7038275SEric Cheng  * as long as we have only one VNIC MAC client which sends and
7048275SEric Cheng  * receives data, but we don't currently enforce this at the MAC layer.
7058275SEric Cheng  */
7068275SEric Cheng static void *
7078275SEric Cheng vnic_mac_client_handle(void *vnic_arg)
7088275SEric Cheng {
7098275SEric Cheng 	vnic_t *vnic = vnic_arg;
7108275SEric Cheng 
7118275SEric Cheng 	return (vnic->vn_mch);
7128275SEric Cheng }
7138275SEric Cheng 
7148275SEric Cheng 
7158275SEric Cheng /*
7165084Sjohnlev  * Return information about the specified capability.
7175084Sjohnlev  */
7185084Sjohnlev /* ARGSUSED */
7195084Sjohnlev static boolean_t
7205084Sjohnlev vnic_m_capab_get(void *arg, mac_capab_t cap, void *cap_data)
7215084Sjohnlev {
7225084Sjohnlev 	vnic_t *vnic = arg;
7235084Sjohnlev 
7245084Sjohnlev 	switch (cap) {
7255084Sjohnlev 	case MAC_CAPAB_HCKSUM: {
7265084Sjohnlev 		uint32_t *hcksum_txflags = cap_data;
7275084Sjohnlev 
7285084Sjohnlev 		*hcksum_txflags = vnic->vn_hcksum_txflags &
7295084Sjohnlev 		    (HCKSUM_INET_FULL_V4 | HCKSUM_IPHDRCKSUM |
7305084Sjohnlev 		    HCKSUM_INET_PARTIAL);
7315084Sjohnlev 		break;
7325084Sjohnlev 	}
7338275SEric Cheng 	case MAC_CAPAB_VNIC: {
7348275SEric Cheng 		mac_capab_vnic_t *vnic_capab = cap_data;
7358275SEric Cheng 
7368275SEric Cheng 		if (vnic->vn_lower_mh == NULL) {
7378275SEric Cheng 			/*
7388275SEric Cheng 			 * It's an anchor VNIC, we don't have an underlying
7398275SEric Cheng 			 * NIC and MAC client handle.
7408275SEric Cheng 			 */
7418275SEric Cheng 			return (B_FALSE);
7428275SEric Cheng 		}
7438275SEric Cheng 
7448275SEric Cheng 		if (vnic_capab != NULL) {
7458275SEric Cheng 			vnic_capab->mcv_arg = vnic;
7468275SEric Cheng 			vnic_capab->mcv_mac_client_handle =
7478275SEric Cheng 			    vnic_mac_client_handle;
7488275SEric Cheng 		}
7498275SEric Cheng 		break;
7508275SEric Cheng 	}
7518275SEric Cheng 	case MAC_CAPAB_ANCHOR_VNIC: {
7528275SEric Cheng 		/* since it's an anchor VNIC we don't have lower mac handle */
7538275SEric Cheng 		if (vnic->vn_lower_mh == NULL) {
7548275SEric Cheng 			ASSERT(vnic->vn_link_id == 0);
7558275SEric Cheng 			return (B_TRUE);
7568275SEric Cheng 		}
7578275SEric Cheng 		return (B_FALSE);
7588275SEric Cheng 	}
7598275SEric Cheng 	case MAC_CAPAB_NO_NATIVEVLAN:
7608275SEric Cheng 	case MAC_CAPAB_NO_ZCOPY:
7618275SEric Cheng 		return (B_TRUE);
7625084Sjohnlev 	default:
7635084Sjohnlev 		return (B_FALSE);
7645084Sjohnlev 	}
7655084Sjohnlev 	return (B_TRUE);
7665084Sjohnlev }
7675084Sjohnlev 
7688275SEric Cheng /* ARGSUSED */
7695084Sjohnlev static int
7705084Sjohnlev vnic_m_start(void *arg)
7715084Sjohnlev {
7725084Sjohnlev 	return (0);
7735084Sjohnlev }
7745084Sjohnlev 
7758275SEric Cheng /* ARGSUSED */
7765084Sjohnlev static void
7775084Sjohnlev vnic_m_stop(void *arg)
7785084Sjohnlev {
7795084Sjohnlev }
7805084Sjohnlev 
7815084Sjohnlev /* ARGSUSED */
7825084Sjohnlev static int
7835084Sjohnlev vnic_m_promisc(void *arg, boolean_t on)
7845084Sjohnlev {
7858275SEric Cheng 	return (0);
7865084Sjohnlev }
7875084Sjohnlev 
7888275SEric Cheng /* ARGSUSED */
7895084Sjohnlev static int
7905084Sjohnlev vnic_m_multicst(void *arg, boolean_t add, const uint8_t *addrp)
7915084Sjohnlev {
7928275SEric Cheng 	return (0);
7935084Sjohnlev }
7945084Sjohnlev 
7955084Sjohnlev static int
7968275SEric Cheng vnic_m_unicst(void *arg, const uint8_t *macaddr)
7975084Sjohnlev {
7985084Sjohnlev 	vnic_t *vnic = arg;
7995084Sjohnlev 
8008275SEric Cheng 	return (mac_vnic_unicast_set(vnic->vn_mch, macaddr));
8015084Sjohnlev }
8025084Sjohnlev 
8038927SGirish.Moodalbail@Sun.COM /*
8048927SGirish.Moodalbail@Sun.COM  * Callback functions for set/get of properties
8058927SGirish.Moodalbail@Sun.COM  */
8068927SGirish.Moodalbail@Sun.COM /*ARGSUSED*/
8078927SGirish.Moodalbail@Sun.COM static int
8088927SGirish.Moodalbail@Sun.COM vnic_m_setprop(void *m_driver, const char *pr_name, mac_prop_id_t pr_num,
8098927SGirish.Moodalbail@Sun.COM     uint_t pr_valsize, const void *pr_val)
8108927SGirish.Moodalbail@Sun.COM {
8118927SGirish.Moodalbail@Sun.COM 	int 		err = ENOTSUP;
8128927SGirish.Moodalbail@Sun.COM 	vnic_t		*vn = m_driver;
8138927SGirish.Moodalbail@Sun.COM 
8148927SGirish.Moodalbail@Sun.COM 	/* allow setting MTU only on an etherstub */
8158927SGirish.Moodalbail@Sun.COM 	if (vn->vn_link_id != DATALINK_INVALID_LINKID)
8168927SGirish.Moodalbail@Sun.COM 		return (err);
8178927SGirish.Moodalbail@Sun.COM 
8188927SGirish.Moodalbail@Sun.COM 	switch (pr_num) {
8198927SGirish.Moodalbail@Sun.COM 	case MAC_PROP_MTU: {
8208927SGirish.Moodalbail@Sun.COM 		uint32_t	mtu;
8218927SGirish.Moodalbail@Sun.COM 
8228927SGirish.Moodalbail@Sun.COM 		if (pr_valsize < sizeof (mtu)) {
8238927SGirish.Moodalbail@Sun.COM 			err = EINVAL;
8248927SGirish.Moodalbail@Sun.COM 			break;
8258927SGirish.Moodalbail@Sun.COM 		}
8268927SGirish.Moodalbail@Sun.COM 		bcopy(pr_val, &mtu, sizeof (mtu));
8279514SGirish.Moodalbail@Sun.COM 		if (mtu < ANCHOR_VNIC_MIN_MTU || mtu > ANCHOR_VNIC_MAX_MTU) {
8289514SGirish.Moodalbail@Sun.COM 			err = EINVAL;
8299514SGirish.Moodalbail@Sun.COM 			break;
8309514SGirish.Moodalbail@Sun.COM 		}
8318927SGirish.Moodalbail@Sun.COM 		err = mac_maxsdu_update(vn->vn_mh, mtu);
8328927SGirish.Moodalbail@Sun.COM 		break;
8338927SGirish.Moodalbail@Sun.COM 	}
8348927SGirish.Moodalbail@Sun.COM 	default:
8358927SGirish.Moodalbail@Sun.COM 		break;
8368927SGirish.Moodalbail@Sun.COM 	}
8378927SGirish.Moodalbail@Sun.COM 	return (err);
8388927SGirish.Moodalbail@Sun.COM }
8398927SGirish.Moodalbail@Sun.COM 
8408927SGirish.Moodalbail@Sun.COM /*ARGSUSED*/
8418927SGirish.Moodalbail@Sun.COM static int
8428927SGirish.Moodalbail@Sun.COM vnic_m_getprop(void *m_driver, const char *pr_name, mac_prop_id_t pr_num,
8438927SGirish.Moodalbail@Sun.COM     uint_t pr_flags, uint_t pr_valsize, void *pr_val, uint_t *perm)
8448927SGirish.Moodalbail@Sun.COM {
8459514SGirish.Moodalbail@Sun.COM 	mac_propval_range_t 	range;
8469514SGirish.Moodalbail@Sun.COM 	vnic_t			*vn = m_driver;
8479514SGirish.Moodalbail@Sun.COM 	int 			err = ENOTSUP;
8489514SGirish.Moodalbail@Sun.COM 
8499514SGirish.Moodalbail@Sun.COM 	/* MTU setting allowed only on an etherstub */
8509514SGirish.Moodalbail@Sun.COM 	if (vn->vn_link_id != DATALINK_INVALID_LINKID)
8519514SGirish.Moodalbail@Sun.COM 		return (err);
8529514SGirish.Moodalbail@Sun.COM 
8539514SGirish.Moodalbail@Sun.COM 	switch (pr_num) {
8549514SGirish.Moodalbail@Sun.COM 	case MAC_PROP_MTU:
8559514SGirish.Moodalbail@Sun.COM 		if (!(pr_flags & MAC_PROP_POSSIBLE))
8569514SGirish.Moodalbail@Sun.COM 			return (ENOTSUP);
8579514SGirish.Moodalbail@Sun.COM 		if (pr_valsize < sizeof (mac_propval_range_t))
8589514SGirish.Moodalbail@Sun.COM 			return (EINVAL);
8599514SGirish.Moodalbail@Sun.COM 		range.mpr_count = 1;
8609514SGirish.Moodalbail@Sun.COM 		range.mpr_type = MAC_PROPVAL_UINT32;
8619514SGirish.Moodalbail@Sun.COM 		range.range_uint32[0].mpur_min = ANCHOR_VNIC_MIN_MTU;
8629514SGirish.Moodalbail@Sun.COM 		range.range_uint32[0].mpur_max = ANCHOR_VNIC_MAX_MTU;
8639514SGirish.Moodalbail@Sun.COM 		bcopy(&range, pr_val, sizeof (range));
8649514SGirish.Moodalbail@Sun.COM 		return (0);
8659514SGirish.Moodalbail@Sun.COM 	default:
8669514SGirish.Moodalbail@Sun.COM 		break;
8679514SGirish.Moodalbail@Sun.COM 	}
8689514SGirish.Moodalbail@Sun.COM 
8699514SGirish.Moodalbail@Sun.COM 	return (err);
8708927SGirish.Moodalbail@Sun.COM }
8718927SGirish.Moodalbail@Sun.COM 
8725084Sjohnlev int
873*10616SSebastien.Roy@Sun.COM vnic_info(vnic_info_t *info, cred_t *credp)
8745084Sjohnlev {
8758275SEric Cheng 	vnic_t		*vnic;
8768275SEric Cheng 	int		err;
8775084Sjohnlev 
878*10616SSebastien.Roy@Sun.COM 	/* Make sure that the VNIC link is visible from the caller's zone. */
879*10616SSebastien.Roy@Sun.COM 	if (!dls_devnet_islinkvisible(info->vn_vnic_id, crgetzoneid(credp)))
880*10616SSebastien.Roy@Sun.COM 		return (ENOENT);
881*10616SSebastien.Roy@Sun.COM 
8828275SEric Cheng 	rw_enter(&vnic_lock, RW_WRITER);
8835084Sjohnlev 
8848275SEric Cheng 	err = mod_hash_find(vnic_hash, VNIC_HASH_KEY(info->vn_vnic_id),
8858275SEric Cheng 	    (mod_hash_val_t *)&vnic);
8868275SEric Cheng 	if (err != 0) {
8878275SEric Cheng 		rw_exit(&vnic_lock);
8888275SEric Cheng 		return (ENOENT);
8895895Syz147064 	}
8905084Sjohnlev 
8918275SEric Cheng 	info->vn_link_id = vnic->vn_link_id;
8928275SEric Cheng 	info->vn_mac_addr_type = vnic->vn_addr_type;
8938275SEric Cheng 	info->vn_mac_len = vnic->vn_addr_len;
8948275SEric Cheng 	bcopy(vnic->vn_addr, info->vn_mac_addr, MAXMACADDRLEN);
8958275SEric Cheng 	info->vn_mac_slot = vnic->vn_slot_id;
8968275SEric Cheng 	info->vn_mac_prefix_len = 0;
8978275SEric Cheng 	info->vn_vid = vnic->vn_vid;
8988275SEric Cheng 	info->vn_force = vnic->vn_force;
8995084Sjohnlev 
9008275SEric Cheng 	bzero(&info->vn_resource_props, sizeof (mac_resource_props_t));
9018275SEric Cheng 	if (vnic->vn_mch != NULL)
9028275SEric Cheng 		mac_resource_ctl_get(vnic->vn_mch, &info->vn_resource_props);
9035084Sjohnlev 
9048275SEric Cheng 	rw_exit(&vnic_lock);
9058275SEric Cheng 	return (0);
9065084Sjohnlev }
9075084Sjohnlev 
9085084Sjohnlev static void
9095084Sjohnlev vnic_notify_cb(void *arg, mac_notify_type_t type)
9105084Sjohnlev {
9118275SEric Cheng 	vnic_t *vnic = arg;
9125084Sjohnlev 
9138275SEric Cheng 	/*
9149052SEric Cheng 	 * Do not deliver notifications if the vnic is not fully initialized
9159052SEric Cheng 	 * or is in process of being torn down.
9168275SEric Cheng 	 */
9179052SEric Cheng 	if (!vnic->vn_enabled)
9188275SEric Cheng 		return;
9195084Sjohnlev 
9208275SEric Cheng 	switch (type) {
9218275SEric Cheng 	case MAC_NOTE_UNICST:
9229052SEric Cheng 		/*
9239052SEric Cheng 		 * Only the VLAN VNIC needs to be notified with primary MAC
9249052SEric Cheng 		 * address change.
9259052SEric Cheng 		 */
9269052SEric Cheng 		if (vnic->vn_addr_type != VNIC_MAC_ADDR_TYPE_PRIMARY)
9279052SEric Cheng 			return;
9289052SEric Cheng 
9298275SEric Cheng 		/*  the unicast MAC address value */
9308275SEric Cheng 		mac_unicast_primary_get(vnic->vn_lower_mh, vnic->vn_addr);
9315084Sjohnlev 
9328275SEric Cheng 		/* notify its upper layer MAC about MAC address change */
9338275SEric Cheng 		mac_unicst_update(vnic->vn_mh, (const uint8_t *)vnic->vn_addr);
9348275SEric Cheng 		break;
9359052SEric Cheng 
9369052SEric Cheng 	case MAC_NOTE_LINK:
9379052SEric Cheng 		mac_link_update(vnic->vn_mh,
9389052SEric Cheng 		    mac_client_stat_get(vnic->vn_mch, MAC_STAT_LINK_STATE));
9399052SEric Cheng 		break;
9409052SEric Cheng 
9418275SEric Cheng 	default:
9428275SEric Cheng 		break;
9435084Sjohnlev 	}
9445084Sjohnlev }
945