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