From: Tom Cherry Date: Fri, 21 Aug 2015 18:24:15 +0000 (+0000) Subject: am 8246d45e: Merge "bundle init.rc contents with its service" X-Git-Tag: android-x86-7.1-r1~86^2~9 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=4e9e23c50a080719f55e54db783f0c2cb130c95b;hp=8246d45e94bfb0ce878f41a01602b7b8d78c40ad;p=android-x86%2Fsystem-netd.git am 8246d45e: Merge "bundle init.rc contents with its service" * commit '8246d45e94bfb0ce878f41a01602b7b8d78c40ad': bundle init.rc contents with its service --- diff --git a/Android.mk b/Android.mk new file mode 100644 index 0000000..5053e7d --- /dev/null +++ b/Android.mk @@ -0,0 +1 @@ +include $(call all-subdir-makefiles) diff --git a/client/Android.mk b/client/Android.mk index 0c5e7ea..e202534 100644 --- a/client/Android.mk +++ b/client/Android.mk @@ -16,7 +16,7 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -LOCAL_C_INCLUDES := bionic/libc/dns/include external/libcxx/include system/netd/include +LOCAL_C_INCLUDES := bionic/libc/dns/include system/netd/include LOCAL_CLANG := true LOCAL_CPPFLAGS := -std=c++11 -Wall -Werror LOCAL_MODULE := libnetd_client diff --git a/client/FwmarkClient.cpp b/client/FwmarkClient.cpp index 0ac1fbb..a82f4c2 100644 --- a/client/FwmarkClient.cpp +++ b/client/FwmarkClient.cpp @@ -16,6 +16,8 @@ #include "FwmarkClient.h" +#include "FwmarkCommand.h" + #include #include #include @@ -42,7 +44,7 @@ FwmarkClient::~FwmarkClient() { } } -int FwmarkClient::send(void* data, size_t len, int fd) { +int FwmarkClient::send(FwmarkCommand* data, int fd) { mChannel = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); if (mChannel == -1) { return -errno; @@ -57,7 +59,7 @@ int FwmarkClient::send(void* data, size_t len, int fd) { iovec iov; iov.iov_base = data; - iov.iov_len = len; + iov.iov_len = sizeof(*data); msghdr message; memset(&message, 0, sizeof(message)); @@ -69,15 +71,17 @@ int FwmarkClient::send(void* data, size_t len, int fd) { char cmsg[CMSG_SPACE(sizeof(fd))]; } cmsgu; - memset(cmsgu.cmsg, 0, sizeof(cmsgu.cmsg)); - message.msg_control = cmsgu.cmsg; - message.msg_controllen = sizeof(cmsgu.cmsg); + if (data->cmdId != FwmarkCommand::QUERY_USER_ACCESS) { + memset(cmsgu.cmsg, 0, sizeof(cmsgu.cmsg)); + message.msg_control = cmsgu.cmsg; + message.msg_controllen = sizeof(cmsgu.cmsg); - cmsghdr* const cmsgh = CMSG_FIRSTHDR(&message); - cmsgh->cmsg_len = CMSG_LEN(sizeof(fd)); - cmsgh->cmsg_level = SOL_SOCKET; - cmsgh->cmsg_type = SCM_RIGHTS; - memcpy(CMSG_DATA(cmsgh), &fd, sizeof(fd)); + cmsghdr* const cmsgh = CMSG_FIRSTHDR(&message); + cmsgh->cmsg_len = CMSG_LEN(sizeof(fd)); + cmsgh->cmsg_level = SOL_SOCKET; + cmsgh->cmsg_type = SCM_RIGHTS; + memcpy(CMSG_DATA(cmsgh), &fd, sizeof(fd)); + } if (TEMP_FAILURE_RETRY(sendmsg(mChannel, &message, 0)) == -1) { return -errno; diff --git a/client/FwmarkClient.h b/client/FwmarkClient.h index 620275e..df7686d 100644 --- a/client/FwmarkClient.h +++ b/client/FwmarkClient.h @@ -19,6 +19,8 @@ #include +struct FwmarkCommand; + class FwmarkClient { public: // Returns true if a socket of the given |family| should be sent to the fwmark server to have @@ -30,7 +32,7 @@ public: // Sends |data| to the fwmark server, along with |fd| as ancillary data using cmsg(3). // Returns 0 on success or a negative errno value on failure. - int send(void* data, size_t len, int fd); + int send(FwmarkCommand* data, int fd); private: int mChannel; diff --git a/client/NetdClient.cpp b/client/NetdClient.cpp index 3157d3a..392b0af 100644 --- a/client/NetdClient.cpp +++ b/client/NetdClient.cpp @@ -65,7 +65,7 @@ int netdClientAccept4(int sockfd, sockaddr* addr, socklen_t* addrlen, int flags) } if (FwmarkClient::shouldSetFwmark(family)) { FwmarkCommand command = {FwmarkCommand::ON_ACCEPT, 0, 0}; - if (int error = FwmarkClient().send(&command, sizeof(command), acceptedSocket)) { + if (int error = FwmarkClient().send(&command, acceptedSocket)) { return closeFdAndSetErrno(acceptedSocket, error); } } @@ -75,7 +75,7 @@ int netdClientAccept4(int sockfd, sockaddr* addr, socklen_t* addrlen, int flags) int netdClientConnect(int sockfd, const sockaddr* addr, socklen_t addrlen) { if (sockfd >= 0 && addr && FwmarkClient::shouldSetFwmark(addr->sa_family)) { FwmarkCommand command = {FwmarkCommand::ON_CONNECT, 0, 0}; - if (int error = FwmarkClient().send(&command, sizeof(command), sockfd)) { + if (int error = FwmarkClient().send(&command, sockfd)) { errno = -error; return -1; } @@ -185,7 +185,7 @@ extern "C" int setNetworkForSocket(unsigned netId, int socketFd) { return -EBADF; } FwmarkCommand command = {FwmarkCommand::SELECT_NETWORK, netId, 0}; - return FwmarkClient().send(&command, sizeof(command), socketFd); + return FwmarkClient().send(&command, socketFd); } extern "C" int setNetworkForProcess(unsigned netId) { @@ -201,7 +201,7 @@ extern "C" int protectFromVpn(int socketFd) { return -EBADF; } FwmarkCommand command = {FwmarkCommand::PROTECT_FROM_VPN, 0, 0}; - return FwmarkClient().send(&command, sizeof(command), socketFd); + return FwmarkClient().send(&command, socketFd); } extern "C" int setNetworkForUser(uid_t uid, int socketFd) { @@ -209,5 +209,10 @@ extern "C" int setNetworkForUser(uid_t uid, int socketFd) { return -EBADF; } FwmarkCommand command = {FwmarkCommand::SELECT_FOR_USER, 0, uid}; - return FwmarkClient().send(&command, sizeof(command), socketFd); + return FwmarkClient().send(&command, socketFd); +} + +extern "C" int queryUserAccess(uid_t uid, unsigned netId) { + FwmarkCommand command = {FwmarkCommand::QUERY_USER_ACCESS, netId, uid}; + return FwmarkClient().send(&command, -1); } diff --git a/include/FwmarkCommand.h b/include/FwmarkCommand.h index 57464b4..be06899 100644 --- a/include/FwmarkCommand.h +++ b/include/FwmarkCommand.h @@ -27,9 +27,11 @@ struct FwmarkCommand { SELECT_NETWORK, PROTECT_FROM_VPN, SELECT_FOR_USER, + QUERY_USER_ACCESS, } cmdId; unsigned netId; // used only in the SELECT_NETWORK command; ignored otherwise. - uid_t uid; // used only in the SELECT_FOR_USER command; ignored otherwise. + uid_t uid; // used only in the SELECT_FOR_USER and QUERY_USER_ACCESS commands; + // ignored otherwise. }; #endif // NETD_INCLUDE_FWMARK_COMMAND_H diff --git a/include/NetdClient.h b/include/NetdClient.h index 71529a3..7db0906 100644 --- a/include/NetdClient.h +++ b/include/NetdClient.h @@ -37,6 +37,8 @@ int protectFromVpn(int socketFd); int setNetworkForUser(uid_t uid, int socketFd); +int queryUserAccess(uid_t uid, unsigned netId); + __END_DECLS #endif // NETD_INCLUDE_NETD_CLIENT_H diff --git a/server/Android.mk b/server/Android.mk index 42bafea..d0cd49e 100644 --- a/server/Android.mk +++ b/server/Android.mk @@ -50,6 +50,7 @@ LOCAL_SRC_FILES := \ ClatdController.cpp \ CommandListener.cpp \ DnsProxyListener.cpp \ + DummyNetwork.cpp \ FirewallController.cpp \ FwmarkServer.cpp \ IdletimerController.cpp \ diff --git a/server/BandwidthController.cpp b/server/BandwidthController.cpp index 8795f7c..d71ce3b 100644 --- a/server/BandwidthController.cpp +++ b/server/BandwidthController.cpp @@ -199,7 +199,7 @@ int BandwidthController::runIptablesCmd(const char *cmd, IptJumpOp jumpHandling, break; } - fullCmd.insert(0, " "); + fullCmd.insert(0, " -w "); fullCmd.insert(0, iptVer == IptIpV4 ? IPTABLES_PATH : IP6TABLES_PATH); if (StrncpyAndCheck(buffer, fullCmd.c_str(), sizeof(buffer))) { diff --git a/server/CommandListener.cpp b/server/CommandListener.cpp index b508d3f..7ecbffc 100644 --- a/server/CommandListener.cpp +++ b/server/CommandListener.cpp @@ -474,7 +474,7 @@ int CommandListener::InterfaceCmd::runCommand(SocketClient *cli, cli->sendMsg(ResponseCode::CommandOkay, "MTU changed", false); } else { cli->sendMsg(ResponseCode::OperationFailed, - "Failed to get MTU", true); + "Failed to set MTU", true); } return 0; } else { @@ -507,37 +507,59 @@ CommandListener::IpFwdCmd::IpFwdCmd() : NetdCommand("ipfwd") { } -int CommandListener::IpFwdCmd::runCommand(SocketClient *cli, - int argc, char **argv) { - int rc = 0; +int CommandListener::IpFwdCmd::runCommand(SocketClient *cli, int argc, char **argv) { + bool matched = false; + bool success; - if (argc < 2) { - cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false); - return 0; - } - - if (!strcmp(argv[1], "status")) { - char *tmp = NULL; + if (argc == 2) { + // 0 1 + // ipfwd status + if (!strcmp(argv[1], "status")) { + char *tmp = NULL; - asprintf(&tmp, "Forwarding %s", (sTetherCtrl->getIpFwdEnabled() ? "enabled" : "disabled")); - cli->sendMsg(ResponseCode::IpFwdStatusResult, tmp, false); - free(tmp); - return 0; - } else if (!strcmp(argv[1], "enable")) { - rc = sTetherCtrl->setIpFwdEnabled(true); - } else if (!strcmp(argv[1], "disable")) { - rc = sTetherCtrl->setIpFwdEnabled(false); - } else { + asprintf(&tmp, "Forwarding %s", + ((sTetherCtrl->forwardingRequestCount() > 0) ? "enabled" : "disabled")); + cli->sendMsg(ResponseCode::IpFwdStatusResult, tmp, false); + free(tmp); + return 0; + } + } else if (argc == 3) { + // 0 1 2 + // ipfwd enable + // ipfwd disable + if (!strcmp(argv[1], "enable")) { + matched = true; + success = sTetherCtrl->enableForwarding(argv[2]); + } else if (!strcmp(argv[1], "disable")) { + matched = true; + success = sTetherCtrl->disableForwarding(argv[2]); + } + } else if (argc == 4) { + // 0 1 2 3 + // ipfwd add wlan0 dummy0 + // ipfwd remove wlan0 dummy0 + int ret = 0; + if (!strcmp(argv[1], "add")) { + matched = true; + ret = RouteController::enableTethering(argv[2], argv[3]); + } else if (!strcmp(argv[1], "remove")) { + matched = true; + ret = RouteController::disableTethering(argv[2], argv[3]); + } + success = (ret == 0); + errno = -ret; + } + + if (!matched) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown ipfwd cmd", false); return 0; } - if (!rc) { + if (success) { cli->sendMsg(ResponseCode::CommandOkay, "ipfwd operation succeeded", false); } else { cli->sendMsg(ResponseCode::OperationFailed, "ipfwd operation failed", true); } - return 0; } @@ -797,15 +819,19 @@ int CommandListener::ResolverCmd::runCommand(SocketClient *cli, int argc, char * int rc = 0; const char **argv = const_cast(margv); - if (argc < 2) { + if (argc < 3) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Resolver missing arguments", false); return 0; } + unsigned netId = stringToNetId(argv[2]); + // TODO: Consider making NetworkController.isValidNetwork() public + // and making that check here. + if (!strcmp(argv[1], "setnetdns")) { // "resolver setnetdns ..." if (argc >= 5) { - rc = sResolverCtrl->setDnsServers(strtoul(argv[2], NULL, 0), argv[3], &argv[4], argc - 4); + rc = sResolverCtrl->setDnsServers(netId, argv[3], &argv[4], argc - 4); } else { cli->sendMsg(ResponseCode::CommandSyntaxError, "Wrong number of arguments to resolver setnetdns", false); @@ -813,7 +839,7 @@ int CommandListener::ResolverCmd::runCommand(SocketClient *cli, int argc, char * } } else if (!strcmp(argv[1], "clearnetdns")) { // "resolver clearnetdns " if (argc == 3) { - rc = sResolverCtrl->clearDnsServers(strtoul(argv[2], NULL, 0)); + rc = sResolverCtrl->clearDnsServers(netId); } else { cli->sendMsg(ResponseCode::CommandSyntaxError, "Wrong number of arguments to resolver clearnetdns", false); @@ -821,7 +847,7 @@ int CommandListener::ResolverCmd::runCommand(SocketClient *cli, int argc, char * } } else if (!strcmp(argv[1], "flushnet")) { // "resolver flushnet " if (argc == 3) { - rc = sResolverCtrl->flushDnsCache(strtoul(argv[2], NULL, 0)); + rc = sResolverCtrl->flushDnsCache(netId); } else { cli->sendMsg(ResponseCode::CommandSyntaxError, "Wrong number of arguments to resolver flushnet", false); @@ -1249,8 +1275,35 @@ int CommandListener::FirewallCmd::sendGenericOkFail(SocketClient *cli, int cond) FirewallRule CommandListener::FirewallCmd::parseRule(const char* arg) { if (!strcmp(arg, "allow")) { return ALLOW; - } else { + } else if (!strcmp(arg, "deny")) { return DENY; + } else { + ALOGE("failed to parse uid rule (%s)", arg); + return ALLOW; + } +} + +FirewallType CommandListener::FirewallCmd::parseFirewallType(const char* arg) { + if (!strcmp(arg, "whitelist")) { + return WHITELIST; + } else if (!strcmp(arg, "blacklist")) { + return BLACKLIST; + } else { + ALOGE("failed to parse firewall type (%s)", arg); + return BLACKLIST; + } +} + +ChildChain CommandListener::FirewallCmd::parseChildChain(const char* arg) { + if (!strcmp(arg, "dozable")) { + return DOZABLE; + } else if (!strcmp(arg, "standby")) { + return STANDBY; + } else if (!strcmp(arg, "none")) { + return NONE; + } else { + ALOGE("failed to parse child firewall chain (%s)", arg); + return INVALID_CHAIN; } } @@ -1262,7 +1315,14 @@ int CommandListener::FirewallCmd::runCommand(SocketClient *cli, int argc, } if (!strcmp(argv[1], "enable")) { - int res = sFirewallCtrl->enableFirewall(); + if (argc != 3) { + cli->sendMsg(ResponseCode::CommandSyntaxError, + "Usage: firewall enable ", false); + return 0; + } + FirewallType firewallType = parseFirewallType(argv[2]); + + int res = sFirewallCtrl->enableFirewall(firewallType); return sendGenericOkFail(cli, res); } if (!strcmp(argv[1], "disable")) { @@ -1322,17 +1382,49 @@ int CommandListener::FirewallCmd::runCommand(SocketClient *cli, int argc, } if (!strcmp(argv[1], "set_uid_rule")) { - if (argc != 4) { + if (argc != 5) { cli->sendMsg(ResponseCode::CommandSyntaxError, - "Usage: firewall set_uid_rule <1000> ", + "Usage: firewall set_uid_rule <1000> ", false); return 0; } - int uid = atoi(argv[2]); - FirewallRule rule = parseRule(argv[3]); + ChildChain childChain = parseChildChain(argv[2]); + if (childChain == INVALID_CHAIN) { + cli->sendMsg(ResponseCode::CommandSyntaxError, + "Invalid chain name. Valid names are: ", + false); + return 0; + } + int uid = atoi(argv[3]); + FirewallRule rule = parseRule(argv[4]); + int res = sFirewallCtrl->setUidRule(childChain, uid, rule); + return sendGenericOkFail(cli, res); + } - int res = sFirewallCtrl->setUidRule(uid, rule); + if (!strcmp(argv[1], "enable_chain")) { + if (argc != 3) { + cli->sendMsg(ResponseCode::CommandSyntaxError, + "Usage: firewall enable_chain ", + false); + return 0; + } + + ChildChain childChain = parseChildChain(argv[2]); + int res = sFirewallCtrl->enableChildChains(childChain, true); + return sendGenericOkFail(cli, res); + } + + if (!strcmp(argv[1], "disable_chain")) { + if (argc != 3) { + cli->sendMsg(ResponseCode::CommandSyntaxError, + "Usage: firewall disable_chain ", + false); + return 0; + } + + ChildChain childChain = parseChildChain(argv[2]); + int res = sFirewallCtrl->enableChildChains(childChain, false); return sendGenericOkFail(cli, res); } @@ -1633,24 +1725,36 @@ int CommandListener::NetworkCommand::runCommand(SocketClient* client, int argc, if (nextArg == argc) { return syntaxError(client, "Missing id"); } + + bool userPermissions = !strcmp(argv[2], "user"); + bool networkPermissions = !strcmp(argv[2], "network"); + if (!userPermissions && !networkPermissions) { + return syntaxError(client, "Unknown argument"); + } + std::vector ids; for (; nextArg < argc; ++nextArg) { - char* endPtr; - unsigned id = strtoul(argv[nextArg], &endPtr, 0); - if (!*argv[nextArg] || *endPtr) { - return syntaxError(client, "Invalid id"); + if (userPermissions) { + char* endPtr; + unsigned id = strtoul(argv[nextArg], &endPtr, 0); + if (!*argv[nextArg] || *endPtr) { + return syntaxError(client, "Invalid id"); + } + ids.push_back(id); + } else { + // networkPermissions + ids.push_back(stringToNetId(argv[nextArg])); } - ids.push_back(id); } - if (!strcmp(argv[2], "user")) { + if (userPermissions) { sNetCtrl->setPermissionForUsers(permission, ids); - } else if (!strcmp(argv[2], "network")) { + } else { + // networkPermissions if (int ret = sNetCtrl->setPermissionForNetworks(permission, ids)) { return operationError(client, "setPermissionForNetworks() failed", ret); } - } else { - return syntaxError(client, "Unknown argument"); } + return success(client); } diff --git a/server/CommandListener.h b/server/CommandListener.h index b71bc45..72f4da1 100644 --- a/server/CommandListener.h +++ b/server/CommandListener.h @@ -136,6 +136,8 @@ private: protected: int sendGenericOkFail(SocketClient *cli, int cond); static FirewallRule parseRule(const char* arg); + static FirewallType parseFirewallType(const char* arg); + static ChildChain parseChildChain(const char* arg); }; class ClatdCmd : public NetdCommand { diff --git a/server/DnsProxyListener.cpp b/server/DnsProxyListener.cpp index a0f0a30..6c71c5b 100644 --- a/server/DnsProxyListener.cpp +++ b/server/DnsProxyListener.cpp @@ -48,18 +48,14 @@ DnsProxyListener::DnsProxyListener(const NetworkController* netCtrl) : registerCmd(new GetHostByNameCmd(this)); } -DnsProxyListener::GetAddrInfoHandler::GetAddrInfoHandler(SocketClient *c, - char* host, - char* service, - struct addrinfo* hints, - unsigned netId, - uint32_t mark) +DnsProxyListener::GetAddrInfoHandler::GetAddrInfoHandler( + SocketClient *c, char* host, char* service, struct addrinfo* hints, + const struct android_net_context& netcontext) : mClient(c), mHost(host), mService(service), mHints(hints), - mNetId(netId), - mMark(mark) { + mNetContext(netcontext) { } DnsProxyListener::GetAddrInfoHandler::~GetAddrInfoHandler() { @@ -160,11 +156,14 @@ static bool sendaddrinfo(SocketClient* c, struct addrinfo* ai) { void DnsProxyListener::GetAddrInfoHandler::run() { if (DBG) { - ALOGD("GetAddrInfoHandler, now for %s / %s / %u / %u", mHost, mService, mNetId, mMark); + ALOGD("GetAddrInfoHandler, now for %s / %s / {%u,%u,%u,%u,%u}", mHost, mService, + mNetContext.app_netid, mNetContext.app_mark, + mNetContext.dns_netid, mNetContext.dns_mark, + mNetContext.uid); } struct addrinfo* result = NULL; - uint32_t rv = android_getaddrinfofornet(mHost, mService, mHints, mNetId, mMark, &result); + uint32_t rv = android_getaddrinfofornetcontext(mHost, mService, mHints, &mNetContext, &result); if (rv) { // getaddrinfo failed mClient->sendBinaryMsg(ResponseCode::DnsProxyOperationFailed, &rv, sizeof(rv)); @@ -229,7 +228,8 @@ int DnsProxyListener::GetAddrInfoCmd::runCommand(SocketClient *cli, unsigned netId = strtoul(argv[7], NULL, 10); uid_t uid = cli->getUid(); - uint32_t mark = mDnsProxyListener->mNetCtrl->getNetworkForDns(&netId, uid); + struct android_net_context netcontext; + mDnsProxyListener->mNetCtrl->getNetworkContext(netId, uid, &netcontext); if (ai_flags != -1 || ai_family != -1 || ai_socktype != -1 || ai_protocol != -1) { @@ -242,21 +242,23 @@ int DnsProxyListener::GetAddrInfoCmd::runCommand(SocketClient *cli, // Only implement AI_ADDRCONFIG if application is using default network since our // implementation only works on the default network. if ((hints->ai_flags & AI_ADDRCONFIG) && - netId != mDnsProxyListener->mNetCtrl->getDefaultNetwork()) { + netcontext.dns_netid != mDnsProxyListener->mNetCtrl->getDefaultNetwork()) { hints->ai_flags &= ~AI_ADDRCONFIG; } } if (DBG) { - ALOGD("GetAddrInfoHandler for %s / %s / %u / %d / %u", + ALOGD("GetAddrInfoHandler for %s / %s / {%u,%u,%u,%u,%u}", name ? name : "[nullhost]", service ? service : "[nullservice]", - netId, uid, mark); + netcontext.app_netid, netcontext.app_mark, + netcontext.dns_netid, netcontext.dns_mark, + netcontext.uid); } cli->incRef(); DnsProxyListener::GetAddrInfoHandler* handler = - new DnsProxyListener::GetAddrInfoHandler(cli, name, service, hints, netId, mark); + new DnsProxyListener::GetAddrInfoHandler(cli, name, service, hints, netcontext); handler->start(); return 0; diff --git a/server/DnsProxyListener.h b/server/DnsProxyListener.h index 106961e..bd7251d 100644 --- a/server/DnsProxyListener.h +++ b/server/DnsProxyListener.h @@ -17,6 +17,7 @@ #ifndef _DNSPROXYLISTENER_H__ #define _DNSPROXYLISTENER_H__ +#include // struct android_net_context #include #include "NetdCommand.h" @@ -46,8 +47,7 @@ private: char* host, char* service, struct addrinfo* hints, - unsigned netId, - uint32_t mark); + const struct android_net_context& netcontext); ~GetAddrInfoHandler(); static void* threadStart(void* handler); @@ -59,8 +59,7 @@ private: char* mHost; // owned char* mService; // owned struct addrinfo* mHints; // owned - unsigned mNetId; - uint32_t mMark; + struct android_net_context mNetContext; }; /* ------ gethostbyname ------*/ diff --git a/server/DummyNetwork.cpp b/server/DummyNetwork.cpp new file mode 100644 index 0000000..a3b3435 --- /dev/null +++ b/server/DummyNetwork.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2014 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 "DummyNetwork.h" + +#include "RouteController.h" + +#define LOG_TAG "Netd" +#include "log/log.h" +#include "errno.h" + +const char* DummyNetwork::INTERFACE_NAME = "dummy0"; + +DummyNetwork::DummyNetwork(unsigned netId) : Network(netId) { + mInterfaces.insert(INTERFACE_NAME); +} + +DummyNetwork::~DummyNetwork() { +} + +Network::Type DummyNetwork::getType() const { + return DUMMY; +} + +int DummyNetwork::addInterface(const std::string& /* interface */) { + return -EINVAL; +} + +int DummyNetwork::removeInterface(const std::string& /* interface */) { + return -EINVAL; +} diff --git a/server/DummyNetwork.h b/server/DummyNetwork.h new file mode 100644 index 0000000..7bc0d3d --- /dev/null +++ b/server/DummyNetwork.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2014 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. + */ + +#ifndef NETD_SERVER_DUMMY_NETWORK_H +#define NETD_SERVER_DUMMY_NETWORK_H + +#include "Network.h" + +class DummyNetwork : public Network { +public: + static const char* INTERFACE_NAME; + explicit DummyNetwork(unsigned netId); + virtual ~DummyNetwork(); + +private: + Type getType() const override; + int addInterface(const std::string& interface) override WARN_UNUSED_RESULT; + int removeInterface(const std::string& interface) override WARN_UNUSED_RESULT; +}; + +#endif // NETD_SERVER_DUMMY_NETWORK_H diff --git a/server/FirewallController.cpp b/server/FirewallController.cpp index 17c6da4..bcf7524 100644 --- a/server/FirewallController.cpp +++ b/server/FirewallController.cpp @@ -23,38 +23,61 @@ #define LOG_NDEBUG 0 #include +#include #include "NetdConstants.h" #include "FirewallController.h" +const char* FirewallController::TABLE = "filter"; + const char* FirewallController::LOCAL_INPUT = "fw_INPUT"; const char* FirewallController::LOCAL_OUTPUT = "fw_OUTPUT"; const char* FirewallController::LOCAL_FORWARD = "fw_FORWARD"; +const char* FirewallController::LOCAL_DOZABLE = "fw_dozable"; +const char* FirewallController::LOCAL_STANDBY = "fw_standby"; + FirewallController::FirewallController(void) { + // If no rules are set, it's in BLACKLIST mode + mFirewallType = BLACKLIST; } int FirewallController::setupIptablesHooks(void) { - return 0; + int res = 0; + // child chains are created but not attached, they will be attached explicitly. + FirewallType firewallType = getFirewallType(DOZABLE); + res |= createChain(LOCAL_DOZABLE, LOCAL_INPUT, firewallType); + + firewallType = getFirewallType(STANDBY); + res |= createChain(LOCAL_STANDBY, LOCAL_INPUT, firewallType); + + return res; } -int FirewallController::enableFirewall(void) { +int FirewallController::enableFirewall(FirewallType ftype) { int res = 0; + if (mFirewallType != ftype) { + // flush any existing rules + disableFirewall(); - // flush any existing rules - disableFirewall(); - - // create default rule to drop all traffic - res |= execIptables(V4V6, "-A", LOCAL_INPUT, "-j", "DROP", NULL); - res |= execIptables(V4V6, "-A", LOCAL_OUTPUT, "-j", "REJECT", NULL); - res |= execIptables(V4V6, "-A", LOCAL_FORWARD, "-j", "REJECT", NULL); + if (ftype == WHITELIST) { + // create default rule to drop all traffic + res |= execIptables(V4V6, "-A", LOCAL_INPUT, "-j", "DROP", NULL); + res |= execIptables(V4V6, "-A", LOCAL_OUTPUT, "-j", "REJECT", NULL); + res |= execIptables(V4V6, "-A", LOCAL_FORWARD, "-j", "REJECT", NULL); + } + // Set this after calling disableFirewall(), since it defaults to WHITELIST there + mFirewallType = ftype; + } return res; } int FirewallController::disableFirewall(void) { int res = 0; + mFirewallType = WHITELIST; + // flush any existing rules res |= execIptables(V4V6, "-F", LOCAL_INPUT, NULL); res |= execIptables(V4V6, "-F", LOCAL_OUTPUT, NULL); @@ -63,12 +86,41 @@ int FirewallController::disableFirewall(void) { return res; } +int FirewallController::enableChildChains(ChildChain chain, bool enable) { + int res = 0; + const char* name; + switch(chain) { + case DOZABLE: + name = LOCAL_DOZABLE; + break; + case STANDBY: + name = LOCAL_STANDBY; + break; + default: + return res; + } + + if (enable) { + res |= attachChain(name, LOCAL_INPUT); + res |= attachChain(name, LOCAL_OUTPUT); + } else { + res |= detachChain(name, LOCAL_INPUT); + res |= detachChain(name, LOCAL_OUTPUT); + } + return res; +} + int FirewallController::isFirewallEnabled(void) { // TODO: verify that rules are still in place near top return -1; } int FirewallController::setInterfaceRule(const char* iface, FirewallRule rule) { + if (mFirewallType == BLACKLIST) { + // Unsupported in BLACKLIST mode + return -1; + } + if (!isIfaceName(iface)) { errno = ENOENT; return -1; @@ -88,6 +140,11 @@ int FirewallController::setInterfaceRule(const char* iface, FirewallRule rule) { } int FirewallController::setEgressSourceRule(const char* addr, FirewallRule rule) { + if (mFirewallType == BLACKLIST) { + // Unsupported in BLACKLIST mode + return -1; + } + IptablesTarget target = V4; if (strchr(addr, ':')) { target = V6; @@ -108,6 +165,11 @@ int FirewallController::setEgressSourceRule(const char* addr, FirewallRule rule) int FirewallController::setEgressDestRule(const char* addr, int protocol, int port, FirewallRule rule) { + if (mFirewallType == BLACKLIST) { + // Unsupported in BLACKLIST mode + return -1; + } + IptablesTarget target = V4; if (strchr(addr, ':')) { target = V6; @@ -134,21 +196,81 @@ int FirewallController::setEgressDestRule(const char* addr, int protocol, int po return res; } -int FirewallController::setUidRule(int uid, FirewallRule rule) { +FirewallType FirewallController::getFirewallType(ChildChain chain) { + switch(chain) { + case DOZABLE: + return WHITELIST; + case STANDBY: + return BLACKLIST; + case NONE: + return mFirewallType; + default: + return BLACKLIST; + } +} + +int FirewallController::setUidRule(ChildChain chain, int uid, FirewallRule rule) { char uidStr[16]; sprintf(uidStr, "%d", uid); const char* op; - if (rule == ALLOW) { - op = "-I"; - } else { - op = "-D"; + const char* target; + FirewallType firewallType = getFirewallType(chain); + if (firewallType == WHITELIST) { + target = "RETURN"; + op = (rule == ALLOW)? "-I" : "-D"; + } else { // BLACKLIST mode + target = "DROP"; + op = (rule == DENY)? "-I" : "-D"; } int res = 0; - res |= execIptables(V4V6, op, LOCAL_INPUT, "-m", "owner", "--uid-owner", uidStr, - "-j", "RETURN", NULL); - res |= execIptables(V4V6, op, LOCAL_OUTPUT, "-m", "owner", "--uid-owner", uidStr, - "-j", "RETURN", NULL); + switch(chain) { + case DOZABLE: + res |= execIptables(V4V6, op, LOCAL_DOZABLE, "-m", "owner", "--uid-owner", + uidStr, "-j", target, NULL); + break; + case STANDBY: + res |= execIptables(V4V6, op, LOCAL_STANDBY, "-m", "owner", "--uid-owner", + uidStr, "-j", target, NULL); + break; + case NONE: + res |= execIptables(V4V6, op, LOCAL_INPUT, "-m", "owner", "--uid-owner", uidStr, + "-j", target, NULL); + res |= execIptables(V4V6, op, LOCAL_OUTPUT, "-m", "owner", "--uid-owner", uidStr, + "-j", target, NULL); + break; + default: + ALOGW("Unknown child chain: %d", chain); + break; + } + return res; +} + +int FirewallController::attachChain(const char* childChain, const char* parentChain) { + return execIptables(V4V6, "-t", TABLE, "-A", parentChain, "-j", childChain, NULL); +} + +int FirewallController::detachChain(const char* childChain, const char* parentChain) { + return execIptables(V4V6, "-t", TABLE, "-D", parentChain, "-j", childChain, NULL); +} + +int FirewallController::createChain(const char* childChain, + const char* parentChain, FirewallType type) { + // Order is important, otherwise later steps may fail. + execIptablesSilently(V4V6, "-t", TABLE, "-D", parentChain, "-j", childChain, NULL); + execIptablesSilently(V4V6, "-t", TABLE, "-F", childChain, NULL); + execIptablesSilently(V4V6, "-t", TABLE, "-X", childChain, NULL); + int res = 0; + res |= execIptables(V4V6, "-t", TABLE, "-N", childChain, NULL); + if (type == WHITELIST) { + // create default white list for system uid range + char uidStr[16]; + sprintf(uidStr, "0-%d", AID_APP - 1); + res |= execIptables(V4V6, "-A", childChain, "-m", "owner", "--uid-owner", + uidStr, "-j", "RETURN", NULL); + // create default rule to drop all traffic + res |= execIptables(V4V6, "-A", childChain, "-j", "DROP", NULL); + } return res; } diff --git a/server/FirewallController.h b/server/FirewallController.h index 158e0fa..b32072e 100644 --- a/server/FirewallController.h +++ b/server/FirewallController.h @@ -19,7 +19,14 @@ #include -enum FirewallRule { ALLOW, DENY }; +enum FirewallRule { DENY, ALLOW }; + +// WHITELIST means the firewall denies all by default, uids must be explicitly ALLOWed +// BLACKLIST means the firewall allows all by default, uids must be explicitly DENYed + +enum FirewallType { WHITELIST, BLACKLIST }; + +enum ChildChain { NONE, DOZABLE, STANDBY, INVALID_CHAIN }; #define PROTOCOL_TCP 6 #define PROTOCOL_UDP 17 @@ -34,7 +41,7 @@ public: int setupIptablesHooks(void); - int enableFirewall(void); + int enableFirewall(FirewallType); int disableFirewall(void); int isFirewallEnabled(void); @@ -44,13 +51,25 @@ public: int setEgressSourceRule(const char*, FirewallRule); /* Match traffic coming-in-from or going-out-to given address, port, and protocol. */ int setEgressDestRule(const char*, int, int, FirewallRule); - /* Match traffic owned by given UID. */ - int setUidRule(int, FirewallRule); + /* Match traffic owned by given UID. This is specific to a particular chain. */ + int setUidRule(ChildChain, int, FirewallRule); + + int enableChildChains(ChildChain, bool); + + static const char* TABLE; static const char* LOCAL_INPUT; static const char* LOCAL_OUTPUT; static const char* LOCAL_FORWARD; + static const char* LOCAL_DOZABLE; + static const char* LOCAL_STANDBY; +private: + FirewallType mFirewallType; + int attachChain(const char*, const char*); + int detachChain(const char*, const char*); + int createChain(const char*, const char*, FirewallType); + FirewallType getFirewallType(ChildChain); }; #endif diff --git a/server/FwmarkServer.cpp b/server/FwmarkServer.cpp index b11e075..530e96a 100644 --- a/server/FwmarkServer.cpp +++ b/server/FwmarkServer.cpp @@ -75,6 +75,15 @@ int FwmarkServer::processClient(SocketClient* client, int* socketFd) { return -EBADMSG; } + Permission permission = mNetworkController->getPermissionForUser(client->getUid()); + + if (command.cmdId == FwmarkCommand::QUERY_USER_ACCESS) { + if ((permission & PERMISSION_SYSTEM) != PERMISSION_SYSTEM) { + return -EPERM; + } + return mNetworkController->checkUserNetworkAccess(command.uid, command.netId); + } + cmsghdr* const cmsgh = CMSG_FIRSTHDR(&message); if (cmsgh && cmsgh->cmsg_level == SOL_SOCKET && cmsgh->cmsg_type == SCM_RIGHTS && cmsgh->cmsg_len == CMSG_LEN(sizeof(*socketFd))) { @@ -91,8 +100,6 @@ int FwmarkServer::processClient(SocketClient* client, int* socketFd) { return -errno; } - Permission permission = mNetworkController->getPermissionForUser(client->getUid()); - switch (command.cmdId) { case FwmarkCommand::ON_ACCEPT: { // Called after a socket accept(). The kernel would've marked the NetId and necessary diff --git a/server/IdletimerController.cpp b/server/IdletimerController.cpp index ae7ae04..e6306fd 100644 --- a/server/IdletimerController.cpp +++ b/server/IdletimerController.cpp @@ -154,6 +154,7 @@ int IdletimerController::setDefaults() { int res; const char *cmd1[] = { NULL, // To be filled inside runIpxtablesCmd + "-w", "-t", "raw", "-F", @@ -166,6 +167,7 @@ int IdletimerController::setDefaults() { const char *cmd2[] = { NULL, // To be filled inside runIpxtablesCmd + "-w", "-t", "mangle", "-F", @@ -201,6 +203,7 @@ int IdletimerController::modifyInterfaceIdletimer(IptOp op, const char *iface, const char *cmd1[] = { NULL, // To be filled inside runIpxtablesCmd + "-w", "-t", "raw", (op == IptOpAdd) ? "-A" : "-D", @@ -223,6 +226,7 @@ int IdletimerController::modifyInterfaceIdletimer(IptOp op, const char *iface, const char *cmd2[] = { NULL, // To be filled inside runIpxtablesCmd + "-w", "-t", "mangle", (op == IptOpAdd) ? "-A" : "-D", diff --git a/server/InterfaceController.cpp b/server/InterfaceController.cpp index 76f5e05..3882bcc 100644 --- a/server/InterfaceController.cpp +++ b/server/InterfaceController.cpp @@ -27,16 +27,61 @@ #include "InterfaceController.h" #include "RouteController.h" -using android::base::ReadFileToString; using android::base::StringPrintf; using android::base::WriteStringToFile; +namespace { + const char ipv6_proc_path[] = "/proc/sys/net/ipv6/conf"; +const char ipv4_neigh_conf_dir[] = "/proc/sys/net/ipv4/neigh"; + +const char ipv6_neigh_conf_dir[] = "/proc/sys/net/ipv6/neigh"; + const char sys_net_path[] = "/sys/class/net"; const char wl_util_path[] = "/system/xbin/wlutil"; +bool isInterfaceName(const char *name) { + return strcmp(name, ".") && + strcmp(name, "..") && + strcmp(name, "default") && + strcmp(name, "all"); +} + +int writeValueToPath( + const char* dirname, const char* subdirname, const char* basename, + const char* value) { + std::string path(StringPrintf("%s/%s/%s", dirname, subdirname, basename)); + return WriteStringToFile(value, path) ? 0 : -1; +} + +void setOnAllInterfaces(const char* dirname, const char* basename, const char* value) { + // Set the default value, which is used by any interfaces that are created in the future. + writeValueToPath(dirname, "default", basename, value); + + // Set the value on all the interfaces that currently exist. + DIR* dir = opendir(dirname); + if (!dir) { + ALOGE("Can't list %s: %s", dirname, strerror(errno)); + return; + } + dirent* d; + while ((d = readdir(dir))) { + if ((d->d_type != DT_DIR) || !isInterfaceName(d->d_name)) { + continue; + } + writeValueToPath(dirname, d->d_name, basename, value); + } + closedir(dir); +} + +void setIPv6UseOutgoingInterfaceAddrsOnly(const char *value) { + setOnAllInterfaces(ipv6_proc_path, "use_oif_addrs_only", value); +} + +} // namespace + InterfaceController::InterfaceController() { // Initial IPv6 settings. // By default, accept_ra is set to 1 (accept RAs unless forwarding is on) on all interfaces. @@ -49,32 +94,38 @@ InterfaceController::InterfaceController() { // Enable optimistic DAD for IPv6 addresses on all interfaces. setIPv6OptimisticMode("1"); -} -InterfaceController::~InterfaceController() { + // Reduce the ARP/ND base reachable time from the default (30sec) to 15sec. + setBaseReachableTimeMs(15 * 1000); + + // When sending traffic via a given interface use only addresses configured + // on that interface as possible source addresses. + setIPv6UseOutgoingInterfaceAddrsOnly("1"); } -int InterfaceController::writeIPv6ProcPath(const char *interface, const char *setting, const char *value) { - if (!isIfaceName(interface)) { - errno = ENOENT; - return -1; - } - std::string path(StringPrintf("%s/%s/%s", ipv6_proc_path, interface, setting)); - return WriteStringToFile(value, path); +InterfaceController::~InterfaceController() { } int InterfaceController::setEnableIPv6(const char *interface, const int on) { - // When disable_ipv6 changes from 1 to 0, the kernel starts autoconf. - // When disable_ipv6 changes from 0 to 1, the kernel clears all autoconf - // addresses and routes and disables IPv6 on the interface. - const char *disable_ipv6 = on ? "0" : "1"; - return writeIPv6ProcPath(interface, "disable_ipv6", disable_ipv6); + if (!isIfaceName(interface)) { + errno = ENOENT; + return -1; + } + // When disable_ipv6 changes from 1 to 0, the kernel starts autoconf. + // When disable_ipv6 changes from 0 to 1, the kernel clears all autoconf + // addresses and routes and disables IPv6 on the interface. + const char *disable_ipv6 = on ? "0" : "1"; + return writeValueToPath(ipv6_proc_path, interface, "disable_ipv6", disable_ipv6); } int InterfaceController::setIPv6PrivacyExtensions(const char *interface, const int on) { - // 0: disable IPv6 privacy addresses - // 0: enable IPv6 privacy addresses and prefer them over non-privacy ones. - return writeIPv6ProcPath(interface, "use_tempaddr", on ? "2" : "0"); + if (!isIfaceName(interface)) { + errno = ENOENT; + return -1; + } + // 0: disable IPv6 privacy addresses + // 0: enable IPv6 privacy addresses and prefer them over non-privacy ones. + return writeValueToPath(ipv6_proc_path, interface, "use_tempaddr", on ? "2" : "0"); } // Enables or disables IPv6 ND offload. This is useful for 464xlat on wifi, IPv6 tethering, and @@ -100,34 +151,8 @@ int InterfaceController::setIPv6NdOffload(char* interface, const int on) { } } -int InterfaceController::isInterfaceName(const char *name) { - return strcmp(name, ".") && - strcmp(name, "..") && - strcmp(name, "default") && - strcmp(name, "all"); -} - -void InterfaceController::setOnAllInterfaces(const char* filename, const char* value) { - // Set the default value, which is used by any interfaces that are created in the future. - writeIPv6ProcPath("default", filename, value); - - // Set the value on all the interfaces that currently exist. - DIR* dir = opendir(ipv6_proc_path); - if (!dir) { - ALOGE("Can't list %s: %s", ipv6_proc_path, strerror(errno)); - return; - } - dirent* d; - while ((d = readdir(dir))) { - if (d->d_type == DT_DIR && isInterfaceName(d->d_name)) { - writeIPv6ProcPath(d->d_name, filename, value); - } - } - closedir(dir); -} - void InterfaceController::setAcceptRA(const char *value) { - setOnAllInterfaces("accept_ra", value); + setOnAllInterfaces(ipv6_proc_path, "accept_ra", value); } // |tableOrOffset| is interpreted as: @@ -137,21 +162,26 @@ void InterfaceController::setAcceptRA(const char *value) { // ID to get the table. If it's set to -1000, routes from interface ID 5 will go into // table 1005, etc. void InterfaceController::setAcceptRARouteTable(int tableOrOffset) { - std::string value(StringPrintf("%d", tableOrOffset)); - setOnAllInterfaces("accept_ra_rt_table", value.c_str()); + std::string value(StringPrintf("%d", tableOrOffset)); + setOnAllInterfaces(ipv6_proc_path, "accept_ra_rt_table", value.c_str()); } int InterfaceController::setMtu(const char *interface, const char *mtu) { - if (!isIfaceName(interface)) { - errno = ENOENT; - return -1; - } - std::string path(StringPrintf("%s/%s/mtu", sys_net_path, interface)); - return WriteStringToFile(mtu, path); + if (!isIfaceName(interface)) { + errno = ENOENT; + return -1; + } + return writeValueToPath(sys_net_path, interface, "mtu", mtu); +} + +void InterfaceController::setBaseReachableTimeMs(unsigned int millis) { + std::string value(StringPrintf("%u", millis)); + setOnAllInterfaces(ipv4_neigh_conf_dir, "base_reachable_time_ms", value.c_str()); + setOnAllInterfaces(ipv6_neigh_conf_dir, "base_reachable_time_ms", value.c_str()); } void InterfaceController::setIPv6OptimisticMode(const char *value) { - setOnAllInterfaces("optimistic_dad", value); - setOnAllInterfaces("use_optimistic", value); + setOnAllInterfaces(ipv6_proc_path, "optimistic_dad", value); + setOnAllInterfaces(ipv6_proc_path, "use_optimistic", value); } diff --git a/server/InterfaceController.h b/server/InterfaceController.h index 60e2131..89728b1 100644 --- a/server/InterfaceController.h +++ b/server/InterfaceController.h @@ -27,11 +27,9 @@ class InterfaceController { int setMtu(const char *interface, const char *mtu); private: - int writeIPv6ProcPath(const char *interface, const char *setting, const char *value); - int isInterfaceName(const char *name); - void setOnAllInterfaces(const char* filename, const char* value); void setAcceptRA(const char* value); void setAcceptRARouteTable(int tableOrOffset); + void setBaseReachableTimeMs(unsigned int millis); void setIPv6OptimisticMode(const char *value); }; diff --git a/server/NatController.cpp b/server/NatController.cpp index e66d971..19d19c7 100644 --- a/server/NatController.cpp +++ b/server/NatController.cpp @@ -95,10 +95,10 @@ int NatController::setupIptablesHooks() { * Bug 17629786 asks to make the failure more obvious, or even fatal * so that all builds eventually gain the performance improvement. */ - {{IPTABLES_PATH, "-F", LOCAL_TETHER_COUNTERS_CHAIN,}, 0}, - {{IPTABLES_PATH, "-X", LOCAL_TETHER_COUNTERS_CHAIN,}, 0}, - {{IPTABLES_PATH, "-N", LOCAL_TETHER_COUNTERS_CHAIN,}, 1}, - {{IPTABLES_PATH, "-t", "mangle", "-A", LOCAL_MANGLE_FORWARD, "-p", "tcp", "--tcp-flags", + {{IPTABLES_PATH, "-w", "-F", LOCAL_TETHER_COUNTERS_CHAIN,}, 0}, + {{IPTABLES_PATH, "-w", "-X", LOCAL_TETHER_COUNTERS_CHAIN,}, 0}, + {{IPTABLES_PATH, "-w", "-N", LOCAL_TETHER_COUNTERS_CHAIN,}, 1}, + {{IPTABLES_PATH, "-w", "-t", "mangle", "-A", LOCAL_MANGLE_FORWARD, "-p", "tcp", "--tcp-flags", "SYN", "SYN", "-j", "TCPMSS", "--clamp-mss-to-pmtu"}, 0}, }; for (unsigned int cmdNum = 0; cmdNum < ARRAY_SIZE(defaultCommands); cmdNum++) { @@ -120,9 +120,9 @@ int NatController::setDefaults() { * - internally it will be memcopied to an array and terminated with a NULL. */ struct CommandsAndArgs defaultCommands[] = { - {{IPTABLES_PATH, "-F", LOCAL_FORWARD,}, 1}, - {{IPTABLES_PATH, "-A", LOCAL_FORWARD, "-j", "DROP"}, 1}, - {{IPTABLES_PATH, "-t", "nat", "-F", LOCAL_NAT_POSTROUTING}, 1}, + {{IPTABLES_PATH, "-w", "-F", LOCAL_FORWARD,}, 1}, + {{IPTABLES_PATH, "-w", "-A", LOCAL_FORWARD, "-j", "DROP"}, 1}, + {{IPTABLES_PATH, "-w", "-t", "nat", "-F", LOCAL_NAT_POSTROUTING}, 1}, }; for (unsigned int cmdNum = 0; cmdNum < ARRAY_SIZE(defaultCommands); cmdNum++) { if (runCmd(ARRAY_SIZE(defaultCommands[cmdNum].cmd), defaultCommands[cmdNum].cmd) && @@ -155,6 +155,7 @@ int NatController::enableNat(const char* intIface, const char* extIface) { if (natCount == 0) { const char *cmd[] = { IPTABLES_PATH, + "-w", "-t", "nat", "-A", @@ -184,6 +185,7 @@ int NatController::enableNat(const char* intIface, const char* extIface) { /* Always make sure the drop rule is at the end */ const char *cmd1[] = { IPTABLES_PATH, + "-w", "-D", LOCAL_FORWARD, "-j", @@ -192,6 +194,7 @@ int NatController::enableNat(const char* intIface, const char* extIface) { runCmd(ARRAY_SIZE(cmd1), cmd1); const char *cmd2[] = { IPTABLES_PATH, + "-w", "-A", LOCAL_FORWARD, "-j", @@ -199,15 +202,6 @@ int NatController::enableNat(const char* intIface, const char* extIface) { }; runCmd(ARRAY_SIZE(cmd2), cmd2); - if (int ret = RouteController::enableTethering(intIface, extIface)) { - ALOGE("failed to add tethering rule for iif=%s oif=%s", intIface, extIface); - if (natCount == 0) { - setDefaults(); - } - errno = -ret; - return -1; - } - natCount++; return 0; } @@ -239,6 +233,7 @@ int NatController::setTetherCountingRules(bool add, const char *intIface, const } const char *cmd2b[] = { IPTABLES_PATH, + "-w", "-A", LOCAL_TETHER_COUNTERS_CHAIN, "-i", @@ -264,6 +259,7 @@ int NatController::setTetherCountingRules(bool add, const char *intIface, const const char *cmd3b[] = { IPTABLES_PATH, + "-w", "-A", LOCAL_TETHER_COUNTERS_CHAIN, "-i", @@ -287,6 +283,7 @@ int NatController::setTetherCountingRules(bool add, const char *intIface, const int NatController::setForwardRules(bool add, const char *intIface, const char *extIface) { const char *cmd1[] = { IPTABLES_PATH, + "-w", add ? "-A" : "-D", LOCAL_FORWARD, "-i", @@ -308,6 +305,7 @@ int NatController::setForwardRules(bool add, const char *intIface, const char *e const char *cmd2[] = { IPTABLES_PATH, + "-w", add ? "-A" : "-D", LOCAL_FORWARD, "-i", @@ -324,6 +322,7 @@ int NatController::setForwardRules(bool add, const char *intIface, const char *e const char *cmd3[] = { IPTABLES_PATH, + "-w", add ? "-A" : "-D", LOCAL_FORWARD, "-i", @@ -354,10 +353,10 @@ int NatController::setForwardRules(bool add, const char *intIface, const char *e return 0; err_return: - cmd2[1] = "-D"; + cmd2[2] = "-D"; runCmd(ARRAY_SIZE(cmd2), cmd2); err_invalid_drop: - cmd1[1] = "-D"; + cmd1[2] = "-D"; runCmd(ARRAY_SIZE(cmd1), cmd1); return rc; } @@ -368,12 +367,6 @@ int NatController::disableNat(const char* intIface, const char* extIface) { return -1; } - if (int ret = RouteController::disableTethering(intIface, extIface)) { - ALOGE("failed to remove tethering rule for iif=%s oif=%s", intIface, extIface); - errno = -ret; - return -1; - } - setForwardRules(false, intIface, extIface); if (--natCount <= 0) { // handle decrement to 0 case (do reset to defaults) and erroneous dec below 0 diff --git a/server/NetdConstants.cpp b/server/NetdConstants.cpp index d2e0bbe..c86538b 100644 --- a/server/NetdConstants.cpp +++ b/server/NetdConstants.cpp @@ -73,6 +73,10 @@ static int execIptables(IptablesTarget target, bool silent, va_list args) { std::list argsList; argsList.push_back(NULL); const char* arg; + + // Wait to avoid failure due to another process holding the lock + argsList.push_back("-w"); + do { arg = va_arg(args, const char *); argsList.push_back(arg); diff --git a/server/NetlinkHandler.cpp b/server/NetlinkHandler.cpp index 0a5a3f0..97dc3e0 100644 --- a/server/NetlinkHandler.cpp +++ b/server/NetlinkHandler.cpp @@ -58,26 +58,26 @@ void NetlinkHandler::onEvent(NetlinkEvent *evt) { } if (!strcmp(subsys, "net")) { - int action = evt->getAction(); + NetlinkEvent::Action action = evt->getAction(); const char *iface = evt->findParam("INTERFACE"); - if (action == evt->NlActionAdd) { + if (action == NetlinkEvent::Action::kAdd) { notifyInterfaceAdded(iface); - } else if (action == evt->NlActionRemove) { + } else if (action == NetlinkEvent::Action::kRemove) { notifyInterfaceRemoved(iface); - } else if (action == evt->NlActionChange) { + } else if (action == NetlinkEvent::Action::kChange) { evt->dump(); notifyInterfaceChanged("nana", true); - } else if (action == evt->NlActionLinkUp) { + } else if (action == NetlinkEvent::Action::kLinkUp) { notifyInterfaceLinkChanged(iface, true); - } else if (action == evt->NlActionLinkDown) { + } else if (action == NetlinkEvent::Action::kLinkDown) { notifyInterfaceLinkChanged(iface, false); - } else if (action == evt->NlActionAddressUpdated || - action == evt->NlActionAddressRemoved) { + } else if (action == NetlinkEvent::Action::kAddressUpdated || + action == NetlinkEvent::Action::kAddressRemoved) { const char *address = evt->findParam("ADDRESS"); const char *flags = evt->findParam("FLAGS"); const char *scope = evt->findParam("SCOPE"); - if (action == evt->NlActionAddressRemoved && iface && address) { + if (action == NetlinkEvent::Action::kAddressRemoved && iface && address) { int resetMask = strchr(address, ':') ? RESET_IPV6_ADDRESSES : RESET_IPV4_ADDRESSES; resetMask |= RESET_IGNORE_INTERFACE_ADDRESS; if (int ret = ifc_reset_connections(iface, resetMask)) { @@ -88,14 +88,14 @@ void NetlinkHandler::onEvent(NetlinkEvent *evt) { if (iface && flags && scope) { notifyAddressChanged(action, address, iface, flags, scope); } - } else if (action == evt->NlActionRdnss) { + } else if (action == NetlinkEvent::Action::kRdnss) { const char *lifetime = evt->findParam("LIFETIME"); const char *servers = evt->findParam("SERVERS"); if (lifetime && servers) { notifyInterfaceDnsServers(iface, lifetime, servers); } - } else if (action == evt->NlActionRouteUpdated || - action == evt->NlActionRouteRemoved) { + } else if (action == NetlinkEvent::Action::kRouteUpdated || + action == NetlinkEvent::Action::kRouteRemoved) { const char *route = evt->findParam("ROUTE"); const char *gateway = evt->findParam("GATEWAY"); const char *iface = evt->findParam("INTERFACE"); @@ -118,8 +118,10 @@ void NetlinkHandler::onEvent(NetlinkEvent *evt) { const char *label = evt->findParam("INTERFACE"); const char *state = evt->findParam("STATE"); const char *timestamp = evt->findParam("TIME_NS"); + const char *uid = evt->findParam("UID"); if (state) - notifyInterfaceClassActivity(label, !strcmp("active", state), timestamp); + notifyInterfaceClassActivity(label, !strcmp("active", state), + timestamp, uid); #if !LOG_NDEBUG } else if (strcmp(subsys, "platform") && strcmp(subsys, "backlight")) { @@ -165,21 +167,26 @@ void NetlinkHandler::notifyQuotaLimitReached(const char *name, const char *iface } void NetlinkHandler::notifyInterfaceClassActivity(const char *name, - bool isActive, const char *timestamp) { + bool isActive, + const char *timestamp, + const char *uid) { if (timestamp == NULL) notify(ResponseCode::InterfaceClassActivity, "IfaceClass %s %s", isActive ? "active" : "idle", name); + else if (uid != NULL && isActive) + notify(ResponseCode::InterfaceClassActivity, + "IfaceClass active %s %s %s", name, timestamp, uid); else notify(ResponseCode::InterfaceClassActivity, "IfaceClass %s %s %s", isActive ? "active" : "idle", name, timestamp); } -void NetlinkHandler::notifyAddressChanged(int action, const char *addr, +void NetlinkHandler::notifyAddressChanged(NetlinkEvent::Action action, const char *addr, const char *iface, const char *flags, const char *scope) { notify(ResponseCode::InterfaceAddressChange, "Address %s %s %s %s %s", - (action == NetlinkEvent::NlActionAddressUpdated) ? kUpdated : kRemoved, + (action == NetlinkEvent::Action::kAddressUpdated) ? kUpdated : kRemoved, addr, iface, flags, scope); } @@ -190,11 +197,11 @@ void NetlinkHandler::notifyInterfaceDnsServers(const char *iface, iface, lifetime, servers); } -void NetlinkHandler::notifyRouteChange(int action, const char *route, +void NetlinkHandler::notifyRouteChange(NetlinkEvent::Action action, const char *route, const char *gateway, const char *iface) { notify(ResponseCode::RouteChange, "Route %s %s%s%s%s%s", - (action == NetlinkEvent::NlActionRouteUpdated) ? kUpdated : kRemoved, + (action == NetlinkEvent::Action::kRouteUpdated) ? kUpdated : kRemoved, route, *gateway ? " via " : "", gateway, diff --git a/server/NetlinkHandler.h b/server/NetlinkHandler.h index bee52dc..d97c864 100644 --- a/server/NetlinkHandler.h +++ b/server/NetlinkHandler.h @@ -17,6 +17,7 @@ #ifndef _NETLINKHANDLER_H #define _NETLINKHANDLER_H +#include #include #include "NetlinkManager.h" @@ -40,12 +41,12 @@ protected: void notifyInterfaceLinkChanged(const char *name, bool isUp); void notifyQuotaLimitReached(const char *name, const char *iface); void notifyInterfaceClassActivity(const char *name, bool isActive, - const char *timestamp); - void notifyAddressChanged(int action, const char *addr, const char *iface, + const char *timestamp, const char *uid); + void notifyAddressChanged(NetlinkEvent::Action action, const char *addr, const char *iface, const char *flags, const char *scope); void notifyInterfaceDnsServers(const char *iface, const char *lifetime, const char *servers); - void notifyRouteChange(int action, const char *route, const char *gateway, const char *iface); + void notifyRouteChange(NetlinkEvent::Action action, const char *route, const char *gateway, const char *iface); void notifyStrictCleartext(const char* uid, const char* hex); }; #endif diff --git a/server/Network.h b/server/Network.h index 115997a..3af53d9 100644 --- a/server/Network.h +++ b/server/Network.h @@ -26,6 +26,7 @@ class Network { public: enum Type { + DUMMY, LOCAL, PHYSICAL, VIRTUAL, diff --git a/server/NetworkController.cpp b/server/NetworkController.cpp index 20d8e97..93a0763 100644 --- a/server/NetworkController.cpp +++ b/server/NetworkController.cpp @@ -32,6 +32,7 @@ #include "NetworkController.h" +#include "DummyNetwork.h" #include "Fwmark.h" #include "LocalNetwork.h" #include "PhysicalNetwork.h" @@ -53,7 +54,8 @@ const unsigned MAX_NET_ID = 65535; const unsigned NetworkController::MIN_OEM_ID = 1; const unsigned NetworkController::MAX_OEM_ID = 50; -// NetIds 51..98 are reserved for future use. +const unsigned NetworkController::DUMMY_NET_ID = 51; +// NetIds 52..98 are reserved for future use. const unsigned NetworkController::LOCAL_NET_ID = 99; // All calls to methods here are made while holding a write lock on mRWLock. @@ -132,6 +134,7 @@ int NetworkController::DelegateImpl::modifyFallthrough(const std::string& physic NetworkController::NetworkController() : mDelegateImpl(new NetworkController::DelegateImpl(this)), mDefaultNetId(NETID_UNSET) { mNetworks[LOCAL_NET_ID] = new LocalNetwork(LOCAL_NET_ID); + mNetworks[DUMMY_NET_ID] = new DummyNetwork(DUMMY_NET_ID); } unsigned NetworkController::getDefaultNetwork() const { @@ -234,6 +237,30 @@ unsigned NetworkController::getNetworkForConnect(uid_t uid) const { return mDefaultNetId; } +void NetworkController::getNetworkContext( + unsigned netId, uid_t uid, struct android_net_context* netcontext) const { + struct android_net_context nc = { + .app_netid = netId, + .app_mark = MARK_UNSET, + .dns_netid = netId, + .dns_mark = MARK_UNSET, + .uid = uid, + }; + + if (nc.app_netid == NETID_UNSET) { + nc.app_netid = getNetworkForConnect(uid); + } + Fwmark fwmark; + fwmark.netId = nc.app_netid; + nc.app_mark = fwmark.intValue; + + nc.dns_mark = getNetworkForDns(&(nc.dns_netid), uid); + + if (netcontext) { + *netcontext = nc; + } +} + unsigned NetworkController::getNetworkForInterface(const char* interface) const { android::RWLock::AutoRLock lock(mRWLock); for (const auto& entry : mNetworks) { diff --git a/server/NetworkController.h b/server/NetworkController.h index 5596f0c..6d72aeb 100644 --- a/server/NetworkController.h +++ b/server/NetworkController.h @@ -43,6 +43,7 @@ public: static const unsigned MIN_OEM_ID; static const unsigned MAX_OEM_ID; static const unsigned LOCAL_NET_ID; + static const unsigned DUMMY_NET_ID; NetworkController(); @@ -55,6 +56,7 @@ public: uint32_t getNetworkForDns(unsigned* netId, uid_t uid) const; unsigned getNetworkForUser(uid_t uid) const; unsigned getNetworkForConnect(uid_t uid) const; + void getNetworkContext(unsigned netId, uid_t uid, struct android_net_context* netcontext) const; unsigned getNetworkForInterface(const char* interface) const; bool isVirtualNetwork(unsigned netId) const; diff --git a/server/RouteController.cpp b/server/RouteController.cpp index 736aa74..889779d 100644 --- a/server/RouteController.cpp +++ b/server/RouteController.cpp @@ -29,11 +29,13 @@ #include "Fwmark.h" #include "UidRanges.h" +#include "DummyNetwork.h" #include "base/file.h" #define LOG_TAG "Netd" #include "log/log.h" #include "logwrap/logwrap.h" +#include "netutils/ifc.h" #include "resolv_netid.h" using android::base::WriteStringToFile; @@ -43,6 +45,7 @@ namespace { // BEGIN CONSTANTS -------------------------------------------------------------------------------- const uint32_t RULE_PRIORITY_VPN_OVERRIDE_SYSTEM = 10000; +const uint32_t RULE_PRIORITY_VPN_OVERRIDE_OIF = 10500; const uint32_t RULE_PRIORITY_VPN_OUTPUT_TO_LOCAL = 11000; const uint32_t RULE_PRIORITY_SECURE_VPN = 12000; const uint32_t RULE_PRIORITY_EXPLICIT_NETWORK = 13000; @@ -89,6 +92,7 @@ const uint8_t AF_FAMILIES[] = {AF_INET, AF_INET6}; const char* const IP_VERSIONS[] = {"-4", "-6"}; const uid_t UID_ROOT = 0; +const char* const IIF_LOOPBACK = "lo"; const char* const IIF_NONE = NULL; const char* const OIF_NONE = NULL; const bool ACTION_ADD = true; @@ -238,8 +242,10 @@ int padInterfaceName(const char* input, char* name, size_t* length, uint16_t* pa // Adds or removes a routing rule for IPv4 and IPv6. // -// + If |table| is non-zero, the rule points at the specified routing table. Otherwise, the rule -// returns ENETUNREACH. +// + If |priority| is RULE_PRIORITY_UNREACHABLE, the rule returns ENETUNREACH (i.e., specifies an +// action of FR_ACT_UNREACHABLE). Otherwise, the rule specifies an action of FR_ACT_TO_TBL. +// + If |table| is non-zero, the rule points at the specified routing table. Otherwise, the table is +// unspecified. An unspecified table is only allowed when deleting a rule. // + If |mask| is non-zero, the rule matches the specified fwmark and mask. Otherwise, |fwmark| is // ignored. // + If |iif| is non-NULL, the rule matches the specified incoming interface. @@ -278,10 +284,20 @@ WARN_UNUSED_RESULT int modifyIpRule(uint16_t action, uint32_t priority, uint32_t // Assemble a rule request and put it in an array of iovec structures. fib_rule_hdr rule = { - .action = static_cast(table != RT_TABLE_UNSPEC ? FR_ACT_TO_TBL : - FR_ACT_UNREACHABLE), + .action = static_cast(priority != RULE_PRIORITY_UNREACHABLE ? FR_ACT_TO_TBL : + FR_ACT_UNREACHABLE), + // Note that here we're implicitly setting rule.table to 0. When we want to specify a + // non-zero table, we do this via the FRATTR_TABLE attribute. }; + // Don't ever create a rule that looks up table 0, because table 0 is the local table. + // It's OK to specify a table ID of 0 when deleting a rule, because that doesn't actually select + // table 0, it's a wildcard that matches anything. + if (table == RT_TABLE_UNSPEC && rule.action == FR_ACT_TO_TBL && action != RTM_DELRULE) { + ALOGE("RT_TABLE_UNSPEC only allowed when deleting rules"); + return -ENOTUNIQ; + } + rtattr fraIifName = { U16_RTA_LENGTH(iifLength), FRA_IIFNAME }; rtattr fraOifName = { U16_RTA_LENGTH(oifLength), FRA_OIFNAME }; @@ -476,7 +492,7 @@ WARN_UNUSED_RESULT int modifyVpnUidRangeRule(uint32_t table, uid_t uidStart, uid } return modifyIpRule(add ? RTM_NEWRULE : RTM_DELRULE, priority, table, fwmark.intValue, - mask.intValue, IIF_NONE, OIF_NONE, uidStart, uidEnd); + mask.intValue, IIF_LOOPBACK, OIF_NONE, uidStart, uidEnd); } // A rule to allow system apps to send traffic over this VPN even if they are not part of the target @@ -531,15 +547,25 @@ WARN_UNUSED_RESULT int modifyExplicitNetworkRule(unsigned netId, uint32_t table, // // Supports apps that use SO_BINDTODEVICE or IP_PKTINFO options and the kernel that already knows // the outgoing interface (typically for link-local communications). -WARN_UNUSED_RESULT int modifyOutputInterfaceRule(const char* interface, uint32_t table, - Permission permission, uid_t uidStart, - uid_t uidEnd, bool add) { +WARN_UNUSED_RESULT int modifyOutputInterfaceRules(const char* interface, uint32_t table, + Permission permission, uid_t uidStart, + uid_t uidEnd, bool add) { Fwmark fwmark; Fwmark mask; fwmark.permission = permission; mask.permission = permission; + // If this rule does not specify a UID range, then also add a corresponding high-priority rule + // for UID. This covers forwarded packets and system daemons such as the tethering DHCP server. + if (uidStart == INVALID_UID && uidEnd == INVALID_UID) { + if (int ret = modifyIpRule(add ? RTM_NEWRULE : RTM_DELRULE, RULE_PRIORITY_VPN_OVERRIDE_OIF, + table, fwmark.intValue, mask.intValue, IIF_NONE, interface, + UID_ROOT, UID_ROOT)) { + return ret; + } + } + return modifyIpRule(add ? RTM_NEWRULE : RTM_DELRULE, RULE_PRIORITY_OUTPUT_INTERFACE, table, fwmark.intValue, mask.intValue, IIF_NONE, interface, uidStart, uidEnd); } @@ -639,6 +665,41 @@ WARN_UNUSED_RESULT int addLocalNetworkRules(unsigned localNetId) { fwmark.intValue, mask.intValue); } +int configureDummyNetwork() { + const char *interface = DummyNetwork::INTERFACE_NAME; + uint32_t table = getRouteTableForInterface(interface); + if (table == RT_TABLE_UNSPEC) { + // getRouteTableForInterface has already looged an error. + return -ESRCH; + } + + ifc_init(); + int ret = ifc_up(interface); + ifc_close(); + if (ret) { + ALOGE("Can't bring up %s: %s", interface, strerror(errno)); + return -errno; + } + + if ((ret = modifyOutputInterfaceRules(interface, table, PERMISSION_NONE, + INVALID_UID, INVALID_UID, ACTION_ADD))) { + ALOGE("Can't create oif rules for %s: %s", interface, strerror(-ret)); + return ret; + } + + if ((ret = modifyIpRoute(RTM_NEWROUTE, table, interface, "0.0.0.0/0", NULL))) { + ALOGE("Can't add IPv4 default route to %s: %s", interface, strerror(-ret)); + return ret; + } + + if ((ret = modifyIpRoute(RTM_NEWROUTE, table, interface, "::/0", NULL))) { + ALOGE("Can't add IPv6 default route to %s: %s", interface, strerror(-ret)); + return ret; + } + + return 0; +} + // Add a new rule to look up the 'main' table, with the same selectors as the "default network" // rule, but with a lower priority. We will never create routes in the main table; it should only be // used for directly-connected routes implicitly created by the kernel when adding IP addresses. @@ -668,8 +729,8 @@ WARN_UNUSED_RESULT int modifyLocalNetwork(unsigned netId, const char* interface, if (int ret = modifyIncomingPacketMark(netId, interface, PERMISSION_NONE, add)) { return ret; } - return modifyOutputInterfaceRule(interface, ROUTE_TABLE_LOCAL_NETWORK, PERMISSION_NONE, - INVALID_UID, INVALID_UID, add); + return modifyOutputInterfaceRules(interface, ROUTE_TABLE_LOCAL_NETWORK, PERMISSION_NONE, + INVALID_UID, INVALID_UID, add); } WARN_UNUSED_RESULT int modifyPhysicalNetwork(unsigned netId, const char* interface, @@ -686,7 +747,7 @@ WARN_UNUSED_RESULT int modifyPhysicalNetwork(unsigned netId, const char* interfa add)) { return ret; } - if (int ret = modifyOutputInterfaceRule(interface, table, permission, INVALID_UID, INVALID_UID, + if (int ret = modifyOutputInterfaceRules(interface, table, permission, INVALID_UID, INVALID_UID, add)) { return ret; } @@ -709,8 +770,8 @@ WARN_UNUSED_RESULT int modifyVirtualNetwork(unsigned netId, const char* interfac range.second, add)) { return ret; } - if (int ret = modifyOutputInterfaceRule(interface, table, PERMISSION_NONE, range.first, - range.second, add)) { + if (int ret = modifyOutputInterfaceRules(interface, table, PERMISSION_NONE, range.first, + range.second, add)) { return ret; } } @@ -866,6 +927,20 @@ WARN_UNUSED_RESULT int flushRoutes(const char* interface) { return ret; } +WARN_UNUSED_RESULT int clearTetheringRules(const char* inputInterface) { + int ret = 0; + while (ret == 0) { + ret = modifyIpRule(RTM_DELRULE, RULE_PRIORITY_TETHERING, 0, MARK_UNSET, MARK_UNSET, + inputInterface, OIF_NONE, INVALID_UID, INVALID_UID); + } + + if (ret == -ENOENT) { + return 0; + } else { + return ret; + } +} + } // namespace int RouteController::Init(unsigned localNetId) { @@ -884,6 +959,9 @@ int RouteController::Init(unsigned localNetId) { if (int ret = addUnreachableRule()) { return ret; } + // Don't complain if we can't add the dummy network, since not all devices support it. + configureDummyNetwork(); + updateTableNamesFile(); return 0; } @@ -913,6 +991,9 @@ int RouteController::removeInterfaceFromPhysicalNetwork(unsigned netId, const ch if (int ret = flushRoutes(interface)) { return ret; } + if (int ret = clearTetheringRules(interface)) { + return ret; + } updateTableNamesFile(); return 0; } diff --git a/server/SoftapController.cpp b/server/SoftapController.cpp index c1db1fc..d9e40a4 100644 --- a/server/SoftapController.cpp +++ b/server/SoftapController.cpp @@ -145,10 +145,10 @@ int SoftapController::setSoftap(int argc, char *argv[]) { "ssid=%s\n" "channel=%d\n" "ieee80211n=1\n" - "hw_mode=g\n" + "hw_mode=%c\n" "ignore_broadcast_ssid=%d\n" "wowlan_triggers=any\n", - argv[2], argv[3], channel, hidden)); + argv[2], argv[3], channel, (channel <= 14) ? 'g' : 'a', hidden)); std::string fbuf; if (argc > 7) { diff --git a/server/TetherController.cpp b/server/TetherController.cpp index 44c7e35..88baa31 100644 --- a/server/TetherController.cpp +++ b/server/TetherController.cpp @@ -27,7 +27,6 @@ #include #include -#include #define LOG_TAG "TetherController" #include #include @@ -37,8 +36,37 @@ #include "Permission.h" #include "TetherController.h" -using android::base::ReadFileToString; -using android::base::WriteStringToFile; +namespace { + +static const char BP_TOOLS_MODE[] = "bp-tools"; +static const char IPV4_FORWARDING_PROC_FILE[] = "/proc/sys/net/ipv4/ip_forward"; +static const char IPV6_FORWARDING_PROC_FILE[] = "/proc/sys/net/ipv6/conf/all/forwarding"; + +bool writeToFile(const char* filename, const char* value) { + int fd = open(filename, O_WRONLY); + if (fd < 0) { + ALOGE("Failed to open %s: %s", filename, strerror(errno)); + return false; + } + + const ssize_t len = strlen(value); + if (write(fd, value, len) != len) { + ALOGE("Failed to write %s to %s: %s", value, filename, strerror(errno)); + close(fd); + return false; + } + close(fd); + return true; +} + +bool inBpToolsMode() { + // In BP tools mode, do not disable IP forwarding + char bootmode[PROPERTY_VALUE_MAX] = {0}; + property_get("ro.bootmode", bootmode, "unknown"); + return !strcmp(BP_TOOLS_MODE, bootmode); +} + +} // namespace TetherController::TetherController() { mInterfaces = new InterfaceCollection(); @@ -46,6 +74,11 @@ TetherController::TetherController() { mDnsForwarders = new NetAddressCollection(); mDaemonFd = -1; mDaemonPid = 0; + if (inBpToolsMode()) { + enableForwarding(BP_TOOLS_MODE); + } else { + setIpFwdEnabled(); + } } TetherController::~TetherController() { @@ -57,35 +90,33 @@ TetherController::~TetherController() { mInterfaces->clear(); mDnsForwarders->clear(); + mForwardingRequests.clear(); } -int TetherController::setIpFwdEnabled(bool enable) { - - ALOGD("Setting IP forward enable = %d", enable); - - // In BP tools mode, do not disable IP forwarding - char bootmode[PROPERTY_VALUE_MAX] = {0}; - property_get("ro.bootmode", bootmode, "unknown"); - if ((enable == false) && (0 == strcmp("bp-tools", bootmode))) { - return 0; - } - - if (!WriteStringToFile(enable ? "1" : "0", "/proc/sys/net/ipv4/ip_forward")) { - ALOGE("Failed to write ip_forward (%s)", strerror(errno)); - return -1; - } +bool TetherController::setIpFwdEnabled() { + bool success = true; + const char* value = mForwardingRequests.empty() ? "0" : "1"; + ALOGD("Setting IP forward enable = %s", value); + success &= writeToFile(IPV4_FORWARDING_PROC_FILE, value); + success &= writeToFile(IPV6_FORWARDING_PROC_FILE, value); + return success; +} - return 0; +bool TetherController::enableForwarding(const char* requester) { + // Don't return an error if this requester already requested forwarding. Only return errors for + // things that the caller caller needs to care about, such as "couldn't write to the file to + // enable forwarding". + mForwardingRequests.insert(requester); + return setIpFwdEnabled(); } -bool TetherController::getIpFwdEnabled() { - std::string enabled; - if (!ReadFileToString("/proc/sys/net/ipv4/ip_forward", &enabled)) { - ALOGE("Failed to read ip_forward (%s)", strerror(errno)); - return -1; - } +bool TetherController::disableForwarding(const char* requester) { + mForwardingRequests.erase(requester); + return setIpFwdEnabled(); +} - return (enabled == "1" ? true : false); +size_t TetherController::forwardingRequestCount() { + return mForwardingRequests.size(); } #define TETHER_START_CONST_ARG 8 diff --git a/server/TetherController.h b/server/TetherController.h index 1c32627..91ffb9c 100644 --- a/server/TetherController.h +++ b/server/TetherController.h @@ -18,6 +18,8 @@ #define _TETHER_CONTROLLER_H #include +#include +#include #include "List.h" @@ -32,16 +34,17 @@ class TetherController { NetAddressCollection *mDnsForwarders; pid_t mDaemonPid; int mDaemonFd; + std::set mForwardingRequests; public: TetherController(); virtual ~TetherController(); - int setIpFwdEnabled(bool enable); - bool getIpFwdEnabled(); + bool enableForwarding(const char* requester); + bool disableForwarding(const char* requester); + size_t forwardingRequestCount(); int startTethering(int num_addrs, struct in_addr* addrs); - int stopTethering(); bool isTetheringStarted(); @@ -55,6 +58,7 @@ public: private: int applyDnsInterfaces(); + bool setIpFwdEnabled(); }; #endif diff --git a/server/oem_iptables_hook.cpp b/server/oem_iptables_hook.cpp index 70260f3..7e4b3cb 100644 --- a/server/oem_iptables_hook.cpp +++ b/server/oem_iptables_hook.cpp @@ -37,6 +37,7 @@ static int runIptablesCmd(int argc, const char **argv) { static bool oemCleanupHooks() { const char *cmd1[] = { IPTABLES_PATH, + "-w", "-F", "oem_out" }; @@ -44,6 +45,7 @@ static bool oemCleanupHooks() { const char *cmd2[] = { IPTABLES_PATH, + "-w", "-F", "oem_fwd" }; @@ -51,6 +53,7 @@ static bool oemCleanupHooks() { const char *cmd3[] = { IPTABLES_PATH, + "-w", "-t", "nat", "-F",