summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBjoern Johansson <bjoernj@google.com>2018-11-05 15:36:18 -0800
committerBjoern Johansson <bjoernj@google.com>2018-11-05 16:05:42 -0800
commitaa76124b9f5fddd3279bb144267fda7a29aa9afe (patch)
tree40ddfe92482c4903543dd67a7ffb7842e43f491a
parent873eee6422fdd7237b9bf86edb37f2958b23829e (diff)
downloadandroid_device_generic_goldfish-aa76124b9f5fddd3279bb144267fda7a29aa9afe.tar.gz
android_device_generic_goldfish-aa76124b9f5fddd3279bb144267fda7a29aa9afe.tar.bz2
android_device_generic_goldfish-aa76124b9f5fddd3279bb144267fda7a29aa9afe.zip
Make netmgr support WiFi direct by forwarding
In order to support WiFi Direct we need two devices to be able to send raw WiFi frames between each other. This is to emulate the situation where the WiFi radios in the devices are able to talk to each other directly without any access point or established networks. The way this is done is by reading WiFi frames from the monitor interface provided by the virtual WiFi driver and then transporting them to another emulator. On the other emulator the frames are then injected into the virtual WiFi driver's monitor interface. The frames are read from the monitor interface and then transported out of Android using the QEMU pipe. Similarly data from the other emulator is read from the QEMU pipe and then injected on the monitor interface. BUG: 111996306 Test: Run CTS Verifier WiFi Direct tests Change-Id: I4f8abf483776ae97a1b6184d012b3a23e55e87a2
-rw-r--r--network/netmgr/Android.bp6
-rw-r--r--network/netmgr/commander.cpp29
-rw-r--r--network/netmgr/commander.h9
-rw-r--r--network/netmgr/commands/wifi_command.cpp2
-rw-r--r--network/netmgr/log.h9
-rw-r--r--network/netmgr/macaddress.h53
-rw-r--r--network/netmgr/main.cpp11
-rw-r--r--network/netmgr/monitor.cpp37
-rw-r--r--network/netmgr/monitor.h9
-rw-r--r--network/netmgr/pollable.h42
-rw-r--r--network/netmgr/poller.cpp92
-rw-r--r--network/netmgr/wifi_forwarder.cpp429
-rw-r--r--network/netmgr/wifi_forwarder.h53
-rw-r--r--sepolicy/common/goldfish_setup.te1
-rw-r--r--sepolicy/common/netmgr.te8
-rwxr-xr-xwifi/init.wifi.sh27
-rw-r--r--wifi/wpa_supplicant.conf1
17 files changed, 740 insertions, 78 deletions
diff --git a/network/netmgr/Android.bp b/network/netmgr/Android.bp
index ab177e6..6686f98 100644
--- a/network/netmgr/Android.bp
+++ b/network/netmgr/Android.bp
@@ -17,6 +17,10 @@
cc_binary {
name: "netmgr",
vendor: true,
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
srcs: [
"address_assigner.cpp",
"commander.cpp",
@@ -26,11 +30,13 @@ cc_binary {
"main.cpp",
"monitor.cpp",
"poller.cpp",
+ "wifi_forwarder.cpp",
"commands/wifi_command.cpp",
],
shared_libs: [
"libcutils",
"liblog",
+ "libpcap",
],
header_libs: [
"goldfish_headers",
diff --git a/network/netmgr/commander.cpp b/network/netmgr/commander.cpp
index 82ed95e..a2dc1aa 100644
--- a/network/netmgr/commander.cpp
+++ b/network/netmgr/commander.cpp
@@ -20,7 +20,10 @@
#include "log.h"
#include <errno.h>
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-function"
#include <qemu_pipe.h>
+#pragma clang diagnostic pop
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
@@ -54,12 +57,17 @@ void Commander::registerCommand(const char* commandStr, Command* command) {
mCommands[commandStr] = command;
}
-Pollable::Data Commander::data() const {
- return Data { mPipeFd, mDeadline };
+void Commander::getPollData(std::vector<pollfd>* fds) const {
+ if (mPipeFd != -1) {
+ fds->push_back(pollfd{mPipeFd, POLLIN, 0});
+ }
}
-void Commander::onReadAvailable() {
+Pollable::Timestamp Commander::getTimeout() const {
+ return mDeadline;
+}
+bool Commander::onReadAvailable(int /*fd*/, int* /*status*/) {
size_t offset = mReceiveBuffer.size();
mReceiveBuffer.resize(offset + kReceiveSpace);
if (mReceiveBuffer.size() > kMaxReceiveBufferSize) {
@@ -78,7 +86,8 @@ void Commander::onReadAvailable() {
continue;
}
LOGE("Commander failed to receive on pipe: %s", strerror(errno));
- return;
+ // Don't exit the looper because of this, keep trying
+ return true;
}
size_t length = static_cast<size_t>(status);
mReceiveBuffer.resize(offset + length);
@@ -89,7 +98,7 @@ void Commander::onReadAvailable() {
'\n');
if (endline == mReceiveBuffer.end()) {
// No endline in sight, keep waiting and buffering
- return;
+ return true;
}
*endline = '\0';
@@ -109,27 +118,29 @@ void Commander::onReadAvailable() {
if (endline == mReceiveBuffer.end()) {
mReceiveBuffer.clear();
// There can be nothing left, just return
- return;
+ return true;
} else {
mReceiveBuffer.erase(mReceiveBuffer.begin(), endline + 1);
// There may be another line in there so keep looping and look
// for more
}
}
- return;
+ return true;
}
}
-void Commander::onClose() {
+bool Commander::onClose(int /*fd*/, int* /*status*/) {
// Pipe was closed from the other end, close it on our side and re-open
closePipe();
openPipe();
+ return true;
}
-void Commander::onTimeout() {
+bool Commander::onTimeout(int* /*status*/) {
if (mPipeFd == -1) {
openPipe();
}
+ return true;
}
void Commander::openPipe() {
diff --git a/network/netmgr/commander.h b/network/netmgr/commander.h
index 9f33526..7f7fc7e 100644
--- a/network/netmgr/commander.h
+++ b/network/netmgr/commander.h
@@ -33,10 +33,11 @@ public:
void registerCommand(const char* commandStr, Command* command);
- Pollable::Data data() const override;
- void onReadAvailable() override;
- void onClose() override;
- void onTimeout() override;
+ void getPollData(std::vector<pollfd>* fds) const override;
+ Timestamp getTimeout() const override;
+ bool onReadAvailable(int fd, int* status) override;
+ bool onClose(int fd, int* status) override;
+ bool onTimeout(int* status) override;
private:
void openPipe();
void closePipe();
diff --git a/network/netmgr/commands/wifi_command.cpp b/network/netmgr/commands/wifi_command.cpp
index 0d0be19..8f48a43 100644
--- a/network/netmgr/commands/wifi_command.cpp
+++ b/network/netmgr/commands/wifi_command.cpp
@@ -91,7 +91,7 @@ WifiCommand::WifiCommand() : mLowestInterfaceNumber(1) {
readConfig();
}
-Result WifiCommand::onCommand(const char* command, const char* args) {
+Result WifiCommand::onCommand(const char* /*command*/, const char* args) {
const char* divider = ::strchr(args, ' ');
if (divider == nullptr) {
// Unknown command, every command needs an argument
diff --git a/network/netmgr/log.h b/network/netmgr/log.h
index e722091..1efe72a 100644
--- a/network/netmgr/log.h
+++ b/network/netmgr/log.h
@@ -34,3 +34,12 @@ void initIsTerminal();
} \
} while (0)
+#define LOGW(...) do { \
+ if (isTerminal) { \
+ fprintf(stderr, __VA_ARGS__); \
+ fprintf(stderr, "\n"); \
+ } else { \
+ ALOGW(__VA_ARGS__); \
+ } \
+} while (0)
+
diff --git a/network/netmgr/macaddress.h b/network/netmgr/macaddress.h
new file mode 100644
index 0000000..9363859
--- /dev/null
+++ b/network/netmgr/macaddress.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2017 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.
+ * 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.
+ */
+
+#pragma once
+
+#include <functional>
+
+#include <linux/if_ether.h>
+#include <stdint.h>
+#include <string.h>
+
+struct MacAddress {
+ uint8_t addr[ETH_ALEN];
+ bool isBroadcast() const {
+ return memcmp(addr, "\xFF\xFF\xFF\xFF\xFF\xFF", ETH_ALEN) == 0;
+ }
+} __attribute__((__packed__));
+
+template<class T>
+inline void hash_combine(size_t& seed, const T& value) {
+ std::hash<T> hasher;
+ seed ^= hasher(value) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
+}
+
+namespace std {
+template<> struct hash<MacAddress> {
+ size_t operator()(const MacAddress& addr) const {
+ size_t seed = 0;
+ // Treat the first 4 bytes as an uint32_t to save some computation
+ hash_combine(seed, *reinterpret_cast<const uint32_t*>(addr.addr));
+ // And the remaining 2 bytes as an uint16_t
+ hash_combine(seed, *reinterpret_cast<const uint16_t*>(addr.addr + 4));
+ return seed;
+ }
+};
+}
+
diff --git a/network/netmgr/main.cpp b/network/netmgr/main.cpp
index c6e20bc..e4b43f6 100644
--- a/network/netmgr/main.cpp
+++ b/network/netmgr/main.cpp
@@ -20,12 +20,15 @@
#include "log.h"
#include "monitor.h"
#include "poller.h"
+#include "wifi_forwarder.h"
#include <arpa/inet.h>
#include <netinet/in.h>
#include <functional>
+static const char kWifiMonitorInterface[] = "hwsim0";
+
static void usage(const char* name) {
LOGE("Usage: %s --if-prefix <prefix> --network <ip/mask>", name);
LOGE(" <prefix> indicates the name of network interfaces to configure.");
@@ -131,9 +134,17 @@ int main(int argc, char* argv[]) {
WifiCommand wifiCommand;
commander.registerCommand("wifi", &wifiCommand);
+ WifiForwarder forwarder(kWifiMonitorInterface);
+ res = forwarder.init();
+ if (!res) {
+ LOGE("%s", res.c_str());
+ return 1;
+ }
+
Poller poller;
poller.addPollable(&monitor);
poller.addPollable(&commander);
+ poller.addPollable(&forwarder);
return poller.run();
}
diff --git a/network/netmgr/monitor.cpp b/network/netmgr/monitor.cpp
index 492e7c6..4ab111e 100644
--- a/network/netmgr/monitor.cpp
+++ b/network/netmgr/monitor.cpp
@@ -44,7 +44,7 @@ void Monitor::setOnInterfaceState(OnInterfaceStateCallback callback) {
mOnInterfaceStateCallback = callback;
}
-void Monitor::onReadAvailable() {
+bool Monitor::onReadAvailable(int /*fd*/, int* /*status*/) {
char buffer[32768];
struct sockaddr_storage storage;
@@ -56,16 +56,21 @@ void Monitor::onReadAvailable() {
MSG_DONTWAIT,
reinterpret_cast<struct sockaddr*>(&storage),
&addrSize);
- if (status < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
- // Nothing to receive, everything is fine
- return;
- } else if (status < 0) {
+ if (status < 0) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ // Nothing to receive, everything is fine
+ return true;
+ } else if (errno == EINTR) {
+ continue;
+ }
LOGE("Monitor receive failed: %s", strerror(errno));
- return;
+ // An error occurred but let's keep trying
+ return true;
} else if (addrSize < 0 ||
static_cast<size_t>(addrSize) != sizeof(struct sockaddr_nl)) {
LOGE("Monitor received invalid address size");
- return;
+ // It's an error but no need to exit, let's keep polling
+ return true;
}
size_t length = static_cast<size_t>(status);
@@ -84,20 +89,30 @@ void Monitor::onReadAvailable() {
}
}
-void Monitor::onClose() {
+bool Monitor::onClose(int /*fd*/, int* status) {
// Socket was closed from the other end, close it from our end and re-open
closeSocket();
Result res = openSocket();
if (!res) {
LOGE("%s", res.c_str());
+ *status = 1;
+ return false;
}
+ return true;
+}
+
+bool Monitor::onTimeout(int* /*status*/) {
+ return true;
}
-void Monitor::onTimeout() {
+void Monitor::getPollData(std::vector<pollfd>* fds) const {
+ if (mSocketFd != -1) {
+ fds->push_back(pollfd{mSocketFd, POLLIN, 0});
+ }
}
-Pollable::Data Monitor::data() const {
- return Pollable::Data{ mSocketFd, Pollable::Timestamp::max() };
+Pollable::Timestamp Monitor::getTimeout() const {
+ return Pollable::Timestamp::max();
}
Result Monitor::openSocket() {
diff --git a/network/netmgr/monitor.h b/network/netmgr/monitor.h
index d75bac7..3d2dc62 100644
--- a/network/netmgr/monitor.h
+++ b/network/netmgr/monitor.h
@@ -38,10 +38,11 @@ public:
void setOnInterfaceState(OnInterfaceStateCallback callback);
// Pollable interface
- void onReadAvailable() override;
- void onClose() override;
- void onTimeout() override;
- Pollable::Data data() const override;
+ void getPollData(std::vector<pollfd>* fds) const override;
+ Timestamp getTimeout() const override;
+ bool onReadAvailable(int fd, int* status) override;
+ bool onClose(int fd, int* status) override;
+ bool onTimeout(int* status) override;
private:
Result openSocket();
diff --git a/network/netmgr/pollable.h b/network/netmgr/pollable.h
index fe87a41..fbf4baf 100644
--- a/network/netmgr/pollable.h
+++ b/network/netmgr/pollable.h
@@ -17,20 +17,46 @@
#pragma once
#include <chrono>
+#include <vector>
+#include <poll.h>
+
+/* An interface for pollable classes.
+ */
class Pollable {
public:
using Clock = std::chrono::steady_clock;
using Timestamp = Clock::time_point;
- struct Data {
- int fd;
- Timestamp deadline;
- };
virtual ~Pollable() = default;
- virtual Data data() const = 0;
- virtual void onReadAvailable() = 0;
- virtual void onClose() = 0;
- virtual void onTimeout() = 0;
+ /* Get the poll data for the next poll loop. The implementation can place
+ * as many fds as needed in |fds|.
+ */
+ virtual void getPollData(std::vector<pollfd>* fds) const = 0;
+ /* Get the timeout for the next poll loop. This should be a timestamp
+ * indicating when the timeout should be triggered. Note that this may
+ * be called at any time and any number of times for a poll loop so the
+ * deadline should not be adjusted in this call, a set deadline should
+ * just be returned. Note specifically that if a call to onReadAvailable
+ * modifies the deadline the timeout for the previous timestamp might not
+ * fire as the poller will check the timestamp AFTER onReadAvailable is
+ * called.
+ */
+ virtual Timestamp getTimeout() const = 0;
+ /* Called when there is data available to read on an fd associated with
+ * the pollable. |fd| indicates which fd to read from. If the call returns
+ * false the poller will exit its poll loop with a return code of |status|.
+ */
+ virtual bool onReadAvailable(int fd, int* status) = 0;
+ /* Called when an fd associated with the pollable is closed. |fd| indicates
+ * which fd was closed. If the call returns false the poller will exit its
+ * poll loop with a return code of |status|.
+ */
+ virtual bool onClose(int fd, int* status) = 0;
+ /* Called when the timeout returned by getPollData has been reached. If
+ * the call returns false the poller will exit its poll loop with a return
+ * code of |status|.
+ */
+ virtual bool onTimeout(int* status) = 0;
};
diff --git a/network/netmgr/poller.cpp b/network/netmgr/poller.cpp
index 20c822d..8dcbcab 100644
--- a/network/netmgr/poller.cpp
+++ b/network/netmgr/poller.cpp
@@ -24,14 +24,23 @@
#include <stdio.h>
#include <string.h>
+#include <unordered_map>
#include <vector>
using std::chrono::duration_cast;
-static struct timespec* getTimeout(Pollable::Timestamp deadline,
- struct timespec* ts) {
+static struct timespec* calculateTimeout(Pollable::Timestamp deadline,
+ struct timespec* ts) {
+ Pollable::Timestamp now = Pollable::Clock::now();
if (deadline < Pollable::Timestamp::max()) {
- auto timeout = deadline - Pollable::Clock::now();
+ if (deadline <= now) {
+ LOGE("Poller found past due deadline, setting to zero");
+ ts->tv_sec = 0;
+ ts->tv_nsec = 0;
+ return ts;
+ }
+
+ auto timeout = deadline - now;
// Convert and round down to seconds
auto seconds = duration_cast<std::chrono::seconds>(timeout);
// Then subtract the seconds from the timeout and convert the remainder
@@ -72,23 +81,26 @@ int Poller::run() {
}
std::vector<struct pollfd> fds;
+ std::unordered_map<int, Pollable*> pollables;
while (true) {
fds.clear();
+ pollables.clear();
Pollable::Timestamp deadline = Pollable::Timestamp::max();
- for (const auto& pollable : mPollables) {
- int fd = pollable->data().fd;
- if (fd != -1) {
- fds.push_back(pollfd{});
- fds.back().fd = fd;
- fds.back().events = POLLIN;
+ for (auto& pollable : mPollables) {
+ size_t start = fds.size();
+ pollable->getPollData(&fds);
+ Pollable::Timestamp pollableDeadline = pollable->getTimeout();
+ // Create a map from each fd to the pollable
+ for (size_t i = start; i < fds.size(); ++i) {
+ pollables[fds[i].fd] = pollable;
}
- if (pollable->data().deadline < deadline) {
- deadline = pollable->data().deadline;
+ if (pollableDeadline < deadline) {
+ deadline = pollableDeadline;
}
}
struct timespec ts = { 0, 0 };
- struct timespec* tsPtr = getTimeout(deadline, &ts);
+ struct timespec* tsPtr = calculateTimeout(deadline, &ts);
status = ::ppoll(fds.data(), fds.size(), tsPtr, &mask);
if (status < 0) {
if (errno == EINTR) {
@@ -98,32 +110,46 @@ int Poller::run() {
// Actual error, time to quit
LOGE("Polling failed: %s", strerror(errno));
return errno;
- }
- // Check for timeouts
- Pollable::Timestamp now = Pollable::Clock::now();
- for (auto& pollable : mPollables) {
- // Since we're going to have a very low number of pollables it's
- // probably faster to just loop through fds here instead of
- // constructing a map from fd to pollable every polling loop.
+ } else if (status > 0) {
+ // Check for read or close events
for (const auto& fd : fds) {
- if (fd.fd == pollable->data().fd) {
- if (fd.revents & POLLIN) {
- // This pollable has data available for reading
- pollable->onReadAvailable();
+ if ((fd.revents & (POLLIN | POLLHUP)) == 0) {
+ // Neither POLLIN nor POLLHUP, not interested
+ continue;
+ }
+ auto pollable = pollables.find(fd.fd);
+ if (pollable == pollables.end()) {
+ // No matching fd, weird and unexpected
+ LOGE("Poller could not find fd matching %d", fd.fd);
+ continue;
+ }
+ if (fd.revents & POLLIN) {
+ // This pollable has data available for reading
+ int status = 0;
+ if (!pollable->second->onReadAvailable(fd.fd, &status)) {
+ // The onReadAvailable handler signaled an exit
+ return status;
}
- if (fd.revents & POLLHUP) {
- // The fd was closed from the other end
- pollable->onClose();
+ }
+ if (fd.revents & POLLHUP) {
+ // The fd was closed from the other end
+ int status = 0;
+ if (!pollable->second->onClose(fd.fd, &status)) {
+ // The onClose handler signaled an exit
+ return status;
}
}
}
- // Potentially trigger both read and timeout for pollables that have
- // different logic for these two cases. By checking the timeout
- // after the read we allow the pollable to update the deadline after
- // the read to prevent this from happening.
- if (now > pollable->data().deadline) {
- // This pollable has reached its deadline
- pollable->onTimeout();
+ }
+ // Check for timeouts
+ Pollable::Timestamp now = Pollable::Clock::now();
+ for (const auto& pollable : mPollables) {
+ if (pollable->getTimeout() <= now) {
+ int status = 0;
+ if (!pollable->onTimeout(&status)) {
+ // The onTimeout handler signaled an exit
+ return status;
+ }
}
}
}
diff --git a/network/netmgr/wifi_forwarder.cpp b/network/netmgr/wifi_forwarder.cpp
new file mode 100644
index 0000000..64f5b66
--- /dev/null
+++ b/network/netmgr/wifi_forwarder.cpp
@@ -0,0 +1,429 @@
+/*
+ * 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_forwarder.h"
+
+#include "log.h"
+
+#include <inttypes.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <linux/if_packet.h>
+#include <linux/kernel.h>
+// Ignore warning about unused static qemu pipe function
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-function"
+#include <qemu_pipe.h>
+#pragma clang diagnostic pop
+#include <string.h>
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <pcap/pcap.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+static const char kQemuPipeName[] = "qemud:wififorward";
+
+// The largest packet size to capture with pcap on the monitor interface
+static const int kPcapSnapLength = 65536;
+
+static const size_t kForwardBufferIncrement = 32768;
+static const size_t kForwardBufferMaxSize = 1 << 20;
+
+static const uint32_t kWifiForwardMagic = 0xD5C4B3A2;
+
+struct WifiForwardHeader {
+ WifiForwardHeader(uint32_t dataLength, uint32_t radioLength)
+ : magic(__cpu_to_le32(kWifiForwardMagic))
+ , fullLength(__cpu_to_le32(dataLength + sizeof(WifiForwardHeader)))
+ , radioLength(__cpu_to_le32(radioLength)) { }
+
+ uint32_t magic;
+ uint32_t fullLength;
+ uint32_t radioLength;
+} __attribute__((__packed__));
+
+struct RadioTapHeader {
+ uint8_t it_version;
+ uint8_t it_pad;
+ uint16_t it_len;
+ uint32_t it_present;
+} __attribute__((__packed__));
+
+enum class FrameType {
+ Management,
+ Control,
+ Data,
+ Extension
+};
+
+enum class ManagementType {
+ AssociationRequest,
+ AssociationResponse,
+ ReassociationRequest,
+ ReassociationResponse,
+ ProbeRequest,
+ ProbeResponse,
+ TimingAdvertisement,
+ Beacon,
+ Atim,
+ Disassociation,
+ Authentication,
+ Deauthentication,
+ Action,
+ ActionNoAck,
+};
+
+enum class ControlType {
+ BeamFormingReportPoll,
+ VhtNdpAnnouncement,
+ ControlFrameExtension,
+ ControlWrapper,
+ BlockAckReq,
+ BlockAck,
+ PsPoll,
+ Rts,
+ Cts,
+ Ack,
+ CfEnd,
+ CfEndCfAck
+};
+
+// Since the IEEE 802.11 header can vary in size depending on content we have
+// to establish a minimum size that we need to be able to inspect and forward
+// the frame. Every frame need to contain at least frame_control, duration_id,
+// and addr1.
+static const uint32_t kMinimumIeee80211Size = sizeof(uint16_t) +
+ sizeof(uint16_t) +
+ sizeof(MacAddress);
+
+WifiForwarder::WifiForwarder(const char* monitorInterfaceName)
+ : mInterfaceName(monitorInterfaceName),
+ mDeadline(Pollable::Timestamp::max()),
+ mMonitorPcap(nullptr),
+ mPipeFd(-1) {
+}
+
+WifiForwarder::~WifiForwarder() {
+ cleanup();
+}
+
+Result WifiForwarder::init() {
+ if (mMonitorPcap || mPipeFd != -1) {
+ return Result::error("WifiForwarder already initialized");
+ }
+
+ mPipeFd = qemu_pipe_open(kQemuPipeName);
+ if (mPipeFd == -1) {
+ // It's OK if this fails, the emulator might not have been started with
+ // this feature enabled. If it's not enabled we'll try again later, in
+ // the meantime there is no point in opening the monitor socket either.
+ LOGE("WifiForwarder unable to open QEMU pipe: %s", strerror(errno));
+ mDeadline = Pollable::Clock::now() + std::chrono::minutes(1);
+ return Result::success();
+ }
+
+ char errorMsg[PCAP_ERRBUF_SIZE];
+ memset(errorMsg, 0, sizeof(errorMsg));
+ mMonitorPcap = pcap_create(mInterfaceName.c_str(), errorMsg);
+ if (mMonitorPcap == nullptr) {
+ return Result::error("WifiForwarder cannot create pcap handle: %s",
+ errorMsg);
+ }
+ int result = pcap_set_snaplen(mMonitorPcap, kPcapSnapLength);
+ if (result != 0) {
+ return Result::error("WifiForwader cannot set pcap snap length: %s",
+ pcap_statustostr(result));
+ }
+
+ result = pcap_set_promisc(mMonitorPcap, 1);
+ if (result != 0) {
+ return Result::error("WifiForwader cannot set pcap promisc mode: %s",
+ pcap_statustostr(result));
+ }
+
+ result = pcap_set_immediate_mode(mMonitorPcap, 1);
+ if (result != 0) {
+ return Result::error("WifiForwader cannot set pcap immediate mode: %s",
+ pcap_statustostr(result));
+ }
+
+ result = pcap_activate(mMonitorPcap);
+ if (result > 0) {
+ // A warning, log it but keep going
+ LOGW("WifiForwader received warnings when activating pcap: %s",
+ pcap_statustostr(result));
+ } else if (result < 0) {
+ // An error, return
+ return Result::error("WifiForwader unable to activate pcap: %s",
+ pcap_statustostr(result));
+ }
+
+ int datalinkType = pcap_datalink(mMonitorPcap);
+ if (datalinkType != DLT_IEEE802_11_RADIO) {
+ // Unexpected data link encapsulation, we don't support this
+ return Result::error("WifiForwarder detected incompatible data link "
+ "encapsulation: %d", datalinkType);
+ }
+ // All done
+ return Result::success();
+}
+
+
+void WifiForwarder::getPollData(std::vector<pollfd>* fds) const {
+ int pcapFd = pcap_get_selectable_fd(mMonitorPcap);
+ if (pcapFd != -1) {
+ fds->push_back(pollfd{pcapFd, POLLIN, 0});
+ } else {
+ LOGE("WifiForwarder unable to get pcap fd");
+ }
+ if (mPipeFd != -1) {
+ fds->push_back(pollfd{mPipeFd, POLLIN, 0});
+ }
+}
+
+Pollable::Timestamp WifiForwarder::getTimeout() const {
+ // If there is no pipe return the deadline, we're going to retry, otherwise
+ // use an infinite timeout.
+ return mPipeFd == -1 ? mDeadline : Pollable::Timestamp::max();
+}
+
+bool WifiForwarder::onReadAvailable(int fd, int* /*status*/) {
+ if (fd == mPipeFd) {
+ injectFromPipe();
+ } else {
+ forwardFromPcap();
+ }
+ return true;
+}
+
+void WifiForwarder::forwardFromPcap() {
+ struct pcap_pkthdr* header = nullptr;
+ const u_char* data = nullptr;
+ int result = pcap_next_ex(mMonitorPcap, &header, &data);
+ if (result == 0) {
+ // Timeout, nothing to do
+ return;
+ } else if (result < 0) {
+ LOGE("WifiForwarder failed to read from pcap: %s",
+ pcap_geterr(mMonitorPcap));
+ return;
+ }
+ if (header->caplen < header->len) {
+ LOGE("WifiForwarder received packet exceeding capture length: %u < %u",
+ header->caplen, header->len);
+ return;
+ }
+
+ if (mPipeFd == -1) {
+ LOGE("WifiForwarder unable to forward data, pipe not open");
+ return;
+ }
+
+ if (header->caplen < sizeof(RadioTapHeader)) {
+ // This packet is too small to be a valid radiotap packet, drop it
+ LOGE("WifiForwarder captured packet that is too small: %u",
+ header->caplen);
+ return;
+ }
+
+ auto radiotap = reinterpret_cast<const RadioTapHeader*>(data);
+ uint32_t radioLen = __le16_to_cpu(radiotap->it_len);
+ if (header->caplen < radioLen + kMinimumIeee80211Size) {
+ // This packet is too small to contain a valid IEEE 802.11 frame
+ LOGE("WifiForwarder captured packet that is too small: %u < %u",
+ header->caplen, radioLen + kMinimumIeee80211Size);
+ return;
+ }
+
+ WifiForwardHeader forwardHeader(header->caplen, radioLen);
+
+ if (!WriteFully(mPipeFd, &forwardHeader, sizeof(forwardHeader))) {
+ LOGE("WifiForwarder failed to write to pipe: %s", strerror(errno));
+ return;
+ }
+
+ if (!WriteFully(mPipeFd, data, header->caplen)) {
+ LOGE("WifiForwarder failed to write to pipe: %s", strerror(errno));
+ return;
+ }
+}
+
+void WifiForwarder::injectFromPipe() {
+ size_t start = mMonitorBuffer.size();
+ size_t newSize = start + kForwardBufferIncrement;
+ if (newSize > kForwardBufferMaxSize) {
+ // We've exceeded the maximum allowed size, drop everything we have so
+ // far and start over. This is most likely caused by some delay in
+ // injection or the injection failing in which case keeping old data
+ // around isn't going to be very useful.
+ LOGE("WifiForwarder ran out of buffer space");
+ newSize = kForwardBufferIncrement;
+ start = 0;
+ }
+ mMonitorBuffer.resize(newSize);
+
+ while (true) {
+ int result = ::read(mPipeFd,
+ mMonitorBuffer.data() + start,
+ mMonitorBuffer.size() - start);
+ if (result < 0) {
+ if (errno == EINTR) {
+ continue;
+ }
+ LOGE("WifiForwarder failed to read to forward buffer: %s",
+ strerror(errno));
+ // Return the buffer to its previous size
+ mMonitorBuffer.resize(start);
+ return;
+ } else if (result == 0) {
+ // Nothing received, nothing to write
+ // Return the buffer to its previous size
+ mMonitorBuffer.resize(start);
+ LOGE("WifiForwarder did not receive anything to inject");
+ return;
+ }
+ // Adjust the buffer size to match everything we recieved
+ mMonitorBuffer.resize(start + static_cast<size_t>(result));
+ break;
+ }
+
+ while (mMonitorBuffer.size() >=
+ sizeof(WifiForwardHeader) + sizeof(RadioTapHeader)) {
+ auto fwd = reinterpret_cast<WifiForwardHeader*>(mMonitorBuffer.data());
+ if (__le32_to_cpu(fwd->magic) != kWifiForwardMagic) {
+ // We are not properly aligned, this can happen for the first read
+ // if the client or server happens to send something that's in the
+ // middle of a stream. Attempt to find the next packet boundary.
+ LOGE("WifiForwarder found incorrect magic, finding next magic");
+ uint32_t le32magic = __cpu_to_le32(kWifiForwardMagic);
+ auto next = reinterpret_cast<unsigned char*>(
+ ::memmem(mMonitorBuffer.data(), mMonitorBuffer.size(),
+ &le32magic, sizeof(le32magic)));
+ if (next) {
+ // We've found a possible candidate, erase everything before
+ size_t length = next - mMonitorBuffer.data();
+ mMonitorBuffer.erase(mMonitorBuffer.begin(),
+ mMonitorBuffer.begin() + length);
+ continue;
+ } else {
+ // There is no possible candidate, drop everything except the
+ // last three bytes. The last three bytes could possibly be the
+ // start of the next magic without actually triggering the
+ // search above.
+ if (mMonitorBuffer.size() > 3) {
+ mMonitorBuffer.erase(mMonitorBuffer.begin(),
+ mMonitorBuffer.end() - 3);
+ }
+ // In this case there is nothing left to parse so just return
+ // right away.
+ return;
+ }
+ }
+ // The length according to the wifi forward header
+ const size_t fullLength = __le32_to_cpu(fwd->fullLength);
+ const size_t payloadLength = fullLength - sizeof(WifiForwardHeader);
+ const size_t radioLength = __le32_to_cpu(fwd->radioLength);
+ // Get the radio tap header, right after the wifi forward header
+ unsigned char* radioTapLocation = mMonitorBuffer.data() + sizeof(*fwd);
+ auto hdr = reinterpret_cast<RadioTapHeader*>(radioTapLocation);
+ const size_t radioHdrLength = __le16_to_cpu(hdr->it_len);
+
+ if (radioLength != radioHdrLength) {
+ LOGE("WifiForwarder radiotap (%u), forwarder (%u) length mismatch",
+ (unsigned)(radioHdrLength), (unsigned)radioLength);
+ // The wifi forward header radio length does not match up with the
+ // radiotap header length. Either this was not an actual packet
+ // boundary or the packet is malformed. Remove a single byte from
+ // the buffer to trigger a new magic marker search.
+ mMonitorBuffer.erase(mMonitorBuffer.begin(),
+ mMonitorBuffer.begin() + 1);
+ continue;
+ }
+ // At this point we have verified that the magic marker is present and
+ // that the length in the wifi forward header matches the radiotap
+ // header length. We're now reasonably sure this is actually a valid
+ // packet that we can process.
+
+ if (fullLength > mMonitorBuffer.size()) {
+ // We have not received enough data yet, wait for more to arrive.
+ return;
+ }
+
+ if (hdr->it_version != 0) {
+ // Unknown header version, skip this packet because we don't know
+ // how to handle it.
+ LOGE("WifiForwarder encountered unknown radiotap version %u",
+ static_cast<unsigned>(hdr->it_version));
+ mMonitorBuffer.erase(mMonitorBuffer.begin(),
+ mMonitorBuffer.begin() + fullLength);
+ continue;
+ }
+
+ if (mMonitorPcap) {
+ // A sufficient amount of data has arrived, forward it.
+ int result = pcap_inject(mMonitorPcap, hdr, payloadLength);
+ if (result < 0) {
+ LOGE("WifiForwarder failed to inject %" PRIu64 " bytes: %s",
+ static_cast<uint64_t>(payloadLength),
+ pcap_geterr(mMonitorPcap));
+ } else if (static_cast<size_t>(result) < payloadLength) {
+ LOGE("WifiForwarder only injected %d out of %" PRIu64 " bytes",
+ result, static_cast<uint64_t>(payloadLength));
+ }
+ } else {
+ LOGE("WifiForwarder could not forward to monitor, pcap not set up");
+ }
+ mMonitorBuffer.erase(mMonitorBuffer.begin(),
+ mMonitorBuffer.begin() + fullLength);
+ }
+
+}
+
+void WifiForwarder::cleanup() {
+ if (mMonitorPcap) {
+ pcap_close(mMonitorPcap);
+ mMonitorPcap = nullptr;
+ }
+ if (mPipeFd != -1) {
+ ::close(mPipeFd);
+ mPipeFd = -1;
+ }
+}
+
+bool WifiForwarder::onClose(int /*fd*/, int* status) {
+ // Don't care which fd, just start all over again for simplicity
+ cleanup();
+ Result res = init();
+ if (!res) {
+ *status = 1;
+ return false;
+ }
+ return true;
+}
+
+bool WifiForwarder::onTimeout(int* status) {
+ if (mPipeFd == -1 && mMonitorPcap == nullptr) {
+ Result res = init();
+ if (!res) {
+ *status = 1;
+ return false;
+ }
+ }
+ return true;
+}
+
diff --git a/network/netmgr/wifi_forwarder.h b/network/netmgr/wifi_forwarder.h
new file mode 100644
index 0000000..5e9939e
--- /dev/null
+++ b/network/netmgr/wifi_forwarder.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#pragma once
+
+#include "macaddress.h"
+#include "pollable.h"
+#include "result.h"
+
+#include <string>
+#include <unordered_set>
+
+struct Ieee80211Header;
+struct pcap;
+typedef struct pcap pcap_t;
+
+class WifiForwarder : public Pollable {
+public:
+ explicit WifiForwarder(const char* monitorInterfaceName);
+ ~WifiForwarder();
+
+ Result init();
+
+ // Pollable interface
+ void getPollData(std::vector<pollfd>* fds) const override;
+ Timestamp getTimeout() const override;
+ bool onReadAvailable(int fd, int* status) override;
+ bool onClose(int fd, int* status) override;
+ bool onTimeout(int* status) override;
+private:
+ void forwardFromPcap();
+ void injectFromPipe();
+ void cleanup();
+
+ std::string mInterfaceName;
+ Pollable::Timestamp mDeadline;
+ std::vector<unsigned char> mMonitorBuffer;
+ pcap_t* mMonitorPcap;
+ int mPipeFd;
+};
diff --git a/sepolicy/common/goldfish_setup.te b/sepolicy/common/goldfish_setup.te
index 0fdcd37..fba9150 100644
--- a/sepolicy/common/goldfish_setup.te
+++ b/sepolicy/common/goldfish_setup.te
@@ -9,6 +9,7 @@ init_daemon_domain(goldfish_setup)
allow goldfish_setup self:capability { fowner chown net_admin net_raw };
allow goldfish_setup self:udp_socket { create ioctl };
allow goldfish_setup vendor_toolbox_exec:file execute_no_trans;
+allow goldfish_setup vendor_file:file execute_no_trans;
allowxperm goldfish_setup self:udp_socket ioctl priv_sock_ioctls;
wakelock_use(goldfish_setup);
allow goldfish_setup vendor_shell_exec:file { rx_file_perms };
diff --git a/sepolicy/common/netmgr.te b/sepolicy/common/netmgr.te
index 20eb00c..fec1f5e 100644
--- a/sepolicy/common/netmgr.te
+++ b/sepolicy/common/netmgr.te
@@ -14,8 +14,16 @@ allow netmgr hostapd_data_file:file rw_file_perms;
allow netmgr hostapd_data_file:dir rw_dir_perms;
# Assign addresses to new interfaces as hostapd brings them up
allow netmgr self:capability { net_raw net_admin };
+allow netmgr self:socket { create ioctl };
+allow netmgr self:packet_socket { ioctl getopt };
allow netmgr self:udp_socket { ioctl };
+allow netmgr proc_net:file { read getattr open };
+allowxperm netmgr self:socket ioctl { SIOCETHTOOL };
allowxperm netmgr self:udp_socket ioctl { SIOCSIFADDR SIOCSIFNETMASK SIOCSIFBRDADDR };
+allowxperm netmgr self:packet_socket ioctl { SIOCGIFINDEX SIOCGIFHWADDR };
+
# Allow netmgr to run iptables to block and unblock network traffic
allow netmgr system_file:file execute_no_trans;
allow netmgr system_file:file lock;
+# Packet socket for wifi forwarding
+allow netmgr self:packet_socket { bind create read setopt write };
diff --git a/wifi/init.wifi.sh b/wifi/init.wifi.sh
index eb84fd0..8ebe591 100755
--- a/wifi/init.wifi.sh
+++ b/wifi/init.wifi.sh
@@ -44,10 +44,14 @@
NAMESPACE="router"
rm -rf /data/vendor/var/run/netns/${NAMESPACE}
rm -rf /data/vendor/var/run/netns/${NAMESPACE}.pid
-# We need to fake a mac address to pass CTS
-# And the kernel only accept mac addresses with some special format
-# (Like, begin with 02)
-/system/bin/ip link set dev wlan0 address 02:00:00:44:55:66
+# Lower the MTU of the WiFi interface to prevent issues with packet injection.
+# The MTU of the WiFi monitor interface cannot be higher than 1500 but injection
+# requires extra space for injection headers which count against the MTU. So if
+# a 1500 byte payload needs to be injected it will fail because with the
+# additional headers the total amount of data will exceed 1500 bytes. This way
+# the payload is restricted to a smaller size that should leave room for the
+# injection headers.
+/system/bin/ip link set wlan0 mtu 1400
createns ${NAMESPACE}
@@ -59,14 +63,21 @@ if [ ! -f /data/vendor/wifi/hostapd/hostapd.conf ]; then
chown wifi:wifi /data/vendor/wifi/hostapd/hostapd.conf
chmod 660 /data/vendor/wifi/hostapd/hostapd.conf
fi
-# Start the network manager as soon as possible after the namespace is available.
-# This ensures that anything that follows is properly managed and monitored.
-setprop ctl.start netmgr
# createns will have created a file that contains the process id (pid) of a
# process running in the network namespace. This pid is needed for some commands
# to access the namespace.
PID=$(cat /data/vendor/var/run/netns/${NAMESPACE}.pid)
+
+# Move the WiFi monitor interface to the other namespace and bring it up. This
+# is what we use for injecting WiFi frames from the outside world.
+/system/bin/ip link set hwsim0 netns ${PID}
+execns ${NAMESPACE} /system/bin/ip link set hwsim0 up
+
+# Start the network manager as soon as possible after the namespace is available.
+# This ensures that anything that follows is properly managed and monitored.
+setprop ctl.start netmgr
+
/system/bin/ip link set eth0 netns ${PID}
/system/bin/ip link add radio0 type veth peer name radio0-peer
/system/bin/ip link set radio0-peer netns ${PID}
@@ -86,7 +97,9 @@ setprop ctl.start dhcpclient_rtr
execns ${NAMESPACE} /system/bin/iptables -w -W 50000 -t nat -A POSTROUTING -s 192.168.232.0/21 -o eth0 -j MASQUERADE
execns ${NAMESPACE} /system/bin/iptables -w -W 50000 -t nat -A POSTROUTING -s 192.168.200.0/24 -o eth0 -j MASQUERADE
/system/bin/iw phy phy1 set netns $PID
+
execns ${NAMESPACE} /system/bin/ip addr add 192.168.232.1/21 dev wlan1
+execns ${NAMESPACE} /system/bin/ip link set wlan1 mtu 1400
execns ${NAMESPACE} /system/bin/ip link set wlan1 up
# Start the IPv6 proxy that will enable use of IPv6 in the main namespace
setprop ctl.start ipv6proxy
diff --git a/wifi/wpa_supplicant.conf b/wifi/wpa_supplicant.conf
index 391c970..5f1eb7b 100644
--- a/wifi/wpa_supplicant.conf
+++ b/wifi/wpa_supplicant.conf
@@ -1,4 +1,3 @@
disable_scan_offload=1
wowlan_triggers=any
ap_scan=1
-p2p_no_group_iface=1