diff options
-rw-r--r-- | Android.mk | 1 | ||||
-rw-r--r-- | client/Android.mk | 3 | ||||
-rw-r--r-- | server/Android.mk | 2 | ||||
-rw-r--r-- | server/CommandListener.cpp | 110 | ||||
-rw-r--r-- | server/DummyNetwork.cpp | 43 | ||||
-rw-r--r-- | server/DummyNetwork.h | 34 | ||||
-rw-r--r-- | server/NatController.cpp | 15 | ||||
-rw-r--r-- | server/NetlinkHandler.cpp | 32 | ||||
-rw-r--r-- | server/NetlinkHandler.h | 5 | ||||
-rw-r--r-- | server/Network.h | 1 | ||||
-rw-r--r-- | server/NetworkController.cpp | 5 | ||||
-rw-r--r-- | server/NetworkController.h | 1 | ||||
-rw-r--r-- | server/RouteController.cpp | 107 | ||||
-rw-r--r-- | server/SoftapController.cpp | 4 | ||||
-rw-r--r-- | server/TetherController.cpp | 68 | ||||
-rw-r--r-- | server/TetherController.h | 10 |
16 files changed, 329 insertions, 112 deletions
diff --git a/Android.mk b/Android.mk new file mode 100644 index 00000000..5053e7d6 --- /dev/null +++ b/Android.mk @@ -0,0 +1 @@ +include $(call all-subdir-makefiles) diff --git a/client/Android.mk b/client/Android.mk index 0c5e7ea3..d3393d04 100644 --- a/client/Android.mk +++ b/client/Android.mk @@ -16,10 +16,11 @@ 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 LOCAL_SRC_FILES := FwmarkClient.cpp NetdClient.cpp +include external/libcxx/libcxx.mk include $(BUILD_SHARED_LIBRARY) diff --git a/server/Android.mk b/server/Android.mk index 35e34f55..cdf25994 100644 --- a/server/Android.mk +++ b/server/Android.mk @@ -47,6 +47,7 @@ LOCAL_SRC_FILES := \ ClatdController.cpp \ CommandListener.cpp \ DnsProxyListener.cpp \ + DummyNetwork.cpp \ FirewallController.cpp \ FwmarkServer.cpp \ IdletimerController.cpp \ @@ -72,6 +73,7 @@ LOCAL_SRC_FILES := \ main.cpp \ oem_iptables_hook.cpp \ +include external/libcxx/libcxx.mk include $(BUILD_EXECUTABLE) include $(CLEAR_VARS) diff --git a/server/CommandListener.cpp b/server/CommandListener.cpp index b508d3fc..d7953966 100644 --- a/server/CommandListener.cpp +++ b/server/CommandListener.cpp @@ -507,37 +507,59 @@ CommandListener::IpFwdCmd::IpFwdCmd() : NetdCommand("ipfwd") { } -int CommandListener::IpFwdCmd::runCommand(SocketClient *cli, - int argc, char **argv) { - int rc = 0; - - if (argc < 2) { - cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false); - return 0; - } - - 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 { +int CommandListener::IpFwdCmd::runCommand(SocketClient *cli, int argc, char **argv) { + bool matched = false; + bool success; + + if (argc == 2) { + // 0 1 + // ipfwd status + if (!strcmp(argv[1], "status")) { + char *tmp = NULL; + + 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 <requester> + // ipfwd disable <requester> + 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<const char **>(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 <netId> <domains> <dns1> <dns2> ..." 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 <netId>" 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 <netId>" 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); @@ -1633,24 +1659,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<unsigned> 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/DummyNetwork.cpp b/server/DummyNetwork.cpp new file mode 100644 index 00000000..ff2cb412 --- /dev/null +++ b/server/DummyNetwork.cpp @@ -0,0 +1,43 @@ +/* + * 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" + +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 00000000..7bc0d3d4 --- /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/NatController.cpp b/server/NatController.cpp index e66d9715..5a15afa9 100644 --- a/server/NatController.cpp +++ b/server/NatController.cpp @@ -199,15 +199,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; } @@ -368,12 +359,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/NetlinkHandler.cpp b/server/NetlinkHandler.cpp index 0a5a3f02..55351190 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"); @@ -174,12 +174,12 @@ void NetlinkHandler::notifyInterfaceClassActivity(const char *name, "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 +190,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 bee52dcb..c70867e2 100644 --- a/server/NetlinkHandler.h +++ b/server/NetlinkHandler.h @@ -17,6 +17,7 @@ #ifndef _NETLINKHANDLER_H #define _NETLINKHANDLER_H +#include <sysutils/NetlinkEvent.h> #include <sysutils/NetlinkListener.h> #include "NetlinkManager.h" @@ -41,11 +42,11 @@ protected: 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, + 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 115997ad..3af53d97 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 20d8e977..76e4a6af 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 { diff --git a/server/NetworkController.h b/server/NetworkController.h index 5596f0c0..073745da 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(); diff --git a/server/RouteController.cpp b/server/RouteController.cpp index b47acd4a..98a965e7 100644 --- a/server/RouteController.cpp +++ b/server/RouteController.cpp @@ -29,10 +29,12 @@ #include "Fwmark.h" #include "UidRanges.h" +#include "DummyNetwork.h" #define LOG_TAG "Netd" #include "log/log.h" #include "logwrap/logwrap.h" +#include "netutils/ifc.h" #include "utils/file.h" #include "resolv_netid.h" @@ -41,6 +43,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; @@ -87,6 +90,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; @@ -236,8 +240,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. @@ -276,10 +282,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<uint8_t>(table != RT_TABLE_UNSPEC ? FR_ACT_TO_TBL : - FR_ACT_UNREACHABLE), + .action = static_cast<uint8_t>(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 }; @@ -474,7 +490,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 @@ -529,15 +545,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); } @@ -637,6 +663,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. @@ -666,8 +727,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, @@ -684,7 +745,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; } @@ -707,8 +768,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; } } @@ -864,6 +925,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) { @@ -882,6 +957,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; } @@ -911,6 +989,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 0759bde7..24655291 100644 --- a/server/SoftapController.cpp +++ b/server/SoftapController.cpp @@ -142,10 +142,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 c9a93fd6..5a08ed21 100644 --- a/server/TetherController.cpp +++ b/server/TetherController.cpp @@ -37,12 +37,36 @@ #include "Permission.h" #include "TetherController.h" +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) { + return WriteStringToFile(value, file); +} + +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(); mDnsNetId = 0; mDnsForwarders = new NetAddressCollection(); mDaemonFd = -1; mDaemonPid = 0; + if (inBpToolsMode()) { + enableForwarding(BP_TOOLS_MODE); + } else { + setIpFwdEnabled(); + } } TetherController::~TetherController() { @@ -54,35 +78,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 (!android::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 (!android::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 1c326270..91ffb9cc 100644 --- a/server/TetherController.h +++ b/server/TetherController.h @@ -18,6 +18,8 @@ #define _TETHER_CONTROLLER_H #include <netinet/in.h> +#include <set> +#include <string> #include "List.h" @@ -32,16 +34,17 @@ class TetherController { NetAddressCollection *mDnsForwarders; pid_t mDaemonPid; int mDaemonFd; + std::set<std::string> 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 |