【原创】源码分析 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.\n",生成 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.\n"
		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;
}
时间: 2024-08-02 05:00:37

【原创】源码分析 TCP 协议中的 SYN queue 和 accept queue 处理的相关文章

jQuery源码分析之jQuery中的循环技巧详解_jquery

jQuery的源码中有很多值得学习借鉴的技巧,本文即收集了jQuery中出现的各种遍历技巧和场景.具体分析如下: // 简单的for-in(事件) for ( type in events ) { } // 缓存length属性,避免每次都去查找length属性,稍微提升遍历速度 // 但是如果遍历HTMLCollection时,性能提升非常明显,因为每次访问HTMLCollection的属性,HTMLCollection都会内部匹配一次所有的节点 for ( var j = 0, l = ha

MapReduce源码分析之Task中关于对应TaskAttempt存储Map方案的一些思考

        我们知道,MapReduce有三层调度模型,即Job-->Task-->TaskAttempt,并且:         1.通常一个Job存在多个Task,这些Task总共有Map Task和Redcue Task两种大的类型(为简化描述,Map-Only作业.JobSetup Task等复杂的情况这里不做考虑):         2.每个Task可以尝试运行1-n此,而且通常很多情况下都是1次,只有当开启了推测执行原理且存在拖后腿Task,或者Task之前执行失败时,Task

tcprstat源码分析之tcp数据包分析

tcprstat是percona用来监测mysql响应时间的.不过对于任何运行在TCP协议上的响应时间,都可以用.本文主要做源码分析,如何使用tcprstat请大家查看博文<tcprstat分析服务的响应速度利器> tcprstat和tcpdump一样,使用libpcap库进行抓包,然后再通过程序对抓取的tcp包进行分析. tcprstat对tcp包分析的大概流程如下: <img src="http://www.bo56.com/wp-content/uploads/2014/

请问socket中各种函数(rec,send等)功能是怎么实现的,有没有socket的源码分析一下。

问题描述 请问socket中各种函数(rec,send等)功能是怎么实现的,有没有socket的源码分析一下. 我想说的是求socket的实现,而不是利用socket去编程,求大神 解决方案 可以先了解一下winpcap编程 解决方案二: Linux TCP/IP协议栈之Socket的实现分析(一 套接字的创建) 解决方案三: 基本上都在driver里面了 你要愿意去读linux 源代码好了 解决方案四: socket的实现就很底层了,那个应该属于嵌入式驱动开发了,需要控制硬件也要开放上层软件接

详解SpringMVC中Controller的方法中参数的工作原理[附带源码分析] good

目录 前言 现象 源码分析 HandlerMethodArgumentResolver与HandlerMethodReturnValueHandler接口介绍 HandlerMethodArgumentResolver与HandlerMethodReturnValueHandler接口的具体应用 常用HandlerMethodArgumentResolver介绍 常用HandlerMethodReturnValueHandler介绍 本文开头现象解释以及解决方案 编写自定义的HandlerMet

Hhadoop-2.7.0中HDFS写文件源码分析(二):客户端实现之DFSPacket

一.简介       HDFS在数据传输过程中,针对数据块Block,不是整个block进行传输的,而是将block切分成一个个的数据包进行传输.而DFSPacket就是HDFS数据传输过程中对数据包的抽象. 二.实现       HDFS客户端在往DataNodes节点写数据时,会以数据包packet的形式写入,且每个数据包包含一个包头,n个连续的校验和数据块checksum chunks和n个连续的实际数据块 actual data chunks,每个校验和数据块对应一个实际数据块,被用来做

Hhadoop-2.7.0中HDFS写文件源码分析(二):客户端实现(1)

一.综述       HDFS写文件是整个Hadoop中最为复杂的流程之一,它涉及到HDFS中NameNode.DataNode.DFSClient等众多角色的分工与合作.       首先上一段代码,客户端是如何写文件的: Configuration conf = new Configuration(); FileSystem fs = FileSystem.get(conf); Path file = new Path("demo.txt"); FSDataOutputStream

jQuery中deferred对象详解及源码分析

jQuery的deferred对象使用详解 jQuery的开发速度很快,几乎每半年一个大版本,每两个月一个小版本.每个版本都会引入一些新功能.今天我想介绍的,就是从jQuery 1.5.0版本开始引入的一个新功能--deferred对象.这个功能很重要,未来将成为jQuery的核心方法,它彻底改变了如何在jQuery中使用ajax.为了实现它,jQuery的全部ajax代码都被改写了.但是,它比较抽象,初学者很难掌握,网上的教程也不多.所以,我把自己的学习笔记整理出来了,希望对大家有用. 本文不

OkHttp 3.7源码分析(五)——连接池

OkHttp3.7源码分析文章列表如下: OkHttp源码分析--整体架构 OkHttp源码分析--拦截器 OkHttp源码分析--任务队列 OkHttp源码分析--缓存策略 OkHttp源码分析--多路复用 接下来讲下OkHttp的连接池管理,这也是OkHttp的核心部分.通过维护连接池,最大限度重用现有连接,减少网络连接的创建开销,以此提升网络请求效率. 1. 背景 1.1 keep-alive机制 在HTTP1.0中HTTP的请求流程如下: 这种方法的好处是简单,各个请求互不干扰.但在复杂