文档章节

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

摩云飞
 摩云飞
发布于 2016/04/25 19:47
字数 937
阅读 578
收藏 0
tcp

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

  • 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;
}

 

© 著作权归作者所有

摩云飞
粉丝 375
博文 534
码字总数 952694
作品 0
徐汇
程序员
私信 提问
【原创】TCP backlog 在 Linux 中如何起作用

How TCP backlog works in Linux January 1, 2014 (updated March 14, 2015) When an application puts a socket into LISTEN state using the listen syscall, it needs to specify a backl......

摩云飞
2016/04/22
243
0
TCP/IP协议中backlog参数

原文链接:http://www.cnblogs.com/Orgliny/p/5780796.html TCP建立连接是要进行三次握手,但是否完成三次握手后,服务器就处理(accept)呢?   backlog其实是一个连接队列,在Linux内核2...

1清风揽月1
2017/10/24
0
0
Linux OS||不响应SYN总结

背景 对外提供TCP服务的进程,在压测时发现,TCP连接SYN响应慢,甚至不响应。导致无法正常接收新的请求,影响业务。 抓包分析: 如上有大量的重传,有时能够正常的响应请求,有时就无法响应请...

枫凡
2017/05/15
0
0
【20180607】关于TCP队列信息溢出的排查和解释

关于TCP队列问题和内核参数的优化 http请求或者https请求间歇性的被丢弃或者访问失败的排查流程 检查dev 和网卡设备层,是否有error和drop。硬件和系统层面错误。 观察socket overflow 和 so...

liuhuang9496
2018/06/07
0
0
Linux中listen()系统调用的backlog参数分析

这篇文章是对上一篇博客网络编程常用接口的内核实现----syslisten()的补充,上篇文章中我说listen()系统调用的backlog参数既是连接队列的长度,也指定了半连接队列的长度(不能说等于),而不...

david_bj
2015/01/09
0
0

没有更多内容

加载失败,请刷新页面

加载更多

还为PDF转Word抓狂?以下神器让你在职场倍受欢迎!

身在职场的你,是否一直在琢磨:如何能让自己在公司更受欢迎?如何才能在办公室里混个好人缘?如何在同事圈里留个好印象?其实,想要让自己成为受欢迎的人,只要让自己成为大家需要的人不就行...

foxit2world
27分钟前
7
0
AndServer+Service打造Android服务器实现so文件调用

so 文件调用 随着 Android 移动安全的高速发展,不管是为了执行效率还是程序的安全性等,关键代码下沉 native 层已成为基本操作。 native 层的开发就是通指的 JNI/NDK 开发,通过 JNI 可以实...

夜幕NightTeam
29分钟前
4
0
Docker下kafka学习三部曲之二:本地环境搭建

在上一章《 Docker下kafka学习,三部曲之一:极速体验kafka》中我们快速体验了kafka的消息分发和订阅功能,但是对环境搭建的印象仅仅是执行了几个命令和脚本,本章我们通过实战来学习如何编写...

程序员欣宸
29分钟前
4
0
萌新推荐!不再为Excel转换PDF发愁,Aspose.Cells for .NET一步到位!

Aspose.Cells for .NET(点击下载)是Excel电子表格编程API,可加快电子表格管理和处理任务,支持构建具有生成,修改,转换,呈现和打印电子表格功能的跨平台应用程序。 将Excel工作簿转换为...

mnrssj
30分钟前
6
0
对于绘画小白怎么画制服?该注意什么?

怎样制作学生服装?想必绘画初学者们常常会想的问题吧,不知道怎样才能画好人物的衣服,别着急,今日就在这儿讲一些关于如何绘画学生衣服校服的教程给我们!期望能够帮到你们! 轻便西装是不...

热爱画画的我
35分钟前
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部