const unsigned NUM_OEM_IDS = NetworkController::MAX_OEM_ID - NetworkController::MIN_OEM_ID + 1;
Permission stringToPermission(const char* arg) {
- if (!strcmp(arg, "android.permission.CHANGE_NETWORK_STATE")) {
+ if (!strcmp(arg, "NETWORK")) {
return PERMISSION_NETWORK;
}
- if (!strcmp(arg, "android.permission.CONNECTIVITY_INTERNAL")) {
+ if (!strcmp(arg, "SYSTEM")) {
return PERMISSION_SYSTEM;
}
return PERMISSION_NONE;
ResolverController *CommandListener::sResolverCtrl = NULL;
FirewallController *CommandListener::sFirewallCtrl = NULL;
ClatdController *CommandListener::sClatdCtrl = NULL;
+StrictController *CommandListener::sStrictCtrl = NULL;
/**
* List of module chains to be created, along with explicit ordering. ORDERING
static const char* FILTER_OUTPUT[] = {
OEM_IPTABLES_FILTER_OUTPUT,
FirewallController::LOCAL_OUTPUT,
+ StrictController::LOCAL_OUTPUT,
BandwidthController::LOCAL_OUTPUT,
NULL,
};
registerCmd(new FirewallCmd());
registerCmd(new ClatdCmd());
registerCmd(new NetworkCommand());
+ registerCmd(new StrictCmd());
if (!sNetCtrl)
sNetCtrl = new NetworkController();
sInterfaceCtrl = new InterfaceController();
if (!sClatdCtrl)
sClatdCtrl = new ClatdController(sNetCtrl);
+ if (!sStrictCtrl)
+ sStrictCtrl = new StrictController();
/*
* This is the only time we touch top-level chains in iptables; controllers
"Failed to change IPv6 state", true);
}
return 0;
+ } else if (!strcmp(argv[1], "ipv6ndoffload")) {
+ if (argc != 4) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError,
+ "Usage: interface ipv6ndoffload <interface> <enable|disable>",
+ false);
+ return 0;
+ }
+ int enable = !strncmp(argv[3], "enable", 7);
+ if (sInterfaceCtrl->setIPv6NdOffload(argv[2], enable) == 0) {
+ cli->sendMsg(ResponseCode::CommandOkay, "IPv6 ND offload changed", false);
+ } else {
+ cli->sendMsg(ResponseCode::OperationFailed,
+ "Failed to change IPv6 ND offload state", true);
+ }
+ return 0;
} else if (!strcmp(argv[1], "setmtu")) {
if (argc != 4) {
cli->sendMsg(ResponseCode::CommandSyntaxError,
cli->sendMsg(ResponseCode::CommandOkay, "MTU changed", false);
} else {
cli->sendMsg(ResponseCode::OperationFailed,
- "Failed to get MTU", true);
+ "Failed to set MTU", true);
}
return 0;
} else {
NetdCommand("ipfwd") {
}
-int CommandListener::IpFwdCmd::runCommand(SocketClient *cli,
- int argc, char **argv) {
- int rc = 0;
+int CommandListener::IpFwdCmd::runCommand(SocketClient *cli, int argc, char **argv) {
+ bool matched = false;
+ bool success;
- if (argc < 2) {
- cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false);
- return 0;
- }
-
- if (!strcmp(argv[1], "status")) {
- char *tmp = NULL;
+ if (argc == 2) {
+ // 0 1
+ // ipfwd status
+ if (!strcmp(argv[1], "status")) {
+ char *tmp = NULL;
- asprintf(&tmp, "Forwarding %s", (sTetherCtrl->getIpFwdEnabled() ? "enabled" : "disabled"));
- cli->sendMsg(ResponseCode::IpFwdStatusResult, tmp, false);
- free(tmp);
- return 0;
- } else if (!strcmp(argv[1], "enable")) {
- rc = sTetherCtrl->setIpFwdEnabled(true);
- } else if (!strcmp(argv[1], "disable")) {
- rc = sTetherCtrl->setIpFwdEnabled(false);
- } else {
+ asprintf(&tmp, "Forwarding %s",
+ ((sTetherCtrl->forwardingRequestCount() > 0) ? "enabled" : "disabled"));
+ cli->sendMsg(ResponseCode::IpFwdStatusResult, tmp, false);
+ free(tmp);
+ return 0;
+ }
+ } else if (argc == 3) {
+ // 0 1 2
+ // ipfwd enable <requester>
+ // ipfwd disable <requester>
+ if (!strcmp(argv[1], "enable")) {
+ matched = true;
+ success = sTetherCtrl->enableForwarding(argv[2]);
+ } else if (!strcmp(argv[1], "disable")) {
+ matched = true;
+ success = sTetherCtrl->disableForwarding(argv[2]);
+ }
+ } else if (argc == 4) {
+ // 0 1 2 3
+ // ipfwd add wlan0 dummy0
+ // ipfwd remove wlan0 dummy0
+ int ret = 0;
+ if (!strcmp(argv[1], "add")) {
+ matched = true;
+ ret = RouteController::enableTethering(argv[2], argv[3]);
+ } else if (!strcmp(argv[1], "remove")) {
+ matched = true;
+ ret = RouteController::disableTethering(argv[2], argv[3]);
+ }
+ success = (ret == 0);
+ errno = -ret;
+ }
+
+ if (!matched) {
cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown ipfwd cmd", false);
return 0;
}
- if (!rc) {
+ if (success) {
cli->sendMsg(ResponseCode::CommandOkay, "ipfwd operation succeeded", false);
} else {
cli->sendMsg(ResponseCode::OperationFailed, "ipfwd operation failed", true);
}
-
return 0;
}
return 0;
}
- int num_addrs = argc - 2;
- int arg_index = 2;
- int array_index = 0;
- in_addr *addrs = (in_addr *)malloc(sizeof(in_addr) * num_addrs);
- while (array_index < num_addrs) {
- if (!inet_aton(argv[arg_index++], &(addrs[array_index++]))) {
+ const int num_addrs = argc - 2;
+ // TODO: consider moving this validation into TetherController.
+ struct in_addr tmp_addr;
+ for (int arg_index = 2; arg_index < argc; arg_index++) {
+ if (!inet_aton(argv[arg_index], &tmp_addr)) {
cli->sendMsg(ResponseCode::CommandParameterError, "Invalid address", false);
- free(addrs);
return 0;
}
}
- rc = sTetherCtrl->startTethering(num_addrs, addrs);
- free(addrs);
+
+ rc = sTetherCtrl->startTethering(num_addrs, &(argv[2]));
} else if (!strcmp(argv[1], "interface")) {
if (!strcmp(argv[2], "add")) {
rc = sTetherCtrl->tetherInterface(argv[3]);
int rc = 0;
const char **argv = const_cast<const char **>(margv);
- if (argc < 2) {
+ if (argc < 3) {
cli->sendMsg(ResponseCode::CommandSyntaxError, "Resolver missing arguments", false);
return 0;
}
+ unsigned netId = stringToNetId(argv[2]);
+ // TODO: Consider making NetworkController.isValidNetwork() public
+ // and making that check here.
+
if (!strcmp(argv[1], "setnetdns")) {
// "resolver setnetdns <netId> <domains> <dns1> <dns2> ..."
if (argc >= 5) {
- rc = sResolverCtrl->setDnsServers(strtoul(argv[2], NULL, 0), argv[3], &argv[4], argc - 4);
+ rc = sResolverCtrl->setDnsServers(netId, argv[3], &argv[4], argc - 4);
} else {
cli->sendMsg(ResponseCode::CommandSyntaxError,
"Wrong number of arguments to resolver setnetdns", false);
return 0;
}
+ } else if (!strcmp(argv[1], "clearnetdns")) { // "resolver clearnetdns <netId>"
+ if (argc == 3) {
+ rc = sResolverCtrl->clearDnsServers(netId);
+ } else {
+ cli->sendMsg(ResponseCode::CommandSyntaxError,
+ "Wrong number of arguments to resolver clearnetdns", false);
+ return 0;
+ }
} else if (!strcmp(argv[1], "flushnet")) { // "resolver flushnet <netId>"
if (argc == 3) {
- rc = sResolverCtrl->flushDnsCache(strtoul(argv[2], NULL, 0));
+ rc = sResolverCtrl->flushDnsCache(netId);
} else {
cli->sendMsg(ResponseCode::CommandSyntaxError,
"Wrong number of arguments to resolver flushnet", false);
FirewallRule CommandListener::FirewallCmd::parseRule(const char* arg) {
if (!strcmp(arg, "allow")) {
return ALLOW;
- } else {
+ } else if (!strcmp(arg, "deny")) {
return DENY;
+ } else {
+ ALOGE("failed to parse uid rule (%s)", arg);
+ return ALLOW;
+ }
+}
+
+FirewallType CommandListener::FirewallCmd::parseFirewallType(const char* arg) {
+ if (!strcmp(arg, "whitelist")) {
+ return WHITELIST;
+ } else if (!strcmp(arg, "blacklist")) {
+ return BLACKLIST;
+ } else {
+ ALOGE("failed to parse firewall type (%s)", arg);
+ return BLACKLIST;
+ }
+}
+
+ChildChain CommandListener::FirewallCmd::parseChildChain(const char* arg) {
+ if (!strcmp(arg, "dozable")) {
+ return DOZABLE;
+ } else if (!strcmp(arg, "standby")) {
+ return STANDBY;
+ } else if (!strcmp(arg, "none")) {
+ return NONE;
+ } else {
+ ALOGE("failed to parse child firewall chain (%s)", arg);
+ return INVALID_CHAIN;
}
}
}
if (!strcmp(argv[1], "enable")) {
- int res = sFirewallCtrl->enableFirewall();
+ if (argc != 3) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError,
+ "Usage: firewall enable <whitelist|blacklist>", false);
+ return 0;
+ }
+ FirewallType firewallType = parseFirewallType(argv[2]);
+
+ int res = sFirewallCtrl->enableFirewall(firewallType);
return sendGenericOkFail(cli, res);
}
if (!strcmp(argv[1], "disable")) {
}
if (!strcmp(argv[1], "set_uid_rule")) {
- if (argc != 4) {
+ if (argc != 5) {
cli->sendMsg(ResponseCode::CommandSyntaxError,
- "Usage: firewall set_uid_rule <1000> <allow|deny>",
+ "Usage: firewall set_uid_rule <dozable|standby|none> <1000> <allow|deny>",
false);
return 0;
}
- int uid = atoi(argv[2]);
- FirewallRule rule = parseRule(argv[3]);
+ ChildChain childChain = parseChildChain(argv[2]);
+ if (childChain == INVALID_CHAIN) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError,
+ "Invalid chain name. Valid names are: <dozable|standby|none>",
+ false);
+ return 0;
+ }
+ int uid = atoi(argv[3]);
+ FirewallRule rule = parseRule(argv[4]);
+ int res = sFirewallCtrl->setUidRule(childChain, uid, rule);
+ return sendGenericOkFail(cli, res);
+ }
+
+ if (!strcmp(argv[1], "enable_chain")) {
+ if (argc != 3) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError,
+ "Usage: firewall enable_chain <dozable|standby>",
+ false);
+ return 0;
+ }
+
+ ChildChain childChain = parseChildChain(argv[2]);
+ int res = sFirewallCtrl->enableChildChains(childChain, true);
+ return sendGenericOkFail(cli, res);
+ }
- int res = sFirewallCtrl->setUidRule(uid, rule);
+ if (!strcmp(argv[1], "disable_chain")) {
+ if (argc != 3) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError,
+ "Usage: firewall disable_chain <dozable|standby>",
+ false);
+ return 0;
+ }
+
+ ChildChain childChain = parseChildChain(argv[2]);
+ int res = sFirewallCtrl->enableChildChains(childChain, false);
return sendGenericOkFail(cli, res);
}
int CommandListener::ClatdCmd::runCommand(SocketClient *cli, int argc,
char **argv) {
int rc = 0;
- if (argc < 2) {
+ if (argc < 3) {
cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false);
return 0;
}
- if(!strcmp(argv[1], "stop")) {
- rc = sClatdCtrl->stopClatd();
+ if (!strcmp(argv[1], "stop")) {
+ rc = sClatdCtrl->stopClatd(argv[2]);
} else if (!strcmp(argv[1], "status")) {
char *tmp = NULL;
-
- asprintf(&tmp, "Clatd status: %s", (sClatdCtrl->isClatdStarted() ?
- "started" : "stopped"));
+ asprintf(&tmp, "Clatd status: %s", (sClatdCtrl->isClatdStarted(argv[2]) ?
+ "started" : "stopped"));
cli->sendMsg(ResponseCode::ClatdStatusResult, tmp, false);
free(tmp);
return 0;
- } else if(!strcmp(argv[1], "start")) {
- if (argc < 3) {
- cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false);
- return 0;
- }
+ } else if (!strcmp(argv[1], "start")) {
rc = sClatdCtrl->startClatd(argv[2]);
} else {
cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown clatd cmd", false);
return 0;
}
+CommandListener::StrictCmd::StrictCmd() :
+ NetdCommand("strict") {
+}
+
+int CommandListener::StrictCmd::sendGenericOkFail(SocketClient *cli, int cond) {
+ if (!cond) {
+ cli->sendMsg(ResponseCode::CommandOkay, "Strict command succeeded", false);
+ } else {
+ cli->sendMsg(ResponseCode::OperationFailed, "Strict command failed", false);
+ }
+ return 0;
+}
+
+StrictPenalty CommandListener::StrictCmd::parsePenalty(const char* arg) {
+ if (!strcmp(arg, "reject")) {
+ return REJECT;
+ } else if (!strcmp(arg, "log")) {
+ return LOG;
+ } else if (!strcmp(arg, "accept")) {
+ return ACCEPT;
+ } else {
+ return INVALID;
+ }
+}
+
+int CommandListener::StrictCmd::runCommand(SocketClient *cli, int argc,
+ char **argv) {
+ if (argc < 2) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing command", false);
+ return 0;
+ }
+
+ if (!strcmp(argv[1], "enable")) {
+ int res = sStrictCtrl->enableStrict();
+ return sendGenericOkFail(cli, res);
+ }
+ if (!strcmp(argv[1], "disable")) {
+ int res = sStrictCtrl->disableStrict();
+ return sendGenericOkFail(cli, res);
+ }
+
+ if (!strcmp(argv[1], "set_uid_cleartext_policy")) {
+ if (argc != 4) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError,
+ "Usage: strict set_uid_cleartext_policy <uid> <accept|log|reject>",
+ false);
+ return 0;
+ }
+
+ errno = 0;
+ unsigned long int uid = strtoul(argv[2], NULL, 0);
+ if (errno || uid > UID_MAX) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError, "Invalid UID", false);
+ return 0;
+ }
+
+ StrictPenalty penalty = parsePenalty(argv[3]);
+ if (penalty == INVALID) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError, "Invalid penalty argument", false);
+ return 0;
+ }
+
+ int res = sStrictCtrl->setUidCleartextPenalty((uid_t) uid, penalty);
+ return sendGenericOkFail(cli, res);
+ }
+
+ cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown command", false);
+ return 0;
+}
+
CommandListener::NetworkCommand::NetworkCommand() : NetdCommand("network") {
}
if (nextArg == argc) {
return syntaxError(client, "Missing id");
}
+
+ bool userPermissions = !strcmp(argv[2], "user");
+ bool networkPermissions = !strcmp(argv[2], "network");
+ if (!userPermissions && !networkPermissions) {
+ 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");
+ if (userPermissions) {
+ char* endPtr;
+ unsigned id = strtoul(argv[nextArg], &endPtr, 0);
+ if (!*argv[nextArg] || *endPtr) {
+ return syntaxError(client, "Invalid id");
+ }
+ ids.push_back(id);
+ } else {
+ // networkPermissions
+ ids.push_back(stringToNetId(argv[nextArg]));
}
- ids.push_back(id);
}
- if (!strcmp(argv[2], "user")) {
+ if (userPermissions) {
sNetCtrl->setPermissionForUsers(permission, ids);
- } else if (!strcmp(argv[2], "network")) {
+ } else {
+ // networkPermissions
if (int ret = sNetCtrl->setPermissionForNetworks(permission, ids)) {
return operationError(client, "setPermissionForNetworks() failed", ret);
}
- } else {
- return syntaxError(client, "Unknown argument");
}
+
return success(client);
}