前言
最近线上服务器,dmesg会给出一些警告信息:
possible SYN flooding on port 8080. Sending cookies.
初看以为是受到DOS拒绝性攻击,但仔细一分析,一天量也就是在1000多条左右,感觉上属于正常可接受范围。
下面需要找出来源,以及原因,以下内容基于Linux 2.6.18内核。
警告输出源头
net/ipv4/Tcp_ipv4.c:
#ifdef CONFIG_SYN_COOKIES
static void syn_flood_warning(struct sk_buff *skb)
{
static unsigned long warntime; // 第一次加载初始化为零,后续warntime = jiffies
if (time_after(jiffies, (warntime + HZ * 60))) {
warntime = jiffies;
printk(KERN_INFO
"possible SYN flooding on port %d. Sending cookies.\n",
ntohs(skb->h.th->dest));
}
}
#endif
很显然,CONFIG_SYN_COOKIES在Linux系统编译时,已被设置true。
time_after宏定义:
#define time_after(a,b) \
(typecheck(unsigned long, a) && \
typecheck(unsigned long, b) && \
((long)(b) - (long)(a) < 0))
两个无符号的时间比较,确定先后顺序。
jiffies真身:
# define jiffies raid6_jiffies()
#define HZ 1000
......
static inline uint32_t raid6_jiffies(void)
{
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec*1000 + tv.tv_usec/1000; // 秒*1000 + 微秒/1000
}
回过头来,再看看syn_flood_warning函数:
static void syn_flood_warning(struct sk_buff *skb)
{
static unsigned long warntime; // 第一次加载初始化为零,后续warntime = jiffies
if (time_after(jiffies, (warntime + HZ * 60))) {
warntime = jiffies;
printk(KERN_INFO
"possible SYN flooding on port %d. Sending cookies.\n",
ntohs(skb->h.th->dest));
}
}
warntime为static类型,第一次调用时被初始化为零,下次调用就是上次的jiffies值了,前后间隔值超过HZ*60就不会输出警告信息了。
有关time_after和jiffies,分享几篇文章:
http://wenku.baidu.com/view/c75658d480eb6294dd886c4e.html
http://www.360doc.com/content/11/1201/09/1317564_168810003.shtml
警告输出需要满足的条件
注意观察want_cookie=1时的条件。
net/ipv4/Tcp_ipv4.c:
int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
{
struct inet_request_sock *ireq;
struct tcp_options_received tmp_opt;
struct request_sock *req;
__u32 saddr = skb->nh.iph->saddr;
__u32 daddr = skb->nh.iph->daddr;
__u32 isn = TCP_SKB_CB(skb)->when; // when在tcp_v4_rcv()中会被置为0
struct dst_entry *dst = NULL;
#ifdef CONFIG_SYN_COOKIES
int want_cookie = 0;
#else
#define want_cookie 0 /* Argh, why doesn't gcc optimize this :( */
#endif
/* Never answer to SYNs send to broadcast or multicast */
if (((struct rtable *)skb->dst)->rt_flags &
(RTCF_BROADCAST | RTCF_MULTICAST))
goto drop;
/* TW buckets are converted to open requests without
* limitations, they conserve resources and peer is
* evidently real one.
*/
// if(判断半连接队列已满 && !0)
if (inet_csk_reqsk_queue_is_full(sk) && !isn) {
#ifdef CONFIG_SYN_COOKIES
if (sysctl_tcp_syncookies) { // net.ipv4.tcp_syncookies = 1
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(连接队列是否已满 && 半连接队列中还有未重传ACK半连接数字 > 1)
if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1)
goto drop;
......
tcp_openreq_init(req, &tmp_opt, skb);
ireq = inet_rsk(req);
ireq->loc_addr = daddr;
ireq->rmt_addr = saddr;
ireq->opt = tcp_v4_save_options(sk, skb);
if (!want_cookie)
TCP_ECN_create_request(req, skb->h.th);
if (want_cookie) { // 半连接队列已满会触发
#ifdef CONFIG_SYN_COOKIES
syn_flood_warning(skb);
#endif
isn = cookie_v4_init_sequence(sk, skb, &req->mss);
} else if (!isn) {
......
}
/* Kill the following clause, if you dislike this way. */
// net.ipv4.tcp_syncookies未设置情况下,sysctl_max_syn_backlog发生的作用
else if (!sysctl_tcp_syncookies &&
(sysctl_max_syn_backlog - inet_csk_reqsk_queue_len(sk) <
(sysctl_max_syn_backlog >> 2)) &&
(!peer || !peer->tcp_ts_stamp) &&
(!dst || !dst_metric(dst, RTAX_RTT))) {
/* Without syncookies last quarter of
* backlog is filled with destinations,
* proven to be alive.
* It means that we continue to communicate
* to destinations, already remembered
* to the moment of synflood.
*/
LIMIT_NETDEBUG(KERN_DEBUG "TCP: drop open "
"request from %u.%u.%u.%u/%u\n",
NIPQUAD(saddr),
ntohs(skb->h.th->source));
dst_release(dst);
goto drop_and_free;
}
isn = tcp_v4_init_sequence(sk, skb);
}
tcp_rsk(req)->snt_isn = isn;
if (tcp_v4_send_synack(sk, req, dst))
goto drop_and_free;
if (want_cookie) {
reqsk_free(req);
} else {
inet_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT);
}
return 0;
drop_and_free:
reqsk_free(req);
drop:
return 0;
}
小结
总之,如系统出现:
possible SYN flooding on port 8080. Sending cookies.
若量不大,是在提醒你需要关心一下sysctl_max_syn_backlog其值是否过低:
sysctl -a | grep 'max_syn_backlog'
不妨成倍增加一下
sysctl -w net.ipv4.tcp_max_syn_backlog=8192
sysctl -p
若进程无法做到重新加载,那就需要重启应用,以适应新的内核参数。进而持续观察一段时间。
貌似tcp_max_syn_backlog参数其完整作用域还没有理解完整,下次有时间再写吧。