Merge "Dropping NAT-T keepalive packet from APF"
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user