1 //
2 // Copyright (C) 2018 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 #include <errno.h>
18 #include <inttypes.h>
19 #include <limits.h>
20 #include <linux/inet_diag.h>
21 #include <linux/netlink.h>
22 #include <linux/sock_diag.h>
23 #include <linux/unistd.h>
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sys/socket.h>
29 #include <sys/types.h>
30
31 #include <gtest/gtest.h>
32
33 #include <cutils/qtaguid.h>
34
35 #include <netdutils/Misc.h>
36 #include <netdutils/Syscalls.h>
37 #include "NetlinkListener.h"
38 #include "TrafficController.h"
39 #include "bpf/BpfMap.h"
40 #include "bpf/BpfUtils.h"
41 #include "netdutils/Netlink.h"
42
43 // A test uid that is large enough so normal apps are not likely to take,
44 constexpr uid_t TEST_UID = UID_MAX - 2;
45 // A test tag arbitrarily selected.
46 constexpr uint32_t TEST_TAG = 0xFF0F0F0F;
47
48 constexpr uint32_t SOCK_CLOSE_WAIT_US = 20 * 1000;
49 constexpr uint32_t ENOBUFS_POLL_WAIT_US = 10 * 1000;
50
51 using android::netdutils::Status;
52 using android::netdutils::statusFromErrno;
53
54 // This test set up a SkDestroyListener that is runing parallel with the production
55 // SkDestroyListener. The test will create thousands of sockets and tag them on the
56 // production cookieUidTagMap and close them in a short time. When the number of
57 // sockets get closed exceeds the buffer size, it will start to return ENOBUFF
58 // error. The error will be ignored by the production SkDestroyListener and the
59 // test will clean up the tags in tearDown if there is any remains.
60
61 // TODO: Instead of test the ENOBUFF error, we can test the production
62 // SkDestroyListener to see if it failed to delete a tagged socket when ENOBUFF
63 // triggerred.
64 class NetlinkListenerTest : public testing::Test {
65 protected:
NetlinkListenerTest()66 NetlinkListenerTest() {}
67 BpfMap<uint64_t, UidTag> mCookieTagMap;
68
SetUp()69 void SetUp() {
70 SKIP_IF_BPF_NOT_SUPPORTED;
71
72 mCookieTagMap.reset(android::bpf::mapRetrieve(COOKIE_TAG_MAP_PATH, 0));
73 ASSERT_TRUE(mCookieTagMap.isValid());
74 }
75
TearDown()76 void TearDown() {
77 SKIP_IF_BPF_NOT_SUPPORTED;
78
79 const auto deleteTestCookieEntries = [](const uint64_t& key, const UidTag& value,
80 BpfMap<uint64_t, UidTag>& map) {
81 if ((value.uid == TEST_UID) && (value.tag == TEST_TAG)) {
82 Status res = map.deleteValue(key);
83 if (isOk(res) || (res.code() == ENOENT)) {
84 return android::netdutils::status::ok;
85 }
86 ALOGE("Failed to delete data(cookie = %" PRIu64 "): %s\n", key,
87 strerror(res.code()));
88 }
89 // Move forward to next cookie in the map.
90 return android::netdutils::status::ok;
91 };
92 EXPECT_OK(mCookieTagMap.iterateWithValue(deleteTestCookieEntries));
93 }
94
checkNoGarbageTagsExist()95 Status checkNoGarbageTagsExist() {
96 const auto checkGarbageTags = [](const uint64_t&, const UidTag& value,
97 const BpfMap<uint64_t, UidTag>&) {
98 if ((TEST_UID == value.uid) && (TEST_TAG == value.tag)) {
99 return statusFromErrno(EUCLEAN, "Closed socket is not untagged");
100 }
101 return android::netdutils::status::ok;
102 };
103 return mCookieTagMap.iterateWithValue(checkGarbageTags);
104 }
105
checkMassiveSocketDestroy(const int totalNumber,bool expectError)106 void checkMassiveSocketDestroy(const int totalNumber, bool expectError) {
107 std::unique_ptr<android::net::NetlinkListenerInterface> skDestroyListener;
108 auto result = android::net::TrafficController::makeSkDestroyListener();
109 if (!isOk(result)) {
110 ALOGE("Unable to create SkDestroyListener: %s", toString(result).c_str());
111 } else {
112 skDestroyListener = std::move(result.value());
113 }
114 int rxErrorCount = 0;
115 // Rx handler extracts nfgenmsg looks up and invokes registered dispatch function.
116 const auto rxErrorHandler = [&rxErrorCount](const int, const int) { rxErrorCount++; };
117 skDestroyListener->registerSkErrorHandler(rxErrorHandler);
118 int fds[totalNumber];
119 for (int i = 0; i < totalNumber; i++) {
120 fds[i] = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
121 EXPECT_LE(0, fds[i]);
122 qtaguid_tagSocket(fds[i], TEST_TAG, TEST_UID);
123 }
124
125 // TODO: Use a separate thread that have it's own fd table so we can
126 // close socket faster by terminating that threads.
127 for (int i = 0; i < totalNumber; i++) {
128 EXPECT_EQ(0, close(fds[i]));
129 }
130 // wait a bit for netlink listner to handle all the messages.
131 usleep(SOCK_CLOSE_WAIT_US);
132 if (expectError) {
133 // If ENOBUFS triggered, check it only called into the handler once, ie.
134 // that the netlink handler is not spinning.
135 int currentErrorCount = rxErrorCount;
136 EXPECT_LT(0, rxErrorCount);
137 usleep(ENOBUFS_POLL_WAIT_US);
138 EXPECT_EQ(currentErrorCount, rxErrorCount);
139 } else {
140 EXPECT_TRUE(isOk(checkNoGarbageTagsExist()));
141 EXPECT_EQ(0, rxErrorCount);
142 }
143 }
144 };
145
TEST_F(NetlinkListenerTest,TestAllSocketUntagged)146 TEST_F(NetlinkListenerTest, TestAllSocketUntagged) {
147 SKIP_IF_BPF_NOT_SUPPORTED;
148
149 checkMassiveSocketDestroy(10, false);
150 checkMassiveSocketDestroy(100, false);
151 }
152
TEST_F(NetlinkListenerTest,TestSkDestroyError)153 TEST_F(NetlinkListenerTest, TestSkDestroyError) {
154 SKIP_IF_BPF_NOT_SUPPORTED;
155
156 checkMassiveSocketDestroy(20000, true);
157 }
158