Snap for 9560500 from 55f4a26c7a4f2ba2e38c39f62bdee4c834817218 to sdk-release Change-Id: I420e81dd2c6c505fb2c77c7093a022528f992ac4
diff --git a/src/android/net/apf/ApfFilter.java b/src/android/net/apf/ApfFilter.java index 70fa673..e4c8015 100644 --- a/src/android/net/apf/ApfFilter.java +++ b/src/android/net/apf/ApfFilter.java
@@ -69,6 +69,7 @@ import com.android.net.module.util.SocketUtils; import com.android.networkstack.util.NetworkStackUtils; +import java.io.ByteArrayOutputStream; import java.io.FileDescriptor; import java.io.IOException; import java.net.Inet4Address; @@ -80,8 +81,10 @@ import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.nio.ByteOrder; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; +import java.util.List; /** * For networks that support packet filtering via APF programs, {@code ApfFilter} @@ -144,6 +147,7 @@ PASSED_ARP_UNKNOWN, PASSED_ARP_UNICAST_REPLY, PASSED_NON_IP_UNICAST, + PASSED_MDNS, DROPPED_ETH_BROADCAST, DROPPED_RA, DROPPED_GARP_REPLY, @@ -162,7 +166,8 @@ DROPPED_ARP_REPLY_SPA_NO_HOST, DROPPED_IPV4_KEEPALIVE_ACK, DROPPED_IPV6_KEEPALIVE_ACK, - DROPPED_IPV4_NATT_KEEPALIVE; + DROPPED_IPV4_NATT_KEEPALIVE, + DROPPED_MDNS; // Returns the negative byte offset from the end of the APF data segment for // a given counter. @@ -343,6 +348,22 @@ // TODO: Select a proper max length private static final int APF_MAX_ETH_TYPE_BLACK_LIST_LEN = 20; + private static final byte[] ETH_MULTICAST_MDNS_V4_MAC_ADDRESS = + {(byte) 0x01, (byte) 0x00, (byte) 0x5e, (byte) 0x00, (byte) 0x00, (byte) 0xfb}; + private static final byte[] ETH_MULTICAST_MDNS_V6_MAC_ADDRESS = + {(byte) 0x33, (byte) 0x33, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0xfb}; + private static final int MDNS_PORT = 5353; + private static final int DNS_HEADER_LEN = 12; + private static final int DNS_QDCOUNT_OFFSET = 4; + // NOTE: this must be added to the IPv4 header length in IPV4_HEADER_SIZE_MEMORY_SLOT, or the + // IPv6 header length. + private static final int MDNS_QDCOUNT_OFFSET = + ETH_HEADER_LEN + UDP_HEADER_LEN + DNS_QDCOUNT_OFFSET; + private static final int MDNS_QNAME_OFFSET = + ETH_HEADER_LEN + UDP_HEADER_LEN + DNS_HEADER_LEN; + + + private final ApfCapabilities mApfCapabilities; private final IpClientCallbacksWrapper mIpClientCallback; private final InterfaceParams mInterfaceParams; @@ -902,6 +923,18 @@ return currentLifetime() <= 0; } + // Filter for a fraction of the lifetime and adjust for the age of the RA. + @GuardedBy("ApfFilter.this") + int filterLifetime() { + return (int) (mMinLifetime / FRACTION_OF_LIFETIME_TO_FILTER) + - (int) (mProgramBaseTime - mLastSeen); + } + + @GuardedBy("ApfFilter.this") + boolean shouldFilter() { + return filterLifetime() > 0; + } + // Append a filter for this RA to {@code gen}. Jump to DROP_LABEL if it should be dropped. // Jump to the next filter if packet doesn't match this RA. // Return Long.MAX_VALUE if we don't install any filter program for this RA. As the return @@ -910,18 +943,13 @@ // filter gets generated makes sure the program lifetime stays unaffected. @GuardedBy("ApfFilter.this") long generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException { - // Filter for a fraction of the lifetime and adjust for the age of the RA. - int filterLifetime = (int) (mMinLifetime / FRACTION_OF_LIFETIME_TO_FILTER) - - (int) (mProgramBaseTime - mLastSeen); - if (filterLifetime <= 0) return Long.MAX_VALUE; - String nextFilterLabel = "Ra" + getUniqueNumberLocked(); // Skip if packet is not the right size gen.addLoadFromMemory(Register.R0, gen.PACKET_SIZE_MEMORY_SLOT); gen.addJumpIfR0NotEquals(mPacket.capacity(), nextFilterLabel); // Skip filter if expired gen.addLoadFromMemory(Register.R0, gen.FILTER_AGE_MEMORY_SLOT); - gen.addJumpIfR0GreaterThan(filterLifetime, nextFilterLabel); + gen.addJumpIfR0GreaterThan(filterLifetime(), nextFilterLabel); for (PacketSection section : mPacketSections) { // Generate code to match the packet bytes. if (section.type == PacketSection.Type.MATCH) { @@ -942,13 +970,13 @@ throw new IllegalStateException( "bogus lifetime size " + section.length); } - gen.addJumpIfR0LessThan(filterLifetime, nextFilterLabel); + gen.addJumpIfR0LessThan(filterLifetime(), nextFilterLabel); } } maybeSetupCounter(gen, Counter.DROPPED_RA); gen.addJump(mCountAndDropLabel); gen.defineLabel(nextFilterLabel); - return filterLifetime; + return filterLifetime(); } } @@ -1175,6 +1203,8 @@ private ArrayList<Ra> mRas = new ArrayList<>(); @GuardedBy("this") private SparseArray<KeepalivePacket> mKeepalivePackets = new SparseArray<>(); + @GuardedBy("this") + private final List<String[]> mMdnsAllowList = new ArrayList<>(); // 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 @@ -1493,6 +1523,124 @@ gen.defineLabel(skipUnsolicitedMulticastNALabel); } + /** Encodes qname in TLV pattern. */ + @VisibleForTesting + public static byte[] encodeQname(String[] labels) { + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + for (String label : labels) { + byte[] labelBytes = label.getBytes(StandardCharsets.UTF_8); + out.write(labelBytes.length); + out.write(labelBytes, 0, labelBytes.length); + } + out.write(0); + return out.toByteArray(); + } + + /** + * Generate filter code to process mDNS packets. Execution of this code ends in * DROP_LABEL + * or PASS_LABEL if the packet is mDNS packets. Otherwise, skip this check. + */ + @GuardedBy("this") + private void generateMdnsFilterLocked(ApfGenerator gen) + throws IllegalInstructionException { + final String skipMdnsv4Filter = "skip_mdns_v4_filter"; + final String skipMdnsFilter = "skip_mdns_filter"; + final String checkMdnsUdpPort = "check_mdns_udp_port"; + final String mDnsAcceptPacket = "mdns_accept_packet"; + final String mDnsDropPacket = "mdns_drop_packet"; + + // Only turn on the filter if multicast filter is on and the qname allowlist is non-empty. + if (!mMulticastFilter || mMdnsAllowList.isEmpty()) { + return; + } + + // Here's a basic summary of what the mDNS filter program does: + // + // if it is a multicast mDNS packet + // if QDCOUNT > 1 and the first QNAME is in the allowlist + // pass + // else: + // drop + // + // A packet is considered as a multicast mDNS packet if it matches all the following + // conditions + // 1. its destination MAC address matches 01:00:5E:00:00:FB or 33:33:00:00:00:FB, for + // v4 and v6 respectively. + // 2. it is an IPv4/IPv6 packet + // 3. it is a UDP packet with port 5353 + + // Check it's L2 mDNS multicast address. + gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET); + gen.addJumpIfBytesNotEqual(Register.R0, ETH_MULTICAST_MDNS_V4_MAC_ADDRESS, + skipMdnsv4Filter); + + // Checks it's IPv4. + gen.addLoad16(Register.R0, ETH_ETHERTYPE_OFFSET); + gen.addJumpIfR0NotEquals(ETH_P_IP, skipMdnsFilter); + + // Checks it's UDP. + gen.addLoad8(Register.R0, IPV4_PROTOCOL_OFFSET); + gen.addJumpIfR0NotEquals(IPPROTO_UDP, skipMdnsFilter); + // Set R1 to IPv4 header. + gen.addLoadFromMemory(Register.R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT); + gen.addJump(checkMdnsUdpPort); + + gen.defineLabel(skipMdnsv4Filter); + + // Checks it's L2 mDNS multicast address. + // Relies on R0 containing the ethernet destination mac address offset. + gen.addJumpIfBytesNotEqual(Register.R0, ETH_MULTICAST_MDNS_V6_MAC_ADDRESS, + skipMdnsFilter); + + // Checks it's IPv6. + gen.addLoad16(Register.R0, ETH_ETHERTYPE_OFFSET); + gen.addJumpIfR0NotEquals(ETH_P_IPV6, skipMdnsFilter); + + // Checks it's UDP. + gen.addLoad8(Register.R0, IPV6_NEXT_HEADER_OFFSET); + gen.addJumpIfR0NotEquals(IPPROTO_UDP, skipMdnsFilter); + + // Set R1 to IPv6 header. + gen.addLoadImmediate(Register.R1, IPV6_HEADER_LEN); + + // Checks it's mDNS UDP port + gen.defineLabel(checkMdnsUdpPort); + gen.addLoad16Indexed(Register.R0, UDP_DESTINATION_PORT_OFFSET); + gen.addJumpIfR0NotEquals(MDNS_PORT, skipMdnsFilter); + + // Only do the QNAME check if the QDCOUNT is more than 0. + // If there is more than one query (QDCOUNT > 1), we only matches the first QNAME. + gen.addLoad16Indexed(Register.R0, MDNS_QDCOUNT_OFFSET); + gen.addJumpIfR0Equals(0, mDnsDropPacket); + + // Load offset for the first QNAME. + gen.addLoadImmediate(Register.R0, MDNS_QNAME_OFFSET); + gen.addAddR1(); + + // Check first QNAME against allowlist + for (int i = 0; i < mMdnsAllowList.size(); ++i) { + final String mDnsNextAllowedQnameCheck = "mdns_next_allowed_qname_check" + i; + final byte[] encodedQname = encodeQname(mMdnsAllowList.get(i)); + gen.addJumpIfBytesNotEqual(Register.R0, encodedQname, mDnsNextAllowedQnameCheck); + // QNAME matched + gen.addJump(mDnsAcceptPacket); + // QNAME not matched + gen.defineLabel(mDnsNextAllowedQnameCheck); + } + // If QNAME doesn't match any entries in allowlist, drop the packet. + gen.defineLabel(mDnsDropPacket); + maybeSetupCounter(gen, Counter.DROPPED_MDNS); + gen.addJump(mCountAndDropLabel); + + gen.defineLabel(mDnsAcceptPacket); + maybeSetupCounter(gen, Counter.PASSED_MDNS); + gen.addJump(mCountAndPassLabel); + + + gen.defineLabel(skipMdnsFilter); + } + + private void generateV6KeepaliveFilters(ApfGenerator gen) throws IllegalInstructionException { generateKeepaliveFilters(gen, TcpKeepaliveAckV6.class, IPPROTO_TCP, IPV6_NEXT_HEADER_OFFSET, "skip_v6_keepalive_filter"); @@ -1567,19 +1715,20 @@ generateArpFilterLocked(gen); gen.defineLabel(skipArpFiltersLabel); + // Add mDNS filter: + generateMdnsFilterLocked(gen); + gen.addLoad16(Register.R0, ETH_ETHERTYPE_OFFSET); + // Add IPv4 filters: String skipIPv4FiltersLabel = "skipIPv4Filters"; - // NOTE: Relies on R0 containing ethertype. This is safe because if we got here, we did not - // execute the ARP filter, since that filter does not fall through, but either drops or - // passes. gen.addJumpIfR0NotEquals(ETH_P_IP, skipIPv4FiltersLabel); generateIPv4FilterLocked(gen); gen.defineLabel(skipIPv4FiltersLabel); // Check for IPv6: - // NOTE: Relies on R0 containing ethertype. This is safe because if we got here, we did not - // execute the ARP or IPv4 filters, since those filters do not fall through, but either - // drop or pass. + // NOTE: Relies on R0 containing ethertype. This is safe because if we got here, we did + // not execute the IPv4 filter, since this filter do not fall through, but either drop or + // pass. String ipv6FilterLabel = "IPv6Filters"; gen.addJumpIfR0Equals(ETH_P_IPV6, ipv6FilterLabel); @@ -1661,6 +1810,7 @@ } for (Ra ra : mRas) { + if (!ra.shouldFilter()) continue; ra.generateFilterLocked(gen); // Stop if we get too big. if (gen.programLengthOverEstimate() > maximumApfProgramSize) { @@ -1755,7 +1905,6 @@ if (VDBG) log("matched RA " + ra); // Update lifetimes. ra.mLastSeen = currentTimeSeconds(); - ra.mMinLifetime = ra.minLifetime(); ra.seenCount++; // Keep mRas in LRU order so as to prioritize generating filters for recently seen @@ -1843,6 +1992,22 @@ installNewProgramLocked(); } + /** Adds qname to the mDNS allowlist */ + public synchronized void addToMdnsAllowList(String[] labels) { + mMdnsAllowList.add(labels); + if (mMulticastFilter) { + installNewProgramLocked(); + } + } + + /** Removes qname from the mDNS allowlist */ + public synchronized void removeFromAllowList(String[] labels) { + mMdnsAllowList.removeIf(e -> Arrays.equals(labels, e)); + if (mMulticastFilter) { + installNewProgramLocked(); + } + } + @VisibleForTesting public synchronized void setDozeMode(boolean isEnabled) { if (mInDozeMode == isEnabled) return;
diff --git a/tests/integration/AndroidTest_Coverage.xml b/tests/integration/AndroidTest_Coverage.xml index 1c63a93..5e5fbfb 100644 --- a/tests/integration/AndroidTest_Coverage.xml +++ b/tests/integration/AndroidTest_Coverage.xml
@@ -14,7 +14,7 @@ limitations under the License. --> <configuration description="Runs coverage tests for NetworkStack"> - <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup"> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> <option name="test-file-name" value="NetworkStackCoverageTests.apk" /> </target_preparer>
diff --git a/tests/unit/src/android/net/apf/ApfTest.java b/tests/unit/src/android/net/apf/ApfTest.java index 0846127..ea4dbc8 100644 --- a/tests/unit/src/android/net/apf/ApfTest.java +++ b/tests/unit/src/android/net/apf/ApfTest.java
@@ -42,6 +42,7 @@ import android.net.IpPrefix; import android.net.LinkAddress; import android.net.LinkProperties; +import android.net.MacAddress; import android.net.NattKeepalivePacketDataParcelable; import android.net.TcpKeepalivePacketDataParcelable; import android.net.apf.ApfFilter.ApfConfiguration; @@ -64,9 +65,11 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.util.HexDump; +import com.android.net.module.util.DnsPacket; import com.android.net.module.util.Inet4AddressUtils; import com.android.net.module.util.InterfaceParams; import com.android.net.module.util.NetworkStackConstants; +import com.android.net.module.util.PacketBuilder; import com.android.net.module.util.SharedLog; import com.android.networkstack.apishim.NetworkInformationShimImpl; import com.android.server.networkstack.tests.R; @@ -74,6 +77,7 @@ import libcore.io.IoUtils; import libcore.io.Streams; +import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -88,6 +92,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.net.Inet4Address; +import java.net.Inet6Address; import java.net.InetAddress; import java.nio.ByteBuffer; import java.util.Arrays; @@ -1020,6 +1025,10 @@ 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 byte[] ETH_MULTICAST_MDNS_v4_MAC_ADDRESS = + {(byte) 0x01, (byte) 0x00, (byte) 0x5e, (byte) 0x00, (byte) 0x00, (byte) 0xfb}; + private static final byte[] ETH_MULTICAST_MDNS_V6_MAC_ADDRESS = + {(byte) 0x33, (byte) 0x33, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0xfb}; private static final int IP_HEADER_OFFSET = ETH_HEADER_LEN; @@ -1051,11 +1060,11 @@ private static final int IPV6_NEXT_HEADER_OFFSET = IP_HEADER_OFFSET + 6; private static final int IPV6_SRC_ADDR_OFFSET = IP_HEADER_OFFSET + 8; private static final int IPV6_DEST_ADDR_OFFSET = IP_HEADER_OFFSET + 24; - private static final int IPV6_TCP_HEADER_OFFSET = IP_HEADER_OFFSET + 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_PAYLOAD_OFFSET = IP_HEADER_OFFSET + IPV6_HEADER_LEN; + private static final int IPV6_TCP_SRC_PORT_OFFSET = IPV6_PAYLOAD_OFFSET + 0; + private static final int IPV6_TCP_DEST_PORT_OFFSET = IPV6_PAYLOAD_OFFSET + 2; + private static final int IPV6_TCP_SEQ_NUM_OFFSET = IPV6_PAYLOAD_OFFSET + 4; + private static final int IPV6_TCP_ACK_NUM_OFFSET = IPV6_PAYLOAD_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 }; @@ -1134,6 +1143,13 @@ private static final byte[] BUG_PROBE_SOURCE_ADDR1 = {0, 0, 1, 2}; private static final byte[] BUG_PROBE_SOURCE_ADDR2 = {3, 4, 0, 0}; private static final byte[] IPV4_ANY_HOST_ADDR = {0, 0, 0, 0}; + private static final byte[] IPV4_MDNS_MULTICAST_ADDR = {(byte) 224, 0, 0, (byte) 251}; + private static final byte[] IPV6_MDNS_MULTICAST_ADDR = + {(byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, (byte) 0xfb}; + private static final int DNS_HEADER_LEN = 12; + private static final int DNS_QDCOUNT_OFFSET = 4; + private static final int IPV6_UDP_DEST_PORT_OFFSET = IPV6_PAYLOAD_OFFSET + 2; + private static final int MDNS_UDP_PORT = 5353; // Helper to initialize a default apfFilter. private ApfFilter setupApfFilter( @@ -1267,6 +1283,98 @@ apfFilter.shutdown(); } + private static byte[] makeMdnsV4Packet(String qname) throws IOException { + final ByteBuffer buf = ByteBuffer.wrap(new byte[100]); + final PacketBuilder builder = new PacketBuilder(buf); + builder.writeL2Header(MacAddress.fromString("11:22:33:44:55:66"), + MacAddress.fromBytes(ETH_MULTICAST_MDNS_v4_MAC_ADDRESS), + (short) ETH_P_IP); + builder.writeIpv4Header((byte) 0 /* tos */, (short) 0 /* id */, + (short) 0 /* flagsAndFragmentOffset */, (byte) 0 /* ttl */, (byte) IPPROTO_UDP, + (Inet4Address) Inet4Address.getByAddress(IPV4_SOURCE_ADDR), + (Inet4Address) Inet4Address.getByAddress(IPV4_MDNS_MULTICAST_ADDR)); + builder.writeUdpHeader((short) MDNS_UDP_PORT, (short) MDNS_UDP_PORT); + buf.put(new DnsPacket.DnsHeader(0, 0, 1, 0).getBytes()); + buf.put(DnsPacket.DnsRecord.makeQuestion(qname, 0, 0).getBytes()); + return builder.finalizePacket().array(); + } + + private static byte[] makeMdnsV6Packet(String qname) throws IOException { + ByteBuffer buf = ByteBuffer.wrap(new byte[100]); + final PacketBuilder builder = new PacketBuilder(buf); + builder.writeL2Header(MacAddress.fromString("11:22:33:44:55:66"), + MacAddress.fromBytes(ETH_MULTICAST_MDNS_V6_MAC_ADDRESS), + (short) ETH_P_IPV6); + builder.writeIpv6Header(0x680515ca /* vtf */, (byte) IPPROTO_UDP, (short) 0 /* hopLimit */, + (Inet6Address) InetAddress.getByAddress(IPV6_ANOTHER_ADDR), + (Inet6Address) Inet6Address.getByAddress(IPV6_MDNS_MULTICAST_ADDR)); + builder.writeUdpHeader((short) MDNS_UDP_PORT, (short) MDNS_UDP_PORT); + buf.put(new DnsPacket.DnsHeader(0, 0, 1, 0).getBytes()); + buf.put(DnsPacket.DnsRecord.makeQuestion(qname, 0, 0).getBytes()); + return builder.finalizePacket().array(); + } + + @Test + public void testQnameEncoding() { + String[] qname = new String[]{"abcd", "ef", "日本"}; + byte[] encodedQname = ApfFilter.encodeQname(qname); + Assert.assertArrayEquals( + new byte[]{0x04, 0x61, 0x62, 0x63, 0x64, 0x02, 0x65, 0x66, 0x06, (byte) 0xe6, + (byte) 0x97, (byte) 0xa5, (byte) 0xe6, (byte) 0x9c, (byte) 0xac, 0x00}, + encodedQname); + } + + @Test + public void testApfFilterMdns() throws Exception { + final byte[] unicastIpv4Addr = {(byte) 192, 0, 2, 63}; + + MockIpClientCallback ipClientCallback = new MockIpClientCallback(); + LinkAddress link = new LinkAddress(InetAddress.getByAddress(unicastIpv4Addr), 24); + LinkProperties lp = new LinkProperties(); + lp.addLinkAddress(link); + + ApfConfiguration config = getDefaultConfig(); + TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipClientCallback, mLog); + apfFilter.setLinkProperties(lp); + + // Construct IPv4 mDNS packet + byte[] mdnsv4packet = makeMdnsV4Packet("test.local"); + byte[] mdnsv6packet = makeMdnsV6Packet("test.local"); + byte[] program = ipClientCallback.getApfProgram(); + // mDNSv4 packet is passed if no mDns filter is turned on + assertPass(program, mdnsv4packet); + // mDNSv6 packet is passed if no mDNS filter is turned on + assertPass(program, mdnsv6packet); + + // mDNSv4 packet with qname in the allowlist is passed + apfFilter.addToMdnsAllowList(new String[]{"test", "local"}); + apfFilter.addToMdnsAllowList(new String[]{"abcd", "local"}); + apfFilter.setMulticastFilter(true); + program = ipClientCallback.getApfProgram(); + assertPass(program, mdnsv4packet); + assertPass(program, mdnsv6packet); + + mdnsv4packet = makeMdnsV4Packet("abcd.local"); + mdnsv6packet = makeMdnsV6Packet("abcd.local"); + assertPass(program, mdnsv4packet); + assertPass(program, mdnsv6packet); + + // mDNSv4 packet with qname not in the allowlist is dropped + mdnsv4packet = makeMdnsV4Packet("ffff.local"); + mdnsv6packet = makeMdnsV6Packet("ffff.local"); + assertDrop(program, mdnsv4packet); + assertDrop(program, mdnsv6packet); + + apfFilter.removeFromAllowList(new String[]{"abcd", "local"}); + program = ipClientCallback.getApfProgram(); + mdnsv4packet = makeMdnsV4Packet("abcd.local"); + mdnsv6packet = makeMdnsV6Packet("abcd.local"); + assertDrop(program, mdnsv4packet); + assertDrop(program, mdnsv6packet); + + apfFilter.shutdown(); + } + @Test public void testApfFilterMulticast() throws Exception { final byte[] unicastIpv4Addr = {(byte)192,0,2,63};