summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorandroid-build-team Robot <android-build-team-robot@google.com>2019-02-11 15:31:23 -0800
committerandroid-build-merger <android-build-merger@google.com>2019-02-11 15:31:23 -0800
commitdd7bd20e319cefbafc69f0748740864f12657f6c (patch)
tree8ee3cb35321655ad38530cd30c15ae99386e7544
parent4a61803e71e83d44f926048eb08ee9477779700c (diff)
parentae97ddcc37e8586a521106c4815fae588026c4c3 (diff)
downloadandroid_device_generic_goldfish-dd7bd20e319cefbafc69f0748740864f12657f6c.tar.gz
android_device_generic_goldfish-dd7bd20e319cefbafc69f0748740864f12657f6c.tar.bz2
android_device_generic_goldfish-dd7bd20e319cefbafc69f0748740864f12657f6c.zip
Snap for 5180536 from b05b939d85b47ed8c2b53114f8116699b859df09 to pi-platform-release
am: ae97ddcc37 Change-Id: I04d4d15458ae1881f3d347ba78e548820e135c5d
-rw-r--r--dhcp/server/dhcpserver.cpp161
-rw-r--r--dhcp/server/dhcpserver.h26
-rw-r--r--dhcp/server/log.h2
-rw-r--r--dhcp/server/main.cpp87
-rw-r--r--include/Android.bp5
-rw-r--r--init.ranchu.rc11
-rw-r--r--network/netmgr/Android.bp44
-rw-r--r--network/netmgr/address_assigner.cpp151
-rw-r--r--network/netmgr/address_assigner.h54
-rw-r--r--network/netmgr/commander.cpp164
-rw-r--r--network/netmgr/commander.h49
-rw-r--r--network/netmgr/commands/command.h37
-rw-r--r--network/netmgr/commands/wifi_command.cpp273
-rw-r--r--network/netmgr/commands/wifi_command.h55
-rw-r--r--network/netmgr/fork.cpp67
-rw-r--r--network/netmgr/fork.h26
-rw-r--r--network/netmgr/interface_state.cpp27
-rw-r--r--network/netmgr/interface_state.h23
-rw-r--r--network/netmgr/log.cpp27
-rw-r--r--network/netmgr/log.h45
-rw-r--r--network/netmgr/macaddress.h53
-rw-r--r--network/netmgr/main.cpp150
-rw-r--r--network/netmgr/monitor.cpp167
-rw-r--r--network/netmgr/monitor.h55
-rw-r--r--network/netmgr/pollable.h62
-rw-r--r--network/netmgr/poller.cpp158
-rw-r--r--network/netmgr/poller.h34
-rw-r--r--network/netmgr/result.h54
-rw-r--r--network/netmgr/timestamp.cpp33
-rw-r--r--network/netmgr/timestamp.h33
-rw-r--r--network/netmgr/wifi_forwarder.cpp432
-rw-r--r--network/netmgr/wifi_forwarder.h53
-rw-r--r--ril/Android.mk1
-rw-r--r--ril/atchannel.c1
-rw-r--r--ril/if_monitor.cpp352
-rw-r--r--ril/if_monitor.h49
-rw-r--r--ril/reference-ril.c127
-rw-r--r--sepolicy/common/execns.te3
-rw-r--r--sepolicy/common/file_contexts1
-rw-r--r--sepolicy/common/goldfish_setup.te6
-rw-r--r--sepolicy/common/hostapd_nohidl.te4
-rw-r--r--sepolicy/common/ipv6proxy.te1
-rw-r--r--sepolicy/common/netmgr.te29
-rw-r--r--vendor.mk1
-rwxr-xr-xwifi/init.wifi.sh34
-rw-r--r--wifi/ipv6proxy/log.h2
-rw-r--r--wifi/ipv6proxy/proxy.cpp44
-rw-r--r--wifi/wpa_supplicant.conf1
48 files changed, 3116 insertions, 158 deletions
diff --git a/dhcp/server/dhcpserver.cpp b/dhcp/server/dhcpserver.cpp
index 33fb61a..d6d4a7b 100644
--- a/dhcp/server/dhcpserver.cpp
+++ b/dhcp/server/dhcpserver.cpp
@@ -35,16 +35,7 @@
static const int kMaxDnsServers = 4;
-DhcpServer::DhcpServer(in_addr_t dhcpRangeStart,
- in_addr_t dhcpRangeEnd,
- in_addr_t netmask,
- in_addr_t gateway,
- unsigned int excludeInterface) :
- mNextAddressOffset(0),
- mDhcpRangeStart(dhcpRangeStart),
- mDhcpRangeEnd(dhcpRangeEnd),
- mNetmask(netmask),
- mGateway(gateway),
+DhcpServer::DhcpServer(unsigned int excludeInterface) :
mExcludeInterface(excludeInterface)
{
}
@@ -147,9 +138,13 @@ void DhcpServer::sendDhcpOffer(const Message& message,
unsigned int interfaceIndex ) {
updateDnsServers();
in_addr_t offerAddress;
+ in_addr_t netmask;
+ in_addr_t gateway;
Result res = getOfferAddress(interfaceIndex,
message.dhcpData.chaddr,
- &offerAddress);
+ &offerAddress,
+ &netmask,
+ &gateway);
if (!res) {
ALOGE("Failed to get address for offer: %s", res.c_str());
return;
@@ -165,8 +160,8 @@ void DhcpServer::sendDhcpOffer(const Message& message,
Message offer = Message::offer(message,
serverAddress,
offerAddress,
- mNetmask,
- mGateway,
+ netmask,
+ gateway,
mDnsServers.data(),
mDnsServers.size());
res = sendMessage(interfaceIndex, serverAddress, offer);
@@ -177,10 +172,15 @@ void DhcpServer::sendDhcpOffer(const Message& message,
void DhcpServer::sendAck(const Message& message, unsigned int interfaceIndex) {
updateDnsServers();
- in_addr_t offerAddress, serverAddress;
+ in_addr_t offerAddress;
+ in_addr_t netmask;
+ in_addr_t gateway;
+ in_addr_t serverAddress;
Result res = getOfferAddress(interfaceIndex,
message.dhcpData.chaddr,
- &offerAddress);
+ &offerAddress,
+ &netmask,
+ &gateway);
if (!res) {
ALOGE("Failed to get address for offer: %s", res.c_str());
return;
@@ -194,8 +194,8 @@ void DhcpServer::sendAck(const Message& message, unsigned int interfaceIndex) {
Message ack = Message::ack(message,
serverAddress,
offerAddress,
- mNetmask,
- mGateway,
+ netmask,
+ gateway,
mDnsServers.data(),
mDnsServers.size());
res = sendMessage(interfaceIndex, serverAddress, ack);
@@ -222,9 +222,13 @@ void DhcpServer::sendNack(const Message& message, unsigned int interfaceIndex) {
bool DhcpServer::isValidDhcpRequest(const Message& message,
unsigned int interfaceIndex) {
in_addr_t offerAddress;
+ in_addr_t netmask;
+ in_addr_t gateway;
Result res = getOfferAddress(interfaceIndex,
message.dhcpData.chaddr,
- &offerAddress);
+ &offerAddress,
+ &netmask,
+ &gateway);
if (!res) {
ALOGE("Failed to get address for offer: %s", res.c_str());
return false;
@@ -251,32 +255,99 @@ void DhcpServer::updateDnsServers() {
}
}
-Result DhcpServer::getInterfaceAddress(unsigned int interfaceIndex,
- in_addr_t* address) {
+Result DhcpServer::getInterfaceData(unsigned int interfaceIndex,
+ unsigned long type,
+ struct ifreq* response) {
char interfaceName[IF_NAMESIZE + 1];
if (if_indextoname(interfaceIndex, interfaceName) == nullptr) {
return Result::error("Failed to get interface name for index %u: %s",
interfaceIndex, strerror(errno));
}
- struct ifreq request;
- memset(&request, 0, sizeof(request));
- request.ifr_addr.sa_family = AF_INET;
- strncpy(request.ifr_name, interfaceName, IFNAMSIZ - 1);
+ memset(response, 0, sizeof(*response));
+ response->ifr_addr.sa_family = AF_INET;
+ strncpy(response->ifr_name, interfaceName, IFNAMSIZ - 1);
- if (::ioctl(mSocket.get(), SIOCGIFADDR, &request) == -1) {
- return Result::error("Failed to get address for interface %s: %s",
+ if (::ioctl(mSocket.get(), type, response) == -1) {
+ return Result::error("Failed to get data for interface %s: %s",
interfaceName, strerror(errno));
}
- auto inAddr = reinterpret_cast<struct sockaddr_in*>(&request.ifr_addr);
- *address = inAddr->sin_addr.s_addr;
-
return Result::success();
}
+Result DhcpServer::getInterfaceAddress(unsigned int interfaceIndex,
+ in_addr_t* address) {
+ struct ifreq data;
+ Result res = getInterfaceData(interfaceIndex, SIOCGIFADDR, &data);
+ if (res.isSuccess()) {
+ auto inAddr = reinterpret_cast<struct sockaddr_in*>(&data.ifr_addr);
+ *address = inAddr->sin_addr.s_addr;
+ }
+ return res;
+}
+
+Result DhcpServer::getInterfaceNetmask(unsigned int interfaceIndex,
+ in_addr_t* address) {
+ struct ifreq data;
+ Result res = getInterfaceData(interfaceIndex, SIOCGIFNETMASK, &data);
+ if (res.isSuccess()) {
+ auto inAddr = reinterpret_cast<struct sockaddr_in*>(&data.ifr_addr);
+ *address = inAddr->sin_addr.s_addr;
+ }
+ return res;
+}
+
+static bool isValidHost(const in_addr_t address,
+ const in_addr_t interfaceAddress,
+ const in_addr_t netmask) {
+ // If the bits outside of the netmask are all zero it's a network address,
+ // don't use this.
+ bool isNetworkAddress = (address & ~netmask) == 0;
+ // If all bits outside of the netmask are set then it's a broadcast address,
+ // don't use this either.
+ bool isBroadcastAddress = (address & ~netmask) == ~netmask;
+ // Don't assign the interface address to a host
+ bool isInterfaceAddress = address == interfaceAddress;
+
+ return !isNetworkAddress && !isBroadcastAddress && !isInterfaceAddress;
+}
+
+static bool addressInRange(const in_addr_t address,
+ const in_addr_t interfaceAddress,
+ const in_addr_t netmask) {
+ if (address <= (interfaceAddress & netmask)) {
+ return false;
+ }
+ if (address >= (interfaceAddress | ~netmask)) {
+ return false;
+ }
+ return true;
+}
+
Result DhcpServer::getOfferAddress(unsigned int interfaceIndex,
const uint8_t* macAddress,
- in_addr_t* address) {
+ in_addr_t* address,
+ in_addr_t* netmask,
+ in_addr_t* gateway) {
+ // The interface address will be the gateway and will be used to determine
+ // the range of valid addresses (along with the netmask) for the client.
+ in_addr_t interfaceAddress = 0;
+ Result res = getInterfaceAddress(interfaceIndex, &interfaceAddress);
+ if (!res) {
+ return res;
+ }
+ // The netmask of the interface will be the netmask for the client as well
+ // as used to determine network range.
+ in_addr_t mask = 0;
+ res = getInterfaceNetmask(interfaceIndex, &mask);
+ if (!res) {
+ return res;
+ }
+
+ // Assign these values now before they are modified below
+ *gateway = interfaceAddress;
+ *netmask = mask;
+
Lease key(interfaceIndex, macAddress);
// Find or create entry, if it's created it will be zero and we update it
@@ -284,18 +355,30 @@ Result DhcpServer::getOfferAddress(unsigned int interfaceIndex,
if (value == 0) {
// Addresses are stored in network byte order so when doing math on them
// they have to be converted to host byte order
- in_addr_t nextAddress = ntohl(mDhcpRangeStart) + mNextAddressOffset;
- uint8_t lastAddressByte = nextAddress & 0xFF;
- while (lastAddressByte == 0xFF || lastAddressByte == 0) {
- // The address ends in .255 or .0 which means it's a broadcast or
- // network address respectively. Increase it further to avoid this.
+ interfaceAddress = ntohl(interfaceAddress);
+ mask = ntohl(mask);
+ // Get a reference to the offset so we can use it and increase it at the
+ // same time. If the entry does not exist it will be created with a
+ // value of zero.
+ in_addr_t& offset = mNextAddressOffsets[interfaceIndex];
+ if (offset == 0) {
+ // Increase if zero to avoid assigning network address
+ ++offset;
+ }
+ // Start out at the first address in the range as determined by netmask
+ in_addr_t nextAddress = (interfaceAddress & mask) + offset;
+
+ // Ensure the address is valid
+ while (!isValidHost(nextAddress, interfaceAddress, mask) &&
+ addressInRange(nextAddress, interfaceAddress, mask)) {
++nextAddress;
- ++mNextAddressOffset;
+ ++offset;
}
- if (nextAddress <= ntohl(mDhcpRangeEnd)) {
- // And then converted back again
+
+ if (addressInRange(nextAddress, interfaceAddress, mask)) {
+ // Convert back to network byte order
value = htonl(nextAddress);
- ++mNextAddressOffset;
+ ++offset;
} else {
// Ran out of addresses
return Result::error("DHCP server is out of addresses");
diff --git a/dhcp/server/dhcpserver.h b/dhcp/server/dhcpserver.h
index c5e1007..276cd5b 100644
--- a/dhcp/server/dhcpserver.h
+++ b/dhcp/server/dhcpserver.h
@@ -30,14 +30,9 @@ class Message;
class DhcpServer {
public:
- // Construct a DHCP server with the given parameters. Ignore any requests
- // and discoveries coming on the network interface identified by
- // |excludeInterface|.
- DhcpServer(in_addr_t dhcpRangeStart,
- in_addr_t dhcpRangeEnd,
- in_addr_t netmask,
- in_addr_t gateway,
- unsigned int excludeInterface);
+ // Construct a DHCP server. Ignore any requests and discoveries coming on
+ // the network interface identified by |excludeInterface|.
+ explicit DhcpServer(unsigned int excludeInterface);
Result init();
Result run();
@@ -54,24 +49,27 @@ private:
bool isValidDhcpRequest(const Message& message,
unsigned int interfaceIndex);
void updateDnsServers();
+ Result getInterfaceData(unsigned int interfaceIndex,
+ unsigned long type,
+ struct ifreq* response);
Result getInterfaceAddress(unsigned int interfaceIndex,
in_addr_t* address);
+ Result getInterfaceNetmask(unsigned int interfaceIndex,
+ in_addr_t* netmask);
Result getOfferAddress(unsigned int interfaceIndex,
const uint8_t* macAddress,
- in_addr_t* address);
+ in_addr_t* address,
+ in_addr_t* netmask,
+ in_addr_t* gateway);
Socket mSocket;
// This is the next address offset. This will be added to whatever the base
// address of the DHCP address range is. For each new MAC address seen this
// value will increase by one.
- in_addr_t mNextAddressOffset;
- in_addr_t mDhcpRangeStart;
- in_addr_t mDhcpRangeEnd;
- in_addr_t mNetmask;
- in_addr_t mGateway;
std::vector<in_addr_t> mDnsServers;
// Map a lease to an IP address for that lease
std::unordered_map<Lease, in_addr_t> mLeases;
+ std::unordered_map<unsigned int, in_addr_t> mNextAddressOffsets;
unsigned int mExcludeInterface;
};
diff --git a/dhcp/server/log.h b/dhcp/server/log.h
index bb1094f..a0f21e0 100644
--- a/dhcp/server/log.h
+++ b/dhcp/server/log.h
@@ -16,5 +16,5 @@
#pragma once
#define LOG_TAG "dhcpserver"
-#include <cutils/log.h>
+#include <log/log.h>
diff --git a/dhcp/server/main.cpp b/dhcp/server/main.cpp
index eecafc1..482ffd6 100644
--- a/dhcp/server/main.cpp
+++ b/dhcp/server/main.cpp
@@ -26,73 +26,10 @@ static void usage(const char* program) {
}
int main(int argc, char* argv[]) {
- in_addr_t rangeStart = 0;
- in_addr_t rangeEnd = 0;
- in_addr_t gateway = 0;
- in_addr_t netmask = 0;
char* excludeInterfaceName = nullptr;
unsigned int excludeInterfaceIndex = 0;
for (int i = 1; i < argc; ++i) {
- if (strcmp("--range", argv[i]) == 0) {
- if (i + 1 >= argc) {
- ALOGE("ERROR: Missing argument to --range parameter");
- usage(argv[0]);
- return 1;
- }
- char* divider = strchr(argv[i + 1], ',');
- if (divider != nullptr) {
- *divider = '\0';
- struct in_addr address;
- if (inet_pton(AF_INET, argv[i + 1], &address) > 0) {
- rangeStart = address.s_addr;
- } else {
- ALOGE("ERROR: Invalid start address '%s'", argv[i + 1]);
- usage(argv[0]);
- return 1;
- }
- char* next = divider + 1;
- if (inet_pton(AF_INET, next, &address) > 0) {
- rangeEnd = address.s_addr;
- } else {
- ALOGE("ERROR: Invalid end address '%s'", next);
- usage(argv[0]);
- return 1;
- }
- } else {
- ALOGE("ERROR: Invalid --range parameter '%s'", argv[i + 1]);
- usage(argv[0]);
- return 1;
- }
- ++i;
- } else if (strcmp("--gateway", argv[i]) == 0) {
- if (i + 1 >= argc) {
- ALOGE("ERROR: Missing argument to --gateway parameter");
- usage(argv[0]);
- return 1;
- }
- struct in_addr address;
- if (inet_pton(AF_INET, argv[i + 1], &address) > 0) {
- gateway = address.s_addr;
- } else {
- ALOGE("ERROR: Invalid gateway '%s'", argv[i + 1]);
- usage(argv[0]);
- return 1;
- }
- } else if (strcmp("--netmask", argv[i]) == 0) {
- if (i + 1 >= argc) {
- ALOGE("ERROR: Missing argument to --netmask parameter");
- usage(argv[0]);
- return 1;
- }
- struct in_addr address;
- if (inet_pton(AF_INET, argv[i + 1], &address) > 0) {
- netmask = address.s_addr;
- } else {
- ALOGE("ERROR: Invalid netmask '%s'", argv[i + 1]);
- usage(argv[0]);
- return 1;
- }
- } else if (strcmp("--exclude-interface", argv[i]) == 0) {
+ if (strcmp("--exclude-interface", argv[i]) == 0) {
if (i + 1 >= argc) {
ALOGE("ERROR: Missing argument to "
"--exclude-interfaces parameter");
@@ -110,27 +47,7 @@ int main(int argc, char* argv[]) {
}
}
- if (rangeStart == 0 || rangeEnd == 0) {
- ALOGE("ERROR: Missing or invalid --range argument");
- usage(argv[0]);
- return 1;
- }
- if (gateway == 0) {
- ALOGE("ERROR: Missing or invalid --gateway argument");
- usage(argv[0]);
- return 1;
- }
- if (netmask == 0) {
- ALOGE("ERROR: Missing or invalid --netmask argument");
- usage(argv[0]);
- return 1;
- }
-
- DhcpServer server(rangeStart,
- rangeEnd,
- netmask,
- gateway,
- excludeInterfaceIndex);
+ DhcpServer server(excludeInterfaceIndex);
Result res = server.init();
if (!res) {
ALOGE("Failed to initialize DHCP server: %s\n", res.c_str());
diff --git a/include/Android.bp b/include/Android.bp
new file mode 100644
index 0000000..2f09676
--- /dev/null
+++ b/include/Android.bp
@@ -0,0 +1,5 @@
+cc_library_headers {
+ name: "goldfish_headers",
+ vendor_available: true,
+ export_include_dirs: ["."],
+}
diff --git a/init.ranchu.rc b/init.ranchu.rc
index bf77184..d7a61d4 100644
--- a/init.ranchu.rc
+++ b/init.ranchu.rc
@@ -57,7 +57,7 @@ on property:dev.bootcomplete=1
service ranchu-net /vendor/bin/init.ranchu-net.sh
class late_start
user root
- group root wakelock
+ group root wakelock wifi
oneshot
service ipv6proxy /vendor/bin/execns router /vendor/bin/ipv6proxy -o eth0 -i wlan1,radio0-peer
@@ -65,16 +65,21 @@ service ipv6proxy /vendor/bin/execns router /vendor/bin/ipv6proxy -o eth0 -i wla
group root
disabled
-service emu_hostapd /vendor/bin/execns -u wifi -g wifi router /vendor/bin/hostapd_nohidl /vendor/etc/simulated_hostapd.conf
+service emu_hostapd /vendor/bin/execns -u wifi -g wifi router /vendor/bin/hostapd_nohidl /data/vendor/wifi/hostapd/hostapd.conf
user root
group root wifi net_raw net_admin
disabled
-service dhcpserver /vendor/bin/execns router /vendor/bin/dhcpserver --range 192.168.232.2,192.168.239.254 --gateway 192.168.232.1 --netmask 255.255.248.0 --exclude-interface eth0
+service dhcpserver /vendor/bin/execns router /vendor/bin/dhcpserver --exclude-interface eth0
user root
group root
disabled
+service netmgr /vendor/bin/execns router /vendor/bin/netmgr --if-prefix wlan1_ --network 192.168.232.9/29
+ user root
+ group root wifi
+ disabled
+
service dhcpclient_rtr /vendor/bin/execns router /vendor/bin/dhcpclient -i eth0
user root
group root
diff --git a/network/netmgr/Android.bp b/network/netmgr/Android.bp
new file mode 100644
index 0000000..6686f98
--- /dev/null
+++ b/network/netmgr/Android.bp
@@ -0,0 +1,44 @@
+//
+// Copyright (C) 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.
+//
+
+cc_binary {
+ name: "netmgr",
+ vendor: true,
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ srcs: [
+ "address_assigner.cpp",
+ "commander.cpp",
+ "fork.cpp",
+ "interface_state.cpp",
+ "log.cpp",
+ "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/address_assigner.cpp b/network/netmgr/address_assigner.cpp
new file mode 100644
index 0000000..11df81d
--- /dev/null
+++ b/network/netmgr/address_assigner.cpp
@@ -0,0 +1,151 @@
+/*
+ * 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 "address_assigner.h"
+
+#include "log.h"
+
+#include <errno.h>
+#include <net/if.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+AddressAssigner::AddressAssigner(const char* interfacePrefix,
+ in_addr_t baseAddress,
+ uint32_t maskLength) :
+ mInterfacePrefix(interfacePrefix),
+ mPrefixLength(strlen(interfacePrefix)),
+ mBaseAddress(baseAddress),
+ mMaskLength(maskLength) {
+
+}
+
+void AddressAssigner::onInterfaceState(unsigned int /*index*/,
+ const char* name,
+ InterfaceState state) {
+ if (strncmp(name, mInterfacePrefix, mPrefixLength) != 0) {
+ // The interface does not match the prefix, ignore this change
+ return;
+ }
+
+ switch (state) {
+ case InterfaceState::Up:
+ assignAddress(name);
+ break;
+ case InterfaceState::Down:
+ freeAddress(name);
+ break;
+ }
+}
+
+void AddressAssigner::assignAddress(const char* interfaceName) {
+ if (mMaskLength > 30) {
+ // The mask length is too long, we can't assign enough IP addresses from
+ // this. A maximum of 30 bits is supported, leaving 4 remaining
+ // addresses, one is network, one is broadcast, one is gateway, one is
+ // client.
+ return;
+ }
+ // Each subnet will have an amount of bits available to it that equals
+ // 32-bits - <mask length>, so if mask length is 29 there will be 3
+ // remaining bits for each subnet. Then the distance between each subnet
+ // is 2 to the power of this number, in our example 2^3 = 8 so to get to the
+ // next subnet we add 8 to the network address.
+ in_addr_t increment = 1 << (32 - mMaskLength);
+
+ // Convert the address to host byte-order first so we can do math on it.
+ for (in_addr_t addr = ntohl(mBaseAddress); true; addr += increment) {
+ // Take the reference of this lookup, that way we can assign a name to
+ // it if needed.
+ auto& usedName = mUsedIpAddresses[addr];
+ if (usedName.empty()) {
+ // This address is not in use, let's use it
+ usedName = interfaceName;
+ // Make sure we convert back to network byte-order when setting it.
+ setIpAddress(interfaceName, htonl(addr));
+ break;
+ }
+ }
+}
+
+void AddressAssigner::setIpAddress(const char* interfaceName,
+ in_addr_t address) {
+ int sock = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
+ if (sock == -1) {
+ LOGE("AddressAssigner unable to open IP socket: %s", strerror(errno));
+ return;
+ }
+ if (!setAddress(sock, SIOCSIFADDR, interfaceName, address)) {
+ LOGE("AddressAssigner unable to set interface address: %s",
+ strerror(errno));
+ ::close(sock);
+ return;
+ }
+
+ // The netmask is the inverted maximum value of the lower bits. That is if
+ // the mask length is 29 then the the largest value of the 3 (32-29) lowest
+ // bits is 7 (2^3 - 1) (111 binary). Inverting this value gives the netmask
+ // because it excludes those three bits and sets every other bit.
+ in_addr_t netmask = htonl(~((1 << (32 - mMaskLength)) - 1));
+
+ if (!setAddress(sock, SIOCSIFNETMASK, interfaceName, netmask)) {
+ LOGE("AddressAssigner unable to set interface netmask: %s",
+ strerror(errno));
+ ::close(sock);
+ return;
+ }
+
+ // The broadcast address is just the assigned address with all bits outside
+ // of the netmask set to one.
+ in_addr_t broadcast = address | ~netmask;
+
+ if (!setAddress(sock, SIOCSIFBRDADDR, interfaceName, broadcast)) {
+ LOGE("AddressAssigner unable to set interface broadcast: %s",
+ strerror(errno));
+ ::close(sock);
+ return;
+ }
+ ::close(sock);
+}
+
+bool AddressAssigner::setAddress(int sock,
+ int type,
+ const char* interfaceName,
+ in_addr_t address) {
+ struct ifreq request;
+ memset(&request, 0, sizeof(request));
+ strlcpy(request.ifr_name, interfaceName, sizeof(request.ifr_name));
+ auto addr = reinterpret_cast<struct sockaddr_in*>(&request.ifr_addr);
+ addr->sin_family = AF_INET;
+ addr->sin_addr.s_addr = address;
+
+ if (::ioctl(sock, type, &request) != 0) {
+ return false;
+ }
+ return true;
+}
+
+void AddressAssigner::freeAddress(const char* interfaceName) {
+ for (auto& ipName : mUsedIpAddresses) {
+ if (ipName.second == interfaceName) {
+ // This is the one, free it up for future use
+ mUsedIpAddresses.erase(ipName.first);
+ }
+ }
+}
+
diff --git a/network/netmgr/address_assigner.h b/network/netmgr/address_assigner.h
new file mode 100644
index 0000000..dde42ee
--- /dev/null
+++ b/network/netmgr/address_assigner.h
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "interface_state.h"
+
+#include <string>
+#include <unordered_map>
+
+#include <netinet/in.h>
+#include <stdint.h>
+
+class AddressAssigner {
+public:
+ AddressAssigner(const char* interfacePrefix,
+ in_addr_t baseAddress,
+ uint32_t maskLength);
+
+ void onInterfaceState(unsigned int index,
+ const char* name,
+ InterfaceState state);
+
+private:
+ void assignAddress(const char* interfaceName);
+ void freeAddress(const char* interfaceName);
+
+ void setIpAddress(const char* interfaceName, in_addr_t address);
+ bool setAddress(int socket,
+ int type,
+ const char* interfaceName,
+ in_addr_t address);
+ void removeIpAddress(const char* interfaceName, in_addr_t address);
+
+ const char* mInterfacePrefix;
+ size_t mPrefixLength;
+ in_addr_t mBaseAddress;
+ uint32_t mMaskLength;
+ std::unordered_map<in_addr_t, std::string> mUsedIpAddresses;
+};
+
diff --git a/network/netmgr/commander.cpp b/network/netmgr/commander.cpp
new file mode 100644
index 0000000..a2dc1aa
--- /dev/null
+++ b/network/netmgr/commander.cpp
@@ -0,0 +1,164 @@
+/*
+ * 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 "commander.h"
+
+#include "commands/command.h"
+#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>
+
+static const char kQemuPipeName[] = "qemud:network";
+
+// How much space to use for each command received
+static const size_t kReceiveSpace = 1024;
+// The maximum amount of bytes to keep in the receive buffer for a single
+// command before dropping data.
+static const size_t kMaxReceiveBufferSize = 65536;
+
+Commander::Commander() : mPipeFd(-1) {
+}
+
+Commander::~Commander() {
+ closePipe();
+}
+
+Result Commander::init() {
+ if (mPipeFd != -1) {
+ return Result::error("Commander already initialized");
+ }
+
+ openPipe();
+
+ return Result::success();
+}
+
+void Commander::registerCommand(const char* commandStr, Command* command) {
+ mCommands[commandStr] = command;
+}
+
+void Commander::getPollData(std::vector<pollfd>* fds) const {
+ if (mPipeFd != -1) {
+ fds->push_back(pollfd{mPipeFd, POLLIN, 0});
+ }
+}
+
+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) {
+ // We have buffered too much data, this should never happen but as a
+ // seurity measure let's just drop everything we have and keep
+ // receiving. Maybe the situation will improve.
+ mReceiveBuffer.resize(kReceiveSpace);
+ offset = 0;
+ }
+ while (true) {
+ int status = ::read(mPipeFd, &mReceiveBuffer[offset], kReceiveSpace);
+
+ if (status < 0) {
+ if (errno == EINTR) {
+ // We got an interrupt, try again
+ continue;
+ }
+ LOGE("Commander failed to receive on pipe: %s", strerror(errno));
+ // Don't exit the looper because of this, keep trying
+ return true;
+ }
+ size_t length = static_cast<size_t>(status);
+ mReceiveBuffer.resize(offset + length);
+
+ while (true) {
+ auto endline = std::find(mReceiveBuffer.begin(),
+ mReceiveBuffer.end(),
+ '\n');
+ if (endline == mReceiveBuffer.end()) {
+ // No endline in sight, keep waiting and buffering
+ return true;
+ }
+
+ *endline = '\0';
+
+ char* args = ::strchr(mReceiveBuffer.data(), ' ');
+ if (args) {
+ *args++ = '\0';
+ }
+ auto command = mCommands.find(mReceiveBuffer.data());
+
+ if (command != mCommands.end()) {
+ command->second->onCommand(mReceiveBuffer.data(), args);
+ }
+ // Now that we have processed this line let's remove it from the
+ // receive buffer
+ ++endline;
+ if (endline == mReceiveBuffer.end()) {
+ mReceiveBuffer.clear();
+ // There can be nothing left, just 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 true;
+ }
+}
+
+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;
+}
+
+bool Commander::onTimeout(int* /*status*/) {
+ if (mPipeFd == -1) {
+ openPipe();
+ }
+ return true;
+}
+
+void Commander::openPipe() {
+ mPipeFd = qemu_pipe_open(kQemuPipeName);
+ if (mPipeFd == -1) {
+ LOGE("Failed to open QEMU pipe '%s': %s",
+ kQemuPipeName,
+ strerror(errno));
+ // Try again in the future
+ mDeadline = Pollable::Clock::now() + std::chrono::minutes(1);
+ } else {
+ mDeadline = Pollable::Timestamp::max();
+ }
+}
+
+void Commander::closePipe() {
+ if (mPipeFd != -1) {
+ ::close(mPipeFd);
+ mPipeFd = -1;
+ }
+}
diff --git a/network/netmgr/commander.h b/network/netmgr/commander.h
new file mode 100644
index 0000000..7f7fc7e
--- /dev/null
+++ b/network/netmgr/commander.h
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "pollable.h"
+#include "result.h"
+
+#include <unordered_map>
+#include <vector>
+
+class Command;
+
+class Commander : public Pollable {
+public:
+ Commander();
+ ~Commander();
+
+ Result init();
+
+ void registerCommand(const char* commandStr, Command* command);
+
+ 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();
+
+ int mPipeFd;
+ Pollable::Timestamp mDeadline;
+ std::vector<char> mReceiveBuffer;
+ std::unordered_map<std::string, Command*> mCommands;
+};
diff --git a/network/netmgr/commands/command.h b/network/netmgr/commands/command.h
new file mode 100644
index 0000000..0612cef
--- /dev/null
+++ b/network/netmgr/commands/command.h
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "../result.h"
+
+// An interface for commands coming through the pipe. Note that each instance
+// can handle any number of commands, all it has to do is differentiate based
+// on the incoming command string or arguments. It will have to be registered
+// multiple times, once for each command though.
+class Command {
+public:
+ virtual ~Command() = default;
+
+ // Work to perform when a command is received. The result will be used to
+ // report the result to the user. If the result indicates success the user
+ // will see an "OK" response, on failure the error message in the result
+ // will be presented to the user. This means that the result error string
+ // should be fairly user-friendly.
+ virtual Result onCommand(const char* command, const char* args) = 0;
+
+};
+
diff --git a/network/netmgr/commands/wifi_command.cpp b/network/netmgr/commands/wifi_command.cpp
new file mode 100644
index 0000000..8f48a43
--- /dev/null
+++ b/network/netmgr/commands/wifi_command.cpp
@@ -0,0 +1,273 @@
+/*
+ * 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_command.h"
+
+#include "../fork.h"
+#include "log.h"
+
+#include <cutils/properties.h>
+#include <errno.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+static const char kHostApdStubFile[] = "/vendor/etc/simulated_hostapd.conf";
+static const char kHostApdConfFile[] = "/data/vendor/wifi/hostapd/hostapd.conf";
+
+static const char kControlRestartProperty[] = "ctl.restart";
+static const char kHostApdServiceName[] = "emu_hostapd";
+
+static const char kIfNamePrefix[] = "wlan1_";
+
+class File {
+public:
+ explicit File(FILE* file) : mFile(file) {}
+ ~File() {
+ if (mFile) {
+ fclose(mFile);
+ }
+ }
+
+ FILE* get() { return mFile; }
+
+ bool operator!() const { return mFile == nullptr; }
+private:
+ FILE* mFile;
+};
+
+class Fd {
+public:
+ explicit Fd(int fd) : mFd(fd) {}
+ ~Fd() {
+ if (mFd != -1) {
+ ::close(mFd);
+ mFd = -1;
+ }
+ }
+
+ int get() const { return mFd; }
+
+private:
+ int mFd;
+};
+
+std::vector<std::string> explode(const char* str) {
+ const char* cur = str;
+ const char* space = nullptr;
+ std::vector<std::string> result;
+ do {
+ space = ::strchr(cur, ' ');
+ if (space) {
+ result.emplace_back(cur, space);
+ cur = space + 1;
+ } else {
+ result.emplace_back(cur);
+ }
+ } while (space);
+
+ return result;
+}
+
+WifiCommand::WifiCommand() : mLowestInterfaceNumber(1) {
+ readConfig();
+}
+
+Result WifiCommand::onCommand(const char* /*command*/, const char* args) {
+ const char* divider = ::strchr(args, ' ');
+ if (divider == nullptr) {
+ // Unknown command, every command needs an argument
+ return Result::error("Invalid wifi command '%s'", args);
+ }
+
+ std::string subCommand(args, divider);
+ if (subCommand.empty()) {
+ return Result::error("Empty wifi command");
+ }
+
+ std::vector<std::string> subArgs = explode(divider + 1);
+ if (subArgs.empty()) {
+ // All of these commands require sub arguments
+ return Result::error("Missing argument to command '%s'",
+ subCommand.c_str());
+ }
+
+ if (subCommand == "add") {
+ return onAdd(subArgs);
+ } else if (subCommand == "block") {
+ return onBlock(subArgs);
+ } else if (subCommand == "unblock") {
+ return onUnblock(subArgs);
+ } else {
+ return Result::error("Unknown wifi command '%s'", subCommand.c_str());
+ }
+}
+
+void WifiCommand::readConfig() {
+}
+
+Result WifiCommand::writeConfig() {
+ File in(fopen(kHostApdStubFile, "r"));
+ if (!in) {
+ return Result::error("Config failure: could not open template: %s",
+ strerror(errno));
+ }
+
+ File out(fopen(kHostApdConfFile, "w"));
+ if (!out) {
+ return Result::error("Config failure: could not open target: %s",
+ strerror(errno));
+ }
+
+ char buffer[32768];
+ while (!feof(in.get())) {
+ size_t bytesRead = fread(buffer, 1, sizeof(buffer), in.get());
+ if (bytesRead != sizeof(buffer) && ferror(in.get())) {
+ return Result::error("Config failure: Error reading template: %s",
+ strerror(errno));
+ }
+
+ size_t bytesWritten = fwrite(buffer, 1, bytesRead, out.get());
+ if (bytesWritten != bytesRead) {
+ return Result::error("Config failure: Error writing target: %s",
+ strerror(errno));
+ }
+ }
+ fprintf(out.get(), "\n\n");
+
+ for (const auto& ap : mAccessPoints) {
+ fprintf(out.get(), "bss=%s\n", ap.second.ifName.c_str());
+ fprintf(out.get(), "ssid=%s\n", ap.second.ssid.c_str());
+ if (!ap.second.password.empty()) {
+ fprintf(out.get(), "wpa=2\n");
+ fprintf(out.get(), "wpa_key_mgmt=WPA-PSK\n");
+ fprintf(out.get(), "rsn_pairwise=CCMP\n");
+ fprintf(out.get(), "wpa_passphrase=%s\n", ap.second.password.c_str());
+ }
+ fprintf(out.get(), "\n");
+ }
+ return Result::success();
+}
+
+Result WifiCommand::triggerHostApd() {
+ property_set(kControlRestartProperty, kHostApdServiceName);
+ return Result::success();
+}
+
+static const char* sSetForwardRule[] = {"/system/bin/iptables",
+ "-w", // Wait for iptables lock if
+ "-W", // needed. This prevents
+ "50000", // spurious failures.
+ "<AddOrDelete>", // To be replaced
+ "FORWARD",
+ "-i",
+ "<InInterface>", // To be replaced
+ "-o",
+ "<OutInterface>", // To be replaced
+ "-j",
+ "DROP",
+ nullptr };
+
+static const char kIpTables[] = "/system/bin/iptables";
+static const char kIp6Tables[] = "/system/bin/ip6tables";
+static const char kAddRule[] = "-A";
+static const char kDeleteRule[] = "-D";
+static const size_t kIpTablesIndex = 0;
+static const size_t kActionIndex = 4;
+static const size_t kInInterfaceIndex = 7;
+static const size_t kOutInterfaceIndex = 9;
+
+
+Result WifiCommand::setBlocked(const char* ifName, bool blocked) {
+ // Blocking means adding block rules, unblocking means removing them
+ sSetForwardRule[kActionIndex] = blocked ? kAddRule : kDeleteRule;
+
+ // Do this for both IPv4 and IPv6 to ensure all traffic is blocked/unblocked
+ for (const auto& iptables : { kIpTables, kIp6Tables }) {
+ // Block traffic coming in from the outside world to this wlan
+ sSetForwardRule[kIpTablesIndex] = iptables;
+ sSetForwardRule[kInInterfaceIndex] = "eth0";
+ sSetForwardRule[kOutInterfaceIndex] = ifName;
+ if (!forkAndExec(sSetForwardRule)) {
+ return Result::error("Internal error: Unable to %s network",
+ blocked ? "block" : "unblock");
+ }
+ // Block traffic going from the wlan to the outside world
+ sSetForwardRule[kInInterfaceIndex] = ifName;
+ sSetForwardRule[kOutInterfaceIndex] = "eth0";
+ if (!forkAndExec(sSetForwardRule)) {
+ return Result::error("Internal error: Unable to %s network",
+ blocked ? "block" : "unblock");
+ }
+ }
+ return Result::success();
+}
+
+Result WifiCommand::onAdd(const std::vector<std::string>& arguments) {
+ AccessPoint& ap = mAccessPoints[arguments[0]];
+ ap.ssid = arguments[0];
+ if (arguments.size() > 1) {
+ ap.password = arguments[1];
+ } else {
+ ap.password.clear();
+ }
+ if (ap.ifName.empty()) {
+ char buffer[sizeof(kIfNamePrefix) + 10];
+ while (true) {
+ snprintf(buffer, sizeof(buffer), "%s%d",
+ kIfNamePrefix, mLowestInterfaceNumber);
+ ap.ifName = buffer;
+ auto usedInterface = mUsedInterfaces.find(ap.ifName);
+ if (usedInterface == mUsedInterfaces.end()) {
+ // This interface is available, use it
+ ++mLowestInterfaceNumber;
+ mUsedInterfaces.insert(ap.ifName);
+ break;
+ }
+ // The interface name was alread in use, try the next one
+ ++mLowestInterfaceNumber;
+ }
+ }
+ Result res = writeConfig();
+ if (!res) {
+ return res;
+ }
+ return triggerHostApd();
+}
+
+Result WifiCommand::onBlock(const std::vector<std::string>& arguments) {
+ auto interface = mAccessPoints.find(arguments[0]);
+ if (interface == mAccessPoints.end()) {
+ return Result::error("Unknown SSID '%s", arguments[0].c_str());
+ }
+ interface->second.blocked = true;
+ return setBlocked(interface->second.ifName.c_str(), true);
+}
+
+Result WifiCommand::onUnblock(const std::vector<std::string>& arguments) {
+ auto interface = mAccessPoints.find(arguments[0]);
+ if (interface == mAccessPoints.end()) {
+ return Result::error("Unknown SSID '%s", arguments[0].c_str());
+ }
+ interface->second.blocked = false;
+ return setBlocked(interface->second.ifName.c_str(), false);
+}
+
diff --git a/network/netmgr/commands/wifi_command.h b/network/netmgr/commands/wifi_command.h
new file mode 100644
index 0000000..f9ce96c
--- /dev/null
+++ b/network/netmgr/commands/wifi_command.h
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "command.h"
+#include "result.h"
+
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+class WifiCommand : public Command {
+public:
+ WifiCommand();
+ virtual ~WifiCommand() = default;
+
+ Result onCommand(const char* command, const char* args) override;
+private:
+ void readConfig();
+ Result writeConfig();
+ Result triggerHostApd();
+ Result setBlocked(const char* ifName, bool blocked);
+
+ Result onAdd(const std::vector<std::string>& args);
+ Result onBlock(const std::vector<std::string>& args);
+ Result onUnblock(const std::vector<std::string>& args);
+
+ struct AccessPoint {
+ AccessPoint() : blocked(false) {}
+ std::string ifName;
+ std::string ssid;
+ std::string password;
+ bool blocked;
+ };
+
+ std::unordered_map<std::string, AccessPoint> mAccessPoints;
+ std::unordered_set<std::string> mUsedInterfaces;
+ int mLowestInterfaceNumber;
+};
+
diff --git a/network/netmgr/fork.cpp b/network/netmgr/fork.cpp
new file mode 100644
index 0000000..49c4c61
--- /dev/null
+++ b/network/netmgr/fork.cpp
@@ -0,0 +1,67 @@
+/*
+ * 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 "fork.h"
+
+#include "log.h"
+
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+bool forkAndExec(const char* argv[]) {
+ pid_t pid = ::fork();
+ if (pid < 0) {
+ // Failed to fork
+ LOGE("fork() failed: %s", strerror(errno));
+ return false;
+ } else if (pid == 0) {
+ // Child
+ char buffer[32768];
+ size_t offset = 0;
+ for (size_t i = 0; argv[i]; ++i) {
+ offset += snprintf(buffer + offset, sizeof(buffer) - offset,
+ "%s ", argv[i]);
+ }
+ LOGE("Running '%s'", buffer);
+ execvp(argv[0], const_cast<char* const*>(argv));
+ LOGE("Failed to run '%s': %s", argv[0], strerror(errno));
+ _exit(1);
+ } else {
+ // Parent
+ int status = 0;
+ do {
+ ::waitpid(pid, &status, 0);
+ if (WIFEXITED(status)) {
+ int exitStatus = WEXITSTATUS(status);
+ if (exitStatus == 0) {
+ return true;
+ }
+ LOGE("Error: '%s' exited with code: %d", argv[0], exitStatus);
+ } else if (WIFSIGNALED(status)) {
+ LOGE("Error: '%s' terminated with signal: %d",
+ argv[0], WTERMSIG(status));
+ }
+ // Other possibilities include being stopped and continued as part
+ // of a trace but we don't really care about that. The important
+ // part is that unless the process explicitly exited or was killed
+ // by a signal we have to keep waiting.
+ } while (!WIFEXITED(status) && !WIFSIGNALED(status));
+ return false;
+ }
+}
diff --git a/network/netmgr/fork.h b/network/netmgr/fork.h
new file mode 100644
index 0000000..a7cfd48
--- /dev/null
+++ b/network/netmgr/fork.h
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+// Fork and run the provided program with arguments and wait until the program
+// exits. The list of arguments in |argv| has to be terminated by a NULL
+// pointer (e.g. { "ls", "-l", "/", nullptr } to run 'ls -l /'
+// Returns true if the process exits normally with a return code of 0. Returns
+// false if the process is terminated by a signal or if it exits with a return
+// code that is not 0.
+bool forkAndExec(const char* argv[]);
+
diff --git a/network/netmgr/interface_state.cpp b/network/netmgr/interface_state.cpp
new file mode 100644
index 0000000..57e169c
--- /dev/null
+++ b/network/netmgr/interface_state.cpp
@@ -0,0 +1,27 @@
+/*
+ * 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 "interface_state.h"
+
+const char* interfaceStateToStr(InterfaceState state) {
+ switch (state) {
+ case InterfaceState::Up:
+ return "Up";
+ case InterfaceState::Down:
+ return "Down";
+ }
+}
+
diff --git a/network/netmgr/interface_state.h b/network/netmgr/interface_state.h
new file mode 100644
index 0000000..5315b0f
--- /dev/null
+++ b/network/netmgr/interface_state.h
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+enum class InterfaceState {
+ Up,
+ Down,
+};
+
diff --git a/network/netmgr/log.cpp b/network/netmgr/log.cpp
new file mode 100644
index 0000000..7256a3f
--- /dev/null
+++ b/network/netmgr/log.cpp
@@ -0,0 +1,27 @@
+/*
+ * 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 "log.h"
+
+#include <stdio.h>
+#include <unistd.h>
+
+bool isTerminal;
+
+void initIsTerminal() {
+ isTerminal = isatty(STDOUT_FILENO) != 0;
+}
+
diff --git a/network/netmgr/log.h b/network/netmgr/log.h
new file mode 100644
index 0000000..1efe72a
--- /dev/null
+++ b/network/netmgr/log.h
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#define LOG_TAG "netmgr"
+#include <log/log.h>
+#include <stdio.h>
+
+extern bool isTerminal;
+void initIsTerminal();
+
+// Print errors to stderr if running from a terminal, otherwise print to logcat
+// This is useful for debugging from a terminal
+#define LOGE(...) do { \
+ if (isTerminal) { \
+ fprintf(stderr, __VA_ARGS__); \
+ fprintf(stderr, "\n"); \
+ } else { \
+ ALOGE(__VA_ARGS__); \
+ } \
+} 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
new file mode 100644
index 0000000..e4b43f6
--- /dev/null
+++ b/network/netmgr/main.cpp
@@ -0,0 +1,150 @@
+/*
+ * 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 "address_assigner.h"
+#include "commander.h"
+#include "commands/wifi_command.h"
+#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.");
+ LOGE(" <ip/mask> is the base IP address to assign to the first interface");
+ LOGE(" and mask indicates the netmask and broadcast to set.");
+ LOGE(" Additionally mask is used to determine the address");
+ LOGE(" for the second interface by skipping ahead one subnet");
+ LOGE(" and the size of the subnet is indicated by <mask>");
+}
+
+static bool parseNetwork(const char* network,
+ in_addr_t* address,
+ uint32_t* mask) {
+ const char* divider = strchr(network, '/');
+ if (divider == nullptr) {
+ LOGE("Network specifier '%s' is missing netmask length", network);
+ return false;
+ }
+ if (divider - network >= INET_ADDRSTRLEN) {
+ LOGE("Network specifier '%s' contains an IP address that is too long",
+ network);
+ return false;
+ }
+
+ char buffer[INET_ADDRSTRLEN];
+ strlcpy(buffer, network, divider - network + 1);
+ struct in_addr addr;
+ if (!::inet_aton(buffer, &addr)) {
+ // String could not be converted to IP address
+ LOGE("Network specifier '%s' contains an invalid IP address '%s'",
+ network, buffer);
+ return false;
+ }
+
+ ++divider;
+
+ char dummy = 0;
+ if (sscanf(divider, "%u%c", mask, &dummy) != 1) {
+ LOGE("Netork specifier '%s' contains an invalid netmask length '%s'",
+ network, divider);
+ return false;
+ }
+
+ *address = addr.s_addr;
+ return true;
+}
+
+int main(int argc, char* argv[]) {
+ const char* interfacePrefix = nullptr;
+ const char* network = nullptr;
+
+ for (int i = 1; i < argc; ++i) {
+ if (strcmp(argv[i], "--if-prefix") == 0 && i + 1 < argc) {
+ interfacePrefix = argv[++i];
+ } else if (strcmp(argv[i], "--network") == 0 && i + 1 < argc) {
+ network = argv[++i];
+ } else {
+ LOGE("Unknown parameter '%s'", argv[i]);
+ usage(argv[0]);
+ return 1;
+ }
+ }
+
+ if (interfacePrefix == nullptr) {
+ LOGE("Missing parameter --if-prefix");
+ }
+ if (network == nullptr) {
+ LOGE("Missing parameter --network");
+ }
+ if (network == nullptr || interfacePrefix == nullptr) {
+ usage(argv[0]);
+ return 1;
+ }
+
+ in_addr_t address = 0;
+ uint32_t mask = 0;
+ if (!parseNetwork(network, &address, &mask)) {
+ return 1;
+ }
+
+ AddressAssigner assigner(interfacePrefix, address, mask);
+ Monitor monitor;
+
+ monitor.setOnInterfaceState(std::bind(&AddressAssigner::onInterfaceState,
+ &assigner,
+ std::placeholders::_1,
+ std::placeholders::_2,
+ std::placeholders::_3));
+
+ Result res = monitor.init();
+ if (!res) {
+ LOGE("%s", res.c_str());
+ return 1;
+ }
+
+ Commander commander;
+ res = commander.init();
+ if (!res) {
+ LOGE("%s", res.c_str());
+ return 1;
+ }
+
+ 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
new file mode 100644
index 0000000..4ab111e
--- /dev/null
+++ b/network/netmgr/monitor.cpp
@@ -0,0 +1,167 @@
+/*
+ * 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 "monitor.h"
+
+#include "log.h"
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <linux/rtnetlink.h>
+#include <net/if.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+Monitor::Monitor() : mSocketFd(-1) {
+
+}
+
+Monitor::~Monitor() {
+ closeSocket();
+}
+
+Result Monitor::init() {
+ return openSocket();
+}
+
+void Monitor::setOnInterfaceState(OnInterfaceStateCallback callback) {
+ mOnInterfaceStateCallback = callback;
+}
+
+bool Monitor::onReadAvailable(int /*fd*/, int* /*status*/) {
+ char buffer[32768];
+ struct sockaddr_storage storage;
+
+ while (true) {
+ socklen_t addrSize = sizeof(storage);
+ int status = ::recvfrom(mSocketFd,
+ buffer,
+ sizeof(buffer),
+ MSG_DONTWAIT,
+ reinterpret_cast<struct sockaddr*>(&storage),
+ &addrSize);
+ 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));
+ // 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");
+ // It's an error but no need to exit, let's keep polling
+ return true;
+ }
+
+ size_t length = static_cast<size_t>(status);
+
+ auto hdr = reinterpret_cast<struct nlmsghdr*>(buffer);
+ while (NLMSG_OK(hdr, length) && hdr->nlmsg_type != NLMSG_DONE) {
+ switch (hdr->nlmsg_type) {
+ case RTM_NEWLINK:
+ handleNewLink(hdr);
+ break;
+ default:
+ break;
+ }
+ NLMSG_NEXT(hdr, length);
+ }
+ }
+}
+
+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::getPollData(std::vector<pollfd>* fds) const {
+ if (mSocketFd != -1) {
+ fds->push_back(pollfd{mSocketFd, POLLIN, 0});
+ }
+}
+
+Pollable::Timestamp Monitor::getTimeout() const {
+ return Pollable::Timestamp::max();
+}
+
+Result Monitor::openSocket() {
+ if (mSocketFd != -1) {
+ return Result::error("Monitor already initialized");
+ }
+
+ mSocketFd = ::socket(AF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_ROUTE);
+ if (mSocketFd == -1) {
+ return Result::error("Monitor failed to open socket: %s",
+ strerror(errno));
+ }
+
+ struct sockaddr_nl addr;
+ memset(&addr, 0, sizeof(addr));
+ addr.nl_family = AF_NETLINK;
+ addr.nl_groups = RTNLGRP_LINK | RTNLGRP_IPV4_IFADDR | RTNLGRP_IPV6_IFADDR;
+
+ struct sockaddr* sa = reinterpret_cast<struct sockaddr*>(&addr);
+ if (::bind(mSocketFd, sa, sizeof(addr)) != 0) {
+ return Result::error("Monitor failed to bind socket: %s",
+ strerror(errno));
+ }
+
+ return Result::success();
+}
+
+void Monitor::closeSocket() {
+ if (mSocketFd != -1) {
+ ::close(mSocketFd);
+ mSocketFd = -1;
+ }
+}
+
+void Monitor::handleNewLink(const struct nlmsghdr* hdr) {
+ if (!mOnInterfaceStateCallback) {
+ return;
+ }
+
+ auto msg = reinterpret_cast<const struct ifinfomsg*>(NLMSG_DATA(hdr));
+
+ if (msg->ifi_change & IFF_UP) {
+ // The interface up/down flag changed, send a notification
+ char name[IF_NAMESIZE + 1] = { 0 };
+ if_indextoname(msg->ifi_index, name);
+
+ InterfaceState state = (msg->ifi_flags & IFF_UP) ? InterfaceState::Up :
+ InterfaceState::Down;
+ mOnInterfaceStateCallback(msg->ifi_index, name, state);
+ }
+}
+
diff --git a/network/netmgr/monitor.h b/network/netmgr/monitor.h
new file mode 100644
index 0000000..3d2dc62
--- /dev/null
+++ b/network/netmgr/monitor.h
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "interface_state.h"
+#include "pollable.h"
+#include "result.h"
+
+const char* interfaceStateToStr(InterfaceState state);
+
+/** Monitor network interfaces and provide notifications of changes to those
+ * interfaces.
+ */
+class Monitor : public Pollable {
+public:
+ using OnInterfaceStateCallback = std::function<void (unsigned int index,
+ const char* name,
+ InterfaceState state)>;
+ Monitor();
+ ~Monitor();
+
+ Result init();
+
+ void setOnInterfaceState(OnInterfaceStateCallback callback);
+
+ // 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:
+ Result openSocket();
+ void closeSocket();
+ void handleNewLink(const struct nlmsghdr* hdr);
+
+ int mSocketFd;
+ OnInterfaceStateCallback mOnInterfaceStateCallback;
+};
+
diff --git a/network/netmgr/pollable.h b/network/netmgr/pollable.h
new file mode 100644
index 0000000..fbf4baf
--- /dev/null
+++ b/network/netmgr/pollable.h
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+#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;
+ virtual ~Pollable() = default;
+
+ /* 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
new file mode 100644
index 0000000..8dcbcab
--- /dev/null
+++ b/network/netmgr/poller.cpp
@@ -0,0 +1,158 @@
+/*
+ * 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 "poller.h"
+
+#include "log.h"
+
+#include <errno.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <unordered_map>
+#include <vector>
+
+using std::chrono::duration_cast;
+
+static struct timespec* calculateTimeout(Pollable::Timestamp deadline,
+ struct timespec* ts) {
+ Pollable::Timestamp now = Pollable::Clock::now();
+ if (deadline < Pollable::Timestamp::max()) {
+ 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
+ auto nanos = duration_cast<std::chrono::nanoseconds>(timeout - seconds);
+
+ ts->tv_sec = seconds.count();
+ ts->tv_nsec = nanos.count();
+
+ return ts;
+ }
+ return nullptr;
+}
+
+Poller::Poller() {
+}
+
+void Poller::addPollable(Pollable* pollable) {
+ mPollables.push_back(pollable);
+}
+
+int Poller::run() {
+ // Block all signals while we're running. This way we don't have to deal
+ // with things like EINTR. We then uses ppoll to set the original mask while
+ // polling. This way polling can be interrupted but socket writing, reading
+ // and ioctl remain interrupt free. If a signal arrives while we're blocking
+ // it it will be placed in the signal queue and handled once ppoll sets the
+ // original mask. This way no signals are lost.
+ sigset_t blockMask, mask;
+ int status = ::sigfillset(&blockMask);
+ if (status != 0) {
+ LOGE("Unable to fill signal set: %s", strerror(errno));
+ return errno;
+ }
+ status = ::sigprocmask(SIG_SETMASK, &blockMask, &mask);
+ if (status != 0) {
+ LOGE("Unable to set signal mask: %s", strerror(errno));
+ return errno;
+ }
+
+ std::vector<struct pollfd> fds;
+ std::unordered_map<int, Pollable*> pollables;
+ while (true) {
+ fds.clear();
+ pollables.clear();
+ Pollable::Timestamp deadline = Pollable::Timestamp::max();
+ 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 (pollableDeadline < deadline) {
+ deadline = pollableDeadline;
+ }
+ }
+
+ struct timespec ts = { 0, 0 };
+ struct timespec* tsPtr = calculateTimeout(deadline, &ts);
+ status = ::ppoll(fds.data(), fds.size(), tsPtr, &mask);
+ if (status < 0) {
+ if (errno == EINTR) {
+ // Interrupted, just keep going
+ continue;
+ }
+ // Actual error, time to quit
+ LOGE("Polling failed: %s", strerror(errno));
+ return errno;
+ } else if (status > 0) {
+ // Check for read or close events
+ for (const auto& fd : fds) {
+ 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
+ int status = 0;
+ if (!pollable->second->onClose(fd.fd, &status)) {
+ // The onClose handler signaled an exit
+ return status;
+ }
+ }
+ }
+ }
+ // 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;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/network/netmgr/poller.h b/network/netmgr/poller.h
new file mode 100644
index 0000000..9794f4d
--- /dev/null
+++ b/network/netmgr/poller.h
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <vector>
+
+#include "pollable.h"
+
+class Poller {
+public:
+ Poller();
+
+ void addPollable(Pollable* pollable);
+
+ int run();
+
+private:
+ std::vector<Pollable*> mPollables;
+};
+
diff --git a/network/netmgr/result.h b/network/netmgr/result.h
new file mode 100644
index 0000000..5087e14
--- /dev/null
+++ b/network/netmgr/result.h
@@ -0,0 +1,54 @@
+/*
+ * 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 <stdio.h>
+#include <stdarg.h>
+
+#include <string>
+
+class Result {
+public:
+ static Result success() {
+ return Result(true);
+ }
+ // Construct a result indicating an error.
+ static Result error(std::string message) {
+ return Result(message);
+ }
+ static Result error(const char* format, ...) {
+ char buffer[1024];
+ va_list args;
+ va_start(args, format);
+ vsnprintf(buffer, sizeof(buffer), format, args);
+ va_end(args);
+ buffer[sizeof(buffer) - 1] = '\0';
+ return Result(std::string(buffer));
+ }
+
+ bool isSuccess() const { return mSuccess; }
+ bool operator!() const { return !mSuccess; }
+
+ const char* c_str() const { return mMessage.c_str(); }
+private:
+ explicit Result(bool success) : mSuccess(success) { }
+ explicit Result(std::string message)
+ : mMessage(message), mSuccess(false) {
+ }
+ std::string mMessage;
+ bool mSuccess;
+};
+
diff --git a/network/netmgr/timestamp.cpp b/network/netmgr/timestamp.cpp
new file mode 100644
index 0000000..36c79ee
--- /dev/null
+++ b/network/netmgr/timestamp.cpp
@@ -0,0 +1,33 @@
+/*
+ * 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 "timestamp.h"
+
+
+Timestamp::Timestamp() {
+ memset(&mTime, 0, sizeof(mTime));
+}
+
+static Timestamp Timestamp::now() {
+ Timestamp t;
+ clock_gettime(CLOCK_MONOTONIC, &t.mTime);
+ return t;
+}
+
+bool Timestamp::operator==(const Timestamp& other) const {
+}
+bool Timestamp::operator<(const Timestamp& other) const {
+}
diff --git a/network/netmgr/timestamp.h b/network/netmgr/timestamp.h
new file mode 100644
index 0000000..8ad7bf8
--- /dev/null
+++ b/network/netmgr/timestamp.h
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <time.h>
+
+class Timestamp {
+public:
+ Timestamp();
+
+ static Timestamp now();
+
+ bool operator==(const Timestamp& other) const;
+ bool operator<(const Timestamp& other) const;
+
+private:
+ struct timespec mTime;
+};
+
diff --git a/network/netmgr/wifi_forwarder.cpp b/network/netmgr/wifi_forwarder.cpp
new file mode 100644
index 0000000..e6997d4
--- /dev/null
+++ b/network/netmgr/wifi_forwarder.cpp
@@ -0,0 +1,432 @@
+/*
+ * 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 {
+ if (mPipeFd == -1) {
+ return;
+ }
+ 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/ril/Android.mk b/ril/Android.mk
index 34bd227..cb5f31a 100644
--- a/ril/Android.mk
+++ b/ril/Android.mk
@@ -8,6 +8,7 @@ include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
reference-ril.c \
atchannel.c \
+ if_monitor.cpp \
misc.c \
at_tok.c
diff --git a/ril/atchannel.c b/ril/atchannel.c
index 0041836..407a204 100644
--- a/ril/atchannel.c
+++ b/ril/atchannel.c
@@ -22,6 +22,7 @@
#include <string.h>
#include <pthread.h>
#include <ctype.h>
+#include <poll.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
diff --git a/ril/if_monitor.cpp b/ril/if_monitor.cpp
new file mode 100644
index 0000000..289477d
--- /dev/null
+++ b/ril/if_monitor.cpp
@@ -0,0 +1,352 @@
+/*
+ * 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 "if_monitor.h"
+
+#include <errno.h>
+#include <linux/rtnetlink.h>
+#include <net/if.h>
+#include <poll.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <mutex>
+#include <thread>
+#include <unordered_map>
+#include <vector>
+
+#define LOG_TAG "RIL-IFMON"
+#include <utils/Log.h>
+
+static const size_t kReadBufferSize = 32768;
+
+static const size_t kControlServer = 0;
+static const size_t kControlClient = 1;
+
+// A list of commands that can be sent to the monitor. These should be one
+// character long as that is all that the monitor will read and process.
+static const char kMonitorStopCommand[] = "\1";
+static const char kMonitorAckCommand[] = "\2";
+
+static size_t addrLength(int addrFamily) {
+ switch (addrFamily) {
+ case AF_INET:
+ return 4;
+ case AF_INET6:
+ return 16;
+ default:
+ return 0;
+ }
+}
+
+bool operator==(const struct ifAddress& left, const struct ifAddress& right) {
+ // The prefix length does not factor in to whether two addresses are the
+ // same or not. Only the family and the address data. This matches the
+ // kernel behavior when attempting to add the same address with different
+ // prefix lengths, those changes are rejected because the address already
+ // exists.
+ return left.family == right.family &&
+ memcmp(&left.addr, &right.addr, addrLength(left.family)) == 0;
+}
+
+class InterfaceMonitor {
+public:
+ InterfaceMonitor() : mSocketFd(-1) {
+ mControlSocket[kControlServer] = -1;
+ mControlSocket[kControlClient] = -1;
+ }
+
+ ~InterfaceMonitor() {
+ if (mControlSocket[kControlClient] != -1) {
+ ::close(mControlSocket[kControlClient]);
+ mControlSocket[kControlClient] = -1;
+ }
+ if (mControlSocket[kControlServer] != -1) {
+ ::close(mControlSocket[kControlServer]);
+ mControlSocket[kControlServer] = -1;
+ }
+
+ if (mSocketFd != -1) {
+ ::close(mSocketFd);
+ mSocketFd = -1;
+ }
+ }
+
+ bool init() {
+ if (mSocketFd != -1) {
+ RLOGE("InterfaceMonitor already initialized");
+ return false;
+ }
+
+ mSocketFd = ::socket(AF_NETLINK,
+ SOCK_DGRAM | SOCK_CLOEXEC,
+ NETLINK_ROUTE);
+ if (mSocketFd == -1) {
+ RLOGE("InterfaceMonitor failed to open socket: %s", strerror(errno));
+ return false;
+ }
+
+ if (::socketpair(AF_UNIX, SOCK_DGRAM, 0, mControlSocket) != 0) {
+ RLOGE("Unable to create control socket pair: %s", strerror(errno));
+ return false;
+ }
+
+ struct sockaddr_nl addr;
+ memset(&addr, 0, sizeof(addr));
+ addr.nl_family = AF_NETLINK;
+ addr.nl_groups = (1 << (RTNLGRP_IPV4_IFADDR - 1)) |
+ (1 << (RTNLGRP_IPV6_IFADDR - 1));
+
+ struct sockaddr* sa = reinterpret_cast<struct sockaddr*>(&addr);
+ if (::bind(mSocketFd, sa, sizeof(addr)) != 0) {
+ RLOGE("InterfaceMonitor failed to bind socket: %s",
+ strerror(errno));
+ return false;
+ }
+
+ return true;
+ }
+
+ void setCallback(ifMonitorCallback callback) {
+ mOnAddressChangeCallback = callback;
+ }
+
+ void runAsync() {
+ std::unique_lock<std::mutex> lock(mThreadMutex);
+ mThread = std::make_unique<std::thread>([this]() { run(); });
+ }
+
+ void requestAddress() {
+ struct {
+ struct nlmsghdr hdr;
+ struct ifaddrmsg msg;
+ char padding[16];
+ } request;
+
+ memset(&request, 0, sizeof(request));
+ request.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(request.msg));
+ request.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT;
+ request.hdr.nlmsg_type = RTM_GETADDR;
+
+ int status = ::send(mSocketFd, &request, request.hdr.nlmsg_len, 0);
+ if (status < 0 ||
+ static_cast<unsigned int>(status) != request.hdr.nlmsg_len) {
+ if (status < 0) {
+ RLOGE("Failed to send netlink request: %s", strerror(errno));
+ } else {
+ RLOGE("Short send only sent %d out of %d bytes",
+ status, (int)request.hdr.nlmsg_len);
+ }
+ }
+ }
+
+ void run() {
+ requestAddress();
+
+ std::vector<struct pollfd> fds(2);
+ fds[0].events = POLLIN;
+ fds[0].fd = mControlSocket[kControlServer];
+ fds[1].events = POLLIN;
+ fds[1].fd = mSocketFd;
+ while (true) {
+ int status = ::poll(fds.data(), fds.size(), -1);
+ if (status < 0) {
+ if (errno == EINTR) {
+ // Interrupted, just keep going
+ continue;
+ }
+ // Actual error, time to quit
+ RLOGE("Polling failed: %s", strerror(errno));
+ break;
+ } else if (status == 0) {
+ // Timeout
+ continue;
+ }
+
+ if (fds[0].revents & POLLIN) {
+ // Control message received
+ char command = -1;
+ if (::read(mControlSocket[kControlServer],
+ &command,
+ sizeof(command)) == 1) {
+ if (command == kMonitorStopCommand[0]) {
+ break;
+ }
+ }
+ } else if (fds[1].revents & POLLIN) {
+ onReadAvailable();
+ }
+ }
+ ::write(mControlSocket[kControlServer], kMonitorAckCommand, 1);
+ }
+
+ void stop() {
+ std::unique_lock<std::mutex> lock(mThreadMutex);
+ if (mThread) {
+ ::write(mControlSocket[kControlClient], kMonitorStopCommand, 1);
+ char ack = -1;
+ while (ack != kMonitorAckCommand[0]) {
+ ::read(mControlSocket[kControlClient], &ack, sizeof(ack));
+ }
+ mThread->join();
+ mThread.reset();
+ }
+ }
+
+private:
+ void onReadAvailable() {
+ char buffer[kReadBufferSize];
+ struct sockaddr_storage storage;
+
+ while (true) {
+ socklen_t addrSize = sizeof(storage);
+ int status = ::recvfrom(mSocketFd,
+ buffer,
+ sizeof(buffer),
+ 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 && errno == EINTR) {
+ // Caught interrupt, try again
+ continue;
+ } else if (status < 0) {
+ RLOGE("InterfaceMonitor receive failed: %s", strerror(errno));
+ return;
+ } else if (addrSize < 0 ||
+ static_cast<size_t>(addrSize) != sizeof(struct sockaddr_nl)) {
+ RLOGE("InterfaceMonitor received invalid address size");
+ return;
+ }
+
+ size_t length = static_cast<size_t>(status);
+
+ auto hdr = reinterpret_cast<struct nlmsghdr*>(buffer);
+ while (NLMSG_OK(hdr, length) && hdr->nlmsg_type != NLMSG_DONE) {
+ switch (hdr->nlmsg_type) {
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ handleAddressChange(hdr);
+ break;
+ default:
+ RLOGE("Received message type %d", (int)hdr->nlmsg_type);
+ break;
+ }
+ NLMSG_NEXT(hdr, length);
+ }
+ }
+ }
+
+ void handleAddressChange(const struct nlmsghdr* hdr) {
+ if (!mOnAddressChangeCallback) {
+ return;
+ }
+
+ auto msg = reinterpret_cast<const struct ifaddrmsg*>(NLMSG_DATA(hdr));
+ std::vector<ifAddress>& ifAddrs = mAddresses[msg->ifa_index];
+
+ auto attr = reinterpret_cast<const struct rtattr*>(IFA_RTA(msg));
+ int attrLen = IFA_PAYLOAD(hdr);
+
+ bool somethingChanged = false;
+ for (;attr && RTA_OK(attr, attrLen); attr = RTA_NEXT(attr, attrLen)) {
+ if (attr->rta_type != IFA_LOCAL && attr->rta_type != IFA_ADDRESS) {
+ continue;
+ }
+
+ ifAddress addr;
+ memset(&addr, 0, sizeof(addr));
+
+ // Ensure that the payload matches the expected address length
+ if (RTA_PAYLOAD(attr) >= addrLength(msg->ifa_family)) {
+ addr.family = msg->ifa_family;
+ addr.prefix = msg->ifa_prefixlen;
+ memcpy(&addr.addr, RTA_DATA(attr), addrLength(addr.family));
+ } else {
+ RLOGE("Invalid address family (%d) and size (%d) combination",
+ int(msg->ifa_family), int(RTA_PAYLOAD(attr)));
+ continue;
+ }
+
+ auto it = std::find(ifAddrs.begin(), ifAddrs.end(), addr);
+ if (hdr->nlmsg_type == RTM_NEWADDR && it == ifAddrs.end()) {
+ // New address does not exist, add it
+ ifAddrs.push_back(addr);
+ somethingChanged = true;
+ } else if (hdr->nlmsg_type == RTM_DELADDR && it != ifAddrs.end()) {
+ // Address was removed and it exists, remove it
+ ifAddrs.erase(it);
+ somethingChanged = true;
+ }
+ }
+
+ if (somethingChanged) {
+ mOnAddressChangeCallback(msg->ifa_index,
+ ifAddrs.data(),
+ ifAddrs.size());
+ }
+ }
+
+ ifMonitorCallback mOnAddressChangeCallback;
+ std::unordered_map<unsigned int, std::vector<ifAddress>> mAddresses;
+ std::unique_ptr<std::thread> mThread;
+ std::mutex mThreadMutex;
+ int mSocketFd;
+ int mControlSocket[2];
+};
+
+extern "C"
+struct ifMonitor* ifMonitorCreate() {
+ auto monitor = std::make_unique<InterfaceMonitor>();
+ if (!monitor || !monitor->init()) {
+ return nullptr;
+ }
+ return reinterpret_cast<struct ifMonitor*>(monitor.release());
+}
+
+extern "C"
+void ifMonitorFree(struct ifMonitor* ifMonitor) {
+ InterfaceMonitor* monitor = reinterpret_cast<InterfaceMonitor*>(ifMonitor);
+ delete monitor;
+}
+
+extern "C"
+void ifMonitorSetCallback(struct ifMonitor* ifMonitor,
+ ifMonitorCallback callback) {
+ InterfaceMonitor* monitor = reinterpret_cast<InterfaceMonitor*>(ifMonitor);
+ monitor->setCallback(callback);
+}
+
+extern "C"
+void ifMonitorRunAsync(struct ifMonitor* ifMonitor) {
+ InterfaceMonitor* monitor = reinterpret_cast<InterfaceMonitor*>(ifMonitor);
+
+ monitor->runAsync();
+}
+
+extern "C"
+void ifMonitorStop(struct ifMonitor* ifMonitor) {
+ InterfaceMonitor* monitor = reinterpret_cast<InterfaceMonitor*>(ifMonitor);
+
+ monitor->stop();
+}
+
diff --git a/ril/if_monitor.h b/ril/if_monitor.h
new file mode 100644
index 0000000..118bf88
--- /dev/null
+++ b/ril/if_monitor.h
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ifMonitor;
+
+struct ifAddress {
+ int family;
+ int prefix;
+ unsigned char addr[16];
+};
+
+// A callback for when the addresses on an interface changes
+typedef void (*ifMonitorCallback)(unsigned int /*interface index*/,
+ const struct ifAddress* /*addresses*/,
+ size_t /*number of addresses */);
+
+struct ifMonitor* ifMonitorCreate();
+void ifMonitorFree(struct ifMonitor* monitor);
+
+void ifMonitorSetCallback(struct ifMonitor* monitor,
+ ifMonitorCallback callback);
+void ifMonitorRunAsync(struct ifMonitor* monitor);
+void ifMonitorStop(struct ifMonitor* monitor);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
diff --git a/ril/reference-ril.c b/ril/reference-ril.c
index 51f63ea..7e52e10 100644
--- a/ril/reference-ril.c
+++ b/ril/reference-ril.c
@@ -41,8 +41,10 @@
#include <sys/wait.h>
#include <stdbool.h>
#include <net/if.h>
+#include <arpa/inet.h>
#include <netinet/in.h>
+#include "if_monitor.h"
#include "ril.h"
#define LOG_TAG "RIL"
@@ -59,6 +61,11 @@ static void *noopRemoveWarning( void *a ) { return a; }
// This is used if Wifi is supported to separate radio and wifi interface
#define PPP_TTY_PATH_RADIO0 "radio0"
+// This is the IP address to provide for radio0 when WiFi is enabled
+// When WiFi is not enabled the RIL should provide the address given by
+// the modem.
+#define RADIO0_IPV4_ADDRESS "192.168.200.2/24"
+
// Default MTU value
#define DEFAULT_MTU 1500
@@ -266,6 +273,10 @@ static int s_mnc = 0;
static int s_lac = 0;
static int s_cid = 0;
+// A string containing all the IPv6 addresses of the radio interface
+static char s_ipv6_addresses[8192];
+static pthread_mutex_t s_ipv6_addresses_mutex = PTHREAD_MUTEX_INITIALIZER;
+
static void pollSIMState (void *param);
static void setRadioState(RIL_RadioState newState);
static void setRadioTechnology(ModemInfo *mdm, int newtech);
@@ -716,13 +727,32 @@ static void requestOrSendDataCallList(RIL_Token *t)
responses[i].ifname = alloca(ifname_size);
strlcpy(responses[i].ifname, radioInterfaceName, ifname_size);
+ // The next token is the IPv4 address provided by the emulator, only use
+ // it if WiFi is not enabled. When WiFi is enabled the network setup is
+ // specific to the system image and the emulator only provides the
+ // IP address for the external interface in the router namespace.
err = at_tok_nextstr(&line, &out);
if (err < 0)
goto error;
- int addresses_size = strlen(out) + 1;
+ pthread_mutex_lock(&s_ipv6_addresses_mutex);
+
+ // Extra space for null terminator and separating space
+ int addresses_size = strlen(out) + strlen(s_ipv6_addresses) + 2;
responses[i].addresses = alloca(addresses_size);
- strlcpy(responses[i].addresses, out, addresses_size);
+ if (*s_ipv6_addresses) {
+ // IPv6 addresses exist, add them
+ snprintf(responses[i].addresses, addresses_size,
+ "%s %s",
+ hasWifi ? RADIO0_IPV4_ADDRESS : out,
+ s_ipv6_addresses);
+ } else {
+ // Only provide the IPv4 address
+ strlcpy(responses[i].addresses,
+ hasWifi ? RADIO0_IPV4_ADDRESS : out,
+ addresses_size);
+ }
+ pthread_mutex_unlock(&s_ipv6_addresses_mutex);
if (isInEmulator()) {
/* We are in the emulator - the dns servers are listed
@@ -733,16 +763,16 @@ static void requestOrSendDataCallList(RIL_Token *t)
* - net.eth0.dns3
* - net.eth0.dns4
*/
- const int dnslist_sz = 128;
+ const int dnslist_sz = 256;
char* dnslist = alloca(dnslist_sz);
const char* separator = "";
int nn;
+ char propName[PROP_NAME_MAX];
+ char propValue[PROP_VALUE_MAX];
dnslist[0] = 0;
for (nn = 1; nn <= 4; nn++) {
/* Probe net.eth0.dns<n> */
- char propName[PROP_NAME_MAX];
- char propValue[PROP_VALUE_MAX];
snprintf(propName, sizeof propName, "net.eth0.dns%d", nn);
@@ -756,6 +786,18 @@ static void requestOrSendDataCallList(RIL_Token *t)
strlcat(dnslist, propValue, dnslist_sz);
separator = " ";
}
+ for (nn = 1; nn <= 4; ++nn) {
+ /* Probe net.eth0.ipv6dns<n> for IPv6 DNS servers */
+ snprintf(propName, sizeof propName, "net.eth0.ipv6dns%d", nn);
+ /* Ignore if undefined */
+ if (property_get(propName, propValue, "") <= 0) {
+ continue;
+ }
+ strlcat(dnslist, separator, dnslist_sz);
+ strlcat(dnslist, propValue, dnslist_sz);
+ separator = " ";
+ }
+
responses[i].dnses = dnslist;
/* There is only one gateway in the emulator. If WiFi is
@@ -3883,16 +3925,79 @@ static void usage(char *s __unused)
#endif
}
+static void onInterfaceAddressChange(unsigned int ifIndex,
+ const struct ifAddress* addresses,
+ size_t numAddresses) {
+ char ifName[IF_NAMESIZE];
+ size_t i;
+ bool hasWifi = hasWifiCapability();
+ const char* radioIfName = getRadioInterfaceName(hasWifi);
+ char* currentLoc;
+ size_t remaining;
+
+ if (if_indextoname(ifIndex, ifName) == NULL) {
+ RLOGE("Unable to get interface name for interface %u", ifIndex);
+ return;
+ }
+ if (strcmp(radioIfName, ifName) != 0) {
+ // This is not for the radio interface, ignore it
+ return;
+ }
+
+ pthread_mutex_lock(&s_ipv6_addresses_mutex);
+ // Clear out any existing addresses, we receive a full set of addresses
+ // that are going to replace the existing ones.
+ s_ipv6_addresses[0] = '\0';
+ currentLoc = s_ipv6_addresses;
+ remaining = sizeof(s_ipv6_addresses);
+ for (i = 0; i < numAddresses; ++i) {
+ if (addresses[i].family != AF_INET6) {
+ // Only care about IPv6 addresses
+ continue;
+ }
+ char address[INET6_ADDRSTRLEN];
+ if (inet_ntop(addresses[i].family, &addresses[i].addr,
+ address, sizeof(address))) {
+ int printed = 0;
+ if (s_ipv6_addresses[0]) {
+ // We've already printed something, separate them
+ if (remaining < 1) {
+ continue;
+ }
+ *currentLoc++ = ' ';
+ --remaining;
+ }
+ printed = snprintf(currentLoc, remaining, "%s/%d",
+ address, addresses[i].prefix);
+ if (printed > 0) {
+ remaining -= (size_t)printed;
+ currentLoc += printed;
+ }
+ } else {
+ RLOGE("Unable to convert address to string for if %s", ifName);
+ }
+ }
+ pthread_mutex_unlock(&s_ipv6_addresses_mutex);
+
+ // Send unsolicited call list change to notify upper layers about the new
+ // addresses
+ requestOrSendDataCallList(NULL);
+}
+
static void *
mainLoop(void *param __unused)
{
int fd;
int ret;
+ struct ifMonitor* monitor = ifMonitorCreate();
AT_DUMP("== ", "entering mainLoop()", -1 );
at_set_on_reader_closed(onATReaderClosed);
at_set_on_timeout(onATTimeout);
+ ifMonitorSetCallback(monitor, &onInterfaceAddressChange);
+ ifMonitorRunAsync(monitor);
+
for (;;) {
fd = -1;
while (fd < 0) {
@@ -3916,20 +4021,21 @@ mainLoop(void *param __unused)
}
if (fd < 0) {
- perror ("opening AT interface. retrying...");
+ RLOGE("Error opening AT interface, retrying...");
sleep(10);
/* never returns */
}
}
s_closed = 0;
- ret = at_open(fd, onUnsolicited);
+ ret = at_open(fd, onUnsolicited);
if (ret < 0) {
RLOGE ("AT error %d on at_open\n", ret);
- return 0;
+ break;
}
+
RIL_requestTimedCallback(initializeCallback, NULL, &TIMEVAL_0);
// Give initializeCallback a chance to dispatched, since
@@ -3939,6 +4045,11 @@ mainLoop(void *param __unused)
waitForClose();
RLOGI("Re-opening after close");
}
+
+ ifMonitorStop(monitor);
+ ifMonitorFree(monitor);
+
+ return NULL;
}
#ifdef RIL_SHLIB
diff --git a/sepolicy/common/execns.te b/sepolicy/common/execns.te
index dc6c424..24eee41 100644
--- a/sepolicy/common/execns.te
+++ b/sepolicy/common/execns.te
@@ -21,6 +21,9 @@ domain_auto_trans(execns, dhcpserver_exec, dhcpserver);
# Allow hostapd_nohidl to be run by execns in its own domain
domain_auto_trans(execns, hostapd_nohidl_exec, hostapd_nohidl);
+# Allow netmgr to be run by execns in its own domain
+domain_auto_trans(execns, netmgr_exec, netmgr);
+
# Allow execns to read createns proc file to get the namespace file
allow execns createns:file read;
allow execns createns:dir search;
diff --git a/sepolicy/common/file_contexts b/sepolicy/common/file_contexts
index 3c9df34..f429ccb 100644
--- a/sepolicy/common/file_contexts
+++ b/sepolicy/common/file_contexts
@@ -25,6 +25,7 @@
/vendor/bin/dhcpclient u:object_r:dhcpclient_exec:s0
/vendor/bin/dhcpserver u:object_r:dhcpserver_exec:s0
/vendor/bin/hostapd_nohidl u:object_r:hostapd_nohidl_exec:s0
+/vendor/bin/netmgr u:object_r:netmgr_exec:s0
/vendor/bin/hw/android\.hardware\.drm@1\.0-service\.widevine u:object_r:hal_drm_widevine_exec:s0
/vendor/bin/hw/android\.hardware\.drm@1\.1-service\.widevine u:object_r:hal_drm_widevine_exec:s0
diff --git a/sepolicy/common/goldfish_setup.te b/sepolicy/common/goldfish_setup.te
index 3041436..fba9150 100644
--- a/sepolicy/common/goldfish_setup.te
+++ b/sepolicy/common/goldfish_setup.te
@@ -6,9 +6,10 @@ init_daemon_domain(goldfish_setup)
# TODO(b/79502552): Invalid property access from emulator vendor
#set_prop(goldfish_setup, debug_prop);
-allow goldfish_setup self:capability { net_admin net_raw };
+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 };
@@ -45,3 +46,6 @@ allow goldfish_setup self:rawip_socket { create getopt setopt };
allow goldfish_setup createns:file { read };
allow goldfish_setup createns:dir { search };
allow goldfish_setup createns:lnk_file { read };
+# Allow goldfish_setup to copy the hostapd conf template to the vendor data dir
+allow goldfish_setup hostapd_data_file:file create_file_perms;
+allow goldfish_setup hostapd_data_file:dir rw_dir_perms;
diff --git a/sepolicy/common/hostapd_nohidl.te b/sepolicy/common/hostapd_nohidl.te
index add648a..badad45 100644
--- a/sepolicy/common/hostapd_nohidl.te
+++ b/sepolicy/common/hostapd_nohidl.te
@@ -6,10 +6,12 @@ net_domain(hostapd_nohidl)
allow hostapd_nohidl execns:fd use;
+allow hostapd_nohidl hostapd_data_file:file r_file_perms;
+allow hostapd_nohidl hostapd_data_file:dir r_dir_perms;
allow hostapd_nohidl self:capability { net_admin net_raw };
allow hostapd_nohidl self:netlink_generic_socket { bind create getattr read setopt write };
allow hostapd_nohidl self:netlink_route_socket nlmsg_write;
-allow hostapd_nohidl self:packet_socket { create setopt };
+allow hostapd_nohidl self:packet_socket { create setopt read write };
allowxperm hostapd_nohidl self:udp_socket ioctl priv_sock_ioctls;
# hostapd will attempt to search sysfs but it's not needed and will spam the log
diff --git a/sepolicy/common/ipv6proxy.te b/sepolicy/common/ipv6proxy.te
index 22976fe..1defe10 100644
--- a/sepolicy/common/ipv6proxy.te
+++ b/sepolicy/common/ipv6proxy.te
@@ -9,6 +9,7 @@ net_domain(ipv6proxy)
domain_auto_trans(execns, ipv6proxy_exec, ipv6proxy);
allow ipv6proxy execns:fd use;
+set_prop(ipv6proxy, net_eth0_prop);
allow ipv6proxy self:capability { sys_admin sys_module net_admin net_raw };
allow ipv6proxy self:packet_socket { bind create read };
allow ipv6proxy self:netlink_route_socket nlmsg_write;
diff --git a/sepolicy/common/netmgr.te b/sepolicy/common/netmgr.te
new file mode 100644
index 0000000..fec1f5e
--- /dev/null
+++ b/sepolicy/common/netmgr.te
@@ -0,0 +1,29 @@
+# Wifi manager
+type netmgr, domain;
+type netmgr_exec, exec_type, vendor_file_type, file_type;
+
+init_daemon_domain(netmgr)
+net_domain(netmgr)
+
+allow netmgr execns:fd use;
+
+# Set ctrl.restart property to restart hostapd when config changes
+set_prop(netmgr, ctl_default_prop);
+# Modify hostapd config file
+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/vendor.mk b/vendor.mk
index 37b170a..aa2db85 100644
--- a/vendor.mk
+++ b/vendor.mk
@@ -123,6 +123,7 @@ PRODUCT_PACKAGES += \
hostapd \
hostapd_nohidl \
ipv6proxy \
+ netmgr \
wpa_supplicant \
PRODUCT_COPY_FILES += \
diff --git a/wifi/init.wifi.sh b/wifi/init.wifi.sh
index 9c08654..8ebe591 100755
--- a/wifi/init.wifi.sh
+++ b/wifi/init.wifi.sh
@@ -44,16 +44,40 @@
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}
+
+# If this is a clean boot we need to copy the hostapd configuration file to the
+# data partition where netmgr can change it if needed. If it already exists we
+# need to preserve the existing settings.
+if [ ! -f /data/vendor/wifi/hostapd/hostapd.conf ]; then
+ cp /vendor/etc/simulated_hostapd.conf /data/vendor/wifi/hostapd/hostapd.conf
+ chown wifi:wifi /data/vendor/wifi/hostapd/hostapd.conf
+ chmod 660 /data/vendor/wifi/hostapd/hostapd.conf
+fi
+
# 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}
@@ -73,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/ipv6proxy/log.h b/wifi/ipv6proxy/log.h
index 527be29..53e8935 100644
--- a/wifi/ipv6proxy/log.h
+++ b/wifi/ipv6proxy/log.h
@@ -18,7 +18,7 @@
#ifdef ANDROID
#define LOG_TAG "ipv6proxy"
-#include <cutils/log.h>
+#include <log/log.h>
#define loge(...) ALOGE(__VA_ARGS__)
#define logd(...) ALOGD(__VA_ARGS__)
diff --git a/wifi/ipv6proxy/proxy.cpp b/wifi/ipv6proxy/proxy.cpp
index 08fc0ed..cb19ff7 100644
--- a/wifi/ipv6proxy/proxy.cpp
+++ b/wifi/ipv6proxy/proxy.cpp
@@ -16,11 +16,14 @@
#include "proxy.h"
+#include <arpa/inet.h>
#include <errno.h>
#include <linux/if_packet.h>
#include <poll.h>
#include <signal.h>
+#include <cutils/properties.h>
+
#include "log.h"
#include "message.h"
#include "packet.h"
@@ -29,6 +32,7 @@
// The prefix length for an address of a single unique node
static const uint8_t kNodePrefixLength = 128;
static const size_t kLinkAddressSize = 6;
+static const size_t kRecursiveDnsOptHeaderSize = 8;
// Rewrite the link address of a neighbor discovery option to the link address
// of |interface|. This can be either a source or target link address as
@@ -46,6 +50,45 @@ static void rewriteLinkAddressOption(Packet& packet,
}
}
+static void extractRecursiveDnsServers(Packet& packet) {
+ for (nd_opt_hdr* opt = packet.firstOpt(); opt; opt = packet.nextOpt(opt)) {
+ if (opt->nd_opt_type != 25 || opt->nd_opt_len < 1) {
+ // Not a RNDSS option, skip it
+ continue;
+ }
+ size_t numEntries = (opt->nd_opt_len - 1) / 2;
+ //Found number of entries, dump each address
+ const char* option = reinterpret_cast<const char*>(opt);
+ option += kRecursiveDnsOptHeaderSize;
+ auto dnsServers = reinterpret_cast<const struct in6_addr*>(option);
+
+ std::vector<std::string> validServers;
+ for (size_t i = 0; i < numEntries; ++i) {
+ char buffer[INET6_ADDRSTRLEN];
+ if (inet_ntop(AF_INET6, &dnsServers[i], buffer, sizeof(buffer))) {
+ validServers.push_back(buffer);
+ } else {
+ loge("Failed to convert RDNSS to string\n");
+ }
+ }
+
+ auto server = validServers.begin();
+ char propName[PROP_NAME_MAX];
+ char propValue[PROP_VALUE_MAX];
+ for (int i = 1; i <= 4; ++i) {
+ snprintf(propName, sizeof(propName), "net.eth0.ipv6dns%d", i);
+ if (server != validServers.end()) {
+ property_set(propName, server->c_str());
+ ++server;
+ } else {
+ // Clear the property if it's no longer a valid server, don't
+ // want to leave old servers around
+ property_set(propName, "");
+ }
+ }
+ }
+}
+
int Proxy::run() {
sigset_t blockMask, originalMask;
int status = ::sigfillset(&blockMask);
@@ -128,6 +171,7 @@ void Proxy::handleOuterMessage(Message& message) {
uint32_t options = kForwardOnly;
switch (packet.type()) {
case Packet::Type::RouterAdvertisement:
+ extractRecursiveDnsServers(packet);
options = kRewriteSourceLink | kSetDefaultGateway;
break;
case Packet::Type::NeighborSolicitation:
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