#include <sys/types.h>
#include <sys/sysmacros.h>
#include <sys/conf.h>
#include <sys/cmn_err.h>
#include <sys/list.h>
#include <sys/kmem.h>
#include <sys/stream.h>
#include <sys/modctl.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/atomic.h>
#include <sys/stat.h>
#include <sys/modhash.h>
#include <sys/strsubr.h>
#include <sys/strsun.h>
#include <sys/sdt.h>
#include <sys/mac.h>
#include <sys/mac_impl.h>
#include <sys/mac_client_impl.h>
#include <sys/mac_client_priv.h>
#include <sys/mac_flow_impl.h>
static kmem_cache_t *mac_bcast_grp_cache;
static uint32_t mac_bcast_id = 0;
void
mac_bcast_init(void)
{
mac_bcast_grp_cache = kmem_cache_create("mac_bcast_grp_cache",
sizeof (mac_bcast_grp_t), 0, NULL, NULL, NULL, NULL, NULL, 0);
}
void
mac_bcast_fini(void)
{
kmem_cache_destroy(mac_bcast_grp_cache);
}
mac_impl_t *
mac_bcast_grp_mip(void *grp)
{
mac_bcast_grp_t *bcast_grp = grp;
return (bcast_grp->mbg_mac_impl);
}
void
mac_bcast_grp_free(void *bcast_grp)
{
mac_bcast_grp_t *grp = bcast_grp;
mac_impl_t *mip = grp->mbg_mac_impl;
ASSERT(MAC_PERIM_HELD((mac_handle_t)mip));
ASSERT(grp->mbg_addr != NULL);
kmem_free(grp->mbg_addr, mip->mi_type->mt_addr_length);
kmem_free(grp->mbg_clients,
grp->mbg_nclients_alloc * sizeof (mac_bcast_grp_mcip_t));
mip->mi_bcast_ngrps--;
kmem_cache_free(mac_bcast_grp_cache, grp);
}
void
mac_bcast_send(void *arg1, void *arg2, mblk_t *mp_chain, boolean_t is_loopback)
{
mac_bcast_grp_t *grp = arg1;
mac_client_impl_t *src_mcip = arg2, *dst_mcip;
mac_impl_t *mip = grp->mbg_mac_impl;
uint64_t gen;
uint_t i;
mblk_t *mp_chain1;
flow_entry_t *flent;
int err;
rw_enter(&mip->mi_rw_lock, RW_READER);
for (i = 0; i < grp->mbg_nclients_alloc; i++) {
dst_mcip = grp->mbg_clients[i].mgb_client;
if (dst_mcip == NULL)
continue;
flent = dst_mcip->mci_flent;
if (flent == NULL || dst_mcip == src_mcip) {
continue;
}
if ((mp_chain1 = mac_copymsgchain_cksum(mp_chain)) == NULL)
break;
FLOW_TRY_REFHOLD(flent, err);
if (err != 0) {
freemsgchain(mp_chain1);
continue;
}
gen = grp->mbg_clients_gen;
rw_exit(&mip->mi_rw_lock);
DTRACE_PROBE4(mac__bcast__send__to, mac_client_impl_t *,
src_mcip, flow_fn_t, dst_mcip->mci_flent->fe_cb_fn,
void *, dst_mcip->mci_flent->fe_cb_arg1,
void *, dst_mcip->mci_flent->fe_cb_arg2);
(dst_mcip->mci_flent->fe_cb_fn)(dst_mcip->mci_flent->fe_cb_arg1,
dst_mcip->mci_flent->fe_cb_arg2, mp_chain1, is_loopback);
FLOW_REFRELE(flent);
rw_enter(&mip->mi_rw_lock, RW_READER);
if (grp->mbg_addrtype == MAC_ADDRTYPE_MULTICAST) {
MCIP_STAT_UPDATE(dst_mcip, multircv, 1);
MCIP_STAT_UPDATE(dst_mcip, multircvbytes,
msgdsize(mp_chain));
} else {
MCIP_STAT_UPDATE(dst_mcip, brdcstrcv, 1);
MCIP_STAT_UPDATE(dst_mcip, brdcstrcvbytes,
msgdsize(mp_chain));
}
if (grp->mbg_clients_gen != gen) {
rw_exit(&mip->mi_rw_lock);
freemsgchain(mp_chain);
return;
}
}
rw_exit(&mip->mi_rw_lock);
if (src_mcip != NULL) {
MCIP_STAT_UPDATE(src_mcip, multixmt, 1);
MCIP_STAT_UPDATE(src_mcip, multixmtbytes, msgdsize(mp_chain));
MCIP_STAT_UPDATE(src_mcip, brdcstxmt, 1);
MCIP_STAT_UPDATE(src_mcip, brdcstxmtbytes, msgdsize(mp_chain));
mp_chain = mac_provider_tx(mip, mip->mi_default_tx_ring,
mp_chain, src_mcip);
if (mp_chain != NULL)
freemsgchain(mp_chain);
} else {
freemsgchain(mp_chain);
}
}
int
mac_bcast_add(mac_client_impl_t *mcip, const uint8_t *addr, uint16_t vid,
mac_addrtype_t addrtype)
{
mac_impl_t *mip = mcip->mci_mip;
mac_bcast_grp_t *grp = NULL, **last_grp;
size_t addr_len = mip->mi_type->mt_addr_length;
int rc = 0;
int i, index = -1;
mac_mcast_addrs_t **prev_mi_addr = NULL;
mac_mcast_addrs_t **prev_mci_addr = NULL;
ASSERT(MAC_PERIM_HELD((mac_handle_t)mip));
ASSERT(addrtype == MAC_ADDRTYPE_MULTICAST ||
addrtype == MAC_ADDRTYPE_BROADCAST);
if (addrtype == MAC_ADDRTYPE_MULTICAST) {
mac_mcast_addrs_t *maddr;
prev_mi_addr = &mip->mi_mcast_addrs;
for (maddr = *prev_mi_addr; maddr != NULL;
prev_mi_addr = &maddr->mma_next, maddr = maddr->mma_next) {
if (bcmp(maddr->mma_addr, addr, addr_len) == 0)
break;
}
if (maddr == NULL) {
rc = mip->mi_multicst(mip->mi_driver, B_TRUE, addr);
if (rc != 0)
return (rc);
maddr = kmem_zalloc(sizeof (mac_mcast_addrs_t),
KM_SLEEP);
bcopy(addr, maddr->mma_addr, addr_len);
*prev_mi_addr = maddr;
} else {
prev_mi_addr = NULL;
}
maddr->mma_ref++;
prev_mci_addr = &mcip->mci_mcast_addrs;
for (maddr = *prev_mci_addr; maddr != NULL;
prev_mci_addr = &maddr->mma_next, maddr = maddr->mma_next) {
if (bcmp(maddr->mma_addr, addr, addr_len) == 0)
break;
}
if (maddr == NULL) {
maddr = kmem_zalloc(sizeof (mac_mcast_addrs_t),
KM_SLEEP);
bcopy(addr, maddr->mma_addr, addr_len);
*prev_mci_addr = maddr;
} else {
prev_mci_addr = NULL;
}
maddr->mma_ref++;
}
last_grp = &mip->mi_bcast_grp;
for (grp = *last_grp; grp != NULL;
last_grp = &grp->mbg_next, grp = grp->mbg_next) {
if (bcmp(grp->mbg_addr, addr, addr_len) == 0 &&
grp->mbg_vid == vid)
break;
}
if (grp == NULL) {
flow_desc_t flow_desc;
char flow_name[MAXFLOWNAMELEN];
grp = kmem_cache_alloc(mac_bcast_grp_cache, KM_SLEEP);
bzero(grp, sizeof (mac_bcast_grp_t));
grp->mbg_next = NULL;
grp->mbg_mac_impl = mip;
DTRACE_PROBE1(mac__bcast__add__new__group, mac_bcast_grp_t *,
grp);
grp->mbg_addr = kmem_zalloc(addr_len, KM_SLEEP);
bcopy(addr, grp->mbg_addr, addr_len);
grp->mbg_addrtype = addrtype;
grp->mbg_vid = vid;
bzero(&flow_desc, sizeof (flow_desc));
bcopy(addr, &flow_desc.fd_dst_mac, addr_len);
flow_desc.fd_mac_len = (uint32_t)addr_len;
flow_desc.fd_mask = FLOW_LINK_DST;
if (vid != 0) {
flow_desc.fd_vid = vid;
flow_desc.fd_mask |= FLOW_LINK_VID;
}
grp->mbg_id = atomic_inc_32_nv(&mac_bcast_id);
(void) sprintf(flow_name,
"mac/%s/mcast%d", mip->mi_name, grp->mbg_id);
rc = mac_flow_create(&flow_desc, NULL, flow_name,
grp, FLOW_MCAST, &grp->mbg_flow_ent);
if (rc != 0) {
kmem_free(grp->mbg_addr, addr_len);
kmem_cache_free(mac_bcast_grp_cache, grp);
goto fail;
}
grp->mbg_flow_ent->fe_mbg = grp;
mip->mi_bcast_ngrps++;
FLOW_REFHOLD(grp->mbg_flow_ent);
grp->mbg_flow_ent->fe_cb_fn = mac_bcast_send;
grp->mbg_flow_ent->fe_cb_arg1 = grp;
grp->mbg_flow_ent->fe_cb_arg2 = NULL;
rc = mac_flow_add(mip->mi_flow_tab, grp->mbg_flow_ent);
if (rc != 0) {
FLOW_FINAL_REFRELE(grp->mbg_flow_ent);
goto fail;
}
*last_grp = grp;
}
ASSERT(grp->mbg_addrtype == addrtype);
rw_enter(&mip->mi_rw_lock, RW_WRITER);
for (i = 0; i < grp->mbg_nclients_alloc; i++) {
if (grp->mbg_clients[i].mgb_client == mcip) {
grp->mbg_clients[i].mgb_client_ref++;
rw_exit(&mip->mi_rw_lock);
return (0);
} else if (grp->mbg_clients[i].mgb_client == NULL &&
index == -1) {
index = i;
}
}
if (grp->mbg_nclients_alloc == grp->mbg_nclients) {
mac_bcast_grp_mcip_t *new_clients;
uint_t new_size = grp->mbg_nclients+1;
new_clients = kmem_zalloc(new_size *
sizeof (mac_bcast_grp_mcip_t), KM_SLEEP);
if (grp->mbg_nclients > 0) {
ASSERT(grp->mbg_clients != NULL);
bcopy(grp->mbg_clients, new_clients, grp->mbg_nclients *
sizeof (mac_bcast_grp_mcip_t));
kmem_free(grp->mbg_clients, grp->mbg_nclients *
sizeof (mac_bcast_grp_mcip_t));
}
grp->mbg_clients = new_clients;
grp->mbg_nclients_alloc = new_size;
index = new_size - 1;
}
ASSERT(index != -1);
grp->mbg_clients[index].mgb_client = mcip;
grp->mbg_clients[index].mgb_client_ref = 1;
grp->mbg_nclients++;
grp->mbg_clients_gen++;
rw_exit(&mip->mi_rw_lock);
return (0);
fail:
if (prev_mi_addr != NULL) {
kmem_free(*prev_mi_addr, sizeof (mac_mcast_addrs_t));
*prev_mi_addr = NULL;
(void) mip->mi_multicst(mip->mi_driver, B_FALSE, addr);
}
if (prev_mci_addr != NULL) {
kmem_free(*prev_mci_addr, sizeof (mac_mcast_addrs_t));
*prev_mci_addr = NULL;
}
return (rc);
}
void
mac_bcast_delete(mac_client_impl_t *mcip, const uint8_t *addr, uint16_t vid)
{
mac_impl_t *mip = mcip->mci_mip;
mac_bcast_grp_t *grp = NULL, **prev;
size_t addr_len = mip->mi_type->mt_addr_length;
flow_entry_t *flent;
uint_t i;
mac_mcast_addrs_t *maddr = NULL;
mac_mcast_addrs_t **mprev;
ASSERT(MAC_PERIM_HELD((mac_handle_t)mip));
prev = &mip->mi_bcast_grp;
for (grp = mip->mi_bcast_grp; grp != NULL; prev = &grp->mbg_next,
grp = grp->mbg_next) {
if (bcmp(grp->mbg_addr, addr, addr_len) == 0 &&
grp->mbg_vid == vid)
break;
}
ASSERT(grp != NULL);
rw_enter(&mip->mi_rw_lock, RW_WRITER);
for (i = 0; i < grp->mbg_nclients_alloc; i++) {
if (grp->mbg_clients[i].mgb_client == mcip)
break;
}
ASSERT(i < grp->mbg_nclients_alloc);
if (--grp->mbg_clients[i].mgb_client_ref > 0)
goto update_maddr;
grp->mbg_clients[i].mgb_client = NULL;
grp->mbg_clients[i].mgb_client_ref = 0;
grp->mbg_clients_gen++;
if (--grp->mbg_nclients == 0) {
*prev = grp->mbg_next;
}
update_maddr:
rw_exit(&mip->mi_rw_lock);
if (grp->mbg_addrtype == MAC_ADDRTYPE_MULTICAST) {
mprev = &mcip->mci_mcast_addrs;
for (maddr = mcip->mci_mcast_addrs; maddr != NULL;
mprev = &maddr->mma_next, maddr = maddr->mma_next) {
if (bcmp(grp->mbg_addr, maddr->mma_addr,
mip->mi_type->mt_addr_length) == 0)
break;
}
ASSERT(maddr != NULL);
if (--maddr->mma_ref == 0) {
*mprev = maddr->mma_next;
maddr->mma_next = NULL;
kmem_free(maddr, sizeof (mac_mcast_addrs_t));
}
mprev = &mip->mi_mcast_addrs;
for (maddr = mip->mi_mcast_addrs; maddr != NULL;
mprev = &maddr->mma_next, maddr = maddr->mma_next) {
if (bcmp(grp->mbg_addr, maddr->mma_addr,
mip->mi_type->mt_addr_length) == 0)
break;
}
ASSERT(maddr != NULL);
if (--maddr->mma_ref == 0) {
(void) mip->mi_multicst(mip->mi_driver, B_FALSE, addr);
*mprev = maddr->mma_next;
maddr->mma_next = NULL;
kmem_free(maddr, sizeof (mac_mcast_addrs_t));
}
}
flent = grp->mbg_flow_ent;
if (grp->mbg_nclients == 0) {
mac_flow_remove(mip->mi_flow_tab, flent, B_FALSE);
mac_flow_wait(flent, FLOW_DRIVER_UPCALL);
FLOW_FINAL_REFRELE(flent);
}
}
void
mac_bcast_refresh(mac_impl_t *mip, mac_multicst_t refresh_fn, void *arg,
boolean_t add)
{
mac_mcast_addrs_t *grp, *next;
ASSERT(refresh_fn != NULL);
ASSERT(MAC_PERIM_HELD((mac_handle_t)mip));
for (grp = mip->mi_mcast_addrs; grp != NULL; grp = next) {
next = grp->mma_next;
refresh_fn(arg, add, grp->mma_addr);
}
}
void
mac_client_bcast_refresh(mac_client_impl_t *mcip, mac_multicst_t refresh_fn,
void *arg, boolean_t add)
{
mac_mcast_addrs_t *grp, *next;
mac_impl_t *mip = mcip->mci_mip;
ASSERT(refresh_fn != NULL);
ASSERT(MAC_PERIM_HELD((mac_handle_t)mip));
for (grp = mcip->mci_mcast_addrs; grp != NULL; grp = next) {
next = grp->mma_next;
refresh_fn(arg, add, grp->mma_addr);
}
}