SYN Flood
下面这段介绍引用自[1].
SYN Flood是一种非常危险而常见的Dos攻击方式。到目前为止,能够有效防范SYN Flood攻击的手段并不多,
SYN Cookie就是其中最著名的一种。
SYN Flood攻击是一种典型的拒绝服务(Denial of Service)攻击。所谓的拒绝服务攻击就是通过进行攻击,使受害主机或
网络不能提供良好的服务,从而间接达到攻击的目的。
SYN Flood攻击利用的是IPv4中TCP协议的三次握手(Three-Way Handshake)过程进行的攻击。
TCP服务器收到TCP SYN request包时,在发送TCP SYN + ACK包回客户机前,TCP服务器要先分配好一个数据区专门
服务于这个即将形成的TCP连接。一般把收到SYN包而还未收到ACK包时的连接状态称为半打开连接(Half-open Connection)。
在最常见的SYN Flood攻击中,攻击者在短时间内发送大量的TCP SYN包给受害者。受害者(服务器)为每个TCP SYN包分配
一个特定的数据区,只要这些SYN包具有不同的源地址(攻击者很容易伪造)。这将给TCP服务器造成很大的系统负担,最终
导致系统不能正常工作。
SYN Cookie
Daniel J. Bernstein和Eric Schenk于 1996 年九月创造了这个SYN Cookie技术。Jeff Weisberg在一个月后发布了最早的实现(在SunOS上),Eric Schenk随后在 1997 年二月发布了他的Linux实现(目前的实现使用,例如net.ipv4.tcp_syncookies)。技术。Jeff Weisberg在一个月后发布了最早的实现(在SunOS上),Eric Schenk随后在 1997 年二月发布了他的Linux实现(目前的实现使用,例如net.ipv4.tcp_syncookies)。
SYN Cookie是对TCP服务器端的三次握手做一些修改,专门用来防范SYN Flood攻击的一种手段。它的原理是,在TCP服务器
接收到TCP SYN包并返回TCP SYN + ACK包时,不分配一个专门的数据区,而是根据这个SYN包计算出一个cookie值。这个
cookie作为将要返回的SYN ACK包的初始序列号。当客户端返回一个ACK包时,根据包头信息计算cookie,与返回的确认序列
号(初始序列号 + 1)进行对比,如果相同,则是一个正常连接,然后,分配资源,建立连接。
实现的关键在于cookie的计算,cookie的计算应该包含本次连接的状态信息,使攻击者不能伪造。
cookie的计算:
服务器收到一个SYN包,计算一个消息摘要mac。
mac = MAC(A, k);
MAC是密码学中的一个消息认证码函数,也就是满足某种安全性质的带密钥的hash函数,它能够提供cookie计算中需要的安全性。
在Linux实现中,MAC函数为SHA1。
A = SOURCE_IP || SOURCE_PORT || DST_IP || DST_PORT || t || MSSIND
k为服务器独有的密钥,实际上是一组随机数。
t为系统启动时间,每60秒加1。
MSSIND为MSS对应的索引。
注:
TCP在三次握手建立连接过程中,会在SYN报文中使用MSS(Maximum Segment Size)选项功能,
协商交互双方能够接收的最大段长MSS值。
何时启动sync cookie?
只有在服务器的accept_queue满载时才会启用,可以看出懒惰启用对效率提升很有效,并且还可以有效防止syn-flood攻击,你可以随便攻击,这个linux不管,linux丝毫不会畏惧dos攻击,也不会提前采取任何行动,一切就像什么也没有发生一样,但是一旦攻击开始影响系统,这里就是接收队列已经爆满,linux就要应付了,启动应急预案,这里就是syn-cookie。
实现
发起一个 TCP 连接时,客户端将一个 TCP SYN 包发送给服务器。作为响应,服务器将 TCP SYN + ACK 包返回给客户端。此数据包中有一个序号,它被 TCP 用来重新组装数据流。根据 TCP 规范,由端点发送的第一个序号可以是由该端点决定的任何值。SYN Cookies 是根据以下规则构造的初始序号:
[1]
-
令
t为一个缓慢递增的时间戳(通常为
time()
>>6,提供 64 秒的分辨率);
[1]
-
令
m为服务器会在 SYN 队列条目中存储的
最大分段大小(Maximum segment size);
[1]
-
令
s为一个加密
散列函数对服务器和客户端各自的 IP 地址和端口号以及
t进行运算的结果。返回得到的数值
s必须是一个24位值。
[1]
初始 TCP 序号,也就是所谓的
SYN cookie,按照如下算法得到:
[1]
注:由于
m必须用 3 位进行编码,服务器在启用了 SYN Cookie 时只能为
m发送八种不同的数值。
[1]
根据 TCP 规范,当客户端发回 TCP ACK 包给服务器以响应服务器的 SYN + ACK 包时,客户端必须使用由服务器发送的初始序号加1作为数据包中的确认号。服务器接着从确认号中减去 1 以便还原向客户端发送的原始 SYN Cookie。
[1]
-
-
重新计算
s来确认这是不是一个有效的 SYN Cookie。
[1]
-
从 3 位编码中解码
m,以便之后用来重建 SYN 队列条目。在此之后,连接照常进行。
[1]
下面是代码实现细节说明:
(1)启用条件
判断是否使用SYN Cookie。如果SYN Cookie功能有编译进内核(CONFIG_SYN_COOKIE),且选项
tcp_syncookies不为0,那么可使用SYN Cookie。同时设置SYN Flood标志(listen_opt->synflood_warned)。
-
- bool tcp_syn_flood_action(struct sock *sk, const struct sk_buff *skb, const char *proto)
- {
- const char *msg = "Dropping request";
- bool want_cookie = false;
- struct listen_sock *lopt;
-
- #ifdef CONFIG_SYN_COOKIE
- if (sysctl_tcp_syncookies) {
- msg = "Sending cookies";
- want_cookie = true;
- NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPREQQFULLDOCOOKIES);
- } else
- #endif
- NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPREQQFULLDROP);
-
- lopt = inet_csk(sk)->icsk_accept_queue.listen_opt;
-
- if (! lopt->synflood_warned) {
- lopt->synflood_warned = 1;
- pr_info("%s: Possible SYN flooding on port %d. %s. Check SNMP counters.\n",
- proto, ntohs(tcp_hdr(skb)->dest), msg);
- }
-
- return want_cookie;
- }
(2)生成cookie
计算SYN Cookie的值。
函数调用路径:
tcp_v4_conn_request
|--> cookie_v4_init_sequence
|--> secure_tcp_syn_cookie
-
-
-
-
- __u32 cookie_v4_init_sequence(struct sock *sk, struct sk_buff *skb, __u16 *mssp)
- {
- const struct iphdr *iph = ip_hdr(skb);
- const struct tcphdr *th = tcp_hdr(skb);
- int mssind;
- const __u16 mss = *mssp;
-
- tcp_synq_overflow(sk);
-
- for (mssind = ARRAY_SIZE(msstab) - 1; mssind; mssind--)
- if (mss >= msstab[mssind])
- break;
- *mssp = msstab[mssind];
-
- NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_SYNCOOKIESSENT);
-
- return secure_tcp_syn_cookie(iph->saddr, iph->daddr, th->source, th->dest, ntohl(th->seq),
- jiffies / (HZ * 60), mssind);
- }
-
- static inline void tcp_synq_overflow(struct sock *sk)
- {
- tcp_sk(sk)->rx_opt.ts_recent_stamp = jiffies;
- }
-
-
-
-
-
-
-
-
-
- static __u16 const msstab[] = {
- 64,
- 512,
- 536,
- 1024,
- 1440,
- 1460,
- 4312,
- 8960,
- };
- static __u32 secure_tcp_syn_cookie(__be32 saddr, __be32 daddr, __be16 sport, __be16 dport,
- __u32 sseq, __u32 count, __u32 data)
- {
-
-
-
-
-
-
-
- return (cookie_hash(saddr, daddr, sport, dport, 0, 0) + sseq + (count << COOKIEBITS) +
- ((cookie_hash(saddr, daddr, sport, dport, count, 1) + data) & COOKIEMASK));
-
- }
-
- #define COOKIEBITS 24
- #define COOKIEMASK (((__u32) 1 << COOKIEBITS) - 1)
- #define SHA_DIGEST_WORDS 5
- #define SHA_WORKSPACE_WORDS 16
服务器的密钥、SHA1计算。
- __u32 syncookie_secret[2] [16 - 4 + SHA_DIGEST_WORDS];
-
- static __init int init_syncookies(void)
- {
- get_random_bytes(syncookie_secret, sizeof(syncookie_secret));
- return 0;
- }
-
- static DEFINE_PER_CPU(__u32 [16 + 5 + SHA_WORKSPACE_WORDS], ipv4_cookie_scratch);
-
- static u32 cookie_hash(__be32 saddr, _be32 daddr, __be16 sport, __be16 dport, u32 count, int c)
- {
- __u32 *tmp = __get_cpu_var(ipv4_cookie_scratch);
-
- memcpy(tmp + 4, syncookie_secret[c], sizeof(syncookie_secret[c]));
- tmp[0] = (__force u32) saddr;
- tmp[1] = (__force u32) daddr;
- tmp[2] = ((__force u32) sport << 16) + (__force u32) dport;
- tmp[3] = count;
-
- sha_transform(tmp + 16, (__u8 *)tmp, tmp + 16 + 5);
- return tmp[17];
- }
SHA1
安全哈希算法(Secure HASH Algorithm)主要适用于数字签名。
对于长度小于2^64位的消息,SHA1会产生一个160位的消息摘要。当接收到消息的时候,这个消息摘要可以用来
验证数据的完整性。在传输的过程中,数据可能会发生变化,那么这时候就会产生不同的消息摘要。
SHA1有如下特性:
1. 不可以从消息摘要中复原信息。
2. 两个不同的消息不会产生同样的消息摘要。
在Git中,也使用SHA1来标识每一次提交。
- /* sha_transform - single block SHA1 transform
- * @digest: 160 bit digest to update
- * @data: 512 bits of data to hash
- * @array: 16 words of workspace (see note)
- *
- * This function generates a SHA1 digest for a single 512-bit block.
- * /
- void sha_transform(__u32 *digest, const char *data, __u32 *array) {}
(3)保存TCP选项信息
tcp_v4_send_synack
|--> tcp_make_synack
|--> cookie_init_timestamp
如果SYNACK段使用SYN Cookie,并且使用时间戳选项,则把TCP选项信息保存在SYNACK段中tsval的低6位。
-
-
-
-
-
- __u32 cookie_init_timestamp(struct request_sock *req)
- {
- struct inet_request_sock *ireq;
- u32 ts, ts_now = tcp_time_stamp;
- u32 options = 0;
- ireq = inet_rsk(req);
-
- options = ireq->wscale_ok ? ireq->snd_wscale : 0xf;
- options |= ireq->sack_ok << 4;
- options |= ireq->ecn_ok << 5;
-
- ts = ts_now & ~TSMASK;
- ts |= options;
-
- if (ts > ts_now) {
- ts >>= TSBITS;
- ts--;
- ts <<= TSBITS;
- ts |= options;
- }
- return ts;
- }
-
- #define TSBITS 6
- #define TSMASK (((__u32) 1 << TSBITS) - 1)
(4)验证cookie
函数调用路径:
tcp_v4_hnd_req
|--> cookie_v4_check
|--> cookie_check
|--> check_tcp_syn_cookie
SYN Cookie的设计非常巧妙, 我们来看看它是怎么验证的。
首先,把ACK包的ack_seq - 1,得到原来计算的cookie。把ACK包的seq - 1,得到SYN段的seq。
cookie的计算公式为:
cookie = cookie_hash(saddr, daddr, sport, dport, 0, 0) + seq +
(t1 << 24) + (cookie_hash(saddr, daddr, sport, dport, t1, 1) + mssind) % 24;
t1为服务器发送SYN Cookie的时间,单位为分钟,保留在高12位。
mssind为MSS的索引(0 - 7),保留在低24位。
现在可以反过来求t1:
t1 = (cookie - cookie_hash(saddr, daddr, sport, dport, 0, 0) - seq) >> 24; /* 高12位表示时间 */
t2为收到ACK的时间,t2 - t1 < 4分钟,才是合法的。也就是说ACK必须在4分钟内到达才行。
验证完时间后,还需验证mssind:
cookie -= (cookie_hash(saddr, daddr, sport, dport, 0, 0) - seq);
mssind = (cookie - cookie_hash(saddr, daddr, sport, dport, t1, 1)) % 24; /* 低24位 */
mssind < 8,才是合法的。
如果t1和mssind都是合法的,则认为此ACK是合法的,可以直接完成三次握手。
-
-
-
-
- static inline int cookie_check(struct sk_buff *skb, __u32 cookie)
- {
- const struct iphdr *iph = ip_hdr(skb);
- const struct tcphdr *th = tcp_hdr(skb);
- __u32 seq = ntohl(th->seq) - 1;
-
- __u32 mssind = check_tcp_syn_cookie(cookie, iph->saddr, iph->daddr, th->source, th->dest,
- seq, jiffies / (HZ * 60), COUNTER_TRIES);
-
-
- return mssind < ARRAY_SIZE(msstab) ? msstab[mssind] : 0;
- }
-
-
-
-
-
-
- #define COUNTER_TRIES 4
-
- static __u32 check_tcp_syn_cookie(__u32 cookie, __be32 saddr, __be32 daddr, __be16 sport,
- __be16 dport, __u32 sseq, __u32 count, __u32 maxdiff)
- {
- __u32 diff;
-
-
- cookie -= cookie_hash(saddr, daddr, sport, dport, 0, 0) + sseq;
-
-
- diff = (count - (cookie >> COOKIEBITS)) & ((__u32) -1 >> COOKIEBITS);
- if (diff >= maxdiff)
- return (__u32)-1;
-
-
- return (cookie - cookie_hash(saddr, daddr, sport, dport, count - diff, 1)) & COOKIEMASK;
- }
(5)建立连接
接收到ACK后,SYN Cookie的处理函数为cookie_v4_check()。
首先要验证cookie是否合法。
如果cookie是不合法的,返回监听sk,会导致之后发送一个RST给客户端。
如果cookie是合法的,则创建和初始化连接请求块。接着为新的连接创建和初始化一个新的传输控制块,
把它和连接请求块关联起来,最后把该连接请求块链入全连接队列中,等待accept()。
时间戳对SYN Cookie有着重要的意义,如果不支持时间戳选项,则通过SYN Cookie建立的连接就会
不支持大多数TCP选项。
- struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb, struct ip_options *opt)
- {
- struct tcp_options_received tcp_opt;
- const u8 *hash_location;
- struct inet_request_sock *ireq;
- struct tcp_request_sock *treq;
- struct tcp_sock *tp = tcp_sk(sk);
- const struct tcphdr *th = tcp_hdr(skb);
- __u32 cookie = ntohl(th->ack_seq) - 1;
- struct sock *ret = sk;
- struct request_sock *req;
- int mss;
- struct rtable *rt;
- __u8 rcv_wscale;
- bool ecn_ok = false;
- struct flowi4 fl4;
-
- if (! sysctl_tcp_syncookies || ! th->ack || th->rst)
- goto out;
-
-
-
-
-
- if (tcp_synq_no_recent_overflow(sk) || (mss = cookie_check(skb, cookie)) == 0) {
- NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_SYNCOOKIESFAILED);
- goto out;
- }
- NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_SYNCOOKIESRECV);
-
-
- memset(&tcp_opt, 0, sizeof(tcp_opt));
-
-
- tcp_parse_options(skb, &tcp_opt, &hash_location, 0, NULL);
-
-
- if (! cookie_check_timestamp(&tcp_opt, &ecn_ok))
- goto out;
-
- ret = NULL;
-
- req = inet_reqsk_alloc(&tcp_request_sock_ops);
- if (! req)
- goto out;
-
- ireq = inet_rsk(req);
- treq = tcp_rsk(req);
- treq->rcv_isn = ntohl(th->seq) - 1;
- treq->snt_isn = cookie;
- req->mss = mss;
- ireq->loc_port = th->dest;
- ireq->rmt_port = th->source;
- ireq->loc_addr = ip_hdr(skb)->daddr;
- ireq->rmt_addr = ip_hdr(skb)->saddr;
- ireq->ecn_ok = ecn_ok;
- ireq->snd_wscale = tcp_opt.snd_wscale;
- ireq->sack_ok = tcp_opt.sack_ok;
- ireq->wscale_ok = tcp_opt.wscale_ok;
- ireq->tstamp_ok = tcp_opt.saw_tstamp;
- req->ts_recent = tcp_opt.saw_tstamp ? tcp_opt.rcv_tsval : 0;
- treq->snt_synack = tcp_opt.saw_tstamp ? tcp_opt.rcv_tsecr : 0;
-
-
-
-
-
- if (opt && opt->optlen) {
- int opt_size = sizeof(struct ip_options_rcu) + opt->optlen;
- ireq->opt = kmalloc(opt_size, GFP_ATOMIC);
-
- if (ireq->opt != NULL && ip_options_echo(&ireq->opt->opt, skb)) {
- kfree(ireq->opt);
- ireq->opt = NULL;
- }
- }
-
-
- if (security_inet_conn_request(sk, skb, req)) {
- reqsk_free(req);
- goto out;
- }
-
- req->expires = 0UL;
- req->retrans = 0;
-
-
-
-
-
-
- flowi4_init_output(&fl4, 0, sk->sk_mark, RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE,
- IPPROTO_TCP, inet_sk_flowi_flags(sk), (opt && opt->srr) ? opt->faddr : ireq->rmt_addr,
- ireq->loc_addr, th->source, th->dest);
- security_req_classify_flow(req, flowi4_to_flowi(&fl4));
- rt = ip_route_output_key(sock_net(sk), &fl4);
- if (IS_ERR(rt)) {
- reqsk_free(req);
- goto out;
- }
-
-
- req->window_clamp = tp->window_clamp ? : dst_metric(&rt->dst, RTAX_WINDOW);
-
-
- tcp_select_initial_window(tcp_full_space(sk), req->mss, &req->rcv_wnd, &req->window_clamp,
- ireq->wscale_ok, &rcv_wscale, dst_metric(&rt->dst, RTAX_INITRWND));
- ireq->rcv_wscale = rcv_wscale;
-
-
-
-
-
- ret = get_cookie_sock(sk, skb, req, &rt->dst);
-
-
-
-
- if (ret)
- inet_sk(ret)->cork.fl.u.ip4 = fl4;
-
- out:
- return ret;
- }
-
-
-
-
-
- #define TCP_TIMEOUT_FALLBACK ((unsigned) (3 * HZ))
-
-
-
-
- static inline bool tcp_synq_no_recent_overflow(const struct sock *sk)
- {
- unsigned long last_overflow = tcp_sk(sk)->rx_opt.ts_recent_stamp;
- return time_after(jiffies, last_overflow + TCP_TIMEOUT_FALLBACK);
- }
如果SYNACK段使用SYN Cookie,并且使用时间戳选项,则把TCP选项信息保存在SYNACK段中tsval的低6位。
所以,现在收到ACK后,可以从ACK段的tsecr中提取出这些选项。
-
-
-
-
-
-
-
- bool cookie_check_timestamp(struct tcp_options_received *tcp_opt, bool *ecn_ok)
- {
-
- u32 options = tcp_opt->rcv_tsecr & TSMASK;
-
-
-
-
- if (! tcp_opt->saw_tstamp) {
- tcp_clear_options(tcp_opt);
- return true;
- }
-
- if (! sysctl_tcp_timestamps)
- return false;
-
- tcp_opt->sack_ok = (options & (1 << 4)) ? TCP_SACK_SEEN : 0;
- *ecn_ok = (options >> 5) & 1;
-
- if (*ecn_ok && ! sysctl_tcp_ecn)
- return false;
-
- if (tcp_opt->sack_ok && ! sysctl_tcp_sack)
- return false;
-
- if ((options & 0xf) == 0xf)
- return true;
-
- tcp_opt->wscale_ok = 1;
- tcp_opt->snd_wscale = options & 0xf;
- return sysctl_tcp_window_scaling != 0;
- }
为新的连接创建和初始化一个传输控制块,然后把完成三次握手的req和新sock关联起来,
并把该连接请求块移入全连接队列中。
- static inline struct sock *get_cookie_sock(struct sock *sk, struct sk_buff *skb,
- struct request_sock *req, struct dst_entry *dst)
- {
- struct inet_connection_sock *icsk = inet_csk(sk);
- struct sock *child;
-
-
-
-
- child = icsk->icsk_af_ops->syn_recv_sock(sk, skb, req, dst);
-
- if (child)
-
- inet_csk_reqsk_queue_add(sk, req, child);
- else
- reqsk_free(req);
-
- return child;
- }
-
- static inline void inet_csk_reqsk_queue_add(struct sock *sk, struct request_sock *req, struct sock *child)
- {
- reqsk_queue_add(&inet_csk(sk)->icsk_accept_queue, req, sk, child);
- }
把完成三次握手的连接请求块,和新的sock关联起来,并把它移入全连接队列中,等待被accept()。
- static inline void reqsk_queue_add(struct request_sock_queue *queue, struct request_sock *req,
- struct sock *parent, struct sock *child)
- {
- req->sk = child;
- sk_acceptq_added(parent);
-
-
- if (queue->rskq_accept_head == NULL)
- queue->rskq_accept_head = req;
- else
- queue->rskq_accept_tail->dl_next = req;
-
- queue->rskq_accept_tail = req;
- req->dl_next = NULL;
- }
-
- static inline void sk_acceptq_added(struct sock *sk)
- {
- sk->sk_ack_backlog++;
- }
评价
SYN Cookie技术由于在建立连接的过程中不需要在服务器端保存任何信息,实现了无状态的三次握手,从而有效的
防御了SYN Flood攻击。但是该方法也存在一些弱点。由于cookie的计算只涉及到包头部分信息,在建立连接的过程
中不在服务器端保存任何信息,所以失去了协议的许多功能,比如超时重传。此外,由于计算cookie有一定的运算量,
增加了连接建立的延迟时间,因此,SYN Cookie技术不能作为高性能服务器的防御手段。通常采用动态资源分配机制,
当分配了一定的资源后再采用cookie技术,linux就是这样实现的。还有一个问题是,当我们避免了SYN Flood攻击的
同时,也提供了另一种拒绝服务攻击方式,攻击者发送大量的ACK报文,服务器忙于计算验证。尽管如此,在预防
SYN Flood供给方面,SYN Cookie技术仍然是有效的(引用自[1])。
SYN Cookies 的使用不与任何协议定义冲突,照理来说它该和所有的 TCP 实现兼容。然而,当 SYN Cookies 使用的时候,会发生两种值得注意的变化:首先,服务器只能编码八种 MSS 数值,因为只有 3 位二进制空间可用。其次,这个服务器必须拒绝所有的TCP 选用项,例如大型窗口和时间戳,因为服务器会在信息被用其他方式存储时丢弃 SYN 队列条目。
[1]
尽管这些限制将不可避免地导致一个不如最佳的体验,它们的效果很少被客户端注意到——这些改变只在被攻击时值得注意。在这样的情况下,牺牲 TCP 选项来保护连接一般被认为是合乎情理的。
[1]
Linux内核从 2.6.26 版本开始为 TCP 选用项加入了有限的支持,通过把它们编码在时间戳内实现。
[1]
较新的TCP Cookie 传输(TCPCT)标准被设计用来克服 SYN Cookies 的这些问题,并且在各种方面改进这套机制。不像 SYN Cookies,TCPCT 是一个 TCP 拓展并且要求两端点都支持 TCPCT。
扩展
Linux内核中的SYN Cookie机制主要的功能是防止本机遭受SYN Flood攻击。
SYN Cookie Firewall利用SYN Cookie的原理,在内网和外网之间实现TCP三次握手过程的代理(proxy)。
一些SYN攻击的防火墙也是基于SYN Cookie,只是把这个功能移动到内核之外的代理服务器上。
原理图示如下:
Reference
[1]. https://www.ibm.com/developerworks/cn/linux/l-syncookie/
后记:
DoS攻击是一个永恒的问题,虽然专业厂商的防火墙,负载均衡类的网关设备能比较有效的防御DoS攻击,但黑客们更倾向于x86+GNU/Linux的组合,原因很简单:足够的廉价。
在linux内核 V3.13里终于加入了SYNPROXY的新功能,见下面的链接, 这个模块是一个基于链接跟踪的netfilter扩展,主要干的工作就是把来自客户端的初始SYN包标记成UNTRACKED然后直接导入 iptables的"SYNPROXY"的动作(类似ACCEPT,NFQUEUE和DROP),这时内核会扮演网关设备的角色继续跟客户端进行TCP的 常规握手流程,SYNPROXY会等到最终的ACK(三次握手)的cookie被验证合法后才会开始让包真正的进入目标端。
开发者Jesper Dangaard Brouer的数据表明SYNPROXY对 于对抗SYN FLOOD DOS攻击是非常有效的,笔者今天也在Debian和SLES-12-beta2对SYNPROXY进行了DoS测试,大致结果是在使用hping3和 metasploit进行测试,开启SYNPROXY后ksoftirq占用会从8%降低到3%以内,有兴趣的可以亲自去玩玩。
SynProxy的链接地址:
https://lwn.net/Articles/563151/