Linux快速统计TCP半连接的数量

如果你想知道当前系统(运行Linux内核)中处在SYN RECV状态的TCP半连接数量的大小,最朴素的方法莫过如下:

netstat -antp|grep SYN_RECV|wc -l
ss -ant|grep SYN-RECV|wc -l

有什么问题吗?

方法朴素归朴素,但只能应对日常需求,如果遇到SYN Flood攻击,执行netstat/ss -ant命令需要 扫描遍历系统中所有的连接 ,然后再grep出半连接,在系统已经遭受攻击时,遍历操作无疑是雪上加霜!

你想如果上百万的半连接到达你的系统,会怎样?

然而,令人遗憾的是,系统中没有这样的关于SYN RECV状态的连接总量的之际统计值,至少我是没有找到,无论从ss -s还是从netstat -s,或者说snmp中,都没有找到这样的统计值。

那么如何在系统在面临攻击已经扛不住的情况下,快速统计半连接的数量,这无疑是一个很有意思的工作。

也许你会想到tcp_diag模块,但是它依然是遍历,只不过是采用更加复杂的netlink接口(研究的不多)。当我看inet_diag_dump_icsk函数时,我失望至极!所以,激动的心瞬间又沉了下去。


2020年第一个雷雨天,开了一下午会,饭也没吃一口,写下这篇文章。


在系统压力很大时,遍历所有的socket不现实, 但是LISTEN socket的数量是固定的 ,你见过系统的Listener超过1万的吗?不超过1万,遍历开销就不是事儿。

系统的Listener不会随着系统压力的增加而增加,它们分布在INET_LHTABLE_SIZE个hash桶中,每一个Listener都保存着自己的半连接计数,用其listen_sock的qlen字段计数,我们只需要把这些加起来就是结果了。

看来只有自己动手了。下面是代码:

#include <linux/module.h>
#include <net/tcp.h>

static unsigned int dump_syn_recv(void)
{
	unsigned int num = 0;
	int i;
	struct inet_hashinfo *hashinfo = &tcp_hashinfo;

	for (i = 0; i < INET_LHTABLE_SIZE; i++) {
		struct sock *sk;
		struct hlist_nulls_node *node;
		struct inet_listen_hashbucket *ilb;

		ilb = &hashinfo->listening_hash[i];
		spin_lock_bh(&ilb->lock);
		sk_nulls_for_each(sk, node, &ilb->head) {
			struct inet_connection_sock *icsk = inet_csk(sk);
			struct listen_sock *lopt;

			read_lock_bh(&icsk->icsk_accept_queue.syn_wait_lock);
			lopt = icsk->icsk_accept_queue.listen_opt;
			if (lopt && lopt->qlen)
				num += lopt->qlen;
			read_unlock_bh(&icsk->icsk_accept_queue.syn_wait_lock);
		}
		spin_unlock_bh(&ilb->lock);
	}
	return num;
}

static ssize_t dump_read(struct file *file, char __user *ubuf, size_t count, loff_t *ppos)
{
	unsigned int num = dump_syn_recv(), n;
	char kbuf[16] = {0};

	if (*ppos != 0) {
		return 0;
	}

	n = snprintf(kbuf, 16, "%d\n", num);
	memcpy(ubuf, kbuf, n);
	*ppos += n;

	return n;
}

static struct file_operations dump_ops = {
	.owner = THIS_MODULE,
	.read = dump_read,
};

static struct proc_dir_entry *ent;
static int __init dumpstat_init(void)
{
	ent = proc_create("syn-recv-cnt", 0660, NULL, &dump_ops);
	if (!ent)
		return -1;

	return 0;
}

static void __exit dumpstat_exit(void)
{
	proc_remove(ent);
}

module_init(dumpstat_init);
module_exit(dumpstat_exit);
MODULE_LICENSE("GPL");

下面是一个模拟攻击时的测试结果:

# cat /proc/syn-recv-cnt
82719

也许有人会质疑这里面的两把锁,其实这种质疑是徒劳的:

  • ilb->lock是hash冲突桶的锁,对于Listener而言,冲突桶并不会很大。
  • icsk_accept_queue.syn_wait_lock是队列锁,它和热点函数互斥,但是互斥区间非常小。

由于这个dump操作基本上是一个oneshot的低频操作,且在Listener固定的情况下,它的时间复杂度是O(1)的,所以我觉得是OK的,这主要要感谢的是,内核自己做了lopt->qlen计数统计,我们只需要将每一个Listener的该字段累加起来就是了。

没有必要看见有锁操作就dis,这是一种因噎废食的偏见。

我不明白为什么tcp diag没有提供一个轻量级的如此这般的dump机制,也许是我没有注意到,不过随他去吧,反正我这个已经够轻量了。


netstat/ss工具好不好?当然好,但是它是常规意义上的好。同样的案例还有延伸到iptables/nftables,ebpf/xdp,如果精准化特定场景,还是需要自己动手来搞非通用方案。

为什么Linux内核为什么直到现在都没有一个计数器来统计半连接的数量?因为那是针对TCP的专有需求,Linux内核显然不是偏向于TCP的。无论如何,我觉得为其增加一些percpu的无锁计数器,是优雅的做法,你至少可以应对别人说你引入原子变量锁总线的开销,是吧,哈哈。

当然,这些话并不是对经理说的。


浙江温州皮鞋湿,下雨进水不会胖。

  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: Linux下有许多可用于调试TCP连接的工具和命令。 1. Wireshark:Wireshark是一种流行的网络封包分析工具,可以捕获和分析网络数据包。它可以用于监视和分析TCP连接查看包的内容、源和目的地IP地址、端口号和标志等信息。 2. tcpdump:tcpdump是一个命令行工具,用于抓取和分析网络封包。可以使用tcpdump命令来监视和捕获TCP连接的数据包。它可以输出TCP连接的相关信息,包括源和目的地IP地址、端口号、数据包的内容和标志等。 3. ss命令:ss命令是一种功能强大的工具,用于查看和调试TCP连接。它可以显示当前系统上的所有TCP连接,包括源和目的地IP地址、端口号、状态、数据包的数量和经过的时间等。 4. lsof命令:lsof命令用于列出当前系统上所有打开的文件和进程。在调试TCP连接时,可以使用lsof命令来查看当前系统上打开的TCP连接,包括源和目的地IP地址、端口号、状态和相关的进程号等。 5. netstat命令:netstat命令用于显示当前系统上的网络连接统计信息。可以使用netstat命令来查看当前系统上的TCP连接信息,包括源和目的地IP地址、端口号、状态和相关的进程号等。 通过使用这些工具和命令,可以有效地调试和分析TCP连接,以便解决网络相关的问题。 ### 回答2: Linux提供了许多用于调试TCP的工具。以下是一些常用的工具: 1. tcpdump:是一个网络封包分析工具,可以捕获网络数据包并显示其内容。可以用来查看特定端口的流量、检查数据包的源和目的地址以及查看数据包的详细信息。 2. netstat:用于查看网络连接状态和统计数据的命令行工具。可以显示当前系统上的所有网络连接,包括TCP连接和监听端口。 3. Wireshark:是一个功能强大的网络协议分析工具,可以用来捕获和分析网络数据包。可以显示TCP连接的各个阶段,包括握手、数据传输和连接关闭等,提供了详细的协议分析功能。 4. tcpflow:用于捕获和保存TCP流量的工具。它可以将TCP连接的传输内容保存为文件,方便后续分析和调试。 5. ss:是一个用于查看套接字信息的命令行工具。可以显示当前系统上的套接字状态,包括TCP连接状态,以及连接相关的详细信息。 除了上述工具外,还有一些其他辅助工具,如nstat、tshark等,用于更深入的网络分析和调试。这些工具可以帮助我们跟踪网络连接的状态、监视数据流量以及分析网络故障等。使用这些工具可以更好地理解和调试TCP连接,提高网络应用的性能和稳定性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值