1/*
2 * Copyright (c) 2014-2015 The Linux Foundation. All rights reserved.
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17#include <linux/kernel.h>
18#include <linux/etherdevice.h>
19#include "mc_osdep.h"
20#include "mc_api.h"
21#include "mc_private.h"
22#include "mc_snooping.h"
23#include "mc_ecm.h"
24
25static mc_bridge_ipv4_update_callback_t __rcu mc_ipv4_event_cb = NULL;
26static mc_bridge_ipv6_update_callback_t __rcu mc_ipv6_event_cb = NULL;
27
28
29static int mc_bridge_if_source_filter(struct mc_mdb_entry *mdb, uint32_t ifindex, struct mc_ip *mc_source)
30{
31	struct mc_port_group *pg;
32	struct hlist_node *pgh;
33	int i;
34
35	/*no bridge port joining*/
36	if (hlist_empty(&mdb->pslist))
37		return 1;
38
39	os_hlist_for_each_entry_rcu(pg, pgh, &mdb->pslist, pslist) {
40		struct mc_fdb_group *fg;
41		struct hlist_node *fgh;
42
43		if (ifindex != ((struct net_bridge_port *)pg->port)->dev->ifindex)
44			continue;
45
46		/*no client joining*/
47		if (hlist_empty(&pg->fslist))
48			return 1;
49
50		/*anyone who would like to receive stream from the source*/
51		os_hlist_for_each_entry_rcu(fg, fgh, &pg->fslist, fslist) {
52			if (!fg->filter_mode || (fg->filter_mode == MC_DEF_FILTER_EXCLUDE && !fg->a.nsrcs))
53				return 0;
54
55			if (fg->filter_mode == MC_DEF_FILTER_INCLUDE && !fg->a.nsrcs)
56				continue;
57
58			if (mdb->group.pro == htons(ETH_P_IP)) {
59				u_int32_t ip4 = mc_source->u.ip4;
60				u_int32_t *srcs = (u_int32_t *) fg->a.srcs;
61
62				for (i = 0; i < fg->a.nsrcs; i++) {
63					if (srcs[i] == ip4)
64						break;
65				}
66			}
67#ifdef MC_SUPPORT_MLD
68			else {
69				struct in6_addr *ip6 = &mc_source->u.ip6;
70				struct in6_addr *srcs = (struct in6_addr *)fg->a.srcs;
71
72				for (i = 0; i < fg->a.nsrcs; i++) {
73					if (!ipv6_addr_cmp(&srcs[i], ip6))
74						break;
75				}
76			}
77#endif
78
79			if ((fg->filter_mode == MC_DEF_FILTER_INCLUDE && i != fg->a.nsrcs) ||
80				(fg->filter_mode == MC_DEF_FILTER_EXCLUDE && i == fg->a.nsrcs))
81				return 0;
82
83		}
84	}
85
86	return 1;
87
88}
89
90static int __mc_bridge_get_ifs(struct net_device *brdev, struct mc_ip *mc_group,
91			       struct mc_ip *mc_source, uint32_t max_dst, uint32_t dst_dev[])
92{
93	struct mc_struct *mc;
94	struct hlist_head *head;
95	struct mc_mdb_entry *mdb;
96	int i;
97	int ifnum;
98
99	mc = MC_DEV(brdev);
100	if (!mc || !mc->started) {
101		return -1;
102	}
103
104	head = &mc->hash[mc_group_hash(mc->salt, mc_group->u.ip4)];
105	mdb = mc_mdb_find(head, mc_group);
106	if (!mdb || !atomic_read(&mdb->users)) {
107		return 0;
108	}
109
110	read_lock(&mdb->rwlock);
111	for (i = 0, ifnum = 0; i < mdb->flood_ifcnt; i++) {
112		if (mc_bridge_if_source_filter(mdb, mdb->flood_ifindex[i], mc_source)) {
113
114			if (mc_group->pro == htons(ETH_P_IP))
115				MC_PRINT("Group "MC_IP4_STR" Source "MC_IP4_STR"  ignored for port %d\n",
116					MC_IP4_FMT((u8 *) &mc_group->u.ip4), MC_IP4_FMT((u8 *) &mc_source->u.ip4), mdb->flood_ifindex[i]);
117#ifdef MC_SUPPORT_MLD
118			else
119				MC_PRINT("Group "MC_IP6_STR" Source "MC_IP6_STR"  ignored for port %d\n",
120					MC_IP6_FMT((__be16 *) &mc_group->u.ip6), MC_IP6_FMT((__be16 *) &mc_source->u.ip6), mdb->flood_ifindex[i]);
121#endif
122			continue;
123		}
124
125		if (ifnum + 1 > max_dst) {
126			MC_PRINT("Multicast interfaces overflow %d/%d\n", mdb->flood_ifcnt, max_dst);
127			ifnum = -1;
128			break;
129		}
130
131		dst_dev[ifnum] = mdb->flood_ifindex[i];
132		ifnum++;
133
134	}
135	read_unlock(&mdb->rwlock);
136
137	return ifnum;
138
139}
140
141int mc_bridge_ipv4_get_if(struct net_device *brdev, __be32 origin, __be32 group,
142			 uint32_t max_dst, uint32_t dst_dev[])
143{
144	struct mc_ip mc_group;
145	struct mc_ip mc_source;
146
147	memset(&mc_group, 0, sizeof(struct mc_ip));
148	mc_group.u.ip4 = group;
149	mc_group.pro = htons(ETH_P_IP);
150
151	memset(&mc_source, 0, sizeof(struct mc_ip));
152	mc_source.u.ip4 = origin;
153	mc_source.pro = htons(ETH_P_IP);
154
155	return __mc_bridge_get_ifs(brdev, &mc_group, &mc_source, max_dst, dst_dev);
156}
157EXPORT_SYMBOL(mc_bridge_ipv4_get_if);
158
159int mc_bridge_ipv4_update_callback_register(mc_bridge_ipv4_update_callback_t snoop_event_cb)
160{
161	mc_bridge_ipv4_update_callback_t event_cb;
162
163	event_cb = rcu_dereference(mc_ipv4_event_cb);
164	if (event_cb && event_cb != snoop_event_cb) {
165		printk("MC callback function is using by another module\n");
166		return -1;
167	}
168
169	rcu_assign_pointer(mc_ipv4_event_cb, snoop_event_cb);
170	return 0;
171}
172EXPORT_SYMBOL(mc_bridge_ipv4_update_callback_register);
173
174int mc_bridge_ipv4_update_callback_deregister(void)
175{
176	rcu_assign_pointer(mc_ipv4_event_cb, NULL);
177	return 0;
178
179}
180EXPORT_SYMBOL(mc_bridge_ipv4_update_callback_deregister);
181
182mc_bridge_ipv4_update_callback_t mc_bridge_ipv4_update_callback_get(void)
183{
184	return rcu_dereference(mc_ipv4_event_cb);
185}
186
187#ifdef MC_SUPPORT_MLD
188int mc_bridge_ipv6_get_if(struct net_device *brdev, struct in6_addr *origin, struct in6_addr *group,
189						  uint32_t max_dst, uint32_t dst_dev[])
190{
191	struct mc_ip mc_group;
192	struct mc_ip mc_source;
193
194	memset(&mc_group, 0, sizeof(struct mc_ip));
195	mc_ipv6_addr_copy(&mc_group.u.ip6, group);
196	mc_group.pro = htons(ETH_P_IPV6);
197
198	memset(&mc_source, 0, sizeof(struct mc_ip));
199	mc_ipv6_addr_copy(&mc_source.u.ip6, origin);
200	mc_source.pro = htons(ETH_P_IPV6);
201
202	return __mc_bridge_get_ifs(brdev, &mc_group, &mc_source, max_dst, dst_dev);
203}
204EXPORT_SYMBOL(mc_bridge_ipv6_get_if);
205
206int mc_bridge_ipv6_update_callback_register(mc_bridge_ipv6_update_callback_t snoop_event_cb)
207{
208	mc_bridge_ipv6_update_callback_t event_cb;
209
210	event_cb = rcu_dereference(mc_ipv6_event_cb);
211	if (event_cb && event_cb != snoop_event_cb) {
212		printk("MC callback function is using by another module\n");
213		return -1;
214	}
215
216	rcu_assign_pointer(mc_ipv6_event_cb, snoop_event_cb);
217	return 0;
218}
219EXPORT_SYMBOL(mc_bridge_ipv6_update_callback_register);
220
221int mc_bridge_ipv6_update_callback_deregister(void)
222{
223	rcu_assign_pointer(mc_ipv6_event_cb, NULL);
224	return 0;
225}
226EXPORT_SYMBOL(mc_bridge_ipv6_update_callback_deregister);
227
228mc_bridge_ipv6_update_callback_t mc_bridge_ipv6_update_callback_get(void)
229{
230	return rcu_dereference(mc_ipv6_event_cb);
231}
232
233#endif
234
235#ifdef _MC_ECM_TEST
236void _test_mc_ipv4_callback(struct net_device *brdev, uint32_t group)
237{
238
239}
240
241void _test_mc_ipv6_callback(struct net_device *brdev, struct in6_addr *group)
242{
243
244}
245
246void _test_mc_get_ifs(void)
247{
248	struct net_device *brdev;
249	__be32 origin;
250	__be32 group;
251	struct in6_addr origin6;
252	struct in6_addr group6;
253	uint32_t max_dst = 16;
254	uint32_t dst_dev[16];
255
256	int ifnum;
257	int i;
258
259	brdev = dev_get_by_name(&init_net, "br-lan");
260	if (!brdev) {
261		printk("Can't get bridge\n");
262		return;
263	}
264
265	/*239.1.2.3*/
266	origin = 0;
267	group = htonl(0xef010203);
268
269	ifnum = mc_bridge_ipv4_get_if(brdev, origin, group, max_dst, dst_dev);
270	if (ifnum < 0)
271		printk("Failed to get IPv4 mc interfaces\n");
272	else {
273		printk("Got IPv4 %d mc interfaces\n", ifnum);
274		for (i = 0; i < ifnum; i++)
275			printk("%d ", dst_dev[i]);
276		printk("\n");
277	}
278
279	/*ff15::0001*/
280	ipv6_addr_set(&origin6, 0, 0, 0, 0);
281	ipv6_addr_set(&group6, htonl(0xff150000), 0, 0, htonl(1));
282
283	ifnum = mc_bridge_ipv6_get_if(brdev, &origin6, &group6, max_dst, dst_dev);
284	if (ifnum < 0)
285		printk("Failed to get IPv4 mc interfaces\n");
286	else {
287		printk("Got IPv6 %d mc interfaces\n", ifnum);
288		for (i = 0; i < ifnum; i++)
289			printk("%d ", dst_dev[i]);
290		printk("\n");
291	}
292
293	dev_put(brdev);
294}
295
296int _test_mc_ecm_init(void)
297{
298	printk("MC callback init\n");
299
300	if (mc_bridge_ipv4_update_callback_register(_test_mc_ipv4_callback) < 0)
301		printk("Failed to register IPv4 callbak\n");
302
303	if (mc_bridge_ipv6_update_callback_register(_test_mc_ipv6_callback) < 0)
304		printk("Failed to register IPv6 callbak\n");
305
306	return 0;
307}
308
309int _test_mc_ecm_deinit(void)
310{
311	printk("MC callback deinit\n");
312
313	if (mc_bridge_ipv4_update_callback_deregister() < 0)
314		printk("Failed to register IPv6 callbak\n");
315
316	if (mc_bridge_ipv6_update_callback_deregister() < 0)
317		printk("Failed to register IPv6 callbak\n");
318
319	return 0;
320}
321
322#endif
323