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 2008 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 if (grp->mbg_addrtype == MAC_ADDRTYPE_MULTICAST) { 128 /* 129 * The address is a multicast address, have the 130 * underlying NIC leave the multicast group. 131 */ 132 (void) mip->mi_multicst(mip->mi_driver, B_FALSE, grp->mbg_addr); 133 } 134 135 ASSERT(grp->mbg_addr != NULL); 136 kmem_free(grp->mbg_addr, mip->mi_type->mt_addr_length); 137 kmem_free(grp->mbg_clients, 138 grp->mbg_nclients_alloc * sizeof (mac_bcast_grp_mcip_t)); 139 mip->mi_bcast_ngrps--; 140 kmem_cache_free(mac_bcast_grp_cache, grp); 141 } 142 143 /* 144 * arg1: broadcast group 145 * arg2: sender MAC client if it is being sent by a MAC client, 146 * NULL if it was received from the wire. 147 */ 148 void 149 mac_bcast_send(void *arg1, void *arg2, mblk_t *mp_chain, boolean_t is_loopback) 150 { 151 mac_bcast_grp_t *grp = arg1; 152 mac_client_impl_t *src_mcip = arg2, *dst_mcip; 153 mac_impl_t *mip = grp->mbg_mac_impl; 154 uint64_t gen; 155 uint_t i; 156 mblk_t *mp_chain1; 157 flow_entry_t *flent; 158 int err; 159 160 rw_enter(&mip->mi_rw_lock, RW_READER); 161 162 /* 163 * Pass a copy of the mp chain to every MAC client except the sender 164 * MAC client, if the packet was not received from the underlying NIC. 165 * 166 * The broadcast group lock should not be held across calls to 167 * the flow's callback function, since the same group could 168 * potentially be accessed from the same context. When the lock 169 * is reacquired, changes to the broadcast group while the lock 170 * was released are caught using a generation counter incremented 171 * each time the list of MAC clients associated with the broadcast 172 * group is changed. 173 */ 174 for (i = 0; i < grp->mbg_nclients_alloc; i++) { 175 dst_mcip = grp->mbg_clients[i].mgb_client; 176 if (dst_mcip == NULL) 177 continue; 178 flent = dst_mcip->mci_flent; 179 if (flent == NULL || dst_mcip == src_mcip) { 180 /* 181 * Don't send a copy of the packet back to 182 * its sender. 183 */ 184 continue; 185 } 186 187 /* 188 * It is important to hold a reference on the 189 * flow_ent here. 190 */ 191 if ((mp_chain1 = mac_copymsgchain_cksum(mp_chain)) == NULL) 192 break; 193 /* 194 * Fix the checksum for packets originating 195 * from the local machine. 196 */ 197 if ((src_mcip != NULL) && 198 (mp_chain1 = mac_fix_cksum(mp_chain1)) == NULL) 199 break; 200 201 FLOW_TRY_REFHOLD(flent, err); 202 if (err != 0) { 203 freemsgchain(mp_chain1); 204 continue; 205 } 206 207 gen = grp->mbg_clients_gen; 208 209 rw_exit(&mip->mi_rw_lock); 210 211 DTRACE_PROBE4(mac__bcast__send__to, mac_client_impl_t *, 212 src_mcip, flow_fn_t, dst_mcip->mci_flent->fe_cb_fn, 213 void *, dst_mcip->mci_flent->fe_cb_arg1, 214 void *, dst_mcip->mci_flent->fe_cb_arg2); 215 216 (dst_mcip->mci_flent->fe_cb_fn)(dst_mcip->mci_flent->fe_cb_arg1, 217 dst_mcip->mci_flent->fe_cb_arg2, mp_chain1, is_loopback); 218 FLOW_REFRELE(flent); 219 220 rw_enter(&mip->mi_rw_lock, RW_READER); 221 222 /* update stats */ 223 if (grp->mbg_addrtype == MAC_ADDRTYPE_MULTICAST) 224 dst_mcip->mci_stat_multircv++; 225 else 226 dst_mcip->mci_stat_brdcstrcv++; 227 228 if (grp->mbg_clients_gen != gen) { 229 /* 230 * The list of MAC clients associated with the group 231 * was changed while the lock was released. 232 * Give up on the current packet. 233 */ 234 rw_exit(&mip->mi_rw_lock); 235 freemsgchain(mp_chain); 236 return; 237 } 238 } 239 rw_exit(&mip->mi_rw_lock); 240 241 if (src_mcip != NULL) { 242 /* 243 * The packet was sent from one of the MAC clients, 244 * so we need to send a copy of the packet to the 245 * underlying NIC so that it can be sent on the wire. 246 */ 247 mblk_t *rest; 248 249 src_mcip->mci_stat_multixmt++; 250 src_mcip->mci_stat_brdcstxmt++; 251 252 rest = MAC_RING_TX_DEFAULT(mip, mp_chain); 253 if (rest != NULL) 254 freemsgchain(rest); 255 } else { 256 freemsgchain(mp_chain); 257 } 258 } 259 260 /* 261 * Add the specified MAC client to the group corresponding to the specified 262 * broadcast or multicast address. 263 * Return 0 on success, or an errno value on failure. 264 */ 265 int 266 mac_bcast_add(mac_client_impl_t *mcip, const uint8_t *addr, uint16_t vid, 267 mac_addrtype_t addrtype) 268 { 269 mac_impl_t *mip = mcip->mci_mip; 270 mac_bcast_grp_t *grp = NULL, **last_grp; 271 size_t addr_len = mip->mi_type->mt_addr_length; 272 int rc = 0; 273 int i, index = -1; 274 mac_mcast_addrs_t *mci_maddr = NULL; 275 mac_mcast_addrs_t *mi_maddr = NULL; 276 mac_mcast_addrs_t **last_maddr; 277 278 ASSERT(MAC_PERIM_HELD((mac_handle_t)mip)); 279 280 ASSERT(addrtype == MAC_ADDRTYPE_MULTICAST || 281 addrtype == MAC_ADDRTYPE_BROADCAST); 282 283 /* The list is protected by the perimeter */ 284 last_grp = &mip->mi_bcast_grp; 285 for (grp = *last_grp; grp != NULL; 286 last_grp = &grp->mbg_next, grp = grp->mbg_next) { 287 if (bcmp(grp->mbg_addr, addr, addr_len) == 0 && 288 grp->mbg_vid == vid) 289 break; 290 } 291 292 if (grp == NULL) { 293 /* 294 * The group does not yet exist, create it. 295 */ 296 flow_desc_t flow_desc; 297 char flow_name[MAXFLOWNAME]; 298 299 grp = kmem_cache_alloc(mac_bcast_grp_cache, KM_SLEEP); 300 bzero(grp, sizeof (mac_bcast_grp_t)); 301 grp->mbg_next = NULL; 302 grp->mbg_mac_impl = mip; 303 304 DTRACE_PROBE1(mac__bcast__add__new__group, mac_bcast_grp_t *, 305 grp); 306 307 grp->mbg_addr = kmem_zalloc(addr_len, KM_SLEEP); 308 bcopy(addr, grp->mbg_addr, addr_len); 309 grp->mbg_addrtype = addrtype; 310 grp->mbg_vid = vid; 311 312 /* 313 * Add a new flow to the underlying MAC. 314 */ 315 bzero(&flow_desc, sizeof (flow_desc)); 316 bcopy(addr, &flow_desc.fd_dst_mac, addr_len); 317 flow_desc.fd_mac_len = (uint32_t)addr_len; 318 319 flow_desc.fd_mask = FLOW_LINK_DST; 320 if (vid != 0) { 321 flow_desc.fd_vid = vid; 322 flow_desc.fd_mask |= FLOW_LINK_VID; 323 } 324 325 grp->mbg_id = atomic_add_32_nv(&mac_bcast_id, 1); 326 (void) sprintf(flow_name, 327 "mac/%s/mcast%d", mip->mi_name, grp->mbg_id); 328 329 rc = mac_flow_create(&flow_desc, NULL, flow_name, 330 grp, FLOW_MCAST, &grp->mbg_flow_ent); 331 if (rc != 0) { 332 kmem_free(grp->mbg_addr, addr_len); 333 kmem_cache_free(mac_bcast_grp_cache, grp); 334 return (rc); 335 } 336 grp->mbg_flow_ent->fe_mbg = grp; 337 mip->mi_bcast_ngrps++; 338 339 /* 340 * Initial creation reference on the flow. This is released 341 * in the corresponding delete action i_mac_bcast_delete() 342 */ 343 FLOW_REFHOLD(grp->mbg_flow_ent); 344 345 /* 346 * When the multicast and broadcast packet is received 347 * by the underlying NIC, mac_rx_classify() will invoke 348 * mac_bcast_send() with arg2=NULL, which will cause 349 * mac_bcast_send() to send a copy of the packet(s) 350 * to every MAC client opened on top of the underlying MAC. 351 * 352 * When the mac_bcast_send() function is invoked from 353 * the transmit path of a MAC client, it will specify the 354 * transmitting MAC client as the arg2 value, which will 355 * allow mac_bcast_send() to skip that MAC client and not 356 * send it a copy of the packet. 357 * 358 * We program the classifier to dispatch matching broadcast 359 * packets to mac_bcast_send(). 360 */ 361 362 grp->mbg_flow_ent->fe_cb_fn = mac_bcast_send; 363 grp->mbg_flow_ent->fe_cb_arg1 = grp; 364 grp->mbg_flow_ent->fe_cb_arg2 = NULL; 365 366 rc = mac_flow_add(mip->mi_flow_tab, grp->mbg_flow_ent); 367 if (rc != 0) { 368 FLOW_FINAL_REFRELE(grp->mbg_flow_ent); 369 return (rc); 370 } 371 372 /* 373 * For multicast addresses, have the underlying MAC 374 * join the corresponsing multicast group. 375 */ 376 if (addrtype == MAC_ADDRTYPE_MULTICAST) { 377 rc = mip->mi_multicst(mip->mi_driver, B_TRUE, addr); 378 if (rc != 0) { 379 mac_flow_remove(mip->mi_flow_tab, 380 grp->mbg_flow_ent, B_FALSE); 381 mac_flow_wait(grp->mbg_flow_ent, 382 FLOW_DRIVER_UPCALL); 383 FLOW_FINAL_REFRELE(grp->mbg_flow_ent); 384 return (rc); 385 } 386 } 387 388 *last_grp = grp; 389 } 390 391 ASSERT(grp->mbg_addrtype == addrtype); 392 393 /* 394 * Add the MAC client to the list of MAC clients associated 395 * with the group. 396 */ 397 rw_enter(&mip->mi_rw_lock, RW_WRITER); 398 if (addrtype == MAC_ADDRTYPE_MULTICAST) { 399 /* 400 * We maintain a separate list for each MAC client. Get 401 * the entry or add, if it is not present. 402 */ 403 last_maddr = &mcip->mci_mcast_addrs; 404 for (mci_maddr = *last_maddr; mci_maddr != NULL; 405 last_maddr = &mci_maddr->mma_next, 406 mci_maddr = mci_maddr->mma_next) { 407 if (bcmp(mci_maddr->mma_addr, addr, addr_len) == 0) 408 break; 409 } 410 if (mci_maddr == NULL) { 411 mci_maddr = kmem_zalloc(sizeof (mac_mcast_addrs_t), 412 KM_SLEEP); 413 bcopy(addr, mci_maddr->mma_addr, addr_len); 414 *last_maddr = mci_maddr; 415 } 416 mci_maddr->mma_ref++; 417 418 /* 419 * In case of a driver (say aggr), we also need this 420 * information on a per MAC instance basis. 421 */ 422 last_maddr = &mip->mi_mcast_addrs; 423 for (mi_maddr = *last_maddr; mi_maddr != NULL; 424 last_maddr = &mi_maddr->mma_next, 425 mi_maddr = mi_maddr->mma_next) { 426 if (bcmp(mi_maddr->mma_addr, addr, addr_len) == 0) 427 break; 428 } 429 if (mi_maddr == NULL) { 430 mi_maddr = kmem_zalloc(sizeof (mac_mcast_addrs_t), 431 KM_SLEEP); 432 bcopy(addr, mi_maddr->mma_addr, addr_len); 433 *last_maddr = mi_maddr; 434 } 435 mi_maddr->mma_ref++; 436 } 437 for (i = 0; i < grp->mbg_nclients_alloc; i++) { 438 /* 439 * The MAC client was already added, say when we have 440 * different unicast addresses with the same vid. 441 * Just increment the ref and we are done. 442 */ 443 if (grp->mbg_clients[i].mgb_client == mcip) { 444 grp->mbg_clients[i].mgb_client_ref++; 445 goto add_done; 446 } else if (grp->mbg_clients[i].mgb_client == NULL && 447 index == -1) { 448 index = i; 449 } 450 } 451 if (grp->mbg_nclients_alloc == grp->mbg_nclients) { 452 mac_bcast_grp_mcip_t *new_clients; 453 uint_t new_size = grp->mbg_nclients+1; 454 455 new_clients = kmem_zalloc(new_size * 456 sizeof (mac_bcast_grp_mcip_t), KM_SLEEP); 457 458 if (grp->mbg_nclients > 0) { 459 ASSERT(grp->mbg_clients != NULL); 460 bcopy(grp->mbg_clients, new_clients, grp->mbg_nclients * 461 sizeof (mac_bcast_grp_mcip_t)); 462 kmem_free(grp->mbg_clients, grp->mbg_nclients * 463 sizeof (mac_bcast_grp_mcip_t)); 464 } 465 466 grp->mbg_clients = new_clients; 467 grp->mbg_nclients_alloc = new_size; 468 index = new_size - 1; 469 } 470 471 ASSERT(index != -1); 472 grp->mbg_clients[index].mgb_client = mcip; 473 grp->mbg_clients[index].mgb_client_ref = 1; 474 grp->mbg_nclients++; 475 /* 476 * Since we're adding to the list of MAC clients using that group, 477 * kick the generation count, which will allow mac_bcast_send() 478 * to detect that condition after re-acquiring the lock. 479 */ 480 grp->mbg_clients_gen++; 481 add_done: 482 rw_exit(&mip->mi_rw_lock); 483 484 return (0); 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 if (grp->mbg_addrtype == MAC_ADDRTYPE_MULTICAST) { 563 mprev = &mcip->mci_mcast_addrs; 564 for (maddr = mcip->mci_mcast_addrs; maddr != NULL; 565 mprev = &maddr->mma_next, maddr = maddr->mma_next) { 566 if (bcmp(grp->mbg_addr, maddr->mma_addr, 567 mip->mi_type->mt_addr_length) == 0) 568 break; 569 } 570 ASSERT(maddr != NULL); 571 if (--maddr->mma_ref == 0) { 572 *mprev = maddr->mma_next; 573 maddr->mma_next = NULL; 574 kmem_free(maddr, sizeof (mac_mcast_addrs_t)); 575 } 576 577 mprev = &mip->mi_mcast_addrs; 578 for (maddr = mip->mi_mcast_addrs; maddr != NULL; 579 mprev = &maddr->mma_next, maddr = maddr->mma_next) { 580 if (bcmp(grp->mbg_addr, maddr->mma_addr, 581 mip->mi_type->mt_addr_length) == 0) 582 break; 583 } 584 ASSERT(maddr != NULL); 585 if (--maddr->mma_ref == 0) { 586 *mprev = maddr->mma_next; 587 maddr->mma_next = NULL; 588 kmem_free(maddr, sizeof (mac_mcast_addrs_t)); 589 } 590 } 591 rw_exit(&mip->mi_rw_lock); 592 593 /* 594 * If the group itself is being removed, remove the 595 * corresponding flow from the underlying NIC. 596 */ 597 flent = grp->mbg_flow_ent; 598 if (grp->mbg_nclients == 0) { 599 mac_flow_remove(mip->mi_flow_tab, flent, B_FALSE); 600 mac_flow_wait(flent, FLOW_DRIVER_UPCALL); 601 FLOW_FINAL_REFRELE(flent); 602 } 603 } 604 605 /* 606 * This will be called by a driver, such as aggr, when a port is added/removed 607 * to add/remove the port to/from all the multcast addresses for that aggr. 608 */ 609 void 610 mac_bcast_refresh(mac_impl_t *mip, mac_multicst_t refresh_fn, void *arg, 611 boolean_t add) 612 { 613 mac_mcast_addrs_t *grp, *next; 614 615 ASSERT(refresh_fn != NULL); 616 617 ASSERT(MAC_PERIM_HELD((mac_handle_t)mip)); 618 619 /* 620 * Walk the multicast address list and call the refresh function for 621 * each address. 622 */ 623 624 for (grp = mip->mi_mcast_addrs; grp != NULL; grp = next) { 625 /* 626 * Save the next pointer just in case the refresh 627 * function's action causes the group entry to be 628 * freed. 629 * We won't be adding to this list as part of the 630 * refresh. 631 */ 632 next = grp->mma_next; 633 refresh_fn(arg, add, grp->mma_addr); 634 } 635 } 636 637 /* 638 * Walk the MAC client's multicast address list and add/remove the addr/vid 639 * ('arg' is 'flent') to all the addresses. 640 */ 641 void 642 mac_client_bcast_refresh(mac_client_impl_t *mcip, mac_multicst_t refresh_fn, 643 void *arg, boolean_t add) 644 { 645 mac_mcast_addrs_t *grp, *next; 646 mac_impl_t *mip = mcip->mci_mip; 647 648 ASSERT(refresh_fn != NULL); 649 650 ASSERT(MAC_PERIM_HELD((mac_handle_t)mip)); 651 /* 652 * Walk the multicast address list and call the refresh function for 653 * each address. 654 * Broadcast addresses are not added or removed through the multicast 655 * entry points, so don't include them as part of the refresh. 656 */ 657 for (grp = mcip->mci_mcast_addrs; grp != NULL; grp = next) { 658 /* 659 * Save the next pointer just in case the refresh 660 * function's action causes the group entry to be 661 * freed. 662 * We won't be adding to this list as part of the 663 * refresh. 664 */ 665 next = grp->mma_next; 666 refresh_fn(arg, add, grp->mma_addr); 667 } 668 } 669