Merge "Dropping NAT-T keepalive packet from APF"

This commit is contained in:
Aaron Huang
2019-05-13 11:33:37 +00:00
committed by Gerrit Code Review
3 changed files with 341 additions and 107 deletions

View File

@@ -155,7 +155,9 @@ public class ApfFilter {
DROPPED_ETHERTYPE_BLACKLISTED,
DROPPED_ARP_REPLY_SPA_NO_HOST,
DROPPED_IPV4_KEEPALIVE_ACK,
DROPPED_IPV6_KEEPALIVE_ACK;
DROPPED_IPV6_KEEPALIVE_ACK,
DROPPED_IPV4_NATT_KEEPALIVE,
DROPPED_IPV6_NATT_KEEPALIVE;
// Returns the negative byte offset from the end of the APF data segment for
// a given counter.
@@ -857,12 +859,129 @@ public class ApfFilter {
}
}
// A class to hold keepalive ack information.
private abstract static class TcpKeepaliveAck {
// TODO: Refactor these subclasses to avoid so much repetition.
private abstract static class KeepalivePacket {
// 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;
static final int IPV4_SRC_ADDR_OFFSET = IP_HEADER_OFFSET + 12;
// 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;
}
// A class to hold NAT-T keepalive ack information.
private abstract static class NattKeepaliveAck extends KeepalivePacket {
static final int UDP_LENGTH_OFFSET = 4;
static final int UDP_HEADER_LEN = 8;
protected static class NattKeepaliveAckData {
public final byte[] srcAddress;
public final int srcPort;
public final byte[] dstAddress;
public final int dstPort;
NattKeepaliveAckData(final NattKeepalivePacketDataParcelable sentKeepalivePacket) {
srcAddress = sentKeepalivePacket.dstAddress;
srcPort = sentKeepalivePacket.dstPort;
dstAddress = sentKeepalivePacket.srcAddress;
dstPort = sentKeepalivePacket.srcPort;
}
}
protected final NattKeepaliveAckData mPacket;
protected final byte[] mSrcDstAddr;
protected final byte[] mPortFingerprint;
// NAT-T keepalive packet
protected final byte[] mPayload = {(byte) 0xff};
NattKeepaliveAck(final NattKeepaliveAckData packet, final byte[] srcDstAddr) {
mPacket = packet;
mSrcDstAddr = srcDstAddr;
mPortFingerprint = generatePortFingerprint(mPacket.srcPort, mPacket.dstPort);
}
static byte[] generatePortFingerprint(int srcPort, int dstPort) {
final ByteBuffer fp = ByteBuffer.allocate(4);
fp.order(ByteOrder.BIG_ENDIAN);
fp.putShort((short) srcPort);
fp.putShort((short) dstPort);
return fp.array();
}
public String toString() {
try {
return String.format("%s -> %s",
NetworkStackUtils.addressAndPortToString(
InetAddress.getByAddress(mPacket.srcAddress), mPacket.srcPort),
NetworkStackUtils.addressAndPortToString(
InetAddress.getByAddress(mPacket.dstAddress), mPacket.dstPort));
} catch (UnknownHostException e) {
return "Unknown host";
}
}
}
private class NattKeepaliveAckV4 extends NattKeepaliveAck {
NattKeepaliveAckV4(final NattKeepalivePacketDataParcelable sentKeepalivePacket) {
this(new NattKeepaliveAckData(sentKeepalivePacket));
}
NattKeepaliveAckV4(final NattKeepaliveAckData packet) {
super(packet, concatArrays(packet.srcAddress, packet.dstAddress) /* srcDstAddr */);
}
@Override
void generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException {
final String nextFilterLabel = "natt_keepalive_filter" + getUniqueNumberLocked();
gen.addLoadImmediate(Register.R0, ETH_HEADER_LEN + IPV4_SRC_ADDR_OFFSET);
gen.addJumpIfBytesNotEqual(Register.R0, mSrcDstAddr, nextFilterLabel);
// A NAT-T keepalive packet contains 1 byte payload with the value 0xff
// Check payload length is 1
gen.addLoadFromMemory(Register.R0, gen.IPV4_HEADER_SIZE_MEMORY_SLOT);
gen.addAdd(UDP_HEADER_LEN);
gen.addSwap();
gen.addLoad16(Register.R0, IPV4_TOTAL_LENGTH_OFFSET);
gen.addNeg(Register.R1);
gen.addAddR1();
gen.addJumpIfR0NotEquals(1, nextFilterLabel);
// R0 = R0 + R1 -> R0 contains IP header
gen.addLoadFromMemory(Register.R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT);
gen.addLoadImmediate(Register.R0, ETH_HEADER_LEN);
gen.addAddR1();
gen.addJumpIfBytesNotEqual(Register.R0, mPortFingerprint, nextFilterLabel);
// Payload offset = R0 + UDP header length
gen.addAdd(UDP_HEADER_LEN);
gen.addJumpIfBytesNotEqual(Register.R0, mPayload, nextFilterLabel);
maybeSetupCounter(gen, Counter.DROPPED_IPV4_NATT_KEEPALIVE);
gen.addJump(mCountAndDropLabel);
gen.defineLabel(nextFilterLabel);
}
}
private class NattKeepaliveAckV6 extends NattKeepaliveAck {
NattKeepaliveAckV6(final NattKeepalivePacketDataParcelable sentKeepalivePacket) {
this(new NattKeepaliveAckData(sentKeepalivePacket));
}
NattKeepaliveAckV6(final NattKeepaliveAckData packet) {
super(packet, concatArrays(packet.srcAddress, packet.dstAddress) /* srcDstAddr */);
}
@Override
void generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException {
throw new UnsupportedOperationException("IPv6 NAT-T Keepalive is not supported yet");
}
}
// A class to hold TCP keepalive ack information.
private abstract static class TcpKeepaliveAck extends KeepalivePacket {
protected static class TcpKeepaliveAckData {
public final byte[] srcAddress;
public final int srcPort;
@@ -870,6 +989,7 @@ public class ApfFilter {
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;
@@ -902,28 +1022,18 @@ public class ApfFilter {
return fp.array();
}
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);
try {
return String.format("%s -> %s , seq=%d, ack=%d",
NetworkStackUtils.addressAndPortToString(
InetAddress.getByAddress(mPacket.srcAddress), mPacket.srcPort),
NetworkStackUtils.addressAndPortToString(
InetAddress.getByAddress(mPacket.dstAddress), mPacket.dstPort),
Integer.toUnsignedLong(mPacket.seq),
Integer.toUnsignedLong(mPacket.ack));
} catch (UnknownHostException e) {
return "Unknown host";
}
}
// Append a filter for this keepalive ack to {@code gen}.
@@ -933,7 +1043,6 @@ public class ApfFilter {
}
private class TcpKeepaliveAckV4 extends TcpKeepaliveAck {
private static final int IPV4_SRC_ADDR_OFFSET = IP_HEADER_OFFSET + 12;
TcpKeepaliveAckV4(final TcpKeepalivePacketDataParcelable sentKeepalivePacket) {
this(new TcpKeepaliveAckData(sentKeepalivePacket));
@@ -987,7 +1096,7 @@ public class ApfFilter {
@Override
void generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException {
throw new UnsupportedOperationException("IPv6 Keepalive is not supported yet");
throw new UnsupportedOperationException("IPv6 TCP Keepalive is not supported yet");
}
}
@@ -997,7 +1106,7 @@ public class ApfFilter {
@GuardedBy("this")
private ArrayList<Ra> mRas = new ArrayList<>();
@GuardedBy("this")
private SparseArray<TcpKeepaliveAck> mKeepaliveAcks = new SparseArray<>();
private SparseArray<KeepalivePacket> mKeepalivePackets = 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
@@ -1171,9 +1280,12 @@ public class ApfFilter {
gen.addJumpIfR0Equals(broadcastAddr, mCountAndDropLabel);
}
// If any keepalive filter matches, drop
// If any TCP keepalive filter matches, drop
generateV4KeepaliveFilters(gen);
// If any NAT-T keepalive filter matches, drop
generateV4NattKeepaliveFilters(gen);
// Otherwise, this is an IPv4 unicast, pass
// If L2 broadcast packet, drop.
// TODO: can we invert this condition to fall through to the common pass case below?
@@ -1191,25 +1303,36 @@ public class ApfFilter {
gen.addJump(mCountAndPassLabel);
}
private void generateV4KeepaliveFilters(ApfGenerator gen) throws IllegalInstructionException {
final String skipV4KeepaliveFilter = "skip_v4_keepalive_filter";
final boolean haveV4KeepaliveAcks = NetworkStackUtils.any(mKeepaliveAcks,
ack -> ack instanceof TcpKeepaliveAckV4);
private void generateFilters(ApfGenerator gen, Class<?> filterType, int proto, int offset,
String label) throws IllegalInstructionException {
final boolean haveKeepaliveAcks = NetworkStackUtils.any(mKeepalivePackets,
ack -> filterType.isInstance(ack));
// If no keepalive acks
if (!haveV4KeepaliveAcks) return;
// If no keepalive packets of this type
if (!haveKeepaliveAcks) return;
// If not tcp, skip keepalive filters
gen.addLoad8(Register.R0, IPV4_PROTOCOL_OFFSET);
gen.addJumpIfR0NotEquals(IPPROTO_TCP, skipV4KeepaliveFilter);
// If not the right proto, skip keepalive filters
gen.addLoad8(Register.R0, offset);
gen.addJumpIfR0NotEquals(proto, label);
// 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);
// Drop Keepalive packets
for (int i = 0; i < mKeepalivePackets.size(); ++i) {
final KeepalivePacket ack = mKeepalivePackets.valueAt(i);
if (filterType.isInstance(ack)) ack.generateFilterLocked(gen);
}
gen.defineLabel(skipV4KeepaliveFilter);
gen.defineLabel(label);
}
private void generateV4KeepaliveFilters(ApfGenerator gen) throws IllegalInstructionException {
generateFilters(gen, TcpKeepaliveAckV4.class, IPPROTO_TCP, IPV4_PROTOCOL_OFFSET,
"skip_v4_keepalive_filter");
}
private void generateV4NattKeepaliveFilters(ApfGenerator gen)
throws IllegalInstructionException {
generateFilters(gen, NattKeepaliveAckV4.class, IPPROTO_UDP, IPV4_PROTOCOL_OFFSET,
"skip_v4_nattkeepalive_filter");
}
/**
@@ -1294,24 +1417,8 @@ public class ApfFilter {
}
private void generateV6KeepaliveFilters(ApfGenerator gen) throws IllegalInstructionException {
final String skipV6KeepaliveFilter = "skip_v6_keepalive_filter";
final boolean haveV6KeepaliveAcks = NetworkStackUtils.any(mKeepaliveAcks,
ack -> ack instanceof TcpKeepaliveAckV6);
// If no keepalive acks
if (!haveV6KeepaliveAcks) return;
// If not tcp, skip keepalive filters
gen.addLoad8(Register.R0, IPV6_NEXT_HEADER_OFFSET);
gen.addJumpIfR0NotEquals(IPPROTO_TCP, skipV6KeepaliveFilter);
// 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);
}
gen.defineLabel(skipV6KeepaliveFilter);
generateFilters(gen, TcpKeepaliveAckV6.class, IPPROTO_TCP, IPV6_NEXT_HEADER_OFFSET,
"skip_v6_keepalive_filter");
}
/**
@@ -1701,11 +1808,11 @@ public class ApfFilter {
public synchronized void addTcpKeepalivePacketFilter(final int slot,
final TcpKeepalivePacketDataParcelable sentKeepalivePacket) {
log("Adding keepalive ack(" + slot + ")");
if (null != mKeepaliveAcks.get(slot)) {
if (null != mKeepalivePackets.get(slot)) {
throw new IllegalArgumentException("Keepalive slot " + slot + " is occupied");
}
final int ipVersion = sentKeepalivePacket.srcAddress.length == 4 ? 4 : 6;
mKeepaliveAcks.put(slot, (ipVersion == 4)
mKeepalivePackets.put(slot, (ipVersion == 4)
? new TcpKeepaliveAckV4(sentKeepalivePacket)
: new TcpKeepaliveAckV6(sentKeepalivePacket));
installNewProgramLocked();
@@ -1720,7 +1827,15 @@ public class ApfFilter {
*/
public synchronized void addNattKeepalivePacketFilter(final int slot,
final NattKeepalivePacketDataParcelable sentKeepalivePacket) {
Log.e(TAG, "APF add NATT keepalive filter is not implemented");
log("Adding NAT-T keepalive packet(" + slot + ")");
if (null != mKeepalivePackets.get(slot)) {
throw new IllegalArgumentException("Natt Keepalive slot " + slot + " is occupied");
}
final int ipVersion = sentKeepalivePacket.srcAddress.length == 4 ? 4 : 6;
mKeepalivePackets.put(slot, (ipVersion == 4)
? new NattKeepaliveAckV4(sentKeepalivePacket)
: new NattKeepaliveAckV6(sentKeepalivePacket));
installNewProgramLocked();
}
/**
@@ -1729,7 +1844,8 @@ public class ApfFilter {
* @param slot The index used to access the filter.
*/
public synchronized void removeKeepalivePacketFilter(int slot) {
mKeepaliveAcks.remove(slot);
log("Removing keepalive packet(" + slot + ")");
mKeepalivePackets.remove(slot);
installNewProgramLocked();
}
@@ -1785,14 +1901,29 @@ public class ApfFilter {
}
pw.decreaseIndent();
pw.println("Keepalive filters:");
pw.println("TCP Keepalive filters:");
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);
for (int i = 0; i < mKeepalivePackets.size(); ++i) {
final KeepalivePacket keepalivePacket = mKeepalivePackets.valueAt(i);
if (keepalivePacket instanceof TcpKeepaliveAck) {
pw.print("Slot ");
pw.print(mKeepalivePackets.keyAt(i));
pw.print(" : ");
pw.println(keepalivePacket);
}
}
pw.decreaseIndent();
pw.println("NAT-T Keepalive filters:");
pw.increaseIndent();
for (int i = 0; i < mKeepalivePackets.size(); ++i) {
final KeepalivePacket keepalivePacket = mKeepalivePackets.valueAt(i);
if (keepalivePacket instanceof NattKeepaliveAck) {
pw.print("Slot ");
pw.print(mKeepalivePackets.keyAt(i));
pw.print(" : ");
pw.println(keepalivePacket);
}
}
pw.decreaseIndent();
@@ -1858,4 +1989,18 @@ public class ApfFilter {
+ (uint8(bytes[2]) << 8)
+ (uint8(bytes[3]));
}
private 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;
}
}

View File

@@ -23,6 +23,8 @@ import android.util.SparseArray;
import java.io.FileDescriptor;
import java.io.IOException;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.List;
import java.util.function.Predicate;
@@ -227,4 +229,13 @@ public class NetworkStackUtils {
private static native void addArpEntry(byte[] ethAddr, byte[] netAddr, String ifname,
FileDescriptor fd) throws IOException;
/**
* Return IP address and port in a string format.
*/
public static String addressAndPortToString(InetAddress address, int port) {
return String.format(
(address instanceof Inet6Address) ? "[%s]:%d" : "%s:%d",
address.getHostAddress(), port);
}
}

View File

@@ -40,6 +40,7 @@ import static org.mockito.Mockito.verify;
import android.content.Context;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.NattKeepalivePacketDataParcelable;
import android.net.TcpKeepalivePacketDataParcelable;
import android.net.apf.ApfFilter.ApfConfiguration;
import android.net.apf.ApfGenerator.IllegalInstructionException;
@@ -998,47 +999,54 @@ public class ApfTest {
}
}
private static final int ETH_HEADER_LEN = 14;
private static final int ETH_DEST_ADDR_OFFSET = 0;
private static final int ETH_ETHERTYPE_OFFSET = 12;
private static final int ETH_HEADER_LEN = 14;
private static final int ETH_DEST_ADDR_OFFSET = 0;
private static final int ETH_ETHERTYPE_OFFSET = 12;
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_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_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 int IPV4_TCP_HEADER_FLAG_OFFSET = IPV4_TCP_HEADER_OFFSET + 13;
private static final int IPV4_TCP_HEADER_FLAG_OFFSET = IPV4_TCP_HEADER_OFFSET + 13;
private static final int IPV4_UDP_HEADER_OFFSET = ETH_HEADER_LEN + IPV4_HEADER_LEN;;
private static final int IPV4_UDP_SRC_PORT_OFFSET = IPV4_UDP_HEADER_OFFSET + 0;
private static final int IPV4_UDP_DEST_PORT_OFFSET = IPV4_UDP_HEADER_OFFSET + 2;
private static final int IPV4_UDP_LENGTH_OFFSET = IPV4_UDP_HEADER_OFFSET + 4;
private static final int IPV4_UDP_PAYLOAD_OFFSET = IPV4_UDP_HEADER_OFFSET + 8;
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;
private static final int IPV6_HEADER_LEN = 40;
private static final int IPV6_NEXT_HEADER_OFFSET = ETH_HEADER_LEN + 6;
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 =
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 };
private static final byte[] IPV6_ALL_ROUTERS_ADDRESS =
{ (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 };
private static final int ICMP6_TYPE_OFFSET = ETH_HEADER_LEN + IPV6_HEADER_LEN;
private static final int ICMP6_ROUTER_SOLICITATION = 133;
private static final int ICMP6_ROUTER_ADVERTISEMENT = 134;
private static final int ICMP6_TYPE_OFFSET = ETH_HEADER_LEN + IPV6_HEADER_LEN;
private static final int ICMP6_ROUTER_SOLICITATION = 133;
private static final int ICMP6_ROUTER_ADVERTISEMENT = 134;
private static final int ICMP6_NEIGHBOR_SOLICITATION = 135;
private static final int ICMP6_NEIGHBOR_ANNOUNCEMENT = 136;
@@ -1050,9 +1058,9 @@ public class ApfTest {
private static final int ICMP6_RA_OPTION_OFFSET =
ETH_HEADER_LEN + IPV6_HEADER_LEN + ICMP6_RA_HEADER_LEN;
private static final int ICMP6_PREFIX_OPTION_TYPE = 3;
private static final int ICMP6_PREFIX_OPTION_LEN = 32;
private static final int ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET = 4;
private static final int ICMP6_PREFIX_OPTION_TYPE = 3;
private static final int ICMP6_PREFIX_OPTION_LEN = 32;
private static final int ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET = 4;
private static final int ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_OFFSET = 8;
// From RFC6106: Recursive DNS Server option
@@ -1063,17 +1071,17 @@ public class ApfTest {
// From RFC4191: Route Information option
private static final int ICMP6_ROUTE_INFO_OPTION_TYPE = 24;
// Above three options all have the same format:
private static final int ICMP6_4_BYTE_OPTION_LEN = 8;
private static final int ICMP6_4_BYTE_OPTION_LEN = 8;
private static final int ICMP6_4_BYTE_LIFETIME_OFFSET = 4;
private static final int ICMP6_4_BYTE_LIFETIME_LEN = 4;
private static final int ICMP6_4_BYTE_LIFETIME_LEN = 4;
private static final int UDP_HEADER_LEN = 8;
private static final int UDP_HEADER_LEN = 8;
private static final int UDP_DESTINATION_PORT_OFFSET = ETH_HEADER_LEN + 22;
private static final int DHCP_CLIENT_PORT = 68;
private static final int DHCP_CLIENT_PORT = 68;
private static final int DHCP_CLIENT_MAC_OFFSET = ETH_HEADER_LEN + UDP_HEADER_LEN + 48;
private static final int ARP_HEADER_OFFSET = ETH_HEADER_LEN;
private static final int ARP_HEADER_OFFSET = ETH_HEADER_LEN;
private static final byte[] ARP_IPV4_REQUEST_HEADER = {
0, 1, // Hardware type: Ethernet (1)
8, 0, // Protocol type: IP (0x0800)
@@ -1714,6 +1722,76 @@ public class ApfTest {
return packet.array();
}
@Test
public void testApfFilterNattKeepalivePacket() 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 = 1024;
final int dstPort = 4500;
final int slot1 = 1;
// NAT-T keepalive
final byte[] payload = {(byte) 0xff};
// src: 10.0.0.5, port: 1024
// dst: 10.0.0.6, port: 4500
InetAddress srcAddr = InetAddress.getByAddress(IPV4_KEEPALIVE_SRC_ADDR);
InetAddress dstAddr = InetAddress.getByAddress(IPV4_KEEPALIVE_DST_ADDR);
final NattKeepalivePacketDataParcelable parcel = new NattKeepalivePacketDataParcelable();
parcel.srcAddress = srcAddr.getAddress();
parcel.srcPort = srcPort;
parcel.dstAddress = dstAddr.getAddress();
parcel.dstPort = dstPort;
apfFilter.addNattKeepalivePacketFilter(slot1, parcel);
program = cb.getApfProgram();
// Verify IPv4 keepalive packet is dropped
// src: 10.0.0.6, port: 4500
// dst: 10.0.0.5, port: 1024
final byte[] nattKaPkt = ipv4UdpPacket(IPV4_KEEPALIVE_DST_ADDR,
IPV4_KEEPALIVE_SRC_ADDR, dstPort, srcPort, 1 /* dataLength */);
System.arraycopy(payload, 0, nattKaPkt, IPV4_UDP_PAYLOAD_OFFSET, payload.length);
assertDrop(program, nattKaPkt);
// Verify IPv4 non-keepalive packet from the same source address is passed
assertPass(program,
ipv4UdpPacket(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR,
dstPort, srcPort, 10 /* dataLength */));
// Verify IPv4 non-keepalive packet from other source address is passed
assertPass(program,
ipv4UdpPacket(IPV4_ANOTHER_ADDR, IPV4_KEEPALIVE_SRC_ADDR,
dstPort, srcPort, 10 /* dataLength */));
apfFilter.removeKeepalivePacketFilter(slot1);
apfFilter.shutdown();
}
private static byte[] ipv4UdpPacket(byte[] sip, byte[] dip, int sport,
int dport, int dataLength) {
final int totalLength = dataLength + IPV4_HEADER_LEN + UDP_HEADER_LEN;
final int udpLength = UDP_HEADER_LEN + dataLength;
ByteBuffer packet = ByteBuffer.wrap(new byte[totalLength + ETH_HEADER_LEN]);
// ether type
packet.putShort(ETH_ETHERTYPE_OFFSET, (short) ETH_P_IP);
// IPv4 header
packet.put(IPV4_VERSION_IHL_OFFSET, (byte) 0x45);
packet.putShort(IPV4_TOTAL_LENGTH_OFFSET, (short) totalLength);
packet.put(IPV4_PROTOCOL_OFFSET, (byte) IPPROTO_UDP);
put(packet, IPV4_SRC_ADDR_OFFSET, sip);
put(packet, IPV4_DEST_ADDR_OFFSET, dip);
packet.putShort(IPV4_UDP_SRC_PORT_OFFSET, (short) sport);
packet.putShort(IPV4_UDP_DEST_PORT_OFFSET, (short) dport);
packet.putShort(IPV4_UDP_LENGTH_OFFSET, (short) udpLength);
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) {