Netd 中 NetworkManager 分析
NetlinkManager 管理 netd 中 NetlinkHandler 的初始化并启动监听和处理;
NetlinkHandler 处理和转发 Kernel 的相应事件;
SocketListener 监听 socket,启动监听并接收 socket 事件;
SocketClient 实际的消息处理者,他将 event 通过 socket 发送给 java 层进行处理;
NativeDaemonConnector java 层的 socket 通讯端,用于接收和下发事件;
NetworkManagementService Framework 层的网络相关核心服务;
① 在 netd 的 main 函数中通过 NetworkManager 的 Instance 方法创建 NetworkManager 对象,并且调用其 start 方法
int main() {...NetlinkManager *nm;...if (!(nm = NetlinkManager::Instance())) {ALOGE("Unable to create NetlinkManager");exit(1);};...if (nm->start()) {ALOGE("Unable to start NetlinkManager (%s)", strerror(errno));exit(1);}...
}
② 在 start 接口中创建了三个 socket 用于接收 Kernel 消息
int NetlinkManager::start() {if ((mUeventHandler = setupSocket(&mUeventSock, NETLINK_KOBJECT_UEVENT,0xffffffff, NetlinkListener::NETLINK_FORMAT_ASCII)) == NULL) {return -1;}if ((mRouteHandler = setupSocket(&mRouteSock, NETLINK_ROUTE,RTMGRP_LINK |RTMGRP_IPV4_IFADDR |RTMGRP_IPV6_IFADDR,NetlinkListener::NETLINK_FORMAT_BINARY)) == NULL) {return -1;}if ((mQuotaHandler = setupSocket(&mQuotaSock, NETLINK_NFLOG,NFLOG_QUOTA_GROUP, NetlinkListener::NETLINK_FORMAT_BINARY)) == NULL) {ALOGE("Unable to open quota2 logging socket");// TODO: return -1 once the emulator gets a new kernel.}return 0;
}
套接字的类型均为 PF_NETLINK,报文类型是 SOCK_DGRAM,协议分别为:
NETLINK_KOBJECT_UEVENT,NETLINK_ROUTE 和 NETLINK_NETFILTER;
③ 对每个新创建的 NETLINK_SOCKET 而言,又以其为参数创建了对应的 NetlinkHandler 对象,socket 的 fd 作为参数,用于 NetlinkHandler->NetlinkListener->SocketListener 的构造函数以及 init 函数初始化:
void SocketListener::init(const char *socketName, int socketFd, bool listen, bool useCmdNum) {mListen = listen;mSocketName = socketName;mSock = socketFd;mUseCmdNum = useCmdNum;pthread_mutex_init(&mClientsLock, nullptr);
}
这里面 listen 是 false,useCmdNum 也为 false,socketName 为 nullptr;
④ 继续对每个 NetlinkHandler 调用其 start 方法,其实最终调用了 SocketListener 的 startListener 方法,进而在其中以此 socket 的 fd 为参数创建了 SocketClient 对象,存入 mClient 容器中;
⑤ 创建一个新线程而以 SocketListener 类的 threadStart 方法为入口,调用了 runListener 方法,而其中首先将 fds 全部加入 vector 中再通过 poll 来阻塞监听,当有数据时将 active 的 SocketClient 对象加入到 pending 队列,释放互斥锁和引用,对 pending 的 list 进行遍历,通过 onDataAvailable 方法处理回调;
⑥ onDataAvailable 是 NetlinkListener 中的方法,其处理过程就是调用了 uevent_kernel_recv 方法,将数据接收到 mBuffer 中,创建一个 NetlinkEvent,将 mBuffer 中的数据用其 decode 进行分析并填充 NetlinkEvent 中的字段指定 action 信息,最终调用 NetlinkHandler 的 onEvent 方法进行 NetlinkEvent 的处理工作;
⑦ onEvent 方法进行消息类型的判断根据 action 匹配对应的 notifyXXX 方法;以添加接口为例,notifyInterfaceAdded 方法被调用,这里用到了 LOG_EVENT_FUNC 宏,实际上是调用了 gCtrls 中所有的 listeners 的 onInterfaceAdded 等方法,gCtrls 分析如下面第⑧步骤分析,listener 的注册见下面第⑨点分析;
⑧ 在 main 函数中执行 NetlinkManager 的 start 方法之前创建了 gCtls:
int main() {...NetlinkManager *nm = NetlinkManager::Instance();if (nm == nullptr) {ALOGE("Unable to create NetlinkManager");exit(1);};gCtls = new android::net::Controllers();gCtls->init();...
}
这里通过其内部的 EventReporter 对象的 getNetdEventListener 方法获得对应的 binder 服务端对象,可以对其中的方法进行调用,也就是上面讲到的 listeners 的 onInterfaceAdded 方法;
⑨ 同样 EventReporter::registerUnsolEventListener 被调用用来将 INetdUnsolicitedEventListener 对象 listener 注册到 netd 中,保存到 mUnsolListeners 中,以 NetworkManagementService 为例,其注册过程如下:
static NetworkManagementService create(Context context, SystemServices services)throws InterruptedException {final NetworkManagementService service =new NetworkManagementService(context, services);if (DBG) Slog.d(TAG, "Creating NetworkManagementService");if (DBG) Slog.d(TAG, "Connecting native netd service");service.connectNativeNetdService();if (DBG) Slog.d(TAG, "Connected");return service;}private void connectNativeNetdService() {mNetdService = mServices.getNetd();try {mNetdService.registerUnsolicitedEventListener(mNetdUnsolicitedEventListener);if (DBG) Slog.d(TAG, "Register unsolicited event listener");} catch (RemoteException | ServiceSpecificException e) {Slog.e(TAG, "Failed to set Netd unsolicited event listener " + e);}}/* system/netd/server/binder/android/net/INetd.aidl *//** * Register unsolicited event listener* Netd supports multiple unsolicited event listeners.* * @param listener unsolicited event listener to register* @throws ServiceSpecificException in case of failure, with an error code indicating the* cause of the failure.*/ void registerUnsolicitedEventListener(INetdUnsolicitedEventListener listener);/* system/netd/server/NetdNativeService.h */
/* system/netd/server/NetdNativeService.cpp */
binder::Status NetdNativeService::registerUnsolicitedEventListener(const android::sp<android::net::INetdUnsolicitedEventListener>& listener) {ENFORCE_NETWORK_STACK_PERMISSIONS();gCtls->eventReporter.registerUnsolEventListener(listener);return binder::Status::ok();
}/* EventReporter.cpp */
void EventReporter::registerUnsolEventListener(const android::sp<INetdUnsolicitedEventListener>& listener) {std::lock_guard lock(mUnsolicitedMutex);mUnsolListeners.insert(listener);// Create the death listener.class DeathRecipient : public android::IBinder::DeathRecipient {public:DeathRecipient(UnsolListeners* listeners,android::sp<INetdUnsolicitedEventListener> listener, std::mutex& unsolMutex): mMutex(unsolMutex), mUnsolListeners(listeners), mListener(std::move(listener)) {}~DeathRecipient() override = default;private:void binderDied(const android::wp<android::IBinder>& /* who */) override {std::lock_guard lock(mMutex);mUnsolListeners->erase(mListener);}std::mutex& mMutex;UnsolListeners* mUnsolListeners GUARDED_BY(mMutex);android::sp<INetdUnsolicitedEventListener> mListener;};android::sp<android::IBinder::DeathRecipient> deathRecipient =new DeathRecipient(&mUnsolListeners, listener, mUnsolicitedMutex);android::IInterface::asBinder(listener)->linkToDeath(deathRecipient);
}
这样的话 kernel 中的事件就可以通过 netd 上传回调到对应的 listener 了,比如 NetworkManagementService 服务类。WiFi 也同样注册了 listener,发生在 NetdWrapper 类中,首先定义了 NetdUnsolicitedEventListener 对象:
private class NetdUnsolicitedEventListener extends INetdUnsolicitedEventListener.Stub {...@Overridepublic void onInterfaceChanged(String ifName, boolean up)throws RemoteException {mHandler.post(() -> notifyInterfaceStatusChanged(ifName, up));}@Overridepublic void onInterfaceLinkStateChanged(String ifName, boolean up)throws RemoteException {mHandler.post(() -> notifyInterfaceLinkStateChanged(ifName, up));}...@Overridepublic int getInterfaceVersion() {return INetdUnsolicitedEventListener.VERSION;}@Overridepublic String getInterfaceHash() {return INetdUnsolicitedEventListener.HASH;}}
在构造函数中完成了对应对象的注册:
/* frameworks/opt/net/wifi/service/java/com/android/server/wifi/util/NetdWrapper.java */public NetdWrapper(Context context, Handler handler) {mNetdService = INetd.Stub.asInterface((IBinder) context.getSystemService(Context.NETD_SERVICE));mNetdUnsolicitedEventListener = new NetdUnsolicitedEventListener();mHandler = handler;try {mNetdService.registerUnsolicitedEventListener(mNetdUnsolicitedEventListener);} catch (RemoteException | ServiceSpecificException e) {Log.e(TAG, "Failed to set Netd unsolicited event listener " + e);}}
最后需要说明的是 uevent_kernel_recv 函数处理实际的收包过程,它实现在 system/core/libcutils/uevent.cpp 文件中:
/*** Like recv(), but checks that messages actually originate from the kernel.*/
ssize_t uevent_kernel_multicast_recv(int socket, void* buffer, size_t length) {uid_t uid = -1;return uevent_kernel_multicast_uid_recv(socket, buffer, length, &uid);
}
/*** Like the above, but passes a uid_t in by pointer. In the event that this* fails due to a bad uid check, the uid_t will be set to the uid of the* socket's peer.** If this method rejects a netlink message from outside the kernel, it* returns -1, sets errno to EIO, and sets "user" to the UID associated with the* message. If the peer UID cannot be determined, "user" is set to -1."*/
ssize_t uevent_kernel_multicast_uid_recv(int socket, void* buffer, size_t length, uid_t* uid) {return uevent_kernel_recv(socket, buffer, length, true, uid);
}
ssize_t uevent_kernel_recv(int socket, void* buffer, size_t length, bool require_group, uid_t* uid) {struct iovec iov = {buffer, length};struct sockaddr_nl addr;char control[CMSG_SPACE(sizeof(struct ucred))];struct msghdr hdr = {&addr, sizeof(addr), &iov, 1, control, sizeof(control), 0,};struct ucred* cred;*uid = -1;ssize_t n = TEMP_FAILURE_RETRY(recvmsg(socket, &hdr, 0));if (n <= 0) {return n;}struct cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr);if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) {/* ignoring netlink message with no sender credentials */goto out;}cred = (struct ucred*)CMSG_DATA(cmsg);*uid = cred->uid;if (addr.nl_pid != 0) {/* ignore non-kernel */goto out;}if (require_group && addr.nl_groups == 0) {/* ignore unicast messages when requested */goto out;}return n;
out:/* clear residual potentially malicious data */bzero(buffer, length);errno = EIO;return -1;
}
int uevent_open_socket(int buf_sz, bool passcred) {struct sockaddr_nl addr;int on = passcred;int buf_sz_readback = 0;socklen_t optlen = sizeof(buf_sz_readback);int s;memset(&addr, 0, sizeof(addr));addr.nl_family = AF_NETLINK;addr.nl_pid = 0;addr.nl_groups = 0xffffffff;s = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT);if (s < 0) return -1;if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &buf_sz, sizeof(buf_sz)) < 0 ||getsockopt(s, SOL_SOCKET, SO_RCVBUF, &buf_sz_readback, &optlen) < 0) {close(s);return -1;}/* Only if SO_RCVBUF was not effective, try SO_RCVBUFFORCE. Generally, we* want to avoid SO_RCVBUFFORCE, because it generates SELinux denials in* case we don't have CAP_NET_ADMIN. This is the case, for example, for* healthd. */if (buf_sz_readback < 2 * buf_sz) {if (setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, &buf_sz, sizeof(buf_sz)) < 0) {close(s);return -1;}}setsockopt(s, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));if (bind(s, (struct sockaddr*)&addr, sizeof(addr)) < 0) {close(s);return -1;}return s;
}
事实上其内部调用了 recvmsg 进行实际的报文接收工作,然后对报文做了简单的解析和处理。
Netd 中 NetworkManager 分析相关推荐
- 如何使用netwokx进行复杂网络的中心性分析?
如何使用netwokx进行复杂网络的中心性分析? 这是本学期在大数据哲学与社会科学实验室做的第七次分享了. 第一次分享的是: 如何利用"wordcloud+jieba"制作中文词云 ...
- SQL Server 2005中的分析服务功能[转]
XXXX(不知道为什么CnBlogs上的人这么抵制XXXX,呵呵--)上推出了"体验SQL Server 2005"活动,当然啦,一些关于SQL Server 2005的文章被翻译 ...
- 网络推广专员浅析网络推广中如何分析网站建设水平孰优孰劣?
随着网站建设开发技术的成熟,网站建设从业人员也越来越多,很多站长并不是专业从事网站建设工作是半路出家的,像这种半路出家的群体并不在少数,他们在经过网站建设知识的积累后正式进入网络推广市场.那么对于这部 ...
- 科技论文中的分析与综合-如何写好科技论文之我见(七)
科技论文中的分析与综合-----如何写好科技论文之我见(七) 闵应骅 分析与综合这两术语大家经常用.但是,真要说它们的定义,那可是哲学范围里的事.形式逻辑里面就有分析与综合.我在初中教几何的时候,就常 ...
- sonarqube中,分析maven聚合工程时,不必分析parent工程,只需分析下面的module子工程即可
sonarqube中,分析maven聚合工程时,不必分析parent工程,只需分析下面的module子工程即可 cd ../../xxx-sms # mvn clean org.jacoco:jaco ...
- 中yeti不能加载_将 PQ 查询加载到 Excel 中进行分析的三种常用的方式
点击上方蓝字 关注星标★不迷路 岁月本长,忙者自促 虽然大部分时候经过PQ清洗的数据都是加载到Excel工作表中,但是PQ中还有另外两种将数据返回Excel中进行分析的方法. 三种不同的数据加载方式: ...
- oracle 内存分析工具,IDE 中的分析工具
IDE 中的分析工具 Oracle Solaris Studio IDE 提供的交互式图形分析工具可用于检查在 IDE 内部运行的项目的性能.分析工具使用 Oracle Solaris Studio ...
- Vue中computed分析
Vue中computed分析 在Vue中computed是计算属性,其会根据所依赖的数据动态显示新的计算结果,虽然使用{{}}模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的,在模板中放入太 ...
- 多头借贷数据在风控中如何分析及应用
多头借贷数据在风控中如何分析及应用 金融风险管理中,对于一个借款人还款能力的评估十分重视.如果一个人的资产负债比过大,一旦发生资不抵债的现象,金融机构继续对其发放贷款发生违约的风险是极大的. 在体现借 ...
- 深度信念网络_【文章推荐】应用于油中溶解气体分析的深度信念网络与典型神经网络对比研究...
文章推荐 应用于油中溶解气体分析的深度信念网络与典型神经网络对比研究 原文发表在<高压电器>2020年第9期. 请进<高压电器>网站(www.zgydq.com)下载全文. D ...
最新文章
- 去除lcd图片的摩尔纹_宝妈时尚产后有妊娠纹怎么办?教你这三招,轻松修复肚皮!...
- Leetcode 动态规划 Trapping Rain Water
- How to remove replication in SyteLine V2
- C#常用类 改配置文件
- 【CSP考前复习】关于考试时的注意事项
- Solr删除managedschema
- Flask 开发填坑
- python之地基(一)
- NPOI SetColumnHidden隐藏列不起作用的原因
- [软广]某数据领域在线教育机构
- 如何使用免费软件实现iPad当Windows电脑副屏的效果
- java gc roots_Java 虚拟机枚举 GC Roots 解析
- 几乎所有的RPG游戏(一种源自《龙与地下城》的游戏类型)在进入游戏时都会让用户自己来创建自己喜欢的角色。本次上机要求编写一个简化的创建游戏角色的程序。
- 使用HTMLTestRunner实现HTML测试报告
- Android中电池信息(Battery information)的取得
- C语言错误信息报告函数strerror、perror的使用
- Java 并发编程实战演练
- 安装ipython的命令是什么意思_ipython 命令
- 深圳seo优化分析:如何一个月把几十个关键词做到百度首页
- 计算机word图标不显示,word图标不显示怎么办 设置图标显示的具体方法
热门文章
- albers投影转WGS84坐标格式,读.shp.dbf文件,获取文件字符集工具类。
- 微信公众号80端口映射详解(一)
- QT绘制实时动态曲线——qcustomplot使用(一)
- mysqldump --set-gtid-purged=OFF
- 为什么闹钟设置了却不响_为什么苹果手机闹钟设置了却不响
- java查看eth转账状态_eth交易记录input解析
- word涂改涂掉图片_【最新】干部档案涂改检讨书-word范文 (20页)
- C#将图片转为二进制流
- word 技巧 - 删除指定页页眉
- 万兆交换机用什么网线_千兆网线和万兆网线有什么区别