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 2009 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 dst_mcip->mci_stat_multircv++; 217 else 218 dst_mcip->mci_stat_brdcstrcv++; 219 220 if (grp->mbg_clients_gen != gen) { 221 /* 222 * The list of MAC clients associated with the group 223 * was changed while the lock was released. 224 * Give up on the current packet. 225 */ 226 rw_exit(&mip->mi_rw_lock); 227 freemsgchain(mp_chain); 228 return; 229 } 230 } 231 rw_exit(&mip->mi_rw_lock); 232 233 if (src_mcip != NULL) { 234 /* 235 * The packet was sent from one of the MAC clients, 236 * so we need to send a copy of the packet to the 237 * underlying NIC so that it can be sent on the wire. 238 */ 239 src_mcip->mci_stat_multixmt++; 240 src_mcip->mci_stat_brdcstxmt++; 241 242 MAC_TX(mip, mip->mi_default_tx_ring, mp_chain, B_FALSE); 243 if (mp_chain != NULL) 244 freemsgchain(mp_chain); 245 } else { 246 freemsgchain(mp_chain); 247 } 248 } 249 250 /* 251 * Add the specified MAC client to the group corresponding to the specified 252 * broadcast or multicast address. 253 * Return 0 on success, or an errno value on failure. 254 */ 255 int 256 mac_bcast_add(mac_client_impl_t *mcip, const uint8_t *addr, uint16_t vid, 257 mac_addrtype_t addrtype) 258 { 259 mac_impl_t *mip = mcip->mci_mip; 260 mac_bcast_grp_t *grp = NULL, **last_grp; 261 size_t addr_len = mip->mi_type->mt_addr_length; 262 int rc = 0; 263 int i, index = -1; 264 mac_mcast_addrs_t **prev_mi_addr = NULL; 265 mac_mcast_addrs_t **prev_mci_addr = NULL; 266 267 ASSERT(MAC_PERIM_HELD((mac_handle_t)mip)); 268 269 ASSERT(addrtype == MAC_ADDRTYPE_MULTICAST || 270 addrtype == MAC_ADDRTYPE_BROADCAST); 271 272 /* 273 * Add the MAC client to the list of MAC clients associated 274 * with the group. 275 */ 276 if (addrtype == MAC_ADDRTYPE_MULTICAST) { 277 mac_mcast_addrs_t *maddr; 278 279 /* 280 * In case of a driver (say aggr), we need this information 281 * on a per MAC instance basis. 282 */ 283 prev_mi_addr = &mip->mi_mcast_addrs; 284 for (maddr = *prev_mi_addr; maddr != NULL; 285 prev_mi_addr = &maddr->mma_next, maddr = maddr->mma_next) { 286 if (bcmp(maddr->mma_addr, addr, addr_len) == 0) 287 break; 288 } 289 if (maddr == NULL) { 290 /* 291 * For multicast addresses, have the underlying MAC 292 * join the corresponding multicast group. 293 */ 294 rc = mip->mi_multicst(mip->mi_driver, B_TRUE, addr); 295 if (rc != 0) 296 return (rc); 297 maddr = kmem_zalloc(sizeof (mac_mcast_addrs_t), 298 KM_SLEEP); 299 bcopy(addr, maddr->mma_addr, addr_len); 300 *prev_mi_addr = maddr; 301 } else { 302 prev_mi_addr = NULL; 303 } 304 maddr->mma_ref++; 305 306 /* 307 * We maintain a separate list for each MAC client. Get 308 * the entry or add, if it is not present. 309 */ 310 prev_mci_addr = &mcip->mci_mcast_addrs; 311 for (maddr = *prev_mci_addr; maddr != NULL; 312 prev_mci_addr = &maddr->mma_next, maddr = maddr->mma_next) { 313 if (bcmp(maddr->mma_addr, addr, addr_len) == 0) 314 break; 315 } 316 if (maddr == NULL) { 317 maddr = kmem_zalloc(sizeof (mac_mcast_addrs_t), 318 KM_SLEEP); 319 bcopy(addr, maddr->mma_addr, addr_len); 320 *prev_mci_addr = maddr; 321 } else { 322 prev_mci_addr = NULL; 323 } 324 maddr->mma_ref++; 325 } 326 327 /* The list is protected by the perimeter */ 328 last_grp = &mip->mi_bcast_grp; 329 for (grp = *last_grp; grp != NULL; 330 last_grp = &grp->mbg_next, grp = grp->mbg_next) { 331 if (bcmp(grp->mbg_addr, addr, addr_len) == 0 && 332 grp->mbg_vid == vid) 333 break; 334 } 335 336 if (grp == NULL) { 337 /* 338 * The group does not yet exist, create it. 339 */ 340 flow_desc_t flow_desc; 341 char flow_name[MAXFLOWNAMELEN]; 342 343 grp = kmem_cache_alloc(mac_bcast_grp_cache, KM_SLEEP); 344 bzero(grp, sizeof (mac_bcast_grp_t)); 345 grp->mbg_next = NULL; 346 grp->mbg_mac_impl = mip; 347 348 DTRACE_PROBE1(mac__bcast__add__new__group, mac_bcast_grp_t *, 349 grp); 350 351 grp->mbg_addr = kmem_zalloc(addr_len, KM_SLEEP); 352 bcopy(addr, grp->mbg_addr, addr_len); 353 grp->mbg_addrtype = addrtype; 354 grp->mbg_vid = vid; 355 356 /* 357 * Add a new flow to the underlying MAC. 358 */ 359 bzero(&flow_desc, sizeof (flow_desc)); 360 bcopy(addr, &flow_desc.fd_dst_mac, addr_len); 361 flow_desc.fd_mac_len = (uint32_t)addr_len; 362 363 flow_desc.fd_mask = FLOW_LINK_DST; 364 if (vid != 0) { 365 flow_desc.fd_vid = vid; 366 flow_desc.fd_mask |= FLOW_LINK_VID; 367 } 368 369 grp->mbg_id = atomic_add_32_nv(&mac_bcast_id, 1); 370 (void) sprintf(flow_name, 371 "mac/%s/mcast%d", mip->mi_name, grp->mbg_id); 372 373 rc = mac_flow_create(&flow_desc, NULL, flow_name, 374 grp, FLOW_MCAST, &grp->mbg_flow_ent); 375 if (rc != 0) { 376 kmem_free(grp->mbg_addr, addr_len); 377 kmem_cache_free(mac_bcast_grp_cache, grp); 378 goto fail; 379 } 380 grp->mbg_flow_ent->fe_mbg = grp; 381 mip->mi_bcast_ngrps++; 382 383 /* 384 * Initial creation reference on the flow. This is released 385 * in the corresponding delete action i_mac_bcast_delete() 386 */ 387 FLOW_REFHOLD(grp->mbg_flow_ent); 388 389 /* 390 * When the multicast and broadcast packet is received 391 * by the underlying NIC, mac_rx_classify() will invoke 392 * mac_bcast_send() with arg2=NULL, which will cause 393 * mac_bcast_send() to send a copy of the packet(s) 394 * to every MAC client opened on top of the underlying MAC. 395 * 396 * When the mac_bcast_send() function is invoked from 397 * the transmit path of a MAC client, it will specify the 398 * transmitting MAC client as the arg2 value, which will 399 * allow mac_bcast_send() to skip that MAC client and not 400 * send it a copy of the packet. 401 * 402 * We program the classifier to dispatch matching broadcast 403 * packets to mac_bcast_send(). 404 */ 405 406 grp->mbg_flow_ent->fe_cb_fn = mac_bcast_send; 407 grp->mbg_flow_ent->fe_cb_arg1 = grp; 408 grp->mbg_flow_ent->fe_cb_arg2 = NULL; 409 410 rc = mac_flow_add(mip->mi_flow_tab, grp->mbg_flow_ent); 411 if (rc != 0) { 412 FLOW_FINAL_REFRELE(grp->mbg_flow_ent); 413 goto fail; 414 } 415 416 *last_grp = grp; 417 } 418 419 ASSERT(grp->mbg_addrtype == addrtype); 420 421 /* 422 * Add the MAC client to the list of MAC clients associated 423 * with the group. 424 */ 425 rw_enter(&mip->mi_rw_lock, RW_WRITER); 426 for (i = 0; i < grp->mbg_nclients_alloc; i++) { 427 /* 428 * The MAC client was already added, say when we have 429 * different unicast addresses with the same vid. 430 * Just increment the ref and we are done. 431 */ 432 if (grp->mbg_clients[i].mgb_client == mcip) { 433 grp->mbg_clients[i].mgb_client_ref++; 434 rw_exit(&mip->mi_rw_lock); 435 return (0); 436 } else if (grp->mbg_clients[i].mgb_client == NULL && 437 index == -1) { 438 index = i; 439 } 440 } 441 if (grp->mbg_nclients_alloc == grp->mbg_nclients) { 442 mac_bcast_grp_mcip_t *new_clients; 443 uint_t new_size = grp->mbg_nclients+1; 444 445 new_clients = kmem_zalloc(new_size * 446 sizeof (mac_bcast_grp_mcip_t), KM_SLEEP); 447 448 if (grp->mbg_nclients > 0) { 449 ASSERT(grp->mbg_clients != NULL); 450 bcopy(grp->mbg_clients, new_clients, grp->mbg_nclients * 451 sizeof (mac_bcast_grp_mcip_t)); 452 kmem_free(grp->mbg_clients, grp->mbg_nclients * 453 sizeof (mac_bcast_grp_mcip_t)); 454 } 455 456 grp->mbg_clients = new_clients; 457 grp->mbg_nclients_alloc = new_size; 458 index = new_size - 1; 459 } 460 461 ASSERT(index != -1); 462 grp->mbg_clients[index].mgb_client = mcip; 463 grp->mbg_clients[index].mgb_client_ref = 1; 464 grp->mbg_nclients++; 465 /* 466 * Since we're adding to the list of MAC clients using that group, 467 * kick the generation count, which will allow mac_bcast_send() 468 * to detect that condition after re-acquiring the lock. 469 */ 470 grp->mbg_clients_gen++; 471 rw_exit(&mip->mi_rw_lock); 472 return (0); 473 474 fail: 475 if (prev_mi_addr != NULL) { 476 kmem_free(*prev_mi_addr, sizeof (mac_mcast_addrs_t)); 477 *prev_mi_addr = NULL; 478 (void) mip->mi_multicst(mip->mi_driver, B_FALSE, addr); 479 } 480 if (prev_mci_addr != NULL) { 481 kmem_free(*prev_mci_addr, sizeof (mac_mcast_addrs_t)); 482 *prev_mci_addr = NULL; 483 } 484 return (rc); 485 } 486 487 /* 488 * Remove the specified MAC client from the group corresponding to 489 * the specific broadcast or multicast address. 490 * 491 * Note: mac_bcast_delete() calls mac_remove_flow() which 492 * will call cv_wait for fe_refcnt to drop to 0. So this function 493 * should not be called from interrupt or STREAMS context. 494 */ 495 void 496 mac_bcast_delete(mac_client_impl_t *mcip, const uint8_t *addr, uint16_t vid) 497 { 498 mac_impl_t *mip = mcip->mci_mip; 499 mac_bcast_grp_t *grp = NULL, **prev; 500 size_t addr_len = mip->mi_type->mt_addr_length; 501 flow_entry_t *flent; 502 uint_t i; 503 mac_mcast_addrs_t *maddr = NULL; 504 mac_mcast_addrs_t **mprev; 505 506 ASSERT(MAC_PERIM_HELD((mac_handle_t)mip)); 507 508 /* find the broadcast group. The list is protected by the perimeter */ 509 prev = &mip->mi_bcast_grp; 510 for (grp = mip->mi_bcast_grp; grp != NULL; prev = &grp->mbg_next, 511 grp = grp->mbg_next) { 512 if (bcmp(grp->mbg_addr, addr, addr_len) == 0 && 513 grp->mbg_vid == vid) 514 break; 515 } 516 ASSERT(grp != NULL); 517 518 /* 519 * Remove the MAC client from the list of MAC clients associated 520 * with that broadcast group. 521 * 522 * We mark the mbg_clients[] location corresponding to the removed MAC 523 * client NULL and reuse that location when we add a new MAC client. 524 */ 525 526 rw_enter(&mip->mi_rw_lock, RW_WRITER); 527 528 for (i = 0; i < grp->mbg_nclients_alloc; i++) { 529 if (grp->mbg_clients[i].mgb_client == mcip) 530 break; 531 } 532 533 ASSERT(i < grp->mbg_nclients_alloc); 534 /* 535 * If there are more references to this MAC client, then we let 536 * it remain till it goes to 0. 537 */ 538 if (--grp->mbg_clients[i].mgb_client_ref > 0) 539 goto update_maddr; 540 541 grp->mbg_clients[i].mgb_client = NULL; 542 grp->mbg_clients[i].mgb_client_ref = 0; 543 544 /* 545 * Since we're removing from the list of MAC clients using that group, 546 * kick the generation count, which will allow mac_bcast_send() 547 * to detect that condition. 548 */ 549 grp->mbg_clients_gen++; 550 551 if (--grp->mbg_nclients == 0) { 552 /* 553 * The last MAC client of the group was just removed. 554 * Unlink the current group from the list of groups 555 * defined on top of the underlying NIC. The group 556 * structure will stay around until the last reference 557 * is dropped. 558 */ 559 *prev = grp->mbg_next; 560 } 561 update_maddr: 562 rw_exit(&mip->mi_rw_lock); 563 564 if (grp->mbg_addrtype == MAC_ADDRTYPE_MULTICAST) { 565 mprev = &mcip->mci_mcast_addrs; 566 for (maddr = mcip->mci_mcast_addrs; maddr != NULL; 567 mprev = &maddr->mma_next, maddr = maddr->mma_next) { 568 if (bcmp(grp->mbg_addr, maddr->mma_addr, 569 mip->mi_type->mt_addr_length) == 0) 570 break; 571 } 572 ASSERT(maddr != NULL); 573 if (--maddr->mma_ref == 0) { 574 *mprev = maddr->mma_next; 575 maddr->mma_next = NULL; 576 kmem_free(maddr, sizeof (mac_mcast_addrs_t)); 577 } 578 579 mprev = &mip->mi_mcast_addrs; 580 for (maddr = mip->mi_mcast_addrs; maddr != NULL; 581 mprev = &maddr->mma_next, maddr = maddr->mma_next) { 582 if (bcmp(grp->mbg_addr, maddr->mma_addr, 583 mip->mi_type->mt_addr_length) == 0) 584 break; 585 } 586 ASSERT(maddr != NULL); 587 if (--maddr->mma_ref == 0) { 588 (void) mip->mi_multicst(mip->mi_driver, B_FALSE, addr); 589 *mprev = maddr->mma_next; 590 maddr->mma_next = NULL; 591 kmem_free(maddr, sizeof (mac_mcast_addrs_t)); 592 } 593 } 594 595 /* 596 * If the group itself is being removed, remove the 597 * corresponding flow from the underlying NIC. 598 */ 599 flent = grp->mbg_flow_ent; 600 if (grp->mbg_nclients == 0) { 601 mac_flow_remove(mip->mi_flow_tab, flent, B_FALSE); 602 mac_flow_wait(flent, FLOW_DRIVER_UPCALL); 603 FLOW_FINAL_REFRELE(flent); 604 } 605 } 606 607 /* 608 * This will be called by a driver, such as aggr, when a port is added/removed 609 * to add/remove the port to/from all the multcast addresses for that aggr. 610 */ 611 void 612 mac_bcast_refresh(mac_impl_t *mip, mac_multicst_t refresh_fn, void *arg, 613 boolean_t add) 614 { 615 mac_mcast_addrs_t *grp, *next; 616 617 ASSERT(refresh_fn != NULL); 618 619 ASSERT(MAC_PERIM_HELD((mac_handle_t)mip)); 620 621 /* 622 * Walk the multicast address list and call the refresh function for 623 * each address. 624 */ 625 626 for (grp = mip->mi_mcast_addrs; grp != NULL; grp = next) { 627 /* 628 * Save the next pointer just in case the refresh 629 * function's action causes the group entry to be 630 * freed. 631 * We won't be adding to this list as part of the 632 * refresh. 633 */ 634 next = grp->mma_next; 635 refresh_fn(arg, add, grp->mma_addr); 636 } 637 } 638 639 /* 640 * Walk the MAC client's multicast address list and add/remove the addr/vid 641 * ('arg' is 'flent') to all the addresses. 642 */ 643 void 644 mac_client_bcast_refresh(mac_client_impl_t *mcip, mac_multicst_t refresh_fn, 645 void *arg, boolean_t add) 646 { 647 mac_mcast_addrs_t *grp, *next; 648 mac_impl_t *mip = mcip->mci_mip; 649 650 ASSERT(refresh_fn != NULL); 651 652 ASSERT(MAC_PERIM_HELD((mac_handle_t)mip)); 653 /* 654 * Walk the multicast address list and call the refresh function for 655 * each address. 656 * Broadcast addresses are not added or removed through the multicast 657 * entry points, so don't include them as part of the refresh. 658 */ 659 for (grp = mcip->mci_mcast_addrs; grp != NULL; grp = next) { 660 /* 661 * Save the next pointer just in case the refresh 662 * function's action causes the group entry to be 663 * freed. 664 * We won't be adding to this list as part of the 665 * refresh. 666 */ 667 next = grp->mma_next; 668 refresh_fn(arg, add, grp->mma_addr); 669 } 670 } 671