The capwap data channel migrated from userspace to kernalspace
This commit is contained in:
@ -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
621
src/ac/kmod/capwap.c
Normal 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
128
src/ac/kmod/capwap.h
Normal 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__ */
|
636
src/ac/kmod/capwap_private.c
Normal file
636
src/ac/kmod/capwap_private.c
Normal 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);
|
||||
}
|
26
src/ac/kmod/capwap_private.h
Normal file
26
src/ac/kmod/capwap_private.h
Normal 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
172
src/ac/kmod/capwap_rfc.h
Normal 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
13
src/ac/kmod/config.h
Normal 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__ */
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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__ */
|
||||
|
@ -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
257
src/ac/kmod/socket.c
Normal 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
45
src/ac/kmod/socket.h
Normal 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__ */
|
Reference in New Issue
Block a user