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