Lorenzo Colitti | 8464e1e | 2016-02-05 00:57:26 +0900 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2016 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 | * sock_diag_test.cpp - unit tests for SockDiag.cpp |
| 17 | */ |
| 18 | |
| 19 | #include <arpa/inet.h> |
| 20 | #include <netinet/in.h> |
| 21 | #include <linux/inet_diag.h> |
| 22 | |
| 23 | #include <gtest/gtest.h> |
| 24 | |
| 25 | #include "NetdConstants.h" |
| 26 | #include "SockDiag.h" |
| 27 | |
| 28 | |
| 29 | #define NUM_SOCKETS 500 |
| 30 | |
| 31 | |
| 32 | class SockDiagTest : public ::testing::Test { |
| 33 | }; |
| 34 | |
| 35 | uint16_t bindAndListen(int s) { |
| 36 | for (int i = 0; i < 10; i++) { |
| 37 | uint16_t port = 1024 + arc4random_uniform(0xffff - 1024); |
| 38 | sockaddr_in6 sin6 = { .sin6_family = AF_INET6, .sin6_port = htons(port) }; |
| 39 | if (bind(s, (sockaddr *) &sin6, sizeof(sin6)) == 0) { |
| 40 | listen(s, 1); |
| 41 | return port; |
| 42 | } |
| 43 | } |
| 44 | close(s); |
| 45 | return 0; |
| 46 | } |
| 47 | |
| 48 | const char *tcpStateName(uint8_t state) { |
| 49 | static const char *states[] = { |
| 50 | "???", |
| 51 | "TCP_ESTABLISHED", |
| 52 | "TCP_SYN_SENT", |
| 53 | "TCP_SYN_RECV", |
| 54 | "TCP_FIN_WAIT1", |
| 55 | "TCP_FIN_WAIT2", |
| 56 | "TCP_TIME_WAIT", |
| 57 | "TCP_CLOSE", |
| 58 | "TCP_CLOSE_WAIT", |
| 59 | "TCP_LAST_ACK", |
| 60 | "TCP_LISTEN", |
| 61 | "TCP_CLOSING", |
| 62 | "TCP_NEW_SYN_RECV", |
| 63 | }; |
| 64 | return states[(state < ARRAY_SIZE(states)) ? state : 0]; |
| 65 | } |
| 66 | |
| 67 | TEST_F(SockDiagTest, TestDump) { |
| 68 | int v4socket = socket(AF_INET, SOCK_STREAM, 0); |
Pierre Imai | b19fcc7 | 2016-03-11 17:54:48 +0900 | [diff] [blame] | 69 | ASSERT_NE(-1, v4socket) << "Failed to open IPv4 socket: " << strerror(errno); |
Lorenzo Colitti | 8464e1e | 2016-02-05 00:57:26 +0900 | [diff] [blame] | 70 | int v6socket = socket(AF_INET6, SOCK_STREAM, 0); |
Pierre Imai | b19fcc7 | 2016-03-11 17:54:48 +0900 | [diff] [blame] | 71 | ASSERT_NE(-1, v6socket) << "Failed to open IPv6 socket: " << strerror(errno); |
Lorenzo Colitti | 8464e1e | 2016-02-05 00:57:26 +0900 | [diff] [blame] | 72 | int listensocket = socket(AF_INET6, SOCK_STREAM, 0); |
Pierre Imai | b19fcc7 | 2016-03-11 17:54:48 +0900 | [diff] [blame] | 73 | ASSERT_NE(-1, listensocket) << "Failed to open listen socket: " << strerror(errno); |
Lorenzo Colitti | 8464e1e | 2016-02-05 00:57:26 +0900 | [diff] [blame] | 74 | |
| 75 | uint16_t port = bindAndListen(listensocket); |
| 76 | ASSERT_NE(0, port) << "Can't bind to server port"; |
| 77 | |
| 78 | // Connect to loopback. |
| 79 | sockaddr_in server4 = { .sin_family = AF_INET, .sin_port = htons(port) }; |
| 80 | sockaddr_in6 server6 = { .sin6_family = AF_INET6, .sin6_port = htons(port) }; |
| 81 | ASSERT_EQ(0, connect(v4socket, (sockaddr *) &server4, sizeof(server4))) |
| 82 | << "IPv4 connect failed: " << strerror(errno); |
| 83 | ASSERT_EQ(0, connect(v6socket, (sockaddr *) &server6, sizeof(server6))) |
| 84 | << "IPv6 connect failed: " << strerror(errno); |
| 85 | |
| 86 | sockaddr_in6 client46, client6; |
| 87 | socklen_t clientlen = std::max(sizeof(client46), sizeof(client6)); |
| 88 | int accepted4 = accept(listensocket, (sockaddr *) &client46, &clientlen); |
| 89 | int accepted6 = accept(listensocket, (sockaddr *) &client6, &clientlen); |
| 90 | ASSERT_NE(-1, accepted4); |
| 91 | ASSERT_NE(-1, accepted6); |
| 92 | |
| 93 | int v4SocketsSeen = 0; |
| 94 | bool seenclient46 = false; |
| 95 | bool seenNull = false; |
| 96 | char src[INET6_ADDRSTRLEN], dst[INET6_ADDRSTRLEN]; |
| 97 | |
| 98 | fprintf(stderr, "Ports:\n server=%d. client46=%d, client6=%d\n", |
| 99 | port, ntohs(client46.sin6_port), ntohs(client6.sin6_port)); |
| 100 | |
| 101 | auto checkIPv4Dump = [&] (uint8_t /* proto */, const inet_diag_msg *msg) { |
| 102 | if (msg == nullptr) { |
| 103 | EXPECT_FALSE(seenNull); |
| 104 | seenNull = true; |
| 105 | return 0; |
| 106 | } |
| 107 | EXPECT_EQ(htonl(INADDR_LOOPBACK), msg->id.idiag_src[0]); |
| 108 | v4SocketsSeen++; |
| 109 | seenclient46 |= (msg->id.idiag_sport == client46.sin6_port); |
| 110 | inet_ntop(AF_INET, msg->id.idiag_src, src, sizeof(src)); |
| 111 | inet_ntop(AF_INET, msg->id.idiag_src, dst, sizeof(dst)); |
| 112 | fprintf(stderr, " v4 %s:%d -> %s:%d %s\n", |
| 113 | src, htons(msg->id.idiag_sport), |
| 114 | dst, htons(msg->id.idiag_dport), |
| 115 | tcpStateName(msg->idiag_state)); |
| 116 | return 0; |
| 117 | }; |
| 118 | |
| 119 | int v6SocketsSeen = 0; |
| 120 | bool seenClient6 = false, seenServer46 = false, seenServer6 = false; |
| 121 | |
| 122 | auto checkIPv6Dump = [&] (uint8_t /* proto */, const inet_diag_msg *msg) { |
| 123 | if (msg == nullptr) { |
| 124 | EXPECT_FALSE(seenNull); |
| 125 | seenNull = true; |
| 126 | return 0; |
| 127 | } |
| 128 | struct in6_addr *saddr = (struct in6_addr *) msg->id.idiag_src; |
| 129 | EXPECT_TRUE( |
| 130 | IN6_IS_ADDR_LOOPBACK(saddr) || |
| 131 | (IN6_IS_ADDR_V4MAPPED(saddr) && saddr->s6_addr32[3] == htonl(INADDR_LOOPBACK))); |
| 132 | v6SocketsSeen++; |
| 133 | seenClient6 |= (msg->id.idiag_sport == client6.sin6_port); |
| 134 | seenServer46 |= (msg->id.idiag_sport == htons(port)); |
| 135 | seenServer6 |= (msg->id.idiag_sport == htons(port)); |
| 136 | inet_ntop(AF_INET6, msg->id.idiag_src, src, sizeof(src)); |
| 137 | inet_ntop(AF_INET6, msg->id.idiag_src, dst, sizeof(dst)); |
| 138 | fprintf(stderr, " v6 [%s]:%d -> [%s]:%d %s\n", |
| 139 | src, htons(msg->id.idiag_sport), |
| 140 | dst, htons(msg->id.idiag_dport), |
| 141 | tcpStateName(msg->idiag_state)); |
| 142 | return 0; |
| 143 | }; |
| 144 | |
| 145 | SockDiag sd; |
| 146 | ASSERT_TRUE(sd.open()) << "Failed to open SOCK_DIAG socket"; |
| 147 | |
| 148 | seenNull = false; |
| 149 | int ret = sd.sendDumpRequest(IPPROTO_TCP, AF_INET, "127.0.0.1"); |
| 150 | ASSERT_EQ(0, ret) << "Failed to send IPv4 dump request: " << strerror(-ret); |
| 151 | fprintf(stderr, "Sent IPv4 dump\n"); |
| 152 | sd.readDiagMsg(IPPROTO_TCP, checkIPv4Dump); |
| 153 | EXPECT_GE(v4SocketsSeen, 1); |
| 154 | EXPECT_TRUE(seenclient46); |
| 155 | EXPECT_FALSE(seenServer46); |
| 156 | |
| 157 | seenNull = false; |
| 158 | ret = sd.sendDumpRequest(IPPROTO_TCP, AF_INET6, "127.0.0.1"); |
| 159 | ASSERT_EQ(0, ret) << "Failed to send mapped dump request: " << strerror(-ret); |
| 160 | fprintf(stderr, "Sent mapped dump\n"); |
| 161 | sd.readDiagMsg(IPPROTO_TCP, checkIPv6Dump); |
| 162 | EXPECT_TRUE(seenServer46); |
| 163 | |
| 164 | seenNull = false; |
| 165 | ret = sd.sendDumpRequest(IPPROTO_TCP, AF_INET6, "::1"); |
| 166 | ASSERT_EQ(0, ret) << "Failed to send IPv6 dump request: " << strerror(-ret); |
| 167 | fprintf(stderr, "Sent IPv6 dump\n"); |
| 168 | |
| 169 | sd.readDiagMsg(IPPROTO_TCP, checkIPv6Dump); |
| 170 | EXPECT_GE(v6SocketsSeen, 1); |
| 171 | EXPECT_TRUE(seenClient6); |
| 172 | EXPECT_TRUE(seenServer6); |
| 173 | |
| 174 | close(v4socket); |
| 175 | close(v6socket); |
| 176 | close(listensocket); |
| 177 | close(accepted4); |
| 178 | close(accepted6); |
| 179 | } |
| 180 | |
Lorenzo Colitti | 1f45771 | 2016-03-24 17:19:28 +0900 | [diff] [blame^] | 181 | |
| 182 | class SockDiagMicroBenchmarkTest : public ::testing::Test { |
| 183 | |
| 184 | public: |
| 185 | void SetUp() { |
| 186 | ASSERT_TRUE(mSd.open()) << "Failed to open SOCK_DIAG socket"; |
| 187 | } |
| 188 | |
| 189 | protected: |
| 190 | SockDiag mSd; |
| 191 | |
| 192 | int destroySockets() { |
| 193 | const int ret = mSd.destroySockets("::1"); |
| 194 | EXPECT_LE(0, ret) << ": Failed to destroy sockets on ::1: " << strerror(-ret); |
| 195 | return ret; |
| 196 | } |
| 197 | |
| 198 | bool shouldHaveClosedSocket(int) { |
| 199 | return true; |
| 200 | } |
| 201 | |
| 202 | void checkSocketState(int i, int sock, const char *msg) { |
| 203 | const char data[] = "foo"; |
| 204 | const int ret = send(sock, data, sizeof(data), 0); |
| 205 | const int err = errno; |
| 206 | if (shouldHaveClosedSocket(i)) { |
| 207 | EXPECT_EQ(-1, ret) << msg << " " << i << " not closed"; |
| 208 | if (ret == -1) { |
| 209 | // Since we're connected to ourselves, the error might be ECONNABORTED (if we |
| 210 | // destroyed the socket) or ECONNRESET (if the other end was destroyed and sent a |
| 211 | // RST). |
| 212 | EXPECT_TRUE(err == ECONNABORTED || err == ECONNRESET) |
| 213 | << msg << ": unexpected error: " << strerror(err); |
| 214 | } |
| 215 | } else { |
| 216 | EXPECT_EQ((ssize_t) sizeof(data), ret) << |
| 217 | "Write on open socket failed: " << strerror(err); |
| 218 | } |
| 219 | } |
| 220 | }; |
| 221 | |
| 222 | TEST_F(SockDiagMicroBenchmarkTest, TestMicroBenchmark) { |
Lorenzo Colitti | 8464e1e | 2016-02-05 00:57:26 +0900 | [diff] [blame] | 223 | fprintf(stderr, "Benchmarking closing %d sockets\n", NUM_SOCKETS); |
| 224 | |
| 225 | int listensocket = socket(AF_INET6, SOCK_STREAM, 0); |
| 226 | ASSERT_NE(-1, listensocket) << "Failed to open listen socket"; |
| 227 | |
| 228 | uint16_t port = bindAndListen(listensocket); |
| 229 | ASSERT_NE(0, port) << "Can't bind to server port"; |
| 230 | sockaddr_in6 server = { .sin6_family = AF_INET6, .sin6_port = htons(port) }; |
| 231 | |
| 232 | using ms = std::chrono::duration<float, std::ratio<1, 1000>>; |
| 233 | |
| 234 | int clientsockets[NUM_SOCKETS], serversockets[NUM_SOCKETS]; |
| 235 | uint16_t clientports[NUM_SOCKETS]; |
| 236 | sockaddr_in6 client; |
| 237 | socklen_t clientlen; |
| 238 | |
| 239 | auto start = std::chrono::steady_clock::now(); |
| 240 | for (int i = 0; i < NUM_SOCKETS; i++) { |
| 241 | int s = socket(AF_INET6, SOCK_STREAM, 0); |
| 242 | clientlen = sizeof(client); |
| 243 | ASSERT_EQ(0, connect(s, (sockaddr *) &server, sizeof(server))) |
| 244 | << "Connecting socket " << i << " failed " << strerror(errno); |
| 245 | serversockets[i] = accept(listensocket, (sockaddr *) &client, &clientlen); |
| 246 | ASSERT_NE(-1, serversockets[i]) |
| 247 | << "Accepting socket " << i << " failed " << strerror(errno); |
| 248 | clientports[i] = client.sin6_port; |
| 249 | clientsockets[i] = s; |
| 250 | } |
| 251 | fprintf(stderr, " Connecting: %6.1f ms\n", |
| 252 | std::chrono::duration_cast<ms>(std::chrono::steady_clock::now() - start).count()); |
| 253 | |
Lorenzo Colitti | 8464e1e | 2016-02-05 00:57:26 +0900 | [diff] [blame] | 254 | start = std::chrono::steady_clock::now(); |
Lorenzo Colitti | 1f45771 | 2016-03-24 17:19:28 +0900 | [diff] [blame^] | 255 | destroySockets(); |
Lorenzo Colitti | 8464e1e | 2016-02-05 00:57:26 +0900 | [diff] [blame] | 256 | fprintf(stderr, " Destroying: %6.1f ms\n", |
| 257 | std::chrono::duration_cast<ms>(std::chrono::steady_clock::now() - start).count()); |
| 258 | |
Lorenzo Colitti | 8464e1e | 2016-02-05 00:57:26 +0900 | [diff] [blame] | 259 | start = std::chrono::steady_clock::now(); |
| 260 | for (int i = 0; i < NUM_SOCKETS; i++) { |
Lorenzo Colitti | 1f45771 | 2016-03-24 17:19:28 +0900 | [diff] [blame^] | 261 | checkSocketState(i, clientsockets[i], "Client socket"); |
| 262 | checkSocketState(i, serversockets[i], "Server socket"); |
Lorenzo Colitti | 8464e1e | 2016-02-05 00:57:26 +0900 | [diff] [blame] | 263 | } |
| 264 | fprintf(stderr, " Verifying: %6.1f ms\n", |
| 265 | std::chrono::duration_cast<ms>(std::chrono::steady_clock::now() - start).count()); |
| 266 | |
Lorenzo Colitti | 8464e1e | 2016-02-05 00:57:26 +0900 | [diff] [blame] | 267 | start = std::chrono::steady_clock::now(); |
| 268 | for (int i = 0; i < NUM_SOCKETS; i++) { |
| 269 | close(clientsockets[i]); |
| 270 | close(serversockets[i]); |
| 271 | } |
| 272 | fprintf(stderr, " Closing: %6.1f ms\n", |
| 273 | std::chrono::duration_cast<ms>(std::chrono::steady_clock::now() - start).count()); |
| 274 | |
| 275 | close(listensocket); |
| 276 | } |