Implement the "network route add/remove" API.
(cherry picked from commit 909c52248a4ad832548c5feb5e9a58323e30ab7b)
Change-Id: Idb09b0c22a3a519dda232995ced406e470ab9768
diff --git a/CommandListener.cpp b/CommandListener.cpp
index 34f356c..542e5f7 100644
--- a/CommandListener.cpp
+++ b/CommandListener.cpp
@@ -1754,8 +1754,35 @@
return success(client);
}
- // network dns <add|remove> <netId> <num-resolvers> <resolver1> .. <resolverN> [searchDomain1] .. [searchDomainM]
- // network route <add|remove> <other-route-params>
+ // 0 1 2 3 4 5 6
+ // network route add <netId> <interface> <destination> [nexthop]
+ // network route remove <netId> <interface> <destination> [nexthop]
+ if (!strcmp(argv[1], "route")) {
+ if (argc < 6 || argc > 7) {
+ return syntaxError(client, "Incorrect number of arguments");
+ }
+ // strtoul() returns 0 on errors, which is fine because 0 is an invalid netId.
+ unsigned netId = strtoul(argv[3], NULL, 0);
+ if (!sNetCtrl->isNetIdValid(netId)) {
+ return paramError(client, "Invalid netId");
+ }
+ const char* interface = argv[4];
+ const char* destination = argv[5];
+ const char* nexthop = argc == 7 ? argv[6] : NULL;
+ if (!strcmp(argv[2], "add")) {
+ if (!sNetCtrl->addRoute(netId, interface, destination, nexthop)) {
+ return operationError(client, "addRoute() failed");
+ }
+ } else if (!strcmp(argv[2], "remove")) {
+ if (!sNetCtrl->removeRoute(netId, interface, destination, nexthop)) {
+ return operationError(client, "removeRoute() failed");
+ }
+ } else {
+ return syntaxError(client, "Unknown argument");
+ }
+ return success(client);
+ }
+
// network legacy <uid> route <add|remove> <other-route-params>
// network vpn create <netId> [owner_uid]
// network vpn destroy <netId>
diff --git a/NetworkController.cpp b/NetworkController.cpp
index 14c63f3..eaaee5a 100644
--- a/NetworkController.cpp
+++ b/NetworkController.cpp
@@ -72,10 +72,8 @@
// Add default network rules for the new netId.
if (isNetIdValid(newNetId)) {
Permission permission = mPermissionsController->getPermissionForNetwork(newNetId);
-
- typedef std::multimap<unsigned, std::string>::const_iterator Iterator;
- std::pair<Iterator, Iterator> range = mNetIdToInterfaces.equal_range(newNetId);
- for (Iterator iter = range.first; iter != range.second; ++iter) {
+ InterfaceRange range = interfacesForNetId(newNetId, &status);
+ for (InterfaceIterator iter = range.first; iter != range.second; ++iter) {
if (!mRouteController->addDefaultNetwork(iter->second.c_str(), permission)) {
status = false;
}
@@ -85,10 +83,8 @@
// Remove the old default network rules.
if (isNetIdValid(oldNetId)) {
Permission permission = mPermissionsController->getPermissionForNetwork(oldNetId);
-
- typedef std::multimap<unsigned, std::string>::const_iterator Iterator;
- std::pair<Iterator, Iterator> range = mNetIdToInterfaces.equal_range(oldNetId);
- for (Iterator iter = range.first; iter != range.second; ++iter) {
+ InterfaceRange range = interfacesForNetId(oldNetId, &status);
+ for (InterfaceIterator iter = range.first; iter != range.second; ++iter) {
if (!mRouteController->removeDefaultNetwork(iter->second.c_str(), permission)) {
status = false;
}
@@ -175,12 +171,10 @@
return false;
}
- typedef std::multimap<unsigned, std::string>::const_iterator Iterator;
- for (Iterator iter = mNetIdToInterfaces.begin(); iter != mNetIdToInterfaces.end(); ++iter) {
- if (iter->second == interface) {
- ALOGE("interface %s already assigned to netId %u", interface, iter->first);
- return false;
- }
+ unsigned existingNetId = netIdForInterface(interface);
+ if (existingNetId != NETID_UNSET) {
+ ALOGE("interface %s already assigned to netId %u", interface, existingNetId);
+ return false;
}
if (!mRouteController->createNetwork(netId, interface, permission)) {
@@ -203,23 +197,20 @@
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) {
+ InterfaceRange range = interfacesForNetId(netId, &status);
+ for (InterfaceIterator iter = range.first; iter != range.second; ++iter) {
if (!mRouteController->destroyNetwork(netId, iter->second.c_str(), permission)) {
status = false;
}
}
-
mPermissionsController->setPermissionForNetwork(PERMISSION_NONE, netId);
mNetIdToInterfaces.erase(netId);
if (netId == getDefaultNetwork()) {
- // Could the default network have changed from below us, after we evaluated the 'if', thus
- // making it wrong to call setDefaultNetwork() now? No, because the default can only change
- // due to another command from CommandListener, and those are serialized (i.e., they can't
- // happen in parallel with the destroyNetwork() that we are currently processing).
+ // Could the default network have changed from below us, after we evaluated the 'if', making
+ // it wrong to call setDefaultNetwork() now? No, because the default can only change due to
+ // a command from CommandListener; but commands are serialized, I.e., we are processing the
+ // destroyNetwork() command here, so a setDefaultNetwork() command can't happen in parallel.
setDefaultNetwork(NETID_UNSET);
}
@@ -245,13 +236,7 @@
continue;
}
- typedef std::multimap<unsigned, std::string>::const_iterator Iterator;
- std::pair<Iterator, Iterator> range = mNetIdToInterfaces.equal_range(netId[i]);
- if (range.first == range.second) {
- ALOGE("unknown netId %u", netId[i]);
- status = false;
- continue;
- }
+ InterfaceRange range = interfacesForNetId(netId[i], &status);
Permission oldPermission = mPermissionsController->getPermissionForNetwork(netId[i]);
if (oldPermission == newPermission) {
@@ -261,7 +246,7 @@
// TODO: ioctl(SIOCKILLADDR, ...) to kill sockets on the network that don't have
// newPermission.
- for (Iterator iter = range.first; iter != range.second; ++iter) {
+ for (InterfaceIterator iter = range.first; iter != range.second; ++iter) {
if (!mRouteController->modifyNetworkPermission(netId[i], iter->second.c_str(),
oldPermission, newPermission)) {
status = false;
@@ -274,6 +259,52 @@
return status;
}
+bool NetworkController::addRoute(unsigned netId, const char* interface, const char* destination,
+ const char* nexthop) {
+ return modifyRoute(netId, interface, destination, nexthop, true);
+}
+
+bool NetworkController::removeRoute(unsigned netId, const char* interface, const char* destination,
+ const char* nexthop) {
+ return modifyRoute(netId, interface, destination, nexthop, false);
+}
+
+unsigned NetworkController::netIdForInterface(const char* interface) {
+ for (InterfaceIterator iter = mNetIdToInterfaces.begin(); iter != mNetIdToInterfaces.end();
+ ++iter) {
+ if (iter->second == interface) {
+ return iter->first;
+ }
+ }
+ return NETID_UNSET;
+}
+
+NetworkController::InterfaceRange NetworkController::interfacesForNetId(unsigned netId,
+ bool* status) {
+ InterfaceRange range = mNetIdToInterfaces.equal_range(netId);
+ if (range.first == range.second) {
+ ALOGE("unknown netId %u", netId);
+ *status = false;
+ }
+ return range;
+}
+
+bool NetworkController::modifyRoute(unsigned netId, const char* interface, const char* destination,
+ const char* nexthop, bool add) {
+ if (!isNetIdValid(netId)) {
+ ALOGE("invalid netId %u", netId);
+ return false;
+ }
+
+ if (netIdForInterface(interface) != netId) {
+ ALOGE("netId %u has no such interface %s", netId, interface);
+ return false;
+ }
+
+ return add ? mRouteController->addRoute(interface, destination, nexthop) :
+ mRouteController->removeRoute(interface, destination, nexthop);
+}
+
NetworkController::UidEntry::UidEntry(
int start, int end, unsigned netId, bool forward_dns)
: uid_start(start),
diff --git a/NetworkController.h b/NetworkController.h
index e113bf5..d877fcc 100644
--- a/NetworkController.h
+++ b/NetworkController.h
@@ -71,7 +71,26 @@
bool setPermissionForUser(Permission permission, const std::vector<unsigned>& uid);
bool setPermissionForNetwork(Permission permission, const std::vector<unsigned>& netId);
+ // Routes are added to tables determined by the interface, so only |interface| is actually used.
+ // |netId| is given only to sanity check that the interface has the correct netId.
+ bool addRoute(unsigned netId, const char* interface, const char* destination,
+ const char* nexthop);
+ bool removeRoute(unsigned netId, const char* interface, const char* destination,
+ const char* nexthop);
+
private:
+ typedef std::multimap<unsigned, std::string>::const_iterator InterfaceIterator;
+ typedef std::pair<InterfaceIterator, InterfaceIterator> InterfaceRange;
+
+ // Returns the netId that |interface| belongs to, or NETID_UNSET if it doesn't belong to any.
+ unsigned netIdForInterface(const char* interface);
+
+ // Returns the interfaces assigned to |netId|. Sets |*status| to false if there are none.
+ InterfaceRange interfacesForNetId(unsigned netId, bool* status);
+
+ bool modifyRoute(unsigned netId, const char* interface, const char* destination,
+ const char* nexthop, bool add);
+
struct UidEntry {
int uid_start;
int uid_end;
diff --git a/RouteController.cpp b/RouteController.cpp
index a8f6700..ec82d1a 100644
--- a/RouteController.cpp
+++ b/RouteController.cpp
@@ -40,10 +40,10 @@
bool runIpRuleCommand(const char* action, uint32_t priority, uint32_t table,
uint32_t fwmark, uint32_t mask, const char* interface) {
-
char priorityString[UINT32_STRLEN];
- char tableString[UINT32_STRLEN];
snprintf(priorityString, sizeof(priorityString), "%u", priority);
+
+ char tableString[UINT32_STRLEN];
snprintf(tableString, sizeof(tableString), "%u", table);
char fwmarkString[sizeof("0x12345678/0x12345678")];
@@ -78,6 +78,32 @@
return true;
}
+bool runIpRouteCommand(const char* action, uint32_t table, const char* interface,
+ const char* destination, const char* nexthop) {
+ char tableString[UINT32_STRLEN];
+ snprintf(tableString, sizeof(tableString), "%u", table);
+
+ int argc = 0;
+ const char* argv[16];
+
+ argv[argc++] = IP_PATH;
+ argv[argc++] = "route";
+ argv[argc++] = action;
+ argv[argc++] = "table";
+ argv[argc++] = tableString;
+ if (destination) {
+ argv[argc++] = destination;
+ argv[argc++] = "dev";
+ argv[argc++] = interface;
+ if (nexthop) {
+ argv[argc++] = "via";
+ argv[argc++] = nexthop;
+ }
+ }
+
+ return android_fork_execvp(argc, const_cast<char**>(argv), NULL, false, false);
+}
+
bool modifyPerNetworkRules(unsigned netId, const char* interface, Permission permission, bool add,
bool modifyIptables) {
uint32_t table = getRouteTableForInterface(interface);
@@ -152,11 +178,16 @@
uint32_t mask = getFwmarkMask(FWMARK_USE_NET_ID, !FWMARK_USE_EXPLICIT, !FWMARK_USE_PROTECT,
permission);
- if (!runIpRuleCommand(action, RULE_PRIORITY_DEFAULT_NETWORK, table, fwmark, mask, NULL)) {
+ return runIpRuleCommand(action, RULE_PRIORITY_DEFAULT_NETWORK, table, fwmark, mask, NULL);
+}
+
+bool modifyRoute(const char* interface, const char* destination, const char* nexthop, bool add) {
+ uint32_t table = getRouteTableForInterface(interface);
+ if (!table) {
return false;
}
- return true;
+ return runIpRouteCommand(add ? ADD : DEL, table, interface, destination, nexthop);
}
} // namespace
@@ -184,3 +215,13 @@
bool RouteController::removeDefaultNetwork(const char* interface, Permission permission) {
return modifyDefaultNetworkRules(interface, permission, DEL);
}
+
+bool RouteController::addRoute(const char* interface, const char* destination,
+ const char* nexthop) {
+ return modifyRoute(interface, destination, nexthop, true);
+}
+
+bool RouteController::removeRoute(const char* interface, const char* destination,
+ const char* nexthop) {
+ return modifyRoute(interface, destination, nexthop, false);
+}
diff --git a/RouteController.h b/RouteController.h
index 1db1da3..560d536 100644
--- a/RouteController.h
+++ b/RouteController.h
@@ -30,6 +30,9 @@
static bool addDefaultNetwork(const char* interface, Permission permission);
static bool removeDefaultNetwork(const char* interface, Permission permission);
+
+ static bool addRoute(const char* interface, const char* destination, const char* nexthop);
+ static bool removeRoute(const char* interface, const char* destination, const char* nexthop);
};
#endif // SYSTEM_NETD_ROUTE_CONTROLLER_H