1712 lines
42 KiB
C
Executable File
1712 lines
42 KiB
C
Executable File
#include <linux/foe_hook.h>
|
|
#include <linux/skbuff.h>
|
|
#include "br_private.h"
|
|
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
|
#include <net/ipv6.h>
|
|
#endif
|
|
|
|
/*---------------------------------------------------------------------*/
|
|
|
|
/*TCSUPPORT_XPON_IGMP && TCSUPPORT_MULTICAST_SPEED start*/
|
|
int g_last_snoop_state = 0;
|
|
unsigned int hw_igmp_flood_enable = 1;
|
|
unsigned int g_snooping_enable = 1;
|
|
/*TCSUPPORT_XPON_IGMP && TCSUPPORT_MULTICAST_SPEED end*/
|
|
|
|
/*TCSUPPORT_IGMP_SNOOPING start*/
|
|
int snoopingdebug = 0;
|
|
/*TCSUPPORT_IGMP_SNOOPING end*/
|
|
|
|
/*---------------------------------------------------------------------*/
|
|
|
|
#define DEBUGP_SNOOP(x, args...) if(snoopingdebug) printk(x, ## args)
|
|
|
|
/*---------------------------------------------------------------------*/
|
|
|
|
/* merge form TC2 main trunck
|
|
* Convert IP6 address to printable (loggable) representation.
|
|
*/
|
|
static char digits[] = "0123456789abcdef";
|
|
static int ip6round = 0;
|
|
char* ip6_sprintf(const struct in6_addr *addr)
|
|
{
|
|
static char ip6buf[8][48];
|
|
int i = 0;
|
|
char *cp = NULL;
|
|
const u_int16_t *a = (const u_int16_t *)addr;
|
|
const u_int8_t *d = NULL;
|
|
int dcolon = 0;
|
|
|
|
ip6round = (ip6round + 1) & 7;
|
|
cp = ip6buf[ip6round];
|
|
|
|
for (i = 0; i < 8; i++) {
|
|
if (dcolon == 1) {
|
|
if (*a == 0) {
|
|
if (i == 7)
|
|
*cp++ = ':';
|
|
a++;
|
|
continue;
|
|
} else
|
|
dcolon = 2;
|
|
}
|
|
if (*a == 0) {
|
|
if (dcolon == 0 && *(a + 1) == 0) {
|
|
if (i == 0)
|
|
*cp++ = ':';
|
|
*cp++ = ':';
|
|
dcolon = 1;
|
|
} else {
|
|
*cp++ = '0';
|
|
*cp++ = ':';
|
|
}
|
|
a++;
|
|
continue;
|
|
}
|
|
d = (const u_char *)a;
|
|
{
|
|
char ch[4] = {0};
|
|
char i, j;
|
|
ch[0] = digits[*d >> 4];
|
|
ch[1] = digits[*d++ & 0xf];
|
|
ch[2] = digits[*d >> 4];
|
|
ch[3] = digits[*d & 0xf];
|
|
for(i=0; i<4; i++)
|
|
{
|
|
if(ch[i] != '0')
|
|
break;
|
|
}
|
|
if(i==4)
|
|
*cp++ = digits[0];
|
|
else
|
|
for(j=i; j<4; j++) *cp++ = ch[j];
|
|
}
|
|
*cp++ = ':';
|
|
a++;
|
|
}
|
|
*--cp = 0;
|
|
return (ip6buf[ip6round]);
|
|
}
|
|
|
|
static inline unsigned long hold_time(const struct net_bridge *br)
|
|
{
|
|
return br->topology_change ? br->forward_delay : br->ageing_time;
|
|
}
|
|
|
|
static inline int has_expired(const struct net_bridge *br,
|
|
const struct net_bridge_fdb_entry *fdb)
|
|
{
|
|
return !fdb->is_static &&
|
|
time_before_eq(fdb->updated + hold_time(br), jiffies);
|
|
}
|
|
|
|
#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
|
|
static inline int br_mdb_fillbuf(struct net_bridge *br, void *buf,
|
|
unsigned long maxnum, unsigned long skip)
|
|
{
|
|
struct __mc_fdb_entry *fe = buf;
|
|
struct net_bridge_mdb_htable *mdb = NULL;
|
|
struct net_bridge_port_group *bpg = NULL;
|
|
int i = 0, num = 0;
|
|
long result = 0;
|
|
struct net_bridge_mdb_entry *f = NULL;
|
|
|
|
mdb = br->mdb;
|
|
if(!mdb)
|
|
return 0;
|
|
memset(buf, 0, maxnum*sizeof(struct __mc_fdb_entry));
|
|
|
|
rcu_read_lock();
|
|
spin_lock_bh(&br->multicast_lock);
|
|
for (i = 0; i < mdb->max; i++) {
|
|
hlist_for_each_entry_rcu(f, &mdb->mhash[i], hlist[mdb->ver]) {
|
|
if (num >= maxnum)
|
|
goto out;
|
|
if (skip) {
|
|
--skip;
|
|
continue;
|
|
}
|
|
bpg = f->ports;
|
|
while(bpg&&(num<maxnum)){
|
|
if (has_expired(br, bpg)){
|
|
bpg = bpg->next;
|
|
continue;
|
|
}
|
|
if(bpg->version ==4){
|
|
sprintf(fe->group_addr,NIPQUAD_FMT ,NIPQUAD(bpg->addr.u.ip4));
|
|
sprintf(fe->src_addr, NIPQUAD_FMT, NIPQUAD(bpg->src_entry.src.s_addr));
|
|
}
|
|
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
|
|
else if(bpg->version == 6){
|
|
strncpy(fe->group_addr,ip6_sprintf(&bpg->addr.u.ip6),sizeof(fe->group_addr));
|
|
strncpy(fe->src_addr,ip6_sprintf(&bpg->src_entry.src6),sizeof(fe->src_addr));
|
|
}
|
|
#endif
|
|
fe->port_no = bpg->port->port_no;
|
|
fe->version = bpg->version;
|
|
memcpy(fe->group_mac, bpg->group_mac, ETH_ALEN);
|
|
memcpy(fe->host_addr, bpg->port->macAddr.addr, ETH_ALEN);
|
|
|
|
fe->filter_mode = bpg->src_entry.filt_mode;
|
|
result = jiffies - bpg->ageing_time;
|
|
fe->ageing_timer_value = jiffies_to_clock_t((result>0) ? result : 0);
|
|
bpg = bpg->next;
|
|
++fe;
|
|
++num;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
out:
|
|
spin_unlock_bh(&br->multicast_lock);
|
|
rcu_read_unlock();
|
|
return num;
|
|
}
|
|
|
|
int get_mc_fdb_entries(struct net_bridge *br, void __user *userbuf,
|
|
unsigned long maxnum, unsigned long offset)
|
|
{
|
|
int num = 0;
|
|
void *buf = NULL;
|
|
size_t size = 0;
|
|
|
|
/* Clamp size to PAGE_SIZE, test maxnum to avoid overflow */
|
|
if (maxnum > PAGE_SIZE/sizeof(struct __mc_fdb_entry))
|
|
maxnum = PAGE_SIZE/sizeof(struct __mc_fdb_entry);
|
|
|
|
size = maxnum * sizeof(struct __mc_fdb_entry);
|
|
|
|
buf = kmalloc(size, GFP_USER);
|
|
if (!buf)
|
|
return -ENOMEM;
|
|
|
|
num = br_mdb_fillbuf(br, buf, maxnum, offset);
|
|
if (num > 0) {
|
|
if (copy_to_user(userbuf, buf, num*sizeof(struct __mc_fdb_entry)))
|
|
num = -EFAULT;
|
|
}
|
|
kfree(buf);
|
|
|
|
return num;
|
|
}
|
|
|
|
int br_multicast_equal_port_group(struct net_bridge_port_group *pg,
|
|
struct net_bridge_port *port, struct br_ip *group)
|
|
{
|
|
if(!pg || !port || !group)
|
|
return 0;
|
|
if((pg->version != port->version) ||(pg->port != port))
|
|
return 0;
|
|
|
|
if(port->version == 4)
|
|
{
|
|
if(pg->src_entry.src.s_addr == port->src_entry.src.s_addr)
|
|
return 1;
|
|
}
|
|
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
|
|
else if(port->version == 6)
|
|
{
|
|
if(ipv6_addr_equal(&pg->src_entry.src6, &port->src_entry.src6))//group ip
|
|
return 1;
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
#else
|
|
static inline int br_mdb_fillbuf(struct net_bridge *br, void *buf,
|
|
unsigned long maxnum, unsigned long skip)
|
|
{
|
|
return 0 ;
|
|
}
|
|
int get_mc_fdb_entries(struct net_bridge *br, void __user *userbuf,
|
|
unsigned long maxnum, unsigned long offset)
|
|
{
|
|
return 0 ;
|
|
}
|
|
int br_multicast_equal_port_group(struct net_bridge_port_group *pg,
|
|
struct net_bridge_port *port, struct br_ip *group)
|
|
{
|
|
return 0 ;
|
|
}
|
|
#endif
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////
|
|
/*TCSUPPORT_XPON_IGMP || TCSUPPORT_MULTICAST_SPEED start*/
|
|
#define MUL_PROTO_IGMP 1
|
|
#define MUL_PROTO_MLD 2
|
|
|
|
static struct list_head hwnat_igmp_entry;
|
|
static unsigned int hwnat_igmp_flag = 1;
|
|
static unsigned int hwnat_age_time = 3000;
|
|
static spinlock_t hwnat_lock;
|
|
static struct net_bridge * hwnat_br = NULL;
|
|
|
|
/*TCSUPPORT_XPON_IGMP && TCSUPPORT_MULTICAST_SPEED start*/
|
|
static spinlock_t hwnat_disable_snooping_lock;
|
|
static LIST_HEAD(multicast_flood_hw_list);
|
|
/*TCSUPPORT_XPON_IGMP && TCSUPPORT_MULTICAST_SPEED end*/
|
|
|
|
/*TCSUPPORT_XPON_IGMP || TCSUPPORT_MULTICAST_SPEED end*/
|
|
|
|
/*TCSUPPORT_XPON_IGMP || TCSUPPORT_MULTICAST_SPEED start*/
|
|
#ifdef TCSUPPORT_RA_HWNAT
|
|
extern int (*hwnat_is_alive_pkt_hook)(struct sk_buff* skb);
|
|
extern int (*hwnat_skb_to_foe_hook)(struct sk_buff* skb);
|
|
extern int (*hwnat_set_special_tag_hook)(int index, int tag);
|
|
extern int (*hwnat_delete_foe_entry_hook)(int index);
|
|
extern int (*hwnat_is_multicast_entry_hook)(int index ,unsigned char* grp_addr,unsigned char* src_addr,int type);
|
|
/*TCSUPPORT_MULTICAST_SPEED start*/
|
|
extern int (*multicast_speed_find_entry_hook)(int index);
|
|
extern int (*multicast_speed_learn_flow_hook)(struct sk_buff* skb);
|
|
extern int (*hwnat_set_rule_according_to_state_hook)(int index, int state,int mask);
|
|
/*TCSUPPORT_MULTICAST_SPEED end*/
|
|
extern int (*xpon_igmp_learn_flow_hook)(struct sk_buff* skb);
|
|
extern int (*hwnat_set_wlan_multicast_hook)(int index,int flag);
|
|
extern int (*wan_multicast_undrop_hook)(void);
|
|
/*TCSUPPORT_XPON_IGMP && TCSUPPORT_MULTICAST_SPEED start*/
|
|
extern int (*multicast_flood_find_entry_hook)(int index);
|
|
extern int (*hwnat_set_multicast_speed_enable_hook)(int enable);
|
|
extern int (*multicast_flood_is_bind_hook)(int index);
|
|
/*TCSUPPORT_XPON_IGMP && TCSUPPORT_MULTICAST_SPEED end*/
|
|
#else
|
|
static int (*hwnat_is_alive_pkt_hook)(struct sk_buff* skb) = NULL;
|
|
static int (*hwnat_skb_to_foe_hook)(struct sk_buff* skb) = NULL;
|
|
static int (*hwnat_set_special_tag_hook)(int index, int tag) = NULL;
|
|
static int (*hwnat_delete_foe_entry_hook)(int index) = NULL;
|
|
static int (*hwnat_is_multicast_entry_hook)(int index ,unsigned char* grp_addr,unsigned char* src_addr,int type) = NULL;
|
|
/*TCSUPPORT_MULTICAST_SPEED start*/
|
|
extern int (*multicast_speed_find_entry_hook)(int index);
|
|
static int (*multicast_speed_learn_flow_hook)(struct sk_buff* skb) = NULL;
|
|
static int (*hwnat_set_rule_according_to_state_hook)(int index, int state,int mask) = NULL;
|
|
/*TCSUPPORT_MULTICAST_SPEED end*/
|
|
static int (*hwnat_set_wlan_multicast_hook)(int index,int flag) = NULL;
|
|
static int (*xpon_igmp_learn_flow_hook)(struct sk_buff* skb) = NULL;
|
|
static int (*wan_multicast_undrop_hook)(void) = NULL;
|
|
/*TCSUPPORT_XPON_IGMP && TCSUPPORT_MULTICAST_SPEED start*/
|
|
static int (*multicast_flood_find_entry_hook)(int index) = NULL;
|
|
static int (*hwnat_set_multicast_speed_enable_hook)(int enable) = NULL;
|
|
static int (*multicast_flood_is_bind_hook)(int index) = NULL;
|
|
/*TCSUPPORT_XPON_IGMP && TCSUPPORT_MULTICAST_SPEED end*/
|
|
#endif
|
|
|
|
/*------------------------------------------------------------------------------*/
|
|
struct list_head* igmp_hwnat_get_list(void)
|
|
{
|
|
return &hwnat_igmp_entry;
|
|
}
|
|
|
|
int igmp_hwnat_debug_on(void)
|
|
{
|
|
return hwnat_igmp_flag&0x02;
|
|
}
|
|
|
|
int igmp_hwnat_enable(void)
|
|
{
|
|
return hwnat_igmp_flag&0x01;
|
|
}
|
|
|
|
#define IGMP_HWNAT_DEBUG(fmt,args...) \
|
|
do{ \
|
|
if(igmp_hwnat_debug_on()) \
|
|
{ \
|
|
printk("[%s %d]:"fmt"\r\n",__FUNCTION__,__LINE__,##args);\
|
|
} \
|
|
}while(0)
|
|
|
|
void* igmp_hwnat_alloc(int size)
|
|
{
|
|
void* ptr = NULL;
|
|
if (size>0)
|
|
{
|
|
ptr = kzalloc(size, GFP_ATOMIC);
|
|
}
|
|
return ptr;
|
|
|
|
}
|
|
|
|
#ifdef TCSUPPORT_MULTICAST_SPEED
|
|
void igmp_hwnat_free(struct rcu_head *head)
|
|
{
|
|
struct IGMP_HWNATEntry_s *entry
|
|
= container_of(head, struct IGMP_HWNATEntry_s,rcu);
|
|
if(entry)
|
|
kfree(entry);
|
|
|
|
return;
|
|
}
|
|
#else
|
|
void igmp_hwnat_free(void* ptr)
|
|
{
|
|
if (ptr)
|
|
kfree(ptr);
|
|
|
|
ptr = NULL;
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
/**************************************************
|
|
Function: Get Port Mask Bit
|
|
Input: pointer to struct net_bridge_port
|
|
Return:
|
|
0-3: ethernet port mask
|
|
8-11: wifi port mask
|
|
-1: error
|
|
**************************************************/
|
|
extern int g_port_reverse_kernel;
|
|
#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
|
|
int igmp_hwnat_get_port(struct net_bridge_port* p)
|
|
{
|
|
if (! p || !( p->dev))
|
|
{
|
|
if (! p)
|
|
IGMP_HWNAT_DEBUG("p == NULL");
|
|
else
|
|
IGMP_HWNAT_DEBUG("p->dev == NULL");
|
|
return -1;
|
|
}
|
|
|
|
IGMP_HWNAT_DEBUG("port name = %s ",p->dev->name);
|
|
|
|
if ((p->dev->name[0] == 'e') && (p->dev->name[4] == '.')) {
|
|
#if defined(TCSUPPORT_CUC_C5_2P)
|
|
unsigned int temport = 0;
|
|
unsigned int port = p->dev->name[5] - '1';
|
|
if(port>=0 && port<=3)
|
|
{
|
|
if(port == 0) temport = 3;
|
|
else if(port == 1) temport = 0;
|
|
else if(port == 2) temport = 1;
|
|
else if(port == 3) temport = 2;
|
|
}
|
|
port = temport;
|
|
|
|
return port;
|
|
#else
|
|
if (g_port_reverse_kernel == 1 && (p->dev->name[5] - '1') <= 3)
|
|
return 3 - (p->dev->name[5] - '1');
|
|
else
|
|
return (p->dev->name[5] - '1');
|
|
#endif
|
|
}
|
|
|
|
#ifdef TCSUPPORT_MULTICAST_SPEED
|
|
if ((strlen(p->dev->name) == 3) && (strncmp(p->dev->name,"ra", 2) == 0) &&
|
|
((p->dev->name[2] >= '0') && (p->dev->name[2] <= '7')))
|
|
{
|
|
return (p->dev->name[2] - '0')+HWNAT_WLAN_IF_BASE;
|
|
}
|
|
|
|
if ((strlen(p->dev->name) == 4) && (strncmp(p->dev->name,"rai", 3) == 0) &&
|
|
((p->dev->name[3] >= '0') && (p->dev->name[3] <= '7')))
|
|
{
|
|
return (p->dev->name[3] - '0')+HWNAT_WLAN_IF_BASE+HWNAT_WLAN_IF_NUM;
|
|
}
|
|
#else
|
|
if ((p->dev->name[0] == 'r') && (p->dev->name[1] == 'a'))
|
|
return (p->dev->name[5] - '0')+8;
|
|
#endif
|
|
|
|
return -1;
|
|
}
|
|
|
|
/**************************************************
|
|
Function: Check if the port can receive the
|
|
multicast flow
|
|
Input:
|
|
br: pointer to struct net_bridge
|
|
port: pointer to struct net_bridge_port
|
|
entry: pointer to struct IGMP_HWNATEntry_t
|
|
Return:
|
|
0: fail; 1: ok
|
|
**************************************************/
|
|
|
|
int igmp_hwnat_should_deliver(struct net_bridge *br,struct net_bridge_port *port,IGMP_HWNATEntry_t* entry)
|
|
{
|
|
struct net_bridge_mdb_htable *mdb = br->mdb;
|
|
struct net_bridge_mdb_entry *mp = NULL;
|
|
struct net_bridge_port_group *pg = NULL;
|
|
struct br_ip group;
|
|
int hash,flag = 0;
|
|
char src[16];
|
|
|
|
if (!mdb)
|
|
{
|
|
IGMP_HWNAT_DEBUG("mdb == NULL");
|
|
return 0;
|
|
}
|
|
|
|
/*<< Shangguanweijie@13Oct2017, add, fix wireless multicast Bug 181666 & Bug 181690, #2476.
|
|
** If group.vid is not inited, It maybe lead hash value exception.
|
|
** TODO: if multicast data with VID, will be a problem.
|
|
*/
|
|
memset(&group, 0, sizeof(group));
|
|
/*>> endof Shangguanweijie@13Oct2017, add. */
|
|
|
|
IGMP_HWNAT_DEBUG("entry->proto=%d, dev->name=%s",entry->proto, port->dev->name);
|
|
|
|
if (entry->proto == MUL_PROTO_IGMP)
|
|
group.proto = htons(ETH_P_IP);
|
|
else if (entry->proto == MUL_PROTO_MLD)
|
|
group.proto = htons(ETH_P_IPV6);
|
|
else
|
|
return 0;
|
|
|
|
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
|
|
memcpy(group.u.ip6.s6_addr,entry->grp_addr,16);
|
|
#endif
|
|
|
|
hash = br_ip_hash_for_export(mdb, &group);
|
|
IGMP_HWNAT_DEBUG("hash(%d), group.u.ip4(%x), group.vid(%d), group.proto(%d)", hash, group.u.ip4, group.vid, group.proto);
|
|
mp = br_multicast_get_group(br, port, &group, hash);
|
|
|
|
if (mp==NULL)
|
|
{
|
|
IGMP_HWNAT_DEBUG("mp == NULL");
|
|
return 0;
|
|
}
|
|
|
|
memset(src,0,16);
|
|
memcpy(src,entry->src_addr,16);
|
|
pg = mp->ports;
|
|
|
|
while(pg)
|
|
{
|
|
if (pg->port != port)
|
|
{
|
|
pg = pg->next;
|
|
continue;
|
|
}
|
|
|
|
if (entry->proto==MUL_PROTO_IGMP)
|
|
{
|
|
if(pg->src_entry.filt_mode == MCAST_INCLUDE)
|
|
{
|
|
if (memcmp(&pg->src_entry.src.s_addr,src,4)==0)
|
|
return 1;
|
|
}
|
|
else if(pg->src_entry.filt_mode == MCAST_EXCLUDE)
|
|
{
|
|
if(0 == pg->src_entry.src.s_addr)
|
|
return 1;
|
|
else if(memcmp(&pg->src_entry.src.s_addr,src,4))
|
|
flag = 2;
|
|
else if(memcmp(&pg->src_entry.src.s_addr,src,4)==0)
|
|
return 0;
|
|
}
|
|
}
|
|
if (entry->proto==MUL_PROTO_MLD)
|
|
return 1;
|
|
|
|
pg = pg->next;
|
|
}
|
|
|
|
if (flag == 2)
|
|
return 1;
|
|
return 0;
|
|
|
|
}
|
|
|
|
/**************************************************
|
|
Function: Get forwarded ports given a multicast
|
|
group
|
|
Input:
|
|
br: pointer to struct net_bridge
|
|
entry: pointer to struct IGMP_HWNATEntry_t
|
|
Return:
|
|
0x0-0x0f0f: port mask
|
|
***************************************************/
|
|
int igmp_hwnat_port_mask(struct net_bridge *br,IGMP_HWNATEntry_t* entry)
|
|
{
|
|
struct net_bridge_port *p = NULL;
|
|
int port = 0,mask = 0;
|
|
int switch_port = 0;
|
|
int ret = 0;
|
|
|
|
IGMP_HWNAT_DEBUG("enter");
|
|
list_for_each_entry(p, &br->port_list, list)
|
|
{
|
|
ret = igmp_hwnat_should_deliver(br,p,entry);
|
|
IGMP_HWNAT_DEBUG("ret(%d)", ret);
|
|
|
|
if (ret==0)
|
|
continue;
|
|
|
|
port = igmp_hwnat_get_port(p);
|
|
IGMP_HWNAT_DEBUG("p->dev->name(%s), port = %d", p->dev->name, port);
|
|
|
|
if (port < 0 )
|
|
continue;
|
|
/*lan port*/
|
|
if(port >= 0 && port <= 3)
|
|
{
|
|
#if defined(TCSUPPORT_CPU_MT7520)
|
|
if(MT7530LanPortMap2Switch_hook)
|
|
{
|
|
switch_port = MT7530LanPortMap2Switch_hook(port);
|
|
}
|
|
port = switch_port;
|
|
#endif
|
|
mask |= 1 << port;
|
|
}
|
|
/*wifi port*/
|
|
else if(port >= HWNAT_WLAN_IF_BASE)
|
|
{
|
|
mask |= 1 << port;
|
|
}
|
|
}
|
|
|
|
IGMP_HWNAT_DEBUG("mask = %d, port = %d",mask, port);
|
|
|
|
return mask;
|
|
}
|
|
|
|
/**************************************************
|
|
Function: Delete a entry maintained by SW
|
|
Input:
|
|
entry: pointer to struct IGMP_HWNATEntry_t
|
|
Return:
|
|
0: ok
|
|
**************************************************/
|
|
int igmp_hwnat_delete_entry(IGMP_HWNATEntry_t* entry)
|
|
{
|
|
if (entry == NULL)
|
|
{
|
|
IGMP_HWNAT_DEBUG("entry == NULL");
|
|
return 0;
|
|
}
|
|
IGMP_HWNAT_DEBUG("enter");
|
|
|
|
del_timer(&entry->age_timer);
|
|
#ifdef TCSUPPORT_MULTICAST_SPEED
|
|
list_del_rcu(&(entry->list));
|
|
call_rcu_bh(&(entry->rcu), igmp_hwnat_free);
|
|
#else
|
|
list_del(&entry->list);
|
|
igmp_hwnat_free(entry);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/**************************************************
|
|
Function: Check if the multicast group is still
|
|
accelerated by HW
|
|
Input:
|
|
entry: pointer to struct IGMP_HWNATEntry_t
|
|
Return:
|
|
0: fail; 1: ok
|
|
**************************************************/
|
|
int igmp_hwnat_check_entry_valid(IGMP_HWNATEntry_t* entry)
|
|
{
|
|
int valid = 0,type = 0;
|
|
|
|
if (entry->proto == MUL_PROTO_MLD)
|
|
type = 1;
|
|
|
|
if (hwnat_is_multicast_entry_hook)
|
|
valid = hwnat_is_multicast_entry_hook(entry->index,entry->grp_addr,entry->src_addr,type);
|
|
|
|
return valid;
|
|
}
|
|
|
|
/**************************************************
|
|
Function: Get the foe index from skb
|
|
Input:
|
|
entry: pointer to struct sk_buff
|
|
Return:
|
|
foe entry index in hwnat
|
|
**************************************************/
|
|
int igmp_hwnat_flow_index(struct sk_buff* skb)
|
|
{
|
|
int index = -1;
|
|
|
|
if (hwnat_skb_to_foe_hook && skb)
|
|
index = hwnat_skb_to_foe_hook(skb);
|
|
|
|
return index;
|
|
}
|
|
|
|
/**************************************************
|
|
Function: Clean the HW accelebration given
|
|
the multicast group
|
|
Input:
|
|
entry: pointer to struct IGMP_HWNATEntry_t
|
|
Return:
|
|
0: ok
|
|
*************************************************/
|
|
int igmp_hwnat_delete_foe(IGMP_HWNATEntry_t* entry)
|
|
{
|
|
if (igmp_hwnat_check_entry_valid(entry)==0)
|
|
{
|
|
IGMP_HWNAT_DEBUG("entry is not valid");
|
|
return 0;
|
|
}
|
|
|
|
IGMP_HWNAT_DEBUG("index = %d ,mask = %x",entry->index,entry->mask);
|
|
|
|
if (hwnat_delete_foe_entry_hook)
|
|
hwnat_delete_foe_entry_hook(entry->index);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**************************************************
|
|
Function: Delete the SW and HW maintained
|
|
multicast flow
|
|
Input:
|
|
entry: pointer to struct IGMP_HWNATEntry_t
|
|
Return:
|
|
0: ok
|
|
**************************************************/
|
|
int igmp_hwnat_delete_flow(IGMP_HWNATEntry_t* entry)
|
|
{
|
|
if (entry == NULL)
|
|
{
|
|
IGMP_HWNAT_DEBUG("entry == NUL");
|
|
return 0;
|
|
}
|
|
|
|
IGMP_HWNAT_DEBUG("entry index = %d",entry->index);
|
|
|
|
igmp_hwnat_delete_foe(entry);
|
|
igmp_hwnat_delete_entry(entry);
|
|
return 0;
|
|
}
|
|
|
|
/**************************************************
|
|
Function: Clear all the SW and HW maintained
|
|
multicast flow
|
|
Input:
|
|
N/A
|
|
Return:
|
|
0: ok
|
|
**************************************************/
|
|
int igmp_hwnat_clear_flows(void)
|
|
{
|
|
IGMP_HWNATEntry_t* entry = NULL,*tmp = NULL;
|
|
struct list_head* hwnat_flow = igmp_hwnat_get_list();
|
|
|
|
IGMP_HWNAT_DEBUG("enter");
|
|
|
|
spin_lock(&hwnat_lock);
|
|
#ifdef TCSUPPORT_MULTICAST_SPEED
|
|
list_for_each_entry_rcu(entry,hwnat_flow,list)
|
|
#else
|
|
list_for_each_entry_safe(entry,tmp,hwnat_flow,list)
|
|
#endif
|
|
{
|
|
igmp_hwnat_delete_flow(entry);
|
|
}
|
|
spin_unlock(&hwnat_lock);
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(igmp_hwnat_clear_flows);
|
|
|
|
/**************************************************
|
|
Function: Find the SW maintained entry given
|
|
foe entry index
|
|
Input:
|
|
index: foe entry index
|
|
Return:
|
|
0: ok
|
|
**************************************************/
|
|
IGMP_HWNATEntry_t* igmp_hwnat_find_entry_rcu(int index)
|
|
{
|
|
IGMP_HWNATEntry_t* entry = NULL;
|
|
struct list_head* hwnat_flow = igmp_hwnat_get_list();
|
|
|
|
rcu_read_lock();
|
|
|
|
list_for_each_entry_rcu(entry,hwnat_flow,list)
|
|
{
|
|
if (entry->index == index)
|
|
{
|
|
rcu_read_unlock();
|
|
return entry;
|
|
}
|
|
}
|
|
|
|
rcu_read_unlock();
|
|
return NULL;
|
|
}
|
|
|
|
/**************************************************
|
|
Function: Find the SW maintained entry given
|
|
foe entry index
|
|
Input:
|
|
index: foe entry index
|
|
Return:
|
|
0: ok
|
|
**************************************************/
|
|
IGMP_HWNATEntry_t* igmp_hwnat_find_entry(int index)
|
|
{
|
|
IGMP_HWNATEntry_t* entry = NULL;
|
|
|
|
#if defined(TCSUPPORT_XPON_IGMP) && defined(TCSUPPORT_MULTICAST_SPEED)
|
|
if(0 == g_snooping_enable)
|
|
{
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
struct list_head* hwnat_flow = igmp_hwnat_get_list();
|
|
|
|
list_for_each_entry_rcu(entry,hwnat_flow,list)
|
|
{
|
|
if (entry->index == index)
|
|
{
|
|
return entry;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**************************************************
|
|
Function: Callback function to check if the HW
|
|
mulitcast flow is still valid
|
|
Input:
|
|
entry: pointer to struct IGMP_HWNATEntry_t
|
|
Return:
|
|
0: ok
|
|
**************************************************/
|
|
void igmp_hwnat_timer_timeout(unsigned long arg)
|
|
{
|
|
IGMP_HWNATEntry_t* entry = (IGMP_HWNATEntry_t* )arg;
|
|
IGMP_HWNAT_DEBUG("enter");
|
|
|
|
if (entry)
|
|
{
|
|
if (igmp_hwnat_check_entry_valid(entry)==0)
|
|
{
|
|
spin_lock(&hwnat_lock);
|
|
igmp_hwnat_delete_entry(entry);
|
|
spin_unlock(&hwnat_lock);
|
|
}
|
|
else
|
|
{
|
|
mod_timer(&entry->age_timer,round_jiffies(jiffies) + hwnat_age_time);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
/**************************************************
|
|
Function: Add a HW accelebrated multicast flow into
|
|
SW maintained entry
|
|
Input:
|
|
entry: pointer to struct IGMP_HWNATEntry_t
|
|
Return:
|
|
0: ok
|
|
***************************************************/
|
|
IGMP_HWNATEntry_t* igmp_hwnat_add_flow(struct sk_buff* skb ,int proto,unsigned char* grp_addr, unsigned char* src_addr)
|
|
{
|
|
IGMP_HWNATEntry_t* entry = NULL;
|
|
struct list_head* hwnat_flow = igmp_hwnat_get_list();
|
|
int index = igmp_hwnat_flow_index(skb);
|
|
|
|
IGMP_HWNAT_DEBUG("index = %d",index);
|
|
|
|
if (index < 0)
|
|
return NULL;
|
|
|
|
entry = (IGMP_HWNATEntry_t* )igmp_hwnat_alloc(sizeof(IGMP_HWNATEntry_t));
|
|
|
|
if (entry==NULL)
|
|
{
|
|
IGMP_HWNAT_DEBUG("alloc entry fail");
|
|
return NULL;
|
|
}
|
|
entry->index = index;
|
|
entry->mask = 0;
|
|
entry->proto = proto;
|
|
memcpy(entry->grp_addr,grp_addr,16);
|
|
memcpy(entry->src_addr,src_addr,16);
|
|
|
|
#ifdef TCSUPPORT_MULTICAST_SPEED
|
|
list_add_tail_rcu(&entry->list, hwnat_flow);
|
|
#else
|
|
list_add_tail(&entry->list,hwnat_flow);
|
|
#endif
|
|
|
|
setup_timer(&entry->age_timer, igmp_hwnat_timer_timeout, (unsigned long)entry);
|
|
mod_timer(&entry->age_timer,round_jiffies(jiffies) + hwnat_age_time);
|
|
|
|
return entry;
|
|
}
|
|
|
|
/**************************************************
|
|
Function: Clear all the SW and HW maintained
|
|
multicast flow
|
|
Input:
|
|
entry: pointer to struct IGMP_HWNATEntry_t
|
|
Return:
|
|
0: ok
|
|
***************************************************/
|
|
int igmp_hwnat_update_flow(IGMP_HWNATEntry_t* entry ,int mask)
|
|
{
|
|
IGMP_HWNAT_DEBUG("index = %d ,mask = %d ",entry->index,mask);
|
|
|
|
if (hwnat_set_special_tag_hook)
|
|
hwnat_set_special_tag_hook(entry->index,mask);
|
|
|
|
entry->mask = mask;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**************************************************
|
|
Function: Enable hwnat wlan multicast
|
|
accelebration give a multicast group
|
|
Input:
|
|
entry: pointer to struct IGMP_HWNATEntry_t
|
|
Return:
|
|
0: ok
|
|
**************************************************/
|
|
int igmp_hwnat_open_wlan(IGMP_HWNATEntry_t* entry)
|
|
{
|
|
|
|
IGMP_HWNAT_DEBUG("enter");
|
|
|
|
if (hwnat_set_wlan_multicast_hook)/*always NULL*/
|
|
hwnat_set_wlan_multicast_hook(entry->index,1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**************************************************
|
|
Function: Disble hwnat wlan multicast
|
|
accelebration give a multicast group
|
|
Input:
|
|
entry: pointer to struct IGMP_HWNATEntry_t
|
|
Return:
|
|
0: ok
|
|
**************************************************/
|
|
int igmp_hwnat_close_wlan(IGMP_HWNATEntry_t* entry)
|
|
{
|
|
IGMP_HWNAT_DEBUG("enter");
|
|
|
|
if (hwnat_set_wlan_multicast_hook)/*always NULL*/
|
|
hwnat_set_wlan_multicast_hook(entry->index,0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**************************************************
|
|
Function: update hw_nat mask to hw&sw entry, update sw wifinum
|
|
Input:
|
|
entry: pointer to struct IGMP_HWNATEntry_t
|
|
mask: port mask
|
|
state: port state, lan? wlan?
|
|
Return:
|
|
0: ok
|
|
**************************************************/
|
|
int igmp_hwnat_update_hw_nat_info(IGMP_HWNATEntry_t* entry,unsigned long mask,int state)
|
|
{
|
|
int i;
|
|
unsigned long wlanmask;
|
|
unsigned long masktemp = mask;
|
|
entry->wifinum = 0;
|
|
IGMP_HWNAT_DEBUG("state=%d,mask=%d",state,mask);
|
|
switch(state)
|
|
{
|
|
//only state i and state iii need to know how much wifi interfaces
|
|
case MULTICAST_SPEED_STATE_I:
|
|
case MULTICAST_SPEED_STATE_III:
|
|
//for multi ssid speed
|
|
wlanmask = (masktemp>>HWNAT_WLAN_IF_BASE)&0x0ff;
|
|
for(i = 0;i < HWNAT_WLAN_IF_MAXNUM; i++)
|
|
{
|
|
if(wlanmask&(1 << i))
|
|
entry->wifinum++;
|
|
}
|
|
break;
|
|
|
|
//fall through and do nothing
|
|
case MULTICAST_SPEED_STATE_II:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if(hwnat_set_rule_according_to_state_hook)
|
|
hwnat_set_rule_according_to_state_hook(entry->index,state,mask);
|
|
|
|
entry->mask = mask;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**************************************************
|
|
Function: Sync the SW maintained multicast
|
|
flow with HW maintained flow
|
|
Input:
|
|
entry: pointer to struct net_bridge
|
|
Return:
|
|
0: ok
|
|
***************************************************/
|
|
int igmp_hwnat_update_all(struct net_bridge *br)
|
|
{
|
|
IGMP_HWNATEntry_t* entry = NULL,*temp = NULL;
|
|
struct list_head* hwnat_flow = igmp_hwnat_get_list();
|
|
unsigned long mask,old_mask;
|
|
#ifdef TCSUPPORT_MULTICAST_SPEED
|
|
int interfaceflag;
|
|
int masktemp;
|
|
#endif
|
|
if (igmp_hwnat_enable()==0)
|
|
return 0;
|
|
|
|
IGMP_HWNAT_DEBUG("enter");
|
|
|
|
spin_lock(&hwnat_lock);
|
|
|
|
#ifdef TCSUPPORT_MULTICAST_SPEED
|
|
list_for_each_entry_rcu(entry,hwnat_flow,list)
|
|
#else
|
|
list_for_each_entry_safe(entry,temp,hwnat_flow,list)
|
|
#endif
|
|
{
|
|
if (igmp_hwnat_check_entry_valid(entry)==0)/*check if is a valid hw_nat entry*/
|
|
{
|
|
igmp_hwnat_delete_entry(entry);
|
|
continue;
|
|
}
|
|
|
|
mask = igmp_hwnat_port_mask(br,entry);
|
|
old_mask = entry->mask;
|
|
if (mask != old_mask)
|
|
{
|
|
#ifdef TCSUPPORT_MULTICAST_SPEED
|
|
masktemp = mask;
|
|
|
|
interfaceflag = (masktemp&0xff) >0?1:0;//compute lan interface
|
|
interfaceflag |= (((masktemp>>HWNAT_WLAN_IF_BASE)&0x0ff) >0?1:0)<<1;//compute wlan interface
|
|
switch (interfaceflag)
|
|
{
|
|
case MULTICAST_SPEED_STATE_I:
|
|
igmp_hwnat_update_hw_nat_info(entry,mask,MULTICAST_SPEED_STATE_I); /*reset special tag*/
|
|
break;
|
|
|
|
case MULTICAST_SPEED_STATE_II:
|
|
igmp_hwnat_update_hw_nat_info(entry,mask,MULTICAST_SPEED_STATE_II);
|
|
break;
|
|
|
|
case MULTICAST_SPEED_STATE_III:
|
|
igmp_hwnat_update_hw_nat_info(entry,mask,MULTICAST_SPEED_STATE_III);
|
|
break;
|
|
|
|
case MULTICAST_SPEED_STATE_IV:
|
|
igmp_hwnat_delete_flow(entry);
|
|
break;
|
|
|
|
default://do nothing
|
|
break;
|
|
}
|
|
#else
|
|
if ((mask&0x0f)==0)
|
|
igmp_hwnat_delete_flow(entry);
|
|
|
|
if ((mask&0x0f) != (old_mask&0x0f) && (mask&0x0f)>0)
|
|
igmp_hwnat_update_flow(entry,mask);
|
|
|
|
if (((mask>>8)&0x0f) > 0 && ((old_mask>>8)&0x0f) == 0)
|
|
igmp_hwnat_open_wlan(entry);
|
|
|
|
if (((mask>>8)&0x0f) == 0 && ((old_mask>>8)&0x0f) > 0)
|
|
igmp_hwnat_close_wlan(entry);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
spin_unlock(&hwnat_lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**************************************************
|
|
Function: Check if the addr is multicast addr
|
|
Input:
|
|
entry: pointer to mac address
|
|
Return:
|
|
0: fail; 1: ok
|
|
***************************************************/
|
|
int igmp_hwnat_is_flow_pkt(char* dst)
|
|
{
|
|
char mac[3] = {0x01,0x00,0x5e};
|
|
|
|
if (memcmp(dst,mac,3)==0)
|
|
return 1;
|
|
|
|
if (dst[0]==0x33 && dst[1] == 0x33)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**************************************************
|
|
Function: SW learn the multicast flow from
|
|
passed skb by hw_nat module
|
|
Input:
|
|
entry: pointer to struct sk_buff
|
|
Return:
|
|
0: ok
|
|
***************************************************/
|
|
int igmp_hwnat_learn_flow(struct sk_buff* skb)
|
|
{
|
|
unsigned char dest_addr[16],src_addr[16];
|
|
short int proto = 0;
|
|
struct iphdr* ih;
|
|
struct ipv6hdr* i6h;
|
|
unsigned short eth_type;
|
|
unsigned char* buff = skb_mac_header(skb)+18;
|
|
IGMP_HWNATEntry_t* entry = NULL;
|
|
int index;
|
|
#ifdef TCSUPPORT_MULTICAST_SPEED
|
|
struct ethhdr *eth;
|
|
struct vlan_ethhdr *vlan_eth;
|
|
char *dst;
|
|
#endif
|
|
|
|
if (igmp_hwnat_enable()==0)
|
|
return 0;
|
|
|
|
#if defined(TCSUPPORT_XPON_IGMP) && defined(TCSUPPORT_MULTICAST_SPEED)
|
|
if(0 == g_snooping_enable)
|
|
{
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
IGMP_HWNAT_DEBUG("name=%s",skb->dev->name);
|
|
|
|
#ifdef TCSUPPORT_MULTICAST_SPEED
|
|
if((strncmp(skb->dev->name,"eth", 3) == 0))
|
|
{
|
|
vlan_eth = vlan_eth_hdr(skb);
|
|
buff = skb_mac_header(skb)+ ETH_HLEN + VLAN_HLEN;
|
|
if(vlan_eth)
|
|
{
|
|
dst = vlan_eth->h_dest;
|
|
eth_type = vlan_eth->h_vlan_encapsulated_proto;
|
|
}
|
|
else
|
|
{
|
|
printk("\r\n%s:vlan_eth == NULL,return",__FUNCTION__);
|
|
return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
eth =(struct ethhdr *)skb->data;
|
|
buff = skb_mac_header(skb)+ ETH_HLEN;
|
|
if(eth)
|
|
{
|
|
dst = eth->h_dest;
|
|
eth_type = eth->h_proto;
|
|
}
|
|
else
|
|
{
|
|
printk("\r\n%s:eth == NULL,return",__FUNCTION__);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (igmp_hwnat_is_flow_pkt(dst)==0)
|
|
{
|
|
IGMP_HWNAT_DEBUG("hw nat rule not match pkt");
|
|
return 0;
|
|
}
|
|
#else
|
|
if (igmp_hwnat_is_flow_pkt(vlan_eth_hdr(skb)->h_dest)==0)
|
|
return 0;
|
|
|
|
eth_type = vlan_eth_hdr(skb)->h_vlan_encapsulated_proto;
|
|
#endif
|
|
|
|
#if defined(TCSUPPORT_CT_SIMCARD_SEPARATION) && defined(TCSUPPORT_CT_2PORTS)
|
|
IGMP_HWNAT_DEBUG("eth_type=%x",eth_type);
|
|
#else
|
|
IGMP_HWNAT_DEBUG("eth_type=%x",eth_type);
|
|
#endif
|
|
memset(dest_addr,0,16);
|
|
memset(src_addr,0,16);
|
|
|
|
if (eth_type==htons(ETH_P_IP))
|
|
{
|
|
proto = MUL_PROTO_IGMP;
|
|
ih = (struct iphdr*)buff;
|
|
memcpy(dest_addr,&ih->daddr,4);
|
|
memcpy(src_addr,&ih->saddr,4);
|
|
}
|
|
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
|
|
else if(eth_type==htons(ETH_P_IPV6))
|
|
{
|
|
proto = MUL_PROTO_MLD;
|
|
i6h = (struct ipv6hdr*)buff;
|
|
memcpy(dest_addr, i6h->daddr.s6_addr,16);
|
|
memcpy(src_addr, i6h->saddr.s6_addr,16);
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
index = igmp_hwnat_flow_index(skb);
|
|
|
|
spin_lock(&hwnat_lock);
|
|
#ifdef TCSUPPORT_MULTICAST_SPEED
|
|
entry = igmp_hwnat_find_entry_rcu(index);
|
|
#else
|
|
entry = igmp_hwnat_find_entry(index);
|
|
#endif
|
|
|
|
if (entry != NULL)
|
|
igmp_hwnat_delete_entry(entry);
|
|
|
|
igmp_hwnat_add_flow(skb,proto,dest_addr,src_addr);
|
|
|
|
spin_unlock(&hwnat_lock);
|
|
|
|
if (hwnat_br)
|
|
igmp_hwnat_update_all(hwnat_br);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**************************************************
|
|
Function: Clean all dropped multicast flow by HW
|
|
Input:
|
|
N/A
|
|
Return:
|
|
0: ok
|
|
***************************************************/
|
|
int igmp_hwnat_multicast_undrop(void)
|
|
{
|
|
if (wan_multicast_undrop_hook)
|
|
wan_multicast_undrop_hook();
|
|
|
|
return 0;
|
|
}
|
|
|
|
int igmp_hwnat_read_proc(char *buf, char **start, off_t off, int count,int *eof, void *data)
|
|
{
|
|
int len = 0;
|
|
IGMP_HWNATEntry_t* entry = NULL;
|
|
struct list_head* hwnat_flow = igmp_hwnat_get_list();
|
|
#if defined(TCSUPPORT_XPON_IGMP) && defined(TCSUPPORT_MULTICAST_SPEED)
|
|
multicast_flood_hwentry_t* flood_entry = NULL;
|
|
multicast_flood_hwentry_t* ptr = NULL;
|
|
#endif
|
|
|
|
len = sprintf(buf,"flag = %d time = %d \n",hwnat_igmp_flag,hwnat_age_time);
|
|
#if defined(TCSUPPORT_XPON_IGMP) && defined(TCSUPPORT_MULTICAST_SPEED)
|
|
len += sprintf(buf+len,"hw_igmp_flood_enable = %d.\n",hw_igmp_flood_enable);
|
|
spin_lock(&hwnat_disable_snooping_lock);
|
|
list_for_each_entry_safe(flood_entry, ptr, &multicast_flood_hw_list, list)
|
|
{
|
|
len += sprintf(buf+len,"flood index = %d port_mask = %d.\n",flood_entry->index, flood_entry->port_mask);
|
|
}
|
|
spin_unlock(&hwnat_disable_snooping_lock);
|
|
#endif
|
|
|
|
len += sprintf(buf+len,"index type mask wlannum grp_addr src_addr \n");
|
|
#ifdef TCSUPPORT_MULTICAST_SPEED
|
|
rcu_read_lock();
|
|
#else
|
|
spin_lock(&hwnat_lock);
|
|
#endif
|
|
|
|
#ifdef TCSUPPORT_MULTICAST_SPEED
|
|
list_for_each_entry_rcu(entry,hwnat_flow,list)
|
|
#else
|
|
list_for_each_entry(entry,hwnat_flow,list)
|
|
#endif
|
|
{
|
|
#ifdef TCSUPPORT_MULTICAST_SPEED
|
|
if(MUL_PROTO_IGMP == entry->proto)
|
|
{
|
|
len += sprintf(buf+len,"%d %d %d %d %u.%u.%u.%u %u.%u.%u.%u \n",entry->index,entry->proto,entry->mask,entry->wifinum
|
|
,entry->grp_addr[0],entry->grp_addr[1],entry->grp_addr[2],entry->grp_addr[3],
|
|
entry->src_addr[0],entry->src_addr[1],entry->src_addr[2],entry->src_addr[3]);
|
|
}
|
|
else
|
|
{
|
|
len += sprintf(buf+len,"%d %d %d %d %s %s \n",entry->index,entry->proto,entry->mask,entry->wifinum
|
|
,ip6_sprintf((struct in6_addr*)(entry->grp_addr)),ip6_sprintf((struct in6_addr*)(entry->src_addr)));
|
|
}
|
|
#else
|
|
len += sprintf(buf+len,"%d %d %d %d %u.%u.%u.%u %u.%u.%u.%u \n",entry->index,entry->proto,entry->mask,entry->wifinum
|
|
,entry->grp_addr[0],entry->grp_addr[1],entry->grp_addr[2],entry->grp_addr[3],
|
|
entry->src_addr[0],entry->src_addr[1],entry->src_addr[2],entry->src_addr[3]);
|
|
#endif
|
|
}
|
|
#ifndef TCSUPPORT_MULTICAST_SPEED
|
|
spin_unlock(&hwnat_lock);
|
|
#else
|
|
rcu_read_unlock();
|
|
#endif
|
|
|
|
*start = buf + off;
|
|
if (len < off + count)
|
|
*eof = 1;
|
|
len -= off;
|
|
if (len > count)
|
|
len = count ;
|
|
if (len <0)
|
|
len = 0;
|
|
|
|
return len;
|
|
|
|
}
|
|
static int igmp_hwnat_write_proc(struct file *file, const char *buffer, unsigned long count, void *data)
|
|
{
|
|
char buff[32],cmd[8];
|
|
int len,flag;
|
|
int flood_flag = 0;
|
|
|
|
if (count > 32)
|
|
len = 32;
|
|
else
|
|
len = count;
|
|
|
|
memset(buff,0,32);
|
|
memset(cmd,0,8);
|
|
if (copy_from_user(buff, buffer, len - 1))
|
|
return -EFAULT;
|
|
|
|
sscanf(buff,"%s %d",cmd,&flag);
|
|
#ifdef TCSUPPORT_MULTICAST_SPEED
|
|
if (memcmp(cmd,"switch",4)==0)
|
|
{
|
|
hwnat_igmp_flag &= 0xfffffffe;
|
|
hwnat_igmp_flag |= ((flag > 0? 1:0));
|
|
}
|
|
|
|
if (memcmp(cmd,"debug",4)==0)
|
|
{
|
|
hwnat_igmp_flag &= 0xfffffffd;
|
|
hwnat_igmp_flag |= (((flag > 0? 1:0)) << 1);
|
|
}
|
|
|
|
if (memcmp(cmd,"default",4)==0)
|
|
{
|
|
hwnat_igmp_flag = 1;
|
|
hwnat_age_time =3000;
|
|
}
|
|
#endif
|
|
if (memcmp(cmd,"flag",4)==0)
|
|
hwnat_igmp_flag = flag;
|
|
|
|
|
|
if (memcmp(cmd,"time",4)==0)
|
|
hwnat_age_time = flag;
|
|
|
|
#if defined(TCSUPPORT_XPON_IGMP) && defined(TCSUPPORT_MULTICAST_SPEED)
|
|
if (memcmp(cmd,"m_flood_hw",4)==0)
|
|
{
|
|
if(flag)
|
|
{
|
|
flood_flag = 1;
|
|
}
|
|
else
|
|
{
|
|
flood_flag = 0;
|
|
}
|
|
|
|
hw_igmp_flood_enable = flood_flag;
|
|
if(hwnat_set_multicast_speed_enable_hook)
|
|
{
|
|
printk("flood_flag = %d, func = %s, line = %d.\n", flood_flag, __FUNCTION__,__LINE__);
|
|
hwnat_set_multicast_speed_enable_hook(flood_flag);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return len;
|
|
}
|
|
|
|
void add_multicast_flood_hwentry(struct sk_buff* skb)
|
|
{
|
|
int index = -1;
|
|
multicast_flood_hwentry_t* entry = NULL;
|
|
multicast_flood_hwentry_t* del_entry = NULL;
|
|
multicast_flood_hwentry_t* ptr = NULL;
|
|
int flood_flag = false;
|
|
unsigned long flags;
|
|
|
|
if(0 == hw_igmp_flood_enable)
|
|
{
|
|
return ;
|
|
}
|
|
|
|
flood_flag = g_snooping_enable ;
|
|
if(flood_flag)
|
|
{
|
|
return ;
|
|
}
|
|
|
|
index = igmp_hwnat_flow_index(skb);
|
|
if(0 > index )
|
|
{
|
|
return;
|
|
}
|
|
|
|
entry = (multicast_flood_hwentry_t* )igmp_hwnat_alloc(sizeof(multicast_flood_hwentry_t));
|
|
if (NULL == entry)
|
|
{
|
|
return ;
|
|
}
|
|
spin_lock_irqsave(&hwnat_disable_snooping_lock, flags);
|
|
list_for_each_entry_safe(del_entry, ptr, &multicast_flood_hw_list, list)
|
|
{
|
|
if(del_entry->index == index)
|
|
{
|
|
list_del(&del_entry->list);
|
|
kfree(del_entry);
|
|
del_entry = NULL;
|
|
}
|
|
}
|
|
|
|
entry->index = index;
|
|
entry->port_mask = 0;
|
|
|
|
list_add_tail(&entry->list, &multicast_flood_hw_list);
|
|
|
|
spin_unlock_irqrestore(&hwnat_disable_snooping_lock, flags);
|
|
|
|
return ;
|
|
}
|
|
|
|
void update_multicast_flood_hwentry(int index, unsigned long mask)
|
|
{
|
|
multicast_flood_hwentry_t* entry = NULL;
|
|
multicast_flood_hwentry_t* ptr = NULL;
|
|
int flood_flag = false;
|
|
unsigned long flags;
|
|
|
|
if(0 == hw_igmp_flood_enable)
|
|
{
|
|
return ;
|
|
}
|
|
|
|
flood_flag = g_snooping_enable;
|
|
if(flood_flag)
|
|
{
|
|
return ;
|
|
}
|
|
|
|
if(0 >= index || 0 > mask)
|
|
{
|
|
return ;
|
|
}
|
|
|
|
spin_lock_irqsave(&hwnat_disable_snooping_lock, flags);
|
|
|
|
list_for_each_entry_safe(entry, ptr, &multicast_flood_hw_list, list)
|
|
{
|
|
if(entry->index == index)
|
|
{
|
|
entry->port_mask |= mask;
|
|
}
|
|
}
|
|
|
|
spin_unlock_irqrestore(&hwnat_disable_snooping_lock, flags);
|
|
|
|
return ;
|
|
}
|
|
|
|
multicast_flood_hwentry_t* find_multicast_flood_hwentry(int index)
|
|
{
|
|
multicast_flood_hwentry_t* entry = NULL;
|
|
multicast_flood_hwentry_t* ptr = NULL;
|
|
int find_flag = 0;
|
|
int flood_flag = false;
|
|
unsigned long flags;
|
|
|
|
if(0 == hw_igmp_flood_enable)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
flood_flag = g_snooping_enable ;
|
|
if(flood_flag)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if(0 >= index)
|
|
{
|
|
return NULL ;
|
|
}
|
|
|
|
spin_lock_irqsave(&hwnat_disable_snooping_lock, flags);
|
|
|
|
list_for_each_entry_safe(entry, ptr, &multicast_flood_hw_list, list)
|
|
{
|
|
if(entry->index == index)
|
|
{
|
|
find_flag = 1;
|
|
goto out;
|
|
}
|
|
}
|
|
out:
|
|
spin_unlock_irqrestore(&hwnat_disable_snooping_lock, flags);
|
|
if(find_flag)
|
|
{
|
|
return entry;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*update mask */
|
|
void update_multicast_flood_mask(int index)
|
|
{
|
|
int wifi_flag = false;
|
|
multicast_flood_hwentry_t* entry = NULL;
|
|
multicast_flood_hwentry_t* ptr = NULL;
|
|
int lan_flag = false;
|
|
int flood_flag = 0;
|
|
unsigned long flags;
|
|
|
|
if(0 >= index)
|
|
{
|
|
return ;
|
|
}
|
|
|
|
if(0 == hw_igmp_flood_enable)
|
|
{
|
|
return ;
|
|
}
|
|
|
|
flood_flag = g_snooping_enable ;
|
|
if(flood_flag)
|
|
{
|
|
return ;
|
|
}
|
|
|
|
spin_lock_irqsave(&hwnat_disable_snooping_lock, flags);
|
|
list_for_each_entry_safe(entry, ptr, &multicast_flood_hw_list, list)
|
|
{
|
|
if(entry->index == index)
|
|
{
|
|
lan_flag |= ((entry->port_mask & 0x0ff) > 0 ? 1 : 0);
|
|
wifi_flag |= (((entry->port_mask >> HWNAT_WLAN_IF_BASE)&0x0ff) > 0 ? 1 : 0);
|
|
if(wifi_flag)
|
|
{
|
|
if(lan_flag)
|
|
{
|
|
if(hwnat_set_rule_according_to_state_hook)
|
|
hwnat_set_rule_according_to_state_hook(index, MULTICAST_SPEED_STATE_I, entry->port_mask);
|
|
}
|
|
else
|
|
{
|
|
if(hwnat_set_rule_according_to_state_hook)
|
|
hwnat_set_rule_according_to_state_hook(index, MULTICAST_SPEED_STATE_III, entry->port_mask);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(hwnat_set_rule_according_to_state_hook)
|
|
hwnat_set_rule_according_to_state_hook(index, MULTICAST_SPEED_STATE_II, entry->port_mask);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
if(multicast_flood_is_bind_hook && (0 == multicast_flood_is_bind_hook(entry->index)))
|
|
{
|
|
list_del(&entry->list);
|
|
kfree(entry);
|
|
entry = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
spin_unlock_irqrestore(&hwnat_disable_snooping_lock, flags);
|
|
|
|
return ;
|
|
}
|
|
|
|
int clear_multicast_flood_hwentry(void)
|
|
{
|
|
multicast_flood_hwentry_t* entry = NULL;
|
|
multicast_flood_hwentry_t* ptr = NULL;
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&hwnat_disable_snooping_lock, flags);
|
|
list_for_each_entry_safe(entry, ptr, &multicast_flood_hw_list, list)
|
|
{
|
|
if (hwnat_delete_foe_entry_hook)
|
|
{
|
|
hwnat_delete_foe_entry_hook(entry->index);
|
|
}
|
|
list_del(&entry->list);
|
|
kfree(entry);
|
|
entry = NULL;
|
|
}
|
|
|
|
spin_unlock_irqrestore(&hwnat_disable_snooping_lock, flags);
|
|
return 0;
|
|
}
|
|
|
|
void igmp_hwnat_init(struct net_bridge *br)
|
|
{
|
|
struct proc_dir_entry *hwnat_proc = NULL;
|
|
INIT_LIST_HEAD(&hwnat_igmp_entry);
|
|
spin_lock_init(&hwnat_lock);
|
|
#ifdef TCSUPPORT_MULTICAST_SPEED
|
|
multicast_speed_learn_flow_hook = igmp_hwnat_learn_flow;
|
|
multicast_speed_find_entry_hook = igmp_hwnat_find_entry;
|
|
|
|
#endif
|
|
|
|
#if defined(TCSUPPORT_XPON_IGMP) && defined(TCSUPPORT_MULTICAST_SPEED)
|
|
spin_lock_init(&hwnat_disable_snooping_lock);
|
|
multicast_flood_find_entry_hook = find_multicast_flood_hwentry;
|
|
#endif
|
|
|
|
xpon_igmp_learn_flow_hook = igmp_hwnat_learn_flow;/*for what? likely no use*/
|
|
|
|
hwnat_proc = create_proc_entry("tc3162/igmp_hwnat", 0, NULL);
|
|
hwnat_proc->read_proc = igmp_hwnat_read_proc;
|
|
hwnat_proc->write_proc = igmp_hwnat_write_proc;
|
|
hwnat_br = br;
|
|
return;
|
|
}
|
|
|
|
void igmp_hwnat_fini(void)
|
|
{
|
|
igmp_hwnat_clear_flows();
|
|
remove_proc_entry("tc3162/igmp_hwnat",0);
|
|
return;
|
|
}
|
|
#else
|
|
int igmp_hwnat_get_port(struct net_bridge_port* p)
|
|
{
|
|
return 0 ;
|
|
}
|
|
int igmp_hwnat_should_deliver(struct net_bridge *br,struct net_bridge_port *port,IGMP_HWNATEntry_t* entry)
|
|
{
|
|
return 0 ;
|
|
}
|
|
int igmp_hwnat_port_mask(struct net_bridge *br,IGMP_HWNATEntry_t* entry)
|
|
{
|
|
return 0 ;
|
|
}
|
|
int igmp_hwnat_delete_entry(IGMP_HWNATEntry_t* entry)
|
|
{
|
|
return 0 ;
|
|
}
|
|
int igmp_hwnat_check_entry_valid(IGMP_HWNATEntry_t* entry)
|
|
{
|
|
return 0 ;
|
|
}
|
|
int igmp_hwnat_flow_index(struct sk_buff* skb)
|
|
{
|
|
return 0 ;
|
|
}
|
|
int igmp_hwnat_delete_foe(IGMP_HWNATEntry_t* entry)
|
|
{
|
|
return 0 ;
|
|
}
|
|
int igmp_hwnat_delete_flow(IGMP_HWNATEntry_t* entry)
|
|
{
|
|
return 0 ;
|
|
}
|
|
int igmp_hwnat_clear_flows(void)
|
|
{
|
|
return 0 ;
|
|
}
|
|
IGMP_HWNATEntry_t* igmp_hwnat_find_entry_rcu(int index)
|
|
{
|
|
return NULL ;
|
|
}
|
|
IGMP_HWNATEntry_t* igmp_hwnat_find_entry(int index)
|
|
{
|
|
return NULL ;
|
|
}
|
|
void igmp_hwnat_timer_timeout(unsigned long arg)
|
|
{
|
|
return ;
|
|
}
|
|
IGMP_HWNATEntry_t* igmp_hwnat_add_flow(struct sk_buff* skb ,int proto,unsigned char* grp_addr, unsigned char* src_addr)
|
|
{
|
|
return NULL ;
|
|
}
|
|
int igmp_hwnat_update_flow(IGMP_HWNATEntry_t* entry ,int mask)
|
|
{
|
|
return 0 ;
|
|
}
|
|
int igmp_hwnat_open_wlan(IGMP_HWNATEntry_t* entry)
|
|
{
|
|
return 0 ;
|
|
}
|
|
int igmp_hwnat_close_wlan(IGMP_HWNATEntry_t* entry)
|
|
{
|
|
return 0 ;
|
|
}
|
|
int igmp_hwnat_update_hw_nat_info(IGMP_HWNATEntry_t* entry,unsigned long mask,int state)
|
|
{
|
|
return 0 ;
|
|
}
|
|
int igmp_hwnat_update_all(struct net_bridge *br)
|
|
{
|
|
return 0 ;
|
|
}
|
|
int igmp_hwnat_is_flow_pkt(char* dst)
|
|
{
|
|
return 0 ;
|
|
}
|
|
int igmp_hwnat_learn_flow(struct sk_buff* skb)
|
|
{
|
|
return 0 ;
|
|
}
|
|
int igmp_hwnat_multicast_undrop(void)
|
|
{
|
|
return 0 ;
|
|
}
|
|
int igmp_hwnat_read_proc(char *buf, char **start, off_t off, int count,int *eof, void *data)
|
|
{
|
|
return 0 ;
|
|
}
|
|
static int igmp_hwnat_write_proc(struct file *file, const char *buffer, unsigned long count, void *data)
|
|
{
|
|
return 0 ;
|
|
}
|
|
void add_multicast_flood_hwentry(struct sk_buff* skb)
|
|
{
|
|
return ;
|
|
}
|
|
void update_multicast_flood_hwentry(int index, unsigned long mask)
|
|
{
|
|
return ;
|
|
}
|
|
multicast_flood_hwentry_t* find_multicast_flood_hwentry(int index)
|
|
{
|
|
return NULL ;
|
|
}
|
|
void update_multicast_flood_mask(int index)
|
|
{
|
|
return ;
|
|
}
|
|
int clear_multicast_flood_hwentry(void)
|
|
{
|
|
return 0 ;
|
|
}
|
|
void igmp_hwnat_init(struct net_bridge *br)
|
|
{
|
|
return ;
|
|
}
|
|
void igmp_hwnat_fini(void)
|
|
{
|
|
return ;
|
|
}
|
|
#endif
|
|
|