1、每个数据流在linux 内核中由一个struct sk_buff结构来表示,这个sk_buff结构有一个32位无符号整型的mark成员,用来保存该skb的mark标记值,在netfilter框架中可以动态修改该mark值。
2、每条在linux内核中成功转发的数据流都会在netfilter中的conntrack模块中保存一条状态跟踪连接记录,这是个struct nf_conn结构,该结构中有一个32位无符号整型的mark成员,用来保存该记录的标记值。
注:未被成功转发的数据流(如被防火墙拦截了),不会在系统中记录其conntrack信息。
默认情况下,在一条数据流被linux成功转发时,struct sk_buff中的mark值不会保存到struct nf_conn结构中的mark成员中,struct nf_conn结构中的mark值始终为0。
使用iptables ... -j CONNMARK --bit-save-mark --nfmask 0xffff0000 --ctmask 0xffffffff 可以将当前匹配的数据流skb的mark值保存在conntrack的mark值中。
使用iptables ...-j CONNMARK --restore-mark --nfmask 0xffffffff --ctmask 0xffff 可以对当前匹配的数据流进行mark值的恢复操作,将该数据流在conntrack模块中对应的记录struct nf_conn中的mark值提取出来保存在当前数据流sk_buff的mark值中。
注:支持mark值的位存储和恢复。
应用:
1、在系统中存在大量的iptables/ebtables规则时,一条数据流被转发需要消耗大量的CPU时间,会严重影响设备的throughput。
方案:使用CONNMARK 的save-mark/retore-mark机制可以用来保存skb的最终状态,使得netfilter可以提前预判该数据包的最终状态(ACCEPT/DROP和其MARK值),实现对该数据流快速转发,且最终状态完成符合整个netfilter框架的完整处理结果。
详细设计:
a. 在Mangle 的PREROUTING最开始,对特定的数据流进行匹配执行CONNMARK --restore-mark的操作,然后根据其skb的mark值中的特定Bit来判断其状态是被ACCEPT还是DROP,如果是ACCEPT则提供ACCEPT该skb,然后在每个tables的主chain中执行判断是否提前ACCEPT的操作。
b. 在Mangle的POSTROUTING中,对满足条件的数据流打上ACCEPTed的标记(skb->mark),然后执行CONNMARK save-mark的操作将skb的mark值保存到conntrack对应的记录中。
c. 当netfilter的规则发生任何变化时,需要调用conntrack -U -m 0 shell命令,将当前conntrack模块中的所有状态跟踪记录的mark值设置为0。保证所有数据包的状态不受旧的规则的影响,在新的netfilter规则框架下,conntrack记录中的每个数据流都能够得到正确的逻辑处理和状态结果。
注意:使用该方案后,系统中转发的数据流的计数就不能通过netfilter规则的执行次数记录来确定了。