当我们收到ack包的时候,根据四层函数调用流程,将进入函数tcp_v4_hnd_req
- static struct sock *tcp_v4_hnd_req(struct sock *sk, struct sk_buff *skb)
- {
- ......
- /* Find possible connection requests. */
- struct request_sock *req = inet_csk_search_req(sk, &prev, th->source,
- iph->saddr, iph->daddr);//查找半连接队列,由于syn攻击时半连接队列已
- 满,调用返回NULL
- if (req)
- return tcp_check_req(sk, skb, req, prev);//检测ack准确性并建立连接,req为空不调用
- nsk = inet_lookup_established(sock_net(sk), &tcp_hashinfo, iph->saddr,
- th->source, iph->daddr, th->dest, inet_iif(skb));
- ......
- #ifdef CONFIG_SYN_COOKIES
- if (!th->rst && !th->syn && th->ack)
- sk = cookie_v4_check(sk, skb, &(IPCB(skb)->opt));//syncookies机制下的连接建立过程
- #endif
- return sk;
- }
接下来分析cookie_v4_check函数
- struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb,
- struct ip_options *opt)
- {
- ......
- __u32 cookie = ntohl(th->ack_seq) - 1;
- ......
- if (time_after(jiffies, tp->last_synq_overflow + TCP_TIMEOUT_INIT) ||
- (mss = cookie_check(skb, cookie)) == 0) {//检测ack的合法性
- NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_SYNCOOKIESFAILED);
- goto out;
- }
- ......
- if (tcp_opt.saw_tstamp)
- cookie_check_timestamp(&tcp_opt);//如果支持时间戳则从时间戳里恢复tcp选项
- ......
- ireq = inet_rsk(req);
- treq = tcp_rsk(req);
- treq->rcv_isn = ntohl(th->seq) - 1;//本机接收起始序列号,即syn包的seq
- treq->snt_isn = cookie;//本机发送起始序列号,即synack包的seq
- ......
- /* Try to redo what tcp_v4_send_synack did. */
- req->window_clamp = tp->window_clamp ? :dst_metric(&rt->u.dst, RTAX_WINDOW);
- tcp_select_initial_window(tcp_full_space(sk), req->mss,
- &req->rcv_wnd, &req->window_clamp,
- ireq->wscale_ok, &rcv_wscale);//计算rcv_wscale等的值,即本机的窗口扩大选项
- 等的值;这个值影响对端初始窗口大小
- ireq->rcv_wscale = rcv_wscale;
- ret = get_cookie_sock(sk, skb, req, &rt->u.dst);//建立连接,将req添加到建立连接的队列里
- out: return ret;
- }
ack合法性的检测是通过函数cookie_check实现的
- #define COUNTER_TRIES 4
- static inline int cookie_check(struct sk_buff *skb, __u32 cookie)
- {
- const struct iphdr *iph = ip_hdr(skb);
- const struct tcphdr *th = tcp_hdr(skb);
- __u32 seq = ntohl(th->seq) - 1;
- __u32 mssind = check_tcp_syn_cookie(cookie, iph->saddr, iph->daddr,
- th->source, th->dest, seq,
- jiffies / (HZ * 60),
- COUNTER_TRIES);//检测ack的合法性,返回计算后的低24位值
- return mssind < NUM_MSS ? msstab[mssind] + 1 : 0;//ack合法返回保存的mss值,不合法返回0
- }
cookie_check调用了
- static __u32 secure_tcp_syn_cookie(__be32 saddr, __be32 daddr, __be16 sport,
- __be16 dport, __u32 sseq, __u32 count,
- __u32 data)
- {
- ......
- return (cookie_hash(saddr, daddr, sport, dport, 0, 0) +
- sseq + (count << COOKIEBITS) +
- ((cookie_hash(saddr, daddr, sport, dport, count, 1) + data)
- & COOKIEMASK));
- }
- synack的seq的值为cookie_hash(...)+syn的seq+高8位的count(jiffies/60*HZ)+低24位的(cookie_hash(...)+mssd(mss区间值))我们记为hash1+seq+count+hash2
- 我们知道ack的ack_seq为synack的htonl(ntohl(seq)+1)
- static __u32 check_tcp_syn_cookie(__u32 cookie, __be32 saddr, __be32 daddr,
- __be16 sport, __be16 dport, __u32 sseq,
- __u32 count, __u32 maxdiff)
- {
- __u32 diff;
- cookie -= cookie_hash(saddr, daddr, sport, dport, 0, 0) + sseq;//减去hash1+seq
- diff = (count - (cookie >> COOKIEBITS)) & ((__u32) - 1 >> COOKIEBITS);//得到当前时间分钟
- 数与高8位中保存的收到syn包时的分钟数的差
- if (diff >= maxdiff)//判断差值是否大于4,如果大于ack不合法
- return (__u32)-1;
- return (cookie -
- cookie_hash(saddr, daddr, sport, dport, count - diff, 1))
- & COOKIEMASK;//减去hash2中的cookie_hash(...)并提取低24位
- }
接下来则根据时间戳里面保存的选项值,提取tcp选项
- void cookie_check_timestamp(struct tcp_options_received *tcp_opt)
- {
- /* echoed timestamp, 9 lowest bits contain options */
- u32 options = tcp_opt->rcv_tsecr & TSMASK;
- tcp_opt->snd_wscale = options & 0xf;//提取对端的接收窗口扩大选项
- options >>= 4;
- tcp_opt->rcv_wscale = options & 0xf;
- tcp_opt->sack_ok = (options >> 4) & 0x1;//提取sack支持
- if (tcp_opt->sack_ok)
- tcp_sack_reset(tcp_opt);
- if (tcp_opt->snd_wscale || tcp_opt->rcv_wscale)
- tcp_opt->wscale_ok = 1;//判断是否支持窗口扩大因子
- }
最后建立连接
- static inline struct sock *get_cookie_sock(struct sock *sk, struct sk_buff *skb,
- struct request_sock *req,
- struct dst_entry *dst)
- {
- struct inet_connection_sock *icsk = inet_csk(sk);
- struct sock *child;
- child = icsk->icsk_af_ops->syn_recv_sock(sk, skb, req, dst);//构造sock(每个监听端口一
- 个)下的子sock(每个连接
- 一个)
- if (child)
- inet_csk_reqsk_queue_add(sk, req, child);//添加到建立连接队列里
- else
- reqsk_free(req);
- return child;
- }
到此整个流程结束
阅读(2507) | 评论(0) | 转发(0) |