UDP包分片倒序

原创 2007年09月26日 12:37:00
前段时间在测试一款设备的时候发现,由其发出来的UDP包(大于1500Bytes),会被分片(fragment)。

  这是本在意料之中的,但意外的是发出来的分片包是乱序的--包尾先发出来,包头后发出来。问题的关键在于防火墙拦截这种乱序的UDP包,即通信没法正常开展。

  因为该设备使用的是本来就是通用的LINUX系统内核,怀疑是内核编译的问题。于是在运行REDHEAD的PC机上再进行测试,发现情况依旧。但是在另一台同样是REDHEAD的PC机上测试,却能得到正序的包。原来是否倒序与LINUX的内核版本有关,2.4.x系列版本是倒序,而2.6.x系列是正序的。

  对2.4.x的核进行跟踪,可以发现其执行过程为

  |      sys_write               fs/read_write.c
| sock_writev net/socket.c
| sock_sendmsg net/socket.c
| inet_sendmsg net/ipv4/af_inet.c
| udp_sendmsg net/ipv4/udp.c
| ip_build_xmit net/ipv4/ip_output.c
| ip_build_xmit_slow net/ipv4/ip_output.c
| output_maybe_reroute net/ipv4/ip_output.c
| ip_output net/ipv4/ip_output.c
| ip_finish_output net/ipv4/ip_output.c
| dev_queue_xmit net/dev.c
| --------------------------------------------
| el3_start_xmit driver/net/3c309.c
V

  事实上在进入output_maybe_reroute函数处理的时候,已经是倒序的了。

通过查看ip_build_xmit_slow函数的注释,有这样的一段话:









  因此倒序是必然的。这个函数的功能是把大包(超过1500Bytes)分拆成小包,并倒序传入output_maybe_reroute,最终实现IP包发出。

为什么要倒序呢,这主要是出于效率的考虑。因为通过要在IP包头带上整个包的checksum,当包被分片时,发送接口必须等到读取了整个包的内容,才 能计算出最终的checksum。如果分片包按正序发送,则发送接口必须先把所有分片包缓存起来,在收到整个包的时候计算其checksum,然后逐一发 出。但是如果的倒序发包,那么就可以做到来一分片包只简单的计算该段的checksum,然后马上发出;直到收到最后一个分片时,算出整个包的 checksum,然后把此值放到IP头上发出。显然后者比前者少了缓冲的过程,性能自然要好些。

  但是接下来却又发现一个奇怪的现象,那就是另一款同样是使用2.4.x内核的设备,在发UDP大包时,却不存在倒序现象。

通过对比分析,发现关键在于ip_build_xmit_slow函数中的

err = NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, ,
skb->dst->dev, output_maybe_reroute);

其中的NF_HOOK宏定义为:








  通常情况下,是调用okfn函数(在这里对应的是output_maybe_reroute)进行处理,第一款设备和

第一台测试PC就是这种情况。但是在内核配置为使用NETFILTER功能的时候,就不一样了,实际上这时将会调用nf_hook_slow函数进行处理。简言之,这个函数会把上层发来的包先缓存起来,并按正序发出去。因此第二款测试设备不存在乱序的情况。

既然找到的根本原因,接下来就好办了。最初是想参考2.6.x内核修改的,但是查看代码后发现,这两个版本相差实在太大了,甚至于根本上没有使用ip_build_xmit_slow函数。那就按照NETFILTER方式修改吧,先把包缓存起来,再按顺序发送出去即是。自己操刀改了些代码,测试通过。



ip_build_xmit_slow( sock *sk,
getfrag ( *,
*,
,
),


*frag,
length,
ipcm_cookie *ipc,
rtable *rt,
flags)
{
fraglen, maxfraglen, fragheaderlen;
err;
offset, mf;
mtu;
u16 id;

hh_len = (rt->u.dst.dev->hard_header_len + +)&~;
nfrags=;
ip_options *opt = ipc->opt;
df = ;

sk_buff *skb_buf[];
skb_buf_idx = ;

mtu = rt->u.dst.pmtu;
(ip_dont_fragment(sk, &rt->u.dst))
df = htons(IP_DF);

length -= ( iphdr);

(opt) {
fragheaderlen = ( iphdr) + opt->optlen;
maxfraglen = ((mtu-( iphdr)-opt->optlen) & ~) + fragheaderlen;
} {
fragheaderlen = ( iphdr);






maxfraglen = ((mtu-( iphdr)) & ~) + fragheaderlen;
}

(length + fragheaderlen > ) {
ip_local_error(sk, , rt->rt_dst, sk->dport, mtu);
-;
}





offset = length - (length % (maxfraglen - fragheaderlen));





fraglen = length - offset + fragheaderlen;

(length-offset==) {
fraglen = maxfraglen;
offset -= maxfraglen-fragheaderlen;
}





mf = ;





(offset > && sk->protinfo.af_inet.pmtudisc==IP_PMTUDISC_DO) {
ip_local_error(sk, , rt->rt_dst, sk->dport, mtu);
-;
}
(flags&MSG_PROBE)
out;





id = sk->protinfo.af_inet.id++;

{
*data;
sk_buff * skb;




(!(flags & MSG_DONTWAIT) nfrags == ) {
skb = sock_alloc_send_skb(sk, fraglen + hh_len + ,
(flags & MSG_DONTWAIT), &err);
} {



skb = sock_wmalloc(sk, fraglen + hh_len + , ,
sk->allocation);
(!skb)
err = -ENOBUFS;
}
(skb == )
error;





skb->priority = sk->priority;
skb->dst = dst_clone(&rt->u.dst);
skb_reserve(skb, hh_len);





data = skb_put(skb, fraglen);
skb->nh.iph = ( iphdr *)data;





{
iphdr *iph = ( iphdr *)data;

iph->version = ;
iph->ihl = ;
(opt) {
iph->ihl += opt->optlen>>;
ip_options_build(skb, opt,
ipc->addr, rt, offset);
}
iph->tos = sk->protinfo.af_inet.tos;
iph->tot_len = htons(fraglen - fragheaderlen + iph->ihl*);
iph->frag_off = htons(offset>>)mfdf;
iph->id = id;
(!mf) {
(offset !df) {




__ip_select_ident(iph, &rt->u.dst);
id = iph->id;
}




mf = htons(IP_MF);
}
(rt->rt_type == RTN_MULTICAST)
iph->ttl = sk->protinfo.af_inet.mc_ttl;

iph->ttl = sk->protinfo.af_inet.ttl;
iph->protocol = sk->protocol;
iph->check = ;
iph->saddr = rt->rt_src;
iph->daddr = rt->rt_dst;
iph->check = ip_fast_csum(( *)iph, iph->ihl);
data += iph->ihl*;
}





(getfrag(frag, data, offset, fraglen-fragheaderlen)) {
err = -;
kfree_skb(skb);
error;
}

offset -= (maxfraglen-fragheaderlen);
fraglen = maxfraglen;

nfrags++;






skb_buf[skb_buf_idx] = skb;
skb_buf_idx++;
} (offset >= );





(--skb_buf_idx >= ) {
err = NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb_buf[skb_buf_idx], ,
skb_buf[skb_buf_idx]->dst->dev, output_maybe_reroute);
(err) {
(err > )
err = sk->protinfo.af_inet.recverr ? net_xmit_errno(err) : ;
(err)
error;
}
}

(nfrags>)
ip_statistics[smp_processor_id()* + !in_softirq()].IpFragCreates += nfrags;
:
;

:


(--skb_buf_idx >=)
kfree_skb(skb_buf[skb_buf_idx]);

IP_INC_STATS(IpOutDiscards);
(nfrags>)
ip_statistics[smp_processor_id()* + !in_softirq()].IpFragCreates += nfrags;
err;
}
3.1415926535897932384626433832

   PS:在写BLOG以后,我一直遵循这样一个习惯:原创的新帖,结尾处标上圆周率,每次增加一位。这是我能记住的最后一位圆周率了,算是一个阶段的结束 吧,以后发文就要先查查圆周率了。作为纪念吧,希望写一篇还算有点技术含量的文章,因此一直拖着,迟迟没动笔。这个文章来源于近段时间处理的一个工程问 题,现在总算写完了--可惜排版难看了点。

 

Qt通过UDP传图片,实现自定义分包和组包

一.包头结构体 //包头 struct PackageHeader { //包头大小(sizeof(PackageHeader)) unsigned int uTransPack...
  • caoshangpa
  • caoshangpa
  • 2016年09月27日 16:35
  • 3123

MTU 巨帧 TCP/UDP与分片

常常见到交换机和网卡说明中提到支持Jumbo Frame,但我一直对以太网的Jumbo Frame(巨帧)如何使用不太理解,今日在网上找到2则现摘录下来,相信看了以后大家会有收获。 ---- 这是一种...
  • forsakening
  • forsakening
  • 2016年02月16日 10:30
  • 4923

IP、TCP、UDP数据包头部抓包及IP分片传输详解

英文头部说明: 32bit          8                    8                       8                  8 ...
  • BUG1314
  • BUG1314
  • 2015年01月09日 16:50
  • 2467

iOS TUN之避免UDP包ip分片

iOS的NetworkExtension给应用暴露了一个虚拟网卡TUN设备,可以设置其MTU值。如果上层应用发送的IP包大于这个MTU就会被分片。(详见:http://www.jianshu.com/...
  • n5
  • n5
  • 2017年06月09日 20:01
  • 836

netfilter实现内核重构skb来发送udp包

#include #include #include #include #include #include #include #include #include #include ...
  • dean_gdp
  • dean_gdp
  • 2014年07月25日 10:03
  • 871

tcp和udp包穿透防火墙-Httptunnel

什么是局域网安全,系统管理员怎样才能保障局域网的安全?这是一个不断变化的安全概念,很长的一个时期以来,在局域网与外界互联处放置一个防火墙,严格控制开放的端口,就能在很大程度上掌握安全的主动权,方便的控...
  • educast
  • educast
  • 2014年09月22日 09:09
  • 3983

android 发送UDP包和java应用程序来接听

转自:http://blog.sina.com.cn/s/blog_6c0a8fbb0100s3k5.html 我参考的博客中忘记说明需要在Manifest中加网络权限() android端代码:...
  • xiaoxiaobian3310903
  • xiaoxiaobian3310903
  • 2012年08月16日 11:04
  • 3211

tcp和udp包分析

  • 2015年06月26日 23:21
  • 676KB
  • 下载

88E1111,千兆以太网UDP包发送,Verilog程序

  • 2016年10月22日 23:14
  • 10KB
  • 下载

C++基于WinPcap抓UDP包并解析内容

  • 2017年11月14日 17:12
  • 57.5MB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:UDP包分片倒序
举报原因:
原因补充:

(最多只允许输入30个字)