免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 4102 | 回复: 11
打印 上一主题 下一主题

[网络子系统] IP分片 ip_fragment中选项处理的一个Bug [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2015-07-03 23:00 |只看该作者 |倒序浏览
本帖最后由 wangjianchangdx 于 2015-07-04 00:38 编辑

ip_fragment 进行分片时分为fast path和slow path。

如果skb已经有frag list, 则直接使用,即为fast path;
slow path则对skb直接进行分片处理。

在fast path中,如下代码遍历frag list,并使用传入的output函数发送frag。
  1. for (;;) {
  2.                         /* Prepare header of the next frame,
  3.                          * before previous one went down. */
  4.                         if (frag) {
  5.                                 frag->ip_summed = CHECKSUM_NONE;
  6.                                 skb_reset_transport_header(frag);
  7.                                 __skb_push(frag, hlen);
  8.                                 skb_reset_network_header(frag);
  9.                                 memcpy(skb_network_header(frag), iph, hlen);
  10.                                 iph = ip_hdr(frag);
  11.                                 iph->tot_len = htons(frag->len);
  12.                                 ip_copy_metadata(frag, skb);
  13.                                 if (offset == 0)
  14.                                         ip_options_fragment(frag);
  15.                                 offset += skb->len - hlen;
  16.                                 iph->frag_off = htons(offset>>3);
  17.                                 if (frag->next)
  18.                                         iph->frag_off |= htons(IP_MF);
  19.                                 /* Ready, complete checksum */
  20.                                 ip_send_check(iph);
  21.                         }

  22.                         err = output(sk, skb);

  23.                         if (!err)
  24.                                 IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGCREATES);
  25.                         if (err || !frag)
  26.                                 break;
复制代码
其中,ip_options_fragment为对第二个分片处理。

  1.                                 if (offset == 0)
  2.                                         ip_options_fragment(frag);
复制代码
而在slow path中,ip_options_fragment为对第一个分片的处理。

  1.                 /* ANK: dirty, but effective trick. Upgrade options only if
  2.                  * the segment to be fragmented was THE FIRST (otherwise,
  3.                  * options are already fixed) and make it ONCE
  4.                  * on the initial skb, so that all the following fragments
  5.                  * will inherit fixed options.
  6.                  */
  7.                 if (offset == 0)
  8.                         ip_options_fragment(skb);
复制代码
从代码上看,fast path中的处理也有矛盾之处:
既然已经从前一个分片中拷贝了iphdr,为何还要再进行选项处理?
全部IP分片的iphdr除了frag flag & offset之外,不应该是一样的吗?

实测中,在桥中,mtu为1500时,Port 1 ping Port 2: ping 192.168.1.2 -l 1516字节的数据包会出错。
1516字节的ICMP Echo Reply中,第二个分片frag->len为64:64/4=16=0x10, 0x10&7=0,走fast path。

  1. skb_walk_frags(skb, frag) {
  2.                         /* Correct geometry. */
  3.                         if (frag->len > mtu ||
  4.                             ((frag->len & 7) && frag->next) ||
  5.                             skb_headroom(frag) < hlen)
  6.                                 goto slow_path_clean;

  7.                         /* Partially cloned skb? */
  8.                         if (skb_shared(frag))
  9.                                 goto slow_path_clean;

  10.                         BUG_ON(frag->sk);
  11.                         if (skb->sk) {
  12.                                 frag->sk = skb->sk;
  13.                                 frag->destructor = sock_wfree;
  14.                         }
  15.                         skb->truesize -= frag->truesize;
  16.                 }
复制代码
第二个分片的内容会被ip_options_fragment中的memset(optptr, IPOPT_NOOP, optlen);置为1,即IPOPT_NOOP.

论坛徽章:
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
2 [报告]
发表于 2015-07-04 22:02 |只看该作者
本帖最后由 Godbach 于 2015-07-04 22:03 编辑

回复 1# wangjianchangdx

后续分片报文时不再需要 IP Option 的吧


   

论坛徽章:
0
3 [报告]
发表于 2015-07-07 00:05 |只看该作者
回复 2# Godbach

rfc 791:

  1. The option-type octet is viewed as having 3 fields:

  2.       1 bit   copied flag,
  3.       2 bits  option class,
  4.       5 bits  option number.

  5.     The copied flag indicates that this option is copied into all
  6.     fragments on fragmentation.

  7.       0 = not copied
  8.       1 = copied
复制代码
有一些IP头选项要复制,有一些不能复制。这个在选项类型里面有定义。

这里无论是slow path还是fast path,都是对第二个分片开始做处理。
fast path的问题在于:
1. 在已经存在frag_list的情况下,仍然从第一个分片直接复制hlen长度的iph到第二个分片,可能会踩踏二层头:

  1.                                 skb_reset_transport_header(frag);
  2.                                 __skb_push(frag, hlen);
  3.                                 skb_reset_network_header(frag);
  4.                                 memcpy(skb_network_header(frag), iph, hlen);
复制代码
2. 在于第二个分片对应的skb的IPCB(skb)->opt 未必被正确的初始化,造成ip_options_fragment处理出错;


   

论坛徽章:
20
程序设计版块每日发帖之星
日期:2015-08-17 06:20:00程序设计版块每日发帖之星
日期:2016-07-16 06:20:00程序设计版块每日发帖之星
日期:2016-07-18 06:20:00每日论坛发贴之星
日期:2016-07-18 06:20:00黑曼巴
日期:2016-12-26 16:00:3215-16赛季CBA联赛之江苏
日期:2017-06-26 11:05:5615-16赛季CBA联赛之上海
日期:2017-07-21 18:12:5015-16赛季CBA联赛之青岛
日期:2017-09-04 17:32:0515-16赛季CBA联赛之吉林
日期:2018-03-26 10:02:16程序设计版块每日发帖之星
日期:2016-07-15 06:20:0015-16赛季CBA联赛之江苏
日期:2016-07-07 18:37:512015亚冠之萨济拖拉机
日期:2015-08-17 12:21:08
4 [报告]
发表于 2015-07-07 20:22 |只看该作者
请问针对哪个版本的内核?在路由场景下问题是否还存在?(其实没太清楚问题是什么

此外,不知道楼主为什么说“其中,ip_options_fragment为对第二个分片处理”,offset==0的判断不是针对第一个(输出)分片么?(楼主是不是想表达第二个skb或者第一个frag?)

论坛徽章:
20
程序设计版块每日发帖之星
日期:2015-08-17 06:20:00程序设计版块每日发帖之星
日期:2016-07-16 06:20:00程序设计版块每日发帖之星
日期:2016-07-18 06:20:00每日论坛发贴之星
日期:2016-07-18 06:20:00黑曼巴
日期:2016-12-26 16:00:3215-16赛季CBA联赛之江苏
日期:2017-06-26 11:05:5615-16赛季CBA联赛之上海
日期:2017-07-21 18:12:5015-16赛季CBA联赛之青岛
日期:2017-09-04 17:32:0515-16赛季CBA联赛之吉林
日期:2018-03-26 10:02:16程序设计版块每日发帖之星
日期:2016-07-15 06:20:0015-16赛季CBA联赛之江苏
日期:2016-07-07 18:37:512015亚冠之萨济拖拉机
日期:2015-08-17 12:21:08
5 [报告]
发表于 2015-07-07 20:34 |只看该作者
#2,ip_rcv里有关于IPCB的显式memset, 楼主指的“未初始化”是指哪个代码路径?

#1,在ip_fragment的上下文,二层头还没被赋值吧?路由环境下,是在output里完成ARP逻辑的。

论坛徽章:
0
6 [报告]
发表于 2015-07-07 23:25 |只看该作者
回复 4# nswcfd

实测在2.6.30,有检查最新版本,这一部分代码没有变化。

ip_options_fragment的处理,是针对第二个及后续分片,因为这些分片里不能拷贝部分IP头选项。
   

论坛徽章:
0
7 [报告]
发表于 2015-07-07 23:37 |只看该作者
本帖最后由 wangjianchangdx 于 2015-07-07 23:39 编辑

回复 5# nswcfd

感谢。

问题2,bridge转发路径中,没有处理。

netif_receive_skb -> br_handle_frame -> br_handle_frame_finish
  -> br_forward -> __br_forward -> br_forward_finish
  -> NF_BR_POST_ROUTING:br_nf_post_routing -> br_nf_dev_queue_xmit -> ip_fragment -> br_dev_queue_push_xmit


今天也看到了注意到问题1,

  1.                         err = output(skb);
复制代码
在ip_finish_output2中有填充,在br_dev_queue_push_xmit中没有:

  1.                         skb_push(skb, ETH_HLEN);

  2.                         dev_queue_xmit(skb);
复制代码

论坛徽章:
20
程序设计版块每日发帖之星
日期:2015-08-17 06:20:00程序设计版块每日发帖之星
日期:2016-07-16 06:20:00程序设计版块每日发帖之星
日期:2016-07-18 06:20:00每日论坛发贴之星
日期:2016-07-18 06:20:00黑曼巴
日期:2016-12-26 16:00:3215-16赛季CBA联赛之江苏
日期:2017-06-26 11:05:5615-16赛季CBA联赛之上海
日期:2017-07-21 18:12:5015-16赛季CBA联赛之青岛
日期:2017-09-04 17:32:0515-16赛季CBA联赛之吉林
日期:2018-03-26 10:02:16程序设计版块每日发帖之星
日期:2016-07-15 06:20:0015-16赛季CBA联赛之江苏
日期:2016-07-07 18:37:512015亚冠之萨济拖拉机
日期:2015-08-17 12:21:08
8 [报告]
发表于 2015-07-08 16:38 |只看该作者
仔细看了代码,才明白了楼主的问题

1)slowpath下,所有分片都是新分配的skb,只是从原始的skb(可以理解为模版)里复制一些信息,原始的skb从未发送(最后直接free),发送的都是new skb。
在这个过程中,仅在循环的第一次(offset==0)对原始skb进行选项处理,把不能copy的选项替换为NOP,替换后的内容供后续分配复制。
注意:由于复制代码在offset==0之前,第一个分片使用替换前的选项。

2)fastpath下,由于有frag_list,不需要分配新的skb,按照skb->frag#0->frag#1...的顺序进行发送。
fastpath的循环模式有点特殊,是准备下一个分片,发送当前分片(相对的,slowpath是准备当前分片,发送当前分片),每个分片的头部是从前一个分片得到的(大家依次当模版,不像slowpath那样是固定模版),
所以offset==0的条件对应的是第二个分片(即frag#0),在发送第一个分片前,把第二个分片的部分选项NOP,而这个更新后的选项,被第三个分片继承,依次传递。

所以,综上,不论是fastpath还是slowpath,行为都是一致的,都是第二个以后的分片选项被修改。
【没有实际验证,如果错误,请指正】

论坛徽章:
20
程序设计版块每日发帖之星
日期:2015-08-17 06:20:00程序设计版块每日发帖之星
日期:2016-07-16 06:20:00程序设计版块每日发帖之星
日期:2016-07-18 06:20:00每日论坛发贴之星
日期:2016-07-18 06:20:00黑曼巴
日期:2016-12-26 16:00:3215-16赛季CBA联赛之江苏
日期:2017-06-26 11:05:5615-16赛季CBA联赛之上海
日期:2017-07-21 18:12:5015-16赛季CBA联赛之青岛
日期:2017-09-04 17:32:0515-16赛季CBA联赛之吉林
日期:2018-03-26 10:02:16程序设计版块每日发帖之星
日期:2016-07-15 06:20:0015-16赛季CBA联赛之江苏
日期:2016-07-07 18:37:512015亚冠之萨济拖拉机
日期:2015-08-17 12:21:08
9 [报告]
发表于 2015-07-08 17:17 |只看该作者
此外,ip_options_fragment依赖于IPCB(skb)->opt的正确初始化,比如在ip_forward场景下的ip_rcv_options/ip_options_compile。

不是很确定在bridge_forward的场景下,有没有设置IPCB(skb)->opt的逻辑?
当然,一般skb初始化的时候,整个skb->cb都被清零,~~~如果bridge的逻辑不处理选项~~~,那么IPCB(skb)就跟实际的选项不匹配(全0),通常这也没坏处,因为ip_options_fragment就退化为空操作。

不太确定楼主的ping不通问题是哪一步出了问题。

PS,br_netfilter作为bridge支持netfilter的hack,估计在很多corner case上都有些诡异的行为(当然都是可解释的)……


论坛徽章:
0
10 [报告]
发表于 2015-07-09 00:01 |只看该作者
回复 8# nswcfd

是这样的。赞一个。
   
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP