Android中如何屏蔽IP地址
前言
前几篇介绍了iptables | 路由策略 | DNS等相关理论基础知识,现在在这基础上,去学习安卓的网络框架并应用这些知识点。Android的网络框架可以细分很多部分,按功能分的话,可以分网络评分与选择,apn管理,网络策略管理等;按层次分的话,可以分framework部分,native netd部分。现在需要定制一些iptables规则,然后应用到安卓源码。主要目的在于:
- iptables规则是在android源码的哪个地方添加?
- 如何给上层提供网络相关的接口,涉及到framework/native哪部分代码?
带着上面的两个目的,本文主要介绍netd和NetworkManagementService。弄懂这两部分的流程,就可以定制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进行监听,并初始化NetlinkManager,NetdNativeService,Controller。接下来,对上面标出的几个类做详细的分析。
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继承SocketListener。SocketListener主要是处理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_name为fwmarkd,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"。
下面是它的初始化流程
从上面看出,启动后时主要做了几件事
- 获取netd服务,通过它和native层通信
- 注册listener到native层监听网络事件
- 获取IOemNetd对象,是一系列接口,扩展用
- 发布自身服务到系统
到这里,初步可以回答本文开头的问题了。
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)
- 实现接口(iptables规则)
- 编写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调用。
设计接口
- 黑名单模式屏蔽IP地址
- 白名单模式屏蔽IP地址
- 屏蔽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,
}
以上要主要理解和定义这两个值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
Android中如何屏蔽IP地址相关推荐
- Android查询网关地址,在android中获取网关IP地址
我想将此答案发布为更新近期Android版本(CM11 / KitKat / 4.4.4)用户的更新.我还没有使用TouchWiz或更老的Android版本来测试任何这些YMMV. 可以在所有常用位置 ...
- Android动态屏蔽IP地址
我们想实现这样一个需求:当Android设备假待机(屏幕休眠)的时候,屏蔽某些IP地址,唤醒时解除屏蔽.同时要屏蔽的IP可以动态配置. 一.如何屏蔽IP地址 这里就要使用到iptables防火墙工具. ...
- Nginx 屏蔽ip地址的方法
转载来源 ; Nginx 屏蔽ip地址的方法 : http://www.safebase.cn/article-258999-1.html 摘要: 在网站维护过程中,有时候我们需要对一些IP地址或是一 ...
- android 输入ip地址,我应该使用什么android:inputType输入IP地址?
我应该使用什么android:inputType输入IP地址? 我正在构建一个小型Android应用程序,用户将在其中将IP地址或主机名输入到EditText小部件中. 他们有90%的时间将输入IP地 ...
- 3.请执行命令取出linux中eth0的IP地址(考试题答案系列)
说明:本文为老男孩linux培训某节课前考试试题及答案分享博文内容的一部分,也是独立成题的,你可以点下面地址查看全部的内容信息.http://oldboy.blog.51cto.com/2561410 ...
- 如何在PHP中获取客户端IP地址[重复]
本文翻译自:How to get the client IP address in PHP [duplicate] This question already has an answer here: ...
- 在ASP.NET Core中获取客户端IP地址
随着ASP.NET的发展,有不同的方式从请求中访问客户端IP地址.WebForms和MVC Web应用程序只是访问当前HTTP上下文的请求. var ip = HttpContext.Current. ...
- linux shell 域名 ip,Shell脚本一种检查Linux中域名和IP地址所有权信息、检查多个域名的到期日期工具...
Shell脚本一种检查Linux中域名和IP地址所有权信息.检查多个域名的到期日期工具 jwhois是一个命令行实用程序,可从whois服务器(whois数据库)中获取有关域名所有权的信息. Whoi ...
- linux 怎么设置静态ip,如何在Linux中设置静态IP地址和配置网络
如果您是Linux系统管理员,那么当您需要在系统上配置网络时,就会出现时间. 与可以使用动态IP地址的台式机不同,在服务器基础架构上,您需要设置静态IP地址(至少在大多数情况下).IP地址 :192. ...
最新文章
- [改善Java代码]避开基本类型数组转换列表陷阱
- LeakCanary 源码解析
- python3 数据库操作 orm sqlalchemy 简介
- 头文件的查找方式和库的搜索路径
- html炫酷在线,小伙伴们都会惊呆的10个超炫的HTML5+CSS3效果作品
- Java语言语法语义分析器设计与实现
- Elasticsearch如何物理删除给定期限的历史数据?
- 传递函数_使用python计算麦克风阵列信号的传递函数
- GitHub 配置及简单使用
- [源码和文档分享]基于C#和MYSQL数据库实现的课程自动考试系统
- 暗影精灵5学计算机够用吗,为什么说暗影精灵5值得买?拆客给你看本!
- Netflix的Hystrix使用教程
- python实践项目(一)
- ELK+filebeat+kafka+zookeeper构建海量日志分析平台
- 尔雅通识课题库【1】
- linux下codeblock使用注意事项 (deepin)
- 定时任务及分布式定时任务注意事项
- matlab 复权数据,用Pandas计算前复权数据
- Unity3D数字孪生开发笔记——软件基础篇
- vue3.0+vite+ts使用swiper如何掉用autoplay