本文主要是介绍linux [报文碎片重组功能] defrag分析/调试流程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
目录
1.碎片整理测试
2.iptables -f 不生效原因
3.报文碎片 丢弃的功能调试过程
测试1.kernel 协议栈分析
测试2:尝试在netfilter中匹配分片报文
代码分析1:
代码分析2
4.小结
1.碎片整理测试
【需求】
路由器过滤greptap 接口上的碎片报文,添加规则·iptabled -A INPUT -f -j DROP·
测试环境
win10: 192.168.0.2
linux: 192.168.0.5
测试1:无分片的报文
win10# ping 192.168.0.5 -->可以ping通
测试2:带分片的报文
win10# ping 192.168.0.5 -l 3000 -->可以ping通
测试3:linux过滤碎片的报文
linux# iptables -A INPUT -f -j DROP #丢弃碎片报文
win10# ping 192.168.0.5 -l 3000 -->依旧可以ping通
linux# iptables -nvL
Chain INPUT (policy ACCEPT 6729 packets, 8666K bytes)
num pkts bytes target prot opt in out source destination
1 0 0 DROP all -f * * 0.0.0.0/0 0.0.0.0/0
测试结论:
可以发现,Chain INPUT 链没有匹配到任何一个报文。
即iptables -f 的规则无效。
iptabled -A INPUT -f -j DROP
2.iptables -f 不生效原因
原文:
Why command "iptables -f" no use ?
Dropping IP fragments is obsolete advice. Linux kernel can and will automatically re-assemble and sanity-check all fragments as needed anyway. This happens before packets are handled by iptables connection tracking, so it is likely this rule may never match anything.
3.报文碎片 丢弃的功能调试过程
功能作用:开启时,可以防止一些tcp 碎片攻击。
测试1.内核协议栈分析
kernel/net/ip_input.c
int ip_rcv()
{
...
//打印 icmp 碎片报文
if((iph->protocol == 1) && ip_is_fragment(iph)){
printk("xxxx find icmp frag len=%d id=%x \n",ntohs(iph->tot_len),ntohs(iph->id),
ntohs(iph->saddr),ntohs(iph->daddr));
}
}
Log 打印:
xxxx find icmp frag len=1500 id=6f4e
xxxx find icmp frag len=1500 id=654e
xxxx find icmp frag len=68 id=654e
→ 即ping -l 3000 被分成3个报文,符合预期
测试结论1:
在协议栈 ip_rcv()函数中,匹配到3个分片报文。
测试2:尝试在netfilter中匹配分片报文
通过iptables s_ip d_ip 匹配
iptables -t mangle -A PREROUTING -s 192.168.0.2 -d 192.168.0.5 -j LOG --log-prefix "11 mangle"
iptables -t nat -A PREROUTING -s 192.168.0.2 -d 192.168.0.5 -j LOG --log-prefix "22 nat"
Log:
[ 3188.325756] 11 mangleIN=BR_LAN OUT= MAC=d8:6c:a1:2f:37:a7:e8:4e:06:66:93:50:08:00 SRC=192.168.0.2 DST=192.168.0.5 LEN=3028 TOS=0x00 PREC=0x00 TTL=128 ID=22063 PROTO=ICMP TYPE=8 CODE=0 ID=1 SEQ=2847
[ 3188.343275] 22 natIN=BR_LAN OUT= MAC=d8:6c:a1:2f:37:a7:e8:4e:06:66:93:50:08:00 SRC=192.168.0.2 DST=192.168.0.5 LEN=3028 TOS=0x00 PREC=0x00 TTL=128 ID=22063 PROTO=ICMP TYPE=8 CODE=0 ID=1 SEQ=2847
-->此处应该打印三个记录,实际只打印一条记录,即分片报文已经重组
测试结论2:
在PREROUTING链的报文长度为3028,
即分片报文在 到达netfilter 的 PREROUTING链之前,已经进行重组。
Linux 网络协议栈 和 netfilter 处理流程图
代码分析1:
ip_rcv()
{
/* 1. 经过PRE_ROUTING钩子点 */
return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING,
net, NULL, skb, dev, NULL,
ip_rcv_finish);
}/* 2.碎片重组功能 注册在 PRE_ROUTING hook 上*/
static struct nf_hook_ops ipv4_defrag_ops[] = {
{
.hook = ipv4_conntrack_defrag,
.pf = NFPROTO_IPV4,
.hooknum = NF_INET_PRE_ROUTING,
.priority = NF_IP_PRI_CONNTRACK_DEFRAG,//mininum -400
},
ipv4_conntrack_defrag()
-nf_ct_ipv4_gather_frags()
--ip_defrage()
分析结论3:
从代码上,确认ip_rcv() 处理在 PREROUTING链 之前。
代码分析2
/* 3.碎片重组 缓冲区 参数*/
ip_defrage()
static struct ctl_table ip4_frags_ns_ctl_table[] = {
{
.procname = "ipfrag_high_thresh",
.data = &init_net.ipv4.frags.high_thresh,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec_minmax,
.extra1 = &init_net.ipv4.frags.low_thresh
},
{
.procname = "ipfrag_low_thresh",
.data = &init_net.ipv4.frags.low_thresh,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec_minmax,
.extra1 = &zero,
.extra2 = &init_net.ipv4.frags.high_thresh
},
}
low_thresh high_thresh的作用,具体看代码。
尝试清除碎片缓存分区,参数对应的proc 节点
Set init_net.ipv4.frags.high_thresh to 0.
Set init_net.ipv4.frags.low_thresh to 0.
echo 0 > /proc/sys/net/ipv4/ipfrag_low_thresh
echo 0 > /proc/sys/net/ipv4/ipfrag_high_thresh
ping 192.168.0.5 -l 3000
Result: time out . --符合预期,ping失败。
测试总结4:
修改sys 节点,即可实现碎片报文的丢弃功能
/proc/sys/net/ipv4/ipfrag_low_thresh
/proc/sys/net/ipv4/ipfrag_high_thresh
4.小结
对ip协议栈的处理和netfilter之间的流程关系,理解的更深刻。
一开始并没有使用/proc/sys 节点实现,而是在函数之中
1.判断为碎片报文,直接丢弃。-->修改内核代码,无扩展性,丢弃。
2.将defrag的buffer修改为0,准备创建sys节点,将buffer 大小参数传递到用户空间修改,在编写的过程中,参照ipv4 sys节点的实现时,发现这两个节点
-->1.运气。 2.内核通过配置提供功能,机制。通过参数适配不同功能需。可适配,可调节。
这篇关于linux [报文碎片重组功能] defrag分析/调试流程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!