赞
踩
前几篇介绍了iptables | 路由策略 | DNS等相关理论基础知识,现在在这基础上,去学习安卓的网络框架并应用这些知识点。Android的网络框架可以细分很多部分,按功能分的话,可以分网络评分与选择,apn管理,网络策略管理等;按层次分的话,可以分framework部分,native netd部分。现在需要定制一些iptables规则,然后应用到安卓源码。主要目的在于:
带着上面的两个目的,本文主要介绍netd和NetworkManagementService。弄懂这两部分的流程,就可以定制iptables规则,向上提供接口,管理网络相关的功能。
要学习安卓网络框架相关的知识,先确定你想通过它来达到什么目的。目前的想法就是做个demo app,实现下面的功能。
带着上面等待实现的小功能,开始学习安卓网络框架。
Network Daemon是Android系统中专门负责网络管理和控制的后台守护进程。它封装了底层的各种网络,隔离了底层网络接口的差异,给Framework提供了统一调用接口,简化了网络的使用。它通过netlink,虚拟文件系统等linux内核提供的用户接口,与内核通信,管理网络相关部分。主要功能有三部分:
netd位于framework层和kernel层中间,是Android系统中网络相关消息和命令转发及处理的中枢模块。netd的工作流程可分为两部分:
手机启动的过程会加载netd.rc文件,启动netd进程。文件如下所示:
android/system/netd/server/netd.rc
从这里看出,配置了几个socket(dnsproxyd, mdns, fwmarkd),netd进程初始化时会去监听这几个socket,上层可以通过这些socket和netd进行通信。
SOCKET | 描述 |
---|---|
dnsproxyd | DNS代理的控制与配置 |
mdns | 多播DNS,Multicast DNS |
fwmarkd | iptables的策略路由配置,流量打标签,设置网络权限等 |
接着看一下netd的main函数,代码删去部分细节,只留主逻辑,下图标出来的就是重点要学习的地方。
从这里看出,netd的main函数很简洁,主要是对rc文件中三个socket进行监听,并初始化NetlinkManager,NetdNativeService,Controller。接下来,对上面标出的几个类做详细的分析。
NetlinkManager主要负责接收并解析来自kernel的UEvent消息,这里不贴代码。它的初始化流程图如下所示:
从上面看出,它创建了四个不同协议的socket和对应的handler对象,每个socket都会对应的创建一个线程进行poll,监听不同协议的kernel事件。当有事件来,便会调用SocketListener的onDataAvailable方法,该方法内部会创建一个NetlinkEvent对象。NetlinkEvent对象根据socket创建时指定的解析类型去解析来自kernel的消息,最后onEvent方法被调用,不同类型的事件在这个方法中进行分类处理,它调用EventReporter通过binder的方式向framework层NetworkManagementService发起回调,调用对应的notifyXXX方法。下面是四个socket的描述:
协议 | 描述 |
---|---|
NETLINK_KOBJECT_UEVENT | 反映网络设备的事件和状态,如网口状态的变化 |
NETLINK_ROUTE | 接收路由信息, 如路由表的更新与删除 |
NETLINK_NFLOG | 接收数据流量使用配额的消息, 如数据使用超限 |
NETLINK_NETFILTER | 接收包过滤(netfilter)的消息 |
再来看一下NetlinkManager涉及到的几个类的类图,NetlinkManager->NetlinkHanler->NetlinkListener->SocketListener.
从这个类图看,NetlinkManager启动NetlinkHandler之后,就交给它去处理事件了。NetlinkHandler继承NetlinkListener, NetlinkListener继承SocketListener。SocketListener主要是处理socket相关的逻辑,起线程监听kernel事件。NetlinkListener主要是创建NetlinkEvent,把读取到的事件消息放在这里进行解析。最后NetlinkHandler负责把解析后的消息,传递给上层framework。可以看到每个类都有自己的业务逻辑,清晰明了。
Controllers顾名思义,是一系列的控制器,包括防火墙,流量统计,带宽,路由表等功能的控制器;它的初始化流程如下图所示。
可以看到,Controllers的主要作用就是创建了很多规则子链,用来控制网络相关的功能。比较老的android版本,都是通过iptables创建规则链来实现这些功能的;但是android引入了bpf之后,很多都是通过bpf来实现,包括数据包的过滤和拦截,app的上网控制等。像之前的fw_dozable,fw_standby,fw_powersave规则子链,都没有创建了,改成采用bpf过滤的方式。而bpf的相关功能实现,是通过TrafficController关联bpf来实现的。
Controllers的成员变量,包括了下面的各个controller:
控制器 | 描述 |
---|---|
NetworkController | 保存网络信息,包括NetId | 接口 | IP地址 |
TetherController | 网络设备绑定,如通过USB共享网络 |
PppController | tty终端绑定pppd(点对点协议的守护进程,在使用VPN时才需要使用它) |
BandwidthController | 带宽控制,实际是监控系统数据流量,流量阈值提醒,流量使用上限,限制app后台流量 |
IdletimerController | 监听网口在设定时间内没有数据通过时,上报一个netlink事件 |
FirewallController | 防火墙功能,制定防火墙规则 |
ClatdController | IPv4-IPv6 地址的转换器(不确定) |
StrictController | 检测app运行网络通信是否进行SSL/TLS数据加密 |
EventReporter | 负责事件上报到framework,提供注册接口,保存Listener |
IptablesRestoreController | 负责执行iptables命令 |
WakeupController | 不懂 |
XfrmController | 与Ipsec相关 |
TrafficController | 与eBPF结合,负责对数据包过滤,流量统计,按UID划分防火墙等 |
TcpSocketMonitor | 定时监听物理网络tcp相关信息,包括发包/丢包数目,rtt等等 |
通过上面这个表格,对这些controller我们有了初步的了解,知道它大致的功能,日后需要调查具体的细节,在回过头去查找就可以。也看到了iptables规则的创建,就在这些controller之中。由此推测,假如需要定制一些网络功能,那么可能要在这些controller中增加iptables规则。
FwmarkServer的作用就是给数据打标签。从前面的netd.rc文件中已经配置socket_name为fwmarkd,netd进程会去初始化,进行监听。请看下面流程图:
从这里看出,主要的逻辑是在processClient中进行处理。其中的逻辑包括,在socket进行connect时,accept完成时,根据具体条件对socket的fwmark进行设置(fwmark包括permission,netid两部分)。这其实是对socket相关系统函数(connect|accept)进行hook,在hook函数中通过链接到FwmarkServer进行打标签。Hook流程和数据进来打标签可以参考:Android 策略路由
NetdNativeService是一个binder服务,它是framework层和HAL层通信的媒介。它发布一个netd服务供framework层使用,实现INetd.aidl接口。
system\netd\server\binder\android\net\INetd.aidl
NetdNativeService的功能实际上是通过调用Controller去实现的,所以说它只是通信的媒介,具体功能实现是放在Controller。那么想要增加新的接口,似乎可以在这里增加。
下面是它的初始化流程:
NetworkManagementService在SystemServer进程中启动,它主要用于获取netd服务和native层通信,对网络进行管理。注册监听事件(unsolicited event listener)到netd,用于native上报网络事件。同时获取IOemNetd实例,实际它是一系列接口,如果要新增接口,可以放在这个IOemNetd对象里面。而NetworkManagementService本身也是一个服务,SystemServer把它发布到系统中,供其他模块使用,服务名是"network_management"。
下面是它的初始化流程
从上面看出,启动后时主要做了几件事
到这里,初步可以回答本文开头的问题了。
iptables相关的规则,需要在native netd中添加,可以参考controller中的实现。
增加接口,需要在framework的NetworkManagementService添加;也需要在native层的INetd.aidl/IOemNetd.aidl中添加,在native为了方便区分系统自带的接口和自定义接口,统一放在IOemNetd.aidl这里添加便可以。上层通过获取netd服务,调用接口
IOemNetd.Stub.asInterface(mNetdService.getOemNetd());
得到IOemNetd的实例OemNetdListener,再调用具体接口。实现IOemNetd.aidl的具体对象如下:
system\netd\server\OemNetdListener.cpp
这里可以简单分三步,按照下面的步骤去实现功能就行。
netd 和 framework 涉及修改的文件如下:
system\netd\server\binder\com\android\internal\net\IOemNetd.aidl
system\netd\server\OemNetdListener.h
system\netd\server\OemNetdListener.cpp
frameworks\base\core\java\android\os\INetworkManagementService.aidl
frameworks\base\services\core\java\com\android\server\NetworkManagementService.java
其中接口实现是放在OemNetdListener.cpp。framework增加相应的接口给APK调用。
三个接口功能不同,先创建对应的三个规则子链;然后在子链中添加规则,实现具体功能。netd和framework同步增加接口,一一对应。所以增加的接口如下所示:
oneway void makeSeaiceChildChain(); //创建子链 oneway void setIdleType(); //清空规则 oneway void setBlackTypeIpList(in @utf8InCpp String addr); //黑名单模式 oneway void setWhiteTypeIpList(in @utf8InCpp String addr); //白名单模式 oneway void setBlockDns(boolean enable); //屏蔽查询DNS与否
- 1
- 2
- 3
- 4
- 5
接口的实现主要就是添加iptables规则,如何添加可以参考FirewallController现成的代码(@makeUidRules)。接口实现在下面这个文件:
netd\server\OemNetdListener.cpp
makeSeaiceChildChain的实现代码如下,它是在启动的时候调用一次,可以在NetworkManagementServer的systemReady调用,创建出三个子链。
::android::binder::Status OemNetdListener::makeSeaiceChildChain() { int res = -1; IptablesTarget target = V4V6; std::string command = "*filter\n"; std::vector<std::string> seaiceChains; //子链 seaiceChains = {WHITE_NAME_TYPE, BLACK_NAME_TYPE, REJECT_DNS_QUERY}; for(std::string& chain : seaiceChains) { ::android::base::StringAppendF(&command, "-N %s\n", chain.c_str()); } ::android::base::StringAppendF(&command, "COMMIT\n"); res = execIptablesRestore(target, command.c_str()); if(res == 0) { return ::android::binder::Status::ok(); } else { return ::android::binder::Status::fromServiceSpecificError(res, ::android::String8::format("makeSeaiceChildChain error: %d", res)); } }
setBlackTypeIpList接口的实现代码如下:
::android::binder::Status OemNetdListener::setBlackTypeIpList(const std::string& addr) { std::lock_guard lock(mMutex); //check chain exist or not int res = -1; IptablesTarget target = V4V6; std::string command = "*filter\n"; ::android::base::StringAppendF(&command, "-C OUTPUT -j %s\n", BLACK_NAME_TYPE); ::android::base::StringAppendF(&command, "COMMIT\n"); res = execIptablesRestore(target, command.c_str()); //clean chain command = "*filter\n"; if (!res) { ::android::base::StringAppendF(&command, "-D OUTPUT -j %s\n", BLACK_NAME_TYPE); } ::android::base::StringAppendF(&command, "-F %s\n", BLACK_NAME_TYPE); ::android::base::StringAppendF(&command, "-A OUTPUT -j %s\n", BLACK_NAME_TYPE); std::stringstream ss(addr); std::string ipAddr; while(std::getline(ss, ipAddr, '-')) { ::android::base::StringAppendF(&command, "-A %s -d %s -j DROP\n", BLACK_NAME_TYPE, ipAddr.c_str()); } ::android::base::StringAppendF(&command, "-A %s -p all -j RETURN\n", BLACK_NAME_TYPE); ::android::base::StringAppendF(&command, "COMMIT\n"); res = execIptablesRestore(target, command.c_str()); if(res == 0) { return ::android::binder::Status::ok(); } else { return ::android::binder::Status::fromServiceSpecificError(res, ::android::String8::format("makeSeaiceChildChain error: %d", res)); } }
其他接口的实现不再一一列举。
NetworkManagementService的接口并不对外公开,不是标准的SDK API,所以需要编写一个需要系统权限的APK。这里主要涉及如何编译一个具有系统权限的APK。参考其他系统APK,主要在于编写正确的android.bp, 然后通过系统编译。如下:
package { // See: http://go/android-license-faq // A large-scale-change added 'default_applicable_licenses' to import // all of the 'license_kinds' from "packages_services_Telephony_license" // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 // default_applicable_licenses: ["packages_services_Telephony_license"], } android_app { name: "seaiceNetApp", srcs: [ "java/**/*.java", ], static_libs: [ "androidx-constraintlayout_constraintlayout", "androidx.cardview_cardview", "androidx.appcompat_appcompat", ], //platform:平台的核心应用签名,签名的apk是完成系统的核心功能。 //这些apk所在的进程UID是system。manifest节点中有添加android:sharedUserId="android.uid.system"。 certificate: "platform", //设置该标记后会使用sdk的hide的api來编译。编译的APK中使用了系统级API,必须设定该值。 //和Android.mk中的LOCAL_PRIVATE_PLATFORM_APIS的作用相同 platform_apis: true, }
以上要主要理解和定义这两个值certificate和platform_apis,才能正确编译。其他的不打算贴代码了,都是一些UI上的逻辑。最后的效果如下所示:
深入理解Android系统网络架构
Android 系统网络框架
Android 框架实现分析 - 网络 - Native层
Netd中的“netd”套接字
Android 策略路由
Android系统中iptables的应用(五)IdlertimerController
Android 6.0 StrictController
Android 10 路由添加原理
Android系统签名简介
About LOCAL_PRIVATE_PLATFORM_APIS in Android.mk
针对非 SDK 接口的限制
Android中的 eBPF 流量监控
速查 | Android 系统目录功能
Android Doze and App Standby模式详解
重看ebpf -代码载入执行点-hook
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。