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> 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 *); 848927SGirish.Moodalbail@Sun.COM static int vnic_m_getprop(void *, const char *, mac_prop_id_t, uint_t, 858927SGirish.Moodalbail@Sun.COM uint_t, void *, uint_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 94*9514SGirish.Moodalbail@Sun.COM #define ANCHOR_VNIC_MIN_MTU 576 95*9514SGirish.Moodalbail@Sun.COM #define ANCHOR_VNIC_MAX_MTU 9000 96*9514SGirish.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 \ 1038927SGirish.Moodalbail@Sun.COM (MC_IOCTL | MC_GETCAPAB | MC_SETPROP | MC_GETPROP) 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, 1148275SEric Cheng vnic_m_ioctl, 1158927SGirish.Moodalbail@Sun.COM vnic_m_capab_get, 1168927SGirish.Moodalbail@Sun.COM NULL, 1178927SGirish.Moodalbail@Sun.COM NULL, 1188927SGirish.Moodalbail@Sun.COM vnic_m_setprop, 1198927SGirish.Moodalbail@Sun.COM vnic_m_getprop 1205084Sjohnlev }; 1215084Sjohnlev 1225084Sjohnlev void 1235084Sjohnlev vnic_dev_init(void) 1245084Sjohnlev { 1255084Sjohnlev vnic_cache = kmem_cache_create("vnic_cache", 1265084Sjohnlev sizeof (vnic_t), 0, NULL, NULL, NULL, NULL, NULL, 0); 1275084Sjohnlev 1285084Sjohnlev vnic_hash = mod_hash_create_idhash("vnic_hash", 1295084Sjohnlev VNIC_HASHSZ, mod_hash_null_valdtor); 1305084Sjohnlev 1315084Sjohnlev rw_init(&vnic_lock, NULL, RW_DEFAULT, NULL); 1325084Sjohnlev 1335084Sjohnlev vnic_count = 0; 1345084Sjohnlev } 1355084Sjohnlev 1365084Sjohnlev void 1375084Sjohnlev vnic_dev_fini(void) 1385084Sjohnlev { 1395084Sjohnlev ASSERT(vnic_count == 0); 1405084Sjohnlev 1415084Sjohnlev rw_destroy(&vnic_lock); 1425084Sjohnlev mod_hash_destroy_idhash(vnic_hash); 1435084Sjohnlev kmem_cache_destroy(vnic_cache); 1445084Sjohnlev } 1455084Sjohnlev 1465084Sjohnlev uint_t 1475084Sjohnlev vnic_dev_count(void) 1485084Sjohnlev { 1495084Sjohnlev return (vnic_count); 1505084Sjohnlev } 1515084Sjohnlev 1528275SEric Cheng static vnic_ioc_diag_t 1538275SEric Cheng vnic_mac2vnic_diag(mac_diag_t diag) 1545084Sjohnlev { 1558275SEric Cheng switch (diag) { 1568275SEric Cheng case MAC_DIAG_MACADDR_NIC: 1578275SEric Cheng return (VNIC_IOC_DIAG_MACADDR_NIC); 1588275SEric Cheng case MAC_DIAG_MACADDR_INUSE: 1598275SEric Cheng return (VNIC_IOC_DIAG_MACADDR_INUSE); 1608275SEric Cheng case MAC_DIAG_MACADDR_INVALID: 1618275SEric Cheng return (VNIC_IOC_DIAG_MACADDR_INVALID); 1628275SEric Cheng case MAC_DIAG_MACADDRLEN_INVALID: 1638275SEric Cheng return (VNIC_IOC_DIAG_MACADDRLEN_INVALID); 1648275SEric Cheng case MAC_DIAG_MACFACTORYSLOTINVALID: 1658275SEric Cheng return (VNIC_IOC_DIAG_MACFACTORYSLOTINVALID); 1668275SEric Cheng case MAC_DIAG_MACFACTORYSLOTUSED: 1678275SEric Cheng return (VNIC_IOC_DIAG_MACFACTORYSLOTUSED); 1688275SEric Cheng case MAC_DIAG_MACFACTORYSLOTALLUSED: 1698275SEric Cheng return (VNIC_IOC_DIAG_MACFACTORYSLOTALLUSED); 1708275SEric Cheng case MAC_DIAG_MACFACTORYNOTSUP: 1718275SEric Cheng return (VNIC_IOC_DIAG_MACFACTORYNOTSUP); 1728275SEric Cheng case MAC_DIAG_MACPREFIX_INVALID: 1738275SEric Cheng return (VNIC_IOC_DIAG_MACPREFIX_INVALID); 1748275SEric Cheng case MAC_DIAG_MACPREFIXLEN_INVALID: 1758275SEric Cheng return (VNIC_IOC_DIAG_MACPREFIXLEN_INVALID); 1768275SEric Cheng case MAC_DIAG_MACNO_HWRINGS: 1778275SEric Cheng return (VNIC_IOC_DIAG_NO_HWRINGS); 1788275SEric Cheng default: 1798275SEric Cheng return (VNIC_IOC_DIAG_NONE); 1805084Sjohnlev } 1815084Sjohnlev } 1825084Sjohnlev 1835084Sjohnlev static int 1848275SEric Cheng vnic_unicast_add(vnic_t *vnic, vnic_mac_addr_type_t vnic_addr_type, 1858275SEric Cheng int *addr_slot, uint_t prefix_len, int *addr_len_ptr_arg, 1868275SEric Cheng uint8_t *mac_addr_arg, uint16_t flags, vnic_ioc_diag_t *diag, 1878275SEric Cheng uint16_t vid) 1885084Sjohnlev { 1898275SEric Cheng mac_diag_t mac_diag; 1908275SEric Cheng uint16_t mac_flags = 0; 1918275SEric Cheng int err; 1928275SEric Cheng uint_t addr_len; 1935084Sjohnlev 1948275SEric Cheng if (flags & VNIC_IOC_CREATE_NODUPCHECK) 1958275SEric Cheng mac_flags |= MAC_UNICAST_NODUPCHECK; 1965084Sjohnlev 1978275SEric Cheng switch (vnic_addr_type) { 1988275SEric Cheng case VNIC_MAC_ADDR_TYPE_FIXED: 1998275SEric Cheng /* 2008275SEric Cheng * The MAC address value to assign to the VNIC 2018275SEric Cheng * is already provided in mac_addr_arg. addr_len_ptr_arg 2028275SEric Cheng * already contains the MAC address length. 2038275SEric Cheng */ 2048275SEric Cheng break; 2055084Sjohnlev 2068275SEric Cheng case VNIC_MAC_ADDR_TYPE_RANDOM: 2078275SEric Cheng /* 2088275SEric Cheng * Random MAC address. There are two sub-cases: 2098275SEric Cheng * 2108275SEric Cheng * 1 - If mac_len == 0, a new MAC address is generated. 2118275SEric Cheng * The length of the MAC address to generated depends 2128275SEric Cheng * on the type of MAC used. The prefix to use for the MAC 2138275SEric Cheng * address is stored in the most significant bytes 2148275SEric Cheng * of the mac_addr argument, and its length is specified 2158275SEric Cheng * by the mac_prefix_len argument. This prefix can 2168275SEric Cheng * correspond to a IEEE OUI in the case of Ethernet, 2178275SEric Cheng * for example. 2188275SEric Cheng * 2198275SEric Cheng * 2 - If mac_len > 0, the address was already picked 2208275SEric Cheng * randomly, and is now passed back during VNIC 2218275SEric Cheng * re-creation. The mac_addr argument contains the MAC 2228275SEric Cheng * address that was generated. We distinguish this 2238275SEric Cheng * case from the fixed MAC address case, since we 2248275SEric Cheng * want the user consumers to know, when they query 2258275SEric Cheng * the list of VNICs, that a VNIC was assigned a 2268275SEric Cheng * random MAC address vs assigned a fixed address 2278275SEric Cheng * specified by the user. 2288275SEric Cheng */ 2295084Sjohnlev 2308275SEric Cheng /* 2318275SEric Cheng * If it's a pre-generated address, we're done. mac_addr_arg 2328275SEric Cheng * and addr_len_ptr_arg already contain the MAC address 2338275SEric Cheng * value and length. 2348275SEric Cheng */ 2358275SEric Cheng if (*addr_len_ptr_arg > 0) 2368275SEric Cheng break; 2375084Sjohnlev 2388275SEric Cheng /* generate a new random MAC address */ 2398275SEric Cheng if ((err = mac_addr_random(vnic->vn_mch, 2408275SEric Cheng prefix_len, mac_addr_arg, &mac_diag)) != 0) { 2418275SEric Cheng *diag = vnic_mac2vnic_diag(mac_diag); 2428275SEric Cheng return (err); 2438275SEric Cheng } 2448275SEric Cheng *addr_len_ptr_arg = mac_addr_len(vnic->vn_lower_mh); 2458275SEric Cheng break; 2468275SEric Cheng 2478275SEric Cheng case VNIC_MAC_ADDR_TYPE_FACTORY: 2488275SEric Cheng err = mac_addr_factory_reserve(vnic->vn_mch, addr_slot); 2498275SEric Cheng if (err != 0) { 2508275SEric Cheng if (err == EINVAL) 2518275SEric Cheng *diag = VNIC_IOC_DIAG_MACFACTORYSLOTINVALID; 2528275SEric Cheng if (err == EBUSY) 2538275SEric Cheng *diag = VNIC_IOC_DIAG_MACFACTORYSLOTUSED; 2548275SEric Cheng if (err == ENOSPC) 2558275SEric Cheng *diag = VNIC_IOC_DIAG_MACFACTORYSLOTALLUSED; 2568275SEric Cheng return (err); 2575084Sjohnlev } 2585084Sjohnlev 2598275SEric Cheng mac_addr_factory_value(vnic->vn_lower_mh, *addr_slot, 2608275SEric Cheng mac_addr_arg, &addr_len, NULL, NULL); 2618275SEric Cheng *addr_len_ptr_arg = addr_len; 2628275SEric Cheng break; 2635084Sjohnlev 2648275SEric Cheng case VNIC_MAC_ADDR_TYPE_AUTO: 2658275SEric Cheng /* first try to allocate a factory MAC address */ 2668275SEric Cheng err = mac_addr_factory_reserve(vnic->vn_mch, addr_slot); 2678275SEric Cheng if (err == 0) { 2688275SEric Cheng mac_addr_factory_value(vnic->vn_lower_mh, *addr_slot, 2698275SEric Cheng mac_addr_arg, &addr_len, NULL, NULL); 2708275SEric Cheng vnic_addr_type = VNIC_MAC_ADDR_TYPE_FACTORY; 2718275SEric Cheng *addr_len_ptr_arg = addr_len; 2728275SEric Cheng break; 2735084Sjohnlev } 2745084Sjohnlev 2755084Sjohnlev /* 2768275SEric Cheng * Allocating a factory MAC address failed, generate a 2778275SEric Cheng * random MAC address instead. 2785084Sjohnlev */ 2798275SEric Cheng if ((err = mac_addr_random(vnic->vn_mch, 2808275SEric Cheng prefix_len, mac_addr_arg, &mac_diag)) != 0) { 2818275SEric Cheng *diag = vnic_mac2vnic_diag(mac_diag); 2828275SEric Cheng return (err); 2835084Sjohnlev } 2848275SEric Cheng *addr_len_ptr_arg = mac_addr_len(vnic->vn_lower_mh); 2858275SEric Cheng vnic_addr_type = VNIC_MAC_ADDR_TYPE_RANDOM; 2868275SEric Cheng break; 2878275SEric Cheng case VNIC_MAC_ADDR_TYPE_PRIMARY: 2888275SEric Cheng /* 2898275SEric Cheng * We get the address here since we copy it in the 2908275SEric Cheng * vnic's vn_addr. 2918275SEric Cheng */ 2928275SEric Cheng mac_unicast_primary_get(vnic->vn_lower_mh, mac_addr_arg); 2938275SEric Cheng *addr_len_ptr_arg = mac_addr_len(vnic->vn_lower_mh); 2948275SEric Cheng mac_flags |= MAC_UNICAST_VNIC_PRIMARY; 2958275SEric Cheng break; 2965084Sjohnlev } 2975084Sjohnlev 2988275SEric Cheng vnic->vn_addr_type = vnic_addr_type; 2995084Sjohnlev 3008275SEric Cheng err = mac_unicast_add(vnic->vn_mch, mac_addr_arg, mac_flags, 3018275SEric Cheng &vnic->vn_muh, vid, &mac_diag); 3028275SEric Cheng if (err != 0) { 3038275SEric Cheng if (vnic_addr_type == VNIC_MAC_ADDR_TYPE_FACTORY) { 3048275SEric Cheng /* release factory MAC address */ 3058275SEric Cheng mac_addr_factory_release(vnic->vn_mch, *addr_slot); 3065084Sjohnlev } 3078275SEric Cheng *diag = vnic_mac2vnic_diag(mac_diag); 3085084Sjohnlev } 3095084Sjohnlev 3105084Sjohnlev return (err); 3115084Sjohnlev } 3125084Sjohnlev 3135084Sjohnlev /* 3145084Sjohnlev * Create a new VNIC upon request from administrator. 3155084Sjohnlev * Returns 0 on success, an errno on failure. 3165084Sjohnlev */ 3178275SEric Cheng /* ARGSUSED */ 3185084Sjohnlev int 3198275SEric Cheng vnic_dev_create(datalink_id_t vnic_id, datalink_id_t linkid, 3208275SEric Cheng vnic_mac_addr_type_t *vnic_addr_type, int *mac_len, uchar_t *mac_addr, 3218275SEric Cheng int *mac_slot, uint_t mac_prefix_len, uint16_t vid, 3228275SEric Cheng mac_resource_props_t *mrp, uint32_t flags, vnic_ioc_diag_t *diag) 3235084Sjohnlev { 3248275SEric Cheng vnic_t *vnic; 3255084Sjohnlev mac_register_t *mac; 3265084Sjohnlev int err; 3278275SEric Cheng boolean_t is_anchor = ((flags & VNIC_IOC_CREATE_ANCHOR) != 0); 3288275SEric Cheng char vnic_name[MAXNAMELEN]; 3298275SEric Cheng const mac_info_t *minfop; 3308275SEric Cheng uint32_t req_hwgrp_flag = ((flags & VNIC_IOC_CREATE_REQ_HWRINGS) != 0) ? 3318275SEric Cheng MAC_OPEN_FLAGS_REQ_HWRINGS : 0; 3325084Sjohnlev 3338275SEric Cheng *diag = VNIC_IOC_DIAG_NONE; 3345084Sjohnlev 3355084Sjohnlev rw_enter(&vnic_lock, RW_WRITER); 3365084Sjohnlev 3375084Sjohnlev /* does a VNIC with the same id already exist? */ 3385084Sjohnlev err = mod_hash_find(vnic_hash, VNIC_HASH_KEY(vnic_id), 3395084Sjohnlev (mod_hash_val_t *)&vnic); 3405084Sjohnlev if (err == 0) { 3415084Sjohnlev rw_exit(&vnic_lock); 3425084Sjohnlev return (EEXIST); 3435084Sjohnlev } 3445084Sjohnlev 3455084Sjohnlev vnic = kmem_cache_alloc(vnic_cache, KM_NOSLEEP); 3465084Sjohnlev if (vnic == NULL) { 3475084Sjohnlev rw_exit(&vnic_lock); 3485084Sjohnlev return (ENOMEM); 3495084Sjohnlev } 3505084Sjohnlev 3518275SEric Cheng bzero(vnic, sizeof (*vnic)); 3525084Sjohnlev 3535084Sjohnlev vnic->vn_id = vnic_id; 3548275SEric Cheng vnic->vn_link_id = linkid; 3558275SEric Cheng 3568275SEric Cheng if (!is_anchor) { 3578275SEric Cheng if (linkid == DATALINK_INVALID_LINKID) { 3588275SEric Cheng err = EINVAL; 3598275SEric Cheng goto bail; 3608275SEric Cheng } 3618275SEric Cheng 3628275SEric Cheng /* 3638275SEric Cheng * Open the lower MAC and assign its initial bandwidth and 3648275SEric Cheng * MAC address. We do this here during VNIC creation and 3658275SEric Cheng * do not wait until the upper MAC client open so that we 3668275SEric Cheng * can validate the VNIC creation parameters (bandwidth, 3678275SEric Cheng * MAC address, etc) and reserve a factory MAC address if 3688275SEric Cheng * one was requested. 3698275SEric Cheng */ 3708275SEric Cheng err = mac_open_by_linkid(linkid, &vnic->vn_lower_mh); 3718275SEric Cheng if (err != 0) 3728275SEric Cheng goto bail; 3738275SEric Cheng 3748275SEric Cheng /* 3758275SEric Cheng * VNIC(vlan) over VNICs(vlans) is not supported. 3768275SEric Cheng */ 3778275SEric Cheng if (mac_is_vnic(vnic->vn_lower_mh)) { 3788275SEric Cheng err = EINVAL; 3798275SEric Cheng goto bail; 3808275SEric Cheng } 3818275SEric Cheng 3828275SEric Cheng /* only ethernet support for now */ 3838275SEric Cheng minfop = mac_info(vnic->vn_lower_mh); 3848275SEric Cheng if (minfop->mi_nativemedia != DL_ETHER) { 3858275SEric Cheng err = ENOTSUP; 3868275SEric Cheng goto bail; 3878275SEric Cheng } 3885084Sjohnlev 3898275SEric Cheng (void) dls_mgmt_get_linkinfo(vnic_id, vnic_name, NULL, NULL, 3908275SEric Cheng NULL); 3918275SEric Cheng err = mac_client_open(vnic->vn_lower_mh, &vnic->vn_mch, 3928275SEric Cheng vnic_name, MAC_OPEN_FLAGS_IS_VNIC | req_hwgrp_flag); 3938275SEric Cheng if (err != 0) 3948275SEric Cheng goto bail; 3958275SEric Cheng 3968275SEric Cheng if (mrp != NULL) { 3978275SEric Cheng err = mac_client_set_resources(vnic->vn_mch, mrp); 3988275SEric Cheng if (err != 0) 3998275SEric Cheng goto bail; 4008275SEric Cheng } 4018275SEric Cheng /* assign a MAC address to the VNIC */ 4025084Sjohnlev 4038275SEric Cheng err = vnic_unicast_add(vnic, *vnic_addr_type, mac_slot, 4048275SEric Cheng mac_prefix_len, mac_len, mac_addr, flags, diag, vid); 4058275SEric Cheng if (err != 0) { 4068275SEric Cheng vnic->vn_muh = NULL; 4078275SEric Cheng if (diag != NULL && req_hwgrp_flag != 0) 4088275SEric Cheng *diag = VNIC_IOC_DIAG_NO_HWRINGS; 4098275SEric Cheng goto bail; 4108275SEric Cheng } 4118275SEric Cheng 4128275SEric Cheng /* register to receive notification from underlying MAC */ 4138275SEric Cheng vnic->vn_mnh = mac_notify_add(vnic->vn_lower_mh, vnic_notify_cb, 4148275SEric Cheng vnic); 4158275SEric Cheng 4168275SEric Cheng *vnic_addr_type = vnic->vn_addr_type; 4178275SEric Cheng vnic->vn_addr_len = *mac_len; 4188275SEric Cheng vnic->vn_vid = vid; 4198275SEric Cheng 4208275SEric Cheng bcopy(mac_addr, vnic->vn_addr, vnic->vn_addr_len); 4218275SEric Cheng 4228275SEric Cheng if (vnic->vn_addr_type == VNIC_MAC_ADDR_TYPE_FACTORY) 4238275SEric Cheng vnic->vn_slot_id = *mac_slot; 4248275SEric Cheng 4258275SEric Cheng /* set the initial VNIC capabilities */ 4268275SEric Cheng if (!mac_capab_get(vnic->vn_lower_mh, MAC_CAPAB_HCKSUM, 4278275SEric Cheng &vnic->vn_hcksum_txflags)) 4288275SEric Cheng vnic->vn_hcksum_txflags = 0; 4298275SEric Cheng } 4305084Sjohnlev 4315084Sjohnlev /* register with the MAC module */ 4325084Sjohnlev if ((mac = mac_alloc(MAC_VERSION)) == NULL) 4335084Sjohnlev goto bail; 4345084Sjohnlev 4355084Sjohnlev mac->m_type_ident = MAC_PLUGIN_IDENT_ETHER; 4365084Sjohnlev mac->m_driver = vnic; 4375084Sjohnlev mac->m_dip = vnic_get_dip(); 4385895Syz147064 mac->m_instance = (uint_t)-1; 4395084Sjohnlev mac->m_src_addr = vnic->vn_addr; 4405084Sjohnlev mac->m_callbacks = &vnic_m_callbacks; 4415084Sjohnlev 4428275SEric Cheng if (!is_anchor) { 4438275SEric Cheng /* 4448275SEric Cheng * If this is a VNIC based VLAN, then we check for the 4458275SEric Cheng * margin unless it has been created with the force 4468275SEric Cheng * flag. If we are configuring a VLAN over an etherstub, 4478275SEric Cheng * we don't check the margin even if force is not set. 4488275SEric Cheng */ 4498275SEric Cheng if (vid == 0 || (flags & VNIC_IOC_CREATE_FORCE) != 0) { 4508275SEric Cheng if (vid != VLAN_ID_NONE) 4518275SEric Cheng vnic->vn_force = B_TRUE; 4528275SEric Cheng /* 4538275SEric Cheng * As the current margin size of the underlying mac is 4548275SEric Cheng * used to determine the margin size of the VNIC 4558275SEric Cheng * itself, request the underlying mac not to change 4568275SEric Cheng * to a smaller margin size. 4578275SEric Cheng */ 4588275SEric Cheng err = mac_margin_add(vnic->vn_lower_mh, 4598275SEric Cheng &vnic->vn_margin, B_TRUE); 4608275SEric Cheng ASSERT(err == 0); 4618275SEric Cheng } else { 4628275SEric Cheng vnic->vn_margin = VLAN_TAGSZ; 4638275SEric Cheng err = mac_margin_add(vnic->vn_lower_mh, 4648275SEric Cheng &vnic->vn_margin, B_FALSE); 4658275SEric Cheng if (err != 0) { 4668275SEric Cheng mac_free(mac); 4678275SEric Cheng if (diag != NULL) 4688275SEric Cheng *diag = VNIC_IOC_DIAG_MACMARGIN_INVALID; 4698275SEric Cheng goto bail; 4708275SEric Cheng } 4718275SEric Cheng } 4725084Sjohnlev 4738275SEric Cheng mac_sdu_get(vnic->vn_lower_mh, &mac->m_min_sdu, 4748275SEric Cheng &mac->m_max_sdu); 4758275SEric Cheng } else { 4768275SEric Cheng vnic->vn_margin = VLAN_TAGSZ; 477*9514SGirish.Moodalbail@Sun.COM mac->m_min_sdu = ANCHOR_VNIC_MIN_MTU; 478*9514SGirish.Moodalbail@Sun.COM mac->m_max_sdu = ANCHOR_VNIC_MAX_MTU; 4798275SEric Cheng } 4808275SEric Cheng 4815895Syz147064 mac->m_margin = vnic->vn_margin; 4828275SEric Cheng 4835084Sjohnlev err = mac_register(mac, &vnic->vn_mh); 4845084Sjohnlev mac_free(mac); 4855895Syz147064 if (err != 0) { 4868275SEric Cheng VERIFY(is_anchor || mac_margin_remove(vnic->vn_lower_mh, 4875895Syz147064 vnic->vn_margin) == 0); 4885084Sjohnlev goto bail; 4895895Syz147064 } 4905895Syz147064 4918275SEric Cheng /* Set the VNIC's MAC in the client */ 4928275SEric Cheng if (!is_anchor) 4938275SEric Cheng mac_set_upper_mac(vnic->vn_mch, vnic->vn_mh); 4948275SEric Cheng 4955895Syz147064 if ((err = dls_devnet_create(vnic->vn_mh, vnic->vn_id)) != 0) { 4968275SEric Cheng VERIFY(is_anchor || mac_margin_remove(vnic->vn_lower_mh, 4975895Syz147064 vnic->vn_margin) == 0); 4985895Syz147064 (void) mac_unregister(vnic->vn_mh); 4995895Syz147064 goto bail; 5005895Syz147064 } 5015084Sjohnlev 5025084Sjohnlev /* add new VNIC to hash table */ 5035084Sjohnlev err = mod_hash_insert(vnic_hash, VNIC_HASH_KEY(vnic_id), 5045084Sjohnlev (mod_hash_val_t)vnic); 5055084Sjohnlev ASSERT(err == 0); 5065084Sjohnlev vnic_count++; 5075084Sjohnlev 5089052SEric Cheng vnic->vn_enabled = B_TRUE; 5095084Sjohnlev rw_exit(&vnic_lock); 5105084Sjohnlev 5115084Sjohnlev return (0); 5125084Sjohnlev 5135084Sjohnlev bail: 5145084Sjohnlev rw_exit(&vnic_lock); 5158275SEric Cheng if (!is_anchor) { 5168275SEric Cheng if (vnic->vn_mnh != NULL) 5178275SEric Cheng (void) mac_notify_remove(vnic->vn_mnh, B_TRUE); 5188275SEric Cheng if (vnic->vn_muh != NULL) 5198275SEric Cheng (void) mac_unicast_remove(vnic->vn_mch, vnic->vn_muh); 5208275SEric Cheng if (vnic->vn_mch != NULL) 5218275SEric Cheng mac_client_close(vnic->vn_mch, MAC_CLOSE_FLAGS_IS_VNIC); 5228275SEric Cheng if (vnic->vn_lower_mh != NULL) 5238275SEric Cheng mac_close(vnic->vn_lower_mh); 5245084Sjohnlev } 5255084Sjohnlev 5268275SEric Cheng kmem_cache_free(vnic_cache, vnic); 5275084Sjohnlev return (err); 5285084Sjohnlev } 5295084Sjohnlev 5305084Sjohnlev /* 5315084Sjohnlev * Modify the properties of an existing VNIC. 5325084Sjohnlev */ 5335084Sjohnlev /* ARGSUSED */ 5345084Sjohnlev int 5355895Syz147064 vnic_dev_modify(datalink_id_t vnic_id, uint_t modify_mask, 5368275SEric Cheng vnic_mac_addr_type_t mac_addr_type, uint_t mac_len, uchar_t *mac_addr, 5378275SEric Cheng uint_t mac_slot, mac_resource_props_t *mrp) 5385084Sjohnlev { 5395084Sjohnlev vnic_t *vnic = NULL; 5405084Sjohnlev 5415084Sjohnlev rw_enter(&vnic_lock, RW_WRITER); 5425084Sjohnlev 5435084Sjohnlev if (mod_hash_find(vnic_hash, VNIC_HASH_KEY(vnic_id), 5445084Sjohnlev (mod_hash_val_t *)&vnic) != 0) { 5455084Sjohnlev rw_exit(&vnic_lock); 5465084Sjohnlev return (ENOENT); 5475084Sjohnlev } 5485084Sjohnlev 5495084Sjohnlev rw_exit(&vnic_lock); 5505084Sjohnlev 5518275SEric Cheng return (0); 5525084Sjohnlev } 5535084Sjohnlev 5548275SEric Cheng /* ARGSUSED */ 5555084Sjohnlev int 5568275SEric Cheng vnic_dev_delete(datalink_id_t vnic_id, uint32_t flags) 5575084Sjohnlev { 5585084Sjohnlev vnic_t *vnic = NULL; 5595084Sjohnlev mod_hash_val_t val; 5605895Syz147064 datalink_id_t tmpid; 5615084Sjohnlev int rc; 5625084Sjohnlev 5635084Sjohnlev rw_enter(&vnic_lock, RW_WRITER); 5645084Sjohnlev 5655084Sjohnlev if (mod_hash_find(vnic_hash, VNIC_HASH_KEY(vnic_id), 5665084Sjohnlev (mod_hash_val_t *)&vnic) != 0) { 5675084Sjohnlev rw_exit(&vnic_lock); 5685084Sjohnlev return (ENOENT); 5695084Sjohnlev } 5705084Sjohnlev 5718275SEric Cheng if ((rc = dls_devnet_destroy(vnic->vn_mh, &tmpid, B_TRUE)) != 0) { 5725895Syz147064 rw_exit(&vnic_lock); 5735895Syz147064 return (rc); 5745895Syz147064 } 5755895Syz147064 5765895Syz147064 ASSERT(vnic_id == tmpid); 5775895Syz147064 5785084Sjohnlev /* 5795084Sjohnlev * We cannot unregister the MAC yet. Unregistering would 5805084Sjohnlev * free up mac_impl_t which should not happen at this time. 5818275SEric Cheng * So disable mac_impl_t by calling mac_disable(). This will prevent 5828275SEric Cheng * any new claims on mac_impl_t. 5835084Sjohnlev */ 5848275SEric Cheng if ((rc = mac_disable(vnic->vn_mh)) != 0) { 5855895Syz147064 (void) dls_devnet_create(vnic->vn_mh, vnic_id); 5865084Sjohnlev rw_exit(&vnic_lock); 5878275SEric Cheng return (rc); 5885084Sjohnlev } 5895084Sjohnlev 5909052SEric Cheng vnic->vn_enabled = B_FALSE; 5915084Sjohnlev (void) mod_hash_remove(vnic_hash, VNIC_HASH_KEY(vnic_id), &val); 5925084Sjohnlev ASSERT(vnic == (vnic_t *)val); 5938275SEric Cheng vnic_count--; 5948275SEric Cheng rw_exit(&vnic_lock); 5955084Sjohnlev 5968275SEric Cheng /* 5978275SEric Cheng * XXX-nicolas shouldn't have a void cast here, if it's 5988275SEric Cheng * expected that the function will never fail, then we should 5998275SEric Cheng * have an ASSERT(). 6008275SEric Cheng */ 6018275SEric Cheng (void) mac_unregister(vnic->vn_mh); 6025084Sjohnlev 6038275SEric Cheng if (vnic->vn_lower_mh != NULL) { 6045084Sjohnlev /* 6058275SEric Cheng * Check if MAC address for the vnic was obtained from the 6068275SEric Cheng * factory MAC addresses. If yes, release it. 6075084Sjohnlev */ 6088275SEric Cheng if (vnic->vn_addr_type == VNIC_MAC_ADDR_TYPE_FACTORY) { 6098275SEric Cheng (void) mac_addr_factory_release(vnic->vn_mch, 6108275SEric Cheng vnic->vn_slot_id); 6118275SEric Cheng } 6128275SEric Cheng (void) mac_margin_remove(vnic->vn_lower_mh, vnic->vn_margin); 6138275SEric Cheng (void) mac_notify_remove(vnic->vn_mnh, B_TRUE); 6148275SEric Cheng (void) mac_unicast_remove(vnic->vn_mch, vnic->vn_muh); 6158275SEric Cheng mac_client_close(vnic->vn_mch, MAC_CLOSE_FLAGS_IS_VNIC); 6168275SEric Cheng mac_close(vnic->vn_lower_mh); 6175084Sjohnlev } 6185084Sjohnlev 6195084Sjohnlev kmem_cache_free(vnic_cache, vnic); 6205084Sjohnlev return (0); 6215084Sjohnlev } 6225084Sjohnlev 6238275SEric Cheng /* ARGSUSED */ 6248275SEric Cheng mblk_t * 6258275SEric Cheng vnic_m_tx(void *arg, mblk_t *mp_chain) 6265084Sjohnlev { 6278275SEric Cheng /* 6288275SEric Cheng * This function could be invoked for an anchor VNIC when sending 6298275SEric Cheng * broadcast and multicast packets, and unicast packets which did 6308275SEric Cheng * not match any local known destination. 6318275SEric Cheng */ 6328275SEric Cheng freemsgchain(mp_chain); 6338275SEric Cheng return (NULL); 6345084Sjohnlev } 6355084Sjohnlev 6368275SEric Cheng /*ARGSUSED*/ 6375084Sjohnlev static void 6388275SEric Cheng vnic_m_ioctl(void *arg, queue_t *q, mblk_t *mp) 6395084Sjohnlev { 6408275SEric Cheng miocnak(q, mp, 0, ENOTSUP); 6415084Sjohnlev } 6425084Sjohnlev 6435084Sjohnlev /* 6448275SEric Cheng * This entry point cannot be passed-through, since it is invoked 6458275SEric Cheng * for the per-VNIC kstats which must be exported independently 6468275SEric Cheng * of the existence of VNIC MAC clients. 6475084Sjohnlev */ 6485084Sjohnlev static int 6495084Sjohnlev vnic_m_stat(void *arg, uint_t stat, uint64_t *val) 6505084Sjohnlev { 6515084Sjohnlev vnic_t *vnic = arg; 6525084Sjohnlev int rval = 0; 6535084Sjohnlev 6548275SEric Cheng if (vnic->vn_lower_mh == NULL) { 6558275SEric Cheng /* 6568275SEric Cheng * It's an anchor VNIC, which does not have any 6578275SEric Cheng * statistics in itself. 6588275SEric Cheng */ 6598275SEric Cheng return (ENOTSUP); 6608275SEric Cheng } 6618275SEric Cheng 6628275SEric Cheng /* 6638275SEric Cheng * ENOTSUP must be reported for unsupported stats, the VNIC 6648275SEric Cheng * driver reports a subset of the stats that would 6658275SEric Cheng * be returned by a real piece of hardware. 6668275SEric Cheng */ 6675084Sjohnlev 6685084Sjohnlev switch (stat) { 6698275SEric Cheng case MAC_STAT_LINK_STATE: 6708275SEric Cheng case MAC_STAT_LINK_UP: 6718275SEric Cheng case MAC_STAT_PROMISC: 6725084Sjohnlev case MAC_STAT_IFSPEED: 6735084Sjohnlev case MAC_STAT_MULTIRCV: 6748275SEric Cheng case MAC_STAT_MULTIXMT: 6755084Sjohnlev case MAC_STAT_BRDCSTRCV: 6765084Sjohnlev case MAC_STAT_BRDCSTXMT: 6778275SEric Cheng case MAC_STAT_OPACKETS: 6788275SEric Cheng case MAC_STAT_OBYTES: 6795084Sjohnlev case MAC_STAT_IERRORS: 6805084Sjohnlev case MAC_STAT_OERRORS: 6815084Sjohnlev case MAC_STAT_RBYTES: 6825084Sjohnlev case MAC_STAT_IPACKETS: 6838275SEric Cheng *val = mac_client_stat_get(vnic->vn_mch, stat); 6845084Sjohnlev break; 6855084Sjohnlev default: 6865084Sjohnlev rval = ENOTSUP; 6875084Sjohnlev } 6885084Sjohnlev 6895084Sjohnlev return (rval); 6905084Sjohnlev } 6915084Sjohnlev 6925084Sjohnlev /* 6938275SEric Cheng * Invoked by the upper MAC to retrieve the lower MAC client handle 6948275SEric Cheng * corresponding to a VNIC. A pointer to this function is obtained 6958275SEric Cheng * by the upper MAC via capability query. 6968275SEric Cheng * 6978275SEric Cheng * XXX-nicolas Note: this currently causes all VNIC MAC clients to 6988275SEric Cheng * receive the same MAC client handle for the same VNIC. This is ok 6998275SEric Cheng * as long as we have only one VNIC MAC client which sends and 7008275SEric Cheng * receives data, but we don't currently enforce this at the MAC layer. 7018275SEric Cheng */ 7028275SEric Cheng static void * 7038275SEric Cheng vnic_mac_client_handle(void *vnic_arg) 7048275SEric Cheng { 7058275SEric Cheng vnic_t *vnic = vnic_arg; 7068275SEric Cheng 7078275SEric Cheng return (vnic->vn_mch); 7088275SEric Cheng } 7098275SEric Cheng 7108275SEric Cheng 7118275SEric Cheng /* 7125084Sjohnlev * Return information about the specified capability. 7135084Sjohnlev */ 7145084Sjohnlev /* ARGSUSED */ 7155084Sjohnlev static boolean_t 7165084Sjohnlev vnic_m_capab_get(void *arg, mac_capab_t cap, void *cap_data) 7175084Sjohnlev { 7185084Sjohnlev vnic_t *vnic = arg; 7195084Sjohnlev 7205084Sjohnlev switch (cap) { 7215084Sjohnlev case MAC_CAPAB_HCKSUM: { 7225084Sjohnlev uint32_t *hcksum_txflags = cap_data; 7235084Sjohnlev 7245084Sjohnlev *hcksum_txflags = vnic->vn_hcksum_txflags & 7255084Sjohnlev (HCKSUM_INET_FULL_V4 | HCKSUM_IPHDRCKSUM | 7265084Sjohnlev HCKSUM_INET_PARTIAL); 7275084Sjohnlev break; 7285084Sjohnlev } 7298275SEric Cheng case MAC_CAPAB_VNIC: { 7308275SEric Cheng mac_capab_vnic_t *vnic_capab = cap_data; 7318275SEric Cheng 7328275SEric Cheng if (vnic->vn_lower_mh == NULL) { 7338275SEric Cheng /* 7348275SEric Cheng * It's an anchor VNIC, we don't have an underlying 7358275SEric Cheng * NIC and MAC client handle. 7368275SEric Cheng */ 7378275SEric Cheng return (B_FALSE); 7388275SEric Cheng } 7398275SEric Cheng 7408275SEric Cheng if (vnic_capab != NULL) { 7418275SEric Cheng vnic_capab->mcv_arg = vnic; 7428275SEric Cheng vnic_capab->mcv_mac_client_handle = 7438275SEric Cheng vnic_mac_client_handle; 7448275SEric Cheng } 7458275SEric Cheng break; 7468275SEric Cheng } 7478275SEric Cheng case MAC_CAPAB_ANCHOR_VNIC: { 7488275SEric Cheng /* since it's an anchor VNIC we don't have lower mac handle */ 7498275SEric Cheng if (vnic->vn_lower_mh == NULL) { 7508275SEric Cheng ASSERT(vnic->vn_link_id == 0); 7518275SEric Cheng return (B_TRUE); 7528275SEric Cheng } 7538275SEric Cheng return (B_FALSE); 7548275SEric Cheng } 7558275SEric Cheng case MAC_CAPAB_NO_NATIVEVLAN: 7568275SEric Cheng case MAC_CAPAB_NO_ZCOPY: 7578275SEric Cheng return (B_TRUE); 7585084Sjohnlev default: 7595084Sjohnlev return (B_FALSE); 7605084Sjohnlev } 7615084Sjohnlev return (B_TRUE); 7625084Sjohnlev } 7635084Sjohnlev 7648275SEric Cheng /* ARGSUSED */ 7655084Sjohnlev static int 7665084Sjohnlev vnic_m_start(void *arg) 7675084Sjohnlev { 7685084Sjohnlev return (0); 7695084Sjohnlev } 7705084Sjohnlev 7718275SEric Cheng /* ARGSUSED */ 7725084Sjohnlev static void 7735084Sjohnlev vnic_m_stop(void *arg) 7745084Sjohnlev { 7755084Sjohnlev } 7765084Sjohnlev 7775084Sjohnlev /* ARGSUSED */ 7785084Sjohnlev static int 7795084Sjohnlev vnic_m_promisc(void *arg, boolean_t on) 7805084Sjohnlev { 7818275SEric Cheng return (0); 7825084Sjohnlev } 7835084Sjohnlev 7848275SEric Cheng /* ARGSUSED */ 7855084Sjohnlev static int 7865084Sjohnlev vnic_m_multicst(void *arg, boolean_t add, const uint8_t *addrp) 7875084Sjohnlev { 7888275SEric Cheng return (0); 7895084Sjohnlev } 7905084Sjohnlev 7915084Sjohnlev static int 7928275SEric Cheng vnic_m_unicst(void *arg, const uint8_t *macaddr) 7935084Sjohnlev { 7945084Sjohnlev vnic_t *vnic = arg; 7955084Sjohnlev 7968275SEric Cheng return (mac_vnic_unicast_set(vnic->vn_mch, macaddr)); 7975084Sjohnlev } 7985084Sjohnlev 7998927SGirish.Moodalbail@Sun.COM /* 8008927SGirish.Moodalbail@Sun.COM * Callback functions for set/get of properties 8018927SGirish.Moodalbail@Sun.COM */ 8028927SGirish.Moodalbail@Sun.COM /*ARGSUSED*/ 8038927SGirish.Moodalbail@Sun.COM static int 8048927SGirish.Moodalbail@Sun.COM vnic_m_setprop(void *m_driver, const char *pr_name, mac_prop_id_t pr_num, 8058927SGirish.Moodalbail@Sun.COM uint_t pr_valsize, const void *pr_val) 8068927SGirish.Moodalbail@Sun.COM { 8078927SGirish.Moodalbail@Sun.COM int err = ENOTSUP; 8088927SGirish.Moodalbail@Sun.COM vnic_t *vn = m_driver; 8098927SGirish.Moodalbail@Sun.COM 8108927SGirish.Moodalbail@Sun.COM /* allow setting MTU only on an etherstub */ 8118927SGirish.Moodalbail@Sun.COM if (vn->vn_link_id != DATALINK_INVALID_LINKID) 8128927SGirish.Moodalbail@Sun.COM return (err); 8138927SGirish.Moodalbail@Sun.COM 8148927SGirish.Moodalbail@Sun.COM switch (pr_num) { 8158927SGirish.Moodalbail@Sun.COM case MAC_PROP_MTU: { 8168927SGirish.Moodalbail@Sun.COM uint32_t mtu; 8178927SGirish.Moodalbail@Sun.COM 8188927SGirish.Moodalbail@Sun.COM if (pr_valsize < sizeof (mtu)) { 8198927SGirish.Moodalbail@Sun.COM err = EINVAL; 8208927SGirish.Moodalbail@Sun.COM break; 8218927SGirish.Moodalbail@Sun.COM } 8228927SGirish.Moodalbail@Sun.COM bcopy(pr_val, &mtu, sizeof (mtu)); 823*9514SGirish.Moodalbail@Sun.COM if (mtu < ANCHOR_VNIC_MIN_MTU || mtu > ANCHOR_VNIC_MAX_MTU) { 824*9514SGirish.Moodalbail@Sun.COM err = EINVAL; 825*9514SGirish.Moodalbail@Sun.COM break; 826*9514SGirish.Moodalbail@Sun.COM } 8278927SGirish.Moodalbail@Sun.COM err = mac_maxsdu_update(vn->vn_mh, mtu); 8288927SGirish.Moodalbail@Sun.COM break; 8298927SGirish.Moodalbail@Sun.COM } 8308927SGirish.Moodalbail@Sun.COM default: 8318927SGirish.Moodalbail@Sun.COM break; 8328927SGirish.Moodalbail@Sun.COM } 8338927SGirish.Moodalbail@Sun.COM return (err); 8348927SGirish.Moodalbail@Sun.COM } 8358927SGirish.Moodalbail@Sun.COM 8368927SGirish.Moodalbail@Sun.COM /*ARGSUSED*/ 8378927SGirish.Moodalbail@Sun.COM static int 8388927SGirish.Moodalbail@Sun.COM vnic_m_getprop(void *m_driver, const char *pr_name, mac_prop_id_t pr_num, 8398927SGirish.Moodalbail@Sun.COM uint_t pr_flags, uint_t pr_valsize, void *pr_val, uint_t *perm) 8408927SGirish.Moodalbail@Sun.COM { 841*9514SGirish.Moodalbail@Sun.COM mac_propval_range_t range; 842*9514SGirish.Moodalbail@Sun.COM vnic_t *vn = m_driver; 843*9514SGirish.Moodalbail@Sun.COM int err = ENOTSUP; 844*9514SGirish.Moodalbail@Sun.COM 845*9514SGirish.Moodalbail@Sun.COM /* MTU setting allowed only on an etherstub */ 846*9514SGirish.Moodalbail@Sun.COM if (vn->vn_link_id != DATALINK_INVALID_LINKID) 847*9514SGirish.Moodalbail@Sun.COM return (err); 848*9514SGirish.Moodalbail@Sun.COM 849*9514SGirish.Moodalbail@Sun.COM switch (pr_num) { 850*9514SGirish.Moodalbail@Sun.COM case MAC_PROP_MTU: 851*9514SGirish.Moodalbail@Sun.COM if (!(pr_flags & MAC_PROP_POSSIBLE)) 852*9514SGirish.Moodalbail@Sun.COM return (ENOTSUP); 853*9514SGirish.Moodalbail@Sun.COM if (pr_valsize < sizeof (mac_propval_range_t)) 854*9514SGirish.Moodalbail@Sun.COM return (EINVAL); 855*9514SGirish.Moodalbail@Sun.COM range.mpr_count = 1; 856*9514SGirish.Moodalbail@Sun.COM range.mpr_type = MAC_PROPVAL_UINT32; 857*9514SGirish.Moodalbail@Sun.COM range.range_uint32[0].mpur_min = ANCHOR_VNIC_MIN_MTU; 858*9514SGirish.Moodalbail@Sun.COM range.range_uint32[0].mpur_max = ANCHOR_VNIC_MAX_MTU; 859*9514SGirish.Moodalbail@Sun.COM bcopy(&range, pr_val, sizeof (range)); 860*9514SGirish.Moodalbail@Sun.COM return (0); 861*9514SGirish.Moodalbail@Sun.COM default: 862*9514SGirish.Moodalbail@Sun.COM break; 863*9514SGirish.Moodalbail@Sun.COM } 864*9514SGirish.Moodalbail@Sun.COM 865*9514SGirish.Moodalbail@Sun.COM return (err); 8668927SGirish.Moodalbail@Sun.COM } 8678927SGirish.Moodalbail@Sun.COM 8685084Sjohnlev int 8698275SEric Cheng vnic_info(vnic_info_t *info) 8705084Sjohnlev { 8718275SEric Cheng vnic_t *vnic; 8728275SEric Cheng int err; 8735084Sjohnlev 8748275SEric Cheng rw_enter(&vnic_lock, RW_WRITER); 8755084Sjohnlev 8768275SEric Cheng err = mod_hash_find(vnic_hash, VNIC_HASH_KEY(info->vn_vnic_id), 8778275SEric Cheng (mod_hash_val_t *)&vnic); 8788275SEric Cheng if (err != 0) { 8798275SEric Cheng rw_exit(&vnic_lock); 8808275SEric Cheng return (ENOENT); 8815895Syz147064 } 8825084Sjohnlev 8838275SEric Cheng info->vn_link_id = vnic->vn_link_id; 8848275SEric Cheng info->vn_mac_addr_type = vnic->vn_addr_type; 8858275SEric Cheng info->vn_mac_len = vnic->vn_addr_len; 8868275SEric Cheng bcopy(vnic->vn_addr, info->vn_mac_addr, MAXMACADDRLEN); 8878275SEric Cheng info->vn_mac_slot = vnic->vn_slot_id; 8888275SEric Cheng info->vn_mac_prefix_len = 0; 8898275SEric Cheng info->vn_vid = vnic->vn_vid; 8908275SEric Cheng info->vn_force = vnic->vn_force; 8915084Sjohnlev 8928275SEric Cheng bzero(&info->vn_resource_props, sizeof (mac_resource_props_t)); 8938275SEric Cheng if (vnic->vn_mch != NULL) 8948275SEric Cheng mac_resource_ctl_get(vnic->vn_mch, &info->vn_resource_props); 8955084Sjohnlev 8968275SEric Cheng rw_exit(&vnic_lock); 8978275SEric Cheng return (0); 8985084Sjohnlev } 8995084Sjohnlev 9005084Sjohnlev static void 9015084Sjohnlev vnic_notify_cb(void *arg, mac_notify_type_t type) 9025084Sjohnlev { 9038275SEric Cheng vnic_t *vnic = arg; 9045084Sjohnlev 9058275SEric Cheng /* 9069052SEric Cheng * Do not deliver notifications if the vnic is not fully initialized 9079052SEric Cheng * or is in process of being torn down. 9088275SEric Cheng */ 9099052SEric Cheng if (!vnic->vn_enabled) 9108275SEric Cheng return; 9115084Sjohnlev 9128275SEric Cheng switch (type) { 9138275SEric Cheng case MAC_NOTE_UNICST: 9149052SEric Cheng /* 9159052SEric Cheng * Only the VLAN VNIC needs to be notified with primary MAC 9169052SEric Cheng * address change. 9179052SEric Cheng */ 9189052SEric Cheng if (vnic->vn_addr_type != VNIC_MAC_ADDR_TYPE_PRIMARY) 9199052SEric Cheng return; 9209052SEric Cheng 9218275SEric Cheng /* the unicast MAC address value */ 9228275SEric Cheng mac_unicast_primary_get(vnic->vn_lower_mh, vnic->vn_addr); 9235084Sjohnlev 9248275SEric Cheng /* notify its upper layer MAC about MAC address change */ 9258275SEric Cheng mac_unicst_update(vnic->vn_mh, (const uint8_t *)vnic->vn_addr); 9268275SEric Cheng break; 9279052SEric Cheng 9289052SEric Cheng case MAC_NOTE_LINK: 9299052SEric Cheng mac_link_update(vnic->vn_mh, 9309052SEric Cheng mac_client_stat_get(vnic->vn_mch, MAC_STAT_LINK_STATE)); 9319052SEric Cheng break; 9329052SEric Cheng 9338275SEric Cheng default: 9348275SEric Cheng break; 9355084Sjohnlev } 9365084Sjohnlev } 937