Fix RAs with different retansmission timer would be dropped by apf
When firmware receives RAs with different retransmission
timer, it is expected the RAs should be accepted by apf
filter. However, they are currently dropped since missing
fields which should be added into match section. It causes
to apf filter treats those RAs as the same and then drops.
This change adds the remaining fields to match section to
compare reachable time and retransmission timer with incoming
RAs.
Also, add test to check that RIOs differing only in the
first 4 bytes are different should be passed.
Bug: 143186590
Test: sent RAs with different rtt and check RAs are accepted
Change-Id: I7e2de29740f96b212634b5aeffe709d57afafc68
diff --git a/src/android/net/apf/ApfFilter.java b/src/android/net/apf/ApfFilter.java
index 33f69f4..75a737d 100644
--- a/src/android/net/apf/ApfFilter.java
+++ b/src/android/net/apf/ApfFilter.java
@@ -514,9 +514,9 @@
public final Type type;
/** Offset into the packet at which this section begins. */
public final int start;
- /** Length of this section. */
+ /** Length of this section in bytes. */
public final int length;
- /** If this is a lifetime, the ICMP option that the defined it. 0 for router lifetime. */
+ /** If this is a lifetime, the ICMP option that defined it. 0 for router lifetime. */
public final int option;
/** If this is a lifetime, the lifetime value. */
public final long lifetime;
@@ -785,8 +785,9 @@
addLifetimeSection(ICMP6_RA_ROUTER_LIFETIME_LEN, 0, routerLifetime);
builder.updateRouterLifetime(routerLifetime);
- // Ensures that the RA is not truncated.
- mPacket.position(ICMP6_RA_OPTION_OFFSET);
+ // Add remaining fields (reachable time and retransmission timer) to match section.
+ addMatchUntil(ICMP6_RA_OPTION_OFFSET);
+
while (mPacket.hasRemaining()) {
final int position = mPacket.position();
final int optionType = getUint8(mPacket, position);
@@ -797,7 +798,7 @@
mPrefixOptionOffsets.add(position);
// Parse valid lifetime
- addMatchSection(ICMP6_PREFIX_OPTION_VALID_LIFETIME_LEN);
+ addMatchSection(ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET);
lifetime = getUint32(mPacket, mPacket.position());
addLifetimeSection(ICMP6_PREFIX_OPTION_VALID_LIFETIME_LEN,
ICMP6_PREFIX_OPTION_TYPE, lifetime);
diff --git a/tests/unit/src/android/net/apf/ApfTest.java b/tests/unit/src/android/net/apf/ApfTest.java
index 24c67d7..ab92892 100644
--- a/tests/unit/src/android/net/apf/ApfTest.java
+++ b/tests/unit/src/android/net/apf/ApfTest.java
@@ -1056,10 +1056,14 @@
private static final int ICMP6_NEIGHBOR_ANNOUNCEMENT = 136;
private static final int ICMP6_RA_HEADER_LEN = 16;
- private static final int ICMP6_RA_ROUTER_LIFETIME_OFFSET =
- IP_HEADER_OFFSET + IPV6_HEADER_LEN + 6;
private static final int ICMP6_RA_CHECKSUM_OFFSET =
IP_HEADER_OFFSET + IPV6_HEADER_LEN + 2;
+ private static final int ICMP6_RA_ROUTER_LIFETIME_OFFSET =
+ IP_HEADER_OFFSET + IPV6_HEADER_LEN + 6;
+ private static final int ICMP6_RA_REACHABLE_TIME_OFFSET =
+ IP_HEADER_OFFSET + IPV6_HEADER_LEN + 8;
+ private static final int ICMP6_RA_RETRANSMISSION_TIMER_OFFSET =
+ IP_HEADER_OFFSET + IPV6_HEADER_LEN + 12;
private static final int ICMP6_RA_OPTION_OFFSET =
IP_HEADER_OFFSET + IPV6_HEADER_LEN + ICMP6_RA_HEADER_LEN;
@@ -2000,6 +2004,25 @@
ipClientCallback.assertNoProgramUpdate();
}
+ private ByteBuffer makeBaseRaPacket() {
+ ByteBuffer basePacket = ByteBuffer.wrap(new byte[ICMP6_RA_OPTION_OFFSET]);
+ final int ROUTER_LIFETIME = 1000;
+ final int VERSION_TRAFFIC_CLASS_FLOW_LABEL_OFFSET = ETH_HEADER_LEN;
+ // IPv6, traffic class = 0, flow label = 0x12345
+ final int VERSION_TRAFFIC_CLASS_FLOW_LABEL = 0x60012345;
+
+ basePacket.putShort(ETH_ETHERTYPE_OFFSET, (short) ETH_P_IPV6);
+ basePacket.putInt(VERSION_TRAFFIC_CLASS_FLOW_LABEL_OFFSET,
+ VERSION_TRAFFIC_CLASS_FLOW_LABEL);
+ basePacket.put(IPV6_NEXT_HEADER_OFFSET, (byte) IPPROTO_ICMPV6);
+ basePacket.put(ICMP6_TYPE_OFFSET, (byte) ICMP6_ROUTER_ADVERTISEMENT);
+ basePacket.putShort(ICMP6_RA_ROUTER_LIFETIME_OFFSET, (short) ROUTER_LIFETIME);
+ basePacket.position(IPV6_DEST_ADDR_OFFSET);
+ basePacket.put(IPV6_ALL_NODES_ADDRESS);
+
+ return basePacket;
+ }
+
@Test
public void testApfFilterRa() throws Exception {
MockIpClientCallback ipClientCallback = new MockIpClientCallback();
@@ -2021,15 +2044,7 @@
final int VERSION_TRAFFIC_CLASS_FLOW_LABEL = 0x60012345;
// Verify RA is passed the first time
- ByteBuffer basePacket = ByteBuffer.wrap(new byte[ICMP6_RA_OPTION_OFFSET]);
- basePacket.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6);
- basePacket.putInt(VERSION_TRAFFIC_CLASS_FLOW_LABEL_OFFSET,
- VERSION_TRAFFIC_CLASS_FLOW_LABEL);
- basePacket.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_ICMPV6);
- basePacket.put(ICMP6_TYPE_OFFSET, (byte)ICMP6_ROUTER_ADVERTISEMENT);
- basePacket.putShort(ICMP6_RA_ROUTER_LIFETIME_OFFSET, (short)ROUTER_LIFETIME);
- basePacket.position(IPV6_DEST_ADDR_OFFSET);
- basePacket.put(IPV6_ALL_NODES_ADDRESS);
+ ByteBuffer basePacket = makeBaseRaPacket();
assertPass(program, basePacket.array());
verifyRaLifetime(apfFilter, ipClientCallback, basePacket, ROUTER_LIFETIME);
@@ -2083,6 +2098,16 @@
verifyRaLifetime(apfFilter, ipClientCallback, routeInfoOptionPacket, ROUTE_LIFETIME);
verifyRaEvent(new RaEvent(ROUTER_LIFETIME, -1, -1, ROUTE_LIFETIME, -1, -1));
+ // Check that RIOs differing only in the first 4 bytes are different.
+ ByteBuffer similarRouteInfoOptionPacket = ByteBuffer.wrap(
+ new byte[ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_OPTION_LEN + IPV6_ADDR_LEN]);
+ basePacket.clear();
+ similarRouteInfoOptionPacket.put(basePacket);
+ addRioOption(similarRouteInfoOptionPacket, ROUTE_LIFETIME, "64:ff9b::/64");
+ // Packet should be passed because it is different.
+ program = ipClientCallback.getApfProgram();
+ assertPass(program, similarRouteInfoOptionPacket.array());
+
ByteBuffer dnsslOptionPacket = ByteBuffer.wrap(
new byte[ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_OPTION_LEN]);
basePacket.clear();
@@ -2111,6 +2136,46 @@
apfFilter.shutdown();
}
+ @Test
+ public void testRaWithDifferentReachableTimeAndRetransTimer() throws Exception {
+ final MockIpClientCallback ipClientCallback = new MockIpClientCallback();
+ final ApfConfiguration config = getDefaultConfig();
+ config.multicastFilter = DROP_MULTICAST;
+ config.ieee802_3Filter = DROP_802_3_FRAMES;
+ final TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipClientCallback, mLog);
+ byte[] program = ipClientCallback.getApfProgram();
+ final int RA_REACHABLE_TIME = 1800;
+ final int RA_RETRANSMISSION_TIMER = 1234;
+
+ // Create an Ra packet without options
+ // Reachable time = 1800, retransmission timer = 1234
+ ByteBuffer raPacket = makeBaseRaPacket();
+ raPacket.position(ICMP6_RA_REACHABLE_TIME_OFFSET);
+ raPacket.putInt(RA_REACHABLE_TIME);
+ raPacket.putInt(RA_RETRANSMISSION_TIMER);
+ // First RA passes filter
+ assertPass(program, raPacket.array());
+
+ // Assume apf is shown the given RA, it generates program to filter it.
+ ipClientCallback.resetApfProgramWait();
+ apfFilter.pretendPacketReceived(raPacket.array());
+ program = ipClientCallback.getApfProgram();
+ assertDrop(program, raPacket.array());
+
+ // A packet with different reachable time should be passed.
+ // Reachable time = 2300, retransmission timer = 1234
+ raPacket.clear();
+ raPacket.putInt(ICMP6_RA_REACHABLE_TIME_OFFSET, RA_REACHABLE_TIME + 500);
+ assertPass(program, raPacket.array());
+
+ // A packet with different retransmission timer should be passed.
+ // Reachable time = 1800, retransmission timer = 2234
+ raPacket.clear();
+ raPacket.putInt(ICMP6_RA_REACHABLE_TIME_OFFSET, RA_REACHABLE_TIME);
+ raPacket.putInt(ICMP6_RA_RETRANSMISSION_TIMER_OFFSET, RA_RETRANSMISSION_TIMER + 1000);
+ assertPass(program, raPacket.array());
+ }
+
/**
* Stage a file for testing, i.e. make it native accessible. Given a resource ID,
* copy that resource into the app's data directory and return the path to it.