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);