luckfox-pico-sdk/sysdrv/drv_ko/wifi/atbm/hal_apollo/mac80211/bridge.c
luckfox-eng29 8f34c2760d project:build.sh: Added fastboot support; custom modifications to U-Boot and kernel implemented using patches.
project:cfg:BoardConfig_IPC: Added fastboot BoardConfig file and firmware post-scripts, distinguishing between
the BoardConfigs for Luckfox Pico Pro and Luckfox Pico Max. project:app: Added fastboot_client and rk_smart_door
for quick boot applications; updated rkipc app to adapt to the latest media library. media:samples: Added more
usage examples. media:rockit: Fixed bugs; removed support for retrieving data frames from VPSS. media:isp:
Updated rkaiq library and related tools to support connection to RKISP_Tuner. sysdrv:Makefile: Added support for
compiling drv_ko on Luckfox Pico Ultra W using Ubuntu; added support for custom root filesystem.
sysdrv:tools:board: Updated Buildroot optional mirror sources, updated some software versions, and stored device
tree files and configuration files that undergo multiple modifications for U-Boot and kernel separately.
sysdrv:source:mcu: Used RISC-V MCU SDK with RT-Thread system, mainly for initializing camera AE during quick
boot. sysdrv:source:uboot: Added support for fastboot; added high baud rate DDR bin for serial firmware upgrades.
sysdrv:source:kernel: Upgraded to version 5.10.160; increased NPU frequency for RV1106G3; added support for
fastboot.

Signed-off-by: luckfox-eng29 <eng29@luckfox.com>
2024-10-14 09:47:04 +08:00

1210 lines
33 KiB
C
Raw Blame History

/*
****************************************************************************************
*
* Copyright (C) altobeam 2015-2018
*
* Project: wifi station bridge
*
* Description:
* wifi station bridge
*
*
****************************************************************************************
*/
#ifdef CONFIG_MAC80211_BRIDGE
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/skbuff.h>
#include <linux/etherdevice.h>
#include <linux/bitmap.h>
#include <linux/rcupdate.h>
#include <linux/export.h>
#include <net/net_namespace.h>
#include <net/ieee80211_radiotap.h>
#include <net/cfg80211.h>
#include <net/atbm_mac80211.h>
#include <asm/unaligned.h>
#include <linux/udp.h>
#include <net/ip.h>
#include "ieee80211_i.h"
#include <linux/if_arp.h>
#include <net/ip.h>
#include <net/ipx.h>
#include <linux/atalk.h>
#include <linux/udp.h>
#include <linux/if_pppox.h>
#include <linux/netdevice.h>
#pragma message("surport bridge")
int my_mod_use = 0;
typedef struct list_head atbm_qentry;
enum
{
BR0_POLICY_OPEN = 0,
BR0_POLICY_ALLOW = 1,
BR0_POLICY_DENY = 2,
};
#define BR0_HASHSIZE 32
#define NETINFO__MAX_LEN 12
struct br0_info{
u8 macaddr[6];
u8 ipaddr[4];
};
struct NET_BR0_INFO_ENTRY
{
atbm_qentry br0_list;
atbm_qentry br0_hash;
unsigned long ageing_timer;
union {
u8 br0_netinfo[NETINFO__MAX_LEN];
struct br0_info info;
}u;
};
struct NETWIFI_S_BRIDGE
{
spinlock_t br_ext_lock;
int as_policy;
atbm_qentry as_list;
atbm_qentry as_hash[BR0_HASHSIZE];
unsigned long ageing_timer;
unsigned char fast_mac[6];
unsigned char fast_ip[4];
struct NET_BR0_INFO_ENTRY *fast_entry;
unsigned char br_mac[6];
unsigned char br_ip[4];
};
static void br0_free_all_locked(struct NETWIFI_S_BRIDGE *);
static int br0_free_all(struct ieee80211_sub_if_data *sdata);
void br0_info_expire(struct ieee80211_sub_if_data *sdata);
void frame_hexdump(char *prefix, u8 *data, int len);
#if defined(CONFIG_ATBM_APOLLO_BR0_DEBUG)
#define br_printk atbm_printk_always
#else
#define br_printk(...)
#endif
/**
* br0_en_queue - add a new atbm_qentry in queue tail
* @list: new entry to be added
* @head: list head to add it before
*
* Insert a new atbm_qentry in queue tail.
*/
static inline void br0_en_queue(atbm_qentry *list, atbm_qentry *head)
{
list_add_tail(list, head);
}
static inline void br0_en_queue_head(atbm_qentry *list, atbm_qentry *head)
{
list_add(list, head);
}
/**
* br0_de_queue - deletes atbm_qentry from queue and reinitialize it.
* @list: the element to delete from the queue.
*/
static inline void br0_de_queue(atbm_qentry *list)
{
list_del_init(list);
}
/**
* br0_queue_empty - tests whether a queue is empty
* @head: the head of the queue.
*/
static inline int br0_queue_empty(atbm_qentry *head)
{
return list_empty(head);
}
#define BR0_LOCK_DESTROY(_as)
#define BR0_LOCK(_as) spin_lock_bh(&(_as)->br_ext_lock)
#define BR0_UNLOCK(_as) spin_unlock_bh(&(_as)->br_ext_lock)
int br0_attach(struct ieee80211_sub_if_data *sdata)
{
struct NETWIFI_S_BRIDGE *br0_priv;
int Num=0;
br0_priv= atbm_kmalloc(sizeof(struct NETWIFI_S_BRIDGE),GFP_KERNEL);
if (br0_priv == NULL)
{
atbm_printk_err("ERROR br0_attach\n\n");
return 0;
}
memset(br0_priv,0,sizeof(struct NETWIFI_S_BRIDGE));
spin_lock_init(&br0_priv->br_ext_lock);
INIT_LIST_HEAD(&br0_priv->as_list);
for (Num = 0; Num < BR0_HASHSIZE; Num++)
{
INIT_LIST_HEAD(&br0_priv->as_hash[Num]);
}
br0_priv->ageing_timer=jiffies;
br0_priv->as_policy = BR0_POLICY_OPEN;
sdata->bridge_priv = br0_priv;
return 1;
}
void br0_detach(struct ieee80211_sub_if_data *sdata)
{
struct NETWIFI_S_BRIDGE *br0_priv = sdata->bridge_priv;
if(br0_priv == NULL)
return;
br0_free_all(sdata);
BR0_LOCK_DESTROY(br0_priv);
atbm_kfree(sdata->bridge_priv);
sdata->bridge_priv = NULL;
}
static int BR0_HASH(const u8 *ipaddr)
{
unsigned long x;
x = ipaddr[3] ^ ipaddr[2];
return x % BR0_HASHSIZE;
}
static __inline struct NET_BR0_INFO_ENTRY *
_br0_find_netinfo(struct NETWIFI_S_BRIDGE *br0_priv, const u8 *macaddr,const u8 *ipaddr)
{
struct NET_BR0_INFO_ENTRY *br_info;
int hash;
hash = BR0_HASH(ipaddr);
list_for_each_entry(br_info, &br0_priv->as_hash[hash], br0_hash)
{
if(memcmp(br_info->u.info.macaddr, macaddr,4)==0)
return br_info;
}
return NULL;
}
static __inline struct NET_BR0_INFO_ENTRY *
_br0_find_netinfo_ip(struct NETWIFI_S_BRIDGE *br0_priv, const u8 *ipaddr)
{
struct NET_BR0_INFO_ENTRY *br_info;
int hash;
hash = BR0_HASH(ipaddr);
list_for_each_entry(br_info, &br0_priv->as_hash[hash], br0_hash)
{
if(memcmp(br_info->u.info.ipaddr, ipaddr,4)==0)
return br_info;
}
return NULL;
}
static void
_br0_free(struct NETWIFI_S_BRIDGE *br0_priv, struct NET_BR0_INFO_ENTRY *br_info)
{
// BR0_LOCK_ASSERT(br0_priv);
br0_de_queue(&br_info->br0_list);
br0_de_queue(&br_info->br0_hash);
// LIST_REMOVE(NET_BR0_INFO_ENTRY, br0_hash);
atbm_kfree(br_info);
}
static int
br0_add(struct ieee80211_sub_if_data *sdata, const u8* mac,const u8 *ipaddr)
{
struct NETWIFI_S_BRIDGE *br0_priv = sdata->bridge_priv;
struct NET_BR0_INFO_ENTRY *br_info, *new;
int hash;
br0_info_expire(sdata);
new= atbm_kmalloc(sizeof(struct NET_BR0_INFO_ENTRY),GFP_KERNEL);
if (new == NULL)
{
atbm_printk_err("ERROR br0_add\n\n");
return 0;
}
memset(new,0,sizeof(struct NET_BR0_INFO_ENTRY));
BR0_LOCK(br0_priv);
hash = BR0_HASH(ipaddr);
br_printk("atbmBr0:add new node %x:%x:%x:%x:%x:%x, ip %d.%d.%d.%d\n",
mac[0],mac[1],mac[2],mac[3],mac[4],mac[5],ipaddr[0],ipaddr[1],ipaddr[2],ipaddr[3]);
list_for_each_entry(br_info, &br0_priv->as_hash[hash], br0_hash)
{
if (memcmp(br_info->u.info.ipaddr, ipaddr,4)==0)
{
br_info->ageing_timer = jiffies;
memcpy(br_info->u.info.macaddr,mac,6);
BR0_UNLOCK(br0_priv);
atbm_kfree(new);
return EEXIST;
}
}
memcpy(new->u.info.macaddr,mac,6);
memcpy(new->u.info.ipaddr,ipaddr,4);
br0_en_queue(&new->br0_list, &br0_priv->as_list);
br0_en_queue_head(&new->br0_hash, &br0_priv->as_hash[hash]);
BR0_UNLOCK(br0_priv);
return 0;
}
/*
static int
br0_remove(struct ieee80211_sub_if_data *sdata, const u8 *ipaddr)
{
struct NETWIFI_S_BRIDGE *br0_priv = sdata->bridge_priv;
struct NET_BR0_INFO_ENTRY *br_info;
BR0_LOCK(br0_priv);
br_info = _br0_find_netinfo_ip(br0_priv, ipaddr);
if (br_info != NULL)
_br0_free(br0_priv, br_info);
BR0_UNLOCK(br0_priv);
return (br_info == NULL ? ENOENT : 0);
}
*/
static void
br0_free_all_locked(struct NETWIFI_S_BRIDGE *br0_priv)
{
struct NET_BR0_INFO_ENTRY *br_info;
BR0_LOCK(br0_priv);
while (!br0_queue_empty(&br0_priv->as_list))
{
br_info = list_first_entry(&br0_priv->as_list, struct NET_BR0_INFO_ENTRY, br0_list);
_br0_free(br0_priv, br_info);
}
br0_priv->fast_entry = NULL;
memset(br0_priv->fast_ip,0,4);
memset(br0_priv->fast_mac,0,6);
BR0_UNLOCK(br0_priv);
}
static int
br0_free_all(struct ieee80211_sub_if_data *sdata)
{
br0_free_all_locked(sdata->bridge_priv);
return 0;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#define __vlan_hdr_del() \
{\
struct ethhdr *ehdr = (struct ethhdr *)skb->data; \
if (ehdr->h_proto == __constant_htons(ETH_P_8021Q)) { \
b_vlan_frame = 1; \
vlan_hdr = *((unsigned short *)(&ehdr[1])); \
memmove(skb->data+4,skb->data,ETH_ALEN*2); \
atbm_skb_pull(skb, 4); \
ehdr = (struct ethhdr *)skb->data; \
} \
}
#define __vlan_hdr_add() \
{ \
if (b_vlan_frame) {\
atbm_skb_push(skb, 4);\
ehdr = (struct ethhdr *)skb->data; \
memmove(skb->data,skb->data+4,ETH_ALEN*2); \
ehdr->h_proto = __constant_htons(ETH_P_8021Q);\
*((unsigned short *)(&ehdr[1]))= vlan_hdr;\
}\
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static unsigned long __br0_timeout(void)
{
unsigned long timeout;
timeout = jiffies - NET_BR0_AGEING_TIME*HZ;
return timeout;
}
static int __br0_has_expired(struct NET_BR0_INFO_ENTRY * fdb)
{
if(time_before_eq(fdb->ageing_timer, __br0_timeout()))
return 1;
return 0;
}
void br0_info_expire(struct ieee80211_sub_if_data *sdata)
{
struct NETWIFI_S_BRIDGE *br0_priv = sdata->bridge_priv;
struct NET_BR0_INFO_ENTRY *br_info,*br_info_next;
if(time_before_eq(br0_priv->ageing_timer, __br0_timeout())==0){
return;
}
BR0_LOCK(br0_priv);
list_for_each_entry_safe(br_info,br_info_next, &br0_priv->as_list, br0_list)
{
if(__br0_has_expired(br_info)){
u8 *mac = br_info->u.info.macaddr;
u8 *ipaddr = br_info->u.info.ipaddr;
atbm_printk_bh("atbmBr0:<WARNING> br0_info_expire %x:%x:%x:%x:%x:%x, ip %d.%d.%d.%d\n",
mac[0],mac[1],mac[2],mac[3],mac[4],mac[5],ipaddr[0],ipaddr[1],ipaddr[2],ipaddr[3]);
_br0_free(br0_priv, br_info);
if(br_info == br0_priv->fast_entry){
br0_priv->fast_entry = NULL;
memset(br0_priv->fast_ip,0,4);
memset(br0_priv->fast_mac,0,6);
}
}
}
BR0_UNLOCK(br0_priv);
br0_priv->ageing_timer = jiffies;
}
#ifdef CONFIG_MAC80211_BRIDGE_MULTI_PORT
static bool ieee80211_need_brigde(struct ieee80211_local *local)
{
struct ieee80211_sub_if_data *temp_sdata = NULL;
u32 n_brports = 0;
rcu_read_lock();
list_for_each_entry_rcu(temp_sdata, &local->interfaces, list){
void *br_port = NULL;
struct net_device *dev = temp_sdata->dev;
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 35))
br_port = dev->br_port;
#else // (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 35))
rcu_read_lock();
br_port = rcu_dereference(dev->rx_handler_data);
rcu_read_unlock();
#endif // (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 35))
if(br_port){
n_brports++;
break;
}
}
rcu_read_unlock();
return n_brports>0?true:false;
}
#endif
static int ieee80211_brigde_network_find_and_replace(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, unsigned char *ipaddr)
{
struct NETWIFI_S_BRIDGE *br0_priv = sdata->bridge_priv;
struct NET_BR0_INFO_ENTRY * entry;
int ret =0;
BR0_LOCK(br0_priv);
entry = _br0_find_netinfo_ip(br0_priv,ipaddr);
if(entry)
{
if(!__br0_has_expired(entry))
{
// replace the destination mac address
memcpy(skb->data, entry->u.info.macaddr, ETH_ALEN);
br_printk("[%s]:atbmBr0:rx change node [%x:%x:%x:%x:%x:%x]->[%x:%x:%x:%x:%x:%x], ip %d.%d.%d.%d\n",sdata->name,
skb->data[0],skb->data[1],skb->data[2],skb->data[3],skb->data[4],skb->data[5],
entry->u.info.macaddr[0],
entry->u.info.macaddr[1],
entry->u.info.macaddr[2],
entry->u.info.macaddr[3],
entry->u.info.macaddr[4],
entry->u.info.macaddr[5],
ipaddr[0],
ipaddr[1],
ipaddr[2],
ipaddr[3]);
entry->ageing_timer = jiffies;
}
else {
atbm_printk_err("<WARNING>__br0_has_expired\n");
}
ret = 1;
}
BR0_UNLOCK(br0_priv);
return ret;
}
#define ieee80211_brigde_network_insert br0_add
int __ieee80211_brigde_change_rxhdr(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
{
struct NETWIFI_S_BRIDGE *priv=sdata->bridge_priv;
struct ethhdr *ehdr = (struct ethhdr *)skb->data;
if(skb == NULL)
return -1;
// atbm_printk_err("rxhdr in (%x),daddr[%pM],saddr[%pM]\n",ehdr->h_proto,skb->data,skb->data+6);
switch(ehdr->h_proto ){
case __constant_htons(ETH_P_IP):
{
struct iphdr* iph = (struct iphdr *)(ehdr +1);
if (!ieee80211_brigde_network_find_and_replace(sdata, skb,(unsigned char *) &iph->daddr)) {
if (*((unsigned char *)&iph->daddr + 3) == 0xff) {
// L2 is unicast but L3 is broadcast, make L2 bacome broadcast
memset(skb->data, 0xff, ETH_ALEN);
}
else {
#if defined(CONFIG_ATBM_APOLLO_BR0_DEBUG)
u8 * ipaddr = (unsigned char *) &iph->daddr;
// forward unknow IP packet to upper TCP/IP
br_printk("atbm_br0: Replace DA with BR's MAC [%x:%x:%x:%x:%x:%x]->[%x:%x:%x:%x:%x:%x], ip %d.%d.%d.%d\n",
skb->data[0],skb->data[1],skb->data[2],skb->data[3],skb->data[4],skb->data[5],
priv->br_mac[0],
priv->br_mac[1],
priv->br_mac[2],
priv->br_mac[3],
priv->br_mac[4],
priv->br_mac[5],
ipaddr[0],
ipaddr[1],
ipaddr[2],
ipaddr[3]);
#endif
memcpy(skb->data, priv->br_mac, ETH_ALEN);
}
}
break;
}
case __constant_htons(ETH_P_ARP):
{
struct arphdr *arp = (struct arphdr *)(ehdr +1);
__be32 src_ipaddr, tgt_ipaddr;
char *sip,*tip;
char *src_devaddr, *tgt_devaddr;
char * arpptr = (char *)(arp + 1);
src_devaddr = arpptr;
arpptr += ETH_ALEN;
sip = arpptr;
memcpy(&src_ipaddr, arpptr, sizeof(u32));
arpptr += sizeof(u32);
tgt_devaddr = arpptr;
arpptr += ETH_ALEN;
tip = arpptr;
memcpy(&tgt_ipaddr, arpptr, sizeof(u32));
atbm_printk_err("arp:ehdr[%pM][%pM],src_devaddr[%pM],tgt_devaddr[%pM],src_ipaddr[%d.%d.%d.%d],tgt_ipaddr[%d.%d.%d.%d]\n",skb->data,
skb->data+6,src_devaddr,tgt_devaddr,sip[0],sip[1],sip[2],sip[3],tip[0],tip[1],tip[2],tip[3]);
ieee80211_brigde_network_find_and_replace(sdata, skb,(unsigned char *) &tgt_ipaddr);
//if(memcmp(tgt_devaddr, skb->data, ETH_ALEN)){
//record sourc
//frame_hexdump("\nrx beforce replace ARP:", ((char *)(arp + 1))-2,22);
// change to ARP target mac address to Lookup result
memcpy(tgt_devaddr, skb->data, ETH_ALEN);
// frame_hexdump("\nrx after replace ARP:", ((char *)(arp + 1))-2,22);
//}
break;
}
default:
break;
}
// atbm_printk_err("rxhdr in (%x),daddr[%pM],saddr[%pM]\n",ehdr->h_proto,skb->data,skb->data+6);
return 0;
}
int ieee80211_brigde_hash_update(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
{
struct ethhdr *ehdr = (struct ethhdr *)skb->data;
if(skb == NULL)
return -1;
switch(ehdr->h_proto ){
case __constant_htons(ETH_P_IP):
{
struct iphdr* iph = (struct iphdr *)(ehdr +1);
if(((unsigned char*)(iph) + (iph->ihl<<2)) >= (skb->data + ETH_HLEN + skb->len))
{
atbm_printk_err(" IP packet len error!!\n");
return -1;
}
//some muticast with source IP is all zero, maybe other case is illegal
//in class A, B, C, host address is all zero or all one is illegal
if (iph->saddr == 0)
break;
//record source IP address and , source mac address into db
ieee80211_brigde_network_insert(sdata, skb->data+ETH_ALEN, (u8 *)&iph->saddr);
break;
}
case __constant_htons(ETH_P_ARP):
{
struct arphdr *arp = (struct arphdr *)(ehdr +1);
__be32 src_ipaddr, tgt_ipaddr;
char *src_devaddr,*tgt_devaddr;
char *arpptr = (char *)(arp + 1);
src_devaddr = arpptr;
arpptr += ETH_ALEN;
memcpy(&src_ipaddr, arpptr, sizeof(u32));
arpptr += sizeof(u32);
tgt_devaddr = arpptr;
arpptr += ETH_ALEN;
memcpy(&tgt_ipaddr, arpptr, sizeof(u32));
if(arp->ar_pro != __constant_htons(ETH_P_IP))
{
atbm_printk_err("BR0: arp protocol unknown (%4x)!\n", htons(arp->ar_pro));
return -1;
}
//some muticast with source IP is all zero,
if (src_ipaddr == 0)
break;
//if(memcmp(src_devaddr,NETDEV_HWADDR(sdata),6)){
//record sourc
//frame_hexdump("\nbeforce replace ARP:", ((char *)(arp + 1))-2,22);
// change to ARP sender mac address to wlan STA address
ieee80211_brigde_network_insert(sdata, src_devaddr, (const u8 *)&src_ipaddr);
atbm_printk_err("%s 2: src_devaddr[%pM]<5D><>tgt_devaddr[%pM] \n",__func__,src_devaddr,tgt_devaddr);
memcpy(src_devaddr, NETDEV_HWADDR(sdata), ETH_ALEN);
//frame_hexdump("\nafter replace ARP:", ((char *)(arp + 1))-2,22);
//}
//ieee80211_brigde_network_insert(sdata, skb->data+ETH_ALEN, &src_ipaddr);
break;
}
default:
break;
}
return 0;
}
int ieee80211_brigde_change_rxhdr(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
{
struct ethhdr *ehdr = (struct ethhdr *)skb->data;
int b_vlan_frame=0;
int ret=0;
u16 vlan_hdr=0;
void *br_port = NULL;
struct NETWIFI_S_BRIDGE *br0_priv = sdata->bridge_priv;
int need_look=1;
if(br0_priv==0)
return -2;
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 35))
br_port =sdata->dev->br_port;
#else // (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 35))
rcu_read_lock();
br_port = rcu_dereference(sdata->dev->rx_handler_data);
rcu_read_unlock();
#endif // (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 35))
if(br_port &&
( (*(u32 *)br0_priv->br_mac) == 0 && (*(u16 *)(br0_priv->br_mac+4)) == 0 )) {
atbm_printk_bh("Re-init br0_netdev_open() due to br_mac==0!\n");
br0_netdev_open(sdata->dev);
}
if( br_port ){
#ifdef CONFIG_MAC80211_BRIDGE_MULTI_PORT
if(ieee80211_need_brigde(sdata->local) == false){
return 0;
}
#endif
if((sdata->vif.type == NL80211_IFTYPE_STATION) &&
(!is_multicast_ether_addr(ehdr->h_dest)))
{
__vlan_hdr_del();
/*
* This function look up the destination network address from
* the NAT2.5 database. Return value = -1 means that the
* corresponding network protocol is NOT support.
*/
if (ehdr->h_proto == __constant_htons(ETH_P_IP)){
struct iphdr* iph = (struct iphdr *)(ehdr +1);
if(br0_priv->fast_entry &&
!memcmp(br0_priv->fast_ip, &iph->daddr, 4)){
memcpy(skb->data, br0_priv->fast_mac, ETH_ALEN);
br0_priv->fast_entry->ageing_timer = jiffies;
need_look = 0;
}
}
if(need_look) {
ret = __ieee80211_brigde_change_rxhdr(sdata, skb);
}
__vlan_hdr_add();
}
}
///
#if 1
if(memcmp(br0_priv->br_mac,ehdr->h_source,6)==0){
#if 0
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_sub_if_data *deliver_sdata;
struct ethhdr *ehdr_deliver = NULL;
struct sk_buff * deliver_skb = NULL;
list_for_each_entry_rcu(deliver_sdata, &local->interfaces, list) {
if (!ieee80211_sdata_running(deliver_sdata))
continue;
if (deliver_sdata==sdata)
continue;
if ((deliver_sdata->vif.type != NL80211_IFTYPE_AP)&&
(deliver_sdata->vif.type != NL80211_IFTYPE_STATION)){
continue;
}
/*
* send multicast frames both to higher layers in
* local net stack and back to the wireless medium
*/
deliver_skb = atbm_skb_copy(skb, GFP_ATOMIC);
deliver_skb->dev = deliver_sdata->dev;
ehdr_deliver = (struct ethhdr *) deliver_skb->data;
memcpy(ehdr_deliver->h_source,deliver_sdata->vif.addr,6);
if (!deliver_skb && net_ratelimit())
atbm_printk_err("%s: failed to clone "
"multicast frame\n", deliver_sdata->name);
break;
}
if (deliver_skb) {
/* send to wireless media */
deliver_skb->protocol = htons(ETH_P_802_3);
skb_reset_network_header(deliver_skb);
skb_reset_mac_header(deliver_skb);
//printk("deliver skb iftype22 %d \n",deliver_sdata->vif.type);
atbm_dev_queue_xmit(deliver_skb);
}
}
#endif
atbm_printk_err("BR0[%s]: sourceHw==br0Hw (%2x:%2x)!h_proto %x,iftype %d\n",sdata->name,br0_priv->br_mac[4],br0_priv->br_mac[5],ehdr->h_proto ,sdata->vif.type);
//frame_hexdump("dump:",ehdr,16);
return -1;
}
#endif
return ret;
}
void *ieee80211_brigde_updata_fast_info(struct ieee80211_sub_if_data *sdata,unsigned char *mac,unsigned char *ipaddr)
{
struct NETWIFI_S_BRIDGE *br0_priv = sdata->bridge_priv;
struct NET_BR0_INFO_ENTRY * entry;
entry = _br0_find_netinfo_ip(br0_priv,ipaddr);
if(entry){
//if ip addr is same ,but mac addr is not the same ,return NULL;
if(memcmp(mac,entry->u.info.macaddr,6))
entry=NULL;
}
return entry;
}
/////////////////////////////////////////////////////////////////////////
//#include "dhcpd.h"
#define DHCP_MAGIC 0x63825363
#define BROADCAST_FLAG 0x8000
struct dhcpMessage {
u_int8_t op;
u_int8_t htype;
u_int8_t hlen;
u_int8_t hops;
u_int32_t xid;
u_int16_t secs;
u_int16_t flags;
u_int32_t ciaddr;
u_int32_t yiaddr;
u_int32_t siaddr;
u_int32_t giaddr;
u_int8_t chaddr[16];
u_int8_t sname[64];
u_int8_t file[128];
u_int32_t cookie;
u_int8_t options[308]; /* 312 - cookie */
};
int ieee80211_is_dhcp_frame(struct sk_buff *skb);
void ieee80211_tx_set_dhcp_bcast_flag(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
{
//struct ethhdr *ehdr = (struct ethhdr *)skb->data;
if(skb == NULL)
return;
if(ieee80211_is_dhcp_frame(skb)) // DHCP request
{
struct iphdr* iph = (struct iphdr *)(skb->data + ETH_HLEN);
struct udphdr *udph = (struct udphdr *)((u8 *)iph + (iph->ihl << 2));
struct dhcpMessage *dhcph = (struct dhcpMessage *)((u8 *)udph + sizeof(struct udphdr));
if(dhcph->cookie == __constant_htonl(DHCP_MAGIC)) // match magic word
{
if(!(dhcph->flags & htons(BROADCAST_FLAG))) // if not broadcast
{
register int sum = 0;
dhcph->flags |= htons(BROADCAST_FLAG);
// recalculate checksum
sum = ~(udph->check) & 0xffff;
sum += dhcph->flags;
while(sum >> 16)
sum = (sum & 0xffff) + (sum >> 16);
udph->check = ~sum;
}
}
}
}
/////////////////////////////////////////////////////////////////////////
struct ieee80211_sub_if_data *ieee80211_brigde_sdata_check(struct ieee80211_local *local,struct sk_buff **pskb,
struct ieee80211_sub_if_data *source_sdata)
{
struct net_device *dev = source_sdata->dev;
struct sk_buff *skb = *pskb;
struct ethhdr *ehdr = (struct ethhdr *)skb->data;
struct ieee80211_sub_if_data *sdata = source_sdata;
struct ieee80211_sub_if_data *sta_sdata = NULL;
struct ieee80211_sub_if_data *temp_sdata = NULL;
struct NETWIFI_S_BRIDGE *br0_priv = NULL;
rcu_read_lock();
if(memcmp(ehdr->h_dest,ehdr->h_source,ETH_ALEN)){
goto exit_rcu;
}
if(rcu_dereference(dev->rx_handler_data) == NULL)
goto exit_rcu;
list_for_each_entry_rcu(temp_sdata, &local->interfaces, list){
if(temp_sdata->vif.type != NL80211_IFTYPE_STATION){
continue;
}
if(temp_sdata->u.mgd.associated == NULL){
continue;
}
sta_sdata = temp_sdata;
break;
}
if(sta_sdata == NULL)
goto exit_rcu;
if(atbm_skb_shared(skb) || atbm_skb_cloned(skb)){
skb = atbm_skb_copy(*pskb, GFP_ATOMIC);
if(skb == NULL){
atbm_printk_err("%s:skb cope err\n",__func__);
goto exit_rcu;
}
dev_kfree_skb(*pskb);
*pskb = skb;
ehdr = (struct ethhdr *)skb->data;
}
atbm_skb_linearize(skb);
br0_priv = sta_sdata->bridge_priv;
// __vlan_hdr_del();
BR0_LOCK(br0_priv);
if (ehdr->h_proto == __constant_htons(ETH_P_IP)){
struct iphdr* iph = (struct iphdr *)(ehdr +1);
u8* daip = (u8 *)(&iph->daddr);
u8* saip = (u8 *)(&iph->saddr);
struct NET_BR0_INFO_ENTRY * entry = NULL;
struct sta_info *sta = NULL;
if(!is_multicast_ether_addr(ehdr->h_dest)){
entry = _br0_find_netinfo_ip(br0_priv,(const u8 *)daip);
if(entry){
sta = rcu_dereference(local->sta_hash[STA_HASH(entry->u.info.macaddr)]);
while (sta) {
if (memcmp(sta->sta.addr, entry->u.info.macaddr, ETH_ALEN) == 0)
break;
sta = rcu_dereference(sta->hnext);
}
}
if(sta == NULL){
sta = rcu_dereference(local->sta_hash[STA_HASH(ehdr->h_dest)]);
while (sta) {
if (memcmp(sta->sta.addr, ehdr->h_dest, ETH_ALEN) == 0)
break;
sta = rcu_dereference(sta->hnext);
}
}
if(sta == NULL){
sdata = sta_sdata;
memcpy(ehdr->h_dest,sdata->u.mgd.bssid,6);
}else {
sdata = sta->sdata;
memcpy(ehdr->h_dest,sta->sta.addr,6);
}
}else {
}
atbm_printk_debug("%s:dmac[%pM],smac[%pM]\n",__func__,ehdr->h_dest,ehdr->h_source);
atbm_printk_debug("%s:dip[%d:%d:%d:%d],sip[%d:%d:%d:%d]\n",__func__,daip[0],daip[1],daip[2],daip[3],saip[0],saip[1],saip[2],saip[3]);
atbm_printk_debug("%s:[%s]->[%s],[%pM],sta[%p],entry[%p]\n",__func__,source_sdata->name,sdata->name,ehdr->h_dest,sta,entry);
}else if(ehdr->h_proto == __constant_htons(ETH_P_ARP)){
struct arphdr *arp = (struct arphdr *)(ehdr +1);
const char *arpptr = (char *)(arp + 1);
u8* damac = (u8 *)(arpptr+ETH_ALEN+4);
u8* daip = (u8 *)(arpptr + ETH_ALEN*2+4);
struct NET_BR0_INFO_ENTRY * entry = _br0_find_netinfo_ip(br0_priv,(const u8 *)daip);
u8 broadcast[6] = {0xff,0xff,0xff,0xff,0xff,0xff};
struct sta_info *sta = NULL;
if(memcmp(ehdr->h_dest,ehdr->h_source,6))
goto exit_unlock;
/*
*ip entry find sta
*/
if(entry){
sta = rcu_dereference(local->sta_hash[STA_HASH(entry->u.info.macaddr)]);
while (sta) {
if (memcmp(sta->sta.addr, entry->u.info.macaddr, ETH_ALEN) == 0)
break;
sta = rcu_dereference(sta->hnext);
}
}
/*
*da mac find sta
*/
if(sta == NULL){
sta = rcu_dereference(local->sta_hash[STA_HASH(damac)]);
while (sta) {
if (memcmp(sta->sta.addr, damac, ETH_ALEN) == 0)
break;
sta = rcu_dereference(sta->hnext);
}
}
if(sta == NULL){
sdata = sta_sdata;
damac = (u8 *)(arpptr+ETH_ALEN+4);
atbm_printk_err("%s:mac[%pM] not in sta list\n",__func__,damac);
}else {
sdata = sta->sdata;
damac = sta->sta.addr;
}
if(arp->ar_op == htons(ARPOP_REQUEST))
memcpy(ehdr->h_dest,broadcast,6);
else if(arp->ar_op == htons(ARPOP_REPLY))
memcpy(ehdr->h_dest,damac,6);
atbm_printk_debug("arp:dip[%d:%d:%d:%d],mac[%pM]\n",daip[0],daip[1],daip[2],daip[3],damac);
}else if(ehdr->h_proto == __constant_htons(ETH_P_PAE)){
struct sta_info *sta = NULL;
sta = rcu_dereference(local->sta_hash[STA_HASH(ehdr->h_dest)]);
while (sta) {
if (memcmp(sta->sta.addr, ehdr->h_dest, ETH_ALEN) == 0)
break;
sta = rcu_dereference(sta->hnext);
}
if(sta == NULL){
goto exit_unlock;
}
if(sta->sdata == source_sdata){
goto exit_unlock;
}
memcpy(ehdr->h_dest,sta->sta.addr,6);
sdata = sta->sdata;
}
exit_unlock:
BR0_UNLOCK(br0_priv);
exit_rcu:
rcu_read_unlock();
return sdata;
}
int ieee80211_brigde_change_txhdr(struct ieee80211_sub_if_data *sdata, struct sk_buff **pskb)
{
struct net_device *dev = sdata->dev;
struct sk_buff *skb = *pskb;
struct ethhdr *ehdr = (struct ethhdr *)skb->data;
struct NETWIFI_S_BRIDGE *br0_priv = sdata->bridge_priv;
void *br_port = NULL;
u16 vlan_hdr=0;
int b_vlan_frame = 0;
int need_insert =1;
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 35))
br_port = dev->br_port;
#else // (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 35))
rcu_read_lock();
br_port = rcu_dereference(dev->rx_handler_data);
rcu_read_unlock();
#endif // (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 35))
if(br_port)
{
#ifdef CONFIG_MAC80211_BRIDGE_MULTI_PORT
if(ieee80211_need_brigde(sdata->local) == false){
return 0;
}
#endif
if(br0_priv ==NULL)
return 0;
/*
printk("tx ehdr->h_source %x:%x:%x:%x:%x:%x h_proto %x (%x %x)\n",ehdr->h_source[0],
ehdr->h_source[1],
ehdr->h_source[2],
ehdr->h_source[3],
ehdr->h_source[4],
ehdr->h_source[5],ehdr->h_proto ,ETH_P_8021Q,ETH_P_IP
);
printk("tx if %x:%x:%x:%x:%x:\n",!is_multicast_ether_addr(ehdr->h_dest),
memcmp(ehdr->h_source, br0_priv->br_mac, ETH_ALEN) ,
!memcmp(ehdr->h_source, br0_priv->fast_mac,ETH_ALEN) ,
ehdr->h_proto != __constant_htons(ETH_P_8021Q) ,
br0_priv->fast_entry);
*/
if(atbm_skb_shared(skb) || atbm_skb_cloned(skb)){
skb = atbm_skb_copy(*pskb, GFP_ATOMIC);
if(skb == NULL){
atbm_printk_err("%s:skb cope err\n",__func__);
return -1;
}
dev_kfree_skb(*pskb);
*pskb = skb;
ehdr = (struct ethhdr *)skb->data;
}
atbm_skb_linearize(skb);
ehdr = (struct ethhdr *)skb->data;
#if 0
if ((is_multicast_ether_addr(ehdr->h_dest))||
(memcmp(ehdr->h_source, br0_priv->br_mac, ETH_ALEN) &&
memcmp(ehdr->h_source, br0_priv->fast_mac,ETH_ALEN)) ||
/*ehdr->h_proto != __constant_htons(ETH_P_IP) ||*/
!br0_priv->fast_entry)
#endif//0
{
__vlan_hdr_del();
if (ehdr->h_proto == __constant_htons(ETH_P_IP)) {
struct iphdr* iph = (struct iphdr *)(ehdr +1);
if((memcmp(ehdr->h_source, br0_priv->fast_mac,ETH_ALEN)==0)
&&(memcmp(br0_priv->fast_ip, &iph->saddr, 4)==0)){
if (br0_priv->fast_entry) {
br0_priv->fast_entry->ageing_timer = jiffies;
need_insert = 0;
}
else {
memset(br0_priv->fast_mac, 0, ETH_ALEN);
memset(br0_priv->fast_ip, 0, 4);
}
}
else {
br0_priv->fast_entry = (struct NET_BR0_INFO_ENTRY *)ieee80211_brigde_updata_fast_info(sdata,ehdr->h_source,(unsigned char *)(&iph->saddr));
if (br0_priv->fast_entry != NULL) {
memcpy(br0_priv->fast_mac, ehdr->h_source, ETH_ALEN);
memcpy(br0_priv->fast_ip, &iph->saddr, 4);
br0_priv->fast_entry->ageing_timer = jiffies;
atbm_printk_debug("ieee80211_brigde_change_txhdr: fast_mac [%x:%x:%x:%x:%x:%x], ip %d.%d.%d.%d\n",
br0_priv->fast_mac[0],
br0_priv->fast_mac[1],
br0_priv->fast_mac[2],
br0_priv->fast_mac[3],
br0_priv->fast_mac[4],
br0_priv->fast_mac[5],
br0_priv->fast_ip[0],
br0_priv->fast_ip[1],
br0_priv->fast_ip[2],
br0_priv->fast_ip[3]);
need_insert = 0;
}
}
}
if(need_insert){
/*
<09><><EFBFBD>¹<EFBFBD>ϣ<EFBFBD><CFA3>
*/
if(ieee80211_brigde_hash_update(sdata, skb) <0) {
atbm_printk_err("ieee80211_brigde_change_txhdr: ieee80211_brigde_change_txhdr fail!\n");
return -1;
}
}
__vlan_hdr_add();
}
//if SA == br_mac && skb== IP => copy SIP to br_ip
if (!memcmp(ehdr->h_source, br0_priv->br_mac, ETH_ALEN) &&
(ehdr->h_proto == __constant_htons(ETH_P_IP)))
memcpy(br0_priv->br_ip, skb->data+WLAN_ETHHDR_LEN+12, 4);
if(memcmp(ehdr->h_dest,sdata->dev->dev_addr,6)==0){
//frame_hexdump("BR0:DA error,drop\n",ehdr,14);
atbm_printk_err("may error![%s]\n",sdata->name);
return 0;
}
//change source mac to station macaddr
if(memcmp(ehdr->h_source, sdata->dev->dev_addr, ETH_ALEN) != 0){
memcpy(ehdr->h_source, sdata->dev->dev_addr, ETH_ALEN);
ieee80211_tx_set_dhcp_bcast_flag(sdata, skb);
}
}
return 0;
}
void ieee80211_brigde_flush(struct ieee80211_sub_if_data *sdata)
{
struct NETWIFI_S_BRIDGE *br0_priv = sdata->bridge_priv;
if(br0_priv ==NULL)
return;
br0_free_all_locked(br0_priv);
}
#define CONFIG_BR_EXT_BRNAME "br0"
static struct net_device *ieee80211_get_br_dev(struct net_device *netdev)
{
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,9,0))
return netdev->master;
#else
return netdev_master_upper_dev_get_rcu(netdev);
#endif
}
void br0_netdev_open(struct net_device *netdev)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(netdev);
struct NETWIFI_S_BRIDGE *br0_priv = sdata->bridge_priv;
if(br0_priv ==NULL)
return;
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 35))
rcu_read_lock();
#endif // (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 35))
{
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 35))
if (netdev->br_port)
#else // (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 35))
if (rcu_dereference(netdev->rx_handler_data))
#endif // (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 35))
{
struct net_device *br_netdev;
#if 1
br_netdev = ieee80211_get_br_dev(netdev);
if(br_netdev == NULL){
atbm_printk_err("br_netdev failed!");
}else {
memcpy(br0_priv->br_mac, br_netdev->dev_addr, ETH_ALEN);
}
#else
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24))
br_netdev = dev_get_by_name(CONFIG_BR_EXT_BRNAME);
#else // (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24))
struct net *devnet = dev_net(netdev);
br_netdev = dev_get_by_name(devnet, CONFIG_BR_EXT_BRNAME);
#endif // (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24))
if (br_netdev) {
memcpy(br0_priv->br_mac, br_netdev->dev_addr, ETH_ALEN);
dev_put(br_netdev);
} else {
printk("%s()-%d: dev_get_by_name(%s) failed!", __FUNCTION__, __LINE__, CONFIG_BR_EXT_BRNAME);
}
#endif
}
else {
atbm_printk_err("%s()-%d: dev_get_by_name(%s)\n", __FUNCTION__, __LINE__, CONFIG_BR_EXT_BRNAME);
}
}//
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 35))
rcu_read_unlock();
#endif // (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 35))
}
#endif //CONFIG_MAC80211_BRIDGE