Added functionality into capwap data channel kernel module

This commit is contained in:
vemax78
2014-12-23 21:12:25 +01:00
parent e2dea6b3de
commit 33ea96d9f5
35 changed files with 2172 additions and 541 deletions

View File

@ -7,6 +7,7 @@ smartcapwap-y := \
netlinkapp.o \
capwap.o \
capwap_private.o \
station.o \
socket.o \
iface.o

View File

@ -1,5 +1,7 @@
#include "config.h"
#include <linux/module.h>
#include <linux/if_ether.h>
#include <linux/etherdevice.h>
#include <linux/ieee80211.h>
#include "socket.h"
#include "capwap.h"
@ -12,6 +14,12 @@
/* */
union capwap_addr sc_localaddr;
/* Ethernet-II snap header (RFC1042 for most EtherTypes) */
static unsigned char sc_rfc1042_header[] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
/* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */
static unsigned char sc_bridge_tunnel_header[] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 };
/* */
static void sc_capwap_fragment_free(struct sc_capwap_fragment* fragment) {
TRACEKMOD("### sc_capwap_fragment_free\n");
@ -32,7 +40,6 @@ static void sc_capwap_fragment_free(struct sc_capwap_fragment* fragment) {
/* */
static void sc_capwap_defrag_evictor(struct sc_capwap_session* session, ktime_t now) {
ktime_t delta;
unsigned long flags;
struct sc_capwap_fragment* fragment;
TRACEKMOD("### sc_capwap_defrag_evictor\n");
@ -45,7 +52,7 @@ static void sc_capwap_defrag_evictor(struct sc_capwap_session* session, ktime_t
/* Remove last old fragment */
if (!list_empty(&session->fragments.lru_list)) {
spin_lock_irqsave(&session->fragments.lock, flags);
spin_lock(&session->fragments.lock);
fragment = list_first_entry(&session->fragments.lru_list, struct sc_capwap_fragment, lru_list);
if (fragment) {
@ -58,7 +65,7 @@ static void sc_capwap_defrag_evictor(struct sc_capwap_session* session, ktime_t
}
}
spin_unlock_irqrestore(&session->fragments.lock, flags);
spin_unlock(&session->fragments.lock);
}
}
@ -104,7 +111,6 @@ static struct sk_buff* sc_capwap_reasm(struct sc_capwap_fragment* fragment) {
/* */
static struct sk_buff* sc_capwap_defrag(struct sc_capwap_session* session, struct sk_buff* skb) {
unsigned long flags;
uint16_t headersize;
uint16_t frag_id;
struct sk_buff* prev;
@ -132,7 +138,7 @@ static struct sk_buff* sc_capwap_defrag(struct sc_capwap_session* session, struc
cb->frag_length = skb->len - headersize;
/* */
spin_lock_irqsave(&session->fragments.lock, flags);
spin_lock(&session->fragments.lock);
/* Get fragment */
fragment = &session->fragments.queues[frag_id % CAPWAP_FRAGMENT_QUEUE];
@ -219,18 +225,158 @@ static struct sk_buff* sc_capwap_defrag(struct sc_capwap_session* session, struc
}
}
spin_unlock_irqrestore(&session->fragments.lock, flags);
spin_unlock(&session->fragments.lock);
return skb_defrag;
error2:
spin_unlock_irqrestore(&session->fragments.lock, flags);
spin_unlock(&session->fragments.lock);
error:
kfree_skb(skb);
return NULL;
}
/* */
static unsigned int sc_capwap_80211_hdrlen(__le16 fc) {
unsigned int hdrlen = 24;
TRACEKMOD("### sc_capwap_80211_hdrlen\n");
if (ieee80211_is_data(fc)) {
if (ieee80211_has_a4(fc)) {
hdrlen = 30;
}
if (ieee80211_is_data_qos(fc)) {
hdrlen += IEEE80211_QOS_CTL_LEN;
if (ieee80211_has_order(fc)) {
hdrlen += IEEE80211_HT_CTL_LEN;
}
}
} else if (ieee80211_is_ctl(fc)) {
if ((fc & cpu_to_le16(0x00E0)) == cpu_to_le16(0x00C0)) {
hdrlen = 10;
} else {
hdrlen = 16;
}
}
return hdrlen;
}
/* */
int sc_capwap_8023_to_80211(struct sk_buff* skb, const uint8_t* bssid) {
uint16_t hdrlen;
int head_need;
struct ieee80211_hdr hdr;
int skip_header_bytes;
uint8_t* encaps_data;
int encaps_len;
struct ethhdr* eh = (struct ethhdr*)skb->data;
uint16_t ethertype = ntohs(eh->h_proto);
TRACEKMOD("### sc_capwap_8023_to_80211\n");
/* IEEE 802.11 header */
hdrlen = 24;
hdr.frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA | IEEE80211_FCTL_FROMDS);
memcpy(hdr.addr1, eh->h_dest, ETH_ALEN);
memcpy(hdr.addr2, bssid, ETH_ALEN);
memcpy(hdr.addr3, eh->h_source, ETH_ALEN);
hdr.duration_id = 0;
hdr.seq_ctrl = 0;
/* */
skip_header_bytes = ETH_HLEN;
if ((ethertype == ETH_P_AARP) || (ethertype == ETH_P_IPX)) {
encaps_data = sc_bridge_tunnel_header;
encaps_len = sizeof(sc_bridge_tunnel_header);
skip_header_bytes -= 2;
} else if (ethertype >= ETH_P_802_3_MIN) {
encaps_data = sc_rfc1042_header;
encaps_len = sizeof(sc_rfc1042_header);
skip_header_bytes -= 2;
} else {
encaps_data = NULL;
encaps_len = 0;
}
/* Remove IEEE 802.3 header */
skb_pull(skb, skip_header_bytes);
/* Check headroom */
head_need = hdrlen + encaps_len - skb_headroom(skb);
if ((head_need > 0) || skb_cloned(skb)) {
head_need = max(head_need, 0);
if (head_need) {
skb_orphan(skb);
}
TRACEKMOD("*** Expand headroom skb of: %d\n", head_need);
if (pskb_expand_head(skb, head_need, 0, GFP_ATOMIC)) {
return -ENOMEM;
}
skb->truesize += head_need;
}
/* Add LLC header */
if (encaps_data) {
memcpy(skb_push(skb, encaps_len), encaps_data, encaps_len);
}
/* Add IEEE 802.11 header */
memcpy(skb_push(skb, hdrlen), &hdr, hdrlen);
skb_reset_mac_header(skb);
return 0;
}
/* */
int sc_capwap_80211_to_8023(struct sk_buff* skb) {
struct ieee80211_hdr* hdr = (struct ieee80211_hdr*)skb->data;
uint16_t hdrlen;
uint16_t ethertype;
uint8_t* payload;
uint8_t dst[ETH_ALEN];
uint8_t src[ETH_ALEN] __aligned(2);
TRACEKMOD("### sc_capwap_80211_to_8023\n");
/* */
hdrlen = sc_capwap_80211_hdrlen(hdr->frame_control);
memcpy(dst, ieee80211_get_DA(hdr), ETH_ALEN);
memcpy(src, ieee80211_get_SA(hdr), ETH_ALEN);
/* */
if (!pskb_may_pull(skb, hdrlen + 8)) {
return -1;
}
/* */
payload = skb->data + hdrlen;
ethertype = (payload[6] << 8) | payload[7];
if (likely((ether_addr_equal(payload, sc_rfc1042_header) && (ethertype != ETH_P_AARP) && (ethertype != ETH_P_IPX)) || ether_addr_equal(payload, sc_bridge_tunnel_header))) {
skb_pull(skb, hdrlen + 6);
memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN);
memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN);
} else {
struct ethhdr *ehdr;
__be16 len;
skb_pull(skb, hdrlen);
len = htons(skb->len);
ehdr = (struct ethhdr *) skb_push(skb, sizeof(struct ethhdr));
memcpy(ehdr->h_dest, dst, ETH_ALEN);
memcpy(ehdr->h_source, src, ETH_ALEN);
ehdr->h_proto = len;
}
return 0;
}
/* */
int sc_capwap_bind(union capwap_addr* sockaddr) {
int ret;
@ -275,13 +421,12 @@ void sc_capwap_freesession(struct sc_capwap_session* session) {
/* */
uint16_t sc_capwap_newfragmentid(struct sc_capwap_session* session) {
uint16_t fragmentid;
unsigned long flags;
TRACEKMOD("### sc_capwap_newfragmentid\n");
spin_lock_irqsave(&session->fragmentid_lock, flags);
spin_lock(&session->fragmentid_lock);
fragmentid = session->fragmentid++;
spin_unlock_irqrestore(&session->fragmentid_lock, flags);
spin_unlock(&session->fragmentid_lock);
return fragmentid;
}
@ -454,8 +599,6 @@ int sc_capwap_parsingpacket(struct sc_capwap_session* session, const union capwa
}
}
/* Parsing complete */
kfree_skb(skb);
return 0;
}
@ -464,6 +607,7 @@ int sc_capwap_parsingpacket(struct sc_capwap_session* session, const union capwa
/* */
int sc_capwap_forwarddata(struct sc_capwap_session* session, uint8_t radioid, uint8_t binding, struct sk_buff* skb, uint32_t flags, struct sc_capwap_radio_addr* radioaddr, int radioaddrlength, struct sc_capwap_wireless_information* winfo, int winfolength) {
int err;
int size;
int length;
int reserve;
@ -482,7 +626,7 @@ int sc_capwap_forwarddata(struct sc_capwap_session* session, uint8_t radioid, ui
reserve = sizeof(struct sc_capwap_header) + radioaddrlength + winfolength;
if (skb_is_nonlinear(skb) || (headroom < reserve)) {
printk("*** Expand socket buffer\n");
clone = skb_copy_expand(skb, max_t(int, headroom, reserve), skb_tailroom(skb), GFP_KERNEL);
clone = skb_copy_expand(skb, max(headroom, reserve), skb_tailroom(skb), GFP_KERNEL);
if (!clone) {
printk("*** Unable to expand socket buffer\n");
return -ENOMEM;
@ -551,7 +695,9 @@ int sc_capwap_forwarddata(struct sc_capwap_session* session, uint8_t radioid, ui
}
/* Send packet */
if (sc_socket_send(SOCKET_UDP, (uint8_t*)header, (size + length), &session->peeraddr) < 0) {
err = sc_socket_send(SOCKET_UDP, (uint8_t*)header, (size + length), &session->peeraddr);
TRACEKMOD("*** Send packet result: %d\n", err);
if (err < 0) {
break;
}
@ -593,18 +739,18 @@ struct sc_capwap_radio_addr* sc_capwap_setradiomacaddress(uint8_t* buffer, int s
radioaddr = (struct sc_capwap_radio_addr*)buffer;
radioaddr->length = MACADDRESS_EUI48_LENGTH;
addr = (struct sc_capwap_macaddress_eui48*)(buffer + sizeof(struct sc_capwap_radio_addr));
addr = (struct sc_capwap_macaddress_eui48*)radioaddr->addr;
memcpy(addr->addr, bssid, MACADDRESS_EUI48_LENGTH);
return radioaddr;
}
/* */
struct sc_capwap_wireless_information* sc_capwap_setwirelessinformation(uint8_t* buffer, int size, uint8_t rssi, uint8_t snr, uint16_t rate) {
struct sc_capwap_wireless_information* sc_capwap_setwinfo_frameinfo(uint8_t* buffer, int size, uint8_t rssi, uint8_t snr, uint16_t rate) {
struct sc_capwap_wireless_information* winfo;
struct sc_capwap_ieee80211_frame_info* frameinfo;
TRACEKMOD("### sc_capwap_setwirelessinformation\n");
TRACEKMOD("### sc_capwap_setwinfo_frameinfo\n");
memset(buffer, 0, size);
@ -618,3 +764,21 @@ struct sc_capwap_wireless_information* sc_capwap_setwirelessinformation(uint8_t*
return winfo;
}
/* */
struct sc_capwap_wireless_information* sc_capwap_setwinfo_destwlans(uint8_t* buffer, int size, uint16_t wlanidbitmap) {
struct sc_capwap_wireless_information* winfo;
struct sc_capwap_destination_wlans* destwlans;
TRACEKMOD("### sc_capwap_setwinfo_destwlans\n");
memset(buffer, 0, size);
winfo = (struct sc_capwap_wireless_information*)buffer;
winfo->length = sizeof(struct sc_capwap_destination_wlans);
destwlans = (struct sc_capwap_destination_wlans*)(buffer + sizeof(struct sc_capwap_wireless_information));
destwlans->wlanidbitmap = cpu_to_be16(wlanidbitmap);
return winfo;
}

View File

@ -21,27 +21,25 @@
/* */
#define SKB_CAPWAP_FLAG_FROM_DATA_CHANNEL 0x0001
#define SKB_CAPWAP_FLAG_FROM_USER_SPACE 0x0002
#define SKB_CAPWAP_FLAG_FROM_IEEE80211 0x0004
#define SKB_CAPWAP_FLAG_PEERADDRESS 0x0010
#define SKB_CAPWAP_FLAG_RADIOID 0x0020
#define SKB_CAPWAP_FLAG_BINDING 0x0040
#define SKB_CAPWAP_FLAG_RADIOADDRESS 0x0080
#define SKB_CAPWAP_FLAG_WIRELESSINFORMATION 0x0100
#define SKB_CAPWAP_FLAG_FROM_AC_TAP 0x0004
#define SKB_CAPWAP_FLAG_FROM_IEEE80211 0x0008
#define SKB_CAPWAP_FLAG_SESSIONID 0x0100
#define SKB_CAPWAP_FLAG_RADIOID 0x0200
#define SKB_CAPWAP_FLAG_BINDING 0x0400
#define SKB_CAPWAP_FLAG_WIRELESSINFORMATION 0x0800
#define SKB_CAPWAP_FLAG_FRAGMENT 0x1000
struct sc_skb_capwap_cb {
uint16_t flags;
struct capwap_addr_little peeraddr;
/* Session ID */
struct sc_capwap_sessionid_element sessionid;
/* Capwap information */
uint8_t radioid;
uint8_t binding;
/* Radio Address */
uint8_t radioaddr_addr[MACADDRESS_EUI48_LENGTH];
/* Wireless Information */
uint8_t winfo_rssi;
uint8_t winfo_snr;
@ -106,16 +104,17 @@ void sc_capwap_initsession(struct sc_capwap_session* session);
void sc_capwap_freesession(struct sc_capwap_session* session);
uint16_t sc_capwap_newfragmentid(struct sc_capwap_session* session);
void sc_capwap_sessionid_printf(const struct sc_capwap_sessionid_element* sessionid, char* string);
int sc_capwap_8023_to_80211(struct sk_buff* skb, const uint8_t* bssid);
int sc_capwap_80211_to_8023(struct sk_buff* skb);
struct sc_capwap_packet* sc_capwap_poppacketqueue(struct sc_capwap_session* session);
void sc_capwap_pushpacketqueue(struct sc_capwap_session* session, struct sc_capwap_packet* packet);
void sc_capwap_sessionid_printf(const struct sc_capwap_sessionid_element* sessionid, char* string);
int sc_capwap_createkeepalive(struct sc_capwap_sessionid_element* sessionid, uint8_t* buffer, int size);
int sc_capwap_parsingpacket(struct sc_capwap_session* session, const union capwap_addr* sockaddr, struct sk_buff* skb);
struct sc_capwap_radio_addr* sc_capwap_setradiomacaddress(uint8_t* buffer, int size, uint8_t* bssid);
struct sc_capwap_wireless_information* sc_capwap_setwirelessinformation(uint8_t* buffer, int size, uint8_t rssi, uint8_t snr, uint16_t rate);
struct sc_capwap_wireless_information* sc_capwap_setwinfo_frameinfo(uint8_t* buffer, int size, uint8_t rssi, uint8_t snr, uint16_t rate);
struct sc_capwap_wireless_information* sc_capwap_setwinfo_destwlans(uint8_t* buffer, int size, uint16_t wlanidbitmap);
int sc_capwap_forwarddata(struct sc_capwap_session* session, uint8_t radioid, uint8_t binding, struct sk_buff* skb, uint32_t flags, struct sc_capwap_radio_addr* radioaddr, int radioaddrlength, struct sc_capwap_wireless_information* winfo, int winfolength);

View File

@ -1,65 +1,93 @@
#include "config.h"
#include <linux/module.h>
#include <linux/hash.h>
#include <linux/slab.h>
#include <linux/kthread.h>
#include <linux/if_ether.h>
#include <linux/if_vlan.h>
#include <linux/etherdevice.h>
#include <linux/smp.h>
#include <linux/lockdep.h>
#include <net/ipv6.h>
#include <net/cfg80211.h>
#include "socket.h"
#include "capwap.h"
#include "nlsmartcapwap.h"
#include "netlinkapp.h"
#include "iface.h"
#include "station.h"
/* */
#define SESSION_HASH_SIZE_SHIFT 16
#define SESSION_HASH_SIZE (1 << SESSION_HASH_SIZE_SHIFT)
#define MAX_WORKER_THREAD 32
/* */
static DEFINE_MUTEX(sc_session_update_mutex);
/* Sessions */
static DEFINE_MUTEX(sc_session_mutex);
static struct list_head sc_session_setup_list;
static struct list_head sc_session_running_list;
static uint32_t sc_session_hash_size;
static uint32_t sc_session_hash_size_shift;
static struct sc_capwap_session_priv** __rcu sc_session_hash_ipaddr;
static struct sc_capwap_session_priv** __rcu sc_session_hash_sessionid;
static struct sc_capwap_session_priv* __rcu sc_session_hash_ipaddr[SESSION_HASH_SIZE];
static struct sc_capwap_session_priv* __rcu sc_session_hash_sessionid[SESSION_HASH_SIZE];
/* Threads */
static DEFINE_SPINLOCK(sc_session_threads_lock);
static uint32_t sc_session_threads_pos;
static uint32_t sc_session_threads_count;
static struct sc_capwap_workthread* sc_session_threads;
static struct sc_capwap_workthread sc_session_threads[MAX_WORKER_THREAD];
/* */
static uint32_t sc_capwap_hash_ipaddr(const union capwap_addr* peeraddr) {
TRACEKMOD("### sc_capwap_hash_ipaddr\n");
return hash_32(((peeraddr->ss.ss_family == AF_INET) ? peeraddr->sin.sin_addr.s_addr : ipv6_addr_hash(&peeraddr->sin6.sin6_addr)), sc_session_hash_size_shift);
return hash_32(((peeraddr->ss.ss_family == AF_INET) ? peeraddr->sin.sin_addr.s_addr : ipv6_addr_hash(&peeraddr->sin6.sin6_addr)), SESSION_HASH_SIZE_SHIFT);
}
/* */
static uint32_t sc_capwap_hash_sessionid(const struct sc_capwap_sessionid_element* sessionid) {
TRACEKMOD("### sc_capwap_hash_sessionid\n");
return (sessionid->id32[0] ^ sessionid->id32[1] ^ sessionid->id32[2] ^ sessionid->id32[3]) % sc_session_hash_size;
return ((sessionid->id32[0] ^ sessionid->id32[1] ^ sessionid->id32[2] ^ sessionid->id32[3]) % SESSION_HASH_SIZE);
}
/* */
static void sc_capwap_closesession(struct sc_capwap_session_priv* sessionpriv) {
uint32_t hash;
struct sc_capwap_session_priv* search;
struct sc_capwap_station* temp;
struct sc_capwap_station* station;
TRACEKMOD("### sc_capwap_closesession\n");
lockdep_assert_held(&sc_session_mutex);
/* Close stations */
list_for_each_entry_safe(station, temp, &sessionpriv->list_stations, list_session) {
sc_stations_releaseconnection(station);
sc_stations_free(station);
}
/* */
if (!list_empty(&sessionpriv->list_stations)) {
TRACEKMOD("*** Bug: the list stations of session is not empty\n");
}
if (!list_empty(&sessionpriv->list_connections)) {
TRACEKMOD("*** Bug: the list connections of session is not empty\n");
}
/* Remove session from list reference */
if (sessionpriv->session.peeraddr.ss.ss_family != AF_UNSPEC) {
/* IP Address */
hash = sc_capwap_hash_ipaddr(&sessionpriv->session.peeraddr);
search = rcu_dereference_protected(sc_session_hash_ipaddr[hash], lockdep_is_held(&sc_session_mutex));
search = rcu_dereference_protected(sc_session_hash_ipaddr[hash], sc_capwap_update_lock_is_locked());
if (search) {
if (search == sessionpriv) {
rcu_assign_pointer(sc_session_hash_ipaddr[hash], sessionpriv->next_ipaddr);
} else {
while (rcu_access_pointer(search->next_ipaddr) && (rcu_access_pointer(search->next_ipaddr) != sessionpriv)) {
search = rcu_dereference_protected(search->next_ipaddr, lockdep_is_held(&sc_session_mutex));
search = rcu_dereference_protected(search->next_ipaddr, sc_capwap_update_lock_is_locked());
}
if (rcu_access_pointer(search->next_ipaddr)) {
@ -70,14 +98,14 @@ static void sc_capwap_closesession(struct sc_capwap_session_priv* sessionpriv) {
/* Session ID */
hash = sc_capwap_hash_sessionid(&sessionpriv->session.sessionid);
search = rcu_dereference_protected(sc_session_hash_sessionid[hash], lockdep_is_held(&sc_session_mutex));
search = rcu_dereference_protected(sc_session_hash_sessionid[hash], sc_capwap_update_lock_is_locked());
if (search) {
if (search == sessionpriv) {
rcu_assign_pointer(sc_session_hash_sessionid[hash], sessionpriv->next_sessionid);
} else {
while (rcu_access_pointer(search->next_sessionid) && (rcu_access_pointer(search->next_sessionid) != sessionpriv)) {
search = rcu_dereference_protected(search->next_sessionid, lockdep_is_held(&sc_session_mutex));
search = rcu_dereference_protected(search->next_sessionid, sc_capwap_update_lock_is_locked());
}
if (rcu_access_pointer(search->next_sessionid)) {
@ -94,8 +122,6 @@ static void sc_capwap_closesession(struct sc_capwap_session_priv* sessionpriv) {
/* Free memory */
sc_capwap_freesession(&sessionpriv->session);
kfree(sessionpriv);
TRACEKMOD("*** Free session\n");
}
/* */
@ -104,38 +130,22 @@ static void sc_capwap_closesessions(void) {
struct sc_capwap_session_priv* temp;
TRACEKMOD("### sc_capwap_closesessions\n");
TRACEKMOD("*** Delete all sessions\n");
/* */
mutex_lock(&sc_session_mutex);
sc_capwap_update_lock();
/* */
list_for_each_entry_safe(sessionpriv, temp, &sc_session_setup_list, list) {
#ifdef DEBUGKMOD
do {
char sessionname[33];
sc_capwap_sessionid_printf(&sessionpriv->session.sessionid, sessionname);
TRACEKMOD("*** Delete setup session: %s\n", sessionname);
} while(0);
#endif
sc_capwap_closesession(sessionpriv);
}
/* */
list_for_each_entry_safe(sessionpriv, temp, &sc_session_running_list, list) {
#ifdef DEBUGKMOD
do {
char sessionname[33];
sc_capwap_sessionid_printf(&sessionpriv->session.sessionid, sessionname);
TRACEKMOD("*** Delete running session: %s\n", sessionname);
} while(0);
#endif
sc_capwap_closesession(sessionpriv);
}
/* */
synchronize_net();
mutex_unlock(&sc_session_mutex);
sc_capwap_update_unlock();
}
/* */
@ -145,14 +155,14 @@ static struct sc_capwap_session_priv* sc_capwap_getsession_ipaddr(const union ca
TRACEKMOD("### sc_capwap_getsession_ipaddr\n");
/* */
sessionpriv = rcu_dereference_check(sc_session_hash_ipaddr[sc_capwap_hash_ipaddr(sockaddr)], lockdep_is_held(&sc_session_mutex));
sessionpriv = rcu_dereference_check(sc_session_hash_ipaddr[sc_capwap_hash_ipaddr(sockaddr)], sc_capwap_update_lock_is_locked());
while (sessionpriv) {
if (!sc_addr_compare(sockaddr, &sessionpriv->session.peeraddr)) {
break;
}
/* */
sessionpriv = rcu_dereference_check(sessionpriv->next_ipaddr, lockdep_is_held(&sc_session_mutex));
sessionpriv = rcu_dereference_check(sessionpriv->next_ipaddr, sc_capwap_update_lock_is_locked());
}
return sessionpriv;
@ -165,14 +175,14 @@ static struct sc_capwap_session_priv* sc_capwap_getsession_sessionid(const struc
TRACEKMOD("### sc_capwap_getsession_sessionid\n");
/* */
sessionpriv = rcu_dereference_check(sc_session_hash_ipaddr[sc_capwap_hash_sessionid(sessionid)], lockdep_is_held(&sc_session_mutex));
sessionpriv = rcu_dereference_check(sc_session_hash_sessionid[sc_capwap_hash_sessionid(sessionid)], sc_capwap_update_lock_is_locked());
while (sessionpriv) {
if (!memcmp(&sessionpriv->session.sessionid, sessionid, sizeof(struct sc_capwap_sessionid_element))) {
break;
}
/* */
sessionpriv = rcu_dereference_check(sessionpriv->next_sessionid, lockdep_is_held(&sc_session_mutex));
sessionpriv = rcu_dereference_check(sessionpriv->next_sessionid, sc_capwap_update_lock_is_locked());
}
return sessionpriv;
@ -186,7 +196,7 @@ static int sc_capwap_deletesetupsession(const struct sc_capwap_sessionid_element
TRACEKMOD("### sc_capwap_deletesetupsession\n");
/* */
mutex_lock(&sc_session_mutex);
sc_capwap_update_lock();
list_for_each_entry(sessionpriv, &sc_session_setup_list, list) {
if (!memcmp(&sessionpriv->session.sessionid, sessionid, sizeof(struct sc_capwap_sessionid_element))) {
@ -204,7 +214,7 @@ static int sc_capwap_deletesetupsession(const struct sc_capwap_sessionid_element
}
/* */
mutex_unlock(&sc_session_mutex);
sc_capwap_update_unlock();
return ret;
}
@ -216,7 +226,7 @@ static int sc_capwap_deleterunningsession(const struct sc_capwap_sessionid_eleme
TRACEKMOD("### sc_capwap_deleterunningsession\n");
/* */
mutex_lock(&sc_session_mutex);
sc_capwap_update_lock();
/* Search session with address hash */
sessionpriv = sc_capwap_getsession_sessionid(sessionid);
@ -233,14 +243,121 @@ static int sc_capwap_deleterunningsession(const struct sc_capwap_sessionid_eleme
}
/* */
mutex_unlock(&sc_session_mutex);
sc_capwap_update_unlock();
return ret;
}
/* */
static int sc_capwap_restrictbroadcastpacket(struct sk_buff* skb, int is80211) {
TRACEKMOD("### sc_capwap_restrictbroadcastpacket\n");
/* TODO: limit some broadcast packet (DHCP) */
return 0;
}
/* */
static int sc_capwap_sendpacket_wtp(struct sc_capwap_session_priv* sessionpriv, uint8_t radioid, uint8_t wlanid, struct sk_buff* skb, int is80211) {
uint32_t flags = 0;
struct sc_capwap_radio_addr* radioaddr = NULL;
uint8_t radioaddrbuffer[CAPWAP_RADIO_EUI48_LENGTH_PADDED];
struct sc_capwap_wlan* wlan = &sessionpriv->wlans[radioid - 1][wlanid - 1];
TRACEKMOD("### sc_capwap_sendpacket_wtp\n");
/* */
if (!wlan->used) {
return -EINVAL;
}
/* Datalink header convertion */
if (is80211 && (wlan->tunnelmode == CAPWAP_ADD_WLAN_TUNNELMODE_8023)) {
sc_capwap_80211_to_8023(skb);
flags |= NLSMARTCAPWAP_FLAGS_TUNNEL_8023;
radioaddr = sc_capwap_setradiomacaddress(radioaddrbuffer, CAPWAP_RADIO_EUI48_LENGTH_PADDED, wlan->bssid);
} else if (!is80211 && (wlan->tunnelmode == CAPWAP_ADD_WLAN_TUNNELMODE_80211)) {
sc_capwap_8023_to_80211(skb, wlan->bssid);
}
/* Forward packet */
return sc_capwap_forwarddata(&sessionpriv->session, radioid, sessionpriv->binding, skb, flags, radioaddr, (radioaddr ? CAPWAP_RADIO_EUI48_LENGTH_PADDED : 0), NULL, 0);
}
/* */
static void sc_capwap_sendbroadcastpacket_wtp(struct sc_netdev_priv* netpriv, uint16_t vlan, struct sk_buff* skb, struct sc_capwap_session_priv* ignore) {
struct sk_buff* clone;
struct sc_capwap_connection* connection;
struct sc_capwap_wireless_information* winfo;
uint8_t buffer[CAPWAP_WINFO_DESTWLAN_LENGTH_PADDED];
int headroom = sizeof(struct sc_capwap_header) + CAPWAP_WINFO_DESTWLAN_LENGTH_PADDED;
TRACEKMOD("### sc_capwap_sendbroadcastpacket_wtp\n");
/* */
if (headroom < skb_headroom(skb)) {
headroom = skb_headroom(skb);
}
/* Send packet for every connection */
list_for_each_entry_rcu(connection, &netpriv->list_connections, list_dev) {
if ((connection->vlan == vlan) && (connection->sessionpriv != ignore)) {
clone = skb_copy_expand(skb, headroom, skb_tailroom(skb), GFP_KERNEL);
if (!clone) {
break;
}
/* Forward packet */
winfo = sc_capwap_setwinfo_destwlans(buffer, CAPWAP_WINFO_DESTWLAN_LENGTH_PADDED, connection->wlanidmask);
sc_capwap_forwarddata(&connection->sessionpriv->session, connection->radioid, connection->sessionpriv->binding, clone, NLSMARTCAPWAP_FLAGS_TUNNEL_8023, NULL, 0, winfo, CAPWAP_WINFO_DESTWLAN_LENGTH_PADDED);
kfree_skb(clone);
}
}
}
/* */
static void sc_capwap_sendpacket_iface(struct sc_capwap_station* station, struct sk_buff* skb) {
struct sc_netdev_priv* devpriv = rcu_dereference(station->devpriv);
TRACEKMOD("### sc_capwap_sendpacket_iface\n");
/* */
if (devpriv->dev->flags & IFF_UP) {
if (station->vlan) {
skb = vlan_insert_tag(skb, htons(ETH_P_8021Q), station->vlan & VLAN_VID_MASK);
if (!skb) {
/* Unable add VLAN id */
spin_lock(&devpriv->lock);
devpriv->dev->stats.rx_dropped++;
spin_unlock(&devpriv->lock);
return;
}
}
/* Prepare to send packet */
skb_reset_mac_header(skb);
skb->protocol = eth_type_trans(skb, devpriv->dev);
/* Send packet */
netif_rx_ni(skb);
/* Update stats */
spin_lock(&devpriv->lock);
devpriv->dev->stats.rx_packets++;
devpriv->dev->stats.rx_bytes += skb->len;
spin_unlock(&devpriv->lock);
} else {
/* Drop packet */
kfree_skb(skb);
spin_lock(&devpriv->lock);
devpriv->dev->stats.rx_dropped++;
spin_unlock(&devpriv->lock);
}
}
/* */
static int sc_capwap_thread_recvpacket(struct sk_buff* skb) {
int ret = 1;
union capwap_addr peeraddr;
struct sc_capwap_session_priv* sessionpriv;
struct sc_skb_capwap_cb* cb = CAPWAP_SKB_CB(skb);
@ -250,14 +367,10 @@ static int sc_capwap_thread_recvpacket(struct sk_buff* skb) {
if (cb->flags & SKB_CAPWAP_FLAG_FROM_USER_SPACE) {
TRACEKMOD("*** Receive SKB_CAPWAP_FLAG_FROM_USER_SPACE\n");
/* Get peer address */
sc_addr_fromlittle(&cb->peeraddr, &peeraddr);
TRACEKMOD("*** Address %d %x %x\n", peeraddr.ss.ss_family, (int)peeraddr.sin.sin_addr.s_addr, (int)peeraddr.sin.sin_port);
/* Send packet*/
rcu_read_lock();
sessionpriv = sc_capwap_getsession_ipaddr(&peeraddr);
sessionpriv = sc_capwap_getsession_sessionid(&cb->sessionid);
if (sessionpriv) {
if (sc_capwap_forwarddata(&sessionpriv->session, cb->radioid, cb->binding, skb, 0, NULL, 0, NULL, 0)) {
TRACEKMOD("*** Unable send packet from sc_netlink_send_data function\n");
@ -268,25 +381,63 @@ static int sc_capwap_thread_recvpacket(struct sk_buff* skb) {
rcu_read_unlock();
} else if (cb->flags & SKB_CAPWAP_FLAG_FROM_DATA_CHANNEL) {
union capwap_addr peeraddr;
TRACEKMOD("*** Receive SKB_CAPWAP_FLAG_FROM_DATA_CHANNEL\n");
/* Get peer address */
if (sc_socket_getpeeraddr(skb, &peeraddr)) {
if (!sc_socket_getpeeraddr(skb, &peeraddr)) {
if (skb_pull(skb, sizeof(struct udphdr))) {
rcu_read_lock();
sessionpriv = sc_capwap_getsession_ipaddr(&peeraddr);
ret = sc_capwap_parsingpacket((sessionpriv ? &sessionpriv->session : NULL), &peeraddr, skb);
rcu_read_unlock();
} else {
TRACEKMOD("*** Invalid packet\n");
ret = -EOVERFLOW;
}
} else {
TRACEKMOD("*** Unable get address from packet\n");
return -EINVAL;
ret = -EINVAL;
}
} else if (cb->flags & SKB_CAPWAP_FLAG_FROM_AC_TAP) {
uint16_t vlan = 0;
struct ethhdr* eh = eth_hdr(skb);
struct sc_capwap_station* station;
TRACEKMOD("*** Receive SKB_CAPWAP_FLAG_FROM_AC_TAP\n");
/* Retrieve VLAN */
if (vlan_tx_tag_present(skb)) {
vlan = vlan_tx_tag_get_id(skb);
} else if (eh->h_proto == htons(ETH_P_8021Q)) {
vlan = ntohs(vlan_eth_hdr(skb)->h_vlan_TCI) & VLAN_VID_MASK;
/* Remove 802.1q from packet */
memmove(skb->data + VLAN_HLEN, skb->data, 2 * ETH_ALEN);
skb_pull(skb, VLAN_HLEN);
skb_reset_mac_header(skb);
}
/* Remove UDP header */
if (!skb_pull(skb, sizeof(struct udphdr))) {
TRACEKMOD("*** Invalid packet\n");
return -EOVERFLOW;
}
/* */
rcu_read_lock();
sessionpriv = sc_capwap_getsession_ipaddr(&peeraddr);
ret = sc_capwap_parsingpacket(&sessionpriv->session, &peeraddr, skb);
if (is_multicast_ether_addr(eh->h_dest)) {
TRACEKMOD("*** Receive broadcast/multicast packet\n");
if (!sc_capwap_restrictbroadcastpacket(skb, 0)) {
sc_capwap_sendbroadcastpacket_wtp((struct sc_netdev_priv*)netdev_priv(skb->dev), vlan, skb, NULL);
}
} else {
station = sc_stations_search(eh->h_dest);
if (station && (station->vlan == vlan)) {
sc_capwap_sendpacket_wtp(rcu_dereference(station->sessionpriv), station->radioid, station->wlanid, skb, 0);
} else {
TRACEKMOD("*** Unable to found station from macaddress\n");
ret = -EINVAL;
}
}
rcu_read_unlock();
}
@ -300,7 +451,7 @@ static int sc_capwap_thread(void* data) {
struct sc_capwap_workthread* thread = (struct sc_capwap_workthread*)data;
TRACEKMOD("### sc_capwap_thread\n");
TRACEKMOD("*** Thread start\n");
TRACEKMOD("*** Thread start: %d\n", smp_processor_id());
for (;;) {
wait_event_interruptible(thread->waitevent, (skb_queue_len(&thread->queue) > 0) || kthread_should_stop());
@ -311,23 +462,44 @@ static int sc_capwap_thread(void* data) {
/* Get packet */
skb = skb_dequeue(&thread->queue);
if (!skb) {
TRACEKMOD("*** Nothing from thread %d\n", smp_processor_id());
continue;
}
/* */
TRACEKMOD("*** Thread receive packet\n");
TRACEKMOD("*** Thread receive packet %d\n", smp_processor_id());
if (sc_capwap_thread_recvpacket(skb)) {
TRACEKMOD("*** Free packet\n");
kfree_skb(skb);
}
}
TRACEKMOD("*** Thread end\n");
/* Purge queue */
skb_queue_purge(&thread->queue);
TRACEKMOD("*** Thread end: %d\n", smp_processor_id());
return 0;
}
/* */
int sc_capwap_sendkeepalive(const union capwap_addr* peeraddr) {
void sc_capwap_update_lock(void) {
mutex_lock(&sc_session_update_mutex);
}
/* */
void sc_capwap_update_unlock(void) {
mutex_unlock(&sc_session_update_mutex);
}
/* */
#ifdef CONFIG_PROVE_LOCKING
int sc_capwap_update_lock_is_locked(void) {
return lockdep_is_held(&sc_session_update_mutex);
}
#endif
/* */
int sc_capwap_sendkeepalive(const struct sc_capwap_sessionid_element* sessionid) {
int ret;
int length;
struct sc_capwap_session_priv* sessionpriv;
@ -339,7 +511,7 @@ int sc_capwap_sendkeepalive(const union capwap_addr* peeraddr) {
rcu_read_lock();
/* Get session */
sessionpriv = sc_capwap_getsession_ipaddr(peeraddr);
sessionpriv = sc_capwap_getsession_sessionid(sessionid);
if (!sessionpriv) {
TRACEKMOD("*** Unknown keep-alive session\n");
ret = -ENOENT;
@ -359,6 +531,7 @@ int sc_capwap_sendkeepalive(const union capwap_addr* peeraddr) {
/* Send packet */
ret = sc_socket_send(SOCKET_UDP, buffer, length, &sessionpriv->session.peeraddr);
TRACEKMOD("*** Send keep-alive result: %d\n", ret);
if (ret > 0) {
ret = 0;
}
@ -369,7 +542,7 @@ done:
}
/* */
int sc_capwap_newsession(const struct sc_capwap_sessionid_element* sessionid, uint16_t mtu) {
int sc_capwap_newsession(const struct sc_capwap_sessionid_element* sessionid, uint8_t binding, uint16_t mtu) {
struct sc_capwap_session_priv* sessionpriv;
TRACEKMOD("### sc_capwap_newsession\n");
@ -383,7 +556,7 @@ int sc_capwap_newsession(const struct sc_capwap_sessionid_element* sessionid, ui
#endif
/* */
sessionpriv = kzalloc(sizeof(struct sc_capwap_session_priv), GFP_KERNEL);
sessionpriv = (struct sc_capwap_session_priv*)kzalloc(sizeof(struct sc_capwap_session_priv), GFP_KERNEL);
if (!sessionpriv) {
TRACEKMOD("*** Unable to create session\n");
return -ENOMEM;
@ -392,75 +565,68 @@ int sc_capwap_newsession(const struct sc_capwap_sessionid_element* sessionid, ui
/* Initialize session */
sc_capwap_initsession(&sessionpriv->session);
memcpy(&sessionpriv->session.sessionid, sessionid, sizeof(struct sc_capwap_sessionid_element));
sessionpriv->binding = binding;
sessionpriv->session.mtu = mtu;
INIT_LIST_HEAD(&sessionpriv->list);
INIT_LIST_HEAD(&sessionpriv->list_stations);
INIT_LIST_HEAD(&sessionpriv->list_connections);
/* Add to setup session list */
mutex_lock(&sc_session_mutex);
sc_capwap_update_lock();
list_add_rcu(&sessionpriv->list, &sc_session_setup_list);
mutex_unlock(&sc_session_mutex);
sc_capwap_update_unlock();
TRACEKMOD("*** Create session\n");
return 0;
}
/* */
int sc_capwap_init(uint32_t hash, uint32_t threads) {
uint32_t i;
int sc_capwap_init(void) {
unsigned long i;
unsigned long cpu;
int err = -ENOMEM;
TRACEKMOD("### sc_capwap_init\n");
TRACEKMOD("*** Init capwap module - hash bitfield: %u - threads: %u\n", hash, threads);
/* */
if (!hash || !threads) {
return -EINVAL;
}
/* Init session */
memset(&sc_localaddr, 0, sizeof(union capwap_addr));
INIT_LIST_HEAD(&sc_session_running_list);
INIT_LIST_HEAD(&sc_session_setup_list);
INIT_LIST_HEAD(&sc_session_running_list);
/* */
sc_session_hash_size_shift = hash;
sc_session_hash_size = 1 << hash;
sc_session_hash_ipaddr = (struct sc_capwap_session_priv**)kzalloc(sizeof(struct sc_capwap_session_priv*) * sc_session_hash_size, GFP_KERNEL);
if (!sc_session_hash_ipaddr) {
goto error;
}
sc_session_hash_sessionid = (struct sc_capwap_session_priv**)kzalloc(sizeof(struct sc_capwap_session_priv*) * sc_session_hash_size, GFP_KERNEL);
if (!sc_session_hash_sessionid) {
goto error1;
}
memset(sc_session_hash_ipaddr, 0, sizeof(struct sc_capwap_session_priv*) * SESSION_HASH_SIZE);
memset(sc_session_hash_sessionid, 0, sizeof(struct sc_capwap_session_priv*) * SESSION_HASH_SIZE);
/* Create threads */
sc_session_threads_pos = 0;
sc_session_threads_count = threads;
sc_session_threads = (struct sc_capwap_workthread*)kzalloc(sizeof(struct sc_capwap_workthread) * threads, GFP_KERNEL);
if (!sc_session_threads) {
goto error2;
}
sc_session_threads_count = 0;
for_each_online_cpu(cpu) {
memset(&sc_session_threads[sc_session_threads_count], 0, sizeof(struct sc_capwap_workthread));
for (i = 0; i < threads; i++) {
sc_session_threads[i].thread = kthread_create(sc_capwap_thread, &sc_session_threads[i], "smartcapwap/%u", i);
if (IS_ERR(sc_session_threads[i].thread)) {
err = PTR_ERR(sc_session_threads[i].thread);
sc_session_threads[i].thread = NULL;
goto error3;
/* Create thread and bind to cpu */
sc_session_threads[sc_session_threads_count].thread = kthread_create(sc_capwap_thread, &sc_session_threads[sc_session_threads_count], "smartcapwap/%u", sc_session_threads_count);
if (!IS_ERR(sc_session_threads[sc_session_threads_count].thread)) {
kthread_bind(sc_session_threads[sc_session_threads_count].thread, cpu);
/* */
sc_session_threads_count++;
if (sc_session_threads_count == MAX_WORKER_THREAD) {
break;
}
} else {
err = PTR_ERR(sc_session_threads[sc_session_threads_count].thread);
sc_session_threads[sc_session_threads_count].thread = NULL;
goto error;
}
}
/* Init sockect */
err = sc_socket_init();
if (err) {
goto error3;
goto error;
}
/* Start threads */
for (i = 0; i < threads; i++) {
for (i = 0; i < sc_session_threads_count; i++) {
skb_queue_head_init(&sc_session_threads[i].queue);
init_waitqueue_head(&sc_session_threads[i].waitevent);
wake_up_process(sc_session_threads[i].thread);
@ -468,22 +634,13 @@ int sc_capwap_init(uint32_t hash, uint32_t threads) {
return 0;
error3:
for (i = 0; i < threads; i++) {
error:
for (i = 0; i < sc_session_threads_count; i++) {
if (sc_session_threads[i].thread) {
kthread_stop(sc_session_threads[i].thread);
}
}
kfree(sc_session_threads);
error2:
kfree(sc_session_hash_sessionid);
error1:
kfree(sc_session_hash_ipaddr);
error:
return err;
}
@ -492,27 +649,16 @@ void sc_capwap_close(void) {
uint32_t i;
TRACEKMOD("### sc_capwap_close\n");
TRACEKMOD("*** Closing capwap module\n");
/* */
/* Close */
sc_socket_close();
sc_capwap_closesessions();
sc_iface_closeall();
/* */
/* Terminate threads */
for (i = 0; i < sc_session_threads_count; i++) {
kthread_stop(sc_session_threads[i].thread);
}
kfree(sc_session_threads);
/* */
sc_capwap_closesessions();
kfree(sc_session_hash_ipaddr);
kfree(sc_session_hash_sessionid);
/* */
sc_iface_closeall();
TRACEKMOD("*** Close capwap module\n");
}
/* */
@ -556,17 +702,64 @@ int sc_capwap_deletesession(const struct sc_capwap_sessionid_element* sessionid)
return -ENOENT;
}
/* */
int sc_capwap_addwlan(const struct sc_capwap_sessionid_element* sessionid, uint8_t radioid, uint8_t wlanid, const uint8_t* bssid, uint8_t macmode, uint8_t tunnelmode) {
int err = -ENOENT;
struct sc_capwap_session_priv* sessionpriv;
TRACEKMOD("### sc_capwap_addwlan\n");
/* */
sc_capwap_update_lock();
/* Search session and interface */
sessionpriv = sc_capwap_getsession_sessionid(sessionid);
if (sessionpriv) {
struct sc_capwap_wlan* wlan = &sessionpriv->wlans[radioid - 1][wlanid - 1];
memcpy(wlan->bssid, bssid, MACADDRESS_EUI48_LENGTH);
wlan->macmode = macmode;
wlan->tunnelmode = tunnelmode;
wlan->used = 1;
err = 0;
}
sc_capwap_update_unlock();
return err;
}
/* */
int sc_capwap_removewlan(const struct sc_capwap_sessionid_element* sessionid, uint8_t radioid, uint8_t wlanid) {
int err = -ENOENT;
struct sc_capwap_session_priv* sessionpriv;
TRACEKMOD("### sc_capwap_removewlan\n");
/* */
sc_capwap_update_lock();
/* Search session and interface */
sessionpriv = sc_capwap_getsession_sessionid(sessionid);
if (sessionpriv) {
sessionpriv->wlans[radioid - 1][wlanid - 1].used = 0;
}
sc_capwap_update_unlock();
return err;
}
/* */
void sc_capwap_recvpacket(struct sk_buff* skb) {
uint32_t pos;
unsigned long flags;
TRACEKMOD("### sc_capwap_recvpacket\n");
spin_lock_irqsave(&sc_session_threads_lock, flags);
spin_lock(&sc_session_threads_lock);
sc_session_threads_pos = ((sc_session_threads_pos + 1) % sc_session_threads_count);
pos = sc_session_threads_pos;
spin_unlock_irqrestore(&sc_session_threads_lock, flags);
spin_unlock(&sc_session_threads_lock);
TRACEKMOD("*** Add packet to thread: %u\n", pos);
@ -583,9 +776,6 @@ struct sc_capwap_session* sc_capwap_recvunknownkeepalive(const union capwap_addr
TRACEKMOD("### sc_capwap_recvunknownkeepalive\n");
/* Must be called under rcu_read_lock() */
rcu_lockdep_assert(rcu_read_lock_held(), "sc_capwap_recvunknownkeepalive() needs rcu_read_lock() protection");
#ifdef DEBUGKMOD
do {
char sessionname[33];
@ -596,7 +786,7 @@ struct sc_capwap_session* sc_capwap_recvunknownkeepalive(const union capwap_addr
/* Change read lock to update lock */
rcu_read_unlock();
mutex_lock(&sc_session_mutex);
sc_capwap_update_lock();
/* Search and remove from setup session */
list_for_each_entry(search, &sc_session_setup_list, list) {
@ -622,27 +812,31 @@ struct sc_capwap_session* sc_capwap_recvunknownkeepalive(const union capwap_addr
/* */
hash = sc_capwap_hash_ipaddr(sockaddr);
sessionpriv->next_ipaddr = sc_session_hash_ipaddr[hash];
sessionpriv->next_ipaddr = rcu_dereference_protected(sc_session_hash_ipaddr[hash], sc_capwap_update_lock_is_locked());
rcu_assign_pointer(sc_session_hash_ipaddr[hash], sessionpriv);
/* */
hash = sc_capwap_hash_sessionid(sessionid);
sessionpriv->next_sessionid = sc_session_hash_sessionid[hash];
sessionpriv->next_sessionid = rcu_dereference_protected(sc_session_hash_sessionid[hash], sc_capwap_update_lock_is_locked());
rcu_assign_pointer(sc_session_hash_sessionid[hash], sessionpriv);
done:
rcu_read_lock();
mutex_unlock(&sc_session_mutex);
sc_capwap_update_unlock();
/* */
return (sessionpriv ? &sessionpriv->session : NULL);
}
/* */
void sc_capwap_parsingdatapacket(struct sc_capwap_session* session, struct sk_buff* skb) {
uint8_t* pos;
uint8_t* srcaddress;
uint8_t* dstaddress;
struct sc_capwap_station* srcstation;
struct sc_capwap_station* dststation;
struct sc_capwap_header* header = (struct sc_capwap_header*)skb->data;
int is80211 = (IS_FLAG_T_HEADER(header) ? 1 : 0);
struct sc_capwap_radio_addr* radioaddr = NULL;
int radioaddrsize = 0;
struct sc_capwap_wireless_information* winfo = NULL;
@ -660,11 +854,67 @@ void sc_capwap_parsingdatapacket(struct sc_capwap_session* session, struct sk_bu
if (IS_FLAG_W_HEADER(header)) {
winfo = (struct sc_capwap_wireless_information*)pos;
radioaddrsize = (sizeof(struct sc_capwap_wireless_information) + winfo->length + 3) & ~3;
winfosize = (sizeof(struct sc_capwap_wireless_information) + winfo->length + 3) & ~3;
pos += winfosize;
}
/* TODO */
/* Body packet */
skb_pull(skb, GET_HLEN_HEADER(header) * 4);
srcaddress = (is80211 ? ieee80211_get_SA((struct ieee80211_hdr*)skb->data) : (uint8_t*)((struct ethhdr*)skb->data)->h_source);
dstaddress = (is80211 ? ieee80211_get_DA((struct ieee80211_hdr*)skb->data) : (uint8_t*)((struct ethhdr*)skb->data)->h_dest);
/* Search source station */
srcstation = sc_stations_search(srcaddress);
if (srcstation) {
struct sc_capwap_session_priv* srcsessionpriv = rcu_dereference(srcstation->sessionpriv);
struct sc_capwap_wlan* wlan = &srcsessionpriv->wlans[srcstation->radioid - 1][srcstation->wlanid - 1];
if (wlan->used) {
/* Check tunnel mode */
if (wlan->tunnelmode != CAPWAP_ADD_WLAN_TUNNELMODE_LOCAL) {
if (is_multicast_ether_addr(dstaddress)) {
if (is80211) {
sc_capwap_80211_to_8023(skb);
}
/* Forward to any session with same connection */
if (!srcsessionpriv->isolation && !sc_capwap_restrictbroadcastpacket(skb, is80211)) {
sc_capwap_sendbroadcastpacket_wtp(rcu_dereference(srcstation->devpriv), srcstation->vlan, skb, srcsessionpriv);
}
/* Forward to physical interface */
sc_capwap_sendpacket_iface(srcstation, skb);
} else {
/* Search destination station */
dststation = sc_stations_search(dstaddress);
if (dststation) {
/* Forward packet */
if (!srcsessionpriv->isolation && (srcsessionpriv != rcu_access_pointer(dststation->sessionpriv))) {
sc_capwap_sendpacket_wtp(rcu_dereference(dststation->sessionpriv), dststation->radioid, dststation->wlanid, skb, is80211);
}
kfree_skb(skb);
} else {
if (is80211) {
sc_capwap_80211_to_8023(skb);
}
/* Forward to physical interface */
sc_capwap_sendpacket_iface(srcstation, skb);
}
}
} else {
TRACEKMOD("*** Receive packet from local tunnel mode wlan session\n");
kfree_skb(skb);
}
} else {
TRACEKMOD("*** Receive packet from disable wlan\n");
kfree_skb(skb);
}
} else {
TRACEKMOD("*** Receive packet from unknown station\n");
kfree_skb(skb);
}
}
/* */
@ -673,4 +923,119 @@ void sc_capwap_parsingmgmtpacket(struct sc_capwap_session* session, struct sk_bu
/* Send packet with capwap header into userspace */
sc_netlink_notify_recv_data(&session->sessionid, skb->data, skb->len);
kfree_skb(skb);
}
/* */
int sc_capwap_authstation(const struct sc_capwap_sessionid_element* sessionid, const uint8_t* address, uint32_t ifindex, uint8_t radioid, uint8_t wlanid, uint16_t vlan) {
int err = 0;
struct sc_capwap_station* station;
struct sc_netdev_priv* devpriv;
struct sc_capwap_session_priv* sessionpriv;
TRACEKMOD("### sc_capwap_authstation\n");
if (!IS_VALID_RADIOID(radioid)) {
return -EINVAL;
}
/* */
sc_capwap_update_lock();
/* Search session and interface */
sessionpriv = sc_capwap_getsession_sessionid(sessionid);
if (sessionpriv) {
devpriv = sc_iface_search(ifindex);
if (devpriv) {
/* Create or Update Station */
station = sc_stations_search(address);
if (station) {
/* Release old connection */
sc_stations_releaseconnection(station);
/* */
station->vlan = vlan;
station->radioid = radioid;
station->wlanid = wlanid;
/* Update interface */
if (rcu_access_pointer(station->devpriv) != devpriv) {
rcu_assign_pointer(station->devpriv, devpriv);
list_replace(&station->list_dev, &devpriv->list_stations);
}
/* Update session */
if (rcu_access_pointer(station->sessionpriv) != sessionpriv) {
rcu_assign_pointer(station->sessionpriv, sessionpriv);
list_replace(&station->list_session, &sessionpriv->list_stations);
}
} else {
station = (struct sc_capwap_station*)kzalloc(sizeof(struct sc_capwap_station), GFP_KERNEL);
if (station) {
memcpy(station->address, address, MACADDRESS_EUI48_LENGTH);
station->vlan = vlan;
station->radioid = radioid;
station->wlanid = wlanid;
/* Assign interface */
rcu_assign_pointer(station->devpriv, devpriv);
list_add(&station->list_dev, &devpriv->list_stations);
/* Assign session */
rcu_assign_pointer(station->sessionpriv, sessionpriv);
list_add(&station->list_session, &sessionpriv->list_stations);
/* Add station */
sc_stations_add(station);
} else {
TRACEKMOD("*** Unable to create station\n");
err = -ENOMEM;
}
}
/* Set new connection */
if (!err && station) {
err = sc_stations_setconnection(station);
if (err) {
TRACEKMOD("*** Unable to set connection\n");
sc_stations_free(station);
}
}
} else {
TRACEKMOD("*** Unable to find interface\n");
err = -EINVAL;
}
} else {
TRACEKMOD("*** Unable to find session\n");
err = -EINVAL;
}
sc_capwap_update_unlock();
return err;
}
/* */
int sc_capwap_deauthstation(const struct sc_capwap_sessionid_element* sessionid, const uint8_t* address) {
int err = -ENOENT;
struct sc_capwap_station* station;
struct sc_capwap_session_priv* sessionpriv;
TRACEKMOD("### sc_capwap_deauthstation\n");
sc_capwap_update_lock();
sessionpriv = sc_capwap_getsession_sessionid(sessionid);
if (sessionpriv) {
station = sc_stations_search(address);
if (station && (rcu_access_pointer(station->sessionpriv) == sessionpriv)) {
sc_stations_releaseconnection(station);
sc_stations_free(station);
err = 0;
}
}
sc_capwap_update_unlock();
return err;
}

View File

@ -1,6 +1,15 @@
#ifndef __KMOD_CAPWAP_PRIVATE_HEADER__
#define __KMOD_CAPWAP_PRIVATE_HEADER__
/* */
struct sc_capwap_wlan {
int used;
uint8_t bssid[MACADDRESS_EUI48_LENGTH];
uint8_t macmode;
uint8_t tunnelmode;
};
/* */
struct sc_capwap_session_priv {
struct sc_capwap_session session;
@ -8,6 +17,14 @@ struct sc_capwap_session_priv {
struct list_head list;
struct sc_capwap_session_priv* __rcu next_ipaddr;
struct sc_capwap_session_priv* __rcu next_sessionid;
struct list_head list_stations;
struct list_head list_connections;
/* */
int isolation;
uint8_t binding;
struct sc_capwap_wlan wlans[CAPWAP_RADIOID_MAX_COUNT][CAPWAP_WLANID_MAX_COUNT];
};
/* */
@ -19,14 +36,32 @@ struct sc_capwap_workthread {
};
/* */
int sc_capwap_init(uint32_t hash, uint32_t threads);
int sc_capwap_init(void);
void sc_capwap_close(void);
/* */
int sc_capwap_sendkeepalive(const union capwap_addr* peeraddr);
void sc_capwap_update_lock(void);
void sc_capwap_update_unlock(void);
#ifdef CONFIG_PROVE_LOCKING
int sc_capwap_update_lock_is_locked(void);
#else
static inline int sc_capwap_update_lock_is_locked(void) { return 1; }
#endif
/* */
int sc_capwap_newsession(const struct sc_capwap_sessionid_element* sessionid, uint16_t mtu);
int sc_capwap_sendkeepalive(const struct sc_capwap_sessionid_element* sessionid);
/* */
int sc_capwap_newsession(const struct sc_capwap_sessionid_element* sessionid, uint8_t binding, uint16_t mtu);
int sc_capwap_deletesession(const struct sc_capwap_sessionid_element* sessionid);
/* */
int sc_capwap_addwlan(const struct sc_capwap_sessionid_element* sessionid, uint8_t radioid, uint8_t wlanid, const uint8_t* bssid, uint8_t macmode, uint8_t tunnelmode);
int sc_capwap_removewlan(const struct sc_capwap_sessionid_element* sessionid, uint8_t radioid, uint8_t wlanid);
/* */
int sc_capwap_authstation(const struct sc_capwap_sessionid_element* sessionid, const uint8_t* address, uint32_t ifindex, uint8_t radioid, uint8_t wlanid, uint16_t vlan);
int sc_capwap_deauthstation(const struct sc_capwap_sessionid_element* sessionid, const uint8_t* address);
#endif /* __KMOD_CAPWAP_PRIVATE_HEADER__ */

View File

@ -79,6 +79,7 @@ struct sc_capwap_header {
#define CAPWAP_RADIO_MAX_LENGTH_PADDED 12
struct sc_capwap_radio_addr {
uint8_t length;
uint8_t addr[0];
} __packed;
/* Wireless Information */
@ -96,6 +97,12 @@ struct sc_capwap_ieee80211_frame_info {
__be16 rate;
} __packed;
/* Destination WLANs */
struct sc_capwap_destination_wlans {
__be16 wlanidbitmap;
__be16 reserved;
} __packed;
/* */
#define CAPWAP_HEADER_MAX_LENGTH (sizeof(struct sc_capwap_header) + CAPWAP_RADIO_MAX_LENGTH_PADDED + CAPWAP_WINFO_MAX_LENGTH_PADDED)
@ -172,4 +179,9 @@ struct sc_capwap_macaddress_eui64 {
#define IS_FLAG_K_HEADER(x) ((x)->flag_k)
#define SET_FLAG_K_HEADER(x, y) ((x)->flag_k = ((y) ? 1 : 0))
/* IEEE 802.11 Add WLAN */
#define CAPWAP_ADD_WLAN_TUNNELMODE_LOCAL 0
#define CAPWAP_ADD_WLAN_TUNNELMODE_8023 1
#define CAPWAP_ADD_WLAN_TUNNELMODE_80211 2
#endif /* __KMOD_CAPWAP_RFC_HEADER__ */

View File

@ -3,67 +3,80 @@
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/smp.h>
#include "iface.h"
/* */
struct sc_netdev_priv {
struct net_device* dev;
struct sc_netdev_priv* next;
};
#include "station.h"
#include "capwap.h"
/* */
#define CAPWAP_IFACE_COUNT 8
#define CAPWAP_IFACE_HASH(x) ((x) % CAPWAP_IFACE_COUNT)
static uint32_t sc_iface_count;
static DEFINE_SPINLOCK(sc_iface_lock);
static struct sc_netdev_priv* sc_iface_hash[CAPWAP_IFACE_COUNT];
static LIST_HEAD(sc_iface_list);
static struct sc_netdev_priv* __rcu sc_iface_hash[CAPWAP_IFACE_COUNT];
/* */
static void sc_iface_netdev_uninit(struct net_device* dev) {
unsigned long flags;
struct sc_netdev_priv* search;
struct sc_capwap_station* temp;
struct sc_capwap_station* station;
int hash = CAPWAP_IFACE_HASH(dev->ifindex);
struct sc_netdev_priv* priv = (struct sc_netdev_priv*)netdev_priv(dev);
TRACEKMOD("### sc_iface_netdev_uninit\n");
/* Remove interface from hash */
spin_lock_irqsave(&sc_iface_lock, flags);
sc_capwap_update_lock();
search = sc_iface_hash[hash];
/* Close stations */
list_for_each_entry_safe(station, temp, &priv->list_stations, list_dev) {
sc_stations_releaseconnection(station);
sc_stations_free(station);
}
/* */
if (!list_empty(&priv->list_stations)) {
TRACEKMOD("*** Bug: the list stations of interface is not empty\n");
}
if (!list_empty(&priv->list_connections)) {
TRACEKMOD("*** Bug: the list connections of interface is not empty\n");
}
/* Remove interface from hash */
search = rcu_dereference_protected(sc_iface_hash[hash], sc_capwap_update_lock_is_locked());
if (search) {
if (priv == search) {
netif_tx_lock_bh(dev);
netif_carrier_off(dev);
netif_tx_unlock_bh(dev);
sc_iface_hash[hash] = priv->next;
rcu_assign_pointer(sc_iface_hash[hash], priv->next);
list_del_rcu(&priv->list);
synchronize_net();
dev_put(dev);
sc_iface_count--;
} else {
while (search->next && (search->next != priv)) {
search = search->next;
while (rcu_access_pointer(search->next) && (rcu_access_pointer(search->next) != priv)) {
search = rcu_dereference_protected(search->next, sc_capwap_update_lock_is_locked());
}
if (search->next) {
if (rcu_access_pointer(search->next)) {
netif_tx_lock_bh(dev);
netif_carrier_off(dev);
netif_tx_unlock_bh(dev);
search->next = priv->next;
rcu_assign_pointer(search->next, priv->next);
list_del_rcu(&priv->list);
synchronize_net();
dev_put(dev);
sc_iface_count--;
}
}
}
spin_unlock_irqrestore(&sc_iface_lock, flags);
/* Close stations with link to this device */
/* TODO */
sc_capwap_update_unlock();
}
/* */
@ -84,9 +97,40 @@ static int sc_iface_netdev_stop(struct net_device* dev) {
/* */
static int sc_iface_netdev_tx(struct sk_buff* skb, struct net_device* dev) {
TRACEKMOD("### sc_iface_netdev_tx\n");
struct sc_netdev_priv* priv = (struct sc_netdev_priv*)netdev_priv(dev);
TRACEKMOD("### sc_iface_netdev_tx %d\n", smp_processor_id());
if (dev->flags & IFF_UP) {
/* Ignore 802.1ad */
if (skb->vlan_proto == htons(ETH_P_8021AD) || (eth_hdr(skb)->h_proto == htons(ETH_P_8021AD))) {
goto drop;
}
/* */
spin_lock(&priv->lock);
dev->stats.tx_packets++;
dev->stats.tx_bytes += skb->len;
spin_unlock(&priv->lock);
/* */
CAPWAP_SKB_CB(skb)->flags = SKB_CAPWAP_FLAG_FROM_AC_TAP;
sc_capwap_recvpacket(skb);
} else {
goto drop;
}
return 0;
drop:
/* Drop packet */
kfree_skb(skb);
/* */
spin_lock(&priv->lock);
dev->stats.rx_dropped++;
spin_unlock(&priv->lock);
/* TODO */
return 0;
}
@ -100,13 +144,16 @@ static int sc_iface_netdev_change_mtu(struct net_device* dev, int new_mtu) {
/* */
static void sc_iface_netdev_setup(struct net_device* dev) {
struct sc_netdev_priv* priv = (struct sc_netdev_priv*)netdev_priv(dev);
struct sc_netdev_priv* devpriv = (struct sc_netdev_priv*)netdev_priv(dev);
TRACEKMOD("### sc_iface_netdev_setup\n");
/* */
memset(priv, 0, sizeof(struct sc_netdev_priv));
priv->dev = dev;
memset(devpriv, 0, sizeof(struct sc_netdev_priv));
devpriv->dev = dev;
spin_lock_init(&devpriv->lock);
INIT_LIST_HEAD(&devpriv->list_stations);
INIT_LIST_HEAD(&devpriv->list_connections);
}
/* */
@ -122,7 +169,6 @@ static const struct net_device_ops capwap_netdev_ops = {
int sc_iface_create(const char* ifname, uint16_t mtu) {
int err;
int hash;
unsigned long flags;
struct net_device* dev;
struct sc_netdev_priv* priv;
@ -156,14 +202,16 @@ int sc_iface_create(const char* ifname, uint16_t mtu) {
/* */
hash = CAPWAP_IFACE_HASH(dev->ifindex);
spin_lock_irqsave(&sc_iface_lock, flags);
/* */
sc_capwap_update_lock();
sc_iface_count++;
priv->next = sc_iface_hash[hash];
sc_iface_hash[hash] = priv;
list_add_rcu(&priv->list, &sc_iface_list);
priv->next = rcu_dereference_protected(sc_iface_hash[hash], sc_capwap_update_lock_is_locked());
rcu_assign_pointer(sc_iface_hash[hash], priv);
dev_hold(dev);
spin_unlock_irqrestore(&sc_iface_lock, flags);
sc_capwap_update_unlock();
/* Enable carrier */
netif_tx_lock_bh(dev);
@ -175,60 +223,77 @@ int sc_iface_create(const char* ifname, uint16_t mtu) {
/* */
int sc_iface_delete(uint32_t ifindex) {
unsigned long flags;
struct sc_netdev_priv* priv;
struct net_device* dev = NULL;
TRACEKMOD("### sc_iface_delete\n");
/* */
spin_lock_irqsave(&sc_iface_lock, flags);
rcu_read_lock();
priv = sc_iface_hash[CAPWAP_IFACE_HASH(ifindex)];
while (priv) {
if (priv->dev->ifindex == ifindex) {
break;
}
priv = priv->next;
/* Search device */
priv = sc_iface_search(ifindex);
if (priv) {
dev = priv->dev;
}
spin_unlock_irqrestore(&sc_iface_lock, flags);
rcu_read_unlock();
/* */
if (!priv) {
if (!dev) {
return -ENOENT;
}
/* */
unregister_netdev(priv->dev);
free_netdev(priv->dev);
/* Unregister device */
unregister_netdev(dev);
free_netdev(dev);
return 0;
}
/* */
struct sc_netdev_priv* sc_iface_search(uint32_t ifindex) {
struct sc_netdev_priv* priv;
TRACEKMOD("### sc_iface_search\n");
priv = rcu_dereference_check(sc_iface_hash[CAPWAP_IFACE_HASH(ifindex)], lockdep_is_held(&sc_iface_mutex));
while (priv) {
if (priv->dev->ifindex == ifindex) {
break;
}
/* */
priv = rcu_dereference_check(priv->next, lockdep_is_held(&sc_iface_mutex));
}
return priv;
}
/* */
void sc_iface_closeall(void) {
int i;
unsigned long flags;
struct sc_netdev_priv* priv;
TRACEKMOD("### sc_iface_closeall\n");
while (sc_iface_count) {
for (;;) {
struct net_device* dev = NULL;
spin_lock_irqsave(&sc_iface_lock, flags);
rcu_read_lock();
for (i = 0; i < CAPWAP_IFACE_COUNT; i++) {
if (sc_iface_hash[i]) {
dev = sc_iface_hash[i]->dev;
break;
}
/* Get device */
priv = list_first_or_null_rcu(&sc_iface_list, struct sc_netdev_priv, list);
if (priv) {
dev = priv->dev;
}
spin_unlock_irqrestore(&sc_iface_lock, flags);
rcu_read_unlock();
/* */
BUG_ON(!dev);
if (!dev) {
break;
}
/* Unregister device */
unregister_netdev(dev);
free_netdev(dev);
}

View File

@ -1,10 +1,27 @@
#ifndef __KMOD_AC_IFACE_HEADER__
#define __KMOD_AC_IFACE_HEADER__
/* */
struct sc_netdev_priv {
struct list_head list;
struct net_device* dev;
spinlock_t lock;
struct list_head list_stations;
struct list_head list_connections;
struct sc_netdev_priv* __rcu next;
};
/* */
int sc_iface_create(const char* ifname, uint16_t mtu);
int sc_iface_delete(uint32_t ifindex);
/* */
struct sc_netdev_priv* sc_iface_search(uint32_t ifindex);
/* */
void sc_iface_closeall(void);
#endif /* __KMOD_AC_IFACE_HEADER__ */

View File

@ -18,6 +18,10 @@
/* */
static u32 sc_netlink_usermodeid;
/* */
static int sc_netlink_pre_doit(struct genl_ops* ops, struct sk_buff* skb, struct genl_info* info);
static void sc_netlink_post_doit(struct genl_ops* ops, struct sk_buff* skb, struct genl_info* info);
/* Netlink Family */
static struct genl_family sc_netlink_family = {
.id = GENL_ID_GENERATE,
@ -26,8 +30,22 @@ static struct genl_family sc_netlink_family = {
.version = 1,
.maxattr = NLSMARTCAPWAP_ATTR_MAX,
.netnsok = true,
.pre_doit = sc_netlink_pre_doit,
.post_doit = sc_netlink_post_doit,
};
/* */
static int sc_netlink_pre_doit(struct genl_ops* ops, struct sk_buff* skb, struct genl_info* info) {
TRACEKMOD("### sc_netlink_pre_doit: %d\n", (int)ops->cmd);
return 0;
}
/* */
static void sc_netlink_post_doit(struct genl_ops* ops, struct sk_buff* skb, struct genl_info* info) {
TRACEKMOD("### sc_netlink_post_doit: %d\n", (int)ops->cmd);
}
/* */
static int sc_netlink_bind(struct sk_buff* skb, struct genl_info* info) {
union capwap_addr sockaddr;
@ -35,7 +53,7 @@ static int sc_netlink_bind(struct sk_buff* skb, struct genl_info* info) {
TRACEKMOD("### sc_netlink_bind\n");
/* Check Link */
if (sc_netlink_usermodeid != info->snd_portid) {
if (!sc_netlink_usermodeid) {
return -ENOLINK;
}
@ -55,58 +73,37 @@ static int sc_netlink_bind(struct sk_buff* skb, struct genl_info* info) {
/* */
static int sc_netlink_send_keepalive(struct sk_buff* skb, struct genl_info* info) {
int ret;
union capwap_addr sockaddr;
TRACEKMOD("### sc_netlink_send_keepalive\n");
/* Check Link */
if (sc_netlink_usermodeid != info->snd_portid) {
if (!sc_netlink_usermodeid) {
return -ENOLINK;
}
/* Check Session address */
if (!info->attrs[NLSMARTCAPWAP_ATTR_ADDRESS]) {
return -EINVAL;
}
/* */
memcpy(&sockaddr.ss, nla_data(info->attrs[NLSMARTCAPWAP_ATTR_ADDRESS]), sizeof(struct sockaddr_storage));
if ((sockaddr.ss.ss_family != AF_INET) && (sockaddr.ss.ss_family != AF_INET6)) {
/* Check Session ID */
if (!info->attrs[NLSMARTCAPWAP_ATTR_SESSION_ID]) {
return -EINVAL;
}
/* Send keep-alive packet */
ret = sc_capwap_sendkeepalive(&sockaddr);
if (ret < 0) {
return ret;
}
return 0;
return sc_capwap_sendkeepalive((struct sc_capwap_sessionid_element*)nla_data(info->attrs[NLSMARTCAPWAP_ATTR_SESSION_ID]));
}
/* */
static int sc_netlink_send_data(struct sk_buff* skb, struct genl_info* info) {
int length;
struct sk_buff* skbdata;
union capwap_addr sockaddr;
struct sc_skb_capwap_cb* cb;
TRACEKMOD("### sc_netlink_send_data\n");
/* Check Link */
if (sc_netlink_usermodeid != info->snd_portid) {
if (!sc_netlink_usermodeid) {
return -ENOLINK;
}
/* */
if (!info->attrs[NLSMARTCAPWAP_ATTR_ADDRESS] || !info->attrs[NLSMARTCAPWAP_ATTR_RADIOID] || !info->attrs[NLSMARTCAPWAP_ATTR_BINDING] || !info->attrs[NLSMARTCAPWAP_ATTR_DATA_FRAME]) {
return -EINVAL;
}
/* */
memcpy(&sockaddr.ss, nla_data(info->attrs[NLSMARTCAPWAP_ATTR_ADDRESS]), sizeof(struct sockaddr_storage));
if ((sockaddr.ss.ss_family != AF_INET) && (sockaddr.ss.ss_family != AF_INET6)) {
if (!info->attrs[NLSMARTCAPWAP_ATTR_SESSION_ID] || !info->attrs[NLSMARTCAPWAP_ATTR_RADIOID] || !info->attrs[NLSMARTCAPWAP_ATTR_BINDING] || !info->attrs[NLSMARTCAPWAP_ATTR_DATA_FRAME]) {
return -EINVAL;
}
@ -125,8 +122,8 @@ static int sc_netlink_send_data(struct sk_buff* skb, struct genl_info* info) {
/* */
cb = CAPWAP_SKB_CB(skbdata);
cb->flags = SKB_CAPWAP_FLAG_FROM_USER_SPACE | SKB_CAPWAP_FLAG_PEERADDRESS | SKB_CAPWAP_FLAG_RADIOID | SKB_CAPWAP_FLAG_BINDING;
sc_addr_tolittle(&sockaddr, &cb->peeraddr);
cb->flags = SKB_CAPWAP_FLAG_FROM_USER_SPACE | SKB_CAPWAP_FLAG_SESSIONID | SKB_CAPWAP_FLAG_RADIOID | SKB_CAPWAP_FLAG_BINDING;
memcpy(&cb->sessionid, nla_data(info->attrs[NLSMARTCAPWAP_ATTR_SESSION_ID]), sizeof(struct sc_capwap_sessionid_element));
cb->radioid = nla_get_u8(info->attrs[NLSMARTCAPWAP_ATTR_RADIOID]);
cb->binding = nla_get_u8(info->attrs[NLSMARTCAPWAP_ATTR_BINDING]);
@ -142,12 +139,12 @@ static int sc_netlink_new_session(struct sk_buff* skb, struct genl_info* info) {
TRACEKMOD("### sc_netlink_new_session\n");
/* Check Link */
if (sc_netlink_usermodeid != info->snd_portid) {
if (!sc_netlink_usermodeid) {
return -ENOLINK;
}
/* Check Session ID */
if (!info->attrs[NLSMARTCAPWAP_ATTR_SESSION_ID]) {
/* Check Session ID & Binding */
if (!info->attrs[NLSMARTCAPWAP_ATTR_SESSION_ID] || !info->attrs[NLSMARTCAPWAP_ATTR_BINDING]) {
return -EINVAL;
}
@ -160,7 +157,7 @@ static int sc_netlink_new_session(struct sk_buff* skb, struct genl_info* info) {
}
/* New session */
return sc_capwap_newsession((struct sc_capwap_sessionid_element*)nla_data(info->attrs[NLSMARTCAPWAP_ATTR_SESSION_ID]), mtu);
return sc_capwap_newsession((struct sc_capwap_sessionid_element*)nla_data(info->attrs[NLSMARTCAPWAP_ATTR_SESSION_ID]), nla_get_u8(info->attrs[NLSMARTCAPWAP_ATTR_BINDING]), mtu);
}
/* */
@ -168,7 +165,7 @@ static int sc_netlink_delete_session(struct sk_buff* skb, struct genl_info* info
TRACEKMOD("### sc_netlink_delete_session\n");
/* Check Link */
if (sc_netlink_usermodeid != info->snd_portid) {
if (!sc_netlink_usermodeid) {
return -ENOLINK;
}
@ -181,17 +178,119 @@ static int sc_netlink_delete_session(struct sk_buff* skb, struct genl_info* info
return sc_capwap_deletesession((struct sc_capwap_sessionid_element*)nla_data(info->attrs[NLSMARTCAPWAP_ATTR_SESSION_ID]));
}
/* */
static int sc_netlink_add_wlan(struct sk_buff* skb, struct genl_info* info) {
uint8_t radioid;
uint8_t wlanid;
TRACEKMOD("### sc_netlink_add_wlan\n");
/* Check Link */
if (!sc_netlink_usermodeid) {
return -ENOLINK;
}
/* Check params */
if (!info->attrs[NLSMARTCAPWAP_ATTR_SESSION_ID] || !info->attrs[NLSMARTCAPWAP_ATTR_RADIOID] || !info->attrs[NLSMARTCAPWAP_ATTR_WLANID] || !info->attrs[NLSMARTCAPWAP_ATTR_MACADDRESS] || !info->attrs[NLSMARTCAPWAP_ATTR_MACMODE] || !info->attrs[NLSMARTCAPWAP_ATTR_TUNNELMODE]) {
return -EINVAL;
}
/* */
radioid = nla_get_u8(info->attrs[NLSMARTCAPWAP_ATTR_RADIOID]);
wlanid = nla_get_u8(info->attrs[NLSMARTCAPWAP_ATTR_WLANID]);
if (!IS_VALID_RADIOID(radioid) || !IS_VALID_WLANID(wlanid)) {
return -EINVAL;
}
/* */
return sc_capwap_addwlan((struct sc_capwap_sessionid_element*)nla_data(info->attrs[NLSMARTCAPWAP_ATTR_SESSION_ID]), radioid, wlanid, (uint8_t*)nla_data(info->attrs[NLSMARTCAPWAP_ATTR_MACADDRESS]), nla_get_u8(info->attrs[NLSMARTCAPWAP_ATTR_MACMODE]), nla_get_u8(info->attrs[NLSMARTCAPWAP_ATTR_TUNNELMODE]));
}
/* */
static int sc_netlink_remove_wlan(struct sk_buff* skb, struct genl_info* info) {
uint8_t radioid;
uint8_t wlanid;
TRACEKMOD("### sc_netlink_remove_wlan\n");
/* Check Link */
if (!sc_netlink_usermodeid) {
return -ENOLINK;
}
/* Check Session ID */
if (!info->attrs[NLSMARTCAPWAP_ATTR_SESSION_ID] || !info->attrs[NLSMARTCAPWAP_ATTR_RADIOID] || !info->attrs[NLSMARTCAPWAP_ATTR_WLANID]) {
return -EINVAL;
}
/* */
radioid = nla_get_u8(info->attrs[NLSMARTCAPWAP_ATTR_RADIOID]);
wlanid = nla_get_u8(info->attrs[NLSMARTCAPWAP_ATTR_WLANID]);
if (!IS_VALID_RADIOID(radioid) || !IS_VALID_WLANID(wlanid)) {
return -EINVAL;
}
return sc_capwap_removewlan((struct sc_capwap_sessionid_element*)nla_data(info->attrs[NLSMARTCAPWAP_ATTR_SESSION_ID]), radioid, wlanid);
}
/* */
static int sc_netlink_auth_station(struct sk_buff* skb, struct genl_info* info) {
uint8_t radioid;
uint8_t wlanid;
uint16_t vlan = 0;
TRACEKMOD("### sc_netlink_auth_station\n");
/* Check Link */
if (!sc_netlink_usermodeid) {
return -ENOLINK;
}
/* Check params */
if (!info->attrs[NLSMARTCAPWAP_ATTR_SESSION_ID] || !info->attrs[NLSMARTCAPWAP_ATTR_MACADDRESS] || !info->attrs[NLSMARTCAPWAP_ATTR_IFPHY_INDEX] || !info->attrs[NLSMARTCAPWAP_ATTR_RADIOID] || !info->attrs[NLSMARTCAPWAP_ATTR_WLANID]) {
return -EINVAL;
}
/* */
radioid = nla_get_u8(info->attrs[NLSMARTCAPWAP_ATTR_RADIOID]);
wlanid = nla_get_u8(info->attrs[NLSMARTCAPWAP_ATTR_WLANID]);
if (!IS_VALID_RADIOID(radioid) || !IS_VALID_WLANID(wlanid)) {
return -EINVAL;
}
/* VLAN */
if (info->attrs[NLSMARTCAPWAP_ATTR_VLAN]) {
vlan = nla_get_u16(info->attrs[NLSMARTCAPWAP_ATTR_VLAN]);
}
/* */
return sc_capwap_authstation((struct sc_capwap_sessionid_element*)nla_data(info->attrs[NLSMARTCAPWAP_ATTR_SESSION_ID]), (uint8_t*)nla_data(info->attrs[NLSMARTCAPWAP_ATTR_MACADDRESS]), nla_get_u32(info->attrs[NLSMARTCAPWAP_ATTR_IFPHY_INDEX]), radioid, wlanid, vlan);
}
/* */
static int sc_netlink_deauth_station(struct sk_buff* skb, struct genl_info* info) {
TRACEKMOD("### sc_netlink_deauth_station\n");
/* Check Link */
if (!sc_netlink_usermodeid) {
return -ENOLINK;
}
/* Check params */
if (!info->attrs[NLSMARTCAPWAP_ATTR_SESSION_ID] || !info->attrs[NLSMARTCAPWAP_ATTR_MACADDRESS]) {
return -EINVAL;
}
/* */
return sc_capwap_deauthstation((struct sc_capwap_sessionid_element*)nla_data(info->attrs[NLSMARTCAPWAP_ATTR_SESSION_ID]), (uint8_t*)nla_data(info->attrs[NLSMARTCAPWAP_ATTR_MACADDRESS]));
}
/* */
static int sc_netlink_link(struct sk_buff* skb, struct genl_info* info) {
int ret;
TRACEKMOD("### sc_netlink_link\n");
if (!info->attrs[NLSMARTCAPWAP_ATTR_HASH_SESSION_BITFIELD] || !info->attrs[NLSMARTCAPWAP_ATTR_SESSION_THREADS_COUNT]) {
TRACEKMOD("*** Invalid link argument\n");
return -EINVAL;
}
/* */
if (sc_netlink_usermodeid) {
TRACEKMOD("*** Busy kernel link\n");
@ -199,7 +298,7 @@ static int sc_netlink_link(struct sk_buff* skb, struct genl_info* info) {
}
/* Initialize library */
ret = sc_capwap_init(nla_get_u32(info->attrs[NLSMARTCAPWAP_ATTR_HASH_SESSION_BITFIELD]), nla_get_u32(info->attrs[NLSMARTCAPWAP_ATTR_SESSION_THREADS_COUNT]));
ret = sc_capwap_init();
if (ret) {
return ret;
}
@ -222,7 +321,7 @@ static int sc_netlink_add_iface(struct sk_buff* skb, struct genl_info* info) {
TRACEKMOD("### sc_netlink_add_iface\n");
/* Check Link */
if (sc_netlink_usermodeid != info->snd_portid) {
if (!sc_netlink_usermodeid) {
return -ENOLINK;
}
@ -277,7 +376,7 @@ static int sc_netlink_delete_iface(struct sk_buff* skb, struct genl_info* info)
TRACEKMOD("### sc_netlink_delete_iface\n");
/* Check Link */
if (sc_netlink_usermodeid != info->snd_portid) {
if (!sc_netlink_usermodeid) {
return -ENOLINK;
}
@ -311,14 +410,18 @@ static const struct nla_policy sc_netlink_policy[NLSMARTCAPWAP_ATTR_MAX + 1] = {
[NLSMARTCAPWAP_ATTR_FLAGS] = { .type = NLA_U32 },
[NLSMARTCAPWAP_ATTR_SESSION_ID] = { .type = NLA_BINARY, .len = sizeof(struct sc_capwap_sessionid_element) },
[NLSMARTCAPWAP_ATTR_RADIOID] = { .type = NLA_U8 },
[NLSMARTCAPWAP_ATTR_WLANID] = { .type = NLA_U8 },
[NLSMARTCAPWAP_ATTR_BINDING] = { .type = NLA_U8 },
[NLSMARTCAPWAP_ATTR_MACMODE] = { .type = NLA_U8 },
[NLSMARTCAPWAP_ATTR_TUNNELMODE] = { .type = NLA_U8 },
[NLSMARTCAPWAP_ATTR_ADDRESS] = { .type = NLA_BINARY, .len = sizeof(struct sockaddr_storage) },
[NLSMARTCAPWAP_ATTR_DATA_FRAME] = { .type = NLA_BINARY, .len = IEEE80211_MAX_DATA_LEN + CAPWAP_HEADER_MAX_LENGTH },
[NLSMARTCAPWAP_ATTR_MTU] = { .type = NLA_U16 },
[NLSMARTCAPWAP_ATTR_HASH_SESSION_BITFIELD] = { .type = NLA_U32 },
[NLSMARTCAPWAP_ATTR_SESSION_THREADS_COUNT] = { .type = NLA_U32 },
[NLSMARTCAPWAP_ATTR_IFPHY_NAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ },
[NLSMARTCAPWAP_ATTR_IFPHY_INDEX] = { .type = NLA_U32 },
[NLSMARTCAPWAP_ATTR_MACADDRESS] = { .type = NLA_BINARY, .len = MACADDRESS_EUI48_LENGTH },
[NLSMARTCAPWAP_ATTR_BSSID] = { .type = NLA_BINARY, .len = MACADDRESS_EUI48_LENGTH },
[NLSMARTCAPWAP_ATTR_VLAN] = { .type = NLA_U16 },
};
/* Netlink Ops */
@ -371,6 +474,30 @@ static struct genl_ops sc_netlink_ops[] = {
.policy = sc_netlink_policy,
.flags = GENL_ADMIN_PERM,
},
{
.cmd = NLSMARTCAPWAP_CMD_ADD_WLAN,
.doit = sc_netlink_add_wlan,
.policy = sc_netlink_policy,
.flags = GENL_ADMIN_PERM,
},
{
.cmd = NLSMARTCAPWAP_CMD_REMOVE_WLAN,
.doit = sc_netlink_remove_wlan,
.policy = sc_netlink_policy,
.flags = GENL_ADMIN_PERM,
},
{
.cmd = NLSMARTCAPWAP_CMD_AUTH_STATION,
.doit = sc_netlink_auth_station,
.policy = sc_netlink_policy,
.flags = GENL_ADMIN_PERM,
},
{
.cmd = NLSMARTCAPWAP_CMD_DEAUTH_STATION,
.doit = sc_netlink_deauth_station,
.policy = sc_netlink_policy,
.flags = GENL_ADMIN_PERM,
},
};
/* Netlink notify */
@ -398,8 +525,7 @@ int sc_netlink_notify_recv_keepalive(const union capwap_addr* sockaddr, struct s
}
/* */
if (nla_put(sk_msg, NLSMARTCAPWAP_ATTR_ADDRESS, sizeof(struct sockaddr_storage), &sockaddr->ss) ||
nla_put(sk_msg, NLSMARTCAPWAP_ATTR_SESSION_ID, sizeof(struct sc_capwap_sessionid_element), sessionid)) {
if (nla_put(sk_msg, NLSMARTCAPWAP_ATTR_SESSION_ID, sizeof(struct sc_capwap_sessionid_element), sessionid)) {
goto error2;
}

View File

@ -16,19 +16,24 @@ enum sc_netlink_attrs {
NLSMARTCAPWAP_ATTR_SESSION_ID,
NLSMARTCAPWAP_ATTR_RADIOID,
NLSMARTCAPWAP_ATTR_WLANID,
NLSMARTCAPWAP_ATTR_BINDING,
NLSMARTCAPWAP_ATTR_MACMODE,
NLSMARTCAPWAP_ATTR_TUNNELMODE,
NLSMARTCAPWAP_ATTR_ADDRESS,
NLSMARTCAPWAP_ATTR_MTU,
NLSMARTCAPWAP_ATTR_DATA_FRAME,
NLSMARTCAPWAP_ATTR_HASH_SESSION_BITFIELD,
NLSMARTCAPWAP_ATTR_SESSION_THREADS_COUNT,
NLSMARTCAPWAP_ATTR_IFPHY_NAME,
NLSMARTCAPWAP_ATTR_IFPHY_INDEX,
NLSMARTCAPWAP_ATTR_MACADDRESS,
NLSMARTCAPWAP_ATTR_BSSID,
NLSMARTCAPWAP_ATTR_VLAN,
/* Last attribute */
__NLSMARTCAPWAP_ATTR_AFTER_LAST,
NLSMARTCAPWAP_ATTR_MAX = __NLSMARTCAPWAP_ATTR_AFTER_LAST - 1
@ -51,9 +56,15 @@ enum sc_netlink_commands {
NLSMARTCAPWAP_CMD_NEW_SESSION,
NLSMARTCAPWAP_CMD_DELETE_SESSION,
NLSMARTCAPWAP_CMD_ADD_WLAN,
NLSMARTCAPWAP_CMD_REMOVE_WLAN,
NLSMARTCAPWAP_CMD_SEND_DATA,
NLSMARTCAPWAP_CMD_RECV_DATA,
NLSMARTCAPWAP_CMD_AUTH_STATION,
NLSMARTCAPWAP_CMD_DEAUTH_STATION,
/* Last command */
__NLSMARTCAPWAP_CMD_AFTER_LAST,
NLSMARTCAPWAP_CMD_MAX = __NLSMARTCAPWAP_CMD_AFTER_LAST - 1

View File

@ -225,29 +225,3 @@ int sc_addr_compare(const union capwap_addr* addr1, const union capwap_addr* add
return -1;
}
/* */
void sc_addr_tolittle(const union capwap_addr* addr, struct capwap_addr_little* little) {
little->family = (uint8_t)addr->ss.ss_family;
if (addr->ss.ss_family == AF_INET) {
memcpy(&little->addr4, &addr->sin.sin_addr, sizeof(struct in_addr));
little->port = addr->sin.sin_port;
} else if (addr->ss.ss_family == AF_INET6) {
memcpy(&little->addr6, &addr->sin6.sin6_addr, sizeof(struct in6_addr));
little->port = addr->sin6.sin6_port;
}
}
/* */
void sc_addr_fromlittle(const struct capwap_addr_little* little, union capwap_addr* addr) {
memset(addr, 0, sizeof(union capwap_addr));
addr->ss.ss_family = little->family;
if (little->family == AF_INET) {
memcpy(&addr->sin.sin_addr, &little->addr4, sizeof(struct in_addr));
addr->sin.sin_port = little->port;
} else if (little->family == AF_INET6) {
memcpy(&addr->sin6.sin6_addr, &little->addr6, sizeof(struct in6_addr));
addr->sin6.sin6_port = little->port;
}
}

View File

@ -10,16 +10,6 @@
#define SOCKET_UDP 0
#define SOCKET_UDPLITE 1
/* Little socket address */
struct capwap_addr_little {
uint8_t family;
union {
struct in_addr addr4;
struct in6_addr addr6;
};
uint16_t port;
};
/* Universal socket address */
union capwap_addr {
struct sockaddr sa;
@ -35,11 +25,11 @@ void sc_socket_close(void);
/* */
int sc_socket_bind(union capwap_addr* sockaddr);
int sc_socket_send(int type, uint8_t* buffer, int length, union capwap_addr* sockaddr);
/* */
int sc_socket_getpeeraddr(struct sk_buff* skb, union capwap_addr* peeraddr);
/* */
int sc_addr_compare(const union capwap_addr* addr1, const union capwap_addr* addr2);
void sc_addr_tolittle(const union capwap_addr* addr, struct capwap_addr_little* little);
void sc_addr_fromlittle(const struct capwap_addr_little* little, union capwap_addr* addr);
#endif /* __KMOD_SOCKET_HEADER__ */

157
src/ac/kmod/station.c Normal file
View File

@ -0,0 +1,157 @@
#include "config.h"
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/netdevice.h>
#include "station.h"
#include "capwap.h"
#include "iface.h"
/* */
#define STATION_HASH_SIZE 65536
/* */
static LIST_HEAD(sc_station_list);
static struct sc_capwap_station* __rcu sc_station_hash_addr[STATION_HASH_SIZE];
/* */
static uint32_t sc_stations_hash_addr(const uint8_t* macaddress) {
TRACEKMOD("### sc_stations_hash_addr\n");
return (((((uint32_t)macaddress[4] << 8) | (uint32_t)macaddress[5]) ^ ((uint32_t)macaddress[3] << 4)) % STATION_HASH_SIZE);
}
/* */
static struct sc_capwap_connection* sc_stations_searchconnection(struct sc_capwap_station* station) {
struct sc_capwap_connection* connection;
struct sc_capwap_session_priv* sessionpriv = rcu_access_pointer(station->sessionpriv);
struct sc_netdev_priv* devpriv = rcu_access_pointer(station->devpriv);
TRACEKMOD("### sc_stations_searchconnection\n");
list_for_each_entry(connection, &sessionpriv->list_connections, list_session) {
if ((connection->devpriv == devpriv) && (connection->radioid == station->radioid) && (connection->vlan == station->vlan)) {
return connection;
}
}
return NULL;
}
/* */
void sc_stations_add(struct sc_capwap_station* station) {
uint32_t hash;
TRACEKMOD("### sc_stations_add\n");
/* */
list_add_rcu(&station->list, &sc_station_list);
hash = sc_stations_hash_addr(station->address);
station->next_addr = rcu_dereference_protected(sc_station_hash_addr[hash], sc_capwap_update_lock_is_locked());
rcu_assign_pointer(sc_station_hash_addr[hash], station);
}
/* */
struct sc_capwap_station* sc_stations_search(const uint8_t* macaddress) {
struct sc_capwap_station* station;
TRACEKMOD("### sc_stations_search\n");
/* */
station = rcu_dereference_check(sc_station_hash_addr[sc_stations_hash_addr(macaddress)], sc_capwap_update_lock_is_locked());
while (station) {
if (!memcmp(&station->address, macaddress, MACADDRESS_EUI48_LENGTH)) {
break;
}
/* */
station = rcu_dereference_check(station->next_addr, sc_capwap_update_lock_is_locked());
}
return station;
}
/* */
void sc_stations_free(struct sc_capwap_station* station) {
uint32_t hash;
struct sc_capwap_station* search;
TRACEKMOD("### sc_stations_free\n");
/* */
hash = sc_stations_hash_addr(station->address);
search = rcu_dereference_protected(sc_station_hash_addr[hash], sc_capwap_update_lock_is_locked());
if (search) {
if (search == station) {
rcu_assign_pointer(sc_station_hash_addr[hash], station->next_addr);
} else {
while (rcu_access_pointer(search->next_addr) && (rcu_access_pointer(search->next_addr) != station)) {
search = rcu_dereference_protected(search->next_addr, sc_capwap_update_lock_is_locked());
}
if (rcu_access_pointer(search->next_addr)) {
rcu_assign_pointer(search->next_addr, station->next_addr);
}
}
}
/* */
list_del_rcu(&station->list_dev);
list_del_rcu(&station->list_session);
synchronize_net();
kfree(station);
}
/* */
int sc_stations_setconnection(struct sc_capwap_station* station) {
struct sc_capwap_connection* connection;
TRACEKMOD("### sc_stations_setconnection\n");
/* */
connection = sc_stations_searchconnection(station);
if (!connection) {
connection = (struct sc_capwap_connection*)kzalloc(sizeof(struct sc_capwap_connection), GFP_KERNEL);
if (!connection) {
TRACEKMOD("*** Unable to create connection\n");
return -ENOMEM;
}
/* */
connection->sessionpriv = rcu_access_pointer(station->sessionpriv);
list_add_rcu(&connection->list_session, &connection->sessionpriv->list_connections);
connection->devpriv = rcu_access_pointer(station->devpriv);
list_add_rcu(&connection->list_dev, &connection->devpriv->list_connections);
connection->radioid = station->radioid;
connection->vlan = station->vlan;
}
/* */
connection->count++;
connection->wlanidmask |= 1 << (station->wlanid - 1);
return 0;
}
/* */
void sc_stations_releaseconnection(struct sc_capwap_station* station) {
struct sc_capwap_connection* connection;
TRACEKMOD("### sc_stations_releaseconnection\n");
connection = sc_stations_searchconnection(station);
if (connection) {
TRACEKMOD("*** Release connection reference %d\n", connection->count);
connection->count--;
if (!connection->count) {
list_del_rcu(&connection->list_session);
list_del_rcu(&connection->list_dev);
synchronize_net();
kfree(connection);
}
}
}

58
src/ac/kmod/station.h Normal file
View File

@ -0,0 +1,58 @@
#ifndef __KMOD_AC_STATION_HEADER__
#define __KMOD_AC_STATION_HEADER__
#include "capwap_rfc.h"
/* */
struct sc_capwap_connection {
int count;
/* Session */
struct sc_capwap_session_priv* sessionpriv;
struct list_head list_session;
/* Interface */
struct sc_netdev_priv* devpriv;
struct list_head list_dev;
/* */
uint16_t vlan;
uint8_t radioid;
uint8_t wlanidmask;
};
/* */
struct sc_capwap_station {
struct list_head list;
/* */
uint8_t address[MACADDRESS_EUI48_LENGTH];
struct sc_capwap_station* __rcu next_addr;
/* Session */
struct sc_capwap_session_priv* __rcu sessionpriv;
struct list_head list_session;
/* Interface */
struct sc_netdev_priv* __rcu devpriv;
struct list_head list_dev;
/* */
uint8_t bssid[MACADDRESS_EUI48_LENGTH];
uint16_t vlan;
uint8_t radioid;
uint8_t wlanid;
};
/* */
void sc_stations_add(struct sc_capwap_station* station);
void sc_stations_free(struct sc_capwap_station* station);
/* */
struct sc_capwap_station* sc_stations_search(const uint8_t* macaddress);
/* */
int sc_stations_setconnection(struct sc_capwap_station* station);
void sc_stations_releaseconnection(struct sc_capwap_station* station);
#endif /* __KMOD_AC_STATION_HEADER__ */