Support legacy routes added by apps via ensureRouteToHost().
This adds the routes to two fixed tables:
+ LEGACY, which has higher priority than other non-explicit lookup tables
(per-network and default network).
+ PRIVILEGED_LEGACY, available only to system apps and has higher priority than
VPNs (system apps are those with the CONNECTIVITY_INTERNAL permission).
This will be changed to per-UID tables once the kernel supports UID-based
routing, so that these legacy routes are scoped to each app and not global.
Also, fix a TODO: The framework (as of http://ag/471599) will not set the
gateway argument if it's actually a direct-connected route.
Change-Id: I0ee1ca89fdc859d75a89021ca8c1902811b1e4a9
diff --git a/server/CommandListener.cpp b/server/CommandListener.cpp
index fbf5670..72ff411 100644
--- a/server/CommandListener.cpp
+++ b/server/CommandListener.cpp
@@ -1619,8 +1619,8 @@
return success(client);
}
- // 0 1 2 3
- // network addiface <netId> <interface>
+ // 0 1 2 3
+ // network addiface <netId> <interface>
// network removeiface <netId> <interface>
if (!strcmp(argv[1], "addiface") || !strcmp(argv[1], "removeiface")) {
if (argc != 4) {
@@ -1705,40 +1705,48 @@
return success(client);
}
- // 0 1 2 3 4 5 6
- // network route add <netId> <interface> <destination> [nexthop]
- // network route remove <netId> <interface> <destination> [nexthop]
+ // 0 1 2 3 4 5 6 7 8
+ // network route [legacy <uid>] add <netId> <interface> <destination> [nexthop]
+ // network route [legacy <uid>] remove <netId> <interface> <destination> [nexthop]
if (!strcmp(argv[1], "route")) {
- if (argc < 6 || argc > 7) {
+ if (argc < 6 || argc > 9) {
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);
- const char* interface = argv[4];
- const char* destination = argv[5];
- const char* nexthop = NULL;
- // If the nexthop (gateway) is equal to INADDR_ANY or in6addr_any, the route is a directly
- // connected route, and we should not set nexthop.
- // TODO: This doesn't catch all the ways of representing the "any" address (e.g.: "0/0",
- // "::0", etc). Fix the callers to not pass us a nexthop in the first place in such cases.
- if (argc == 7 && strcmp(argv[6], "0.0.0.0") && strcmp(argv[6], "::")) {
- nexthop = argv[6];
+
+ int nextArg = 2;
+ bool legacy = false;
+ unsigned uid = 0;
+ if (!strcmp(argv[nextArg], "legacy")) {
+ ++nextArg;
+ legacy = true;
+ uid = strtoul(argv[nextArg++], NULL, 0);
}
- 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 {
+
+ bool add = false;
+ if (!strcmp(argv[nextArg], "add")) {
+ add = true;
+ } else if (strcmp(argv[nextArg], "remove")) {
return syntaxError(client, "Unknown argument");
}
+ ++nextArg;
+
+ // strtoul() returns 0 on errors, which is fine because 0 is an invalid netId.
+ unsigned netId = strtoul(argv[nextArg++], NULL, 0);
+ const char* interface = argv[nextArg++];
+ const char* destination = argv[nextArg++];
+ const char* nexthop = argc > nextArg ? argv[nextArg] : NULL;
+
+ if (add) {
+ if (!sNetCtrl->addRoute(netId, interface, destination, nexthop, legacy, uid)) {
+ return operationError(client, "addRoute() failed");
+ }
+ } else if (!sNetCtrl->removeRoute(netId, interface, destination, nexthop, legacy, uid)) {
+ return operationError(client, "removeRoute() failed");
+ }
+
return success(client);
}
- // network legacy <uid> route <add|remove> <other-route-params>
// network vpn create <netId> [owner_uid]
// network vpn destroy <netId>
// network <bind|unbind> <netId> <uid1> .. <uidN>
diff --git a/server/NetworkController.cpp b/server/NetworkController.cpp
index 9504dc0..bf6383e 100644
--- a/server/NetworkController.cpp
+++ b/server/NetworkController.cpp
@@ -325,13 +325,13 @@
}
bool NetworkController::addRoute(unsigned netId, const char* interface, const char* destination,
- const char* nexthop) {
- return modifyRoute(netId, interface, destination, nexthop, true);
+ const char* nexthop, bool legacy, unsigned uid) {
+ return modifyRoute(netId, interface, destination, nexthop, true, legacy, uid);
}
bool NetworkController::removeRoute(unsigned netId, const char* interface, const char* destination,
- const char* nexthop) {
- return modifyRoute(netId, interface, destination, nexthop, false);
+ const char* nexthop, bool legacy, unsigned uid) {
+ return modifyRoute(netId, interface, destination, nexthop, false, legacy, uid);
}
bool NetworkController::isValidNetwork(unsigned netId) const {
@@ -344,7 +344,7 @@
}
bool NetworkController::modifyRoute(unsigned netId, const char* interface, const char* destination,
- const char* nexthop, bool add) {
+ const char* nexthop, bool add, bool legacy, unsigned uid) {
if (!isValidNetwork(netId)) {
ALOGE("invalid netId %u", netId);
return false;
@@ -355,8 +355,19 @@
return false;
}
- return add ? mRouteController->addRoute(interface, destination, nexthop) :
- mRouteController->removeRoute(interface, destination, nexthop);
+ RouteController::TableType tableType;
+ if (legacy) {
+ if (mPermissionsController->getPermissionForUser(uid) & PERMISSION_CONNECTIVITY_INTERNAL) {
+ tableType = RouteController::PRIVILEGED_LEGACY;
+ } else {
+ tableType = RouteController::LEGACY;
+ }
+ } else {
+ tableType = RouteController::INTERFACE;
+ }
+
+ return add ? mRouteController->addRoute(interface, destination, nexthop, tableType, uid) :
+ mRouteController->removeRoute(interface, destination, nexthop, tableType, uid);
}
NetworkController::UidEntry::UidEntry(int start, int end, unsigned netId, bool forward_dns)
diff --git a/server/NetworkController.h b/server/NetworkController.h
index 27785d3..30c9142 100644
--- a/server/NetworkController.h
+++ b/server/NetworkController.h
@@ -68,9 +68,9 @@
// 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);
+ const char* nexthop, bool legacy, unsigned uid);
bool removeRoute(unsigned netId, const char* interface, const char* destination,
- const char* nexthop);
+ const char* nexthop, bool legacy, unsigned uid);
bool isValidNetwork(unsigned netId) const;
@@ -80,7 +80,7 @@
typedef std::pair<InterfaceIterator, InterfaceIterator> InterfaceRange;
bool modifyRoute(unsigned netId, const char* interface, const char* destination,
- const char* nexthop, bool add);
+ const char* nexthop, bool add, bool legacy, unsigned uid);
struct UidEntry {
int uid_start;
diff --git a/server/RouteController.cpp b/server/RouteController.cpp
index 7f9ce39..36773d7 100644
--- a/server/RouteController.cpp
+++ b/server/RouteController.cpp
@@ -25,13 +25,19 @@
namespace {
+const uint32_t RULE_PRIORITY_PRIVILEGED_LEGACY = 11000;
const uint32_t RULE_PRIORITY_PER_NETWORK_EXPLICIT = 13000;
const uint32_t RULE_PRIORITY_PER_NETWORK_INTERFACE = 14000;
+const uint32_t RULE_PRIORITY_LEGACY = 16000;
const uint32_t RULE_PRIORITY_PER_NETWORK_NORMAL = 17000;
const uint32_t RULE_PRIORITY_DEFAULT_NETWORK = 19000;
const uint32_t RULE_PRIORITY_MAIN = 20000;
const uint32_t RULE_PRIORITY_UNREACHABLE = 21000;
+// TODO: These should be turned into per-UID tables once the kernel supports UID-based routing.
+const int ROUTE_TABLE_PRIVILEGED_LEGACY = RouteController::ROUTE_TABLE_OFFSET_FROM_INDEX - 901;
+const int ROUTE_TABLE_LEGACY = RouteController::ROUTE_TABLE_OFFSET_FROM_INDEX - 902;
+
uint32_t getRouteTableForInterface(const char* interface) {
uint32_t index = if_nametoindex(interface);
return index ? index + RouteController::ROUTE_TABLE_OFFSET_FROM_INDEX : 0;
@@ -202,8 +208,24 @@
}
bool modifyRoute(const char* interface, const char* destination, const char* nexthop,
- const char* action) {
- uint32_t table = getRouteTableForInterface(interface);
+ const char* action, RouteController::TableType tableType, unsigned /* uid */) {
+ uint32_t table = 0;
+ switch (tableType) {
+ case RouteController::INTERFACE: {
+ table = getRouteTableForInterface(interface);
+ break;
+ }
+ case RouteController::LEGACY: {
+ // TODO: Use the UID to assign a unique table per UID instead of this fixed table.
+ table = ROUTE_TABLE_LEGACY;
+ break;
+ }
+ case RouteController::PRIVILEGED_LEGACY: {
+ // TODO: Use the UID to assign a unique table per UID instead of this fixed table.
+ table = ROUTE_TABLE_PRIVILEGED_LEGACY;
+ break;
+ }
+ }
if (!table) {
return false;
}
@@ -252,6 +274,25 @@
runIpRuleCommand(ADD, RULE_PRIORITY_MAIN, RT_TABLE_MAIN, fwmark.intValue, mask.intValue, NULL);
+ // Add rules to allow lookup of legacy routes.
+ //
+ // TODO: Remove these once the kernel supports UID-based routing. Instead, add them on demand
+ // when routes are added.
+ fwmark.netId = 0;
+ mask.netId = 0;
+
+ fwmark.explicitlySelected = false;
+ mask.explicitlySelected = true;
+
+ runIpRuleCommand(ADD, RULE_PRIORITY_LEGACY, ROUTE_TABLE_LEGACY, fwmark.intValue, mask.intValue,
+ NULL);
+
+ fwmark.permission = PERMISSION_CONNECTIVITY_INTERNAL;
+ mask.permission = PERMISSION_CONNECTIVITY_INTERNAL;
+
+ runIpRuleCommand(ADD, RULE_PRIORITY_PRIVILEGED_LEGACY, ROUTE_TABLE_PRIVILEGED_LEGACY,
+ fwmark.intValue, mask.intValue, NULL);
+
// TODO: Uncomment once we are sure everything works.
#if 0
// Add a rule to preempt the pre-defined "from all lookup main" rule. This ensures that packets
@@ -287,11 +328,11 @@
}
bool RouteController::addRoute(const char* interface, const char* destination,
- const char* nexthop) {
- return modifyRoute(interface, destination, nexthop, ADD);
+ const char* nexthop, TableType tableType, unsigned uid) {
+ return modifyRoute(interface, destination, nexthop, ADD, tableType, uid);
}
bool RouteController::removeRoute(const char* interface, const char* destination,
- const char* nexthop) {
- return modifyRoute(interface, destination, nexthop, DEL);
+ const char* nexthop, TableType tableType, unsigned uid) {
+ return modifyRoute(interface, destination, nexthop, DEL, tableType, uid);
}
diff --git a/server/RouteController.h b/server/RouteController.h
index 6d66ed7..7292579 100644
--- a/server/RouteController.h
+++ b/server/RouteController.h
@@ -21,6 +21,13 @@
class RouteController {
public:
+ // How the routing table number is determined for route modification requests.
+ enum TableType {
+ INTERFACE, // Compute the table number based on the interface index.
+ LEGACY, // Based on the UID; such tables can override the default network routes.
+ PRIVILEGED_LEGACY, // Based on the UID; such tables can bypass VPNs.
+ };
+
static const int ROUTE_TABLE_OFFSET_FROM_INDEX = 1000;
static void Init();
@@ -34,8 +41,10 @@
static bool addToDefaultNetwork(const char* interface, Permission permission);
static bool removeFromDefaultNetwork(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);
+ static bool addRoute(const char* interface, const char* destination, const char* nexthop,
+ TableType tableType, unsigned uid);
+ static bool removeRoute(const char* interface, const char* destination, const char* nexthop,
+ TableType tableType, unsigned uid);
};
#endif // NETD_SERVER_ROUTE_CONTROLLER_H