Use JBSMATCH opcode for ethernet address matching
This commit refactors the code to use the JBSMATCH opcode when possible
for ethernet address matching, improving code density.
Before:
03-21 14:43:07.523 28553 28566 I ApfFilterTest: all feature on, program size: 4503
After:
03-21 16:43:22.941 1490 1510 I ApfFilterTest: all feature on, program size: 4446
Test: TH
Change-Id: Id2f20bdfe2da579b3f1d8e441533532aaee2816d
diff --git a/src/android/net/apf/ApfFilter.java b/src/android/net/apf/ApfFilter.java
index 7c6692f..bde487e 100644
--- a/src/android/net/apf/ApfFilter.java
+++ b/src/android/net/apf/ApfFilter.java
@@ -1830,8 +1830,8 @@
// Pass if non-broadcast reply.
// This also accepts multicast arp, but we assume those don't exist.
- gen.addLoadImmediate(R0, ETH_DEST_ADDR_OFFSET);
- gen.addCountAndPassIfBytesAtR0NotEqual(ETHER_BROADCAST, PASSED_ARP_UNICAST_REPLY);
+ gen.addCountAndPassIfBytesAtOffsetNotEqual(ETH_DEST_ADDR_OFFSET, ETHER_BROADCAST,
+ PASSED_ARP_UNICAST_REPLY);
// It is a broadcast reply.
if (mIPv4Address == null) {
@@ -1897,8 +1897,8 @@
// the future, such packets will likely be dropped by multicast filters.
// Since the device may have packet forwarding enabled, APF needs to pass any received
// unicast IPv4 ping not destined for the device's IP address to the kernel.
- gen.addLoadImmediate(R0, ETHER_DST_ADDR_OFFSET)
- .addJumpIfBytesAtR0NotEqual(mHardwareAddress, skipIpv4PingFilter)
+ gen.addJumpIfBytesAtOffsetNotEqual(
+ ETH_DEST_ADDR_OFFSET, mHardwareAddress, skipIpv4PingFilter)
.addLoadImmediate(R0, IPV4_DEST_ADDR_OFFSET)
.addJumpIfBytesAtR0NotEqual(mIPv4Address, skipIpv4PingFilter);
@@ -1979,11 +1979,11 @@
// address for IPv4 mDNS packet) or the device's MAC address, skip filtering.
// We need to check both the mDNS multicast MAC address and the device's MAC address
// because multicast to unicast conversion might have occurred.
- gen.addLoadImmediate(R0, ETH_DEST_ADDR_OFFSET)
- .addJumpIfBytesAtR0EqualsNoneOf(
- List.of(mHardwareAddress, ETH_MULTICAST_MDNS_V4_MAC_ADDRESS),
- skipMdnsFilter
- );
+ gen.addJumpIfBytesAtOffsetEqualsNoneOf(
+ ETH_DEST_ADDR_OFFSET,
+ List.of(mHardwareAddress, ETH_MULTICAST_MDNS_V4_MAC_ADDRESS),
+ skipMdnsFilter
+ );
// Ignore packets with IPv4 options (header size not equal to 20) as they are rare.
gen.addLoadFromMemory(R0, MemorySlot.IPV4_HEADER_SIZE)
@@ -2185,8 +2185,8 @@
// Otherwise, this is an IPv4 unicast, pass
// If L2 broadcast packet, drop.
// TODO: can we invert this condition to fall through to the common pass case below?
- gen.addLoadImmediate(R0, ETH_DEST_ADDR_OFFSET);
- gen.addCountAndPassIfBytesAtR0NotEqual(ETHER_BROADCAST, PASSED_IPV4_UNICAST);
+ gen.addCountAndPassIfBytesAtOffsetNotEqual(ETH_DEST_ADDR_OFFSET, ETHER_BROADCAST,
+ PASSED_IPV4_UNICAST);
gen.addCountAndDrop(DROPPED_IPV4_L2_BROADCAST);
}
@@ -2329,8 +2329,8 @@
// used by processes other than clatd. This is because APF cannot reliably detect signal
// on when IPV6_{JOIN,LEAVE}_ANYCAST is triggered.
final List<byte[]> allMACs = getKnownMacAddresses();
- v6Gen.addLoadImmediate(R0, ETH_DEST_ADDR_OFFSET)
- .addCountAndDropIfBytesAtR0EqualsNoneOf(allMACs, DROPPED_IPV6_NS_OTHER_HOST);
+ v6Gen.addCountAndDropIfBytesAtOffsetEqualsNoneOf(ETH_DEST_ADDR_OFFSET, allMACs,
+ DROPPED_IPV6_NS_OTHER_HOST);
// Dst IPv6 address check:
final List<byte[]> allSuffixes = getSolicitedNodeMcastAddressSuffix(allIPv6Addrs);
@@ -2444,11 +2444,11 @@
// address for IPv6 mDNS packet) or the device's MAC address, skip filtering.
// We need to check both the mDNS multicast MAC address and the device's MAC address
// because multicast to unicast conversion might have occurred.
- gen.addLoadImmediate(R0, ETH_DEST_ADDR_OFFSET)
- .addJumpIfBytesAtR0EqualsNoneOf(
- List.of(mHardwareAddress, ETH_MULTICAST_MDNS_V6_MAC_ADDRESS),
- skipMdnsFilter
- );
+ gen.addJumpIfBytesAtOffsetEqualsNoneOf(
+ ETH_DEST_ADDR_OFFSET,
+ List.of(mHardwareAddress, ETH_MULTICAST_MDNS_V6_MAC_ADDRESS),
+ skipMdnsFilter
+ );
// Skip filtering if the packet is not an IPv6 UDP packet.
gen.addLoad8intoR0(IPV6_NEXT_HEADER_OFFSET)
@@ -2509,8 +2509,8 @@
true /* includeNonTentative */,
false /* includeTentative */,
false /* includeAnycast */);
- gen.addLoadImmediate(R0, ETHER_DST_ADDR_OFFSET)
- .addJumpIfBytesAtR0NotEqual(mHardwareAddress, skipPing6Offload)
+ gen.addJumpIfBytesAtOffsetNotEqual(
+ ETHER_DST_ADDR_OFFSET, mHardwareAddress, skipPing6Offload)
.addLoadImmediate(R0, IPV6_DEST_ADDR_OFFSET)
.addJumpIfBytesAtR0EqualsNoneOf(nonTentativeIPv6Addrs, skipPing6Offload);
@@ -3604,8 +3604,8 @@
// Pass unicast TDLS packet but drop non-unicast TDLS packet.
short skipTDLScheck = gen.getUniqueLabel();
gen.addJumpIfR0NotEquals(0x890DL, skipTDLScheck)
- .addLoadImmediate(R0, ETH_DEST_ADDR_OFFSET)
- .addCountAndDropIfBytesAtR0NotEqual(mHardwareAddress, DROPPED_NON_UNICAST_TDLS)
+ .addCountAndDropIfBytesAtOffsetNotEqual(
+ ETH_DEST_ADDR_OFFSET, mHardwareAddress, DROPPED_NON_UNICAST_TDLS)
.addCountAndPass(PASSED_NON_IP_UNICAST)
.defineLabel(skipTDLScheck);
@@ -3650,8 +3650,8 @@
gen.addJumpIfR0Equals(ETH_P_IPV6, ipv6FilterLabel);
// Drop non-IP non-ARP broadcasts, pass the rest
- gen.addLoadImmediate(R0, ETH_DEST_ADDR_OFFSET);
- gen.addCountAndPassIfBytesAtR0NotEqual(ETHER_BROADCAST, PASSED_NON_IP_UNICAST);
+ gen.addCountAndPassIfBytesAtOffsetNotEqual(ETH_DEST_ADDR_OFFSET, ETHER_BROADCAST,
+ PASSED_NON_IP_UNICAST);
gen.addCountAndDrop(DROPPED_ETH_BROADCAST);
// Add IPv6 filters:
@@ -3739,11 +3739,17 @@
}
void preloadData(ApfV61GeneratorBase<?> gen) throws IllegalInstructionException {
+ final List<byte[]> preloadedMacAddress = getKnownMacAddresses();
final List<byte[]> preloadedIPv6Address = getIpv6Addresses(true /* includeNonTentative */,
true /* includeTentative */, true /* includeAnycast */);
- final int preloadDataSize = preloadedIPv6Address.size() * 16;
+ final int preloadDataSize =
+ preloadedIPv6Address.size() * 16 + preloadedMacAddress.size() * 6;
final byte[] preloadData = new byte[preloadDataSize];
int offset = 0;
+ for (byte[] addr : preloadedMacAddress) {
+ System.arraycopy(addr, 0, preloadData, offset, 6);
+ offset += 6;
+ }
for (byte[] addr : preloadedIPv6Address) {
System.arraycopy(addr, 0, preloadData, offset, 16);
offset += 16;
diff --git a/src/android/net/apf/ApfV4GeneratorBase.java b/src/android/net/apf/ApfV4GeneratorBase.java
index f142e31..fb929dc 100644
--- a/src/android/net/apf/ApfV4GeneratorBase.java
+++ b/src/android/net/apf/ApfV4GeneratorBase.java
@@ -482,6 +482,17 @@
}
/**
+ * Add an instruction to the end of the program to jump to {@code tgt} if the bytes of the
+ * packet at an offset specified by {@code offset} don't match {@code bytes}.
+ * This method needs to be non-final because APFv4 and APFv6 share the same implementation,
+ * but in APFv6.1, this method will be overridden to use the JBSPTRMATCH instruction.
+ */
+ public Type addJumpIfBytesAtOffsetNotEqual(int offset, @NonNull byte[] bytes, short tgt)
+ throws IllegalInstructionException {
+ return addLoadImmediate(R0, offset).addJumpIfBytesAtR0NotEqual(bytes, tgt);
+ }
+
+ /**
* Add instructions to the end of the program to increase counter and drop packet if the
* bytes of the packet at an offset specified by register0 don't match {@code bytes}.
* WARNING: may modify R1
@@ -499,6 +510,28 @@
/**
* Add instructions to the end of the program to increase counter and drop packet if the
+ * bytes of the packet at an offset specified by {@code offset} don't match {@code bytes}.
+ * This method needs to be non-final because APFv4 and APFv6 share the same implementation,
+ * but in APFv6.1, this method will be overridden to use the JBSPTRMATCH instruction.
+ */
+ public Type addCountAndDropIfBytesAtOffsetNotEqual(int offset, byte[] bytes,
+ ApfCounterTracker.Counter cnt) throws IllegalInstructionException {
+ return addLoadImmediate(R0, offset).addCountAndDropIfBytesAtR0NotEqual(bytes, cnt);
+ }
+
+ /**
+ * Add instructions to the end of the program to increase counter and pass packet if the
+ * bytes of the packet at an offset specified by {@code offset} don't match {@code bytes}.
+ * This method needs to be non-final because APFv4 and APFv6 share the same implementation,
+ * but in APFv6.1, this method will be overridden to use the JBSPTRMATCH instruction.
+ */
+ public Type addCountAndPassIfBytesAtOffsetNotEqual(int offset, byte[] bytes,
+ ApfCounterTracker.Counter cnt) throws IllegalInstructionException {
+ return addLoadImmediate(R0, offset).addCountAndPassIfBytesAtR0NotEqual(bytes, cnt);
+ }
+
+ /**
+ * Add instructions to the end of the program to increase counter and drop packet if the
* bytes of the packet at an offset specified by register0 match {@code bytes}.
* WARNING: may modify R1
*/
diff --git a/src/android/net/apf/ApfV61GeneratorBase.java b/src/android/net/apf/ApfV61GeneratorBase.java
index 014d893..c686b71 100644
--- a/src/android/net/apf/ApfV61GeneratorBase.java
+++ b/src/android/net/apf/ApfV61GeneratorBase.java
@@ -325,6 +325,24 @@
return addJumpIfBytesAtOffsetEqualsNoneOf(offset, bytesList, cnt.getJumpPassLabel());
}
+ @Override
+ public Type addCountAndPassIfBytesAtOffsetNotEqual(int offset, byte[] bytes,
+ ApfCounterTracker.Counter cnt) throws IllegalInstructionException {
+ return addJumpIfBytesAtOffsetEqualsNoneOf(offset, List.of(bytes), cnt.getJumpPassLabel());
+ }
+
+ @Override
+ public Type addCountAndDropIfBytesAtOffsetNotEqual(int offset, byte[] bytes,
+ ApfCounterTracker.Counter cnt) throws IllegalInstructionException {
+ return addJumpIfBytesAtOffsetEqualsNoneOf(offset, List.of(bytes), cnt.getJumpDropLabel());
+ }
+
+ @Override
+ public Type addJumpIfBytesAtOffsetNotEqual(int offset, @NonNull byte[] bytes, short tgt)
+ throws IllegalInstructionException {
+ return addJumpIfBytesAtOffsetEqualsNoneOf(offset, List.of(bytes), tgt);
+ }
+
/**
* Appends a conditional jump instruction to the program: Jumps to {@code tgt} if the UDP
* payload's DNS questions contain the QNAMEs specified in {@code qnames} and qtype
diff --git a/src/android/net/apf/ApfV6Generator.java b/src/android/net/apf/ApfV6Generator.java
index 045423b..07bd191 100644
--- a/src/android/net/apf/ApfV6Generator.java
+++ b/src/android/net/apf/ApfV6Generator.java
@@ -262,6 +262,18 @@
}
@Override
+ public ApfV6Generator addJumpIfBytesAtOffsetEqualsAnyOf(int offset, List<byte[]> bytesList,
+ short tgt) throws IllegalInstructionException {
+ return addLoadImmediate(R0, offset).addJumpIfBytesAtR0EqualsAnyOf(bytesList, tgt);
+ }
+
+ @Override
+ public ApfV6Generator addJumpIfBytesAtOffsetEqualsNoneOf(int offset, List<byte[]> bytesList,
+ short tgt) throws IllegalInstructionException {
+ return addLoadImmediate(R0, offset).addJumpIfBytesAtR0EqualsNoneOf(bytesList, tgt);
+ }
+
+ @Override
public ApfV6Generator addCountAndDropIfR0IsNoneOf(@NonNull Set<Long> values,
ApfCounterTracker.Counter cnt) throws IllegalInstructionException {
if (values.isEmpty()) {
diff --git a/src/android/net/apf/ApfV6GeneratorBase.java b/src/android/net/apf/ApfV6GeneratorBase.java
index 68a29c5..90f0a28 100644
--- a/src/android/net/apf/ApfV6GeneratorBase.java
+++ b/src/android/net/apf/ApfV6GeneratorBase.java
@@ -552,6 +552,21 @@
return addJumpIfBytesAtR0EqualsHelper(bytesList, tgt, false /* jumpOnMatch */);
}
+ /**
+ * Add an instruction to the end of the program to jump to {@code tgt} if the bytes of the
+ * packet at an offset specified by {@code offset} match any of the elements in
+ * {@code bytesSet}.
+ */
+ public abstract Type addJumpIfBytesAtOffsetEqualsAnyOf(int offset,
+ @NonNull List<byte[]> bytesList, short tgt) throws IllegalInstructionException;
+
+ /**
+ * Add an instruction to the end of the program to jump to {@code tgt} if the bytes of the
+ * packet at an offset specified by {@code offset} match none of the elements in
+ * {@code bytesSet}.
+ */
+ public abstract Type addJumpIfBytesAtOffsetEqualsNoneOf(int offset,
+ @NonNull List<byte[]> bytesList, short tgt) throws IllegalInstructionException;
/**
* Check if the byte is valid dns character: A-Z,0-9,-,_,%,@
diff --git a/tests/unit/src/android/net/apf/ApfFilterTest.kt b/tests/unit/src/android/net/apf/ApfFilterTest.kt
index 2d20ed6..6fcf641 100644
--- a/tests/unit/src/android/net/apf/ApfFilterTest.kt
+++ b/tests/unit/src/android/net/apf/ApfFilterTest.kt
@@ -22,6 +22,8 @@
import android.net.MacAddress
import android.net.NattKeepalivePacketDataParcelable
import android.net.TcpKeepalivePacketDataParcelable
+import android.net.apf.ApfConstants.ETH_MULTICAST_MDNS_V4_MAC_ADDRESS
+import android.net.apf.ApfConstants.ETH_MULTICAST_MDNS_V6_MAC_ADDRESS
import android.net.apf.ApfCounterTracker.Counter.DROPPED_ARP_NON_IPV4
import android.net.apf.ApfCounterTracker.Counter.DROPPED_ARP_OTHER_HOST
import android.net.apf.ApfCounterTracker.Counter.DROPPED_ARP_REPLY_SPA_NO_HOST
@@ -240,6 +242,8 @@
intArrayOf(0x33, 0x33, 0xff, 0x55, 0x66, 0x77).map { it.toByte() }.toByteArray(),
// 33:33:ff:bb:cc:dd
intArrayOf(0x33, 0x33, 0xff, 0xbb, 0xcc, 0xdd).map { it.toByte() }.toByteArray(),
+ ETH_MULTICAST_MDNS_V4_MAC_ADDRESS,
+ ETH_MULTICAST_MDNS_V6_MAC_ADDRESS
)
// Using scapy to generate payload:
@@ -5797,7 +5801,8 @@
)
assertThat(program.size).isLessThan(apfRamSize + 1)
assertThat(program).isNotEqualTo(ByteArray(apfRamSize) { 0 })
- val step = Random.nextInt(1, 16)
+ // TODO: reduce after fixing 'Failed to receive adb shell test output within 66000 ms'
+ val step = Random.nextInt(1, 64)
apfRamSize += step
}
}
@@ -5817,7 +5822,8 @@
val availableRam = apfRamSize - ApfCounterTracker.Counter.totalSize()
assertThat(program.size).isLessThan(availableRam + 1)
assertThat(program).isNotEqualTo(ByteArray(availableRam) { 0 })
- val step = Random.nextInt(1, 16)
+ // TODO: reduce after fixing 'Failed to receive adb shell test output within 66000 ms'
+ val step = Random.nextInt(1, 64)
apfRamSize += step
}
}
@@ -5836,7 +5842,8 @@
val availableRam = apfRamSize - ApfCounterTracker.Counter.totalSize()
assertThat(program.size).isLessThan(availableRam + 1)
assertThat(program).isNotEqualTo(ByteArray(availableRam) { 0 })
- val step = Random.nextInt(1, 16)
+ // TODO: reduce after fixing 'Failed to receive adb shell test output within 66000 ms'
+ val step = Random.nextInt(1, 64)
apfRamSize += step
}
}