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;