Merge "[MultiDisplay] Convert the Android.mk to Android.bp"
diff --git a/data/etc/x86/emulatorip b/data/etc/x86/emulatorip
new file mode 100755
index 0000000..72541a3
--- /dev/null
+++ b/data/etc/x86/emulatorip
Binary files differ
diff --git a/dhcp/client/dhcpclient.cpp b/dhcp/client/dhcpclient.cpp
index 20dd143..0897b4c 100644
--- a/dhcp/client/dhcpclient.cpp
+++ b/dhcp/client/dhcpclient.cpp
@@ -50,8 +50,9 @@
return inet_ntop(AF_INET, &addr, buffer, sizeof(buffer));
}
-DhcpClient::DhcpClient()
- : mRandomEngine(std::random_device()()),
+DhcpClient::DhcpClient(uint32_t options)
+ : mOptions(options),
+ mRandomEngine(std::random_device()()),
mRandomDistribution(-kTimeoutSpan, kTimeoutSpan),
mState(State::Init),
mNextTimeout(kInitialTimeout),
@@ -419,45 +420,50 @@
}
}
- res = mInterface.setAddress(mDhcpInfo.offeredAddress);
- if (!res) {
- ALOGE("Could not configure DHCP: %s", res.c_str());
- return false;
- }
-
- res = mInterface.setSubnetMask(mDhcpInfo.subnetMask);
- if (!res) {
- ALOGE("Could not configure DHCP: %s", res.c_str());
- return false;
- }
-
- res = mRouter.setDefaultGateway(mDhcpInfo.gateway, mInterface.getIndex());
- if (!res) {
- ALOGE("Could not configure DHCP: %s", res.c_str());
- return false;
- }
char propName[64];
snprintf(propName, sizeof(propName), "net.%s.gw",
mInterface.getName().c_str());
- property_set(propName, addrToStr(mDhcpInfo.gateway).c_str());
+ if (property_set(propName, addrToStr(mDhcpInfo.gateway).c_str()) != 0) {
+ ALOGE("Failed to set %s: %s", propName, strerror(errno));
+ }
int numDnsEntries = sizeof(mDhcpInfo.dns) / sizeof(mDhcpInfo.dns[0]);
for (int i = 0; i < numDnsEntries; ++i) {
snprintf(propName, sizeof(propName), "net.%s.dns%d",
mInterface.getName().c_str(), i + 1);
if (mDhcpInfo.dns[i] != 0) {
- property_set(propName, addrToStr(mDhcpInfo.dns[i]).c_str());
+ if (property_set(propName,
+ addrToStr(mDhcpInfo.dns[i]).c_str()) != 0) {
+ ALOGE("Failed to set %s: %s", propName, strerror(errno));
+ }
} else {
// Clear out any previous value here in case it was set
- property_set(propName, "");
+ if (property_set(propName, "") != 0) {
+ ALOGE("Failed to clear %s: %s", propName, strerror(errno));
+ }
}
}
+ res = mInterface.setAddress(mDhcpInfo.offeredAddress,
+ mDhcpInfo.subnetMask);
+ if (!res) {
+ ALOGE("Could not configure DHCP: %s", res.c_str());
+ return false;
+ }
+
+ if ((mOptions & static_cast<uint32_t>(ClientOption::NoGateway)) == 0) {
+ res = mRouter.setDefaultGateway(mDhcpInfo.gateway,
+ mInterface.getIndex());
+ if (!res) {
+ ALOGE("Could not configure DHCP: %s", res.c_str());
+ return false;
+ }
+ }
return true;
}
void DhcpClient::haltNetwork() {
- Result res = mInterface.setAddress(0);
+ Result res = mInterface.setAddress(0, 0);
if (!res) {
ALOGE("Could not halt network: %s", res.c_str());
}
diff --git a/dhcp/client/dhcpclient.h b/dhcp/client/dhcpclient.h
index 718f4c9..128d416 100644
--- a/dhcp/client/dhcpclient.h
+++ b/dhcp/client/dhcpclient.h
@@ -27,10 +27,16 @@
#include <random>
+// Options to configure the behavior of the DHCP client.
+enum class ClientOption : uint32_t {
+ NoGateway = (1 << 0), // Do not configure the system's default gateway
+};
class DhcpClient {
public:
- DhcpClient();
+ // Create a DHCP client with the given |options|. These options are values
+ // from the ClientOption enum.
+ explicit DhcpClient(uint32_t options);
// Initialize the DHCP client to listen on |interfaceName|.
Result init(const char* interfaceName);
@@ -72,6 +78,7 @@
uint16_t sourcePort, uint16_t destinationPort,
const uint8_t* data, size_t size);
+ uint32_t mOptions;
std::mt19937 mRandomEngine; // Mersenne Twister RNG
std::uniform_int_distribution<int> mRandomDistribution;
diff --git a/dhcp/client/interface.cpp b/dhcp/client/interface.cpp
index 805fa52..a13af08 100644
--- a/dhcp/client/interface.cpp
+++ b/dhcp/client/interface.cpp
@@ -16,13 +16,26 @@
#include "interface.h"
+#include "netlink.h"
+
#include <errno.h>
#include <linux/if.h>
#include <linux/if_ether.h>
#include <linux/route.h>
+#include <linux/rtnetlink.h>
#include <string.h>
#include <unistd.h>
+in_addr_t broadcastFromNetmask(in_addr_t address, in_addr_t netmask) {
+ // The broadcast address is the address with the bits excluded in the
+ // netmask set to 1. For example if address = 10.0.2.15 and netmask is
+ // 255.255.255.0 then the broadcast is 10.0.2.255. If instead netmask was
+ // 255.0.0.0.0 then the broadcast would be 10.255.255.255
+ //
+ // Simply set all the lower bits to 1 and that should do it.
+ return address | (~netmask);
+}
+
Interface::Interface() : mSocketFd(-1) {
}
@@ -40,7 +53,7 @@
return Result::error("Interface initialized more than once");
}
- mSocketFd = ::socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_IP);
+ mSocketFd = ::socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
if (mSocketFd == -1) {
return Result::error("Failed to create interface socket for '%s': %s",
interfaceName, strerror(errno));
@@ -61,7 +74,7 @@
return res;
}
- res = setAddress(0);
+ res = setAddress(0, 0);
if (!res) {
return res;
}
@@ -93,37 +106,65 @@
return Result::success();
}
-Result Interface::setAddress(in_addr_t address) {
- struct ifreq request = createRequest();
+Result Interface::setAddress(in_addr_t address, in_addr_t subnetMask) {
+ struct Request {
+ struct nlmsghdr hdr;
+ struct ifaddrmsg msg;
+ char buf[256];
+ } request;
- auto requestAddr = reinterpret_cast<struct sockaddr_in*>(&request.ifr_addr);
- requestAddr->sin_family = AF_INET;
- requestAddr->sin_port = 0;
- requestAddr->sin_addr.s_addr = address;
+ memset(&request, 0, sizeof(request));
- int status = ::ioctl(mSocketFd, SIOCSIFADDR, &request);
- if (status != 0) {
- return Result::error("Failed to set interface address for '%s': %s",
- mInterfaceName.c_str(), strerror(errno));
+ request.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(request.msg));
+ request.hdr.nlmsg_type = RTM_NEWADDR;
+ request.hdr.nlmsg_flags = NLM_F_REQUEST |
+ NLM_F_ACK |
+ NLM_F_CREATE |
+ NLM_F_REPLACE;
+
+ request.msg.ifa_family = AF_INET;
+ // Count the number of bits in the subnet mask, this is the length.
+ request.msg.ifa_prefixlen = __builtin_popcount(subnetMask);
+ request.msg.ifa_index = mIndex;
+
+ addRouterAttribute(request, IFA_ADDRESS, &address, sizeof(address));
+ addRouterAttribute(request, IFA_LOCAL, &address, sizeof(address));
+ in_addr_t broadcast = broadcastFromNetmask(address, subnetMask);
+ addRouterAttribute(request, IFA_BROADCAST, &broadcast, sizeof(broadcast));
+
+ struct sockaddr_nl nlAddr;
+ memset(&nlAddr, 0, sizeof(nlAddr));
+ nlAddr.nl_family = AF_NETLINK;
+
+ int status = ::sendto(mSocketFd, &request, request.hdr.nlmsg_len, 0,
+ reinterpret_cast<sockaddr*>(&nlAddr),
+ sizeof(nlAddr));
+ if (status == -1) {
+ return Result::error("Unable to set interface address: %s",
+ strerror(errno));
}
-
- return Result::success();
-}
-
-Result Interface::setSubnetMask(in_addr_t subnetMask) {
- struct ifreq request = createRequest();
-
- auto addr = reinterpret_cast<struct sockaddr_in*>(&request.ifr_addr);
- addr->sin_family = AF_INET;
- addr->sin_port = 0;
- addr->sin_addr.s_addr = subnetMask;
-
- int status = ::ioctl(mSocketFd, SIOCSIFNETMASK, &request);
- if (status != 0) {
- return Result::error("Failed to set subnet mask for '%s': %s",
- mInterfaceName.c_str(), strerror(errno));
+ char buffer[8192];
+ status = ::recv(mSocketFd, buffer, sizeof(buffer), 0);
+ if (status < 0) {
+ return Result::error("Unable to read netlink response: %s",
+ strerror(errno));
}
-
+ size_t responseSize = static_cast<size_t>(status);
+ if (responseSize < sizeof(nlmsghdr)) {
+ return Result::error("Received incomplete response from netlink");
+ }
+ auto response = reinterpret_cast<const nlmsghdr*>(buffer);
+ if (response->nlmsg_type == NLMSG_ERROR) {
+ if (responseSize < NLMSG_HDRLEN + sizeof(nlmsgerr)) {
+ return Result::error("Recieved an error from netlink but the "
+ "response was incomplete");
+ }
+ auto err = reinterpret_cast<const nlmsgerr*>(NLMSG_DATA(response));
+ if (err->error) {
+ return Result::error("Could not set interface address: %s",
+ strerror(-err->error));
+ }
+ }
return Result::success();
}
diff --git a/dhcp/client/interface.h b/dhcp/client/interface.h
index b7f549a..ca9e9e5 100644
--- a/dhcp/client/interface.h
+++ b/dhcp/client/interface.h
@@ -40,8 +40,7 @@
Result bringUp();
Result bringDown();
Result setMtu(uint16_t mtu);
- Result setAddress(in_addr_t address);
- Result setSubnetMask(in_addr_t subnetMask);
+ Result setAddress(in_addr_t address, in_addr_t subnetMask);
private:
struct ifreq createRequest() const;
diff --git a/dhcp/client/main.cpp b/dhcp/client/main.cpp
index f4a3ea9..70b854f 100644
--- a/dhcp/client/main.cpp
+++ b/dhcp/client/main.cpp
@@ -18,7 +18,9 @@
#include "log.h"
static void usage(const char* program) {
- ALOGE("Usage: %s -i <interface>", program);
+ ALOGE("Usage: %s [--no-gateway] -i <interface>", program);
+ ALOGE(" If the optional parameter --no-gateway is specified the client");
+ ALOGE(" will not configure the default gateway of the system.");
}
int main(int argc, char* argv[]) {
@@ -27,6 +29,7 @@
return 1;
}
const char* interfaceName = nullptr;
+ uint32_t options = 0;
for (int i = 1; i < argc; ++i) {
if (strcmp(argv[i], "-i") == 0) {
@@ -37,6 +40,8 @@
usage(argv[0]);
return 1;
}
+ } else if (strcmp(argv[i], "--no-gateway") == 0) {
+ options |= static_cast<uint32_t>(ClientOption::NoGateway);
} else {
ALOGE("ERROR: unknown parameters %s", argv[i]);
usage(argv[0]);
@@ -49,7 +54,7 @@
return 1;
}
- DhcpClient client;
+ DhcpClient client(options);
Result res = client.init(interfaceName);
if (!res) {
ALOGE("Failed to initialize DHCP client: %s\n", res.c_str());
diff --git a/dhcp/client/netlink.h b/dhcp/client/netlink.h
new file mode 100644
index 0000000..e0c916f
--- /dev/null
+++ b/dhcp/client/netlink.h
@@ -0,0 +1,22 @@
+#pragma once
+
+#include <linux/rtnetlink.h>
+
+template<class Request>
+inline void addRouterAttribute(Request& r,
+ int type,
+ const void* data,
+ size_t size) {
+ // Calculate the offset into the character buffer where the RTA data lives
+ // We use offsetof on the buffer to get it. This avoids undefined behavior
+ // by casting the buffer (which is safe because it's char) instead of the
+ // Request struct.(which is undefined because of aliasing)
+ size_t offset = NLMSG_ALIGN(r.hdr.nlmsg_len) - offsetof(Request, buf);
+ auto attr = reinterpret_cast<struct rtattr*>(r.buf + offset);
+ attr->rta_type = type;
+ attr->rta_len = RTA_LENGTH(size);
+ memcpy(RTA_DATA(attr), data, size);
+
+ // Update the message length to include the router attribute.
+ r.hdr.nlmsg_len = NLMSG_ALIGN(r.hdr.nlmsg_len) + RTA_ALIGN(attr->rta_len);
+}
diff --git a/dhcp/client/router.cpp b/dhcp/client/router.cpp
index 9b9f17d..7c87e2d 100644
--- a/dhcp/client/router.cpp
+++ b/dhcp/client/router.cpp
@@ -16,31 +16,14 @@
#include "router.h"
+#include "netlink.h"
+
#include <linux/rtnetlink.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
-template<class Request>
-static void addRouterAttribute(Request& r,
- int type,
- const void* data,
- size_t size) {
- // Calculate the offset into the character buffer where the RTA data lives
- // We use offsetof on the buffer to get it. This avoids undefined behavior
- // by casting the buffer (which is safe because it's char) instead of the
- // Request struct.(which is undefined because of aliasing)
- size_t offset = NLMSG_ALIGN(r.hdr.nlmsg_len) - offsetof(Request, buf);
- auto attr = reinterpret_cast<struct rtattr*>(r.buf + offset);
- attr->rta_type = type;
- attr->rta_len = RTA_LENGTH(size);
- memcpy(RTA_DATA(attr), data, size);
-
- // Update the message length to include the router attribute.
- r.hdr.nlmsg_len = NLMSG_ALIGN(r.hdr.nlmsg_len) + RTA_ALIGN(attr->rta_len);
-}
-
Router::Router() : mSocketFd(-1) {
}
diff --git a/dhcp/server/Android.mk b/dhcp/server/Android.mk
deleted file mode 100644
index 38234de..0000000
--- a/dhcp/server/Android.mk
+++ /dev/null
@@ -1,22 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
- dhcpserver.cpp \
- main.cpp \
- ../common/message.cpp \
- ../common/socket.cpp \
- ../common/utils.cpp \
-
-
-LOCAL_CPPFLAGS += -Werror
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/../common
-LOCAL_SHARED_LIBRARIES := libcutils liblog
-LOCAL_PROPRIETARY_MODULE := true
-LOCAL_MODULE := dhcpserver
-
-LOCAL_MODULE_CLASS := EXECUTABLES
-
-include $(BUILD_EXECUTABLE)
-
diff --git a/dhcp/server/dhcpserver.cpp b/dhcp/server/dhcpserver.cpp
deleted file mode 100644
index d6d4a7b..0000000
--- a/dhcp/server/dhcpserver.cpp
+++ /dev/null
@@ -1,390 +0,0 @@
-/*
- * Copyright 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.
- */
-
-#include "dhcpserver.h"
-
-#include "dhcp.h"
-#include "log.h"
-#include "message.h"
-
-#include <arpa/inet.h>
-#include <errno.h>
-#include <linux/sockios.h>
-#include <net/if.h>
-#include <netinet/in.h>
-#include <poll.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <unistd.h>
-
-#include <cutils/properties.h>
-
-static const int kMaxDnsServers = 4;
-
-DhcpServer::DhcpServer(unsigned int excludeInterface) :
- mExcludeInterface(excludeInterface)
-{
-}
-
-Result DhcpServer::init() {
- Result res = mSocket.open(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
- if (!res) {
- return res;
- }
- res = mSocket.enableOption(SOL_IP, IP_PKTINFO);
- if (!res) {
- return res;
- }
- res = mSocket.enableOption(SOL_SOCKET, SO_BROADCAST);
- if (!res) {
- return res;
- }
-
- res = mSocket.bindIp(INADDR_ANY, PORT_BOOTP_SERVER);
- if (!res) {
- return res;
- }
-
- return Result::success();
-}
-
-Result DhcpServer::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, originalMask;
- int status = ::sigfillset(&blockMask);
- if (status != 0) {
- return Result::error("Unable to fill signal set: %s", strerror(errno));
- }
- status = ::sigprocmask(SIG_SETMASK, &blockMask, &originalMask);
- if (status != 0) {
- return Result::error("Unable to set signal mask: %s", strerror(errno));
- }
-
- struct pollfd fds;
- fds.fd = mSocket.get();
- fds.events = POLLIN;
- Message message;
- while ((status = ::ppoll(&fds, 1, nullptr, &originalMask)) >= 0) {
- if (status == 0) {
- // Timeout
- continue;
- }
-
- unsigned int interfaceIndex = 0;
- Result res = mSocket.receiveFromInterface(&message,
- &interfaceIndex);
- if (!res) {
- ALOGE("Failed to recieve on socket: %s", res.c_str());
- continue;
- }
- if (interfaceIndex == 0 || mExcludeInterface == interfaceIndex) {
- // Received packet on unknown or unwanted interface, drop it
- continue;
- }
- if (!message.isValidDhcpMessage(OP_BOOTREQUEST)) {
- // Not a DHCP request, drop it
- continue;
- }
- switch (message.type()) {
- case DHCPDISCOVER:
- // Someone is trying to find us, let them know we exist
- sendDhcpOffer(message, interfaceIndex);
- break;
- case DHCPREQUEST:
- // Someone wants a lease based on an offer
- if (isValidDhcpRequest(message, interfaceIndex)) {
- // The request matches our offer, acknowledge it
- sendAck(message, interfaceIndex);
- } else {
- // Request for something other than we offered, denied
- sendNack(message, interfaceIndex);
- }
- break;
- }
- }
- // Polling failed, exit
- return Result::error("Polling failed: %s", strerror(errno));
-}
-
-Result DhcpServer::sendMessage(unsigned int interfaceIndex,
- in_addr_t /*sourceAddress*/,
- const Message& message) {
- return mSocket.sendOnInterface(interfaceIndex,
- INADDR_BROADCAST,
- PORT_BOOTP_CLIENT,
- message);
-}
-
-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,
- &netmask,
- &gateway);
- if (!res) {
- ALOGE("Failed to get address for offer: %s", res.c_str());
- return;
- }
- in_addr_t serverAddress;
- res = getInterfaceAddress(interfaceIndex, &serverAddress);
- if (!res) {
- ALOGE("Failed to get address for interface %u: %s",
- interfaceIndex, res.c_str());
- return;
- }
-
- Message offer = Message::offer(message,
- serverAddress,
- offerAddress,
- netmask,
- gateway,
- mDnsServers.data(),
- mDnsServers.size());
- res = sendMessage(interfaceIndex, serverAddress, offer);
- if (!res) {
- ALOGE("Failed to send DHCP offer: %s", res.c_str());
- }
-}
-
-void DhcpServer::sendAck(const Message& message, unsigned int interfaceIndex) {
- updateDnsServers();
- in_addr_t offerAddress;
- in_addr_t netmask;
- in_addr_t gateway;
- in_addr_t serverAddress;
- Result res = getOfferAddress(interfaceIndex,
- message.dhcpData.chaddr,
- &offerAddress,
- &netmask,
- &gateway);
- if (!res) {
- ALOGE("Failed to get address for offer: %s", res.c_str());
- return;
- }
- res = getInterfaceAddress(interfaceIndex, &serverAddress);
- if (!res) {
- ALOGE("Failed to get address for interface %u: %s",
- interfaceIndex, res.c_str());
- return;
- }
- Message ack = Message::ack(message,
- serverAddress,
- offerAddress,
- netmask,
- gateway,
- mDnsServers.data(),
- mDnsServers.size());
- res = sendMessage(interfaceIndex, serverAddress, ack);
- if (!res) {
- ALOGE("Failed to send DHCP ack: %s", res.c_str());
- }
-}
-
-void DhcpServer::sendNack(const Message& message, unsigned int interfaceIndex) {
- in_addr_t serverAddress;
- Result res = getInterfaceAddress(interfaceIndex, &serverAddress);
- if (!res) {
- ALOGE("Failed to get address for interface %u: %s",
- interfaceIndex, res.c_str());
- return;
- }
- Message nack = Message::nack(message, serverAddress);
- res = sendMessage(interfaceIndex, serverAddress, nack);
- if (!res) {
- ALOGE("Failed to send DHCP nack: %s", res.c_str());
- }
-}
-
-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,
- &netmask,
- &gateway);
- if (!res) {
- ALOGE("Failed to get address for offer: %s", res.c_str());
- return false;
- }
- if (message.requestedIp() != offerAddress) {
- ALOGE("Client requested a different IP address from the offered one");
- return false;
- }
- return true;
-}
-
-void DhcpServer::updateDnsServers() {
- char key[64];
- char value[PROPERTY_VALUE_MAX];
- mDnsServers.clear();
- for (int i = 1; i <= kMaxDnsServers; ++i) {
- snprintf(key, sizeof(key), "net.eth0.dns%d", i);
- if (property_get(key, value, nullptr) > 0) {
- struct in_addr address;
- if (::inet_pton(AF_INET, value, &address) > 0) {
- mDnsServers.push_back(address.s_addr);
- }
- }
- }
-}
-
-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));
- }
- memset(response, 0, sizeof(*response));
- response->ifr_addr.sa_family = AF_INET;
- strncpy(response->ifr_name, interfaceName, IFNAMSIZ - 1);
-
- if (::ioctl(mSocket.get(), type, response) == -1) {
- return Result::error("Failed to get data for interface %s: %s",
- interfaceName, strerror(errno));
- }
-
- 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* 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
- in_addr_t& value = mLeases[key];
- 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
- 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;
- ++offset;
- }
-
- if (addressInRange(nextAddress, interfaceAddress, mask)) {
- // Convert back to network byte order
- value = htonl(nextAddress);
- ++offset;
- } else {
- // Ran out of addresses
- return Result::error("DHCP server is out of addresses");
- }
- }
- *address = value;
- return Result::success();
-}
-
diff --git a/dhcp/server/dhcpserver.h b/dhcp/server/dhcpserver.h
deleted file mode 100644
index 276cd5b..0000000
--- a/dhcp/server/dhcpserver.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright 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 "lease.h"
-#include "result.h"
-#include "socket.h"
-
-#include <netinet/in.h>
-#include <stdint.h>
-
-#include <unordered_map>
-#include <vector>
-
-class Message;
-
-class DhcpServer {
-public:
- // 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();
-
-private:
- Result sendMessage(unsigned int interfaceIndex,
- in_addr_t sourceAddress,
- const Message& message);
-
- void sendDhcpOffer(const Message& message, unsigned int interfaceIndex);
- void sendAck(const Message& message, unsigned int interfaceIndex);
- void sendNack(const Message& message, unsigned int interfaceIndex);
-
- 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* 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.
- 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/lease.h b/dhcp/server/lease.h
deleted file mode 100644
index bad5614..0000000
--- a/dhcp/server/lease.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright 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 <linux/if_ether.h>
-#include <stdint.h>
-
-#include <functional>
-
-// A lease consists of both the interface index and the MAC address. This
-// way the server can run on many different interfaces that have the same
-// client MAC address without giving out the same IP address. The reason
-// this is useful is because we might have several virtual interfaces, one
-// for each access point, that all have the same endpoint on the other side.
-// This endpoint would then have the same MAC address and always get the
-// same address. But for routing purposes it's useful to give it different
-// addresses depending on the server side interface. That way the routing
-// table can be set up so that packets are forwarded to the correct access
-// point interface based on IP address.
-struct Lease {
- Lease(unsigned int interfaceIndex, const uint8_t* macAddress) {
- InterfaceIndex = interfaceIndex;
- memcpy(MacAddress, macAddress, sizeof(MacAddress));
- }
- unsigned int InterfaceIndex;
- uint8_t MacAddress[ETH_ALEN];
-};
-
-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<Lease> {
- size_t operator()(const Lease& lease) const {
- size_t seed = 0;
- hash_combine(seed, lease.InterfaceIndex);
- // Treat the first 4 bytes as an uint32_t to save some computation
- hash_combine(seed, *reinterpret_cast<const uint32_t*>(lease.MacAddress));
- // And the remaining 2 bytes as an uint16_t
- hash_combine(seed,
- *reinterpret_cast<const uint16_t*>(lease.MacAddress + 4));
- return seed;
- }
-};
-}
-
-inline bool operator==(const Lease& left, const Lease& right) {
- return left.InterfaceIndex == right.InterfaceIndex &&
- memcmp(left.MacAddress, right.MacAddress, sizeof(left.MacAddress)) == 0;
-}
diff --git a/dhcp/server/main.cpp b/dhcp/server/main.cpp
deleted file mode 100644
index 482ffd6..0000000
--- a/dhcp/server/main.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright 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.
- */
-
-#include "dhcpserver.h"
-
-#include "log.h"
-
-#include <arpa/inet.h>
-#include <net/if.h>
-
-static void usage(const char* program) {
- ALOGE("Usage: %s -i <interface> -r <", program);
-}
-
-int main(int argc, char* argv[]) {
- char* excludeInterfaceName = nullptr;
- unsigned int excludeInterfaceIndex = 0;
- for (int i = 1; i < argc; ++i) {
- if (strcmp("--exclude-interface", argv[i]) == 0) {
- if (i + 1 >= argc) {
- ALOGE("ERROR: Missing argument to "
- "--exclude-interfaces parameter");
- usage(argv[0]);
- return 1;
- }
- excludeInterfaceName = argv[i + 1];
- excludeInterfaceIndex = if_nametoindex(excludeInterfaceName);
- if (excludeInterfaceIndex == 0) {
- ALOGE("ERROR: Invalid argument '%s' to --exclude-interface",
- argv[i + 1]);
- usage(argv[0]);
- return 1;
- }
- }
- }
-
- DhcpServer server(excludeInterfaceIndex);
- Result res = server.init();
- if (!res) {
- ALOGE("Failed to initialize DHCP server: %s\n", res.c_str());
- return 1;
- }
-
- res = server.run();
- if (!res) {
- ALOGE("DHCP server failed: %s\n", res.c_str());
- return 1;
- }
- // This is weird and shouldn't happen, the server should run forever.
- return 0;
-}
-
-
diff --git a/init.ranchu.rc b/init.ranchu.rc
index 36ae4d8..da0f0db 100644
--- a/init.ranchu.rc
+++ b/init.ranchu.rc
@@ -20,6 +20,7 @@
mkdir /data/vendor/var 0755 root root
mkdir /data/vendor/var/run 0755 root root
mkdir /data/vendor/var/run/netns 0755 root root
+ start ranchu-net
on zygote-start
# Create the directories used by the Wireless subsystem
@@ -67,33 +68,32 @@
user root
group root wakelock wifi
oneshot
-
-service ipv6proxy /vendor/bin/execns router /vendor/bin/ipv6proxy -o eth0 -i wlan1,radio0-peer
- user root
- group root
- disabled
+ disabled # Started on post-fs-data
service emu_hostapd /vendor/bin/execns 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 --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
+service netmgr /vendor/bin/execns router /vendor/bin/netmgr --if-prefix wlan1 --bridge eth0,radio0-peer
user root
group root wifi
disabled
-service dhcpclient_rtr /vendor/bin/execns router /vendor/bin/dhcpclient -i eth0
+service wifi_forwarder /vendor/bin/wifi_forwarder
+ user root
+ group root wifi
+ disabled
+
+service dhcpclient_rtr /vendor/bin/dhcpclient -i radio0 --no-gateway
user root
group root
disabled
-service dhcpclient_def /vendor/bin/dhcpclient -i eth0
+on property:vendor.network.bridged=1
+ start dhcpclient_rtr
+
+service dhcpclient_def /vendor/bin/dhcpclient -i eth0 --no-gateway
user root
group root
disabled
diff --git a/input/qwerty2.idc b/input/qwerty2.idc
index 711dea7..ca41665 100644
--- a/input/qwerty2.idc
+++ b/input/qwerty2.idc
@@ -15,9 +15,14 @@
#
# Emulator keyboard configuration file #2.
#
+touch.deviceType = touchScreen
+touch.orientationAware = 1
keyboard.layout = qwerty
keyboard.characterMap = qwerty2
keyboard.orientationAware = 0
keyboard.builtIn = 1
+cursor.mode = navigation
+cursor.orientationAware = 1
+
diff --git a/network/netmgr/Android.bp b/network/netmgr/Android.bp
index 6686f98..7530c91 100644
--- a/network/netmgr/Android.bp
+++ b/network/netmgr/Android.bp
@@ -22,14 +22,15 @@
"-Werror",
],
srcs: [
- "address_assigner.cpp",
+ "bridge.cpp",
+ "bridge_builder.cpp",
"commander.cpp",
- "fork.cpp",
"interface_state.cpp",
"log.cpp",
"main.cpp",
"monitor.cpp",
"poller.cpp",
+ "utils.cpp",
"wifi_forwarder.cpp",
"commands/wifi_command.cpp",
],
diff --git a/network/netmgr/address_assigner.cpp b/network/netmgr/address_assigner.cpp
deleted file mode 100644
index 11df81d..0000000
--- a/network/netmgr/address_assigner.cpp
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * 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
deleted file mode 100644
index dde42ee..0000000
--- a/network/netmgr/address_assigner.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * 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/bridge.cpp b/network/netmgr/bridge.cpp
new file mode 100644
index 0000000..adbe21e
--- /dev/null
+++ b/network/netmgr/bridge.cpp
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2019, 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 "bridge.h"
+
+#include "log.h"
+
+#include <errno.h>
+#include <net/if.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+Bridge::Bridge(const std::string& bridgeName) : mBridgeName(bridgeName) {
+}
+
+Bridge::~Bridge() {
+ if (mSocketFd != -1) {
+ ::close(mSocketFd);
+ mSocketFd = -1;
+ }
+}
+
+Result Bridge::init() {
+ Result res = createSocket();
+ if (!res) {
+ return res;
+ }
+ res = createBridge();
+ if (!res) {
+ return res;
+ }
+ return Result::success();
+}
+
+Result Bridge::addInterface(const std::string& interfaceName) {
+ return doInterfaceOperation(interfaceName, SIOCBRADDIF, "add");
+}
+
+Result Bridge::removeInterface(const std::string& interfaceName) {
+ return doInterfaceOperation(interfaceName, SIOCBRDELIF, "remove");
+}
+
+Result Bridge::createSocket() {
+ if (mSocketFd != -1) {
+ return Result::error("Bridge already initialized");
+ }
+ mSocketFd = ::socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+ if (mSocketFd < 0) {
+ return Result::error("Unable to create socket for bridge: %s",
+ strerror(errno));
+ }
+ return Result::success();
+}
+
+Result Bridge::createBridge() {
+ int res = ::ioctl(mSocketFd, SIOCBRADDBR, mBridgeName.c_str());
+ if (res < 0) {
+ // If the bridge already exists we just keep going, that's fine.
+ // Otherwise something went wrong.
+ if (errno != EEXIST) {
+ return Result::error("Cannot create bridge %s: %s",
+ mBridgeName.c_str(), strerror(errno));
+ }
+ }
+
+ struct ifreq request;
+ memset(&request, 0, sizeof(request));
+ // Determine interface index of bridge
+ request.ifr_ifindex = if_nametoindex(mBridgeName.c_str());
+ if (request.ifr_ifindex == 0) {
+ return Result::error("Unable to get bridge %s interface index",
+ mBridgeName.c_str());
+ }
+ // Get bridge interface flags
+ strlcpy(request.ifr_name, mBridgeName.c_str(), sizeof(request.ifr_name));
+ res = ::ioctl(mSocketFd, SIOCGIFFLAGS, &request);
+ if (res != 0) {
+ return Result::error("Unable to get interface flags for bridge %s: %s",
+ mBridgeName.c_str(), strerror(errno));
+ }
+
+ if ((request.ifr_flags & IFF_UP) != 0) {
+ // Bridge is already up, it's ready to go
+ return Result::success();
+ }
+
+ // Bridge is not up, it needs to be up to work
+ request.ifr_flags |= IFF_UP;
+ res = ::ioctl(mSocketFd, SIOCSIFFLAGS, &request);
+ if (res != 0) {
+ return Result::error("Unable to set interface flags for bridge %s: %s",
+ strerror(errno));
+ }
+
+ return Result::success();
+}
+
+Result Bridge::doInterfaceOperation(const std::string& interfaceName,
+ unsigned long operation,
+ const char* operationName) {
+ struct ifreq request;
+ memset(&request, 0, sizeof(request));
+
+ request.ifr_ifindex = if_nametoindex(interfaceName.c_str());
+ if (request.ifr_ifindex == 0) {
+ return Result::error("Bridge unable to %s interface '%s', no such "
+ "interface", operationName, interfaceName.c_str());
+ }
+ strlcpy(request.ifr_name, mBridgeName.c_str(), sizeof(request.ifr_name));
+ int res = ::ioctl(mSocketFd, operation, &request);
+ // An errno of EBUSY most likely indicates that the interface is already
+ // part of the bridge. Ignore this.
+ if (res < 0 && errno != EBUSY) {
+ return Result::error("Bridge unable to %s interface '%s': %s",
+ operationName, interfaceName.c_str(),
+ strerror(errno));
+ }
+ return Result::success();
+}
diff --git a/network/netmgr/bridge.h b/network/netmgr/bridge.h
new file mode 100644
index 0000000..fc7ff51
--- /dev/null
+++ b/network/netmgr/bridge.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2019, 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"
+
+#include <string>
+
+class Bridge {
+public:
+ explicit Bridge(const std::string& bridgeName);
+ ~Bridge();
+
+ Result init();
+
+ Result addInterface(const std::string& interfaceName);
+ Result removeInterface(const std::string& interfaceName);
+private:
+ Result createSocket();
+ Result createBridge();
+ Result doInterfaceOperation(const std::string& interfaceName,
+ unsigned long operation,
+ const char* operationName);
+
+ std::string mBridgeName;
+ int mSocketFd = -1;
+};
diff --git a/network/netmgr/bridge_builder.cpp b/network/netmgr/bridge_builder.cpp
new file mode 100644
index 0000000..d772eae
--- /dev/null
+++ b/network/netmgr/bridge_builder.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2019, 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 "bridge_builder.h"
+
+#include "bridge.h"
+#include "log.h"
+#include "result.h"
+
+BridgeBuilder::BridgeBuilder(Bridge& bridge, const char* interfacePrefix) :
+ mBridge(bridge),
+ mInterfacePrefix(interfacePrefix),
+ mPrefixLength(strlen(interfacePrefix)) {
+}
+
+void BridgeBuilder::onInterfaceState(unsigned int /*index*/,
+ const char* name,
+ InterfaceState state) {
+ if (strncmp(name, mInterfacePrefix, mPrefixLength) != 0) {
+ // Does not match our prefix, ignore it
+ return;
+ }
+
+ if (state == InterfaceState::Up) {
+ Result res = mBridge.addInterface(name);
+ if (!res) {
+ LOGE("%s", res.c_str());
+ }
+ }
+}
diff --git a/network/netmgr/bridge_builder.h b/network/netmgr/bridge_builder.h
new file mode 100644
index 0000000..e3d9580
--- /dev/null
+++ b/network/netmgr/bridge_builder.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2019, 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 <stddef.h>
+
+class Bridge;
+
+class BridgeBuilder {
+public:
+ // Construct a bridge builder that will add any interface that comes up to
+ // |bridge| if the interface name begins with |interfacePrefix|.
+ BridgeBuilder(Bridge& bridge, const char* interfacePrefix);
+
+ void onInterfaceState(unsigned int index,
+ const char* name,
+ InterfaceState state);
+
+private:
+ Bridge& mBridge;
+ const char* mInterfacePrefix;
+ size_t mPrefixLength;
+};
diff --git a/network/netmgr/commands/wifi_command.cpp b/network/netmgr/commands/wifi_command.cpp
index 8f48a43..27bf95a 100644
--- a/network/netmgr/commands/wifi_command.cpp
+++ b/network/netmgr/commands/wifi_command.cpp
@@ -16,18 +16,13 @@
#include "wifi_command.h"
-#include "../fork.h"
-#include "log.h"
+#include "../bridge.h"
+#include "../utils.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";
@@ -70,24 +65,9 @@
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) {
+WifiCommand::WifiCommand(Bridge& bridge)
+ : mBridge(bridge)
+ , mLowestInterfaceNumber(1) {
readConfig();
}
@@ -103,7 +83,7 @@
return Result::error("Empty wifi command");
}
- std::vector<std::string> subArgs = explode(divider + 1);
+ 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'",
@@ -172,55 +152,6 @@
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];
@@ -256,18 +187,18 @@
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());
+ return Result::error("Unknown SSID '%s'", arguments[0].c_str());
}
interface->second.blocked = true;
- return setBlocked(interface->second.ifName.c_str(), true);
+ return mBridge.removeInterface(interface->second.ifName.c_str());
}
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());
+ return Result::error("Unknown SSID '%s'", arguments[0].c_str());
}
interface->second.blocked = false;
- return setBlocked(interface->second.ifName.c_str(), false);
+ return mBridge.addInterface(interface->second.ifName.c_str());
}
diff --git a/network/netmgr/commands/wifi_command.h b/network/netmgr/commands/wifi_command.h
index f9ce96c..b47edb8 100644
--- a/network/netmgr/commands/wifi_command.h
+++ b/network/netmgr/commands/wifi_command.h
@@ -24,9 +24,11 @@
#include <unordered_set>
#include <vector>
+class Bridge;
+
class WifiCommand : public Command {
public:
- WifiCommand();
+ explicit WifiCommand(Bridge& bridge);
virtual ~WifiCommand() = default;
Result onCommand(const char* command, const char* args) override;
@@ -34,7 +36,6 @@
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);
@@ -48,6 +49,7 @@
bool blocked;
};
+ Bridge& mBridge;
std::unordered_map<std::string, AccessPoint> mAccessPoints;
std::unordered_set<std::string> mUsedInterfaces;
int mLowestInterfaceNumber;
diff --git a/network/netmgr/interface_state.h b/network/netmgr/interface_state.h
index 5315b0f..6d56aba 100644
--- a/network/netmgr/interface_state.h
+++ b/network/netmgr/interface_state.h
@@ -21,3 +21,4 @@
Down,
};
+const char* interfaceStateToStr(InterfaceState state);
diff --git a/network/netmgr/main.cpp b/network/netmgr/main.cpp
index e4b43f6..d83de39 100644
--- a/network/netmgr/main.cpp
+++ b/network/netmgr/main.cpp
@@ -14,23 +14,28 @@
* limitations under the License.
*/
-#include "address_assigner.h"
+#include "bridge.h"
+#include "bridge_builder.h"
#include "commander.h"
#include "commands/wifi_command.h"
#include "log.h"
#include "monitor.h"
#include "poller.h"
+#include "utils.h"
#include "wifi_forwarder.h"
#include <arpa/inet.h>
#include <netinet/in.h>
+#include <cutils/properties.h>
+
#include <functional>
-static const char kWifiMonitorInterface[] = "hwsim0";
+static const char kBridgeName[] = "br0";
+static const char kNetworkBridgedProperty[] = "vendor.network.bridged";
static void usage(const char* name) {
- LOGE("Usage: %s --if-prefix <prefix> --network <ip/mask>", name);
+ LOGE("Usage: %s --if-prefix <prefix> --bridge <if1,if2,...>", 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.");
@@ -39,52 +44,27 @@
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;
- }
+static Result addBridgeInterfaces(Bridge& bridge, const char* interfaces) {
+ std::vector<std::string> ifNames = explode(interfaces, ',');
- 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;
+ for (const auto& ifName : ifNames) {
+ Result res = bridge.addInterface(ifName);
+ if (!res) {
+ return res;
+ }
}
-
- ++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;
+ return Result::success();
}
int main(int argc, char* argv[]) {
const char* interfacePrefix = nullptr;
- const char* network = nullptr;
+ const char* bridgeInterfaces = 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 if (strcmp(argv[i], "--bridge") == 0 && i + 1 < argc) {
+ bridgeInterfaces = argv[++i];
} else {
LOGE("Unknown parameter '%s'", argv[i]);
usage(argv[0]);
@@ -95,30 +75,39 @@
if (interfacePrefix == nullptr) {
LOGE("Missing parameter --if-prefix");
}
- if (network == nullptr) {
- LOGE("Missing parameter --network");
+ if (bridgeInterfaces == nullptr) {
+ LOGE("Missing parameter --bridge");
}
- if (network == nullptr || interfacePrefix == nullptr) {
+ if (interfacePrefix == nullptr || bridgeInterfaces == nullptr) {
usage(argv[0]);
return 1;
}
- in_addr_t address = 0;
- uint32_t mask = 0;
- if (!parseNetwork(network, &address, &mask)) {
+ Bridge bridge(kBridgeName);
+ Result res = bridge.init();
+ if (!res) {
+ LOGE("%s", res.c_str());
+ return 1;
+ }
+ res = addBridgeInterfaces(bridge, bridgeInterfaces);
+ if (!res) {
+ LOGE("%s", res.c_str());
return 1;
}
- AddressAssigner assigner(interfacePrefix, address, mask);
+ BridgeBuilder bridgeBuilder(bridge, interfacePrefix);
+
+ property_set(kNetworkBridgedProperty, "1");
+
Monitor monitor;
- monitor.setOnInterfaceState(std::bind(&AddressAssigner::onInterfaceState,
- &assigner,
+ monitor.setOnInterfaceState(std::bind(&BridgeBuilder::onInterfaceState,
+ &bridgeBuilder,
std::placeholders::_1,
std::placeholders::_2,
std::placeholders::_3));
- Result res = monitor.init();
+ res = monitor.init();
if (!res) {
LOGE("%s", res.c_str());
return 1;
@@ -131,20 +120,13 @@
return 1;
}
- WifiCommand wifiCommand;
+ WifiCommand wifiCommand(bridge);
commander.registerCommand("wifi", &wifiCommand);
- WifiForwarder forwarder(kWifiMonitorInterface);
- res = forwarder.init();
- if (!res) {
- LOGE("%s", res.c_str());
- return 1;
- }
-
Poller poller;
poller.addPollable(&monitor);
poller.addPollable(&commander);
- poller.addPollable(&forwarder);
+
return poller.run();
}
diff --git a/network/netmgr/monitor.cpp b/network/netmgr/monitor.cpp
index 4ab111e..a9d248a 100644
--- a/network/netmgr/monitor.cpp
+++ b/network/netmgr/monitor.cpp
@@ -37,7 +37,11 @@
}
Result Monitor::init() {
- return openSocket();
+ Result res = openSocket();
+ if (!res) {
+ return res;
+ }
+ return requestInterfaces();
}
void Monitor::setOnInterfaceState(OnInterfaceStateCallback callback) {
@@ -84,7 +88,7 @@
default:
break;
}
- NLMSG_NEXT(hdr, length);
+ hdr = NLMSG_NEXT(hdr, length);
}
}
}
@@ -140,6 +144,36 @@
return Result::success();
}
+Result Monitor::requestInterfaces() {
+ if (mSocketFd == -1) {
+ return Result::error("Monitor not initialized yet");
+ }
+
+ struct {
+ struct nlmsghdr hdr;
+ struct rtgenmsg gen;
+ } request;
+
+ memset(&request, 0, sizeof(request));
+
+ request.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(request.gen));
+ request.hdr.nlmsg_type = RTM_GETLINK;
+ request.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+ request.hdr.nlmsg_seq = 1;
+ request.hdr.nlmsg_pid = getpid();
+ request.gen.rtgen_family = AF_PACKET;
+
+ ssize_t result = TEMP_FAILURE_RETRY(::send(mSocketFd,
+ &request,
+ request.hdr.nlmsg_len,
+ 0));
+ if (result < 0) {
+ return Result::error("Failed to request interfaces: %s",
+ strerror(errno));
+ }
+ return Result::success();
+}
+
void Monitor::closeSocket() {
if (mSocketFd != -1) {
::close(mSocketFd);
@@ -154,14 +188,23 @@
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);
+ char name[IF_NAMESIZE + 1] = { 0 };
+ const unsigned int ifIndex = msg->ifi_index;
+ if (if_indextoname(ifIndex, name) == nullptr) {
+ LOGE("Unable to get interface name for interface index %u", ifIndex);
+ }
- InterfaceState state = (msg->ifi_flags & IFF_UP) ? InterfaceState::Up :
- InterfaceState::Down;
- mOnInterfaceStateCallback(msg->ifi_index, name, state);
+ bool isUp = !!(msg->ifi_flags & IFF_UP);
+ auto iterator = mUpInterfaces.find(ifIndex);
+
+ if (iterator == mUpInterfaces.end() && isUp) {
+ // The interface was not known to be up but is up, known state changed
+ mUpInterfaces.insert(ifIndex);
+ mOnInterfaceStateCallback(ifIndex, name, InterfaceState::Up);
+ } else if (iterator != mUpInterfaces.end() && !isUp) {
+ // The interface was known to be up, now it's not, known state changed
+ mUpInterfaces.erase(iterator);
+ mOnInterfaceStateCallback(ifIndex, name, InterfaceState::Down);
}
}
diff --git a/network/netmgr/monitor.h b/network/netmgr/monitor.h
index 3d2dc62..00f525e 100644
--- a/network/netmgr/monitor.h
+++ b/network/netmgr/monitor.h
@@ -20,7 +20,7 @@
#include "pollable.h"
#include "result.h"
-const char* interfaceStateToStr(InterfaceState state);
+#include <unordered_set>
/** Monitor network interfaces and provide notifications of changes to those
* interfaces.
@@ -46,10 +46,12 @@
private:
Result openSocket();
+ Result requestInterfaces();
void closeSocket();
void handleNewLink(const struct nlmsghdr* hdr);
int mSocketFd;
OnInterfaceStateCallback mOnInterfaceStateCallback;
+ std::unordered_set<unsigned int> mUpInterfaces;
};
diff --git a/network/netmgr/timestamp.cpp b/network/netmgr/timestamp.cpp
deleted file mode 100644
index 36c79ee..0000000
--- a/network/netmgr/timestamp.cpp
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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/utils.cpp b/network/netmgr/utils.cpp
new file mode 100644
index 0000000..fa71c1e
--- /dev/null
+++ b/network/netmgr/utils.cpp
@@ -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.
+ */
+
+#include "utils.h"
+
+#include <string.h>
+
+std::vector<std::string> explode(const char* str, char divider) {
+ const char* cur = str;
+ const char* space = nullptr;
+ std::vector<std::string> result;
+ do {
+ space = ::strchr(cur, divider);
+ if (space) {
+ result.emplace_back(cur, space);
+ cur = space + 1;
+ } else {
+ result.emplace_back(cur);
+ }
+ } while (space);
+
+ return result;
+}
+
diff --git a/network/netmgr/timestamp.h b/network/netmgr/utils.h
similarity index 73%
copy from network/netmgr/timestamp.h
copy to network/netmgr/utils.h
index 8ad7bf8..7c05882 100644
--- a/network/netmgr/timestamp.h
+++ b/network/netmgr/utils.h
@@ -16,18 +16,7 @@
#pragma once
-#include <time.h>
+#include <string>
+#include <vector>
-class Timestamp {
-public:
- Timestamp();
-
- static Timestamp now();
-
- bool operator==(const Timestamp& other) const;
- bool operator<(const Timestamp& other) const;
-
-private:
- struct timespec mTime;
-};
-
+std::vector<std::string> explode(const char* str, char divider);
diff --git a/network/wifi_forwarder/Android.bp b/network/wifi_forwarder/Android.bp
new file mode 100644
index 0000000..6dbc9ca
--- /dev/null
+++ b/network/wifi_forwarder/Android.bp
@@ -0,0 +1,46 @@
+//
+// Copyright (C) 2019 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: "wifi_forwarder",
+ vendor: true,
+ cppflags: [
+ "-Wall",
+ "-Werror",
+ "--std=c++17",
+ ],
+ srcs: [
+ "frame.cpp",
+ "local_connection.cpp",
+ "main.cpp",
+ "netlink_message.cpp",
+ "netlink_socket.cpp",
+ "poller.cpp",
+ "remote_connection.cpp",
+ "wifi_forwarder.cpp",
+ ],
+ shared_libs: [
+ "libcutils",
+ "liblog",
+ "libnl",
+ "libpcap",
+ "libutils",
+ "libutilscallstack",
+ ],
+ header_libs: [
+ "goldfish_headers",
+ ],
+}
diff --git a/network/wifi_forwarder/cache.h b/network/wifi_forwarder/cache.h
new file mode 100644
index 0000000..bd64f1a
--- /dev/null
+++ b/network/wifi_forwarder/cache.h
@@ -0,0 +1,192 @@
+/*
+ * Copyright 2019, 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 <inttypes.h>
+#include "log.h"
+
+#include <chrono>
+#include <unordered_map>
+
+// Default amount of time until a cache entry expires
+static constexpr auto kDefaultCacheTimeout = std::chrono::seconds(30);
+
+template<typename Key,
+ typename Value,
+ typename Timestamp = std::chrono::steady_clock::time_point>
+class Cache {
+public:
+ using TimedValue = std::pair<Timestamp, Value>;
+
+ using MapType = std::unordered_map<Key, TimedValue>;
+ using key_type = typename MapType::key_type;
+ using mapped_type = Value;
+
+ class ConstIterator {
+ public:
+ class IterPair {
+ public:
+ IterPair(const Key& key, const Value& value)
+ : first(key), second(value) {
+ }
+
+ const IterPair* operator->() const { return this; }
+ const Key& first;
+ const Value& second;
+ private:
+ };
+
+ ConstIterator(typename MapType::const_iterator current)
+ : mCurrent(current) { }
+
+ IterPair operator->() const {
+ return IterPair(mCurrent->first, mCurrent->second.second);
+ }
+
+ IterPair operator*() const {
+ return IterPair(mCurrent->first, mCurrent->second.second);
+ }
+
+ bool operator==(const ConstIterator& other) const {
+ return mCurrent == other.mCurrent;
+ }
+
+ bool operator!=(const ConstIterator& other) const {
+ return mCurrent != other.mCurrent;
+ }
+
+ typename MapType::const_iterator internal() const { return mCurrent; }
+
+ private:
+ typename MapType::const_iterator mCurrent;
+ };
+ class Iterator {
+ public:
+ class IterPair {
+ public:
+ IterPair(const Key& key, Value& value) : first(key), second(value) { }
+
+ IterPair* operator->() { return this; }
+ const Key& first;
+ Value& second;
+ private:
+ };
+
+ Iterator(typename MapType::iterator current) : mCurrent(current) { }
+
+ IterPair operator->() {
+ return IterPair(mCurrent->first, mCurrent->second.second);
+ }
+
+ IterPair operator*() {
+ return IterPair(mCurrent->first, mCurrent->second.second);
+ }
+
+ bool operator==(const Iterator& other) const {
+ return mCurrent == other.mCurrent;
+ }
+
+ bool operator!=(const Iterator& other) const {
+ return mCurrent != other.mCurrent;
+ }
+
+ typename MapType::iterator internal() { return mCurrent; }
+
+ private:
+ typename MapType::iterator mCurrent;
+ };
+
+ using iterator = Iterator;
+ using const_iterator = ConstIterator;
+ using insert_return_type = std::pair<const_iterator, bool>;
+
+ Cache(std::chrono::milliseconds timeout = kDefaultCacheTimeout)
+ : mTimeout(timeout) {
+ }
+
+ template<typename M>
+ insert_return_type insert_or_assign(const key_type& key, M&& value) {
+ std::pair<typename MapType::iterator,bool> inserted =
+ mMap.insert_or_assign(key, TimedValue(mCurrentTime,
+ std::move(value)));
+ return insert_return_type(inserted.first, inserted.second);
+ }
+
+ mapped_type& operator[](const key_type& key) {
+ TimedValue& v = mMap[key];
+ v.first = mCurrentTime;
+ return v.second;
+ }
+
+ iterator find(const key_type& key) {
+ return iterator(mMap.find(key));
+ }
+
+ const_iterator find(const key_type& key) const {
+ return const_iterator(mMap.find(key));
+ }
+
+ iterator erase(const_iterator pos) {
+ return iterator(mMap.erase(pos.internal()));
+ }
+
+ iterator erase(iterator pos) {
+ return iterator(mMap.erase(pos.internal()));
+ }
+
+ size_t erase(const key_type& key) {
+ return mMap.erase(key);
+ }
+
+ iterator begin() {
+ return iterator(mMap.begin());
+ }
+
+ iterator end() {
+ return iterator(mMap.end());
+ }
+
+ const_iterator begin() const {
+ return const_iterator(mMap.begin());
+ }
+
+ const_iterator end() const {
+ return const_iterator(mMap.end());
+ }
+
+ void setCurrentTime(Timestamp currentTime) {
+ mCurrentTime = currentTime;
+ }
+
+ void expireEntries() {
+ for (auto it = mMap.begin(); it != mMap.end(); ) {
+ const Timestamp timestamp = it->second.first;
+ if (mCurrentTime > timestamp &&
+ (mCurrentTime - timestamp) > mTimeout) {
+ // This entry has expired, remove it
+ it = mMap.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ }
+private:
+ const std::chrono::milliseconds mTimeout;
+ Timestamp mCurrentTime;
+ MapType mMap;
+};
+
diff --git a/network/wifi_forwarder/frame.cpp b/network/wifi_forwarder/frame.cpp
new file mode 100644
index 0000000..3b20b4e
--- /dev/null
+++ b/network/wifi_forwarder/frame.cpp
@@ -0,0 +1,370 @@
+/*
+ * Copyright 2019, 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 "frame.h"
+
+#include "hwsim.h"
+#include "ieee80211.h"
+
+#include <asm/byteorder.h>
+
+#include <sstream>
+
+static constexpr uint64_t kSlotTime = 9;
+
+static const AccessCategory kPriorityToAc[8] = {
+ AccessCategory::BestEffort,
+ AccessCategory::Background,
+ AccessCategory::Background,
+ AccessCategory::BestEffort,
+ AccessCategory::Video,
+ AccessCategory::Video,
+ AccessCategory::Voice,
+ AccessCategory::Voice,
+};
+
+static uint32_t calcContentionWindowMin(AccessCategory ac) {
+ switch (ac) {
+ case AccessCategory::Voice:
+ return 3;
+ case AccessCategory::Video:
+ return 7;
+ case AccessCategory::BestEffort:
+ return 15;
+ case AccessCategory::Background:
+ return 15;
+ }
+}
+
+static uint32_t calcContentionWindowMax(AccessCategory ac) {
+ switch (ac) {
+ case AccessCategory::Voice:
+ return 7;
+ case AccessCategory::Video:
+ return 15;
+ case AccessCategory::BestEffort:
+ return 1023;
+ case AccessCategory::Background:
+ return 1023;
+ }
+}
+
+FrameType frameTypeFromByte(uint8_t byte) {
+ if (byte == static_cast<uint8_t>(FrameType::Ack)) {
+ return FrameType::Ack;
+ } else if (byte == static_cast<uint8_t>(FrameType::Data)) {
+ return FrameType::Data;
+ }
+ return FrameType::Unknown;
+}
+
+FrameInfo::FrameInfo(const MacAddress& transmitter,
+ uint64_t cookie,
+ uint32_t flags,
+ uint32_t channel,
+ const hwsim_tx_rate* rates,
+ size_t numRates)
+ : mTransmitter(transmitter)
+ , mCookie(cookie)
+ , mFlags(flags)
+ , mChannel(channel) {
+ size_t i = 0;
+ for (; i < numRates; ++i) {
+ mTxRates[i].count = 0;
+ mTxRates[i].idx = rates[i].idx;
+ }
+ for (; i < mTxRates.size(); ++i) {
+ mTxRates[i].count = 0;
+ mTxRates[i].idx = -1;
+ }
+}
+
+bool FrameInfo::shouldAck() const {
+ return !!(mFlags & HWSIM_TX_CTL_REQ_TX_STATUS);
+}
+
+Frame::Frame(const uint8_t* data, size_t size) : mData(data, data + size) {
+}
+
+Frame::Frame(const uint8_t* data, size_t size, const MacAddress& transmitter,
+ uint64_t cookie, uint32_t flags, uint32_t channel,
+ const hwsim_tx_rate* rates, size_t numRates)
+ : mData(data, data + size)
+ , mInfo(transmitter, cookie, flags, channel, rates, numRates)
+ , mContentionWindow(calcContentionWindowMin(getAc()))
+ , mContentionWindowMax(calcContentionWindowMax(getAc())) {
+ memcpy(mInitialTxRates.data(), rates, numRates * sizeof(*rates));
+}
+
+bool Frame::incrementAttempts() {
+ Rates& rates = mInfo.rates();
+ for (size_t i = 0; i < rates.size(); ++i) {
+ if (mInitialTxRates[i].idx == -1) {
+ // We've run out of attempts
+ break;
+ }
+ if (rates[i].count < mInitialTxRates[i].count) {
+ ++rates[i].count;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool Frame::hasRemainingAttempts() {
+ Rates& rates = mInfo.rates();
+ for (size_t i = 0; i < rates.size(); ++i) {
+ if (mInitialTxRates[i].idx == -1) {
+ break;
+ }
+ if (rates[i].count < mInitialTxRates[i].count) {
+ return true;
+ }
+ }
+ return false;
+}
+
+const MacAddress& Frame::source() const {
+ auto hdr = reinterpret_cast<const struct ieee80211_hdr*>(mData.data());
+ return *reinterpret_cast<const MacAddress*>(hdr->addr2);
+}
+
+const MacAddress& Frame::destination() const {
+ auto hdr = reinterpret_cast<const struct ieee80211_hdr*>(mData.data());
+ return *reinterpret_cast<const MacAddress*>(hdr->addr1);
+}
+
+std::string Frame::str() const {
+ uint8_t type = (mData[0] >> 2) & 0x3;
+ uint8_t subType = (mData[0] >> 4) & 0x0F;
+
+ std::stringstream ss;
+ ss << "[ Ck: " << cookie() << " Ch: " << channel() << " ] ";
+ switch (type) {
+ case 0:
+ // Management
+ ss << "Management (";
+ switch (subType) {
+ case 0:
+ ss << "Association Request";
+ break;
+ case 1:
+ ss << "Association Response";
+ break;
+ case 2:
+ ss << "Reassociation Request";
+ break;
+ case 3:
+ ss << "Reassociation Response";
+ break;
+ case 4:
+ ss << "Probe Request";
+ break;
+ case 5:
+ ss << "Probe Response";
+ break;
+ case 6:
+ ss << "Timing Advertisement";
+ break;
+ case 8:
+ ss << "Beacon";
+ break;
+ case 9:
+ ss << "ATIM";
+ break;
+ case 10:
+ ss << "Disassociation";
+ break;
+ case 11:
+ ss << "Authentication";
+ break;
+ case 12:
+ ss << "Deauthentication";
+ break;
+ case 13:
+ ss << "Action";
+ break;
+ case 14:
+ ss << "Action No Ack";
+ break;
+ default:
+ ss << subType;
+ break;
+ }
+ ss << ')';
+ break;
+ case 1:
+ // Control
+ ss << "Control (";
+ switch (subType) {
+ case 4:
+ ss << "Beamforming Report Poll";
+ break;
+ case 5:
+ ss << "VHT NDP Announcement";
+ break;
+ case 6:
+ ss << "Control Frame Extension";
+ break;
+ case 7:
+ ss << "Control Wrapper";
+ break;
+ case 8:
+ ss << "Block Ack Request";
+ break;
+ case 9:
+ ss << "Block Ack";
+ break;
+ case 10:
+ ss << "PS-Poll";
+ break;
+ case 11:
+ ss << "RTS";
+ break;
+ case 12:
+ ss << "CTS";
+ break;
+ case 13:
+ ss << "Ack";
+ break;
+ case 14:
+ ss << "CF-End";
+ break;
+ case 15:
+ ss << "CF-End+CF-Ack";
+ break;
+ default:
+ ss << subType;
+ break;
+ }
+ ss << ')';
+ break;
+ case 2:
+ // Data
+ ss << "Data (";
+ switch (subType) {
+ case 0:
+ ss << "Data";
+ break;
+ case 1:
+ ss << "Data+CF-Ack";
+ break;
+ case 2:
+ ss << "Data+CF-Poll";
+ break;
+ case 3:
+ ss << "Data+CF-Ack+CF-Poll";
+ break;
+ case 4:
+ ss << "Null";
+ break;
+ case 5:
+ ss << "CF-Ack";
+ break;
+ case 6:
+ ss << "CF-Poll";
+ break;
+ case 7:
+ ss << "CF-Ack+CF-Poll";
+ break;
+ case 8:
+ ss << "QoS Data";
+ break;
+ case 9:
+ ss << "QoS Data+CF-Ack";
+ break;
+ case 10:
+ ss << "QoS Data+CF-Poll";
+ break;
+ case 11:
+ ss << "QoS Data+CF-Ack+CF-Poll";
+ break;
+ case 12:
+ ss << "QoS Null";
+ break;
+ case 14:
+ ss << "QoS CF-Poll";
+ break;
+ case 15:
+ ss << "QoS CF-Poll+CF-Ack";
+ break;
+ default:
+ ss << subType;
+ break;
+ }
+ ss << ')';
+ break;
+ case 3:
+ // Extension
+ ss << "Extension (";
+ switch (subType) {
+ case 0:
+ ss << "DMG Beacon";
+ break;
+ default:
+ ss << subType;
+ break;
+ }
+ ss << ')';
+ break;
+ default:
+ ss << type << " (" << subType << ')';
+ break;
+ }
+ return ss.str();
+}
+
+bool Frame::isBeacon() const {
+ uint8_t totalType = mData[0] & 0xFC;
+ return totalType == 0x80;
+}
+
+bool Frame::isData() const {
+ uint8_t totalType = mData[0] & 0x0C;
+ return totalType == 0x08;
+}
+
+bool Frame::isDataQoS() const {
+ uint8_t totalType = mData[0] & 0xFC;
+ return totalType == 0x88;
+}
+
+uint16_t Frame::getQoSControl() const {
+ // Some frames have 4 address fields instead of 3 which pushes QoS control
+ // forward 6 bytes.
+ bool uses4Addresses = !!(mData[1] & 0x03);
+ const uint8_t* addr = &mData[uses4Addresses ? 30 : 24];
+
+ return __le16_to_cpu(*reinterpret_cast<const uint16_t*>(addr));
+}
+
+uint64_t Frame::calcNextTimeout() {
+ mNextTimeout = (mContentionWindow * kSlotTime) / 2;
+ mContentionWindow = std::min((mContentionWindow * 2) + 1,
+ mContentionWindowMax);
+ return mNextTimeout;
+}
+
+AccessCategory Frame::getAc() const {
+ if (!isData()) {
+ return AccessCategory::Voice;
+ }
+ if (!isDataQoS()) {
+ return AccessCategory::BestEffort;
+ }
+ uint16_t priority = getQoSControl() & 0x07;
+ return kPriorityToAc[priority];
+}
diff --git a/network/wifi_forwarder/frame.h b/network/wifi_forwarder/frame.h
new file mode 100644
index 0000000..ac76f82
--- /dev/null
+++ b/network/wifi_forwarder/frame.h
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2019, 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 <stdint.h>
+
+#include <array>
+#include <vector>
+
+#define IEEE80211_TX_MAX_RATES 4
+
+struct hwsim_tx_rate {
+ signed char idx;
+ unsigned char count;
+} __attribute__((__packed__));
+
+enum class FrameType : uint8_t {
+ Unknown,
+ Ack,
+ Data,
+};
+
+enum class AccessCategory {
+ Voice,
+ Video,
+ BestEffort,
+ Background,
+};
+
+FrameType frameTypeFromByte(uint8_t byte);
+
+using Rates = std::array<hwsim_tx_rate, IEEE80211_TX_MAX_RATES>;
+
+class FrameInfo {
+public:
+ FrameInfo() = default;
+ FrameInfo(const FrameInfo&) = default;
+ FrameInfo(FrameInfo&&) = default;
+ FrameInfo(const MacAddress& transmitter,
+ uint64_t cookie,
+ uint32_t flags,
+ uint32_t channel,
+ const hwsim_tx_rate* rates,
+ size_t numRates);
+
+ FrameInfo& operator=(const FrameInfo&) = default;
+ FrameInfo& operator=(FrameInfo&&) = default;
+
+ const Rates& rates() const { return mTxRates; }
+ Rates& rates() { return mTxRates; }
+
+ const MacAddress& transmitter() const { return mTransmitter; }
+ uint64_t cookie() const { return mCookie; }
+ uint32_t flags() const { return mFlags; }
+ uint32_t channel() const { return mChannel; }
+
+ bool shouldAck() const;
+
+private:
+ Rates mTxRates;
+ MacAddress mTransmitter;
+ uint64_t mCookie = 0;
+ uint32_t mFlags = 0;
+ uint32_t mChannel = 0;
+};
+
+class Frame {
+public:
+ Frame() = default;
+ Frame(const uint8_t* data, size_t size);
+ Frame(const uint8_t* data,
+ size_t size,
+ const MacAddress& transmitter,
+ uint64_t cookie,
+ uint32_t flags,
+ uint32_t channel,
+ const hwsim_tx_rate* rates,
+ size_t numRates);
+ Frame(Frame&& other) = default;
+
+ Frame& operator=(Frame&& other) = default;
+
+ size_t size() const { return mData.size(); }
+ const uint8_t* data() const { return mData.data(); }
+ uint8_t* data() { return mData.data(); }
+ uint64_t cookie() const { return mInfo.cookie(); }
+ uint32_t flags() const { return mInfo.flags(); }
+ uint32_t channel() const { return mInfo.channel(); }
+
+ Rates& rates() { return mInfo.rates(); }
+ const Rates& rates() const { return mInfo.rates(); }
+
+ const Rates& initialRates() const { return mInitialTxRates; }
+
+ // Increment the number of attempts made in the tx rates
+ bool incrementAttempts();
+ bool hasRemainingAttempts();
+
+ // The transmitter as indicated by hwsim
+ const MacAddress& transmitter() const { return mInfo.transmitter(); }
+ // The source as indicated by the 802.11 frame
+ const MacAddress& source() const;
+ // The destination as indicated by the 802.11 frame
+ const MacAddress& destination() const;
+
+ std::string str() const;
+
+ const FrameInfo& info() const { return mInfo; }
+ FrameInfo& info() { return mInfo; }
+
+ bool isBeacon() const;
+ bool isData() const;
+ bool isDataQoS() const;
+
+ uint16_t getQoSControl() const;
+
+ bool shouldAck() const { return mInfo.shouldAck(); }
+
+ uint64_t calcNextTimeout();
+
+ void setRadioDestination(const MacAddress& destination) {
+ mRadioDestination = destination;
+ }
+ const MacAddress& radioDestination() const { return mRadioDestination; }
+private:
+ Frame(const Frame&) = delete;
+ Frame& operator=(const Frame&) = delete;
+
+ AccessCategory getAc() const;
+
+ std::vector<uint8_t> mData;
+ FrameInfo mInfo;
+ MacAddress mRadioDestination;
+ Rates mInitialTxRates;
+ uint64_t mNextTimeout = 0;
+ // The contention winow determines how much time to back off on each retry.
+ // The contention window initial value and max value are determined by the
+ // access category of the frame.
+ uint32_t mContentionWindow = 0;
+ uint32_t mContentionWindowMax = 0;
+};
+
diff --git a/network/wifi_forwarder/frame_id.h b/network/wifi_forwarder/frame_id.h
new file mode 100644
index 0000000..545693a
--- /dev/null
+++ b/network/wifi_forwarder/frame_id.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2019, 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 <string.h>
+
+struct FrameId {
+ FrameId() {}
+ FrameId(uint64_t cookie, const MacAddress& transmitter)
+ : cookie(cookie), transmitter(transmitter) {
+ }
+ FrameId(const FrameId&) = default;
+ FrameId(FrameId&&) = default;
+
+ FrameId& operator=(const FrameId&) = default;
+ FrameId& operator=(FrameId&&) = default;
+
+ uint64_t cookie;
+ MacAddress transmitter;
+};
+
+namespace std {
+template<> struct hash<FrameId> {
+ size_t operator()(const FrameId& id) const {
+ size_t seed = 0;
+ hash_combine(seed, id.cookie);
+ hash_combine(seed, id.transmitter);
+ return seed;
+ }
+};
+}
+
+inline bool operator==(const FrameId& left, const FrameId& right) {
+ return left.cookie == right.cookie && left.transmitter == right.transmitter;
+}
+
+inline bool operator<(const FrameId& left, const FrameId& right) {
+ if (left.cookie < right.cookie) {
+ return true;
+ }
+ if (left.cookie > right.cookie) {
+ return false;
+ }
+ return memcmp(left.transmitter.addr,
+ right.transmitter.addr,
+ sizeof(left.transmitter.addr)) < 0;
+}
diff --git a/dhcp/server/log.h b/network/wifi_forwarder/hash.h
similarity index 69%
copy from dhcp/server/log.h
copy to network/wifi_forwarder/hash.h
index a0f21e0..ecd8c23 100644
--- a/dhcp/server/log.h
+++ b/network/wifi_forwarder/hash.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2019, 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.
@@ -13,8 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
#pragma once
-#define LOG_TAG "dhcpserver"
-#include <log/log.h>
+#include <functional>
+
+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);
+}
diff --git a/network/wifi_forwarder/hwsim.h b/network/wifi_forwarder/hwsim.h
new file mode 100644
index 0000000..1a7b09b
--- /dev/null
+++ b/network/wifi_forwarder/hwsim.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2019, 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
+
+// mac80211_hwsim flags, from kernel drivers/net/wireless/mac80211_hwsim.h
+#define BIT(num) (1UL << (num))
+enum hwsim_tx_control_flags {
+ HWSIM_TX_CTL_REQ_TX_STATUS = BIT(0),
+ HWSIM_TX_CTL_NO_ACK = BIT(1),
+ HWSIM_TX_STAT_ACK = BIT(2),
+};
+
+// mac80211_hwsim commands, from kernel drivers/net/wireless/mac80211_hwsim.h
+enum HwSimCommand {
+ HWSIM_CMD_UNSPEC,
+ HWSIM_CMD_REGISTER,
+ HWSIM_CMD_FRAME,
+ HWSIM_CMD_TX_INFO_FRAME,
+ HWSIM_CMD_NEW_RADIO,
+ HWSIM_CMD_DEL_RADIO,
+ HWSIM_CMD_GET_RADIO,
+ __HWSIM_CMD_MAX,
+};
+
+// mac80211_hwsim attributes, from kernel drivers/net/wireless/mac80211_hwsim.h
+enum HwSimAttribute {
+ HWSIM_ATTR_UNSPEC,
+ HWSIM_ATTR_ADDR_RECEIVER,
+ HWSIM_ATTR_ADDR_TRANSMITTER,
+ HWSIM_ATTR_FRAME,
+ HWSIM_ATTR_FLAGS,
+ HWSIM_ATTR_RX_RATE,
+ HWSIM_ATTR_SIGNAL,
+ HWSIM_ATTR_TX_INFO,
+ HWSIM_ATTR_COOKIE,
+ HWSIM_ATTR_CHANNELS,
+ HWSIM_ATTR_RADIO_ID,
+ HWSIM_ATTR_REG_HINT_ALPHA2,
+ HWSIM_ATTR_REG_CUSTOM_REG,
+ HWSIM_ATTR_REG_STRICT_REG,
+ HWSIM_ATTR_SUPPORT_P2P_DEVICE,
+ HWSIM_ATTR_USE_CHANCTX,
+ HWSIM_ATTR_DESTROY_RADIO_ON_CLOSE,
+ HWSIM_ATTR_RADIO_NAME,
+ HWSIM_ATTR_NO_VIF,
+ HWSIM_ATTR_FREQ,
+ __HWSIM_ATTR_MAX,
+};
+#define HWSIM_ATTR_MAX (__HWSIM_ATTR_MAX - 1)
+
diff --git a/network/netmgr/timestamp.h b/network/wifi_forwarder/ieee80211.h
similarity index 60%
copy from network/netmgr/timestamp.h
copy to network/wifi_forwarder/ieee80211.h
index 8ad7bf8..6bcf232 100644
--- a/network/netmgr/timestamp.h
+++ b/network/wifi_forwarder/ieee80211.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2018, The Android Open Source Project
+ * Copyright 2019, 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.
@@ -16,18 +16,16 @@
#pragma once
-#include <time.h>
+#include <net/ethernet.h>
+#include <stdint.h>
-class Timestamp {
-public:
- Timestamp();
-
- static Timestamp now();
-
- bool operator==(const Timestamp& other) const;
- bool operator<(const Timestamp& other) const;
-
-private:
- struct timespec mTime;
-};
+struct ieee80211_hdr {
+ uint16_t frame_control;
+ uint16_t duration_id;
+ uint8_t addr1[ETH_ALEN];
+ uint8_t addr2[ETH_ALEN];
+ uint8_t addr3[ETH_ALEN];
+ uint16_t seq_ctrl;
+ uint8_t addr4[ETH_ALEN];
+} __attribute__((__packed__));
diff --git a/network/wifi_forwarder/local_connection.cpp b/network/wifi_forwarder/local_connection.cpp
new file mode 100644
index 0000000..1cebc17
--- /dev/null
+++ b/network/wifi_forwarder/local_connection.cpp
@@ -0,0 +1,392 @@
+/*
+ * Copyright 2019, 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 "local_connection.h"
+
+#include "hwsim.h"
+#include "log.h"
+#include "macaddress.h"
+#include "netlink_message.h"
+
+#include <net/ethernet.h>
+#include <netlink/netlink.h>
+#include <netlink/genl/ctrl.h>
+#include <netlink/genl/genl.h>
+
+#include <utils/CallStack.h>
+
+#include <inttypes.h>
+#include <stdlib.h>
+
+static const char kHwSimFamilyName[] = "MAC80211_HWSIM";
+static const int kHwSimVersion = 1;
+static const unsigned int kDefaultSignalStrength = -50;
+static const int kDefaultSocketBufferSize = 8 * (1 << 20);
+
+static uint32_t getSeqNum(struct nl_msg* msg) {
+ return nlmsg_hdr(msg)->nlmsg_seq;
+}
+
+static int onSent(struct nl_msg* , void*) {
+ return NL_OK;
+}
+
+static int onSeqCheck(struct nl_msg* msg, void*) {
+ uint32_t seq = getSeqNum(msg);
+ return seq == 0 ? NL_SKIP : NL_OK;
+}
+
+LocalConnection::LocalConnection(OnFrameCallback onFrameCallback,
+ OnAckCallback onAckCallback,
+ OnErrorCallback onErrorCallback)
+ : mOnFrameCallback(onFrameCallback)
+ , mOnAckCallback(onAckCallback)
+ , mOnErrorCallback(onErrorCallback) {
+
+}
+
+Result LocalConnection::init(std::chrono::steady_clock::time_point now) {
+ Result res = mNetlinkSocket.init();
+ if (!res) { return res; }
+ res = mNetlinkSocket.setOnMsgInCallback(staticOnMessage, this);
+ if (!res) { return res; }
+ res = mNetlinkSocket.setOnMsgOutCallback(onSent, this);
+ if (!res) { return res; }
+ res = mNetlinkSocket.setOnSeqCheckCallback(onSeqCheck, this);
+ if (!res) { return res; }
+ res = mNetlinkSocket.setOnAckCallback(staticOnAck, this);
+ if (!res) { return res; }
+ res = mNetlinkSocket.setOnErrorCallback(staticOnError, this);
+ if (!res) { return res; }
+ res = mNetlinkSocket.connectGeneric();
+ if (!res) { return res; }
+ res = mNetlinkSocket.setBufferSizes(kDefaultSocketBufferSize,
+ kDefaultSocketBufferSize);
+ if (!res) { return res; }
+
+ mNetlinkFamily = mNetlinkSocket.resolveNetlinkFamily(kHwSimFamilyName);
+ if (mNetlinkFamily < 0) {
+ return Result::error("Failed to resolve netlink family name: %s",
+ nl_geterror(mNetlinkFamily));
+ }
+
+ mPendingFrames.setCurrentTime(now);
+ mSequenceNumberCookies.setCurrentTime(now);
+
+ mLastCacheTimeUpdate = now;
+ mLastCacheExpiration = now;
+
+ return registerReceiver();
+}
+
+int LocalConnection::getFd() const {
+ return mNetlinkSocket.getFd();
+}
+
+bool LocalConnection::receive() {
+ return mNetlinkSocket.receive();
+}
+
+uint32_t LocalConnection::transferFrame(std::unique_ptr<Frame> frame,
+ const MacAddress& dest) {
+ NetlinkMessage msg;
+
+ if (!msg.initGeneric(mNetlinkFamily, HWSIM_CMD_FRAME, kHwSimVersion)) {
+ ALOGE("LocalConnection transferFrame failed to init msg");
+ return 0;
+ }
+
+ frame->incrementAttempts();
+
+ if (!msg.addAttribute(HWSIM_ATTR_ADDR_RECEIVER, dest.addr, ETH_ALEN) ||
+ !msg.addAttribute(HWSIM_ATTR_FRAME, frame->data(), frame->size()) ||
+ !msg.addAttribute(HWSIM_ATTR_RX_RATE, 1u) ||
+ !msg.addAttribute(HWSIM_ATTR_SIGNAL, kDefaultSignalStrength) ||
+ !msg.addAttribute(HWSIM_ATTR_FREQ, frame->channel())) {
+
+ ALOGE("LocalConnection transferFrame failed to set attrs");
+ return 0;
+ }
+
+ if (!mNetlinkSocket.send(msg)) {
+ return 0;
+ }
+
+ // Store the radio destination for potential retransmissions.
+ frame->setRadioDestination(dest);
+
+ uint32_t seqNum = msg.getSeqNum();
+ uint64_t cookie = frame->cookie();
+ FrameId id(cookie, frame->transmitter());
+ mSequenceNumberCookies[seqNum] = id;
+ mPendingFrames[id] = std::move(frame);
+
+ return seqNum;
+}
+
+uint32_t LocalConnection::cloneFrame(const Frame& frame,
+ const MacAddress& dest) {
+ auto copy = std::make_unique<Frame>(frame.data(),
+ frame.size(),
+ frame.transmitter(),
+ frame.cookie(),
+ frame.flags(),
+ frame.channel(),
+ frame.rates().data(),
+ frame.rates().size());
+ return transferFrame(std::move(copy), dest);
+}
+
+bool LocalConnection::ackFrame(FrameInfo& info, bool success) {
+ NetlinkMessage msg;
+
+ if (!msg.initGeneric(mNetlinkFamily,
+ HWSIM_CMD_TX_INFO_FRAME,
+ kHwSimVersion)) {
+ ALOGE("LocalConnection ackFrame failed to create msg");
+ return false;
+ }
+
+ uint32_t flags = info.flags();
+ if (success) {
+ flags |= HWSIM_TX_STAT_ACK;
+ }
+
+ const uint8_t* transmitter = info.transmitter().addr;
+ const Rates& rates = info.rates();
+ if (!msg.addAttribute(HWSIM_ATTR_ADDR_TRANSMITTER, transmitter, ETH_ALEN) ||
+ !msg.addAttribute(HWSIM_ATTR_TX_INFO, rates.data(), rates.size()) ||
+ !msg.addAttribute(HWSIM_ATTR_FLAGS, flags) ||
+ !msg.addAttribute(HWSIM_ATTR_SIGNAL, kDefaultSignalStrength) ||
+ !msg.addAttribute(HWSIM_ATTR_COOKIE, info.cookie())) {
+
+ ALOGE("LocalConnection ackFrame failed to set attributes");
+ return false;
+ }
+
+ if (!mNetlinkSocket.send(msg)) {
+ return false;
+ }
+ mPendingFrames.erase(FrameId(info.cookie(), info.transmitter()));
+ return true;
+}
+
+std::chrono::steady_clock::time_point LocalConnection::getTimeout() const {
+ if (mRetryQueue.empty()) {
+ return std::chrono::steady_clock::time_point::max();
+ }
+ return mRetryQueue.top().first;
+}
+
+void LocalConnection::onTimeout(std::chrono::steady_clock::time_point now) {
+
+ if (now - mLastCacheTimeUpdate > std::chrono::seconds(1)) {
+ // Only update the time once per second, there's no need for a super
+ // high resolution here. We just want to make sure these caches don't
+ // fill up over a long period of time.
+ mPendingFrames.setCurrentTime(now);
+ mSequenceNumberCookies.setCurrentTime(now);
+ mLastCacheTimeUpdate = now;
+ }
+ if (now - mLastCacheExpiration > std::chrono::seconds(10)) {
+ // Only expire entries every 10 seconds, this is an operation that has
+ // some cost to it and doesn't have to happen very often.
+ mPendingFrames.expireEntries();
+ mSequenceNumberCookies.expireEntries();
+ mLastCacheExpiration = now;
+ }
+
+ while (!mRetryQueue.empty() && now >= mRetryQueue.top().first) {
+ FrameId id = mRetryQueue.top().second;
+ auto frameIt = mPendingFrames.find(id);
+ if (frameIt != mPendingFrames.end()) {
+ // Frame is still available, retry it
+ std::unique_ptr<Frame> frame = std::move(frameIt->second);
+ mPendingFrames.erase(frameIt);
+ MacAddress dest = frame->radioDestination();
+ transferFrame(std::move(frame), dest);
+ }
+ mRetryQueue.pop();
+ }
+}
+
+Result LocalConnection::registerReceiver() {
+ NetlinkMessage msg;
+
+ if (!msg.initGeneric(mNetlinkFamily, HWSIM_CMD_REGISTER, kHwSimVersion)) {
+ return Result::error("Failed to create register receiver message");
+ }
+
+ if (!mNetlinkSocket.send(msg)) {
+ return Result::error("Failed to send register receiver message");
+ }
+ return Result::success();
+}
+
+int LocalConnection::staticOnMessage(struct nl_msg* msg, void* context) {
+ if (!context) {
+ return NL_SKIP;
+ }
+ auto connection = static_cast<LocalConnection*>(context);
+ return connection->onMessage(msg);
+}
+
+int LocalConnection::staticOnAck(struct nl_msg* msg, void* context) {
+ if (!context) {
+ return NL_SKIP;
+ }
+ auto connection = static_cast<LocalConnection*>(context);
+ return connection->onAck(msg);
+}
+
+int LocalConnection::staticOnError(struct sockaddr_nl* addr,
+ struct nlmsgerr* error,
+ void* context) {
+ if (!context) {
+ return NL_SKIP;
+ }
+ auto connection = static_cast<LocalConnection*>(context);
+ return connection->onError(addr, error);
+}
+
+int LocalConnection::onMessage(struct nl_msg* msg) {
+ struct nlmsghdr* hdr = nlmsg_hdr(msg);
+ auto generic = reinterpret_cast<const struct genlmsghdr*>(nlmsg_data(hdr));
+
+ switch (generic->cmd) {
+ case HWSIM_CMD_FRAME:
+ return onFrame(msg);
+ }
+ return NL_OK;
+}
+
+int LocalConnection::onFrame(struct nl_msg* msg) {
+
+ struct nlmsghdr* hdr = nlmsg_hdr(msg);
+ std::unique_ptr<Frame> frame = parseFrame(hdr);
+ if (!frame) {
+ return NL_SKIP;
+ }
+
+ mOnFrameCallback(std::move(frame));
+ return NL_OK;
+}
+
+std::unique_ptr<Frame> LocalConnection::parseFrame(struct nlmsghdr* hdr) {
+ struct nlattr* attrs[HWSIM_ATTR_MAX + 1];
+ genlmsg_parse(hdr, 0, attrs, HWSIM_ATTR_MAX, nullptr);
+ if (!attrs[HWSIM_ATTR_ADDR_TRANSMITTER]) {
+ ALOGE("Received cmd frame without transmitter address");
+ return nullptr;
+ }
+ if (!attrs[HWSIM_ATTR_TX_INFO]) {
+ ALOGE("Received cmd frame without tx rates");
+ return nullptr;
+ }
+ if (!attrs[HWSIM_ATTR_FREQ]) {
+ ALOGE("Recieved cmd frame without channel frequency");
+ return nullptr;
+ }
+
+ uint64_t cookie = nla_get_u64(attrs[HWSIM_ATTR_COOKIE]);
+
+ const auto& source = *reinterpret_cast<const MacAddress*>(
+ nla_data(attrs[HWSIM_ATTR_ADDR_TRANSMITTER]));
+
+ auto rates = reinterpret_cast<const hwsim_tx_rate*>(
+ nla_data(attrs[HWSIM_ATTR_TX_INFO]));
+ int rateLength = nla_len(attrs[HWSIM_ATTR_TX_INFO]);
+ // Make sure the length is valid, must be multiple of hwsim_tx_rate
+ if (rateLength <= 0 || (rateLength % sizeof(hwsim_tx_rate)) != 0) {
+ ALOGE("Invalid tx rate length %d", rateLength);
+ }
+ size_t numRates = static_cast<size_t>(rateLength) / sizeof(hwsim_tx_rate);
+
+ int length = nla_len(attrs[HWSIM_ATTR_FRAME]);
+ auto data = reinterpret_cast<uint8_t*>(nla_data(attrs[HWSIM_ATTR_FRAME]));
+
+ uint32_t flags = nla_get_u32(attrs[HWSIM_ATTR_FLAGS]);
+
+ uint32_t channel = nla_get_u32(attrs[HWSIM_ATTR_FREQ]);
+
+ return std::make_unique<Frame>(data, length, source, cookie,
+ flags, channel, rates, numRates);
+}
+
+int LocalConnection::onAck(struct nl_msg* msg) {
+ struct nlmsghdr* hdr = nlmsg_hdr(msg);
+ uint32_t seqNum = hdr->nlmsg_seq;
+ auto cookie = mSequenceNumberCookies.find(seqNum);
+ if (cookie == mSequenceNumberCookies.end()) {
+ // This is not a frame we sent. This is fairly common for libnl's
+ // internal use so don't log this.
+ return NL_SKIP;
+ }
+ auto pendingFrame = mPendingFrames.find(cookie->second);
+ // We don't need to keep this around anymore, erase it.
+ mSequenceNumberCookies.erase(seqNum);
+ if (pendingFrame == mPendingFrames.end()) {
+ // We have no cookie associated with this sequence number. This might
+ // happen if the remote connection already acked the frame and removed
+ // the frame info. Consider this resolved.
+ return NL_SKIP;
+ }
+
+ Frame* frame = pendingFrame->second.get();
+ mOnAckCallback(frame->info());
+ // Make sure to erase using the cookie instead of the iterator. The callback
+ // might have already erased this entry so the iterator could be invalid at
+ // this point. Instead of a fancy scheme of checking this just play it safe
+ // to allow the callback more freedom.
+ mPendingFrames.erase(cookie->second);
+
+ return NL_OK;
+}
+
+int LocalConnection::onError(struct sockaddr_nl*, struct nlmsgerr* error) {
+ struct nlmsghdr* hdr = &error->msg;
+ uint32_t seqNum = hdr->nlmsg_seq;
+
+ auto idIt = mSequenceNumberCookies.find(seqNum);
+ if (idIt == mSequenceNumberCookies.end()) {
+ return NL_SKIP;
+ }
+ FrameId id = idIt->second;
+ // No need to keep the sequence number around anymore, it's expired and is
+ // no longer useful.
+ mSequenceNumberCookies.erase(idIt);
+
+ auto frameIt = mPendingFrames.find(id);
+ if (frameIt == mPendingFrames.end()) {
+ return NL_SKIP;
+ }
+ Frame* frame = frameIt->second.get();
+
+ if (!frame->hasRemainingAttempts()) {
+ // This frame has used up all its attempts, there's nothing we can do
+ mOnErrorCallback(frame->info());
+ mPendingFrames.erase(id);
+ return NL_SKIP;
+ }
+ // Store the frame in the retry queue
+ uint64_t timeout = frame->calcNextTimeout();
+ auto deadline = std::chrono::steady_clock::now() +
+ std::chrono::microseconds(timeout);
+ mRetryQueue.emplace(deadline, id);
+
+ return NL_SKIP;
+}
+
diff --git a/network/wifi_forwarder/local_connection.h b/network/wifi_forwarder/local_connection.h
new file mode 100644
index 0000000..9f3969a
--- /dev/null
+++ b/network/wifi_forwarder/local_connection.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2019, 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 "cache.h"
+#include "frame.h"
+#include "frame_id.h"
+#include "macaddress.h"
+#include "netlink_socket.h"
+#include "result.h"
+
+#include <chrono>
+#include <functional>
+#include <memory>
+#include <queue>
+
+struct MacAddress;
+
+class LocalConnection {
+public:
+ using OnFrameCallback = std::function<void (std::unique_ptr<Frame>)>;
+ using OnAckCallback = std::function<void (FrameInfo&)>;
+ using OnErrorCallback = OnAckCallback;
+
+ LocalConnection(OnFrameCallback onFrameCallback,
+ OnAckCallback onAckCallback,
+ OnErrorCallback onErrorCallback);
+ Result init(std::chrono::steady_clock::time_point now);
+
+ int getFd() const;
+ bool receive();
+ uint32_t cloneFrame(const Frame& frame, const MacAddress& destination);
+ uint32_t transferFrame(std::unique_ptr<Frame> frame,
+ const MacAddress& destination);
+ bool ackFrame(FrameInfo& info, bool success);
+
+ std::chrono::steady_clock::time_point getTimeout() const;
+ void onTimeout(std::chrono::steady_clock::time_point now);
+
+private:
+ using Timestamp = std::chrono::steady_clock::time_point;
+
+ Result registerReceiver();
+ static int staticOnMessage(struct nl_msg* msg, void* context);
+ static int staticOnAck(struct nl_msg* msg, void* context);
+ static int staticOnError(struct sockaddr_nl* addr,
+ struct nlmsgerr* error,
+ void* context);
+ int onMessage(struct nl_msg* msg);
+ int onFrame(struct nl_msg* msg);
+ std::unique_ptr<Frame> parseFrame(struct nlmsghdr* hdr);
+
+ int onAck(struct nl_msg* msg);
+ int onError(struct sockaddr_nl* addr, struct nlmsgerr* error);
+ bool sendFrameOnNetlink(const Frame& frame, const MacAddress& dest);
+
+ OnFrameCallback mOnFrameCallback;
+ OnAckCallback mOnAckCallback;
+ OnErrorCallback mOnErrorCallback;
+
+ NetlinkSocket mNetlinkSocket;
+ int mNetlinkFamily = -1;
+
+ // [cookie,transmitter] -> frame.
+ Cache<FrameId, std::unique_ptr<Frame>> mPendingFrames;
+ // sequence number -> [cookie,transmitter]
+ Cache<uint32_t, FrameId> mSequenceNumberCookies;
+
+ Timestamp mLastCacheTimeUpdate;
+ Timestamp mLastCacheExpiration;
+
+ // A queue (using an ordered map) with the next timeout mapping to the
+ // cookie and transmitter of the frame to retry. This way we can easily
+ // determine when the next deadline is by looking at the first entry and we
+ // can quickly determine which frames to retry by starting at the beginning.
+ std::priority_queue<std::pair<Timestamp, FrameId>> mRetryQueue;
+};
+
diff --git a/dhcp/server/log.h b/network/wifi_forwarder/log.h
similarity index 80%
rename from dhcp/server/log.h
rename to network/wifi_forwarder/log.h
index a0f21e0..edadc43 100644
--- a/dhcp/server/log.h
+++ b/network/wifi_forwarder/log.h
@@ -1,11 +1,11 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ * 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,
@@ -13,8 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
#pragma once
-#define LOG_TAG "dhcpserver"
+#define LOG_TAG "wifi_forwarder"
#include <log/log.h>
-
diff --git a/network/wifi_forwarder/macaddress.h b/network/wifi_forwarder/macaddress.h
new file mode 100644
index 0000000..a9bcb66
--- /dev/null
+++ b/network/wifi_forwarder/macaddress.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2019, 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 "hash.h"
+
+#include <linux/if_ether.h>
+#include <stdint.h>
+#include <string.h>
+
+// Format macros for printf, e.g. printf("MAC address: " PRIMAC, MACARG(mac))
+#define PRIMAC "%02x:%02x:%02x:%02x:%02x:%02x"
+#define MACARG(x) (x)[0], (x)[1], (x)[2], (x)[3],(x)[4], (x)[5]
+
+struct MacAddress {
+ MacAddress() {
+ memset(addr, 0, sizeof(addr));
+ }
+ MacAddress(uint8_t b1, uint8_t b2, uint8_t b3,
+ uint8_t b4, uint8_t b5, uint8_t b6) {
+ addr[0] = b1; addr[1] = b2; addr[2] = b3;
+ addr[3] = b4; addr[4] = b5; addr[5] = b6;
+ }
+ uint8_t addr[ETH_ALEN];
+ bool isBroadcast() const {
+ return memcmp(addr, "\xFF\xFF\xFF\xFF\xFF\xFF", ETH_ALEN) == 0;
+ }
+ bool isMulticast() const {
+ return addr[0] & 0x01;
+ }
+ bool empty() const {
+ return memcmp(addr, "\x00\x00\x00\x00\x00\x00", ETH_ALEN) == 0;
+ }
+ uint8_t operator[](size_t index) const {
+ return addr[index];
+ }
+} __attribute__((__packed__));
+
+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;
+ }
+};
+}
+
+inline bool operator==(const MacAddress& left, const MacAddress& right) {
+ return memcmp(left.addr, right.addr, ETH_ALEN) == 0;
+}
+
+inline bool operator!=(const MacAddress& left, const MacAddress& right) {
+ return memcmp(left.addr, right.addr, ETH_ALEN) != 0;
+}
+
diff --git a/network/wifi_forwarder/main.cpp b/network/wifi_forwarder/main.cpp
new file mode 100644
index 0000000..57127a9
--- /dev/null
+++ b/network/wifi_forwarder/main.cpp
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2019, 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 "poller.h"
+#include "wifi_forwarder.h"
+
+int main(int /*argc*/, char* /*argv*/[]) {
+ WifiForwarder forwarder;
+ Result res = forwarder.init();
+ if (!res) {
+ ALOGE("%s", res.c_str());
+ return 1;
+ }
+
+ Poller poller;
+ poller.addPollable(&forwarder);
+ return poller.run();
+}
+
diff --git a/network/wifi_forwarder/netlink_message.cpp b/network/wifi_forwarder/netlink_message.cpp
new file mode 100644
index 0000000..265c296
--- /dev/null
+++ b/network/wifi_forwarder/netlink_message.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2019, 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 "netlink_message.h"
+
+#include <netlink/genl/genl.h>
+#include <netlink/msg.h>
+
+NetlinkMessage::NetlinkMessage() {
+}
+
+NetlinkMessage::NetlinkMessage(NetlinkMessage&& other) {
+ std::swap(mMessage, other.mMessage);
+}
+
+NetlinkMessage::~NetlinkMessage() {
+ if (mMessage) {
+ nlmsg_free(mMessage);
+ mMessage = nullptr;
+ }
+}
+
+NetlinkMessage& NetlinkMessage::operator=(NetlinkMessage&& other) {
+ if (mMessage) {
+ nlmsg_free(mMessage);
+ mMessage = nullptr;
+ }
+ std::swap(mMessage, other.mMessage);
+ return *this;
+}
+
+bool NetlinkMessage::initGeneric(int family, uint8_t command, int version) {
+ if (mMessage) {
+ return false;
+ }
+
+ mMessage = nlmsg_alloc();
+ if (!mMessage) {
+ return false;
+ }
+
+ return genlmsg_put(mMessage, NL_AUTO_PORT, NL_AUTO_SEQ, family, 0,
+ NLM_F_REQUEST, command, version) != nullptr;
+}
+
+uint32_t NetlinkMessage::getSeqNum() const {
+ return nlmsg_hdr(mMessage)->nlmsg_seq;
+}
diff --git a/network/wifi_forwarder/netlink_message.h b/network/wifi_forwarder/netlink_message.h
new file mode 100644
index 0000000..53d97a9
--- /dev/null
+++ b/network/wifi_forwarder/netlink_message.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2019, 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"
+
+#include <netlink/netlink.h>
+
+#include <type_traits>
+
+class NetlinkMessage {
+public:
+ NetlinkMessage();
+ NetlinkMessage(NetlinkMessage&& other);
+ ~NetlinkMessage();
+
+ NetlinkMessage& operator=(NetlinkMessage&& other);
+
+ bool initGeneric(int family, uint8_t command, int version);
+
+ template<typename T,
+ typename = typename std::enable_if<!std::is_pointer<T>::value>::type>
+ bool addAttribute(int attribute, const T& value) {
+ return nla_put(mMessage, attribute, sizeof(T), &value) == 0;
+ }
+ template<typename T,
+ typename = typename std::enable_if<std::is_pointer<T>::value>::type>
+ bool addAttribute(int attribute, T data, size_t numElements) {
+ return nla_put(mMessage,
+ attribute,
+ sizeof(*data) * numElements,
+ data) == 0;
+ }
+
+ uint32_t getSeqNum() const;
+
+ struct nl_msg* get() { return mMessage; }
+
+private:
+ NetlinkMessage(const NetlinkMessage&) = delete;
+ NetlinkMessage& operator=(const NetlinkMessage&) = delete;
+
+ struct nl_msg* mMessage = nullptr;
+};
+
diff --git a/network/wifi_forwarder/netlink_socket.cpp b/network/wifi_forwarder/netlink_socket.cpp
new file mode 100644
index 0000000..5af66a5
--- /dev/null
+++ b/network/wifi_forwarder/netlink_socket.cpp
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2019, 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 "netlink_socket.h"
+
+#include "log.h"
+#include "netlink_message.h"
+
+#include <netlink/genl/ctrl.h>
+#include <netlink/genl/genl.h>
+#include <netlink/netlink.h>
+
+NetlinkSocket::NetlinkSocket() {
+}
+
+NetlinkSocket::~NetlinkSocket() {
+ if (mSocket) {
+ nl_socket_free(mSocket);
+ mSocket = nullptr;
+ mCallback = nullptr;
+ }
+}
+
+Result NetlinkSocket::init() {
+ if (mSocket || mCallback) {
+ return Result::error("Netlink socket already initialized");
+ }
+ mCallback = nl_cb_alloc(NL_CB_CUSTOM);
+ if (!mCallback) {
+ return Result::error("Netlink socket failed to allocate callbacks");
+ }
+ mSocket = nl_socket_alloc_cb(mCallback);
+ if (!mSocket) {
+ return Result::error("Failed to allocate netlink socket");
+ }
+ return Result::success();
+}
+
+Result NetlinkSocket::setBufferSizes(int rxBufferSize, int txBufferSize) {
+ int res = nl_socket_set_buffer_size(mSocket, rxBufferSize, txBufferSize);
+ if (res != 0) {
+ return Result::error("Failed to set buffer sizes: %s",
+ nl_geterror(res));
+ }
+ return Result::success();
+}
+
+Result NetlinkSocket::setOnMsgInCallback(int (*callback)(struct nl_msg*, void*),
+ void* context) {
+ if (nl_cb_set(mCallback, NL_CB_MSG_IN, NL_CB_CUSTOM, callback, context)) {
+ return Result::error("Failed to set OnMsgIn callback");
+ }
+ return Result::success();
+}
+
+Result NetlinkSocket::setOnMsgOutCallback(int (*callback)(struct nl_msg*,
+ void*),
+ void* context) {
+ if (nl_cb_set(mCallback, NL_CB_MSG_OUT, NL_CB_CUSTOM, callback, context)) {
+ return Result::error("Failed to set OnMsgOut callback");
+ }
+ return Result::success();
+}
+
+Result NetlinkSocket::setOnSeqCheckCallback(int (*callback)(struct nl_msg*,
+ void*),
+ void* context) {
+ if (nl_cb_set(mCallback, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
+ callback, context)) {
+ return Result::error("Failed to set OnSeqCheck callback");
+ }
+ return Result::success();
+}
+
+Result NetlinkSocket::setOnAckCallback(int (*callback)(struct nl_msg*, void*),
+ void* context) {
+ if (nl_cb_set(mCallback, NL_CB_ACK, NL_CB_CUSTOM, callback, context)) {
+ return Result::error("Failed to set OnAck callback");
+ }
+ return Result::success();
+}
+
+Result NetlinkSocket::setOnErrorCallback(int (*callback)(struct sockaddr_nl*,
+ struct nlmsgerr*,
+ void*),
+ void* context) {
+ if (nl_cb_err(mCallback, NL_CB_CUSTOM, callback, context)) {
+ return Result::error("Failed to set OnError callback");
+ }
+ return Result::success();
+}
+
+Result NetlinkSocket::connectGeneric() {
+ int status = genl_connect(mSocket);
+ if (status < 0) {
+ return Result::error("WifiNetlinkForwarder socket connect failed: %d",
+ status);
+ }
+ return Result::success();
+}
+
+int NetlinkSocket::resolveNetlinkFamily(const char* familyName) {
+ return genl_ctrl_resolve(mSocket, familyName);
+}
+
+bool NetlinkSocket::send(NetlinkMessage& message) {
+ int status = nl_send_auto(mSocket, message.get()) >= 0;
+ if (status < 0) {
+ ALOGE("Failed to send on netlink socket: %s", nl_geterror(status));
+ return false;
+ }
+ return true;
+}
+
+bool NetlinkSocket::receive() {
+ int res = nl_recvmsgs_default(mSocket);
+ if (res != 0) {
+ ALOGE("Failed to receive messages on netlink socket: %s",
+ nl_geterror(res));
+ return false;
+ }
+ return true;
+}
+
+int NetlinkSocket::getFd() const {
+ if (!mSocket) {
+ return -1;
+ }
+ return nl_socket_get_fd(mSocket);
+}
diff --git a/network/wifi_forwarder/netlink_socket.h b/network/wifi_forwarder/netlink_socket.h
new file mode 100644
index 0000000..9ff392c
--- /dev/null
+++ b/network/wifi_forwarder/netlink_socket.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2019, 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"
+
+struct nl_cb;
+struct nl_msg;
+struct nl_sock;
+struct nlmsgerr;
+struct sockaddr_nl;
+
+class NetlinkMessage;
+
+class NetlinkSocket {
+public:
+ NetlinkSocket();
+ ~NetlinkSocket();
+
+ Result init();
+
+ /* Set the size of the receive buffer to |rxBufferSize| bytes and the
+ * transmit buffer to |txBufferSize| bytes.
+ */
+ Result setBufferSizes(int rxBufferSize, int txBufferSize);
+
+ Result setOnMsgInCallback(int (*callback)(struct nl_msg*, void*),
+ void* context);
+ Result setOnMsgOutCallback(int (*callback)(struct nl_msg*, void*),
+ void* context);
+ Result setOnSeqCheckCallback(int (*callback)(struct nl_msg*, void*),
+ void* context);
+ Result setOnAckCallback(int (*callback)(struct nl_msg*, void*),
+ void* context);
+ Result setOnErrorCallback(int (*callback)(struct sockaddr_nl*,
+ struct nlmsgerr*,
+ void*),
+ void* context);
+
+ /* Connect socket to generic netlink. This needs to be done before generic
+ * netlink messages can be sent. After this is done the caller should use
+ * @resolveGenericNetlinkFamily to determine the generic family id to use.
+ * Then create NetlinkMessage's with that family id.
+ */
+ Result connectGeneric();
+
+ /* Resolve a generic family name to a family identifier. This is used when
+ * sending generic netlink messages to indicate where the message should go.
+ * Examples of family names are "mac80211_hwsim" or "nl80211".
+ */
+ int resolveNetlinkFamily(const char* familyName);
+
+ /* Send a netlink message on this socket. */
+ bool send(NetlinkMessage& message);
+
+ /* Receive all pending message. This method does not return any messages,
+ * instead they will be provided through the callback set with
+ * setOnMsgInCallback. This callback will be called on the same thread as
+ * this method while this method is running. */
+ bool receive();
+
+ int getFd() const;
+
+private:
+ struct nl_cb* mCallback = nullptr;
+ struct nl_sock* mSocket = nullptr;
+};
+
diff --git a/network/wifi_forwarder/pollable.h b/network/wifi_forwarder/pollable.h
new file mode 100644
index 0000000..51724c0
--- /dev/null
+++ b/network/wifi_forwarder/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(Timestamp now, int* status) = 0;
+};
+
diff --git a/network/wifi_forwarder/poller.cpp b/network/wifi_forwarder/poller.cpp
new file mode 100644
index 0000000..319dff4
--- /dev/null
+++ b/network/wifi_forwarder/poller.cpp
@@ -0,0 +1,157 @@
+/*
+ * 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) {
+ 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) {
+ ALOGE("Unable to fill signal set: %s", strerror(errno));
+ return errno;
+ }
+ status = ::sigprocmask(SIG_SETMASK, &blockMask, &mask);
+ if (status != 0) {
+ ALOGE("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
+ ALOGE("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
+ ALOGE("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(now, &status)) {
+ // The onTimeout handler signaled an exit
+ return status;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/network/netmgr/timestamp.h b/network/wifi_forwarder/poller.h
similarity index 75%
rename from network/netmgr/timestamp.h
rename to network/wifi_forwarder/poller.h
index 8ad7bf8..9794f4d 100644
--- a/network/netmgr/timestamp.h
+++ b/network/wifi_forwarder/poller.h
@@ -16,18 +16,19 @@
#pragma once
-#include <time.h>
+#include <vector>
-class Timestamp {
+#include "pollable.h"
+
+class Poller {
public:
- Timestamp();
+ Poller();
- static Timestamp now();
+ void addPollable(Pollable* pollable);
- bool operator==(const Timestamp& other) const;
- bool operator<(const Timestamp& other) const;
+ int run();
private:
- struct timespec mTime;
+ std::vector<Pollable*> mPollables;
};
diff --git a/network/wifi_forwarder/remote_connection.cpp b/network/wifi_forwarder/remote_connection.cpp
new file mode 100644
index 0000000..7255e05
--- /dev/null
+++ b/network/wifi_forwarder/remote_connection.cpp
@@ -0,0 +1,335 @@
+/*
+ * Copyright 2019, 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 "remote_connection.h"
+
+#include "hwsim.h"
+#include "frame.h"
+#include "log.h"
+
+#include <errno.h>
+#include <linux/kernel.h>
+#include <netinet/in.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 <sys/uio.h>
+#include <unistd.h>
+
+static const char kQemuPipeName[] = "qemud:wififorward";
+static const size_t kReceiveBufferIncrement = 32768;
+static const size_t kReceiveBufferMaxSize = 1 << 20;
+
+static const uint8_t kWifiForwardVersion = 0x01;
+static const uint32_t kWifiForwardMagic = 0xD6C4B3A2;
+
+// This matches with the kernel constant IEEE80211_TX_MAX_RATES in
+// include/net/mac80211.h in the kernel tree.
+static const uint32_t kMaxNumRates = 4;
+
+struct WifiForwardHeader {
+ WifiForwardHeader(FrameType type, const MacAddress& transmitter,
+ uint32_t fullLength, uint64_t cookie,
+ uint32_t flags, uint32_t channel, uint32_t numRates,
+ const hwsim_tx_rate* txRates)
+ : magic(__cpu_to_le32(kWifiForwardMagic))
+ , version(kWifiForwardVersion)
+ , type(static_cast<uint16_t>(type))
+ , transmitter(transmitter)
+ , dataOffset(sizeof(WifiForwardHeader))
+ , fullLength(__cpu_to_le32(fullLength))
+ , cookie(__cpu_to_le64(cookie))
+ , flags(__cpu_to_le32(flags))
+ , channel(__cpu_to_le32(channel))
+ , numRates(__cpu_to_le32(numRates)) {
+ memcpy(rates, txRates, std::min(numRates, kMaxNumRates));
+ if (numRates < kMaxNumRates) {
+ memset(&rates[numRates],
+ 0,
+ sizeof(rates[0]) * (kMaxNumRates - numRates));
+ }
+ }
+
+ uint32_t magic;
+ uint8_t version;
+ uint8_t type;
+ MacAddress transmitter;
+ uint16_t dataOffset;
+ uint32_t fullLength;
+ uint64_t cookie;
+ uint32_t flags;
+ uint32_t channel;
+ uint32_t numRates;
+ hwsim_tx_rate rates[kMaxNumRates];
+} __attribute__((__packed__));
+
+RemoteConnection::RemoteConnection(OnFrameCallback onFrameCallback,
+ OnAckCallback onAckCallback,
+ OnErrorCallback onErrorCallback)
+ : mOnFrameCallback(onFrameCallback)
+ , mOnAckCallback(onAckCallback)
+ , mOnErrorCallback(onErrorCallback) {
+
+}
+
+RemoteConnection::~RemoteConnection() {
+ if (mPipeFd != -1) {
+ ::close(mPipeFd);
+ mPipeFd = -1;
+ }
+}
+
+Result RemoteConnection::init() {
+ if (mPipeFd != -1) {
+ return Result::error("RemoteConnetion already initialized");
+ }
+
+ mPipeFd = qemu_pipe_open(kQemuPipeName);
+ if (mPipeFd == -1) {
+ return Result::error("RemoteConnection failed to open pipe");
+ }
+ return Result::success();
+}
+
+Pollable::Timestamp RemoteConnection::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();
+}
+
+void RemoteConnection::receive() {
+ size_t start = mBuffer.size();
+ size_t newSize = start + kReceiveBufferIncrement;
+ if (newSize > kReceiveBufferMaxSize) {
+ // 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.
+ ALOGE("RemoteConnection ran out of buffer space");
+ newSize = kReceiveBufferIncrement;
+ start = 0;
+ }
+ mBuffer.resize(newSize);
+
+ while (true) {
+ int result = ::read(mPipeFd,
+ mBuffer.data() + start,
+ mBuffer.size() - start);
+ if (result < 0) {
+ if (errno == EINTR) {
+ continue;
+ }
+ ALOGE("RemoteConnection failed to read to forward buffer: %s",
+ strerror(errno));
+ // Return the buffer to its previous size
+ mBuffer.resize(start);
+ return;
+ } else if (result == 0) {
+ // Nothing received, nothing to write
+ // Return the buffer to its previous size
+ mBuffer.resize(start);
+ ALOGE("RemoteConnection did not receive anything to inject");
+ return;
+ }
+ // Adjust the buffer size to match everything we recieved
+ mBuffer.resize(start + static_cast<size_t>(result));
+ break;
+ }
+
+ while (mBuffer.size() >= sizeof(WifiForwardHeader)) {
+ auto fwd = reinterpret_cast<WifiForwardHeader*>(mBuffer.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.
+ ALOGE("RemoteConnection found incorrect magic, finding next magic");
+ uint32_t le32magic = __cpu_to_le32(kWifiForwardMagic);
+ auto next = reinterpret_cast<unsigned char*>(
+ ::memmem(mBuffer.data(), mBuffer.size(),
+ &le32magic, sizeof(le32magic)));
+ if (next) {
+ // We've found a possible candidate, erase everything before
+ size_t length = next - mBuffer.data();
+ mBuffer.erase(mBuffer.begin(), mBuffer.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 (mBuffer.size() > 3) {
+ mBuffer.erase(mBuffer.begin(), mBuffer.end() - 3);
+ }
+ // In this case there is nothing left to parse so just return
+ // right away.
+ return;
+ }
+ }
+ // Check if we support this version
+ if (fwd->version != kWifiForwardVersion) {
+ // Unsupported version
+ if (!mReportedVersionMismatches.test(fwd->version)) {
+ // Only report these once per version or this might become
+ // very spammy.
+ ALOGE("RemoteConnection encountered unknown version %u",
+ fwd->version);
+ mReportedVersionMismatches.set(fwd->version);
+ }
+ // Drop the magic from the buffer and attempt to find the next magic
+ mBuffer.erase(mBuffer.begin(), mBuffer.begin() + 4);
+ continue;
+ }
+ // The length according to the wifi forward header
+ const uint32_t fullLength = __le32_to_cpu(fwd->fullLength);
+ const uint16_t offset = __le16_to_cpu(fwd->dataOffset);
+ if (offset < sizeof(WifiForwardHeader) || offset > fullLength) {
+ // The frame offset is not large enough to go past the header
+ // or it's outside of the bounds of the length of the frame.
+ ALOGE("Invalid data offset in header %u, full length is %u",
+ offset, fullLength);
+ // Erase the magic and try again
+ mBuffer.erase(mBuffer.begin(), mBuffer.begin() + 4);
+ continue;
+ }
+ const size_t frameLength = fullLength - offset;
+
+ if (fullLength > mBuffer.size()) {
+ // We have not received enough data yet, wait for more to arrive.
+ return;
+ }
+
+ FrameType type = frameTypeFromByte(fwd->type);
+ if (frameLength == 0 && type != FrameType::Ack) {
+ ALOGE("Received empty frame for non-ack frame");
+ return;
+ }
+ unsigned char* frameData = mBuffer.data() + offset;
+ if (type == FrameType::Ack) {
+ FrameInfo info(fwd->transmitter,
+ __le64_to_cpu(fwd->cookie),
+ __le32_to_cpu(fwd->flags),
+ __le32_to_cpu(fwd->channel),
+ fwd->rates,
+ __le32_to_cpu(fwd->numRates));
+
+ if (info.flags() & HWSIM_TX_STAT_ACK) {
+ mOnAckCallback(info);
+ } else {
+ mOnErrorCallback(info);
+ }
+ } else if (type == FrameType::Data) {
+ auto frame = std::make_unique<Frame>(frameData,
+ frameLength,
+ fwd->transmitter,
+ __le64_to_cpu(fwd->cookie),
+ __le32_to_cpu(fwd->flags),
+ __le32_to_cpu(fwd->channel),
+ fwd->rates,
+ __le32_to_cpu(fwd->numRates));
+ mOnFrameCallback(std::move(frame));
+ } else {
+ ALOGE("Received unknown message type %u from remote",
+ static_cast<uint8_t>(fwd->type));
+ }
+
+ mBuffer.erase(mBuffer.begin(), mBuffer.begin() + fullLength);
+ }
+}
+
+bool RemoteConnection::sendFrame(std::unique_ptr<Frame> frame) {
+ if (mPipeFd == -1) {
+ ALOGE("RemoteConnection unable to forward data, pipe not open");
+ return false;
+ }
+
+ WifiForwardHeader header(FrameType::Data,
+ frame->transmitter(),
+ frame->size() + sizeof(WifiForwardHeader),
+ frame->cookie(),
+ frame->flags(),
+ frame->channel(),
+ frame->rates().size(),
+ frame->rates().data());
+#if 1
+ constexpr size_t count = 2;
+ struct iovec iov[count];
+ iov[0].iov_base = &header;
+ iov[0].iov_len = sizeof(header);
+ iov[1].iov_base = frame->data();
+ iov[1].iov_len = frame->size();
+
+ size_t totalSize = iov[0].iov_len + iov[1].iov_len;
+
+ size_t current = 0;
+ for (;;) {
+ ssize_t written = ::writev(mPipeFd, iov + current, count - current);
+ if (written < 0) {
+ ALOGE("RemoteConnection failed to write to pipe: %s",
+ strerror(errno));
+ return false;
+ }
+ if (static_cast<size_t>(written) == totalSize) {
+ // Optimize for most common case, everything was written
+ break;
+ }
+ totalSize -= written;
+ // Determine how much is left to write after this
+ while (current < count && written >= iov[current].iov_len) {
+ written -= iov[current++].iov_len;
+ }
+ if (current == count) {
+ break;
+ }
+ iov[current].iov_base =
+ reinterpret_cast<char*>(iov[current].iov_base) + written;
+ iov[current].iov_len -= written;
+ }
+#else
+ if (!WriteFully(mPipeFd, &header, sizeof(header))) {
+ ALOGE("RemoteConnection failed to write to pipe: %s", strerror(errno));
+ return false;
+ }
+
+ if (!WriteFully(mPipeFd, frame->data(), frame->size())) {
+ ALOGE("RemoteConnection failed to write to pipe: %s", strerror(errno));
+ return false;
+ }
+#endif
+ return true;
+}
+
+bool RemoteConnection::ackFrame(FrameInfo& info, bool success) {
+ uint32_t flags = info.flags();
+ if (success) {
+ flags |= HWSIM_TX_STAT_ACK;
+ }
+ WifiForwardHeader header(FrameType::Ack,
+ info.transmitter(),
+ sizeof(WifiForwardHeader),
+ info.cookie(),
+ flags,
+ info.channel(),
+ info.rates().size(),
+ info.rates().data());
+
+ if (!WriteFully(mPipeFd, &header, sizeof(header))) {
+ ALOGE("RemoteConnection failed to write to pipe: %s", strerror(errno));
+ return false;
+ }
+ return true;
+}
diff --git a/network/wifi_forwarder/remote_connection.h b/network/wifi_forwarder/remote_connection.h
new file mode 100644
index 0000000..94c7e36
--- /dev/null
+++ b/network/wifi_forwarder/remote_connection.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2019, 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 "frame.h"
+#include "pollable.h"
+#include "result.h"
+
+#include <bitset>
+#include <functional>
+#include <memory>
+#include <vector>
+
+struct MacAddress;
+
+class RemoteConnection {
+public:
+ using OnFrameCallback = std::function<void (std::unique_ptr<Frame>)>;
+ using OnAckCallback = std::function<void (FrameInfo&)>;
+ using OnErrorCallback = OnAckCallback;
+
+ RemoteConnection(OnFrameCallback onFrameCallback,
+ OnAckCallback onAckCallback,
+ OnErrorCallback onErrorCallback);
+ ~RemoteConnection();
+
+ Result init();
+
+ int getFd() const { return mPipeFd; }
+ Pollable::Timestamp getTimeout() const;
+ void receive();
+ bool sendFrame(std::unique_ptr<Frame> frame);
+ bool ackFrame(FrameInfo& info, bool success);
+
+private:
+ RemoteConnection(const RemoteConnection&) = delete;
+ RemoteConnection& operator=(const RemoteConnection&) = delete;
+
+ OnFrameCallback mOnFrameCallback;
+ OnAckCallback mOnAckCallback;
+ OnErrorCallback mOnErrorCallback;
+
+ Pollable::Timestamp mDeadline = Pollable::Timestamp::max();
+ std::vector<unsigned char> mBuffer;
+ int mPipeFd = -1;
+ std::bitset<256> mReportedVersionMismatches;
+};
+
diff --git a/wifi/ipv6proxy/result.h b/network/wifi_forwarder/result.h
similarity index 63%
rename from wifi/ipv6proxy/result.h
rename to network/wifi_forwarder/result.h
index 5cd2b03..5087e14 100644
--- a/wifi/ipv6proxy/result.h
+++ b/network/wifi_forwarder/result.h
@@ -15,28 +15,40 @@
*/
#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. NOTE: the data in |message| will
- // NOT be copied. It must be kept alive for as long as its intended to be
- // used. This way the object is kept light-weight.
- static Result error(const char* message) {
+ // 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; }
+ const char* c_str() const { return mMessage.c_str(); }
private:
explicit Result(bool success) : mSuccess(success) { }
- explicit Result(const char* message)
+ explicit Result(std::string message)
: mMessage(message), mSuccess(false) {
}
- const char* mMessage;
+ std::string mMessage;
bool mSuccess;
};
diff --git a/network/wifi_forwarder/wifi_forwarder.cpp b/network/wifi_forwarder/wifi_forwarder.cpp
new file mode 100644
index 0000000..b56c7cd
--- /dev/null
+++ b/network/wifi_forwarder/wifi_forwarder.cpp
@@ -0,0 +1,258 @@
+/*
+ * Copyright 2019, 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 "frame.h"
+#include "log.h"
+
+static constexpr bool kDebugTraffic = false;
+static constexpr bool kDebugBeaconTraffic = false;
+
+// How many seconds to keep aliases alive for. Since this is used to keep track
+// of randomized MAC addresses they will expire after a while. Set the value
+// pretty high to ensure that we don't accidentally lose entries just because
+// there's not a lot of frames going through.
+static constexpr auto kAliasesCacheTimeout = std::chrono::hours(8);
+
+WifiForwarder::WifiForwarder()
+ : mAliases(kAliasesCacheTimeout)
+ , mLocalConnection([this](std::unique_ptr<Frame> frame) {
+ forwardFrame(std::move(frame), RadioType::Local);
+ },
+ [this](FrameInfo& info) { onAck(info, true); },
+ [this](FrameInfo& info) { onAck(info, false); })
+ , mRemoteConnection([this](std::unique_ptr<Frame> frame) {
+ forwardFrame(std::move(frame), RadioType::Remote);
+ },
+ [this](FrameInfo& info) { onAck(info, true); },
+ [this](FrameInfo& info) { onAck(info, false); }) {
+}
+
+Result WifiForwarder::init() {
+ auto now = Pollable::Clock::now();
+ Result res = mRemoteConnection.init();
+ if (!res) {
+ // 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. If
+ // this does not succeed we don't need to perform the rest of the
+ // initialization either. Just let WiFi work the same as normal.
+ ALOGE("RemoteConnection failed to initialize: %s", res.c_str());
+ mInitDeadline = now + std::chrono::minutes(1);
+ return Result::success();
+ }
+
+ mAliases.setCurrentTime(now);
+ res = mLocalConnection.init(now);
+ if (!res) {
+ return res;
+ }
+ mDeadline = now + std::chrono::seconds(1);
+ return Result::success();
+}
+
+void WifiForwarder::getPollData(std::vector<pollfd>* fds) const {
+ int fd = mLocalConnection.getFd();
+ if (fd >= 0) {
+ struct pollfd pfd = { fd, POLLIN, 0 };
+ fds->push_back(pfd);
+ }
+ fd = mRemoteConnection.getFd();
+ if (fd >= 0) {
+ struct pollfd pfd = { fd, POLLIN, 0 };
+ fds->push_back(pfd);
+ }
+}
+
+Pollable::Timestamp WifiForwarder::getTimeout() const {
+ if (mRemoteConnection.getFd() == -1) {
+ return mInitDeadline;
+ }
+ return std::min(mLocalConnection.getTimeout(), mDeadline);
+}
+
+bool WifiForwarder::onReadAvailable(int fd, int* /*status*/) {
+ if (fd == mRemoteConnection.getFd()) {
+ mRemoteConnection.receive();
+ } else if (fd == mLocalConnection.getFd()) {
+ mLocalConnection.receive();
+ }
+ return true;
+}
+
+bool WifiForwarder::onClose(int /*fd*/, int* /*status*/) {
+ ALOGE("WifiForwarder socket closed unexpectedly");
+ return false;
+}
+
+bool WifiForwarder::onTimeout(Timestamp now, int* /*status*/) {
+ bool success = true;
+ if (now >= mInitDeadline) {
+ Result res = init();
+ success = res.isSuccess();
+ if (mRemoteConnection.getFd() == -1) {
+ // Remote connection not set up by init, try again later
+ mInitDeadline = now + std::chrono::minutes(1);
+ }
+ }
+ mLocalConnection.onTimeout(now);
+ if (now >= mDeadline) {
+ mDeadline += std::chrono::seconds(1);
+ mAliases.setCurrentTime(now);
+ mAliases.expireEntries();
+ }
+ return success;
+}
+
+const char* WifiForwarder::radioTypeToStr(RadioType type) const {
+ switch (type) {
+ case RadioType::Unknown:
+ return "Unknown";
+ case RadioType::Local:
+ return "Local";
+ case RadioType::Remote:
+ return "Remote";
+ }
+}
+
+void WifiForwarder::onAck(FrameInfo& info, bool success) {
+ RadioType type = mRadios[info.transmitter()];
+ if (type == RadioType::Remote) {
+ if (kDebugTraffic) {
+ ALOGE("] ACK -] " PRIMAC " [ %" PRIu64 " ] success: %s",
+ MACARG(info.transmitter()), info.cookie(),
+ success ? "true" : "false");
+ }
+ if (!mRemoteConnection.ackFrame(info, success)) {
+ ALOGE("WifiForwarder failed to ack remote frame");
+ }
+ } else if (type == RadioType::Local) {
+ if (kDebugTraffic) {
+ ALOGE("> ACK -> " PRIMAC " [ %" PRIu64 " ] success: %s",
+ MACARG(info.transmitter()), info.cookie(),
+ success ? "true" : "false");
+ }
+ if (!mLocalConnection.ackFrame(info, success)) {
+ ALOGE("WifiForwarder failed to ack local frame");
+ }
+ } else {
+ ALOGE("Unknown transmitter in ack: " PRIMAC,
+ MACARG(info.transmitter()));
+ }
+}
+
+void WifiForwarder::forwardFrame(std::unique_ptr<Frame> frame,
+ RadioType sourceType) {
+ if (kDebugTraffic) {
+ if (!frame->isBeacon() || kDebugBeaconTraffic) {
+ bool isRemote = sourceType == RadioType::Remote;
+ ALOGE("%c " PRIMAC " -%c " PRIMAC " %s",
+ isRemote ? '[' : '<', isRemote ? ']' : '>',
+ MACARG(frame->source()),
+ MACARG(frame->destination()),
+ frame->str().c_str());
+ }
+ }
+
+ const MacAddress& source = frame->source();
+ const MacAddress& transmitter = frame->transmitter();
+ const MacAddress& destination = frame->destination();
+ auto& currentType = mRadios[transmitter];
+ if (currentType != RadioType::Unknown && currentType != sourceType) {
+ ALOGE("Replacing type for MAC " PRIMAC " of type %s with type %s, "
+ "this might indicate duplicate MACs on different emulators",
+ MACARG(source), radioTypeToStr(currentType),
+ radioTypeToStr(sourceType));
+ }
+ currentType = sourceType;
+ mAliases[source] = transmitter;
+
+ bool isMulticast = destination.isMulticast();
+ bool sendOnRemote = isMulticast;
+ for (const auto& radio : mRadios) {
+ const MacAddress& radioAddress = radio.first;
+ RadioType radioType = radio.second;
+ if (radioAddress == transmitter) {
+ // Don't send back to the transmitter
+ continue;
+ }
+ if (sourceType == RadioType::Remote && radioType == RadioType::Remote) {
+ // Don't forward frames back to the remote, the remote will have
+ // taken care of this.
+ continue;
+ }
+ bool forward = false;
+ if (isMulticast || destination == radioAddress) {
+ // The frame is either multicast or directly intended for this
+ // radio. Forward it.
+ forward = true;
+ } else {
+ auto alias = mAliases.find(destination);
+ if (alias != mAliases.end() && alias->second == radioAddress) {
+ // The frame is destined for an address that is a known alias
+ // for this radio.
+ forward = true;
+ }
+ }
+ uint32_t seq = 0;
+ if (forward) {
+ switch (radioType) {
+ case RadioType::Unknown:
+ ALOGE("Attempted to forward frame to unknown radio type");
+ break;
+ case RadioType::Local:
+ if (kDebugTraffic) {
+ if (!frame->isBeacon() || kDebugBeaconTraffic) {
+ ALOGE("> " PRIMAC " -> " PRIMAC " %s",
+ MACARG(frame->source()),
+ MACARG(frame->destination()),
+ frame->str().c_str());
+ }
+ }
+ if (isMulticast) {
+ // Clone the frame, it might be reused
+ seq = mLocalConnection.cloneFrame(*frame, radioAddress);
+ } else {
+ // This frame has a specific destination, move it
+ seq = mLocalConnection.transferFrame(std::move(frame),
+ radioAddress);
+ // Return so that we don't accidentally reuse the frame.
+ // This should be safe now because it's a unicast frame
+ // so it should not be sent to multiple radios.
+ return;
+ }
+ break;
+ case RadioType::Remote:
+ sendOnRemote = true;
+ break;
+ }
+ }
+ }
+ if (sendOnRemote && sourceType != RadioType::Remote) {
+ if (kDebugTraffic) {
+ if (!frame->isBeacon() || kDebugBeaconTraffic) {
+ ALOGE("] " PRIMAC " -] " PRIMAC " %s",
+ MACARG(frame->source()),
+ MACARG(frame->destination()),
+ frame->str().c_str());
+ }
+ }
+ // This is either a multicast message or destined for a radio known to
+ // be a remote. No need to send multiple times to a remote, the remote
+ // will handle that on its own.
+ mRemoteConnection.sendFrame(std::move(frame));
+ }
+}
diff --git a/network/wifi_forwarder/wifi_forwarder.h b/network/wifi_forwarder/wifi_forwarder.h
new file mode 100644
index 0000000..dfe57d8
--- /dev/null
+++ b/network/wifi_forwarder/wifi_forwarder.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2019, 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 "cache.h"
+#include "frame.h"
+#include "local_connection.h"
+#include "macaddress.h"
+#include "pollable.h"
+#include "remote_connection.h"
+#include "result.h"
+
+#include <unordered_map>
+
+class WifiForwarder : public Pollable {
+public:
+ WifiForwarder();
+ virtual ~WifiForwarder() = default;
+ 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(Timestamp now, int* status) override;
+private:
+ enum class RadioType {
+ Unknown,
+ Local,
+ Remote
+ };
+
+ const char* radioTypeToStr(RadioType type) const;
+
+ void onAck(FrameInfo& info, bool success);
+
+ void forwardFrame(std::unique_ptr<Frame> frame, RadioType sourceType);
+
+ std::unordered_map<MacAddress, RadioType> mRadios;
+ Cache<MacAddress, MacAddress> mAliases;
+ LocalConnection mLocalConnection;
+ RemoteConnection mRemoteConnection;
+ Pollable::Timestamp mInitDeadline = Pollable::Timestamp::max();
+ Pollable::Timestamp mDeadline = Pollable::Timestamp::max();
+};
+
diff --git a/ril/Android.mk b/ril/Android.mk
index cb5f31a..a5423ce 100644
--- a/ril/Android.mk
+++ b/ril/Android.mk
@@ -9,6 +9,7 @@
reference-ril.c \
atchannel.c \
if_monitor.cpp \
+ ipv6_monitor.cpp \
misc.c \
at_tok.c
diff --git a/ril/if_monitor.cpp b/ril/if_monitor.cpp
index 289477d..87681f5 100644
--- a/ril/if_monitor.cpp
+++ b/ril/if_monitor.cpp
@@ -16,7 +16,9 @@
#include "if_monitor.h"
+#include <arpa/inet.h>
#include <errno.h>
+#include <ifaddrs.h>
#include <linux/rtnetlink.h>
#include <net/if.h>
#include <poll.h>
@@ -30,6 +32,7 @@
#include <mutex>
#include <thread>
#include <unordered_map>
+#include <unordered_set>
#include <vector>
#define LOG_TAG "RIL-IFMON"
@@ -56,6 +59,18 @@
}
}
+static const void* getSockAddrData(const struct sockaddr* addr) {
+ switch (addr->sa_family) {
+ case AF_INET:
+ return &reinterpret_cast<const struct sockaddr_in*>(addr)->sin_addr;
+ case AF_INET6:
+ return
+ &reinterpret_cast<const struct sockaddr_in6*>(addr)->sin6_addr;
+ default:
+ return nullptr;
+ }
+}
+
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
@@ -133,32 +148,75 @@
mThread = std::make_unique<std::thread>([this]() { run(); });
}
- void requestAddress() {
- struct {
- struct nlmsghdr hdr;
- struct ifaddrmsg msg;
- char padding[16];
- } request;
+ void requestAddresses() {
+ struct ifaddrs* addresses = nullptr;
- 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;
+ if (getifaddrs(&addresses) != 0) {
+ RLOGE("Unable to retrieve list of interfaces, cannot get initial "
+ "interface addresses: %s", strerror(errno));
+ return;
+ }
- 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);
+ for (struct ifaddrs* cur = addresses; cur; cur = cur->ifa_next) {
+ if (cur->ifa_name == nullptr ||
+ cur->ifa_addr == nullptr ||
+ cur->ifa_netmask == nullptr) {
+ // Interface doesn't have all the information we need. Rely on
+ // the netlink notification to catch this interface later if it
+ // is configured correctly.
+ continue;
+ }
+ if (cur->ifa_flags & IFF_LOOPBACK) {
+ // Not interested in loopback devices, they will never be radio
+ // interfaces.
+ continue;
+ }
+ unsigned int ifIndex = if_nametoindex(cur->ifa_name);
+ if (ifIndex == 0) {
+ RLOGE("Encountered interface %s with no index: %s",
+ cur->ifa_name, strerror(errno));
+ continue;
+ }
+ ifAddress addr;
+ addr.family = cur->ifa_addr->sa_family;
+ addr.prefix = getPrefix(cur->ifa_netmask);
+ memcpy(addr.addr,
+ getSockAddrData(cur->ifa_addr),
+ addrLength(cur->ifa_addr->sa_family));
+ mAddresses[ifIndex].push_back(addr);
+ }
+ freeifaddrs(addresses);
+
+ if (mOnAddressChangeCallback) {
+ for (const auto& ifAddr : mAddresses) {
+ mOnAddressChangeCallback(ifAddr.first,
+ ifAddr.second.data(),
+ ifAddr.second.size());
}
}
}
+ int getPrefix(const struct sockaddr* addr) {
+ // This uses popcnt, a built-in instruction on some CPUs, to count
+ // the number of bits in a 32-bit word. The number of bits in a netmask
+ // equals the width of the prefix. For example a netmask of
+ // 255.255.255.0 has 24 bits set and that's also its width.
+ if (addr->sa_family == AF_INET) {
+ auto v4 = reinterpret_cast<const struct sockaddr_in*>(addr);
+ return __builtin_popcount(v4->sin_addr.s_addr);
+ } else if (addr->sa_family == AF_INET6) {
+ auto v6 = reinterpret_cast<const struct sockaddr_in6*>(addr);
+ // Copy to our own array to avoid aliasing
+ uint64_t words[2];
+ memcpy(words, v6->sin6_addr.s6_addr, sizeof(words));
+ return __builtin_popcountll(words[0]) +
+ __builtin_popcountll(words[1]);
+ }
+ return 0;
+ }
+
void run() {
- requestAddress();
+ requestAddresses();
std::vector<struct pollfd> fds(2);
fds[0].events = POLLIN;
@@ -251,11 +309,16 @@
RLOGE("Received message type %d", (int)hdr->nlmsg_type);
break;
}
- NLMSG_NEXT(hdr, length);
+ hdr = NLMSG_NEXT(hdr, length);
}
}
}
+ std::string getInterfaceName(unsigned int ifIndex) {
+ char buffer[IF_NAMESIZE] = { '\0' };
+ return if_indextoname(ifIndex, buffer);
+ }
+
void handleAddressChange(const struct nlmsghdr* hdr) {
if (!mOnAddressChangeCallback) {
return;
diff --git a/ril/ipv6_monitor.cpp b/ril/ipv6_monitor.cpp
new file mode 100644
index 0000000..8d577bc
--- /dev/null
+++ b/ril/ipv6_monitor.cpp
@@ -0,0 +1,496 @@
+/*
+ * Copyright 2019, 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 "ipv6_monitor.h"
+
+#include <errno.h>
+#include <linux/filter.h>
+#include <net/if.h>
+#include <netinet/ether.h>
+#include <netinet/icmp6.h>
+#include <netinet/ip6.h>
+#include <poll.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <array>
+#include <mutex>
+#include <string>
+#include <thread>
+#include <unordered_set>
+#include <vector>
+
+#define LOG_TAG "RIL-IPV6MON"
+#include <utils/Log.h>
+
+static constexpr size_t kReadBufferSize = 32768;
+
+static constexpr size_t kRecursiveDnsOptHeaderSize = 8;
+
+static constexpr size_t kControlClient = 0;
+static constexpr size_t kControlServer = 1;
+
+static constexpr char kMonitorAckCommand = '\1';
+static constexpr char kMonitorStopCommand = '\2';
+
+// The amount of time to wait before trying to initialize interface again if
+// it's not ready when rild starts.
+static constexpr int kDeferredTimeoutMilliseconds = 1000;
+
+bool operator==(const in6_addr& left, const in6_addr& right) {
+ return ::memcmp(left.s6_addr, right.s6_addr, sizeof(left.s6_addr)) == 0;
+}
+
+bool operator!=(const in6_addr& left, const in6_addr& right) {
+ return ::memcmp(left.s6_addr, right.s6_addr, sizeof(left.s6_addr)) != 0;
+}
+
+template<class T>
+static 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<in6_addr> {
+ size_t operator()(const in6_addr& ad) const {
+ size_t seed = 0;
+ hash_combine(seed, *reinterpret_cast<const uint32_t*>(&ad.s6_addr[0]));
+ hash_combine(seed, *reinterpret_cast<const uint32_t*>(&ad.s6_addr[4]));
+ hash_combine(seed, *reinterpret_cast<const uint32_t*>(&ad.s6_addr[8]));
+ hash_combine(seed, *reinterpret_cast<const uint32_t*>(&ad.s6_addr[12]));
+ return seed;
+ }
+};
+} // namespace std
+
+static constexpr uint32_t kIpTypeOffset = offsetof(ip6_hdr, ip6_nxt);
+static constexpr uint32_t kIcmpTypeOffset = sizeof(ip6_hdr) +
+ offsetof(icmp6_hdr, icmp6_type);
+
+// This is BPF program that will filter out anything that is not an NDP router
+// advertisement. It's a very basic assembler syntax. The jumps indicate how
+// many instructions to jump in addition to the automatic increment of the
+// program counter. So a jump statement with a zero means to go to the next
+// instruction, a value of 3 means that the next instruction will be the 4th
+// after the current one.
+static const struct sock_filter kNdpFilter[] = {
+ // Load byte at absolute address kIpTypeOffset
+ BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kIpTypeOffset),
+ // Jump, if byte is IPPROTO_ICMPV6 jump 0 instructions, if not jump 3.
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_ICMPV6, 0, 3),
+ // Load byte at absolute address kIcmpTypeOffset
+ BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kIcmpTypeOffset),
+ // Jump, if byte is ND_ROUTER_ADVERT jump 0 instructions, if not jump 1
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ND_ROUTER_ADVERT, 0, 1),
+ // Return the number of bytes to accept, accept all of them
+ BPF_STMT(BPF_RET | BPF_K, std::numeric_limits<uint32_t>::max()),
+ // Accept zero bytes, this is where the failed jumps go
+ BPF_STMT(BPF_RET | BPF_K, 0)
+};
+static constexpr size_t kNdpFilterSize =
+ sizeof(kNdpFilter) / sizeof(kNdpFilter[0]);
+
+class Ipv6Monitor {
+public:
+ Ipv6Monitor(const char* interfaceName);
+ ~Ipv6Monitor();
+
+ enum class InitResult {
+ Error,
+ Deferred,
+ Success,
+ };
+ InitResult init();
+ void setCallback(ipv6MonitorCallback callback);
+ void runAsync();
+ void stop();
+
+private:
+ InitResult initInterfaces();
+ void run();
+ void onReadAvailable();
+
+ ipv6MonitorCallback mMonitorCallback;
+
+ in6_addr mGateway;
+ std::unordered_set<in6_addr> mDnsServers;
+
+ std::unique_ptr<std::thread> mThread;
+ std::mutex mThreadMutex;
+
+ std::string mInterfaceName;
+ int mSocketFd;
+ int mControlSocket[2];
+ int mPollTimeout = -1;
+ bool mFullyInitialized = false;
+};
+
+Ipv6Monitor::Ipv6Monitor(const char* interfaceName) :
+ mMonitorCallback(nullptr),
+ mInterfaceName(interfaceName),
+ mSocketFd(-1) {
+ memset(&mGateway, 0, sizeof(mGateway));
+ mControlSocket[0] = -1;
+ mControlSocket[1] = -1;
+}
+
+Ipv6Monitor::~Ipv6Monitor() {
+ for (int& fd : mControlSocket) {
+ if (fd != -1) {
+ ::close(fd);
+ fd = -1;
+ }
+ }
+ if (mSocketFd != -1) {
+ ::close(mSocketFd);
+ mSocketFd = -1;
+ }
+}
+
+Ipv6Monitor::InitResult Ipv6Monitor::init() {
+ if (mSocketFd != -1) {
+ RLOGE("Ipv6Monitor already initialized");
+ return InitResult::Error;
+ }
+
+ if (::socketpair(AF_UNIX, SOCK_DGRAM, 0, mControlSocket) != 0) {
+ RLOGE("Ipv6Monitor failed to create control socket pair: %s",
+ strerror(errno));
+ return InitResult::Error;
+ }
+
+ mSocketFd = ::socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, ETH_P_IPV6);
+ if (mSocketFd == -1) {
+ RLOGE("Ipv6Monitor failed to open socket: %s", strerror(errno));
+ return InitResult::Error;
+ }
+ // If interface initialization fails we'll retry later
+ return initInterfaces();
+}
+
+void Ipv6Monitor::setCallback(ipv6MonitorCallback callback) {
+ mMonitorCallback = callback;
+}
+
+Ipv6Monitor::InitResult Ipv6Monitor::initInterfaces() {
+ if (mFullyInitialized) {
+ RLOGE("Ipv6Monitor already initialized");
+ return InitResult::Error;
+ }
+ struct ifreq request;
+ memset(&request, 0, sizeof(request));
+ strlcpy(request.ifr_name, mInterfaceName.c_str(), sizeof(request.ifr_name));
+
+ // Set the ALLMULTI flag so we can capture multicast traffic
+ int status = ::ioctl(mSocketFd, SIOCGIFFLAGS, &request);
+ if (status != 0) {
+ if (errno == ENODEV) {
+ // It is not guaranteed that the network is entirely set up by the
+ // time rild has started. If that's the case the radio interface
+ // might not be up yet, try again later.
+ RLOGE("Ipv6Monitor could not initialize %s yet, retrying later",
+ mInterfaceName.c_str());
+ mPollTimeout = kDeferredTimeoutMilliseconds;
+ return InitResult::Deferred;
+ }
+ RLOGE("Ipv6Monitor failed to get interface flags for %s: %s",
+ mInterfaceName.c_str(), strerror(errno));
+ return InitResult::Error;
+ }
+
+ if ((request.ifr_flags & IFF_ALLMULTI) == 0) {
+ // The flag is not set, we have to make another call
+ request.ifr_flags |= IFF_ALLMULTI;
+
+ status = ::ioctl(mSocketFd, SIOCSIFFLAGS, &request);
+ if (status != 0) {
+ RLOGE("Ipv6Monitor failed to set interface flags for %s: %s",
+ mInterfaceName.c_str(), strerror(errno));
+ return InitResult::Error;
+ }
+ }
+
+ // Add a BPF filter to the socket so that we only receive the specific
+ // type of packet we're interested in. Otherwise we will receive ALL
+ // traffic on this interface.
+ struct sock_fprog filter;
+ filter.len = kNdpFilterSize;
+ // The API doesn't have const but it's not going to modify it so this is OK
+ filter.filter = const_cast<struct sock_filter*>(kNdpFilter);
+ status = ::setsockopt(mSocketFd,
+ SOL_SOCKET,
+ SO_ATTACH_FILTER,
+ &filter,
+ sizeof(filter));
+ if (status != 0) {
+ RLOGE("Ipv6Monitor failed to set socket filter: %s", strerror(errno));
+ return InitResult::Error;
+ }
+
+ // Get the hardware address of the interface into a sockaddr struct for bind
+ struct sockaddr_ll ethAddr;
+ memset(ðAddr, 0, sizeof(ethAddr));
+ ethAddr.sll_family = AF_PACKET;
+ ethAddr.sll_protocol = htons(ETH_P_IPV6);
+ ethAddr.sll_ifindex = if_nametoindex(mInterfaceName.c_str());
+ if (ethAddr.sll_ifindex == 0) {
+ RLOGE("Ipv6Monitor failed to find index for %s: %s",
+ mInterfaceName.c_str(), strerror(errno));
+ return InitResult::Error;
+ }
+
+ status = ::ioctl(mSocketFd, SIOCGIFHWADDR, &request);
+ if (status != 0) {
+ RLOGE("Ipv6Monitor failed to get hardware address for %s: %s",
+ mInterfaceName.c_str(), strerror(errno));
+ return InitResult::Error;
+ }
+ memcpy(ethAddr.sll_addr, request.ifr_addr.sa_data, ETH_ALEN);
+
+ // Now bind to the hardware address
+ status = ::bind(mSocketFd,
+ reinterpret_cast<const struct sockaddr*>(ðAddr),
+ sizeof(ethAddr));
+ if (status != 0) {
+ RLOGE("Ipv6Monitor failed to bind to %s hardware address: %s",
+ mInterfaceName.c_str(), strerror(errno));
+ return InitResult::Error;
+ }
+ mFullyInitialized = true;
+ return InitResult::Success;
+}
+
+void Ipv6Monitor::runAsync() {
+ std::unique_lock<std::mutex> lock(mThreadMutex);
+ mThread = std::make_unique<std::thread>([this]() { run(); });
+}
+
+void Ipv6Monitor::stop() {
+ std::unique_lock<std::mutex> lock(mThreadMutex);
+ if (!mThread) {
+ return;
+ }
+ ::write(mControlSocket[kControlClient], &kMonitorStopCommand, 1);
+ char ack = -1;
+ while (ack != kMonitorAckCommand) {
+ ::read(mControlSocket[kControlClient], &ack, sizeof(ack));
+ }
+ mThread->join();
+ mThread.reset();
+}
+
+void Ipv6Monitor::run() {
+ std::array<struct pollfd, 2> fds;
+ fds[0].events = POLLIN;
+ fds[0].fd = mControlSocket[kControlServer];
+ fds[1].events = POLLIN;
+ fds[1].fd = mSocketFd;
+
+ bool running = true;
+ while (running) {
+ int status = ::poll(fds.data(), fds.size(), mPollTimeout);
+ if (status < 0) {
+ if (errno == EINTR) {
+ // Interrupted, keep going
+ continue;
+ }
+ // An error occurred
+ RLOGE("Ipv6Monitor fatal failure polling failed; %s",
+ strerror(errno));
+ break;
+ } else if (status == 0) {
+ // Timeout, nothing to read
+ if (!mFullyInitialized) {
+ InitResult result = initInterfaces();
+ switch (result) {
+ case InitResult::Error:
+ // Something went wrong this time and we can't recover
+ running = false;
+ break;
+ case InitResult::Deferred:
+ // We need to keep waiting and then try again
+ mPollTimeout = kDeferredTimeoutMilliseconds;
+ break;
+ case InitResult::Success:
+ // Interfaces are initialized, no need to timeout again
+ mPollTimeout = -1;
+ break;
+ }
+ }
+ continue;
+ }
+
+ if (fds[0].revents & POLLIN) {
+ // Control message received
+ char command = -1;
+ if (::read(mControlSocket[kControlServer],
+ &command,
+ sizeof(command)) == 1) {
+ if (command == kMonitorStopCommand) {
+ break;
+ }
+ }
+ } else if (fds[1].revents & POLLIN) {
+ onReadAvailable();
+ }
+ }
+ ::write(mControlSocket[kControlServer], &kMonitorAckCommand, 1);
+}
+
+void Ipv6Monitor::onReadAvailable() {
+ char buffer[kReadBufferSize];
+
+ ssize_t bytesRead = 0;
+ while (true) {
+ bytesRead = ::recv(mSocketFd, buffer, sizeof(buffer), 0);
+ if (bytesRead < 0) {
+ if (errno == EINTR) {
+ // Interrupted, try again right away
+ continue;
+ }
+ if (errno != EAGAIN && errno != EWOULDBLOCK) {
+ // Do not report an error for the above error codes, they are
+ // part of the normal turn of events. We just need to try again
+ // later when we run into those errors.
+ RLOGE("Ipv6Monitor failed to receive data: %s",
+ strerror(errno));
+ }
+ return;
+ }
+ break;
+ }
+
+ if (mMonitorCallback == nullptr) {
+ // No point in doing anything, we have read the data so the socket
+ // buffer doesn't fill up and that's all we can do.
+ return;
+ }
+
+ if (static_cast<size_t>(bytesRead) < sizeof(ip6_hdr) + sizeof(icmp6_hdr)) {
+ // This message cannot be an ICMPv6 packet, ignore it
+ return;
+ }
+
+ auto ipv6 = reinterpret_cast<const ip6_hdr*>(buffer);
+ uint8_t version = (ipv6->ip6_vfc & 0xF0) >> 4;
+ if (version != 6 || ipv6->ip6_nxt != IPPROTO_ICMPV6) {
+ // This message is not an IPv6 packet or not an ICMPv6 packet, ignore it
+ return;
+ }
+
+ // The ICMP header starts right after the IPv6 header
+ auto icmp = reinterpret_cast<const icmp6_hdr*>(buffer + sizeof(ip6_hdr));
+ if (icmp->icmp6_code != 0) {
+ // All packets we care about have an icmp code of zero.
+ return;
+ }
+
+ if (icmp->icmp6_type != ND_ROUTER_ADVERT) {
+ // We only care about router advertisements
+ return;
+ }
+
+ // At this point we know it's a valid packet, let's look inside
+
+ // The gateway is the same as the source in the IP header
+ in6_addr gateway = ipv6->ip6_src;
+
+ // Search through the options for DNS servers
+ const char* options = buffer + sizeof(ip6_hdr) + sizeof(nd_router_advert);
+ const nd_opt_hdr* option = reinterpret_cast<const nd_opt_hdr*>(options);
+
+ std::vector<in6_addr> dnsServers;
+ const nd_opt_hdr* nextOpt = nullptr;
+ for (const nd_opt_hdr* opt = option; opt; opt = nextOpt) {
+ auto nextOptLoc =
+ reinterpret_cast<const char*>(opt) + opt->nd_opt_len * 8u;
+ if (nextOptLoc > buffer + bytesRead) {
+ // Not enough room for this option, abort
+ break;
+ }
+ if (nextOptLoc < buffer + bytesRead) {
+ nextOpt = reinterpret_cast<const nd_opt_hdr*>(nextOptLoc);
+ } else {
+ nextOpt = nullptr;
+ }
+ if (opt->nd_opt_type != 25 || opt->nd_opt_len < 1) {
+ // Not an RNDSS option, skip it
+ continue;
+ }
+
+ size_t numEntries = (opt->nd_opt_len - 1) / 2;
+ const char* addrLoc = reinterpret_cast<const char*>(opt);
+ addrLoc += kRecursiveDnsOptHeaderSize;
+ auto addrs = reinterpret_cast<const in6_addr*>(addrLoc);
+
+ for (size_t i = 0; i < numEntries; ++i) {
+ dnsServers.push_back(addrs[i]);
+ }
+ }
+
+ bool changed = false;
+ if (gateway != mGateway) {
+ changed = true;
+ mGateway = gateway;
+ }
+
+ for (const auto& dns : dnsServers) {
+ if (mDnsServers.find(dns) == mDnsServers.end()) {
+ mDnsServers.insert(dns);
+ changed = true;
+ }
+ }
+
+ if (changed) {
+ mMonitorCallback(&gateway, dnsServers.data(), dnsServers.size());
+ }
+}
+
+extern "C"
+struct ipv6Monitor* ipv6MonitorCreate(const char* interfaceName) {
+ auto monitor = std::make_unique<Ipv6Monitor>(interfaceName);
+ if (!monitor || monitor->init() == Ipv6Monitor::InitResult::Error) {
+ return nullptr;
+ }
+ return reinterpret_cast<struct ipv6Monitor*>(monitor.release());
+}
+
+extern "C"
+void ipv6MonitorFree(struct ipv6Monitor* ipv6Monitor) {
+ auto monitor = reinterpret_cast<Ipv6Monitor*>(ipv6Monitor);
+ delete monitor;
+}
+
+extern "C"
+void ipv6MonitorSetCallback(struct ipv6Monitor* ipv6Monitor,
+ ipv6MonitorCallback callback) {
+ auto monitor = reinterpret_cast<Ipv6Monitor*>(ipv6Monitor);
+ monitor->setCallback(callback);
+}
+
+extern "C"
+void ipv6MonitorRunAsync(struct ipv6Monitor* ipv6Monitor) {
+ auto monitor = reinterpret_cast<Ipv6Monitor*>(ipv6Monitor);
+ monitor->runAsync();
+}
+
+extern "C"
+void ipv6MonitorStop(struct ipv6Monitor* ipv6Monitor) {
+ auto monitor = reinterpret_cast<Ipv6Monitor*>(ipv6Monitor);
+ monitor->stop();
+}
+
diff --git a/ril/ipv6_monitor.h b/ril/ipv6_monitor.h
new file mode 100644
index 0000000..b58402d
--- /dev/null
+++ b/ril/ipv6_monitor.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2019, 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 <netinet/in.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ipv6Monitor;
+
+// A callback for when the IPv6 configuration changes.
+typedef void (*ipv6MonitorCallback)(const struct in6_addr* /*gateway*/,
+ const struct in6_addr* /*dns servers*/,
+ size_t /*number of dns servers */);
+
+// Create an IPv6 monitor that will monitor |interfaceName| for IPv6 router
+// advertisements. The monitor will trigger a callback if the gateway and/or
+// DNS servers provided by router advertisements change at any point.
+struct ipv6Monitor* ipv6MonitorCreate(const char* interfaceName);
+void ipv6MonitorFree(struct ipv6Monitor* monitor);
+
+void ipv6MonitorSetCallback(struct ipv6Monitor* monitor,
+ ipv6MonitorCallback callback);
+void ipv6MonitorRunAsync(struct ipv6Monitor* monitor);
+void ipv6MonitorStop(struct ipv6Monitor* monitor);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+
diff --git a/ril/reference-ril.c b/ril/reference-ril.c
index 48ad12c..8566231 100644
--- a/ril/reference-ril.c
+++ b/ril/reference-ril.c
@@ -45,11 +45,17 @@
#include <netinet/in.h>
#include "if_monitor.h"
+#include "ipv6_monitor.h"
#include "ril.h"
#define LOG_TAG "RIL"
#include <utils/Log.h>
+#define MAX(x, y) ({\
+ __typeof__(x) _x = (x); \
+ __typeof__(y) _y = (y); \
+ _x > _y ? _x : _y; })
+
static void *noopRemoveWarning( void *a ) { return a; }
#define RIL_UNUSED_PARM(a) noopRemoveWarning((void *)&(a));
@@ -273,9 +279,13 @@
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;
+// A string containing all IP addresses of the radio interface
+static char s_if_addresses[8192];
+// A string containing the IPv6 gateway of the radio interface
+static char s_ipv6_gateway[INET6_ADDRSTRLEN];
+// A string containing the IPv6 DNS servers of the radio interface
+static char s_ipv6_dns[8192];
+static pthread_mutex_t s_addresses_mutex = PTHREAD_MUTEX_INITIALIZER;
static void pollSIMState (void *param);
static void setRadioState(RIL_RadioState newState);
@@ -618,6 +628,8 @@
char propValue[PROP_VALUE_MAX];
bool hasWifi = hasWifiCapability();
const char* radioInterfaceName = getRadioInterfaceName(hasWifi);
+ char ipv6Gateway[INET6_ADDRSTRLEN];
+ char ipv6Dns[8192];
err = at_send_command_multiline ("AT+CGACT?", "+CGACT:", &p_response);
if (err != 0 || p_response->success == 0) {
@@ -735,24 +747,23 @@
if (err < 0)
goto error;
- pthread_mutex_lock(&s_ipv6_addresses_mutex);
+ pthread_mutex_lock(&s_addresses_mutex);
- // Extra space for null terminator and separating space
- int addresses_size = strlen(out) + strlen(s_ipv6_addresses) + 2;
+ // Extra space for null terminator
+ int addresses_size = MAX(strlen(out), strlen(s_if_addresses)) + 1;
responses[i].addresses = alloca(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);
+ if (*s_if_addresses) {
+ // Interface addresses exist, use them.
+ strlcpy(responses[i].addresses, s_if_addresses, addresses_size);
} else {
- // Only provide the IPv4 address
- strlcpy(responses[i].addresses,
- hasWifi ? RADIO0_IPV4_ADDRESS : out,
- addresses_size);
+ // No known interface address, use whatever the modem provided
+ strlcpy(responses[i].addresses, out, addresses_size);
}
- pthread_mutex_unlock(&s_ipv6_addresses_mutex);
+
+ strlcpy(ipv6Gateway, s_ipv6_gateway, sizeof(ipv6Gateway));
+ strlcpy(ipv6Dns, s_ipv6_dns, sizeof(ipv6Dns));
+
+ pthread_mutex_unlock(&s_addresses_mutex);
if (isInEmulator()) {
/* We are in the emulator - the dns servers are listed
@@ -769,12 +780,15 @@
int nn;
char propName[PROP_NAME_MAX];
char propValue[PROP_VALUE_MAX];
+ char* gateways = NULL;
+ size_t gatewaysSize = 0;
dnslist[0] = 0;
for (nn = 1; nn <= 4; nn++) {
/* Probe net.eth0.dns<n> */
- snprintf(propName, sizeof propName, "net.eth0.dns%d", nn);
+ snprintf(propName, sizeof propName, "net.%s.dns%d",
+ radioInterfaceName, nn);
/* Ignore if undefined */
if (property_get(propName, propValue, "") <= 0) {
@@ -788,7 +802,8 @@
}
for (nn = 1; nn <= 4; ++nn) {
/* Probe net.eth0.ipv6dns<n> for IPv6 DNS servers */
- snprintf(propName, sizeof propName, "net.eth0.ipv6dns%d", nn);
+ snprintf(propName, sizeof propName, "net.%s.ipv6dns%d",
+ radioInterfaceName, nn);
/* Ignore if undefined */
if (property_get(propName, propValue, "") <= 0) {
continue;
@@ -800,13 +815,31 @@
responses[i].dnses = dnslist;
- /* There is only one gateway in the emulator. If WiFi is
- * configured the interface visible to RIL will be behind a NAT
- * where the gateway is different. */
- if (hasWifi) {
- responses[i].gateways = "192.168.200.1";
- } else if (property_get("net.eth0.gw", propValue, "") > 0) {
- responses[i].gateways = propValue;
+ /* There is only one gateway in the emulator. */
+ snprintf(propName, sizeof propName, "net.%s.gw",
+ radioInterfaceName);
+
+ gatewaysSize = strlen(ipv6Gateway);
+ if (property_get(propName, propValue, "") > 0) {
+ if (gatewaysSize > 0) {
+ // Room for a separating space
+ ++gatewaysSize;
+ }
+ gatewaysSize += strlen(propValue);
+ }
+ if (gatewaysSize > 0) {
+ // Room for a terminating null byte
+ ++gatewaysSize;
+ responses[i].gateways = alloca(gatewaysSize);
+ if (ipv6Gateway[0]) {
+ strlcpy(responses[i].gateways, ipv6Gateway, gatewaysSize);
+ }
+ if (propValue[0]) {
+ if (responses[i].gateways[0] != '\0') {
+ strlcat(responses[i].gateways, " ", gatewaysSize);
+ }
+ strlcat(responses[i].gateways, propValue, gatewaysSize);
+ }
} else {
responses[i].gateways = "";
}
@@ -823,13 +856,14 @@
at_response_free(p_response);
- if (t != NULL)
+ if (t != NULL) {
RIL_onRequestComplete(*t, RIL_E_SUCCESS, responses,
n * sizeof(RIL_Data_Call_Response_v11));
- else
+ } else {
RIL_onUnsolicitedResponse(RIL_UNSOL_DATA_CALL_LIST_CHANGED,
responses,
n * sizeof(RIL_Data_Call_Response_v11));
+ }
return;
@@ -3950,22 +3984,18 @@
return;
}
- pthread_mutex_lock(&s_ipv6_addresses_mutex);
+ pthread_mutex_lock(&s_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);
+ s_if_addresses[0] = '\0';
+ currentLoc = s_if_addresses;
+ remaining = sizeof(s_if_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]) {
+ if (s_if_addresses[0]) {
// We've already printed something, separate them
if (remaining < 1) {
continue;
@@ -3983,7 +4013,38 @@
RLOGE("Unable to convert address to string for if %s", ifName);
}
}
- pthread_mutex_unlock(&s_ipv6_addresses_mutex);
+ pthread_mutex_unlock(&s_addresses_mutex);
+
+ // Send unsolicited call list change to notify upper layers about the new
+ // addresses
+ requestOrSendDataCallList(NULL);
+}
+
+static void onIpv6Change(const struct in6_addr* gateway,
+ const struct in6_addr* dnsServers,
+ size_t numDnsServers) {
+ char* dnsLoc = s_ipv6_dns;
+ size_t remaining = sizeof(s_ipv6_dns);
+ char address[INET6_ADDRSTRLEN];
+
+ pthread_mutex_lock(&s_addresses_mutex);
+
+ inet_ntop(AF_INET6, gateway, s_ipv6_gateway, sizeof(s_ipv6_gateway));
+
+ s_ipv6_dns[0] = '\0';
+ for (size_t i = 0; i < numDnsServers; ++i) {
+ if (inet_ntop(AF_INET6, &dnsServers[i], address, sizeof(address))) {
+ size_t len = strlcat(s_ipv6_dns, address, sizeof(s_ipv6_dns));
+ if (i + 1 < numDnsServers && len + 1 < sizeof(s_ipv6_dns)) {
+ // There's more to come and there's room for more, separate
+ // multiple DNS servers by a space.
+ s_ipv6_dns[len] = ' ';
+ s_ipv6_dns[len + 1] = '\0';
+ }
+ }
+ }
+
+ pthread_mutex_unlock(&s_addresses_mutex);
// Send unsolicited call list change to notify upper layers about the new
// addresses
@@ -3995,14 +4056,20 @@
{
int fd;
int ret;
- struct ifMonitor* monitor = ifMonitorCreate();
+ bool hasWifi = hasWifiCapability();
+ const char* radioInterfaceName = getRadioInterfaceName(hasWifi);
+ struct ifMonitor* ifMonitor = ifMonitorCreate();
+ struct ipv6Monitor* ipv6Monitor = ipv6MonitorCreate(radioInterfaceName);
AT_DUMP("== ", "entering mainLoop()", -1 );
at_set_on_reader_closed(onATReaderClosed);
at_set_on_timeout(onATTimeout);
- ifMonitorSetCallback(monitor, &onInterfaceAddressChange);
- ifMonitorRunAsync(monitor);
+ ifMonitorSetCallback(ifMonitor, &onInterfaceAddressChange);
+ ifMonitorRunAsync(ifMonitor);
+
+ ipv6MonitorSetCallback(ipv6Monitor, &onIpv6Change);
+ ipv6MonitorRunAsync(ipv6Monitor);
for (;;) {
fd = -1;
@@ -4052,8 +4119,11 @@
RLOGI("Re-opening after close");
}
- ifMonitorStop(monitor);
- ifMonitorFree(monitor);
+ ifMonitorStop(ifMonitor);
+ ifMonitorFree(ifMonitor);
+
+ ipv6MonitorStop(ipv6Monitor);
+ ipv6MonitorFree(ipv6Monitor);
return NULL;
}
diff --git a/sepolicy/common/dhcpclient.te b/sepolicy/common/dhcpclient.te
index f1ba3f0..cff4471 100644
--- a/sepolicy/common/dhcpclient.te
+++ b/sepolicy/common/dhcpclient.te
@@ -8,14 +8,14 @@
allow dhcpclient execns:fd use;
set_prop(dhcpclient, net_eth0_prop);
+set_prop(dhcpclient, net_radio0_prop);
dontaudit dhcpclient kernel:system module_request;
allow dhcpclient self:capability { net_admin net_raw };
-allow dhcpclient self:udp_socket create;
-allow dhcpclient self:netlink_route_socket { write nlmsg_write };
+allow dhcpclient self:netlink_route_socket { ioctl write nlmsg_write };
allow dhcpclient varrun_file:dir search;
allow dhcpclient self:packet_socket { create bind write read };
-allowxperm dhcpclient self:udp_socket ioctl { SIOCSIFFLAGS
- SIOCSIFADDR
- SIOCSIFNETMASK
- SIOCSIFMTU
- SIOCGIFHWADDR };
+allowxperm dhcpclient self:netlink_route_socket ioctl { SIOCGIFFLAGS
+ SIOCSIFFLAGS
+ SIOCSIFMTU
+ SIOCGIFINDEX
+ SIOCGIFHWADDR };
diff --git a/sepolicy/common/dhcprelay.te b/sepolicy/common/dhcprelay.te
new file mode 100644
index 0000000..4c4ffdf
--- /dev/null
+++ b/sepolicy/common/dhcprelay.te
@@ -0,0 +1,21 @@
+# DHCP relay
+type dhcprelay, domain;
+type dhcprelay_exec, exec_type, vendor_file_type, file_type;
+
+init_daemon_domain(dhcprelay)
+net_domain(dhcprelay)
+
+allow dhcprelay execns:fd use;
+
+set_prop(dhcprelay, net_eth0_prop);
+dontaudit dhcprelay kernel:system module_request;
+allow dhcprelay self:capability { net_admin net_bind_service net_raw };
+allow dhcprelay self:udp_socket create;
+allow dhcprelay self:netlink_route_socket { write nlmsg_write };
+allow dhcprelay varrun_file:dir search;
+allow dhcprelay self:packet_socket { create bind write read };
+allowxperm dhcprelay self:udp_socket ioctl { SIOCSIFFLAGS
+ SIOCSIFADDR
+ SIOCSIFNETMASK
+ SIOCSIFMTU
+ SIOCGIFHWADDR };
diff --git a/sepolicy/common/dhcpserver.te b/sepolicy/common/dhcpserver.te
deleted file mode 100644
index 7e8ba26..0000000
--- a/sepolicy/common/dhcpserver.te
+++ /dev/null
@@ -1,12 +0,0 @@
-# DHCP server
-type dhcpserver, domain;
-type dhcpserver_exec, exec_type, vendor_file_type, file_type;
-
-init_daemon_domain(dhcpserver)
-net_domain(dhcpserver)
-
-allow dhcpserver execns:fd use;
-
-get_prop(dhcpserver, net_eth0_prop);
-allow dhcpserver self:udp_socket { ioctl create setopt bind };
-allow dhcpserver self:capability { net_raw net_bind_service };
diff --git a/sepolicy/common/execns.te b/sepolicy/common/execns.te
index 24eee41..265fb83 100644
--- a/sepolicy/common/execns.te
+++ b/sepolicy/common/execns.te
@@ -15,9 +15,6 @@
# Allow dhcpclient to be run by execns in its own domain
domain_auto_trans(execns, dhcpclient_exec, dhcpclient);
-# Allow dhcpserver to be run by execns in its own domain
-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);
diff --git a/sepolicy/common/file_contexts b/sepolicy/common/file_contexts
index 117a59f..240bc49 100644
--- a/sepolicy/common/file_contexts
+++ b/sepolicy/common/file_contexts
@@ -23,12 +23,12 @@
/vendor/bin/qemu-props u:object_r:qemu_props_exec:s0
/vendor/bin/createns u:object_r:createns_exec:s0
/vendor/bin/execns u:object_r:execns_exec:s0
-/vendor/bin/ipv6proxy u:object_r:ipv6proxy_exec:s0
+/vendor/bin/ip u:object_r:goldfish_ip_exec:s0
/vendor/bin/iw u:object_r:goldfish_iw_exec:s0
/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/wifi_forwarder u:object_r:wifi_forwarder_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\.2-service\.widevine u:object_r:hal_drm_widevine_exec:s0
diff --git a/sepolicy/common/goldfish_ip.te b/sepolicy/common/goldfish_ip.te
new file mode 100644
index 0000000..c914596
--- /dev/null
+++ b/sepolicy/common/goldfish_ip.te
@@ -0,0 +1,5 @@
+type goldfish_ip, domain;
+type goldfish_ip_exec, exec_type, vendor_file_type, file_type;
+
+init_daemon_domain(goldfish_ip)
+net_domain(goldfish_ip)
diff --git a/sepolicy/common/goldfish_setup.te b/sepolicy/common/goldfish_setup.te
index 187d055..e3071f7 100644
--- a/sepolicy/common/goldfish_setup.te
+++ b/sepolicy/common/goldfish_setup.te
@@ -14,6 +14,7 @@
wakelock_use(goldfish_setup);
allow goldfish_setup vendor_shell_exec:file { rx_file_perms };
#============= goldfish_setup ==============
+allow goldfish_setup goldfish_ip_exec:file execute_no_trans;
allow goldfish_setup goldfish_iw_exec:file execute_no_trans;
# Set system properties to start services
@@ -33,10 +34,6 @@
allow goldfish_setup kernel:system module_request;
set_prop(goldfish_setup, qemu_prop);
get_prop(goldfish_setup, net_share_prop);
-# Allow goldfish_setup to run /system/bin/ip and /system/bin/iw
-# TODO(b/113124961): clean up this Treble violation.
-typeattribute goldfish_setup vendor_executes_system_violators;
-allow goldfish_setup system_file:file rx_file_perms;
# Allow goldfish_setup to run init.wifi.sh
allow goldfish_setup goldfish_setup_exec:file execute_no_trans;
#Allow goldfish_setup to run createns in its own domain
@@ -52,4 +49,5 @@
# 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;
-allow goldfish_setup system_file:file { execute getattr open read };
+#allow goldfish_setup system_file:file { execute getattr open read };
+dontaudit goldfish_setup self:capability dac_override;
diff --git a/sepolicy/common/ipv6proxy.te b/sepolicy/common/ipv6proxy.te
deleted file mode 100644
index a631501..0000000
--- a/sepolicy/common/ipv6proxy.te
+++ /dev/null
@@ -1,18 +0,0 @@
-# IPv6 proxying
-type ipv6proxy, domain;
-type ipv6proxy_exec, exec_type, vendor_file_type, file_type;
-
-init_daemon_domain(ipv6proxy)
-net_domain(ipv6proxy)
-
-# Allow ipv6proxy to be run by execns in its own domain
-domain_auto_trans(execns, ipv6proxy_exec, ipv6proxy);
-allow ipv6proxy execns:fd use;
-
-set_prop(ipv6proxy, net_eth0_prop);
-dontaudit ipv6proxy kernel:system module_request;
-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;
-allow ipv6proxy varrun_file:dir search;
-allowxperm ipv6proxy self:udp_socket ioctl { SIOCSIFFLAGS SIOCGIFHWADDR };
diff --git a/sepolicy/common/netmgr.te b/sepolicy/common/netmgr.te
index 96caf82..c7abcb9 100644
--- a/sepolicy/common/netmgr.te
+++ b/sepolicy/common/netmgr.te
@@ -7,25 +7,31 @@
allow netmgr execns:fd use;
+# Set property to indicate bridging is complete
+set_prop(netmgr, vendor_net);
# 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:file create_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:socket { create };
+allow netmgr self:unix_dgram_socket ioctl;
+allow netmgr self:packet_socket { ioctl getopt map };
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:unix_dgram_socket ioctl { SIOCETHTOOL };
+allowxperm netmgr self:udp_socket ioctl { SIOCSIFFLAGS
+ SIOCBRADDBR
+ SIOCBRADDIF
+ SIOCBRDELIF };
allowxperm netmgr self:packet_socket ioctl { SIOCGIFINDEX SIOCGIFHWADDR };
-# Allow netmgr to run iptables to block and unblock network traffic
-# TODO(b/113124961): clean up this Treble violation.
-typeattribute netmgr vendor_executes_system_violators;
-allow netmgr system_file:file execute_no_trans;
-allow netmgr system_file:file lock;
+# Allow netmgr to run ip and modify route table to block unblock traffic
+allow netmgr goldfish_ip_exec:file execute_no_trans;
+allow netmgr self:netlink_route_socket nlmsg_write;
# Packet socket for wifi forwarding
allow netmgr self:packet_socket { bind create read setopt write };
+allow netmgr kernel:system module_request;
+allow netmgr self:capability sys_module;
diff --git a/sepolicy/common/property.te b/sepolicy/common/property.te
index 3593a39..4046105 100644
--- a/sepolicy/common/property.te
+++ b/sepolicy/common/property.te
@@ -2,4 +2,6 @@
type qemu_cmdline, property_type;
type radio_noril_prop, property_type;
type net_eth0_prop, property_type;
+type net_radio0_prop, property_type;
type net_share_prop, property_type;
+type vendor_net, property_type;
diff --git a/sepolicy/common/property_contexts b/sepolicy/common/property_contexts
index 4a82974..86f0114 100644
--- a/sepolicy/common/property_contexts
+++ b/sepolicy/common/property_contexts
@@ -1,9 +1,11 @@
qemu. u:object_r:qemu_prop:s0
qemu.cmdline u:object_r:qemu_cmdline:s0
vendor.qemu u:object_r:qemu_prop:s0
+vendor.network u:object_r:vendor_net:s0
ro.emu. u:object_r:qemu_prop:s0
ro.emulator. u:object_r:qemu_prop:s0
ro.radio.noril u:object_r:radio_noril_prop:s0
net.eth0. u:object_r:net_eth0_prop:s0
+net.radio0. u:object_r:net_radio0_prop:s0
net.shared_net_ip u:object_r:net_share_prop:s0
ro.zygote.disable_gl_preload u:object_r:qemu_prop:s0
diff --git a/sepolicy/common/radio.te b/sepolicy/common/radio.te
index 742d3b2..38faf6f 100644
--- a/sepolicy/common/radio.te
+++ b/sepolicy/common/radio.te
@@ -1,3 +1,4 @@
# Allow the radio to read these properties, they only have an SELinux label in
# the emulator.
get_prop(radio, net_eth0_prop);
+allow radio net_radio0_prop:file { getattr read open map };
diff --git a/sepolicy/common/rild.te b/sepolicy/common/rild.te
index ea18373..c17fa5e 100644
--- a/sepolicy/common/rild.te
+++ b/sepolicy/common/rild.te
@@ -1,3 +1,10 @@
# Allow rild to read these properties, they only have an SELinux label in the
# emulator.
get_prop(rild, net_eth0_prop);
+get_prop(rild, net_radio0_prop);
+
+# IPv6 router advertisement detection
+allow rild self:packet_socket { bind create ioctl read setopt };
+allowxperm rild self:packet_socket ioctl { SIOCGIFFLAGS
+ SIOCSIFFLAGS
+ SIOCGIFHWADDR };
diff --git a/sepolicy/common/wifi_forwarder.te b/sepolicy/common/wifi_forwarder.te
new file mode 100644
index 0000000..3eb7bba
--- /dev/null
+++ b/sepolicy/common/wifi_forwarder.te
@@ -0,0 +1,11 @@
+# Wifi forwarder
+type wifi_forwarder, domain;
+type wifi_forwarder_exec, exec_type, vendor_file_type, file_type;
+
+init_daemon_domain(wifi_forwarder)
+net_domain(wifi_forwarder)
+
+allow wifi_forwarder self:capability { net_admin };
+# Generic netlink socket for wifi forwarding
+allow wifi_forwarder self:netlink_generic_socket { bind create getattr setopt read write };
+
diff --git a/tnc/Android.bp b/tnc/Android.bp
new file mode 100644
index 0000000..fb0236f
--- /dev/null
+++ b/tnc/Android.bp
@@ -0,0 +1,32 @@
+//
+// 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: "tnc",
+ vendor: true,
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ srcs: [
+ "main.cpp",
+ ],
+ shared_libs: [
+ "libcutils",
+ "liblog",
+ ],
+}
+
diff --git a/tnc/main.cpp b/tnc/main.cpp
new file mode 100644
index 0000000..d6ffd11
--- /dev/null
+++ b/tnc/main.cpp
@@ -0,0 +1,217 @@
+
+#include <errno.h>
+#include <netdb.h>
+#include <net/if.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <initializer_list>
+
+static void usage(const char* program) {
+ fprintf(stderr, "Usage: %s [-s|-c|-b] <ip> <port>\n", program);
+}
+
+enum class Mode {
+ Bridge,
+ Client,
+ Server,
+};
+
+bool resolve(const char* name, const char* port, struct addrinfo** addrs) {
+ struct addrinfo hints;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM;
+
+ int res = ::getaddrinfo(name, port, &hints, addrs);
+ if (res != 0) {
+ fprintf(stderr, "ERROR: Unable to resolve '%s' and port '%s': %s\n",
+ name, port, gai_strerror(res));
+ return false;
+ }
+ return true;
+}
+
+int runClient(struct addrinfo* addrs) {
+ int fd = -1;
+ for (struct addrinfo* addr = addrs; addr != nullptr; addr = addr->ai_next) {
+ fd = ::socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
+ if (fd < 0) {
+ continue;
+ }
+ if (::connect(fd, addr->ai_addr, addr->ai_addrlen) == 0) {
+ break;
+ }
+ ::close(fd);
+ }
+ ::freeaddrinfo(addrs);
+ if (fd < 0) {
+ fprintf(stderr, "Unable to connect to server\n");
+ return 1;
+ }
+ if (::send(fd, "boop", 4, 0) != 4) {
+ ::close(fd);
+ fprintf(stderr, "Failed to send message to server\n");
+ return 1;
+ }
+ ::close(fd);
+ return 0;
+}
+
+int runServer(struct addrinfo* addrs) {
+ int fd = -1;
+ for (struct addrinfo* addr = addrs; addr != nullptr; addr = addr->ai_next) {
+ fd = ::socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
+ if (fd < 0) {
+ continue;
+ }
+ if (::bind(fd, addr->ai_addr, addr->ai_addrlen) == 0) {
+ break;
+ }
+ ::close(fd);
+ }
+ ::freeaddrinfo(addrs);
+ if (fd < 0) {
+ fprintf(stderr, "Unable to bind to address\n");
+ return 1;
+ }
+ char buffer[1024];
+ for (;;) {
+ struct sockaddr_storage addr;
+ socklen_t addrSize = sizeof(addr);
+ ssize_t bytesRead = recvfrom(fd, buffer, sizeof(buffer), 0,
+ reinterpret_cast<struct sockaddr*>(&addr),
+ &addrSize);
+ if (bytesRead < 0) {
+ if (errno == EINTR) {
+ continue;
+ }
+ fprintf(stderr, "Error receiving on socket: %s\n", strerror(errno));
+ ::close(fd);
+ return 1;
+ } else if (bytesRead == 0) {
+ fprintf(stderr, "Socket unexpectedly closed\n");
+ ::close(fd);
+ return 1;
+ }
+ printf("Received message from client '%*s'\n",
+ static_cast<int>(bytesRead), buffer);
+ }
+}
+
+static const char kBridgeName[] = "br0";
+
+static int configureBridge() {
+ int fd = ::socket(AF_LOCAL, SOCK_STREAM, 0);
+ if (fd < 0) {
+ fprintf(stderr, "ERROR: Could not open bridge socket: %s\n",
+ strerror(errno));
+ return 1;
+ }
+
+ int res = ::ioctl(fd, SIOCBRADDBR, kBridgeName);
+ if (res < 0) {
+ fprintf(stderr, "ERROR: cannot create bridge: %s\n", strerror(errno));
+ ::close(fd);
+ return 1;
+ }
+
+ for (const auto& ifName : { "eth0", "wlan1", "radio0-peer" }) {
+ struct ifreq request;
+ memset(&request, 0, sizeof(request));
+ request.ifr_ifindex = if_nametoindex(ifName);
+ if (request.ifr_ifindex == 0) {
+ fprintf(stderr, "ERROR: Unable to get interface index for %s\n",
+ ifName);
+ ::close(fd);
+ return 1;
+ }
+ strlcpy(request.ifr_name, kBridgeName, sizeof(request.ifr_name));
+ res = ::ioctl(fd, SIOCBRADDIF, &request);
+ if (res < 0) {
+ fprintf(stderr, "ERROR: cannot add if %s to bridge: %s\n",
+ ifName, strerror(errno));
+ ::close(fd);
+ return 1;
+ }
+ }
+
+ struct ifreq request;
+ memset(&request, 0, sizeof(request));
+ request.ifr_ifindex = if_nametoindex(kBridgeName);
+ if (request.ifr_ifindex == 0) {
+ fprintf(stderr, "ERROR: Unable to get interface index for %s\n",
+ kBridgeName);
+ ::close(fd);
+ return 1;
+ }
+ strlcpy(request.ifr_name, kBridgeName, sizeof(request.ifr_name));
+ res = ::ioctl(fd, SIOCGIFFLAGS, &request);
+ if (res != 0) {
+ fprintf(stderr, "ERROR: Unable to get interface index for %s\n",
+ kBridgeName);
+ ::close(fd);
+ return 1;
+ }
+ if ((request.ifr_flags & IFF_UP) == 0) {
+ // Bridge is not up, it needs to be up to work
+ request.ifr_flags |= IFF_UP;
+ res = ::ioctl(fd, SIOCSIFFLAGS, &request);
+ if (res != 0) {
+ fprintf(stderr, "ERROR: Unable to set interface flags for %s\n",
+ kBridgeName);
+ ::close(fd);
+ return 1;
+ }
+ }
+
+ ::close(fd);
+ return 0;
+}
+
+int main(int argc, char* argv[]) {
+ if (argc < 2) {
+ usage(argv[0]);
+ return 1;
+ }
+
+ Mode mode;
+ if (strcmp("-b", argv[1]) == 0) {
+ mode = Mode::Bridge;
+ } else if (strcmp("-c", argv[1]) == 0) {
+ mode = Mode::Client;
+ } else if (strcmp("-s", argv[1]) == 0) {
+ mode = Mode::Server;
+ } else {
+ fprintf(stderr, "ERROR: Invalid option '%s'\n", argv[1]);
+ usage(argv[0]);
+ return 1;
+ }
+
+ struct addrinfo* addrs = nullptr;
+ if (mode == Mode::Client || mode == Mode::Server) {
+ if (argc != 4) {
+ usage(argv[0]);
+ return 1;
+ }
+ if (!resolve(argv[2], argv[3], &addrs)) {
+ usage(argv[0]);
+ return 1;
+ }
+ }
+
+ switch (mode) {
+ case Mode::Bridge:
+ return configureBridge();
+ case Mode::Client:
+ return runClient(addrs);
+ case Mode::Server:
+ return runServer(addrs);
+ }
+}
+
diff --git a/vendor.mk b/vendor.mk
index 16dcbf9..fe4f6a8 100644
--- a/vendor.mk
+++ b/vendor.mk
@@ -86,6 +86,7 @@
android.hardware.wifi@1.0-service \
android.hardware.biometrics.fingerprint@2.1-service \
sh_vendor \
+ ip_vendor \
iw_vendor \
audio.r_submix.default \
local_time.default \
@@ -145,12 +146,11 @@
PRODUCT_PACKAGES += \
createns \
dhcpclient \
- dhcpserver \
execns \
hostapd \
hostapd_nohidl \
- ipv6proxy \
netmgr \
+ wifi_forwarder \
wpa_supplicant \
PRODUCT_PACKAGES += android.hardware.thermal@2.0-service.mock
diff --git a/wifi/createns/createns.cpp b/wifi/createns/createns.cpp
index 66f6dd3..95aff53 100644
--- a/wifi/createns/createns.cpp
+++ b/wifi/createns/createns.cpp
@@ -103,8 +103,8 @@
path += ".pid";
Fd fd(::open(path.c_str(),
- O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC,
- S_IRUSR | S_IRGRP | S_IROTH));
+ O_CREAT | O_TRUNC | O_WRONLY | O_CLOEXEC,
+ S_IRUSR | S_IWUSR | S_IRGRP));
if (fd.get() == -1) {
ALOGE("Unable to create file '%s': %s", path.c_str(), strerror(errno));
return false;
@@ -223,6 +223,7 @@
ALOGE("Failed to create pipe: %s", strerror(errno));
return 1;
}
+
Fd readPipe(fds[0]);
Fd writePipe(fds[1]);
@@ -239,7 +240,8 @@
}
{
// Open and then immediately close the fd
- Fd fd(::open(path.c_str(), O_CREAT | O_EXCL | O_RDONLY | O_CLOEXEC, 0));
+ Fd fd(::open(path.c_str(), O_CREAT | O_TRUNC | O_RDONLY | O_CLOEXEC,
+ S_IRUSR | S_IWUSR | S_IRGRP));
if (fd.get() == -1) {
ALOGE("Failed to open file %s: %s", path.c_str(), strerror(errno));
return 1;
diff --git a/wifi/init.wifi.sh b/wifi/init.wifi.sh
index ff64e91..fd84d12 100755
--- a/wifi/init.wifi.sh
+++ b/wifi/init.wifi.sh
@@ -42,19 +42,35 @@
#
NAMESPACE="router"
-rm -rf /data/vendor/var/run/netns/${NAMESPACE}
-rm -rf /data/vendor/var/run/netns/${NAMESPACE}.pid
-# 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}
+# 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=$(</data/vendor/var/run/netns/${NAMESPACE}.pid)
+
+/vendor/bin/ip link set eth0 netns ${PID}
+
+/vendor/bin/ip link add radio0 type veth peer name radio0-peer netns ${PID}
+
+# Enable privacy addresses for radio0, this is done by the framework for wlan0
+sysctl -wq net.ipv6.conf.radio0.use_tempaddr=2
+
+execns ${NAMESPACE} /vendor/bin/ip link set radio0-peer up
+
+execns ${NAMESPACE} /vendor/bin/ip link set eth0 up
+
+/vendor/bin/ip link set radio0 up
+
+execns ${NAMESPACE} /vendor/bin/ip link set wlan1 up
+
+/vendor/bin/iw phy phy1 set netns $PID
+
+setprop ctl.start netmgr
+
+setprop ctl.start wifi_forwarder
+
# 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.
@@ -64,48 +80,5 @@
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}
-# Enable privacy addresses for radio0, this is done by the framework for wlan0
-sysctl -wq net.ipv6.conf.radio0.use_tempaddr=2
-/system/bin/ip addr add 192.168.200.2/24 broadcast 192.168.200.255 dev radio0
-execns ${NAMESPACE} /system/bin/ip addr add 192.168.200.1/24 dev radio0-peer
-execns ${NAMESPACE} sysctl -wq net.ipv6.conf.all.forwarding=1
-execns ${NAMESPACE} /system/bin/ip link set radio0-peer up
-# Start the dhcp client for eth0 to acquire an address
-setprop ctl.start dhcpclient_rtr
-# Create iptables entries. -w will cause an indefinite wait for the exclusive
-# lock. Without this flag iptables can sporadically fail if something else is
-# modifying the iptables at the same time. -W indicates the number of micro-
-# seconds between each retry. The default is one second which seems like a long
-# time. Keep this short so we don't slow down startup too much.
-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
-/vendor/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
-execns ${NAMESPACE} sysctl -wq net.ipv4.ip_forward=1
# Start hostapd, the access point software
setprop ctl.start emu_hostapd
-# Start DHCP server for the wifi interface
-setprop ctl.start dhcpserver
-/system/bin/ip link set radio0 up
diff --git a/wifi/ipv6proxy/Android.mk b/wifi/ipv6proxy/Android.mk
deleted file mode 100644
index a1695d6..0000000
--- a/wifi/ipv6proxy/Android.mk
+++ /dev/null
@@ -1,24 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
- address.cpp \
- interface.cpp \
- log.cpp \
- main.cpp \
- namespace.cpp \
- packet.cpp \
- proxy.cpp \
- router.cpp \
- socket.cpp \
-
-
-LOCAL_CPPFLAGS += -Werror
-LOCAL_SHARED_LIBRARIES := libcutils liblog
-LOCAL_PROPRIETARY_MODULE := true
-LOCAL_MODULE := ipv6proxy
-
-LOCAL_MODULE_CLASS := EXECUTABLES
-
-include $(BUILD_EXECUTABLE)
diff --git a/wifi/ipv6proxy/address.cpp b/wifi/ipv6proxy/address.cpp
deleted file mode 100644
index def9c7b..0000000
--- a/wifi/ipv6proxy/address.cpp
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * 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.
- */
-
-#include "address.h"
-
-#include <arpa/inet.h>
-#include <errno.h>
-#include <linux/if_ether.h>
-#include <linux/if_packet.h>
-#include <netdb.h>
-#include <net/if.h>
-#include <string.h>
-#include <sys/ioctl.h>
-
-#include "socket.h"
-
-std::string addrToStr(const struct in6_addr& addr) {
- char buf[INET6_ADDRSTRLEN];
- if (inet_ntop(AF_INET6, &addr, buf, sizeof(buf)) == nullptr) {
- return "[unknown]";
- }
- return buf;
-}
-
-Address::Address() {
- mStorage.reserve(sizeof(struct sockaddr_storage));
-}
-
-Address::Address(const struct sockaddr_nl& address)
- : mStorage(sizeof(address)) {
- memcpy(mStorage.data(), &address, sizeof(address));
-}
-Address::Address(const struct sockaddr_in6& address)
- : mStorage(sizeof(address)) {
- memcpy(mStorage.data(), &address, sizeof(address));
-}
-
-Address::Address(struct in6_addr address)
- : mStorage(sizeof(struct sockaddr_in6)) {
- auto sockaddr = reinterpret_cast<struct sockaddr_in6*>(mStorage.data());
- sockaddr->sin6_family = AF_INET6;
- sockaddr->sin6_addr = address;
-}
-
-void Address::reset() {
- mStorage.resize(sizeof(struct sockaddr_storage));
-}
-
-Result Address::resolveInet(const std::string& address) {
- struct addrinfo hints;
- memset(&hints, 0, sizeof(hints));
- hints.ai_family = AF_INET6;
- hints.ai_socktype = SOCK_RAW;
- struct addrinfo* addrinfo;
- int res = ::getaddrinfo(address.c_str(), nullptr, &hints, &addrinfo);
- if (res != 0) {
- return Result::error(gai_strerror(res));
- }
- mStorage.resize(addrinfo->ai_addrlen);
- memcpy(mStorage.data(), addrinfo->ai_addr, mStorage.size());
- freeaddrinfo(addrinfo);
-
- return Result::success();
-}
-
-Result Address::resolveEth(const std::string& interfaceName) {
- mStorage.resize(sizeof(struct sockaddr_ll));
- memset(mStorage.data(), 0, mStorage.size());
- auto addr = reinterpret_cast<struct sockaddr_ll*>(mStorage.data());
- addr->sll_family = AF_PACKET;
- addr->sll_protocol = htons(ETH_P_IPV6);
-
- unsigned int index = if_nametoindex(interfaceName.c_str());
- if (index == 0) {
- return Result::error(strerror(errno));
- }
- addr->sll_ifindex = index;
-
- struct ifreq request;
- memset(&request, 0, sizeof(request));
- strncpy(request.ifr_name, interfaceName.c_str(), sizeof(request.ifr_name));
- request.ifr_name[sizeof(request.ifr_name) - 1] = '\0';
-
- Socket socket;
- Result res = socket.open(AF_INET, SOCK_DGRAM, IPPROTO_IP);
- if (!res) {
- return res;
- }
- int status = ::ioctl(socket.get(), SIOCGIFHWADDR, &request);
- if (status != 0) {
- return Result::error(strerror(errno));
- }
- memcpy(addr->sll_addr, request.ifr_addr.sa_data, 6);
-
- return Result::success();
-}
diff --git a/wifi/ipv6proxy/address.h b/wifi/ipv6proxy/address.h
deleted file mode 100644
index f9cec0b..0000000
--- a/wifi/ipv6proxy/address.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * 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 "result.h"
-
-#include <linux/netlink.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-
-#include <string>
-#include <vector>
-
-// Convert an IPv6 address struct to a string for debugging purposes
-std::string addrToStr(const struct in6_addr& addr);
-
-// Represents any kind of address in a struct sockaddr.
-class Address {
-public:
- Address();
- explicit Address(const struct sockaddr_nl& address);
- explicit Address(const struct sockaddr_in6& address);
- explicit Address(struct in6_addr address);
-
- template<typename T>
- const T* get() const {
- return reinterpret_cast<const T*>(mStorage.data());
- }
-
- template<typename T>
- T* get() {
- return reinterpret_cast<T*>(mStorage.data());
- }
-
- ssize_t size() const { return mStorage.size(); }
-
- // Reset the address to be the max size possible for an address
- void reset();
-
- // Resolve |address| into an IPv6 address. |address| may be either a domain
- // name or just a string containing a numeric address.
- Result resolveInet(const std::string& address);
- // Resolve |interfaceName| into a link layer address. This can be used to
- // create a struct sockaddr_nl that can be used to listen on the given
- // interface at the link layer.
- Result resolveEth(const std::string& interfaceName);
-private:
- std::vector<char> mStorage;
-};
-
diff --git a/wifi/ipv6proxy/interface.cpp b/wifi/ipv6proxy/interface.cpp
deleted file mode 100644
index e9597ab..0000000
--- a/wifi/ipv6proxy/interface.cpp
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * 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.
- */
-
-#include "interface.h"
-
-#include <errno.h>
-#include <linux/if_ether.h>
-#include <net/if.h>
-#include <string.h>
-#include <sys/ioctl.h>
-
-#include "log.h"
-
-Interface::Interface(const std::string& name) : mName(name) {
-}
-
-bool Interface::init() {
- // setAllMulti will set the ALLMULTI flag for the interface, this allows us
- // to capture all the traffic needed to perform proxying.
- return setAllMulti() &&
- resolveAddresses() &&
- configureIpSocket() &&
- configureIcmpSocket();
-}
-
-bool Interface::setAllMulti() {
- struct ifreq request;
- memset(&request, 0, sizeof(request));
- strncpy(request.ifr_name, mName.c_str(), sizeof(request.ifr_name));
- request.ifr_name[sizeof(request.ifr_name) - 1] = '\0';
-
- Socket socket;
- Result res = socket.open(AF_INET, SOCK_DGRAM, IPPROTO_IP);
- if (!res) {
- loge("Failed to open IP socket for interface %s: %s\n",
- mName.c_str(), strerror(errno));
- return false;
- }
- int status = ::ioctl(socket.get(), SIOCGIFFLAGS, &request);
- if (status != 0) {
- loge("Failed to get interface flags for %s: %s\n",
- mName.c_str(), strerror(errno));
- return false;
- }
-
- if ((request.ifr_flags & IFF_ALLMULTI) != 0) {
- // AllMulti is already enabled, nothing to do
- return true;
- }
-
- request.ifr_flags |= IFF_ALLMULTI;
-
- status = ::ioctl(socket.get(), SIOCSIFFLAGS, &request);
- if (status != 0) {
- loge("Failed to enable AllMulti flag for %s: %s\n",
- mName.c_str(), strerror(errno));
- return false;
- }
- return true;
-}
-
-bool Interface::resolveAddresses() {
- Result res = mLinkAddr.resolveEth(mName);
- if (!res) {
- loge("Unable to resolve interface %s: %s\n",
- mName.c_str(), res.c_str());
- return false;
- }
- mIndex = if_nametoindex(mName.c_str());
- if (mIndex == 0) {
- loge("Unable to get interface index for '%s': %s\n",
- mName.c_str(), strerror(errno));
- return false;
- }
- return true;
-}
-
-bool Interface::configureIcmpSocket() {
- Result res = mIcmpSocket.open(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
- if (!res) {
- loge("Error opening socket: %s\n", res.c_str());
- return false;
- }
-
- // The ICMP messages we are going to send need a hop limit of 255 to be
- // accepted.
- res = mIcmpSocket.setMulticastHopLimit(255);
- if (!res) {
- loge("Error setting socket hop limit: %s\n", res.c_str());
- return false;
- }
- res = mIcmpSocket.setUnicastHopLimit(255);
- if (!res) {
- loge("Error setting socket hop limit: %s\n", res.c_str());
- return false;
- }
-
- // We only care about one specific interface
- res = mIcmpSocket.setInterface(mName);
- if (!res) {
- loge("Error socket interface: %s\n", res.c_str());
- return false;
- }
-
- // Make sure the socket allows transparent proxying, this allows sending of
- // packets with a source address that is different from the interface.
- res = mIcmpSocket.setTransparent(true);
- if (!res) {
- loge("Error socket interface: %s\n", res.c_str());
- return false;
- }
-
- return true;
-}
-
-bool Interface::configureIpSocket() {
- Result res = mIpSocket.open(AF_PACKET, SOCK_DGRAM, ETH_P_IPV6);
- if (!res) {
- loge("Error opening socket: %s\n", res.c_str());
- return false;
- }
-
- res = mIpSocket.bind(mLinkAddr);
- if (!res) {
- loge("Error binding socket: %s\n", res.c_str());
- return false;
- }
- return true;
-}
-
diff --git a/wifi/ipv6proxy/interface.h b/wifi/ipv6proxy/interface.h
deleted file mode 100644
index e6e5d78..0000000
--- a/wifi/ipv6proxy/interface.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * 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 <stdint.h>
-#include <string>
-
-#include "address.h"
-#include "socket.h"
-
-// A class that contains the information used by the proxy that is specific to a
-// single interface. This includes the name and index of the interface as well
-// as the sockets used to send and receive data on the interface.
-//
-// The class also contains the functionality needed to initialized and configure
-// the interface, sockets and addresses.
-class Interface {
-public:
- explicit Interface(const std::string& name);
-
- bool init();
-
- const std::string& name() const { return mName; }
- uint32_t index() const { return mIndex; }
- Socket& ipSocket() { return mIpSocket; }
- Socket& icmpSocket() { return mIcmpSocket; }
- const Address& linkAddr() const { return mLinkAddr; }
-
-private:
- bool setAllMulti();
- bool resolveAddresses();
- bool configureIcmpSocket();
- bool configureIpSocket();
-
- std::string mName;
- uint32_t mIndex;
- Socket mIpSocket;
- Socket mIcmpSocket;
- Address mLinkAddr;
-};
-
diff --git a/wifi/ipv6proxy/log.cpp b/wifi/ipv6proxy/log.cpp
deleted file mode 100644
index a23b062..0000000
--- a/wifi/ipv6proxy/log.cpp
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * 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.
- */
-
-#include "log.h"
-
-#ifndef ANDROID
-#include <stdarg.h>
-#ifdef USE_LOG_TIMESTAMPS
-#include <time.h>
-#endif // USE_LOG_TIMESTAMPS
-
-static void vlogf(FILE* stream, const char* fmt, va_list args) {
-#ifdef USE_LOG_TIMESTAMPS
- struct timespec ts;
- struct tm localTime;
- static bool newLine = true;
- if (newLine && clock_gettime(CLOCK_REALTIME, &ts) == 0) {
- time_t now = ts.tv_sec;
- if (localtime_r(&now, &localTime)) {
- char format[32];
- char timestamp[1024];
- snprintf(format, sizeof(format), "[%%T.%03lld] ", (long long)(ts.tv_nsec) / 1000000);
- strftime(timestamp, sizeof(timestamp), format, &localTime);
- fprintf(stream, "%s ", timestamp);
- }
- }
- newLine = (fmt[strlen(fmt) - 1] == '\n');
-#endif // USE_LOG_TIMESTAMPS
- vfprintf(stream, fmt, args);
-}
-
-static void logf(FILE* stream, const char* fmt, ...) {
- va_list args;
- va_start(args, fmt);
- vlogf(stream, fmt, args);
- va_end(args);
-}
-
-void loge(const char* fmt, ...) {
- va_list args;
- va_start(args, fmt);
- vlogf(stderr, fmt, args);
- va_end(args);
-}
-
-void logd(const char* fmt, ...) {
- va_list args;
- va_start(args, fmt);
- vlogf(stdout, fmt, args);
- va_end(args);
-}
-#endif // !ANDROID
diff --git a/wifi/ipv6proxy/log.h b/wifi/ipv6proxy/log.h
deleted file mode 100644
index 53e8935..0000000
--- a/wifi/ipv6proxy/log.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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
-
-#ifdef ANDROID
-
-#define LOG_TAG "ipv6proxy"
-#include <log/log.h>
-
-#define loge(...) ALOGE(__VA_ARGS__)
-#define logd(...) ALOGD(__VA_ARGS__)
-
-#else
-#include <stdio.h>
-
-void loge(const char* fmt, ...);
-void logd(const char* fmt, ...);
-
-#endif
diff --git a/wifi/ipv6proxy/main.cpp b/wifi/ipv6proxy/main.cpp
deleted file mode 100644
index 91445ca..0000000
--- a/wifi/ipv6proxy/main.cpp
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * 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.
- */
-
-#include <vector>
-
-#include <string.h>
-
-#include "log.h"
-#include "namespace.h"
-#include "proxy.h"
-
-void usage(const char* program) {
- loge("Usage: %s -n [namespace|pid] -o <outer if> -i <inner ifs>\n",
- program);
- loge(" -o Outer interface that connects to an existing IPv6 network.\n"
- " -i Comma separated list of inner interfaces that would like\n"
- " to access the IPv6 network available on the outer interface.\n"
- " -n Optional parameter that causes the proxy to run in the given\n"
- " network namespace. If a name is given instead of a PID the\n"
- " namespace is expected to be set up by iproute2 or with a\n"
- " similar approach where the namespace is linked in\n"
- " /var/run/netns. A PID is assumed if the argument is numeric.\n"
- " If providing a PID the same namespace that the PID is\n"
- " running in will be used. In this scenario there is no\n"
- " requirement for a file in /var/run/netns.\n"
- "\n"
- " The proxy will ensure that router solicitations from inner\n"
- " interfaces are forwarded to the outer interface and that\n"
- " router advertisements from the outer interface are forwarded\n"
- " to the inner interfaces. In addition to this neighbor\n"
- " solicitations and advertisements will also be forwarded in a\n"
- " way that enables IPv6 connectivity and routes will be set up\n"
- " for source addresses on the inner interfaces so that replies\n"
- " can reach those sources as expected.\n"
- );
-}
-
-static std::vector<const char*> splitString(char* str, char delimiter) {
- std::vector<const char*> parts;
- char* part = nullptr;
- do {
- parts.push_back(str);
- part = strchr(str, delimiter);
- if (part != nullptr) {
- *part = '\0';
- str = part + 1;
- }
- } while (part != nullptr);
- return parts;
-}
-
-int main(int argc, char* argv[]) {
- char* inner = nullptr;
- const char* outer = nullptr;
- const char* netNamespace = nullptr;
- for (int i = 1; i < argc; ++i) {
- if (strcmp(argv[i], "-o") == 0) {
- outer = argv[++i];
- } else if (strcmp(argv[i], "-i") == 0) {
- inner = argv[++i];
- } else if (strcmp(argv[i], "-h") == 0 ||
- strcmp(argv[i], "--help") == 0) {
- usage(argv[0]);
- return 1;
- } else if (strcmp(argv[i], "-n") == 0) {
- netNamespace = argv[++i];
- } else {
- loge("ERROR: Unknown argument '%s'\n\n", argv[i]);
- usage(argv[0]);
- return 1;
- }
- }
- bool error = false;
- if (inner == nullptr) {
- loge("ERROR: Missing inner interface\n");
- error = true;
- }
- if (outer == nullptr) {
- loge("ERROR: Missing outer interface\n");
- error = true;
- }
- if (netNamespace) {
- if (!setNetworkNamespace(netNamespace)) {
- error = true;
- }
- }
- if (error) {
- usage(argv[0]);
- return 1;
- }
-
- std::vector<const char*> innerInterfaces = splitString(inner, ',');
-
- Proxy proxy(outer, innerInterfaces.begin(), innerInterfaces.end());
-
- return proxy.run();
-}
-
diff --git a/wifi/ipv6proxy/message.h b/wifi/ipv6proxy/message.h
deleted file mode 100644
index 9b35dae..0000000
--- a/wifi/ipv6proxy/message.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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
-
-class Message {
-public:
-
- size_t size() const { return mSize; }
- size_t capacity() const { return sizeof(mData); }
- const char* data() const { return mData; }
- char* data() { return mData; }
-
- void setSize(size_t size) { mSize = size; }
-
-protected:
- char mData[8192];
- size_t mSize;
-};
-
diff --git a/wifi/ipv6proxy/namespace.cpp b/wifi/ipv6proxy/namespace.cpp
deleted file mode 100644
index 04841c2..0000000
--- a/wifi/ipv6proxy/namespace.cpp
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * 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.
- */
-
-#include "namespace.h"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <linux/limits.h>
-#include <sched.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include "log.h"
-
-static const char kNetNsDir[] = "/data/vendor/var/run/netns";
-
-// Set the current namespace to that of the /proc/<pid>/ns/net provided in
-// |path|. This may be a link or mount point for that same file, anything that
-// when opened will be an fd usable by setns is fine.
-static bool setNamespaceFromPath(const char* path) {
- int nsFd = open(path, O_RDONLY | O_CLOEXEC);
- if (nsFd == -1) {
- loge("Cannot open network namespace at '%s': %s\n",
- path, strerror(errno));
- return false;
- }
-
- if (setns(nsFd, CLONE_NEWNET) == -1) {
- loge("Cannot set network namespace at '%s': %s\n",
- path, strerror(errno));
- close(nsFd);
- return false;
- }
- close(nsFd);
- return true;
-}
-
-bool setNetworkNamespace(const char* ns) {
- // There is a file in the net namespace dir (usually /var/run/netns) with
- // the same name as the namespace. This file is bound to /proc/<pid>/ns/net
- // by the 'ip' command when the namespace is created. This allows us to
- // access the file of a process running in that network namespace without
- // knowing its pid, knowing the namespace name is enough.
- //
- // We are going to call setns which requires a file descriptor to that proc
- // file in /proc/<pid>/net. The process has to already be running in that
- // namespace. Since the file in the net namespace dir has been bound to
- // such a file already we just have to open /var/run/netns/<namespace> and
- // we have the required file descriptor.
- char nsPath[PATH_MAX];
- snprintf(nsPath, sizeof(nsPath), "%s/%s", kNetNsDir, ns);
- return setNamespaceFromPath(nsPath);
-}
-
-bool setNetworkNamespace(pid_t pid) {
- // If we know the pid we can create the path to the /proc file right away
- // and use that when we call setns.
- char nsPath[PATH_MAX];
- static_assert(sizeof(pid_t) <= sizeof(unsigned long long),
- "Cast requires sizeof(pid_t) <= sizeof(unsigned long long)");
- snprintf(nsPath, sizeof(nsPath), "/proc/%llu/ns/net/",
- static_cast<unsigned long long>(pid));
- return setNamespaceFromPath(nsPath);
-}
-
diff --git a/wifi/ipv6proxy/namespace.h b/wifi/ipv6proxy/namespace.h
deleted file mode 100644
index 8a70374..0000000
--- a/wifi/ipv6proxy/namespace.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * 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 <sys/types.h>
-
-// Move the process into the namespace |ns|
-bool setNetworkNamespace(const char* ns);
-
-// Move the process into the same namespace as the process |pid| is in.
-bool setNetworkNamespace(pid_t pid);
diff --git a/wifi/ipv6proxy/packet.cpp b/wifi/ipv6proxy/packet.cpp
deleted file mode 100644
index fa15a75..0000000
--- a/wifi/ipv6proxy/packet.cpp
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * 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.
- */
-
-#include "packet.h"
-
-#include "address.h"
-
-Packet::Packet(Message& message)
- : mMessage(message),
- mType(Type::Other),
- mIp(nullptr),
- mIcmp(nullptr),
- mFirstOpt(nullptr) {
- if (message.size() < sizeof(ip6_hdr) + sizeof(icmp6_hdr)) {
- mType = Type::Other;
- return;
- }
- mIp = reinterpret_cast<const ip6_hdr*>(message.data());
- uint8_t version = (mIp->ip6_vfc & 0xF0) >> 4;
- if (version != 6 || mIp->ip6_nxt != IPPROTO_ICMPV6) {
- mType = Type::Other;
- return;
- }
-
- size_t size = message.size() - sizeof(ip6_hdr);
- char* data = message.data() + sizeof(ip6_hdr);
- mIcmp = reinterpret_cast<const icmp6_hdr*>(data);
- if (mIcmp->icmp6_code != 0) {
- // All messages we care about have a code of zero
- mType = Type::Other;
- return;
- }
-
- size_t headerSize = 0;
- switch (mIcmp->icmp6_type) {
- case ND_ROUTER_SOLICIT:
- headerSize = sizeof(nd_router_solicit);
- mType = Type::RouterSolicitation;
- break;
- case ND_ROUTER_ADVERT:
- headerSize = sizeof(nd_router_advert);
- mType = Type::RouterAdvertisement;
- break;
- case ND_NEIGHBOR_SOLICIT:
- headerSize = sizeof(nd_neighbor_solicit);
- mType = Type::NeighborSolicitation;
- break;
- case ND_NEIGHBOR_ADVERT:
- headerSize = sizeof(nd_neighbor_advert);
- mType = Type::NeighborAdvertisement;
- break;
- default:
- mType = Type::Other;
- return;
- }
- if (size < headerSize) {
- mType = Type::Other;
- return;
- }
-
- // We might have options
- char* options = data + headerSize;
- if (options + sizeof(nd_opt_hdr) < data + size) {
- nd_opt_hdr* option = reinterpret_cast<nd_opt_hdr*>(options);
- // Option length is in units of 8 bytes, multiply by 8 to get bytes
- if (options + option->nd_opt_len * 8u <= data + size) {
- mFirstOpt = option;
- }
- }
-}
-
-std::string Packet::description() const {
- char buffer[256];
- switch (mType) {
- case Type::NeighborSolicitation: {
- auto ns = reinterpret_cast<const nd_neighbor_solicit*>(icmp());
- snprintf(buffer, sizeof(buffer), "Neighbor Solicitation for %s",
- addrToStr(ns->nd_ns_target).c_str());
- return buffer;
- }
- case Type::NeighborAdvertisement: {
- auto na = reinterpret_cast<const nd_neighbor_advert*>(icmp());
- snprintf(buffer, sizeof(buffer),
- "Neighbor Advertisement for %s",
- addrToStr(na->nd_na_target).c_str());
- return buffer;
- }
- case Type::RouterSolicitation:
- return "Router Solicitation";
- case Type::RouterAdvertisement:
- return "Router Advertisement";
- default:
- break;
- }
- return "[unknown]";
-}
-
-nd_opt_hdr* Packet::firstOpt() {
- return mFirstOpt;
-}
-
-nd_opt_hdr* Packet::nextOpt(nd_opt_hdr* currentHeader) {
- char* end = mMessage.data() + mMessage.size();
- char* current = reinterpret_cast<char*>(currentHeader);
- if (currentHeader < mFirstOpt || current >= end) {
- // The provided header does not belong to this packet info.
- return nullptr;
- }
- char* next = current + currentHeader->nd_opt_len * 8u;
- if (next >= end) {
- // The next header points passed the message data
- return nullptr;
- }
- nd_opt_hdr* nextHeader = reinterpret_cast<nd_opt_hdr*>(next);
- if (next + nextHeader->nd_opt_len * 8u > end) {
- // The next option extends beyond the message data
- return nullptr;
- }
- return nextHeader;
-}
-
diff --git a/wifi/ipv6proxy/packet.h b/wifi/ipv6proxy/packet.h
deleted file mode 100644
index 49e69d4..0000000
--- a/wifi/ipv6proxy/packet.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * 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 <netinet/icmp6.h>
-#include <netinet/ip6.h>
-
-#include <string>
-#include <vector>
-
-#include "message.h"
-
-class Packet {
-public:
- enum class Type {
- NeighborSolicitation,
- NeighborAdvertisement,
- RouterSolicitation,
- RouterAdvertisement,
- Other
- };
-
- explicit Packet(Message& message);
-
- // Create a string that can be used for debug purposes to describe
- // what type of packet and potentially its target for certain types.
- std::string description() const;
-
- // Full size including IP header
- size_t fullSize() const {
- return mMessage.size();
- }
- // Remaining size including ICMPv6 header but excluding IP header
- size_t icmpSize() const {
- return mMessage.size() - sizeof(ip6_hdr);
- }
-
- Type type() const {
- return mType;
- }
- const ip6_hdr* ip() const {
- return mIp;
- }
- const icmp6_hdr* icmp() const {
- return mIcmp;
- }
-
- nd_opt_hdr* firstOpt();
- nd_opt_hdr* nextOpt(nd_opt_hdr* currentHeader);
-
-private:
- Message& mMessage;
- Type mType;
-
- const ip6_hdr* mIp;
- const icmp6_hdr* mIcmp;
- nd_opt_hdr* mFirstOpt;
-};
-
diff --git a/wifi/ipv6proxy/proxy.cpp b/wifi/ipv6proxy/proxy.cpp
deleted file mode 100644
index cb19ff7..0000000
--- a/wifi/ipv6proxy/proxy.cpp
+++ /dev/null
@@ -1,262 +0,0 @@
-/*
- * 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.
- */
-
-#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"
-#include "result.h"
-
-// 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
-// specified by |optionType|. The valid values are ND_OPT_TARGET_LINKADDR and
-// ND_OPT_SOURCE_LINKADDR. This will modify the message data inside |packet|.
-static void rewriteLinkAddressOption(Packet& packet,
- const Interface& interface,
- int optionType) {
- for (nd_opt_hdr* opt = packet.firstOpt(); opt; opt = packet.nextOpt(opt)) {
- if (opt->nd_opt_type == optionType) {
- auto src = interface.linkAddr().get<sockaddr_ll>();
- auto dest = reinterpret_cast<char*>(opt) + sizeof(nd_opt_hdr);
- memcpy(dest, src->sll_addr, kLinkAddressSize);
- }
- }
-}
-
-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);
- if (status != 0) {
- loge("Unable to fill signal set: %s\n", strerror(errno));
- return 1;
- }
- status = ::sigprocmask(SIG_SETMASK, &blockMask, &originalMask);
- if (status != 0) {
- loge("Unable to set signal mask: %s\n", strerror(errno));
- return 1;
- }
- // Init outer interface and router
- if (!mOuterIf.init() || !mRouter.init()) {
- return 1;
- }
- // Init all inner interfaces
- for (size_t i = 0; i < mInnerIfs.size(); ++i) {
- if (!mInnerIfs[i].init()) {
- return 1;
- }
- }
-
- // Create list of FDs to poll, we're only looking for input (POLLIN)
- std::vector<pollfd> fds(mInnerIfs.size() + 1);
- fds[0].fd = mOuterIf.ipSocket().get();
- fds[0].events = POLLIN;
- for (size_t i = 0; i < mInnerIfs.size(); ++i) {
- fds[i + 1].fd = mInnerIfs[i].ipSocket().get();
- fds[i + 1].events = POLLIN;
- }
-
- Message message;
- while (status >= 0) {
- status = ::ppoll(fds.data(), fds.size(), nullptr, &originalMask);
- if (status > 0) {
- // Something available to read
- for (const struct pollfd& fd : fds) {
- if (receiveIfPossible(fd, mOuterIf.ipSocket(), &message)) {
- // Received a message on the outer interface
- handleOuterMessage(message);
- } else {
- for (auto& inner : mInnerIfs) {
- if (receiveIfPossible(fd, inner.ipSocket(), &message)) {
- // Received a message on the inner interface
- handleInnerMessage(inner, message);
- }
- }
- }
- }
- }
- }
- loge("Polling failed: %s\n", strerror(errno));
- return 1;
-}
-
-bool Proxy::receiveIfPossible(const pollfd& fd,
- Socket& socket,
- Message* message) {
- // Check if it's actually the socket we're interested in
- if (fd.fd != socket.get()) {
- return false;
- }
- // Check if there is something to read on this socket
- if ((fd.revents & POLLIN) == 0) {
- return false;
- }
-
- // Receive the message and place the data in the message parameter
- Result res = socket.receive(message);
- if (!res) {
- loge("Error receiving on socket: %s\n", res.c_str());
- return false;
- }
- return true;
-}
-
-void Proxy::handleOuterMessage(Message& message) {
- Packet packet(message);
- uint32_t options = kForwardOnly;
- switch (packet.type()) {
- case Packet::Type::RouterAdvertisement:
- extractRecursiveDnsServers(packet);
- options = kRewriteSourceLink | kSetDefaultGateway;
- break;
- case Packet::Type::NeighborSolicitation:
- options = kSpoofSource;
- break;
- case Packet::Type::NeighborAdvertisement:
- options = kRewriteTargetLink;
- break;
- default:
- return;
- }
- for (auto& inner : mInnerIfs) {
- forward(mOuterIf, inner, packet, options);
- }
-}
-
-void Proxy::handleInnerMessage(const Interface& inner, Message& message) {
- Packet packet(message);
- uint32_t options = kForwardOnly;
- switch (packet.type()) {
- case Packet::Type::RouterSolicitation:
- options = kSpoofSource;
- break;
- case Packet::Type::NeighborSolicitation:
- options = kSpoofSource | kAddRoute;
- break;
- case Packet::Type::NeighborAdvertisement:
- options = kRewriteTargetLink | kSpoofSource | kAddRoute;
- break;
- default:
- return;
- }
- forward(inner, mOuterIf, packet, options);
-}
-
-void Proxy::forward(const Interface& from,
- Interface& to,
- Packet& packet,
- uint32_t options) {
- if (mLogDebug) {
- logd("Forwarding %s from %s/%s to %s/%s\n",
- packet.description().c_str(),
- from.name().c_str(), addrToStr(packet.ip()->ip6_src).c_str(),
- to.name().c_str(), addrToStr(packet.ip()->ip6_dst).c_str());
- }
-
- if (options & kRewriteTargetLink) {
- rewriteLinkAddressOption(packet, to, ND_OPT_TARGET_LINKADDR);
- }
- if (options & kRewriteSourceLink) {
- rewriteLinkAddressOption(packet, to, ND_OPT_SOURCE_LINKADDR);
- }
-
- Result res = Result::success();
- if (options & kSpoofSource) {
- // Spoof the source of the packet so that it appears to originate from
- // the same source that we see.
- res = to.icmpSocket().sendFrom(packet.ip()->ip6_src,
- packet.ip()->ip6_dst,
- packet.icmp(),
- packet.icmpSize());
- } else {
- res = to.icmpSocket().sendTo(packet.ip()->ip6_dst,
- packet.icmp(),
- packet.icmpSize());
- }
- if (!res) {
- loge("Failed to forward %s from %s to %s: %s\n",
- packet.description().c_str(),
- from.name().c_str(), to.name().c_str(),
- res.c_str());
- }
-
- if (options & kAddRoute) {
- mRouter.addRoute(packet.ip()->ip6_src, kNodePrefixLength, from.index());
- }
- if (packet.type() == Packet::Type::RouterAdvertisement &&
- options & kSetDefaultGateway) {
- // Set the default gateway from this router advertisement. This is
- // needed so that packets that are forwarded as a result of proxying
- // actually have somewhere to go.
- if (!mRouter.setDefaultGateway(packet.ip()->ip6_src, from.index())) {
- loge("Failed to set default gateway %s\n",
- addrToStr(packet.ip()->ip6_src).c_str());
- }
- }
-}
-
diff --git a/wifi/ipv6proxy/proxy.h b/wifi/ipv6proxy/proxy.h
deleted file mode 100644
index 5179137..0000000
--- a/wifi/ipv6proxy/proxy.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * 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 <string>
-#include <vector>
-
-#include "interface.h"
-#include "router.h"
-
-struct pollfd;
-class Message;
-class Packet;
-class Socket;
-
-class Proxy {
-public:
- template<typename Iter>
- Proxy(std::string outerInterfaceName,
- Iter innerInterfacesBegin, Iter innerInterfacesEnd)
- : mOuterIf(outerInterfaceName),
- mLogDebug(false) {
-
- for (Iter i = innerInterfacesBegin; i != innerInterfacesEnd; ++i) {
- mInnerIfs.emplace_back(*i);
- }
- }
-
- int run();
-
-private:
- enum ForwardOpt {
- kForwardOnly = 0,
- kRewriteTargetLink = (1 << 0),
- kRewriteSourceLink = (1 << 1),
- kSpoofSource = (1 << 2),
- kAddRoute = (1 << 3),
- kSetDefaultGateway = (1 << 4)
- };
-
- bool receiveIfPossible(const pollfd&, Socket& socket, Message* message);
-
- void handleOuterMessage(Message& message);
- void handleInnerMessage(const Interface& inner, Message& message);
- void forward(const Interface& from, Interface& to,
- Packet& packet, uint32_t options);
-
- std::vector<Interface> mInnerIfs;
- Interface mOuterIf;
-
- Router mRouter;
- bool mLogDebug;
-};
-
diff --git a/wifi/ipv6proxy/router.cpp b/wifi/ipv6proxy/router.cpp
deleted file mode 100644
index be505c9..0000000
--- a/wifi/ipv6proxy/router.cpp
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * 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.
- */
-
-#include "router.h"
-
-#include <linux/rtnetlink.h>
-#include <stddef.h>
-#include <string.h>
-
-#include "address.h"
-#include "log.h"
-
-template<class Request>
-static void addRouterAttribute(Request& r,
- int type,
- const void* data,
- size_t size) {
- // Calculate the offset into the character buffer where the RTA data lives
- // We use offsetof on the buffer to get it. This avoids undefined behavior
- // by casting the buffer (which is safe because it's char) instead of the
- // Request struct.(which is undefined because of aliasing)
- size_t offset = NLMSG_ALIGN(r.hdr.nlmsg_len) - offsetof(Request, buf);
- auto attr = reinterpret_cast<struct rtattr*>(r.buf + offset);
- attr->rta_type = type;
- attr->rta_len = RTA_LENGTH(size);
- memcpy(RTA_DATA(attr), data, size);
-
- // Update the message length to include the router attribute.
- r.hdr.nlmsg_len = NLMSG_ALIGN(r.hdr.nlmsg_len) + RTA_ALIGN(attr->rta_len);
-}
-
-bool Router::init() {
- // Create a netlink socket to the router
- Result res = mSocket.open(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
- if (!res) {
- loge("Unable to open netlink socket: %s\n", res.c_str());
- return false;
- }
- return true;
-}
-
-bool Router::addNeighbor(const struct in6_addr& address,
- unsigned int interfaceIndex) {
- struct Request {
- struct nlmsghdr hdr;
- struct ndmsg msg;
- char buf[256];
- } request;
-
- memset(&request, 0, sizeof(request));
-
- unsigned short msgLen = NLMSG_LENGTH(sizeof(request.msg));
- // Set up a request to create a new neighbor
- request.hdr.nlmsg_len = msgLen;
- request.hdr.nlmsg_type = RTM_NEWNEIGH;
- request.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
-
- // The neighbor is a permanent IPv6 proxy
- request.msg.ndm_family = AF_INET6;
- request.msg.ndm_state = NUD_PERMANENT;
- request.msg.ndm_flags = NTF_PROXY;
- request.msg.ndm_ifindex = interfaceIndex;
-
- addRouterAttribute(request, NDA_DST, &address, sizeof(address));
-
- return sendNetlinkMessage(&request, request.hdr.nlmsg_len);
-}
-
-bool Router::addRoute(const struct in6_addr& address,
- uint8_t bits,
- uint32_t ifaceIndex) {
- struct Request {
- struct nlmsghdr hdr;
- struct rtmsg msg;
- char buf[256];
- } request;
-
- memset(&request, 0, sizeof(request));
-
- // Set up a request to create a new route
- request.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(request.msg));
- request.hdr.nlmsg_type = RTM_NEWROUTE;
- request.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
-
- request.msg.rtm_family = AF_INET6;
- request.msg.rtm_dst_len = bits;
- request.msg.rtm_table = RT_TABLE_MAIN;
- request.msg.rtm_protocol = RTPROT_RA;
- request.msg.rtm_scope = RT_SCOPE_UNIVERSE;
- request.msg.rtm_type = RTN_UNICAST;
-
- addRouterAttribute(request, RTA_DST, &address, sizeof(address));
- addRouterAttribute(request, RTA_OIF, &ifaceIndex, sizeof(ifaceIndex));
-
- return sendNetlinkMessage(&request, request.hdr.nlmsg_len);
-}
-
-bool Router::setDefaultGateway(const struct in6_addr& address,
- unsigned int ifaceIndex) {
- struct Request {
- struct nlmsghdr hdr;
- struct rtmsg msg;
- char buf[256];
- } request;
-
- memset(&request, 0, sizeof(request));
-
- // Set up a request to create a new route
- request.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(request.msg));
- request.hdr.nlmsg_type = RTM_NEWROUTE;
- request.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
-
- request.msg.rtm_family = AF_INET6;
- request.msg.rtm_dst_len = 0;
- request.msg.rtm_src_len = 0;
- request.msg.rtm_table = RT_TABLE_MAIN;
- request.msg.rtm_protocol = RTPROT_RA;
- request.msg.rtm_scope = RT_SCOPE_UNIVERSE;
- request.msg.rtm_type = RTN_UNICAST;
-
- struct in6_addr anyAddress;
- memset(&anyAddress, 0, sizeof(anyAddress));
- addRouterAttribute(request, RTA_GATEWAY, &address, sizeof(address));
- addRouterAttribute(request, RTA_OIF, &ifaceIndex, sizeof(ifaceIndex));
- addRouterAttribute(request, RTA_SRC, &anyAddress, sizeof(anyAddress));
-
- return sendNetlinkMessage(&request, request.hdr.nlmsg_len);
-}
-
-bool Router::sendNetlinkMessage(const void* data, size_t size) {
- struct sockaddr_nl netlinkAddress;
- memset(&netlinkAddress, 0, sizeof(netlinkAddress));
- netlinkAddress.nl_family = AF_NETLINK;
- Result res = mSocket.sendTo(netlinkAddress, data, size);
- if (!res) {
- loge("Unable to send on netlink socket: %s\n", res.c_str());
- return false;
- }
- return true;
-}
-
diff --git a/wifi/ipv6proxy/router.h b/wifi/ipv6proxy/router.h
deleted file mode 100644
index 66e896f..0000000
--- a/wifi/ipv6proxy/router.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * 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 <stdint.h>
-
-#include <netinet/in.h>
-
-#include "socket.h"
-
-class Router {
-public:
- // Initialize the router, this has to be called before any other methods can
- // be called. It only needs to be called once.
- bool init();
-
- // Indicate that |address| is a neighbor to this node and that it is
- // accessible on the interface with index |interfaceIndex|.
- bool addNeighbor(const struct in6_addr& address, uint32_t interfaceIndex);
-
- // Add a route to |address|/|bits| on interface |interfaceIndex|. The
- // |bits| parameter indicates the bitmask of the address, for example in
- // the routing entry 2001:db8::/32 the |bits| parameter would be 32.
- bool addRoute(const struct in6_addr& address,
- uint8_t bits,
- uint32_t interfaceIndex);
-
- // Set the default gateway route to |address| on interface with index
- // |interfaceIndex|. Overwrites any existing default gateway with the same
- // address.
- bool setDefaultGateway(const struct in6_addr& address,
- unsigned int interfaceIndex);
-private:
- bool sendNetlinkMessage(const void* data, size_t size);
-
- // Netlink socket for setting up neighbors and routes
- Socket mSocket;
-};
-
diff --git a/wifi/ipv6proxy/socket.cpp b/wifi/ipv6proxy/socket.cpp
deleted file mode 100644
index 3b67816..0000000
--- a/wifi/ipv6proxy/socket.cpp
+++ /dev/null
@@ -1,272 +0,0 @@
-/*
- * 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.
- */
-
-#include "socket.h"
-
-#include <errno.h>
-#include <string.h>
-
-#include <linux/in6.h>
-#include <net/ethernet.h>
-#include <netinet/in.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <unistd.h>
-
-#include "address.h"
-#include "message.h"
-
-Socket::Socket() : mState(State::New), mSocket(-1) {
-}
-
-Socket::Socket(Socket&& other) noexcept : mState(other.mState), mSocket(other.mSocket) {
- other.mSocket = -1;
- other.mState = State::Moved;
-}
-
-Socket::~Socket() {
- if (mSocket != -1) {
- close(mSocket);
- mSocket = -1;
- }
- mState = State::Destructed;
-}
-
-Socket& Socket::operator=(Socket&& other) noexcept {
- if (mSocket != -1) {
- close(mSocket);
- }
- mSocket = other.mSocket;
- mState = other.mState;
- other.mSocket = -1;
- other.mState = State::Moved;
-
- return *this;
-}
-
-Result Socket::open(int domain, int type, int protocol) {
- if (mState != State::New) {
- return Result::error("open called on socket in invalid state");
- }
- mSocket = ::socket(domain, type | SOCK_CLOEXEC, protocol);
- if (mSocket == -1) {
- return Result::error(strerror(errno));
- }
- mState = State::Open;
- return Result::success();
-}
-
-Result Socket::setInterface(const std::string& interface) {
- if (mState != State::Open) {
- return Result::error("attempting to set option in invalid state");
- }
- int res = ::setsockopt(mSocket, SOL_SOCKET, SO_BINDTODEVICE,
- interface.c_str(), interface.size());
-
- return res == -1 ? Result::error(strerror(errno)) : Result::success();
-}
-
-Result Socket::setMulticastHopLimit(int hopLimit) {
- if (mState != State::Open) {
- return Result::error("attempting to set option in invalid state");
- }
- int res = ::setsockopt(mSocket, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
- &hopLimit, sizeof(hopLimit));
-
- return res == -1 ? Result::error(strerror(errno)) : Result::success();
-}
-
-Result Socket::setUnicastHopLimit(int hopLimit) {
- if (mState != State::Open) {
- return Result::error("attempting to set option in invalid state");
- }
- int res = ::setsockopt(mSocket, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
- &hopLimit, sizeof(hopLimit));
-
- return res == -1 ? Result::error(strerror(errno)) : Result::success();
-}
-
-Result Socket::setTransparent(bool transparent) {
- if (mState != State::Open) {
- return Result::error("attempting to set option in invalid state");
- }
- int v = transparent ? 1 : 0;
- int res = ::setsockopt(mSocket, IPPROTO_IPV6, IPV6_TRANSPARENT,
- &v, sizeof(v));
-
- return res == -1 ? Result::error(strerror(errno)) : Result::success();
-}
-
-Result Socket::bind(const Address& address) {
- if (mState != State::Open) {
- return Result::error("bind called on socket in invalid state");
- }
-
- int res = ::bind(mSocket, address.get<sockaddr>(), address.size());
- if (res == -1) {
- return Result::error(strerror(errno));
- }
-
- mState = State::Bound;
- return Result::success();
-}
-
-Result Socket::receive(Message* receivingMessage) {
- if (receivingMessage == nullptr) {
- return Result::error("No receivingMessage provided");
- }
- if (mState != State::Bound) {
- return Result::error("Attempt to receive on a socket that isn't bound");
- }
-
- ssize_t rxBytes = ::recv(mSocket,
- receivingMessage->data(),
- receivingMessage->capacity(),
- 0);
- if (rxBytes < 0) {
- return Result::error(strerror(errno));
- }
-
- receivingMessage->setSize(static_cast<size_t>(rxBytes));
- return Result::success();
-}
-
-Result Socket::receiveFrom(Message* receivingMessage, Address* from) {
- if (receivingMessage == nullptr) {
- return Result::error("No receivingMessage provided");
- }
- if (from == nullptr) {
- return Result::error("No from address provided");
- }
- if (mState != State::Bound) {
- return Result::error("Attempt to receive on a socket that isn't bound");
- }
-
- from->reset();
- sockaddr* source = from->get<sockaddr>();
- socklen_t sourceLen = from->size();
- ssize_t rxBytes = ::recvfrom(mSocket,
- receivingMessage->data(),
- receivingMessage->capacity(),
- 0,
- source,
- &sourceLen);
- if (rxBytes < 0) {
- return Result::error(strerror(errno));
- }
-
- receivingMessage->setSize(static_cast<size_t>(rxBytes));
- return Result::success();
-}
-
-Result Socket::send(const void* data, size_t size) {
- if (mState != State::Bound && mState != State::Open) {
- return Result::error("Attempt to send on a socket in invalid state");
- }
-
- int res = ::send(mSocket, data, size, 0);
- if (res == -1) {
- return Result::error(strerror(errno));
- }
- return Result::success();
-}
-
-Result Socket::sendTo(const sockaddr& destination,
- size_t destinationSize,
- const void* data,
- size_t size) {
- if (mState != State::Bound && mState != State::Open) {
- return Result::error("Attempt to send on a socket in invalid state");
- }
-
- int res = ::sendto(mSocket, data, size, 0, &destination, destinationSize);
- if (res == -1) {
- return Result::error(strerror(errno));
- }
- return Result::success();
-}
-
-Result Socket::sendTo(const in6_addr& destination,
- const void* data,
- size_t size) {
- sockaddr_in6 addr;
- memset(&addr, 0, sizeof(addr));
- addr.sin6_family = AF_INET6;
- addr.sin6_addr = destination;
- return sendTo(*reinterpret_cast<sockaddr*>(&addr),
- sizeof(addr),
- data,
- size);
-}
-
-Result Socket::sendFrom(const struct in6_addr& fromAddress,
- const sockaddr& destination,
- size_t destinationSize,
- const void* data,
- size_t size) {
- struct msghdr messageHeader;
- memset(&messageHeader, 0, sizeof(messageHeader));
- // Even when sending this struct requires a non-const pointer, even when
- // it's only going to be read. Do a const_cast instead of creating a
- // method signature with illogical const-behavior.
- messageHeader.msg_name = const_cast<struct sockaddr*>(&destination);
- messageHeader.msg_namelen = destinationSize;
-
- struct iovec iov;
- messageHeader.msg_iov = &iov;
- messageHeader.msg_iovlen = 1;
-
- memset(&iov, 0, sizeof(iov));
- iov.iov_base = const_cast<void*>(data);
- iov.iov_len = size;
-
- char control[CMSG_SPACE(sizeof(struct in6_pktinfo))] = { 0 };
- messageHeader.msg_control = control;
- messageHeader.msg_controllen = sizeof(control);
-
- struct cmsghdr* controlHeader = CMSG_FIRSTHDR(&messageHeader);
- controlHeader->cmsg_level = IPPROTO_IPV6;
- controlHeader->cmsg_type = IPV6_PKTINFO;
- controlHeader->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
-
- auto packetInfoData = CMSG_DATA(controlHeader);
- auto packetInfo = reinterpret_cast<struct in6_pktinfo*>(packetInfoData);
- packetInfo->ipi6_addr = fromAddress;
-
- int res = ::sendmsg(mSocket, &messageHeader, 0);
- if (res == -1) {
- int error = errno;
- printf("sendmsg failed: %d\n", error);
- return Result::error(strerror(error));
- }
- return Result::success();
-}
-
-Result Socket::sendFrom(const in6_addr& fromAddress,
- const in6_addr& destination,
- const void* data,
- size_t size) {
- sockaddr_in6 addr;
- memset(&addr, 0, sizeof(addr));
- addr.sin6_family = AF_INET6;
- addr.sin6_addr = destination;
-
- return sendFrom(fromAddress,
- *reinterpret_cast<sockaddr*>(&addr),
- sizeof(addr),
- data,
- size);
-}
diff --git a/wifi/ipv6proxy/socket.h b/wifi/ipv6proxy/socket.h
deleted file mode 100644
index 9cbdda3..0000000
--- a/wifi/ipv6proxy/socket.h
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * 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 "result.h"
-
-#include <netinet/in.h>
-
-#include <stdint.h>
-#include <string>
-
-class Address;
-class Message;
-
-class Socket {
-public:
- enum class Domain {
- IpV4,
- IpV6,
- Packet,
- };
-
- enum class Type {
- Stream, // A streaming protocol, use this with Infer for TCP
- Datagram, // A datagram protocol, use this with Infer for UDP
- Raw, // A raw socket
- };
- enum class Protocol {
- Infer, // Infer the protocol from the type, such as TCP for Stream
- Ip, // Internet Protocol for raw sockets
- IcmpV6, // ICMPv6 control protocol for Raw sockets
- EthIpV6, // Ethernet packets containing IPV6, for packet sockets
- };
-
- // Construct an empty socket object, next use open() to start using it
- Socket();
- // Move construct a socket, The constructed socket will be in the same state
- // that |other| is. After this |other| will be in an undefined state and
- // should no longer be used.
- Socket(Socket&& other) noexcept;
- ~Socket();
-
- // Move the |other| socket object into this one. If this object has an open
- // socket it will be closed first. After that this object will have the
- // same state that |other| did. |other| will be left in an undefined state
- // and should not be used.
- Socket& operator=(Socket&& other) noexcept;
-
- int get() const { return mSocket; }
-
- Result open(int domain, int type, int protocol);
-
- /** Options, these must be called between open and bind **/
-
- // Bind to a specific interface regardless of the address that the socket
- // is going to bind to.
- Result setInterface(const std::string& interface);
-
- // Set the hop limit for multicast traffic on the socket. Each router hop
- // decreases this value by one, when it reaches zero the packet is
- // discarded.
- Result setMulticastHopLimit(int hopLimit);
-
- // Set the hop limit for unicast traffic on the socket. Each router hop
- // decreases this value by one, when it reaches zero the packet is
- // discarded.
- Result setUnicastHopLimit(int hopLimit);
-
- // Configure the socket to be transparent. This allows packets sent to have
- // a source address that is different from the network interface's source
- // address.
- Result setTransparent(bool transparent);
-
- /** Binding **/
-
- Result bind(const Address& address);
-
- /** Sending and receiving **/
-
- Result receive(Message* receivingMessage);
- Result receiveFrom(Message* receivingMessage, Address* from);
-
- Result send(const void* data, size_t size);
-
- // Send a packet to a specific |destination| of any address type.
- Result sendTo(const sockaddr& destination,
- size_t destinationSize,
- const void* data,
- size_t size);
- // Convenience function to send to a specific IPv6 address.
- Result sendTo(const in6_addr& destination, const void* data, size_t size);
- // Convenience method to use sendTo with a more specific sockaddr struct
- // without having to specify the size or do the casting.
- template<typename T>
- Result sendTo(const T& destination, const void* data, size_t size) {
- return sendTo(*reinterpret_cast<const sockaddr*>(&destination),
- sizeof(destination),
- data,
- size);
- }
-
- // Send a packet with a specific source IPv6 address to a given
- // |destination|. Rewriting the source in this manner usually requires root.
- Result sendFrom(const in6_addr& fromAddress,
- const sockaddr& destination,
- size_t destinationSize,
- const void* data,
- size_t size);
- Result sendFrom(const in6_addr& fromAddress,
- const in6_addr& destination,
- const void* data,
- size_t size);
- // Convenience method to use sendFrom with a more specific sockaddr struct
- // without having to specify the size or do the casting.
- template<typename T>
- Result sendFrom(const in6_addr& fromAddress,
- const T& destination,
- const void* data,
- size_t size) {
- return sendFrom(fromAddress,
- *reinterpret_cast<const sockaddr*>(&destination),
- sizeof(destination),
- data,
- size);
- }
-
-private:
- // No copy construction or assignment allowed, support move semantics only
- Socket(const Socket&);
- Socket& operator=(const Socket&);
-
- enum class State {
- New,
- Open,
- Bound,
- Moved,
- Destructed,
- };
-
- State mState;
- int mSocket;
-};
-
diff --git a/x86-vendor.mk b/x86-vendor.mk
index 29157ba..5fa9dc9 100644
--- a/x86-vendor.mk
+++ b/x86-vendor.mk
@@ -17,5 +17,9 @@
device/generic/goldfish/data/etc/encryptionkey.img:images/x86/encryptionkey.img \
prebuilts/qemu-kernel/x86_64/$(PRODUCT_KERNEL_VERSION)/kernel-qemu2:images/x86/kernel-ranchu-64
+PRODUCT_COPY_FILES += \
+ device/generic/goldfish/data/etc/x86/emulatorip:$(TARGET_COPY_OUT_VENDOR)/bin/ip
+
+
PRODUCT_SHIPPING_API_LEVEL := 28
TARGET_USES_MKE2FS := true
diff --git a/x86_64-vendor.mk b/x86_64-vendor.mk
index b0cbf0a..a41a53e 100644
--- a/x86_64-vendor.mk
+++ b/x86_64-vendor.mk
@@ -17,5 +17,9 @@
device/generic/goldfish/data/etc/encryptionkey.img:images/x86_64/encryptionkey.img \
prebuilts/qemu-kernel/x86_64/$(PRODUCT_KERNEL_VERSION)/kernel-qemu2:images/x86_64/kernel-ranchu
+PRODUCT_COPY_FILES += \
+ device/generic/goldfish/data/etc/x86/emulatorip:$(TARGET_COPY_OUT_VENDOR)/bin/ip
+
+
PRODUCT_SHIPPING_API_LEVEL := 28
TARGET_USES_MKE2FS := true