使用CONNMARK解决Linux上源进源出问题

内容纲要

FWMARK是啥?

FWMARK是Linux在处理网络数据包的时候给数据包加上的一个标识,我们可以根据fwmark配置不同的转发规则。

作用域

FWMARK作用于单个数据包

比如我们可以采用ip rule add fwmark 0x1234 lookup 456这样,让被mark为0x1234的数据包查询路由表456出去。

由于FWMARK是针对单个PACKET而言的,因此在下文中我们所指的Packet Mark与这里的FWMARK相同。

如何给出去/进来的数据包设置FWMARK?

  1. 如果要对服务器外出连接设置fwmark,可以使用socket,可以通过setsockopt来设置数据包的MARK,详见这里

  2. 如果要对于某个Wireguard网卡的流入数据包设置fwmark,可以使用wg set $WG_IFACE fwmark $MARK来设置进来的数据包的fwmark

  3. 也可以直接通过iptables设置-j MARK --set-mark $MARK

  4. 在路由器对于单条连接或者入站连接设置CONNMARK,然后restorefwmark,将在下文介绍。

CONNMARK又是啥?

CONNMARK是用于标识一条有状态连接的MARK。

不同于之前所述针对单个数据包的FWMARKCONNMARK标识的是一条连接,比如对于TCP和UDP就是一条四元组。

如何设置CONNMARK?

我们可以使用iptablesmangle表,对流入进来的单条connection设置mark。

例如:

ip6tables -t mangle -A PREROUTING -i ens192 -j CONNMARK --set-mark 4134

通过这条规则,我们就对从ens192网卡流入进来的连接设置了一个CONNMARK为4134,但这里的CONNMARK针对的是连接而不是数据包,因此这里并不能根据我们设置ip rule转发。

如何让CONNMARK生效为PACKET MARK?

iptablesmangle表,可以使用-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的连接的数据包打上了4134FWMARK

注:OUTPUT为本机发送的数据包,如果作为路由器转发其它网卡进来的数据包,可改为PREROUTING链。

这些东西有啥用?

实战场景:一台服务器,两个不同的网络,源进源出。

家里服务器IPv6有两个连接,分别是自己的BGP网络与电信的宽带,现在他想要让这两个网络进来的流量访问他自己的服务器都有较好的体验,也就是说从各自网卡进来的入站连接都从各自的网卡出去。

假设这两个连接的网卡分别是ens192ens224,且网关均为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

特别感谢

Soha

twd2

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注

Back to Top