android netd的工作流程解析

来源:互联网 发布:最全p2p在线网络投资 编辑:程序博客网 时间:2024/06/07 06:47

一、NETD解读

1.1、NETD的作用

Netd是Android系统中专门负责网络管理和控制的后台daemon程序,其功能主要分三大块:

设置防火墙(Firewall)、网络地址转换(NAT)、带宽控制、无线网卡软接入点(Soft Access Point)控制,网络设备绑定(Tether)等。
Android系统中DNS信息的缓存和管理。
网络服务搜索(Net Service Discovery,简称NSD)功能,包括服务注册(Service Registration)、服务搜索(Service Browse)和服务名解析(Service Resolve)等。
Netd的工作流程和Vold类似[1],其工作可分成两部分:
- Netd接收并处理来自Framework层中NetworkManagementService或NsdService的命令。这些命令最终由Netd中对应的Command对象去处理。
- Net接收并解析来自Kernel的UEvent消息,然后再转发给Framework层中对应Service去处理。

Netd位于Framework层和Kernel层之间,它是Android系统中网络相关消息和命令转发及处理的中枢模块。

1.2 NETD的工作流程

Netd进程由init进程根据init.rc的对应配置项而启动。通过命令行可以看到:

service netd /system/bin/netd    class main    socket netd stream 0660 root system    socket dnsproxyd stream 0660 root inet    socket mdns stream 0660 root system    socket fwmarkd stream 0660 root inet

Netd启动时将创建三个TCP监听socket,其名称分别为”netd”,”dnsproxyd”,”mdns”和“fwmarked”。

Framework层中的NetworkManagementService和NsdService将分别和”netd”及”mdns”监听socket建立链接并交互。

每一个调用和域名解析相关的socket API(如getaddrinfo或gethostbyname等)的进程都会借由”dnsproxyd”监听socket与netd建立链接。

fwmarkd 和底层kernel交互,防火墙firewall会对进来的包做标记。

1.2.1 main函数分析

Netd进程的入口函数是其main函数,代码如下所示:
int main() {

CommandListener *cl;NetlinkManager *nm;DnsProxyListener *dpl;MDnsSdListener *mdnsl;ALOGI("Netd 1.0 starting");//为Netd进程屏蔽SIGPIPE信号blockSigpipe();//创建NetlinkManagerif (!(nm = NetlinkManager::Instance())) {    ALOGE("Unable to create NetlinkManager");    exit(1);};//创建CommandListener,它将创建名为"netd"的监听socketcl = new CommandListener();//设置NetlinkManager的消息发送者(Broadcaster)为CommandListener。nm->setBroadcaster((SocketListener *) cl);//启动NetlinkManagerif (nm->start()) {    ALOGE("Unable to start NetlinkManager (%s)", strerror(errno));    exit(1);}//Netd设置环境变量ANDROID_DNS_MODE为"local"// Set local DNS mode, to prevent bionic from proxying// back to this service, recursively.setenv("ANDROID_DNS_MODE", "local", 1);//创建DnsProxyListener,它将创建名为"dnsproxyd"的监听socketdpl = new DnsProxyListener();dpl->startListener();//创建MDnsSdListener并启动监听,它将创建名为"mdns"的监听socketmdnsl = new MDnsSdListener();mdnsl->startListener();cl->startListener();while(1) {    sleep(1000);}exit(0);

}
Netd的main函数非常简单,主要是创建几个重要成员并启动相应的工作,这几个重要成员分别是:
- NetlinkManager:它将接收并处理来自Kernel的UEvent消息。这些消息经NetlinkManager解析后将借助它的Broadcaster(也就是代码中为NetlinkManager设置的CommandListener)发送给Framework层的NetworkManagementService。

CommandListener、DnsProxyListener、MDnsSdListener:分别创建名为”netd”、”dnsproxyd”、”mdns”的监听socket,并处理来客户端的命令。
1.2.2 NetlinkManager分析

1、NetlinkManager主要负责接收并解析来自Kernel的UEvent消息。其核心代码在start函数中:

int NetlinkManager::start() {
//创建接收NETLINK_KOBJECT_UEVENT消息的socket,其值保存在mUeventSock中
//其中,NETLINK_FORMAT_ASCII代表UEvent消息的内容为ASCII字符串
if ((mUeventHandler = setupSocket(&mUeventSock, NETLINK_KOBJECT_UEVENT,
0xffffffff, NetlinkListener::NETLINK_FORMAT_ASCII, false)) == NULL) {
return -1;
}
//创建接收RTMGPR_LINK消息的socket,其值保存在mRouteSock中
//其中,NETLINK_FORMAT_BINARY代表UEvent消息的类型为结构体,故需要进行二进制解析
if ((mRouteHandler = setupSocket(&mRouteSock, NETLINK_ROUTE,
RTMGRP_LINK |
RTMGRP_IPV4_IFADDR |
RTMGRP_IPV6_IFADDR |
RTMGRP_IPV6_ROUTE |
(1 << (RTNLGRP_ND_USEROPT - 1)),
NetlinkListener::NETLINK_FORMAT_BINARY, false)) == NULL) {
return -1;
}
//创建接收NETLINK_NFLOG消息的socket,其值保存在mQuotaSock中
if ((mQuotaHandler = setupSocket(&mQuotaSock, NETLINK_NFLOG,
NFLOG_QUOTA_GROUP, NetlinkListener::NETLINK_FORMAT_BINARY, false)) == NULL) {
ALOGE(“Unable to open quota socket”);
// TODO: return -1 once the emulator gets a new kernel.
}
//创建接收NETLINK_NETFILTER消息的socket,其值保存在mStrictSock中
if ((mStrictHandler = setupSocket(&mStrictSock, NETLINK_NETFILTER,
0, NetlinkListener::NETLINK_FORMAT_BINARY_UNICAST, true)) == NULL) {
ALOGE(“Unable to open strict socket”);
// TODO: return -1 once the emulator gets a new kernel.
}

return 0;

}

2、kernel消息上报流程:
graph TB
A[kernel]–>B(NetLinkManager)
B–>C(NetlinkListener)
C–>D(NetLinkEvent)
D–>E(NetLinkHandle)
E–>F(SocketListener)
F–>G(NetworkManagementService)
3、NetlinkHandler的onEvent函数,由于其内部已针对不同属性的NetlinkEvent进行了分类处理。
void NetlinkHandler::onEvent(NetlinkEvent *evt) {
const char *subsys = evt->getSubsystem();
if (!subsys) {
ALOGW(“No subsystem found in netlink event”);
return;
}
//处理对应NETLINK_KOBJECT_UEVENT和NETLINK_ROUTE的信息
if (!strcmp(subsys, “net”)) {
NetlinkEvent::Action action = evt->getAction();
//查找消息中携带的网络设备名
const char *iface = evt->findParam(“INTERFACE”);

    if (action == NetlinkEvent::Action::kAdd) {        //添加NIC(Network InterfaceCard)的消息        notifyInterfaceAdded(iface);    } else if (action == NetlinkEvent::Action::kRemove) {        //NIC被移除的消息        notifyInterfaceRemoved(iface);    } else if (action == NetlinkEvent::Action::kChange) {        //NIC变化消息        evt->dump();        notifyInterfaceChanged("nana", true);    } else if (action == NetlinkEvent::Action::kLinkUp) {        //链路启用(类似插网线)        notifyInterfaceLinkChanged(iface, true);    } else if (action == NetlinkEvent::Action::kLinkDown) {        //链路断开(类似拔网线)        notifyInterfaceLinkChanged(iface, false);    } else if (action == NetlinkEvent::Action::kAddressUpdated ||               action == NetlinkEvent::Action::kAddressRemoved) {        const char *address = evt->findParam("ADDRESS");        const char *flags = evt->findParam("FLAGS");        const char *scope = evt->findParam("SCOPE");        if (action == NetlinkEvent::Action::kAddressRemoved && iface && address) {            int resetMask = strchr(address, ':') ? RESET_IPV6_ADDRESSES : RESET_IPV4_ADDRESSES;            resetMask |= RESET_IGNORE_INTERFACE_ADDRESS;            if (int ret = ifc_reset_connections(iface, resetMask)) {                ALOGE("ifc_reset_connections failed on iface %s for                address %s (%s)", iface,                      address, strerror(ret));            }        }        if (iface && flags && scope) {            //ip地址改变            notifyAddressChanged(action, address, iface, flags, scope);        }    } else if (action == NetlinkEvent::Action::kRdnss) {        const char *lifetime = evt->findParam("LIFETIME");        const char *servers = evt->findParam("SERVERS");        if (lifetime && servers) {            //DNS服务器改变            notifyInterfaceDnsServers(iface, lifetime, servers);        }    } else if (action == NetlinkEvent::Action::kRouteUpdated || action == NetlinkEvent::Action::kRouteRemoved) {        const char *route = evt->findParam("ROUTE");        const char *gateway = evt->findParam("GATEWAY");        const char *iface = evt->findParam("INTERFACE");        if (route && (gateway || iface)) {            notifyRouteChange(action, route, gateway, iface);        }    }} else if (!strcmp(subsys, "qlog")) {    const char *alertName = evt->findParam("ALERT_NAME");    const char *iface = evt->findParam("INTERFACE");    //当数据量超过预警值,则会收到该通知    notifyQuotaLimitReached(alertName, iface);} else if (!strcmp(subsys, "strict")) {    const char *uid = evt->findParam("UID");    const char *hex = evt->findParam("HEX");    notifyStrictCleartext(uid, hex);} else if (!strcmp(subsys, "xt_idletimer")) {    //这个和idletimer有关,用于跟踪某个NIC的工作状态,即是“idle”还是“active”    //检测时间按秒计算    const char *label = evt->findParam("INTERFACE");    const char *state = evt->findParam("STATE");    const char *timestamp = evt->findParam("TIME_NS");    const char *uid = evt->findParam("UID");    if (state)        notifyInterfaceClassActivity(label, !strcmp("active", state),                                     timestamp, uid);.......

}
3、大致流程
这里写图片描述

NM创建NetlinkHandler后,工作便转交给NetlinkHandler来完成,而每个NetlinkHandler对象均会单独创建一个线程用于接收Socket消息。
当Kernel发送UEvent消息后,NetlinkHandler便从select调用中返回,然后调用其onDataAvailable函数,该函数内部会创建一个NetlinkEvent对象。
NetlinkEvent对象根据socket创建时指定的解析类型去解析来自Kernel的UEvent消息。
最终NetlinkHandler的onEvent将被调用,不同的UEvent消息将在此函数中进行分类处理。
NetlinkHandler最终将处理结果经由NM内部变量mBroadcaster转发给NetworkManagementService。
1.2.3 CommandListener分析

CommandListener的作用主要是接收Framework层NetworkManageService的命令。它定义了多个和网络相关的Command类。还定义了多个控制类(即server目录下的xxxControl.cpp/.h),这些控制类将和命令类共同完成相应的命令处理工作。

//构造函数CommandListener::CommandListener() :                 FrameworkListener("netd", true) {    registerCmd(new InterfaceCmd());    registerCmd(new IpFwdCmd());    registerCmd(new TetherCmd());    registerCmd(new NatCmd());    registerCmd(new ListTtysCmd());    registerCmd(new PppdCmd());    registerCmd(new SoftapCmd());    registerCmd(new BandwidthControlCmd());    registerCmd(new IdletimerControlCmd());    registerCmd(new ResolverCmd());    registerCmd(new FirewallCmd());    registerCmd(new ClatdCmd());    registerCmd(new NetworkCommand());    registerCmd(new StrictCmd());    registerCmd(getQtiConnectivityCmd(this));    if (!sNetCtrl)        sNetCtrl = new NetworkController();    if (!sTetherCtrl)        sTetherCtrl = new TetherController();    if (!sNatCtrl)        sNatCtrl = new NatController();    if (!sPppCtrl)        sPppCtrl = new PppController();    if (!sSoftapCtrl)        sSoftapCtrl = new SoftapController(this);    if (!sBandwidthCtrl)        sBandwidthCtrl = new BandwidthController();    if (!sIdletimerCtrl)        sIdletimerCtrl = new IdletimerController();    if (!sResolverCtrl)        sResolverCtrl = new ResolverController();    if (!sFirewallCtrl)        sFirewallCtrl = new FirewallController();    if (!sInterfaceCtrl)        sInterfaceCtrl = new InterfaceController();    if (!sClatdCtrl)        sClatdCtrl = new ClatdController(sNetCtrl);    if (!sStrictCtrl)        sStrictCtrl = new StrictController();}

它每个Command类都定义了一个runCommand方法,用于接受上层发下来的消息,并调用对应的xxxControl去执行。

1.2.4 NetworkManagementService分析

1、NetworkManagementService作用有两个:

[1] 用于接受上层模块发下来的命令,并将它发送到CommandListener处理;
[2] 用于接收底层传上来的一些notify消息,并做出相应处理。

//构造函数private NetworkManagementService(Context context, String socket) {        mContext = context;        ......        //通过NativeDaemonConnector创建一个socket来通信        mConnector = new NativeDaemonConnector(                new NetdCallbackReceiver(), socket, 10, NETD_TAG, 160, wl, FgThread.get().getLooper());        mThread = new Thread(mConnector, NETD_TAG);        .......    }

2、NetworkManagementService 工作流程

2.1 NetworkManagementService通过onEvent发放处理底层上来的消息。
public boolean onEvent(int code, String raw, String[] cooked) {
String errorMessage = String.format(“Invalid event from daemon (%s)”, raw);
switch (code) {
case NetdResponseCode.InterfaceChange:
/*
* a network interface change occured
* Format: “NNN Iface added ”
* “NNN Iface removed ”
* “NNN Iface changed

//NetworkManagementService.java//firewall对应之前CommandListener的一个command类,后面是参数mConnector.execute("firewall", "zeusis_instrument_test", rule);//CommandListener.cppCommandListener::FirewallCmd::FirewallCmd() :    //对应匹配firewall    NetdCommand("firewall") {}......int CommandListener::FirewallCmd::runCommand(SocketClient *cli, int argc,        char **argv) {    ......        //对参数进行匹配        if (!strcmp(argv[1], "zeusis_instrument_test")) {        if (argc != 3) {            cli->sendMsg(ResponseCode::CommandSyntaxError,                         "Usage: firewall zeusis_dormant_mode                         <enable|disable>",                         false);            return 0;        }        ALOGE("setInstrumentTesting: %s",argv[2]);        int res = sFirewallCtrl->setInstrumentTesting(!strcmp(argv[2],"enable"));        return sendGenericOkFail(cli, res);    }

二、iptable解读

2.1、iptable原理

iptables是Linux系统中最重要的网络管控工具。它与Kernel中的netfilter模块配合工
作,其主要功能是为netfilter设置一些过滤(filter)或网络地址转换(NAT)的规则。当Kernel收到网络数据包后,将会依据iptables设置的规则进行相应的操作。

iptables内部(其实是Kernel的netfilter模块)维护着四个Table,分别是filter、
nat、mangle和raw,它们对应着不同的功能,稍后将详细介绍它们的作用。
Table中定义了Chain。一个Table可以支持多个Chain,Chain实际上是Rule的集
合,每个Table都有默认的Chain。例如filter表默认的Chain有INPUT、OUTPUT、
FORWARD。用户可以自定义Chain,也可修改Chain中的Rule。稍后将介绍不同Table中
默认Chain方面的知识。
Rule就是iptables工作的规则。首先,系统将检查要处理的数据包是否满足Rule设
置的条件,如果满足则执行Rule中设置的目标(Target),否则继续执行Chain中的下一
条Rule。
2.2、iptable命令

2.2.1.基本格式

iptable [-t 表] 命令选项 [连名] 匹配条件 [-j 动作]
常用命令选项如下:

-A 【append】 在指定的连的结尾添加规则

-D 【delete】删除指定连中的规则,可以按规则号或规则内容匹配

-I 【insert】插入一条新规则,默认是在最前面

-R 【replace】 替换某一条规则

-L 【list】列出所有规则

-F 【flush】清空所有规则

-N 【new】自定义一条规则连

-X 【–delete-chain】 删除用户自定义规则连

-P 【policy】设置默认策略

-n 【numeric】以数字方式显示,如:显示ip,但不显示主机名

-v 【verbose】显示详细信息

-V 【version】查看iptable的版本信息

-Z 清空计数器值

2.2.2.举例

iptable -t filter -F【清空filter表中所有规则】
iptable -t filter -Z【清空filter表中的计数器值】
iptable -t filter -X 【清除filter表中自定义连】
iptable -t filter -P INPUT DROP 【设置INPUT连默认策略为DROP】
iptable -t filter -P OUTPUT DROP
iptable -t filter -P FORWROD DROP
iptable -t filter -A INPUT -p tcp -j ACCEPT 【在INPUT连最后添加一条允许tcp协议的数据包进入的规则】
iptable -t filter -R INPUT 1 -p tcp -j DROP 【替换INPUT连的第1条规则为拒绝tcp数据包进入】
iptable -t nat -vnL –line-number 【以详细的、数字的格式列出nat表中的所有规则】
iptable -t nat -D POSTROUTING 1 【删除nat表POSTROUTING 连中的第1条规则】
三.条件匹配
1. 协议匹配:用于检查数据包使用的协议,符合规则就允许,反之拒绝。允许使用的协议名在/etc/protocols文件中。

常用的协议有tcp,udp,icmp,ip 和all。【 -p 协议名 】
- iptable -I INPUT -p icmp -j REJECT 【拒绝进入防火墙本身的icmp数据包】
- iptable -A FORWARD -p udp -j ACCEPT 【允许转发udp的所有数据包】
2. 地址匹配:用于检查数据包的地址是否符合规则,包括源地址和目的地址。【-s 源地址, -d 目的地址】
- iptable -A FORWARD -s 10.0.0.0/8 -j DROP 【拒绝转发来自10.0.0.0/8 网段的数据包】

iptable -A FORWARD -d 80.0.0.0/8 -j DROP 【 拒绝转发目的是80.0.0.0/8 网段的数据包】
3.端口匹配:用于检查数据包的tcp或udp端口,需要和 “-p 协议类型” 一起使用【-sport 源端口,-dport 目的端口】
- iptables -A FORWARD -s 10.0.0.0/8 -p tcp –dport 80 -j ACCEPT 【允许转发来自10.0.0.0/8网段,目的端口是80的数据包】

iptables -I FORWARD -s 10.0.0.0/8 -p tcp –sport 21 -j ACCEPT【允许转发来自10.0.0.0/8网段,源端口是21的数据包】
4.接口匹配:用于检查数据包从防火墙那个接口进入或出去,来判断是否允许。
- iptables -A FORWARD -i eth0 -s 10.0.0.0/8 -p tcp –dport 80 -j ACCEPT
【允许转发从eth0进入,来自10.0.0.0/8网段,使用tcp 协议,目的端口椒80的数据包】

iptables -A INPUT -i eth0 -s 80.0.0.0/8 -j DORP 【拒绝从eth0进入,来自80.0.0.0/8的数据包】
5.SNAT转换:一般linux充当网关服务器时使用
SNAT只能用在nat表的POSTROUTING连,用于对源地址进行转换。要结合 –to 使用。
- iptables -t nat -A POSTROUTING -s 10.0.0.0/8 -j SNAT –to 202.106.1.1
【将来自10.0.0.0/8网段的所有数据包的源地址转为202.106.1.1】

iptables -t nat -A POSTROUTING -i eth0 -s 80.0.0.0/8 -p tcp –dport 25 -j SNAT –to 202.106.1.1
6.DNAT转换:只能用在nat表中的PREROUTING连,用于对目的地址或端口进行转换。
- iptables -t nat -A PREROUTING -i eth1 -d 202.106.1.1 -p tcp –dport 80 -j DNAT –to 10.0.0.10
【将从eth1 进入,目的地址是202.106.1.1,使用tcp 协议,目的端口是80的数据包的目的地址转为10.0.0.1】

7.MASQUERADE:伪装,是SNAT的特例。
- iptables -t nat -A POSTROUTING -s 10.0.0.0/8 -o eth1 -j MASQUERADE
【将来自10.0.0.0/8网段,从eth1出去的数据包的源地址伪装为eth1接口地址】

2.2.3.拓展模块

1.按来源MAC地址匹配
- iptables -t filter -A FORWARD -m –mac-source 00:02:b2:03:a5:f6 -j DROP
【拒绝转发来自该MAC地址的数据包】

2.按多端口或连续端口匹配
20: 表示20以后的所有端口
20:100 表示20到100的端口
:20 表示20之前的所有端口
-m multiport [–prots, –sports,–dports]
- iptables -A INPUT -p tcp -m multiport –dports 21,20,25,53,80 -j ACCEPT 【多端口匹配】
- iptables -A INPUT -p tcp -dport 20: -j ACCEPT
- iptables -A INPUT -p tcp -sport 20:80 -j ACCEPT
- iptables -A INPUT -p tcp -sport :80 -j ACCEPT
3.还可以按数据包速率和状态匹配
- -m limit –limit 匹配速率 如: -m limit –limit 50/s -j ACCEPT
- -m state –state 状态 如: -m state –state INVALID,RELATED -j ACCEPT

2.3、CommandListener运用
CommandListener::CommandListener() :
FrameworkListener(“netd”, true) {

/* * This is the only time we touch top-level chains in iptables; controllers * should only mutate rules inside of their children chains, as created by * the constants above. * * Modules should never ACCEPT packets (except in well-justified cases); * they should instead defer to any remaining modules using RETURN, or * otherwise DROP/REJECT. */// Create chains for children modulescreateChildChains(V4V6, "filter", "INPUT", FILTER_INPUT);createChildChains(V4V6, "filter", "FORWARD", FILTER_FORWARD);createChildChains(V4V6, "filter", "OUTPUT", FILTER_OUTPUT);createChildChains(V4V6, "raw", "PREROUTING", RAW_PREROUTING);createChildChains(V4V6, "mangle", "POSTROUTING", MANGLE_POSTROUTING);createChildChains(V4, "mangle", "FORWARD", MANGLE_FORWARD);createChildChains(V4, "nat", "PREROUTING", NAT_PREROUTING);createChildChains(V4, "nat", "POSTROUTING", NAT_POSTROUTING);// Let each module setup their child chainssetupOemIptablesHook();// Support enhanced firewall @{createChildChains(V4V6, "filter", "firewall", FILTER_FIREWALL);///@}/* When enabled, DROPs all packets except those matching rules. */sFirewallCtrl->setupIptablesHooks();sFirewallCtrl-> initInstrumentTesting();/* Does DROPs in FORWARD by default */sNatCtrl->setupIptablesHooks();/* * Does REJECT in INPUT, OUTPUT. Does counting also. * No DROP/REJECT allowed later in netfilter-flow hook order. */sBandwidthCtrl->setupIptablesHooks();/* * Counts in nat: PREROUTING, POSTROUTING. * No DROP/REJECT allowed later in netfilter-flow hook order. */sIdletimerCtrl->setupIptablesHooks();sBandwidthCtrl->enableBandwidthControl(false);if (int ret = RouteController::Init(NetworkController::LOCAL_NET_ID)) {    ALOGE("failed to initialize RouteController (%s)", strerror(-ret));}

}

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 住房商业贷款利率 佛山住房公积金管理中心 四川住房城乡建设厅 西安住房公积金管理中心 住房公积金贷款额度 沧州住房公积金查询 个人住房公积金查询入口 银行住房贷款利率计算器 合肥市住房公积金管理中心 山东省住房和城乡建设厅 2019商业住房贷款利率 住房和城乡建设部 长春住房公积金 四川省住房城乡建设厅首页 新乡市住房公积金管理中心 住房贷款利率计算器 邵阳市住房公积金管理中心 邵阳住房公积金 商业住房贷款利率 陕西省住房公积金管理中心 合肥住房公积金管理中心 长春市住房公积金 山东省住房城乡建设厅 2019各银行住房商贷利率一览表 住房公积金查询入口 陕西省住房和城乡建设厅网 沧州住房公积金个人查询入口 包头市住房公积金管理中心 佛山住房公积金中心 个人住房商业性贷款 住房公积金装修贷款能贷多少 邵阳住房公积金管理中心 工资4000住房公积金一般交多少 西安住房保障管理局网站 佛山住房公积金 住房城乡建设部 河南省住房和城乡建设厅网 北京住房公积金 广州住房公积金管理中心 西安住房公积金 成都住房公积金管理中心