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 /*
22*12325SCathy.Zhou@Sun.COM * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
235084Sjohnlev */
245084Sjohnlev
255084Sjohnlev #include <sys/types.h>
2610616SSebastien.Roy@Sun.COM #include <sys/cred.h>
275084Sjohnlev #include <sys/sysmacros.h>
285084Sjohnlev #include <sys/conf.h>
295084Sjohnlev #include <sys/cmn_err.h>
305084Sjohnlev #include <sys/list.h>
315084Sjohnlev #include <sys/ksynch.h>
325084Sjohnlev #include <sys/kmem.h>
335084Sjohnlev #include <sys/stream.h>
345084Sjohnlev #include <sys/modctl.h>
355084Sjohnlev #include <sys/ddi.h>
365084Sjohnlev #include <sys/sunddi.h>
375084Sjohnlev #include <sys/atomic.h>
385084Sjohnlev #include <sys/stat.h>
395084Sjohnlev #include <sys/modhash.h>
405084Sjohnlev #include <sys/strsubr.h>
415084Sjohnlev #include <sys/strsun.h>
425084Sjohnlev #include <sys/dlpi.h>
435084Sjohnlev #include <sys/mac.h>
448275SEric Cheng #include <sys/mac_provider.h>
458275SEric Cheng #include <sys/mac_client.h>
468275SEric Cheng #include <sys/mac_client_priv.h>
475084Sjohnlev #include <sys/mac_ether.h>
485895Syz147064 #include <sys/dls.h>
495084Sjohnlev #include <sys/pattr.h>
508275SEric Cheng #include <sys/time.h>
518275SEric Cheng #include <sys/vlan.h>
525084Sjohnlev #include <sys/vnic.h>
535084Sjohnlev #include <sys/vnic_impl.h>
548275SEric Cheng #include <sys/mac_flow_impl.h>
555084Sjohnlev #include <inet/ip_impl.h>
565084Sjohnlev
578275SEric Cheng /*
588275SEric Cheng * Note that for best performance, the VNIC is a passthrough design.
598275SEric Cheng * For each VNIC corresponds a MAC client of the underlying MAC (lower MAC).
608275SEric Cheng * This MAC client is opened by the VNIC driver at VNIC creation,
618275SEric Cheng * and closed when the VNIC is deleted.
628275SEric Cheng * When a MAC client of the VNIC itself opens a VNIC, the MAC layer
638275SEric Cheng * (upper MAC) detects that the MAC being opened is a VNIC. Instead
648275SEric Cheng * of allocating a new MAC client, it asks the VNIC driver to return
658275SEric Cheng * the lower MAC client handle associated with the VNIC, and that handle
668275SEric Cheng * is returned to the upper MAC client directly. This allows access
678275SEric Cheng * by upper MAC clients of the VNIC to have direct access to the lower
688275SEric Cheng * MAC client for the control path and data path.
698275SEric Cheng *
708275SEric Cheng * Due to this passthrough, some of the entry points exported by the
718275SEric Cheng * VNIC driver are never directly invoked. These entry points include
728275SEric Cheng * vnic_m_start, vnic_m_stop, vnic_m_promisc, vnic_m_multicst, etc.
738275SEric Cheng */
748275SEric Cheng
755084Sjohnlev static int vnic_m_start(void *);
765084Sjohnlev static void vnic_m_stop(void *);
775084Sjohnlev static int vnic_m_promisc(void *, boolean_t);
785084Sjohnlev static int vnic_m_multicst(void *, boolean_t, const uint8_t *);
795084Sjohnlev static int vnic_m_unicst(void *, const uint8_t *);
805084Sjohnlev static int vnic_m_stat(void *, uint_t, uint64_t *);
818275SEric Cheng static void vnic_m_ioctl(void *, queue_t *, mblk_t *);
828927SGirish.Moodalbail@Sun.COM static int vnic_m_setprop(void *, const char *, mac_prop_id_t, uint_t,
838927SGirish.Moodalbail@Sun.COM const void *);
8411878SVenu.Iyer@Sun.COM static void vnic_m_propinfo(void *, const char *, mac_prop_id_t,
8511878SVenu.Iyer@Sun.COM mac_prop_info_handle_t);
865084Sjohnlev static mblk_t *vnic_m_tx(void *, mblk_t *);
875084Sjohnlev static boolean_t vnic_m_capab_get(void *, mac_capab_t, void *);
885084Sjohnlev static void vnic_notify_cb(void *, mac_notify_type_t);
895084Sjohnlev
905084Sjohnlev static kmem_cache_t *vnic_cache;
915084Sjohnlev static krwlock_t vnic_lock;
925084Sjohnlev static uint_t vnic_count;
935084Sjohnlev
949514SGirish.Moodalbail@Sun.COM #define ANCHOR_VNIC_MIN_MTU 576
959514SGirish.Moodalbail@Sun.COM #define ANCHOR_VNIC_MAX_MTU 9000
969514SGirish.Moodalbail@Sun.COM
975084Sjohnlev /* hash of VNICs (vnic_t's), keyed by VNIC id */
985084Sjohnlev static mod_hash_t *vnic_hash;
995084Sjohnlev #define VNIC_HASHSZ 64
1005084Sjohnlev #define VNIC_HASH_KEY(vnic_id) ((mod_hash_key_t)(uintptr_t)vnic_id)
1015084Sjohnlev
1028927SGirish.Moodalbail@Sun.COM #define VNIC_M_CALLBACK_FLAGS \
10311878SVenu.Iyer@Sun.COM (MC_IOCTL | MC_GETCAPAB | MC_SETPROP | MC_PROPINFO)
1045084Sjohnlev
1055084Sjohnlev static mac_callbacks_t vnic_m_callbacks = {
1065084Sjohnlev VNIC_M_CALLBACK_FLAGS,
1075084Sjohnlev vnic_m_stat,
1085084Sjohnlev vnic_m_start,
1095084Sjohnlev vnic_m_stop,
1105084Sjohnlev vnic_m_promisc,
1115084Sjohnlev vnic_m_multicst,
1125084Sjohnlev vnic_m_unicst,
1135084Sjohnlev vnic_m_tx,
11411878SVenu.Iyer@Sun.COM NULL,
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,
12011878SVenu.Iyer@Sun.COM NULL,
12111878SVenu.Iyer@Sun.COM vnic_m_propinfo
1225084Sjohnlev };
1235084Sjohnlev
1245084Sjohnlev void
vnic_dev_init(void)1255084Sjohnlev vnic_dev_init(void)
1265084Sjohnlev {
1275084Sjohnlev vnic_cache = kmem_cache_create("vnic_cache",
1285084Sjohnlev sizeof (vnic_t), 0, NULL, NULL, NULL, NULL, NULL, 0);
1295084Sjohnlev
1305084Sjohnlev vnic_hash = mod_hash_create_idhash("vnic_hash",
1315084Sjohnlev VNIC_HASHSZ, mod_hash_null_valdtor);
1325084Sjohnlev
1335084Sjohnlev rw_init(&vnic_lock, NULL, RW_DEFAULT, NULL);
1345084Sjohnlev
1355084Sjohnlev vnic_count = 0;
1365084Sjohnlev }
1375084Sjohnlev
1385084Sjohnlev void
vnic_dev_fini(void)1395084Sjohnlev vnic_dev_fini(void)
1405084Sjohnlev {
1415084Sjohnlev ASSERT(vnic_count == 0);
1425084Sjohnlev
1435084Sjohnlev rw_destroy(&vnic_lock);
1445084Sjohnlev mod_hash_destroy_idhash(vnic_hash);
1455084Sjohnlev kmem_cache_destroy(vnic_cache);
1465084Sjohnlev }
1475084Sjohnlev
1485084Sjohnlev uint_t
vnic_dev_count(void)1495084Sjohnlev vnic_dev_count(void)
1505084Sjohnlev {
1515084Sjohnlev return (vnic_count);
1525084Sjohnlev }
1535084Sjohnlev
1548275SEric Cheng static vnic_ioc_diag_t
vnic_mac2vnic_diag(mac_diag_t diag)1558275SEric Cheng vnic_mac2vnic_diag(mac_diag_t diag)
1565084Sjohnlev {
1578275SEric Cheng switch (diag) {
1588275SEric Cheng case MAC_DIAG_MACADDR_NIC:
1598275SEric Cheng return (VNIC_IOC_DIAG_MACADDR_NIC);
1608275SEric Cheng case MAC_DIAG_MACADDR_INUSE:
1618275SEric Cheng return (VNIC_IOC_DIAG_MACADDR_INUSE);
1628275SEric Cheng case MAC_DIAG_MACADDR_INVALID:
1638275SEric Cheng return (VNIC_IOC_DIAG_MACADDR_INVALID);
1648275SEric Cheng case MAC_DIAG_MACADDRLEN_INVALID:
1658275SEric Cheng return (VNIC_IOC_DIAG_MACADDRLEN_INVALID);
1668275SEric Cheng case MAC_DIAG_MACFACTORYSLOTINVALID:
1678275SEric Cheng return (VNIC_IOC_DIAG_MACFACTORYSLOTINVALID);
1688275SEric Cheng case MAC_DIAG_MACFACTORYSLOTUSED:
1698275SEric Cheng return (VNIC_IOC_DIAG_MACFACTORYSLOTUSED);
1708275SEric Cheng case MAC_DIAG_MACFACTORYSLOTALLUSED:
1718275SEric Cheng return (VNIC_IOC_DIAG_MACFACTORYSLOTALLUSED);
1728275SEric Cheng case MAC_DIAG_MACFACTORYNOTSUP:
1738275SEric Cheng return (VNIC_IOC_DIAG_MACFACTORYNOTSUP);
1748275SEric Cheng case MAC_DIAG_MACPREFIX_INVALID:
1758275SEric Cheng return (VNIC_IOC_DIAG_MACPREFIX_INVALID);
1768275SEric Cheng case MAC_DIAG_MACPREFIXLEN_INVALID:
1778275SEric Cheng return (VNIC_IOC_DIAG_MACPREFIXLEN_INVALID);
1788275SEric Cheng case MAC_DIAG_MACNO_HWRINGS:
1798275SEric Cheng return (VNIC_IOC_DIAG_NO_HWRINGS);
1808275SEric Cheng default:
1818275SEric Cheng return (VNIC_IOC_DIAG_NONE);
1825084Sjohnlev }
1835084Sjohnlev }
1845084Sjohnlev
1855084Sjohnlev static int
vnic_unicast_add(vnic_t * vnic,vnic_mac_addr_type_t vnic_addr_type,int * addr_slot,uint_t prefix_len,int * addr_len_ptr_arg,uint8_t * mac_addr_arg,uint16_t flags,vnic_ioc_diag_t * diag,uint16_t vid,boolean_t req_hwgrp_flag)1868275SEric Cheng vnic_unicast_add(vnic_t *vnic, vnic_mac_addr_type_t vnic_addr_type,
1878275SEric Cheng int *addr_slot, uint_t prefix_len, int *addr_len_ptr_arg,
1888275SEric Cheng uint8_t *mac_addr_arg, uint16_t flags, vnic_ioc_diag_t *diag,
18911878SVenu.Iyer@Sun.COM uint16_t vid, boolean_t req_hwgrp_flag)
1905084Sjohnlev {
1918275SEric Cheng mac_diag_t mac_diag;
1928275SEric Cheng uint16_t mac_flags = 0;
1938275SEric Cheng int err;
1948275SEric Cheng uint_t addr_len;
1955084Sjohnlev
1968275SEric Cheng if (flags & VNIC_IOC_CREATE_NODUPCHECK)
1978275SEric Cheng mac_flags |= MAC_UNICAST_NODUPCHECK;
1985084Sjohnlev
1998275SEric Cheng switch (vnic_addr_type) {
2008275SEric Cheng case VNIC_MAC_ADDR_TYPE_FIXED:
20111076SCathy.Zhou@Sun.COM case VNIC_MAC_ADDR_TYPE_VRID:
2028275SEric Cheng /*
2038275SEric Cheng * The MAC address value to assign to the VNIC
2048275SEric Cheng * is already provided in mac_addr_arg. addr_len_ptr_arg
2058275SEric Cheng * already contains the MAC address length.
2068275SEric Cheng */
2078275SEric Cheng break;
2085084Sjohnlev
2098275SEric Cheng case VNIC_MAC_ADDR_TYPE_RANDOM:
2108275SEric Cheng /*
2118275SEric Cheng * Random MAC address. There are two sub-cases:
2128275SEric Cheng *
2138275SEric Cheng * 1 - If mac_len == 0, a new MAC address is generated.
2148275SEric Cheng * The length of the MAC address to generated depends
2158275SEric Cheng * on the type of MAC used. The prefix to use for the MAC
2168275SEric Cheng * address is stored in the most significant bytes
2178275SEric Cheng * of the mac_addr argument, and its length is specified
2188275SEric Cheng * by the mac_prefix_len argument. This prefix can
2198275SEric Cheng * correspond to a IEEE OUI in the case of Ethernet,
2208275SEric Cheng * for example.
2218275SEric Cheng *
2228275SEric Cheng * 2 - If mac_len > 0, the address was already picked
2238275SEric Cheng * randomly, and is now passed back during VNIC
2248275SEric Cheng * re-creation. The mac_addr argument contains the MAC
2258275SEric Cheng * address that was generated. We distinguish this
2268275SEric Cheng * case from the fixed MAC address case, since we
2278275SEric Cheng * want the user consumers to know, when they query
2288275SEric Cheng * the list of VNICs, that a VNIC was assigned a
2298275SEric Cheng * random MAC address vs assigned a fixed address
2308275SEric Cheng * specified by the user.
2318275SEric Cheng */
2325084Sjohnlev
2338275SEric Cheng /*
2348275SEric Cheng * If it's a pre-generated address, we're done. mac_addr_arg
2358275SEric Cheng * and addr_len_ptr_arg already contain the MAC address
2368275SEric Cheng * value and length.
2378275SEric Cheng */
2388275SEric Cheng if (*addr_len_ptr_arg > 0)
2398275SEric Cheng break;
2405084Sjohnlev
2418275SEric Cheng /* generate a new random MAC address */
2428275SEric Cheng if ((err = mac_addr_random(vnic->vn_mch,
2438275SEric Cheng prefix_len, mac_addr_arg, &mac_diag)) != 0) {
2448275SEric Cheng *diag = vnic_mac2vnic_diag(mac_diag);
2458275SEric Cheng return (err);
2468275SEric Cheng }
2478275SEric Cheng *addr_len_ptr_arg = mac_addr_len(vnic->vn_lower_mh);
2488275SEric Cheng break;
2498275SEric Cheng
2508275SEric Cheng case VNIC_MAC_ADDR_TYPE_FACTORY:
2518275SEric Cheng err = mac_addr_factory_reserve(vnic->vn_mch, addr_slot);
2528275SEric Cheng if (err != 0) {
2538275SEric Cheng if (err == EINVAL)
2548275SEric Cheng *diag = VNIC_IOC_DIAG_MACFACTORYSLOTINVALID;
2558275SEric Cheng if (err == EBUSY)
2568275SEric Cheng *diag = VNIC_IOC_DIAG_MACFACTORYSLOTUSED;
2578275SEric Cheng if (err == ENOSPC)
2588275SEric Cheng *diag = VNIC_IOC_DIAG_MACFACTORYSLOTALLUSED;
2598275SEric Cheng return (err);
2605084Sjohnlev }
2615084Sjohnlev
2628275SEric Cheng mac_addr_factory_value(vnic->vn_lower_mh, *addr_slot,
2638275SEric Cheng mac_addr_arg, &addr_len, NULL, NULL);
2648275SEric Cheng *addr_len_ptr_arg = addr_len;
2658275SEric Cheng break;
2665084Sjohnlev
2678275SEric Cheng case VNIC_MAC_ADDR_TYPE_AUTO:
2688275SEric Cheng /* first try to allocate a factory MAC address */
2698275SEric Cheng err = mac_addr_factory_reserve(vnic->vn_mch, addr_slot);
2708275SEric Cheng if (err == 0) {
2718275SEric Cheng mac_addr_factory_value(vnic->vn_lower_mh, *addr_slot,
2728275SEric Cheng mac_addr_arg, &addr_len, NULL, NULL);
2738275SEric Cheng vnic_addr_type = VNIC_MAC_ADDR_TYPE_FACTORY;
2748275SEric Cheng *addr_len_ptr_arg = addr_len;
2758275SEric Cheng break;
2765084Sjohnlev }
2775084Sjohnlev
2785084Sjohnlev /*
2798275SEric Cheng * Allocating a factory MAC address failed, generate a
2808275SEric Cheng * random MAC address instead.
2815084Sjohnlev */
2828275SEric Cheng if ((err = mac_addr_random(vnic->vn_mch,
2838275SEric Cheng prefix_len, mac_addr_arg, &mac_diag)) != 0) {
2848275SEric Cheng *diag = vnic_mac2vnic_diag(mac_diag);
2858275SEric Cheng return (err);
2865084Sjohnlev }
2878275SEric Cheng *addr_len_ptr_arg = mac_addr_len(vnic->vn_lower_mh);
2888275SEric Cheng vnic_addr_type = VNIC_MAC_ADDR_TYPE_RANDOM;
2898275SEric Cheng break;
2908275SEric Cheng case VNIC_MAC_ADDR_TYPE_PRIMARY:
2918275SEric Cheng /*
2928275SEric Cheng * We get the address here since we copy it in the
2938275SEric Cheng * vnic's vn_addr.
29411878SVenu.Iyer@Sun.COM * We can't ask for hardware resources since we
29511878SVenu.Iyer@Sun.COM * don't currently support hardware classification
29611878SVenu.Iyer@Sun.COM * for these MAC clients.
2978275SEric Cheng */
29811878SVenu.Iyer@Sun.COM if (req_hwgrp_flag) {
29911878SVenu.Iyer@Sun.COM *diag = VNIC_IOC_DIAG_NO_HWRINGS;
30011878SVenu.Iyer@Sun.COM return (ENOTSUP);
30111878SVenu.Iyer@Sun.COM }
3028275SEric Cheng mac_unicast_primary_get(vnic->vn_lower_mh, mac_addr_arg);
3038275SEric Cheng *addr_len_ptr_arg = mac_addr_len(vnic->vn_lower_mh);
3048275SEric Cheng mac_flags |= MAC_UNICAST_VNIC_PRIMARY;
3058275SEric Cheng break;
3065084Sjohnlev }
3075084Sjohnlev
3088275SEric Cheng vnic->vn_addr_type = vnic_addr_type;
3095084Sjohnlev
3108275SEric Cheng err = mac_unicast_add(vnic->vn_mch, mac_addr_arg, mac_flags,
3118275SEric Cheng &vnic->vn_muh, vid, &mac_diag);
3128275SEric Cheng if (err != 0) {
3138275SEric Cheng if (vnic_addr_type == VNIC_MAC_ADDR_TYPE_FACTORY) {
3148275SEric Cheng /* release factory MAC address */
3158275SEric Cheng mac_addr_factory_release(vnic->vn_mch, *addr_slot);
3165084Sjohnlev }
3178275SEric Cheng *diag = vnic_mac2vnic_diag(mac_diag);
3185084Sjohnlev }
3195084Sjohnlev
3205084Sjohnlev return (err);
3215084Sjohnlev }
3225084Sjohnlev
3235084Sjohnlev /*
3245084Sjohnlev * Create a new VNIC upon request from administrator.
3255084Sjohnlev * Returns 0 on success, an errno on failure.
3265084Sjohnlev */
3278275SEric Cheng /* ARGSUSED */
3285084Sjohnlev int
vnic_dev_create(datalink_id_t vnic_id,datalink_id_t linkid,vnic_mac_addr_type_t * vnic_addr_type,int * mac_len,uchar_t * mac_addr,int * mac_slot,uint_t mac_prefix_len,uint16_t vid,vrid_t vrid,int af,mac_resource_props_t * mrp,uint32_t flags,vnic_ioc_diag_t * diag,cred_t * credp)3298275SEric Cheng vnic_dev_create(datalink_id_t vnic_id, datalink_id_t linkid,
3308275SEric Cheng vnic_mac_addr_type_t *vnic_addr_type, int *mac_len, uchar_t *mac_addr,
33111076SCathy.Zhou@Sun.COM int *mac_slot, uint_t mac_prefix_len, uint16_t vid, vrid_t vrid,
33211076SCathy.Zhou@Sun.COM int af, mac_resource_props_t *mrp, uint32_t flags, vnic_ioc_diag_t *diag,
33310616SSebastien.Roy@Sun.COM cred_t *credp)
3345084Sjohnlev {
3358275SEric Cheng vnic_t *vnic;
3365084Sjohnlev mac_register_t *mac;
3375084Sjohnlev int err;
3388275SEric Cheng boolean_t is_anchor = ((flags & VNIC_IOC_CREATE_ANCHOR) != 0);
3398275SEric Cheng char vnic_name[MAXNAMELEN];
3408275SEric Cheng const mac_info_t *minfop;
34111878SVenu.Iyer@Sun.COM uint32_t req_hwgrp_flag = B_FALSE;
3425084Sjohnlev
3438275SEric Cheng *diag = VNIC_IOC_DIAG_NONE;
3445084Sjohnlev
3455084Sjohnlev rw_enter(&vnic_lock, RW_WRITER);
3465084Sjohnlev
3475084Sjohnlev /* does a VNIC with the same id already exist? */
3485084Sjohnlev err = mod_hash_find(vnic_hash, VNIC_HASH_KEY(vnic_id),
3495084Sjohnlev (mod_hash_val_t *)&vnic);
3505084Sjohnlev if (err == 0) {
3515084Sjohnlev rw_exit(&vnic_lock);
3525084Sjohnlev return (EEXIST);
3535084Sjohnlev }
3545084Sjohnlev
3555084Sjohnlev vnic = kmem_cache_alloc(vnic_cache, KM_NOSLEEP);
3565084Sjohnlev if (vnic == NULL) {
3575084Sjohnlev rw_exit(&vnic_lock);
3585084Sjohnlev return (ENOMEM);
3595084Sjohnlev }
3605084Sjohnlev
3618275SEric Cheng bzero(vnic, sizeof (*vnic));
3625084Sjohnlev
3635084Sjohnlev vnic->vn_id = vnic_id;
3648275SEric Cheng vnic->vn_link_id = linkid;
36511076SCathy.Zhou@Sun.COM vnic->vn_vrid = vrid;
36611076SCathy.Zhou@Sun.COM vnic->vn_af = af;
3678275SEric Cheng
3688275SEric Cheng if (!is_anchor) {
3698275SEric Cheng if (linkid == DATALINK_INVALID_LINKID) {
3708275SEric Cheng err = EINVAL;
3718275SEric Cheng goto bail;
3728275SEric Cheng }
3738275SEric Cheng
3748275SEric Cheng /*
3758275SEric Cheng * Open the lower MAC and assign its initial bandwidth and
3768275SEric Cheng * MAC address. We do this here during VNIC creation and
3778275SEric Cheng * do not wait until the upper MAC client open so that we
3788275SEric Cheng * can validate the VNIC creation parameters (bandwidth,
3798275SEric Cheng * MAC address, etc) and reserve a factory MAC address if
3808275SEric Cheng * one was requested.
3818275SEric Cheng */
3828275SEric Cheng err = mac_open_by_linkid(linkid, &vnic->vn_lower_mh);
3838275SEric Cheng if (err != 0)
3848275SEric Cheng goto bail;
3858275SEric Cheng
3868275SEric Cheng /*
3878275SEric Cheng * VNIC(vlan) over VNICs(vlans) is not supported.
3888275SEric Cheng */
3898275SEric Cheng if (mac_is_vnic(vnic->vn_lower_mh)) {
3908275SEric Cheng err = EINVAL;
3918275SEric Cheng goto bail;
3928275SEric Cheng }
3938275SEric Cheng
3948275SEric Cheng /* only ethernet support for now */
3958275SEric Cheng minfop = mac_info(vnic->vn_lower_mh);
3968275SEric Cheng if (minfop->mi_nativemedia != DL_ETHER) {
3978275SEric Cheng err = ENOTSUP;
3988275SEric Cheng goto bail;
3998275SEric Cheng }
4005084Sjohnlev
4018275SEric Cheng (void) dls_mgmt_get_linkinfo(vnic_id, vnic_name, NULL, NULL,
4028275SEric Cheng NULL);
4038275SEric Cheng err = mac_client_open(vnic->vn_lower_mh, &vnic->vn_mch,
40411878SVenu.Iyer@Sun.COM vnic_name, MAC_OPEN_FLAGS_IS_VNIC);
4058275SEric Cheng if (err != 0)
4068275SEric Cheng goto bail;
4078275SEric Cheng
4088275SEric Cheng if (mrp != NULL) {
40911878SVenu.Iyer@Sun.COM if ((mrp->mrp_mask & MRP_RX_RINGS) != 0 ||
41011878SVenu.Iyer@Sun.COM (mrp->mrp_mask & MRP_TX_RINGS) != 0) {
41111878SVenu.Iyer@Sun.COM req_hwgrp_flag = B_TRUE;
41211878SVenu.Iyer@Sun.COM }
4138275SEric Cheng err = mac_client_set_resources(vnic->vn_mch, mrp);
4148275SEric Cheng if (err != 0)
4158275SEric Cheng goto bail;
4168275SEric Cheng }
4178275SEric Cheng /* assign a MAC address to the VNIC */
4185084Sjohnlev
4198275SEric Cheng err = vnic_unicast_add(vnic, *vnic_addr_type, mac_slot,
42011878SVenu.Iyer@Sun.COM mac_prefix_len, mac_len, mac_addr, flags, diag, vid,
42111878SVenu.Iyer@Sun.COM req_hwgrp_flag);
4228275SEric Cheng if (err != 0) {
4238275SEric Cheng vnic->vn_muh = NULL;
42411878SVenu.Iyer@Sun.COM if (diag != NULL && req_hwgrp_flag)
4258275SEric Cheng *diag = VNIC_IOC_DIAG_NO_HWRINGS;
4268275SEric Cheng goto bail;
4278275SEric Cheng }
4288275SEric Cheng
4298275SEric Cheng /* register to receive notification from underlying MAC */
4308275SEric Cheng vnic->vn_mnh = mac_notify_add(vnic->vn_lower_mh, vnic_notify_cb,
4318275SEric Cheng vnic);
4328275SEric Cheng
4338275SEric Cheng *vnic_addr_type = vnic->vn_addr_type;
4348275SEric Cheng vnic->vn_addr_len = *mac_len;
4358275SEric Cheng vnic->vn_vid = vid;
4368275SEric Cheng
4378275SEric Cheng bcopy(mac_addr, vnic->vn_addr, vnic->vn_addr_len);
4388275SEric Cheng
4398275SEric Cheng if (vnic->vn_addr_type == VNIC_MAC_ADDR_TYPE_FACTORY)
4408275SEric Cheng vnic->vn_slot_id = *mac_slot;
4418275SEric Cheng
442*12325SCathy.Zhou@Sun.COM /*
443*12325SCathy.Zhou@Sun.COM * Set the initial VNIC capabilities. If the VNIC is created
444*12325SCathy.Zhou@Sun.COM * over MACs which does not support nactive vlan, disable
445*12325SCathy.Zhou@Sun.COM * VNIC's hardware checksum capability if its VID is not 0,
446*12325SCathy.Zhou@Sun.COM * since the underlying MAC would get the hardware checksum
447*12325SCathy.Zhou@Sun.COM * offset wrong in case of VLAN packets.
448*12325SCathy.Zhou@Sun.COM */
449*12325SCathy.Zhou@Sun.COM if (vid == 0 || !mac_capab_get(vnic->vn_lower_mh,
450*12325SCathy.Zhou@Sun.COM MAC_CAPAB_NO_NATIVEVLAN, NULL)) {
451*12325SCathy.Zhou@Sun.COM if (!mac_capab_get(vnic->vn_lower_mh, MAC_CAPAB_HCKSUM,
452*12325SCathy.Zhou@Sun.COM &vnic->vn_hcksum_txflags))
453*12325SCathy.Zhou@Sun.COM vnic->vn_hcksum_txflags = 0;
454*12325SCathy.Zhou@Sun.COM } else {
4558275SEric Cheng vnic->vn_hcksum_txflags = 0;
456*12325SCathy.Zhou@Sun.COM }
4578275SEric Cheng }
4585084Sjohnlev
4595084Sjohnlev /* register with the MAC module */
4605084Sjohnlev if ((mac = mac_alloc(MAC_VERSION)) == NULL)
4615084Sjohnlev goto bail;
4625084Sjohnlev
4635084Sjohnlev mac->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
4645084Sjohnlev mac->m_driver = vnic;
4655084Sjohnlev mac->m_dip = vnic_get_dip();
4665895Syz147064 mac->m_instance = (uint_t)-1;
4675084Sjohnlev mac->m_src_addr = vnic->vn_addr;
4685084Sjohnlev mac->m_callbacks = &vnic_m_callbacks;
4695084Sjohnlev
4708275SEric Cheng if (!is_anchor) {
4718275SEric Cheng /*
4728275SEric Cheng * If this is a VNIC based VLAN, then we check for the
4738275SEric Cheng * margin unless it has been created with the force
4748275SEric Cheng * flag. If we are configuring a VLAN over an etherstub,
4758275SEric Cheng * we don't check the margin even if force is not set.
4768275SEric Cheng */
4778275SEric Cheng if (vid == 0 || (flags & VNIC_IOC_CREATE_FORCE) != 0) {
4788275SEric Cheng if (vid != VLAN_ID_NONE)
4798275SEric Cheng vnic->vn_force = B_TRUE;
4808275SEric Cheng /*
4818275SEric Cheng * As the current margin size of the underlying mac is
4828275SEric Cheng * used to determine the margin size of the VNIC
4838275SEric Cheng * itself, request the underlying mac not to change
4848275SEric Cheng * to a smaller margin size.
4858275SEric Cheng */
4868275SEric Cheng err = mac_margin_add(vnic->vn_lower_mh,
4878275SEric Cheng &vnic->vn_margin, B_TRUE);
4888275SEric Cheng ASSERT(err == 0);
4898275SEric Cheng } else {
4908275SEric Cheng vnic->vn_margin = VLAN_TAGSZ;
4918275SEric Cheng err = mac_margin_add(vnic->vn_lower_mh,
4928275SEric Cheng &vnic->vn_margin, B_FALSE);
4938275SEric Cheng if (err != 0) {
4948275SEric Cheng mac_free(mac);
4958275SEric Cheng if (diag != NULL)
4968275SEric Cheng *diag = VNIC_IOC_DIAG_MACMARGIN_INVALID;
4978275SEric Cheng goto bail;
4988275SEric Cheng }
4998275SEric Cheng }
5005084Sjohnlev
5018275SEric Cheng mac_sdu_get(vnic->vn_lower_mh, &mac->m_min_sdu,
5028275SEric Cheng &mac->m_max_sdu);
5038275SEric Cheng } else {
5048275SEric Cheng vnic->vn_margin = VLAN_TAGSZ;
5059514SGirish.Moodalbail@Sun.COM mac->m_min_sdu = ANCHOR_VNIC_MIN_MTU;
5069514SGirish.Moodalbail@Sun.COM mac->m_max_sdu = ANCHOR_VNIC_MAX_MTU;
5078275SEric Cheng }
5088275SEric Cheng
5095895Syz147064 mac->m_margin = vnic->vn_margin;
5108275SEric Cheng
5115084Sjohnlev err = mac_register(mac, &vnic->vn_mh);
5125084Sjohnlev mac_free(mac);
5135895Syz147064 if (err != 0) {
5148275SEric Cheng VERIFY(is_anchor || mac_margin_remove(vnic->vn_lower_mh,
5155895Syz147064 vnic->vn_margin) == 0);
5165084Sjohnlev goto bail;
5175895Syz147064 }
5185895Syz147064
5198275SEric Cheng /* Set the VNIC's MAC in the client */
5208275SEric Cheng if (!is_anchor)
52111878SVenu.Iyer@Sun.COM mac_set_upper_mac(vnic->vn_mch, vnic->vn_mh, mrp);
5228275SEric Cheng
52310616SSebastien.Roy@Sun.COM err = dls_devnet_create(vnic->vn_mh, vnic->vn_id, crgetzoneid(credp));
52410616SSebastien.Roy@Sun.COM if (err != 0) {
5258275SEric Cheng VERIFY(is_anchor || mac_margin_remove(vnic->vn_lower_mh,
5265895Syz147064 vnic->vn_margin) == 0);
5275895Syz147064 (void) mac_unregister(vnic->vn_mh);
5285895Syz147064 goto bail;
5295895Syz147064 }
5305084Sjohnlev
5315084Sjohnlev /* add new VNIC to hash table */
5325084Sjohnlev err = mod_hash_insert(vnic_hash, VNIC_HASH_KEY(vnic_id),
5335084Sjohnlev (mod_hash_val_t)vnic);
5345084Sjohnlev ASSERT(err == 0);
5355084Sjohnlev vnic_count++;
5365084Sjohnlev
5379052SEric Cheng vnic->vn_enabled = B_TRUE;
5385084Sjohnlev rw_exit(&vnic_lock);
5395084Sjohnlev
5405084Sjohnlev return (0);
5415084Sjohnlev
5425084Sjohnlev bail:
5435084Sjohnlev rw_exit(&vnic_lock);
5448275SEric Cheng if (!is_anchor) {
5458275SEric Cheng if (vnic->vn_mnh != NULL)
5468275SEric Cheng (void) mac_notify_remove(vnic->vn_mnh, B_TRUE);
5478275SEric Cheng if (vnic->vn_muh != NULL)
5488275SEric Cheng (void) mac_unicast_remove(vnic->vn_mch, vnic->vn_muh);
5498275SEric Cheng if (vnic->vn_mch != NULL)
5508275SEric Cheng mac_client_close(vnic->vn_mch, MAC_CLOSE_FLAGS_IS_VNIC);
5518275SEric Cheng if (vnic->vn_lower_mh != NULL)
5528275SEric Cheng mac_close(vnic->vn_lower_mh);
5535084Sjohnlev }
5545084Sjohnlev
5558275SEric Cheng kmem_cache_free(vnic_cache, vnic);
5565084Sjohnlev return (err);
5575084Sjohnlev }
5585084Sjohnlev
5595084Sjohnlev /*
5605084Sjohnlev * Modify the properties of an existing VNIC.
5615084Sjohnlev */
5625084Sjohnlev /* ARGSUSED */
5635084Sjohnlev int
vnic_dev_modify(datalink_id_t vnic_id,uint_t modify_mask,vnic_mac_addr_type_t mac_addr_type,uint_t mac_len,uchar_t * mac_addr,uint_t mac_slot,mac_resource_props_t * mrp)5645895Syz147064 vnic_dev_modify(datalink_id_t vnic_id, uint_t modify_mask,
5658275SEric Cheng vnic_mac_addr_type_t mac_addr_type, uint_t mac_len, uchar_t *mac_addr,
5668275SEric Cheng uint_t mac_slot, mac_resource_props_t *mrp)
5675084Sjohnlev {
5685084Sjohnlev vnic_t *vnic = NULL;
5695084Sjohnlev
5705084Sjohnlev rw_enter(&vnic_lock, RW_WRITER);
5715084Sjohnlev
5725084Sjohnlev if (mod_hash_find(vnic_hash, VNIC_HASH_KEY(vnic_id),
5735084Sjohnlev (mod_hash_val_t *)&vnic) != 0) {
5745084Sjohnlev rw_exit(&vnic_lock);
5755084Sjohnlev return (ENOENT);
5765084Sjohnlev }
5775084Sjohnlev
5785084Sjohnlev rw_exit(&vnic_lock);
5795084Sjohnlev
5808275SEric Cheng return (0);
5815084Sjohnlev }
5825084Sjohnlev
5838275SEric Cheng /* ARGSUSED */
5845084Sjohnlev int
vnic_dev_delete(datalink_id_t vnic_id,uint32_t flags,cred_t * credp)58510616SSebastien.Roy@Sun.COM vnic_dev_delete(datalink_id_t vnic_id, uint32_t flags, cred_t *credp)
5865084Sjohnlev {
5875084Sjohnlev vnic_t *vnic = NULL;
5885084Sjohnlev mod_hash_val_t val;
5895895Syz147064 datalink_id_t tmpid;
5905084Sjohnlev int rc;
5915084Sjohnlev
5925084Sjohnlev rw_enter(&vnic_lock, RW_WRITER);
5935084Sjohnlev
5945084Sjohnlev if (mod_hash_find(vnic_hash, VNIC_HASH_KEY(vnic_id),
5955084Sjohnlev (mod_hash_val_t *)&vnic) != 0) {
5965084Sjohnlev rw_exit(&vnic_lock);
5975084Sjohnlev return (ENOENT);
5985084Sjohnlev }
5995084Sjohnlev
6008275SEric Cheng if ((rc = dls_devnet_destroy(vnic->vn_mh, &tmpid, B_TRUE)) != 0) {
6015895Syz147064 rw_exit(&vnic_lock);
6025895Syz147064 return (rc);
6035895Syz147064 }
6045895Syz147064
6055895Syz147064 ASSERT(vnic_id == tmpid);
6065895Syz147064
6075084Sjohnlev /*
6085084Sjohnlev * We cannot unregister the MAC yet. Unregistering would
6095084Sjohnlev * free up mac_impl_t which should not happen at this time.
6108275SEric Cheng * So disable mac_impl_t by calling mac_disable(). This will prevent
6118275SEric Cheng * any new claims on mac_impl_t.
6125084Sjohnlev */
6138275SEric Cheng if ((rc = mac_disable(vnic->vn_mh)) != 0) {
61410616SSebastien.Roy@Sun.COM (void) dls_devnet_create(vnic->vn_mh, vnic_id,
61510616SSebastien.Roy@Sun.COM crgetzoneid(credp));
6165084Sjohnlev rw_exit(&vnic_lock);
6178275SEric Cheng return (rc);
6185084Sjohnlev }
6195084Sjohnlev
6209052SEric Cheng vnic->vn_enabled = B_FALSE;
6215084Sjohnlev (void) mod_hash_remove(vnic_hash, VNIC_HASH_KEY(vnic_id), &val);
6225084Sjohnlev ASSERT(vnic == (vnic_t *)val);
6238275SEric Cheng vnic_count--;
6248275SEric Cheng rw_exit(&vnic_lock);
6255084Sjohnlev
6268275SEric Cheng /*
6278275SEric Cheng * XXX-nicolas shouldn't have a void cast here, if it's
6288275SEric Cheng * expected that the function will never fail, then we should
6298275SEric Cheng * have an ASSERT().
6308275SEric Cheng */
6318275SEric Cheng (void) mac_unregister(vnic->vn_mh);
6325084Sjohnlev
6338275SEric Cheng if (vnic->vn_lower_mh != NULL) {
6345084Sjohnlev /*
6358275SEric Cheng * Check if MAC address for the vnic was obtained from the
6368275SEric Cheng * factory MAC addresses. If yes, release it.
6375084Sjohnlev */
6388275SEric Cheng if (vnic->vn_addr_type == VNIC_MAC_ADDR_TYPE_FACTORY) {
6398275SEric Cheng (void) mac_addr_factory_release(vnic->vn_mch,
6408275SEric Cheng vnic->vn_slot_id);
6418275SEric Cheng }
6428275SEric Cheng (void) mac_margin_remove(vnic->vn_lower_mh, vnic->vn_margin);
6438275SEric Cheng (void) mac_notify_remove(vnic->vn_mnh, B_TRUE);
6448275SEric Cheng (void) mac_unicast_remove(vnic->vn_mch, vnic->vn_muh);
6458275SEric Cheng mac_client_close(vnic->vn_mch, MAC_CLOSE_FLAGS_IS_VNIC);
6468275SEric Cheng mac_close(vnic->vn_lower_mh);
6475084Sjohnlev }
6485084Sjohnlev
6495084Sjohnlev kmem_cache_free(vnic_cache, vnic);
6505084Sjohnlev return (0);
6515084Sjohnlev }
6525084Sjohnlev
6538275SEric Cheng /* ARGSUSED */
6548275SEric Cheng mblk_t *
vnic_m_tx(void * arg,mblk_t * mp_chain)6558275SEric Cheng vnic_m_tx(void *arg, mblk_t *mp_chain)
6565084Sjohnlev {
6578275SEric Cheng /*
6588275SEric Cheng * This function could be invoked for an anchor VNIC when sending
6598275SEric Cheng * broadcast and multicast packets, and unicast packets which did
6608275SEric Cheng * not match any local known destination.
6618275SEric Cheng */
6628275SEric Cheng freemsgchain(mp_chain);
6638275SEric Cheng return (NULL);
6645084Sjohnlev }
6655084Sjohnlev
6668275SEric Cheng /*ARGSUSED*/
6675084Sjohnlev static void
vnic_m_ioctl(void * arg,queue_t * q,mblk_t * mp)6688275SEric Cheng vnic_m_ioctl(void *arg, queue_t *q, mblk_t *mp)
6695084Sjohnlev {
6708275SEric Cheng miocnak(q, mp, 0, ENOTSUP);
6715084Sjohnlev }
6725084Sjohnlev
6735084Sjohnlev /*
6748275SEric Cheng * This entry point cannot be passed-through, since it is invoked
6758275SEric Cheng * for the per-VNIC kstats which must be exported independently
6768275SEric Cheng * of the existence of VNIC MAC clients.
6775084Sjohnlev */
6785084Sjohnlev static int
vnic_m_stat(void * arg,uint_t stat,uint64_t * val)6795084Sjohnlev vnic_m_stat(void *arg, uint_t stat, uint64_t *val)
6805084Sjohnlev {
6815084Sjohnlev vnic_t *vnic = arg;
6825084Sjohnlev int rval = 0;
6835084Sjohnlev
6848275SEric Cheng if (vnic->vn_lower_mh == NULL) {
6858275SEric Cheng /*
6868275SEric Cheng * It's an anchor VNIC, which does not have any
6878275SEric Cheng * statistics in itself.
6888275SEric Cheng */
6898275SEric Cheng return (ENOTSUP);
6908275SEric Cheng }
6918275SEric Cheng
6928275SEric Cheng /*
6938275SEric Cheng * ENOTSUP must be reported for unsupported stats, the VNIC
6948275SEric Cheng * driver reports a subset of the stats that would
6958275SEric Cheng * be returned by a real piece of hardware.
6968275SEric Cheng */
6975084Sjohnlev
6985084Sjohnlev switch (stat) {
6998275SEric Cheng case MAC_STAT_LINK_STATE:
7008275SEric Cheng case MAC_STAT_LINK_UP:
7018275SEric Cheng case MAC_STAT_PROMISC:
7025084Sjohnlev case MAC_STAT_IFSPEED:
7035084Sjohnlev case MAC_STAT_MULTIRCV:
7048275SEric Cheng case MAC_STAT_MULTIXMT:
7055084Sjohnlev case MAC_STAT_BRDCSTRCV:
7065084Sjohnlev case MAC_STAT_BRDCSTXMT:
7078275SEric Cheng case MAC_STAT_OPACKETS:
7088275SEric Cheng case MAC_STAT_OBYTES:
7095084Sjohnlev case MAC_STAT_IERRORS:
7105084Sjohnlev case MAC_STAT_OERRORS:
7115084Sjohnlev case MAC_STAT_RBYTES:
7125084Sjohnlev case MAC_STAT_IPACKETS:
7138275SEric Cheng *val = mac_client_stat_get(vnic->vn_mch, stat);
7145084Sjohnlev break;
7155084Sjohnlev default:
7165084Sjohnlev rval = ENOTSUP;
7175084Sjohnlev }
7185084Sjohnlev
7195084Sjohnlev return (rval);
7205084Sjohnlev }
7215084Sjohnlev
7225084Sjohnlev /*
7238275SEric Cheng * Invoked by the upper MAC to retrieve the lower MAC client handle
7248275SEric Cheng * corresponding to a VNIC. A pointer to this function is obtained
7258275SEric Cheng * by the upper MAC via capability query.
7268275SEric Cheng *
7278275SEric Cheng * XXX-nicolas Note: this currently causes all VNIC MAC clients to
7288275SEric Cheng * receive the same MAC client handle for the same VNIC. This is ok
7298275SEric Cheng * as long as we have only one VNIC MAC client which sends and
7308275SEric Cheng * receives data, but we don't currently enforce this at the MAC layer.
7318275SEric Cheng */
7328275SEric Cheng static void *
vnic_mac_client_handle(void * vnic_arg)7338275SEric Cheng vnic_mac_client_handle(void *vnic_arg)
7348275SEric Cheng {
7358275SEric Cheng vnic_t *vnic = vnic_arg;
7368275SEric Cheng
7378275SEric Cheng return (vnic->vn_mch);
7388275SEric Cheng }
7398275SEric Cheng
7408275SEric Cheng
7418275SEric Cheng /*
7425084Sjohnlev * Return information about the specified capability.
7435084Sjohnlev */
7445084Sjohnlev /* ARGSUSED */
7455084Sjohnlev static boolean_t
vnic_m_capab_get(void * arg,mac_capab_t cap,void * cap_data)7465084Sjohnlev vnic_m_capab_get(void *arg, mac_capab_t cap, void *cap_data)
7475084Sjohnlev {
7485084Sjohnlev vnic_t *vnic = arg;
7495084Sjohnlev
7505084Sjohnlev switch (cap) {
7515084Sjohnlev case MAC_CAPAB_HCKSUM: {
7525084Sjohnlev uint32_t *hcksum_txflags = cap_data;
7535084Sjohnlev
7545084Sjohnlev *hcksum_txflags = vnic->vn_hcksum_txflags &
7555084Sjohnlev (HCKSUM_INET_FULL_V4 | HCKSUM_IPHDRCKSUM |
7565084Sjohnlev HCKSUM_INET_PARTIAL);
7575084Sjohnlev break;
7585084Sjohnlev }
7598275SEric Cheng case MAC_CAPAB_VNIC: {
7608275SEric Cheng mac_capab_vnic_t *vnic_capab = cap_data;
7618275SEric Cheng
7628275SEric Cheng if (vnic->vn_lower_mh == NULL) {
7638275SEric Cheng /*
7648275SEric Cheng * It's an anchor VNIC, we don't have an underlying
7658275SEric Cheng * NIC and MAC client handle.
7668275SEric Cheng */
7678275SEric Cheng return (B_FALSE);
7688275SEric Cheng }
7698275SEric Cheng
7708275SEric Cheng if (vnic_capab != NULL) {
7718275SEric Cheng vnic_capab->mcv_arg = vnic;
7728275SEric Cheng vnic_capab->mcv_mac_client_handle =
7738275SEric Cheng vnic_mac_client_handle;
7748275SEric Cheng }
7758275SEric Cheng break;
7768275SEric Cheng }
7778275SEric Cheng case MAC_CAPAB_ANCHOR_VNIC: {
7788275SEric Cheng /* since it's an anchor VNIC we don't have lower mac handle */
7798275SEric Cheng if (vnic->vn_lower_mh == NULL) {
7808275SEric Cheng ASSERT(vnic->vn_link_id == 0);
7818275SEric Cheng return (B_TRUE);
7828275SEric Cheng }
7838275SEric Cheng return (B_FALSE);
7848275SEric Cheng }
7858275SEric Cheng case MAC_CAPAB_NO_NATIVEVLAN:
786*12325SCathy.Zhou@Sun.COM return (B_FALSE);
7878275SEric Cheng case MAC_CAPAB_NO_ZCOPY:
7888275SEric Cheng return (B_TRUE);
78911076SCathy.Zhou@Sun.COM case MAC_CAPAB_VRRP: {
79011076SCathy.Zhou@Sun.COM mac_capab_vrrp_t *vrrp_capab = cap_data;
79111076SCathy.Zhou@Sun.COM
79211076SCathy.Zhou@Sun.COM if (vnic->vn_vrid != 0) {
79311076SCathy.Zhou@Sun.COM if (vrrp_capab != NULL)
79411076SCathy.Zhou@Sun.COM vrrp_capab->mcv_af = vnic->vn_af;
79511076SCathy.Zhou@Sun.COM return (B_TRUE);
79611076SCathy.Zhou@Sun.COM }
79711076SCathy.Zhou@Sun.COM return (B_FALSE);
79811076SCathy.Zhou@Sun.COM }
7995084Sjohnlev default:
8005084Sjohnlev return (B_FALSE);
8015084Sjohnlev }
8025084Sjohnlev return (B_TRUE);
8035084Sjohnlev }
8045084Sjohnlev
8058275SEric Cheng /* ARGSUSED */
8065084Sjohnlev static int
vnic_m_start(void * arg)8075084Sjohnlev vnic_m_start(void *arg)
8085084Sjohnlev {
8095084Sjohnlev return (0);
8105084Sjohnlev }
8115084Sjohnlev
8128275SEric Cheng /* ARGSUSED */
8135084Sjohnlev static void
vnic_m_stop(void * arg)8145084Sjohnlev vnic_m_stop(void *arg)
8155084Sjohnlev {
8165084Sjohnlev }
8175084Sjohnlev
8185084Sjohnlev /* ARGSUSED */
8195084Sjohnlev static int
vnic_m_promisc(void * arg,boolean_t on)8205084Sjohnlev vnic_m_promisc(void *arg, boolean_t on)
8215084Sjohnlev {
8228275SEric Cheng return (0);
8235084Sjohnlev }
8245084Sjohnlev
8258275SEric Cheng /* ARGSUSED */
8265084Sjohnlev static int
vnic_m_multicst(void * arg,boolean_t add,const uint8_t * addrp)8275084Sjohnlev vnic_m_multicst(void *arg, boolean_t add, const uint8_t *addrp)
8285084Sjohnlev {
8298275SEric Cheng return (0);
8305084Sjohnlev }
8315084Sjohnlev
8325084Sjohnlev static int
vnic_m_unicst(void * arg,const uint8_t * macaddr)8338275SEric Cheng vnic_m_unicst(void *arg, const uint8_t *macaddr)
8345084Sjohnlev {
8355084Sjohnlev vnic_t *vnic = arg;
8365084Sjohnlev
8378275SEric Cheng return (mac_vnic_unicast_set(vnic->vn_mch, macaddr));
8385084Sjohnlev }
8395084Sjohnlev
8408927SGirish.Moodalbail@Sun.COM /*
8418927SGirish.Moodalbail@Sun.COM * Callback functions for set/get of properties
8428927SGirish.Moodalbail@Sun.COM */
8438927SGirish.Moodalbail@Sun.COM /*ARGSUSED*/
8448927SGirish.Moodalbail@Sun.COM static int
vnic_m_setprop(void * m_driver,const char * pr_name,mac_prop_id_t pr_num,uint_t pr_valsize,const void * pr_val)8458927SGirish.Moodalbail@Sun.COM vnic_m_setprop(void *m_driver, const char *pr_name, mac_prop_id_t pr_num,
8468927SGirish.Moodalbail@Sun.COM uint_t pr_valsize, const void *pr_val)
8478927SGirish.Moodalbail@Sun.COM {
8488927SGirish.Moodalbail@Sun.COM int err = ENOTSUP;
8498927SGirish.Moodalbail@Sun.COM vnic_t *vn = m_driver;
8508927SGirish.Moodalbail@Sun.COM
8518927SGirish.Moodalbail@Sun.COM /* allow setting MTU only on an etherstub */
8528927SGirish.Moodalbail@Sun.COM if (vn->vn_link_id != DATALINK_INVALID_LINKID)
8538927SGirish.Moodalbail@Sun.COM return (err);
8548927SGirish.Moodalbail@Sun.COM
8558927SGirish.Moodalbail@Sun.COM switch (pr_num) {
8568927SGirish.Moodalbail@Sun.COM case MAC_PROP_MTU: {
8578927SGirish.Moodalbail@Sun.COM uint32_t mtu;
8588927SGirish.Moodalbail@Sun.COM
8598927SGirish.Moodalbail@Sun.COM if (pr_valsize < sizeof (mtu)) {
8608927SGirish.Moodalbail@Sun.COM err = EINVAL;
8618927SGirish.Moodalbail@Sun.COM break;
8628927SGirish.Moodalbail@Sun.COM }
8638927SGirish.Moodalbail@Sun.COM bcopy(pr_val, &mtu, sizeof (mtu));
8649514SGirish.Moodalbail@Sun.COM if (mtu < ANCHOR_VNIC_MIN_MTU || mtu > ANCHOR_VNIC_MAX_MTU) {
8659514SGirish.Moodalbail@Sun.COM err = EINVAL;
8669514SGirish.Moodalbail@Sun.COM break;
8679514SGirish.Moodalbail@Sun.COM }
8688927SGirish.Moodalbail@Sun.COM err = mac_maxsdu_update(vn->vn_mh, mtu);
8698927SGirish.Moodalbail@Sun.COM break;
8708927SGirish.Moodalbail@Sun.COM }
8718927SGirish.Moodalbail@Sun.COM default:
8728927SGirish.Moodalbail@Sun.COM break;
8738927SGirish.Moodalbail@Sun.COM }
8748927SGirish.Moodalbail@Sun.COM return (err);
8758927SGirish.Moodalbail@Sun.COM }
8768927SGirish.Moodalbail@Sun.COM
87711878SVenu.Iyer@Sun.COM /* ARGSUSED */
vnic_m_propinfo(void * m_driver,const char * pr_name,mac_prop_id_t pr_num,mac_prop_info_handle_t prh)87811878SVenu.Iyer@Sun.COM static void vnic_m_propinfo(void *m_driver, const char *pr_name,
87911878SVenu.Iyer@Sun.COM mac_prop_id_t pr_num, mac_prop_info_handle_t prh)
8808927SGirish.Moodalbail@Sun.COM {
88111878SVenu.Iyer@Sun.COM vnic_t *vn = m_driver;
8829514SGirish.Moodalbail@Sun.COM
8839514SGirish.Moodalbail@Sun.COM /* MTU setting allowed only on an etherstub */
8849514SGirish.Moodalbail@Sun.COM if (vn->vn_link_id != DATALINK_INVALID_LINKID)
88511878SVenu.Iyer@Sun.COM return;
8869514SGirish.Moodalbail@Sun.COM
8879514SGirish.Moodalbail@Sun.COM switch (pr_num) {
8889514SGirish.Moodalbail@Sun.COM case MAC_PROP_MTU:
88911878SVenu.Iyer@Sun.COM mac_prop_info_set_range_uint32(prh,
89011878SVenu.Iyer@Sun.COM ANCHOR_VNIC_MIN_MTU, ANCHOR_VNIC_MAX_MTU);
8919514SGirish.Moodalbail@Sun.COM break;
8929514SGirish.Moodalbail@Sun.COM }
89311878SVenu.Iyer@Sun.COM }
8949514SGirish.Moodalbail@Sun.COM
8958927SGirish.Moodalbail@Sun.COM
8965084Sjohnlev int
vnic_info(vnic_info_t * info,cred_t * credp)89710616SSebastien.Roy@Sun.COM vnic_info(vnic_info_t *info, cred_t *credp)
8985084Sjohnlev {
8998275SEric Cheng vnic_t *vnic;
9008275SEric Cheng int err;
9015084Sjohnlev
90210616SSebastien.Roy@Sun.COM /* Make sure that the VNIC link is visible from the caller's zone. */
90310616SSebastien.Roy@Sun.COM if (!dls_devnet_islinkvisible(info->vn_vnic_id, crgetzoneid(credp)))
90410616SSebastien.Roy@Sun.COM return (ENOENT);
90510616SSebastien.Roy@Sun.COM
9068275SEric Cheng rw_enter(&vnic_lock, RW_WRITER);
9075084Sjohnlev
9088275SEric Cheng err = mod_hash_find(vnic_hash, VNIC_HASH_KEY(info->vn_vnic_id),
9098275SEric Cheng (mod_hash_val_t *)&vnic);
9108275SEric Cheng if (err != 0) {
9118275SEric Cheng rw_exit(&vnic_lock);
9128275SEric Cheng return (ENOENT);
9135895Syz147064 }
9145084Sjohnlev
9158275SEric Cheng info->vn_link_id = vnic->vn_link_id;
9168275SEric Cheng info->vn_mac_addr_type = vnic->vn_addr_type;
9178275SEric Cheng info->vn_mac_len = vnic->vn_addr_len;
9188275SEric Cheng bcopy(vnic->vn_addr, info->vn_mac_addr, MAXMACADDRLEN);
9198275SEric Cheng info->vn_mac_slot = vnic->vn_slot_id;
9208275SEric Cheng info->vn_mac_prefix_len = 0;
9218275SEric Cheng info->vn_vid = vnic->vn_vid;
9228275SEric Cheng info->vn_force = vnic->vn_force;
92311076SCathy.Zhou@Sun.COM info->vn_vrid = vnic->vn_vrid;
92411076SCathy.Zhou@Sun.COM info->vn_af = vnic->vn_af;
9255084Sjohnlev
9268275SEric Cheng bzero(&info->vn_resource_props, sizeof (mac_resource_props_t));
9278275SEric Cheng if (vnic->vn_mch != NULL)
9288275SEric Cheng mac_resource_ctl_get(vnic->vn_mch, &info->vn_resource_props);
9295084Sjohnlev
9308275SEric Cheng rw_exit(&vnic_lock);
9318275SEric Cheng return (0);
9325084Sjohnlev }
9335084Sjohnlev
9345084Sjohnlev static void
vnic_notify_cb(void * arg,mac_notify_type_t type)9355084Sjohnlev vnic_notify_cb(void *arg, mac_notify_type_t type)
9365084Sjohnlev {
9378275SEric Cheng vnic_t *vnic = arg;
9385084Sjohnlev
9398275SEric Cheng /*
9409052SEric Cheng * Do not deliver notifications if the vnic is not fully initialized
9419052SEric Cheng * or is in process of being torn down.
9428275SEric Cheng */
9439052SEric Cheng if (!vnic->vn_enabled)
9448275SEric Cheng return;
9455084Sjohnlev
9468275SEric Cheng switch (type) {
9478275SEric Cheng case MAC_NOTE_UNICST:
9489052SEric Cheng /*
9499052SEric Cheng * Only the VLAN VNIC needs to be notified with primary MAC
9509052SEric Cheng * address change.
9519052SEric Cheng */
9529052SEric Cheng if (vnic->vn_addr_type != VNIC_MAC_ADDR_TYPE_PRIMARY)
9539052SEric Cheng return;
9549052SEric Cheng
9558275SEric Cheng /* the unicast MAC address value */
9568275SEric Cheng mac_unicast_primary_get(vnic->vn_lower_mh, vnic->vn_addr);
9575084Sjohnlev
9588275SEric Cheng /* notify its upper layer MAC about MAC address change */
9598275SEric Cheng mac_unicst_update(vnic->vn_mh, (const uint8_t *)vnic->vn_addr);
9608275SEric Cheng break;
9619052SEric Cheng
9629052SEric Cheng case MAC_NOTE_LINK:
9639052SEric Cheng mac_link_update(vnic->vn_mh,
9649052SEric Cheng mac_client_stat_get(vnic->vn_mch, MAC_STAT_LINK_STATE));
9659052SEric Cheng break;
9669052SEric Cheng
9678275SEric Cheng default:
9688275SEric Cheng break;
9695084Sjohnlev }
9705084Sjohnlev }
971