[NFCT.NS.8] Parse conntrack attribute CTA_STATUS and CTA_TIMEOUT am: 9140fe3b08
Original change: https://android-review.googlesource.com/c/platform/packages/modules/NetworkStack/+/1488577
MUST ONLY BE SUBMITTED BY AUTOMERGER
Change-Id: I495768c34767cd180dc39da1df74273234bc4a29
diff --git a/common/netlinkclient/src/android/net/netlink/ConntrackMessage.java b/common/netlinkclient/src/android/net/netlink/ConntrackMessage.java
index 4326f7a..30a1165 100644
--- a/common/netlinkclient/src/android/net/netlink/ConntrackMessage.java
+++ b/common/netlinkclient/src/android/net/netlink/ConntrackMessage.java
@@ -22,6 +22,7 @@
import static java.nio.ByteOrder.BIG_ENDIAN;
+import android.annotation.NonNull;
import android.system.OsConstants;
import java.net.Inet4Address;
@@ -42,6 +43,7 @@
// enum ctattr_type
public static final short CTA_TUPLE_ORIG = 1;
public static final short CTA_TUPLE_REPLY = 2;
+ public static final short CTA_STATUS = 3;
public static final short CTA_TIMEOUT = 7;
// enum ctattr_tuple
@@ -105,35 +107,78 @@
// Just build the netlink header and netfilter header for now and pretend the whole message
// was consumed.
// TODO: Parse the conntrack attributes.
- final ConntrackMessage conntrackMsg = new ConntrackMessage(header);
-
- conntrackMsg.mNfGenMsg = StructNfGenMsg.parse(byteBuffer);
- if (conntrackMsg.mNfGenMsg == null) {
+ final StructNfGenMsg nfGenMsg = StructNfGenMsg.parse(byteBuffer);
+ if (nfGenMsg == null) {
return null;
}
- byteBuffer.position(byteBuffer.limit());
- return conntrackMsg;
+ final int baseOffset = byteBuffer.position();
+ StructNlAttr nlAttr = StructNlAttr.findNextAttrOfType(CTA_STATUS, byteBuffer);
+ int status = 0;
+ if (nlAttr != null) {
+ status = nlAttr.getValueAsBe32(0);
+ }
+
+ byteBuffer.position(baseOffset);
+ nlAttr = StructNlAttr.findNextAttrOfType(CTA_TIMEOUT, byteBuffer);
+ int timeoutSec = 0;
+ if (nlAttr != null) {
+ timeoutSec = nlAttr.getValueAsBe32(0);
+ }
+
+ // Advance to the end of the message.
+ byteBuffer.position(baseOffset);
+ final int kMinConsumed = StructNlMsgHdr.STRUCT_SIZE + StructNfGenMsg.STRUCT_SIZE;
+ final int kAdditionalSpace = NetlinkConstants.alignedLengthOf(
+ header.nlmsg_len - kMinConsumed);
+ if (byteBuffer.remaining() < kAdditionalSpace) {
+ return null;
+ }
+ byteBuffer.position(baseOffset + kAdditionalSpace);
+
+ return new ConntrackMessage(header, nfGenMsg, status, timeoutSec);
}
- protected StructNfGenMsg mNfGenMsg;
+ /**
+ * Netfilter header.
+ */
+ public final StructNfGenMsg nfGenMsg;
+ /**
+ * Connection status. A bitmask of ip_conntrack_status enum flags.
+ *
+ * The status is determined by the parsed attribute value CTA_STATUS, or 0 if the status could
+ * not be parsed successfully (for example, if it was truncated or absent). For the message
+ * from kernel, the valid status is non-zero. For the message from user space, the status may
+ * be 0 (absent).
+ */
+ public final int status;
+ /**
+ * Conntrack timeout.
+ *
+ * The timeout is determined by the parsed attribute value CTA_TIMEOUT, or 0 if the timeout
+ * could not be parsed successfully (for example, if it was truncated or absent). For
+ * IPCTNL_MSG_CT_NEW event, the valid timeout is non-zero. For IPCTNL_MSG_CT_DELETE event, the
+ * timeout is 0 (absent).
+ */
+ public final int timeoutSec;
private ConntrackMessage() {
super(new StructNlMsgHdr());
- mNfGenMsg = new StructNfGenMsg((byte) OsConstants.AF_INET);
+ nfGenMsg = new StructNfGenMsg((byte) OsConstants.AF_INET);
+ status = 0;
+ timeoutSec = 0;
}
- private ConntrackMessage(StructNlMsgHdr header) {
+ private ConntrackMessage(@NonNull StructNlMsgHdr header, @NonNull StructNfGenMsg nfGenMsg,
+ int status, int timeoutSec) {
super(header);
- mNfGenMsg = null;
- }
-
- public StructNfGenMsg getNfHeader() {
- return mNfGenMsg;
+ this.nfGenMsg = nfGenMsg;
+ this.status = status;
+ this.timeoutSec = timeoutSec;
}
public void pack(ByteBuffer byteBuffer) {
mHeader.pack(byteBuffer);
- mNfGenMsg.pack(byteBuffer);
+ nfGenMsg.pack(byteBuffer);
}
}
diff --git a/common/netlinkclient/src/android/net/netlink/RtNetlinkNeighborMessage.java b/common/netlinkclient/src/android/net/netlink/RtNetlinkNeighborMessage.java
index 6ae22c0..099ff07 100644
--- a/common/netlinkclient/src/android/net/netlink/RtNetlinkNeighborMessage.java
+++ b/common/netlinkclient/src/android/net/netlink/RtNetlinkNeighborMessage.java
@@ -48,23 +48,6 @@
public static final short NDA_IFINDEX = 8;
public static final short NDA_MASTER = 9;
- private static StructNlAttr findNextAttrOfType(short attrType, ByteBuffer byteBuffer) {
- while (byteBuffer != null && byteBuffer.remaining() > 0) {
- final StructNlAttr nlAttr = StructNlAttr.peek(byteBuffer);
- if (nlAttr == null) {
- break;
- }
- if (nlAttr.nla_type == attrType) {
- return StructNlAttr.parse(byteBuffer);
- }
- if (byteBuffer.remaining() < nlAttr.getAlignedLength()) {
- break;
- }
- byteBuffer.position(byteBuffer.position() + nlAttr.getAlignedLength());
- }
- return null;
- }
-
public static RtNetlinkNeighborMessage parse(StructNlMsgHdr header, ByteBuffer byteBuffer) {
final RtNetlinkNeighborMessage neighMsg = new RtNetlinkNeighborMessage(header);
@@ -75,25 +58,25 @@
// Some of these are message-type dependent, and not always present.
final int baseOffset = byteBuffer.position();
- StructNlAttr nlAttr = findNextAttrOfType(NDA_DST, byteBuffer);
+ StructNlAttr nlAttr = StructNlAttr.findNextAttrOfType(NDA_DST, byteBuffer);
if (nlAttr != null) {
neighMsg.mDestination = nlAttr.getValueAsInetAddress();
}
byteBuffer.position(baseOffset);
- nlAttr = findNextAttrOfType(NDA_LLADDR, byteBuffer);
+ nlAttr = StructNlAttr.findNextAttrOfType(NDA_LLADDR, byteBuffer);
if (nlAttr != null) {
neighMsg.mLinkLayerAddr = nlAttr.nla_value;
}
byteBuffer.position(baseOffset);
- nlAttr = findNextAttrOfType(NDA_PROBES, byteBuffer);
+ nlAttr = StructNlAttr.findNextAttrOfType(NDA_PROBES, byteBuffer);
if (nlAttr != null) {
neighMsg.mNumProbes = nlAttr.getValueAsInt(0);
}
byteBuffer.position(baseOffset);
- nlAttr = findNextAttrOfType(NDA_CACHEINFO, byteBuffer);
+ nlAttr = StructNlAttr.findNextAttrOfType(NDA_CACHEINFO, byteBuffer);
if (nlAttr != null) {
neighMsg.mCacheInfo = StructNdaCacheInfo.parse(nlAttr.getValueAsByteBuffer());
}
diff --git a/common/netlinkclient/src/android/net/netlink/StructNfGenMsg.java b/common/netlinkclient/src/android/net/netlink/StructNfGenMsg.java
index e0f4333..fca78b8 100644
--- a/common/netlinkclient/src/android/net/netlink/StructNfGenMsg.java
+++ b/common/netlinkclient/src/android/net/netlink/StructNfGenMsg.java
@@ -79,8 +79,11 @@
public void pack(ByteBuffer byteBuffer) {
byteBuffer.put(nfgen_family);
byteBuffer.put(version);
- // TODO: probably need to handle the little endian case.
+
+ final ByteOrder originalOrder = byteBuffer.order();
+ byteBuffer.order(ByteOrder.BIG_ENDIAN);
byteBuffer.putShort(res_id);
+ byteBuffer.order(originalOrder);
}
private static boolean hasAvailableSpace(@NonNull ByteBuffer byteBuffer) {
diff --git a/common/netlinkclient/src/android/net/netlink/StructNlAttr.java b/common/netlinkclient/src/android/net/netlink/StructNlAttr.java
index 59458c1..a3a26b9 100644
--- a/common/netlinkclient/src/android/net/netlink/StructNlAttr.java
+++ b/common/netlinkclient/src/android/net/netlink/StructNlAttr.java
@@ -16,6 +16,8 @@
package android.net.netlink;
+import androidx.annotation.Nullable;
+
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
@@ -85,6 +87,33 @@
return struct;
}
+ /**
+ * Find next netlink attribute with a given type from {@link ByteBuffer}.
+ *
+ * @param attrType The given netlink attribute type is requested for.
+ * @param byteBuffer The buffer from which to find the netlink attribute.
+ * @return the found netlink attribute, or {@code null} if the netlink attribute could not be
+ * found or parsed successfully (for example, if it was truncated).
+ */
+ @Nullable
+ public static StructNlAttr findNextAttrOfType(short attrType,
+ @Nullable ByteBuffer byteBuffer) {
+ while (byteBuffer != null && byteBuffer.remaining() > 0) {
+ final StructNlAttr nlAttr = StructNlAttr.peek(byteBuffer);
+ if (nlAttr == null) {
+ break;
+ }
+ if (nlAttr.nla_type == attrType) {
+ return StructNlAttr.parse(byteBuffer);
+ }
+ if (byteBuffer.remaining() < nlAttr.getAlignedLength()) {
+ break;
+ }
+ byteBuffer.position(byteBuffer.position() + nlAttr.getAlignedLength());
+ }
+ return null;
+ }
+
public short nla_len = (short) NLA_HEADERLEN;
public short nla_type;
public byte[] nla_value;
diff --git a/tests/unit/src/android/net/netlink/ConntrackMessageTest.java b/tests/unit/src/android/net/netlink/ConntrackMessageTest.java
index 18a0c52..6af1046 100644
--- a/tests/unit/src/android/net/netlink/ConntrackMessageTest.java
+++ b/tests/unit/src/android/net/netlink/ConntrackMessageTest.java
@@ -82,6 +82,14 @@
public static final byte[] CT_V4UPDATE_TCP_BYTES =
HexEncoding.decode(CT_V4UPDATE_TCP_HEX.replaceAll(" ", "").toCharArray(), false);
+ private byte[] makeIPv4TimeoutUpdateRequestTcp() throws Exception {
+ return ConntrackMessage.newIPv4TimeoutUpdateRequest(
+ OsConstants.IPPROTO_TCP,
+ (Inet4Address) InetAddress.getByName("192.168.43.209"), 44333,
+ (Inet4Address) InetAddress.getByName("23.211.13.26"), 443,
+ 432000);
+ }
+
// Example 2: UDP (100.96.167.146, 37069) -> (216.58.197.10, 443)
public static final String CT_V4UPDATE_UDP_HEX =
// struct nlmsghdr
@@ -115,17 +123,27 @@
public static final byte[] CT_V4UPDATE_UDP_BYTES =
HexEncoding.decode(CT_V4UPDATE_UDP_HEX.replaceAll(" ", "").toCharArray(), false);
+ private byte[] makeIPv4TimeoutUpdateRequestUdp() throws Exception {
+ return ConntrackMessage.newIPv4TimeoutUpdateRequest(
+ OsConstants.IPPROTO_UDP,
+ (Inet4Address) InetAddress.getByName("100.96.167.146"), 37069,
+ (Inet4Address) InetAddress.getByName("216.58.197.10"), 443,
+ 180);
+ }
+
@Test
- public void testConntrackIPv4TcpTimeoutUpdate() throws Exception {
+ public void testConntrackMakeIPv4TcpTimeoutUpdate() throws Exception {
assumeTrue(USING_LE);
- final byte[] tcp = ConntrackMessage.newIPv4TimeoutUpdateRequest(
- OsConstants.IPPROTO_TCP,
- (Inet4Address) InetAddress.getByName("192.168.43.209"), 44333,
- (Inet4Address) InetAddress.getByName("23.211.13.26"), 443,
- 432000);
+ final byte[] tcp = makeIPv4TimeoutUpdateRequestTcp();
assertArrayEquals(CT_V4UPDATE_TCP_BYTES, tcp);
+ }
+ @Test
+ public void testConntrackParseIPv4TcpTimeoutUpdate() throws Exception {
+ assumeTrue(USING_LE);
+
+ final byte[] tcp = makeIPv4TimeoutUpdateRequestTcp();
final ByteBuffer byteBuffer = ByteBuffer.wrap(tcp);
byteBuffer.order(ByteOrder.nativeOrder());
final NetlinkMessage msg = NetlinkMessage.parse(byteBuffer, OsConstants.NETLINK_NETFILTER);
@@ -142,26 +160,30 @@
assertEquals(1, hdr.nlmsg_seq);
assertEquals(0, hdr.nlmsg_pid);
- final StructNfGenMsg nfmsgHdr = conntrackMessage.getNfHeader();
+ final StructNfGenMsg nfmsgHdr = conntrackMessage.nfGenMsg;
assertNotNull(nfmsgHdr);
assertEquals((byte) OsConstants.AF_INET, nfmsgHdr.nfgen_family);
assertEquals((byte) StructNfGenMsg.NFNETLINK_V0, nfmsgHdr.version);
assertEquals((short) 0, nfmsgHdr.res_id);
- // TODO: Parse the CTA_TUPLE_ORIG and CTA_TIMEOUT.
+ // TODO: Parse the CTA_TUPLE_ORIG.
+ assertEquals(0 /* absent */, conntrackMessage.status);
+ assertEquals(432000, conntrackMessage.timeoutSec);
}
@Test
- public void testConntrackIPv4UdpTimeoutUpdate() throws Exception {
+ public void testConntrackMakeIPv4UdpTimeoutUpdate() throws Exception {
assumeTrue(USING_LE);
- final byte[] udp = ConntrackMessage.newIPv4TimeoutUpdateRequest(
- OsConstants.IPPROTO_UDP,
- (Inet4Address) InetAddress.getByName("100.96.167.146"), 37069,
- (Inet4Address) InetAddress.getByName("216.58.197.10"), 443,
- 180);
+ final byte[] udp = makeIPv4TimeoutUpdateRequestUdp();
assertArrayEquals(CT_V4UPDATE_UDP_BYTES, udp);
+ }
+ @Test
+ public void testConntrackParseIPv4UdpTimeoutUpdate() throws Exception {
+ assumeTrue(USING_LE);
+
+ final byte[] udp = makeIPv4TimeoutUpdateRequestUdp();
final ByteBuffer byteBuffer = ByteBuffer.wrap(udp);
byteBuffer.order(ByteOrder.nativeOrder());
final NetlinkMessage msg = NetlinkMessage.parse(byteBuffer, OsConstants.NETLINK_NETFILTER);
@@ -178,20 +200,22 @@
assertEquals(1, hdr.nlmsg_seq);
assertEquals(0, hdr.nlmsg_pid);
- final StructNfGenMsg nfmsgHdr = conntrackMessage.getNfHeader();
+ final StructNfGenMsg nfmsgHdr = conntrackMessage.nfGenMsg;
assertNotNull(nfmsgHdr);
assertEquals((byte) OsConstants.AF_INET, nfmsgHdr.nfgen_family);
assertEquals((byte) StructNfGenMsg.NFNETLINK_V0, nfmsgHdr.version);
assertEquals((short) 0, nfmsgHdr.res_id);
- // TODO: Parse the CTA_TUPLE_ORIG and CTA_TIMEOUT.
+ // TODO: Parse the CTA_TUPLE_ORIG.
+ assertEquals(0 /* absent */, conntrackMessage.status);
+ assertEquals(180, conntrackMessage.timeoutSec);
}
// TODO: Add conntrack message attributes to have further verification.
public static final String CT_V4NEW_HEX =
// CHECKSTYLE:OFF IndentationCheck
// struct nlmsghdr
- "14000000" + // length = 20
+ "24000000" + // length = 36
"0001" + // type = NFNL_SUBSYS_CTNETLINK (1) << 8 | IPCTNL_MSG_CT_NEW (0)
"0006" + // flags = NLM_F_CREATE | NLM_F_EXCL
"00000000" + // seqno = 0
@@ -199,7 +223,17 @@
// struct nfgenmsg
"02" + // nfgen_family = AF_INET
"00" + // version = NFNETLINK_V0
- "1234"; // res_id = 0x1234 (big endian)
+ "1234" + // res_id = 0x1234 (big endian)
+ // struct nlattr
+ "0800" + // nla_len = 8
+ "0300" + // nla_type = CTA_STATUS
+ "00000198" + // nla_value = 0b110011000 (big endian)
+ // IPS_CONFIRMED (1 << 3) | IPS_SRC_NAT (1 << 4) |
+ // IPS_SRC_NAT_DONE (1 << 7) | IPS_DST_NAT_DONE (1 << 8)
+ // struct nlattr
+ "0800" + // nla_len = 8
+ "0700" + // nla_type = CTA_TIMEOUT
+ "00000078"; // nla_value = 120 (big endian)
// CHECKSTYLE:ON IndentationCheck
public static final byte[] CT_V4NEW_BYTES =
HexEncoding.decode(CT_V4NEW_HEX.replaceAll(" ", "").toCharArray(), false);
@@ -217,17 +251,20 @@
final StructNlMsgHdr hdr = conntrackMessage.getHeader();
assertNotNull(hdr);
- assertEquals(20, hdr.nlmsg_len);
+ assertEquals(36, hdr.nlmsg_len);
assertEquals(makeCtType(IPCTNL_MSG_CT_NEW), hdr.nlmsg_type);
assertEquals((short) (StructNlMsgHdr.NLM_F_CREATE | StructNlMsgHdr.NLM_F_EXCL),
hdr.nlmsg_flags);
assertEquals(0, hdr.nlmsg_seq);
assertEquals(0, hdr.nlmsg_pid);
- final StructNfGenMsg nfmsgHdr = conntrackMessage.getNfHeader();
+ final StructNfGenMsg nfmsgHdr = conntrackMessage.nfGenMsg;
assertNotNull(nfmsgHdr);
assertEquals((byte) OsConstants.AF_INET, nfmsgHdr.nfgen_family);
assertEquals((byte) StructNfGenMsg.NFNETLINK_V0, nfmsgHdr.version);
assertEquals((short) 0x1234, nfmsgHdr.res_id);
+
+ assertEquals(0x198, conntrackMessage.status);
+ assertEquals(120, conntrackMessage.timeoutSec);
}
}