OSDN Git Service

Allow finer-grained locking, and use it in FirewallCmd.
[android-x86/system-netd.git] / server / CommandListener.cpp
index e03a5fc..1a291b1 100644 (file)
@@ -36,6 +36,7 @@
 #include <netutils/ifc.h>
 #include <sysutils/SocketClient.h>
 
+#include "Controllers.h"
 #include "CommandListener.h"
 #include "ResponseCode.h"
 #include "BandwidthController.h"
 #include <string>
 #include <vector>
 
+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, "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;
@@ -65,23 +70,37 @@ 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);
 }
 
-}  // namespace
+class LockingFrameworkCommand : public FrameworkCommand {
+public:
+    LockingFrameworkCommand(FrameworkCommand *wrappedCmd, android::RWLock& lock) :
+            FrameworkCommand(wrappedCmd->getCommand()),
+            mWrappedCmd(wrappedCmd),
+            mLock(lock) {}
 
-NetworkController *CommandListener::sNetCtrl = NULL;
-TetherController *CommandListener::sTetherCtrl = NULL;
-NatController *CommandListener::sNatCtrl = NULL;
-PppController *CommandListener::sPppCtrl = NULL;
-SoftapController *CommandListener::sSoftapCtrl = NULL;
-BandwidthController * CommandListener::sBandwidthCtrl = NULL;
-IdletimerController * CommandListener::sIdletimerCtrl = NULL;
-InterfaceController *CommandListener::sInterfaceCtrl = NULL;
-ResolverController *CommandListener::sResolverCtrl = NULL;
-FirewallController *CommandListener::sFirewallCtrl = NULL;
-ClatdController *CommandListener::sClatdCtrl = NULL;
+    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
@@ -106,6 +125,7 @@ static const char* FILTER_FORWARD[] = {
 static const char* FILTER_OUTPUT[] = {
         OEM_IPTABLES_FILTER_OUTPUT,
         FirewallController::LOCAL_OUTPUT,
+        StrictController::LOCAL_OUTPUT,
         BandwidthController::LOCAL_OUTPUT,
         NULL,
 };
@@ -122,6 +142,11 @@ static const char* MANGLE_POSTROUTING[] = {
         NULL,
 };
 
+static const char* MANGLE_FORWARD[] = {
+        NatController::LOCAL_MANGLE_FORWARD,
+        NULL,
+};
+
 static const char* NAT_PREROUTING[] = {
         OEM_IPTABLES_NAT_PREROUTING,
         NULL,
@@ -152,44 +177,26 @@ static void createChildChains(IptablesTarget target, const char* table, const ch
     } while (*(++childChain) != NULL);
 }
 
+void CommandListener::registerLockingCmd(FrameworkCommand *cmd, android::RWLock& lock) {
+    registerCmd(new LockingFrameworkCommand(cmd, lock));
+}
+
 CommandListener::CommandListener() :
                  FrameworkListener("netd", true) {
-    registerCmd(new InterfaceCmd());
-    registerCmd(new IpFwdCmd());
-    registerCmd(new TetherCmd());
-    registerCmd(new NatCmd());
-    registerCmd(new ListTtysCmd());
-    registerCmd(new PppdCmd());
-    registerCmd(new SoftapCmd());
-    registerCmd(new BandwidthControlCmd());
-    registerCmd(new IdletimerControlCmd());
-    registerCmd(new ResolverCmd());
-    registerCmd(new FirewallCmd());
-    registerCmd(new ClatdCmd());
-    registerCmd(new NetworkCommand());
-
-    if (!sNetCtrl)
-        sNetCtrl = new NetworkController();
-    if (!sTetherCtrl)
-        sTetherCtrl = new TetherController();
-    if (!sNatCtrl)
-        sNatCtrl = new NatController();
-    if (!sPppCtrl)
-        sPppCtrl = new PppController();
-    if (!sSoftapCtrl)
-        sSoftapCtrl = new SoftapController();
-    if (!sBandwidthCtrl)
-        sBandwidthCtrl = new BandwidthController();
-    if (!sIdletimerCtrl)
-        sIdletimerCtrl = new IdletimerController();
-    if (!sResolverCtrl)
-        sResolverCtrl = new ResolverController();
-    if (!sFirewallCtrl)
-        sFirewallCtrl = new FirewallController();
-    if (!sInterfaceCtrl)
-        sInterfaceCtrl = new InterfaceController();
-    if (!sClatdCtrl)
-        sClatdCtrl = new ClatdController(sNetCtrl);
+    registerLockingCmd(new InterfaceCmd());
+    registerLockingCmd(new IpFwdCmd());
+    registerLockingCmd(new TetherCmd());
+    registerLockingCmd(new NatCmd());
+    registerLockingCmd(new ListTtysCmd());
+    registerLockingCmd(new PppdCmd());
+    registerLockingCmd(new SoftapCmd());
+    registerLockingCmd(new BandwidthControlCmd());
+    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
@@ -207,6 +214,7 @@ CommandListener::CommandListener() :
     createChildChains(V4V6, "filter", "OUTPUT", FILTER_OUTPUT);
     createChildChains(V4V6, "raw", "PREROUTING", RAW_PREROUTING);
     createChildChains(V4V6, "mangle", "POSTROUTING", MANGLE_POSTROUTING);
+    createChildChains(V4, "mangle", "FORWARD", MANGLE_FORWARD);
     createChildChains(V4, "nat", "PREROUTING", NAT_PREROUTING);
     createChildChains(V4, "nat", "POSTROUTING", NAT_POSTROUTING);
 
@@ -214,22 +222,22 @@ CommandListener::CommandListener() :
     setupOemIptablesHook();
 
     /* When enabled, DROPs all packets except those matching rules. */
-    sFirewallCtrl->setupIptablesHooks();
+    gCtls->firewallCtrl.setupIptablesHooks();
 
     /* Does DROPs in FORWARD by default */
-    sNatCtrl->setupIptablesHooks();
+    gCtls->natCtrl.setupIptablesHooks();
     /*
      * Does REJECT in INPUT, OUTPUT. Does counting also.
      * No DROP/REJECT allowed later in netfilter-flow hook order.
      */
-    sBandwidthCtrl->setupIptablesHooks();
+    gCtls->bandwidthCtrl.setupIptablesHooks();
     /*
      * Counts in nat: PREROUTING, POSTROUTING.
      * No DROP/REJECT allowed later in netfilter-flow hook order.
      */
-    sIdletimerCtrl->setupIptablesHooks();
+    gCtls->idletimerCtrl.setupIptablesHooks();
 
-    sBandwidthCtrl->enableBandwidthControl(false);
+    gCtls->bandwidthCtrl.enableBandwidthControl(false);
 
     if (int ret = RouteController::Init(NetworkController::LOCAL_NET_ID)) {
         ALOGE("failed to initialize RouteController (%s)", strerror(-ret));
@@ -336,18 +344,18 @@ int CommandListener::InterfaceCmd::runCommand(SocketClient *cli,
                 // Handle flags only case
                 index = 3;
             } else {
-                if (ifc_set_addr(argv[2], addr.s_addr)) {
-                    cli->sendMsg(ResponseCode::OperationFailed, "Failed to set address", true);
+                if (ifc_set_addr(argv[2], 0)) {
+                    cli->sendMsg(ResponseCode::OperationFailed, "Failed to clear address", true);
                     ifc_close();
                     return 0;
                 }
-
-                // Set prefix length on a non zero address
-                if (addr.s_addr != 0 && ifc_set_prefixLength(argv[2], atoi(argv[4]))) {
-                   cli->sendMsg(ResponseCode::OperationFailed, "Failed to set prefixLength", 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 */
@@ -405,7 +413,7 @@ int CommandListener::InterfaceCmd::runCommand(SocketClient *cli,
                 return 0;
             }
             int enable = !strncmp(argv[3], "enable", 7);
-            if (sInterfaceCtrl->setIPv6PrivacyExtensions(argv[2], enable) == 0) {
+            if (gCtls->interfaceCtrl.setIPv6PrivacyExtensions(argv[2], enable) == 0) {
                 cli->sendMsg(ResponseCode::CommandOkay, "IPv6 privacy extensions changed", false);
             } else {
                 cli->sendMsg(ResponseCode::OperationFailed,
@@ -421,24 +429,39 @@ int CommandListener::InterfaceCmd::runCommand(SocketClient *cli,
             }
 
             int enable = !strncmp(argv[3], "enable", 7);
-            if (sInterfaceCtrl->setEnableIPv6(argv[2], enable) == 0) {
+            if (gCtls->interfaceCtrl.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 <interface> <enable|disable>",
+                        false);
+                return 0;
+            }
+            int enable = !strncmp(argv[3], "enable", 7);
+            if (gCtls->interfaceCtrl.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 <interface> <val>", false);
                 return 0;
             }
-            if (sInterfaceCtrl->setMtu(argv[2], argv[3]) == 0) {
+            if (gCtls->interfaceCtrl.setMtu(argv[2], argv[3]) == 0) {
                 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 {
@@ -456,7 +479,7 @@ CommandListener::ListTtysCmd::ListTtysCmd() :
 
 int CommandListener::ListTtysCmd::runCommand(SocketClient *cli,
                                              int /* argc */, char ** /* argv */) {
-    TtyCollection *tlist = sPppCtrl->getTtyList();
+    TtyCollection *tlist = gCtls->pppCtrl.getTtyList();
     TtyCollection::iterator it;
 
     for (it = tlist->begin(); it != tlist->end(); ++it) {
@@ -471,37 +494,59 @@ CommandListener::IpFwdCmd::IpFwdCmd() :
                  NetdCommand("ipfwd") {
 }
 
-int CommandListener::IpFwdCmd::runCommand(SocketClient *cli,
-                                                      int argc, char **argv) {
-    int rc = 0;
-
-    if (argc < 2) {
-        cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false);
-        return 0;
-    }
+int CommandListener::IpFwdCmd::runCommand(SocketClient *cli, int argc, char **argv) {
+    bool matched = false;
+    bool success;
 
-    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",
+                     ((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  <requester>
+        // ipfwd disable <requester>
+        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 (!rc) {
+    if (success) {
         cli->sendMsg(ResponseCode::CommandOkay, "ipfwd operation succeeded", false);
     } else {
         cli->sendMsg(ResponseCode::OperationFailed, "ipfwd operation failed", true);
     }
-
     return 0;
 }
 
@@ -519,32 +564,29 @@ int CommandListener::TetherCmd::runCommand(SocketClient *cli,
     }
 
     if (!strcmp(argv[1], "stop")) {
-        rc = sTetherCtrl->stopTethering();
+        rc = gCtls->tetherCtrl.stopTethering();
     } else if (!strcmp(argv[1], "status")) {
         char *tmp = NULL;
 
         asprintf(&tmp, "Tethering services %s",
-                 (sTetherCtrl->isTetheringStarted() ? "started" : "stopped"));
+                 (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")) {
-            InterfaceCollection *ilist = sTetherCtrl->getTetheredInterfaceList();
+            InterfaceCollection *ilist = gCtls->tetherCtrl.getTetheredInterfaceList();
             InterfaceCollection::iterator it;
             for (it = ilist->begin(); it != ilist->end(); ++it) {
                 cli->sendMsg(ResponseCode::TetherInterfaceListResult, *it, false);
             }
         } else if (!strcmp(argv[1], "dns") && !strcmp(argv[2], "list")) {
             char netIdStr[UINT32_STRLEN];
-            snprintf(netIdStr, sizeof(netIdStr), "%u", sTetherCtrl->getDnsNetId());
+            snprintf(netIdStr, sizeof(netIdStr), "%u", gCtls->tetherCtrl.getDnsNetId());
             cli->sendMsg(ResponseCode::TetherDnsFwdNetIdResult, netIdStr, false);
 
-            NetAddressCollection *dlist = sTetherCtrl->getDnsForwarders();
-            NetAddressCollection::iterator it;
-
-            for (it = dlist->begin(); it != dlist->end(); ++it) {
-                cli->sendMsg(ResponseCode::TetherDnsFwdTgtListResult, inet_ntoa(*it), false);
+            for (const auto &fwdr : *(gCtls->tetherCtrl.getDnsForwarders())) {
+                cli->sendMsg(ResponseCode::TetherDnsFwdTgtListResult, fwdr.c_str(), false);
             }
         }
     } else {
@@ -562,24 +604,22 @@ int CommandListener::TetherCmd::runCommand(SocketClient *cli,
                 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 = gCtls->tetherCtrl.startTethering(num_addrs, &(argv[2]));
         } else if (!strcmp(argv[1], "interface")) {
             if (!strcmp(argv[2], "add")) {
-                rc = sTetherCtrl->tetherInterface(argv[3]);
+                rc = gCtls->tetherCtrl.tetherInterface(argv[3]);
             } else if (!strcmp(argv[2], "remove")) {
-                rc = sTetherCtrl->untetherInterface(argv[3]);
+                rc = gCtls->tetherCtrl.untetherInterface(argv[3]);
             /* else if (!strcmp(argv[2], "list")) handled above */
             } else {
                 cli->sendMsg(ResponseCode::CommandParameterError,
@@ -593,7 +633,7 @@ int CommandListener::TetherCmd::runCommand(SocketClient *cli,
                     return 0;
                 }
                 unsigned netId = stringToNetId(argv[3]);
-                rc = sTetherCtrl->setDnsForwarders(netId, &argv[4], argc - 4);
+                rc = gCtls->tetherCtrl.setDnsForwarders(netId, &argv[4], argc - 4);
             /* else if (!strcmp(argv[2], "list")) handled above */
             } else {
                 cli->sendMsg(ResponseCode::CommandParameterError,
@@ -632,15 +672,15 @@ int CommandListener::NatCmd::runCommand(SocketClient *cli,
     // nat  enable intiface extiface
     // nat disable intiface extiface
     if (!strcmp(argv[1], "enable") && argc >= 4) {
-        rc = sNatCtrl->enableNat(argv[2], argv[3]);
+        rc = gCtls->natCtrl.enableNat(argv[2], argv[3]);
         if(!rc) {
             /* Ignore ifaces for now. */
-            rc = sBandwidthCtrl->setGlobalAlertInForwardChain();
+            rc = gCtls->bandwidthCtrl.setGlobalAlertInForwardChain();
         }
     } else if (!strcmp(argv[1], "disable") && argc >= 4) {
         /* Ignore ifaces for now. */
-        rc = sBandwidthCtrl->removeGlobalAlertInForwardChain();
-        rc |= sNatCtrl->disableNat(argv[2], argv[3]);
+        rc = gCtls->bandwidthCtrl.removeGlobalAlertInForwardChain();
+        rc |= gCtls->natCtrl.disableNat(argv[2], argv[3]);
     } else {
         cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown nat cmd", false);
         return 0;
@@ -690,9 +730,9 @@ int CommandListener::PppdCmd::runCommand(SocketClient *cli,
             cli->sendMsg(ResponseCode::CommandParameterError, "Invalid dns2 address", false);
             return 0;
         }
-        rc = sPppCtrl->attachPppd(argv[2], l, r, dns1, dns2);
+        rc = gCtls->pppCtrl.attachPppd(argv[2], l, r, dns1, dns2);
     } else if (!strcmp(argv[1], "detach")) {
-        rc = sPppCtrl->detachPppd(argv[2]);
+        rc = gCtls->pppCtrl.detachPppd(argv[2]);
     } else {
         cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown pppd cmd", false);
         return 0;
@@ -716,7 +756,7 @@ int CommandListener::SoftapCmd::runCommand(SocketClient *cli,
     int rc = ResponseCode::SoftapStatusResult;
     char *retbuf = NULL;
 
-    if (sSoftapCtrl == NULL) {
+    if (gCtls == nullptr) {
       cli->sendMsg(ResponseCode::ServiceStartFailed, "SoftAP is not available", false);
       return -1;
     }
@@ -727,19 +767,19 @@ int CommandListener::SoftapCmd::runCommand(SocketClient *cli,
     }
 
     if (!strcmp(argv[1], "startap")) {
-        rc = sSoftapCtrl->startSoftap();
+        rc = gCtls->softapCtrl.startSoftap();
     } else if (!strcmp(argv[1], "stopap")) {
-        rc = sSoftapCtrl->stopSoftap();
+        rc = gCtls->softapCtrl.stopSoftap();
     } else if (!strcmp(argv[1], "fwreload")) {
-        rc = sSoftapCtrl->fwReloadSoftap(argc, argv);
+        rc = gCtls->softapCtrl.fwReloadSoftap(argc, argv);
     } else if (!strcmp(argv[1], "status")) {
         asprintf(&retbuf, "Softap service %s running",
-                 (sSoftapCtrl->isSoftapStarted() ? "is" : "is not"));
+                 (gCtls->softapCtrl.isSoftapStarted() ? "is" : "is not"));
         cli->sendMsg(rc, retbuf, false);
         free(retbuf);
         return 0;
     } else if (!strcmp(argv[1], "set")) {
-        rc = sSoftapCtrl->setSoftap(argc, argv);
+        rc = gCtls->softapCtrl.setSoftap(argc, argv);
     } else {
         cli->sendMsg(ResponseCode::CommandSyntaxError, "Unrecognized SoftAP command", false);
         return 0;
@@ -761,23 +801,35 @@ int CommandListener::ResolverCmd::runCommand(SocketClient *cli, int argc, char *
     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 = gCtls->resolverCtrl.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 = gCtls->resolverCtrl.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 = gCtls->resolverCtrl.flushDnsCache(netId);
         } else {
             cli->sendMsg(ResponseCode::CommandSyntaxError,
                     "Wrong number of arguments to resolver flushnet", false);
@@ -829,13 +881,13 @@ int CommandListener::BandwidthControlCmd::runCommand(SocketClient *cli, int argc
     ALOGV("bwctrlcmd: argc=%d %s %s ...", argc, argv[0], argv[1]);
 
     if (!strcmp(argv[1], "enable")) {
-        int rc = sBandwidthCtrl->enableBandwidthControl(true);
+        int rc = gCtls->bandwidthCtrl.enableBandwidthControl(true);
         sendGenericOkFail(cli, rc);
         return 0;
 
     }
     if (!strcmp(argv[1], "disable")) {
-        int rc = sBandwidthCtrl->disableBandwidthControl();
+        int rc = gCtls->bandwidthCtrl.disableBandwidthControl();
         sendGenericOkFail(cli, rc);
         return 0;
 
@@ -845,7 +897,7 @@ int CommandListener::BandwidthControlCmd::runCommand(SocketClient *cli, int argc
             sendGenericSyntaxError(cli, "removequota <interface>");
             return 0;
         }
-        int rc = sBandwidthCtrl->removeInterfaceSharedQuota(argv[2]);
+        int rc = gCtls->bandwidthCtrl.removeInterfaceSharedQuota(argv[2]);
         sendGenericOkFail(cli, rc);
         return 0;
 
@@ -856,7 +908,7 @@ int CommandListener::BandwidthControlCmd::runCommand(SocketClient *cli, int argc
             sendGenericSyntaxError(cli, "getquota");
             return 0;
         }
-        int rc = sBandwidthCtrl->getInterfaceSharedQuota(&bytes);
+        int rc = gCtls->bandwidthCtrl.getInterfaceSharedQuota(&bytes);
         if (rc) {
             sendGenericOpFailed(cli, "Failed to get quota");
             return 0;
@@ -876,7 +928,7 @@ int CommandListener::BandwidthControlCmd::runCommand(SocketClient *cli, int argc
             return 0;
         }
 
-        int rc = sBandwidthCtrl->getInterfaceQuota(argv[2], &bytes);
+        int rc = gCtls->bandwidthCtrl.getInterfaceQuota(argv[2], &bytes);
         if (rc) {
             sendGenericOpFailed(cli, "Failed to get quota");
             return 0;
@@ -893,7 +945,7 @@ int CommandListener::BandwidthControlCmd::runCommand(SocketClient *cli, int argc
             sendGenericSyntaxError(cli, "setquota <interface> <bytes>");
             return 0;
         }
-        int rc = sBandwidthCtrl->setInterfaceSharedQuota(argv[2], atoll(argv[3]));
+        int rc = gCtls->bandwidthCtrl.setInterfaceSharedQuota(argv[2], atoll(argv[3]));
         sendGenericOkFail(cli, rc);
         return 0;
     }
@@ -905,7 +957,7 @@ int CommandListener::BandwidthControlCmd::runCommand(SocketClient *cli, int argc
         }
 
         for (int q = 3; argc >= 4; q++, argc--) {
-            rc = sBandwidthCtrl->setInterfaceSharedQuota(argv[q], atoll(argv[2]));
+            rc = gCtls->bandwidthCtrl.setInterfaceSharedQuota(argv[q], atoll(argv[2]));
             if (rc) {
                 char *msg;
                 asprintf(&msg, "bandwidth setquotas %s %s failed", argv[2], argv[q]);
@@ -927,7 +979,7 @@ int CommandListener::BandwidthControlCmd::runCommand(SocketClient *cli, int argc
         }
 
         for (int q = 2; argc >= 3; q++, argc--) {
-            rc = sBandwidthCtrl->removeInterfaceSharedQuota(argv[q]);
+            rc = gCtls->bandwidthCtrl.removeInterfaceSharedQuota(argv[q]);
             if (rc) {
                 char *msg;
                 asprintf(&msg, "bandwidth removequotas %s failed", argv[q]);
@@ -946,7 +998,7 @@ int CommandListener::BandwidthControlCmd::runCommand(SocketClient *cli, int argc
             sendGenericSyntaxError(cli, "removeiquota <interface>");
             return 0;
         }
-        int rc = sBandwidthCtrl->removeInterfaceQuota(argv[2]);
+        int rc = gCtls->bandwidthCtrl.removeInterfaceQuota(argv[2]);
         sendGenericOkFail(cli, rc);
         return 0;
 
@@ -956,7 +1008,7 @@ int CommandListener::BandwidthControlCmd::runCommand(SocketClient *cli, int argc
             sendGenericSyntaxError(cli, "setiquota <interface> <bytes>");
             return 0;
         }
-        int rc = sBandwidthCtrl->setInterfaceQuota(argv[2], atoll(argv[3]));
+        int rc = gCtls->bandwidthCtrl.setInterfaceQuota(argv[2], atoll(argv[3]));
         sendGenericOkFail(cli, rc);
         return 0;
 
@@ -966,7 +1018,7 @@ int CommandListener::BandwidthControlCmd::runCommand(SocketClient *cli, int argc
             sendGenericSyntaxError(cli, "addnaughtyapps <appUid> ...");
             return 0;
         }
-        int rc = sBandwidthCtrl->addNaughtyApps(argc - 2, argv + 2);
+        int rc = gCtls->bandwidthCtrl.addNaughtyApps(argc - 2, argv + 2);
         sendGenericOkFail(cli, rc);
         return 0;
 
@@ -977,7 +1029,7 @@ int CommandListener::BandwidthControlCmd::runCommand(SocketClient *cli, int argc
             sendGenericSyntaxError(cli, "removenaughtyapps <appUid> ...");
             return 0;
         }
-        int rc = sBandwidthCtrl->removeNaughtyApps(argc - 2, argv + 2);
+        int rc = gCtls->bandwidthCtrl.removeNaughtyApps(argc - 2, argv + 2);
         sendGenericOkFail(cli, rc);
         return 0;
     }
@@ -987,13 +1039,13 @@ int CommandListener::BandwidthControlCmd::runCommand(SocketClient *cli, int argc
             return 0;
         }
         if (!strcmp(argv[2], "enable")) {
-            int rc = sBandwidthCtrl->enableHappyBox();
+            int rc = gCtls->bandwidthCtrl.enableHappyBox();
             sendGenericOkFail(cli, rc);
             return 0;
 
         }
         if (!strcmp(argv[2], "disable")) {
-            int rc = sBandwidthCtrl->disableHappyBox();
+            int rc = gCtls->bandwidthCtrl.disableHappyBox();
             sendGenericOkFail(cli, rc);
             return 0;
         }
@@ -1005,7 +1057,7 @@ int CommandListener::BandwidthControlCmd::runCommand(SocketClient *cli, int argc
             sendGenericSyntaxError(cli, "addniceapps <appUid> ...");
             return 0;
         }
-        int rc = sBandwidthCtrl->addNiceApps(argc - 2, argv + 2);
+        int rc = gCtls->bandwidthCtrl.addNiceApps(argc - 2, argv + 2);
         sendGenericOkFail(cli, rc);
         return 0;
     }
@@ -1014,7 +1066,7 @@ int CommandListener::BandwidthControlCmd::runCommand(SocketClient *cli, int argc
             sendGenericSyntaxError(cli, "removeniceapps <appUid> ...");
             return 0;
         }
-        int rc = sBandwidthCtrl->removeNiceApps(argc - 2, argv + 2);
+        int rc = gCtls->bandwidthCtrl.removeNiceApps(argc - 2, argv + 2);
         sendGenericOkFail(cli, rc);
         return 0;
     }
@@ -1023,7 +1075,7 @@ int CommandListener::BandwidthControlCmd::runCommand(SocketClient *cli, int argc
             sendGenericSyntaxError(cli, "setglobalalert <bytes>");
             return 0;
         }
-        int rc = sBandwidthCtrl->setGlobalAlert(atoll(argv[2]));
+        int rc = gCtls->bandwidthCtrl.setGlobalAlert(atoll(argv[2]));
         sendGenericOkFail(cli, rc);
         return 0;
     }
@@ -1033,7 +1085,7 @@ int CommandListener::BandwidthControlCmd::runCommand(SocketClient *cli, int argc
             return 0;
         }
         /* We ignore the interfaces for now. */
-        int rc = sBandwidthCtrl->setGlobalAlertInForwardChain();
+        int rc = gCtls->bandwidthCtrl.setGlobalAlertInForwardChain();
         sendGenericOkFail(cli, rc);
         return 0;
 
@@ -1043,7 +1095,7 @@ int CommandListener::BandwidthControlCmd::runCommand(SocketClient *cli, int argc
             sendGenericSyntaxError(cli, "removeglobalalert");
             return 0;
         }
-        int rc = sBandwidthCtrl->removeGlobalAlert();
+        int rc = gCtls->bandwidthCtrl.removeGlobalAlert();
         sendGenericOkFail(cli, rc);
         return 0;
 
@@ -1054,7 +1106,7 @@ int CommandListener::BandwidthControlCmd::runCommand(SocketClient *cli, int argc
             return 0;
         }
         /* We ignore the interfaces for now. */
-        int rc = sBandwidthCtrl->removeGlobalAlertInForwardChain();
+        int rc = gCtls->bandwidthCtrl.removeGlobalAlertInForwardChain();
         sendGenericOkFail(cli, rc);
         return 0;
 
@@ -1064,7 +1116,7 @@ int CommandListener::BandwidthControlCmd::runCommand(SocketClient *cli, int argc
             sendGenericSyntaxError(cli, "setsharedalert <bytes>");
             return 0;
         }
-        int rc = sBandwidthCtrl->setSharedAlert(atoll(argv[2]));
+        int rc = gCtls->bandwidthCtrl.setSharedAlert(atoll(argv[2]));
         sendGenericOkFail(cli, rc);
         return 0;
 
@@ -1074,7 +1126,7 @@ int CommandListener::BandwidthControlCmd::runCommand(SocketClient *cli, int argc
             sendGenericSyntaxError(cli, "removesharedalert");
             return 0;
         }
-        int rc = sBandwidthCtrl->removeSharedAlert();
+        int rc = gCtls->bandwidthCtrl.removeSharedAlert();
         sendGenericOkFail(cli, rc);
         return 0;
 
@@ -1084,7 +1136,7 @@ int CommandListener::BandwidthControlCmd::runCommand(SocketClient *cli, int argc
             sendGenericSyntaxError(cli, "setinterfacealert <interface> <bytes>");
             return 0;
         }
-        int rc = sBandwidthCtrl->setInterfaceAlert(argv[2], atoll(argv[3]));
+        int rc = gCtls->bandwidthCtrl.setInterfaceAlert(argv[2], atoll(argv[3]));
         sendGenericOkFail(cli, rc);
         return 0;
 
@@ -1094,7 +1146,7 @@ int CommandListener::BandwidthControlCmd::runCommand(SocketClient *cli, int argc
             sendGenericSyntaxError(cli, "removeinterfacealert <interface>");
             return 0;
         }
-        int rc = sBandwidthCtrl->removeInterfaceAlert(argv[2]);
+        int rc = gCtls->bandwidthCtrl.removeInterfaceAlert(argv[2]);
         sendGenericOkFail(cli, rc);
         return 0;
 
@@ -1109,11 +1161,11 @@ int CommandListener::BandwidthControlCmd::runCommand(SocketClient *cli, int argc
         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 && sNatCtrl->ifacePairList.empty()) {
+        if (argc <= 2 && gCtls->natCtrl.ifacePairList.empty()) {
             cli->sendMsg(ResponseCode::CommandOkay, "Tethering stats list completed", false);
             return 0;
         }
-        int rc = sBandwidthCtrl->getTetherStats(cli, tetherStats, extraProcessingInfo);
+        int rc = gCtls->bandwidthCtrl.getTetherStats(cli, tetherStats, extraProcessingInfo);
         if (rc) {
                 extraProcessingInfo.insert(0, "Failed to get tethering stats.\n");
                 sendGenericOpFailed(cli, extraProcessingInfo.c_str());
@@ -1141,7 +1193,7 @@ int CommandListener::IdletimerControlCmd::runCommand(SocketClient *cli, int argc
     ALOGV("idletimerctrlcmd: argc=%d %s %s ...", argc, argv[0], argv[1]);
 
     if (!strcmp(argv[1], "enable")) {
-      if (0 != sIdletimerCtrl->enableIdletimerControl()) {
+      if (0 != gCtls->idletimerCtrl.enableIdletimerControl()) {
         cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false);
       } else {
         cli->sendMsg(ResponseCode::CommandOkay, "Enable success", false);
@@ -1150,7 +1202,7 @@ int CommandListener::IdletimerControlCmd::runCommand(SocketClient *cli, int argc
 
     }
     if (!strcmp(argv[1], "disable")) {
-      if (0 != sIdletimerCtrl->disableIdletimerControl()) {
+      if (0 != gCtls->idletimerCtrl.disableIdletimerControl()) {
         cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false);
       } else {
         cli->sendMsg(ResponseCode::CommandOkay, "Disable success", false);
@@ -1162,7 +1214,7 @@ int CommandListener::IdletimerControlCmd::runCommand(SocketClient *cli, int argc
             cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false);
             return 0;
         }
-        if(0 != sIdletimerCtrl->addInterfaceIdletimer(
+        if(0 != gCtls->idletimerCtrl.addInterfaceIdletimer(
                                         argv[2], atoi(argv[3]), argv[4])) {
           cli->sendMsg(ResponseCode::OperationFailed, "Failed to add interface", false);
         } else {
@@ -1176,7 +1228,7 @@ int CommandListener::IdletimerControlCmd::runCommand(SocketClient *cli, int argc
             return 0;
         }
         // ashish: fixme timeout
-        if (0 != sIdletimerCtrl->removeInterfaceIdletimer(
+        if (0 != gCtls->idletimerCtrl.removeInterfaceIdletimer(
                                         argv[2], atoi(argv[3]), argv[4])) {
           cli->sendMsg(ResponseCode::OperationFailed, "Failed to remove interface", false);
         } else {
@@ -1205,8 +1257,35 @@ int CommandListener::FirewallCmd::sendGenericOkFail(SocketClient *cli, int cond)
 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;
     }
 }
 
@@ -1218,15 +1297,22 @@ int CommandListener::FirewallCmd::runCommand(SocketClient *cli, int argc,
     }
 
     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 = gCtls->firewallCtrl.enableFirewall(firewallType);
         return sendGenericOkFail(cli, res);
     }
     if (!strcmp(argv[1], "disable")) {
-        int res = sFirewallCtrl->disableFirewall();
+        int res = gCtls->firewallCtrl.disableFirewall();
         return sendGenericOkFail(cli, res);
     }
     if (!strcmp(argv[1], "is_enabled")) {
-        int res = sFirewallCtrl->isFirewallEnabled();
+        int res = gCtls->firewallCtrl.isFirewallEnabled();
         return sendGenericOkFail(cli, res);
     }
 
@@ -1240,7 +1326,7 @@ int CommandListener::FirewallCmd::runCommand(SocketClient *cli, int argc,
         const char* iface = argv[2];
         FirewallRule rule = parseRule(argv[3]);
 
-        int res = sFirewallCtrl->setInterfaceRule(iface, rule);
+        int res = gCtls->firewallCtrl.setInterfaceRule(iface, rule);
         return sendGenericOkFail(cli, res);
     }
 
@@ -1255,7 +1341,7 @@ int CommandListener::FirewallCmd::runCommand(SocketClient *cli, int argc,
         const char* addr = argv[2];
         FirewallRule rule = parseRule(argv[3]);
 
-        int res = sFirewallCtrl->setEgressSourceRule(addr, rule);
+        int res = gCtls->firewallCtrl.setEgressSourceRule(addr, rule);
         return sendGenericOkFail(cli, res);
     }
 
@@ -1272,23 +1358,55 @@ int CommandListener::FirewallCmd::runCommand(SocketClient *cli, int argc,
         FirewallRule rule = parseRule(argv[4]);
 
         int res = 0;
-        res |= sFirewallCtrl->setEgressDestRule(addr, PROTOCOL_TCP, port, rule);
-        res |= sFirewallCtrl->setEgressDestRule(addr, PROTOCOL_UDP, port, rule);
+        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 != 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 = 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 <dozable|standby>",
+                         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 <dozable|standby>",
+                         false);
+            return 0;
+        }
 
-        int res = sFirewallCtrl->setUidRule(uid, rule);
+        ChildChain childChain = parseChildChain(argv[2]);
+        int res = gCtls->firewallCtrl.enableChildChains(childChain, false);
         return sendGenericOkFail(cli, res);
     }
 
@@ -1302,27 +1420,22 @@ CommandListener::ClatdCmd::ClatdCmd() : NetdCommand("clatd") {
 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 = gCtls->clatdCtrl.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", (gCtls->clatdCtrl.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;
-        }
-        rc = sClatdCtrl->startClatd(argv[2]);
+    } else if (!strcmp(argv[1], "start")) {
+        rc = gCtls->clatdCtrl.startClatd(argv[2]);
     } else {
         cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown clatd cmd", false);
         return 0;
@@ -1337,6 +1450,76 @@ int CommandListener::ClatdCmd::runCommand(SocketClient *cli, int argc,
     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 <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 = 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") {
 }
 
@@ -1362,9 +1545,11 @@ int CommandListener::NetworkCommand::runCommand(SocketClient* client, int argc,
         return syntaxError(client, "Missing argument");
     }
 
-    //    0      1      2      3      4       5         6            7               8
-    // network route [legacy <uid>]  add   <netId> <interface> <destination> [nexthop|"unreachable"]
-    // network route [legacy <uid>] remove <netId> <interface> <destination> [nexthop|"unreachable"]
+    //    0      1      2      3      4       5         6            7           8
+    // network route [legacy <uid>]  add   <netId> <interface> <destination> [nexthop]
+    // network route [legacy <uid>] remove <netId> <interface> <destination> [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");
@@ -1398,9 +1583,9 @@ int CommandListener::NetworkCommand::runCommand(SocketClient* client, int argc,
 
         int ret;
         if (add) {
-            ret = sNetCtrl->addRoute(netId, interface, destination, nexthop, legacy, uid);
+            ret = gCtls->netCtrl.addRoute(netId, interface, destination, nexthop, legacy, uid);
         } else {
-            ret = sNetCtrl->removeRoute(netId, interface, destination, nexthop, legacy, uid);
+            ret = gCtls->netCtrl.removeRoute(netId, interface, destination, nexthop, legacy, uid);
         }
         if (ret) {
             return operationError(client, add ? "addRoute() failed" : "removeRoute() failed", ret);
@@ -1418,11 +1603,11 @@ int CommandListener::NetworkCommand::runCommand(SocketClient* client, int argc,
         }
         unsigned netId = stringToNetId(argv[3]);
         if (!strcmp(argv[2], "add")) {
-            if (int ret = sNetCtrl->addInterfaceToNetwork(netId, argv[4])) {
+            if (int ret = gCtls->netCtrl.addInterfaceToNetwork(netId, argv[4])) {
                 return operationError(client, "addInterfaceToNetwork() failed", ret);
             }
         } else if (!strcmp(argv[2], "remove")) {
-            if (int ret = sNetCtrl->removeInterfaceFromNetwork(netId, argv[4])) {
+            if (int ret = gCtls->netCtrl.removeInterfaceFromNetwork(netId, argv[4])) {
                 return operationError(client, "removeInterfaceFromNetwork() failed", ret);
             }
         } else {
@@ -1444,7 +1629,7 @@ int CommandListener::NetworkCommand::runCommand(SocketClient* client, int argc,
         if (argc == 6 && !strcmp(argv[3], "vpn")) {
             bool hasDns = atoi(argv[4]);
             bool secure = atoi(argv[5]);
-            if (int ret = sNetCtrl->createVirtualNetwork(netId, hasDns, secure)) {
+            if (int ret = gCtls->netCtrl.createVirtualNetwork(netId, hasDns, secure)) {
                 return operationError(client, "createVirtualNetwork() failed", ret);
             }
         } else if (argc > 4) {
@@ -1457,7 +1642,7 @@ int CommandListener::NetworkCommand::runCommand(SocketClient* client, int argc,
                     return syntaxError(client, "Unknown permission");
                 }
             }
-            if (int ret = sNetCtrl->createPhysicalNetwork(netId, permission)) {
+            if (int ret = gCtls->netCtrl.createPhysicalNetwork(netId, permission)) {
                 return operationError(client, "createPhysicalNetwork() failed", ret);
             }
         }
@@ -1471,7 +1656,7 @@ int CommandListener::NetworkCommand::runCommand(SocketClient* client, int argc,
             return syntaxError(client, "Incorrect number of arguments");
         }
         unsigned netId = stringToNetId(argv[2]);
-        if (int ret = sNetCtrl->destroyNetwork(netId)) {
+        if (int ret = gCtls->netCtrl.destroyNetwork(netId)) {
             return operationError(client, "destroyNetwork() failed", ret);
         }
         return success(client);
@@ -1493,7 +1678,7 @@ int CommandListener::NetworkCommand::runCommand(SocketClient* client, int argc,
         } else if (strcmp(argv[2], "clear")) {
             return syntaxError(client, "Unknown argument");
         }
-        if (int ret = sNetCtrl->setDefaultNetwork(netId)) {
+        if (int ret = gCtls->netCtrl.setDefaultNetwork(netId)) {
             return operationError(client, "setDefaultNetwork() failed", ret);
         }
         return success(client);
@@ -1522,24 +1707,36 @@ int CommandListener::NetworkCommand::runCommand(SocketClient* client, int argc,
         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")) {
-            sNetCtrl->setPermissionForUsers(permission, ids);
-        } else if (!strcmp(argv[2], "network")) {
-            if (int ret = sNetCtrl->setPermissionForNetworks(permission, ids)) {
+        if (userPermissions) {
+            gCtls->netCtrl.setPermissionForUsers(permission, ids);
+        } else {
+            // networkPermissions
+            if (int ret = gCtls->netCtrl.setPermissionForNetworks(permission, ids)) {
                 return operationError(client, "setPermissionForNetworks() failed", ret);
             }
-        } else {
-            return syntaxError(client, "Unknown argument");
         }
+
         return success(client);
     }
 
@@ -1556,11 +1753,11 @@ int CommandListener::NetworkCommand::runCommand(SocketClient* client, int argc,
             return syntaxError(client, "Invalid UIDs");
         }
         if (!strcmp(argv[2], "add")) {
-            if (int ret = sNetCtrl->addUsersToNetwork(netId, uidRanges)) {
+            if (int ret = gCtls->netCtrl.addUsersToNetwork(netId, uidRanges)) {
                 return operationError(client, "addUsersToNetwork() failed", ret);
             }
         } else if (!strcmp(argv[2], "remove")) {
-            if (int ret = sNetCtrl->removeUsersFromNetwork(netId, uidRanges)) {
+            if (int ret = gCtls->netCtrl.removeUsersFromNetwork(netId, uidRanges)) {
                 return operationError(client, "removeUsersFromNetwork() failed", ret);
             }
         } else {
@@ -1581,9 +1778,9 @@ int CommandListener::NetworkCommand::runCommand(SocketClient* client, int argc,
             uids.push_back(strtoul(argv[i], NULL, 0));
         }
         if (!strcmp(argv[2], "allow")) {
-            sNetCtrl->allowProtect(uids);
+            gCtls->netCtrl.allowProtect(uids);
         } else if (!strcmp(argv[2], "deny")) {
-            sNetCtrl->denyProtect(uids);
+            gCtls->netCtrl.denyProtect(uids);
         } else {
             return syntaxError(client, "Unknown argument");
         }