summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChiachang Wang <chiachangwang@google.com>2020-02-12 09:28:01 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2020-02-12 09:28:01 +0000
commit67ab467166a0343a6dc27a7c9adb04d877d9f40b (patch)
tree2e9de24715ff8e404e70442209e57f8d5148cbef
parent1852ead9d9039afdd39036d93fed0c5a30ced3dc (diff)
parent85b49af029494009da6574cfa275758585da5301 (diff)
Merge "update structure of TcpInfo"
-rw-r--r--src/com/android/networkstack/netlink/TcpInfo.java132
-rw-r--r--src/com/android/networkstack/netlink/TcpSocketTracker.java16
-rw-r--r--tests/unit/src/com/android/networkstack/netlink/TcpInfoTest.java67
-rw-r--r--tests/unit/src/com/android/networkstack/netlink/TcpSocketTrackerTest.java51
4 files changed, 81 insertions, 185 deletions
diff --git a/src/com/android/networkstack/netlink/TcpInfo.java b/src/com/android/networkstack/netlink/TcpInfo.java
index e6036b5..31a408f 100644
--- a/src/com/android/networkstack/netlink/TcpInfo.java
+++ b/src/com/android/networkstack/netlink/TcpInfo.java
@@ -22,11 +22,9 @@ import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
+import java.nio.BufferOverflowException;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
-import java.util.Collections;
-import java.util.LinkedHashMap;
-import java.util.Map;
import java.util.Objects;
/**
@@ -91,27 +89,39 @@ public class TcpInfo {
}
private static final String TAG = "TcpInfo";
- private final Map<Field, Number> mFieldsValues;
+ @VisibleForTesting
+ static final int LOST_OFFSET = getFieldOffset(Field.LOST);
+ @VisibleForTesting
+ static final int RETRANSMITS_OFFSET = getFieldOffset(Field.RETRANSMITS);
+ @VisibleForTesting
+ static final int SEGS_IN_OFFSET = getFieldOffset(Field.SEGS_IN);
+ @VisibleForTesting
+ static final int SEGS_OUT_OFFSET = getFieldOffset(Field.SEGS_OUT);
+ final int mSegsIn;
+ final int mSegsOut;
+ final int mLost;
+ final int mRetransmits;
+
+ private static int getFieldOffset(@NonNull final Field needle) {
+ int offset = 0;
+ for (final Field field : Field.values()) {
+ if (field == needle) return offset;
+ offset += field.size;
+ }
+ throw new IllegalArgumentException("Unknown field");
+ }
private TcpInfo(@NonNull ByteBuffer bytes, int infolen) {
- final int start = bytes.position();
- final LinkedHashMap<Field, Number> fields = new LinkedHashMap<>();
- for (final Field field : Field.values()) {
- switch (field.size) {
- case Byte.BYTES:
- fields.put(field, getByte(bytes, start, infolen));
- break;
- case Integer.BYTES:
- fields.put(field, getInt(bytes, start, infolen));
- break;
- case Long.BYTES:
- fields.put(field, getLong(bytes, start, infolen));
- break;
- default:
- Log.e(TAG, "Unexpected size:" + field.size);
- }
+ // SEGS_IN is the last required field in the buffer, so if the buffer is long enough for
+ // SEGS_IN it's long enough for everything
+ if (SEGS_IN_OFFSET + Field.SEGS_IN.size > infolen) {
+ throw new IllegalArgumentException("Length " + infolen + " is less than required.");
}
- mFieldsValues = Collections.unmodifiableMap(fields);
+ final int start = bytes.position();
+ mSegsIn = bytes.getInt(start + SEGS_IN_OFFSET);
+ mSegsOut = bytes.getInt(start + SEGS_OUT_OFFSET);
+ mLost = bytes.getInt(start + LOST_OFFSET);
+ mRetransmits = bytes.get(start + RETRANSMITS_OFFSET);
// tcp_info structure grows over time as new fields are added. Jump to the end of the
// structure, as unknown fields might remain at the end of the structure if the tcp_info
// struct was expanded.
@@ -119,12 +129,11 @@ public class TcpInfo {
}
@VisibleForTesting
- TcpInfo(@NonNull Map<Field, Number> info) {
- final LinkedHashMap<Field, Number> fields = new LinkedHashMap<>();
- for (final Field field : Field.values()) {
- fields.put(field, info.get(field));
- }
- mFieldsValues = Collections.unmodifiableMap(fields);
+ TcpInfo(int retransmits, int lost, int segsOut, int segsIn) {
+ mRetransmits = retransmits;
+ mLost = lost;
+ mSegsOut = segsOut;
+ mSegsIn = segsIn;
}
/** Parse a TcpInfo from a giving ByteBuffer with a specific length. */
@@ -132,53 +141,13 @@ public class TcpInfo {
public static TcpInfo parse(@NonNull ByteBuffer bytes, int infolen) {
try {
return new TcpInfo(bytes, infolen);
- } catch (BufferUnderflowException | IllegalArgumentException e) {
+ } catch (BufferUnderflowException | BufferOverflowException | IllegalArgumentException
+ | IndexOutOfBoundsException e) {
Log.e(TAG, "parsing error.", e);
return null;
}
}
- /**
- * Helper function for handling different struct tcp_info versions in the kernel.
- */
- private static boolean isValidTargetPosition(int start, int len, int pos, int targetBytes)
- throws IllegalArgumentException {
- // Equivalent to new Range(start, start + len).contains(new Range(pos, pos + targetBytes))
- if (len < 0 || targetBytes < 0) throw new IllegalArgumentException();
- // Check that start < pos < start + len
- if (pos < start || pos > start + len) return false;
- // Pos is inside the range and targetBytes is positive. Offset is valid if end of 2nd range
- // is below end of 1st range.
- return pos + targetBytes <= start + len;
- }
-
- /** Get value for specific key. */
- @Nullable
- public Number getValue(@NonNull Field key) {
- return mFieldsValues.get(key);
- }
-
- @Nullable
- private static Byte getByte(@NonNull ByteBuffer buffer, int start, int len) {
- if (!isValidTargetPosition(start, len, buffer.position(), Byte.BYTES)) return null;
-
- return buffer.get();
- }
-
- @Nullable
- private static Integer getInt(@NonNull ByteBuffer buffer, int start, int len) {
- if (!isValidTargetPosition(start, len, buffer.position(), Integer.BYTES)) return null;
-
- return buffer.getInt();
- }
-
- @Nullable
- private static Long getLong(@NonNull ByteBuffer buffer, int start, int len) {
- if (!isValidTargetPosition(start, len, buffer.position(), Long.BYTES)) return null;
-
- return buffer.getLong();
- }
-
private static String decodeWscale(byte num) {
return String.valueOf((num >> 4) & 0x0f) + ":" + String.valueOf(num & 0x0f);
}
@@ -210,33 +179,18 @@ public class TcpInfo {
if (!(obj instanceof TcpInfo)) return false;
TcpInfo other = (TcpInfo) obj;
- for (final Field key : mFieldsValues.keySet()) {
- if (!Objects.equals(mFieldsValues.get(key), other.mFieldsValues.get(key))) {
- return false;
- }
- }
- return true;
+ return mSegsIn == other.mSegsIn && mSegsOut == other.mSegsOut
+ && mRetransmits == other.mRetransmits && mLost == other.mLost;
}
@Override
public int hashCode() {
- return Objects.hash(mFieldsValues.values().toArray());
+ return Objects.hash(mLost, mRetransmits, mSegsIn, mSegsOut);
}
@Override
public String toString() {
- String str = "TcpInfo{ ";
- for (final Field key : mFieldsValues.keySet()) {
- str += key.name().toLowerCase() + "=";
- if (key == Field.STATE) {
- str += getTcpStateName(mFieldsValues.get(key).intValue()) + " ";
- } else if (key == Field.WSCALE) {
- str += decodeWscale(mFieldsValues.get(key).byteValue()) + " ";
- } else {
- str += mFieldsValues.get(key) + " ";
- }
- }
- str += "}";
- return str;
+ return "TcpInfo{lost=" + mLost + ", retransmit=" + mRetransmits + ", received=" + mSegsIn
+ + ", sent=" + mSegsOut + "}";
}
}
diff --git a/src/com/android/networkstack/netlink/TcpSocketTracker.java b/src/com/android/networkstack/netlink/TcpSocketTracker.java
index 78813bd..f660f81 100644
--- a/src/com/android/networkstack/netlink/TcpSocketTracker.java
+++ b/src/com/android/networkstack/netlink/TcpSocketTracker.java
@@ -340,16 +340,16 @@ public class TcpSocketTracker {
return null;
}
- stat.sentCount = current.tcpInfo.getValue(TcpInfo.Field.SEGS_OUT).intValue();
- stat.receivedCount = current.tcpInfo.getValue(TcpInfo.Field.SEGS_IN).intValue();
- stat.lostCount = current.tcpInfo.getValue(TcpInfo.Field.LOST).intValue();
- stat.retransmitCount = current.tcpInfo.getValue(TcpInfo.Field.RETRANSMITS).intValue();
+ stat.sentCount = current.tcpInfo.mSegsOut;
+ stat.receivedCount = current.tcpInfo.mSegsIn;
+ stat.lostCount = current.tcpInfo.mLost;
+ stat.retransmitCount = current.tcpInfo.mRetransmits;
if (previous != null && previous.tcpInfo != null) {
- stat.sentCount -= previous.tcpInfo.getValue(TcpInfo.Field.SEGS_OUT).intValue();
- stat.receivedCount -= previous.tcpInfo.getValue(TcpInfo.Field.SEGS_IN).intValue();
- stat.lostCount -= previous.tcpInfo.getValue(TcpInfo.Field.LOST).intValue();
- stat.retransmitCount -= previous.tcpInfo.getValue(TcpInfo.Field.RETRANSMITS).intValue();
+ stat.sentCount -= previous.tcpInfo.mSegsOut;
+ stat.receivedCount -= previous.tcpInfo.mSegsIn;
+ stat.lostCount -= previous.tcpInfo.mLost;
+ stat.retransmitCount -= previous.tcpInfo.mRetransmits;
}
return stat;
diff --git a/tests/unit/src/com/android/networkstack/netlink/TcpInfoTest.java b/tests/unit/src/com/android/networkstack/netlink/TcpInfoTest.java
index f65de9c..ddab8c7 100644
--- a/tests/unit/src/com/android/networkstack/netlink/TcpInfoTest.java
+++ b/tests/unit/src/com/android/networkstack/netlink/TcpInfoTest.java
@@ -89,6 +89,8 @@ public class TcpInfoTest {
"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
@@ -100,40 +102,48 @@ public class TcpInfoTest {
@Test
public void testParseTcpInfo() {
final ByteBuffer buffer = ByteBuffer.wrap(TCP_INFO_BYTES);
- final Map<TcpInfo.Field, Number> expected = makeTestTcpInfoHash();
+ // 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);
+ }
- assertEquals(parsedInfo, new TcpInfo(expected));
+ @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 Map<TcpInfo.Field, Number> expected = makeTestTcpInfoHash();
final TcpInfo parsedInfo =
TcpInfo.parse(buffer, TCP_INFO_LENGTH_V1 + EXPANDED_TCP_INFO_LENGTH);
- assertEquals(parsedInfo, new TcpInfo(expected));
+ 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, new TcpInfo(expected));
+ assertEquals(parsedInfoShorterLen, TEST_TCPINFO);
assertEquals(TCP_INFO_LENGTH_V1, buffer.position());
}
@Test
- public void testValidOffset() {
- final ByteBuffer buffer = ByteBuffer.wrap(TCP_INFO_BYTES);
-
- final Map<TcpInfo.Field, Number> expected = makeShortTestTcpInfoHash();
- final TcpInfo parsedInfo = TcpInfo.parse(buffer, SHORT_TEST_TCP_INFO);
-
- assertEquals(parsedInfo, new TcpInfo(expected));
- }
-
- @Test
public void testTcpStateName() {
assertEquals(TcpInfo.getTcpStateName(4), TCP_FIN_WAIT1);
assertEquals(TcpInfo.getTcpStateName(1), TCP_ESTABLISHED);
@@ -156,39 +166,14 @@ public class TcpInfoTest {
@Test
public void testMalformedTcpInfo() {
final ByteBuffer buffer = ByteBuffer.wrap(MALFORMED_TCP_INFO_BYTES);
- final Map<TcpInfo.Field, Number> expected = makeShortTestTcpInfoHash();
TcpInfo parsedInfo = TcpInfo.parse(buffer, SHORT_TEST_TCP_INFO);
- assertEquals(parsedInfo, new TcpInfo(expected));
+ assertEquals(parsedInfo, null);
parsedInfo = TcpInfo.parse(buffer, TCP_INFO_LENGTH_V1);
assertEquals(parsedInfo, null);
}
- @Test
- public void testGetValue() {
- ByteBuffer buffer = ByteBuffer.wrap(TCP_INFO_BYTES);
-
- final Map<TcpInfo.Field, Number> expected = makeShortTestTcpInfoHash();
- expected.put(TcpInfo.Field.MAX_PACING_RATE, 10_000L);
- expected.put(TcpInfo.Field.FACKETS, 10);
-
- final TcpInfo expectedInfo = new TcpInfo(expected);
- assertEquals((byte) 0x01, expectedInfo.getValue(TcpInfo.Field.STATE));
- assertEquals((byte) 0x00, expectedInfo.getValue(TcpInfo.Field.CASTATE));
- assertEquals((byte) 0x00, expectedInfo.getValue(TcpInfo.Field.RETRANSMITS));
- assertEquals((byte) 0x00, expectedInfo.getValue(TcpInfo.Field.PROBES));
- assertEquals((byte) 0x00, expectedInfo.getValue(TcpInfo.Field.BACKOFF));
- assertEquals((byte) 0x07, expectedInfo.getValue(TcpInfo.Field.OPTIONS));
- assertEquals((byte) 0x88, expectedInfo.getValue(TcpInfo.Field.WSCALE));
- assertEquals((byte) 0x00, expectedInfo.getValue(TcpInfo.Field.DELIVERY_RATE_APP_LIMITED));
-
- assertEquals(10_000L, expectedInfo.getValue(TcpInfo.Field.MAX_PACING_RATE));
- assertEquals(10, expectedInfo.getValue(TcpInfo.Field.FACKETS));
- assertEquals(null, expectedInfo.getValue(TcpInfo.Field.RTT));
-
- }
-
// Make a TcpInfo contains only first 8 bytes.
private Map<TcpInfo.Field, Number> makeShortTestTcpInfoHash() {
final Map<TcpInfo.Field, Number> info = new LinkedHashMap<>();
diff --git a/tests/unit/src/com/android/networkstack/netlink/TcpSocketTrackerTest.java b/tests/unit/src/com/android/networkstack/netlink/TcpSocketTrackerTest.java
index a21e7cf..6a09f12 100644
--- a/tests/unit/src/com/android/networkstack/netlink/TcpSocketTrackerTest.java
+++ b/tests/unit/src/com/android/networkstack/netlink/TcpSocketTrackerTest.java
@@ -61,7 +61,6 @@ import org.mockito.quality.Strictness;
import java.io.FileDescriptor;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
-import java.util.HashMap;
// TODO: Add more tests for missing coverage.
@RunWith(AndroidJUnit4.class)
@@ -174,6 +173,8 @@ public class TcpSocketTrackerTest {
"0000000000000000"; // deliverRate = 0
private static final byte[] SOCK_DIAG_TCP_INET_BYTES =
HexEncoding.decode(SOCK_DIAG_TCP_INET_HEX.toCharArray(), false);
+ private static final TcpInfo TEST_TCPINFO =
+ new TcpInfo(5 /* retransmits */, 0 /* lost */, 10 /* segsOut */, 0 /* segsIn */);
private static final String TEST_RESPONSE_HEX = SOCK_DIAG_TCP_INET_HEX
// struct nlmsghdr
@@ -253,52 +254,8 @@ public class TcpSocketTrackerTest {
buffer.position(SOCKDIAG_MSG_HEADER_SIZE);
final TcpSocketTracker.SocketInfo parsed =
tst.parseSockInfo(buffer, AF_INET, 276, 100L);
- final HashMap<TcpInfo.Field, Number> expected = new HashMap<>();
- expected.put(TcpInfo.Field.STATE, (byte) 0x01);
- expected.put(TcpInfo.Field.CASTATE, (byte) 0x00);
- expected.put(TcpInfo.Field.RETRANSMITS, (byte) 0x05);
- expected.put(TcpInfo.Field.PROBES, (byte) 0x00);
- expected.put(TcpInfo.Field.BACKOFF, (byte) 0x00);
- expected.put(TcpInfo.Field.OPTIONS, (byte) 0x07);
- expected.put(TcpInfo.Field.WSCALE, (byte) 0x88);
- expected.put(TcpInfo.Field.DELIVERY_RATE_APP_LIMITED, (byte) 0x00);
- expected.put(TcpInfo.Field.RTO, 1806666);
- expected.put(TcpInfo.Field.ATO, 0);
- expected.put(TcpInfo.Field.SND_MSS, 1326);
- expected.put(TcpInfo.Field.RCV_MSS, 536);
- expected.put(TcpInfo.Field.UNACKED, 0);
- expected.put(TcpInfo.Field.SACKED, 0);
- expected.put(TcpInfo.Field.LOST, 0);
- expected.put(TcpInfo.Field.RETRANS, 0);
- expected.put(TcpInfo.Field.FACKETS, 0);
- expected.put(TcpInfo.Field.LAST_DATA_SENT, 187);
- expected.put(TcpInfo.Field.LAST_ACK_SENT, 0);
- expected.put(TcpInfo.Field.LAST_DATA_RECV, 187);
- expected.put(TcpInfo.Field.LAST_ACK_RECV, 187);
- expected.put(TcpInfo.Field.PMTU, 1500);
- expected.put(TcpInfo.Field.RCV_SSTHRESH, 87600);
- expected.put(TcpInfo.Field.RTT, 601150);
- expected.put(TcpInfo.Field.RTTVAR, 300575);
- expected.put(TcpInfo.Field.SND_SSTHRESH, 1400);
- expected.put(TcpInfo.Field.SND_CWND, 10);
- expected.put(TcpInfo.Field.ADVMSS, 1448);
- expected.put(TcpInfo.Field.REORDERING, 3);
- expected.put(TcpInfo.Field.RCV_RTT, 0);
- expected.put(TcpInfo.Field.RCV_SPACE, 87600);
- expected.put(TcpInfo.Field.TOTAL_RETRANS, 0);
- expected.put(TcpInfo.Field.PACING_RATE, 44115L);
- expected.put(TcpInfo.Field.MAX_PACING_RATE, -1L);
- expected.put(TcpInfo.Field.BYTES_ACKED, 1L);
- expected.put(TcpInfo.Field.BYTES_RECEIVED, 0L);
- expected.put(TcpInfo.Field.SEGS_OUT, 10);
- expected.put(TcpInfo.Field.SEGS_IN, 0);
- expected.put(TcpInfo.Field.NOTSENT_BYTES, 0);
- expected.put(TcpInfo.Field.MIN_RTT, 601150);
- expected.put(TcpInfo.Field.DATA_SEGS_IN, 0);
- expected.put(TcpInfo.Field.DATA_SEGS_OUT, 0);
- expected.put(TcpInfo.Field.DELIVERY_RATE, 0L);
-
- assertEquals(parsed.tcpInfo, new TcpInfo(expected));
+
+ assertEquals(parsed.tcpInfo, TEST_TCPINFO);
assertEquals(parsed.fwmark, 789125);
assertEquals(parsed.updateTime, 100);
assertEquals(parsed.ipFamily, AF_INET);