#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; }