Loading common/netlinkclient/src/android/net/netlink/ConntrackMessage.java +13 −2 Original line number Original line Diff line number Diff line Loading @@ -102,9 +102,16 @@ public class ConntrackMessage extends NetlinkMessage { * message could not be parsed successfully (for example, if it was truncated). * message could not be parsed successfully (for example, if it was truncated). */ */ public static ConntrackMessage parse(StructNlMsgHdr header, ByteBuffer byteBuffer) { public static ConntrackMessage parse(StructNlMsgHdr header, ByteBuffer byteBuffer) { // Just build the netlink header for now and pretend the whole message was consumed. // Just build the netlink header and netfilter header for now and pretend the whole message // TODO: Parse the netfilter message header and conntrack attributes. // was consumed. // TODO: Parse the conntrack attributes. final ConntrackMessage conntrackMsg = new ConntrackMessage(header); final ConntrackMessage conntrackMsg = new ConntrackMessage(header); conntrackMsg.mNfGenMsg = StructNfGenMsg.parse(byteBuffer); if (conntrackMsg.mNfGenMsg == null) { return null; } byteBuffer.position(byteBuffer.limit()); byteBuffer.position(byteBuffer.limit()); return conntrackMsg; return conntrackMsg; } } Loading @@ -121,6 +128,10 @@ public class ConntrackMessage extends NetlinkMessage { mNfGenMsg = null; mNfGenMsg = null; } } public StructNfGenMsg getNfHeader() { return mNfGenMsg; } public void pack(ByteBuffer byteBuffer) { public void pack(ByteBuffer byteBuffer) { mHeader.pack(byteBuffer); mHeader.pack(byteBuffer); mNfGenMsg.pack(byteBuffer); mNfGenMsg.pack(byteBuffer); Loading common/netlinkclient/src/android/net/netlink/StructNfGenMsg.java +40 −0 Original line number Original line Diff line number Diff line Loading @@ -16,7 +16,12 @@ package android.net.netlink; package android.net.netlink; import android.annotation.NonNull; import android.annotation.Nullable; import java.nio.ByteBuffer; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Objects; /** /** Loading @@ -35,6 +40,36 @@ public class StructNfGenMsg { final public byte version; final public byte version; final public short res_id; // N.B.: this is big endian in the kernel final public short res_id; // N.B.: this is big endian in the kernel /** * Parses a netfilter netlink header from a {@link ByteBuffer}. * * @param byteBuffer The buffer from which to parse the netfilter netlink header. * @return the parsed netfilter netlink header, or {@code null} if the netfilter netlink header * could not be parsed successfully (for example, if it was truncated). */ @Nullable public static StructNfGenMsg parse(@NonNull ByteBuffer byteBuffer) { Objects.requireNonNull(byteBuffer); if (!hasAvailableSpace(byteBuffer)) return null; final byte nfgen_family = byteBuffer.get(); final byte version = byteBuffer.get(); final ByteOrder originalOrder = byteBuffer.order(); byteBuffer.order(ByteOrder.BIG_ENDIAN); final short res_id = byteBuffer.getShort(); byteBuffer.order(originalOrder); return new StructNfGenMsg(nfgen_family, version, res_id); } public StructNfGenMsg(byte family, byte ver, short id) { nfgen_family = family; version = ver; res_id = id; } public StructNfGenMsg(byte family) { public StructNfGenMsg(byte family) { nfgen_family = family; nfgen_family = family; version = (byte) NFNETLINK_V0; version = (byte) NFNETLINK_V0; Loading @@ -44,6 +79,11 @@ public class StructNfGenMsg { public void pack(ByteBuffer byteBuffer) { public void pack(ByteBuffer byteBuffer) { byteBuffer.put(nfgen_family); byteBuffer.put(nfgen_family); byteBuffer.put(version); byteBuffer.put(version); // TODO: probably need to handle the little endian case. byteBuffer.putShort(res_id); byteBuffer.putShort(res_id); } } private static boolean hasAvailableSpace(@NonNull ByteBuffer byteBuffer) { return byteBuffer.remaining() >= STRUCT_SIZE; } } } tests/unit/src/android/net/netlink/ConntrackMessageTest.java +103 −0 Original line number Original line Diff line number Diff line Loading @@ -16,7 +16,13 @@ package android.net.netlink; package android.net.netlink; import static android.net.netlink.NetlinkConstants.IPCTNL_MSG_CT_NEW; import static android.net.netlink.NetlinkConstants.NFNL_SUBSYS_CTNETLINK; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeTrue; import static org.junit.Assume.assumeTrue; import android.system.OsConstants; import android.system.OsConstants; Loading @@ -31,6 +37,7 @@ import org.junit.runner.RunWith; import java.net.Inet4Address; import java.net.Inet4Address; import java.net.InetAddress; import java.net.InetAddress; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.ByteOrder; @RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class) Loading @@ -38,6 +45,10 @@ import java.nio.ByteOrder; public class ConntrackMessageTest { public class ConntrackMessageTest { private static final boolean USING_LE = (ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN); private static final boolean USING_LE = (ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN); private short makeCtType(short msgType) { return (short) (NFNL_SUBSYS_CTNETLINK << 8 | (byte) msgType); } // Example 1: TCP (192.168.43.209, 44333) -> (23.211.13.26, 443) // Example 1: TCP (192.168.43.209, 44333) -> (23.211.13.26, 443) public static final String CT_V4UPDATE_TCP_HEX = public static final String CT_V4UPDATE_TCP_HEX = // struct nlmsghdr // struct nlmsghdr Loading Loading @@ -114,6 +125,30 @@ public class ConntrackMessageTest { (Inet4Address) InetAddress.getByName("23.211.13.26"), 443, (Inet4Address) InetAddress.getByName("23.211.13.26"), 443, 432000); 432000); assertArrayEquals(CT_V4UPDATE_TCP_BYTES, tcp); assertArrayEquals(CT_V4UPDATE_TCP_BYTES, tcp); final ByteBuffer byteBuffer = ByteBuffer.wrap(tcp); byteBuffer.order(ByteOrder.nativeOrder()); final NetlinkMessage msg = NetlinkMessage.parse(byteBuffer, OsConstants.NETLINK_NETFILTER); assertNotNull(msg); assertTrue(msg instanceof ConntrackMessage); final ConntrackMessage conntrackMessage = (ConntrackMessage) msg; final StructNlMsgHdr hdr = conntrackMessage.getHeader(); assertNotNull(hdr); assertEquals(80, hdr.nlmsg_len); assertEquals(makeCtType(IPCTNL_MSG_CT_NEW), hdr.nlmsg_type); assertEquals((short) (StructNlMsgHdr.NLM_F_REPLACE | StructNlMsgHdr.NLM_F_REQUEST | StructNlMsgHdr.NLM_F_ACK), hdr.nlmsg_flags); assertEquals(1, hdr.nlmsg_seq); assertEquals(0, hdr.nlmsg_pid); final StructNfGenMsg nfmsgHdr = conntrackMessage.getNfHeader(); 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. } } @Test @Test Loading @@ -126,5 +161,73 @@ public class ConntrackMessageTest { (Inet4Address) InetAddress.getByName("216.58.197.10"), 443, (Inet4Address) InetAddress.getByName("216.58.197.10"), 443, 180); 180); assertArrayEquals(CT_V4UPDATE_UDP_BYTES, udp); assertArrayEquals(CT_V4UPDATE_UDP_BYTES, udp); final ByteBuffer byteBuffer = ByteBuffer.wrap(udp); byteBuffer.order(ByteOrder.nativeOrder()); final NetlinkMessage msg = NetlinkMessage.parse(byteBuffer, OsConstants.NETLINK_NETFILTER); assertNotNull(msg); assertTrue(msg instanceof ConntrackMessage); final ConntrackMessage conntrackMessage = (ConntrackMessage) msg; final StructNlMsgHdr hdr = conntrackMessage.getHeader(); assertNotNull(hdr); assertEquals(80, hdr.nlmsg_len); assertEquals(makeCtType(IPCTNL_MSG_CT_NEW), hdr.nlmsg_type); assertEquals((short) (StructNlMsgHdr.NLM_F_REPLACE | StructNlMsgHdr.NLM_F_REQUEST | StructNlMsgHdr.NLM_F_ACK), hdr.nlmsg_flags); assertEquals(1, hdr.nlmsg_seq); assertEquals(0, hdr.nlmsg_pid); final StructNfGenMsg nfmsgHdr = conntrackMessage.getNfHeader(); 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: Add conntrack message attributes to have further verification. public static final String CT_V4NEW_HEX = // CHECKSTYLE:OFF IndentationCheck // struct nlmsghdr "14000000" + // length = 20 "0001" + // type = NFNL_SUBSYS_CTNETLINK (1) << 8 | IPCTNL_MSG_CT_NEW (0) "0006" + // flags = NLM_F_CREATE | NLM_F_EXCL "00000000" + // seqno = 0 "00000000" + // pid = 0 // struct nfgenmsg "02" + // nfgen_family = AF_INET "00" + // version = NFNETLINK_V0 "1234"; // res_id = 0x1234 (big endian) // CHECKSTYLE:ON IndentationCheck public static final byte[] CT_V4NEW_BYTES = HexEncoding.decode(CT_V4NEW_HEX.replaceAll(" ", "").toCharArray(), false); @Test public void testParseCtNew() { assumeTrue(USING_LE); final ByteBuffer byteBuffer = ByteBuffer.wrap(CT_V4NEW_BYTES); byteBuffer.order(ByteOrder.nativeOrder()); final NetlinkMessage msg = NetlinkMessage.parse(byteBuffer, OsConstants.NETLINK_NETFILTER); assertNotNull(msg); assertTrue(msg instanceof ConntrackMessage); final ConntrackMessage conntrackMessage = (ConntrackMessage) msg; final StructNlMsgHdr hdr = conntrackMessage.getHeader(); assertNotNull(hdr); assertEquals(20, 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(); assertNotNull(nfmsgHdr); assertEquals((byte) OsConstants.AF_INET, nfmsgHdr.nfgen_family); assertEquals((byte) StructNfGenMsg.NFNETLINK_V0, nfmsgHdr.version); assertEquals((short) 0x1234, nfmsgHdr.res_id); } } } } Loading
common/netlinkclient/src/android/net/netlink/ConntrackMessage.java +13 −2 Original line number Original line Diff line number Diff line Loading @@ -102,9 +102,16 @@ public class ConntrackMessage extends NetlinkMessage { * message could not be parsed successfully (for example, if it was truncated). * message could not be parsed successfully (for example, if it was truncated). */ */ public static ConntrackMessage parse(StructNlMsgHdr header, ByteBuffer byteBuffer) { public static ConntrackMessage parse(StructNlMsgHdr header, ByteBuffer byteBuffer) { // Just build the netlink header for now and pretend the whole message was consumed. // Just build the netlink header and netfilter header for now and pretend the whole message // TODO: Parse the netfilter message header and conntrack attributes. // was consumed. // TODO: Parse the conntrack attributes. final ConntrackMessage conntrackMsg = new ConntrackMessage(header); final ConntrackMessage conntrackMsg = new ConntrackMessage(header); conntrackMsg.mNfGenMsg = StructNfGenMsg.parse(byteBuffer); if (conntrackMsg.mNfGenMsg == null) { return null; } byteBuffer.position(byteBuffer.limit()); byteBuffer.position(byteBuffer.limit()); return conntrackMsg; return conntrackMsg; } } Loading @@ -121,6 +128,10 @@ public class ConntrackMessage extends NetlinkMessage { mNfGenMsg = null; mNfGenMsg = null; } } public StructNfGenMsg getNfHeader() { return mNfGenMsg; } public void pack(ByteBuffer byteBuffer) { public void pack(ByteBuffer byteBuffer) { mHeader.pack(byteBuffer); mHeader.pack(byteBuffer); mNfGenMsg.pack(byteBuffer); mNfGenMsg.pack(byteBuffer); Loading
common/netlinkclient/src/android/net/netlink/StructNfGenMsg.java +40 −0 Original line number Original line Diff line number Diff line Loading @@ -16,7 +16,12 @@ package android.net.netlink; package android.net.netlink; import android.annotation.NonNull; import android.annotation.Nullable; import java.nio.ByteBuffer; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Objects; /** /** Loading @@ -35,6 +40,36 @@ public class StructNfGenMsg { final public byte version; final public byte version; final public short res_id; // N.B.: this is big endian in the kernel final public short res_id; // N.B.: this is big endian in the kernel /** * Parses a netfilter netlink header from a {@link ByteBuffer}. * * @param byteBuffer The buffer from which to parse the netfilter netlink header. * @return the parsed netfilter netlink header, or {@code null} if the netfilter netlink header * could not be parsed successfully (for example, if it was truncated). */ @Nullable public static StructNfGenMsg parse(@NonNull ByteBuffer byteBuffer) { Objects.requireNonNull(byteBuffer); if (!hasAvailableSpace(byteBuffer)) return null; final byte nfgen_family = byteBuffer.get(); final byte version = byteBuffer.get(); final ByteOrder originalOrder = byteBuffer.order(); byteBuffer.order(ByteOrder.BIG_ENDIAN); final short res_id = byteBuffer.getShort(); byteBuffer.order(originalOrder); return new StructNfGenMsg(nfgen_family, version, res_id); } public StructNfGenMsg(byte family, byte ver, short id) { nfgen_family = family; version = ver; res_id = id; } public StructNfGenMsg(byte family) { public StructNfGenMsg(byte family) { nfgen_family = family; nfgen_family = family; version = (byte) NFNETLINK_V0; version = (byte) NFNETLINK_V0; Loading @@ -44,6 +79,11 @@ public class StructNfGenMsg { public void pack(ByteBuffer byteBuffer) { public void pack(ByteBuffer byteBuffer) { byteBuffer.put(nfgen_family); byteBuffer.put(nfgen_family); byteBuffer.put(version); byteBuffer.put(version); // TODO: probably need to handle the little endian case. byteBuffer.putShort(res_id); byteBuffer.putShort(res_id); } } private static boolean hasAvailableSpace(@NonNull ByteBuffer byteBuffer) { return byteBuffer.remaining() >= STRUCT_SIZE; } } }
tests/unit/src/android/net/netlink/ConntrackMessageTest.java +103 −0 Original line number Original line Diff line number Diff line Loading @@ -16,7 +16,13 @@ package android.net.netlink; package android.net.netlink; import static android.net.netlink.NetlinkConstants.IPCTNL_MSG_CT_NEW; import static android.net.netlink.NetlinkConstants.NFNL_SUBSYS_CTNETLINK; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeTrue; import static org.junit.Assume.assumeTrue; import android.system.OsConstants; import android.system.OsConstants; Loading @@ -31,6 +37,7 @@ import org.junit.runner.RunWith; import java.net.Inet4Address; import java.net.Inet4Address; import java.net.InetAddress; import java.net.InetAddress; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.ByteOrder; @RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class) Loading @@ -38,6 +45,10 @@ import java.nio.ByteOrder; public class ConntrackMessageTest { public class ConntrackMessageTest { private static final boolean USING_LE = (ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN); private static final boolean USING_LE = (ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN); private short makeCtType(short msgType) { return (short) (NFNL_SUBSYS_CTNETLINK << 8 | (byte) msgType); } // Example 1: TCP (192.168.43.209, 44333) -> (23.211.13.26, 443) // Example 1: TCP (192.168.43.209, 44333) -> (23.211.13.26, 443) public static final String CT_V4UPDATE_TCP_HEX = public static final String CT_V4UPDATE_TCP_HEX = // struct nlmsghdr // struct nlmsghdr Loading Loading @@ -114,6 +125,30 @@ public class ConntrackMessageTest { (Inet4Address) InetAddress.getByName("23.211.13.26"), 443, (Inet4Address) InetAddress.getByName("23.211.13.26"), 443, 432000); 432000); assertArrayEquals(CT_V4UPDATE_TCP_BYTES, tcp); assertArrayEquals(CT_V4UPDATE_TCP_BYTES, tcp); final ByteBuffer byteBuffer = ByteBuffer.wrap(tcp); byteBuffer.order(ByteOrder.nativeOrder()); final NetlinkMessage msg = NetlinkMessage.parse(byteBuffer, OsConstants.NETLINK_NETFILTER); assertNotNull(msg); assertTrue(msg instanceof ConntrackMessage); final ConntrackMessage conntrackMessage = (ConntrackMessage) msg; final StructNlMsgHdr hdr = conntrackMessage.getHeader(); assertNotNull(hdr); assertEquals(80, hdr.nlmsg_len); assertEquals(makeCtType(IPCTNL_MSG_CT_NEW), hdr.nlmsg_type); assertEquals((short) (StructNlMsgHdr.NLM_F_REPLACE | StructNlMsgHdr.NLM_F_REQUEST | StructNlMsgHdr.NLM_F_ACK), hdr.nlmsg_flags); assertEquals(1, hdr.nlmsg_seq); assertEquals(0, hdr.nlmsg_pid); final StructNfGenMsg nfmsgHdr = conntrackMessage.getNfHeader(); 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. } } @Test @Test Loading @@ -126,5 +161,73 @@ public class ConntrackMessageTest { (Inet4Address) InetAddress.getByName("216.58.197.10"), 443, (Inet4Address) InetAddress.getByName("216.58.197.10"), 443, 180); 180); assertArrayEquals(CT_V4UPDATE_UDP_BYTES, udp); assertArrayEquals(CT_V4UPDATE_UDP_BYTES, udp); final ByteBuffer byteBuffer = ByteBuffer.wrap(udp); byteBuffer.order(ByteOrder.nativeOrder()); final NetlinkMessage msg = NetlinkMessage.parse(byteBuffer, OsConstants.NETLINK_NETFILTER); assertNotNull(msg); assertTrue(msg instanceof ConntrackMessage); final ConntrackMessage conntrackMessage = (ConntrackMessage) msg; final StructNlMsgHdr hdr = conntrackMessage.getHeader(); assertNotNull(hdr); assertEquals(80, hdr.nlmsg_len); assertEquals(makeCtType(IPCTNL_MSG_CT_NEW), hdr.nlmsg_type); assertEquals((short) (StructNlMsgHdr.NLM_F_REPLACE | StructNlMsgHdr.NLM_F_REQUEST | StructNlMsgHdr.NLM_F_ACK), hdr.nlmsg_flags); assertEquals(1, hdr.nlmsg_seq); assertEquals(0, hdr.nlmsg_pid); final StructNfGenMsg nfmsgHdr = conntrackMessage.getNfHeader(); 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: Add conntrack message attributes to have further verification. public static final String CT_V4NEW_HEX = // CHECKSTYLE:OFF IndentationCheck // struct nlmsghdr "14000000" + // length = 20 "0001" + // type = NFNL_SUBSYS_CTNETLINK (1) << 8 | IPCTNL_MSG_CT_NEW (0) "0006" + // flags = NLM_F_CREATE | NLM_F_EXCL "00000000" + // seqno = 0 "00000000" + // pid = 0 // struct nfgenmsg "02" + // nfgen_family = AF_INET "00" + // version = NFNETLINK_V0 "1234"; // res_id = 0x1234 (big endian) // CHECKSTYLE:ON IndentationCheck public static final byte[] CT_V4NEW_BYTES = HexEncoding.decode(CT_V4NEW_HEX.replaceAll(" ", "").toCharArray(), false); @Test public void testParseCtNew() { assumeTrue(USING_LE); final ByteBuffer byteBuffer = ByteBuffer.wrap(CT_V4NEW_BYTES); byteBuffer.order(ByteOrder.nativeOrder()); final NetlinkMessage msg = NetlinkMessage.parse(byteBuffer, OsConstants.NETLINK_NETFILTER); assertNotNull(msg); assertTrue(msg instanceof ConntrackMessage); final ConntrackMessage conntrackMessage = (ConntrackMessage) msg; final StructNlMsgHdr hdr = conntrackMessage.getHeader(); assertNotNull(hdr); assertEquals(20, 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(); assertNotNull(nfmsgHdr); assertEquals((byte) OsConstants.AF_INET, nfmsgHdr.nfgen_family); assertEquals((byte) StructNfGenMsg.NFNETLINK_V0, nfmsgHdr.version); assertEquals((short) 0x1234, nfmsgHdr.res_id); } } } }