OSDN Git Service

Implement support for bypassable VPNs.
authorSreeram Ramachandran <sreeram@google.com>
Wed, 23 Jul 2014 20:27:31 +0000 (13:27 -0700)
committerSreeram Ramachandran <sreeram@google.com>
Fri, 25 Jul 2014 21:58:08 +0000 (14:58 -0700)
Bypassable VPNs grab all traffic by default (just like secure VPNs), but:
+ They allow all apps to choose other networks using the multinetwork APIs.
  If these other networks are insecure ("untrusted"), they will enforce that the
  app holds the necessary permissions, such as CHANGE_NETWORK_STATE.
+ They support consistent routing. If an app has an existing connection over
  some other network when the bypassable VPN comes up, it's not interrupted.

Bug: 15347374
Change-Id: Iaee9c6f6fa8103215738570d2b65d3fcf10343f3

server/CommandListener.cpp
server/NetworkController.cpp
server/NetworkController.h
server/RouteController.cpp
server/RouteController.h
server/VirtualNetwork.cpp
server/VirtualNetwork.h

index b6f9630..ab6b952 100644 (file)
@@ -1413,16 +1413,17 @@ int CommandListener::NetworkCommand::runCommand(SocketClient* client, int argc,
     //    0      1       2         3
     // network create <netId> [permission]
     //
-    //    0      1       2     3     4
-    // network create <netId> vpn <hasDns>
+    //    0      1       2     3     4        5
+    // network create <netId> vpn <hasDns> <secure>
     if (!strcmp(argv[1], "create")) {
         if (argc < 3) {
             return syntaxError(client, "Missing argument");
         }
         unsigned netId = stringToNetId(argv[2]);
-        if (argc == 5 && !strcmp(argv[3], "vpn")) {
+        if (argc == 6 && !strcmp(argv[3], "vpn")) {
             bool hasDns = atoi(argv[4]);
-            if (int ret = sNetCtrl->createVirtualNetwork(netId, hasDns)) {
+            bool secure = atoi(argv[5]);
+            if (int ret = sNetCtrl->createVirtualNetwork(netId, hasDns, secure)) {
                 return operationError(client, "createVirtualNetwork() failed", ret);
             }
         } else if (argc > 4) {
index 90b7682..d151490 100644 (file)
@@ -141,7 +141,7 @@ int NetworkController::createPhysicalNetwork(unsigned netId, Permission permissi
     return 0;
 }
 
-int NetworkController::createVirtualNetwork(unsigned netId, bool hasDns) {
+int NetworkController::createVirtualNetwork(unsigned netId, bool hasDns, bool secure) {
     if (netId < MIN_NET_ID || netId > MAX_NET_ID) {
         ALOGE("invalid netId %u", netId);
         return -EINVAL;
@@ -153,7 +153,7 @@ int NetworkController::createVirtualNetwork(unsigned netId, bool hasDns) {
     }
 
     android::RWLock::AutoWLock lock(mRWLock);
-    mNetworks[netId] = new VirtualNetwork(netId, hasDns);
+    mNetworks[netId] = new VirtualNetwork(netId, hasDns, secure);
     return 0;
 }
 
@@ -236,7 +236,8 @@ bool NetworkController::canUserSelectNetwork(uid_t uid, unsigned netId) const {
         return static_cast<VirtualNetwork*>(network)->appliesToUser(uid);
     }
     VirtualNetwork* virtualNetwork = getVirtualNetworkForUserLocked(uid);
-    if (virtualNetwork && mProtectableUsers.find(uid) == mProtectableUsers.end()) {
+    if (virtualNetwork && virtualNetwork->isSecure() &&
+            mProtectableUsers.find(uid) == mProtectableUsers.end()) {
         return false;
     }
     Permission networkPermission = static_cast<PhysicalNetwork*>(network)->getPermission();
index f0b42c4..f065ba5 100644 (file)
@@ -56,7 +56,7 @@ public:
     bool isVirtualNetwork(unsigned netId) const;
 
     int createPhysicalNetwork(unsigned netId, Permission permission) WARN_UNUSED_RESULT;
-    int createVirtualNetwork(unsigned netId, bool hasDns) WARN_UNUSED_RESULT;
+    int createVirtualNetwork(unsigned netId, bool hasDns, bool secure) WARN_UNUSED_RESULT;
     int destroyNetwork(unsigned netId) WARN_UNUSED_RESULT;
 
     int addInterfaceToNetwork(unsigned netId, const char* interface) WARN_UNUSED_RESULT;
index f7454ad..92aead0 100644 (file)
@@ -45,7 +45,7 @@ const uint32_t RULE_PRIORITY_LEGACY_NETWORK      = 16000;
 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_BYPASSABLE_VPN      = 20000;
 // const uint32_t RULE_PRIORITY_VPN_FALLTHROUGH     = 21000;
 const uint32_t RULE_PRIORITY_DEFAULT_NETWORK     = 22000;
 const uint32_t RULE_PRIORITY_DIRECTLY_CONNECTED  = 23000;
@@ -444,15 +444,26 @@ WARN_UNUSED_RESULT int modifyVpnOutputToLocalRule(const char* vpnInterface, bool
 // have, if they are subject to this VPN, their traffic has to go through it. Allows the traffic to
 // bypass the VPN if the protectedFromVpn bit is set.
 WARN_UNUSED_RESULT int modifyVpnUidRangeRule(uint32_t table, uid_t uidStart, uid_t uidEnd,
-                                             bool add) {
+                                             bool secure, bool add) {
     Fwmark fwmark;
     Fwmark mask;
 
     fwmark.protectedFromVpn = false;
     mask.protectedFromVpn = true;
 
-    return modifyIpRule(add ? RTM_NEWRULE : RTM_DELRULE, RULE_PRIORITY_SECURE_VPN, table,
-                        fwmark.intValue, mask.intValue, IIF_NONE, OIF_NONE, uidStart, uidEnd);
+    uint32_t priority;
+
+    if (secure) {
+        priority = RULE_PRIORITY_SECURE_VPN;
+    } else {
+        priority = RULE_PRIORITY_BYPASSABLE_VPN;
+
+        fwmark.explicitlySelected = false;
+        mask.explicitlySelected = true;
+    }
+
+    return modifyIpRule(add ? RTM_NEWRULE : RTM_DELRULE, priority, table, fwmark.intValue,
+                        mask.intValue, IIF_NONE, OIF_NONE, uidStart, uidEnd);
 }
 
 // A rule to allow system apps to send traffic over this VPN even if they are not part of the target
@@ -460,7 +471,8 @@ WARN_UNUSED_RESULT int modifyVpnUidRangeRule(uint32_t table, uid_t uidStart, uid
 //
 // This is needed for DnsProxyListener to correctly resolve a request for a user who is in the
 // target set, but where the DnsProxyListener itself is not.
-WARN_UNUSED_RESULT int modifyVpnSystemPermissionRule(unsigned netId, uint32_t table, bool add) {
+WARN_UNUSED_RESULT int modifyVpnSystemPermissionRule(unsigned netId, uint32_t table, bool secure,
+                                                     bool add) {
     Fwmark fwmark;
     Fwmark mask;
 
@@ -470,8 +482,10 @@ WARN_UNUSED_RESULT int modifyVpnSystemPermissionRule(unsigned netId, uint32_t ta
     fwmark.permission = PERMISSION_SYSTEM;
     mask.permission = PERMISSION_SYSTEM;
 
-    return modifyIpRule(add ? RTM_NEWRULE : RTM_DELRULE, RULE_PRIORITY_SECURE_VPN, table,
-                        fwmark.intValue, mask.intValue);
+    uint32_t priority = secure ? RULE_PRIORITY_SECURE_VPN : RULE_PRIORITY_BYPASSABLE_VPN;
+
+    return modifyIpRule(add ? RTM_NEWRULE : RTM_DELRULE, priority, table, fwmark.intValue,
+                        mask.intValue);
 }
 
 // A rule to route traffic based on an explicitly chosen network.
@@ -636,7 +650,7 @@ WARN_UNUSED_RESULT int modifyPhysicalNetwork(unsigned netId, const char* interfa
 }
 
 WARN_UNUSED_RESULT int modifyVirtualNetwork(unsigned netId, const char* interface,
-                                            const UidRanges& uidRanges, bool add,
+                                            const UidRanges& uidRanges, bool secure, bool add,
                                             bool modifyNonUidBasedRules) {
     uint32_t table = getRouteTableForInterface(interface);
     if (table == RT_TABLE_UNSPEC) {
@@ -644,7 +658,7 @@ WARN_UNUSED_RESULT int modifyVirtualNetwork(unsigned netId, const char* interfac
     }
 
     for (const UidRanges::Range& range : uidRanges.getRanges()) {
-        if (int ret = modifyVpnUidRangeRule(table, range.first, range.second, add)) {
+        if (int ret = modifyVpnUidRangeRule(table, range.first, range.second, secure, add)) {
             return ret;
         }
         if (int ret = modifyExplicitNetworkRule(netId, table, PERMISSION_NONE, range.first,
@@ -664,7 +678,7 @@ WARN_UNUSED_RESULT int modifyVirtualNetwork(unsigned netId, const char* interfac
         if (int ret = modifyVpnOutputToLocalRule(interface, add)) {
             return ret;
         }
-        if (int ret = modifyVpnSystemPermissionRule(netId, table, add)) {
+        if (int ret = modifyVpnSystemPermissionRule(netId, table, secure, add)) {
             return ret;
         }
         return modifyExplicitNetworkRule(netId, table, PERMISSION_NONE, UID_ROOT, UID_ROOT, add);
@@ -854,8 +868,8 @@ int RouteController::removeInterfaceFromPhysicalNetwork(unsigned netId, const ch
 }
 
 int RouteController::addInterfaceToVirtualNetwork(unsigned netId, const char* interface,
-                                                  const UidRanges& uidRanges) {
-    if (int ret = modifyVirtualNetwork(netId, interface, uidRanges, ACTION_ADD,
+                                                  bool secure, const UidRanges& uidRanges) {
+    if (int ret = modifyVirtualNetwork(netId, interface, uidRanges, secure, ACTION_ADD,
                                        MODIFY_NON_UID_BASED_RULES)) {
         return ret;
     }
@@ -864,8 +878,8 @@ int RouteController::addInterfaceToVirtualNetwork(unsigned netId, const char* in
 }
 
 int RouteController::removeInterfaceFromVirtualNetwork(unsigned netId, const char* interface,
-                                                       const UidRanges& uidRanges) {
-    if (int ret = modifyVirtualNetwork(netId, interface, uidRanges, ACTION_DEL,
+                                                       bool secure, const UidRanges& uidRanges) {
+    if (int ret = modifyVirtualNetwork(netId, interface, uidRanges, secure, ACTION_DEL,
                                        MODIFY_NON_UID_BASED_RULES)) {
         return ret;
     }
@@ -886,15 +900,15 @@ int RouteController::modifyPhysicalNetworkPermission(unsigned netId, const char*
     return modifyPhysicalNetwork(netId, interface, oldPermission, ACTION_DEL);
 }
 
-int RouteController::addUsersToVirtualNetwork(unsigned netId, const char* interface,
+int RouteController::addUsersToVirtualNetwork(unsigned netId, const char* interface, bool secure,
                                               const UidRanges& uidRanges) {
-    return modifyVirtualNetwork(netId, interface, uidRanges, ACTION_ADD,
+    return modifyVirtualNetwork(netId, interface, uidRanges, secure, ACTION_ADD,
                                 !MODIFY_NON_UID_BASED_RULES);
 }
 
 int RouteController::removeUsersFromVirtualNetwork(unsigned netId, const char* interface,
-                                                   const UidRanges& uidRanges) {
-    return modifyVirtualNetwork(netId, interface, uidRanges, ACTION_DEL,
+                                                   bool secure, const UidRanges& uidRanges) {
+    return modifyVirtualNetwork(netId, interface, uidRanges, secure, ACTION_DEL,
                                 !MODIFY_NON_UID_BASED_RULES);
 }
 
index 1d6075d..aa2d8c9 100644 (file)
@@ -47,18 +47,18 @@ public:
     static int removeInterfaceFromPhysicalNetwork(unsigned netId, const char* interface,
                                                   Permission permission) WARN_UNUSED_RESULT;
 
-    static int addInterfaceToVirtualNetwork(unsigned netId, const char* interface,
+    static int addInterfaceToVirtualNetwork(unsigned netId, const char* interface, bool secure,
                                             const UidRanges& uidRanges) WARN_UNUSED_RESULT;
-    static int removeInterfaceFromVirtualNetwork(unsigned netId, const char* interface,
+    static int removeInterfaceFromVirtualNetwork(unsigned netId, const char* interface, bool secure,
                                                  const UidRanges& uidRanges) WARN_UNUSED_RESULT;
 
     static int modifyPhysicalNetworkPermission(unsigned netId, const char* interface,
                                                Permission oldPermission,
                                                Permission newPermission) WARN_UNUSED_RESULT;
 
-    static int addUsersToVirtualNetwork(unsigned netId, const char* interface,
+    static int addUsersToVirtualNetwork(unsigned netId, const char* interface, bool secure,
                                         const UidRanges& uidRanges) WARN_UNUSED_RESULT;
-    static int removeUsersFromVirtualNetwork(unsigned netId, const char* interface,
+    static int removeUsersFromVirtualNetwork(unsigned netId, const char* interface, bool secure,
                                              const UidRanges& uidRanges) WARN_UNUSED_RESULT;
 
     static int addInterfaceToDefaultNetwork(const char* interface,
index 565bd55..5db3645 100644 (file)
@@ -21,7 +21,8 @@
 #define LOG_TAG "Netd"
 #include "log/log.h"
 
-VirtualNetwork::VirtualNetwork(unsigned netId, bool hasDns): Network(netId), mHasDns(hasDns) {
+VirtualNetwork::VirtualNetwork(unsigned netId, bool hasDns, bool secure) :
+        Network(netId), mHasDns(hasDns), mSecure(secure) {
 }
 
 VirtualNetwork::~VirtualNetwork() {
@@ -31,13 +32,17 @@ bool VirtualNetwork::getHasDns() const {
     return mHasDns;
 }
 
+bool VirtualNetwork::isSecure() const {
+    return mSecure;
+}
+
 bool VirtualNetwork::appliesToUser(uid_t uid) const {
     return mUidRanges.hasUid(uid);
 }
 
 int VirtualNetwork::addUsers(const UidRanges& uidRanges) {
     for (const std::string& interface : mInterfaces) {
-        if (int ret = RouteController::addUsersToVirtualNetwork(mNetId, interface.c_str(),
+        if (int ret = RouteController::addUsersToVirtualNetwork(mNetId, interface.c_str(), mSecure,
                                                                 uidRanges)) {
             ALOGE("failed to add users on interface %s of netId %u", interface.c_str(), mNetId);
             return ret;
@@ -50,7 +55,7 @@ int VirtualNetwork::addUsers(const UidRanges& uidRanges) {
 int VirtualNetwork::removeUsers(const UidRanges& uidRanges) {
     for (const std::string& interface : mInterfaces) {
         if (int ret = RouteController::removeUsersFromVirtualNetwork(mNetId, interface.c_str(),
-                                                                     uidRanges)) {
+                                                                     mSecure, uidRanges)) {
             ALOGE("failed to remove users on interface %s of netId %u", interface.c_str(), mNetId);
             return ret;
         }
@@ -67,7 +72,7 @@ int VirtualNetwork::addInterface(const std::string& interface) {
     if (hasInterface(interface)) {
         return 0;
     }
-    if (int ret = RouteController::addInterfaceToVirtualNetwork(mNetId, interface.c_str(),
+    if (int ret = RouteController::addInterfaceToVirtualNetwork(mNetId, interface.c_str(), mSecure,
                                                                 mUidRanges)) {
         ALOGE("failed to add interface %s to VPN netId %u", interface.c_str(), mNetId);
         return ret;
@@ -81,7 +86,7 @@ int VirtualNetwork::removeInterface(const std::string& interface) {
         return 0;
     }
     if (int ret = RouteController::removeInterfaceFromVirtualNetwork(mNetId, interface.c_str(),
-                                                                     mUidRanges)) {
+                                                                     mSecure, mUidRanges)) {
         ALOGE("failed to remove interface %s from VPN netId %u", interface.c_str(), mNetId);
         return ret;
     }
index 92a1b0e..d315f97 100644 (file)
 #include "Network.h"
 #include "UidRanges.h"
 
+// A VirtualNetwork may be "secure" or not.
+//
+// A secure VPN is the usual type of VPN that grabs the default route (and thus all user traffic).
+// Only a few privileged UIDs may skip the VPN and go directly to the underlying physical network.
+//
+// A non-secure VPN ("bypassable" VPN) also grabs all user traffic by default. But all apps are
+// permitted to skip it and pick any other network for their connections.
 class VirtualNetwork : public Network {
 public:
-    VirtualNetwork(unsigned netId, bool hasDns);
+    VirtualNetwork(unsigned netId, bool hasDns, bool secure);
     virtual ~VirtualNetwork();
 
     bool getHasDns() const;
+    bool isSecure() const;
     bool appliesToUser(uid_t uid) const;
 
     int addUsers(const UidRanges& uidRanges) WARN_UNUSED_RESULT;
@@ -37,6 +45,7 @@ private:
     int removeInterface(const std::string& interface) override WARN_UNUSED_RESULT;
 
     const bool mHasDns;
+    const bool mSecure;
     UidRanges mUidRanges;
 };