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
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;
} // 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);
}
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;
}
android::RWLock::AutoWLock lock(mRWLock);
+ if (int ret = modifyFallthroughLocked(netId, true)) {
+ return ret;
+ }
mNetworks[netId] = new VirtualNetwork(netId, hasDns, secure);
return 0;
}
return ret;
}
mDefaultNetId = NETID_UNSET;
+ } else if (network->getType() == Network::VIRTUAL) {
+ if (int ret = modifyFallthroughLocked(netId, false)) {
+ return ret;
+ }
}
mNetworks.erase(netId);
delete network;
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;
+}
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;
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() {
}
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;
}
}
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;
}
}
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;
}
}
return ret;
}
if (mIsDefault) {
- if (int ret = addToDefault(mNetId, interface, mPermission)) {
+ if (int ret = addToDefault(mNetId, interface, mPermission, mDelegate)) {
return ret;
}
}
return ret;
}
if (mIsDefault) {
- if (int ret = removeFromDefault(mNetId, interface, mPermission)) {
+ if (int ret = removeFromDefault(mNetId, interface, mPermission, mDelegate)) {
return ret;
}
}
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.
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;
};
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;
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;
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);
+}
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