免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 3595 | 回复: 9

logic/big bug in linux ip fragment, again ¥5 [复制链接]

论坛徽章:
0
发表于 2007-08-13 14:43 |显示全部楼层

  1. /*
  2. * linux-2.6.22.1/net/ipv4/ip_fragment.c
  3. *
  4. * Oops, a fragment queue timed out.
  5. * Kill it and send an ICMP reply.
  6. *
  7. * sisi 2007-8-13 02:27pm
  8. */
  9. static void ip_expire(unsigned long arg)
  10. {
  11.         struct ipq *qp = (struct ipq *) arg;

  12.         spin_lock(&qp->lock);

  13.         if (qp->last_in & COMPLETE)
  14.                 goto out;

  15.         ipq_kill(qp);

  16.         IP_INC_STATS_BH(IPSTATS_MIB_REASMTIMEOUT);
  17.         IP_INC_STATS_BH(IPSTATS_MIB_REASMFAILS);

  18.         if ((qp->last_in&FIRST_IN) && qp->fragments != NULL) {
  19.                 struct sk_buff *head = qp->fragments;
  20.                 /*
  21.                  * Send an ICMP "Fragment Reassembly Timeout" message.
  22.                  *
  23.                  * 由这里引发
  24.                  */
  25.                 if ((head->dev = dev_get_by_index(qp->iif)) != NULL) {
  26.                         icmp_send(head, ICMP_TIME_EXCEEDED, ICMP_EXC_FRAGTIME, 0);
  27.                         dev_put(head->dev);
  28.                 }
  29.         }
  30. out:
  31.         spin_unlock(&qp->lock);
  32.         ipq_put(qp, NULL);
  33. }

  34. void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info)
  35. {
  36.         struct iphdr *iph;
  37.         int room;
  38.         struct icmp_bxm icmp_param;
  39.         struct rtable *rt = (struct rtable *)skb_in->dst;
  40.         struct ipcm_cookie ipc;
  41.         __be32 saddr;
  42.         u8  tos;

  43.         if (!rt) {
  44.                 /* 在这里兑现,
  45.                  *
  46.                  * defarg, if issued by netfilter,
  47.                  * at PRE_ROUTING in normal cases,
  48.                  * 谁给skb_in->dst赋值?
  49.                  *
  50.                  * 本应发送Fragment Reassembly Timeout message,
  51.                  * 确没有发。
  52.                  */
  53.                 goto out;
  54.         }
  55.         /*
  56.          *        Find the original header. It is expected to be valid, of course.
  57.          *        Check this, icmp_send is called from the most obscure devices
  58.          *        sometimes.
  59.          */
  60.         iph = ip_hdr(skb_in);

  61.         if ((u8 *)iph < skb_in->head ||
  62.             (skb_in->network_header + sizeof(*iph)) > skb_in->tail)
  63.                 goto out;

  64.         /*
  65.          *        No replies to physical multicast/broadcast
  66.          */
  67.         if (skb_in->pkt_type != PACKET_HOST)
  68.                 goto out;

  69.         /*
  70.          *        Now check at the protocol level
  71.          */
  72.         if (rt->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST))
  73.                 goto out;

  74.         /*
  75.          *        Only reply to fragment 0. We byte re-order the constant
  76.          *        mask for efficiency.
  77.          */
  78.         if (iph->frag_off & htons(IP_OFFSET))
  79.                 goto out;

  80.         /*
  81.          *        If we send an ICMP error to an ICMP error a mess would result..
  82.          */
  83.         if (icmp_pointers[type].error) {
  84.                 /*
  85.                  *        We are an error, check if we are replying to an
  86.                  *        ICMP error
  87.                  */
  88.                 if (iph->protocol == IPPROTO_ICMP) {
  89.                         u8 _inner_type, *itp;

  90.                         itp = skb_header_pointer(skb_in,
  91.                                                  skb_network_header(skb_in) +
  92.                                                  (iph->ihl << 2) +
  93.                                                  offsetof(struct icmphdr,
  94.                                                           type) -
  95.                                                  skb_in->data,
  96.                                                  sizeof(_inner_type),
  97.                                                  &_inner_type);
  98.                         if (itp == NULL)
  99.                                 goto out;

  100.                         /*
  101.                          *        Assume any unknown ICMP type is an error. This
  102.                          *        isn't specified by the RFC, but think about it..
  103.                          */
  104.                         if (*itp > NR_ICMP_TYPES ||
  105.                             icmp_pointers[*itp].error)
  106.                                 goto out;
  107.                 }
  108.         }

  109.         if (icmp_xmit_lock())
  110.                 return;

  111.         /*
  112.          *        Construct source address and options.
  113.          */

  114.         saddr = iph->daddr;
  115.         if (!(rt->rt_flags & RTCF_LOCAL)) {
  116.                 struct net_device *dev = NULL;

  117.                 if (rt->fl.iif && sysctl_icmp_errors_use_inbound_ifaddr)
  118.                         dev = dev_get_by_index(rt->fl.iif);

  119.                 if (dev) {
  120.                         saddr = inet_select_addr(dev, 0, RT_SCOPE_LINK);
  121.                         dev_put(dev);
  122.                 } else
  123.                         saddr = 0;
  124.         }

  125.         tos = icmp_pointers[type].error ? ((iph->tos & IPTOS_TOS_MASK) |
  126.                                            IPTOS_PREC_INTERNETCONTROL) :
  127.                                           iph->tos;

  128.         if (ip_options_echo(&icmp_param.replyopts, skb_in))
  129.                 goto out_unlock;


  130.         /*
  131.          *        Prepare data for ICMP header.
  132.          */

  133.         icmp_param.data.icmph.type         = type;
  134.         icmp_param.data.icmph.code         = code;
  135.         icmp_param.data.icmph.un.gateway = info;
  136.         icmp_param.data.icmph.checksum         = 0;
  137.         icmp_param.skb          = skb_in;
  138.         icmp_param.offset = skb_network_offset(skb_in);
  139.         icmp_out_count(icmp_param.data.icmph.type);
  140.         inet_sk(icmp_socket->sk)->tos = tos;
  141.         ipc.addr = iph->saddr;
  142.         ipc.opt = &icmp_param.replyopts;

  143.         {
  144.                 struct flowi fl = {
  145.                         .nl_u = {
  146.                                 .ip4_u = {
  147.                                         .daddr = icmp_param.replyopts.srr ?
  148.                                                 icmp_param.replyopts.faddr :
  149.                                                 iph->saddr,
  150.                                         .saddr = saddr,
  151.                                         .tos = RT_TOS(tos)
  152.                                 }
  153.                         },
  154.                         .proto = IPPROTO_ICMP,
  155.                         .uli_u = {
  156.                                 .icmpt = {
  157.                                         .type = type,
  158.                                         .code = code
  159.                                 }
  160.                         }
  161.                 };
  162.                 security_skb_classify_flow(skb_in, &fl);
  163.                 if (ip_route_output_key(&rt, &fl))
  164.                         goto out_unlock;
  165.         }

  166.         if (!icmpv4_xrlim_allow(rt, type, code))
  167.                 goto ende;

  168.         /* RFC says return as much as we can without exceeding 576 bytes. */

  169.         room = dst_mtu(&rt->u.dst);
  170.         if (room > 576)
  171.                 room = 576;
  172.         room -= sizeof(struct iphdr) + icmp_param.replyopts.optlen;
  173.         room -= sizeof(struct icmphdr);

  174.         icmp_param.data_len = skb_in->len - icmp_param.offset;
  175.         if (icmp_param.data_len > room)
  176.                 icmp_param.data_len = room;
  177.         icmp_param.head_len = sizeof(struct icmphdr);

  178.         icmp_push_reply(&icmp_param, &ipc, rt);
  179. ende:
  180.         ip_rt_put(rt);
  181. out_unlock:
  182.         icmp_xmit_unlock();
  183. out:;
  184. }
复制代码

[ 本帖最后由 sisi8408 于 2007-8-13 15:56 编辑 ]

论坛徽章:
0
发表于 2007-08-13 15:39 |显示全部楼层

回复 #1 sisi8408 的帖子

How to fix it ?

dst_entry is used by several checks in icmp_send and  it's seen that send icmps bypass these checks is not sensible.

论坛徽章:
0
发表于 2007-08-13 15:54 |显示全部楼层
again ¥5 and daemeon,

ip_route_output_key seems shoot the bug.

[ 本帖最后由 sisi8408 于 2007-8-13 16:13 编辑 ]

论坛徽章:
0
发表于 2007-08-13 16:26 |显示全部楼层
perhaps ip_route_input is more suitable

论坛徽章:
0
发表于 2007-08-13 16:30 |显示全部楼层

回复 #4 daemeon 的帖子

so please, you add ip_route_input in icmp_send,
and ¥5 for your nice effort.

论坛徽章:
0
发表于 2007-08-13 17:43 |显示全部楼层
--- linux-2.6.22/net/ipv4/icmp.c        2007-07-09 07:32:17.000000000 +0800
+++ new/net/ipv4/icmp.c        2007-08-13 17:36:21.000000000 +0800
@@ -440,9 +440,6 @@ void icmp_send(struct sk_buff *skb_in, i
        __be32 saddr;
        u8  tos;

-        if (!rt)
-                goto out;
-
        /*
         *        Find the original header. It is expected to be valid, of course.
         *        Check this, icmp_send is called from the most obscure devices
@@ -461,16 +458,24 @@ void icmp_send(struct sk_buff *skb_in, i
                goto out;

        /*
-         *        Now check at the protocol level
+         *        Only reply to fragment 0. We byte re-order the constant
+         *        mask for efficiency.
         */
-        if (rt->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST))
+        if (iph->frag_off & htons(IP_OFFSET))
                goto out;

+        if (!rt) {
+                if (ip_route_input(skb_in, iph->daddr, iph->saddr,
+                                        iph->tos, skb_in->dev) != 0)
+                        goto out;
+
+                rt = (struct rtable *)skb_in->dst;
+        }
+
        /*
-         *        Only reply to fragment 0. We byte re-order the constant
-         *        mask for efficiency.
+         *        Now check at the protocol level
         */
-        if (iph->frag_off & htons(IP_OFFSET))
+        if (rt->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST))
                goto out;

        /*


icmp.patch.tar.gz (689 Bytes, 下载次数: 43)

论坛徽章:
0
发表于 2007-08-13 19:36 |显示全部楼层

  1. +        if (!rt) {
  2. +                struct net_device __ndev;
  3. +
  4. +                if (skb_in->input_dev)
  5. +                    __ndev = skb_in->input_dev;
  6. +                else
  7. +                   __ndev = get_dev_by_index(skb_in->iif);
  8. +                if (!__ndev)
  9. +                   goto out;
  10. +
  11. +                if (ip_route_input(skb_in, iph->daddr, iph->saddr,
  12. +                                             iph->tos, __ndev) != 0)
  13. +                        goto out;
  14. +
  15. +                rt = (struct rtable *)skb_in->dst;
  16. +        }
复制代码

anyway, you win ¥5

[ 本帖最后由 rtable 于 2007-8-13 19:40 编辑 ]

论坛徽章:
0
发表于 2007-08-13 21:07 |显示全部楼层

回复 #7 rtable 的帖子

thanks you for point out this.

论坛徽章:
0
发表于 2007-08-14 13:00 |显示全部楼层

回复 #6 daemeon 的帖子

¥4 to you, ok?
期待ing......去蓉,当面奉上。

论坛徽章:
0
发表于 2007-08-14 13:43 |显示全部楼层
Welcome back. hehe
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP