前言
前阵子问一个老同事,我说你觉得android数据网络管理这部分知识点的核心是什么?
他说:apn管理,网络评分机制,这些是最基础最简单的,我认为的核心应该是策略路由。
问题来了,什么是策略路由呢?
基本概念
我们都熟悉路由器,它是三层设备。路由器的作用是把数据包转发,将数据包从一个网络转发到另一个网络,那么路由器是如何确定数据包的下一跳转发到哪个路由设备呢,答案是通过路由表。 路由表的两个作用:选择网卡和指定下一跳地址(网关地址–指路由器地址)。
路由表本身很简单,而基于策略的路由比传统路由在功能上更强大,使用更灵活,它不仅能够根据目的地址,而且能够根据报文大小,应用或IP源地址等属性来选择转发路径。简单说就是Linux有多张路由表,而路由策略会根据一些条件,将路由请求转向不同的路由表。比如源地址在某些范围内走路由表A,另外的数据包走路由表B,类似这样的规则是有策略路由来控制。
ip命令
Linux下,策略路由主要是通过ip命令来管理的。所以要学习策略路由,首先要熟悉ip命令,它是Linux系统中一个强大的网络管理工具,主要功能包括:
- 可替代ifconfig命令,通过ip工具可管理系统中的网络接口,包括配置和查看网络接口情况,使能或禁止指定的网络接口。
- 可替代route命令,通过ip工具设置主机路由,网络路由,网关参数等。
- 可替代arp命令,通过ip工具查看,修改和管理系统的ARP缓存等。
命令详解
ip的命令格式如下(可通过在命令行输入 ip -h 查询):
ip [OPTIONS] OBJECT {COMMAND | help}
对象OBJECT = {link | addr | route | rule | addrlabel | neigh | ntable | tunnel | xfrm | maddr | …}
选项OPTIONS = {-V[ersion] | -S[tatistics] | -d[etails] | -r[esolve] | -h[uman-readable] | -f[amily] …}
命令COMMAND一般是add, del, show之类的操作命令。
常用的对象取值
对象 | 描述 |
---|---|
link | 网络设备 |
address | 设备上的协议地址(IP/IPv6地址) |
route | 路由表条目 |
rule | 路由策略数据库中的规则 |
neighbour | 邻居表 |
tunnel | IP隧道 |
maddress | 多播地址 |
常用的选项取值
选项 | 描述 |
---|---|
-V,-Version | 显示指令版本信息 |
-s,-stats, -statistics | 输出详细信息 |
-h, -human, -human-readable | 输出人类可读的统计信息和后缀 |
-o, -oneline | 将每条记录输出到一行 |
常用的ip命令
ip命令 | 说明 |
---|---|
ip addr show | 显示网卡及配置的地址信息 |
ip addr add 192.168.0.123/24 dev eth0 | 设置IP |
ip link set eth0 up | 启用被禁用的网卡 |
ip link set eth0 down | 禁用网卡 |
ip route show | 查看路由信息 |
ip route add default via 192.168.0.150/24 | 所有数据包都通过192.168.0.150来转发 |
ip -s link | 显示所有网络接口的统计信息 |
ip -s link ls eth0 | 获取特定网络接口的信息 |
ip rule show | 显示路由策略信息 |
路由表管理
查看路由表
ip route命令用于查看并编辑计算机的IP路由表。linux可以定义1-252个路由表,linux系统维护4个路由表。在linux系统中,可以在 /etc/iproute2/rt_tables 中查看:
编号 | 表名 | 描述 |
---|---|---|
0 | unspec | 系统保留表 |
255 | local | 本地路由表,存有本地接口地址,广播地址,NAT地址。由系统自动维护,管理员不能操作此表 |
254 | main | 主路由表,ip route命令若没指定表都是默认操作这个表 |
253 | default | 默认路由表,一般存放默认路由 |
安卓系统中,netd进程会创建更多的路由表,创建的逻辑和流程可以查阅代码:\netd\server\RouteController.cpp。
可以通过adb命令查看文件 /data/misc/net/rt_tables,路由表如下所示:
显示路由信息
通过ip route show table命令,我们来查看android手机中路由信息。
1.查看main路由表
seaice:/ # ip route show table main
10.20.173.184/29 dev rmnet_data0 proto kernel scope link src 10.20.173.188
这里表示如果来源为10.20.173.188的数据包要发到10.20.173.184/29这个网段,直接通过rmnet_data0这个网络接口发出。
2.查看rmnet_data0路由表
seaice:/ # ip route show table rmnet_data0
default via 10.20.173.189 dev rmnet_data0 proto static mtu 1460
10.20.173.184/29 dev rmnet_data0 proto static scope link
最后一行表示发往这个网段10.20.173.184/29的数据包,直接通过rmnet_data0这个网络接口发出。倒数第二行表示其他数据包统一转发给10.20.173.189主机处理。
添加路由
先查看main表的内容,再添加一条路由信息,最后显示结果:
seaice:/ # ip route show table main
10.20.173.184/29 dev rmnet_data0 proto kernel scope link src 10.20.173.188
seaice:/ # ip route add 192.168.2.0/24 via 10.20.173.189 table main
seaice:/ # ip route show table main
10.20.173.184/29 dev rmnet_data0 proto kernel scope link src 10.20.173.188
192.168.2.0/24 via 10.20.173.189 dev rmnet_data0
删除路由
先查看main表的内容,然后删除一条路由信息,最后显示结果:
seaice:/ # ip route show table main
10.20.173.184/29 dev rmnet_data0 proto kernel scope link src 10.20.173.188
192.168.2.0/24 via 10.20.173.189 dev rmnet_data0
seaice:/ # ip route del 192.168.2.0/24 table main
seaice:/ # ip route show table main
10.20.173.184/29 dev rmnet_data0 proto kernel scope link src 10.20.173.188
路由策略
在linux系统中,一条路由策略主要包含三个信息:
- 优先级
- 条件
- 路由表
其中优先级数字越小表示优先级越高,然后是满足某个条件下,由指定的路由表来进行路由。
ip rule命令格式
Usage: ip rule [ list | add | del ] SELECTOR ACTION
SELECTOR := [ from PREFIX ] [ to PREFIX ] [ tos TOS ][ dev STRING ] [ pref NUMBER ]
ACTION := [ table TABLE_ID ] [ nat ADDRESS ][ prohibit | reject | unreachable ]
[ flowid CLASSID ]
TABLE_ID := [ local | main | default | new | NUMBER ]
参数解析如下:
From -- 源地址
To -- 目的地址(这里是选择规则时使用,查找路由表时也使用)
Tos -- IP包头的TOS(type of sevice)域
Dev -- 物理接口
Fwmark -- iptables标签
查看路由策略数据库
使用ip rule list命令,查看路由策略数据库的内容。下面是linux系统下执行命令后得到的结果,可以看到系统的三条默认规则,而这三条规则默认分别对应于local、main以及default三个路由表。
seaice@seaice-VirtualBox:~$ ip rule list
0: from all lookup local
32766: from all lookup main
32767: from all lookup default
linux系统中是按照规则的优先级顺序依次匹配。首先,系统会根据规则0在本地路由表(local)里寻找路由,如果目的地址是本地网络或广播网络,就可以找到匹配的路由;如果没有找到路由,就会匹配下一个不空的规则,这里是规则32766,那么会在主路由表里面寻找路由;如果还没有找到,则匹配规则32767,在默认路由表里面寻找;如果最后还是没找到,则路由失败。
优先级 | 条件 | 路由表 |
---|---|---|
0 | from all | local |
32766 | from all | main |
32767 | from all | default |
安卓系统中,netd进程会创建更多的路由策略,创建的逻辑和流程可以查阅代码:\netd\server\RouteController.cpp。通过adb命令执行ip -4 rule show,结果如下所示:
ip -4 rule list
0: from all lookup local
10000: from all fwmark 0xc0000/0xd0000 lookup legacy_system
11000: from all iif lo oif dummy0 uidrange 0-0 lookup dummy0
11000: from all iif lo oif rmnet_data2 uidrange 0-0 lookup rmnet_data2
11000: from all iif lo oif rmnet_data0 uidrange 0-0 lookup rmnet_data0
16000: from all fwmark 0x10063/0x1ffff iif lo lookup local_network
16000: from all fwmark 0xd006a/0xdffff iif lo lookup rmnet_data2
16000: from all fwmark 0x1006b/0x1ffff iif lo lookup rmnet_data0
17000: from all iif lo oif dummy0 lookup dummy0
17000: from all fwmark 0xc0000/0xc0000 iif lo oif rmnet_data2 lookup rmnet_data2
17000: from all iif lo oif rmnet_data0 lookup rmnet_data0
18000: from all fwmark 0x0/0x10000 lookup legacy_system
19000: from all fwmark 0x0/0x10000 lookup legacy_network
20000: from all fwmark 0x0/0x10000 lookup local_network
23000: from all fwmark 0x6b/0x1ffff iif lo lookup rmnet_data0
29000: from all fwmark 0x0/0xffff iif lo lookup rmnet_data0
32000: from all unreachable
现在来对其中的某些策略路由进行解析
10000: from all fwmark 0xc0000/0xd0000 lookup legacy_system
优先级为10000的策略表示,所有的数据包(from all), 其iptables的mark和0xd0000按位与后,所得结果为0xc0000的数据包(fwmark 0xc0000/0xd0000),使用legacy_system路由表进行路由查找(lookup legacy_system)。
11000: from all iif lo oif rmnet_data0 uidrange 0-0 lookup rmnet_data0
优先级为11000的策略表示,所有是的数据包(from all),如果是从lo回环接口输入(iif lo),从rmnet_data0接口输出(oif rmnet_data0),其uid为0(uidrange 0-0)即系统用户,使用rmnet_data0路由表进行路由查找。lo接口的作用是,假如一个本地进程向另一个本地进程发送数据,那么将会使用lo接口,此时如果在rmnet_data0接口上抓包是无法抓到的,但是在lo接口上能够抓到。
29000: from all fwmark 0x0/0xffff iif lo lookup rmnet_data0
优先级为29000的策略表示,所有的数据包,其iptables的mark和0xffff按位与后,所得结果为0x0,且是从lo回环接口输入的数据包,使用rmnet_data0路由表。在不主动设置数据包的mark时,数据包的mark就是0,所以在不设置mark的时候,数据包通常会满足这条路由策略。
添加路由策略
在添加策略前时,要确认好条件,优先级,路由表ID,再执行添加规则的操作。条件是用来判断哪些数据包符合这条策略,可以用来匹配的字段包括数据包的源地址,目的地址,TOS,fwmark, dev等等。那么下面我们来演示一下。
根据数据包的源地址来决定参考那个路由表。下面两个命令指出,如果数据包的来源IP是109.131.7.10, 就参考路由表10;如果来源端的IP是109.131.8.0/24网段的IP,就参考路由20。
root@seaice-VirtualBox:/home/seaice# ip rule add from 109.131.7.10 table 10
root@seaice-VirtualBox:/home/seaice# ip rule add from 109.131.8.0/24 table 20
root@seaice-VirtualBox:/home/seaice# ip rule list
0: from all lookup local
32764: from 109.131.8.0/24 lookup 20
32765: from 109.131.7.10 lookup 10
32766: from all lookup main
32767: from all lookup default
可以发现,在添加规则时,如果没有特别设置优先级别,那么,优先级别默认会从32766开始递减,如32765、32764……,如果我们需要特别设置优先级别,可以在ip rule add命令的最后加上prio XXX参数。
根据数据包目的地址与上面类似。
root@seaice-VirtualBox:/home/seaice# ip rule add to 109.131.9.0/24 table 30
root@seaice-VirtualBox:/home/seaice# ip rule add to 109.131.10.10 table 40
根据fwmark作为匹配条件时,需要和iptables打标签配合使用。比如我们想把发出去的http数据包去寻找特定的路由表寻找路由,假设路由表是50。那么我们可以先把http的数据包mark值设为1,再通过路由策略判断fwmark的值,去寻找指定的路由表。
seaice# iptables -t mangle -A PREROUTING -p tcp --dport 80 -j MARK --set-mark 1
seaice# ip rule add fwmark 1 table 50
最后,我们还可以使用数据包输入的接口来作为判断依据,如下所示,我们希望凡是由enp0s3接口送入的数据包都去查找路由表55进行路由。
root@seaice-VirtualBox:/# ip rule add dev enp0s3 table 55
删除路由策略
ip命令提供删除规则的命令很灵活,可以使用优先级,条件,路由表其中一个值来删除规则。先看上面添加规则后,目前的路由策略信息,如下所示:
root@seaice-VirtualBox:/# ip rule list
0: from all lookup local
32760: from all iif enp0s3 lookup 55
32761: from all fwmark 0x1 lookup 50
32762: from all to 109.131.10.10 lookup 40
32763: from all to 109.131.9.0/24 lookup 30
32764: from 109.131.8.0/24 lookup 20
32765: from 109.131.7.10 lookup 10
32766: from all lookup main
32767: from all lookup default
我们根据上面所说的删除命令,执行过程如下所示
root@seaice-VirtualBox:/# ip rule del prio 32760
root@seaice-VirtualBox:/# ip rule del fwmark 1
root@seaice-VirtualBox:/# ip rule del table 40
root@seaice-VirtualBox:/# ip rule del table 30
root@seaice-VirtualBox:/# ip rule del from 109.131.8.0/24
root@seaice-VirtualBox:/# ip rule del prio 32765
root@seaice-VirtualBox:/# ip rule list
0: from all lookup local
32766: from all lookup main
32767: from all lookup default
创建新路由表
单纯添加路由表并没有意义,因为新增出来的路由表,系统默认是不会去使用的。如果要添加到main以外的路由表,只有先添加规则才能确定新的路由表名称(table id),有了新的路由表之后,才会把路由添加到新的路由表中。
ip rule add from 192.168.2.0/24 table 10
ip route add 192.168.1.0/24 dev eth1 table 10
ip route add default via 192.168.1.254 table 10
ip route show table 10
注意!不要混淆路由表(route)和规则(rule):ip rule规则指向路由表,多个规则可以引用一个路由表,而且某些路由表可以没有策略指向它。如果系统管理员删除了指向某个路由表的所有规则,这个表就没有用了,但是仍然存在,直到里面的所有路由都被删除,它才会消失。
fwmark的定义
Android 通过多个路由表 + fwmark的形式,来设置整体路由策略,确定网络请求通过哪块网卡发出。 在多网卡的情况下,包括数据网络、wifi网络、以太网网络,网络管理程序可以进行灵活的配置,指定数据从哪个网络发出。另一方面在vpn网络下,也能方便实现vpn代理,配置哪些应用通过vpn网络进行数据代理。
fwmark是用于实现策略路由的,mark的意思是给数据流量打标记,这样路由规则就可以根据mark值做一些更细粒度的路由策略。 fwmark分两部分,包括匹配值/掩码。 路由策略只关心掩码位为1的位。 Android 使用的fwmark为21位,查阅代码netd/include/Fwmark.h,定义如下:
union Fwmark {
uint32_t intValue;
struct {
unsigned netId : 16; //低16位,网络ID。
bool explicitlySelected : 1; //是否显示指定使用该网络
bool protectedFromVpn : 1; //是否受 vpn 保护
Permission permission : 2; //该数据包所属的应用权限
bool uidBillingDone : 1;
};
constexpr Fwmark() : intValue(0) {}
static inline uint32_t getUidBillingMask() {
Fwmark m;
m.uidBillingDone = true;
return m.intValue;
}
};
static const unsigned FWMARK_NET_ID_MASK = 0xffff; //net id
};
权限的定义如下:
enum Permission {
PERMISSION_NONE = 0x0,
PERMISSION_NETWORK = 0x1,
PERMISSION_SYSTEM = 0x3,
}
根据上面fwmark的定义,我们分析下面这条路由规则
10000: from all fwmark 0xc0000/0xd0000 lookup leagcy_system
0xc0000 = 1100 0000 0000 0000 0000,
表示拥有系统权限没有明确指定网络的数据包要查 leagcy_system 路由表。
一个案例
iptables网关服务器三块网卡:eth0(网通ip:10.0.0.1)、eth1(电信ip:20.0.0.1);eth2:网关192.168.10.1(内网用户的网关)。
要求:公司内网要求192.168.10.1—100以内的ip使用 10.0.0.1 网关上网(网通),其他IP使用 20.0.0.1 (电信)上网;
iptables网关服务器配置如下:
ip route add default gw 20.0.0.1
ip route add table 10 via 10.0.0.1 dev eth0
#eth0 是10.0.0.1所在的网卡,10是路由表的编号
ip rule add fwmark 10 table 10
#fwmark 10 是标记,table 10 是路由表10。 标记了 10 的数据使用table10 路由表
iptables -A PREROUTING -t mangle -i eth2 -s 192.168.10.1 - 192.168.10.100 -j MARK --set-mark 10
因为mangle的处理是优先于nat 和fiter表的,所以相应数据包到达之后先打上标记,之后再通过ip rule规则。对应的数据包使用相应的路由表进行路由,最后读取路由表信息,将数据包送出网关。
参考
Linux prerouting和postrouting的区别
Android&Linux策略路由
Linux系列—策略路由、ip rule、ip route
IPROUTE2 Utility Suite Howto
Linux ip 命令详解
IP命令详解
路由知识之ip route 命令中的疑惑