现在我们来分析一下,syncookies的基本原理。
由于syncookies机制不要求建立半连接,当第三次握手ack到来的时候,正常情况下会查找半连接队列,此时我们在半连接队列里找不到储存的半连接信息,那么这又将如何根据ack建立正常的连接呢?
首先我们来看下,主机回的synack有什么特点:上节中我们看到tcp_rsk(req)->snt_isn = isn,将计算的isn值赋给了snt_isn成员;在函数tcp_make_synack中,
tcp_init_nondata_skb(skb, tcp_rsk(req)->snt_isn, TCPCB_FLAG_SYN | TCPCB_FLAG_ACK); th->seq = htonl(TCP_SKB_CB(skb)->seq); th->ack_seq = htonl(tcp_rsk(req)->rcv_isn + 1);
|
上图中可以看出调用了函数tcp_init_nondata_skb,
static void tcp_init_nondata_skb(struct sk_buff *skb, u32 seq, u8 flags) { skb->csum = 0;
TCP_SKB_CB(skb)->flags = flags; TCP_SKB_CB(skb)->sacked = 0;
skb_shinfo(skb)->gso_segs = 1; skb_shinfo(skb)->gso_size = 0; skb_shinfo(skb)->gso_type = 0;
TCP_SKB_CB(skb)->seq = seq; if (flags & (TCPCB_FLAG_SYN | TCPCB_FLAG_FIN)) seq++; TCP_SKB_CB(skb)->end_seq = seq; }
|
可以看到函数将tcp_rsk(req)->snt_isn的值赋给了TCP_SKB_CB(skb)->seq;最后,
th->seq = htonl(TCP_SKB_CB(skb)->seq),则th->seq的值就等于tcp_rsk(req)->snt_isn也就是我们在上一节计算的isn值;这就是在syncookies机制下的synack包的特殊之处。
下面来看下isn的计算过程:
#define COOKIEBITS 24 /* Upper bits store count */ #define COOKIEMASK (((__u32)1 << COOKIEBITS) - 1)
|
static __u16 const msstab[] = { 64 - 1, 256 - 1, 512 - 1, 536 - 1, 1024 - 1, 1440 - 1, 1460 - 1, 4312 - 1, (__u16)-1 };
|
__u32 cookie_v4_init_sequence(struct sock *sk, struct sk_buff *skb, __u16 *mssp)//*mssp为协商后的mss值,这在tcp_v4_conn_request函数中根据解析接收到的syn包tcp选项中的mss值与本机mss值协商较小值。 { struct tcp_sock *tp = tcp_sk(sk); const struct iphdr *iph = ip_hdr(skb); const struct tcphdr *th = tcp_hdr(skb); int mssind; const __u16 mss = *mssp;
tp->last_synq_overflow = jiffies;//这个设置是为了当发送synack后3秒(内核初始化的RTO)内没有收到ack就认为此ack不合法
/* XXX sort msstab[] by probability? Binary search? */ for (mssind = 0; mss > msstab[mssind + 1]; mssind++) ; *mssp = msstab[mssind] + 1;//mssind为协商的mss在msstab中的位置
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_SYNCOOKIESSENT);
return secure_tcp_syn_cookie(iph->saddr, iph->daddr, th->source, th->dest, ntohl(th->seq), jiffies / (HZ * 60), mssind); }
|
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));//count=jiffies/(HZ*60),表示密钥一分钟更新一次 }
|
我们来分析下这个cookie值是怎么计算的。从函数secure_tcp_syn_cookie中可以看出,cookie值有三部分组成:32位的cookie_hash函数返回值+32位的syn包的序列号(seq)+高8位的count值+低24位的cookie_hash+data。data值为mssind的值。
计算完后cookie值,就将它作为synack的seq值,发送synack;那么如何通过接收到的ack包来建立正常的连接呢,我们应该知道此时半连接队列中没有记录该syn包的信息,如果没有syncookie机制,即使有收到第三次握手的ack包也会毫不留情的丢弃。
我们知道三次握手的序列号关系是这样的:第一次握手(用户发送syn包,th为tcp头)th->seq=t;第二次握手(服务器发送synack包)th->seq=s,th->ack_seq=t+1;第三次握手(用户发送ack包)th->seq=t+1,th->ack_seq=s+1;那么我们显然可以根据收到的ack包中的seq和ack_seq来恢复协商后的mss值。现在来看下,是如何判断ack的合法性的呢。
阅读(2496) | 评论(0) | 转发(0) |