Don't reset a VPN's NetId in the connect() shim.

Change-Id: I0cc6c0e221a40c9100c8f4c0c5e761fce3f9b0ae
diff --git a/server/FwmarkServer.cpp b/server/FwmarkServer.cpp
index 39a8d74..d8098e8 100644
--- a/server/FwmarkServer.cpp
+++ b/server/FwmarkServer.cpp
@@ -95,16 +95,23 @@
 
     switch (command.cmdId) {
         case FwmarkCommand::ON_ACCEPT: {
-            // Called after a socket accept(). The kernel would've marked the netId and necessary
+            // Called after a socket accept(). The kernel would've marked the NetId and necessary
             // permissions bits, so we just add the rest of the user's permissions here.
             permission = static_cast<Permission>(permission | fwmark.permission);
             break;
         }
 
         case FwmarkCommand::ON_CONNECT: {
-            // Set the netId (of the default network) into the fwmark, if it has not already been
-            // set explicitly. Called before a socket connect() happens.
-            if (!fwmark.explicitlySelected) {
+            // Called before a socket connect() happens. Set the default network's NetId into the
+            // fwmark so that the socket routes consistently over that network. Do this even if the
+            // socket already has a NetId, so that calling connect() multiple times still works.
+            //
+            // But respect the existing NetId if it had been explicitly preferred, indicated by:
+            // + The explicit bit having been set.
+            // + Or, the NetId being that of a VPN, which indicates a proxy acting on behalf of a
+            //   user who is subject to the VPN. The explicit bit is not set so that it works even
+            //   if the VPN is a split tunnel, but it's an explicit network preference nonetheless.
+            if (!fwmark.explicitlySelected && !mNetworkController->isVirtualNetwork(fwmark.netId)) {
                 fwmark.netId = mNetworkController->getDefaultNetwork();
             }
             break;
diff --git a/server/NetworkController.cpp b/server/NetworkController.cpp
index 3bc0e70..44eb2ef 100644
--- a/server/NetworkController.cpp
+++ b/server/NetworkController.cpp
@@ -111,6 +111,12 @@
     return NETID_UNSET;
 }
 
+bool NetworkController::isVirtualNetwork(unsigned netId) const {
+    android::RWLock::AutoRLock lock(mRWLock);
+    Network* network = getNetworkLocked(netId);
+    return network && network->getType() == Network::VIRTUAL;
+}
+
 unsigned NetworkController::getNetIdForLocalNetwork() const {
     return MIN_NET_ID - 1;
 }
diff --git a/server/NetworkController.h b/server/NetworkController.h
index f0a5ea7..479af5e 100644
--- a/server/NetworkController.h
+++ b/server/NetworkController.h
@@ -51,6 +51,7 @@
     // requests to VPNs without DNS servers.
     unsigned getNetworkForUser(uid_t uid, unsigned requestedNetId, bool forDns) const;
     unsigned getNetworkForInterface(const char* interface) const;
+    bool isVirtualNetwork(unsigned netId) const;
 
     // TODO: Remove this hack.
     unsigned getNetIdForLocalNetwork() const;