blob: 27bf95a86f9cd9f19699067c9abd86375c84de63 [file] [log] [blame]
/*
* Copyright 2018, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "wifi_command.h"
#include "../bridge.h"
#include "../utils.h"
#include <cutils/properties.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
static const char kHostApdStubFile[] = "/vendor/etc/simulated_hostapd.conf";
static const char kHostApdConfFile[] = "/data/vendor/wifi/hostapd/hostapd.conf";
static const char kControlRestartProperty[] = "ctl.restart";
static const char kHostApdServiceName[] = "emu_hostapd";
static const char kIfNamePrefix[] = "wlan1_";
class File {
public:
explicit File(FILE* file) : mFile(file) {}
~File() {
if (mFile) {
fclose(mFile);
}
}
FILE* get() { return mFile; }
bool operator!() const { return mFile == nullptr; }
private:
FILE* mFile;
};
class Fd {
public:
explicit Fd(int fd) : mFd(fd) {}
~Fd() {
if (mFd != -1) {
::close(mFd);
mFd = -1;
}
}
int get() const { return mFd; }
private:
int mFd;
};
WifiCommand::WifiCommand(Bridge& bridge)
: mBridge(bridge)
, mLowestInterfaceNumber(1) {
readConfig();
}
Result WifiCommand::onCommand(const char* /*command*/, const char* args) {
const char* divider = ::strchr(args, ' ');
if (divider == nullptr) {
// Unknown command, every command needs an argument
return Result::error("Invalid wifi command '%s'", args);
}
std::string subCommand(args, divider);
if (subCommand.empty()) {
return Result::error("Empty wifi command");
}
std::vector<std::string> subArgs = explode(divider + 1, ' ');
if (subArgs.empty()) {
// All of these commands require sub arguments
return Result::error("Missing argument to command '%s'",
subCommand.c_str());
}
if (subCommand == "add") {
return onAdd(subArgs);
} else if (subCommand == "block") {
return onBlock(subArgs);
} else if (subCommand == "unblock") {
return onUnblock(subArgs);
} else {
return Result::error("Unknown wifi command '%s'", subCommand.c_str());
}
}
void WifiCommand::readConfig() {
}
Result WifiCommand::writeConfig() {
File in(fopen(kHostApdStubFile, "r"));
if (!in) {
return Result::error("Config failure: could not open template: %s",
strerror(errno));
}
File out(fopen(kHostApdConfFile, "w"));
if (!out) {
return Result::error("Config failure: could not open target: %s",
strerror(errno));
}
char buffer[32768];
while (!feof(in.get())) {
size_t bytesRead = fread(buffer, 1, sizeof(buffer), in.get());
if (bytesRead != sizeof(buffer) && ferror(in.get())) {
return Result::error("Config failure: Error reading template: %s",
strerror(errno));
}
size_t bytesWritten = fwrite(buffer, 1, bytesRead, out.get());
if (bytesWritten != bytesRead) {
return Result::error("Config failure: Error writing target: %s",
strerror(errno));
}
}
fprintf(out.get(), "\n\n");
for (const auto& ap : mAccessPoints) {
fprintf(out.get(), "bss=%s\n", ap.second.ifName.c_str());
fprintf(out.get(), "ssid=%s\n", ap.second.ssid.c_str());
if (!ap.second.password.empty()) {
fprintf(out.get(), "wpa=2\n");
fprintf(out.get(), "wpa_key_mgmt=WPA-PSK\n");
fprintf(out.get(), "rsn_pairwise=CCMP\n");
fprintf(out.get(), "wpa_passphrase=%s\n", ap.second.password.c_str());
}
fprintf(out.get(), "\n");
}
return Result::success();
}
Result WifiCommand::triggerHostApd() {
property_set(kControlRestartProperty, kHostApdServiceName);
return Result::success();
}
Result WifiCommand::onAdd(const std::vector<std::string>& arguments) {
AccessPoint& ap = mAccessPoints[arguments[0]];
ap.ssid = arguments[0];
if (arguments.size() > 1) {
ap.password = arguments[1];
} else {
ap.password.clear();
}
if (ap.ifName.empty()) {
char buffer[sizeof(kIfNamePrefix) + 10];
while (true) {
snprintf(buffer, sizeof(buffer), "%s%d",
kIfNamePrefix, mLowestInterfaceNumber);
ap.ifName = buffer;
auto usedInterface = mUsedInterfaces.find(ap.ifName);
if (usedInterface == mUsedInterfaces.end()) {
// This interface is available, use it
++mLowestInterfaceNumber;
mUsedInterfaces.insert(ap.ifName);
break;
}
// The interface name was alread in use, try the next one
++mLowestInterfaceNumber;
}
}
Result res = writeConfig();
if (!res) {
return res;
}
return triggerHostApd();
}
Result WifiCommand::onBlock(const std::vector<std::string>& arguments) {
auto interface = mAccessPoints.find(arguments[0]);
if (interface == mAccessPoints.end()) {
return Result::error("Unknown SSID '%s'", arguments[0].c_str());
}
interface->second.blocked = true;
return mBridge.removeInterface(interface->second.ifName.c_str());
}
Result WifiCommand::onUnblock(const std::vector<std::string>& arguments) {
auto interface = mAccessPoints.find(arguments[0]);
if (interface == mAccessPoints.end()) {
return Result::error("Unknown SSID '%s'", arguments[0].c_str());
}
interface->second.blocked = false;
return mBridge.addInterface(interface->second.ifName.c_str());
}