• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "../fork.h"
20 #include "log.h"
21 
22 #include <cutils/properties.h>
23 #include <errno.h>
24 #include <net/if.h>
25 #include <netinet/in.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <sys/ioctl.h>
29 #include <sys/socket.h>
30 #include <sys/types.h>
31 #include <unistd.h>
32 
33 static const char kHostApdStubFile[] = "/vendor/etc/simulated_hostapd.conf";
34 static const char kHostApdConfFile[] = "/data/vendor/wifi/hostapd/hostapd.conf";
35 
36 static const char kControlRestartProperty[] = "ctl.restart";
37 static const char kHostApdServiceName[] = "emu_hostapd";
38 
39 static const char kIfNamePrefix[] = "wlan1_";
40 
41 class File {
42 public:
File(FILE * file)43     explicit File(FILE* file) : mFile(file) {}
~File()44     ~File() {
45         if (mFile) {
46             fclose(mFile);
47         }
48     }
49 
get()50     FILE* get() { return mFile; }
51 
operator !() const52     bool operator!() const { return mFile == nullptr; }
53 private:
54     FILE* mFile;
55 };
56 
57 class Fd {
58 public:
Fd(int fd)59     explicit Fd(int fd) : mFd(fd) {}
~Fd()60     ~Fd() {
61         if (mFd != -1) {
62             ::close(mFd);
63             mFd = -1;
64         }
65     }
66 
get() const67     int get() const { return mFd; }
68 
69 private:
70     int mFd;
71 };
72 
explode(const char * str)73 std::vector<std::string> explode(const char* str) {
74     const char* cur = str;
75     const char* space = nullptr;
76     std::vector<std::string> result;
77     do {
78         space = ::strchr(cur, ' ');
79         if (space) {
80             result.emplace_back(cur, space);
81             cur = space + 1;
82         } else {
83             result.emplace_back(cur);
84         }
85     } while (space);
86 
87     return result;
88 }
89 
WifiCommand()90 WifiCommand::WifiCommand() : mLowestInterfaceNumber(1) {
91     readConfig();
92 }
93 
onCommand(const char *,const char * args)94 Result WifiCommand::onCommand(const char* /*command*/, const char* args) {
95     const char* divider = ::strchr(args, ' ');
96     if (divider == nullptr) {
97         // Unknown command, every command needs an argument
98         return Result::error("Invalid wifi command '%s'", args);
99     }
100 
101     std::string subCommand(args, divider);
102     if (subCommand.empty()) {
103         return Result::error("Empty wifi command");
104     }
105 
106     std::vector<std::string> subArgs = explode(divider + 1);
107     if (subArgs.empty()) {
108         // All of these commands require sub arguments
109         return Result::error("Missing argument to command '%s'",
110                              subCommand.c_str());
111     }
112 
113     if (subCommand == "add") {
114         return onAdd(subArgs);
115     } else if (subCommand == "block") {
116         return onBlock(subArgs);
117     } else if (subCommand == "unblock") {
118         return onUnblock(subArgs);
119     } else {
120         return Result::error("Unknown wifi command '%s'", subCommand.c_str());
121     }
122 }
123 
readConfig()124 void WifiCommand::readConfig() {
125 }
126 
writeConfig()127 Result WifiCommand::writeConfig() {
128     File in(fopen(kHostApdStubFile, "r"));
129     if (!in) {
130         return Result::error("Config failure: could not open template: %s",
131                              strerror(errno));
132     }
133 
134     File out(fopen(kHostApdConfFile, "w"));
135     if (!out) {
136         return Result::error("Config failure: could not open target: %s",
137                              strerror(errno));
138     }
139 
140     char buffer[32768];
141     while (!feof(in.get())) {
142         size_t bytesRead = fread(buffer, 1, sizeof(buffer), in.get());
143         if (bytesRead != sizeof(buffer) && ferror(in.get())) {
144             return Result::error("Config failure: Error reading template: %s",
145                                  strerror(errno));
146         }
147 
148         size_t bytesWritten = fwrite(buffer, 1, bytesRead, out.get());
149         if (bytesWritten != bytesRead) {
150             return Result::error("Config failure: Error writing target: %s",
151                                  strerror(errno));
152         }
153     }
154     fprintf(out.get(), "\n\n");
155 
156     for (const auto& ap : mAccessPoints) {
157         fprintf(out.get(), "bss=%s\n", ap.second.ifName.c_str());
158         fprintf(out.get(), "ssid=%s\n", ap.second.ssid.c_str());
159         if (!ap.second.password.empty()) {
160             fprintf(out.get(), "wpa=2\n");
161             fprintf(out.get(), "wpa_key_mgmt=WPA-PSK\n");
162             fprintf(out.get(), "rsn_pairwise=CCMP\n");
163             fprintf(out.get(), "wpa_passphrase=%s\n", ap.second.password.c_str());
164         }
165         fprintf(out.get(), "\n");
166     }
167     return Result::success();
168 }
169 
triggerHostApd()170 Result WifiCommand::triggerHostApd() {
171     property_set(kControlRestartProperty, kHostApdServiceName);
172     return Result::success();
173 }
174 
175 static const char* sSetForwardRule[] = {"/system/bin/iptables",
176                                         "-w",    // Wait for iptables lock if
177                                         "-W",    // needed. This prevents
178                                         "50000", // spurious failures.
179                                         "<AddOrDelete>", // To be replaced
180                                         "FORWARD",
181                                         "-i",
182                                         "<InInterface>", // To be replaced
183                                         "-o",
184                                         "<OutInterface>", // To be replaced
185                                         "-j",
186                                         "DROP",
187                                         nullptr };
188 
189 static const char kIpTables[] = "/system/bin/iptables";
190 static const char kIp6Tables[] = "/system/bin/ip6tables";
191 static const char kAddRule[] = "-A";
192 static const char kDeleteRule[] = "-D";
193 static const size_t kIpTablesIndex = 0;
194 static const size_t kActionIndex = 4;
195 static const size_t kInInterfaceIndex = 7;
196 static const size_t kOutInterfaceIndex = 9;
197 
198 
setBlocked(const char * ifName,bool blocked)199 Result WifiCommand::setBlocked(const char* ifName, bool blocked) {
200     // Blocking means adding block rules, unblocking means removing them
201     sSetForwardRule[kActionIndex] = blocked ? kAddRule : kDeleteRule;
202 
203     // Do this for both IPv4 and IPv6 to ensure all traffic is blocked/unblocked
204     for (const auto& iptables : { kIpTables, kIp6Tables }) {
205         // Block traffic coming in from the outside world to this wlan
206         sSetForwardRule[kIpTablesIndex] = iptables;
207         sSetForwardRule[kInInterfaceIndex] = "eth0";
208         sSetForwardRule[kOutInterfaceIndex] = ifName;
209         if (!forkAndExec(sSetForwardRule)) {
210             return Result::error("Internal error: Unable to %s network",
211                                  blocked ? "block" : "unblock");
212         }
213         // Block traffic going from the wlan to the outside world
214         sSetForwardRule[kInInterfaceIndex] = ifName;
215         sSetForwardRule[kOutInterfaceIndex] = "eth0";
216         if (!forkAndExec(sSetForwardRule)) {
217             return Result::error("Internal error: Unable to %s network",
218                                  blocked ? "block" : "unblock");
219         }
220     }
221     return Result::success();
222 }
223 
onAdd(const std::vector<std::string> & arguments)224 Result WifiCommand::onAdd(const std::vector<std::string>& arguments) {
225     AccessPoint& ap = mAccessPoints[arguments[0]];
226     ap.ssid = arguments[0];
227     if (arguments.size() > 1) {
228         ap.password = arguments[1];
229     } else {
230         ap.password.clear();
231     }
232     if (ap.ifName.empty()) {
233         char buffer[sizeof(kIfNamePrefix) + 10];
234         while (true) {
235             snprintf(buffer, sizeof(buffer), "%s%d",
236                      kIfNamePrefix, mLowestInterfaceNumber);
237             ap.ifName = buffer;
238             auto usedInterface = mUsedInterfaces.find(ap.ifName);
239             if (usedInterface == mUsedInterfaces.end()) {
240                 // This interface is available, use it
241                 ++mLowestInterfaceNumber;
242                 mUsedInterfaces.insert(ap.ifName);
243                 break;
244             }
245             // The interface name was alread in use, try the next one
246             ++mLowestInterfaceNumber;
247         }
248     }
249     Result res = writeConfig();
250     if (!res) {
251         return res;
252     }
253     return triggerHostApd();
254 }
255 
onBlock(const std::vector<std::string> & arguments)256 Result WifiCommand::onBlock(const std::vector<std::string>& arguments) {
257     auto interface = mAccessPoints.find(arguments[0]);
258     if (interface == mAccessPoints.end()) {
259         return Result::error("Unknown SSID '%s", arguments[0].c_str());
260     }
261     interface->second.blocked = true;
262     return setBlocked(interface->second.ifName.c_str(), true);
263 }
264 
onUnblock(const std::vector<std::string> & arguments)265 Result WifiCommand::onUnblock(const std::vector<std::string>& arguments) {
266     auto interface = mAccessPoints.find(arguments[0]);
267     if (interface == mAccessPoints.end()) {
268         return Result::error("Unknown SSID '%s", arguments[0].c_str());
269     }
270     interface->second.blocked = false;
271     return setBlocked(interface->second.ifName.c_str(), false);
272 }
273 
274