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

2011年(18)

2010年(6)

分类: LINUX

2011-05-06 22:50:00

(3)目的端接收syn到发送synack
从前文得知,当数据报每经过一个源路由路径节点时,ip选项中的offset字段的值就加4,那么到达源路由路径的最后一个节点并得到转发后,offset字段的值所表示的偏移地址已经指向选项的末尾了
  1. void ip_forward_options(struct sk_buff *skb)
  2. {
  3.     ......
  4.     if (opt->srr_is_hit) {
  5.         int srrptr, srrspace;

  6.         optptr = raw + opt->srr;

  7.         for ( srrptr=optptr[2], srrspace = optptr[1];
  8.          srrptr <= srrspace;
  9.          srrptr += 4
  10.          ) {
  11.             if (srrptr + 3 > srrspace)
  12.                 break;
  13.             if (memcmp(&rt->rt_dst, &optptr[srrptr-1], 4) == 0)
  14.                 break;
  15.         }
  16.         if (srrptr + 3 <= srrspace) {
  17.             opt->is_changed = 1;
  18.             ip_rt_get_source(&optptr[srrptr-1], rt);
  19.             skb->nh.iph->daddr = rt->rt_dst;
  20.             optptr[2] = srrptr+4;//偏移量加4,即移动一个ip地址
  21.         } else if (net_ratelimit())
  22.             printk(KERN_CRIT "ip_forward(): Argh! Destination lost!\n");
  23.         if (opt->ts_needaddr) {
  24.             optptr = raw + opt->ts;
  25.             ip_rt_get_source(&optptr[optptr[2]-9], rt);
  26.             opt->is_changed = 1;
  27.         }
  28.     }
  29.     if (opt->is_changed) {
  30.         opt->is_changed = 0;
  31.         ip_send_check(skb->nh.iph);
  32.     }
  33. }
当syn包到达目的地址时,将进入函数
  1. int ip_options_rcv_srr(struct sk_buff *skb)
  2. {
  3.     struct ip_options *opt = &(IPCB(skb)->opt);
  4.     int srrspace, srrptr;
  5.     u32 nexthop;
  6.     struct iphdr *iph = skb->nh.iph;
  7.     unsigned char * optptr = skb->nh.raw + opt->srr;
  8.     ......

  9.     for (srrptr=optptr[2], srrspace = optptr[1]; srrptr <= srrspace; srrptr += 4) {
  10. //optptr[1]为选项的长度,optptr[2]为offset;由于在目的端,offset指向选项的末尾,并且大小为options的长度+1(为对齐的选项长度);则srrptr>srrspace,此for循环将不执行
  11.         if (srrptr + 3 > srrspace) {
  12.             icmp_send(skb, ICMP_PARAMETERPROB, 0, htonl((opt->srr+2)<<24));
  13.             return -EINVAL;
  14.         }
  15.         memcpy(&nexthop, &optptr[srrptr-1], 4);

  16.         rt = (struct rtable*)skb->dst;
  17.         skb->dst = NULL;
  18.         err = ip_route_input(skb, nexthop, iph->saddr, iph->tos, skb->dev);
  19.         rt2 = (struct rtable*)skb->dst;
  20.         if (err || (rt2->rt_type != RTN_UNICAST && rt2->rt_type != RTN_LOCAL)) {
  21.             ip_rt_put(rt2);
  22.             skb->dst = &rt->u.dst;
  23.             return -EINVAL;
  24.         }
  25.         ip_rt_put(rt);
  26.         if (rt2->rt_type != RTN_LOCAL)
  27.             break;
  28.         /* Superfast 8) loopback forward */
  29.         memcpy(&iph->daddr, &optptr[srrptr-1], 4);
  30.         opt->is_changed = 1;
  31.     }
  32.     if (srrptr <= srrspace) {//srrptr>srrspace,不执行
  33.         opt->srr_is_hit = 1;
  34.         opt->is_changed = 1;
  35.     }
  36.     return 0;//直接返回
  37. }
返回到函数ip_rcv_finish
  1. static inline int ip_rcv_finish(struct sk_buff *skb)
  2. {
  3.     struct iphdr *iph = skb->nh.iph;
  4.     struct rtable *rt;

  5.     /*
  6.      *    Initialise the virtual path cache for the packet. It describes
  7.      *    how the packet travels inside Linux networking.
  8.      */
  9.     if (skb->dst == NULL) {
  10.         int err = ip_route_input(skb, iph->daddr, iph->saddr, iph->tos,
  11.                      skb->dev);//dst->input注册为ip_local_deliver
  12.         if (unlikely(err)) {
  13.             if (err == -EHOSTUNREACH)
  14.                 IP_INC_STATS_BH(IPSTATS_MIB_INADDRERRORS);
  15.             else if (err == -ENETUNREACH)
  16.                 IP_INC_STATS_BH(IPSTATS_MIB_INNOROUTES);
  17.             goto drop;
  18.         }
  19.     }

  20.     ......
  21.     if (iph->ihl > 5 && ip_rcv_options(skb))//dst->input没有被修改,因为此时数据包已经到达目
  22.                                               的端
  23.         goto drop;
  24.     ......
  25.     return dst_input(skb);//本机包的后续处理过程
  26.     ......
  27. }

此时接收到的数据的ip选项中保存了源路由路径节点ip地址,并且是按数据包通过节点的顺序存储的;选项保存在了skb的cb中可以通过&(IPCB(skb)->opt)得到。

现在我们来分析tcp层函数tcp_v4_conn_reques函数(不考虑syncookies的过程)

  1. int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
  2. {
  3.     struct inet_request_sock *ireq;
  4.     struct tcp_options_received tmp_opt;
  5.     struct request_sock *req;
  6.     __u32 saddr = skb->nh.iph->saddr;
  7.     __u32 daddr = skb->nh.iph->daddr;
  8.     __u32 isn = TCP_SKB_CB(skb)->when;
  9.     struct dst_entry *dst = NULL;
  10.     ......

  11.     ireq = inet_rsk(req);
  12.     ireq->loc_addr = daddr;
  13.     ireq->rmt_addr = saddr;
  14.     ireq->opt = tcp_v4_save_options(sk, skb);//将skb中cb保存的ip选项拷贝到ireq->opt中
  15.     ......
  16.     if (tcp_v4_send_synack(sk, req, dst))//发送synack
  17.         goto drop_and_free;
  18.     ......
  19. }

现分析tcp_v4_save_options函数

  1. static struct ip_options *tcp_v4_save_options(struct sock *sk,
  2.                      struct sk_buff *skb)
  3. {
  4.     struct ip_options *opt = &(IPCB(skb)->opt);//opt指向skb中的cb
  5.     struct ip_options *dopt = NULL;

  6.     if (opt && opt->optlen) {
  7.         int opt_size = optlength(opt);
  8.         dopt = kmalloc(opt_size, GFP_ATOMIC);
  9.         if (dopt) {
  10.             if (ip_options_echo(dopt, skb)) {//处理skb中保存的ip选项
  11.                 kfree(dopt);
  12.                 dopt = NULL;
  13.             }
  14.         }
  15.     }
  16.     return dopt;
  17. }
  1. int ip_options_echo(struct ip_options * dopt, struct sk_buff * skb)
  2. {
  3.     struct ip_options *sopt;
  4.     unsigned char *sptr, *dptr;
  5.     int soffset, doffset;
  6.     int    optlen;
  7.     u32    daddr;

  8.     memset(dopt, 0, sizeof(struct ip_options));

  9.     dopt->is_data = 1;

  10.     sopt = &(IPCB(skb)->opt);//sopt指向skb的cb

  11.     if (sopt->optlen == 0) {
  12.         dopt->optlen = 0;
  13.         return 0;
  14.     }

  15.     sptr = skb->nh.raw;
  16.     dptr = dopt->__data;
  17.     ......
  18.     if (sopt->srr) {//只分析源路由选项,其他如记录路由选项和时间戳选项可类似分析
  19.         unsigned char * start = sptr+sopt->srr;//start指向选项的一个字节
  20.         u32 faddr;

  21.         optlen = start[1];
  22.         soffset = start[2];//此时为目的端,则相对于选项起始位置的偏移量指向了选项的末尾;譬如,客户端192.168.18.71 源路由路径节点192.168.18.72 目的端192.168.18.73,则此时len=7,offset=8(1个ip地址的长度(18.72)+1字节type+1字节len+1字节offset+1字节对齐)
  23.         doffset = 0;
  24.         if (soffset > optlen)
  25.             soffset = optlen + 1;
  26.         soffset -= 4;//偏移量减4,即向左移动一个ip地址长度(4字节)
  27.         if (soffset > 3) {
  28.             memcpy(&faddr, &start[soffset-1], 4);//现在偏移量标识的是倒数第一个ip地址(源路由路径节点中的最后的节点),譬如客户端A,路由节点B,路由节点C,服务器端D,那么此时的faddr为C的ip地址。faddr为我们发送synack的下一跳地址,也为synack的目的地址
  29.             for (soffset-=4, doffset=4; soffset > 3; soffset-=4, doffset+=4)
  30.                 memcpy(&dptr[doffset-1], &start[soffset-1], 4);//将skb中保存的ip选项中的数据段(ip地址)拷贝到dopt->__data字段中,并且以逆序保存,譬如接收到的options中的ip顺序是18.71
  31. 18.72 18.73,faddr为18.73,那么现在dopt->__data中的是18.72 18.71
  32.             /*
  33.              * RFC1812 requires to fix illegal source routes.
  34.              */
  35.             if (memcmp(&skb->nh.iph->saddr, &start[soffset+3], 4) == 0)
  36.                 doffset -= 4;
  37.         }
  38.         if (doffset > 3) {
  39.             memcpy(&start[doffset-1], &daddr, 4);
  40.             dopt->faddr = faddr;//填充dopt的下一跳地址
  41.             dptr[0] = start[0];//填充dopt的type位,和收到的options一致
  42.             dptr[1] = doffset+3;//填充len位,doffset的值为源路由路径ip的个数*4,3为
  43.                                   len+type+offset 3字节
  44.             dptr[2] = 4;//指向源路由路径首个ip,也就是到达下一跳后,下一跳将选为目的ip地址的路径节点
  45.             dptr += doffset+3;
  46.             dopt->srr = dopt->optlen + sizeof(struct iphdr);//填充srr,相对于ip头的偏移量
  47.             dopt->optlen += doffset+3;
  48.             dopt->is_strictroute = sopt->is_strictroute;//如果接收的options是严格路由,设定
  49.                                                           严格路由选项
  50.         }
  51.     }
  52.     ......
  53.     return 0;//返回
  54. }
现在来分析tcp_v4_send_synack
  1. static int tcp_v4_send_synack(struct sock *sk, struct request_sock *req,
  2.              struct dst_entry *dst)
  3. {
  4.     const struct inet_request_sock *ireq = inet_rsk(req);
  5.     int err = -1;
  6.     struct sk_buff * skb;

  7.     /* First, grab a route. */
  8.     if (!dst && (dst = inet_csk_route_req(sk, req)) == NULL)//查找路由,构造路由缓存项。目的
  9.                                                               地址我faddr
  10.         goto out;

  11.     skb = tcp_make_synack(sk, dst, req);//构造skb并填充tcp头

  12.     if (skb) {
  13.         struct tcphdr *th = skb->h.th;

  14.         th->check = tcp_v4_check(th, skb->len,
  15.                      ireq->loc_addr,
  16.                      ireq->rmt_addr,
  17.                      csum_partial((char *)th, skb->len,
  18.                          skb->csum));

  19.         err = ip_build_and_send_pkt(skb, sk, ireq->loc_addr,//填充ip头和ip选项;
  20.                            ireq->loc_addr为本地地址,ireq->rmt_addr为目的地址,即syn包的源地址
  21.                      ireq->rmt_addr,
  22.                      ireq->opt);
  23.         if (err == NET_XMIT_CN)
  24.             err = 0;
  25.     }

  26. out:
  27.     dst_release(dst);
  28.     return err;
  29. }
  1. struct dst_entry* inet_csk_route_req(struct sock *sk,
  2.                  const struct request_sock *req)
  3. {
  4.     struct rtable *rt;
  5.     const struct inet_request_sock *ireq = inet_rsk(req);
  6.     struct ip_options *opt = inet_rsk(req)->opt;//tcp_v4_save_options函数将skb保存的
  7.                                                   options拷贝到inet_rsk(req)->opt
  8.     struct flowi fl = { .oif = sk->sk_bound_dev_if,
  9.              .nl_u = { .ip4_u =
  10.                  { .daddr = ((opt && opt->srr) ?//如果有源路由选项,则路由的下一跳为选项中的下一
  11.                                                   跳地址,即options结构的faddr
  12.                          opt->faddr :
  13.                          ireq->rmt_addr),
  14.                     .saddr = ireq->loc_addr,
  15.                     .tos = RT_CONN_FLAGS(sk) } },
  16.              .proto = sk->sk_protocol,
  17.              .uli_u = { .ports =
  18.                  { .sport = inet_sk(sk)->sport,
  19.                      .dport = ireq->rmt_port } } };

  20.     security_req_classify_flow(req, &fl);
  21.     if (ip_route_output_flow(&rt, &fl, sk, 0)) {
  22.         IP_INC_STATS_BH(IPSTATS_MIB_OUTNOROUTES);
  23.         return NULL;
  24.     }
  25.     if (opt && opt->is_strictroute && rt->rt_dst != rt->rt_gateway) {//如果是严格路由选
  26.                                                    项,并且路由的下一跳和网关不一致,就返回
  27.         ip_rt_put(rt);
  28.         IP_INC_STATS_BH(IPSTATS_MIB_OUTNOROUTES);
  29.         return NULL;
  30.     }
  31.     return &rt->u.dst;
  32. }
现在我们来看ip头的构造
  1. int ip_build_and_send_pkt(struct sk_buff *skb, struct sock *sk,
  2.              u32 saddr, u32 daddr, struct ip_options *opt)
  3. {
  4.     struct inet_sock *inet = inet_sk(sk);
  5.     struct rtable *rt = (struct rtable *)skb->dst;
  6.     struct iphdr *iph;

  7.     /* Build the IP header. */
  8.     if (opt)
  9.         iph=(struct iphdr *)skb_push(skb,sizeof(struct iphdr) + opt->optlen);
  10.     else
  11.         iph=(struct iphdr *)skb_push(skb,sizeof(struct iphdr));

  12.     iph->version = 4;
  13.     iph->ihl = 5;
  14.     iph->tos = inet->tos;
  15.     if (ip_dont_fragment(sk, &rt->u.dst))
  16.         iph->frag_off = htons(IP_DF);
  17.     else
  18.         iph->frag_off = 0;
  19.     iph->ttl = ip_select_ttl(inet, &rt->u.dst);
  20.     iph->daddr = rt->rt_dst;//synack包的目的地址为路由的下一跳地址,即options的faddr
  21.     iph->saddr = rt->rt_src;//本地地址
  22.     iph->protocol = sk->sk_protocol;
  23.     iph->tot_len = htons(skb->len);
  24.     ip_select_ident(iph, &rt->u.dst, sk);
  25.     skb->nh.iph = iph;

  26.     if (opt && opt->optlen) {
  27.         iph->ihl += opt->optlen>>2;
  28.         ip_options_build(skb, opt, daddr, rt, 0);//填充ip选项,daddr为syn包的源地址(客户端
  29.                                                    地址);opt为inet_rsk(req)->opt,为我们保
  30.                                                    存的syn包的选项
  31.     }
  32.     ip_send_check(iph);

  33.     skb->priority = sk->sk_priority;

  34.     /* Send it out. */
  35.     return NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev,//dst_output发送
  36.          dst_output);
  37. }
  1. void ip_options_build(struct sk_buff * skb, struct ip_options * opt,
  2.              u32 daddr, struct rtable *rt, int is_frag)
  3. {
  4.     unsigned char * iph = skb->nh.raw;

  5.     memcpy(&(IPCB(skb)->opt), opt, sizeof(struct ip_options));
  6.     memcpy(iph+sizeof(struct iphdr), opt->__data, opt->optlen);
  7.     opt = &(IPCB(skb)->opt);
  8.     opt->is_data = 0;

  9.     if (opt->srr)
  10.         memcpy(iph+opt->srr+iph[opt->srr+1]-4, &daddr, 4);//如果是源路径路由,则将syn包的源地
  11.                                                             址放到ip选项源路径的尾部

  12.     if (!is_frag) {//如果不是分片
  13.         if (opt->rr_needaddr)//记录路由的地址部分
  14.             ip_rt_get_source(iph+opt->rr+iph[opt->rr+2]-5, rt);//通过路由缓存获取源地址
  15.         if (opt->ts_needaddr)//时间戳选项的地址部分
  16.             ip_rt_get_source(iph+opt->ts+iph[opt->ts+2]-9, rt);
  17.         if (opt->ts_needtime) {//时间戳选项的时间部分
  18.             struct timeval tv;
  19.             __u32 midtime;
  20.             do_gettimeofday(&tv);
  21.             midtime = htonl((tv.tv_sec % 86400) * 1000 + tv.tv_usec / 1000);
  22.             memcpy(iph+opt->ts+iph[opt->ts+2]-5, &midtime, 4);
  23.         }
  24.         return;
  25.     }
  26.     if (opt->rr) {//如果是记录路由选项,并且是分片,将这些选项替换成无操作
  27.         memset(iph+opt->rr, IPOPT_NOP, iph[opt->rr+1]);
  28.         opt->rr = 0;
  29.         opt->rr_needaddr = 0;
  30.     }
  31.     if (opt->ts) {
  32.         memset(iph+opt->ts, IPOPT_NOP, iph[opt->ts+1]);
  33.         opt->ts = 0;
  34.         opt->ts_needaddr = opt->ts_needtime = 0;
  35.     }
  36. }
  37. 此时ip选项的填充完成,如果我们的环境是这样:客户端(18.71),路由节点1(18.72),路由节点2(18.72),服务器端(18.76);那么在服务器端收到的syn包的选项中的地址顺序是18.71 18.72,iptions_echo后选项中的数据是18.71,则发送的synack的地址顺序是18.71 18.73

下面是实例分析

实验(一)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

在服务器端18.76上抓包:

上图为syn包

上图为synack包

如果设置网关不影响数据包的接收转发和发送

实验(二)SSRR

没有设置网关的时候数据包流程正常

如果在18.76上配置18.79的网关,则synack发送不了。

 

如是,ip选项基本上分析完了

阅读(4579) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~