使用CONNMARK解决Linux上源进源出问题
FWMARK是啥?
FWMARK是Linux在处理网络数据包的时候给数据包加上的一个标识,我们可以根据fwmark配置不同的转发规则。
作用域
FWMARK
作用于单个数据包。
比如我们可以采用ip rule add fwmark 0x1234 lookup 456
这样,让被mark为0x1234
的数据包查询路由表456
出去。
由于FWMARK
是针对单个PACKET而言的,因此在下文中我们所指的Packet Mark
与这里的FWMARK
相同。
如何给出去/进来的数据包设置FWMARK?
-
如果要对服务器外出连接设置fwmark,可以使用
socket
,可以通过setsockopt
来设置数据包的MARK
,详见这里。 -
如果要对于某个Wireguard网卡的流入数据包设置fwmark,可以使用
wg set $WG_IFACE fwmark $MARK
来设置进来的数据包的fwmark
。 -
也可以直接通过
iptables
设置-j MARK --set-mark $MARK
-
在路由器对于单条连接或者入站连接设置
CONNMARK
,然后restore
为fwmark
,将在下文介绍。
CONNMARK又是啥?
CONNMARK是用于标识一条有状态连接的MARK。
不同于之前所述针对单个数据包的FWMARK
,CONNMARK
标识的是一条连接,比如对于TCP和UDP就是一条四元组。
如何设置CONNMARK?
我们可以使用iptables
的mangle
表,对流入进来的单条connection
设置mark。
例如:
ip6tables -t mangle -A PREROUTING -i ens192 -j CONNMARK --set-mark 4134
通过这条规则,我们就对从ens192网卡流入进来的连接设置了一个CONNMARK
为4134,但这里的CONNMARK
针对的是连接而不是数据包,因此这里并不能根据我们设置ip rule
转发。
如何让CONNMARK生效为PACKET MARK?
iptables
的mangle
表,可以使用-m connmark
来匹配已经设置的connmark
规则,然后使用-j CONNMARK --restore-mark
来将已有的connmark
变为数据包的fwmark
例如:
ip6tables -t mangle -A OUTPUT -m connmark --mark 4134 -j CONNMARK --restore-mark
通过这条命令,我们就把从OUTPUT
链中出去的,之前被打上CONNMARK 4134
的连接的数据包打上了4134
的FWMARK
。
注:OUTPUT
为本机发送的数据包,如果作为路由器转发其它网卡进来的数据包,可改为PREROUTING
链。
这些东西有啥用?
实战场景:一台服务器,两个不同的网络,源进源出。
家里服务器IPv6有两个连接,分别是自己的BGP网络与电信的宽带,现在他想要让这两个网络进来的流量访问他自己的服务器都有较好的体验,也就是说从各自网卡进来的入站连接都从各自的网卡出去。
假设这两个连接的网卡分别是ens192
与ens224
,且网关均为fe80::1
,那么我们可以加入以下规则与路由表:
# 添加路由表(如果存在interface可能会down的情况,请采用其他方式解决,比如ifupdown可以在配置文件中写post-up)
ip -6 route add default via fe80::1 mtu 1492 dev ens192 table 4134 # 因为PPPoE仅有1492,RA会下发,但是静态路由直接设置的话默认还是1500
ip -6 route add default via fe80::1 dev ens224 table 208618
# 还应该在对应表中添加对应网卡的Link-Local路由,否则局域网无法访问。
ip -6 route add 240e:37a:20b6:de00::/64 dev ens192 table 4134
ip -6 route add 2a0e:aa06:470:8000::/64 dev ens224 table 208618
# 如果作为路由器使用且有下级路由也应该相应地添加
#ip -6 route add 2a0e:aa06:470:8001::/64 via fe80::2 dev ens224 table 208618
# 设置连接电信网卡入站的FWMARK
ip6tables -t mangle -A PREROUTING -i ens192 -j CONNMARK --set-mark 4134
ip6tables -t mangle -A OUTPUT -m connmark --mark 4134 -j CONNMARK --restore-mark
#ip6tables -t mangle -A PREROUTING -m connmark --mark 4134 -j CONNMARK --restore-mark # 可选,不作为路由器可不加
# 设置连接自己BGP网络网卡入站的FWMARK
ip6tables -t mangle -A PREROUTING -i ens224 -j CONNMARK --set-mark 208618
ip6tables -t mangle -A OUTPUT -m connmark --mark 208618 -j CONNMARK --restore-mark
#ip6tables -t mangle -A PREROUTING -m connmark --mark 208618 -j CONNMARK --restore-mark # 可选,不作为路由器可不加
# 分别添加ip rule,转发给不同的路由表
ip -6 rule add fwmark 4134 lookup 4134
ip -6 rule add fwmark 208618 lookup 208618
改造前后ping测试
从校园网(CERNET2)到家里服务器的两个地址(注意到240e地址的TTL和延迟的变化)
# 改造前,尽管连接的是电信的IP,但是回复数据包依然从自己BGP网络Spoof流出
$ ping 2a0e:aa06:470:8000::1 -c 3
PING 2a0e:aa06:470:8000::1(2a0e:aa06:470:8000::1) 56 data bytes
64 bytes from 2a0e:aa06:470:8000::1: icmp_seq=1 ttl=51 time=128 ms
64 bytes from 2a0e:aa06:470:8000::1: icmp_seq=2 ttl=51 time=128 ms
64 bytes from 2a0e:aa06:470:8000::1: icmp_seq=3 ttl=51 time=127 ms
--- 2a0e:aa06:470:8000::1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2004ms
rtt min/avg/max/mdev = 126.607/127.238/127.580/0.446 ms
$ ping 240e:37a:20b6:de00:20c:29ff:fe92:a83c -c 3
PING 240e:37a:20b6:de00:20c:29ff:fe92:a83c(240e:37a:20b6:de00:20c:29ff:fe92:a83c) 56 data bytes
64 bytes from 240e:37a:20b6:de00:20c:29ff:fe92:a83c: icmp_seq=1 ttl=51 time=106 ms
64 bytes from 240e:37a:20b6:de00:20c:29ff:fe92:a83c: icmp_seq=2 ttl=51 time=106 ms
64 bytes from 240e:37a:20b6:de00:20c:29ff:fe92:a83c: icmp_seq=3 ttl=51 time=105 ms
--- 240e:37a:20b6:de00:20c:29ff:fe92:a83c ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2001ms
rtt min/avg/max/mdev = 105.430/105.622/105.776/0.143 ms
# 改造后
$ ping 2a0e:aa06:470:8000::1 -c 3
PING 2a0e:aa06:470:8000::1(2a0e:aa06:470:8000::1) 56 data bytes
64 bytes from 2a0e:aa06:470:8000::1: icmp_seq=1 ttl=51 time=128 ms
64 bytes from 2a0e:aa06:470:8000::1: icmp_seq=2 ttl=51 time=127 ms
64 bytes from 2a0e:aa06:470:8000::1: icmp_seq=3 ttl=51 time=127 ms
--- 2a0e:aa06:470:8000::1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 126.808/127.288/127.746/0.383 ms
$ ping 240e:37a:20b6:de00:20c:29ff:fe92:a83c -c 3
PING 240e:37a:20b6:de00:20c:29ff:fe92:a83c(240e:37a:20b6:de00:20c:29ff:fe92:a83c) 56 data bytes
64 bytes from 240e:37a:20b6:de00:20c:29ff:fe92:a83c: icmp_seq=1 ttl=50 time=61.1 ms
64 bytes from 240e:37a:20b6:de00:20c:29ff:fe92:a83c: icmp_seq=2 ttl=50 time=61.4 ms
64 bytes from 240e:37a:20b6:de00:20c:29ff:fe92:a83c: icmp_seq=3 ttl=50 time=61.2 ms
--- 240e:37a:20b6:de00:20c:29ff:fe92:a83c ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 61.090/61.240/61.407/0.129 ms
参考资料
https://book.huihoo.com/iptables-tutorial/x9125.htm