993 lines
30 KiB
C
993 lines
30 KiB
C
/*
|
|
* Copyright (c) 2012 Broadcom Corporation
|
|
* All Rights Reserved
|
|
*
|
|
<:label-BRCM:2012:DUAL/GPL:standard
|
|
|
|
Unless you and Broadcom execute a separate written software license
|
|
agreement governing use of this software, this software is licensed
|
|
to you under the terms of the GNU General Public License version 2
|
|
(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php,
|
|
with the following added to such license:
|
|
|
|
As a special exception, the copyright holders of this software give
|
|
you permission to link this software with independent modules, and
|
|
to copy and distribute the resulting executable under terms of your
|
|
choice, provided that you also meet, for each linked independent
|
|
module, the terms and conditions of the license of that module.
|
|
An independent module is a module which is not derived from this
|
|
software. The special exception does not apply to any modifications
|
|
of the software.
|
|
|
|
Not withstanding the above, under no circumstances may you combine
|
|
this software in any way with any other Broadcom software provided
|
|
under a license other than the GPL, without Broadcom's express prior
|
|
written consent.
|
|
|
|
:>
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/init.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/times.h>
|
|
#include <linux/netdevice.h>
|
|
#include <linux/etherdevice.h>
|
|
#include <linux/jhash.h>
|
|
#include <asm/atomic.h>
|
|
#include <linux/ip.h>
|
|
#include <linux/seq_file.h>
|
|
#include <linux/proc_fs.h>
|
|
#include <linux/list.h>
|
|
#if defined(CONFIG_MIPS_BRCM) && defined(CONFIG_BLOG)
|
|
#include <linux/if_vlan.h>
|
|
#include <linux/blog.h>
|
|
#include <linux/blog_rule.h>
|
|
#endif
|
|
#include <linux/rtnetlink.h>
|
|
#include "br_private.h"
|
|
#if defined(CONFIG_MIPS_BRCM) && defined(CONFIG_BR_IGMP_SNOOP)
|
|
#include "br_igmp.h"
|
|
#endif
|
|
#if defined(CONFIG_MIPS_BRCM) && defined(CONFIG_BR_MLD_SNOOP)
|
|
#include "br_mld.h"
|
|
#include <linux/module.h>
|
|
#endif
|
|
#include <linux/bcm_skb_defines.h>
|
|
|
|
#if defined(CONFIG_MIPS_BRCM) && (defined(CONFIG_BR_IGMP_SNOOP) || defined(CONFIG_BR_MLD_SNOOP))
|
|
static t_MCAST_CFG multiConfig = { -1 };
|
|
|
|
void br_mcast_set_pri_queue(int val)
|
|
{
|
|
multiConfig.mcastPriQueue = val;
|
|
}
|
|
|
|
int br_mcast_get_pri_queue(void)
|
|
{
|
|
return multiConfig.mcastPriQueue;
|
|
}
|
|
|
|
void br_mcast_set_skb_mark_queue(struct sk_buff *skb)
|
|
{
|
|
int isMulticast = 0;
|
|
const unsigned char *dest = eth_hdr(skb)->h_dest;
|
|
|
|
#if defined(CONFIG_BR_MLD_SNOOP)
|
|
if((BR_MLD_MULTICAST_MAC_PREFIX == dest[0]) &&
|
|
(BR_MLD_MULTICAST_MAC_PREFIX == dest[1])) {
|
|
isMulticast = 1;
|
|
}
|
|
#endif
|
|
|
|
#if defined(CONFIG_BR_IGMP_SNOOP)
|
|
if (is_multicast_ether_addr(dest)) {
|
|
isMulticast = 1;
|
|
}
|
|
#endif
|
|
|
|
if ( (isMulticast) && (multiConfig.mcastPriQueue != -1) )
|
|
{
|
|
skb->mark = SKBMARK_SET_Q(skb->mark, multiConfig.mcastPriQueue);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if defined(CONFIG_MIPS_BRCM) && defined(CONFIG_BLOG)
|
|
inline void br_mcast_ipv4_to_eth(unsigned long ipv4_addr,
|
|
unsigned char *mac_addr_p)
|
|
{
|
|
unsigned char *ipv4_addr8_p = (unsigned char *)(&ipv4_addr);
|
|
|
|
mac_addr_p[0] = 0x01;
|
|
mac_addr_p[1] = 0x00;
|
|
mac_addr_p[2] = 0x5E;
|
|
mac_addr_p[3] = ipv4_addr8_p[1] & 0x7F;
|
|
mac_addr_p[4] = ipv4_addr8_p[2];
|
|
mac_addr_p[5] = ipv4_addr8_p[3];
|
|
}
|
|
|
|
inline void br_mcast_ipv6_to_eth(unsigned char *ipv6_addr,
|
|
unsigned char *mac_addr_p)
|
|
{
|
|
mac_addr_p[0] = 0x33;
|
|
mac_addr_p[1] = 0x33;
|
|
mac_addr_p[2] = ipv6_addr[12];
|
|
mac_addr_p[3] = ipv6_addr[13];
|
|
mac_addr_p[4] = ipv6_addr[14];
|
|
mac_addr_p[5] = ipv6_addr[15];
|
|
}
|
|
|
|
void br_mcast_blog_release(t_BR_MCAST_PROTO_TYPE proto, void *mc_fdb)
|
|
{
|
|
Blog_t *blog_p = BLOG_NULL;
|
|
uint32_t blog_idx = BLOG_KEY_INVALID;
|
|
BlogTraffic_t traffic;
|
|
|
|
#if defined(CONFIG_MIPS_BRCM) && defined(CONFIG_BR_IGMP_SNOOP)
|
|
if(proto == BR_MCAST_PROTO_IGMP)
|
|
{
|
|
blog_idx = ((struct net_bridge_mc_fdb_entry *)mc_fdb)->blog_idx;
|
|
((struct net_bridge_mc_fdb_entry *)mc_fdb)->blog_idx = 0;
|
|
traffic = BlogTraffic_IPV4_MCAST;
|
|
}
|
|
#endif
|
|
#if defined(CONFIG_MIPS_BRCM) && defined(CONFIG_BR_MLD_SNOOP)
|
|
if(proto == BR_MCAST_PROTO_MLD)
|
|
{
|
|
blog_idx = ((struct net_br_mld_mc_fdb_entry *)mc_fdb)->blog_idx;
|
|
((struct net_br_mld_mc_fdb_entry *)mc_fdb)->blog_idx = 0;
|
|
traffic = BlogTraffic_IPV6_MCAST;
|
|
}
|
|
#endif
|
|
|
|
if(BLOG_KEY_INVALID == blog_idx)
|
|
return;
|
|
|
|
blog_p = blog_deactivate(blog_idx, traffic, BlogClient_fcache);
|
|
if ( blog_p )
|
|
{
|
|
blog_rule_free_list(blog_p);
|
|
blog_put(blog_p);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void br_mcast_blog_process_wan(blogRule_t *rule_p,
|
|
void *mc_fdb,
|
|
t_BR_MCAST_PROTO_TYPE proto,
|
|
struct net_device **wan_dev_pp,
|
|
struct net_device **wan_vlan_dev_pp)
|
|
{
|
|
blogRuleAction_t ruleAction;
|
|
struct net_device *dev_p = NULL;
|
|
struct net_bridge_mc_fdb_entry *igmp_fdb = NULL;
|
|
#if defined(CONFIG_MIPS_BRCM) && defined(CONFIG_BR_MLD_SNOOP)
|
|
struct net_br_mld_mc_fdb_entry *mld_fdb = NULL;
|
|
#endif
|
|
uint8_t *dev_addr = NULL;
|
|
uint32_t phyType;
|
|
char wan_ops;
|
|
|
|
if(!mc_fdb)
|
|
return;
|
|
|
|
#if defined(CONFIG_MIPS_BRCM) && defined(CONFIG_BR_MLD_SNOOP)
|
|
if(BR_MCAST_PROTO_MLD == proto)
|
|
{
|
|
mld_fdb = (struct net_br_mld_mc_fdb_entry *)mc_fdb;
|
|
dev_p = mld_fdb->from_dev;
|
|
dev_addr = mld_fdb->dst->dev->dev_addr;
|
|
wan_ops = mld_fdb->type;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
igmp_fdb = (struct net_bridge_mc_fdb_entry *)mc_fdb;
|
|
dev_p = igmp_fdb->from_dev;
|
|
dev_addr = igmp_fdb->dst->dev->dev_addr;
|
|
wan_ops = igmp_fdb->type;
|
|
}
|
|
|
|
while(1)
|
|
{
|
|
if(netdev_path_is_root(dev_p))
|
|
{
|
|
*wan_dev_pp = dev_p;
|
|
break;
|
|
}
|
|
|
|
if(dev_p->priv_flags & IFF_PPP)
|
|
{
|
|
rule_p->filter.hasPppoeHeader = 1;
|
|
memset(&ruleAction, 0, sizeof(blogRuleAction_t));
|
|
ruleAction.cmd = BLOG_RULE_CMD_POP_PPPOE_HDR;
|
|
blog_rule_add_action(rule_p, &ruleAction);
|
|
|
|
memset(&ruleAction, 0, sizeof(blogRuleAction_t));
|
|
ruleAction.cmd = BLOG_RULE_CMD_SET_MAC_DA;
|
|
if(igmp_fdb)
|
|
br_mcast_ipv4_to_eth(igmp_fdb->grp.s_addr, ruleAction.macAddr);
|
|
#if defined(CONFIG_MIPS_BRCM) && defined(CONFIG_BR_MLD_SNOOP)
|
|
else
|
|
br_mcast_ipv6_to_eth(mld_fdb->grp.s6_addr, ruleAction.macAddr);
|
|
#endif
|
|
blog_rule_add_action(rule_p, &ruleAction);
|
|
}
|
|
else if(*wan_vlan_dev_pp == NULL &&
|
|
dev_p->priv_flags & IFF_BCM_VLAN)
|
|
{
|
|
*wan_vlan_dev_pp = dev_p;
|
|
}
|
|
dev_p = netdev_path_next_dev(dev_p);
|
|
}
|
|
|
|
/* For IPoA */
|
|
phyType = netdev_path_get_hw_port_type(*wan_dev_pp);
|
|
phyType = BLOG_GET_HW_ACT(phyType);
|
|
if((phyType == VC_MUX_IPOA) || (phyType == LLC_SNAP_ROUTE_IP))
|
|
{
|
|
memset(&ruleAction, 0, sizeof(blogRuleAction_t));
|
|
ruleAction.cmd = BLOG_RULE_CMD_SET_MAC_DA;
|
|
if(igmp_fdb)
|
|
br_mcast_ipv4_to_eth(igmp_fdb->grp.s_addr, ruleAction.macAddr);
|
|
#if defined(CONFIG_MIPS_BRCM) && defined(CONFIG_BR_MLD_SNOOP)
|
|
else
|
|
br_mcast_ipv6_to_eth(mld_fdb->grp.s6_addr, ruleAction.macAddr);
|
|
#endif
|
|
blog_rule_add_action(rule_p, &ruleAction);
|
|
}
|
|
|
|
if(wan_ops == MCPD_IF_TYPE_ROUTED)
|
|
{
|
|
memset(&ruleAction, 0, sizeof(blogRuleAction_t));
|
|
ruleAction.cmd = BLOG_RULE_CMD_SET_MAC_SA;
|
|
memcpy(ruleAction.macAddr, dev_addr, ETH_ALEN);
|
|
blog_rule_add_action(rule_p, &ruleAction);
|
|
|
|
memset(&ruleAction, 0, sizeof(blogRuleAction_t));
|
|
ruleAction.cmd = BLOG_RULE_CMD_DECR_TTL;
|
|
blog_rule_add_action(rule_p, &ruleAction);
|
|
}
|
|
}
|
|
|
|
static void br_mcast_blog_process_lan(void *mc_fdb,
|
|
t_BR_MCAST_PROTO_TYPE proto,
|
|
struct net_device **lan_dev_pp,
|
|
struct net_device **lan_vlan_dev_pp)
|
|
{
|
|
struct net_device *dev_p = NULL;
|
|
struct net_bridge_mc_fdb_entry *igmp_fdb = NULL;
|
|
#if defined(CONFIG_MIPS_BRCM) && defined(CONFIG_BR_MLD_SNOOP)
|
|
struct net_br_mld_mc_fdb_entry *mld_fdb = NULL;
|
|
#endif
|
|
|
|
if(!mc_fdb)
|
|
return;
|
|
|
|
#if defined(CONFIG_MIPS_BRCM) && defined(CONFIG_BR_MLD_SNOOP)
|
|
if (BR_MCAST_PROTO_MLD == proto )
|
|
{
|
|
mld_fdb = (struct net_br_mld_mc_fdb_entry *)mc_fdb;
|
|
dev_p = mld_fdb->dst->dev;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
igmp_fdb = (struct net_bridge_mc_fdb_entry *)mc_fdb;
|
|
dev_p = igmp_fdb->dst->dev;
|
|
}
|
|
|
|
while(1)
|
|
{
|
|
if(netdev_path_is_root(dev_p))
|
|
{
|
|
*lan_dev_pp = dev_p;
|
|
break;
|
|
}
|
|
|
|
if(*lan_vlan_dev_pp == NULL &&
|
|
dev_p->priv_flags & IFF_BCM_VLAN)
|
|
{
|
|
*lan_vlan_dev_pp = dev_p;
|
|
}
|
|
|
|
dev_p = netdev_path_next_dev(dev_p);
|
|
}
|
|
}
|
|
|
|
static void br_mcast_mc_fdb_update_bydev(t_BR_MCAST_PROTO_TYPE proto,
|
|
struct net_bridge *br,
|
|
struct net_device *dev)
|
|
{
|
|
if(!br || !dev)
|
|
return;
|
|
|
|
if(BR_MCAST_PROTO_IGMP == proto)
|
|
br_igmp_mc_fdb_update_bydev(br, dev);
|
|
#if defined(CONFIG_MIPS_BRCM) && defined(CONFIG_BR_MLD_SNOOP)
|
|
else if(BR_MCAST_PROTO_MLD == proto)
|
|
br_mld_mc_fdb_update_bydev(br, dev);
|
|
#endif
|
|
|
|
return;
|
|
}
|
|
|
|
void br_mcast_vlan_notify_for_blog_update(struct net_device *ndev,
|
|
blogRuleVlanNotifyDirection_t direction,
|
|
uint32_t nbrOfTags)
|
|
{
|
|
struct net_bridge *br = NULL;
|
|
struct net_device *dev = NULL;
|
|
|
|
if((ndev->priv_flags & IFF_WANDEV) && (direction == DIR_TX))
|
|
return;
|
|
|
|
read_lock(&dev_base_lock);
|
|
for(dev = first_net_device(&init_net);
|
|
dev;
|
|
dev = next_net_device(dev))
|
|
{
|
|
br = netdev_priv(dev);
|
|
if((dev->priv_flags & IFF_EBRIDGE) && (br))
|
|
{
|
|
/* snooping entries could be present even if snooping is
|
|
disabled, update existing entries */
|
|
#if defined(CONFIG_MIPS_BRCM) && defined(CONFIG_BR_IGMP_SNOOP)
|
|
spin_lock_bh(&br->lock);
|
|
br_mcast_mc_fdb_update_bydev(BR_MCAST_PROTO_IGMP, br, ndev);
|
|
spin_unlock_bh(&br->lock);
|
|
#endif
|
|
#if defined(CONFIG_MIPS_BRCM) && defined(CONFIG_BR_MLD_SNOOP)
|
|
spin_lock_bh(&br->lock);
|
|
br_mcast_mc_fdb_update_bydev(BR_MCAST_PROTO_MLD, br, ndev);
|
|
spin_unlock_bh(&br->lock);
|
|
#endif /* CONFIG_BR_MLD_SNOOP */
|
|
}
|
|
}
|
|
read_unlock(&dev_base_lock);
|
|
|
|
return;
|
|
}
|
|
|
|
void br_mcast_handle_netdevice_events(struct net_device *ndev, unsigned long event)
|
|
{
|
|
struct net_bridge *br = NULL;
|
|
struct net_device *dev = NULL;
|
|
int i;
|
|
|
|
switch (event) {
|
|
case NETDEV_GOING_DOWN:
|
|
case NETDEV_CHANGE:
|
|
for(dev = first_net_device(&init_net);
|
|
dev;
|
|
dev = next_net_device(dev)) {
|
|
br = netdev_priv(dev);
|
|
if((dev->priv_flags & IFF_EBRIDGE) && (br)) {
|
|
#if defined(CONFIG_MIPS_BRCM) && defined(CONFIG_BR_MLD_SNOOP)
|
|
struct net_br_mld_mc_fdb_entry *mld_dst;
|
|
struct net_br_mld_mc_rep_entry *mldrep, *mldrep_n;
|
|
#endif
|
|
#if defined(CONFIG_MIPS_BRCM) && defined(CONFIG_BR_IGMP_SNOOP)
|
|
/* snooping entries could be present even if snooping is
|
|
disabled, update existing entries */
|
|
struct net_bridge_mc_fdb_entry *dst;
|
|
struct net_bridge_mc_rep_entry *rep_entry, *rep_entry_n;
|
|
spin_lock_bh(&br->lock);
|
|
spin_lock_bh(&br->mcl_lock);
|
|
for (i = 0; i < BR_IGMP_HASH_SIZE; i++)
|
|
{
|
|
struct hlist_node *h, *n;
|
|
hlist_for_each_entry_safe(dst, h, n, &br->mc_hash[i], hlist)
|
|
{
|
|
if((!memcmp(ndev->name, dst->wan_name, IFNAMSIZ)) ||
|
|
(!memcmp(ndev->name, dst->lan_name, IFNAMSIZ)) ||
|
|
(!memcmp(ndev->name, dev->name, IFNAMSIZ))) {
|
|
if ( br->igmp_snooping )
|
|
{
|
|
mcpd_nl_send_igmp_purge_entry(dst);
|
|
}
|
|
list_for_each_entry_safe(rep_entry,
|
|
rep_entry_n, &dst->rep_list, list) {
|
|
list_del(&rep_entry->list);
|
|
br_igmp_mc_rep_free(rep_entry);
|
|
}
|
|
hlist_del(&dst->hlist);
|
|
br_mcast_blog_release(BR_MCAST_PROTO_IGMP, dst);
|
|
br_igmp_mc_fdb_free(dst);
|
|
}
|
|
}
|
|
}
|
|
spin_unlock_bh(&br->mcl_lock);
|
|
spin_unlock_bh(&br->lock);
|
|
#endif
|
|
#if defined(CONFIG_MIPS_BRCM) && defined(CONFIG_BR_MLD_SNOOP)
|
|
/* snooping entries could be present even if snooping is
|
|
disabled, update existing entries */
|
|
spin_lock_bh(&br->lock);
|
|
spin_lock_bh(&br->mld_mcl_lock);
|
|
for (i = 0; i < BR_MLD_HASH_SIZE; i++)
|
|
{
|
|
struct hlist_node *h, *n;
|
|
hlist_for_each_entry_safe(mld_dst, h, n, &br->mld_mc_hash[i], hlist)
|
|
{
|
|
if((!memcmp(ndev->name, mld_dst->wan_name, IFNAMSIZ)) ||
|
|
(!memcmp(ndev->name, mld_dst->lan_name, IFNAMSIZ)) ||
|
|
(!memcmp(ndev->name, dev->name, IFNAMSIZ))) {
|
|
list_for_each_entry_safe(mldrep,
|
|
mldrep_n, &mld_dst->rep_list, list) {
|
|
list_del(&mldrep->list);
|
|
br_mld_mc_rep_free(mldrep);
|
|
}
|
|
hlist_del(&mld_dst->hlist);
|
|
br_mld_wl_del_entry(br,mld_dst);
|
|
br_mcast_blog_release(BR_MCAST_PROTO_MLD, mld_dst);
|
|
br_mld_mc_fdb_free(mld_dst);
|
|
}
|
|
}
|
|
}
|
|
spin_unlock_bh(&br->mld_mcl_lock);
|
|
spin_unlock_bh(&br->lock);
|
|
#endif
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void *br_mcast_mc_fdb_copy(t_BR_MCAST_PROTO_TYPE proto,
|
|
struct net_bridge *br,
|
|
const void *mc_fdb)
|
|
{
|
|
if(!mc_fdb)
|
|
return NULL;
|
|
|
|
if(BR_MCAST_PROTO_IGMP == proto)
|
|
return br_igmp_mc_fdb_copy(br, (struct net_bridge_mc_fdb_entry *)mc_fdb);
|
|
#if defined(CONFIG_MIPS_BRCM) && defined(CONFIG_BR_MLD_SNOOP)
|
|
else if(BR_MCAST_PROTO_MLD == proto)
|
|
return br_mld_mc_fdb_copy(br, (struct net_br_mld_mc_fdb_entry *)mc_fdb);
|
|
#endif
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void br_mcast_mc_fdb_del_entry(t_BR_MCAST_PROTO_TYPE proto,
|
|
struct net_bridge *br,
|
|
void *mc_fdb)
|
|
{
|
|
|
|
if(!mc_fdb)
|
|
return;
|
|
|
|
if(BR_MCAST_PROTO_IGMP == proto)
|
|
br_igmp_mc_fdb_del_entry(br, (struct net_bridge_mc_fdb_entry *)mc_fdb);
|
|
#if defined(CONFIG_MIPS_BRCM) && defined(CONFIG_BR_MLD_SNOOP)
|
|
else if(BR_MCAST_PROTO_MLD == proto)
|
|
br_mld_mc_fdb_del_entry(br, (struct net_br_mld_mc_fdb_entry *)mc_fdb);
|
|
#endif
|
|
|
|
return;
|
|
} /* br_mcast_mc_fdb_del_entry */
|
|
|
|
static void br_mcast_blog_link_devices(Blog_t *blog_p, struct net_device *rxDev,
|
|
struct net_device *txDev, int wanType )
|
|
{
|
|
struct net_device *dev_p;
|
|
uint32_t delta;
|
|
struct net_device *rxPath[MAX_VIRT_DEV];
|
|
int rxPathIdx = 0;
|
|
int i;
|
|
|
|
/* save rx path required for reverse path traversal for delta calc */
|
|
memset(&rxPath[0], 0, (MAX_VIRT_DEV * sizeof(struct net_device *)));
|
|
dev_p = rxDev;
|
|
while(1)
|
|
{
|
|
if(netdev_path_is_root(dev_p))
|
|
{
|
|
break;
|
|
}
|
|
rxPath[rxPathIdx] = dev_p;
|
|
rxPathIdx++;
|
|
dev_p = netdev_path_next_dev(dev_p);
|
|
}
|
|
|
|
/* omit Ethernet header from virtual dev RX stats */
|
|
delta = BLOG_ETH_HDR_LEN;
|
|
|
|
for(i = (MAX_VIRT_DEV-1); i >= 0; i--)
|
|
{
|
|
if(NULL == rxPath[i])
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if ( rxPath[i]->priv_flags & IFF_PPP )
|
|
{
|
|
delta += BLOG_PPPOE_HDR_LEN;
|
|
}
|
|
|
|
if ( rxPath[i]->priv_flags & IFF_802_1Q_VLAN )
|
|
{
|
|
delta += BLOG_VLAN_HDR_LEN;
|
|
}
|
|
|
|
if ( (rxPath[i]->priv_flags & IFF_BCM_VLAN) &&
|
|
(blog_p->vtag_num > 0) )
|
|
{
|
|
delta += BLOG_VLAN_HDR_LEN;
|
|
}
|
|
|
|
blog_link(IF_DEVICE_MCAST, blog_p, rxPath[i], DIR_RX, delta);
|
|
dev_p = netdev_path_next_dev(dev_p);
|
|
}
|
|
|
|
/* include Ethernet header in virtual TX stats */
|
|
delta -= BLOG_ETH_HDR_LEN;
|
|
|
|
if ( (wanType == MCPD_IF_TYPE_ROUTED) && (txDev->br_port) )
|
|
{
|
|
/* routed packets will come through br_dev_xmit, link bridge
|
|
device with blog */
|
|
blog_link(IF_DEVICE_MCAST, blog_p, txDev->br_port->br->dev,
|
|
DIR_TX, delta );
|
|
}
|
|
|
|
dev_p = txDev;
|
|
while(1)
|
|
{
|
|
if(netdev_path_is_root(dev_p))
|
|
{
|
|
break;
|
|
}
|
|
|
|
if ( dev_p->priv_flags & IFF_802_1Q_VLAN )
|
|
{
|
|
delta -= BLOG_VLAN_HDR_LEN;
|
|
}
|
|
|
|
if ( dev_p->priv_flags & IFF_BCM_VLAN )
|
|
{
|
|
delta -= BLOG_VLAN_HDR_LEN;
|
|
}
|
|
|
|
blog_link(IF_DEVICE_MCAST, blog_p, dev_p, DIR_TX, delta);
|
|
dev_p = netdev_path_next_dev(dev_p);
|
|
}
|
|
}
|
|
|
|
|
|
static int br_mcast_vlan_process(struct net_bridge *br,
|
|
void *mc_fdb,
|
|
t_BR_MCAST_PROTO_TYPE proto,
|
|
Blog_t *blog_p)
|
|
{
|
|
Blog_t *new_blog_p;
|
|
void *new_mc_fdb = NULL;
|
|
blogRule_t *rule_p = NULL;
|
|
int firstRule = 1;
|
|
uint32_t vid = 0;
|
|
blogRuleFilter_t *rule_filter = NULL;
|
|
BlogTraffic_t traffic;
|
|
int activates = 0;
|
|
void *rxDev;
|
|
void *txDev;
|
|
int wanType;
|
|
|
|
if(!mc_fdb || !blog_p || !br)
|
|
return 0;
|
|
|
|
if(!((BR_MCAST_PROTO_IGMP == proto) || (BR_MCAST_PROTO_MLD == proto)))
|
|
return 0;
|
|
|
|
firstRule = 1;
|
|
rule_p = (blogRule_t *)blog_p->blogRule_p;
|
|
while( rule_p )
|
|
{
|
|
blogRuleFilter_t *filter_p;
|
|
|
|
filter_p = &rule_p->filter;
|
|
/* if there is a rule that specifies a protocol filter that does not match
|
|
blog key protocol skip it */
|
|
if(blog_rule_filterInUse(filter_p->ipv4.mask.ip_proto))
|
|
{
|
|
if(filter_p->ipv4.mask.ip_proto & BLOG_RULE_IP_PROTO_MASK)
|
|
{
|
|
uint8_t proto;
|
|
|
|
proto = filter_p->ipv4.value.ip_proto >> BLOG_RULE_IP_PROTO_SHIFT;
|
|
if (proto != blog_p->key.protocol)
|
|
{
|
|
/* skip this rule */
|
|
blog_p->blogRule_p = rule_p->next_p;
|
|
blog_rule_free(rule_p);
|
|
rule_p = blog_p->blogRule_p;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if defined(CONFIG_MIPS_BRCM) && defined(CONFIG_BR_MLD_SNOOP)
|
|
if(blog_rule_filterInUse(filter_p->ipv6.mask.nxtHdr))
|
|
{
|
|
if(filter_p->ipv6.mask.nxtHdr & BLOG_RULE_IP6_NXT_HDR_MASK)
|
|
{
|
|
uint8_t nxtHdr;
|
|
|
|
nxtHdr = filter_p->ipv6.value.nxtHdr >> BLOG_RULE_IP6_NXT_HDR_SHIFT;
|
|
if (nxtHdr != blog_p->key.protocol)
|
|
{
|
|
/* skip this rule */
|
|
blog_p->blogRule_p = rule_p->next_p;
|
|
blog_rule_free(rule_p);
|
|
rule_p = blog_p->blogRule_p;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* create new fdb entry unless this is the first rule. For the
|
|
first rule use the fdb entry that was passed in */
|
|
if ( 1 == firstRule )
|
|
{
|
|
firstRule = 0;
|
|
new_mc_fdb = mc_fdb;
|
|
}
|
|
else
|
|
{
|
|
new_mc_fdb = br_mcast_mc_fdb_copy(proto, br , mc_fdb);
|
|
if(!new_mc_fdb)
|
|
{
|
|
printk(KERN_WARNING "%s new_mc_fdb allocation failed\n",__FUNCTION__);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* get a new blog and copy original blog */
|
|
new_blog_p = blog_get();
|
|
if(new_blog_p == BLOG_NULL) {
|
|
br_mcast_mc_fdb_del_entry(proto, br, new_mc_fdb);
|
|
break;
|
|
}
|
|
blog_copy(new_blog_p, blog_p);
|
|
|
|
/* pop the rule off the original blog now that a new fdb and blog have been
|
|
allocated. This is to ensure that all rules are freed in case of error */
|
|
blog_p->blogRule_p = rule_p->next_p;
|
|
rule_p->next_p = NULL;
|
|
new_blog_p->blogRule_p = rule_p;
|
|
|
|
rule_filter = &(((blogRule_t *)new_blog_p->blogRule_p)->filter);
|
|
new_blog_p->vtag_num = rule_filter->nbrOfVlanTags;
|
|
vid = ((rule_filter->vlan[0].value.h_vlan_TCI &
|
|
rule_filter->vlan[0].mask.h_vlan_TCI) & 0xFFF);
|
|
new_blog_p->vid = vid ? vid : 0xFFFF;
|
|
vid = ((rule_filter->vlan[1].value.h_vlan_TCI &
|
|
rule_filter->vlan[1].mask.h_vlan_TCI) & 0xFFF);
|
|
new_blog_p->vid |= vid ? (vid << 16) : 0xFFFF0000;
|
|
|
|
blog_link(MCAST_FDB, new_blog_p, (void *)new_mc_fdb, 0, 0);
|
|
|
|
#if defined(CONFIG_MIPS_BRCM) && defined(CONFIG_BR_MLD_SNOOP)
|
|
if(BR_MCAST_PROTO_MLD == proto)
|
|
{
|
|
traffic = BlogTraffic_IPV6_MCAST;
|
|
((struct net_br_mld_mc_fdb_entry *)new_mc_fdb)->wan_tci = new_blog_p->vid;
|
|
((struct net_br_mld_mc_fdb_entry *)new_mc_fdb)->num_tags = new_blog_p->vtag_num;
|
|
rxDev = ((struct net_br_mld_mc_fdb_entry *)new_mc_fdb)->from_dev;
|
|
txDev = ((struct net_br_mld_mc_fdb_entry *)new_mc_fdb)->dst->dev;
|
|
wanType = ((struct net_br_mld_mc_fdb_entry *)new_mc_fdb)->type;
|
|
}
|
|
else//IGMP
|
|
#endif
|
|
{
|
|
traffic = BlogTraffic_IPV4_MCAST;
|
|
((struct net_bridge_mc_fdb_entry *)new_mc_fdb)->wan_tci = new_blog_p->vid;
|
|
((struct net_bridge_mc_fdb_entry *)new_mc_fdb)->num_tags = new_blog_p->vtag_num;
|
|
rxDev = ((struct net_bridge_mc_fdb_entry *)new_mc_fdb)->from_dev;
|
|
txDev = ((struct net_bridge_mc_fdb_entry *)new_mc_fdb)->dst->dev;
|
|
wanType = ((struct net_bridge_mc_fdb_entry *)new_mc_fdb)->type;
|
|
}
|
|
|
|
br_mcast_blog_link_devices(new_blog_p, rxDev, txDev, wanType);
|
|
|
|
if ( blog_activate(new_blog_p, traffic, BlogClient_fcache) == BLOG_KEY_INVALID )
|
|
{
|
|
blog_rule_free_list(new_blog_p);
|
|
blog_put(new_blog_p);
|
|
if ( new_mc_fdb != mc_fdb )
|
|
{
|
|
br_mcast_mc_fdb_del_entry(proto, br, new_mc_fdb);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
activates++;
|
|
}
|
|
|
|
/* advance to the next rule */
|
|
rule_p = blog_p->blogRule_p;
|
|
}
|
|
|
|
/* Free blog. The blog will only have rules if there was an error */
|
|
blog_rule_free_list(blog_p);
|
|
blog_put(blog_p);
|
|
|
|
return activates;
|
|
} /* br_mcast_vlan_process */
|
|
|
|
|
|
int br_mcast_blog_process(struct net_bridge *br,
|
|
void *mc_fdb,
|
|
t_BR_MCAST_PROTO_TYPE proto)
|
|
{
|
|
Blog_t *blog_p = BLOG_NULL;
|
|
blogRule_t *rule_p = NULL;
|
|
struct net_device *wan_vlan_dev_p = NULL;
|
|
struct net_device *lan_vlan_dev_p = NULL;
|
|
struct net_device *wan_dev_p = NULL;
|
|
struct net_device *lan_dev_p = NULL;
|
|
struct net_bridge_mc_fdb_entry *igmp_fdb = NULL;
|
|
#if defined(CONFIG_MIPS_BRCM) && defined(CONFIG_BR_MLD_SNOOP)
|
|
struct net_br_mld_mc_fdb_entry *mld_fdb = NULL;
|
|
#endif
|
|
struct net_device *from_dev = NULL;
|
|
uint32_t phyType;
|
|
int numActivates;
|
|
|
|
if(!mc_fdb)
|
|
return -1;
|
|
|
|
#if defined(CONFIG_MIPS_BRCM) && defined(CONFIG_BR_MLD_SNOOP)
|
|
if(BR_MCAST_PROTO_MLD == proto)
|
|
{
|
|
mld_fdb = (struct net_br_mld_mc_fdb_entry *)mc_fdb;
|
|
from_dev = mld_fdb->from_dev;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
igmp_fdb = (struct net_bridge_mc_fdb_entry *)mc_fdb;
|
|
from_dev = igmp_fdb->from_dev;
|
|
}
|
|
|
|
/* allocate blog */
|
|
blog_p = blog_get();
|
|
if(blog_p == BLOG_NULL) {
|
|
printk(KERN_WARNING "%s blog_p allocation failed\n",__FUNCTION__);
|
|
return -1;
|
|
}
|
|
|
|
/* allocate blog rule */
|
|
rule_p = blog_rule_alloc();
|
|
if(rule_p == NULL)
|
|
{
|
|
printk(KERN_WARNING "%s blog_rule allocation failed\n",__FUNCTION__);
|
|
blog_put(blog_p);
|
|
return -1;
|
|
}
|
|
|
|
blog_rule_init(rule_p);
|
|
blog_p->blogRule_p = (void *)rule_p;
|
|
|
|
/* for LAN2LAN don't do anything */
|
|
if(br->dev != from_dev)
|
|
{
|
|
/* find WAN devices */
|
|
br_mcast_blog_process_wan(rule_p, mc_fdb, proto,
|
|
&wan_dev_p, &wan_vlan_dev_p);
|
|
}
|
|
|
|
/* find LAN devices */
|
|
br_mcast_blog_process_lan(mc_fdb, proto, &lan_dev_p, &lan_vlan_dev_p);
|
|
|
|
/* for LAN2LAN don't do anything */
|
|
if(br->dev == from_dev)
|
|
{
|
|
blog_p->rx.info.phyHdr = 0;
|
|
blog_p->rx.info.channel = 0xFF; /* for lan2lan mcast */
|
|
blog_p->rx.info.bmap.BCM_SWC = 1;
|
|
}
|
|
else
|
|
{
|
|
phyType = netdev_path_get_hw_port_type(wan_dev_p);
|
|
blog_p->rx.info.phyHdr = (phyType & BLOG_PHYHDR_MASK);
|
|
phyType = BLOG_GET_HW_ACT(phyType);
|
|
|
|
if(blog_p->rx.info.phyHdrType == BLOG_GPONPHY)
|
|
{
|
|
unsigned int hw_subport_mcast_idx;
|
|
|
|
hw_subport_mcast_idx = netdev_path_get_hw_subport_mcast_idx(wan_dev_p);
|
|
|
|
if(hw_subport_mcast_idx < CONFIG_BCM_MAX_GEM_PORTS)
|
|
{
|
|
blog_p->rx.info.channel = hw_subport_mcast_idx;
|
|
}
|
|
else
|
|
{
|
|
/* Not a GPON Multicast WAN device */
|
|
blog_rule_free_list(blog_p);
|
|
blog_put(blog_p);
|
|
return -1;
|
|
}
|
|
}
|
|
else /* Ethernet or DSL WAN device */
|
|
{
|
|
blog_p->rx.info.channel = netdev_path_get_hw_port(wan_dev_p);
|
|
}
|
|
|
|
if( (blog_p->rx.info.phyHdrType == BLOG_XTMPHY) &&
|
|
((phyType == VC_MUX_PPPOA) ||
|
|
(phyType == VC_MUX_IPOA) ||
|
|
(phyType == LLC_SNAP_ROUTE_IP) ||
|
|
(phyType == LLC_ENCAPS_PPP)) )
|
|
{
|
|
blog_p->insert_eth = 1;
|
|
}
|
|
|
|
if( (blog_p->rx.info.phyHdrType == BLOG_XTMPHY) &&
|
|
((phyType == VC_MUX_PPPOA) ||
|
|
(phyType == LLC_ENCAPS_PPP)) )
|
|
{
|
|
blog_p->pop_pppoa = 1;
|
|
}
|
|
|
|
#if defined(CONFIG_BCM96816) || defined(CONFIG_BCM96818)
|
|
blog_p->rx.info.bmap.BCM_SWC = 1;
|
|
#else
|
|
if(blog_p->rx.info.phyHdrType == BLOG_ENETPHY)
|
|
{
|
|
blog_p->rx.info.bmap.BCM_SWC = 1;
|
|
}
|
|
else
|
|
{
|
|
blog_p->rx.info.bmap.BCM_XPHY = 1;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
blog_p->tx.info.bmap.BCM_SWC = 1;
|
|
|
|
blog_p->key.l1_tuple.phy = blog_p->rx.info.phyHdr;
|
|
blog_p->key.l1_tuple.channel = blog_p->rx.info.channel;
|
|
blog_p->key.protocol = BLOG_IPPROTO_UDP;
|
|
|
|
phyType = netdev_path_get_hw_port_type(lan_dev_p);
|
|
blog_p->tx.info.phyHdr = (phyType & BLOG_PHYHDR_MASK);
|
|
blog_p->tx.info.channel = netdev_path_get_hw_port(lan_dev_p);
|
|
|
|
#if defined(CONFIG_MIPS_BRCM) && defined(CONFIG_BR_MLD_SNOOP)
|
|
if(BR_MCAST_PROTO_MLD == proto)
|
|
{
|
|
BCM_IN6_ASSIGN_ADDR(&blog_p->tupleV6.saddr, &mld_fdb->src_entry.src);
|
|
BCM_IN6_ASSIGN_ADDR(&blog_p->tupleV6.daddr, &mld_fdb->grp);
|
|
blog_p->rx.info.bmap.L3_IPv6 = 1;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
blog_p->rx.tuple.saddr = igmp_fdb->src_entry.src.s_addr;
|
|
blog_p->rx.tuple.daddr = igmp_fdb->grp.s_addr;
|
|
blog_p->tx.tuple.saddr = igmp_fdb->src_entry.src.s_addr;
|
|
blog_p->tx.tuple.daddr = igmp_fdb->grp.s_addr;
|
|
blog_p->rx.info.bmap.L3_IPv4 = 1;
|
|
}
|
|
|
|
blog_p->rx.dev_p = wan_dev_p;
|
|
blog_p->rx.info.multicast = 1;
|
|
blog_p->tx.dev_p = lan_dev_p;
|
|
|
|
if ( multiConfig.mcastPriQueue != -1 )
|
|
{
|
|
blog_p->mark = SKBMARK_SET_Q(blog_p->mark, multiConfig.mcastPriQueue);
|
|
}
|
|
|
|
/* add vlan blog rules, if any vlan interfaces were found */
|
|
if(blogRuleVlanHook && (wan_vlan_dev_p || lan_vlan_dev_p)) {
|
|
if(blogRuleVlanHook(blog_p, wan_vlan_dev_p, lan_vlan_dev_p) < 0) {
|
|
printk(KERN_WARNING "Error while processing VLAN blog rules\n");
|
|
blog_rule_free_list(blog_p);
|
|
blog_put(blog_p);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* blog must have at least one rule */
|
|
if (NULL == blog_p->blogRule_p)
|
|
{
|
|
/* blogRule_p == NULL may be valid if there are no
|
|
VLAN rules and the default behavior for either interface is DROP */
|
|
//printk(KERN_WARNING "Error while processing VLAN blog rules\n");
|
|
blog_put(blog_p);
|
|
return -1;
|
|
}
|
|
|
|
numActivates = br_mcast_vlan_process(br, mc_fdb, proto, blog_p);
|
|
if ( 0 == numActivates )
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
} /* br_mcast_blog_process */
|
|
#endif
|
|
|
|
#if defined(CONFIG_MIPS_BRCM) && defined(CONFIG_BR_MLD_SNOOP)
|
|
struct net_device *br_get_device_by_index(char *brname,char index) {
|
|
struct net_bridge *br = NULL;
|
|
struct net_bridge_port *from_br = NULL;
|
|
struct net_device *dev = dev_get_by_name(&init_net,brname);
|
|
if(!dev)
|
|
return NULL;
|
|
br = netdev_priv(dev);
|
|
if(!br)
|
|
{
|
|
dev_put(dev);
|
|
return NULL;
|
|
}
|
|
rcu_read_lock();
|
|
from_br = br_get_port(br, index);
|
|
rcu_read_unlock();
|
|
if(!from_br) return NULL;
|
|
else return from_br->dev;
|
|
}
|
|
|
|
static int br_get_port_id_by_name(struct net_bridge *br,char *name) {
|
|
struct net_bridge_port *p;
|
|
list_for_each_entry_rcu(p, &br->port_list, list) {
|
|
//printk("p-dev->name:%s, devic name:%s,port_id:%d\n",p->dev->name,name,p->port_no);
|
|
if(!strcmp(name,p->dev->name))
|
|
return (int)p->port_no;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static RAW_NOTIFIER_HEAD(mcast_snooping_chain);
|
|
|
|
int register_mcast_snooping_notifier(struct notifier_block *nb) {
|
|
return raw_notifier_chain_register(&mcast_snooping_chain,nb);
|
|
}
|
|
|
|
int unregister_mcast_snooping_notifier(struct notifier_block *nb) {
|
|
return raw_notifier_chain_unregister(&mcast_snooping_chain,nb);
|
|
}
|
|
|
|
int mcast_snooping_call_chain(unsigned long val,void *v)
|
|
{
|
|
return raw_notifier_call_chain(&mcast_snooping_chain,val,v);
|
|
}
|
|
|
|
|
|
void br_mcast_wl_flush(struct net_bridge *br) {
|
|
t_MCPD_MLD_SNOOP_ENTRY snoopEntry;
|
|
struct net_bridge_port *p;
|
|
list_for_each_entry_rcu(p, &br->port_list, list) {
|
|
if(!strncmp(p->dev->name,"wl",2)){
|
|
snoopEntry.port_no= p->port_no;
|
|
memcpy(snoopEntry.br_name,br->dev->name,IFNAMSIZ);
|
|
mcast_snooping_call_chain(SNOOPING_FLUSH_ENTRY_ALL,(void *)&snoopEntry);
|
|
}
|
|
}
|
|
}
|
|
|
|
void br_mld_wl_del_entry(struct net_bridge *br,struct net_br_mld_mc_fdb_entry *dst) {
|
|
if(dst && (!strncmp(dst->lan_name,"wl",2))) {
|
|
t_MCPD_MLD_SNOOP_ENTRY snoopEntry;
|
|
snoopEntry.port_no=(char)br_get_port_id_by_name(br,dst->lan_name);
|
|
memcpy(snoopEntry.br_name,br->dev->name,IFNAMSIZ);
|
|
memcpy(&snoopEntry.grp,&dst->grp,sizeof(struct in6_addr));
|
|
mcast_snooping_call_chain(SNOOPING_FLUSH_ENTRY,(void *)&snoopEntry);
|
|
}
|
|
|
|
}
|
|
EXPORT_SYMBOL(unregister_mcast_snooping_notifier);
|
|
EXPORT_SYMBOL(register_mcast_snooping_notifier);
|
|
EXPORT_SYMBOL(br_get_device_by_index);
|
|
#endif
|