void ac_dfa_retransmition_timeout(struct capwap_timeout* timeout, unsigned long index, void* context, void* param) { struct ac_session_t* session = (struct ac_session_t*)context; if (!session->requestfragmentpacket->count) { capwap_logging_warning("Invalid retransmition request packet"); ac_session_teardown(session); } else { session->retransmitcount++; if (session->retransmitcount >= AC_MAX_RETRANSMIT) { capwap_logging_info("Retransmition request packet timeout"); /* Timeout reset state */ ac_free_reference_last_request(session); ac_session_teardown(session); } else { /* Retransmit Request */ capwap_logging_debug("Retransmition request packet"); if (!capwap_crypt_sendto_fragmentpacket(&session->dtls, session->requestfragmentpacket)) { capwap_logging_error("Error to send request packet"); } /* Update timeout */ capwap_timeout_set(session->timeout, session->idtimercontrol, AC_RETRANSMIT_INTERVAL, ac_dfa_retransmition_timeout, session, NULL); } } }
static void ac_dfa_execute(struct ac_session_t* session, struct capwap_parsed_packet* packet) { ASSERT(session != NULL); ASSERT(packet != NULL); /* Execute state */ switch (session->state) { case CAPWAP_DTLS_CONNECT_STATE: { ac_session_teardown(session); break; } case CAPWAP_JOIN_STATE: { ac_dfa_state_join(session, packet); break; } case CAPWAP_POSTJOIN_STATE: { ac_dfa_state_postjoin(session, packet); break; } case CAPWAP_IMAGE_DATA_STATE: { ac_dfa_state_imagedata(session, packet); break; } case CAPWAP_CONFIGURE_STATE: { ac_dfa_state_configure(session, packet); break; } case CAPWAP_RESET_STATE: { ac_dfa_state_reset(session, packet); break; } case CAPWAP_DATA_CHECK_STATE: { ac_dfa_state_datacheck(session, packet); break; } case CAPWAP_RUN_STATE: { ac_dfa_state_run(session, packet); break; } default: { capwap_logging_debug("Unknown AC action event: %lu", session->state); ac_session_teardown(session); break; } } }
static int ac_session_action_addwlan(struct ac_session_t* session, struct ac_notify_addwlan_t* notify) { struct capwap_header_data capwapheader; struct capwap_packet_txmng* txmngpacket; struct capwap_80211_addwlan_element addwlan; ASSERT(session->requestfragmentpacket->count == 0); /* Check if WLAN id is valid and not used */ if (!IS_VALID_RADIOID(notify->radioid) || !IS_VALID_WLANID(notify->wlanid)) { return AC_NO_ERROR; } else if (ac_wlans_get_bssid_with_wlanid(session, notify->radioid, notify->wlanid)) { return AC_NO_ERROR; } /* */ memset(&addwlan, 0, sizeof(struct capwap_80211_addwlan_element)); addwlan.radioid = notify->radioid; addwlan.wlanid = notify->wlanid; addwlan.capability = notify->capability; addwlan.qos = notify->qos; addwlan.authmode = notify->authmode; addwlan.macmode = notify->macmode; addwlan.tunnelmode = notify->tunnelmode; addwlan.suppressssid = notify->suppressssid; addwlan.ssid = (uint8_t*)notify->ssid; /* Build packet */ capwap_header_init(&capwapheader, CAPWAP_RADIOID_NONE, session->binding); txmngpacket = capwap_packet_txmng_create_ctrl_message(&capwapheader, CAPWAP_IEEE80211_WLAN_CONFIGURATION_REQUEST, session->localseqnumber, session->mtu); /* Add message element */ capwap_packet_txmng_add_message_element(txmngpacket, CAPWAP_ELEMENT_80211_ADD_WLAN, &addwlan); /* CAPWAP_ELEMENT_80211_IE */ /* CAPWAP_ELEMENT_VENDORPAYLOAD */ /* TODO */ /* WLAN Configuration Request complete, get fragment packets */ capwap_packet_txmng_get_fragment_packets(txmngpacket, session->requestfragmentpacket, session->fragmentid); if (session->requestfragmentpacket->count > 1) { session->fragmentid++; } /* Free packets manager */ capwap_packet_txmng_free(txmngpacket); /* Send WLAN Configuration Request to WTP */ if (capwap_crypt_sendto_fragmentpacket(&session->dtls, session->requestfragmentpacket)) { session->retransmitcount = 0; capwap_timeout_set(session->timeout, session->idtimercontrol, AC_RETRANSMIT_INTERVAL, ac_dfa_retransmition_timeout, session, NULL); } else { capwap_logging_debug("Warning: error to send WLAN Configuration Request packet"); ac_free_reference_last_request(session); ac_session_teardown(session); } return AC_NO_ERROR; }
void ac_dfa_state_postjoin(struct ac_session_t* session, struct capwap_parsed_packet* packet) { ASSERT(session != NULL); ASSERT(packet != NULL); if (packet->rxmngpacket->ctrlmsg.type == CAPWAP_CONFIGURATION_STATUS_REQUEST) { ac_dfa_change_state(session, CAPWAP_CONFIGURE_STATE); ac_dfa_state_configure(session, packet); } else if (packet->rxmngpacket->ctrlmsg.type == CAPWAP_IMAGE_DATA_REQUEST) { ac_dfa_change_state(session, CAPWAP_IMAGE_DATA_STATE); ac_dfa_state_imagedata(session, packet); } else { ac_session_teardown(session); } }
static int ac_session_action_station_configuration_ieee8011_delete_station(struct ac_session_t* session, struct ac_notify_station_configuration_ieee8011_delete_station* notify) { struct capwap_header_data capwapheader; struct capwap_packet_txmng* txmngpacket; struct capwap_deletestation_element deletestation; ASSERT(session->requestfragmentpacket->count == 0); /* Check if RADIO id is valid */ if (!IS_VALID_RADIOID(notify->radioid)) { return AC_NO_ERROR; } /* */ memset(&deletestation, 0, sizeof(struct capwap_deletestation_element)); deletestation.radioid = notify->radioid; deletestation.length = MACADDRESS_EUI48_LENGTH; deletestation.address = notify->address; /* Build packet */ capwap_header_init(&capwapheader, CAPWAP_RADIOID_NONE, session->binding); txmngpacket = capwap_packet_txmng_create_ctrl_message(&capwapheader, CAPWAP_STATION_CONFIGURATION_REQUEST, session->localseqnumber, session->mtu); /* Add message element */ capwap_packet_txmng_add_message_element(txmngpacket, CAPWAP_ELEMENT_DELETESTATION, &deletestation); /* CAPWAP_ELEMENT_VENDORPAYLOAD */ /* TODO */ /* Station Configuration Request complete, get fragment packets */ capwap_packet_txmng_get_fragment_packets(txmngpacket, session->requestfragmentpacket, session->fragmentid); if (session->requestfragmentpacket->count > 1) { session->fragmentid++; } /* Free packets manager */ capwap_packet_txmng_free(txmngpacket); /* Send Station Configuration Request to WTP */ if (capwap_crypt_sendto_fragmentpacket(&session->dtls, session->requestfragmentpacket)) { session->retransmitcount = 0; capwap_timeout_set(session->timeout, session->idtimercontrol, AC_RETRANSMIT_INTERVAL, ac_dfa_retransmition_timeout, session, NULL); } else { capwap_logging_debug("Warning: error to send Station Configuration Request packet"); ac_free_reference_last_request(session); ac_session_teardown(session); } return AC_NO_ERROR; }
static int ac_session_action_resetwtp(struct ac_session_t* session, struct ac_notify_reset_t* reset) { struct capwap_header_data capwapheader; struct capwap_packet_txmng* txmngpacket; struct capwap_imageidentifier_element imageidentifier; ASSERT(session->requestfragmentpacket->count == 0); /* */ imageidentifier.vendor = reset->vendor; imageidentifier.name = reset->name; /* Build packet */ capwap_header_init(&capwapheader, CAPWAP_RADIOID_NONE, session->binding); txmngpacket = capwap_packet_txmng_create_ctrl_message(&capwapheader, CAPWAP_RESET_REQUEST, session->localseqnumber, session->mtu); /* Add message element */ capwap_packet_txmng_add_message_element(txmngpacket, CAPWAP_ELEMENT_IMAGEIDENTIFIER, &imageidentifier); /* CAPWAP_ELEMENT_VENDORPAYLOAD */ /* TODO */ /* Reset request complete, get fragment packets */ capwap_packet_txmng_get_fragment_packets(txmngpacket, session->requestfragmentpacket, session->fragmentid); if (session->requestfragmentpacket->count > 1) { session->fragmentid++; } /* Free packets manager */ capwap_packet_txmng_free(txmngpacket); /* Send Reset Request to WTP */ if (capwap_crypt_sendto_fragmentpacket(&session->dtls, session->requestfragmentpacket)) { session->retransmitcount = 0; ac_dfa_change_state(session, CAPWAP_RESET_STATE); capwap_timeout_set(session->timeout, session->idtimercontrol, AC_RETRANSMIT_INTERVAL, ac_dfa_retransmition_timeout, session, NULL); } else { capwap_logging_debug("Warning: error to send Reset Request packet"); ac_free_reference_last_request(session); ac_session_teardown(session); } return AC_NO_ERROR; }
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); }
static int ac_session_action_authorizestation_response(struct ac_session_t* session, struct ac_soap_response* response, struct ac_notify_station_configuration_ieee8011_add_station* notify) { int result = -1; int ifindex = -1; uint16_t vlan = 0; struct ac_if_datachannel* datachannel; struct ac_wlan* wlan; struct json_object* jsonroot; struct json_object* jsonsection; struct json_object* jsonelement; struct capwap_header_data capwapheader; struct capwap_packet_txmng* txmngpacket; struct capwap_addstation_element addstation; struct capwap_80211_station_element station; /* Receive SOAP response with JSON result { DataChannelInterface: { Index: [int], VLAN: [int/string] }, } */ /* */ jsonroot = ac_soapclient_parse_json_response(response); if (!jsonroot) { return -1; } /* */ jsonsection = compat_json_object_object_get(jsonroot, "DataChannelInterface"); if (jsonsection && (json_object_get_type(jsonsection) == json_type_object)) { jsonelement = compat_json_object_object_get(jsonsection, "Index"); if (jsonelement && (json_object_get_type(jsonelement) == json_type_int)) { unsigned long index = (unsigned long)json_object_get_int(jsonelement); /* Retrieve interface index */ capwap_rwlock_rdlock(&g_ac.ifdatachannellock); datachannel = (struct ac_if_datachannel*)capwap_hash_search(g_ac.ifdatachannel, &index); if (datachannel) { ifindex = datachannel->ifindex; } capwap_rwlock_unlock(&g_ac.ifdatachannellock); /* Prepare request */ if (ifindex >= 0) { wlan = ac_wlans_get_bssid_with_wlanid(session, notify->radioid, notify->wlanid); if (wlan) { memset(&addstation, 0, sizeof(struct capwap_addstation_element)); addstation.radioid = notify->radioid; addstation.length = MACADDRESS_EUI48_LENGTH; addstation.address = notify->address; if (wlan->tunnelmode == CAPWAP_ADD_WLAN_TUNNELMODE_LOCAL) { jsonelement = compat_json_object_object_get(jsonsection, "VLAN"); if (jsonelement && (json_object_get_type(jsonelement) == json_type_string)) { const char* wtpvlan = json_object_get_string(jsonelement); if (wtpvlan && (strlen(wtpvlan) < CAPWAP_ADDSTATION_VLAN_MAX_LENGTH)) { addstation.vlan = (uint8_t*)wtpvlan; /* Free with jsonroot */ } } } /* */ memset(&station, 0, sizeof(struct capwap_80211_station_element)); station.radioid = notify->radioid; station.associationid = notify->associationid; memcpy(station.address, notify->address, MACADDRESS_EUI48_LENGTH); station.capabilities = notify->capabilities; station.wlanid = notify->wlanid; station.supportedratescount = notify->supportedratescount; memcpy(station.supportedrates, notify->supportedrates, station.supportedratescount); /* Build packet */ capwap_header_init(&capwapheader, CAPWAP_RADIOID_NONE, session->binding); txmngpacket = capwap_packet_txmng_create_ctrl_message(&capwapheader, CAPWAP_STATION_CONFIGURATION_REQUEST, session->localseqnumber, session->mtu); /* Add message element */ capwap_packet_txmng_add_message_element(txmngpacket, CAPWAP_ELEMENT_ADDSTATION, &addstation); capwap_packet_txmng_add_message_element(txmngpacket, CAPWAP_ELEMENT_80211_STATION, &station); /* CAPWAP_ELEMENT_VENDORPAYLOAD */ /* TODO */ /* Station Configuration Request complete, get fragment packets */ capwap_packet_txmng_get_fragment_packets(txmngpacket, session->requestfragmentpacket, session->fragmentid); if (session->requestfragmentpacket->count > 1) { session->fragmentid++; } /* Free packets manager */ capwap_packet_txmng_free(txmngpacket); /* Send Station Configuration Request to WTP */ if (capwap_crypt_sendto_fragmentpacket(&session->dtls, session->requestfragmentpacket)) { /* Retrive VLAN */ if (wlan->tunnelmode != CAPWAP_ADD_WLAN_TUNNELMODE_LOCAL) { jsonelement = compat_json_object_object_get(jsonroot, "DataChannelInterface.VLAN"); if (jsonelement && (json_object_get_type(jsonelement) == json_type_int)) { int acvlan = json_object_get_int(jsonelement); if ((acvlan > 0) && (acvlan < VLAN_MAX)) { vlan = (uint16_t)acvlan; } } } /* Authorize station also into kernel module */ if (!ac_kmod_authorize_station(&session->sessionid, addstation.address, ifindex, notify->radioid, notify->wlanid, vlan)) { result = 0; session->retransmitcount = 0; capwap_timeout_set(session->timeout, session->idtimercontrol, AC_RETRANSMIT_INTERVAL, ac_dfa_retransmition_timeout, session, NULL); } else { capwap_logging_warning("Unable to authorize station into kernel module data channel"); ac_free_reference_last_request(session); ac_session_teardown(session); } } else { capwap_logging_debug("Warning: error to send Station Configuration Request packet"); ac_free_reference_last_request(session); ac_session_teardown(session); } } } } } /* */ json_object_put(jsonroot); return result; }
void ac_dfa_teardown_timeout(struct capwap_timeout* timeout, unsigned long index, void* context, void* param) { capwap_logging_info("Session timeout, teardown"); ac_session_teardown((struct ac_session_t*)context); }
void ac_dfa_state_join(struct ac_session_t* session, struct capwap_parsed_packet* packet) { unsigned short binding; struct ac_soap_response* response; struct capwap_header_data capwapheader; struct capwap_packet_txmng* txmngpacket; struct capwap_sessionid_element* sessionid; struct capwap_wtpboarddata_element* wtpboarddata; struct capwap_resultcode_element resultcode = { .code = CAPWAP_RESULTCODE_FAILURE }; ASSERT(session != NULL); ASSERT(packet != NULL); /* Check binding */ binding = GET_WBID_HEADER(packet->rxmngpacket->header); if (ac_valid_binding(binding)) { if (packet->rxmngpacket->ctrlmsg.type == CAPWAP_JOIN_REQUEST) { /* Get sessionid and verify unique id */ sessionid = (struct capwap_sessionid_element*)capwap_get_message_element_data(packet, CAPWAP_ELEMENT_SESSIONID); if (!ac_has_sessionid(sessionid)) { char* wtpid; /* Checking macaddress for detect if WTP already connected */ wtpboarddata = (struct capwap_wtpboarddata_element*)capwap_get_message_element_data(packet, CAPWAP_ELEMENT_WTPBOARDDATA); /* Get printable WTPID */ wtpid = ac_get_printable_wtpid(wtpboarddata); if (wtpid && !ac_has_wtpid(wtpid)) { /* Request authorization of Backend for complete join */ response = ac_soap_authorizewtpsession(session, wtpid); if (response) { resultcode.code = ac_dfa_state_join_check_authorizejoin(session, response); ac_soapclient_free_response(response); } else { resultcode.code = CAPWAP_RESULTCODE_JOIN_FAILURE_UNKNOWN_SOURCE; } } else { capwap_logging_info("WTP Id %s already used in another session", wtpid); resultcode.code = CAPWAP_RESULTCODE_JOIN_FAILURE_UNKNOWN_SOURCE; } /* */ if (CAPWAP_RESULTCODE_OK(resultcode.code)) { session->wtpid = wtpid; memcpy(&session->sessionid, sessionid, sizeof(struct capwap_sessionid_element)); session->binding = binding; } else if (wtpid) { capwap_free(wtpid); } } else { char sessionname[33]; capwap_sessionid_printf(sessionid, sessionname); capwap_logging_info("Session Id %s already used in another session", sessionname); resultcode.code = CAPWAP_RESULTCODE_JOIN_FAILURE_ID_ALREADY_IN_USE; } } else { resultcode.code = CAPWAP_RESULTCODE_MSG_UNEXPECTED_INVALID_CURRENT_STATE; } } else { resultcode.code = CAPWAP_RESULTCODE_JOIN_FAILURE_BINDING_NOT_SUPPORTED; } /* Create response */ capwap_header_init(&capwapheader, CAPWAP_RADIOID_NONE, binding); txmngpacket = capwap_packet_txmng_create_ctrl_message(&capwapheader, CAPWAP_JOIN_RESPONSE, packet->rxmngpacket->ctrlmsg.seq, session->mtu); /* */ if (CAPWAP_RESULTCODE_OK(resultcode.code)) { response = ac_dfa_state_join_parsing_request(session, packet); if (response) { resultcode.code = ac_dfa_state_join_create_response(session, packet, response, txmngpacket); ac_soapclient_free_response(response); } } /* Add always result code message element */ capwap_packet_txmng_add_message_element(txmngpacket, CAPWAP_ELEMENT_RESULTCODE, &resultcode); /* Join response complete, get fragment packets */ ac_free_reference_last_response(session); capwap_packet_txmng_get_fragment_packets(txmngpacket, session->responsefragmentpacket, session->fragmentid); if (session->responsefragmentpacket->count > 1) { session->fragmentid++; } /* Free packets manager */ capwap_packet_txmng_free(txmngpacket); /* Save remote sequence number */ session->remotetype = packet->rxmngpacket->ctrlmsg.type; session->remoteseqnumber = packet->rxmngpacket->ctrlmsg.seq; /* Send Join response to WTP */ if (capwap_crypt_sendto_fragmentpacket(&session->dtls, session->responsefragmentpacket)) { if (CAPWAP_RESULTCODE_OK(resultcode.code)) { ac_dfa_change_state(session, CAPWAP_POSTJOIN_STATE); capwap_timeout_set(session->timeout, session->idtimercontrol, AC_JOIN_INTERVAL, ac_dfa_teardown_timeout, session, NULL); } else { ac_session_teardown(session); } } else { /* Error to send packets */ capwap_logging_debug("Warning: error to send join response packet"); ac_session_teardown(session); } }
void ac_dfa_state_imagedata(struct ac_session_t* session, struct capwap_parsed_packet* packet) { /* TODO */ ac_session_teardown(session); }