blob: 702de479abb2b82549a04e2311aec0a72d0cd739 [file] [log] [blame]
Remi NGUYEN VAN1fc930c2019-08-09 13:23:47 +09001/*
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
17package android.net.netlink;
18
19import static android.net.netlink.StructNlMsgHdr.NLM_F_DUMP;
20import static android.net.netlink.StructNlMsgHdr.NLM_F_REQUEST;
21import static android.system.OsConstants.AF_INET;
22import static android.system.OsConstants.AF_INET6;
23import static android.system.OsConstants.IPPROTO_TCP;
24import static android.system.OsConstants.IPPROTO_UDP;
25import static android.system.OsConstants.SOCK_DGRAM;
26import static android.system.OsConstants.SOCK_STREAM;
27
28import static org.junit.Assert.assertArrayEquals;
29import static org.junit.Assert.assertEquals;
30import static org.junit.Assert.assertNotEquals;
31import static org.junit.Assert.assertNotNull;
32import static org.junit.Assert.assertTrue;
33import static org.junit.Assert.fail;
Remi NGUYEN VAN954eafa2020-02-07 13:32:31 +090034import static org.junit.Assume.assumeTrue;
Remi NGUYEN VAN1fc930c2019-08-09 13:23:47 +090035
36import android.app.Instrumentation;
37import android.content.Context;
38import android.net.ConnectivityManager;
39import android.net.netlink.StructNlMsgHdr;
40import android.os.Process;
41import android.system.Os;
42
43import androidx.test.InstrumentationRegistry;
44import androidx.test.filters.SmallTest;
45import androidx.test.runner.AndroidJUnit4;
46
Remi NGUYEN VAN954eafa2020-02-07 13:32:31 +090047import com.android.networkstack.apishim.ShimUtils;
48
Remi NGUYEN VAN1fc930c2019-08-09 13:23:47 +090049import libcore.util.HexEncoding;
50
51import org.junit.Before;
52import org.junit.Test;
53import org.junit.runner.RunWith;
54
55import java.io.FileDescriptor;
56import java.net.Inet4Address;
57import java.net.Inet6Address;
58import java.net.InetAddress;
59import java.net.InetSocketAddress;
60import java.nio.ByteBuffer;
61import java.nio.ByteOrder;
62
63@RunWith(AndroidJUnit4.class)
64@SmallTest
65public class InetDiagSocketTest {
66 private final String TAG = "InetDiagSocketTest";
67 private ConnectivityManager mCm;
68 private Context mContext;
69 private final static int SOCKET_TIMEOUT_MS = 100;
70
71 @Before
72 public void setUp() throws Exception {
73 Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
74 mContext = instrumentation.getTargetContext();
75 mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
76 }
77
78 private class Connection {
79 public int socketDomain;
80 public int socketType;
81 public InetAddress localAddress;
82 public InetAddress remoteAddress;
83 public InetAddress localhostAddress;
84 public InetSocketAddress local;
85 public InetSocketAddress remote;
86 public int protocol;
87 public FileDescriptor localFd;
88 public FileDescriptor remoteFd;
89
90 public FileDescriptor createSocket() throws Exception {
91 return Os.socket(socketDomain, socketType, protocol);
92 }
93
94 public Connection(String to, String from) throws Exception {
95 remoteAddress = InetAddress.getByName(to);
96 if (from != null) {
97 localAddress = InetAddress.getByName(from);
98 } else {
99 localAddress = (remoteAddress instanceof Inet4Address) ?
100 Inet4Address.getByName("localhost") : Inet6Address.getByName("::");
101 }
102 if ((localAddress instanceof Inet4Address) && (remoteAddress instanceof Inet4Address)) {
103 socketDomain = AF_INET;
104 localhostAddress = Inet4Address.getByName("localhost");
105 } else {
106 socketDomain = AF_INET6;
107 localhostAddress = Inet6Address.getByName("::");
108 }
109 }
110
111 public void close() throws Exception {
112 Os.close(localFd);
113 }
114 }
115
116 private class TcpConnection extends Connection {
117 public TcpConnection(String to, String from) throws Exception {
118 super(to, from);
119 protocol = IPPROTO_TCP;
120 socketType = SOCK_STREAM;
121
122 remoteFd = createSocket();
123 Os.bind(remoteFd, remoteAddress, 0);
124 Os.listen(remoteFd, 10);
125 int remotePort = ((InetSocketAddress) Os.getsockname(remoteFd)).getPort();
126
127 localFd = createSocket();
128 Os.bind(localFd, localAddress, 0);
129 Os.connect(localFd, remoteAddress, remotePort);
130
131 local = (InetSocketAddress) Os.getsockname(localFd);
132 remote = (InetSocketAddress) Os.getpeername(localFd);
133 }
134
135 public void close() throws Exception {
136 super.close();
137 Os.close(remoteFd);
138 }
139 }
140 private class UdpConnection extends Connection {
141 public UdpConnection(String to, String from) throws Exception {
142 super(to, from);
143 protocol = IPPROTO_UDP;
144 socketType = SOCK_DGRAM;
145
146 remoteFd = null;
147 localFd = createSocket();
148 Os.bind(localFd, localAddress, 0);
149
150 Os.connect(localFd, remoteAddress, 7);
151 local = (InetSocketAddress) Os.getsockname(localFd);
152 remote = new InetSocketAddress(remoteAddress, 7);
153 }
154 }
155
156 private void checkConnectionOwnerUid(int protocol, InetSocketAddress local,
157 InetSocketAddress remote, boolean expectSuccess) {
158 final int uid = mCm.getConnectionOwnerUid(protocol, local, remote);
159
160 if (expectSuccess) {
161 assertEquals(Process.myUid(), uid);
162 } else {
163 assertNotEquals(Process.myUid(), uid);
164 }
165 }
166
167 private int findLikelyFreeUdpPort(UdpConnection conn) throws Exception {
168 UdpConnection udp = new UdpConnection(conn.remoteAddress.getHostAddress(),
169 conn.localAddress.getHostAddress());
170 final int localPort = udp.local.getPort();
171 udp.close();
172 return localPort;
173 }
174
175 /**
176 * Create a test connection for UDP and TCP sockets and verify that this
177 * {protocol, local, remote} socket result in receiving a valid UID.
178 */
179 public void checkGetConnectionOwnerUid(String to, String from) throws Exception {
180 TcpConnection tcp = new TcpConnection(to, from);
181 checkConnectionOwnerUid(tcp.protocol, tcp.local, tcp.remote, true);
182 checkConnectionOwnerUid(IPPROTO_UDP, tcp.local, tcp.remote, false);
183 checkConnectionOwnerUid(tcp.protocol, new InetSocketAddress(0), tcp.remote, false);
184 checkConnectionOwnerUid(tcp.protocol, tcp.local, new InetSocketAddress(0), false);
185 tcp.close();
186
187 UdpConnection udp = new UdpConnection(to,from);
188 checkConnectionOwnerUid(udp.protocol, udp.local, udp.remote, true);
189 checkConnectionOwnerUid(IPPROTO_TCP, udp.local, udp.remote, false);
190 checkConnectionOwnerUid(udp.protocol, new InetSocketAddress(findLikelyFreeUdpPort(udp)),
191 udp.remote, false);
192 udp.close();
193 }
194
195 @Test
196 public void testGetConnectionOwnerUid() throws Exception {
Remi NGUYEN VAN954eafa2020-02-07 13:32:31 +0900197 // Skip the test for API <= Q, as b/141603906 this was only fixed in Q-QPR2
Lorenzo Colitti0d08b442020-04-27 11:49:39 +0900198 assumeTrue(ShimUtils.isAtLeastR());
Remi NGUYEN VAN1fc930c2019-08-09 13:23:47 +0900199 checkGetConnectionOwnerUid("::", null);
200 checkGetConnectionOwnerUid("::", "::");
201 checkGetConnectionOwnerUid("0.0.0.0", null);
202 checkGetConnectionOwnerUid("0.0.0.0", "0.0.0.0");
203 checkGetConnectionOwnerUid("127.0.0.1", null);
204 checkGetConnectionOwnerUid("127.0.0.1", "127.0.0.2");
205 checkGetConnectionOwnerUid("::1", null);
206 checkGetConnectionOwnerUid("::1", "::1");
207 }
208
209 /* Verify fix for b/141603906 */
210 @Test
211 public void testB141603906() throws Exception {
Remi NGUYEN VAN954eafa2020-02-07 13:32:31 +0900212 // Skip the test for API <= Q, as b/141603906 this was only fixed in Q-QPR2
Lorenzo Colitti0d08b442020-04-27 11:49:39 +0900213 assumeTrue(ShimUtils.isAtLeastR());
Remi NGUYEN VAN1fc930c2019-08-09 13:23:47 +0900214 final InetSocketAddress src = new InetSocketAddress(0);
215 final InetSocketAddress dst = new InetSocketAddress(0);
216 final int numThreads = 8;
217 final int numSockets = 5000;
218 final Thread[] threads = new Thread[numThreads];
219
220 for (int i = 0; i < numThreads; i++) {
221 threads[i] = new Thread(() -> {
222 for (int j = 0; j < numSockets; j++) {
223 mCm.getConnectionOwnerUid(IPPROTO_TCP, src, dst);
224 }
225 });
226 }
227
228 for (Thread thread : threads) {
229 thread.start();
230 }
231
232 for (Thread thread : threads) {
233 thread.join();
234 }
235 }
236
237 // Hexadecimal representation of InetDiagReqV2 request.
238 private static final String INET_DIAG_REQ_V2_UDP_INET4_HEX =
239 // struct nlmsghdr
240 "48000000" + // length = 72
241 "1400" + // type = SOCK_DIAG_BY_FAMILY
242 "0103" + // flags = NLM_F_REQUEST | NLM_F_DUMP
243 "00000000" + // seqno
244 "00000000" + // pid (0 == kernel)
245 // struct inet_diag_req_v2
246 "02" + // family = AF_INET
247 "11" + // protcol = IPPROTO_UDP
248 "00" + // idiag_ext
249 "00" + // pad
250 "ffffffff" + // idiag_states
251 // inet_diag_sockid
252 "a5de" + // idiag_sport = 42462
253 "b971" + // idiag_dport = 47473
254 "0a006402000000000000000000000000" + // idiag_src = 10.0.100.2
255 "08080808000000000000000000000000" + // idiag_dst = 8.8.8.8
256 "00000000" + // idiag_if
257 "ffffffffffffffff"; // idiag_cookie = INET_DIAG_NOCOOKIE
258 private static final byte[] INET_DIAG_REQ_V2_UDP_INET4_BYTES =
259 HexEncoding.decode(INET_DIAG_REQ_V2_UDP_INET4_HEX.toCharArray(), false);
260
261 @Test
262 public void testInetDiagReqV2UdpInet4() throws Exception {
263 InetSocketAddress local = new InetSocketAddress(InetAddress.getByName("10.0.100.2"),
264 42462);
265 InetSocketAddress remote = new InetSocketAddress(InetAddress.getByName("8.8.8.8"),
266 47473);
267 final byte[] msg = InetDiagMessage.InetDiagReqV2(IPPROTO_UDP, local, remote, AF_INET,
268 (short) (NLM_F_REQUEST | NLM_F_DUMP));
269 assertArrayEquals(INET_DIAG_REQ_V2_UDP_INET4_BYTES, msg);
270 }
271
272 // Hexadecimal representation of InetDiagReqV2 request.
273 private static final String INET_DIAG_REQ_V2_TCP_INET6_HEX =
274 // struct nlmsghdr
275 "48000000" + // length = 72
276 "1400" + // type = SOCK_DIAG_BY_FAMILY
277 "0100" + // flags = NLM_F_REQUEST
278 "00000000" + // seqno
279 "00000000" + // pid (0 == kernel)
280 // struct inet_diag_req_v2
281 "0a" + // family = AF_INET6
282 "06" + // protcol = IPPROTO_TCP
283 "00" + // idiag_ext
284 "00" + // pad
285 "ffffffff" + // idiag_states
286 // inet_diag_sockid
287 "a5de" + // idiag_sport = 42462
288 "b971" + // idiag_dport = 47473
289 "fe8000000000000086c9b2fffe6aed4b" + // idiag_src = fe80::86c9:b2ff:fe6a:ed4b
290 "08080808000000000000000000000000" + // idiag_dst = 8.8.8.8
291 "00000000" + // idiag_if
292 "ffffffffffffffff"; // idiag_cookie = INET_DIAG_NOCOOKIE
293 private static final byte[] INET_DIAG_REQ_V2_TCP_INET6_BYTES =
294 HexEncoding.decode(INET_DIAG_REQ_V2_TCP_INET6_HEX.toCharArray(), false);
295
296 @Test
297 public void testInetDiagReqV2TcpInet6() throws Exception {
298 InetSocketAddress local = new InetSocketAddress(
299 InetAddress.getByName("fe80::86c9:b2ff:fe6a:ed4b"), 42462);
300 InetSocketAddress remote = new InetSocketAddress(InetAddress.getByName("8.8.8.8"),
301 47473);
302 byte[] msg = InetDiagMessage.InetDiagReqV2(IPPROTO_TCP, local, remote, AF_INET6,
303 NLM_F_REQUEST);
304
305 assertArrayEquals(INET_DIAG_REQ_V2_TCP_INET6_BYTES, msg);
306 }
307
308 // Hexadecimal representation of InetDiagReqV2 request with extension, INET_DIAG_INFO.
309 private static final String INET_DIAG_REQ_V2_TCP_INET_INET_DIAG_HEX =
310 // struct nlmsghdr
311 "48000000" + // length = 72
312 "1400" + // type = SOCK_DIAG_BY_FAMILY
313 "0100" + // flags = NLM_F_REQUEST
314 "00000000" + // seqno
315 "00000000" + // pid (0 == kernel)
316 // struct inet_diag_req_v2
317 "02" + // family = AF_INET
318 "06" + // protcol = IPPROTO_TCP
319 "02" + // idiag_ext = INET_DIAG_INFO
320 "00" + // pad
321 "ffffffff" + // idiag_states
322 // inet_diag_sockid
323 "3039" + // idiag_sport = 12345
324 "d431" + // idiag_dport = 54321
325 "01020304000000000000000000000000" + // idiag_src = 1.2.3.4
326 "08080404000000000000000000000000" + // idiag_dst = 8.8.4.4
327 "00000000" + // idiag_if
328 "ffffffffffffffff"; // idiag_cookie = INET_DIAG_NOCOOKIE
329
330 private static final byte[] INET_DIAG_REQ_V2_TCP_INET_INET_DIAG_BYTES =
331 HexEncoding.decode(INET_DIAG_REQ_V2_TCP_INET_INET_DIAG_HEX.toCharArray(), false);
332 private static final int TCP_ALL_STATES = 0xffffffff;
333 @Test
334 public void testInetDiagReqV2TcpInetWithExt() throws Exception {
335 InetSocketAddress local = new InetSocketAddress(
336 InetAddress.getByName("1.2.3.4"), 12345);
337 InetSocketAddress remote = new InetSocketAddress(InetAddress.getByName("8.8.4.4"),
338 54321);
339 byte[] msg = InetDiagMessage.InetDiagReqV2(IPPROTO_TCP, local, remote, AF_INET,
340 NLM_F_REQUEST, 0 /* pad */, 2 /* idiagExt */, TCP_ALL_STATES);
341
342 assertArrayEquals(INET_DIAG_REQ_V2_TCP_INET_INET_DIAG_BYTES, msg);
343
344 local = new InetSocketAddress(
345 InetAddress.getByName("fe80::86c9:b2ff:fe6a:ed4b"), 42462);
346 remote = new InetSocketAddress(InetAddress.getByName("8.8.8.8"),
347 47473);
348 msg = InetDiagMessage.InetDiagReqV2(IPPROTO_TCP, local, remote, AF_INET6,
349 NLM_F_REQUEST, 0 /* pad */, 0 /* idiagExt */, TCP_ALL_STATES);
350
351 assertArrayEquals(INET_DIAG_REQ_V2_TCP_INET6_BYTES, msg);
352 }
353
354 // Hexadecimal representation of InetDiagReqV2 request with no socket specified.
355 private static final String INET_DIAG_REQ_V2_TCP_INET6_NO_ID_SPECIFIED_HEX =
356 // struct nlmsghdr
357 "48000000" + // length = 72
358 "1400" + // type = SOCK_DIAG_BY_FAMILY
359 "0100" + // flags = NLM_F_REQUEST
360 "00000000" + // seqno
361 "00000000" + // pid (0 == kernel)
362 // struct inet_diag_req_v2
363 "0a" + // family = AF_INET6
364 "06" + // protcol = IPPROTO_TCP
365 "00" + // idiag_ext
366 "00" + // pad
367 "ffffffff" + // idiag_states
368 // inet_diag_sockid
369 "0000" + // idiag_sport
370 "0000" + // idiag_dport
371 "00000000000000000000000000000000" + // idiag_src
372 "00000000000000000000000000000000" + // idiag_dst
373 "00000000" + // idiag_if
374 "0000000000000000"; // idiag_cookie
375
376 private static final byte[] INET_DIAG_REQ_V2_TCP_INET6_NO_ID_SPECIFED_BYTES =
377 HexEncoding.decode(INET_DIAG_REQ_V2_TCP_INET6_NO_ID_SPECIFIED_HEX.toCharArray(), false);
378
379 @Test
380 public void testInetDiagReqV2TcpInet6NoIdSpecified() throws Exception {
381 InetSocketAddress local = new InetSocketAddress(
382 InetAddress.getByName("fe80::fe6a:ed4b"), 12345);
383 InetSocketAddress remote = new InetSocketAddress(InetAddress.getByName("8.8.4.4"),
384 54321);
385 // Verify no socket specified if either local or remote socket address is null.
386 byte[] msgExt = InetDiagMessage.InetDiagReqV2(IPPROTO_TCP, null, null, AF_INET6,
387 NLM_F_REQUEST, 0 /* pad */, 0 /* idiagExt */, TCP_ALL_STATES);
388 byte[] msg;
389 try {
390 msg = InetDiagMessage.InetDiagReqV2(IPPROTO_TCP, null, remote, AF_INET6,
391 NLM_F_REQUEST);
392 fail("Both remote and local should be null, expected UnknownHostException");
393 } catch (NullPointerException e) {
394 }
395
396 try {
397 msg = InetDiagMessage.InetDiagReqV2(IPPROTO_TCP, local, null, AF_INET6,
398 NLM_F_REQUEST, 0 /* pad */, 0 /* idiagExt */, TCP_ALL_STATES);
399 fail("Both remote and local should be null, expected UnknownHostException");
400 } catch (NullPointerException e) {
401 }
402
403 msg = InetDiagMessage.InetDiagReqV2(IPPROTO_TCP, null, null, AF_INET6,
404 NLM_F_REQUEST, 0 /* pad */, 0 /* idiagExt */, TCP_ALL_STATES);
405 assertArrayEquals(INET_DIAG_REQ_V2_TCP_INET6_NO_ID_SPECIFED_BYTES, msg);
406 assertArrayEquals(INET_DIAG_REQ_V2_TCP_INET6_NO_ID_SPECIFED_BYTES, msgExt);
407 }
408
409 // Hexadecimal representation of InetDiagReqV2 request.
410 private static final String INET_DIAG_MSG_HEX =
411 // struct nlmsghdr
412 "58000000" + // length = 88
413 "1400" + // type = SOCK_DIAG_BY_FAMILY
414 "0200" + // flags = NLM_F_MULTI
415 "00000000" + // seqno
416 "f5220000" + // pid (0 == kernel)
417 // struct inet_diag_msg
418 "0a" + // family = AF_INET6
419 "01" + // idiag_state
420 "00" + // idiag_timer
421 "00" + // idiag_retrans
422 // inet_diag_sockid
423 "a817" + // idiag_sport = 43031
424 "960f" + // idiag_dport = 38415
425 "fe8000000000000086c9b2fffe6aed4b" + // idiag_src = fe80::86c9:b2ff:fe6a:ed4b
426 "00000000000000000000ffff08080808" + // idiag_dst = 8.8.8.8
427 "00000000" + // idiag_if
428 "ffffffffffffffff" + // idiag_cookie = INET_DIAG_NOCOOKIE
429 "00000000" + // idiag_expires
430 "00000000" + // idiag_rqueue
431 "00000000" + // idiag_wqueue
432 "a3270000" + // idiag_uid
433 "A57E1900"; // idiag_inode
434 private static final byte[] INET_DIAG_MSG_BYTES =
435 HexEncoding.decode(INET_DIAG_MSG_HEX.toCharArray(), false);
436
437 @Test
438 public void testParseInetDiagResponse() throws Exception {
439 final ByteBuffer byteBuffer = ByteBuffer.wrap(INET_DIAG_MSG_BYTES);
440 byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
441 final NetlinkMessage msg = NetlinkMessage.parse(byteBuffer);
442 assertNotNull(msg);
443
444 assertTrue(msg instanceof InetDiagMessage);
445 final InetDiagMessage inetDiagMsg = (InetDiagMessage) msg;
446 assertEquals(10147, inetDiagMsg.mStructInetDiagMsg.idiag_uid);
447
448 final StructNlMsgHdr hdr = inetDiagMsg.getHeader();
449 assertNotNull(hdr);
450 assertEquals(NetlinkConstants.SOCK_DIAG_BY_FAMILY, hdr.nlmsg_type);
451 assertEquals(StructNlMsgHdr.NLM_F_MULTI, hdr.nlmsg_flags);
452 assertEquals(0, hdr.nlmsg_seq);
453 assertEquals(8949, hdr.nlmsg_pid);
454 }
455}