OSDN Git Service

Parse IPv6 tethering counters.
authorLorenzo Colitti <lorenzo@google.com>
Mon, 11 Jul 2016 02:36:25 +0000 (11:36 +0900)
committerLorenzo Colitti <lorenzo@google.com>
Tue, 12 Jul 2016 04:58:41 +0000 (13:58 +0900)
Bug: 9580643
Change-Id: Icbfd8c6480a4e14433004e90b71a104ae4da9c5d

server/BandwidthController.cpp
server/BandwidthControllerTest.cpp

index 5596784..09dc483 100644 (file)
@@ -1100,6 +1100,12 @@ void BandwidthController::addStats(TetherStatsList& statsList, const TetherStats
  *         27     2002 RETURN     all  --  rmnet0 wlan0   0.0.0.0/0            0.0.0.0/0
  *       1040   107471 RETURN     all  --  bt-pan rmnet0  0.0.0.0/0            0.0.0.0/0
  *       1450  1708806 RETURN     all  --  rmnet0 bt-pan  0.0.0.0/0            0.0.0.0/0
+ * or:
+ *   Chain natctrl_tether_counters (0 references)
+ *       pkts      bytes target     prot opt in     out     source               destination
+ *          0        0 RETURN     all      wlan0  rmnet_data0  ::/0                 ::/0
+ *          0        0 RETURN     all      rmnet_data0 wlan0   ::/0                 ::/0
+ *
  * It results in an error if invoked and no tethering counter rules exist. The constraint
  * helps detect complete parsing failure.
  */
@@ -1128,8 +1134,17 @@ int BandwidthController::addForwardChainStats(const TetherStats& filter,
     while (NULL != (buffPtr = fgets(lineBuffer, MAX_IPT_OUTPUT_LINE_LEN, fp))) {
         /* Clean up, so a failed parse can still print info */
         iface0[0] = iface1[0] = rest[0] = packets = bytes = 0;
-        res = sscanf(buffPtr, "%" SCNd64" %" SCNd64" RETURN all -- %s %s 0.%s",
-                &packets, &bytes, iface0, iface1, rest);
+        if (strstr(buffPtr, "0.0.0.0")) {
+            // IPv4 has -- indicating what to do with fragments...
+            //       26     2373 RETURN     all  --  wlan0  rmnet0  0.0.0.0/0            0.0.0.0/0
+            res = sscanf(buffPtr, "%" SCNd64" %" SCNd64" RETURN all -- %s %s 0.%s",
+                    &packets, &bytes, iface0, iface1, rest);
+        } else {
+            // ... but IPv6 does not.
+            //       26     2373 RETURN     all      wlan0  rmnet0  ::/0                 ::/0
+            res = sscanf(buffPtr, "%" SCNd64" %" SCNd64" RETURN all %s %s ::/%s",
+                    &packets, &bytes, iface0, iface1, rest);
+        }
         ALOGV("parse res=%d iface0=<%s> iface1=<%s> pkts=%" PRId64" bytes=%" PRId64" rest=<%s> orig line=<%s>", res,
              iface0, iface1, packets, bytes, rest, buffPtr);
         extraProcessingInfo += buffPtr;
@@ -1207,14 +1222,7 @@ char *BandwidthController::TetherStats::getStatsLine(void) const {
     return msg;
 }
 
-int BandwidthController::getTetherStats(SocketClient *cli, TetherStats& filter,
-                                        std::string &extraProcessingInfo) {
-    int res;
-    std::string fullCmd;
-    FILE *iptOutput;
-
-    TetherStatsList statsList;
-
+std::string getTetherStatsCommand(const char *binary) {
     /*
      * Why not use some kind of lib to talk to iptables?
      * Because the only libs are libiptc and libip6tc in iptables, and they are
@@ -1222,18 +1230,33 @@ int BandwidthController::getTetherStats(SocketClient *cli, TetherStats& filter,
      * preloaded/linked, and require apparently a lot of wrapper code to get
      * the wanted info.
      */
-    fullCmd = IPTABLES_PATH;
-    fullCmd += " -nvx -w -L ";
-    fullCmd += NatController::LOCAL_TETHER_COUNTERS_CHAIN;
-    iptOutput = popenFunction(fullCmd.c_str(), "r");
-    if (!iptOutput) {
-            ALOGE("Failed to run %s err=%s", fullCmd.c_str(), strerror(errno));
-            extraProcessingInfo += "Failed to run iptables.";
-        return -1;
-    }
+    return android::base::StringPrintf("%s -nvx -w -L %s", binary,
+                                       NatController::LOCAL_TETHER_COUNTERS_CHAIN);
+}
 
-    res = addForwardChainStats(filter, statsList, iptOutput, extraProcessingInfo);
-    pclose(iptOutput);
+int BandwidthController::getTetherStats(SocketClient *cli, TetherStats& filter,
+                                        std::string &extraProcessingInfo) {
+    int res = 0;
+    std::string fullCmd;
+    FILE *iptOutput;
+
+    TetherStatsList statsList;
+
+    for (const auto binary : {IPTABLES_PATH, IP6TABLES_PATH}) {
+        fullCmd = getTetherStatsCommand(binary);
+        iptOutput = popenFunction(fullCmd.c_str(), "r");
+        if (!iptOutput) {
+                ALOGE("Failed to run %s err=%s", fullCmd.c_str(), strerror(errno));
+                extraProcessingInfo += "Failed to run iptables.";
+            return -1;
+        }
+
+        res = addForwardChainStats(filter, statsList, iptOutput, extraProcessingInfo);
+        pclose(iptOutput);
+        if (res != 0) {
+            return res;
+        }
+    }
 
     if (filter.intIface[0] && filter.extIface[0] && statsList.size() == 1) {
         cli->sendMsg(ResponseCode::TetheringStatsResult, statsList[0].getStatsLine(), false);
@@ -1246,7 +1269,6 @@ int BandwidthController::getTetherStats(SocketClient *cli, TetherStats& filter,
         }
     }
 
-    /* Currently NatController doesn't do ipv6 tethering, so we are done. */
     return res;
 }
 
index 9a60402..3e11596 100644 (file)
@@ -43,6 +43,15 @@ public:
     void addPopenContents(std::string contents) {
         sPopenContents.push_back(contents);
     }
+
+    void addPopenContents(std::string contents1, std::string contents2) {
+        sPopenContents.push_back(contents1);
+        sPopenContents.push_back(contents2);
+    }
+
+    void clearPopenContents() {
+        sPopenContents.clear();
+    }
 };
 
 TEST_F(BandwidthControllerTest, TestSetupIptablesHooks) {
@@ -149,6 +158,13 @@ std::string kIPv4TetherCounters = android::base::Join(std::vector<std::string> {
     "    1450  1708806 RETURN     all  --  rmnet0 bt-pan  0.0.0.0/0            0.0.0.0/0",
 }, '\n');
 
+std::string kIPv6TetherCounters = android::base::Join(std::vector<std::string> {
+    "Chain natctrl_tether_counters (2 references)",
+    "    pkts      bytes target     prot opt in     out     source               destination",
+    "   10000 10000000 RETURN     all      wlan0  rmnet0  ::/0                 ::/0",
+    "   20000 20000000 RETURN     all      rmnet0 wlan0   ::/0                 ::/0",
+}, '\n');
+
 std::string readSocketClientResponse(int fd) {
     char buf[32768];
     ssize_t bytesRead = read(fd, buf, sizeof(buf));
@@ -175,63 +191,104 @@ TEST_F(BandwidthControllerTest, TestGetTetherStats) {
 
     std::string err;
     BandwidthController::TetherStats filter;
-    addPopenContents(kIPv4TetherCounters);
+
+    // If no filter is specified, both IPv4 and IPv6 counters must have at least one interface pair.
+    addPopenContents(kIPv4TetherCounters, "");
+    ASSERT_EQ(-1, mBw.getTetherStats(&cli, filter, err));
+    expectNoSocketClientResponse(socketPair[1]);
+    clearPopenContents();
+
+    addPopenContents("", kIPv6TetherCounters);
+    ASSERT_EQ(-1, mBw.getTetherStats(&cli, filter, err));
+    clearPopenContents();
+
+    // IPv4 and IPv6 counters are properly added together.
+    addPopenContents(kIPv4TetherCounters, kIPv6TetherCounters);
+    filter = BandwidthController::TetherStats();
     std::string expected =
-            "114 wlan0 rmnet0 2373 26 2002 27\n"
+            "114 wlan0 rmnet0 10002373 10026 20002002 20027\n"
             "114 bt-pan rmnet0 107471 1040 1708806 1450\n"
             "200 Tethering stats list completed\n";
     ASSERT_EQ(0, mBw.getTetherStats(&cli, filter, err));
     ASSERT_EQ(expected, readSocketClientResponse(socketPair[1]));
+    expectNoSocketClientResponse(socketPair[1]);
+    clearPopenContents();
 
-    addPopenContents(kIPv4TetherCounters);
+    // Test filtering.
+    addPopenContents(kIPv4TetherCounters, kIPv6TetherCounters);
     filter = BandwidthController::TetherStats("bt-pan", "rmnet0", -1, -1, -1, -1);
     expected = "221 bt-pan rmnet0 107471 1040 1708806 1450\n";
     ASSERT_EQ(0, mBw.getTetherStats(&cli, filter, err));
     ASSERT_EQ(expected, readSocketClientResponse(socketPair[1]));
+    expectNoSocketClientResponse(socketPair[1]);
+    clearPopenContents();
 
-    addPopenContents(kIPv4TetherCounters);
-    filter = BandwidthController::TetherStats("rmnet0", "wlan0", -1, -1, -1, -1);
-    expected = "221 rmnet0 wlan0 2002 27 2373 26\n";
+    addPopenContents(kIPv4TetherCounters, kIPv6TetherCounters);
+    filter = BandwidthController::TetherStats("wlan0", "rmnet0", -1, -1, -1, -1);
+    expected = "221 wlan0 rmnet0 10002373 10026 20002002 20027\n";
     ASSERT_EQ(0, mBw.getTetherStats(&cli, filter, err));
     ASSERT_EQ(expected, readSocketClientResponse(socketPair[1]));
+    clearPopenContents();
 
-    addPopenContents(kIPv4TetherCounters);
+    // Select nonexistent interfaces.
+    addPopenContents(kIPv4TetherCounters, kIPv6TetherCounters);
     filter = BandwidthController::TetherStats("rmnet0", "foo0", -1, -1, -1, -1);
     expected = "200 Tethering stats list completed\n";
     ASSERT_EQ(0, mBw.getTetherStats(&cli, filter, err));
     ASSERT_EQ(expected, readSocketClientResponse(socketPair[1]));
+    clearPopenContents();
 
     // No stats with a filter: no error.
-    addPopenContents("");
+    addPopenContents("", "");
     ASSERT_EQ(0, mBw.getTetherStats(&cli, filter, err));
     ASSERT_EQ("200 Tethering stats list completed\n", readSocketClientResponse(socketPair[1]));
-    addPopenContents("foo");
+    clearPopenContents();
+
+    addPopenContents("foo", "foo");
     ASSERT_EQ(0, mBw.getTetherStats(&cli, filter, err));
     ASSERT_EQ("200 Tethering stats list completed\n", readSocketClientResponse(socketPair[1]));
+    clearPopenContents();
 
     // No stats and empty filter: error.
     filter = BandwidthController::TetherStats();
-    addPopenContents("");
+    addPopenContents("", kIPv6TetherCounters);
     ASSERT_EQ(-1, mBw.getTetherStats(&cli, filter, err));
-    addPopenContents("foo");
+    expectNoSocketClientResponse(socketPair[1]);
+    clearPopenContents();
+
+    addPopenContents(kIPv4TetherCounters, "");
     ASSERT_EQ(-1, mBw.getTetherStats(&cli, filter, err));
     expectNoSocketClientResponse(socketPair[1]);
+    clearPopenContents();
 
     // Include only one pair of interfaces and things are fine.
     std::vector<std::string> counterLines = android::base::Split(kIPv4TetherCounters, "\n");
     std::vector<std::string> brokenCounterLines = counterLines;
     counterLines.resize(4);
     std::string counters = android::base::Join(counterLines, "\n") + "\n";
-    addPopenContents(counters);
+    addPopenContents(counters, counters);
     expected =
-            "114 wlan0 rmnet0 2373 26 2002 27\n"
+            "114 wlan0 rmnet0 4746 52 4004 54\n"
             "200 Tethering stats list completed\n";
     ASSERT_EQ(0, mBw.getTetherStats(&cli, filter, err));
     ASSERT_EQ(expected, readSocketClientResponse(socketPair[1]));
+    clearPopenContents();
 
     // But if interfaces aren't paired, it's always an error.
     counterLines.resize(3);
     counters = android::base::Join(counterLines, "\n") + "\n";
+    addPopenContents(counters, counters);
+    ASSERT_EQ(-1, mBw.getTetherStats(&cli, filter, err));
+    expectNoSocketClientResponse(socketPair[1]);
+    clearPopenContents();
+
+    // popen() failing is always an error.
+    addPopenContents(kIPv4TetherCounters);
+    ASSERT_EQ(-1, mBw.getTetherStats(&cli, filter, err));
+    expectNoSocketClientResponse(socketPair[1]);
+    clearPopenContents();
+    addPopenContents(kIPv6TetherCounters);
     ASSERT_EQ(-1, mBw.getTetherStats(&cli, filter, err));
     expectNoSocketClientResponse(socketPair[1]);
+    clearPopenContents();
 }