freewtp/src/common/capwap_protocol.c

1328 lines
40 KiB
C

#include "capwap.h"
#include "capwap_protocol.h"
#include "capwap_network.h"
#include "capwap_dfa.h"
#include "capwap_list.h"
#include "capwap_array.h"
#include "md5.h"
/* */
static struct capwap_fragment_sender* capwap_defragment_add_sender(capwap_fragment_list* defraglist, struct sockaddr_storage* sendaddr, struct capwap_header* header);
static struct capwap_list_item* capwap_defragment_create_packet(void* payload, int size, unsigned short offset);
/* Check valid packet */
int capwap_sanity_check(int isctrlsocket, int state, void* buffer, int buffersize, int dtlsctrlenable, int dtlsdataenable) {
struct capwap_preamble* preamble;
ASSERT(buffer != NULL);
ASSERT(buffersize > sizeof(struct capwap_preamble));
preamble = (struct capwap_preamble*)buffer;
if (preamble->version != CAPWAP_PROTOCOL_VERSION) {
return CAPWAP_WRONG_PACKET;
}
if (isctrlsocket) {
if (dtlsctrlenable) {
if ((preamble->type == CAPWAP_PREAMBLE_DTLS_HEADER) && (buffersize >= sizeof(struct capwap_dtls_header))) {
if (state == CAPWAP_DISCOVERY_STATE) {
return CAPWAP_WRONG_PACKET;
}
return CAPWAP_DTLS_PACKET;
} else if ((preamble->type == CAPWAP_PREAMBLE_HEADER) && (buffersize >= sizeof(struct capwap_header))) {
struct capwap_header* header = (struct capwap_header*)preamble;
if (buffersize >= GET_HLEN_HEADER(header) * 4) {
if ((state != CAPWAP_DISCOVERY_STATE) && (state != CAPWAP_UNDEF_STATE)) {
return CAPWAP_WRONG_PACKET;
}
return CAPWAP_PLAIN_PACKET;
}
}
} else {
if ((preamble->type == CAPWAP_PREAMBLE_HEADER) && (buffersize >= sizeof(struct capwap_header))) {
struct capwap_header* header = (struct capwap_header*)preamble;
if (buffersize >= GET_HLEN_HEADER(header) * 4) {
return CAPWAP_PLAIN_PACKET;
}
}
}
} else {
if ((state != CAPWAP_DATA_CHECK_TO_RUN_STATE) && (state != CAPWAP_RUN_STATE) && (state != CAPWAP_UNDEF_STATE)) {
return CAPWAP_WRONG_PACKET;
}
if (dtlsdataenable) {
if ((preamble->type == CAPWAP_PREAMBLE_DTLS_HEADER) && (buffersize >= sizeof(struct capwap_dtls_header))) {
return CAPWAP_DTLS_PACKET;
}
} else {
if ((preamble->type == CAPWAP_PREAMBLE_HEADER) && (buffersize >= sizeof(struct capwap_header))) {
struct capwap_header* header = (struct capwap_header*)preamble;
if (buffersize >= GET_HLEN_HEADER(header) * 4) {
return CAPWAP_PLAIN_PACKET;
}
}
}
}
return CAPWAP_WRONG_PACKET;
}
/* */
int capwap_defragment_packets(struct sockaddr_storage* sendaddr, void* buffer, int buffersize, capwap_fragment_list* defraglist, struct capwap_packet* packet) {
struct capwap_header* header = (struct capwap_header*)buffer;
struct capwap_fragment_sender* defragsend;
int headersize;
ASSERT(sendaddr != NULL);
ASSERT(buffer != NULL);
ASSERT(buffersize > sizeof(struct capwap_header));
ASSERT(defraglist != NULL);
ASSERT(packet != NULL);
headersize = GET_HLEN_HEADER(header) * 4;
defragsend = capwap_defragment_find_sender(defraglist, sendaddr);
if (IS_FLAG_F_HEADER(header)) {
struct capwap_list_item* searchpacket;
struct capwap_fragment_packet* itempacket;
unsigned short fragid = GET_FRAGMENT_ID_HEADER(header);
unsigned short fragoffset = GET_FRAGMENT_OFFSET_HEADER(header);
char* payload = (char*)buffer + headersize;
int payloadsize = buffersize - headersize;
/* Size of payload is multiple of 64bits */
if ((payloadsize % 8) != 0) {
return CAPWAP_WRONG_FRAGMENT;
}
if (!defragsend) {
/* Create new defragment item */
defragsend = capwap_defragment_add_sender(defraglist, sendaddr, header);
if (!defragsend) {
return CAPWAP_WRONG_FRAGMENT;
}
} else if (fragid != defragsend->fragment_id) {
/* Wrong fragment id */
capwap_defragment_remove_sender(defraglist, sendaddr);
return CAPWAP_WRONG_FRAGMENT;
}
if (fragoffset == 0) {
/* Save header of the first packet of fragmentation */
defragsend->header = (struct capwap_header*)capwap_clone(header, headersize);
}
/* Defragment payload */
searchpacket = defragsend->packetlist->last;
if (!searchpacket) {
struct capwap_list_item* packet = capwap_defragment_create_packet(payload, payloadsize, fragoffset);
capwap_itemlist_insert_before(defragsend->packetlist, NULL, packet);
} else {
while (searchpacket != NULL) {
itempacket = (struct capwap_fragment_packet*)searchpacket->item;
ASSERT(itempacket != NULL);
if (itempacket->offset > fragoffset) {
if (!searchpacket->prev) {
struct capwap_list_item* packet = capwap_defragment_create_packet(payload, payloadsize, fragoffset);
capwap_itemlist_insert_before(defragsend->packetlist, searchpacket, packet);
break;
}
} else if (itempacket->offset < fragoffset) {
if (!defragsend->islastrecv || (searchpacket != defragsend->packetlist->last)) {
struct capwap_list_item* packet = capwap_defragment_create_packet(payload, payloadsize, fragoffset);
capwap_itemlist_insert_after(defragsend->packetlist, searchpacket, packet);
}
break;
} else {
/* Duplicate packet */
break;
}
/* Prev fragment */
searchpacket = searchpacket->prev;
}
}
/* If last packet, mark end of fragmentation */
if (!defragsend->islastrecv) {
defragsend->islastrecv = IS_FLAG_L_HEADER(header);
}
/* Check if defragmentation is completed */
if (defragsend->islastrecv) {
unsigned long checkoffset = 0;
payloadsize = 0;
searchpacket = defragsend->packetlist->first;
while (searchpacket != NULL) {
itempacket = (struct capwap_fragment_packet*)searchpacket->item;
if (checkoffset != itempacket->offset) {
return CAPWAP_REQUEST_MORE_FRAGMENT;
}
/* Next fragment */
payloadsize += itempacket->size;
checkoffset += itempacket->size / 8;
searchpacket = searchpacket->next;
}
/* Defragment complete */
ASSERT(defragsend->header != NULL);
headersize = GET_HLEN_HEADER(defragsend->header) * 4;
packet->packetsize = headersize + payloadsize;
packet->header = (struct capwap_header*)capwap_alloc(packet->packetsize);
if (!packet->header) {
capwap_outofmemory();
}
/* Copy header and remove fragmention information */
memcpy(packet->header, defragsend->header, headersize);
SET_FLAG_F_HEADER(packet->header, 0);
SET_FLAG_L_HEADER(packet->header, 0);
SET_FRAGMENT_ID_HEADER(packet->header, 0);
SET_FRAGMENT_OFFSET_HEADER(packet->header, 0);
/* Copy payload */
packet->payload = (char*)packet->header + headersize;
payload = packet->payload;
searchpacket = defragsend->packetlist->first;
while (searchpacket != NULL) {
itempacket = (struct capwap_fragment_packet*)searchpacket->item;
memcpy(payload, itempacket->buffer, itempacket->size);
payload += itempacket->size;
/* Next */
searchpacket = searchpacket->next;
}
capwap_defragment_remove_sender(defraglist, sendaddr);
return CAPWAP_RECEIVE_COMPLETE_PACKET;
}
return CAPWAP_REQUEST_MORE_FRAGMENT;
} else {
/* Check if already received fragment packets */
if (defragsend) {
/* Overlap fragment packet with complete packet */
capwap_defragment_remove_sender(defraglist, sendaddr);
} else {
/* Copy buffer */
packet->packetsize = buffersize;
packet->header = (struct capwap_header*)capwap_clone(buffer, packet->packetsize);
packet->payload = (void*)((char*)buffer + headersize);
return CAPWAP_RECEIVE_COMPLETE_PACKET;
}
}
return CAPWAP_WRONG_FRAGMENT;
}
/* */
capwap_fragment_list* capwap_defragment_init_list(void) {
return capwap_list_create();
}
/* */
static void capwap_defragment_free_packetlist(struct capwap_list* packetlist) {
struct capwap_list_item* search;
ASSERT(packetlist != NULL);
search = packetlist->first;
while (search) {
struct capwap_fragment_packet* packet = (struct capwap_fragment_packet*)search->item;
ASSERT(packet->buffer != NULL);
capwap_free(packet->buffer);
/* Next */
search = search->next;
}
capwap_list_free(packetlist);
}
/* */
void capwap_defragment_flush_list(capwap_fragment_list* defraglist) {
struct capwap_fragment_sender* item;
ASSERT(defraglist != NULL);
while (defraglist->first) {
item = (struct capwap_fragment_sender*)defraglist->first->item;
ASSERT(item != NULL);
ASSERT(item->packetlist != NULL);
capwap_defragment_free_packetlist(item->packetlist);
capwap_itemlist_free(capwap_itemlist_remove(defraglist, defraglist->first));
}
}
/* */
void capwap_defragment_free_list(capwap_fragment_list* defraglist) {
ASSERT(defraglist != NULL);
capwap_defragment_flush_list(defraglist);
capwap_list_free(defraglist);
}
/* */
struct capwap_fragment_sender* capwap_defragment_find_sender(capwap_fragment_list* defraglist, struct sockaddr_storage* sendaddr) {
struct capwap_fragment_sender* item;
struct capwap_list_item* search;
ASSERT(defraglist != NULL);
ASSERT(sendaddr != NULL);
search = defraglist->first;
while (search) {
item = (struct capwap_fragment_sender*)search->item;
ASSERT(item != NULL);
if (!capwap_compare_ip(sendaddr, &item->sendaddr)) {
return item;
}
search = search->next;
}
return NULL;
}
/* */
static struct capwap_fragment_sender* capwap_defragment_add_sender(capwap_fragment_list* defraglist, struct sockaddr_storage* sendaddr, struct capwap_header* header) {
struct capwap_list_item* item;
struct capwap_fragment_sender* sender;
int headersize;
ASSERT(defraglist != NULL);
ASSERT(sendaddr != NULL);
ASSERT(header != NULL);
item = capwap_itemlist_create(sizeof(struct capwap_fragment_sender));
sender = (struct capwap_fragment_sender*)item->item;
memset(sender, 0, sizeof(struct capwap_fragment_sender));
memcpy(&sender->sendaddr, sendaddr, sizeof(struct sockaddr_storage));
sender->fragment_id = GET_FRAGMENT_ID_HEADER(header);
headersize = GET_HLEN_HEADER(header) * 4;
sender->header = (struct capwap_header*)capwap_alloc(headersize);
if (!sender->header) {
capwap_outofmemory();
}
memcpy(sender->header, header, headersize);
sender->packetlist = capwap_list_create();
/* Add item to list */
capwap_itemlist_insert_after(defraglist, NULL, item);
return sender;
}
/* */
int capwap_defragment_remove_sender(capwap_fragment_list* defraglist, struct sockaddr_storage* sendaddr) {
int found = 0;
struct capwap_fragment_sender* item;
struct capwap_list_item* search;
ASSERT(defraglist != NULL);
ASSERT(sendaddr != NULL);
search = defraglist->first;
while (search) {
item = (struct capwap_fragment_sender*)search->item;
ASSERT(item != NULL);
if (!capwap_compare_ip(sendaddr, &item->sendaddr)) {
ASSERT(item->packetlist);
found = 1;
capwap_defragment_free_packetlist(item->packetlist);
capwap_itemlist_free(capwap_itemlist_remove(defraglist, search));
break;
}
search = search->next;
}
return found;
}
/* */
static struct capwap_list_item* capwap_defragment_create_packet(void* payload, int size, unsigned short offset) {
struct capwap_fragment_packet* packet;
struct capwap_list_item* item;
ASSERT(payload != NULL);
ASSERT(size > 0);
/* New fragment */
item = capwap_itemlist_create(sizeof(struct capwap_fragment_packet));
packet = (struct capwap_fragment_packet*)item->item;
memset(packet, 0, sizeof(struct capwap_fragment_packet));
packet->buffer = capwap_clone(payload, size);
packet->size = size;
packet->offset = offset;
return item;
}
/* */
void capwap_free_packet(struct capwap_packet* packet) {
ASSERT(packet != NULL);
if (packet->header) {
capwap_free(packet->header);
memset(packet, 0, sizeof(struct capwap_packet));
}
}
/* Creare tx packet */
struct capwap_build_packet* capwap_tx_packet_create(unsigned short radioid, unsigned short binding) {
struct capwap_build_packet* packet;
struct capwap_header* header;
packet = (struct capwap_build_packet*)capwap_alloc(sizeof(struct capwap_build_packet));
if (!packet) {
capwap_outofmemory();
}
memset(packet, 0, sizeof(struct capwap_build_packet));
header = &packet->header;
/* Standard configuration */
SET_VERSION_HEADER(header, CAPWAP_PROTOCOL_VERSION);
SET_TYPE_HEADER(header, CAPWAP_PREAMBLE_HEADER);
SET_HLEN_HEADER(header, sizeof(struct capwap_header) / 4);
SET_RID_HEADER(header, radioid);
SET_WBID_HEADER(header, binding);
/* Message elements list */
packet->elementslist = capwap_list_create();
return packet;
}
/* Destroy tx packet */
void capwap_build_packet_free(struct capwap_build_packet* buildpacket) {
ASSERT(buildpacket != NULL);
/* */
capwap_list_free(buildpacket->elementslist);
capwap_free(buildpacket);
}
/* Add radio macaddress to packet */
void capwap_build_packet_set_radio_macaddress(struct capwap_build_packet* buildpacket, int radiotype, char* macaddress) {
struct capwap_header* header;
ASSERT(buildpacket != NULL);
header = &buildpacket->header;
if (radiotype == CAPWAP_MACADDRESS_NONE) {
if (IS_FLAG_M_HEADER(header)) {
if (!IS_FLAG_W_HEADER(header)) {
SET_HLEN_HEADER(header, sizeof(struct capwap_header) / 4);
} else {
struct capwap_wireless_information* wireless = GET_WIRELESS_INFORMATION_STRUCT(header);
int lengthpadded = (((sizeof(struct capwap_wireless_information) + wireless->length) + 3) / 4);
/* Move wireless information */
memmove(((char*)header + sizeof(struct capwap_header)), wireless, lengthpadded * 4);
SET_HLEN_HEADER(header, (sizeof(struct capwap_header) / 4) + lengthpadded);
}
SET_FLAG_M_HEADER(header, 0);
}
} else {
int i;
int radiosizepadded;
struct capwap_mac_address* radio;
int size = sizeof(struct capwap_header) / 4;
ASSERT(macaddress != NULL);
ASSERT((radiotype == CAPWAP_MACADDRESS_EUI48) || (radiotype == CAPWAP_MACADDRESS_EUI64));
if (IS_FLAG_M_HEADER(header)) {
radio = GET_RADIO_MAC_ADDRESS_STRUCT(header);
if (radio->length == radiotype) {
/* Rewrite mac address */
memcpy(radio->address, macaddress, radiotype);
return;
}
/* Remove old radio mac address */
capwap_build_packet_set_radio_macaddress(buildpacket, CAPWAP_MACADDRESS_NONE, NULL);
}
/* Radio mac address size*/
radio = (struct capwap_mac_address*)((char*)header + sizeof(struct capwap_header));
radiosizepadded = (((sizeof(struct capwap_mac_address) + radiotype) + 3) / 4);
size += radiosizepadded;
/* Wireless information */
if (IS_FLAG_W_HEADER(header)) {
struct capwap_wireless_information* wireless = GET_WIRELESS_INFORMATION_STRUCT(header);
int lengthpadded = (((sizeof(struct capwap_wireless_information) + wireless->length) + 3) / 4);
memmove((char*)radio + radiosizepadded, wireless, lengthpadded * 4);
size += lengthpadded;
}
radio->length = radiotype;
memcpy(radio->address, macaddress, radiotype);
for (i = (radiosizepadded * 4) - 2; i >= radiotype; i--) {
radio->address[i] = 0;
}
SET_FLAG_M_HEADER(header, 1);
SET_HLEN_HEADER(header, size);
}
}
/* Add Wireless Specific Information to packet */
void capwap_build_packet_set_wireless_information(struct capwap_build_packet* buildpacket, void* buffer, unsigned char length) {
struct capwap_header* header;
int size;
ASSERT(buildpacket != NULL);
header = &buildpacket->header;
/* Calculate size of header */
size = sizeof(struct capwap_header) / 4;
if (IS_FLAG_M_HEADER(header)) {
struct capwap_mac_address* radio = GET_RADIO_MAC_ADDRESS_STRUCT(header);
size += ((sizeof(struct capwap_mac_address) + radio->length) + 3) / 4;
}
/* Remove old wireless information */
if (IS_FLAG_W_HEADER(header)) {
SET_HLEN_HEADER(header, size);
}
/* Add new wireless information */
if (length > 0) {
int i;
struct capwap_wireless_information* wireless;
int lengthpadded = (((sizeof(struct capwap_wireless_information) + length) + 3) / 4);
ASSERT(buffer != NULL);
wireless = GET_WIRELESS_INFORMATION_STRUCT(header);
wireless->length = length;
memcpy(wireless->data, buffer, length);
for (i = (lengthpadded * 4) - 2; i >= length; i--) {
wireless->data[i] = 0;
}
/* Update size */
size += lengthpadded;
SET_HLEN_HEADER(header, size);
}
}
/* Set control message type */
void capwap_build_packet_set_control_message_type(struct capwap_build_packet* buildpacket, unsigned long type, unsigned char seq) {
ASSERT(buildpacket != NULL);
buildpacket->ctrlmsg.type = htonl(type);
buildpacket->ctrlmsg.seq = seq;
buildpacket->ctrlmsg.length = 0;
buildpacket->ctrlmsg.flags = 0;
}
/* Add message element */
void capwap_build_packet_add_message_element(struct capwap_build_packet* buildpacket, struct capwap_message_element* element) {
struct capwap_list_item* itemlist;
unsigned long length;
ASSERT(buildpacket != NULL);
if ((element == NULL) || (element->length == 0)) {
capwap_logging_debug("Warning, add null element to packet");
return;
}
/* Create item and add into last position of list*/
length = ntohs(element->length) + sizeof(struct capwap_message_element);
itemlist = capwap_itemlist_create_with_item(element, length);
capwap_itemlist_insert_after(buildpacket->elementslist, NULL, itemlist);
/* */
if (buildpacket->isctrlmsg) {
buildpacket->ctrlmsg.length = htons(ntohs(buildpacket->ctrlmsg.length) + length);
} else {
buildpacket->datamsg.length = htons(ntohs(buildpacket->datamsg.length) + length);
}
}
/* Generate fragment packets */
int capwap_fragment_build_packet(struct capwap_build_packet* buildpacket, capwap_fragment_packet_array* packets, unsigned short mtu, unsigned short fragmentid) {
unsigned short i;
unsigned short reqpacket;
unsigned long length = 0;
unsigned short headerlength = 0;
struct capwap_header* header;
unsigned short fragmentposition = 0;
struct capwap_list_item* item = NULL;
unsigned long itempos = 0;
ASSERT(buildpacket != NULL);
ASSERT(packets != NULL);
/* Free array */
capwap_fragment_free(packets);
if ((mtu > 0) && (mtu < CAPWAP_HEADER_MAX_SIZE)) {
/* Mtu must be greater than the maximum size of capwap header */
capwap_logging_debug("The mtu is too small: %hu", mtu);
return -1;
}
/* Get length raw packet */
header = &buildpacket->header;
headerlength = GET_HLEN_HEADER(header) * 4;
if (buildpacket->isctrlmsg) {
length = sizeof(struct capwap_control_message) + ntohs(buildpacket->ctrlmsg.length);
} else if (IS_FLAG_K_HEADER(header)) {
length = sizeof(struct capwap_data_message) + ntohs(buildpacket->datamsg.length);
}
/* Retrive number of request packet for send a capwap message */
if (!mtu || ((headerlength + length) <= mtu)) {
reqpacket = 1;
} else {
unsigned long lengthpayload = length;
unsigned short mtupayload;
/* Detect mtu payload */
mtupayload = mtu - sizeof(struct capwap_header);
mtupayload -= mtupayload % 8;
/* Calculate number of request packets without size of header */
if (IS_FLAG_M_HEADER(header)) {
struct capwap_mac_address* radio = GET_RADIO_MAC_ADDRESS_STRUCT(header);
lengthpayload += ((sizeof(struct capwap_mac_address) + radio->length) + 3) / 4;
}
if (IS_FLAG_W_HEADER(header)) {
struct capwap_wireless_information* wireless = GET_WIRELESS_INFORMATION_STRUCT(header);
lengthpayload += ((sizeof(struct capwap_wireless_information) + wireless->length) + 3) / 4;
}
/* Request packet padded */
reqpacket = (lengthpayload + (mtupayload - 1)) / mtupayload;
}
/* Create packets */
capwap_array_resize(packets, reqpacket);
for (i = 0; i < reqpacket; i++) {
long payloadsize = 0;
struct capwap_packet* packet = (struct capwap_packet*)capwap_array_get_item_pointer(packets, i);
memset(packet, 0, sizeof(struct capwap_packet));
if (reqpacket == 1) {
/* Build header */
packet->packetsize = headerlength + length;
packet->header = (struct capwap_header*)capwap_alloc(packet->packetsize);
memcpy(packet->header, &buildpacket->header, headerlength);
packet->payload = (void*)(((char*)packet->header) + headerlength);
payloadsize = length;
/* Disable fragmentation */
SET_FLAG_F_HEADER(packet->header, 0);
SET_FRAGMENT_ID_HEADER(packet->header, 0);
SET_FRAGMENT_OFFSET_HEADER(packet->header, 0);
SET_FLAG_L_HEADER(packet->header, 0);
} else {
unsigned short headerpos = ((i == 0) ? headerlength : sizeof(struct capwap_header));
unsigned short mtupayload;
/* Detect mtu payload */
mtupayload = mtu - headerpos;
mtupayload -= mtupayload % 8;
/* Build header */
packet->packetsize = headerpos + mtupayload;
packet->header = (struct capwap_header*)capwap_alloc(packet->packetsize);
memcpy(packet->header, &buildpacket->header, headerpos);
packet->payload = (void*)(((char*)packet->header) + headerpos);
payloadsize = mtupayload;
if (i > 0) {
/* Radio mac address and wireless information is sent only into first packet */
SET_FLAG_M_HEADER(packet->header, 0);
SET_FLAG_W_HEADER(packet->header, 0);
SET_HLEN_HEADER(packet->header, sizeof(struct capwap_header) / 4);
}
/* Use fragmentation */
SET_FLAG_F_HEADER(packet->header, 1);
SET_FRAGMENT_ID_HEADER(packet->header, fragmentid);
SET_FRAGMENT_OFFSET_HEADER(packet->header, fragmentposition);
SET_FLAG_L_HEADER(packet->header, (((i + 1) == reqpacket) ? 1 : 0));
}
/* Build payload */
if (length > 0) {
char* pos = (char*)packet->payload;
/* Data/Control Message */
if (i == 0) {
if (buildpacket->isctrlmsg) {
/* Control Message header can not fragment */
if (payloadsize < sizeof(struct capwap_control_message)) {
capwap_logging_debug("Unable fragments packet, mtu is too small");
capwap_fragment_free(packets);
return -1;
}
memcpy(packet->payload, &buildpacket->ctrlmsg, sizeof(struct capwap_control_message));
pos += sizeof(struct capwap_control_message);
payloadsize -= sizeof(struct capwap_control_message);
} else if (IS_FLAG_K_HEADER(header)) {
/* Data Message header can not fragment */
if (payloadsize < sizeof(struct capwap_data_message)) {
capwap_logging_debug("Unable fragments packet, mtu is too small");
capwap_fragment_free(packets);
return -1;
}
memcpy(packet->payload, &buildpacket->datamsg, sizeof(struct capwap_data_message));
pos += sizeof(struct capwap_data_message);
payloadsize -= sizeof(struct capwap_data_message);
}
/* Configure message elements */
item = buildpacket->elementslist->first;
itempos = 0;
}
/* Add message elements */
while ((item != NULL) && (payloadsize > 0)) {
unsigned short elementcopy;
unsigned short elementlength;
struct capwap_message_element* element = (struct capwap_message_element*)item->item;
ASSERT(element != NULL);
/* Copy message element */
elementlength = sizeof(struct capwap_message_element) + ntohs(element->length);
elementcopy = min(elementlength - itempos, payloadsize);
memcpy(pos, &((char*)element)[itempos], elementcopy);
pos += elementcopy;
itempos += elementcopy;
payloadsize -= elementcopy;
ASSERT(payloadsize >= 0);
/* Next element */
if (itempos == elementlength) {
item = item->next;
itempos = 0;
}
}
if (((i + 1) == reqpacket) && (payloadsize > 0)) {
packet->packetsize -= payloadsize;
} else {
ASSERT(payloadsize == 0);
}
}
}
/* Return 1 if fragment packet */
return ((reqpacket > 1) ? 1 : 0);
}
/* */
void capwap_fragment_free(capwap_fragment_packet_array* packets) {
unsigned long i;
ASSERT(packets != NULL);
if (packets->count == 0) {
return;
}
for (i = 0; i < packets->count; i++) {
capwap_free_packet((struct capwap_packet*)capwap_array_get_item_pointer(packets, i));
}
capwap_array_resize(packets, 0);
}
/* */
struct capwap_build_packet* capwap_rx_packet_create(void* buffer, int buffersize, int isctrlpacket) {
struct capwap_build_packet* buildpacket;
struct capwap_header* header;
char* pos = (char*)buffer;
int length;
int controlsize;
ASSERT(buffer != NULL);
ASSERT(buffersize > 0);
/* Header */
header = (struct capwap_header*)buffer;
length = GET_HLEN_HEADER(header) * 4;
if (buffersize < length) {
return NULL;
}
/* Build packet */
buildpacket = (struct capwap_build_packet*)capwap_alloc(sizeof(struct capwap_build_packet));
if (!buildpacket) {
capwap_outofmemory();
}
/* */
memset(buildpacket, 0, sizeof(struct capwap_build_packet));
buildpacket->isctrlmsg = (isctrlpacket ? 1 : 0);
/* Header packet */
memcpy(&buildpacket->header, pos, length);
pos += length;
buffersize -= length;
if (buildpacket->isctrlmsg) {
if (buffersize < sizeof(struct capwap_control_message)) {
capwap_logging_debug("Invalid capwap packet, size of control message body is great of raw packet");
capwap_free(buildpacket);
return NULL;
}
/* Control message header */
memcpy(&buildpacket->ctrlmsg, pos, sizeof(struct capwap_control_message));
pos += sizeof(struct capwap_control_message);
buffersize -= sizeof(struct capwap_control_message);
/* Check the packet size */
controlsize = ntohs(buildpacket->ctrlmsg.length);
if (controlsize > buffersize) {
capwap_logging_debug("Invalid capwap packet, size of control message body is great of raw packet");
capwap_free(buildpacket);
return NULL;
}
/* Message elements list */
buildpacket->elementslist = capwap_list_create();
while (controlsize > 0) {
struct capwap_message_element* element = (struct capwap_message_element*)pos;
int elementsize = ntohs(element->length) + sizeof(struct capwap_message_element);
struct capwap_list_item* itemlist;
/* Clone message element */
itemlist = capwap_itemlist_create(elementsize);
memcpy(itemlist->item, pos, elementsize);
capwap_itemlist_insert_after(buildpacket->elementslist, NULL, itemlist);
/* Next */
pos += elementsize;
controlsize -= elementsize;
buffersize -= elementsize;
}
} else {
if (IS_FLAG_K_HEADER(&buildpacket->header)) {
if (buffersize < sizeof(struct capwap_data_message)) {
capwap_logging_debug("Invalid capwap packet, size of data message body is great of raw packet");
capwap_free(buildpacket);
return NULL;
}
/* Control message header */
memcpy(&buildpacket->datamsg, pos, sizeof(struct capwap_data_message));
pos += sizeof(struct capwap_data_message);
buffersize -= sizeof(struct capwap_data_message);
/* Check the packet size */
controlsize = ntohs(buildpacket->datamsg.length);
if (controlsize > buffersize) {
capwap_logging_debug("Invalid capwap packet, size of data message body is great of raw packet");
capwap_free(buildpacket);
return NULL;
}
/* Message elements list */
buildpacket->elementslist = capwap_list_create();
while (controlsize > 0) {
struct capwap_message_element* element = (struct capwap_message_element*)pos;
int elementsize = ntohs(element->length) + sizeof(struct capwap_message_element);
struct capwap_list_item* itemlist;
/* Clone message element */
itemlist = capwap_itemlist_create(elementsize);
memcpy(itemlist->item, pos, elementsize);
capwap_itemlist_insert_after(buildpacket->elementslist, NULL, itemlist);
/* Next */
pos += elementsize;
controlsize -= elementsize;
buffersize -= elementsize;
}
} else {
/* TODO */
}
}
return buildpacket;
}
/* */
unsigned long capwap_build_packet_validate(struct capwap_build_packet* buildpacket, capwap_unrecognized_element_array* reasonarray) {
unsigned short binding;
int ieee80211delta = CAPWAP_80211_MESSAGE_ELEMENTS_START - CAPWAP_MESSAGE_ELEMENTS_COUNT;
int elements[CAPWAP_MESSAGE_ELEMENTS_COUNT + CAPWAP_80211_MESSAGE_ELEMENTS_COUNT];
struct capwap_list_item* item;
unsigned long result = CAPWAP_VALID_PACKET;
struct capwap_resultcode_element* resultcodeelement = NULL;
ASSERT(buildpacket != NULL);
/* Reset flags */
memset(elements, 0, sizeof(elements));
/* Scan all elements */
item = buildpacket->elementslist->first;
while (item != NULL) {
struct unrecognized_info info = { 0, 0 };
struct capwap_message_element* elementitem = (struct capwap_message_element*)item->item;
unsigned short type = ntohs(elementitem->type);
struct capwap_message_elements_func* f = capwap_get_message_element(type); \
if (f && f->check && f->parsing) {
if (f->check(elementitem)) {
if (IS_MESSAGE_ELEMENTS(type)) {
elements[type] = 1;
if (type == CAPWAP_ELEMENT_RESULTCODE) {
resultcodeelement = (struct capwap_resultcode_element*)f->parsing(elementitem);
}
} else if (IS_80211_MESSAGE_ELEMENTS(type)) {
elements[type - ieee80211delta] = 1;
} else {
/* Unknown message element */
info.element = type;
info.reason = CAPWAP_REASON_UNKNOWN_MESSAGE_ELEMENT;
}
} else {
/* Invalid message element */
info.element = type;
info.reason = CAPWAP_REASON_UNKNOWN_MESSAGE_ELEMENT_VALUE;
}
} else {
/* Unable parsing message element */
info.element = type;
info.reason = CAPWAP_REASON_UNSUPPORTED_MESSAGE_ELEMENT;
}
/* Copy error */
if ((info.element != 0) && reasonarray) {
struct unrecognized_info* reasoninfo = capwap_array_get_item_pointer(reasonarray, reasonarray->count);
memcpy(reasoninfo, &info, sizeof(struct unrecognized_info));
result |= CAPWAP_UNRECOGNIZED_MSG_ELEMENT;
}
/* Next item */
item = item->next;
}
/* Verify flags */
binding = GET_WBID_HEADER(&buildpacket->header);
switch (ntohl(buildpacket->ctrlmsg.type)) {
case CAPWAP_DISCOVERY_REQUEST: {
if (elements[CAPWAP_ELEMENT_DISCOVERYTYPE] &&
elements[CAPWAP_ELEMENT_WTPBOARDDATA] &&
elements[CAPWAP_ELEMENT_WTPDESCRIPTOR] &&
elements[CAPWAP_ELEMENT_WTPFRAMETUNNELMODE] &&
elements[CAPWAP_ELEMENT_WTPMACTYPE]) {
if (binding == CAPWAP_WIRELESS_BINDING_IEEE80211) {
if (!elements[CAPWAP_ELEMENT_80211_WTPRADIOINFORMATION - ieee80211delta]) {
result |= CAPWAP_MISSING_MANDATORY_MSG_ELEMENT;
}
}
} else {
result |= CAPWAP_MISSING_MANDATORY_MSG_ELEMENT;
}
break;
}
case CAPWAP_DISCOVERY_RESPONSE: {
if (elements[CAPWAP_ELEMENT_ACDESCRIPTION] &&
elements[CAPWAP_ELEMENT_ACNAME] &&
(elements[CAPWAP_ELEMENT_CONTROLIPV4] || elements[CAPWAP_ELEMENT_CONTROLIPV6])) {
if (binding == CAPWAP_WIRELESS_BINDING_IEEE80211) {
if (!elements[CAPWAP_ELEMENT_80211_WTPRADIOINFORMATION - ieee80211delta]) {
result |= CAPWAP_MISSING_MANDATORY_MSG_ELEMENT;
}
}
} else {
result |= CAPWAP_MISSING_MANDATORY_MSG_ELEMENT;
}
break;
}
case CAPWAP_JOIN_REQUEST: {
if (elements[CAPWAP_ELEMENT_LOCATION] &&
elements[CAPWAP_ELEMENT_WTPBOARDDATA] &&
elements[CAPWAP_ELEMENT_WTPDESCRIPTOR] &&
elements[CAPWAP_ELEMENT_WTPNAME] &&
elements[CAPWAP_ELEMENT_SESSIONID] &&
elements[CAPWAP_ELEMENT_WTPFRAMETUNNELMODE] &&
elements[CAPWAP_ELEMENT_WTPMACTYPE] &&
elements[CAPWAP_ELEMENT_ECNSUPPORT] &&
(elements[CAPWAP_ELEMENT_LOCALIPV4] || elements[CAPWAP_ELEMENT_LOCALIPV6])) {
if (binding == CAPWAP_WIRELESS_BINDING_IEEE80211) {
if (!elements[CAPWAP_ELEMENT_80211_WTPRADIOINFORMATION - ieee80211delta]) {
result |= CAPWAP_MISSING_MANDATORY_MSG_ELEMENT;
}
}
} else {
result |= CAPWAP_MISSING_MANDATORY_MSG_ELEMENT;
}
break;
}
case CAPWAP_JOIN_RESPONSE: {
if (elements[CAPWAP_ELEMENT_RESULTCODE]) {
if ((resultcodeelement->code == CAPWAP_RESULTCODE_SUCCESS) || (resultcodeelement->code == CAPWAP_RESULTCODE_SUCCESS_NAT_DETECTED)) {
if (elements[CAPWAP_ELEMENT_ACDESCRIPTION] &&
elements[CAPWAP_ELEMENT_ACNAME] &&
elements[CAPWAP_ELEMENT_ECNSUPPORT] &&
(elements[CAPWAP_ELEMENT_CONTROLIPV4] || elements[CAPWAP_ELEMENT_CONTROLIPV6]) &&
(elements[CAPWAP_ELEMENT_LOCALIPV4] || elements[CAPWAP_ELEMENT_LOCALIPV6])) {
if (binding == CAPWAP_WIRELESS_BINDING_IEEE80211) {
if (!elements[CAPWAP_ELEMENT_80211_WTPRADIOINFORMATION - ieee80211delta]) {
result |= CAPWAP_MISSING_MANDATORY_MSG_ELEMENT;
}
}
} else {
result |= CAPWAP_MISSING_MANDATORY_MSG_ELEMENT;
}
} else if (resultcodeelement->code == CAPWAP_RESULTCODE_FAILURE_UNRECOGNIZED_MESSAGE_ELEMENT) {
if (!elements[CAPWAP_ELEMENT_RETURNEDMESSAGE]) {
result |= CAPWAP_MISSING_MANDATORY_MSG_ELEMENT;
}
}
} else {
result |= CAPWAP_MISSING_MANDATORY_MSG_ELEMENT;
}
break;
}
case CAPWAP_CONFIGURATION_STATUS_REQUEST: {
if (elements[CAPWAP_ELEMENT_ACNAME] &&
elements[CAPWAP_ELEMENT_RADIOADMSTATE] &&
elements[CAPWAP_ELEMENT_STATISTICSTIMER] &&
elements[CAPWAP_ELEMENT_WTPREBOOTSTAT]) {
/* TODO binding */
} else {
result |= CAPWAP_MISSING_MANDATORY_MSG_ELEMENT;
}
break;
}
case CAPWAP_CONFIGURATION_STATUS_RESPONSE: {
if (elements[CAPWAP_ELEMENT_TIMERS] &&
elements[CAPWAP_ELEMENT_DECRYPTERRORREPORTPERIOD] &&
elements[CAPWAP_ELEMENT_IDLETIMEOUT] &&
elements[CAPWAP_ELEMENT_WTPFALLBACK] &&
(elements[CAPWAP_ELEMENT_ACIPV4LIST] || elements[CAPWAP_ELEMENT_ACIPV6LIST])) {
/* TODO binding */
} else {
result |= CAPWAP_MISSING_MANDATORY_MSG_ELEMENT;
}
break;
}
case CAPWAP_CONFIGURATION_UPDATE_REQUEST: {
break;
}
case CAPWAP_CONFIGURATION_UPDATE_RESPONSE: {
break;
}
case CAPWAP_WTP_EVENT_REQUEST: {
break;
}
case CAPWAP_WTP_EVENT_RESPONSE: {
break;
}
case CAPWAP_CHANGE_STATE_EVENT_REQUEST: {
if (elements[CAPWAP_ELEMENT_RADIOOPRSTATE] &&
elements[CAPWAP_ELEMENT_RESULTCODE]) {
/* TODO binding */
} else {
result |= CAPWAP_MISSING_MANDATORY_MSG_ELEMENT;
}
break;
}
case CAPWAP_CHANGE_STATE_EVENT_RESPONSE: {
/* TODO binding */
break;
}
case CAPWAP_ECHO_REQUEST: {
break;
}
case CAPWAP_ECHO_RESPONSE: {
break;
}
case CAPWAP_IMAGE_DATA_REQUEST: {
break;
}
case CAPWAP_IMAGE_DATA_RESPONSE: {
break;
}
case CAPWAP_RESET_REQUEST: {
if (!elements[CAPWAP_ELEMENT_IMAGEIDENTIFIER]) {
result |= CAPWAP_MISSING_MANDATORY_MSG_ELEMENT;
}
break;
}
case CAPWAP_RESET_RESPONSE: {
if (!elements[CAPWAP_ELEMENT_RESULTCODE]) {
result |= CAPWAP_MISSING_MANDATORY_MSG_ELEMENT;
}
break;
}
case CAPWAP_PRIMARY_DISCOVERY_REQUEST: {
break;
}
case CAPWAP_PRIMARY_DISCOVERY_RESPONSE: {
break;
}
case CAPWAP_DATA_TRANSFER_REQUEST: {
break;
}
case CAPWAP_DATA_TRANSFER_RESPONSE: {
break;
}
case CAPWAP_CLEAR_CONFIGURATION_REQUEST: {
break;
}
case CAPWAP_CLEAR_CONFIGURATION_RESPONSE: {
break;
}
case CAPWAP_STATION_CONFIGURATION_REQUEST: {
break;
}
case CAPWAP_STATION_CONFIGURATION_RESPONSE: {
break;
}
}
if (resultcodeelement) {
capwap_get_message_element(CAPWAP_ELEMENT_RESULTCODE)->free(resultcodeelement);
}
return result;
}
/* Detect if type is a request */
int capwap_is_request_type(unsigned long type) {
if ((type == CAPWAP_DISCOVERY_REQUEST) ||
(type == CAPWAP_JOIN_REQUEST) ||
(type == CAPWAP_CONFIGURATION_STATUS_REQUEST) ||
(type == CAPWAP_CONFIGURATION_UPDATE_REQUEST) ||
(type == CAPWAP_WTP_EVENT_REQUEST) ||
(type == CAPWAP_CHANGE_STATE_EVENT_REQUEST) ||
(type == CAPWAP_ECHO_REQUEST) ||
(type == CAPWAP_IMAGE_DATA_REQUEST) ||
(type == CAPWAP_RESET_REQUEST) ||
(type == CAPWAP_PRIMARY_DISCOVERY_REQUEST) ||
(type == CAPWAP_DATA_TRANSFER_REQUEST) ||
(type == CAPWAP_CLEAR_CONFIGURATION_REQUEST) ||
(type == CAPWAP_STATION_CONFIGURATION_REQUEST)) {
/* Request type */
return 1;
}
return 0;
}
/* Retrieve packet digest */
void capwap_get_packet_digest(void* buffer, unsigned long length, unsigned char packetdigest[16]) {
MD5_CTX mdContext;
ASSERT(buffer != NULL);
ASSERT(length > 0);
MD5Init(&mdContext);
MD5Update(&mdContext, (unsigned char*)buffer, length);
MD5Final(&mdContext);
memcpy(&packetdigest[0], &mdContext.digest[0], sizeof(unsigned char) * 16);
}
/* Verify duplicate packet */
int capwap_recv_retrasmitted_request(struct capwap_dtls* dtls, struct capwap_packet* packet, unsigned char lastseqnumber, unsigned char packetdigest[16], struct capwap_socket* sock, capwap_fragment_packet_array* txfragmentpacket, struct sockaddr_storage* sendfromaddr, struct sockaddr_storage* sendtoaddr) {
unsigned char recvpacketdigest[16];
unsigned short lengthpayload;
ASSERT(packet != NULL);
ASSERT(sock != NULL);
ASSERT(txfragmentpacket != NULL);
ASSERT(sendtoaddr != NULL);
lengthpayload = packet->packetsize - GET_HLEN_HEADER(packet->header) * 4;
if (lengthpayload >= sizeof(struct capwap_control_message)) {
struct capwap_control_message* ctrlmsg = (struct capwap_control_message*)packet->payload;
/* Check if request */
if (capwap_is_request_type(ntohl(ctrlmsg->type)) && (ctrlmsg->seq == lastseqnumber)) {
/* Check packet digest */
capwap_get_packet_digest((void*)packet->header, packet->packetsize, recvpacketdigest);
if (!memcmp(&recvpacketdigest[0], &packetdigest[0], sizeof(unsigned char) * 16)) {
int i;
/* Retransmit response */
for (i = 0; i < txfragmentpacket->count; i++) {
struct capwap_packet* txpacket = (struct capwap_packet*)capwap_array_get_item_pointer(txfragmentpacket, i);
ASSERT(txpacket != NULL);
if (!capwap_crypt_sendto(dtls, sock->socket[sock->type], txpacket->header, txpacket->packetsize, sendfromaddr, sendtoaddr)) {
capwap_logging_debug("Warning: error to resend response packet");
break;
}
}
return 1;
}
}
}
return 0;
}
/* Check valid message type */
int capwap_check_message_type(struct capwap_dtls* dtls, struct capwap_packet* packet, unsigned short mtu) {
unsigned short lengthpayload;
ASSERT(packet != NULL);
ASSERT(mtu > 0);
lengthpayload = packet->packetsize - GET_HLEN_HEADER(packet->header) * 4;
if (lengthpayload >= sizeof(struct capwap_control_message)) {
struct capwap_control_message* ctrlmsg = (struct capwap_control_message*)packet->payload;
unsigned long type = ntohl(ctrlmsg->type);
if ((type >= CAPWAP_FIRST_MESSAGE_TYPE) && (type <= CAPWAP_LAST_MESSAGE_TYPE)) {
return 1;
}
/* Unknown message type */
if ((type % 2) != 0) {
int i;
struct capwap_build_packet* responsepacket;
struct capwap_resultcode_element resultcode = { CAPWAP_RESULTCODE_MSG_UNEXPECTED_UNRECOGNIZED_REQUEST };
capwap_fragment_packet_array* txfragmentpacket = NULL;
/* Odd message type, response with "Unrecognized Request" */
responsepacket = capwap_tx_packet_create(CAPWAP_RADIOID_NONE, GET_WBID_HEADER(packet->header));
responsepacket->isctrlmsg = 1;
capwap_build_packet_set_control_message_type(responsepacket, type + 1, ctrlmsg->seq);
capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_RESULTCODE_ELEMENT(&resultcode));
txfragmentpacket = capwap_array_create(sizeof(struct capwap_packet), 0);
if (capwap_fragment_build_packet(responsepacket, txfragmentpacket, mtu, 0) >= 0) {
for (i = 0; i < txfragmentpacket->count; i++) {
struct capwap_packet* txpacket = (struct capwap_packet*)capwap_array_get_item_pointer(txfragmentpacket, i);
ASSERT(txpacket != NULL);
if (!capwap_crypt_sendto(dtls, packet->socket.socket[packet->socket.type], txpacket->header, txpacket->packetsize, &packet->remoteaddr, &packet->remoteaddr)) {
break;
}
}
}
/* Free memory */
capwap_fragment_free(txfragmentpacket);
capwap_array_free(txfragmentpacket);
capwap_build_packet_free(responsepacket);
}
}
return 0;
}
int capwap_get_sessionid_from_keepalive(struct capwap_build_packet* buildpacket, struct capwap_sessionid_element* session) {
int found = 0;
ASSERT(buildpacket != NULL);
ASSERT(session != NULL);
/* Check is Data Packet KeepAlive */
if (IS_FLAG_K_HEADER(&buildpacket->header) && !capwap_build_packet_validate(buildpacket, NULL)) {
struct capwap_list_item* item = buildpacket->elementslist->first;
while (!found && (item != NULL)) {
struct capwap_message_element* elementitem = (struct capwap_message_element*)item->item;
unsigned short type = ntohs(elementitem->type);
struct capwap_message_elements_func* f = capwap_get_message_element(type);
ASSERT(f != NULL);
ASSERT(f->parsing != NULL);
switch (type) {
case CAPWAP_ELEMENT_SESSIONID: {
struct capwap_sessionid_element* tempsession;
tempsession = (struct capwap_sessionid_element*)f->parsing(elementitem);
memcpy(session, tempsession, sizeof(struct capwap_sessionid_element));
f->free(tempsession);
found = 1;
break;
}
}
/* Next element */
item = item->next;
}
}
return found;
}