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