Merge "Only Log Netlink Messages on Eng Builds" into pi-dev
diff --git a/server/NetlinkHandler.cpp b/server/NetlinkHandler.cpp
index 9b033ff..2bc9c27 100644
--- a/server/NetlinkHandler.cpp
+++ b/server/NetlinkHandler.cpp
@@ -55,6 +55,18 @@
     return this->stopListener();
 }
 
+static long parseIfIndex(const char* ifIndex) {
+    if (ifIndex == nullptr) {
+        return 0;
+    }
+    long ifaceIndex = strtol(ifIndex, NULL, 10);
+    // strtol returns 0 on error, which is fine because 0 is not a valid ifindex.
+    if (errno == ERANGE && (ifaceIndex == LONG_MAX || ifaceIndex == LONG_MIN)) {
+        return 0;
+    }
+    return ifaceIndex;
+}
+
 void NetlinkHandler::onEvent(NetlinkEvent *evt) {
     const char *subsys = evt->getSubsystem();
     if (!subsys) {
@@ -69,15 +81,11 @@
             (action == NetlinkEvent::Action::kLinkUp) ||
             (action == NetlinkEvent::Action::kLinkDown)) {
             const char *ifIndex = evt->findParam("IFINDEX");
-            if (ifIndex) {
-                // strtol returns 0 on error, which is fine because 0 is not a valid ifindex.
-                long ifaceIndex = strtol(ifIndex, NULL, 10);
-                if (ifaceIndex == 0 ||
-                    (errno == ERANGE && (ifaceIndex == LONG_MAX || ifaceIndex == LONG_MIN))) {
-                    ALOGE("invalid interface index: %s(%s)", iface, ifIndex);
-                } else {
-                    gCtls->trafficCtrl.addInterface(iface, ifaceIndex);
-                }
+            long ifaceIndex = parseIfIndex(ifIndex);
+            if (ifaceIndex) {
+                gCtls->trafficCtrl.addInterface(iface, ifaceIndex);
+            } else {
+                ALOGE("invalid interface index: %s(%s)", iface, ifIndex);
             }
         }
 
@@ -97,25 +105,35 @@
             const char *address = evt->findParam("ADDRESS");
             const char *flags = evt->findParam("FLAGS");
             const char *scope = evt->findParam("SCOPE");
-            if (action == NetlinkEvent::Action::kAddressRemoved && iface && address) {
-                // Note: if this interface was deleted, iface is "" and we don't notify.
-                SockDiag sd;
-                if (sd.open()) {
-                    char addrstr[INET6_ADDRSTRLEN];
-                    strncpy(addrstr, address, sizeof(addrstr));
-                    char *slash = strchr(addrstr, '/');
-                    if (slash) {
-                        *slash = '\0';
-                    }
+            const char *ifIndex = evt->findParam("IFINDEX");
+            char addrstr[INET6_ADDRSTRLEN + strlen("/128")];
+            strlcpy(addrstr, address, sizeof(addrstr));
+            char *slash = strchr(addrstr, '/');
+            if (slash) {
+                *slash = '\0';
+            }
 
-                    int ret = sd.destroySockets(addrstr);
-                    if (ret < 0) {
-                        ALOGE("Error destroying sockets: %s", strerror(ret));
+            long ifaceIndex = parseIfIndex(ifIndex);
+            if (!ifaceIndex) {
+                ALOGE("invalid interface index: %s(%s)", iface, ifIndex);
+            }
+            if (action == NetlinkEvent::Action::kAddressUpdated) {
+                gCtls->netCtrl.addInterfaceAddress(ifaceIndex, address);
+            } else {  // action == NetlinkEvent::Action::kAddressRemoved
+                bool shouldDestroy = gCtls->netCtrl.removeInterfaceAddress(ifaceIndex, address);
+                if (shouldDestroy) {
+                    SockDiag sd;
+                    if (sd.open()) {
+                        int ret = sd.destroySockets(addrstr);
+                        if (ret < 0) {
+                            ALOGE("Error destroying sockets: %s", strerror(-ret));
+                        }
+                    } else {
+                        ALOGE("Error opening NETLINK_SOCK_DIAG socket: %s", strerror(errno));
                     }
-                } else {
-                    ALOGE("Error opening NETLINK_SOCK_DIAG socket: %s", strerror(errno));
                 }
             }
+            // Note: if this interface was deleted, iface is "" and we don't notify.
             if (iface && iface[0] && address && flags && scope) {
                 notifyAddressChanged(action, address, iface, flags, scope);
             }
diff --git a/server/NetworkController.cpp b/server/NetworkController.cpp
index 5b2cd89..5bbfe3f 100644
--- a/server/NetworkController.cpp
+++ b/server/NetworkController.cpp
@@ -28,6 +28,8 @@
 #define LOG_TAG "Netd"
 #include "log/log.h"
 
+#include <android-base/strings.h>
+
 #include "cutils/misc.h"
 #include "resolv_netid.h"
 
@@ -192,6 +194,19 @@
     Fwmark fwmark;
     fwmark.protectedFromVpn = true;
     fwmark.permission = PERMISSION_SYSTEM;
+
+    // Common case: there is no VPN that applies to the user, and the query did not specify a netId.
+    // Therefore, it is safe to set the explicit bit on this query and skip all the complex logic
+    // below. While this looks like a special case, it is actually the one that handles the vast
+    // majority of DNS queries.
+    // TODO: untangle this code.
+    if (*netId == NETID_UNSET && getVirtualNetworkForUserLocked(uid) == nullptr) {
+        *netId = mDefaultNetId;
+        fwmark.netId = *netId;
+        fwmark.explicitlySelected = true;
+        return fwmark.intValue;
+    }
+
     if (checkUserNetworkAccessLocked(uid, *netId) == 0) {
         // If a non-zero NetId was explicitly specified, and the user has permission for that
         // network, use that network's DNS servers. Do not fall through to the default network even
@@ -210,7 +225,8 @@
     } else {
         // If the user is subject to a VPN and the VPN provides DNS servers, use those servers
         // (possibly falling through to the default network if the VPN doesn't provide a route to
-        // them). Otherwise, use the default network's DNS servers.
+        // them). Otherwise, use the default network's DNS servers. We cannot set the explicit bit
+        // because we need to be able to fall through a split tunnel to the default network.
         VirtualNetwork* virtualNetwork = getVirtualNetworkForUserLocked(uid);
         if (virtualNetwork && virtualNetwork->getHasDns()) {
             *netId = virtualNetwork->getNetId();
@@ -331,6 +347,10 @@
 
 bool NetworkController::isVirtualNetwork(unsigned netId) const {
     android::RWLock::AutoRLock lock(mRWLock);
+    return isVirtualNetworkLocked(netId);
+}
+
+bool NetworkController::isVirtualNetworkLocked(unsigned netId) const {
     Network* network = getNetworkLocked(netId);
     return network && network->getType() == Network::VIRTUAL;
 }
@@ -452,6 +472,14 @@
     delete network;
     _resolv_delete_cache_for_net(netId);
 
+    for (auto iter = mIfindexToLastNetId.begin(); iter != mIfindexToLastNetId.end();) {
+        if (iter->second == netId) {
+            iter = mIfindexToLastNetId.erase(iter);
+        } else {
+            ++iter;
+        }
+    }
+
     updateTcpSocketMonitorPolling();
 
     return ret;
@@ -470,8 +498,18 @@
         ALOGE("interface %s already assigned to netId %u", interface, existingNetId);
         return -EBUSY;
     }
+    if (int ret = getNetworkLocked(netId)->addInterface(interface)) {
+        return ret;
+    }
 
-    return getNetworkLocked(netId)->addInterface(interface);
+    int ifIndex = RouteController::getIfIndex(interface);
+    if (ifIndex) {
+        mIfindexToLastNetId[ifIndex] = netId;
+    } else {
+        // Cannot happen, since addInterface() above will have failed.
+        ALOGE("inconceivable! added interface %s with no index", interface);
+    }
+    return 0;
 }
 
 int NetworkController::removeInterfaceFromNetwork(unsigned netId, const char* interface) {
@@ -569,6 +607,53 @@
     return modifyRoute(netId, interface, destination, nexthop, false, legacy, uid);
 }
 
+void NetworkController::addInterfaceAddress(unsigned ifIndex, const char* address) {
+    android::RWLock::AutoWLock lock(mRWLock);
+
+    if (ifIndex == 0) {
+        ALOGE("Attempting to add address %s without ifindex", address);
+        return;
+    }
+    mAddressToIfindices[address].insert(ifIndex);
+}
+
+// Returns whether we should call SOCK_DESTROY on the removed address.
+bool NetworkController::removeInterfaceAddress(unsigned ifindex, const char* address) {
+    android::RWLock::AutoWLock lock(mRWLock);
+    // First, update mAddressToIfindices map
+    auto ifindicesIter = mAddressToIfindices.find(address);
+    if (ifindicesIter == mAddressToIfindices.end()) {
+        ALOGE("Removing unknown address %s from ifindex %u", address, ifindex);
+        return true;
+    }
+    std::unordered_set<unsigned>& ifindices = ifindicesIter->second;
+    if (ifindices.erase(ifindex) > 0) {
+        if (ifindices.size() == 0) {
+            mAddressToIfindices.erase(ifindicesIter);
+        }
+    } else {
+        ALOGE("No record of address %s on interface %u", address, ifindex);
+        return true;
+    }
+    // Then, check for VPN handover condition
+    if (mIfindexToLastNetId.find(ifindex) == mIfindexToLastNetId.end()) {
+        ALOGE("Interface index %u was never in a currently-connected netId", ifindex);
+        return true;
+    }
+    unsigned lastNetId = mIfindexToLastNetId[ifindex];
+    for (unsigned idx : ifindices) {
+        unsigned activeNetId = mIfindexToLastNetId[idx];
+        // If this IP address is still assigned to another interface in the same network,
+        // then we don't need to destroy sockets on it because they are likely still valid.
+        // For now we do this only on VPNs.
+        // TODO: evaluate extending this to all network types.
+        if (lastNetId == activeNetId && isVirtualNetworkLocked(activeNetId)) {
+            return false;
+        }
+    }
+    return true;
+}
+
 bool NetworkController::canProtectLocked(uid_t uid) const {
     return ((getPermissionForUserLocked(uid) & PERMISSION_SYSTEM) == PERMISSION_SYSTEM) ||
            mProtectableUsers.find(uid) != mProtectableUsers.end();
@@ -617,6 +702,23 @@
     }
     dw.decIndent();
 
+    dw.blankline();
+    dw.println("Interface <-> last network map:");
+    dw.incIndent();
+    for (const auto& i : mIfindexToLastNetId) {
+        dw.println("Ifindex: %u NetId: %u", i.first, i.second);
+    }
+    dw.decIndent();
+
+    dw.blankline();
+    dw.println("Interface addresses:");
+    dw.incIndent();
+    for (const auto& i : mAddressToIfindices) {
+        dw.println("address: %s ifindices: [%s]", i.first.c_str(),
+                android::base::Join(i.second, ", ").c_str());
+    }
+    dw.decIndent();
+
     dw.decIndent();
 
     dw.decIndent();
diff --git a/server/NetworkController.h b/server/NetworkController.h
index 627e44d..5e7af80 100644
--- a/server/NetworkController.h
+++ b/server/NetworkController.h
@@ -27,6 +27,8 @@
 #include <map>
 #include <set>
 #include <sys/types.h>
+#include <unordered_map>
+#include <unordered_set>
 #include <vector>
 
 struct android_net_context;
@@ -123,6 +125,12 @@
     int removeRoute(unsigned netId, const char* interface, const char* destination,
                     const char* nexthop, bool legacy, uid_t uid) WARN_UNUSED_RESULT;
 
+    // Notes that the specified address has appeared on the specified interface.
+    void addInterfaceAddress(unsigned ifIndex, const char* address);
+    // Notes that the specified address has been removed from the specified interface.
+    // Returns true if we should destroy sockets on this address.
+    bool removeInterfaceAddress(unsigned ifIndex, const char* address);
+
     bool canProtect(uid_t uid) const;
     void allowProtect(const std::vector<uid_t>& uids);
     void denyProtect(const std::vector<uid_t>& uids);
@@ -137,6 +145,7 @@
     unsigned getNetworkForConnectLocked(uid_t uid) const;
     unsigned getNetworkForInterfaceLocked(const char* interface) const;
     bool canProtectLocked(uid_t uid) const;
+    bool isVirtualNetworkLocked(unsigned netId) const;
 
     VirtualNetwork* getVirtualNetworkForUserLocked(uid_t uid) const;
     Permission getPermissionForUserLocked(uid_t uid) const;
@@ -151,12 +160,27 @@
     class DelegateImpl;
     DelegateImpl* const mDelegateImpl;
 
-    // mRWLock guards all accesses to mDefaultNetId, mNetworks, mUsers and mProtectableUsers.
+    // mRWLock guards all accesses to mDefaultNetId, mNetworks, mUsers, mProtectableUsers,
+    // mIfindexToLastNetId and mAddressToIfindices.
     mutable android::RWLock mRWLock;
     unsigned mDefaultNetId;
     std::map<unsigned, Network*> mNetworks;  // Map keys are NetIds.
     std::map<uid_t, Permission> mUsers;
     std::set<uid_t> mProtectableUsers;
+    // Map interface (ifIndex) to its current NetId, or the last NetId if the interface was removed
+    // from the network and not added to another network. This state facilitates the interface to
+    // NetId lookup during RTM_DELADDR (NetworkController::removeInterfaceAddress), when the
+    // interface in question might already have been removed from the network.
+    // An interface is added to this map when it is added to a network and removed from this map
+    // when its network is destroyed.
+    std::unordered_map<unsigned, unsigned> mIfindexToLastNetId;
+    // Map IP address to the list of active interfaces (ifIndex) that have that address.
+    // Also contains IP addresses configured on interfaces that have not been added to any network.
+    // TODO: Does not track IP addresses present when netd is started or restarts after a crash.
+    // This is not a problem for its intended use (tracking IP addresses on VPN interfaces), but
+    // we should fix it.
+    std::unordered_map<std::string, std::unordered_set<unsigned>> mAddressToIfindices;
+
 };
 
 }  // namespace net
diff --git a/server/RouteController.cpp b/server/RouteController.cpp
index b1aa5ac..c78854d 100644
--- a/server/RouteController.cpp
+++ b/server/RouteController.cpp
@@ -154,6 +154,18 @@
     return iter->second;
 }
 
+uint32_t RouteController::getIfIndex(const char* interface) {
+    android::RWLock::AutoRLock lock(sInterfaceToTableLock);
+
+    auto iter = sInterfaceToTable.find(interface);
+    if (iter == sInterfaceToTable.end()) {
+        ALOGE("getIfIndex: cannot find interface %s", interface);
+        return 0;
+    }
+
+    return iter->second - ROUTE_TABLE_OFFSET_FROM_INDEX;
+}
+
 uint32_t RouteController::getRouteTableForInterface(const char* interface) {
     android::RWLock::AutoRLock lock(sInterfaceToTableLock);
     return getRouteTableForInterfaceLocked(interface);
diff --git a/server/RouteController.h b/server/RouteController.h
index de79b61..6e10cce 100644
--- a/server/RouteController.h
+++ b/server/RouteController.h
@@ -45,6 +45,15 @@
 
     static int Init(unsigned localNetId) WARN_UNUSED_RESULT;
 
+    // Returns an ifindex given the interface name, by looking up in sInterfaceToTable.
+    // This is currently only used by NetworkController::addInterfaceToNetwork
+    // and should probabaly be changed to passing the ifindex into RouteController instead.
+    // We do this instead of calling if_nametoindex because the same interface name can
+    // correspond to different interface indices over time. This way, even if the interface
+    // index has changed, we can still free any map entries indexed by the ifindex that was
+    // used to add them.
+    static uint32_t getIfIndex(const char* interface);
+
     static int addInterfaceToLocalNetwork(unsigned netId, const char* interface) WARN_UNUSED_RESULT;
     static int removeInterfaceFromLocalNetwork(unsigned netId,
                                                const char* interface) WARN_UNUSED_RESULT;