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

2011年(18)

2010年(6)

分类: LINUX

2011-04-26 15:41:03

当我们收到ack包的时候,根据四层函数调用流程,将进入函数tcp_v4_hnd_req
  1. static struct sock *tcp_v4_hnd_req(struct sock *sk, struct sk_buff *skb)
  2. {
  3.     ......
  4.     /* Find possible connection requests. */
  5.     struct request_sock *req = inet_csk_search_req(sk, &prev, th->source,
  6.                          iph->saddr, iph->daddr);//查找半连接队列,由于syn攻击时半连接队列已
  7.                                                    满,调用返回NULL
  8.     if (req)
  9.         return tcp_check_req(sk, skb, req, prev);//检测ack准确性并建立连接,req为空不调用
  10.     nsk = inet_lookup_established(sock_net(sk), &tcp_hashinfo, iph->saddr,
  11.             th->source, iph->daddr, th->dest, inet_iif(skb));
  12.     ......
  13. #ifdef CONFIG_SYN_COOKIES
  14.     if (!th->rst && !th->syn && th->ack)
  15.         sk = cookie_v4_check(sk, skb, &(IPCB(skb)->opt));//syncookies机制下的连接建立过程
  16. #endif
  17.     return sk;
  18. }

接下来分析cookie_v4_check函数

  1. struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb,
  2.              struct ip_options *opt)
  3. {
  4.     ......
  5.     __u32 cookie = ntohl(th->ack_seq) - 1;
  6.     ......
  7.     if (time_after(jiffies, tp->last_synq_overflow + TCP_TIMEOUT_INIT) ||
  8.      (mss = cookie_check(skb, cookie)) == 0) {//检测ack的合法性
  9.         NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_SYNCOOKIESFAILED);
  10.         goto out;
  11.     }
  12.     ......
  13.     if (tcp_opt.saw_tstamp)
  14.         cookie_check_timestamp(&tcp_opt);//如果支持时间戳则从时间戳里恢复tcp选项
  15.     ......
  16.     ireq = inet_rsk(req);
  17.     treq = tcp_rsk(req);
  18.     treq->rcv_isn        = ntohl(th->seq) - 1;//本机接收起始序列号,即syn包的seq
  19.     treq->snt_isn        = cookie;//本机发送起始序列号,即synack包的seq
  20.     ......
  21.     /* Try to redo what tcp_v4_send_synack did. */
  22.     req->window_clamp = tp->window_clamp ? :dst_metric(&rt->u.dst, RTAX_WINDOW);

  23.     tcp_select_initial_window(tcp_full_space(sk), req->mss,
  24.                  &req->rcv_wnd, &req->window_clamp,
  25.                  ireq->wscale_ok, &rcv_wscale);//计算rcv_wscale等的值,即本机的窗口扩大选项
  26.                                                  等的值;这个值影响对端初始窗口大小
  27.     ireq->rcv_wscale = rcv_wscale;
  28.     ret = get_cookie_sock(sk, skb, req, &rt->u.dst);//建立连接,将req添加到建立连接的队列里
  29. out:    return ret;
  30. }

ack合法性的检测是通过函数cookie_check实现的

  1. #define COUNTER_TRIES 4

  2. static inline int cookie_check(struct sk_buff *skb, __u32 cookie)
  3. {
  4.     const struct iphdr *iph = ip_hdr(skb);
  5.     const struct tcphdr *th = tcp_hdr(skb);
  6.     __u32 seq = ntohl(th->seq) - 1;
  7.     __u32 mssind = check_tcp_syn_cookie(cookie, iph->saddr, iph->daddr,
  8.                      th->source, th->dest, seq,
  9.                      jiffies / (HZ * 60),
  10.                      COUNTER_TRIES);//检测ack的合法性,返回计算后的低24位值

  11.     return mssind < NUM_MSS ? msstab[mssind] + 1 : 0;//ack合法返回保存的mss值,不合法返回0
  12. }
cookie_check调用了
  1. static __u32 secure_tcp_syn_cookie(__be32 saddr, __be32 daddr, __be16 sport,
  2.                  __be16 dport, __u32 sseq, __u32 count,
  3.                  __u32 data)
  4. {
  5.     ......
  6.     return (cookie_hash(saddr, daddr, sport, dport, 0, 0) +
  7.         sseq + (count << COOKIEBITS) +
  8.         ((cookie_hash(saddr, daddr, sport, dport, count, 1) + data)
  9.          & COOKIEMASK));
  10. }
  11. synack的seq的值为cookie_hash(...)+syn的seq+高8位的count(jiffies/60*HZ)+低24位的(cookie_hash(...)+mssd(mss区间值))我们记为hash1+seq+count+hash2
  12. 我们知道ack的ack_seq为synack的htonl(ntohl(seq)+1)
  13. static __u32 check_tcp_syn_cookie(__u32 cookie, __be32 saddr, __be32 daddr,
  14.                  __be16 sport, __be16 dport, __u32 sseq,
  15.                  __u32 count, __u32 maxdiff)
  16. {
  17.     __u32 diff;
  18.     cookie -= cookie_hash(saddr, daddr, sport, dport, 0, 0) + sseq;//减去hash1+seq
  19.     diff = (count - (cookie >> COOKIEBITS)) & ((__u32) - 1 >> COOKIEBITS);//得到当前时间分钟
  20.                                      数与高8位中保存的收到syn包时的分钟数的差
  21.     if (diff >= maxdiff)//判断差值是否大于4,如果大于ack不合法
  22.         return (__u32)-1;
  23.     return (cookie -
  24.         cookie_hash(saddr, daddr, sport, dport, count - diff, 1))
  25.         & COOKIEMASK;//减去hash2中的cookie_hash(...)并提取低24位
  26. }

接下来则根据时间戳里面保存的选项值,提取tcp选项

  1. void cookie_check_timestamp(struct tcp_options_received *tcp_opt)
  2. {
  3.     /* echoed timestamp, 9 lowest bits contain options */
  4.     u32 options = tcp_opt->rcv_tsecr & TSMASK;

  5.     tcp_opt->snd_wscale = options & 0xf;//提取对端的接收窗口扩大选项
  6.     options >>= 4;
  7.     tcp_opt->rcv_wscale = options & 0xf;

  8.     tcp_opt->sack_ok = (options >> 4) & 0x1;//提取sack支持

  9.     if (tcp_opt->sack_ok)
  10.         tcp_sack_reset(tcp_opt);

  11.     if (tcp_opt->snd_wscale || tcp_opt->rcv_wscale)
  12.         tcp_opt->wscale_ok = 1;//判断是否支持窗口扩大因子
  13. }

最后建立连接

  1. static inline struct sock *get_cookie_sock(struct sock *sk, struct sk_buff *skb,
  2.                      struct request_sock *req,
  3.                      struct dst_entry *dst)
  4. {
  5.     struct inet_connection_sock *icsk = inet_csk(sk);
  6.     struct sock *child;

  7.     child = icsk->icsk_af_ops->syn_recv_sock(sk, skb, req, dst);//构造sock(每个监听端口一
  8.                                                                   个)下的子sock(每个连接
  9.                                                                   一个)
  10.     if (child)
  11.         inet_csk_reqsk_queue_add(sk, req, child);//添加到建立连接队列里
  12.     else
  13.         reqsk_free(req);
  14.     return child;
  15. }
到此整个流程结束
阅读(2463) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~