Loading services/net/java/android/net/apf/ApfFilter.java +41 −13 Original line number Diff line number Diff line Loading @@ -180,6 +180,7 @@ public class ApfFilter { private static final int IPV4_FRAGMENT_OFFSET_MASK = 0x1fff; private static final int IPV4_PROTOCOL_OFFSET = ETH_HEADER_LEN + 9; private static final int IPV4_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 16; private static final int IPV4_ANY_HOST_ADDRESS = 0; private static final int IPV6_NEXT_HEADER_OFFSET = ETH_HEADER_LEN + 6; private static final int IPV6_SRC_ADDR_OFFSET = ETH_HEADER_LEN + 8; Loading @@ -201,12 +202,14 @@ public class ApfFilter { private static final int DHCP_CLIENT_MAC_OFFSET = ETH_HEADER_LEN + UDP_HEADER_LEN + 28; private static final int ARP_HEADER_OFFSET = ETH_HEADER_LEN; private static final byte[] ARP_IPV4_REQUEST_HEADER = new byte[]{ private static final int ARP_OPCODE_OFFSET = ARP_HEADER_OFFSET + 6; private static final short ARP_OPCODE_REQUEST = 1; private static final short ARP_OPCODE_REPLY = 2; private static final byte[] ARP_IPV4_HEADER = new byte[]{ 0, 1, // Hardware type: Ethernet (1) 8, 0, // Protocol type: IP (0x0800) 6, // Hardware size: 6 4, // Protocol size: 4 0, 1 // Opcode: request (1) }; private static final int ARP_TARGET_IP_ADDRESS_OFFSET = ETH_HEADER_LEN + 24; Loading Loading @@ -667,23 +670,48 @@ public class ApfFilter { private void generateArpFilterLocked(ApfGenerator gen) throws IllegalInstructionException { // Here's a basic summary of what the ARP filter program does: // // if interface has IPv4 address: // if it's not an ARP IPv4 request: // if not ARP IPv4 // pass // if it's not a request for our IPv4 address: // if not ARP IPv4 reply or request // pass // if unicast ARP reply // pass // if interface has no IPv4 address // if target ip is 0.0.0.0 // drop // else // if target ip is not the interface ip // drop // pass if (mIPv4Address != null) { // if it's not an ARP IPv4 request, pass final String checkTargetIPv4 = "checkTargetIPv4"; // Pass if not ARP IPv4. gen.addLoadImmediate(Register.R0, ARP_HEADER_OFFSET); gen.addJumpIfBytesNotEqual(Register.R0, ARP_IPV4_REQUEST_HEADER, gen.PASS_LABEL); // if it's not a request for our IPv4 address, drop gen.addJumpIfBytesNotEqual(Register.R0, ARP_IPV4_HEADER, gen.PASS_LABEL); // Pass if unknown ARP opcode. gen.addLoad16(Register.R0, ARP_OPCODE_OFFSET); gen.addJumpIfR0Equals(ARP_OPCODE_REQUEST, checkTargetIPv4); // Skip to unicast check gen.addJumpIfR0NotEquals(ARP_OPCODE_REPLY, gen.PASS_LABEL); // Pass if unicast reply. gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET); gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, gen.PASS_LABEL); // Either a unicast request, a unicast reply, or a broadcast reply. gen.defineLabel(checkTargetIPv4); if (mIPv4Address == null) { // When there is no IPv4 address, drop GARP replies (b/29404209). gen.addLoad32(Register.R0, ARP_TARGET_IP_ADDRESS_OFFSET); gen.addJumpIfR0Equals(IPV4_ANY_HOST_ADDRESS, gen.DROP_LABEL); } else { // When there is an IPv4 address, drop unicast/broadcast requests // and broadcast replies with a different target IPv4 address. gen.addLoadImmediate(Register.R0, ARP_TARGET_IP_ADDRESS_OFFSET); gen.addJumpIfBytesNotEqual(Register.R0, mIPv4Address, gen.DROP_LABEL); } // Otherwise, pass gen.addJump(gen.PASS_LABEL); } Loading services/tests/servicestests/src/android/net/apf/ApfTest.java +70 −30 Original line number Diff line number Diff line Loading @@ -580,7 +580,7 @@ public class ApfTest extends AndroidTestCase { public TestApfFilter(IpManager.Callback ipManagerCallback, boolean multicastFilter, IpConnectivityLog log) throws Exception { super(new ApfCapabilities(2, 1000, ARPHRD_ETHER), NetworkInterface.getByName("lo"), super(new ApfCapabilities(2, 1536, ARPHRD_ETHER), NetworkInterface.getByName("lo"), ipManagerCallback, multicastFilter, log); } Loading Loading @@ -618,6 +618,7 @@ public class ApfTest extends AndroidTestCase { } private static final int ETH_HEADER_LEN = 14; private static final int ETH_DEST_ADDR_OFFSET = 0; private static final int ETH_ETHERTYPE_OFFSET = 12; private static final byte[] ETH_BROADCAST_MAC_ADDRESS = new byte[]{ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff }; Loading Loading @@ -676,9 +677,18 @@ public class ApfTest extends AndroidTestCase { 4, // Protocol size: 4 0, 1 // Opcode: request (1) }; private static final byte[] ARP_IPV4_REPLY_HEADER = new byte[]{ 0, 1, // Hardware type: Ethernet (1) 8, 0, // Protocol type: IP (0x0800) 6, // Hardware size: 6 4, // Protocol size: 4 0, 2 // Opcode: reply (2) }; private static final int ARP_TARGET_IP_ADDRESS_OFFSET = ETH_HEADER_LEN + 24; private static final byte[] MOCK_IPV4_ADDR = new byte[]{10, 0, 0, 1}; private static final byte[] ANOTHER_IPV4_ADDR = new byte[]{10, 0, 0, 2}; private static final byte[] IPV4_ANY_HOST_ADDR = new byte[]{0, 0, 0, 0}; @LargeTest public void testApfFilterIPv4() throws Exception { Loading Loading @@ -801,51 +811,81 @@ public class ApfTest extends AndroidTestCase { apfFilter.shutdown(); } private void verifyArpFilter(MockIpManagerCallback ipManagerCallback, ApfFilter apfFilter, LinkProperties linkProperties, int filterResult) { ipManagerCallback.resetApfProgramWait(); apfFilter.setLinkProperties(linkProperties); byte[] program = ipManagerCallback.getApfProgram(); ByteBuffer packet = ByteBuffer.wrap(new byte[100]); packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_ARP); assertPass(program, packet.array(), 0); packet.position(ARP_HEADER_OFFSET); packet.put(ARP_IPV4_REQUEST_HEADER); assertVerdict(filterResult, program, packet.array(), 0); packet.position(ARP_TARGET_IP_ADDRESS_OFFSET); packet.put(MOCK_IPV4_ADDR); assertPass(program, packet.array(), 0); private byte[] getProgram(MockIpManagerCallback cb, ApfFilter filter, LinkProperties lp) { cb.resetApfProgramWait(); filter.setLinkProperties(lp); return cb.getApfProgram(); } private void verifyArpFilter(byte[] program, int filterResult) { // Verify ARP request packet assertPass(program, arpRequestBroadcast(MOCK_IPV4_ADDR), 0); assertVerdict(filterResult, program, arpRequestBroadcast(ANOTHER_IPV4_ADDR), 0); assertDrop(program, arpRequestBroadcast(IPV4_ANY_HOST_ADDR), 0); // Verify unicast ARP reply packet is always accepted. assertPass(program, arpReplyUnicast(MOCK_IPV4_ADDR), 0); assertPass(program, arpReplyUnicast(ANOTHER_IPV4_ADDR), 0); assertPass(program, arpReplyUnicast(IPV4_ANY_HOST_ADDR), 0); // Verify GARP reply packets are always filtered assertDrop(program, garpReply(), 0); } @LargeTest public void testApfFilterArp() throws Exception { MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback(); ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, ALLOW_MULTICAST, mLog); byte[] program = ipManagerCallback.getApfProgram(); // Verify initially ARP filter is off ByteBuffer packet = ByteBuffer.wrap(new byte[100]); packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_ARP); assertPass(program, packet.array(), 0); packet.position(ARP_HEADER_OFFSET); packet.put(ARP_IPV4_REQUEST_HEADER); assertPass(program, packet.array(), 0); packet.position(ARP_TARGET_IP_ADDRESS_OFFSET); packet.put(MOCK_IPV4_ADDR); assertPass(program, packet.array(), 0); // Verify initially ARP request filter is off, and GARP filter is on. verifyArpFilter(ipManagerCallback.getApfProgram(), PASS); // Inform ApfFilter of our address and verify ARP filtering is on LinkAddress linkAddress = new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 24); LinkProperties lp = new LinkProperties(); assertTrue(lp.addLinkAddress( new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 24))); verifyArpFilter(ipManagerCallback, apfFilter, lp, DROP); assertTrue(lp.addLinkAddress(linkAddress)); verifyArpFilter(getProgram(ipManagerCallback, apfFilter, lp), DROP); // Inform ApfFilter of loss of IP and verify ARP filtering is off verifyArpFilter(ipManagerCallback, apfFilter, new LinkProperties(), PASS); verifyArpFilter(getProgram(ipManagerCallback, apfFilter, new LinkProperties()), PASS); apfFilter.shutdown(); } private static byte[] arpRequestBroadcast(byte[] tip) { ByteBuffer packet = ByteBuffer.wrap(new byte[100]); packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_ARP); packet.position(ETH_DEST_ADDR_OFFSET); packet.put(ETH_BROADCAST_MAC_ADDRESS); packet.position(ARP_HEADER_OFFSET); packet.put(ARP_IPV4_REQUEST_HEADER); packet.position(ARP_TARGET_IP_ADDRESS_OFFSET); packet.put(tip); return packet.array(); } private static byte[] arpReplyUnicast(byte[] tip) { ByteBuffer packet = ByteBuffer.wrap(new byte[100]); packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_ARP); packet.position(ARP_HEADER_OFFSET); packet.put(ARP_IPV4_REPLY_HEADER); packet.position(ARP_TARGET_IP_ADDRESS_OFFSET); packet.put(tip); return packet.array(); } private static byte[] garpReply() { ByteBuffer packet = ByteBuffer.wrap(new byte[100]); packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_ARP); packet.position(ETH_DEST_ADDR_OFFSET); packet.put(ETH_BROADCAST_MAC_ADDRESS); packet.position(ARP_HEADER_OFFSET); packet.put(ARP_IPV4_REPLY_HEADER); packet.position(ARP_TARGET_IP_ADDRESS_OFFSET); packet.put(IPV4_ANY_HOST_ADDR); return packet.array(); } // Verify that the last program pushed to the IpManager.Callback properly filters the // given packet for the given lifetime. private void verifyRaLifetime(MockIpManagerCallback ipManagerCallback, ByteBuffer packet, Loading Loading
services/net/java/android/net/apf/ApfFilter.java +41 −13 Original line number Diff line number Diff line Loading @@ -180,6 +180,7 @@ public class ApfFilter { private static final int IPV4_FRAGMENT_OFFSET_MASK = 0x1fff; private static final int IPV4_PROTOCOL_OFFSET = ETH_HEADER_LEN + 9; private static final int IPV4_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 16; private static final int IPV4_ANY_HOST_ADDRESS = 0; private static final int IPV6_NEXT_HEADER_OFFSET = ETH_HEADER_LEN + 6; private static final int IPV6_SRC_ADDR_OFFSET = ETH_HEADER_LEN + 8; Loading @@ -201,12 +202,14 @@ public class ApfFilter { private static final int DHCP_CLIENT_MAC_OFFSET = ETH_HEADER_LEN + UDP_HEADER_LEN + 28; private static final int ARP_HEADER_OFFSET = ETH_HEADER_LEN; private static final byte[] ARP_IPV4_REQUEST_HEADER = new byte[]{ private static final int ARP_OPCODE_OFFSET = ARP_HEADER_OFFSET + 6; private static final short ARP_OPCODE_REQUEST = 1; private static final short ARP_OPCODE_REPLY = 2; private static final byte[] ARP_IPV4_HEADER = new byte[]{ 0, 1, // Hardware type: Ethernet (1) 8, 0, // Protocol type: IP (0x0800) 6, // Hardware size: 6 4, // Protocol size: 4 0, 1 // Opcode: request (1) }; private static final int ARP_TARGET_IP_ADDRESS_OFFSET = ETH_HEADER_LEN + 24; Loading Loading @@ -667,23 +670,48 @@ public class ApfFilter { private void generateArpFilterLocked(ApfGenerator gen) throws IllegalInstructionException { // Here's a basic summary of what the ARP filter program does: // // if interface has IPv4 address: // if it's not an ARP IPv4 request: // if not ARP IPv4 // pass // if it's not a request for our IPv4 address: // if not ARP IPv4 reply or request // pass // if unicast ARP reply // pass // if interface has no IPv4 address // if target ip is 0.0.0.0 // drop // else // if target ip is not the interface ip // drop // pass if (mIPv4Address != null) { // if it's not an ARP IPv4 request, pass final String checkTargetIPv4 = "checkTargetIPv4"; // Pass if not ARP IPv4. gen.addLoadImmediate(Register.R0, ARP_HEADER_OFFSET); gen.addJumpIfBytesNotEqual(Register.R0, ARP_IPV4_REQUEST_HEADER, gen.PASS_LABEL); // if it's not a request for our IPv4 address, drop gen.addJumpIfBytesNotEqual(Register.R0, ARP_IPV4_HEADER, gen.PASS_LABEL); // Pass if unknown ARP opcode. gen.addLoad16(Register.R0, ARP_OPCODE_OFFSET); gen.addJumpIfR0Equals(ARP_OPCODE_REQUEST, checkTargetIPv4); // Skip to unicast check gen.addJumpIfR0NotEquals(ARP_OPCODE_REPLY, gen.PASS_LABEL); // Pass if unicast reply. gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET); gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, gen.PASS_LABEL); // Either a unicast request, a unicast reply, or a broadcast reply. gen.defineLabel(checkTargetIPv4); if (mIPv4Address == null) { // When there is no IPv4 address, drop GARP replies (b/29404209). gen.addLoad32(Register.R0, ARP_TARGET_IP_ADDRESS_OFFSET); gen.addJumpIfR0Equals(IPV4_ANY_HOST_ADDRESS, gen.DROP_LABEL); } else { // When there is an IPv4 address, drop unicast/broadcast requests // and broadcast replies with a different target IPv4 address. gen.addLoadImmediate(Register.R0, ARP_TARGET_IP_ADDRESS_OFFSET); gen.addJumpIfBytesNotEqual(Register.R0, mIPv4Address, gen.DROP_LABEL); } // Otherwise, pass gen.addJump(gen.PASS_LABEL); } Loading
services/tests/servicestests/src/android/net/apf/ApfTest.java +70 −30 Original line number Diff line number Diff line Loading @@ -580,7 +580,7 @@ public class ApfTest extends AndroidTestCase { public TestApfFilter(IpManager.Callback ipManagerCallback, boolean multicastFilter, IpConnectivityLog log) throws Exception { super(new ApfCapabilities(2, 1000, ARPHRD_ETHER), NetworkInterface.getByName("lo"), super(new ApfCapabilities(2, 1536, ARPHRD_ETHER), NetworkInterface.getByName("lo"), ipManagerCallback, multicastFilter, log); } Loading Loading @@ -618,6 +618,7 @@ public class ApfTest extends AndroidTestCase { } private static final int ETH_HEADER_LEN = 14; private static final int ETH_DEST_ADDR_OFFSET = 0; private static final int ETH_ETHERTYPE_OFFSET = 12; private static final byte[] ETH_BROADCAST_MAC_ADDRESS = new byte[]{ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff }; Loading Loading @@ -676,9 +677,18 @@ public class ApfTest extends AndroidTestCase { 4, // Protocol size: 4 0, 1 // Opcode: request (1) }; private static final byte[] ARP_IPV4_REPLY_HEADER = new byte[]{ 0, 1, // Hardware type: Ethernet (1) 8, 0, // Protocol type: IP (0x0800) 6, // Hardware size: 6 4, // Protocol size: 4 0, 2 // Opcode: reply (2) }; private static final int ARP_TARGET_IP_ADDRESS_OFFSET = ETH_HEADER_LEN + 24; private static final byte[] MOCK_IPV4_ADDR = new byte[]{10, 0, 0, 1}; private static final byte[] ANOTHER_IPV4_ADDR = new byte[]{10, 0, 0, 2}; private static final byte[] IPV4_ANY_HOST_ADDR = new byte[]{0, 0, 0, 0}; @LargeTest public void testApfFilterIPv4() throws Exception { Loading Loading @@ -801,51 +811,81 @@ public class ApfTest extends AndroidTestCase { apfFilter.shutdown(); } private void verifyArpFilter(MockIpManagerCallback ipManagerCallback, ApfFilter apfFilter, LinkProperties linkProperties, int filterResult) { ipManagerCallback.resetApfProgramWait(); apfFilter.setLinkProperties(linkProperties); byte[] program = ipManagerCallback.getApfProgram(); ByteBuffer packet = ByteBuffer.wrap(new byte[100]); packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_ARP); assertPass(program, packet.array(), 0); packet.position(ARP_HEADER_OFFSET); packet.put(ARP_IPV4_REQUEST_HEADER); assertVerdict(filterResult, program, packet.array(), 0); packet.position(ARP_TARGET_IP_ADDRESS_OFFSET); packet.put(MOCK_IPV4_ADDR); assertPass(program, packet.array(), 0); private byte[] getProgram(MockIpManagerCallback cb, ApfFilter filter, LinkProperties lp) { cb.resetApfProgramWait(); filter.setLinkProperties(lp); return cb.getApfProgram(); } private void verifyArpFilter(byte[] program, int filterResult) { // Verify ARP request packet assertPass(program, arpRequestBroadcast(MOCK_IPV4_ADDR), 0); assertVerdict(filterResult, program, arpRequestBroadcast(ANOTHER_IPV4_ADDR), 0); assertDrop(program, arpRequestBroadcast(IPV4_ANY_HOST_ADDR), 0); // Verify unicast ARP reply packet is always accepted. assertPass(program, arpReplyUnicast(MOCK_IPV4_ADDR), 0); assertPass(program, arpReplyUnicast(ANOTHER_IPV4_ADDR), 0); assertPass(program, arpReplyUnicast(IPV4_ANY_HOST_ADDR), 0); // Verify GARP reply packets are always filtered assertDrop(program, garpReply(), 0); } @LargeTest public void testApfFilterArp() throws Exception { MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback(); ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, ALLOW_MULTICAST, mLog); byte[] program = ipManagerCallback.getApfProgram(); // Verify initially ARP filter is off ByteBuffer packet = ByteBuffer.wrap(new byte[100]); packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_ARP); assertPass(program, packet.array(), 0); packet.position(ARP_HEADER_OFFSET); packet.put(ARP_IPV4_REQUEST_HEADER); assertPass(program, packet.array(), 0); packet.position(ARP_TARGET_IP_ADDRESS_OFFSET); packet.put(MOCK_IPV4_ADDR); assertPass(program, packet.array(), 0); // Verify initially ARP request filter is off, and GARP filter is on. verifyArpFilter(ipManagerCallback.getApfProgram(), PASS); // Inform ApfFilter of our address and verify ARP filtering is on LinkAddress linkAddress = new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 24); LinkProperties lp = new LinkProperties(); assertTrue(lp.addLinkAddress( new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 24))); verifyArpFilter(ipManagerCallback, apfFilter, lp, DROP); assertTrue(lp.addLinkAddress(linkAddress)); verifyArpFilter(getProgram(ipManagerCallback, apfFilter, lp), DROP); // Inform ApfFilter of loss of IP and verify ARP filtering is off verifyArpFilter(ipManagerCallback, apfFilter, new LinkProperties(), PASS); verifyArpFilter(getProgram(ipManagerCallback, apfFilter, new LinkProperties()), PASS); apfFilter.shutdown(); } private static byte[] arpRequestBroadcast(byte[] tip) { ByteBuffer packet = ByteBuffer.wrap(new byte[100]); packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_ARP); packet.position(ETH_DEST_ADDR_OFFSET); packet.put(ETH_BROADCAST_MAC_ADDRESS); packet.position(ARP_HEADER_OFFSET); packet.put(ARP_IPV4_REQUEST_HEADER); packet.position(ARP_TARGET_IP_ADDRESS_OFFSET); packet.put(tip); return packet.array(); } private static byte[] arpReplyUnicast(byte[] tip) { ByteBuffer packet = ByteBuffer.wrap(new byte[100]); packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_ARP); packet.position(ARP_HEADER_OFFSET); packet.put(ARP_IPV4_REPLY_HEADER); packet.position(ARP_TARGET_IP_ADDRESS_OFFSET); packet.put(tip); return packet.array(); } private static byte[] garpReply() { ByteBuffer packet = ByteBuffer.wrap(new byte[100]); packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_ARP); packet.position(ETH_DEST_ADDR_OFFSET); packet.put(ETH_BROADCAST_MAC_ADDRESS); packet.position(ARP_HEADER_OFFSET); packet.put(ARP_IPV4_REPLY_HEADER); packet.position(ARP_TARGET_IP_ADDRESS_OFFSET); packet.put(IPV4_ANY_HOST_ADDR); return packet.array(); } // Verify that the last program pushed to the IpManager.Callback properly filters the // given packet for the given lifetime. private void verifyRaLifetime(MockIpManagerCallback ipManagerCallback, ByteBuffer packet, Loading