前言

新申请的服务器内核为2.6.32,原先的TCP Server直接在新内核的Linxu服务器上运行,运行dmesg命令,可以看到大量的SYN flooding警告: possible SYN flooding on port 8080. Sending cookies.

原先的2.6.18内核的参数在2.6.32内核版本情况下,简单调整"net.ipv4.tcp_max_syn_backlog"已经没有作用。

怎么办,只能再次阅读2.6.32源码,以下即是。

最后小结处有直接结论,心急的你可以直接阅读总结好了。

linux内核2.6.32有关backlog值分析

net/Socket.c:SYSCALL_DEFINE2(listen, int, fd, int, backlog)

{

struct socket *sock;

int err, fput_needed;

int somaxconn;

sock = sockfd_lookup_light(fd, &err, &fput_needed);

if (sock) {

somaxconn = sock_net(sock->sk)->core.sysctl_somaxconn;

if ((unsigned)backlog > somaxconn)

backlog = somaxconn;

err = security_socket_listen(sock, backlog);

if (!err)

err = sock->ops->listen(sock, backlog);

fput_light(sock->file, fput_needed);

}

return err;

}

net/ipv4/Af_inet.c:/*

* Move a socket into listening state.

*/

int inet_listen(struct socket *sock, int backlog)

{

struct sock *sk = sock->sk;

unsigned char old_state;

int err;

lock_sock(sk);

err = -EINVAL;

if (sock->state != SS_UNCONNECTED || sock->type != SOCK_STREAM)

goto out;

old_state = sk->sk_state;

if (!((1 << old_state) & (TCPF_CLOSE | TCPF_LISTEN)))

goto out;

/* Really, if the socket is already in listen state

* we can only allow the backlog to be adjusted.

*/

if (old_state != TCP_LISTEN) {

err = inet_csk_listen_start(sk, backlog);

if (err)

goto out;

}

sk->sk_max_ack_backlog = backlog;

err = 0;

out:

release_sock(sk);

return err;

}

inet_listen调用inet_csk_listen_start函数,所传入的backlog参数改头换面,变成了不可修改的常量nr_table_entries了。

net/ipv4/Inet_connection_sock.c:int inet_csk_listen_start(struct sock *sk, const int nr_table_entries)

{

struct inet_sock *inet = inet_sk(sk);

struct inet_connection_sock *icsk = inet_csk(sk);

int rc = reqsk_queue_alloc(&icsk->icsk_accept_queue, nr_table_entries);

if (rc != 0)

return rc;

sk->sk_max_ack_backlog = 0;

sk->sk_ack_backlog = 0;

inet_csk_delack_init(sk);

/* There is race window here: we announce ourselves listening,

* but this transition is still not validated by get_port().

* It is OK, because this socket enters to hash table only

* after validation is complete.

*/

sk->sk_state = TCP_LISTEN;

if (!sk->sk_prot->get_port(sk, inet->num)) {

inet->sport = htons(inet->num);

sk_dst_reset(sk);

sk->sk_prot->hash(sk);

return 0;

}

sk->sk_state = TCP_CLOSE;

__reqsk_queue_destroy(&icsk->icsk_accept_queue);

return -EADDRINUSE;

}

下面处理的是TCP SYN_RECV状态的连接,处于握手阶段,也可以说是半连接时,等待着连接方第三次握手。/*

* Maximum number of SYN_RECV sockets in queue per LISTEN socket.

* One SYN_RECV socket costs about 80bytes on a 32bit machine.

* It would be better to replace it with a global counter for all sockets

* but then some measure against one socket starving all other sockets

* would be needed.

*

* It was 128 by default. Experiments with real servers show, that

* it is absolutely not enough even at 100conn/sec. 256 cures most

* of problems. This value is adjusted to 128 for very small machines

* (<=32Mb of memory) and to 1024 on normal or better ones (>=256Mb).

* Note : Dont forget somaxconn that may limit backlog too.

*/

int reqsk_queue_alloc(struct request_sock_queue *queue,

unsigned int nr_table_entries)

{

size_t lopt_size = sizeof(struct listen_sock);

struct listen_sock *lopt;

nr_table_entries = min_t(u32, nr_table_entries, sysctl_max_syn_backlog);

nr_table_entries = max_t(u32, nr_table_entries, 8);

nr_table_entries = roundup_pow_of_two(nr_table_entries + 1);

lopt_size += nr_table_entries * sizeof(struct request_sock *);

if (lopt_size > PAGE_SIZE)

lopt = __vmalloc(lopt_size,

GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO,

PAGE_KERNEL);

else

lopt = kzalloc(lopt_size, GFP_KERNEL);

if (lopt == NULL)

return -ENOMEM;

for (lopt->max_qlen_log = 3;

(1 << lopt->max_qlen_log) < nr_table_entries;

lopt->max_qlen_log++);

get_random_bytes(&lopt->hash_rnd, sizeof(lopt->hash_rnd));

rwlock_init(&queue->syn_wait_lock);

queue->rskq_accept_head = NULL;

lopt->nr_table_entries = nr_table_entries;

write_lock_bh(&queue->syn_wait_lock);

queue->listen_opt = lopt;

write_unlock_bh(&queue->syn_wait_lock);

return 0;

}

关键要看nr_table_entries变量,在reqsk_queue_alloc函数中nr_table_entries变成了无符号变量,可修改的,变化受限。

比如实际内核参数值为:

net.ipv4.tcp_max_syn_backlog = 65535

所传入的backlog(不大于net.core.somaxconn = 65535)为8102,那么// 取listen函数的backlog和sysctl_max_syn_backlog最小值,结果为8102

nr_table_entries = min_t(u32, nr_table_entries, sysctl_max_syn_backlog);

// 取nr_table_entries和8进行比较的最大值,结果为8102

nr_table_entries = max_t(u32, nr_table_entries, 8);

// 可看做 nr_table_entries*2,结果为8102*2=16204

nr_table_entries = roundup_pow_of_two(nr_table_entries + 1);

计算结果,max_qlen_log = 14

2.6.18内核中max_qlen_log的计算方法for (lopt->max_qlen_log = 6;

(1 << lopt->max_qlen_log) < sysctl_max_syn_backlog;

lopt->max_qlen_log++);

很显然,sysctl_max_syn_backlog参与了运算,sysctl_max_syn_backlog值很大的话会导致max_qlen_log值相对比也很大

若sysctl_max_syn_backlog=65535,那么max_qlen_log=16

2.6.18内核中半连接长度为2^16=65536

作为listen_sock结构定义了需要处理的处理半连接的队列元素个数为nr_table_entries,此例中为16204长度。/** struct listen_sock - listen state

*

* @max_qlen_log - log_2 of maximal queued SYNs/REQUESTs

*/

struct listen_sock {

u8 max_qlen_log;

/* 3 bytes hole, try to use */

int qlen;

int qlen_young;

int clock_hand;

u32 hash_rnd;

u32 nr_table_entries;

struct request_sock *syn_table[0];

};

经描述而知,2^max_qlen_log = 半连接队列长度qlen值。

再回头看看报告SYN flooding的函数:

net/ipv4/Tcp_ipv4.c#ifdef CONFIG_SYN_COOKIES

static void syn_flood_warning(struct sk_buff *skb)

{

static unsigned long warntime;

if (time_after(jiffies, (warntime + HZ * 60))) {

warntime = jiffies;

printk(KERN_INFO

"possible SYN flooding on port %d. Sending cookies.\n",

ntohs(tcp_hdr(skb)->dest));

}

}

#endif

被调用的处,已精简若干代码: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.

*/

// 判断半连接队列是否已满 && !0

if (inet_csk_reqsk_queue_is_full(sk) && !isn) {

#ifdef CONFIG_SYN_COOKIES

if (sysctl_tcp_syncookies) {

want_cookie = 1;

} else

#endif

goto drop;

}

/* 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.

*/

if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1)

goto drop;

req = inet_reqsk_alloc(&tcp_request_sock_ops);

if (!req)

goto drop;

......

if (!want_cookie)

TCP_ECN_create_request(req, tcp_hdr(skb));

if (want_cookie) {

#ifdef CONFIG_SYN_COOKIES

syn_flood_warning(skb);

req->cookie_ts = tmp_opt.tstamp_ok;

#endif

isn = cookie_v4_init_sequence(sk, skb, &req->mss);

} else if (!isn) {

......

}

......

}

判断半连接队列已满的函数很关键,可以看看运算法则:

include/net/Inet_connection_sock.h:static inline int inet_csk_reqsk_queue_is_full(const struct sock *sk)

{

return reqsk_queue_is_full(&inet_csk(sk)->icsk_accept_queue);

}

include/net/Rquest_sock.h:static inline int reqsk_queue_is_full(const struct request_sock_queue *queue)

{

// 向右移位max_qlen_log个单位

return queue->listen_opt->qlen >> queue->listen_opt->max_qlen_log;

}

返回1,自然表示半连接队列已满。

以上仅仅是分析了半连接队列已满的判断条件,总之应用程序所传入的backlog很关键,如值太小,很容易得到1.

若 somaxconn = 128,sysctl_max_syn_backlog = 4096,backlog = 511 则最终 nr_table_entries = 256,max_qlen_log = 8。那么超过256个半连接的队列,257 >> 8 = 1,队列已满。

如何设置backlog,还得需要结合具体应用程序,需要为其调用listen方法赋值。

Netty backlog处理

Tcp Server使用Netty 3.7 版本,版本较低,在处理backlog,若我们不手动指定backlog值,JDK 1.6默认为50。

有证如下: java.net.ServerSocket:public void bind(SocketAddress endpoint, int backlog) throws IOException {

if (isClosed())

throw new SocketException("Socket is closed");

if (!oldImpl && isBound())

throw new SocketException("Already bound");

if (endpoint == null)

endpoint = new InetSocketAddress(0);

if (!(endpoint instanceof InetSocketAddress))

throw new IllegalArgumentException("Unsupported address type");

InetSocketAddress epoint = (InetSocketAddress) endpoint;

if (epoint.isUnresolved())

throw new SocketException("Unresolved address");

if (backlog < 1)

backlog = 50;

try {

SecurityManager security = System.getSecurityManager();

if (security != null)

security.checkListen(epoint.getPort());

getImpl().bind(epoint.getAddress(), epoint.getPort());

getImpl().listen(backlog);

bound = true;

} catch(SecurityException e) {

bound = false;

throw e;

} catch(IOException e) {

bound = false;

throw e;

}

}

netty中,处理backlog的地方:

org/jboss/netty/channel/socket/DefaultServerSocketChannelConfig.java:@Override

public boolean setOption(String key, Object value) {

if (super.setOption(key, value)) {

return true;

}

if ("receiveBufferSize".equals(key)) {

setReceiveBufferSize(ConversionUtil.toInt(value));

} else if ("reuseAddress".equals(key)) {

setReuseAddress(ConversionUtil.toBoolean(value));

} else if ("backlog".equals(key)) {

setBacklog(ConversionUtil.toInt(value));

} else {

return false;

}

return true;

}

既然需要我们手动指定backlog值,那么可以这样做:bootstrap.setOption("backlog", 8102); // 设置大一些没有关系,系统内核会自动与net.core.somaxconn相比较,取最低值

小结

在linux内核2.6.32,若在没有遭受到SYN flooding攻击的情况下,可以适当调整:

sysctl -w net.core.somaxconn=32768

sysctl -w net.ipv4.tcp_max_syn_backlog=65535

sysctl -p

另千万别忘记修改TCP Server的listen接口所传入的backlog值,若不设置或者过小,都会有可能造成SYN flooding的警告信息。开始不妨设置成1024,然后观察一段时间根据实际情况需要再慢慢往上调。

无论你如何设置,最终backlog值范围为:

backlog <= net.core.somaxconn

半连接队列长度约为:

半连接队列长度 ≈ 2 * min(backlog, net.ipv4.tcpmax_syn_backlog)

另,若出现SYN flooding时,此时TCP SYN_RECV数量表示半连接队列已经满,可以查看一下:ss -ant | awk 'NR>1 {++s[$1]} END {for(k in s) print k,s[k]}'

感谢运维书坤小伙提供的比较好用查看命令。

java roundup函数_随手记之Linux 2.6.32内核SYN flooding警告信息相关推荐

  1. linux 查看syn网络日志,随手记之Linux内核SYN flooding警告信息

    前言 最近线上服务器,dmesg会给出一些警告信息: possible SYN flooding on port 8080. Sending cookies. 初看以为是受到DOS拒绝性攻击,但仔细一 ...

  2. java main函数_一行JAVA代码如何运行起来?

    在程序员的世界中,你总会听到一句"PHP是世界上最好的语言"的调侃.然而在你进入软件程序开发之后,你会发现即使开发语言千千万,最盛行的还是JAVA.从淘宝的技术变迁中我们可以见一些 ...

  3. java peek函数_基础篇:JAVA.Stream函数,优雅的数据流操作

    写在开头:本文是转载于掘金上的一篇文章,已获得原作者授权,我会在文章最后放上原作者和原文链接. 前言 平时操作集合数据,我们一般都是for或者iterator去遍历,不是很好看.java提供了Stre ...

  4. java 匿名函数_国外程序员用的火热的Vavr是什么鬼?让函数式编程更简单!

    引言 相信很多人关注 Vavr 的原因,还是因为 Hystrix 库.Hystrix 不更新了,并在 GitHub 主页上推荐了 Resilience4j,而 Vavr 作为 Resilience4j ...

  5. java roundup函数_Excel函数(2)if、rand、round函数

    阅读提示:阅读对象:完全不了解这些函数,和对这几个函数有初步了解想要进一步深入的 建议阅读时间:10分钟 知识点:if.if嵌套.AND OR.iferror.rand.randbetween.abs ...

  6. java length()函数_小猿圈介绍java函数式编码结构及优势

    对于java大家都已经不陌生了吧,今天小猿圈Java讲师就分享一篇关于java函数式编码结构及优势的知识点,希望对于学习java的你有一定的帮助,想学习就需要积累. 探讨三种下一代JVM语言:Groo ...

  7. java string()函数_从Java中的String函数返回String构建器?

    我有以下程序,我必须将字符串附加到另一个字符串,我使用字符串构建器以标准方式执行.但是,即使在将其转换为toString()之后,该函数也不允许我返回ab.我想问为什么? import java.ut ...

  8. java arraylist 函数_使用Java-8中的函数ArrayList

    问题描述: 我希望能够使用从另一个类传入的函数的ArrayList(其中函数已在其他类中定义).如果在一个类中定义了可能具有不同输入和返回类型的函数列表,我希望能够将其中一些的ArrayList(可能 ...

  9. java lambda函数_最常用的 Java 8 中的 Lambda 函数(项目中实用笔记)

    最常用的 Java 8 中的 Lambda 函数(项目中实用笔记) 简介 Java 8 中的新特性,虽然现在都出到了Java14版本,不过在日常的开发过程中,8的版本是足够使用了,再说现在的8以上的版 ...

  10. java string()函数_转载java String.split()函数的用法详解

    转载java String.split()函数的用法详解 如果您发现本文排版有问题,可以先点击下面的链接切换至老版进行查看!!!在java.lang包中有String.split()方法的原型是: p ...

最新文章

  1. mysql yum多实例_centos-7yum 安装 (mairadb) 实现 mysql 多实例
  2. 神经网络最容易忽略的问题影响了网络的性能(图像识别)
  3. 动态链接库dll的两种加载方式
  4. lesson2-python3数据类型
  5. POJ1003/1004/1005/1207/3299/2159/1083/3094/2388解题(刷一波水题)
  6. java spit 点_java split 的一些用法注意点。
  7. 在钢筋混泥土的城市,打铁还需身体硬
  8. Android之单元测试
  9. 通过命令运行jar包(指定外部依赖jar包)
  10. 黑胶唱片的“另类”用途
  11. 实验二 同步时序方式设计_秒表
  12. 我总是那么傻,爱的那么真,最后伤了自己:情感日志
  13. c语言 switch案例,C语言switch语句实例
  14. flutter限制数字输入
  15. 入职体检时为什么要检查血常规
  16. Echarts之饼图内外圆重叠且图例不统一
  17. vb+flash 开发常见问题之--保存时出现错误:“系统错误H80004005(-2147467259),未指定的错误”
  18. C++ 和 EasyX绘制心形代码
  19. 基于python3的Opencv图像处理教程(从零到实践)(贾志刚)pdf笔记、代码
  20. 计算机毕业论文答辩评语,关于论文答辩评语30篇

热门文章

  1. Android手机截图怎么做,手机截屏怎么弄,详细教您手机截图方法
  2. tbody 不能充满table的原因
  3. java如何导出excel_JAVA如何导出EXCEL表格
  4. Godaddy域名注册详细图文教程(转)
  5. Godaddy域名被盗诉讼全过程 法院起诉成功拿回域名
  6. 集体的智慧:为什么SaaS打败企业软件?
  7. 使用sh_metutil生成采样300秒的ztd
  8. 如何查看有关计算机系统类型,电脑系统类型在哪查看
  9. python转码时出现'illegal multibyte sequen'错误
  10. 一零四五、FAILED: SemanticException [Error 10293]: Unable to create temp file for insert values Expressio