1
0
This repository has been archived on 2024-07-22. You can view files and clone it, but cannot push or open issues or pull requests.
TP-Link_Archer-XR500v/EN7526G_3.18Kernel_SDK/linux-3.18.21/net/bridge/ecnt_br.c
2024-07-22 01:58:46 -03:00

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