diff options
author | android-build-team Robot <android-build-team-robot@google.com> | 2018-11-07 04:07:11 +0000 |
---|---|---|
committer | android-build-team Robot <android-build-team-robot@google.com> | 2018-11-07 04:07:11 +0000 |
commit | 7cedf5da5331fb08ee306eb19958f397f86e97aa (patch) | |
tree | 40ddfe92482c4903543dd67a7ffb7842e43f491a | |
parent | 20077d56e6f6dae98d2365d4d67e739bc49a5076 (diff) | |
parent | aa76124b9f5fddd3279bb144267fda7a29aa9afe (diff) | |
download | android_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.bp | 6 | ||||
-rw-r--r-- | network/netmgr/commander.cpp | 29 | ||||
-rw-r--r-- | network/netmgr/commander.h | 9 | ||||
-rw-r--r-- | network/netmgr/commands/wifi_command.cpp | 2 | ||||
-rw-r--r-- | network/netmgr/log.h | 9 | ||||
-rw-r--r-- | network/netmgr/macaddress.h | 53 | ||||
-rw-r--r-- | network/netmgr/main.cpp | 11 | ||||
-rw-r--r-- | network/netmgr/monitor.cpp | 37 | ||||
-rw-r--r-- | network/netmgr/monitor.h | 9 | ||||
-rw-r--r-- | network/netmgr/pollable.h | 42 | ||||
-rw-r--r-- | network/netmgr/poller.cpp | 92 | ||||
-rw-r--r-- | network/netmgr/wifi_forwarder.cpp | 429 | ||||
-rw-r--r-- | network/netmgr/wifi_forwarder.h | 53 | ||||
-rw-r--r-- | sepolicy/common/goldfish_setup.te | 1 | ||||
-rw-r--r-- | sepolicy/common/netmgr.te | 8 | ||||
-rwxr-xr-x | wifi/init.wifi.sh | 27 | ||||
-rw-r--r-- | wifi/wpa_supplicant.conf | 1 |
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 |