• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  
17  // #define LOG_NDEBUG 0
18  
19  /*
20   * The CommandListener, FrameworkListener don't allow for
21   * multiple calls in parallel to reach the BandwidthController.
22   * If they ever were to allow it, then netd/ would need some tweaking.
23   */
24  
25  #include <errno.h>
26  #include <fcntl.h>
27  #include <stdio.h>
28  #include <stdlib.h>
29  #include <string.h>
30  #include <ctype.h>
31  
32  #define __STDC_FORMAT_MACROS 1
33  #include <inttypes.h>
34  
35  #include <sys/socket.h>
36  #include <sys/stat.h>
37  #include <sys/types.h>
38  #include <sys/wait.h>
39  
40  #include <linux/netlink.h>
41  #include <linux/rtnetlink.h>
42  #include <linux/pkt_sched.h>
43  
44  #define LOG_TAG "BandwidthController"
45  #include <cutils/log.h>
46  #include <cutils/properties.h>
47  #include <logwrap/logwrap.h>
48  
49  #include "NetdConstants.h"
50  #include "BandwidthController.h"
51  #include "NatController.h"  /* For LOCAL_TETHER_COUNTERS_CHAIN */
52  #include "ResponseCode.h"
53  
54  /* Alphabetical */
55  #define ALERT_IPT_TEMPLATE "%s %s -m quota2 ! --quota %" PRId64" --name %s"
56  const char BandwidthController::ALERT_GLOBAL_NAME[] = "globalAlert";
57  const char* BandwidthController::LOCAL_INPUT = "bw_INPUT";
58  const char* BandwidthController::LOCAL_FORWARD = "bw_FORWARD";
59  const char* BandwidthController::LOCAL_OUTPUT = "bw_OUTPUT";
60  const char* BandwidthController::LOCAL_RAW_PREROUTING = "bw_raw_PREROUTING";
61  const char* BandwidthController::LOCAL_MANGLE_POSTROUTING = "bw_mangle_POSTROUTING";
62  const int  BandwidthController::MAX_CMD_ARGS = 32;
63  const int  BandwidthController::MAX_CMD_LEN = 1024;
64  const int  BandwidthController::MAX_IFACENAME_LEN = 64;
65  const int  BandwidthController::MAX_IPT_OUTPUT_LINE_LEN = 256;
66  
67  /**
68   * Some comments about the rules:
69   *  * Ordering
70   *    - when an interface is marked as costly it should be INSERTED into the INPUT/OUTPUT chains.
71   *      E.g. "-I bw_INPUT -i rmnet0 --jump costly"
72   *    - quota'd rules in the costly chain should be before bw_penalty_box lookups.
73   *    - bw_happy_box rejects everything by default.
74   *    - the qtaguid counting is done at the end of the bw_INPUT/bw_OUTPUT user chains.
75   *
76   * * global quota vs per interface quota
77   *   - global quota for all costly interfaces uses a single costly chain:
78   *    . initial rules
79   *      iptables -N bw_costly_shared
80   *      iptables -I bw_INPUT -i iface0 --jump bw_costly_shared
81   *      iptables -I bw_OUTPUT -o iface0 --jump bw_costly_shared
82   *      iptables -I bw_costly_shared -m quota \! --quota 500000 \
83   *          --jump REJECT --reject-with icmp-net-prohibited
84   *      iptables -A bw_costly_shared --jump bw_penalty_box
85   *      If the happy box is enabled,
86   *        iptables -A bw_penalty_box --jump bw_happy_box
87   *
88   *    . adding a new iface to this, E.g.:
89   *      iptables -I bw_INPUT -i iface1 --jump bw_costly_shared
90   *      iptables -I bw_OUTPUT -o iface1 --jump bw_costly_shared
91   *
92   *   - quota per interface. This is achieve by having "costly" chains per quota.
93   *     E.g. adding a new costly interface iface0 with its own quota:
94   *      iptables -N bw_costly_iface0
95   *      iptables -I bw_INPUT -i iface0 --jump bw_costly_iface0
96   *      iptables -I bw_OUTPUT -o iface0 --jump bw_costly_iface0
97   *      iptables -A bw_costly_iface0 -m quota \! --quota 500000 \
98   *          --jump REJECT --reject-with icmp-port-unreachable
99   *      iptables -A bw_costly_iface0 --jump bw_penalty_box
100   *
101   * * bw_penalty_box handling:
102   *  - only one bw_penalty_box for all interfaces
103   *   E.g  Adding an app, it has to preserve the appened bw_happy_box, so "-I":
104   *    iptables -I bw_penalty_box -m owner --uid-owner app_3 \
105   *        --jump REJECT --reject-with icmp-port-unreachable
106   *
107   * * bw_happy_box handling:
108   *  - The bw_happy_box goes at the end of the penalty box.
109   *   E.g  Adding a happy app,
110   *    iptables -I bw_happy_box -m owner --uid-owner app_3 \
111   *        --jump RETURN
112   */
113  const char *BandwidthController::IPT_FLUSH_COMMANDS[] = {
114      /*
115       * Cleanup rules.
116       * Should normally include bw_costly_<iface>, but we rely on the way they are setup
117       * to allow coexistance.
118       */
119      "-F bw_INPUT",
120      "-F bw_OUTPUT",
121      "-F bw_FORWARD",
122      "-F bw_happy_box",
123      "-F bw_penalty_box",
124      "-F bw_costly_shared",
125  
126      "-t raw -F bw_raw_PREROUTING",
127      "-t mangle -F bw_mangle_POSTROUTING",
128  };
129  
130  /* The cleanup commands assume flushing has been done. */
131  const char *BandwidthController::IPT_CLEANUP_COMMANDS[] = {
132      "-X bw_happy_box",
133      "-X bw_penalty_box",
134      "-X bw_costly_shared",
135  };
136  
137  const char *BandwidthController::IPT_SETUP_COMMANDS[] = {
138      "-N bw_happy_box",
139      "-N bw_penalty_box",
140      "-N bw_costly_shared",
141  };
142  
143  const char *BandwidthController::IPT_BASIC_ACCOUNTING_COMMANDS[] = {
144      "-A bw_INPUT -m owner --socket-exists", /* This is a tracking rule. */
145  
146      "-A bw_OUTPUT -m owner --socket-exists", /* This is a tracking rule. */
147  
148      "-A bw_costly_shared --jump bw_penalty_box",
149  
150      "-t raw -A bw_raw_PREROUTING -m owner --socket-exists", /* This is a tracking rule. */
151      "-t mangle -A bw_mangle_POSTROUTING -m owner --socket-exists", /* This is a tracking rule. */
152  };
153  
BandwidthController(void)154  BandwidthController::BandwidthController(void) {
155  }
156  
runIpxtablesCmd(const char * cmd,IptJumpOp jumpHandling,IptFailureLog failureHandling)157  int BandwidthController::runIpxtablesCmd(const char *cmd, IptJumpOp jumpHandling,
158                                           IptFailureLog failureHandling) {
159      int res = 0;
160  
161      ALOGV("runIpxtablesCmd(cmd=%s)", cmd);
162      res |= runIptablesCmd(cmd, jumpHandling, IptIpV4, failureHandling);
163      res |= runIptablesCmd(cmd, jumpHandling, IptIpV6, failureHandling);
164      return res;
165  }
166  
StrncpyAndCheck(char * buffer,const char * src,size_t buffSize)167  int BandwidthController::StrncpyAndCheck(char *buffer, const char *src, size_t buffSize) {
168  
169      memset(buffer, '\0', buffSize);  // strncpy() is not filling leftover with '\0'
170      strncpy(buffer, src, buffSize);
171      return buffer[buffSize - 1];
172  }
173  
runIptablesCmd(const char * cmd,IptJumpOp jumpHandling,IptIpVer iptVer,IptFailureLog failureHandling)174  int BandwidthController::runIptablesCmd(const char *cmd, IptJumpOp jumpHandling,
175                                          IptIpVer iptVer, IptFailureLog failureHandling) {
176      char buffer[MAX_CMD_LEN];
177      const char *argv[MAX_CMD_ARGS];
178      int argc = 0;
179      char *next = buffer;
180      char *tmp;
181      int res;
182      int status = 0;
183  
184      std::string fullCmd = cmd;
185  
186      switch (jumpHandling) {
187      case IptJumpReject:
188          /*
189           * Must be carefull what one rejects with, as uper layer protocols will just
190           * keep on hammering the device until the number of retries are done.
191           * For port-unreachable (default), TCP should consider as an abort (RFC1122).
192           */
193          fullCmd += " --jump REJECT";
194          break;
195      case IptJumpReturn:
196          fullCmd += " --jump RETURN";
197          break;
198      case IptJumpNoAdd:
199          break;
200      }
201  
202      fullCmd.insert(0, " ");
203      fullCmd.insert(0, iptVer == IptIpV4 ? IPTABLES_PATH : IP6TABLES_PATH);
204  
205      if (StrncpyAndCheck(buffer, fullCmd.c_str(), sizeof(buffer))) {
206          ALOGE("iptables command too long");
207          return -1;
208      }
209  
210      argc = 0;
211      while ((tmp = strsep(&next, " "))) {
212          argv[argc++] = tmp;
213          if (argc >= MAX_CMD_ARGS) {
214              ALOGE("iptables argument overflow");
215              return -1;
216          }
217      }
218  
219      argv[argc] = NULL;
220      res = android_fork_execvp(argc, (char **)argv, &status, false,
221              failureHandling == IptFailShow);
222      res = res || !WIFEXITED(status) || WEXITSTATUS(status);
223      if (res && failureHandling == IptFailShow) {
224        ALOGE("runIptablesCmd(): res=%d status=%d failed %s", res, status,
225              fullCmd.c_str());
226      }
227      return res;
228  }
229  
flushCleanTables(bool doClean)230  void BandwidthController::flushCleanTables(bool doClean) {
231      /* Flush and remove the bw_costly_<iface> tables */
232      flushExistingCostlyTables(doClean);
233  
234      /* Some of the initialCommands are allowed to fail */
235      runCommands(sizeof(IPT_FLUSH_COMMANDS) / sizeof(char*),
236              IPT_FLUSH_COMMANDS, RunCmdFailureOk);
237  
238      if (doClean) {
239          runCommands(sizeof(IPT_CLEANUP_COMMANDS) / sizeof(char*),
240                  IPT_CLEANUP_COMMANDS, RunCmdFailureOk);
241      }
242  }
243  
setupIptablesHooks(void)244  int BandwidthController::setupIptablesHooks(void) {
245  
246      /* flush+clean is allowed to fail */
247      flushCleanTables(true);
248      runCommands(sizeof(IPT_SETUP_COMMANDS) / sizeof(char*),
249              IPT_SETUP_COMMANDS, RunCmdFailureBad);
250  
251      return 0;
252  }
253  
enableBandwidthControl(bool force)254  int BandwidthController::enableBandwidthControl(bool force) {
255      int res;
256      char value[PROPERTY_VALUE_MAX];
257  
258      if (!force) {
259              property_get("persist.bandwidth.enable", value, "1");
260              if (!strcmp(value, "0"))
261                      return 0;
262      }
263  
264      /* Let's pretend we started from scratch ... */
265      sharedQuotaIfaces.clear();
266      quotaIfaces.clear();
267      naughtyAppUids.clear();
268      niceAppUids.clear();
269      globalAlertBytes = 0;
270      globalAlertTetherCount = 0;
271      sharedQuotaBytes = sharedAlertBytes = 0;
272  
273      flushCleanTables(false);
274      res = runCommands(sizeof(IPT_BASIC_ACCOUNTING_COMMANDS) / sizeof(char*),
275              IPT_BASIC_ACCOUNTING_COMMANDS, RunCmdFailureBad);
276  
277      return res;
278  
279  }
280  
disableBandwidthControl(void)281  int BandwidthController::disableBandwidthControl(void) {
282  
283      flushCleanTables(false);
284      return 0;
285  }
286  
runCommands(int numCommands,const char * commands[],RunCmdErrHandling cmdErrHandling)287  int BandwidthController::runCommands(int numCommands, const char *commands[],
288                                       RunCmdErrHandling cmdErrHandling) {
289      int res = 0;
290      IptFailureLog failureLogging = IptFailShow;
291      if (cmdErrHandling == RunCmdFailureOk) {
292          failureLogging = IptFailHide;
293      }
294      ALOGV("runCommands(): %d commands", numCommands);
295      for (int cmdNum = 0; cmdNum < numCommands; cmdNum++) {
296          res = runIpxtablesCmd(commands[cmdNum], IptJumpNoAdd, failureLogging);
297          if (res && cmdErrHandling != RunCmdFailureOk)
298              return res;
299      }
300      return 0;
301  }
302  
makeIptablesSpecialAppCmd(IptOp op,int uid,const char * chain)303  std::string BandwidthController::makeIptablesSpecialAppCmd(IptOp op, int uid, const char *chain) {
304      std::string res;
305      char *buff;
306      const char *opFlag;
307  
308      switch (op) {
309      case IptOpInsert:
310          opFlag = "-I";
311          break;
312      case IptOpAppend:
313          ALOGE("Append op not supported for %s uids", chain);
314          res = "";
315          return res;
316          break;
317      case IptOpReplace:
318          opFlag = "-R";
319          break;
320      default:
321      case IptOpDelete:
322          opFlag = "-D";
323          break;
324      }
325      asprintf(&buff, "%s %s -m owner --uid-owner %d", opFlag, chain, uid);
326      res = buff;
327      free(buff);
328      return res;
329  }
330  
enableHappyBox(void)331  int BandwidthController::enableHappyBox(void) {
332      char cmd[MAX_CMD_LEN];
333      int res = 0;
334  
335      /*
336       * We tentatively delete before adding, which helps recovering
337       * from bad states (e.g. netd died).
338       */
339  
340      /* Should not exist, but ignore result if already there. */
341      snprintf(cmd, sizeof(cmd), "-N bw_happy_box");
342      runIpxtablesCmd(cmd, IptJumpNoAdd);
343  
344      /* Should be empty, but clear in case something was wrong. */
345      niceAppUids.clear();
346      snprintf(cmd, sizeof(cmd), "-F bw_happy_box");
347      res |= runIpxtablesCmd(cmd, IptJumpNoAdd);
348  
349      snprintf(cmd, sizeof(cmd), "-D bw_penalty_box -j bw_happy_box");
350      runIpxtablesCmd(cmd, IptJumpNoAdd);
351      snprintf(cmd, sizeof(cmd), "-A bw_penalty_box -j bw_happy_box");
352      res |= runIpxtablesCmd(cmd, IptJumpNoAdd);
353  
354      /* Reject. Defaulting to prot-unreachable */
355      snprintf(cmd, sizeof(cmd), "-D bw_happy_box -j REJECT");
356      runIpxtablesCmd(cmd, IptJumpNoAdd);
357      snprintf(cmd, sizeof(cmd), "-A bw_happy_box -j REJECT");
358      res |= runIpxtablesCmd(cmd, IptJumpNoAdd);
359  
360      return res;
361  }
362  
disableHappyBox(void)363  int BandwidthController::disableHappyBox(void) {
364      char cmd[MAX_CMD_LEN];
365  
366      /* Best effort */
367      snprintf(cmd, sizeof(cmd), "-D bw_penalty_box -j bw_happy_box");
368      runIpxtablesCmd(cmd, IptJumpNoAdd);
369      niceAppUids.clear();
370      snprintf(cmd, sizeof(cmd), "-F bw_happy_box");
371      runIpxtablesCmd(cmd, IptJumpNoAdd);
372      snprintf(cmd, sizeof(cmd), "-X bw_happy_box");
373      runIpxtablesCmd(cmd, IptJumpNoAdd);
374  
375      return 0;
376  }
377  
addNaughtyApps(int numUids,char * appUids[])378  int BandwidthController::addNaughtyApps(int numUids, char *appUids[]) {
379      return manipulateNaughtyApps(numUids, appUids, SpecialAppOpAdd);
380  }
381  
removeNaughtyApps(int numUids,char * appUids[])382  int BandwidthController::removeNaughtyApps(int numUids, char *appUids[]) {
383      return manipulateNaughtyApps(numUids, appUids, SpecialAppOpRemove);
384  }
385  
addNiceApps(int numUids,char * appUids[])386  int BandwidthController::addNiceApps(int numUids, char *appUids[]) {
387      return manipulateNiceApps(numUids, appUids, SpecialAppOpAdd);
388  }
389  
removeNiceApps(int numUids,char * appUids[])390  int BandwidthController::removeNiceApps(int numUids, char *appUids[]) {
391      return manipulateNiceApps(numUids, appUids, SpecialAppOpRemove);
392  }
393  
manipulateNaughtyApps(int numUids,char * appStrUids[],SpecialAppOp appOp)394  int BandwidthController::manipulateNaughtyApps(int numUids, char *appStrUids[], SpecialAppOp appOp) {
395      return manipulateSpecialApps(numUids, appStrUids, "bw_penalty_box", naughtyAppUids, IptJumpReject, appOp);
396  }
397  
manipulateNiceApps(int numUids,char * appStrUids[],SpecialAppOp appOp)398  int BandwidthController::manipulateNiceApps(int numUids, char *appStrUids[], SpecialAppOp appOp) {
399      return manipulateSpecialApps(numUids, appStrUids, "bw_happy_box", niceAppUids, IptJumpReturn, appOp);
400  }
401  
402  
manipulateSpecialApps(int numUids,char * appStrUids[],const char * chain,std::list<int> & specialAppUids,IptJumpOp jumpHandling,SpecialAppOp appOp)403  int BandwidthController::manipulateSpecialApps(int numUids, char *appStrUids[],
404                                                 const char *chain,
405                                                 std::list<int /*appUid*/> &specialAppUids,
406                                                 IptJumpOp jumpHandling, SpecialAppOp appOp) {
407  
408      int uidNum;
409      const char *failLogTemplate;
410      IptOp op;
411      int appUids[numUids];
412      std::string iptCmd;
413      std::list<int /*uid*/>::iterator it;
414  
415      switch (appOp) {
416      case SpecialAppOpAdd:
417          op = IptOpInsert;
418          failLogTemplate = "Failed to add app uid %s(%d) to %s.";
419          break;
420      case SpecialAppOpRemove:
421          op = IptOpDelete;
422          failLogTemplate = "Failed to delete app uid %s(%d) from %s box.";
423          break;
424      default:
425          ALOGE("Unexpected app Op %d", appOp);
426          return -1;
427      }
428  
429      for (uidNum = 0; uidNum < numUids; uidNum++) {
430          char *end;
431          appUids[uidNum] = strtoul(appStrUids[uidNum], &end, 0);
432          if (*end || !*appStrUids[uidNum]) {
433              ALOGE(failLogTemplate, appStrUids[uidNum], appUids[uidNum], chain);
434              goto fail_parse;
435          }
436      }
437  
438      for (uidNum = 0; uidNum < numUids; uidNum++) {
439          int uid = appUids[uidNum];
440          for (it = specialAppUids.begin(); it != specialAppUids.end(); it++) {
441              if (*it == uid)
442                  break;
443          }
444          bool found = (it != specialAppUids.end());
445  
446          if (appOp == SpecialAppOpRemove) {
447              if (!found) {
448                  ALOGE("No such appUid %d to remove", uid);
449                  return -1;
450              }
451              specialAppUids.erase(it);
452          } else {
453              if (found) {
454                  ALOGE("appUid %d exists already", uid);
455                  return -1;
456              }
457              specialAppUids.push_front(uid);
458          }
459  
460          iptCmd = makeIptablesSpecialAppCmd(op, uid, chain);
461          if (runIpxtablesCmd(iptCmd.c_str(), jumpHandling)) {
462              ALOGE(failLogTemplate, appStrUids[uidNum], uid, chain);
463              goto fail_with_uidNum;
464          }
465      }
466      return 0;
467  
468  fail_with_uidNum:
469      /* Try to remove the uid that failed in any case*/
470      iptCmd = makeIptablesSpecialAppCmd(IptOpDelete, appUids[uidNum], chain);
471      runIpxtablesCmd(iptCmd.c_str(), jumpHandling);
472  fail_parse:
473      return -1;
474  }
475  
makeIptablesQuotaCmd(IptOp op,const char * costName,int64_t quota)476  std::string BandwidthController::makeIptablesQuotaCmd(IptOp op, const char *costName, int64_t quota) {
477      std::string res;
478      char *buff;
479      const char *opFlag;
480  
481      ALOGV("makeIptablesQuotaCmd(%d, %" PRId64")", op, quota);
482  
483      switch (op) {
484      case IptOpInsert:
485          opFlag = "-I";
486          break;
487      case IptOpAppend:
488          opFlag = "-A";
489          break;
490      case IptOpReplace:
491          opFlag = "-R";
492          break;
493      default:
494      case IptOpDelete:
495          opFlag = "-D";
496          break;
497      }
498  
499      // The requried IP version specific --jump REJECT ... will be added later.
500      asprintf(&buff, "%s bw_costly_%s -m quota2 ! --quota %" PRId64" --name %s", opFlag, costName, quota,
501               costName);
502      res = buff;
503      free(buff);
504      return res;
505  }
506  
prepCostlyIface(const char * ifn,QuotaType quotaType)507  int BandwidthController::prepCostlyIface(const char *ifn, QuotaType quotaType) {
508      char cmd[MAX_CMD_LEN];
509      int res = 0, res1, res2;
510      int ruleInsertPos = 1;
511      std::string costString;
512      const char *costCString;
513  
514      /* The "-N costly" is created upfront, no need to handle it here. */
515      switch (quotaType) {
516      case QuotaUnique:
517          costString = "bw_costly_";
518          costString += ifn;
519          costCString = costString.c_str();
520          /*
521           * Flush the bw_costly_<iface> is allowed to fail in case it didn't exist.
522           * Creating a new one is allowed to fail in case it existed.
523           * This helps with netd restarts.
524           */
525          snprintf(cmd, sizeof(cmd), "-F %s", costCString);
526          res1 = runIpxtablesCmd(cmd, IptJumpNoAdd, IptFailHide);
527          snprintf(cmd, sizeof(cmd), "-N %s", costCString);
528          res2 = runIpxtablesCmd(cmd, IptJumpNoAdd, IptFailHide);
529          res = (res1 && res2) || (!res1 && !res2);
530  
531          snprintf(cmd, sizeof(cmd), "-A %s -j bw_penalty_box", costCString);
532          res |= runIpxtablesCmd(cmd, IptJumpNoAdd);
533          break;
534      case QuotaShared:
535          costCString = "bw_costly_shared";
536          break;
537      default:
538          ALOGE("Unexpected quotatype %d", quotaType);
539          return -1;
540      }
541  
542      if (globalAlertBytes) {
543          /* The alert rule comes 1st */
544          ruleInsertPos = 2;
545      }
546  
547      snprintf(cmd, sizeof(cmd), "-D bw_INPUT -i %s --jump %s", ifn, costCString);
548      runIpxtablesCmd(cmd, IptJumpNoAdd, IptFailHide);
549  
550      snprintf(cmd, sizeof(cmd), "-I bw_INPUT %d -i %s --jump %s", ruleInsertPos, ifn, costCString);
551      res |= runIpxtablesCmd(cmd, IptJumpNoAdd);
552  
553      snprintf(cmd, sizeof(cmd), "-D bw_OUTPUT -o %s --jump %s", ifn, costCString);
554      runIpxtablesCmd(cmd, IptJumpNoAdd, IptFailHide);
555  
556      snprintf(cmd, sizeof(cmd), "-I bw_OUTPUT %d -o %s --jump %s", ruleInsertPos, ifn, costCString);
557      res |= runIpxtablesCmd(cmd, IptJumpNoAdd);
558      return res;
559  }
560  
cleanupCostlyIface(const char * ifn,QuotaType quotaType)561  int BandwidthController::cleanupCostlyIface(const char *ifn, QuotaType quotaType) {
562      char cmd[MAX_CMD_LEN];
563      int res = 0;
564      std::string costString;
565      const char *costCString;
566  
567      switch (quotaType) {
568      case QuotaUnique:
569          costString = "bw_costly_";
570          costString += ifn;
571          costCString = costString.c_str();
572          break;
573      case QuotaShared:
574          costCString = "bw_costly_shared";
575          break;
576      default:
577          ALOGE("Unexpected quotatype %d", quotaType);
578          return -1;
579      }
580  
581      snprintf(cmd, sizeof(cmd), "-D bw_INPUT -i %s --jump %s", ifn, costCString);
582      res |= runIpxtablesCmd(cmd, IptJumpNoAdd);
583      snprintf(cmd, sizeof(cmd), "-D bw_OUTPUT -o %s --jump %s", ifn, costCString);
584      res |= runIpxtablesCmd(cmd, IptJumpNoAdd);
585  
586      /* The "-N bw_costly_shared" is created upfront, no need to handle it here. */
587      if (quotaType == QuotaUnique) {
588          snprintf(cmd, sizeof(cmd), "-F %s", costCString);
589          res |= runIpxtablesCmd(cmd, IptJumpNoAdd);
590          snprintf(cmd, sizeof(cmd), "-X %s", costCString);
591          res |= runIpxtablesCmd(cmd, IptJumpNoAdd);
592      }
593      return res;
594  }
595  
setInterfaceSharedQuota(const char * iface,int64_t maxBytes)596  int BandwidthController::setInterfaceSharedQuota(const char *iface, int64_t maxBytes) {
597      char ifn[MAX_IFACENAME_LEN];
598      int res = 0;
599      std::string quotaCmd;
600      std::string ifaceName;
601      ;
602      const char *costName = "shared";
603      std::list<std::string>::iterator it;
604  
605      if (!maxBytes) {
606          /* Don't talk about -1, deprecate it. */
607          ALOGE("Invalid bytes value. 1..max_int64.");
608          return -1;
609      }
610      if (!isIfaceName(iface))
611          return -1;
612      if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
613          ALOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
614          return -1;
615      }
616      ifaceName = ifn;
617  
618      if (maxBytes == -1) {
619          return removeInterfaceSharedQuota(ifn);
620      }
621  
622      /* Insert ingress quota. */
623      for (it = sharedQuotaIfaces.begin(); it != sharedQuotaIfaces.end(); it++) {
624          if (*it == ifaceName)
625              break;
626      }
627  
628      if (it == sharedQuotaIfaces.end()) {
629          res |= prepCostlyIface(ifn, QuotaShared);
630          if (sharedQuotaIfaces.empty()) {
631              quotaCmd = makeIptablesQuotaCmd(IptOpInsert, costName, maxBytes);
632              res |= runIpxtablesCmd(quotaCmd.c_str(), IptJumpReject);
633              if (res) {
634                  ALOGE("Failed set quota rule");
635                  goto fail;
636              }
637              sharedQuotaBytes = maxBytes;
638          }
639          sharedQuotaIfaces.push_front(ifaceName);
640  
641      }
642  
643      if (maxBytes != sharedQuotaBytes) {
644          res |= updateQuota(costName, maxBytes);
645          if (res) {
646              ALOGE("Failed update quota for %s", costName);
647              goto fail;
648          }
649          sharedQuotaBytes = maxBytes;
650      }
651      return 0;
652  
653      fail:
654      /*
655       * TODO(jpa): once we get rid of iptables in favor of rtnetlink, reparse
656       * rules in the kernel to see which ones need cleaning up.
657       * For now callers needs to choose if they want to "ndc bandwidth enable"
658       * which resets everything.
659       */
660      removeInterfaceSharedQuota(ifn);
661      return -1;
662  }
663  
664  /* It will also cleanup any shared alerts */
removeInterfaceSharedQuota(const char * iface)665  int BandwidthController::removeInterfaceSharedQuota(const char *iface) {
666      char ifn[MAX_IFACENAME_LEN];
667      int res = 0;
668      std::string ifaceName;
669      std::list<std::string>::iterator it;
670      const char *costName = "shared";
671  
672      if (!isIfaceName(iface))
673          return -1;
674      if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
675          ALOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
676          return -1;
677      }
678      ifaceName = ifn;
679  
680      for (it = sharedQuotaIfaces.begin(); it != sharedQuotaIfaces.end(); it++) {
681          if (*it == ifaceName)
682              break;
683      }
684      if (it == sharedQuotaIfaces.end()) {
685          ALOGE("No such iface %s to delete", ifn);
686          return -1;
687      }
688  
689      res |= cleanupCostlyIface(ifn, QuotaShared);
690      sharedQuotaIfaces.erase(it);
691  
692      if (sharedQuotaIfaces.empty()) {
693          std::string quotaCmd;
694          quotaCmd = makeIptablesQuotaCmd(IptOpDelete, costName, sharedQuotaBytes);
695          res |= runIpxtablesCmd(quotaCmd.c_str(), IptJumpReject);
696          sharedQuotaBytes = 0;
697          if (sharedAlertBytes) {
698              removeSharedAlert();
699              sharedAlertBytes = 0;
700          }
701      }
702      return res;
703  }
704  
setInterfaceQuota(const char * iface,int64_t maxBytes)705  int BandwidthController::setInterfaceQuota(const char *iface, int64_t maxBytes) {
706      char ifn[MAX_IFACENAME_LEN];
707      int res = 0;
708      std::string ifaceName;
709      const char *costName;
710      std::list<QuotaInfo>::iterator it;
711      std::string quotaCmd;
712  
713      if (!isIfaceName(iface))
714          return -1;
715  
716      if (!maxBytes) {
717          /* Don't talk about -1, deprecate it. */
718          ALOGE("Invalid bytes value. 1..max_int64.");
719          return -1;
720      }
721      if (maxBytes == -1) {
722          return removeInterfaceQuota(iface);
723      }
724  
725      if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
726          ALOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
727          return -1;
728      }
729      ifaceName = ifn;
730      costName = iface;
731  
732      /* Insert ingress quota. */
733      for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
734          if (it->ifaceName == ifaceName)
735              break;
736      }
737  
738      if (it == quotaIfaces.end()) {
739          /* Preparing the iface adds a penalty/happy box check */
740          res |= prepCostlyIface(ifn, QuotaUnique);
741          /*
742           * The rejecting quota limit should go after the penalty/happy box checks
743           * or else a naughty app could just eat up the quota.
744           * So we append here.
745           */
746          quotaCmd = makeIptablesQuotaCmd(IptOpAppend, costName, maxBytes);
747          res |= runIpxtablesCmd(quotaCmd.c_str(), IptJumpReject);
748          if (res) {
749              ALOGE("Failed set quota rule");
750              goto fail;
751          }
752  
753          quotaIfaces.push_front(QuotaInfo(ifaceName, maxBytes, 0));
754  
755      } else {
756          res |= updateQuota(costName, maxBytes);
757          if (res) {
758              ALOGE("Failed update quota for %s", iface);
759              goto fail;
760          }
761          it->quota = maxBytes;
762      }
763      return 0;
764  
765      fail:
766      /*
767       * TODO(jpa): once we get rid of iptables in favor of rtnetlink, reparse
768       * rules in the kernel to see which ones need cleaning up.
769       * For now callers needs to choose if they want to "ndc bandwidth enable"
770       * which resets everything.
771       */
772      removeInterfaceSharedQuota(ifn);
773      return -1;
774  }
775  
getInterfaceSharedQuota(int64_t * bytes)776  int BandwidthController::getInterfaceSharedQuota(int64_t *bytes) {
777      return getInterfaceQuota("shared", bytes);
778  }
779  
getInterfaceQuota(const char * costName,int64_t * bytes)780  int BandwidthController::getInterfaceQuota(const char *costName, int64_t *bytes) {
781      FILE *fp;
782      char *fname;
783      int scanRes;
784  
785      if (!isIfaceName(costName))
786          return -1;
787  
788      asprintf(&fname, "/proc/net/xt_quota/%s", costName);
789      fp = fopen(fname, "r");
790      free(fname);
791      if (!fp) {
792          ALOGE("Reading quota %s failed (%s)", costName, strerror(errno));
793          return -1;
794      }
795      scanRes = fscanf(fp, "%" SCNd64, bytes);
796      ALOGV("Read quota res=%d bytes=%" PRId64, scanRes, *bytes);
797      fclose(fp);
798      return scanRes == 1 ? 0 : -1;
799  }
800  
removeInterfaceQuota(const char * iface)801  int BandwidthController::removeInterfaceQuota(const char *iface) {
802  
803      char ifn[MAX_IFACENAME_LEN];
804      int res = 0;
805      std::string ifaceName;
806      const char *costName;
807      std::list<QuotaInfo>::iterator it;
808  
809      if (!isIfaceName(iface))
810          return -1;
811      if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
812          ALOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
813          return -1;
814      }
815      ifaceName = ifn;
816      costName = iface;
817  
818      for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
819          if (it->ifaceName == ifaceName)
820              break;
821      }
822  
823      if (it == quotaIfaces.end()) {
824          ALOGE("No such iface %s to delete", ifn);
825          return -1;
826      }
827  
828      /* This also removes the quota command of CostlyIface chain. */
829      res |= cleanupCostlyIface(ifn, QuotaUnique);
830  
831      quotaIfaces.erase(it);
832  
833      return res;
834  }
835  
updateQuota(const char * quotaName,int64_t bytes)836  int BandwidthController::updateQuota(const char *quotaName, int64_t bytes) {
837      FILE *fp;
838      char *fname;
839  
840      if (!isIfaceName(quotaName)) {
841          ALOGE("updateQuota: Invalid quotaName \"%s\"", quotaName);
842          return -1;
843      }
844  
845      asprintf(&fname, "/proc/net/xt_quota/%s", quotaName);
846      fp = fopen(fname, "w");
847      free(fname);
848      if (!fp) {
849          ALOGE("Updating quota %s failed (%s)", quotaName, strerror(errno));
850          return -1;
851      }
852      fprintf(fp, "%" PRId64"\n", bytes);
853      fclose(fp);
854      return 0;
855  }
856  
runIptablesAlertCmd(IptOp op,const char * alertName,int64_t bytes)857  int BandwidthController::runIptablesAlertCmd(IptOp op, const char *alertName, int64_t bytes) {
858      int res = 0;
859      const char *opFlag;
860      char *alertQuotaCmd;
861  
862      switch (op) {
863      case IptOpInsert:
864          opFlag = "-I";
865          break;
866      case IptOpAppend:
867          opFlag = "-A";
868          break;
869      case IptOpReplace:
870          opFlag = "-R";
871          break;
872      default:
873      case IptOpDelete:
874          opFlag = "-D";
875          break;
876      }
877  
878      asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, opFlag, "bw_INPUT",
879          bytes, alertName);
880      res |= runIpxtablesCmd(alertQuotaCmd, IptJumpNoAdd);
881      free(alertQuotaCmd);
882      asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, opFlag, "bw_OUTPUT",
883          bytes, alertName);
884      res |= runIpxtablesCmd(alertQuotaCmd, IptJumpNoAdd);
885      free(alertQuotaCmd);
886      return res;
887  }
888  
runIptablesAlertFwdCmd(IptOp op,const char * alertName,int64_t bytes)889  int BandwidthController::runIptablesAlertFwdCmd(IptOp op, const char *alertName, int64_t bytes) {
890      int res = 0;
891      const char *opFlag;
892      char *alertQuotaCmd;
893  
894      switch (op) {
895      case IptOpInsert:
896          opFlag = "-I";
897          break;
898      case IptOpAppend:
899          opFlag = "-A";
900          break;
901      case IptOpReplace:
902          opFlag = "-R";
903          break;
904      default:
905      case IptOpDelete:
906          opFlag = "-D";
907          break;
908      }
909  
910      asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, opFlag, "bw_FORWARD",
911          bytes, alertName);
912      res = runIpxtablesCmd(alertQuotaCmd, IptJumpNoAdd);
913      free(alertQuotaCmd);
914      return res;
915  }
916  
setGlobalAlert(int64_t bytes)917  int BandwidthController::setGlobalAlert(int64_t bytes) {
918      const char *alertName = ALERT_GLOBAL_NAME;
919      int res = 0;
920  
921      if (!bytes) {
922          ALOGE("Invalid bytes value. 1..max_int64.");
923          return -1;
924      }
925      if (globalAlertBytes) {
926          res = updateQuota(alertName, bytes);
927      } else {
928          res = runIptablesAlertCmd(IptOpInsert, alertName, bytes);
929          if (globalAlertTetherCount) {
930              ALOGV("setGlobalAlert for %d tether", globalAlertTetherCount);
931              res |= runIptablesAlertFwdCmd(IptOpInsert, alertName, bytes);
932          }
933      }
934      globalAlertBytes = bytes;
935      return res;
936  }
937  
setGlobalAlertInForwardChain(void)938  int BandwidthController::setGlobalAlertInForwardChain(void) {
939      const char *alertName = ALERT_GLOBAL_NAME;
940      int res = 0;
941  
942      globalAlertTetherCount++;
943      ALOGV("setGlobalAlertInForwardChain(): %d tether", globalAlertTetherCount);
944  
945      /*
946       * If there is no globalAlert active we are done.
947       * If there is an active globalAlert but this is not the 1st
948       * tether, we are also done.
949       */
950      if (!globalAlertBytes || globalAlertTetherCount != 1) {
951          return 0;
952      }
953  
954      /* We only add the rule if this was the 1st tether added. */
955      res = runIptablesAlertFwdCmd(IptOpInsert, alertName, globalAlertBytes);
956      return res;
957  }
958  
removeGlobalAlert(void)959  int BandwidthController::removeGlobalAlert(void) {
960  
961      const char *alertName = ALERT_GLOBAL_NAME;
962      int res = 0;
963  
964      if (!globalAlertBytes) {
965          ALOGE("No prior alert set");
966          return -1;
967      }
968      res = runIptablesAlertCmd(IptOpDelete, alertName, globalAlertBytes);
969      if (globalAlertTetherCount) {
970          res |= runIptablesAlertFwdCmd(IptOpDelete, alertName, globalAlertBytes);
971      }
972      globalAlertBytes = 0;
973      return res;
974  }
975  
removeGlobalAlertInForwardChain(void)976  int BandwidthController::removeGlobalAlertInForwardChain(void) {
977      int res = 0;
978      const char *alertName = ALERT_GLOBAL_NAME;
979  
980      if (!globalAlertTetherCount) {
981          ALOGE("No prior alert set");
982          return -1;
983      }
984  
985      globalAlertTetherCount--;
986      /*
987       * If there is no globalAlert active we are done.
988       * If there is an active globalAlert but there are more
989       * tethers, we are also done.
990       */
991      if (!globalAlertBytes || globalAlertTetherCount >= 1) {
992          return 0;
993      }
994  
995      /* We only detete the rule if this was the last tether removed. */
996      res = runIptablesAlertFwdCmd(IptOpDelete, alertName, globalAlertBytes);
997      return res;
998  }
999  
setSharedAlert(int64_t bytes)1000  int BandwidthController::setSharedAlert(int64_t bytes) {
1001      if (!sharedQuotaBytes) {
1002          ALOGE("Need to have a prior shared quota set to set an alert");
1003          return -1;
1004      }
1005      if (!bytes) {
1006          ALOGE("Invalid bytes value. 1..max_int64.");
1007          return -1;
1008      }
1009      return setCostlyAlert("shared", bytes, &sharedAlertBytes);
1010  }
1011  
removeSharedAlert(void)1012  int BandwidthController::removeSharedAlert(void) {
1013      return removeCostlyAlert("shared", &sharedAlertBytes);
1014  }
1015  
setInterfaceAlert(const char * iface,int64_t bytes)1016  int BandwidthController::setInterfaceAlert(const char *iface, int64_t bytes) {
1017      std::list<QuotaInfo>::iterator it;
1018  
1019      if (!isIfaceName(iface)) {
1020          ALOGE("setInterfaceAlert: Invalid iface \"%s\"", iface);
1021          return -1;
1022      }
1023  
1024      if (!bytes) {
1025          ALOGE("Invalid bytes value. 1..max_int64.");
1026          return -1;
1027      }
1028      for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
1029          if (it->ifaceName == iface)
1030              break;
1031      }
1032  
1033      if (it == quotaIfaces.end()) {
1034          ALOGE("Need to have a prior interface quota set to set an alert");
1035          return -1;
1036      }
1037  
1038      return setCostlyAlert(iface, bytes, &it->alert);
1039  }
1040  
removeInterfaceAlert(const char * iface)1041  int BandwidthController::removeInterfaceAlert(const char *iface) {
1042      std::list<QuotaInfo>::iterator it;
1043  
1044      if (!isIfaceName(iface)) {
1045          ALOGE("removeInterfaceAlert: Invalid iface \"%s\"", iface);
1046          return -1;
1047      }
1048  
1049      for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
1050          if (it->ifaceName == iface)
1051              break;
1052      }
1053  
1054      if (it == quotaIfaces.end()) {
1055          ALOGE("No prior alert set for interface %s", iface);
1056          return -1;
1057      }
1058  
1059      return removeCostlyAlert(iface, &it->alert);
1060  }
1061  
setCostlyAlert(const char * costName,int64_t bytes,int64_t * alertBytes)1062  int BandwidthController::setCostlyAlert(const char *costName, int64_t bytes, int64_t *alertBytes) {
1063      char *alertQuotaCmd;
1064      char *chainName;
1065      int res = 0;
1066      char *alertName;
1067  
1068      if (!isIfaceName(costName)) {
1069          ALOGE("setCostlyAlert: Invalid costName \"%s\"", costName);
1070          return -1;
1071      }
1072  
1073      if (!bytes) {
1074          ALOGE("Invalid bytes value. 1..max_int64.");
1075          return -1;
1076      }
1077      asprintf(&alertName, "%sAlert", costName);
1078      if (*alertBytes) {
1079          res = updateQuota(alertName, *alertBytes);
1080      } else {
1081          asprintf(&chainName, "bw_costly_%s", costName);
1082          asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, "-A", chainName, bytes, alertName);
1083          res |= runIpxtablesCmd(alertQuotaCmd, IptJumpNoAdd);
1084          free(alertQuotaCmd);
1085          free(chainName);
1086      }
1087      *alertBytes = bytes;
1088      free(alertName);
1089      return res;
1090  }
1091  
removeCostlyAlert(const char * costName,int64_t * alertBytes)1092  int BandwidthController::removeCostlyAlert(const char *costName, int64_t *alertBytes) {
1093      char *alertQuotaCmd;
1094      char *chainName;
1095      char *alertName;
1096      int res = 0;
1097  
1098      if (!isIfaceName(costName)) {
1099          ALOGE("removeCostlyAlert: Invalid costName \"%s\"", costName);
1100          return -1;
1101      }
1102  
1103      asprintf(&alertName, "%sAlert", costName);
1104      if (!*alertBytes) {
1105          ALOGE("No prior alert set for %s alert", costName);
1106          return -1;
1107      }
1108  
1109      asprintf(&chainName, "bw_costly_%s", costName);
1110      asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, "-D", chainName, *alertBytes, alertName);
1111      res |= runIpxtablesCmd(alertQuotaCmd, IptJumpNoAdd);
1112      free(alertQuotaCmd);
1113      free(chainName);
1114  
1115      *alertBytes = 0;
1116      free(alertName);
1117      return res;
1118  }
1119  
1120  /*
1121   * Parse the ptks and bytes out of:
1122   *   Chain natctrl_tether_counters (4 references)
1123   *       pkts      bytes target     prot opt in     out     source               destination
1124   *         26     2373 RETURN     all  --  wlan0  rmnet0  0.0.0.0/0            0.0.0.0/0
1125   *         27     2002 RETURN     all  --  rmnet0 wlan0   0.0.0.0/0            0.0.0.0/0
1126   *       1040   107471 RETURN     all  --  bt-pan rmnet0  0.0.0.0/0            0.0.0.0/0
1127   *       1450  1708806 RETURN     all  --  rmnet0 bt-pan  0.0.0.0/0            0.0.0.0/0
1128   * It results in an error if invoked and no tethering counter rules exist. The constraint
1129   * helps detect complete parsing failure.
1130   */
parseForwardChainStats(SocketClient * cli,const TetherStats filter,FILE * fp,std::string & extraProcessingInfo)1131  int BandwidthController::parseForwardChainStats(SocketClient *cli, const TetherStats filter,
1132                                                  FILE *fp, std::string &extraProcessingInfo) {
1133      int res;
1134      char lineBuffer[MAX_IPT_OUTPUT_LINE_LEN];
1135      char iface0[MAX_IPT_OUTPUT_LINE_LEN];
1136      char iface1[MAX_IPT_OUTPUT_LINE_LEN];
1137      char rest[MAX_IPT_OUTPUT_LINE_LEN];
1138  
1139      TetherStats stats;
1140      char *buffPtr;
1141      int64_t packets, bytes;
1142      int statsFound = 0;
1143  
1144      bool filterPair = filter.intIface[0] && filter.extIface[0];
1145  
1146      char *filterMsg = filter.getStatsLine();
1147      ALOGV("filter: %s",  filterMsg);
1148      free(filterMsg);
1149  
1150      stats = filter;
1151  
1152      while (NULL != (buffPtr = fgets(lineBuffer, MAX_IPT_OUTPUT_LINE_LEN, fp))) {
1153          /* Clean up, so a failed parse can still print info */
1154          iface0[0] = iface1[0] = rest[0] = packets = bytes = 0;
1155          res = sscanf(buffPtr, "%" SCNd64" %" SCNd64" RETURN all -- %s %s 0.%s",
1156                  &packets, &bytes, iface0, iface1, rest);
1157          ALOGV("parse res=%d iface0=<%s> iface1=<%s> pkts=%" PRId64" bytes=%" PRId64" rest=<%s> orig line=<%s>", res,
1158               iface0, iface1, packets, bytes, rest, buffPtr);
1159          extraProcessingInfo += buffPtr;
1160  
1161          if (res != 5) {
1162              continue;
1163          }
1164          /*
1165           * The following assumes that the 1st rule has in:extIface out:intIface,
1166           * which is what NatController sets up.
1167           * If not filtering, the 1st match rx, and sets up the pair for the tx side.
1168           */
1169          if (filter.intIface[0] && filter.extIface[0]) {
1170              if (filter.intIface == iface0 && filter.extIface == iface1) {
1171                  ALOGV("2Filter RX iface_in=%s iface_out=%s rx_bytes=%" PRId64" rx_packets=%" PRId64" ", iface0, iface1, bytes, packets);
1172                  stats.rxPackets = packets;
1173                  stats.rxBytes = bytes;
1174              } else if (filter.intIface == iface1 && filter.extIface == iface0) {
1175                  ALOGV("2Filter TX iface_in=%s iface_out=%s rx_bytes=%" PRId64" rx_packets=%" PRId64" ", iface0, iface1, bytes, packets);
1176                  stats.txPackets = packets;
1177                  stats.txBytes = bytes;
1178              }
1179          } else if (filter.intIface[0] || filter.extIface[0]) {
1180              if (filter.intIface == iface0 || filter.extIface == iface1) {
1181                  ALOGV("1Filter RX iface_in=%s iface_out=%s rx_bytes=%" PRId64" rx_packets=%" PRId64" ", iface0, iface1, bytes, packets);
1182                  stats.intIface = iface0;
1183                  stats.extIface = iface1;
1184                  stats.rxPackets = packets;
1185                  stats.rxBytes = bytes;
1186              } else if (filter.intIface == iface1 || filter.extIface == iface0) {
1187                  ALOGV("1Filter TX iface_in=%s iface_out=%s rx_bytes=%" PRId64" rx_packets=%" PRId64" ", iface0, iface1, bytes, packets);
1188                  stats.intIface = iface1;
1189                  stats.extIface = iface0;
1190                  stats.txPackets = packets;
1191                  stats.txBytes = bytes;
1192              }
1193          } else /* if (!filter.intFace[0] && !filter.extIface[0]) */ {
1194              if (!stats.intIface[0]) {
1195                  ALOGV("0Filter RX iface_in=%s iface_out=%s rx_bytes=%" PRId64" rx_packets=%" PRId64" ", iface0, iface1, bytes, packets);
1196                  stats.intIface = iface0;
1197                  stats.extIface = iface1;
1198                  stats.rxPackets = packets;
1199                  stats.rxBytes = bytes;
1200              } else if (stats.intIface == iface1 && stats.extIface == iface0) {
1201                  ALOGV("0Filter TX iface_in=%s iface_out=%s rx_bytes=%" PRId64" rx_packets=%" PRId64" ", iface0, iface1, bytes, packets);
1202                  stats.txPackets = packets;
1203                  stats.txBytes = bytes;
1204              }
1205          }
1206          if (stats.rxBytes != -1 && stats.txBytes != -1) {
1207              ALOGV("rx_bytes=%" PRId64" tx_bytes=%" PRId64" filterPair=%d", stats.rxBytes, stats.txBytes, filterPair);
1208              /* Send out stats, and prep for the next if needed. */
1209              char *msg = stats.getStatsLine();
1210              if (filterPair) {
1211                  cli->sendMsg(ResponseCode::TetheringStatsResult, msg, false);
1212                  return 0;
1213              } else {
1214                  cli->sendMsg(ResponseCode::TetheringStatsListResult, msg, false);
1215                  stats = filter;
1216              }
1217              free(msg);
1218              statsFound++;
1219          }
1220      }
1221  
1222      /* It is always an error to find only one side of the stats. */
1223      /* It is an error to find nothing when not filtering. */
1224      if (((stats.rxBytes == -1) != (stats.txBytes == -1)) ||
1225          (!statsFound && !filterPair)) {
1226          return -1;
1227      }
1228      cli->sendMsg(ResponseCode::CommandOkay, "Tethering stats list completed", false);
1229      return 0;
1230  }
1231  
getStatsLine(void) const1232  char *BandwidthController::TetherStats::getStatsLine(void) const {
1233      char *msg;
1234      asprintf(&msg, "%s %s %" PRId64" %" PRId64" %" PRId64" %" PRId64, intIface.c_str(), extIface.c_str(),
1235              rxBytes, rxPackets, txBytes, txPackets);
1236      return msg;
1237  }
1238  
getTetherStats(SocketClient * cli,TetherStats & stats,std::string & extraProcessingInfo)1239  int BandwidthController::getTetherStats(SocketClient *cli, TetherStats &stats, std::string &extraProcessingInfo) {
1240      int res;
1241      std::string fullCmd;
1242      FILE *iptOutput;
1243  
1244      /*
1245       * Why not use some kind of lib to talk to iptables?
1246       * Because the only libs are libiptc and libip6tc in iptables, and they are
1247       * not easy to use. They require the known iptables match modules to be
1248       * preloaded/linked, and require apparently a lot of wrapper code to get
1249       * the wanted info.
1250       */
1251      fullCmd = IPTABLES_PATH;
1252      fullCmd += " -nvx -L ";
1253      fullCmd += NatController::LOCAL_TETHER_COUNTERS_CHAIN;
1254      iptOutput = popen(fullCmd.c_str(), "r");
1255      if (!iptOutput) {
1256              ALOGE("Failed to run %s err=%s", fullCmd.c_str(), strerror(errno));
1257              extraProcessingInfo += "Failed to run iptables.";
1258          return -1;
1259      }
1260      res = parseForwardChainStats(cli, stats, iptOutput, extraProcessingInfo);
1261      pclose(iptOutput);
1262  
1263      /* Currently NatController doesn't do ipv6 tethering, so we are done. */
1264      return res;
1265  }
1266  
flushExistingCostlyTables(bool doClean)1267  void BandwidthController::flushExistingCostlyTables(bool doClean) {
1268      std::string fullCmd;
1269      FILE *iptOutput;
1270  
1271      /* Only lookup ip4 table names as ip6 will have the same tables ... */
1272      fullCmd = IPTABLES_PATH;
1273      fullCmd += " -S";
1274      iptOutput = popen(fullCmd.c_str(), "r");
1275      if (!iptOutput) {
1276              ALOGE("Failed to run %s err=%s", fullCmd.c_str(), strerror(errno));
1277          return;
1278      }
1279      /* ... then flush/clean both ip4 and ip6 iptables. */
1280      parseAndFlushCostlyTables(iptOutput, doClean);
1281      pclose(iptOutput);
1282  }
1283  
parseAndFlushCostlyTables(FILE * fp,bool doRemove)1284  void BandwidthController::parseAndFlushCostlyTables(FILE *fp, bool doRemove) {
1285      int res;
1286      char lineBuffer[MAX_IPT_OUTPUT_LINE_LEN];
1287      char costlyIfaceName[MAX_IPT_OUTPUT_LINE_LEN];
1288      char cmd[MAX_CMD_LEN];
1289      char *buffPtr;
1290  
1291      while (NULL != (buffPtr = fgets(lineBuffer, MAX_IPT_OUTPUT_LINE_LEN, fp))) {
1292          costlyIfaceName[0] = '\0';   /* So that debugging output always works */
1293          res = sscanf(buffPtr, "-N bw_costly_%s", costlyIfaceName);
1294          ALOGV("parse res=%d costly=<%s> orig line=<%s>", res,
1295              costlyIfaceName, buffPtr);
1296          if (res != 1) {
1297              continue;
1298          }
1299          /* Exclusions: "shared" is not an ifacename */
1300          if (!strcmp(costlyIfaceName, "shared")) {
1301              continue;
1302          }
1303  
1304          snprintf(cmd, sizeof(cmd), "-F bw_costly_%s", costlyIfaceName);
1305          runIpxtablesCmd(cmd, IptJumpNoAdd, IptFailHide);
1306          if (doRemove) {
1307              snprintf(cmd, sizeof(cmd), "-X bw_costly_%s", costlyIfaceName);
1308              runIpxtablesCmd(cmd, IptJumpNoAdd, IptFailHide);
1309          }
1310      }
1311  }
1312