Merge "[KA07] Drop TCP keepalive ack packets" am: 6110472c77 am: 4c5e9c842e
am: f073b981cc
Change-Id: I424b9a07bf7b3ae1950542568ae2f03f2e91d0d5
This commit is contained in:
@@ -23,6 +23,7 @@ import static android.system.OsConstants.ETH_P_ARP;
|
||||
import static android.system.OsConstants.ETH_P_IP;
|
||||
import static android.system.OsConstants.ETH_P_IPV6;
|
||||
import static android.system.OsConstants.IPPROTO_ICMPV6;
|
||||
import static android.system.OsConstants.IPPROTO_TCP;
|
||||
import static android.system.OsConstants.IPPROTO_UDP;
|
||||
import static android.system.OsConstants.SOCK_RAW;
|
||||
|
||||
@@ -56,6 +57,7 @@ import android.system.Os;
|
||||
import android.text.format.DateUtils;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
@@ -150,7 +152,9 @@ public class ApfFilter {
|
||||
DROPPED_IPV6_NON_ICMP_MULTICAST,
|
||||
DROPPED_802_3_FRAME,
|
||||
DROPPED_ETHERTYPE_BLACKLISTED,
|
||||
DROPPED_ARP_REPLY_SPA_NO_HOST;
|
||||
DROPPED_ARP_REPLY_SPA_NO_HOST,
|
||||
DROPPED_IPV4_KEEPALIVE_ACK,
|
||||
DROPPED_IPV6_KEEPALIVE_ACK;
|
||||
|
||||
// Returns the negative byte offset from the end of the APF data segment for
|
||||
// a given counter.
|
||||
@@ -286,6 +290,7 @@ public class ApfFilter {
|
||||
private static final int IPV4_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 16;
|
||||
private static final int IPV4_ANY_HOST_ADDRESS = 0;
|
||||
private static final int IPV4_BROADCAST_ADDRESS = -1; // 255.255.255.255
|
||||
private static final int IPV4_HEADER_LEN = 20; // Without options
|
||||
|
||||
// Traffic class and Flow label are not byte aligned. Luckily we
|
||||
// don't care about either value so we'll consider bytes 1-3 of the
|
||||
@@ -306,6 +311,8 @@ public class ApfFilter {
|
||||
private static final int UDP_DESTINATION_PORT_OFFSET = ETH_HEADER_LEN + 2;
|
||||
private static final int UDP_HEADER_LEN = 8;
|
||||
|
||||
private static final int TCP_HEADER_SIZE_OFFSET = 12;
|
||||
|
||||
private static final int DHCP_CLIENT_PORT = 68;
|
||||
// NOTE: this must be added to the IPv4 header length in IPV4_HEADER_SIZE_MEMORY_SLOT
|
||||
private static final int DHCP_CLIENT_MAC_OFFSET = ETH_HEADER_LEN + UDP_HEADER_LEN + 28;
|
||||
@@ -789,7 +796,7 @@ public class ApfFilter {
|
||||
|
||||
boolean isExpired() {
|
||||
// TODO: We may want to handle 0 lifetime RAs differently, if they are common. We'll
|
||||
// have to calculte the filter lifetime specially as a fraction of 0 is still 0.
|
||||
// have to calculate the filter lifetime specially as a fraction of 0 is still 0.
|
||||
return currentLifetime() <= 0;
|
||||
}
|
||||
|
||||
@@ -848,11 +855,147 @@ public class ApfFilter {
|
||||
}
|
||||
}
|
||||
|
||||
// A class to hold keepalive ack information.
|
||||
private abstract static class TcpKeepaliveAck {
|
||||
// Note that the offset starts from IP header.
|
||||
// These must be added ether header length when generating program.
|
||||
static final int IP_HEADER_OFFSET = 0;
|
||||
|
||||
protected static class TcpKeepaliveAckData {
|
||||
public final byte[] srcAddress;
|
||||
public final int srcPort;
|
||||
public final byte[] dstAddress;
|
||||
public final int dstPort;
|
||||
public final int seq;
|
||||
public final int ack;
|
||||
// Create the characteristics of the ack packet from the sent keepalive packet.
|
||||
TcpKeepaliveAckData(final TcpKeepalivePacketDataParcelable sentKeepalivePacket) {
|
||||
srcAddress = sentKeepalivePacket.dstAddress;
|
||||
srcPort = sentKeepalivePacket.dstPort;
|
||||
dstAddress = sentKeepalivePacket.srcAddress;
|
||||
dstPort = sentKeepalivePacket.srcPort;
|
||||
seq = sentKeepalivePacket.ack;
|
||||
ack = sentKeepalivePacket.seq + 1;
|
||||
}
|
||||
}
|
||||
|
||||
protected final TcpKeepaliveAckData mPacket;
|
||||
protected final byte[] mSrcDstAddr;
|
||||
|
||||
TcpKeepaliveAck(final TcpKeepaliveAckData packet, final byte[] srcDstAddr) {
|
||||
mPacket = packet;
|
||||
mSrcDstAddr = srcDstAddr;
|
||||
}
|
||||
|
||||
static byte[] concatArrays(final byte[]... arr) {
|
||||
int size = 0;
|
||||
for (byte[] a : arr) {
|
||||
size += a.length;
|
||||
}
|
||||
final byte[] result = new byte[size];
|
||||
int offset = 0;
|
||||
for (byte[] a : arr) {
|
||||
System.arraycopy(a, 0, result, offset, a.length);
|
||||
offset += a.length;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return String.format("%s(%d) -> %s(%d), seq=%d, ack=%d",
|
||||
mPacket.srcAddress,
|
||||
mPacket.srcPort,
|
||||
mPacket.dstAddress,
|
||||
mPacket.dstPort,
|
||||
mPacket.seq,
|
||||
mPacket.ack);
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
private class TcpKeepaliveAckV4 extends TcpKeepaliveAck {
|
||||
private static final int IPV4_SRC_ADDR_OFFSET = IP_HEADER_OFFSET + 12;
|
||||
private static final int IPV4_TCP_SRC_PORT_OFFSET = 0;
|
||||
private static final int IPV4_TCP_DST_PORT_OFFSET = 2;
|
||||
private static final int IPV4_TCP_SEQ_OFFSET = 4;
|
||||
private static final int IPV4_TCP_ACK_OFFSET = 8;
|
||||
|
||||
TcpKeepaliveAckV4(final TcpKeepalivePacketDataParcelable sentKeepalivePacket) {
|
||||
this(new TcpKeepaliveAckData(sentKeepalivePacket));
|
||||
}
|
||||
TcpKeepaliveAckV4(final TcpKeepaliveAckData packet) {
|
||||
super(packet, concatArrays(packet.srcAddress, packet.dstAddress) /* srcDstAddr */);
|
||||
}
|
||||
|
||||
@Override
|
||||
void generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException {
|
||||
final String nextFilterLabel = "keepalive_ack" + getUniqueNumberLocked();
|
||||
gen.addLoad8(Register.R0, IPV4_PROTOCOL_OFFSET);
|
||||
gen.addJumpIfR0NotEquals(IPPROTO_TCP, nextFilterLabel);
|
||||
gen.addLoadImmediate(Register.R0, ETH_HEADER_LEN + IPV4_SRC_ADDR_OFFSET);
|
||||
gen.addJumpIfBytesNotEqual(Register.R0, mSrcDstAddr, nextFilterLabel);
|
||||
|
||||
// Pass the packet if it's not zero-sized :
|
||||
// Load the IP header size into R1
|
||||
gen.addLoadFromMemory(Register.R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT);
|
||||
// Load the TCP header size into R0 (it's indexed by R1)
|
||||
gen.addLoad8Indexed(Register.R0, ETH_HEADER_LEN + TCP_HEADER_SIZE_OFFSET);
|
||||
// Size offset is in the top nibble, but it must be multiplied by 4, and the two
|
||||
// top bits of the low nibble are guaranteed to be zeroes. Right-shift R0 by 2.
|
||||
gen.addRightShift(2);
|
||||
// R0 += R1 -> R0 contains TCP + IP headers lenght
|
||||
gen.addAddR1();
|
||||
// Add the Ethernet header length to R0.
|
||||
gen.addLoadImmediate(Register.R1, ETH_HEADER_LEN);
|
||||
gen.addAddR1();
|
||||
// Compare total length of headers to the size of the packet.
|
||||
gen.addLoadFromMemory(Register.R1, gen.PACKET_SIZE_MEMORY_SLOT);
|
||||
gen.addNeg(Register.R0);
|
||||
gen.addAddR1();
|
||||
gen.addJumpIfR0NotEquals(0, nextFilterLabel);
|
||||
|
||||
// Add IPv4 header length
|
||||
gen.addLoadFromMemory(Register.R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT);
|
||||
gen.addLoad16Indexed(Register.R0, ETH_HEADER_LEN + IPV4_TCP_SRC_PORT_OFFSET);
|
||||
gen.addJumpIfR0NotEquals(mPacket.srcPort, nextFilterLabel);
|
||||
gen.addLoad16Indexed(Register.R0, ETH_HEADER_LEN + IPV4_TCP_DST_PORT_OFFSET);
|
||||
gen.addJumpIfR0NotEquals(mPacket.dstPort, nextFilterLabel);
|
||||
gen.addLoad32Indexed(Register.R0, ETH_HEADER_LEN + IPV4_TCP_SEQ_OFFSET);
|
||||
gen.addJumpIfR0NotEquals(mPacket.seq, nextFilterLabel);
|
||||
gen.addLoad32Indexed(Register.R0, ETH_HEADER_LEN + IPV4_TCP_ACK_OFFSET);
|
||||
gen.addJumpIfR0NotEquals(mPacket.ack, nextFilterLabel);
|
||||
|
||||
maybeSetupCounter(gen, Counter.DROPPED_IPV4_KEEPALIVE_ACK);
|
||||
gen.addJump(mCountAndDropLabel);
|
||||
gen.defineLabel(nextFilterLabel);
|
||||
}
|
||||
}
|
||||
|
||||
private class TcpKeepaliveAckV6 extends TcpKeepaliveAck {
|
||||
TcpKeepaliveAckV6(final TcpKeepalivePacketDataParcelable sentKeepalivePacket) {
|
||||
this(new TcpKeepaliveAckData(sentKeepalivePacket));
|
||||
}
|
||||
TcpKeepaliveAckV6(final TcpKeepaliveAckData packet) {
|
||||
super(packet, concatArrays(packet.srcAddress, packet.dstAddress) /* srcDstAddr */);
|
||||
}
|
||||
|
||||
@Override
|
||||
void generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException {
|
||||
throw new UnsupportedOperationException("IPv6 Keepalive is not supported yet");
|
||||
}
|
||||
}
|
||||
|
||||
// Maximum number of RAs to filter for.
|
||||
private static final int MAX_RAS = 10;
|
||||
|
||||
@GuardedBy("this")
|
||||
private ArrayList<Ra> mRas = new ArrayList<Ra>();
|
||||
private ArrayList<Ra> mRas = new ArrayList<>();
|
||||
@GuardedBy("this")
|
||||
private SparseArray<TcpKeepaliveAck> mKeepaliveAcks = new SparseArray<>();
|
||||
|
||||
// There is always some marginal benefit to updating the installed APF program when an RA is
|
||||
// seen because we can extend the program's lifetime slightly, but there is some cost to
|
||||
@@ -981,6 +1124,8 @@ public class ApfFilter {
|
||||
// drop
|
||||
// if it's IPv4 broadcast:
|
||||
// drop
|
||||
// if keepalive ack
|
||||
// drop
|
||||
// pass
|
||||
|
||||
if (mMulticastFilter) {
|
||||
@@ -1024,6 +1169,9 @@ public class ApfFilter {
|
||||
gen.addJumpIfR0Equals(broadcastAddr, mCountAndDropLabel);
|
||||
}
|
||||
|
||||
// If any keepalive filters,
|
||||
generateKeepaliveFilter(gen);
|
||||
|
||||
// If L2 broadcast packet, drop.
|
||||
// TODO: can we invert this condition to fall through to the common pass case below?
|
||||
maybeSetupCounter(gen, Counter.PASSED_IPV4_UNICAST);
|
||||
@@ -1031,6 +1179,8 @@ public class ApfFilter {
|
||||
gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, mCountAndPassLabel);
|
||||
maybeSetupCounter(gen, Counter.DROPPED_IPV4_L2_BROADCAST);
|
||||
gen.addJump(mCountAndDropLabel);
|
||||
} else {
|
||||
generateKeepaliveFilter(gen);
|
||||
}
|
||||
|
||||
// Otherwise, pass
|
||||
@@ -1038,6 +1188,13 @@ public class ApfFilter {
|
||||
gen.addJump(mCountAndPassLabel);
|
||||
}
|
||||
|
||||
private void generateKeepaliveFilter(ApfGenerator gen) throws IllegalInstructionException {
|
||||
// Drop IPv4 Keepalive acks
|
||||
for (int i = 0; i < mKeepaliveAcks.size(); ++i) {
|
||||
final TcpKeepaliveAck ack = mKeepaliveAcks.valueAt(i);
|
||||
if (ack instanceof TcpKeepaliveAckV4) ack.generateFilterLocked(gen);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate filter code to process IPv6 packets. Execution of this code ends in either the
|
||||
@@ -1058,6 +1215,8 @@ public class ApfFilter {
|
||||
// drop
|
||||
// if it's ICMPv6 NA to ff02::1:
|
||||
// drop
|
||||
// if keepalive ack
|
||||
// drop
|
||||
|
||||
gen.addLoad8(Register.R0, IPV6_NEXT_HEADER_OFFSET);
|
||||
|
||||
@@ -1113,6 +1272,12 @@ public class ApfFilter {
|
||||
maybeSetupCounter(gen, Counter.DROPPED_IPV6_MULTICAST_NA);
|
||||
gen.addJump(mCountAndDropLabel);
|
||||
gen.defineLabel(skipUnsolicitedMulticastNALabel);
|
||||
|
||||
// Drop IPv6 Keepalive acks
|
||||
for (int i = 0; i < mKeepaliveAcks.size(); ++i) {
|
||||
final TcpKeepaliveAck ack = mKeepaliveAcks.valueAt(i);
|
||||
if (ack instanceof TcpKeepaliveAckV6) ack.generateFilterLocked(gen);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1491,16 +1656,23 @@ public class ApfFilter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add keepalive packet filter.
|
||||
* Add keepalive ack packet filter.
|
||||
* This will add a filter to drop acks to the keepalive packet passed as an argument.
|
||||
*
|
||||
* @param slot The index used to access the filter.
|
||||
* @param pkt Parameters needed to compose the filter.
|
||||
* @param sentKeepalivePacket The attributes of the sent keepalive packet.
|
||||
*/
|
||||
public synchronized void addKeepalivePacketFilter(int slot,
|
||||
TcpKeepalivePacketDataParcelable pkt) {
|
||||
// TODO: implement this.
|
||||
Log.e(TAG, "APF function is not implemented: addKeepalivePacketFilter(" + slot + ", "
|
||||
+ pkt + ")");
|
||||
public synchronized void addKeepalivePacketFilter(final int slot,
|
||||
final TcpKeepalivePacketDataParcelable sentKeepalivePacket) {
|
||||
log("Adding keepalive ack(" + slot + ")");
|
||||
if (null != mKeepaliveAcks.get(slot)) {
|
||||
throw new IllegalArgumentException("Keepalive slot " + slot + " is occupied");
|
||||
}
|
||||
final int ipVersion = sentKeepalivePacket.srcAddress.length == 4 ? 4 : 6;
|
||||
mKeepaliveAcks.put(slot, (ipVersion == 4)
|
||||
? new TcpKeepaliveAckV4(sentKeepalivePacket)
|
||||
: new TcpKeepaliveAckV6(sentKeepalivePacket));
|
||||
installNewProgramLocked();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1509,8 +1681,8 @@ public class ApfFilter {
|
||||
* @param slot The index used to access the filter.
|
||||
*/
|
||||
public synchronized void removeKeepalivePacketFilter(int slot) {
|
||||
// TODO: implement this.
|
||||
Log.e(TAG, "APF function is not implemented: removeKeepalivePacketFilter(" + slot + ")");
|
||||
mKeepaliveAcks.remove(slot);
|
||||
installNewProgramLocked();
|
||||
}
|
||||
|
||||
static public long counterValue(byte[] data, Counter counter)
|
||||
@@ -1565,6 +1737,17 @@ public class ApfFilter {
|
||||
}
|
||||
pw.decreaseIndent();
|
||||
|
||||
pw.println("Keepalive filter:");
|
||||
pw.increaseIndent();
|
||||
for (int i = 0; i < mKeepaliveAcks.size(); ++i) {
|
||||
final TcpKeepaliveAck keepaliveAck = mKeepaliveAcks.valueAt(i);
|
||||
pw.print("Slot ");
|
||||
pw.print(mKeepaliveAcks.keyAt(i));
|
||||
pw.print(" : ");
|
||||
pw.println(keepaliveAck);
|
||||
}
|
||||
pw.decreaseIndent();
|
||||
|
||||
if (DBG) {
|
||||
pw.println("Last program:");
|
||||
pw.increaseIndent();
|
||||
|
||||
@@ -476,7 +476,7 @@ public class ApfGenerator {
|
||||
|
||||
/**
|
||||
* Add an instruction to the end of the program to load 16-bits from the packet into
|
||||
* {@code register}. The offset of the loaded 16-bits from the begining of the packet is
|
||||
* {@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 register, int offset) {
|
||||
@@ -488,7 +488,7 @@ public class ApfGenerator {
|
||||
|
||||
/**
|
||||
* Add an instruction to the end of the program to load 32-bits from the packet into
|
||||
* {@code register}. The offset of the loaded 32-bits from the begining of the packet is
|
||||
* {@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 register, int offset) {
|
||||
|
||||
@@ -39,13 +39,14 @@ import static org.mockito.Mockito.verify;
|
||||
import android.content.Context;
|
||||
import android.net.LinkAddress;
|
||||
import android.net.LinkProperties;
|
||||
import android.net.SocketKeepalive;
|
||||
import android.net.TcpKeepalivePacketData;
|
||||
import android.net.TcpKeepalivePacketData.TcpSocketInfo;
|
||||
import android.net.apf.ApfFilter.ApfConfiguration;
|
||||
import android.net.apf.ApfGenerator.IllegalInstructionException;
|
||||
import android.net.apf.ApfGenerator.Register;
|
||||
import android.net.ip.IIpClientCallbacks;
|
||||
import android.net.ip.IpClient;
|
||||
import android.net.ip.IpClient.IpClientCallbacksWrapper;
|
||||
import android.net.ip.IpClientCallbacks;
|
||||
import android.net.metrics.IpConnectivityLog;
|
||||
import android.net.metrics.RaEvent;
|
||||
import android.net.util.InterfaceParams;
|
||||
@@ -1003,15 +1004,31 @@ public class ApfTest {
|
||||
private static final byte[] ETH_BROADCAST_MAC_ADDRESS =
|
||||
{(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff };
|
||||
|
||||
private static final int IPV4_HEADER_LEN = 20;
|
||||
private static final int IPV4_VERSION_IHL_OFFSET = ETH_HEADER_LEN + 0;
|
||||
private static final int IPV4_TOTAL_LENGTH_OFFSET = ETH_HEADER_LEN + 2;
|
||||
private static final int IPV4_PROTOCOL_OFFSET = ETH_HEADER_LEN + 9;
|
||||
private static final int IPV4_SRC_ADDR_OFFSET = ETH_HEADER_LEN + 12;
|
||||
private static final int IPV4_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 16;
|
||||
private static final int IPV4_TCP_HEADER_LEN = 20;
|
||||
private static final int IPV4_TCP_HEADER_OFFSET = ETH_HEADER_LEN + IPV4_HEADER_LEN;
|
||||
private static final int IPV4_TCP_SRC_PORT_OFFSET = IPV4_TCP_HEADER_OFFSET + 0;
|
||||
private static final int IPV4_TCP_DEST_PORT_OFFSET = IPV4_TCP_HEADER_OFFSET + 2;
|
||||
private static final int IPV4_TCP_SEQ_NUM_OFFSET = IPV4_TCP_HEADER_OFFSET + 4;
|
||||
private static final int IPV4_TCP_ACK_NUM_OFFSET = IPV4_TCP_HEADER_OFFSET + 8;
|
||||
private static final int IPV4_TCP_HEADER_LENGTH_OFFSET = IPV4_TCP_HEADER_OFFSET + 12;
|
||||
private static final byte[] IPV4_BROADCAST_ADDRESS =
|
||||
{(byte) 255, (byte) 255, (byte) 255, (byte) 255};
|
||||
|
||||
private static final int IPV6_NEXT_HEADER_OFFSET = ETH_HEADER_LEN + 6;
|
||||
private static final int IPV6_HEADER_LEN = 40;
|
||||
private static final int IPV6_SRC_ADDR_OFFSET = ETH_HEADER_LEN + 8;
|
||||
private static final int IPV6_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 24;
|
||||
private static final int IPV6_TCP_HEADER_OFFSET = ETH_HEADER_LEN + IPV6_HEADER_LEN;
|
||||
private static final int IPV6_TCP_SRC_PORT_OFFSET = IPV6_TCP_HEADER_OFFSET + 0;
|
||||
private static final int IPV6_TCP_DEST_PORT_OFFSET = IPV6_TCP_HEADER_OFFSET + 2;
|
||||
private static final int IPV6_TCP_SEQ_NUM_OFFSET = IPV6_TCP_HEADER_OFFSET + 4;
|
||||
private static final int IPV6_TCP_ACK_NUM_OFFSET = IPV6_TCP_HEADER_OFFSET + 8;
|
||||
// The IPv6 all nodes address ff02::1
|
||||
private static final byte[] IPV6_ALL_NODES_ADDRESS =
|
||||
{ (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
|
||||
@@ -1491,6 +1508,200 @@ public class ApfTest {
|
||||
return packet.array();
|
||||
}
|
||||
|
||||
private static final byte[] IPV4_KEEPALIVE_SRC_ADDR = {10, 0, 0, 5};
|
||||
private static final byte[] IPV4_KEEPALIVE_DST_ADDR = {10, 0, 0, 6};
|
||||
private static final byte[] IPV4_ANOTHER_ADDR = {10, 0 , 0, 7};
|
||||
private static final byte[] IPV6_KEEPALIVE_SRC_ADDR =
|
||||
{(byte) 0x24, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, (byte) 0xfa, (byte) 0xf1};
|
||||
private static final byte[] IPV6_KEEPALIVE_DST_ADDR =
|
||||
{(byte) 0x24, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, (byte) 0xfa, (byte) 0xf2};
|
||||
private static final byte[] IPV6_ANOTHER_ADDR =
|
||||
{(byte) 0x24, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, (byte) 0xfa, (byte) 0xf5};
|
||||
|
||||
@Test
|
||||
public void testApfFilterKeepaliveAck() throws Exception {
|
||||
final MockIpClientCallback cb = new MockIpClientCallback();
|
||||
final ApfConfiguration config = getDefaultConfig();
|
||||
config.multicastFilter = DROP_MULTICAST;
|
||||
config.ieee802_3Filter = DROP_802_3_FRAMES;
|
||||
final TestApfFilter apfFilter = new TestApfFilter(mContext, config, cb, mLog);
|
||||
byte[] program;
|
||||
final int srcPort = 12345;
|
||||
final int dstPort = 54321;
|
||||
final int seqNum = 2123456789;
|
||||
final int ackNum = 1234567890;
|
||||
final int anotherSrcPort = 23456;
|
||||
final int anotherDstPort = 65432;
|
||||
final int anotherSeqNum = 2123456780;
|
||||
final int anotherAckNum = 1123456789;
|
||||
final int slot1 = 1;
|
||||
final int slot2 = 2;
|
||||
final int window = 14480;
|
||||
final int windowScale = 4;
|
||||
|
||||
// src: 10.0.0.5, port: 12345
|
||||
// dst: 10.0.0.6, port: 54321
|
||||
InetAddress srcAddr = InetAddress.getByAddress(IPV4_KEEPALIVE_SRC_ADDR);
|
||||
InetAddress dstAddr = InetAddress.getByAddress(IPV4_KEEPALIVE_DST_ADDR);
|
||||
|
||||
final TcpSocketInfo v4Tsi = new TcpSocketInfo(
|
||||
srcAddr, srcPort, dstAddr, dstPort, seqNum, ackNum, window, windowScale);
|
||||
final TcpKeepalivePacketData ipv4TcpKeepalivePacket =
|
||||
TcpKeepalivePacketData.tcpKeepalivePacket(v4Tsi);
|
||||
|
||||
apfFilter.addKeepalivePacketFilter(slot1, ipv4TcpKeepalivePacket.toStableParcelable());
|
||||
program = cb.getApfProgram();
|
||||
|
||||
// Verify IPv4 keepalive ack packet is dropped
|
||||
// src: 10.0.0.6, port: 54321
|
||||
// dst: 10.0.0.5, port: 12345
|
||||
assertDrop(program,
|
||||
ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR,
|
||||
dstPort, srcPort, ackNum, seqNum + 1, 0 /* dataLength */));
|
||||
// Verify IPv4 non-keepalive ack packet from the same source address is passed
|
||||
assertPass(program,
|
||||
ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR,
|
||||
dstPort, srcPort, ackNum + 100, seqNum, 0 /* dataLength */));
|
||||
assertPass(program,
|
||||
ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR,
|
||||
dstPort, srcPort, ackNum, seqNum + 1, 10 /* dataLength */));
|
||||
// Verify IPv4 packet from another address is passed
|
||||
assertPass(program,
|
||||
ipv4Packet(IPV4_ANOTHER_ADDR, IPV4_KEEPALIVE_SRC_ADDR, anotherSrcPort,
|
||||
anotherDstPort, anotherSeqNum, anotherAckNum));
|
||||
|
||||
// Remove IPv4 keepalive filter
|
||||
apfFilter.removeKeepalivePacketFilter(slot1);
|
||||
|
||||
try {
|
||||
// src: 2404:0:0:0:0:0:faf1, port: 12345
|
||||
// dst: 2404:0:0:0:0:0:faf2, port: 54321
|
||||
srcAddr = InetAddress.getByAddress(IPV6_KEEPALIVE_SRC_ADDR);
|
||||
dstAddr = InetAddress.getByAddress(IPV6_KEEPALIVE_DST_ADDR);
|
||||
final TcpSocketInfo v6Tsi = new TcpSocketInfo(
|
||||
srcAddr, srcPort, dstAddr, dstPort, seqNum, ackNum, window, windowScale);
|
||||
final TcpKeepalivePacketData ipv6TcpKeepalivePacket =
|
||||
TcpKeepalivePacketData.tcpKeepalivePacket(v6Tsi);
|
||||
apfFilter.addKeepalivePacketFilter(slot1, ipv6TcpKeepalivePacket.toStableParcelable());
|
||||
program = cb.getApfProgram();
|
||||
|
||||
// Verify IPv6 keepalive ack packet is dropped
|
||||
// src: 2404:0:0:0:0:0:faf2, port: 54321
|
||||
// dst: 2404:0:0:0:0:0:faf1, port: 12345
|
||||
assertDrop(program,
|
||||
ipv6Packet(IPV6_KEEPALIVE_DST_ADDR, IPV6_KEEPALIVE_SRC_ADDR,
|
||||
dstPort, srcPort, ackNum, seqNum + 1));
|
||||
// Verify IPv6 non-keepalive ack packet from the same source address is passed
|
||||
assertPass(program,
|
||||
ipv6Packet(IPV6_KEEPALIVE_DST_ADDR, IPV6_KEEPALIVE_SRC_ADDR,
|
||||
dstPort, srcPort, ackNum + 100, seqNum));
|
||||
// Verify IPv6 packet from another address is passed
|
||||
assertPass(program,
|
||||
ipv6Packet(IPV6_ANOTHER_ADDR, IPV6_KEEPALIVE_SRC_ADDR, anotherSrcPort,
|
||||
anotherDstPort, anotherSeqNum, anotherAckNum));
|
||||
|
||||
// Remove IPv6 keepalive filter
|
||||
apfFilter.removeKeepalivePacketFilter(slot1);
|
||||
|
||||
// Verify multiple filters
|
||||
apfFilter.addKeepalivePacketFilter(slot1, ipv4TcpKeepalivePacket.toStableParcelable());
|
||||
apfFilter.addKeepalivePacketFilter(slot2, ipv6TcpKeepalivePacket.toStableParcelable());
|
||||
program = cb.getApfProgram();
|
||||
|
||||
// Verify IPv4 keepalive ack packet is dropped
|
||||
// src: 10.0.0.6, port: 54321
|
||||
// dst: 10.0.0.5, port: 12345
|
||||
assertDrop(program,
|
||||
ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR,
|
||||
dstPort, srcPort, ackNum, seqNum + 1));
|
||||
// Verify IPv4 non-keepalive ack packet from the same source address is passed
|
||||
assertPass(program,
|
||||
ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR,
|
||||
dstPort, srcPort, ackNum + 100, seqNum));
|
||||
// Verify IPv4 packet from another address is passed
|
||||
assertPass(program,
|
||||
ipv4Packet(IPV4_ANOTHER_ADDR, IPV4_KEEPALIVE_SRC_ADDR, anotherSrcPort,
|
||||
anotherDstPort, anotherSeqNum, anotherAckNum));
|
||||
|
||||
// Verify IPv6 keepalive ack packet is dropped
|
||||
// src: 2404:0:0:0:0:0:faf2, port: 54321
|
||||
// dst: 2404:0:0:0:0:0:faf1, port: 12345
|
||||
assertDrop(program,
|
||||
ipv6Packet(IPV6_KEEPALIVE_DST_ADDR, IPV6_KEEPALIVE_SRC_ADDR,
|
||||
dstPort, srcPort, ackNum, seqNum + 1));
|
||||
// Verify IPv6 non-keepalive ack packet from the same source address is passed
|
||||
assertPass(program,
|
||||
ipv6Packet(IPV6_KEEPALIVE_DST_ADDR, IPV6_KEEPALIVE_SRC_ADDR,
|
||||
dstPort, srcPort, ackNum + 100, seqNum));
|
||||
// Verify IPv6 packet from another address is passed
|
||||
assertPass(program,
|
||||
ipv6Packet(IPV6_ANOTHER_ADDR, IPV6_KEEPALIVE_SRC_ADDR, anotherSrcPort,
|
||||
anotherDstPort, anotherSeqNum, anotherAckNum));
|
||||
|
||||
// Remove keepalive filters
|
||||
apfFilter.removeKeepalivePacketFilter(slot1);
|
||||
apfFilter.removeKeepalivePacketFilter(slot2);
|
||||
} catch (SocketKeepalive.InvalidPacketException e) {
|
||||
// TODO: support V6 packets
|
||||
}
|
||||
|
||||
program = cb.getApfProgram();
|
||||
|
||||
// Verify IPv4, IPv6 packets are passed
|
||||
assertPass(program,
|
||||
ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR,
|
||||
dstPort, srcPort, ackNum, seqNum + 1));
|
||||
assertPass(program,
|
||||
ipv6Packet(IPV6_KEEPALIVE_DST_ADDR, IPV6_KEEPALIVE_SRC_ADDR,
|
||||
dstPort, srcPort, ackNum, seqNum + 1));
|
||||
assertPass(program,
|
||||
ipv4Packet(IPV4_ANOTHER_ADDR, IPV4_KEEPALIVE_SRC_ADDR, srcPort,
|
||||
dstPort, anotherSeqNum, anotherAckNum));
|
||||
assertPass(program,
|
||||
ipv6Packet(IPV6_ANOTHER_ADDR, IPV6_KEEPALIVE_SRC_ADDR, srcPort,
|
||||
dstPort, anotherSeqNum, anotherAckNum));
|
||||
|
||||
apfFilter.shutdown();
|
||||
}
|
||||
|
||||
private static byte[] ipv4Packet(byte[] sip, byte[] tip, int sport,
|
||||
int dport, int seq, int ack) {
|
||||
ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
|
||||
packet.putShort(ETH_ETHERTYPE_OFFSET, (short) ETH_P_IP);
|
||||
packet.put(IPV4_VERSION_IHL_OFFSET, (byte) 0x45);
|
||||
put(packet, IPV4_SRC_ADDR_OFFSET, sip);
|
||||
put(packet, IPV4_DEST_ADDR_OFFSET, tip);
|
||||
packet.putShort(IPV4_TCP_SRC_PORT_OFFSET, (short) sport);
|
||||
packet.putShort(IPV4_TCP_DEST_PORT_OFFSET, (short) dport);
|
||||
packet.putInt(IPV4_TCP_SEQ_NUM_OFFSET, seq);
|
||||
packet.putInt(IPV4_TCP_ACK_NUM_OFFSET, ack);
|
||||
return packet.array();
|
||||
}
|
||||
|
||||
private static byte[] ipv4Packet(byte[] sip, byte[] tip, int sport,
|
||||
int dport, int seq, int ack, int dataLength) {
|
||||
final int totalLength = dataLength + IPV4_HEADER_LEN + IPV4_TCP_HEADER_LEN;
|
||||
|
||||
ByteBuffer packet = ByteBuffer.wrap(ipv4Packet(sip, tip, sport, dport, seq, ack));
|
||||
packet.putShort(IPV4_TOTAL_LENGTH_OFFSET, (short) totalLength);
|
||||
// TCP header length 5, reserved 3 bits, NS=0
|
||||
packet.put(IPV4_TCP_HEADER_LENGTH_OFFSET, (byte) 0x50);
|
||||
return packet.array();
|
||||
}
|
||||
|
||||
private static byte[] ipv6Packet(byte[] sip, byte[] tip, int sport,
|
||||
int dport, int seq, int ack) {
|
||||
ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
|
||||
packet.putShort(ETH_ETHERTYPE_OFFSET, (short) ETH_P_IPV6);
|
||||
put(packet, IPV6_SRC_ADDR_OFFSET, sip);
|
||||
put(packet, IPV6_DEST_ADDR_OFFSET, tip);
|
||||
packet.putShort(IPV6_TCP_SRC_PORT_OFFSET, (short) sport);
|
||||
packet.putShort(IPV6_TCP_DEST_PORT_OFFSET, (short) dport);
|
||||
packet.putInt(IPV6_TCP_SEQ_NUM_OFFSET, seq);
|
||||
packet.putInt(IPV6_TCP_ACK_NUM_OFFSET, ack);
|
||||
return packet.array();
|
||||
}
|
||||
|
||||
// Verify that the last program pushed to the IpClient.Callback properly filters the
|
||||
// given packet for the given lifetime.
|
||||
private void verifyRaLifetime(byte[] program, ByteBuffer packet, int lifetime) {
|
||||
|
||||
Reference in New Issue
Block a user