freewtp/src/ac/ac_dfa_join.c

273 lines
11 KiB
C

#include "ac.h"
#include "capwap_dfa.h"
#include "capwap_array.h"
#include "ac_session.h"
/* */
int ac_dfa_state_join(struct ac_session_t* session, struct capwap_packet* packet) {
int i;
int status = AC_DFA_ACCEPT_PACKET;
struct capwap_resultcode_element resultcode = { CAPWAP_RESULTCODE_FAILURE };
struct capwap_build_packet* responsepacket;
ASSERT(session != NULL);
if (packet) {
struct capwap_build_packet* buildpacket;
buildpacket = capwap_rx_packet_create((void*)packet->header, packet->packetsize, packet->socket.isctrlsocket);
if (buildpacket) {
int validpacket;
unsigned long checkpacket;
struct capwap_array* returnedmessagearray = NULL;
capwap_unrecognized_element_array* unrecognizedarray;
struct capwap_element_join_request joinrequest;
unsigned short binding = GET_WBID_HEADER(&buildpacket->header);
/* */
unrecognizedarray = capwap_array_create(sizeof(struct unrecognized_info), 0);
/* */
checkpacket = capwap_build_packet_validate(buildpacket, unrecognizedarray);
if (!checkpacket) {
if (ac_valid_binding(binding)) {
if (ntohl(buildpacket->ctrlmsg.type) == CAPWAP_JOIN_REQUEST) {
resultcode.code = CAPWAP_RESULTCODE_SUCCESS;
} else {
resultcode.code = CAPWAP_RESULTCODE_MSG_UNEXPECTED_INVALID_CURRENT_STATE;
}
} else {
resultcode.code = CAPWAP_RESULTCODE_JOIN_FAILURE_BINDING_NOT_SUPPORTED;
}
} else {
if ((checkpacket & CAPWAP_MISSING_MANDATORY_MSG_ELEMENT) != 0) {
resultcode.code = CAPWAP_RESULTCODE_FAILURE_MISSING_MANDATORY_MSG_ELEMENT;
} else if ((checkpacket & CAPWAP_UNRECOGNIZED_MSG_ELEMENT) != 0) {
struct capwap_list_item* itemelement;
resultcode.code = CAPWAP_RESULTCODE_FAILURE_UNRECOGNIZED_MESSAGE_ELEMENT;
returnedmessagearray = capwap_array_create(sizeof(struct capwap_returnedmessage_element), unrecognizedarray->count);
for (i = 0; i < unrecognizedarray->count; i++) {
struct unrecognized_info* reasoninfo = capwap_array_get_item_pointer(unrecognizedarray, i);
/* Search element */
itemelement = buildpacket->elementslist->first;
while (itemelement != NULL) {
struct capwap_message_element* elementitem = (struct capwap_message_element*)itemelement->item;
if (ntohs(elementitem->type) == reasoninfo->element) {
struct capwap_returnedmessage_element* returnedelement = capwap_array_get_item_pointer(returnedmessagearray, i);
unsigned short length = sizeof(struct capwap_message_element) + ntohs(elementitem->length);
returnedelement->reason = reasoninfo->reason;
returnedelement->length = min(length, CAPWAP_RETURNED_MESSAGE_MAX_LENGTH);
memcpy(&returnedelement->message[0], elementitem, returnedelement->length);
break;
}
/* Next */
itemelement = itemelement->next;
}
}
}
}
/* */
capwap_array_free(unrecognizedarray);
/* */
capwap_init_element_join_request(&joinrequest, binding);
if (resultcode.code == CAPWAP_RESULTCODE_SUCCESS) {
/* Parsing elements list */
if (capwap_parsing_element_join_request(&joinrequest, buildpacket->elementslist->first)) {
/* TODO: gestione richiesta */
/* Get sessionid */
memcpy(&session->sessionid, joinrequest.sessionid, sizeof(struct capwap_sessionid_element));
/* Get binding */
session->binding = binding;
resultcode.code = CAPWAP_RESULTCODE_SUCCESS;
}
}
/* Create response */
responsepacket = capwap_tx_packet_create(CAPWAP_RADIOID_NONE, binding);
responsepacket->isctrlmsg = 1;
/* Prepare join response */
capwap_build_packet_set_control_message_type(responsepacket, CAPWAP_JOIN_RESPONSE, buildpacket->ctrlmsg.seq);
capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_RESULTCODE_ELEMENT(&resultcode));
/* Check is valid packet after parsing request */
validpacket = (((resultcode.code == CAPWAP_RESULTCODE_SUCCESS) || (resultcode.code == CAPWAP_RESULTCODE_SUCCESS_NAT_DETECTED)) ? 1 : 0);
if (validpacket) {
struct capwap_list* controllist;
struct capwap_list_item* item;
/* Update statistics */
ac_update_statistics();
capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_ACDESCRIPTOR_ELEMENT(&g_ac.descriptor));
capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_ACNAME_ELEMENT(&g_ac.acname));
if (binding == CAPWAP_WIRELESS_BINDING_IEEE80211) {
for (i = 0; i < joinrequest.binding.ieee80211.wtpradioinformation->count; i++) {
struct capwap_80211_wtpradioinformation_element* radio;
radio = (struct capwap_80211_wtpradioinformation_element*)capwap_array_get_item_pointer(joinrequest.binding.ieee80211.wtpradioinformation, i);
capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_80211_WTPRADIOINFORMATION_ELEMENT(radio));
}
} else {
capwap_logging_debug("Unknown capwap binding");
}
capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_ECNSUPPORT_ELEMENT(&session->dfa.ecn));
/* Get information from any local address */
controllist = capwap_list_create();
ac_get_control_information(controllist);
for (item = controllist->first; item != NULL; item = item->next) {
struct ac_session_control* sessioncontrol = (struct ac_session_control*)item->item;
if (sessioncontrol->localaddress.ss_family == AF_INET) {
struct capwap_controlipv4_element element;
memcpy(&element.address, &((struct sockaddr_in*)&sessioncontrol->localaddress)->sin_addr, sizeof(struct in_addr));
element.wtpcount = sessioncontrol->count;
capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_CONTROLIPV4_ELEMENT(&element));
} else if (sessioncontrol->localaddress.ss_family == AF_INET6) {
struct capwap_controlipv6_element element;
memcpy(&element.address, &((struct sockaddr_in6*)&sessioncontrol->localaddress)->sin6_addr, sizeof(struct in6_addr));
element.wtpcount = sessioncontrol->count;
capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_CONTROLIPV6_ELEMENT(&element));
}
}
capwap_list_free(controllist);
if (session->acctrladdress.ss_family == AF_INET) {
struct capwap_localipv4_element addr;
memcpy(&addr.address, &((struct sockaddr_in*)&session->acctrladdress)->sin_addr, sizeof(struct in_addr));
capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_LOCALIPV4_ELEMENT(&addr));
} else if (session->acctrladdress.ss_family == AF_INET6) {
struct capwap_localipv6_element addr;
memcpy(&addr.address, &((struct sockaddr_in6*)&session->acctrladdress)->sin6_addr, sizeof(struct in6_addr));
capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_LOCALIPV6_ELEMENT(&addr));
}
/* CAPWAP_CREATE_ACIPV4LIST_ELEMENT */ /* TODO */
/* CAPWAP_CREATE_ACIPV6LIST_ELEMENT */ /* TODO */
capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_TRANSPORT_ELEMENT(&session->dfa.transport));
/* CAPWAP_CREATE_IMAGEIDENTIFIER_ELEMENT */ /* TODO */
/* CAPWAP_CREATE_MAXIMUMMESSAGELENGTH_ELEMENT */ /* TODO */
/* CAPWAP_CREATE_VENDORSPECIFICPAYLOAD_ELEMENT */ /* TODO */
} else if (resultcode.code == CAPWAP_RESULTCODE_FAILURE_UNRECOGNIZED_MESSAGE_ELEMENT) {
ASSERT(returnedmessagearray != NULL);
for (i = 0; i < returnedmessagearray->count; i++) {
capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_RETURNEDMESSAGE_ELEMENT(capwap_array_get_item_pointer(returnedmessagearray, i)));
}
capwap_array_free(returnedmessagearray);
}
/* Validate packet */
if (!validpacket || !capwap_build_packet_validate(responsepacket, NULL)) {
int result;
/* Free old reference for this request */
ac_free_reference_last_response(session);
/* Send join response to WTP */
result = capwap_fragment_build_packet(responsepacket, session->responsefragmentpacket, session->mtu, session->fragmentid);
if (result >= 0) {
if (result == 1) {
session->fragmentid++;
}
/* Save remote sequence number */
session->remoteseqnumber = buildpacket->ctrlmsg.seq;
capwap_get_packet_digest((void*)packet->header, packet->packetsize, session->lastrecvpackethash);
/* Send */
for (i = 0; i < session->responsefragmentpacket->count; i++) {
struct capwap_packet* txpacket = (struct capwap_packet*)capwap_array_get_item_pointer(session->responsefragmentpacket, i);
ASSERT(txpacket != NULL);
if (!capwap_crypt_sendto(&session->ctrldtls, session->ctrlsocket.socket[session->ctrlsocket.type], txpacket->header, txpacket->packetsize, &session->acctrladdress, &session->wtpctrladdress)) {
/* Response is already created and saved. When receive a re-request, DFA autoresponse */
capwap_logging_debug("Warning: error to send join response packet");
break;
}
}
}
} else {
capwap_logging_debug("Warning: build invalid join response packet");
}
/* Free memory */
capwap_build_packet_free(responsepacket);
capwap_free_element_join_request(&joinrequest, binding);
capwap_build_packet_free(buildpacket);
/* Change state */
if (validpacket) {
ac_dfa_change_state(session, CAPWAP_POSTJOIN_STATE);
} else {
ac_dfa_change_state(session, CAPWAP_JOIN_TO_DTLS_TEARDOWN_STATE);
status = AC_DFA_NO_PACKET;
}
}
} else {
/* Join timeout */
ac_dfa_change_state(session, CAPWAP_JOIN_TO_DTLS_TEARDOWN_STATE);
status = AC_DFA_NO_PACKET;
}
return status;
}
/* */
int ac_dfa_state_postjoin(struct ac_session_t* session, struct capwap_packet* packet) {
int status = AC_DFA_ACCEPT_PACKET;
ASSERT(session != NULL);
if (packet) {
unsigned short lengthpayload;
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_CONFIGURATION_STATUS_REQUEST) {
ac_dfa_change_state(session, CAPWAP_CONFIGURE_STATE);
status = ac_dfa_state_configure(session, packet);
} else if (type == CAPWAP_IMAGE_DATA_REQUEST) {
ac_dfa_change_state(session, CAPWAP_IMAGE_DATA_STATE);
status = ac_dfa_state_imagedata(session, packet);
}
}
} else {
/* Join timeout */
ac_dfa_change_state(session, CAPWAP_JOIN_TO_DTLS_TEARDOWN_STATE);
status = AC_DFA_NO_PACKET;
}
return status;
}
/* */
int ac_dfa_state_join_to_dtlsteardown(struct ac_session_t* session, struct capwap_packet* packet) {
return ac_session_teardown_connection(session);
}