Implement network default set/clear.
(cherry picked from commit 4cc7df247a7ec67da3b5edfb9356077fbbf0150c)
Change-Id: Ib35d54816884c6a6ba28231c9b1f54e362d1d16a
diff --git a/CommandListener.cpp b/CommandListener.cpp
index 34a28fa..34f356c 100644
--- a/CommandListener.cpp
+++ b/CommandListener.cpp
@@ -1728,11 +1728,35 @@
return success(client);
}
+ // 0 1 2 3
+ // network default set <netId>
+ // network default clear
+ if (!strcmp(argv[1], "default")) {
+ if (argc < 3) {
+ return syntaxError(client, "Missing argument");
+ }
+ unsigned netId = NETID_UNSET;
+ if (!strcmp(argv[2], "set")) {
+ if (argc < 4) {
+ return syntaxError(client, "Missing netId");
+ }
+ // strtoul() returns 0 on errors, which is fine because 0 is an invalid netId.
+ netId = strtoul(argv[3], NULL, 0);
+ if (!sNetCtrl->isNetIdValid(netId)) {
+ return paramError(client, "Invalid netId");
+ }
+ } else if (strcmp(argv[2], "clear")) {
+ return syntaxError(client, "Unknown argument");
+ }
+ if (!sNetCtrl->setDefaultNetwork(netId)) {
+ return operationError(client, "setDefaultNetwork() 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>
- // network default set <netId>
- // network default clear
// network vpn create <netId> [owner_uid]
// network vpn destroy <netId>
// network <bind|unbind> <netId> <uid1> .. <uidN>
diff --git a/NetworkController.cpp b/NetworkController.cpp
index 185f7d7..14c63f3 100644
--- a/NetworkController.cpp
+++ b/NetworkController.cpp
@@ -51,12 +51,51 @@
}
unsigned NetworkController::getDefaultNetwork() const {
+ android::RWLock::AutoRLock lock(mRWLock);
return mDefaultNetId;
}
-void NetworkController::setDefaultNetwork(unsigned netId) {
- android::RWLock::AutoWLock lock(mRWLock);
- mDefaultNetId = netId;
+bool NetworkController::setDefaultNetwork(unsigned newNetId) {
+ unsigned oldNetId;
+ {
+ android::RWLock::AutoWLock lock(mRWLock);
+ oldNetId = mDefaultNetId;
+ mDefaultNetId = newNetId;
+ }
+
+ if (oldNetId == newNetId) {
+ return true;
+ }
+
+ bool status = true;
+
+ // 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) {
+ if (!mRouteController->addDefaultNetwork(iter->second.c_str(), permission)) {
+ status = false;
+ }
+ }
+ }
+
+ // 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) {
+ if (!mRouteController->removeDefaultNetwork(iter->second.c_str(), permission)) {
+ status = false;
+ }
+ }
+ }
+
+ return status;
}
void NetworkController::setNetworkForPid(int pid, unsigned netId) {
@@ -176,6 +215,14 @@
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).
+ setDefaultNetwork(NETID_UNSET);
+ }
+
return status;
}
diff --git a/NetworkController.h b/NetworkController.h
index 1063f92..e113bf5 100644
--- a/NetworkController.h
+++ b/NetworkController.h
@@ -51,7 +51,7 @@
void clearNetworkPreference();
unsigned getDefaultNetwork() const;
- void setDefaultNetwork(unsigned netId);
+ bool setDefaultNetwork(unsigned netId);
void setNetworkForPid(int pid, unsigned netId);
bool setNetworkForUidRange(int uid_start, int uid_end, unsigned netId, bool forward_dns);
bool clearNetworkForUidRange(int uid_start, int uid_end, unsigned netId);
@@ -91,6 +91,12 @@
PermissionsController* const mPermissionsController;
RouteController* const mRouteController;
+ // Maps a netId to all its interfaces.
+ //
+ // We need to know interface names to configure incoming packet marking and because routing
+ // tables are associated with interfaces and not with netIds.
+ //
+ // An interface may belong to at most one netId, but a netId may have multiple interfaces.
std::multimap<unsigned, std::string> mNetIdToInterfaces;
};
diff --git a/RouteController.cpp b/RouteController.cpp
index 737dd9f..a8f6700 100644
--- a/RouteController.cpp
+++ b/RouteController.cpp
@@ -24,13 +24,14 @@
namespace {
-const uint32_t RULE_PRIORITY_PER_NETWORK_EXPLICIT = 300;
-const uint32_t RULE_PRIORITY_PER_NETWORK_OIF = 400;
-const uint32_t RULE_PRIORITY_PER_NETWORK_NORMAL = 700;
+const uint32_t RULE_PRIORITY_PER_NETWORK_EXPLICIT = 300;
+const uint32_t RULE_PRIORITY_PER_NETWORK_INTERFACE = 400;
+const uint32_t RULE_PRIORITY_PER_NETWORK_NORMAL = 700;
+const uint32_t RULE_PRIORITY_DEFAULT_NETWORK = 900;
-const bool FWMARK_USE_NET_ID = true;
+const bool FWMARK_USE_NET_ID = true;
const bool FWMARK_USE_EXPLICIT = true;
-const bool FWMARK_USE_PROTECT = true;
+const bool FWMARK_USE_PROTECT = true;
uint32_t getRouteTableForInterface(const char* interface) {
uint32_t index = if_nametoindex(interface);
@@ -38,7 +39,7 @@
}
bool runIpRuleCommand(const char* action, uint32_t priority, uint32_t table,
- uint32_t fwmark, uint32_t mask, const char* oif) {
+ uint32_t fwmark, uint32_t mask, const char* interface) {
char priorityString[UINT32_STRLEN];
char tableString[UINT32_STRLEN];
@@ -65,9 +66,9 @@
argv[argc++] = "fwmark";
argv[argc++] = fwmarkString;
}
- if (oif) {
+ if (interface) {
argv[argc++] = "oif";
- argv[argc++] = oif;
+ argv[argc++] = interface;
}
if (android_fork_execvp(argc, const_cast<char**>(argv), NULL, false, false)) {
return false;
@@ -77,8 +78,8 @@
return true;
}
-bool modifyRules(unsigned netId, const char* interface, Permission permission, bool add,
- bool modifyIptables) {
+bool modifyPerNetworkRules(unsigned netId, const char* interface, Permission permission, bool add,
+ bool modifyIptables) {
uint32_t table = getRouteTableForInterface(interface);
if (!table) {
return false;
@@ -105,7 +106,8 @@
// 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);
- if (!runIpRuleCommand(action, RULE_PRIORITY_PER_NETWORK_OIF, table, fwmark, mask, interface)) {
+ if (!runIpRuleCommand(action, RULE_PRIORITY_PER_NETWORK_INTERFACE, table, fwmark, mask,
+ interface)) {
return false;
}
@@ -140,20 +142,45 @@
return true;
}
+bool modifyDefaultNetworkRules(const char* interface, Permission permission, const char* action) {
+ uint32_t table = getRouteTableForInterface(interface);
+ if (!table) {
+ return false;
+ }
+
+ uint32_t fwmark = getFwmark(0, !FWMARK_USE_EXPLICIT, !FWMARK_USE_PROTECT, permission);
+ 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 false;
+ }
+
+ return true;
+}
+
} // namespace
bool RouteController::createNetwork(unsigned netId, const char* interface, Permission permission) {
- return modifyRules(netId, interface, permission, true, true);
+ return modifyPerNetworkRules(netId, interface, permission, true, true);
}
bool RouteController::destroyNetwork(unsigned netId, const char* interface, Permission permission) {
- return modifyRules(netId, interface, permission, false, true);
+ return modifyPerNetworkRules(netId, interface, permission, false, true);
// TODO: Flush the routing table.
}
bool RouteController::modifyNetworkPermission(unsigned netId, const char* interface,
Permission oldPermission, Permission newPermission) {
// Add the new rules before deleting the old ones, to avoid race conditions.
- return modifyRules(netId, interface, newPermission, true, false) &&
- modifyRules(netId, interface, oldPermission, false, false);
+ return modifyPerNetworkRules(netId, interface, newPermission, true, false) &&
+ modifyPerNetworkRules(netId, interface, oldPermission, false, false);
+}
+
+bool RouteController::addDefaultNetwork(const char* interface, Permission permission) {
+ return modifyDefaultNetworkRules(interface, permission, ADD);
+}
+
+bool RouteController::removeDefaultNetwork(const char* interface, Permission permission) {
+ return modifyDefaultNetworkRules(interface, permission, DEL);
}
diff --git a/RouteController.h b/RouteController.h
index 65f8f84..1db1da3 100644
--- a/RouteController.h
+++ b/RouteController.h
@@ -27,6 +27,9 @@
static bool destroyNetwork(unsigned netId, const char* interface, Permission permission);
static bool modifyNetworkPermission(unsigned netId, const char* interface,
Permission oldPermission, Permission newPermission);
+
+ static bool addDefaultNetwork(const char* interface, Permission permission);
+ static bool removeDefaultNetwork(const char* interface, Permission permission);
};
#endif // SYSTEM_NETD_ROUTE_CONTROLLER_H