BandwidthController.cpp revision 26e0d49fa743d7881104196a9eda733bd2aac92f
14a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall/*
24a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall * Copyright (C) 2011 The Android Open Source Project
34a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall *
44a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall * Licensed under the Apache License, Version 2.0 (the "License");
54a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall * you may not use this file except in compliance with the License.
64a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall * You may obtain a copy of the License at
74a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall *
84a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall *      http://www.apache.org/licenses/LICENSE-2.0
94a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall *
104a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall * Unless required by applicable law or agreed to in writing, software
114a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall * distributed under the License is distributed on an "AS IS" BASIS,
124a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
134a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall * See the License for the specific language governing permissions and
144a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall * limitations under the License.
154a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall */
164a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall
174a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall#include <stdlib.h>
184a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall#include <fcntl.h>
194a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall#include <string.h>
204a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall
214a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall#include <sys/socket.h>
224a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall#include <sys/stat.h>
234a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall#include <sys/types.h>
244a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall#include <sys/wait.h>
254a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall
264a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall#include <linux/netlink.h>
274a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall#include <linux/rtnetlink.h>
284a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall#include <linux/pkt_sched.h>
294a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall
304a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall#define LOG_TAG "BandwidthController"
314a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall#include <cutils/log.h>
324a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall#include <cutils/properties.h>
334a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall
344a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrallextern "C" int logwrap(int argc, const char **argv, int background);
354a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall
364a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall#include "BandwidthController.h"
374a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall
38fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrallconst int BandwidthController::MAX_CMD_LEN = 1024;
394a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrallconst int BandwidthController::MAX_IFACENAME_LEN = 64;
404a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrallconst int BandwidthController::MAX_CMD_ARGS = 32;
414a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrallconst char BandwidthController::IPTABLES_PATH[] = "/system/bin/iptables";
42fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrallconst char BandwidthController::IP6TABLES_PATH[] = "/system/bin/ip6tables";
434a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall
444a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall/**
454a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall * Some comments about the rules:
464a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall *  * Ordering
474a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall *    - when an interface is marked as costly it should be INSERTED into the INPUT/OUTPUT chains.
484a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall *      E.g. "-I INPUT -i rmnet0 --goto costly"
494a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall *    - quota'd rules in the costly chain should be before penalty_box lookups.
504a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall *
514a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall * * global quota vs per interface quota
524a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall *   - global quota for all costly interfaces uses a single costly chain:
534a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall *    . initial rules
544a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall *      iptables -N costly
554a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall *      iptables -I INPUT -i iface0 --goto costly
564a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall *      iptables -I OUTPUT -o iface0 --goto costly
574a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall *      iptables -I costly -m quota \! --quota 500000 --jump REJECT --reject-with icmp-net-prohibited
584a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall *      iptables -A costly                            --jump penalty_box
594a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall *      iptables -A costly -m owner --socket-exists
604a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall *    . adding a new iface to this, E.g.:
614a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall *      iptables -I INPUT -i iface1 --goto costly
624a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall *      iptables -I OUTPUT -o iface1 --goto costly
634a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall *
644a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall *   - quota per interface. This is achieve by having "costly" chains per quota.
654a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall *     E.g. adding a new costly interface iface0 with its own quota:
664a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall *      iptables -N costly_iface0
674a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall *      iptables -I INPUT -i iface0 --goto costly_iface0
684a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall *      iptables -I OUTPUT -o iface0 --goto costly_iface0
694a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall *      iptables -A costly_iface0 -m quota \! --quota 500000 --jump REJECT --reject-with icmp-net-prohibited
704a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall *      iptables -A costly_iface0                            --jump penalty_box
714a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall *      iptables -A costly_iface0 -m owner --socket-exists
724a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall *
734a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall * * penalty_box handling:
744a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall *  - only one penalty_box for all interfaces
754a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall *   E.g  Adding an app:
764a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall *    iptables -A penalty_box -m owner --uid-owner app_3 --jump REJECT --reject-with icmp-net-prohibited
774a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall */
784a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrallconst char *BandwidthController::cleanupCommands[] = {
790dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    /* Cleanup rules. */
800dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    "-F",
810dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    "-t raw -F",
820dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    "-X costly",
830dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    "-X penalty_box",
840dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall};
854a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall
864a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrallconst char *BandwidthController::setupCommands[] = {
870dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    /* Created needed chains. */
880dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    "-N costly",
890dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    "-N penalty_box",
900dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall};
910dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall
920dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrallconst char *BandwidthController::basicAccountingCommands[] = {
930dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    "-F INPUT",
940dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    "-A INPUT -i lo --jump ACCEPT",
950dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    "-A INPUT -m owner --socket-exists", /* This is a tracking rule. */
960dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall
970dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    "-F OUTPUT",
980dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    "-A OUTPUT -o lo --jump ACCEPT",
990dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    "-A OUTPUT -m owner --socket-exists", /* This is a tracking rule. */
1000dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall
1010dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    "-F costly",
1020dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    "-A costly --jump penalty_box",
1030dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    "-A costly -m owner --socket-exists", /* This is a tracking rule. */
1040dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    /* TODO(jpa): Figure out why iptables doesn't correctly return from this
1050dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall     * chain. For now, hack the chain exit with an ACCEPT.
1060dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall     */
1070dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    "-A costly --jump ACCEPT",
1080dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall};
1094a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall
1104a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP AbgrallBandwidthController::BandwidthController(void) {
1114a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall
1124a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall    char value[PROPERTY_VALUE_MAX];
1134a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall
1144a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall    property_get("persist.bandwidth.enable", value, "0");
1154a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall    if (!strcmp(value, "1")) {
1164a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall        enableBandwidthControl();
1174a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall    }
1184a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall
1194a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall}
1204a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall
12126e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrallint BandwidthController::runIpxtablesCmd(const char *cmd, IptRejectOp rejectHandling) {
1220dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    int res = 0;
12326e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    LOGD("runIpxtablesCmd(cmd=%s)", cmd);
12426e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    res |= runIptablesCmd(cmd, rejectHandling, IptIpV4);
12526e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    res |= runIptablesCmd(cmd, rejectHandling, IptIpV6);
1260dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    return res;
1270dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall}
1280dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall
12926e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrallint BandwidthController::StrncpyAndCheck(char *buffer, const char *src, size_t buffSize) {
13026e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall
13126e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    memset(buffer, '\0', buffSize);  // strncpy() is not filling leftover with '\0'
13226e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    strncpy(buffer, src, buffSize);
13326e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    return buffer[buffSize - 1];
13426e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall}
13526e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall
13626e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrallint BandwidthController::runIptablesCmd(const char *cmd, IptRejectOp rejectHandling, IptIpVer iptVer) {
13726e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    char buffer[MAX_CMD_LEN];
1384a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall    const char *argv[MAX_CMD_ARGS];
13926e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    int argc = 0;
1404a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall    char *next = buffer;
1414a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall    char *tmp;
1424a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall
1430dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    std::string fullCmd = cmd;
14426e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall
14526e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    if (rejectHandling == IptRejectAdd) {
1460dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall        fullCmd += " --jump REJECT --reject-with";
14726e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall        switch (iptVer) {
14826e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall        case IptIpV4:
14926e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall                fullCmd += " icmp-net-prohibited";
15026e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall                break;
15126e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall        case IptIpV6:
15226e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall                fullCmd += " icmp6-adm-prohibited";
15326e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall                break;
1540dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall        }
1550dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    }
1560dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall
15726e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    argc = 0;
15826e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    argv[argc++] = iptVer == IptIpV4 ? IPTABLES_PATH : IP6TABLES_PATH;
1590dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    LOGD("About to run: %s %s", argv[0], fullCmd.c_str());
160fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall
16126e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    LOGD("runIpxtablesCmd(): fullCmd.c_str()=%s buffSize=%d", fullCmd.c_str(), sizeof(buffer));
16226e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    if (StrncpyAndCheck(buffer, fullCmd.c_str(), sizeof(buffer))) {
1630dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall        LOGE("iptables command too long");
1640dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall        return -1;
1650dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    }
1664a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall
1674a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall    while ((tmp = strsep(&next, " "))) {
16826e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall        argv[argc++] = tmp;
1690dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall        if (argc >= MAX_CMD_ARGS) {
1704a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall            LOGE("iptables argument overflow");
1714a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall            return -1;
1724a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall        }
1734a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall    }
174fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall
1754a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall    argv[argc] = NULL;
17626e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    LOGD("runIpxtablesCmd(): argc=%d, argv[argc]=%p", argc, argv[argc]);
1774a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall    /* TODO(jpa): Once this stabilizes, remove logwrap() as it tends to wedge netd
1784a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall     * Then just talk directly to the kernel via rtnetlink.
1794a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall     */
18026e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    for (int i = 0 ; i < argc; i++) {
18126e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall            LOGD("runIpxtablesCmd(): argv[%d]=%p:%s", i, argv[i], argv[i]?:"null");
18226e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    }
1834a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall    return logwrap(argc, argv, 0);
1844a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall}
1854a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall
1864a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrallint BandwidthController::enableBandwidthControl(void) {
187fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall    int res;
188fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall    /* Some of the initialCommands are allowed to fail */
18926e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    runCommands(sizeof(cleanupCommands) / sizeof(char*), cleanupCommands, RunCmdFailureOk);
19026e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    runCommands(sizeof(setupCommands) / sizeof(char*), setupCommands, RunCmdFailureOk);
19126e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    res = runCommands(sizeof(basicAccountingCommands) / sizeof(char*), basicAccountingCommands, RunCmdFailureBad);
192fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall    return res;
1934a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall
1944a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall}
1954a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall
1964a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrallint BandwidthController::disableBandwidthControl(void) {
197fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall    /* The cleanupCommands are allowed to fail. */
19826e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    runCommands(sizeof(cleanupCommands) / sizeof(char*), cleanupCommands, RunCmdFailureOk);
199fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall    return 0;
2004a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall}
2014a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall
20226e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrallint BandwidthController::runCommands(int numCommands, const char *commands[], RunCmdErrHandling cmdErrHandling) {
203fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall    int res = 0;
204fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall    LOGD("runCommands(): %d commands", numCommands);
205fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall    for (int cmdNum = 0; cmdNum < numCommands; cmdNum++) {
20626e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall        res = runIpxtablesCmd(commands[cmdNum], IptRejectNoAdd);
20726e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall        if (res && cmdErrHandling != RunCmdFailureBad)
208fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall            return res;
209fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall    }
21026e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    return cmdErrHandling == RunCmdFailureBad ? res : 0;
211fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall}
212fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall
2130dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrallstd::string BandwidthController::makeIptablesNaughtyCmd(IptOp op, int uid) {
214fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall    std::string res;
21526e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    char *convBuff;
216fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall
217fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall    switch (op) {
218fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall        case IptOpInsert:
219fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall            res = "-I";
220fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall            break;
221fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall        case IptOpReplace:
222fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall            res = "-R";
223fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall            break;
224fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall        default:
225fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall        case IptOpDelete:
226fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall            res = "-D";
227fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall            break;
228fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall    }
229fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall    res += " penalty_box";
23026e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    asprintf(&convBuff, "%d", uid);
231fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall    res += " -m owner --uid-owner ";
232fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall    res += convBuff;
23326e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    free(convBuff);
23426e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    LOGD("makeIptablesNaughtyCmd() res=%s", res.c_str());
235fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall    return res;
236fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall}
237fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall
238fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrallint BandwidthController::addNaughtyApps(int numUids, char *appUids[]) {
23926e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    return maninpulateNaughtyApps(numUids, appUids, NaughtyAppOpAdd);
240fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall}
241fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall
242fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrallint BandwidthController::removeNaughtyApps(int numUids, char *appUids[]) {
24326e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    return maninpulateNaughtyApps(numUids, appUids, NaughtyAppOpRemove);
244fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall}
245fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall
24626e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrallint BandwidthController::maninpulateNaughtyApps(int numUids, char *appStrUids[], NaughtyAppOp appOp) {
247fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall    char cmd[MAX_CMD_LEN];
248fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall    int uidNum;
24926e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    const char *failLogTemplate;
25026e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    IptOp op;
251fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall    int appUids[numUids];
25226e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    std::string naughtyCmd;
25326e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    LOGD("manipulateNaughtyApps()");
25426e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    switch (appOp) {
25526e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    case NaughtyAppOpAdd:
25626e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall            op = IptOpInsert;
25726e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall            failLogTemplate = "Failed to add app uid %d to penalty box.";
25826e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall            break;
25926e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    case NaughtyAppOpRemove:
26026e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall            op = IptOpDelete;
26126e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall            failLogTemplate = "Failed to delete app uid %d from penalty box.";
26226e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall            break;
26326e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    }
26426e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall
265fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall    for (uidNum = 0; uidNum < numUids; uidNum++) {
266fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall        appUids[uidNum] = atol(appStrUids[uidNum]);
267fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall        if (appUids[uidNum] == 0) {
26826e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall            LOGE(failLogTemplate, appUids[uidNum]);
269fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall            goto fail_parse;
270fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall        }
271fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall    }
27226e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    LOGD("manipulateNaughtyApps() got the appUids");
273fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall
274fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall    for (uidNum = 0; uidNum < numUids; uidNum++) {
27526e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall        naughtyCmd = makeIptablesNaughtyCmd(op, appUids[uidNum]);
27626e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall        if (runIpxtablesCmd(naughtyCmd.c_str(), IptRejectAdd)) {
27726e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall            LOGE(failLogTemplate, appUids[uidNum]);
278fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall            goto fail_with_uidNum;
279fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall        }
280fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall    }
281fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall    return 0;
282fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall
28326e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrallfail_with_uidNum:
284fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall    /* Try to remove the uid that failed in any case*/
28526e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    naughtyCmd = makeIptablesNaughtyCmd(IptOpDelete, appUids[uidNum]);
28626e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    runIpxtablesCmd(naughtyCmd.c_str(), IptRejectAdd);
28726e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrallfail_parse:
28826e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    return -1;
2894a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall}
2904a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall
29126e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrallstd::string BandwidthController::makeIptablesQuotaCmd(IptOp op, const char *costName, int64_t quota) {
292fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall    std::string res;
293fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall    char convBuff[21]; // log10(2^64) ~ 20
2940dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall
2950dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    LOGD("makeIptablesQuotaCmd(%d, %llu)", op, quota);
2960dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall
297fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall    switch (op) {
298fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall        case IptOpInsert:
299fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall            res = "-I";
300fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall            break;
301fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall        case IptOpReplace:
302fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall            res = "-R";
303fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall            break;
304fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall        default:
305fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall        case IptOpDelete:
306fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall            res = "-D";
307fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall            break;
308fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall    }
309fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall    res += " costly";
310fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall    if (costName) {
3110dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall        res += "_";
3120dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall        res += costName;
313fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall    }
314fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall    sprintf(convBuff, "%lld", quota);
3150dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    /* TODO(jpa): Use -m quota2 --name " + costName + " ! --quota "
3160dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall     * once available.
3170dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall     */
318fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall    res += " -m quota ! --quota ";
319fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall    res += convBuff;
320fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall    ;
3210dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    // The requried --jump REJECT ... will be added later.
3220dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    return res;
3230dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall}
3240dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall
32526e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrallint BandwidthController::prepCostlyIface(const char *ifn, QuotaType quotaType) {
3260dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    char cmd[MAX_CMD_LEN];
3270dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    int res = 0;
3280dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    std::string costString;
3290dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    const char *costCString;
3300dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall
3310dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    costString = "costly";
3320dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    /* The "-N costly" is created upfront, no need to handle it here. */
33326e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    switch (quotaType) {
33426e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    case QuotaUnique:
3350dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall        costString += "_";
3360dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall        costString += ifn;
3370dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall        costCString = costString.c_str();
3380dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall        snprintf(cmd, sizeof(cmd), "-N %s", costCString);
33926e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall        res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
3400dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall        snprintf(cmd, sizeof(cmd), "-A %s -j penalty_box", costCString);
34126e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall        res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
3420dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall        snprintf(cmd, sizeof(cmd), "-A %s -m owner --socket-exists", costCString);
34326e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall        res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
3440dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall        /* TODO(jpa): Figure out why iptables doesn't correctly return from this
3450dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall         * chain. For now, hack the chain exit with an ACCEPT.
3460dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall         */
3470dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall        snprintf(cmd, sizeof(cmd), "-A %s --jump ACCEPT", costCString);
34826e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall        res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
34926e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall        break;
35026e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    case QuotaShared:
3510dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall        costCString = costString.c_str();
35226e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall        break;
3530dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    }
3540dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall
3550dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    snprintf(cmd, sizeof(cmd), "-I INPUT -i %s --goto %s", ifn, costCString);
35626e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
3570dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    snprintf(cmd, sizeof(cmd), "-I OUTPUT -o %s --goto %s", ifn, costCString);
35826e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
3590dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    return res;
3600dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall}
3610dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall
36226e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrallint BandwidthController::cleanupCostlyIface(const char *ifn, QuotaType quotaType) {
3630dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    char cmd[MAX_CMD_LEN];
3640dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    int res = 0;
3650dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    std::string costString;
3660dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    const char *costCString;
3670dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall
3680dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    costString = "costly";
36926e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    switch (quotaType) {
37026e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    case QuotaUnique:
3710dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall        costString += "_";
3720dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall        costString += ifn;
3730dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall        costCString = costString.c_str();
37426e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall        break;
37526e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    case QuotaShared:
3760dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall        costCString = costString.c_str();
37726e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall        break;
3780dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    }
3790dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall
3800dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    snprintf(cmd, sizeof(cmd), "-D INPUT -i %s --goto %s", ifn, costCString);
38126e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
3820dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    snprintf(cmd, sizeof(cmd), "-D OUTPUT -o %s --goto %s", ifn, costCString);
38326e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
3840dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall
3850dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    /* The "-N costly" is created upfront, no need to handle it here. */
38626e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    if (quotaType == QuotaUnique) {
3870dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall        snprintf(cmd, sizeof(cmd), "-F %s", costCString);
38826e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall        res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
389fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall    }
390fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall    return res;
391fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall}
3924a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall
3930dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrallint BandwidthController::setInterfaceSharedQuota(const char *iface, int64_t maxBytes) {
3944a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall    char cmd[MAX_CMD_LEN];
3954a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall    char ifn[MAX_IFACENAME_LEN];
396fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall    int res = 0;
39726e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    std::string quotaCmd;
39826e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    std::string ifaceName;;
39926e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    const char *costName = NULL; /* Shared quota */
40026e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    std::list<std::string>::iterator it;
4014a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall
40226e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
40326e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall        LOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
40426e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall        return -1;
40526e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    }
40626e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    ifaceName = ifn;
4074a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall
4084a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall    if (maxBytes == -1) {
409fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall        return removeInterfaceSharedQuota(ifn);
4104a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall    }
4114a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall
4124a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall    /* Insert ingress quota. */
4130dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    for (it = sharedQuotaIfaces.begin(); it != sharedQuotaIfaces.end(); it++) {
4140dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall        if (*it == ifaceName)
415fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall            break;
4164a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall    }
417fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall
4180dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    if (it == sharedQuotaIfaces.end()) {
41926e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall        res |= prepCostlyIface(ifn, QuotaShared);
4200dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall        if (sharedQuotaIfaces.empty()) {
4210dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall            quotaCmd = makeIptablesQuotaCmd(IptOpInsert, costName, maxBytes);
42226e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall            res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
4234a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall            if (res) {
424fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall                LOGE("Failed set quota rule.");
425fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall                goto fail;
4264a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall            }
427fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall            sharedQuotaBytes = maxBytes;
428fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall        }
4290dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall        sharedQuotaIfaces.push_front(ifaceName);
430fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall
431fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall    }
432fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall
433fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall    if (maxBytes != sharedQuotaBytes) {
434fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall        /* Instead of replacing, which requires being aware of the rules in
435fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall         * the kernel, we just add a new one, then delete the older one.
436fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall         */
437fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall
4380dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall        quotaCmd = makeIptablesQuotaCmd(IptOpInsert, costName, maxBytes);
43926e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall        res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
440fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall
4410dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall        quotaCmd = makeIptablesQuotaCmd(IptOpDelete, costName, sharedQuotaBytes);
44226e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall        res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
443fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall
444fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall        if (res) {
445fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall            LOGE("Failed replace quota rule.");
446fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall            goto fail;
447fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall        }
448fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall        sharedQuotaBytes = maxBytes;
4494a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall    }
4504a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall    return 0;
451fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall
452fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall    fail:
4534a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall    /*
4544a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall     * TODO(jpa): once we get rid of iptables in favor of rtnetlink, reparse
4554a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall     * rules in the kernel to see which ones need cleaning up.
456fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall     * For now callers needs to choose if they want to "ndc bandwidth enable"
457fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall     * which resets everything.
4584a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall     */
459fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall    removeInterfaceSharedQuota(ifn);
4604a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall    return -1;
4614a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall}
4624a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall
463fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrallint BandwidthController::removeInterfaceSharedQuota(const char *iface) {
4644a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall    char ifn[MAX_IFACENAME_LEN];
465fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall    int res = 0;
46626e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    std::string ifaceName;
4670dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    std::list<std::string>::iterator it;
4684a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall
46926e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    if(StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
47026e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall        LOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
47126e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall        return -1;
47226e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    }
47326e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    ifaceName =ifn;
47426e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall
4750dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    for (it = sharedQuotaIfaces.begin(); it != sharedQuotaIfaces.end(); it++) {
4760dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall        if (*it == ifaceName)
477fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall            break;
4784a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall    }
4790dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    if (it == sharedQuotaIfaces.end()) {
480fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall        LOGE("No such iface %s to delete.", ifn);
481fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall        return -1;
4824a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall    }
483fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall
48426e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    res |= cleanupCostlyIface(ifn, QuotaShared);
4850dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    sharedQuotaIfaces.erase(it);
486fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall
4870dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    if (sharedQuotaIfaces.empty()) {
488fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall        std::string quotaCmd;
4890dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall        quotaCmd = makeIptablesQuotaCmd(IptOpDelete, NULL, sharedQuotaBytes);
49026e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall        res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
491fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall        sharedQuotaBytes = -1;
492fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall    }
493fa6f46d3370ae5475fc3bc8273bbe04ee7348d60JP Abgrall
4944a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall    return res;
4954a5f5ca3c9e07fc3e6feca2afde07f41a8a64f11JP Abgrall}
4960dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall
4970dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrallint BandwidthController::setInterfaceQuota(const char *iface, int64_t maxBytes) {
4980dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    char ifn[MAX_IFACENAME_LEN];
4990dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    int res = 0;
50026e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    std::string ifaceName;
50126e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    const char *costName;
50226e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    std::list<QuotaInfo>::iterator it;
50326e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    std::string quotaCmd;
5040dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall
5050dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    if (maxBytes == -1) {
50626e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall        return removeInterfaceQuota(iface);
5070dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    }
5080dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall
50926e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    if(StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
51026e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall        LOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
51126e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall        return -1;
51226e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    }
51326e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    ifaceName = ifn;
51426e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    costName = iface;
51526e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall
5160dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall
5170dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    /* Insert ingress quota. */
5180dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
5190dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall        if (it->first == ifaceName)
5200dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall            break;
5210dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    }
5220dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall
5230dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    if (it == quotaIfaces.end()) {
52426e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall        res |= prepCostlyIface(ifn, QuotaUnique);
5250dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall        quotaCmd = makeIptablesQuotaCmd(IptOpInsert, costName, maxBytes);
52626e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall        res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
5270dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall        if (res) {
5280dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall            LOGE("Failed set quota rule.");
5290dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall            goto fail;
5300dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall        }
5310dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall
5320dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall        quotaIfaces.push_front(QuotaInfo(ifaceName, maxBytes));
5330dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall
5340dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    } else {
5350dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall        /* Instead of replacing, which requires being aware of the rules in
5360dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall         * the kernel, we just add a new one, then delete the older one.
5370dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall         */
5380dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall        quotaCmd = makeIptablesQuotaCmd(IptOpInsert, costName, maxBytes);
53926e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall        res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
5400dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall
5410dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall        quotaCmd = makeIptablesQuotaCmd(IptOpDelete, costName, it->second);
54226e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall        res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
5430dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall
5440dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall        if (res) {
5450dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall            LOGE("Failed replace quota rule.");
5460dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall            goto fail;
5470dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall        }
5480dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall        it->second = maxBytes;
5490dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    }
5500dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    return 0;
5510dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall
5520dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    fail:
5530dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    /*
5540dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall     * TODO(jpa): once we get rid of iptables in favor of rtnetlink, reparse
5550dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall     * rules in the kernel to see which ones need cleaning up.
5560dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall     * For now callers needs to choose if they want to "ndc bandwidth enable"
5570dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall     * which resets everything.
5580dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall     */
5590dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    removeInterfaceSharedQuota(ifn);
5600dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    return -1;
5610dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall}
5620dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall
5630dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrallint BandwidthController::removeInterfaceQuota(const char *iface) {
5640dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall
5650dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    char ifn[MAX_IFACENAME_LEN];
5660dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    int res = 0;
56726e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    std::string ifaceName;
56826e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    const char *costName;
56926e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    std::list<QuotaInfo>::iterator it;
5700dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall
57126e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    if(StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
57226e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall        LOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
57326e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall        return -1;
57426e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    }
57526e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    ifaceName = ifn;
57626e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    costName = iface;
5770dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall
5780dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
5790dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall        if (it->first == ifaceName)
5800dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall            break;
5810dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    }
5820dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall
5830dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    if (it == quotaIfaces.end()) {
5840dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall        LOGE("No such iface %s to delete.", ifn);
5850dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall        return -1;
5860dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    }
5870dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall
5880dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    /* This also removes the quota command of CostlyIface chain. */
58926e0d49fa743d7881104196a9eda733bd2aac92fJP Abgrall    res |= cleanupCostlyIface(ifn, QuotaUnique);
5900dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall
5910dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    quotaIfaces.erase(it);
5920dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall
5930dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall    return res;
5940dad7c2f1f6994fbe5e85b9e1fc72d29d6453211JP Abgrall}
595