private static final int ETH_DEST_ADDR_OFFSET = 0;
private static final int ETH_ETHERTYPE_OFFSET = 12;
private static final int ETH_TYPE_MIN = 0x0600;
+ private static final int ETH_TYPE_MAX = 0xFFFF;
private static final byte[] ETH_BROADCAST_MAC_ADDRESS =
{(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff };
// TODO: Make these offsets relative to end of link-layer header; don't include ETH_HEADER_LEN.
private static final int ARP_TARGET_IP_ADDRESS_OFFSET = ETH_HEADER_LEN + 24;
// Do not log ApfProgramEvents whose actual lifetimes was less than this.
private static final int APF_PROGRAM_EVENT_LIFETIME_THRESHOLD = 2;
+ // Limit on the Black List size to cap on program usage for this
+ // TODO: Select a proper max length
+ private static final int APF_MAX_ETH_TYPE_BLACK_LIST_LEN = 20;
private final ApfCapabilities mApfCapabilities;
private final IpManager.Callback mIpManagerCallback;
@GuardedBy("this")
private boolean mMulticastFilter;
private final boolean mDrop802_3Frames;
+ private final int[] mEthTypeBlackList;
+
// Our IPv4 address, if we have just one, otherwise null.
@GuardedBy("this")
private byte[] mIPv4Address;
@VisibleForTesting
ApfFilter(ApfCapabilities apfCapabilities, NetworkInterface networkInterface,
IpManager.Callback ipManagerCallback, boolean multicastFilter,
- boolean ieee802_3Filter, IpConnectivityLog log) {
+ boolean ieee802_3Filter, int[] ethTypeBlackList, IpConnectivityLog log) {
mApfCapabilities = apfCapabilities;
mIpManagerCallback = ipManagerCallback;
mNetworkInterface = networkInterface;
mMulticastFilter = multicastFilter;
mDrop802_3Frames = ieee802_3Filter;
+
+ // Now fill the black list from the passed array
+ mEthTypeBlackList = filterEthTypeBlackList(ethTypeBlackList);
+
mMetricsLog = log;
// TODO: ApfFilter should not generate programs until IpManager sends provisioning success.
return mUniqueCounter++;
}
+ @GuardedBy("this")
+ private static int[] filterEthTypeBlackList(int[] ethTypeBlackList) {
+ ArrayList<Integer> bl = new ArrayList<Integer>();
+
+ for (int p : ethTypeBlackList) {
+ // Check if the protocol is a valid ether type
+ if ((p < ETH_TYPE_MIN) || (p > ETH_TYPE_MAX)) {
+ continue;
+ }
+
+ // Check if the protocol is not repeated in the passed array
+ if (bl.contains(p)) {
+ continue;
+ }
+
+ // Check if list reach its max size
+ if (bl.size() == APF_MAX_ETH_TYPE_BLACK_LIST_LEN) {
+ Log.w(TAG, "Passed EthType Black List size too large (" + bl.size() +
+ ") using top " + APF_MAX_ETH_TYPE_BLACK_LIST_LEN + " protocols");
+ break;
+ }
+
+ // Now add the protocol to the list
+ bl.add(p);
+ }
+
+ return bl.stream().mapToInt(Integer::intValue).toArray();
+ }
+
/**
* Attempt to start listening for RAs and, if RAs are received, generating and installing
* filters to ignore useless RAs.
* Begin generating an APF program to:
* <ul>
* <li>Drop/Pass 802.3 frames (based on policy)
+ * <li>Drop packets with EtherType within the Black List
* <li>Drop ARP requests not for us, if mIPv4Address is set,
* <li>Drop IPv4 broadcast packets, except DHCP destined to our MAC,
* <li>Drop IPv4 multicast packets, if mMulticastFilter,
//
// if it's a 802.3 Frame (ethtype < 0x0600):
// drop or pass based on configurations
+ // if it has a ether-type that belongs to the black list
+ // drop
// if it's ARP:
// insert ARP filter to drop or pass these appropriately
// if it's IPv4:
gen.addJumpIfR0LessThan(ETH_TYPE_MIN, gen.DROP_LABEL);
}
+ // Handle ether-type black list
+ for (int p : mEthTypeBlackList) {
+ gen.addJumpIfR0Equals(p, gen.DROP_LABEL);
+ }
+
// Add ARP filters:
String skipArpFiltersLabel = "skipArpFilters";
gen.addJumpIfR0NotEquals(ETH_P_ARP, skipArpFiltersLabel);
*/
public static ApfFilter maybeCreate(ApfCapabilities apfCapabilities,
NetworkInterface networkInterface, IpManager.Callback ipManagerCallback,
- boolean multicastFilter, boolean ieee802_3Filter) {
+ boolean multicastFilter, boolean ieee802_3Filter, int[] ethTypeBlackList) {
if (apfCapabilities == null || networkInterface == null) return null;
if (apfCapabilities.apfVersionSupported == 0) return null;
if (apfCapabilities.maximumApfProgramSize < 512) {
return null;
}
return new ApfFilter(apfCapabilities, networkInterface, ipManagerCallback,
- multicastFilter, ieee802_3Filter, new IpConnectivityLog());
+ multicastFilter, ieee802_3Filter, ethTypeBlackList, new IpConnectivityLog());
}
public synchronized void shutdown() {
private final long mFixedTimeMs = SystemClock.elapsedRealtime();
public TestApfFilter(IpManager.Callback ipManagerCallback, boolean multicastFilter,
- boolean ieee802_3Filter, IpConnectivityLog log) throws Exception {
+ boolean ieee802_3Filter, int[] ethTypeBlackList,
+ IpConnectivityLog log) throws Exception {
super(new ApfCapabilities(2, 1700, ARPHRD_ETHER), NetworkInterface.getByName("lo"),
- ipManagerCallback, multicastFilter, ieee802_3Filter, log);
+ ipManagerCallback, multicastFilter, ieee802_3Filter, ethTypeBlackList, log);
}
// Pretend an RA packet has been received and show it to ApfFilter.
LinkAddress link = new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 19);
LinkProperties lp = new LinkProperties();
lp.addLinkAddress(link);
+ final int[] ethTypeBlackList = {};
ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, DROP_MULTICAST,
- ALLOW_802_3_FRAMES, mLog);
+ ALLOW_802_3_FRAMES, ethTypeBlackList, mLog);
apfFilter.setLinkProperties(lp);
byte[] program = ipManagerCallback.getApfProgram();
@SmallTest
public void testApfFilterIPv6() throws Exception {
+ final int[] ethTypeBlackList = {};
MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, ALLOW_MULTICAST,
- ALLOW_802_3_FRAMES, mLog);
+ ALLOW_802_3_FRAMES, ethTypeBlackList, mLog);
byte[] program = ipManagerCallback.getApfProgram();
// Verify empty IPv6 packet is passed
final byte[] broadcastIpv4Addr = {(byte)192,0,2,(byte)255};
final byte[] multicastIpv4Addr = {(byte)224,0,0,1};
final byte[] multicastIpv6Addr = {(byte)0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,(byte)0xfb};
+ final int[] ethTypeBlackList = {};
MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
LinkAddress link = new LinkAddress(InetAddress.getByAddress(unicastIpv4Addr), 24);
lp.addLinkAddress(link);
ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, ALLOW_MULTICAST,
- DROP_802_3_FRAMES, mLog);
+ DROP_802_3_FRAMES, ethTypeBlackList, mLog);
apfFilter.setLinkProperties(lp);
byte[] program = ipManagerCallback.getApfProgram();
ipManagerCallback.resetApfProgramWait();
apfFilter.shutdown();
apfFilter = new TestApfFilter(ipManagerCallback, DROP_MULTICAST,
- DROP_802_3_FRAMES, mLog);
+ DROP_802_3_FRAMES, ethTypeBlackList, mLog);
apfFilter.setLinkProperties(lp);
program = ipManagerCallback.getApfProgram();
assertDrop(program, mcastv4packet.array());
LinkAddress link = new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 19);
LinkProperties lp = new LinkProperties();
lp.addLinkAddress(link);
+ final int[] ethTypeBlackList = {};
ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, ALLOW_MULTICAST,
- ALLOW_802_3_FRAMES, mLog);
+ ALLOW_802_3_FRAMES, ethTypeBlackList, mLog);
apfFilter.setLinkProperties(lp);
byte[] program = ipManagerCallback.getApfProgram();
ipManagerCallback.resetApfProgramWait();
apfFilter.shutdown();
apfFilter = new TestApfFilter(ipManagerCallback, ALLOW_MULTICAST,
- DROP_802_3_FRAMES, mLog);
+ DROP_802_3_FRAMES, ethTypeBlackList, mLog);
apfFilter.setLinkProperties(lp);
program = ipManagerCallback.getApfProgram();
apfFilter.shutdown();
}
+ @SmallTest
+ public void testApfFilterEthTypeBL() throws Exception {
+ MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
+ LinkAddress link = new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 19);
+ LinkProperties lp = new LinkProperties();
+ lp.addLinkAddress(link);
+ final int[] emptyBlackList = {};
+ final int[] ipv4BlackList = {ETH_P_IP};
+ final int[] ipv4Ipv6BlackList = {ETH_P_IP, ETH_P_IPV6};
+
+ ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, ALLOW_MULTICAST,
+ ALLOW_802_3_FRAMES, emptyBlackList, mLog);
+ apfFilter.setLinkProperties(lp);
+
+ byte[] program = ipManagerCallback.getApfProgram();
+
+ // Verify empty packet of 100 zero bytes is passed
+ // Note that eth-type = 0 makes it an IEEE802.3 frame
+ ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
+ assertPass(program, packet.array());
+
+ // Verify empty packet with IPv4 is passed
+ packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
+ assertPass(program, packet.array());
+
+ // Verify empty IPv6 packet is passed
+ packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6);
+ assertPass(program, packet.array());
+
+ // Now add IPv4 to the black list
+ ipManagerCallback.resetApfProgramWait();
+ apfFilter.shutdown();
+ apfFilter = new TestApfFilter(ipManagerCallback, ALLOW_MULTICAST,
+ ALLOW_802_3_FRAMES, ipv4BlackList, mLog);
+ apfFilter.setLinkProperties(lp);
+ program = ipManagerCallback.getApfProgram();
+
+ // Verify that IPv4 frame will be dropped
+ packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
+ assertDrop(program, packet.array());
+
+ // Verify that IPv6 frame will pass
+ packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6);
+ assertPass(program, packet.array());
+
+ // Now let us have both IPv4 and IPv6 in the black list
+ ipManagerCallback.resetApfProgramWait();
+ apfFilter.shutdown();
+ apfFilter = new TestApfFilter(ipManagerCallback, ALLOW_MULTICAST,
+ ALLOW_802_3_FRAMES, ipv4Ipv6BlackList, mLog);
+ apfFilter.setLinkProperties(lp);
+ program = ipManagerCallback.getApfProgram();
+
+ // Verify that IPv4 frame will be dropped
+ packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
+ assertDrop(program, packet.array());
+
+ // Verify that IPv6 frame will be dropped
+ packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6);
+ assertDrop(program, packet.array());
+
+ apfFilter.shutdown();
+ }
+
private byte[] getProgram(MockIpManagerCallback cb, ApfFilter filter, LinkProperties lp) {
cb.resetApfProgramWait();
filter.setLinkProperties(lp);
@SmallTest
public void testApfFilterArp() throws Exception {
+ final int[] ethTypeBlackList = {};
MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, ALLOW_MULTICAST,
- DROP_802_3_FRAMES, mLog);
+ DROP_802_3_FRAMES, ethTypeBlackList, mLog);
// Verify initially ARP request filter is off, and GARP filter is on.
verifyArpFilter(ipManagerCallback.getApfProgram(), PASS);
@SmallTest
public void testApfFilterRa() throws Exception {
MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
+ final int[] ethTypeBlackList = {};
TestApfFilter apfFilter = new TestApfFilter(ipManagerCallback, DROP_MULTICAST,
- DROP_802_3_FRAMES, mLog);
+ DROP_802_3_FRAMES, ethTypeBlackList, mLog);
byte[] program = ipManagerCallback.getApfProgram();
final int ROUTER_LIFETIME = 1000;
public void testRaParsing() throws Exception {
final int maxRandomPacketSize = 512;
final Random r = new Random();
+ final int[] ethTypeBlackList = {};
MockIpManagerCallback cb = new MockIpManagerCallback();
TestApfFilter apfFilter = new TestApfFilter(cb, DROP_MULTICAST,
- DROP_802_3_FRAMES, mLog);
+ DROP_802_3_FRAMES, ethTypeBlackList, mLog);
for (int i = 0; i < 1000; i++) {
byte[] packet = new byte[r.nextInt(maxRandomPacketSize + 1)];
r.nextBytes(packet);
public void testRaProcessing() throws Exception {
final int maxRandomPacketSize = 512;
final Random r = new Random();
+ final int[] ethTypeBlackList = {};
MockIpManagerCallback cb = new MockIpManagerCallback();
TestApfFilter apfFilter = new TestApfFilter(cb, DROP_MULTICAST,
- DROP_802_3_FRAMES, mLog);
+ DROP_802_3_FRAMES, ethTypeBlackList, mLog);
for (int i = 0; i < 1000; i++) {
byte[] packet = new byte[r.nextInt(maxRandomPacketSize + 1)];
r.nextBytes(packet);