本文共 3856 字,大约阅读时间需要 12 分钟。
echo 1 > /proc/sys/net/ipv4/tcp_syncookies注意,开启该机制并不意味着所有的连接都是使用 SYN cookies 机制来完成连接的建立,只有在半连接队列已满的情况下才会触发 SYN cookies 机制。由于 SYN cookies 机制严重违背 TCP 协议,不允许使用TCP 扩展,可能对某些服务造成严重的性能影响(如 SMTP 转发),对于防御 SYN flood 攻击的确有效。对于没有受到攻击的高负载服务器,不要开启此选项,可以通过修改 tcp_max_syn_backlog 、tcp_synack_retries 和tcp_abort_on_overflow 系统参数来调节。
int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb){#ifdef CONFIG_SYN_COOKIES int want_cookie = 0;#else#define want_cookie 0 /* Argh, why doesn't gcc optimize this :( */#endif ...... /* TW buckets are converted to open requests without * limitations, they conserve resources and peer is * evidently real one. */ if (inet_csk_reqsk_queue_is_full(sk) && !isn) {#ifdef CONFIG_SYN_COOKIES if (sysctl_tcp_syncookies) { want_cookie = 1; } else#endif goto drop; } ...... drop: return 0;}如果没有开启 SYN cookies 机制,在半连接队列满时,会跳转到 drop 处,返回 0 。在调用 tcp_v4_conn_request() 的 tcp_rcv_state_process() 中会直接释放 SKB 包。
if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1) goto drop;sk_acceptq_is_full() 函数很好理解,根据字面意思就可以看出,该函数是检查连接队列是否已满。inet_csk_reqsk_queue_young() 函数返回的是半连接队列中未重传过 SYN+ACK 段的连接请求块数量。如果连接队列已满并且半连接队列中的连接请求块中未重传的数量大于 1,则会跳转到 drop 处,丢弃 SYN 包。如果半连接队列中未重传的请求块数量大于 1,则表示未来可能有 2 个完成的连接,这些新完成的连接要放到连接队列中,但此时连接队列已满。如果在接收到三次握手中最后的 ACK 后连接队列中没有空闲的位置,会忽略接收到的 ACK 包,连接建立会推迟,所以此时最好丢掉部分新的连接请求,空出资源以完成正在进行的连接建立过程。还要注意,这个判断并没有考虑半连接队列是否已满的问题。从这里可以看出,即使开启了 SYN cookies 机制并不意味着一定可以完成连接的建立。
req = inet_reqsk_alloc(&tcp_request_sock_ops); if (!req) goto drop;看到这里可能就有人疑惑,既然开启了 SYN cookies 机制,仍然分配连接请求块,那和正常的连接构建也没有什么区别了。这里之所以要分配连接请求块是用于发送 SYN+ACK 包给客户端,发送后会释放掉,并不会加入到半连接队列中。
if (want_cookie) {#ifdef CONFIG_SYN_COOKIES syn_flood_warning(skb); req->cookie_ts = tmp_opt.tstamp_ok;#endif isn = cookie_v4_init_sequence(sk, skb, &req->mss); }计算得到的 cookie 值会保存在连接请求块 tcp_request_sock 结构的 snt_isn 成员中,接着会调用 __tcp_v4_send_synack() 函数发送 SYN+ACK 包,然后释放前面分配的连接请求块,如下所示:
if (__tcp_v4_send_synack(sk, req, dst) || want_cookie) goto drop_and_free;在服务器端发送完 SYN+ACK 包后,我们看到在服务器端没有保存任何关于这个未完成连接的信息,所以在接收到客户端的 ACK 包后,只能根据前面发送的 SYN+ACK 包中的 cookie 值来决定是否继续构建连接。
static struct sock *tcp_v4_hnd_req(struct sock *sk, struct sk_buff *skb){ ...... #ifdef CONFIG_SYN_COOKIES if (!th->rst && !th->syn && th->ack) sk = cookie_v4_check(sk, skb, &(IPCB(skb)->opt));#endif return sk;}由于在服务器端没有保存未完成连接的信息,所以在半连接队列或 ehash 散列表中都不会找到对应的 sock 结构。如果开启了 SYN cookies 机制,则会检查接收到的数据包是否是 ACK 包,如果是,在cookie_v4_check() 中会调用 cookie_check() 函数检查 ACK 包中的 cookie 值是否有效。如果有效,则会分配 request_sock 结构,并根据 ACK 包初始化相应的成员,开始构建描述连接的 sock 结构。创建过程和正常的连接创建过程一样。
转载地址:http://oxrlo.baihongyu.com/