BandwidthController字面意思是帶寬控制,實際模塊實現的是監控系統數據流量使用情況的,主要分爲流量閾值提醒(alert),流量使用上限(limite),限制app後臺流量等。因android系統演進,最初設計的命令和相關功能已經不再使用,所以,如下是在android5.0系統中,據framework層保留在用的API,得出全部在用的bandwidth相關Netd cmd
接下來根據系統啓動,到相關功能使用時code運行流程和iptables規則的改動來分析,只對相關功能的enable進行分析,相對應的disable的反向操作省略。
bandwidth enable|disable removeiquota|riq<iface> setiquota|sq<interface><bytes> addnaughtyapps|ana<appUid...> removenaughtyapps|rna<appUid...> setgolbalalert|sga<bytes>cd .. setinterfacealert|sia<iface><bytes> removeinterfacealert|ria<iface> gettetherstats|gts<iface0><iface1>
(1)Netd.CommanderListener初始化後
(2)Framework SystemReady後filter表:createChildChains(V4V6, "filter", "INPUT", FILTER_INPUT); createChildChains(V4V6, "filter", "FORWARD", FILTER_FORWARD); createChildChains(V4V6, "filter", "OUTPUT", FILTER_OUTPUT); sBandwidthCtrl->setupIptablesHooks(); sBandwidthCtrl->enableBandwidthControl(false);
//分別創建3條子鏈,位置正是filter表可掛的INPUT/OUTPUT/FORWARD三條鏈
-N bw_FORWARD
-N bw_INPUT
-N bw_OUTPUT
//創建3條子鏈,costly表付費iface一般就是常規的數據流量,
//happy_box與penalty_box規則就是針對限制app的UID的黑白名單規則,
//happy_box已不再使用,shared這條鏈原本是用做各條costly_[iface]鏈彙總路線
-N bw_costly_shared
-N bw_happy_box
-N bw_penalty_box
//添加默認jump規則,保證創建的子鏈中的規則被遍歷
-A INPUT -j bw_INPUT
-A FORWARD -j bw_FORWARD
-A OUTPUT -j bw_OUTPUT
//tracking rule沒啥作用
-A bw_INPUT -m owner --socket-exists
-A bw_OUTPUT -m owner --socket-exists
mangle表:
//tracking rule
-N bw_mangle_POSTROUTING
-A POSTROUTING -j bw_mangle_POSTROUTING
-A bw_mangle_POSTROUTING -m owner –socket-exists
raw表:
//tracking rule
-N bw_raw_PREROUTING
-A PREROUTING -j bw_raw_PREROUTING
-A bw_raw_PREROUTING -m owner --socket-exists
(3)開啓手機的流量:會在filter表新增2條規則:sBandwidthCtrl->enableBandwidthControl(true); sBandwidthCtrl->setGlobalAlert([argv]);
-A bw_INPUT -m quota2 ! --name globalAlert --quota 2097152
-A bw_OUTPUT -m quota2 ! --name globalAlert --quota 2097152
默認2M(2097152)的alert流量計數,動態記錄在/proc/net/xt_quota/globalAlert中,隨經過該鏈的的數據包增加計數遞減,減到0後觸發netlink事件,netd來handle並上報給framework,觸發framework去pollstate並重新更新規則中的alert計數,這兩條規則的意義並不是策略性,只是循環觸發framework和native的相關記錄func的update。
(4)在setting設置界面開啓流量超值提醒(alert):在filter表中新增加表&規則:sBandwidthCtrl->setInterfaceQuota([iface], [java.Long.MAX_value = 9223372036854775807]);
-N bw_costly_ccmni0
-A bw_FORWARD -o ccmni0 -j bw_costly_ccmni0
-A bw_INPUT -i ccmni0 -j bw_costly_ccmni0
-A bw_OUTPUT -o ccmni0 -j bw_costly_ccmni0
-A bw_costly_ccmni0 -j bw_penalty_box
-A bw_costly_ccmni0 -m quota2 ! --name ccmni0 --quota 9223372036854775807 -j REJECT --reject-with icmp-port-unreachable
以上新增加的規則實現了,經過ccmni0的數據統計,要經過鏈bw_penalty_box ,爲實現黑名單的功能鋪墊,經過ccmni0的數據超過 9223372036854775807,將會直接reject掉數通過該鏈的所有據包。這個計數記錄在/proc/net/xt_quota/ccmni0中,是int64最大值,很難到達,而且計數會一直被framework來更新回2的64方,所以不用擔心reject被觸發。
(5)在setting設置界面開啓流量超值提醒(limite):在filter表中新增加規則:sBandwidthCtrl->setGlobalAlert([argv]);
-A bw_INPUT -m quota2 ! --name globalAlert --quota 131072
-A bw_OUTPUT -m quota2 ! --name globalAlert --quota 131072
只是更改了globalAlert的數值,從默認的2M改爲經過邏輯計算後的數值,以setting中選擇的值爲輸入,經過如下framework中的邏輯MathUtils.constrain(setting_val/1000,128KB,2MB)轉換後,寫入globalAlert中。例如設置20M超值提醒,globalAlert設置爲128K,設置1G提醒,globalAlert設爲1M。計算的邏輯是設置恰當的閾值節點觸發framework和native中相關記錄的循環update。在update的過程中framework會去比較setting中設置的流量閾值和當前流量使用值(記錄在/proc/net/xt_qtaguid下)
(6)在setting中禁用個別應用的後臺流量(naughtapps):在filter表中新增加規則:sBandwidthCtrl->setInterfaceQuota([iface], argv));
-A bw_costly_ccmni1 -m quota2 ! --name ccmni1 --quota 25694376 -j REJECT --reject-with icmp-port-unreachable
加入流量上限規則,本例中設置25M上限,超過後流量不可以使用,任何經過該鏈的數據包都會被reject掉,具體是將封包protocal改爲icmp的port-unreachable並返回給發送端。
關於android的擴展match功能可以quota2的幫助文檔,參考如下手冊:在filter表中新增加規則:sBandwidthCtrl->addNaughtyApps([appUids]...);
-A bw_penalty_box -m owner --uid-owner 10023 -j REJECT --reject-with icmp-port-unreachable
這裏針對一個或者多個UID參數來禁用個別應用的後臺數據使用,關於所有應用的UID信息可以在package.xml中查:/data/system/packages.xml,注意,以上這條規則是禁用全部由UID=10023數據流量請求,那麼關於應用是否處於foreground/background是由framework進行判斷的,也就是當應用處於foreground的時候,這條規則會被remove掉,不用擔心應用無法使用流量。
$ adb shell iptables -m -quota2 -h
quota match options:
--grow provide an increasing counter
--no-change never change counter/quota value for matching packets
--name name name for the file in sysfs
[!] --quota quota initial quota (bytes or packets)
--packets count packets instead of bytes
$ adb shell iptables -m owner -h
owner match options:
[!] --uid-owner userid[-userid] Match local UID
[!] --gid-owner groupid[-groupid] Match local GID
[!] --socket-exists Match if socket exists