OSDN Git Service

Implement network create/destroy.
authorSreeram Ramachandran <sreeram@google.com>
Mon, 7 Apr 2014 21:10:04 +0000 (14:10 -0700)
committerSreeram Ramachandran <sreeram@google.com>
Tue, 13 May 2014 18:52:23 +0000 (11:52 -0700)
(cherry picked from commit 382fc20107aa38cca062f10b4bc05c47b6c7f191)

Change-Id: Id5f8df447d812c8e8cf03addbb5e70abdcdfceb9

13 files changed:
Android.mk
CommandListener.cpp
CommandListener.h
Fwmark.cpp [new file with mode: 0644]
Fwmark.h
NetworkController.cpp
NetworkController.h
Permission.cpp [moved from NetId.cpp with 58% similarity]
Permission.h [new file with mode: 0644]
PermissionsController.cpp [new file with mode: 0644]
PermissionsController.h [new file with mode: 0644]
RouteController.cpp [new file with mode: 0644]
RouteController.h [moved from NetId.h with 63% similarity]

index 4e7df6b..66903d7 100644 (file)
@@ -8,18 +8,21 @@ LOCAL_SRC_FILES:=                                      \
                   CommandListener.cpp                  \
                   DnsProxyListener.cpp                 \
                   FirewallController.cpp               \
+                  Fwmark.cpp                           \
                   IdletimerController.cpp              \
                   InterfaceController.cpp              \
                   MDnsSdListener.cpp                   \
                   NatController.cpp                    \
                   NetdCommand.cpp                      \
                   NetdConstants.cpp                    \
-                  NetId.cpp                            \
                   NetlinkHandler.cpp                   \
                   NetlinkManager.cpp                   \
                   NetworkController.cpp                \
+                  Permission.cpp                       \
+                  PermissionsController.cpp            \
                   PppController.cpp                    \
                   ResolverController.cpp               \
+                  RouteController.cpp                  \
                   SecondaryTableController.cpp         \
                   SoftapController.cpp                 \
                   TetherController.cpp                 \
index fb7c371..4cb165c 100644 (file)
 #include "oem_iptables_hook.h"
 #include "NetdConstants.h"
 #include "FirewallController.h"
-#include "NetId.h"
+#include "PermissionsController.h"
+#include "RouteController.h"
 
+#include <string>
+#include <vector>
+
+PermissionsController* CommandListener::sPermissionsController = NULL;
+RouteController* CommandListener::sRouteController = NULL;
 NetworkController *CommandListener::sNetCtrl = NULL;
 TetherController *CommandListener::sTetherCtrl = NULL;
 NatController *CommandListener::sNatCtrl = NULL;
@@ -149,10 +155,14 @@ CommandListener::CommandListener() :
     registerCmd(new ResolverCmd());
     registerCmd(new FirewallCmd());
     registerCmd(new ClatdCmd());
-    registerCmd(new NetworkCmd());
+    registerCmd(new NetworkCommand());
 
+    if (!sPermissionsController)
+        sPermissionsController = new PermissionsController();
+    if (!sRouteController)
+        sRouteController = new RouteController();
     if (!sNetCtrl)
-        sNetCtrl = new NetworkController();
+        sNetCtrl = new NetworkController(sPermissionsController, sRouteController);
     if (!sSecondaryTableCtrl)
         sSecondaryTableCtrl = new SecondaryTableController(sNetCtrl);
     if (!sTetherCtrl)
@@ -1590,79 +1600,76 @@ int CommandListener::ClatdCmd::runCommand(SocketClient *cli, int argc,
     return 0;
 }
 
-CommandListener::NetworkCmd::NetworkCmd() : NetdCommand("network") {
+CommandListener::NetworkCommand::NetworkCommand() : NetdCommand("network") {
+}
+
+int CommandListener::NetworkCommand::syntaxError(SocketClient* client, const char* message) {
+    client->sendMsg(ResponseCode::CommandSyntaxError, message, false);
+    return 0;
+}
+
+int CommandListener::NetworkCommand::paramError(SocketClient* client, const char* message) {
+    client->sendMsg(ResponseCode::CommandParameterError, message, false);
+    return 0;
 }
 
-int CommandListener::NetworkCmd::syntaxError(SocketClient* cli, const char* message) {
-    cli->sendMsg(ResponseCode::CommandSyntaxError, message, false);
+int CommandListener::NetworkCommand::operationError(SocketClient* client, const char* message) {
+    client->sendMsg(ResponseCode::OperationFailed, message, true);
     return 0;
 }
 
-int CommandListener::NetworkCmd::paramError(SocketClient* cli, const char* message) {
-    cli->sendMsg(ResponseCode::CommandParameterError, message, false);
+int CommandListener::NetworkCommand::success(SocketClient* client) {
+    client->sendMsg(ResponseCode::CommandOkay, "success", false);
     return 0;
 }
 
-int CommandListener::NetworkCmd::runCommand(SocketClient* cli, int argc, char** argv) {
+int CommandListener::NetworkCommand::runCommand(SocketClient* client, int argc, char** argv) {
     if (argc < 2) {
-        return syntaxError(cli, "Missing argument");
+        return syntaxError(client, "Missing argument");
     }
+
     //    0      1       2         3         4
     // network create <netId> <interface> [CNS|CI]
     if (!strcmp(argv[1], "create")) {
         if (argc < 4) {
-            return syntaxError(cli, "Missing argument");
-        }
-        unsigned int netId = strtoul(argv[2], NULL, 0);
-        if (!isNetIdValid(netId)) {
-            return paramError(cli, "Invalid NetId");
-        }
-        const char* iface = argv[3];
-        const char* perm = argc > 4 ? argv[4] : NULL;
-        if (perm && strcmp(perm, "CNS") && strcmp(perm, "CI")) {
-            return paramError(cli, "Invalid permission");
-        }
-        // netIdToInterfaces[netId].push_back(iface);
-        // bool is_cns = perm && !strcmp(perm, "CNS");
-        // bool is_ci = perm && !strcmp(perm, "CI");
-        // if (perm) netIdToPermission[netId] = perm;
-        // int table = ...;  // compute routing table number for iface
-        // int fwmark = getFwmark(netId, false, false, is_cns, is_ci);
-        // int mask = getFwmarkMask(true, false, false, is_cns, is_ci);
-        // int exp_fwmark = getFwmark(netId, true, false, is_cns, is_ci);
-        // int exp_mask = getFwmarkMask(true, true, false, is_cns, is_ci);
-        // ip rule add fwmark <exp_fwmark>/<exp_mask> table <table>
-        // ip rule add oif <iface> table <table>
-        // ip rule add fwmark <fwmark>/<mask> table <table>
-        return 0;
+            return syntaxError(client, "Missing argument");
+        }
+        // strtoul() returns 0 on errors, which is fine because 0 is an invalid NetId.
+        unsigned netId = strtoul(argv[2], NULL, 0);
+        if (!sNetCtrl->isNetIdValid(netId)) {
+            return paramError(client, "Invalid netId");
+        }
+        const char* interface = argv[3];
+        Permission permission = PERMISSION_NONE;
+        if (argc > 4) {
+            permission = permissionFromString(argv[4]);
+            if (permission == PERMISSION_NONE) {
+                return paramError(client, "Invalid permission");
+            }
+        }
+        if (!sNetCtrl->createNetwork(netId, interface, permission)) {
+            return operationError(client, "createNetwork() failed");
+        }
+        return success(client);
     }
+
     //    0       1       2
     // network destroy <netId>
     if (!strcmp(argv[1], "destroy")) {
         if (argc < 3) {
-            return syntaxError(cli, "Missing argument");
-        }
-        unsigned int netId = strtoul(argv[2], NULL, 0);
-        if (!isNetIdValid(netId)) {
-            return paramError(cli, "Invalid NetId");
-        }
-        // const char* perm = netIdToPermission[netId].c_str();
-        // bool is_cns = !strcmp(perm, "CNS");
-        // bool is_ci = !strcmp(perm, "CI");
-        // int fwmark = getFwmark(netId, false, false, is_cns, is_ci);
-        // int mask = getFwmarkMask(true, false, false, is_cns, is_ci);
-        // int exp_fwmark = getFwmark(netId, true, false, is_cns, is_ci);
-        // int exp_mask = getFwmarkMask(true, true, false, is_cns, is_ci);
-        // foreach iface in netIdToInterfaces[netId]:
-        //     int table = ...;  // compute routing table number for iface
-        //     ip rule del fwmark <exp_fwmark>/<exp_mask> table <table>
-        //     ip rule del oif <iface> table <table>
-        //     ip rule del fwmark <fwmark>/<mask> table <table>
-        //     ioctl(SIOCKILLADDR, ...);
-        // netIdToInterfaces.erase(netId);
-        // netIdToPermission.erase(netId);
-        return 0;
+            return syntaxError(client, "Missing argument");
+        }
+        // strtoul() returns 0 on errors, which is fine because 0 is an invalid NetId.
+        unsigned netId = strtoul(argv[2], NULL, 0);
+        if (!sNetCtrl->isNetIdValid(netId)) {
+            return paramError(client, "Invalid NetId");
+        }
+        if (!sNetCtrl->destroyNetwork(netId)) {
+            return operationError(client, "destroyNetwork() failed");
+        }
+        return success(client);
     }
+
     // network dns <add|remove> <netId> <num-resolvers> <resolver1> .. <resolverN> [searchDomain1] .. [searchDomainM]
     // network route <add|remove> <other-route-params>
     // network legacy <uid> route <add|remove> <other-route-params>
@@ -1670,7 +1677,7 @@ int CommandListener::NetworkCmd::runCommand(SocketClient* cli, int argc, char**
     //     -- "kill-old-default's-sockets" is a bool
     // network default clear
     //     -- when no interfaces are active (e.g.: airplane mode)
-    // network permission add [CI] [CNS] <uid1> .. <uidN>
+    // network permission set [CI] [CNS] <uid1> .. <uidN>
     // network permission clear <uid1> .. <uidN>
     // network vpn create <netId> [owner_uid]
     // network vpn destroy <netId>
@@ -1679,5 +1686,6 @@ int CommandListener::NetworkCmd::runCommand(SocketClient* cli, int argc, char**
     // TODO:
     //   o tethering
     //   o p2p
-    return syntaxError(cli, "Unknown argument");
+
+    return syntaxError(client, "Unknown argument");
 }
index da00e7c..ad24d87 100644 (file)
@@ -33,6 +33,9 @@
 #include "FirewallController.h"
 #include "ClatdController.h"
 
+class PermissionsController;
+class RouteController;
+
 class CommandListener : public FrameworkListener {
     static TetherController *sTetherCtrl;
     static NatController *sNatCtrl;
@@ -45,6 +48,8 @@ class CommandListener : public FrameworkListener {
     static SecondaryTableController *sSecondaryTableCtrl;
     static FirewallController *sFirewallCtrl;
     static ClatdController *sClatdCtrl;
+    static PermissionsController* sPermissionsController;
+    static RouteController* sRouteController;
 
 public:
     static NetworkController *sNetCtrl;
@@ -145,14 +150,16 @@ private:
         int runCommand(SocketClient *c, int argc, char ** argv);
     };
 
-    class NetworkCmd : public NetdCommand {
+    class NetworkCommand : public NetdCommand {
     public:
-        NetworkCmd();
-        virtual ~NetworkCmd() {}
-        int runCommand(SocketClient* c, int argc, char** argv);
+        NetworkCommand();
+        virtual ~NetworkCommand() {}
+        int runCommand(SocketClient* client, int argc, char** argv);
     private:
         int syntaxError(SocketClient* cli, const char* message);
         int paramError(SocketClient* cli, const char* message);
+        int operationError(SocketClient* cli, const char* message);
+        int success(SocketClient* cli);
     };
 };
 
diff --git a/Fwmark.cpp b/Fwmark.cpp
new file mode 100644 (file)
index 0000000..62aac5b
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Fwmark.h"
+
+namespace {
+
+const uint32_t FWMARK_MASK_NET_ID = 0xffff;
+const uint32_t FWMARK_MASK_EXPLICIT = 0x10000;
+const uint32_t FWMARK_MASK_PROTECT = 0x20000;
+const uint32_t FWMARK_MASK_CHANGE_NETWORK_STATE = 0x40000;
+const uint32_t FWMARK_MASK_CONNECTIVITY_INTERNAL = 0x80000;
+
+}  // namespace
+
+uint32_t getFwmark(unsigned netId, bool exp, bool protect, Permission permission) {
+    uint32_t fwmark = netId & FWMARK_MASK_NET_ID;
+    if (exp) {
+        fwmark |= FWMARK_MASK_EXPLICIT;
+    }
+    if (protect) {
+        fwmark |= FWMARK_MASK_PROTECT;
+    }
+    if (permission == PERMISSION_CHANGE_NETWORK_STATE) {
+        fwmark |= FWMARK_MASK_CHANGE_NETWORK_STATE;
+    } else if (permission == PERMISSION_CONNECTIVITY_INTERNAL) {
+        // CONNECTIVITY_INTERNAL implies CHANGE_NETWORK_STATE.
+        fwmark |= FWMARK_MASK_CHANGE_NETWORK_STATE;
+        fwmark |= FWMARK_MASK_CONNECTIVITY_INTERNAL;
+    }
+    return fwmark;
+}
+
+uint32_t getFwmarkMask(bool netId, bool exp, bool protect, Permission permission) {
+    return getFwmark(netId ? FWMARK_MASK_NET_ID : 0, exp, protect, permission);
+}
index a736bb6..ee3bed0 100644 (file)
--- a/Fwmark.h
+++ b/Fwmark.h
  * limitations under the License.
  */
 
-#ifndef _FWMARK_H
-#define _FWMARK_H
+#ifndef SYSTEM_NETD_FWMARK_H
+#define SYSTEM_NETD_FWMARK_H
 
-const unsigned int FWMARK_NETID = 0xffff;
-const unsigned int FWMARK_EXPLICIT = 0x10000;
-const unsigned int FWMARK_PROTECT = 0x20000;
-const unsigned int FWMARK_CNS = 0x40000;  // CHANGE_NETWORK_STATE
-const unsigned int FWMARK_CI = 0x80000;  // CONNECTIVITY_INTERNAL
+#include "Permission.h"
 
-unsigned int getFwmark(unsigned int netId, bool exp, bool protect, bool cns,
-                       bool ci) {
-    unsigned int fwmark = netId & FWMARK_NETID;
-    if (exp) fwmark |= FWMARK_EXPLICIT;
-    if (protect) fwmark |= FWMARK_PROTECT;
-    if (cns) fwmark |= FWMARK_CNS;
-    if (ci) fwmark |= FWMARK_CI;
-    return fwmark;
-}
+#include <stdint.h>
 
-unsigned int getFwmarkMask(bool netId, bool exp, bool protect, bool cns, bool ci) {
-    return getFwmark(netId ? FWMARK_NETID : 0, exp, protect, cns, ci);
-}
-#endif
+// Composes a fwmark comprising of |netId|, along with bits representing:
+//     |exp|: true if the |netId| is being explicitly requested
+//     |protect|: true if VPNs should be bypassed
+//     |permission|: != PERMISSION_NONE to assert that |permission| is held
+uint32_t getFwmark(unsigned netId, bool exp, bool protect, Permission permission);
+
+// Composes a mask to test parts of the fwmark (see getFwmark() for details).
+uint32_t getFwmarkMask(bool netId, bool exp, bool protect, Permission permission);
+
+#endif  // SYSTEM_NETD_FWMARK_H
index 937347b..fe78935 100644 (file)
 
 #include "NetworkController.h"
 
-// Mark 1 is reserved for SecondaryTableController::PROTECT_MARK.
-NetworkController::NetworkController()
+#include "PermissionsController.h"
+#include "RouteController.h"
+
+namespace {
+
+// Keep these in sync with ConnectivityService.java.
+const unsigned int MIN_NET_ID = 10;
+const unsigned int MAX_NET_ID = 65535;
+
+}  // namespace
+
+bool NetworkController::isNetIdValid(unsigned netId) {
+    return MIN_NET_ID <= netId && netId <= MAX_NET_ID;
+}
+
+NetworkController::NetworkController(PermissionsController* permissionsController,
+                                     RouteController* routeController)
         : mDefaultNetId(NETID_UNSET),
-          mNextFreeNetId(10) {}
+          mNextFreeNetId(MIN_NET_ID),
+          mPermissionsController(permissionsController),
+          mRouteController(routeController) {
+}
 
 void NetworkController::clearNetworkPreference() {
     android::RWLock::AutoWLock lock(mRWLock);
@@ -53,7 +71,7 @@ void NetworkController::setNetworkForPid(int pid, unsigned netId) {
 bool NetworkController::setNetworkForUidRange(int uid_start, int uid_end, unsigned netId,
         bool forward_dns) {
     android::RWLock::AutoWLock lock(mRWLock);
-    if (uid_start > uid_end || netId == NETID_UNSET)
+    if (uid_start > uid_end || !isNetIdValid(netId))
         return false;
 
     for (std::list<UidEntry>::iterator it = mUidMap.begin(); it != mUidMap.end(); ++it) {
@@ -69,7 +87,7 @@ bool NetworkController::setNetworkForUidRange(int uid_start, int uid_end, unsign
 
 bool NetworkController::clearNetworkForUidRange(int uid_start, int uid_end, unsigned netId) {
     android::RWLock::AutoWLock lock(mRWLock);
-    if (uid_start > uid_end || netId == NETID_UNSET)
+    if (uid_start > uid_end || !isNetIdValid(netId))
         return false;
 
     for (std::list<UidEntry>::iterator it = mUidMap.begin(); it != mUidMap.end(); ++it) {
@@ -91,7 +109,7 @@ unsigned NetworkController::getNetwork(int uid, unsigned requested_netId, int pi
             break;
         return it->netId;
     }
-    if (requested_netId != NETID_UNSET)
+    if (isNetIdValid(requested_netId))
         return requested_netId;
     if (pid != PID_UNSPECIFIED) {
         std::map<int, unsigned>::const_iterator it = mPidMap.find(pid);
@@ -111,6 +129,53 @@ unsigned NetworkController::getNetworkId(const char* interface) {
     return netId;
 }
 
+bool NetworkController::createNetwork(unsigned netId, const char* interface,
+                                      Permission permission) {
+    if (!isNetIdValid(netId) || !interface) {
+        return false;
+    }
+
+    typedef std::multimap<unsigned, std::string>::const_iterator Iterator;
+    for (Iterator iter = mNetIdToInterfaces.begin(); iter != mNetIdToInterfaces.end(); ++iter) {
+        if (iter->second == interface) {
+            return false;
+        }
+    }
+
+    if (!mRouteController->createNetwork(netId, interface, permission)) {
+        return false;
+    }
+
+    mPermissionsController->setPermissionForNetwork(netId, permission);
+    mNetIdToInterfaces.insert(std::pair<unsigned, std::string>(netId, interface));
+    return true;
+}
+
+bool NetworkController::destroyNetwork(unsigned netId) {
+    if (!isNetIdValid(netId)) {
+        return false;
+    }
+
+    // TODO: ioctl(SIOCKILLADDR, ...);
+
+    bool status = true;
+
+    Permission permission = mPermissionsController->getPermissionForNetwork(netId);
+
+    typedef std::multimap<unsigned, std::string>::const_iterator Iterator;
+    std::pair<Iterator, Iterator> range = mNetIdToInterfaces.equal_range(netId);
+    for (Iterator iter = range.first; iter != range.second; ++iter) {
+        if (!mRouteController->destroyNetwork(netId, iter->second.c_str(), permission)) {
+            status = false;
+        }
+    }
+
+    mPermissionsController->clearPermissionForNetwork(netId);
+    mNetIdToInterfaces.erase(netId);
+
+    return status;
+}
+
 NetworkController::UidEntry::UidEntry(
     int start, int end, unsigned netId, bool forward_dns)
       : uid_start(start),
index dad011d..c51fc3d 100644 (file)
@@ -17,6 +17,8 @@
 #ifndef _NETD_NETWORKCONTROLLER_H
 #define _NETD_NETWORKCONTROLLER_H
 
+#include "Permission.h"
+
 #include <list>
 #include <map>
 #include <string>
@@ -25,6 +27,9 @@
 #include <stdint.h>
 #include <utils/RWLock.h>
 
+class PermissionsController;
+class RouteController;
+
 /*
  * Keeps track of default, per-pid, and per-uid-range network selection, as
  * well as the mark associated with each network. Networks are identified
@@ -38,7 +43,10 @@ public:
         PID_UNSPECIFIED = 0,
     };
 
-    NetworkController();
+    static bool isNetIdValid(unsigned netId);
+
+    NetworkController(PermissionsController* permCtrl,
+                      RouteController* routeCtrl);
 
     void clearNetworkPreference();
     unsigned getDefaultNetwork() const;
@@ -56,6 +64,9 @@ public:
 
     unsigned getNetworkId(const char* interface);
 
+    bool createNetwork(unsigned netId, const char* interface, Permission permission);
+    bool destroyNetwork(unsigned netId);
+
 private:
     struct UidEntry {
         int uid_start;
@@ -72,6 +83,11 @@ private:
 
     std::map<std::string, unsigned> mIfaceNetidMap;
     unsigned mNextFreeNetId;
+
+    PermissionsController* const mPermissionsController;
+    RouteController* const mRouteController;
+
+    std::multimap<unsigned, std::string> mNetIdToInterfaces;
 };
 
 #endif
similarity index 58%
rename from NetId.cpp
rename to Permission.cpp
index a07cc9f..c5bf677 100644 (file)
--- a/NetId.cpp
  * limitations under the License.
  */
 
-#include "NetId.h"
+#include "Permission.h"
 
-bool isNetIdValid(unsigned int netId) {
-    return MIN_NET_ID <= netId && netId <= MAX_NET_ID;
+#include <string.h>
+
+Permission permissionFromString(const char* permission) {
+    if (permission) {
+        if (!strcmp(permission, "android.permission.CHANGE_NETWORK_STATE")) {
+            return PERMISSION_CHANGE_NETWORK_STATE;
+        }
+        if (!strcmp(permission, "android.permission.CONNECTIVITY_INTERNAL")) {
+            return PERMISSION_CONNECTIVITY_INTERNAL;
+        }
+    }
+    return PERMISSION_NONE;
 }
diff --git a/Permission.h b/Permission.h
new file mode 100644 (file)
index 0000000..bc51b27
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SYSTEM_NETD_PERMISSION_H
+#define SYSTEM_NETD_PERMISSION_H
+
+// This enum represents the permissions we care about for networking. When applied to an app, it's
+// the permission the app (UID) has been granted. When applied to a network, it's the permission an
+// app must hold to be allowed to use the network. PERMISSION_NONE means "no special permission is
+// held by the app" or "no special permission is required to use the network".
+//
+// Currently, each permission includes all the permissions above it (i.e., CONNECTIVITY_INTERNAL
+// implies CHANGE_NETWORK_STATE), which is why these are not bit values that need to be OR'ed
+// together. This may change in the future.
+enum Permission {
+    PERMISSION_NONE,
+    PERMISSION_CHANGE_NETWORK_STATE,
+    PERMISSION_CONNECTIVITY_INTERNAL
+};
+
+Permission permissionFromString(const char* permission);
+
+#endif  // SYSTEM_NETD_PERMISSION_H
diff --git a/PermissionsController.cpp b/PermissionsController.cpp
new file mode 100644 (file)
index 0000000..dc72229
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "PermissionsController.h"
+
+Permission PermissionsController::getPermissionForNetwork(unsigned netId) const {
+    std::map<unsigned, Permission>::const_iterator iter = mNetworks.find(netId);
+    return iter != mNetworks.end() ? iter->second : PERMISSION_NONE;
+}
+
+void PermissionsController::setPermissionForNetwork(unsigned netId, Permission permission) {
+    if (permission == PERMISSION_NONE) {
+        clearPermissionForNetwork(netId);
+        return;
+    }
+    mNetworks[netId] = permission;
+}
+
+void PermissionsController::clearPermissionForNetwork(unsigned netId) {
+    mNetworks.erase(netId);
+}
diff --git a/PermissionsController.h b/PermissionsController.h
new file mode 100644 (file)
index 0000000..f7acd30
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SYSTEM_NETD_PERMISSIONS_CONTROLLER_H
+#define SYSTEM_NETD_PERMISSIONS_CONTROLLER_H
+
+#include "Permission.h"
+
+#include <map>
+
+class PermissionsController {
+public:
+    Permission getPermissionForNetwork(unsigned netId) const;
+    void setPermissionForNetwork(unsigned netId, Permission permission);
+    void clearPermissionForNetwork(unsigned netId);
+
+private:
+    std::map<unsigned, Permission> mNetworks;
+};
+
+#endif  // SYSTEM_NETD_PERMISSIONS_CONTROLLER_H
diff --git a/RouteController.cpp b/RouteController.cpp
new file mode 100644 (file)
index 0000000..b686a46
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "RouteController.h"
+
+#include "Fwmark.h"
+#include "NetdConstants.h"
+
+#include <logwrap/logwrap.h>
+#include <net/if.h>
+#include <stdio.h>
+
+namespace {
+
+// TODO: Keep this in sync with the kernel.
+const uint32_t ROUTE_TABLE_OFFSET_FROM_INDEX = 255;
+
+const char* const RULE_PRIORITY_PER_NETWORK_EXPLICIT = "300";
+const char* const RULE_PRIORITY_PER_NETWORK_OIF = "400";
+const char* const RULE_PRIORITY_PER_NETWORK_NORMAL = "700";
+
+const bool FWMARK_USE_NET_ID = true;
+const bool FWMARK_USE_EXPLICIT = true;
+const bool FWMARK_USE_PROTECT = true;
+
+// TODO: Tell the kernel about the offset using sysctls during init.
+uint32_t getRouteTableForInterface(const char* interface) {
+    uint32_t index = static_cast<uint32_t>(if_nametoindex(interface));
+    return index ? index + ROUTE_TABLE_OFFSET_FROM_INDEX : 0;
+}
+
+bool runIpRuleCommand(const char* action, const char* priority, const char* table,
+                      const char* fwmark, const char* oif) {
+    const char* version[] = {"-4", "-6"};
+    for (size_t i = 0; i < ARRAY_SIZE(version); ++i) {
+        int argc = 0;
+        const char* argv[16];
+
+        argv[argc++] = IP_PATH;
+        argv[argc++] = version[i];
+        argv[argc++] = "rule";
+        argv[argc++] = action;
+        argv[argc++] = "priority";
+        argv[argc++] = priority;
+        argv[argc++] = "table";
+        argv[argc++] = table;
+        if (fwmark) {
+            argv[argc++] = "fwmark";
+            argv[argc++] = fwmark;
+        }
+        if (oif) {
+            argv[argc++] = "oif";
+            argv[argc++] = oif;
+        }
+        if (android_fork_execvp(argc, const_cast<char**>(argv), NULL, false, false)) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool modifyNetwork(unsigned netId, const char* interface, Permission permission, bool add) {
+    uint32_t table = getRouteTableForInterface(interface);
+    if (!table) {
+        return false;
+    }
+
+    char table_string[sizeof("0x12345678")];
+    snprintf(table_string, sizeof(table_string), "0x%x", table);
+
+    char mark_string[sizeof("0x12345678/0x12345678")];
+    const char* action = add ? ADD : DEL;
+
+    // A rule to route traffic based on an explicitly chosen network.
+    //
+    // Supports apps that use the multinetwork APIs to restrict their traffic to a network.
+    //
+    // We don't really need to check the permission bits of the fwmark here, as they would've been
+    // checked at the time the netId was set into the fwmark, but we do so to be consistent.
+    uint32_t fwmark = getFwmark(netId, FWMARK_USE_EXPLICIT, !FWMARK_USE_PROTECT, permission);
+    uint32_t mask = getFwmarkMask(FWMARK_USE_NET_ID, FWMARK_USE_EXPLICIT, !FWMARK_USE_PROTECT,
+                                  permission);
+    snprintf(mark_string, sizeof(mark_string), "0x%x/0x%x", fwmark, mask);
+    if (!runIpRuleCommand(action, RULE_PRIORITY_PER_NETWORK_EXPLICIT, table_string, mark_string,
+                          NULL)) {
+        return false;
+    }
+
+    // A rule to route traffic based on a chosen outgoing interface.
+    //
+    // Supports apps that use SO_BINDTODEVICE or IP_PKTINFO options and the kernel that already
+    // knows the outgoing interface (typically for link-local communications).
+    fwmark = getFwmark(0, !FWMARK_USE_EXPLICIT, !FWMARK_USE_PROTECT, permission);
+    mask = getFwmark(!FWMARK_USE_NET_ID, !FWMARK_USE_EXPLICIT, !FWMARK_USE_PROTECT, permission);
+    snprintf(mark_string, sizeof(mark_string), "0x%x/0x%x", fwmark, mask);
+    if (!runIpRuleCommand(action, RULE_PRIORITY_PER_NETWORK_OIF, table_string, mark_string,
+                          interface)) {
+        return false;
+    }
+
+    // A rule to route traffic based on the chosen network.
+    //
+    // This is for sockets that have not explicitly requested a particular network, but have been
+    // bound to one when they called connect(). This ensures that sockets connected on a particular
+    // network stay on that network even if the default network changes.
+    fwmark = getFwmark(netId, !FWMARK_USE_EXPLICIT, !FWMARK_USE_PROTECT, permission);
+    mask = getFwmarkMask(FWMARK_USE_NET_ID, !FWMARK_USE_EXPLICIT, !FWMARK_USE_PROTECT, permission);
+    snprintf(mark_string, sizeof(mark_string), "0x%x/0x%x", fwmark, mask);
+    if (!runIpRuleCommand(action, RULE_PRIORITY_PER_NETWORK_NORMAL, table_string, mark_string,
+                          NULL)) {
+        return false;
+    }
+
+    // An iptables rule to mark incoming packets on a network with the netId of the network.
+    //
+    // This is so that the kernel can:
+    // + Use the right fwmark for (and thus correctly route) replies (e.g.: TCP RST, ICMP errors,
+    //   ping replies, etc).
+    // + Mark sockets that accept connections from this interface so that the connection stays on
+    //   the same interface.
+    action = add ? "-A" : "-D";
+    snprintf(mark_string, sizeof(mark_string), "0x%x", netId);
+    if (execIptables(V4V6, "-t", "mangle", action, "INPUT", "-i", interface, "-j", "MARK",
+                     "--set-mark", mark_string, NULL)) {
+        return false;
+    }
+
+    return true;
+}
+
+}  // namespace
+
+bool RouteController::createNetwork(unsigned netId, const char* interface, Permission permission) {
+    return modifyNetwork(netId, interface, permission, true);
+}
+
+bool RouteController::destroyNetwork(unsigned netId, const char* interface, Permission permission) {
+    return modifyNetwork(netId, interface, permission, false);
+}
similarity index 63%
rename from NetId.h
rename to RouteController.h
index 207d29e..b600bc0 100644 (file)
--- a/NetId.h
  * limitations under the License.
  */
 
-#ifndef _NET_ID_H
-#define _NET_ID_H
+#ifndef SYSTEM_NETD_ROUTE_CONTROLLER_H
+#define SYSTEM_NETD_ROUTE_CONTROLLER_H
 
-// Keep these in sync with ConnectivityService.java.
-const unsigned int MIN_NET_ID = 10;
-const unsigned int MAX_NET_ID = 65535;
+#include "Permission.h"
 
-bool isNetIdValid(unsigned int netId);
+class RouteController {
+public:
+    static bool createNetwork(unsigned netId, const char* interface, Permission permission);
+    static bool destroyNetwork(unsigned netId, const char* interface, Permission permission);
+};
 
-#endif
+#endif  // SYSTEM_NETD_ROUTE_CONTROLLER_H