• Android 11 IP白名单功能


    背景

    最近做车载项目,有个需求是设置域名白名单,整体思路是增加一个接口,然后上层应用通过接口传域名到netd中,然后netd在进行DNS学习,最后将学习后的IP地址传到防火墙进行流量控制。这里就必须先讲一下netd相关知识。

    作用

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

    • 设置防火墙(Firewall)、网络地址转换(NAT)、带宽控制、无线网卡软接入点(Soft Access Point)控制,网络设备绑定(Tether)等
    • Android系统中DNS信息的缓存和管理
    • 网络服务搜索(Net Service Discovery,简称NSD)功能,包括服务注册(Service Registration)、服务搜索(Service Browse)和服务名解析(Service Resolve)等

    Netd位于Framework层和Kernel层之间,它是Android系统中网络相关消息和命令转发及处理的中枢模块,Netd的工作流程可分成两部分:

    • Netd接收并处理来自Framework层中NetworkManagementService或NsdService的命令。这些命令最终由Netd中对应的Command对象去处理。
    • Net接收并解析来自Kernel的UEvent消息,然后再转发给Framework层中对应Service去处理。

    工作流程

    1. 启动(代码位于/system/core/rootdir下的init.rc):

      ==========================================================================================

      start netd
      ==========================================================================================

    2. 配置(代码位于/system/netd/server/netd.rc):

      ==========================================================================================

      service netd /system/bin/netd
      class main
      capabilities CHOWN DAC_OVERRIDE DAC_READ_SEARCH FOWNER IPC_LOCK KILL NET_ADMIN NET_BIND_SERVICE NET_RAW SETUID SETGID
      socket dnsproxyd stream 0660 root inet
      socket mdns stream 0660 root system
      socket fwmarkd stream 0660 root inet
      onrestart restart zygote
      onrestart restart zygote_secondary
      # b/121354779: netd itself is not updatable, but on startup it dlopen()s the resolver library
      # from the DNS resolver APEX. Mark it as updatable so init won't start it until all APEX
      # packages are ready.
      updatable
      ==========================================================================================
      可以看到启动了三个Socket,分别是dnsproxyd、mdns和fwmarkd

    3. 主要逻辑梳理main.cpp:
      在main函数当中
      NetlinkManager(Android老版本会有一个CommandListener,然后通过set方法设置给NetlinkManager)
      android::net::Controllers
      DnsProxyListener                      →    socket:dnsproxyd(过程比较复杂,先是到./packages/modules/DnsResolver/include/netd_resolv/resolv.h的resolv_init,实现在DnsResolver.cpp里,最后到DnsProxyListener里)
      MDnsSdListener                        →     socket:mdns
      FwmarkServer                            →     socket:fwmarkd
      NetdNativeService                   →     binder:netd
      NetdHwService

    DNS解析白名单相关流程

    1. 在resolv.h中声明函数指针
      typedef void (*dns_resolve_result_callback)(const char* address, const char* ip);
    2. 在main里面initDNSProxy的方法里增加一个回调
      1. void dnsResolverResultCallback(const char* address, const char* ip) {
      2. gLog.info("dnsResolverResultCallback addres = %s, ip = %s", address, ip);
      3. gCtls->checkDnsResultWithWhiteList(address, ip);
      4. }
      5. bool initDnsResolver() {
      6. ResolverNetdCallbacks callbacks = {
      7. .check_calling_permission = &checkCallingPermissionCallback,
      8. .get_network_context = &getNetworkContextCallback,
      9. .log = &logCallback,
      10. .tagSocket = &tagSocketCallback,
      11. .evaluate_domain_name = &evaluateDomainNameCallback,
      12. .dns_resolve_result_callback = &dnsResolverResultCallback,
      13. };
      14. return resolv_init(&callbacks);
      15. }
    3. 在DnsResolver中注册回调
      1. bool resolv_init(const ResolverNetdCallbacks* callbacks) {
      2. ……
      3. gResNetdCallbacks.dns_resolve_result_callback = callbacks->dns_resolve_result_callback;
      4. ……
      5. }
    4. 在DnsProxyListener将DNS解析的结果返回到netd中
      1. void DnsProxyListener::GetAddrInfoHandler::run() {
      2. ……
      3. if(gResNetdCallbacks.dns_resolve_result_callback != nullptr) {
      4. struct sockaddr_in sinOriginal = *(struct sockaddr_in*) ai->ai_addr;
      5. char buf[INET6_ADDRSTRLEN]; // big enough for either IPv4 or IPv6
      6. inet_ntop(AF_INET, &sinOriginal.sin_addr.s_addr, buf, sizeof(buf));
      7. gResNetdCallbacks.dns_resolve_result_callback(mHost, buf);
      8. }
      9. ……
      10. }
    5. 在FireWall中增加响应的指令进行对应的操作

    解决编译问题

    1. 修改系统的头文件后系统会检测对应的库,如果发现系统库的头文件修改就会报编译错误,提醒你执行:./development/vndk/tools/header-checker/utils/create_reference_dumps.py  -l libnetd_resolv -product {$productname}
    2. Android查看Aosp源码
      1. cd ~/aosp //具体的源码根目录

      2. source build/envsetup.sh //用于初始化环境变量

      3. mmm development/tools/idegen/  //生成文件out/host/linux-x86/framework/idegen.jar

      4. ./development/tools/idegen/idegen.sh//源码根目录生成文件android.ipr(工程相关设置), android.iml(模块相关配置)

    调试相关

    1. 查看Dns相关的日志:

      adb shell service call dnsresolver 10 i32 1

      VERBOSE 0

      DEBUG 1

      INFO 2

      WARNING 3

      ERROR 4

    2. 查看net相关日志:dumpsys netd
    3. 查看ebpf相关map:dumpsys netd trafficcontroller
  • 相关阅读:
    MySQL数据库的简介及select语句的执行流程
    java常用集合之Map
    剑指 Offer 56 数组中数字出现的次数(异或)
    【无标题】
    nginx部署vue后显示500 Internal Server Error解决方案
    CDN策略好坏的重要性
    了解ActiveMQ、RabbitMQ、RocketMQ和Kafka的特点
    R语言 ggdendro_谱系图
    遗失备份悲剧?迅软DSE来拯救,告诉你不备份的办公文件会带来怎样的下场!
    C++ 多级继承与多重继承:代码组织与灵活性的平衡
  • 原文地址:https://blog.csdn.net/woaily1346/article/details/127899206