我是靠谱客的博主 彩色钢笔,最近开发中收集的这篇文章主要介绍Netd工作流程,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

本文为《深入理解Android Wi-Fi、NFC和GPS卷》读书笔记,Android源码为Android 5.1

Netd进程由init进程根据init.rc的对应配置项而启动:
android-5.1/system/core/rootdir/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和fwmarkd。
Framework 层中的 NetworkManagementService 和 NsdService 将分别和netd及mdns监听socket建立链接并交互。
每一个调用和域名解析相关的socket API(如 getaddrinfo 或 gethostbyname 等)的进程都会借由 dnsproxyd 监听 socket 与 netd 建立链接。
下面开始分析Netd进程。
android-5.1/system/netd/server/main.cpp

int main() {

    CommandListener *cl;
    NetlinkManager *nm;
    DnsProxyListener *dpl;
    MDnsSdListener *mdnsl;
    FwmarkServer* fwmarkServer;

    ALOGI("Netd 1.0 starting");
    remove_pid_file();

	//为Netd进程屏蔽SIGPIPE信号
    blockSigpipe();

	//创建NetlinkManager
    if (!(nm = NetlinkManager::Instance())) {
        ALOGE("Unable to create NetlinkManager");
        exit(1);
    };

	//创建CommandListener,它将创建名为netd的监听socket
    cl = new CommandListener();
	//设置NetlinkManager的消息发送者(Broadcaster)为CommandListener
    nm->setBroadcaster((SocketListener *) cl);

	//启动NetlinkManager
    if (nm->start()) {
        ALOGE("Unable to start NetlinkManager (%s)", strerror(errno));
        exit(1);
    }

    // Set local DNS mode, to prevent bionic from proxying
    // back to this service, recursively.
	//为本Netd设置环境变量 ANDROID_DNS_MODE 为local
    setenv("ANDROID_DNS_MODE", "local", 1);
	//创建 DnsProxyListener,它将创建名为 dnsproxyd 的监听socket
    dpl = new DnsProxyListener(CommandListener::sNetCtrl);
    if (dpl->startListener()) {
        ALOGE("Unable to start DnsProxyListener (%s)", strerror(errno));
        exit(1);
    }

	//创建 MDnsSdListener并启动监听,它将创建名为mdns的监听socket
    mdnsl = new MDnsSdListener();
    if (mdnsl->startListener()) {
        ALOGE("Unable to start MDnsSdListener (%s)", strerror(errno));
        exit(1);
    }

	//创建 FwmarkServer并启动监听,它将创建名为fwmarkd的监听
    fwmarkServer = new FwmarkServer(CommandListener::sNetCtrl);
    if (fwmarkServer->startListener()) {
        ALOGE("Unable to start FwmarkServer (%s)", strerror(errno));
        exit(1);
    }

    /*
     * Now that we're up, we can respond to commands
     */
    if (cl->startListener()) {
        ALOGE("Unable to start CommandListener (%s)", strerror(errno));
        exit(1);
    }

    bool wrote_pid = write_pid_file();

    while(1) {
        sleep(30); // 30 sec
        if (!wrote_pid) {
            wrote_pid = write_pid_file();
        }
    }

    ALOGI("Netd exiting");
    remove_pid_file();
    exit(0);
}
Netd的main函数主要是创建几个重要成员并启动相应的工作,这四个重要成员分别如下:
NetlinkManager:接收并处理来自Kernel的UEvent消息。这些消息经NetlinkManager解析后将借助它的Broadcaster(也就是代码中为NetlinkManager设置的CommandListener)发送给Framework层的 NetworkManagementService。
CommandListener、DnsProxyListener、MDnsSDListener、FwmarkServer:分别创建名为netd、dnsproxyd、mdns、fwmarkServer的监听socket

NetlinkManager分析
NetlinkManager(以后简称NM)主要负责接收并解析来自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)) == NULL) {
        return -1;
    }

	//创建接收 RTMGRP_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)) == NULL) {
        return -1;
    }

	//创建接收 NETLINK_NFLOG 消息的socket,其值保存在 mQuotaSock 中
    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;
}
NM的start函数主要是向Kernel注册三个用于接收UEvent事件的socket,这三个UEvent分别对应于以下内容
NETLINK_KOBJECT_UEVENT: 代表 kobject 事件,由于这些事件包含的信息由ASCII字符串表述,故上述代码中使用了 NETLINK_FORMAT_ASCII。 它表示将采用字符串解析的方法去解析接收到的UEvent 消息。 kobject 一般用来通知内核中某个模块的加载或卸载。对于NM来说,其关注的是 /sys/class/net下相应模块的加载或卸载消息。
NETLINK_ROUTE:代表Kernel中routing或link改变时对应的消息。 NETLINK_ROUTE 包含很多子项,上述代码中使用了 RTMGRP_LINK 项。二者结合起来使用,表示NM希望收到网络链路断开或接通时对应的UEvent消息。由于对应UEvent消息内部封装了 nlmsghdr 等相关结构体,故上述代码使用了 NETLINK_FORMAT_BINARY来指示解析 UEvent消息时将使用二进制的解析方法。
NETLINK_NFLOG:和2.3.6节介绍的带宽控制有关。Netd中的带宽控制可以设置一个预警值,当网络数据超过一定字节数就会触发Kernel发送一个警告。该功能属于iptables 的扩展项。值得指出的是,上述代码中有关 NETLINK_NFLOG 相关socket的设置并非所有 Kernel版本都支持。同时, NFLOG_QUOTA_GROUP 的值是直接定义在 NetlinkManager.cpp中的,而非和其他类似系统定义一样定义在系统头文件中,这也表明 NFLOG_QUOTA_GROUP 的功能比较新。
NetlinkHandler接收到的UEvent消息会转换成一个NetlinkEvent对象。NetlinkEvent对象封装了对 UEvent消息的解析方法。对于 NETLINK_FORMAT_ASCII类型,其 parseAsciiNetlinkMessage函数会被调用,而对于 NETLINK_FORMAT_BINARY类型,其 parseBinaryNetlinkMessage 函数会被调用。
NM处理流程的输入为一个解析后的 NetlinkEvent对象。NM完成相应工作后,其处理结果将经由 mBroadcaster对象传递给 Framework层的接收者,也就是 NetworkManagementService。
CommandListener 从 FrameworkListener派生,而 FrameworkListener 内部有一个数组 mCommands,用来存储注册到 FrameworkListener中的命令处理对象。
下面简单了解 NetlinkHandler 的 onEvent 函数:

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")) {
        int action = evt->getAction();
        const char *iface = evt->findParam("INTERFACE");//查找消息中携带的网络设备名

        if (action == evt->NlActionAdd) {
            notifyInterfaceAdded(iface); //添加NIC(Network Interface Card)的消息
        } else if (action == evt->NlActionRemove) {
            notifyInterfaceRemoved(iface);	//NIC被移除的消息
        } else if (action == evt->NlActionChange) {
            evt->dump();
            notifyInterfaceChanged("nana", true); //NIC变化消息
        } else if (action == evt->NlActionLinkUp) { //下面两个消息来自 NETLINK_ROUTE
            notifyInterfaceLinkChanged(iface, true);	//链路启用(类此插网线)
        } else if (action == evt->NlActionLinkDown) {
            notifyInterfaceLinkChanged(iface, false);	//链路断开(类似拔网线)
        } else if (action == evt->NlActionAddressUpdated ||
                   action == evt->NlActionAddressRemoved) {
            const char *address = evt->findParam("ADDRESS");
            const char *flags = evt->findParam("FLAGS");
            const char *scope = evt->findParam("SCOPE");
            if (action == evt->NlActionAddressRemoved && 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) {
                notifyAddressChanged(action, address, iface, flags, scope);
            }
        } else if (action == evt->NlActionRdnss) {
            const char *lifetime = evt->findParam("LIFETIME");
            const char *servers = evt->findParam("SERVERS");
            if (lifetime && servers) {
                notifyInterfaceDnsServers(iface, lifetime, servers);
            }
        } else if (action == evt->NlActionRouteUpdated ||
                   action == evt->NlActionRouteRemoved) {
            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")) {	//对应 NETLINK_NFLOG
        const char *alertName = evt->findParam("ALERT_NAME");
        const char *iface = evt->findParam("INTERFACE");
        notifyQuotaLimitReached(alertName, iface);		//当数据量超过预警值,则会收到该通知

    } 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");
        if (state)
            notifyInterfaceClassActivity(label, !strcmp("active", state), timestamp);

#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
    }
}
由上边代码可知, NETLINK_KOBJECT_UEVENT和 NETLINK_ROUTE 主要反映网络设备的事件和状态,包括NIC的添加、删除和修改,以及链路的连接状态等。 NETLINK_NFLOG 用于反映设置的log是否超过配额。另外,上边代码中还处理了 xt_idletimer 的 uevent 消息,它和后文介绍的 IdleTimerCmd 有关,主要用来监视网络设备的收发工作状态。当对应设备工作或空闲时间超过设置的监控时间后, Kernel将会发送携带其状态(idle或active)的UEvent消息。
NM创建 NetlinkHandler 后,工作便转交给 NetlinkHandler来完成,而每个 NetlinkHandler对象均会单独创建一个线程用于接收 socket 消息。当 Kernel 发送 UEvent 消息后, NetlinkHandler便从select调用中返回,然后调用其 onDataAvailable 函数,该函数内部会创建一个 NetlinkEvent 对象。 NetlinkEvent 对象根据 socket 创建时指定的解析类型去解析来自 Kernel 的UEvent消息。 最终NetlinkHandler 的 onEvent 将被调用,不同的UEvent消息将在此函数中进行分类处理。 NetlinkHandler 最终将处理结果经由 NM 内部变量 mBroadcaster 转发给 NetworkManagementService。
Netd 中的第二个重要成员是 CommandListener(以后简称CL),其主要作用是接收来自 Framework 层 NetworkManageService 的命令。从角色来看,CL仅是一个Listener 。它在收到命令后,只是将它们转交给对应的命令处理对象去处理。 CL 内部定义了许多命令,而这些命令都有较深的背景知识。
CL定义了11个和网络相关的Command 类。这些类均从 NetdCommand 派生。 CL 还定义了11个控制类, 这些控制类将和命令类共同完成相应的命令处理工作。
CL创建时,需要注册自己支持的命令类:
构造函数:
android-5.1/system/netd/server/CommandListener.cpp

CommandListener::CommandListener() :
                 FrameworkListener("netd", true) {
    registerCmd(new InterfaceCmd());//注册11个命令类对象
    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());

	//创建对应的控制类对象
    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();
    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);

    /*
     * 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 modules
    //初始化 iptables 中的各个Table 及相应 Chain 和 Rules
    createChildChains(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 chains
    //netd允许OEM厂商可自定义一些规则,这些规则在 /system/bin/oem-iptables-init.sh文件中保存
    setupOemIptablesHook();

    /* When enabled, DROPs all packets except those matching rules. */
    //初始化 iptables中的一些chain,以及初始化路由表
    sFirewallCtrl->setupIptablesHooks();

    /* 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();

    //初始化时,Netd将禁止带宽控制功能
    sBandwidthCtrl->enableBandwidthControl(false);

    if (int ret = RouteController::Init(NetworkController::LOCAL_NET_ID)) {
        ALOGE("failed to initialize RouteController (%s)", strerror(-ret));
    }
}
由于 CL 的间接基类也是 SocketListener,所以其工作流程和 NetlinkHandler 类似。

CL构造函数的后半部分工作主要是利用iptables等工具创建较多的Chain和Rule,以及对某些命令控制对象进行初始化。


DnsProxyListener 和 Android 系统中的 DNS 管理有关。
getaddrinfo 函数分析
android-5.1/bionic/libc/dns/net/getaddrinfo.c

int
getaddrinfo(const char *hostname, const char *servname,
    const struct addrinfo *hints, struct addrinfo **res)
{
	//Android平台的特殊定制
	return android_getaddrinfofornet(hostname, servname, hints, NETID_UNSET, MARK_UNSET, res);
}
Android平台的 getaddrinfo 会调用其定制的 android_getaddrinfofornet 函数完成一些特殊操作:
int
android_getaddrinfofornet(const char *hostname, const char *servname,
    const struct addrinfo *hints, unsigned netid, unsigned mark, struct addrinfo **res)
{
	struct addrinfo sentinel;
	struct addrinfo *cur;
	int error = 0;
	struct addrinfo ai;
	struct addrinfo ai0;
	struct addrinfo *pai;
	const struct explore *ex;
	//取 ANDROID_DNS_MODE 环境变量
	//只有android-5.1/system/netd/server/main.cpp中设置了此变量:setenv("ANDROID_DNS_MODE", "local", 1);
	const char* cache_mode = getenv("ANDROID_DNS_MODE");

	/* hostname is allowed to be NULL */
	/* servname is allowed to be NULL */
	/* hints is allowed to be NULL */
	assert(res != NULL);
	memset(&sentinel, 0, sizeof(sentinel));
	cur = &sentinel;
	pai = &ai;
	pai->ai_flags = 0;
	pai->ai_family = PF_UNSPEC;
	pai->ai_socktype = ANY;
	pai->ai_protocol = ANY;
	pai->ai_addrlen = 0;
	pai->ai_canonname = NULL;
	pai->ai_addr = NULL;
	pai->ai_next = NULL;

	if (hostname == NULL && servname == NULL)
		return EAI_NONAME;
	if (hints) {
		/* error check for hints */
		if (hints->ai_addrlen || hints->ai_canonname ||
		    hints->ai_addr || hints->ai_next)
			ERR(EAI_BADHINTS); /* xxx */
		if (hints->ai_flags & ~AI_MASK)
			ERR(EAI_BADFLAGS);
		switch (hints->ai_family) {
		case PF_UNSPEC:
		case PF_INET:
#ifdef INET6
		case PF_INET6:
#endif
			break;
		default:
			ERR(EAI_FAMILY);
		}
		memcpy(pai, hints, sizeof(*pai));

		/*
		 * if both socktype/protocol are specified, check if they
		 * are meaningful combination.
		 */
		if (pai->ai_socktype != ANY && pai->ai_protocol != ANY) {
			for (ex = explore; ex->e_af >= 0; ex++) {
				if (pai->ai_family != ex->e_af)
					continue;
				if (ex->e_socktype == ANY)
					continue;
				if (ex->e_protocol == ANY)
					continue;
				if (pai->ai_socktype == ex->e_socktype
				 && pai->ai_protocol != ex->e_protocol) {
					ERR(EAI_BADHINTS);
				}
			}
		}
	}

	/*
	 * check for special cases.  (1) numeric servname is disallowed if
	 * socktype/protocol are left unspecified. (2) servname is disallowed
	 * for raw and other inet{,6} sockets.
	 */
	if (MATCH_FAMILY(pai->ai_family, PF_INET, 1)
#ifdef PF_INET6
	 || MATCH_FAMILY(pai->ai_family, PF_INET6, 1)
#endif
	    ) {
		ai0 = *pai;	/* backup *pai */

		if (pai->ai_family == PF_UNSPEC) {
#ifdef PF_INET6
			pai->ai_family = PF_INET6;
#else
			pai->ai_family = PF_INET;
#endif
		}
		error = get_portmatch(pai, servname);
		if (error)
			ERR(error);

		*pai = ai0;
	}

	ai0 = *pai;

	/* NULL hostname, or numeric hostname */
	for (ex = explore; ex->e_af >= 0; ex++) {
		*pai = ai0;

		/* PF_UNSPEC entries are prepared for DNS queries only */
		if (ex->e_af == PF_UNSPEC)
			continue;

		if (!MATCH_FAMILY(pai->ai_family, ex->e_af, WILD_AF(ex)))
			continue;
		if (!MATCH(pai->ai_socktype, ex->e_socktype, WILD_SOCKTYPE(ex)))
			continue;
		if (!MATCH(pai->ai_protocol, ex->e_protocol, WILD_PROTOCOL(ex)))
			continue;

		if (pai->ai_family == PF_UNSPEC)
			pai->ai_family = ex->e_af;
		if (pai->ai_socktype == ANY && ex->e_socktype != ANY)
			pai->ai_socktype = ex->e_socktype;
		if (pai->ai_protocol == ANY && ex->e_protocol != ANY)
			pai->ai_protocol = ex->e_protocol;

		if (hostname == NULL)
			error = explore_null(pai, servname, &cur->ai_next);
		else
			error = explore_numeric_scope(pai, hostname, servname,
			    &cur->ai_next);

		if (error)
			goto free;

		while (cur->ai_next)
			cur = cur->ai_next;
	}

	/*
	 * XXX
	 * If numeric representation of AF1 can be interpreted as FQDN
	 * representation of AF2, we need to think again about the code below.
	 */
	if (sentinel.ai_next)
		goto good;

	if (hostname == NULL)
		ERR(EAI_NODATA);
	if (pai->ai_flags & AI_NUMERICHOST)
		ERR(EAI_NONAME);

        /*
         * BEGIN ANDROID CHANGES; proxying to the cache
         */
	//如果是Netd进程调用,则判断不成立,因为两个都返回0
	//如果非Netd进程调用,则判断成立
	if (cache_mode == NULL || strcmp(cache_mode, "local") != 0) {
		// we're not the proxy - pass the request to them
		return android_getaddrinfo_proxy(hostname, servname, hints, res, netid);
	}

	/*
	 * hostname as alphabetical name.
	 * we would like to prefer AF_INET6 than AF_INET, so we'll make a
	 * outer loop by AFs.
	 */
	for (ex = explore; ex->e_af >= 0; ex++) {
		*pai = ai0;

		/* require exact match for family field */
		if (pai->ai_family != ex->e_af)
			continue;

		if (!MATCH(pai->ai_socktype, ex->e_socktype,
				WILD_SOCKTYPE(ex))) {
			continue;
		}
		if (!MATCH(pai->ai_protocol, ex->e_protocol,
				WILD_PROTOCOL(ex))) {
			continue;
		}

		if (pai->ai_socktype == ANY && ex->e_socktype != ANY)
			pai->ai_socktype = ex->e_socktype;
		if (pai->ai_protocol == ANY && ex->e_protocol != ANY)
			pai->ai_protocol = ex->e_protocol;

		error = explore_fqdn(pai, hostname, servname,
			&cur->ai_next, netid, mark);

		while (cur && cur->ai_next)
			cur = cur->ai_next;
	}

	/* XXX */
	if (sentinel.ai_next)
		error = 0;

	if (error)
		goto free;
	if (error == 0) {
		if (sentinel.ai_next) {
 good:
			*res = sentinel.ai_next;
			return SUCCESS;
		} else
			error = EAI_FAIL;
	}
 free:
 bad:
	if (sentinel.ai_next)
		freeaddrinfo(sentinel.ai_next);
	*res = NULL;
	return error;
}
非Netd进程调用时,会执行函数 android_getaddrinfo_proxy

static int
android_getaddrinfo_proxy(
    const char *hostname, const char *servname,
    const struct addrinfo *hints, struct addrinfo **res, unsigned netid)
{
	int sock;
	const int one = 1;
	struct sockaddr_un proxy_addr;
	FILE* proxy = NULL;
	int success = 0;

	// Clear this at start, as we use its non-NULLness later (in the
	// error path) to decide if we have to free up any memory we
	// allocated in the process (before failing).
	*res = NULL;

	// Bogus things we can't serialize.  Don't use the proxy.  These will fail - let them.
	if ((hostname != NULL &&
	     strcspn(hostname, " nrt^'"") != strlen(hostname)) ||
	    (servname != NULL &&
	     strcspn(servname, " nrt^'"") != strlen(servname))) {
		return EAI_NODATA;
	}

	//建立和Netd中 DnsProxyListener 的连接,将请求转发给它去执行
	sock = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
	if (sock < 0) {
		return EAI_NODATA;
	}

	setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
	memset(&proxy_addr, 0, sizeof(proxy_addr));
	proxy_addr.sun_family = AF_UNIX;
	strlcpy(proxy_addr.sun_path, "/dev/socket/dnsproxyd",
		sizeof(proxy_addr.sun_path));
	if (TEMP_FAILURE_RETRY(connect(sock,
				       (const struct sockaddr*) &proxy_addr,
				       sizeof(proxy_addr))) != 0) {
		close(sock);
		return EAI_NODATA;
	}

	netid = __netdClientDispatch.netIdForResolv(netid);

	// Send the request.
	//发送请求,处理回复等
	proxy = fdopen(sock, "r+");
	if (fprintf(proxy, "getaddrinfo %s %s %d %d %d %d %u",
		    hostname == NULL ? "^" : hostname,
		    servname == NULL ? "^" : servname,
		    hints == NULL ? -1 : hints->ai_flags,
		    hints == NULL ? -1 : hints->ai_family,
		    hints == NULL ? -1 : hints->ai_socktype,
		    hints == NULL ? -1 : hints->ai_protocol,
		    netid) < 0) {
		goto exit;
	}
	// literal NULL byte at end, required by FrameworkListener
	if (fputc(0, proxy) == EOF ||
	    fflush(proxy) != 0) {
		goto exit;
	}

	char buf[4];
	// read result code for gethostbyaddr
	if (fread(buf, 1, sizeof(buf), proxy) != sizeof(buf)) {
		goto exit;
	}

	int result_code = (int)strtol(buf, NULL, 10);
	// verify the code itself
	if (result_code != DnsProxyQueryResult ) {
		fread(buf, 1, sizeof(buf), proxy);
		goto exit;
	}

	struct addrinfo* ai = NULL;
	struct addrinfo** nextres = res;
	while (1) {
		uint32_t addrinfo_len;
		if (fread(&addrinfo_len, sizeof(addrinfo_len),
			  1, proxy) != 1) {
			break;
		}
		addrinfo_len = ntohl(addrinfo_len);
		if (addrinfo_len == 0) {
			success = 1;
			break;
		}

		if (addrinfo_len < sizeof(struct addrinfo)) {
			break;
		}
		struct addrinfo* ai = calloc(1, addrinfo_len +
					     sizeof(struct sockaddr_storage));
		if (ai == NULL) {
			break;
		}

		if (fread(ai, addrinfo_len, 1, proxy) != 1) {
			// Error; fall through.
			break;
		}

		// Zero out the pointer fields we copied which aren't
		// valid in this address space.
		ai->ai_addr = NULL;
		ai->ai_canonname = NULL;
		ai->ai_next = NULL;

		// struct sockaddr
		uint32_t addr_len;
		if (fread(&addr_len, sizeof(addr_len), 1, proxy) != 1) {
			break;
		}
		addr_len = ntohl(addr_len);
		if (addr_len != 0) {
			if (addr_len > sizeof(struct sockaddr_storage)) {
				// Bogus; too big.
				break;
			}
			struct sockaddr* addr = (struct sockaddr*)(ai + 1);
			if (fread(addr, addr_len, 1, proxy) != 1) {
				break;
			}
			ai->ai_addr = addr;
		}

		// cannonname
		uint32_t name_len;
		if (fread(&name_len, sizeof(name_len), 1, proxy) != 1) {
			break;
		}
		name_len = ntohl(name_len);
		if (name_len != 0) {
			ai->ai_canonname = (char*) malloc(name_len);
			if (fread(ai->ai_canonname, name_len, 1, proxy) != 1) {
				break;
			}
			if (ai->ai_canonname[name_len - 1] != '') {
				// The proxy should be returning this
				// NULL-terminated.
				break;
			}
		}

		*nextres = ai;
		nextres = &ai->ai_next;
		ai = NULL;
	}

	if (ai != NULL) {
		// Clean up partially-built addrinfo that we never ended up
		// attaching to the response.
		freeaddrinfo(ai);
	}
exit:
	if (proxy != NULL) {
		fclose(proxy);
	}

	if (success) {
		return 0;
	}

	// Proxy failed;
	// clean up memory we might've allocated.
	if (*res) {
		freeaddrinfo(*res);
		*res = NULL;
	}
	return EAI_NODATA;
}
当非Netd进程调用 getaddrinfo 时,会调用函数 android_getaddrinfo_proxy, 它将和Netd进程中的 dnsproxyd 监听 socket建立连接,然后把请求发给 DNSProxyListener去执行。当然最后还是会调回到 getaddrinfo 去执行,而此时就变成了 Netd进程的调用了,Netd进程将向指定的 DNS 服务器发起请求以解析域名。
Android系统中通过这种方式来管理DNS的好处是,所有解析后得到的 DNS 记录都将缓存在 Netd 进程中,从而使这些信息成为一个公共的资源,最大程度做到信息共享。

MDnsSdListener: Multicast DNS Service Discovery
组播服务
MDnsSdListener 对应的 Framework 层服务为 NsdService(Nsd为 Network Service Discovery的缩写)。
MDnsSdListener的内部类 Monitor 用于和 mdnsd 后台进程通信,它将调用Bonjour AIP。
Monitor 内部针对每个 DNSService 都会建立一个 Element 对象,该对象通过 Monitor 的 mHead 指针保存在一个list中。
Handler 是 MDnsSdListener 注册的 Command。
MDnsSdListener 运行过程主要可分成三步:
1.Netd创建 MDnsSdListener对象,其内部会创建 Monitor对象,而Monitor对象将启动一个线程用于和 mdnsd通信,并接收来自Handler的请求。
2.NsdService 启动完毕后将向 MDnsSdListener发送 "start-service"命令。
3.NsdService 响应应用程序的请求,向 MDnsSdListener 发送其他命令,例如 "discovery" 等。Monitor将最终处理这些请求。
先看第一步,当 MDnsSdListener 构造时,会创建一个 Monitor 对象:
android-5.1/system/netd/server/MDnsSdListener.cpp

MDnsSdListener::Monitor::Monitor() {
    mHead = NULL;
    mLiveCount = 0;
    mPollFds = NULL;
    mPollRefs = NULL;
    mPollSize = 10;
	//创建两个socket,用于接收 MDnsSdListener 对象的命令
    socketpair(AF_LOCAL, SOCK_STREAM, 0, mCtrlSocketPair);
    pthread_mutex_init(&mHeadMutex, NULL);

	//创建线程,线程函数是 threadStart,其内部会调用 run
    pthread_create(&mThread, NULL, MDnsSdListener::Monitor::threadStart, this);
    pthread_detach(mThread);
}
Monitor的threadStart线程将调用其 run 函数,该函数通过 poll 方式侦听包括 mCtrlSocketPair 在内的 socket 信息。
当 NsdService 发送 start-service 命令后, Handler 的 runCommand 将执行 Monitor 的 startService 函数:
android-5.1/system/netd/server/MDnsSdListener.cpp

int MDnsSdListener::Monitor::startService() {
    int result = 0;
    char property_value[PROPERTY_VALUE_MAX];
    pthread_mutex_lock(&mHeadMutex);
	//MDNS_SERVICE_STATUS是一个字符串,值为"init.svc.mdnsd",在 init.rc 配置文件中, mdnsd是一个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 命令启动 mdnsd
        property_set("ctl.start", MDNS_SERVICE_NAME);
		//如果mdnsd成功启动,则属性变为running
        wait_for_property(MDNS_SERVICE_STATUS, "running", 5);
        result = -1;
    } else {
        result = 0;
    }
    pthread_mutex_unlock(&mHeadMutex);
    return result;
}
startService的实现比较有趣,充分利用了init的属性控制以启动 mdnsd 进程。
当 NsdService 发送注册服务请求时, Handler 的 serviceRegister 函数将被调用:
android-5.1/system/netd/server/MDnsSdListener.cpp

void MDnsSdListener::Handler::serviceRegister(SocketClient *cli, int requestId,
        const char *interfaceName, const char *serviceName, const char *serviceType,
        const char *domain, const char *host, int port, int txtLen, void *txtRecord) {
    if (VDBG) {
        ALOGD("serviceRegister(%d, %s, %s, %s, %s, %s, %d, %d, <binary>)", requestId,
                interfaceName, serviceName, serviceType, domain, host, port, txtLen);
    }
    Context *context = new Context(requestId, mListener);
    DNSServiceRef *ref = mMonitor->allocateServiceRef(requestId, context);
    port = htons(port);
    if (ref == NULL) {
        ALOGE("requestId %d already in use during register call", requestId);
        cli->sendMsg(ResponseCode::CommandParameterError,
                "RequestId already in use during register call", false);
        return;
    }
    DNSServiceFlags nativeFlags = 0;
    int interfaceInt = ifaceNameToI(interfaceName);
	//调用 Bonjour API DNSServiceRegister,并注册回调函数 MDnsSdListenerRegisterCallback
    DNSServiceErrorType result = DNSServiceRegister(ref, interfaceInt, nativeFlags, serviceName,
            serviceType, domain, host, port, txtLen, txtRecord, &MDnsSdListenerRegisterCallback,
            context);
    if (result != kDNSServiceErr_NoError) {
        ALOGE("service register request %d got an error from DNSServiceRegister %d", requestId,
                result);
        mMonitor->freeServiceRef(requestId);
        cli->sendMsg(ResponseCode::CommandParameterError,
                "serviceRegister request got an error from DNSServiceRegister", false);
        return;
    }
	调用 Monitor 对象进行 rescan
    mMonitor->startMonitoring(requestId);
    if (VDBG) ALOGD("serviceRegister successful");
    cli->sendMsg(ResponseCode::CommandOkay, "serviceRegister started", false);
    return;
}
DNSServiceRegister 内部将把请求发送给 mdnsd 去处理,处理的结果通过 MDnsSdListenerRegisterCallback 返回:

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);
    char *msg;
    int refNumber = context->mRefNumber;
    if (errorCode != kDNSServiceErr_NoError) {
        asprintf(&msg, "%d %d", refNumber, errorCode);
        context->mListener->sendBroadcast(ResponseCode::ServiceRegistrationFailed, msg, false);
        if (DBG) ALOGE("register failure for %d, error= %d", refNumber, errorCode);
    } else {
        char *quotedServiceName = SocketClient::quoteArg(serviceName);
        asprintf(&msg, "%d %s", refNumber, quotedServiceName);
        free(quotedServiceName);
		//将处理结果返回给 NsdService
        context->mListener->sendBroadcast(ResponseCode::ServiceRegistrationSucceeded, msg, false);
        if (VDBG) ALOGD("register succeeded for %d as %s", refNumber, serviceName);
    }
    free(msg);
}
Netd的处理流程:
1、NM接收 Kernel 的UEvent消息,然后转发给 Framework 层的客户端
2、CL、DPL以及 MDnsSdListener 接收来自客户端的请求并处理它们。

最后

以上就是彩色钢笔为你收集整理的Netd工作流程的全部内容,希望文章能够帮你解决Netd工作流程所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(38)

评论列表共有 0 条评论

立即
投稿
返回
顶部