OSDN Git Service

Implement the fallthrough rule to support split tunnel VPNs.
authorSreeram Ramachandran <sreeram@google.com>
Wed, 23 Jul 2014 05:23:20 +0000 (22:23 -0700)
committerSreeram Ramachandran <sreeram@google.com>
Fri, 25 Jul 2014 23:27:04 +0000 (16:27 -0700)
Change-Id: Ibc48caedb5954c6b12bfa553d978bab56c4b09aa

server/Network.cpp
server/Network.h
server/NetworkController.cpp
server/NetworkController.h
server/PhysicalNetwork.cpp
server/PhysicalNetwork.h
server/RouteController.cpp
server/RouteController.h

index 5104de2..0ca6247 100644 (file)
@@ -33,6 +33,10 @@ bool Network::hasInterface(const std::string& interface) const {
     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
index 39c81aa..115997a 100644 (file)
@@ -40,6 +40,7 @@ public:
     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;
index d151490..3d979f9 100644 (file)
@@ -50,7 +50,81 @@ const unsigned MAX_NET_ID = 65535;
 
 }  // 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 @@ int NetworkController::createPhysicalNetwork(unsigned netId, Permission permissi
         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 @@ int NetworkController::createVirtualNetwork(unsigned netId, bool hasDns, bool se
     }
 
     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 @@ int NetworkController::destroyNetwork(unsigned netId) {
             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 @@ int NetworkController::modifyRoute(unsigned netId, const char* interface, const
     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;
+}
index f065ba5..51af530 100644 (file)
@@ -90,6 +90,10 @@ private:
 
     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;
index 395bea4..62343c4 100644 (file)
 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 @@ int PhysicalNetwork::setPermission(Permission permission) {
     }
     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 @@ int PhysicalNetwork::addAsDefault() {
         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 @@ int PhysicalNetwork::removeAsDefault() {
         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 @@ int PhysicalNetwork::addInterface(const std::string& interface) {
         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 @@ int PhysicalNetwork::removeInterface(const std::string& interface) {
         return ret;
     }
     if (mIsDefault) {
-        if (int ret = removeFromDefault(mNetId, interface, mPermission)) {
+        if (int ret = removeFromDefault(mNetId, interface, mPermission, mDelegate)) {
             return ret;
         }
     }
index 6ee118b..2ef10df 100644 (file)
 
 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 @@ private:
     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;
 };
index 92aead0..92b4a94 100644 (file)
@@ -46,7 +46,7 @@ const uint32_t RULE_PRIORITY_LOCAL_NETWORK       = 17000;
 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 @@ WARN_UNUSED_RESULT int modifyImplicitNetworkRule(unsigned netId, uint32_t table,
                         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::enableTethering(const char* inputInterface, const char* out
 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);
+}
index aa2d8c9..3d00a66 100644 (file)
@@ -75,6 +75,11 @@ public:
                                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