Chinaunix首页 | 论坛 | 博客
  • 博客访问: 145289
  • 博文数量: 24
  • 博客积分: 791
  • 博客等级: 军士长
  • 技术积分: 350
  • 用 户 组: 普通用户
  • 注册时间: 2010-02-22 11:00
文章存档

2011年(18)

2010年(6)

分类: LINUX

2011-05-05 21:56:07

ip选项处理(一)中讨论了syn包的发送过程对ip选项的处理,接下来分析接收syn并转发的过程
(2)路由节点接收syn到转发syn
我们知道ip层接收数据的函数调用过程是ip_rcv ---> ip_rcv_finish,在ip_rcv_finish中将会对ip选项进行处理
  1. static inline int ip_rcv_finish(struct sk_buff *skb)
  2. {
  3.     ......
  4.     if (iph->ihl > 5 && ip_rcv_options(skb))//ip选项的处理
  5.         goto drop;
  6.     ......
  7.     return dst_input(skb);
  8.     ......
  9. }
  10. static inline int dst_input(struct sk_buff *skb)
    {
          int err;
          for (;;) {
               err = skb->dst->input(skb);//skb->dst->input注册为ip_forward函数
               ......
           }
    }
分析ip_rcv_options函数
  1. static inline int ip_rcv_options(struct sk_buff *skb)
  2. {
  3.     ......
  4.     struct net_device *dev = skb->dev;
  5.     .......
  6.     if (ip_options_compile(NULL, skb)) {//解析ip选项各字段值,并保存在skb的cb中
  7.         IP_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
  8.         goto drop;
  9.     }

  10.     opt = &(IPCB(skb)->opt);
  11.     if (unlikely(opt->srr)) {
  12.         struct in_device *in_dev = in_dev_get(dev);
  13.         if (in_dev) {
  14.             if (!IN_DEV_SOURCE_ROUTE(in_dev)) {//
  15. #define IN_DEV_SOURCE_ROUTE(in_dev) (ipv4_devconf.accept_source_route && (in_dev)->cnf.accept_source_route)
  16. 可以看出如果accept_source_route为0,就丢弃该报;所以如果我们要支持源路径路由,则必须设置
  17. echo 1 > /proc/sys/net/ipv4/conf/eth0/accept_source_route //网卡支持
  18. echo 1 > /proc/sys/net/ipv4/conf/all/accept_source_route
  19. 内核中
  20. accept_source_route - BOOLEAN
     Accept packets with SRR option.
     conf/all/accept_source_route must also be set to TRUE to accept packets
     with SRR option on the interface
     default TRUE (router)
      FALSE (host)

  21.                 if (IN_DEV_LOG_MARTIANS(in_dev) &&
  22.                  net_ratelimit())
  23.                     printk(KERN_INFO "source route option "
  24.                      "%u.%u.%u.%u -> %u.%u.%u.%u\n",
  25.                      NIPQUAD(iph->saddr),
  26.                      NIPQUAD(iph->daddr));
  27.                 in_dev_put(in_dev);
  28.                 goto drop;
  29.             }

  30.             in_dev_put(in_dev);
  31.         }

  32.         if (ip_options_rcv_srr(skb))//检查输入数据报中的宽松源路由及严格源路由选项,并根据源路由
  33.                                       选项更新IP数据包的下一跳地址
  34.             goto drop;
  35.     }

  36.     return 0;
  37. drop:
  38.     return -1;
  39. }
  1. int ip_options_rcv_srr(struct sk_buff *skb)
  2. {
  3.     ......
  4.     if (!opt->srr)//待处理的数据报中没有源路径路由选项,则返回
  5.         return 0;

  6.     if (skb->pkt_type != PACKET_HOST)
  7.         return -EINVAL;
  8.     if (rt->rt_type == RTN_UNICAST) {//在路由类型为RTN_UNICAST,即网关或直接连接的路由情况下执行严格路由是会有问题的。此时会发送一个参数错误ICMP差错报文给发送方,并返回参数无效错误
  9.         if (!opt->is_strictroute)//非严格路径路由;宽松路由时,当数据包到达网关时,会立即返回
  10.             return 0;
  11.         icmp_send(skb, ICMP_PARAMETERPROB, 0, htonl(16<<24));
  12.         return -EINVAL;
  13.     }
  14.     if (rt->rt_type != RTN_LOCAL)
  15.         return -EINVAL;

  16.     for (srrptr=optptr[2], srrspace = optptr[1]; srrptr <= srrspace; srrptr += 4) {//
  17. srrptr等于offset,srrspace为选项的长度,因为此时为路由节点,offset不是指向选项尾部,所以
  18. optptr[1]-optptr[2]>0
  19.         if (srrptr + 3 > srrspace) {
  20.             icmp_send(skb, ICMP_PARAMETERPROB, 0, htonl((opt->srr+2)<<24));
  21.             return -EINVAL;
  22.         }
  23.         memcpy(&nexthop, &optptr[srrptr-1], 4);//设定下一跳地址,即为偏移量指定的ip地址

  24.         rt = (struct rtable*)skb->dst;
  25.         skb->dst = NULL;
  26.         err = ip_route_input(skb, nexthop, iph->saddr, iph->tos, skb->dev);//根据下一跳来
  27.                                                查找路由,此时dst->input指向了ip_forward函数
  28.         rt2 = (struct rtable*)skb->dst;
  29.         if (err || (rt2->rt_type != RTN_UNICAST && rt2->rt_type != RTN_LOCAL)) {//skb->dst的下一跳被设置为ip选项中选择的下一跳地址;这个用于在转发时,设置数据包的目的地址时使用
  30.             ip_rt_put(rt2);
  31.             skb->dst = &rt->u.dst;
  32.             return -EINVAL;
  33.         }
  34.         ip_rt_put(rt);
  35.         if (rt2->rt_type != RTN_LOCAL)//rt2->rt_type==RTN_UNICAST,所以break
  36.             break;
  37.         /* Superfast 8) loopback forward */
  38.         memcpy(&iph->daddr, &optptr[srrptr-1], 4);//设定ip头的目的地址为下一跳地址
  39.         opt->is_changed = 1;//标识该数据报目的地址做了修改
  40.     }
  41.     if (srrptr <= srrspace) {
  42.         opt->srr_is_hit = 1;/如果源路由选项的路径列表没有遍历完,则说明该IP数据报的目的地址是从源路由选项选出的,因此需设置srr_is_hit标志,待转发时需要进一步处理。同时还需要设置is_changed,标识需重新计算IP数据报的首部校验和
  43.         opt->is_changed = 1;
  44.     }
  45.     return 0;
  46. }
ip_options_rcv_srr函数返回后,数据包的目的地址暂时未改为了下一跳的地址,dst->input设置为ip_forward函数,则会进入转发流程
  1. int ip_forward(struct sk_buff *skb)
  2. {
  3.     struct iphdr *iph;    /* Our header */
  4.     struct rtable *rt;    /* Route we use */
  5.     struct ip_options * opt    = &(IPCB(skb)->opt);//指向skb的cb中保存的ip options
  6.     ......
  7.     rt = (struct rtable*)skb->dst;
  8.     if (opt->is_strictroute && rt->rt_dst != rt->rt_gateway)//如果是严格路由,并且目的地址和网关地址不一致,就会发送ICMP目的不可达报文,报告源路由失败;我们将在试验中提及
  9.         goto sr_failed;
  10.     ......

  11.     return NF_HOOK(PF_INET, NF_IP_FORWARD, skb, skb->dev, rt->u.dst.dev,
  12.          ip_forward_finish);//调用ip_forward_finish函数

  13. sr_failed:
  14.         /*
  15.      *    Strict routing permits no gatewaying
  16.      */
  17.          icmp_send(skb, ICMP_DEST_UNREACH, ICMP_SR_FAILED, 0);//发送ICMP目的不可达报文
  18.          goto drop;
  19.     ......
  20. }
  1. static inline int ip_forward_finish(struct sk_buff *skb)
  2. {
  3.     struct ip_options * opt    = &(IPCB(skb)->opt);//获得ip选项

  4.     IP_INC_STATS_BH(IPSTATS_MIB_OUTFORWDATAGRAMS);

  5.     if (unlikely(opt->optlen))
  6.         ip_forward_options(skb);//想转发的数据包添加所需的关于本地IP的信息,包括记录路由选项和时
  7.                                   间戳选项

  8.     return dst_output(skb);//在ip层发送出去
  9. }
  1. void ip_forward_options(struct sk_buff *skb)
  2. {
  3.     struct ip_options * opt    = &(IPCB(skb)->opt);
  4.     unsigned char * optptr;
  5.     struct rtable *rt = (struct rtable*)skb->dst;
  6.     unsigned char *raw = skb->nh.raw;

  7.     if (opt->rr_needaddr) {
  8.         optptr = (unsigned char *)raw + opt->rr;
  9.         ip_rt_get_source(&optptr[optptr[2]-5], rt);//如果需要记录IP地址,则获取本地地址并设置到
  10.                                                      IP记录路由选项中
  11.         opt->is_changed = 1;
  12.     }
  13.     if (opt->srr_is_hit) {//如果目的地址是从源路由选项指定的,则还需要判断输出路由缓存的目的地址是否存在于路由选项中。
  14.         int srrptr, srrspace;

  15.         optptr = raw + opt->srr;

  16.         for ( srrptr=optptr[2], srrspace = optptr[1];
  17.          srrptr <= srrspace;
  18.          srrptr += 4
  19.          ) {
  20.             if (srrptr + 3 > srrspace)
  21.                 break;
  22.             if (memcmp(&rt->rt_dst, &optptr[srrptr-1], 4) == 0)
  23.                 break;
  24.         }
  25.         if (srrptr + 3 <= srrspace) {
  26.             opt->is_changed = 1;
  27.             ip_rt_get_source(&optptr[srrptr-1], rt);//将本地地址加入到ip选项中第一个ip地址之前
  28.             skb->nh.iph->daddr = rt->rt_dst;//数据包的目的地址被设置为下一跳地址;rt->rt_dst在函数ip_options_rcv_srr被设置为下一跳地址
  29.             optptr[2] = srrptr+4;//偏移量指向下一跳地址
  30.         } else if (net_ratelimit())
  31.             printk(KERN_CRIT "ip_forward(): Argh! Destination lost!\n");
  32.         if (opt->ts_needaddr) {
  33.             optptr = raw + opt->ts;
  34.             ip_rt_get_source(&optptr[optptr[2]-9], rt);
  35.             opt->is_changed = 1;
  36.         }
  37.     }
  38.     if (opt->is_changed) {//ip头的目的地址被修改后,就必须重新计算校验和
  39.         opt->is_changed = 0;
  40.         ip_send_check(skb->nh.iph);
  41.     }
  42. }

下面通过两个实验来说明:

实验一(LSRR):

客户端:192.168.18.73 路由节点1:192.168.18.71 路由节点2:192.168.18.72 服务器端:192.168.18.76 18.71上18.0网段有个网关192.168.18.79

1.在18.71上抓包(接收并转发syn)

上图为接收的syn包

上图为转发的syn包

2.在18.72上抓包(接收和转发syn)

上图为接收的syn包

上图为转发的syn包

同样在18.71设置18.79网关时,不影响宽松路由功能

实验二(SSRR)

偏移量的改变以及ip选项中路径的改变和LSRR类似

严格路由时,如果路由节点的网关和ip选项中下一跳不一致时,就会发送icmp目的不可达报文

我在18.71上设置18.79网关,18.71不会转发syn而是发送ICMP目的不可达报文

阅读(4423) | 评论(0) | 转发(2) |
0

上一篇:夜殇

下一篇:linux ip选项处理(三)

给主人留下些什么吧!~~