15373Sraghuram /* 25373Sraghuram * CDDL HEADER START 35373Sraghuram * 45373Sraghuram * The contents of this file are subject to the terms of the 55373Sraghuram * Common Development and Distribution License (the "License"). 65373Sraghuram * You may not use this file except in compliance with the License. 75373Sraghuram * 85373Sraghuram * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 95373Sraghuram * or http://www.opensolaris.org/os/licensing. 105373Sraghuram * See the License for the specific language governing permissions 115373Sraghuram * and limitations under the License. 125373Sraghuram * 135373Sraghuram * When distributing Covered Code, include this CDDL HEADER in each 145373Sraghuram * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 155373Sraghuram * If applicable, add the following below this CDDL HEADER, with the 165373Sraghuram * fields enclosed by brackets "[]" replaced with your own identifying 175373Sraghuram * information: Portions Copyright [yyyy] [name of copyright owner] 185373Sraghuram * 195373Sraghuram * CDDL HEADER END 205373Sraghuram */ 215373Sraghuram 225373Sraghuram /* 235935Ssb155480 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 245373Sraghuram * Use is subject to license terms. 255373Sraghuram */ 265373Sraghuram 275373Sraghuram #pragma ident "%Z%%M% %I% %E% SMI" 285373Sraghuram 295373Sraghuram #include <sys/types.h> 305373Sraghuram #include <sys/errno.h> 315373Sraghuram #include <sys/debug.h> 325373Sraghuram #include <sys/time.h> 335373Sraghuram #include <sys/sysmacros.h> 345373Sraghuram #include <sys/systm.h> 355373Sraghuram #include <sys/user.h> 365373Sraghuram #include <sys/stropts.h> 375373Sraghuram #include <sys/stream.h> 385373Sraghuram #include <sys/strlog.h> 395373Sraghuram #include <sys/strsubr.h> 405373Sraghuram #include <sys/cmn_err.h> 415373Sraghuram #include <sys/cpu.h> 425373Sraghuram #include <sys/kmem.h> 435373Sraghuram #include <sys/conf.h> 445373Sraghuram #include <sys/ddi.h> 455373Sraghuram #include <sys/sunddi.h> 465373Sraghuram #include <sys/ksynch.h> 475373Sraghuram #include <sys/stat.h> 485373Sraghuram #include <sys/kstat.h> 495373Sraghuram #include <sys/vtrace.h> 505373Sraghuram #include <sys/strsun.h> 515373Sraghuram #include <sys/dlpi.h> 525373Sraghuram #include <sys/ethernet.h> 535373Sraghuram #include <net/if.h> 545373Sraghuram #include <sys/varargs.h> 555373Sraghuram #include <sys/machsystm.h> 565373Sraghuram #include <sys/modctl.h> 575373Sraghuram #include <sys/modhash.h> 585373Sraghuram #include <sys/mac.h> 595373Sraghuram #include <sys/mac_ether.h> 605373Sraghuram #include <sys/taskq.h> 615373Sraghuram #include <sys/note.h> 625373Sraghuram #include <sys/mach_descrip.h> 635373Sraghuram #include <sys/mac.h> 645373Sraghuram #include <sys/mdeg.h> 655373Sraghuram #include <sys/ldc.h> 665373Sraghuram #include <sys/vsw_fdb.h> 675373Sraghuram #include <sys/vsw.h> 685373Sraghuram #include <sys/vio_mailbox.h> 695373Sraghuram #include <sys/vnet_mailbox.h> 705373Sraghuram #include <sys/vnet_common.h> 715373Sraghuram #include <sys/vio_util.h> 725373Sraghuram #include <sys/sdt.h> 735373Sraghuram #include <sys/atomic.h> 746419Ssb155480 #include <sys/vlan.h> 755373Sraghuram 765373Sraghuram /* Switching setup routines */ 775373Sraghuram void vsw_setup_switching_timeout(void *arg); 785373Sraghuram void vsw_stop_switching_timeout(vsw_t *vswp); 795373Sraghuram int vsw_setup_switching(vsw_t *); 80*6537Swentaoy void vsw_switch_frame_nop(vsw_t *vswp, mblk_t *mp, int caller, 81*6537Swentaoy vsw_port_t *port, mac_resource_handle_t mrh); 825373Sraghuram static int vsw_setup_layer2(vsw_t *); 835373Sraghuram static int vsw_setup_layer3(vsw_t *); 845373Sraghuram 855373Sraghuram /* Switching/data transmit routines */ 865373Sraghuram static void vsw_switch_l2_frame(vsw_t *vswp, mblk_t *mp, int caller, 876419Ssb155480 vsw_port_t *port, mac_resource_handle_t); 885373Sraghuram static void vsw_switch_l3_frame(vsw_t *vswp, mblk_t *mp, int caller, 896419Ssb155480 vsw_port_t *port, mac_resource_handle_t); 905935Ssb155480 static int vsw_forward_all(vsw_t *vswp, mblk_t *mp, 915935Ssb155480 int caller, vsw_port_t *port); 925935Ssb155480 static int vsw_forward_grp(vsw_t *vswp, mblk_t *mp, 935373Sraghuram int caller, vsw_port_t *port); 945373Sraghuram 956419Ssb155480 /* VLAN routines */ 966419Ssb155480 void vsw_create_vlans(void *arg, int type); 976419Ssb155480 void vsw_destroy_vlans(void *arg, int type); 986419Ssb155480 void vsw_vlan_add_ids(void *arg, int type); 996419Ssb155480 void vsw_vlan_remove_ids(void *arg, int type); 1006419Ssb155480 static void vsw_vlan_create_hash(void *arg, int type); 1016419Ssb155480 static void vsw_vlan_destroy_hash(void *arg, int type); 1026419Ssb155480 boolean_t vsw_frame_lookup_vid(void *arg, int caller, struct ether_header *ehp, 1036419Ssb155480 uint16_t *vidp); 1046419Ssb155480 mblk_t *vsw_vlan_frame_pretag(void *arg, int type, mblk_t *mp); 1056419Ssb155480 uint32_t vsw_vlan_frames_untag(void *arg, int type, mblk_t **np, mblk_t **npt); 1066419Ssb155480 boolean_t vsw_vlan_lookup(mod_hash_t *vlan_hashp, uint16_t vid); 1076419Ssb155480 1085373Sraghuram /* Forwarding database (FDB) routines */ 1096419Ssb155480 void vsw_fdbe_add(vsw_t *vswp, void *port); 1106419Ssb155480 void vsw_fdbe_del(vsw_t *vswp, struct ether_addr *eaddr); 1116419Ssb155480 static vsw_fdbe_t *vsw_fdbe_find(vsw_t *vswp, struct ether_addr *); 1126419Ssb155480 static void vsw_fdbe_find_cb(mod_hash_key_t key, mod_hash_val_t val); 1136419Ssb155480 1145373Sraghuram int vsw_add_rem_mcst(vnet_mcast_msg_t *, vsw_port_t *); 1155373Sraghuram int vsw_add_mcst(vsw_t *, uint8_t, uint64_t, void *); 1165373Sraghuram int vsw_del_mcst(vsw_t *, uint8_t, uint64_t, void *); 1175373Sraghuram void vsw_del_mcst_vsw(vsw_t *); 1185373Sraghuram 1195373Sraghuram /* Support functions */ 1205373Sraghuram static mblk_t *vsw_dupmsgchain(mblk_t *mp); 1215935Ssb155480 static uint32_t vsw_get_same_dest_list(struct ether_header *ehp, 1225373Sraghuram mblk_t **rhead, mblk_t **rtail, mblk_t **mpp); 1235373Sraghuram 1245373Sraghuram 1255373Sraghuram /* 1265373Sraghuram * Functions imported from other files. 1275373Sraghuram */ 1285373Sraghuram extern mblk_t *vsw_tx_msg(vsw_t *, mblk_t *); 1295373Sraghuram extern mcst_addr_t *vsw_del_addr(uint8_t, void *, uint64_t); 1305373Sraghuram extern int vsw_mac_open(vsw_t *vswp); 1315373Sraghuram extern void vsw_mac_close(vsw_t *vswp); 1325935Ssb155480 extern void vsw_mac_rx(vsw_t *vswp, mac_resource_handle_t mrh, 1335935Ssb155480 mblk_t *mp, vsw_macrx_flags_t flags); 1345373Sraghuram extern void vsw_set_addrs(vsw_t *vswp); 1355373Sraghuram extern int vsw_get_hw_maddr(vsw_t *); 1365373Sraghuram extern int vsw_mac_attach(vsw_t *vswp); 1375935Ssb155480 extern int vsw_portsend(vsw_port_t *port, mblk_t *mp, mblk_t *mpt, 1385935Ssb155480 uint32_t count); 1396495Sspeer extern void vsw_hio_init(vsw_t *vswp); 1406495Sspeer extern void vsw_hio_start_ports(vsw_t *vswp); 1415373Sraghuram 1425373Sraghuram /* 1435373Sraghuram * Tunables used in this file. 1445373Sraghuram */ 1456419Ssb155480 extern int vsw_setup_switching_delay; 1466419Ssb155480 extern uint32_t vsw_vlan_nchains; 1476419Ssb155480 extern uint32_t vsw_fdbe_refcnt_delay; 1485373Sraghuram 1496419Ssb155480 #define VSW_FDBE_REFHOLD(p) \ 1506419Ssb155480 { \ 1516419Ssb155480 atomic_inc_32(&(p)->refcnt); \ 1526419Ssb155480 ASSERT((p)->refcnt != 0); \ 1536419Ssb155480 } 1546419Ssb155480 1556419Ssb155480 #define VSW_FDBE_REFRELE(p) \ 1566419Ssb155480 { \ 1576419Ssb155480 ASSERT((p)->refcnt != 0); \ 1586419Ssb155480 atomic_dec_32(&(p)->refcnt); \ 1596419Ssb155480 } 1605373Sraghuram 1615373Sraghuram /* 1625373Sraghuram * Timeout routine to setup switching mode: 1635373Sraghuram * vsw_setup_switching() is invoked from vsw_attach() or vsw_update_md_prop() 1645373Sraghuram * initially. If it fails and the error is EAGAIN, then this timeout handler 1655373Sraghuram * is started to retry vsw_setup_switching(). vsw_setup_switching() is retried 1665373Sraghuram * until we successfully finish it; or the returned error is not EAGAIN. 1675373Sraghuram */ 1685373Sraghuram void 1695373Sraghuram vsw_setup_switching_timeout(void *arg) 1705373Sraghuram { 1715373Sraghuram vsw_t *vswp = (vsw_t *)arg; 1725373Sraghuram int rv; 1735373Sraghuram 1745373Sraghuram if (vswp->swtmout_enabled == B_FALSE) 1755373Sraghuram return; 1765373Sraghuram 1775373Sraghuram rv = vsw_setup_switching(vswp); 1785373Sraghuram 1795373Sraghuram if (rv == 0) { 1805373Sraghuram /* 1815373Sraghuram * Successfully setup switching mode. 1825373Sraghuram * Program unicst, mcst addrs of vsw 1835373Sraghuram * interface and ports in the physdev. 1845373Sraghuram */ 1855373Sraghuram vsw_set_addrs(vswp); 1866495Sspeer 1876495Sspeer /* Start HIO for ports that have already connected */ 1886495Sspeer vsw_hio_start_ports(vswp); 1895373Sraghuram } 1905373Sraghuram 1915373Sraghuram mutex_enter(&vswp->swtmout_lock); 1925373Sraghuram 1935373Sraghuram if (rv == EAGAIN && vswp->swtmout_enabled == B_TRUE) { 1945373Sraghuram /* 1955373Sraghuram * Reschedule timeout() if the error is EAGAIN and the 1965373Sraghuram * timeout is still enabled. For errors other than EAGAIN, 1975373Sraghuram * we simply return without rescheduling timeout(). 1985373Sraghuram */ 1995373Sraghuram vswp->swtmout_id = 2005373Sraghuram timeout(vsw_setup_switching_timeout, vswp, 2015373Sraghuram (vsw_setup_switching_delay * drv_usectohz(MICROSEC))); 2025373Sraghuram goto exit; 2035373Sraghuram } 2045373Sraghuram 2055373Sraghuram /* timeout handler completed */ 2065373Sraghuram vswp->swtmout_enabled = B_FALSE; 2075373Sraghuram vswp->swtmout_id = 0; 2085373Sraghuram 2095373Sraghuram exit: 2105373Sraghuram mutex_exit(&vswp->swtmout_lock); 2115373Sraghuram } 2125373Sraghuram 2135373Sraghuram /* 2145373Sraghuram * Cancel the timeout handler to setup switching mode. 2155373Sraghuram */ 2165373Sraghuram void 2175373Sraghuram vsw_stop_switching_timeout(vsw_t *vswp) 2185373Sraghuram { 2195373Sraghuram timeout_id_t tid; 2205373Sraghuram 2215373Sraghuram mutex_enter(&vswp->swtmout_lock); 2225373Sraghuram 2235373Sraghuram tid = vswp->swtmout_id; 2245373Sraghuram 2255373Sraghuram if (tid != 0) { 2265373Sraghuram /* signal timeout handler to stop */ 2275373Sraghuram vswp->swtmout_enabled = B_FALSE; 2285373Sraghuram vswp->swtmout_id = 0; 2295373Sraghuram mutex_exit(&vswp->swtmout_lock); 2305373Sraghuram 2315373Sraghuram (void) untimeout(tid); 2325373Sraghuram } else { 2335373Sraghuram mutex_exit(&vswp->swtmout_lock); 2345373Sraghuram } 2355373Sraghuram 2365373Sraghuram (void) atomic_swap_32(&vswp->switching_setup_done, B_FALSE); 2375373Sraghuram 2385373Sraghuram mutex_enter(&vswp->mac_lock); 2395373Sraghuram vswp->mac_open_retries = 0; 2405373Sraghuram mutex_exit(&vswp->mac_lock); 2415373Sraghuram } 2425373Sraghuram 2435373Sraghuram /* 2445373Sraghuram * Setup the required switching mode. 2455373Sraghuram * This routine is invoked from vsw_attach() or vsw_update_md_prop() 2465373Sraghuram * initially. If it fails and the error is EAGAIN, then a timeout handler 2475373Sraghuram * is started to retry vsw_setup_switching(), until it successfully finishes; 2485373Sraghuram * or the returned error is not EAGAIN. 2495373Sraghuram * 2505373Sraghuram * Returns: 2515373Sraghuram * 0 on success. 2525373Sraghuram * EAGAIN if retry is needed. 2535373Sraghuram * 1 on all other failures. 2545373Sraghuram */ 2555373Sraghuram int 2565373Sraghuram vsw_setup_switching(vsw_t *vswp) 2575373Sraghuram { 2585373Sraghuram int i, rv = 1; 2595373Sraghuram 2605373Sraghuram D1(vswp, "%s: enter", __func__); 2615373Sraghuram 2625373Sraghuram /* 2635373Sraghuram * Select best switching mode. 2645373Sraghuram * Note that we start from the saved smode_idx. This is done as 2655373Sraghuram * this routine can be called from the timeout handler to retry 2665373Sraghuram * setting up a specific mode. Currently only the function which 2675373Sraghuram * sets up layer2/promisc mode returns EAGAIN if the underlying 2685373Sraghuram * physical device is not available yet, causing retries. 2695373Sraghuram */ 2705373Sraghuram for (i = vswp->smode_idx; i < vswp->smode_num; i++) { 2715373Sraghuram vswp->smode_idx = i; 2725373Sraghuram switch (vswp->smode[i]) { 2735373Sraghuram case VSW_LAYER2: 2745373Sraghuram case VSW_LAYER2_PROMISC: 2755373Sraghuram rv = vsw_setup_layer2(vswp); 2765373Sraghuram break; 2775373Sraghuram 2785373Sraghuram case VSW_LAYER3: 2795373Sraghuram rv = vsw_setup_layer3(vswp); 2805373Sraghuram break; 2815373Sraghuram 2825373Sraghuram default: 2835373Sraghuram DERR(vswp, "unknown switch mode"); 2845373Sraghuram break; 2855373Sraghuram } 2865373Sraghuram 2875373Sraghuram if ((rv == 0) || (rv == EAGAIN)) 2885373Sraghuram break; 2895373Sraghuram 2905373Sraghuram /* all other errors(rv != 0): continue & select the next mode */ 2915373Sraghuram rv = 1; 2925373Sraghuram } 2935373Sraghuram 2945373Sraghuram if (rv && (rv != EAGAIN)) { 2955373Sraghuram cmn_err(CE_WARN, "!vsw%d: Unable to setup specified " 2965373Sraghuram "switching mode", vswp->instance); 2975373Sraghuram } else if (rv == 0) { 2985373Sraghuram (void) atomic_swap_32(&vswp->switching_setup_done, B_TRUE); 2995373Sraghuram } 3005373Sraghuram 3015373Sraghuram D2(vswp, "%s: Operating in mode %d", __func__, 3025373Sraghuram vswp->smode[vswp->smode_idx]); 3035373Sraghuram 3045373Sraghuram D1(vswp, "%s: exit", __func__); 3055373Sraghuram 3065373Sraghuram return (rv); 3075373Sraghuram } 3085373Sraghuram 3095373Sraghuram /* 3105373Sraghuram * Setup for layer 2 switching. 3115373Sraghuram * 3125373Sraghuram * Returns: 3135373Sraghuram * 0 on success. 3145373Sraghuram * EAGAIN if retry is needed. 3155373Sraghuram * EIO on all other failures. 3165373Sraghuram */ 3175373Sraghuram static int 3185373Sraghuram vsw_setup_layer2(vsw_t *vswp) 3195373Sraghuram { 3205373Sraghuram int rv; 3215373Sraghuram 3225373Sraghuram D1(vswp, "%s: enter", __func__); 3235373Sraghuram 3245373Sraghuram vswp->vsw_switch_frame = vsw_switch_l2_frame; 3255373Sraghuram 3265373Sraghuram rv = strlen(vswp->physname); 3275373Sraghuram if (rv == 0) { 3285373Sraghuram /* 3295373Sraghuram * Physical device name is NULL, which is 3305373Sraghuram * required for layer 2. 3315373Sraghuram */ 3325373Sraghuram cmn_err(CE_WARN, "!vsw%d: no physical device name specified", 3335373Sraghuram vswp->instance); 3345373Sraghuram return (EIO); 3355373Sraghuram } 3365373Sraghuram 3375373Sraghuram mutex_enter(&vswp->mac_lock); 3385373Sraghuram 3395373Sraghuram rv = vsw_mac_open(vswp); 3405373Sraghuram if (rv != 0) { 3415373Sraghuram if (rv != EAGAIN) { 3425373Sraghuram cmn_err(CE_WARN, "!vsw%d: Unable to open physical " 3435373Sraghuram "device: %s\n", vswp->instance, vswp->physname); 3445373Sraghuram } 3455373Sraghuram mutex_exit(&vswp->mac_lock); 3465373Sraghuram return (rv); 3475373Sraghuram } 3485373Sraghuram 3495373Sraghuram if (vswp->smode[vswp->smode_idx] == VSW_LAYER2) { 3505373Sraghuram /* 3515373Sraghuram * Verify that underlying device can support multiple 3525373Sraghuram * unicast mac addresses. 3535373Sraghuram */ 3545373Sraghuram rv = vsw_get_hw_maddr(vswp); 3555373Sraghuram if (rv != 0) { 3565373Sraghuram goto exit_error; 3575373Sraghuram } 3585373Sraghuram } 3595373Sraghuram 3605373Sraghuram /* 3615373Sraghuram * Attempt to link into the MAC layer so we can get 3625373Sraghuram * and send packets out over the physical adapter. 3635373Sraghuram */ 3645373Sraghuram rv = vsw_mac_attach(vswp); 3655373Sraghuram if (rv != 0) { 3665373Sraghuram /* 3675373Sraghuram * Registration with the MAC layer has failed, 3685373Sraghuram * so return error so that can fall back to next 3695373Sraghuram * prefered switching method. 3705373Sraghuram */ 3715373Sraghuram cmn_err(CE_WARN, "!vsw%d: Unable to setup physical device: " 3725373Sraghuram "%s\n", vswp->instance, vswp->physname); 3735373Sraghuram goto exit_error; 3745373Sraghuram } 3755373Sraghuram 3765373Sraghuram D1(vswp, "%s: exit", __func__); 3775373Sraghuram 3785373Sraghuram mutex_exit(&vswp->mac_lock); 3796495Sspeer 3806495Sspeer /* Initialize HybridIO related stuff */ 3816495Sspeer vsw_hio_init(vswp); 3825373Sraghuram return (0); 3835373Sraghuram 3845373Sraghuram exit_error: 3855373Sraghuram vsw_mac_close(vswp); 3865373Sraghuram mutex_exit(&vswp->mac_lock); 3875373Sraghuram return (EIO); 3885373Sraghuram } 3895373Sraghuram 3905373Sraghuram static int 3915373Sraghuram vsw_setup_layer3(vsw_t *vswp) 3925373Sraghuram { 3935373Sraghuram D1(vswp, "%s: enter", __func__); 3945373Sraghuram 3955373Sraghuram D2(vswp, "%s: operating in layer 3 mode", __func__); 3965373Sraghuram vswp->vsw_switch_frame = vsw_switch_l3_frame; 3975373Sraghuram 3985373Sraghuram D1(vswp, "%s: exit", __func__); 3995373Sraghuram 4005373Sraghuram return (0); 4015373Sraghuram } 4025373Sraghuram 403*6537Swentaoy /* ARGSUSED */ 404*6537Swentaoy void 405*6537Swentaoy vsw_switch_frame_nop(vsw_t *vswp, mblk_t *mp, int caller, vsw_port_t *port, 406*6537Swentaoy mac_resource_handle_t mrh) 407*6537Swentaoy { 408*6537Swentaoy freemsgchain(mp); 409*6537Swentaoy } 410*6537Swentaoy 4115373Sraghuram /* 4125373Sraghuram * Switch the given ethernet frame when operating in layer 2 mode. 4135373Sraghuram * 4145373Sraghuram * vswp: pointer to the vsw instance 4155373Sraghuram * mp: pointer to chain of ethernet frame(s) to be switched 4165373Sraghuram * caller: identifies the source of this frame as: 4175373Sraghuram * 1. VSW_VNETPORT - a vsw port (connected to a vnet). 4185373Sraghuram * 2. VSW_PHYSDEV - the physical ethernet device 4195373Sraghuram * 3. VSW_LOCALDEV - vsw configured as a virtual interface 4205373Sraghuram * arg: argument provided by the caller. 4215373Sraghuram * 1. for VNETPORT - pointer to the corresponding vsw_port_t. 4225373Sraghuram * 2. for PHYSDEV - NULL 4235373Sraghuram * 3. for LOCALDEV - pointer to to this vsw_t(self) 4245373Sraghuram */ 4255373Sraghuram void 4265373Sraghuram vsw_switch_l2_frame(vsw_t *vswp, mblk_t *mp, int caller, 4275373Sraghuram vsw_port_t *arg, mac_resource_handle_t mrh) 4285373Sraghuram { 4295373Sraghuram struct ether_header *ehp; 4305373Sraghuram mblk_t *bp, *ret_m; 4315373Sraghuram mblk_t *mpt = NULL; 4325935Ssb155480 uint32_t count; 4336419Ssb155480 vsw_fdbe_t *fp; 4345373Sraghuram 4355373Sraghuram D1(vswp, "%s: enter (caller %d)", __func__, caller); 4365373Sraghuram 4375373Sraghuram /* 4385373Sraghuram * PERF: rather than breaking up the chain here, scan it 4395373Sraghuram * to find all mblks heading to same destination and then 4405373Sraghuram * pass that sub-chain to the lower transmit functions. 4415373Sraghuram */ 4425373Sraghuram 4435373Sraghuram /* process the chain of packets */ 4445373Sraghuram bp = mp; 4455373Sraghuram while (bp) { 4465373Sraghuram ehp = (struct ether_header *)bp->b_rptr; 4475935Ssb155480 count = vsw_get_same_dest_list(ehp, &mp, &mpt, &bp); 4485935Ssb155480 ASSERT(count != 0); 4495373Sraghuram 4505373Sraghuram D2(vswp, "%s: mblk data buffer %lld : actual data size %lld", 4515373Sraghuram __func__, MBLKSIZE(mp), MBLKL(mp)); 4525373Sraghuram 4535373Sraghuram if (ether_cmp(&ehp->ether_dhost, &vswp->if_addr) == 0) { 4545373Sraghuram /* 4555373Sraghuram * If destination is VSW_LOCALDEV (vsw as an eth 4565373Sraghuram * interface) and if the device is up & running, 4575373Sraghuram * send the packet up the stack on this host. 4585373Sraghuram * If the virtual interface is down, drop the packet. 4595373Sraghuram */ 4605373Sraghuram if (caller != VSW_LOCALDEV) { 4615935Ssb155480 vsw_mac_rx(vswp, mrh, mp, VSW_MACRX_FREEMSG); 4625373Sraghuram } else { 4635373Sraghuram freemsgchain(mp); 4645373Sraghuram } 4655373Sraghuram continue; 4665373Sraghuram } 4675373Sraghuram 4686419Ssb155480 /* 4696419Ssb155480 * Find fdb entry for the destination 4706419Ssb155480 * and hold a reference to it. 4716419Ssb155480 */ 4726419Ssb155480 fp = vsw_fdbe_find(vswp, &ehp->ether_dhost); 4736419Ssb155480 if (fp != NULL) { 4745373Sraghuram 4755373Sraghuram /* 4765373Sraghuram * If plumbed and in promisc mode then copy msg 4775373Sraghuram * and send up the stack. 4785373Sraghuram */ 4795935Ssb155480 vsw_mac_rx(vswp, mrh, mp, 4805935Ssb155480 VSW_MACRX_PROMISC | VSW_MACRX_COPYMSG); 4815373Sraghuram 4825373Sraghuram /* 4835373Sraghuram * If the destination is in FDB, the packet 4845373Sraghuram * should be forwarded to the correponding 4855373Sraghuram * vsw_port (connected to a vnet device - 4865373Sraghuram * VSW_VNETPORT) 4875373Sraghuram */ 4886419Ssb155480 (void) vsw_portsend(fp->portp, mp, mpt, count); 4895373Sraghuram 4906419Ssb155480 /* Release the reference on the fdb entry */ 4916419Ssb155480 VSW_FDBE_REFRELE(fp); 4925373Sraghuram } else { 4935373Sraghuram /* 4945373Sraghuram * Destination not in FDB. 4955373Sraghuram * 4965373Sraghuram * If the destination is broadcast or 4975373Sraghuram * multicast forward the packet to all 4985373Sraghuram * (VNETPORTs, PHYSDEV, LOCALDEV), 4995373Sraghuram * except the caller. 5005373Sraghuram */ 5015373Sraghuram if (IS_BROADCAST(ehp)) { 5025935Ssb155480 D2(vswp, "%s: BROADCAST pkt", __func__); 5035935Ssb155480 (void) vsw_forward_all(vswp, mp, caller, arg); 5045373Sraghuram } else if (IS_MULTICAST(ehp)) { 5055935Ssb155480 D2(vswp, "%s: MULTICAST pkt", __func__); 5065935Ssb155480 (void) vsw_forward_grp(vswp, mp, caller, arg); 5075373Sraghuram } else { 5085373Sraghuram /* 5095373Sraghuram * If the destination is unicast, and came 5105373Sraghuram * from either a logical network device or 5115373Sraghuram * the switch itself when it is plumbed, then 5125373Sraghuram * send it out on the physical device and also 5135373Sraghuram * up the stack if the logical interface is 5145373Sraghuram * in promiscious mode. 5155373Sraghuram * 5165373Sraghuram * NOTE: The assumption here is that if we 5175373Sraghuram * cannot find the destination in our fdb, its 5185373Sraghuram * a unicast address, and came from either a 5195373Sraghuram * vnet or down the stack (when plumbed) it 5205373Sraghuram * must be destinded for an ethernet device 5215373Sraghuram * outside our ldoms. 5225373Sraghuram */ 5235373Sraghuram if (caller == VSW_VNETPORT) { 5245373Sraghuram /* promisc check copy etc */ 5255935Ssb155480 vsw_mac_rx(vswp, mrh, mp, 5265373Sraghuram VSW_MACRX_PROMISC | 5275373Sraghuram VSW_MACRX_COPYMSG); 5285373Sraghuram 5295373Sraghuram if ((ret_m = vsw_tx_msg(vswp, mp)) 5305373Sraghuram != NULL) { 5315373Sraghuram DERR(vswp, "%s: drop mblks to " 5325373Sraghuram "phys dev", __func__); 5335373Sraghuram freemsgchain(ret_m); 5345373Sraghuram } 5355373Sraghuram 5365373Sraghuram } else if (caller == VSW_PHYSDEV) { 5375373Sraghuram /* 5385373Sraghuram * Pkt seen because card in promisc 5395373Sraghuram * mode. Send up stack if plumbed in 5405373Sraghuram * promisc mode, else drop it. 5415373Sraghuram */ 5425935Ssb155480 vsw_mac_rx(vswp, mrh, mp, 5435373Sraghuram VSW_MACRX_PROMISC | 5445373Sraghuram VSW_MACRX_FREEMSG); 5455373Sraghuram 5465373Sraghuram } else if (caller == VSW_LOCALDEV) { 5475373Sraghuram /* 5485373Sraghuram * Pkt came down the stack, send out 5495373Sraghuram * over physical device. 5505373Sraghuram */ 5515373Sraghuram if ((ret_m = vsw_tx_msg(vswp, mp)) 5525373Sraghuram != NULL) { 5535373Sraghuram DERR(vswp, "%s: drop mblks to " 5545373Sraghuram "phys dev", __func__); 5555373Sraghuram freemsgchain(ret_m); 5565373Sraghuram } 5575373Sraghuram } 5585373Sraghuram } 5595373Sraghuram } 5605373Sraghuram } 5615373Sraghuram D1(vswp, "%s: exit\n", __func__); 5625373Sraghuram } 5635373Sraghuram 5645373Sraghuram /* 5655373Sraghuram * Switch ethernet frame when in layer 3 mode (i.e. using IP 5665373Sraghuram * layer to do the routing). 5675373Sraghuram * 5685373Sraghuram * There is a large amount of overlap between this function and 5695373Sraghuram * vsw_switch_l2_frame. At some stage we need to revisit and refactor 5705373Sraghuram * both these functions. 5715373Sraghuram */ 5725373Sraghuram void 5735373Sraghuram vsw_switch_l3_frame(vsw_t *vswp, mblk_t *mp, int caller, 5745373Sraghuram vsw_port_t *arg, mac_resource_handle_t mrh) 5755373Sraghuram { 5765373Sraghuram struct ether_header *ehp; 5775373Sraghuram mblk_t *bp = NULL; 5785373Sraghuram mblk_t *mpt; 5795935Ssb155480 uint32_t count; 5806419Ssb155480 vsw_fdbe_t *fp; 5815373Sraghuram 5825373Sraghuram D1(vswp, "%s: enter (caller %d)", __func__, caller); 5835373Sraghuram 5845373Sraghuram /* 5855373Sraghuram * In layer 3 mode should only ever be switching packets 5865373Sraghuram * between IP layer and vnet devices. So make sure thats 5875373Sraghuram * who is invoking us. 5885373Sraghuram */ 5895373Sraghuram if ((caller != VSW_LOCALDEV) && (caller != VSW_VNETPORT)) { 5905373Sraghuram DERR(vswp, "%s: unexpected caller (%d)", __func__, caller); 5915373Sraghuram freemsgchain(mp); 5925373Sraghuram return; 5935373Sraghuram } 5945373Sraghuram 5955373Sraghuram /* process the chain of packets */ 5965373Sraghuram bp = mp; 5975373Sraghuram while (bp) { 5985373Sraghuram ehp = (struct ether_header *)bp->b_rptr; 5995935Ssb155480 count = vsw_get_same_dest_list(ehp, &mp, &mpt, &bp); 6005935Ssb155480 ASSERT(count != 0); 6015373Sraghuram 6025373Sraghuram D2(vswp, "%s: mblk data buffer %lld : actual data size %lld", 6035373Sraghuram __func__, MBLKSIZE(mp), MBLKL(mp)); 6045373Sraghuram 6056419Ssb155480 /* 6066419Ssb155480 * Find fdb entry for the destination 6076419Ssb155480 * and hold a reference to it. 6086419Ssb155480 */ 6096419Ssb155480 fp = vsw_fdbe_find(vswp, &ehp->ether_dhost); 6106419Ssb155480 if (fp != NULL) { 6115373Sraghuram 6125373Sraghuram D2(vswp, "%s: sending to target port", __func__); 6136419Ssb155480 (void) vsw_portsend(fp->portp, mp, mpt, count); 6145373Sraghuram 6156419Ssb155480 /* Release the reference on the fdb entry */ 6166419Ssb155480 VSW_FDBE_REFRELE(fp); 6175373Sraghuram } else { 6185373Sraghuram /* 6195373Sraghuram * Destination not in FDB 6205373Sraghuram * 6215373Sraghuram * If the destination is broadcast or 6225373Sraghuram * multicast forward the packet to all 6235373Sraghuram * (VNETPORTs, PHYSDEV, LOCALDEV), 6245373Sraghuram * except the caller. 6255373Sraghuram */ 6265373Sraghuram if (IS_BROADCAST(ehp)) { 6275373Sraghuram D2(vswp, "%s: BROADCAST pkt", __func__); 6285935Ssb155480 (void) vsw_forward_all(vswp, mp, caller, arg); 6295373Sraghuram } else if (IS_MULTICAST(ehp)) { 6305373Sraghuram D2(vswp, "%s: MULTICAST pkt", __func__); 6315935Ssb155480 (void) vsw_forward_grp(vswp, mp, caller, arg); 6325373Sraghuram } else { 6335373Sraghuram /* 6345373Sraghuram * Unicast pkt from vnet that we don't have 6355373Sraghuram * an FDB entry for, so must be destinded for 6365373Sraghuram * the outside world. Attempt to send up to the 6375373Sraghuram * IP layer to allow it to deal with it. 6385373Sraghuram */ 6395373Sraghuram if (caller == VSW_VNETPORT) { 6405935Ssb155480 vsw_mac_rx(vswp, mrh, 6415935Ssb155480 mp, VSW_MACRX_FREEMSG); 6425373Sraghuram } 6435373Sraghuram } 6445373Sraghuram } 6455373Sraghuram } 6465373Sraghuram 6475373Sraghuram D1(vswp, "%s: exit", __func__); 6485373Sraghuram } 6495373Sraghuram 6505373Sraghuram /* 6515373Sraghuram * Forward the ethernet frame to all ports (VNETPORTs, PHYSDEV, LOCALDEV), 6525373Sraghuram * except the caller (port on which frame arrived). 6535373Sraghuram */ 6545373Sraghuram static int 6555935Ssb155480 vsw_forward_all(vsw_t *vswp, mblk_t *mp, int caller, vsw_port_t *arg) 6565373Sraghuram { 6575373Sraghuram vsw_port_list_t *plist = &vswp->plist; 6585373Sraghuram vsw_port_t *portp; 6595373Sraghuram mblk_t *nmp = NULL; 6605373Sraghuram mblk_t *ret_m = NULL; 6615373Sraghuram int skip_port = 0; 6625373Sraghuram 6635373Sraghuram D1(vswp, "vsw_forward_all: enter\n"); 6645373Sraghuram 6655373Sraghuram /* 6665373Sraghuram * Broadcast message from inside ldoms so send to outside 6675373Sraghuram * world if in either of layer 2 modes. 6685373Sraghuram */ 6695373Sraghuram if (((vswp->smode[vswp->smode_idx] == VSW_LAYER2) || 6705373Sraghuram (vswp->smode[vswp->smode_idx] == VSW_LAYER2_PROMISC)) && 6715373Sraghuram ((caller == VSW_LOCALDEV) || (caller == VSW_VNETPORT))) { 6725373Sraghuram 6735373Sraghuram nmp = vsw_dupmsgchain(mp); 6745373Sraghuram if (nmp) { 6755373Sraghuram if ((ret_m = vsw_tx_msg(vswp, nmp)) != NULL) { 6765373Sraghuram DERR(vswp, "%s: dropping pkt(s) " 6775373Sraghuram "consisting of %ld bytes of data for" 6785373Sraghuram " physical device", __func__, MBLKL(ret_m)); 6795373Sraghuram freemsgchain(ret_m); 6805373Sraghuram } 6815373Sraghuram } 6825373Sraghuram } 6835373Sraghuram 6845373Sraghuram if (caller == VSW_VNETPORT) 6855373Sraghuram skip_port = 1; 6865373Sraghuram 6875373Sraghuram /* 6885373Sraghuram * Broadcast message from other vnet (layer 2 or 3) or outside 6895373Sraghuram * world (layer 2 only), send up stack if plumbed. 6905373Sraghuram */ 6915373Sraghuram if ((caller == VSW_PHYSDEV) || (caller == VSW_VNETPORT)) { 6925935Ssb155480 vsw_mac_rx(vswp, NULL, mp, VSW_MACRX_COPYMSG); 6935373Sraghuram } 6945373Sraghuram 6955373Sraghuram /* send it to all VNETPORTs */ 6965373Sraghuram READ_ENTER(&plist->lockrw); 6975373Sraghuram for (portp = plist->head; portp != NULL; portp = portp->p_next) { 6985373Sraghuram D2(vswp, "vsw_forward_all: port %d", portp->p_instance); 6995373Sraghuram /* 7005373Sraghuram * Caution ! - don't reorder these two checks as arg 7015373Sraghuram * will be NULL if the caller is PHYSDEV. skip_port is 7025373Sraghuram * only set if caller is VNETPORT. 7035373Sraghuram */ 7045373Sraghuram if ((skip_port) && (portp == arg)) { 7055373Sraghuram continue; 7065373Sraghuram } else { 7075373Sraghuram nmp = vsw_dupmsgchain(mp); 7085373Sraghuram if (nmp) { 7095935Ssb155480 mblk_t *mpt = nmp; 7105935Ssb155480 uint32_t count = 1; 7115373Sraghuram 7125373Sraghuram /* Find tail */ 7135373Sraghuram while (mpt->b_next != NULL) { 7145373Sraghuram mpt = mpt->b_next; 7155935Ssb155480 count++; 7165373Sraghuram } 7175373Sraghuram /* 7185373Sraghuram * The plist->lockrw is protecting the 7195373Sraghuram * portp from getting destroyed here. 7205373Sraghuram * So, no ref_cnt is incremented here. 7215373Sraghuram */ 7225935Ssb155480 (void) vsw_portsend(portp, nmp, mpt, count); 7235373Sraghuram } else { 7245373Sraghuram DERR(vswp, "vsw_forward_all: nmp NULL"); 7255373Sraghuram } 7265373Sraghuram } 7275373Sraghuram } 7285373Sraghuram RW_EXIT(&plist->lockrw); 7295373Sraghuram 7305373Sraghuram freemsgchain(mp); 7315373Sraghuram 7325373Sraghuram D1(vswp, "vsw_forward_all: exit\n"); 7335373Sraghuram return (0); 7345373Sraghuram } 7355373Sraghuram 7365373Sraghuram /* 7375373Sraghuram * Forward pkts to any devices or interfaces which have registered 7385373Sraghuram * an interest in them (i.e. multicast groups). 7395373Sraghuram */ 7405373Sraghuram static int 7415935Ssb155480 vsw_forward_grp(vsw_t *vswp, mblk_t *mp, int caller, vsw_port_t *arg) 7425373Sraghuram { 7435373Sraghuram struct ether_header *ehp = (struct ether_header *)mp->b_rptr; 7445373Sraghuram mfdb_ent_t *entp = NULL; 7455373Sraghuram mfdb_ent_t *tpp = NULL; 7465373Sraghuram vsw_port_t *port; 7475373Sraghuram uint64_t key = 0; 7485373Sraghuram mblk_t *nmp = NULL; 7495373Sraghuram mblk_t *ret_m = NULL; 7505373Sraghuram boolean_t check_if = B_TRUE; 7515373Sraghuram 7525373Sraghuram /* 7535373Sraghuram * Convert address to hash table key 7545373Sraghuram */ 7556419Ssb155480 KEY_HASH(key, &ehp->ether_dhost); 7565373Sraghuram 7575373Sraghuram D1(vswp, "%s: key 0x%llx", __func__, key); 7585373Sraghuram 7595373Sraghuram /* 7605373Sraghuram * If pkt came from either a vnet or down the stack (if we are 7615373Sraghuram * plumbed) and we are in layer 2 mode, then we send the pkt out 7625373Sraghuram * over the physical adapter, and then check to see if any other 7635373Sraghuram * vnets are interested in it. 7645373Sraghuram */ 7655373Sraghuram if (((vswp->smode[vswp->smode_idx] == VSW_LAYER2) || 7665373Sraghuram (vswp->smode[vswp->smode_idx] == VSW_LAYER2_PROMISC)) && 7675373Sraghuram ((caller == VSW_VNETPORT) || (caller == VSW_LOCALDEV))) { 7685373Sraghuram nmp = vsw_dupmsgchain(mp); 7695373Sraghuram if (nmp) { 7705373Sraghuram if ((ret_m = vsw_tx_msg(vswp, nmp)) != NULL) { 7715373Sraghuram DERR(vswp, "%s: dropping pkt(s) consisting of " 7725373Sraghuram "%ld bytes of data for physical device", 7735373Sraghuram __func__, MBLKL(ret_m)); 7745373Sraghuram freemsgchain(ret_m); 7755373Sraghuram } 7765373Sraghuram } 7775373Sraghuram } 7785373Sraghuram 7795373Sraghuram READ_ENTER(&vswp->mfdbrw); 7805373Sraghuram if (mod_hash_find(vswp->mfdb, (mod_hash_key_t)key, 7815373Sraghuram (mod_hash_val_t *)&entp) != 0) { 7825373Sraghuram D3(vswp, "%s: no table entry found for addr 0x%llx", 7835373Sraghuram __func__, key); 7845373Sraghuram } else { 7855373Sraghuram /* 7865373Sraghuram * Send to list of devices associated with this address... 7875373Sraghuram */ 7885373Sraghuram for (tpp = entp; tpp != NULL; tpp = tpp->nextp) { 7895373Sraghuram 7905373Sraghuram /* dont send to ourselves */ 7915373Sraghuram if ((caller == VSW_VNETPORT) && 7925373Sraghuram (tpp->d_addr == (void *)arg)) { 7935373Sraghuram port = (vsw_port_t *)tpp->d_addr; 7945373Sraghuram D3(vswp, "%s: not sending to ourselves" 7955373Sraghuram " : port %d", __func__, port->p_instance); 7965373Sraghuram continue; 7975373Sraghuram 7985373Sraghuram } else if ((caller == VSW_LOCALDEV) && 7995373Sraghuram (tpp->d_type == VSW_LOCALDEV)) { 8005935Ssb155480 D2(vswp, "%s: not sending back up stack", 8015373Sraghuram __func__); 8025373Sraghuram continue; 8035373Sraghuram } 8045373Sraghuram 8055373Sraghuram if (tpp->d_type == VSW_VNETPORT) { 8065373Sraghuram port = (vsw_port_t *)tpp->d_addr; 8075373Sraghuram D3(vswp, "%s: sending to port %ld for addr " 8085373Sraghuram "0x%llx", __func__, port->p_instance, key); 8095373Sraghuram 8105373Sraghuram nmp = vsw_dupmsgchain(mp); 8115373Sraghuram if (nmp) { 8125935Ssb155480 mblk_t *mpt = nmp; 8135935Ssb155480 uint32_t count = 1; 8145373Sraghuram 8155373Sraghuram /* Find tail */ 8165373Sraghuram while (mpt->b_next != NULL) { 8175373Sraghuram mpt = mpt->b_next; 8185935Ssb155480 count++; 8195373Sraghuram } 8205373Sraghuram /* 8215373Sraghuram * The vswp->mfdbrw is protecting the 8225373Sraghuram * portp from getting destroyed here. 8235373Sraghuram * So, no ref_cnt is incremented here. 8245373Sraghuram */ 8255935Ssb155480 (void) vsw_portsend(port, nmp, mpt, 8265935Ssb155480 count); 8275373Sraghuram } 8285373Sraghuram } else { 8295935Ssb155480 vsw_mac_rx(vswp, NULL, 8305935Ssb155480 mp, VSW_MACRX_COPYMSG); 8315935Ssb155480 D2(vswp, "%s: sending up stack" 8325373Sraghuram " for addr 0x%llx", __func__, key); 8335373Sraghuram check_if = B_FALSE; 8345373Sraghuram } 8355373Sraghuram } 8365373Sraghuram } 8375373Sraghuram 8385373Sraghuram RW_EXIT(&vswp->mfdbrw); 8395373Sraghuram 8405373Sraghuram /* 8415373Sraghuram * If the pkt came from either a vnet or from physical device, 8425373Sraghuram * and if we havent already sent the pkt up the stack then we 8435373Sraghuram * check now if we can/should (i.e. the interface is plumbed 8445373Sraghuram * and in promisc mode). 8455373Sraghuram */ 8465373Sraghuram if ((check_if) && 8475373Sraghuram ((caller == VSW_VNETPORT) || (caller == VSW_PHYSDEV))) { 8485935Ssb155480 vsw_mac_rx(vswp, NULL, mp, 8495373Sraghuram VSW_MACRX_PROMISC | VSW_MACRX_COPYMSG); 8505373Sraghuram } 8515373Sraghuram 8525373Sraghuram freemsgchain(mp); 8535373Sraghuram 8545373Sraghuram D1(vswp, "%s: exit", __func__); 8555373Sraghuram 8565373Sraghuram return (0); 8575373Sraghuram } 8585373Sraghuram 8595373Sraghuram /* 8606419Ssb155480 * This function creates the vlan id hash table for the given vsw device or 8616419Ssb155480 * port. It then adds each vlan that the device or port has been assigned, 8626419Ssb155480 * into this hash table. 8636419Ssb155480 * Arguments: 8646419Ssb155480 * arg: vsw device or port. 8656419Ssb155480 * type: type of arg; VSW_LOCALDEV(vsw device) or VSW_VNETPORT(port). 8666419Ssb155480 */ 8676419Ssb155480 void 8686419Ssb155480 vsw_create_vlans(void *arg, int type) 8696419Ssb155480 { 8706419Ssb155480 /* create vlan hash table */ 8716419Ssb155480 vsw_vlan_create_hash(arg, type); 8726419Ssb155480 8736419Ssb155480 /* add vlan ids of the vsw device into its hash table */ 8746419Ssb155480 vsw_vlan_add_ids(arg, type); 8756419Ssb155480 } 8766419Ssb155480 8776419Ssb155480 /* 8786419Ssb155480 * This function removes the vlan ids of the vsw device or port from its hash 8796419Ssb155480 * table. It then destroys the vlan hash table. 8806419Ssb155480 * Arguments: 8816419Ssb155480 * arg: vsw device or port. 8826419Ssb155480 * type: type of arg; VSW_LOCALDEV(vsw device) or VSW_VNETPORT(port). 8836419Ssb155480 */ 8846419Ssb155480 void 8856419Ssb155480 vsw_destroy_vlans(void *arg, int type) 8866419Ssb155480 { 8876419Ssb155480 /* remove vlan ids from the hash table */ 8886419Ssb155480 vsw_vlan_remove_ids(arg, type); 8896419Ssb155480 8906419Ssb155480 /* destroy vlan-hash-table */ 8916419Ssb155480 vsw_vlan_destroy_hash(arg, type); 8926419Ssb155480 } 8936419Ssb155480 8946419Ssb155480 /* 8956419Ssb155480 * Create a vlan-id hash table for the given vsw device or port. 8966419Ssb155480 */ 8976419Ssb155480 static void 8986419Ssb155480 vsw_vlan_create_hash(void *arg, int type) 8996419Ssb155480 { 9006419Ssb155480 char hashname[MAXNAMELEN]; 9016419Ssb155480 9026419Ssb155480 if (type == VSW_LOCALDEV) { 9036419Ssb155480 vsw_t *vswp = (vsw_t *)arg; 9046419Ssb155480 9056419Ssb155480 (void) snprintf(hashname, MAXNAMELEN, "vsw%d-vlan-hash", 9066419Ssb155480 vswp->instance); 9076419Ssb155480 9086419Ssb155480 vswp->vlan_nchains = vsw_vlan_nchains; 9096419Ssb155480 vswp->vlan_hashp = mod_hash_create_idhash(hashname, 9106419Ssb155480 vswp->vlan_nchains, mod_hash_null_valdtor); 9116419Ssb155480 9126419Ssb155480 } else if (type == VSW_VNETPORT) { 9136419Ssb155480 vsw_port_t *portp = (vsw_port_t *)arg; 9146419Ssb155480 9156419Ssb155480 (void) snprintf(hashname, MAXNAMELEN, "port%d-vlan-hash", 9166419Ssb155480 portp->p_instance); 9176419Ssb155480 9186419Ssb155480 portp->vlan_nchains = vsw_vlan_nchains; 9196419Ssb155480 portp->vlan_hashp = mod_hash_create_idhash(hashname, 9206419Ssb155480 portp->vlan_nchains, mod_hash_null_valdtor); 9216419Ssb155480 9226419Ssb155480 } else { 9236419Ssb155480 return; 9246419Ssb155480 } 9256419Ssb155480 } 9266419Ssb155480 9276419Ssb155480 /* 9286419Ssb155480 * Destroy the vlan-id hash table for the given vsw device or port. 9296419Ssb155480 */ 9306419Ssb155480 static void 9316419Ssb155480 vsw_vlan_destroy_hash(void *arg, int type) 9326419Ssb155480 { 9336419Ssb155480 if (type == VSW_LOCALDEV) { 9346419Ssb155480 vsw_t *vswp = (vsw_t *)arg; 9356419Ssb155480 9366419Ssb155480 mod_hash_destroy_hash(vswp->vlan_hashp); 9376419Ssb155480 vswp->vlan_nchains = 0; 9386419Ssb155480 } else if (type == VSW_VNETPORT) { 9396419Ssb155480 vsw_port_t *portp = (vsw_port_t *)arg; 9406419Ssb155480 9416419Ssb155480 mod_hash_destroy_hash(portp->vlan_hashp); 9426419Ssb155480 portp->vlan_nchains = 0; 9436419Ssb155480 } else { 9446419Ssb155480 return; 9456419Ssb155480 } 9466419Ssb155480 } 9476419Ssb155480 9486419Ssb155480 /* 9496419Ssb155480 * Add vlan ids of the given vsw device or port into its hash table. 9505373Sraghuram */ 9516419Ssb155480 void 9526419Ssb155480 vsw_vlan_add_ids(void *arg, int type) 9536419Ssb155480 { 9546419Ssb155480 int rv; 9556419Ssb155480 int i; 9566419Ssb155480 9576419Ssb155480 if (type == VSW_LOCALDEV) { 9586419Ssb155480 vsw_t *vswp = (vsw_t *)arg; 9596419Ssb155480 9606419Ssb155480 rv = mod_hash_insert(vswp->vlan_hashp, 9616419Ssb155480 (mod_hash_key_t)VLAN_ID_KEY(vswp->pvid), 9626419Ssb155480 (mod_hash_val_t)B_TRUE); 9636419Ssb155480 ASSERT(rv == 0); 9646419Ssb155480 9656419Ssb155480 for (i = 0; i < vswp->nvids; i++) { 9666419Ssb155480 rv = mod_hash_insert(vswp->vlan_hashp, 9676419Ssb155480 (mod_hash_key_t)VLAN_ID_KEY(vswp->vids[i]), 9686419Ssb155480 (mod_hash_val_t)B_TRUE); 9696419Ssb155480 ASSERT(rv == 0); 9706419Ssb155480 } 9716419Ssb155480 9726419Ssb155480 } else if (type == VSW_VNETPORT) { 9736419Ssb155480 vsw_port_t *portp = (vsw_port_t *)arg; 9746419Ssb155480 9756419Ssb155480 rv = mod_hash_insert(portp->vlan_hashp, 9766419Ssb155480 (mod_hash_key_t)VLAN_ID_KEY(portp->pvid), 9776419Ssb155480 (mod_hash_val_t)B_TRUE); 9786419Ssb155480 ASSERT(rv == 0); 9796419Ssb155480 9806419Ssb155480 for (i = 0; i < portp->nvids; i++) { 9816419Ssb155480 rv = mod_hash_insert(portp->vlan_hashp, 9826419Ssb155480 (mod_hash_key_t)VLAN_ID_KEY(portp->vids[i]), 9836419Ssb155480 (mod_hash_val_t)B_TRUE); 9846419Ssb155480 ASSERT(rv == 0); 9856419Ssb155480 } 9866419Ssb155480 9876419Ssb155480 } else { 9886419Ssb155480 return; 9896419Ssb155480 } 9906419Ssb155480 } 9916419Ssb155480 9926419Ssb155480 /* 9936419Ssb155480 * Remove vlan ids of the given vsw device or port from its hash table. 9946419Ssb155480 */ 9956419Ssb155480 void 9966419Ssb155480 vsw_vlan_remove_ids(void *arg, int type) 9976419Ssb155480 { 9986419Ssb155480 mod_hash_val_t vp; 9996419Ssb155480 int rv; 10006419Ssb155480 int i; 10016419Ssb155480 10026419Ssb155480 if (type == VSW_LOCALDEV) { 10036419Ssb155480 vsw_t *vswp = (vsw_t *)arg; 10046419Ssb155480 10056419Ssb155480 rv = vsw_vlan_lookup(vswp->vlan_hashp, vswp->pvid); 10066419Ssb155480 if (rv == B_TRUE) { 10076419Ssb155480 rv = mod_hash_remove(vswp->vlan_hashp, 10086419Ssb155480 (mod_hash_key_t)VLAN_ID_KEY(vswp->pvid), 10096419Ssb155480 (mod_hash_val_t *)&vp); 10106419Ssb155480 ASSERT(rv == 0); 10116419Ssb155480 } 10126419Ssb155480 10136419Ssb155480 for (i = 0; i < vswp->nvids; i++) { 10146419Ssb155480 rv = vsw_vlan_lookup(vswp->vlan_hashp, vswp->vids[i]); 10156419Ssb155480 if (rv == B_TRUE) { 10166419Ssb155480 rv = mod_hash_remove(vswp->vlan_hashp, 10176419Ssb155480 (mod_hash_key_t)VLAN_ID_KEY(vswp->vids[i]), 10186419Ssb155480 (mod_hash_val_t *)&vp); 10196419Ssb155480 ASSERT(rv == 0); 10206419Ssb155480 } 10216419Ssb155480 } 10226419Ssb155480 10236419Ssb155480 } else if (type == VSW_VNETPORT) { 10246419Ssb155480 vsw_port_t *portp = (vsw_port_t *)arg; 10256419Ssb155480 10266419Ssb155480 portp = (vsw_port_t *)arg; 10276419Ssb155480 rv = vsw_vlan_lookup(portp->vlan_hashp, portp->pvid); 10286419Ssb155480 if (rv == B_TRUE) { 10296419Ssb155480 rv = mod_hash_remove(portp->vlan_hashp, 10306419Ssb155480 (mod_hash_key_t)VLAN_ID_KEY(portp->pvid), 10316419Ssb155480 (mod_hash_val_t *)&vp); 10326419Ssb155480 ASSERT(rv == 0); 10336419Ssb155480 } 10346419Ssb155480 10356419Ssb155480 for (i = 0; i < portp->nvids; i++) { 10366419Ssb155480 rv = vsw_vlan_lookup(portp->vlan_hashp, portp->vids[i]); 10376419Ssb155480 if (rv == B_TRUE) { 10386419Ssb155480 rv = mod_hash_remove(portp->vlan_hashp, 10396419Ssb155480 (mod_hash_key_t)VLAN_ID_KEY(portp->vids[i]), 10406419Ssb155480 (mod_hash_val_t *)&vp); 10416419Ssb155480 ASSERT(rv == 0); 10426419Ssb155480 } 10436419Ssb155480 } 10446419Ssb155480 10456419Ssb155480 } else { 10466419Ssb155480 return; 10476419Ssb155480 } 10486419Ssb155480 } 10496419Ssb155480 10506419Ssb155480 /* 10516419Ssb155480 * Find the given vlan id in the hash table. 10526419Ssb155480 * Return: B_TRUE if the id is found; B_FALSE if not found. 10536419Ssb155480 */ 10546419Ssb155480 boolean_t 10556419Ssb155480 vsw_vlan_lookup(mod_hash_t *vlan_hashp, uint16_t vid) 10566419Ssb155480 { 10576419Ssb155480 int rv; 10586419Ssb155480 mod_hash_val_t vp; 10596419Ssb155480 10606419Ssb155480 rv = mod_hash_find(vlan_hashp, VLAN_ID_KEY(vid), (mod_hash_val_t *)&vp); 10616419Ssb155480 10626419Ssb155480 if (rv != 0) 10636419Ssb155480 return (B_FALSE); 10646419Ssb155480 10656419Ssb155480 return (B_TRUE); 10666419Ssb155480 } 10676419Ssb155480 10686419Ssb155480 /* 10696419Ssb155480 * Add an entry into FDB for the given vsw. 10706419Ssb155480 */ 10716419Ssb155480 void 10726419Ssb155480 vsw_fdbe_add(vsw_t *vswp, void *port) 10735373Sraghuram { 10745373Sraghuram uint64_t addr = 0; 10756419Ssb155480 vsw_port_t *portp; 10766419Ssb155480 vsw_fdbe_t *fp; 10776419Ssb155480 int rv; 10785373Sraghuram 10796419Ssb155480 portp = (vsw_port_t *)port; 10806419Ssb155480 KEY_HASH(addr, &portp->p_macaddr); 10815373Sraghuram 10826419Ssb155480 fp = kmem_zalloc(sizeof (vsw_fdbe_t), KM_SLEEP); 10836419Ssb155480 fp->portp = port; 10845373Sraghuram 10855373Sraghuram /* 10865373Sraghuram * Note: duplicate keys will be rejected by mod_hash. 10875373Sraghuram */ 10886419Ssb155480 rv = mod_hash_insert(vswp->fdb_hashp, (mod_hash_key_t)addr, 10896419Ssb155480 (mod_hash_val_t)fp); 10906419Ssb155480 ASSERT(rv == 0); 10915373Sraghuram } 10925373Sraghuram 10935373Sraghuram /* 10945373Sraghuram * Remove an entry from FDB. 10955373Sraghuram */ 10966419Ssb155480 void 10976419Ssb155480 vsw_fdbe_del(vsw_t *vswp, struct ether_addr *eaddr) 10985373Sraghuram { 10995373Sraghuram uint64_t addr = 0; 11006419Ssb155480 vsw_fdbe_t *fp; 11016419Ssb155480 int rv; 11025373Sraghuram 11036419Ssb155480 KEY_HASH(addr, eaddr); 11046419Ssb155480 11056419Ssb155480 /* 11066419Ssb155480 * Remove the entry from fdb hash table. 11076419Ssb155480 * This prevents further references to this fdb entry. 11086419Ssb155480 */ 11096419Ssb155480 rv = mod_hash_remove(vswp->fdb_hashp, (mod_hash_key_t)addr, 11106419Ssb155480 (mod_hash_val_t *)&fp); 11116419Ssb155480 if (rv != 0) { 11126419Ssb155480 /* invalid key? */ 11136419Ssb155480 return; 11146419Ssb155480 } 11156419Ssb155480 11166419Ssb155480 /* 11176419Ssb155480 * If there are threads already ref holding before the entry was 11186419Ssb155480 * removed from hash table, then wait for ref count to drop to zero. 11196419Ssb155480 */ 11206419Ssb155480 while (fp->refcnt != 0) { 11216419Ssb155480 delay(drv_usectohz(vsw_fdbe_refcnt_delay)); 11226419Ssb155480 } 11236419Ssb155480 11246419Ssb155480 kmem_free(fp, sizeof (*fp)); 11256419Ssb155480 } 11265373Sraghuram 11276419Ssb155480 /* 11286419Ssb155480 * Search fdb for a given mac address. If an entry is found, hold 11296419Ssb155480 * a reference to it and return the entry, else returns NULL. 11306419Ssb155480 */ 11316419Ssb155480 static vsw_fdbe_t * 11326419Ssb155480 vsw_fdbe_find(vsw_t *vswp, struct ether_addr *addrp) 11336419Ssb155480 { 11346419Ssb155480 uint64_t key = 0; 11356419Ssb155480 vsw_fdbe_t *fp; 11366419Ssb155480 int rv; 11376419Ssb155480 11386419Ssb155480 KEY_HASH(key, addrp); 11396419Ssb155480 11406419Ssb155480 rv = mod_hash_find_cb(vswp->fdb_hashp, (mod_hash_key_t)key, 11416419Ssb155480 (mod_hash_val_t *)&fp, vsw_fdbe_find_cb); 11426419Ssb155480 11436419Ssb155480 if (rv != 0) 11446419Ssb155480 return (NULL); 11456419Ssb155480 11466419Ssb155480 return (fp); 11476419Ssb155480 } 11486419Ssb155480 11496419Ssb155480 /* 11506419Ssb155480 * Callback function provided to mod_hash_find_cb(). After finding the fdb 11516419Ssb155480 * entry corresponding to the key (macaddr), this callback will be invoked by 11526419Ssb155480 * mod_hash_find_cb() to atomically increment the reference count on the fdb 11536419Ssb155480 * entry before returning the found entry. 11546419Ssb155480 */ 11556419Ssb155480 static void 11566419Ssb155480 vsw_fdbe_find_cb(mod_hash_key_t key, mod_hash_val_t val) 11576419Ssb155480 { 11586419Ssb155480 _NOTE(ARGUNUSED(key)) 11596419Ssb155480 VSW_FDBE_REFHOLD((vsw_fdbe_t *)val); 11606419Ssb155480 } 11615373Sraghuram 11626419Ssb155480 /* 11636419Ssb155480 * A given frame must be always tagged with the appropriate vlan id (unless it 11646419Ssb155480 * is in the default-vlan) before the mac address switching function is called. 11656419Ssb155480 * Otherwise, after switching function determines the destination, we cannot 11666419Ssb155480 * figure out if the destination belongs to the the same vlan that the frame 11676419Ssb155480 * originated from and if it needs tag/untag. Frames which are inbound from 11686419Ssb155480 * the external(physical) network over a vlan trunk link are always tagged. 11696419Ssb155480 * However frames which are received from a vnet-port over ldc or frames which 11706419Ssb155480 * are coming down the stack on the service domain over vsw interface may be 11716419Ssb155480 * untagged. These frames must be tagged with the appropriate pvid of the 11726419Ssb155480 * sender (vnet-port or vsw device), before invoking the switching function. 11736419Ssb155480 * 11746419Ssb155480 * Arguments: 11756419Ssb155480 * arg: caller of the function. 11766419Ssb155480 * type: type of arg(caller): VSW_LOCALDEV(vsw) or VSW_VNETPORT(port) 11776419Ssb155480 * mp: frame(s) to be tagged. 11786419Ssb155480 */ 11796419Ssb155480 mblk_t * 11806419Ssb155480 vsw_vlan_frame_pretag(void *arg, int type, mblk_t *mp) 11816419Ssb155480 { 11826419Ssb155480 vsw_t *vswp; 11836419Ssb155480 vsw_port_t *portp; 11846419Ssb155480 struct ether_header *ehp; 11856419Ssb155480 mblk_t *bp; 11866419Ssb155480 mblk_t *bpt; 11876419Ssb155480 mblk_t *bph; 11886419Ssb155480 mblk_t *bpn; 11896419Ssb155480 uint16_t pvid; 11905373Sraghuram 11916419Ssb155480 ASSERT((type == VSW_LOCALDEV) || (type == VSW_VNETPORT)); 11925373Sraghuram 11936419Ssb155480 if (type == VSW_LOCALDEV) { 11946419Ssb155480 vswp = (vsw_t *)arg; 11956419Ssb155480 pvid = vswp->pvid; 11966419Ssb155480 portp = NULL; 11976419Ssb155480 } else { 11986419Ssb155480 /* VSW_VNETPORT */ 11996419Ssb155480 portp = (vsw_port_t *)arg; 12006419Ssb155480 pvid = portp->pvid; 12016419Ssb155480 vswp = portp->p_vswp; 12026419Ssb155480 } 12036419Ssb155480 12046419Ssb155480 bpn = bph = bpt = NULL; 12056419Ssb155480 12066419Ssb155480 for (bp = mp; bp != NULL; bp = bpn) { 12076419Ssb155480 12086419Ssb155480 bpn = bp->b_next; 12096419Ssb155480 bp->b_next = bp->b_prev = NULL; 12106419Ssb155480 12116419Ssb155480 /* Determine if it is an untagged frame */ 12126419Ssb155480 ehp = (struct ether_header *)bp->b_rptr; 12135373Sraghuram 12146419Ssb155480 if (ehp->ether_type != ETHERTYPE_VLAN) { /* untagged */ 12156419Ssb155480 12166419Ssb155480 /* no need to tag if the frame is in default vlan */ 12176419Ssb155480 if (pvid != vswp->default_vlan_id) { 12186419Ssb155480 bp = vnet_vlan_insert_tag(bp, pvid); 12196419Ssb155480 if (bp == NULL) { 12206419Ssb155480 continue; 12216419Ssb155480 } 12226419Ssb155480 } 12236419Ssb155480 } 12246419Ssb155480 12256419Ssb155480 /* build a chain of processed packets */ 12266419Ssb155480 if (bph == NULL) { 12276419Ssb155480 bph = bpt = bp; 12286419Ssb155480 } else { 12296419Ssb155480 bpt->b_next = bp; 12306419Ssb155480 bpt = bp; 12316419Ssb155480 } 12326419Ssb155480 12336419Ssb155480 } 12346419Ssb155480 12356419Ssb155480 return (bph); 12365373Sraghuram } 12375373Sraghuram 12385373Sraghuram /* 12396419Ssb155480 * Frames destined to a vnet-port or to the local vsw interface, must be 12406419Ssb155480 * untagged if necessary before sending. This function first checks that the 12416419Ssb155480 * frame can be sent to the destination in the vlan identified by the frame 12426419Ssb155480 * tag. Note that when this function is invoked the frame must have been 12436419Ssb155480 * already tagged (unless it is in the default-vlan). Because, this function is 12446419Ssb155480 * called when the switching function determines the destination and invokes 12456419Ssb155480 * its send function (vnet-port or vsw interface) and all frames would have 12466419Ssb155480 * been tagged by this time (see comments in vsw_vlan_frame_pretag()). 12476419Ssb155480 * 12486419Ssb155480 * Arguments: 12496419Ssb155480 * arg: destination device. 12506419Ssb155480 * type: type of arg(destination): VSW_LOCALDEV(vsw) or VSW_VNETPORT(port) 12516419Ssb155480 * np: head of pkt chain to be validated and untagged. 12526419Ssb155480 * npt: tail of pkt chain to be validated and untagged. 12536419Ssb155480 * 12546419Ssb155480 * Returns: 12556419Ssb155480 * np: head of updated chain of packets 12566419Ssb155480 * npt: tail of updated chain of packets 12576419Ssb155480 * rv: count of any packets dropped 12585373Sraghuram */ 12596419Ssb155480 uint32_t 12606419Ssb155480 vsw_vlan_frame_untag(void *arg, int type, mblk_t **np, mblk_t **npt) 12615373Sraghuram { 12626419Ssb155480 mblk_t *bp; 12636419Ssb155480 mblk_t *bpt; 12646419Ssb155480 mblk_t *bph; 12656419Ssb155480 mblk_t *bpn; 12666419Ssb155480 vsw_port_t *portp; 12676419Ssb155480 vsw_t *vswp; 12686419Ssb155480 uint32_t count; 12696419Ssb155480 struct ether_header *ehp; 12706419Ssb155480 boolean_t is_tagged; 12716419Ssb155480 boolean_t rv; 12726419Ssb155480 uint16_t vlan_id; 12736419Ssb155480 uint16_t pvid; 12746419Ssb155480 mod_hash_t *vlan_hashp; 12755373Sraghuram 12766419Ssb155480 ASSERT((type == VSW_LOCALDEV) || (type == VSW_VNETPORT)); 12775373Sraghuram 12786419Ssb155480 if (type == VSW_LOCALDEV) { 12796419Ssb155480 vswp = (vsw_t *)arg; 12806419Ssb155480 pvid = vswp->pvid; 12816419Ssb155480 vlan_hashp = vswp->vlan_hashp; 12826419Ssb155480 portp = NULL; 12836419Ssb155480 } else { 12846419Ssb155480 /* type == VSW_VNETPORT */ 12856419Ssb155480 portp = (vsw_port_t *)arg; 12866419Ssb155480 vswp = portp->p_vswp; 12876419Ssb155480 vlan_hashp = portp->vlan_hashp; 12886419Ssb155480 pvid = portp->pvid; 12895373Sraghuram } 12905373Sraghuram 12916419Ssb155480 bpn = bph = bpt = NULL; 12926419Ssb155480 count = 0; 12936419Ssb155480 12946419Ssb155480 for (bp = *np; bp != NULL; bp = bpn) { 12956419Ssb155480 12966419Ssb155480 bpn = bp->b_next; 12976419Ssb155480 bp->b_next = bp->b_prev = NULL; 12986419Ssb155480 12996419Ssb155480 /* 13006419Ssb155480 * Determine the vlan id that the frame belongs to. 13016419Ssb155480 */ 13026419Ssb155480 ehp = (struct ether_header *)bp->b_rptr; 13036419Ssb155480 is_tagged = vsw_frame_lookup_vid(arg, type, ehp, &vlan_id); 13046419Ssb155480 13056419Ssb155480 /* 13066419Ssb155480 * Check if the destination is in the same vlan. 13076419Ssb155480 */ 13086419Ssb155480 rv = vsw_vlan_lookup(vlan_hashp, vlan_id); 13096419Ssb155480 if (rv == B_FALSE) { 13106419Ssb155480 /* drop the packet */ 13116419Ssb155480 freemsg(bp); 13126419Ssb155480 count++; 13136419Ssb155480 continue; 13146419Ssb155480 } 13156419Ssb155480 13166419Ssb155480 /* 13176419Ssb155480 * Check the frame header if tag/untag is needed. 13186419Ssb155480 */ 13196419Ssb155480 if (is_tagged == B_FALSE) { 13206419Ssb155480 /* 13216419Ssb155480 * Untagged frame. We shouldn't have an untagged 13226419Ssb155480 * packet at this point, unless the destination's 13236419Ssb155480 * vlan id is default-vlan-id; if it is not the 13246419Ssb155480 * default-vlan-id, we drop the packet. 13256419Ssb155480 */ 13266419Ssb155480 if (vlan_id != vswp->default_vlan_id) { 13276419Ssb155480 /* drop the packet */ 13286419Ssb155480 freemsg(bp); 13296419Ssb155480 count++; 13306419Ssb155480 continue; 13316419Ssb155480 } 13326419Ssb155480 } else { 13336419Ssb155480 /* 13346419Ssb155480 * Tagged frame, untag if it's the destination's pvid. 13356419Ssb155480 */ 13366419Ssb155480 if (vlan_id == pvid) { 13376419Ssb155480 13386419Ssb155480 bp = vnet_vlan_remove_tag(bp); 13396419Ssb155480 if (bp == NULL) { 13406419Ssb155480 /* packet dropped */ 13416419Ssb155480 count++; 13426419Ssb155480 continue; 13436419Ssb155480 } 13446419Ssb155480 } 13456419Ssb155480 } 13465373Sraghuram 13476419Ssb155480 /* build a chain of processed packets */ 13486419Ssb155480 if (bph == NULL) { 13496419Ssb155480 bph = bpt = bp; 13506419Ssb155480 } else { 13516419Ssb155480 bpt->b_next = bp; 13526419Ssb155480 bpt = bp; 13536419Ssb155480 } 13546419Ssb155480 13556419Ssb155480 } 13566419Ssb155480 13576419Ssb155480 *np = bph; 13586419Ssb155480 *npt = bpt; 13596419Ssb155480 13606419Ssb155480 return (count); 13616419Ssb155480 } 13626419Ssb155480 13636419Ssb155480 /* 13646419Ssb155480 * Lookup the vlan id of the given frame. If it is a vlan-tagged frame, 13656419Ssb155480 * then the vlan-id is available in the tag; otherwise, its vlan id is 13666419Ssb155480 * implicitly obtained based on the caller (destination of the frame: 13676419Ssb155480 * VSW_VNETPORT or VSW_LOCALDEV). 13686419Ssb155480 * The vlan id determined is returned in vidp. 13696419Ssb155480 * Returns: B_TRUE if it is a tagged frame; B_FALSE if it is untagged. 13706419Ssb155480 */ 13716419Ssb155480 boolean_t 13726419Ssb155480 vsw_frame_lookup_vid(void *arg, int caller, struct ether_header *ehp, 13736419Ssb155480 uint16_t *vidp) 13746419Ssb155480 { 13756419Ssb155480 struct ether_vlan_header *evhp; 13766419Ssb155480 vsw_t *vswp; 13776419Ssb155480 vsw_port_t *portp; 13786419Ssb155480 13796419Ssb155480 /* If it's a tagged frame, get the vid from vlan header */ 13806419Ssb155480 if (ehp->ether_type == ETHERTYPE_VLAN) { 13816419Ssb155480 13826419Ssb155480 evhp = (struct ether_vlan_header *)ehp; 13836419Ssb155480 *vidp = VLAN_ID(ntohs(evhp->ether_tci)); 13846419Ssb155480 return (B_TRUE); 13856419Ssb155480 } 13866419Ssb155480 13876419Ssb155480 /* Untagged frame; determine vlan id based on caller */ 13886419Ssb155480 switch (caller) { 13896419Ssb155480 13906419Ssb155480 case VSW_VNETPORT: 13916419Ssb155480 /* 13926419Ssb155480 * packet destined to a vnet; vlan-id is pvid of vnet-port. 13936419Ssb155480 */ 13946419Ssb155480 portp = (vsw_port_t *)arg; 13956419Ssb155480 *vidp = portp->pvid; 13966419Ssb155480 break; 13976419Ssb155480 13986419Ssb155480 case VSW_LOCALDEV: 13996419Ssb155480 14006419Ssb155480 /* 14016419Ssb155480 * packet destined to vsw interface; 14026419Ssb155480 * vlan-id is port-vlan-id of vsw device. 14036419Ssb155480 */ 14046419Ssb155480 vswp = (vsw_t *)arg; 14056419Ssb155480 *vidp = vswp->pvid; 14066419Ssb155480 break; 14076419Ssb155480 } 14086419Ssb155480 14096419Ssb155480 return (B_FALSE); 14105373Sraghuram } 14115373Sraghuram 14125373Sraghuram /* 14135373Sraghuram * Add or remove multicast address(es). 14145373Sraghuram * 14155373Sraghuram * Returns 0 on success, 1 on failure. 14165373Sraghuram */ 14175373Sraghuram int 14185373Sraghuram vsw_add_rem_mcst(vnet_mcast_msg_t *mcst_pkt, vsw_port_t *port) 14195373Sraghuram { 14205373Sraghuram mcst_addr_t *mcst_p = NULL; 14215373Sraghuram vsw_t *vswp = port->p_vswp; 14225373Sraghuram uint64_t addr = 0x0; 14235373Sraghuram int i; 14245373Sraghuram 14255373Sraghuram D1(vswp, "%s: enter", __func__); 14265373Sraghuram 14275373Sraghuram D2(vswp, "%s: %d addresses", __func__, mcst_pkt->count); 14285373Sraghuram 14295373Sraghuram for (i = 0; i < mcst_pkt->count; i++) { 14305373Sraghuram /* 14315373Sraghuram * Convert address into form that can be used 14325373Sraghuram * as hash table key. 14335373Sraghuram */ 14346419Ssb155480 KEY_HASH(addr, &(mcst_pkt->mca[i])); 14355373Sraghuram 14365373Sraghuram /* 14375373Sraghuram * Add or delete the specified address/port combination. 14385373Sraghuram */ 14395373Sraghuram if (mcst_pkt->set == 0x1) { 14405373Sraghuram D3(vswp, "%s: adding multicast address 0x%llx for " 14415373Sraghuram "port %ld", __func__, addr, port->p_instance); 14425373Sraghuram if (vsw_add_mcst(vswp, VSW_VNETPORT, addr, port) == 0) { 14435373Sraghuram /* 14445373Sraghuram * Update the list of multicast 14455373Sraghuram * addresses contained within the 14465373Sraghuram * port structure to include this new 14475373Sraghuram * one. 14485373Sraghuram */ 14495373Sraghuram mcst_p = kmem_zalloc(sizeof (mcst_addr_t), 14505373Sraghuram KM_NOSLEEP); 14515373Sraghuram if (mcst_p == NULL) { 14525373Sraghuram DERR(vswp, "%s: unable to alloc mem", 14535373Sraghuram __func__); 14545373Sraghuram (void) vsw_del_mcst(vswp, 14555373Sraghuram VSW_VNETPORT, addr, port); 14565373Sraghuram return (1); 14575373Sraghuram } 14585373Sraghuram 14595373Sraghuram mcst_p->nextp = NULL; 14605373Sraghuram mcst_p->addr = addr; 14615373Sraghuram ether_copy(&mcst_pkt->mca[i], &mcst_p->mca); 14625373Sraghuram 14635373Sraghuram /* 14645373Sraghuram * Program the address into HW. If the addr 14655373Sraghuram * has already been programmed then the MAC 14665373Sraghuram * just increments a ref counter (which is 14675373Sraghuram * used when the address is being deleted) 14685373Sraghuram */ 14695373Sraghuram mutex_enter(&vswp->mac_lock); 14705373Sraghuram if (vswp->mh != NULL) { 14715373Sraghuram if (mac_multicst_add(vswp->mh, 14725373Sraghuram (uchar_t *)&mcst_pkt->mca[i])) { 14735373Sraghuram mutex_exit(&vswp->mac_lock); 14745373Sraghuram cmn_err(CE_WARN, "!vsw%d: " 14755373Sraghuram "unable to add multicast " 14765373Sraghuram "address: %s\n", 14775373Sraghuram vswp->instance, 14785373Sraghuram ether_sprintf((void *) 14795373Sraghuram &mcst_p->mca)); 14805373Sraghuram (void) vsw_del_mcst(vswp, 14815373Sraghuram VSW_VNETPORT, addr, port); 14825373Sraghuram kmem_free(mcst_p, 14835373Sraghuram sizeof (*mcst_p)); 14845373Sraghuram return (1); 14855373Sraghuram } 14865373Sraghuram mcst_p->mac_added = B_TRUE; 14875373Sraghuram } 14885373Sraghuram mutex_exit(&vswp->mac_lock); 14895373Sraghuram 14905373Sraghuram mutex_enter(&port->mca_lock); 14915373Sraghuram mcst_p->nextp = port->mcap; 14925373Sraghuram port->mcap = mcst_p; 14935373Sraghuram mutex_exit(&port->mca_lock); 14945373Sraghuram 14955373Sraghuram } else { 14965373Sraghuram DERR(vswp, "%s: error adding multicast " 14975373Sraghuram "address 0x%llx for port %ld", 14985373Sraghuram __func__, addr, port->p_instance); 14995373Sraghuram return (1); 15005373Sraghuram } 15015373Sraghuram } else { 15025373Sraghuram /* 15035373Sraghuram * Delete an entry from the multicast hash 15045373Sraghuram * table and update the address list 15055373Sraghuram * appropriately. 15065373Sraghuram */ 15075373Sraghuram if (vsw_del_mcst(vswp, VSW_VNETPORT, addr, port) == 0) { 15085373Sraghuram D3(vswp, "%s: deleting multicast address " 15095373Sraghuram "0x%llx for port %ld", __func__, addr, 15105373Sraghuram port->p_instance); 15115373Sraghuram 15125373Sraghuram mcst_p = vsw_del_addr(VSW_VNETPORT, port, addr); 15135373Sraghuram ASSERT(mcst_p != NULL); 15145373Sraghuram 15155373Sraghuram /* 15165373Sraghuram * Remove the address from HW. The address 15175373Sraghuram * will actually only be removed once the ref 15185373Sraghuram * count within the MAC layer has dropped to 15195373Sraghuram * zero. I.e. we can safely call this fn even 15205373Sraghuram * if other ports are interested in this 15215373Sraghuram * address. 15225373Sraghuram */ 15235373Sraghuram mutex_enter(&vswp->mac_lock); 15245373Sraghuram if (vswp->mh != NULL && mcst_p->mac_added) { 15255373Sraghuram if (mac_multicst_remove(vswp->mh, 15265373Sraghuram (uchar_t *)&mcst_pkt->mca[i])) { 15275373Sraghuram mutex_exit(&vswp->mac_lock); 15285373Sraghuram cmn_err(CE_WARN, "!vsw%d: " 15295373Sraghuram "unable to remove mcast " 15305373Sraghuram "address: %s\n", 15315373Sraghuram vswp->instance, 15325373Sraghuram ether_sprintf((void *) 15335373Sraghuram &mcst_p->mca)); 15345373Sraghuram kmem_free(mcst_p, 15355373Sraghuram sizeof (*mcst_p)); 15365373Sraghuram return (1); 15375373Sraghuram } 15385373Sraghuram mcst_p->mac_added = B_FALSE; 15395373Sraghuram } 15405373Sraghuram mutex_exit(&vswp->mac_lock); 15415373Sraghuram kmem_free(mcst_p, sizeof (*mcst_p)); 15425373Sraghuram 15435373Sraghuram } else { 15445373Sraghuram DERR(vswp, "%s: error deleting multicast " 15455373Sraghuram "addr 0x%llx for port %ld", 15465373Sraghuram __func__, addr, port->p_instance); 15475373Sraghuram return (1); 15485373Sraghuram } 15495373Sraghuram } 15505373Sraghuram } 15515373Sraghuram D1(vswp, "%s: exit", __func__); 15525373Sraghuram return (0); 15535373Sraghuram } 15545373Sraghuram 15555373Sraghuram /* 15565373Sraghuram * Add a new multicast entry. 15575373Sraghuram * 15585373Sraghuram * Search hash table based on address. If match found then 15595373Sraghuram * update associated val (which is chain of ports), otherwise 15605373Sraghuram * create new key/val (addr/port) pair and insert into table. 15615373Sraghuram */ 15625373Sraghuram int 15635373Sraghuram vsw_add_mcst(vsw_t *vswp, uint8_t devtype, uint64_t addr, void *arg) 15645373Sraghuram { 15655373Sraghuram int dup = 0; 15665373Sraghuram int rv = 0; 15675373Sraghuram mfdb_ent_t *ment = NULL; 15685373Sraghuram mfdb_ent_t *tmp_ent = NULL; 15695373Sraghuram mfdb_ent_t *new_ent = NULL; 15705373Sraghuram void *tgt = NULL; 15715373Sraghuram 15725373Sraghuram if (devtype == VSW_VNETPORT) { 15735373Sraghuram /* 15745373Sraghuram * Being invoked from a vnet. 15755373Sraghuram */ 15765373Sraghuram ASSERT(arg != NULL); 15775373Sraghuram tgt = arg; 15785373Sraghuram D2(NULL, "%s: port %d : address 0x%llx", __func__, 15795373Sraghuram ((vsw_port_t *)arg)->p_instance, addr); 15805373Sraghuram } else { 15815373Sraghuram /* 15825373Sraghuram * We are being invoked via the m_multicst mac entry 15835373Sraghuram * point. 15845373Sraghuram */ 15855373Sraghuram D2(NULL, "%s: address 0x%llx", __func__, addr); 15865373Sraghuram tgt = (void *)vswp; 15875373Sraghuram } 15885373Sraghuram 15895373Sraghuram WRITE_ENTER(&vswp->mfdbrw); 15905373Sraghuram if (mod_hash_find(vswp->mfdb, (mod_hash_key_t)addr, 15915373Sraghuram (mod_hash_val_t *)&ment) != 0) { 15925373Sraghuram 15935373Sraghuram /* address not currently in table */ 15945373Sraghuram ment = kmem_alloc(sizeof (mfdb_ent_t), KM_SLEEP); 15955373Sraghuram ment->d_addr = (void *)tgt; 15965373Sraghuram ment->d_type = devtype; 15975373Sraghuram ment->nextp = NULL; 15985373Sraghuram 15995373Sraghuram if (mod_hash_insert(vswp->mfdb, (mod_hash_key_t)addr, 16005373Sraghuram (mod_hash_val_t)ment) != 0) { 16015373Sraghuram DERR(vswp, "%s: hash table insertion failed", __func__); 16025373Sraghuram kmem_free(ment, sizeof (mfdb_ent_t)); 16035373Sraghuram rv = 1; 16045373Sraghuram } else { 16055373Sraghuram D2(vswp, "%s: added initial entry for 0x%llx to " 16065373Sraghuram "table", __func__, addr); 16075373Sraghuram } 16085373Sraghuram } else { 16095373Sraghuram /* 16105373Sraghuram * Address in table. Check to see if specified port 16115373Sraghuram * is already associated with the address. If not add 16125373Sraghuram * it now. 16135373Sraghuram */ 16145373Sraghuram tmp_ent = ment; 16155373Sraghuram while (tmp_ent != NULL) { 16165373Sraghuram if (tmp_ent->d_addr == (void *)tgt) { 16175373Sraghuram if (devtype == VSW_VNETPORT) { 16185373Sraghuram DERR(vswp, "%s: duplicate port entry " 16195373Sraghuram "found for portid %ld and key " 16205373Sraghuram "0x%llx", __func__, 16215373Sraghuram ((vsw_port_t *)arg)->p_instance, 16225373Sraghuram addr); 16235373Sraghuram } else { 16245373Sraghuram DERR(vswp, "%s: duplicate entry found" 16255373Sraghuram "for key 0x%llx", __func__, addr); 16265373Sraghuram } 16275373Sraghuram rv = 1; 16285373Sraghuram dup = 1; 16295373Sraghuram break; 16305373Sraghuram } 16315373Sraghuram tmp_ent = tmp_ent->nextp; 16325373Sraghuram } 16335373Sraghuram 16345373Sraghuram /* 16355373Sraghuram * Port not on list so add it to end now. 16365373Sraghuram */ 16375373Sraghuram if (0 == dup) { 16385373Sraghuram D2(vswp, "%s: added entry for 0x%llx to table", 16395373Sraghuram __func__, addr); 16405373Sraghuram new_ent = kmem_alloc(sizeof (mfdb_ent_t), KM_SLEEP); 16415373Sraghuram new_ent->d_addr = (void *)tgt; 16425373Sraghuram new_ent->d_type = devtype; 16435373Sraghuram new_ent->nextp = NULL; 16445373Sraghuram 16455373Sraghuram tmp_ent = ment; 16465373Sraghuram while (tmp_ent->nextp != NULL) 16475373Sraghuram tmp_ent = tmp_ent->nextp; 16485373Sraghuram 16495373Sraghuram tmp_ent->nextp = new_ent; 16505373Sraghuram } 16515373Sraghuram } 16525373Sraghuram 16535373Sraghuram RW_EXIT(&vswp->mfdbrw); 16545373Sraghuram return (rv); 16555373Sraghuram } 16565373Sraghuram 16575373Sraghuram /* 16585373Sraghuram * Remove a multicast entry from the hashtable. 16595373Sraghuram * 16605373Sraghuram * Search hash table based on address. If match found, scan 16615373Sraghuram * list of ports associated with address. If specified port 16625373Sraghuram * found remove it from list. 16635373Sraghuram */ 16645373Sraghuram int 16655373Sraghuram vsw_del_mcst(vsw_t *vswp, uint8_t devtype, uint64_t addr, void *arg) 16665373Sraghuram { 16675373Sraghuram mfdb_ent_t *ment = NULL; 16685373Sraghuram mfdb_ent_t *curr_p, *prev_p; 16695373Sraghuram void *tgt = NULL; 16705373Sraghuram 16715373Sraghuram D1(vswp, "%s: enter", __func__); 16725373Sraghuram 16735373Sraghuram if (devtype == VSW_VNETPORT) { 16745373Sraghuram tgt = (vsw_port_t *)arg; 16755373Sraghuram D2(vswp, "%s: removing port %d from mFDB for address" 16765373Sraghuram " 0x%llx", __func__, ((vsw_port_t *)tgt)->p_instance, addr); 16775373Sraghuram } else { 16785373Sraghuram D2(vswp, "%s: removing entry", __func__); 16795373Sraghuram tgt = (void *)vswp; 16805373Sraghuram } 16815373Sraghuram 16825373Sraghuram WRITE_ENTER(&vswp->mfdbrw); 16835373Sraghuram if (mod_hash_find(vswp->mfdb, (mod_hash_key_t)addr, 16845373Sraghuram (mod_hash_val_t *)&ment) != 0) { 16855373Sraghuram D2(vswp, "%s: address 0x%llx not in table", __func__, addr); 16865373Sraghuram RW_EXIT(&vswp->mfdbrw); 16875373Sraghuram return (1); 16885373Sraghuram } 16895373Sraghuram 16905373Sraghuram prev_p = curr_p = ment; 16915373Sraghuram 16925373Sraghuram while (curr_p != NULL) { 16935373Sraghuram if (curr_p->d_addr == (void *)tgt) { 16945373Sraghuram if (devtype == VSW_VNETPORT) { 16955373Sraghuram D2(vswp, "%s: port %d found", __func__, 16965373Sraghuram ((vsw_port_t *)tgt)->p_instance); 16975373Sraghuram } else { 16985373Sraghuram D2(vswp, "%s: instance found", __func__); 16995373Sraghuram } 17005373Sraghuram 17015373Sraghuram if (prev_p == curr_p) { 17025373Sraghuram /* 17035373Sraghuram * head of list, if no other element is in 17045373Sraghuram * list then destroy this entry, otherwise 17055373Sraghuram * just replace it with updated value. 17065373Sraghuram */ 17075373Sraghuram ment = curr_p->nextp; 17085373Sraghuram if (ment == NULL) { 17095373Sraghuram (void) mod_hash_destroy(vswp->mfdb, 17105373Sraghuram (mod_hash_val_t)addr); 17115373Sraghuram } else { 17125373Sraghuram (void) mod_hash_replace(vswp->mfdb, 17135373Sraghuram (mod_hash_key_t)addr, 17145373Sraghuram (mod_hash_val_t)ment); 17155373Sraghuram } 17165373Sraghuram } else { 17175373Sraghuram /* 17185373Sraghuram * Not head of list, no need to do 17195373Sraghuram * replacement, just adjust list pointers. 17205373Sraghuram */ 17215373Sraghuram prev_p->nextp = curr_p->nextp; 17225373Sraghuram } 17235373Sraghuram break; 17245373Sraghuram } 17255373Sraghuram 17265373Sraghuram prev_p = curr_p; 17275373Sraghuram curr_p = curr_p->nextp; 17285373Sraghuram } 17295373Sraghuram 17305373Sraghuram RW_EXIT(&vswp->mfdbrw); 17315373Sraghuram 17325373Sraghuram D1(vswp, "%s: exit", __func__); 17335373Sraghuram 17345373Sraghuram if (curr_p == NULL) 17355373Sraghuram return (1); 17365373Sraghuram kmem_free(curr_p, sizeof (mfdb_ent_t)); 17375373Sraghuram return (0); 17385373Sraghuram } 17395373Sraghuram 17405373Sraghuram /* 17415373Sraghuram * Port is being deleted, but has registered an interest in one 17425373Sraghuram * or more multicast groups. Using the list of addresses maintained 17435373Sraghuram * within the port structure find the appropriate entry in the hash 17445373Sraghuram * table and remove this port from the list of interested ports. 17455373Sraghuram */ 17465373Sraghuram void 17475373Sraghuram vsw_del_mcst_port(vsw_port_t *port) 17485373Sraghuram { 17495373Sraghuram mcst_addr_t *mcap = NULL; 17505373Sraghuram vsw_t *vswp = port->p_vswp; 17515373Sraghuram 17525373Sraghuram D1(vswp, "%s: enter", __func__); 17535373Sraghuram 17545373Sraghuram mutex_enter(&port->mca_lock); 17555373Sraghuram 17565373Sraghuram while ((mcap = port->mcap) != NULL) { 17575373Sraghuram 17585373Sraghuram port->mcap = mcap->nextp; 17595373Sraghuram 17605373Sraghuram mutex_exit(&port->mca_lock); 17615373Sraghuram 17625373Sraghuram (void) vsw_del_mcst(vswp, VSW_VNETPORT, 17635373Sraghuram mcap->addr, port); 17645373Sraghuram 17655373Sraghuram /* 17665373Sraghuram * Remove the address from HW. The address 17675373Sraghuram * will actually only be removed once the ref 17685373Sraghuram * count within the MAC layer has dropped to 17695373Sraghuram * zero. I.e. we can safely call this fn even 17705373Sraghuram * if other ports are interested in this 17715373Sraghuram * address. 17725373Sraghuram */ 17735373Sraghuram mutex_enter(&vswp->mac_lock); 17745373Sraghuram if (vswp->mh != NULL && mcap->mac_added) { 17755373Sraghuram (void) mac_multicst_remove(vswp->mh, 17765373Sraghuram (uchar_t *)&mcap->mca); 17775373Sraghuram } 17785373Sraghuram mutex_exit(&vswp->mac_lock); 17795373Sraghuram 17805373Sraghuram kmem_free(mcap, sizeof (*mcap)); 17815373Sraghuram 17825373Sraghuram mutex_enter(&port->mca_lock); 17835373Sraghuram 17845373Sraghuram } 17855373Sraghuram 17865373Sraghuram mutex_exit(&port->mca_lock); 17875373Sraghuram 17885373Sraghuram D1(vswp, "%s: exit", __func__); 17895373Sraghuram } 17905373Sraghuram 17915373Sraghuram /* 17925373Sraghuram * This vsw instance is detaching, but has registered an interest in one 17935373Sraghuram * or more multicast groups. Using the list of addresses maintained 17945373Sraghuram * within the vsw structure find the appropriate entry in the hash 17955373Sraghuram * table and remove this instance from the list of interested ports. 17965373Sraghuram */ 17975373Sraghuram void 17985373Sraghuram vsw_del_mcst_vsw(vsw_t *vswp) 17995373Sraghuram { 18005373Sraghuram mcst_addr_t *next_p = NULL; 18015373Sraghuram 18025373Sraghuram D1(vswp, "%s: enter", __func__); 18035373Sraghuram 18045373Sraghuram mutex_enter(&vswp->mca_lock); 18055373Sraghuram 18065373Sraghuram while (vswp->mcap != NULL) { 18075373Sraghuram DERR(vswp, "%s: deleting addr 0x%llx", 18085373Sraghuram __func__, vswp->mcap->addr); 18095373Sraghuram (void) vsw_del_mcst(vswp, VSW_LOCALDEV, vswp->mcap->addr, NULL); 18105373Sraghuram 18115373Sraghuram next_p = vswp->mcap->nextp; 18125373Sraghuram kmem_free(vswp->mcap, sizeof (mcst_addr_t)); 18135373Sraghuram vswp->mcap = next_p; 18145373Sraghuram } 18155373Sraghuram 18165373Sraghuram vswp->mcap = NULL; 18175373Sraghuram mutex_exit(&vswp->mca_lock); 18185373Sraghuram 18195373Sraghuram D1(vswp, "%s: exit", __func__); 18205373Sraghuram } 18215373Sraghuram 18225935Ssb155480 static uint32_t 18235373Sraghuram vsw_get_same_dest_list(struct ether_header *ehp, 18245373Sraghuram mblk_t **rhead, mblk_t **rtail, mblk_t **mpp) 18255373Sraghuram { 18265935Ssb155480 uint32_t count = 0; 18275935Ssb155480 mblk_t *bp; 18285935Ssb155480 mblk_t *nbp; 18295935Ssb155480 mblk_t *head = NULL; 18305935Ssb155480 mblk_t *tail = NULL; 18315935Ssb155480 mblk_t *prev = NULL; 18325935Ssb155480 struct ether_header *behp; 18335373Sraghuram 18345373Sraghuram /* process the chain of packets */ 18355373Sraghuram bp = *mpp; 18365373Sraghuram while (bp) { 18375373Sraghuram nbp = bp->b_next; 18385373Sraghuram behp = (struct ether_header *)bp->b_rptr; 18395373Sraghuram bp->b_prev = NULL; 18405373Sraghuram if (ether_cmp(&ehp->ether_dhost, &behp->ether_dhost) == 0) { 18415373Sraghuram if (prev == NULL) { 18425373Sraghuram *mpp = nbp; 18435373Sraghuram } else { 18445373Sraghuram prev->b_next = nbp; 18455373Sraghuram } 18465373Sraghuram bp->b_next = NULL; 18475373Sraghuram if (head == NULL) { 18485373Sraghuram head = tail = bp; 18495373Sraghuram } else { 18505373Sraghuram tail->b_next = bp; 18515373Sraghuram tail = bp; 18525373Sraghuram } 18535373Sraghuram count++; 18545373Sraghuram } else { 18555373Sraghuram prev = bp; 18565373Sraghuram } 18575373Sraghuram bp = nbp; 18585373Sraghuram } 18595373Sraghuram *rhead = head; 18605373Sraghuram *rtail = tail; 18615373Sraghuram DTRACE_PROBE1(vsw_same_dest, int, count); 18625373Sraghuram return (count); 18635373Sraghuram } 18645373Sraghuram 18655373Sraghuram static mblk_t * 18665373Sraghuram vsw_dupmsgchain(mblk_t *mp) 18675373Sraghuram { 18685373Sraghuram mblk_t *nmp = NULL; 18695373Sraghuram mblk_t **nmpp = &nmp; 18705373Sraghuram 18715373Sraghuram for (; mp != NULL; mp = mp->b_next) { 18725373Sraghuram if ((*nmpp = dupmsg(mp)) == NULL) { 18735373Sraghuram freemsgchain(nmp); 18745373Sraghuram return (NULL); 18755373Sraghuram } 18765373Sraghuram 18775373Sraghuram nmpp = &((*nmpp)->b_next); 18785373Sraghuram } 18795373Sraghuram 18805373Sraghuram return (nmp); 18815373Sraghuram } 1882