在三次握手过程中,server端的TCP收到SYN请求后会建立一个request_sock保存在syn_table中。如果有恶意攻击者大量发送IP地址或端口不同的SYN包,则server端TCP的syn_table很快会被占满,而普通用户对server的正常访问会因为syn_table已满而被拒绝。这就是SYN Flood攻击。SYN Cookie技术就是为了应对SYN Flood攻击而产生的,它的原理是,在TCP服务器收到SYN包并返回SYN|ACK包时,不分配一个专门的数据区,而是根据这个SYN包计算出一个cookie值,在发送SYN|ACK时将这个cookie作为其序列号。在收到ACK包时,TCP服务器根据确认号得到之前的cookie,再根据这个cookie值检查这个ACK包的合法性。如果合法,再分配sock保存未来的TCP连接的信息。
如果要Linux内核支持SYN Cookie功能,必须开启CONFIG_SYN_COOKIES编译选项。下面分析一下TCP对SYN Cookie功能的实现。
如果要Linux内核支持SYN Cookie功能,必须开启CONFIG_SYN_COOKIES编译选项。下面分析一下TCP对SYN Cookie功能的实现。
TCP服务器在接收到SYN时,会调用tcp_v4_conn_request函数:
1465 int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
1466 {
1467 struct tcp_options_received tmp_opt;
1468 struct request_sock *req;
1469 struct inet_request_sock *ireq;
1470 struct tcp_sock *tp = tcp_sk(sk);
1471 struct dst_entry *dst = NULL;
1472 __be32 saddr = ip_hdr(skb)->saddr;
1473 __be32 daddr = ip_hdr(skb)->daddr;
1474 __u32 isn = TCP_SKB_CB(skb)->when;
1475 bool want_cookie = false;
1476 struct flowi4 fl4;
1477 struct tcp_fastopen_cookie foc = { .len = -1 };
1478 struct tcp_fastopen_cookie valid_foc = { .len = -1 };
1479 struct sk_buff *skb_synack;
1480 int do_fastopen;
...
1486 /* TW buckets are converted to open requests without
1487 * limitations, they conserve resources and peer is
1488 * evidently real one.
1489 */
1490 if (inet_csk_reqsk_queue_is_full(sk) && !isn) {//如果存储request sock的accept队列已满且SYN包没有命中TIME_WAIT socekt
1491 want_cookie = tcp_syn_flood_action(sk, skb, "TCP");//如果用户使用sysctl_tcp_syncookies开启了syn cookie功能,则want_cookie为1
1492 if (!want_cookie)//如果使用syn cookie机制,不需要保存request sock,所以不需要受到accept queue的限制
1493 goto drop;
1494 }
...
1506 req = inet_reqsk_alloc(&tcp_request_sock_ops);//申请一个request sock,但不会保存到accept queue中
1507 if (!req)
1508 goto drop;
...
1517 tcp_parse_options(skb, &tmp_opt, 0, want_cookie ? NULL : &foc);//如果使用syn cookie,则不能使用fast open功能,因为无处存储相关信息
1518
1519 if (want_cookie && !tmp_opt.saw_tstamp)//使用syn cookie时如果对端没有开启时间戳选项
1520 tcp_clear_options(&tmp_opt); //则本端不支持SACK和窗口扩大选项,因为无处存储相关信息
...
1523 tcp_openreq_init(req, &tmp_opt, skb);
...
1537 if (want_cookie) {//使用syn cookie的话,
1538 isn = cookie_v4_init_sequence(sk, skb, &req->mss);//生成syn cookie并以之作为SYN|ACK的起始序列号
1539 req->cookie_ts = tmp_opt.tstamp_ok;
1540 } else if (!isn) {
...
1578 tcp_rsk(req)->snt_isn = isn;
...
1598 skb_synack = tcp_make_synack(sk, dst, req,
1599 fastopen_cookie_present(&valid_foc) ? &valid_foc : NULL);
...
1607 if (likely(!do_fastopen)) {
1608 int err;
1609 err = ip_build_and_send_pkt(skb_synack, sk, ireq->loc_addr,
1610 ireq->rmt_addr, ireq->opt);
1611 err = net_xmit_eval(err);
1612 if (err || want_cookie)//发送完SYN|ACK后,丢弃此request sock
1613 goto drop_and_free;
...
1629 drop_and_free:
1630 reqsk_free(req);
1490:只有accpet队列满时才会启用syn cookie