1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 #include <sys/types.h>
  27 #include <sys/sysmacros.h>
  28 #include <sys/conf.h>
  29 #include <sys/cmn_err.h>
  30 #include <sys/list.h>
  31 #include <sys/kmem.h>
  32 #include <sys/stream.h>
  33 #include <sys/modctl.h>
  34 #include <sys/ddi.h>
  35 #include <sys/sunddi.h>
  36 #include <sys/atomic.h>
  37 #include <sys/stat.h>
  38 #include <sys/modhash.h>
  39 #include <sys/strsubr.h>
  40 #include <sys/strsun.h>
  41 #include <sys/sdt.h>
  42 #include <sys/mac.h>
  43 #include <sys/mac_impl.h>
  44 #include <sys/mac_client_impl.h>
  45 #include <sys/mac_client_priv.h>
  46 #include <sys/mac_flow_impl.h>
  47 
  48 /*
  49  * Broadcast and multicast traffic must be distributed to the MAC clients
  50  * that are defined on top of the same MAC. The set of
  51  * destinations to which a multicast packet must be sent is a subset
  52  * of all MAC clients defined on top of the MAC. A MAC client can be member
  53  * of more than one such subset.
  54  *
  55  * To accomodate these requirements, we introduce broadcast groups.
  56  * A broadcast group is associated with a broadcast or multicast
  57  * address. The members of a broadcast group consist of the MAC clients
  58  * that should received copies of packets sent to the address
  59  * associated with the group, and are defined on top of the
  60  * same MAC.
  61  *
  62  * The broadcast groups defined on top of a MAC are chained,
  63  * hanging off the mac_impl_t. The broadcast group id's are
  64  * unique globally (tracked by mac_bcast_id).
  65  */
  66 
  67 /*
  68  * The same MAC client may be added for different <addr,vid> tuple,
  69  * we maintain a ref count for the number of times it has been added
  70  * to account for deleting the MAC client from the group.
  71  */
  72 typedef struct mac_bcast_grp_mcip_s {
  73         mac_client_impl_t       *mgb_client;
  74         int                     mgb_client_ref;
  75 } mac_bcast_grp_mcip_t;
  76 
  77 typedef struct mac_bcast_grp_s {                        /* Protected by */
  78         struct mac_bcast_grp_s  *mbg_next;              /* SL */
  79         void                    *mbg_addr;              /* SL */
  80         uint16_t                mbg_vid;                /* SL */
  81         mac_impl_t              *mbg_mac_impl;          /* WO */
  82         mac_addrtype_t          mbg_addrtype;           /* WO */
  83         flow_entry_t            *mbg_flow_ent;          /* WO */
  84         mac_bcast_grp_mcip_t    *mbg_clients;           /* mi_rw_lock */
  85         uint_t                  mbg_nclients;           /* mi_rw_lock */
  86         uint_t                  mbg_nclients_alloc;     /* SL */
  87         uint64_t                mbg_clients_gen;        /* mi_rw_lock */
  88         uint32_t                mbg_id;                 /* atomic */
  89 } mac_bcast_grp_t;
  90 
  91 static kmem_cache_t *mac_bcast_grp_cache;
  92 static uint32_t mac_bcast_id = 0;
  93 
  94 void
  95 mac_bcast_init(void)
  96 {
  97         mac_bcast_grp_cache = kmem_cache_create("mac_bcast_grp_cache",
  98             sizeof (mac_bcast_grp_t), 0, NULL, NULL, NULL, NULL, NULL, 0);
  99 }
 100 
 101 void
 102 mac_bcast_fini(void)
 103 {
 104         kmem_cache_destroy(mac_bcast_grp_cache);
 105 }
 106 
 107 mac_impl_t *
 108 mac_bcast_grp_mip(void *grp)
 109 {
 110         mac_bcast_grp_t *bcast_grp = grp;
 111 
 112         return (bcast_grp->mbg_mac_impl);
 113 }
 114 
 115 /*
 116  * Free the specific broadcast group. Invoked when the last reference
 117  * to the group is released.
 118  */
 119 void
 120 mac_bcast_grp_free(void *bcast_grp)
 121 {
 122         mac_bcast_grp_t *grp = bcast_grp;
 123         mac_impl_t *mip = grp->mbg_mac_impl;
 124 
 125         ASSERT(MAC_PERIM_HELD((mac_handle_t)mip));
 126 
 127         ASSERT(grp->mbg_addr != NULL);
 128         kmem_free(grp->mbg_addr, mip->mi_type->mt_addr_length);
 129         kmem_free(grp->mbg_clients,
 130             grp->mbg_nclients_alloc * sizeof (mac_bcast_grp_mcip_t));
 131         mip->mi_bcast_ngrps--;
 132         kmem_cache_free(mac_bcast_grp_cache, grp);
 133 }
 134 
 135 /*
 136  * arg1: broadcast group
 137  * arg2: sender MAC client if it is being sent by a MAC client,
 138  * NULL if it was received from the wire.
 139  */
 140 void
 141 mac_bcast_send(void *arg1, void *arg2, mblk_t *mp_chain, boolean_t is_loopback)
 142 {
 143         mac_bcast_grp_t *grp = arg1;
 144         mac_client_impl_t *src_mcip = arg2, *dst_mcip;
 145         mac_impl_t *mip = grp->mbg_mac_impl;
 146         uint64_t gen;
 147         uint_t i;
 148         mblk_t *mp_chain1;
 149         flow_entry_t    *flent;
 150         int err;
 151 
 152         rw_enter(&mip->mi_rw_lock, RW_READER);
 153 
 154         /*
 155          * Pass a copy of the mp chain to every MAC client except the sender
 156          * MAC client, if the packet was not received from the underlying NIC.
 157          *
 158          * The broadcast group lock should not be held across calls to
 159          * the flow's callback function, since the same group could
 160          * potentially be accessed from the same context. When the lock
 161          * is reacquired, changes to the broadcast group while the lock
 162          * was released are caught using a generation counter incremented
 163          * each time the list of MAC clients associated with the broadcast
 164          * group is changed.
 165          */
 166         for (i = 0; i < grp->mbg_nclients_alloc; i++) {
 167                 dst_mcip = grp->mbg_clients[i].mgb_client;
 168                 if (dst_mcip == NULL)
 169                         continue;
 170                 flent = dst_mcip->mci_flent;
 171                 if (flent == NULL || dst_mcip == src_mcip) {
 172                         /*
 173                          * Don't send a copy of the packet back to
 174                          * its sender.
 175                          */
 176                         continue;
 177                 }
 178 
 179                 /*
 180                  * It is important to hold a reference on the
 181                  * flow_ent here.
 182                  */
 183                 if ((mp_chain1 = mac_copymsgchain_cksum(mp_chain)) == NULL)
 184                         break;
 185                 /*
 186                  * Fix the checksum for packets originating
 187                  * from the local machine.
 188                  */
 189                 if ((src_mcip != NULL) &&
 190                     (mp_chain1 = mac_fix_cksum(mp_chain1)) == NULL)
 191                         break;
 192 
 193                 FLOW_TRY_REFHOLD(flent, err);
 194                 if (err != 0) {
 195                         freemsgchain(mp_chain1);
 196                         continue;
 197                 }
 198 
 199                 gen = grp->mbg_clients_gen;
 200 
 201                 rw_exit(&mip->mi_rw_lock);
 202 
 203                 DTRACE_PROBE4(mac__bcast__send__to, mac_client_impl_t *,
 204                     src_mcip, flow_fn_t, dst_mcip->mci_flent->fe_cb_fn,
 205                     void *, dst_mcip->mci_flent->fe_cb_arg1,
 206                     void *, dst_mcip->mci_flent->fe_cb_arg2);
 207 
 208                 (dst_mcip->mci_flent->fe_cb_fn)(dst_mcip->mci_flent->fe_cb_arg1,
 209                     dst_mcip->mci_flent->fe_cb_arg2, mp_chain1, is_loopback);
 210                 FLOW_REFRELE(flent);
 211 
 212                 rw_enter(&mip->mi_rw_lock, RW_READER);
 213 
 214                 /* update stats */
 215                 if (grp->mbg_addrtype == MAC_ADDRTYPE_MULTICAST) {
 216                         MCIP_STAT_UPDATE(dst_mcip, multircv, 1);
 217                         MCIP_STAT_UPDATE(dst_mcip, multircvbytes,
 218                             msgdsize(mp_chain));
 219                 } else {
 220                         MCIP_STAT_UPDATE(dst_mcip, brdcstrcv, 1);
 221                         MCIP_STAT_UPDATE(dst_mcip, brdcstrcvbytes,
 222                             msgdsize(mp_chain));
 223                 }
 224 
 225                 if (grp->mbg_clients_gen != gen) {
 226                         /*
 227                          * The list of MAC clients associated with the group
 228                          * was changed while the lock was released.
 229                          * Give up on the current packet.
 230                          */
 231                         rw_exit(&mip->mi_rw_lock);
 232                         freemsgchain(mp_chain);
 233                         return;
 234                 }
 235         }
 236         rw_exit(&mip->mi_rw_lock);
 237 
 238         if (src_mcip != NULL) {
 239                 /*
 240                  * The packet was sent from one of the MAC clients,
 241                  * so we need to send a copy of the packet to the
 242                  * underlying NIC so that it can be sent on the wire.
 243                  */
 244                 MCIP_STAT_UPDATE(src_mcip, multixmt, 1);
 245                 MCIP_STAT_UPDATE(src_mcip, multixmtbytes, msgdsize(mp_chain));
 246                 MCIP_STAT_UPDATE(src_mcip, brdcstxmt, 1);
 247                 MCIP_STAT_UPDATE(src_mcip, brdcstxmtbytes, msgdsize(mp_chain));
 248 
 249                 MAC_TX(mip, mip->mi_default_tx_ring, mp_chain, src_mcip);
 250                 if (mp_chain != NULL)
 251                         freemsgchain(mp_chain);
 252         } else {
 253                 freemsgchain(mp_chain);
 254         }
 255 }
 256 
 257 /*
 258  * Add the specified MAC client to the group corresponding to the specified
 259  * broadcast or multicast address.
 260  * Return 0 on success, or an errno value on failure.
 261  */
 262 int
 263 mac_bcast_add(mac_client_impl_t *mcip, const uint8_t *addr, uint16_t vid,
 264     mac_addrtype_t addrtype)
 265 {
 266         mac_impl_t              *mip = mcip->mci_mip;
 267         mac_bcast_grp_t         *grp = NULL, **last_grp;
 268         size_t                  addr_len = mip->mi_type->mt_addr_length;
 269         int                     rc = 0;
 270         int                     i, index = -1;
 271         mac_mcast_addrs_t       **prev_mi_addr = NULL;
 272         mac_mcast_addrs_t       **prev_mci_addr = NULL;
 273 
 274         ASSERT(MAC_PERIM_HELD((mac_handle_t)mip));
 275 
 276         ASSERT(addrtype == MAC_ADDRTYPE_MULTICAST ||
 277             addrtype == MAC_ADDRTYPE_BROADCAST);
 278 
 279         /*
 280          * Add the MAC client to the list of MAC clients associated
 281          * with the group.
 282          */
 283         if (addrtype == MAC_ADDRTYPE_MULTICAST) {
 284                 mac_mcast_addrs_t       *maddr;
 285 
 286                 /*
 287                  * In case of a driver (say aggr), we need this information
 288                  * on a per MAC instance basis.
 289                  */
 290                 prev_mi_addr = &mip->mi_mcast_addrs;
 291                 for (maddr = *prev_mi_addr; maddr != NULL;
 292                     prev_mi_addr = &maddr->mma_next, maddr = maddr->mma_next) {
 293                         if (bcmp(maddr->mma_addr, addr, addr_len) == 0)
 294                                 break;
 295                 }
 296                 if (maddr == NULL) {
 297                         /*
 298                          * For multicast addresses, have the underlying MAC
 299                          * join the corresponding multicast group.
 300                          */
 301                         rc = mip->mi_multicst(mip->mi_driver, B_TRUE, addr);
 302                         if (rc != 0)
 303                                 return (rc);
 304                         maddr = kmem_zalloc(sizeof (mac_mcast_addrs_t),
 305                             KM_SLEEP);
 306                         bcopy(addr, maddr->mma_addr, addr_len);
 307                         *prev_mi_addr = maddr;
 308                 } else {
 309                         prev_mi_addr = NULL;
 310                 }
 311                 maddr->mma_ref++;
 312 
 313                 /*
 314                  * We maintain a separate list for each MAC client. Get
 315                  * the entry or add, if it is not present.
 316                  */
 317                 prev_mci_addr = &mcip->mci_mcast_addrs;
 318                 for (maddr = *prev_mci_addr; maddr != NULL;
 319                     prev_mci_addr = &maddr->mma_next, maddr = maddr->mma_next) {
 320                         if (bcmp(maddr->mma_addr, addr, addr_len) == 0)
 321                                 break;
 322                 }
 323                 if (maddr == NULL) {
 324                         maddr = kmem_zalloc(sizeof (mac_mcast_addrs_t),
 325                             KM_SLEEP);
 326                         bcopy(addr, maddr->mma_addr, addr_len);
 327                         *prev_mci_addr = maddr;
 328                 } else {
 329                         prev_mci_addr = NULL;
 330                 }
 331                 maddr->mma_ref++;
 332         }
 333 
 334         /* The list is protected by the perimeter */
 335         last_grp = &mip->mi_bcast_grp;
 336         for (grp = *last_grp; grp != NULL;
 337             last_grp = &grp->mbg_next, grp = grp->mbg_next) {
 338                 if (bcmp(grp->mbg_addr, addr, addr_len) == 0 &&
 339                     grp->mbg_vid == vid)
 340                         break;
 341         }
 342 
 343         if (grp == NULL) {
 344                 /*
 345                  * The group does not yet exist, create it.
 346                  */
 347                 flow_desc_t flow_desc;
 348                 char flow_name[MAXFLOWNAMELEN];
 349 
 350                 grp = kmem_cache_alloc(mac_bcast_grp_cache, KM_SLEEP);
 351                 bzero(grp, sizeof (mac_bcast_grp_t));
 352                 grp->mbg_next = NULL;
 353                 grp->mbg_mac_impl = mip;
 354 
 355                 DTRACE_PROBE1(mac__bcast__add__new__group, mac_bcast_grp_t *,
 356                     grp);
 357 
 358                 grp->mbg_addr = kmem_zalloc(addr_len, KM_SLEEP);
 359                 bcopy(addr, grp->mbg_addr, addr_len);
 360                 grp->mbg_addrtype = addrtype;
 361                 grp->mbg_vid = vid;
 362 
 363                 /*
 364                  * Add a new flow to the underlying MAC.
 365                  */
 366                 bzero(&flow_desc, sizeof (flow_desc));
 367                 bcopy(addr, &flow_desc.fd_dst_mac, addr_len);
 368                 flow_desc.fd_mac_len = (uint32_t)addr_len;
 369 
 370                 flow_desc.fd_mask = FLOW_LINK_DST;
 371                 if (vid != 0) {
 372                         flow_desc.fd_vid = vid;
 373                         flow_desc.fd_mask |= FLOW_LINK_VID;
 374                 }
 375 
 376                 grp->mbg_id = atomic_add_32_nv(&mac_bcast_id, 1);
 377                 (void) sprintf(flow_name,
 378                     "mac/%s/mcast%d", mip->mi_name, grp->mbg_id);
 379 
 380                 rc = mac_flow_create(&flow_desc, NULL, flow_name,
 381                     grp, FLOW_MCAST, &grp->mbg_flow_ent);
 382                 if (rc != 0) {
 383                         kmem_free(grp->mbg_addr, addr_len);
 384                         kmem_cache_free(mac_bcast_grp_cache, grp);
 385                         goto fail;
 386                 }
 387                 grp->mbg_flow_ent->fe_mbg = grp;
 388                 mip->mi_bcast_ngrps++;
 389 
 390                 /*
 391                  * Initial creation reference on the flow. This is released
 392                  * in the corresponding delete action i_mac_bcast_delete()
 393                  */
 394                 FLOW_REFHOLD(grp->mbg_flow_ent);
 395 
 396                 /*
 397                  * When the multicast and broadcast packet is received
 398                  * by the underlying NIC, mac_rx_classify() will invoke
 399                  * mac_bcast_send() with arg2=NULL, which will cause
 400                  * mac_bcast_send() to send a copy of the packet(s)
 401                  * to every MAC client opened on top of the underlying MAC.
 402                  *
 403                  * When the mac_bcast_send() function is invoked from
 404                  * the transmit path of a MAC client, it will specify the
 405                  * transmitting MAC client as the arg2 value, which will
 406                  * allow mac_bcast_send() to skip that MAC client and not
 407                  * send it a copy of the packet.
 408                  *
 409                  * We program the classifier to dispatch matching broadcast
 410                  * packets to mac_bcast_send().
 411                  */
 412 
 413                 grp->mbg_flow_ent->fe_cb_fn = mac_bcast_send;
 414                 grp->mbg_flow_ent->fe_cb_arg1 = grp;
 415                 grp->mbg_flow_ent->fe_cb_arg2 = NULL;
 416 
 417                 rc = mac_flow_add(mip->mi_flow_tab, grp->mbg_flow_ent);
 418                 if (rc != 0) {
 419                         FLOW_FINAL_REFRELE(grp->mbg_flow_ent);
 420                         goto fail;
 421                 }
 422 
 423                 *last_grp = grp;
 424         }
 425 
 426         ASSERT(grp->mbg_addrtype == addrtype);
 427 
 428         /*
 429          * Add the MAC client to the list of MAC clients associated
 430          * with the group.
 431          */
 432         rw_enter(&mip->mi_rw_lock, RW_WRITER);
 433         for (i = 0; i < grp->mbg_nclients_alloc; i++) {
 434                 /*
 435                  * The MAC client was already added, say when we have
 436                  * different unicast addresses with the same vid.
 437                  * Just increment the ref and we are done.
 438                  */
 439                 if (grp->mbg_clients[i].mgb_client == mcip) {
 440                         grp->mbg_clients[i].mgb_client_ref++;
 441                         rw_exit(&mip->mi_rw_lock);
 442                         return (0);
 443                 } else if (grp->mbg_clients[i].mgb_client == NULL &&
 444                     index == -1) {
 445                         index = i;
 446                 }
 447         }
 448         if (grp->mbg_nclients_alloc == grp->mbg_nclients) {
 449                 mac_bcast_grp_mcip_t    *new_clients;
 450                 uint_t                  new_size = grp->mbg_nclients+1;
 451 
 452                 new_clients = kmem_zalloc(new_size *
 453                     sizeof (mac_bcast_grp_mcip_t), KM_SLEEP);
 454 
 455                 if (grp->mbg_nclients > 0) {
 456                         ASSERT(grp->mbg_clients != NULL);
 457                         bcopy(grp->mbg_clients, new_clients, grp->mbg_nclients *
 458                             sizeof (mac_bcast_grp_mcip_t));
 459                         kmem_free(grp->mbg_clients, grp->mbg_nclients *
 460                             sizeof (mac_bcast_grp_mcip_t));
 461                 }
 462 
 463                 grp->mbg_clients = new_clients;
 464                 grp->mbg_nclients_alloc = new_size;
 465                 index = new_size - 1;
 466         }
 467 
 468         ASSERT(index != -1);
 469         grp->mbg_clients[index].mgb_client = mcip;
 470         grp->mbg_clients[index].mgb_client_ref = 1;
 471         grp->mbg_nclients++;
 472         /*
 473          * Since we're adding to the list of MAC clients using that group,
 474          * kick the generation count, which will allow mac_bcast_send()
 475          * to detect that condition after re-acquiring the lock.
 476          */
 477         grp->mbg_clients_gen++;
 478         rw_exit(&mip->mi_rw_lock);
 479         return (0);
 480 
 481 fail:
 482         if (prev_mi_addr != NULL) {
 483                 kmem_free(*prev_mi_addr, sizeof (mac_mcast_addrs_t));
 484                 *prev_mi_addr = NULL;
 485                 (void) mip->mi_multicst(mip->mi_driver, B_FALSE, addr);
 486         }
 487         if (prev_mci_addr != NULL) {
 488                 kmem_free(*prev_mci_addr, sizeof (mac_mcast_addrs_t));
 489                 *prev_mci_addr = NULL;
 490         }
 491         return (rc);
 492 }
 493 
 494 /*
 495  * Remove the specified MAC client from the group corresponding to
 496  * the specific broadcast or multicast address.
 497  *
 498  * Note: mac_bcast_delete() calls  mac_remove_flow() which
 499  * will call cv_wait for fe_refcnt to drop to 0. So this function
 500  * should not be called from interrupt or STREAMS context.
 501  */
 502 void
 503 mac_bcast_delete(mac_client_impl_t *mcip, const uint8_t *addr, uint16_t vid)
 504 {
 505         mac_impl_t *mip = mcip->mci_mip;
 506         mac_bcast_grp_t *grp = NULL, **prev;
 507         size_t addr_len = mip->mi_type->mt_addr_length;
 508         flow_entry_t *flent;
 509         uint_t i;
 510         mac_mcast_addrs_t       *maddr = NULL;
 511         mac_mcast_addrs_t       **mprev;
 512 
 513         ASSERT(MAC_PERIM_HELD((mac_handle_t)mip));
 514 
 515         /* find the broadcast group. The list is protected by the perimeter */
 516         prev = &mip->mi_bcast_grp;
 517         for (grp = mip->mi_bcast_grp; grp != NULL; prev = &grp->mbg_next,
 518             grp = grp->mbg_next) {
 519                 if (bcmp(grp->mbg_addr, addr, addr_len) == 0 &&
 520                     grp->mbg_vid == vid)
 521                         break;
 522         }
 523         ASSERT(grp != NULL);
 524 
 525         /*
 526          * Remove the MAC client from the list of MAC clients associated
 527          * with that broadcast group.
 528          *
 529          * We mark the mbg_clients[] location corresponding to the removed MAC
 530          * client NULL and reuse that location when we add a new MAC client.
 531          */
 532 
 533         rw_enter(&mip->mi_rw_lock, RW_WRITER);
 534 
 535         for (i = 0; i < grp->mbg_nclients_alloc; i++) {
 536                 if (grp->mbg_clients[i].mgb_client == mcip)
 537                         break;
 538         }
 539 
 540         ASSERT(i < grp->mbg_nclients_alloc);
 541         /*
 542          * If there are more references to this MAC client, then we let
 543          * it remain till it goes to 0.
 544          */
 545         if (--grp->mbg_clients[i].mgb_client_ref > 0)
 546                 goto update_maddr;
 547 
 548         grp->mbg_clients[i].mgb_client = NULL;
 549         grp->mbg_clients[i].mgb_client_ref = 0;
 550 
 551         /*
 552          * Since we're removing from the list of MAC clients using that group,
 553          * kick the generation count, which will allow mac_bcast_send()
 554          * to detect that condition.
 555          */
 556         grp->mbg_clients_gen++;
 557 
 558         if (--grp->mbg_nclients == 0) {
 559                 /*
 560                  * The last MAC client of the group was just removed.
 561                  * Unlink the current group from the list of groups
 562                  * defined on top of the underlying NIC. The group
 563                  * structure will stay around until the last reference
 564                  * is dropped.
 565                  */
 566                 *prev = grp->mbg_next;
 567         }
 568 update_maddr:
 569         rw_exit(&mip->mi_rw_lock);
 570 
 571         if (grp->mbg_addrtype == MAC_ADDRTYPE_MULTICAST) {
 572                 mprev = &mcip->mci_mcast_addrs;
 573                 for (maddr = mcip->mci_mcast_addrs; maddr != NULL;
 574                     mprev = &maddr->mma_next, maddr = maddr->mma_next) {
 575                         if (bcmp(grp->mbg_addr, maddr->mma_addr,
 576                             mip->mi_type->mt_addr_length) == 0)
 577                                 break;
 578                 }
 579                 ASSERT(maddr != NULL);
 580                 if (--maddr->mma_ref == 0) {
 581                         *mprev = maddr->mma_next;
 582                         maddr->mma_next = NULL;
 583                         kmem_free(maddr, sizeof (mac_mcast_addrs_t));
 584                 }
 585 
 586                 mprev = &mip->mi_mcast_addrs;
 587                 for (maddr = mip->mi_mcast_addrs; maddr != NULL;
 588                     mprev = &maddr->mma_next, maddr = maddr->mma_next) {
 589                         if (bcmp(grp->mbg_addr, maddr->mma_addr,
 590                             mip->mi_type->mt_addr_length) == 0)
 591                                 break;
 592                 }
 593                 ASSERT(maddr != NULL);
 594                 if (--maddr->mma_ref == 0) {
 595                         (void) mip->mi_multicst(mip->mi_driver, B_FALSE, addr);
 596                         *mprev = maddr->mma_next;
 597                         maddr->mma_next = NULL;
 598                         kmem_free(maddr, sizeof (mac_mcast_addrs_t));
 599                 }
 600         }
 601 
 602         /*
 603          * If the group itself is being removed, remove the
 604          * corresponding flow from the underlying NIC.
 605          */
 606         flent = grp->mbg_flow_ent;
 607         if (grp->mbg_nclients == 0) {
 608                 mac_flow_remove(mip->mi_flow_tab, flent, B_FALSE);
 609                 mac_flow_wait(flent, FLOW_DRIVER_UPCALL);
 610                 FLOW_FINAL_REFRELE(flent);
 611         }
 612 }
 613 
 614 /*
 615  * This will be called by a driver, such as aggr, when a port is added/removed
 616  * to add/remove the port to/from all the multcast addresses for that aggr.
 617  */
 618 void
 619 mac_bcast_refresh(mac_impl_t *mip, mac_multicst_t refresh_fn, void *arg,
 620     boolean_t add)
 621 {
 622         mac_mcast_addrs_t *grp, *next;
 623 
 624         ASSERT(refresh_fn != NULL);
 625 
 626         ASSERT(MAC_PERIM_HELD((mac_handle_t)mip));
 627 
 628         /*
 629          * Walk the multicast address list and call the refresh function for
 630          * each address.
 631          */
 632 
 633         for (grp = mip->mi_mcast_addrs; grp != NULL; grp = next) {
 634                 /*
 635                  * Save the next pointer just in case the refresh
 636                  * function's action causes the group entry to be
 637                  * freed.
 638                  * We won't be adding to this list as part of the
 639                  * refresh.
 640                  */
 641                 next = grp->mma_next;
 642                 refresh_fn(arg, add, grp->mma_addr);
 643         }
 644 }
 645 
 646 /*
 647  * Walk the MAC client's multicast address list and add/remove the addr/vid
 648  * ('arg' is 'flent') to all the addresses.
 649  */
 650 void
 651 mac_client_bcast_refresh(mac_client_impl_t *mcip, mac_multicst_t refresh_fn,
 652     void *arg, boolean_t add)
 653 {
 654         mac_mcast_addrs_t *grp, *next;
 655         mac_impl_t              *mip = mcip->mci_mip;
 656 
 657         ASSERT(refresh_fn != NULL);
 658 
 659         ASSERT(MAC_PERIM_HELD((mac_handle_t)mip));
 660         /*
 661          * Walk the multicast address list and call the refresh function for
 662          * each address.
 663          * Broadcast addresses are not added or removed through the multicast
 664          * entry points, so don't include them as part of the refresh.
 665          */
 666         for (grp = mcip->mci_mcast_addrs; grp != NULL; grp = next) {
 667                 /*
 668                  * Save the next pointer just in case the refresh
 669                  * function's action causes the group entry to be
 670                  * freed.
 671                  * We won't be adding to this list as part of the
 672                  * refresh.
 673                  */
 674                 next = grp->mma_next;
 675                 refresh_fn(arg, add, grp->mma_addr);
 676         }
 677 }