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

相关推荐
颜酱1 分钟前
# 02 | 搭骨架:用 LangGraph 编排 12 步工作流(思路)
前端·人工智能·后端
颜酱2 分钟前
02 | 搭骨架:用 LangGraph 编排 12 步工作流
前端·人工智能·后端
嘉伟桑1 小时前
Python调用电价API返回JSON示例:分时电价、现货电价和字段解析
后端
武子康1 小时前
Shippy:确定性工具、会话级 Sandbox 与 Live-Data Eval(4 类收敛 + 7 步实现方案 + 6 类评测指标)
前端·人工智能·后端
我叫黑大帅2 小时前
我为什么单一消费者的场景下,要用 Redis List 当消息队列?
redis·后端·面试
AskHarries2 小时前
文件上传系统
后端
止语Lab3 小时前
好的 DX 不等于少写代码——三种语言的摩擦力设计课
后端
吃饱了得干活3 小时前
别再手动解析 LLM 输出了!LangChain 四种结构化输出方案对比
后端·python·langchain
程序员天天困3 小时前
Arthas trace 命令怎么用?一行定位最慢那行代码
jvm·后端
Huiturn3 小时前
GPT 5.6 连续编码 10 小时,纯 Python 啃下 Word 二进制格式——doc2docx 实现拆解
后端