JP Abgrall | 4a5f5ca | 2011-06-15 18:37:39 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2011 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
JP Abgrall | 8a93272 | 2011-07-13 19:17:35 -0700 | [diff] [blame^] | 17 | #include <errno.h> |
JP Abgrall | 4a5f5ca | 2011-06-15 18:37:39 -0700 | [diff] [blame] | 18 | #include <fcntl.h> |
JP Abgrall | 8a93272 | 2011-07-13 19:17:35 -0700 | [diff] [blame^] | 19 | #include <stdlib.h> |
JP Abgrall | 4a5f5ca | 2011-06-15 18:37:39 -0700 | [diff] [blame] | 20 | #include <string.h> |
| 21 | |
| 22 | #include <sys/socket.h> |
| 23 | #include <sys/stat.h> |
| 24 | #include <sys/types.h> |
| 25 | #include <sys/wait.h> |
| 26 | |
| 27 | #include <linux/netlink.h> |
| 28 | #include <linux/rtnetlink.h> |
| 29 | #include <linux/pkt_sched.h> |
| 30 | |
| 31 | #define LOG_TAG "BandwidthController" |
| 32 | #include <cutils/log.h> |
| 33 | #include <cutils/properties.h> |
| 34 | |
| 35 | extern "C" int logwrap(int argc, const char **argv, int background); |
| 36 | |
| 37 | #include "BandwidthController.h" |
| 38 | |
JP Abgrall | fa6f46d | 2011-06-17 23:17:28 -0700 | [diff] [blame] | 39 | const int BandwidthController::MAX_CMD_LEN = 1024; |
JP Abgrall | 4a5f5ca | 2011-06-15 18:37:39 -0700 | [diff] [blame] | 40 | const int BandwidthController::MAX_IFACENAME_LEN = 64; |
| 41 | const int BandwidthController::MAX_CMD_ARGS = 32; |
| 42 | const char BandwidthController::IPTABLES_PATH[] = "/system/bin/iptables"; |
JP Abgrall | fa6f46d | 2011-06-17 23:17:28 -0700 | [diff] [blame] | 43 | const char BandwidthController::IP6TABLES_PATH[] = "/system/bin/ip6tables"; |
JP Abgrall | 8a93272 | 2011-07-13 19:17:35 -0700 | [diff] [blame^] | 44 | const char BandwidthController::ALERT_IPT_TEMPLATE[] = "%s %s -m quota2 ! --quota %lld --name %s"; |
| 45 | const int BandwidthController::ALERT_RULE_POS_IN_COSTLY_CHAIN = 4; |
JP Abgrall | 4a5f5ca | 2011-06-15 18:37:39 -0700 | [diff] [blame] | 46 | |
| 47 | /** |
| 48 | * Some comments about the rules: |
| 49 | * * Ordering |
| 50 | * - when an interface is marked as costly it should be INSERTED into the INPUT/OUTPUT chains. |
| 51 | * E.g. "-I INPUT -i rmnet0 --goto costly" |
| 52 | * - quota'd rules in the costly chain should be before penalty_box lookups. |
| 53 | * |
| 54 | * * global quota vs per interface quota |
| 55 | * - global quota for all costly interfaces uses a single costly chain: |
| 56 | * . initial rules |
JP Abgrall | bfa7466 | 2011-06-29 19:23:04 -0700 | [diff] [blame] | 57 | * iptables -N costly_shared |
| 58 | * iptables -I INPUT -i iface0 --goto costly_shared |
| 59 | * iptables -I OUTPUT -o iface0 --goto costly_shared |
| 60 | * iptables -I costly_shared -m quota \! --quota 500000 \ |
| 61 | * --jump REJECT --reject-with icmp-net-prohibited |
| 62 | * iptables -A costly_shared --jump penalty_box |
| 63 | * iptables -A costly_shared -m owner --socket-exists |
JP Abgrall | 8a93272 | 2011-07-13 19:17:35 -0700 | [diff] [blame^] | 64 | * |
JP Abgrall | 4a5f5ca | 2011-06-15 18:37:39 -0700 | [diff] [blame] | 65 | * . adding a new iface to this, E.g.: |
JP Abgrall | bfa7466 | 2011-06-29 19:23:04 -0700 | [diff] [blame] | 66 | * iptables -I INPUT -i iface1 --goto costly_shared |
| 67 | * iptables -I OUTPUT -o iface1 --goto costly_shared |
JP Abgrall | 4a5f5ca | 2011-06-15 18:37:39 -0700 | [diff] [blame] | 68 | * |
| 69 | * - quota per interface. This is achieve by having "costly" chains per quota. |
| 70 | * E.g. adding a new costly interface iface0 with its own quota: |
| 71 | * iptables -N costly_iface0 |
| 72 | * iptables -I INPUT -i iface0 --goto costly_iface0 |
| 73 | * iptables -I OUTPUT -o iface0 --goto costly_iface0 |
JP Abgrall | bfa7466 | 2011-06-29 19:23:04 -0700 | [diff] [blame] | 74 | * iptables -A costly_iface0 -m quota \! --quota 500000 \ |
| 75 | * --jump REJECT --reject-with icmp-net-prohibited |
| 76 | * iptables -A costly_iface0 --jump penalty_box |
JP Abgrall | 4a5f5ca | 2011-06-15 18:37:39 -0700 | [diff] [blame] | 77 | * iptables -A costly_iface0 -m owner --socket-exists |
| 78 | * |
| 79 | * * penalty_box handling: |
| 80 | * - only one penalty_box for all interfaces |
| 81 | * E.g Adding an app: |
JP Abgrall | bfa7466 | 2011-06-29 19:23:04 -0700 | [diff] [blame] | 82 | * iptables -A penalty_box -m owner --uid-owner app_3 \ |
| 83 | * --jump REJECT --reject-with icmp-net-prohibited |
JP Abgrall | 4a5f5ca | 2011-06-15 18:37:39 -0700 | [diff] [blame] | 84 | */ |
| 85 | const char *BandwidthController::cleanupCommands[] = { |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 86 | /* Cleanup rules. */ |
| 87 | "-F", |
| 88 | "-t raw -F", |
JP Abgrall | 39f8f24 | 2011-06-29 19:21:58 -0700 | [diff] [blame] | 89 | /* TODO: If at some point we need more user chains than here, then we will need |
| 90 | * a different cleanup approach. |
| 91 | */ |
JP Abgrall | bfa7466 | 2011-06-29 19:23:04 -0700 | [diff] [blame] | 92 | "-X", /* Should normally only be costly_shared, penalty_box, and costly_<iface> */ |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 93 | }; |
JP Abgrall | 4a5f5ca | 2011-06-15 18:37:39 -0700 | [diff] [blame] | 94 | |
| 95 | const char *BandwidthController::setupCommands[] = { |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 96 | /* Created needed chains. */ |
JP Abgrall | bfa7466 | 2011-06-29 19:23:04 -0700 | [diff] [blame] | 97 | "-N costly_shared", |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 98 | "-N penalty_box", |
| 99 | }; |
JP Abgrall | 4a5f5ca | 2011-06-15 18:37:39 -0700 | [diff] [blame] | 100 | |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 101 | const char *BandwidthController::basicAccountingCommands[] = { |
| 102 | "-F INPUT", |
| 103 | "-A INPUT -i lo --jump ACCEPT", |
| 104 | "-A INPUT -m owner --socket-exists", /* This is a tracking rule. */ |
JP Abgrall | 4a5f5ca | 2011-06-15 18:37:39 -0700 | [diff] [blame] | 105 | |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 106 | "-F OUTPUT", |
| 107 | "-A OUTPUT -o lo --jump ACCEPT", |
| 108 | "-A OUTPUT -m owner --socket-exists", /* This is a tracking rule. */ |
JP Abgrall | 4a5f5ca | 2011-06-15 18:37:39 -0700 | [diff] [blame] | 109 | |
JP Abgrall | bfa7466 | 2011-06-29 19:23:04 -0700 | [diff] [blame] | 110 | "-F costly_shared", |
| 111 | "-A costly_shared --jump penalty_box", |
| 112 | "-A costly_shared -m owner --socket-exists", /* This is a tracking rule. */ |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 113 | /* TODO(jpa): Figure out why iptables doesn't correctly return from this |
| 114 | * chain. For now, hack the chain exit with an ACCEPT. |
| 115 | */ |
JP Abgrall | bfa7466 | 2011-06-29 19:23:04 -0700 | [diff] [blame] | 116 | "-A costly_shared --jump ACCEPT", |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 117 | }; |
JP Abgrall | 4a5f5ca | 2011-06-15 18:37:39 -0700 | [diff] [blame] | 118 | |
| 119 | BandwidthController::BandwidthController(void) { |
JP Abgrall | 4a5f5ca | 2011-06-15 18:37:39 -0700 | [diff] [blame] | 120 | char value[PROPERTY_VALUE_MAX]; |
| 121 | |
| 122 | property_get("persist.bandwidth.enable", value, "0"); |
| 123 | if (!strcmp(value, "1")) { |
| 124 | enableBandwidthControl(); |
| 125 | } |
| 126 | |
| 127 | } |
| 128 | |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 129 | int BandwidthController::runIpxtablesCmd(const char *cmd, IptRejectOp rejectHandling) { |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 130 | int res = 0; |
JP Abgrall | 8a93272 | 2011-07-13 19:17:35 -0700 | [diff] [blame^] | 131 | |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 132 | LOGD("runIpxtablesCmd(cmd=%s)", cmd); |
| 133 | res |= runIptablesCmd(cmd, rejectHandling, IptIpV4); |
| 134 | res |= runIptablesCmd(cmd, rejectHandling, IptIpV6); |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 135 | return res; |
| 136 | } |
| 137 | |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 138 | int BandwidthController::StrncpyAndCheck(char *buffer, const char *src, size_t buffSize) { |
| 139 | |
| 140 | memset(buffer, '\0', buffSize); // strncpy() is not filling leftover with '\0' |
| 141 | strncpy(buffer, src, buffSize); |
| 142 | return buffer[buffSize - 1]; |
| 143 | } |
| 144 | |
JP Abgrall | 8a93272 | 2011-07-13 19:17:35 -0700 | [diff] [blame^] | 145 | int BandwidthController::runIptablesCmd(const char *cmd, IptRejectOp rejectHandling, |
| 146 | IptIpVer iptVer) { |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 147 | char buffer[MAX_CMD_LEN]; |
JP Abgrall | 4a5f5ca | 2011-06-15 18:37:39 -0700 | [diff] [blame] | 148 | const char *argv[MAX_CMD_ARGS]; |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 149 | int argc = 0; |
JP Abgrall | 4a5f5ca | 2011-06-15 18:37:39 -0700 | [diff] [blame] | 150 | char *next = buffer; |
| 151 | char *tmp; |
| 152 | |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 153 | std::string fullCmd = cmd; |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 154 | |
| 155 | if (rejectHandling == IptRejectAdd) { |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 156 | fullCmd += " --jump REJECT --reject-with"; |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 157 | switch (iptVer) { |
| 158 | case IptIpV4: |
JP Abgrall | 8a93272 | 2011-07-13 19:17:35 -0700 | [diff] [blame^] | 159 | fullCmd += " icmp-net-prohibited"; |
| 160 | break; |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 161 | case IptIpV6: |
JP Abgrall | 8a93272 | 2011-07-13 19:17:35 -0700 | [diff] [blame^] | 162 | fullCmd += " icmp6-adm-prohibited"; |
| 163 | break; |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 164 | } |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 165 | } |
JP Abgrall | fa6f46d | 2011-06-17 23:17:28 -0700 | [diff] [blame] | 166 | |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 167 | argc = 0; |
| 168 | argv[argc++] = iptVer == IptIpV4 ? IPTABLES_PATH : IP6TABLES_PATH; |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 169 | |
JP Abgrall | a9f802c | 2011-06-29 15:46:45 -0700 | [diff] [blame] | 170 | LOGD("runIptablesCmd(): %s %s", argv[0], fullCmd.c_str()); |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 171 | if (StrncpyAndCheck(buffer, fullCmd.c_str(), sizeof(buffer))) { |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 172 | LOGE("iptables command too long"); |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 173 | return -1; |
| 174 | } |
JP Abgrall | 4a5f5ca | 2011-06-15 18:37:39 -0700 | [diff] [blame] | 175 | |
| 176 | while ((tmp = strsep(&next, " "))) { |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 177 | argv[argc++] = tmp; |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 178 | if (argc >= MAX_CMD_ARGS) { |
JP Abgrall | 4a5f5ca | 2011-06-15 18:37:39 -0700 | [diff] [blame] | 179 | LOGE("iptables argument overflow"); |
JP Abgrall | 4a5f5ca | 2011-06-15 18:37:39 -0700 | [diff] [blame] | 180 | return -1; |
| 181 | } |
| 182 | } |
JP Abgrall | fa6f46d | 2011-06-17 23:17:28 -0700 | [diff] [blame] | 183 | |
JP Abgrall | 4a5f5ca | 2011-06-15 18:37:39 -0700 | [diff] [blame] | 184 | argv[argc] = NULL; |
| 185 | /* TODO(jpa): Once this stabilizes, remove logwrap() as it tends to wedge netd |
| 186 | * Then just talk directly to the kernel via rtnetlink. |
| 187 | */ |
| 188 | return logwrap(argc, argv, 0); |
| 189 | } |
| 190 | |
JP Abgrall | 4a5f5ca | 2011-06-15 18:37:39 -0700 | [diff] [blame] | 191 | int BandwidthController::enableBandwidthControl(void) { |
JP Abgrall | fa6f46d | 2011-06-17 23:17:28 -0700 | [diff] [blame] | 192 | int res; |
| 193 | /* Some of the initialCommands are allowed to fail */ |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 194 | runCommands(sizeof(cleanupCommands) / sizeof(char*), cleanupCommands, RunCmdFailureOk); |
| 195 | runCommands(sizeof(setupCommands) / sizeof(char*), setupCommands, RunCmdFailureOk); |
JP Abgrall | 8a93272 | 2011-07-13 19:17:35 -0700 | [diff] [blame^] | 196 | res = runCommands(sizeof(basicAccountingCommands) / sizeof(char*), basicAccountingCommands, |
| 197 | RunCmdFailureBad); |
| 198 | |
| 199 | sharedQuotaBytes = sharedAlertBytes = 0; |
| 200 | sharedQuotaIfaces.clear(); |
| 201 | quotaIfaces.clear(); |
| 202 | naughtyAppUids.clear(); |
| 203 | |
JP Abgrall | fa6f46d | 2011-06-17 23:17:28 -0700 | [diff] [blame] | 204 | return res; |
JP Abgrall | 4a5f5ca | 2011-06-15 18:37:39 -0700 | [diff] [blame] | 205 | |
| 206 | } |
| 207 | |
| 208 | int BandwidthController::disableBandwidthControl(void) { |
JP Abgrall | fa6f46d | 2011-06-17 23:17:28 -0700 | [diff] [blame] | 209 | /* The cleanupCommands are allowed to fail. */ |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 210 | runCommands(sizeof(cleanupCommands) / sizeof(char*), cleanupCommands, RunCmdFailureOk); |
JP Abgrall | fa6f46d | 2011-06-17 23:17:28 -0700 | [diff] [blame] | 211 | return 0; |
JP Abgrall | 4a5f5ca | 2011-06-15 18:37:39 -0700 | [diff] [blame] | 212 | } |
| 213 | |
JP Abgrall | 8a93272 | 2011-07-13 19:17:35 -0700 | [diff] [blame^] | 214 | int BandwidthController::runCommands(int numCommands, const char *commands[], |
| 215 | RunCmdErrHandling cmdErrHandling) { |
JP Abgrall | fa6f46d | 2011-06-17 23:17:28 -0700 | [diff] [blame] | 216 | int res = 0; |
| 217 | LOGD("runCommands(): %d commands", numCommands); |
| 218 | for (int cmdNum = 0; cmdNum < numCommands; cmdNum++) { |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 219 | res = runIpxtablesCmd(commands[cmdNum], IptRejectNoAdd); |
| 220 | if (res && cmdErrHandling != RunCmdFailureBad) |
JP Abgrall | fa6f46d | 2011-06-17 23:17:28 -0700 | [diff] [blame] | 221 | return res; |
| 222 | } |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 223 | return cmdErrHandling == RunCmdFailureBad ? res : 0; |
JP Abgrall | fa6f46d | 2011-06-17 23:17:28 -0700 | [diff] [blame] | 224 | } |
| 225 | |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 226 | std::string BandwidthController::makeIptablesNaughtyCmd(IptOp op, int uid) { |
JP Abgrall | fa6f46d | 2011-06-17 23:17:28 -0700 | [diff] [blame] | 227 | std::string res; |
JP Abgrall | 8a93272 | 2011-07-13 19:17:35 -0700 | [diff] [blame^] | 228 | char *buff; |
| 229 | const char *opFlag; |
JP Abgrall | fa6f46d | 2011-06-17 23:17:28 -0700 | [diff] [blame] | 230 | |
| 231 | switch (op) { |
JP Abgrall | 8a93272 | 2011-07-13 19:17:35 -0700 | [diff] [blame^] | 232 | case IptOpInsert: |
| 233 | opFlag = "-I"; |
| 234 | break; |
| 235 | case IptOpReplace: |
| 236 | opFlag = "-R"; |
| 237 | break; |
| 238 | default: |
| 239 | case IptOpDelete: |
| 240 | opFlag = "-D"; |
| 241 | break; |
JP Abgrall | fa6f46d | 2011-06-17 23:17:28 -0700 | [diff] [blame] | 242 | } |
JP Abgrall | 8a93272 | 2011-07-13 19:17:35 -0700 | [diff] [blame^] | 243 | asprintf(&buff, "%s penalty_box -m owner --uid-owner %d", opFlag, uid); |
| 244 | res = buff; |
| 245 | free(buff); |
JP Abgrall | fa6f46d | 2011-06-17 23:17:28 -0700 | [diff] [blame] | 246 | return res; |
| 247 | } |
| 248 | |
| 249 | int BandwidthController::addNaughtyApps(int numUids, char *appUids[]) { |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 250 | return maninpulateNaughtyApps(numUids, appUids, NaughtyAppOpAdd); |
JP Abgrall | fa6f46d | 2011-06-17 23:17:28 -0700 | [diff] [blame] | 251 | } |
| 252 | |
| 253 | int BandwidthController::removeNaughtyApps(int numUids, char *appUids[]) { |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 254 | return maninpulateNaughtyApps(numUids, appUids, NaughtyAppOpRemove); |
JP Abgrall | fa6f46d | 2011-06-17 23:17:28 -0700 | [diff] [blame] | 255 | } |
| 256 | |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 257 | int BandwidthController::maninpulateNaughtyApps(int numUids, char *appStrUids[], NaughtyAppOp appOp) { |
JP Abgrall | fa6f46d | 2011-06-17 23:17:28 -0700 | [diff] [blame] | 258 | char cmd[MAX_CMD_LEN]; |
| 259 | int uidNum; |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 260 | const char *failLogTemplate; |
| 261 | IptOp op; |
JP Abgrall | fa6f46d | 2011-06-17 23:17:28 -0700 | [diff] [blame] | 262 | int appUids[numUids]; |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 263 | std::string naughtyCmd; |
JP Abgrall | 8a93272 | 2011-07-13 19:17:35 -0700 | [diff] [blame^] | 264 | |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 265 | switch (appOp) { |
| 266 | case NaughtyAppOpAdd: |
JP Abgrall | 8a93272 | 2011-07-13 19:17:35 -0700 | [diff] [blame^] | 267 | op = IptOpInsert; |
| 268 | failLogTemplate = "Failed to add app uid %d to penalty box."; |
| 269 | break; |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 270 | case NaughtyAppOpRemove: |
JP Abgrall | 8a93272 | 2011-07-13 19:17:35 -0700 | [diff] [blame^] | 271 | op = IptOpDelete; |
| 272 | failLogTemplate = "Failed to delete app uid %d from penalty box."; |
| 273 | break; |
JP Abgrall | fa6f46d | 2011-06-17 23:17:28 -0700 | [diff] [blame] | 274 | } |
| 275 | |
| 276 | for (uidNum = 0; uidNum < numUids; uidNum++) { |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 277 | appUids[uidNum] = atol(appStrUids[uidNum]); |
| 278 | if (appUids[uidNum] == 0) { |
| 279 | LOGE(failLogTemplate, appUids[uidNum]); |
| 280 | goto fail_parse; |
| 281 | } |
| 282 | } |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 283 | |
| 284 | for (uidNum = 0; uidNum < numUids; uidNum++) { |
| 285 | naughtyCmd = makeIptablesNaughtyCmd(op, appUids[uidNum]); |
| 286 | if (runIpxtablesCmd(naughtyCmd.c_str(), IptRejectAdd)) { |
| 287 | LOGE(failLogTemplate, appUids[uidNum]); |
JP Abgrall | fa6f46d | 2011-06-17 23:17:28 -0700 | [diff] [blame] | 288 | goto fail_with_uidNum; |
| 289 | } |
| 290 | } |
| 291 | return 0; |
| 292 | |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 293 | fail_with_uidNum: |
JP Abgrall | fa6f46d | 2011-06-17 23:17:28 -0700 | [diff] [blame] | 294 | /* Try to remove the uid that failed in any case*/ |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 295 | naughtyCmd = makeIptablesNaughtyCmd(IptOpDelete, appUids[uidNum]); |
| 296 | runIpxtablesCmd(naughtyCmd.c_str(), IptRejectAdd); |
| 297 | fail_parse: |
| 298 | return -1; |
JP Abgrall | 4a5f5ca | 2011-06-15 18:37:39 -0700 | [diff] [blame] | 299 | } |
| 300 | |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 301 | std::string BandwidthController::makeIptablesQuotaCmd(IptOp op, const char *costName, int64_t quota) { |
JP Abgrall | fa6f46d | 2011-06-17 23:17:28 -0700 | [diff] [blame] | 302 | std::string res; |
JP Abgrall | 8a93272 | 2011-07-13 19:17:35 -0700 | [diff] [blame^] | 303 | char *buff; |
| 304 | const char *opFlag; |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 305 | |
JP Abgrall | 8a93272 | 2011-07-13 19:17:35 -0700 | [diff] [blame^] | 306 | LOGD("makeIptablesQuotaCmd(%d, %lld)", op, quota); |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 307 | |
JP Abgrall | fa6f46d | 2011-06-17 23:17:28 -0700 | [diff] [blame] | 308 | switch (op) { |
JP Abgrall | 8a93272 | 2011-07-13 19:17:35 -0700 | [diff] [blame^] | 309 | case IptOpInsert: |
| 310 | opFlag = "-I"; |
| 311 | break; |
| 312 | case IptOpReplace: |
| 313 | opFlag = "-R"; |
| 314 | break; |
| 315 | default: |
| 316 | case IptOpDelete: |
| 317 | opFlag = "-D"; |
| 318 | break; |
JP Abgrall | fa6f46d | 2011-06-17 23:17:28 -0700 | [diff] [blame] | 319 | } |
JP Abgrall | 8a93272 | 2011-07-13 19:17:35 -0700 | [diff] [blame^] | 320 | |
JP Abgrall | bfa7466 | 2011-06-29 19:23:04 -0700 | [diff] [blame] | 321 | // The requried IP version specific --jump REJECT ... will be added later. |
JP Abgrall | 8a93272 | 2011-07-13 19:17:35 -0700 | [diff] [blame^] | 322 | asprintf(&buff, "%s costly_%s -m quota2 ! --quota %lld --name %s", opFlag, costName, quota, |
| 323 | costName); |
| 324 | res = buff; |
| 325 | free(buff); |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 326 | return res; |
| 327 | } |
| 328 | |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 329 | int BandwidthController::prepCostlyIface(const char *ifn, QuotaType quotaType) { |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 330 | char cmd[MAX_CMD_LEN]; |
| 331 | int res = 0; |
JP Abgrall | 8a93272 | 2011-07-13 19:17:35 -0700 | [diff] [blame^] | 332 | int ruleInsertPos = 1; |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 333 | std::string costString; |
| 334 | const char *costCString; |
| 335 | |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 336 | /* The "-N costly" is created upfront, no need to handle it here. */ |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 337 | switch (quotaType) { |
| 338 | case QuotaUnique: |
JP Abgrall | bfa7466 | 2011-06-29 19:23:04 -0700 | [diff] [blame] | 339 | costString = "costly_"; |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 340 | costString += ifn; |
| 341 | costCString = costString.c_str(); |
| 342 | snprintf(cmd, sizeof(cmd), "-N %s", costCString); |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 343 | res |= runIpxtablesCmd(cmd, IptRejectNoAdd); |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 344 | snprintf(cmd, sizeof(cmd), "-A %s -j penalty_box", costCString); |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 345 | res |= runIpxtablesCmd(cmd, IptRejectNoAdd); |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 346 | snprintf(cmd, sizeof(cmd), "-A %s -m owner --socket-exists", costCString); |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 347 | res |= runIpxtablesCmd(cmd, IptRejectNoAdd); |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 348 | /* TODO(jpa): Figure out why iptables doesn't correctly return from this |
| 349 | * chain. For now, hack the chain exit with an ACCEPT. |
| 350 | */ |
| 351 | snprintf(cmd, sizeof(cmd), "-A %s --jump ACCEPT", costCString); |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 352 | res |= runIpxtablesCmd(cmd, IptRejectNoAdd); |
| 353 | break; |
| 354 | case QuotaShared: |
JP Abgrall | bfa7466 | 2011-06-29 19:23:04 -0700 | [diff] [blame] | 355 | costCString = "costly_shared"; |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 356 | break; |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 357 | } |
| 358 | |
JP Abgrall | 8a93272 | 2011-07-13 19:17:35 -0700 | [diff] [blame^] | 359 | if (globalAlertBytes) { |
| 360 | /* The alert rule comes 1st */ |
| 361 | ruleInsertPos = 2; |
| 362 | } |
| 363 | snprintf(cmd, sizeof(cmd), "-I INPUT %d -i %s --goto %s", ruleInsertPos, ifn, costCString); |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 364 | res |= runIpxtablesCmd(cmd, IptRejectNoAdd); |
JP Abgrall | 8a93272 | 2011-07-13 19:17:35 -0700 | [diff] [blame^] | 365 | snprintf(cmd, sizeof(cmd), "-I OUTPUT %d -o %s --goto %s", ruleInsertPos, ifn, costCString); |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 366 | res |= runIpxtablesCmd(cmd, IptRejectNoAdd); |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 367 | return res; |
| 368 | } |
| 369 | |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 370 | int BandwidthController::cleanupCostlyIface(const char *ifn, QuotaType quotaType) { |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 371 | char cmd[MAX_CMD_LEN]; |
| 372 | int res = 0; |
| 373 | std::string costString; |
| 374 | const char *costCString; |
| 375 | |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 376 | switch (quotaType) { |
| 377 | case QuotaUnique: |
JP Abgrall | bfa7466 | 2011-06-29 19:23:04 -0700 | [diff] [blame] | 378 | costString = "costly_"; |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 379 | costString += ifn; |
| 380 | costCString = costString.c_str(); |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 381 | break; |
| 382 | case QuotaShared: |
JP Abgrall | bfa7466 | 2011-06-29 19:23:04 -0700 | [diff] [blame] | 383 | costCString = "costly_shared"; |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 384 | break; |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 385 | } |
| 386 | |
| 387 | snprintf(cmd, sizeof(cmd), "-D INPUT -i %s --goto %s", ifn, costCString); |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 388 | res |= runIpxtablesCmd(cmd, IptRejectNoAdd); |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 389 | snprintf(cmd, sizeof(cmd), "-D OUTPUT -o %s --goto %s", ifn, costCString); |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 390 | res |= runIpxtablesCmd(cmd, IptRejectNoAdd); |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 391 | |
JP Abgrall | bfa7466 | 2011-06-29 19:23:04 -0700 | [diff] [blame] | 392 | /* The "-N costly_shared" is created upfront, no need to handle it here. */ |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 393 | if (quotaType == QuotaUnique) { |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 394 | snprintf(cmd, sizeof(cmd), "-F %s", costCString); |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 395 | res |= runIpxtablesCmd(cmd, IptRejectNoAdd); |
JP Abgrall | a9f802c | 2011-06-29 15:46:45 -0700 | [diff] [blame] | 396 | snprintf(cmd, sizeof(cmd), "-X %s", costCString); |
| 397 | res |= runIpxtablesCmd(cmd, IptRejectNoAdd); |
JP Abgrall | fa6f46d | 2011-06-17 23:17:28 -0700 | [diff] [blame] | 398 | } |
| 399 | return res; |
| 400 | } |
JP Abgrall | 4a5f5ca | 2011-06-15 18:37:39 -0700 | [diff] [blame] | 401 | |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 402 | int BandwidthController::setInterfaceSharedQuota(const char *iface, int64_t maxBytes) { |
JP Abgrall | 4a5f5ca | 2011-06-15 18:37:39 -0700 | [diff] [blame] | 403 | char cmd[MAX_CMD_LEN]; |
| 404 | char ifn[MAX_IFACENAME_LEN]; |
JP Abgrall | fa6f46d | 2011-06-17 23:17:28 -0700 | [diff] [blame] | 405 | int res = 0; |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 406 | std::string quotaCmd; |
JP Abgrall | 8a93272 | 2011-07-13 19:17:35 -0700 | [diff] [blame^] | 407 | std::string ifaceName; |
| 408 | ; |
JP Abgrall | bfa7466 | 2011-06-29 19:23:04 -0700 | [diff] [blame] | 409 | const char *costName = "shared"; |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 410 | std::list<std::string>::iterator it; |
JP Abgrall | 4a5f5ca | 2011-06-15 18:37:39 -0700 | [diff] [blame] | 411 | |
JP Abgrall | 8a93272 | 2011-07-13 19:17:35 -0700 | [diff] [blame^] | 412 | if (!maxBytes) { |
| 413 | /* Don't talk about -1, deprecate it. */ |
| 414 | LOGE("Invalid bytes value. 1..max_int64."); |
| 415 | return -1; |
| 416 | } |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 417 | if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) { |
| 418 | LOGE("Interface name longer than %d", MAX_IFACENAME_LEN); |
| 419 | return -1; |
| 420 | } |
| 421 | ifaceName = ifn; |
JP Abgrall | 4a5f5ca | 2011-06-15 18:37:39 -0700 | [diff] [blame] | 422 | |
| 423 | if (maxBytes == -1) { |
JP Abgrall | fa6f46d | 2011-06-17 23:17:28 -0700 | [diff] [blame] | 424 | return removeInterfaceSharedQuota(ifn); |
JP Abgrall | 4a5f5ca | 2011-06-15 18:37:39 -0700 | [diff] [blame] | 425 | } |
| 426 | |
| 427 | /* Insert ingress quota. */ |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 428 | for (it = sharedQuotaIfaces.begin(); it != sharedQuotaIfaces.end(); it++) { |
| 429 | if (*it == ifaceName) |
JP Abgrall | fa6f46d | 2011-06-17 23:17:28 -0700 | [diff] [blame] | 430 | break; |
JP Abgrall | 4a5f5ca | 2011-06-15 18:37:39 -0700 | [diff] [blame] | 431 | } |
JP Abgrall | fa6f46d | 2011-06-17 23:17:28 -0700 | [diff] [blame] | 432 | |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 433 | if (it == sharedQuotaIfaces.end()) { |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 434 | res |= prepCostlyIface(ifn, QuotaShared); |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 435 | if (sharedQuotaIfaces.empty()) { |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 436 | quotaCmd = makeIptablesQuotaCmd(IptOpInsert, costName, maxBytes); |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 437 | res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd); |
JP Abgrall | 4a5f5ca | 2011-06-15 18:37:39 -0700 | [diff] [blame] | 438 | if (res) { |
JP Abgrall | 8a93272 | 2011-07-13 19:17:35 -0700 | [diff] [blame^] | 439 | LOGE("Failed set quota rule"); |
JP Abgrall | fa6f46d | 2011-06-17 23:17:28 -0700 | [diff] [blame] | 440 | goto fail; |
JP Abgrall | 4a5f5ca | 2011-06-15 18:37:39 -0700 | [diff] [blame] | 441 | } |
JP Abgrall | fa6f46d | 2011-06-17 23:17:28 -0700 | [diff] [blame] | 442 | sharedQuotaBytes = maxBytes; |
| 443 | } |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 444 | sharedQuotaIfaces.push_front(ifaceName); |
JP Abgrall | fa6f46d | 2011-06-17 23:17:28 -0700 | [diff] [blame] | 445 | |
| 446 | } |
| 447 | |
| 448 | if (maxBytes != sharedQuotaBytes) { |
JP Abgrall | 8a93272 | 2011-07-13 19:17:35 -0700 | [diff] [blame^] | 449 | res |= updateQuota(costName, maxBytes); |
JP Abgrall | fa6f46d | 2011-06-17 23:17:28 -0700 | [diff] [blame] | 450 | if (res) { |
JP Abgrall | 8a93272 | 2011-07-13 19:17:35 -0700 | [diff] [blame^] | 451 | LOGE("Failed update quota for %s", costName); |
JP Abgrall | fa6f46d | 2011-06-17 23:17:28 -0700 | [diff] [blame] | 452 | goto fail; |
| 453 | } |
| 454 | sharedQuotaBytes = maxBytes; |
JP Abgrall | 4a5f5ca | 2011-06-15 18:37:39 -0700 | [diff] [blame] | 455 | } |
| 456 | return 0; |
JP Abgrall | fa6f46d | 2011-06-17 23:17:28 -0700 | [diff] [blame] | 457 | |
| 458 | fail: |
JP Abgrall | 4a5f5ca | 2011-06-15 18:37:39 -0700 | [diff] [blame] | 459 | /* |
JP Abgrall | 4a5f5ca | 2011-06-15 18:37:39 -0700 | [diff] [blame] | 460 | * TODO(jpa): once we get rid of iptables in favor of rtnetlink, reparse |
| 461 | * rules in the kernel to see which ones need cleaning up. |
JP Abgrall | fa6f46d | 2011-06-17 23:17:28 -0700 | [diff] [blame] | 462 | * For now callers needs to choose if they want to "ndc bandwidth enable" |
| 463 | * which resets everything. |
JP Abgrall | 4a5f5ca | 2011-06-15 18:37:39 -0700 | [diff] [blame] | 464 | */ |
JP Abgrall | fa6f46d | 2011-06-17 23:17:28 -0700 | [diff] [blame] | 465 | removeInterfaceSharedQuota(ifn); |
JP Abgrall | 4a5f5ca | 2011-06-15 18:37:39 -0700 | [diff] [blame] | 466 | return -1; |
| 467 | } |
| 468 | |
JP Abgrall | 8a93272 | 2011-07-13 19:17:35 -0700 | [diff] [blame^] | 469 | /* It will also cleanup any shared alerts */ |
JP Abgrall | fa6f46d | 2011-06-17 23:17:28 -0700 | [diff] [blame] | 470 | int BandwidthController::removeInterfaceSharedQuota(const char *iface) { |
JP Abgrall | 4a5f5ca | 2011-06-15 18:37:39 -0700 | [diff] [blame] | 471 | char ifn[MAX_IFACENAME_LEN]; |
JP Abgrall | fa6f46d | 2011-06-17 23:17:28 -0700 | [diff] [blame] | 472 | int res = 0; |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 473 | std::string ifaceName; |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 474 | std::list<std::string>::iterator it; |
JP Abgrall | bfa7466 | 2011-06-29 19:23:04 -0700 | [diff] [blame] | 475 | const char *costName = "shared"; |
JP Abgrall | 4a5f5ca | 2011-06-15 18:37:39 -0700 | [diff] [blame] | 476 | |
JP Abgrall | 8a93272 | 2011-07-13 19:17:35 -0700 | [diff] [blame^] | 477 | if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) { |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 478 | LOGE("Interface name longer than %d", MAX_IFACENAME_LEN); |
| 479 | return -1; |
| 480 | } |
JP Abgrall | 8a93272 | 2011-07-13 19:17:35 -0700 | [diff] [blame^] | 481 | ifaceName = ifn; |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 482 | |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 483 | for (it = sharedQuotaIfaces.begin(); it != sharedQuotaIfaces.end(); it++) { |
| 484 | if (*it == ifaceName) |
JP Abgrall | fa6f46d | 2011-06-17 23:17:28 -0700 | [diff] [blame] | 485 | break; |
JP Abgrall | 4a5f5ca | 2011-06-15 18:37:39 -0700 | [diff] [blame] | 486 | } |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 487 | if (it == sharedQuotaIfaces.end()) { |
JP Abgrall | 8a93272 | 2011-07-13 19:17:35 -0700 | [diff] [blame^] | 488 | LOGE("No such iface %s to delete", ifn); |
JP Abgrall | fa6f46d | 2011-06-17 23:17:28 -0700 | [diff] [blame] | 489 | return -1; |
JP Abgrall | 4a5f5ca | 2011-06-15 18:37:39 -0700 | [diff] [blame] | 490 | } |
JP Abgrall | fa6f46d | 2011-06-17 23:17:28 -0700 | [diff] [blame] | 491 | |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 492 | res |= cleanupCostlyIface(ifn, QuotaShared); |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 493 | sharedQuotaIfaces.erase(it); |
JP Abgrall | fa6f46d | 2011-06-17 23:17:28 -0700 | [diff] [blame] | 494 | |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 495 | if (sharedQuotaIfaces.empty()) { |
JP Abgrall | fa6f46d | 2011-06-17 23:17:28 -0700 | [diff] [blame] | 496 | std::string quotaCmd; |
JP Abgrall | bfa7466 | 2011-06-29 19:23:04 -0700 | [diff] [blame] | 497 | quotaCmd = makeIptablesQuotaCmd(IptOpDelete, costName, sharedQuotaBytes); |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 498 | res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd); |
JP Abgrall | 8a93272 | 2011-07-13 19:17:35 -0700 | [diff] [blame^] | 499 | sharedQuotaBytes = 0; |
| 500 | if (sharedAlertBytes) { |
| 501 | removeSharedAlert(); |
| 502 | sharedAlertBytes = 0; |
| 503 | } |
JP Abgrall | fa6f46d | 2011-06-17 23:17:28 -0700 | [diff] [blame] | 504 | } |
JP Abgrall | 4a5f5ca | 2011-06-15 18:37:39 -0700 | [diff] [blame] | 505 | return res; |
| 506 | } |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 507 | |
| 508 | int BandwidthController::setInterfaceQuota(const char *iface, int64_t maxBytes) { |
| 509 | char ifn[MAX_IFACENAME_LEN]; |
| 510 | int res = 0; |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 511 | std::string ifaceName; |
| 512 | const char *costName; |
| 513 | std::list<QuotaInfo>::iterator it; |
| 514 | std::string quotaCmd; |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 515 | |
JP Abgrall | 8a93272 | 2011-07-13 19:17:35 -0700 | [diff] [blame^] | 516 | if (!maxBytes) { |
| 517 | /* Don't talk about -1, deprecate it. */ |
| 518 | LOGE("Invalid bytes value. 1..max_int64."); |
| 519 | return -1; |
| 520 | } |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 521 | if (maxBytes == -1) { |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 522 | return removeInterfaceQuota(iface); |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 523 | } |
| 524 | |
JP Abgrall | 8a93272 | 2011-07-13 19:17:35 -0700 | [diff] [blame^] | 525 | if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) { |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 526 | LOGE("Interface name longer than %d", MAX_IFACENAME_LEN); |
| 527 | return -1; |
| 528 | } |
| 529 | ifaceName = ifn; |
| 530 | costName = iface; |
| 531 | |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 532 | /* Insert ingress quota. */ |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 533 | for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) { |
JP Abgrall | 8a93272 | 2011-07-13 19:17:35 -0700 | [diff] [blame^] | 534 | if (it->ifaceName == ifaceName) |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 535 | break; |
| 536 | } |
| 537 | |
| 538 | if (it == quotaIfaces.end()) { |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 539 | res |= prepCostlyIface(ifn, QuotaUnique); |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 540 | quotaCmd = makeIptablesQuotaCmd(IptOpInsert, costName, maxBytes); |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 541 | res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd); |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 542 | if (res) { |
JP Abgrall | 8a93272 | 2011-07-13 19:17:35 -0700 | [diff] [blame^] | 543 | LOGE("Failed set quota rule"); |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 544 | goto fail; |
| 545 | } |
| 546 | |
JP Abgrall | 8a93272 | 2011-07-13 19:17:35 -0700 | [diff] [blame^] | 547 | quotaIfaces.push_front(QuotaInfo(ifaceName, maxBytes, 0)); |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 548 | |
| 549 | } else { |
JP Abgrall | 8a93272 | 2011-07-13 19:17:35 -0700 | [diff] [blame^] | 550 | res |= updateQuota(costName, maxBytes); |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 551 | if (res) { |
JP Abgrall | 8a93272 | 2011-07-13 19:17:35 -0700 | [diff] [blame^] | 552 | LOGE("Failed update quota for %s", iface); |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 553 | goto fail; |
| 554 | } |
JP Abgrall | 8a93272 | 2011-07-13 19:17:35 -0700 | [diff] [blame^] | 555 | it->quota = maxBytes; |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 556 | } |
| 557 | return 0; |
| 558 | |
| 559 | fail: |
| 560 | /* |
| 561 | * TODO(jpa): once we get rid of iptables in favor of rtnetlink, reparse |
| 562 | * rules in the kernel to see which ones need cleaning up. |
| 563 | * For now callers needs to choose if they want to "ndc bandwidth enable" |
| 564 | * which resets everything. |
| 565 | */ |
| 566 | removeInterfaceSharedQuota(ifn); |
| 567 | return -1; |
| 568 | } |
| 569 | |
JP Abgrall | 8a93272 | 2011-07-13 19:17:35 -0700 | [diff] [blame^] | 570 | int BandwidthController::getInterfaceSharedQuota(int64_t *bytes) { |
| 571 | return getInterfaceQuota("shared", bytes); |
| 572 | } |
| 573 | |
| 574 | int BandwidthController::getInterfaceQuota(const char *costName, int64_t *bytes) { |
| 575 | FILE *fp; |
| 576 | char *fname; |
| 577 | int scanRes; |
| 578 | |
| 579 | asprintf(&fname, "/proc/net/xt_quota/%s", costName); |
| 580 | fp = fopen(fname, "r"); |
| 581 | free(fname); |
| 582 | if (!fp) { |
| 583 | LOGE("Reading quota %s failed (%s)", costName, strerror(errno)); |
| 584 | return -1; |
| 585 | } |
| 586 | scanRes = fscanf(fp, "%lld", bytes); |
| 587 | LOGD("Read quota res=%d bytes=%lld", scanRes, *bytes); |
| 588 | fclose(fp); |
| 589 | return scanRes == 1 ? 0 : -1; |
| 590 | } |
| 591 | |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 592 | int BandwidthController::removeInterfaceQuota(const char *iface) { |
| 593 | |
| 594 | char ifn[MAX_IFACENAME_LEN]; |
| 595 | int res = 0; |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 596 | std::string ifaceName; |
| 597 | const char *costName; |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 598 | std::list<QuotaInfo>::iterator it; |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 599 | |
JP Abgrall | 8a93272 | 2011-07-13 19:17:35 -0700 | [diff] [blame^] | 600 | if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) { |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 601 | LOGE("Interface name longer than %d", MAX_IFACENAME_LEN); |
| 602 | return -1; |
| 603 | } |
| 604 | ifaceName = ifn; |
| 605 | costName = iface; |
| 606 | |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 607 | for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) { |
JP Abgrall | 8a93272 | 2011-07-13 19:17:35 -0700 | [diff] [blame^] | 608 | if (it->ifaceName == ifaceName) |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 609 | break; |
| 610 | } |
| 611 | |
| 612 | if (it == quotaIfaces.end()) { |
JP Abgrall | 8a93272 | 2011-07-13 19:17:35 -0700 | [diff] [blame^] | 613 | LOGE("No such iface %s to delete", ifn); |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 614 | return -1; |
| 615 | } |
| 616 | |
| 617 | /* This also removes the quota command of CostlyIface chain. */ |
JP Abgrall | 26e0d49 | 2011-06-24 19:21:51 -0700 | [diff] [blame] | 618 | res |= cleanupCostlyIface(ifn, QuotaUnique); |
JP Abgrall | 0dad7c2 | 2011-06-24 11:58:14 -0700 | [diff] [blame] | 619 | |
| 620 | quotaIfaces.erase(it); |
| 621 | |
| 622 | return res; |
| 623 | } |
JP Abgrall | 8a93272 | 2011-07-13 19:17:35 -0700 | [diff] [blame^] | 624 | |
| 625 | int BandwidthController::updateQuota(const char *quotaName, int64_t bytes) { |
| 626 | FILE *fp; |
| 627 | char *fname; |
| 628 | |
| 629 | asprintf(&fname, "/proc/net/xt_quota/%s", quotaName); |
| 630 | fp = fopen(fname, "w"); |
| 631 | free(fname); |
| 632 | if (!fp) { |
| 633 | LOGE("Updating quota %s failed (%s)", quotaName, strerror(errno)); |
| 634 | return -1; |
| 635 | } |
| 636 | fprintf(fp, "%lld\n", bytes); |
| 637 | fclose(fp); |
| 638 | return 0; |
| 639 | } |
| 640 | |
| 641 | int BandwidthController::runIptablesAlertCmd(IptOp op, const char *alertName, int64_t bytes) { |
| 642 | int res = 0; |
| 643 | const char *opFlag; |
| 644 | char *alertQuotaCmd; |
| 645 | |
| 646 | switch (op) { |
| 647 | case IptOpInsert: |
| 648 | opFlag = "-I"; |
| 649 | break; |
| 650 | case IptOpReplace: |
| 651 | opFlag = "-R"; |
| 652 | break; |
| 653 | default: |
| 654 | case IptOpDelete: |
| 655 | opFlag = "-D"; |
| 656 | break; |
| 657 | } |
| 658 | |
| 659 | asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, opFlag, "INPUT", bytes, alertName, alertName); |
| 660 | res |= runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd); |
| 661 | free(alertQuotaCmd); |
| 662 | asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, opFlag, "OUTPUT", bytes, alertName, alertName); |
| 663 | res |= runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd); |
| 664 | free(alertQuotaCmd); |
| 665 | return res; |
| 666 | } |
| 667 | |
| 668 | int BandwidthController::setGlobalAlert(int64_t bytes) { |
| 669 | char *alertQuotaCmd; |
| 670 | const char *alertName = "globalAlert"; |
| 671 | int res = 0; |
| 672 | |
| 673 | if (!bytes) { |
| 674 | LOGE("Invalid bytes value. 1..max_int64."); |
| 675 | return -1; |
| 676 | } |
| 677 | if (globalAlertBytes) { |
| 678 | res = updateQuota(alertName, bytes); |
| 679 | } else { |
| 680 | res = runIptablesAlertCmd(IptOpInsert, alertName, bytes); |
| 681 | } |
| 682 | globalAlertBytes = bytes; |
| 683 | return res; |
| 684 | } |
| 685 | |
| 686 | int BandwidthController::removeGlobalAlert(void) { |
| 687 | char *alertQuotaCmd; |
| 688 | |
| 689 | const char *alertName = "globalAlert"; |
| 690 | int res = 0; |
| 691 | |
| 692 | if (!globalAlertBytes) { |
| 693 | LOGE("No prior alert set"); |
| 694 | return -1; |
| 695 | } |
| 696 | res = runIptablesAlertCmd(IptOpDelete, alertName, globalAlertBytes); |
| 697 | globalAlertBytes = 0; |
| 698 | return res; |
| 699 | } |
| 700 | |
| 701 | int BandwidthController::setSharedAlert(int64_t bytes) { |
| 702 | if (!sharedQuotaBytes) { |
| 703 | LOGE("Need to have a prior shared quota set to set an alert"); |
| 704 | return -1; |
| 705 | } |
| 706 | if (!bytes) { |
| 707 | LOGE("Invalid bytes value. 1..max_int64."); |
| 708 | return -1; |
| 709 | } |
| 710 | return setCostlyAlert("shared", bytes, &sharedAlertBytes); |
| 711 | } |
| 712 | |
| 713 | int BandwidthController::removeSharedAlert(void) { |
| 714 | return removeCostlyAlert("shared", &sharedAlertBytes); |
| 715 | } |
| 716 | |
| 717 | int BandwidthController::setInterfaceAlert(const char *iface, int64_t bytes) { |
| 718 | std::list<QuotaInfo>::iterator it; |
| 719 | |
| 720 | if (!bytes) { |
| 721 | LOGE("Invalid bytes value. 1..max_int64."); |
| 722 | return -1; |
| 723 | } |
| 724 | for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) { |
| 725 | if (it->ifaceName == iface) |
| 726 | break; |
| 727 | } |
| 728 | |
| 729 | if (it == quotaIfaces.end()) { |
| 730 | LOGE("Need to have a prior interface quota set to set an alert"); |
| 731 | return -1; |
| 732 | } |
| 733 | |
| 734 | return setCostlyAlert(iface, bytes, &it->alert); |
| 735 | } |
| 736 | |
| 737 | int BandwidthController::removeInterfaceAlert(const char *iface) { |
| 738 | std::list<QuotaInfo>::iterator it; |
| 739 | |
| 740 | for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) { |
| 741 | if (it->ifaceName == iface) |
| 742 | break; |
| 743 | } |
| 744 | |
| 745 | if (it == quotaIfaces.end()) { |
| 746 | LOGE("No prior alert set for interface %s", iface); |
| 747 | return -1; |
| 748 | } |
| 749 | |
| 750 | return removeCostlyAlert(iface, &it->alert); |
| 751 | } |
| 752 | |
| 753 | int BandwidthController::setCostlyAlert(const char *costName, int64_t bytes, int64_t *alertBytes) { |
| 754 | char *alertQuotaCmd; |
| 755 | char *chainNameAndPos; |
| 756 | int res = 0; |
| 757 | char *alertName; |
| 758 | |
| 759 | if (!bytes) { |
| 760 | LOGE("Invalid bytes value. 1..max_int64."); |
| 761 | return -1; |
| 762 | } |
| 763 | asprintf(&alertName, "%sAlert", costName); |
| 764 | if (*alertBytes) { |
| 765 | res = updateQuota(alertName, *alertBytes); |
| 766 | } else { |
| 767 | asprintf(&chainNameAndPos, "costly_%s %d", costName, ALERT_RULE_POS_IN_COSTLY_CHAIN); |
| 768 | asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, "-I", chainNameAndPos, bytes, alertName, |
| 769 | alertName); |
| 770 | res |= runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd); |
| 771 | free(alertQuotaCmd); |
| 772 | free(chainNameAndPos); |
| 773 | } |
| 774 | *alertBytes = bytes; |
| 775 | free(alertName); |
| 776 | return res; |
| 777 | } |
| 778 | |
| 779 | int BandwidthController::removeCostlyAlert(const char *costName, int64_t *alertBytes) { |
| 780 | char *alertQuotaCmd; |
| 781 | char *chainName; |
| 782 | char *alertName; |
| 783 | int res = 0; |
| 784 | |
| 785 | asprintf(&alertName, "%sAlert", costName); |
| 786 | if (!*alertBytes) { |
| 787 | LOGE("No prior alert set for %s alert", costName); |
| 788 | return -1; |
| 789 | } |
| 790 | |
| 791 | asprintf(&chainName, "costly_%s", costName); |
| 792 | asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, "-D", chainName, *alertBytes, alertName, alertName); |
| 793 | res |= runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd); |
| 794 | free(alertQuotaCmd); |
| 795 | free(chainName); |
| 796 | |
| 797 | *alertBytes = 0; |
| 798 | free(alertName); |
| 799 | return res; |
| 800 | } |