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 /* 225084Sjohnlev * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 235084Sjohnlev * Use is subject to license terms. 245084Sjohnlev */ 255084Sjohnlev 265084Sjohnlev #pragma ident "%Z%%M% %I% %E% SMI" 275084Sjohnlev 285084Sjohnlev #include <sys/types.h> 295084Sjohnlev #include <sys/sysmacros.h> 305084Sjohnlev #include <sys/conf.h> 315084Sjohnlev #include <sys/cmn_err.h> 325084Sjohnlev #include <sys/list.h> 335084Sjohnlev #include <sys/ksynch.h> 345084Sjohnlev #include <sys/kmem.h> 355084Sjohnlev #include <sys/stream.h> 365084Sjohnlev #include <sys/modctl.h> 375084Sjohnlev #include <sys/ddi.h> 385084Sjohnlev #include <sys/sunddi.h> 395084Sjohnlev #include <sys/atomic.h> 405084Sjohnlev #include <sys/stat.h> 415084Sjohnlev #include <sys/modhash.h> 425084Sjohnlev #include <sys/strsubr.h> 435084Sjohnlev #include <sys/strsun.h> 445084Sjohnlev #include <sys/dlpi.h> 455084Sjohnlev #include <sys/mac.h> 465084Sjohnlev #include <sys/mac_ether.h> 475084Sjohnlev #include <sys/pattr.h> 485084Sjohnlev #if 0 495084Sjohnlev #include <sys/vlan.h> 505084Sjohnlev #endif 515084Sjohnlev #include <sys/vnic.h> 525084Sjohnlev #include <sys/vnic_impl.h> 535084Sjohnlev #include <sys/gld.h> 545084Sjohnlev #include <inet/ip.h> 555084Sjohnlev #include <inet/ip_impl.h> 565084Sjohnlev 575084Sjohnlev static int vnic_m_start(void *); 585084Sjohnlev static void vnic_m_stop(void *); 595084Sjohnlev static int vnic_m_promisc(void *, boolean_t); 605084Sjohnlev static int vnic_m_multicst(void *, boolean_t, const uint8_t *); 615084Sjohnlev static int vnic_m_unicst(void *, const uint8_t *); 625084Sjohnlev static int vnic_m_stat(void *, uint_t, uint64_t *); 635084Sjohnlev static void vnic_m_resources(void *); 645084Sjohnlev static mblk_t *vnic_m_tx(void *, mblk_t *); 655084Sjohnlev static boolean_t vnic_m_capab_get(void *, mac_capab_t, void *); 665084Sjohnlev static void vnic_mac_free(vnic_mac_t *); 675084Sjohnlev static uint_t vnic_info_walker(mod_hash_key_t, mod_hash_val_t *, void *); 685084Sjohnlev static void vnic_notify_cb(void *, mac_notify_type_t); 695084Sjohnlev static int vnic_modify_mac_addr(vnic_t *, uint_t, uchar_t *); 705084Sjohnlev static mblk_t *vnic_active_tx(void *, mblk_t *); 715084Sjohnlev static int vnic_promisc_set(vnic_t *, boolean_t); 725084Sjohnlev 735084Sjohnlev static kmem_cache_t *vnic_cache; 745084Sjohnlev static kmem_cache_t *vnic_mac_cache; 755084Sjohnlev static krwlock_t vnic_lock; 765084Sjohnlev static kmutex_t vnic_mac_lock; 775084Sjohnlev static uint_t vnic_count; 785084Sjohnlev 795084Sjohnlev /* hash of VNICs (vnic_t's), keyed by VNIC id */ 805084Sjohnlev static mod_hash_t *vnic_hash; 815084Sjohnlev #define VNIC_HASHSZ 64 825084Sjohnlev #define VNIC_HASH_KEY(vnic_id) ((mod_hash_key_t)(uintptr_t)vnic_id) 835084Sjohnlev 845084Sjohnlev /* 855084Sjohnlev * Hash of underlying open MACs (vnic_mac_t's), keyed by the string 865084Sjohnlev * "<device name><instance number>/<port number>". 875084Sjohnlev */ 885084Sjohnlev static mod_hash_t *vnic_mac_hash; 895084Sjohnlev #define VNIC_MAC_HASHSZ 64 905084Sjohnlev 915084Sjohnlev #define VNIC_MAC_REFHOLD(va) { \ 925084Sjohnlev ASSERT(MUTEX_HELD(&vnic_mac_lock)); \ 935084Sjohnlev (va)->va_refs++; \ 945084Sjohnlev ASSERT((va)->va_refs != 0); \ 955084Sjohnlev } 965084Sjohnlev 975084Sjohnlev #define VNIC_MAC_REFRELE(va) { \ 985084Sjohnlev ASSERT(MUTEX_HELD(&vnic_mac_lock)); \ 995084Sjohnlev ASSERT((va)->va_refs != 0); \ 1005084Sjohnlev if (--((va)->va_refs) == 0) \ 1015084Sjohnlev vnic_mac_free(va); \ 1025084Sjohnlev } 1035084Sjohnlev 1045084Sjohnlev static uchar_t vnic_brdcst_mac[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; 1055084Sjohnlev 1065084Sjohnlev /* used by vnic_walker */ 1075084Sjohnlev typedef struct vnic_info_state { 1085084Sjohnlev uint32_t vs_vnic_id; 1095084Sjohnlev char vs_dev_name[MAXNAMELEN]; 1105084Sjohnlev boolean_t vs_vnic_found; 1115084Sjohnlev vnic_info_new_vnic_fn_t vs_new_vnic_fn; 1125084Sjohnlev void *vs_fn_arg; 1135084Sjohnlev int vs_rc; 1145084Sjohnlev } vnic_info_state_t; 1155084Sjohnlev 1165084Sjohnlev #define VNIC_M_CALLBACK_FLAGS (MC_RESOURCES | MC_GETCAPAB) 1175084Sjohnlev 1185084Sjohnlev static mac_callbacks_t vnic_m_callbacks = { 1195084Sjohnlev VNIC_M_CALLBACK_FLAGS, 1205084Sjohnlev vnic_m_stat, 1215084Sjohnlev vnic_m_start, 1225084Sjohnlev vnic_m_stop, 1235084Sjohnlev vnic_m_promisc, 1245084Sjohnlev vnic_m_multicst, 1255084Sjohnlev vnic_m_unicst, 1265084Sjohnlev vnic_m_tx, 1275084Sjohnlev vnic_m_resources, 1285084Sjohnlev NULL, /* m_ioctl */ 1295084Sjohnlev vnic_m_capab_get 1305084Sjohnlev }; 1315084Sjohnlev 1325084Sjohnlev /* ARGSUSED */ 1335084Sjohnlev static int 1345084Sjohnlev vnic_mac_ctor(void *buf, void *arg, int kmflag) 1355084Sjohnlev { 1365084Sjohnlev vnic_mac_t *vnic_mac = buf; 1375084Sjohnlev 1385084Sjohnlev bzero(vnic_mac, sizeof (vnic_mac_t)); 1395084Sjohnlev rw_init(&vnic_mac->va_bcast_grp_lock, NULL, RW_DRIVER, NULL); 1405084Sjohnlev rw_init(&vnic_mac->va_promisc_lock, NULL, RW_DRIVER, NULL); 1415084Sjohnlev 1425084Sjohnlev return (0); 1435084Sjohnlev } 1445084Sjohnlev 1455084Sjohnlev /* ARGSUSED */ 1465084Sjohnlev static void 1475084Sjohnlev vnic_mac_dtor(void *buf, void *arg) 1485084Sjohnlev { 1495084Sjohnlev vnic_mac_t *vnic_mac = buf; 1505084Sjohnlev 1515084Sjohnlev rw_destroy(&vnic_mac->va_promisc_lock); 1525084Sjohnlev rw_destroy(&vnic_mac->va_bcast_grp_lock); 1535084Sjohnlev } 1545084Sjohnlev 1555084Sjohnlev void 1565084Sjohnlev vnic_dev_init(void) 1575084Sjohnlev { 1585084Sjohnlev vnic_cache = kmem_cache_create("vnic_cache", 1595084Sjohnlev sizeof (vnic_t), 0, NULL, NULL, NULL, NULL, NULL, 0); 1605084Sjohnlev 1615084Sjohnlev vnic_mac_cache = kmem_cache_create("vnic_mac_cache", 1625084Sjohnlev sizeof (vnic_mac_t), 0, vnic_mac_ctor, vnic_mac_dtor, 1635084Sjohnlev NULL, NULL, NULL, 0); 1645084Sjohnlev 1655084Sjohnlev vnic_hash = mod_hash_create_idhash("vnic_hash", 1665084Sjohnlev VNIC_HASHSZ, mod_hash_null_valdtor); 1675084Sjohnlev 1685084Sjohnlev vnic_mac_hash = mod_hash_create_strhash("vnic_mac_hash", 1695084Sjohnlev VNIC_MAC_HASHSZ, mod_hash_null_valdtor); 1705084Sjohnlev 1715084Sjohnlev rw_init(&vnic_lock, NULL, RW_DEFAULT, NULL); 1725084Sjohnlev 1735084Sjohnlev mutex_init(&vnic_mac_lock, NULL, MUTEX_DEFAULT, NULL); 1745084Sjohnlev 1755084Sjohnlev vnic_count = 0; 1765084Sjohnlev } 1775084Sjohnlev 1785084Sjohnlev void 1795084Sjohnlev vnic_dev_fini(void) 1805084Sjohnlev { 1815084Sjohnlev ASSERT(vnic_count == 0); 1825084Sjohnlev 1835084Sjohnlev mutex_destroy(&vnic_mac_lock); 1845084Sjohnlev rw_destroy(&vnic_lock); 1855084Sjohnlev mod_hash_destroy_strhash(vnic_mac_hash); 1865084Sjohnlev mod_hash_destroy_idhash(vnic_hash); 1875084Sjohnlev kmem_cache_destroy(vnic_mac_cache); 1885084Sjohnlev kmem_cache_destroy(vnic_cache); 1895084Sjohnlev } 1905084Sjohnlev 1915084Sjohnlev uint_t 1925084Sjohnlev vnic_dev_count(void) 1935084Sjohnlev { 1945084Sjohnlev return (vnic_count); 1955084Sjohnlev } 1965084Sjohnlev 1975084Sjohnlev static int 1985084Sjohnlev vnic_mac_open(const char *dev_name, vnic_mac_t **vmp) 1995084Sjohnlev { 2005084Sjohnlev char *str_key; 2015084Sjohnlev int err; 2025084Sjohnlev vnic_mac_t *vnic_mac = NULL; 2035084Sjohnlev const mac_info_t *mip; 2045084Sjohnlev 2055084Sjohnlev *vmp = NULL; 2065084Sjohnlev 2075084Sjohnlev mutex_enter(&vnic_mac_lock); 2085084Sjohnlev 2095084Sjohnlev err = mod_hash_find(vnic_mac_hash, (mod_hash_key_t)dev_name, 2105084Sjohnlev (mod_hash_val_t *)&vnic_mac); 2115084Sjohnlev if (err == 0) { 2125084Sjohnlev /* this MAC is already opened, increment reference count */ 2135084Sjohnlev VNIC_MAC_REFHOLD(vnic_mac); 2145084Sjohnlev mutex_exit(&vnic_mac_lock); 2155084Sjohnlev *vmp = vnic_mac; 2165084Sjohnlev return (0); 2175084Sjohnlev } 2185084Sjohnlev 2195084Sjohnlev vnic_mac = kmem_cache_alloc(vnic_mac_cache, KM_SLEEP); 2205084Sjohnlev 221*5733Syz147064 if ((err = mac_open(dev_name, &vnic_mac->va_mh)) != 0) { 2225084Sjohnlev vnic_mac->va_mh = NULL; 2235084Sjohnlev goto bail; 2245084Sjohnlev } 2255084Sjohnlev 2265084Sjohnlev /* only ethernet support, for now */ 2275084Sjohnlev mip = mac_info(vnic_mac->va_mh); 2285084Sjohnlev if (mip->mi_media != DL_ETHER) { 2295084Sjohnlev err = ENOTSUP; 2305084Sjohnlev goto bail; 2315084Sjohnlev } 2325084Sjohnlev if (mip->mi_media != mip->mi_nativemedia) { 2335084Sjohnlev err = ENOTSUP; 2345084Sjohnlev goto bail; 2355084Sjohnlev } 2365084Sjohnlev 2375084Sjohnlev (void) strcpy(vnic_mac->va_dev_name, dev_name); 2385084Sjohnlev 2395084Sjohnlev /* add entry to hash table */ 2405084Sjohnlev str_key = kmem_alloc(strlen(dev_name) + 1, KM_SLEEP); 2415084Sjohnlev (void) strcpy(str_key, dev_name); 2425084Sjohnlev err = mod_hash_insert(vnic_mac_hash, (mod_hash_key_t)str_key, 2435084Sjohnlev (mod_hash_val_t)vnic_mac); 2445084Sjohnlev ASSERT(err == 0); 2455084Sjohnlev 2465084Sjohnlev /* initialize the flow table associated with lower MAC */ 2475084Sjohnlev vnic_mac->va_addr_len = ETHERADDRL; 2485084Sjohnlev (void) vnic_classifier_flow_tab_init(vnic_mac, vnic_mac->va_addr_len, 2495084Sjohnlev KM_SLEEP); 2505084Sjohnlev 2515084Sjohnlev vnic_mac->va_txinfo = mac_vnic_tx_get(vnic_mac->va_mh); 2525084Sjohnlev vnic_mac->va_notify_hdl = mac_notify_add(vnic_mac->va_mh, 2535084Sjohnlev vnic_notify_cb, vnic_mac); 2545084Sjohnlev 2555084Sjohnlev VNIC_MAC_REFHOLD(vnic_mac); 2565084Sjohnlev *vmp = vnic_mac; 2575084Sjohnlev mutex_exit(&vnic_mac_lock); 2585084Sjohnlev return (0); 2595084Sjohnlev 2605084Sjohnlev bail: 2615084Sjohnlev if (vnic_mac != NULL) { 2625084Sjohnlev if (vnic_mac->va_mh != NULL) 2635084Sjohnlev mac_close(vnic_mac->va_mh); 2645084Sjohnlev kmem_cache_free(vnic_mac_cache, vnic_mac); 2655084Sjohnlev } 2665084Sjohnlev mutex_exit(&vnic_mac_lock); 2675084Sjohnlev return (err); 2685084Sjohnlev } 2695084Sjohnlev 2705084Sjohnlev /* 2715084Sjohnlev * Create a new flow for the active MAC client sharing the NIC 2725084Sjohnlev * with the VNICs. This allows the unicast packets for that NIC 2735084Sjohnlev * to be classified and passed up to the active MAC client. It 2745084Sjohnlev * also allows packets sent from a VNIC to the active link to 2755084Sjohnlev * be classified by the VNIC transmit function and delivered via 2765084Sjohnlev * the MAC module locally. Returns B_TRUE on success, B_FALSE on 2775084Sjohnlev * failure. 2785084Sjohnlev */ 2795084Sjohnlev static int 2805084Sjohnlev vnic_init_active_rx(vnic_mac_t *vnic_mac) 2815084Sjohnlev { 2825084Sjohnlev uchar_t nic_mac_addr[MAXMACADDRLEN]; 2835084Sjohnlev 2845084Sjohnlev if (vnic_mac->va_active_flow != NULL) 2855084Sjohnlev return (B_TRUE); 2865084Sjohnlev 2875084Sjohnlev mac_unicst_get(vnic_mac->va_mh, nic_mac_addr); 2885084Sjohnlev 2895084Sjohnlev vnic_mac->va_active_flow = vnic_classifier_flow_create( 2905084Sjohnlev vnic_mac->va_addr_len, nic_mac_addr, NULL, B_TRUE, KM_SLEEP); 2915084Sjohnlev 2925084Sjohnlev vnic_classifier_flow_add(vnic_mac, vnic_mac->va_active_flow, 2935084Sjohnlev (vnic_rx_fn_t)mac_active_rx, vnic_mac->va_mh, NULL); 2945084Sjohnlev return (B_TRUE); 2955084Sjohnlev } 2965084Sjohnlev 2975084Sjohnlev static void 2985084Sjohnlev vnic_fini_active_rx(vnic_mac_t *vnic_mac) 2995084Sjohnlev { 3005084Sjohnlev if (vnic_mac->va_active_flow == NULL) 3015084Sjohnlev return; 3025084Sjohnlev 3035084Sjohnlev vnic_classifier_flow_remove(vnic_mac, vnic_mac->va_active_flow); 3045084Sjohnlev vnic_classifier_flow_destroy(vnic_mac->va_active_flow); 3055084Sjohnlev vnic_mac->va_active_flow = NULL; 3065084Sjohnlev } 3075084Sjohnlev 3085084Sjohnlev static void 3095084Sjohnlev vnic_update_active_rx(vnic_mac_t *vnic_mac) 3105084Sjohnlev { 3115084Sjohnlev if (vnic_mac->va_active_flow == NULL) 3125084Sjohnlev return; 3135084Sjohnlev 3145084Sjohnlev vnic_fini_active_rx(vnic_mac); 3155084Sjohnlev (void) vnic_init_active_rx(vnic_mac); 3165084Sjohnlev } 3175084Sjohnlev 3185084Sjohnlev /* 3195084Sjohnlev * Copy an mblk, preserving its hardware checksum flags. 3205084Sjohnlev */ 3215084Sjohnlev mblk_t * 3225084Sjohnlev vnic_copymsg_cksum(mblk_t *mp) 3235084Sjohnlev { 3245084Sjohnlev mblk_t *mp1; 3255084Sjohnlev uint32_t start, stuff, end, value, flags; 3265084Sjohnlev 3275084Sjohnlev mp1 = copymsg(mp); 3285084Sjohnlev if (mp1 == NULL) 3295084Sjohnlev return (NULL); 3305084Sjohnlev 3315084Sjohnlev hcksum_retrieve(mp, NULL, NULL, &start, &stuff, &end, &value, &flags); 3325084Sjohnlev (void) hcksum_assoc(mp1, NULL, NULL, start, stuff, end, value, 3335084Sjohnlev flags, KM_NOSLEEP); 3345084Sjohnlev 3355084Sjohnlev return (mp1); 3365084Sjohnlev } 3375084Sjohnlev 3385084Sjohnlev /* 3395084Sjohnlev * Copy an mblk chain, presenting the hardware checksum flags of the 3405084Sjohnlev * individual mblks. 3415084Sjohnlev */ 3425084Sjohnlev mblk_t * 3435084Sjohnlev vnic_copymsgchain_cksum(mblk_t *mp) 3445084Sjohnlev { 3455084Sjohnlev mblk_t *nmp = NULL; 3465084Sjohnlev mblk_t **nmpp = &nmp; 3475084Sjohnlev 3485084Sjohnlev for (; mp != NULL; mp = mp->b_next) { 3495084Sjohnlev if ((*nmpp = vnic_copymsg_cksum(mp)) == NULL) { 3505084Sjohnlev freemsgchain(nmp); 3515084Sjohnlev return (NULL); 3525084Sjohnlev } 3535084Sjohnlev 3545084Sjohnlev nmpp = &((*nmpp)->b_next); 3555084Sjohnlev } 3565084Sjohnlev 3575084Sjohnlev return (nmp); 3585084Sjohnlev } 3595084Sjohnlev 3605084Sjohnlev 3615084Sjohnlev /* 3625084Sjohnlev * Process the specified mblk chain for proper handling of hardware 3635084Sjohnlev * checksum offload. This routine is invoked for loopback VNIC traffic. 3645084Sjohnlev * The function handles a NULL mblk chain passed as argument. 3655084Sjohnlev */ 3665084Sjohnlev mblk_t * 3675084Sjohnlev vnic_fix_cksum(mblk_t *mp_chain) 3685084Sjohnlev { 3695084Sjohnlev mblk_t *mp, *prev = NULL, *new_chain = mp_chain, *mp1; 3705084Sjohnlev uint32_t flags, start, stuff, end, value; 3715084Sjohnlev 3725084Sjohnlev for (mp = mp_chain; mp != NULL; prev = mp, mp = mp->b_next) { 3735084Sjohnlev uint16_t len; 3745084Sjohnlev uint32_t offset; 3755084Sjohnlev struct ether_header *ehp; 3765084Sjohnlev uint16_t sap; 3775084Sjohnlev 3785084Sjohnlev hcksum_retrieve(mp, NULL, NULL, &start, &stuff, &end, &value, 3795084Sjohnlev &flags); 3805084Sjohnlev if (flags == 0) 3815084Sjohnlev continue; 3825084Sjohnlev 3835084Sjohnlev /* 3845084Sjohnlev * Since the processing of checksum offload for loopback 3855084Sjohnlev * traffic requires modification of the packet contents, 3865084Sjohnlev * ensure sure that we are always modifying our own copy. 3875084Sjohnlev */ 3885084Sjohnlev if (DB_REF(mp) > 1) { 3895084Sjohnlev mp1 = copymsg(mp); 3905084Sjohnlev if (mp1 == NULL) 3915084Sjohnlev continue; 3925084Sjohnlev mp1->b_next = mp->b_next; 3935084Sjohnlev mp->b_next = NULL; 3945084Sjohnlev freemsg(mp); 3955084Sjohnlev if (prev != NULL) 3965084Sjohnlev prev->b_next = mp1; 3975084Sjohnlev else 3985084Sjohnlev new_chain = mp1; 3995084Sjohnlev mp = mp1; 4005084Sjohnlev } 4015084Sjohnlev 4025084Sjohnlev /* 4035084Sjohnlev * Ethernet, and optionally VLAN header. 4045084Sjohnlev */ 4055084Sjohnlev /*LINTED*/ 4065084Sjohnlev ehp = (struct ether_header *)mp->b_rptr; 4075084Sjohnlev if (ntohs(ehp->ether_type) == VLAN_TPID) { 4085084Sjohnlev struct ether_vlan_header *evhp; 4095084Sjohnlev 4105084Sjohnlev ASSERT(MBLKL(mp) >= 4115084Sjohnlev sizeof (struct ether_vlan_header)); 4125084Sjohnlev /*LINTED*/ 4135084Sjohnlev evhp = (struct ether_vlan_header *)mp->b_rptr; 4145084Sjohnlev sap = ntohs(evhp->ether_type); 4155084Sjohnlev offset = sizeof (struct ether_vlan_header); 4165084Sjohnlev } else { 4175084Sjohnlev sap = ntohs(ehp->ether_type); 4185084Sjohnlev offset = sizeof (struct ether_header); 4195084Sjohnlev } 4205084Sjohnlev 4215084Sjohnlev if (MBLKL(mp) <= offset) { 4225084Sjohnlev offset -= MBLKL(mp); 4235084Sjohnlev if (mp->b_cont == NULL) { 4245084Sjohnlev /* corrupted packet, skip it */ 4255084Sjohnlev if (prev != NULL) 4265084Sjohnlev prev->b_next = mp->b_next; 4275084Sjohnlev else 4285084Sjohnlev new_chain = mp->b_next; 4295084Sjohnlev mp1 = mp->b_next; 4305084Sjohnlev mp->b_next = NULL; 4315084Sjohnlev freemsg(mp); 4325084Sjohnlev mp = mp1; 4335084Sjohnlev continue; 4345084Sjohnlev } 4355084Sjohnlev mp = mp->b_cont; 4365084Sjohnlev } 4375084Sjohnlev 4385084Sjohnlev if (flags & (HCK_FULLCKSUM | HCK_IPV4_HDRCKSUM)) { 4395084Sjohnlev ipha_t *ipha = NULL; 4405084Sjohnlev 4415084Sjohnlev /* 4425084Sjohnlev * In order to compute the full and header 4435084Sjohnlev * checksums, we need to find and parse 4445084Sjohnlev * the IP and/or ULP headers. 4455084Sjohnlev */ 4465084Sjohnlev 4475084Sjohnlev sap = (sap < ETHERTYPE_802_MIN) ? 0 : sap; 4485084Sjohnlev 4495084Sjohnlev /* 4505084Sjohnlev * IP header. 4515084Sjohnlev */ 4525084Sjohnlev if (sap != ETHERTYPE_IP) 4535084Sjohnlev continue; 4545084Sjohnlev 4555084Sjohnlev ASSERT(MBLKL(mp) >= offset + sizeof (ipha_t)); 4565084Sjohnlev /*LINTED*/ 4575084Sjohnlev ipha = (ipha_t *)(mp->b_rptr + offset); 4585084Sjohnlev 4595084Sjohnlev if (flags & HCK_FULLCKSUM) { 4605084Sjohnlev ipaddr_t src, dst; 4615084Sjohnlev uint32_t cksum; 4625084Sjohnlev uint16_t *up; 4635084Sjohnlev uint8_t proto; 4645084Sjohnlev 4655084Sjohnlev /* 4665084Sjohnlev * Pointer to checksum field in ULP header. 4675084Sjohnlev */ 4685084Sjohnlev proto = ipha->ipha_protocol; 4695084Sjohnlev ASSERT(ipha->ipha_version_and_hdr_length == 4705084Sjohnlev IP_SIMPLE_HDR_VERSION); 4715084Sjohnlev if (proto == IPPROTO_TCP) { 4725084Sjohnlev /*LINTED*/ 4735084Sjohnlev up = IPH_TCPH_CHECKSUMP(ipha, 4745084Sjohnlev IP_SIMPLE_HDR_LENGTH); 4755084Sjohnlev } else { 4765084Sjohnlev ASSERT(proto == IPPROTO_UDP); 4775084Sjohnlev /*LINTED*/ 4785084Sjohnlev up = IPH_UDPH_CHECKSUMP(ipha, 4795084Sjohnlev IP_SIMPLE_HDR_LENGTH); 4805084Sjohnlev } 4815084Sjohnlev 4825084Sjohnlev /* 4835084Sjohnlev * Pseudo-header checksum. 4845084Sjohnlev */ 4855084Sjohnlev src = ipha->ipha_src; 4865084Sjohnlev dst = ipha->ipha_dst; 4875084Sjohnlev len = ntohs(ipha->ipha_length) - 4885084Sjohnlev IP_SIMPLE_HDR_LENGTH; 4895084Sjohnlev 4905084Sjohnlev cksum = (dst >> 16) + (dst & 0xFFFF) + 4915084Sjohnlev (src >> 16) + (src & 0xFFFF); 4925084Sjohnlev cksum += htons(len); 4935084Sjohnlev 4945084Sjohnlev /* 4955084Sjohnlev * The checksum value stored in the packet needs 4965084Sjohnlev * to be correct. Compute it here. 4975084Sjohnlev */ 4985084Sjohnlev *up = 0; 4995084Sjohnlev cksum += (((proto) == IPPROTO_UDP) ? 5005084Sjohnlev IP_UDP_CSUM_COMP : IP_TCP_CSUM_COMP); 5015084Sjohnlev cksum = IP_CSUM(mp, IP_SIMPLE_HDR_LENGTH + 5025084Sjohnlev offset, cksum); 5035084Sjohnlev *(up) = (uint16_t)(cksum ? cksum : ~cksum); 5045084Sjohnlev 5055084Sjohnlev flags |= HCK_FULLCKSUM_OK; 5065084Sjohnlev value = 0xffff; 5075084Sjohnlev } 5085084Sjohnlev 5095084Sjohnlev if (flags & HCK_IPV4_HDRCKSUM) { 5105084Sjohnlev ASSERT(ipha != NULL); 5115084Sjohnlev ipha->ipha_hdr_checksum = 5125084Sjohnlev (uint16_t)ip_csum_hdr(ipha); 5135084Sjohnlev } 5145084Sjohnlev } 5155084Sjohnlev 5165084Sjohnlev if (flags & HCK_PARTIALCKSUM) { 5175084Sjohnlev uint16_t *up, partial, cksum; 5185084Sjohnlev uchar_t *ipp; /* ptr to beginning of IP header */ 5195084Sjohnlev 5205084Sjohnlev if (mp->b_cont != NULL) { 5215084Sjohnlev mblk_t *mp1; 5225084Sjohnlev 5235084Sjohnlev mp1 = msgpullup(mp, offset + end); 5245084Sjohnlev if (mp1 == NULL) 5255084Sjohnlev continue; 5265084Sjohnlev mp1->b_next = mp->b_next; 5275084Sjohnlev mp->b_next = NULL; 5285084Sjohnlev freemsg(mp); 5295084Sjohnlev if (prev != NULL) 5305084Sjohnlev prev->b_next = mp1; 5315084Sjohnlev else 5325084Sjohnlev new_chain = mp1; 5335084Sjohnlev mp = mp1; 5345084Sjohnlev } 5355084Sjohnlev 5365084Sjohnlev ipp = mp->b_rptr + offset; 5375084Sjohnlev /*LINTED*/ 5385084Sjohnlev up = (uint16_t *)((uchar_t *)ipp + stuff); 5395084Sjohnlev partial = *up; 5405084Sjohnlev *up = 0; 5415084Sjohnlev 5425084Sjohnlev cksum = IP_BCSUM_PARTIAL(mp->b_rptr + offset + start, 5435084Sjohnlev end - start, partial); 5445084Sjohnlev cksum = ~cksum; 5455084Sjohnlev *up = cksum ? cksum : ~cksum; 5465084Sjohnlev 5475084Sjohnlev /* 5485084Sjohnlev * Since we already computed the whole checksum, 5495084Sjohnlev * indicate to the stack that it has already 5505084Sjohnlev * been verified by the hardware. 5515084Sjohnlev */ 5525084Sjohnlev flags &= ~HCK_PARTIALCKSUM; 5535084Sjohnlev flags |= (HCK_FULLCKSUM | HCK_FULLCKSUM_OK); 5545084Sjohnlev value = 0xffff; 5555084Sjohnlev } 5565084Sjohnlev 5575084Sjohnlev (void) hcksum_assoc(mp, NULL, NULL, start, stuff, end, 5585084Sjohnlev value, flags, KM_NOSLEEP); 5595084Sjohnlev } 5605084Sjohnlev 5615084Sjohnlev return (new_chain); 5625084Sjohnlev } 5635084Sjohnlev 5645084Sjohnlev static void 5655084Sjohnlev vnic_mac_close(vnic_mac_t *vnic_mac) 5665084Sjohnlev { 5675084Sjohnlev mutex_enter(&vnic_mac_lock); 5685084Sjohnlev VNIC_MAC_REFRELE(vnic_mac); 5695084Sjohnlev mutex_exit(&vnic_mac_lock); 5705084Sjohnlev } 5715084Sjohnlev 5725084Sjohnlev static void 5735084Sjohnlev vnic_mac_free(vnic_mac_t *vnic_mac) 5745084Sjohnlev { 5755084Sjohnlev mod_hash_val_t val; 5765084Sjohnlev 5775084Sjohnlev ASSERT(MUTEX_HELD(&vnic_mac_lock)); 5785084Sjohnlev vnic_fini_active_rx(vnic_mac); 5795084Sjohnlev mac_notify_remove(vnic_mac->va_mh, vnic_mac->va_notify_hdl); 5805084Sjohnlev if (vnic_mac->va_mac_set) { 5815084Sjohnlev vnic_mac->va_mac_set = B_FALSE; 5825084Sjohnlev mac_vnic_clear(vnic_mac->va_mh); 5835084Sjohnlev } 5845084Sjohnlev vnic_classifier_flow_tab_fini(vnic_mac); 5855084Sjohnlev mac_close(vnic_mac->va_mh); 5865084Sjohnlev 5875084Sjohnlev (void) mod_hash_remove(vnic_mac_hash, 5885084Sjohnlev (mod_hash_key_t)vnic_mac->va_dev_name, &val); 5895084Sjohnlev ASSERT(vnic_mac == (vnic_mac_t *)val); 5905084Sjohnlev 5915084Sjohnlev kmem_cache_free(vnic_mac_cache, vnic_mac); 5925084Sjohnlev } 5935084Sjohnlev 5945084Sjohnlev /* 5955084Sjohnlev * Initial VNIC receive routine. Invoked for packets that are steered 5965084Sjohnlev * to a VNIC but the VNIC has not been started yet. 5975084Sjohnlev */ 5985084Sjohnlev /* ARGSUSED */ 5995084Sjohnlev static void 6005084Sjohnlev vnic_rx_initial(void *arg1, void *arg2, mblk_t *mp_chain) 6015084Sjohnlev { 6025084Sjohnlev vnic_t *vnic = arg1; 6035084Sjohnlev mblk_t *mp; 6045084Sjohnlev 6055084Sjohnlev /* update stats */ 6065084Sjohnlev for (mp = mp_chain; mp != NULL; mp = mp->b_next) 6075084Sjohnlev vnic->vn_stat_ierrors++; 6085084Sjohnlev freemsgchain(mp_chain); 6095084Sjohnlev } 6105084Sjohnlev 6115084Sjohnlev /* 6125084Sjohnlev * VNIC receive routine invoked after the classifier for the VNIC 6135084Sjohnlev * has been initialized and the VNIC has been started. 6145084Sjohnlev */ 6155084Sjohnlev /* ARGSUSED */ 6165084Sjohnlev void 6175084Sjohnlev vnic_rx(void *arg1, void *arg2, mblk_t *mp_chain) 6185084Sjohnlev { 6195084Sjohnlev vnic_t *vnic = arg1; 6205084Sjohnlev mblk_t *mp; 6215084Sjohnlev 6225084Sjohnlev /* update stats */ 6235084Sjohnlev for (mp = mp_chain; mp != NULL; mp = mp->b_next) { 6245084Sjohnlev vnic->vn_stat_ipackets++; 6255084Sjohnlev vnic->vn_stat_rbytes += msgdsize(mp); 6265084Sjohnlev } 6275084Sjohnlev 6285084Sjohnlev /* pass packet up */ 6295084Sjohnlev mac_rx(vnic->vn_mh, NULL, mp_chain); 6305084Sjohnlev } 6315084Sjohnlev 6325084Sjohnlev /* 6335084Sjohnlev * Routine to create a MAC-based VNIC. Adds the passed MAC address 6345084Sjohnlev * to an unused slot in the NIC if one is available. Otherwise it 6355084Sjohnlev * sets the NIC in promiscuous mode and assigns the MAC address to 6365084Sjohnlev * a Rx ring if available or a soft ring. 6375084Sjohnlev */ 6385084Sjohnlev static int 6395084Sjohnlev vnic_add_unicstaddr(vnic_t *vnic, mac_multi_addr_t *maddr) 6405084Sjohnlev { 6415084Sjohnlev vnic_mac_t *vnic_mac = vnic->vn_vnic_mac; 6425084Sjohnlev int err; 6435084Sjohnlev 6445638Sdme if (mac_unicst_verify(vnic_mac->va_mh, maddr->mma_addr, 6455638Sdme maddr->mma_addrlen) == B_FALSE) 6465638Sdme return (EINVAL); 6475638Sdme 6485084Sjohnlev if (mac_vnic_capab_get(vnic_mac->va_mh, MAC_CAPAB_MULTIADDRESS, 6495084Sjohnlev &(vnic->vn_mma_capab))) { 6505084Sjohnlev if (vnic->vn_maddr_naddrfree == 0) { 6515084Sjohnlev /* 6525084Sjohnlev * No free address slots available. 6535084Sjohnlev * Enable promiscuous mode. 6545084Sjohnlev */ 6555084Sjohnlev goto set_promisc; 6565084Sjohnlev } 6575084Sjohnlev 6585084Sjohnlev err = vnic->vn_maddr_add(vnic->vn_maddr_handle, maddr); 6595084Sjohnlev if (err != 0) { 6605084Sjohnlev if (err == ENOSPC) { 6615084Sjohnlev /* 6625084Sjohnlev * There was a race to add addresses 6635084Sjohnlev * with other multiple address consumers, 6645084Sjohnlev * and we lost out. Use promisc mode. 6655084Sjohnlev */ 6665084Sjohnlev goto set_promisc; 6675084Sjohnlev } 6685084Sjohnlev 6695084Sjohnlev return (err); 6705084Sjohnlev } 6715084Sjohnlev 6725084Sjohnlev vnic->vn_slot_id = maddr->mma_slot; 6735084Sjohnlev vnic->vn_multi_mac = B_TRUE; 6745084Sjohnlev } else { 6755084Sjohnlev /* 6765084Sjohnlev * Either multiple MAC address support is not 6775084Sjohnlev * available or all available addresses have 6785084Sjohnlev * been used up. 6795084Sjohnlev */ 6805084Sjohnlev set_promisc: 6815084Sjohnlev err = mac_promisc_set(vnic_mac->va_mh, B_TRUE, MAC_DEVPROMISC); 6825084Sjohnlev if (err != 0) { 6835084Sjohnlev return (err); 6845084Sjohnlev } 6855084Sjohnlev 6865084Sjohnlev vnic->vn_promisc_mac = B_TRUE; 6875084Sjohnlev } 6885084Sjohnlev return (err); 6895084Sjohnlev } 6905084Sjohnlev 6915084Sjohnlev /* 6925084Sjohnlev * VNIC is getting deleted. Remove the MAC address from the slot. 6935084Sjohnlev * If promiscuous mode was being used, then unset the promiscuous mode. 6945084Sjohnlev */ 6955084Sjohnlev static int 6965084Sjohnlev vnic_remove_unicstaddr(vnic_t *vnic) 6975084Sjohnlev { 6985084Sjohnlev vnic_mac_t *vnic_mac = vnic->vn_vnic_mac; 6995084Sjohnlev int err; 7005084Sjohnlev 7015084Sjohnlev if (vnic->vn_multi_mac) { 7025084Sjohnlev ASSERT(vnic->vn_promisc_mac == B_FALSE); 7035084Sjohnlev err = vnic->vn_maddr_remove(vnic->vn_maddr_handle, 7045084Sjohnlev vnic->vn_slot_id); 7055084Sjohnlev vnic->vn_multi_mac = B_FALSE; 7065084Sjohnlev } 7075084Sjohnlev 7085084Sjohnlev if (vnic->vn_promisc_mac) { 7095084Sjohnlev ASSERT(vnic->vn_multi_mac == B_FALSE); 7105084Sjohnlev err = mac_promisc_set(vnic_mac->va_mh, B_FALSE, MAC_DEVPROMISC); 7115084Sjohnlev vnic->vn_promisc_mac = B_FALSE; 7125084Sjohnlev } 7135084Sjohnlev 7145084Sjohnlev return (err); 7155084Sjohnlev } 7165084Sjohnlev 7175084Sjohnlev /* 7185084Sjohnlev * Create a new VNIC upon request from administrator. 7195084Sjohnlev * Returns 0 on success, an errno on failure. 7205084Sjohnlev */ 7215084Sjohnlev int 7225084Sjohnlev vnic_dev_create(uint_t vnic_id, char *dev_name, int mac_len, uchar_t *mac_addr) 7235084Sjohnlev { 7245084Sjohnlev vnic_t *vnic = NULL; 7255084Sjohnlev mac_register_t *mac; 7265084Sjohnlev int err; 7275084Sjohnlev vnic_mac_t *vnic_mac; 7285084Sjohnlev const mac_info_t *lower_mac_info; 7295084Sjohnlev mac_multi_addr_t maddr; 7305084Sjohnlev mac_txinfo_t tx_info; 7315084Sjohnlev 7325084Sjohnlev if (mac_len != ETHERADDRL) { 7335084Sjohnlev /* currently only ethernet NICs are supported */ 7345084Sjohnlev return (EINVAL); 7355084Sjohnlev } 7365084Sjohnlev 7375084Sjohnlev rw_enter(&vnic_lock, RW_WRITER); 7385084Sjohnlev 7395084Sjohnlev /* does a VNIC with the same id already exist? */ 7405084Sjohnlev err = mod_hash_find(vnic_hash, VNIC_HASH_KEY(vnic_id), 7415084Sjohnlev (mod_hash_val_t *)&vnic); 7425084Sjohnlev if (err == 0) { 7435084Sjohnlev rw_exit(&vnic_lock); 7445084Sjohnlev return (EEXIST); 7455084Sjohnlev } 7465084Sjohnlev 7475084Sjohnlev vnic = kmem_cache_alloc(vnic_cache, KM_NOSLEEP); 7485084Sjohnlev if (vnic == NULL) { 7495084Sjohnlev rw_exit(&vnic_lock); 7505084Sjohnlev return (ENOMEM); 7515084Sjohnlev } 7525084Sjohnlev 7535084Sjohnlev /* open underlying MAC */ 7545084Sjohnlev err = vnic_mac_open(dev_name, &vnic_mac); 7555084Sjohnlev if (err != 0) { 7565084Sjohnlev kmem_cache_free(vnic_cache, vnic); 7575084Sjohnlev rw_exit(&vnic_lock); 7585084Sjohnlev return (err); 7595084Sjohnlev } 7605084Sjohnlev 7615084Sjohnlev bzero(vnic, sizeof (*vnic)); 7625084Sjohnlev vnic->vn_id = vnic_id; 7635084Sjohnlev vnic->vn_vnic_mac = vnic_mac; 7645084Sjohnlev 7655084Sjohnlev vnic->vn_started = B_FALSE; 7665084Sjohnlev vnic->vn_promisc = B_FALSE; 7675084Sjohnlev vnic->vn_multi_mac = B_FALSE; 7685084Sjohnlev vnic->vn_bcast_grp = B_FALSE; 7695084Sjohnlev 7705084Sjohnlev /* set the VNIC MAC address */ 7715084Sjohnlev maddr.mma_addrlen = mac_len; 7725084Sjohnlev maddr.mma_slot = 0; 7735084Sjohnlev maddr.mma_flags = 0; 7745084Sjohnlev bcopy(mac_addr, maddr.mma_addr, mac_len); 7755084Sjohnlev if ((err = vnic_add_unicstaddr(vnic, &maddr)) != 0) 7765084Sjohnlev goto bail; 7775084Sjohnlev bcopy(mac_addr, vnic->vn_addr, mac_len); 7785084Sjohnlev 7795084Sjohnlev /* set the initial VNIC capabilities */ 7805084Sjohnlev if (!mac_vnic_capab_get(vnic_mac->va_mh, MAC_CAPAB_HCKSUM, 7815084Sjohnlev &vnic->vn_hcksum_txflags)) 7825084Sjohnlev vnic->vn_hcksum_txflags = 0; 7835084Sjohnlev 7845084Sjohnlev /* register with the MAC module */ 7855084Sjohnlev if ((mac = mac_alloc(MAC_VERSION)) == NULL) 7865084Sjohnlev goto bail; 7875084Sjohnlev 7885084Sjohnlev mac->m_type_ident = MAC_PLUGIN_IDENT_ETHER; 7895084Sjohnlev mac->m_driver = vnic; 7905084Sjohnlev mac->m_dip = vnic_get_dip(); 7915084Sjohnlev mac->m_instance = vnic_id; 7925084Sjohnlev mac->m_src_addr = vnic->vn_addr; 7935084Sjohnlev mac->m_callbacks = &vnic_m_callbacks; 7945084Sjohnlev 7955084Sjohnlev lower_mac_info = mac_info(vnic_mac->va_mh); 7965084Sjohnlev mac->m_min_sdu = lower_mac_info->mi_sdu_min; 7975084Sjohnlev mac->m_max_sdu = lower_mac_info->mi_sdu_max; 7985084Sjohnlev 7995084Sjohnlev err = mac_register(mac, &vnic->vn_mh); 8005084Sjohnlev mac_free(mac); 8015084Sjohnlev if (err != 0) 8025084Sjohnlev goto bail; 8035084Sjohnlev 8045084Sjohnlev /* add new VNIC to hash table */ 8055084Sjohnlev err = mod_hash_insert(vnic_hash, VNIC_HASH_KEY(vnic_id), 8065084Sjohnlev (mod_hash_val_t)vnic); 8075084Sjohnlev ASSERT(err == 0); 8085084Sjohnlev vnic_count++; 8095084Sjohnlev 8105084Sjohnlev rw_exit(&vnic_lock); 8115084Sjohnlev 8125084Sjohnlev /* Create a flow, initialized with the MAC address of the VNIC */ 8135084Sjohnlev if ((vnic->vn_flow_ent = vnic_classifier_flow_create(mac_len, mac_addr, 8145084Sjohnlev NULL, B_FALSE, KM_SLEEP)) == NULL) { 8155084Sjohnlev (void) vnic_dev_delete(vnic_id); 8165084Sjohnlev vnic = NULL; 8175084Sjohnlev err = ENOMEM; 8185084Sjohnlev goto bail_unlocked; 8195084Sjohnlev } 8205084Sjohnlev 8215084Sjohnlev vnic_classifier_flow_add(vnic_mac, vnic->vn_flow_ent, vnic_rx_initial, 8225084Sjohnlev vnic, vnic); 8235084Sjohnlev 8245084Sjohnlev /* setup VNIC to receive broadcast packets */ 8255084Sjohnlev err = vnic_bcast_add(vnic, vnic_brdcst_mac, MAC_ADDRTYPE_BROADCAST); 8265084Sjohnlev if (err != 0) { 8275084Sjohnlev (void) vnic_dev_delete(vnic_id); 8285084Sjohnlev vnic = NULL; 8295084Sjohnlev goto bail_unlocked; 8305084Sjohnlev } 8315084Sjohnlev vnic->vn_bcast_grp = B_TRUE; 8325084Sjohnlev 8335084Sjohnlev mutex_enter(&vnic_mac_lock); 8345084Sjohnlev if (!vnic_mac->va_mac_set) { 8355084Sjohnlev /* 8365084Sjohnlev * We want to MAC layer to call the VNIC tx outbound 8375084Sjohnlev * routine, so that local broadcast packets sent by 8385084Sjohnlev * the active interface sharing the underlying NIC (if 8395084Sjohnlev * any), can be broadcast to every VNIC. 8405084Sjohnlev */ 8415084Sjohnlev tx_info.mt_fn = vnic_active_tx; 8425084Sjohnlev tx_info.mt_arg = vnic_mac; 8435084Sjohnlev if (!mac_vnic_set(vnic_mac->va_mh, &tx_info, 8445084Sjohnlev vnic_m_capab_get, vnic)) { 8455084Sjohnlev mutex_exit(&vnic_mac_lock); 8465084Sjohnlev (void) vnic_dev_delete(vnic_id); 8475084Sjohnlev vnic = NULL; 8485084Sjohnlev err = EBUSY; 8495084Sjohnlev goto bail_unlocked; 8505084Sjohnlev } 8515084Sjohnlev vnic_mac->va_mac_set = B_TRUE; 8525084Sjohnlev } 8535084Sjohnlev mutex_exit(&vnic_mac_lock); 8545084Sjohnlev 8555084Sjohnlev /* allow passing packets to NIC's active MAC client */ 8565084Sjohnlev if (!vnic_init_active_rx(vnic_mac)) { 8575084Sjohnlev (void) vnic_dev_delete(vnic_id); 8585084Sjohnlev vnic = NULL; 8595084Sjohnlev err = ENOMEM; 8605084Sjohnlev goto bail_unlocked; 8615084Sjohnlev } 8625084Sjohnlev 8635084Sjohnlev return (0); 8645084Sjohnlev 8655084Sjohnlev bail: 8665084Sjohnlev (void) vnic_remove_unicstaddr(vnic); 8675084Sjohnlev vnic_mac_close(vnic_mac); 8685084Sjohnlev rw_exit(&vnic_lock); 8695084Sjohnlev 8705084Sjohnlev bail_unlocked: 8715084Sjohnlev if (vnic != NULL) { 8725084Sjohnlev kmem_cache_free(vnic_cache, vnic); 8735084Sjohnlev } 8745084Sjohnlev 8755084Sjohnlev return (err); 8765084Sjohnlev } 8775084Sjohnlev 8785084Sjohnlev /* 8795084Sjohnlev * Modify the properties of an existing VNIC. 8805084Sjohnlev */ 8815084Sjohnlev /* ARGSUSED */ 8825084Sjohnlev int 8835084Sjohnlev vnic_dev_modify(uint_t vnic_id, uint_t modify_mask, 8845084Sjohnlev vnic_mac_addr_type_t mac_addr_type, uint_t mac_len, uchar_t *mac_addr) 8855084Sjohnlev { 8865084Sjohnlev vnic_t *vnic = NULL; 8875084Sjohnlev int rv = 0; 8885084Sjohnlev boolean_t notify_mac_addr = B_FALSE; 8895084Sjohnlev 8905084Sjohnlev rw_enter(&vnic_lock, RW_WRITER); 8915084Sjohnlev 8925084Sjohnlev if (mod_hash_find(vnic_hash, VNIC_HASH_KEY(vnic_id), 8935084Sjohnlev (mod_hash_val_t *)&vnic) != 0) { 8945084Sjohnlev rw_exit(&vnic_lock); 8955084Sjohnlev return (ENOENT); 8965084Sjohnlev } 8975084Sjohnlev 8985084Sjohnlev if (modify_mask & VNIC_IOC_MODIFY_ADDR) { 8995084Sjohnlev rv = vnic_modify_mac_addr(vnic, mac_len, mac_addr); 9005084Sjohnlev if (rv == 0) 9015084Sjohnlev notify_mac_addr = B_TRUE; 9025084Sjohnlev } 9035084Sjohnlev 9045084Sjohnlev rw_exit(&vnic_lock); 9055084Sjohnlev 9065084Sjohnlev if (notify_mac_addr) 9075084Sjohnlev mac_unicst_update(vnic->vn_mh, mac_addr); 9085084Sjohnlev 9095084Sjohnlev return (rv); 9105084Sjohnlev } 9115084Sjohnlev 9125084Sjohnlev int 9135084Sjohnlev vnic_dev_delete(uint_t vnic_id) 9145084Sjohnlev { 9155084Sjohnlev vnic_t *vnic = NULL; 9165084Sjohnlev mod_hash_val_t val; 9175084Sjohnlev vnic_flow_t *flent; 9185084Sjohnlev int rc; 9195702Sdme vnic_mac_t *vnic_mac; 9205084Sjohnlev 9215084Sjohnlev rw_enter(&vnic_lock, RW_WRITER); 9225084Sjohnlev 9235084Sjohnlev if (mod_hash_find(vnic_hash, VNIC_HASH_KEY(vnic_id), 9245084Sjohnlev (mod_hash_val_t *)&vnic) != 0) { 9255084Sjohnlev rw_exit(&vnic_lock); 9265084Sjohnlev return (ENOENT); 9275084Sjohnlev } 9285084Sjohnlev 9295084Sjohnlev /* 9305084Sjohnlev * We cannot unregister the MAC yet. Unregistering would 9315084Sjohnlev * free up mac_impl_t which should not happen at this time. 9325084Sjohnlev * Packets could be entering vnic_rx() through the 9335084Sjohnlev * flow entry and so mac_impl_t cannot be NULL. So disable 9345084Sjohnlev * mac_impl_t by calling mac_disable(). This will prevent any 9355084Sjohnlev * new claims on mac_impl_t. 9365084Sjohnlev */ 9375084Sjohnlev if (mac_disable(vnic->vn_mh) != 0) { 9385084Sjohnlev rw_exit(&vnic_lock); 9395084Sjohnlev return (EBUSY); 9405084Sjohnlev } 9415084Sjohnlev 9425084Sjohnlev (void) mod_hash_remove(vnic_hash, VNIC_HASH_KEY(vnic_id), &val); 9435084Sjohnlev ASSERT(vnic == (vnic_t *)val); 9445084Sjohnlev 9455084Sjohnlev if (vnic->vn_bcast_grp) 9465084Sjohnlev (void) vnic_bcast_delete(vnic, vnic_brdcst_mac); 9475084Sjohnlev 9485084Sjohnlev flent = vnic->vn_flow_ent; 9495084Sjohnlev if (flent != NULL) { 9505084Sjohnlev /* 9515084Sjohnlev * vnic_classifier_flow_destroy() ensures that the 9525084Sjohnlev * flow is no longer used. 9535084Sjohnlev */ 9545084Sjohnlev vnic_classifier_flow_remove(vnic->vn_vnic_mac, flent); 9555084Sjohnlev vnic_classifier_flow_destroy(flent); 9565084Sjohnlev } 9575084Sjohnlev 9585084Sjohnlev rc = mac_unregister(vnic->vn_mh); 9595084Sjohnlev ASSERT(rc == 0); 9605084Sjohnlev (void) vnic_remove_unicstaddr(vnic); 9615702Sdme vnic_mac = vnic->vn_vnic_mac; 9625084Sjohnlev kmem_cache_free(vnic_cache, vnic); 9635084Sjohnlev vnic_count--; 9645084Sjohnlev rw_exit(&vnic_lock); 9655702Sdme vnic_mac_close(vnic_mac); 9665084Sjohnlev return (0); 9675084Sjohnlev } 9685084Sjohnlev 9695084Sjohnlev /* 9705084Sjohnlev * For the specified packet chain, return a sub-chain to be sent 9715084Sjohnlev * and the transmit function to be used to send the packet. Also 9725084Sjohnlev * return a pointer to the sub-chain of packets that should 9735084Sjohnlev * be re-classified. If the function returns NULL, the packet 9745084Sjohnlev * should be sent using the underlying NIC. 9755084Sjohnlev */ 9765084Sjohnlev static vnic_flow_t * 9775084Sjohnlev vnic_classify(vnic_mac_t *vnic_mac, mblk_t *mp, mblk_t **mp_chain_rest) 9785084Sjohnlev { 9795084Sjohnlev vnic_flow_t *flow_ent; 9805084Sjohnlev 9815084Sjohnlev /* one packet at a time */ 9825084Sjohnlev *mp_chain_rest = mp->b_next; 9835084Sjohnlev mp->b_next = NULL; 9845084Sjohnlev 9855084Sjohnlev /* do classification on the packet */ 9865084Sjohnlev flow_ent = vnic_classifier_get_flow(vnic_mac, mp); 9875084Sjohnlev 9885084Sjohnlev return (flow_ent); 9895084Sjohnlev } 9905084Sjohnlev 9915084Sjohnlev /* 9925084Sjohnlev * Send a packet chain to a local VNIC or an active MAC client. 9935084Sjohnlev */ 9945084Sjohnlev static void 9955084Sjohnlev vnic_local_tx(vnic_mac_t *vnic_mac, vnic_flow_t *flow_ent, mblk_t *mp_chain) 9965084Sjohnlev { 9975084Sjohnlev mblk_t *mp1; 9985084Sjohnlev const vnic_flow_fn_info_t *fn_info; 9995084Sjohnlev vnic_t *vnic; 10005084Sjohnlev 10015084Sjohnlev if (!vnic_classifier_is_active(flow_ent) && 10025084Sjohnlev mac_promisc_get(vnic_mac->va_mh, MAC_PROMISC)) { 10035084Sjohnlev /* 10045084Sjohnlev * If the MAC is in promiscous mode, 10055084Sjohnlev * send a copy of the active client. 10065084Sjohnlev */ 10075084Sjohnlev if ((mp1 = vnic_copymsgchain_cksum(mp_chain)) == NULL) 10085084Sjohnlev goto sendit; 10095084Sjohnlev if ((mp1 = vnic_fix_cksum(mp1)) == NULL) 10105084Sjohnlev goto sendit; 10115084Sjohnlev mac_active_rx(vnic_mac->va_mh, NULL, mp1); 10125084Sjohnlev } 10135084Sjohnlev sendit: 10145084Sjohnlev fn_info = vnic_classifier_get_fn_info(flow_ent); 10155084Sjohnlev /* 10165084Sjohnlev * If the vnic to which we would deliver this packet is in 10175084Sjohnlev * promiscuous mode then it already received the packet via 10185084Sjohnlev * vnic_promisc_rx(). 10195084Sjohnlev * 10205084Sjohnlev * XXX assumes that ff_arg2 is a vnic_t pointer if it is 10215084Sjohnlev * non-NULL (currently always true). 10225084Sjohnlev */ 10235084Sjohnlev vnic = (vnic_t *)fn_info->ff_arg2; 10245084Sjohnlev if ((vnic != NULL) && vnic->vn_promisc) 10255084Sjohnlev freemsg(mp_chain); 10265084Sjohnlev else if ((mp1 = vnic_fix_cksum(mp_chain)) != NULL) 10275084Sjohnlev (fn_info->ff_fn)(fn_info->ff_arg1, fn_info->ff_arg2, mp1); 10285084Sjohnlev } 10295084Sjohnlev 10305084Sjohnlev /* 10315084Sjohnlev * This function is invoked when a MAC client needs to send a packet 10325084Sjohnlev * to a NIC which is shared by VNICs. It is passed to the MAC layer 10335084Sjohnlev * by a call to mac_vnic_set() when the NIC is opened, and is returned 10345084Sjohnlev * to MAC clients by mac_tx_get() when VNICs are present. 10355084Sjohnlev */ 10365084Sjohnlev mblk_t * 10375084Sjohnlev vnic_active_tx(void *arg, mblk_t *mp_chain) 10385084Sjohnlev { 10395084Sjohnlev vnic_mac_t *vnic_mac = arg; 10405084Sjohnlev mblk_t *mp, *extra_mp = NULL; 10415084Sjohnlev vnic_flow_t *flow_ent; 10425084Sjohnlev void *flow_cookie; 10435084Sjohnlev const mac_txinfo_t *mtp = vnic_mac->va_txinfo; 10445084Sjohnlev 10455084Sjohnlev for (mp = mp_chain; mp != NULL; mp = extra_mp) { 10465084Sjohnlev mblk_t *next; 10475084Sjohnlev 10485084Sjohnlev next = mp->b_next; 10495084Sjohnlev mp->b_next = NULL; 10505084Sjohnlev 10515084Sjohnlev vnic_promisc_rx(vnic_mac, (vnic_t *)-1, mp); 10525084Sjohnlev 10535084Sjohnlev flow_ent = vnic_classify(vnic_mac, mp, &extra_mp); 10545084Sjohnlev ASSERT(extra_mp == NULL); 10555084Sjohnlev extra_mp = next; 10565084Sjohnlev 10575084Sjohnlev if (flow_ent != NULL) { 10585084Sjohnlev flow_cookie = vnic_classifier_get_client_cookie( 10595084Sjohnlev flow_ent); 10605084Sjohnlev if (flow_cookie != NULL) { 10615084Sjohnlev /* 10625084Sjohnlev * Send a copy to every VNIC defined on the 10635084Sjohnlev * interface, as well as the underlying MAC. 10645084Sjohnlev */ 10655084Sjohnlev vnic_bcast_send(flow_cookie, (vnic_t *)-1, mp); 10665084Sjohnlev } else { 10675084Sjohnlev /* 10685084Sjohnlev * loopback the packet to a local VNIC or 10695084Sjohnlev * an active MAC client. 10705084Sjohnlev */ 10715084Sjohnlev vnic_local_tx(vnic_mac, flow_ent, mp); 10725084Sjohnlev } 10735084Sjohnlev VNIC_FLOW_REFRELE(flow_ent); 10745084Sjohnlev mp_chain = NULL; 10755084Sjohnlev } else { 10765084Sjohnlev /* 10775084Sjohnlev * Non-VNIC destination, send via the underlying 10785084Sjohnlev * NIC. In order to avoid a recursive call 10795084Sjohnlev * to this function, we ensured that mtp points 10805084Sjohnlev * to the unerlying NIC transmit function 10815084Sjohnlev * by inilizating through mac_vnic_tx_get(). 10825084Sjohnlev */ 10835084Sjohnlev mp_chain = mtp->mt_fn(mtp->mt_arg, mp); 10845084Sjohnlev if (mp_chain != NULL) 10855084Sjohnlev break; 10865084Sjohnlev } 10875084Sjohnlev } 10885084Sjohnlev 10895084Sjohnlev if ((mp_chain != NULL) && (extra_mp != NULL)) { 10905084Sjohnlev ASSERT(mp_chain->b_next == NULL); 10915084Sjohnlev mp_chain->b_next = extra_mp; 10925084Sjohnlev } 10935084Sjohnlev return (mp_chain); 10945084Sjohnlev } 10955084Sjohnlev 10965084Sjohnlev /* 10975084Sjohnlev * VNIC transmit function. 10985084Sjohnlev */ 10995084Sjohnlev mblk_t * 11005084Sjohnlev vnic_m_tx(void *arg, mblk_t *mp_chain) 11015084Sjohnlev { 11025084Sjohnlev vnic_t *vnic = arg; 11035084Sjohnlev vnic_mac_t *vnic_mac = vnic->vn_vnic_mac; 11045084Sjohnlev mblk_t *mp, *extra_mp = NULL; 11055084Sjohnlev vnic_flow_t *flow_ent; 11065084Sjohnlev void *flow_cookie; 11075084Sjohnlev 11085084Sjohnlev /* 11095084Sjohnlev * Update stats. 11105084Sjohnlev */ 11115084Sjohnlev for (mp = mp_chain; mp != NULL; mp = mp->b_next) { 11125084Sjohnlev vnic->vn_stat_opackets++; 11135084Sjohnlev vnic->vn_stat_obytes += msgdsize(mp); 11145084Sjohnlev } 11155084Sjohnlev 11165084Sjohnlev for (mp = mp_chain; mp != NULL; mp = extra_mp) { 11175084Sjohnlev mblk_t *next; 11185084Sjohnlev 11195084Sjohnlev next = mp->b_next; 11205084Sjohnlev mp->b_next = NULL; 11215084Sjohnlev 11225084Sjohnlev vnic_promisc_rx(vnic->vn_vnic_mac, vnic, mp); 11235084Sjohnlev 11245084Sjohnlev flow_ent = vnic_classify(vnic->vn_vnic_mac, mp, &extra_mp); 11255084Sjohnlev ASSERT(extra_mp == NULL); 11265084Sjohnlev extra_mp = next; 11275084Sjohnlev 11285084Sjohnlev if (flow_ent != NULL) { 11295084Sjohnlev flow_cookie = vnic_classifier_get_client_cookie( 11305084Sjohnlev flow_ent); 11315084Sjohnlev if (flow_cookie != NULL) { 11325084Sjohnlev /* 11335084Sjohnlev * The vnic_bcast_send function expects 11345084Sjohnlev * to receive the sender VNIC as value 11355084Sjohnlev * for arg2. 11365084Sjohnlev */ 11375084Sjohnlev vnic_bcast_send(flow_cookie, vnic, mp); 11385084Sjohnlev } else { 11395084Sjohnlev /* 11405084Sjohnlev * loopback the packet to a local VNIC or 11415084Sjohnlev * an active MAC client. 11425084Sjohnlev */ 11435084Sjohnlev vnic_local_tx(vnic_mac, flow_ent, mp); 11445084Sjohnlev } 11455084Sjohnlev VNIC_FLOW_REFRELE(flow_ent); 11465084Sjohnlev mp_chain = NULL; 11475084Sjohnlev } else { 11485084Sjohnlev /* 11495084Sjohnlev * Non-local destination, send via the underlying 11505084Sjohnlev * NIC. 11515084Sjohnlev */ 11525084Sjohnlev const mac_txinfo_t *mtp = vnic->vn_txinfo; 11535084Sjohnlev mp_chain = mtp->mt_fn(mtp->mt_arg, mp); 11545084Sjohnlev if (mp_chain != NULL) 11555084Sjohnlev break; 11565084Sjohnlev } 11575084Sjohnlev } 11585084Sjohnlev 11595084Sjohnlev /* update stats to account for unsent packets */ 11605084Sjohnlev for (mp = mp_chain; mp != NULL; mp = mp->b_next) { 11615084Sjohnlev vnic->vn_stat_opackets--; 11625084Sjohnlev vnic->vn_stat_obytes -= msgdsize(mp); 11635084Sjohnlev vnic->vn_stat_oerrors++; 11645084Sjohnlev /* 11655084Sjohnlev * link back in the last portion not counted due to bandwidth 11665084Sjohnlev * control. 11675084Sjohnlev */ 11685084Sjohnlev if (mp->b_next == NULL) { 11695084Sjohnlev mp->b_next = extra_mp; 11705084Sjohnlev break; 11715084Sjohnlev } 11725084Sjohnlev } 11735084Sjohnlev 11745084Sjohnlev return (mp_chain); 11755084Sjohnlev } 11765084Sjohnlev 11775084Sjohnlev /* ARGSUSED */ 11785084Sjohnlev static void 11795084Sjohnlev vnic_m_resources(void *arg) 11805084Sjohnlev { 11815084Sjohnlev /* no resources to advertise */ 11825084Sjohnlev } 11835084Sjohnlev 11845084Sjohnlev static int 11855084Sjohnlev vnic_m_stat(void *arg, uint_t stat, uint64_t *val) 11865084Sjohnlev { 11875084Sjohnlev vnic_t *vnic = arg; 11885084Sjohnlev int rval = 0; 11895084Sjohnlev 11905084Sjohnlev rw_enter(&vnic_lock, RW_READER); 11915084Sjohnlev 11925084Sjohnlev switch (stat) { 11935084Sjohnlev case ETHER_STAT_LINK_DUPLEX: 11945084Sjohnlev *val = mac_stat_get(vnic->vn_vnic_mac->va_mh, 11955084Sjohnlev ETHER_STAT_LINK_DUPLEX); 11965084Sjohnlev break; 11975084Sjohnlev case MAC_STAT_IFSPEED: 11985084Sjohnlev *val = mac_stat_get(vnic->vn_vnic_mac->va_mh, 11995084Sjohnlev MAC_STAT_IFSPEED); 12005084Sjohnlev break; 12015084Sjohnlev case MAC_STAT_MULTIRCV: 12025084Sjohnlev *val = vnic->vn_stat_multircv; 12035084Sjohnlev break; 12045084Sjohnlev case MAC_STAT_BRDCSTRCV: 12055084Sjohnlev *val = vnic->vn_stat_brdcstrcv; 12065084Sjohnlev break; 12075084Sjohnlev case MAC_STAT_MULTIXMT: 12085084Sjohnlev *val = vnic->vn_stat_multixmt; 12095084Sjohnlev break; 12105084Sjohnlev case MAC_STAT_BRDCSTXMT: 12115084Sjohnlev *val = vnic->vn_stat_brdcstxmt; 12125084Sjohnlev break; 12135084Sjohnlev case MAC_STAT_IERRORS: 12145084Sjohnlev *val = vnic->vn_stat_ierrors; 12155084Sjohnlev break; 12165084Sjohnlev case MAC_STAT_OERRORS: 12175084Sjohnlev *val = vnic->vn_stat_oerrors; 12185084Sjohnlev break; 12195084Sjohnlev case MAC_STAT_RBYTES: 12205084Sjohnlev *val = vnic->vn_stat_rbytes; 12215084Sjohnlev break; 12225084Sjohnlev case MAC_STAT_IPACKETS: 12235084Sjohnlev *val = vnic->vn_stat_ipackets; 12245084Sjohnlev break; 12255084Sjohnlev case MAC_STAT_OBYTES: 12265084Sjohnlev *val = vnic->vn_stat_obytes; 12275084Sjohnlev break; 12285084Sjohnlev case MAC_STAT_OPACKETS: 12295084Sjohnlev *val = vnic->vn_stat_opackets; 12305084Sjohnlev break; 12315084Sjohnlev default: 12325084Sjohnlev rval = ENOTSUP; 12335084Sjohnlev } 12345084Sjohnlev 12355084Sjohnlev rw_exit(&vnic_lock); 12365084Sjohnlev return (rval); 12375084Sjohnlev } 12385084Sjohnlev 12395084Sjohnlev /* 12405084Sjohnlev * Return information about the specified capability. 12415084Sjohnlev */ 12425084Sjohnlev /* ARGSUSED */ 12435084Sjohnlev static boolean_t 12445084Sjohnlev vnic_m_capab_get(void *arg, mac_capab_t cap, void *cap_data) 12455084Sjohnlev { 12465084Sjohnlev vnic_t *vnic = arg; 12475084Sjohnlev 12485084Sjohnlev switch (cap) { 12495084Sjohnlev case MAC_CAPAB_POLL: 12505084Sjohnlev return (B_TRUE); 12515084Sjohnlev case MAC_CAPAB_HCKSUM: { 12525084Sjohnlev uint32_t *hcksum_txflags = cap_data; 12535084Sjohnlev 12545084Sjohnlev *hcksum_txflags = vnic->vn_hcksum_txflags & 12555084Sjohnlev (HCKSUM_INET_FULL_V4 | HCKSUM_IPHDRCKSUM | 12565084Sjohnlev HCKSUM_INET_PARTIAL); 12575084Sjohnlev break; 12585084Sjohnlev } 12595084Sjohnlev default: 12605084Sjohnlev return (B_FALSE); 12615084Sjohnlev } 12625084Sjohnlev return (B_TRUE); 12635084Sjohnlev } 12645084Sjohnlev 12655084Sjohnlev static int 12665084Sjohnlev vnic_m_start(void *arg) 12675084Sjohnlev { 12685084Sjohnlev vnic_t *vnic = arg; 12695084Sjohnlev mac_handle_t lower_mh = vnic->vn_vnic_mac->va_mh; 12705084Sjohnlev int rc; 12715084Sjohnlev 12725084Sjohnlev rc = mac_start(lower_mh); 12735084Sjohnlev if (rc != 0) 12745084Sjohnlev return (rc); 12755084Sjohnlev 12765084Sjohnlev vnic_classifier_flow_update_fn(vnic->vn_flow_ent, vnic_rx, vnic, vnic); 12775084Sjohnlev return (0); 12785084Sjohnlev } 12795084Sjohnlev 12805084Sjohnlev static void 12815084Sjohnlev vnic_m_stop(void *arg) 12825084Sjohnlev { 12835084Sjohnlev vnic_t *vnic = arg; 12845084Sjohnlev mac_handle_t lower_mh = vnic->vn_vnic_mac->va_mh; 12855084Sjohnlev 12865084Sjohnlev vnic_classifier_flow_update_fn(vnic->vn_flow_ent, vnic_rx_initial, 12875084Sjohnlev vnic, vnic); 12885084Sjohnlev mac_stop(lower_mh); 12895084Sjohnlev } 12905084Sjohnlev 12915084Sjohnlev /* ARGSUSED */ 12925084Sjohnlev static int 12935084Sjohnlev vnic_m_promisc(void *arg, boolean_t on) 12945084Sjohnlev { 12955084Sjohnlev vnic_t *vnic = arg; 12965084Sjohnlev 12975084Sjohnlev return (vnic_promisc_set(vnic, on)); 12985084Sjohnlev } 12995084Sjohnlev 13005084Sjohnlev static int 13015084Sjohnlev vnic_m_multicst(void *arg, boolean_t add, const uint8_t *addrp) 13025084Sjohnlev { 13035084Sjohnlev vnic_t *vnic = arg; 13045084Sjohnlev int rc = 0; 13055084Sjohnlev 13065084Sjohnlev if (add) 13075084Sjohnlev rc = vnic_bcast_add(vnic, addrp, MAC_ADDRTYPE_MULTICAST); 13085084Sjohnlev else 13095084Sjohnlev vnic_bcast_delete(vnic, addrp); 13105084Sjohnlev 13115084Sjohnlev return (rc); 13125084Sjohnlev } 13135084Sjohnlev 13145084Sjohnlev static int 13155084Sjohnlev vnic_m_unicst(void *arg, const uint8_t *mac_addr) 13165084Sjohnlev { 13175084Sjohnlev vnic_t *vnic = arg; 13185084Sjohnlev vnic_mac_t *vnic_mac = vnic->vn_vnic_mac; 13195084Sjohnlev int rv; 13205084Sjohnlev 13215084Sjohnlev rw_enter(&vnic_lock, RW_WRITER); 13225084Sjohnlev rv = vnic_modify_mac_addr(vnic, vnic_mac->va_addr_len, 13235084Sjohnlev (uchar_t *)mac_addr); 13245084Sjohnlev rw_exit(&vnic_lock); 13255084Sjohnlev 13265084Sjohnlev if (rv == 0) 13275084Sjohnlev mac_unicst_update(vnic->vn_mh, mac_addr); 13285084Sjohnlev return (0); 13295084Sjohnlev } 13305084Sjohnlev 13315084Sjohnlev int 13325084Sjohnlev vnic_info(uint_t *nvnics, uint32_t vnic_id, char *dev_name, void *fn_arg, 13335084Sjohnlev vnic_info_new_vnic_fn_t new_vnic_fn) 13345084Sjohnlev { 13355084Sjohnlev vnic_info_state_t state; 13365084Sjohnlev int rc = 0; 13375084Sjohnlev 13385084Sjohnlev rw_enter(&vnic_lock, RW_READER); 13395084Sjohnlev 13405084Sjohnlev *nvnics = vnic_count; 13415084Sjohnlev 13425084Sjohnlev bzero(&state, sizeof (state)); 13435084Sjohnlev state.vs_vnic_id = vnic_id; 13445084Sjohnlev bcopy(state.vs_dev_name, dev_name, MAXNAMELEN); 13455084Sjohnlev state.vs_new_vnic_fn = new_vnic_fn; 13465084Sjohnlev state.vs_fn_arg = fn_arg; 13475084Sjohnlev 13485084Sjohnlev mod_hash_walk(vnic_hash, vnic_info_walker, &state); 13495084Sjohnlev 13505084Sjohnlev if ((rc = state.vs_rc) == 0 && vnic_id != 0 && 1351*5733Syz147064 !state.vs_vnic_found) 13525084Sjohnlev rc = ENOENT; 13535084Sjohnlev 13545084Sjohnlev rw_exit(&vnic_lock); 13555084Sjohnlev return (rc); 13565084Sjohnlev } 13575084Sjohnlev 13585084Sjohnlev /* 13595084Sjohnlev * Walker invoked when building a list of vnics that must be passed 13605084Sjohnlev * up to user space. 13615084Sjohnlev */ 13625084Sjohnlev /*ARGSUSED*/ 13635084Sjohnlev static uint_t 13645084Sjohnlev vnic_info_walker(mod_hash_key_t key, mod_hash_val_t *val, void *arg) 13655084Sjohnlev { 13665084Sjohnlev vnic_t *vnic; 13675084Sjohnlev vnic_info_state_t *state = arg; 13685084Sjohnlev 13695084Sjohnlev if (state->vs_rc != 0) 13705084Sjohnlev return (MH_WALK_TERMINATE); /* terminate walk */ 13715084Sjohnlev 13725084Sjohnlev vnic = (vnic_t *)val; 13735084Sjohnlev 13745084Sjohnlev if (state->vs_vnic_id != 0 && vnic->vn_id != state->vs_vnic_id) 13755084Sjohnlev goto bail; 13765084Sjohnlev 13775084Sjohnlev state->vs_vnic_found = B_TRUE; 13785084Sjohnlev 13795084Sjohnlev state->vs_rc = state->vs_new_vnic_fn(state->vs_fn_arg, 13805084Sjohnlev vnic->vn_id, vnic->vn_addr_type, vnic->vn_vnic_mac->va_addr_len, 13815084Sjohnlev vnic->vn_addr, vnic->vn_vnic_mac->va_dev_name); 13825084Sjohnlev bail: 13835084Sjohnlev return ((state->vs_rc == 0) ? MH_WALK_CONTINUE : MH_WALK_TERMINATE); 13845084Sjohnlev } 13855084Sjohnlev 13865084Sjohnlev /* 13875084Sjohnlev * vnic_notify_cb() and vnic_notify_walker() below are used to 13885084Sjohnlev * process events received from an underlying NIC and, if needed, 13895084Sjohnlev * forward these events to the VNICs defined on top of that NIC. 13905084Sjohnlev */ 13915084Sjohnlev 13925084Sjohnlev typedef struct vnic_notify_state { 13935084Sjohnlev mac_notify_type_t vo_type; 13945084Sjohnlev vnic_mac_t *vo_vnic_mac; 13955084Sjohnlev } vnic_notify_state_t; 13965084Sjohnlev 13975084Sjohnlev /* ARGSUSED */ 13985084Sjohnlev static uint_t 13995084Sjohnlev vnic_notify_walker(mod_hash_key_t key, mod_hash_val_t *val, void *arg) 14005084Sjohnlev { 14015084Sjohnlev vnic_t *vnic = (vnic_t *)val; 14025084Sjohnlev vnic_notify_state_t *state = arg; 14035084Sjohnlev 14045084Sjohnlev /* ignore VNICs that don't use the specified underlying MAC */ 14055084Sjohnlev if (vnic->vn_vnic_mac != state->vo_vnic_mac) 14065084Sjohnlev return (MH_WALK_CONTINUE); 14075084Sjohnlev 14085084Sjohnlev switch (state->vo_type) { 14095084Sjohnlev case MAC_NOTE_TX: 14105084Sjohnlev mac_tx_update(vnic->vn_mh); 14115084Sjohnlev break; 14125084Sjohnlev case MAC_NOTE_LINK: 14135084Sjohnlev /* 14145084Sjohnlev * The VNIC link state must be up regardless of 14155084Sjohnlev * the link state of the underlying NIC to maintain 14165084Sjohnlev * connectivity between VNICs on the same host. 14175084Sjohnlev */ 14185084Sjohnlev mac_link_update(vnic->vn_mh, LINK_STATE_UP); 14195084Sjohnlev break; 14205084Sjohnlev case MAC_NOTE_UNICST: 14215084Sjohnlev vnic_update_active_rx(vnic->vn_vnic_mac); 14225084Sjohnlev break; 14235084Sjohnlev case MAC_NOTE_VNIC: 14245084Sjohnlev /* only for clients which share a NIC with a VNIC */ 14255084Sjohnlev break; 14265084Sjohnlev case MAC_NOTE_PROMISC: 14275084Sjohnlev mutex_enter(&vnic_mac_lock); 14285084Sjohnlev vnic->vn_vnic_mac->va_txinfo = mac_vnic_tx_get( 14295084Sjohnlev vnic->vn_vnic_mac->va_mh); 14305084Sjohnlev mutex_exit(&vnic_mac_lock); 14315084Sjohnlev break; 14325084Sjohnlev } 14335084Sjohnlev 14345084Sjohnlev return (MH_WALK_CONTINUE); 14355084Sjohnlev } 14365084Sjohnlev 14375084Sjohnlev static void 14385084Sjohnlev vnic_notify_cb(void *arg, mac_notify_type_t type) 14395084Sjohnlev { 14405084Sjohnlev vnic_mac_t *vnic = arg; 14415084Sjohnlev vnic_notify_state_t state; 14425084Sjohnlev 14435084Sjohnlev state.vo_type = type; 14445084Sjohnlev state.vo_vnic_mac = vnic; 14455084Sjohnlev 14465084Sjohnlev rw_enter(&vnic_lock, RW_READER); 14475084Sjohnlev mod_hash_walk(vnic_hash, vnic_notify_walker, &state); 14485084Sjohnlev rw_exit(&vnic_lock); 14495084Sjohnlev } 14505084Sjohnlev 14515084Sjohnlev static int 14525084Sjohnlev vnic_modify_mac_addr(vnic_t *vnic, uint_t mac_len, uchar_t *mac_addr) 14535084Sjohnlev { 14545084Sjohnlev vnic_mac_t *vnic_mac = vnic->vn_vnic_mac; 14555084Sjohnlev vnic_flow_t *vnic_flow = vnic->vn_flow_ent; 14565084Sjohnlev 14575084Sjohnlev ASSERT(RW_WRITE_HELD(&vnic_lock)); 14585084Sjohnlev 14595084Sjohnlev if (mac_len != vnic_mac->va_addr_len) 14605084Sjohnlev return (EINVAL); 14615084Sjohnlev 14625084Sjohnlev vnic_classifier_flow_update_addr(vnic_flow, mac_addr); 14635084Sjohnlev return (0); 14645084Sjohnlev } 14655084Sjohnlev 14665084Sjohnlev static int 14675084Sjohnlev vnic_promisc_set(vnic_t *vnic, boolean_t on) 14685084Sjohnlev { 14695084Sjohnlev vnic_mac_t *vnic_mac = vnic->vn_vnic_mac; 14705084Sjohnlev int r = -1; 14715084Sjohnlev 14725084Sjohnlev if (vnic->vn_promisc == on) 14735084Sjohnlev return (0); 14745084Sjohnlev 14755084Sjohnlev if (on) { 14765084Sjohnlev r = mac_promisc_set(vnic_mac->va_mh, B_TRUE, MAC_DEVPROMISC); 14775084Sjohnlev if (r != 0) 14785084Sjohnlev return (r); 14795084Sjohnlev 14805084Sjohnlev rw_enter(&vnic_mac->va_promisc_lock, RW_WRITER); 14815084Sjohnlev vnic->vn_promisc_next = vnic_mac->va_promisc; 14825084Sjohnlev vnic_mac->va_promisc = vnic; 14835084Sjohnlev vnic_mac->va_promisc_gen++; 14845084Sjohnlev 14855084Sjohnlev vnic->vn_promisc = B_TRUE; 14865084Sjohnlev rw_exit(&vnic_mac->va_promisc_lock); 14875084Sjohnlev 14885084Sjohnlev return (0); 14895084Sjohnlev } else { 14905084Sjohnlev vnic_t *loop, *prev = NULL; 14915084Sjohnlev 14925084Sjohnlev rw_enter(&vnic_mac->va_promisc_lock, RW_WRITER); 14935084Sjohnlev loop = vnic_mac->va_promisc; 14945084Sjohnlev 14955084Sjohnlev while ((loop != NULL) && (loop != vnic)) { 14965084Sjohnlev prev = loop; 14975084Sjohnlev loop = loop->vn_promisc_next; 14985084Sjohnlev } 14995084Sjohnlev 15005084Sjohnlev if ((loop != NULL) && 15015084Sjohnlev ((r = mac_promisc_set(vnic_mac->va_mh, B_FALSE, 15025084Sjohnlev MAC_DEVPROMISC)) == 0)) { 15035084Sjohnlev if (prev != NULL) 15045084Sjohnlev prev->vn_promisc_next = loop->vn_promisc_next; 15055084Sjohnlev else 15065084Sjohnlev vnic_mac->va_promisc = loop->vn_promisc_next; 15075084Sjohnlev vnic_mac->va_promisc_gen++; 15085084Sjohnlev 15095084Sjohnlev vnic->vn_promisc = B_FALSE; 15105084Sjohnlev } 15115084Sjohnlev rw_exit(&vnic_mac->va_promisc_lock); 15125084Sjohnlev 15135084Sjohnlev return (r); 15145084Sjohnlev } 15155084Sjohnlev } 15165084Sjohnlev 15175084Sjohnlev void 15185084Sjohnlev vnic_promisc_rx(vnic_mac_t *vnic_mac, vnic_t *sender, mblk_t *mp) 15195084Sjohnlev { 15205084Sjohnlev vnic_t *loop; 15215084Sjohnlev vnic_flow_t *flow; 15225084Sjohnlev const vnic_flow_fn_info_t *fn_info; 15235084Sjohnlev mac_header_info_t hdr_info; 15245084Sjohnlev boolean_t dst_must_match = B_TRUE; 15255084Sjohnlev 15265084Sjohnlev ASSERT(mp->b_next == NULL); 15275084Sjohnlev 15285084Sjohnlev rw_enter(&vnic_mac->va_promisc_lock, RW_READER); 15295084Sjohnlev if (vnic_mac->va_promisc == NULL) 15305084Sjohnlev goto done; 15315084Sjohnlev 15325084Sjohnlev if (mac_header_info(vnic_mac->va_mh, mp, &hdr_info) != 0) 15335084Sjohnlev goto done; 15345084Sjohnlev 15355084Sjohnlev /* 15365084Sjohnlev * If this is broadcast or multicast then the destination 15375084Sjohnlev * address need not match for us to deliver it. 15385084Sjohnlev */ 15395084Sjohnlev if ((hdr_info.mhi_dsttype == MAC_ADDRTYPE_BROADCAST) || 15405084Sjohnlev (hdr_info.mhi_dsttype == MAC_ADDRTYPE_MULTICAST)) 15415084Sjohnlev dst_must_match = B_FALSE; 15425084Sjohnlev 15435084Sjohnlev for (loop = vnic_mac->va_promisc; 15445084Sjohnlev loop != NULL; 15455084Sjohnlev loop = loop->vn_promisc_next) { 15465084Sjohnlev if (loop == sender) 15475084Sjohnlev continue; 15485084Sjohnlev 15495084Sjohnlev if (dst_must_match && 15505084Sjohnlev (bcmp(hdr_info.mhi_daddr, loop->vn_addr, 15515084Sjohnlev sizeof (loop->vn_addr)) != 0)) 15525084Sjohnlev continue; 15535084Sjohnlev 15545084Sjohnlev flow = loop->vn_flow_ent; 15555084Sjohnlev ASSERT(flow != NULL); 15565084Sjohnlev 15575084Sjohnlev if (!flow->vf_is_active) { 15585159Sjohnlev mblk_t *copy; 15595159Sjohnlev uint64_t gen; 15605159Sjohnlev 15615159Sjohnlev if ((copy = vnic_copymsg_cksum(mp)) == NULL) 15625159Sjohnlev break; 15635159Sjohnlev if ((sender != NULL) && 15645159Sjohnlev ((copy = vnic_fix_cksum(copy)) == NULL)) 15655159Sjohnlev break; 15665159Sjohnlev 15675084Sjohnlev VNIC_FLOW_REFHOLD(flow); 15685084Sjohnlev gen = vnic_mac->va_promisc_gen; 15695084Sjohnlev rw_exit(&vnic_mac->va_promisc_lock); 15705084Sjohnlev 15715159Sjohnlev fn_info = vnic_classifier_get_fn_info(flow); 15725159Sjohnlev (fn_info->ff_fn)(fn_info->ff_arg1, 15735159Sjohnlev fn_info->ff_arg2, copy); 15745084Sjohnlev 15755084Sjohnlev VNIC_FLOW_REFRELE(flow); 15765084Sjohnlev rw_enter(&vnic_mac->va_promisc_lock, RW_READER); 15775084Sjohnlev if (vnic_mac->va_promisc_gen != gen) 15785084Sjohnlev break; 15795084Sjohnlev } 15805084Sjohnlev } 15815084Sjohnlev done: 15825084Sjohnlev rw_exit(&vnic_mac->va_promisc_lock); 15835084Sjohnlev } 1584