#include <string>
#include <vector>
+namespace {
+
+// Parses string permissions in argv[*nextArg], argv[*nextArg + 1], etc. and converts them into a
+// Permission enum. On return, nextArg will point to one past the last valid permissions string.
+Permission parseMultiplePermissions(int argc, char** argv, int* nextArg) {
+ Permission permission = PERMISSION_NONE;
+ for (; *nextArg < argc; ++*nextArg) {
+ Permission p = permissionFromString(argv[*nextArg]);
+ if (p == PERMISSION_NONE) {
+ break;
+ }
+ permission = static_cast<Permission>(permission | p);
+ }
+ return permission;
+}
+
+} // namespace
+
PermissionsController* CommandListener::sPermissionsController = NULL;
RouteController* CommandListener::sRouteController = NULL;
NetworkController *CommandListener::sNetCtrl = NULL;
return syntaxError(client, "Missing argument");
}
- // 0 1 2 3 4
- // network create <netId> <interface> [CNS|CI]
+ // 0 1 2 3 4
+ // network create <netId> <interface> [<permission> ...]
if (!strcmp(argv[1], "create")) {
if (argc < 4) {
return syntaxError(client, "Missing argument");
return paramError(client, "Invalid netId");
}
const char* interface = argv[3];
- Permission permission = PERMISSION_NONE;
- if (argc > 4) {
- permission = permissionFromString(argv[4]);
- if (permission == PERMISSION_NONE) {
- return paramError(client, "Invalid permission");
- }
+ int nextArg = 4;
+ Permission permission = parseMultiplePermissions(argc, argv, &nextArg);
+ if (nextArg != argc) {
+ return syntaxError(client, "Unknown trailing argument(s)");
}
if (!sNetCtrl->createNetwork(netId, interface, permission)) {
return operationError(client, "createNetwork() failed");
// 0 1 2
// network destroy <netId>
if (!strcmp(argv[1], "destroy")) {
- if (argc < 3) {
- return syntaxError(client, "Missing netId");
+ if (argc != 3) {
+ 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[2], NULL, 0);
return success(client);
}
+ // 0 1 2 3 4
+ // network permission user set [<permission> ...] <uid> ...
+ // network permission user clear <uid> ...
+ // network permission network set [<permission> ...] <netId> ...
+ // network permission network clear <netId> ...
+ if (!strcmp(argv[1], "permission")) {
+ if (argc < 5) {
+ return syntaxError(client, "Missing argument");
+ }
+ int nextArg = 4;
+ Permission permission = PERMISSION_NONE;
+ if (!strcmp(argv[3], "set")) {
+ permission = parseMultiplePermissions(argc, argv, &nextArg);
+ } else if (strcmp(argv[3], "clear")) {
+ return syntaxError(client, "Unknown argument");
+ }
+ std::vector<unsigned> ids;
+ for (; nextArg < argc; ++nextArg) {
+ char* endPtr;
+ unsigned id = strtoul(argv[nextArg], &endPtr, 0);
+ if (!*argv[nextArg] || *endPtr) {
+ return syntaxError(client, "Invalid id");
+ }
+ ids.push_back(id);
+ }
+ if (ids.empty()) {
+ return syntaxError(client, "Missing id");
+ }
+ if (!strcmp(argv[2], "user")) {
+ if (!sNetCtrl->setPermissionForUser(permission, ids)) {
+ return operationError(client, "setPermissionForUser() failed");
+ }
+ } else if (!strcmp(argv[2], "network")) {
+ if (!sNetCtrl->setPermissionForNetwork(permission, ids)) {
+ return operationError(client, "setPermissionForNetwork() failed");
+ }
+ } else {
+ return syntaxError(client, "Unknown argument");
+ }
+ 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 permission user|network set [CI] [CNS] <id1> .. <idN>
- // network permission user|network clear <id1> .. <idN>
// network vpn create <netId> [owner_uid]
// network vpn destroy <netId>
// network <bind|unbind> <netId> <uid1> .. <uidN>
if (protect) {
fwmark |= FWMARK_MASK_PROTECT;
}
- if (permission == PERMISSION_CHANGE_NETWORK_STATE) {
- fwmark |= FWMARK_MASK_CHANGE_NETWORK_STATE;
- } else if (permission == PERMISSION_CONNECTIVITY_INTERNAL) {
- // CONNECTIVITY_INTERNAL implies CHANGE_NETWORK_STATE.
+ if (permission & PERMISSION_CHANGE_NETWORK_STATE) {
fwmark |= FWMARK_MASK_CHANGE_NETWORK_STATE;
+ }
+ if (permission & PERMISSION_CONNECTIVITY_INTERNAL) {
fwmark |= FWMARK_MASK_CONNECTIVITY_INTERNAL;
}
return fwmark;
bool NetworkController::createNetwork(unsigned netId, const char* interface,
Permission permission) {
if (!isNetIdValid(netId) || !interface) {
+ ALOGE("invalid netId %u or interface null", netId);
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;
}
}
return false;
}
- mPermissionsController->setPermissionForNetwork(netId, permission);
+ mPermissionsController->setPermissionForNetwork(permission, netId);
mNetIdToInterfaces.insert(std::pair<unsigned, std::string>(netId, interface));
return true;
}
bool NetworkController::destroyNetwork(unsigned netId) {
if (!isNetIdValid(netId)) {
+ ALOGE("invalid netId %u", netId);
return false;
}
- // TODO: ioctl(SIOCKILLADDR, ...);
+ // TODO: ioctl(SIOCKILLADDR, ...) to kill all sockets on the old network.
bool status = true;
}
}
- mPermissionsController->clearPermissionForNetwork(netId);
+ mPermissionsController->setPermissionForNetwork(PERMISSION_NONE, netId);
mNetIdToInterfaces.erase(netId);
return status;
}
+bool NetworkController::setPermissionForUser(Permission permission,
+ const std::vector<unsigned>& uid) {
+ for (size_t i = 0; i < uid.size(); ++i) {
+ mPermissionsController->setPermissionForUser(permission, uid[i]);
+ }
+ return true;
+}
+
+bool NetworkController::setPermissionForNetwork(Permission newPermission,
+ const std::vector<unsigned>& netId) {
+ bool status = true;
+
+ for (size_t i = 0; i < netId.size(); ++i) {
+ if (!isNetIdValid(netId[i])) {
+ ALOGE("invalid netId %u", netId[i]);
+ status = false;
+ 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;
+ }
+
+ Permission oldPermission = mPermissionsController->getPermissionForNetwork(netId[i]);
+ if (oldPermission == newPermission) {
+ continue;
+ }
+
+ // TODO: ioctl(SIOCKILLADDR, ...) to kill sockets on the network that don't have
+ // newPermission.
+
+ for (Iterator iter = range.first; iter != range.second; ++iter) {
+ if (!mRouteController->modifyNetworkPermission(netId[i], iter->second.c_str(),
+ oldPermission, newPermission)) {
+ status = false;
+ }
+ }
+
+ mPermissionsController->setPermissionForNetwork(newPermission, netId[i]);
+ }
+
+ return status;
+}
+
NetworkController::UidEntry::UidEntry(
int start, int end, unsigned netId, bool forward_dns)
: uid_start(start),
#include <list>
#include <map>
#include <string>
+#include <vector>
#include <stddef.h>
#include <stdint.h>
bool createNetwork(unsigned netId, const char* interface, Permission permission);
bool destroyNetwork(unsigned netId);
+ bool setPermissionForUser(Permission permission, const std::vector<unsigned>& uid);
+ bool setPermissionForNetwork(Permission permission, const std::vector<unsigned>& netId);
+
private:
struct UidEntry {
int uid_start;
// app must hold to be allowed to use the network. PERMISSION_NONE means "no special permission is
// held by the app" or "no special permission is required to use the network".
//
-// Currently, each permission includes all the permissions above it (i.e., CONNECTIVITY_INTERNAL
-// implies CHANGE_NETWORK_STATE), which is why these are not bit values that need to be OR'ed
-// together. This may change in the future.
+// Permissions are flags that can be OR'ed together to represent combinations of permissions.
+//
+// NOTE: netd doesn't enforce any semantics for the permission values. E.g., while all apps with
+// CONNECTIVITY_INTERNAL may also implicitly or explicitly have CHANGE_NETWORK_STATE, netd doesn't
+// enforce it. It's the responsibility of the caller to set all permissions bits explicitly. I.e.,
+// if you set the PERMISSION_CONNECTIVITY_INTERNAL bit, you should probably also set the
+// PERMISSION_CHANGE_NETWORK_STATE bit.
enum Permission {
- PERMISSION_NONE,
- PERMISSION_CHANGE_NETWORK_STATE,
- PERMISSION_CONNECTIVITY_INTERNAL
+ PERMISSION_NONE = 0x0,
+ PERMISSION_CHANGE_NETWORK_STATE = 0x1,
+ PERMISSION_CONNECTIVITY_INTERNAL = 0x2
};
Permission permissionFromString(const char* permission);
#include "PermissionsController.h"
-Permission PermissionsController::getPermissionForNetwork(unsigned netId) const {
- std::map<unsigned, Permission>::const_iterator iter = mNetworks.find(netId);
- return iter != mNetworks.end() ? iter->second : PERMISSION_NONE;
+namespace {
+
+Permission get(const std::map<unsigned, Permission>& map, unsigned id) {
+ std::map<unsigned, Permission>::const_iterator iter = map.find(id);
+ return iter != map.end() ? iter->second : PERMISSION_NONE;
}
-void PermissionsController::setPermissionForNetwork(unsigned netId, Permission permission) {
+void set(std::map<unsigned, Permission>* map, Permission permission, unsigned id) {
if (permission == PERMISSION_NONE) {
- clearPermissionForNetwork(netId);
- return;
+ map->erase(id);
+ } else {
+ (*map)[id] = permission;
}
- mNetworks[netId] = permission;
}
-void PermissionsController::clearPermissionForNetwork(unsigned netId) {
- mNetworks.erase(netId);
+} // namespace
+
+Permission PermissionsController::getPermissionForUser(unsigned uid) const {
+ return get(mUsers, uid);
+}
+
+void PermissionsController::setPermissionForUser(Permission permission, unsigned uid) {
+ set(&mUsers, permission, uid);
+}
+
+Permission PermissionsController::getPermissionForNetwork(unsigned netId) const {
+ return get(mNetworks, netId);
+}
+
+void PermissionsController::setPermissionForNetwork(Permission permission, unsigned netId) {
+ set(&mNetworks, permission, netId);
}
class PermissionsController {
public:
+ Permission getPermissionForUser(unsigned uid) const;
+ void setPermissionForUser(Permission permission, unsigned uid);
+
Permission getPermissionForNetwork(unsigned netId) const;
- void setPermissionForNetwork(unsigned netId, Permission permission);
- void clearPermissionForNetwork(unsigned netId);
+ void setPermissionForNetwork(Permission permission, unsigned netId);
private:
+ std::map<unsigned, Permission> mUsers;
std::map<unsigned, Permission> mNetworks;
};
return true;
}
-bool modifyNetwork(unsigned netId, const char* interface, Permission permission, bool add) {
+bool modifyRules(unsigned netId, const char* interface, Permission permission, bool add,
+ bool modifyIptables) {
uint32_t table = getRouteTableForInterface(interface);
if (!table) {
return false;
// ping replies).
// + Mark sockets that accept connections from this interface so that the connection stays on
// the same interface.
- action = add ? "-A" : "-D";
- char markString[UINT32_HEX_STRLEN];
- snprintf(markString, sizeof(markString), "0x%x", netId);
- if (execIptables(V4V6, "-t", "mangle", action, "INPUT", "-i", interface, "-j", "MARK",
- "--set-mark", markString, NULL)) {
- return false;
+ if (modifyIptables) {
+ action = add ? "-A" : "-D";
+ char markString[UINT32_HEX_STRLEN];
+ snprintf(markString, sizeof(markString), "0x%x", netId);
+ if (execIptables(V4V6, "-t", "mangle", action, "INPUT", "-i", interface, "-j", "MARK",
+ "--set-mark", markString, NULL)) {
+ return false;
+ }
}
return true;
} // namespace
bool RouteController::createNetwork(unsigned netId, const char* interface, Permission permission) {
- return modifyNetwork(netId, interface, permission, true);
+ return modifyRules(netId, interface, permission, true, true);
}
bool RouteController::destroyNetwork(unsigned netId, const char* interface, Permission permission) {
- return modifyNetwork(netId, interface, permission, false);
+ return modifyRules(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);
}
static bool createNetwork(unsigned netId, const char* interface, Permission permission);
static bool destroyNetwork(unsigned netId, const char* interface, Permission permission);
+ static bool modifyNetworkPermission(unsigned netId, const char* interface,
+ Permission oldPermission, Permission newPermission);
};
#endif // SYSTEM_NETD_ROUTE_CONTROLLER_H