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