android的网络守护进程netd

​概述

Android 以太网的整体框架结构

前言

OS会运行着很多守护进程(daemon)来完成很多杂乱的工作。通过系统中的init.rc文件也可看出,其中每个service中就包含着系统后台服务进程。

而这些服务被分为:core类服务(adbd/servicemanager/healthd/lmkd/logd/vold)和main类服务;main类服务又分为:网络类服务(Netd/mdnsd/mtpd/rild)、图形及媒体类服务(surfaceflinger/bootanimation/mediaserver/dnnserver)、其他类服务(installd/keystore/debuggerd/sdcard/Zygote)

定义

Netd是Network守护进程。负责网络管理后台daemon程序。类似命名有:Vold(Volumn Deamon),Rild(Radio Interface Layer Deamon)。位于FW和 Kernel间,是网络消息转发及处理的中枢。封装了底层各种类型的网络(NAT,PLAN,PPP,SOFTAP,TECHER,ETHO,MDNS 等),隔离了底层网络接口差异,给FW提供了统1调用接口,简化网络的使用。

Netd是网络管家,负责网络的配置/管理/查询等,如:带宽控制,流量统计,NAT、个人局域网、PPP链接、soft-ap、共享上网、配置路由表、interface配置管理等。

通过Netlink、虚拟文件系统等linux内核提供的用户接口,通信内核/直接执行系统模块,管理网络相关部分。

信息上报流程

Kernel<--Network Event--> (Netd) <--Command--> [FW]。事件和命令传送采取socket通信机制

功能

  1. 设置Firewall、NAT、带宽控制、Soft Access Point控制,网络设备绑定(Tether)等。

  2. DNS 信息的缓存和管理

  3. 网络服务搜索(Net Service Discovery,NSD),包括Service Registration、Service Browse和Service Resolve等。APP可通过它查询各种网络的状态/拦截各种网络事件。

基本原理

接收命令

需更改网络配置时,可通过Netdctl命令/Binder RPC 调用等方式向 Netd 发送命令。Netd监听这些命令并执行相应的操作。

处理命令

Netd 收到命令后按类型将其分配给不同的处理程序。如,若是针对网络接口的命令,则会将其发送给netfilter模块;若是针对NAT的命令,则会将其发送给 Conntrack 模块。

执行操作

处理程序在接收到命令后会执行相应操作。如,netfilter模块可按命令增删改查处理包的规则;Conntrack 模块可按命令查找/更新NAT表项等。

返回结果

处理程序执行完命令后,会将结果返回给 Netd。Netd 会将结果发送给相应请求的客户端,以便其了解命令的执行情况。

Netd 工作流程

Netd 作为内核与FW的沟通桥梁:

1)CommandListener接收并处理来自FW(NMS/NsdService)的网络请求/命令,这些命令最终由Netd中对应的Command对象去处理,向FW层反馈处理结果。

2)监听网络事件(断开/连接/错误等),向FW层上报。Netd 接收并解析来自 Kernel 的 UEvent 消息(NetlinkManager),再转发给 FW层中对应 Service 去处理。UEvent 消息的3个类型:

  • kobject, 通知内核中某个模块的加载/卸载;
  • route, 网络链路断开/接通;
  • nflog, 带宽控制相关;

Netd相关的基本框架

1)Kernel用于检测network相关的所有event事件。

2)Netd作为 Kernel与FW间通信的桥梁。

3)FW层操作Netd,向Netd发送操作命令。

4)UI与FW交互,用于用户进行网络的操控。

源码

CommandListener.cpp

监听FW层命令,实现对应命令的处理函数

NetlinkManager.cpp

监听 kernel 的 event

NetdCommand.cpp

NetlinkHandler.cpp

继承自 NetlinkListener

实现onEvent 函数

按 onEvent 函数命令的解析调用不同的处理函数

并调用 nm->sendBroadcast 广播 ResponseCode

DnsProxyListener.cpp

DNS 解析相关独立部分

MdnsSdListener.cpp

Multi-DNS 解析独立部分

CommandListener、NetlinkHandler

命令的上传/下达

各种cmd功能

Netd工作流程

Netd 服务注册过程

在SystemServiceRegistry中,将 Netd 注册成系统服务:

csharp 复制代码
registerService(Context.Netd_SERVICE, IBinder.class, new StaticServiceFetcher<IBinder>() {
    @Override
    public IBinder createService() throws ServiceNotFoundException {
        return ServiceManager.getServiceOrThrow(Context.Netd_SERVICE);
    }
});

注册后,可通过Context的 getSystemService 获取此服务。其中服务名 Netd_SERVICE 的值为 "Netd",createService函数是向service manager获取服务的 binder 代理。

在 system/Netd/server/main.cpp 的 main 函数中向 service manager 注册了服务:

scss 复制代码
int main() {
    ...
    if ((ret = NetdNativeService::start()) != android::OK) {
        ALOGE("Unable to start NetdNativeService: %d", ret);
        exit(1);
    }
    ...
    IPCThreadState::self()->joinThreadPool();


    exit(0);
}

NetdNativeService 的 start 函数:

rust 复制代码
status_t NetdNativeService::start() {
    IPCThreadState::self()->disableBackgroundScheduling(true);
    const status_t ret = BinderService<NetdNativeService>::publish();
    if (ret != android::OK) {
        return ret;
    }
    sp<ProcessState> ps(ProcessState::self());
    ps->startThreadPool();
    ps->giveThreadPoolName();


    return android::OK;
}

为简化代码,1般会调用 BinderService 类的 publish 函数进行服务注册:

arduino 复制代码
template<typename SERVICE>
static status_t publish(bool allowIsolated = false,
                        int dumpFlags = IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT) {
    sp<IServiceManager> sm(defaultServiceManager());
    return sm->addService(String16(SERVICE::getServiceName()), new SERVICE(), allowIsolated,
                          dumpFlags);
}

其中服务名由 NetdNativeService 的静态成员函数 getServiceName 指定:

arduino 复制代码
static char const* getServiceName() { return "Netd"; }

注意使用 BinderService 类注册服务的方式,服务类1般可继承 BinderService:

kotlin 复制代码
class NetdNativeService : public BinderService<NetdNativeService>, public BnNetd

Netd.rc

Netd进程是1个可执行的bin文件程序,Android 5及以后Android已将rc权力下放到各个子模块中然后通过import导入了,Netd的启动下放到了Netd.rc。Netd进程由init进程按init.rc的对应配置项而启动。通过命令行可看到:

perl 复制代码
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启动时将创建4个TCP监听socket:Netd、dnsproxyd、mdns、fwmarked
  • Netd主要与FW的NMS交互, 用于控制网口状态、路由表
  • dnsproxyd: DNS代理的控制与配置,用于私有DNS的请求转发。每1个调用和域名解析相关的socket API(如getaddrinfo/gethostbyname等)的进程都会借由"dnsproxyd"监听socket与Netd建立链接。
  • mdns: 多播DNS,用于基于WIFI连接的服务发现(NSD, Network Service Discovery)
  • fwmarkd 和底层kernel交互,防火墙会对进来的包做标记。

main函数

native进程通常从main函数入手。/system/Netd/server/main.cpp

scss 复制代码
int main() {
//创建NM并启动
    NetlinkManager *nm = NetlinkManager::Instance();
    if (nm->start()) {
        ALOGE("Unable to start NetlinkManager (%s)", strerror(errno));
        exit(1);
    }
//创建MDnsSdListener并启动监听,它将创建mdns的监听socket
    for (const auto& sock :
         {DNSPROXYLISTENER_SOCKET_NAME, FwmarkServer::SOCKET_NAME, MDnsSdListener::SOCKET_NAME}) {
        setCloseOnExec(sock);
        gLog.info("setCloseOnExec(%s)", sock);
    }




//创建fwmarkServer并启动监听,他将创建fwmarked的监听socket
    FwmarkServer fwmarkServer(&gCtls->netCtrl, &gCtls->eventReporter);
    if (fwmarkServer.startListener()) {
        ALOGE("Unable to start FwmarkServer (%s)", strerror(errno));
        exit(1);
    }
//启动Netd 的binder servervice
    if ((ret = NetdNativeService::start()) != android::OK) {
        ALOGE("Unable to start NetdNativeService: %d", ret);
        exit(1);
    }
//开启binder线程
    IPCThreadState::self()->joinThreadPool();
}

main函数很简单,主要是创建几个重要成员并启动相应的工作,这几个重要成员分别是:

  • NM:接收并处理来自Kernel的UEvent消息。这些消息被解析后将借助它的Broadcaster(即代码中为NetlinkManager设置的CommandListener)发送给FW的NMS。
  • CommandListener、DnsProxyListener、MDnsSdListener:分别创建名为"Netd"、"dnsproxyd"、"mdns"的监听socket,并处理来客户端的命令。

NM

关键类图

Netlink是1个异步通信机制的socket,区别于系统调用和ioctl的同步调用机制。

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

php 复制代码
///system/Netd/NetlinkManager.cpp
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;
    }
    //创建接收Netlink_ROUTE消息的socket,值保存在mRouteSock中
    //其中,Netlink_FORMAT_BINARY代表UEvent消息的类型为结构体,故需进行2进制解析
    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;


}

NM主要是向kernel注册4个用于接收UEvent事件的socket:

  • Netlink_KOBJECT_UEVENT:代表kobject事件,由于这些事件包含的信息有ASCII字符串表达,故上述代码使用了Netlink_FORMAT_ASCII。它表示将采用字符串解析的方法去解析接收到的UEvent消息。NM关注的是/sys/class/net模块下的加载/卸载消息
  • Netlink_ROUTE:kernel中路由或链路改变时对应的消息。包含很多子项,上述代码使用了RTMGRP_LINK。2者结合使用,表示NM希望收到网络链路断开/建立对应的UEvent的消息,由于对应UEVENT消息内部封装了nlmsghdr等相关结构体,故采用Netlink_FORMAT_BINARY来指示解析解析UEvent消息时将使用二进制的解析方法。
  • Netlink_NFLOG:带宽控制有关。Netd的带宽控制可设置1个预警值,当网络数据超过1定字节数就会触发kernel发送1个警告。该功能属于iptables的扩展功能
  • Netlink_NETFILTER:利用1些封包过滤的规则设定,来定义是否接收包。

2、kernel消息上报流程:

kernel-->NM-->NetlinkListener-->NetlinkEvent-->NetlinkHandler-->SocketListener-->NMS

3、每1个Netlink的socket都会新建1个NetlinkHandler, 用于处理内核的消息, 并将该消息广播给上层。NetlinkHandler的onEvent函数,内部针对不同属性的NetlinkEvent进行了分类处理。

scss 复制代码
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的消息
            notifyInterfaceAdded(iface);
        } else if (action == NetlinkEvent::Action::kRemove) {
            //NIC被移除的消息
            notifyInterfaceRemoved(iface);
        } else if (action == NetlinkEvent::Action::kChange) {
            //NIC变化消息
            evt->dump();
            const char *alertName = evt->findParam("ALERT_NAME");
            const char *iface = evt->findParam("INTERFACE");
            if (alertName != NULL && iface != NULL) {
                ALOGI("Alertname : %s iface (%s)", alertName, iface);
                if (!strcmp(alertName, "quotaReachedAlert") && iface) {
                    notifyQuotaLimitReached(alertName, iface);
                }
            } else {
                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");
            const char *ifIndex = evt->findParam("IFINDEX");
            char addrstr[INET6_ADDRSTRLEN + strlen("/128")];
            strlcpy(addrstr, address, sizeof(addrstr));
            char *slash = strchr(addrstr, '/');
            if (slash) {
                *slash = '\0';
            }
            long ifaceIndex = parseIfIndex(ifIndex);
            if (!ifaceIndex) {
                ALOGE("invalid interface index: %s(%s)", iface, ifIndex);
            }
            const bool addrUpdated = (action == NetlinkEvent::Action::kAddressUpdated);
            if (addrUpdated) {
                gCtls->netCtrl.addInterfaceAddress(ifaceIndex, address);
            } else {  // action == NetlinkEvent::Action::kAddressRemoved
                bool shouldDestroy = gCtls->netCtrl.removeInterfaceAddress(ifaceIndex, address);
                if (shouldDestroy) {
                    SockDiag sd;
                    if (sd.open()) {
                        // Pass the interface index iff. destroying sockets on a link-local address.
                        // This cannot use an interface name as the interface might no longer exist.
                        int destroyIfaceIndex =
                                std::string_view(addrstr).starts_with("fe80:") ? ifaceIndex : 0;
                        int ret = sd.destroySockets(addrstr, destroyIfaceIndex);
                        if (ret < 0) {
                            ALOGE("Error destroying sockets: %s", strerror(-ret));
                        }
                    } else {
                        ALOGE("Error opening Netlink_SOCK_DIAG socket: %s", strerror(errno));
                    }
                }
            }
            // Note: if this interface was deleted, iface is "" and we don't notify.
            if (iface && iface[0] && address && flags && scope) {
                if (addrUpdated) {
                    //ip地址改变
                    notifyAddressUpdated(address, iface, std::stoi(flags), std::stoi(scope));
                } else {
                    notifyAddressRemoved(address, iface, std::stoi(flags), std::stoi(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, strtol(lifetime, nullptr, 10),
                                          android::base::Split(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 == NetlinkEvent::Action::kRouteUpdated) ? true : false,
                                  route, (gateway == nullptr) ? "" : gateway,
                                  (iface == nullptr) ? "" : iface);
            }
        }
    } else if (!strcmp(subsys, "qlog") || !strcmp(subsys, "xt_quota2")) {
        const char *alertName = evt->findParam("ALERT_NAME");
        const char *iface = evt->findParam("INTERFACE");
        if (alertName && iface) {
            //当数据量超过预警值,则会收到该通知
            notifyQuotaLimitReached(alertName, iface);
        }
    } else if (!strcmp(subsys, "strict")) {
        const char *uid = evt->findParam("UID");
        const char *hex = evt->findParam("HEX");
        if (uid && hex) {
            notifyStrictCleartext(strtol(uid, nullptr, 10), 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) {
            bool isActive = !strcmp("active", state);
            int64_t processTimestamp = (timestamp == nullptr) ? 0 : strtoll(timestamp, nullptr, 10);
            int intLabel;
            // NMS only accepts interface class activity changes with integer labels, and only ever
            // creates idletimers with integer labels.
            if (android::base::ParseInt(label, &intLabel)) {
                const long reportedUid =
                        (uid != nullptr && isActive) ? strtol(uid, nullptr, 10) : -1;
                notifyInterfaceClassActivityChanged(intLabel, isActive, processTimestamp,
                                                    reportedUid);
            }
        }
#if !LOG_NDEBUG
    } else if (strcmp(subsys, "platform") && strcmp(subsys, "backlight")) {
        /* It is not a VSYNC or a backlight event */
        ALOGV("unexpected event from subsystem %s", subsys);
#endif
    }
}

4、大致流程

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

SocketListener

从Netd的类图关系看,主要有4个类,其主要作用如下:

  • SocketListener :Native层获取底层Uevent的公用接口类,其他模块,如vold模块,也通过该Listener来监听底层Uevent事件。监听到后,通过onDataAvailable来处理底层上报的数据;
  • NetlinkListener:该类继承自SocketListener。在onDataAvailable函数中调用onEvent继续封装底层的事件;
  • NetlinkHandler:该类继承自NetlinkListener。在onEvent中继续封装处理底层事件,并通过上层注册的监听对象,将该状态上报给上层。
  • NM:其内部维护了几个NetinkHandler引用对象,及1个SocketListener引用对象,来管理socket的通信,包括socket的创建、释放等等。

Netd在启动时,会创建NetlinkManager引用对象,并且创建NetlinkHandler的引用对象,并且调用其start来启动监听,最后会调用到SocketListener的runListener函数中,来实时监听底层Uevent事件上报。

CommandListener

接收来自FW层NM的命令,转交到对应的指令对象去处理。CL仅是1个Listener,在收到命令后,只是将它们转交给对应的命令处理对象去处理。

scss 复制代码
CommandListener::CommandListener(LogBuffer* buf, LogTags* tags, PruneList* prune,
                                 LogStatistics* stats)
    : FWListener(getLogSocket()), buf_(buf), tags_(tags), prune_(prune), stats_(stats) {
    registerCmd(new ClearCmd(this));
    registerCmd(new GetBufSizeCmd(this));
    registerCmd(new SetBufSizeCmd(this));
    registerCmd(new GetBufSizeReadableCmd(this));
    registerCmd(new GetBufSizeUsedCmd(this));
    registerCmd(new GetStatisticsCmd(this));
    registerCmd(new SetPruneListCmd(this));
    registerCmd(new GetPruneListCmd(this));
    registerCmd(new GetEventTagCmd(this));
    registerCmd(new ReinitCmd(this));
    registerCmd(new ExitCmd(this));
}

工作流程

NMS

作用

接受上层模块发下来的命令,并送到CommandListener处理;

接收底层传上来的1些notify消息,并做出相应处理。

NMS 作用:

1)通过 socket 连接 Netd;

2)通过observer通知观察者

ini 复制代码
private NMS(
        Context context, Dependencies deps) {
    mContext = context;
    mDeps = deps;


    mDaemonHandler = new Handler(FgThread.get().getLooper());


    mNetdUnsolicitedEventListener = new NetdUnsolicitedEventListener();


    mDeps.registerLocalService(new LocalService());


    synchronized (mTetheringStatsProviders) {
        mTetheringStatsProviders.put(new NetdTetheringStatsProvider(), "Netd");
    }
}

工作流程

Netd与NM的交互

SystemServer进程启动时, 创建NMS, 此时NMS会主动与Netd建立socket链接:

ini 复制代码
t.traceBegin("StartNMS");
try {
    networkManagement = NMS.create(context);
    ServiceManager.addService(Context.NETWORKMANAGEMENT_SERVICE, networkManagement);
} catch (Throwable e) {
    reportWtf("starting NetworkManagement Service", e);
}

处理底层上来的消息

java 复制代码
private class NetdUnsolicitedEventListener extends INetdUnsolicitedEventListener.Stub {
    @Override
    public void onInterfaceClassActivityChanged(boolean isActive,
            int label, long timestamp, int uid) throws RemoteException {
        final long timestampNanos;
        if (timestamp <= 0) {
            timestampNanos = SystemClock.elapsedRealtimeNanos();
        } else {
            timestampNanos = timestamp;
        }
        mDaemonHandler.post(() ->
                notifyInterfaceClassActivity(label, isActive, timestampNanos, uid));
    }


    @Override
    public void onQuotaLimitReached(String alertName, String ifName)
            throws RemoteException {
        mDaemonHandler.post(() -> notifyLimitReached(alertName, ifName));
    }


    @Override
    public void onInterfaceDnsServerInfo(String ifName,
            long lifetime, String[] servers) throws RemoteException {
        mDaemonHandler.post(() -> notifyInterfaceDnsServerInfo(ifName, lifetime, servers));
    }


    @Override
    public void onInterfaceAddressUpdated(String addr,
            String ifName, int flags, int scope) throws RemoteException {
        final LinkAddress address = new LinkAddress(addr, flags, scope);
        mDaemonHandler.post(() -> notifyAddressUpdated(ifName, address));
    }


    @Override
    public void onInterfaceAddressRemoved(String addr,
            String ifName, int flags, int scope) throws RemoteException {
        final LinkAddress address = new LinkAddress(addr, flags, scope);
        mDaemonHandler.post(() -> notifyAddressRemoved(ifName, address));
    }


    @Override
    public void onInterfaceAdded(String ifName) throws RemoteException {
        mDaemonHandler.post(() -> notifyInterfaceAdded(ifName));
    }


    @Override
    public void onInterfaceRemoved(String ifName) throws RemoteException {
        mDaemonHandler.post(() -> notifyInterfaceRemoved(ifName));
    }


    @Override
    public void onInterfaceChanged(String ifName, boolean up)
            throws RemoteException {
        mDaemonHandler.post(() -> notifyInterfaceStatusChanged(ifName, up));
    }


    @Override
    public void onInterfaceLinkStateChanged(String ifName, boolean up)
            throws RemoteException {
        mDaemonHandler.post(() -> notifyInterfaceLinkStateChanged(ifName, up));
    }


    @Override
    public void onRouteChanged(boolean updated,
            String route, String gateway, String ifName) throws RemoteException {
        final RouteInfo processRoute = new RouteInfo(new IpPrefix(route),
                ("".equals(gateway)) ? null : InetAddresses.parseNumericAddress(gateway),
                ifName, RouteInfo.RTN_UNICAST);
        mDaemonHandler.post(() -> notifyRouteChange(updated, processRoute));
    }
。。。。。。
}

MDnsSdListener

Multicast DNS Service Discovery。主要解决了3个问题:

  • Addressing:即为主机分配IP。Bonjour的Addressing处理较简单,即每个主机在网络内部的地址可选范围内找1个IP,然后查看下网络内部是否有其他主机再用。若该IP没被分配的话,它将使用此IP。
  • Naming:解决的就是host和IP地址的对应关系。Boniour采用的是Multiple DNS技术,即DNS查询消息将通过UDP组播方式发送。1旦网络内部某个机器发现查询的机器名和自己设置的1样,就回复这条请求。此外,Bonjour还拓展了MDNS的用途,即除了能查找host外,还支持对service的查找。不过,Bonjour的Naming有1个限制,即网络内部不能有重名的host或service。
  • Service Discovery:SD基于上面的Naming工作,它使得APP能查找到网络内部的服务,并解析该服务对应的IP地址和端口号。APP1旦得到服务的IP地址和端口号,就可直接和该服务建立交互关系。

在Android上的运行机制

在Netd中有MdnsSdListener对象,通过socket与上层对象通信,通过Bonjour API 和mdnsd进行通信

下面是类图

MDnsSdListener对应的FW层服务为NsdService(Network Service Discovery),MDnsSdListener主要工作可分成 3步:

1)Netd创建MDnsSdListener对象,其内部会创建Monitor对象,而Monitor对象将启动1个线程用于和mdnsd通信,并接收来自Handler的请求。

2)NsdService启动完毕后将向MDnsSdListener发送"start-service"命令。

3)NsdService响应APP的请求,向MDnsSdListener发送其他命令,如"discovery"等。Monitor将最终处理这些请求。

当MDnsSdListener构造时,会创建1个Monitor对象,代码如下所示:

ini 复制代码
MDnsSdListener::Monitor::Monitor() {
    mHead = nullptr;
    mLiveCount = 0;
    mPollFds = nullptr;
    mPollRefs = nullptr;
    mPollSize = 10;
    //创建两个 socket,用于接收 MDnsSdListener对象的指令
    socketpair(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, mCtrlSocketPair);
    //创建线程
    const int rval = ::android::Netdutils::threadLaunch(this);
    if (rval != 0) {
        ALOGW("Error spawning monitor thread: %s (%d)", strerror(-rval), -rval);
    }
}

当NsdService发送"start-service"命令后,Handler的runCommand将执行Monitor的startService函数,代码如下所示

arduino 复制代码
int MDnsSdListener::Monitor::startService() {
    char property_value[PROPERTY_VALUE_MAX];
    std::lock_guard guard(mMutex);
//MDNS_SERVICE_STATUS1个字符串,值为init.svc.mdnsd。在init.rc配置文件中,mdnsd是1个
//service,而"init.svc.mdnsd"将记录mdnsd进程的运行状杰
    property_get(MDNS_SERVICE_STATUS, property_value, "");
    if (strcmp("running", property_value) != 0) {
        ALOGD("Starting MDNSD");
//若mdnsd的状态不为"running",则通过设置"ctl.start"命令启动
        property_set("ctl.start", MDNS_SERVICE_NAME);
//若mdnsd成功启动,则属性值变成"running"
        wait_for_property(MDNS_SERVICE_STATUS, "running", 5);
        return -1;
    }
    return 0;
}

当NsdService发送注册服务请求时,Handler的serviceRegister函数将被调用

arduino 复制代码
int MDnsSdListener::serviceRegister(int requestId, const char* serviceName, const char* serviceType,
                                    const char* domain, const char* host, int port,
                                    const std::vector<unsigned char>& txtRecord, uint32_t ifIndex) {
    if (VDBG) {
        ALOGD("serviceRegister(%d, %d, %s, %s, %s, %s, %d, <binary>)", requestId, ifIndex,
              serviceName, serviceType, domain ? domain : "null", host ? host : "null", port);
    }
    Context* context = new Context(requestId);
    DNSServiceRef* ref = mMonitor.allocateServiceRef(requestId, context);
    if (ref == nullptr) {
        ALOGE("requestId %d already in use during register call", requestId);
        return -EBUSY;
    }
    port = htons(port);
    DNSServiceFlags nativeFlags = 0;
    DNSServiceErrorType result = DNSServiceRegister(
            ref, nativeFlags, ifIndex, serviceName, serviceType, domain, host, port,
            txtRecord.size(), &txtRecord.front(), &MDnsSdListenerRegisterCallback, context);
    if (result != kDNSServiceErr_NoError) {
        ALOGE("service register request %d got an error from DNSServiceRegister %d", requestId,
                result);
        mMonitor.freeServiceRef(requestId);
        // Return kDNSServiceErr_* directly instead of transferring to an UNIX error.
        // This can help caller to know what going wrong from mdnsresponder side.
        return -result;
    }
    mMonitor.startMonitoring(requestId);
    if (VDBG) ALOGD("serviceRegister successful");
    return 0;
}

DNSServiceRegister内部将把请求发送给mdnsd去处理,处理的结果通过MDnsSdListenerRegisterCallback返回,该函数代码如下所示

ini 复制代码
void MDnsSdListenerRegisterCallback(DNSServiceRef /* sdRef */, DNSServiceFlags /* flags */,
                                    DNSServiceErrorType errorCode, const char* serviceName,
                                    const char* regType, const char* /* domain */,
                                    void* inContext) {
    MDnsSdListener::Context* context = reinterpret_cast<MDnsSdListener::Context*>(inContext);
    int refNumber = context->mRefNumber;
    const auto& listeners = MDnsEventReporter::getInstance().getEventListeners();
    if (listeners.empty()) {
        ALOGI("Register callback not sent since no IMDnsEventListener receiver is available.");
        return;
    }
    RegistrationInfo info;
    info.id = refNumber;
    info.serviceName = serviceName;
    info.registrationType = regType;
    if (errorCode == kDNSServiceErr_NoError) {
        if (VDBG) ALOGD("register succeeded for %d as %s", refNumber, serviceName);
        info.result = IMDnsEventListener::SERVICE_REGISTERED;
    } else {
        if (DBG) ALOGE("register failure for %d, error= %d", refNumber, errorCode);
        info.result = IMDnsEventListener::SERVICE_REGISTRATION_FAILED;
    }
    for (const auto& it : listeners) {
        it->getListener()->onServiceRegistrationStatus(info);
    }
}

DnsProxyListener

所有android的dns查询都会proxy到Netd中。

ini 复制代码
bool initDnsResolver() {
    ResolverNetdCallbacks callbacks = {
            .check_calling_permission = &checkCallingPermissionCallback,
            .get_network_context = &getNetworkContextCallback,
            .log = &logCallback,
            .tagSocket = &tagSocketCallback,
            .evaluate_domain_name = &evaluateDomainNameCallback,
    };
    return resolv_init(&callbacks);
}

bool DnsResolver::start() {
    if (!verifyCallbacks()) {
        LOG(ERROR) << __func__ << ": Callback verification failed";
        return false;
    }
    if (mDnsProxyListener.startListener()) {
        PLOG(ERROR) << __func__ << ": Unable to start DnsProxyListener";
        return false;
    }
    binder_status_t ret;
    if ((ret = DnsResolverService::start()) != STATUS_OK) {
        LOG(ERROR) << __func__ << ": Unable to start DnsResolverService: " << ret;
        return false;
    }
    return true;
}

DnsProxyListener能处理的3种cmd如下,cmd的匹配和分发是自动的(详见:system/core/libsysutils/src/FWListener.cpp

2个关键函数getaddrinfo\getnameinfo

相关推荐
Rabbb1 小时前
C# JSON属性排序、比较 Newtonsoft.Json
后端
蓝易云1 小时前
在Linux、CentOS7中设置shell脚本开机自启动服务
前端·后端·centos
一千柯橘1 小时前
Nestjs 解决 request entity too large
javascript·后端
userkang2 小时前
消失的前后端,崛起的智能体
前端·人工智能·后端·ai·硬件工程
慧一居士2 小时前
Kafka HA集群配置搭建与SpringBoot使用示例总结
spring boot·后端·kafka
@_猿来如此3 小时前
Django 实现电影推荐系统:从搭建到功能完善(附源码)
数据库·后端·python·django
言之。3 小时前
【Go语言】ORM(对象关系映射)库
开发语言·后端·golang
极客智谷3 小时前
深入理解Java线程池:从原理到实战的完整指南
java·后端
我的耳机没电了3 小时前
mySpace项目遇到的问题
后端
陈随易4 小时前
长跑8年,Node.js框架Koa v3.0终发布
前端·后端·程序员