New commands for replacing qtaguid native API

Added four new commands in FWmark server client module to realize the
functionality of the native qtaguid code inside libcutils. These
commands allows processes to tag/untag a specific socket. Change counter
set of a specific uid and delete the statistics of a uid tag pair

Test: netd_integration_test: all tests pass
      netd_unit_test: 71 tests passed, test program ended with
      segmentation fault.
      netd_benchmark_test:
	Benchmark                                                                    Time           CPU Iterations
	-----------------------------------------------------------------------------------------------------------
	ipv4_metrics_reporting_no_fwmark/min_time:0.500/manual_time             490311 ns    1321836 ns       1523 734166
	ipv4_metrics_reporting_no_load/min_time:0.500/manual_time               971644 ns    1754024 ns        677 1498332
	ipv4_full_reporting_no_load/min_time:0.500/manual_time                 1668522 ns    1905846 ns        330 2428333
	ipv4_metrics_reporting_high_load/min_time:0.500/real_time/threads:1    1563602 ns    1152833 ns        329
	ipv4_full_reporting_high_load/min_time:0.500/real_time/threads:1       2186832 ns    1446244 ns        337
	ipv6_metrics_reporting_no_fwmark/min_time:0.500/manual_time             518447 ns    1402849 ns       1194 766667
	ipv6_metrics_reporting_no_load/min_time:0.500/manual_time              1124998 ns    1872343 ns        551 1585000
	ipv6_full_reporting_no_load/min_time:0.500/manual_time                 1707226 ns    1999032 ns        503 2586667
	ipv6_metrics_reporting_high_load/min_time:0.500/real_time/threads:1    1790899 ns    1311401 ns        447
	ipv6_full_reporting_high_load/min_time:0.500/real_time/threads:1       2224263 ns    1443308 ns        311
	DnsFixture/getaddrinfo_log_nothing/real_time/threads:1                 8738083 ns    1648450 ns        100
	DnsFixture/getaddrinfo_log_nothing/real_time/threads:2                 4154537 ns     989402 ns        136
	DnsFixture/getaddrinfo_log_nothing/real_time/threads:4                 3849070 ns     988386 ns        232
	DnsFixture/getaddrinfo_log_nothing/real_time/threads:8                 1867350 ns     603123 ns        552
	DnsFixture/getaddrinfo_log_nothing/real_time/threads:16                4677366 ns     809034 ns        160
	DnsFixture/getaddrinfo_log_nothing/real_time/threads:32                2932518 ns     680202 ns        320
	DnsFixture/getaddrinfo_log_metrics/real_time/threads:1                 6851425 ns    1285477 ns        100
	DnsFixture/getaddrinfo_log_metrics/real_time/threads:2                 4348378 ns     973831 ns        122
	DnsFixture/getaddrinfo_log_metrics/real_time/threads:4                 3850877 ns     900753 ns        204
	DnsFixture/getaddrinfo_log_metrics/real_time/threads:8                 1492482 ns     593676 ns        536
	DnsFixture/getaddrinfo_log_metrics/real_time/threads:16                2170729 ns     598599 ns        304
	DnsFixture/getaddrinfo_log_metrics/real_time/threads:32                2159187 ns     600411 ns        320
	DnsFixture/getaddrinfo_log_everything/real_time/threads:1              5321642 ns     906195 ns        101
	DnsFixture/getaddrinfo_log_everything/real_time/threads:2              3689002 ns     887944 ns        266
	DnsFixture/getaddrinfo_log_everything/real_time/threads:4              2879940 ns     789382 ns        440
	DnsFixture/getaddrinfo_log_everything/real_time/threads:8              2578146 ns     696420 ns        408
	DnsFixture/getaddrinfo_log_everything/real_time/threads:16             1857378 ns     593976 ns        544
	DnsFixture/getaddrinfo_log_everything/real_time/threads:32             2395951 ns     617320 ns        640

Bug: 30950746
Change-Id: I3a20f4dc523097d46fc1f7a9117b59f083d5c23d
diff --git a/client/FwmarkClient.cpp b/client/FwmarkClient.cpp
index a76f49a..31b1436 100644
--- a/client/FwmarkClient.cpp
+++ b/client/FwmarkClient.cpp
@@ -38,6 +38,12 @@
     return isBuildDebuggable && getenv(name);
 }
 
+bool commandHasFd(int cmdId) {
+    return (cmdId != FwmarkCommand::QUERY_USER_ACCESS) &&
+        (cmdId != FwmarkCommand::SET_COUNTERSET) &&
+        (cmdId != FwmarkCommand::DELETE_TAGDATA);
+}
+
 }  // namespace
 
 bool FwmarkClient::shouldSetFwmark(int family) {
@@ -73,6 +79,8 @@
                                    sizeof(FWMARK_SERVER_PATH))) == -1) {
         // If we are unable to connect to the fwmark server, assume there's no error. This protects
         // against future changes if the fwmark server goes away.
+        // TODO: This means that fd will very likely be misrouted. See if we can delete this in a
+        //       separate CL.
         return 0;
     }
 
@@ -90,7 +98,7 @@
         char cmsg[CMSG_SPACE(sizeof(fd))];
     } cmsgu;
 
-    if (data->cmdId != FwmarkCommand::QUERY_USER_ACCESS) {
+    if (commandHasFd(data->cmdId)) {
         memset(cmsgu.cmsg, 0, sizeof(cmsgu.cmsg));
         message.msg_control = cmsgu.cmsg;
         message.msg_controllen = sizeof(cmsgu.cmsg);
diff --git a/client/NetdClient.cpp b/client/NetdClient.cpp
index 948b617..821f488 100644
--- a/client/NetdClient.cpp
+++ b/client/NetdClient.cpp
@@ -67,7 +67,7 @@
         }
     }
     if (FwmarkClient::shouldSetFwmark(family)) {
-        FwmarkCommand command = {FwmarkCommand::ON_ACCEPT, 0, 0};
+        FwmarkCommand command = {FwmarkCommand::ON_ACCEPT, 0, 0, 0};
         if (int error = FwmarkClient().send(&command, acceptedSocket, nullptr)) {
             return closeFdAndSetErrno(acceptedSocket, error);
         }
@@ -79,7 +79,7 @@
     const bool shouldSetFwmark = (sockfd >= 0) && addr
             && FwmarkClient::shouldSetFwmark(addr->sa_family);
     if (shouldSetFwmark) {
-        FwmarkCommand command = {FwmarkCommand::ON_CONNECT, 0, 0};
+        FwmarkCommand command = {FwmarkCommand::ON_CONNECT, 0, 0, 0};
         if (int error = FwmarkClient().send(&command, sockfd, nullptr)) {
             errno = -error;
             return -1;
@@ -96,7 +96,7 @@
         FwmarkConnectInfo connectInfo(ret == 0 ? 0 : connectErrno, latencyMs, addr);
         // TODO: get the netId from the socket mark once we have continuous benchmark runs
         FwmarkCommand command = {FwmarkCommand::ON_CONNECT_COMPLETE, /* netId (ignored) */ 0,
-                /* uid (filled in by the server) */ 0};
+                                 /* uid (filled in by the server) */ 0, 0};
         // Ignore return value since it's only used for logging
         FwmarkClient().send(&command, sockfd, &connectInfo);
     }
@@ -205,7 +205,7 @@
     if (socketFd < 0) {
         return -EBADF;
     }
-    FwmarkCommand command = {FwmarkCommand::SELECT_NETWORK, netId, 0};
+    FwmarkCommand command = {FwmarkCommand::SELECT_NETWORK, netId, 0, 0};
     return FwmarkClient().send(&command, socketFd, nullptr);
 }
 
@@ -221,7 +221,7 @@
     if (socketFd < 0) {
         return -EBADF;
     }
-    FwmarkCommand command = {FwmarkCommand::PROTECT_FROM_VPN, 0, 0};
+    FwmarkCommand command = {FwmarkCommand::PROTECT_FROM_VPN, 0, 0, 0};
     return FwmarkClient().send(&command, socketFd, nullptr);
 }
 
@@ -229,11 +229,31 @@
     if (socketFd < 0) {
         return -EBADF;
     }
-    FwmarkCommand command = {FwmarkCommand::SELECT_FOR_USER, 0, uid};
+    FwmarkCommand command = {FwmarkCommand::SELECT_FOR_USER, 0, uid, 0};
     return FwmarkClient().send(&command, socketFd, nullptr);
 }
 
 extern "C" int queryUserAccess(uid_t uid, unsigned netId) {
-    FwmarkCommand command = {FwmarkCommand::QUERY_USER_ACCESS, netId, uid};
+    FwmarkCommand command = {FwmarkCommand::QUERY_USER_ACCESS, netId, uid, 0};
+    return FwmarkClient().send(&command, -1, nullptr);
+}
+
+extern "C" int tagSocket(int socketFd, uint32_t tag, uid_t uid) {
+    FwmarkCommand command = {FwmarkCommand::TAG_SOCKET, 0, uid, tag};
+    return FwmarkClient().send(&command, socketFd, nullptr);
+}
+
+extern "C" int untagSocket(int socketFd) {
+    FwmarkCommand command = {FwmarkCommand::UNTAG_SOCKET, 0, 0, 0};
+    return FwmarkClient().send(&command, socketFd, nullptr);
+}
+
+extern "C" int setCounterSet(uint32_t counterSet, uid_t uid) {
+    FwmarkCommand command = {FwmarkCommand::SET_COUNTERSET, 0, uid, counterSet};
+    return FwmarkClient().send(&command, -1, nullptr);
+}
+
+extern "C" int deleteTagData(uint32_t tag, uid_t uid) {
+    FwmarkCommand command = {FwmarkCommand::DELETE_TAGDATA, 0, uid, tag};
     return FwmarkClient().send(&command, -1, nullptr);
 }
diff --git a/include/FwmarkCommand.h b/include/FwmarkCommand.h
index 39ff233..5854233 100644
--- a/include/FwmarkCommand.h
+++ b/include/FwmarkCommand.h
@@ -51,6 +51,8 @@
 // Commands sent from clients to the fwmark server to mark sockets (i.e., set their SO_MARK).
 // ON_CONNECT_COMPLETE command should be accompanied by FwmarkConnectInfo which should  contain
 // info about that connect attempt
+// TODO: rework this struct into a more flexible data structure such as union or
+// a hierarchy class.
 struct FwmarkCommand {
     enum {
         ON_ACCEPT,
@@ -60,10 +62,20 @@
         SELECT_FOR_USER,
         QUERY_USER_ACCESS,
         ON_CONNECT_COMPLETE,
+        TAG_SOCKET,
+        UNTAG_SOCKET,
+        // TODO: use binder to pass the following two request in future after we
+        // completely get rid of qtaguid module, since these are privileged
+        // command.
+        SET_COUNTERSET,
+        DELETE_TAGDATA,
     } cmdId;
     unsigned netId;  // used only in the SELECT_NETWORK command; ignored otherwise.
-    uid_t uid;  // used only in the SELECT_FOR_USER and QUERY_USER_ACCESS commands;
-                // ignored otherwise.
+    uid_t uid;       // used in the SELECT_FOR_USER, QUERY_USER_ACCESS, TAG_SOCKET,
+                     // SET_COUNTERSET, and DELETE_TAGDATA command; ignored otherwise.
+    uint32_t trafficCtrlInfo;  // used in TAG_SOCKET, SET_COUNTERSET and SET_PACIFIER command;
+                               // ignored otherwise. Depend on the case, it can be a tag, a
+                               // counterSet or a pacifier signal.
 };
 
 #endif  // NETD_INCLUDE_FWMARK_COMMAND_H
diff --git a/include/NetdClient.h b/include/NetdClient.h
index 7db0906..d439daa 100644
--- a/include/NetdClient.h
+++ b/include/NetdClient.h
@@ -39,6 +39,13 @@
 
 int queryUserAccess(uid_t uid, unsigned netId);
 
+int tagSocket(int socketFd, uint32_t tag, uid_t uid);
+
+int untagSocket(int socketFd);
+
+int setCounterSet(uint32_t counterSet, uid_t uid);
+
+int deleteTagData(uint32_t tag, uid_t uid);
 __END_DECLS
 
 #endif  // NETD_INCLUDE_NETD_CLIENT_H
diff --git a/server/FwmarkServer.cpp b/server/FwmarkServer.cpp
index 4800864..5fe4cbe 100644
--- a/server/FwmarkServer.cpp
+++ b/server/FwmarkServer.cpp
@@ -20,6 +20,7 @@
 #include "FwmarkCommand.h"
 #include "NetdConstants.h"
 #include "NetworkController.h"
+#include "TrafficController.h"
 #include "resolv_netid.h"
 
 #include <netinet/in.h>
@@ -27,17 +28,27 @@
 #include <unistd.h>
 #include <utils/String16.h>
 
+#include <binder/IServiceManager.h>
+
 using android::String16;
 using android::net::metrics::INetdEventListener;
 
 namespace android {
 namespace net {
 
-FwmarkServer::FwmarkServer(NetworkController* networkController, EventReporter* eventReporter) :
-        SocketListener(SOCKET_NAME, true), mNetworkController(networkController),
-        mEventReporter(eventReporter) {
+const char UPDATE_DEVICE_STATS[] = "android.permission.UPDATE_DEVICE_STATS";
+
+bool hasUpdateDeviceStatsPermission(SocketClient* client) {
+    return checkPermission(String16(UPDATE_DEVICE_STATS), client->getPid(), client->getUid());
 }
 
+FwmarkServer::FwmarkServer(NetworkController* networkController, EventReporter* eventReporter,
+                           TrafficController* trafficCtrl)
+    : SocketListener(SOCKET_NAME, true),
+      mNetworkController(networkController),
+      mEventReporter(eventReporter),
+      mTrafficCtrl(trafficCtrl) {}
+
 bool FwmarkServer::onDataAvailable(SocketClient* client) {
     int socketFd = -1;
     int error = processClient(client, &socketFd);
@@ -97,6 +108,20 @@
         return mNetworkController->checkUserNetworkAccess(command.uid, command.netId);
     }
 
+    if (command.cmdId == FwmarkCommand::SET_COUNTERSET) {
+        if (!hasUpdateDeviceStatsPermission(client)) {
+            return -EPERM;
+        }
+        return mTrafficCtrl->setCounterSet(command.trafficCtrlInfo, command.uid);
+    }
+
+    if (command.cmdId == FwmarkCommand::DELETE_TAGDATA) {
+        if (!hasUpdateDeviceStatsPermission(client)) {
+            return -EPERM;
+        }
+        return mTrafficCtrl->deleteTagData(command.trafficCtrlInfo, command.uid);
+    }
+
     cmsghdr* const cmsgh = CMSG_FIRSTHDR(&message);
     if (cmsgh && cmsgh->cmsg_level == SOL_SOCKET && cmsgh->cmsg_type == SCM_RIGHTS &&
         cmsgh->cmsg_len == CMSG_LEN(sizeof(*socketFd))) {
@@ -240,6 +265,26 @@
             break;
         }
 
+        case FwmarkCommand::TAG_SOCKET: {
+            // If the UID is -1, tag as the caller's UID:
+            //  - TrafficStats and NetworkManagementSocketTagger use -1 to indicate "use the
+            //    caller's UID".
+            //  - xt_qtaguid will see -1 on the command line, fail to parse it as a uint32_t, and
+            //    fall back to current_fsuid().
+            if (static_cast<int>(command.uid) == -1) {
+                command.uid = client->getUid();
+            }
+            if (command.uid != client->getUid() && !hasUpdateDeviceStatsPermission(client)) {
+                return -EPERM;
+            }
+            return mTrafficCtrl->tagSocket(*socketFd, command.trafficCtrlInfo, command.uid);
+        }
+
+        case FwmarkCommand::UNTAG_SOCKET: {
+            // Any process can untag a socket it has an fd for.
+            return mTrafficCtrl->untagSocket(*socketFd);
+        }
+
         default: {
             // unknown command
             return -EPROTO;
diff --git a/server/FwmarkServer.h b/server/FwmarkServer.h
index 4d109c0..2f44db1 100644
--- a/server/FwmarkServer.h
+++ b/server/FwmarkServer.h
@@ -17,8 +17,8 @@
 #ifndef NETD_SERVER_FWMARK_SERVER_H
 #define NETD_SERVER_FWMARK_SERVER_H
 
-#include "android/net/metrics/INetdEventListener.h"
 #include "EventReporter.h"
+#include "TrafficController.h"
 #include "sysutils/SocketListener.h"
 
 namespace android {
@@ -28,9 +28,10 @@
 
 class FwmarkServer : public SocketListener {
 public:
-    explicit FwmarkServer(NetworkController* networkController, EventReporter* eventReporter);
+  explicit FwmarkServer(NetworkController* networkController, EventReporter* eventReporter,
+                        TrafficController* trafficCtrl);
 
-    static constexpr const char* SOCKET_NAME = "fwmarkd";
+  static constexpr const char* SOCKET_NAME = "fwmarkd";
 
 private:
     // Overridden from SocketListener:
@@ -41,6 +42,7 @@
 
     NetworkController* const mNetworkController;
     EventReporter* mEventReporter;
+    TrafficController* mTrafficCtrl;
 };
 
 }  // namespace net
diff --git a/server/main.cpp b/server/main.cpp
index d3e0046..aa65b15 100644
--- a/server/main.cpp
+++ b/server/main.cpp
@@ -136,7 +136,7 @@
         exit(1);
     }
 
-    FwmarkServer fwmarkServer(&gCtls->netCtrl, &gCtls->eventReporter);
+    FwmarkServer fwmarkServer(&gCtls->netCtrl, &gCtls->eventReporter, &gCtls->trafficCtrl);
     if (fwmarkServer.startListener()) {
         ALOGE("Unable to start FwmarkServer (%s)", strerror(errno));
         exit(1);