The capwap data channel migrated from userspace to kernalspace

This commit is contained in:
vemax78
2014-09-10 21:58:23 +02:00
parent 71006a9121
commit 8d9985fdea
104 changed files with 6967 additions and 4840 deletions

View File

@ -4,7 +4,10 @@ obj-m += smartcapwap.o
smartcapwap-y := \
main.o \
netlinkapp.o
netlinkapp.o \
capwap.o \
capwap_private.o \
socket.o
all:
make -C /lib/modules/$(KVERSION)/build M="$(PWD)" modules

621
src/ac/kmod/capwap.c Normal file
View File

@ -0,0 +1,621 @@
#include "config.h"
#include <linux/module.h>
#include <linux/ieee80211.h>
#include "socket.h"
#include "capwap.h"
#include "nlsmartcapwap.h"
#include "netlinkapp.h"
/* */
#define TIMEOUT_PACKET 10
/* */
union capwap_addr sc_localaddr;
/* */
static void sc_capwap_fragment_free(struct sc_capwap_fragment* fragment) {
TRACEKMOD("### sc_capwap_fragment_free\n");
/* */
list_del(&fragment->lru_list);
fragment->flags = 0;
/* Free socket buffer */
while (fragment->fragments) {
struct sk_buff* next = fragment->fragments->next;
kfree_skb(fragment->fragments);
fragment->fragments = next;
}
}
/* */
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");
/* */
if (now.tv64 == 0) {
TRACEKMOD("*** Get time\n");
now = ktime_get();
}
/* Remove last old fragment */
if (!list_empty(&session->fragments.lru_list)) {
spin_lock_irqsave(&session->fragments.lock, flags);
fragment = list_first_entry(&session->fragments.lru_list, struct sc_capwap_fragment, lru_list);
if (fragment) {
delta = ktime_sub(now, fragment->tstamp);
if ((delta.tv64 < 0) || (delta.tv64 > NSEC_PER_SEC)) {
TRACEKMOD("*** Expired fragment %hu\n", fragment->fragmentid);
/* Reset fragment */
sc_capwap_fragment_free(fragment);
}
}
spin_unlock_irqrestore(&session->fragments.lock, flags);
}
}
/* */
static struct sk_buff* sc_capwap_reasm(struct sc_capwap_fragment* fragment) {
int len;
int offset;
struct sk_buff* skb;
struct sk_buff* skbfrag;
struct sc_capwap_header* header;
/* */
skbfrag = fragment->fragments;
len = GET_HLEN_HEADER((struct sc_capwap_header*)skbfrag->data) * 4;
/* Create new packet */
skb = alloc_skb(len + fragment->totallength, GFP_KERNEL);
if (!skb) {
return NULL;
}
/* The first capwap header is header of reassembled packet without fragment field */
header = (struct sc_capwap_header*)skb_put(skb, len);
memcpy(header, skb->data, len);
SET_FLAG_F_HEADER(header, 0);
SET_FLAG_L_HEADER(header, 0);
header->frag_id = (__be16)0;
header->frag_off = (__be16)0;
/* Copy body */
while (skbfrag) {
offset = GET_HLEN_HEADER((struct sc_capwap_header*)skbfrag->data) * 4;
len = skb->len - offset;
/* */
memcpy(skb_put(skb, len), skb->data + offset, len);
skbfrag = skbfrag->next;
}
return skb;
}
/* */
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;
struct sk_buff* next;
struct sc_capwap_fragment* fragment;
struct sc_skb_capwap_cb* cb;
struct sk_buff* skb_defrag = NULL;
struct sc_capwap_header* header = (struct sc_capwap_header*)skb->data;
TRACEKMOD("### sc_capwap_defrag\n");
/* */
headersize = GET_HLEN_HEADER(header) * 4;
if (skb->len < headersize) {
goto error;
}
/* */
frag_id = be16_to_cpu(header->frag_id);
/* */
cb = CAPWAP_SKB_CB(skb);
cb->flags |= SKB_CAPWAP_FLAG_FRAGMENT;
cb->frag_offset = be16_to_cpu(header->frag_off);
cb->frag_length = skb->len - headersize;
/* */
spin_lock_irqsave(&session->fragments.lock, flags);
/* Get fragment */
fragment = &session->fragments.queues[frag_id % CAPWAP_FRAGMENT_QUEUE];
if ((fragment->flags & CAPWAP_FRAGMENT_ENABLE) && (fragment->fragmentid != frag_id)) {
goto error2; /* Queue fragment busy*/
}
/* Init fragment */
if (!(fragment->flags & CAPWAP_FRAGMENT_ENABLE)) {
fragment->flags = CAPWAP_FRAGMENT_ENABLE;
fragment->fragmentid = frag_id;
fragment->fragments = NULL;
fragment->lastfragment = NULL;
fragment->recvlength = 0;
fragment->totallength = 0;
list_add_tail(&fragment->lru_list, &session->fragments.lru_list);
}
/* Search fragment position */
prev = fragment->lastfragment;
if (!prev) {
next = NULL;
} else if (CAPWAP_SKB_CB(prev)->frag_offset < cb->frag_offset) {
if ((CAPWAP_SKB_CB(prev)->frag_offset + CAPWAP_SKB_CB(prev)->frag_length) < cb->frag_offset) {
next = NULL;
} else {
sc_capwap_fragment_free(fragment);
goto error2; /* Overlap error */
}
} else {
prev = NULL;
for (next = fragment->fragments; next != NULL; next = next->next) {
struct sc_skb_capwap_cb* next_cb = CAPWAP_SKB_CB(next);
if (next_cb->frag_offset < cb->frag_offset) {
if ((next_cb->frag_offset + next_cb->frag_length) < cb->frag_offset) {
break;
} else {
sc_capwap_fragment_free(fragment);
goto error2; /* Overlap error */
}
}
prev = next;
}
}
/* Insert fragment */
skb->prev = NULL;
skb->next = next;
if (!next) {
fragment->lastfragment = skb;
}
if (prev) {
prev->next = skb;
} else {
fragment->fragments = skb;
}
/* Update size */
fragment->recvlength += cb->frag_length;
if (IS_FLAG_L_HEADER(header)) {
fragment->totallength = cb->frag_offset + cb->frag_length;
fragment->flags |= CAPWAP_FRAGMENT_LAST;
}
/* Check if receive all fragment */
if ((fragment->flags & CAPWAP_FRAGMENT_LAST) && (fragment->recvlength == fragment->totallength)) {
skb_defrag = sc_capwap_reasm(fragment);
/* Free fragment complete */
sc_capwap_fragment_free(fragment);
} else {
/* Update timeout */
fragment->tstamp = skb->tstamp;
if (fragment->tstamp.tv64 == 0) {
fragment->tstamp = ktime_get();
}
/* Set LRU timeout */
if (!list_is_last(&fragment->lru_list, &session->fragments.lru_list)) {
list_move_tail(&fragment->lru_list, &session->fragments.lru_list);
}
}
spin_unlock_irqrestore(&session->fragments.lock, flags);
return skb_defrag;
error2:
spin_unlock_irqrestore(&session->fragments.lock, flags);
error:
kfree_skb(skb);
return NULL;
}
/* */
int sc_capwap_bind(union capwap_addr* sockaddr) {
int ret;
TRACEKMOD("### sc_capwap_bind\n");
/* */
ret = sc_socket_bind(sockaddr);
if (ret) {
return ret;
}
memcpy(&sc_localaddr, sockaddr, sizeof(union capwap_addr));
return 0;
}
/* */
void sc_capwap_initsession(struct sc_capwap_session* session) {
TRACEKMOD("### sc_capwap_initsession\n");
INIT_LIST_HEAD(&session->list);
spin_lock_init(&session->fragmentid_lock);
/* Defragment packets */
memset(&session->fragments, 0, sizeof(struct sc_capwap_fragment_queue));
INIT_LIST_HEAD(&session->fragments.lru_list);
spin_lock_init(&session->fragments.lock);
}
/* */
void sc_capwap_freesession(struct sc_capwap_session* session) {
struct sc_capwap_fragment* temp;
struct sc_capwap_fragment* fragment;
TRACEKMOD("### sc_capwap_freesession\n");
/* Free socket buffers */
list_for_each_entry_safe(fragment, temp, &session->fragments.lru_list, lru_list) {
sc_capwap_fragment_free(fragment);
}
}
/* */
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);
fragmentid = session->fragmentid++;
spin_unlock_irqrestore(&session->fragmentid_lock, flags);
return fragmentid;
}
/* */
int sc_capwap_createkeepalive(struct sc_capwap_sessionid_element* sessionid, uint8_t* buffer, int size) {
int length;
struct sc_capwap_header* header;
struct sc_capwap_data_message* dataheader;
struct sc_capwap_message_element* msgelement;
TRACEKMOD("### sc_capwap_createkeepalive\n");
/* */
if (size < CAPWAP_KEEP_ALIVE_MAX_SIZE) {
return -ENOMEM;
}
/* Preamble CAPWAP header */
header = (struct sc_capwap_header*)buffer;
length = sizeof(struct sc_capwap_header);
buffer += sizeof(struct sc_capwap_header);
memset(header, 0, sizeof(struct sc_capwap_header));
SET_VERSION_HEADER(header, CAPWAP_PROTOCOL_VERSION);
SET_TYPE_HEADER(header, CAPWAP_PREAMBLE_HEADER);
SET_HLEN_HEADER(header, sizeof(struct sc_capwap_header) / 4);
SET_WBID_HEADER(header, CAPWAP_WIRELESS_BINDING_IEEE80211);
SET_FLAG_K_HEADER(header, 1);
/* CAPWAP Data header */
dataheader = (struct sc_capwap_data_message*)buffer;
length += sizeof(struct sc_capwap_data_message);
buffer += sizeof(struct sc_capwap_data_message);
dataheader->length = cpu_to_be16(sizeof(struct sc_capwap_data_message) + sizeof(struct sc_capwap_message_element) + sizeof(struct sc_capwap_sessionid_element));
/* CAPWAP Keep-Alive Message Element */
msgelement = (struct sc_capwap_message_element*)buffer;
length += sizeof(struct sc_capwap_message_element);
buffer += sizeof(struct sc_capwap_message_element);
msgelement->type = cpu_to_be16(CAPWAP_ELEMENT_SESSIONID);
msgelement->length = cpu_to_be16(sizeof(struct sc_capwap_sessionid_element));
/* Session ID */
memcpy(buffer, sessionid, sizeof(struct sc_capwap_sessionid_element));
length += sizeof(struct sc_capwap_sessionid_element);
return length;
}
/* */
int sc_capwap_parsingpacket(struct sc_capwap_session* session, const union capwap_addr* sockaddr, struct sk_buff* skb) {
int length;
uint16_t headersize;
struct sc_capwap_data_message* dataheader;
struct sc_capwap_message_element* message;
struct sc_capwap_header* header = (struct sc_capwap_header*)skb->data;
TRACEKMOD("### sc_capwap_parsingpacket\n");
/* Linearize socket buffer */
if (skb_linearize(skb)) {
TRACEKMOD("*** Unable to linearize packet\n");
return -EINVAL;
}
/* Check header */
if (skb->len < sizeof(struct sc_capwap_header)) {
TRACEKMOD("*** Invalid capwap header length\n");
return -EINVAL;
} else if (GET_VERSION_HEADER(header) != CAPWAP_PROTOCOL_VERSION) {
TRACEKMOD("*** Invalid capwap header version\n");
return -EINVAL;
} else if (GET_TYPE_HEADER(header) != CAPWAP_PREAMBLE_HEADER) {
TRACEKMOD("*** Packet is encrypted\n");
return -EINVAL; /* Accept only plain packet */
}
/* Cleaning old fragments */
if (session) {
sc_capwap_defrag_evictor(session, skb->tstamp);
}
/* */
if (IS_FLAG_K_HEADER(header)) {
/* Keep alive can not fragment */
if (IS_FLAG_F_HEADER(header)) {
TRACEKMOD("*** Keep alive can not fragment\n");
return -EINVAL;
}
/* */
length = skb->len;
headersize = GET_HLEN_HEADER(header) * 4;
if (length < (headersize + sizeof(struct sc_capwap_data_message))) {
TRACEKMOD("*** Invalid capwap data header length\n");
return -EINVAL;
}
/* Data message */
length -= headersize;
dataheader = (struct sc_capwap_data_message*)(((uint8_t*)header) + headersize);
headersize = ntohs(dataheader->length);
if (length < headersize) {
TRACEKMOD("*** Capwap data header length mismatch\n");
return -EINVAL;
}
/* Message elements */
headersize -= sizeof(struct sc_capwap_data_message);
message = (struct sc_capwap_message_element*)(((uint8_t*)dataheader) + sizeof(struct sc_capwap_data_message));
while (headersize > 0) {
uint16_t msglength = ntohs(message->length);
if (headersize < (msglength + sizeof(struct sc_capwap_message_element))) {
TRACEKMOD("*** Invalid capwap message element length\n");
return -EINVAL;
}
/* */
if ((ntohs(message->type) == CAPWAP_ELEMENT_SESSIONID) && (msglength == sizeof(struct sc_capwap_sessionid_element))) {
struct sc_capwap_sessionid_element* sessionid = (struct sc_capwap_sessionid_element*)(((uint8_t*)message) + sizeof(struct sc_capwap_message_element));
if (!session) {
session = sc_capwap_recvunknownkeepalive(sockaddr, sessionid);
if (!session) {
TRACEKMOD("*** Receive unknown keep alive without valid session\n");
return -EINVAL;
}
} else if (memcmp(&session->sessionid, sessionid, sizeof(struct sc_capwap_sessionid_element))) {
TRACEKMOD("*** Session id mismatch\n");
return -EINVAL;
}
/* Session found */
sc_netlink_notify_recv_keepalive(sockaddr, sessionid);
/* Parsing complete */
kfree_skb(skb);
return 0;
}
/* Next message element */
msglength += sizeof(struct sc_capwap_message_element);
message = (struct sc_capwap_message_element*)(((uint8_t*)message) + msglength);
headersize -= msglength;
}
} else if (session) {
if (IS_FLAG_F_HEADER(header)) {
skb = sc_capwap_defrag(session, skb);
if (!skb) {
return 0;
}
/* Get new header info */
header = (struct sc_capwap_header*)skb->data;
}
/* Parsing data/management packet */
if (!IS_FLAG_T_HEADER(header)) {
sc_capwap_parsingdatapacket(session, skb);
} else if (GET_WBID_HEADER(header) == CAPWAP_WIRELESS_BINDING_IEEE80211) {
struct ieee80211_hdr* hdr = (struct ieee80211_hdr*)(skb->data + GET_HLEN_HEADER(header) * 4);
if (ieee80211_is_data_present(hdr->frame_control)) {
sc_capwap_parsingdatapacket(session, skb);
} else if (ieee80211_is_mgmt(hdr->frame_control) || ieee80211_is_ctl(hdr->frame_control)) {
sc_capwap_parsingmgmtpacket(session, skb);
}
}
/* Parsing complete */
kfree_skb(skb);
return 0;
}
return -EINVAL;
}
/* */
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 size;
int length;
int reserve;
int headroom;
int requestfragment;
__be16 fragmentid = 0;
int fragmentoffset = 0;
struct sc_capwap_header* header;
struct sk_buff* clone = NULL;
int packetlength = skb->len;
TRACEKMOD("### sc_capwap_forwarddata\n");
/* Check headroom */
headroom = skb_headroom(skb);
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);
if (!clone) {
printk("*** Unable to expand socket buffer\n");
return -ENOMEM;
}
skb = clone;
}
/* Check MTU */
requestfragment = (((packetlength + reserve) > session->mtu) ? 1 : 0);
if (requestfragment) {
fragmentid = cpu_to_be16(sc_capwap_newfragmentid(session));
}
/* */
header = (struct sc_capwap_header*)skb_push(skb, sizeof(struct sc_capwap_header) + radioaddrlength + winfolength);
while (packetlength > 0) {
memset(header, 0, sizeof(struct sc_capwap_header));
SET_VERSION_HEADER(header, CAPWAP_PROTOCOL_VERSION);
SET_TYPE_HEADER(header, CAPWAP_PREAMBLE_HEADER);
SET_WBID_HEADER(header, binding);
SET_RID_HEADER(header, radioid);
SET_FLAG_T_HEADER(header, ((flags & NLSMARTCAPWAP_FLAGS_TUNNEL_8023) ? 0 : 1));
if (!fragmentoffset) {
uint8_t* headeroption = (uint8_t*)header + sizeof(struct sc_capwap_header);
if (radioaddr) {
SET_FLAG_M_HEADER(header, 1);
memcpy(headeroption, radioaddr, radioaddrlength);
headeroption += radioaddrlength;
}
if (winfo) {
SET_FLAG_W_HEADER(header, 1);
memcpy(headeroption, winfo, winfolength);
headeroption += winfolength;
}
size = sizeof(struct sc_capwap_header) + radioaddrlength + winfolength;
SET_HLEN_HEADER(header, size / 4);
} else {
size = sizeof(struct sc_capwap_header);
SET_HLEN_HEADER(header, size / 4);
}
/* Calculate body size */
length = session->mtu - size;
if (packetlength <= length) {
length = packetlength;
} else if (requestfragment) {
length -= length % 8; /* Capwap fragment size is module 8 */
} else {
break;
}
/* Fragment options */
if (requestfragment) {
SET_FLAG_F_HEADER(header, 1);
if (packetlength == length) {
SET_FLAG_L_HEADER(header, 1);
}
header->frag_id = fragmentid;
header->frag_off = cpu_to_be16(fragmentoffset);
}
/* Send packet */
if (sc_socket_send(SOCKET_UDP, (uint8_t*)header, (size + length), &session->peeraddr) < 0) {
break;
}
/* */
header = (struct sc_capwap_header*)((uint8_t*)header + (size + length));
fragmentoffset += length;
packetlength -= length;
}
if (clone) {
kfree_skb(clone);
}
return (!packetlength ? 0 : -EIO);
}
/* */
void sc_capwap_sessionid_printf(const struct sc_capwap_sessionid_element* sessionid, char* string) {
int i;
char* pos = string;
for (i = 0; i < 16; i++) {
snprintf(pos, 3, "%02x", sessionid->id[i]);
pos += 2;
}
*pos = 0;
}
/* */
struct sc_capwap_radio_addr* sc_capwap_setradiomacaddress(uint8_t* buffer, int size, uint8_t* bssid) {
struct sc_capwap_radio_addr* radioaddr;
struct sc_capwap_macaddress_eui48* addr;
TRACEKMOD("### sc_capwap_setwirelessinformation\n");
memset(buffer, 0, size);
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));
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* winfo;
struct sc_capwap_ieee80211_frame_info* frameinfo;
TRACEKMOD("### sc_capwap_setwirelessinformation\n");
memset(buffer, 0, size);
winfo = (struct sc_capwap_wireless_information*)buffer;
winfo->length = sizeof(struct sc_capwap_ieee80211_frame_info);
frameinfo = (struct sc_capwap_ieee80211_frame_info*)(buffer + sizeof(struct sc_capwap_wireless_information));
frameinfo->rssi = rssi;
frameinfo->snr = snr;
frameinfo->rate = cpu_to_be16(rate);
return winfo;
}

128
src/ac/kmod/capwap.h Normal file
View File

@ -0,0 +1,128 @@
#ifndef __KMOD_CAPWAP_HEADER__
#define __KMOD_CAPWAP_HEADER__
#include "capwap_rfc.h"
#include "socket.h"
/* */
#define MAX_MTU 9000
#define DEFAULT_MTU 1450
#define MIN_MTU 500
#define IEEE80211_MTU 7981
/* */
#define CAPWAP_FRAGMENT_QUEUE 16
/* */
#define CAPWAP_FRAGMENT_ENABLE 0x0001
#define CAPWAP_FRAGMENT_LRUQUEUE 0x0002
#define CAPWAP_FRAGMENT_LAST 0x0004
/* */
#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_FRAGMENT 0x1000
struct sc_skb_capwap_cb {
uint16_t flags;
struct capwap_addr_little peeraddr;
/* 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;
uint16_t winfo_rate;
/* Fragment */
uint16_t frag_offset;
uint16_t frag_length;
};
#define CAPWAP_SKB_CB(skb) ((struct sc_skb_capwap_cb*)((skb)->cb))
/* */
struct sc_capwap_fragment {
struct list_head lru_list;
uint8_t flags;
ktime_t tstamp;
uint16_t fragmentid;
struct sk_buff* fragments;
struct sk_buff* lastfragment;
int recvlength;
int totallength;
};
/* */
struct sc_capwap_fragment_queue {
spinlock_t lock;
struct list_head lru_list;
struct sc_capwap_fragment queues[CAPWAP_FRAGMENT_QUEUE];
};
/* */
struct sc_capwap_session {
struct list_head list;
struct sc_capwap_session* __rcu next;
uint16_t mtu;
union capwap_addr peeraddr;
struct sc_capwap_sessionid_element sessionid;
uint16_t fragmentid;
spinlock_t fragmentid_lock;
struct sc_capwap_fragment_queue fragments;
};
/* */
extern union capwap_addr sc_localaddr;
/* Dipendent implementation function */
void sc_capwap_recvpacket(struct sk_buff* skb);
struct sc_capwap_session* sc_capwap_recvunknownkeepalive(const union capwap_addr* sockaddr, struct sc_capwap_sessionid_element* sessionid);
void sc_capwap_parsingdatapacket(struct sc_capwap_session* session, struct sk_buff* skb);
void sc_capwap_parsingmgmtpacket(struct sc_capwap_session* session, struct sk_buff* skb);
/* Indipendent implementation function */
int sc_capwap_bind(union capwap_addr* sockaddr);
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);
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);
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);
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);
/* Private funciotn */
#include "capwap_private.h"
#endif /* __KMOD_CAPWAP_HEADER__ */

View File

@ -0,0 +1,636 @@
#include "config.h"
#include <linux/module.h>
#include <linux/hash.h>
#include <linux/kthread.h>
#include <net/ipv6.h>
#include "socket.h"
#include "capwap.h"
#include "nlsmartcapwap.h"
#include "netlinkapp.h"
/* Sessions */
static struct mutex sc_wtpsession_mutex;
static struct list_head sc_wtpsession_list;
static struct list_head sc_wtpsession_setup_list;
static uint32_t sc_wtpsession_size;
static uint32_t sc_wtpsession_size_shift;
static struct sc_capwap_session** __rcu sc_wtpsession_hash;
/* Threads */
static DEFINE_SPINLOCK(sc_wtpsession_threads_lock);
static uint32_t sc_wtpsession_threads_pos;
static uint32_t sc_wtpsession_threads_count;
static struct sc_capwap_workthread* sc_wtpsession_threads;
/* */
static uint32_t sc_capwap_hash(const union capwap_addr* peeraddr) {
TRACEKMOD("### sc_capwap_hash\n");
return hash_32(((peeraddr->ss.ss_family == AF_INET) ? peeraddr->sin.sin_addr.s_addr : ipv6_addr_hash(&peeraddr->sin6.sin6_addr)), sc_wtpsession_size_shift);
}
/* */
static void sc_capwap_closewtpsession(struct sc_capwap_session* wtpsession) {
TRACEKMOD("### sc_capwap_closewtpsession\n");
lockdep_assert_held(&sc_wtpsession_mutex);
/* Remove session from list reference */
if (wtpsession->peeraddr.ss.ss_family != AF_UNSPEC) {
uint32_t hash = sc_capwap_hash(&wtpsession->peeraddr);
struct sc_capwap_session* search = rcu_dereference_protected(sc_wtpsession_hash[hash], lockdep_is_held(&sc_wtpsession_mutex));
if (search) {
if (search == wtpsession) {
rcu_assign_pointer(sc_wtpsession_hash[hash], wtpsession->next);
} else {
while (rcu_access_pointer(search->next) && (rcu_access_pointer(search->next) != wtpsession)) {
search = rcu_dereference_protected(search->next, lockdep_is_held(&sc_wtpsession_mutex));
}
if (rcu_access_pointer(search->next)) {
rcu_assign_pointer(search->next, wtpsession->next);
}
}
}
}
/* */
list_del_rcu(&wtpsession->list);
synchronize_net();
/* Free memory */
sc_capwap_freesession(wtpsession);
kfree(wtpsession);
TRACEKMOD("*** Free session\n");
}
/* */
static void sc_capwap_closewtpsessions(void) {
struct sc_capwap_session* wtpsession;
struct sc_capwap_session* temp;
TRACEKMOD("### sc_capwap_closewtpsessions\n");
TRACEKMOD("*** Delete all sessions\n");
/* */
mutex_lock(&sc_wtpsession_mutex);
/* */
list_for_each_entry_safe(wtpsession, temp, &sc_wtpsession_setup_list, list) {
#ifdef DEBUGKMOD
do {
char sessionname[33];
sc_capwap_sessionid_printf(&wtpsession->sessionid, sessionname);
TRACEKMOD("*** Delete setup session: %s\n", sessionname);
} while(0);
#endif
sc_capwap_closewtpsession(wtpsession);
}
/* */
list_for_each_entry_safe(wtpsession, temp, &sc_wtpsession_list, list) {
#ifdef DEBUGKMOD
do {
char sessionname[33];
sc_capwap_sessionid_printf(&wtpsession->sessionid, sessionname);
TRACEKMOD("*** Delete running session: %s\n", sessionname);
} while(0);
#endif
sc_capwap_closewtpsession(wtpsession);
}
/* */
synchronize_net();
mutex_unlock(&sc_wtpsession_mutex);
}
/* */
static int sc_capwap_deletesetupsession(const struct sc_capwap_sessionid_element* sessionid) {
int ret = -ENOENT;
struct sc_capwap_session* wtpsession;
TRACEKMOD("### sc_capwap_deletesetupsession\n");
/* */
mutex_lock(&sc_wtpsession_mutex);
list_for_each_entry(wtpsession, &sc_wtpsession_setup_list, list) {
if (!memcmp(&wtpsession->sessionid, sessionid, sizeof(struct sc_capwap_sessionid_element))) {
#ifdef DEBUGKMOD
do {
char sessionname[33];
sc_capwap_sessionid_printf(&wtpsession->sessionid, sessionname);
TRACEKMOD("*** Delete setup session: %s\n", sessionname);
} while(0);
#endif
sc_capwap_closewtpsession(wtpsession);
ret = 0;
break;
}
}
/* */
mutex_unlock(&sc_wtpsession_mutex);
return ret;
}
/* */
static int sc_capwap_deleterunningsession(const union capwap_addr* peeraddr) {
int ret = -ENOENT;
struct sc_capwap_session* wtpsession;
TRACEKMOD("### sc_capwap_deleterunningsession\n");
/* */
mutex_lock(&sc_wtpsession_mutex);
/* Search session with address hash */
wtpsession = sc_capwap_getsession(peeraddr);
if (wtpsession) {
#ifdef DEBUGKMOD
do {
char sessionname[33];
sc_capwap_sessionid_printf(&wtpsession->sessionid, sessionname);
TRACEKMOD("*** Delete running session: %s\n", sessionname);
} while(0);
#endif
sc_capwap_closewtpsession(wtpsession);
ret = 0;
}
/* */
mutex_unlock(&sc_wtpsession_mutex);
return ret;
}
/* */
static int sc_capwap_thread_recvpacket(struct sk_buff* skb) {
int ret = 1;
union capwap_addr peeraddr;
struct sc_capwap_session* session;
struct sc_skb_capwap_cb* cb = CAPWAP_SKB_CB(skb);
TRACEKMOD("### sc_capwap_thread_recvpacket\n");
/* */
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();
session = sc_capwap_getsession(&peeraddr);
if (session) {
if (sc_capwap_forwarddata(session, cb->radioid, cb->binding, skb, 0, NULL, 0, NULL, 0)) {
TRACEKMOD("*** Unable send packet from sc_netlink_send_data function\n");
}
} else {
TRACEKMOD("*** Unable to find session\n");
}
rcu_read_unlock();
} else if (cb->flags & SKB_CAPWAP_FLAG_FROM_DATA_CHANNEL) {
TRACEKMOD("*** Receive SKB_CAPWAP_FLAG_FROM_DATA_CHANNEL\n");
/* Get peer address */
if (sc_socket_getpeeraddr(skb, &peeraddr)) {
TRACEKMOD("*** Unable get address from packet\n");
return -EINVAL;
}
/* Remove UDP header */
if (!skb_pull(skb, sizeof(struct udphdr))) {
TRACEKMOD("*** Invalid packet\n");
return -EOVERFLOW;
}
/* */
rcu_read_lock();
session = sc_capwap_getsession(&peeraddr);
ret = sc_capwap_parsingpacket(session, &peeraddr, skb);
rcu_read_unlock();
}
return ret;
}
/* */
static int sc_capwap_thread(void* data) {
struct sk_buff* skb;
struct sc_capwap_workthread* thread = (struct sc_capwap_workthread*)data;
TRACEKMOD("### sc_capwap_thread\n");
TRACEKMOD("*** Thread start\n");
for (;;) {
wait_event_interruptible(thread->waitevent, (skb_queue_len(&thread->queue) > 0) || kthread_should_stop());
if (kthread_should_stop()) {
break;
}
/* Get packet */
skb = skb_dequeue(&thread->queue);
if (!skb) {
continue;
}
/* */
TRACEKMOD("*** Thread receive packet\n");
if (sc_capwap_thread_recvpacket(skb)) {
TRACEKMOD("*** Free packet\n");
kfree_skb(skb);
}
}
TRACEKMOD("*** Thread end\n");
return 0;
}
/* */
int sc_capwap_sendkeepalive(const union capwap_addr* peeraddr) {
int ret;
int length;
struct sc_capwap_session* session;
uint8_t buffer[CAPWAP_KEEP_ALIVE_MAX_SIZE];
TRACEKMOD("### sc_capwap_sendkeepalive\n");
/* */
rcu_read_lock();
/* Get session */
session = sc_capwap_getsession(peeraddr);
if (!session) {
TRACEKMOD("*** Unknown keep-alive session\n");
ret = -ENOENT;
goto done;
}
#ifdef DEBUGKMOD
do {
char sessionname[33];
sc_capwap_sessionid_printf(&session->sessionid, sessionname);
TRACEKMOD("*** Send keep-alive session: %s\n", sessionname);
} while(0);
#endif
/* Build keepalive */
length = sc_capwap_createkeepalive(&session->sessionid, buffer, CAPWAP_KEEP_ALIVE_MAX_SIZE);
/* Send packet */
ret = sc_socket_send(SOCKET_UDP, buffer, length, &session->peeraddr);
if (ret > 0) {
ret = 0;
}
done:
rcu_read_unlock();
return ret;
}
/* */
int sc_capwap_newsession(const struct sc_capwap_sessionid_element* sessionid, uint16_t mtu) {
struct sc_capwap_session* session;
TRACEKMOD("### sc_capwap_newsession\n");
#ifdef DEBUGKMOD
do {
char sessionname[33];
sc_capwap_sessionid_printf(sessionid, sessionname);
TRACEKMOD("*** Create session: %s\n", sessionname);
} while(0);
#endif
/* */
session = kzalloc(sizeof(struct sc_capwap_session), GFP_KERNEL);
if (!session) {
TRACEKMOD("*** Unable to create session\n");
return -ENOMEM;
}
/* Initialize session */
sc_capwap_initsession(session);
memcpy(&session->sessionid, sessionid, sizeof(struct sc_capwap_sessionid_element));
session->mtu = mtu;
/* Add to setup session list */
mutex_lock(&sc_wtpsession_mutex);
list_add_rcu(&session->list, &sc_wtpsession_setup_list);
mutex_unlock(&sc_wtpsession_mutex);
TRACEKMOD("*** Create session\n");
return 0;
}
/* */
int sc_capwap_init(uint32_t hash, uint32_t threads) {
uint32_t i;
int err = -ENOMEM;
TRACEKMOD("### sc_capwap_init\n");
TRACEKMOD("*** Init capwap module - hash bitfield: %u - threads: %u\n", hash, threads);
/* Init session */
memset(&sc_localaddr, 0, sizeof(union capwap_addr));
mutex_init(&sc_wtpsession_mutex);
INIT_LIST_HEAD(&sc_wtpsession_list);
INIT_LIST_HEAD(&sc_wtpsession_setup_list);
sc_wtpsession_size_shift = hash;
sc_wtpsession_size = 1 << hash;
sc_wtpsession_hash = (struct sc_capwap_session**)kzalloc(sizeof(struct sc_capwap_session*) * sc_wtpsession_size, GFP_KERNEL);
if (!sc_wtpsession_hash) {
goto error;
}
/* Create threads */
sc_wtpsession_threads_pos = 0;
sc_wtpsession_threads_count = threads;
sc_wtpsession_threads = (struct sc_capwap_workthread*)kzalloc(sizeof(struct sc_capwap_workthread) * threads, GFP_KERNEL);
if (!sc_wtpsession_threads) {
goto error2;
}
for (i = 0; i < threads; i++) {
sc_wtpsession_threads[i].thread = kthread_create(sc_capwap_thread, &sc_wtpsession_threads[i], "smartcapwap/%u", i);
if (IS_ERR(sc_wtpsession_threads[i].thread)) {
err = PTR_ERR(sc_wtpsession_threads[i].thread);
sc_wtpsession_threads[i].thread = NULL;
goto error3;
}
}
/* Init sockect */
err = sc_socket_init();
if (err) {
goto error3;
}
/* Start threads */
for (i = 0; i < threads; i++) {
skb_queue_head_init(&sc_wtpsession_threads[i].queue);
init_waitqueue_head(&sc_wtpsession_threads[i].waitevent);
wake_up_process(sc_wtpsession_threads[i].thread);
}
return 0;
error3:
for (i = 0; i < threads; i++) {
if (sc_wtpsession_threads[i].thread) {
kthread_stop(sc_wtpsession_threads[i].thread);
}
}
kfree(sc_wtpsession_threads);
error2:
kfree(sc_wtpsession_hash);
error:
return err;
}
/* */
void sc_capwap_close(void) {
uint32_t i;
TRACEKMOD("### sc_capwap_close\n");
TRACEKMOD("*** Closing capwap module\n");
sc_socket_close();
/* */
for (i = 0; i < sc_wtpsession_threads_count; i++) {
kthread_stop(sc_wtpsession_threads[i].thread);
}
kfree(sc_wtpsession_threads);
/* */
sc_capwap_closewtpsessions();
mutex_destroy(&sc_wtpsession_mutex);
kfree(sc_wtpsession_hash);
TRACEKMOD("*** Close capwap module\n");
}
/* */
int sc_capwap_deletesession(const union capwap_addr* sockaddr, const struct sc_capwap_sessionid_element* sessionid) {
struct sc_capwap_session* wtpsession;
TRACEKMOD("### sc_capwap_deletesession\n");
#ifdef DEBUGKMOD
do {
char sessionname[33];
sc_capwap_sessionid_printf(sessionid, sessionname);
TRACEKMOD("*** Delete session: %s\n", sessionname);
} while(0);
#endif
/* Searching item with read lock */
rcu_read_lock();
/* Search into running session list */
if (sockaddr && sc_capwap_getsession(sockaddr)) {
rcu_read_unlock();
/* Remove session with address */
return sc_capwap_deleterunningsession(sockaddr);
} else {
list_for_each_entry_rcu(wtpsession, &sc_wtpsession_list, list) {
#ifdef DEBUGKMOD
do {
char sessionname[33];
sc_capwap_sessionid_printf(&wtpsession->sessionid, sessionname);
TRACEKMOD("*** Check running session for delete: %s\n", sessionname);
} while(0);
#endif
if (!memcmp(&wtpsession->sessionid, sessionid, sizeof(struct sc_capwap_sessionid_element))) {
union capwap_addr peeraddr;
/* Get peer address */
memcpy(&peeraddr, &wtpsession->peeraddr, sizeof(union capwap_addr));
rcu_read_unlock();
/* Remove session with address */
return sc_capwap_deleterunningsession(&peeraddr);
}
}
}
/* Search into setup session list */
list_for_each_entry_rcu(wtpsession, &sc_wtpsession_setup_list, list) {
#ifdef DEBUGKMOD
do {
char sessionname[33];
sc_capwap_sessionid_printf(&wtpsession->sessionid, sessionname);
TRACEKMOD("*** Check setup session for delete: %s\n", sessionname);
} while(0);
#endif
if (!memcmp(&wtpsession->sessionid, sessionid, sizeof(struct sc_capwap_sessionid_element))) {
rcu_read_unlock();
/* Remove session with sessionid */
return sc_capwap_deletesetupsession(sessionid);
}
}
rcu_read_unlock();
TRACEKMOD("*** Session not found\n");
return -ENOENT;
}
/* */
struct sc_capwap_session* sc_capwap_getsession(const union capwap_addr* sockaddr) {
struct sc_capwap_session* session;
TRACEKMOD("### sc_capwap_getsession\n");
/* */
session = rcu_dereference_check(sc_wtpsession_hash[sc_capwap_hash(sockaddr)], lockdep_is_held(&sc_wtpsession_mutex));
while (session) {
if (!sc_addr_compare(sockaddr, &session->peeraddr)) {
break;
}
/* */
session = rcu_dereference_check(session->next, lockdep_is_held(&sc_wtpsession_mutex));
}
return session;
}
/* */
void sc_capwap_recvpacket(struct sk_buff* skb) {
uint32_t pos;
unsigned long flags;
TRACEKMOD("### sc_capwap_recvpacket\n");
spin_lock_irqsave(&sc_wtpsession_threads_lock, flags);
sc_wtpsession_threads_pos = ((sc_wtpsession_threads_pos + 1) % sc_wtpsession_threads_count);
pos = sc_wtpsession_threads_pos;
spin_unlock_irqrestore(&sc_wtpsession_threads_lock, flags);
TRACEKMOD("*** Add packet to thread: %u\n", pos);
/* Queue packet */
skb_queue_tail(&sc_wtpsession_threads[pos].queue, skb);
wake_up_interruptible(&sc_wtpsession_threads[pos].waitevent);
}
/* */
struct sc_capwap_session* sc_capwap_recvunknownkeepalive(const union capwap_addr* sockaddr, struct sc_capwap_sessionid_element* sessionid) {
uint32_t hash;
struct sc_capwap_session* search;
struct sc_capwap_session* wtpsession = NULL;
TRACEKMOD("### sc_capwap_recvunknownkeepalive\n");
#ifdef DEBUGKMOD
do {
char sessionname[33];
sc_capwap_sessionid_printf(sessionid, sessionname);
TRACEKMOD("*** Receive unknown keep-alive: %s\n", sessionname);
} while(0);
#endif
/* Change read lock to update lock */
rcu_read_unlock();
mutex_lock(&sc_wtpsession_mutex);
/* Search and remove from setup session */
list_for_each_entry(search, &sc_wtpsession_setup_list, list) {
#ifdef DEBUGKMOD
do {
char sessionname[33];
sc_capwap_sessionid_printf(&search->sessionid, sessionname);
TRACEKMOD("*** Check setup session: %s\n", sessionname);
} while(0);
#endif
if (!memcmp(&search->sessionid, sessionid, sizeof(struct sc_capwap_sessionid_element))) {
wtpsession = search;
break;
}
}
/* */
if (wtpsession) {
TRACEKMOD("*** Setup session found\n");
list_del_rcu(&wtpsession->list);
synchronize_net();
} else {
TRACEKMOD("*** Setup session not found\n");
goto done;
}
/* */
hash = sc_capwap_hash(sockaddr);
memcpy(&wtpsession->peeraddr, sockaddr, sizeof(union capwap_addr));
/* Add to list */
list_add_rcu(&wtpsession->list, &sc_wtpsession_list);
wtpsession->next = sc_wtpsession_hash[hash];
rcu_assign_pointer(sc_wtpsession_hash[hash], wtpsession);
done:
rcu_read_lock();
mutex_unlock(&sc_wtpsession_mutex);
/* */
return wtpsession;
}
/* */
void sc_capwap_parsingdatapacket(struct sc_capwap_session* session, struct sk_buff* skb) {
uint8_t* pos;
struct sc_capwap_header* header = (struct sc_capwap_header*)skb->data;
struct sc_capwap_radio_addr* radioaddr = NULL;
int radioaddrsize = 0;
struct sc_capwap_wireless_information* winfo = NULL;
int winfosize = 0;
TRACEKMOD("### sc_capwap_parsingdatapacket\n");
/* Retrieve optional attribute */
pos = skb->data + sizeof(struct sc_capwap_header);
if (IS_FLAG_M_HEADER(header)) {
radioaddr = (struct sc_capwap_radio_addr*)pos;
radioaddrsize = (sizeof(struct sc_capwap_radio_addr) + radioaddr->length + 3) & ~3;
pos += radioaddrsize;
}
if (IS_FLAG_W_HEADER(header)) {
winfo = (struct sc_capwap_wireless_information*)pos;
radioaddrsize = (sizeof(struct sc_capwap_wireless_information) + winfo->length + 3) & ~3;
pos += winfosize;
}
/* TODO */
}
/* */
void sc_capwap_parsingmgmtpacket(struct sc_capwap_session* session, struct sk_buff* skb) {
TRACEKMOD("### sc_capwap_parsingmgmtpacket\n");
/* Send packet with capwap header into userspace */
sc_netlink_notify_recv_data(&session->sessionid, skb->data, skb->len);
}

View File

@ -0,0 +1,26 @@
#ifndef __KMOD_CAPWAP_PRIVATE_HEADER__
#define __KMOD_CAPWAP_PRIVATE_HEADER__
/* */
struct sc_capwap_workthread {
struct task_struct* thread;
struct sk_buff_head queue;
wait_queue_head_t waitevent;
};
/* */
int sc_capwap_init(uint32_t hash, uint32_t threads);
void sc_capwap_close(void);
/* */
struct sc_capwap_session* sc_capwap_getsession(const union capwap_addr* sockaddr);
/* */
int sc_capwap_sendkeepalive(const union capwap_addr* peeraddr);
/* */
int sc_capwap_newsession(const struct sc_capwap_sessionid_element* sessionid, uint16_t mtu);
int sc_capwap_deletesession(const union capwap_addr* sockaddr, const struct sc_capwap_sessionid_element* sessionid);
#endif /* __KMOD_CAPWAP_PRIVATE_HEADER__ */

172
src/ac/kmod/capwap_rfc.h Normal file
View File

@ -0,0 +1,172 @@
#ifndef __KMOD_CAPWAP_RFC_HEADER__
#define __KMOD_CAPWAP_RFC_HEADER__
#include <linux/types.h>
#include <asm/byteorder.h>
/* */
#define CAPWAP_RADIOID_MAX_COUNT 31
#define IS_VALID_RADIOID(x) ((x >= 1) && (x <= CAPWAP_RADIOID_MAX_COUNT))
#define CAPWAP_WLANID_MAX_COUNT 16
#define IS_VALID_WLANID(x) ((x >= 1) && (x <= CAPWAP_WLANID_MAX_COUNT))
/* */
#define CAPWAP_WIRELESS_BINDING_NONE 0
#define CAPWAP_WIRELESS_BINDING_IEEE80211 1
/* */
#define CAPWAP_ELEMENT_SESSIONID 35
/* */
#define CAPWAP_KEEPALIVE_SIZE (sizeof(struct sc_capwap_dtls_header) + \
sizeof(struct sc_capwap_header) + \
sizeof(struct sc_capwap_data_message) + \
sizeof(struct sc_capwap_message_element) + \
sizeof(struct sc_capwap_sessionid_element))
/* Preamble */
struct sc_capwap_preamble {
#if defined(__BIG_ENDIAN_BITFIELD)
uint8_t version: 4,
type: 4;
#elif defined(__LITTLE_ENDIAN_BITFIELD)
uint8_t type: 4,
version: 4;
#endif
} __packed;
/* DTLS header */
struct sc_capwap_dtls_header {
struct sc_capwap_preamble preamble;
uint8_t reserved[3];
} __packed;
/* Plain header */
struct sc_capwap_header {
struct sc_capwap_preamble preamble;
#if defined(__BIG_ENDIAN_BITFIELD)
uint16_t hlen: 5,
rid: 5,
wbid: 5,
flag_t: 1;
uint8_t flag_f: 1,
flag_l: 1,
flag_w: 1,
flag_m: 1,
flag_k: 1,
flag_res: 3;
#elif defined(__LITTLE_ENDIAN_BITFIELD)
uint16_t _rid_hi: 3,
hlen: 5,
flag_t: 1,
wbid: 5,
_rid_lo: 2;
uint8_t flag_res: 3,
flag_k: 1,
flag_m: 1,
flag_w: 1,
flag_l: 1,
flag_f: 1;
#endif
__be16 frag_id;
__be16 frag_off;
} __packed;
/* Mac Address */
#define CAPWAP_RADIO_EUI48_LENGTH_PADDED 8
#define CAPWAP_RADIO_EUI64_LENGTH_PADDED 12
#define CAPWAP_RADIO_MAX_LENGTH_PADDED 12
struct sc_capwap_radio_addr {
uint8_t length;
} __packed;
/* Wireless Information */
#define CAPWAP_WINFO_FRAMEINFO_LENGTH_PADDED 8
#define CAPWAP_WINFO_DESTWLAN_LENGTH_PADDED 8
#define CAPWAP_WINFO_MAX_LENGTH_PADDED 8
struct sc_capwap_wireless_information {
uint8_t length;
} __packed;
/* IEEE802.11 Wireless Information */
struct sc_capwap_ieee80211_frame_info {
uint8_t rssi;
uint8_t snr;
__be16 rate;
} __packed;
/* */
#define CAPWAP_HEADER_MAX_LENGTH (sizeof(struct sc_capwap_header) + CAPWAP_RADIO_MAX_LENGTH_PADDED + CAPWAP_WINFO_MAX_LENGTH_PADDED)
/* Data channel message */
struct sc_capwap_data_message {
__be16 length;
} __packed;
/* Message element */
struct sc_capwap_message_element {
__be16 type;
__be16 length;
} __packed;
/* Session id message element */
struct sc_capwap_sessionid_element {
uint8_t id[16];
} __packed;
/* */
#define MACADDRESS_EUI48_LENGTH 6
struct sc_capwap_macaddress_eui48 {
uint8_t addr[MACADDRESS_EUI48_LENGTH];
} __packed;
/* */
#define MACADDRESS_EUI64_LENGTH 8
struct sc_capwap_macaddress_eui64 {
uint8_t addr[MACADDRESS_EUI64_LENGTH];
} __packed;
/* Capwap preamble */
#define CAPWAP_PROTOCOL_VERSION 0
#define CAPWAP_PREAMBLE_HEADER 0
#define CAPWAP_PREAMBLE_DTLS_HEADER 1
#define CAPWAP_WIRELESS_BINDING_NONE 0
#define CAPWAP_WIRELESS_BINDING_IEEE80211 1
/* */
#define CAPWAP_KEEP_ALIVE_MAX_SIZE (sizeof(struct sc_capwap_header) + sizeof(struct sc_capwap_data_message) + sizeof(struct sc_capwap_message_element) + sizeof(struct sc_capwap_sessionid_element))
/* */
#define GET_VERSION_HEADER(x) ((x)->preamble.version)
#define SET_VERSION_HEADER(x, y) ((x)->preamble.version = (uint8_t)(y))
#define GET_TYPE_HEADER(x) ((x)->preamble.type)
#define SET_TYPE_HEADER(x, y) ((x)->preamble.type = (uint8_t)(y))
#define GET_HLEN_HEADER(x) ((x)->hlen)
#define SET_HLEN_HEADER(x, y) ((x)->hlen = (uint16_t)(y))
#if defined(__BIG_ENDIAN_BITFIELD)
#define GET_RID_HEADER(x) ((uint8_t)((x)->rid))
#define SET_RID_HEADER(x, y) ((x)->rid = (uint16_t)(y))
#elif defined(__LITTLE_ENDIAN_BITFIELD)
#define GET_RID_HEADER(x) ((uint8_t)((uint16_t)((x)->_rid_hi << 2 | (x)->_rid_lo)))
#define SET_RID_HEADER(x, y) ({ (x)->_rid_hi = (uint16_t)((y) >> 2); (x)->_rid_lo = (uint16_t)((y) & 0x0003); })
#endif
#define GET_WBID_HEADER(x) ((uint8_t)((x)->wbid))
#define SET_WBID_HEADER(x, y) ((x)->wbid = (uint16_t)(y))
#define IS_FLAG_T_HEADER(x) ((x)->flag_t)
#define SET_FLAG_T_HEADER(x, y) ((x)->flag_t = ((y) ? 1 : 0))
#define IS_FLAG_F_HEADER(x) ((x)->flag_f)
#define SET_FLAG_F_HEADER(x, y) ((x)->flag_f = ((y) ? 1 : 0))
#define IS_FLAG_L_HEADER(x) ((x)->flag_l)
#define SET_FLAG_L_HEADER(x, y) ((x)->flag_l = ((y) ? 1 : 0))
#define IS_FLAG_W_HEADER(x) ((x)->flag_w)
#define SET_FLAG_W_HEADER(x, y) ((x)->flag_w = ((y) ? 1 : 0))
#define IS_FLAG_M_HEADER(x) ((x)->flag_m)
#define SET_FLAG_M_HEADER(x, y) ((x)->flag_m = ((y) ? 1 : 0))
#define IS_FLAG_K_HEADER(x) ((x)->flag_k)
#define SET_FLAG_K_HEADER(x, y) ((x)->flag_k = ((y) ? 1 : 0))
#endif /* __KMOD_CAPWAP_RFC_HEADER__ */

13
src/ac/kmod/config.h Normal file
View File

@ -0,0 +1,13 @@
#ifndef __KMOD_CONFIG_HEADER__
#define __KMOD_CONFIG_HEADER__
#define DEBUGKMOD 1
#ifdef DEBUGKMOD
#define TRACEKMOD(s, args...) printk(s, ##args)
#else
#define TRACEKMOD(s, args...)
#endif
#endif /* __KMOD_CONFIG_HEADER__ */

View File

@ -1,21 +1,29 @@
#include "config.h"
#include <linux/module.h>
#include <linux/kernel.h>
#include "netlinkapp.h"
/* */
static int __init smartcapwap_ac_init(void) {
int result = 0;
int ret;
/* */
result = nlsmartcapwap_ac_init();
TRACEKMOD("### smartcapwap_ac_init\n");
return result;
/* Initialize netlink */
ret = sc_netlink_init();
if (ret) {
return ret;
}
return ret;
}
module_init(smartcapwap_ac_init);
/* */
static void __exit smartcapwap_ac_exit(void) {
nlsmartcapwap_ac_exit();
TRACEKMOD("### smartcapwap_ac_exit\n");
sc_netlink_exit();
}
module_exit(smartcapwap_ac_exit);

View File

@ -1,5 +1,8 @@
#include "config.h"
#include <linux/module.h>
#include <linux/version.h>
#include <linux/version.h>
#include <linux/socket.h>
#include <linux/rtnetlink.h>
#include <linux/netlink.h>
#include <net/genetlink.h>
@ -9,90 +12,251 @@
#include <linux/ieee80211.h>
#include "nlsmartcapwap.h"
#include "netlinkapp.h"
#include "capwap.h"
/* */
struct nlsmartcapwap_ac_device {
struct list_head list;
u32 usermodeid;
};
static u32 sc_netlink_usermodeid;
/* */
static u32 nlsmartcapwap_ac_usermodeid = 0;
static LIST_HEAD(nlsmartcapwap_ac_dev_list);
/* Netlink Family */
static struct genl_family nlsmartcapwap_ac_family = {
.id = GENL_ID_GENERATE,
.name = SMARTCAPWAP_AC_GENL_NAME,
.hdrsize = 0,
.version = 1,
.maxattr = NLSMARTCAPWAP_AC_ATTR_MAX,
.netnsok = true,
};
/* */
static void nlsmartcapwap_ac_free_device(struct nlsmartcapwap_ac_device* nldev) {
/* Free memory */
kfree(nldev);
}
/* */
static void nlsmartcapwap_ac_close(void) {
struct nlsmartcapwap_ac_device* nldev;
struct nlsmartcapwap_ac_device* tmp;
list_for_each_entry_safe(nldev, tmp, &nlsmartcapwap_ac_dev_list, list) {
list_del(&nldev->list);
/* Free device */
nlsmartcapwap_ac_free_device(nldev);
}
}
/* */
static int nlsmartcapwap_ac_link(struct sk_buff* skb, struct genl_info* info) {
int ret = 0;
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0)
u32 portid = info->snd_pid;
#else
u32 portid = info->snd_portid;
#endif
static int sc_netlink_pre_doit(struct genl_ops* ops, struct sk_buff* skb, struct genl_info* info) {
TRACEKMOD("### sc_netlink_pre_doit\n");
rtnl_lock();
return 0;
}
if (!nlsmartcapwap_ac_usermodeid) {
nlsmartcapwap_ac_usermodeid = portid;
} else if (nlsmartcapwap_ac_usermodeid == portid) {
ret = -EALREADY;
} else {
ret = -EBUSY;
}
/* */
static void sc_netlink_post_doit(struct genl_ops* ops, struct sk_buff* skb, struct genl_info* info) {
TRACEKMOD("### sc_netlink_post_doit\n");
rtnl_unlock();
}
/* Netlink Family */
static struct genl_family sc_netlink_family = {
.id = GENL_ID_GENERATE,
.name = NLSMARTCAPWAP_GENL_NAME,
.hdrsize = 0,
.version = 1,
.maxattr = NLSMARTCAPWAP_ATTR_MAX,
.netnsok = true,
.pre_doit = sc_netlink_pre_doit,
.post_doit = sc_netlink_post_doit,
};
/* */
static int sc_netlink_bind(struct sk_buff* skb, struct genl_info* info) {
union capwap_addr sockaddr;
TRACEKMOD("### sc_netlink_bind\n");
/* Check Link */
if (sc_netlink_usermodeid != info->snd_portid) {
return -ENOLINK;
}
/* Get bind address */
if (!info->attrs[NLSMARTCAPWAP_ATTR_ADDRESS] || (nla_len(info->attrs[NLSMARTCAPWAP_ATTR_ADDRESS]) != sizeof(struct sockaddr_storage))) {
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)) {
return -EINVAL;
}
/* Bind socket */
return sc_capwap_bind(&sockaddr);
}
/* */
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) {
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)) {
return -EINVAL;
}
/* Send keep-alive packet */
ret = sc_capwap_sendkeepalive(&sockaddr);
if (ret < 0) {
return ret;
}
return 0;
}
/* */
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) {
return -ENOLINK;
} else 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)) {
return -EINVAL;
}
/* Create socket buffer */
length = nla_len(info->attrs[NLSMARTCAPWAP_ATTR_DATA_FRAME]);
skbdata = alloc_skb(length + CAPWAP_HEADER_MAX_LENGTH, GFP_KERNEL);
if (!skbdata) {
return -ENOMEM;
}
/* Reserve space for Capwap Header */
skb_reserve(skbdata, CAPWAP_HEADER_MAX_LENGTH);
/* Copy data into socket buffer */
memcpy(skb_put(skbdata, length), nla_data(info->attrs[NLSMARTCAPWAP_ATTR_DATA_FRAME]), length);
/* */
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->radioid = nla_get_u8(info->attrs[NLSMARTCAPWAP_ATTR_RADIOID]);
cb->binding = nla_get_u8(info->attrs[NLSMARTCAPWAP_ATTR_BINDING]);
/* */
sc_capwap_recvpacket(skbdata);
return 0;
}
/* */
static int sc_netlink_new_session(struct sk_buff* skb, struct genl_info* info) {
uint16_t mtu = DEFAULT_MTU;
TRACEKMOD("### sc_netlink_new_session\n");
/* Check Link */
if (sc_netlink_usermodeid != info->snd_portid) {
return -ENOLINK;
}
/* Check Session ID */
if (!info->attrs[NLSMARTCAPWAP_ATTR_SESSION_ID] || (nla_len(info->attrs[NLSMARTCAPWAP_ATTR_SESSION_ID]) != sizeof(struct sc_capwap_sessionid_element))) {
return -EINVAL;
}
/* Get MTU */
if (info->attrs[NLSMARTCAPWAP_ATTR_MTU]) {
mtu = nla_get_u16(info->attrs[NLSMARTCAPWAP_ATTR_MTU]);
if ((mtu < MIN_MTU) || (mtu > MAX_MTU)) {
return -EINVAL;
}
}
/* New session */
return sc_capwap_newsession((struct sc_capwap_sessionid_element*)nla_data(info->attrs[NLSMARTCAPWAP_ATTR_SESSION_ID]), mtu);
}
/* */
static int sc_netlink_delete_session(struct sk_buff* skb, struct genl_info* info) {
union capwap_addr sockaddr;
TRACEKMOD("### sc_netlink_delete_session\n");
/* Check Link */
if (sc_netlink_usermodeid != info->snd_portid) {
return -ENOLINK;
}
/* Check Session ID */
if (!info->attrs[NLSMARTCAPWAP_ATTR_SESSION_ID] || (nla_len(info->attrs[NLSMARTCAPWAP_ATTR_SESSION_ID]) != sizeof(struct sc_capwap_sessionid_element))) {
return -EINVAL;
}
/* Check Address */
if (info->attrs[NLSMARTCAPWAP_ATTR_ADDRESS] && (nla_len(info->attrs[NLSMARTCAPWAP_ATTR_ADDRESS]) == sizeof(struct sockaddr_storage))) {
memcpy(&sockaddr.ss, nla_data(info->attrs[NLSMARTCAPWAP_ATTR_ADDRESS]), sizeof(struct sockaddr_storage));
} else {
sockaddr.ss.ss_family = AF_UNSPEC;
}
/* Delete session */
return sc_capwap_deletesession(((sockaddr.ss.ss_family == AF_UNSPEC) ? NULL : &sockaddr), (struct sc_capwap_sessionid_element*)nla_data(info->attrs[NLSMARTCAPWAP_ATTR_SESSION_ID]));
}
/* */
static int sc_netlink_link(struct sk_buff* skb, struct genl_info* info) {
int ret = 0;
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) {
uint32_t hash = nla_get_u32(info->attrs[NLSMARTCAPWAP_ATTR_HASH_SESSION_BITFIELD]);
uint32_t threads = nla_get_u32(info->attrs[NLSMARTCAPWAP_ATTR_SESSION_THREADS_COUNT]);
if (!hash || !threads) {
TRACEKMOD("*** Invalid link argument: %u %u\n", hash, threads);
return -EINVAL;
}
/* Initialize library */
ret = sc_capwap_init(hash, threads);
if (!ret) {
sc_netlink_usermodeid = info->snd_portid;
/* Deny unload module */
try_module_get(THIS_MODULE);
}
} else if (sc_netlink_usermodeid == info->snd_portid) {
TRACEKMOD("*** Already link\n");
ret = -EALREADY;
} else {
TRACEKMOD("*** Busy kernel link\n");
ret = -EBUSY;
}
return ret;
}
/* */
static int nlsmartcapwap_ac_netlink_notify(struct notifier_block* nb, unsigned long state, void* _notify) {
static int sc_netlink_notify(struct notifier_block* nb, unsigned long state, void* _notify) {
struct netlink_notify* notify = (struct netlink_notify*)_notify;
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0)
u32 portid = notify->pid;
#else
u32 portid = notify->portid;
#endif
/* */
if (state == NETLINK_URELEASE) {
rtnl_lock();
if (nlsmartcapwap_ac_usermodeid == portid) {
nlsmartcapwap_ac_usermodeid = 0;
if (sc_netlink_usermodeid == notify->portid) {
/* Close capwap engine */
sc_capwap_close();
/* Close all devices */
nlsmartcapwap_ac_close();
/* Allow unload module */
module_put(THIS_MODULE);
sc_netlink_usermodeid = 0;
}
rtnl_unlock();
@ -102,57 +266,172 @@ static int nlsmartcapwap_ac_netlink_notify(struct notifier_block* nb, unsigned l
}
/* */
static const struct nla_policy nlsmartcapwap_ac_policy[NLSMARTCAPWAP_AC_ATTR_MAX + 1] = {
[NLSMARTCAPWAP_AC_ATTR_FLAGS] = { .type = NLA_U32 },
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_BINDING] = { .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 },
};
/* Netlink Ops */
static struct genl_ops nlsmartcapwap_ac_ops[] = {
static struct genl_ops sc_netlink_ops[] = {
{
.cmd = NLSMARTCAPWAP_AC_CMD_LINK,
.doit = nlsmartcapwap_ac_link,
.policy = nlsmartcapwap_ac_policy,
.cmd = NLSMARTCAPWAP_CMD_LINK,
.doit = sc_netlink_link,
.policy = sc_netlink_policy,
.flags = GENL_ADMIN_PERM,
},
{
.cmd = NLSMARTCAPWAP_CMD_BIND,
.doit = sc_netlink_bind,
.policy = sc_netlink_policy,
.flags = GENL_ADMIN_PERM,
},
{
.cmd = NLSMARTCAPWAP_CMD_SEND_KEEPALIVE,
.doit = sc_netlink_send_keepalive,
.policy = sc_netlink_policy,
.flags = GENL_ADMIN_PERM,
},
{
.cmd = NLSMARTCAPWAP_CMD_SEND_DATA,
.doit = sc_netlink_send_data,
.policy = sc_netlink_policy,
.flags = GENL_ADMIN_PERM,
},
{
.cmd = NLSMARTCAPWAP_CMD_NEW_SESSION,
.doit = sc_netlink_new_session,
.policy = sc_netlink_policy,
.flags = GENL_ADMIN_PERM,
},
{
.cmd = NLSMARTCAPWAP_CMD_DELETE_SESSION,
.doit = sc_netlink_delete_session,
.policy = sc_netlink_policy,
.flags = GENL_ADMIN_PERM,
},
};
/* Netlink notify */
static struct notifier_block nlsmartcapwap_ac_netlink_notifier = {
.notifier_call = nlsmartcapwap_ac_netlink_notify,
static struct notifier_block sc_netlink_notifier = {
.notifier_call = sc_netlink_notify,
};
/* */
int nlsmartcapwap_ac_init(void) {
int sc_netlink_notify_recv_keepalive(const union capwap_addr* sockaddr, struct sc_capwap_sessionid_element* sessionid) {
void* msg;
struct sk_buff* sk_msg;
TRACEKMOD("### sc_netlink_notify_recv_keepalive\n");
/* Alloc message */
sk_msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
if (!sk_msg) {
return -ENOMEM;
}
/* Set command */
msg = genlmsg_put(sk_msg, 0, 0, &sc_netlink_family, 0, NLSMARTCAPWAP_CMD_RECV_KEEPALIVE);
if (!msg) {
goto error;
}
/* */
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)) {
goto error2;
}
/* Send message */
genlmsg_end(sk_msg, msg);
return genlmsg_unicast(&init_net, sk_msg, sc_netlink_usermodeid);
error2:
genlmsg_cancel(sk_msg, msg);
error:
nlmsg_free(sk_msg);
return -ENOMEM;
}
/* */
int sc_netlink_notify_recv_data(struct sc_capwap_sessionid_element* sessionid, uint8_t* packet, int length) {
void* msg;
struct sk_buff* sk_msg;
TRACEKMOD("### sc_netlink_notify_recv_data\n");
/* Alloc message */
sk_msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
if (!sk_msg) {
return -ENOMEM;
}
/* Set command */
msg = genlmsg_put(sk_msg, 0, 0, &sc_netlink_family, 0, NLSMARTCAPWAP_CMD_RECV_DATA);
if (!msg) {
goto error;
}
/* */
if (nla_put(sk_msg, NLSMARTCAPWAP_ATTR_SESSION_ID, sizeof(struct sc_capwap_sessionid_element), sessionid) ||
nla_put(sk_msg, NLSMARTCAPWAP_ATTR_DATA_FRAME, length, packet)) {
goto error2;
}
/* Send message */
genlmsg_end(sk_msg, msg);
return genlmsg_unicast(&init_net, sk_msg, sc_netlink_usermodeid);
error2:
genlmsg_cancel(sk_msg, msg);
error:
nlmsg_free(sk_msg);
return -ENOMEM;
}
/* */
int sc_netlink_init(void) {
int ret;
TRACEKMOD("### sc_netlink_init\n");
/* */
sc_netlink_usermodeid = 0;
/* Register netlink family */
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,12,0)
ret = genl_register_family_with_ops(&nlsmartcapwap_ac_family, nlsmartcapwap_ac_ops, sizeof(nlsmartcapwap_ac_ops) / sizeof(nlsmartcapwap_ac_ops[0]));
ret = genl_register_family_with_ops(&sc_netlink_family, sc_netlink_ops, sizeof(sc_netlink_ops) / sizeof(sc_netlink_ops[0]));
#else
ret = genl_register_family_with_ops(&nlsmartcapwap_ac_family, nlsmartcapwap_ac_ops);
ret = genl_register_family_with_ops(&sc_netlink_family, sc_netlink_ops);
#endif
if (ret) {
return ret;
goto error;
}
/* Register netlink notifier */
ret = netlink_register_notifier(&nlsmartcapwap_ac_netlink_notifier);
ret = netlink_register_notifier(&sc_netlink_notifier);
if (ret) {
genl_unregister_family(&nlsmartcapwap_ac_family);
return ret;
goto error2;
}
return 0;
error2:
genl_unregister_family(&sc_netlink_family);
error:
return ret;
}
/* */
void nlsmartcapwap_ac_exit(void) {
/* */
rtnl_lock();
nlsmartcapwap_ac_close();
rtnl_unlock();
void sc_netlink_exit(void) {
TRACEKMOD("### sc_netlink_exit\n");
/* */
netlink_unregister_notifier(&nlsmartcapwap_ac_netlink_notifier);
genl_unregister_family(&nlsmartcapwap_ac_family);
netlink_unregister_notifier(&sc_netlink_notifier);
genl_unregister_family(&sc_netlink_family);
}

View File

@ -1,8 +1,15 @@
#ifndef __KMOD_AC_NETLINKAPP_HEADER__
#define __KMOD_AC_NETLINKAPP_HEADER__
#include "capwap_rfc.h"
#include "socket.h"
/* */
int nlsmartcapwap_ac_init(void);
void nlsmartcapwap_ac_exit(void);
int sc_netlink_init(void);
void sc_netlink_exit(void);
/* */
int sc_netlink_notify_recv_keepalive(const union capwap_addr* sockaddr, struct sc_capwap_sessionid_element* sessionid);
int sc_netlink_notify_recv_data(struct sc_capwap_sessionid_element* sessionid, uint8_t* packet, int length);
#endif /* __KMOD_AC_NETLINKAPP_HEADER__ */

View File

@ -2,28 +2,55 @@
#define __AC_NLSMARTCAPWAP_HEADER__
/* */
#define SMARTCAPWAP_AC_GENL_NAME "smartcapwap_ac"
#define NLSMARTCAPWAP_GENL_NAME "smartcapwap_ac"
/* */
enum nlsmartcapwap_ac_attrs {
NLSMARTCAPWAP_AC_ATTR_UNSPEC,
#define NLSMARTCAPWAP_FLAGS_TUNNEL_8023 0x00000001
NLSMARTCAPWAP_AC_ATTR_FLAGS,
/* */
enum sc_netlink_attrs {
NLSMARTCAPWAP_ATTR_UNSPEC,
NLSMARTCAPWAP_ATTR_FLAGS,
NLSMARTCAPWAP_ATTR_SESSION_ID,
NLSMARTCAPWAP_ATTR_RADIOID,
NLSMARTCAPWAP_ATTR_BINDING,
NLSMARTCAPWAP_ATTR_ADDRESS,
NLSMARTCAPWAP_ATTR_MTU,
NLSMARTCAPWAP_ATTR_DATA_FRAME,
NLSMARTCAPWAP_ATTR_HASH_SESSION_BITFIELD,
NLSMARTCAPWAP_ATTR_SESSION_THREADS_COUNT,
/* Last attribute */
__NLSMARTCAPWAP_AC_ATTR_AFTER_LAST,
NLSMARTCAPWAP_AC_ATTR_MAX = __NLSMARTCAPWAP_AC_ATTR_AFTER_LAST - 1
__NLSMARTCAPWAP_ATTR_AFTER_LAST,
NLSMARTCAPWAP_ATTR_MAX = __NLSMARTCAPWAP_ATTR_AFTER_LAST - 1
};
/* */
enum nlsmartcapwap_ac_commands {
NLSMARTCAPWAP_AC_CMD_UNSPEC,
enum sc_netlink_commands {
NLSMARTCAPWAP_CMD_UNSPEC,
NLSMARTCAPWAP_AC_CMD_LINK,
NLSMARTCAPWAP_CMD_LINK,
NLSMARTCAPWAP_CMD_BIND,
NLSMARTCAPWAP_CMD_SEND_KEEPALIVE,
NLSMARTCAPWAP_CMD_RECV_KEEPALIVE,
NLSMARTCAPWAP_CMD_NEW_SESSION,
NLSMARTCAPWAP_CMD_DELETE_SESSION,
NLSMARTCAPWAP_CMD_SEND_DATA,
NLSMARTCAPWAP_CMD_RECV_DATA,
/* Last command */
__NLSMARTCAPWAP_AC_CMD_AFTER_LAST,
NLSMARTCAPWAP_AC_CMD_MAX = __NLSMARTCAPWAP_AC_CMD_AFTER_LAST - 1
__NLSMARTCAPWAP_CMD_AFTER_LAST,
NLSMARTCAPWAP_CMD_MAX = __NLSMARTCAPWAP_CMD_AFTER_LAST - 1
};
#endif /* __AC_NLSMARTCAPWAP_HEADER__ */

257
src/ac/kmod/socket.c Normal file
View File

@ -0,0 +1,257 @@
#include "config.h"
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/socket.h>
#include <linux/kthread.h>
#include <linux/net.h>
#include <linux/if_ether.h>
#include <linux/udp.h>
#include <net/ipv6.h>
#include <net/sock.h>
#include <net/udp.h>
#include "socket.h"
#include "capwap.h"
/* Socket */
#define SOCKET_COUNT 2
static struct socket* sc_sockets[SOCKET_COUNT];
/* */
int sc_socket_recvpacket(struct sock* sk, struct sk_buff* skb) {
struct sc_skb_capwap_cb* cb = CAPWAP_SKB_CB(skb);
TRACEKMOD("### sc_socket_recvpacket\n");
/* */
cb->flags = SKB_CAPWAP_FLAG_FROM_DATA_CHANNEL;
/* */
sc_capwap_recvpacket(skb);
return 0;
}
/* */
static int sc_socket_create(int type, union capwap_addr* sockaddr, uint16_t protocol) {
int ret;
TRACEKMOD("### sc_socket_create\n");
/* Create socket */
ret = sock_create_kern(sockaddr->ss.ss_family, SOCK_DGRAM, protocol, &sc_sockets[type]);
if (ret) {
goto failure;
}
/* Bind to interface */
ret = kernel_bind(sc_sockets[type], &sockaddr->sa, sizeof(union capwap_addr));
if (ret) {
goto failure2;
}
/* Set callback */
udp_sk(sc_sockets[type]->sk)->encap_type = 1;
udp_sk(sc_sockets[type]->sk)->encap_rcv = sc_socket_recvpacket;
/* */
if (!((sockaddr->ss.ss_family == AF_INET) ? sockaddr->sin.sin_port : sockaddr->sin6.sin6_port)) {
union capwap_addr localaddr;
int localaddrsize = sizeof(union capwap_addr);
/* Retrieve port */
ret = kernel_getsockname(sc_sockets[type], &localaddr.sa, &localaddrsize);
if (ret) {
goto failure2;
}
/* */
if ((sockaddr->ss.ss_family == AF_INET) && (localaddr.ss.ss_family == AF_INET)) {
sockaddr->sin.sin_port = localaddr.sin.sin_port;
} else if ((sockaddr->ss.ss_family == AF_INET6) && (localaddr.ss.ss_family == AF_INET6)) {
sockaddr->sin6.sin6_port = localaddr.sin6.sin6_port;
} else {
ret = -EFAULT;
goto failure2;
}
}
return ret;
failure2:
sock_release(sc_sockets[type]);
sc_sockets[type] = 0;
failure:
return ret;
}
/* */
int sc_socket_getpeeraddr(struct sk_buff* skb, union capwap_addr* peeraddr) {
unsigned char* nethdr;
TRACEKMOD("### sc_socket_getpeeraddr\n");
/* */
nethdr = skb_network_header(skb);
if (!nethdr) {
return -EINVAL;
}
/* */
switch (ntohs(skb->protocol)) {
case ETH_P_IP: {
/* Validate IPv4 header */
if ((nethdr[0] & 0xf0) != 0x40) {
return -EINVAL;
}
/* Retrieve address */
peeraddr->sin.sin_family = AF_INET;
peeraddr->sin.sin_addr.s_addr = ((struct iphdr*)nethdr)->saddr;
peeraddr->sin.sin_port = udp_hdr(skb)->source;
break;
}
case ETH_P_IPV6: {
/* Validate IPv6 header */
if ((nethdr[0] & 0xf0) != 0x60) {
return -EINVAL;
}
/* Retrieve address */
peeraddr->sin6.sin6_family = AF_INET6;
memcpy(&peeraddr->sin6.sin6_addr, &((struct ipv6hdr*)nethdr)->saddr, sizeof(struct in6_addr));
peeraddr->sin6.sin6_port = udp_hdr(skb)->source;
break;
}
default: {
return -EINVAL;
}
}
return 0;
}
/* */
int sc_socket_send(int type, uint8_t* buffer, int length, union capwap_addr* sockaddr) {
struct kvec vec;
struct msghdr msg;
TRACEKMOD("### sc_socket_send\n");
/* */
vec.iov_base = buffer;
vec.iov_len = length;
/* */
memset(&msg, 0, sizeof(struct msghdr));
msg.msg_name = sockaddr;
msg.msg_namelen = sizeof(union capwap_addr);
msg.msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT;
/* */
return kernel_sendmsg(sc_sockets[type], &msg, &vec, 1, length);
}
/* */
int sc_socket_init(void) {
TRACEKMOD("### sc_socket_init\n");
memset(sc_sockets, 0, sizeof(sc_sockets));
return 0;
}
/* */
int sc_socket_bind(union capwap_addr* sockaddr) {
int ret;
TRACEKMOD("### sc_socket_bind\n");
/* */
if (sc_sockets[SOCKET_UDP] || sc_sockets[SOCKET_UDPLITE]) {
return -EBUSY;
}
/* UDP socket */
ret = sc_socket_create(SOCKET_UDP, sockaddr, IPPROTO_UDP);
if (ret) {
goto failure;
}
/* UDPLite socket */
ret = sc_socket_create(SOCKET_UDPLITE, sockaddr, IPPROTO_UDPLITE);
if (ret) {
goto failure;
}
/* */
udp_encap_enable();
if (sockaddr->ss.ss_family == AF_INET6) {
udpv6_encap_enable();
}
return 0;
failure:
sc_socket_close();
return ret;
}
/* */
void sc_socket_close(void) {
TRACEKMOD("### sc_socket_close\n");
/* Close sockets */
if (sc_sockets[SOCKET_UDP]) {
kernel_sock_shutdown(sc_sockets[SOCKET_UDP], SHUT_RDWR);
sock_release(sc_sockets[SOCKET_UDP]);
}
if (sc_sockets[SOCKET_UDPLITE]) {
kernel_sock_shutdown(sc_sockets[SOCKET_UDPLITE], SHUT_RDWR);
sock_release(sc_sockets[SOCKET_UDPLITE]);
}
memset(sc_sockets, 0, sizeof(sc_sockets));
}
/* */
int sc_addr_compare(const union capwap_addr* addr1, const union capwap_addr* addr2) {
TRACEKMOD("### sc_addr_compare\n");
if (addr1->ss.ss_family == addr2->ss.ss_family) {
if (addr1->ss.ss_family == AF_INET) {
return (((addr1->sin.sin_addr.s_addr == addr2->sin.sin_addr.s_addr) && (addr1->sin.sin_port == addr2->sin.sin_port)) ? 0 : -1);
} else if (addr1->ss.ss_family == AF_INET6) {
return ((!memcmp(&addr1->sin6.sin6_addr, &addr2->sin6.sin6_addr, sizeof(struct in6_addr)) && (addr1->sin6.sin6_port == addr2->sin6.sin6_port)) ? 0 : -1);
}
}
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;
}
}

45
src/ac/kmod/socket.h Normal file
View File

@ -0,0 +1,45 @@
#ifndef __KMOD_SOCKET_HEADER__
#define __KMOD_SOCKET_HEADER__
#include <linux/socket.h>
#include <linux/in.h>
#include <linux/in6.h>
#include <linux/skbuff.h>
/* */
#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;
struct sockaddr_in sin;
struct sockaddr_in6 sin6;
struct sockaddr_storage ss;
};
/* */
int sc_socket_init(void);
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__ */