聂永的博客

记录工作/学习的点点滴滴。

随手记之Linux内核SYN flooding警告信息

前言

最近线上服务器,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参数其完整作用域还没有理解完整,下次有时间再写吧。

posted on 2014-08-06 21:57 nieyong 阅读(6770) 评论(5)  编辑  收藏 所属分类: Socket

评论

# re: 随手记之Linux内核SYN flooding警告信息 2014-08-07 09:06 金利锁业

谢谢博主分享,,,,,,  回复  更多评论   

# re: 随手记之Linux内核SYN flooding警告信息 2014-08-08 17:18 用户体验

行家一出手就只有没有,用户体验不错.  回复  更多评论   

# re: 随手记之Linux内核SYN flooding警告信息 2014-08-09 11:28 金利锁业

支持博主分享,。  回复  更多评论   

# re: 随手记之Linux内核SYN flooding警告信息 2014-08-23 11:46 好邻居官网

谢谢指教了~感谢分享了!  回复  更多评论   

# re: 随手记之Linux内核SYN flooding警告信息 2014-08-25 09:39 喜欢研究的人类

以前在
http://www.copyright8.com/
有介绍,现在怎么变成了读后感的网站?我晕  回复  更多评论   


只有注册用户登录后才能发表评论。


网站导航:
 

公告

所有文章皆为原创,若转载请标明出处,谢谢~

新浪微博,欢迎关注:

导航

<2014年8月>
272829303112
3456789
10111213141516
17181920212223
24252627282930
31123456

统计

常用链接

留言簿(58)

随笔分类(130)

随笔档案(151)

个人收藏

最新随笔

搜索

最新评论

阅读排行榜

评论排行榜