免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
1234下一页
最近访问板块 发新帖
查看: 4120 | 回复: 30

[网络子系统] ip_frag_reasm函数中的一个问题【已解决】 [复制链接]

论坛徽章:
1
IT运维版块每日发帖之星
日期:2015-11-17 06:20:00
发表于 2016-10-16 20:52 |显示全部楼层
本帖最后由 jiufei19 于 2016-10-21 15:28 编辑

内核版本V2.6.23

在ip_frag_reasm对报文分段进行重组时,有如下代码:

static struct sk_buff *ip_frag_reasm(struct ipq *qp, struct net_device *dev)
{
     ...
    if (skb_shinfo(head)->frag_list) {
        ...
     }
     ...
}

在重组IP报文时,红色代码检查当前head的frag_list链表不为空,请问为啥需要进行此判断,被重组的报文只来自2个方向,一个是本地外出报文再经loopback环回,一个是网络转发报文,对于前者,在L4层可以进行辅助分割数据的操作,在经过ip_push_pending_frames函数处理后,会在首个分段的frag_list链表上链接所有后续分段,但是接着在IP层的ip_fragment函数中会逐个将各个分段添加IP首部后发出,此时frag_list链表将不复存在了;对于网络转发来的报文,更不会出现frag_list链表不为空的现象。


在内核版本3以上的其他内核版本中,虽然ip_frag_reasm函数的写法有部分改变,但是仍然有对frag_list不为空的检查!

那么,为啥内核要进行此判断?什么情况下在重组时会出现skb_shinfo(head)->frag_list不为空的场景?

论坛徽章:
36
IT运维版块每日发帖之星
日期:2016-04-10 06:20:00IT运维版块每日发帖之星
日期:2016-04-16 06:20:0015-16赛季CBA联赛之广东
日期:2016-04-16 19:59:32IT运维版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-19 06:20:00每日论坛发贴之星
日期:2016-04-19 06:20:00IT运维版块每日发帖之星
日期:2016-04-25 06:20:00IT运维版块每日发帖之星
日期:2016-05-06 06:20:00IT运维版块每日发帖之星
日期:2016-05-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-13 06:20:00IT运维版块每日发帖之星
日期:2016-05-28 06:20:00每日论坛发贴之星
日期:2016-05-28 06:20:00
发表于 2016-10-17 22:23 |显示全部楼层
回复 1# jiufei19
对于网络转发来的报文,更不会出现frag_list链表不为空的现象。
为什么不可能呢?

论坛徽章:
1
IT运维版块每日发帖之星
日期:2015-11-17 06:20:00
发表于 2016-10-18 08:58 |显示全部楼层
Godbach 发表于 2016-10-17 22:23
回复 1# jiufei19
为什么不可能呢?

感谢版主答复。当前正在执行ip_frag_reasm,也就是说本地主机的IP实体刚刚把所有分段全部收到,而这些分段都是来自网络发来的,难道网络上发来的报文对应的skb的frag_list链表上会有数据?想不通呢,请斑竹进一步明示

论坛徽章:
36
IT运维版块每日发帖之星
日期:2016-04-10 06:20:00IT运维版块每日发帖之星
日期:2016-04-16 06:20:0015-16赛季CBA联赛之广东
日期:2016-04-16 19:59:32IT运维版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-19 06:20:00每日论坛发贴之星
日期:2016-04-19 06:20:00IT运维版块每日发帖之星
日期:2016-04-25 06:20:00IT运维版块每日发帖之星
日期:2016-05-06 06:20:00IT运维版块每日发帖之星
日期:2016-05-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-13 06:20:00IT运维版块每日发帖之星
日期:2016-05-28 06:20:00每日论坛发贴之星
日期:2016-05-28 06:20:00
发表于 2016-10-18 13:34 |显示全部楼层
回复 3# jiufei19

2.6.32 上有个注释,你看一下。
507     /* If the first fragment is fragmented itself, we split
508      * it to two chunks: the first with data and paged part
509      * and the second, holding only fragments. */

510     if (skb_has_frags(head)) {
511         struct sk_buff *clone;
512         int i, plen = 0;
513
514         if ((clone = alloc_skb(0, GFP_ATOMIC)) == NULL)
515             goto out_nomem;



论坛徽章:
1
IT运维版块每日发帖之星
日期:2015-11-17 06:20:00
发表于 2016-10-18 17:36 |显示全部楼层
本帖最后由 jiufei19 于 2016-10-18 17:38 编辑

回复 4# Godbach

这个注释我看了,它只是说如果这个skb是一个带有frag_list的,则如何如何,关键是为啥会带有frag_list,从网络上到来的报文从ip_rcv开始,我基本捋了一遍,没有看到有对frag_list的操作呀!难道是我哪里理解错误了?

论坛徽章:
36
IT运维版块每日发帖之星
日期:2016-04-10 06:20:00IT运维版块每日发帖之星
日期:2016-04-16 06:20:0015-16赛季CBA联赛之广东
日期:2016-04-16 19:59:32IT运维版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-19 06:20:00每日论坛发贴之星
日期:2016-04-19 06:20:00IT运维版块每日发帖之星
日期:2016-04-25 06:20:00IT运维版块每日发帖之星
日期:2016-05-06 06:20:00IT运维版块每日发帖之星
日期:2016-05-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-13 06:20:00IT运维版块每日发帖之星
日期:2016-05-28 06:20:00每日论坛发贴之星
日期:2016-05-28 06:20:00
发表于 2016-10-18 19:06 |显示全部楼层
回复 5# jiufei19

注释的意思,感觉应该是skb 上第一个分片又被分片的情形。具体代码就需要找一下

论坛徽章:
1
IT运维版块每日发帖之星
日期:2015-11-17 06:20:00
发表于 2016-10-18 20:44 |显示全部楼层
本帖最后由 jiufei19 于 2016-10-19 11:22 编辑
Godbach 发表于 2016-10-18 19:06
回复 5# jiufei19

注释的意思,感觉应该是skb 上第一个分片又被分片的情形。具体代码就需要找一下

感谢斑竹答复。

当前是在ip_frag_reasm函数中,也即收到了一个原始IP报文的最后一个未到分段,于是启动重组过程,由于分段是通过ip_frag_queue添加到qp->fragments链表上,所以此时是不可能涉及frag_list链表的,除非是收到首个分段自然就在其frag_list上有数据,或者收到该首个分段后,在进行重组前由于某个操作而导致其frag_list上产生数据,到底是什么场景太难以理解了。作为对比,在ip_frag_queue中有如下判断:

!(IPCB(skb)->flags & IPSKB_FRAG_COMPLETE)

这个IPSKB_FRAG_COMPLETE标记是本地分段操作时打上的标记,但是当通过环回设备的发送后,在环回设备接收过程中并不会丢失,因为环回设备是直接调用netif_rx将数据投入协议栈,所以这里环回设备就可以解释为啥进行这个关于 IPSKB_FRAG_COMPLETE的条件判断。

而对于ip_frag_reasm,我实在想不出什么场景会产生frag_list不为空,之前曾以为也是环回设备的发送产生的,但是仔细分析了下,不可能。这里希望其他同仁能对此问题解惑,谢谢!

论坛徽章:
1
IT运维版块每日发帖之星
日期:2015-11-17 06:20:00
发表于 2016-10-19 14:09 |显示全部楼层
本帖最后由 jiufei19 于 2016-10-21 16:17 编辑

为了更清楚描述我的问题,我下面用一幅图来说明问题


这里需要纠正下该图,即当frag_list非空后,则就不会再有两个链表在clone结点上了,就只有frag_list自身,因为后面说了这个是环回设备的发送与接收产生的现象
捕获.PNG

论坛徽章:
1
IT运维版块每日发帖之星
日期:2015-11-17 06:20:00
发表于 2016-10-19 23:38 |显示全部楼层
本帖最后由 jiufei19 于 2016-10-20 09:36 编辑

如上图所示,最后重组成功的报文就是由head指针指向的head结点代表,假设该head的frag_list非空,于是将申请一个主缓存区长度为0的skb,即clone,然后将原来head上面的frag_list链表移动到clone的frag_list上,之后再让head结点的frag_list指向clone结点,这样clone上就有两个链表,一个是【clone】->frag_list,另一个是clone->next。

很显然,此时明显造成混乱了,将来比如UDP的skb_copy_datagram_iovec()函数将沿着【clone】->next指针进行读取,那么【clone】->frag_list上的数据不就无法读取了吗,这到底是咋回事呢,烦请大家解惑!

另外,如果head的frag_list为空,则最后重组完毕后,会将原来在head->next上的其他分段移动到head->frag_list上,此时skb_copy_datagram_iovec()函数沿着【head】->frag_list指针进行读取,就不会有任何问题,如下图所示

捕获.PNG

论坛徽章:
1
IT运维版块每日发帖之星
日期:2015-11-17 06:20:00
发表于 2016-10-20 11:23 |显示全部楼层
本帖最后由 jiufei19 于 2016-10-21 14:18 编辑

为了更清楚表明我的问题,下面我列出用户进程读取udp数据报的代码调用过程

udp_recvmsg-->skb_recv_datagram-->skb_copy_datagram_iovec

  247 int skb_copy_datagram_iovec(const struct sk_buff *skb, int offset,
  248                 struct iovec *to, int len)
  249 {
  ...         ...
  254     if (copy > 0) {        
  ...            ...
  257         if (memcpy_toiovec(to, skb->data + offset, copy))
  ...            ...
  262     }
  265     for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
  ...            ...
  290     }

  291
  292     if (skb_shinfo(skb)->frag_list) {
   ...           ...  
// 这里是递归调用skb_copy_datagram_iovec,所以这个问题解决了
  314     }
  ...         ..

上述红色代码即是客户端读取udp数据报的操作,分别从skb的主buffer、frags[]内存页、frag_list这三个地方读取数据,因此如果head本身带有frag_list链表,并且其又在pq->fragments链表上通过next指针与后续分段链接的话,那么重组成功后,就会出现我这里说的混乱现象。

到底是咋回事?========》递归调用解决此问题了,没有混乱!
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP