1 /* 2 * Copyright (C) 2017 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 package android.net.netlink; 18 19 import static android.net.netlink.NetlinkConstants.IPCTNL_MSG_CT_NEW; 20 import static android.net.netlink.NetlinkConstants.NFNL_SUBSYS_CTNETLINK; 21 22 import static org.junit.Assert.assertArrayEquals; 23 import static org.junit.Assert.assertEquals; 24 import static org.junit.Assert.assertNotNull; 25 import static org.junit.Assert.assertNull; 26 import static org.junit.Assert.assertTrue; 27 import static org.junit.Assume.assumeTrue; 28 29 import android.system.OsConstants; 30 31 import androidx.test.filters.SmallTest; 32 import androidx.test.runner.AndroidJUnit4; 33 34 import libcore.util.HexEncoding; 35 36 import org.junit.Test; 37 import org.junit.runner.RunWith; 38 39 import java.net.Inet4Address; 40 import java.net.InetAddress; 41 import java.nio.ByteBuffer; 42 import java.nio.ByteOrder; 43 import java.util.Arrays; 44 45 @RunWith(AndroidJUnit4.class) 46 @SmallTest 47 public class ConntrackMessageTest { 48 private static final boolean USING_LE = (ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN); 49 makeCtType(short msgType)50 private short makeCtType(short msgType) { 51 return (short) (NFNL_SUBSYS_CTNETLINK << 8 | (byte) msgType); 52 } 53 54 // Example 1: TCP (192.168.43.209, 44333) -> (23.211.13.26, 443) 55 public static final String CT_V4UPDATE_TCP_HEX = 56 // struct nlmsghdr 57 "50000000" + // length = 80 58 "0001" + // type = (1 << 8) | 0 59 "0501" + // flags 60 "01000000" + // seqno = 1 61 "00000000" + // pid = 0 62 // struct nfgenmsg 63 "02" + // nfgen_family = AF_INET 64 "00" + // version = NFNETLINK_V0 65 "0000" + // res_id 66 // struct nlattr 67 "3400" + // nla_len = 52 68 "0180" + // nla_type = nested CTA_TUPLE_ORIG 69 // struct nlattr 70 "1400" + // nla_len = 20 71 "0180" + // nla_type = nested CTA_TUPLE_IP 72 "0800 0100 C0A82BD1" + // nla_type=CTA_IP_V4_SRC, ip=192.168.43.209 73 "0800 0200 17D30D1A" + // nla_type=CTA_IP_V4_DST, ip=23.211.13.26 74 // struct nlattr 75 "1C00" + // nla_len = 28 76 "0280" + // nla_type = nested CTA_TUPLE_PROTO 77 "0500 0100 06 000000" + // nla_type=CTA_PROTO_NUM, proto=6 78 "0600 0200 AD2D 0000" + // nla_type=CTA_PROTO_SRC_PORT, port=44333 (big endian) 79 "0600 0300 01BB 0000" + // nla_type=CTA_PROTO_DST_PORT, port=443 (big endian) 80 // struct nlattr 81 "0800" + // nla_len = 8 82 "0700" + // nla_type = CTA_TIMEOUT 83 "00069780"; // nla_value = 432000 (big endian) 84 public static final byte[] CT_V4UPDATE_TCP_BYTES = 85 HexEncoding.decode(CT_V4UPDATE_TCP_HEX.replaceAll(" ", "").toCharArray(), false); 86 makeIPv4TimeoutUpdateRequestTcp()87 private byte[] makeIPv4TimeoutUpdateRequestTcp() throws Exception { 88 return ConntrackMessage.newIPv4TimeoutUpdateRequest( 89 OsConstants.IPPROTO_TCP, 90 (Inet4Address) InetAddress.getByName("192.168.43.209"), 44333, 91 (Inet4Address) InetAddress.getByName("23.211.13.26"), 443, 92 432000); 93 } 94 95 // Example 2: UDP (100.96.167.146, 37069) -> (216.58.197.10, 443) 96 public static final String CT_V4UPDATE_UDP_HEX = 97 // struct nlmsghdr 98 "50000000" + // length = 80 99 "0001" + // type = (1 << 8) | 0 100 "0501" + // flags 101 "01000000" + // seqno = 1 102 "00000000" + // pid = 0 103 // struct nfgenmsg 104 "02" + // nfgen_family = AF_INET 105 "00" + // version = NFNETLINK_V0 106 "0000" + // res_id 107 // struct nlattr 108 "3400" + // nla_len = 52 109 "0180" + // nla_type = nested CTA_TUPLE_ORIG 110 // struct nlattr 111 "1400" + // nla_len = 20 112 "0180" + // nla_type = nested CTA_TUPLE_IP 113 "0800 0100 6460A792" + // nla_type=CTA_IP_V4_SRC, ip=100.96.167.146 114 "0800 0200 D83AC50A" + // nla_type=CTA_IP_V4_DST, ip=216.58.197.10 115 // struct nlattr 116 "1C00" + // nla_len = 28 117 "0280" + // nla_type = nested CTA_TUPLE_PROTO 118 "0500 0100 11 000000" + // nla_type=CTA_PROTO_NUM, proto=17 119 "0600 0200 90CD 0000" + // nla_type=CTA_PROTO_SRC_PORT, port=37069 (big endian) 120 "0600 0300 01BB 0000" + // nla_type=CTA_PROTO_DST_PORT, port=443 (big endian) 121 // struct nlattr 122 "0800" + // nla_len = 8 123 "0700" + // nla_type = CTA_TIMEOUT 124 "000000B4"; // nla_value = 180 (big endian) 125 public static final byte[] CT_V4UPDATE_UDP_BYTES = 126 HexEncoding.decode(CT_V4UPDATE_UDP_HEX.replaceAll(" ", "").toCharArray(), false); 127 makeIPv4TimeoutUpdateRequestUdp()128 private byte[] makeIPv4TimeoutUpdateRequestUdp() throws Exception { 129 return ConntrackMessage.newIPv4TimeoutUpdateRequest( 130 OsConstants.IPPROTO_UDP, 131 (Inet4Address) InetAddress.getByName("100.96.167.146"), 37069, 132 (Inet4Address) InetAddress.getByName("216.58.197.10"), 443, 133 180); 134 } 135 136 @Test testConntrackMakeIPv4TcpTimeoutUpdate()137 public void testConntrackMakeIPv4TcpTimeoutUpdate() throws Exception { 138 assumeTrue(USING_LE); 139 140 final byte[] tcp = makeIPv4TimeoutUpdateRequestTcp(); 141 assertArrayEquals(CT_V4UPDATE_TCP_BYTES, tcp); 142 } 143 144 @Test testConntrackParseIPv4TcpTimeoutUpdate()145 public void testConntrackParseIPv4TcpTimeoutUpdate() throws Exception { 146 assumeTrue(USING_LE); 147 148 final byte[] tcp = makeIPv4TimeoutUpdateRequestTcp(); 149 final ByteBuffer byteBuffer = ByteBuffer.wrap(tcp); 150 byteBuffer.order(ByteOrder.nativeOrder()); 151 final NetlinkMessage msg = NetlinkMessage.parse(byteBuffer, OsConstants.NETLINK_NETFILTER); 152 assertNotNull(msg); 153 assertTrue(msg instanceof ConntrackMessage); 154 final ConntrackMessage conntrackMessage = (ConntrackMessage) msg; 155 156 final StructNlMsgHdr hdr = conntrackMessage.getHeader(); 157 assertNotNull(hdr); 158 assertEquals(80, hdr.nlmsg_len); 159 assertEquals(makeCtType(IPCTNL_MSG_CT_NEW), hdr.nlmsg_type); 160 assertEquals((short) (StructNlMsgHdr.NLM_F_REPLACE | StructNlMsgHdr.NLM_F_REQUEST 161 | StructNlMsgHdr.NLM_F_ACK), hdr.nlmsg_flags); 162 assertEquals(1, hdr.nlmsg_seq); 163 assertEquals(0, hdr.nlmsg_pid); 164 165 final StructNfGenMsg nfmsgHdr = conntrackMessage.nfGenMsg; 166 assertNotNull(nfmsgHdr); 167 assertEquals((byte) OsConstants.AF_INET, nfmsgHdr.nfgen_family); 168 assertEquals((byte) StructNfGenMsg.NFNETLINK_V0, nfmsgHdr.version); 169 assertEquals((short) 0, nfmsgHdr.res_id); 170 171 assertEquals(InetAddress.parseNumericAddress("192.168.43.209"), 172 conntrackMessage.tupleOrig.srcIp); 173 assertEquals(InetAddress.parseNumericAddress("23.211.13.26"), 174 conntrackMessage.tupleOrig.dstIp); 175 assertEquals((byte) OsConstants.IPPROTO_TCP, conntrackMessage.tupleOrig.protoNum); 176 assertEquals((short) 44333, conntrackMessage.tupleOrig.srcPort); 177 assertEquals((short) 443, conntrackMessage.tupleOrig.dstPort); 178 179 assertNull(conntrackMessage.tupleReply); 180 181 assertEquals(0 /* absent */, conntrackMessage.status); 182 assertEquals(432000, conntrackMessage.timeoutSec); 183 } 184 185 @Test testConntrackMakeIPv4UdpTimeoutUpdate()186 public void testConntrackMakeIPv4UdpTimeoutUpdate() throws Exception { 187 assumeTrue(USING_LE); 188 189 final byte[] udp = makeIPv4TimeoutUpdateRequestUdp(); 190 assertArrayEquals(CT_V4UPDATE_UDP_BYTES, udp); 191 } 192 193 @Test testConntrackParseIPv4UdpTimeoutUpdate()194 public void testConntrackParseIPv4UdpTimeoutUpdate() throws Exception { 195 assumeTrue(USING_LE); 196 197 final byte[] udp = makeIPv4TimeoutUpdateRequestUdp(); 198 final ByteBuffer byteBuffer = ByteBuffer.wrap(udp); 199 byteBuffer.order(ByteOrder.nativeOrder()); 200 final NetlinkMessage msg = NetlinkMessage.parse(byteBuffer, OsConstants.NETLINK_NETFILTER); 201 assertNotNull(msg); 202 assertTrue(msg instanceof ConntrackMessage); 203 final ConntrackMessage conntrackMessage = (ConntrackMessage) msg; 204 205 final StructNlMsgHdr hdr = conntrackMessage.getHeader(); 206 assertNotNull(hdr); 207 assertEquals(80, hdr.nlmsg_len); 208 assertEquals(makeCtType(IPCTNL_MSG_CT_NEW), hdr.nlmsg_type); 209 assertEquals((short) (StructNlMsgHdr.NLM_F_REPLACE | StructNlMsgHdr.NLM_F_REQUEST 210 | StructNlMsgHdr.NLM_F_ACK), hdr.nlmsg_flags); 211 assertEquals(1, hdr.nlmsg_seq); 212 assertEquals(0, hdr.nlmsg_pid); 213 214 final StructNfGenMsg nfmsgHdr = conntrackMessage.nfGenMsg; 215 assertNotNull(nfmsgHdr); 216 assertEquals((byte) OsConstants.AF_INET, nfmsgHdr.nfgen_family); 217 assertEquals((byte) StructNfGenMsg.NFNETLINK_V0, nfmsgHdr.version); 218 assertEquals((short) 0, nfmsgHdr.res_id); 219 220 assertEquals(InetAddress.parseNumericAddress("100.96.167.146"), 221 conntrackMessage.tupleOrig.srcIp); 222 assertEquals(InetAddress.parseNumericAddress("216.58.197.10"), 223 conntrackMessage.tupleOrig.dstIp); 224 assertEquals((byte) OsConstants.IPPROTO_UDP, conntrackMessage.tupleOrig.protoNum); 225 assertEquals((short) 37069, conntrackMessage.tupleOrig.srcPort); 226 assertEquals((short) 443, conntrackMessage.tupleOrig.dstPort); 227 228 assertNull(conntrackMessage.tupleReply); 229 230 assertEquals(0 /* absent */, conntrackMessage.status); 231 assertEquals(180, conntrackMessage.timeoutSec); 232 } 233 234 public static final String CT_V4NEW_TCP_HEX = 235 // CHECKSTYLE:OFF IndentationCheck 236 // struct nlmsghdr 237 "8C000000" + // length = 140 238 "0001" + // type = NFNL_SUBSYS_CTNETLINK (1) << 8 | IPCTNL_MSG_CT_NEW (0) 239 "0006" + // flags = NLM_F_CREATE (1 << 10) | NLM_F_EXCL (1 << 9) 240 "00000000" + // seqno = 0 241 "00000000" + // pid = 0 242 // struct nfgenmsg 243 "02" + // nfgen_family = AF_INET 244 "00" + // version = NFNETLINK_V0 245 "1234" + // res_id = 0x1234 (big endian) 246 // struct nlattr 247 "3400" + // nla_len = 52 248 "0180" + // nla_type = nested CTA_TUPLE_ORIG 249 // struct nlattr 250 "1400" + // nla_len = 20 251 "0180" + // nla_type = nested CTA_TUPLE_IP 252 "0800 0100 C0A8500C" + // nla_type=CTA_IP_V4_SRC, ip=192.168.80.12 253 "0800 0200 8C700874" + // nla_type=CTA_IP_V4_DST, ip=140.112.8.116 254 // struct nlattr 255 "1C00" + // nla_len = 28 256 "0280" + // nla_type = nested CTA_TUPLE_PROTO 257 "0500 0100 06 000000" + // nla_type=CTA_PROTO_NUM, proto=IPPROTO_TCP (6) 258 "0600 0200 F3F1 0000" + // nla_type=CTA_PROTO_SRC_PORT, port=62449 (big endian) 259 "0600 0300 01BB 0000" + // nla_type=CTA_PROTO_DST_PORT, port=443 (big endian) 260 // struct nlattr 261 "3400" + // nla_len = 52 262 "0280" + // nla_type = nested CTA_TUPLE_REPLY 263 // struct nlattr 264 "1400" + // nla_len = 20 265 "0180" + // nla_type = nested CTA_TUPLE_IP 266 "0800 0100 8C700874" + // nla_type=CTA_IP_V4_SRC, ip=140.112.8.116 267 "0800 0200 6451B301" + // nla_type=CTA_IP_V4_DST, ip=100.81.179.1 268 // struct nlattr 269 "1C00" + // nla_len = 28 270 "0280" + // nla_type = nested CTA_TUPLE_PROTO 271 "0500 0100 06 000000" + // nla_type=CTA_PROTO_NUM, proto=IPPROTO_TCP (6) 272 "0600 0200 01BB 0000" + // nla_type=CTA_PROTO_SRC_PORT, port=443 (big endian) 273 "0600 0300 F3F1 0000" + // nla_type=CTA_PROTO_DST_PORT, port=62449 (big endian) 274 // struct nlattr 275 "0800" + // nla_len = 8 276 "0300" + // nla_type = CTA_STATUS 277 "00000198" + // nla_value = 0b110011000 (big endian) 278 // IPS_CONFIRMED (1 << 3) | IPS_SRC_NAT (1 << 4) | 279 // IPS_SRC_NAT_DONE (1 << 7) | IPS_DST_NAT_DONE (1 << 8) 280 // struct nlattr 281 "0800" + // nla_len = 8 282 "0700" + // nla_type = CTA_TIMEOUT 283 "00000078"; // nla_value = 120 (big endian) 284 // CHECKSTYLE:ON IndentationCheck 285 public static final byte[] CT_V4NEW_TCP_BYTES = 286 HexEncoding.decode(CT_V4NEW_TCP_HEX.replaceAll(" ", "").toCharArray(), false); 287 288 @Test testParseCtNew()289 public void testParseCtNew() { 290 assumeTrue(USING_LE); 291 292 final ByteBuffer byteBuffer = ByteBuffer.wrap(CT_V4NEW_TCP_BYTES); 293 byteBuffer.order(ByteOrder.nativeOrder()); 294 final NetlinkMessage msg = NetlinkMessage.parse(byteBuffer, OsConstants.NETLINK_NETFILTER); 295 assertNotNull(msg); 296 assertTrue(msg instanceof ConntrackMessage); 297 final ConntrackMessage conntrackMessage = (ConntrackMessage) msg; 298 299 final StructNlMsgHdr hdr = conntrackMessage.getHeader(); 300 assertNotNull(hdr); 301 assertEquals(140, hdr.nlmsg_len); 302 assertEquals(makeCtType(IPCTNL_MSG_CT_NEW), hdr.nlmsg_type); 303 assertEquals((short) (StructNlMsgHdr.NLM_F_CREATE | StructNlMsgHdr.NLM_F_EXCL), 304 hdr.nlmsg_flags); 305 assertEquals(0, hdr.nlmsg_seq); 306 assertEquals(0, hdr.nlmsg_pid); 307 308 final StructNfGenMsg nfmsgHdr = conntrackMessage.nfGenMsg; 309 assertNotNull(nfmsgHdr); 310 assertEquals((byte) OsConstants.AF_INET, nfmsgHdr.nfgen_family); 311 assertEquals((byte) StructNfGenMsg.NFNETLINK_V0, nfmsgHdr.version); 312 assertEquals((short) 0x1234, nfmsgHdr.res_id); 313 314 assertEquals(InetAddress.parseNumericAddress("192.168.80.12"), 315 conntrackMessage.tupleOrig.srcIp); 316 assertEquals(InetAddress.parseNumericAddress("140.112.8.116"), 317 conntrackMessage.tupleOrig.dstIp); 318 assertEquals((byte) OsConstants.IPPROTO_TCP, conntrackMessage.tupleOrig.protoNum); 319 assertEquals((short) 62449, conntrackMessage.tupleOrig.srcPort); 320 assertEquals((short) 443, conntrackMessage.tupleOrig.dstPort); 321 322 assertEquals(InetAddress.parseNumericAddress("140.112.8.116"), 323 conntrackMessage.tupleReply.srcIp); 324 assertEquals(InetAddress.parseNumericAddress("100.81.179.1"), 325 conntrackMessage.tupleReply.dstIp); 326 assertEquals((byte) OsConstants.IPPROTO_TCP, conntrackMessage.tupleReply.protoNum); 327 assertEquals((short) 443, conntrackMessage.tupleReply.srcPort); 328 assertEquals((short) 62449, conntrackMessage.tupleReply.dstPort); 329 330 assertEquals(0x198, conntrackMessage.status); 331 assertEquals(120, conntrackMessage.timeoutSec); 332 } 333 334 @Test testParseTruncation()335 public void testParseTruncation() { 336 assumeTrue(USING_LE); 337 338 // Expect no crash while parsing the truncated message which has been truncated to every 339 // length between 0 and its full length - 1. 340 for (int len = 0; len < CT_V4NEW_TCP_BYTES.length; len++) { 341 final byte[] truncated = Arrays.copyOfRange(CT_V4NEW_TCP_BYTES, 0, len); 342 343 final ByteBuffer byteBuffer = ByteBuffer.wrap(truncated); 344 byteBuffer.order(ByteOrder.nativeOrder()); 345 final NetlinkMessage msg = NetlinkMessage.parse(byteBuffer, 346 OsConstants.NETLINK_NETFILTER); 347 } 348 } 349 350 @Test testParseTruncationWithInvalidByte()351 public void testParseTruncationWithInvalidByte() { 352 assumeTrue(USING_LE); 353 354 // Expect no crash while parsing the message which is truncated by invalid bytes. The 355 // message has been truncated to every length between 0 and its full length - 1. 356 for (byte invalid : new byte[]{(byte) 0x00, (byte) 0xff}) { 357 for (int len = 0; len < CT_V4NEW_TCP_BYTES.length; len++) { 358 final byte[] truncated = new byte[CT_V4NEW_TCP_BYTES.length]; 359 Arrays.fill(truncated, (byte) invalid); 360 System.arraycopy(CT_V4NEW_TCP_BYTES, 0, truncated, 0, len); 361 362 final ByteBuffer byteBuffer = ByteBuffer.wrap(truncated); 363 byteBuffer.order(ByteOrder.nativeOrder()); 364 final NetlinkMessage msg = NetlinkMessage.parse(byteBuffer, 365 OsConstants.NETLINK_NETFILTER); 366 } 367 } 368 } 369 370 // Malformed conntrack messages. 371 public static final String CT_MALFORMED_HEX = 372 // CHECKSTYLE:OFF IndentationCheck 373 // <-- nlmsghr -->|<-nfgenmsg->|<-- CTA_TUPLE_ORIG -->| 374 // CTA_TUPLE_ORIG has no nla_value. 375 "18000000 0001 0006 00000000 00000000 02 00 0000 0400 0180" 376 // nested CTA_TUPLE_IP has no nla_value. 377 + "1C000000 0001 0006 00000000 00000000 02 00 0000 0800 0180 0400 0180" 378 // nested CTA_IP_V4_SRC has no nla_value. 379 + "20000000 0001 0006 00000000 00000000 02 00 0000 0C00 0180 0800 0180 0400 0100" 380 // nested CTA_TUPLE_PROTO has no nla_value. 381 // <-- nlmsghr -->|<-nfgenmsg->|<-- CTA_TUPLE_ORIG 382 + "30000000 0001 0006 00000000 00000000 02 00 0000 1C00 0180 1400 0180 0800 0100" 383 // -->| 384 + "C0A8500C 0800 0200 8C700874 0400 0280"; 385 // CHECKSTYLE:ON IndentationCheck 386 public static final byte[] CT_MALFORMED_BYTES = 387 HexEncoding.decode(CT_MALFORMED_HEX.replaceAll(" ", "").toCharArray(), false); 388 389 @Test testParseMalformation()390 public void testParseMalformation() { 391 assumeTrue(USING_LE); 392 393 final ByteBuffer byteBuffer = ByteBuffer.wrap(CT_MALFORMED_BYTES); 394 byteBuffer.order(ByteOrder.nativeOrder()); 395 396 // Expect no crash while parsing the malformed message. 397 int messageCount = 0; 398 while (byteBuffer.remaining() > 0) { 399 final NetlinkMessage msg = NetlinkMessage.parse(byteBuffer, 400 OsConstants.NETLINK_NETFILTER); 401 messageCount++; 402 } 403 assertEquals(4, messageCount); 404 } 405 406 @Test testToString()407 public void testToString() { 408 assumeTrue(USING_LE); 409 410 final ByteBuffer byteBuffer = ByteBuffer.wrap(CT_V4NEW_TCP_BYTES); 411 byteBuffer.order(ByteOrder.nativeOrder()); 412 final NetlinkMessage msg = NetlinkMessage.parse(byteBuffer, OsConstants.NETLINK_NETFILTER); 413 assertNotNull(msg); 414 assertTrue(msg instanceof ConntrackMessage); 415 final ConntrackMessage conntrackMessage = (ConntrackMessage) msg; 416 417 // Bug: "nlmsg_flags{1536(NLM_F_MATCH))" is not correct because StructNlMsgHdr 418 // #stringForNlMsgFlags can't convert all flags (ex: NLM_F_CREATE) and can't distinguish 419 // the flags which have the same value (ex: NLM_F_MATCH <0x200> and NLM_F_EXCL <0x200>). 420 // The flags output string should be "NLM_F_CREATE|NLM_F_EXCL" in this case. 421 // TODO: correct the flag converted string once #stringForNlMsgFlags does. 422 final String expected = "" 423 + "ConntrackMessage{" 424 + "nlmsghdr{StructNlMsgHdr{ nlmsg_len{140}, nlmsg_type{256(IPCTNL_MSG_CT_NEW)}, " 425 + "nlmsg_flags{1536(NLM_F_MATCH))}, nlmsg_seq{0}, nlmsg_pid{0} }}, " 426 + "nfgenmsg{NfGenMsg{ nfgen_family{AF_INET}, version{0}, res_id{4660} }}, " 427 + "tuple_orig{Tuple{IPPROTO_TCP: 192.168.80.12:62449 -> 140.112.8.116:443}}, " 428 + "tuple_reply{Tuple{IPPROTO_TCP: 140.112.8.116:443 -> 100.81.179.1:62449}}, " 429 + "status{408(IPS_CONFIRMED|IPS_SRC_NAT|IPS_SRC_NAT_DONE|IPS_DST_NAT_DONE)}, " 430 + "timeout_sec{120}}"; 431 assertEquals(expected, conntrackMessage.toString()); 432 } 433 } 434