Android路由管理

1 阅读6分钟

一、系统架构总览

应用层
  ├─ ConnectivityManager API
  │  ├─ requestNetwork()
  │  ├─ setProcessDefaultNetwork()
  │  └─ bindProcessToNetwork()
  └─ Socket APIs
     ├─ SO_BINDTODEVICE
     ├─ SO_MARK (fwmark)
     └─ IP_PKTINFO

    ↓ IPC (Binder)

系统层 (frameworks_base)
  ├─ ConnectivityService
  │  └─ 网络生命周期管理
  ├─ NetworkPolicyManagerService
  │  └─ 流量限制策略
  └─ RoutingCoordinatorService
     └─ 高层路由协调

    ↓ IPC (Binder)

netd 层 (system_netd) - C++ 实现
  ├─ RouteController
  │  ├─ 策略路由规则 (Policy-based Routing)
  │  ├─ 路由表管理
  │  └─ 接口转发
  ├─ NetlinkCommands
  │  └─ netlink socket 通信
  └─ Fwmark/FwmarkCommand
     └─ Socket 标记管理

    ↓ netlink socket

Linux Kernel (5.x+)
  ├─ netlink 子系统
  ├─ 路由表 (main, local, per-interface tables)
  ├─ 策略路由规则库 (FIB rules)
  ├─ iptables/nftables 防火墙
  └─ 网络栈 (TCP/IP, IPv6等)

二、核心组件详解

2.1 Fwmark 标记系统

位置system_netd/include/Fwmark.h

作用:对 socket 进行标记,用于策略路由决策

struct Fwmark {
    // 位字段定义
    uint32_t netId : 16;              // 网络ID (0-65535)
    uint32_t explicitlySelected : 1;  // 是否明确选择网络
    uint32_t protectedFromVpn : 1;    // 是否受 VPN 保护
    uint32_t permission : 2;          // 权限标记 (NONE/SYSTEM/INTERNET等)
    uint32_t reserved : 12;           // 保留位
    
    uint32_t intValue;  // 用于内核操作
};

标记的应用

  1. 进程级标记 - 通过 setsockopt(SO_MARK) 标记 socket
  2. 进制数据包标记 - iptables MARK 规则标记入站数据包
  3. 路由决策 - 内核根据标记选择路由表

2.2 策略路由规则系统

文件system_netd/server/RouteController.cpp

规则优先级(从高到低):

// 规则优先级
RULE_PRIORITY_LOCAL_ROUTES              = 9000
RULE_PRIORITY_SECURE_VPN                = 10000
RULE_PRIORITY_BYPASSABLE_VPN_*          = 11000-12000
RULE_PRIORITY_VPN_OVERRIDE_*            = 13000-14000
RULE_PRIORITY_EXPLICIT_NETWORK          = 16000
RULE_PRIORITY_UID_EXPLICIT_NETWORK      = 17000-18000
RULE_PRIORITY_IMPLICIT_NETWORK          = 19000
RULE_PRIORITY_UID_IMPLICIT_NETWORK      = 20000-21000
RULE_PRIORITY_OUTPUT_INTERFACE          = 22000
RULE_PRIORITY_UID_LOCAL_ROUTES          = 23000
RULE_PRIORITY_LOCAL_NETWORK             = 24000
RULE_PRIORITY_LEGACY_SYSTEM             = 25000
RULE_PRIORITY_VPN_FALLTHROUGH           = 26000
RULE_PRIORITY_LEGACY_NETWORK            = 27000
RULE_PRIORITY_MAIN                      = 32000

关键规则示例

// 1. VPN UID 范围规则
modifyVpnUidRangeRule(table, uidStart, uidEnd, priority, secure, add)
  → rtnetlink RTM_NEWRULE/RTM_DELRULE
  → 匹配条件: fwmark (protectedFromVpn), UID range
  → 指向: VPN 路由表
  → 作用: 将 UID 范围内的流量路由到 VPN

// 2. 显式网络选择规则
modifyExplicitNetworkRule(netId, table, permission, uidStart, uidEnd)
  → rtnetlink RTM_NEWRULE/RTM_DELRULE
  → 匹配条件: fwmark (netId, explicitlySelected), UID range, permission
  → 指向: 网络专用路由表
  → 作用: 应用显式选择的网络

// 3. 隐式网络规则
modifyImplicitNetworkRule(netId, table)
  → rtnetlink RTM_NEWRULE/RTM_DELRULE
  → 匹配条件: fwmark (netId, !explicitlySelected)
  → 指向: 网络专用路由表
  → 作用: connect() 时绑定的网络

2.3 网络接口路由表管理

每个物理网络接口对应一个路由表:

接口: wlan0  → 路由表: 10000
接口: rmnet0 → 路由表: 10001
接口: eth0   → 路由表: 10002

计算方式: table_id = interface_index + ROUTE_TABLE_OFFSET_FROM_INDEX

路由表类型:
├─ Global table (主表)
│  └─ 用于 unicast routes, default route 等
├─ Local table (<interface>_local)
│  └─ 用于本地连接路由
└─ 特殊表
   ├─ RT_TABLE_LOCAL (255) - 本地路由
   ├─ RT_TABLE_MAIN (254) - 主路由表
   ├─ ROUTE_TABLE_LOCAL_NETWORK (97) - 本地网络特殊路由
   ├─ ROUTE_TABLE_LEGACY_NETWORK (98) - 遗留网络
   └─ ROUTE_TABLE_LEGACY_SYSTEM (99) - 遗留系统

三、路由决策流程

Socket 创建:
  ↓
socket() → 内核分配 socketsetsockopt(SO_MARK) 或自动标记
  ↓
fwmark 设置:
  ├─ app 显式选择: explicitlySelected = 1
  ├─ app 隐式绑定: explicitlySelected = 0
  ├─ 权限检查: permission 字段
  ├─ VPN 保护: protectedFromVpn 字段
  └─ 网络ID: netId 字段

connect()/send():
  ↓
内核查询 FIB 规则:
  1. 遍历规则表 (按优先级从高到低)
  2. 匹配规则条件 (fwmark, UID, IIF等)
  3. 第一条匹配 → 使用指定的路由表
  
路由表选择逻辑:
  ├─ VPN 保护 && 在 VPN UID 范围
  │  → 路由绕过 VPN
  ├─ 显式选择网络 && 权限检查通过
  │  → 使用选定网络的路由表
  ├─ 连接到默认网络
  │  → 使用默认网络的路由表
  └─ 无匹配规则
     → 使用主路由表 (main)

路由查找 (查询路由表):
  1. 最长前缀匹配 (Longest Prefix Match)
  2. 查找目标 IP 对应的路由条目
  3. 获得下一跳或出接口
  4. 可选: 根据 MTU/priority 调整

发送数据包:
  ├─ 源地址选择
  ├─ ARP 解析 (IPv4)
  ├─ 邻接表查找
  └─ 驱动级发送

四、路由表的生命周期

1. 接口连接时:
   ConnectivityService 通知 netd
   ↓
   RouteController.createPhysicalNetwork()
   ↓
   为接口分配路由表 ID
   
2. 获得 IP 地址时:
   DHCP/RA 获得 IP
   ↓
   RouteController.addRoute()
   ↓
   添加路由条目到接口路由表
   ├─ 直连路由 (connected routes)
   ├─ 默认路由 (default route)
   └─ 其他静态路由

3. 网络切换时:
   新网络成为默认
   ↓
   RouterController.updateDefaultNetwork()
   ↓
   修改策略规则优先级
   ├─ 旧网络隐式规则优先级降低
   └─ 新网络隐式规则优先级提升

4. 接口断开时:
   ConnectivityService 通知 netd
   ↓
   RouteController.destroyPhysicalNetwork()
   ↓
   清理所有规则和路由:
   ├─ RouteController.flushRoutes(interface)
   ├─ RouteController.removeInterfaceFromNetwork()
   └─ RouteController.removeUsersFromNetwork()

五、特殊场景处理

5.1 VPN 分隧道 (Split Tunnel)

VPN 未拦截流量的处理:

1. VPN 指定 underlying networks:
   declaredUnderlyingNetworks = {network1, network2}

2. 创建 fallthrough 规则:
   modifyVpnFallthroughRule()
   ├─ VPN netId 的规则
   ├─ 如果 VPN 表中无路由
   ├─ 则查询 underlying network 的路由表
   └─ 允许流量绕过 VPN

3. 数据流:
   源地址 → VPN fwmark → VPN 表查询
   ├─ 有匹配路由 → 通过 VPN
   └─ 无匹配路由 → fallthrough 规则 → underlying network 表

5.2 NAT64 (464xlat)

IPv6-only 网络上的 IPv4 支持:

Nat464Xlat 流程:
1. 检测网络类型: IPv6 only + 无 IPv4
2. NAT64 前缀发现:
   ├─ Router Advertisement (RA) 中的前缀
   ├─ DNS 查询 (RFC 7050 - well-known prefix)
   └─ 默认前缀: 64:ff9b::/96

3. 启动 clatd 守护进程:
   ├─ 创建虚拟 TUN 接口 (v4-<iface>)
   ├─ 配置 IPv4 地址 (192.0.0.4)
   ├─ 翻译 IPv4 ↔ IPv6 数据包
   └─ 管理路由规则

4. 路由配置:
   IPv4 流量 (0.0.0.0/0)
   ├─ 进入 v4-wlan0 interface
   ├─ clatd 翻译成 IPv6
   ├─ 通过原始网络发送
   └─ 响应翻译回 IPv4

5.3 多播路由 (Multicast Routing)

MulticastRoutingCoordinatorService:

1. 添加虚拟接口 (MIF):
   ├─ 映射物理接口  虚拟索引
   ├─ 最多 32 个虚拟接口
   └─ 通过 setsockopt(MRT6_ADD_MIF) 配置

2. 多播转发缓存 (MFC):
   MFC Key: (incoming interface, source address, group address)
   MFC Value: {outgoing interfaces, last used time}
   
   作用: 决定组播数据包转发方向

3. 配置模式:
   ├─ FORWARD_NONE - 不转发
   ├─ FORWARD_WITH_MIN_SCOPE - 基于作用域转发
   └─ FORWARD_SELECTED - 选择特定组地址转发

4. 组播组管理:
   ├─ JOIN: 表示对该组的兴趣
   ├─ LEAVE: 停止接收
   └─ NOCACHE: kernel upcall 触发 MFC 添加

六、调试和监测

# 查看所有路由规则
adb shell ip rule list [all]

# 查看特定路由表
adb shell ip route show table <table_id>
adb shell ip route show table wlan0
adb shell ip route show table all

# 查看 netid 对应的路由表
adb shell netd list_table_for_interface <iface>

# 跟踪 socket 标记
adb logcat | grep -i fwmark
adb logcat | grep -i "RouteController"

# 查看连接状态
adb shell ss -tlnp | grep -E ":(80|443)"
adb shell netstat -an | grep ESTABLISHED

# 网络命名空间操作
adb shell ip netns list
adb shell ip netns exec <netns> ip route show

# dumpsys 查看路由状态
adb shell dumpsys connectivity --verbose

# 实时路由追踪
adb shell tcpdump -i any -n "host <ip>"

七、Android 15 路由改进

新特性:
├─ eBPF 路由策略强化
│  └─ 基于 BPF programs 的高性能路由
├─ 网络切片支持
│  ├─ 5G URSP (UE Route Selection Policy)
│  ├─ Traffic Descriptor 匹配
│  └─ NSSAI (Network Slice Selection)
├─ 改进的 VPN 支持
│  ├─ 应用级 VPN 选择
│  ├─ VPN 网络隔离
│  └─ 更精细的权限控制
└─ 性能优化
   ├─ 缓存优化
   ├─ 规则合并
   └─ 内核-用户态交互优化

这个分析涵盖了 Android 路由系统从应用层到内核层的完整技术栈,包括策略路由、特殊场景处理和调试方法。