Fix WiFi-Direct and Tethering.
A LocalNetwork object now always exists in the NetworkController, with a fixed
NetId that's guaranteed not to collide with NetIds created by the framework.
When routes are added on an interface tracked by the LocalNetwork, they are
added to a fixed "local_network" table.
When NAT is enabled, we add a special "iif -> oif" tethering rule.
Bug: 15413694
Bug: 15413741
Change-Id: I36effc438d5ac193a77174493bf196cb68a5b97a
diff --git a/server/RouteController.cpp b/server/RouteController.cpp
index 8ad10d7..ec51613 100644
--- a/server/RouteController.cpp
+++ b/server/RouteController.cpp
@@ -43,7 +43,7 @@
const uint32_t RULE_PRIORITY_LEGACY_SYSTEM = 15000;
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_TETHERING = 18000;
const uint32_t RULE_PRIORITY_IMPLICIT_NETWORK = 19000;
// const uint32_t RULE_PRIORITY_BYPASSABLE_VPN = 20000;
// const uint32_t RULE_PRIORITY_VPN_FALLTHROUGH = 21000;
@@ -51,9 +51,11 @@
const uint32_t RULE_PRIORITY_DIRECTLY_CONNECTED = 23000;
const uint32_t RULE_PRIORITY_UNREACHABLE = 24000;
+const uint32_t ROUTE_TABLE_LOCAL_NETWORK = 97;
const uint32_t ROUTE_TABLE_LEGACY_NETWORK = 98;
const uint32_t ROUTE_TABLE_LEGACY_SYSTEM = 99;
+const char* const ROUTE_TABLE_NAME_LOCAL_NETWORK = "local_network";
const char* const ROUTE_TABLE_NAME_LEGACY_NETWORK = "legacy_network";
const char* const ROUTE_TABLE_NAME_LEGACY_SYSTEM = "legacy_system";
@@ -80,6 +82,7 @@
const char* const IP_VERSIONS[] = {"-4", "-6"};
const uid_t UID_ROOT = 0;
+const char* const IIF_NONE = NULL;
const char* const OIF_NONE = NULL;
const bool ACTION_ADD = true;
const bool ACTION_DEL = false;
@@ -147,6 +150,7 @@
addTableName(RT_TABLE_LOCAL, ROUTE_TABLE_NAME_LOCAL, &contents);
addTableName(RT_TABLE_MAIN, ROUTE_TABLE_NAME_MAIN, &contents);
+ addTableName(ROUTE_TABLE_LOCAL_NETWORK, ROUTE_TABLE_NAME_LOCAL_NETWORK, &contents);
addTableName(ROUTE_TABLE_LEGACY_NETWORK, ROUTE_TABLE_NAME_LEGACY_NETWORK, &contents);
addTableName(ROUTE_TABLE_LEGACY_SYSTEM, ROUTE_TABLE_NAME_LEGACY_SYSTEM, &contents);
@@ -219,36 +223,53 @@
return ret;
}
+// Returns 0 on success or negative errno on failure.
+int padInterfaceName(const char* input, char* name, size_t* length, uint16_t* padding) {
+ if (!input) {
+ *length = 0;
+ *padding = 0;
+ return 0;
+ }
+ *length = strlcpy(name, input, IFNAMSIZ) + 1;
+ if (*length > IFNAMSIZ) {
+ ALOGE("interface name too long (%zu > %u)", *length, IFNAMSIZ);
+ return -ENAMETOOLONG;
+ }
+ *padding = RTA_SPACE(*length) - RTA_LENGTH(*length);
+ return 0;
+}
+
// Adds or removes a routing rule for IPv4 and IPv6.
//
// + If |table| is non-zero, the rule points at the specified routing table. Otherwise, the rule
// returns ENETUNREACH.
// + If |mask| is non-zero, the rule matches the specified fwmark and mask. Otherwise, |fwmark| is
// ignored.
-// + If |interface| is non-NULL, the rule matches the specified outgoing interface.
+// + If |iif| is non-NULL, the rule matches the specified incoming interface.
+// + If |oif| is non-NULL, the rule matches the specified outgoing interface.
+// + If |uidStart| and |uidEnd| are not INVALID_UID, the rule matches packets from UIDs in that
+// range (inclusive). Otherwise, the rule matches packets from all UIDs.
//
// Returns 0 on success or negative errno on failure.
WARN_UNUSED_RESULT int modifyIpRule(uint16_t action, uint32_t priority, uint32_t table,
- uint32_t fwmark, uint32_t mask, const char* interface,
- uid_t uidStart, uid_t uidEnd) {
+ uint32_t fwmark, uint32_t mask, const char* iif,
+ const char* oif, uid_t uidStart, uid_t uidEnd) {
// Ensure that if you set a bit in the fwmark, it's not being ignored by the mask.
if (fwmark & ~mask) {
ALOGE("mask 0x%x does not select all the bits set in fwmark 0x%x", mask, fwmark);
return -ERANGE;
}
- // The interface name must include exactly one terminating NULL and be properly padded, or older
+ // Interface names must include exactly one terminating NULL and be properly padded, or older
// kernels will refuse to delete rules.
- uint16_t paddingLength = 0;
- size_t interfaceLength = 0;
- char oifname[IFNAMSIZ];
- if (interface != OIF_NONE) {
- interfaceLength = strlcpy(oifname, interface, IFNAMSIZ) + 1;
- if (interfaceLength > IFNAMSIZ) {
- ALOGE("interface name too long (%zu > %u)", interfaceLength, IFNAMSIZ);
- return -ENAMETOOLONG;
- }
- paddingLength = RTA_SPACE(interfaceLength) - RTA_LENGTH(interfaceLength);
+ char iifName[IFNAMSIZ], oifName[IFNAMSIZ];
+ size_t iifLength, oifLength;
+ uint16_t iifPadding, oifPadding;
+ if (int ret = padInterfaceName(iif, iifName, &iifLength, &iifPadding)) {
+ return ret;
+ }
+ if (int ret = padInterfaceName(oif, oifName, &oifLength, &oifPadding)) {
+ return ret;
}
// Either both start and end UID must be specified, or neither.
@@ -264,7 +285,8 @@
FR_ACT_UNREACHABLE),
};
- rtattr fraOifname = { U16_RTA_LENGTH(interfaceLength), FRA_OIFNAME };
+ rtattr fraIifName = { U16_RTA_LENGTH(iifLength), FRA_IIFNAME };
+ rtattr fraOifName = { U16_RTA_LENGTH(oifLength), FRA_OIFNAME };
iovec iov[] = {
{ NULL, 0 },
@@ -281,9 +303,12 @@
{ &uidStart, isUidRule ? sizeof(uidStart) : 0 },
{ &FRATTR_UID_END, isUidRule ? sizeof(FRATTR_UID_END) : 0 },
{ &uidEnd, isUidRule ? sizeof(uidEnd) : 0 },
- { &fraOifname, interface != OIF_NONE ? sizeof(fraOifname) : 0 },
- { oifname, interfaceLength },
- { PADDING_BUFFER, paddingLength },
+ { &fraIifName, iif != IIF_NONE ? sizeof(fraIifName) : 0 },
+ { iifName, iifLength },
+ { PADDING_BUFFER, iifPadding },
+ { &fraOifName, oif != OIF_NONE ? sizeof(fraOifName) : 0 },
+ { oifName, oifLength },
+ { PADDING_BUFFER, oifPadding },
};
uint16_t flags = (action == RTM_NEWRULE) ? NETLINK_CREATE_REQUEST_FLAGS : NETLINK_REQUEST_FLAGS;
@@ -297,6 +322,12 @@
return 0;
}
+WARN_UNUSED_RESULT int modifyIpRule(uint16_t action, uint32_t priority, uint32_t table,
+ uint32_t fwmark, uint32_t mask) {
+ return modifyIpRule(action, priority, table, fwmark, mask, IIF_NONE, OIF_NONE, INVALID_UID,
+ INVALID_UID);
+}
+
// Adds or deletes an IPv4 or IPv6 route.
// Returns 0 on success or negative errno on failure.
WARN_UNUSED_RESULT int modifyIpRoute(uint16_t action, uint32_t table, const char* interface,
@@ -370,7 +401,7 @@
}
// Add rules to allow legacy routes added through the requestRouteToHost() API.
-WARN_UNUSED_RESULT int AddLegacyRouteRules() {
+WARN_UNUSED_RESULT int addLegacyRouteRules() {
Fwmark fwmark;
Fwmark mask;
@@ -379,13 +410,11 @@
// Rules to allow legacy routes to override the default network.
if (int ret = modifyIpRule(RTM_NEWRULE, RULE_PRIORITY_LEGACY_SYSTEM, ROUTE_TABLE_LEGACY_SYSTEM,
- fwmark.intValue, mask.intValue, OIF_NONE, INVALID_UID,
- INVALID_UID)) {
+ fwmark.intValue, mask.intValue)) {
return ret;
}
if (int ret = modifyIpRule(RTM_NEWRULE, RULE_PRIORITY_LEGACY_NETWORK,
- ROUTE_TABLE_LEGACY_NETWORK, fwmark.intValue,
- mask.intValue, OIF_NONE, INVALID_UID, INVALID_UID)) {
+ ROUTE_TABLE_LEGACY_NETWORK, fwmark.intValue, mask.intValue)) {
return ret;
}
@@ -394,7 +423,7 @@
// A rule to allow legacy routes from system apps to override VPNs.
return modifyIpRule(RTM_NEWRULE, RULE_PRIORITY_VPN_OVERRIDE_SYSTEM, ROUTE_TABLE_LEGACY_SYSTEM,
- fwmark.intValue, mask.intValue, OIF_NONE, INVALID_UID, INVALID_UID);
+ fwmark.intValue, mask.intValue);
}
// Add a new rule to look up the 'main' table, with the same selectors as the "default network"
@@ -402,7 +431,7 @@
// route, the rule we're adding will never be used for normal routing lookups. However, the kernel
// may fall-through to it to find directly-connected routes when it validates that a nexthop (in a
// route being added) is reachable.
-WARN_UNUSED_RESULT int AddDirectlyConnectedRule() {
+WARN_UNUSED_RESULT int addDirectlyConnectedRule() {
Fwmark fwmark;
Fwmark mask;
@@ -410,14 +439,26 @@
mask.netId = FWMARK_NET_ID_MASK;
return modifyIpRule(RTM_NEWRULE, RULE_PRIORITY_DIRECTLY_CONNECTED, RT_TABLE_MAIN,
- fwmark.intValue, mask.intValue, OIF_NONE, UID_ROOT, UID_ROOT);
+ fwmark.intValue, mask.intValue, IIF_NONE, OIF_NONE, UID_ROOT, UID_ROOT);
}
// Add a rule to preempt the pre-defined "from all lookup main" rule. Packets that reach this rule
// will be null-routed, and won't fall-through to the main table.
-WARN_UNUSED_RESULT int AddUnreachableRule() {
+WARN_UNUSED_RESULT int addUnreachableRule() {
return modifyIpRule(RTM_NEWRULE, RULE_PRIORITY_UNREACHABLE, RT_TABLE_UNSPEC, MARK_UNSET,
- MARK_UNSET, OIF_NONE, INVALID_UID, INVALID_UID);
+ MARK_UNSET);
+}
+
+// A rule to lookup the local network before looking up the default network.
+WARN_UNUSED_RESULT int addImplicitLocalNetworkRule() {
+ Fwmark fwmark;
+ Fwmark mask;
+
+ fwmark.explicitlySelected = false;
+ mask.explicitlySelected = true;
+
+ return modifyIpRule(RTM_NEWRULE, RULE_PRIORITY_LOCAL_NETWORK, ROUTE_TABLE_LOCAL_NETWORK,
+ fwmark.intValue, mask.intValue);
}
// An iptables rule to mark incoming packets on a network with the netId of the network.
@@ -471,7 +512,7 @@
mask.permission = permission;
return modifyIpRule(add ? RTM_NEWRULE : RTM_DELRULE, RULE_PRIORITY_EXPLICIT_NETWORK, table,
- fwmark.intValue, mask.intValue, OIF_NONE, uidStart, uidEnd);
+ fwmark.intValue, mask.intValue, IIF_NONE, OIF_NONE, uidStart, uidEnd);
}
// A rule to route traffic based on a chosen outgoing interface.
@@ -488,7 +529,7 @@
mask.permission = permission;
return modifyIpRule(add ? RTM_NEWRULE : RTM_DELRULE, RULE_PRIORITY_OUTPUT_INTERFACE, table,
- fwmark.intValue, mask.intValue, interface, uidStart, uidEnd);
+ fwmark.intValue, mask.intValue, IIF_NONE, interface, uidStart, uidEnd);
}
// A rule to route traffic based on the chosen network.
@@ -511,7 +552,7 @@
mask.permission = permission;
return modifyIpRule(add ? RTM_NEWRULE : RTM_DELRULE, RULE_PRIORITY_IMPLICIT_NETWORK, table,
- fwmark.intValue, mask.intValue, OIF_NONE, INVALID_UID, INVALID_UID);
+ fwmark.intValue, mask.intValue);
}
// A rule to route all traffic from a given set of UIDs to go over the VPN.
@@ -528,7 +569,7 @@
mask.protectedFromVpn = true;
return modifyIpRule(add ? RTM_NEWRULE : RTM_DELRULE, RULE_PRIORITY_SECURE_VPN, table,
- fwmark.intValue, mask.intValue, OIF_NONE, uidStart, uidEnd);
+ 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
@@ -547,39 +588,26 @@
mask.permission = PERMISSION_SYSTEM;
return modifyIpRule(add ? RTM_NEWRULE : RTM_DELRULE, RULE_PRIORITY_SECURE_VPN, table,
- fwmark.intValue, mask.intValue, OIF_NONE, INVALID_UID, INVALID_UID);
+ fwmark.intValue, mask.intValue);
}
-// A rule to allow local network routes to override the default network.
-WARN_UNUSED_RESULT int modifyLocalOverrideRule(uint32_t table, bool add) {
- Fwmark fwmark;
- Fwmark mask;
-
- fwmark.explicitlySelected = false;
- mask.explicitlySelected = true;
-
- return modifyIpRule(add ? RTM_NEWRULE : RTM_DELRULE, RULE_PRIORITY_LOCAL_NETWORK, table,
- fwmark.intValue, mask.intValue, OIF_NONE, INVALID_UID, INVALID_UID);
-}
-
-WARN_UNUSED_RESULT int modifyLocalNetwork(unsigned netId, const char* interface, bool add) {
- uint32_t table = getRouteTableForInterface(interface);
+WARN_UNUSED_RESULT int modifyTetheringRule(uint16_t action, const char* inputInterface,
+ const char* outputInterface) {
+ uint32_t table = getRouteTableForInterface(outputInterface);
if (table == RT_TABLE_UNSPEC) {
return -ESRCH;
}
+ return modifyIpRule(action, RULE_PRIORITY_TETHERING, table, MARK_UNSET, MARK_UNSET,
+ inputInterface, outputInterface, INVALID_UID, INVALID_UID);
+}
+
+WARN_UNUSED_RESULT int modifyLocalNetwork(unsigned netId, const char* interface, bool add) {
if (int ret = modifyIncomingPacketMark(netId, interface, PERMISSION_NONE, add)) {
return ret;
}
- if (int ret = modifyExplicitNetworkRule(netId, table, PERMISSION_NONE, INVALID_UID, INVALID_UID,
- add)) {
- return ret;
- }
- if (int ret = modifyOutputInterfaceRule(interface, table, PERMISSION_NONE, INVALID_UID,
- INVALID_UID, add)) {
- return ret;
- }
- return modifyLocalOverrideRule(table, add);
+ return modifyOutputInterfaceRule(interface, ROUTE_TABLE_LOCAL_NETWORK, PERMISSION_NONE,
+ INVALID_UID, INVALID_UID, add);
}
WARN_UNUSED_RESULT int modifyPhysicalNetwork(unsigned netId, const char* interface,
@@ -656,7 +684,7 @@
mask.permission = permission;
return modifyIpRule(action, RULE_PRIORITY_DEFAULT_NETWORK, table, fwmark.intValue,
- mask.intValue, OIF_NONE, INVALID_UID, INVALID_UID);
+ mask.intValue);
}
// Adds or removes an IPv4 or IPv6 route to the specified table and, if it's a directly-connected
@@ -673,6 +701,10 @@
}
break;
}
+ case RouteController::LOCAL_NETWORK: {
+ table = ROUTE_TABLE_LOCAL_NETWORK;
+ break;
+ }
case RouteController::LEGACY_NETWORK: {
table = ROUTE_TABLE_LEGACY_NETWORK;
break;
@@ -687,7 +719,8 @@
// We allow apps to call requestRouteToHost() multiple times with the same route, so ignore
// EEXIST failures when adding routes to legacy tables.
if (ret && !(action == RTM_NEWROUTE && ret == -EEXIST &&
- tableType != RouteController::INTERFACE)) {
+ (tableType == RouteController::LEGACY_NETWORK ||
+ tableType == RouteController::LEGACY_SYSTEM))) {
return ret;
}
@@ -737,40 +770,37 @@
} // namespace
-int RouteController::Init() {
- if (int ret = AddDirectlyConnectedRule()) {
+int RouteController::Init(unsigned localNetId) {
+ if (int ret = addDirectlyConnectedRule()) {
return ret;
}
- if (int ret = AddLegacyRouteRules()) {
+ if (int ret = addLegacyRouteRules()) {
return ret;
}
// TODO: Enable once we are sure everything works.
if (false) {
- if (int ret = AddUnreachableRule()) {
+ if (int ret = addUnreachableRule()) {
return ret;
}
}
+ if (int ret = addImplicitLocalNetworkRule()) {
+ return ret;
+ }
+ // Add a rule to lookup the local network it has been explicitly selected.
+ if (int ret = modifyExplicitNetworkRule(localNetId, ROUTE_TABLE_LOCAL_NETWORK, PERMISSION_NONE,
+ INVALID_UID, INVALID_UID, ACTION_ADD)) {
+ return ret;
+ }
updateTableNamesFile();
return 0;
}
int RouteController::addInterfaceToLocalNetwork(unsigned netId, const char* interface) {
- if (int ret = modifyLocalNetwork(netId, interface, ACTION_ADD)) {
- return ret;
- }
- updateTableNamesFile();
- return 0;
+ return modifyLocalNetwork(netId, interface, ACTION_ADD);
}
int RouteController::removeInterfaceFromLocalNetwork(unsigned netId, const char* interface) {
- if (int ret = modifyLocalNetwork(netId, interface, ACTION_DEL)) {
- return ret;
- }
- if (int ret = flushRoutes(interface)) {
- return ret;
- }
- updateTableNamesFile();
- return 0;
+ return modifyLocalNetwork(netId, interface, ACTION_DEL);
}
int RouteController::addInterfaceToPhysicalNetwork(unsigned netId, const char* interface,
@@ -857,3 +887,11 @@
const char* nexthop, TableType tableType) {
return modifyRoute(RTM_DELROUTE, interface, destination, nexthop, tableType);
}
+
+int RouteController::enableTethering(const char* inputInterface, const char* outputInterface) {
+ return modifyTetheringRule(RTM_NEWRULE, inputInterface, outputInterface);
+}
+
+int RouteController::disableTethering(const char* inputInterface, const char* outputInterface) {
+ return modifyTetheringRule(RTM_DELRULE, inputInterface, outputInterface);
+}