blob: 1653351630a7008510d02aed06cea7dfd4210bfb [file] [log] [blame]
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless requied by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
/*
* This socket tagging test is to ensure that the
* netfilter/xt_qtaguid kernel module somewhat behaves as expected
* with respect to tagging sockets.
*/
#define LOG_TAG "SocketTagUsrSpaceTest"
#include "SocketTagUserSpace.h"
#include <arpa/inet.h>
#include <assert.h>
#include <cutils/qtaguid.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <string>
#include <fstream>
#include <android-base/stringprintf.h>
#include <gtest/gtest.h>
#include <qtaguid/qtaguid.h>
#include <utils/Log.h>
static const int kMaxCounterSet = 2;
namespace android {
#define SKIP_IF_QTAGUID_NOT_SUPPORTED() \
do { \
bool hasQtaguidSupport; \
EXPECT_EQ(checkKernelSupport(&hasQtaguidSupport), 0) \
<< "kernel support check failed"; \
if (!hasQtaguidSupport) { \
GTEST_LOG_(INFO) << "skipped since kernel version is not compatible.\n"; \
return; \
} \
} while (0);
/* A helper program to generate some traffic between two socket. */
int server_download(SockInfo sock_server, SockInfo sock_client) {
struct sockaddr_in server, client;
server.sin_addr.s_addr = inet_addr("127.0.0.1");
server.sin_family = AF_INET;
server.sin_port = htons(8765);
if (bind(sock_server.fd, (struct sockaddr *)&server, sizeof(server)) < 0) {
std::cerr << "bind failed" << std::endl;
return -1;
}
listen(sock_server.fd, 3);
int sock_addr_length;
sock_addr_length = sizeof(struct sockaddr_in);
if (connect(sock_client.fd, (struct sockaddr *)&server, sizeof(server)), 0) {
return -1;
}
int new_socket;
new_socket = accept(sock_server.fd, (struct sockaddr *)&client,
reinterpret_cast<socklen_t *>(&sock_addr_length));
if (new_socket < 0) {
return -1;
}
int packet_Count = 1024;
char byte_buffer[1024];
snprintf(byte_buffer, sizeof(byte_buffer), "%d", packet_Count);
send(sock_client.fd, "start", 5, 0);
if (recv(new_socket, byte_buffer, strlen(byte_buffer), 0) < 0) {
close(new_socket);
return -1;
}
memset(byte_buffer, 'x', 1023);
byte_buffer[1023] = '\0';
if (send(new_socket, byte_buffer, strlen(byte_buffer), 0) < 0) {
close(new_socket);
return -1;
}
EXPECT_GE(recv(sock_client.fd, byte_buffer, 1024, 0), 0);
close(new_socket);
return 0;
}
int checkKernelSupport(bool *qtaguidSupport) {
int ret;
struct utsname buf;
int kernel_version_major;
int kernel_version_minor;
ret = uname(&buf);
if (ret) {
ret = -errno;
std::cout << "Get system information failed: %s\n"
<< strerror(errno) << std::endl;
return ret;
}
char dummy;
ret = sscanf(buf.release, "%d.%d%c", &kernel_version_major,
&kernel_version_minor, &dummy);
// For device running kernel 4.9 or above and running Android P, it should use
// the eBPF cgroup filter to monitoring networking stats instead. So it may
// not have xt_qtaguid module on device. But for devices that still have
// xt_qtaguid, this test is still useful to make sure it behaves properly.
// b/30950746
if (ret >= 2 && ((kernel_version_major == 4 && kernel_version_minor >= 9) ||
(kernel_version_major > 4))) {
*qtaguidSupport = (access("/dev/xt_qtaguid", F_OK) != -1);
} else {
*qtaguidSupport = true;
}
return 0;
}
/* socket setup, initial the socket and try to validate the socket. */
int SockInfo::setup(int tag) {
fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0) {
std::cout << "socket creation failed: %s" << strerror(errno) << std::endl;
return -1;
}
if (legacy_tagSocket(fd, tag, getuid()) < 0) {
std::cout << "socket setup: failed to tag" << std::endl;
close(fd);
return -1;
}
if (legacy_untagSocket(fd) < 0) {
std::cout << "socket setup: Unexpected results" << std::endl;
close(fd);
return -1;
}
return 0;
}
/* Check if the socket is properly tagged by read through the proc file.*/
bool SockInfo::checkTag(uint64_t acct_tag, uid_t uid) {
std::ifstream fctrl("/proc/net/xt_qtaguid/ctrl", std::fstream::in);
if (!fctrl.is_open()) {
std::cout << "qtaguid ctrl open failed!" << std::endl;
}
uint64_t full_tag = (acct_tag << 32) | uid;
std::string buff =
android::base::StringPrintf(" tag=0x%" PRIx64 " (uid=%u)", full_tag, uid);
std::string ctrl_data;
std::size_t pos = std::string::npos;
while (std::getline(fctrl, ctrl_data)) {
pos = ctrl_data.find(buff);
if (pos != std::string::npos) break;
}
return pos != std::string::npos;
}
/*
* Check if the socket traffic statistics is properly recorded by reading the
* corresponding proc file.
*/
bool SockInfo::checkStats(uint64_t acct_tag, uid_t uid, int counterSet,
uint32_t *stats_result) {
uint64_t kTag = (uint64_t)acct_tag << 32;
std::ifstream fstats("/proc/net/xt_qtaguid/stats", std::fstream::in);
if (!fstats.is_open()) {
std::cout << "qtaguid ctrl open failed!" << std::endl;
}
std::string buff =
android::base::StringPrintf("0x%" PRIx64 " %u %d", kTag, uid, counterSet);
std::string stats_data;
std::size_t pos = std::string::npos;
while (std::getline(fstats, stats_data)) {
pos = stats_data.find(buff);
if (pos != std::string::npos) {
std::string match_data = stats_data.substr(pos);
sscanf(match_data.c_str(), "0x%" PRIx64 " %u %d %d %d", &kTag, &uid,
&counterSet, stats_result, stats_result + 1);
return pos != std::string::npos;
}
}
return pos != std::string::npos;
}
class SocketTagUsrSpaceTest : public ::testing::Test {
protected:
uint32_t stats_result_[2];
SockInfo sock_0;
SockInfo sock_1;
uid_t fake_uid;
uid_t fake_uid2;
uid_t inet_uid;
uid_t my_uid;
pid_t my_pid;
int valid_tag1;
int valid_tag2;
uint64_t max_uint_tag;
virtual void SetUp() {
my_uid = getuid();
my_pid = getpid();
srand48(my_pid * my_uid);
// Adjust fake UIDs and tags so that multiple instances can run
// in parallel.
fake_uid = rand() & 0x7FFFFFFF;
fake_uid2 = rand() & 0x7FFFFFFF;
inet_uid = 1024;
valid_tag1 = (my_pid << 12) | (rand());
valid_tag2 = (my_pid << 12) | (rand());
max_uint_tag = 0xffffffff00000000llu;
max_uint_tag = 1llu << 63 | (((uint64_t)my_pid << 48) ^ max_uint_tag);
// Check the node /dev/xt_qtaguid exist before start.
struct stat nodeStat;
EXPECT_GE(stat("/dev/xt_qtaguid", &nodeStat), 0)
<< "fail to check /dev/xt_qtaguid";
// We want to clean up any previous faulty test runs.
EXPECT_GE(legacy_deleteTagData(0, fake_uid), 0)
<< "Failed to delete fake_uid";
EXPECT_GE(legacy_deleteTagData(0, fake_uid2), 0)
<< "Failed to delete fake_uid2";
EXPECT_GE(legacy_deleteTagData(0, my_uid), 0) << "Failed to delete my_uid";
EXPECT_GE(legacy_deleteTagData(0, inet_uid), 0)
<< "Failed to delete inet_uid";
ASSERT_FALSE(sock_0.setup(valid_tag1)) << "socket0 setup failed";
ASSERT_FALSE(sock_1.setup(valid_tag1)) << "socket1 setup failed";
}
virtual void TearDown() {
if (sock_0.fd >= 0) {
close(sock_0.fd);
}
if (sock_1.fd >= 0) {
close(sock_1.fd);
}
}
};
/* Tag to a invalid socket fd, should fail */
TEST_F(SocketTagUsrSpaceTest, invalidSockfdFail) {
EXPECT_LT(legacy_tagSocket(-1, valid_tag1, my_uid), 0)
<< "Invalid socketfd case 1, should fail.";
}
/* Check the stats of a invalid socket, should fail. */
TEST_F(SocketTagUsrSpaceTest, CheckStatsInvalidSocketFail) {
SKIP_IF_QTAGUID_NOT_SUPPORTED();
memset(stats_result_, 0, 2);
EXPECT_FALSE(sock_0.checkStats(valid_tag1, fake_uid, 0, stats_result_))
<< "No stats should be here";
}
/* Untag invalid socket fd, should fail */
TEST_F(SocketTagUsrSpaceTest, UntagInvalidSocketFail) {
EXPECT_LT(legacy_untagSocket(-1), 0) << "invalid socket fd, should fail";
EXPECT_LT(legacy_untagSocket(sock_0.fd), 0)
<< "no tags on sock0, should fail";
}
/*
* Set the counter to a number larger then max counter avalaible
* should fail
*/
TEST_F(SocketTagUsrSpaceTest, CounterSetNumExceedFail) {
int wrongCounterNum = kMaxCounterSet + 1;
EXPECT_LT(legacy_setCounterSet(wrongCounterNum, my_uid), 0)
<< "Invalid counter set number, should fail.";
}
/* Tag without valid uid, should be tagged with my_uid */
TEST_F(SocketTagUsrSpaceTest, NoUidTag) {
SKIP_IF_QTAGUID_NOT_SUPPORTED();
EXPECT_GE(legacy_tagSocket(sock_0.fd, valid_tag1, 0), 0)
<< "tag failed without uid";
EXPECT_TRUE(sock_0.checkTag(valid_tag1, my_uid)) << "Tag not found";
}
/*
* Tag without tag and uid number, should be tagged with tag 0 and
* my_uid
*/
TEST_F(SocketTagUsrSpaceTest, NoTagNoUid) {
SKIP_IF_QTAGUID_NOT_SUPPORTED();
EXPECT_GE(legacy_tagSocket(sock_0.fd, 0, 0), 0)
<< "no tag and uid infomation";
ASSERT_TRUE(sock_0.checkTag(0, my_uid)) << "Tag not found";
}
/* Untag from a tagged socket */
TEST_F(SocketTagUsrSpaceTest, ValidUntag) {
SKIP_IF_QTAGUID_NOT_SUPPORTED();
EXPECT_GE(legacy_tagSocket(sock_0.fd, valid_tag1, my_uid), 0);
EXPECT_TRUE(sock_0.checkTag(valid_tag1, my_uid)) << "Tag not found";
EXPECT_GE(legacy_untagSocket(sock_0.fd), 0);
EXPECT_FALSE(sock_0.checkTag(valid_tag1, my_uid)) << "Tag should be removed";
}
/* Tag a socket for the first time */
TEST_F(SocketTagUsrSpaceTest, ValidFirsttag) {
SKIP_IF_QTAGUID_NOT_SUPPORTED();
EXPECT_GE(legacy_tagSocket(sock_0.fd, valid_tag2, fake_uid), 0);
EXPECT_TRUE(sock_0.checkTag(valid_tag2, fake_uid)) << "Tag not found.";
}
/* ReTag a already tagged socket with the same tag and uid */
TEST_F(SocketTagUsrSpaceTest, ValidReTag) {
SKIP_IF_QTAGUID_NOT_SUPPORTED();
EXPECT_GE(legacy_tagSocket(sock_0.fd, valid_tag2, fake_uid), 0);
EXPECT_GE(legacy_tagSocket(sock_0.fd, valid_tag2, fake_uid), 0);
EXPECT_TRUE(sock_0.checkTag(valid_tag2, fake_uid)) << "Tag not found.";
}
/*
* Retag a already tagged socket with the same uid but different tag
* Should keep the second one and untag the original one
*/
TEST_F(SocketTagUsrSpaceTest, ValidReTagWithAcctTagChange) {
SKIP_IF_QTAGUID_NOT_SUPPORTED();
EXPECT_GE(legacy_tagSocket(sock_0.fd, valid_tag2, fake_uid), 0);
EXPECT_GE(legacy_tagSocket(sock_0.fd, valid_tag1, fake_uid), 0);
EXPECT_TRUE(sock_0.checkTag(valid_tag1, fake_uid)) << "Tag not found.";
EXPECT_FALSE(sock_0.checkTag(valid_tag2, fake_uid))
<< "Tag should not be here";
}
/*
* Retag a already tagged socket with different uid and different tag.
* Should keep both
*/
TEST_F(SocketTagUsrSpaceTest, ReTagWithUidChange) {
EXPECT_GE(legacy_tagSocket(sock_0.fd, valid_tag2, fake_uid), 0);
EXPECT_GE(legacy_tagSocket(sock_0.fd, valid_tag1, fake_uid2), 0);
}
/*
* Retag a already tagged socket with different uid but the same tag.
* The original one should be replaced by the new one.
*/
TEST_F(SocketTagUsrSpaceTest, ReTagWithUidChange2) {
SKIP_IF_QTAGUID_NOT_SUPPORTED();
EXPECT_GE(legacy_tagSocket(sock_0.fd, valid_tag2, fake_uid), 0);
EXPECT_GE(legacy_tagSocket(sock_0.fd, valid_tag2, fake_uid2), 0);
EXPECT_TRUE(sock_0.checkTag(valid_tag2, fake_uid2)) << "Tag not found.";
EXPECT_FALSE(sock_0.checkTag(valid_tag2, fake_uid))
<< "Tag should not be here";
}
/* Tag two sockets with two uids and two tags. */
TEST_F(SocketTagUsrSpaceTest, TagAnotherSocket) {
SKIP_IF_QTAGUID_NOT_SUPPORTED();
EXPECT_GE(legacy_tagSocket(sock_0.fd, max_uint_tag, my_uid), 0);
EXPECT_GE(legacy_tagSocket(sock_1.fd, valid_tag1, fake_uid2), 0);
EXPECT_TRUE(sock_1.checkTag(valid_tag1, fake_uid2)) << "Tag not found.";
EXPECT_GE(legacy_untagSocket(sock_0.fd), 0);
EXPECT_FALSE(sock_0.checkTag(max_uint_tag, fake_uid))
<< "Tag should not be there";
EXPECT_TRUE(sock_1.checkTag(valid_tag1, fake_uid2)) << "Tag not found";
EXPECT_GE(legacy_untagSocket(sock_1.fd), 0);
EXPECT_FALSE(sock_1.checkTag(valid_tag1, fake_uid2))
<< "Tag should not be there";
}
/* Tag two sockets with the same uid but different acct_tags. */
TEST_F(SocketTagUsrSpaceTest, SameUidTwoSocket) {
SKIP_IF_QTAGUID_NOT_SUPPORTED();
EXPECT_GE(legacy_tagSocket(sock_0.fd, valid_tag1, my_uid), 0);
EXPECT_GE(legacy_tagSocket(sock_1.fd, valid_tag2, my_uid), 0);
EXPECT_TRUE(sock_1.checkTag(valid_tag2, my_uid)) << "Tag not found.";
EXPECT_TRUE(sock_0.checkTag(valid_tag1, my_uid)) << "Tag not found.";
EXPECT_GE(legacy_untagSocket(sock_0.fd), 0);
EXPECT_FALSE(sock_0.checkTag(valid_tag1, my_uid))
<< "Tag should not be there";
EXPECT_TRUE(sock_1.checkTag(valid_tag2, my_uid)) << "Tag not found";
EXPECT_GE(legacy_untagSocket(sock_1.fd), 0);
EXPECT_FALSE(sock_1.checkTag(valid_tag2, my_uid))
<< "Tag should not be there";
}
/* Tag two sockets with the same acct_tag but different uids */
TEST_F(SocketTagUsrSpaceTest, SameTagTwoSocket) {
SKIP_IF_QTAGUID_NOT_SUPPORTED();
EXPECT_GE(legacy_tagSocket(sock_0.fd, valid_tag1, fake_uid), 0);
EXPECT_GE(legacy_tagSocket(sock_1.fd, valid_tag1, fake_uid2), 0);
EXPECT_TRUE(sock_1.checkTag(valid_tag1, fake_uid)) << "Tag not found.";
EXPECT_TRUE(sock_0.checkTag(valid_tag1, fake_uid2)) << "Tag not found.";
EXPECT_GE(legacy_untagSocket(sock_0.fd), 0);
EXPECT_FALSE(sock_0.checkTag(valid_tag1, fake_uid))
<< "Tag should not be there";
EXPECT_TRUE(sock_1.checkTag(valid_tag1, fake_uid2)) << "Tag not found";
EXPECT_GE(legacy_untagSocket(sock_1.fd), 0);
EXPECT_FALSE(sock_1.checkTag(valid_tag1, fake_uid2))
<< "Tag should not be there";
}
/* Tag a closed socket, should fail. */
TEST_F(SocketTagUsrSpaceTest, TagInvalidSocketFail) {
SKIP_IF_QTAGUID_NOT_SUPPORTED();
close(sock_0.fd);
EXPECT_LT(legacy_tagSocket(sock_0.fd, valid_tag1, my_uid), 0);
EXPECT_FALSE(sock_0.checkTag(valid_tag1, my_uid))
<< "Tag should not be there";
}
/* untag from a closed socket, should fail. */
TEST_F(SocketTagUsrSpaceTest, UntagClosedSocketFail) {
SKIP_IF_QTAGUID_NOT_SUPPORTED();
EXPECT_GE(legacy_tagSocket(sock_1.fd, valid_tag1, my_uid), 0);
EXPECT_TRUE(sock_1.checkTag(valid_tag1, my_uid));
close(sock_1.fd);
EXPECT_LT(legacy_untagSocket(sock_1.fd), 0) << "no tag attached, should fail";
}
/*
* try to connect with google.com in order to generate some
* tranffic through the socket, the traffic statistics should
* be stored in the stats file and will be returned.
*/
TEST_F(SocketTagUsrSpaceTest, dataTransmitTest) {
SKIP_IF_QTAGUID_NOT_SUPPORTED();
memset(stats_result_, 0, 2);
EXPECT_GE(legacy_tagSocket(sock_0.fd, valid_tag1, inet_uid), 0);
EXPECT_TRUE(sock_0.checkTag(valid_tag1, inet_uid)) << "tag not found";
EXPECT_GE(server_download(sock_0, sock_1), 0);
EXPECT_GE(legacy_untagSocket(sock_0.fd), 0);
close(sock_0.fd);
EXPECT_TRUE(sock_0.checkStats(valid_tag1, inet_uid, 0, stats_result_))
<< "failed to retreive data";
EXPECT_GT(*stats_result_, (uint32_t)0) << "no stats found for this socket";
EXPECT_GT(*(stats_result_ + 1), (uint32_t)0)
<< "no stats stored for this socket";
}
/* Generate some traffic first and then delete the
* tag and uid from stats. All the stats related should
* be deleted. checkStats() should return false.
*/
TEST_F(SocketTagUsrSpaceTest, dataStatsDeleteTest) {
SKIP_IF_QTAGUID_NOT_SUPPORTED();
memset(stats_result_, 0, 2);
EXPECT_GE(legacy_tagSocket(sock_0.fd, valid_tag1, fake_uid), 0);
EXPECT_TRUE(sock_0.checkTag(valid_tag1, fake_uid)) << "tag not found";
EXPECT_GE(server_download(sock_0, sock_1), 0);
EXPECT_GE(legacy_untagSocket(sock_0.fd), 0);
EXPECT_TRUE(sock_0.checkStats(valid_tag1, fake_uid, 0, stats_result_))
<< "failed to retreive data";
EXPECT_GT(*stats_result_, (uint32_t)0) << "no stats found for this socket";
EXPECT_GT(*(stats_result_ + 1), (uint32_t)0)
<< "no stats stored for this socket";
EXPECT_GE(legacy_deleteTagData(0, fake_uid), 0)
<< "Failed to delete fake_uid";
EXPECT_FALSE(sock_0.checkStats(valid_tag1, fake_uid, 0, stats_result_))
<< "NO DATA should be stored";
}
/*
* try to store the traffic stats in the secound counter
* insdead of the first. All the stats should be stored
* in the secound counter.
*/
TEST_F(SocketTagUsrSpaceTest, CounterSetTest) {
SKIP_IF_QTAGUID_NOT_SUPPORTED();
memset(stats_result_, 0, 2);
EXPECT_GE(legacy_tagSocket(sock_1.fd, valid_tag1, inet_uid), 0);
EXPECT_GE(legacy_setCounterSet(1, inet_uid), 0);
EXPECT_TRUE(sock_1.checkTag(valid_tag1, inet_uid)) << "tag not found";
EXPECT_GE(server_download(sock_0, sock_1), 0);
EXPECT_GE(legacy_untagSocket(sock_1.fd), 0);
close(sock_1.fd);
EXPECT_TRUE(sock_1.checkStats(valid_tag1, inet_uid, 1, stats_result_))
<< "failed to retreive data";
uint32_t packet_count = 1;
uint32_t total_byte = 1024;
EXPECT_GT(*stats_result_, total_byte) << "no stats found for this socket";
EXPECT_GT(*(stats_result_ + 1), packet_count)
<< "wrong stats stored for this socket";
uint32_t stats_foreground[2] = {0, 0};
EXPECT_TRUE(sock_0.checkStats(valid_tag1, inet_uid, 0, stats_foreground))
<< "fail to retrieve data";
EXPECT_LE(*stats_foreground, (uint32_t)0) << "stats data is not zero";
}
} // namespace android