/* Teardown connection */ void ac_session_teardown(struct ac_session_t* session) { ASSERT(session != NULL); /* Remove session from list */ capwap_rwlock_wrlock(&g_ac.sessionslock); capwap_itemlist_remove(g_ac.sessions, session->itemlist); capwap_rwlock_unlock(&g_ac.sessionslock); /* Remove all pending packets */ while (session->packets->count > 0) { capwap_itemlist_free(capwap_itemlist_remove_head(session->packets)); } /* Close DTSL Control */ if (session->dtls.enable) { capwap_crypt_close(&session->dtls); } /* Cancel all notify event */ if (session->notifyevent->first) { char buffer[5]; struct ac_soap_response* response; capwap_itoa(SOAP_EVENT_STATUS_CANCEL, buffer); while (session->notifyevent->first != NULL) { struct ac_session_notify_event_t* notify = (struct ac_session_notify_event_t*)session->notifyevent->first->item; /* Cancel event */ response = ac_soap_updatebackendevent(session, notify->idevent, buffer); if (response) { ac_soapclient_free_response(response); } /* Remove notify event */ capwap_itemlist_free(capwap_itemlist_remove(session->notifyevent, session->notifyevent->first)); } } /* Remove timer */ if (session->idtimercontrol != CAPWAP_TIMEOUT_INDEX_NO_SET) { capwap_timeout_unset(session->timeout, session->idtimercontrol); session->idtimercontrol = CAPWAP_TIMEOUT_INDEX_NO_SET; } if (session->idtimerkeepalivedead != CAPWAP_TIMEOUT_INDEX_NO_SET) { capwap_timeout_unset(session->timeout, session->idtimerkeepalivedead); session->idtimerkeepalivedead = CAPWAP_TIMEOUT_INDEX_NO_SET; } /* */ ac_dfa_change_state(session, CAPWAP_DTLS_TEARDOWN_STATE); }
void capwap_list_flush(struct capwap_list* list) { struct capwap_list_item* item; struct capwap_list_item* next; ASSERT(list != NULL); item = list->first; while (item) { next = item->next; capwap_itemlist_free(item); item = next; } list->first = NULL; list->last = NULL; list->count = 0; }
void capwap_timeout_unset(struct capwap_timeout* timeout, unsigned long index) { struct capwap_list_item* itemlist; ASSERT(timeout != NULL); if (index != CAPWAP_TIMEOUT_INDEX_NO_SET) { itemlist = (struct capwap_list_item*)capwap_hash_search(timeout->itemsreference, &index); if (itemlist) { #ifdef CAPWAP_TIMEOUT_LOGGING_DEBUG log_printf(LOG_DEBUG, "Unset timeout: %lu", index); #endif /* */ capwap_hash_delete(timeout->itemsreference, &index); capwap_itemlist_free(capwap_itemlist_remove(timeout->itemstimeout, itemlist)); } } }
static void ac_session_msgqueue_parsing_item(struct ac_session_msgqueue_item_t* item) { switch (item->message) { case AC_MESSAGE_QUEUE_CLOSE_THREAD: { struct capwap_list_item* search = g_ac.sessionsthread->first; while (search != NULL) { struct ac_session_thread_t* sessionthread = (struct ac_session_thread_t*)search->item; ASSERT(sessionthread != NULL); if (sessionthread->threadid == item->message_close_thread.threadid) { void* dummy; /* Clean thread resource */ pthread_join(sessionthread->threadid, &dummy); capwap_itemlist_free(capwap_itemlist_remove(g_ac.sessionsthread, search)); break; } /* */ search = search->next; } break; } case AC_MESSAGE_QUEUE_UPDATE_CONFIGURATION: { ac_update_configuration(item->message_configuration.jsonroot); /* Free JSON */ json_object_put(item->message_configuration.jsonroot); break; } case AC_MESSAGE_QUEUE_CLOSE_ALLSESSIONS: { ac_close_sessions(); /* Close all sessions */ break; } default: { capwap_logging_debug("Unknown message queue item: %lu", item->message); break; } } }
unsigned long capwap_timeout_hasexpired(struct capwap_timeout* timeout) { long delta; struct capwap_timeout_item* item; struct capwap_list_item* itemlist; unsigned long index; capwap_timeout_expire callback; void* context; void* param; ASSERT(timeout != NULL); /* */ delta = capwap_timeout_getcoming(timeout); if ((delta > 0) || (delta == CAPWAP_TIMEOUT_INFINITE)) { return 0; } /* */ itemlist = capwap_itemlist_remove_head(timeout->itemstimeout); item = (struct capwap_timeout_item*)itemlist->item; #ifdef CAPWAP_TIMEOUT_LOGGING_DEBUG log_printf(LOG_DEBUG, "Expired timeout: %lu", item->index); #endif /* Cache callback before release timeout timer */ index = item->index; callback = item->callback; context = item->context; param = item->param; /* Free memory */ capwap_hash_delete(timeout->itemsreference, &index); capwap_itemlist_free(itemlist); /* */ if (callback) { callback(timeout, index, context, param); } return index; }
/* Change WTP state machine */ void ac_dfa_change_state(struct ac_session_t* session, int state) { struct capwap_list_item* search; ASSERT(session != NULL); if (state != session->state) { #ifdef DEBUG char sessionname[33]; capwap_sessionid_printf(&session->sessionid, sessionname); capwap_logging_debug("Session AC %s change state from %s to %s", sessionname, capwap_dfa_getname(session->state), capwap_dfa_getname(state)); #endif session->state = state; /* Search into notify event */ search = session->notifyevent->first; while (search != NULL) { struct ac_session_notify_event_t* notify = (struct ac_session_notify_event_t*)search->item; if ((notify->action == NOTIFY_ACTION_CHANGE_STATE) && (notify->session_state == state)) { char buffer[4]; struct ac_soap_response* response; /* */ response = ac_soap_updatebackendevent(session, notify->idevent, capwap_itoa(SOAP_EVENT_STATUS_COMPLETE, buffer)); if (response) { ac_soapclient_free_response(response); } /* Remove notify event */ capwap_itemlist_free(capwap_itemlist_remove(session->notifyevent, search)); break; } search = search->next; } } }
static void ac_session_run(struct ac_session_t* session) { int res; int check; int length; struct capwap_list_item* search; char buffer[CAPWAP_MAX_PACKET_SIZE]; ASSERT(session != NULL); /* Configure DFA */ if (g_ac.enabledtls) { if (!ac_dtls_setup(session)) { ac_session_teardown(session); /* Teardown connection */ } } else { /* Wait Join request */ ac_dfa_change_state(session, CAPWAP_JOIN_STATE); capwap_timeout_set(session->timeout, session->idtimercontrol, AC_JOIN_INTERVAL, ac_dfa_teardown_timeout, session, NULL); } while (session->state != CAPWAP_DTLS_TEARDOWN_STATE) { /* Get packet */ length = ac_network_read(session, buffer, sizeof(buffer)); if (length < 0) { if ((length == CAPWAP_ERROR_SHUTDOWN) || (length == CAPWAP_ERROR_CLOSE)) { ac_session_teardown(session); } } else if (length > 0) { /* Check generic capwap packet */ check = capwap_sanity_check(CAPWAP_UNDEF_STATE, buffer, length, 0); if (check == CAPWAP_PLAIN_PACKET) { struct capwap_parsed_packet packet; /* Defragment management */ if (!session->rxmngpacket) { session->rxmngpacket = capwap_packet_rxmng_create_message(); } /* If request, defragmentation packet */ check = capwap_packet_rxmng_add_recv_packet(session->rxmngpacket, buffer, length); if (check == CAPWAP_RECEIVE_COMPLETE_PACKET) { /* Receive all fragment */ if (capwap_is_request_type(session->rxmngpacket->ctrlmsg.type) && (session->remotetype == session->rxmngpacket->ctrlmsg.type) && (session->remoteseqnumber == session->rxmngpacket->ctrlmsg.seq)) { /* Retransmit response */ if (!capwap_crypt_sendto_fragmentpacket(&session->dtls, session->responsefragmentpacket)) { capwap_logging_error("Error to resend response packet"); } else { capwap_logging_debug("Retrasmitted control packet"); } } else { /* Check message type */ res = capwap_check_message_type(session->rxmngpacket); if (res == VALID_MESSAGE_TYPE) { res = capwap_parsing_packet(session->rxmngpacket, &packet); if (res == PARSING_COMPLETE) { int hasrequest = capwap_is_request_type(session->rxmngpacket->ctrlmsg.type); /* Validate packet */ if (!capwap_validate_parsed_packet(&packet, NULL)) { /* Search into notify event */ search = session->notifyevent->first; while (search != NULL) { struct ac_session_notify_event_t* notify = (struct ac_session_notify_event_t*)search->item; if (hasrequest && (notify->action == NOTIFY_ACTION_RECEIVE_REQUEST_CONTROLMESSAGE)) { char buffer[4]; struct ac_soap_response* response; /* */ response = ac_soap_updatebackendevent(session, notify->idevent, capwap_itoa(SOAP_EVENT_STATUS_COMPLETE, buffer)); if (response) { ac_soapclient_free_response(response); } /* Remove notify event */ capwap_itemlist_free(capwap_itemlist_remove(session->notifyevent, search)); break; } else if (!hasrequest && (notify->action == NOTIFY_ACTION_RECEIVE_RESPONSE_CONTROLMESSAGE)) { char buffer[4]; struct ac_soap_response* response; struct capwap_resultcode_element* resultcode; /* Check the success of the Request */ resultcode = (struct capwap_resultcode_element*)capwap_get_message_element_data(&packet, CAPWAP_ELEMENT_RESULTCODE); response = ac_soap_updatebackendevent(session, notify->idevent, capwap_itoa(((!resultcode || CAPWAP_RESULTCODE_OK(resultcode->code)) ? SOAP_EVENT_STATUS_COMPLETE : SOAP_EVENT_STATUS_GENERIC_ERROR), buffer)); if (response) { ac_soapclient_free_response(response); } /* Remove notify event */ capwap_itemlist_free(capwap_itemlist_remove(session->notifyevent, search)); break; } search = search->next; } /* */ ac_dfa_execute(session, &packet); } else { capwap_logging_debug("Failed validation parsed control packet"); if (capwap_is_request_type(session->rxmngpacket->ctrlmsg.type)) { capwap_logging_warning("Missing Mandatory Message Element, send Response Packet with error"); ac_send_invalid_request(session, CAPWAP_RESULTCODE_FAILURE_MISSING_MANDATORY_MSG_ELEMENT); } } } else { capwap_logging_debug("Failed parsing packet"); if ((res == UNRECOGNIZED_MESSAGE_ELEMENT) && capwap_is_request_type(session->rxmngpacket->ctrlmsg.type)) { capwap_logging_warning("Unrecognized Message Element, send Response Packet with error"); ac_send_invalid_request(session, CAPWAP_RESULTCODE_FAILURE_UNRECOGNIZED_MESSAGE_ELEMENT); /* TODO: add the unrecognized message element */ } } } else { capwap_logging_debug("Invalid message type"); if (res == INVALID_REQUEST_MESSAGE_TYPE) { capwap_logging_warning("Unexpected Unrecognized Request, send Response Packet with error"); ac_send_invalid_request(session, CAPWAP_RESULTCODE_MSG_UNEXPECTED_UNRECOGNIZED_REQUEST); } } } /* Free memory */ capwap_free_parsed_packet(&packet); if (session->rxmngpacket) { capwap_packet_rxmng_free(session->rxmngpacket); session->rxmngpacket = NULL; } } else if (check != CAPWAP_REQUEST_MORE_FRAGMENT) { /* Discard fragments */ if (session->rxmngpacket) { capwap_packet_rxmng_free(session->rxmngpacket); session->rxmngpacket = NULL; } } } } } /* Wait teardown timeout before kill session */ capwap_timeout_wait(AC_DTLS_SESSION_DELETE_INTERVAL); ac_dfa_state_teardown(session); /* Release reference session */ ac_session_destroy(session); }
/* Release reference of session */ static void ac_session_destroy(struct ac_session_t* session) { #ifdef DEBUG char sessionname[33]; #endif ASSERT(session != NULL); #ifdef DEBUG capwap_sessionid_printf(&session->sessionid, sessionname); capwap_logging_debug("Release Session AC %s", sessionname); #endif /* Release last reference */ capwap_lock_enter(&session->sessionlock); session->count--; /* Terminate SOAP request pending */ if (session->soaprequest) { ac_soapclient_shutdown_request(session->soaprequest); } /* Check if all reference is release */ while (session->count > 0) { #ifdef DEBUG capwap_logging_debug("Wait for release Session AC %s (count=%d)", sessionname, session->count); #endif /* */ capwap_event_reset(&session->changereference); capwap_lock_exit(&session->sessionlock); /* Wait */ capwap_event_wait(&session->changereference); capwap_lock_enter(&session->sessionlock); } capwap_lock_exit(&session->sessionlock); /* Close data channel */ ac_kmod_delete_datasession(&session->sessionid); /* Free DTSL Control */ capwap_crypt_freesession(&session->dtls); /* Free resource */ while (session->packets->count > 0) { capwap_itemlist_free(capwap_itemlist_remove_head(session->packets)); } /* Free WLANS */ ac_wlans_destroy(session); /* */ capwap_event_destroy(&session->changereference); capwap_event_destroy(&session->waitpacket); capwap_lock_destroy(&session->sessionlock); capwap_list_free(session->action); capwap_list_free(session->packets); /* Free fragments packet */ if (session->rxmngpacket) { capwap_packet_rxmng_free(session->rxmngpacket); } capwap_list_free(session->requestfragmentpacket); capwap_list_free(session->responsefragmentpacket); capwap_list_free(session->notifyevent); capwap_timeout_free(session->timeout); /* Free DFA resource */ capwap_array_free(session->dfa.acipv4list.addresses); capwap_array_free(session->dfa.acipv6list.addresses); if (session->wtpid) { capwap_free(session->wtpid); } /* Free item */ capwap_itemlist_free(session->itemlist); }
static int ac_network_read(struct ac_session_t* session, void* buffer, int length) { int result = 0; long waittimeout; ASSERT(session != NULL); ASSERT(buffer != NULL); ASSERT(length > 0); for (;;) { capwap_lock_enter(&session->sessionlock); if (!session->running) { capwap_lock_exit(&session->sessionlock); return CAPWAP_ERROR_CLOSE; } else if (!session->requestfragmentpacket->count && (session->action->count > 0)) { struct capwap_list_item* itemaction; itemaction = capwap_itemlist_remove_head(session->action); capwap_lock_exit(&session->sessionlock); /* */ result = ac_session_action_execute(session, (struct ac_session_action*)itemaction->item); /* Free packet */ capwap_itemlist_free(itemaction); return result; } else if (session->packets->count > 0) { struct capwap_list_item* itempacket; /* Get packet */ itempacket = capwap_itemlist_remove_head(session->packets); capwap_lock_exit(&session->sessionlock); if (itempacket) { struct ac_packet* packet = (struct ac_packet*)itempacket->item; long packetlength = itempacket->itemsize - sizeof(struct ac_packet); if (!packet->plainbuffer && session->dtls.enable) { int oldaction = session->dtls.action; /* Decrypt packet */ result = capwap_decrypt_packet(&session->dtls, packet->buffer, packetlength, buffer, length); if (result == CAPWAP_ERROR_AGAIN) { /* Check is handshake complete */ if ((oldaction == CAPWAP_DTLS_ACTION_HANDSHAKE) && (session->dtls.action == CAPWAP_DTLS_ACTION_DATA)) { if (session->state == CAPWAP_DTLS_CONNECT_STATE) { ac_dfa_change_state(session, CAPWAP_JOIN_STATE); capwap_timeout_set(session->timeout, session->idtimercontrol, AC_JOIN_INTERVAL, ac_dfa_teardown_timeout, session, NULL); } } } } else { if (packetlength <= length) { memcpy(buffer, packet->buffer, packetlength); result = packetlength; } } /* Free packet */ capwap_itemlist_free(itempacket); } return result; } capwap_lock_exit(&session->sessionlock); /* Get timeout */ waittimeout = capwap_timeout_getcoming(session->timeout); if (!waittimeout) { capwap_timeout_hasexpired(session->timeout); return AC_ERROR_TIMEOUT; } /* Wait packet */ capwap_event_wait_timeout(&session->waitpacket, waittimeout); } return 0; }
static void ac_discovery_run(void) { int sizedata; struct capwap_list_item* itempacket; struct ac_discovery_packet* acpacket; struct capwap_parsed_packet packet; struct capwap_packet_rxmng* rxmngpacket; while (!g_ac_discovery.endthread) { /* Get packet */ capwap_lock_enter(&g_ac_discovery.packetslock); itempacket = NULL; if (g_ac_discovery.packets->count > 0) { itempacket = capwap_itemlist_remove_head(g_ac_discovery.packets); } capwap_lock_exit(&g_ac_discovery.packetslock); if (!itempacket) { /* Wait packet with timeout*/ if (!capwap_event_wait_timeout(&g_ac_discovery.waitpacket, AC_DISCOVERY_CLEANUP_TIMEOUT)) { ac_discovery_cleanup(); } continue; } /* */ acpacket = (struct ac_discovery_packet*)itempacket->item; sizedata = itempacket->itemsize - sizeof(struct ac_discovery_packet); /* Accept only discovery request don't fragment */ rxmngpacket = capwap_packet_rxmng_create_message(); if (capwap_packet_rxmng_add_recv_packet(rxmngpacket, acpacket->data, sizedata) == CAPWAP_RECEIVE_COMPLETE_PACKET) { /* Validate message */ if (capwap_check_message_type(rxmngpacket) == VALID_MESSAGE_TYPE) { /* Parsing packet */ if (capwap_parsing_packet(rxmngpacket, &packet) == PARSING_COMPLETE) { /* Validate packet */ if (!capwap_validate_parsed_packet(&packet, NULL)) { struct capwap_packet_txmng* txmngpacket; /* */ capwap_logging_debug("Receive discovery request packet"); /* Creare discovery response */ txmngpacket = ac_create_discovery_response(&packet); if (txmngpacket) { struct capwap_list* responsefragmentpacket; /* Discovery response complete, get fragment packets */ responsefragmentpacket = capwap_list_create(); capwap_packet_txmng_get_fragment_packets(txmngpacket, responsefragmentpacket, g_ac_discovery.fragmentid); if (responsefragmentpacket->count > 1) { g_ac_discovery.fragmentid++; } /* Free packets manager */ capwap_packet_txmng_free(txmngpacket); /* Send discovery response to WTP */ if (!capwap_sendto_fragmentpacket(acpacket->sendsock, responsefragmentpacket, &acpacket->sender)) { capwap_logging_debug("Warning: error to send discovery response packet"); } /* Don't buffering a packets sent */ capwap_list_free(responsefragmentpacket); } } } /* Free resource */ capwap_free_parsed_packet(&packet); } } /* Free resource */ capwap_packet_rxmng_free(rxmngpacket); /* Free packet */ capwap_itemlist_free(itempacket); } }