Merge remote-tracking branch 'goog/mirror-m-wireless-internal-release'
Change-Id: I51337014e2851f47dd5e183c4bfdf39bafa59942
diff --git a/server/Android.mk b/server/Android.mk
index 59e015b..ecc8829 100644
--- a/server/Android.mk
+++ b/server/Android.mk
@@ -48,6 +48,7 @@
ClatdController.cpp \
CommandListener.cpp \
DnsProxyListener.cpp \
+ DummyNetwork.cpp \
FirewallController.cpp \
FwmarkServer.cpp \
IdletimerController.cpp \
diff --git a/server/CommandListener.cpp b/server/CommandListener.cpp
index 55c6411..d795396 100644
--- a/server/CommandListener.cpp
+++ b/server/CommandListener.cpp
@@ -507,37 +507,59 @@
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 (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 (!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 {
+ 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;
}
diff --git a/server/DummyNetwork.cpp b/server/DummyNetwork.cpp
new file mode 100644
index 0000000..ff2cb41
--- /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 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/NatController.cpp b/server/NatController.cpp
index e66d971..5a15afa 100644
--- a/server/NatController.cpp
+++ b/server/NatController.cpp
@@ -199,15 +199,6 @@
};
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 @@
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/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..76e4a6a 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 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 @@
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 5596f0c..073745d 100644
--- a/server/NetworkController.h
+++ b/server/NetworkController.h
@@ -43,6 +43,7 @@
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 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 @@
// 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 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 @@
// 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 @@
// 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 };
@@ -476,7 +492,7 @@
}
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 @@
//
// 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
if (int ret = flushRoutes(interface)) {
return ret;
}
+ if (int ret = clearTetheringRules(interface)) {
+ return ret;
+ }
updateTableNamesFile();
return 0;
}
diff --git a/server/TetherController.cpp b/server/TetherController.cpp
index 44c7e35..092d456 100644
--- a/server/TetherController.cpp
+++ b/server/TetherController.cpp
@@ -40,12 +40,36 @@
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) {
+ 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() {
@@ -57,35 +81,33 @@
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;
- }
-
- return 0;
+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;
}
-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::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();
+}
- return (enabled == "1" ? true : false);
+bool TetherController::disableForwarding(const char* requester) {
+ mForwardingRequests.erase(requester);
+ return setIpFwdEnabled();
+}
+
+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 <netinet/in.h>
+#include <set>
+#include <string>
#include "List.h"
@@ -32,16 +34,17 @@
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 @@
private:
int applyDnsInterfaces();
+ bool setIpFwdEnabled();
};
#endif