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 */
72typedef 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
77typedef 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
91static kmem_cache_t *mac_bcast_grp_cache;
92static uint32_t mac_bcast_id = 0;
93
94void
95mac_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
101void
102mac_bcast_fini(void)
103{
104	kmem_cache_destroy(mac_bcast_grp_cache);
105}
106
107mac_impl_t *
108mac_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 */
119void
120mac_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 */
140void
141mac_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 */
262int
263mac_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
481fail:
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 */
502void
503mac_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	}
568update_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 */
618void
619mac_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 */
650void
651mac_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}
678