1 /*
2 * Copyright 2018, 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 "wifi_command.h"
18
19 #include "../bridge.h"
20 #include "../utils.h"
21
22 #include <cutils/properties.h>
23 #include <errno.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include <unistd.h>
27
28 static const char kHostApdStubFile[] = "/vendor/etc/simulated_hostapd.conf";
29 static const char kHostApdConfFile[] = "/data/vendor/wifi/hostapd/hostapd.conf";
30
31 static const char kControlRestartProperty[] = "ctl.restart";
32 static const char kHostApdServiceName[] = "emu_hostapd";
33
34 static const char kIfNamePrefix[] = "wlan1_";
35
36 class File {
37 public:
File(FILE * file)38 explicit File(FILE* file) : mFile(file) {}
~File()39 ~File() {
40 if (mFile) {
41 fclose(mFile);
42 }
43 }
44
get()45 FILE* get() { return mFile; }
46
operator !() const47 bool operator!() const { return mFile == nullptr; }
48 private:
49 FILE* mFile;
50 };
51
52 class Fd {
53 public:
Fd(int fd)54 explicit Fd(int fd) : mFd(fd) {}
~Fd()55 ~Fd() {
56 if (mFd != -1) {
57 ::close(mFd);
58 mFd = -1;
59 }
60 }
61
get() const62 int get() const { return mFd; }
63
64 private:
65 int mFd;
66 };
67
WifiCommand(Bridge & bridge)68 WifiCommand::WifiCommand(Bridge& bridge)
69 : mBridge(bridge)
70 , mLowestInterfaceNumber(1) {
71 readConfig();
72 }
73
onCommand(const char *,const char * args)74 Result WifiCommand::onCommand(const char* /*command*/, const char* args) {
75 const char* divider = ::strchr(args, ' ');
76 if (divider == nullptr) {
77 // Unknown command, every command needs an argument
78 return Result::error("Invalid wifi command '%s'", args);
79 }
80
81 std::string subCommand(args, divider);
82 if (subCommand.empty()) {
83 return Result::error("Empty wifi command");
84 }
85
86 std::vector<std::string> subArgs = explode(divider + 1, ' ');
87 if (subArgs.empty()) {
88 // All of these commands require sub arguments
89 return Result::error("Missing argument to command '%s'",
90 subCommand.c_str());
91 }
92
93 if (subCommand == "add") {
94 return onAdd(subArgs);
95 } else if (subCommand == "block") {
96 return onBlock(subArgs);
97 } else if (subCommand == "unblock") {
98 return onUnblock(subArgs);
99 } else {
100 return Result::error("Unknown wifi command '%s'", subCommand.c_str());
101 }
102 }
103
readConfig()104 void WifiCommand::readConfig() {
105 }
106
writeConfig()107 Result WifiCommand::writeConfig() {
108 File in(fopen(kHostApdStubFile, "r"));
109 if (!in) {
110 return Result::error("Config failure: could not open template: %s",
111 strerror(errno));
112 }
113
114 File out(fopen(kHostApdConfFile, "w"));
115 if (!out) {
116 return Result::error("Config failure: could not open target: %s",
117 strerror(errno));
118 }
119
120 char buffer[32768];
121 while (!feof(in.get())) {
122 size_t bytesRead = fread(buffer, 1, sizeof(buffer), in.get());
123 if (bytesRead != sizeof(buffer) && ferror(in.get())) {
124 return Result::error("Config failure: Error reading template: %s",
125 strerror(errno));
126 }
127
128 size_t bytesWritten = fwrite(buffer, 1, bytesRead, out.get());
129 if (bytesWritten != bytesRead) {
130 return Result::error("Config failure: Error writing target: %s",
131 strerror(errno));
132 }
133 }
134 fprintf(out.get(), "\n\n");
135
136 for (const auto& ap : mAccessPoints) {
137 fprintf(out.get(), "bss=%s\n", ap.second.ifName.c_str());
138 fprintf(out.get(), "ssid=%s\n", ap.second.ssid.c_str());
139 if (!ap.second.password.empty()) {
140 fprintf(out.get(), "wpa=2\n");
141 fprintf(out.get(), "wpa_key_mgmt=WPA-PSK\n");
142 fprintf(out.get(), "rsn_pairwise=CCMP\n");
143 fprintf(out.get(), "wpa_passphrase=%s\n", ap.second.password.c_str());
144 }
145 fprintf(out.get(), "\n");
146 }
147 return Result::success();
148 }
149
triggerHostApd()150 Result WifiCommand::triggerHostApd() {
151 property_set(kControlRestartProperty, kHostApdServiceName);
152 return Result::success();
153 }
154
onAdd(const std::vector<std::string> & arguments)155 Result WifiCommand::onAdd(const std::vector<std::string>& arguments) {
156 AccessPoint& ap = mAccessPoints[arguments[0]];
157 ap.ssid = arguments[0];
158 if (arguments.size() > 1) {
159 ap.password = arguments[1];
160 } else {
161 ap.password.clear();
162 }
163 if (ap.ifName.empty()) {
164 char buffer[sizeof(kIfNamePrefix) + 10];
165 while (true) {
166 snprintf(buffer, sizeof(buffer), "%s%d",
167 kIfNamePrefix, mLowestInterfaceNumber);
168 ap.ifName = buffer;
169 auto usedInterface = mUsedInterfaces.find(ap.ifName);
170 if (usedInterface == mUsedInterfaces.end()) {
171 // This interface is available, use it
172 ++mLowestInterfaceNumber;
173 mUsedInterfaces.insert(ap.ifName);
174 break;
175 }
176 // The interface name was alread in use, try the next one
177 ++mLowestInterfaceNumber;
178 }
179 }
180 Result res = writeConfig();
181 if (!res) {
182 return res;
183 }
184 return triggerHostApd();
185 }
186
onBlock(const std::vector<std::string> & arguments)187 Result WifiCommand::onBlock(const std::vector<std::string>& arguments) {
188 auto interface = mAccessPoints.find(arguments[0]);
189 if (interface == mAccessPoints.end()) {
190 return Result::error("Unknown SSID '%s'", arguments[0].c_str());
191 }
192 interface->second.blocked = true;
193 return mBridge.removeInterface(interface->second.ifName.c_str());
194 }
195
onUnblock(const std::vector<std::string> & arguments)196 Result WifiCommand::onUnblock(const std::vector<std::string>& arguments) {
197 auto interface = mAccessPoints.find(arguments[0]);
198 if (interface == mAccessPoints.end()) {
199 return Result::error("Unknown SSID '%s'", arguments[0].c_str());
200 }
201 interface->second.blocked = false;
202 return mBridge.addInterface(interface->second.ifName.c_str());
203 }
204
205