Tyler Wear | b37f551 | 2021-10-01 13:22:00 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2022 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 | |
Tyler Wear | 4e7aced | 2022-04-19 17:50:04 -0700 | [diff] [blame] | 17 | #include <aidl/android/net/connectivity/aidl/ConnectivityNative.h> |
| 18 | #include <android/binder_manager.h> |
| 19 | #include <android/binder_process.h> |
Tyler Wear | b37f551 | 2021-10-01 13:22:00 -0700 | [diff] [blame] | 20 | #include <android-modules-utils/sdk_level.h> |
| 21 | #include <cutils/misc.h> // FIRST_APPLICATION_UID |
| 22 | #include <gtest/gtest.h> |
| 23 | #include <netinet/in.h> |
Tyler Wear | b37f551 | 2021-10-01 13:22:00 -0700 | [diff] [blame] | 24 | |
Tyler Wear | 4e7aced | 2022-04-19 17:50:04 -0700 | [diff] [blame] | 25 | #include "bpf/BpfUtils.h" |
Tyler Wear | b37f551 | 2021-10-01 13:22:00 -0700 | [diff] [blame] | 26 | |
| 27 | using aidl::android::net::connectivity::aidl::IConnectivityNative; |
| 28 | |
| 29 | class ConnectivityNativeBinderTest : public ::testing::Test { |
| 30 | public: |
| 31 | std::vector<int32_t> mActualBlockedPorts; |
| 32 | |
| 33 | ConnectivityNativeBinderTest() { |
| 34 | AIBinder* binder = AServiceManager_getService("connectivity_native"); |
| 35 | ndk::SpAIBinder sBinder = ndk::SpAIBinder(binder); |
| 36 | mService = aidl::android::net::connectivity::aidl::IConnectivityNative::fromBinder(sBinder); |
| 37 | } |
| 38 | |
| 39 | void SetUp() override { |
| 40 | // Skip test case if not on T. |
| 41 | if (!android::modules::sdklevel::IsAtLeastT()) GTEST_SKIP() << |
| 42 | "Should be at least T device."; |
| 43 | |
Tyler Wear | 4e7aced | 2022-04-19 17:50:04 -0700 | [diff] [blame] | 44 | // Skip test case if not on 5.4 kernel which is required by bpf prog. |
| 45 | if (!android::bpf::isAtLeastKernelVersion(5, 4, 0)) GTEST_SKIP() << |
| 46 | "Kernel should be at least 5.4."; |
| 47 | |
Tyler Wear | b37f551 | 2021-10-01 13:22:00 -0700 | [diff] [blame] | 48 | ASSERT_NE(nullptr, mService.get()); |
| 49 | |
| 50 | // If there are already ports being blocked on device unblockAllPortsForBind() store |
| 51 | // the currently blocked ports and add them back at the end of the test. Do this for |
| 52 | // every test case so additional test cases do not forget to add ports back. |
| 53 | ndk::ScopedAStatus status = mService->getPortsBlockedForBind(&mActualBlockedPorts); |
| 54 | EXPECT_TRUE(status.isOk()) << status.getDescription (); |
| 55 | |
| 56 | } |
| 57 | |
| 58 | void TearDown() override { |
| 59 | ndk::ScopedAStatus status; |
| 60 | if (mActualBlockedPorts.size() > 0) { |
| 61 | for (int i : mActualBlockedPorts) { |
| 62 | mService->blockPortForBind(i); |
| 63 | EXPECT_TRUE(status.isOk()) << status.getDescription (); |
| 64 | } |
| 65 | } |
| 66 | } |
| 67 | |
| 68 | protected: |
| 69 | std::shared_ptr<IConnectivityNative> mService; |
| 70 | |
| 71 | void runSocketTest (sa_family_t family, const int type, bool blockPort) { |
| 72 | ndk::ScopedAStatus status; |
| 73 | in_port_t port = 0; |
| 74 | int sock, sock2; |
| 75 | // Open two sockets with SO_REUSEADDR and expect they can both bind to port. |
| 76 | sock = openSocket(&port, family, type, false /* expectBindFail */); |
| 77 | sock2 = openSocket(&port, family, type, false /* expectBindFail */); |
| 78 | |
| 79 | int blockedPort = 0; |
| 80 | if (blockPort) { |
| 81 | blockedPort = ntohs(port); |
| 82 | status = mService->blockPortForBind(blockedPort); |
| 83 | EXPECT_TRUE(status.isOk()) << status.getDescription (); |
| 84 | } |
| 85 | |
| 86 | int sock3 = openSocket(&port, family, type, blockPort /* expectBindFail */); |
| 87 | |
| 88 | if (blockPort) { |
| 89 | EXPECT_EQ(-1, sock3); |
| 90 | status = mService->unblockPortForBind(blockedPort); |
| 91 | EXPECT_TRUE(status.isOk()) << status.getDescription (); |
| 92 | } else { |
| 93 | EXPECT_NE(-1, sock3); |
| 94 | } |
| 95 | |
| 96 | close(sock); |
| 97 | close(sock2); |
| 98 | close(sock3); |
| 99 | } |
| 100 | |
| 101 | /* |
| 102 | * Open the socket and update the port. |
| 103 | */ |
| 104 | int openSocket(in_port_t* port, sa_family_t family, const int type, bool expectBindFail) { |
| 105 | int ret = 0; |
| 106 | int enable = 1; |
| 107 | const int sock = socket(family, type, 0); |
| 108 | ret = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)); |
| 109 | EXPECT_EQ(0, ret); |
| 110 | |
| 111 | if (family == AF_INET) { |
| 112 | struct sockaddr_in addr4 = { .sin_family = family, .sin_port = htons(*port) }; |
| 113 | ret = bind(sock, (struct sockaddr*) &addr4, sizeof(addr4)); |
| 114 | } else { |
| 115 | struct sockaddr_in6 addr6 = { .sin6_family = family, .sin6_port = htons(*port) }; |
| 116 | ret = bind(sock, (struct sockaddr*) &addr6, sizeof(addr6)); |
| 117 | } |
| 118 | |
| 119 | if (expectBindFail) { |
| 120 | EXPECT_NE(0, ret); |
| 121 | // If port is blocked, return here since the port is not needed |
| 122 | // for subsequent sockets. |
| 123 | close(sock); |
| 124 | return -1; |
| 125 | } |
| 126 | EXPECT_EQ(0, ret) << "bind unexpectedly failed, errno: " << errno; |
| 127 | |
| 128 | if (family == AF_INET) { |
| 129 | struct sockaddr_in sin; |
| 130 | socklen_t len = sizeof(sin); |
| 131 | EXPECT_NE(-1, getsockname(sock, (struct sockaddr *)&sin, &len)); |
| 132 | EXPECT_NE(0, ntohs(sin.sin_port)); |
| 133 | if (*port != 0) EXPECT_EQ(*port, ntohs(sin.sin_port)); |
| 134 | *port = ntohs(sin.sin_port); |
| 135 | } else { |
| 136 | struct sockaddr_in6 sin; |
| 137 | socklen_t len = sizeof(sin); |
| 138 | EXPECT_NE(-1, getsockname(sock, (struct sockaddr *)&sin, &len)); |
| 139 | EXPECT_NE(0, ntohs(sin.sin6_port)); |
| 140 | if (*port != 0) EXPECT_EQ(*port, ntohs(sin.sin6_port)); |
| 141 | *port = ntohs(sin.sin6_port); |
| 142 | } |
| 143 | return sock; |
| 144 | } |
| 145 | }; |
| 146 | |
| 147 | TEST_F(ConnectivityNativeBinderTest, PortUnblockedV4Udp) { |
| 148 | runSocketTest(AF_INET, SOCK_DGRAM, false); |
| 149 | } |
| 150 | |
| 151 | TEST_F(ConnectivityNativeBinderTest, PortUnblockedV4Tcp) { |
| 152 | runSocketTest(AF_INET, SOCK_STREAM, false); |
| 153 | } |
| 154 | |
| 155 | TEST_F(ConnectivityNativeBinderTest, PortUnblockedV6Udp) { |
| 156 | runSocketTest(AF_INET6, SOCK_DGRAM, false); |
| 157 | } |
| 158 | |
| 159 | TEST_F(ConnectivityNativeBinderTest, PortUnblockedV6Tcp) { |
| 160 | runSocketTest(AF_INET6, SOCK_STREAM, false); |
| 161 | } |
| 162 | |
| 163 | TEST_F(ConnectivityNativeBinderTest, BlockPort4Udp) { |
| 164 | runSocketTest(AF_INET, SOCK_DGRAM, true); |
| 165 | } |
| 166 | |
| 167 | TEST_F(ConnectivityNativeBinderTest, BlockPort4Tcp) { |
| 168 | runSocketTest(AF_INET, SOCK_STREAM, true); |
| 169 | } |
| 170 | |
| 171 | TEST_F(ConnectivityNativeBinderTest, BlockPort6Udp) { |
| 172 | runSocketTest(AF_INET6, SOCK_DGRAM, true); |
| 173 | } |
| 174 | |
| 175 | TEST_F(ConnectivityNativeBinderTest, BlockPort6Tcp) { |
| 176 | runSocketTest(AF_INET6, SOCK_STREAM, true); |
| 177 | } |
| 178 | |
| 179 | TEST_F(ConnectivityNativeBinderTest, BlockPortTwice) { |
| 180 | ndk::ScopedAStatus status = mService->blockPortForBind(5555); |
| 181 | EXPECT_TRUE(status.isOk()) << status.getDescription (); |
| 182 | status = mService->blockPortForBind(5555); |
| 183 | EXPECT_TRUE(status.isOk()) << status.getDescription (); |
| 184 | status = mService->unblockPortForBind(5555); |
| 185 | EXPECT_TRUE(status.isOk()) << status.getDescription (); |
| 186 | } |
| 187 | |
| 188 | TEST_F(ConnectivityNativeBinderTest, GetBlockedPorts) { |
| 189 | ndk::ScopedAStatus status; |
| 190 | std::vector<int> blockedPorts{1, 100, 1220, 1333, 2700, 5555, 5600, 65000}; |
| 191 | for (int i : blockedPorts) { |
| 192 | status = mService->blockPortForBind(i); |
| 193 | EXPECT_TRUE(status.isOk()) << status.getDescription (); |
| 194 | } |
| 195 | std::vector<int32_t> actualBlockedPorts; |
| 196 | status = mService->getPortsBlockedForBind(&actualBlockedPorts); |
| 197 | EXPECT_TRUE(status.isOk()) << status.getDescription (); |
| 198 | EXPECT_FALSE(actualBlockedPorts.empty()); |
| 199 | EXPECT_EQ(blockedPorts, actualBlockedPorts); |
| 200 | |
| 201 | // Remove the ports we added. |
| 202 | status = mService->unblockAllPortsForBind(); |
| 203 | EXPECT_TRUE(status.isOk()) << status.getDescription (); |
| 204 | status = mService->getPortsBlockedForBind(&actualBlockedPorts); |
| 205 | EXPECT_TRUE(status.isOk()) << status.getDescription (); |
| 206 | EXPECT_TRUE(actualBlockedPorts.empty()); |
| 207 | } |
| 208 | |
| 209 | TEST_F(ConnectivityNativeBinderTest, UnblockAllPorts) { |
| 210 | ndk::ScopedAStatus status; |
| 211 | std::vector<int> blockedPorts{1, 100, 1220, 1333, 2700, 5555, 5600, 65000}; |
| 212 | |
| 213 | if (mActualBlockedPorts.size() > 0) { |
| 214 | status = mService->unblockAllPortsForBind(); |
| 215 | } |
| 216 | |
| 217 | for (int i : blockedPorts) { |
| 218 | status = mService->blockPortForBind(i); |
| 219 | EXPECT_TRUE(status.isOk()) << status.getDescription (); |
| 220 | } |
| 221 | |
| 222 | std::vector<int32_t> actualBlockedPorts; |
| 223 | status = mService->getPortsBlockedForBind(&actualBlockedPorts); |
| 224 | EXPECT_TRUE(status.isOk()) << status.getDescription (); |
| 225 | EXPECT_FALSE(actualBlockedPorts.empty()); |
| 226 | |
| 227 | status = mService->unblockAllPortsForBind(); |
| 228 | EXPECT_TRUE(status.isOk()) << status.getDescription (); |
| 229 | status = mService->getPortsBlockedForBind(&actualBlockedPorts); |
| 230 | EXPECT_TRUE(status.isOk()) << status.getDescription (); |
| 231 | EXPECT_TRUE(actualBlockedPorts.empty()); |
| 232 | // If mActualBlockedPorts is not empty, ports will be added back in teardown. |
| 233 | } |
| 234 | |
| 235 | TEST_F(ConnectivityNativeBinderTest, BlockNegativePort) { |
Steven Moreland | 5d0843b | 2022-06-22 20:27:15 +0000 | [diff] [blame] | 236 | int retry = 0; |
| 237 | ndk::ScopedAStatus status; |
| 238 | do { |
| 239 | status = mService->blockPortForBind(-1); |
| 240 | // TODO: find out why transaction failed is being thrown on the first attempt. |
| 241 | } while (status.getExceptionCode() == EX_TRANSACTION_FAILED && retry++ < 5); |
Tyler Wear | b37f551 | 2021-10-01 13:22:00 -0700 | [diff] [blame] | 242 | EXPECT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode()); |
| 243 | } |
| 244 | |
| 245 | TEST_F(ConnectivityNativeBinderTest, UnblockNegativePort) { |
Steven Moreland | 5d0843b | 2022-06-22 20:27:15 +0000 | [diff] [blame] | 246 | int retry = 0; |
| 247 | ndk::ScopedAStatus status; |
| 248 | do { |
| 249 | status = mService->unblockPortForBind(-1); |
| 250 | // TODO: find out why transaction failed is being thrown on the first attempt. |
| 251 | } while (status.getExceptionCode() == EX_TRANSACTION_FAILED && retry++ < 5); |
Tyler Wear | b37f551 | 2021-10-01 13:22:00 -0700 | [diff] [blame] | 252 | EXPECT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode()); |
| 253 | } |
| 254 | |
| 255 | TEST_F(ConnectivityNativeBinderTest, BlockMaxPort) { |
Steven Moreland | 5d0843b | 2022-06-22 20:27:15 +0000 | [diff] [blame] | 256 | int retry = 0; |
| 257 | ndk::ScopedAStatus status; |
| 258 | do { |
| 259 | status = mService->blockPortForBind(65536); |
| 260 | // TODO: find out why transaction failed is being thrown on the first attempt. |
| 261 | } while (status.getExceptionCode() == EX_TRANSACTION_FAILED && retry++ < 5); |
Tyler Wear | b37f551 | 2021-10-01 13:22:00 -0700 | [diff] [blame] | 262 | EXPECT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode()); |
| 263 | } |
| 264 | |
| 265 | TEST_F(ConnectivityNativeBinderTest, UnblockMaxPort) { |
Steven Moreland | 5d0843b | 2022-06-22 20:27:15 +0000 | [diff] [blame] | 266 | int retry = 0; |
| 267 | ndk::ScopedAStatus status; |
| 268 | do { |
| 269 | status = mService->unblockPortForBind(65536); |
| 270 | // TODO: find out why transaction failed is being thrown on the first attempt. |
| 271 | } while (status.getExceptionCode() == EX_TRANSACTION_FAILED && retry++ < 5); |
Tyler Wear | b37f551 | 2021-10-01 13:22:00 -0700 | [diff] [blame] | 272 | EXPECT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode()); |
| 273 | } |
| 274 | |
| 275 | TEST_F(ConnectivityNativeBinderTest, CheckPermission) { |
Steven Moreland | 5d0843b | 2022-06-22 20:27:15 +0000 | [diff] [blame] | 276 | int retry = 0; |
Tyler Wear | b37f551 | 2021-10-01 13:22:00 -0700 | [diff] [blame] | 277 | int curUid = getuid(); |
| 278 | EXPECT_EQ(0, seteuid(FIRST_APPLICATION_UID + 2000)) << "seteuid failed: " << strerror(errno); |
Steven Moreland | 5d0843b | 2022-06-22 20:27:15 +0000 | [diff] [blame] | 279 | ndk::ScopedAStatus status; |
| 280 | do { |
| 281 | status = mService->blockPortForBind(5555); |
| 282 | // TODO: find out why transaction failed is being thrown on the first attempt. |
| 283 | } while (status.getExceptionCode() == EX_TRANSACTION_FAILED && retry++ < 5); |
Tyler Wear | b37f551 | 2021-10-01 13:22:00 -0700 | [diff] [blame] | 284 | EXPECT_EQ(EX_SECURITY, status.getExceptionCode()); |
| 285 | EXPECT_EQ(0, seteuid(curUid)) << "seteuid failed: " << strerror(errno); |
| 286 | } |