Android 封包篩選器

Android Packet Filter (APF) 可讓架構控制執行階段的硬體封包篩選邏輯。如此一來,系統就可以透過捨棄硬體中的封包來節省電力,同時允許 Android 架構在執行階段根據網路條件變更篩選規則。

APF 總覽

APF 由兩個主要元件組成:

  • APF 解譯器會在網路硬體 (通常是 Wi-Fi 晶片組) 上執行。APF 解譯器會在硬體接收的封包上執行 APF 位元碼,並決定是否接受或捨棄這些位元碼。
  • APF 程式產生程式碼會在主要 CPU 上執行。程式碼會根據網路和裝置狀態建立及更新 APF 程式。

Wi-Fi HAL 方法可讓 Android 架構安裝 APF 程式位元碼,並讀取目前的計數器。網路堆疊 Mainline 模組可在 APF 執行時隨時更新 APF 程式位元碼。

我們已導入數種 APF 篩選器。舉例來說,APF 的篩選器可捨棄不允許的乙太網路、篩選 IPv6 路由器通告 (RA) 封包、在未保留多點傳播鎖定的情況下篩選多點傳送和廣播流量、捨棄其他主機的 DHCP 封包,以及捨棄不請自來的位址解析通訊協定 (ARP) 與 (鄰近探索) ND 封包。完整的篩選器清單是在 ApfFilter 中定義。

由於 APF 程式產生程式碼是網路堆疊模組的一部分,因此只需每月 Mainline 更新,即可更新篩選邏輯並新增篩選器。

APF 整合

apf_interpreter.h 中定義了 APF API。Wi-Fi 韌體程式碼會呼叫 int accept_packet(),判斷是否應捨棄封包 (傳回值零) 或傳遞 (非零傳回值)。APF 指示長度不一。每個指示的長度至少為一個位元組。APF 指令代碼定義於 apf.h

APF 需要專用記憶體。這些記憶體會用於 APF 程式本身和用於資料儲存,且除非透過 APF HAL 方法,否則晶片組不得清除或寫入記憶體。APF 位元碼會使用資料儲存空間來儲存接受和已捨棄封包的計數器。資料地區可從 Android 架構讀取。APF 可用的記憶體容量下限為 1024 位元組,

對 APF 進行偵錯

如要檢查裝置是否已啟用 APF,顯示目前的程式及顯示目前的計數器,請執行 adb shell dumpsys network_stack 指令。這個指令的範例如下:

adb shell dumpsys network_stack
......
IpClient.wlan0 APF dump:
    Capabilities: ApfCapabilities{version: 4, maxSize: 4096, format: 1}
......
    Last program:
      6bfcb03a01b8120c6b9494026506006b907c025e88a27c025988a47c025488b87c024f88cd7c024a88e17c024588e384004408066a0e6bdca4022b000600010800060412147a1e016bd884021f00021a1c6b8c7c021c0000686bd4a402080006ffffffffffff6a266bbca402010004c0a801eb6bf87401f6120c84005f08000a17821f1112149c00181fffab0d2a108211446a3239a20506c2fc393057dd6bf47401cb0a1e52f06bac7c01c600e06bb41a1e7e000001b9ffffffff6bb07e000001aec0a801ff6be868a4019a0006ffffffffffff6bb874019b6bf07401907c001386dd686bd0a4017d0006ffffffffffff6bc874017e0a147a0e3a6b980a267c017000ff6be07401650a366ba87c016200858219886a26a2050fff02000000000000000000000000006ba4740146aa0e84013700e6aa0f8c0130006068a4011b000f33330000000184c9b26aed4c86dd606a12a2f02600b03afffe8000000000000086c9b2fffe6aed4cff02000000000000000000000000000186006a3aa2e9024000123c92e4606a3ea2d70800000000000000006a56a2ce04030440c01a5a92c9601a5e92c4606a62a2bb04000000006a66a2a6102401fa00049c048400000000000000006a76a29d04030440c01a7a9298601a7e9293606c0082a28904000000006c0086a27310fdfd9ed67950000400000000000000006c0096a2690418033c001a9a9264606c009ea24e102401fa00049c048000000000000000006c00aea24404180330001ab2923f606c00b6a22910fdfd9ed67950000000000000000000006c00c6a21f04190300001aca921a606c00cea20410fdfd9ed67950000400000000000000016bc472086be4b03a01b87206b03a01b87201
    APF packet counters:
      TOTAL_PACKETS: 469
      PASSED_DHCP: 4
      PASSED_IPV4: 65
      PASSED_IPV6_NON_ICMP: 64
      PASSED_IPV4_UNICAST: 64
      PASSED_IPV6_ICMP: 223
      PASSED_IPV6_UNICAST_NON_ICMP: 6
      PASSED_ARP_UNICAST_REPLY: 4
      PASSED_NON_IP_UNICAST: 1
      DROPPED_RA: 4
      DROPPED_IPV4_BROADCAST_ADDR: 7
      DROPPED_IPV4_BROADCAST_NET: 27

這個範例 adb shell dumpsys network_stack 指令的輸出內容包括下列項目:

  • ApfCapabilities{version: 4, maxSize: 4096, format: 1}:這表示 Wi-Fi 晶片支援 APF (第 4 版)。
  • Last program:本節是最新安裝的 APF 程式二進位檔,採十六進位字串格式。
  • APF packet counters:這個部分顯示 APF 傳遞或捨棄的封包數量,以及具體原因。

如要將程式碼解碼及拆解為人類可讀的組合語言,請使用 apf_disassembler 工具。如要編譯可執行的二進位檔,請執行 m apf_disassembler 指令。以下範例說明如何使用 apf_disassembler 工具。

echo "6bfcb03a01b8120c6b949401e906006b907c01e288a27c01dd88a47c01d888b87c01d388cd7c01ce88e17c01c988e384004008066a0e6bdca401af000600010800060412147a1e016bd88401a300021a1c6b8c7c01a00000686bd4a4018c0006ffffffffffff1a266bc07c018900006bf874017e120c84005408000a17821f1112149c00181fffab0d2a108211446a3239a205065a56483ac3146bf47401530a1e52f06bac7c014e00e06bb41a1e7e00000141ffffffff6be868a4012d0006ffffffffffff6bb874012e6bf07401237c001386dd686bd0a401100006ffffffffffff6bc87401110a147a0d3a6b980a267c010300ff6be072f90a366ba87af8858218886a26a2040fff02000000000000000000000000006ba472ddaa0e82d0aeaa0f8c00c9025868a2b60f5a56483ac3140c8126f3895186dd606a12a28b2600783afffe8000000000000002005efffe00026fff02000000000000000000000000000186006a3aa284024000123c94007d02586a3ea2700800000000000000006a56a26704190500001a5a94006002586a5ea23b2020014860486000000000000000006464200148604860000000000000000000646a7ea23204030440c01a8294002b02581a8694002402586c008aa21a04000000006c008ea204102a0079e10abcf60500000000000000006bc472086be4b03a01b87206b03a01b87201" | out/host/linux-x86/bin/apf_disassembler
       0: li    r1, -4
       2: lddw  r0, [r1+0]
       3: add   r0, 1
       5: stdw  r0, [r1+0]
       6: ldh   r0, [12]
       8: li    r1, -108
      10: jlt   r0, 0x600, 504
      15: li    r1, -112
      17: jeq   r0, 0x88a2, 504
      22: jeq   r0, 0x88a4, 504
      27: jeq   r0, 0x88b8, 504
      32: jeq   r0, 0x88cd, 504
      37: jeq   r0, 0x88e1, 504
      42: jeq   r0, 0x88e3, 504
      47: jne   r0, 0x806, 116
......

如要離線查看 APF 結果,請使用 apf_run 工具。如要編譯可執行的二進位檔,請執行 m apf_run 指令。以下範例說明如何使用 apf_run 指令檢查單一封包。

如要提供原始封包的十六進位二進位字串呈現方式,請使用 --packet 選項。如要提供資料地區的十六進位二進位字串,用來儲存 APF 計數器,請使用 --data option。由於每個計數器的長度為 4 個位元組,資料地區的長度要夠長,才能確保不會發生緩衝區溢位。

out/host/linux-x86/bin/apf_run --program 6bfcb03a01b8120c6b9494010c06006b907c010588a27c010088a47c00fb88b87c00f688cd7c00f188e17c00ec88e384003908066a0e6bdca2d40600010800060412147a18016bd882ca021a1c6b8c7ac900686bd4a2b706ffffffffffff6a266bbca2b204c0a814656bf872a8120c84005808000a17821e1112149c00171fffab0d2a108210446a3239a204064651dbcc88ff6bf4727e0a1e52f06bac7a7be06bb41a1e7e0000006effffffff6bb07e00000063c0a814ff6be868a25106ffffffffffff6bb872536bf072497c001086dd686bd0a23806ffffffffffff6bc8723a0a147a0b3a6b980a267a2eff6be072240a366ba87a23858218886a26a2040fff02000000000000000000000000006ba472086be4b03a01b87206b03a01b87201 --packet 5ebcd79a8f0dc244efaab81408060001080006040002c244efaab814c0a8ca1e5ebcd79a8f0d --data 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Packet passed
Data: 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000001

如要依據 tcpdump 擷取的 pcap 檔案檢查 APF 結果,請使用 apf_run 指令,如下所示:

out/host/linux-x86/bin/apf_run --program 6bfcb03a01b8120c6b989401df06006b947c01d888a27c01d388a47c01ce88b87c01c988cd7c01c488e17c01bf88e384004408066a0e6bdca401a5000600010800060412147a1e016bd884019900021a1c6b907c01960000686bd4a401820006ffffffffffff6a266bc0a4017b0004c0a82b056bf874017084005f08000a17821f1112149c00181fffab0d2a108211446a3239a20506fabe589435936bf47401470a1e52f06bb07c014200e06bb81a1e7e00000135ffffffff6bb47e0000012ac0a82bff6be868a401160006ffffffffffff6bbc7401176bf074010c7c001086dd686bd0a2fb06ffffffffffff6bcc72fd0a147a0b3a6b9c0a267af1ff6be072e70a366bac7ae6858218886a26a2040fff02000000000000000000000000006ba872cbaa0e82be8eaa0f8c00b7025868a2a40ffabe5894359352a9874d08aa86dd606a12a2792600583afffe80000000000000f7d4e8ccd81ddb43fe80000000000000f8be58fffe94359386006a3aa272024108123c94006b02586a3ea25e0800000000000000006a56a25504030440c01a5a94004e02581a5e94004702586a62a23e04000000006a66a229102409891f9a26ae6d00000000000000006a76a22004190300001a7a94001902586a7ea204102409891f9a26ae6dba98e781ca9ef9ba6bc872086be4b03a01b87206b03a01b87201 --pcap apf.pcap --data 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
37 packets dropped
1733 packets passed
Data: 00000000000000000000000000000000000000000200000005000000000000000000000002000000000000001b000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000689000000000000003c00000000000000000000000000000000000006ea