系列目录:第一篇:全景图与调用链路概览 |第二篇:内核层—netfilter/iptables与路由策略| 第三篇:Native层(上)—netd守护进程与CommandListener | 第四篇:Native层(下)—netd Controller详解 | 第五篇:Framework层(上)—ConnectivityService核心机制 | 第六篇:Framework层(下)—NMS/NPMS/NSS三大服务 | 第七篇:连接建立—WiFi/移动数据/以太网完整流程 | 第八篇:应用API层—ConnectivityManager使用与实战调试
一、为什么内核层是关键
Android 的网络能力建立在 Linux 内核之上。理解下列内核机制,才能弄懂 netd 和各种 Controller 到底在操作什么:
- netfilter / iptables:防火墙、NAT、数据包过滤——几乎所有网络安全和共享功能的基础
- 路由表与策略路由:多网络并存时,每个网络需要独立的路由表
- fwmark:Socket 标记机制,决定数据包走哪张路由表
本章直接从内核视角拆解这些基础设施,后续所有关于 netd Controller 的分析都将引用此处的概念。
二、netfilter 框架
2.1 五链四表
netfilter 是 Linux 内核中的一个数据包过滤框架,定义了五个钩子点(Hook)和四张表(Table)
网络层数据包流向: 入口 本机进程 出口 │ ▲ │ ▼ │ │ PREROUTING ──────────→ routing ──────────→ FORWARD ─┐ │ │ │ │ │ ▼ │ │ │ INPUT │ │ │ │ │ │ │ 本机进程接收 │ │ │ │ │ │ 本机进程发送 │ │ │ │ │ │ └────────────────── routing ◄────────────────┘ │ │ │ ▼ │ OUTPUT ──────────→ POSTROUTING │ ▼ 出口关键:数据包在内核中沿着这五个钩子点依次穿行,每个钩子点都可以挂载 iptables 规则链——Android 正是利用这些钩子点来实现防火墙、NAT 共享和 DNS 拦截。
五个 Hook 点的含义:
| Hook | 触发时机 | 典型用途 |
|---|---|---|
| PREROUTING | 数据包进入网络栈后立刻触发 | DNAT(目的地址转换)、入口流量过滤 |
| INPUT | 数据包路由到本机后触发 | 本机入站防火墙 |
| FORWARD | 数据包不是发给本机时触发 | 路由器上的转发过滤(网络共享的关键) |
| OUTPUT | 本机发出的数据包触发 | 本机出站防火墙 |
| POSTROUTING | 数据包即将离开网络栈时触发 | SNAT(源地址转换)、MASQUERADE |
四张表的用途:
| 表名 | 用途 | 内建链 |
|---|---|---|
| filter | 过滤数据包(允许/拒绝) | INPUT, FORWARD, OUTPUT |
| nat | 网络地址转换 | PREROUTING, INPUT, OUTPUT, POSTROUTING |
| mangle | 修改数据包内容(如 TTL、TOS) | 全部五链 |
| raw | 设置连接跟踪豁免 | PREROUTING, OUTPUT |
2.2 Android 对 netfilter 的使用
Android 大量使用 iptables 来实现各种网络功能。adb shell iptables -t nat -L -n查看一个典型设备上的规则,会发现大量由 netd 自动生成的规则:
- 网络共享(Tethering):在 nat 表的 POSTROUTING 链中添加 MASQUERADE 规则,实现类似路由器的 NAT
- 防火墙:在 filter 表的 OUTPUT 链中根据 UID 拒绝/允许出站流量(uid_owner 匹配)
- DNS 拦截:将 DNS 请求(UDP 53)重定向到 netd 的 dnsproxyd socket
三、Android 中的 iptables 命令详解
3.1 nat_controller 的网络共享规则
源码路径:system/netd/server/TetherController.cpp
当开启 WiFi 热点时,netd 的 TetherController 会执行:
# 启用 IP 转发echo1>/proc/sys/net/ipv4/ip_forward# 添加 NAT 规则(MASQUERADE)iptables-tnat-APOSTROUTING-ormnet_data0-jMASQUERADE# 添加 FORWARD 规则(允许转发)iptables-AFORWARD-iwlan0-ormnet_data0-jACCEPT iptables-AFORWARD-irmnet_data0-owlan0-mstate--stateESTABLISHED,RELATED-jACCEPT关键:MASQUERADE 是一种特殊的 SNAT——自动替换源 IP 为出口接口的 IP,是移动热点的核心技术。FORWARD 链的
ESTABLISHED,RELATED规则确保只有已建立连接的返回流量能通过,避免未授权的入站访问。
3.2 firewall_controller 的 UID 过滤
源码路径:system/netd/server/FirewallController.cpp
网络防火墙的核心是对每个 UID 的规则控制:
# 禁止 UID 10086 使用 WiFiiptables-AOUTPUT-owlan0-mowner --uid-owner10086-jREJECT# 禁止 UID 10086 使用移动数据iptables-AOUTPUT-ormnet_data0-mowner --uid-owner10086-jREJECT# 省电模式下禁止所有后台应用使用移动数据iptables-Abw_costly_rmnet_data0-mowner --uid-owner10086-jREJECT关键:
-m owner --uid-owner匹配器是 Android 内核中的扩展模块(xt_owner),根据发起 socket 的进程 UID 匹配数据包。这使得 Android 能以进程粒度控制网络访问,而非传统 Linux 的 IP/端口粒度。
-m owner --uid-owner匹配器是 Android 内核中的一个扩展模块(xt_owner),根据发起 socket 的进程 UID 匹配数据包。
3.3 DNSPROXYLISTENER 的 DNS 重定向
netd 中的 DnsProxyListener 通过 iptables 将所有的 DNS 请求重定向到 netd 的 DNS 代理端口:
源码路径:system/netd/server/DnsProxyListener.cpp
# 拦截 WiFi 上的 DNS 请求,重定向到 netd 的 dnsproxyd socketiptables-tnat-AOUTPUT-pudp--dport53-jDNAT --to-destination127.0.0.1:53# 实际上 netd 在本地监听 UDP 53 端口(通过 dnsproxyd socket)关键:DNS 拦截是 Android 网络管理的核心机制——通过将 UDP 53 端口的流量统一重定向到 netd,Android 得以按网络接口(WiFi/移动数据/VPN)使用不同的上游 DNS 服务器,并实现统一的 DNS 缓存。
通过 DNS 拦截,Android 能够:
- 统一管理 DNS 缓存
- 根据不同网络接口使用不同的 DNS 服务器
- 在 VPN 连接时使用 VPN 的 DNS
四、路由表与策略路由
4.1 传统路由表 vs Linux 策略路由
传统路由表只有一个(main),所有数据包都用同一张表决定出口。但 Android 需要支持多网络并存,所以使用了 Linux 的策略路由机制:
传统路由: 数据包 → 查 main 路由表 → 走默认路由 → 一个网络出口 策略路由(Android) 数据包 → 根据 fwmark 查 rule → 转到 netId 对应的路由表 → 使用该网络的出口关键:传统路由是"一条路走到黑",策略路由是"看标记选路"——fwmark 就是那个标记,内核根据它决定查询哪张路由表,从而让 WiFi 和移动数据的流量各走各的路。
4.2 Android 的路由表组织
源码路径:system/netd/server/RouteController.cpp
每个网络类型分配一个独立的 netId 和对应的路由表:
路由表 netId 网络类型 物理接口 ───────────────────────────────────────── 99 99 本地/回环 lo 100 100 WiFi wlan0 101 101 移动数据 rmnet_data0 102 102 VPN tun0 ...关键:路由表编号与 netId 一一对应,这个映射关系由
RouteController在/data/misc/net/rt_tables文件中维护,确保每个物理网络有独立的路由命名空间。
每个路由表的内容大致如下(以 WiFi 为例)
# 路由表 100 的内容iproute show table100# 直连路由(本网段)192.168.1.0/24 dev wlan0 proto kernel scopelinksrc192.168.1.100# 默认路由default via192.168.1.1 dev wlan0 proto static关键:每张路由表至少包含两条路由——直连路由(link scope)负责本网段通信,默认路由(default)负责所有其他流量。
RouteController通过 netlink socket 直接向内核写入这些路由条目。
4.3 fwmark 机制
fwmark(Firewall Mark)是 Linux 内核中为 Socket 打标记的机制。Android 利用它来选择路由表。
源码路径:system/netd/server/RouteController.cpp
// RouteController 中的路由优先级定义constuint32_tRULE_PRIORITY_VPN_OVERRIDE_SYSTEM=10000;constuint32_tRULE_PRIORITY_VPN_OVERRIDE_OIF=10500;constuint32_tRULE_PRIORITY_VPN_OUTPUT_TO_LOCAL=11000;constuint32_tRULE_PRIORITY_SECURE_VPN=12000;constuint32_tRULE_PRIORITY_PROHIBIT_NON_VPN=12500;关键:优先级数值越小越优先匹配。VPN 相关规则(10000-12500)排在前面,确保 VPN 流量优先于普通网络流量被路由;
PROHIBIT_NON_VPN(12500)则在安全 VPN 模式下主动丢弃未走 VPN 的数据包。
策略路由规则通过ip rule命令配置:
# 查看当前策略路由规则iprule show# 典型输出:# 0: from all lookup local → 本地表(最高优先级)# 10000: from all fwmark 0x3e8 lookup 1000 → 直连路由保护# 10500: from all fwmark 0x3e8/0xc0000 lookup 1000 → VPN 优先# 11000: from all fwmark 0x3e8/0xc0000 lookup local → VPN 到本地# 12000: from all fwmark 0x10064/0x1ffff lookup 100 → WiFi 网络(netId=100)# 13000: from all fwmark 0x20065/0x1ffff lookup 101 → 移动数据(netId=101)# ...关键:每条策略路由规则由
fwmark/掩码匹配条件决定——0x10064/0x1ffff中的0x10064提取出 netId=100 和权限位,0x1ffff掩码确保只匹配低 17 位,从而精确区分不同网络的数据包。
fwmark 的计算方式由Fwmark.h中的位域定义:
源码路径:system/netd/include/Fwmark.h
unionFwmark{uint32_tintValue;struct{unsignednetId:16;// 网络 ID(低 16 位)boolexplicitlySelected:1;// 是否显式选择boolprotectedFromVpn:1;// 是否受 VPN 保护Permission permission:2;// 权限位(18-19 位)};};关键:
Fwmark是一个 32 位 union——同一个intValue可以按位域拆解出 netId、选择标志、VPN 保护和权限四个字段,内核的ip rule匹配时直接使用intValue与掩码做按位比较。
fwmark = netId | (explicitlySelected << 16) | (protectedFromVpn << 17) | (permission << 18) 权限位: PERMISSION_NONE = 0x0 // 普通应用 PERMISSION_NETWORK = 0x1 // 特权网络应用 PERMISSION_SYSTEM = 0x3 // 系统应用(包含 NETWORK 权限)关键:权限位位于 fwmark 的高位(18-19),确保系统应用(
PERMISSION_SYSTEM = 0x3)的 fwmark 值始终大于普通应用,配合策略路由的优先级匹配可以实现"系统应用可绕过某些网络限制"的效果。
示例:
- WiFi 的 netId=100,普通应用:fwmark =
0x00000064 - 移动数据的 netId=101,系统应用:fwmark =
0x000C0065(permission=0x3 << 18)
每个 Socket 创建时可以通过setsockopt(SO_MARK)设置 fwmark,或在 connect 前调用bindServiceInfo()(Android 特有)来绑定到特定网络。
五、网络接口管理
5.1 Linux 网络接口
Android 设备上常见的网络接口:
| 接口名 | 类型 | 说明 |
|---|---|---|
| lo | 回环 | 127.0.0.1 |
| wlan0 | WiFi | IEEE 802.11 无线接口 |
| rmnet_data0 | 移动数据 | 蜂窝数据接口 |
| eth0 | 以太网 | 有线网口(平板/车机常见) |
| p2p0 | WiFi P2P | WiFi Direct 接口 |
| tun0 | VPN | VPN 虚拟网络接口 |
| rndis0 | USB 共享 | USB RNDIS 网络共享接口 |
5.2 接口生命周期
一个网络接口从出现到就绪经历:
接口创建 → 接口 UP → 配置 IP → 添加路由 → 网络就绪关键:接口创建和 UP 由内核驱动触发(netd 被动接收 netlink 事件),配置 IP 和添加路由由 Framework 主动下发命令,网络就绪则由
NetworkMonitor通过 ping 验证。这是一个"内核通知 → 用户态配置 → 验证确认"的协作流程。
各阶段对应的事件和操作:
| 阶段 | 触发 | netd 处理 | Controller |
|---|---|---|---|
| 接口创建 | 内核驱动加载 | 收到 RTM_NEWLINK 消息 | NetworkController |
| 接口 UP | 驱动启动 | 收到链路状态变化 | NetworkController |
| 配置 IP | DHCP / 静态配置 | Framework 下发命令 | NetworkController::setInterfaceConfig() |
| 添加路由 | Framework 下发 | 写入对应路由表 | RouteController |
| 网络就绪 | 路由 ping 通 | NetworkMonitor 验证 | — |
六、连接跟踪(conntrack)
netfilter 的 conntrack 模块会跟踪所有活动连接。这在网络共享(Tethering)中至关重要——NAT 需要根据连接跟踪表来决定如何转换返回的数据包。
# 查看当前连接跟踪表cat/proc/net/nf_conntrack# 典型条目:# ipv4 2 tcp 6 431999 ESTABLISHED src=192.168.43.5 dst=142.250.80.4# sport=45678 dport=443 src=10.0.0.1 dst=192.168.43.5 sport=443 dport=45678关键:conntrack 表中的每条记录都包含原始方向和回复方向两个四元组——内核正是利用这个双向映射,在 NAT 场景下自动将返回数据包的目的地址从公网 IP 还原为内网 IP。
连接跟踪也用于-m state匹配,例如允许 ESTABLISHED 和 RELATED 的连接通过防火墙。
七、小结
本篇梳理了 Android 网络子系统在内核层的三个核心基础设施:
| 机制 | 作用 | 在 Android 中的关键使用场景 |
|---|---|---|
| netfilter/iptables | 数据包过滤与修改 | 防火墙、NAT 共享、DNS 拦截 |
| 策略路由 + fwmark | 多网络出口选择 | 多网络并存、VPN 路由 |
| 连接跟踪 | 连接状态维护 | NAT 转发、防火墙状态匹配 |
下一篇将深入 Native 层,拆解 netd 的启动流程和 CommandListener 架构——它是如何接收 Framework 层的命令,并通过这些内核机制完成网络操作的。