/** @file wlan_exp_node_ap.c * @brief Access Point WLAN Experiment * * This contains code for the 802.11 Access Point's WLAN experiment interface. * * @copyright Copyright 2013-2019, Mango Communications. All rights reserved. * Distributed under the Mango Communications Reference Design License * See LICENSE.txt included in the design archive or * at http://mangocomm.com/802.11/license * * This file is part of the Mango 802.11 Reference Design (https://mangocomm.com/802.11) */ /***************************** Include Files *********************************/ #include "wlan_mac_high_sw_config.h" #if WLAN_SW_CONFIG_ENABLE_WLAN_EXP #include "wlan_platform_common.h" #include "wlan_platform_high.h" #include "wlan_exp_node.h" #include "wlan_exp_node_ap.h" #include "wlan_mac_common.h" #include "wlan_mac_entries.h" #include "wlan_mac_ltg.h" #include "wlan_mac_addr_filter.h" #include "wlan_mac_event_log.h" #include "wlan_mac_high.h" #include "wlan_mac_ap.h" #include "wlan_mac_network_info.h" #include "wlan_mac_station_info.h" #include "wlan_mac_queue.h" #include "xil_io.h" // Check that there is enough memory to support MAX_NUM_ASSOC CASSERT( (MAX_NUM_ASSOC ) <= STATION_INFO_DL_ENTRY_MEM_NUM, insufficient_WLAN_OPTIONS_AUX_SIZE_KB_STATION_INFO_for_max_associations ) /*************************** Constant Definitions ****************************/ /*********************** Global Variable Definitions *************************/ extern network_info_t* active_network_info; extern function_ptr_t wlan_exp_purge_all_wireless_tx_queue_callback; extern u8 gl_dtim_mcast_buffer_enable; extern u32 max_queue_size; /*************************** Variable Definitions ****************************/ /*************************** Functions Prototypes ****************************/ /******************************** Functions **********************************/ /*****************************************************************************/ /** * Process Node Commands * * This function is part of the Ethernet processing system and will process the * various node related commands. * * @return int - Status of the command: * NO_RESP_SENT - No response has been sent * RESP_SENT - A response has been sent * * @note See on-line documentation for more information about the Ethernet * packet structure: www.warpproject.org * *****************************************************************************/ int process_wlan_exp_app_cmd(cmd_resp_hdr_t* cmd_hdr, eth_tx_queue_buffer_t* eth_tx_queue_buffer) { // // IMPORTANT ENDIAN NOTES: // - command // - header - Already endian swapped by the framework (safe to access directly) // - args - Must be endian swapped as necessary by code (framework does not know the contents of the command) // - response // - header - Will be endian swapped by the framework (safe to write directly) // - args - Must be endian swapped as necessary by code (framework does not know the contents of the response) // // Standard variables u32 resp_sent = NO_RESP_SENT; u32 cmd_id = CMD_TO_CMDID(cmd_hdr->cmd); // Segment 0 length includes a fully formed command response header // because one was created with default values suitable for a responseless // acknowledgment. cmd_resp_hdr_t* resp_hdr = (cmd_resp_hdr_t*)(eth_tx_queue_buffer->seg0 + eth_tx_queue_buffer->seg0_len - sizeof(cmd_resp_hdr_t)); u32* cmd_args_32 = (u32*)((u8*)cmd_hdr + sizeof(cmd_resp_hdr_t)); // // NOTE: Response header cmd, length, and num_args fields have already been initialized. // switch(cmd_id){ //----------------------------------------------------------------------------- // WLAN Exp Node Commands that must be implemented in child classes //----------------------------------------------------------------------------- //--------------------------------------------------------------------- case CMDID_NODE_RESET_STATE: { // NODE_RESET_STATE Packet Format: // - cmd_args_32[0] - Flags // [0] - NODE_RESET_LOG // [1] - NODE_RESET_TXRX_COUNTS // [2] - NODE_RESET_LTG // [3] - NODE_RESET_TX_DATA_QUEUE // [4] - NODE_RESET_ASSOCIATIONS // [5] - NODE_RESET_BSS_INFO // interrupt_state_t prev_interrupt_state; u32 status = CMD_PARAM_SUCCESS; u32 flags = Xil_Ntohl(cmd_args_32[0]); // Disable interrupts so no packets interrupt the reset prev_interrupt_state = wlan_platform_intc_stop(); #if WLAN_SW_CONFIG_ENABLE_LOGGING // Configure the LOG based on the flag bits if (flags & CMD_PARAM_NODE_RESET_FLAG_LOG) { wlan_exp_printf(WLAN_EXP_PRINT_INFO, print_type_event_log, "Reset log\n"); event_log_reset(); } #endif //WLAN_SW_CONFIG_ENABLE_LOGGING if (flags & CMD_PARAM_NODE_RESET_FLAG_TXRX_COUNTS) { wlan_exp_printf(WLAN_EXP_PRINT_INFO, print_type_counts, "Reseting Counts\n"); #if WLAN_SW_CONFIG_ENABLE_TXRX_COUNTS txrx_counts_zero_all(); #endif //WLAN_SW_CONFIG_ENABLE_TXRX_COUNTS } #if WLAN_SW_CONFIG_ENABLE_LTG if (flags & CMD_PARAM_NODE_RESET_FLAG_LTG) { status = ltg_sched_remove(LTG_REMOVE_ALL); if (status != 0) { wlan_exp_printf(WLAN_EXP_PRINT_ERROR, print_type_ltg, "Failed to remove all LTGs\n"); status = CMD_PARAM_ERROR + CMD_PARAM_LTG_ERROR; } else { wlan_exp_printf(WLAN_EXP_PRINT_INFO, print_type_ltg, "Removing All LTGs\n"); } } #endif //WLAN_SW_CONFIG_ENABLE_LTG if (flags & CMD_PARAM_NODE_RESET_FLAG_TX_DATA_QUEUE) { wlan_exp_printf(WLAN_EXP_PRINT_INFO, print_type_queue, "Purging all data transmit queues\n"); wlan_exp_purge_all_wireless_tx_queue_callback(); } if (flags & CMD_PARAM_NODE_RESET_FLAG_BSS) { wlan_exp_printf(WLAN_EXP_PRINT_INFO, print_type_node, "Resetting BSS \n"); // Note: Interrupts are currently disabled // Deauthenticate all stations // - This will send deauthentication packets to each Station deauthenticate_all_stations(); // Set "active_bss_info" to NULL configure_bss(NULL,0); } if (flags & CMD_PARAM_NODE_RESET_FLAG_NETWORK_LIST) { wlan_exp_printf(WLAN_EXP_PRINT_INFO, print_type_node, "Resetting Network List\n"); wlan_mac_high_reset_network_list(); } // Call MAC specific reset with the flags // Re-enable interrupts wlan_platform_intc_set_state(prev_interrupt_state); // Send response of success wlan_exp_add_u32_resp_arg(eth_tx_queue_buffer, resp_hdr, status); } break; //--------------------------------------------------------------------- case CMDID_NODE_DISASSOCIATE: { // Disassociate device from node // // Message format: // cmd_args_32[0:1] MAC Address (All 0xFF means all station info) // // Response format: // resp_args_32[0] Status // u8 mac_addr[MAC_ADDR_LEN]; station_info_entry_t* curr_entry; station_info_t* curr_station_info; interrupt_state_t prev_interrupt_state; u32 status = CMD_PARAM_SUCCESS; wlan_exp_printf(WLAN_EXP_PRINT_INFO, print_type_node, "Disassociate\n"); // Get MAC Address wlan_exp_get_mac_addr(&((u32 *)cmd_args_32)[0], &mac_addr[0]); // Depending on the mac_addr argument to this command, there are two actions that need to take place: // 1) If the mac_addr is all zeros (00-00-00-00-00-00), this is a magic number that indicates // that all currently-associated stations must be removed from the network // 2) If the mac_addr matches the MAC address of a currently associated station, it should be removed // from the network prev_interrupt_state = wlan_platform_intc_stop(); // This function is safe to call even if get_network_member_list() is NULL. // In that case, it will search through the flat list of station_info_t managed // by the High Framework. So, a non-NULL curr_entry does not necessarily mean // an associated station with this address has been found. For that to be the // case, both curr_entry and get_network_member_list() must be non-NULL. curr_entry = station_info_find_by_addr( mac_addr, get_network_member_list() ); if(wlan_addr_eq(mac_addr, zero_addr)){ // Remove all currently associated stations deauthenticate_all_stations(); wlan_exp_printf(WLAN_EXP_PRINT_INFO, print_type_node, "Disassociated node: "); wlan_exp_print_mac_address(WLAN_EXP_PRINT_INFO, &mac_addr[0]); wlan_exp_printf(WLAN_EXP_PRINT_INFO, NULL, "\n"); } else if(curr_entry && get_network_member_list()) { // There is currently an active network and we found this MAC address in the member list. // We will remove this curr_station_info = (curr_entry->data); deauthenticate_station(curr_station_info); // Set return parameters and print info to console wlan_exp_printf(WLAN_EXP_PRINT_INFO, print_type_node, "Disassociated node: "); wlan_exp_print_mac_address(WLAN_EXP_PRINT_INFO, &mac_addr[0]); wlan_exp_printf(WLAN_EXP_PRINT_INFO, NULL, "\n"); } else { // Either we have no active network or there is no node in the member list matching this MAC address wlan_exp_printf(WLAN_EXP_PRINT_INFO, print_type_node, "Could not find specified node: "); wlan_exp_print_mac_address(WLAN_EXP_PRINT_INFO, &mac_addr[0]); wlan_exp_printf(WLAN_EXP_PRINT_INFO, NULL, "\n"); status = CMD_PARAM_ERROR; } wlan_platform_intc_set_state(prev_interrupt_state); // Send response wlan_exp_add_u32_resp_arg(eth_tx_queue_buffer, resp_hdr, status); } break; //----------------------------------------------------------------------------- // AP Specific Commands //----------------------------------------------------------------------------- //--------------------------------------------------------------------- case CMDID_NODE_AP_CONFIG: { // Set AP configuration flags // // Message format: // cmd_args_32[0] Flags // [ 0] - NODE_AP_CONFIG_FLAG_DTIM_MULTICAST_BUFFERING // cmd_args_32[1] Mask for flags // // Response format: // resp_args_32[0] Status (CMD_PARAM_SUCCESS/CMD_PARAM_ERROR) // u32 status = CMD_PARAM_SUCCESS; u32 flags = Xil_Ntohl(cmd_args_32[0]); u32 mask = Xil_Ntohl(cmd_args_32[1]); wlan_exp_printf(WLAN_EXP_PRINT_INFO, print_type_node, "AP: Config flags = 0x%08x mask = 0x%08x\n", flags, mask); // Configure based on the flag bit / mask if (mask & CMD_PARAM_NODE_AP_CONFIG_FLAG_DTIM_MULTICAST_BUFFER) { if(flags & CMD_PARAM_NODE_AP_CONFIG_FLAG_DTIM_MULTICAST_BUFFER){ gl_dtim_mcast_buffer_enable = 1; wlan_mac_high_enable_mcast_buffering(gl_dtim_mcast_buffer_enable); } else { gl_dtim_mcast_buffer_enable = 0; wlan_mac_high_enable_mcast_buffering(gl_dtim_mcast_buffer_enable); } } // Send response of status wlan_exp_add_u32_resp_arg(eth_tx_queue_buffer, resp_hdr, status); } break; //--------------------------------------------------------------------- case CMDID_NODE_AP_SET_AUTHENTICATION_ADDR_FILTER: { // Allow / Disallow wireless authentications // // Message format: // cmd_args_32[0] Command: // - Write (CMD_PARAM_WRITE_VAL) // cmd_args_32[1] Number of address filters // cmd_args_32[2:N] [Compare address (u64), (Mask (u64)] // // Response format: // resp_args_32[0] Status // u32 i; u8 mac_addr[MAC_ADDR_LEN]; u8 mask[MAC_ADDR_LEN]; interrupt_state_t prev_interrupt_state; u32 status = CMD_PARAM_SUCCESS; u32 msg_cmd = Xil_Ntohl(cmd_args_32[0]); u32 num_ranges = Xil_Ntohl(cmd_args_32[1]); switch (msg_cmd) { case CMD_PARAM_WRITE_VAL: // Need to disable interrupts during this operation so the filter does not have any holes prev_interrupt_state = wlan_platform_intc_stop(); // Reset the current address filter wlan_mac_addr_filter_reset(); // Add all the address ranges to the filter for (i = 0; i < num_ranges; i++) { // Extract the address and the mask wlan_exp_get_mac_addr(&((u32 *)cmd_args_32)[2 + (4*i)], &mac_addr[0]); wlan_exp_get_mac_addr(&((u32 *)cmd_args_32)[4 + (4*i)], &mask[0]); wlan_exp_printf(WLAN_EXP_PRINT_INFO, print_type_node, "Adding Address filter: ("); wlan_exp_print_mac_address(WLAN_EXP_PRINT_INFO, mac_addr); wlan_exp_printf(WLAN_EXP_PRINT_INFO, NULL, ", "); wlan_exp_print_mac_address(WLAN_EXP_PRINT_INFO, mask); wlan_exp_printf(WLAN_EXP_PRINT_INFO, NULL, "\n"); if (wlan_mac_addr_filter_add(mask, mac_addr) == -1) { status = CMD_PARAM_ERROR; } } wlan_platform_intc_set_state(prev_interrupt_state); break; default: wlan_exp_printf(WLAN_EXP_PRINT_ERROR, print_type_node, "Unknown command for 0x%6x: %d\n", cmd_id, msg_cmd); status = CMD_PARAM_ERROR; break; } // Send response wlan_exp_add_u32_resp_arg(eth_tx_queue_buffer, resp_hdr, status); } break; //----------------------------------------------------------------------------- // Association Commands //----------------------------------------------------------------------------- //--------------------------------------------------------------------- case CMDID_NODE_ASSOCIATE: { // Associate with the device // // Message format: // cmd_args_32[0] Association flags // CMD_PARAM_AP_ASSOCIATE_FLAG_ALLOW_TIMEOUT // CMD_PARAM_AP_ASSOCIATE_FLAG_STATION_INFO_DO_NOT_REMOVE // cmd_args_32[1] Association flags mask // cmd_args_32[2:3] Association MAC Address // // Response format: // resp_args_32[0] Status // u32 flags; u32 mask; u8 mac_addr[MAC_ADDR_LEN]; interrupt_state_t prev_interrupt_state; u32 status = CMD_PARAM_SUCCESS; station_info_entry_t* station_info_entry = NULL; station_info_t* curr_station_info = NULL; u8 station_flags = 0; u16 station_capabilities = 0; u32 error_reason = 0; wlan_exp_printf(WLAN_EXP_PRINT_INFO, print_type_node, "AP: Associate\n"); // Get MAC Address wlan_exp_get_mac_addr(&((u32 *)cmd_args_32)[2], &mac_addr[0]); if(active_network_info != NULL){ station_info_entry = station_info_find_by_addr(mac_addr, &(active_network_info->members)); } if ((active_network_info != NULL) && ((station_info_entry != NULL) || (active_network_info->members.length < MAX_NUM_ASSOC)) ) { // Get flags flags = Xil_Ntohl(cmd_args_32[0]); mask = Xil_Ntohl(cmd_args_32[1]); wlan_exp_printf(WLAN_EXP_PRINT_INFO, print_type_node, "Associate flags = 0x%08x mask = 0x%08x\n", flags, mask); // Disable interrupts to avoid race conditions between wlan_exp and wireless Tx/Rx when // modifying the AP's association table prev_interrupt_state = wlan_platform_intc_stop(); // Add association curr_station_info = network_info_add_member(active_network_info, &mac_addr[0], max_queue_size); // Set return parameters and print info to console if (curr_station_info != NULL) { // Update the new station_info flags field // Only override the defaults set by the framework add_station_info if the wlan_exp command explicitly included a flag station_flags = curr_station_info->flags; // Raise the KEEP flag to prevent the MAC High Framework from removing the struct station_flags |= STATION_INFO_FLAG_KEEP; if (mask & CMD_PARAM_AP_ASSOCIATE_FLAG_DISABLE_INACTIVITY_TIMEOUT) { if (flags & CMD_PARAM_AP_ASSOCIATE_FLAG_DISABLE_INACTIVITY_TIMEOUT) { station_flags |= STATION_INFO_FLAG_DISABLE_ASSOC_CHECK; } else { station_flags &= ~STATION_INFO_FLAG_DISABLE_ASSOC_CHECK; } } if (mask & CMD_PARAM_AP_ASSOCIATE_FLAG_HT_CAPABLE_STA) { if (flags & CMD_PARAM_AP_ASSOCIATE_FLAG_HT_CAPABLE_STA) { station_capabilities |= STATION_INFO_CAPABILITIES_HT_CAPABLE; } else { station_capabilities &= ~STATION_INFO_CAPABILITIES_HT_CAPABLE; } } // Update the station_info flags curr_station_info->flags = station_flags; curr_station_info->capabilities = station_capabilities; // Re-enable interrupts wlan_platform_intc_set_state(prev_interrupt_state); // // TODO: (Optional) Log association state change // // Update the hex display wlan_platform_high_userio_disp_status(USERIO_DISP_STATUS_MEMBER_LIST_UPDATE, active_network_info->members.length); wlan_exp_printf(WLAN_EXP_PRINT_INFO, print_type_node, "Associated with node: "); } else { // Re-enable interrupts wlan_platform_intc_set_state(prev_interrupt_state); wlan_exp_printf(WLAN_EXP_PRINT_INFO, print_type_node, "Could not associate with node: "); status = CMD_PARAM_ERROR; error_reason |= NODE_ASSOCIATE_ERROR_MEMORY; } } else { wlan_exp_printf(WLAN_EXP_PRINT_INFO, print_type_node, "Could not associate with node: "); status = CMD_PARAM_ERROR; error_reason |= NODE_ASSOCIATE_ERROR_TOO_MANY_ASSOC; } wlan_exp_print_mac_address(WLAN_EXP_PRINT_INFO, &mac_addr[0]); wlan_exp_printf(WLAN_EXP_PRINT_INFO, NULL, "\n"); // Send response wlan_exp_add_u32_resp_arg(eth_tx_queue_buffer, resp_hdr, status); if (curr_station_info != NULL) { wlan_exp_add_u32_resp_arg(eth_tx_queue_buffer, resp_hdr, curr_station_info->ID); } else { wlan_exp_add_u32_resp_arg(eth_tx_queue_buffer, resp_hdr, error_reason); } } break; //--------------------------------------------------------------------- default: { wlan_exp_printf(WLAN_EXP_PRINT_ERROR, print_type_node, "Unknown node command: 0x%x\n", cmd_id); } break; } return resp_sent; } #endif