Merge "[KA07] Drop TCP keepalive ack packets"

am: 6110472c77

Change-Id: I8294b49aa5a7a6f24996a697ca9f26b2f8179357
This commit is contained in:
Chalard Jean
2019-02-06 10:06:29 -08:00
committed by android-build-merger
3 changed files with 410 additions and 16 deletions

View File

@@ -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();

View File

@@ -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) {

View File

@@ -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) {