blob: ddab8c7e2c30dd3a6f1d708c149516023ab93991 [file] [log] [blame]
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.networkstack.netlink;
import static org.junit.Assert.assertEquals;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import libcore.util.HexEncoding;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.nio.ByteBuffer;
import java.util.LinkedHashMap;
import java.util.Map;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class TcpInfoTest {
private static final int TCP_INFO_LENGTH_V1 = 192;
private static final int SHORT_TEST_TCP_INFO = 8;
private static final String TCP_ESTABLISHED = "TCP_ESTABLISHED";
private static final String TCP_FIN_WAIT1 = "TCP_FIN_WAIT1";
private static final String TCP_SYN_SENT = "TCP_SYN_SENT";
private static final String UNKNOWN_20 = "UNKNOWN:20";
// Refer to rfc793 for the value definition.
private static final String TCP_INFO_HEX =
"01" + // state = TCP_ESTABLISHED
"00" + // ca_state = TCP_CA_OPEN
"00" + // retransmits = 0
"00" + // probes = 0
"00" + // backoff = 0
"07" + // option = TCPI_OPT_WSCALE|TCPI_OPT_SACK|TCPI_OPT_TIMESTAMPS
"88" + // wscale = 8
"00" + // delivery_rate_app_limited = 0
"001B914A" + // rto = 1806666
"00000000" + // ato = 0
"0000052E" + // sndMss = 1326
"00000218" + // rcvMss = 536
"00000000" + // unsacked = 0
"00000000" + // acked = 0
"00000000" + // lost = 0
"00000000" + // retrans = 0
"00000000" + // fackets = 0
"000000BB" + // lastDataSent = 187
"00000000" + // lastAckSent = 0
"000000BB" + // lastDataRecv = 187
"000000BB" + // lastDataAckRecv = 187
"000005DC" + // pmtu = 1500
"00015630" + // rcvSsthresh = 87600
"00092C3E" + // rttt = 601150
"0004961F" + // rttvar = 300575
"00000578" + // sndSsthresh = 1400
"0000000A" + // sndCwnd = 10
"000005A8" + // advmss = 1448
"00000003" + // reordering = 3
"00000000" + // rcvrtt = 0
"00015630" + // rcvspace = 87600
"00000000" + // totalRetrans = 0
"000000000000AC53" + // pacingRate = 44115
"FFFFFFFFFFFFFFFF" + // maxPacingRate = 18446744073709551615
"0000000000000001" + // bytesAcked = 1
"0000000000000000" + // bytesReceived = 0
"00000002" + // SegsOut = 2
"00000001" + // SegsIn = 1
"00000000" + // NotSentBytes = 0
"00092C3E" + // minRtt = 601150
"00000000" + // DataSegsIn = 0
"00000000" + // DataSegsOut = 0
"0000000000000000" + // deliverRate = 0
"0000000000000000" + // busyTime = 0
"0000000000000000" + // RwndLimited = 0
"0000000000000000"; // sndBufLimited = 0
private static final byte[] TCP_INFO_BYTES =
HexEncoding.decode(TCP_INFO_HEX.toCharArray(), false);
private static final TcpInfo TEST_TCPINFO =
new TcpInfo(0 /* retransmits */, 0 /* lost */, 2 /* segsOut */, 1 /* segsIn */);
private static final String EXPANDED_TCP_INFO_HEX = TCP_INFO_HEX
+ "00000000" // tcpi_delivered
+ "00000000"; // tcpi_delivered_ce
private static final byte[] EXPANDED_TCP_INFO_BYTES =
HexEncoding.decode(EXPANDED_TCP_INFO_HEX.toCharArray(), false);
private static final int EXPANDED_TCP_INFO_LENGTH =
EXPANDED_TCP_INFO_BYTES.length - TCP_INFO_BYTES.length;
@Test
public void testParseTcpInfo() {
final ByteBuffer buffer = ByteBuffer.wrap(TCP_INFO_BYTES);
// Length is less than required
final TcpInfo nullInfo = TcpInfo.parse(buffer, SHORT_TEST_TCP_INFO);
assertEquals(nullInfo, null);
final TcpInfo parsedInfo = TcpInfo.parse(buffer, TCP_INFO_LENGTH_V1);
assertEquals(parsedInfo, TEST_TCPINFO);
// Make a data that TcpInfo is not started from the begining of the buffer.
final ByteBuffer bufferWithHeader =
ByteBuffer.allocate(EXPANDED_TCP_INFO_BYTES.length + TCP_INFO_BYTES.length);
bufferWithHeader.put(EXPANDED_TCP_INFO_BYTES);
bufferWithHeader.put(TCP_INFO_BYTES);
final TcpInfo infoWithHeader = TcpInfo.parse(buffer, TCP_INFO_LENGTH_V1);
bufferWithHeader.position(EXPANDED_TCP_INFO_BYTES.length);
assertEquals(parsedInfo, TEST_TCPINFO);
}
@Test
public void testFieldOffset() {
assertEquals(TcpInfo.RETRANSMITS_OFFSET, 2);
assertEquals(TcpInfo.LOST_OFFSET, 32);
assertEquals(TcpInfo.SEGS_OUT_OFFSET, 136);
assertEquals(TcpInfo.SEGS_IN_OFFSET, 140);
}
@Test
public void testParseTcpInfoExpanded() {
final ByteBuffer buffer = ByteBuffer.wrap(EXPANDED_TCP_INFO_BYTES);
final TcpInfo parsedInfo =
TcpInfo.parse(buffer, TCP_INFO_LENGTH_V1 + EXPANDED_TCP_INFO_LENGTH);
assertEquals(parsedInfo, TEST_TCPINFO);
assertEquals(buffer.limit(), buffer.position());
// reset the index.
buffer.position(0);
final TcpInfo parsedInfoShorterLen = TcpInfo.parse(buffer, TCP_INFO_LENGTH_V1);
assertEquals(parsedInfoShorterLen, TEST_TCPINFO);
assertEquals(TCP_INFO_LENGTH_V1, buffer.position());
}
@Test
public void testTcpStateName() {
assertEquals(TcpInfo.getTcpStateName(4), TCP_FIN_WAIT1);
assertEquals(TcpInfo.getTcpStateName(1), TCP_ESTABLISHED);
assertEquals(TcpInfo.getTcpStateName(2), TCP_SYN_SENT);
assertEquals(TcpInfo.getTcpStateName(20), UNKNOWN_20);
}
private static final String MALFORMED_TCP_INFO_HEX =
"01" + // state = TCP_ESTABLISHED
"00" + // ca_state = TCP_CA_OPEN
"00" + // retransmits = 0
"00" + // probes = 0
"00" + // backoff = 0
"07" + // option = TCPI_OPT_WSCALE|TCPI_OPT_SACK|TCPI_OPT_TIMESTAMPS
"88" + // wscale = 8
"00" + // delivery_rate_app_limited = 0
"001B"; // Incomplete bytes, expect to be an int.
private static final byte[] MALFORMED_TCP_INFO_BYTES =
HexEncoding.decode(MALFORMED_TCP_INFO_HEX.toCharArray(), false);
@Test
public void testMalformedTcpInfo() {
final ByteBuffer buffer = ByteBuffer.wrap(MALFORMED_TCP_INFO_BYTES);
TcpInfo parsedInfo = TcpInfo.parse(buffer, SHORT_TEST_TCP_INFO);
assertEquals(parsedInfo, null);
parsedInfo = TcpInfo.parse(buffer, TCP_INFO_LENGTH_V1);
assertEquals(parsedInfo, null);
}
// Make a TcpInfo contains only first 8 bytes.
private Map<TcpInfo.Field, Number> makeShortTestTcpInfoHash() {
final Map<TcpInfo.Field, Number> info = new LinkedHashMap<>();
info.put(TcpInfo.Field.STATE, (byte) 0x01);
info.put(TcpInfo.Field.CASTATE, (byte) 0x00);
info.put(TcpInfo.Field.RETRANSMITS, (byte) 0x00);
info.put(TcpInfo.Field.PROBES, (byte) 0x00);
info.put(TcpInfo.Field.BACKOFF, (byte) 0x00);
info.put(TcpInfo.Field.OPTIONS, (byte) 0x07);
info.put(TcpInfo.Field.WSCALE, (byte) 0x88);
info.put(TcpInfo.Field.DELIVERY_RATE_APP_LIMITED, (byte) 0x00);
return info;
}
private Map<TcpInfo.Field, Number> makeTestTcpInfoHash() {
final Map<TcpInfo.Field, Number> info = makeShortTestTcpInfoHash();
info.put(TcpInfo.Field.RTO, 1806666);
info.put(TcpInfo.Field.ATO, 0);
info.put(TcpInfo.Field.SND_MSS, 1326);
info.put(TcpInfo.Field.RCV_MSS, 536);
info.put(TcpInfo.Field.UNACKED, 0);
info.put(TcpInfo.Field.SACKED, 0);
info.put(TcpInfo.Field.LOST, 0);
info.put(TcpInfo.Field.RETRANS, 0);
info.put(TcpInfo.Field.FACKETS, 0);
info.put(TcpInfo.Field.LAST_DATA_SENT, 187);
info.put(TcpInfo.Field.LAST_ACK_SENT, 0);
info.put(TcpInfo.Field.LAST_DATA_RECV, 187);
info.put(TcpInfo.Field.LAST_ACK_RECV, 187);
info.put(TcpInfo.Field.PMTU, 1500);
info.put(TcpInfo.Field.RCV_SSTHRESH, 87600);
info.put(TcpInfo.Field.RTT, 601150);
info.put(TcpInfo.Field.RTTVAR, 300575);
info.put(TcpInfo.Field.SND_SSTHRESH, 1400);
info.put(TcpInfo.Field.SND_CWND, 10);
info.put(TcpInfo.Field.ADVMSS, 1448);
info.put(TcpInfo.Field.REORDERING, 3);
info.put(TcpInfo.Field.RCV_RTT, 0);
info.put(TcpInfo.Field.RCV_SPACE, 87600);
info.put(TcpInfo.Field.TOTAL_RETRANS, 0);
info.put(TcpInfo.Field.PACING_RATE, 44115L);
info.put(TcpInfo.Field.MAX_PACING_RATE, -1L);
info.put(TcpInfo.Field.BYTES_ACKED, 1L);
info.put(TcpInfo.Field.BYTES_RECEIVED, 0L);
info.put(TcpInfo.Field.SEGS_OUT, 2);
info.put(TcpInfo.Field.SEGS_IN, 1);
info.put(TcpInfo.Field.NOTSENT_BYTES, 0);
info.put(TcpInfo.Field.MIN_RTT, 601150);
info.put(TcpInfo.Field.DATA_SEGS_IN, 0);
info.put(TcpInfo.Field.DATA_SEGS_OUT, 0);
info.put(TcpInfo.Field.DELIVERY_RATE, 0L);
info.put(TcpInfo.Field.BUSY_TIME, 0L);
info.put(TcpInfo.Field.RWND_LIMITED, 0L);
info.put(TcpInfo.Field.SNDBUF_LIMITED, 0L);
return info;
}
}