Add local rules for local networks
When a network is a local network, two new IP rules
should be installed :
• A rule that keeps the traffic to directly connected IPs
working when the catch-all local rule is removed. This
rule has PRIORITY_LOCAL_NETWORK and matches on the
explicit bit being off, because the explicit rule will
be installed separately by ConnectivityService in a
followup patch at priority EXPLICIT_NETWORK.
20000: from all fwmark 0x0/0x10000 lookup xxxx
• A rule that matches on LOCAL_NET_ID explicitly pointing
to the same table to keep traffic to netId 99 working.
This is necessary for the continued working of dnsmasq,
and might be necessary to keep traffic sent to 99 by
other means on OEM devices.
16000: from all fwmark 0x10063/0x1ffff iif lo lookup xxxx
Test: new tests in this patch
Change-Id: If8729fc6f3716a580c936584b851bc38000b5de5
diff --git a/server/PhysicalNetwork.cpp b/server/PhysicalNetwork.cpp
index 923412a..161fa2a 100644
--- a/server/PhysicalNetwork.cpp
+++ b/server/PhysicalNetwork.cpp
@@ -61,12 +61,7 @@
mDelegate(delegate),
mPermission(PERMISSION_NONE),
mIsDefault(false),
- mIsLocalNetwork(local) {
- // TODO : remove this log, it's only present to avoid -Wunused-private-field from blocking
- // compilation
- ALOGI("Created physical network instance netId=%d local=%s", netId,
- mIsLocalNetwork ? "true" : "false");
-}
+ mIsLocalNetwork(local) {}
PhysicalNetwork::~PhysicalNetwork() {}
@@ -120,8 +115,8 @@
destroySocketsLackingPermission(permission);
for (const std::string& interface : mInterfaces) {
- if (int ret = RouteController::modifyPhysicalNetworkPermission(mNetId, interface.c_str(),
- mPermission, permission)) {
+ if (int ret = RouteController::modifyPhysicalNetworkPermission(
+ mNetId, interface.c_str(), mPermission, permission, mIsLocalNetwork)) {
ALOGE("failed to change permission on interface %s of netId %u from %x to %x",
interface.c_str(), mNetId, mPermission, permission);
return ret;
@@ -178,8 +173,8 @@
}
for (const std::string& interface : mInterfaces) {
- int ret = RouteController::addUsersToPhysicalNetwork(mNetId, interface.c_str(),
- {{subPriority, uidRanges}});
+ int ret = RouteController::addUsersToPhysicalNetwork(
+ mNetId, interface.c_str(), {{subPriority, uidRanges}}, mIsLocalNetwork);
if (ret) {
ALOGE("failed to add users on interface %s of netId %u", interface.c_str(), mNetId);
return ret;
@@ -193,8 +188,8 @@
if (!isValidSubPriority(subPriority)) return -EINVAL;
for (const std::string& interface : mInterfaces) {
- int ret = RouteController::removeUsersFromPhysicalNetwork(mNetId, interface.c_str(),
- {{subPriority, uidRanges}});
+ int ret = RouteController::removeUsersFromPhysicalNetwork(
+ mNetId, interface.c_str(), {{subPriority, uidRanges}}, mIsLocalNetwork);
if (ret) {
ALOGE("failed to remove users on interface %s of netId %u", interface.c_str(), mNetId);
return ret;
@@ -208,8 +203,8 @@
if (hasInterface(interface)) {
return 0;
}
- if (int ret = RouteController::addInterfaceToPhysicalNetwork(mNetId, interface.c_str(),
- mPermission, mUidRangeMap)) {
+ if (int ret = RouteController::addInterfaceToPhysicalNetwork(
+ mNetId, interface.c_str(), mPermission, mUidRangeMap, mIsLocalNetwork)) {
ALOGE("failed to add interface %s to netId %u", interface.c_str(), mNetId);
return ret;
}
@@ -235,8 +230,8 @@
// done last as further requests to the RouteController regarding this interface will fail
// to find the interface index in the cache in cases where the interface is already gone
// (e.g. bt-pan).
- if (int ret = RouteController::removeInterfaceFromPhysicalNetwork(mNetId, interface.c_str(),
- mPermission, mUidRangeMap)) {
+ if (int ret = RouteController::removeInterfaceFromPhysicalNetwork(
+ mNetId, interface.c_str(), mPermission, mUidRangeMap, mIsLocalNetwork)) {
ALOGE("failed to remove interface %s from netId %u", interface.c_str(), mNetId);
return ret;
}
diff --git a/server/RouteController.cpp b/server/RouteController.cpp
index 86b23b6..97ca84e 100644
--- a/server/RouteController.cpp
+++ b/server/RouteController.cpp
@@ -595,6 +595,34 @@
mask.intValue, IIF_LOOPBACK, OIF_NONE, uidStart, uidEnd);
}
+// A rule to route traffic based on an local network.
+//
+// Supports apps that send traffic to local IPs without binding to a particular network.
+//
+[[nodiscard]] static int modifyLocalNetworkRule(uint32_t table, bool add) {
+ Fwmark fwmark;
+ Fwmark mask;
+
+ fwmark.explicitlySelected = false;
+ mask.explicitlySelected = true;
+
+ if (const int ret = modifyIpRule(add ? RTM_NEWRULE : RTM_DELRULE, RULE_PRIORITY_LOCAL_NETWORK,
+ table, fwmark.intValue, mask.intValue, IIF_NONE, OIF_NONE,
+ INVALID_UID, INVALID_UID)) {
+ return ret;
+ }
+
+ fwmark.explicitlySelected = true;
+ mask.explicitlySelected = true;
+
+ fwmark.netId = INetd::LOCAL_NET_ID;
+ mask.netId = FWMARK_NET_ID_MASK;
+
+ return modifyIpRule(add ? RTM_NEWRULE : RTM_DELRULE, RULE_PRIORITY_EXPLICIT_NETWORK, table,
+ fwmark.intValue, mask.intValue, IIF_LOOPBACK, OIF_NONE, INVALID_UID,
+ INVALID_UID);
+}
+
// 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
@@ -858,7 +886,7 @@
/* static */
int RouteController::modifyPhysicalNetwork(unsigned netId, const char* interface,
const UidRangeMap& uidRangeMap, Permission permission,
- bool add, bool modifyNonUidBasedRules) {
+ bool add, bool modifyNonUidBasedRules, bool local) {
uint32_t table = getRouteTableForInterface(interface, false /* local */);
if (table == RT_TABLE_UNSPEC) {
return -ESRCH;
@@ -904,6 +932,11 @@
UidRanges::SUB_PRIORITY_HIGHEST, add)) {
return ret;
}
+ if (local) {
+ if (const int ret = modifyLocalNetworkRule(table, add)) {
+ return ret;
+ }
+ }
if (int ret = modifyOutputInterfaceRules(interface, table, permission, INVALID_UID, INVALID_UID,
UidRanges::SUB_PRIORITY_HIGHEST, add)) {
return ret;
@@ -1302,9 +1335,9 @@
int RouteController::addInterfaceToPhysicalNetwork(unsigned netId, const char* interface,
Permission permission,
- const UidRangeMap& uidRangeMap) {
+ const UidRangeMap& uidRangeMap, bool local) {
if (int ret = modifyPhysicalNetwork(netId, interface, uidRangeMap, permission, ACTION_ADD,
- MODIFY_NON_UID_BASED_RULES)) {
+ MODIFY_NON_UID_BASED_RULES, local)) {
return ret;
}
@@ -1320,9 +1353,10 @@
int RouteController::removeInterfaceFromPhysicalNetwork(unsigned netId, const char* interface,
Permission permission,
- const UidRangeMap& uidRangeMap) {
+ const UidRangeMap& uidRangeMap,
+ bool local) {
if (int ret = modifyPhysicalNetwork(netId, interface, uidRangeMap, permission, ACTION_DEL,
- MODIFY_NON_UID_BASED_RULES)) {
+ MODIFY_NON_UID_BASED_RULES, local)) {
return ret;
}
@@ -1366,17 +1400,17 @@
int RouteController::modifyPhysicalNetworkPermission(unsigned netId, const char* interface,
Permission oldPermission,
- Permission newPermission) {
+ Permission newPermission, bool local) {
// Physical network rules either use permission bits or UIDs, but not both.
// So permission changes don't affect any UID-based rules.
UidRangeMap emptyUidRangeMap;
// Add the new rules before deleting the old ones, to avoid race conditions.
if (int ret = modifyPhysicalNetwork(netId, interface, emptyUidRangeMap, newPermission,
- ACTION_ADD, MODIFY_NON_UID_BASED_RULES)) {
+ ACTION_ADD, MODIFY_NON_UID_BASED_RULES, local)) {
return ret;
}
return modifyPhysicalNetwork(netId, interface, emptyUidRangeMap, oldPermission, ACTION_DEL,
- MODIFY_NON_UID_BASED_RULES);
+ MODIFY_NON_UID_BASED_RULES, local);
}
int RouteController::addUsersToRejectNonSecureNetworkRule(const UidRanges& uidRanges) {
@@ -1500,15 +1534,15 @@
}
int RouteController::addUsersToPhysicalNetwork(unsigned netId, const char* interface,
- const UidRangeMap& uidRangeMap) {
+ const UidRangeMap& uidRangeMap, bool local) {
return modifyPhysicalNetwork(netId, interface, uidRangeMap, PERMISSION_NONE, ACTION_ADD,
- !MODIFY_NON_UID_BASED_RULES);
+ !MODIFY_NON_UID_BASED_RULES, local);
}
int RouteController::removeUsersFromPhysicalNetwork(unsigned netId, const char* interface,
- const UidRangeMap& uidRangeMap) {
+ const UidRangeMap& uidRangeMap, bool local) {
return modifyPhysicalNetwork(netId, interface, uidRangeMap, PERMISSION_NONE, ACTION_DEL,
- !MODIFY_NON_UID_BASED_RULES);
+ !MODIFY_NON_UID_BASED_RULES, local);
}
int RouteController::addUsersToUnreachableNetwork(unsigned netId, const UidRangeMap& uidRangeMap) {
diff --git a/server/RouteController.h b/server/RouteController.h
index 0d4e2b9..a56d4e0 100644
--- a/server/RouteController.h
+++ b/server/RouteController.h
@@ -121,11 +121,13 @@
[[nodiscard]] static int addInterfaceToPhysicalNetwork(unsigned netId, const char* interface,
Permission permission,
- const UidRangeMap& uidRangeMap);
+ const UidRangeMap& uidRangeMap,
+ bool local);
[[nodiscard]] static int removeInterfaceFromPhysicalNetwork(unsigned netId,
const char* interface,
Permission permission,
- const UidRangeMap& uidRangeMap);
+ const UidRangeMap& uidRangeMap,
+ bool local);
[[nodiscard]] static int addInterfaceToVirtualNetwork(unsigned netId, const char* interface,
bool secure,
@@ -138,7 +140,7 @@
[[nodiscard]] static int modifyPhysicalNetworkPermission(unsigned netId, const char* interface,
Permission oldPermission,
- Permission newPermission);
+ Permission newPermission, bool local);
[[nodiscard]] static int addUsersToVirtualNetwork(unsigned netId, const char* interface,
bool secure, const UidRangeMap& uidRangeMap,
@@ -179,10 +181,11 @@
Permission permission);
[[nodiscard]] static int addUsersToPhysicalNetwork(unsigned netId, const char* interface,
- const UidRangeMap& uidRangeMap);
+ const UidRangeMap& uidRangeMap, bool local);
[[nodiscard]] static int removeUsersFromPhysicalNetwork(unsigned netId, const char* interface,
- const UidRangeMap& uidRangeMap);
+ const UidRangeMap& uidRangeMap,
+ bool local);
[[nodiscard]] static int addUsersToUnreachableNetwork(unsigned netId,
const UidRangeMap& uidRangeMap);
@@ -221,7 +224,7 @@
static int modifyDefaultNetwork(uint16_t action, const char* interface, Permission permission);
static int modifyPhysicalNetwork(unsigned netId, const char* interface,
const UidRangeMap& uidRangeMap, Permission permission,
- bool add, bool modifyNonUidBasedRules);
+ bool add, bool modifyNonUidBasedRules, bool local);
static int modifyUnreachableNetwork(unsigned netId, const UidRangeMap& uidRangeMap, bool add);
static int modifyRoute(uint16_t action, uint16_t flags, const char* interface,
const char* destination, const char* nexthop, TableType tableType,
diff --git a/tests/binder_test.cpp b/tests/binder_test.cpp
index d4902bc..135c13c 100644
--- a/tests/binder_test.cpp
+++ b/tests/binder_test.cpp
@@ -121,6 +121,7 @@
using android::net::RULE_PRIORITY_BYPASSABLE_VPN_NO_LOCAL_EXCLUSION;
using android::net::RULE_PRIORITY_DEFAULT_NETWORK;
using android::net::RULE_PRIORITY_EXPLICIT_NETWORK;
+using android::net::RULE_PRIORITY_LOCAL_NETWORK;
using android::net::RULE_PRIORITY_LOCAL_ROUTES;
using android::net::RULE_PRIORITY_OUTPUT_INTERFACE;
using android::net::RULE_PRIORITY_PROHIBIT_NON_VPN;
@@ -178,6 +179,8 @@
{// 2001:db8:cafe::8888
.u6_addr8 = {0x20, 0x01, 0x0d, 0xb8, 0xca, 0xfe, 0, 0, 0, 0, 0, 0, 0, 0, 0x88, 0x88}}};
+typedef enum { ALL_EXIST, NONE_EXIST } ExistMode;
+
class NetdBinderTest : public NetNativeTestBase {
public:
NetdBinderTest() {
@@ -734,6 +737,16 @@
}
}
+static bool ipRuleExists(const char* ipVersion, const std::string& ipRule) {
+ std::vector<std::string> rules = listIpRules(ipVersion);
+ for (const auto& rule : rules) {
+ if (rule.find(ipRule) != std::string::npos) {
+ return true;
+ }
+ }
+ return false;
+}
+
static bool ipRuleExistsForRange(const uint32_t priority, const UidRangeParcel& range,
const std::string& action, const char* ipVersion,
const char* oif) {
@@ -773,6 +786,26 @@
return ipRuleExistsForRange(priority, range, action, nullptr);
}
+static void expectRuleForV4AndV6(ExistMode mode, const std::string& rule) {
+ for (const auto& ipVersion : {IP_RULE_V4, IP_RULE_V6}) {
+ if (mode == ALL_EXIST) {
+ EXPECT_TRUE(ipRuleExists(ipVersion, rule));
+ } else {
+ EXPECT_FALSE(ipRuleExists(ipVersion, rule));
+ }
+ }
+}
+
+static void expectLocalIpRuleExists(ExistMode mode, const std::string& ifName) {
+ std::string localIpRule = StringPrintf("%u:\tfrom all fwmark 0x0/0x10000 lookup %s",
+ RULE_PRIORITY_LOCAL_NETWORK, ifName.c_str());
+ expectRuleForV4AndV6(mode, localIpRule);
+
+ std::string dnsMasqRule = StringPrintf("%u:\tfrom all fwmark 0x10063/0x1ffff iif lo lookup %s",
+ RULE_PRIORITY_EXPLICIT_NETWORK, ifName.c_str());
+ expectRuleForV4AndV6(mode, dnsMasqRule);
+}
+
namespace {
UidRangeParcel makeUidRangeParcel(int start, int stop) {
@@ -849,6 +882,26 @@
EXPECT_EQ(ENONET, mNetd->networkDestroy(TEST_NETID1).serviceSpecificErrorCode());
}
+class LocalNetworkParameterizedTest : public NetdBinderTest,
+ public testing::WithParamInterface<bool> {};
+
+// Exercise both local and non-local networks
+INSTANTIATE_TEST_SUITE_P(LocalNetworkTests, LocalNetworkParameterizedTest, testing::Bool(),
+ [](const testing::TestParamInfo<bool>& info) {
+ return info.param ? "Local" : "NonLocal";
+ });
+
+TEST_P(LocalNetworkParameterizedTest, LocalNetworkUidRules) {
+ const bool local = GetParam();
+ const auto type = local ? NativeNetworkType::PHYSICAL_LOCAL : NativeNetworkType::PHYSICAL;
+ auto config = makeNativeNetworkConfig(TEST_NETID1, type, INetd::PERMISSION_NONE,
+ false /* secure */, false /* excludeLocalRoutes */);
+ EXPECT_TRUE(mNetd->networkCreate(config).isOk());
+ EXPECT_TRUE(mNetd->networkAddInterface(TEST_NETID1, sTun.name()).isOk());
+
+ expectLocalIpRuleExists(local ? ALL_EXIST : NONE_EXIST, sTun.name());
+}
+
TEST_F(NetdBinderTest, NetworkRejectNonSecureVpn) {
std::vector<UidRangeParcel> uidRanges = {makeUidRangeParcel(BASE_UID + 150, BASE_UID + 224),
makeUidRangeParcel(BASE_UID + 226, BASE_UID + 300)};
@@ -1566,16 +1619,6 @@
}
}
-bool ipRuleExists(const char* ipVersion, const std::string& ipRule) {
- std::vector<std::string> rules = listIpRules(ipVersion);
- for (const auto& rule : rules) {
- if (rule.find(ipRule) != std::string::npos) {
- return true;
- }
- }
- return false;
-}
-
std::vector<std::string> ipRouteSubstrings(const std::string& ifName, const std::string& dst,
const std::string& nextHop, const std::string& mtu) {
std::vector<std::string> routeSubstrings;
@@ -1637,18 +1680,14 @@
StringPrintf("%u:\tfrom all fwmark 0x0/0xffff iif lo lookup %s",
RULE_PRIORITY_DEFAULT_NETWORK, ifName);
- for (const auto& ipVersion : {IP_RULE_V4, IP_RULE_V6}) {
- EXPECT_TRUE(ipRuleExists(ipVersion, networkDefaultRule));
- }
+ expectRuleForV4AndV6(ALL_EXIST, networkDefaultRule);
}
void expectNetworkDefaultIpRuleDoesNotExist() {
std::string networkDefaultRule =
StringPrintf("%u:\tfrom all fwmark 0x0/0xffff iif lo", RULE_PRIORITY_DEFAULT_NETWORK);
- for (const auto& ipVersion : {IP_RULE_V4, IP_RULE_V6}) {
- EXPECT_FALSE(ipRuleExists(ipVersion, networkDefaultRule));
- }
+ expectRuleForV4AndV6(NONE_EXIST, networkDefaultRule);
}
void expectNetworkPermissionIpRuleExists(const char* ifName, int permission) {
@@ -1671,9 +1710,7 @@
break;
}
- for (const auto& ipVersion : {IP_RULE_V4, IP_RULE_V6}) {
- EXPECT_TRUE(ipRuleExists(ipVersion, networkPermissionRule));
- }
+ expectRuleForV4AndV6(ALL_EXIST, networkPermissionRule);
}
// TODO: It is a duplicate function, need to remove it
@@ -5516,4 +5553,4 @@
EXPECT_EQ(0, setNetworkForProcess(ENTERPRISE_NETID_2));
EXPECT_EQ(0, setNetworkForProcess(ENTERPRISE_NETID_3));
}
-}
\ No newline at end of file
+}