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