summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorandroid-build-team Robot <android-build-team-robot@google.com>2018-11-07 04:07:11 +0000
committerandroid-build-team Robot <android-build-team-robot@google.com>2018-11-07 04:07:11 +0000
commit7cedf5da5331fb08ee306eb19958f397f86e97aa (patch)
tree40ddfe92482c4903543dd67a7ffb7842e43f491a
parent20077d56e6f6dae98d2365d4d67e739bc49a5076 (diff)
parentaa76124b9f5fddd3279bb144267fda7a29aa9afe (diff)
downloadandroid_device_generic_goldfish-7cedf5da5331fb08ee306eb19958f397f86e97aa.tar.gz
android_device_generic_goldfish-7cedf5da5331fb08ee306eb19958f397f86e97aa.tar.bz2
android_device_generic_goldfish-7cedf5da5331fb08ee306eb19958f397f86e97aa.zip
Snap for 5115288 from aa76124b9f5fddd3279bb144267fda7a29aa9afe to pi-qpr2-release
Change-Id: If8d613cafaffda76bdcc72ebb95cc5d85c26f873
-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