1 /*
<lambda>null2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package android.net.apf
17 
18 import android.content.Context
19 import android.net.LinkAddress
20 import android.net.LinkProperties
21 import android.net.MacAddress
22 import android.net.NattKeepalivePacketDataParcelable
23 import android.net.TcpKeepalivePacketDataParcelable
24 import android.net.apf.ApfCounterTracker.Counter.DROPPED_ARP_NON_IPV4
25 import android.net.apf.ApfCounterTracker.Counter.DROPPED_ARP_OTHER_HOST
26 import android.net.apf.ApfCounterTracker.Counter.DROPPED_ARP_REPLY_SPA_NO_HOST
27 import android.net.apf.ApfCounterTracker.Counter.DROPPED_ARP_REQUEST_REPLIED
28 import android.net.apf.ApfCounterTracker.Counter.DROPPED_ARP_UNKNOWN
29 import android.net.apf.ApfCounterTracker.Counter.DROPPED_ARP_V6_ONLY
30 import android.net.apf.ApfCounterTracker.Counter.DROPPED_ETHERTYPE_NOT_ALLOWED
31 import android.net.apf.ApfCounterTracker.Counter.DROPPED_GARP_REPLY
32 import android.net.apf.ApfCounterTracker.Counter.DROPPED_IPV4_BROADCAST_ADDR
33 import android.net.apf.ApfCounterTracker.Counter.DROPPED_IPV4_BROADCAST_NET
34 import android.net.apf.ApfCounterTracker.Counter.DROPPED_IPV4_KEEPALIVE_ACK
35 import android.net.apf.ApfCounterTracker.Counter.DROPPED_IPV4_L2_BROADCAST
36 import android.net.apf.ApfCounterTracker.Counter.DROPPED_IPV4_MULTICAST
37 import android.net.apf.ApfCounterTracker.Counter.DROPPED_IPV4_NATT_KEEPALIVE
38 import android.net.apf.ApfCounterTracker.Counter.DROPPED_IPV4_NON_DHCP4
39 import android.net.apf.ApfCounterTracker.Counter.DROPPED_IPV4_TCP_PORT7_UNICAST
40 import android.net.apf.ApfCounterTracker.Counter.DROPPED_IPV6_MULTICAST_NA
41 import android.net.apf.ApfCounterTracker.Counter.DROPPED_IPV6_NON_ICMP_MULTICAST
42 import android.net.apf.ApfCounterTracker.Counter.DROPPED_IPV6_NS_INVALID
43 import android.net.apf.ApfCounterTracker.Counter.DROPPED_IPV6_NS_OTHER_HOST
44 import android.net.apf.ApfCounterTracker.Counter.DROPPED_IPV6_NS_REPLIED_NON_DAD
45 import android.net.apf.ApfCounterTracker.Counter.PASSED_ETHER_OUR_SRC_MAC
46 import android.net.apf.ApfCounterTracker.Counter.PASSED_ARP_BROADCAST_REPLY
47 import android.net.apf.ApfCounterTracker.Counter.PASSED_ARP_REQUEST
48 import android.net.apf.ApfCounterTracker.Counter.PASSED_ARP_UNICAST_REPLY
49 import android.net.apf.ApfCounterTracker.Counter.PASSED_DHCP
50 import android.net.apf.ApfCounterTracker.Counter.PASSED_IPV4
51 import android.net.apf.ApfCounterTracker.Counter.PASSED_IPV4_FROM_DHCPV4_SERVER
52 import android.net.apf.ApfCounterTracker.Counter.PASSED_IPV4_UNICAST
53 import android.net.apf.ApfCounterTracker.Counter.PASSED_IPV6_ICMP
54 import android.net.apf.ApfCounterTracker.Counter.PASSED_IPV6_NON_ICMP
55 import android.net.apf.ApfCounterTracker.Counter.PASSED_IPV6_NS_DAD
56 import android.net.apf.ApfCounterTracker.Counter.PASSED_IPV6_NS_NO_ADDRESS
57 import android.net.apf.ApfCounterTracker.Counter.PASSED_IPV6_NS_NO_SLLA_OPTION
58 import android.net.apf.ApfCounterTracker.Counter.PASSED_IPV6_NS_TENTATIVE
59 import android.net.apf.ApfCounterTracker.Counter.PASSED_MLD
60 import android.net.apf.ApfFilter.Dependencies
61 import android.net.apf.ApfTestHelpers.Companion.TIMEOUT_MS
62 import android.net.apf.ApfTestHelpers.Companion.consumeInstalledProgram
63 import android.net.apf.ApfTestHelpers.Companion.consumeTransmittedPackets
64 import android.net.apf.ApfTestHelpers.Companion.verifyProgramRun
65 import android.net.apf.BaseApfGenerator.APF_VERSION_3
66 import android.net.apf.BaseApfGenerator.APF_VERSION_6
67 import android.net.ip.IpClient.IpClientCallbacksWrapper
68 import android.net.nsd.NsdManager
69 import android.net.nsd.OffloadEngine
70 import android.net.nsd.OffloadServiceInfo
71 import android.os.Build
72 import android.os.Handler
73 import android.os.HandlerThread
74 import android.os.SystemClock
75 import android.system.Os
76 import android.system.OsConstants.AF_UNIX
77 import android.system.OsConstants.IFA_F_TENTATIVE
78 import android.system.OsConstants.SOCK_STREAM
79 import androidx.test.filters.SmallTest
80 import com.android.internal.annotations.GuardedBy
81 import com.android.net.module.util.HexDump
82 import com.android.net.module.util.InterfaceParams
83 import com.android.net.module.util.NetworkStackConstants.ARP_ETHER_IPV4_LEN
84 import com.android.net.module.util.NetworkStackConstants.ARP_REPLY
85 import com.android.net.module.util.NetworkStackConstants.ARP_REQUEST
86 import com.android.net.module.util.NetworkStackConstants.ETHER_HEADER_LEN
87 import com.android.net.module.util.NetworkStackConstants.ICMPV6_NA_HEADER_LEN
88 import com.android.net.module.util.NetworkStackConstants.ICMPV6_NS_HEADER_LEN
89 import com.android.net.module.util.NetworkStackConstants.IPV6_HEADER_LEN
90 import com.android.net.module.util.arp.ArpPacket
91 import com.android.networkstack.metrics.NetworkQuirkMetrics
92 import com.android.networkstack.packets.NeighborAdvertisement
93 import com.android.networkstack.packets.NeighborSolicitation
94 import com.android.networkstack.util.NetworkStackUtils
95 import com.android.testutils.DevSdkIgnoreRule
96 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
97 import com.android.testutils.DevSdkIgnoreRunner
98 import com.android.testutils.quitResources
99 import com.android.testutils.waitForIdle
100 import java.io.FileDescriptor
101 import java.net.Inet4Address
102 import java.net.Inet6Address
103 import java.net.InetAddress
104 import kotlin.test.assertContentEquals
105 import kotlin.test.assertEquals
106 import libcore.io.IoUtils
107 import org.junit.After
108 import org.junit.Before
109 import org.junit.Rule
110 import org.junit.Test
111 import org.junit.runner.RunWith
112 import org.mockito.ArgumentCaptor
113 import org.mockito.ArgumentMatchers.any
114 import org.mockito.ArgumentMatchers.anyInt
115 import org.mockito.ArgumentMatchers.anyLong
116 import org.mockito.ArgumentMatchers.eq
117 import org.mockito.Mock
118 import org.mockito.Mockito
119 import org.mockito.Mockito.doAnswer
120 import org.mockito.Mockito.doReturn
121 import org.mockito.Mockito.never
122 import org.mockito.Mockito.times
123 import org.mockito.Mockito.verify
124 import org.mockito.MockitoAnnotations
125 import org.mockito.invocation.InvocationOnMock
126 
127 /**
128  * Test for APF filter.
129  */
130 @DevSdkIgnoreRunner.MonitorThreadLeak
131 @RunWith(DevSdkIgnoreRunner::class)
132 @SmallTest
133 class ApfFilterTest {
134     companion object {
135         private const val THREAD_QUIT_MAX_RETRY_COUNT = 3
136         private const val TAG = "ApfFilterTest"
137     }
138 
139     @get:Rule
140     val ignoreRule = DevSdkIgnoreRule()
141 
142     @Mock
143     private lateinit var context: Context
144 
145     @Mock private lateinit var metrics: NetworkQuirkMetrics
146 
147     @Mock private lateinit var dependencies: Dependencies
148 
149     @Mock private lateinit var ipClientCallback: IpClientCallbacksWrapper
150     @Mock private lateinit var nsdManager: NsdManager
151 
152     @GuardedBy("mApfFilterCreated")
153     private val mApfFilterCreated = ArrayList<AndroidPacketFilter>()
154     private val loInterfaceParams = InterfaceParams.getByName("lo")
155     private val ifParams =
156         InterfaceParams(
157             "lo",
158             loInterfaceParams.index,
159             MacAddress.fromBytes(byteArrayOf(2, 3, 4, 5, 6, 7)),
160             loInterfaceParams.defaultMtu
161         )
162     private val hostIpv4Address = byteArrayOf(10, 0, 0, 1)
163     private val senderIpv4Address = byteArrayOf(10, 0, 0, 2)
164     private val arpBroadcastMacAddress = intArrayOf(0xff, 0xff, 0xff, 0xff, 0xff, 0xff)
165         .map { it.toByte() }.toByteArray()
166     private val senderMacAddress = intArrayOf(0x02, 0x22, 0x33, 0x44, 0x55, 0x66)
167         .map { it.toByte() }.toByteArray()
168     private val senderIpv6Address =
169         // 2001::200:1a:1122:3344
170         intArrayOf(0x20, 0x01, 0, 0, 0, 0, 0, 0, 0x02, 0, 0, 0x1a, 0x11, 0x22, 0x33, 0x44)
171             .map{ it.toByte() }.toByteArray()
172     private val hostIpv6Addresses = listOf(
173         // 2001::200:1a:3344:1122
174         intArrayOf(0x20, 0x01, 0, 0, 0, 0, 0, 0, 0x02, 0, 0, 0x1a, 0x33, 0x44, 0x11, 0x22)
175             .map{ it.toByte() }.toByteArray(),
176         // 2001::100:1b:4455:6677
177         intArrayOf(0x20, 0x01, 0, 0, 0, 0, 0, 0, 0x01, 0, 0, 0x1b, 0x44, 0x55, 0x66, 0x77)
178             .map{ it.toByte() }.toByteArray()
179     )
180     private val hostIpv6TentativeAddresses = listOf(
181         // 2001::200:1a:1234:5678
182         intArrayOf(0x20, 0x01, 0, 0, 0, 0, 0, 0, 0x02, 0, 0, 0x1a, 0x12, 0x34, 0x56, 0x78)
183             .map{ it.toByte() }.toByteArray(),
184         // 2001::100:1b:1234:5678
185         intArrayOf(0x20, 0x01, 0, 0, 0, 0, 0, 0, 0x01, 0, 0, 0x1b, 0x12, 0x34, 0x56, 0x78)
186             .map{ it.toByte() }.toByteArray()
187     )
188     private val hostAnycast6Addresses = listOf(
189         // 2001::100:1b:aabb:ccdd
190         intArrayOf(0x20, 0x01, 0, 0, 0, 0, 0, 0, 0x01, 0, 0, 0x1b, 0xaa, 0xbb, 0xcc, 0xdd)
191             .map{ it.toByte() }.toByteArray()
192     )
193     private val hostMulticastMacAddresses = listOf(
194         // 33:33:00:00:00:01
195         intArrayOf(0x33, 0x33, 0, 0, 0, 1).map { it.toByte() }.toByteArray(),
196         // 33:33:ff:44:11:22
197         intArrayOf(0x33, 0x33, 0xff, 0x44, 0x11, 0x22).map { it.toByte() }.toByteArray(),
198         // 33:33:ff:55:66:77
199         intArrayOf(0x33, 0x33, 0xff, 0x55, 0x66, 0x77).map { it.toByte() }.toByteArray(),
200         // 33:33:ff:bb:cc:dd
201         intArrayOf(0x33, 0x33, 0xff, 0xbb, 0xcc, 0xdd).map { it.toByte() }.toByteArray(),
202     )
203 
204     private val handlerThread by lazy {
205         HandlerThread("$TAG handler thread").apply { start() }
206     }
207     private val handler by lazy { Handler(handlerThread.looper) }
208     private var writerSocket = FileDescriptor()
209 
210     @Before
211     fun setUp() {
212         MockitoAnnotations.initMocks(this)
213         // mock anycast6 address from /proc/net/anycast6
214         doReturn(hostAnycast6Addresses).`when`(dependencies).getAnycast6Addresses(any())
215 
216         // mock ether multicast mac address from /proc/net/dev_mcast
217         doReturn(hostMulticastMacAddresses).`when`(dependencies).getEtherMulticastAddresses(any())
218 
219         // mock nd traffic class from /proc/sys/net/ipv6/conf/{ifname}/ndisc_tclass
220         doReturn(0).`when`(dependencies).getNdTrafficClass(any())
221         doAnswer { invocation: InvocationOnMock ->
222             synchronized(mApfFilterCreated) {
223                 mApfFilterCreated.add(invocation.getArgument(0))
224             }
225         }.`when`(dependencies).onApfFilterCreated(any())
226         doReturn(SystemClock.elapsedRealtime()).`when`(dependencies).elapsedRealtime()
227         val readSocket = FileDescriptor()
228         Os.socketpair(AF_UNIX, SOCK_STREAM, 0, writerSocket, readSocket)
229         doReturn(readSocket).`when`(dependencies).createPacketReaderSocket(anyInt())
230         doReturn(nsdManager).`when`(context).getSystemService(NsdManager::class.java)
231     }
232 
233     private fun shutdownApfFilters() {
234         quitResources(THREAD_QUIT_MAX_RETRY_COUNT, {
235             synchronized(mApfFilterCreated) {
236                 val ret = ArrayList(mApfFilterCreated)
237                 mApfFilterCreated.clear()
238                 return@quitResources ret
239             }
240         }, { apf: AndroidPacketFilter ->
241             handler.post { apf.shutdown() }
242         })
243 
244         synchronized(mApfFilterCreated) {
245             assertEquals(
246                 0,
247                 mApfFilterCreated.size.toLong(),
248                 "ApfFilters did not fully shutdown."
249             )
250         }
251     }
252 
253     @After
254     fun tearDown() {
255         IoUtils.closeQuietly(writerSocket)
256         shutdownApfFilters()
257         handler.waitForIdle(TIMEOUT_MS)
258         Mockito.framework().clearInlineMocks()
259         ApfJniUtils.resetTransmittedPacketMemory()
260         handlerThread.quitSafely()
261         handlerThread.join()
262     }
263 
264     private fun getDefaultConfig(apfVersion: Int = APF_VERSION_6): ApfFilter.ApfConfiguration {
265         val config = ApfFilter.ApfConfiguration()
266         config.apfVersionSupported = apfVersion
267         // 4K is the highly recommended value in APFv6 for vendor
268         config.apfRamSize = 4096
269         config.multicastFilter = false
270         config.ieee802_3Filter = false
271         config.ethTypeBlackList = IntArray(0)
272         config.shouldHandleArpOffload = true
273         config.shouldHandleNdOffload = true
274         return config
275     }
276 
277     private fun getApfFilter(
278             apfCfg: ApfFilter.ApfConfiguration = getDefaultConfig(APF_VERSION_6)
279     ): ApfFilter {
280         lateinit var apfFilter: ApfFilter
281         handler.post {
282             apfFilter = ApfFilter(
283                     handler,
284                     context,
285                     apfCfg,
286                     ifParams,
287                     ipClientCallback,
288                     metrics,
289                     dependencies
290             )
291         }
292         handlerThread.waitForIdle(TIMEOUT_MS)
293         return apfFilter
294     }
295 
296     private fun doTestEtherTypeAllowListFilter(apfFilter: ApfFilter) {
297         val program = consumeInstalledProgram(ipClientCallback, installCnt = 2)
298 
299         // Using scapy to generate IPv4 mDNS packet:
300         //   eth = Ether(src="E8:9F:80:66:60:BB", dst="01:00:5E:00:00:FB")
301         //   ip = IP(src="192.168.1.1")
302         //   udp = UDP(sport=5353, dport=5353)
303         //   dns = DNS(qd=DNSQR(qtype="PTR", qname="a.local"))
304         //   p = eth/ip/udp/dns
305         val mdnsPkt = """
306             01005e0000fbe89f806660bb080045000035000100004011d812c0a80101e00000f
307             b14e914e900214d970000010000010000000000000161056c6f63616c00000c0001
308         """.replace("\\s+".toRegex(), "").trim()
309         verifyProgramRun(
310             apfFilter.mApfVersionSupported,
311             program,
312             HexDump.hexStringToByteArray(mdnsPkt),
313             PASSED_IPV4
314         )
315 
316         // Using scapy to generate RA packet:
317         //  eth = Ether(src="E8:9F:80:66:60:BB", dst="33:33:00:00:00:01")
318         //  ip6 = IPv6(src="fe80::1", dst="ff02::1")
319         //  icmp6 = ICMPv6ND_RA(routerlifetime=3600, retranstimer=3600)
320         //  p = eth/ip6/icmp6
321         val raPkt = """
322             333300000001e89f806660bb86dd6000000000103afffe800000000000000000000000
323             000001ff0200000000000000000000000000018600600700080e100000000000000e10
324         """.replace("\\s+".toRegex(), "").trim()
325         verifyProgramRun(
326             apfFilter.mApfVersionSupported,
327             program,
328             HexDump.hexStringToByteArray(raPkt),
329             PASSED_IPV6_ICMP
330         )
331 
332         // Using scapy to generate ethernet packet with type 0x88A2:
333         //  p = Ether(type=0x88A2)/Raw(load="01")
334         val ethPkt = "ffffffffffff047bcb463fb588a23031"
335         verifyProgramRun(
336             apfFilter.mApfVersionSupported,
337             program,
338             HexDump.hexStringToByteArray(ethPkt),
339             DROPPED_ETHERTYPE_NOT_ALLOWED
340         )
341     }
342 
343     private fun generateNsPacket(
344         srcMac: ByteArray,
345         dstMac: ByteArray,
346         srcIp: ByteArray,
347         dstIp: ByteArray,
348         target: ByteArray,
349     ): ByteArray {
350         val nsPacketBuf = NeighborSolicitation.build(
351             MacAddress.fromBytes(srcMac),
352             MacAddress.fromBytes(dstMac),
353             InetAddress.getByAddress(srcIp) as Inet6Address,
354             InetAddress.getByAddress(dstIp) as Inet6Address,
355             InetAddress.getByAddress(target) as Inet6Address
356         )
357 
358         val nsPacket = ByteArray(
359             ETHER_HEADER_LEN + IPV6_HEADER_LEN + ICMPV6_NS_HEADER_LEN + 8 // option length
360         )
361         nsPacketBuf.get(nsPacket)
362         return nsPacket
363     }
364 
365     private fun generateNaPacket(
366         srcMac: ByteArray,
367         dstMac: ByteArray,
368         srcIp: ByteArray,
369         dstIp: ByteArray,
370         flags: Int,
371         target: ByteArray,
372     ): ByteArray {
373         val naPacketBuf = NeighborAdvertisement.build(
374             MacAddress.fromBytes(srcMac),
375             MacAddress.fromBytes(dstMac),
376             InetAddress.getByAddress(srcIp) as Inet6Address,
377             InetAddress.getByAddress(dstIp) as Inet6Address,
378             flags,
379             InetAddress.getByAddress(target) as Inet6Address
380         )
381         val naPacket = ByteArray(
382             ETHER_HEADER_LEN + IPV6_HEADER_LEN + ICMPV6_NA_HEADER_LEN + 8 // lla option length
383         )
384 
385         naPacketBuf.get(naPacket)
386         return naPacket
387     }
388 
389     @Test
390     @IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
391     fun testV4EtherTypeAllowListFilter() {
392         val apfFilter = getApfFilter(getDefaultConfig(APF_VERSION_3))
393         doTestEtherTypeAllowListFilter(apfFilter)
394     }
395 
396     @Test
397     @IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
398     fun testV6EtherTypeAllowListFilter() {
399         val apfFilter = getApfFilter(getDefaultConfig(APF_VERSION_6))
400         doTestEtherTypeAllowListFilter(apfFilter)
401     }
402 
403     @Test
404     fun testIPv4PacketFilterOnV6OnlyNetwork() {
405         val apfFilter = getApfFilter()
406         apfFilter.updateClatInterfaceState(true)
407         val program = consumeInstalledProgram(ipClientCallback, installCnt = 3)
408 
409         // Using scapy to generate IPv4 mDNS packet:
410         //   eth = Ether(src="E8:9F:80:66:60:BB", dst="01:00:5E:00:00:FB")
411         //   ip = IP(src="192.168.1.1")
412         //   udp = UDP(sport=5353, dport=5353)
413         //   dns = DNS(qd=DNSQR(qtype="PTR", qname="a.local"))
414         //   p = eth/ip/udp/dns
415         val mdnsPkt = """
416             01005e0000fbe89f806660bb080045000035000100004011d812c0a80101e00000f
417             b14e914e900214d970000010000010000000000000161056c6f63616c00000c0001
418         """.replace("\\s+".toRegex(), "").trim()
419         verifyProgramRun(
420             apfFilter.mApfVersionSupported,
421             program,
422             HexDump.hexStringToByteArray(mdnsPkt),
423             DROPPED_IPV4_NON_DHCP4
424         )
425 
426         // Using scapy to generate non UDP protocol packet:
427         //   ether = Ether(src='00:11:22:33:44:55', dst='ff:ff:ff:ff:ff:ff')
428         //   ip = IP(src='192.168.1.1', dst='255.255.255.255', proto=12)
429         //   pkt = ether/ip
430         val nonUdpPkt = """
431             ffffffffffff00112233445508004500001400010000400cb934c0a80101ffffffff
432         """.replace("\\s+".toRegex(), "").trim()
433         verifyProgramRun(
434             apfFilter.mApfVersionSupported,
435             program,
436             HexDump.hexStringToByteArray(nonUdpPkt),
437             DROPPED_IPV4_NON_DHCP4
438         )
439 
440         // Using scapy to generate fragmented UDP protocol packet:
441         //   ether = Ether(src='00:11:22:33:44:55', dst='ff:ff:ff:ff:ff:ff')
442         //   ip = IP(src='192.168.1.1', dst='255.255.255.255', flags=1, frag=10, proto=17)
443         //   pkt = ether/ip
444         val fragmentUdpPkt = """
445             ffffffffffff0011223344550800450000140001200a40119925c0a80101ffffffff
446         """.replace("\\s+".toRegex(), "").trim()
447         verifyProgramRun(
448             apfFilter.mApfVersionSupported,
449             program,
450             HexDump.hexStringToByteArray(fragmentUdpPkt),
451             DROPPED_IPV4_NON_DHCP4
452         )
453 
454         // Using scapy to generate destination port is not DHCP client port packet:
455         //   ether = Ether(src='00:11:22:33:44:55', dst='ff:ff:ff:ff:ff:ff')
456         //   ip = IP(src='192.168.1.1', dst='255.255.255.255')
457         //   udp = UDP(dport=70)
458         //   pkt = ether/ip/udp
459         val nonDhcpServerPkt = """
460             ffffffffffff00112233445508004500001c000100004011b927c0a80101ffffffff0035004600083dba
461         """.replace("\\s+".toRegex(), "").trim()
462         verifyProgramRun(
463             apfFilter.mApfVersionSupported,
464             program,
465             HexDump.hexStringToByteArray(nonDhcpServerPkt),
466             DROPPED_IPV4_NON_DHCP4
467         )
468 
469         // Using scapy to generate DHCP4 offer packet:
470         //   ether = Ether(src='00:11:22:33:44:55', dst='ff:ff:ff:ff:ff:ff')
471         //   ip = IP(src='192.168.1.1', dst='255.255.255.255')
472         //   udp = UDP(sport=67, dport=68)
473         //   bootp = BOOTP(op=2,
474         //                 yiaddr='192.168.1.100',
475         //                 siaddr='192.168.1.1',
476         //                 chaddr=b'\x00\x11\x22\x33\x44\x55')
477         //   dhcp_options = [('message-type', 'offer'),
478         //                   ('server_id', '192.168.1.1'),
479         //                   ('subnet_mask', '255.255.255.0'),
480         //                   ('router', '192.168.1.1'),
481         //                   ('lease_time', 86400),
482         //                   ('name_server', '8.8.8.8'),
483         //                   'end']
484         //   dhcp = DHCP(options=dhcp_options)
485         //   dhcp_offer_packet = ether/ip/udp/bootp/dhcp
486         val dhcp4Pkt = """
487             ffffffffffff00112233445508004500012e000100004011b815c0a80101ffffffff0043
488             0044011a5ffc02010600000000000000000000000000c0a80164c0a80101000000000011
489             223344550000000000000000000000000000000000000000000000000000000000000000
490             000000000000000000000000000000000000000000000000000000000000000000000000
491             000000000000000000000000000000000000000000000000000000000000000000000000
492             000000000000000000000000000000000000000000000000000000000000000000000000
493             000000000000000000000000000000000000000000000000000000000000000000000000
494             0000000000000000000000000000000000000000000000000000638253633501023604c0
495             a801010104ffffff000304c0a80101330400015180060408080808ff
496         """.replace("\\s+".toRegex(), "").trim()
497         verifyProgramRun(
498             apfFilter.mApfVersionSupported,
499             program,
500             HexDump.hexStringToByteArray(dhcp4Pkt),
501             PASSED_IPV4_FROM_DHCPV4_SERVER
502         )
503 
504         // Duplicate of dhcp4Pkt with DF flag set.
505         val dhcp4PktDf = """
506             ffffffffffff00112233445508004500012e000140004011b815c0a80101ffffffff0043
507             0044011a5ffc02010600000000000000000000000000c0a80164c0a80101000000000011
508             223344550000000000000000000000000000000000000000000000000000000000000000
509             000000000000000000000000000000000000000000000000000000000000000000000000
510             000000000000000000000000000000000000000000000000000000000000000000000000
511             000000000000000000000000000000000000000000000000000000000000000000000000
512             000000000000000000000000000000000000000000000000000000000000000000000000
513             0000000000000000000000000000000000000000000000000000638253633501023604c0
514             a801010104ffffff000304c0a80101330400015180060408080808ff
515         """.replace("\\s+".toRegex(), "").trim()
516         verifyProgramRun(
517             apfFilter.mApfVersionSupported,
518             program,
519             HexDump.hexStringToByteArray(dhcp4PktDf),
520             PASSED_IPV4_FROM_DHCPV4_SERVER
521         )
522 
523         // Using scapy to generate DHCP4 offer packet:
524         //   eth = Ether(src="E8:9F:80:66:60:BB", dst="01:00:5E:00:00:FB")
525         //   ip = IP(src="192.168.1.10", dst="192.168.1.20")  # IPv4
526         //   udp = UDP(sport=12345, dport=53)
527         //   dns = DNS(qd=DNSQR(qtype="PTR", qname="a.local"))
528         //   pkt = eth / ip / udp / dns
529         //   fragments = fragment(pkt, fragsize=30)
530         //   fragments[1]
531         val fragmentedUdpPkt = """
532             01005e0000fbe89f806660bb08004500001d000100034011f75dc0a8010ac0a8
533             01146f63616c00000c0001
534         """.replace("\\s+".toRegex(), "").trim()
535         verifyProgramRun(
536             apfFilter.mApfVersionSupported,
537             program,
538             HexDump.hexStringToByteArray(fragmentedUdpPkt),
539             DROPPED_IPV4_NON_DHCP4
540         )
541     }
542 
543     @Test
544     fun testLoopbackFilter() {
545         val apfConfig = getDefaultConfig()
546         val apfFilter = getApfFilter(apfConfig)
547         val program = consumeInstalledProgram(ipClientCallback, installCnt = 2)
548         // Using scapy to generate echo-ed broadcast packet:
549         //   ether = Ether(src=${ifParams.macAddr}, dst='ff:ff:ff:ff:ff:ff')
550         //   ip = IP(src='192.168.1.1', dst='255.255.255.255', proto=21)
551         //   pkt = ether/ip
552         val nonDhcpBcastPkt = """
553             ffffffffffff020304050607080045000014000100004015b92bc0a80101ffffffff
554         """.replace("\\s+".toRegex(), "").trim()
555         verifyProgramRun(
556                 apfFilter.mApfVersionSupported,
557                 program,
558                 HexDump.hexStringToByteArray(nonDhcpBcastPkt),
559                 PASSED_ETHER_OUR_SRC_MAC
560         )
561     }
562 
563     @Test
564     fun testIPv4MulticastPacketFilter() {
565         val apfConfig = getDefaultConfig()
566         apfConfig.multicastFilter = true
567         val apfFilter = getApfFilter(apfConfig)
568         consumeInstalledProgram(ipClientCallback, installCnt = 2)
569         val linkAddress = LinkAddress(InetAddress.getByAddress(hostIpv4Address), 24)
570         val lp = LinkProperties()
571         lp.addLinkAddress(linkAddress)
572         apfFilter.setLinkProperties(lp)
573         val program = consumeInstalledProgram(ipClientCallback, installCnt = 1)
574 
575         // Using scapy to generate DHCP4 offer packet:
576         //   ether = Ether(src='00:11:22:33:44:55', dst='ff:ff:ff:ff:ff:ff')
577         //   ip = IP(src='192.168.1.1', dst='255.255.255.255')
578         //   udp = UDP(sport=67, dport=68)
579         //   bootp = BOOTP(op=2,
580         //                 yiaddr='192.168.1.100',
581         //                 siaddr='192.168.1.1',
582         //                 chaddr=b'\x02\x03\x04\x05\x06\x07')
583         //   dhcp_options = [('message-type', 'offer'),
584         //                   ('server_id', '192.168.1.1'),
585         //                   ('subnet_mask', '255.255.255.0'),
586         //                   ('router', '192.168.1.1'),
587         //                   ('lease_time', 86400),
588         //                   ('name_server', '8.8.8.8'),
589         //                   'end']
590         //   dhcp = DHCP(options=dhcp_options)
591         //   dhcp_offer_packet = ether/ip/udp/bootp/dhcp
592         val dhcp4Pkt = """
593             ffffffffffff00112233445508004500012e000100004011b815c0a80101ffffffff0043
594             0044011a5ffc02010600000000000000000000000000c0a80164c0a80101000000000203
595             040506070000000000000000000000000000000000000000000000000000000000000000
596             000000000000000000000000000000000000000000000000000000000000000000000000
597             000000000000000000000000000000000000000000000000000000000000000000000000
598             000000000000000000000000000000000000000000000000000000000000000000000000
599             000000000000000000000000000000000000000000000000000000000000000000000000
600             0000000000000000000000000000000000000000000000000000638253633501023604c0
601             a801010104ffffff000304c0a80101330400015180060408080808ff
602         """.replace("\\s+".toRegex(), "").trim()
603         verifyProgramRun(
604             apfFilter.mApfVersionSupported,
605             program,
606             HexDump.hexStringToByteArray(dhcp4Pkt),
607             PASSED_DHCP
608         )
609 
610         // Using scapy to generate non DHCP multicast packet:
611         //   ether = Ether(src='00:11:22:33:44:55', dst='ff:ff:ff:ff:ff:ff')
612         //   ip = IP(src='192.168.1.1', dst='224.0.0.1', proto=21)
613         //   pkt = ether/ip
614         val nonDhcpMcastPkt = """
615             ffffffffffff001122334455080045000014000100004015d929c0a80101e0000001
616         """.replace("\\s+".toRegex(), "").trim()
617         verifyProgramRun(
618             apfFilter.mApfVersionSupported,
619             program,
620             HexDump.hexStringToByteArray(nonDhcpMcastPkt),
621             DROPPED_IPV4_MULTICAST
622         )
623 
624         // Using scapy to generate non DHCP broadcast packet:
625         //   ether = Ether(src='00:11:22:33:44:55', dst='ff:ff:ff:ff:ff:ff')
626         //   ip = IP(src='192.168.1.1', dst='255.255.255.255', proto=21)
627         //   pkt = ether/ip
628         val nonDhcpBcastPkt = """
629             ffffffffffff001122334455080045000014000100004015b92bc0a80101ffffffff
630         """.replace("\\s+".toRegex(), "").trim()
631         verifyProgramRun(
632             apfFilter.mApfVersionSupported,
633             program,
634             HexDump.hexStringToByteArray(nonDhcpBcastPkt),
635             DROPPED_IPV4_BROADCAST_ADDR
636         )
637 
638         // Using scapy to generate non DHCP subnet broadcast packet:
639         //   ether = Ether(src='00:11:22:33:44:55', dst='ff:ff:ff:ff:ff:ff')
640         //   ip = IP(src='192.168.1.1', dst='10.0.0.255', proto=21)
641         //   pkt = ether/ip
642         val nonDhcpNetBcastPkt = """
643             ffffffffffff001122334455080045000014000100004015ae2cc0a801010a0000ff
644         """.replace("\\s+".toRegex(), "").trim()
645         verifyProgramRun(
646             apfFilter.mApfVersionSupported,
647             program,
648             HexDump.hexStringToByteArray(nonDhcpNetBcastPkt),
649             DROPPED_IPV4_BROADCAST_NET
650         )
651 
652         // Using scapy to generate non DHCP unicast packet:
653         //   ether = Ether(src='00:11:22:33:44:55', dst='02:03:04:05:06:07')
654         //   ip = IP(src='192.168.1.1', dst='192.168.1.2', proto=21)
655         //   pkt = ether/ip
656         val nonDhcpUcastPkt = """
657             020304050607001122334455080045000014000100004015f780c0a80101c0a80102
658         """.replace("\\s+".toRegex(), "").trim()
659         verifyProgramRun(
660             apfFilter.mApfVersionSupported,
661             program,
662             HexDump.hexStringToByteArray(nonDhcpUcastPkt),
663             PASSED_IPV4_UNICAST
664         )
665 
666         // Using scapy to generate non DHCP unicast packet with broadcast ether destination:
667         //   ether = Ether(src='00:11:22:33:44:55', dst='ff:ff:ff:ff:ff:ff')
668         //   ip = IP(src='192.168.1.1', dst='192.168.1.2', proto=21)
669         //   pkt = ether/ip
670         val nonDhcpUcastL2BcastPkt = """
671             ffffffffffff001122334455080045000014000100004015f780c0a80101c0a80102
672         """.replace("\\s+".toRegex(), "").trim()
673         verifyProgramRun(
674             apfFilter.mApfVersionSupported,
675             program,
676             HexDump.hexStringToByteArray(nonDhcpUcastL2BcastPkt),
677             DROPPED_IPV4_L2_BROADCAST
678         )
679     }
680 
681     @Test
682     fun testArpFilterDropPktsOnV6OnlyNetwork() {
683         val apfFilter = getApfFilter()
684         consumeInstalledProgram(ipClientCallback, installCnt = 2)
685         apfFilter.updateClatInterfaceState(true)
686         val program = consumeInstalledProgram(ipClientCallback, installCnt = 1)
687 
688         // Drop ARP request packet when clat is enabled
689         // Using scapy to generate ARP request packet:
690         // eth = Ether(src="00:01:02:03:04:05", dst="01:02:03:04:05:06")
691         // arp = ARP()
692         // pkt = eth/arp
693         val arpPkt = """
694             010203040506000102030405080600010800060400015c857e3c74e1c0a8012200000000000000000000
695         """.replace("\\s+".toRegex(), "").trim()
696         verifyProgramRun(
697             APF_VERSION_6,
698             program,
699             HexDump.hexStringToByteArray(arpPkt),
700             DROPPED_ARP_V6_ONLY
701         )
702     }
703 
704     @Test
705     fun testIPv4TcpKeepaliveFilter() {
706         val srcAddr = byteArrayOf(10, 0, 0, 5)
707         val dstAddr = byteArrayOf(10, 0, 0, 6)
708         val srcPort = 12345
709         val dstPort = 54321
710         val seqNum = 2123456789
711         val ackNum = 1234567890
712 
713         // src: 10.0.0.5:12345
714         // dst: 10.0.0.6:54321
715         val parcel = TcpKeepalivePacketDataParcelable()
716         parcel.srcAddress = InetAddress.getByAddress(srcAddr).address
717         parcel.srcPort = srcPort
718         parcel.dstAddress = InetAddress.getByAddress(dstAddr).address
719         parcel.dstPort = dstPort
720         parcel.seq = seqNum
721         parcel.ack = ackNum
722 
723         val apfConfig = getDefaultConfig()
724         apfConfig.multicastFilter = true
725         apfConfig.ieee802_3Filter = true
726         val apfFilter = getApfFilter(apfConfig)
727         consumeInstalledProgram(ipClientCallback, installCnt = 2)
728         apfFilter.addTcpKeepalivePacketFilter(1, parcel)
729         var program = consumeInstalledProgram(ipClientCallback, installCnt = 1)
730 
731         // Drop IPv4 keepalive ack
732         // Using scapy to generate IPv4 TCP keepalive ack packet with seq + 1:
733         // eth = Ether(src="00:01:02:03:04:05", dst="01:02:03:04:05:06")
734         // ip = IP(src='10.0.0.6', dst='10.0.0.5')
735         // tcp = TCP(sport=54321, dport=12345, flags="A", seq=1234567890, ack=2123456790)
736         // pkt = eth/ip/tcp
737         val keepaliveAckPkt = """
738             01020304050600010203040508004500002800010000400666c50a0000060a000005d4313039499602d2
739             7e916116501020004b4f0000
740         """.replace("\\s+".toRegex(), "").trim()
741         verifyProgramRun(
742             APF_VERSION_6,
743             program,
744             HexDump.hexStringToByteArray(keepaliveAckPkt),
745             DROPPED_IPV4_KEEPALIVE_ACK
746         )
747 
748         // Pass IPv4 non-keepalive ack from the same source address
749         // Using scapy to generate IPv4 TCP non-keepalive ack from the same source address:
750         // eth = Ether(src="00:01:02:03:04:05", dst="01:02:03:04:05:06")
751         // ip = IP(src='10.0.0.6', dst='10.0.0.5')
752         // tcp = TCP(sport=54321, dport=12345, flags="A", seq=1234567990, ack=2123456789)
753         // pkt = eth/ip/tcp
754         val nonKeepaliveAckPkt1 = """
755             01020304050600010203040508004500002800010000400666c50a0000060a000005d431303949960336
756             7e916115501020004aec0000
757         """.replace("\\s+".toRegex(), "").trim()
758         verifyProgramRun(
759             APF_VERSION_6,
760             program,
761             HexDump.hexStringToByteArray(nonKeepaliveAckPkt1),
762             PASSED_IPV4_UNICAST
763         )
764 
765         // Pass IPv4 non-keepalive ack from the same source address
766         // Using scapy to generate IPv4 TCP non-keepalive ack from the same source address:
767         // eth = Ether(src="00:01:02:03:04:05", dst="01:02:03:04:05:06")
768         // ip = IP(src='10.0.0.6', dst='10.0.0.5')
769         // tcp = TCP(sport=54321, dport=12345, flags="A", seq=1234567890, ack=2123456790)
770         // payload = Raw(b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09')
771         // pkt = eth/ip/tcp/payload
772         val nonKeepaliveAckPkt2 = """
773             01020304050600010203040508004500003200010000400666bb0a0000060a000005d4313039499602d27
774             e91611650102000372c000000010203040506070809
775         """.replace("\\s+".toRegex(), "").trim()
776         verifyProgramRun(
777             APF_VERSION_6,
778             program,
779             HexDump.hexStringToByteArray(nonKeepaliveAckPkt2),
780             PASSED_IPV4_UNICAST
781         )
782 
783         // Pass IPv4 keepalive ack from another address
784         // Using scapy to generate IPv4 TCP keepalive ack from another address:
785         // eth = Ether(src="00:01:02:03:04:05", dst="01:02:03:04:05:06")
786         // ip = IP(src='10.0.0.7', dst='10.0.0.5')
787         // tcp = TCP(sport=23456, dport=65432, flags="A", seq=2123456780, ack=1123456789)
788         // pkt = eth/ip/tcp
789         val otherSrcKeepaliveAck = """
790             01020304050600010203040508004500002800010000400666c40a0000070a0000055ba0ff987e91610c4
791             2f697155010200066e60000
792         """.replace("\\s+".toRegex(), "").trim()
793         verifyProgramRun(
794             APF_VERSION_6,
795             program,
796             HexDump.hexStringToByteArray(otherSrcKeepaliveAck),
797             PASSED_IPV4_UNICAST
798         )
799 
800         // test IPv4 packets when TCP keepalive filter is removed
801         apfFilter.removeKeepalivePacketFilter(1)
802         program = consumeInstalledProgram(ipClientCallback, installCnt = 1)
803         verifyProgramRun(
804             APF_VERSION_6,
805             program,
806             HexDump.hexStringToByteArray(keepaliveAckPkt),
807             PASSED_IPV4_UNICAST
808         )
809 
810         verifyProgramRun(
811             APF_VERSION_6,
812             program,
813             HexDump.hexStringToByteArray(otherSrcKeepaliveAck),
814             PASSED_IPV4_UNICAST
815         )
816     }
817 
818     @Test
819     fun testIPv4NattKeepaliveFilter() {
820         val srcAddr = byteArrayOf(10, 0, 0, 5)
821         val dstAddr = byteArrayOf(10, 0, 0, 6)
822         val srcPort = 1024
823         val dstPort = 4500
824 
825         // src: 10.0.0.5:1024
826         // dst: 10.0.0.6:4500
827         val parcel = NattKeepalivePacketDataParcelable()
828         parcel.srcAddress = InetAddress.getByAddress(srcAddr).address
829         parcel.srcPort = srcPort
830         parcel.dstAddress = InetAddress.getByAddress(dstAddr).address
831         parcel.dstPort = dstPort
832 
833         val apfConfig = getDefaultConfig()
834         apfConfig.multicastFilter = true
835         apfConfig.ieee802_3Filter = true
836         val apfFilter = getApfFilter(apfConfig)
837         consumeInstalledProgram(ipClientCallback, installCnt = 2)
838         apfFilter.addNattKeepalivePacketFilter(1, parcel)
839         val program = consumeInstalledProgram(ipClientCallback, installCnt = 1)
840 
841         // Drop IPv4 keepalive response packet
842         // Using scapy to generate IPv4 NAT-T keepalive ack packet with payload 0xff:
843         // eth = Ether(src="00:01:02:03:04:05", dst="01:02:03:04:05:06")
844         // ip = IP(src='10.0.0.6', dst='10.0.0.5')
845         // udp = UDP(sport=4500, dport=1024)
846         // payload = NAT_KEEPALIVE(nat_keepalive=0xff)
847         // pkt = eth/ip/udp/payload
848         val validNattPkt = """
849             01020304050600010203040508004500001d00010000401166c50a0000060a000005119404000009d73cff
850         """.replace("\\s+".toRegex(), "").trim()
851         verifyProgramRun(
852             APF_VERSION_6,
853             program,
854             HexDump.hexStringToByteArray(validNattPkt),
855             DROPPED_IPV4_NATT_KEEPALIVE
856         )
857 
858         // Pass IPv4 keepalive response packet with 0xfe payload
859         // Using scapy to generate IPv4 NAT-T keepalive ack packet with payload 0xfe:
860         // eth = Ether(src="00:01:02:03:04:05", dst="01:02:03:04:05:06")
861         // ip = IP(src='10.0.0.6', dst='10.0.0.5')
862         // udp = UDP(sport=4500, dport=1024)
863         // payload = NAT_KEEPALIVE(nat_keepalive=0xfe)
864         // pkt = eth/ip/udp/payload
865         val invalidNattPkt = """
866             01020304050600010203040508004500001d00010000401166c50a0000060a000005119404000009d83cfe
867         """.replace("\\s+".toRegex(), "").trim()
868         verifyProgramRun(
869             APF_VERSION_6,
870             program,
871             HexDump.hexStringToByteArray(invalidNattPkt),
872             PASSED_IPV4_UNICAST
873         )
874 
875         // Pass IPv4 non-keepalive response packet from the same source address
876         // Using scapy to generate IPv4 NAT-T keepalive ack packet with payload 0xfe:
877         // eth = Ether(src="00:01:02:03:04:05", dst="01:02:03:04:05:06")
878         // ip = IP(src='10.0.0.6', dst='10.0.0.5')
879         // udp = UDP(sport=4500, dport=1024)
880         // payload = Raw(b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09')
881         // pkt = eth/ip/udp/payload
882         val nonNattPkt = """
883             01020304050600010203040508004500002600010000401166bc0a0000060a000005119404000012c2120
884             0010203040506070809
885         """.replace("\\s+".toRegex(), "").trim()
886         verifyProgramRun(
887             APF_VERSION_6,
888             program,
889             HexDump.hexStringToByteArray(nonNattPkt),
890             PASSED_IPV4_UNICAST
891         )
892 
893         // Pass IPv4 non-keepalive response packet from other source address
894         // Using scapy to generate IPv4 NAT-T keepalive ack packet with payload 0xfe:
895         // eth = Ether(src="00:01:02:03:04:05", dst="01:02:03:04:05:06")
896         // ip = IP(src='10.0.0.7', dst='10.0.0.5')
897         // udp = UDP(sport=4500, dport=1024)
898         // payload = Raw(b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09')
899         // pkt = eth/ip/udp/payload
900         val otherSrcNonNattPkt = """
901             01020304050600010203040508004500002600010000401166bb0a0000070a000005119404000012c2110
902             0010203040506070809
903         """.replace("\\s+".toRegex(), "").trim()
904         verifyProgramRun(
905             APF_VERSION_6,
906             program,
907             HexDump.hexStringToByteArray(otherSrcNonNattPkt),
908             PASSED_IPV4_UNICAST
909         )
910     }
911 
912     @Test
913     fun testIPv4TcpPort7Filter() {
914         val apfFilter = getApfFilter()
915         val program = consumeInstalledProgram(ipClientCallback, installCnt = 2)
916 
917         // Drop IPv4 TCP port 7 packet
918         // Using scapy to generate IPv4 TCP port 7 packet:
919         // eth = Ether(src="00:01:02:03:04:05", dst="01:02:03:04:05:06")
920         // ip = IP(src='10.0.0.6', dst='10.0.0.5')
921         // tcp = TCP(dport=7)
922         // pkt = eth/ip/tcp
923         val tcpPort7Pkt = """
924             01020304050600010203040508004500002800010000400666c50a0000060a00000500140007000000000
925             0000000500220007bbd0000
926         """.replace("\\s+".toRegex(), "").trim()
927         verifyProgramRun(
928             APF_VERSION_6,
929             program,
930             HexDump.hexStringToByteArray(tcpPort7Pkt),
931             DROPPED_IPV4_TCP_PORT7_UNICAST
932         )
933 
934         // Pass IPv4 TCP initial fragment packet
935         // Using scapy to generate IPv4 TCP initial fragment packet:
936         // eth = Ether(src="00:01:02:03:04:05", dst="01:02:03:04:05:06")
937         // ip = IP(src='10.0.0.6', dst='10.0.0.5', flags=1, frag=0)
938         // tcp = TCP()
939         // pkt = eth/ip/tcp
940         val initialFragmentTcpPkt = """
941             01020304050600010203040508004500002800012000400646c50a0000060a00000500140050000000000
942             0000000500220007b740000
943         """.replace("\\s+".toRegex(), "").trim()
944         verifyProgramRun(
945             APF_VERSION_6,
946             program,
947             HexDump.hexStringToByteArray(initialFragmentTcpPkt),
948             PASSED_IPV4
949         )
950 
951         // Pass IPv4 TCP fragment packet
952         // Using scapy to generate IPv4 TCP fragment packet:
953         // eth = Ether(src="00:01:02:03:04:05", dst="01:02:03:04:05:06")
954         // ip = IP(src='10.0.0.6', dst='10.0.0.5', flags=1, frag=100)
955         // tcp = TCP()
956         // pkt = eth/ip/tcp
957         val fragmentTcpPkt = """
958             01020304050600010203040508004500002800012064400646610a0000060a00000500140050000000000
959             0000000500220007b740000
960         """.replace("\\s+".toRegex(), "").trim()
961         verifyProgramRun(
962             APF_VERSION_6,
963             program,
964             HexDump.hexStringToByteArray(fragmentTcpPkt),
965             PASSED_IPV4
966         )
967     }
968 
969     @Test
970     fun testIPv6MulticastPacketFilterInDozeMode() {
971         val apfConfig = getDefaultConfig()
972         apfConfig.multicastFilter = true
973         val apfFilter = getApfFilter(apfConfig)
974         consumeInstalledProgram(ipClientCallback, installCnt = 2)
975         val lp = LinkProperties()
976         for (addr in hostIpv6Addresses) {
977             lp.addLinkAddress(LinkAddress(InetAddress.getByAddress(addr), 64))
978         }
979         apfFilter.setLinkProperties(lp)
980         apfFilter.setDozeMode(true)
981         val program = consumeInstalledProgram(ipClientCallback, installCnt = 2)
982         // Using scapy to generate non ICMPv6 sent to ff00::/8 (multicast prefix) packet:
983         // eth = Ether(src="00:01:02:03:04:05", dst="01:02:03:04:05:06")
984         // ip6 = IPv6(src="2001::200:1a:1122:3344", dst="ff00::1", nh=59)
985         // pkt = eth/ip6
986         val nonIcmpv6McastPkt = """
987             ffffffffffff00112233445586dd6000000000003b4020010000000000000200001a11223344ff00000
988             0000000000000000000000000
989         """.replace("\\s+".toRegex(), "").trim()
990         verifyProgramRun(
991             APF_VERSION_6,
992             program,
993             HexDump.hexStringToByteArray(nonIcmpv6McastPkt),
994             DROPPED_IPV6_NON_ICMP_MULTICAST
995         )
996 
997         // Using scapy to generate ICMPv6 echo sent to ff00::/8 (multicast prefix) packet:
998         // eth = Ether(src="00:01:02:03:04:05", dst="02:03:04:05:06:07")
999         // ip6 = IPv6(src="2001::200:1a:1122:3344", dst="ff00::1", hlim=255)
1000         // icmp6 = ICMPv6EchoRequest()
1001         // pkt = eth/ip6/icmp6
1002         val icmpv6EchoPkt = """
1003             02030405060700010203040586dd6000000000083aff20010000000000000200001a11223344ff00000
1004             000000000000000000000000180001a3a00000000
1005         """.replace("\\s+".toRegex(), "").trim()
1006         verifyProgramRun(
1007             APF_VERSION_6,
1008             program,
1009             HexDump.hexStringToByteArray(icmpv6EchoPkt),
1010             DROPPED_IPV6_NON_ICMP_MULTICAST
1011         )
1012     }
1013 
1014     @Test
1015     fun testIPv6PacketFilter() {
1016         val apfFilter = getApfFilter()
1017         consumeInstalledProgram(ipClientCallback, installCnt = 2)
1018         val lp = LinkProperties()
1019         for (addr in hostIpv6Addresses) {
1020             lp.addLinkAddress(LinkAddress(InetAddress.getByAddress(addr), 64))
1021         }
1022         apfFilter.setLinkProperties(lp)
1023         val program = consumeInstalledProgram(ipClientCallback, installCnt = 1)
1024         // Using scapy to generate non ICMPv6 packet:
1025         // eth = Ether(src="00:01:02:03:04:05", dst="01:02:03:04:05:06")
1026         // ip6 = IPv6(src="2001::200:1a:1122:3344", dst="2001::200:1a:3344:1122", nh=59)
1027         // pkt = eth/ip6
1028         val nonIcmpv6Pkt = """
1029             ffffffffffff00112233445586dd6000000000003b4020010000000000000200001a112233442001000
1030             0000000000200001a33441122
1031         """.replace("\\s+".toRegex(), "").trim()
1032         verifyProgramRun(
1033             APF_VERSION_6,
1034             program,
1035             HexDump.hexStringToByteArray(nonIcmpv6Pkt),
1036             PASSED_IPV6_NON_ICMP
1037         )
1038 
1039         // Using scapy to generate ICMPv6 NA sent to ff02::/120 packet:
1040         // eth = Ether(src="00:01:02:03:04:05", dst="01:02:03:04:05:06")
1041         // ip6 = IPv6(src="2001::200:1a:1122:3344", dst="ff02::1")
1042         // icmp6 = ICMPv6ND_NA()
1043         // pkt = eth/ip6/icmp6
1044         val icmpv6McastNaPkt = """
1045             01020304050600010203040586dd6000000000183aff20010000000000000200001a11223344ff02000
1046             000000000000000000000000188007227a000000000000000000000000000000000000000
1047         """.replace("\\s+".toRegex(), "").trim()
1048         verifyProgramRun(
1049             APF_VERSION_6,
1050             program,
1051             HexDump.hexStringToByteArray(icmpv6McastNaPkt),
1052             DROPPED_IPV6_MULTICAST_NA
1053         )
1054 
1055         // Using scapy to generate IPv6 packet with hop-by-hop option:
1056         // eth = Ether(src="00:01:02:03:04:05", dst="01:02:03:04:05:06")
1057         // ip6 = IPv6(src="2001::200:1a:1122:3344", dst="2001::200:1a:3344:1122", nh=0)
1058         // pkt = eth/ip6
1059         val ipv6WithHopByHopOptionPkt = """
1060             01020304050600010203040586dd600000000000004020010000000000000200001a112233442001000
1061             0000000000200001a33441122
1062         """.replace("\\s+".toRegex(), "").trim()
1063         verifyProgramRun(
1064             APF_VERSION_6,
1065             program,
1066             HexDump.hexStringToByteArray(ipv6WithHopByHopOptionPkt),
1067             PASSED_MLD
1068         )
1069     }
1070 
1071     @Test
1072     fun testArpFilterDropPktsNoIPv4() {
1073         val apfFilter = getApfFilter()
1074         val program = consumeInstalledProgram(ipClientCallback, installCnt = 2)
1075 
1076         // Drop ARP request packet with invalid hw type
1077         // Using scapy to generate ARP request packet with invalid hw type :
1078         // eth = Ether(src="00:01:02:03:04:05", dst="01:02:03:04:05:06")
1079         // arp = ARP(hwtype=3)
1080         // pkt = eth/arp
1081         val invalidHwTypePkt = """
1082             01020304050600010203040508060003080000040001c0a8012200000000
1083         """.replace("\\s+".toRegex(), "").trim()
1084         verifyProgramRun(
1085             APF_VERSION_6,
1086             program,
1087             HexDump.hexStringToByteArray(invalidHwTypePkt),
1088             DROPPED_ARP_NON_IPV4
1089         )
1090 
1091         // Drop ARP request packet with invalid proto type
1092         // Using scapy to generate ARP request packet with invalid proto type:
1093         // eth = Ether(src="00:01:02:03:04:05", dst="01:02:03:04:05:06")
1094         // arp = ARP(ptype=20)
1095         // pkt = eth/arp
1096         val invalidProtoTypePkt = """
1097             010203040506000102030405080600010014060000015c857e3c74e1000000000000
1098         """.replace("\\s+".toRegex(), "").trim()
1099         verifyProgramRun(
1100             APF_VERSION_6,
1101             program,
1102             HexDump.hexStringToByteArray(invalidProtoTypePkt),
1103             DROPPED_ARP_NON_IPV4
1104         )
1105 
1106         // Drop ARP request packet with invalid hw len
1107         // Using scapy to generate ARP request packet with invalid hw len:
1108         // eth = Ether(src="00:01:02:03:04:05", dst="01:02:03:04:05:06")
1109         // arp = ARP(hwlen=20)
1110         // pkt = eth/arp
1111         val invalidHwLenPkt = """
1112             01020304050600010203040508060001080014040001000000000000000000000000
1113             0000000000000000c0a8012200000000000000000000000000000000000000000000
1114             0000
1115         """.replace("\\s+".toRegex(), "").trim()
1116         verifyProgramRun(
1117             APF_VERSION_6,
1118             program,
1119             HexDump.hexStringToByteArray(invalidHwLenPkt),
1120             DROPPED_ARP_NON_IPV4
1121         )
1122 
1123         // Drop ARP request packet with invalid proto len
1124         // Using scapy to generate ARP request packet with invalid proto len:
1125         // eth = Ether(src="00:01:02:03:04:05", dst="01:02:03:04:05:06")
1126         // arp = ARP(plen=20)
1127         // pkt = eth/arp
1128         val invalidProtoLenPkt = """
1129             010203040506000102030405080600010800061400015c857e3c74e1000000000000
1130             00000000000000000000000000000000000000000000000000000000000000000000
1131             000000000000
1132         """.replace("\\s+".toRegex(), "").trim()
1133         verifyProgramRun(
1134             APF_VERSION_6,
1135             program,
1136             HexDump.hexStringToByteArray(invalidProtoLenPkt),
1137             DROPPED_ARP_NON_IPV4
1138         )
1139 
1140         // Drop ARP request packet with invalid opcode
1141         // Using scapy to generate ARP request packet with invalid opcode:
1142         // eth = Ether(src="00:01:02:03:04:05", dst="01:02:03:04:05:06")
1143         // arp = ARP(op=5)
1144         // pkt = eth/arp
1145         val invalidOpPkt = """
1146             010203040506000102030405080600010800060400055c857e3c74e1c0a8012200000000000000000000
1147         """.replace("\\s+".toRegex(), "").trim()
1148         verifyProgramRun(
1149             APF_VERSION_6,
1150             program,
1151             HexDump.hexStringToByteArray(invalidOpPkt),
1152             DROPPED_ARP_UNKNOWN
1153         )
1154 
1155         // Drop ARP reply packet with zero source protocol address
1156         // Using scapy to generate ARP request packet with zero source protocol address:
1157         // eth = Ether(src="00:01:02:03:04:05", dst="01:02:03:04:05:06")
1158         // arp = ARP(op=2, psrc="0.0.0.0)
1159         // pkt = eth/arp
1160         val noHostArpReplyPkt = """
1161             010203040506000102030405080600010800060400025c857e3c74e10000000000000000000000000000
1162         """.replace("\\s+".toRegex(), "").trim()
1163         verifyProgramRun(
1164             APF_VERSION_6,
1165             program,
1166             HexDump.hexStringToByteArray(noHostArpReplyPkt),
1167             DROPPED_ARP_REPLY_SPA_NO_HOST
1168         )
1169 
1170         // Drop ARP reply packet with ethernet broadcast destination
1171         // Using scapy to generate ARP reply packet with ethernet broadcast destination:
1172         // eth = Ether(src="00:01:02:03:04:05", dst="FF:FF:FF:FF:FF:FF")
1173         // arp = ARP(op=2, pdst="0.0.0.0")
1174         // pkt = eth/arp
1175         val garpReplyPkt = """
1176             ffffffffffff000102030405080600010800060400025c857e3c74e1c0a8012200000000000000000000
1177         """.replace("\\s+".toRegex(), "").trim()
1178         verifyProgramRun(
1179             APF_VERSION_6,
1180             program,
1181             HexDump.hexStringToByteArray(garpReplyPkt),
1182             DROPPED_GARP_REPLY
1183         )
1184     }
1185 
1186     @Test
1187     fun testArpFilterPassPktsNoIPv4() {
1188         val apfFilter = getApfFilter()
1189         val program = consumeInstalledProgram(ipClientCallback, installCnt = 2)
1190         // Pass non-broadcast ARP reply packet
1191         // Using scapy to generate unicast ARP reply packet:
1192         // eth = Ether(src="00:01:02:03:04:05", dst="01:02:03:04:05:06")
1193         // arp = ARP(op=2, psrc="1.2.3.4")
1194         // pkt = eth/arp
1195         val nonBcastArpReplyPkt = """
1196             010203040506000102030405080600010800060400025c857e3c74e10102030400000000000000000000
1197         """.replace("\\s+".toRegex(), "").trim()
1198         verifyProgramRun(
1199             APF_VERSION_6,
1200             program,
1201             HexDump.hexStringToByteArray(nonBcastArpReplyPkt),
1202             PASSED_ARP_UNICAST_REPLY
1203         )
1204 
1205         // Pass ARP request packet if device doesn't have any IPv4 address
1206         // Using scapy to generate ARP request packet:
1207         // eth = Ether(src="00:01:02:03:04:05", dst="FF:FF:FF:FF:FF:FF")
1208         // arp = ARP(op=1, pdst="1.2.3.4")
1209         // pkt = eth/arp
1210         val arpRequestPkt = """
1211             ffffffffffff000102030405080600010800060400015c857e3c74e1c0a8012200000000000001020304
1212         """.replace("\\s+".toRegex(), "").trim()
1213         verifyProgramRun(
1214             APF_VERSION_6,
1215             program,
1216             HexDump.hexStringToByteArray(arpRequestPkt),
1217             PASSED_ARP_REQUEST
1218         )
1219     }
1220 
1221     @Test
1222     fun testArpFilterDropPktsWithIPv4() {
1223         val apfFilter = getApfFilter()
1224         consumeInstalledProgram(ipClientCallback, installCnt = 2)
1225         val linkAddress = LinkAddress(InetAddress.getByAddress(hostIpv4Address), 24)
1226         val lp = LinkProperties()
1227         lp.addLinkAddress(linkAddress)
1228         apfFilter.setLinkProperties(lp)
1229         val program = consumeInstalledProgram(ipClientCallback, installCnt = 1)
1230         // Drop ARP reply packet is not for the device
1231         // Using scapy to generate ARP reply packet not for the device:
1232         // eth = Ether(src="00:01:02:03:04:05", dst="FF:FF:FF:FF:FF:FF")
1233         // arp = ARP(op=2, pdst="1.2.3.4")
1234         // pkt = eth/arp
1235         val otherHostArpReplyPkt = """
1236             ffffffffffff000102030405080600010800060400025c857e3c74e1c0a8012200000000000001020304
1237         """.replace("\\s+".toRegex(), "").trim()
1238         verifyProgramRun(
1239             APF_VERSION_6,
1240             program,
1241             HexDump.hexStringToByteArray(otherHostArpReplyPkt),
1242             DROPPED_ARP_OTHER_HOST
1243         )
1244 
1245         // Drop broadcast ARP request packet not for the device
1246         // Using scapy to generate ARP broadcast request packet not for the device:
1247         // eth = Ether(src="00:01:02:03:04:05", dst="FF:FF:FF:FF:FF:FF")
1248         // arp = ARP(op=1, pdst="1.2.3.4")
1249         // pkt = eth/arp
1250         val otherHostArpRequestPkt = """
1251             ffffffffffff000102030405080600010800060400015c857e3c74e1c0a8012200000000000001020304
1252         """.replace("\\s+".toRegex(), "").trim()
1253         verifyProgramRun(
1254             APF_VERSION_6,
1255             program,
1256             HexDump.hexStringToByteArray(otherHostArpRequestPkt),
1257             DROPPED_ARP_OTHER_HOST
1258         )
1259     }
1260 
1261     @Test
1262     fun testArpFilterPassPktsWithIPv4() {
1263         val apfFilter = getApfFilter()
1264         consumeInstalledProgram(ipClientCallback, installCnt = 2)
1265         val linkAddress = LinkAddress(InetAddress.getByAddress(hostIpv4Address), 24)
1266         val lp = LinkProperties()
1267         lp.addLinkAddress(linkAddress)
1268         apfFilter.setLinkProperties(lp)
1269         val program = consumeInstalledProgram(ipClientCallback, installCnt = 1)
1270 
1271         // Using scapy to generate ARP broadcast reply packet:
1272         // eth = Ether(src="00:01:02:03:04:05", dst="FF:FF:FF:FF:FF:FF")
1273         // arp = ARP(op=2, pdst="10.0.0.1")
1274         // pkt = eth/arp
1275         val bcastArpReplyPkt = """
1276             ffffffffffff000102030405080600010800060400025c857e3c74e1c0a801220000000000000a000001
1277         """.replace("\\s+".toRegex(), "").trim()
1278         verifyProgramRun(
1279             APF_VERSION_6,
1280             program,
1281             HexDump.hexStringToByteArray(bcastArpReplyPkt),
1282             PASSED_ARP_BROADCAST_REPLY
1283         )
1284     }
1285 
1286     // The APFv6 code path is only turned on in V+
1287     @IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
1288     @Test
1289     fun testArpTransmit() {
1290         val apfFilter = getApfFilter()
1291         consumeInstalledProgram(ipClientCallback, installCnt = 2)
1292         val linkAddress = LinkAddress(InetAddress.getByAddress(hostIpv4Address), 24)
1293         val lp = LinkProperties()
1294         lp.addLinkAddress(linkAddress)
1295         apfFilter.setLinkProperties(lp)
1296         val program = consumeInstalledProgram(ipClientCallback, installCnt = 1)
1297         val receivedArpPacketBuf = ArpPacket.buildArpPacket(
1298             arpBroadcastMacAddress,
1299             senderMacAddress,
1300             hostIpv4Address,
1301             HexDump.hexStringToByteArray("000000000000"),
1302             senderIpv4Address,
1303             ARP_REQUEST.toShort()
1304         )
1305         val receivedArpPacket = ByteArray(ARP_ETHER_IPV4_LEN)
1306         receivedArpPacketBuf.get(receivedArpPacket)
1307         verifyProgramRun(
1308             apfFilter.mApfVersionSupported,
1309             program,
1310             receivedArpPacket,
1311             DROPPED_ARP_REQUEST_REPLIED
1312         )
1313 
1314         val transmittedPackets = consumeTransmittedPackets(1)
1315         val expectedArpReplyBuf = ArpPacket.buildArpPacket(
1316             senderMacAddress,
1317             apfFilter.mHardwareAddress,
1318             senderIpv4Address,
1319             senderMacAddress,
1320             hostIpv4Address,
1321             ARP_REPLY.toShort()
1322         )
1323         val expectedArpReplyPacket = ByteArray(ARP_ETHER_IPV4_LEN)
1324         expectedArpReplyBuf.get(expectedArpReplyPacket)
1325         assertContentEquals(
1326             expectedArpReplyPacket + ByteArray(18) { 0 },
1327             transmittedPackets[0]
1328         )
1329     }
1330 
1331     @Test
1332     fun testArpOffloadDisabled() {
1333         val apfConfig = getDefaultConfig()
1334         apfConfig.shouldHandleArpOffload = false
1335         val apfFilter = getApfFilter(apfConfig)
1336         consumeInstalledProgram(ipClientCallback, installCnt = 2)
1337         val linkAddress = LinkAddress(InetAddress.getByAddress(hostIpv4Address), 24)
1338         val lp = LinkProperties()
1339         lp.addLinkAddress(linkAddress)
1340         apfFilter.setLinkProperties(lp)
1341         val program = consumeInstalledProgram(ipClientCallback, installCnt = 1)
1342         val receivedArpPacketBuf = ArpPacket.buildArpPacket(
1343             arpBroadcastMacAddress,
1344             senderMacAddress,
1345             hostIpv4Address,
1346             HexDump.hexStringToByteArray("000000000000"),
1347             senderIpv4Address,
1348             ARP_REQUEST.toShort()
1349         )
1350         val receivedArpPacket = ByteArray(ARP_ETHER_IPV4_LEN)
1351         receivedArpPacketBuf.get(receivedArpPacket)
1352         verifyProgramRun(
1353             apfFilter.mApfVersionSupported,
1354             program,
1355             receivedArpPacket,
1356             PASSED_ARP_REQUEST
1357         )
1358     }
1359 
1360     @Test
1361     @IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
1362     fun testNsFilterNoIPv6() {
1363         doReturn(listOf<ByteArray>()).`when`(dependencies).getAnycast6Addresses(any())
1364         val apfFilter = getApfFilter()
1365         // validate NS packet check when there is no IPv6 address
1366         val program = consumeInstalledProgram(ipClientCallback, installCnt = 2)
1367         // Using scapy to generate IPv6 NS packet:
1368         // eth = Ether(src="00:01:02:03:04:05", dst="01:02:03:04:05:06")
1369         // ip6 = IPv6(src="2001::200:1a:1122:3344", dst="2001::200:1a:3344:1122", hlim=255)
1370         // icmp6 = ICMPv6ND_NS(tgt="2001::200:1a:3344:1122")
1371         // pkt = eth/ip6/icmp6
1372         val nsPkt = """
1373             01020304050600010203040586DD6000000000183AFF200100000000000
1374             00200001A1122334420010000000000000200001A334411228700452900
1375             00000020010000000000000200001A33441122
1376         """.replace("\\s+".toRegex(), "").trim()
1377         // when there is no IPv6 addresses -> pass NS packet
1378         verifyProgramRun(
1379             apfFilter.mApfVersionSupported,
1380             program,
1381             HexDump.hexStringToByteArray(nsPkt),
1382             PASSED_IPV6_NS_NO_ADDRESS
1383         )
1384     }
1385 
1386     @Test
1387     @IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
1388     fun testNsFilter() {
1389         val apfFilter = getApfFilter()
1390         consumeInstalledProgram(ipClientCallback, installCnt = 2)
1391         val lp = LinkProperties()
1392         for (addr in hostIpv6Addresses) {
1393             lp.addLinkAddress(LinkAddress(InetAddress.getByAddress(addr), 64))
1394         }
1395 
1396         for (addr in hostIpv6TentativeAddresses) {
1397             lp.addLinkAddress(
1398                 LinkAddress(
1399                     InetAddress.getByAddress(addr),
1400                     64,
1401                     IFA_F_TENTATIVE,
1402                     0
1403                 )
1404             )
1405         }
1406 
1407         apfFilter.setLinkProperties(lp)
1408         consumeInstalledProgram(ipClientCallback, installCnt = 1)
1409         apfFilter.updateClatInterfaceState(true)
1410         val program = consumeInstalledProgram(ipClientCallback, installCnt = 1)
1411 
1412         // validate Ethernet dst address check
1413         // Using scapy to generate IPv6 NS packet:
1414         // eth = Ether(src="00:01:02:03:04:05", dst="00:05:04:03:02:01")
1415         // ip6 = IPv6(src="2001::200:1a:1122:3344", dst="2001::200:1a:3344:1122", hlim=255)
1416         // icmp6 = ICMPv6ND_NS(tgt="2001::200:1a:3344:1122")
1417         // icmp6_opt = ICMPv6NDOptDstLLAddr(lladdr="00:01:02:03:04:05")
1418         // pkt = eth/ip6/icmp6/icmp6_opt
1419         val nonHostDstMacNsPkt = """
1420             00050403020100010203040586DD6000000000203AFF2001000000000000
1421             0200001A1122334420010000000000000200001A3344112287003D170000
1422             000020010000000000000200001A334411220201000102030405
1423         """.replace("\\s+".toRegex(), "").trim()
1424         // invalid unicast ether dst -> pass
1425         verifyProgramRun(
1426             apfFilter.mApfVersionSupported,
1427             program,
1428             HexDump.hexStringToByteArray(nonHostDstMacNsPkt),
1429             DROPPED_IPV6_NS_OTHER_HOST
1430         )
1431 
1432         // Using scapy to generate IPv6 NS packet:
1433         // eth = Ether(src="00:01:02:03:04:05", dst="33:33:ff:03:02:01")
1434         // ip6 = IPv6(src="2001::200:1a:1122:3344", dst="2001::200:1a:3344:1122", hlim=255)
1435         // icmp6 = ICMPv6ND_NS(tgt="2001::200:1a:3344:1122")
1436         // icmp6_opt = ICMPv6NDOptDstLLAddr(lladdr="00:01:02:03:04:05")
1437         // pkt = eth/ip6/icmp6/icmp6_opt
1438         val nonMcastDstMacNsPkt = """
1439             3333FF03020100010203040586DD6000000000203AFF20010000000000
1440             000200001A1122334420010000000000000200001A3344112287003D17
1441             0000000020010000000000000200001A334411220201000102030405
1442         """.replace("\\s+".toRegex(), "").trim()
1443         // mcast dst mac is not one of solicited mcast mac derived from one of device's ip -> pass
1444         verifyProgramRun(
1445             apfFilter.mApfVersionSupported,
1446             program,
1447             HexDump.hexStringToByteArray(nonMcastDstMacNsPkt),
1448             DROPPED_IPV6_NS_OTHER_HOST
1449         )
1450 
1451         // Using scapy to generate IPv6 NS packet:
1452         // eth = Ether(src="00:01:02:03:04:05", dst="33:33:ff:44:11:22")
1453         // ip6 = IPv6(src="2001::200:1a:1122:3344", dst="2001::200:1a:3344:1122", hlim=255)
1454         // icmp6 = ICMPv6ND_NS(tgt="2001::200:1a:3344:1122")
1455         // icmp6_opt = ICMPv6NDOptSrcLLAddr(lladdr="00:01:02:03:04:05")
1456         // pkt = eth/ip6/icmp6/icmp6_opt
1457         val hostMcastDstMacNsPkt = """
1458             3333FF44112200010203040586DD6000000000203AFF20010000000000
1459             000200001A1122334420010000000000000200001A3344112287003E17
1460             0000000020010000000000000200001A334411220101000102030405
1461         """.replace("\\s+".toRegex(), "").trim()
1462         // mcast dst mac is one of solicited mcast mac derived from one of device's ip
1463         // -> drop and replied
1464         verifyProgramRun(
1465             apfFilter.mApfVersionSupported,
1466             program,
1467             HexDump.hexStringToByteArray(hostMcastDstMacNsPkt),
1468             DROPPED_IPV6_NS_REPLIED_NON_DAD
1469         )
1470 
1471         // Using scapy to generate IPv6 NS packet:
1472         // eth = Ether(src="00:01:02:03:04:05", dst="FF:FF:FF:FF:FF:FF")
1473         // ip6 = IPv6(src="2001::200:1a:1122:3344", dst="2001::200:1a:3344:1122", hlim=255)
1474         // icmp6 = ICMPv6ND_NS(tgt="2001::200:1a:3344:1122")
1475         // icmp6_opt = ICMPv6NDOptSrcLLAddr(lladdr="00:01:02:03:04:05")
1476         // pkt = eth/ip6/icmp6/icmp6_opt
1477         val broadcastNsPkt = """
1478             FFFFFFFFFFFF00010203040586DD6000000000203AFF200100000000000002000
1479             01A1122334420010000000000000200001A3344112287003E1700000000200100
1480             00000000000200001A334411220101000102030405
1481         """.replace("\\s+".toRegex(), "").trim()
1482         // mcast dst mac is broadcast address -> drop and replied
1483         verifyProgramRun(
1484             apfFilter.mApfVersionSupported,
1485             program,
1486             HexDump.hexStringToByteArray(broadcastNsPkt),
1487             DROPPED_IPV6_NS_REPLIED_NON_DAD
1488         )
1489 
1490         // validate IPv6 dst address check
1491 
1492         // Using scapy to generate IPv6 NS packet:
1493         // eth = Ether(src="00:01:02:03:04:05", dst="02:03:04:05:06:07")
1494         // ip6 = IPv6(src="2001::200:1a:1122:3344", dst="2001::200:1a:3344:1122", hlim=255)
1495         // icmp6 = ICMPv6ND_NS(tgt="2001::200:1a:3344:1122")
1496         // icmp6_opt = ICMPv6NDOptSrcLLAddr(lladdr="00:01:02:03:04:05")
1497         // pkt = eth/ip6/icmp6/icmp6_opt
1498         val validHostDstIpNsPkt = """
1499             02030405060700010203040586DD6000000000203AFF200100000000000
1500             00200001A1122334420010000000000000200001A3344112287003E1700
1501             00000020010000000000000200001A334411220101000102030405
1502         """.replace("\\s+".toRegex(), "").trim()
1503         // dst ip is one of device's ip -> drop and replied
1504         verifyProgramRun(
1505             apfFilter.mApfVersionSupported,
1506             program,
1507             HexDump.hexStringToByteArray(validHostDstIpNsPkt),
1508             DROPPED_IPV6_NS_REPLIED_NON_DAD
1509         )
1510 
1511         // Using scapy to generate IPv6 NS packet:
1512         // eth = Ether(src="00:01:02:03:04:05", dst="02:03:04:05:06:07")
1513         // ip6 = IPv6(src="2001::200:1a:1122:3344", dst="2001::100:1b:aabb:ccdd", hlim=255)
1514         // icmp6 = ICMPv6ND_NS(tgt="2001::100:1b:aabb:ccdd")
1515         // icmp6_opt = ICMPv6NDOptSrcLLAddr(lladdr="00:01:02:03:04:05")
1516         // pkt = eth/ip6/icmp6/icmp6_opt
1517         val validHostAnycastDstIpNsPkt = """
1518             02030405060700010203040586DD6000000000203AFF20010000
1519             000000000200001A1122334420010000000000000100001BAABB
1520             CCDD8700D9AE0000000020010000000000000100001BAABBCCDD
1521             0101000102030405
1522         """.replace("\\s+".toRegex(), "").trim()
1523         // dst ip is device's anycast address -> drop and replied
1524         verifyProgramRun(
1525             apfFilter.mApfVersionSupported,
1526             program,
1527             HexDump.hexStringToByteArray(validHostAnycastDstIpNsPkt),
1528             DROPPED_IPV6_NS_REPLIED_NON_DAD
1529         )
1530 
1531         // Using scapy to generate IPv6 NS packet:
1532         // eth = Ether(src="00:01:02:03:04:05", dst="02:03:04:05:06:07")
1533         // ip6 = IPv6(src="2001::200:1a:1122:3344", dst="2001::200:1a:4444:5555", hlim=255)
1534         // icmp6 = ICMPv6ND_NS(tgt="2001::200:1a:3344:1122")
1535         // icmp6_opt = ICMPv6NDOptSrcLLAddr(lladdr="00:01:02:03:04:05")
1536         // pkt = eth/ip6/icmp6/icmp6_opt
1537         val nonHostUcastDstIpNsPkt = """
1538             02030405060700010203040586DD6000000000203AFF2001000000000
1539             0000200001A1122334420010000000000000200001A444455558700E8
1540             E30000000020010000000000000200001A334411220101000102030405
1541         """.replace("\\s+".toRegex(), "").trim()
1542         // unicast dst ip is not one of device's ip -> pass
1543         verifyProgramRun(
1544             apfFilter.mApfVersionSupported,
1545             program,
1546             HexDump.hexStringToByteArray(nonHostUcastDstIpNsPkt),
1547             DROPPED_IPV6_NS_OTHER_HOST
1548         )
1549 
1550         // Using scapy to generate IPv6 NS packet:
1551         // eth = Ether(src="00:01:02:03:04:05", dst="02:03:04:05:06:07")
1552         // ip6 = IPv6(src="2001::200:1a:1122:3344", dst="ff02::1:ff44:1133", hlim=255)
1553         // icmp6 = ICMPv6ND_NS(tgt="2001::200:1a:3344:1122")
1554         // icmp6_opt = ICMPv6NDOptSrcLLAddr(lladdr="00:01:02:03:04:05")
1555         // pkt = eth/ip6/icmp6/icmp6_opt
1556         val nonHostMcastDstIpNsPkt = """
1557             02030405060700010203040586DD6000000000203AFF2001000000000
1558             0000200001A11223344FF0200000000000000000001FF441133870095
1559             1C0000000020010000000000000200001A334411220101000102030405
1560         """.replace("\\s+".toRegex(), "").trim()
1561         // mcast dst ip is not one of solicited mcast ip derived from one of device's ip -> pass
1562         verifyProgramRun(
1563             apfFilter.mApfVersionSupported,
1564             program,
1565             HexDump.hexStringToByteArray(nonHostMcastDstIpNsPkt),
1566             DROPPED_IPV6_NS_OTHER_HOST
1567         )
1568 
1569         // Using scapy to generate IPv6 NS packet:
1570         // eth = Ether(src="00:01:02:03:04:05", dst="02:03:04:05:06:07")
1571         // ip6 = IPv6(src="2001::200:1a:1122:3344", dst="ff02::1:ff44:1122", hlim=255)
1572         // icmp6 = ICMPv6ND_NS(tgt="2001::200:1a:3344:1122")
1573         // icmp6_opt = ICMPv6NDOptSrcLLAddr(lladdr="00:01:02:03:04:05")
1574         // pkt = eth/ip6/icmp6/icmp6_opt
1575         val hostMcastDstIpNsPkt =
1576             "02030405060700010203040586DD6000000000203AFF2001000000000000" +
1577                     "0200001A11223344FF0200000000000000000001FF4411228700952D0000" +
1578                     "000020010000000000000200001A334411220101000102030405"
1579         // mcast dst ip is one of solicited mcast ip derived from one of device's ip
1580         //   -> drop and replied
1581         verifyProgramRun(
1582             apfFilter.mApfVersionSupported,
1583             program,
1584             HexDump.hexStringToByteArray(hostMcastDstIpNsPkt),
1585             DROPPED_IPV6_NS_REPLIED_NON_DAD
1586         )
1587 
1588         // validate IPv6 NS payload check
1589 
1590         // Using scapy to generate IPv6 NS packet:
1591         // eth = Ether(src="00:01:02:03:04:05", dst="02:03:04:05:06:07")
1592         // ip6 = IPv6(src="2001::200:1a:1122:3344", dst="2001::200:1a:3344:1122", hlim=255, plen=20)
1593         // icmp6 = ICMPv6ND_NS(tgt="2001::200:1a:3344:1122")
1594         // icmp6_opt = ICMPv6NDOptSrcLLAddr(lladdr="01:02:03:04:05:06")
1595         // pkt = eth/ip6/icmp6/icmp6_opt
1596         val shortNsPkt = """
1597             02030405060700010203040586DD6000000000143AFF20010000000000000200001A1
1598             122334420010000000000000200001A3344112287003B140000000020010000000000
1599             000200001A334411220101010203040506
1600         """.replace("\\s+".toRegex(), "").trim()
1601         // payload len < 24 -> drop
1602         verifyProgramRun(
1603             apfFilter.mApfVersionSupported,
1604             program,
1605             HexDump.hexStringToByteArray(shortNsPkt),
1606             DROPPED_IPV6_NS_INVALID
1607         )
1608 
1609         // Using scapy to generate IPv6 NS packet:
1610         // eth = Ether(src="00:01:02:03:04:05", dst="02:03:04:05:06:07")
1611         // ip6 = IPv6(src="2001::200:1a:1122:3344", dst="2001::200:1a:3344:1122", hlim=255)
1612         // icmp6 = ICMPv6ND_NS(tgt="2001::200:1a:4444:5555")
1613         // icmp6_opt = ICMPv6NDOptSrcLLAddr(lladdr="01:02:03:04:05:06")
1614         // pkt = eth/ip6/icmp6/icmp6_opt
1615         val otherHostNsPkt = """
1616             02030405060700010203040586DD6000000000203AFF200100000000000002000
1617             01A1122334420010000000000000200001A334411228700E5E000000000200100
1618             00000000000200001A444455550101010203040506
1619         """.replace("\\s+".toRegex(), "").trim()
1620         // target ip is not one of device's ip -> drop
1621         verifyProgramRun(
1622             apfFilter.mApfVersionSupported,
1623             program,
1624             HexDump.hexStringToByteArray(otherHostNsPkt),
1625             DROPPED_IPV6_NS_OTHER_HOST
1626         )
1627 
1628         // Using scapy to generate IPv6 NS packet:
1629         // eth = Ether(src="00:01:02:03:04:05", dst="02:03:04:05:06:07")
1630         // ip6 = IPv6(src="2001::200:1a:1122:3344", dst="2001::200:1a:3344:1122", hlim=20)
1631         // icmp6 = ICMPv6ND_NS(tgt="2001::200:1a:3344:1122")
1632         // icmp6_opt = ICMPv6NDOptSrcLLAddr(lladdr="01:02:03:04:05:06")
1633         // pkt = eth/ip6/icmp6/icmp6_opt
1634         val invalidHoplimitNsPkt = """
1635             02030405060700010203040586DD6000000000203A14200100000000000
1636             00200001A1122334420010000000000000200001A3344112287003B1400
1637             00000020010000000000000200001A334411220101010203040506
1638         """.replace("\\s+".toRegex(), "").trim()
1639         // hoplimit is not 255 -> drop
1640         verifyProgramRun(
1641             apfFilter.mApfVersionSupported,
1642             program,
1643             HexDump.hexStringToByteArray(invalidHoplimitNsPkt),
1644             DROPPED_IPV6_NS_INVALID
1645         )
1646 
1647         // Using scapy to generate IPv6 NS packet:
1648         // eth = Ether(src="00:01:02:03:04:05", dst="02:03:04:05:06:07")
1649         // ip6 = IPv6(src="2001::200:1a:1122:3344", dst="2001::200:1a:3344:1122", hlim=255)
1650         // icmp6 = ICMPv6ND_NS(tgt="2001::200:1a:3344:1122", code=5)
1651         // icmp6_opt = ICMPv6NDOptSrcLLAddr(lladdr="01:02:03:04:05:06")
1652         // pkt = eth/ip6/icmp6/icmp6_opt
1653         val invalidIcmpCodeNsPkt = """
1654             02030405060700010203040586DD6000000000203AFF200100000000000
1655             00200001A1122334420010000000000000200001A3344112287053B0F00
1656             00000020010000000000000200001A334411220101010203040506
1657         """.replace("\\s+".toRegex(), "").trim()
1658         // icmp6 code is not 0 -> drop
1659         verifyProgramRun(
1660             apfFilter.mApfVersionSupported,
1661             program,
1662             HexDump.hexStringToByteArray(invalidIcmpCodeNsPkt),
1663             DROPPED_IPV6_NS_INVALID
1664         )
1665 
1666         // Using scapy to generate IPv6 NS packet:
1667         // eth = Ether(src="00:01:02:03:04:05", dst="02:03:04:05:06:07")
1668         // ip6 = IPv6(src="2001::200:1a:1122:3344", dst="2001::200:1a:3344:1122", hlim=255)
1669         // icmp6 = ICMPv6ND_NS(tgt="2001::200:1a:1234:5678")
1670         // icmp6_opt = ICMPv6NDOptSrcLLAddr(lladdr="01:02:03:04:05:06")
1671         // pkt = eth/ip6/icmp6/icmp6_opt
1672         val tentativeTargetIpNsPkt = """
1673             02030405060700010203040586DD6000000000203AFF200100000000
1674             00000200001A1122334420010000000000000200001A334411228700
1675             16CE0000000020010000000000000200001A123456780101010203040506
1676         """.replace("\\s+".toRegex(), "").trim()
1677         // target ip is one of tentative address -> pass
1678         verifyProgramRun(
1679             apfFilter.mApfVersionSupported,
1680             program,
1681             HexDump.hexStringToByteArray(tentativeTargetIpNsPkt),
1682             PASSED_IPV6_NS_TENTATIVE
1683         )
1684 
1685         // Using scapy to generate IPv6 NS packet:
1686         // eth = Ether(src="00:01:02:03:04:05", dst="02:03:04:05:06:07")
1687         // ip6 = IPv6(src="2001::200:1a:1122:3344", dst="2001::200:1a:3344:1122", hlim=255)
1688         // icmp6 = ICMPv6ND_NS(tgt="2001::200:1c:2255:6666")
1689         // icmp6_opt = ICMPv6NDOptSrcLLAddr(lladdr="01:02:03:04:05:06")
1690         // pkt = eth/ip6/icmp6/icmp6_opt
1691         val invalidTargetIpNsPkt = """
1692             02030405060700010203040586DD6000000000203AFF200100000000000
1693             00200001A1122334420010000000000000200001A334411228700F6BC00
1694             00000020010000000000000200001C225566660101010203040506
1695         """.replace("\\s+".toRegex(), "").trim()
1696         // target ip is none of {non-tentative, anycast} -> drop
1697         verifyProgramRun(
1698             apfFilter.mApfVersionSupported,
1699             program,
1700             HexDump.hexStringToByteArray(invalidTargetIpNsPkt),
1701             DROPPED_IPV6_NS_OTHER_HOST
1702         )
1703 
1704         // Using scapy to generate IPv6 NS packet:
1705         // eth = Ether(src="00:01:02:03:04:05", dst="02:03:04:05:06:07")
1706         // ip6 = IPv6(src="::", dst="ff02::1:ff44:1122", hlim=255)
1707         // icmp6 = ICMPv6ND_NS(tgt="2001::200:1a:3344:1122")
1708         // icmp6_opt = ICMPv6NDOptDstLLAddr(lladdr="02:03:04:05:06:07")
1709         // pkt = eth/ip6/icmp6/icmp6_opt
1710         val dadNsPkt = """
1711             02030405060700010203040586DD6000000000203AFF000000000000000000000000000
1712             00000FF0200000000000000000001FF4411228700F4A800000000200100000000000002
1713             00001A334411220201020304050607
1714         """.replace("\\s+".toRegex(), "").trim()
1715         // DAD NS request -> pass
1716         verifyProgramRun(
1717             apfFilter.mApfVersionSupported,
1718             program,
1719             HexDump.hexStringToByteArray(dadNsPkt),
1720             PASSED_IPV6_NS_DAD
1721         )
1722 
1723         // Using scapy to generate IPv6 NS packet:
1724         // eth = Ether(src="00:01:02:03:04:05", dst="02:03:04:05:06:07")
1725         // ip6 = IPv6(src="2001::200:1a:1122:3344", dst="2001::200:1a:3344:1122", hlim=255)
1726         // icmp6 = ICMPv6ND_NS(tgt="2001::200:1a:3344:1122")
1727         // pkt = eth/ip6/icmp6
1728         val noOptionNsPkt = """
1729             02030405060700010203040586DD6000000000183AFF2001000000000000020000
1730             1A1122334420010000000000000200001A33441122870045290000000020010000
1731             000000000200001A33441122
1732         """.replace("\\s+".toRegex(), "").trim()
1733         // payload len < 32 -> pass
1734         verifyProgramRun(
1735             apfFilter.mApfVersionSupported,
1736             program,
1737             HexDump.hexStringToByteArray(noOptionNsPkt),
1738             PASSED_IPV6_NS_NO_SLLA_OPTION
1739         )
1740 
1741         // Using scapy to generate IPv6 NS packet:
1742         // eth = Ether(src="00:01:02:03:04:05", dst="02:03:04:05:06:07")
1743         // ip6 = IPv6(src="ff01::200:1a:1122:3344", dst="2001::200:1a:3344:1122", hlim=255)
1744         // icmp6 = ICMPv6ND_NS(tgt="2001::200:1a:3344:1122")
1745         // icmp6_opt = ICMPv6NDOptSrcLLAddr(lladdr="01:02:03:04:05:06")
1746         // pkt = eth/ip6/icmp6/icmp6_opt
1747         val nonDadMcastSrcIpPkt = """
1748             02030405060700010203040586DD6000000000203AFFFF01000000000000
1749             0200001A1122334420010000000000000200001A3344112287005C130000
1750             000020010000000000000200001A334411220101010203040506
1751         """.replace("\\s+".toRegex(), "").trim()
1752         // non-DAD src IPv6 is FF::/8 -> drop
1753         verifyProgramRun(
1754             apfFilter.mApfVersionSupported,
1755             program,
1756             HexDump.hexStringToByteArray(nonDadMcastSrcIpPkt),
1757             DROPPED_IPV6_NS_INVALID
1758         )
1759 
1760         // Using scapy to generate IPv6 NS packet:
1761         // eth = Ether(src="00:01:02:03:04:05", dst="02:03:04:05:06:07")
1762         // ip6 = IPv6(src="0001::200:1a:1122:3344", dst="2001::200:1a:3344:1122", hlim=255)
1763         // icmp6 = ICMPv6ND_NS(tgt="2001::200:1a:3344:1122")
1764         // icmp6_opt = ICMPv6NDOptSrcLLAddr(lladdr="01:02:03:04:05:06")
1765         // pkt = eth/ip6/icmp6/icmp6_opt
1766         val nonDadLoopbackSrcIpPkt = """
1767             02030405060700010203040586DD6000000000203AFF0001000000000
1768             0000200001A1122334420010000000000000200001A3344112287005B
1769             140000000020010000000000000200001A334411220101010203040506
1770         """.replace("\\s+".toRegex(), "").trim()
1771         // non-DAD src IPv6 is 00::/8 -> drop
1772         verifyProgramRun(
1773             apfFilter.mApfVersionSupported,
1774             program,
1775             HexDump.hexStringToByteArray(nonDadLoopbackSrcIpPkt),
1776             DROPPED_IPV6_NS_INVALID
1777         )
1778 
1779         // Using scapy to generate IPv6 NS packet:
1780         // eth = Ether(src="00:01:02:03:04:05", dst="02:03:04:05:06:07")
1781         // ip6 = IPv6(src="2001::200:1a:1122:3344", dst="2001::200:1a:3344:1122", hlim=255)
1782         // icmp6 = ICMPv6ND_NS(tgt="2001::200:1a:3344:1122")
1783         // icmp6_opt1 = ICMPv6NDOptDstLLAddr(lladdr="01:02:03:04:05:06")
1784         // icmp6_opt2 = ICMPv6NDOptSrcLLAddr(lladdr="01:02:03:04:05:06")
1785         // pkt = eth/ip6/icmp6/icmp6_opt1/icmp6_opt2
1786         val sllaNotFirstOptionNsPkt = """
1787             02030405060700010203040586DD6000000000283AFF200100000000
1788             00000200001A1122334420010000000000000200001A334411228700
1789             2FFF0000000020010000000000000200001A33441122020101020304
1790             05060101010203040506
1791         """.replace("\\s+".toRegex(), "").trim()
1792         // non-DAD with multiple options, SLLA in 2nd option -> pass
1793         verifyProgramRun(
1794             apfFilter.mApfVersionSupported,
1795             program,
1796             HexDump.hexStringToByteArray(sllaNotFirstOptionNsPkt),
1797             PASSED_IPV6_NS_NO_SLLA_OPTION
1798         )
1799 
1800         // Using scapy to generate IPv6 NS packet:
1801         // eth = Ether(src="00:01:02:03:04:05", dst="02:03:04:05:06:07")
1802         // ip6 = IPv6(src="2001::200:1a:1122:3344", dst="2001::200:1a:3344:1122", hlim=255)
1803         // icmp6 = ICMPv6ND_NS(tgt="2001::200:1a:3344:1122")
1804         // icmp6_opt = ICMPv6NDOptDstLLAddr(lladdr="01:02:03:04:05:06")
1805         // pkt = eth/ip6/icmp6/icmp6_opt
1806         val noSllaOptionNsPkt = """
1807             02030405060700010203040586DD6000000000203AFF200100000000000002
1808             00001A1122334420010000000000000200001A3344112287003A1400000000
1809             20010000000000000200001A334411220201010203040506
1810         """.replace("\\s+".toRegex(), "").trim()
1811         // non-DAD with one option but not SLLA -> pass
1812         verifyProgramRun(
1813             apfFilter.mApfVersionSupported,
1814             program,
1815             HexDump.hexStringToByteArray(noSllaOptionNsPkt),
1816             PASSED_IPV6_NS_NO_SLLA_OPTION
1817         )
1818 
1819         // Using scapy to generate IPv6 NS packet:
1820         // eth = Ether(src="00:01:02:03:04:05", dst="02:03:04:05:06:07")
1821         // ip6 = IPv6(src="2001::200:1a:1122:3344", dst="2001::200:1a:3344:1122", hlim=255)
1822         // icmp6 = ICMPv6ND_NS(tgt="2001::200:1a:3344:1122")
1823         // icmp6_opt = ICMPv6NDOptSrcLLAddr(lladdr="01:02:03:04:05:06")
1824         // pkt = eth/ip6/icmp6/icmp6_opt
1825         val mcastMacSllaOptionNsPkt = """
1826             02030405060700010203040586DD6000000000203AFF200100000000
1827             00000200001A1122334420010000000000000200001A334411228700
1828             3B140000000020010000000000000200001A33441122010101020304
1829             0506
1830         """.replace("\\s+".toRegex(), "").trim()
1831         // non-DAD, SLLA is multicast MAC -> drop
1832         verifyProgramRun(
1833             apfFilter.mApfVersionSupported,
1834             program,
1835             HexDump.hexStringToByteArray(mcastMacSllaOptionNsPkt),
1836             DROPPED_IPV6_NS_INVALID
1837         )
1838     }
1839 
1840     // The APFv6 code path is only turned on in V+
1841     @IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
1842     @Test
1843     fun testNaTransmit() {
1844         val apfFilter = getApfFilter()
1845         val lp = LinkProperties()
1846         for (addr in hostIpv6Addresses) {
1847             lp.addLinkAddress(LinkAddress(InetAddress.getByAddress(addr), 64))
1848         }
1849 
1850         apfFilter.setLinkProperties(lp)
1851         val program = consumeInstalledProgram(ipClientCallback, installCnt = 3)
1852         val validIpv6Addresses = hostIpv6Addresses + hostAnycast6Addresses
1853         val expectPackets = mutableListOf<ByteArray>()
1854         for (addr in validIpv6Addresses) {
1855             // unicast solicited NS request
1856             val receivedUcastNsPacket = generateNsPacket(
1857                 senderMacAddress,
1858                 apfFilter.mHardwareAddress,
1859                 senderIpv6Address,
1860                 addr,
1861                 addr
1862             )
1863 
1864             verifyProgramRun(
1865                 apfFilter.mApfVersionSupported,
1866                 program,
1867                 receivedUcastNsPacket,
1868                 DROPPED_IPV6_NS_REPLIED_NON_DAD
1869             )
1870 
1871             val expectedUcastNaPacket = generateNaPacket(
1872                 apfFilter.mHardwareAddress,
1873                 senderMacAddress,
1874                 addr,
1875                 senderIpv6Address,
1876                 0xe0000000.toInt(), //  R=1, S=1, O=1
1877                 addr
1878             )
1879             expectPackets.add(expectedUcastNaPacket)
1880 
1881             val solicitedMcastAddr = NetworkStackUtils.ipv6AddressToSolicitedNodeMulticast(
1882                 InetAddress.getByAddress(addr) as Inet6Address
1883             )!!
1884             val mcastDa = NetworkStackUtils.ipv6MulticastToEthernetMulticast(solicitedMcastAddr)
1885                 .toByteArray()
1886 
1887             // multicast solicited NS request
1888             var receivedMcastNsPacket = generateNsPacket(
1889                 senderMacAddress,
1890                 mcastDa,
1891                 senderIpv6Address,
1892                 solicitedMcastAddr.address,
1893                 addr
1894             )
1895 
1896             verifyProgramRun(
1897                 apfFilter.mApfVersionSupported,
1898                 program,
1899                 receivedMcastNsPacket,
1900                 DROPPED_IPV6_NS_REPLIED_NON_DAD
1901             )
1902 
1903             val expectedMcastNaPacket = generateNaPacket(
1904                 apfFilter.mHardwareAddress,
1905                 senderMacAddress,
1906                 addr,
1907                 senderIpv6Address,
1908                 0xe0000000.toInt(), // R=1, S=1, O=1
1909                 addr
1910             )
1911             expectPackets.add(expectedMcastNaPacket)
1912         }
1913 
1914         val transmitPackets = consumeTransmittedPackets(expectPackets.size)
1915         for (i in transmitPackets.indices) {
1916             assertContentEquals(expectPackets[i], transmitPackets[i])
1917         }
1918     }
1919 
1920     // The APFv6 code path is only turned on in V+
1921     @IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
1922     @Test
1923     fun testNaTransmitWithTclass() {
1924         // mock nd traffic class from /proc/sys/net/ipv6/conf/{ifname}/ndisc_tclass to 20
1925         doReturn(20).`when`(dependencies).getNdTrafficClass(any())
1926         val apfFilter = getApfFilter()
1927         val lp = LinkProperties()
1928         for (addr in hostIpv6Addresses) {
1929             lp.addLinkAddress(LinkAddress(InetAddress.getByAddress(addr), 64))
1930         }
1931         apfFilter.setLinkProperties(lp)
1932         val program = consumeInstalledProgram(ipClientCallback, installCnt = 3)
1933         // Using scapy to generate IPv6 NS packet:
1934         // eth = Ether(src="00:01:02:03:04:05", dst="02:03:04:05:06:07")
1935         // ip6 = IPv6(src="2001::200:1a:1122:3344", dst="ff02::1:ff44:1122", hlim=255, tc=20)
1936         // icmp6 = ICMPv6ND_NS(tgt="2001::200:1a:3344:1122")
1937         // icmp6_opt = ICMPv6NDOptSrcLLAddr(lladdr="00:01:02:03:04:05")
1938         // pkt = eth/ip6/icmp6/icmp6_opt
1939         val hostMcastDstIpNsPkt = """
1940             02030405060700010203040586DD6140000000203AFF2001000000000000
1941             0200001A11223344FF0200000000000000000001FF4411228700952D0000
1942             000020010000000000000200001A334411220101000102030405
1943         """.replace("\\s+".toRegex(), "").trim()
1944         verifyProgramRun(
1945             apfFilter.mApfVersionSupported,
1946             program,
1947             HexDump.hexStringToByteArray(hostMcastDstIpNsPkt),
1948             DROPPED_IPV6_NS_REPLIED_NON_DAD
1949         )
1950 
1951         val transmitPkts = consumeTransmittedPackets(1)
1952         // Using scapy to generate IPv6 NA packet:
1953         // eth = Ether(src="02:03:04:05:06:07", dst="00:01:02:03:04:05")
1954         // ip6 = IPv6(src="2001::200:1a:3344:1122", dst="2001::200:1a:1122:3344", hlim=255, tc=20)
1955         // icmp6 = ICMPv6ND_NA(tgt="2001::200:1a:3344:1122", R=1, S=1, O=1)
1956         // icmp6_opt = ICMPv6NDOptDstLLAddr(lladdr="02:03:04:05:06:07")
1957         // pkt = eth/ip6/icmp6/icmp6_opt
1958         val expectedNaPacket = """
1959             00010203040502030405060786DD6140000000203AFF2001000000000000020
1960             0001A3344112220010000000000000200001A1122334488005610E000000020
1961             010000000000000200001A334411220201020304050607
1962         """.replace("\\s+".toRegex(), "").trim()
1963         assertContentEquals(
1964             HexDump.hexStringToByteArray(expectedNaPacket),
1965             transmitPkts[0]
1966         )
1967     }
1968 
1969     @Test
1970     fun testNdOffloadDisabled() {
1971         val apfConfig = getDefaultConfig()
1972         apfConfig.shouldHandleNdOffload = false
1973         val apfFilter = getApfFilter(apfConfig)
1974         val lp = LinkProperties()
1975         for (addr in hostIpv6Addresses) {
1976             lp.addLinkAddress(LinkAddress(InetAddress.getByAddress(addr), 64))
1977         }
1978 
1979         apfFilter.setLinkProperties(lp)
1980         val program = consumeInstalledProgram(ipClientCallback, installCnt = 3)
1981         val validIpv6Addresses = hostIpv6Addresses + hostAnycast6Addresses
1982         for (addr in validIpv6Addresses) {
1983             // unicast solicited NS request
1984             val receivedUcastNsPacket = generateNsPacket(
1985                 senderMacAddress,
1986                 apfFilter.mHardwareAddress,
1987                 senderIpv6Address,
1988                 addr,
1989                 addr
1990             )
1991 
1992             verifyProgramRun(
1993                 apfFilter.mApfVersionSupported,
1994                 program,
1995                 receivedUcastNsPacket,
1996                 PASSED_IPV6_ICMP
1997             )
1998 
1999             val solicitedMcastAddr = NetworkStackUtils.ipv6AddressToSolicitedNodeMulticast(
2000                 InetAddress.getByAddress(addr) as Inet6Address
2001             )!!
2002             val mcastDa = NetworkStackUtils.ipv6MulticastToEthernetMulticast(solicitedMcastAddr)
2003                 .toByteArray()
2004 
2005             // multicast solicited NS request
2006             var receivedMcastNsPacket = generateNsPacket(
2007                 senderMacAddress,
2008                 mcastDa,
2009                 senderIpv6Address,
2010                 solicitedMcastAddr.address,
2011                 addr
2012             )
2013 
2014             verifyProgramRun(
2015                 apfFilter.mApfVersionSupported,
2016                 program,
2017                 receivedMcastNsPacket,
2018                 PASSED_IPV6_ICMP
2019             )
2020         }
2021     }
2022 
2023     @IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
2024     @Test
2025     fun testRegisterOffloadEngine() {
2026         val apfConfig = getDefaultConfig()
2027         apfConfig.shouldHandleMdnsOffload = true
2028         val apfFilter = getApfFilter(apfConfig)
2029         val captor = ArgumentCaptor.forClass(OffloadEngine::class.java)
2030         verify(nsdManager).registerOffloadEngine(
2031                 eq(ifParams.name),
2032                 anyLong(),
2033                 anyLong(),
2034                 any(),
2035                 captor.capture()
2036         )
2037         val offloadEngine = captor.value
2038         val info1 = OffloadServiceInfo(
2039                 OffloadServiceInfo.Key("TestServiceName", "_advertisertest._tcp"),
2040                 listOf(),
2041                 "Android_test.local",
2042                 byteArrayOf(0x01, 0x02, 0x03, 0x04),
2043                 0,
2044                 OffloadEngine.OFFLOAD_TYPE_REPLY.toLong()
2045         )
2046         val info2 = OffloadServiceInfo(
2047                 OffloadServiceInfo.Key("TestServiceName2", "_advertisertest._tcp"),
2048                 listOf(),
2049                 "Android_test.local",
2050                 byteArrayOf(0x01, 0x02, 0x03, 0x04),
2051                 0,
2052                 OffloadEngine.OFFLOAD_TYPE_REPLY.toLong()
2053         )
2054         val updatedInfo1 = OffloadServiceInfo(
2055                 OffloadServiceInfo.Key("TestServiceName", "_advertisertest._tcp"),
2056                 listOf(),
2057                 "Android_test.local",
2058                 byteArrayOf(),
2059                 0,
2060                 OffloadEngine.OFFLOAD_TYPE_REPLY.toLong()
2061         )
2062         handler.post { offloadEngine.onOffloadServiceUpdated(info1) }
2063         handlerThread.waitForIdle(TIMEOUT_MS)
2064         assertContentEquals(listOf(info1), apfFilter.mOffloadServiceInfos)
2065         handler.post { offloadEngine.onOffloadServiceUpdated(info2) }
2066         handlerThread.waitForIdle(TIMEOUT_MS)
2067         assertContentEquals(listOf(info1, info2), apfFilter.mOffloadServiceInfos)
2068         handler.post { offloadEngine.onOffloadServiceUpdated(updatedInfo1) }
2069         handlerThread.waitForIdle(TIMEOUT_MS)
2070         assertContentEquals(listOf(info2, updatedInfo1), apfFilter.mOffloadServiceInfos)
2071         handler.post { offloadEngine.onOffloadServiceRemoved(updatedInfo1) }
2072         handlerThread.waitForIdle(TIMEOUT_MS)
2073         assertContentEquals(listOf(info2), apfFilter.mOffloadServiceInfos)
2074 
2075         handler.post { apfFilter.shutdown() }
2076         handlerThread.waitForIdle(TIMEOUT_MS)
2077         verify(nsdManager).unregisterOffloadEngine(any())
2078     }
2079 
2080     @Test
2081     fun testApfProgramUpdate() {
2082         val apfFilter = getApfFilter()
2083         consumeInstalledProgram(ipClientCallback, installCnt = 2)
2084         // add IPv4 address, expect to have apf program update
2085         val lp = LinkProperties()
2086         val linkAddress = LinkAddress(InetAddress.getByAddress(hostIpv4Address), 24)
2087         lp.addLinkAddress(linkAddress)
2088         apfFilter.setLinkProperties(lp)
2089         consumeInstalledProgram(ipClientCallback, installCnt = 1)
2090 
2091         // add the same IPv4 address, expect to have no apf program update
2092         apfFilter.setLinkProperties(lp)
2093         verify(ipClientCallback, never()).installPacketFilter(any())
2094 
2095         // add IPv6 addresses, expect to have apf program update
2096         for (addr in hostIpv6Addresses) {
2097             lp.addLinkAddress(LinkAddress(InetAddress.getByAddress(addr), 64))
2098         }
2099 
2100         apfFilter.setLinkProperties(lp)
2101         consumeInstalledProgram(ipClientCallback, installCnt = 1)
2102 
2103         // add the same IPv6 addresses, expect to have no apf program update
2104         apfFilter.setLinkProperties(lp)
2105         verify(ipClientCallback, never()).installPacketFilter(any())
2106 
2107         // add more tentative IPv6 addresses, expect to have apf program update
2108         for (addr in hostIpv6TentativeAddresses) {
2109             lp.addLinkAddress(
2110                 LinkAddress(
2111                     InetAddress.getByAddress(addr),
2112                     64,
2113                     IFA_F_TENTATIVE,
2114                     0
2115                 )
2116             )
2117         }
2118 
2119         apfFilter.setLinkProperties(lp)
2120         consumeInstalledProgram(ipClientCallback, installCnt = 1)
2121 
2122         // add the same IPv6 addresses, expect to have no apf program update
2123         apfFilter.setLinkProperties(lp)
2124         verify(ipClientCallback, never()).installPacketFilter(any())
2125     }
2126 
2127     @Test
2128     fun testApfFilterInitializationCleanUpTheApfMemoryRegion() {
2129         val apfFilter = getApfFilter()
2130         val programCaptor = ArgumentCaptor.forClass(ByteArray::class.java)
2131         verify(ipClientCallback, times(2)).installPacketFilter(programCaptor.capture())
2132         val program = programCaptor.allValues.first()
2133         assertContentEquals(ByteArray(4096) { 0 }, program)
2134     }
2135 
2136     @Test
2137     fun testApfFilterResumeWillCleanUpTheApfMemoryRegion() {
2138         val apfFilter = getApfFilter()
2139         consumeInstalledProgram(ipClientCallback, installCnt = 2)
2140         apfFilter.resume()
2141         val program = consumeInstalledProgram(ipClientCallback, installCnt = 1)
2142         assertContentEquals(ByteArray(4096) { 0 }, program)
2143     }
2144 
2145     @Test
2146     fun testApfIPv4MulticastAddrsUpdate() {
2147         val apfFilter = getApfFilter()
2148         // mock IPv4 multicast address from /proc/net/igmp
2149         val mcastAddrs = mutableListOf(
2150             InetAddress.getByName("224.0.0.1") as Inet4Address
2151         )
2152         consumeInstalledProgram(ipClientCallback, installCnt = 2)
2153 
2154         doReturn(mcastAddrs).`when`(dependencies).getIPv4MulticastAddresses(any())
2155         apfFilter.updateIPv4MulticastAddrs()
2156         consumeInstalledProgram(ipClientCallback, installCnt = 1)
2157         assertEquals(mcastAddrs.toSet(), apfFilter.mIPv4MulticastAddresses)
2158 
2159         val addr = InetAddress.getByName("239.0.0.1") as Inet4Address
2160         mcastAddrs.add(addr)
2161         doReturn(mcastAddrs).`when`(dependencies).getIPv4MulticastAddresses(any())
2162         apfFilter.updateIPv4MulticastAddrs()
2163         consumeInstalledProgram(ipClientCallback, installCnt = 1)
2164         assertEquals(mcastAddrs.toSet(), apfFilter.mIPv4MulticastAddresses)
2165 
2166         apfFilter.updateIPv4MulticastAddrs()
2167         verify(ipClientCallback, never()).installPacketFilter(any())
2168     }
2169 }
2170