SoftapController.cpp revision 78dcb76aff7d3b9458374832a79688d36901127f
1/* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include <stdlib.h> 18#include <errno.h> 19#include <fcntl.h> 20#include <string.h> 21 22#include <sys/socket.h> 23#include <sys/stat.h> 24#include <sys/ioctl.h> 25#include <sys/types.h> 26#include <sys/wait.h> 27 28#include <netinet/in.h> 29#include <arpa/inet.h> 30 31#include <linux/wireless.h> 32 33#include <openssl/evp.h> 34#include <openssl/sha.h> 35 36#define LOG_TAG "SoftapController" 37#include <cutils/log.h> 38#include <netutils/ifc.h> 39#include <private/android_filesystem_config.h> 40#include "wifi.h" 41 42#include "SoftapController.h" 43 44static const char HOSTAPD_CONF_FILE[] = "/data/misc/wifi/hostapd.conf"; 45 46SoftapController::SoftapController() { 47 mPid = 0; 48 mSock = socket(AF_INET, SOCK_DGRAM, 0); 49 if (mSock < 0) 50 LOGE("Failed to open socket"); 51 memset(mIface, 0, sizeof(mIface)); 52} 53 54SoftapController::~SoftapController() { 55 if (mSock >= 0) 56 close(mSock); 57} 58 59int SoftapController::setCommand(char *iface, const char *fname, unsigned buflen) { 60#ifdef HAVE_HOSTAPD 61 return 0; 62#else 63 char tBuf[SOFTAP_MAX_BUFFER_SIZE]; 64 struct iwreq wrq; 65 struct iw_priv_args *priv_ptr; 66 int i, j, ret; 67 int cmd = 0, sub_cmd = 0; 68 69 strncpy(wrq.ifr_name, iface, sizeof(wrq.ifr_name)); 70 wrq.u.data.pointer = tBuf; 71 wrq.u.data.length = sizeof(tBuf) / sizeof(struct iw_priv_args); 72 wrq.u.data.flags = 0; 73 if ((ret = ioctl(mSock, SIOCGIWPRIV, &wrq)) < 0) { 74 LOGE("SIOCGIPRIV failed: %d", ret); 75 return ret; 76 } 77 78 priv_ptr = (struct iw_priv_args *)wrq.u.data.pointer; 79 for(i=0; i < wrq.u.data.length;i++) { 80 if (strcmp(priv_ptr[i].name, fname) == 0) { 81 cmd = priv_ptr[i].cmd; 82 break; 83 } 84 } 85 86 if (i == wrq.u.data.length) { 87 LOGE("iface:%s, fname: %s - function not supported", iface, fname); 88 return -1; 89 } 90 91 if (cmd < SIOCDEVPRIVATE) { 92 for(j=0; j < i; j++) { 93 if ((priv_ptr[j].set_args == priv_ptr[i].set_args) && 94 (priv_ptr[j].get_args == priv_ptr[i].get_args) && 95 (priv_ptr[j].name[0] == '\0')) 96 break; 97 } 98 if (j == i) { 99 LOGE("iface:%s, fname: %s - invalid private ioctl", iface, fname); 100 return -1; 101 } 102 sub_cmd = cmd; 103 cmd = priv_ptr[j].cmd; 104 } 105 106 strncpy(wrq.ifr_name, iface, sizeof(wrq.ifr_name)); 107 if ((buflen == 0) && (*mBuf != 0)) 108 wrq.u.data.length = strlen(mBuf) + 1; 109 else 110 wrq.u.data.length = buflen; 111 wrq.u.data.pointer = mBuf; 112 wrq.u.data.flags = sub_cmd; 113 ret = ioctl(mSock, cmd, &wrq); 114 return ret; 115#endif 116} 117 118int SoftapController::startDriver(char *iface) { 119 int ret; 120 121 if (mSock < 0) { 122 LOGE("Softap driver start - failed to open socket"); 123 return -1; 124 } 125 if (!iface || (iface[0] == '\0')) { 126 LOGD("Softap driver start - wrong interface"); 127 iface = mIface; 128 } 129 130 *mBuf = 0; 131 ret = setCommand(iface, "START"); 132 if (ret < 0) { 133 LOGE("Softap driver start: %d", ret); 134 return ret; 135 } 136#ifdef HAVE_HOSTAPD 137 ifc_init(); 138 ret = ifc_up(iface); 139 ifc_close(); 140#endif 141 usleep(AP_DRIVER_START_DELAY); 142 LOGD("Softap driver start: %d", ret); 143 return ret; 144} 145 146int SoftapController::stopDriver(char *iface) { 147 int ret; 148 149 if (mSock < 0) { 150 LOGE("Softap driver stop - failed to open socket"); 151 return -1; 152 } 153 if (!iface || (iface[0] == '\0')) { 154 LOGD("Softap driver stop - wrong interface"); 155 iface = mIface; 156 } 157 *mBuf = 0; 158#ifdef HAVE_HOSTAPD 159 ifc_init(); 160 ret = ifc_down(iface); 161 ifc_close(); 162 if (ret < 0) { 163 LOGE("Softap %s down: %d", iface, ret); 164 } 165#endif 166 ret = setCommand(iface, "STOP"); 167 LOGD("Softap driver stop: %d", ret); 168 return ret; 169} 170 171int SoftapController::startSoftap() { 172 pid_t pid = 1; 173 int ret = 0; 174 175 if (mPid) { 176 LOGE("Softap already started"); 177 return 0; 178 } 179 if (mSock < 0) { 180 LOGE("Softap startap - failed to open socket"); 181 return -1; 182 } 183#ifdef HAVE_HOSTAPD 184 if ((pid = fork()) < 0) { 185 LOGE("fork failed (%s)", strerror(errno)); 186 return -1; 187 } 188#endif 189 if (!pid) { 190#ifdef HAVE_HOSTAPD 191 if (execl("/system/bin/hostapd", "/system/bin/hostapd", 192 HOSTAPD_CONF_FILE, (char *) NULL)) { 193 LOGE("execl failed (%s)", strerror(errno)); 194 } 195#endif 196 LOGE("Should never get here!"); 197 return -1; 198 } else { 199 *mBuf = 0; 200 ret = setCommand(mIface, "AP_BSS_START"); 201 if (ret) { 202 LOGE("Softap startap - failed: %d", ret); 203 } 204 else { 205 mPid = pid; 206 LOGD("Softap startap - Ok"); 207 usleep(AP_BSS_START_DELAY); 208 } 209 } 210 return ret; 211 212} 213 214int SoftapController::stopSoftap() { 215 int ret; 216 217 if (mPid == 0) { 218 LOGE("Softap already stopped"); 219 return 0; 220 } 221 222#ifdef HAVE_HOSTAPD 223 LOGD("Stopping Softap service"); 224 kill(mPid, SIGTERM); 225 waitpid(mPid, NULL, 0); 226#endif 227 if (mSock < 0) { 228 LOGE("Softap stopap - failed to open socket"); 229 return -1; 230 } 231 *mBuf = 0; 232 ret = setCommand(mIface, "AP_BSS_STOP"); 233 mPid = 0; 234 LOGD("Softap service stopped: %d", ret); 235 usleep(AP_BSS_STOP_DELAY); 236 return ret; 237} 238 239bool SoftapController::isSoftapStarted() { 240 return (mPid != 0 ? true : false); 241} 242 243int SoftapController::addParam(int pos, const char *cmd, const char *arg) 244{ 245 if (pos < 0) 246 return pos; 247 if ((unsigned)(pos + strlen(cmd) + strlen(arg) + 1) >= sizeof(mBuf)) { 248 LOGE("Command line is too big"); 249 return -1; 250 } 251 pos += sprintf(&mBuf[pos], "%s=%s,", cmd, arg); 252 return pos; 253} 254 255/* 256 * Arguments: 257 * argv[2] - wlan interface 258 * argv[3] - softap interface 259 * argv[4] - SSID 260 * argv[5] - Security 261 * argv[6] - Key 262 * argv[7] - Channel 263 * argv[8] - Preamble 264 * argv[9] - Max SCB 265 */ 266int SoftapController::setSoftap(int argc, char *argv[]) { 267 char psk_str[2*SHA256_DIGEST_LENGTH+1]; 268 int ret = 0, i = 0, fd; 269 char *ssid, *iface; 270 271 if (mSock < 0) { 272 LOGE("Softap set - failed to open socket"); 273 return -1; 274 } 275 if (argc < 4) { 276 LOGE("Softap set - missing arguments"); 277 return -1; 278 } 279 280 strncpy(mIface, argv[3], sizeof(mIface)); 281 iface = argv[2]; 282 283#ifdef HAVE_HOSTAPD 284 char *wbuf = NULL; 285 char *fbuf = NULL; 286 287 if (argc > 4) { 288 ssid = argv[4]; 289 } else { 290 ssid = (char *)"AndroidAP"; 291 } 292 293 asprintf(&wbuf, "interface=%s\ndriver=nl80211\nctrl_interface=" 294 "/data/misc/wifi/hostapd\nssid=%s\nchannel=6\n", iface, ssid); 295 296 if (argc > 5) { 297 if (!strcmp(argv[5], "wpa-psk")) { 298 generatePsk(ssid, argv[6], psk_str); 299 asprintf(&fbuf, "%swpa=1\nwpa_psk=%s\n", wbuf, psk_str); 300 } else if (!strcmp(argv[5], "wpa2-psk")) { 301 generatePsk(ssid, argv[6], psk_str); 302 asprintf(&fbuf, "%swpa=2\nwpa_psk=%s\n", wbuf, psk_str); 303 } else if (!strcmp(argv[5], "open")) { 304 asprintf(&fbuf, "%s", wbuf); 305 } 306 } else { 307 asprintf(&fbuf, "%s", wbuf); 308 } 309 310 fd = open(HOSTAPD_CONF_FILE, O_CREAT | O_TRUNC | O_WRONLY, 0660); 311 if (fd < 0) { 312 LOGE("Cannot update \"%s\": %s", HOSTAPD_CONF_FILE, strerror(errno)); 313 free(wbuf); 314 free(fbuf); 315 return -1; 316 } 317 if (write(fd, fbuf, strlen(fbuf)) < 0) { 318 LOGE("Cannot write to \"%s\": %s", HOSTAPD_CONF_FILE, strerror(errno)); 319 ret = -1; 320 } 321 close(fd); 322 free(wbuf); 323 free(fbuf); 324 325 /* Note: apparently open can fail to set permissions correctly at times */ 326 if (chmod(HOSTAPD_CONF_FILE, 0660) < 0) { 327 LOGE("Error changing permissions of %s to 0660: %s", 328 HOSTAPD_CONF_FILE, strerror(errno)); 329 unlink(HOSTAPD_CONF_FILE); 330 return -1; 331 } 332 333 if (chown(HOSTAPD_CONF_FILE, AID_SYSTEM, AID_WIFI) < 0) { 334 LOGE("Error changing group ownership of %s to %d: %s", 335 HOSTAPD_CONF_FILE, AID_WIFI, strerror(errno)); 336 unlink(HOSTAPD_CONF_FILE); 337 return -1; 338 } 339 340#else 341 /* Create command line */ 342 i = addParam(i, "ASCII_CMD", "AP_CFG"); 343 if (argc > 4) { 344 ssid = argv[4]; 345 } else { 346 ssid = (char *)"AndroidAP"; 347 } 348 i = addParam(i, "SSID", ssid); 349 if (argc > 5) { 350 i = addParam(i, "SEC", argv[5]); 351 } else { 352 i = addParam(i, "SEC", "open"); 353 } 354 if (argc > 6) { 355 generatePsk(ssid, argv[6], psk_str); 356 i = addParam(i, "KEY", psk_str); 357 } else { 358 i = addParam(i, "KEY", "12345678"); 359 } 360 if (argc > 7) { 361 i = addParam(i, "CHANNEL", argv[7]); 362 } else { 363 i = addParam(i, "CHANNEL", "6"); 364 } 365 if (argc > 8) { 366 i = addParam(i, "PREAMBLE", argv[8]); 367 } else { 368 i = addParam(i, "PREAMBLE", "0"); 369 } 370 if (argc > 9) { 371 i = addParam(i, "MAX_SCB", argv[9]); 372 } else { 373 i = addParam(i, "MAX_SCB", "8"); 374 } 375 if ((i < 0) || ((unsigned)(i + 4) >= sizeof(mBuf))) { 376 LOGE("Softap set - command is too big"); 377 return i; 378 } 379 sprintf(&mBuf[i], "END"); 380 381 /* system("iwpriv eth0 WL_AP_CFG ASCII_CMD=AP_CFG,SSID=\"AndroidAP\",SEC=\"open\",KEY=12345,CHANNEL=1,PREAMBLE=0,MAX_SCB=8,END"); */ 382 ret = setCommand(iface, "AP_SET_CFG"); 383 if (ret) { 384 LOGE("Softap set - failed: %d", ret); 385 } 386 else { 387 LOGD("Softap set - Ok"); 388 usleep(AP_SET_CFG_DELAY); 389 } 390#endif 391 return ret; 392} 393 394void SoftapController::generatePsk(char *ssid, char *passphrase, char *psk_str) { 395 unsigned char psk[SHA256_DIGEST_LENGTH]; 396 int j; 397 // Use the PKCS#5 PBKDF2 with 4096 iterations 398 PKCS5_PBKDF2_HMAC_SHA1(passphrase, strlen(passphrase), 399 reinterpret_cast<const unsigned char *>(ssid), strlen(ssid), 400 4096, SHA256_DIGEST_LENGTH, psk); 401 for (j=0; j < SHA256_DIGEST_LENGTH; j++) { 402 sprintf(&psk_str[j<<1], "%02x", psk[j]); 403 } 404 psk_str[j<<1] = '\0'; 405} 406 407 408/* 409 * Arguments: 410 * argv[2] - interface name 411 * argv[3] - AP or STA 412 */ 413int SoftapController::fwReloadSoftap(int argc, char *argv[]) 414{ 415 int ret, i = 0; 416 char *iface; 417 char *fwpath; 418 419 if (mSock < 0) { 420 LOGE("Softap fwrealod - failed to open socket"); 421 return -1; 422 } 423 if (argc < 4) { 424 LOGE("Softap fwreload - missing arguments"); 425 return -1; 426 } 427 428 iface = argv[2]; 429 430 if (strcmp(argv[3], "AP") == 0) { 431 fwpath = (char *)wifi_get_fw_path(WIFI_GET_FW_PATH_AP); 432 } else { 433 fwpath = (char *)wifi_get_fw_path(WIFI_GET_FW_PATH_STA); 434 } 435 if (!fwpath) 436 return -1; 437#ifdef HAVE_HOSTAPD 438 ret = wifi_change_fw_path((const char *)fwpath); 439#else 440 sprintf(mBuf, "FW_PATH=%s", fwpath); 441 ret = setCommand(iface, "WL_FW_RELOAD"); 442#endif 443 if (ret) { 444 LOGE("Softap fwReload - failed: %d", ret); 445 } 446 else { 447 LOGD("Softap fwReload - Ok"); 448 } 449 return ret; 450} 451 452int SoftapController::clientsSoftap(char **retbuf) 453{ 454 int ret; 455 456 if (mSock < 0) { 457 LOGE("Softap clients - failed to open socket"); 458 return -1; 459 } 460 *mBuf = 0; 461 ret = setCommand(mIface, "AP_GET_STA_LIST", SOFTAP_MAX_BUFFER_SIZE); 462 if (ret) { 463 LOGE("Softap clients - failed: %d", ret); 464 } else { 465 asprintf(retbuf, "Softap clients:%s", mBuf); 466 LOGD("Softap clients:%s", mBuf); 467 } 468 return ret; 469} 470