summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/android/net/apf/ApfCounterTracker.java5
-rw-r--r--src/android/net/apf/ApfFilter.java58
-rw-r--r--src/android/net/apf/ApfV4Generator.java (renamed from src/android/net/apf/ApfGenerator.java)302
-rw-r--r--src/android/net/apf/DnsUtils.java24
-rw-r--r--src/android/net/apf/JumpTable.java14
-rw-r--r--src/android/net/apf/LegacyApfFilter.java67
-rw-r--r--src/android/net/dhcp/DhcpClient.java38
-rw-r--r--src/android/net/dhcp6/Dhcp6Packet.java8
-rw-r--r--src/android/net/ip/IpClient.java56
-rw-r--r--src/android/net/ip/IpClientLinkObserver.java23
-rw-r--r--src/android/net/ip/IpReachabilityMonitor.java42
-rwxr-xr-xsrc/com/android/networkstack/util/NetworkStackUtils.java35
-rwxr-xr-xsrc/com/android/server/connectivity/NetworkMonitor.java403
-rw-r--r--tests/integration/AndroidManifest_root.xml4
-rw-r--r--tests/integration/common/android/net/ip/IpClientIntegrationTestCommon.java689
-rw-r--r--tests/integration/root/android/net/ip/IpClientRootTest.kt53
-rw-r--r--tests/integration/signature/android/net/NetworkStatsIntegrationTest.kt10
-rw-r--r--tests/integration/signature/android/net/ip/IpClientSignatureTest.kt19
-rw-r--r--tests/integration/signature/android/net/util/NetworkStackUtilsIntegrationTest.kt12
-rw-r--r--tests/unit/jni/apf_jni.cpp10
-rw-r--r--tests/unit/src/android/net/apf/ApfTest.java786
-rw-r--r--tests/unit/src/android/net/apf/ApfTestUtils.java28
-rw-r--r--tests/unit/src/android/net/apf/ApfV5Test.kt218
-rw-r--r--tests/unit/src/android/net/apf/Bpf2Apf.java11
-rw-r--r--tests/unit/src/android/net/apf/JumpTableTest.kt6
-rw-r--r--tests/unit/src/android/net/dhcp6/Dhcp6PacketTest.kt15
-rw-r--r--tests/unit/src/android/net/ip/IpReachabilityMonitorTest.kt122
-rw-r--r--tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java411
28 files changed, 2606 insertions, 863 deletions
diff --git a/src/android/net/apf/ApfCounterTracker.java b/src/android/net/apf/ApfCounterTracker.java
index 05276661..b02efa07 100644
--- a/src/android/net/apf/ApfCounterTracker.java
+++ b/src/android/net/apf/ApfCounterTracker.java
@@ -40,7 +40,10 @@ public class ApfCounterTracker {
@VisibleForTesting
public enum Counter {
RESERVED_OOB, // Points to offset 0 from the end of the buffer (out-of-bounds)
- TOTAL_PACKETS,
+ ENDIANNESS, // APFv6 interpreter stores 0x12345678 here
+ TOTAL_PACKETS, // hardcoded in APFv6 interpreter
+ PASSED_ALLOCATE_FAILURE, // hardcoded in APFv6 interpreter
+ PASSED_TRANSMIT_FAILURE, // hardcoded in APFv6 interpreter
PASSED_ARP,
PASSED_DHCP,
PASSED_IPV4,
diff --git a/src/android/net/apf/ApfFilter.java b/src/android/net/apf/ApfFilter.java
index ee2990b8..9a3d58b7 100644
--- a/src/android/net/apf/ApfFilter.java
+++ b/src/android/net/apf/ApfFilter.java
@@ -46,8 +46,8 @@ import android.net.LinkProperties;
import android.net.NattKeepalivePacketDataParcelable;
import android.net.TcpKeepalivePacketDataParcelable;
import android.net.apf.ApfCounterTracker.Counter;
-import android.net.apf.ApfGenerator.IllegalInstructionException;
-import android.net.apf.ApfGenerator.Register;
+import android.net.apf.ApfV4Generator.IllegalInstructionException;
+import android.net.apf.ApfV4Generator.Register;
import android.net.ip.IpClient.IpClientCallbacksWrapper;
import android.os.PowerManager;
import android.os.SystemClock;
@@ -139,7 +139,7 @@ public class ApfFilter implements AndroidPacketFilter {
/**
* When APFv4 is supported, loads R1 with the offset of the specified counter.
*/
- private void maybeSetupCounter(ApfGenerator gen, Counter c) {
+ private void maybeSetupCounter(ApfV4Generator gen, Counter c) {
if (mApfCapabilities.hasDataAccess()) {
gen.addLoadImmediate(Register.R1, c.offset());
}
@@ -413,8 +413,8 @@ public class ApfFilter implements AndroidPacketFilter {
} else {
// APFv4 unsupported: turn jumps to the counter trampolines to immediately PASS or DROP,
// preserving the original pre-APFv4 behavior.
- mCountAndPassLabel = ApfGenerator.PASS_LABEL;
- mCountAndDropLabel = ApfGenerator.DROP_LABEL;
+ mCountAndPassLabel = ApfV4Generator.PASS_LABEL;
+ mCountAndDropLabel = ApfV4Generator.DROP_LABEL;
}
// Now fill the black list from the passed array
@@ -490,7 +490,6 @@ public class ApfFilter implements AndroidPacketFilter {
return mUniqueCounter++;
}
- @GuardedBy("this")
private static int[] filterEthTypeBlackList(int[] ethTypeBlackList) {
ArrayList<Integer> bl = new ArrayList<Integer>();
@@ -1135,7 +1134,7 @@ public class ApfFilter implements AndroidPacketFilter {
// Append a filter for this RA to {@code gen}. Jump to DROP_LABEL if it should be dropped.
// Jump to the next filter if packet doesn't match this RA.
@GuardedBy("ApfFilter.this")
- void generateFilterLocked(ApfGenerator gen, int timeSeconds)
+ void generateFilterLocked(ApfV4Generator gen, int timeSeconds)
throws IllegalInstructionException {
String nextFilterLabel = "Ra" + getUniqueNumberLocked();
// Skip if packet is not the right size
@@ -1236,7 +1235,7 @@ public class ApfFilter implements AndroidPacketFilter {
// Append a filter for this keepalive ack to {@code gen}.
// Jump to drop if it matches the keepalive ack.
// Jump to the next filter if packet doesn't match the keepalive ack.
- abstract void generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException;
+ abstract void generateFilterLocked(ApfV4Generator gen) throws IllegalInstructionException;
}
// A class to hold NAT-T keepalive ack information.
@@ -1279,7 +1278,8 @@ public class ApfFilter implements AndroidPacketFilter {
}
@Override
- void generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException {
+ @GuardedBy("ApfFilter.this")
+ void generateFilterLocked(ApfV4Generator gen) throws IllegalInstructionException {
final String nextFilterLabel = "natt_keepalive_filter" + getUniqueNumberLocked();
gen.addLoadImmediate(Register.R0, ETH_HEADER_LEN + IPV4_SRC_ADDR_OFFSET);
@@ -1381,7 +1381,7 @@ public class ApfFilter implements AndroidPacketFilter {
// Append a filter for this keepalive ack to {@code gen}.
// Jump to drop if it matches the keepalive ack.
// Jump to the next filter if packet doesn't match the keepalive ack.
- abstract void generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException;
+ abstract void generateFilterLocked(ApfV4Generator gen) throws IllegalInstructionException;
}
private class TcpKeepaliveAckV4 extends TcpKeepaliveAck {
@@ -1394,7 +1394,8 @@ public class ApfFilter implements AndroidPacketFilter {
}
@Override
- void generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException {
+ @GuardedBy("ApfFilter.this")
+ void generateFilterLocked(ApfV4Generator gen) throws IllegalInstructionException {
final String nextFilterLabel = "keepalive_ack" + getUniqueNumberLocked();
gen.addLoadImmediate(Register.R0, ETH_HEADER_LEN + IPV4_SRC_ADDR_OFFSET);
@@ -1437,7 +1438,7 @@ public class ApfFilter implements AndroidPacketFilter {
}
@Override
- void generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException {
+ void generateFilterLocked(ApfV4Generator gen) throws IllegalInstructionException {
throw new UnsupportedOperationException("IPv6 TCP Keepalive is not supported yet");
}
}
@@ -1498,7 +1499,7 @@ public class ApfFilter implements AndroidPacketFilter {
* - Packet being filtered is ARP
*/
@GuardedBy("this")
- private void generateArpFilterLocked(ApfGenerator gen) throws IllegalInstructionException {
+ private void generateArpFilterLocked(ApfV4Generator gen) throws IllegalInstructionException {
// Here's a basic summary of what the ARP filter program does:
//
// if not ARP IPv4
@@ -1566,7 +1567,7 @@ public class ApfFilter implements AndroidPacketFilter {
* - Packet being filtered is IPv4
*/
@GuardedBy("this")
- private void generateIPv4FilterLocked(ApfGenerator gen) throws IllegalInstructionException {
+ private void generateIPv4FilterLocked(ApfV4Generator gen) throws IllegalInstructionException {
// Here's a basic summary of what the IPv4 filter program does:
//
// if filtering multicast (i.e. multicast lock not held):
@@ -1649,7 +1650,8 @@ public class ApfFilter implements AndroidPacketFilter {
gen.addJump(mCountAndPassLabel);
}
- private void generateKeepaliveFilters(ApfGenerator gen, Class<?> filterType, int proto,
+ @GuardedBy("this")
+ private void generateKeepaliveFilters(ApfV4Generator gen, Class<?> filterType, int proto,
int offset, String label) throws IllegalInstructionException {
final boolean haveKeepaliveResponses = CollectionUtils.any(mKeepalivePackets,
ack -> filterType.isInstance(ack));
@@ -1670,12 +1672,14 @@ public class ApfFilter implements AndroidPacketFilter {
gen.defineLabel(label);
}
- private void generateV4KeepaliveFilters(ApfGenerator gen) throws IllegalInstructionException {
+ @GuardedBy("this")
+ private void generateV4KeepaliveFilters(ApfV4Generator gen) throws IllegalInstructionException {
generateKeepaliveFilters(gen, TcpKeepaliveAckV4.class, IPPROTO_TCP, IPV4_PROTOCOL_OFFSET,
"skip_v4_keepalive_filter");
}
- private void generateV4NattKeepaliveFilters(ApfGenerator gen)
+ @GuardedBy("this")
+ private void generateV4NattKeepaliveFilters(ApfV4Generator gen)
throws IllegalInstructionException {
generateKeepaliveFilters(gen, NattKeepaliveResponse.class,
IPPROTO_UDP, IPV4_PROTOCOL_OFFSET, "skip_v4_nattkeepalive_filter");
@@ -1688,7 +1692,7 @@ public class ApfFilter implements AndroidPacketFilter {
* - Packet being filtered is IPv6
*/
@GuardedBy("this")
- private void generateIPv6FilterLocked(ApfGenerator gen) throws IllegalInstructionException {
+ private void generateIPv6FilterLocked(ApfV4Generator gen) throws IllegalInstructionException {
// Here's a basic summary of what the IPv6 filter program does:
//
// if there is a hop-by-hop option present (e.g. MLD query)
@@ -1788,7 +1792,7 @@ public class ApfFilter implements AndroidPacketFilter {
* or PASS_LABEL if the packet is mDNS packets. Otherwise, skip this check.
*/
@GuardedBy("this")
- private void generateMdnsFilterLocked(ApfGenerator gen)
+ private void generateMdnsFilterLocked(ApfV4Generator gen)
throws IllegalInstructionException {
final String skipMdnsv4Filter = "skip_mdns_v4_filter";
final String skipMdnsFilter = "skip_mdns_filter";
@@ -1900,7 +1904,7 @@ public class ApfFilter implements AndroidPacketFilter {
* R0/R1 have nothing useful in them, and can be clobbered.
*/
@GuardedBy("this")
- private void generateV4TcpPort7FilterLocked(ApfGenerator gen)
+ private void generateV4TcpPort7FilterLocked(ApfV4Generator gen)
throws IllegalInstructionException {
final String skipPort7V4Filter = "skip_port7_v4_filter";
@@ -1925,7 +1929,8 @@ public class ApfFilter implements AndroidPacketFilter {
gen.defineLabel(skipPort7V4Filter);
}
- private void generateV6KeepaliveFilters(ApfGenerator gen) throws IllegalInstructionException {
+ @GuardedBy("this")
+ private void generateV6KeepaliveFilters(ApfV4Generator gen) throws IllegalInstructionException {
generateKeepaliveFilters(gen, TcpKeepaliveAckV6.class, IPPROTO_TCP, IPV6_NEXT_HEADER_OFFSET,
"skip_v6_keepalive_filter");
}
@@ -1952,9 +1957,9 @@ public class ApfFilter implements AndroidPacketFilter {
*/
@GuardedBy("this")
@VisibleForTesting
- protected ApfGenerator emitPrologueLocked() throws IllegalInstructionException {
+ protected ApfV4Generator emitPrologueLocked() throws IllegalInstructionException {
// This is guaranteed to succeed because of the check in maybeCreate.
- ApfGenerator gen = new ApfGenerator(mApfCapabilities.apfVersionSupported);
+ ApfV4Generator gen = new ApfV4Generator(mApfCapabilities.apfVersionSupported);
if (mApfCapabilities.hasDataAccess()) {
// Increment TOTAL_PACKETS
@@ -2037,7 +2042,7 @@ public class ApfFilter implements AndroidPacketFilter {
* before jumping to the actual PASS and DROP labels.
*/
@GuardedBy("this")
- private void emitEpilogue(ApfGenerator gen) throws IllegalInstructionException {
+ private void emitEpilogue(ApfV4Generator gen) throws IllegalInstructionException {
// If APFv4 is unsupported, no epilogue is necessary: if execution reached this far, it
// will just fall-through to the PASS label.
if (!mApfCapabilities.hasDataAccess()) return;
@@ -2067,6 +2072,7 @@ public class ApfFilter implements AndroidPacketFilter {
* Generate and install a new filter program.
*/
@GuardedBy("this")
+ @SuppressWarnings("GuardedBy") // errorprone false positive on ra#generateFilterLocked
@VisibleForTesting
public void installNewProgramLocked() {
ArrayList<Ra> rasToFilter = new ArrayList<>();
@@ -2082,7 +2088,7 @@ public class ApfFilter implements AndroidPacketFilter {
int timeSeconds = secondsSinceBoot();
try {
// Step 1: Determine how many RA filters we can fit in the program.
- ApfGenerator gen = emitPrologueLocked();
+ ApfV4Generator gen = emitPrologueLocked();
// The epilogue normally goes after the RA filters, but add it early to include its
// length when estimating the total.
@@ -2251,7 +2257,7 @@ public class ApfFilter implements AndroidPacketFilter {
// For now only support generating programs for Ethernet frames. If this restriction is
// lifted the program generator will need its offsets adjusted.
if (apfCapabilities.apfPacketFormat != ARPHRD_ETHER) return null;
- if (!ApfGenerator.supportsVersion(apfCapabilities.apfVersionSupported)) {
+ if (!ApfV4Generator.supportsVersion(apfCapabilities.apfVersionSupported)) {
Log.e(TAG, "Unsupported APF version: " + apfCapabilities.apfVersionSupported);
return null;
}
diff --git a/src/android/net/apf/ApfGenerator.java b/src/android/net/apf/ApfV4Generator.java
index c6ff4416..a7986e61 100644
--- a/src/android/net/apf/ApfGenerator.java
+++ b/src/android/net/apf/ApfV4Generator.java
@@ -16,12 +16,13 @@
package android.net.apf;
-import static android.net.apf.ApfGenerator.Register.R0;
-import static android.net.apf.ApfGenerator.Register.R1;
+import static android.net.apf.ApfV4Generator.Register.R0;
+import static android.net.apf.ApfV4Generator.Register.R1;
import androidx.annotation.NonNull;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.net.module.util.HexDump;
import java.util.ArrayList;
import java.util.HashMap;
@@ -31,11 +32,11 @@ import java.util.List;
* APF assembler/generator. A tool for generating an APF program.
*
* Call add*() functions to add instructions to the program, then call
- * {@link ApfGenerator#generate} to get the APF bytecode for the program.
+ * {@link ApfV4Generator#generate} to get the APF bytecode for the program.
*
* @hide
*/
-public class ApfGenerator {
+public class ApfV4Generator {
/**
* This exception is thrown when an attempt is made to generate an illegal instruction.
*/
@@ -53,8 +54,7 @@ public class ApfGenerator {
// It is a U32 big-endian value and is always incremented by 1.
// This is more or less equivalent to: lddw R0, -N4; add R0,1; stdw R0, -N4; {pass,drop}
// e.g. "pass", "pass 1", "drop", "drop 1"
- PASS(0),
- DROP(0),
+ PASSDROP(0),
LDB(1), // Load 1 byte from immediate offset, e.g. "ldb R0, [5]"
LDH(2), // Load 2 bytes from immediate offset, e.g. "ldh R0, [5]"
LDW(3), // Load 4 bytes from immediate offset, e.g. "ldw R0, [5]"
@@ -117,10 +117,9 @@ public class ApfGenerator {
ALLOCATE(36),
// Transmit and deallocate the buffer (transmission can be delayed until the program
// terminates). R=0 means discard the buffer, R=1 means transmit the buffer.
- // "e.g. trans"
+ // "e.g. transmit"
// "e.g. discard"
- TRANSMIT(37),
- DISCARD(37),
+ TRANSMITDISCARD(37),
// Write 1, 2 or 4 byte value from register to the output buffer and auto-increment the
// output buffer pointer.
// e.g. "ewrite1 r0"
@@ -134,7 +133,27 @@ public class ApfGenerator {
// when R=1, the copy length is stored in R1.
// e.g. "pktcopy r0, 5", "pktcopy r0, r1", "datacopy r0, 5", "datacopy r0, r1"
EPKTCOPY(41),
- EDATACOPY(42);
+ EDATACOPY(42),
+ // Jumps if the UDP payload content (starting at R0) does not contain one
+ // of the specified QNAMEs, applying case insensitivity.
+ // R0: Offset to UDP payload content
+ // R=0/1 meaning 'does not match'/'matches'
+ // imm1: Opcode
+ // imm2: Label offset
+ // imm3(u8): Question type (PTR/SRV/TXT/A/AAAA)
+ // imm4(bytes): TLV-encoded QNAME list (null-terminated)
+ // e.g.: "jdnsqmatch R0,label,0x0c,\002aa\005local\0\0"
+ JDNSQMATCH(43),
+ // Jumps if the UDP payload content (starting at R0) does not contain one
+ // of the specified NAMEs in answers/authority/additional records, applying
+ // case insensitivity.
+ // R=0/1 meaning 'does not match'/'matches'
+ // R0: Offset to UDP payload content
+ // imm1: Opcode
+ // imm2: Label offset
+ // imm3(bytes): TLV-encoded QNAME list (null-terminated)
+ // e.g.: "jdnsamatch R0,label,0x0c,\002aa\005local\0\0"
+ JDNSAMATCH(44);
final int value;
@@ -493,12 +512,19 @@ public class ApfGenerator {
int writingOffset = offset;
bytecode[writingOffset++] = generateInstructionByte();
int indeterminateSize = calculateRequiredIndeterminateSize();
+ int startOffset = 0;
+ if (mOpcode == Opcodes.EXT.value) {
+ // For extend opcode, always write the actual opcode first.
+ writingOffset = mIntImms.get(startOffset++).writeValue(bytecode, writingOffset,
+ indeterminateSize);
+ }
if (mTargetLabel != null) {
writingOffset = writeValue(calculateTargetLabelOffset(), bytecode, writingOffset,
indeterminateSize);
}
- for (IntImmediate imm : mIntImms) {
- writingOffset = imm.writeValue(bytecode, writingOffset, indeterminateSize);
+ for (int i = startOffset; i < mIntImms.size(); ++i) {
+ writingOffset = mIntImms.get(i).writeValue(bytecode, writingOffset,
+ indeterminateSize);
}
if (mBytesImm != null) {
System.arraycopy(mBytesImm, 0, bytecode, writingOffset, mBytesImm.length);
@@ -615,7 +641,7 @@ public class ApfGenerator {
* the requested version is unsupported.
*/
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
- public ApfGenerator(int version) throws IllegalInstructionException {
+ public ApfV4Generator(int version) throws IllegalInstructionException {
mVersion = version;
requireApfVersion(MIN_APF_VERSION);
}
@@ -633,7 +659,7 @@ public class ApfGenerator {
}
}
- private ApfGenerator append(Instruction instruction) {
+ private ApfV4Generator append(Instruction instruction) {
if (mGenerated) {
throw new IllegalStateException("Program already generated");
}
@@ -645,7 +671,7 @@ public class ApfGenerator {
* Define a label at the current end of the program. Jumps can jump to this label. Labels are
* their own separate instructions, though with size 0. This facilitates having labels with
* no corresponding code to execute, for example a label at the end of a program. For example
- * an {@link ApfGenerator} might be passed to a function that adds a filter like so:
+ * an {@link ApfV4Generator} might be passed to a function that adds a filter like so:
* <pre>
* load from packet
* compare loaded data, jump if not equal to "next_filter"
@@ -656,14 +682,14 @@ public class ApfGenerator {
* </pre>
* In this case "next_filter" may not have any generated code associated with it.
*/
- public ApfGenerator defineLabel(String name) throws IllegalInstructionException {
+ public ApfV4Generator defineLabel(String name) throws IllegalInstructionException {
return append(new Instruction(Opcodes.LABEL).setLabel(name));
}
/**
* Add an unconditional jump instruction to the end of the program.
*/
- public ApfGenerator addJump(String target) {
+ public ApfV4Generator addJump(String target) {
return append(new Instruction(Opcodes.JMP).setTargetLabel(target));
}
@@ -671,7 +697,7 @@ public class ApfGenerator {
* Add an instruction to the end of the program to load the byte at offset {@code offset}
* bytes from the beginning of the packet into {@code register}.
*/
- public ApfGenerator addLoad8(Register r, int ofs) {
+ public ApfV4Generator addLoad8(Register r, int ofs) {
return append(new Instruction(Opcodes.LDB, r).addUnsigned(ofs));
}
@@ -679,7 +705,7 @@ public class ApfGenerator {
* Add an instruction to the end of the program to load 16-bits at offset {@code offset}
* bytes from the beginning of the packet into {@code register}.
*/
- public ApfGenerator addLoad16(Register r, int ofs) {
+ public ApfV4Generator addLoad16(Register r, int ofs) {
return append(new Instruction(Opcodes.LDH, r).addUnsigned(ofs));
}
@@ -687,7 +713,7 @@ public class ApfGenerator {
* Add an instruction to the end of the program to load 32-bits at offset {@code offset}
* bytes from the beginning of the packet into {@code register}.
*/
- public ApfGenerator addLoad32(Register r, int ofs) {
+ public ApfV4Generator addLoad32(Register r, int ofs) {
return append(new Instruction(Opcodes.LDW, r).addUnsigned(ofs));
}
@@ -696,7 +722,7 @@ public class ApfGenerator {
* {@code register}. The offset of the loaded byte from the beginning of the packet is
* the sum of {@code offset} and the value in register R1.
*/
- public ApfGenerator addLoad8Indexed(Register r, int ofs) {
+ public ApfV4Generator addLoad8Indexed(Register r, int ofs) {
return append(new Instruction(Opcodes.LDBX, r).addUnsigned(ofs));
}
@@ -705,7 +731,7 @@ public class ApfGenerator {
* {@code register}. The offset of the loaded 16-bits from the beginning of the packet is
* the sum of {@code offset} and the value in register R1.
*/
- public ApfGenerator addLoad16Indexed(Register r, int ofs) {
+ public ApfV4Generator addLoad16Indexed(Register r, int ofs) {
return append(new Instruction(Opcodes.LDHX, r).addUnsigned(ofs));
}
@@ -714,42 +740,42 @@ public class ApfGenerator {
* {@code register}. The offset of the loaded 32-bits from the beginning of the packet is
* the sum of {@code offset} and the value in register R1.
*/
- public ApfGenerator addLoad32Indexed(Register r, int ofs) {
+ public ApfV4Generator addLoad32Indexed(Register r, int ofs) {
return append(new Instruction(Opcodes.LDWX, r).addUnsigned(ofs));
}
/**
* Add an instruction to the end of the program to add {@code value} to register R0.
*/
- public ApfGenerator addAdd(int val) {
+ public ApfV4Generator addAdd(int val) {
return append(new Instruction(Opcodes.ADD).addTwosCompUnsigned(val));
}
/**
* Add an instruction to the end of the program to multiply register R0 by {@code value}.
*/
- public ApfGenerator addMul(int val) {
+ public ApfV4Generator addMul(int val) {
return append(new Instruction(Opcodes.MUL).addUnsigned(val));
}
/**
* Add an instruction to the end of the program to divide register R0 by {@code value}.
*/
- public ApfGenerator addDiv(int val) {
+ public ApfV4Generator addDiv(int val) {
return append(new Instruction(Opcodes.DIV).addUnsigned(val));
}
/**
* Add an instruction to the end of the program to logically and register R0 with {@code value}.
*/
- public ApfGenerator addAnd(int val) {
+ public ApfV4Generator addAnd(int val) {
return append(new Instruction(Opcodes.AND).addTwosCompUnsigned(val));
}
/**
* Add an instruction to the end of the program to logically or register R0 with {@code value}.
*/
- public ApfGenerator addOr(int val) {
+ public ApfV4Generator addOr(int val) {
return append(new Instruction(Opcodes.OR).addTwosCompUnsigned(val));
}
@@ -757,7 +783,7 @@ public class ApfGenerator {
* Add an instruction to the end of the program to shift left register R0 by {@code value} bits.
*/
// TODO: consider whether should change the argument type to byte
- public ApfGenerator addLeftShift(int val) {
+ public ApfV4Generator addLeftShift(int val) {
return append(new Instruction(Opcodes.SH).addSigned(val));
}
@@ -766,28 +792,28 @@ public class ApfGenerator {
* bits.
*/
// TODO: consider whether should change the argument type to byte
- public ApfGenerator addRightShift(int val) {
+ public ApfV4Generator addRightShift(int val) {
return append(new Instruction(Opcodes.SH).addSigned(-val));
}
/**
* Add an instruction to the end of the program to add register R1 to register R0.
*/
- public ApfGenerator addAddR1() {
+ public ApfV4Generator addAddR1() {
return append(new Instruction(Opcodes.ADD, R1));
}
/**
* Add an instruction to the end of the program to multiply register R0 by register R1.
*/
- public ApfGenerator addMulR1() {
+ public ApfV4Generator addMulR1() {
return append(new Instruction(Opcodes.MUL, R1));
}
/**
* Add an instruction to the end of the program to divide register R0 by register R1.
*/
- public ApfGenerator addDivR1() {
+ public ApfV4Generator addDivR1() {
return append(new Instruction(Opcodes.DIV, R1));
}
@@ -795,7 +821,7 @@ public class ApfGenerator {
* Add an instruction to the end of the program to logically and register R0 with register R1
* and store the result back into register R0.
*/
- public ApfGenerator addAndR1() {
+ public ApfV4Generator addAndR1() {
return append(new Instruction(Opcodes.AND, R1));
}
@@ -803,7 +829,7 @@ public class ApfGenerator {
* Add an instruction to the end of the program to logically or register R0 with register R1
* and store the result back into register R0.
*/
- public ApfGenerator addOrR1() {
+ public ApfV4Generator addOrR1() {
return append(new Instruction(Opcodes.OR, R1));
}
@@ -811,14 +837,14 @@ public class ApfGenerator {
* Add an instruction to the end of the program to shift register R0 left by the value in
* register R1.
*/
- public ApfGenerator addLeftShiftR1() {
+ public ApfV4Generator addLeftShiftR1() {
return append(new Instruction(Opcodes.SH, R1));
}
/**
* Add an instruction to the end of the program to move {@code value} into {@code register}.
*/
- public ApfGenerator addLoadImmediate(Register register, int value) {
+ public ApfV4Generator addLoadImmediate(Register register, int value) {
return append(new Instruction(Opcodes.LI, register).addSigned(value));
}
@@ -826,7 +852,7 @@ public class ApfGenerator {
* Add an instruction to the end of the program to jump to {@code target} if register R0's
* value equals {@code value}.
*/
- public ApfGenerator addJumpIfR0Equals(int val, String tgt) {
+ public ApfV4Generator addJumpIfR0Equals(int val, String tgt) {
return append(new Instruction(Opcodes.JEQ).addTwosCompUnsigned(val).setTargetLabel(tgt));
}
@@ -834,7 +860,7 @@ public class ApfGenerator {
* Add an instruction to the end of the program to jump to {@code target} if register R0's
* value does not equal {@code value}.
*/
- public ApfGenerator addJumpIfR0NotEquals(int val, String tgt) {
+ public ApfV4Generator addJumpIfR0NotEquals(int val, String tgt) {
return append(new Instruction(Opcodes.JNE).addTwosCompUnsigned(val).setTargetLabel(tgt));
}
@@ -842,7 +868,7 @@ public class ApfGenerator {
* Add an instruction to the end of the program to jump to {@code target} if register R0's
* value is greater than {@code value}.
*/
- public ApfGenerator addJumpIfR0GreaterThan(int val, String tgt) {
+ public ApfV4Generator addJumpIfR0GreaterThan(int val, String tgt) {
return append(new Instruction(Opcodes.JGT).addUnsigned(val).setTargetLabel(tgt));
}
@@ -850,7 +876,7 @@ public class ApfGenerator {
* Add an instruction to the end of the program to jump to {@code target} if register R0's
* value is less than {@code value}.
*/
- public ApfGenerator addJumpIfR0LessThan(int val, String tgt) {
+ public ApfV4Generator addJumpIfR0LessThan(int val, String tgt) {
return append(new Instruction(Opcodes.JLT).addUnsigned(val).setTargetLabel(tgt));
}
@@ -858,14 +884,14 @@ public class ApfGenerator {
* Add an instruction to the end of the program to jump to {@code target} if register R0's
* value has any bits set that are also set in {@code value}.
*/
- public ApfGenerator addJumpIfR0AnyBitsSet(int val, String tgt) {
+ public ApfV4Generator addJumpIfR0AnyBitsSet(int val, String tgt) {
return append(new Instruction(Opcodes.JSET).addTwosCompUnsigned(val).setTargetLabel(tgt));
}
/**
* Add an instruction to the end of the program to jump to {@code target} if register R0's
* value equals register R1's value.
*/
- public ApfGenerator addJumpIfR0EqualsR1(String tgt) {
+ public ApfV4Generator addJumpIfR0EqualsR1(String tgt) {
return append(new Instruction(Opcodes.JEQ, R1).setTargetLabel(tgt));
}
@@ -873,7 +899,7 @@ public class ApfGenerator {
* Add an instruction to the end of the program to jump to {@code target} if register R0's
* value does not equal register R1's value.
*/
- public ApfGenerator addJumpIfR0NotEqualsR1(String tgt) {
+ public ApfV4Generator addJumpIfR0NotEqualsR1(String tgt) {
return append(new Instruction(Opcodes.JNE, R1).setTargetLabel(tgt));
}
@@ -881,7 +907,7 @@ public class ApfGenerator {
* Add an instruction to the end of the program to jump to {@code target} if register R0's
* value is greater than register R1's value.
*/
- public ApfGenerator addJumpIfR0GreaterThanR1(String tgt) {
+ public ApfV4Generator addJumpIfR0GreaterThanR1(String tgt) {
return append(new Instruction(Opcodes.JGT, R1).setTargetLabel(tgt));
}
@@ -889,7 +915,7 @@ public class ApfGenerator {
* Add an instruction to the end of the program to jump to {@code target} if register R0's
* value is less than register R1's value.
*/
- public ApfGenerator addJumpIfR0LessThanR1(String target) {
+ public ApfV4Generator addJumpIfR0LessThanR1(String target) {
return append(new Instruction(Opcodes.JLT, R1).setTargetLabel(target));
}
@@ -897,24 +923,37 @@ public class ApfGenerator {
* Add an instruction to the end of the program to jump to {@code target} if register R0's
* value has any bits set that are also set in R1's value.
*/
- public ApfGenerator addJumpIfR0AnyBitsSetR1(String tgt) {
+ public ApfV4Generator addJumpIfR0AnyBitsSetR1(String tgt) {
return append(new Instruction(Opcodes.JSET, R1).setTargetLabel(tgt));
}
/**
- * Add an instruction to the end of the program to jump to {@code target} if the bytes of the
+ * 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 register} don't match {@code bytes}
+ * R=0 means check for not equal
*/
- public ApfGenerator addJumpIfBytesAtR0NotEqual(byte[] bytes, String tgt) {
+ public ApfV4Generator addJumpIfBytesAtR0NotEqual(byte[] bytes, String tgt) {
return append(new Instruction(Opcodes.JNEBS).addUnsigned(
bytes.length).setTargetLabel(tgt).setBytesImm(bytes));
}
/**
+ * 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 register} match {@code bytes}
+ * R=1 means check for equal.
+ */
+ public ApfV4Generator addJumpIfBytesAtR0Equal(byte[] bytes, String tgt)
+ throws IllegalInstructionException {
+ requireApfVersion(MIN_APF_VERSION_IN_DEV);
+ return append(new Instruction(Opcodes.JNEBS, R1).addUnsigned(
+ bytes.length).setTargetLabel(tgt).setBytesImm(bytes));
+ }
+
+ /**
* Add an instruction to the end of the program to load memory slot {@code slot} into
* {@code register}.
*/
- public ApfGenerator addLoadFromMemory(Register r, int slot)
+ public ApfV4Generator addLoadFromMemory(Register r, int slot)
throws IllegalInstructionException {
return append(new Instruction(ExtendedOpcodes.LDM, slot, r));
}
@@ -923,7 +962,7 @@ public class ApfGenerator {
* Add an instruction to the end of the program to store {@code register} into memory slot
* {@code slot}.
*/
- public ApfGenerator addStoreToMemory(Register r, int slot)
+ public ApfV4Generator addStoreToMemory(Register r, int slot)
throws IllegalInstructionException {
return append(new Instruction(ExtendedOpcodes.STM, slot, r));
}
@@ -931,21 +970,21 @@ public class ApfGenerator {
/**
* Add an instruction to the end of the program to logically not {@code register}.
*/
- public ApfGenerator addNot(Register r) {
+ public ApfV4Generator addNot(Register r) {
return append(new Instruction(ExtendedOpcodes.NOT, r));
}
/**
* Add an instruction to the end of the program to negate {@code register}.
*/
- public ApfGenerator addNeg(Register r) {
+ public ApfV4Generator addNeg(Register r) {
return append(new Instruction(ExtendedOpcodes.NEG, r));
}
/**
* Add an instruction to swap the values in register R0 and register R1.
*/
- public ApfGenerator addSwap() {
+ public ApfV4Generator addSwap() {
return append(new Instruction(ExtendedOpcodes.SWAP));
}
@@ -953,56 +992,56 @@ public class ApfGenerator {
* Add an instruction to the end of the program to move the value into
* {@code register} from the other register.
*/
- public ApfGenerator addMove(Register r) {
+ public ApfV4Generator addMove(Register r) {
return append(new Instruction(ExtendedOpcodes.MOVE, r));
}
/**
* Add an instruction to the end of the program to let the program immediately return PASS.
*/
- public ApfGenerator addPass() {
+ public ApfV4Generator addPass() {
// PASS requires using R0 because it shares opcode with DROP
- return append(new Instruction(Opcodes.PASS));
+ return append(new Instruction(Opcodes.PASSDROP));
}
/**
* Add an instruction to the end of the program to increment the counter value and
* immediately return PASS.
*/
- public ApfGenerator addCountAndPass(int cnt) throws IllegalInstructionException {
+ public ApfV4Generator addCountAndPass(int cnt) throws IllegalInstructionException {
requireApfVersion(MIN_APF_VERSION_IN_DEV);
checkRange("CounterNumber", cnt /* value */, 1 /* lowerBound */,
1000 /* upperBound */);
// PASS requires using R0 because it shares opcode with DROP
- return append(new Instruction(Opcodes.PASS).addUnsigned(cnt));
+ return append(new Instruction(Opcodes.PASSDROP).addUnsigned(cnt));
}
/**
* Add an instruction to the end of the program to let the program immediately return DROP.
*/
- public ApfGenerator addDrop() throws IllegalInstructionException {
+ public ApfV4Generator addDrop() throws IllegalInstructionException {
requireApfVersion(MIN_APF_VERSION_IN_DEV);
// DROP requires using R1 because it shares opcode with PASS
- return append(new Instruction(Opcodes.DROP, R1));
+ return append(new Instruction(Opcodes.PASSDROP, R1));
}
/**
* Add an instruction to the end of the program to increment the counter value and
* immediately return DROP.
*/
- public ApfGenerator addCountAndDrop(int cnt) throws IllegalInstructionException {
+ public ApfV4Generator addCountAndDrop(int cnt) throws IllegalInstructionException {
requireApfVersion(MIN_APF_VERSION_IN_DEV);
checkRange("CounterNumber", cnt /* value */, 1 /* lowerBound */,
1000 /* upperBound */);
// DROP requires using R1 because it shares opcode with PASS
- return append(new Instruction(Opcodes.DROP, R1).addUnsigned(cnt));
+ return append(new Instruction(Opcodes.PASSDROP, R1).addUnsigned(cnt));
}
/**
* Add an instruction to the end of the program to call the apf_allocate_buffer() function.
* Buffer length to be allocated is stored in register 0.
*/
- public ApfGenerator addAllocateR0() throws IllegalInstructionException {
+ public ApfV4Generator addAllocateR0() throws IllegalInstructionException {
requireApfVersion(MIN_APF_VERSION_IN_DEV);
return append(new Instruction(ExtendedOpcodes.ALLOCATE));
}
@@ -1012,7 +1051,7 @@ public class ApfGenerator {
*
* @param size the buffer length to be allocated.
*/
- public ApfGenerator addAllocate(int size) throws IllegalInstructionException {
+ public ApfV4Generator addAllocate(int size) throws IllegalInstructionException {
requireApfVersion(MIN_APF_VERSION_IN_DEV);
// R1 means the extra be16 immediate is present
return append(new Instruction(ExtendedOpcodes.ALLOCATE, R1).addU16(size));
@@ -1022,7 +1061,7 @@ public class ApfGenerator {
* Add an instruction to the beginning of the program to reserve the data region.
* @param data the actual data byte
*/
- public ApfGenerator addData(byte[] data) throws IllegalInstructionException {
+ public ApfV4Generator addData(byte[] data) throws IllegalInstructionException {
requireApfVersion(MIN_APF_VERSION_IN_DEV);
if (!mInstructions.isEmpty()) {
throw new IllegalInstructionException("data instruction has to come first");
@@ -1033,25 +1072,25 @@ public class ApfGenerator {
/**
* Add an instruction to the end of the program to transmit the allocated buffer.
*/
- public ApfGenerator addTransmit() throws IllegalInstructionException {
+ public ApfV4Generator addTransmit() throws IllegalInstructionException {
requireApfVersion(MIN_APF_VERSION_IN_DEV);
// TRANSMIT requires using R0 because it shares opcode with DISCARD
- return append(new Instruction(ExtendedOpcodes.TRANSMIT));
+ return append(new Instruction(ExtendedOpcodes.TRANSMITDISCARD));
}
/**
* Add an instruction to the end of the program to discard the allocated buffer.
*/
- public ApfGenerator addDiscard() throws IllegalInstructionException {
+ public ApfV4Generator addDiscard() throws IllegalInstructionException {
requireApfVersion(MIN_APF_VERSION_IN_DEV);
// DISCARD requires using R1 because it shares opcode with TRANSMIT
- return append(new Instruction(ExtendedOpcodes.DISCARD, R1));
+ return append(new Instruction(ExtendedOpcodes.TRANSMITDISCARD, R1));
}
/**
* Add an instruction to the end of the program to write 1 byte value to output buffer.
*/
- public ApfGenerator addWriteU8(int val) throws IllegalInstructionException {
+ public ApfV4Generator addWriteU8(int val) throws IllegalInstructionException {
requireApfVersion(MIN_APF_VERSION_IN_DEV);
return append(new Instruction(Opcodes.WRITE).overrideLenField(1).addU8(val));
}
@@ -1059,7 +1098,7 @@ public class ApfGenerator {
/**
* Add an instruction to the end of the program to write 2 bytes value to output buffer.
*/
- public ApfGenerator addWriteU16(int val) throws IllegalInstructionException {
+ public ApfV4Generator addWriteU16(int val) throws IllegalInstructionException {
requireApfVersion(MIN_APF_VERSION_IN_DEV);
return append(new Instruction(Opcodes.WRITE).overrideLenField(2).addU16(val));
}
@@ -1067,7 +1106,7 @@ public class ApfGenerator {
/**
* Add an instruction to the end of the program to write 4 bytes value to output buffer.
*/
- public ApfGenerator addWriteU32(long val) throws IllegalInstructionException {
+ public ApfV4Generator addWriteU32(long val) throws IllegalInstructionException {
requireApfVersion(MIN_APF_VERSION_IN_DEV);
return append(new Instruction(Opcodes.WRITE).overrideLenField(4).addU32(val));
}
@@ -1076,7 +1115,7 @@ public class ApfGenerator {
* Add an instruction to the end of the program to write 1 byte value from register to output
* buffer.
*/
- public ApfGenerator addWriteU8(Register reg) throws IllegalInstructionException {
+ public ApfV4Generator addWriteU8(Register reg) throws IllegalInstructionException {
requireApfVersion(MIN_APF_VERSION_IN_DEV);
return append(new Instruction(ExtendedOpcodes.EWRITE1, reg));
}
@@ -1085,7 +1124,7 @@ public class ApfGenerator {
* Add an instruction to the end of the program to write 2 byte value from register to output
* buffer.
*/
- public ApfGenerator addWriteU16(Register reg) throws IllegalInstructionException {
+ public ApfV4Generator addWriteU16(Register reg) throws IllegalInstructionException {
requireApfVersion(MIN_APF_VERSION_IN_DEV);
return append(new Instruction(ExtendedOpcodes.EWRITE2, reg));
}
@@ -1094,7 +1133,7 @@ public class ApfGenerator {
* Add an instruction to the end of the program to write 4 byte value from register to output
* buffer.
*/
- public ApfGenerator addWriteU32(Register reg) throws IllegalInstructionException {
+ public ApfV4Generator addWriteU32(Register reg) throws IllegalInstructionException {
requireApfVersion(MIN_APF_VERSION_IN_DEV);
return append(new Instruction(ExtendedOpcodes.EWRITE4, reg));
}
@@ -1108,7 +1147,7 @@ public class ApfGenerator {
* one time.
* @return the ApfGenerator object
*/
- public ApfGenerator addDataCopy(int src, int len)
+ public ApfV4Generator addDataCopy(int src, int len)
throws IllegalInstructionException {
requireApfVersion(MIN_APF_VERSION_IN_DEV);
return append(new Instruction(Opcodes.PKTDATACOPY, R1).addUnsigned(src).addU8(len));
@@ -1123,7 +1162,7 @@ public class ApfGenerator {
* one time.
* @return the ApfGenerator object
*/
- public ApfGenerator addPacketCopy(int src, int len)
+ public ApfV4Generator addPacketCopy(int src, int len)
throws IllegalInstructionException {
requireApfVersion(MIN_APF_VERSION_IN_DEV);
return append(new Instruction(Opcodes.PKTDATACOPY, R0).addUnsigned(src).addU8(len));
@@ -1137,7 +1176,7 @@ public class ApfGenerator {
* @param len the number of bytes to be copied, only <= 255 bytes can be copied at once.
* @return the ApfGenerator object
*/
- public ApfGenerator addDataCopyFromR0(int len) throws IllegalInstructionException {
+ public ApfV4Generator addDataCopyFromR0(int len) throws IllegalInstructionException {
requireApfVersion(MIN_APF_VERSION_IN_DEV);
return append(new Instruction(ExtendedOpcodes.EDATACOPY).addU8(len));
}
@@ -1150,7 +1189,7 @@ public class ApfGenerator {
* @param len the number of bytes to be copied, only <= 255 bytes can be copied at once.
* @return the ApfGenerator object
*/
- public ApfGenerator addPacketCopyFromR0(int len) throws IllegalInstructionException {
+ public ApfV4Generator addPacketCopyFromR0(int len) throws IllegalInstructionException {
requireApfVersion(MIN_APF_VERSION_IN_DEV);
return append(new Instruction(ExtendedOpcodes.EPKTCOPY).addU8(len));
}
@@ -1163,7 +1202,7 @@ public class ApfGenerator {
*
* @return the ApfGenerator object
*/
- public ApfGenerator addDataCopyFromR0LenR1() throws IllegalInstructionException {
+ public ApfV4Generator addDataCopyFromR0LenR1() throws IllegalInstructionException {
requireApfVersion(MIN_APF_VERSION_IN_DEV);
return append(new Instruction(ExtendedOpcodes.EDATACOPY, R1));
}
@@ -1176,11 +1215,106 @@ public class ApfGenerator {
*
* @return the ApfGenerator object
*/
- public ApfGenerator addPacketCopyFromR0LenR1() throws IllegalInstructionException {
+ public ApfV4Generator addPacketCopyFromR0LenR1() throws IllegalInstructionException {
requireApfVersion(MIN_APF_VERSION_IN_DEV);
return append(new Instruction(ExtendedOpcodes.EPKTCOPY, R1));
}
+ /**
+ * Check if the byte is valid dns character: A-Z,0-9,-,_
+ */
+ private static boolean isValidDnsCharacter(byte c) {
+ return (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-' || c == '_';
+ }
+
+ private static void validateNames(@NonNull byte[] names) {
+ final int len = names.length;
+ if (len < 4) {
+ throw new IllegalArgumentException("qnames must have at least length 4");
+ }
+ final String errorMessage = "qname: " + HexDump.toHexString(names)
+ + "is not null-terminated list of TLV-encoded names";
+ int i = 0;
+ while (i < len - 1) {
+ int label_len = names[i++];
+ if (label_len < 1 || label_len > 63) {
+ throw new IllegalArgumentException(
+ "label len: " + label_len + " must be between 1 and 63");
+ }
+ if (i + label_len >= len - 1) {
+ throw new IllegalArgumentException(errorMessage);
+ }
+ while (label_len-- > 0) {
+ if (!isValidDnsCharacter(names[i++])) {
+ throw new IllegalArgumentException("qname: " + HexDump.toHexString(names)
+ + " contains invalid character");
+ }
+ }
+ if (names[i] == 0) {
+ i++; // skip null terminator.
+ }
+ }
+ if (names[len - 1] != 0) {
+ throw new IllegalArgumentException(errorMessage);
+ }
+ }
+
+ /**
+ * Appends a conditional jump instruction to the program: Jumps to {@code tgt} if the UDP
+ * payload's DNS questions do NOT contain the QNAMEs specified in {@code qnames} and qtype
+ * equals {@code qtype}. Examines the payload starting at the offset in R0.
+ * R = 0 means check for "does not contain".
+ */
+ public ApfV4Generator addJumpIfPktAtR0DoesNotContainDnsQ(@NonNull byte[] qnames, int qtype,
+ @NonNull String tgt) throws IllegalInstructionException {
+ requireApfVersion(MIN_APF_VERSION_IN_DEV);
+ validateNames(qnames);
+ return append(new Instruction(ExtendedOpcodes.JDNSQMATCH).setTargetLabel(tgt).addU8(
+ qtype).setBytesImm(qnames));
+ }
+
+ /**
+ * 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
+ * equals {@code qtype}. Examines the payload starting at the offset in R0.
+ * R = 1 means check for "contain".
+ */
+ public ApfV4Generator addJumpIfPktAtR0ContainDnsQ(@NonNull byte[] qnames, int qtype,
+ @NonNull String tgt) throws IllegalInstructionException {
+ requireApfVersion(MIN_APF_VERSION_IN_DEV);
+ validateNames(qnames);
+ return append(new Instruction(ExtendedOpcodes.JDNSQMATCH, R1).setTargetLabel(tgt).addU8(
+ qtype).setBytesImm(qnames));
+ }
+
+ /**
+ * Appends a conditional jump instruction to the program: Jumps to {@code tgt} if the UDP
+ * payload's DNS answers/authority/additional records do NOT contain the NAMEs
+ * specified in {@code Names}. Examines the payload starting at the offset in R0.
+ * R = 0 means check for "does not contain".
+ */
+ public ApfV4Generator addJumpIfPktAtR0DoesNotContainDnsA(@NonNull byte[] names,
+ @NonNull String tgt) throws IllegalInstructionException {
+ requireApfVersion(MIN_APF_VERSION_IN_DEV);
+ validateNames(names);
+ return append(new Instruction(ExtendedOpcodes.JDNSAMATCH).setTargetLabel(tgt).setBytesImm(
+ names));
+ }
+
+ /**
+ * Appends a conditional jump instruction to the program: Jumps to {@code tgt} if the UDP
+ * payload's DNS answers/authority/additional records contain the NAMEs
+ * specified in {@code Names}. Examines the payload starting at the offset in R0.
+ * R = 1 means check for "contain".
+ */
+ public ApfV4Generator addJumpIfPktAtR0ContainDnsA(@NonNull byte[] names,
+ @NonNull String tgt) throws IllegalInstructionException {
+ requireApfVersion(MIN_APF_VERSION_IN_DEV);
+ validateNames(names);
+ return append(new Instruction(ExtendedOpcodes.JDNSAMATCH, R1).setTargetLabel(
+ tgt).setBytesImm(names));
+ }
+
private static void checkRange(@NonNull String variableName, long value, long lowerBound,
long upperBound) {
if (value >= lowerBound && value <= upperBound) {
@@ -1197,7 +1331,7 @@ public class ApfGenerator {
* @{code offset} to the other register.
* Requires APF v4 or greater.
*/
- public ApfGenerator addLoadData(Register dst, int ofs)
+ public ApfV4Generator addLoadData(Register dst, int ofs)
throws IllegalInstructionException {
requireApfVersion(APF_VERSION_4);
return append(new Instruction(Opcodes.LDDW, dst).addSigned(ofs));
@@ -1209,7 +1343,7 @@ public class ApfGenerator {
* @{code offset} to the other register.
* Requires APF v4 or greater.
*/
- public ApfGenerator addStoreData(Register src, int ofs)
+ public ApfV4Generator addStoreData(Register src, int ofs)
throws IllegalInstructionException {
requireApfVersion(APF_VERSION_4);
return append(new Instruction(Opcodes.STDW, src).addSigned(ofs));
diff --git a/src/android/net/apf/DnsUtils.java b/src/android/net/apf/DnsUtils.java
index 5bd2515b..f0f3039a 100644
--- a/src/android/net/apf/DnsUtils.java
+++ b/src/android/net/apf/DnsUtils.java
@@ -16,8 +16,8 @@
package android.net.apf;
-import static android.net.apf.ApfGenerator.Register.R0;
-import static android.net.apf.ApfGenerator.Register.R1;
+import static android.net.apf.ApfV4Generator.Register.R0;
+import static android.net.apf.ApfV4Generator.Register.R1;
import static com.android.net.module.util.NetworkStackConstants.ETHER_HEADER_LEN;
import static com.android.net.module.util.NetworkStackConstants.UDP_HEADER_LEN;
@@ -82,7 +82,7 @@ public class DnsUtils {
* - R1: label length
* - m[SLOT_CURRENT_PARSE_OFFSET]: offset of label text
*/
- private static void genParseDnsLabel(ApfGenerator gen, JumpTable jumpTable) throws Exception {
+ private static void genParseDnsLabel(ApfV4Generator gen, JumpTable jumpTable) throws Exception {
final String labelParseDnsLabelReal = "parse_dns_label_real";
final String labelPointerOffsetStored = "pointer_offset_stored";
@@ -101,7 +101,7 @@ public class DnsUtils {
* JGT R0, R1, DROP // Bad pointer. Drop.
*/
gen.addLoadFromMemory(R0, SLOT_DNS_HEADER_OFFSET);
- gen.addJumpIfR0GreaterThanR1(ApfGenerator.DROP_LABEL);
+ gen.addJumpIfR0GreaterThanR1(ApfV4Generator.DROP_LABEL);
/**
* // Now parse the label.
@@ -147,8 +147,8 @@ public class DnsUtils {
gen.addLoadFromMemory(R1, SLOT_DNS_HEADER_OFFSET);
gen.addAddR1();
gen.addLoadFromMemory(R1, SLOT_CURRENT_PARSE_OFFSET);
- gen.addJumpIfR0EqualsR1(ApfGenerator.DROP_LABEL);
- gen.addJumpIfR0GreaterThanR1(ApfGenerator.DROP_LABEL);
+ gen.addJumpIfR0EqualsR1(ApfV4Generator.DROP_LABEL);
+ gen.addJumpIfR0GreaterThanR1(ApfV4Generator.DROP_LABEL);
gen.addStoreToMemory(R0, SLOT_CURRENT_PARSE_OFFSET);
/** // Pointer chased. Parse starting from the pointer destination (which may also be a
@@ -192,7 +192,7 @@ public class DnsUtils {
* Outputs:
* None
*/
- private static void genFindNextDnsQuestion(ApfGenerator gen, JumpTable jumpTable)
+ private static void genFindNextDnsQuestion(ApfV4Generator gen, JumpTable jumpTable)
throws Exception {
final String labelFindNextDnsQuestionFollow = "find_next_dns_question_follow";
final String labelFindNextDnsQuestionLabel = "find_next_dns_question_label";
@@ -257,7 +257,7 @@ public class DnsUtils {
gen.addLoadFromMemory(R0, SLOT_NEGATIVE_QDCOUNT_REMAINING);
gen.addAdd(1);
gen.addStoreToMemory(R0, SLOT_NEGATIVE_QDCOUNT_REMAINING);
- gen.addJumpIfR0Equals(0, ApfGenerator.DROP_LABEL);
+ gen.addJumpIfR0Equals(0, ApfV4Generator.DROP_LABEL);
// If not, return.
gen.addJump(jumpTable.getStartLabel());
@@ -278,7 +278,7 @@ public class DnsUtils {
return "dns_nomatch_" + labelIndex;
}
- private static void addMatchLabel(@NonNull ApfGenerator gen, @NonNull JumpTable jumpTable,
+ private static void addMatchLabel(@NonNull ApfV4Generator gen, @NonNull JumpTable jumpTable,
int labelIndex, @NonNull String label, @NonNull String nextLabel) throws Exception {
final String parsedLabel = getPostMatchJumpTargetForLabel(labelIndex);
final String noMatchLabel = getNoMatchLabel(labelIndex);
@@ -346,7 +346,7 @@ public class DnsUtils {
* than hit the instruction limit.
* </ul>
*/
- public static void generateFilter(ApfGenerator gen, String[] labels) throws Exception {
+ public static void generateFilter(ApfV4Generator gen, String[] labels) throws Exception {
final int etherPlusUdpLen = ETHER_HEADER_LEN + UDP_HEADER_LEN;
final String labelJumpTable = "jump_table";
@@ -401,11 +401,11 @@ public class DnsUtils {
gen.defineLabel(LABEL_START_MATCH);
for (int i = 0; i < labels.length; i++) {
final String nextLabel = (i == labels.length - 1)
- ? ApfGenerator.PASS_LABEL
+ ? ApfV4Generator.PASS_LABEL
: getStartMatchLabel(i + 1);
addMatchLabel(gen, table, i, labels[i], nextLabel);
}
- gen.addJump(ApfGenerator.DROP_LABEL);
+ gen.addJump(ApfV4Generator.DROP_LABEL);
}
private DnsUtils() {
diff --git a/src/android/net/apf/JumpTable.java b/src/android/net/apf/JumpTable.java
index b449697d..9b012258 100644
--- a/src/android/net/apf/JumpTable.java
+++ b/src/android/net/apf/JumpTable.java
@@ -16,7 +16,7 @@
package android.net.apf;
-import static android.net.apf.ApfGenerator.Register.R0;
+import static android.net.apf.ApfV4Generator.Register.R0;
import androidx.annotation.NonNull;
@@ -34,8 +34,8 @@ import java.util.Objects;
* At compile time, any code that calls a subroutine must:
*
* <ul>
- * <li>Define a label (via {@link ApfGenerator#defineLabel}) immediately after the code that invokes
- * the subroutine.
+ * <li>Define a label (via {@link ApfV4Generator#defineLabel}) immediately after the code that
+ * invokes the subroutine.
* <li>Add the label to the jump table using {@link #addLabel}.
* <li>Generate the jump table in the program.
* </ul>
@@ -88,7 +88,7 @@ public class JumpTable {
Objects.requireNonNull(startLabel);
mStartLabel = startLabel;
if (returnAddressMemorySlot < 0
- || returnAddressMemorySlot >= ApfGenerator.FIRST_PREFILLED_MEMORY_SLOT) {
+ || returnAddressMemorySlot >= ApfV4Generator.FIRST_PREFILLED_MEMORY_SLOT) {
throw new IllegalArgumentException("Invalid memory slot " + returnAddressMemorySlot);
}
mReturnAddressMemorySlot = returnAddressMemorySlot;
@@ -122,8 +122,8 @@ public class JumpTable {
}
/** Generates APF code for this jump table */
- public void generate(@NonNull ApfGenerator gen)
- throws ApfGenerator.IllegalInstructionException {
+ public void generate(@NonNull ApfV4Generator gen)
+ throws ApfV4Generator.IllegalInstructionException {
gen.defineLabel(mStartLabel);
gen.addLoadFromMemory(R0, mReturnAddressMemorySlot);
for (Map.Entry<String, Integer> e : mJumpLabels.entrySet()) {
@@ -131,6 +131,6 @@ public class JumpTable {
}
// Cannot happen unless the program is malformed (i.e., the APF code loads an invalid return
// label index before jumping to the subroutine.
- gen.addJump(ApfGenerator.PASS_LABEL);
+ gen.addJump(ApfV4Generator.PASS_LABEL);
}
}
diff --git a/src/android/net/apf/LegacyApfFilter.java b/src/android/net/apf/LegacyApfFilter.java
index 6b93d89f..d81f8b41 100644
--- a/src/android/net/apf/LegacyApfFilter.java
+++ b/src/android/net/apf/LegacyApfFilter.java
@@ -43,8 +43,8 @@ import android.net.LinkProperties;
import android.net.NattKeepalivePacketDataParcelable;
import android.net.TcpKeepalivePacketDataParcelable;
import android.net.apf.ApfCounterTracker.Counter;
-import android.net.apf.ApfGenerator.IllegalInstructionException;
-import android.net.apf.ApfGenerator.Register;
+import android.net.apf.ApfV4Generator.IllegalInstructionException;
+import android.net.apf.ApfV4Generator.Register;
import android.net.ip.IpClient.IpClientCallbacksWrapper;
import android.net.metrics.ApfProgramEvent;
import android.net.metrics.ApfStats;
@@ -124,7 +124,7 @@ public class LegacyApfFilter implements AndroidPacketFilter {
/**
* When APFv4 is supported, loads R1 with the offset of the specified counter.
*/
- private void maybeSetupCounter(ApfGenerator gen, Counter c) {
+ private void maybeSetupCounter(ApfV4Generator gen, Counter c) {
if (mApfCapabilities.hasDataAccess()) {
gen.addLoadImmediate(Register.R1, c.offset());
}
@@ -204,7 +204,7 @@ public class LegacyApfFilter implements AndroidPacketFilter {
private void logStats() {
final long nowMs = mClock.elapsedRealtime();
- synchronized (this) {
+ synchronized (LegacyApfFilter.this) {
final ApfStats stats = new ApfStats.Builder()
.setReceivedRas(mReceivedRas)
.setMatchingRas(mMatchingRas)
@@ -408,8 +408,8 @@ public class LegacyApfFilter implements AndroidPacketFilter {
} else {
// APFv4 unsupported: turn jumps to the counter trampolines to immediately PASS or DROP,
// preserving the original pre-APFv4 behavior.
- mCountAndPassLabel = ApfGenerator.PASS_LABEL;
- mCountAndDropLabel = ApfGenerator.DROP_LABEL;
+ mCountAndPassLabel = ApfV4Generator.PASS_LABEL;
+ mCountAndDropLabel = ApfV4Generator.DROP_LABEL;
}
// Now fill the black list from the passed array
@@ -439,7 +439,6 @@ public class LegacyApfFilter implements AndroidPacketFilter {
return mUniqueCounter++;
}
- @GuardedBy("this")
private static int[] filterEthTypeBlackList(int[] ethTypeBlackList) {
ArrayList<Integer> bl = new ArrayList<Integer>();
@@ -952,13 +951,13 @@ public class LegacyApfFilter implements AndroidPacketFilter {
}
// Filter for a fraction of the lifetime and adjust for the age of the RA.
- @GuardedBy("ApfFilter.this")
+ @GuardedBy("LegacyApfFilter.this")
int filterLifetime() {
return (int) (mMinLifetime / FRACTION_OF_LIFETIME_TO_FILTER)
- (int) (mProgramBaseTime - mLastSeen);
}
- @GuardedBy("ApfFilter.this")
+ @GuardedBy("LegacyApfFilter.this")
boolean shouldFilter() {
return filterLifetime() > 0;
}
@@ -969,8 +968,8 @@ public class LegacyApfFilter implements AndroidPacketFilter {
// value of this function is used to calculate the program min lifetime (which corresponds
// to the smallest generated filter lifetime). Returning Long.MAX_VALUE in the case no
// filter gets generated makes sure the program lifetime stays unaffected.
- @GuardedBy("ApfFilter.this")
- long generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException {
+ @GuardedBy("LegacyApfFilter.this")
+ long generateFilterLocked(ApfV4Generator gen) throws IllegalInstructionException {
String nextFilterLabel = "Ra" + getUniqueNumberLocked();
// Skip if packet is not the right size
gen.addLoadFromMemory(Register.R0, gen.PACKET_SIZE_MEMORY_SLOT);
@@ -1018,7 +1017,7 @@ public class LegacyApfFilter implements AndroidPacketFilter {
// Append a filter for this keepalive ack to {@code gen}.
// Jump to drop if it matches the keepalive ack.
// Jump to the next filter if packet doesn't match the keepalive ack.
- abstract void generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException;
+ abstract void generateFilterLocked(ApfV4Generator gen) throws IllegalInstructionException;
}
// A class to hold NAT-T keepalive ack information.
@@ -1061,7 +1060,8 @@ public class LegacyApfFilter implements AndroidPacketFilter {
}
@Override
- void generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException {
+ @GuardedBy("LegacyApfFilter.this")
+ void generateFilterLocked(ApfV4Generator gen) throws IllegalInstructionException {
final String nextFilterLabel = "natt_keepalive_filter" + getUniqueNumberLocked();
gen.addLoadImmediate(Register.R0, ETH_HEADER_LEN + IPV4_SRC_ADDR_OFFSET);
@@ -1163,7 +1163,7 @@ public class LegacyApfFilter implements AndroidPacketFilter {
// Append a filter for this keepalive ack to {@code gen}.
// Jump to drop if it matches the keepalive ack.
// Jump to the next filter if packet doesn't match the keepalive ack.
- abstract void generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException;
+ abstract void generateFilterLocked(ApfV4Generator gen) throws IllegalInstructionException;
}
private class TcpKeepaliveAckV4 extends TcpKeepaliveAck {
@@ -1176,7 +1176,8 @@ public class LegacyApfFilter implements AndroidPacketFilter {
}
@Override
- void generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException {
+ @GuardedBy("LegacyApfFilter.this")
+ void generateFilterLocked(ApfV4Generator gen) throws IllegalInstructionException {
final String nextFilterLabel = "keepalive_ack" + getUniqueNumberLocked();
gen.addLoadImmediate(Register.R0, ETH_HEADER_LEN + IPV4_SRC_ADDR_OFFSET);
@@ -1219,7 +1220,7 @@ public class LegacyApfFilter implements AndroidPacketFilter {
}
@Override
- void generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException {
+ void generateFilterLocked(ApfV4Generator gen) throws IllegalInstructionException {
throw new UnsupportedOperationException("IPv6 TCP Keepalive is not supported yet");
}
}
@@ -1292,7 +1293,7 @@ public class LegacyApfFilter implements AndroidPacketFilter {
* - Packet being filtered is ARP
*/
@GuardedBy("this")
- private void generateArpFilterLocked(ApfGenerator gen) throws IllegalInstructionException {
+ private void generateArpFilterLocked(ApfV4Generator gen) throws IllegalInstructionException {
// Here's a basic summary of what the ARP filter program does:
//
// if not ARP IPv4
@@ -1360,7 +1361,7 @@ public class LegacyApfFilter implements AndroidPacketFilter {
* - Packet being filtered is IPv4
*/
@GuardedBy("this")
- private void generateIPv4FilterLocked(ApfGenerator gen) throws IllegalInstructionException {
+ private void generateIPv4FilterLocked(ApfV4Generator gen) throws IllegalInstructionException {
// Here's a basic summary of what the IPv4 filter program does:
//
// if filtering multicast (i.e. multicast lock not held):
@@ -1441,7 +1442,8 @@ public class LegacyApfFilter implements AndroidPacketFilter {
gen.addJump(mCountAndPassLabel);
}
- private void generateKeepaliveFilters(ApfGenerator gen, Class<?> filterType, int proto,
+ @GuardedBy("this")
+ private void generateKeepaliveFilters(ApfV4Generator gen, Class<?> filterType, int proto,
int offset, String label) throws IllegalInstructionException {
final boolean haveKeepaliveResponses = CollectionUtils.any(mKeepalivePackets,
ack -> filterType.isInstance(ack));
@@ -1462,12 +1464,14 @@ public class LegacyApfFilter implements AndroidPacketFilter {
gen.defineLabel(label);
}
- private void generateV4KeepaliveFilters(ApfGenerator gen) throws IllegalInstructionException {
+ @GuardedBy("this")
+ private void generateV4KeepaliveFilters(ApfV4Generator gen) throws IllegalInstructionException {
generateKeepaliveFilters(gen, TcpKeepaliveAckV4.class, IPPROTO_TCP, IPV4_PROTOCOL_OFFSET,
"skip_v4_keepalive_filter");
}
- private void generateV4NattKeepaliveFilters(ApfGenerator gen)
+ @GuardedBy("this")
+ private void generateV4NattKeepaliveFilters(ApfV4Generator gen)
throws IllegalInstructionException {
generateKeepaliveFilters(gen, NattKeepaliveResponse.class,
IPPROTO_UDP, IPV4_PROTOCOL_OFFSET, "skip_v4_nattkeepalive_filter");
@@ -1480,7 +1484,7 @@ public class LegacyApfFilter implements AndroidPacketFilter {
* - Packet being filtered is IPv6
*/
@GuardedBy("this")
- private void generateIPv6FilterLocked(ApfGenerator gen) throws IllegalInstructionException {
+ private void generateIPv6FilterLocked(ApfV4Generator gen) throws IllegalInstructionException {
// Here's a basic summary of what the IPv6 filter program does:
//
// if there is a hop-by-hop option present (e.g. MLD query)
@@ -1580,7 +1584,7 @@ public class LegacyApfFilter implements AndroidPacketFilter {
* or PASS_LABEL if the packet is mDNS packets. Otherwise, skip this check.
*/
@GuardedBy("this")
- private void generateMdnsFilterLocked(ApfGenerator gen)
+ private void generateMdnsFilterLocked(ApfV4Generator gen)
throws IllegalInstructionException {
final String skipMdnsv4Filter = "skip_mdns_v4_filter";
final String skipMdnsFilter = "skip_mdns_filter";
@@ -1680,8 +1684,8 @@ public class LegacyApfFilter implements AndroidPacketFilter {
gen.defineLabel(skipMdnsFilter);
}
-
- private void generateV6KeepaliveFilters(ApfGenerator gen) throws IllegalInstructionException {
+ @GuardedBy("this")
+ private void generateV6KeepaliveFilters(ApfV4Generator gen) throws IllegalInstructionException {
generateKeepaliveFilters(gen, TcpKeepaliveAckV6.class, IPPROTO_TCP, IPV6_NEXT_HEADER_OFFSET,
"skip_v6_keepalive_filter");
}
@@ -1707,9 +1711,9 @@ public class LegacyApfFilter implements AndroidPacketFilter {
* </ul>
*/
@GuardedBy("this")
- protected ApfGenerator emitPrologueLocked() throws IllegalInstructionException {
+ protected ApfV4Generator emitPrologueLocked() throws IllegalInstructionException {
// This is guaranteed to succeed because of the check in maybeCreate.
- ApfGenerator gen = new ApfGenerator(mApfCapabilities.apfVersionSupported);
+ ApfV4Generator gen = new ApfV4Generator(mApfCapabilities.apfVersionSupported);
if (mApfCapabilities.hasDataAccess()) {
// Increment TOTAL_PACKETS
@@ -1792,7 +1796,7 @@ public class LegacyApfFilter implements AndroidPacketFilter {
* before jumping to the actual PASS and DROP labels.
*/
@GuardedBy("this")
- private void emitEpilogue(ApfGenerator gen) throws IllegalInstructionException {
+ private void emitEpilogue(ApfV4Generator gen) throws IllegalInstructionException {
// If APFv4 is unsupported, no epilogue is necessary: if execution reached this far, it
// will just fall-through to the PASS label.
if (!mApfCapabilities.hasDataAccess()) return;
@@ -1822,6 +1826,8 @@ public class LegacyApfFilter implements AndroidPacketFilter {
* Generate and install a new filter program.
*/
@GuardedBy("this")
+ // errorprone false positive on ra#shouldFilter and ra#generateFilterLocked
+ @SuppressWarnings("GuardedBy")
@VisibleForTesting
public void installNewProgramLocked() {
purgeExpiredRasLocked();
@@ -1837,7 +1843,7 @@ public class LegacyApfFilter implements AndroidPacketFilter {
mProgramBaseTime = currentTimeSeconds();
try {
// Step 1: Determine how many RA filters we can fit in the program.
- ApfGenerator gen = emitPrologueLocked();
+ ApfV4Generator gen = emitPrologueLocked();
// The epilogue normally goes after the RA filters, but add it early to include its
// length when estimating the total.
@@ -1914,6 +1920,7 @@ public class LegacyApfFilter implements AndroidPacketFilter {
/**
* Returns {@code true} if a new program should be installed because the current one dies soon.
*/
+ @GuardedBy("this")
private boolean shouldInstallnewProgram() {
long expiry = mLastTimeInstalledProgram + mLastInstalledProgramMinLifetime;
return expiry < currentTimeSeconds() + MAX_PROGRAM_LIFETIME_WORTH_REFRESHING;
@@ -2042,7 +2049,7 @@ public class LegacyApfFilter implements AndroidPacketFilter {
// 1. the program generator will need its offsets adjusted.
// 2. the packet filter attached to our packet socket will need its offset adjusted.
if (apfCapabilities.apfPacketFormat != ARPHRD_ETHER) return null;
- if (!ApfGenerator.supportsVersion(apfCapabilities.apfVersionSupported)) {
+ if (!ApfV4Generator.supportsVersion(apfCapabilities.apfVersionSupported)) {
Log.e(TAG, "Unsupported APF version: " + apfCapabilities.apfVersionSupported);
return null;
}
diff --git a/src/android/net/dhcp/DhcpClient.java b/src/android/net/dhcp/DhcpClient.java
index b0bac047..4e6a222a 100644
--- a/src/android/net/dhcp/DhcpClient.java
+++ b/src/android/net/dhcp/DhcpClient.java
@@ -53,7 +53,6 @@ import static com.android.net.module.util.NetworkStackConstants.IPV4_ADDR_ANY;
import static com.android.net.module.util.NetworkStackConstants.IPV4_CONFLICT_ANNOUNCE_NUM;
import static com.android.net.module.util.NetworkStackConstants.IPV4_CONFLICT_PROBE_NUM;
import static com.android.net.module.util.SocketUtils.closeSocketQuietly;
-import static com.android.networkstack.util.NetworkStackUtils.DHCP_INIT_REBOOT_VERSION;
import static com.android.networkstack.util.NetworkStackUtils.DHCP_IP_CONFLICT_DETECT_VERSION;
import static com.android.networkstack.util.NetworkStackUtils.DHCP_RAPID_COMMIT_VERSION;
import static com.android.networkstack.util.NetworkStackUtils.DHCP_SLOW_RETRANSMISSION_VERSION;
@@ -555,13 +554,6 @@ public class DhcpClient extends StateMachine {
}
/**
- * check whether or not to support caching the last lease info and INIT-REBOOT state.
- */
- public boolean isDhcpLeaseCacheEnabled() {
- return mDependencies.isFeatureNotChickenedOut(mContext, DHCP_INIT_REBOOT_VERSION);
- }
-
- /**
* check whether or not to support DHCP Rapid Commit option.
*/
public boolean isDhcpRapidCommitEnabled() {
@@ -584,7 +576,7 @@ public class DhcpClient extends StateMachine {
}
private void recordMetricEnabledFeatures() {
- if (isDhcpLeaseCacheEnabled()) mMetrics.setDhcpEnabledFeature(DhcpFeature.DF_INITREBOOT);
+ mMetrics.setDhcpEnabledFeature(DhcpFeature.DF_INITREBOOT);
if (isDhcpRapidCommitEnabled()) mMetrics.setDhcpEnabledFeature(DhcpFeature.DF_RAPIDCOMMIT);
if (isDhcpIpConflictDetectEnabled()) mMetrics.setDhcpEnabledFeature(DhcpFeature.DF_DAD);
if (mConfiguration.isPreconnectionEnabled) {
@@ -855,17 +847,13 @@ public class DhcpClient extends StateMachine {
}
private void notifySuccess() {
- if (isDhcpLeaseCacheEnabled()) {
- maybeSaveLeaseToIpMemoryStore();
- }
+ maybeSaveLeaseToIpMemoryStore();
mController.sendMessage(
CMD_POST_DHCP_ACTION, DHCP_SUCCESS, 0, new DhcpResults(mDhcpLease));
}
private void notifyFailure(int arg) {
- if (isDhcpLeaseCacheEnabled()) {
- setLeaseExpiredToIpMemoryStore();
- }
+ setLeaseExpiredToIpMemoryStore();
mController.sendMessage(CMD_POST_DHCP_ACTION, arg, 0, null);
}
@@ -1028,7 +1016,7 @@ public class DhcpClient extends StateMachine {
if (mConfiguration.isPreconnectionEnabled) {
transitionTo(mDhcpPreconnectingState);
} else {
- startInitRebootOrInit();
+ startInitReboot();
}
recordMetricEnabledFeatures();
return HANDLED;
@@ -1354,13 +1342,8 @@ public class DhcpClient extends StateMachine {
}
}
- private void startInitRebootOrInit() {
- if (isDhcpLeaseCacheEnabled()) {
- preDhcpTransitionTo(mWaitBeforeObtainingConfigurationState,
- mObtainingConfigurationState);
- } else {
- preDhcpTransitionTo(mWaitBeforeStartState, mDhcpInitState);
- }
+ private void startInitReboot() {
+ preDhcpTransitionTo(mWaitBeforeObtainingConfigurationState, mObtainingConfigurationState);
}
class DhcpPreconnectingState extends TimeoutState {
@@ -1392,7 +1375,7 @@ public class DhcpClient extends StateMachine {
mConfiguration.isPreconnectionEnabled);
return HANDLED;
case CMD_ABORT_PRECONNECTION:
- startInitRebootOrInit();
+ startInitReboot();
return HANDLED;
default:
return NOT_HANDLED;
@@ -1410,7 +1393,7 @@ public class DhcpClient extends StateMachine {
// and send a DISCOVER.
@Override
public void timeout() {
- startInitRebootOrInit();
+ startInitReboot();
}
private void sendPreconnectionPacket() {
@@ -1507,7 +1490,8 @@ public class DhcpClient extends StateMachine {
// IpClient sees the IP address appear, it will enter provisioned state without any
// configuration information from DHCP. http://b/146850745.
notifySuccess();
- mController.sendMessage(CMD_CONFIGURE_LINKADDRESS, mDhcpLease.ipAddress);
+ mController.sendMessage(CMD_CONFIGURE_LINKADDRESS, mDhcpLease.leaseDuration, 0,
+ mDhcpLease.ipAddress);
}
@Override
@@ -2083,7 +2067,7 @@ public class DhcpClient extends StateMachine {
}
protected void timeout() {
- startInitRebootOrInit();
+ startInitReboot();
}
}
diff --git a/src/android/net/dhcp6/Dhcp6Packet.java b/src/android/net/dhcp6/Dhcp6Packet.java
index 53dd274d..ff7b036d 100644
--- a/src/android/net/dhcp6/Dhcp6Packet.java
+++ b/src/android/net/dhcp6/Dhcp6Packet.java
@@ -561,8 +561,6 @@ public class Dhcp6Packet {
if (pd != null) {
newPacket.mPrefixDelegation = pd;
newPacket.mIaId = pd.iaid;
- } else {
- throw new ParseException("Missing IA_PD option");
}
newPacket.mStatusCode = statusCode;
newPacket.mRapidCommit = rapidCommit;
@@ -600,8 +598,10 @@ public class Dhcp6Packet {
Log.e(TAG, "Unexpected transaction ID " + mTransId + ", expected " + transId);
return false;
}
- // mPrefixDelegation is guaranteed to be non-null. In decode() function, we throw the
- // exception if IA_PD option doesn't exist.
+ if (mPrefixDelegation == null) {
+ Log.e(TAG, "DHCPv6 message without IA_PD option, ignoring");
+ return false;
+ }
if (!mPrefixDelegation.isValid()) {
Log.e(TAG, "DHCPv6 message takes invalid IA_PD option, ignoring");
return false;
diff --git a/src/android/net/ip/IpClient.java b/src/android/net/ip/IpClient.java
index d7ef4df2..56dbb6f1 100644
--- a/src/android/net/ip/IpClient.java
+++ b/src/android/net/ip/IpClient.java
@@ -3052,14 +3052,22 @@ public class IpClient extends StateMachine {
}
}
- private void addInterfaceAddress(@NonNull final Inet6Address address,
+ private void addInterfaceAddress(@Nullable final Inet6Address address,
@NonNull final IaPrefixOption ipo) {
final int flags = IFA_F_NOPREFIXROUTE | IFA_F_MANAGETEMPADDR | IFA_F_NODAD;
final long now = SystemClock.elapsedRealtime();
- final long deprecationTime = now + ipo.preferred;
- final long expirationTime = now + ipo.valid;
- final LinkAddress la = new LinkAddress(address, RFC7421_PREFIX_LENGTH, flags,
- RT_SCOPE_UNIVERSE /* scope */, deprecationTime, expirationTime);
+ // Per RFC8415 section 21.22 the preferred/valid lifetime in IA Prefix option
+ // expressed in units of seconds.
+ final long deprecationTime = now + ipo.preferred * 1000;
+ final long expirationTime = now + ipo.valid * 1000;
+ final LinkAddress la;
+ try {
+ la = new LinkAddress(address, RFC7421_PREFIX_LENGTH, flags,
+ RT_SCOPE_UNIVERSE /* scope */, deprecationTime, expirationTime);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Invalid IPv6 link address " + e);
+ return;
+ }
if (!la.isGlobalPreferred()) {
Log.w(TAG, la + " is not a global IPv6 address");
return;
@@ -3075,15 +3083,21 @@ public class IpClient extends StateMachine {
private void updateDelegatedAddresses(@NonNull final List<IaPrefixOption> valid) {
if (valid.isEmpty()) return;
+ final List<IpPrefix> zeroLifetimePrefixList = new ArrayList<>();
for (IaPrefixOption ipo : valid) {
final IpPrefix prefix = ipo.getIpPrefix();
// The prefix with preferred/valid lifetime of 0 is considered as a valid prefix,
- // it can be passed to IpClient from Dhcp6Client, however, client should stop using
- // the global addresses derived from this prefix immediately.
+ // and can be passed to IpClient from Dhcp6Client, but client should stop using
+ // the global addresses derived from this prefix asap. Deleting the associated
+ // global IPv6 addresses immediately before adding another IPv6 address may result
+ // in a race where the device throws the provisioning failure callback due to the
+ // loss of all valid IPv6 addresses, however, IPv6 provisioning will soon complete
+ // successfully when the user space sees the new IPv6 address update. To avoid this
+ // race, temporarily store all prefix(es) with 0 preferred/valid lifetime and then
+ // delete them after iterating through all valid IA prefix options.
if (ipo.withZeroLifetimes()) {
- Log.d(TAG, "Delete IPv6 address derived from prefix " + prefix
- + " with 0 preferred/valid lifetime");
- deleteIpv6PrefixDelegationAddresses(prefix);
+ zeroLifetimePrefixList.add(prefix);
+ continue;
}
// Otherwise, configure IPv6 addresses derived from the delegated prefix(es) on
// the interface. We've checked that delegated prefix is valid upon receiving the
@@ -3094,6 +3108,15 @@ public class IpClient extends StateMachine {
macAddressToEui64(mInterfaceParams.macAddr));
addInterfaceAddress(address, ipo);
}
+
+ // Delete global IPv6 addresses derived from prefix with 0 preferred/valid lifetime.
+ if (!zeroLifetimePrefixList.isEmpty()) {
+ for (IpPrefix prefix : zeroLifetimePrefixList) {
+ Log.d(TAG, "Delete IPv6 address derived from prefix " + prefix
+ + " with 0 preferred/valid lifetime");
+ deleteIpv6PrefixDelegationAddresses(prefix);
+ }
+ }
}
private void removeExpiredDelegatedAddresses(@NonNull final List<IaPrefixOption> expired) {
@@ -3235,8 +3258,19 @@ public class IpClient extends StateMachine {
break;
case DhcpClient.CMD_CONFIGURE_LINKADDRESS: {
+ final int leaseDuration = msg.arg1;
final LinkAddress ipAddress = (LinkAddress) msg.obj;
- if (mInterfaceCtrl.setIPv4Address(ipAddress)) {
+ // For IPv4 link addresses, there is no concept of preferred/valid lifetimes.
+ // Populate the ifa_cacheinfo attribute in the netlink message with the DHCP
+ // lease duration, which is used by the kernel to maintain the validity of the
+ // IP addresses.
+ if (NetlinkUtils.sendRtmNewAddressRequest(mInterfaceParams.index,
+ ipAddress.getAddress(),
+ (short) ipAddress.getPrefixLength(),
+ 0 /* flags */,
+ (byte) RT_SCOPE_UNIVERSE /* scope */,
+ leaseDuration /* preferred */,
+ leaseDuration /* valid */)) {
// Although it's impossible to happen that DHCP client becomes null in
// RunningState and then NPE is thrown when it attempts to send a message
// on an null object, sometimes it's found during stress tests. If this
diff --git a/src/android/net/ip/IpClientLinkObserver.java b/src/android/net/ip/IpClientLinkObserver.java
index 2068caa7..22ee1010 100644
--- a/src/android/net/ip/IpClientLinkObserver.java
+++ b/src/android/net/ip/IpClientLinkObserver.java
@@ -21,6 +21,7 @@ import static android.system.OsConstants.AF_UNSPEC;
import static android.system.OsConstants.IFF_LOOPBACK;
import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ROUTER_ADVERTISEMENT;
+import static com.android.net.module.util.NetworkStackConstants.INFINITE_LEASE;
import static com.android.net.module.util.netlink.NetlinkConstants.IFF_LOWER_UP;
import static com.android.net.module.util.netlink.NetlinkConstants.RTM_F_CLONED;
import static com.android.net.module.util.netlink.NetlinkConstants.RTN_UNICAST;
@@ -38,6 +39,7 @@ import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.RouteInfo;
import android.os.Handler;
+import android.os.SystemClock;
import android.system.OsConstants;
import android.util.Log;
@@ -56,6 +58,7 @@ import com.android.net.module.util.netlink.NetlinkMessage;
import com.android.net.module.util.netlink.RtNetlinkAddressMessage;
import com.android.net.module.util.netlink.RtNetlinkLinkMessage;
import com.android.net.module.util.netlink.RtNetlinkRouteMessage;
+import com.android.net.module.util.netlink.StructIfacacheInfo;
import com.android.net.module.util.netlink.StructIfaddrMsg;
import com.android.net.module.util.netlink.StructIfinfoMsg;
import com.android.net.module.util.netlink.StructNdOptPref64;
@@ -629,13 +632,31 @@ public class IpClientLinkObserver implements NetworkObserver {
}
}
+ // The preferred/valid in ifa_cacheinfo expressed in units of seconds, convert
+ // it to milliseconds for deprecationTime or expirationTime used in LinkAddress.
+ private static long getDeprecationOrExpirationTime(
+ @Nullable final StructIfacacheInfo cacheInfo, long now, boolean deprecationTime) {
+ if (cacheInfo == null) return LinkAddress.LIFETIME_UNKNOWN;
+ final long lifetime = deprecationTime ? cacheInfo.preferred : cacheInfo.valid;
+ return (lifetime == Integer.toUnsignedLong(INFINITE_LEASE))
+ ? LinkAddress.LIFETIME_PERMANENT
+ : now + lifetime * 1000;
+ }
+
private void processRtNetlinkAddressMessage(RtNetlinkAddressMessage msg) {
if (!mNetlinkEventParsingEnabled) return;
final StructIfaddrMsg ifaddrMsg = msg.getIfaddrHeader();
if (ifaddrMsg.index != mIfindex) return;
+
+ final StructIfacacheInfo cacheInfo = msg.getIfacacheInfo();
+ final long now = SystemClock.elapsedRealtime();
+ final long deprecationTime =
+ getDeprecationOrExpirationTime(cacheInfo, now, true /* deprecationTime */);
+ final long expirationTime =
+ getDeprecationOrExpirationTime(cacheInfo, now, false /* deprecationTime */);
final LinkAddress la = new LinkAddress(msg.getIpAddress(), ifaddrMsg.prefixLen,
- msg.getFlags(), ifaddrMsg.scope);
+ msg.getFlags(), ifaddrMsg.scope, deprecationTime, expirationTime);
switch (msg.getHeader().nlmsg_type) {
case NetlinkConstants.RTM_NEWADDR:
diff --git a/src/android/net/ip/IpReachabilityMonitor.java b/src/android/net/ip/IpReachabilityMonitor.java
index e252a68b..d2b1064d 100644
--- a/src/android/net/ip/IpReachabilityMonitor.java
+++ b/src/android/net/ip/IpReachabilityMonitor.java
@@ -23,6 +23,9 @@ import static android.net.metrics.IpReachabilityEvent.PROVISIONING_LOST_ORGANIC;
import static com.android.networkstack.util.NetworkStackUtils.IP_REACHABILITY_IGNORE_INCOMPLETE_IPV6_DEFAULT_ROUTER_VERSION;
import static com.android.networkstack.util.NetworkStackUtils.IP_REACHABILITY_IGNORE_INCOMPLETE_IPV6_DNS_SERVER_VERSION;
+import static com.android.networkstack.util.NetworkStackUtils.IP_REACHABILITY_IGNORE_ORGANIC_NUD_FAILURE_VERSION;
+import static com.android.networkstack.util.NetworkStackUtils.IP_REACHABILITY_MCAST_RESOLICIT_VERSION;
+import static com.android.networkstack.util.NetworkStackUtils.IP_REACHABILITY_ROUTER_MAC_CHANGE_FAILURE_ONLY_AFTER_ROAM_VERSION;
import android.content.Context;
import android.net.ConnectivityManager;
@@ -235,8 +238,11 @@ public class IpReachabilityMonitor {
private int mInterSolicitIntervalMs;
@NonNull
private final Callback mCallback;
+ private final boolean mMulticastResolicitEnabled;
private final boolean mIgnoreIncompleteIpv6DnsServerEnabled;
private final boolean mIgnoreIncompleteIpv6DefaultRouterEnabled;
+ private final boolean mMacChangeFailureOnlyAfterRoam;
+ private final boolean mIgnoreOrganicNudFailure;
public IpReachabilityMonitor(
Context context, InterfaceParams ifParams, Handler h, SharedLog log, Callback callback,
@@ -258,10 +264,16 @@ public class IpReachabilityMonitor {
mUsingMultinetworkPolicyTracker = usingMultinetworkPolicyTracker;
mCm = context.getSystemService(ConnectivityManager.class);
mDependencies = dependencies;
+ mMulticastResolicitEnabled = dependencies.isFeatureNotChickenedOut(context,
+ IP_REACHABILITY_MCAST_RESOLICIT_VERSION);
mIgnoreIncompleteIpv6DnsServerEnabled = dependencies.isFeatureNotChickenedOut(context,
IP_REACHABILITY_IGNORE_INCOMPLETE_IPV6_DNS_SERVER_VERSION);
mIgnoreIncompleteIpv6DefaultRouterEnabled = dependencies.isFeatureEnabled(context,
IP_REACHABILITY_IGNORE_INCOMPLETE_IPV6_DEFAULT_ROUTER_VERSION);
+ mMacChangeFailureOnlyAfterRoam = dependencies.isFeatureNotChickenedOut(context,
+ IP_REACHABILITY_ROUTER_MAC_CHANGE_FAILURE_ONLY_AFTER_ROAM_VERSION);
+ mIgnoreOrganicNudFailure = dependencies.isFeatureEnabled(context,
+ IP_REACHABILITY_IGNORE_ORGANIC_NUD_FAILURE_VERSION);
mMetricsLog = metricsLog;
mNetd = netd;
Preconditions.checkNotNull(mNetd);
@@ -270,8 +282,10 @@ public class IpReachabilityMonitor {
// In case the overylaid parameters specify an invalid configuration, set the parameters
// to the hardcoded defaults first, then set them to the values used in the steady state.
try {
- setNeighborParameters(MIN_NUD_SOLICIT_NUM, MIN_NUD_SOLICIT_INTERVAL_MS,
- NUD_MCAST_RESOLICIT_NUM);
+ int numResolicits = mMulticastResolicitEnabled
+ ? NUD_MCAST_RESOLICIT_NUM
+ : INVALID_NUD_MCAST_RESOLICIT_NUM;
+ setNeighborParameters(MIN_NUD_SOLICIT_NUM, MIN_NUD_SOLICIT_INTERVAL_MS, numResolicits);
} catch (Exception e) {
Log.e(TAG, "Failed to adjust neighbor parameters with hardcoded defaults");
}
@@ -349,7 +363,15 @@ public class IpReachabilityMonitor {
private boolean hasDefaultRouterNeighborMacAddressChanged(
@Nullable final NeighborEvent prev, @NonNull final NeighborEvent event) {
- if (prev == null || !isNeighborDefaultRouter(event)) return false;
+ // TODO: once this rolls out safely, merge something like aosp/2908139 and remove this code.
+ if (mMacChangeFailureOnlyAfterRoam) {
+ if (!isNeighborDefaultRouter(event)) return false;
+ if (prev == null || prev.nudState != StructNdMsg.NUD_PROBE) return false;
+ if (!isNudFailureDueToRoam()) return false;
+ } else {
+ // Previous, incorrect, behaviour: MAC address changes are a failure at all times.
+ if (prev == null || !isNeighborDefaultRouter(event)) return false;
+ }
return !event.macAddr.equals(prev.macAddr);
}
@@ -408,7 +430,8 @@ public class IpReachabilityMonitor {
private void handleNeighborReachable(@Nullable final NeighborEvent prev,
@NonNull final NeighborEvent event) {
- if (hasDefaultRouterNeighborMacAddressChanged(prev, event)) {
+ if (mMulticastResolicitEnabled
+ && hasDefaultRouterNeighborMacAddressChanged(prev, event)) {
// This implies device has confirmed the neighbor's reachability from
// other states(e.g., NUD_PROBE or NUD_STALE), checking if the mac
// address hasn't changed is required. If Mac address does change, then
@@ -512,7 +535,13 @@ public class IpReachabilityMonitor {
Log.w(TAG, logMsg);
// TODO: remove |ip| when the callback signature no longer has
// an InetAddress argument.
- mCallback.notifyLost(ip, logMsg, type);
+ // Notify critical neighbor lost as long as the NUD failures
+ // are not from kernel organic or the NUD failure event type is
+ // NUD_ORGANIC_FAILED_CRITICAL but the experiment flag is not
+ // enabled. Regardless, the event metrics are still recoreded.
+ if (type != NudEventType.NUD_ORGANIC_FAILED_CRITICAL || !mIgnoreOrganicNudFailure) {
+ mCallback.notifyLost(ip, logMsg, type);
+ }
}
logNudFailed(event, type);
}
@@ -574,7 +603,8 @@ public class IpReachabilityMonitor {
private long getProbeWakeLockDuration() {
final long gracePeriodMs = 500;
- final int numSolicits = mNumSolicits + NUD_MCAST_RESOLICIT_NUM;
+ final int numSolicits =
+ mNumSolicits + (mMulticastResolicitEnabled ? NUD_MCAST_RESOLICIT_NUM : 0);
return (long) (numSolicits * mInterSolicitIntervalMs) + gracePeriodMs;
}
diff --git a/src/com/android/networkstack/util/NetworkStackUtils.java b/src/com/android/networkstack/util/NetworkStackUtils.java
index 829d0c6a..c2de03cd 100755
--- a/src/com/android/networkstack/util/NetworkStackUtils.java
+++ b/src/com/android/networkstack/util/NetworkStackUtils.java
@@ -151,11 +151,6 @@ public class NetworkStackUtils {
new String [] {"https://www.google.com/generate_204"};
/**
- * Minimum module version at which to enable the DHCP INIT-REBOOT state.
- */
- public static final String DHCP_INIT_REBOOT_VERSION = "dhcp_init_reboot_version";
-
- /**
* Minimum module version at which to enable the DHCP Rapid Commit option.
*/
public static final String DHCP_RAPID_COMMIT_VERSION = "dhcp_rapid_commit_version";
@@ -212,6 +207,13 @@ public class NetworkStackUtils {
"ipclient_accept_ipv6_link_local_dns_version";
/**
+ * Experiment flag to enable "mcast_resolicit" neighbor parameter in IpReachabilityMonitor,
+ * set it to 3 by default.
+ */
+ public static final String IP_REACHABILITY_MCAST_RESOLICIT_VERSION =
+ "ip_reachability_mcast_resolicit_version";
+
+ /**
* Experiment flag to attempt to ignore the on-link IPv6 DNS server which fails to respond to
* address resolution.
*/
@@ -226,6 +228,18 @@ public class NetworkStackUtils {
"ip_reachability_ignore_incompleted_ipv6_default_router_version";
/**
+ * Experiment flag to treat router MAC address changes as a failure only on roam.
+ */
+ public static final String IP_REACHABILITY_ROUTER_MAC_CHANGE_FAILURE_ONLY_AFTER_ROAM_VERSION =
+ "ip_reachability_router_mac_change_failure_only_after_roam_version";
+
+ /**
+ * Experiment flag to ignore all NUD failures from kernel organic.
+ */
+ public static final String IP_REACHABILITY_IGNORE_ORGANIC_NUD_FAILURE_VERSION =
+ "ip_reachability_ignore_organic_nud_failure_version";
+
+ /**
* Experiment flag to enable DHCPv6 Prefix Delegation(RFC8415) in IpClient.
*/
public static final String IPCLIENT_DHCPV6_PREFIX_DELEGATION_VERSION =
@@ -235,10 +249,12 @@ public class NetworkStackUtils {
* Experiment flag to enable new ra filter.
*/
public static final String APF_NEW_RA_FILTER_VERSION = "apf_new_ra_filter_version";
+
/**
* Experiment flag to enable the feature of polling counters in Apf.
*/
public static final String APF_POLLING_COUNTERS_VERSION = "apf_polling_counters_version";
+
/**
* Experiment flag to enable the feature of ignoring any individual RA section with lifetime
* below accept_ra_min_lft sysctl.
@@ -246,6 +262,12 @@ public class NetworkStackUtils {
public static final String IPCLIENT_IGNORE_LOW_RA_LIFETIME_VERSION =
"ipclient_ignore_low_ra_lifetime_version";
+ /**
+ * Feature flag to send private DNS resolution queries and probes on a background thread.
+ */
+ public static final String NETWORKMONITOR_ASYNC_PRIVDNS_RESOLUTION =
+ "networkmonitor_async_privdns_resolution";
+
/**** BEGIN Feature Kill Switch Flags ****/
/**
@@ -350,9 +372,10 @@ public class NetworkStackUtils {
* Generate an IPv6 address based on the given prefix(/64) and stable interface
* identifier(EUI64).
*/
+ @Nullable
public static Inet6Address createInet6AddressFromEui64(@NonNull final IpPrefix prefix,
@NonNull final byte[] eui64) {
- if (prefix.getPrefixLength() != 64) {
+ if (prefix.getPrefixLength() > 64) {
Log.e(TAG, "Invalid IPv6 prefix length " + prefix.getPrefixLength());
return null;
}
diff --git a/src/com/android/server/connectivity/NetworkMonitor.java b/src/com/android/server/connectivity/NetworkMonitor.java
index 9303f956..78a47ea8 100755
--- a/src/com/android/server/connectivity/NetworkMonitor.java
+++ b/src/com/android/server/connectivity/NetworkMonitor.java
@@ -23,6 +23,9 @@ import static android.net.CaptivePortal.APP_RETURN_WANTED_AS_IS;
import static android.net.ConnectivityManager.EXTRA_CAPTIVE_PORTAL_PROBE_SPEC;
import static android.net.ConnectivityManager.EXTRA_CAPTIVE_PORTAL_URL;
import static android.net.DnsResolver.FLAG_EMPTY;
+import static android.net.DnsResolver.FLAG_NO_CACHE_LOOKUP;
+import static android.net.DnsResolver.TYPE_A;
+import static android.net.DnsResolver.TYPE_AAAA;
import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_INVALID;
import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY;
import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_VALID;
@@ -122,6 +125,7 @@ import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.os.Bundle;
+import android.os.CancellationSignal;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
@@ -140,7 +144,9 @@ import android.telephony.CellSignalStrength;
import android.telephony.SignalStrength;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
+import android.util.ArraySet;
import android.util.Log;
+import android.util.Pair;
import android.util.SparseArray;
import androidx.annotation.ArrayRes;
@@ -234,6 +240,10 @@ public class NetworkMonitor extends StateMachine {
@VisibleForTesting
static final String CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT =
"captive_portal_dns_probe_timeout";
+ @VisibleForTesting
+ static final String CONFIG_ASYNC_PRIVDNS_PROBE_TIMEOUT_MS =
+ "async_privdns_probe_timeout_ms";
+ private static final int DEFAULT_PRIVDNS_PROBE_TIMEOUT_MS = 10_000;
private static final int SOCKET_TIMEOUT_MS = 10000;
private static final int PROBE_TIMEOUT_MS = 3000;
@@ -404,6 +414,32 @@ public class NetworkMonitor extends StateMachine {
*/
private static final int EVENT_RESOURCE_CONFIG_CHANGED = 25;
+ /**
+ * Message to self to notify that private DNS strict mode hostname resolution has finished.
+ *
+ * <p>arg2 = Last DNS rcode.
+ * obj = Pair&lt;List&lt;InetAddress&gt;, DnsCallback&gt;: query results and DnsCallback used.
+ */
+ private static final int CMD_STRICT_MODE_RESOLUTION_COMPLETED = 26;
+
+ /**
+ * Message to self to notify that the private DNS probe has finished.
+ *
+ * <p>arg2 = Last DNS rcode.
+ * obj = Pair&lt;List&lt;InetAddress&gt;, DnsCallback&gt;: query results and DnsCallback used.
+ */
+ private static final int CMD_PRIVATE_DNS_PROBE_COMPLETED = 27;
+
+ /**
+ * Message to self to notify that private DNS hostname resolution or probing has failed.
+ */
+ private static final int CMD_PRIVATE_DNS_EVALUATION_FAILED = 28;
+
+ /**
+ * Message to self to notify that a DNS query has timed out.
+ */
+ private static final int CMD_DNS_TIMEOUT = 29;
+
// Start mReevaluateDelayMs at this value and double.
@VisibleForTesting
static final int INITIAL_REEVALUATE_DELAY_MS = 1000;
@@ -504,6 +540,10 @@ public class NetworkMonitor extends StateMachine {
private final State mEvaluatingState = new EvaluatingState();
private final State mCaptivePortalState = new CaptivePortalState();
private final State mEvaluatingPrivateDnsState = new EvaluatingPrivateDnsState();
+ private final State mStartingPrivateDnsEvaluation = new StartingPrivateDnsEvaluation();
+ private final State mResolvingPrivateDnsState = new ResolvingPrivateDnsState();
+ private final State mProbingForPrivateDnsState = new ProbingForPrivateDnsState();
+
private final State mProbingState = new ProbingState();
private final State mWaitingForNextProbeState = new WaitingForNextProbeState();
private final State mEvaluatingBandwidthState = new EvaluatingBandwidthState();
@@ -544,6 +584,8 @@ public class NetworkMonitor extends StateMachine {
private final boolean mMetricsEnabled;
private final boolean mReevaluateWhenResumeEnabled;
+ private final boolean mAsyncPrivdnsResolutionEnabled;
+
@NonNull
private final NetworkInformationShim mInfoShim = NetworkInformationShimImpl.newInstance();
@@ -614,6 +656,9 @@ public class NetworkMonitor extends StateMachine {
addState(mWaitingForNextProbeState, mEvaluatingState);
addState(mCaptivePortalState, mMaybeNotifyState);
addState(mEvaluatingPrivateDnsState, mDefaultState);
+ addState(mStartingPrivateDnsEvaluation, mEvaluatingPrivateDnsState);
+ addState(mResolvingPrivateDnsState, mEvaluatingPrivateDnsState);
+ addState(mProbingForPrivateDnsState, mEvaluatingPrivateDnsState);
addState(mEvaluatingBandwidthState, mDefaultState);
addState(mValidatedState, mDefaultState);
setInitialState(mDefaultState);
@@ -632,6 +677,8 @@ public class NetworkMonitor extends StateMachine {
NetworkStackUtils.VALIDATION_METRICS_VERSION);
mReevaluateWhenResumeEnabled = deps.isFeatureEnabled(
context, NetworkStackUtils.REEVALUATE_WHEN_RESUME);
+ mAsyncPrivdnsResolutionEnabled = deps.isFeatureEnabled(context,
+ NetworkStackUtils.NETWORKMONITOR_ASYNC_PRIVDNS_RESOLUTION);
mUseHttps = getUseHttpsValidation();
mCaptivePortalUserAgent = getCaptivePortalUserAgent();
mCaptivePortalFallbackSpecs =
@@ -1049,6 +1096,13 @@ public class NetworkMonitor extends StateMachine {
if (tst != null) {
tst.setOpportunisticMode(cfg.inOpportunisticMode());
}
+ if (mAsyncPrivdnsResolutionEnabled) {
+ // When using async privdns validation, reevaluate on any change of
+ // configuration (even if turning it off), as this will handle
+ // cancelling current attempts and transitioning to validated state.
+ removeMessages(CMD_EVALUATE_PRIVATE_DNS);
+ sendMessage(CMD_EVALUATE_PRIVATE_DNS);
+ }
break;
}
@@ -1599,13 +1653,28 @@ public class NetworkMonitor extends StateMachine {
@Override
public boolean processMessage(Message msg) {
switch (msg.what) {
- case CMD_EVALUATE_PRIVATE_DNS:
+ case CMD_EVALUATE_PRIVATE_DNS: {
+ if (mAsyncPrivdnsResolutionEnabled) {
+ // Cancel any previously scheduled retry attempt
+ removeMessages(CMD_EVALUATE_PRIVATE_DNS);
+
+ if (inStrictMode()) {
+ // Note this may happen even in the case where the current state is
+ // resolve or probe: private DNS evaluation would then restart.
+ transitionTo(mStartingPrivateDnsEvaluation);
+ } else {
+ mEvaluationState.removeProbeResult(NETWORK_VALIDATION_PROBE_PRIVDNS);
+ transitionToPrivateDnsEvaluationSuccessState();
+ }
+ break;
+ }
+
if (inStrictMode()) {
- if (!isStrictModeHostnameResolved()) {
+ if (!isStrictModeHostnameResolved(mPrivateDnsConfig)) {
resolveStrictModeHostname();
- if (isStrictModeHostnameResolved()) {
- notifyPrivateDnsConfigResolved();
+ if (isStrictModeHostnameResolved(mPrivateDnsConfig)) {
+ notifyPrivateDnsConfigResolved(mPrivateDnsConfig);
} else {
handlePrivateDnsEvaluationFailure();
// The private DNS probe fails-fast if the server hostname cannot
@@ -1630,23 +1699,24 @@ public class NetworkMonitor extends StateMachine {
handlePrivateDnsEvaluationFailure();
break;
}
- handlePrivateDnsEvaluationSuccess();
+ mEvaluationState.noteProbeResult(NETWORK_VALIDATION_PROBE_PRIVDNS,
+ true /* succeeded */);
} else {
mEvaluationState.removeProbeResult(NETWORK_VALIDATION_PROBE_PRIVDNS);
}
-
- if (needEvaluatingBandwidth()) {
- transitionTo(mEvaluatingBandwidthState);
- } else {
- // All good!
- transitionTo(mValidatedState);
- }
+ transitionToPrivateDnsEvaluationSuccessState();
break;
- case CMD_PRIVATE_DNS_SETTINGS_CHANGED:
+ }
+ case CMD_PRIVATE_DNS_SETTINGS_CHANGED: {
// When settings change the reevaluation timer must be reset.
mPrivateDnsReevalDelayMs = INITIAL_REEVALUATE_DELAY_MS;
// Let the message bubble up and be handled by parent states as usual.
return NOT_HANDLED;
+ }
+ // Only used with mAsyncPrivdnsResolutionEnabled
+ case CMD_PRIVATE_DNS_EVALUATION_FAILED: {
+ reschedulePrivateDnsEvaluation();
+ }
default:
return NOT_HANDLED;
}
@@ -1657,12 +1727,6 @@ public class NetworkMonitor extends StateMachine {
return !TextUtils.isEmpty(mPrivateDnsProviderHostname);
}
- private boolean isStrictModeHostnameResolved() {
- return (mPrivateDnsConfig != null)
- && mPrivateDnsConfig.hostname.equals(mPrivateDnsProviderHostname)
- && (mPrivateDnsConfig.ips.length > 0);
- }
-
private void resolveStrictModeHostname() {
try {
// Do a blocking DNS resolution using the network-assigned nameservers.
@@ -1675,24 +1739,15 @@ public class NetworkMonitor extends StateMachine {
}
}
- private void notifyPrivateDnsConfigResolved() {
- try {
- mCallback.notifyPrivateDnsConfigResolved(mPrivateDnsConfig.toParcel());
- } catch (RemoteException e) {
- Log.e(TAG, "Error sending private DNS config resolved notification", e);
- }
- }
-
- private void handlePrivateDnsEvaluationSuccess() {
- mEvaluationState.noteProbeResult(NETWORK_VALIDATION_PROBE_PRIVDNS,
- true /* succeeded */);
- }
-
private void handlePrivateDnsEvaluationFailure() {
mEvaluationState.noteProbeResult(NETWORK_VALIDATION_PROBE_PRIVDNS,
false /* succeeded */);
mEvaluationState.reportEvaluationResult(NETWORK_VALIDATION_RESULT_INVALID,
null /* redirectUrl */);
+ reschedulePrivateDnsEvaluation();
+ }
+
+ private void reschedulePrivateDnsEvaluation() {
// Queue up a re-evaluation with backoff.
//
// TODO: Consider abandoning this state after a few attempts and
@@ -1730,6 +1785,285 @@ public class NetworkMonitor extends StateMachine {
}
}
+ private void transitionToPrivateDnsEvaluationSuccessState() {
+ if (needEvaluatingBandwidth()) {
+ transitionTo(mEvaluatingBandwidthState);
+ } else {
+ // All good!
+ transitionTo(mValidatedState);
+ }
+ }
+
+ private class StartingPrivateDnsEvaluation extends State {
+ @Override
+ public void enter() {
+ transitionTo(mResolvingPrivateDnsState);
+ }
+ }
+
+ private class DnsCallback implements DnsResolver.Callback<List<InetAddress>> {
+ private final int mReplyMessage;
+ final CancellationSignal mCancellationSignal;
+ final boolean mHighPriorityResults;
+
+ DnsCallback(int replyMessage, boolean highPriorityResults) {
+ mReplyMessage = replyMessage;
+ mCancellationSignal = new CancellationSignal();
+ mHighPriorityResults = highPriorityResults;
+ }
+
+ @Override
+ public void onAnswer(List<InetAddress> answer, int rcode) {
+ sendMessage(mReplyMessage, 0, rcode, new Pair<>(answer, this));
+ }
+
+ @Override
+ public void onError(DnsResolver.DnsException error) {
+ sendMessage(mReplyMessage, 0, error.code, new Pair<>(null, this));
+ }
+ }
+
+ /**
+ * Base class for a state that is sending a DNS query, cancelled if the state is exited.
+ */
+ private abstract class DnsQueryState extends State {
+ private static final int ERROR_TIMEOUT = -1;
+ private final int mCompletedCommand;
+ private final ArraySet<DnsCallback> mPendingQueries = new ArraySet<>(2);
+ private final List<InetAddress> mResults = new ArrayList<>();
+ private String mQueryName;
+ private long mStartTime;
+
+ private DnsQueryState(int completedCommand) {
+ mCompletedCommand = completedCommand;
+ }
+
+ @Override
+ public void enter() {
+ mPendingQueries.clear();
+ mResults.clear();
+ mStartTime = SystemClock.elapsedRealtimeNanos();
+
+ mQueryName = getQueryName();
+ if (TextUtils.isEmpty(mQueryName)) {
+ // No query necessary (in particular not in strict mode): skip DNS query states
+ mEvaluationState.removeProbeResult(NETWORK_VALIDATION_PROBE_PRIVDNS);
+ transitionToPrivateDnsEvaluationSuccessState();
+ return;
+ }
+
+ final DnsResolver resolver = mDependencies.getDnsResolver();
+ mPendingQueries.addAll(sendQueries(mQueryName, resolver));
+ sendMessageDelayed(CMD_DNS_TIMEOUT, getTimeoutMs());
+ }
+
+ @Override
+ public void exit() {
+ removeMessages(CMD_DNS_TIMEOUT);
+ cancelAllQueries();
+ }
+
+ @Override
+ public boolean processMessage(Message msg) {
+ if (msg.what == mCompletedCommand) {
+ final Pair<List<InetAddress>, DnsCallback> result =
+ (Pair<List<InetAddress>, DnsCallback>) msg.obj;
+ if (!mPendingQueries.remove(result.second)) {
+ // Ignore previous queries if the state was exited and re-entered. This state
+ // calls cancelAllQueries on exit, but this may still happen if results were
+ // already posted when the querier processed the cancel request.
+ return HANDLED;
+ }
+
+ if (result.first != null) {
+ if (result.second.mHighPriorityResults) {
+ mResults.addAll(0, result.first);
+ } else {
+ mResults.addAll(result.first);
+ }
+ }
+
+ if (mPendingQueries.isEmpty()) {
+ removeMessages(CMD_DNS_TIMEOUT);
+ final long time = SystemClock.elapsedRealtimeNanos() - mStartTime;
+ onQueryDone(mQueryName, mResults, msg.arg2 /* lastRCode */, time);
+ }
+ return HANDLED;
+ } else if (msg.what == CMD_DNS_TIMEOUT) {
+ cancelAllQueries();
+ // If some queries were successful, onQueryDone will still proceed, even if
+ // lastRCode is not a success code.
+ onQueryDone(mQueryName, mResults, ERROR_TIMEOUT /* lastRCode */,
+ SystemClock.elapsedRealtimeNanos() - mStartTime);
+ return HANDLED;
+ }
+ return NOT_HANDLED;
+ }
+
+ private void cancelAllQueries() {
+ for (int i = 0; i < mPendingQueries.size(); i++) {
+ mPendingQueries.valueAt(i).mCancellationSignal.cancel();
+ }
+ mPendingQueries.clear();
+ }
+
+ abstract void onQueryDone(@NonNull String queryName, @NonNull List<InetAddress> answer,
+ int lastRCode, long elapsedNanos);
+
+ @NonNull
+ abstract String getQueryName();
+
+ abstract List<DnsCallback> sendQueries(@NonNull String queryName,
+ @NonNull DnsResolver resolver);
+
+ abstract long getTimeoutMs();
+ }
+
+ private class ResolvingPrivateDnsState extends DnsQueryState {
+ private ResolvingPrivateDnsState() {
+ super(CMD_STRICT_MODE_RESOLUTION_COMPLETED);
+ }
+
+ @Override
+ List<DnsCallback> sendQueries(@NonNull String queryName, @NonNull DnsResolver resolver) {
+ // Follow legacy behavior that sent AAAA and A queries synchronously in sequence: AAAA
+ // is marked as highPriorityResults, so they are placed first in the resulting list.
+ final DnsCallback v6Cb = new DnsCallback(CMD_STRICT_MODE_RESOLUTION_COMPLETED,
+ true /* highPriorityResults */);
+ final DnsCallback v4Cb = new DnsCallback(CMD_STRICT_MODE_RESOLUTION_COMPLETED,
+ false /* highPriorityResults */);
+
+ resolver.query(mCleartextDnsNetwork, queryName, TYPE_AAAA, FLAG_NO_CACHE_LOOKUP,
+ Runnable::run, v6Cb.mCancellationSignal, v6Cb);
+ resolver.query(mCleartextDnsNetwork, queryName, TYPE_A, FLAG_NO_CACHE_LOOKUP,
+ Runnable::run, v4Cb.mCancellationSignal, v4Cb);
+
+ return List.of(v6Cb, v4Cb);
+ }
+
+ @Override
+ void onQueryDone(@NonNull String queryName, @NonNull List<InetAddress> answer,
+ int lastRCode, long elapsedNanos) {
+ if (!Objects.equals(queryName, mPrivateDnsProviderHostname)) {
+ validationLog("Ignoring stale private DNS resolve answers for " + queryName
+ + " (now \"" + mPrivateDnsProviderHostname + "\"): " + answer);
+ // This may happen if mPrivateDnsProviderHostname was changed, in which case
+ // reevaluation must have been queued (CMD_EVALUATE_PRIVATE_DNS), but results for
+ // the first evaluation are received before the reevaluation command gets processed.
+ // Just ignore the results and wait for reevaluation to be processed.
+ // More generally, reevaluation is scheduled every time the hostname changes, so
+ // IP addresses matching the hostname are eventually received, but intermediate
+ // results should be ignored to avoid reporting a PrivateDnsConfig with IP addresses
+ // that don't match mPrivateDnsProviderHostname.
+ return;
+ }
+
+ if (!answer.isEmpty()) {
+ final InetAddress[] ips = answer.toArray(new InetAddress[0]);
+ final PrivateDnsConfig config =
+ new PrivateDnsConfig(mPrivateDnsProviderHostname, ips);
+ notifyPrivateDnsConfigResolved(config);
+
+ validationLog("Strict mode hostname resolution " + elapsedNanos + "ns OK "
+ + answer + " for " + mPrivateDnsProviderHostname);
+ transitionTo(mProbingForPrivateDnsState);
+ } else {
+ mEvaluationState.noteProbeResult(NETWORK_VALIDATION_PROBE_PRIVDNS,
+ false /* succeeded */);
+ mEvaluationState.reportEvaluationResult(NETWORK_VALIDATION_RESULT_INVALID,
+ null /* redirectUrl */);
+
+ validationLog("Strict mode hostname resolution " + elapsedNanos + "ns FAIL "
+ + "lastRCode " + lastRCode + " for " + mPrivateDnsProviderHostname);
+ sendMessage(CMD_PRIVATE_DNS_EVALUATION_FAILED);
+
+ // The private DNS probe fails-fast if the server hostname cannot
+ // be resolved. Record it as a failure with zero latency.
+ recordProbeEventMetrics(ProbeType.PT_PRIVDNS, 0 /* latency */,
+ ProbeResult.PR_FAILURE, null /* capportData */);
+ }
+ }
+
+ @NonNull
+ @Override
+ String getQueryName() {
+ return mPrivateDnsProviderHostname;
+ }
+
+ @Override
+ long getTimeoutMs() {
+ return getDnsProbeTimeout();
+ }
+ }
+
+ private class ProbingForPrivateDnsState extends DnsQueryState {
+ private ProbingForPrivateDnsState() {
+ super(CMD_PRIVATE_DNS_PROBE_COMPLETED);
+ }
+
+ @Override
+ public void enter() {
+ super.enter();
+ }
+
+ @Override
+ List<DnsCallback> sendQueries(@NonNull String queryName, @NonNull DnsResolver resolver) {
+ final DnsCallback cb = new DnsCallback(CMD_PRIVATE_DNS_PROBE_COMPLETED,
+ false /* highPriorityResults */);
+ resolver.query(mNetwork, queryName, FLAG_EMPTY, Runnable::run, cb.mCancellationSignal,
+ cb);
+ return Collections.singletonList(cb);
+ }
+
+ @Override
+ void onQueryDone(@NonNull String queryName, @NonNull List<InetAddress> answer,
+ int lastRCode, long elapsedNanos) {
+ final boolean success = !answer.isEmpty();
+ recordProbeEventMetrics(ProbeType.PT_PRIVDNS, elapsedNanos,
+ success ? ProbeResult.PR_SUCCESS :
+ ProbeResult.PR_FAILURE, null /* capportData */);
+ logValidationProbe(elapsedNanos, PROBE_PRIVDNS, success ? DNS_SUCCESS : DNS_FAILURE);
+
+ final String strIps = Objects.toString(answer);
+ validationLog(PROBE_PRIVDNS, queryName,
+ String.format("%dus: %s", elapsedNanos / 1000, strIps));
+
+ mEvaluationState.noteProbeResult(NETWORK_VALIDATION_PROBE_PRIVDNS, success);
+ if (success) {
+ transitionToPrivateDnsEvaluationSuccessState();
+ } else {
+ mEvaluationState.reportEvaluationResult(NETWORK_VALIDATION_RESULT_INVALID,
+ null /* redirectUrl */);
+ sendMessage(CMD_PRIVATE_DNS_EVALUATION_FAILED);
+ }
+ }
+
+ @Override
+ long getTimeoutMs() {
+ return getAsyncPrivateDnsProbeTimeout();
+ }
+
+ @NonNull
+ @Override
+ String getQueryName() {
+ return UUID.randomUUID().toString().substring(0, 8) + PRIVATE_DNS_PROBE_HOST_SUFFIX;
+ }
+ }
+
+ private boolean isStrictModeHostnameResolved(PrivateDnsConfig config) {
+ return (config != null)
+ && config.hostname.equals(mPrivateDnsProviderHostname)
+ && (config.ips.length > 0);
+ }
+
+ private void notifyPrivateDnsConfigResolved(@NonNull PrivateDnsConfig config) {
+ try {
+ mCallback.notifyPrivateDnsConfigResolved(config.toParcel());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error sending private DNS config resolved notification", e);
+ }
+ }
+
private class ProbingState extends State {
private Thread mThread;
@@ -2186,6 +2520,11 @@ public class NetworkMonitor extends StateMachine {
CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT, DEFAULT_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT);
}
+ private int getAsyncPrivateDnsProbeTimeout() {
+ return mDependencies.getDeviceConfigPropertyInt(NAMESPACE_CONNECTIVITY,
+ CONFIG_ASYNC_PRIVDNS_PROBE_TIMEOUT_MS, DEFAULT_PRIVDNS_PROBE_TIMEOUT_MS);
+ }
+
/**
* Gets an integer setting from resources or device config
*
diff --git a/tests/integration/AndroidManifest_root.xml b/tests/integration/AndroidManifest_root.xml
index 02b82c53..ffc859a4 100644
--- a/tests/integration/AndroidManifest_root.xml
+++ b/tests/integration/AndroidManifest_root.xml
@@ -16,6 +16,10 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.server.networkstack.roottests">
+ <!-- Used by requesting a network and sending packet on that network. -->
+ <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
+ <uses-permission android:name="android.permission.INTERNET" />
+
<application android:debuggable="true">
<uses-library android:name="android.test.runner" />
</application>
diff --git a/tests/integration/common/android/net/ip/IpClientIntegrationTestCommon.java b/tests/integration/common/android/net/ip/IpClientIntegrationTestCommon.java
index 2f1f7d16..72a6b4df 100644
--- a/tests/integration/common/android/net/ip/IpClientIntegrationTestCommon.java
+++ b/tests/integration/common/android/net/ip/IpClientIntegrationTestCommon.java
@@ -17,8 +17,6 @@
package android.net.ip;
import static android.Manifest.permission.MANAGE_TEST_NETWORKS;
-import static android.Manifest.permission.READ_DEVICE_CONFIG;
-import static android.Manifest.permission.WRITE_DEVICE_CONFIG;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
@@ -80,7 +78,7 @@ import static com.android.net.module.util.NetworkStackConstants.NEIGHBOR_ADVERTI
import static com.android.net.module.util.NetworkStackConstants.NEIGHBOR_ADVERTISEMENT_FLAG_SOLICITED;
import static com.android.net.module.util.NetworkStackConstants.PIO_FLAG_AUTONOMOUS;
import static com.android.net.module.util.NetworkStackConstants.PIO_FLAG_ON_LINK;
-import static com.android.net.module.util.NetworkStackConstants.RFC7421_PREFIX_LENGTH;
+import static com.android.networkstack.util.NetworkStackUtils.IP_REACHABILITY_ROUTER_MAC_CHANGE_FAILURE_ONLY_AFTER_ROAM_VERSION;
import static com.android.testutils.MiscAsserts.assertThrows;
import static com.android.testutils.ParcelUtils.parcelingRoundTrip;
import static com.android.testutils.TestPermissionUtil.runAsShell;
@@ -121,7 +119,6 @@ import static org.mockito.Mockito.when;
import android.app.AlarmManager;
import android.app.AlarmManager.OnAlarmListener;
import android.app.Instrumentation;
-import android.app.UiAutomation;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -181,11 +178,9 @@ import android.os.PowerManager;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
-import android.provider.DeviceConfig;
import android.stats.connectivity.NudEventType;
import android.system.ErrnoException;
import android.system.Os;
-import android.util.ArrayMap;
import android.util.Log;
import androidx.annotation.NonNull;
@@ -228,7 +223,6 @@ import com.android.server.NetworkObserverRegistry;
import com.android.server.NetworkStackService.NetworkStackServiceManager;
import com.android.testutils.CompatUtil;
import com.android.testutils.DevSdkIgnoreRule;
-import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import com.android.testutils.HandlerUtils;
import com.android.testutils.TapPacketReader;
@@ -272,7 +266,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
-import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.Random;
@@ -313,6 +306,10 @@ public abstract class IpClientIntegrationTestCommon {
protected static final long TEST_TIMEOUT_MS = 2_000L;
private static final long TEST_WAIT_ENOBUFS_TIMEOUT_MS = 30_000L;
private static final long TEST_WAIT_RENEW_REBIND_RETRANSMIT_MS = 15_000L;
+ // To prevent the flakiness about deprecationTime and expirationTime check, +/- 2s tolerance
+ // should be enough between the timestamp when the IP provisioning completes successfully and
+ // when IpClientLinkObserver sees the RTM_NEWADDR netlink events.
+ private static final long TEST_LIFETIME_TOLERANCE_MS = 2_000L;
@Rule
public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule();
@@ -482,8 +479,6 @@ public abstract class IpClientIntegrationTestCommon {
};
protected class Dependencies extends IpClient.Dependencies {
- // Can't use SparseIntArray, it doesn't have an easy way to know if a key is not present.
- private HashMap<String, Integer> mIntConfigProperties = new HashMap<>();
private DhcpClient mDhcpClient;
private Dhcp6Client mDhcp6Client;
private boolean mIsHostnameConfigurationEnabled;
@@ -557,12 +552,22 @@ public abstract class IpClientIntegrationTestCommon {
}
@Override
+ public int getDeviceConfigPropertyInt(String name, int ignoredDefaultValue) {
+ // Default is never used because all device config properties must be mocked by test.
+ try {
+ return Integer.parseInt(getDeviceConfigProperty(name));
+ } catch (NumberFormatException e) {
+ throw new IllegalStateException("Non-mocked device config property " + name);
+ }
+ }
+
+ @Override
public Dhcp6Client.Dependencies getDhcp6ClientDependencies() {
return new Dhcp6Client.Dependencies() {
@Override
public int getDeviceConfigPropertyInt(String name, int defaultValue) {
return Dependencies.this.getDeviceConfigPropertyInt(name,
- 0 /* default value */);
+ defaultValue);
}
};
}
@@ -584,7 +589,7 @@ public abstract class IpClientIntegrationTestCommon {
@Override
public int getIntDeviceConfig(final String name, int minimumValue,
int maximumValue, int defaultValue) {
- return getDeviceConfigPropertyInt(name, 0 /* default value */);
+ return Dependencies.this.getDeviceConfigPropertyInt(name, defaultValue);
}
@Override
@@ -634,19 +639,6 @@ public abstract class IpClientIntegrationTestCommon {
}
@Override
- public int getDeviceConfigPropertyInt(String name, int defaultValue) {
- Integer value = mIntConfigProperties.get(name);
- if (value == null) {
- throw new IllegalStateException("Non-mocked device config property " + name);
- }
- return value;
- }
-
- public void setDeviceConfigProperty(String name, int value) {
- mIntConfigProperties.put(name, value);
- }
-
- @Override
public NetworkQuirkMetrics getNetworkQuirkMetrics() {
return new NetworkQuirkMetrics(mNetworkQuirkMetricsDeps);
}
@@ -656,7 +648,26 @@ public abstract class IpClientIntegrationTestCommon {
protected abstract IIpClient makeIIpClient(
@NonNull String ifaceName, @NonNull IIpClientCallbacks cb);
- protected abstract void setFeatureEnabled(String name, boolean enabled);
+ // In production. features are enabled if the flag is lower than the package version.
+ // For testing, we can just use 1 for enabled and -1 for disabled or chickened out.
+ static final String FEATURE_ENABLED = "1";
+ static final String FEATURE_DISABLED = "-1";
+
+ final void setFeatureEnabled(String feature, boolean enabled) {
+ setDeviceConfigProperty(feature, enabled ? FEATURE_ENABLED : FEATURE_DISABLED);
+ }
+
+ final void setFeatureChickenedOut(String feature, boolean chickenedOut) {
+ setDeviceConfigProperty(feature, chickenedOut ? FEATURE_DISABLED : FEATURE_ENABLED);
+ }
+
+ final void setDeviceConfigProperty(String name, int value) {
+ setDeviceConfigProperty(name, Integer.toString(value));
+ }
+
+ protected abstract void setDeviceConfigProperty(String name, String value);
+
+ protected abstract String getDeviceConfigProperty(String name);
protected abstract boolean isFeatureEnabled(String name);
@@ -681,36 +692,8 @@ public abstract class IpClientIntegrationTestCommon {
return !useNetworkStackSignature() && mIsSignatureRequiredTest;
}
- private ArrayMap<String, String> mOriginalPropertyValues = new ArrayMap<>();
-
- protected void setDeviceConfigProperty(String name, String value) {
- final UiAutomation am = InstrumentationRegistry.getInstrumentation().getUiAutomation();
- am.adoptShellPermissionIdentity(READ_DEVICE_CONFIG, WRITE_DEVICE_CONFIG);
- try {
- // Do not use computeIfAbsent as it would overwrite null values,
- // property originally unset.
- if (!mOriginalPropertyValues.containsKey(name)) {
- mOriginalPropertyValues.put(name,
- DeviceConfig.getProperty(DeviceConfig.NAMESPACE_CONNECTIVITY, name));
- }
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_CONNECTIVITY, name, value,
- false /* makeDefault */);
- } finally {
- am.dropShellPermissionIdentity();
- }
- }
-
- protected void setDeviceConfigProperty(String name, int value) {
- setDeviceConfigProperty(name, Integer.toString(value));
- }
-
- private void setFeatureChickenedOut(String name, boolean chickenedOut) {
- setDeviceConfigProperty(name, chickenedOut ? "-1" : "0");
- }
-
- protected void setDhcpFeatures(final boolean isDhcpLeaseCacheEnabled,
- final boolean isRapidCommitEnabled, final boolean isDhcpIpConflictDetectEnabled) {
- setFeatureEnabled(NetworkStackUtils.DHCP_INIT_REBOOT_VERSION, isDhcpLeaseCacheEnabled);
+ private void setDhcpFeatures(final boolean isRapidCommitEnabled,
+ final boolean isDhcpIpConflictDetectEnabled) {
setFeatureEnabled(NetworkStackUtils.DHCP_RAPID_COMMIT_VERSION, isRapidCommitEnabled);
setFeatureEnabled(NetworkStackUtils.DHCP_IP_CONFLICT_DETECT_VERSION,
isDhcpIpConflictDetectEnabled);
@@ -832,28 +815,28 @@ public abstract class IpClientIntegrationTestCommon {
when(mPackageManager.getPackagesForUid(TEST_DEVICE_OWNER_APP_UID)).thenReturn(
new String[] { TEST_DEVICE_OWNER_APP_PACKAGE });
- mDependencies.setDeviceConfigProperty(IpClient.CONFIG_MIN_RDNSS_LIFETIME, 67);
- mDependencies.setDeviceConfigProperty(DhcpClient.DHCP_RESTART_CONFIG_DELAY, 10);
- mDependencies.setDeviceConfigProperty(DhcpClient.ARP_FIRST_PROBE_DELAY_MS, 10);
- mDependencies.setDeviceConfigProperty(DhcpClient.ARP_PROBE_MIN_MS, 10);
- mDependencies.setDeviceConfigProperty(DhcpClient.ARP_PROBE_MAX_MS, 20);
- mDependencies.setDeviceConfigProperty(DhcpClient.ARP_FIRST_ANNOUNCE_DELAY_MS, 10);
- mDependencies.setDeviceConfigProperty(DhcpClient.ARP_ANNOUNCE_INTERVAL_MS, 10);
+ setDeviceConfigProperty(IpClient.CONFIG_MIN_RDNSS_LIFETIME, 67);
+ setDeviceConfigProperty(DhcpClient.DHCP_RESTART_CONFIG_DELAY, 10);
+ setDeviceConfigProperty(DhcpClient.ARP_FIRST_PROBE_DELAY_MS, 10);
+ setDeviceConfigProperty(DhcpClient.ARP_PROBE_MIN_MS, 10);
+ setDeviceConfigProperty(DhcpClient.ARP_PROBE_MAX_MS, 20);
+ setDeviceConfigProperty(DhcpClient.ARP_FIRST_ANNOUNCE_DELAY_MS, 10);
+ setDeviceConfigProperty(DhcpClient.ARP_ANNOUNCE_INTERVAL_MS, 10);
// Set the initial netlink socket receive buffer size to a minimum of 100KB to ensure test
// cases are still working, meanwhile in order to easily overflow the receive buffer by
// sending as few RAs as possible for test case where it's used to verify ENOBUFS.
- mDependencies.setDeviceConfigProperty(CONFIG_SOCKET_RECV_BUFSIZE, 100 * 1024);
+ setDeviceConfigProperty(CONFIG_SOCKET_RECV_BUFSIZE, 100 * 1024);
// Set the timeout to wait IPv6 autoconf to complete.
- mDependencies.setDeviceConfigProperty(CONFIG_IPV6_AUTOCONF_TIMEOUT, 500);
+ setDeviceConfigProperty(CONFIG_IPV6_AUTOCONF_TIMEOUT, 500);
// Set the minimal RA lifetime value, any RA section with liftime below this value will be
// ignored.
- mDependencies.setDeviceConfigProperty(CONFIG_ACCEPT_RA_MIN_LFT, DEFAULT_ACCEPT_RA_MIN_LFT);
+ setDeviceConfigProperty(CONFIG_ACCEPT_RA_MIN_LFT, DEFAULT_ACCEPT_RA_MIN_LFT);
// Set the polling interval to update APF data snapshot.
- mDependencies.setDeviceConfigProperty(CONFIG_APF_COUNTER_POLLING_INTERVAL_SECS,
+ setDeviceConfigProperty(CONFIG_APF_COUNTER_POLLING_INTERVAL_SECS,
DEFAULT_APF_COUNTER_POLLING_INTERVAL_SECS);
}
@@ -876,22 +859,6 @@ public abstract class IpClientIntegrationTestCommon {
awaitIpClientShutdown();
}
- @After
- public void tearDownDeviceConfigProperties() {
- if (testSkipped()) return;
- final UiAutomation am = InstrumentationRegistry.getInstrumentation().getUiAutomation();
- am.adoptShellPermissionIdentity(READ_DEVICE_CONFIG, WRITE_DEVICE_CONFIG);
- try {
- for (String key : mOriginalPropertyValues.keySet()) {
- if (key == null) continue;
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_CONNECTIVITY, key,
- mOriginalPropertyValues.get(key), false /* makeDefault */);
- }
- } finally {
- am.dropShellPermissionIdentity();
- }
- }
-
private void setUpTapInterface() throws Exception {
final Instrumentation inst = InstrumentationRegistry.getInstrumentation();
final TestNetworkInterface iface = runAsShell(MANAGE_TEST_NETWORKS, () -> {
@@ -1230,10 +1197,12 @@ public abstract class IpClientIntegrationTestCommon {
mIIpClient.startProvisioning(cfg.toStableParcelable());
}
- private void startIpClientProvisioning(final boolean isDhcpLeaseCacheEnabled,
- final boolean shouldReplyRapidCommitAck, final boolean isPreconnectionEnabled,
- final boolean isDhcpIpConflictDetectEnabled, final String displayName,
- final ScanResultInfo scanResultInfo, final Layer2Information layer2Info)
+ private void startIpClientProvisioning(final boolean shouldReplyRapidCommitAck,
+ final boolean isPreconnectionEnabled,
+ final boolean isDhcpIpConflictDetectEnabled,
+ final String displayName,
+ final ScanResultInfo scanResultInfo,
+ final Layer2Information layer2Info)
throws Exception {
ProvisioningConfiguration.Builder prov = new ProvisioningConfiguration.Builder()
.withoutIpReachabilityMonitor()
@@ -1246,8 +1215,7 @@ public abstract class IpClientIntegrationTestCommon {
if (displayName != null) prov.withDisplayName(displayName);
if (scanResultInfo != null) prov.withScanResultInfo(scanResultInfo);
- setDhcpFeatures(isDhcpLeaseCacheEnabled, shouldReplyRapidCommitAck,
- isDhcpIpConflictDetectEnabled);
+ setDhcpFeatures(shouldReplyRapidCommitAck, isDhcpIpConflictDetectEnabled);
startIpClientProvisioning(prov.build());
if (!isPreconnectionEnabled) {
@@ -1256,10 +1224,10 @@ public abstract class IpClientIntegrationTestCommon {
verify(mCb, never()).onProvisioningFailure(any());
}
- private void startIpClientProvisioning(final boolean isDhcpLeaseCacheEnabled,
- final boolean isDhcpRapidCommitEnabled, final boolean isPreconnectionEnabled,
+ private void startIpClientProvisioning(final boolean isDhcpRapidCommitEnabled,
+ final boolean isPreconnectionEnabled,
final boolean isDhcpIpConflictDetectEnabled) throws Exception {
- startIpClientProvisioning(isDhcpLeaseCacheEnabled, isDhcpRapidCommitEnabled,
+ startIpClientProvisioning(isDhcpRapidCommitEnabled,
isPreconnectionEnabled, isDhcpIpConflictDetectEnabled,
null /* displayName */, null /* ScanResultInfo */, null /* layer2Info */);
}
@@ -1311,13 +1279,12 @@ public abstract class IpClientIntegrationTestCommon {
// Helper method to complete DHCP 2-way or 4-way handshake
private List<DhcpPacket> performDhcpHandshake(final boolean isSuccessLease,
- final Integer leaseTimeSec, final boolean isDhcpLeaseCacheEnabled,
- final boolean shouldReplyRapidCommitAck, final int mtu,
+ final Integer leaseTimeSec, final boolean shouldReplyRapidCommitAck, final int mtu,
final boolean isDhcpIpConflictDetectEnabled,
final String captivePortalApiUrl, final String displayName,
final ScanResultInfo scanResultInfo, final Layer2Information layer2Info)
throws Exception {
- startIpClientProvisioning(isDhcpLeaseCacheEnabled, shouldReplyRapidCommitAck,
+ startIpClientProvisioning(shouldReplyRapidCommitAck,
false /* isPreconnectionEnabled */, isDhcpIpConflictDetectEnabled,
displayName, scanResultInfo, layer2Info);
return handleDhcpPackets(isSuccessLease, leaseTimeSec, shouldReplyRapidCommitAck, mtu,
@@ -1371,18 +1338,17 @@ public abstract class IpClientIntegrationTestCommon {
}
private List<DhcpPacket> performDhcpHandshake(final boolean isSuccessLease,
- final Integer leaseTimeSec, final boolean isDhcpLeaseCacheEnabled,
- final boolean isDhcpRapidCommitEnabled, final int mtu,
+ final Integer leaseTimeSec, final boolean isDhcpRapidCommitEnabled, final int mtu,
final boolean isDhcpIpConflictDetectEnabled) throws Exception {
- return performDhcpHandshake(isSuccessLease, leaseTimeSec, isDhcpLeaseCacheEnabled,
- isDhcpRapidCommitEnabled, mtu, isDhcpIpConflictDetectEnabled,
+ return performDhcpHandshake(isSuccessLease, leaseTimeSec, isDhcpRapidCommitEnabled,
+ mtu, isDhcpIpConflictDetectEnabled,
null /* captivePortalApiUrl */, null /* displayName */, null /* scanResultInfo */,
null /* layer2Info */);
}
private List<DhcpPacket> performDhcpHandshake() throws Exception {
return performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
- false /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */,
+ false /* shouldReplyRapidCommitAck */,
TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */);
}
@@ -1430,8 +1396,8 @@ public abstract class IpClientIntegrationTestCommon {
.onNetworkAttributesRetrieved(new Status(SUCCESS), TEST_L2KEY, na);
return null;
}).when(mIpMemoryStore).retrieveNetworkAttributes(eq(TEST_L2KEY), any());
- startIpClientProvisioning(true /* isDhcpLeaseCacheEnabled */,
- false /* shouldReplyRapidCommitAck */, false /* isPreconnectionEnabled */,
+ startIpClientProvisioning(false /* shouldReplyRapidCommitAck */,
+ false /* isPreconnectionEnabled */,
false /* isDhcpIpConflictDetectEnabled */);
return getNextDhcpPacket();
}
@@ -1477,7 +1443,7 @@ public abstract class IpClientIntegrationTestCommon {
if (shouldChangeMtu) mtu = TEST_MIN_MTU;
performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
- true /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */,
+ false /* shouldReplyRapidCommitAck */,
mtu, false /* isDhcpIpConflictDetectEnabled */);
verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, mtu);
@@ -1529,11 +1495,8 @@ public abstract class IpClientIntegrationTestCommon {
final boolean shouldFirePreconnectionTimeout,
final boolean timeoutBeforePreconnectionComplete) throws Exception {
final long currentTime = System.currentTimeMillis();
- final ArgumentCaptor<InterfaceConfigurationParcel> ifConfig =
- ArgumentCaptor.forClass(InterfaceConfigurationParcel.class);
-
- startIpClientProvisioning(true /* isDhcpLeaseCacheEnabled */,
- shouldReplyRapidCommitAck, true /* isDhcpPreConnectionEnabled */,
+ startIpClientProvisioning(shouldReplyRapidCommitAck,
+ true /* isDhcpPreConnectionEnabled */,
false /* isDhcpIpConflictDetectEnabled */);
DhcpPacket packet = assertDiscoverPacketOnPreconnectionStart();
final int preconnDiscoverTransId = packet.getTransactionId();
@@ -1588,12 +1551,7 @@ public abstract class IpClientIntegrationTestCommon {
}
}
verify(mCb, timeout(TEST_TIMEOUT_MS)).setFallbackMulticastFilter(true);
-
- final LinkAddress ipAddress = new LinkAddress(CLIENT_ADDR, PREFIX_LENGTH);
- verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceSetCfg(ifConfig.capture());
- assertEquals(ifConfig.getValue().ifName, mIfaceName);
- assertEquals(ifConfig.getValue().ipv4Addr, ipAddress.getAddress().getHostAddress());
- assertEquals(ifConfig.getValue().prefixLength, PREFIX_LENGTH);
+ verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU);
}
@@ -1651,7 +1609,7 @@ public abstract class IpClientIntegrationTestCommon {
final long currentTime = System.currentTimeMillis();
performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
- true /* isDhcpLeaseCacheEnabled */, shouldReplyRapidCommitAck,
+ shouldReplyRapidCommitAck,
TEST_DEFAULT_MTU, isDhcpIpConflictDetectEnabled);
// If we receive an ARP packet here, it's guaranteed to be from IP conflict detection,
@@ -1712,8 +1670,8 @@ public abstract class IpClientIntegrationTestCommon {
@Test
public void testDhcpInit() throws Exception {
- startIpClientProvisioning(false /* isDhcpLeaseCacheEnabled */,
- false /* shouldReplyRapidCommitAck */, false /* isPreconnectionEnabled */,
+ startIpClientProvisioning(false /* shouldReplyRapidCommitAck */,
+ false /* isPreconnectionEnabled */,
false /* isDhcpIpConflictDetectEnabled */);
final DhcpPacket packet = getNextDhcpPacket();
assertTrue(packet instanceof DhcpDiscoverPacket);
@@ -1723,7 +1681,7 @@ public abstract class IpClientIntegrationTestCommon {
public void testHandleSuccessDhcpLease() throws Exception {
final long currentTime = System.currentTimeMillis();
performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
- true /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */,
+ false /* shouldReplyRapidCommitAck */,
TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */);
verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU);
@@ -1732,7 +1690,7 @@ public abstract class IpClientIntegrationTestCommon {
@Test
public void testHandleFailureDhcpLease() throws Exception {
performDhcpHandshake(false /* isSuccessLease */, TEST_LEASE_DURATION_S,
- true /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */,
+ false /* shouldReplyRapidCommitAck */,
TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */);
verify(mCb, never()).onProvisioningSuccess(any());
@@ -1743,7 +1701,7 @@ public abstract class IpClientIntegrationTestCommon {
public void testHandleInfiniteLease() throws Exception {
final long currentTime = System.currentTimeMillis();
performDhcpHandshake(true /* isSuccessLease */, INFINITE_LEASE,
- true /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */,
+ false /* shouldReplyRapidCommitAck */,
TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */);
verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
assertIpMemoryStoreNetworkAttributes(INFINITE_LEASE, currentTime, TEST_DEFAULT_MTU);
@@ -1753,26 +1711,17 @@ public abstract class IpClientIntegrationTestCommon {
public void testHandleNoLease() throws Exception {
final long currentTime = System.currentTimeMillis();
performDhcpHandshake(true /* isSuccessLease */, null /* no lease time */,
- true /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */,
+ false /* shouldReplyRapidCommitAck */,
TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */);
verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
assertIpMemoryStoreNetworkAttributes(null, currentTime, TEST_DEFAULT_MTU);
}
- @Test @IgnoreAfter(Build.VERSION_CODES.Q) // INIT-REBOOT is enabled on R.
- public void testHandleDisableInitRebootState() throws Exception {
- performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
- false /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */,
- TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */);
- verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
- assertIpMemoryNeverStoreNetworkAttributes();
- }
-
@Test
public void testHandleRapidCommitOption() throws Exception {
final long currentTime = System.currentTimeMillis();
performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
- true /* isDhcpLeaseCacheEnabled */, true /* shouldReplyRapidCommitAck */,
+ true /* shouldReplyRapidCommitAck */,
TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */);
verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU);
@@ -1780,8 +1729,8 @@ public abstract class IpClientIntegrationTestCommon {
@Test @IgnoreUpTo(Build.VERSION_CODES.Q)
public void testRollbackFromRapidCommitOption() throws Exception {
- startIpClientProvisioning(false /* isDhcpLeaseCacheEnabled */,
- true /* isDhcpRapidCommitEnabled */, false /* isPreConnectionEnabled */,
+ startIpClientProvisioning(true /* isDhcpRapidCommitEnabled */,
+ false /* isPreConnectionEnabled */,
false /* isDhcpIpConflictDetectEnabled */);
final List<DhcpPacket> discoverList = new ArrayList<DhcpPacket>();
@@ -1857,8 +1806,8 @@ public abstract class IpClientIntegrationTestCommon {
@Test
public void testDhcpClientRapidCommitEnabled() throws Exception {
- startIpClientProvisioning(false /* isDhcpLeaseCacheEnabled */,
- true /* shouldReplyRapidCommitAck */, false /* isPreconnectionEnabled */,
+ startIpClientProvisioning(true /* shouldReplyRapidCommitAck */,
+ false /* isPreconnectionEnabled */,
false /* isDhcpIpConflictDetectEnabled */);
final DhcpPacket packet = getNextDhcpPacket();
assertTrue(packet instanceof DhcpDiscoverPacket);
@@ -1925,8 +1874,7 @@ public abstract class IpClientIntegrationTestCommon {
final long currentTime = System.currentTimeMillis();
setFeatureEnabled(NetworkStackUtils.DHCP_SLOW_RETRANSMISSION_VERSION, true);
performDhcpHandshake(true /* isSuccessLease */,
- TEST_LEASE_DURATION_S, true /* isDhcpLeaseCacheEnabled */,
- false /* isDhcpRapidCommitEnabled */, TEST_DEFAULT_MTU,
+ TEST_LEASE_DURATION_S, false /* isDhcpRapidCommitEnabled */, TEST_DEFAULT_MTU,
false /* isDhcpIpConflictDetectEnabled */);
final LinkProperties lp =
verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
@@ -2030,7 +1978,7 @@ public abstract class IpClientIntegrationTestCommon {
long currentTime = System.currentTimeMillis();
performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
- true /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */,
+ false /* shouldReplyRapidCommitAck */,
TEST_MIN_MTU, false /* isDhcpIpConflictDetectEnabled */);
verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_MIN_MTU);
@@ -2047,7 +1995,7 @@ public abstract class IpClientIntegrationTestCommon {
currentTime = System.currentTimeMillis();
// Intend to set mtu option to 0, then verify that won't influence interface mtu restore.
performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
- true /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */,
+ false /* shouldReplyRapidCommitAck */,
0 /* mtu */, false /* isDhcpIpConflictDetectEnabled */);
verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, 0 /* mtu */);
@@ -2535,7 +2483,7 @@ public abstract class IpClientIntegrationTestCommon {
private void doIPv4OnlyProvisioningAndExitWithLeftAddress() throws Exception {
final long currentTime = System.currentTimeMillis();
performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
- true /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */,
+ false /* shouldReplyRapidCommitAck */,
TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */);
verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU);
@@ -2583,8 +2531,8 @@ public abstract class IpClientIntegrationTestCommon {
// Enter ClearingIpAddressesState to clear the remaining IPv4 addresses and transition to
// PreconnectionState instead of RunningState.
- startIpClientProvisioning(false /* isDhcpLeaseCacheEnabled */,
- false /* shouldReplyRapidCommitAck */, true /* isDhcpPreConnectionEnabled */,
+ startIpClientProvisioning(false /* shouldReplyRapidCommitAck */,
+ true /* isDhcpPreConnectionEnabled */,
false /* isDhcpIpConflictDetectEnabled */);
assertDiscoverPacketOnPreconnectionStart();
@@ -2710,7 +2658,7 @@ public abstract class IpClientIntegrationTestCommon {
.withoutIpReachabilityMonitor()
.withPreconnection()
.build();
- setDhcpFeatures(false /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */,
+ setDhcpFeatures(false /* shouldReplyRapidCommitAck */,
false /* isDhcpIpConflictDetectEnabled */);
startIpClientProvisioning(config);
assertDiscoverPacketOnPreconnectionStart();
@@ -2741,8 +2689,8 @@ public abstract class IpClientIntegrationTestCommon {
// Start provisioning again to verify IpClient can process CMD_START correctly at
// StoppedState.
- startIpClientProvisioning(false /* isDhcpLeaseCacheEnabled */,
- false /* shouldReplyRapidCommitAck */, false /* isPreConnectionEnabled */,
+ startIpClientProvisioning(false /* shouldReplyRapidCommitAck */,
+ false /* isPreConnectionEnabled */,
false /* isDhcpIpConflictDetectEnabled */);
final DhcpPacket discover = getNextDhcpPacket();
assertTrue(discover instanceof DhcpDiscoverPacket);
@@ -2811,8 +2759,7 @@ public abstract class IpClientIntegrationTestCommon {
final long currentTime = System.currentTimeMillis();
final List<DhcpPacket> sentPackets = performDhcpHandshake(true /* isSuccessLease */,
- TEST_LEASE_DURATION_S, true /* isDhcpLeaseCacheEnabled */,
- false /* isDhcpRapidCommitEnabled */, TEST_DEFAULT_MTU,
+ TEST_LEASE_DURATION_S, false /* isDhcpRapidCommitEnabled */, TEST_DEFAULT_MTU,
false /* isDhcpIpConflictDetectEnabled */);
assertEquals(2, sentPackets.size());
@@ -2828,8 +2775,7 @@ public abstract class IpClientIntegrationTestCommon {
final long currentTime = System.currentTimeMillis();
final List<DhcpPacket> sentPackets = performDhcpHandshake(true /* isSuccessLease */,
- TEST_LEASE_DURATION_S, true /* isDhcpLeaseCacheEnabled */,
- false /* isDhcpRapidCommitEnabled */, TEST_DEFAULT_MTU,
+ TEST_LEASE_DURATION_S, false /* isDhcpRapidCommitEnabled */, TEST_DEFAULT_MTU,
false /* isDhcpIpConflictDetectEnabled */);
assertEquals(2, sentPackets.size());
@@ -2845,8 +2791,7 @@ public abstract class IpClientIntegrationTestCommon {
final long currentTime = System.currentTimeMillis();
final List<DhcpPacket> sentPackets = performDhcpHandshake(true /* isSuccessLease */,
- TEST_LEASE_DURATION_S, true /* isDhcpLeaseCacheEnabled */,
- false /* isDhcpRapidCommitEnabled */, TEST_DEFAULT_MTU,
+ TEST_LEASE_DURATION_S, false /* isDhcpRapidCommitEnabled */, TEST_DEFAULT_MTU,
false /* isDhcpIpConflictDetectEnabled */);
assertEquals(2, sentPackets.size());
@@ -2858,8 +2803,8 @@ public abstract class IpClientIntegrationTestCommon {
private LinkProperties runDhcpClientCaptivePortalApiTest(boolean featureEnabled,
boolean serverSendsOption) throws Exception {
- startIpClientProvisioning(false /* isDhcpLeaseCacheEnabled */,
- false /* shouldReplyRapidCommitAck */, false /* isPreConnectionEnabled */,
+ startIpClientProvisioning(false /* shouldReplyRapidCommitAck */,
+ false /* isPreConnectionEnabled */,
false /* isDhcpIpConflictDetectEnabled */);
final DhcpPacket discover = getNextDhcpPacket();
assertTrue(discover instanceof DhcpDiscoverPacket);
@@ -2968,8 +2913,7 @@ public abstract class IpClientIntegrationTestCommon {
data);
final long currentTime = System.currentTimeMillis();
final List<DhcpPacket> sentPackets = performDhcpHandshake(true /* isSuccessLease */,
- TEST_LEASE_DURATION_S, true /* isDhcpLeaseCacheEnabled */,
- false /* isDhcpRapidCommitEnabled */, TEST_DEFAULT_MTU,
+ TEST_LEASE_DURATION_S, false /* isDhcpRapidCommitEnabled */, TEST_DEFAULT_MTU,
false /* isDhcpIpConflictDetectEnabled */,
null /* captivePortalApiUrl */, displayName, info /* scanResultInfo */,
null /* layer2Info */);
@@ -3083,7 +3027,7 @@ public abstract class IpClientIntegrationTestCommon {
mDependencies.setHostnameConfiguration(true /* isHostnameConfigurationEnabled */,
null /* hostname */);
performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
- true /* isDhcpLeaseCacheEnabled */, false /* isDhcpRapidCommitEnabled */,
+ false /* isDhcpRapidCommitEnabled */,
TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */,
null /* captivePortalApiUrl */, displayName, null /* scanResultInfo */,
layer2Info);
@@ -3236,8 +3180,7 @@ public abstract class IpClientIntegrationTestCommon {
// Enable rapid commit to accelerate DHCP handshake to shorten test duration,
// not strictly necessary.
- setDhcpFeatures(false /* isDhcpLeaseCacheEnabled */, true /* isRapidCommitEnabled */,
- false /* isDhcpIpConflictDetectEnabled */);
+ setDhcpFeatures(true /* isRapidCommitEnabled */, false /* isDhcpIpConflictDetectEnabled */);
// Both signature and root tests can use this function to do dual-stack provisioning.
if (useNetworkStackSignature()) {
mIpc.startProvisioning(config);
@@ -3340,7 +3283,7 @@ public abstract class IpClientIntegrationTestCommon {
final ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
.withoutIpReachabilityMonitor()
.build();
- setDhcpFeatures(false /* isDhcpLeaseCacheEnabled */, false /* isRapidCommitEnabled */,
+ setDhcpFeatures(false /* isRapidCommitEnabled */,
false /* isDhcpIpConflictDetectEnabled */);
startIpClientProvisioning(config);
@@ -3404,8 +3347,7 @@ public abstract class IpClientIntegrationTestCommon {
.withPreconnection()
.build();
- setDhcpFeatures(false /* isDhcpLeaseCacheEnabled */, true /* isRapidCommitEnabled */,
- false /* isDhcpIpConflictDetectEnabled */);
+ setDhcpFeatures(true /* isRapidCommitEnabled */, false /* isDhcpIpConflictDetectEnabled */);
startIpClientProvisioning(config);
final DhcpPacket packet = assertDiscoverPacketOnPreconnectionStart();
@@ -3460,7 +3402,7 @@ public abstract class IpClientIntegrationTestCommon {
MacAddress.fromString(TEST_DEFAULT_BSSID)))
.build();
- setDhcpFeatures(true /* isDhcpLeaseCacheEnabled */, false /* isRapidCommitEnabled */,
+ setDhcpFeatures(false /* isRapidCommitEnabled */,
false /* isDhcpIpConflictDetectEnabled */);
startIpClientProvisioning(config);
@@ -3586,7 +3528,7 @@ public abstract class IpClientIntegrationTestCommon {
);
private DhcpPacket doCustomizedDhcpOptionsTest(final List<DhcpOption> options,
- final ScanResultInfo info, boolean isDhcpLeaseCacheEnabled) throws Exception {
+ final ScanResultInfo info) throws Exception {
ProvisioningConfiguration.Builder prov = new ProvisioningConfiguration.Builder()
.withoutIpReachabilityMonitor()
.withLayer2Information(new Layer2Information(TEST_L2KEY, TEST_CLUSTER,
@@ -3595,7 +3537,7 @@ public abstract class IpClientIntegrationTestCommon {
.withDhcpOptions(options)
.withoutIPv6();
- setDhcpFeatures(isDhcpLeaseCacheEnabled, false /* isRapidCommitEnabled */,
+ setDhcpFeatures(false /* isRapidCommitEnabled */,
false /* isDhcpIpConflictDetectEnabled */);
startIpClientProvisioning(prov.build());
@@ -3609,8 +3551,7 @@ public abstract class IpClientIntegrationTestCommon {
public void testDiscoverCustomizedDhcpOptions() throws Exception {
final ScanResultInfo info = makeScanResultInfo(TEST_VENDOR_SPECIFIC_IE_ID, TEST_OEM_OUI,
TEST_VENDOR_SPECIFIC_IE_TYPE);
- final DhcpPacket packet = doCustomizedDhcpOptionsTest(TEST_OEM_DHCP_OPTIONS, info,
- false /* isDhcpLeaseCacheEnabled */);
+ final DhcpPacket packet = doCustomizedDhcpOptionsTest(TEST_OEM_DHCP_OPTIONS, info);
assertTrue(packet instanceof DhcpDiscoverPacket);
assertEquals(packet.mVendorId, TEST_OEM_VENDOR_ID);
@@ -3621,8 +3562,7 @@ public abstract class IpClientIntegrationTestCommon {
public void testDiscoverCustomizedDhcpOptions_nullDhcpOptions() throws Exception {
final ScanResultInfo info = makeScanResultInfo(TEST_VENDOR_SPECIFIC_IE_ID, TEST_OEM_OUI,
TEST_VENDOR_SPECIFIC_IE_TYPE);
- final DhcpPacket packet = doCustomizedDhcpOptionsTest(null /* options */, info,
- false /* isDhcpLeaseCacheEnabled */);
+ final DhcpPacket packet = doCustomizedDhcpOptionsTest(null /* options */, info);
assertTrue(packet instanceof DhcpDiscoverPacket);
assertEquals(packet.mVendorId, new String("android-dhcp-" + Build.VERSION.RELEASE));
@@ -3632,7 +3572,7 @@ public abstract class IpClientIntegrationTestCommon {
@Test
public void testDiscoverCustomizedDhcpOptions_nullScanResultInfo() throws Exception {
final DhcpPacket packet = doCustomizedDhcpOptionsTest(TEST_OEM_DHCP_OPTIONS,
- null /* scanResultInfo */, false /* isDhcpLeaseCacheEnabled */);
+ null /* scanResultInfo */);
assertTrue(packet instanceof DhcpDiscoverPacket);
assertEquals(packet.mVendorId, new String("android-dhcp-" + Build.VERSION.RELEASE));
@@ -3643,8 +3583,7 @@ public abstract class IpClientIntegrationTestCommon {
public void testDiscoverCustomizedDhcpOptions_disallowedOui() throws Exception {
final ScanResultInfo info = makeScanResultInfo(TEST_VENDOR_SPECIFIC_IE_ID,
new byte[]{ 0x00, 0x11, 0x22} /* oui */, TEST_VENDOR_SPECIFIC_IE_TYPE);
- final DhcpPacket packet = doCustomizedDhcpOptionsTest(TEST_OEM_DHCP_OPTIONS, info,
- false /* isDhcpLeaseCacheEnabled */);
+ final DhcpPacket packet = doCustomizedDhcpOptionsTest(TEST_OEM_DHCP_OPTIONS, info);
assertTrue(packet instanceof DhcpDiscoverPacket);
assertEquals(packet.mVendorId, new String("android-dhcp-" + Build.VERSION.RELEASE));
@@ -3655,8 +3594,7 @@ public abstract class IpClientIntegrationTestCommon {
public void testDiscoverCustomizedDhcpOptions_invalidIeId() throws Exception {
final ScanResultInfo info = makeScanResultInfo(0xde /* vendor-specific IE */, TEST_OEM_OUI,
TEST_VENDOR_SPECIFIC_IE_TYPE);
- final DhcpPacket packet = doCustomizedDhcpOptionsTest(TEST_OEM_DHCP_OPTIONS, info,
- false /* isDhcpLeaseCacheEnabled */);
+ final DhcpPacket packet = doCustomizedDhcpOptionsTest(TEST_OEM_DHCP_OPTIONS, info);
assertTrue(packet instanceof DhcpDiscoverPacket);
assertEquals(packet.mVendorId, new String("android-dhcp-" + Build.VERSION.RELEASE));
@@ -3667,8 +3605,7 @@ public abstract class IpClientIntegrationTestCommon {
public void testDiscoverCustomizedDhcpOptions_invalidVendorSpecificType() throws Exception {
final ScanResultInfo info = makeScanResultInfo(TEST_VENDOR_SPECIFIC_IE_ID, TEST_OEM_OUI,
(byte) 0x10 /* vendor-specific IE type */);
- final DhcpPacket packet = doCustomizedDhcpOptionsTest(TEST_OEM_DHCP_OPTIONS, info,
- false /* isDhcpLeaseCacheEnabled */);
+ final DhcpPacket packet = doCustomizedDhcpOptionsTest(TEST_OEM_DHCP_OPTIONS, info);
assertTrue(packet instanceof DhcpDiscoverPacket);
assertEquals(packet.mVendorId, new String("android-dhcp-" + Build.VERSION.RELEASE));
@@ -3679,8 +3616,7 @@ public abstract class IpClientIntegrationTestCommon {
public void testDiscoverCustomizedDhcpOptions_legacyVendorSpecificType() throws Exception {
final ScanResultInfo info = makeScanResultInfo(TEST_VENDOR_SPECIFIC_IE_ID, TEST_OEM_OUI,
LEGACY_TEST_VENDOR_SPECIFIC_IE_TYPE);
- final DhcpPacket packet = doCustomizedDhcpOptionsTest(TEST_OEM_DHCP_OPTIONS, info,
- false /* isDhcpLeaseCacheEnabled */);
+ final DhcpPacket packet = doCustomizedDhcpOptionsTest(TEST_OEM_DHCP_OPTIONS, info);
assertTrue(packet instanceof DhcpDiscoverPacket);
assertEquals(packet.mVendorId, new String("android-dhcp-" + Build.VERSION.RELEASE));
@@ -3696,8 +3632,7 @@ public abstract class IpClientIntegrationTestCommon {
makeDhcpOption((byte) 26, HexDump.toByteArray(TEST_DEFAULT_MTU)));
final ScanResultInfo info = makeScanResultInfo(TEST_VENDOR_SPECIFIC_IE_ID, TEST_OEM_OUI,
TEST_VENDOR_SPECIFIC_IE_TYPE);
- final DhcpPacket packet = doCustomizedDhcpOptionsTest(options, info,
- false /* isDhcpLeaseCacheEnabled */);
+ final DhcpPacket packet = doCustomizedDhcpOptionsTest(options, info);
assertTrue(packet instanceof DhcpDiscoverPacket);
assertEquals(packet.mVendorId, TEST_OEM_VENDOR_ID);
@@ -3714,8 +3649,7 @@ public abstract class IpClientIntegrationTestCommon {
makeDhcpOption((byte) 42, null));
final ScanResultInfo info = makeScanResultInfo(TEST_VENDOR_SPECIFIC_IE_ID, TEST_OEM_OUI,
TEST_VENDOR_SPECIFIC_IE_TYPE);
- final DhcpPacket packet = doCustomizedDhcpOptionsTest(options, info,
- false /* isDhcpLeaseCacheEnabled */);
+ final DhcpPacket packet = doCustomizedDhcpOptionsTest(options, info);
assertTrue(packet instanceof DhcpDiscoverPacket);
assertEquals(packet.mVendorId, TEST_OEM_VENDOR_ID);
@@ -3730,8 +3664,7 @@ public abstract class IpClientIntegrationTestCommon {
makeDhcpOption((byte) 77, null));
final ScanResultInfo info = makeScanResultInfo(TEST_VENDOR_SPECIFIC_IE_ID, TEST_OEM_OUI,
TEST_VENDOR_SPECIFIC_IE_TYPE);
- final DhcpPacket packet = doCustomizedDhcpOptionsTest(options, info,
- false /* isDhcpLeaseCacheEnabled */);
+ final DhcpPacket packet = doCustomizedDhcpOptionsTest(options, info);
assertTrue(packet instanceof DhcpDiscoverPacket);
assertTrue(packet.hasRequestedParam((byte) 77 /* DHCP_USER_CLASS */));
@@ -3744,8 +3677,7 @@ public abstract class IpClientIntegrationTestCommon {
final ScanResultInfo info = makeScanResultInfo(TEST_VENDOR_SPECIFIC_IE_ID, TEST_OEM_OUI,
TEST_VENDOR_SPECIFIC_IE_TYPE);
- final DhcpPacket packet = doCustomizedDhcpOptionsTest(TEST_OEM_DHCP_OPTIONS, info,
- true /* isDhcpLeaseCacheEnabled */);
+ final DhcpPacket packet = doCustomizedDhcpOptionsTest(TEST_OEM_DHCP_OPTIONS, info);
assertTrue(packet instanceof DhcpRequestPacket);
assertEquals(packet.mVendorId, TEST_OEM_VENDOR_ID);
@@ -3758,8 +3690,7 @@ public abstract class IpClientIntegrationTestCommon {
final ScanResultInfo info = makeScanResultInfo(TEST_VENDOR_SPECIFIC_IE_ID, TEST_OEM_OUI,
TEST_VENDOR_SPECIFIC_IE_TYPE);
- final DhcpPacket packet = doCustomizedDhcpOptionsTest(null /* options */, info,
- true /* isDhcpLeaseCacheEnabled */);
+ final DhcpPacket packet = doCustomizedDhcpOptionsTest(null /* options */, info);
assertTrue(packet instanceof DhcpRequestPacket);
assertEquals(packet.mVendorId, new String("android-dhcp-" + Build.VERSION.RELEASE));
@@ -3771,7 +3702,7 @@ public abstract class IpClientIntegrationTestCommon {
setUpRetrievedNetworkAttributesForInitRebootState();
final DhcpPacket packet = doCustomizedDhcpOptionsTest(TEST_OEM_DHCP_OPTIONS,
- null /* scanResultInfo */, true /* isDhcpLeaseCacheEnabled */);
+ null /* scanResultInfo */);
assertTrue(packet instanceof DhcpRequestPacket);
assertEquals(packet.mVendorId, new String("android-dhcp-" + Build.VERSION.RELEASE));
@@ -3784,8 +3715,7 @@ public abstract class IpClientIntegrationTestCommon {
final ScanResultInfo info = makeScanResultInfo(TEST_VENDOR_SPECIFIC_IE_ID,
new byte[]{ 0x00, 0x11, 0x22} /* oui */, TEST_VENDOR_SPECIFIC_IE_TYPE);
- final DhcpPacket packet = doCustomizedDhcpOptionsTest(TEST_OEM_DHCP_OPTIONS, info,
- true /* isDhcpLeaseCacheEnabled */);
+ final DhcpPacket packet = doCustomizedDhcpOptionsTest(TEST_OEM_DHCP_OPTIONS, info);
assertTrue(packet instanceof DhcpRequestPacket);
assertEquals(packet.mVendorId, new String("android-dhcp-" + Build.VERSION.RELEASE));
@@ -3798,8 +3728,7 @@ public abstract class IpClientIntegrationTestCommon {
final ScanResultInfo info = makeScanResultInfo(0xde /* vendor-specific IE */, TEST_OEM_OUI,
TEST_VENDOR_SPECIFIC_IE_TYPE);
- final DhcpPacket packet = doCustomizedDhcpOptionsTest(TEST_OEM_DHCP_OPTIONS, info,
- true /* isDhcpLeaseCacheEnabled */);
+ final DhcpPacket packet = doCustomizedDhcpOptionsTest(TEST_OEM_DHCP_OPTIONS, info);
assertTrue(packet instanceof DhcpRequestPacket);
assertEquals(packet.mVendorId, new String("android-dhcp-" + Build.VERSION.RELEASE));
@@ -3812,8 +3741,7 @@ public abstract class IpClientIntegrationTestCommon {
final ScanResultInfo info = makeScanResultInfo(TEST_VENDOR_SPECIFIC_IE_ID, TEST_OEM_OUI,
(byte) 0x20 /* vendor-specific IE type */);
- final DhcpPacket packet = doCustomizedDhcpOptionsTest(TEST_OEM_DHCP_OPTIONS, info,
- true /* isDhcpLeaseCacheEnabled */);
+ final DhcpPacket packet = doCustomizedDhcpOptionsTest(TEST_OEM_DHCP_OPTIONS, info);
assertTrue(packet instanceof DhcpRequestPacket);
assertEquals(packet.mVendorId, new String("android-dhcp-" + Build.VERSION.RELEASE));
@@ -3826,8 +3754,7 @@ public abstract class IpClientIntegrationTestCommon {
final ScanResultInfo info = makeScanResultInfo(TEST_VENDOR_SPECIFIC_IE_ID, TEST_OEM_OUI,
LEGACY_TEST_VENDOR_SPECIFIC_IE_TYPE);
- final DhcpPacket packet = doCustomizedDhcpOptionsTest(TEST_OEM_DHCP_OPTIONS, info,
- true /* isDhcpLeaseCacheEnabled */);
+ final DhcpPacket packet = doCustomizedDhcpOptionsTest(TEST_OEM_DHCP_OPTIONS, info);
assertTrue(packet instanceof DhcpRequestPacket);
assertEquals(packet.mVendorId, new String("android-dhcp-" + Build.VERSION.RELEASE));
@@ -3845,8 +3772,7 @@ public abstract class IpClientIntegrationTestCommon {
makeDhcpOption((byte) 26, HexDump.toByteArray(TEST_DEFAULT_MTU)));
final ScanResultInfo info = makeScanResultInfo(TEST_VENDOR_SPECIFIC_IE_ID, TEST_OEM_OUI,
TEST_VENDOR_SPECIFIC_IE_TYPE);
- final DhcpPacket packet = doCustomizedDhcpOptionsTest(options, info,
- true /* isDhcpLeaseCacheEnabled */);
+ final DhcpPacket packet = doCustomizedDhcpOptionsTest(options, info);
assertTrue(packet instanceof DhcpRequestPacket);
assertEquals(packet.mVendorId, TEST_OEM_VENDOR_ID);
@@ -3865,8 +3791,7 @@ public abstract class IpClientIntegrationTestCommon {
makeDhcpOption((byte) 42, null));
final ScanResultInfo info = makeScanResultInfo(TEST_VENDOR_SPECIFIC_IE_ID, TEST_OEM_OUI,
TEST_VENDOR_SPECIFIC_IE_TYPE);
- final DhcpPacket packet = doCustomizedDhcpOptionsTest(options, info,
- true /* isDhcpLeaseCacheEnabled */);
+ final DhcpPacket packet = doCustomizedDhcpOptionsTest(options, info);
assertTrue(packet instanceof DhcpRequestPacket);
assertEquals(packet.mVendorId, TEST_OEM_VENDOR_ID);
@@ -3883,8 +3808,7 @@ public abstract class IpClientIntegrationTestCommon {
makeDhcpOption((byte) 77, null));
final ScanResultInfo info = makeScanResultInfo(TEST_VENDOR_SPECIFIC_IE_ID, TEST_OEM_OUI,
TEST_VENDOR_SPECIFIC_IE_TYPE);
- final DhcpPacket packet = doCustomizedDhcpOptionsTest(options, info,
- true /* isDhcpLeaseCacheEnabled */);
+ final DhcpPacket packet = doCustomizedDhcpOptionsTest(options, info);
assertTrue(packet instanceof DhcpRequestPacket);
assertTrue(packet.hasRequestedParam((byte) 77 /* DHCP_USER_CLASS */));
@@ -3936,7 +3860,6 @@ public abstract class IpClientIntegrationTestCommon {
setFeatureEnabled(NetworkStackUtils.IPCLIENT_GRATUITOUS_NA_VERSION,
true /* isGratuitousNaEnabled */);
- assertTrue(isFeatureEnabled(NetworkStackUtils.IPCLIENT_GRATUITOUS_NA_VERSION));
startIpClientProvisioning(config);
doIpv6OnlyProvisioning();
@@ -3966,7 +3889,7 @@ public abstract class IpClientIntegrationTestCommon {
// Enable rapid commit to accelerate DHCP handshake to shorten test duration,
// not strictly necessary.
- setDhcpFeatures(false /* isDhcpLeaseCacheEnabled */, true /* isRapidCommitEnabled */,
+ setDhcpFeatures(true /* isRapidCommitEnabled */,
false /* isDhcpIpConflictDetectEnabled */);
// Disable gratuitious neighbor discovery feature manually, if the feature is enabled on
@@ -3974,13 +3897,10 @@ public abstract class IpClientIntegrationTestCommon {
// mess up the assert of received NA packets.
setFeatureEnabled(NetworkStackUtils.IPCLIENT_GRATUITOUS_NA_VERSION,
false /* isGratuitousNaEnabled */);
- assumeFalse(isFeatureEnabled(NetworkStackUtils.IPCLIENT_GRATUITOUS_NA_VERSION));
if (isGratuitousArpNaRoamingEnabled) {
setFeatureEnabled(NetworkStackUtils.IPCLIENT_GARP_NA_ROAMING_VERSION, true);
- assumeTrue(isFeatureEnabled(NetworkStackUtils.IPCLIENT_GARP_NA_ROAMING_VERSION));
} else {
setFeatureEnabled(NetworkStackUtils.IPCLIENT_GARP_NA_ROAMING_VERSION, false);
- assumeFalse(isFeatureEnabled(NetworkStackUtils.IPCLIENT_GARP_NA_ROAMING_VERSION));
}
startIpClientProvisioning(prov.build());
}
@@ -4140,6 +4060,12 @@ public abstract class IpClientIntegrationTestCommon {
return ns;
}
+ // Override this function with disabled experiment flag by default, in order not to
+ // affect those tests which are just related to basic IpReachabilityMonitor infra.
+ private void prepareIpReachabilityMonitorTest() throws Exception {
+ prepareIpReachabilityMonitorTest(false /* isMulticastResolicitEnabled */);
+ }
+
private void assertNotifyNeighborLost(Inet6Address targetIp, NudEventType eventType)
throws Exception {
// For root test suite, rely on the IIpClient aidl interface version constant defined in
@@ -4172,7 +4098,8 @@ public abstract class IpClientIntegrationTestCommon {
verify(mCb, never()).onReachabilityLost(any());
}
- private void prepareIpReachabilityMonitorTest() throws Exception {
+ private void prepareIpReachabilityMonitorTest(boolean isMulticastResolicitEnabled)
+ throws Exception {
final ScanResultInfo info = makeScanResultInfo(TEST_DEFAULT_SSID, TEST_DEFAULT_BSSID);
ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
.withLayer2Information(new Layer2Information(TEST_L2KEY, TEST_CLUSTER,
@@ -4181,6 +4108,8 @@ public abstract class IpClientIntegrationTestCommon {
.withDisplayName(TEST_DEFAULT_SSID)
.withoutIPv4()
.build();
+ setFeatureEnabled(NetworkStackUtils.IP_REACHABILITY_MCAST_RESOLICIT_VERSION,
+ isMulticastResolicitEnabled);
startIpClientProvisioning(config);
verify(mCb, timeout(TEST_TIMEOUT_MS)).setFallbackMulticastFilter(true);
doIpv6OnlyProvisioning();
@@ -4194,15 +4123,11 @@ public abstract class IpClientIntegrationTestCommon {
final List<NeighborSolicitation> nsList = waitForMultipleNeighborSolicitations();
final int expectedNudSolicitNum = readNudSolicitNumPostRoamingFromResource();
- int expectedSize = expectedNudSolicitNum + NUD_MCAST_RESOLICIT_NUM;
- assertEquals(expectedSize, nsList.size());
- for (NeighborSolicitation ns : nsList.subList(0, expectedNudSolicitNum)) {
+ assertEquals(expectedNudSolicitNum, nsList.size());
+ for (NeighborSolicitation ns : nsList) {
assertUnicastNeighborSolicitation(ns, ROUTER_MAC /* dstMac */,
ROUTER_LINK_LOCAL /* dstIp */, ROUTER_LINK_LOCAL /* targetIp */);
}
- for (NeighborSolicitation ns : nsList.subList(expectedNudSolicitNum, nsList.size())) {
- assertMulticastNeighborSolicitation(ns, ROUTER_LINK_LOCAL /* targetIp */);
- }
}
@Test
@@ -4237,10 +4162,43 @@ public abstract class IpClientIntegrationTestCommon {
assertNeverNotifyNeighborLost();
}
+ private void runIpReachabilityMonitorMcastResolicitProbeFailedTest() throws Exception {
+ prepareIpReachabilityMonitorTest(true /* isMulticastResolicitEnabled */);
+
+ final List<NeighborSolicitation> nsList = waitForMultipleNeighborSolicitations();
+ final int expectedNudSolicitNum = readNudSolicitNumPostRoamingFromResource();
+ int expectedSize = expectedNudSolicitNum + NUD_MCAST_RESOLICIT_NUM;
+ assertEquals(expectedSize, nsList.size());
+ for (NeighborSolicitation ns : nsList.subList(0, expectedNudSolicitNum)) {
+ assertUnicastNeighborSolicitation(ns, ROUTER_MAC /* dstMac */,
+ ROUTER_LINK_LOCAL /* dstIp */, ROUTER_LINK_LOCAL /* targetIp */);
+ }
+ for (NeighborSolicitation ns : nsList.subList(expectedNudSolicitNum, nsList.size())) {
+ assertMulticastNeighborSolicitation(ns, ROUTER_LINK_LOCAL /* targetIp */);
+ }
+ }
+
+ @Test
+ public void testIpReachabilityMonitor_mcastResolicitProbeFailed() throws Exception {
+ runIpReachabilityMonitorMcastResolicitProbeFailedTest();
+ assertNotifyNeighborLost(ROUTER_LINK_LOCAL /* targetIp */,
+ NudEventType.NUD_POST_ROAMING_FAILED_CRITICAL);
+ }
+
+ @Test @SignatureRequiredTest(reason = "requires mock callback object")
+ public void testIpReachabilityMonitor_mcastResolicitProbeFailed_legacyCallback()
+ throws Exception {
+ when(mCb.getInterfaceVersion()).thenReturn(12 /* assign an older interface aidl version */);
+
+ runIpReachabilityMonitorMcastResolicitProbeFailedTest();
+ verify(mCb, timeout(TEST_TIMEOUT_MS)).onReachabilityLost(any());
+ verify(mCb, never()).onReachabilityFailure(any());
+ }
+
@Test
public void testIpReachabilityMonitor_mcastResolicitProbeReachableWithSameLinkLayerAddress()
throws Exception {
- prepareIpReachabilityMonitorTest();
+ prepareIpReachabilityMonitorTest(true /* isMulticastResolicitEnabled */);
final NeighborSolicitation ns = waitForUnicastNeighborSolicitation(ROUTER_MAC /* dstMac */,
ROUTER_LINK_LOCAL /* dstIp */, ROUTER_LINK_LOCAL /* targetIp */);
@@ -4257,7 +4215,7 @@ public abstract class IpClientIntegrationTestCommon {
@Test
public void testIpReachabilityMonitor_mcastResolicitProbeReachableWithDiffLinkLayerAddress()
throws Exception {
- prepareIpReachabilityMonitorTest();
+ prepareIpReachabilityMonitorTest(true /* isMulticastResolicitEnabled */);
final NeighborSolicitation ns = waitForUnicastNeighborSolicitation(ROUTER_MAC /* dstMac */,
ROUTER_LINK_LOCAL /* dstIp */, ROUTER_LINK_LOCAL /* targetIp */);
@@ -4278,9 +4236,113 @@ public abstract class IpClientIntegrationTestCommon {
NudEventType.NUD_POST_ROAMING_MAC_ADDRESS_CHANGED);
}
- private void sendUdpPacketToNetwork(final Network network, final Inet6Address remoteIp,
+ private void prepareIpReachabilityMonitorIpv4AddressResolutionTest() throws Exception {
+ mNetworkAgentThread =
+ new HandlerThread(IpClientIntegrationTestCommon.class.getSimpleName());
+ mNetworkAgentThread.start();
+
+ ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
+ .withoutIPv6()
+ .build();
+ setDhcpFeatures(true /* isRapidCommitEnabled */, false /* isDhcpIpConflictDetectEnabled */);
+ startIpClientProvisioning(config);
+
+ // Start IPv4 provisioning and wait until entire provisioning completes.
+ handleDhcpPackets(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
+ true /* shouldReplyRapidCommitAck */, TEST_DEFAULT_MTU, null /* serverSentUrl */);
+ final LinkProperties lp =
+ verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
+
+ runAsShell(MANAGE_TEST_NETWORKS, () -> createTestNetworkAgentAndRegister(lp));
+
+ // Send a UDP packet to IPv4 DNS server to trigger address resolution process for IPv4
+ // on-link DNS server or default router.
+ final Random random = new Random();
+ final byte[] data = new byte[100];
+ random.nextBytes(data);
+ sendUdpPacketToNetwork(mNetworkAgent.getNetwork(), SERVER_ADDR, 1234 /* port */, data);
+ }
+
+ private void doTestIpReachabilityMonitor_replyBroadcastArpRequestWithDiffMacAddresses(
+ boolean disconnect) throws Exception {
+ prepareIpReachabilityMonitorIpv4AddressResolutionTest();
+
+ // Respond to the broadcast ARP request.
+ final ArpPacket request = getNextArpPacket();
+ assertArpRequest(request, SERVER_ADDR);
+ sendArpReply(request.senderHwAddress.toByteArray() /* dst */, ROUTER_MAC_BYTES /* srcMac */,
+ request.senderIp /* target IP */, SERVER_ADDR /* sender IP */);
+
+ Thread.sleep(1500);
+
+ // Reply with a different MAC address but the same server IP.
+ final MacAddress gateway = MacAddress.fromString("00:11:22:33:44:55");
+ sendArpReply(request.senderHwAddress.toByteArray() /* dst */,
+ gateway.toByteArray() /* srcMac */,
+ request.senderIp /* target IP */, SERVER_ADDR /* sender IP */);
+
+ if (disconnect) {
+ final ArgumentCaptor<ReachabilityLossInfoParcelable> lossInfoCaptor =
+ ArgumentCaptor.forClass(ReachabilityLossInfoParcelable.class);
+ verify(mCb, timeout(TEST_TIMEOUT_MS)).onReachabilityFailure(lossInfoCaptor.capture());
+ assertEquals(ReachabilityLossReason.ORGANIC, lossInfoCaptor.getValue().reason);
+ } else {
+ verify(mCb, after(100).never()).onReachabilityFailure(any());
+ }
+ }
+
+ @Test
+ public void testIpReachabilityMonitor_macAddressChangedWithoutRoam_ok()
+ throws Exception {
+ setFeatureChickenedOut(IP_REACHABILITY_ROUTER_MAC_CHANGE_FAILURE_ONLY_AFTER_ROAM_VERSION,
+ false);
+ doTestIpReachabilityMonitor_replyBroadcastArpRequestWithDiffMacAddresses(false);
+ }
+
+ @Test
+ public void testIpReachabilityMonitor_macAddressChangedWithoutRoam_disconnect()
+ throws Exception {
+ setFeatureChickenedOut(IP_REACHABILITY_ROUTER_MAC_CHANGE_FAILURE_ONLY_AFTER_ROAM_VERSION,
+ true);
+ doTestIpReachabilityMonitor_replyBroadcastArpRequestWithDiffMacAddresses(true);
+ }
+
+ @Test
+ public void testIpReachabilityMonitor_ignoreIpv4DefaultRouterOrganicNudFailure()
+ throws Exception {
+ setFeatureEnabled(NetworkStackUtils.IP_REACHABILITY_IGNORE_ORGANIC_NUD_FAILURE_VERSION,
+ true /* ignoreOrganicNudFailure */);
+ prepareIpReachabilityMonitorIpv4AddressResolutionTest();
+
+ ArpPacket packet;
+ while ((packet = getNextArpPacket(TEST_TIMEOUT_MS)) != null) {
+ // wait address resolution to complete.
+ }
+ verify(mCb, never()).onReachabilityFailure(any());
+ }
+
+ @Test
+ public void testIpReachabilityMonitor_ignoreIpv4DefaultRouterOrganicNudFailure_flagoff()
+ throws Exception {
+ setFeatureEnabled(NetworkStackUtils.IP_REACHABILITY_IGNORE_ORGANIC_NUD_FAILURE_VERSION,
+ false /* ignoreOrganicNudFailure */);
+ prepareIpReachabilityMonitorIpv4AddressResolutionTest();
+
+ ArpPacket packet;
+ while ((packet = getNextArpPacket(TEST_TIMEOUT_MS)) != null) {
+ // wait address resolution to complete.
+ }
+ final ArgumentCaptor<ReachabilityLossInfoParcelable> lossInfoCaptor =
+ ArgumentCaptor.forClass(ReachabilityLossInfoParcelable.class);
+ verify(mCb).onReachabilityFailure(lossInfoCaptor.capture());
+ assertEquals(ReachabilityLossReason.ORGANIC, lossInfoCaptor.getValue().reason);
+ }
+
+ private void sendUdpPacketToNetwork(final Network network, final InetAddress remoteIp,
int port, final byte[] data) throws Exception {
- final DatagramSocket socket = new DatagramSocket(0, (InetAddress) Inet6Address.ANY);
+ final InetAddress laddr =
+ (remoteIp instanceof Inet6Address) ? Inet6Address.ANY : Inet4Address.ANY;
+ final DatagramSocket socket = new DatagramSocket(0, laddr);
final DatagramPacket pkt = new DatagramPacket(data, data.length, remoteIp, port);
network.bindSocket(socket);
socket.send(pkt);
@@ -4290,12 +4352,13 @@ public abstract class IpClientIntegrationTestCommon {
final Inet6Address targetIp,
final boolean isIgnoreIncompleteIpv6DnsServerEnabled,
final boolean isIgnoreIncompleteIpv6DefaultRouterEnabled,
+ final boolean isIgnoreOrganicNudFailureEnabled,
final boolean expectNeighborLost) throws Exception {
mNetworkAgentThread =
new HandlerThread(IpClientIntegrationTestCommon.class.getSimpleName());
mNetworkAgentThread.start();
- setDhcpFeatures(false /* isDhcpLeaseCacheEnabled */, true /* isRapidCommitEnabled */,
+ setDhcpFeatures(true /* isRapidCommitEnabled */,
false /* isDhcpIpConflictDetectEnabled */);
setFeatureEnabled(
NetworkStackUtils.IP_REACHABILITY_IGNORE_INCOMPLETE_IPV6_DNS_SERVER_VERSION,
@@ -4303,6 +4366,9 @@ public abstract class IpClientIntegrationTestCommon {
setFeatureEnabled(
NetworkStackUtils.IP_REACHABILITY_IGNORE_INCOMPLETE_IPV6_DEFAULT_ROUTER_VERSION,
isIgnoreIncompleteIpv6DefaultRouterEnabled);
+ setFeatureEnabled(
+ NetworkStackUtils.IP_REACHABILITY_IGNORE_ORGANIC_NUD_FAILURE_VERSION,
+ isIgnoreOrganicNudFailureEnabled);
final ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
.build();
startIpClientProvisioning(config);
@@ -4354,18 +4420,17 @@ public abstract class IpClientIntegrationTestCommon {
}
@Test
- @SignatureRequiredTest(reason = "Need to mock NetworkAgent")
public void testIpReachabilityMonitor_incompleteIpv6DnsServerInDualStack() throws Exception {
final Inet6Address targetIp =
(Inet6Address) InetAddresses.parseNumericAddress(IPV6_ON_LINK_DNS_SERVER);
runIpReachabilityMonitorAddressResolutionTest(IPV6_ON_LINK_DNS_SERVER, targetIp,
true /* isIgnoreIncompleteIpv6DnsServerEnabled */,
false /* isIgnoreIncompleteIpv6DefaultRouterEnabled */,
+ false /* isIgnoreOrganicNudFailureEnabled */,
false /* expectNeighborLost */);
}
@Test
- @SignatureRequiredTest(reason = "Need to mock NetworkAgent")
public void testIpReachabilityMonitor_incompleteIpv6DnsServerInDualStack_flagoff()
throws Exception {
final Inet6Address targetIp =
@@ -4373,28 +4438,75 @@ public abstract class IpClientIntegrationTestCommon {
runIpReachabilityMonitorAddressResolutionTest(IPV6_ON_LINK_DNS_SERVER, targetIp,
false /* isIgnoreIncompleteIpv6DnsServerEnabled */,
false /* isIgnoreIncompleteIpv6DefaultRouterEnabled */,
+ false /* isIgnoreOrganicNudFailureEnabled */,
true /* expectNeighborLost */);
}
@Test
- @SignatureRequiredTest(reason = "Need to mock the NetworkAgent")
public void testIpReachabilityMonitor_incompleteIpv6DefaultRouterInDualStack()
throws Exception {
runIpReachabilityMonitorAddressResolutionTest(IPV6_OFF_LINK_DNS_SERVER,
ROUTER_LINK_LOCAL /* targetIp */,
false /* isIgnoreIncompleteIpv6DnsServerEnabled */,
true /* isIgnoreIncompleteIpv6DefaultRouterEnabled */,
+ false /* isIgnoreOrganicNudFailureEnabled */,
false /* expectNeighborLost */);
}
@Test
- @SignatureRequiredTest(reason = "Need to mock the NetworkAgent")
public void testIpReachabilityMonitor_incompleteIpv6DefaultRouterInDualStack_flagoff()
throws Exception {
runIpReachabilityMonitorAddressResolutionTest(IPV6_OFF_LINK_DNS_SERVER,
ROUTER_LINK_LOCAL /* targetIp */,
false /* isIgnoreIncompleteIpv6DnsServerEnabled */,
false /* isIgnoreIncompleteIpv6DefaultRouterEnabled */,
+ false /* isIgnoreOrganicNudFailureEnabled */,
+ true /* expectNeighborLost */);
+ }
+
+ @Test
+ public void testIpReachabilityMonitor_ignoreOnLinkIpv6DnsOrganicNudFailure()
+ throws Exception {
+ final Inet6Address targetIp =
+ (Inet6Address) InetAddresses.parseNumericAddress(IPV6_ON_LINK_DNS_SERVER);
+ runIpReachabilityMonitorAddressResolutionTest(IPV6_ON_LINK_DNS_SERVER, targetIp,
+ false /* isIgnoreIncompleteIpv6DnsServerEnabled */,
+ false /* isIgnoreIncompleteIpv6DefaultRouterEnabled */,
+ true /* isIgnoreOrganicNudFailureEnabled */,
+ false /* expectNeighborLost */);
+ }
+
+ @Test
+ public void testIpReachabilityMonitor_ignoreOnLinkIpv6DnsOrganicNudFailure_flagoff()
+ throws Exception {
+ final Inet6Address targetIp =
+ (Inet6Address) InetAddresses.parseNumericAddress(IPV6_ON_LINK_DNS_SERVER);
+ runIpReachabilityMonitorAddressResolutionTest(IPV6_ON_LINK_DNS_SERVER, targetIp,
+ false /* isIgnoreIncompleteIpv6DnsServerEnabled */,
+ false /* isIgnoreIncompleteIpv6DefaultRouterEnabled */,
+ false /* isIgnoreOrganicNudFailureEnabled */,
+ true /* expectNeighborLost */);
+ }
+
+ @Test
+ public void testIpReachabilityMonitor_ignoreIpv6DefaultRouterOrganicNudFailure()
+ throws Exception {
+ runIpReachabilityMonitorAddressResolutionTest(IPV6_OFF_LINK_DNS_SERVER,
+ ROUTER_LINK_LOCAL /* targetIp */,
+ false /* isIgnoreIncompleteIpv6DnsServerEnabled */,
+ false /* isIgnoreIncompleteIpv6DefaultRouterEnabled */,
+ true /* isIgnoreOrganicNudFailureEnabled */,
+ false /* expectNeighborLost */);
+ }
+
+ @Test
+ public void testIpReachabilityMonitor_ignoreIpv6DefaultRouterOrganicNudFailure_flagoff()
+ throws Exception {
+ runIpReachabilityMonitorAddressResolutionTest(IPV6_OFF_LINK_DNS_SERVER,
+ ROUTER_LINK_LOCAL /* targetIp */,
+ false /* isIgnoreIncompleteIpv6DnsServerEnabled */,
+ false /* isIgnoreIncompleteIpv6DefaultRouterEnabled */,
+ false /* isIgnoreOrganicNudFailureEnabled */,
true /* expectNeighborLost */);
}
@@ -4471,7 +4583,7 @@ public abstract class IpClientIntegrationTestCommon {
reset(mCb);
// Speed up provisioning by enabling rapid commit. TODO: why is this necessary?
- setDhcpFeatures(false /* isDhcpLeaseCacheEnabled */, true /* isRapidCommitEnabled */,
+ setDhcpFeatures(true /* isRapidCommitEnabled */,
false /* isDhcpIpConflictDetectEnabled */);
config = new ProvisioningConfiguration.Builder()
.build();
@@ -4739,7 +4851,7 @@ public abstract class IpClientIntegrationTestCommon {
@IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
public void testMaxDtimMultiplier_IPv4OnlyNetwork() throws Exception {
performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
- true /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */,
+ false /* shouldReplyRapidCommitAck */,
TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */);
verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
verify(mCb, timeout(TEST_TIMEOUT_MS).times(1)).setMaxDtimMultiplier(
@@ -4813,7 +4925,7 @@ public abstract class IpClientIntegrationTestCommon {
private IaPrefixOption buildIaPrefixOption(final IpPrefix prefix, int preferred,
int valid) {
return new IaPrefixOption((short) IaPrefixOption.LENGTH, preferred, valid,
- (byte) RFC7421_PREFIX_LENGTH, prefix.getRawAddress() /* prefix */);
+ (byte) prefix.getPrefixLength(), prefix.getRawAddress() /* prefix */);
}
private void handleDhcp6Packets(final IpPrefix prefix, boolean shouldReplyRapidCommit)
@@ -4874,7 +4986,25 @@ public abstract class IpClientIntegrationTestCommon {
handleDhcp6Packets(prefix, true /* shouldReplyRapidCommit */);
final ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class);
verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningSuccess(captor.capture());
- assertTrue(hasIpv6AddressPrefixedWith(captor.getValue(), prefix));
+ final LinkProperties lp = captor.getValue();
+ assertTrue(hasIpv6AddressPrefixedWith(lp, prefix));
+
+ // Only run the test when the flag of parsing netlink events is enabled, where the
+ // deprecationTime and expirationTime is set.
+ if (mIsNetlinkEventParseEnabled) {
+ final long now = SystemClock.elapsedRealtime();
+ long when = 0;
+ for (LinkAddress la : lp.getLinkAddresses()) {
+ if (la.getAddress().isLinkLocalAddress()) {
+ assertLinkAddressPermanentLifetime(la);
+ } else if (la.isGlobalPreferred()) {
+ when = now + 4500 * 1000; // preferred=4500s
+ assertLinkAddressDeprecationTime(la, when);
+ when = now + 7200 * 1000; // valid=7200s
+ assertLinkAddressExpirationTime(la, when);
+ }
+ }
+ }
}
@Test
@@ -4955,7 +5085,7 @@ public abstract class IpClientIntegrationTestCommon {
ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
.build();
- setDhcpFeatures(false /* isDhcpLeaseCacheEnabled */, true /* isRapidCommitEnabled */,
+ setDhcpFeatures(true /* isRapidCommitEnabled */,
false /* isDhcpIpConflictDetectEnabled */);
startIpClientProvisioning(config);
@@ -5013,7 +5143,7 @@ public abstract class IpClientIntegrationTestCommon {
ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
.build();
- setDhcpFeatures(false /* isDhcpLeaseCacheEnabled */, true /* isRapidCommitEnabled */,
+ setDhcpFeatures(true /* isRapidCommitEnabled */,
false /* isDhcpIpConflictDetectEnabled */);
startIpClientProvisioning(config);
@@ -5571,6 +5701,7 @@ public abstract class IpClientIntegrationTestCommon {
mNetd.getProcSysNet(INetd.IPV6, INetd.CONF, mIfaceName, "accept_ra_defrtr"));
assertEquals(1, acceptRaDefRtr);
}
+
private void runDhcpDomainSearchListOptionTest(final String domainName,
final List<String> domainSearchList, final String expectedDomain) throws Exception {
when(mResources.getBoolean(R.bool.config_dhcp_client_domain_search_list)).thenReturn(true);
@@ -5618,4 +5749,104 @@ public abstract class IpClientIntegrationTestCommon {
runDhcpDomainSearchListOptionTest(null /* domainName */, searchList,
expectedDomain);
}
+
+ private void assertLinkAddressDeprecationTime(final LinkAddress la, final long when) {
+ assertTrue(la.getDeprecationTime() != LinkAddress.LIFETIME_UNKNOWN);
+ // Allow +/- 2 seconds to prevent flaky tests
+ assertTrue(la.getDeprecationTime() < when + TEST_LIFETIME_TOLERANCE_MS);
+ assertTrue(la.getDeprecationTime() > when - TEST_LIFETIME_TOLERANCE_MS);
+ }
+
+ private void assertLinkAddressExpirationTime(final LinkAddress la, final long when) {
+ assertTrue(la.getExpirationTime() != LinkAddress.LIFETIME_UNKNOWN);
+ // Allow +/- 2 seconds to prevent flaky tests
+ assertTrue(la.getExpirationTime() < when + TEST_LIFETIME_TOLERANCE_MS);
+ assertTrue(la.getExpirationTime() > when - TEST_LIFETIME_TOLERANCE_MS);
+ }
+
+ private void assertLinkAddressPermanentLifetime(final LinkAddress la) {
+ assertEquals(LinkAddress.LIFETIME_PERMANENT, la.getDeprecationTime());
+ assertEquals(LinkAddress.LIFETIME_PERMANENT, la.getExpirationTime());
+ }
+
+ @Test
+ public void testPopulateLinkAddressLifetime() throws Exception {
+ // Only run the test when the flag of parsing netlink events is enabled to verify the
+ // code of setting deprecationTime/expirationTime added when IpClientLinkObserver sees
+ // the RTM_NEWADDR, and we are going to delete the dead old code path completely soon.
+ assumeTrue(mIsNetlinkEventParseEnabled);
+
+ final LinkProperties lp = doDualStackProvisioning();
+ final long now = SystemClock.elapsedRealtime();
+ long when = 0;
+ for (LinkAddress la : lp.getLinkAddresses()) {
+ if (la.isIpv4()) {
+ when = now + 3600 * 1000; // DHCP lease duration
+ assertLinkAddressDeprecationTime(la, when);
+ assertLinkAddressExpirationTime(la, when);
+ } else if (la.isIpv6() && la.getAddress().isLinkLocalAddress()) {
+ assertLinkAddressPermanentLifetime(la);
+ } else if (la.isIpv6() && la.isGlobalPreferred()) {
+ when = now + 1800 * 1000; // preferred=1800s
+ assertLinkAddressDeprecationTime(la, when);
+ when = now + 3600 * 1000; // valid=3600s
+ assertLinkAddressExpirationTime(la, when);
+ }
+ }
+ }
+
+ @Test
+ public void testPopulateLinkAddressLifetime_infiniteLeaseDuration() throws Exception {
+ // Only run the test when the flag of parsing netlink events is enabled.
+ assumeTrue(mIsNetlinkEventParseEnabled);
+
+ final ProvisioningConfiguration cfg = new ProvisioningConfiguration.Builder()
+ .withoutIPv6()
+ .build();
+
+ startIpClientProvisioning(cfg);
+ handleDhcpPackets(true /* isSuccessLease */, DhcpPacket.INFINITE_LEASE,
+ false /* shouldReplyRapidCommitAck */, TEST_DEFAULT_MTU,
+ null /* captivePortalApiUrl */, null /* ipv6OnlyWaitTime */,
+ null /* domainName */, null /* domainSearchList */);
+
+ final ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class);
+ verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningSuccess(captor.capture());
+ final LinkProperties lp = captor.getValue();
+ assertNotNull(lp);
+ for (LinkAddress la : lp.getLinkAddresses()) {
+ if (la.isIpv4()) {
+ assertLinkAddressPermanentLifetime(la);
+ }
+ }
+ }
+
+ @Test
+ public void testPopulateLinkAddressLifetime_minimalLeaseDuration() throws Exception {
+ // Only run the test when the flag of parsing netlink events is enabled.
+ assumeTrue(mIsNetlinkEventParseEnabled);
+
+ final ProvisioningConfiguration cfg = new ProvisioningConfiguration.Builder()
+ .withoutIPv6()
+ .build();
+
+ startIpClientProvisioning(cfg);
+ handleDhcpPackets(true /* isSuccessLease */, 59 /* lease duration */,
+ false /* shouldReplyRapidCommitAck */, TEST_DEFAULT_MTU,
+ null /* captivePortalApiUrl */, null /* ipv6OnlyWaitTime */,
+ null /* domainName */, null /* domainSearchList */);
+
+ final ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class);
+ verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningSuccess(captor.capture());
+ final LinkProperties lp = captor.getValue();
+ assertNotNull(lp);
+ for (LinkAddress la : lp.getLinkAddresses()) {
+ if (la.isIpv4()) {
+ final long now = SystemClock.elapsedRealtime();
+ final long when = now + 60 * 1000; // minimal lease duration
+ assertLinkAddressDeprecationTime(la, when);
+ assertLinkAddressExpirationTime(la, when);
+ }
+ }
+ }
}
diff --git a/tests/integration/root/android/net/ip/IpClientRootTest.kt b/tests/integration/root/android/net/ip/IpClientRootTest.kt
index 77d327f9..715e27d5 100644
--- a/tests/integration/root/android/net/ip/IpClientRootTest.kt
+++ b/tests/integration/root/android/net/ip/IpClientRootTest.kt
@@ -16,7 +16,7 @@
package android.net.ip
-import android.Manifest
+import android.Manifest.permission.NETWORK_SETTINGS
import android.Manifest.permission.READ_DEVICE_CONFIG
import android.Manifest.permission.WRITE_DEVICE_CONFIG
import android.net.IIpMemoryStore
@@ -27,10 +27,13 @@ import android.net.ipmemorystore.OnNetworkAttributesRetrievedListener
import android.net.ipmemorystore.Status
import android.net.networkstack.TestNetworkStackServiceClient
import android.os.Process
+import android.provider.DeviceConfig
+import android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY
+import android.util.ArrayMap
import android.util.Log
import androidx.test.platform.app.InstrumentationRegistry
-import com.android.net.module.util.DeviceConfigUtils
import java.lang.System.currentTimeMillis
+import java.lang.UnsupportedOperationException
import java.util.concurrent.CompletableFuture
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
@@ -80,7 +83,7 @@ class IpClientRootTest : IpClientIntegrationTestCommon() {
// Connect to the NetworkStack only once, as it is relatively expensive (~50ms plus any
// polling time waiting for the test UID to be allowed), and there should be minimal
// side-effects between tests compared to reconnecting every time.
- automation.adoptShellPermissionIdentity(Manifest.permission.NETWORK_SETTINGS)
+ automation.adoptShellPermissionIdentity(NETWORK_SETTINGS)
try {
automation.executeShellCommand("su root service call network_stack " +
"$ALLOW_TEST_UID_INDEX i32 " + Process.myUid())
@@ -122,7 +125,7 @@ class IpClientRootTest : IpClientIntegrationTestCommon() {
@JvmStatic @AfterClass
fun tearDownClass() {
nsClient.disconnect()
- automation.adoptShellPermissionIdentity(Manifest.permission.NETWORK_SETTINGS)
+ automation.adoptShellPermissionIdentity(NETWORK_SETTINGS)
try {
// Reset the test UID as -1.
// This may not be called if the test process is terminated before completing,
@@ -176,27 +179,47 @@ class IpClientRootTest : IpClientIntegrationTestCommon() {
return ipClientCaptor.value
}
- override fun setFeatureEnabled(feature: String, enabled: Boolean) {
- // The feature is enabled if the flag is lower than the package version.
- // Package versions follow a standard format with 9 digits.
- // TODO: consider resetting flag values on reboot when set to special values like "1" or
- // "999999999"
- setDeviceConfigProperty(feature, if (enabled) "1" else "999999999")
- }
+ // These are not needed in IpClientRootTest because there is no dependency injection and
+ // IpClient always uses the production implementations.
+ override fun getDeviceConfigProperty(name: String) = throw UnsupportedOperationException()
+ override fun isFeatureEnabled(name: String) = throw UnsupportedOperationException()
+ override fun isFeatureNotChickenedOut(name: String) = throw UnsupportedOperationException()
+
+ private val mOriginalPropertyValues = ArrayMap<String, String>()
- override fun isFeatureEnabled(name: String): Boolean {
+ override fun setDeviceConfigProperty(name: String?, value: String?) {
automation.adoptShellPermissionIdentity(READ_DEVICE_CONFIG, WRITE_DEVICE_CONFIG)
try {
- return DeviceConfigUtils.isNetworkStackFeatureEnabled(mContext, name)
+ // Do not use computeIfAbsent as it would overwrite null values,
+ // property originally unset.
+ if (!mOriginalPropertyValues.containsKey(name)) {
+ mOriginalPropertyValues[name] = DeviceConfig.getProperty(
+ DeviceConfig.NAMESPACE_CONNECTIVITY,
+ (name)!!
+ )
+ }
+ DeviceConfig.setProperty(
+ DeviceConfig.NAMESPACE_CONNECTIVITY,
+ (name)!!, value,
+ false /* makeDefault */
+ )
} finally {
automation.dropShellPermissionIdentity()
}
}
- override fun isFeatureNotChickenedOut(name: String): Boolean {
+ @After
+ fun tearDownDeviceConfigProperties() {
+ if (testSkipped()) return
automation.adoptShellPermissionIdentity(READ_DEVICE_CONFIG, WRITE_DEVICE_CONFIG)
try {
- return DeviceConfigUtils.isNetworkStackFeatureNotChickenedOut(mContext, name)
+ for (key in mOriginalPropertyValues.keys) {
+ if (key == null) continue
+ DeviceConfig.setProperty(
+ DeviceConfig.NAMESPACE_CONNECTIVITY, key,
+ mOriginalPropertyValues[key], false /* makeDefault */
+ )
+ }
} finally {
automation.dropShellPermissionIdentity()
}
diff --git a/tests/integration/signature/android/net/NetworkStatsIntegrationTest.kt b/tests/integration/signature/android/net/NetworkStatsIntegrationTest.kt
index b10e6e14..2e52e3f1 100644
--- a/tests/integration/signature/android/net/NetworkStatsIntegrationTest.kt
+++ b/tests/integration/signature/android/net/NetworkStatsIntegrationTest.kt
@@ -67,10 +67,8 @@ private const val TEST_TAG = 0xF00D
@IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
class NetworkStatsIntegrationTest {
private val TAG = NetworkStatsIntegrationTest::class.java.simpleName
- private val INTERNAL_V6ADDR =
+ private val LOCAL_V6ADDR =
LinkAddress(InetAddresses.parseNumericAddress("2001:db8::1234"), 64)
- private val EXTERNAL_V6ADDR =
- LinkAddress(InetAddresses.parseNumericAddress("2001:db8::5678"), 64)
// Remote address, both the client and server will have a hallucination that
// they are talking to this address.
@@ -101,13 +99,13 @@ class NetworkStatsIntegrationTest {
private val inst = InstrumentationRegistry.getInstrumentation()
private val context = inst.getContext()
private val packetBridge = runAsShell(MANAGE_TEST_NETWORKS) {
- PacketBridge(context, INTERNAL_V6ADDR, EXTERNAL_V6ADDR, REMOTE_V6ADDR.address)
+ PacketBridge(context, listOf(LOCAL_V6ADDR), REMOTE_V6ADDR.address)
}
private val cm = context.getSystemService(ConnectivityManager::class.java)!!
// Set up DNS server for testing server and DNS64.
private val fakeDns = TestDnsServer(
- packetBridge.externalNetwork, InetSocketAddress(EXTERNAL_V6ADDR.address, DNS_SERVER_PORT)
+ packetBridge.externalNetwork, InetSocketAddress(LOCAL_V6ADDR.address, DNS_SERVER_PORT)
).apply {
start()
setAnswer(
@@ -118,7 +116,7 @@ class NetworkStatsIntegrationTest {
}
// Start up test http server.
- private val httpServer = TestHttpServer(EXTERNAL_V6ADDR.address.hostAddress).apply {
+ private val httpServer = TestHttpServer(LOCAL_V6ADDR.address.hostAddress).apply {
start()
}
diff --git a/tests/integration/signature/android/net/ip/IpClientSignatureTest.kt b/tests/integration/signature/android/net/ip/IpClientSignatureTest.kt
index 2f91f4fd..f4010730 100644
--- a/tests/integration/signature/android/net/ip/IpClientSignatureTest.kt
+++ b/tests/integration/signature/android/net/ip/IpClientSignatureTest.kt
@@ -33,10 +33,14 @@ import org.mockito.Mockito.verify
* Tests for IpClient, run with signature permissions.
*/
class IpClientSignatureTest : IpClientIntegrationTestCommon() {
+ companion object {
+ private val TAG = IpClientSignatureTest::class.java.simpleName
+ }
+
private val DEFAULT_NUD_SOLICIT_NUM_POST_ROAM = 5
private val DEFAULT_NUD_SOLICIT_NUM_STEADY_STATE = 10
- private val mEnabledFeatures = ArrayMap<String, Boolean>()
+ private val mDeviceConfigProperties = ArrayMap<String, String>()
override fun makeIIpClient(ifaceName: String, cb: IIpClientCallbacks): IIpClient {
return mIpc.makeConnector()
@@ -45,21 +49,20 @@ class IpClientSignatureTest : IpClientIntegrationTestCommon() {
override fun useNetworkStackSignature() = true
override fun isFeatureEnabled(name: String): Boolean {
- return mEnabledFeatures.get(name) ?: false
+ return FEATURE_ENABLED.equals(getDeviceConfigProperty(name))
}
override fun isFeatureNotChickenedOut(name: String): Boolean {
- return mEnabledFeatures.get(name) ?: true
+ return !FEATURE_DISABLED.equals(getDeviceConfigProperty(name))
}
- override fun setFeatureEnabled(name: String, enabled: Boolean) {
- mEnabledFeatures.put(name, enabled)
+ override fun setDeviceConfigProperty(name: String, value: String) {
+ mDeviceConfigProperties.put(name, value)
}
- override fun setDeviceConfigProperty(name: String, value: Int) {
- mDependencies.setDeviceConfigProperty(name, value)
+ override fun getDeviceConfigProperty(name: String): String? {
+ return mDeviceConfigProperties.get(name)
}
-
override fun getStoredNetworkAttributes(l2Key: String, timeout: Long): NetworkAttributes {
val networkAttributesCaptor = ArgumentCaptor.forClass(NetworkAttributes::class.java)
diff --git a/tests/integration/signature/android/net/util/NetworkStackUtilsIntegrationTest.kt b/tests/integration/signature/android/net/util/NetworkStackUtilsIntegrationTest.kt
index 3f01beaa..f5c06a18 100644
--- a/tests/integration/signature/android/net/util/NetworkStackUtilsIntegrationTest.kt
+++ b/tests/integration/signature/android/net/util/NetworkStackUtilsIntegrationTest.kt
@@ -30,7 +30,6 @@ import android.system.Os
import android.system.OsConstants
import android.system.OsConstants.AF_INET
import android.system.OsConstants.AF_PACKET
-import android.system.OsConstants.ARPHRD_ETHER
import android.system.OsConstants.ETH_P_IPV6
import android.system.OsConstants.IPPROTO_UDP
import android.system.OsConstants.SOCK_CLOEXEC
@@ -264,14 +263,15 @@ class NetworkStackUtilsIntegrationTest {
// Don't accept the prefix length larger than 64.
assertNull(NetworkStackUtils.createInet6AddressFromEui64(prefix, eui64))
+ // prefix length equals to or less than 64 is acceptable.
prefix = IpPrefix("2001:db8:1::/48")
- // Don't accept the prefix length less than 64.
- assertNull(NetworkStackUtils.createInet6AddressFromEui64(prefix, eui64))
-
- prefix = IpPrefix("2001:db8:1::/64")
// IPv6 address string is formed by combining the IPv6 prefix("2001:db8:1::") and
// EUI64 converted from TEST_SRC_MAC, see above test for the output EUI64 example.
- val expected = parseNumericAddress("2001:db8:1::b898:76ff:fe54:3210") as Inet6Address
+ var expected = parseNumericAddress("2001:db8:1::b898:76ff:fe54:3210") as Inet6Address
+ assertEquals(expected, NetworkStackUtils.createInet6AddressFromEui64(prefix, eui64))
+
+ prefix = IpPrefix("2001:db8:1:2::/64")
+ expected = parseNumericAddress("2001:db8:1:2:b898:76ff:fe54:3210") as Inet6Address
assertEquals(expected, NetworkStackUtils.createInet6AddressFromEui64(prefix, eui64))
}
diff --git a/tests/unit/jni/apf_jni.cpp b/tests/unit/jni/apf_jni.cpp
index 8e14b3a4..39dd2c29 100644
--- a/tests/unit/jni/apf_jni.cpp
+++ b/tests/unit/jni/apf_jni.cpp
@@ -245,8 +245,6 @@ static jboolean com_android_server_ApfTest_dropsAllPackets(
return true;
}
-static char output_buffer[512];
-
static jobjectArray com_android_server_ApfTest_disassembleApf(
JNIEnv* env, jclass, jbyteArray jprogram) {
uint32_t program_len = env->GetArrayLength(jprogram);
@@ -256,9 +254,7 @@ static jobjectArray com_android_server_ApfTest_disassembleApf(
reinterpret_cast<jbyte*>(buf.data()));
std::vector<std::string> disassemble_output;
for (uint32_t pc = 0; pc < program_len;) {
- pc = apf_disassemble(buf.data(), program_len, pc, output_buffer,
- sizeof(output_buffer) / sizeof(output_buffer[0]));
- disassemble_output.emplace_back(output_buffer);
+ disassemble_output.emplace_back(apf_disassemble(buf.data(), program_len, &pc));
}
jclass stringClass = env->FindClass("java/lang/String");
jobjectArray disassembleOutput =
@@ -281,14 +277,14 @@ jbyteArray com_android_server_ApfTest_getTransmittedPacket(JNIEnv* env,
if (apf_test_tx_packet_len == 0) { return jdata; }
env->SetByteArrayRegion(jdata, 0, (jint) apf_test_tx_packet_len,
- reinterpret_cast<jbyte*>(apf_test_tx_packet));
+ reinterpret_cast<jbyte*>(apf_test_buffer));
return jdata;
}
void com_android_server_ApfTest_resetTransmittedPacketMemory(JNIEnv, jclass) {
apf_test_tx_packet_len = 0;
- memset(apf_test_tx_packet, 0, APF_TX_BUFFER_SIZE);
+ memset(apf_test_buffer, 0xff, sizeof(apf_test_buffer));
}
extern "C" jint JNI_OnLoad(JavaVM* vm, void*) {
diff --git a/tests/unit/src/android/net/apf/ApfTest.java b/tests/unit/src/android/net/apf/ApfTest.java
index 4e1187b1..5c6a906d 100644
--- a/tests/unit/src/android/net/apf/ApfTest.java
+++ b/tests/unit/src/android/net/apf/ApfTest.java
@@ -16,9 +16,11 @@
package android.net.apf;
-import static android.net.apf.ApfGenerator.APF_VERSION_4;
-import static android.net.apf.ApfGenerator.Register.R0;
-import static android.net.apf.ApfGenerator.Register.R1;
+import static android.net.apf.ApfV4Generator.APF_VERSION_4;
+import static android.net.apf.ApfV4Generator.DROP_LABEL;
+import static android.net.apf.ApfV4Generator.PASS_LABEL;
+import static android.net.apf.ApfV4Generator.Register.R0;
+import static android.net.apf.ApfV4Generator.Register.R1;
import static android.net.apf.ApfJniUtils.compareBpfApf;
import static android.net.apf.ApfJniUtils.compileToBpf;
import static android.net.apf.ApfJniUtils.dropsAllPackets;
@@ -37,6 +39,8 @@ import static android.system.OsConstants.IPPROTO_IPV6;
import static android.system.OsConstants.IPPROTO_TCP;
import static android.system.OsConstants.IPPROTO_UDP;
+import static com.android.net.module.util.HexDump.hexStringToByteArray;
+import static com.android.net.module.util.HexDump.toHexString;
import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ECHO_REQUEST_TYPE;
import static org.junit.Assert.assertEquals;
@@ -63,10 +67,10 @@ import android.net.NattKeepalivePacketDataParcelable;
import android.net.TcpKeepalivePacketDataParcelable;
import android.net.apf.ApfCounterTracker.Counter;
import android.net.apf.ApfFilter.ApfConfiguration;
-import android.net.apf.ApfGenerator.IllegalInstructionException;
import android.net.apf.ApfTestUtils.MockIpClientCallback;
import android.net.apf.ApfTestUtils.TestApfFilter;
import android.net.apf.ApfTestUtils.TestLegacyApfFilter;
+import android.net.apf.ApfV4Generator.IllegalInstructionException;
import android.net.metrics.IpConnectivityLog;
import android.os.Build;
import android.os.PowerManager;
@@ -197,11 +201,11 @@ public class ApfTest {
return config;
}
- private void assertPass(ApfGenerator gen) throws ApfGenerator.IllegalInstructionException {
+ private void assertPass(ApfV4Generator gen) throws ApfV4Generator.IllegalInstructionException {
ApfTestUtils.assertPass(mApfVersion, gen);
}
- private void assertDrop(ApfGenerator gen) throws ApfGenerator.IllegalInstructionException {
+ private void assertDrop(ApfV4Generator gen) throws ApfV4Generator.IllegalInstructionException {
ApfTestUtils.assertDrop(mApfVersion, gen);
}
@@ -221,13 +225,13 @@ public class ApfTest {
ApfTestUtils.assertDrop(mApfVersion, program, packet, filterAge);
}
- private void assertPass(ApfGenerator gen, byte[] packet, int filterAge)
- throws ApfGenerator.IllegalInstructionException {
+ private void assertPass(ApfV4Generator gen, byte[] packet, int filterAge)
+ throws ApfV4Generator.IllegalInstructionException {
ApfTestUtils.assertPass(mApfVersion, gen, packet, filterAge);
}
- private void assertDrop(ApfGenerator gen, byte[] packet, int filterAge)
- throws ApfGenerator.IllegalInstructionException {
+ private void assertDrop(ApfV4Generator gen, byte[] packet, int filterAge)
+ throws ApfV4Generator.IllegalInstructionException {
ApfTestUtils.assertDrop(mApfVersion, gen, packet, filterAge);
}
@@ -256,20 +260,20 @@ public class ApfTest {
// Empty program should pass because having the program counter reach the
// location immediately after the program indicates the packet should be
// passed to the AP.
- ApfGenerator gen = new ApfGenerator(MIN_APF_VERSION);
+ ApfV4Generator gen = new ApfV4Generator(MIN_APF_VERSION);
assertPass(gen);
// Test jumping to pass label.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addJump(gen.PASS_LABEL);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
+ gen.addJump(PASS_LABEL);
byte[] program = gen.generate();
assertEquals(1, program.length);
assertEquals((14 << 3) | (0 << 1) | 0, program[0]);
assertPass(program, new byte[MIN_PKT_SIZE], 0);
// Test jumping to drop label.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addJump(gen.DROP_LABEL);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
+ gen.addJump(DROP_LABEL);
program = gen.generate();
assertEquals(2, program.length);
assertEquals((14 << 3) | (1 << 1) | 0, program[0]);
@@ -277,362 +281,362 @@ public class ApfTest {
assertDrop(program, new byte[15], 15);
// Test jumping if equal to 0.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addJumpIfR0Equals(0, gen.DROP_LABEL);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
+ gen.addJumpIfR0Equals(0, DROP_LABEL);
assertDrop(gen);
// Test jumping if not equal to 0.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addJumpIfR0NotEquals(0, gen.DROP_LABEL);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
+ gen.addJumpIfR0NotEquals(0, DROP_LABEL);
assertPass(gen);
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addLoadImmediate(R0, 1);
- gen.addJumpIfR0NotEquals(0, gen.DROP_LABEL);
+ gen.addJumpIfR0NotEquals(0, DROP_LABEL);
assertDrop(gen);
// Test jumping if registers equal.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addJumpIfR0EqualsR1(gen.DROP_LABEL);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
+ gen.addJumpIfR0EqualsR1(DROP_LABEL);
assertDrop(gen);
// Test jumping if registers not equal.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addJumpIfR0NotEqualsR1(gen.DROP_LABEL);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
+ gen.addJumpIfR0NotEqualsR1(DROP_LABEL);
assertPass(gen);
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addLoadImmediate(R0, 1);
- gen.addJumpIfR0NotEqualsR1(gen.DROP_LABEL);
+ gen.addJumpIfR0NotEqualsR1(DROP_LABEL);
assertDrop(gen);
// Test load immediate.
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addLoadImmediate(R0, 1234567890);
- gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
+ gen.addJumpIfR0Equals(1234567890, DROP_LABEL);
assertDrop(gen);
// Test add.
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addAdd(1234567890);
- gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
+ gen.addJumpIfR0Equals(1234567890, DROP_LABEL);
assertDrop(gen);
// Test add with a small signed negative value.
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addAdd(-1);
- gen.addJumpIfR0Equals(-1, gen.DROP_LABEL);
+ gen.addJumpIfR0Equals(-1, DROP_LABEL);
assertDrop(gen);
// Test subtract.
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addAdd(-1234567890);
- gen.addJumpIfR0Equals(-1234567890, gen.DROP_LABEL);
+ gen.addJumpIfR0Equals(-1234567890, DROP_LABEL);
assertDrop(gen);
// Test or.
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addOr(1234567890);
- gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
+ gen.addJumpIfR0Equals(1234567890, DROP_LABEL);
assertDrop(gen);
// Test and.
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addLoadImmediate(R0, 1234567890);
gen.addAnd(123456789);
- gen.addJumpIfR0Equals(1234567890 & 123456789, gen.DROP_LABEL);
+ gen.addJumpIfR0Equals(1234567890 & 123456789, DROP_LABEL);
assertDrop(gen);
// Test left shift.
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addLoadImmediate(R0, 1234567890);
gen.addLeftShift(1);
- gen.addJumpIfR0Equals(1234567890 << 1, gen.DROP_LABEL);
+ gen.addJumpIfR0Equals(1234567890 << 1, DROP_LABEL);
assertDrop(gen);
// Test right shift.
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addLoadImmediate(R0, 1234567890);
gen.addRightShift(1);
- gen.addJumpIfR0Equals(1234567890 >> 1, gen.DROP_LABEL);
+ gen.addJumpIfR0Equals(1234567890 >> 1, DROP_LABEL);
assertDrop(gen);
// Test multiply.
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addLoadImmediate(R0, 123456789);
gen.addMul(2);
- gen.addJumpIfR0Equals(123456789 * 2, gen.DROP_LABEL);
+ gen.addJumpIfR0Equals(123456789 * 2, DROP_LABEL);
assertDrop(gen);
// Test divide.
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addLoadImmediate(R0, 1234567890);
gen.addDiv(2);
- gen.addJumpIfR0Equals(1234567890 / 2, gen.DROP_LABEL);
+ gen.addJumpIfR0Equals(1234567890 / 2, DROP_LABEL);
assertDrop(gen);
// Test divide by zero.
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addDiv(0);
- gen.addJump(gen.DROP_LABEL);
+ gen.addJump(DROP_LABEL);
assertPass(gen);
// Test add.
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addLoadImmediate(R1, 1234567890);
gen.addAddR1();
- gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
+ gen.addJumpIfR0Equals(1234567890, DROP_LABEL);
assertDrop(gen);
// Test subtract.
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addLoadImmediate(R1, -1234567890);
gen.addAddR1();
- gen.addJumpIfR0Equals(-1234567890, gen.DROP_LABEL);
+ gen.addJumpIfR0Equals(-1234567890, DROP_LABEL);
assertDrop(gen);
// Test or.
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addLoadImmediate(R1, 1234567890);
gen.addOrR1();
- gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
+ gen.addJumpIfR0Equals(1234567890, DROP_LABEL);
assertDrop(gen);
// Test and.
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addLoadImmediate(R0, 1234567890);
gen.addLoadImmediate(R1, 123456789);
gen.addAndR1();
- gen.addJumpIfR0Equals(1234567890 & 123456789, gen.DROP_LABEL);
+ gen.addJumpIfR0Equals(1234567890 & 123456789, DROP_LABEL);
assertDrop(gen);
// Test left shift.
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addLoadImmediate(R0, 1234567890);
gen.addLoadImmediate(R1, 1);
gen.addLeftShiftR1();
- gen.addJumpIfR0Equals(1234567890 << 1, gen.DROP_LABEL);
+ gen.addJumpIfR0Equals(1234567890 << 1, DROP_LABEL);
assertDrop(gen);
// Test right shift.
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addLoadImmediate(R0, 1234567890);
gen.addLoadImmediate(R1, -1);
gen.addLeftShiftR1();
- gen.addJumpIfR0Equals(1234567890 >> 1, gen.DROP_LABEL);
+ gen.addJumpIfR0Equals(1234567890 >> 1, DROP_LABEL);
assertDrop(gen);
// Test multiply.
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addLoadImmediate(R0, 123456789);
gen.addLoadImmediate(R1, 2);
gen.addMulR1();
- gen.addJumpIfR0Equals(123456789 * 2, gen.DROP_LABEL);
+ gen.addJumpIfR0Equals(123456789 * 2, DROP_LABEL);
assertDrop(gen);
// Test divide.
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addLoadImmediate(R0, 1234567890);
gen.addLoadImmediate(R1, 2);
gen.addDivR1();
- gen.addJumpIfR0Equals(1234567890 / 2, gen.DROP_LABEL);
+ gen.addJumpIfR0Equals(1234567890 / 2, DROP_LABEL);
assertDrop(gen);
// Test divide by zero.
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addDivR1();
- gen.addJump(gen.DROP_LABEL);
+ gen.addJump(DROP_LABEL);
assertPass(gen);
// Test byte load.
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addLoad8(R0, 1);
- gen.addJumpIfR0Equals(45, gen.DROP_LABEL);
+ gen.addJumpIfR0Equals(45, DROP_LABEL);
assertDrop(gen, new byte[]{123,45,0,0,0,0,0,0,0,0,0,0,0,0,0}, 0);
// Test out of bounds load.
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addLoad8(R0, 16);
- gen.addJumpIfR0Equals(0, gen.DROP_LABEL);
+ gen.addJumpIfR0Equals(0, DROP_LABEL);
assertPass(gen, new byte[]{123,45,0,0,0,0,0,0,0,0,0,0,0,0,0}, 0);
// Test half-word load.
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addLoad16(R0, 1);
- gen.addJumpIfR0Equals((45 << 8) | 67, gen.DROP_LABEL);
+ gen.addJumpIfR0Equals((45 << 8) | 67, DROP_LABEL);
assertDrop(gen, new byte[]{123,45,67,0,0,0,0,0,0,0,0,0,0,0,0}, 0);
// Test word load.
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addLoad32(R0, 1);
- gen.addJumpIfR0Equals((45 << 24) | (67 << 16) | (89 << 8) | 12, gen.DROP_LABEL);
+ gen.addJumpIfR0Equals((45 << 24) | (67 << 16) | (89 << 8) | 12, DROP_LABEL);
assertDrop(gen, new byte[]{123,45,67,89,12,0,0,0,0,0,0,0,0,0,0}, 0);
// Test byte indexed load.
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addLoadImmediate(R1, 1);
gen.addLoad8Indexed(R0, 0);
- gen.addJumpIfR0Equals(45, gen.DROP_LABEL);
+ gen.addJumpIfR0Equals(45, DROP_LABEL);
assertDrop(gen, new byte[]{123,45,0,0,0,0,0,0,0,0,0,0,0,0,0}, 0);
// Test out of bounds indexed load.
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addLoadImmediate(R1, 8);
gen.addLoad8Indexed(R0, 8);
- gen.addJumpIfR0Equals(0, gen.DROP_LABEL);
+ gen.addJumpIfR0Equals(0, DROP_LABEL);
assertPass(gen, new byte[]{123,45,0,0,0,0,0,0,0,0,0,0,0,0,0}, 0);
// Test half-word indexed load.
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addLoadImmediate(R1, 1);
gen.addLoad16Indexed(R0, 0);
- gen.addJumpIfR0Equals((45 << 8) | 67, gen.DROP_LABEL);
+ gen.addJumpIfR0Equals((45 << 8) | 67, DROP_LABEL);
assertDrop(gen, new byte[]{123,45,67,0,0,0,0,0,0,0,0,0,0,0,0}, 0);
// Test word indexed load.
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addLoadImmediate(R1, 1);
gen.addLoad32Indexed(R0, 0);
- gen.addJumpIfR0Equals((45 << 24) | (67 << 16) | (89 << 8) | 12, gen.DROP_LABEL);
+ gen.addJumpIfR0Equals((45 << 24) | (67 << 16) | (89 << 8) | 12, DROP_LABEL);
assertDrop(gen, new byte[]{123,45,67,89,12,0,0,0,0,0,0,0,0,0,0}, 0);
// Test jumping if greater than.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addJumpIfR0GreaterThan(0, gen.DROP_LABEL);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
+ gen.addJumpIfR0GreaterThan(0, DROP_LABEL);
assertPass(gen);
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addLoadImmediate(R0, 1);
- gen.addJumpIfR0GreaterThan(0, gen.DROP_LABEL);
+ gen.addJumpIfR0GreaterThan(0, DROP_LABEL);
assertDrop(gen);
// Test jumping if less than.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addJumpIfR0LessThan(0, gen.DROP_LABEL);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
+ gen.addJumpIfR0LessThan(0, DROP_LABEL);
assertPass(gen);
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addJumpIfR0LessThan(1, gen.DROP_LABEL);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
+ gen.addJumpIfR0LessThan(1, DROP_LABEL);
assertDrop(gen);
// Test jumping if any bits set.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addJumpIfR0AnyBitsSet(3, gen.DROP_LABEL);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
+ gen.addJumpIfR0AnyBitsSet(3, DROP_LABEL);
assertPass(gen);
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addLoadImmediate(R0, 1);
- gen.addJumpIfR0AnyBitsSet(3, gen.DROP_LABEL);
+ gen.addJumpIfR0AnyBitsSet(3, DROP_LABEL);
assertDrop(gen);
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addLoadImmediate(R0, 3);
- gen.addJumpIfR0AnyBitsSet(3, gen.DROP_LABEL);
+ gen.addJumpIfR0AnyBitsSet(3, DROP_LABEL);
assertDrop(gen);
// Test jumping if register greater than.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addJumpIfR0GreaterThanR1(gen.DROP_LABEL);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
+ gen.addJumpIfR0GreaterThanR1(DROP_LABEL);
assertPass(gen);
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addLoadImmediate(R0, 2);
gen.addLoadImmediate(R1, 1);
- gen.addJumpIfR0GreaterThanR1(gen.DROP_LABEL);
+ gen.addJumpIfR0GreaterThanR1(DROP_LABEL);
assertDrop(gen);
// Test jumping if register less than.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addJumpIfR0LessThanR1(gen.DROP_LABEL);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
+ gen.addJumpIfR0LessThanR1(DROP_LABEL);
assertPass(gen);
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addLoadImmediate(R1, 1);
- gen.addJumpIfR0LessThanR1(gen.DROP_LABEL);
+ gen.addJumpIfR0LessThanR1(DROP_LABEL);
assertDrop(gen);
// Test jumping if any bits set in register.
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addLoadImmediate(R1, 3);
- gen.addJumpIfR0AnyBitsSetR1(gen.DROP_LABEL);
+ gen.addJumpIfR0AnyBitsSetR1(DROP_LABEL);
assertPass(gen);
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addLoadImmediate(R1, 3);
gen.addLoadImmediate(R0, 1);
- gen.addJumpIfR0AnyBitsSetR1(gen.DROP_LABEL);
+ gen.addJumpIfR0AnyBitsSetR1(DROP_LABEL);
assertDrop(gen);
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addLoadImmediate(R1, 3);
gen.addLoadImmediate(R0, 3);
- gen.addJumpIfR0AnyBitsSetR1(gen.DROP_LABEL);
+ gen.addJumpIfR0AnyBitsSetR1(DROP_LABEL);
assertDrop(gen);
// Test load from memory.
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addLoadFromMemory(R0, 0);
- gen.addJumpIfR0Equals(0, gen.DROP_LABEL);
+ gen.addJumpIfR0Equals(0, DROP_LABEL);
assertDrop(gen);
// Test store to memory.
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addLoadImmediate(R1, 1234567890);
gen.addStoreToMemory(R1, 12);
gen.addLoadFromMemory(R0, 12);
- gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
+ gen.addJumpIfR0Equals(1234567890, DROP_LABEL);
assertDrop(gen);
// Test filter age pre-filled memory.
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addLoadFromMemory(R0, gen.FILTER_AGE_MEMORY_SLOT);
- gen.addJumpIfR0Equals(123, gen.DROP_LABEL);
+ gen.addJumpIfR0Equals(123, DROP_LABEL);
assertDrop(gen, new byte[MIN_PKT_SIZE], 123);
// Test packet size pre-filled memory.
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addLoadFromMemory(R0, gen.PACKET_SIZE_MEMORY_SLOT);
- gen.addJumpIfR0Equals(MIN_PKT_SIZE, gen.DROP_LABEL);
+ gen.addJumpIfR0Equals(MIN_PKT_SIZE, DROP_LABEL);
assertDrop(gen);
// Test IPv4 header size pre-filled memory.
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addLoadFromMemory(R0, gen.IPV4_HEADER_SIZE_MEMORY_SLOT);
- gen.addJumpIfR0Equals(20, gen.DROP_LABEL);
+ gen.addJumpIfR0Equals(20, DROP_LABEL);
assertDrop(gen, new byte[]{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0x45}, 0);
// Test not.
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addLoadImmediate(R0, 1234567890);
gen.addNot(R0);
- gen.addJumpIfR0Equals(~1234567890, gen.DROP_LABEL);
+ gen.addJumpIfR0Equals(~1234567890, DROP_LABEL);
assertDrop(gen);
// Test negate.
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addLoadImmediate(R0, 1234567890);
gen.addNeg(R0);
- gen.addJumpIfR0Equals(-1234567890, gen.DROP_LABEL);
+ gen.addJumpIfR0Equals(-1234567890, DROP_LABEL);
assertDrop(gen);
// Test move.
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addLoadImmediate(R1, 1234567890);
gen.addMove(R0);
- gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
+ gen.addJumpIfR0Equals(1234567890, DROP_LABEL);
assertDrop(gen);
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addLoadImmediate(R0, 1234567890);
gen.addMove(R1);
- gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
+ gen.addJumpIfR0Equals(1234567890, DROP_LABEL);
assertDrop(gen);
// Test swap.
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addLoadImmediate(R1, 1234567890);
gen.addSwap();
- gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
+ gen.addJumpIfR0Equals(1234567890, DROP_LABEL);
assertDrop(gen);
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addLoadImmediate(R0, 1234567890);
gen.addSwap();
- gen.addJumpIfR0Equals(0, gen.DROP_LABEL);
+ gen.addJumpIfR0Equals(0, DROP_LABEL);
assertDrop(gen);
// Test jump if bytes not equal.
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addLoadImmediate(R0, 1);
- gen.addJumpIfBytesAtR0NotEqual(new byte[]{123}, gen.DROP_LABEL);
+ gen.addJumpIfBytesAtR0NotEqual(new byte[]{123}, DROP_LABEL);
program = gen.generate();
assertEquals(6, program.length);
assertEquals((13 << 3) | (1 << 1) | 0, program[0]);
@@ -642,34 +646,34 @@ public class ApfTest {
assertEquals(1, program[4]);
assertEquals(123, program[5]);
assertDrop(program, new byte[MIN_PKT_SIZE], 0);
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addLoadImmediate(R0, 1);
- gen.addJumpIfBytesAtR0NotEqual(new byte[]{123}, gen.DROP_LABEL);
+ gen.addJumpIfBytesAtR0NotEqual(new byte[]{123}, DROP_LABEL);
byte[] packet123 = {0,123,0,0,0,0,0,0,0,0,0,0,0,0,0};
assertPass(gen, packet123, 0);
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addJumpIfBytesAtR0NotEqual(new byte[]{123}, gen.DROP_LABEL);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
+ gen.addJumpIfBytesAtR0NotEqual(new byte[]{123}, DROP_LABEL);
assertDrop(gen, packet123, 0);
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addLoadImmediate(R0, 1);
- gen.addJumpIfBytesAtR0NotEqual(new byte[]{1, 2, 30, 4, 5}, gen.DROP_LABEL);
+ gen.addJumpIfBytesAtR0NotEqual(new byte[]{1, 2, 30, 4, 5}, DROP_LABEL);
byte[] packet12345 = {0,1,2,3,4,5,0,0,0,0,0,0,0,0,0};
assertDrop(gen, packet12345, 0);
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addLoadImmediate(R0, 1);
- gen.addJumpIfBytesAtR0NotEqual(new byte[]{1, 2, 3, 4, 5}, gen.DROP_LABEL);
+ gen.addJumpIfBytesAtR0NotEqual(new byte[]{1, 2, 3, 4, 5}, DROP_LABEL);
assertPass(gen, packet12345, 0);
}
- @Test(expected = ApfGenerator.IllegalInstructionException.class)
+ @Test(expected = ApfV4Generator.IllegalInstructionException.class)
public void testApfGeneratorWantsV2OrGreater() throws Exception {
// The minimum supported APF version is 2.
- new ApfGenerator(1);
+ new ApfV4Generator(1);
}
@Test
public void testApfDataOpcodesWantApfV3() throws IllegalInstructionException, Exception {
- ApfGenerator gen = new ApfGenerator(MIN_APF_VERSION);
+ ApfV4Generator gen = new ApfV4Generator(MIN_APF_VERSION);
try {
gen.addStoreData(R0, 0);
fail();
@@ -689,25 +693,25 @@ public class ApfTest {
*/
@Test
public void testImmediateEncoding() throws IllegalInstructionException {
- ApfGenerator gen;
+ ApfV4Generator gen;
// 0-byte immediate: li R0, 0
- gen = new ApfGenerator(4);
+ gen = new ApfV4Generator(4);
gen.addLoadImmediate(R0, 0);
assertProgramEquals(new byte[]{LI_OP | SIZE0}, gen.generate());
// 1-byte immediate: li R0, 42
- gen = new ApfGenerator(4);
+ gen = new ApfV4Generator(4);
gen.addLoadImmediate(R0, 42);
assertProgramEquals(new byte[]{LI_OP | SIZE8, 42}, gen.generate());
// 2-byte immediate: li R1, 0x1234
- gen = new ApfGenerator(4);
+ gen = new ApfV4Generator(4);
gen.addLoadImmediate(R1, 0x1234);
assertProgramEquals(new byte[]{LI_OP | SIZE16 | R1_REG, 0x12, 0x34}, gen.generate());
// 4-byte immediate: li R0, 0x12345678
- gen = new ApfGenerator(3);
+ gen = new ApfV4Generator(3);
gen.addLoadImmediate(R0, 0x12345678);
assertProgramEquals(
new byte[]{LI_OP | SIZE32, 0x12, 0x34, 0x56, 0x78},
@@ -719,21 +723,21 @@ public class ApfTest {
*/
@Test
public void testNegativeImmediateEncoding() throws IllegalInstructionException {
- ApfGenerator gen;
+ ApfV4Generator gen;
// 1-byte negative immediate: li R0, -42
- gen = new ApfGenerator(3);
+ gen = new ApfV4Generator(3);
gen.addLoadImmediate(R0, -42);
assertProgramEquals(new byte[]{LI_OP | SIZE8, -42}, gen.generate());
// 2-byte negative immediate: li R1, -0x1122
- gen = new ApfGenerator(3);
+ gen = new ApfV4Generator(3);
gen.addLoadImmediate(R1, -0x1122);
assertProgramEquals(new byte[]{LI_OP | SIZE16 | R1_REG, (byte)0xEE, (byte)0xDE},
gen.generate());
// 4-byte negative immediate: li R0, -0x11223344
- gen = new ApfGenerator(3);
+ gen = new ApfV4Generator(3);
gen.addLoadImmediate(R0, -0x11223344);
assertProgramEquals(
new byte[]{LI_OP | SIZE32, (byte)0xEE, (byte)0xDD, (byte)0xCC, (byte)0xBC},
@@ -745,26 +749,26 @@ public class ApfTest {
*/
@Test
public void testLoadStoreDataEncoding() throws IllegalInstructionException {
- ApfGenerator gen;
+ ApfV4Generator gen;
// Load data with no offset: lddw R0, [0 + r1]
- gen = new ApfGenerator(APF_VERSION_4);
+ gen = new ApfV4Generator(APF_VERSION_4);
gen.addLoadData(R0, 0);
assertProgramEquals(new byte[]{LDDW_OP | SIZE0}, gen.generate());
// Store data with 8bit negative offset: lddw r0, [-42 + r1]
- gen = new ApfGenerator(APF_VERSION_4);
+ gen = new ApfV4Generator(APF_VERSION_4);
gen.addStoreData(R0, -42);
assertProgramEquals(new byte[]{STDW_OP | SIZE8, -42}, gen.generate());
// Store data to R1 with 16bit negative offset: stdw r1, [-0x1122 + r0]
- gen = new ApfGenerator(APF_VERSION_4);
+ gen = new ApfV4Generator(APF_VERSION_4);
gen.addStoreData(R1, -0x1122);
assertProgramEquals(new byte[]{STDW_OP | SIZE16 | R1_REG, (byte)0xEE, (byte)0xDE},
gen.generate());
// Load data to R1 with 32bit negative offset: lddw r1, [0xDEADBEEF + r0]
- gen = new ApfGenerator(APF_VERSION_4);
+ gen = new ApfV4Generator(APF_VERSION_4);
gen.addLoadData(R1, 0xDEADBEEF);
assertProgramEquals(
new byte[]{LDDW_OP | SIZE32 | R1_REG,
@@ -782,12 +786,12 @@ public class ApfTest {
byte[] expected_data = data.clone();
// No memory access instructions: should leave the data segment untouched.
- ApfGenerator gen = new ApfGenerator(APF_VERSION_4);
+ ApfV4Generator gen = new ApfV4Generator(APF_VERSION_4);
assertDataMemoryContents(PASS, gen.generate(), packet, data, expected_data);
// Expect value 0x87654321 to be stored starting from address -11 from the end of the
// data buffer, in big-endian order.
- gen = new ApfGenerator(APF_VERSION_4);
+ gen = new ApfV4Generator(APF_VERSION_4);
gen.addLoadImmediate(R0, 0x87654321);
gen.addLoadImmediate(R1, -5);
gen.addStoreData(R0, -6); // -5 + -6 = -11 (offset +5 with data_len=16)
@@ -804,10 +808,10 @@ public class ApfTest {
@Test
public void testApfDataRead() throws IllegalInstructionException, Exception {
// Program that DROPs if address 10 (-6) contains 0x87654321.
- ApfGenerator gen = new ApfGenerator(APF_VERSION_4);
+ ApfV4Generator gen = new ApfV4Generator(APF_VERSION_4);
gen.addLoadImmediate(R1, 1000);
gen.addLoadData(R0, -1006); // 1000 + -1006 = -6 (offset +10 with data_len=16)
- gen.addJumpIfR0Equals(0x87654321, gen.DROP_LABEL);
+ gen.addJumpIfR0Equals(0x87654321, DROP_LABEL);
byte[] program = gen.generate();
byte[] packet = new byte[MIN_PKT_SIZE];
@@ -833,7 +837,7 @@ public class ApfTest {
*/
@Test
public void testApfDataReadModifyWrite() throws IllegalInstructionException, Exception {
- ApfGenerator gen = new ApfGenerator(APF_VERSION_4);
+ ApfV4Generator gen = new ApfV4Generator(APF_VERSION_4);
gen.addLoadImmediate(R1, -22);
gen.addLoadData(R0, 0); // Load from address 32 -22 + 0 = 10
gen.addAdd(0x78453412); // 87654321 + 78453412 = FFAA7733
@@ -860,38 +864,38 @@ public class ApfTest {
byte[] expected_data = data;
// Program that DROPs unconditionally. This is our the baseline.
- ApfGenerator gen = new ApfGenerator(APF_VERSION_4);
+ ApfV4Generator gen = new ApfV4Generator(APF_VERSION_4);
gen.addLoadImmediate(R0, 3);
gen.addLoadData(R1, 7);
- gen.addJump(gen.DROP_LABEL);
+ gen.addJump(DROP_LABEL);
assertDataMemoryContents(DROP, gen.generate(), packet, data, expected_data);
// Same program as before, but this time we're trying to load past the end of the data.
- gen = new ApfGenerator(APF_VERSION_4);
+ gen = new ApfV4Generator(APF_VERSION_4);
gen.addLoadImmediate(R0, 20);
gen.addLoadData(R1, 15); // 20 + 15 > 32
- gen.addJump(gen.DROP_LABEL); // Not reached.
+ gen.addJump(DROP_LABEL); // Not reached.
assertDataMemoryContents(PASS, gen.generate(), packet, data, expected_data);
// Subtracting an immediate should work...
- gen = new ApfGenerator(APF_VERSION_4);
+ gen = new ApfV4Generator(APF_VERSION_4);
gen.addLoadImmediate(R0, 20);
gen.addLoadData(R1, -4);
- gen.addJump(gen.DROP_LABEL);
+ gen.addJump(DROP_LABEL);
assertDataMemoryContents(DROP, gen.generate(), packet, data, expected_data);
// ...and underflowing simply wraps around to the end of the buffer...
- gen = new ApfGenerator(APF_VERSION_4);
+ gen = new ApfV4Generator(APF_VERSION_4);
gen.addLoadImmediate(R0, 20);
gen.addLoadData(R1, -30);
- gen.addJump(gen.DROP_LABEL);
+ gen.addJump(DROP_LABEL);
assertDataMemoryContents(DROP, gen.generate(), packet, data, expected_data);
// ...but doesn't allow accesses before the start of the buffer
- gen = new ApfGenerator(APF_VERSION_4);
+ gen = new ApfV4Generator(APF_VERSION_4);
gen.addLoadImmediate(R0, 20);
gen.addLoadData(R1, -1000);
- gen.addJump(gen.DROP_LABEL); // Not reached.
+ gen.addJump(DROP_LABEL); // Not reached.
assertDataMemoryContents(PASS, gen.generate(), packet, data, expected_data);
}
@@ -1379,18 +1383,18 @@ public class ApfTest {
}
/** Adds to the program a no-op instruction that is one byte long. */
- private void addOneByteNoop(ApfGenerator gen) {
+ private void addOneByteNoop(ApfV4Generator gen) {
gen.addLeftShift(0);
}
@Test
public void testAddOneByteNoopAddsOneByte() throws Exception {
- ApfGenerator gen = new ApfGenerator(MIN_APF_VERSION);
+ ApfV4Generator gen = new ApfV4Generator(MIN_APF_VERSION);
addOneByteNoop(gen);
assertEquals(1, gen.generate().length);
final int count = 42;
- gen = new ApfGenerator(MIN_APF_VERSION);
+ gen = new ApfV4Generator(MIN_APF_VERSION);
for (int i = 0; i < count; i++) {
addOneByteNoop(gen);
}
@@ -1469,8 +1473,8 @@ public class ApfTest {
apfFilter.shutdown();
}
- private ApfGenerator generateDnsFilter(boolean ipv6, String... labels) throws Exception {
- ApfGenerator gen = new ApfGenerator(MIN_APF_VERSION);
+ private ApfV4Generator generateDnsFilter(boolean ipv6, String... labels) throws Exception {
+ ApfV4Generator gen = new ApfV4Generator(MIN_APF_VERSION);
gen.addLoadImmediate(R1, ipv6 ? IPV6_HEADER_LEN : IPV4_HEADER_LEN);
DnsUtils.generateFilter(gen, labels);
return gen;
@@ -1479,7 +1483,7 @@ public class ApfTest {
private void doTestDnsParsing(boolean expectPass, boolean ipv6, String filterName,
byte[] pkt) throws Exception {
final String[] labels = filterName.split(/*regex=*/ "[.]");
- ApfGenerator gen = generateDnsFilter(ipv6, labels);
+ ApfV4Generator gen = generateDnsFilter(ipv6, labels);
// Hack to prevent the APF instruction limit triggering.
for (int i = 0; i < 500; i++) {
@@ -1543,7 +1547,7 @@ public class ApfTest {
String filterName) throws Exception {
final String[] labels = filterName.split(/*regex=*/ "[.]");
- ApfGenerator gen = generateDnsFilter(/*ipv6=*/ true, labels);
+ ApfV4Generator gen = generateDnsFilter(/*ipv6=*/ true, labels);
assertEquals("Program for " + filterName + " had unexpected length:",
expectedLength, gen.generate().length);
}
@@ -1567,7 +1571,7 @@ public class ApfTest {
// Check that the generated code, when the program contains the specified number of extra
// bytes, is capable of dropping the packet.
- ApfGenerator gen = generateDnsFilter(/*ipv6=*/ true, labels);
+ ApfV4Generator gen = generateDnsFilter(/*ipv6=*/ true, labels);
for (int i = 0; i < expectedNecessaryOverhead; i++) {
addOneByteNoop(gen);
}
@@ -3401,4 +3405,420 @@ public class ApfTest {
public void testNoMetricsWrittenForShortDuration_LegacyApfFilter() throws Exception {
verifyNoMetricsWrittenForShortDuration(true /* isLegacy */);
}
+
+ @Test
+ public void testFullApfV4ProgramGenerationIPV6() throws IllegalInstructionException {
+ ApfV4Generator gen = new ApfV4Generator(APF_VERSION_4);
+ gen.addLoadImmediate(R1, -4);
+ gen.addLoadData(R0, 0);
+ gen.addAdd(1);
+ gen.addStoreData(R0, 0);
+ gen.addLoad16(R0, 12);
+ gen.addLoadImmediate(R1, -108);
+ gen.addJumpIfR0LessThan(0x600, "LABEL_504");
+ gen.addLoadImmediate(R1, -112);
+ gen.addJumpIfR0Equals(0x88a2, "LABEL_504");
+ gen.addJumpIfR0Equals(0x88a4, "LABEL_504");
+ gen.addJumpIfR0Equals(0x88b8, "LABEL_504");
+ gen.addJumpIfR0Equals(0x88cd, "LABEL_504");
+ gen.addJumpIfR0Equals(0x88e1, "LABEL_504");
+ gen.addJumpIfR0Equals(0x88e3, "LABEL_504");
+ gen.addJumpIfR0NotEquals(0x806, "LABEL_116");
+ gen.addLoadImmediate(R0, 14);
+ gen.addLoadImmediate(R1, -36);
+ gen.addJumpIfBytesAtR0NotEqual(hexStringToByteArray("000108000604"), "LABEL_498");
+ gen.addLoad16(R0, 20);
+ gen.addJumpIfR0Equals(0x1, "LABEL_102");
+ gen.addLoadImmediate(R1, -40);
+ gen.addJumpIfR0NotEquals(0x2, "LABEL_498");
+ gen.addLoad32(R0, 28);
+ gen.addLoadImmediate(R1, -116);
+ gen.addJumpIfR0Equals(0x0, "LABEL_504");
+ gen.addLoadImmediate(R0, 0);
+ gen.addLoadImmediate(R1, -44);
+ gen.addJumpIfBytesAtR0NotEqual(hexStringToByteArray("ffffffffffff"), "LABEL_498");
+
+ gen.defineLabel("LABEL_102");
+ gen.addLoad32(R0, 38);
+ gen.addLoadImmediate(R1, -64);
+ gen.addJumpIfR0Equals(0x0, "LABEL_504");
+ gen.addLoadImmediate(R1, -8);
+ gen.addJump("LABEL_498");
+
+ gen.defineLabel("LABEL_116");
+ gen.addLoad16(R0, 12);
+ gen.addJumpIfR0NotEquals(0x800, "LABEL_207");
+ gen.addLoad8(R0, 23);
+ gen.addJumpIfR0NotEquals(0x11, "LABEL_159");
+ gen.addLoad16(R0, 20);
+ gen.addJumpIfR0AnyBitsSet(0x1fff, "LABEL_159");
+ gen.addLoadFromMemory(R1, 13);
+ gen.addLoad16Indexed(R0, 16);
+ gen.addJumpIfR0NotEquals(0x44, "LABEL_159");
+ gen.addLoadImmediate(R0, 50);
+ gen.addAddR1();
+ gen.addJumpIfBytesAtR0NotEqual(hexStringToByteArray("e212507c6345"), "LABEL_159");
+ gen.addLoadImmediate(R1, -12);
+ gen.addJump("LABEL_498");
+
+ gen.defineLabel("LABEL_159");
+ gen.addLoad8(R0, 30);
+ gen.addAnd(240);
+ gen.addLoadImmediate(R1, -84);
+ gen.addJumpIfR0Equals(0xe0, "LABEL_504");
+ gen.addLoadImmediate(R1, -76);
+ gen.addLoad32(R0, 30);
+ gen.addJumpIfR0Equals(0xffffffff, "LABEL_504");
+ gen.addLoadImmediate(R1, -24);
+ gen.addLoadImmediate(R0, 0);
+ gen.addJumpIfBytesAtR0NotEqual(hexStringToByteArray("ffffffffffff"), "LABEL_498");
+ gen.addLoadImmediate(R1, -72);
+ gen.addJump("LABEL_504");
+ gen.addLoadImmediate(R1, -16);
+ gen.addJump("LABEL_498");
+
+ gen.defineLabel("LABEL_207");
+ gen.addJumpIfR0Equals(0x86dd, "LABEL_231");
+ gen.addLoadImmediate(R0, 0);
+ gen.addLoadImmediate(R1, -48);
+ gen.addJumpIfBytesAtR0NotEqual(hexStringToByteArray("ffffffffffff"), "LABEL_498");
+ gen.addLoadImmediate(R1, -56);
+ gen.addJump("LABEL_504");
+
+ gen.defineLabel("LABEL_231");
+ gen.addLoad8(R0, 20);
+ gen.addJumpIfR0Equals(0x3a, "LABEL_249");
+ gen.addLoadImmediate(R1, -104);
+ gen.addLoad8(R0, 38);
+ gen.addJumpIfR0Equals(0xff, "LABEL_504");
+ gen.addLoadImmediate(R1, -32);
+ gen.addJump("LABEL_498");
+
+ gen.defineLabel("LABEL_249");
+ gen.addLoad8(R0, 54);
+ gen.addLoadImmediate(R1, -88);
+ gen.addJumpIfR0Equals(0x85, "LABEL_504");
+ gen.addJumpIfR0NotEquals(0x88, "LABEL_283");
+ gen.addLoadImmediate(R0, 38);
+ gen.addJumpIfBytesAtR0NotEqual(hexStringToByteArray("ff0200000000000000000000000000"), "LABEL_283");
+ gen.addLoadImmediate(R1, -92);
+ gen.addJump("LABEL_504");
+
+ gen.defineLabel("LABEL_283");
+ gen.addLoadFromMemory(R0, 14);
+ gen.addJumpIfR0NotEquals(0xa6, "LABEL_496");
+ gen.addLoadFromMemory(R0, 15);
+ gen.addJumpIfR0GreaterThan(0x254, "LABEL_496");
+ gen.addLoadImmediate(R0, 0);
+ gen.addJumpIfBytesAtR0NotEqual(hexStringToByteArray("e212507c6345648788fd6df086dd68"), "LABEL_496");
+ gen.addLoadImmediate(R0, 18);
+ gen.addJumpIfBytesAtR0NotEqual(hexStringToByteArray("00703afffe800000000000002a0079e10abc1539fe80000000000000e01250fffe7c63458600"), "LABEL_496");
+ gen.addLoadImmediate(R0, 58);
+ gen.addJumpIfBytesAtR0NotEqual(hexStringToByteArray("4000"), "LABEL_496");
+ gen.addLoad16(R0, 60);
+ gen.addJumpIfR0LessThan(0x254, "LABEL_496");
+ gen.addLoadImmediate(R0, 62);
+ gen.addJumpIfBytesAtR0NotEqual(hexStringToByteArray("0000000000000000"), "LABEL_496");
+ gen.addLoadImmediate(R0, 78);
+ gen.addJumpIfBytesAtR0NotEqual(hexStringToByteArray("19050000"), "LABEL_496");
+ gen.addLoad32(R0, 82);
+ gen.addJumpIfR0LessThan(0x254, "LABEL_496");
+ gen.addLoadImmediate(R0, 86);
+ gen.addJumpIfBytesAtR0NotEqual(hexStringToByteArray("2001486048600000000000000000646420014860486000000000000000000064"), "LABEL_496");
+ gen.addLoadImmediate(R0, 118);
+ gen.addJumpIfBytesAtR0NotEqual(hexStringToByteArray("030440c0"), "LABEL_496");
+ gen.addLoad32(R0, 122);
+ gen.addJumpIfR0LessThan(0x254, "LABEL_496");
+ gen.addLoad32(R0, 126);
+ gen.addJumpIfR0LessThan(0x254, "LABEL_496");
+ gen.addLoadImmediate(R0, 130);
+ gen.addJumpIfBytesAtR0NotEqual(hexStringToByteArray("00000000"), "LABEL_496");
+ gen.addLoadImmediate(R0, 134);
+ gen.addJumpIfBytesAtR0NotEqual(hexStringToByteArray("2a0079e10abc15390000000000000000"), "LABEL_496");
+ gen.addLoadImmediate(R1, -60);
+ gen.addJump("LABEL_504");
+
+ gen.defineLabel("LABEL_496");
+ gen.addLoadImmediate(R1, -28);
+
+ gen.defineLabel("LABEL_498");
+ gen.addLoadData(R0, 0);
+ gen.addAdd(1);
+ gen.addStoreData(R0, 0);
+ gen.addJump(PASS_LABEL);
+
+ gen.defineLabel("LABEL_504");
+ gen.addLoadData(R0, 0);
+ gen.addAdd(1);
+ gen.addStoreData(R0, 0);
+ gen.addJump(DROP_LABEL);
+
+ byte[] program = gen.generate();
+ final String programString = toHexString(program).toLowerCase();
+ final String referenceProgramHexString = "6bfcb03a01b8120c6b949401e906006b907c01e288a27c01dd88a47c01d888b87c01d388cd7c01ce88e17c01c988e384004008066a0e6bdca401af000600010800060412147a1e016bd88401a300021a1c6b8c7c01a00000686bd4a4018c0006ffffffffffff1a266bc07c018900006bf874017e120c84005408000a17821f1112149c00181fffab0d2a108211446a3239a20506e212507c63456bf47401530a1e52f06bac7c014e00e06bb41a1e7e00000141ffffffff6be868a4012d0006ffffffffffff6bb874012e6bf07401237c001386dd686bd0a401100006ffffffffffff6bc87401110a147a0d3a6b980a267c010300ff6be072f90a366ba87af8858218886a26a2040fff02000000000000000000000000006ba472ddaa0e82d0a6aa0f8c00c9025468a2b60fe212507c6345648788fd6df086dd686a12a28b2600703afffe800000000000002a0079e10abc1539fe80000000000000e01250fffe7c634586006a3aa284024000123c94007d02546a3ea2700800000000000000006a4ea26704190500001a5294006002546a56a23b2020014860486000000000000000006464200148604860000000000000000000646a76a23204030440c01a7a94002b02541a7e94002402546c0082a21a04000000006c0086a204102a0079e10abc153900000000000000006bc472086be4b03a01b87206b03a01b87201";
+ assertEquals(referenceProgramHexString, programString);
+ }
+
+ @Test
+ public void testFullApfV4ProgramGenerationIPV4() throws IllegalInstructionException {
+ ApfV4Generator gen = new ApfV4Generator(APF_VERSION_4);
+ gen.addLoadImmediate(R1, -4);
+ gen.addLoadData(R0, 0);
+ gen.addAdd(1);
+ gen.addStoreData(R0, 0);
+ gen.addLoad16(R0, 12);
+ gen.addLoadImmediate(R1, -108);
+ gen.addJumpIfR0LessThan(0x600, "LABEL_283");
+ gen.addLoadImmediate(R1, -112);
+ gen.addJumpIfR0Equals(0x88a2, "LABEL_283");
+ gen.addJumpIfR0Equals(0x88a4, "LABEL_283");
+ gen.addJumpIfR0Equals(0x88b8, "LABEL_283");
+ gen.addJumpIfR0Equals(0x88cd, "LABEL_283");
+ gen.addJumpIfR0Equals(0x88e1, "LABEL_283");
+ gen.addJumpIfR0Equals(0x88e3, "LABEL_283");
+ gen.addJumpIfR0NotEquals(0x806, "LABEL_109");
+ gen.addLoadImmediate(R0, 14);
+ gen.addLoadImmediate(R1, -36);
+ gen.addJumpIfBytesAtR0NotEqual(hexStringToByteArray("000108000604"), "LABEL_277");
+ gen.addLoad16(R0, 20);
+ gen.addJumpIfR0Equals(0x1, "LABEL_94");
+ gen.addLoadImmediate(R1, -40);
+ gen.addJumpIfR0NotEquals(0x2, "LABEL_277");
+ gen.addLoad32(R0, 28);
+ gen.addLoadImmediate(R1, -116);
+ gen.addJumpIfR0Equals(0x0, "LABEL_283");
+ gen.addLoadImmediate(R0, 0);
+ gen.addLoadImmediate(R1, -44);
+ gen.addJumpIfBytesAtR0NotEqual(hexStringToByteArray("ffffffffffff"), "LABEL_277");
+
+ gen.defineLabel("LABEL_94");
+ gen.addLoadImmediate(R0, 38);
+ gen.addLoadImmediate(R1, -68);
+ gen.addJumpIfBytesAtR0NotEqual(hexStringToByteArray("c0a801b3"), "LABEL_283");
+ gen.addLoadImmediate(R1, -8);
+ gen.addJump("LABEL_277");
+
+ gen.defineLabel("LABEL_109");
+ gen.addLoad16(R0, 12);
+ gen.addJumpIfR0NotEquals(0x800, "LABEL_204");
+ gen.addLoad8(R0, 23);
+ gen.addJumpIfR0NotEquals(0x11, "LABEL_151");
+ gen.addLoad16(R0, 20);
+ gen.addJumpIfR0AnyBitsSet(0x1fff, "LABEL_151");
+ gen.addLoadFromMemory(R1, 13);
+ gen.addLoad16Indexed(R0, 16);
+ gen.addJumpIfR0NotEquals(0x44, "LABEL_151");
+ gen.addLoadImmediate(R0, 50);
+ gen.addAddR1();
+ gen.addJumpIfBytesAtR0NotEqual(hexStringToByteArray("f683d58f832b"), "LABEL_151");
+ gen.addLoadImmediate(R1, -12);
+ gen.addJump("LABEL_277");
+
+ gen.defineLabel("LABEL_151");
+ gen.addLoad8(R0, 30);
+ gen.addAnd(240);
+ gen.addLoadImmediate(R1, -84);
+ gen.addJumpIfR0Equals(0xe0, "LABEL_283");
+ gen.addLoadImmediate(R1, -76);
+ gen.addLoad32(R0, 30);
+ gen.addJumpIfR0Equals(0xffffffff, "LABEL_283");
+ gen.addLoadImmediate(R1, -80);
+ gen.addJumpIfR0Equals(0xc0a801ff, "LABEL_283");
+ gen.addLoadImmediate(R1, -24);
+ gen.addLoadImmediate(R0, 0);
+ gen.addJumpIfBytesAtR0NotEqual(hexStringToByteArray("ffffffffffff"), "LABEL_277");
+ gen.addLoadImmediate(R1, -72);
+ gen.addJump("LABEL_283");
+ gen.addLoadImmediate(R1, -16);
+ gen.addJump("LABEL_277");
+
+ gen.defineLabel("LABEL_204");
+ gen.addJumpIfR0Equals(0x86dd, "LABEL_225");
+ gen.addLoadImmediate(R0, 0);
+ gen.addLoadImmediate(R1, -48);
+ gen.addJumpIfBytesAtR0NotEqual(hexStringToByteArray("ffffffffffff"), "LABEL_277");
+ gen.addLoadImmediate(R1, -56);
+ gen.addJump("LABEL_283");
+
+ gen.defineLabel("LABEL_225");
+ gen.addLoad8(R0, 20);
+ gen.addJumpIfR0Equals(0x3a, "LABEL_241");
+ gen.addLoadImmediate(R1, -104);
+ gen.addLoad8(R0, 38);
+ gen.addJumpIfR0Equals(0xff, "LABEL_283");
+ gen.addLoadImmediate(R1, -32);
+ gen.addJump("LABEL_277");
+
+ gen.defineLabel("LABEL_241");
+ gen.addLoad8(R0, 54);
+ gen.addLoadImmediate(R1, -88);
+ gen.addJumpIfR0Equals(0x85, "LABEL_283");
+ gen.addJumpIfR0NotEquals(0x88, "LABEL_275");
+ gen.addLoadImmediate(R0, 38);
+ gen.addJumpIfBytesAtR0NotEqual(hexStringToByteArray("ff0200000000000000000000000000"), "LABEL_275");
+ gen.addLoadImmediate(R1, -92);
+ gen.addJump("LABEL_283");
+
+ gen.defineLabel("LABEL_275");
+ gen.addLoadImmediate(R1, -28);
+
+ gen.defineLabel("LABEL_277");
+ gen.addLoadData(R0, 0);
+ gen.addAdd(1);
+ gen.addStoreData(R0, 0);
+ gen.addJump(PASS_LABEL);
+
+ gen.defineLabel("LABEL_283");
+ gen.addLoadData(R0, 0);
+ gen.addAdd(1);
+ gen.addStoreData(R0, 0);
+ gen.addJump(DROP_LABEL);
+
+ byte[] program = gen.generate();
+ final String programString = toHexString(program).toLowerCase();
+ final String referenceProgramHexString = "6bfcb03a01b8120c6b9494010c06006b907c010588a27c010088a47c00fb88b87c00f688cd7c00f188e17c00ec88e384003908066a0e6bdca2d40600010800060412147a18016bd882ca021a1c6b8c7ac900686bd4a2b706ffffffffffff6a266bbca2b204c0a801b36bf872a8120c84005808000a17821e1112149c00171fffab0d2a108210446a3239a20406f683d58f832b6bf4727e0a1e52f06bac7a7be06bb41a1e7e0000006effffffff6bb07e00000063c0a801ff6be868a25106ffffffffffff6bb872536bf072497c001086dd686bd0a23806ffffffffffff6bc8723a0a147a0b3a6b980a267a2eff6be072240a366ba87a23858218886a26a2040fff02000000000000000000000000006ba472086be4b03a01b87206b03a01b87201";
+ assertEquals(referenceProgramHexString, programString);
+ }
+
+ @Test
+ public void testFullApfV4ProgramGenerationNatTKeepAliveV4() throws IllegalInstructionException {
+ ApfV4Generator gen = new ApfV4Generator(APF_VERSION_4);
+ gen.addLoadImmediate(R1, -4);
+ gen.addLoadData(R0, 0);
+ gen.addAdd(1);
+ gen.addStoreData(R0, 0);
+ gen.addLoad16(R0, 12);
+ gen.addLoadImmediate(R1, -108);
+ gen.addJumpIfR0LessThan(0x600, "LABEL_345");
+ gen.addLoadImmediate(R1, -112);
+ gen.addJumpIfR0Equals(0x88a2, "LABEL_345");
+ gen.addJumpIfR0Equals(0x88a4, "LABEL_345");
+ gen.addJumpIfR0Equals(0x88b8, "LABEL_345");
+ gen.addJumpIfR0Equals(0x88cd, "LABEL_345");
+ gen.addJumpIfR0Equals(0x88e1, "LABEL_345");
+ gen.addJumpIfR0Equals(0x88e3, "LABEL_345");
+ gen.addJumpIfR0NotEquals(0x806, "LABEL_115");
+ gen.addLoadImmediate(R0, 14);
+ gen.addLoadImmediate(R1, -36);
+ gen.addJumpIfBytesAtR0NotEqual(hexStringToByteArray("000108000604"), "LABEL_339");
+ gen.addLoad16(R0, 20);
+ gen.addJumpIfR0Equals(0x1, "LABEL_100");
+ gen.addLoadImmediate(R1, -40);
+ gen.addJumpIfR0NotEquals(0x2, "LABEL_339");
+ gen.addLoad32(R0, 28);
+ gen.addLoadImmediate(R1, -116);
+ gen.addJumpIfR0Equals(0x0, "LABEL_345");
+ gen.addLoadImmediate(R0, 0);
+ gen.addLoadImmediate(R1, -44);
+ gen.addJumpIfBytesAtR0NotEqual(hexStringToByteArray("ffffffffffff"), "LABEL_339");
+
+ gen.defineLabel("LABEL_100");
+ gen.addLoadImmediate(R0, 38);
+ gen.addLoadImmediate(R1, -68);
+ gen.addJumpIfBytesAtR0NotEqual(hexStringToByteArray("c0a801be"), "LABEL_345");
+ gen.addLoadImmediate(R1, -8);
+ gen.addJump("LABEL_339");
+
+ gen.defineLabel("LABEL_115");
+ gen.addLoad16(R0, 12);
+ gen.addJumpIfR0NotEquals(0x800, "LABEL_263");
+ gen.addLoad8(R0, 23);
+ gen.addJumpIfR0NotEquals(0x11, "LABEL_157");
+ gen.addLoad16(R0, 20);
+ gen.addJumpIfR0AnyBitsSet(0x1fff, "LABEL_157");
+ gen.addLoadFromMemory(R1, 13);
+ gen.addLoad16Indexed(R0, 16);
+ gen.addJumpIfR0NotEquals(0x44, "LABEL_157");
+ gen.addLoadImmediate(R0, 50);
+ gen.addAddR1();
+ gen.addJumpIfBytesAtR0NotEqual(hexStringToByteArray("ea42226789c0"), "LABEL_157");
+ gen.addLoadImmediate(R1, -12);
+ gen.addJump("LABEL_339");
+
+ gen.defineLabel("LABEL_157");
+ gen.addLoad8(R0, 30);
+ gen.addAnd(240);
+ gen.addLoadImmediate(R1, -84);
+ gen.addJumpIfR0Equals(0xe0, "LABEL_345");
+ gen.addLoadImmediate(R1, -76);
+ gen.addLoad32(R0, 30);
+ gen.addJumpIfR0Equals(0xffffffff, "LABEL_345");
+ gen.addLoadImmediate(R1, -80);
+ gen.addJumpIfR0Equals(0xc0a801ff, "LABEL_345");
+ gen.addLoad8(R0, 23);
+ gen.addJumpIfR0NotEquals(0x11, "LABEL_243");
+ gen.addLoadImmediate(R0, 26);
+ gen.addJumpIfBytesAtR0NotEqual(hexStringToByteArray("6b7a1f1fc0a801be"), "LABEL_243");
+ gen.addLoadFromMemory(R0, 13);
+ gen.addAdd(8);
+ gen.addSwap();
+ gen.addLoad16(R0, 16);
+ gen.addNeg(R1);
+ gen.addAddR1();
+ gen.addJumpIfR0NotEquals(0x1, "LABEL_243");
+ gen.addLoadFromMemory(R0, 13);
+ gen.addAdd(14);
+ gen.addJumpIfBytesAtR0NotEqual(hexStringToByteArray("1194ceca"), "LABEL_243");
+ gen.addAdd(8);
+ gen.addJumpIfBytesAtR0NotEqual(hexStringToByteArray("ff"), "LABEL_243");
+ gen.addLoadImmediate(R1, -128);
+ gen.addJump("LABEL_345");
+
+ gen.defineLabel("LABEL_243");
+ gen.addLoadImmediate(R1, -24);
+ gen.addLoadImmediate(R0, 0);
+ gen.addJumpIfBytesAtR0NotEqual(hexStringToByteArray("ffffffffffff"), "LABEL_339");
+ gen.addLoadImmediate(R1, -72);
+ gen.addJump("LABEL_345");
+ gen.addLoadImmediate(R1, -16);
+ gen.addJump("LABEL_339");
+
+ gen.defineLabel("LABEL_263");
+ gen.addJumpIfR0Equals(0x86dd, "LABEL_284");
+ gen.addLoadImmediate(R0, 0);
+ gen.addLoadImmediate(R1, -48);
+ gen.addJumpIfBytesAtR0NotEqual(hexStringToByteArray("ffffffffffff"), "LABEL_339");
+ gen.addLoadImmediate(R1, -56);
+ gen.addJump("LABEL_345");
+
+ gen.defineLabel("LABEL_284");
+ gen.addLoad8(R0, 20);
+ gen.addJumpIfR0Equals(0x0, "LABEL_339");
+ gen.addJumpIfR0Equals(0x3a, "LABEL_303");
+ gen.addLoadImmediate(R1, -104);
+ gen.addLoad8(R0, 38);
+ gen.addJumpIfR0Equals(0xff, "LABEL_345");
+ gen.addLoadImmediate(R1, -32);
+ gen.addJump("LABEL_339");
+
+ gen.defineLabel("LABEL_303");
+ gen.addLoad8(R0, 54);
+ gen.addLoadImmediate(R1, -88);
+ gen.addJumpIfR0Equals(0x85, "LABEL_345");
+ gen.addJumpIfR0NotEquals(0x88, "LABEL_337");
+ gen.addLoadImmediate(R0, 38);
+ gen.addJumpIfBytesAtR0NotEqual(hexStringToByteArray("ff0200000000000000000000000000"), "LABEL_337");
+ gen.addLoadImmediate(R1, -92);
+ gen.addJump("LABEL_345");
+
+ gen.defineLabel("LABEL_337");
+ gen.addLoadImmediate(R1, -28);
+
+ gen.defineLabel("LABEL_339");
+ gen.addLoadData(R0, 0);
+ gen.addAdd(1);
+ gen.addStoreData(R0, 0);
+ gen.addJump(PASS_LABEL);
+
+ gen.defineLabel("LABEL_345");
+ gen.addLoadData(R0, 0);
+ gen.addAdd(1);
+ gen.addStoreData(R0, 0);
+ gen.addJump(DROP_LABEL);
+
+ byte[] program = gen.generate();
+ final String programString = toHexString(program).toLowerCase();
+ final String referenceProgramHexString = "6bfcb03a01b8120c6b9494014a06006b907c014388a27c013e88a47c013988b87c013488cd7c012f88e17c012a88e384003f08066a0e6bdca40110000600010800060412147a1c016bd884010400021a1c6b8c7c01010000686bd4a2ef06ffffffffffff6a266bbca2ea04c0a801be6bf872e0120c84008d08000a17821e1112149c00171fffab0d2a108210446a3239a20406ea42226789c06bf472b60a1e52f06bac7ab3e06bb41a1e7e000000a6ffffffff6bb07e0000009bc0a801ff0a178230116a1aa223086b7a1f1fc0a801beaa0d3a08aa221210ab2139821501aa0d3a0ea20a041194ceca3a08a20401ff6b8072666be868a25406ffffffffffff6bb872566bf0724c7c001086dd686bd0a23b06ffffffffffff6bc8723d0a147a32007a0b3a6b980a267a2eff6be072240a366ba87a23858218886a26a2040fff02000000000000000000000000006ba472086be4b03a01b87206b03a01b87201";
+ assertEquals(referenceProgramHexString, programString);
+ }
}
diff --git a/tests/unit/src/android/net/apf/ApfTestUtils.java b/tests/unit/src/android/net/apf/ApfTestUtils.java
index abbdd6b2..56d7cae9 100644
--- a/tests/unit/src/android/net/apf/ApfTestUtils.java
+++ b/tests/unit/src/android/net/apf/ApfTestUtils.java
@@ -28,7 +28,7 @@ import static org.mockito.Mockito.mock;
import android.content.Context;
import android.net.LinkAddress;
import android.net.LinkProperties;
-import android.net.apf.ApfGenerator.IllegalInstructionException;
+import android.net.apf.ApfV4Generator.IllegalInstructionException;
import android.net.ip.IIpClientCallbacks;
import android.net.ip.IpClient;
import android.net.metrics.IpConnectivityLog;
@@ -155,7 +155,7 @@ public class ApfTestUtils {
*/
public static void assertDataMemoryContents(int apfVersion, int expected, byte[] program,
byte[] packet, byte[] data, byte[] expectedData)
- throws ApfGenerator.IllegalInstructionException, Exception {
+ throws ApfV4Generator.IllegalInstructionException, Exception {
assertReturnCodesEqual(expected,
apfSimulate(apfVersion, program, packet, data, 0 /* filterAge */));
@@ -176,8 +176,8 @@ public class ApfTestUtils {
apfSimulate(apfVersion, program, packet, data, 0 /* filterAge */));
}
- private static void assertVerdict(int apfVersion, int expected, ApfGenerator gen, byte[] packet,
- int filterAge) throws ApfGenerator.IllegalInstructionException {
+ private static void assertVerdict(int apfVersion, int expected, ApfV4Generator gen,
+ byte[] packet, int filterAge) throws ApfV4Generator.IllegalInstructionException {
assertReturnCodesEqual(expected,
apfSimulate(apfVersion, gen.generate(), packet, null, filterAge));
}
@@ -185,32 +185,32 @@ public class ApfTestUtils {
/**
* Runs the APF program and checks the return code is PASS.
*/
- public static void assertPass(int apfVersion, ApfGenerator gen, byte[] packet, int filterAge)
- throws ApfGenerator.IllegalInstructionException {
+ public static void assertPass(int apfVersion, ApfV4Generator gen, byte[] packet, int filterAge)
+ throws ApfV4Generator.IllegalInstructionException {
assertVerdict(apfVersion, PASS, gen, packet, filterAge);
}
/**
* Runs the APF program and checks the return code is DROP.
*/
- public static void assertDrop(int apfVersion, ApfGenerator gen, byte[] packet, int filterAge)
- throws ApfGenerator.IllegalInstructionException {
+ public static void assertDrop(int apfVersion, ApfV4Generator gen, byte[] packet, int filterAge)
+ throws ApfV4Generator.IllegalInstructionException {
assertVerdict(apfVersion, DROP, gen, packet, filterAge);
}
/**
* Runs the APF program and checks the return code is PASS.
*/
- public static void assertPass(int apfVersion, ApfGenerator gen)
- throws ApfGenerator.IllegalInstructionException {
+ public static void assertPass(int apfVersion, ApfV4Generator gen)
+ throws ApfV4Generator.IllegalInstructionException {
assertVerdict(apfVersion, PASS, gen, new byte[MIN_PKT_SIZE], 0);
}
/**
* Runs the APF program and checks the return code is DROP.
*/
- public static void assertDrop(int apfVersion, ApfGenerator gen)
- throws ApfGenerator.IllegalInstructionException {
+ public static void assertDrop(int apfVersion, ApfV4Generator gen)
+ throws ApfV4Generator.IllegalInstructionException {
assertVerdict(apfVersion, DROP, gen, new byte[MIN_PKT_SIZE], 0);
}
@@ -383,7 +383,7 @@ public class ApfTestUtils {
@Override
@GuardedBy("this")
- protected ApfGenerator emitPrologueLocked() throws IllegalInstructionException {
+ protected ApfV4Generator emitPrologueLocked() throws IllegalInstructionException {
if (mThrowsExceptionWhenGeneratesProgram) {
throw new IllegalStateException();
}
@@ -479,7 +479,7 @@ public class ApfTestUtils {
@Override
@GuardedBy("this")
- protected ApfGenerator emitPrologueLocked() throws IllegalInstructionException {
+ protected ApfV4Generator emitPrologueLocked() throws IllegalInstructionException {
if (mThrowsExceptionWhenGeneratesProgram) {
throw new IllegalStateException();
}
diff --git a/tests/unit/src/android/net/apf/ApfV5Test.kt b/tests/unit/src/android/net/apf/ApfV5Test.kt
index 1977a6c2..30d19b2c 100644
--- a/tests/unit/src/android/net/apf/ApfV5Test.kt
+++ b/tests/unit/src/android/net/apf/ApfV5Test.kt
@@ -15,9 +15,11 @@
*/
package android.net.apf
-import android.net.apf.ApfGenerator.IllegalInstructionException
-import android.net.apf.ApfGenerator.Register.R0
-import android.net.apf.ApfGenerator.Register.R1
+import android.net.apf.ApfV4Generator.IllegalInstructionException
+import android.net.apf.ApfV4Generator.MIN_APF_VERSION
+import android.net.apf.ApfV4Generator.MIN_APF_VERSION_IN_DEV
+import android.net.apf.ApfV4Generator.Register.R0
+import android.net.apf.ApfV4Generator.Register.R1
import androidx.test.filters.SmallTest
import androidx.test.runner.AndroidJUnit4
import java.lang.IllegalArgumentException
@@ -35,7 +37,7 @@ class ApfV5Test {
@Test
fun testApfInstructionVersionCheck() {
- var gen = ApfGenerator(ApfGenerator.MIN_APF_VERSION)
+ var gen = ApfV4Generator(MIN_APF_VERSION)
assertFailsWith<IllegalInstructionException> { gen.addDrop() }
assertFailsWith<IllegalInstructionException> { gen.addCountAndDrop(12) }
assertFailsWith<IllegalInstructionException> { gen.addCountAndPass(1000) }
@@ -59,18 +61,28 @@ class ApfV5Test {
assertFailsWith<IllegalInstructionException> { gen.addDataCopyFromR0LenR1() }
assertFailsWith<IllegalInstructionException> { gen.addPacketCopyFromR0(10) }
assertFailsWith<IllegalInstructionException> { gen.addDataCopyFromR0(10) }
+ assertFailsWith<IllegalInstructionException> {
+ gen.addJumpIfBytesAtR0Equal(byteArrayOf('A'.code.toByte()), ApfV4Generator.DROP_LABEL) }
+ assertFailsWith<IllegalInstructionException> { gen.addJumpIfPktAtR0DoesNotContainDnsQ(
+ byteArrayOf(1, 'A'.code.toByte()), 0x0c, ApfV4Generator.DROP_LABEL) }
+ assertFailsWith<IllegalInstructionException> { gen.addJumpIfPktAtR0ContainDnsQ(
+ byteArrayOf(1, 'A'.code.toByte()), 0x0c, ApfV4Generator.DROP_LABEL) }
+ assertFailsWith<IllegalInstructionException> { gen.addJumpIfPktAtR0DoesNotContainDnsA(
+ byteArrayOf(1, 'A'.code.toByte()), ApfV4Generator.DROP_LABEL) }
+ assertFailsWith<IllegalInstructionException> { gen.addJumpIfPktAtR0ContainDnsA(
+ byteArrayOf(1, 'A'.code.toByte()), ApfV4Generator.DROP_LABEL) }
}
@Test
fun testDataInstructionMustComeFirst() {
- var gen = ApfGenerator(ApfGenerator.MIN_APF_VERSION_IN_DEV)
+ var gen = ApfV4Generator(MIN_APF_VERSION_IN_DEV)
gen.addAllocateR0()
assertFailsWith<IllegalInstructionException> { gen.addData(ByteArray(3) { 0x01 }) }
}
@Test
fun testApfInstructionEncodingSizeCheck() {
- var gen = ApfGenerator(ApfGenerator.MIN_APF_VERSION_IN_DEV)
+ var gen = ApfV4Generator(MIN_APF_VERSION_IN_DEV)
assertFailsWith<IllegalArgumentException> { gen.addAllocate(65536) }
assertFailsWith<IllegalArgumentException> { gen.addAllocate(-1) }
assertFailsWith<IllegalArgumentException> { gen.addDataCopy(-1, 1) }
@@ -83,41 +95,125 @@ class ApfV5Test {
assertFailsWith<IllegalArgumentException> { gen.addDataCopyFromR0(256) }
assertFailsWith<IllegalArgumentException> { gen.addPacketCopyFromR0(-1) }
assertFailsWith<IllegalArgumentException> { gen.addDataCopyFromR0(-1) }
+ assertFailsWith<IllegalArgumentException> { gen.addJumpIfPktAtR0DoesNotContainDnsQ(
+ byteArrayOf(1, 'A'.code.toByte(), 0, 0), 256, ApfV4Generator.DROP_LABEL) }
+ assertFailsWith<IllegalArgumentException> { gen.addJumpIfPktAtR0DoesNotContainDnsQ(
+ byteArrayOf(1, 'a'.code.toByte(), 0, 0), 0x0c, ApfV4Generator.DROP_LABEL) }
+ assertFailsWith<IllegalArgumentException> { gen.addJumpIfPktAtR0DoesNotContainDnsQ(
+ byteArrayOf(1, '.'.code.toByte(), 0, 0), 0x0c, ApfV4Generator.DROP_LABEL) }
+ assertFailsWith<IllegalArgumentException> { gen.addJumpIfPktAtR0DoesNotContainDnsQ(
+ byteArrayOf(0, 0), 0xc0, ApfV4Generator.DROP_LABEL) }
+ assertFailsWith<IllegalArgumentException> { gen.addJumpIfPktAtR0DoesNotContainDnsQ(
+ byteArrayOf(1, 'A'.code.toByte()), 0xc0, ApfV4Generator.DROP_LABEL) }
+ assertFailsWith<IllegalArgumentException> { gen.addJumpIfPktAtR0DoesNotContainDnsQ(
+ byteArrayOf(64) + ByteArray(64) { 'A'.code.toByte() } + byteArrayOf(0, 0),
+ 0xc0, ApfV4Generator.DROP_LABEL) }
+ assertFailsWith<IllegalArgumentException> { gen.addJumpIfPktAtR0DoesNotContainDnsQ(
+ byteArrayOf(1, 'A'.code.toByte(), 1, 'B'.code.toByte(), 0),
+ 0xc0, ApfV4Generator.DROP_LABEL) }
+ assertFailsWith<IllegalArgumentException> { gen.addJumpIfPktAtR0DoesNotContainDnsQ(
+ byteArrayOf(1, 'A'.code.toByte(), 1, 'B'.code.toByte()),
+ 0xc0, ApfV4Generator.DROP_LABEL) }
+ assertFailsWith<IllegalArgumentException> { gen.addJumpIfPktAtR0ContainDnsQ(
+ byteArrayOf(1, 'A'.code.toByte(), 0, 0), 256, ApfV4Generator.DROP_LABEL) }
+ assertFailsWith<IllegalArgumentException> { gen.addJumpIfPktAtR0ContainDnsQ(
+ byteArrayOf(1, 'a'.code.toByte(), 0, 0), 0x0c, ApfV4Generator.DROP_LABEL) }
+ assertFailsWith<IllegalArgumentException> { gen.addJumpIfPktAtR0ContainDnsQ(
+ byteArrayOf(1, '.'.code.toByte(), 0, 0), 0x0c, ApfV4Generator.DROP_LABEL) }
+ assertFailsWith<IllegalArgumentException> { gen.addJumpIfPktAtR0ContainDnsQ(
+ byteArrayOf(0, 0), 0xc0, ApfV4Generator.DROP_LABEL) }
+ assertFailsWith<IllegalArgumentException> { gen.addJumpIfPktAtR0ContainDnsQ(
+ byteArrayOf(1, 'A'.code.toByte()), 0xc0, ApfV4Generator.DROP_LABEL) }
+ assertFailsWith<IllegalArgumentException> { gen.addJumpIfPktAtR0ContainDnsQ(
+ byteArrayOf(64) + ByteArray(64) { 'A'.code.toByte() } + byteArrayOf(0, 0),
+ 0xc0, ApfV4Generator.DROP_LABEL) }
+ assertFailsWith<IllegalArgumentException> { gen.addJumpIfPktAtR0ContainDnsQ(
+ byteArrayOf(1, 'A'.code.toByte(), 1, 'B'.code.toByte(), 0),
+ 0xc0, ApfV4Generator.DROP_LABEL) }
+ assertFailsWith<IllegalArgumentException> { gen.addJumpIfPktAtR0ContainDnsQ(
+ byteArrayOf(1, 'A'.code.toByte(), 1, 'B'.code.toByte()),
+ 0xc0, ApfV4Generator.DROP_LABEL) }
+ assertFailsWith<IllegalArgumentException> { gen.addJumpIfPktAtR0DoesNotContainDnsA(
+ byteArrayOf(1, 'a'.code.toByte(), 0, 0), ApfV4Generator.DROP_LABEL) }
+ assertFailsWith<IllegalArgumentException> { gen.addJumpIfPktAtR0DoesNotContainDnsA(
+ byteArrayOf(1, '.'.code.toByte(), 0, 0), ApfV4Generator.DROP_LABEL) }
+ assertFailsWith<IllegalArgumentException> { gen.addJumpIfPktAtR0DoesNotContainDnsA(
+ byteArrayOf(0, 0), ApfV4Generator.DROP_LABEL) }
+ assertFailsWith<IllegalArgumentException> { gen.addJumpIfPktAtR0DoesNotContainDnsA(
+ byteArrayOf(1, 'A'.code.toByte()), ApfV4Generator.DROP_LABEL) }
+ assertFailsWith<IllegalArgumentException> { gen.addJumpIfPktAtR0DoesNotContainDnsA(
+ byteArrayOf(64) + ByteArray(64) { 'A'.code.toByte() } + byteArrayOf(0, 0),
+ ApfV4Generator.DROP_LABEL) }
+ assertFailsWith<IllegalArgumentException> { gen.addJumpIfPktAtR0DoesNotContainDnsA(
+ byteArrayOf(1, 'A'.code.toByte(), 1, 'B'.code.toByte(), 0),
+ ApfV4Generator.DROP_LABEL) }
+ assertFailsWith<IllegalArgumentException> { gen.addJumpIfPktAtR0DoesNotContainDnsA(
+ byteArrayOf(1, 'A'.code.toByte(), 1, 'B'.code.toByte()),
+ ApfV4Generator.DROP_LABEL) }
+ assertFailsWith<IllegalArgumentException> { gen.addJumpIfPktAtR0ContainDnsA(
+ byteArrayOf(1, 'a'.code.toByte(), 0, 0), ApfV4Generator.DROP_LABEL) }
+ assertFailsWith<IllegalArgumentException> { gen.addJumpIfPktAtR0ContainDnsA(
+ byteArrayOf(1, '.'.code.toByte(), 0, 0), ApfV4Generator.DROP_LABEL) }
+ assertFailsWith<IllegalArgumentException> { gen.addJumpIfPktAtR0ContainDnsA(
+ byteArrayOf(0, 0), ApfV4Generator.DROP_LABEL) }
+ assertFailsWith<IllegalArgumentException> { gen.addJumpIfPktAtR0ContainDnsA(
+ byteArrayOf(1, 'A'.code.toByte()), ApfV4Generator.DROP_LABEL) }
+ assertFailsWith<IllegalArgumentException> { gen.addJumpIfPktAtR0ContainDnsA(
+ byteArrayOf(64) + ByteArray(64) { 'A'.code.toByte() } + byteArrayOf(0, 0),
+ ApfV4Generator.DROP_LABEL) }
+ assertFailsWith<IllegalArgumentException> { gen.addJumpIfPktAtR0ContainDnsA(
+ byteArrayOf(1, 'A'.code.toByte(), 1, 'B'.code.toByte(), 0),
+ ApfV4Generator.DROP_LABEL) }
+ assertFailsWith<IllegalArgumentException> { gen.addJumpIfPktAtR0ContainDnsA(
+ byteArrayOf(1, 'A'.code.toByte(), 1, 'B'.code.toByte()),
+ ApfV4Generator.DROP_LABEL) }
}
@Test
fun testApfInstructionsEncoding() {
- var gen = ApfGenerator(ApfGenerator.MIN_APF_VERSION)
+ var gen = ApfV4Generator(MIN_APF_VERSION)
gen.addPass()
var program = gen.generate()
// encoding PASS opcode: opcode=0, imm_len=0, R=0
assertContentEquals(
byteArrayOf(encodeInstruction(opcode = 0, immLength = 0, register = 0)), program)
+ assertContentEquals(
+ listOf("0: pass"),
+ ApfJniUtils.disassembleApf(program).map { it.trim() } )
- gen = ApfGenerator(ApfGenerator.MIN_APF_VERSION_IN_DEV)
+ gen = ApfV4Generator(MIN_APF_VERSION_IN_DEV)
gen.addDrop()
program = gen.generate()
// encoding DROP opcode: opcode=0, imm_len=0, R=1
assertContentEquals(
byteArrayOf(encodeInstruction(opcode = 0, immLength = 0, register = 1)), program)
+ assertContentEquals(
+ listOf("0: drop"),
+ ApfJniUtils.disassembleApf(program).map { it.trim() } )
- gen = ApfGenerator(ApfGenerator.MIN_APF_VERSION_IN_DEV)
+ gen = ApfV4Generator(MIN_APF_VERSION_IN_DEV)
gen.addCountAndPass(129)
program = gen.generate()
// encoding COUNT(PASS) opcode: opcode=0, imm_len=size_of(imm), R=0, imm=counterNumber
assertContentEquals(
byteArrayOf(encodeInstruction(opcode = 0, immLength = 1, register = 0),
0x81.toByte()), program)
+ assertContentEquals(
+ listOf("0: pass 129"),
+ ApfJniUtils.disassembleApf(program).map { it.trim() } )
- gen = ApfGenerator(ApfGenerator.MIN_APF_VERSION_IN_DEV)
+ gen = ApfV4Generator(MIN_APF_VERSION_IN_DEV)
gen.addCountAndDrop(1000)
program = gen.generate()
// encoding COUNT(DROP) opcode: opcode=0, imm_len=size_of(imm), R=1, imm=counterNumber
assertContentEquals(
byteArrayOf(encodeInstruction(opcode = 0, immLength = 2, register = 1),
0x03, 0xe8.toByte()), program)
+ assertContentEquals(
+ listOf("0: drop 1000"),
+ ApfJniUtils.disassembleApf(program).map { it.trim() } )
- gen = ApfGenerator(ApfGenerator.MIN_APF_VERSION_IN_DEV)
+ gen = ApfV4Generator(MIN_APF_VERSION_IN_DEV)
gen.addAllocateR0()
gen.addAllocate(1500)
program = gen.generate()
@@ -128,10 +224,10 @@ class ApfV5Test {
encodeInstruction(opcode = 21, immLength = 1, register = 1), 36, 0x05,
0xDC.toByte()),
program)
- // TODO: add back disassembling test check after we update the apf_disassembler
- // assertContentEquals(arrayOf(" 0: alloc"), ApfJniUtils.disassembleApf(program))
+ assertContentEquals(listOf("0: allocate r0", "2: allocate 1500"),
+ ApfJniUtils.disassembleApf(program).map { it.trim() })
- gen = ApfGenerator(ApfGenerator.MIN_APF_VERSION_IN_DEV)
+ gen = ApfV4Generator(MIN_APF_VERSION_IN_DEV)
gen.addTransmit()
gen.addDiscard()
program = gen.generate()
@@ -142,10 +238,10 @@ class ApfV5Test {
encodeInstruction(opcode = 21, immLength = 1, register = 0), 37,
encodeInstruction(opcode = 21, immLength = 1, register = 1), 37,
), program)
- // TODO: add back disassembling test check after we update the apf_disassembler
- // assertContentEquals(arrayOf(" 0: trans"), ApfJniUtils.disassembleApf(program))
+ assertContentEquals(listOf("0: discard", "2: transmit"),
+ ApfJniUtils.disassembleApf(program).map { it.trim() })
- gen = ApfGenerator(ApfGenerator.MIN_APF_VERSION_IN_DEV)
+ gen = ApfV4Generator(MIN_APF_VERSION_IN_DEV)
val largeByteArray = ByteArray(256) { 0x01 }
gen.addData(largeByteArray)
program = gen.generate()
@@ -153,8 +249,10 @@ class ApfV5Test {
assertContentEquals(byteArrayOf(
encodeInstruction(opcode = 14, immLength = 2, register = 1), 0x01, 0x00) +
largeByteArray, program)
+ assertContentEquals(listOf("0: data 256," + "01".repeat(256) ),
+ ApfJniUtils.disassembleApf(program).map { it.trim() })
- gen = ApfGenerator(ApfGenerator.MIN_APF_VERSION_IN_DEV)
+ gen = ApfV4Generator(MIN_APF_VERSION_IN_DEV)
gen.addWriteU8(0x01)
gen.addWriteU16(0x0102)
gen.addWriteU32(0x01020304)
@@ -176,19 +274,20 @@ class ApfV5Test {
encodeInstruction(24, 4, 0), 0x00, 0x00, 0x00, 0x00,
encodeInstruction(24, 4, 0), 0x80.toByte(), 0x00, 0x00,
0x00), program)
- assertContentEquals(arrayOf(
- " 0: write 0x01",
- " 2: write 0x0102",
- " 5: write 0x01020304",
- " 10: write 0x00",
- " 12: write 0x80",
- " 14: write 0x0000",
- " 17: write 0x8000",
- " 20: write 0x00000000",
- " 25: write 0x80000000"),
- ApfJniUtils.disassembleApf(program))
-
- gen = ApfGenerator(ApfGenerator.MIN_APF_VERSION_IN_DEV)
+ assertContentEquals(listOf(
+ "0: write 0x01",
+ "2: write 0x0102",
+ "5: write 0x01020304",
+ "10: write 0x00",
+ "12: write 0x80",
+ "14: write 0x0000",
+ "17: write 0x8000",
+ "20: write 0x00000000",
+ "25: write 0x80000000"
+ ),
+ ApfJniUtils.disassembleApf(program).map { it.trim() })
+
+ gen = ApfV4Generator(MIN_APF_VERSION_IN_DEV)
gen.addWriteU8(R0)
gen.addWriteU16(R0)
gen.addWriteU32(R0)
@@ -204,16 +303,15 @@ class ApfV5Test {
encodeInstruction(21, 1, 1), 39,
encodeInstruction(21, 1, 1), 40
), program)
- // TODO: add back disassembling test check after we update the apf_disassembler
-// assertContentEquals(arrayOf(
-// " 0: ewrite1 r0",
-// " 2: ewrite2 r0",
-// " 4: ewrite4 r0",
-// " 6: ewrite1 r1",
-// " 8: ewrite2 r1",
-// " 10: ewrite4 r1"), ApfJniUtils.disassembleApf(program))
-
- gen = ApfGenerator(ApfGenerator.MIN_APF_VERSION_IN_DEV)
+ assertContentEquals(listOf(
+ "0: ewrite1 r0",
+ "2: ewrite2 r0",
+ "4: ewrite4 r0",
+ "6: ewrite1 r1",
+ "8: ewrite2 r1",
+ "10: ewrite4 r1"), ApfJniUtils.disassembleApf(program).map { it.trim() })
+
+ gen = ApfV4Generator(MIN_APF_VERSION_IN_DEV)
gen.addDataCopy(0, 10)
gen.addDataCopy(1, 5)
gen.addPacketCopy(1000, 255)
@@ -224,12 +322,12 @@ class ApfV5Test {
encodeInstruction(25, 2, 0),
0x03.toByte(), 0xe8.toByte(), 0xff.toByte(),
), program)
- // TODO: add back disassembling test check after we update the apf_disassembler
-// assertContentEquals(arrayOf(
-// " 0: dcopy 0, 5",
-// " 3: pcopy 1000, 255"), ApfJniUtils.disassembleApf(program))
+ assertContentEquals(listOf(
+ "0: dcopy 0, 10",
+ "2: dcopy 1, 5",
+ "5: pcopy 1000, 255"), ApfJniUtils.disassembleApf(program).map { it.trim() })
- gen = ApfGenerator(ApfGenerator.MIN_APF_VERSION_IN_DEV)
+ gen = ApfV4Generator(MIN_APF_VERSION_IN_DEV)
gen.addPacketCopyFromR0LenR1()
gen.addPacketCopyFromR0(5)
gen.addDataCopyFromR0LenR1()
@@ -245,6 +343,34 @@ class ApfV5Test {
// assertContentEquals(arrayOf(
// " 0: dcopy [r1+0], 5",
// " 4: pcopy [r0+1000], 255"), ApfJniUtils.disassembleApf(program))
+
+ gen = ApfV4Generator(MIN_APF_VERSION_IN_DEV)
+ gen.addJumpIfBytesAtR0Equal(byteArrayOf('a'.code.toByte()), ApfV4Generator.DROP_LABEL)
+ program = gen.generate()
+ assertContentEquals(
+ byteArrayOf(encodeInstruction(opcode = 20, immLength = 1, register = 1),
+ 1, 1, 'a'.code.toByte()), program)
+
+ val qnames = byteArrayOf(1, 'A'.code.toByte(), 1, 'B'.code.toByte(), 0, 0)
+ gen = ApfV4Generator(MIN_APF_VERSION_IN_DEV)
+ gen.addJumpIfPktAtR0DoesNotContainDnsQ(qnames, 0x0c, ApfV4Generator.DROP_LABEL)
+ gen.addJumpIfPktAtR0ContainDnsQ(qnames, 0x0c, ApfV4Generator.DROP_LABEL)
+ program = gen.generate()
+ assertContentEquals(byteArrayOf(
+ encodeInstruction(21, 1, 0), 43, 11, 0x0c.toByte(),
+ ) + qnames + byteArrayOf(
+ encodeInstruction(21, 1, 1), 43, 1, 0x0c.toByte(),
+ ) + qnames, program)
+
+ gen = ApfV4Generator(MIN_APF_VERSION_IN_DEV)
+ gen.addJumpIfPktAtR0DoesNotContainDnsA(qnames, ApfV4Generator.DROP_LABEL)
+ gen.addJumpIfPktAtR0ContainDnsA(qnames, ApfV4Generator.DROP_LABEL)
+ program = gen.generate()
+ assertContentEquals(byteArrayOf(
+ encodeInstruction(21, 1, 0), 44, 10,
+ ) + qnames + byteArrayOf(
+ encodeInstruction(21, 1, 1), 44, 1,
+ ) + qnames, program)
}
private fun encodeInstruction(opcode: Int, immLength: Int, register: Int): Byte {
diff --git a/tests/unit/src/android/net/apf/Bpf2Apf.java b/tests/unit/src/android/net/apf/Bpf2Apf.java
index 5d57cde2..795c2b35 100644
--- a/tests/unit/src/android/net/apf/Bpf2Apf.java
+++ b/tests/unit/src/android/net/apf/Bpf2Apf.java
@@ -16,9 +16,8 @@
package android.net.apf;
-import android.net.apf.ApfGenerator;
-import android.net.apf.ApfGenerator.IllegalInstructionException;
-import android.net.apf.ApfGenerator.Register;
+import android.net.apf.ApfV4Generator.IllegalInstructionException;
+import android.net.apf.ApfV4Generator.Register;
import java.io.BufferedReader;
import java.io.InputStreamReader;
@@ -52,7 +51,7 @@ public class Bpf2Apf {
* APF instruction(s) and append them to {@code gen}. Here's an example line:
* (001) jeq #0x86dd jt 2 jf 7
*/
- private static void convertLine(String line, ApfGenerator gen)
+ private static void convertLine(String line, ApfV4Generator gen)
throws IllegalInstructionException {
if (line.indexOf("(") != 0 || line.indexOf(")") != 4 || line.indexOf(" ") != 5) {
throw new IllegalArgumentException("Unhandled instruction: " + line);
@@ -307,7 +306,7 @@ public class Bpf2Apf {
* program and return it.
*/
public static byte[] convert(String bpf) throws IllegalInstructionException {
- ApfGenerator gen = new ApfGenerator(3);
+ ApfV4Generator gen = new ApfV4Generator(3);
for (String line : bpf.split("\\n")) convertLine(line, gen);
return gen.generate();
}
@@ -320,7 +319,7 @@ public class Bpf2Apf {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String line = null;
StringBuilder responseData = new StringBuilder();
- ApfGenerator gen = new ApfGenerator(3);
+ ApfV4Generator gen = new ApfV4Generator(3);
while ((line = in.readLine()) != null) convertLine(line, gen);
System.out.write(gen.generate());
}
diff --git a/tests/unit/src/android/net/apf/JumpTableTest.kt b/tests/unit/src/android/net/apf/JumpTableTest.kt
index 6fdf38f7..3858aac0 100644
--- a/tests/unit/src/android/net/apf/JumpTableTest.kt
+++ b/tests/unit/src/android/net/apf/JumpTableTest.kt
@@ -34,7 +34,7 @@ import org.mockito.MockitoAnnotations
class JumpTableTest {
@Mock
- lateinit var gen: ApfGenerator
+ lateinit var gen: ApfV4Generator
@Before
fun setUp() {
@@ -94,11 +94,11 @@ class JumpTableTest {
j.generate(gen)
inOrder.verify(gen).defineLabel(name)
- inOrder.verify(gen).addLoadFromMemory(ApfGenerator.Register.R0, slot)
+ inOrder.verify(gen).addLoadFromMemory(ApfV4Generator.Register.R0, slot)
inOrder.verify(gen).addJumpIfR0Equals(0, "foo")
inOrder.verify(gen).addJumpIfR0Equals(1, "bar")
inOrder.verify(gen).addJumpIfR0Equals(2, "baz")
- inOrder.verify(gen).addJump(ApfGenerator.PASS_LABEL)
+ inOrder.verify(gen).addJump(ApfV4Generator.PASS_LABEL)
inOrder.verifyNoMoreInteractions()
}
}
diff --git a/tests/unit/src/android/net/dhcp6/Dhcp6PacketTest.kt b/tests/unit/src/android/net/dhcp6/Dhcp6PacketTest.kt
index 32cf4647..8e100e4c 100644
--- a/tests/unit/src/android/net/dhcp6/Dhcp6PacketTest.kt
+++ b/tests/unit/src/android/net/dhcp6/Dhcp6PacketTest.kt
@@ -29,21 +29,6 @@ import org.junit.runner.RunWith
@SmallTest
class Dhcp6PacketTest {
@Test
- fun testDecodeDhcp6PacketWithoutIaPdOption() {
- val solicitHex =
- // Solicit, Transaction ID
- "01000F51" +
- // client identifier option(option_len=12)
- "0001000C0003001B024CCBFFFE5F6EA9" +
- // elapsed time option(option_len=2)
- "000800020000"
- val bytes = HexDump.hexStringToByteArray(solicitHex)
- assertThrows(Dhcp6Packet.ParseException::class.java) {
- Dhcp6Packet.decode(bytes, bytes.size)
- }
- }
-
- @Test
fun testDecodeDhcp6SolicitPacket() {
val solicitHex =
// Solicit, Transaction ID
diff --git a/tests/unit/src/android/net/ip/IpReachabilityMonitorTest.kt b/tests/unit/src/android/net/ip/IpReachabilityMonitorTest.kt
index 4d57df5e..f93a3bdf 100644
--- a/tests/unit/src/android/net/ip/IpReachabilityMonitorTest.kt
+++ b/tests/unit/src/android/net/ip/IpReachabilityMonitorTest.kt
@@ -34,10 +34,8 @@ import android.stats.connectivity.IpType.IPV6
import android.stats.connectivity.NudEventType
import android.stats.connectivity.NudEventType.NUD_CONFIRM_FAILED
import android.stats.connectivity.NudEventType.NUD_CONFIRM_FAILED_CRITICAL
-import android.stats.connectivity.NudEventType.NUD_CONFIRM_MAC_ADDRESS_CHANGED
import android.stats.connectivity.NudEventType.NUD_ORGANIC_FAILED
import android.stats.connectivity.NudEventType.NUD_ORGANIC_FAILED_CRITICAL
-import android.stats.connectivity.NudEventType.NUD_ORGANIC_MAC_ADDRESS_CHANGED
import android.stats.connectivity.NudEventType.NUD_POST_ROAMING_FAILED
import android.stats.connectivity.NudEventType.NUD_POST_ROAMING_FAILED_CRITICAL
import android.stats.connectivity.NudEventType.NUD_POST_ROAMING_MAC_ADDRESS_CHANGED
@@ -53,9 +51,13 @@ import com.android.net.module.util.InterfaceParams
import com.android.net.module.util.SharedLog
import com.android.net.module.util.ip.IpNeighborMonitor
import com.android.net.module.util.netlink.StructNdMsg.NUD_FAILED
+import com.android.net.module.util.netlink.StructNdMsg.NUD_PROBE
import com.android.net.module.util.netlink.StructNdMsg.NUD_REACHABLE
import com.android.net.module.util.netlink.StructNdMsg.NUD_STALE
import com.android.networkstack.metrics.IpReachabilityMonitorMetrics
+import com.android.networkstack.util.NetworkStackUtils.IP_REACHABILITY_IGNORE_ORGANIC_NUD_FAILURE_VERSION
+import com.android.networkstack.util.NetworkStackUtils.IP_REACHABILITY_MCAST_RESOLICIT_VERSION
+import com.android.networkstack.util.NetworkStackUtils.IP_REACHABILITY_ROUTER_MAC_CHANGE_FAILURE_ONLY_AFTER_ROAM_VERSION
import com.android.testutils.makeNewNeighMessage
import com.android.testutils.waitForIdle
import java.io.FileDescriptor
@@ -89,6 +91,9 @@ private const val TEST_TIMEOUT_MS = 10_000L
private val TEST_IPV4_GATEWAY = parseNumericAddress("192.168.222.3") as Inet4Address
private val TEST_IPV6_GATEWAY = parseNumericAddress("2001:db8::1") as Inet6Address
+private val TEST_MAC_1 = "001122334455"
+private val TEST_MAC_2 = "1122334455aa"
+
// IPv4 gateway is also DNS server.
private val TEST_IPV4_GATEWAY_DNS = parseNumericAddress("192.168.222.100") as Inet4Address
@@ -258,6 +263,14 @@ class IpReachabilityMonitorTest {
}.`when`(dependencies).makeIpNeighborMonitor(any(), any(), any())
doReturn(mIpReachabilityMonitorMetrics)
.`when`(dependencies).getIpReachabilityMonitorMetrics()
+ doReturn(true).`when`(dependencies).isFeatureNotChickenedOut(any(),
+ eq(IP_REACHABILITY_MCAST_RESOLICIT_VERSION))
+
+ // TODO: test with non-default flag combinations.
+ // Note: because dependencies is a mock, all features that are not specified here are
+ // neither enabled nor chickened out.
+ doReturn(true).`when`(dependencies).isFeatureNotChickenedOut(any(),
+ eq(IP_REACHABILITY_ROUTER_MAC_CHANGE_FAILURE_ONLY_AFTER_ROAM_VERSION))
val monitorFuture = CompletableFuture<IpReachabilityMonitor>()
// IpReachabilityMonitor needs to be started from the handler thread
@@ -300,6 +313,15 @@ class IpReachabilityMonitorTest {
lostNeighbor: InetAddress,
eventType: NudEventType
) {
+ runLoseProvisioningTest(newLp, lostNeighbor, eventType, true /* expectedNotifyLost */)
+ }
+
+ private fun runLoseProvisioningTest(
+ newLp: LinkProperties,
+ lostNeighbor: InetAddress,
+ eventType: NudEventType,
+ expectedNotifyLost: Boolean
+ ) {
reachabilityMonitor.updateLinkProperties(newLp)
neighborMonitor.enqueuePacket(makeNewNeighMessage(TEST_IPV4_GATEWAY, NUD_STALE))
@@ -308,8 +330,12 @@ class IpReachabilityMonitorTest {
neighborMonitor.enqueuePacket(makeNewNeighMessage(TEST_IPV6_DNS, NUD_STALE))
neighborMonitor.enqueuePacket(makeNewNeighMessage(lostNeighbor, NUD_FAILED))
- verify(callback, timeout(TEST_TIMEOUT_MS)).notifyLost(eq(lostNeighbor), anyString(),
- eq(eventType))
+ if (expectedNotifyLost) {
+ verify(callback, timeout(TEST_TIMEOUT_MS)).notifyLost(eq(lostNeighbor), anyString(),
+ eq(eventType))
+ } else {
+ verify(callback, never()).notifyLost(eq(lostNeighbor), anyString(), any())
+ }
}
private fun verifyNudFailureMetrics(
@@ -324,6 +350,13 @@ class IpReachabilityMonitorTest {
.setNudNeighborType(eq(lostNeighborType))
}
+ private fun verifyNudFailureMetricsNotReported(
+ ) {
+ verify(mIpReachabilityMonitorMetrics, never()).setNudIpType(any())
+ verify(mIpReachabilityMonitorMetrics, never()).setNudEventType(any())
+ verify(mIpReachabilityMonitorMetrics, never()).setNudNeighborType(any())
+ }
+
// Verify if the notifyLost will be called when one neighbor has lost but it's still
// provisioned.
private fun runLoseNeighborStillProvisionedTest(
@@ -343,12 +376,12 @@ class IpReachabilityMonitorTest {
private fun prepareNeighborReachableButMacAddrChangedTest(
newLp: LinkProperties,
- neighbor: InetAddress
+ neighbor: InetAddress,
+ macaddr: String
) {
reachabilityMonitor.updateLinkProperties(newLp)
- neighborMonitor.enqueuePacket(makeNewNeighMessage(neighbor, NUD_REACHABLE,
- "001122334455" /* oldMac */))
+ neighborMonitor.enqueuePacket(makeNewNeighMessage(neighbor, NUD_REACHABLE, macaddr))
handlerThread.waitForIdle(TEST_TIMEOUT_MS)
verify(callback, never()).notifyLost(eq(neighbor), anyString(),
any(NudEventType::class.java))
@@ -376,6 +409,38 @@ class IpReachabilityMonitorTest {
NUD_ORGANIC_FAILED_CRITICAL)
}
+ @Test
+ fun testLoseProvisioning_ignoreOrganicIpv4DnsLost() {
+ doReturn(true).`when`(dependencies).isFeatureEnabled(any(),
+ eq(IP_REACHABILITY_IGNORE_ORGANIC_NUD_FAILURE_VERSION))
+ runLoseProvisioningTest(TEST_LINK_PROPERTIES, TEST_IPV4_DNS, NUD_ORGANIC_FAILED_CRITICAL,
+ false /* expectedNotifyLost */)
+ }
+
+ @Test
+ fun testLoseProvisioning_ignoreOrganicIpv6DnsLost() {
+ doReturn(true).`when`(dependencies).isFeatureEnabled(any(),
+ eq(IP_REACHABILITY_IGNORE_ORGANIC_NUD_FAILURE_VERSION))
+ runLoseProvisioningTest(TEST_LINK_PROPERTIES, TEST_IPV6_DNS, NUD_ORGANIC_FAILED_CRITICAL,
+ false /* expectedNotifyLost */)
+ }
+
+ @Test
+ fun testLoseProvisioning_ignoreOrganicIpv4GatewayLost() {
+ doReturn(true).`when`(dependencies).isFeatureEnabled(any(),
+ eq(IP_REACHABILITY_IGNORE_ORGANIC_NUD_FAILURE_VERSION))
+ runLoseProvisioningTest(TEST_LINK_PROPERTIES, TEST_IPV4_GATEWAY,
+ NUD_ORGANIC_FAILED_CRITICAL, false /* expectedNotifyLost */)
+ }
+
+ @Test
+ fun testLoseProvisioning_ignoreOrganicIpv6GatewayLost() {
+ doReturn(true).`when`(dependencies).isFeatureEnabled(any(),
+ eq(IP_REACHABILITY_IGNORE_ORGANIC_NUD_FAILURE_VERSION))
+ runLoseProvisioningTest(TEST_LINK_PROPERTIES, TEST_IPV6_GATEWAY,
+ NUD_ORGANIC_FAILED_CRITICAL, false /* expectedNotifyLost */)
+ }
+
private fun runNudProbeFailureMetricsTest(
lp: LinkProperties,
lostNeighbor: InetAddress,
@@ -554,47 +619,62 @@ class IpReachabilityMonitorTest {
verifyNudFailureMetrics(NUD_CONFIRM_FAILED_CRITICAL, IPV6, NUD_NEIGHBOR_GATEWAY)
}
- private fun verifyNudMacAddrChangedType(
+ private fun probeWithNeighborEvent(dueToRoam: Boolean, neighbor: InetAddress, macaddr: String) {
+ reachabilityMonitor.probeAll(dueToRoam)
+ neighborMonitor.enqueuePacket(makeNewNeighMessage(neighbor, NUD_PROBE, macaddr))
+ }
+
+ private fun verifyNudMacAddrChanged(
neighbor: InetAddress,
eventType: NudEventType,
ipType: IpType
) {
- neighborMonitor.enqueuePacket(makeNewNeighMessage(neighbor, NUD_REACHABLE,
- "1122334455aa" /* newMac */))
+ neighborMonitor.enqueuePacket(makeNewNeighMessage(neighbor, NUD_REACHABLE, TEST_MAC_2))
verify(callback, timeout(TEST_TIMEOUT_MS)).notifyLost(eq(neighbor), anyString(),
eq(eventType))
verifyNudFailureMetrics(eventType, ipType, NUD_NEIGHBOR_GATEWAY)
}
+ private fun verifyNudMacAddrChangeNotReported(
+ neighbor: InetAddress,
+ ) {
+ neighborMonitor.enqueuePacket(makeNewNeighMessage(neighbor, NUD_REACHABLE, TEST_MAC_2))
+ verify(callback, never()).notifyLost(eq(neighbor), anyString(), any())
+ verifyNudFailureMetricsNotReported()
+ }
+
@Test
fun testNudProbeFailedMetrics_defaultIPv6GatewayMacAddrChangedAfterRoaming() {
- prepareNeighborReachableButMacAddrChangedTest(TEST_LINK_PROPERTIES, TEST_IPV6_GATEWAY)
-
- reachabilityMonitor.probeAll(true /* dueToRoam */)
- verifyNudMacAddrChangedType(TEST_IPV6_GATEWAY, NUD_POST_ROAMING_MAC_ADDRESS_CHANGED, IPV6)
+ prepareNeighborReachableButMacAddrChangedTest(TEST_LINK_PROPERTIES, TEST_IPV6_GATEWAY,
+ TEST_MAC_1)
+ probeWithNeighborEvent(true /* dueToRoam */, TEST_IPV6_GATEWAY, TEST_MAC_1)
+ verifyNudMacAddrChanged(TEST_IPV6_GATEWAY, NUD_POST_ROAMING_MAC_ADDRESS_CHANGED, IPV6)
}
@Test
fun testNudProbeFailedMetrics_defaultIPv4GatewayMacAddrChangedAfterRoaming() {
- prepareNeighborReachableButMacAddrChangedTest(TEST_LINK_PROPERTIES, TEST_IPV4_GATEWAY)
+ prepareNeighborReachableButMacAddrChangedTest(TEST_LINK_PROPERTIES, TEST_IPV4_GATEWAY,
+ TEST_MAC_1)
- reachabilityMonitor.probeAll(true /* dueToRoam */)
- verifyNudMacAddrChangedType(TEST_IPV4_GATEWAY, NUD_POST_ROAMING_MAC_ADDRESS_CHANGED, IPV4)
+ probeWithNeighborEvent(true /* dueToRoam */, TEST_IPV4_GATEWAY, TEST_MAC_1)
+ verifyNudMacAddrChanged(TEST_IPV4_GATEWAY, NUD_POST_ROAMING_MAC_ADDRESS_CHANGED, IPV4)
}
@Test
fun testNudProbeFailedMetrics_defaultIPv6GatewayMacAddrChangedAfterConfirm() {
- prepareNeighborReachableButMacAddrChangedTest(TEST_LINK_PROPERTIES, TEST_IPV6_GATEWAY)
+ prepareNeighborReachableButMacAddrChangedTest(TEST_LINK_PROPERTIES, TEST_IPV6_GATEWAY,
+ TEST_MAC_1)
reachabilityMonitor.probeAll(false /* dueToRoam */)
- verifyNudMacAddrChangedType(TEST_IPV6_GATEWAY, NUD_CONFIRM_MAC_ADDRESS_CHANGED, IPV6)
+ verifyNudMacAddrChangeNotReported(TEST_IPV6_GATEWAY)
}
@Test
fun testNudProbeFailedMetrics_defaultIPv6GatewayMacAddrChangedAfterOrganic() {
- prepareNeighborReachableButMacAddrChangedTest(TEST_LINK_PROPERTIES, TEST_IPV6_GATEWAY)
+ prepareNeighborReachableButMacAddrChangedTest(TEST_LINK_PROPERTIES, TEST_IPV6_GATEWAY,
+ TEST_MAC_1)
- verifyNudMacAddrChangedType(TEST_IPV6_GATEWAY, NUD_ORGANIC_MAC_ADDRESS_CHANGED, IPV6)
+ verifyNudMacAddrChangeNotReported(TEST_IPV6_GATEWAY)
}
@SuppressLint("NewApi")
diff --git a/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java b/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java
index 77e3a129..43bee559 100644
--- a/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java
+++ b/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java
@@ -19,6 +19,7 @@ package com.android.server.connectivity;
import static android.content.Intent.ACTION_CONFIGURATION_CHANGED;
import static android.net.CaptivePortal.APP_RETURN_DISMISSED;
import static android.net.CaptivePortal.APP_RETURN_WANTED_AS_IS;
+import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
import static android.net.DnsResolver.TYPE_A;
import static android.net.DnsResolver.TYPE_AAAA;
import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_DNS;
@@ -29,6 +30,7 @@ import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_PRIVDNS;
import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL;
import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_SKIPPED;
import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID;
+import static android.net.InetAddresses.parseNumericAddress;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
@@ -64,6 +66,7 @@ import static com.android.networkstack.util.NetworkStackUtils.CAPTIVE_PORTAL_USE
import static com.android.networkstack.util.NetworkStackUtils.DEFAULT_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT;
import static com.android.networkstack.util.NetworkStackUtils.DNS_PROBE_PRIVATE_IP_NO_INTERNET_VERSION;
import static com.android.networkstack.util.NetworkStackUtils.REEVALUATE_WHEN_RESUME;
+import static com.android.server.connectivity.NetworkMonitor.CONFIG_ASYNC_PRIVDNS_PROBE_TIMEOUT_MS;
import static com.android.server.connectivity.NetworkMonitor.INITIAL_REEVALUATE_DELAY_MS;
import static com.android.server.connectivity.NetworkMonitor.extractCharset;
@@ -123,6 +126,7 @@ import android.net.Network;
import android.net.NetworkAgentConfig;
import android.net.NetworkCapabilities;
import android.net.NetworkTestResultParcelable;
+import android.net.PrivateDnsConfigParcel;
import android.net.Uri;
import android.net.captiveportal.CaptivePortalProbeResult;
import android.net.metrics.IpConnectivityLog;
@@ -168,6 +172,7 @@ import com.android.networkstack.apishim.common.UnsupportedApiLevelException;
import com.android.networkstack.metrics.DataStallDetectionStats;
import com.android.networkstack.metrics.DataStallStatsUtils;
import com.android.networkstack.netlink.TcpSocketTracker;
+import com.android.networkstack.util.NetworkStackUtils;
import com.android.server.NetworkStackService.NetworkStackServiceManager;
import com.android.server.connectivity.nano.CellularData;
import com.android.server.connectivity.nano.DnsEvent;
@@ -214,8 +219,12 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Random;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
import javax.net.ssl.SSLHandshakeException;
@@ -366,11 +375,11 @@ public class NetworkMonitorTest {
class DnsEntry {
final String mHostname;
final int mType;
- final List<InetAddress> mAddresses;
- DnsEntry(String host, int type, List<InetAddress> addr) {
+ final AddressSupplier mAddressesSupplier;
+ DnsEntry(String host, int type, AddressSupplier addr) {
mHostname = host;
mType = type;
- mAddresses = addr;
+ mAddressesSupplier = addr;
}
// Full match or partial match that target host contains the entry hostname to support
// random private dns probe hostname.
@@ -378,6 +387,21 @@ public class NetworkMonitorTest {
return hostname.endsWith(mHostname) && type == mType;
}
}
+ interface AddressSupplier {
+ List<InetAddress> get() throws DnsResolver.DnsException;
+ }
+
+ class InstantAddressSupplier implements AddressSupplier {
+ private final List<InetAddress> mAddresses;
+ InstantAddressSupplier(List<InetAddress> addresses) {
+ mAddresses = addresses;
+ }
+ @Override
+ public List<InetAddress> get() {
+ return mAddresses;
+ }
+ }
+
private final ArrayList<DnsEntry> mAnswers = new ArrayList<DnsEntry>();
private boolean mNonBypassPrivateDnsWorking = true;
@@ -387,40 +411,76 @@ public class NetworkMonitorTest {
}
/** Clears all DNS entries. */
- private synchronized void clearAll() {
- mAnswers.clear();
+ private void clearAll() {
+ synchronized (mAnswers) {
+ mAnswers.clear();
+ }
}
/** Returns the answer for a given name and type on the given mock network. */
- private synchronized List<InetAddress> getAnswer(Object mock, String hostname, int type) {
- if (mock == mNetwork && !mNonBypassPrivateDnsWorking) {
- return null;
+ private CompletableFuture<List<InetAddress>> getAnswer(Network mockNetwork, String hostname,
+ int type) {
+ if (mockNetwork == mNetwork && !mNonBypassPrivateDnsWorking) {
+ return CompletableFuture.completedFuture(null);
}
- return mAnswers.stream().filter(e -> e.matches(hostname, type))
- .map(answer -> answer.mAddresses).findFirst().orElse(null);
+ final AddressSupplier answerSupplier;
+
+ synchronized (mAnswers) {
+ answerSupplier = mAnswers.stream()
+ .filter(e -> e.matches(hostname, type))
+ .map(answer -> answer.mAddressesSupplier).findFirst().orElse(null);
+ }
+ if (answerSupplier == null) {
+ return CompletableFuture.completedFuture(null);
+ }
+
+ if (answerSupplier instanceof InstantAddressSupplier) {
+ // Save latency waiting for a query thread if the answer is hardcoded.
+ return CompletableFuture.completedFuture(
+ ((InstantAddressSupplier) answerSupplier).get());
+ }
+ final CompletableFuture<List<InetAddress>> answerFuture = new CompletableFuture<>();
+ new Thread(() -> {
+ try {
+ answerFuture.complete(answerSupplier.get());
+ } catch (DnsResolver.DnsException e) {
+ answerFuture.completeExceptionally(e);
+ }
+ }).start();
+ return answerFuture;
}
/** Sets the answer for a given name and type. */
- private synchronized void setAnswer(String hostname, String[] answer, int type)
- throws UnknownHostException {
- DnsEntry record = new DnsEntry(hostname, type, generateAnswer(answer));
- // Remove the existing one.
- mAnswers.removeIf(entry -> entry.matches(hostname, type));
- // Add or replace a new record.
- mAnswers.add(record);
+ private void setAnswer(String hostname, String[] answer, int type) {
+ setAnswer(hostname, new InstantAddressSupplier(generateAnswer(answer)), type);
+ }
+
+ private void setAnswer(String hostname, AddressSupplier answerSupplier, int type) {
+ DnsEntry record = new DnsEntry(hostname, type, answerSupplier);
+ synchronized (mAnswers) {
+ // Remove the existing one.
+ mAnswers.removeIf(entry -> entry.matches(hostname, type));
+ // Add or replace a new record.
+ mAnswers.add(record);
+ }
}
private List<InetAddress> generateAnswer(String[] answer) {
if (answer == null) return new ArrayList<>();
- return Arrays.stream(answer).map(addr -> InetAddress.parseNumericAddress(addr))
- .collect(toList());
+ return Arrays.stream(answer).map(InetAddresses::parseNumericAddress).collect(toList());
}
/** Simulates a getAllByName call for the specified name on the specified mock network. */
- private InetAddress[] getAllByName(Object mock, String hostname)
+ private InetAddress[] getAllByName(Network mockNetwork, String hostname)
throws UnknownHostException {
- List<InetAddress> answer = queryAllTypes(mock, hostname);
+ final List<InetAddress> answer;
+ try {
+ answer = queryAllTypes(mockNetwork, hostname).get(
+ HANDLER_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ } catch (ExecutionException | InterruptedException | TimeoutException e) {
+ throw new AssertionError("No mock DNS reply within timeout", e);
+ }
if (answer == null || answer.size() == 0) {
throw new UnknownHostException(hostname);
}
@@ -428,57 +488,76 @@ public class NetworkMonitorTest {
}
// Regardless of the type, depends on what the responses contained in the network.
- private List<InetAddress> queryAllTypes(Object mock, String hostname) {
- List<InetAddress> answer = new ArrayList<>();
- addAllIfNotNull(answer, getAnswer(mock, hostname, TYPE_A));
- addAllIfNotNull(answer, getAnswer(mock, hostname, TYPE_AAAA));
- return answer;
- }
-
- private void addAllIfNotNull(List<InetAddress> list, List<InetAddress> c) {
- if (c != null) {
- list.addAll(c);
+ private CompletableFuture<List<InetAddress>> queryAllTypes(
+ Network mockNetwork, String hostname) {
+ if (mockNetwork == mNetwork && !mNonBypassPrivateDnsWorking) {
+ return CompletableFuture.completedFuture(null);
}
+
+ final CompletableFuture<List<InetAddress>> aFuture =
+ getAnswer(mockNetwork, hostname, TYPE_A)
+ .exceptionally(e -> Collections.emptyList());
+ final CompletableFuture<List<InetAddress>> aaaaFuture =
+ getAnswer(mockNetwork, hostname, TYPE_AAAA)
+ .exceptionally(e -> Collections.emptyList());
+
+ final CompletableFuture<List<InetAddress>> combinedFuture = new CompletableFuture<>();
+ aFuture.thenAcceptBoth(aaaaFuture, (res1, res2) -> {
+ final List<InetAddress> answer = new ArrayList<>();
+ if (res1 != null) answer.addAll(res1);
+ if (res2 != null) answer.addAll(res2);
+ combinedFuture.complete(answer);
+ });
+ return combinedFuture;
}
/** Starts mocking DNS queries. */
private void startMocking() throws UnknownHostException {
// Queries on mNetwork using getAllByName.
doAnswer(invocation -> {
- return getAllByName(invocation.getMock(), invocation.getArgument(0));
+ return getAllByName((Network) invocation.getMock(), invocation.getArgument(0));
}).when(mNetwork).getAllByName(any());
// Queries on mCleartextDnsNetwork using DnsResolver#query.
doAnswer(invocation -> {
- return mockQuery(invocation, 1 /* posHostname */, 3 /* posExecutor */,
- 5 /* posCallback */, -1 /* posType */);
+ return mockQuery(invocation, 0 /* posNetwork */, 1 /* posHostname */,
+ 3 /* posExecutor */, 5 /* posCallback */, -1 /* posType */);
}).when(mDnsResolver).query(any(), any(), anyInt(), any(), any(), any());
// Queries on mCleartextDnsNetwork using DnsResolver#query with QueryType.
doAnswer(invocation -> {
- return mockQuery(invocation, 1 /* posHostname */, 4 /* posExecutor */,
- 6 /* posCallback */, 2 /* posType */);
+ return mockQuery(invocation, 0 /* posNetwork */, 1 /* posHostname */,
+ 4 /* posExecutor */, 6 /* posCallback */, 2 /* posType */);
}).when(mDnsResolver).query(any(), any(), anyInt(), anyInt(), any(), any(), any());
}
// Mocking queries on DnsResolver#query.
- private Answer mockQuery(InvocationOnMock invocation, int posHostname, int posExecutor,
- int posCallback, int posType) {
+ private Answer mockQuery(InvocationOnMock invocation, int posNetwork, int posHostname,
+ int posExecutor, int posCallback, int posType) {
String hostname = (String) invocation.getArgument(posHostname);
Executor executor = (Executor) invocation.getArgument(posExecutor);
DnsResolver.Callback<List<InetAddress>> callback = invocation.getArgument(posCallback);
- List<InetAddress> answer;
-
- answer = posType != -1
- ? getAnswer(invocation.getMock(), hostname, invocation.getArgument(posType)) :
- queryAllTypes(invocation.getMock(), hostname);
-
- if (answer != null && answer.size() > 0) {
- new Handler(Looper.getMainLooper()).post(() -> {
- executor.execute(() -> callback.onAnswer(answer, 0));
- });
- }
- // If no answers, do nothing. sendDnsProbeWithTimeout will time out and throw UHE.
+ Network network = invocation.getArgument(posNetwork);
+
+ final CompletableFuture<List<InetAddress>> answerFuture = posType != -1
+ ? getAnswer(network, hostname, invocation.getArgument(posType))
+ : queryAllTypes(network, hostname);
+
+ answerFuture.whenComplete((answer, exception) -> {
+ new Handler(Looper.getMainLooper()).post(() -> executor.execute(() -> {
+ if (exception != null) {
+ if (!(exception instanceof DnsResolver.DnsException)) {
+ throw new AssertionError("Test error building DNS response", exception);
+ }
+ callback.onError((DnsResolver.DnsException) exception);
+ return;
+ }
+ if (answer != null && answer.size() > 0) {
+ callback.onAnswer(answer, 0);
+ }
+ }));
+ });
+ // If the future does not complete or has no answer do nothing. The timeout should fire.
return null;
}
}
@@ -528,6 +607,8 @@ public class NetworkMonitorTest {
// it will fail the test because of timeout expired for querying AAAA and A sequentially.
doReturn(200).when(mResources)
.getInteger(eq(R.integer.config_captive_portal_dns_probe_timeout));
+ doReturn(200).when(mDependencies).getDeviceConfigPropertyInt(
+ eq(NAMESPACE_CONNECTIVITY), eq(CONFIG_ASYNC_PRIVDNS_PROBE_TIMEOUT_MS), anyInt());
doAnswer((invocation) -> {
URL url = invocation.getArgument(0);
@@ -1239,7 +1320,7 @@ public class NetworkMonitorTest {
setPortal302(mHttpConnection);
final String httpHost = new URL(TEST_HTTP_URL).getHost();
mFakeDns.setAnswer(httpHost, new String[] { "2001:db8::123" }, TYPE_AAAA);
- final InetAddress parsedPrivateAddr = InetAddresses.parseNumericAddress(privateAddr);
+ final InetAddress parsedPrivateAddr = parseNumericAddress(privateAddr);
mFakeDns.setAnswer(httpHost, new String[] { privateAddr },
(parsedPrivateAddr instanceof Inet6Address) ? TYPE_AAAA : TYPE_A);
}
@@ -2199,8 +2280,7 @@ public class NetworkMonitorTest {
NETWORK_VALIDATION_RESULT_VALID, 0 /* probesSucceeded */));
}
- @Test
- public void testPrivateDnsSuccess() throws Exception {
+ private void runPrivateDnsSuccessTest() throws Exception {
setStatus(mHttpsConnection, 204);
setStatus(mHttpConnection, 204);
@@ -2232,7 +2312,20 @@ public class NetworkMonitorTest {
}
@Test
- public void testProbeStatusChanged() throws Exception {
+ public void testPrivateDnsSuccess_SyncDns() throws Exception {
+ doReturn(false).when(mDependencies).isFeatureEnabled(
+ any(), eq(NetworkStackUtils.NETWORKMONITOR_ASYNC_PRIVDNS_RESOLUTION));
+ runPrivateDnsSuccessTest();
+ }
+
+ @Test
+ public void testPrivateDnsSuccess_AsyncDns() throws Exception {
+ doReturn(true).when(mDependencies).isFeatureEnabled(
+ any(), eq(NetworkStackUtils.NETWORKMONITOR_ASYNC_PRIVDNS_RESOLUTION));
+ runPrivateDnsSuccessTest();
+ }
+
+ private void runProbeStatusChangedTest() throws Exception {
// Set no record in FakeDns and expect validation to fail.
setStatus(mHttpsConnection, 204);
setStatus(mHttpConnection, 204);
@@ -2255,7 +2348,20 @@ public class NetworkMonitorTest {
}
@Test
- public void testPrivateDnsResolutionRetryUpdate() throws Exception {
+ public void testProbeStatusChanged_SyncDns() throws Exception {
+ doReturn(false).when(mDependencies).isFeatureEnabled(
+ any(), eq(NetworkStackUtils.NETWORKMONITOR_ASYNC_PRIVDNS_RESOLUTION));
+ runProbeStatusChangedTest();
+ }
+
+ @Test
+ public void testProbeStatusChanged_AsyncDns() throws Exception {
+ doReturn(true).when(mDependencies).isFeatureEnabled(
+ any(), eq(NetworkStackUtils.NETWORKMONITOR_ASYNC_PRIVDNS_RESOLUTION));
+ runProbeStatusChangedTest();
+ }
+
+ private void runPrivateDnsResolutionRetryUpdateTest() throws Exception {
// Set no record in FakeDns and expect validation to fail.
setStatus(mHttpsConnection, 204);
setStatus(mHttpConnection, 204);
@@ -2300,6 +2406,197 @@ public class NetworkMonitorTest {
}
@Test
+ public void testPrivateDnsResolutionRetryUpdate_SyncDns() throws Exception {
+ doReturn(false).when(mDependencies).isFeatureEnabled(
+ any(), eq(NetworkStackUtils.NETWORKMONITOR_ASYNC_PRIVDNS_RESOLUTION));
+ runPrivateDnsResolutionRetryUpdateTest();
+ }
+
+ @Test
+ public void testPrivateDnsResolutionRetryUpdate_AsyncDns() throws Exception {
+ doReturn(true).when(mDependencies).isFeatureEnabled(
+ any(), eq(NetworkStackUtils.NETWORKMONITOR_ASYNC_PRIVDNS_RESOLUTION));
+ runPrivateDnsResolutionRetryUpdateTest();
+ }
+
+ @Test
+ public void testAsyncPrivateDnsResolution_PartialTimeout() throws Exception {
+ doReturn(true).when(mDependencies).isFeatureEnabled(
+ any(), eq(NetworkStackUtils.NETWORKMONITOR_ASYNC_PRIVDNS_RESOLUTION));
+ setStatus(mHttpsConnection, 204);
+ setStatus(mHttpConnection, 204);
+
+ WrappedNetworkMonitor wnm = makeCellNotMeteredNetworkMonitor();
+ wnm.notifyPrivateDnsSettingsChanged(new PrivateDnsConfig("dns.google", new InetAddress[0]));
+
+ // Only provide AAAA answer
+ mFakeDns.setAnswer("dns.google", new String[]{"2001:db8::1"}, TYPE_AAAA);
+
+ notifyNetworkConnected(wnm, CELL_NOT_METERED_CAPABILITIES);
+ verifyNetworkTestedValidFromPrivateDns(1 /* interactions */);
+
+ final PrivateDnsConfigParcel expectedConfig = new PrivateDnsConfigParcel();
+ expectedConfig.hostname = "dns.google";
+ expectedConfig.ips = new String[] {"2001:db8::1"};
+ expectedConfig.privateDnsMode = PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
+
+ verify(mCallbacks).notifyPrivateDnsConfigResolved(expectedConfig);
+ }
+
+ @Test
+ public void testAsyncPrivateDnsResolution_PartialFailure() throws Exception {
+ doReturn(true).when(mDependencies).isFeatureEnabled(
+ any(), eq(NetworkStackUtils.NETWORKMONITOR_ASYNC_PRIVDNS_RESOLUTION));
+ setStatus(mHttpsConnection, 204);
+ setStatus(mHttpConnection, 204);
+
+ WrappedNetworkMonitor wnm = makeCellNotMeteredNetworkMonitor();
+ wnm.notifyPrivateDnsSettingsChanged(new PrivateDnsConfig("dns.google", new InetAddress[0]));
+
+ // A succeeds, AAAA fails
+ mFakeDns.setAnswer("dns.google", new String[]{"192.0.2.123"}, TYPE_A);
+ mFakeDns.setAnswer("dns.google", () -> {
+ // DnsResolver.DnsException constructor is T+, so use a mock instead
+ throw mock(DnsResolver.DnsException.class);
+ }, TYPE_AAAA);
+
+ notifyNetworkConnected(wnm, CELL_NOT_METERED_CAPABILITIES);
+ verifyNetworkTestedValidFromPrivateDns(1 /* interactions */);
+
+ final PrivateDnsConfigParcel expectedConfig = new PrivateDnsConfigParcel();
+ expectedConfig.hostname = "dns.google";
+ expectedConfig.ips = new String[] {"192.0.2.123"};
+ expectedConfig.privateDnsMode = PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
+
+ verify(mCallbacks).notifyPrivateDnsConfigResolved(expectedConfig);
+ }
+
+ @Test
+ public void testAsyncPrivateDnsResolution_AQuerySucceedsFirst_PrioritizeAAAA()
+ throws Exception {
+ doReturn(true).when(mDependencies).isFeatureEnabled(
+ any(), eq(NetworkStackUtils.NETWORKMONITOR_ASYNC_PRIVDNS_RESOLUTION));
+ setStatus(mHttpsConnection, 204);
+ setStatus(mHttpConnection, 204);
+
+ WrappedNetworkMonitor wnm = makeCellNotMeteredNetworkMonitor();
+ wnm.notifyPrivateDnsSettingsChanged(new PrivateDnsConfig("dns.google", new InetAddress[0]));
+
+ final ConditionVariable v4Queried = new ConditionVariable();
+ mFakeDns.setAnswer("dns.google", () -> {
+ v4Queried.open();
+ return List.of(parseNumericAddress("192.0.2.123"));
+ }, TYPE_A);
+ mFakeDns.setAnswer("dns.google", () -> {
+ // Make sure the v6 query processing is a bit slower than the v6 one. The small delay
+ // below still does not guarantee that the v4 query will complete first, but it should
+ // the large majority of the time, which should be enough to test it. Even if it does
+ // not, the test should pass.
+ v4Queried.block(HANDLER_TIMEOUT_MS);
+ SystemClock.sleep(10L);
+ return List.of(parseNumericAddress("2001:db8::1"), parseNumericAddress("2001:db8::2"));
+ }, TYPE_AAAA);
+
+ notifyNetworkConnected(wnm, CELL_NOT_METERED_CAPABILITIES);
+ verifyNetworkTestedValidFromPrivateDns(1 /* interactions */);
+
+ final PrivateDnsConfigParcel expectedConfig = new PrivateDnsConfigParcel();
+ expectedConfig.hostname = "dns.google";
+ // The IPv6 addresses are still first
+ expectedConfig.ips = new String[] {"2001:db8::1", "2001:db8::2", "192.0.2.123"};
+ expectedConfig.privateDnsMode = PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
+
+ verify(mCallbacks).notifyPrivateDnsConfigResolved(expectedConfig);
+ }
+
+ @Test
+ public void testAsyncPrivateDnsResolution_ConfigChange_RestartsWithNewConfig()
+ throws Exception {
+ doReturn(true).when(mDependencies).isFeatureEnabled(
+ any(), eq(NetworkStackUtils.NETWORKMONITOR_ASYNC_PRIVDNS_RESOLUTION));
+ setStatus(mHttpsConnection, 204);
+ setStatus(mHttpConnection, 204);
+
+ WrappedNetworkMonitor wnm = makeCellNotMeteredNetworkMonitor();
+ wnm.notifyPrivateDnsSettingsChanged(new PrivateDnsConfig("v1.google", new InetAddress[0]));
+
+ final ConditionVariable blockReplies = new ConditionVariable();
+ final CountDownLatch queriedLatch = new CountDownLatch(2);
+ mFakeDns.setAnswer("v1.google", () -> {
+ queriedLatch.countDown();
+ blockReplies.block(HANDLER_TIMEOUT_MS);
+ return List.of(parseNumericAddress("192.0.2.123"));
+ }, TYPE_A);
+ mFakeDns.setAnswer("v1.google", () -> {
+ queriedLatch.countDown();
+ blockReplies.block(HANDLER_TIMEOUT_MS);
+ return List.of(parseNumericAddress("2001:db8::1"));
+ }, TYPE_AAAA);
+
+ notifyNetworkConnected(wnm, CELL_NOT_METERED_CAPABILITIES);
+
+ queriedLatch.await(HANDLER_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+
+ // Send config update while DNS queries are in flight
+ mFakeDns.setAnswer("v2.google", new String[] { "192.0.2.124" }, TYPE_A);
+ mFakeDns.setAnswer("v2.google", new String[] { "2001:db8::2" }, TYPE_AAAA);
+ wnm.notifyPrivateDnsSettingsChanged(new PrivateDnsConfig("v2.google", new InetAddress[0]));
+
+ // Let the original queries finish. Once DNS queries finish results are posted to the
+ // handler, so they will be processed on the handler after the DNS settings change.
+ blockReplies.open();
+
+ // Expect only callbacks for the 2nd configuration
+ verifyNetworkTestedValidFromPrivateDns(1 /* interactions */);
+
+ final PrivateDnsConfigParcel expectedConfig = new PrivateDnsConfigParcel();
+ expectedConfig.hostname = "v2.google";
+ expectedConfig.ips = new String[] {"2001:db8::2", "192.0.2.124"};
+ expectedConfig.privateDnsMode = PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
+
+ verify(mCallbacks).notifyPrivateDnsConfigResolved(expectedConfig);
+ }
+
+ @Test
+ public void testAsyncPrivateDnsResolution_TurnOffStrictMode_SkipsDnsValidation()
+ throws Exception {
+ doReturn(true).when(mDependencies).isFeatureEnabled(
+ any(), eq(NetworkStackUtils.NETWORKMONITOR_ASYNC_PRIVDNS_RESOLUTION));
+ setStatus(mHttpsConnection, 204);
+ setStatus(mHttpConnection, 204);
+
+ WrappedNetworkMonitor wnm = makeCellNotMeteredNetworkMonitor();
+ wnm.notifyPrivateDnsSettingsChanged(new PrivateDnsConfig("v1.google", new InetAddress[0]));
+
+ final ConditionVariable blockReplies = new ConditionVariable();
+ final CountDownLatch queriedLatch = new CountDownLatch(2);
+ mFakeDns.setAnswer("v1.google", () -> {
+ queriedLatch.countDown();
+ blockReplies.block(HANDLER_TIMEOUT_MS);
+ return List.of(parseNumericAddress("192.0.2.123"));
+ }, TYPE_A);
+ mFakeDns.setAnswer("v1.google", () -> {
+ queriedLatch.countDown();
+ blockReplies.block(HANDLER_TIMEOUT_MS);
+ return List.of(parseNumericAddress("2001:db8::1"));
+ }, TYPE_AAAA);
+
+ notifyNetworkConnected(wnm, CELL_NOT_METERED_CAPABILITIES);
+
+ queriedLatch.await(HANDLER_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+
+ // Send config update while DNS queries are in flight
+ wnm.notifyPrivateDnsSettingsChanged(new PrivateDnsConfig(true /* useTls */));
+
+ // Let the original queries finish. Once DNS queries finish results are posted to the
+ // handler, so they will be processed on the handler after the DNS settings change.
+ blockReplies.open();
+
+ verifyNetworkTestedValidFromHttps(1 /* interactions */);
+ verify(mCallbacks, never()).notifyPrivateDnsConfigResolved(any());
+ }
+
+ @Test
public void testReevaluationInterval_networkResume() throws Exception {
// Setup nothing and expect validation to fail.
doReturn(true).when(mDependencies).isFeatureEnabled(any(), eq(REEVALUATE_WHEN_RESUME));
@@ -2725,8 +3022,8 @@ public class NetworkMonitorTest {
} catch (UnknownHostException e) {
}
- mFakeDns.setAnswer("www.android.com", null, TYPE_A);
- mFakeDns.setAnswer("www.android.com", null, TYPE_AAAA);
+ mFakeDns.setAnswer("www.android.com", (String[]) null, TYPE_A);
+ mFakeDns.setAnswer("www.android.com", (String[]) null, TYPE_AAAA);
try {
wnm.sendDnsProbeWithTimeout("www.android.com", shortTimeoutMs);
fail("DNS query timed out, expected UnknownHostException");