Implement the fallthrough rule to support split tunnel VPNs.
Change-Id: Ibc48caedb5954c6b12bfa553d978bab56c4b09aa
diff --git a/server/Network.cpp b/server/Network.cpp
index 5104de2..0ca6247 100644
--- a/server/Network.cpp
+++ b/server/Network.cpp
@@ -33,6 +33,10 @@
return mInterfaces.find(interface) != mInterfaces.end();
}
+const std::set<std::string>& Network::getInterfaces() const {
+ return mInterfaces;
+}
+
int Network::clearInterfaces() {
while (!mInterfaces.empty()) {
// Make a copy of the string, so removeInterface() doesn't lose its parameter when it
diff --git a/server/Network.h b/server/Network.h
index 39c81aa..115997a 100644
--- a/server/Network.h
+++ b/server/Network.h
@@ -40,6 +40,7 @@
unsigned getNetId() const;
bool hasInterface(const std::string& interface) const;
+ const std::set<std::string>& getInterfaces() const;
// These return 0 on success or negative errno on failure.
virtual int addInterface(const std::string& interface) WARN_UNUSED_RESULT = 0;
diff --git a/server/NetworkController.cpp b/server/NetworkController.cpp
index d151490..3d979f9 100644
--- a/server/NetworkController.cpp
+++ b/server/NetworkController.cpp
@@ -50,7 +50,81 @@
} // namespace
-NetworkController::NetworkController() : mDefaultNetId(NETID_UNSET) {
+// All calls to methods here are made while holding a write lock on mRWLock.
+class NetworkController::DelegateImpl : public PhysicalNetwork::Delegate {
+public:
+ explicit DelegateImpl(NetworkController* networkController);
+ virtual ~DelegateImpl();
+
+ int modifyFallthrough(unsigned vpnNetId, const std::string& physicalInterface,
+ Permission permission, bool add) WARN_UNUSED_RESULT;
+
+private:
+ int addFallthrough(const std::string& physicalInterface,
+ Permission permission) override WARN_UNUSED_RESULT;
+ int removeFallthrough(const std::string& physicalInterface,
+ Permission permission) override WARN_UNUSED_RESULT;
+
+ int modifyFallthrough(const std::string& physicalInterface, Permission permission,
+ bool add) WARN_UNUSED_RESULT;
+
+ NetworkController* const mNetworkController;
+};
+
+NetworkController::DelegateImpl::DelegateImpl(NetworkController* networkController) :
+ mNetworkController(networkController) {
+}
+
+NetworkController::DelegateImpl::~DelegateImpl() {
+}
+
+int NetworkController::DelegateImpl::modifyFallthrough(unsigned vpnNetId,
+ const std::string& physicalInterface,
+ Permission permission, bool add) {
+ if (add) {
+ if (int ret = RouteController::addVirtualNetworkFallthrough(vpnNetId,
+ physicalInterface.c_str(),
+ permission)) {
+ ALOGE("failed to add fallthrough to %s for VPN netId %u", physicalInterface.c_str(),
+ vpnNetId);
+ return ret;
+ }
+ } else {
+ if (int ret = RouteController::removeVirtualNetworkFallthrough(vpnNetId,
+ physicalInterface.c_str(),
+ permission)) {
+ ALOGE("failed to remove fallthrough to %s for VPN netId %u", physicalInterface.c_str(),
+ vpnNetId);
+ return ret;
+ }
+ }
+ return 0;
+}
+
+int NetworkController::DelegateImpl::addFallthrough(const std::string& physicalInterface,
+ Permission permission) {
+ return modifyFallthrough(physicalInterface, permission, true);
+}
+
+int NetworkController::DelegateImpl::removeFallthrough(const std::string& physicalInterface,
+ Permission permission) {
+ return modifyFallthrough(physicalInterface, permission, false);
+}
+
+int NetworkController::DelegateImpl::modifyFallthrough(const std::string& physicalInterface,
+ Permission permission, bool add) {
+ for (const auto& entry : mNetworkController->mNetworks) {
+ if (entry.second->getType() == Network::VIRTUAL) {
+ if (int ret = modifyFallthrough(entry.first, physicalInterface, permission, add)) {
+ return ret;
+ }
+ }
+ }
+ return 0;
+}
+
+NetworkController::NetworkController() :
+ mDelegateImpl(new NetworkController::DelegateImpl(this)), mDefaultNetId(NETID_UNSET) {
mNetworks[LOCAL_NET_ID] = new LocalNetwork(LOCAL_NET_ID);
}
@@ -129,7 +203,7 @@
return -EEXIST;
}
- PhysicalNetwork* physicalNetwork = new PhysicalNetwork(netId);
+ PhysicalNetwork* physicalNetwork = new PhysicalNetwork(netId, mDelegateImpl);
if (int ret = physicalNetwork->setPermission(permission)) {
ALOGE("inconceivable! setPermission cannot fail on an empty network");
delete physicalNetwork;
@@ -153,6 +227,9 @@
}
android::RWLock::AutoWLock lock(mRWLock);
+ if (int ret = modifyFallthroughLocked(netId, true)) {
+ return ret;
+ }
mNetworks[netId] = new VirtualNetwork(netId, hasDns, secure);
return 0;
}
@@ -176,6 +253,10 @@
return ret;
}
mDefaultNetId = NETID_UNSET;
+ } else if (network->getType() == Network::VIRTUAL) {
+ if (int ret = modifyFallthroughLocked(netId, false)) {
+ return ret;
+ }
}
mNetworks.erase(netId);
delete network;
@@ -371,3 +452,22 @@
return add ? RouteController::addRoute(interface, destination, nexthop, tableType) :
RouteController::removeRoute(interface, destination, nexthop, tableType);
}
+
+int NetworkController::modifyFallthroughLocked(unsigned vpnNetId, bool add) {
+ if (mDefaultNetId == NETID_UNSET) {
+ return 0;
+ }
+ Network* network = getNetworkLocked(mDefaultNetId);
+ if (!network || network->getType() != Network::PHYSICAL) {
+ ALOGE("cannot find previously set default network with netId %u", mDefaultNetId);
+ return -ESRCH;
+ }
+ Permission permission = static_cast<PhysicalNetwork*>(network)->getPermission();
+ for (const auto& physicalInterface : network->getInterfaces()) {
+ if (int ret = mDelegateImpl->modifyFallthrough(vpnNetId, physicalInterface, permission,
+ add)) {
+ return ret;
+ }
+ }
+ return 0;
+}
diff --git a/server/NetworkController.h b/server/NetworkController.h
index f065ba5..51af530 100644
--- a/server/NetworkController.h
+++ b/server/NetworkController.h
@@ -90,6 +90,10 @@
int modifyRoute(unsigned netId, const char* interface, const char* destination,
const char* nexthop, bool add, bool legacy, uid_t uid) WARN_UNUSED_RESULT;
+ int modifyFallthroughLocked(unsigned vpnNetId, bool add) WARN_UNUSED_RESULT;
+
+ class DelegateImpl;
+ DelegateImpl* const mDelegateImpl;
// mRWLock guards all accesses to mDefaultNetId, mNetworks, mUsers and mProtectableUsers.
mutable android::RWLock mRWLock;
diff --git a/server/PhysicalNetwork.cpp b/server/PhysicalNetwork.cpp
index 395bea4..62343c4 100644
--- a/server/PhysicalNetwork.cpp
+++ b/server/PhysicalNetwork.cpp
@@ -24,28 +24,38 @@
namespace {
WARN_UNUSED_RESULT int addToDefault(unsigned netId, const std::string& interface,
- Permission permission) {
+ Permission permission, PhysicalNetwork::Delegate* delegate) {
if (int ret = RouteController::addInterfaceToDefaultNetwork(interface.c_str(), permission)) {
ALOGE("failed to add interface %s to default netId %u", interface.c_str(), netId);
return ret;
}
+ if (int ret = delegate->addFallthrough(interface, permission)) {
+ return ret;
+ }
return 0;
}
WARN_UNUSED_RESULT int removeFromDefault(unsigned netId, const std::string& interface,
- Permission permission) {
+ Permission permission,
+ PhysicalNetwork::Delegate* delegate) {
if (int ret = RouteController::removeInterfaceFromDefaultNetwork(interface.c_str(),
permission)) {
ALOGE("failed to remove interface %s from default netId %u", interface.c_str(), netId);
return ret;
}
+ if (int ret = delegate->removeFallthrough(interface, permission)) {
+ return ret;
+ }
return 0;
}
} // namespace
-PhysicalNetwork::PhysicalNetwork(unsigned netId) :
- Network(netId), mPermission(PERMISSION_NONE), mIsDefault(false) {
+PhysicalNetwork::Delegate::~Delegate() {
+}
+
+PhysicalNetwork::PhysicalNetwork(unsigned netId, PhysicalNetwork::Delegate* delegate) :
+ Network(netId), mDelegate(delegate), mPermission(PERMISSION_NONE), mIsDefault(false) {
}
PhysicalNetwork::~PhysicalNetwork() {
@@ -69,10 +79,10 @@
}
if (mIsDefault) {
for (const std::string& interface : mInterfaces) {
- if (int ret = addToDefault(mNetId, interface, permission)) {
+ if (int ret = addToDefault(mNetId, interface, permission, mDelegate)) {
return ret;
}
- if (int ret = removeFromDefault(mNetId, interface, mPermission)) {
+ if (int ret = removeFromDefault(mNetId, interface, mPermission, mDelegate)) {
return ret;
}
}
@@ -86,7 +96,7 @@
return 0;
}
for (const std::string& interface : mInterfaces) {
- if (int ret = addToDefault(mNetId, interface, mPermission)) {
+ if (int ret = addToDefault(mNetId, interface, mPermission, mDelegate)) {
return ret;
}
}
@@ -99,7 +109,7 @@
return 0;
}
for (const std::string& interface : mInterfaces) {
- if (int ret = removeFromDefault(mNetId, interface, mPermission)) {
+ if (int ret = removeFromDefault(mNetId, interface, mPermission, mDelegate)) {
return ret;
}
}
@@ -121,7 +131,7 @@
return ret;
}
if (mIsDefault) {
- if (int ret = addToDefault(mNetId, interface, mPermission)) {
+ if (int ret = addToDefault(mNetId, interface, mPermission, mDelegate)) {
return ret;
}
}
@@ -139,7 +149,7 @@
return ret;
}
if (mIsDefault) {
- if (int ret = removeFromDefault(mNetId, interface, mPermission)) {
+ if (int ret = removeFromDefault(mNetId, interface, mPermission, mDelegate)) {
return ret;
}
}
diff --git a/server/PhysicalNetwork.h b/server/PhysicalNetwork.h
index 6ee118b..2ef10df 100644
--- a/server/PhysicalNetwork.h
+++ b/server/PhysicalNetwork.h
@@ -22,7 +22,17 @@
class PhysicalNetwork : public Network {
public:
- explicit PhysicalNetwork(unsigned netId);
+ class Delegate {
+ public:
+ virtual ~Delegate();
+
+ virtual int addFallthrough(const std::string& physicalInterface,
+ Permission permission) WARN_UNUSED_RESULT = 0;
+ virtual int removeFallthrough(const std::string& physicalInterface,
+ Permission permission) WARN_UNUSED_RESULT = 0;
+ };
+
+ PhysicalNetwork(unsigned netId, Delegate* delegate);
virtual ~PhysicalNetwork();
// These refer to permissions that apps must have in order to use this network.
@@ -37,6 +47,7 @@
int addInterface(const std::string& interface) override WARN_UNUSED_RESULT;
int removeInterface(const std::string& interface) override WARN_UNUSED_RESULT;
+ Delegate* const mDelegate;
Permission mPermission;
bool mIsDefault;
};
diff --git a/server/RouteController.cpp b/server/RouteController.cpp
index 92aead0..92b4a94 100644
--- a/server/RouteController.cpp
+++ b/server/RouteController.cpp
@@ -46,7 +46,7 @@
const uint32_t RULE_PRIORITY_TETHERING = 18000;
const uint32_t RULE_PRIORITY_IMPLICIT_NETWORK = 19000;
const uint32_t RULE_PRIORITY_BYPASSABLE_VPN = 20000;
-// const uint32_t RULE_PRIORITY_VPN_FALLTHROUGH = 21000;
+const uint32_t RULE_PRIORITY_VPN_FALLTHROUGH = 21000;
const uint32_t RULE_PRIORITY_DEFAULT_NETWORK = 22000;
const uint32_t RULE_PRIORITY_DIRECTLY_CONNECTED = 23000;
const uint32_t RULE_PRIORITY_UNREACHABLE = 24000;
@@ -554,6 +554,35 @@
fwmark.intValue, mask.intValue);
}
+// A rule to enable split tunnel VPNs.
+//
+// If a packet with a VPN's netId doesn't find a route in the VPN's routing table, it's allowed to
+// go over the default network, provided it wasn't explicitly restricted to the VPN and has the
+// permissions required by the default network.
+WARN_UNUSED_RESULT int modifyVpnFallthroughRule(uint16_t action, unsigned vpnNetId,
+ const char* physicalInterface,
+ Permission permission) {
+ uint32_t table = getRouteTableForInterface(physicalInterface);
+ if (table == RT_TABLE_UNSPEC) {
+ return -ESRCH;
+ }
+
+ Fwmark fwmark;
+ Fwmark mask;
+
+ fwmark.netId = vpnNetId;
+ mask.netId = FWMARK_NET_ID_MASK;
+
+ fwmark.explicitlySelected = false;
+ mask.explicitlySelected = true;
+
+ fwmark.permission = permission;
+ mask.permission = permission;
+
+ return modifyIpRule(action, RULE_PRIORITY_VPN_FALLTHROUGH, table, fwmark.intValue,
+ mask.intValue);
+}
+
// Add rules to allow legacy routes added through the requestRouteToHost() API.
WARN_UNUSED_RESULT int addLegacyRouteRules() {
Fwmark fwmark;
@@ -938,3 +967,14 @@
int RouteController::disableTethering(const char* inputInterface, const char* outputInterface) {
return modifyTetheredNetwork(RTM_DELRULE, inputInterface, outputInterface);
}
+
+int RouteController::addVirtualNetworkFallthrough(unsigned vpnNetId, const char* physicalInterface,
+ Permission permission) {
+ return modifyVpnFallthroughRule(RTM_NEWRULE, vpnNetId, physicalInterface, permission);
+}
+
+int RouteController::removeVirtualNetworkFallthrough(unsigned vpnNetId,
+ const char* physicalInterface,
+ Permission permission) {
+ return modifyVpnFallthroughRule(RTM_DELRULE, vpnNetId, physicalInterface, permission);
+}
diff --git a/server/RouteController.h b/server/RouteController.h
index aa2d8c9..3d00a66 100644
--- a/server/RouteController.h
+++ b/server/RouteController.h
@@ -75,6 +75,11 @@
const char* outputInterface) WARN_UNUSED_RESULT;
static int disableTethering(const char* inputInterface,
const char* outputInterface) WARN_UNUSED_RESULT;
+
+ static int addVirtualNetworkFallthrough(unsigned vpnNetId, const char* physicalInterface,
+ Permission permission) WARN_UNUSED_RESULT;
+ static int removeVirtualNetworkFallthrough(unsigned vpnNetId, const char* physicalInterface,
+ Permission permission) WARN_UNUSED_RESULT;
};
#endif // NETD_SERVER_ROUTE_CONTROLLER_H