/* * Copyright (c) 2010-2013, The Linux Foundation. All rights reserved. * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of The Linux Foundation, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "nl80211_copy.h" #include "qsap_api.h" #include "qsap.h" #define QCSAP_IOCTL_GETPARAM (SIOCIWFIRSTPRIV+1) #define WLAN_PRIV_SET_THREE_INT_GET_NONE (SIOCIWFIRSTPRIV + 4) #define QCSAP_IOCTL_GET_CHANNEL (SIOCIWFIRSTPRIV+9) #define QCSAP_IOCTL_ASSOC_STA_MACADDR (SIOCIWFIRSTPRIV+10) #define QCSAP_IOCTL_DISASSOC_STA (SIOCIWFIRSTPRIV+11) #define QCSAP_IOCTL_AP_STATS (SIOCIWFIRSTPRIV+12) #define QCSAP_IOCTL_SET_CHANNEL_RANGE (SIOCIWFIRSTPRIV+17) #define QCSAP_PARAM_GET_AUTO_CHANNEL 9 #define WE_SET_SAP_CHANNELS 3 #define LOG_TAG "QCSDK" #include #include #define SKIP_BLANK_SPACE(x) {while(*x != '\0') { if((*x == ' ') || (*x == '\t')) x++; else break; }} /** If this variable is enabled, the soft AP is reloaded, after the commit * command is received */ static volatile int gIniUpdated = 0; /** Supported command requests. * WANRING: The enum eCMD_REQ in the file qsap_api.h should be * updated if Cmd_req[], us updated */ s8 *Cmd_req[eCMD_REQ_LAST] = { "get", "set" }; /** Supported config file requests. * WANRING: The enum eConf_req in the file qsap_api.h should be * updated if Conf_req[], us updated */ s8 *Conf_req[CONF_REQ_LAST] = { "dual2g", "dual5g", "owe", "60g", }; /* * WARNING: On updating the cmd_list, the enum esap_cmd in file * qsap_api.h must be updates to reflect the changes */ static struct Command cmd_list[eCMD_LAST] = { { "ssid", "QualcommSoftAP" }, { "ignore_broadcast_ssid", "0" }, { "channel", "1" }, { "beacon_int", "100" }, { "dtim_period", "2" }, { "hw_mode", "n" }, { "auth_algs", "3" }, { "security_mode", "0" }, { "wep_key0", NULL }, { "wep_key1", NULL }, { "wep_key2", NULL }, { "wep_key3", NULL }, { "wep_default_key", NULL }, { "wpa_passphrase", NULL }, { "wpa_pairwise", NULL }, { "rsn_pairwise", NULL }, { "mac_address", "00deadbeef04" }, { "reset_ap", NULL }, { "macaddr_acl", "0" }, { "add_to_allow_list", NULL }, { "add_to_deny_list", NULL }, { "remove_from_allow_list", NULL }, { "remove_from_deny_list", NULL }, { "allow_list", "" }, { "deny_list", "" }, { "commit", NULL }, { "enable_softap", NULL }, { "disassoc_sta", NULL }, { "reset_to_default", NULL }, { "protection_flag", "1" }, { "data_rate", "0" }, { "sta_mac_list", NULL }, { "tx_power", "27" }, { "sdk_version", SDK_VERSION }, { "wmm_enabled", "0" }, /** Warning: Do not change the order of the WPS commands */ { "wps_state", "0" }, { "config_methods", NULL }, { "uuid", NULL }, { "device_name", NULL }, { "manufacturer", NULL }, { "model_name", NULL }, { "model_number", NULL }, { "serial_number", NULL }, { "device_type", NULL }, { "os_version", NULL }, { "friendly_name", NULL }, { "manufacturer_url", NULL }, { "model_description", NULL }, { "model_url", NULL }, { "upc", NULL }, /************ WPS commands end *********/ { "fragm_threshold", NULL }, { "rts_threshold", NULL }, { "wpa_group_rekey", NULL }, { "country_code", NULL }, { "ap_isolate", NULL }, { "ieee80211d", NULL }, { "apstat", NULL }, { "auto_shut_off_time", NULL }, { "energy_detect_threshold", "128" }, { "basic_rates", NULL }, { "require_ht", NULL }, { "ieee80211n", "1" }, { "setchannelrange", NULL }, { "autochannel", NULL }, { "ieee80211w", NULL }, { "wpa_key_mgmt", NULL }, { "max_num_sta", "8" }, { "ieee80211ac", NULL }, { "vht_oper_chwidth", NULL }, { "chanlist", NULL }, { "ht_capab", NULL }, { "ieee80211h", NULL }, { "enable_wigig_softap", NULL }, { "interface", NULL }, { "ssid2", NULL }, { "bridge", NULL }, { "ctrl_interface", NULL }, { "vendor_elements", NULL }, { "assocresp_elements", NULL }, { "acs_exclude_dfs", NULL }, { "wowlan_triggers", "any" }, { "accept_mac_file", NULL }, { "deny_mac_file", NULL }, { "owe_transition_ifname", NULL }, { "sae_require_mfp", NULL }, { "ieee80211ax", NULL }, { "enable_edmg", NULL }, { "edmg_channel", NULL }, }; struct Command qsap_str[eSTR_LAST] = { { "wpa", NULL }, { "accept_mac_file", NULL }, { "deny_mac_file", NULL }, { "gAPMacAddr", "00deadbeef04" },/** AP MAC address */ { "gEnableApProt", "1" },/** protection flag in ini file */ { "gFixedRate", "0" },/** Fixed rate in ini */ { "gTxPowerCap", "27" },/** Tx power in ini */ { "gFragmentationThreshold", "2346" },/** Fragmentation threshold in ini */ { "RTSThreshold", "2347" },/** RTS threshold in ini */ { "gAPCntryCode", "USI" },/** Country code in ini */ { "gDisableIntraBssFwd", "0" },/** Intra-bss forward in ini */ { "WmmIsEnabled", "0" },/** WMM */ { "g11dSupportEnabled", "1" },/** 802.11d support */ { "ieee80211n", NULL }, { "ctrl_interface", NULL }, { "interface", NULL }, { "eap_server", NULL }, { "gAPAutoShutOff", "0" }, { "gEnablePhyAgcListenMode", "128" }, }; /** Supported operating mode */ char *hw_mode[HW_MODE_UNKNOWN] = { "b", "g", "n", "g-only", "n-only", "a", "any", "ad" }; /** configuration file path */ char *pconffile = CONFIG_FILE; char *fIni = WIFI_DRIVER_CONF_FILE; s8 ini_file[PROPERTY_VALUE_MAX] = {0}; static int qsap_scnprintf(char *str, size_t size, const char *format, ...) { va_list arg_ptr; int ret = 0; if (size < 1) return 0; va_start(arg_ptr, format); ret = vsnprintf(str, size, format, arg_ptr); va_end(arg_ptr); return (ret < (int)size) ? ret : (int)(size - 1); } /** * @brief * For a give configuration parameter, read the configuration value from the file. * @param pfile [IN] configuration file path * @param pcmd [IN] pointer to the comand structure * @param presp [OUT] buffer to store the configuration value * @param plen [IN-OUT] The length of the buffer is provided as input. * The length of the configuration parameter value, stored * in the 'presp', is provided as the output * @param ignore_comment [IN] if set, read the commented value also * @return void */ static s32 qsap_read_cfg(s8 *pfile, struct Command * pcmd, s8 *presp, u32 *plen, s8 *var, s32 ignore_comment) { FILE *fcfg; s8 buf[MAX_CONF_LINE_LEN]; u16 len; s8 *val; /** Open the configuration file */ fcfg = fopen(pfile, "r"); if(NULL == fcfg) { ALOGE("%s : unable to open file \n", __func__); *plen = qsap_scnprintf(presp, *plen, "%s", ERR_RES_UNAVAILABLE); return eERR_FILE_OPEN; } /** Read the line from the configuration file */ len = strlen(pcmd->name); while(NULL != fgets(buf, MAX_CONF_LINE_LEN, fcfg)) { s8 *pline = buf; if (strlen(buf) == 0) continue; /** Skip the commented lines */ if(buf[0] == '#') { if (ignore_comment) { pline++; } else continue; } /** Identify the configuration parameter in the configuration file */ if(!strncmp(pline, pcmd->name, len) && (pline[len] == '=')) { int tmp_indx; /* Delate all \r \n combinations infront of the config string */ tmp_indx = strlen(buf)-1; while( (buf[tmp_indx] == '\r') || (buf[tmp_indx] == '\n') ) tmp_indx--; buf[tmp_indx+1] = '\0'; if ( NULL != var ) { val = strchr(pline, '='); if(NULL == val) break; *plen = qsap_scnprintf(presp, *plen, "%s %s%s", SUCCESS, var, val); } else { *plen = qsap_scnprintf(presp, *plen, "%s %s", SUCCESS, pline); } fclose(fcfg); return eSUCCESS; } } #if 0 /** Configuration parameter is absent in the file */ *plen = qsap_scnprintf(presp, *plen, "%s", ERR_FEATURE_NOT_ENABLED); #else /** Value not found in the configuration file */ /** Send the default value, if we are reading from ini file */ if ( pcmd->default_value ) { *plen = qsap_scnprintf(presp, *plen, "%s %s=%s", SUCCESS, var?var:pcmd->name, pcmd->default_value); fclose(fcfg); return eSUCCESS; } else { /** Configuration parameter is absent in the file */ *plen = qsap_scnprintf(presp, *plen, "%s", ERR_FEATURE_NOT_ENABLED); } #endif fclose(fcfg); return eERR_CONFIG_PARAM_MISSING; } /** * @brief * Write the configuration parameter value into the configuration file. * @param pfile [IN] configuration file path. * @param pcmd [IN] command name * @param pVal [IN] configuration parameter to be written to the file. * @param presp [OUT] buffer to store the configuration value. * @param plen [IN-OUT] The length of the buffer is provided as input. * The length of the configuration parameter value, stored * in the 'presp', is provided as the output * @return void */ static s32 qsap_write_cfg(s8 *pfile, struct Command * pcmd, s8 *pVal, s8 *presp, u32 *plen, s32 inifile) { FILE *fcfg, *ftmp; s8 buf[MAX_CONF_LINE_LEN+1]; s16 len, result = FALSE; ALOGD("cmd=%s, Val:%s, INI:%ld \n", pcmd->name, pVal, inifile); /** Open the configuration file */ fcfg = fopen(pfile, "r"); if(NULL == fcfg) { ALOGE("%s : unable to open file \n", __func__); *plen = qsap_scnprintf(presp, *plen, "%s", ERR_RES_UNAVAILABLE); return eERR_FILE_OPEN; } qsap_scnprintf(buf, sizeof(buf), "%s~", pfile); /** Open a temporary file */ ftmp = fopen(buf, "w+"); if(NULL == ftmp) { ALOGE("%s : unable to open tmp file \n", __func__); *plen = qsap_scnprintf(presp, *plen, "%s", ERR_RES_UNAVAILABLE); fclose(fcfg); return eERR_FILE_OPEN; } /** Read the values from the configuration file */ len = strlen(pcmd->name); while(NULL != fgets(buf, MAX_CONF_LINE_LEN, fcfg)) { s8 *pline = buf; /** commented line */ if(buf[0] == '#') pline++; /** Identify the configuration parameter to be updated */ if((!strncmp(pline, pcmd->name, len)) && (result == FALSE)) { if(pline[len] == '=') { qsap_scnprintf(buf, sizeof(buf), "%s=%s\n", pcmd->name, pVal); result = TRUE; ALOGD("Updated:%s\n", buf); } } if(inifile && (!strncmp(pline, "END", 3))) break; fprintf(ftmp, "%s", buf); } if (result == FALSE) { /* Configuration line not found */ /* Add the new line at the end of file */ qsap_scnprintf(buf, sizeof(buf), "%s=%s\n", pcmd->name, pVal); fprintf(ftmp, "%s", buf); ALOGD("Adding a new line in %s file: [%s] \n", inifile ? "inifile" : "hostapd.conf", buf); } if(inifile) { gIniUpdated = 1; fprintf(ftmp, "END\n"); while(NULL != fgets(buf, MAX_CONF_LINE_LEN, fcfg)) fprintf(ftmp, "%s", buf); } fclose(fcfg); fclose(ftmp); qsap_scnprintf(buf, sizeof(buf), "%s~", pfile); /** Restore the updated configuration file */ result = rename(buf, pfile); *plen = qsap_scnprintf(presp, *plen, "%s", (result == eERR_UNKNOWN) ? ERR_FEATURE_NOT_ENABLED : SUCCESS); /** Remove the temporary file. Dont care the return value */ unlink(buf); /* chmod is needed because open() didn't set permisions properly */ if (chmod(pfile, 0660) < 0) { ALOGE("Error changing permissions of %s to 0660: %s", pfile, strerror(errno)); unlink(pfile); return -1; } if(result == eERR_UNKNOWN) return eERR_FEATURE_NOT_ENABLED; return eSUCCESS; } /** * @brief Read the security mode set in the configuration * @param pfile [IN] configuration file path. * @param presp [OUT] buffer to store the security mode. * @param plen [IN-OUT] The length of the buffer is provided as input. * The length of the security mode value, stored * in the 'presp', is provided as the output * @return void */ static sec_mode_t qsap_read_security_mode(s8 *pfile, s8 *presp, u32 *plen) { sec_mode_t mode; u32 temp = *plen; /** Read the WEP default key */ qsap_read_cfg(pfile, &cmd_list[eCMD_DEFAULT_KEY], presp, plen, NULL, GET_ENABLED_ONLY); if ( !strcmp(presp, ERR_FEATURE_NOT_ENABLED) ) { *plen = temp; /* WEP, is not enabled */ /** Read WPA security status */ qsap_read_cfg(pfile, &qsap_str[STR_WPA], presp, plen, NULL, GET_ENABLED_ONLY); if ( !strcmp(presp, ERR_FEATURE_NOT_ENABLED) ) { /** WPA is disabled, No security */ mode = SEC_MODE_NONE; } else { /** WPA, WPA2 or WPA-WPA2 mixed security */ s8 * ptmp = presp; while((*plen)-- && (*ptmp++ != '=') ); mode = *plen ? ( *ptmp == '1' ? SEC_MODE_WPA_PSK : *ptmp == '2' ? SEC_MODE_WPA2_PSK : *ptmp == '3' ? SEC_MODE_WPA_WPA2_PSK : SEC_MODE_INVALID ): SEC_MODE_INVALID; } } else { /** Verify if, WPA is disabled */ *plen = temp; qsap_read_cfg(pfile, &qsap_str[STR_WPA], presp, plen, NULL, GET_ENABLED_ONLY); if ( !strcmp(presp, ERR_FEATURE_NOT_ENABLED) ) { /** WPA is disabled, hence WEP is enabled */ mode = SEC_MODE_WEP; } else { *plen = qsap_scnprintf(presp, temp, "%s", ERR_UNKNOWN); return SEC_MODE_INVALID; } } if(mode != SEC_MODE_INVALID) { *plen = qsap_scnprintf(presp, temp,"%s %s=%d", SUCCESS, cmd_list[eCMD_SEC_MODE].name, mode); } else { *plen = qsap_scnprintf(presp, temp,"%s", ERR_NOT_SUPPORTED); } return mode; } /** * @brief * Enable or disable a configuration parameter in the configuration file. * @param pfile [IN] configuration file name * @param pcmd [IN] configuration command structure * @param status [IN] status to be set. The valid values are 'ENABLE' or 'DISABLE' * @return On success, return 0 * On failure, return -1 */ static s32 qsap_change_cfg(s8 *pfile, struct Command * pcmd, u32 status) { FILE *fcfg, *ftmp; s8 buf[MAX_CONF_LINE_LEN+1]; u16 len; /** Open the configuartion file */ fcfg = fopen(pfile, "r"); if(NULL == fcfg) { ALOGE("%s : unable to open file \n", __func__); return eERR_UNKNOWN; } qsap_scnprintf(buf, sizeof(buf), "%s~", pfile); /** Open a temporary file */ ftmp = fopen(buf, "w"); if(NULL == ftmp) { ALOGE("%s : unable to open tmp file \n", __func__); fclose(fcfg); return eERR_UNKNOWN; } /** Read the configuration parameters from the configuration file */ len = strlen(pcmd->name); while(NULL != fgets(buf+1, MAX_CONF_LINE_LEN, fcfg)) { s8 *p = buf+1; /** Commented line */ if(p[0] == '#') p++; /** Identify the configuration parameter */ if(!strncmp(p, pcmd->name, len)) { if(p[len] == '=') { if(status == DISABLE) { fprintf(ftmp, "#%s", p); } else { fprintf(ftmp, "%s", p); } continue; } } fprintf(ftmp, "%s", buf+1); } fclose(fcfg); fclose(ftmp); qsap_scnprintf(buf, sizeof(buf), "%s~", pfile); /** Restore the new configuration file */ if(eERR_UNKNOWN == rename(buf, pfile)) { ALOGE("unable to rename the file \n"); return eERR_UNKNOWN; } /** Delete the temporary file */ unlink(buf); /* chmod is needed because open() didn't set permisions properly */ if (chmod(pfile, 0660) < 0) { ALOGE("Error changing permissions of %s to 0660: %s", pfile, strerror(errno)); unlink(pfile); return -1; } return 0; } /** * @brief * Set the security mode in the configuration. The security mode * can be : * 1. No security * 2. WEP * 3. WPA * 4. WPA2 * 5. WPA and WPA2 mixed mode * @param pfile [IN] configuration file name * @param sec_mode [IN] security mode to be set * @param presp [OUTPUT] presp The command output format : * On success, * success = * On failure, * failure * @param plen [IN-OUT] plen * [IN] The length of the buffer, presp * [OUT] The length of the response in the buffer, presp * @return void */ static void qsap_set_security_mode(s8 *pfile, u32 sec_mode, s8 *presp, u32 *plen) { s16 wep, wpa; s8 sec[MAX_INT_STR]; s32 rsn_status = DISABLE; s32 ret = eERR_UNKNOWN; /** Is valid security mode ? */ if(sec_mode >= SEC_MODE_INVALID) { *plen = qsap_scnprintf(presp, *plen, "%s", ERR_UNKNOWN); return; } /** No security */ if(SEC_MODE_NONE == sec_mode) { wep = DISABLE; wpa = DISABLE; } /** WEP security */ else if(SEC_MODE_WEP == sec_mode) { wep = ENABLE; wpa = DISABLE; } else { /** WPA, WPA2 and mixed-mode security */ u16 wpa_val; u32 tmp = *plen; wep = DISABLE; wpa = ENABLE; if(sec_mode == SEC_MODE_WPA_PSK) wpa_val = WPA_IN_CONF_FILE; else if(sec_mode == SEC_MODE_WPA2_PSK) { wpa_val = WPA2_IN_CONF_FILE; rsn_status = ENABLE; } else if(sec_mode == SEC_MODE_WPA_WPA2_PSK) { wpa_val = WPA_WPA2_IN_CONF_FILE; rsn_status = ENABLE; } qsap_scnprintf(sec, sizeof(sec), "%u", wpa_val); qsap_write_cfg(pfile, &qsap_str[STR_WPA], sec, presp, plen, HOSTAPD_CONF_QCOM_FILE); *plen = tmp; } /** The configuration parameters for the security to be set are enabled * and the configuration parameters for the other security types are * disabled in the configuration file */ if(eERR_UNKNOWN == qsap_change_cfg(pfile, &cmd_list[eCMD_DEFAULT_KEY], wep)) { ALOGE("%s: wep_default_key error\n", __func__); goto end; } if(eERR_UNKNOWN == qsap_change_cfg(pfile, &cmd_list[eCMD_WEP_KEY0], wep)) { ALOGE("%s: CMD_WEP_KEY0 \n", __func__); goto end; } if(eERR_UNKNOWN == qsap_change_cfg(pfile, &cmd_list[eCMD_WEP_KEY1], wep)) { ALOGE("%s: CMD_WEP_KEY1 \n", __func__); goto end; } if(eERR_UNKNOWN == qsap_change_cfg(pfile, &cmd_list[eCMD_WEP_KEY2], wep)) { ALOGE("%s: CMD_WEP_KEY2 \n", __func__); goto end; } if(eERR_UNKNOWN == qsap_change_cfg(pfile, &cmd_list[eCMD_WEP_KEY3], wep)) { ALOGE("%s: CMD_WEP_KEY3 \n", __func__); goto end; } if(eERR_UNKNOWN == qsap_change_cfg(pfile, &cmd_list[eCMD_PASSPHRASE], wpa)) { ALOGE("%s: Passphrase error\n", __func__); goto end; } if((sec_mode != SEC_MODE_NONE) && (sec_mode != SEC_MODE_WEP)) { u32 state = !rsn_status; if(sec_mode == SEC_MODE_WPA_WPA2_PSK) state = ENABLE; if(eERR_UNKNOWN == qsap_change_cfg(pfile, &cmd_list[eCMD_WPA_PAIRWISE], state)) { ALOGE("%s: WPA Pairwise\n", __func__); goto end; } } if(eERR_UNKNOWN == qsap_change_cfg(pfile, &cmd_list[eCMD_RSN_PAIRWISE], rsn_status)) { ALOGE("%s: WPA2 Pairwise\n", __func__); goto end; } if(eERR_UNKNOWN == qsap_change_cfg(pfile, &qsap_str[STR_WPA], wpa)) { ALOGE("%s: WPA\n", __func__); goto end; } ret = eSUCCESS; end: *plen = qsap_scnprintf(presp, *plen, "%s", (ret == eSUCCESS) ? SUCCESS : ERR_UNKNOWN); return; } /** * @brief * Get the file path having the allow or deny MAC address list * @param pcfgfile [IN] configuration file name * @param pcmd [IN] pcmd pointer to the command string * @param pfile [OUT] buffer to store the return value, containing the file name * or the error message. * @param plen [IN-OUT] size of the buffer 'pfile', is provided as input and * the length of the file name is returned as output * @return * On success, a pointer to the file name in the buffer 'pfile'. * On failure, NULL is returned */ static s8 *qsap_get_allow_deny_file_name(s8 *pcfgfile, struct Command * pcmd, s8 *pfile, u32 *plen) { if(eSUCCESS == qsap_read_cfg(pcfgfile, pcmd, pfile, plen, NULL, GET_ENABLED_ONLY)) { return strchr(pfile, '=') + 1; } return NULL; } int qsap_hostd_exec(int argc, char ** argv) { #define MAX_CMD_SIZE 256 char qcCmdBuf[MAX_CMD_SIZE], *pCmdBuf; u32 len = MAX_CMD_SIZE; int i = 2, ret; if ( argc < 4 ) { ALOGD("failure: invalid arguments"); return -1; } argc -= 2; pCmdBuf = qcCmdBuf; while (argc--) { ret = snprintf(pCmdBuf, len, " %s", argv[i]); if ((ret < 0) || (ret >= (int)len)) { /* Error case */ /* TODO: Command too long send the error message */ *pCmdBuf = '\0'; break; } ALOGD("argv[%d] (%s)",i, argv[i]); pCmdBuf += ret; len -= ret; i++; } ALOGD("QCCMD data (%s)", pCmdBuf); len = MAX_CMD_SIZE; qsap_hostd_exec_cmd(qcCmdBuf, qcCmdBuf, (u32*)&len); return 0; } /** Function to identify a valid MAC address */ static int isValid_MAC_address(char *pMac) { int i, len; len = strlen(pMac); if(len < MAC_ADDR_LEN) return FALSE; for(i=0; i= MAX_ALLOWED_MAC) { ALOGE("%s : File is full\n", __func__); *plen = qsap_scnprintf(presp, *plen, "%s", ERR_UNKNOWN); fclose(fp); return; } /** Update all the input MAC addresses into the MAC list file */ len = strlen(pVal); while(len > 0) { int i = 0; /** Get a MAC address from the input string */ while((*pVal != ' ' ) && (*pVal != '\0')) { macbuf[i] = *pVal; i++; pVal++; if(i == MAC_ADDR_LEN) break; } macbuf[i] = '\0'; pVal++; /** Is valid MAC address input ? */ if(TRUE == isValid_MAC_address(macbuf)) { /** Append the MAC address to the file */ fprintf(fp, "%s\n", macbuf); num_macs++; /** Evaluate with the allowed limit */ if(num_macs == MAX_ALLOWED_MAC) { ALOGE("MAC file is full now.... \n"); break; } } len -= strlen(macbuf); if(*pVal != '\0') len--; } fclose(fp); *plen = qsap_scnprintf(presp, *plen, "%s", SUCCESS); return; } /** * @brief * Remove one or more MAC addresses from the allow or deny MAC list file. * @param pfile [IN] path of the allow or deny list file. * @param pVal [IN] a list of MAC addresses to be removed from the MAC list file. * @param presp [OUT] the buffer to store the response * @param plen [IN-OUT] The length of the 'presp' buffer is provided as input. * The lenght of the response, stored in 'presp', is * provided as output * @return void */ static void qsap_remove_from_file(s8 *pfile, s8 *pVal, s8 *presp, u32 *plen) { FILE *fp; FILE *ftmp; s8 buf[MAX_CONF_LINE_LEN]; int status; /** Open the allow or deny MAC list file */ fp = fopen(pfile, "r+"); if(NULL == fp) { ALOGE("%s : unable to open the file \n", __func__); *plen = qsap_scnprintf(presp, *plen, "%s", ERR_RES_UNAVAILABLE); return; } qsap_scnprintf(buf, sizeof(buf), "%s~", pfile); /** Open a temporary file */ ftmp = fopen(buf, "w"); if(ftmp == NULL) { ALOGE("%s : unable to open the file \n", __func__); *plen = qsap_scnprintf(presp, *plen, "%s", ERR_RES_UNAVAILABLE); fclose(fp); return; } /** Read all the MAC addresses from the file */ while(NULL != fgets(buf, MAX_CONF_LINE_LEN, fp)) { s8 *plist; s32 slen; int write_back = 1; plist = pVal; slen = strlen(pVal); /** Compare each MAC address in the file with all the * input MAC addresses */ write_back = 1; while(slen > 0) { if(0 == strncmp(buf, plist, MAC_ADDR_LEN)) { write_back = 0; break; } while((*plist != ' ') && (*plist != '\0')) { plist++; slen--; } while(((*plist == ' ') || (*plist == '\t')) && (*plist != '\0')) { plist++; slen--; } } /** Update the file */ if(write_back) { fprintf(ftmp, "%s", buf); } } fclose(fp); fclose(ftmp); qsap_scnprintf(buf, sizeof(buf), "%s~", pfile); /** Restore the configuration file */ status = rename(buf, pfile); qsap_scnprintf(presp, *plen, "%s", (status == eERR_UNKNOWN) ? ERR_FEATURE_NOT_ENABLED : SUCCESS); unlink(buf); return; } /** * @brief * Identify the MAC list file and the type of updation on the file. * The MAC list file can be : Allow file or Deny file. * The type of operation is : Add to file or Delete from file * * @param file [IN] path of the allow or deny MAC list file. * @param cNum [IN] command number to 'type of file' and the 'type of updation' * to be done. * @param pVal [IN] A list of one or more MAC addresses. Multiple MAC addresses * are separated by a SPACE character * @param presp [OUT] Buffer to store the command response * @param plen [IN-OUT] The length of the 'presp' buffer is provided as input * The length of the response, stored in the 'presp' is provided * as the output * @return void */ static void qsap_update_mac_list(s8 *pfile, esap_cmd_t cNum, s8 *pVal, s8 *presp, u32 *plen) { ALOGD("%s : Updating file %s \n", __func__, pfile); switch(cNum) { case eCMD_ADD_TO_ALLOW: case eCMD_ADD_TO_DENY: qsap_add_mac_to_file(pfile, pVal, presp, plen); break; case eCMD_REMOVE_FROM_ALLOW: case eCMD_REMOVE_FROM_DENY: qsap_remove_from_file(pfile, pVal, presp, plen); break; default: *plen = qsap_scnprintf(presp, *plen, "%s", ERR_UNKNOWN); return; } return; } /** * @brief * @param fconfig [INPUT] configuration file name * @param cNum [INPUT] command number. The valid command numbers supported by * this function are : * eCMD_ALLOW_LIST - Get the MAC address list from the allow list * eCMD_DENY_LIST - Get the MAC address list from the deny list * @param presp [OUTPUT] presp The command output format : * On success, * success = * On failure, * failure * @param plen [IN-OUT] plen * [IN] The length of the buffer, presp * [OUT] The length of the response in the buffer, presp * @return void **/ static void qsap_get_mac_list(s8 *fconfile, esap_cmd_t cNum, s8 *presp, u32 *plen) { s8 buf[MAX_CONF_LINE_LEN]; FILE *fp; u32 len_remain; s8 *pfile, *pOut; esap_cmd_t sNum; int cnt = 0; /** Identify the allow or deny file */ if(eCMD_ALLOW_LIST == cNum) { sNum = STR_ACCEPT_MAC_FILE; } else if(eCMD_DENY_LIST == cNum) { sNum = STR_DENY_MAC_FILE; } else { *plen = qsap_scnprintf(presp, *plen, "%s", ERR_UNKNOWN); return; } /** Get the MAC allow or MAC deny file path */ len_remain = MAX_CONF_LINE_LEN; if(NULL == (pfile = qsap_get_allow_deny_file_name(fconfile, &qsap_str[sNum], buf, &len_remain))) { ALOGE("%s:Unknown error\n", __func__); *plen = qsap_scnprintf(presp, *plen, "%s", ERR_RES_UNAVAILABLE); return; } /** Open allow / deny file, and read the MAC addresses */ fp = fopen(pfile, "r"); if(NULL == fp) { ALOGE("%s: file open error\n",__func__); *plen = qsap_scnprintf(presp, *plen, "%s", ERR_RES_UNAVAILABLE); return; } *plen -= qsap_scnprintf(presp, *plen, "%s %s=", SUCCESS, cmd_list[cNum].name); pOut = presp + strlen(presp); /** Read the MAC address from the MAC allow or deny file */ while(NULL != (fgets(buf, MAX_CONF_LINE_LEN, fp))) { u32 len; /** Avoid the commented lines */ if(buf[0] == '#') continue; if(FALSE == isValid_MAC_address(buf)) continue; buf[strlen(buf)-1] = '\0'; if(*plen < strlen(buf)) { *pOut = '\0'; break; } len = qsap_scnprintf(pOut, *plen, "%s ", buf); cnt++; if (cnt >= MAX_ALLOWED_MAC) { break; } pOut += len; *plen -= len; } *plen = strlen(presp); fclose(fp); return; } static int qsap_read_mac_address(s8 *presp, u32 *plen) { char *ptr; char mac[MAC_ADDR_LEN]; u32 len, i; int nRet = eERR_INVALID_MAC_ADDR; len = *plen; if(eSUCCESS != qsap_read_cfg(fIni, &qsap_str[STR_MAC_IN_INI], presp, plen, cmd_list[eCMD_MAC_ADDR].name, GET_ENABLED_ONLY)) { ALOGE("%s :MAC addr read failure \n",__func__); goto end; } ptr = strchr(presp, '='); if(NULL == ptr) goto end; strlcpy(mac, ptr+1, MAC_ADDR_LEN); *plen = qsap_scnprintf(presp, len, "%s %s=", SUCCESS, cmd_list[eCMD_MAC_ADDR].name); ptr = presp + strlen(presp); len -= strlen(presp); /* decrease the total available buf length */ for(i=0; iname); } } return ptr; } static void qsap_read_wps_state(s8 *presp, u32 *plen) { u32 tlen = *plen; s32 status; s8 *pstate; if(NULL == (pstate = qsap_get_config_value(pconffile, &cmd_list[eCMD_WPS_STATE], presp, &tlen))) { /** unable to read the wps configuration, WPS is disabled !*/ ALOGD("%s :wps_state not in cfg file \n", __func__); status = DISABLE; } else { status = (atoi(pstate) == WPS_STATE_ENABLE) ? ENABLE : DISABLE; } *plen = qsap_scnprintf(presp, *plen, "success %s=%ld", cmd_list[eCMD_WPS_STATE].name, status); return; } /** * Get the channel being used in the soft AP. */ int qsap_get_operating_channel(s32 *pchan) { int sock; struct iwreq wrq; s8 interface[MAX_CONF_LINE_LEN]; u32 len = MAX_CONF_LINE_LEN; s8 *pif; int ret; if(ENABLE != is_softap_enabled()) { goto error; } if(NULL == (pif = qsap_get_config_value(pconffile, &qsap_str[STR_INTERFACE], interface, &len))) { ALOGE("%s :interface error \n", __func__); goto error; } interface[len] = '\0'; sock = socket(AF_INET, SOCK_DGRAM, 0); if(sock < 0) { ALOGE("%s :socket error \n", __func__); goto error; } *pchan = 0; strlcpy(wrq.ifr_name, pif, sizeof(wrq.ifr_name)); wrq.u.data.length = sizeof(s32); wrq.u.data.pointer = pchan; wrq.u.data.flags = 0; ret = ioctl(sock, QCSAP_IOCTL_GET_CHANNEL, &wrq); if(ret < 0) { ALOGE("%s: ioctl failure \n",__func__); close(sock); goto error; } ALOGE("Recv len :%d \n", wrq.u.data.length); *pchan = *(int *)(&wrq.u.name[0]); ALOGE("Operating channel :%ld \n", *pchan); close(sock); return eSUCCESS; error: *pchan = 0; ALOGE("%s: Failed to read channel \n", __func__); return eERR_CHAN_READ; } /** * Get the sap auto channel selection for soft AP. */ int qsap_get_sap_auto_channel_selection(s32 *pautochan) { int sock; struct iwreq wrq; s8 interface[MAX_CONF_LINE_LEN]; u32 len = MAX_CONF_LINE_LEN; s8 *pif; int ret; sap_auto_channel_info sap_autochan_info; s32 *pchan; if(ENABLE != is_softap_enabled()) { ALOGE("%s :is_softap_enabled() goto error \n", __func__); goto error; } if(NULL == (pif = qsap_get_config_value(pconffile, &qsap_str[STR_INTERFACE], interface, &len))) { ALOGD("%s :interface error \n", __func__); goto error; } interface[len] = '\0'; sock = socket(AF_INET, SOCK_DGRAM, 0); if(sock < 0) { ALOGD("%s :socket error \n", __func__); goto error; } *pautochan = 0; memset(&sap_autochan_info, 0, sizeof(sap_autochan_info)); memset(&wrq, 0, sizeof(wrq)); strlcpy(wrq.ifr_name, pif, sizeof(wrq.ifr_name)); wrq.u.data.length = sizeof(s32); sap_autochan_info.subioctl = QCSAP_PARAM_GET_AUTO_CHANNEL; wrq.u.data.pointer = pautochan; memcpy(wrq.u.name, (char *)(&sap_autochan_info), sizeof(sap_autochan_info)); wrq.u.data.flags = 0; ret = ioctl(sock, QCSAP_IOCTL_GETPARAM, &wrq); if(ret < 0) { ALOGE("%s: ioctl failure \n",__func__); close(sock); goto error; } ALOGD("Recv len :%d \n", wrq.u.data.length); *pautochan = *(int *)(&wrq.u.name[0]); ALOGD("Sap auto channel selection pautochan=%ld \n", *pautochan); close(sock); return eSUCCESS; error: *pautochan = 0; ALOGE("%s: Failed to read sap auto channel selection\n", __func__); return eERR_GET_AUTO_CHAN; } static int iftypeCallback(struct nl_msg* msg, void* arg) { struct nlmsghdr* ret_hdr = nlmsg_hdr(msg); struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; int* type = arg; struct genlmsghdr *gnlh = (struct genlmsghdr*) nlmsg_data(ret_hdr); nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); if (tb_msg[NL80211_ATTR_IFTYPE]) { *type = nla_get_u32(tb_msg[NL80211_ATTR_IFTYPE]); } return NL_SKIP; } /** * Get the mode of operation. */ int qsap_get_mode(s32 *pmode) { int ret = eERR_UNKNOWN; struct nl_sock* sk = NULL; int nl80211_id = -1; int if_type = -1; s8 interface[MAX_CONF_LINE_LEN]; u32 len = MAX_CONF_LINE_LEN; s8 *pif; struct nl_msg* msg = NULL; //get interface name if(NULL == (pif = qsap_get_config_value(pconffile, &qsap_str[STR_INTERFACE], interface, &len))) { ALOGD("%s :interface error \n", __func__); goto nla_put_failure; } interface[len] = '\0'; //allocate socket sk = nl_socket_alloc(); //return if socket allocation fails if(sk == NULL){ ALOGE( "socket allocation failure"); return ret; } //connect to generic netlink if (genl_connect(sk)) { ALOGE( "Netlink socket Connection failure"); ret = eERR_UNKNOWN; goto nla_put_failure; } //find the nl80211 driver ID nl80211_id = genl_ctrl_resolve(sk, "nl80211"); //attach a callback nl_socket_modify_cb(sk, NL_CB_VALID, NL_CB_CUSTOM, iftypeCallback, &if_type); //allocate a message msg = nlmsg_alloc(); //return if message allocation fails if(msg == NULL){ ALOGE( "message allocation failure"); goto nla_put_failure; } // setup the message genlmsg_put(msg, 0, 0, nl80211_id, 0, 0, NL80211_CMD_GET_INTERFACE, 0); //add message attributes NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(pif)); // Send the message ret = nl_send_auto_complete(sk, msg); if (ret < 0 ) { ALOGE( "nl_send_auto_complete failure"); ret = eERR_UNKNOWN; goto nla_put_failure; } //block for message to return nl_recvmsgs_default(sk); *pmode = if_type; ALOGI("%s: (%s) NL80211 Get Mode = %d \n",__func__, pif, (int)*pmode); ret = eSUCCESS; nla_put_failure: if (sk) nl_socket_free(sk); if (msg) nlmsg_free(msg); return ret; } /** * Set the channel Range for soft AP. */ int qsap_set_channel_range(s8 *buf) { int sock; struct iwreq wrq; s8 interface[MAX_CONF_LINE_LEN]; u32 len = MAX_CONF_LINE_LEN; s8 *pif; s8 *temp; int ret, i; sap_channel_info sap_chan_range; sta_channel_info sta_chan_range; ALOGE("buf :%s\n", buf); temp = buf; temp = strchr(buf, '='); if (NULL == temp) { goto error; } temp++; if (NULL == (pif = qsap_get_config_value(pconffile, &qsap_str[STR_INTERFACE], interface, &len))) { ALOGE("%s :interface error\n", __func__); goto error; } interface[len] = '\0'; sock = socket(AF_INET, SOCK_DGRAM, 0); if (sock < 0) { ALOGE("%s :socket error\n", __func__); goto error; } memset(&sap_chan_range, 0, sizeof(sap_chan_range)); memset(&sta_chan_range, 0, sizeof(sta_chan_range)); memset(&wrq, 0, sizeof(wrq)); if (ENABLE != is_softap_enabled()) { strlcpy(wrq.ifr_name, "wlan0", sizeof(wrq.ifr_name)); sta_chan_range.subioctl = WE_SET_SAP_CHANNELS; ret = sscanf(temp, "%d %d %d", &(sta_chan_range.stastartchan), &(sta_chan_range.staendchan), &(sta_chan_range.staband)); if (3 != ret) { ALOGE("%s : sscanf is not successful\n", __func__); close(sock); goto error; } memcpy(wrq.u.name, (char *)(&sta_chan_range), sizeof(sta_chan_range)); ALOGE("%s :Softap is off,Send SET_CHANNEL_RANGE over sta interface\n", __func__); ret = ioctl(sock, WLAN_PRIV_SET_THREE_INT_GET_NONE, &wrq); } else { strlcpy(wrq.ifr_name, pif, sizeof(wrq.ifr_name)); ret = sscanf(temp, "%d %d %d", &(sap_chan_range.startchan), &(sap_chan_range.endchan), &(sap_chan_range.band)); if (3 != ret) { ALOGE("%s : sscanf is not successful\n", __func__); close(sock); goto error; } memcpy(wrq.u.name, (char *)(&sap_chan_range), sizeof(sap_chan_range)); ALOGE("%s :SAP is on,Send SET_CHANNEL_RANGE over softap interface\n", __func__); ret = ioctl(sock, QCSAP_IOCTL_SET_CHANNEL_RANGE, &wrq); } if (ret < 0) { ALOGE("%s: ioctl failure\n", __func__); close(sock); goto error; } ALOGE("Recv len :%d\n", wrq.u.data.length); close(sock); return eSUCCESS; error: ALOGE("%s: Failed to set channel range\n", __func__); return eERR_SET_CHAN_RANGE; } int qsap_read_channel(s8 *pfile, struct Command *pcmd, s8 *presp, u32 *plen, s8 *pvar) { s8 *pval; s32 chan; u32 len = *plen; if(eSUCCESS == qsap_get_operating_channel(&chan)) { *plen = qsap_scnprintf(presp, len, "%s %s=%lu", SUCCESS, pcmd->name, chan); ALOGD("presp :%s\n", presp); } else { *plen = qsap_scnprintf(presp, len, "%s", ERR_UNKNOWN); } return eSUCCESS; } int qsap_read_auto_channel(struct Command *pcmd, s8 *presp, u32 *plen) { s8 *pval, *pautoval; s32 pautochan; u32 len = *plen; int autochan; ALOGE("%s :\n", __func__); if (eSUCCESS == qsap_get_sap_auto_channel_selection(&pautochan)) { *plen = qsap_scnprintf(presp, len, "%s autochannel=%lu", SUCCESS, pautochan); ALOGE("presp :%s\n", presp); } else { *plen = qsap_scnprintf(presp, len, "%s", ERR_UNKNOWN); } return eSUCCESS; } static int qsap_mac_to_macstr(s8 *pmac, u32 slen, s8 *pmstr, u32 *plen) { int len; int totlen = 0; while((slen > 0) && (*plen > 0)) { len = qsap_scnprintf(pmstr, *plen, "%.2X:%.2X:%.2X:%.2X:%.2X:%.2X ", (int)pmac[0], (int)pmac[1], (int)pmac[2], (int)pmac[3], (int)pmac[4], (int)pmac[5]); pmac += 6; slen -= 6; *plen -= len; pmstr += len; totlen += len; } if(totlen > 0) { *pmstr--; totlen--; } *pmstr = '\0'; *plen = totlen; return 0; } #define MAX_STA_ALLOWED 8 void qsap_get_associated_sta_mac(s8 *presp, u32 *plen) { int sock, ret; struct iwreq wrq; s8 interface[MAX_CONF_LINE_LEN]; u32 len = MAX_CONF_LINE_LEN; s8 *pif; s8 *pbuf, *pout; u32 buflen; u32 recvLen; u32 tlen; if(ENABLE != is_softap_enabled()) { goto error; } if(NULL == (pif = qsap_get_config_value(pconffile, &qsap_str[STR_INTERFACE], interface, &len))) { ALOGE("%s :interface error \n", __func__); goto error; } interface[len] = '\0'; sock = socket(AF_INET, SOCK_DGRAM, 0); if(sock < 0) { ALOGE("%s :socket failure \n", __func__); goto error; } /* response has length field + 6 bytes per STA */ buflen = sizeof(u32) + (MAX_STA_ALLOWED * 6); pbuf = (s8 *)malloc(buflen); if(NULL == pbuf) { ALOGE("%s :No memory \n", __func__); close(sock); goto error; } #define SIZE_OF_MAC_INT (6) strlcpy(wrq.ifr_name, pif, sizeof(wrq.ifr_name)); wrq.u.data.length = buflen; wrq.u.data.pointer = (void *)pbuf; wrq.u.data.flags = 0; ret = ioctl(sock, QCSAP_IOCTL_ASSOC_STA_MACADDR, &wrq); if(ret < 0) { ALOGE("%s :ioctl failure \n", __func__); free(pbuf); close(sock); goto error; } recvLen = *(u32 *)(wrq.u.data.pointer); recvLen -= sizeof(u32); len = qsap_scnprintf(presp, *plen, "%s %s=", SUCCESS, cmd_list[eCMD_ASSOC_STA_MACS].name); pout = presp + len; tlen = *plen - len; qsap_mac_to_macstr(pbuf+sizeof(u32), recvLen, pout, &tlen); *plen = len + tlen; free(pbuf); close(sock); return; error: *plen = qsap_scnprintf(presp, *plen, "%s", ERR_UNKNOWN); return; } static void qsap_read_wep_key(s8 *pfile, struct Command *pcmd, s8 *presp, u32 *plen, s8 *var) { s8 *pwep; s8 *pkey; if(eSUCCESS != qsap_read_cfg(pfile, pcmd, presp, plen, var, GET_COMMENTED_VALUE)) return; pwep = strchr(presp, '='); if(NULL == pwep) return; pwep++; if(pwep[0] == '"') { pkey = pwep; pwep++; while(*pwep != '\0') { *pkey = *pwep; pkey++; pwep++; } *pkey--; *pkey = '\0'; *plen -= 2; } return; } void qsap_read_ap_stats(s8 *presp, u32 *plen) { int sock, ret; struct iwreq wrq; s8 interface[MAX_CONF_LINE_LEN]; u32 len = MAX_CONF_LINE_LEN; s8 *pif; s8 *pbuf, *pout; u32 tlen; if(ENABLE != is_softap_enabled()) { *plen = qsap_scnprintf(presp, *plen, "%s", ERR_SOFTAP_NOT_STARTED); return; } if(NULL == (pif = qsap_get_config_value(pconffile, &qsap_str[STR_INTERFACE], interface, &len))) { ALOGE("%s :interface error \n", __func__); goto error; } interface[len] = '\0'; sock = socket(AF_INET, SOCK_DGRAM, 0); if(sock < 0) { ALOGE("%s :socket failure \n", __func__); goto error; } pbuf = (s8 *)malloc(MAX_RESP_LEN); if(NULL == pbuf) { ALOGE("%s :No memory \n", __func__); close(sock); goto error; } strlcpy(wrq.ifr_name, pif, sizeof(wrq.ifr_name)); wrq.u.data.length = MAX_RESP_LEN; wrq.u.data.pointer = (void *)pbuf; wrq.u.data.flags = 0; ret = ioctl(sock, QCSAP_IOCTL_AP_STATS, &wrq); if(ret < 0) { ALOGE("%s :ioctl failure \n", __func__); free(pbuf); close(sock); goto error; } *plen = qsap_scnprintf(presp, *plen, "%s %s=%s", SUCCESS, cmd_list[eCMD_AP_STATISTICS].name, pbuf); free(pbuf); close(sock); return; error: *plen = qsap_scnprintf(presp, *plen, "%s", ERR_UNKNOWN); return; } void qsap_read_autoshutoff(s8 *presp, u32 *plen) { u32 tlen, time = 0; s8 *ptime; tlen = *plen; if(NULL == (ptime = qsap_get_config_value(fIni, &qsap_str[STR_AP_AUTOSHUTOFF], presp, &tlen))) { /** unable to read the AP shutoff time */ ALOGE("%s :Failed to read AP shutoff time\n", __func__); } else { time = atoi(ptime); time = time / 60; /** Convert seconds to minutes */ } *plen = qsap_scnprintf(presp, *plen, "success %s=%ld", cmd_list[eCMD_AP_AUTOSHUTOFF].name, time); return; } /** * @brief * Get the configuration information from the softAP configuration * files * @param cNum [INPUT] * @param presp [OUTPUT] presp The command output format : * On success, * success = * On failure, * failure * @param plen [IN-OUT] plen * [IN] The length of the buffer, presp * [OUT] The length of the response in the buffer, presp * @return void **/ static void qsap_get_from_config(esap_cmd_t cNum, s8 *presp, u32 *plen) { u32 len; int status; s8 * pval; switch(cNum) { case eCMD_ENABLE_SOFTAP: status = is_softap_enabled(); *plen = qsap_scnprintf(presp, *plen, "%s %s=%d", SUCCESS, cmd_list[cNum].name, status); break; case eCMD_WPA_PAIRWISE: case eCMD_RSN_PAIRWISE: case eCMD_DEFAULT_KEY: case eCMD_PASSPHRASE: case eCMD_GTK_TIMEOUT: qsap_read_cfg(pconffile, &cmd_list[cNum], presp, plen, NULL, GET_COMMENTED_VALUE); break; case eCMD_SSID: case eCMD_BSSID: case eCMD_BCN_INTERVAL: case eCMD_DTIM_PERIOD: case eCMD_HW_MODE: case eCMD_AUTH_ALGS: case eCMD_MAC_ACL: case eCMD_WPS_CONFIG_METHOD: case eCMD_UUID: case eCMD_DEVICE_NAME: case eCMD_MANUFACTURER: case eCMD_MODEL_NAME: case eCMD_MODEL_NUMBER: case eCMD_SERIAL_NUMBER: case eCMD_DEVICE_TYPE: case eCMD_OS_VERSION: case eCMD_FRIENDLY_NAME: case eCMD_MANUFACTURER_URL: case eCMD_MODEL_DESC: case eCMD_MODEL_URL: case eCMD_UPC: case eCMD_SDK_VERSION: case eCMD_COUNTRY_CODE: qsap_read_cfg(pconffile, &cmd_list[cNum], presp, plen, NULL, GET_ENABLED_ONLY); break; case eCMD_WEP_KEY0: case eCMD_WEP_KEY1: case eCMD_WEP_KEY2: case eCMD_WEP_KEY3: qsap_read_wep_key(pconffile, &cmd_list[cNum], presp, plen, NULL); break; case eCMD_CHAN: qsap_read_channel(pconffile, &cmd_list[cNum], presp, plen, NULL); break; case eCMD_FRAG_THRESHOLD: case eCMD_REGULATORY_DOMAIN: case eCMD_RTS_THRESHOLD: case eCMD_IEEE80211H: qsap_read_cfg(pconffile, &cmd_list[cNum], presp, plen, NULL, GET_ENABLED_ONLY); break; case eCMD_ALLOW_LIST: /* fall through */ case eCMD_DENY_LIST: qsap_get_mac_list(pconffile, cNum, presp, plen); break; case eCMD_SEC_MODE: qsap_read_security_mode(pconffile, presp, plen); break; case eCMD_MAC_ADDR: if(eSUCCESS != qsap_read_mac_address(presp, plen)) { *plen = qsap_scnprintf(presp, *plen, "%s", ERR_NOT_SUPPORTED); } break; case eCMD_WMM_STATE: qsap_read_cfg(pconffile, &cmd_list[cNum], presp, plen, NULL, GET_ENABLED_ONLY); break; case eCMD_WPS_STATE: qsap_read_wps_state(presp, plen); break; case eCMD_PROTECTION_FLAG: qsap_read_cfg(fIni, &qsap_str[STR_PROT_FLAG_IN_INI], presp, plen, cmd_list[eCMD_PROTECTION_FLAG].name, GET_ENABLED_ONLY); break; case eCMD_DATA_RATES: qsap_read_cfg(fIni, &qsap_str[STR_DATA_RATE_IN_INI], presp, plen, cmd_list[eCMD_DATA_RATES].name, GET_ENABLED_ONLY); break; case eCMD_ASSOC_STA_MACS: qsap_get_associated_sta_mac(presp, plen); break; case eCMD_TX_POWER: qsap_read_cfg(fIni, &qsap_str[STR_TX_POWER_IN_INI], presp, plen, cmd_list[eCMD_TX_POWER].name, GET_ENABLED_ONLY); break; case eCMD_INTRA_BSS_FORWARD: qsap_read_cfg(pconffile, &cmd_list[eCMD_INTRA_BSS_FORWARD], presp, plen, NULL, GET_ENABLED_ONLY); break; case eCMD_AP_STATISTICS: qsap_read_ap_stats(presp, plen); break; case eCMD_AP_AUTOSHUTOFF: qsap_read_autoshutoff(presp, plen); break; case eCMD_AP_ENERGY_DETECT_TH: qsap_read_cfg(fIni, &qsap_str[STR_AP_ENERGY_DETECT_TH], presp, plen, cmd_list[eCMD_AP_ENERGY_DETECT_TH].name, GET_ENABLED_ONLY); break; case eCMD_GET_AUTO_CHANNEL: qsap_read_auto_channel(&cmd_list[cNum],presp, plen); break; default: /** Error case */ *plen = qsap_scnprintf(presp, *plen, "%s", ERR_INVALID_ARG); } len = *plen-1; /** Remove the space or tabs in the end of response */ while(len) { if((presp[len] == ' ') || (presp[len] == '\t')) len--; else break; } presp[len+1] = '\0'; *plen = len+1; return; } /** * @brief * Identify the command number corresponding to the input user command. * @param cName [INPUT] command name * @return * On success, * command number in the range 0 to (eCMD_INVALID-1) * On failure, * eCMD_INVALID **/ static esap_cmd_t qsap_get_cmd_num(s8 *cName) { s16 i, len; for(i=0; i [ ...]" * These commands are used to retreive the soft AP * configuration information * * @param pcmd [IN] pointer to the structure, storing the command. * @param presp [OUT] pointer to the buffer, to store the command response. * The command output format : * On success, * success = * On failure, * failure * @param plen [IN-OUT] * [IN] : Maximum length of the reponse buffer * [OUT]: Reponse length * @return * void */ static void qsap_handle_get_request(s8 *pcmd, s8 *presp, u32 *plen) { esap_cmd_t cNum; pcmd += strlen("get"); SKIP_BLANK_SPACE(pcmd); cNum = qsap_get_cmd_num(pcmd); if(cNum == eCMD_INVALID) { *plen = qsap_scnprintf(presp, *plen, "%s", ERR_INVALID_PARAM); return; } qsap_get_from_config(cNum, presp, plen); return; } static s16 is_valid_wep_key(s8 *pwep, s8 *pkey, s16 len) { int weplen; s16 ret = TRUE; int ascii = FALSE; weplen = strlen(pwep); /** Remove the double quotes if any */ if((pwep[0] == '"') && (pwep[weplen-1] == '"')) { pwep[weplen-1] = '\0'; pwep++; weplen -= 2; } /** The WEP key should be of length 5, 13 or 16 characters * or 10, 26, or 32 digits */ switch(weplen) { case WEP_64_KEY_ASCII: case WEP_128_KEY_ASCII: case WEP_152_KEY_ASCII: weplen--; while(weplen--) { if(0 == isascii(pwep[weplen])) { ALOGD("%c not ascii \n", pwep[weplen]); return FALSE; } } ascii = TRUE; break; case WEP_64_KEY_HEX: case WEP_128_KEY_HEX: case WEP_152_KEY_HEX: while(weplen--) { if(0 == isxdigit(pwep[weplen])) return FALSE; } break; default: ret = FALSE; } qsap_scnprintf(pkey, len, (ascii == TRUE) ? "\"%s\"" : "%s", pwep); return ret; } s16 wifi_qsap_reset_to_default(s8 *pcfgfile, s8 *pdefault) { FILE *fcfg, *ftmp; char buf[MAX_CONF_LINE_LEN]; int status = eSUCCESS; fcfg = fopen(pdefault, "r"); if(NULL == fcfg) { ALOGE("%s : unable to open file \n", __func__); return eERR_FILE_OPEN; } qsap_scnprintf(buf, sizeof(buf), "%s~", pcfgfile); ftmp = fopen(buf, "w+"); if(NULL == ftmp) { ALOGE("%s : unable to open file \n", __func__); fclose(fcfg); return eERR_FILE_OPEN; } while(NULL != fgets(buf, MAX_CONF_LINE_LEN, fcfg)) { fprintf(ftmp, "%s", buf); } fclose(fcfg); fclose(ftmp); qsap_scnprintf(buf, sizeof(buf), "%s~", pcfgfile); if(eERR_UNKNOWN == rename(buf, pcfgfile)) status = eERR_CONF_FILE; /** Remove the temporary file. Dont care the return value */ unlink(buf); return status; } #define CTRL_IFACE_PATH_LEN (128) void qsap_del_ctrl_iface(void) { u32 len; s8 dst_path[CTRL_IFACE_PATH_LEN], *pcif, *pif; s8 interface[64]; s8 path[CTRL_IFACE_PATH_LEN + 64]; len = CTRL_IFACE_PATH_LEN; if(NULL == (pcif = qsap_get_config_value(pconffile, &qsap_str[STR_CTRL_INTERFACE], dst_path, &len))) { ALOGE("%s :ctrl_iface path error \n", __func__); goto error; } len = 64; if(NULL == (pif = qsap_get_config_value(pconffile, &qsap_str[STR_INTERFACE], interface, &len))) { ALOGE("%s :interface error \n", __func__); goto error; } if ((int)sizeof(path) <= snprintf(path, sizeof(path), "%s/%s", pcif, pif)) { ALOGE("Iface path : truncating error, %s \n", path); goto error; } unlink(path); error: return; } static int qsap_send_cmd_to_hostapd(s8 *pcmd) { int sock; struct sockaddr_un cli; struct sockaddr_un ser; struct timeval timeout; int ret = eERR_SEND_TO_HOSTAPD; u32 len; fd_set read; s8 dst_path[CTRL_IFACE_PATH_LEN], *pcif, *pif; s8 interface[64]; s8 *ptr; u32 retry_cnt = 3; size_t ptr_size; len = CTRL_IFACE_PATH_LEN; ptr_size = (sizeof(ser.sun_path) < CTRL_IFACE_PATH_LEN)? CTRL_IFACE_PATH_LEN : sizeof(ser.sun_path); ptr = malloc(ptr_size); if(NULL == ptr) { ALOGE("%s :No memory \n", __func__); return ret; } if(NULL == (pcif = qsap_get_config_value(pconffile, &qsap_str[STR_CTRL_INTERFACE], dst_path, &len))) { ALOGE("%s :ctrl_iface path error \n", __func__); goto error; } len = 64; if(NULL == (pif = qsap_get_config_value(pconffile, &qsap_str[STR_INTERFACE], interface, &len))) { ALOGE("%s :interface error \n", __func__); goto error; } if ((int)sizeof(ser.sun_path) <= snprintf(ptr, sizeof(ser.sun_path), "%s/%s", pcif, pif)) { /* the sun_path is truncated. */ ALOGE("Iface path : truncating error, %s \n", ptr); goto error; } ALOGD("Connect to :%s\n", ptr); sock = socket(PF_UNIX, SOCK_DGRAM, 0); if(sock < 0) { ALOGE("%s :Socket error \n", __func__); goto error; } cli.sun_family = AF_UNIX; qsap_scnprintf(cli.sun_path, sizeof(cli.sun_path), SDK_CTRL_IF); ret = bind(sock, (struct sockaddr *)&cli, sizeof(cli)); if(ret < 0) { ALOGE("Bind Failure\n"); goto close_ret; } ser.sun_family = AF_UNIX; qsap_scnprintf(ser.sun_path, sizeof(ser.sun_path), "%s", ptr); ALOGD("Connect to: %s,(%d)\n", ser.sun_path, sock); ret = connect(sock, (struct sockaddr *)&ser, sizeof(ser)); if(ret < 0) { ALOGE("Connect Failure...\n"); goto close_ret; } ret = send(sock, pcmd, strlen(pcmd), 0); if(ret < 0) { ALOGE("Unable to send cmd to hostapd \n"); goto close_ret; } /* reset the ptr buffer size for recv */ len = ptr_size; #define HOSTAPD_RECV_TIMEOUT (2) while(1) { timeout.tv_sec = HOSTAPD_RECV_TIMEOUT; timeout.tv_usec = 0; FD_ZERO(&read); FD_SET(sock, &read); ret = select(sock+1, &read, NULL, NULL, &timeout); if(FD_ISSET(sock, &read)) { ret = recv(sock, ptr, len, 0); if(ret < 0) { ALOGE("%s: recv() failed \n", __func__); goto close_ret; } if ((u32)ret >= len) ptr[len-1] = 0; else ptr[ret] = 0; if((ret > 0) && (ptr[0] == '<')) { ALOGE("Not the expected response...\n: %s", ptr); retry_cnt--; if(retry_cnt) continue; break; } if(!strncmp(ptr, "FAIL", 4)) { ALOGE("Command failed in hostapd \n"); goto close_ret; } else { break; } } else { ALOGE("%s: Select failed \n", __func__); goto close_ret; } } ret = eSUCCESS; close_ret: close(sock); error: free(ptr); unlink(SDK_CTRL_IF); return ret; } static void qsap_update_wps_config(s8 *pVal, s8 *presp, u32 *plen) { u32 tlen = *plen; s32 status; s8 pwps_state[MAX_INT_STR+1]; s32 i; /* Enable/disable the following in hostapd.conf * 1. Update the wps_state * 2. Set eap_server=1 * 3. Update UPnP related variables */ status = atoi(pVal); qsap_scnprintf(pwps_state, sizeof(pwps_state), "%d", (status == ENABLE) ? WPS_STATE_ENABLE : WPS_STATE_DISABLE); qsap_write_cfg(pconffile, &cmd_list[eCMD_WPS_STATE], pwps_state, presp, &tlen, HOSTAPD_CONF_QCOM_FILE); if(eERR_UNKNOWN == qsap_change_cfg(pconffile, &cmd_list[eCMD_WPS_STATE], status)) { ALOGE("%s: Failed to enable %s\n", __func__, cmd_list[eCMD_WPS_STATE].name); goto error; } qsap_scnprintf(pwps_state, sizeof(pwps_state), "%d", ENABLE); /** update the eap_server=1 */ qsap_write_cfg(pconffile, &qsap_str[STR_EAP_SERVER], pwps_state, presp, plen, HOSTAPD_CONF_QCOM_FILE); for(i=eCMD_UUID; i<=eCMD_UPC; i++) { if(eERR_UNKNOWN == qsap_change_cfg(pconffile, &cmd_list[i], status)) { ALOGE("%s: failed to set %s\n", __func__, cmd_list[i].name); goto error; } } return; error: *plen = qsap_scnprintf(presp, *plen, "%s", ERR_UNKNOWN); return; } static void qsap_config_wps_method(s8 *pVal, s8 *presp, u32 *plen) { s8 buf[64]; s8 *ptr; int i; s32 value; /** INPUT : <0/1> */ /** PBC method : WPS_PBC */ /** PIN method : WPS_PIN any */ ptr = pVal; i = 0; while((*ptr != '\0') && (*ptr != ' ')) { buf[i] = *ptr; ptr++; i++; } buf[i] = '\0'; /** Identify the WPS method */ value = atoi(buf); if(TRUE != IS_VALID_WPS_CONFIG(value)) { *plen = qsap_scnprintf(presp, *plen, "%s", ERR_INVALID_PARAM); return; } SKIP_BLANK_SPACE(ptr); if( (value == WPS_CONFIG_PIN) && (*ptr == '\0') ){ ALOGE("%s :Invalid command \n", __func__); *plen = qsap_scnprintf(presp, *plen, "%s", ERR_INVALID_PARAM); return; } if(value == WPS_CONFIG_PBC) qsap_scnprintf(buf, sizeof(buf), "WPS_PBC"); else { if(strlen(ptr) < WPS_KEY_LEN) { ALOGD("%s :Invalid WPS key length\n", __func__); *plen = qsap_scnprintf(presp, *plen, "%s", ERR_INVALID_PARAM); return; } qsap_scnprintf(buf, sizeof(buf), "WPS_PIN any %s", ptr); } value = qsap_send_cmd_to_hostapd(buf); *plen = qsap_scnprintf(presp, *plen, "%s", (value == eSUCCESS) ? SUCCESS: ERR_UNKNOWN); return; } s32 atoh(u8 *str) { u32 val = 0; u32 pos = 0; s32 len = strlen((char *)str) - 1; while(len >= 0) { switch(str[len]) { case '0' ... '9': val += (str[len] - '0') << pos; break; case 'a' ... 'f': val += (str[len] - 'a' + 10) << pos; break; case 'A'... 'F': val += (str[len] - 'A' + 10) << pos; break; } len--; pos += 4; } return val; } int qsap_get_mac_in_bytes(char *psmac, char *pbmac) { int val; u8 str[3]; u32 i; str[2] = '\0'; if(FALSE == isValid_MAC_address(psmac)) { return FALSE; } for(i=0; i 14) { /** Change the operating mode to 'A' */ ulen = *tlen; if(eSUCCESS != qsap_write_cfg(pcfg, &cmd_list[eCMD_HW_MODE], hw_mode[HW_MODE_A], tbuf, &ulen, HOSTAPD_CONF_QCOM_FILE)) { ALOGE("%s :Unable to update the operating mode \n", __func__); return eERR_UNKNOWN; } } end: qsap_scnprintf(schan, sizeof(schan), "%ld", channel); return qsap_write_cfg(pcfg, &cmd_list[eCMD_CHAN], schan, tbuf, tlen, HOSTAPD_CONF_QCOM_FILE); } static int qsap_set_operating_mode(s32 mode, s8 *pmode, int pmode_len, s8 *tbuf, u32 *tlen) { u32 ulen; s8 *pcfgval; s32 channel; s8 sconf[MAX_INT_STR+1]; s8 *pcfg = pconffile; s32 rate_idx; s8 ieee11n_enable[] = "1"; s8 ieee11n_disable[] = "0"; ulen = *tlen; /** Update the operating mode */ qsap_change_cfg(pcfg, &cmd_list[eCMD_BASIC_RATES],DISABLE); qsap_change_cfg(pcfg, &cmd_list[eCMD_REQUIRE_HT],DISABLE); qsap_write_cfg(pcfg, &cmd_list[eCMD_IEEE80211N],ieee11n_disable, tbuf, &ulen, HOSTAPD_CONF_QCOM_FILE); switch(mode) { case HW_MODE_G_ONLY: qsap_change_cfg(pcfg, &cmd_list[eCMD_BASIC_RATES],ENABLE); break; case HW_MODE_N_ONLY: qsap_change_cfg(pcfg, &cmd_list[eCMD_REQUIRE_HT],ENABLE); /* fall through */ case HW_MODE_N: case HW_MODE_G: case HW_MODE_A: case HW_MODE_ANY: ulen = *tlen; qsap_write_cfg(pcfg, &cmd_list[eCMD_IEEE80211N],ieee11n_enable, tbuf, &ulen, HOSTAPD_CONF_QCOM_FILE); break; case HW_MODE_B: ulen = *tlen; qsap_write_cfg(pcfg, &cmd_list[eCMD_IEEE80211N],ieee11n_disable, tbuf, &ulen, HOSTAPD_CONF_QCOM_FILE); break; case HW_MODE_AD: /** For 802.11ad, disable the 802.11 HT */ qsap_change_cfg(pcfg, &cmd_list[eCMD_HT_CAPAB], DISABLE); break; } if(mode == HW_MODE_G_ONLY || mode == HW_MODE_N_ONLY || mode == HW_MODE_N ) { qsap_scnprintf(pmode, pmode_len, "%s",hw_mode[HW_MODE_G]); } return qsap_write_cfg(pcfg, &cmd_list[eCMD_HW_MODE], pmode, tbuf, tlen, HOSTAPD_CONF_QCOM_FILE); } static int qsap_set_data_rate(s32 drate_idx, s8 *presp, u32 *plen) { u32 ulen; s8 *pmode; s8 sconf[MAX_INT_STR+1]; int ret = eERR_UNKNOWN; if(TRUE != IS_VALID_DATA_RATE_IDX(drate_idx)) { ALOGE("%s :Invalid rate index \n", __func__); goto end; } ulen = *plen; /** Read the current operating mode */ if(NULL == (pmode = qsap_get_config_value(pconffile, &cmd_list[eCMD_HW_MODE], presp, &ulen))) { ALOGE("%s :Unable to read mode \n", __func__); goto end; } /** Validate the rate index against the current operating mode */ if(((!strcmp(pmode, hw_mode[HW_MODE_B])) && (drate_idx > B_MODE_MAX_DATA_RATE_IDX)) || ((!strcmp(pmode, hw_mode[HW_MODE_G]) || (!strcmp(pmode, hw_mode[HW_MODE_G_ONLY]))) && (drate_idx > G_ONLY_MODE_MAX_DATA_RATE_IDX))) { ALOGE("%s :Invalid rate index \n", __func__); goto end; } qsap_scnprintf(sconf, sizeof(sconf), "%ld", drate_idx); /** Update the rate index in the configuration */ return qsap_write_cfg(fIni, &qsap_str[STR_DATA_RATE_IN_INI], sconf, presp, plen, INI_CONF_FILE); end: *plen = qsap_scnprintf(presp, *plen, "%s", ERR_UNKNOWN); return ret; } /** * @brief * Handle the user requests of the form, * "set ..." * These commands are used to update the soft AP * configuration information * * @param pcmd [IN] pointer to the string, storing the command. * @param presp [OUT] pointer to the buffer, to store the command response. * The command output format : * On success, * success * On failure, * failure * @param plen [IN-OUT] * [IN]: Maximum length of the reponse buffer * [OUT]: Reponse length * @return * void */ static void qsap_handle_set_request(s8 *pcmd, s8 *presp, u32 *plen) { esap_cmd_t cNum; esap_str_t sNum = STR_DENY_MAC_FILE; s8 *pVal, *pfile; s8 filename[MAX_FILE_PATH_LEN]; u32 ulen; s32 status; s32 value; s16 ini = HOSTAPD_CONF_QCOM_FILE; s8 *pcfg = pconffile; pcmd += strlen("set"); SKIP_BLANK_SPACE(pcmd); if(!(strncmp(pcmd, Conf_req[CONF_2g], strlen(Conf_req[CONF_2g])))) { pcmd += strlen(Conf_req[CONF_2g]); SKIP_BLANK_SPACE(pcmd); } else if (!(strncmp(pcmd, Conf_req[CONF_5g], strlen(Conf_req[CONF_5g])))) { pcmd += strlen(Conf_req[CONF_5g]); SKIP_BLANK_SPACE(pcmd); } else if (!(strncmp(pcmd, Conf_req[CONF_owe], strlen(Conf_req[CONF_owe])))) { pcmd += strlen(Conf_req[CONF_owe]); SKIP_BLANK_SPACE(pcmd); } else if (!(strncmp(pcmd, Conf_req[CONF_60g], strlen(Conf_req[CONF_60g])))) { pcmd += strlen(Conf_req[CONF_60g]); SKIP_BLANK_SPACE(pcmd); } else { // DO NOTHING } cNum = qsap_get_cmd_num(pcmd); if(cNum == eCMD_INVALID) { *plen = qsap_scnprintf(presp, *plen, "%s", ERR_INVALID_ARG); ALOGE("Invalid command number :%d\n", cNum); return; } pVal = pcmd + strlen(cmd_list[cNum].name); if( (cNum != eCMD_COMMIT) && (cNum != eCMD_RESET_TO_DEFAULT) && ((*pVal != '=') || (((eCMD_PASSPHRASE != cNum)) && (strlen(pVal) < 2)))) { *plen = qsap_scnprintf(presp, *plen, "%s", ERR_INVALID_ARG); return; } pVal++; if((cNum != eCMD_COMMIT) && (cNum != eCMD_RESET_TO_DEFAULT)) { ALOGE("Cmd: %s Argument :%s \n", cmd_list[cNum].name, pVal); } switch(cNum) { case eCMD_ADD_TO_ALLOW: case eCMD_REMOVE_FROM_ALLOW: sNum = STR_ACCEPT_MAC_FILE; /* fall through */ case eCMD_ADD_TO_DENY: case eCMD_REMOVE_FROM_DENY: ulen = MAX_FILE_PATH_LEN; if(NULL != (pfile = qsap_get_allow_deny_file_name(pconffile, &qsap_str[sNum], filename, &ulen))) { qsap_update_mac_list(pfile, cNum, pVal, presp, plen); } else { *plen = qsap_scnprintf(presp, *plen, "%s", ERR_RES_UNAVAILABLE); } return; case eCMD_SEC_MODE: value = atoi(pVal); if(FALSE == IS_VALID_SEC_MODE(value)) goto error; /** Write back the integer value. This is to avoid values like 01, 001, 0001 * being written to the configuration. */ qsap_scnprintf(pVal, strlen(pVal)+1, "%ld", value); qsap_set_security_mode(pconffile, value, presp, plen); return; case eCMD_MAC_ACL: value = atoi(pVal); if(FALSE == IS_VALID_MAC_ACL(value)) goto error; /** Write back the integer value. This is to avoid values like 01, 001, 0001 * being written to the configuration */ qsap_scnprintf(pVal, strlen(pVal)+1, "%ld", value); if(ACL_ALLOW_LIST == value) { value = ENABLE; status = DISABLE; } else if(ACL_DENY_LIST == value){ value = DISABLE; status = ENABLE; } else { // must be ACL_ALLOW_AND_DENY_LIST value = ENABLE; status = ENABLE; } if(eERR_UNKNOWN != qsap_change_cfg(pconffile, &qsap_str[STR_ACCEPT_MAC_FILE], value)) { if(eERR_UNKNOWN != qsap_change_cfg(pconffile, &qsap_str[STR_DENY_MAC_FILE], status)) { qsap_write_cfg(pconffile, &cmd_list[cNum], pVal, presp, plen, HOSTAPD_CONF_QCOM_FILE); } else { goto error; } } else { goto error; } return; case eCMD_COMMIT: #if 0 // COMMIT is not required currently for ICS framework if ( gIniUpdated ) { status = wifi_qsap_reload_softap(); gIniUpdated = 0; } else { status = commit(); } *plen = qsap_scnprintf(presp, *plen, "%s", (status == eSUCCESS)? SUCCESS : ERR_UNKNOWN); #endif *plen = qsap_scnprintf(presp, *plen, "%s", SUCCESS); return; case eCMD_ENABLE_SOFTAP: value = atoi(pVal); if(TRUE != IS_VALID_SOFTAP_ENABLE(value)) goto error; if ( *pVal == '0' ) { status = wifi_qsap_unload_driver(); } else { status = wifi_qsap_load_driver(); } *plen = qsap_scnprintf(presp, *plen, "%s", (status==eSUCCESS) ? SUCCESS : "failure Could not enable softap"); return; case eCMD_ENABLE_WIGIG_SOFTAP: value = atoi(pVal); if (TRUE != IS_VALID_SOFTAP_ENABLE(value)) goto error; if ( *pVal == '0' ) { status = wifi_qsap_stop_wigig_softap(); } else { status = wifi_qsap_start_wigig_softap(); } *plen = qsap_scnprintf(presp, *plen, "%s", (status==eSUCCESS) ? SUCCESS : "failure Could not enable Wigig softap"); return; case eCMD_SSID: value = strlen(pVal); if(SSD_MAX_LEN < value) goto error; /* Disable ssid2 while setting ssid */ qsap_change_cfg(pcfg, &cmd_list[eCMD_SSID2], DISABLE); break; case eCMD_SET_MAX_CLIENTS: value = strlen(pVal); break; case eCMD_BSSID: value = atoi(pVal); if(FALSE == IS_VALID_BSSID(value)) goto error; /** Write back the integer value. This is to avoid values like 01, 001, 0001 * being written to the configuration */ qsap_scnprintf(pVal, strlen(pVal)+1, "%ld", value); break; case eCMD_PASSPHRASE: value = strlen(pVal); if(FALSE == IS_VALID_PASSPHRASE_LEN(value)) goto error; break; case eCMD_CHAN: value = atoi(pVal); ulen = MAX_FILE_PATH_LEN; value = qsap_set_channel(value, filename, &ulen); *plen = qsap_scnprintf(presp, *plen, "%s", (value == eSUCCESS) ? SUCCESS : ERR_UNKNOWN); return; case eCMD_BCN_INTERVAL: value = atoi(pVal); if(FALSE == IS_VALID_BEACON(value)) goto error; /** Write back the integer value. This is to avoid values like 01, 001, 0001 * being written to the configuration */ qsap_scnprintf(pVal, strlen(pVal)+1, "%ld", value); break; case eCMD_DTIM_PERIOD: value = atoi(pVal); if(FALSE == IS_VALID_DTIM_PERIOD(value)) goto error; /** Write back the integer value. This is to avoid values like 01, 001, 0001 * being written to the configuration */ qsap_scnprintf(pVal, strlen(pVal)+1, "%ld", value); break; case eCMD_HW_MODE: status = FALSE; for(value=HW_MODE_B; value CTRY_MAX_LEN ) goto error; break; case eCMD_AP_AUTOSHUTOFF: value = atoi(pVal); if(TRUE != IS_VALID_APSHUTOFFTIME(value)) goto error; /* copy a larger value back to pVal. Please pay special care * in caller to make sure that the buffer has sufficient size. */ qsap_scnprintf(pVal, MAX_INT_STR, "%ld", value*60); cNum = STR_AP_AUTOSHUTOFF; ini = INI_CONF_FILE; break; case eCMD_AP_ENERGY_DETECT_TH: value = atoi(pVal); if(TRUE != IS_VALID_ENERGY_DETECT_TH(value)) goto error; qsap_scnprintf(pVal, strlen(pVal)+1, "%ld", value); cNum = STR_AP_ENERGY_DETECT_TH; ini = INI_CONF_FILE; break; case eCMD_IEEE80211H: value = atoi(pVal); if(TRUE != IS_VALID_DFS_STATE(value)) goto error; qsap_scnprintf(pVal, strlen(pVal)+1, "%ld", value); break; case eCMD_SET_CHANNEL_RANGE: ALOGE("eCMD_SET_CHANNEL_RANGE pcmd :%s\n", pcmd); value = qsap_set_channel_range(pcmd); *plen = qsap_scnprintf(presp, *plen, "%s", (value == eSUCCESS) ? SUCCESS : ERR_UNKNOWN); return; case eCMD_SSID2: /* Disable ssid while setting ssid2 */ qsap_change_cfg(pcfg, &cmd_list[eCMD_SSID], DISABLE); break; default: ; /** Do not goto error, in default case */ } if(ini == INI_CONF_FILE) { ALOGD("WRITE TO INI FILE :%s\n", qsap_str[cNum].name); qsap_write_cfg(fIni, &qsap_str[cNum], pVal, presp, plen, ini); } else { qsap_write_cfg(pcfg, &cmd_list[cNum], pVal, presp, plen, ini); } return; error: *plen = qsap_scnprintf(presp, *plen, "%s", ERR_INVALID_PARAM); return; } /** * @brief * Initiate the command and return response * @param pcmd string containing the command request * The format of the command is * get param=value * or * set param=value * @param presp buffer to store the command response * @param plen length of the respone buffer * @return * void */ void qsap_hostd_exec_cmd(s8 *pcmd, s8 *presp, u32 *plen) { ALOGD("CMD INPUT [%s][%lu]\n", pcmd, *plen); /* Skip any blank spaces */ SKIP_BLANK_SPACE(pcmd); if(!(strncmp(pcmd, Cmd_req[eCMD_SET], strlen(Cmd_req[eCMD_SET])))) { if(!(strncmp(pcmd+4, Conf_req[CONF_2g], strlen(Conf_req[CONF_2g])))) { pconffile = CONFIG_FILE_2G; } else if (!(strncmp(pcmd+4, Conf_req[CONF_5g], strlen(Conf_req[CONF_5g])))) { pconffile = CONFIG_FILE_5G; } else if (!(strncmp(pcmd+4, Conf_req[CONF_owe], strlen(Conf_req[CONF_owe])))) { pconffile = CONFIG_FILE_OWE; } else if (!(strncmp(pcmd+4, Conf_req[CONF_60g], strlen(Conf_req[CONF_60g])))) { pconffile = CONFIG_FILE_60G; } else { pconffile = CONFIG_FILE; } } check_for_configuration_files(); if(!strncmp(pcmd, Cmd_req[eCMD_GET], strlen(Cmd_req[eCMD_GET])) && isblank(pcmd[strlen(Cmd_req[eCMD_GET])])) { qsap_handle_get_request(pcmd, presp, plen); } else if(!(strncmp(pcmd, Cmd_req[eCMD_SET], strlen(Cmd_req[eCMD_SET]))) && isblank(pcmd[strlen(Cmd_req[eCMD_SET])]) ) { qsap_handle_set_request(pcmd, presp, plen); } else { *plen = qsap_scnprintf(presp, *plen, "%s", ERR_INVALIDREQ); } ALOGD("CMD OUTPUT [%s]\nlen :%lu\n\n", presp, *plen); return; } /* netd and Froyo Native UI specific API */ #define DEFAULT_INTFERACE "wlan0" #define DEFAULT_SSID "SOFTAP_SSID" #define DEFAULT_CHANNEL 4 #define DEFAULT_PASSPHRASE "12345678" #define DEFAULT_AUTH_ALG 1 #define RECV_BUF_LEN 255 #define CMD_BUF_LEN 255 #define SET_BUF_LEN 15 /** Command input argv[3] = SSID, argv[4] = BROADCAST/HIDDEN, argv[5] = CHANNEL argv[6] = SECURITY, argv[7] = KEY, */ int qsapsetSoftap(int argc, char *argv[]) { char cmdbuf[CMD_BUF_LEN]; char respbuf[RECV_BUF_LEN]; u32 rlen = RECV_BUF_LEN; int i; int hidden = 0; int sec = SEC_MODE_NONE; char setCmd[SET_BUF_LEN] = "set"; int offset = 0; ALOGD("%s, %s, %s, %d\n", __FUNCTION__, argv[0], argv[1], argc); for ( i=0; i 2 && (strncmp(argv[2], Conf_req[CONF_2g], 4) == 0 || strncmp(argv[2], Conf_req[CONF_owe], 3) == 0 || strncmp(argv[2], Conf_req[CONF_60g], 3) == 0)) { snprintf(setCmd, SET_BUF_LEN, "set %s", argv[2]); offset = 1; argc--; } /* set interface */ if (argc > 2) { snprintf(cmdbuf, CMD_BUF_LEN, "%s interface=%s", setCmd, argv[2 + offset]); } else { snprintf(cmdbuf, CMD_BUF_LEN, "%s interface=%s", setCmd, DEFAULT_INTFERACE); } (void) qsap_hostd_exec_cmd(cmdbuf, respbuf, &rlen); /** set SSID */ if(argc > 3) { // In case of dual2g/5g, Set ssid2 with hex values to accomodate sapce and special characters. if (offset) qsap_scnprintf(cmdbuf, sizeof(cmdbuf), "%s ssid2=%s", setCmd, argv[3 + offset]); else qsap_scnprintf(cmdbuf, sizeof(cmdbuf), "%s ssid=%s",setCmd, argv[3]); } else { qsap_scnprintf(cmdbuf, sizeof(cmdbuf), "%s ssid=%s_%d", setCmd, DEFAULT_SSID, rand()); } (void) qsap_hostd_exec_cmd(cmdbuf, respbuf, &rlen); if(strncmp("success", respbuf, rlen) != 0) { ALOGE("Failed to set ssid\n"); return eERR_UNKNOWN; } rlen = RECV_BUF_LEN; if (argc > 4) { if (strcmp(argv[4 + offset], "hidden") == 0) { hidden = 1; } snprintf(cmdbuf, CMD_BUF_LEN, "%s ignore_broadcast_ssid=%d", setCmd, hidden); (void) qsap_hostd_exec_cmd(cmdbuf, respbuf, &rlen); if(strncmp("success", respbuf, rlen) != 0) { ALOGE("Failed to set ignore_broadcast_ssid \n"); return -1; } } /** channel */ rlen = RECV_BUF_LEN; if(argc > 5) { snprintf(cmdbuf, CMD_BUF_LEN, "%s channel=%d", setCmd, atoi(argv[5 + offset])); (void) qsap_hostd_exec_cmd(cmdbuf, respbuf, &rlen); if(strncmp("success", respbuf, rlen) != 0) { ALOGE("Failed to set channel \n"); return -1; } } /** Security */ rlen = RECV_BUF_LEN; if(argc > 6) { /**TODO : need to identify the SEC strings for "wep", "wpa", "wpa2" */ if(!strcmp(argv[6 + offset], "open")) sec = SEC_MODE_NONE; else if(!strcmp(argv[6 + offset], "wep")) sec = SEC_MODE_WEP; else if(!strcmp(argv[6 + offset], "wpa-psk")) sec = SEC_MODE_WPA_PSK; else if(!strcmp(argv[6 + offset], "wpa2-psk")) sec = SEC_MODE_WPA2_PSK; qsap_scnprintf(cmdbuf, sizeof(cmdbuf), "%s security_mode=%d",setCmd, sec); } else { qsap_scnprintf(cmdbuf, sizeof(cmdbuf) , "%s security_mode=%d", setCmd, DEFAULT_AUTH_ALG); } (void) qsap_hostd_exec_cmd(cmdbuf, respbuf, &rlen); if(strncmp("success", respbuf, rlen) != 0) { ALOGE("Failed to set security mode\n"); return -1; } /** Key -- passphrase */ rlen = RECV_BUF_LEN; if ( (sec == SEC_MODE_WPA_PSK) || (sec == SEC_MODE_WPA2_PSK) ) { if(argc > 7) { /* If the input passphrase is more than 63 characters, consider first 63 characters only*/ if ( strlen(argv[7 + offset]) > 63 ) argv[7 + offset][63] = '\0'; qsap_scnprintf(cmdbuf, CMD_BUF_LEN, "%s wpa_passphrase=%s",setCmd, argv[7 + offset]); } else { qsap_scnprintf(cmdbuf, sizeof(cmdbuf), "%s wpa_passphrase=%s", setCmd, DEFAULT_PASSPHRASE); } } (void) qsap_hostd_exec_cmd(cmdbuf, respbuf, &rlen); if(strncmp("success", respbuf, rlen) != 0) { ALOGE("Failed to set passphrase \n"); return -1; } rlen = RECV_BUF_LEN; if(argc > 8) { qsap_scnprintf(cmdbuf, sizeof(cmdbuf), "%s max_num_sta=%d",setCmd, atoi(argv[8 + offset])); } (void) qsap_hostd_exec_cmd(cmdbuf, respbuf, &rlen); if(strncmp("success", respbuf, rlen) != 0) { ALOGE("Failed to set maximun client connections number \n"); return -1; } rlen = RECV_BUF_LEN; qsap_scnprintf(cmdbuf, sizeof(cmdbuf), "%s commit", setCmd); (void) qsap_hostd_exec_cmd(cmdbuf, respbuf, &rlen); if(strncmp("success", respbuf, rlen) != 0) { ALOGE("Failed to COMMIT \n"); return -1; } return 0; } static int check_for_config_file_size(FILE *fp) { int length = 0; if( NULL != fp ) { fseek(fp, 0L, SEEK_END); length = ftell(fp); } return length; } void check_for_configuration_files(void) { FILE * fp; char *pfile; /* Check if configuration files are present, if not create the default files */ /* If configuration file does not exist copy the default file */ if ( NULL == (fp = fopen(pconffile, "r")) ) { wifi_qsap_reset_to_default(pconffile, DEFAULT_CONFIG_FILE_PATH); } else { /* The configuration file could be of 0 byte size, replace with default */ if (check_for_config_file_size(fp) <= 0) wifi_qsap_reset_to_default(pconffile, DEFAULT_CONFIG_FILE_PATH); fclose(fp); } /* If Accept MAC list file does not exist, copy the default file */ if ( NULL == (fp = fopen(ACCEPT_LIST_FILE, "r")) ) { wifi_qsap_reset_to_default(ACCEPT_LIST_FILE, DEFAULT_ACCEPT_LIST_FILE_PATH); } else { /* The configuration file could be of 0 byte size, replace with default */ if (check_for_config_file_size(fp) <= 0) wifi_qsap_reset_to_default(ACCEPT_LIST_FILE, DEFAULT_ACCEPT_LIST_FILE_PATH); fclose(fp); } /* Provide read and write permissions to the owner */ pfile = ACCEPT_LIST_FILE; if (chmod(pfile, 0660) < 0) { ALOGE("Error changing permissions of %s to 0660: %s", pfile, strerror(errno)); } /* If deny MAC list file does not exist, copy the default file */ if ( NULL == (fp = fopen(DENY_LIST_FILE, "r")) ) { wifi_qsap_reset_to_default(DENY_LIST_FILE, DEFAULT_DENY_LIST_FILE_PATH); } else { /* The configuration file could be of 0 byte size, replace with default */ if (check_for_config_file_size(fp) <= 0) wifi_qsap_reset_to_default(DENY_LIST_FILE, DEFAULT_DENY_LIST_FILE_PATH); fclose(fp); } /* Provide read and write permissions to the owner */ pfile = DENY_LIST_FILE; if (chmod(pfile, 0660) < 0) { ALOGE("Error changing permissions of %s to 0660: %s", pfile, strerror(errno)); } return; } void qsap_set_ini_filename(void) { if (property_get("vendor.wlan.driver.config", ini_file, NULL)) { fIni = ini_file; ALOGE("INI FILE PROP PRESENT %s\n", fIni); } else ALOGE("INI FILE PROP NOT PRESENT: Use default path %s\n", fIni); return; } // IOCTL command to create and delete bridge interface // #ifndef SIOCBRADDBR #define SIOCBRADDBR 0x89a0 #endif #ifndef SIOCBRDELBR #define SIOCBRDELBR 0x89a1 #endif static int linux_set_iface_flags(int sock, const char *ifname, int dev_up) { struct ifreq ifr; int ret; if (sock < 0) return -1; memset(&ifr, 0, sizeof(ifr)); strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); if (ioctl(sock, SIOCGIFFLAGS, &ifr) != 0) { ret = errno ? -errno : -999; ALOGE("Could not read interface %s flags: %s", ifname, strerror(errno)); return ret; } if (dev_up) { if (ifr.ifr_flags & IFF_UP) return 0; ifr.ifr_flags |= IFF_UP; } else { if (!(ifr.ifr_flags & IFF_UP)) return 0; ifr.ifr_flags &= ~IFF_UP; } if (ioctl(sock, SIOCSIFFLAGS, &ifr) != 0) { ret = errno ? -errno : -999; ALOGE("Could not set interface %s flags (%s): %s", ifname, dev_up ? "UP" : "DOWN", strerror(errno)); return ret; } return 0; } int qsap_control_bridge(int argc, char ** argv) { int br_socket; if (argc < 4) { ALOGE("Command not supported"); return -1; } br_socket = socket(PF_INET, SOCK_DGRAM, 0); if (br_socket < 0) { ALOGE("socket(PF_INET,SOCK_DGRAM): %s",strerror(errno)); return -1; } if (!strncmp(argv[2],"create", 6)) { if (ioctl(br_socket, SIOCBRADDBR, argv[3]) < 0) { ALOGE("Could not add bridge %s: %s", argv[3], strerror(errno)); return -1; } } else if (!strncmp(argv[2],"remove", 6)) { if (ioctl(br_socket, SIOCBRDELBR, argv[3]) < 0) { ALOGE("Could not add remove %s: %s", argv[3], strerror(errno)); return -1; } } else if (!strncmp(argv[2],"up", 2)) { return linux_set_iface_flags(br_socket, argv[3], 1); } else if (!strncmp(argv[2],"down", 4)) { return linux_set_iface_flags(br_socket, argv[3], 0); } else { ALOGE("Command %s not handled.", argv[2]); return -1; } return 0; } int linux_get_ifhwaddr(const char *ifname, char *addr) { struct ifreq ifr; int sock = socket(AF_INET, SOCK_DGRAM, 0); #ifndef MAC2STR #define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] #define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" #endif memset(&ifr, 0, sizeof(ifr)); strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); if (ioctl(sock, SIOCGIFHWADDR, &ifr)) { ALOGE("Could not get interface %s hwaddr: %s", ifname, strerror(errno)); return -1; } if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { ALOGE("%s: Invalid HW-addr family 0x%04x", ifname, ifr.ifr_hwaddr.sa_family); return -1; } memcpy(addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); ALOGE("%s: " MACSTR, ifname, MAC2STR(addr)); return 0; } int qsap_add_or_remove_interface(const char *newIface , int createIface) { const char *wlanIface = "wlan0"; int retVal = 0; struct nl_msg *msg = NULL; struct nl_cb *cb = NULL; struct nl_cb *s_cb = NULL; struct nl_sock *nl_sock = NULL; int nl80211_id; enum nl80211_iftype type = NL80211_IFTYPE_AP; /* Allocate a netlink socket */ s_cb = nl_cb_alloc(NL_CB_DEFAULT); if (!s_cb) { ALOGE( "Failed to allocate Netlink Socket"); retVal = -ENOMEM; goto nla_put_failure; } nl_sock = nl_socket_alloc_cb(s_cb); if (!nl_sock) { ALOGE( "Netlink socket Allocation failure"); retVal = -ENOMEM; goto nla_put_failure; } /* connect to generic netlink socket */ if (genl_connect(nl_sock)) { ALOGE( "Netlink socket Connection failure"); retVal = -ENOLINK; goto nla_put_failure; } nl80211_id = genl_ctrl_resolve(nl_sock, "nl80211"); if (nl80211_id < 0) { ALOGE( "nl80211 generic netlink not found"); retVal = -ENOENT; goto nla_put_failure; } msg = nlmsg_alloc(); if(!msg) { ALOGE( "Failed to allocate netlink message"); retVal = -ENOMEM; goto nla_put_failure; } cb = nl_cb_alloc(NL_CB_DEFAULT); if (!cb) { ALOGE( "Failed to allocate netlink callback"); retVal = -ENOMEM; goto nla_put_failure; } if (createIface == 1) { /* Issue NL80211_CMD_NEW_INTERFACE */ genlmsg_put( msg, 0, 0, nl80211_id, 0, 0, NL80211_CMD_NEW_INTERFACE, 0); nla_put_u32( msg, NL80211_ATTR_IFINDEX, if_nametoindex( wlanIface )); NLA_PUT_STRING( msg, NL80211_ATTR_IFNAME, newIface); nla_put_u32( msg, NL80211_ATTR_IFTYPE, type); } else { genlmsg_put( msg, 0, 0, nl80211_id, 0, 0, NL80211_CMD_DEL_INTERFACE, 0); nla_put_u32( msg, NL80211_ATTR_IFINDEX, if_nametoindex( newIface )); } retVal = nl_send_auto_complete(nl_sock, msg ); if (retVal < 0 ) { goto nla_put_failure; } else { ALOGD("Interface %s is %s - Ok", (createIface == 1 ? "created":"removed") ,newIface); } nla_put_failure: if (nl_sock) nl_socket_free(nl_sock); if (s_cb) nl_cb_put(s_cb); if (msg) nlmsg_free(msg); if (cb) nl_cb_put(cb); return retVal; }