前言

前几篇介绍了iptables | 路由策略 | DNS等相关理论基础知识,现在在这基础上,去学习安卓的网络框架并应用这些知识点。Android的网络框架可以细分很多部分,按功能分的话,可以分网络评分与选择,apn管理,网络策略管理等;按层次分的话,可以分framework部分,native netd部分。现在需要定制一些iptables规则,然后应用到安卓源码。主要目的在于:

  • iptables规则是在android源码的哪个地方添加?
  • 如何给上层提供网络相关的接口,涉及到framework/native哪部分代码?

带着上面的两个目的,本文主要介绍netdNetworkManagementService。弄懂这两部分的流程,就可以定制iptables规则,向上提供接口,管理网络相关的功能。

基本需求

要学习安卓网络框架相关的知识,先确定你想通过它来达到什么目的。目前的想法就是做个demo app,实现下面的功能。

  • 显示当前网络信息,网口名称/IP地址/DNS地址/连接状态等。
  • 建立白名单模式和黑名单模式,允许/屏蔽某个IP地址访问网络。
  • 查询网址的IP地址,方便我们屏蔽或放行。
  • 提供屏蔽/允许DNS查询功能。

带着上面等待实现的小功能,开始学习安卓网络框架。

netd(Network Daemon)

基本概念

Network Daemon是Android系统中专门负责网络管理和控制的后台守护进程。它封装了底层的各种网络,隔离了底层网络接口的差异,给Framework提供了统一调用接口,简化了网络的使用。它通过netlink,虚拟文件系统等linux内核提供的用户接口,与内核通信,管理网络相关部分。主要功能有三部分:

  • 设置防火墙(Firewall),网络地址转换(NAT),带宽控制,流量统计,无线网上软接入点(soft acess point)控制,网络设备绑定(Tether),配置路由表,interface管理等。
  • Android系统中的DNS信息缓存和管理。
  • 网络服务搜索(Net Service Discovery, NSD)功能。

netd位于framework层和kernel层中间,是Android系统中网络相关消息和命令转发及处理的中枢模块。netd的工作流程可分为两部分:

  • netd接收并处理来自framework层的NetworkManagementService或NsdService的命令。
  • netd接收并解析来自kernel的UEvent消息,然后再转发给framework层对应的service去处理。

启动与初始化

手机启动的过程会加载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进行监听,并初始化NetlinkManagerNetdNativeServiceController。接下来,对上面标出的几个类做详细的分析。

NetlinkManager

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继承SocketListenerSocketListener主要是处理socket相关的逻辑,起线程监听kernel事件。NetlinkListener主要是创建NetlinkEvent,把读取到的事件消息放在这里进行解析。最后NetlinkHandler负责把解析后的消息,传递给上层framework。可以看到每个类都有自己的业务逻辑,清晰明了。

Controllers

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

FwmarkServer的作用就是给数据打标签。从前面的netd.rc文件中已经配置socket_namefwmarkd,netd进程会去初始化,进行监听。请看下面流程图:

从这里看出,主要的逻辑是在processClient中进行处理。其中的逻辑包括,在socket进行connect时,accept完成时,根据具体条件对socket的fwmark进行设置(fwmark包括permission,netid两部分)。这其实是对socket相关系统函数(connect|accept)进行hook,在hook函数中通过链接到FwmarkServer进行打标签。Hook流程和数据进来打标签可以参考Android 策略路由

NetdNativeService

NetdNativeService是一个binder服务,它是framework层和HAL层通信的媒介。它发布一个netd服务供framework层使用,实现INetd.aidl接口。

system\netd\server\binder\android\net\INetd.aidl

NetdNativeService的功能实际上是通过调用Controller去实现的,所以说它只是通信的媒介,具体功能实现是放在Controller。那么想要增加新的接口,似乎可以在这里增加。

下面是它的初始化流程:

NetworkManagementService

NetworkManagementService在SystemServer进程中启动,它主要用于获取netd服务和native层通信,对网络进行管理。注册监听事件(unsolicited event listener)到netd,用于native上报网络事件。同时获取IOemNetd实例,实际它是一系列接口,如果要新增接口,可以放在这个IOemNetd对象里面。而NetworkManagementService本身也是一个服务,SystemServer把它发布到系统中,供其他模块使用,服务名是"network_management"。

下面是它的初始化流程

从上面看出,启动后时主要做了几件事

  1. 获取netd服务,通过它和native层通信
  2. 注册listener到native层监听网络事件
  3. 获取IOemNetd对象,是一系列接口,扩展用
  4. 发布自身服务到系统

到这里,初步可以回答本文开头的问题了。

  • 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

方案实现

这里可以简单分三步,按照下面的步骤去实现功能就行。

  1. 设计接口(netd & framework)
  2. 实现接口(iptables规则)
  3. 编写APK(UI界面,编译源码)

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调用。

设计接口

  1. 黑名单模式屏蔽IP地址
  2. 白名单模式屏蔽IP地址
  3. 屏蔽DNS的开关

三个接口功能不同,先创建对应的三个规则子链;然后在子链中添加规则,实现具体功能。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与否

实现接口

接口的实现主要就是添加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 notint 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 chaincommand = "*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));}}

其他接口的实现不再一一列举。

编写APK

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,
}

以上要主要理解和定义这两个值certificateplatform_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

Android中如何屏蔽IP地址相关推荐

  1. Android查询网关地址,在android中获取网关IP地址

    我想将此答案发布为更新近期Android版本(CM11 / KitKat / 4.4.4)用户的更新.我还没有使用TouchWiz或更老的Android版本来测试任何这些YMMV. 可以在所有常用位置 ...

  2. Android动态屏蔽IP地址

    我们想实现这样一个需求:当Android设备假待机(屏幕休眠)的时候,屏蔽某些IP地址,唤醒时解除屏蔽.同时要屏蔽的IP可以动态配置. 一.如何屏蔽IP地址 这里就要使用到iptables防火墙工具. ...

  3. Nginx 屏蔽ip地址的方法

    转载来源 ; Nginx 屏蔽ip地址的方法 : http://www.safebase.cn/article-258999-1.html 摘要: 在网站维护过程中,有时候我们需要对一些IP地址或是一 ...

  4. android 输入ip地址,我应该使用什么android:inputType输入IP地址?

    我应该使用什么android:inputType输入IP地址? 我正在构建一个小型Android应用程序,用户将在其中将IP地址或主机名输入到EditText小部件中. 他们有90%的时间将输入IP地 ...

  5. 3.请执行命令取出linux中eth0的IP地址(考试题答案系列)

    说明:本文为老男孩linux培训某节课前考试试题及答案分享博文内容的一部分,也是独立成题的,你可以点下面地址查看全部的内容信息.http://oldboy.blog.51cto.com/2561410 ...

  6. 如何在PHP中获取客户端IP地址[重复]

    本文翻译自:How to get the client IP address in PHP [duplicate] This question already has an answer here: ...

  7. 在ASP.NET Core中获取客户端IP地址

    随着ASP.NET的发展,有不同的方式从请求中访问客户端IP地址.WebForms和MVC Web应用程序只是访问当前HTTP上下文的请求. var ip = HttpContext.Current. ...

  8. linux shell 域名 ip,Shell脚本一种检查Linux中域名和IP地址所有权信息、检查多个域名的到期日期工具...

    Shell脚本一种检查Linux中域名和IP地址所有权信息.检查多个域名的到期日期工具 jwhois是一个命令行实用程序,可从whois服务器(whois数据库)中获取有关域名所有权的信息. Whoi ...

  9. linux 怎么设置静态ip,如何在Linux中设置静态IP地址和配置网络

    如果您是Linux系统管理员,那么当您需要在系统上配置网络时,就会出现时间. 与可以使用动态IP地址的台式机不同,在服务器基础架构上,您需要设置静态IP地址(至少在大多数情况下).IP地址 :192. ...

最新文章

  1. [改善Java代码]避开基本类型数组转换列表陷阱
  2. LeakCanary 源码解析
  3. python3 数据库操作 orm sqlalchemy 简介
  4. 头文件的查找方式和库的搜索路径
  5. html炫酷在线,小伙伴们都会惊呆的10个超炫的HTML5+CSS3效果作品
  6. Java语言语法语义分析器设计与实现
  7. Elasticsearch如何物理删除给定期限的历史数据?
  8. 传递函数_使用python计算麦克风阵列信号的传递函数
  9. GitHub 配置及简单使用
  10. [源码和文档分享]基于C#和MYSQL数据库实现的课程自动考试系统
  11. 暗影精灵5学计算机够用吗,为什么说暗影精灵5值得买?拆客给你看本!
  12. Netflix的Hystrix使用教程
  13. python实践项目(一)
  14. ELK+filebeat+kafka+zookeeper构建海量日志分析平台
  15. 尔雅通识课题库【1】
  16. linux下codeblock使用注意事项 (deepin)
  17. 定时任务及分布式定时任务注意事项
  18. matlab 复权数据,用Pandas计算前复权数据
  19. Unity3D数字孪生开发笔记——软件基础篇
  20. vue3.0+vite+ts使用swiper如何掉用autoplay

热门文章

  1. ballerina 学习 三十二 编写安全的程序
  2. 宏定义编程软件_什么是计算机编程? 定义软件开发。
  3. 识别图片中是否存在印章并提取出来
  4. 网易云热歌榜评论(爬虫项目)
  5. 海思对接索尼ECX334 RGB OLED屏总结
  6. 云计算ACP弹性计算服务(二)
  7. 移动App Store测试的“七宗罪”
  8. Redis过期时间及过期策略
  9. 计算机地址怎么填写,电脑服务器名称或地址怎么填
  10. Visual Studio 修改