zoukankan      html  css  js  c++  java
  • tcp的半连接与完全连接队列(三)源码分析

    TCP 协议中的 SYN queue 和 accept queue 处理 

    若要理解本文意图说明的问题,可能需要以下知识背景:

    • listen 系统调用的 backlog 参数含义,以及与 net.core.somaxconn 参数的关系;
    • SYN flood 攻击与防护;
    • SYN queue 和 accept queue 的用途,以及在不同 linux 版本中的实现差异;


    ----

    在 SYN queue 未满的情况下,在收到 SYN 包后,TCP 协议栈自动回复 SYN,ACK 包,之后在收到 ACK 时,根据 accept queue 状态进行后续处理;
    若 SYN queue 已满,在收到 SYN 时
          若设置 net.ipv4.tcp_syncookies = 0 ,则直接丢弃当前 SYN 包;
          若设置 net.ipv4.tcp_syncookies = 1 ,则令 want_cookie = 1 继续后面的处理;
                若 accept queue 已满,并且 qlen_young 的值大于 1 ,则直接丢弃当前 SYN 包;
                若 accept queue 未满,或者 qlen_young 的值未大于 1 ,则输出 "possible SYN flooding on port %d. Sending cookies. ",生成 syncookie 并在 SYN,ACK 中带上;

    ---  以下源码取自 linux-2.6.32 版本 ---

    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.
    	 */
    	// 判定 SYN queue 是否已满
    	if (inet_csk_reqsk_queue_is_full(sk) && !isn) {
    #ifdef CONFIG_SYN_COOKIES
    		if (sysctl_tcp_syncookies) {  // SYN queue 已满,并且设置了 net.ipv4.tcp_syncookies = 1 ,则设置 want_cookie = 1 以便后续处理
    			want_cookie = 1;
    		} else
    #endif
    		goto drop;    // 否则,直接丢弃当前 SYN 包
    	}
    
    	/* Accept backlog is full. If we have already queued enough
    	 * of warm entries in syn queue, drop request. It is better than
    	 * clogging syn queue with openreqs with exponentially increasing
    	 * timeout.
    	 */
    	// 若此时 accept queue 也已满,并且 qlen_young 的值大于 1(即保存在 SYN queue 中未进行 SYN,ACK 重传的连接超过 1 个)
    	// 则直接丢弃当前 SYN 包(相当于针对 SYN 进行了速率限制)
    	if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1)
    		goto drop;
    ...
    	// 若 accept queue 未满,或者 qlen_young 的值未大于 1
    	if (want_cookie) {
    #ifdef CONFIG_SYN_COOKIES
    		syn_flood_warning(skb);  // 输出 "possible SYN flooding on port %d. Sending cookies.
    "
    		req->cookie_ts = tmp_opt.tstamp_ok;  // 为当前 socket 设置启用 cookie 标识
    #endif
    		// 生成 syncookie
    		isn = cookie_v4_init_sequence(sk, skb, &req->mss);
    	} else if (!isn) {
    	    ...
    	}
    	// 保存 syncookie 值
    	tcp_rsk(req)->snt_isn = isn;
    
    	// 回复 SYN,ACK ,若之前设置了 net.ipv4.tcp_syncookies = 1 则会释放对应的 socket 结构
    	if (__tcp_v4_send_synack(sk, req, dst) || want_cookie)
    		goto drop_and_free;
    		
    	// 启动 SYN,ACK 重传定时器
    	inet_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT);
    	return 0;
    
    drop_and_release:
    	dst_release(dst);
    drop_and_free:
    	reqsk_free(req);
    drop:
    	return 0;
    }

    ---

    若 accept queue 已满,在收到三次握手最后的 ACK 时
          若设置 tcp_abort_on_overflow = 1 ,则 TCP 协议栈回复 RST 包,并直接从 SYN queue 中删除该连接信息;
          若设置 tcp_abort_on_overflow = 0 ,则 TCP 协议栈将该连接标记为 acked ,但仍保留在 SYN queue 中,并启动 timer 以便重发 SYN,ACK 包;当 SYN,ACK 的重传次数超过 net.ipv4.tcp_synack_retries 设置的值时,再将该连接从 SYN queue 中删除;

    ---

    /*
     *	Process an incoming packet for SYN_RECV sockets represented
     *	as a request_sock.
     */
    struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb,
            struct request_sock *req,
            struct request_sock **prev)
    {
        ...
    	/* OK, ACK is valid, create big socket and
    	 * feed this segment to it. It will repeat all
    	 * the tests. THIS SEGMENT MUST MOVE SOCKET TO
    	 * ESTABLISHED STATE. If it will be dropped after
    	 * socket is created, wait for troubles.
    	 */
    	// 调用 net/ipv4/tcp_ipv4.c 中的 tcp_v4_syn_recv_sock 函数
    	// 判定 accept queue 是否已经满,若已满,则返回的 child 为 NULL
    	child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb, req, NULL);
    	if (child == NULL)
    		goto listen_overflow;
    
    	// 在 accept queue 未满的情况下,将 ESTABLISHED 连接从 SYN queue 搬移到 accept queue 中
    	inet_csk_reqsk_queue_unlink(sk, req, prev);
    	inet_csk_reqsk_queue_removed(sk, req);
    
    	inet_csk_reqsk_queue_add(sk, req, child);
    	return child;
    
    listen_overflow:
    	// 若 accept queue 已满,但设置的是 net.ipv4.tcp_abort_on_overflow = 0
    	if (!sysctl_tcp_abort_on_overflow) {
    		inet_rsk(req)->acked = 1;    // 则只标记为 acked ,直接返回,相当于忽略当前 ACK
    		return NULL;
    	}
    
    embryonic_reset:
    	// 若 accept queue 已满,但设置的是 net.ipv4.tcp_abort_on_overflow = 1
    	NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_EMBRYONICRSTS);   // 变更统计信息
    	if (!(flg & TCP_FLAG_RST))
    		req->rsk_ops->send_reset(sk, skb);   // 发送 RST
    
    	inet_csk_reqsk_queue_drop(sk, req, prev);    // 直接从 SYN queue 中删除该连接信息
        return NULL;
    }
  • 相关阅读:
    python 【第一篇】初识python
    python 【目录】
    python 爬虫必知必会
    MySQL测试代码
    MySQL学习笔记
    pycharm社区版无database 解决方法
    windows python flask上传文件出现IOError: [Errno 13] Permission denied: 'E:\git\test\static\uploads'的解决方法
    ubuntu下python flask环境搭建
    windows下的python flask环境搭建
    Mockito (十四)
  • 原文地址:https://www.cnblogs.com/shihuvini/p/9650218.html
Copyright © 2011-2022 走看看