Total Pageviews

Saturday 7 September 2019

用iptables mangle + 策略路由进行分流

在网关上解决一个网卡分流的问题,简单可以描述为tcp数据走eth0,udp数据走eth1,icmp数据走eth2。研究了下,发现可以利用iptables的mangle表和策略路由来进行流量分发。 在了解iptables之前,我觉得应该首先了解下数据包在内核路由子系统前后会经过那些处理以及linux防火墙里面非常核心的netfilter系统。
image
这上面是netfilter系统非常典型的几个钩子处理,本人最初接触netfilter的时候是在学校的一次大作业当中开发的一个包过滤防火墙,职业辗转,最后还是回到来netfilter相关的技术,当然也没有这么底层了,更多的谁在用户层徘徊。
在了解netfilter之后就得开始考虑netfilter和iptables之间的关系了,netfilter可能很多开发人员接触的不多,但是或多或少了解过iptabes,其实很多人说iptables是linux下的防火墙,这个说法我觉得不太合适,iptables是linux防火墙的一部分,简单理解iptables其实只是防火墙的前端,给用户用的,还有就是给内核模块发送规则操作命令,真正实现防火墙功能和维护规则表的其实是内核模块。

iptables的四表五链。

四表:raw,mangle,nat,filter四表
五链:也就是上图当中的PREROUTING,INPUT,FORWARD,OUTPUT,POSTROUTING五个hook点。
链和表的关系是1:n的关系,一个hook点上可以有多个表,此时就有一个表的优先级的问题,四表的优先级分别是
raw>mangle>nat>filter
表和链的关系是一个hook点上可以有多个表,具体对应关系如表1所示:
表1:
table hook
raw PREROUTING,OUTPUT
mangle PREROUTING, INPUT, FORWARD,OUTPUT,POSTROUTING
nat PREROUTING,POSTROUTING,OUTPUT
filter INPUT,FORWARD,OUTPUT
通常来说,filter主要用于包过滤防火墙的实现,nat表主要做DNAT和SNAT的功能,mangle表主要用来做qos,其一个特点是可以修改数据包。raw表主要用来加快数据包穿越防火墙的速度,提高防火墙的性能,目前raw表还没有接触过相关的使用场景。
这篇文章主要使用mangle表。

问题

有了iptables的一些背景之后,开始解决我们遇到的问题。
当前模式:我们在网关上有一个应用加速的程序,负责对tcp,udp,icmp三种协议的数据包进行加速,tcp和udp都用到了conntrack相关的内容,icmp由于不是在用户层实现的,所以需要用netfilter的queue机制让用户态接管icmp
面对的问题:首先一个是conntrack的问题,我们需要跟内核通信获取conntrack记录,对于tcp而言没太大问题,除非有非常非常多的短链接,因为只需要在握手完成之后就不用在查conntrack记录了,对于udp而言,每个数据包都需要与内核通信查找conntrack记录,而关于icmp的问题,用过libnetfilter_queue或者说用过netlink的都知道,这玩意并不是很好用,久不久就会buffer unavailable,当然icmp数据通常也不会造成这一问题。
所以可能会回到一些比较成熟的方案去实现udp和icmp的加速,tcp依旧保持当前状态。比较成熟的加速方案,大部分都会想到虚拟网卡。于是就有了当前的分流模式。
image
这里面通过mangle给数据包设置mark值,然后添加路由表和mark值匹配,最后在将表规则route到对应的网卡
mangle表set-mark

iptables -t mangle -A PREROUTING -p udp -j MARK --set-mark 10
iptables -t mangle -A PREROUTING -p icmp -j MARK --set-mark 11

建表

ip rule add from all fwmark 10 table 10
ip rule add from all fwmark 11 table 11

route
ip route add default dev udp_0 table 10
ip route add default dev icmp_0 table 11
这样就完成了网卡协议分流的操作,后续就是程序处理了,具体情况具体分析。我这边后续处理可以简单描述为
0)解析协议栈,把ip头和udp头解析并剥离
1)读取udp数据
2)通过加速通道发送出去
3)接收加速通道返回回来的消息
4)通过原始套接字构造udp响应返回给客户端,从而替换原来方式。

from https://github.com/ICKelin/article/issues/2

No comments:

Post a Comment