/* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // #define LOG_NDEBUG 0 #include #include #include #include #include #include #include #include #include #include #include #define __STDC_FORMAT_MACROS 1 #include #define LOG_TAG "CommandListener" #include #include #include #include "Controllers.h" #include "CommandListener.h" #include "ResponseCode.h" #include "BandwidthController.h" #include "IdletimerController.h" #include "InterfaceController.h" #include "oem_iptables_hook.h" #include "NetdConstants.h" #include "FirewallController.h" #include "RouteController.h" #include "UidRanges.h" #include #include using android::net::gCtls; namespace { const unsigned NUM_OEM_IDS = NetworkController::MAX_OEM_ID - NetworkController::MIN_OEM_ID + 1; Permission stringToPermission(const char* arg) { if (!strcmp(arg, "NETWORK")) { return PERMISSION_NETWORK; } if (!strcmp(arg, "SYSTEM")) { return PERMISSION_SYSTEM; } return PERMISSION_NONE; } unsigned stringToNetId(const char* arg) { if (!strcmp(arg, "local")) { return NetworkController::LOCAL_NET_ID; } // OEM NetIds are "oem1", "oem2", .., "oem50". if (!strncmp(arg, "oem", 3)) { unsigned n = strtoul(arg + 3, NULL, 0); if (1 <= n && n <= NUM_OEM_IDS) { return NetworkController::MIN_OEM_ID + n; } return NETID_UNSET; } // strtoul() returns 0 on errors, which is fine because 0 is an invalid netId. return strtoul(arg, NULL, 0); } class LockingFrameworkCommand : public FrameworkCommand { public: LockingFrameworkCommand(FrameworkCommand *wrappedCmd, android::RWLock& lock) : FrameworkCommand(wrappedCmd->getCommand()), mWrappedCmd(wrappedCmd), mLock(lock) {} int runCommand(SocketClient *c, int argc, char **argv) { android::RWLock::AutoWLock lock(mLock); return mWrappedCmd->runCommand(c, argc, argv); } private: FrameworkCommand *mWrappedCmd; android::RWLock& mLock; }; } // namespace /** * List of module chains to be created, along with explicit ordering. ORDERING * IS CRITICAL, AND SHOULD BE TRIPLE-CHECKED WITH EACH CHANGE. */ static const char* FILTER_INPUT[] = { // Bandwidth should always be early in input chain, to make sure we // correctly count incoming traffic against data plan. BandwidthController::LOCAL_INPUT, FirewallController::LOCAL_INPUT, NULL, }; static const char* FILTER_FORWARD[] = { OEM_IPTABLES_FILTER_FORWARD, FirewallController::LOCAL_FORWARD, BandwidthController::LOCAL_FORWARD, NatController::LOCAL_FORWARD, NULL, }; static const char* FILTER_OUTPUT[] = { OEM_IPTABLES_FILTER_OUTPUT, FirewallController::LOCAL_OUTPUT, StrictController::LOCAL_OUTPUT, BandwidthController::LOCAL_OUTPUT, NULL, }; static const char* RAW_PREROUTING[] = { BandwidthController::LOCAL_RAW_PREROUTING, IdletimerController::LOCAL_RAW_PREROUTING, NatController::LOCAL_RAW_PREROUTING, NULL, }; static const char* MANGLE_POSTROUTING[] = { BandwidthController::LOCAL_MANGLE_POSTROUTING, IdletimerController::LOCAL_MANGLE_POSTROUTING, NULL, }; static const char* MANGLE_FORWARD[] = { NatController::LOCAL_MANGLE_FORWARD, NULL, }; static const char* NAT_PREROUTING[] = { OEM_IPTABLES_NAT_PREROUTING, NULL, }; static const char* NAT_POSTROUTING[] = { NatController::LOCAL_NAT_POSTROUTING, NULL, }; static void createChildChains(IptablesTarget target, const char* table, const char* parentChain, const char** childChains) { const char** childChain = childChains; do { // Order is important: // -D to delete any pre-existing jump rule (removes references // that would prevent -X from working) // -F to flush any existing chain // -X to delete any existing chain // -N to create the chain // -A to append the chain to parent execIptablesSilently(target, "-t", table, "-D", parentChain, "-j", *childChain, NULL); execIptablesSilently(target, "-t", table, "-F", *childChain, NULL); execIptablesSilently(target, "-t", table, "-X", *childChain, NULL); execIptables(target, "-t", table, "-N", *childChain, NULL); execIptables(target, "-t", table, "-A", parentChain, "-j", *childChain, NULL); } while (*(++childChain) != NULL); } void CommandListener::registerLockingCmd(FrameworkCommand *cmd, android::RWLock& lock) { registerCmd(new LockingFrameworkCommand(cmd, lock)); } CommandListener::CommandListener() : FrameworkListener("netd", true) { registerLockingCmd(new InterfaceCmd()); registerLockingCmd(new IpFwdCmd()); registerLockingCmd(new TetherCmd()); registerLockingCmd(new NatCmd()); registerLockingCmd(new ListTtysCmd()); registerLockingCmd(new PppdCmd()); registerLockingCmd(new SoftapCmd()); registerLockingCmd(new BandwidthControlCmd(), gCtls->bandwidthCtrl.lock); registerLockingCmd(new IdletimerControlCmd()); registerLockingCmd(new ResolverCmd()); registerLockingCmd(new FirewallCmd(), gCtls->firewallCtrl.lock); registerLockingCmd(new ClatdCmd()); registerLockingCmd(new NetworkCommand()); registerLockingCmd(new StrictCmd()); /* * This is the only time we touch top-level chains in iptables; controllers * should only mutate rules inside of their children chains, as created by * the constants above. * * Modules should never ACCEPT packets (except in well-justified cases); * they should instead defer to any remaining modules using RETURN, or * otherwise DROP/REJECT. */ // Create chains for children modules createChildChains(V4V6, "filter", "INPUT", FILTER_INPUT); createChildChains(V4V6, "filter", "FORWARD", FILTER_FORWARD); createChildChains(V4V6, "filter", "OUTPUT", FILTER_OUTPUT); createChildChains(V4V6, "raw", "PREROUTING", RAW_PREROUTING); createChildChains(V4V6, "mangle", "POSTROUTING", MANGLE_POSTROUTING); createChildChains(V4V6, "mangle", "FORWARD", MANGLE_FORWARD); createChildChains(V4, "nat", "PREROUTING", NAT_PREROUTING); createChildChains(V4, "nat", "POSTROUTING", NAT_POSTROUTING); // Let each module setup their child chains setupOemIptablesHook(); /* When enabled, DROPs all packets except those matching rules. */ gCtls->firewallCtrl.setupIptablesHooks(); /* Does DROPs in FORWARD by default */ gCtls->natCtrl.setupIptablesHooks(); /* * Does REJECT in INPUT, OUTPUT. Does counting also. * No DROP/REJECT allowed later in netfilter-flow hook order. */ gCtls->bandwidthCtrl.setupIptablesHooks(); /* * Counts in nat: PREROUTING, POSTROUTING. * No DROP/REJECT allowed later in netfilter-flow hook order. */ gCtls->idletimerCtrl.setupIptablesHooks(); gCtls->bandwidthCtrl.enableBandwidthControl(false); if (int ret = RouteController::Init(NetworkController::LOCAL_NET_ID)) { ALOGE("failed to initialize RouteController (%s)", strerror(-ret)); } } CommandListener::InterfaceCmd::InterfaceCmd() : NetdCommand("interface") { } int CommandListener::InterfaceCmd::runCommand(SocketClient *cli, int argc, char **argv) { if (argc < 2) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false); return 0; } if (!strcmp(argv[1], "list")) { DIR *d; struct dirent *de; if (!(d = opendir("/sys/class/net"))) { cli->sendMsg(ResponseCode::OperationFailed, "Failed to open sysfs dir", true); return 0; } while((de = readdir(d))) { if (de->d_name[0] == '.') continue; cli->sendMsg(ResponseCode::InterfaceListResult, de->d_name, false); } closedir(d); cli->sendMsg(ResponseCode::CommandOkay, "Interface list completed", false); return 0; } else { /* * These commands take a minimum of 3 arguments */ if (argc < 3) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false); return 0; } if (!strcmp(argv[1], "getcfg")) { struct in_addr addr; int prefixLength; unsigned char hwaddr[6]; unsigned flags = 0; ifc_init(); memset(hwaddr, 0, sizeof(hwaddr)); if (ifc_get_info(argv[2], &addr.s_addr, &prefixLength, &flags)) { cli->sendMsg(ResponseCode::OperationFailed, "Interface not found", true); ifc_close(); return 0; } if (ifc_get_hwaddr(argv[2], (void *) hwaddr)) { ALOGW("Failed to retrieve HW addr for %s (%s)", argv[2], strerror(errno)); } char *addr_s = strdup(inet_ntoa(addr)); const char *updown, *brdcst, *loopbk, *ppp, *running, *multi; updown = (flags & IFF_UP) ? "up" : "down"; brdcst = (flags & IFF_BROADCAST) ? " broadcast" : ""; loopbk = (flags & IFF_LOOPBACK) ? " loopback" : ""; ppp = (flags & IFF_POINTOPOINT) ? " point-to-point" : ""; running = (flags & IFF_RUNNING) ? " running" : ""; multi = (flags & IFF_MULTICAST) ? " multicast" : ""; char *flag_s; asprintf(&flag_s, "%s%s%s%s%s%s", updown, brdcst, loopbk, ppp, running, multi); char *msg = NULL; asprintf(&msg, "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x %s %d %s", hwaddr[0], hwaddr[1], hwaddr[2], hwaddr[3], hwaddr[4], hwaddr[5], addr_s, prefixLength, flag_s); cli->sendMsg(ResponseCode::InterfaceGetCfgResult, msg, false); free(addr_s); free(flag_s); free(msg); ifc_close(); return 0; } else if (!strcmp(argv[1], "setcfg")) { // arglist: iface [addr prefixLength] flags if (argc < 4) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false); return 0; } ALOGD("Setting iface cfg"); struct in_addr addr; int index = 5; ifc_init(); if (!inet_aton(argv[3], &addr)) { // Handle flags only case index = 3; } else { if (ifc_set_addr(argv[2], 0)) { cli->sendMsg(ResponseCode::OperationFailed, "Failed to clear address", true); ifc_close(); return 0; } if (addr.s_addr != 0) { if (ifc_add_address(argv[2], argv[3], atoi(argv[4]))) { cli->sendMsg(ResponseCode::OperationFailed, "Failed to set address", true); ifc_close(); return 0; } } } /* Process flags */ for (int i = index; i < argc; i++) { char *flag = argv[i]; if (!strcmp(flag, "up")) { ALOGD("Trying to bring up %s", argv[2]); if (ifc_up(argv[2])) { ALOGE("Error upping interface"); cli->sendMsg(ResponseCode::OperationFailed, "Failed to up interface", true); ifc_close(); return 0; } } else if (!strcmp(flag, "down")) { ALOGD("Trying to bring down %s", argv[2]); if (ifc_down(argv[2])) { ALOGE("Error downing interface"); cli->sendMsg(ResponseCode::OperationFailed, "Failed to down interface", true); ifc_close(); return 0; } } else if (!strcmp(flag, "broadcast")) { // currently ignored } else if (!strcmp(flag, "multicast")) { // currently ignored } else if (!strcmp(flag, "running")) { // currently ignored } else if (!strcmp(flag, "loopback")) { // currently ignored } else if (!strcmp(flag, "point-to-point")) { // currently ignored } else { cli->sendMsg(ResponseCode::CommandParameterError, "Flag unsupported", false); ifc_close(); return 0; } } cli->sendMsg(ResponseCode::CommandOkay, "Interface configuration set", false); ifc_close(); return 0; } else if (!strcmp(argv[1], "clearaddrs")) { // arglist: iface ALOGD("Clearing all IP addresses on %s", argv[2]); ifc_clear_addresses(argv[2]); cli->sendMsg(ResponseCode::CommandOkay, "Interface IP addresses cleared", false); return 0; } else if (!strcmp(argv[1], "ipv6privacyextensions")) { if (argc != 4) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: interface ipv6privacyextensions ", false); return 0; } int enable = !strncmp(argv[3], "enable", 7); if (InterfaceController::setIPv6PrivacyExtensions(argv[2], enable) == 0) { cli->sendMsg(ResponseCode::CommandOkay, "IPv6 privacy extensions changed", false); } else { cli->sendMsg(ResponseCode::OperationFailed, "Failed to set ipv6 privacy extensions", true); } return 0; } else if (!strcmp(argv[1], "ipv6")) { if (argc != 4) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: interface ipv6 ", false); return 0; } int enable = !strncmp(argv[3], "enable", 7); if (InterfaceController::setEnableIPv6(argv[2], enable) == 0) { cli->sendMsg(ResponseCode::CommandOkay, "IPv6 state changed", false); } else { cli->sendMsg(ResponseCode::OperationFailed, "Failed to change IPv6 state", true); } return 0; } else if (!strcmp(argv[1], "ipv6ndoffload")) { if (argc != 4) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: interface ipv6ndoffload ", false); return 0; } int enable = !strncmp(argv[3], "enable", 7); if (InterfaceController::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, "Usage: interface setmtu ", false); return 0; } if (InterfaceController::setMtu(argv[2], argv[3]) == 0) { cli->sendMsg(ResponseCode::CommandOkay, "MTU changed", false); } else { cli->sendMsg(ResponseCode::OperationFailed, "Failed to set MTU", true); } return 0; } else { cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown interface cmd", false); return 0; } } return 0; } CommandListener::ListTtysCmd::ListTtysCmd() : NetdCommand("list_ttys") { } int CommandListener::ListTtysCmd::runCommand(SocketClient *cli, int /* argc */, char ** /* argv */) { TtyCollection *tlist = gCtls->pppCtrl.getTtyList(); TtyCollection::iterator it; for (it = tlist->begin(); it != tlist->end(); ++it) { cli->sendMsg(ResponseCode::TtyListResult, *it, false); } cli->sendMsg(ResponseCode::CommandOkay, "Ttys listed.", false); return 0; } CommandListener::IpFwdCmd::IpFwdCmd() : NetdCommand("ipfwd") { } int CommandListener::IpFwdCmd::runCommand(SocketClient *cli, int argc, char **argv) { bool matched = false; bool success; if (argc == 2) { // 0 1 // ipfwd status if (!strcmp(argv[1], "status")) { char *tmp = NULL; asprintf(&tmp, "Forwarding %s", ((gCtls->tetherCtrl.forwardingRequestCount() > 0) ? "enabled" : "disabled")); cli->sendMsg(ResponseCode::IpFwdStatusResult, tmp, false); free(tmp); return 0; } } else if (argc == 3) { // 0 1 2 // ipfwd enable // ipfwd disable if (!strcmp(argv[1], "enable")) { matched = true; success = gCtls->tetherCtrl.enableForwarding(argv[2]); } else if (!strcmp(argv[1], "disable")) { matched = true; success = gCtls->tetherCtrl.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 (success) { cli->sendMsg(ResponseCode::CommandOkay, "ipfwd operation succeeded", false); } else { cli->sendMsg(ResponseCode::OperationFailed, "ipfwd operation failed", true); } return 0; } CommandListener::TetherCmd::TetherCmd() : NetdCommand("tether") { } int CommandListener::TetherCmd::runCommand(SocketClient *cli, int argc, char **argv) { int rc = 0; if (argc < 2) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false); return 0; } if (!strcmp(argv[1], "stop")) { rc = gCtls->tetherCtrl.stopTethering(); } else if (!strcmp(argv[1], "status")) { char *tmp = NULL; asprintf(&tmp, "Tethering services %s", (gCtls->tetherCtrl.isTetheringStarted() ? "started" : "stopped")); cli->sendMsg(ResponseCode::TetherStatusResult, tmp, false); free(tmp); return 0; } else if (argc == 3) { if (!strcmp(argv[1], "interface") && !strcmp(argv[2], "list")) { for (const auto &ifname : gCtls->tetherCtrl.getTetheredInterfaceList()) { cli->sendMsg(ResponseCode::TetherInterfaceListResult, ifname.c_str(), false); } } else if (!strcmp(argv[1], "dns") && !strcmp(argv[2], "list")) { char netIdStr[UINT32_STRLEN]; snprintf(netIdStr, sizeof(netIdStr), "%u", gCtls->tetherCtrl.getDnsNetId()); cli->sendMsg(ResponseCode::TetherDnsFwdNetIdResult, netIdStr, false); for (const auto &fwdr : gCtls->tetherCtrl.getDnsForwarders()) { cli->sendMsg(ResponseCode::TetherDnsFwdTgtListResult, fwdr.c_str(), false); } } } else { /* * These commands take a minimum of 4 arguments */ if (argc < 4) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false); return 0; } if (!strcmp(argv[1], "start")) { if (argc % 2 == 1) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Bad number of arguments", false); return 0; } 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); return 0; } } rc = gCtls->tetherCtrl.startTethering(num_addrs, &(argv[2])); } else if (!strcmp(argv[1], "interface")) { if (!strcmp(argv[2], "add")) { rc = gCtls->tetherCtrl.tetherInterface(argv[3]); } else if (!strcmp(argv[2], "remove")) { rc = gCtls->tetherCtrl.untetherInterface(argv[3]); /* else if (!strcmp(argv[2], "list")) handled above */ } else { cli->sendMsg(ResponseCode::CommandParameterError, "Unknown tether interface operation", false); return 0; } } else if (!strcmp(argv[1], "dns")) { if (!strcmp(argv[2], "set")) { if (argc < 5) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false); return 0; } unsigned netId = stringToNetId(argv[3]); rc = gCtls->tetherCtrl.setDnsForwarders(netId, &argv[4], argc - 4); /* else if (!strcmp(argv[2], "list")) handled above */ } else { cli->sendMsg(ResponseCode::CommandParameterError, "Unknown tether interface operation", false); return 0; } } else { cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown tether cmd", false); return 0; } } if (!rc) { cli->sendMsg(ResponseCode::CommandOkay, "Tether operation succeeded", false); } else { cli->sendMsg(ResponseCode::OperationFailed, "Tether operation failed", true); } return 0; } CommandListener::NatCmd::NatCmd() : NetdCommand("nat") { } int CommandListener::NatCmd::runCommand(SocketClient *cli, int argc, char **argv) { int rc = 0; if (argc < 5) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false); return 0; } // 0 1 2 3 // nat enable intiface extiface // nat disable intiface extiface if (!strcmp(argv[1], "enable") && argc >= 4) { rc = gCtls->natCtrl.enableNat(argv[2], argv[3]); if(!rc) { /* Ignore ifaces for now. */ rc = gCtls->bandwidthCtrl.setGlobalAlertInForwardChain(); } } else if (!strcmp(argv[1], "disable") && argc >= 4) { /* Ignore ifaces for now. */ rc = gCtls->bandwidthCtrl.removeGlobalAlertInForwardChain(); rc |= gCtls->natCtrl.disableNat(argv[2], argv[3]); } else { cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown nat cmd", false); return 0; } if (!rc) { cli->sendMsg(ResponseCode::CommandOkay, "Nat operation succeeded", false); } else { cli->sendMsg(ResponseCode::OperationFailed, "Nat operation failed", true); } return 0; } CommandListener::PppdCmd::PppdCmd() : NetdCommand("pppd") { } int CommandListener::PppdCmd::runCommand(SocketClient *cli, int argc, char **argv) { int rc = 0; if (argc < 3) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false); return 0; } if (!strcmp(argv[1], "attach")) { struct in_addr l, r, dns1, dns2; memset(&dns1, 0, sizeof(struct in_addr)); memset(&dns2, 0, sizeof(struct in_addr)); if (!inet_aton(argv[3], &l)) { cli->sendMsg(ResponseCode::CommandParameterError, "Invalid local address", false); return 0; } if (!inet_aton(argv[4], &r)) { cli->sendMsg(ResponseCode::CommandParameterError, "Invalid remote address", false); return 0; } if ((argc > 3) && (!inet_aton(argv[5], &dns1))) { cli->sendMsg(ResponseCode::CommandParameterError, "Invalid dns1 address", false); return 0; } if ((argc > 4) && (!inet_aton(argv[6], &dns2))) { cli->sendMsg(ResponseCode::CommandParameterError, "Invalid dns2 address", false); return 0; } rc = gCtls->pppCtrl.attachPppd(argv[2], l, r, dns1, dns2); } else if (!strcmp(argv[1], "detach")) { rc = gCtls->pppCtrl.detachPppd(argv[2]); } else { cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown pppd cmd", false); return 0; } if (!rc) { cli->sendMsg(ResponseCode::CommandOkay, "Pppd operation succeeded", false); } else { cli->sendMsg(ResponseCode::OperationFailed, "Pppd operation failed", true); } return 0; } CommandListener::SoftapCmd::SoftapCmd() : NetdCommand("softap") { } int CommandListener::SoftapCmd::runCommand(SocketClient *cli, int argc, char **argv) { int rc = ResponseCode::SoftapStatusResult; char *retbuf = NULL; if (gCtls == nullptr) { cli->sendMsg(ResponseCode::ServiceStartFailed, "SoftAP is not available", false); return -1; } if (argc < 2) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument in a SoftAP command", false); return 0; } if (!strcmp(argv[1], "startap")) { rc = gCtls->softapCtrl.startSoftap(); } else if (!strcmp(argv[1], "stopap")) { rc = gCtls->softapCtrl.stopSoftap(); } else if (!strcmp(argv[1], "fwreload")) { rc = gCtls->softapCtrl.fwReloadSoftap(argc, argv); } else if (!strcmp(argv[1], "status")) { asprintf(&retbuf, "Softap service %s running", (gCtls->softapCtrl.isSoftapStarted() ? "is" : "is not")); cli->sendMsg(rc, retbuf, false); free(retbuf); return 0; } else if (!strcmp(argv[1], "set")) { rc = gCtls->softapCtrl.setSoftap(argc, argv); } else { cli->sendMsg(ResponseCode::CommandSyntaxError, "Unrecognized SoftAP command", false); return 0; } if (rc >= 400 && rc < 600) cli->sendMsg(rc, "SoftAP command has failed", false); else cli->sendMsg(rc, "Ok", false); return 0; } CommandListener::ResolverCmd::ResolverCmd() : NetdCommand("resolver") { } int CommandListener::ResolverCmd::runCommand(SocketClient *cli, int argc, char **margv) { int rc = 0; const char **argv = const_cast(margv); 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")) { if (!parseAndExecuteSetNetDns(netId, argc, argv)) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Wrong number of or invalid arguments to resolver setnetdns", false); return 0; } } else if (!strcmp(argv[1], "clearnetdns")) { // "resolver clearnetdns " if (argc == 3) { rc = gCtls->resolverCtrl.clearDnsServers(netId); } else { cli->sendMsg(ResponseCode::CommandSyntaxError, "Wrong number of arguments to resolver clearnetdns", false); return 0; } } else { cli->sendMsg(ResponseCode::CommandSyntaxError,"Resolver unknown command", false); return 0; } if (!rc) { cli->sendMsg(ResponseCode::CommandOkay, "Resolver command succeeded", false); } else { cli->sendMsg(ResponseCode::OperationFailed, "Resolver command failed", true); } return 0; } bool CommandListener::ResolverCmd::parseAndExecuteSetNetDns(int netId, int argc, const char** argv) { // "resolver setnetdns [ ...] [--params ]" // TODO: This code has to be replaced by a Binder call ASAP if (argc < 5) { return false; } int end = argc; __res_params params; const __res_params* paramsPtr = nullptr; if (end > 6 && !strcmp(argv[end - 2], "--params")) { const char* paramsStr = argv[end - 1]; end -= 2; if (sscanf(paramsStr, "%hu %hhu %hhu %hhu", ¶ms.sample_validity, ¶ms.success_threshold, ¶ms.min_samples, ¶ms.max_samples) != 4) { return false; } paramsPtr = ¶ms; } return gCtls->resolverCtrl.setDnsServers(netId, argv[3], &argv[4], end - 4, paramsPtr) == 0; } CommandListener::BandwidthControlCmd::BandwidthControlCmd() : NetdCommand("bandwidth") { } void CommandListener::BandwidthControlCmd::sendGenericSyntaxError(SocketClient *cli, const char *usageMsg) { char *msg; asprintf(&msg, "Usage: bandwidth %s", usageMsg); cli->sendMsg(ResponseCode::CommandSyntaxError, msg, false); free(msg); } void CommandListener::BandwidthControlCmd::sendGenericOkFail(SocketClient *cli, int cond) { if (!cond) { cli->sendMsg(ResponseCode::CommandOkay, "Bandwidth command succeeeded", false); } else { cli->sendMsg(ResponseCode::OperationFailed, "Bandwidth command failed", false); } } void CommandListener::BandwidthControlCmd::sendGenericOpFailed(SocketClient *cli, const char *errMsg) { cli->sendMsg(ResponseCode::OperationFailed, errMsg, false); } int CommandListener::BandwidthControlCmd::runCommand(SocketClient *cli, int argc, char **argv) { if (argc < 2) { sendGenericSyntaxError(cli, " "); return 0; } ALOGV("bwctrlcmd: argc=%d %s %s ...", argc, argv[0], argv[1]); if (!strcmp(argv[1], "enable")) { int rc = gCtls->bandwidthCtrl.enableBandwidthControl(true); sendGenericOkFail(cli, rc); return 0; } if (!strcmp(argv[1], "disable")) { int rc = gCtls->bandwidthCtrl.disableBandwidthControl(); sendGenericOkFail(cli, rc); return 0; } if (!strcmp(argv[1], "removequota") || !strcmp(argv[1], "rq")) { if (argc != 3) { sendGenericSyntaxError(cli, "removequota "); return 0; } int rc = gCtls->bandwidthCtrl.removeInterfaceSharedQuota(argv[2]); sendGenericOkFail(cli, rc); return 0; } if (!strcmp(argv[1], "getquota") || !strcmp(argv[1], "gq")) { int64_t bytes; if (argc != 2) { sendGenericSyntaxError(cli, "getquota"); return 0; } int rc = gCtls->bandwidthCtrl.getInterfaceSharedQuota(&bytes); if (rc) { sendGenericOpFailed(cli, "Failed to get quota"); return 0; } char *msg; asprintf(&msg, "%" PRId64, bytes); cli->sendMsg(ResponseCode::QuotaCounterResult, msg, false); free(msg); return 0; } if (!strcmp(argv[1], "getiquota") || !strcmp(argv[1], "giq")) { int64_t bytes; if (argc != 3) { sendGenericSyntaxError(cli, "getiquota "); return 0; } int rc = gCtls->bandwidthCtrl.getInterfaceQuota(argv[2], &bytes); if (rc) { sendGenericOpFailed(cli, "Failed to get quota"); return 0; } char *msg; asprintf(&msg, "%" PRId64, bytes); cli->sendMsg(ResponseCode::QuotaCounterResult, msg, false); free(msg); return 0; } if (!strcmp(argv[1], "setquota") || !strcmp(argv[1], "sq")) { if (argc != 4) { sendGenericSyntaxError(cli, "setquota "); return 0; } int rc = gCtls->bandwidthCtrl.setInterfaceSharedQuota(argv[2], atoll(argv[3])); sendGenericOkFail(cli, rc); return 0; } if (!strcmp(argv[1], "setquotas") || !strcmp(argv[1], "sqs")) { int rc; if (argc < 4) { sendGenericSyntaxError(cli, "setquotas ..."); return 0; } for (int q = 3; argc >= 4; q++, argc--) { rc = gCtls->bandwidthCtrl.setInterfaceSharedQuota(argv[q], atoll(argv[2])); if (rc) { char *msg; asprintf(&msg, "bandwidth setquotas %s %s failed", argv[2], argv[q]); cli->sendMsg(ResponseCode::OperationFailed, msg, false); free(msg); return 0; } } sendGenericOkFail(cli, rc); return 0; } if (!strcmp(argv[1], "removequotas") || !strcmp(argv[1], "rqs")) { int rc; if (argc < 3) { sendGenericSyntaxError(cli, "removequotas ..."); return 0; } for (int q = 2; argc >= 3; q++, argc--) { rc = gCtls->bandwidthCtrl.removeInterfaceSharedQuota(argv[q]); if (rc) { char *msg; asprintf(&msg, "bandwidth removequotas %s failed", argv[q]); cli->sendMsg(ResponseCode::OperationFailed, msg, false); free(msg); return 0; } } sendGenericOkFail(cli, rc); return 0; } if (!strcmp(argv[1], "removeiquota") || !strcmp(argv[1], "riq")) { if (argc != 3) { sendGenericSyntaxError(cli, "removeiquota "); return 0; } int rc = gCtls->bandwidthCtrl.removeInterfaceQuota(argv[2]); sendGenericOkFail(cli, rc); return 0; } if (!strcmp(argv[1], "setiquota") || !strcmp(argv[1], "siq")) { if (argc != 4) { sendGenericSyntaxError(cli, "setiquota "); return 0; } int rc = gCtls->bandwidthCtrl.setInterfaceQuota(argv[2], atoll(argv[3])); sendGenericOkFail(cli, rc); return 0; } if (!strcmp(argv[1], "addnaughtyapps") || !strcmp(argv[1], "ana")) { if (argc < 3) { sendGenericSyntaxError(cli, "addnaughtyapps ..."); return 0; } int rc = gCtls->bandwidthCtrl.addNaughtyApps(argc - 2, argv + 2); sendGenericOkFail(cli, rc); return 0; } if (!strcmp(argv[1], "removenaughtyapps") || !strcmp(argv[1], "rna")) { if (argc < 3) { sendGenericSyntaxError(cli, "removenaughtyapps ..."); return 0; } int rc = gCtls->bandwidthCtrl.removeNaughtyApps(argc - 2, argv + 2); sendGenericOkFail(cli, rc); return 0; } if (!strcmp(argv[1], "addniceapps") || !strcmp(argv[1], "aha")) { if (argc < 3) { sendGenericSyntaxError(cli, "addniceapps ..."); return 0; } int rc = gCtls->bandwidthCtrl.addNiceApps(argc - 2, argv + 2); sendGenericOkFail(cli, rc); return 0; } if (!strcmp(argv[1], "removeniceapps") || !strcmp(argv[1], "rha")) { if (argc < 3) { sendGenericSyntaxError(cli, "removeniceapps ..."); return 0; } int rc = gCtls->bandwidthCtrl.removeNiceApps(argc - 2, argv + 2); sendGenericOkFail(cli, rc); return 0; } if (!strcmp(argv[1], "setglobalalert") || !strcmp(argv[1], "sga")) { if (argc != 3) { sendGenericSyntaxError(cli, "setglobalalert "); return 0; } int rc = gCtls->bandwidthCtrl.setGlobalAlert(atoll(argv[2])); sendGenericOkFail(cli, rc); return 0; } if (!strcmp(argv[1], "debugsettetherglobalalert") || !strcmp(argv[1], "dstga")) { if (argc != 4) { sendGenericSyntaxError(cli, "debugsettetherglobalalert "); return 0; } /* We ignore the interfaces for now. */ int rc = gCtls->bandwidthCtrl.setGlobalAlertInForwardChain(); sendGenericOkFail(cli, rc); return 0; } if (!strcmp(argv[1], "removeglobalalert") || !strcmp(argv[1], "rga")) { if (argc != 2) { sendGenericSyntaxError(cli, "removeglobalalert"); return 0; } int rc = gCtls->bandwidthCtrl.removeGlobalAlert(); sendGenericOkFail(cli, rc); return 0; } if (!strcmp(argv[1], "debugremovetetherglobalalert") || !strcmp(argv[1], "drtga")) { if (argc != 4) { sendGenericSyntaxError(cli, "debugremovetetherglobalalert "); return 0; } /* We ignore the interfaces for now. */ int rc = gCtls->bandwidthCtrl.removeGlobalAlertInForwardChain(); sendGenericOkFail(cli, rc); return 0; } if (!strcmp(argv[1], "setsharedalert") || !strcmp(argv[1], "ssa")) { if (argc != 3) { sendGenericSyntaxError(cli, "setsharedalert "); return 0; } int rc = gCtls->bandwidthCtrl.setSharedAlert(atoll(argv[2])); sendGenericOkFail(cli, rc); return 0; } if (!strcmp(argv[1], "removesharedalert") || !strcmp(argv[1], "rsa")) { if (argc != 2) { sendGenericSyntaxError(cli, "removesharedalert"); return 0; } int rc = gCtls->bandwidthCtrl.removeSharedAlert(); sendGenericOkFail(cli, rc); return 0; } if (!strcmp(argv[1], "setinterfacealert") || !strcmp(argv[1], "sia")) { if (argc != 4) { sendGenericSyntaxError(cli, "setinterfacealert "); return 0; } int rc = gCtls->bandwidthCtrl.setInterfaceAlert(argv[2], atoll(argv[3])); sendGenericOkFail(cli, rc); return 0; } if (!strcmp(argv[1], "removeinterfacealert") || !strcmp(argv[1], "ria")) { if (argc != 3) { sendGenericSyntaxError(cli, "removeinterfacealert "); return 0; } int rc = gCtls->bandwidthCtrl.removeInterfaceAlert(argv[2]); sendGenericOkFail(cli, rc); return 0; } if (!strcmp(argv[1], "gettetherstats") || !strcmp(argv[1], "gts")) { BandwidthController::TetherStats tetherStats; std::string extraProcessingInfo = ""; if (argc < 2 || argc > 4) { sendGenericSyntaxError(cli, "gettetherstats [ ]"); return 0; } tetherStats.intIface = argc > 2 ? argv[2] : ""; tetherStats.extIface = argc > 3 ? argv[3] : ""; // No filtering requested and there are no interface pairs to lookup. if (argc <= 2 && gCtls->natCtrl.ifacePairList.empty()) { cli->sendMsg(ResponseCode::CommandOkay, "Tethering stats list completed", false); return 0; } int rc = gCtls->bandwidthCtrl.getTetherStats(cli, tetherStats, extraProcessingInfo); if (rc) { extraProcessingInfo.insert(0, "Failed to get tethering stats.\n"); sendGenericOpFailed(cli, extraProcessingInfo.c_str()); return 0; } return 0; } cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown bandwidth cmd", false); return 0; } CommandListener::IdletimerControlCmd::IdletimerControlCmd() : NetdCommand("idletimer") { } int CommandListener::IdletimerControlCmd::runCommand(SocketClient *cli, int argc, char **argv) { // TODO(ashish): Change the error statements if (argc < 2) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false); return 0; } ALOGV("idletimerctrlcmd: argc=%d %s %s ...", argc, argv[0], argv[1]); if (!strcmp(argv[1], "enable")) { if (0 != gCtls->idletimerCtrl.enableIdletimerControl()) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false); } else { cli->sendMsg(ResponseCode::CommandOkay, "Enable success", false); } return 0; } if (!strcmp(argv[1], "disable")) { if (0 != gCtls->idletimerCtrl.disableIdletimerControl()) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false); } else { cli->sendMsg(ResponseCode::CommandOkay, "Disable success", false); } return 0; } if (!strcmp(argv[1], "add")) { if (argc != 5) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false); return 0; } if(0 != gCtls->idletimerCtrl.addInterfaceIdletimer( argv[2], atoi(argv[3]), argv[4])) { cli->sendMsg(ResponseCode::OperationFailed, "Failed to add interface", false); } else { cli->sendMsg(ResponseCode::CommandOkay, "Add success", false); } return 0; } if (!strcmp(argv[1], "remove")) { if (argc != 5) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false); return 0; } // ashish: fixme timeout if (0 != gCtls->idletimerCtrl.removeInterfaceIdletimer( argv[2], atoi(argv[3]), argv[4])) { cli->sendMsg(ResponseCode::OperationFailed, "Failed to remove interface", false); } else { cli->sendMsg(ResponseCode::CommandOkay, "Remove success", false); } return 0; } cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown idletimer cmd", false); return 0; } CommandListener::FirewallCmd::FirewallCmd() : NetdCommand("firewall") { } int CommandListener::FirewallCmd::sendGenericOkFail(SocketClient *cli, int cond) { if (!cond) { cli->sendMsg(ResponseCode::CommandOkay, "Firewall command succeeded", false); } else { cli->sendMsg(ResponseCode::OperationFailed, "Firewall command failed", false); } return 0; } FirewallRule CommandListener::FirewallCmd::parseRule(const char* arg) { if (!strcmp(arg, "allow")) { return ALLOW; } 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, "powersave")) { return POWERSAVE; } else if (!strcmp(arg, "none")) { return NONE; } else { ALOGE("failed to parse child firewall chain (%s)", arg); return INVALID_CHAIN; } } int CommandListener::FirewallCmd::runCommand(SocketClient *cli, int argc, char **argv) { if (argc < 2) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing command", false); return 0; } if (!strcmp(argv[1], "enable")) { if (argc != 3) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: firewall enable ", false); return 0; } FirewallType firewallType = parseFirewallType(argv[2]); int res = gCtls->firewallCtrl.enableFirewall(firewallType); return sendGenericOkFail(cli, res); } if (!strcmp(argv[1], "disable")) { int res = gCtls->firewallCtrl.disableFirewall(); return sendGenericOkFail(cli, res); } if (!strcmp(argv[1], "is_enabled")) { int res = gCtls->firewallCtrl.isFirewallEnabled(); return sendGenericOkFail(cli, res); } if (!strcmp(argv[1], "set_interface_rule")) { if (argc != 4) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: firewall set_interface_rule ", false); return 0; } const char* iface = argv[2]; FirewallRule rule = parseRule(argv[3]); int res = gCtls->firewallCtrl.setInterfaceRule(iface, rule); return sendGenericOkFail(cli, res); } if (!strcmp(argv[1], "set_egress_source_rule")) { if (argc != 4) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: firewall set_egress_source_rule <192.168.0.1> ", false); return 0; } const char* addr = argv[2]; FirewallRule rule = parseRule(argv[3]); int res = gCtls->firewallCtrl.setEgressSourceRule(addr, rule); return sendGenericOkFail(cli, res); } if (!strcmp(argv[1], "set_egress_dest_rule")) { if (argc != 5) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: firewall set_egress_dest_rule <192.168.0.1> <80> ", false); return 0; } const char* addr = argv[2]; int port = atoi(argv[3]); FirewallRule rule = parseRule(argv[4]); int res = 0; res |= gCtls->firewallCtrl.setEgressDestRule(addr, PROTOCOL_TCP, port, rule); res |= gCtls->firewallCtrl.setEgressDestRule(addr, PROTOCOL_UDP, port, rule); return sendGenericOkFail(cli, res); } if (!strcmp(argv[1], "set_uid_rule")) { if (argc != 5) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: firewall set_uid_rule <1000> ", false); return 0; } ChildChain childChain = parseChildChain(argv[2]); if (childChain == INVALID_CHAIN) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Invalid chain name. Valid names are: ", false); return 0; } int uid = atoi(argv[3]); FirewallRule rule = parseRule(argv[4]); int res = gCtls->firewallCtrl.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 ", false); return 0; } ChildChain childChain = parseChildChain(argv[2]); int res = gCtls->firewallCtrl.enableChildChains(childChain, true); return sendGenericOkFail(cli, res); } if (!strcmp(argv[1], "disable_chain")) { if (argc != 3) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: firewall disable_chain ", false); return 0; } ChildChain childChain = parseChildChain(argv[2]); int res = gCtls->firewallCtrl.enableChildChains(childChain, false); return sendGenericOkFail(cli, res); } cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown command", false); return 0; } CommandListener::ClatdCmd::ClatdCmd() : NetdCommand("clatd") { } int CommandListener::ClatdCmd::runCommand(SocketClient *cli, int argc, char **argv) { int rc = 0; if (argc < 3) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false); return 0; } if (!strcmp(argv[1], "stop")) { rc = gCtls->clatdCtrl.stopClatd(argv[2]); } else if (!strcmp(argv[1], "status")) { char *tmp = NULL; asprintf(&tmp, "Clatd status: %s", (gCtls->clatdCtrl.isClatdStarted(argv[2]) ? "started" : "stopped")); cli->sendMsg(ResponseCode::ClatdStatusResult, tmp, false); free(tmp); return 0; } else if (!strcmp(argv[1], "start")) { rc = gCtls->clatdCtrl.startClatd(argv[2]); } else { cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown clatd cmd", false); return 0; } if (!rc) { cli->sendMsg(ResponseCode::CommandOkay, "Clatd operation succeeded", false); } else { cli->sendMsg(ResponseCode::OperationFailed, "Clatd operation failed", 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 = gCtls->strictCtrl.enableStrict(); return sendGenericOkFail(cli, res); } if (!strcmp(argv[1], "disable")) { int res = gCtls->strictCtrl.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 ", 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 = gCtls->strictCtrl.setUidCleartextPenalty((uid_t) uid, penalty); return sendGenericOkFail(cli, res); } cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown command", false); return 0; } CommandListener::NetworkCommand::NetworkCommand() : NetdCommand("network") { } int CommandListener::NetworkCommand::syntaxError(SocketClient* client, const char* message) { client->sendMsg(ResponseCode::CommandSyntaxError, message, false); return 0; } int CommandListener::NetworkCommand::operationError(SocketClient* client, const char* message, int ret) { errno = -ret; client->sendMsg(ResponseCode::OperationFailed, message, true); return 0; } int CommandListener::NetworkCommand::success(SocketClient* client) { client->sendMsg(ResponseCode::CommandOkay, "success", false); return 0; } int CommandListener::NetworkCommand::runCommand(SocketClient* client, int argc, char** argv) { if (argc < 2) { return syntaxError(client, "Missing argument"); } // 0 1 2 3 4 5 6 7 8 // network route [legacy ] add [nexthop] // network route [legacy ] remove [nexthop] // // nexthop may be either an IPv4/IPv6 address or one of "unreachable" or "throw". if (!strcmp(argv[1], "route")) { if (argc < 6 || argc > 9) { return syntaxError(client, "Incorrect number of arguments"); } int nextArg = 2; bool legacy = false; uid_t uid = 0; if (!strcmp(argv[nextArg], "legacy")) { ++nextArg; legacy = true; uid = strtoul(argv[nextArg++], NULL, 0); } bool add = false; if (!strcmp(argv[nextArg], "add")) { add = true; } else if (strcmp(argv[nextArg], "remove")) { return syntaxError(client, "Unknown argument"); } ++nextArg; if (argc < nextArg + 3 || argc > nextArg + 4) { return syntaxError(client, "Incorrect number of arguments"); } unsigned netId = stringToNetId(argv[nextArg++]); const char* interface = argv[nextArg++]; const char* destination = argv[nextArg++]; const char* nexthop = argc > nextArg ? argv[nextArg] : NULL; int ret; if (add) { ret = gCtls->netCtrl.addRoute(netId, interface, destination, nexthop, legacy, uid); } else { ret = gCtls->netCtrl.removeRoute(netId, interface, destination, nexthop, legacy, uid); } if (ret) { return operationError(client, add ? "addRoute() failed" : "removeRoute() failed", ret); } return success(client); } // 0 1 2 3 4 // network interface add // network interface remove if (!strcmp(argv[1], "interface")) { if (argc != 5) { return syntaxError(client, "Missing argument"); } unsigned netId = stringToNetId(argv[3]); if (!strcmp(argv[2], "add")) { if (int ret = gCtls->netCtrl.addInterfaceToNetwork(netId, argv[4])) { return operationError(client, "addInterfaceToNetwork() failed", ret); } } else if (!strcmp(argv[2], "remove")) { if (int ret = gCtls->netCtrl.removeInterfaceFromNetwork(netId, argv[4])) { return operationError(client, "removeInterfaceFromNetwork() failed", ret); } } else { return syntaxError(client, "Unknown argument"); } return success(client); } // 0 1 2 3 // network create [permission] // // 0 1 2 3 4 5 // network create vpn if (!strcmp(argv[1], "create")) { if (argc < 3) { return syntaxError(client, "Missing argument"); } unsigned netId = stringToNetId(argv[2]); if (argc == 6 && !strcmp(argv[3], "vpn")) { bool hasDns = atoi(argv[4]); bool secure = atoi(argv[5]); if (int ret = gCtls->netCtrl.createVirtualNetwork(netId, hasDns, secure)) { return operationError(client, "createVirtualNetwork() failed", ret); } } else if (argc > 4) { return syntaxError(client, "Unknown trailing argument(s)"); } else { Permission permission = PERMISSION_NONE; if (argc == 4) { permission = stringToPermission(argv[3]); if (permission == PERMISSION_NONE) { return syntaxError(client, "Unknown permission"); } } if (int ret = gCtls->netCtrl.createPhysicalNetwork(netId, permission)) { return operationError(client, "createPhysicalNetwork() failed", ret); } } return success(client); } // 0 1 2 // network destroy if (!strcmp(argv[1], "destroy")) { if (argc != 3) { return syntaxError(client, "Incorrect number of arguments"); } unsigned netId = stringToNetId(argv[2]); if (int ret = gCtls->netCtrl.destroyNetwork(netId)) { return operationError(client, "destroyNetwork() failed", ret); } return success(client); } // 0 1 2 3 // network default set // 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"); } netId = stringToNetId(argv[3]); } else if (strcmp(argv[2], "clear")) { return syntaxError(client, "Unknown argument"); } if (int ret = gCtls->netCtrl.setDefaultNetwork(netId)) { return operationError(client, "setDefaultNetwork() failed", ret); } return success(client); } // 0 1 2 3 4 5 // network permission user set ... // network permission user clear ... // network permission network set ... // network permission network clear ... 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 = stringToPermission(argv[4]); if (permission == PERMISSION_NONE) { return syntaxError(client, "Unknown permission"); } nextArg = 5; } else if (strcmp(argv[3], "clear")) { return syntaxError(client, "Unknown argument"); } 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 ids; for (; nextArg < argc; ++nextArg) { 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])); } } if (userPermissions) { gCtls->netCtrl.setPermissionForUsers(permission, ids); } else { // networkPermissions if (int ret = gCtls->netCtrl.setPermissionForNetworks(permission, ids)) { return operationError(client, "setPermissionForNetworks() failed", ret); } } return success(client); } // 0 1 2 3 4 // network users add [[-]] ... // network users remove [[-]] ... if (!strcmp(argv[1], "users")) { if (argc < 4) { return syntaxError(client, "Missing argument"); } unsigned netId = stringToNetId(argv[3]); UidRanges uidRanges; if (!uidRanges.parseFrom(argc - 4, argv + 4)) { return syntaxError(client, "Invalid UIDs"); } if (!strcmp(argv[2], "add")) { if (int ret = gCtls->netCtrl.addUsersToNetwork(netId, uidRanges)) { return operationError(client, "addUsersToNetwork() failed", ret); } } else if (!strcmp(argv[2], "remove")) { if (int ret = gCtls->netCtrl.removeUsersFromNetwork(netId, uidRanges)) { return operationError(client, "removeUsersFromNetwork() failed", ret); } } else { return syntaxError(client, "Unknown argument"); } return success(client); } // 0 1 2 3 // network protect allow ... // network protect deny ... if (!strcmp(argv[1], "protect")) { if (argc < 4) { return syntaxError(client, "Missing argument"); } std::vector uids; for (int i = 3; i < argc; ++i) { uids.push_back(strtoul(argv[i], NULL, 0)); } if (!strcmp(argv[2], "allow")) { gCtls->netCtrl.allowProtect(uids); } else if (!strcmp(argv[2], "deny")) { gCtls->netCtrl.denyProtect(uids); } else { return syntaxError(client, "Unknown argument"); } return success(client); } return syntaxError(client, "Unknown argument"); }