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 \
#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;
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)
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>
// -- "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>
// TODO:
// o tethering
// o p2p
- return syntaxError(cli, "Unknown argument");
+
+ return syntaxError(client, "Unknown argument");
}
#include "FirewallController.h"
#include "ClatdController.h"
+class PermissionsController;
+class RouteController;
+
class CommandListener : public FrameworkListener {
static TetherController *sTetherCtrl;
static NatController *sNatCtrl;
static SecondaryTableController *sSecondaryTableCtrl;
static FirewallController *sFirewallCtrl;
static ClatdController *sClatdCtrl;
+ static PermissionsController* sPermissionsController;
+ static RouteController* sRouteController;
public:
static NetworkController *sNetCtrl;
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);
};
};
--- /dev/null
+/*
+ * 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);
+}
* 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
#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);
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) {
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) {
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);
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),
#ifndef _NETD_NETWORKCONTROLLER_H
#define _NETD_NETWORKCONTROLLER_H
+#include "Permission.h"
+
#include <list>
#include <map>
#include <string>
#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
PID_UNSPECIFIED = 0,
};
- NetworkController();
+ static bool isNetIdValid(unsigned netId);
+
+ NetworkController(PermissionsController* permCtrl,
+ RouteController* routeCtrl);
void clearNetworkPreference();
unsigned getDefaultNetwork() const;
unsigned getNetworkId(const char* interface);
+ bool createNetwork(unsigned netId, const char* interface, Permission permission);
+ bool destroyNetwork(unsigned netId);
+
private:
struct UidEntry {
int uid_start;
std::map<std::string, unsigned> mIfaceNetidMap;
unsigned mNextFreeNetId;
+
+ PermissionsController* const mPermissionsController;
+ RouteController* const mRouteController;
+
+ std::multimap<unsigned, std::string> mNetIdToInterfaces;
};
#endif
* 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;
}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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);
+}
* 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