2008-02-04 23:19:21

by Glenn Griffin

[permalink] [raw]
Subject: [PATCH] Add IPv6 support to TCP SYN cookies

Add IPv6 support to TCP SYN cookies. This is written and tested against
2.6.24, and applies cleanly to linus' current HEAD (d2fc0b). Unfortunately
linus' HEAD breaks my sky2 card at the moment, so I'm unable to test against
that. I see no reason why it would be affected though. Comments/suggestions
are welcome.

Signed-off-by: Glenn Griffin <[email protected]>
---
include/net/tcp.h | 4 +
net/ipv4/syncookies.c | 203 ++++++++++++++++++++++++++++++++++++++++++++++++-
net/ipv6/tcp_ipv6.c | 77 +++++++++++++-----
3 files changed, 260 insertions(+), 24 deletions(-)

diff --git a/include/net/tcp.h b/include/net/tcp.h
index cb5b033..02dc6dd 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -435,6 +435,9 @@ extern struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb,
struct ip_options *opt);
extern __u32 cookie_v4_init_sequence(struct sock *sk, struct sk_buff *skb,
__u16 *mss);
+extern struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb);
+extern __u32 cookie_v6_init_sequence(struct sock *sk, struct sk_buff *skb,
+ __u16 *mss);

/* tcp_output.c */

@@ -1337,6 +1340,7 @@ extern int tcp_proc_register(struct tcp_seq_afinfo *afinfo);
extern void tcp_proc_unregister(struct tcp_seq_afinfo *afinfo);

extern struct request_sock_ops tcp_request_sock_ops;
+extern struct request_sock_ops tcp6_request_sock_ops;

extern int tcp_v4_destroy_sock(struct sock *sk);

diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c
index 2da1be0..b342bae 100644
--- a/net/ipv4/syncookies.c
+++ b/net/ipv4/syncookies.c
@@ -3,6 +3,7 @@
*
* Copyright (C) 1997 Andi Kleen
* Based on ideas by D.J.Bernstein and Eric Schenk.
+ * IPv6 Support Added by Glenn Griffin (2008)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -10,8 +11,6 @@
* 2 of the License, or (at your option) any later version.
*
* $Id: syncookies.c,v 1.18 2002/02/01 22:01:04 davem Exp $
- *
- * Missing: IPv6 support.
*/

#include <linux/tcp.h>
@@ -19,6 +18,7 @@
#include <linux/random.h>
#include <linux/cryptohash.h>
#include <linux/kernel.h>
+#include <net/ipv6.h>
#include <net/tcp.h>

extern int sysctl_tcp_syncookies;
@@ -281,3 +281,202 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb,
ret = get_cookie_sock(sk, skb, req, &rt->u.dst);
out: return ret;
}
+
+/* IPv6 Implementation
+ * Just a reimplementation of the above IPv4 implementation adjusting for
+ * the longer address length. Could optionally add an additional addrlen
+ * argument to most of the above functions.
+ *
+ * Reference the code comments above to understand what is going on
+ */
+
+static u32 cookie_hash6(__be32 *saddr, __be32 *daddr, __be16 sport,
+ __be16 dport, u32 count, int c)
+{
+ __u32 tmp[16 + 5 + SHA_WORKSPACE_WORDS];
+
+ /*
+ * we have 320 bits of information to hash, copy in the remaining
+ * 192 bits required for sha_transform, from the syncookie_secret
+ * and overwrite the digest with the secret
+ */
+ memcpy(tmp + 10, syncookie_secret[c], 44);
+ memcpy(tmp, saddr, 16);
+ memcpy(tmp + 4, daddr, 16);
+ tmp[8] = ((__force u32)sport << 16) + (__force u32)dport;
+ tmp[9] = count;
+ sha_transform(tmp + 16, (__u8 *)tmp, tmp + 16 + 5);
+
+ return tmp[17];
+}
+
+static __u32 secure_tcp_syn_cookie6(__be32 *saddr, __be32 *daddr, __be16 sport,
+ __be16 dport, __u32 sseq, __u32 count,
+ __u32 data)
+{
+ return (cookie_hash6(saddr, daddr, sport, dport, 0, 0) +
+ sseq + (count << COOKIEBITS) +
+ ((cookie_hash6(saddr, daddr, sport, dport, count, 1) + data)
+ & COOKIEMASK));
+}
+
+static __u32 check_tcp_syn_cookie6(__u32 cookie, __be32 *saddr, __be32 *daddr,
+ __be16 sport, __be16 dport, __u32 sseq,
+ __u32 count, __u32 maxdiff)
+{
+ __u32 diff;
+
+ cookie -= cookie_hash6(saddr, daddr, sport, dport, 0, 0) + sseq;
+
+ diff = (count - (cookie >> COOKIEBITS)) & ((__u32) -1 >> COOKIEBITS);
+ if (diff >= maxdiff)
+ return (__u32)-1;
+
+ return (cookie -
+ cookie_hash6(saddr, daddr, sport, dport, count - diff, 1))
+ & COOKIEMASK;
+}
+
+__u32 cookie_v6_init_sequence(struct sock *sk, struct sk_buff *skb, __u16 *mssp)
+{
+ struct ipv6hdr *iph = ipv6_hdr(skb);
+ const struct tcphdr *th = tcp_hdr(skb);
+ int mssind;
+ const __u16 mss = *mssp;
+
+ tcp_sk(sk)->last_synq_overflow = jiffies;
+
+ for (mssind = 0; mss > msstab[mssind + 1]; mssind++)
+ ;
+ *mssp = msstab[mssind] + 1;
+
+ NET_INC_STATS_BH(LINUX_MIB_SYNCOOKIESSENT);
+
+ return secure_tcp_syn_cookie6(iph->saddr.s6_addr32,
+ iph->daddr.s6_addr32,
+ th->source, th->dest, ntohl(th->seq),
+ jiffies / (HZ * 60), mssind);
+}
+
+static inline int cookie_check6(struct sk_buff *skb, __u32 cookie)
+{
+ struct ipv6hdr *iph = ipv6_hdr(skb);
+ const struct tcphdr *th = tcp_hdr(skb);
+ __u32 seq = ntohl(th->seq) - 1;
+ __u32 mssind = check_tcp_syn_cookie6(cookie, iph->saddr.s6_addr32,
+ iph->daddr.s6_addr32, th->source,
+ th->dest, seq, jiffies / (HZ * 60),
+ COUNTER_TRIES);
+
+ return mssind < NUM_MSS ? msstab[mssind] + 1 : 0;
+}
+
+struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
+{
+ struct inet_request_sock *ireq;
+ struct inet6_request_sock *ireq6;
+ struct tcp_request_sock *treq;
+ struct ipv6_pinfo *np = inet6_sk(sk);
+ 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 dst_entry *dst;
+ __u8 rcv_wscale;
+
+ if (!sysctl_tcp_syncookies || !th->ack)
+ goto out;
+
+ if (time_after(jiffies, tp->last_synq_overflow + TCP_TIMEOUT_INIT) ||
+ (mss = cookie_check6(skb, cookie)) == 0) {
+ NET_INC_STATS_BH(LINUX_MIB_SYNCOOKIESFAILED);
+ goto out;
+ }
+
+ NET_INC_STATS_BH(LINUX_MIB_SYNCOOKIESRECV);
+
+ ret = NULL;
+ req = inet6_reqsk_alloc(&tcp6_request_sock_ops);
+ if (!req)
+ goto out;
+
+ ireq = inet_rsk(req);
+ ireq6 = inet6_rsk(req);
+ treq = tcp_rsk(req);
+ ireq6->pktopts = NULL;
+
+ if (security_inet_conn_request(sk, skb, req)) {
+ reqsk_free(req);
+ goto out;
+ }
+
+ req->mss = mss;
+ ireq->rmt_port = th->source;
+ ipv6_addr_copy(&ireq6->rmt_addr, &ipv6_hdr(skb)->saddr);
+ ipv6_addr_copy(&ireq6->loc_addr, &ipv6_hdr(skb)->daddr);
+ if (ipv6_opt_accepted(sk, skb) ||
+ np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo ||
+ np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim) {
+ atomic_inc(&skb->users);
+ ireq6->pktopts = skb;
+ }
+
+ ireq6->iif = sk->sk_bound_dev_if;
+ /* So that link locals have meaning */
+ if (!sk->sk_bound_dev_if &&
+ ipv6_addr_type(&ireq6->rmt_addr) & IPV6_ADDR_LINKLOCAL)
+ ireq6->iif = inet6_iif(skb);
+
+ req->expires = 0UL;
+ req->retrans = 0;
+ ireq->snd_wscale = ireq->rcv_wscale = ireq->tstamp_ok = 0;
+ ireq->wscale_ok = ireq->sack_ok = 0;
+ treq->rcv_isn = ntohl(th->seq) - 1;
+ treq->snt_isn = cookie;
+
+ /*
+ * We need to lookup the dst_entry to get the correct window size.
+ * This is taken from tcp_v6_syn_recv_sock. Somebody please enlighten
+ * me if there is a preferred way.
+ */
+ {
+ struct in6_addr *final_p = NULL, final;
+ struct flowi fl;
+ memset(&fl, 0, sizeof(fl));
+ fl.proto = IPPROTO_TCP;
+ ipv6_addr_copy(&fl.fl6_dst, &ireq6->rmt_addr);
+ if (np->opt && np->opt->srcrt) {
+ struct rt0_hdr *rt0 = (struct rt0_hdr *) np->opt->srcrt;
+ ipv6_addr_copy(&final, &fl.fl6_dst);
+ ipv6_addr_copy(&fl.fl6_dst, rt0->addr);
+ final_p = &final;
+ }
+ ipv6_addr_copy(&fl.fl6_src, &ireq6->loc_addr);
+ fl.oif = sk->sk_bound_dev_if;
+ fl.fl_ip_dport = inet_rsk(req)->rmt_port;
+ fl.fl_ip_sport = inet_sk(sk)->sport;
+ security_req_classify_flow(req, &fl);
+ if (ip6_dst_lookup(sk, &dst, &fl)) {
+ reqsk_free(req);
+ goto out;
+ }
+ if (final_p)
+ ipv6_addr_copy(&fl.fl6_dst, final_p);
+ if ((xfrm_lookup(&dst, &fl, sk, 0)) < 0)
+ goto out;
+ }
+
+ req->window_clamp = dst_metric(dst, RTAX_WINDOW);
+ tcp_select_initial_window(tcp_full_space(sk), req->mss,
+ &req->rcv_wnd, &req->window_clamp,
+ 0, &rcv_wscale);
+
+ ireq->rcv_wscale = rcv_wscale;
+
+ ret = get_cookie_sock(sk, skb, req, dst);
+
+out: return ret;
+}
+
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 93980c3..ad39bd1 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -520,6 +520,20 @@ done:
return err;
}

+static inline void syn_flood_warning(struct sk_buff *skb)
+{
+#ifdef CONFIG_SYN_COOKIES
+ if (sysctl_tcp_syncookies)
+ printk(KERN_INFO
+ "TCPv6: Possible SYN flooding on port %d. "
+ "Sending cookies.\n", ntohs(tcp_hdr(skb)->dest));
+ else
+#endif
+ printk(KERN_INFO
+ "TCPv6: Possible SYN flooding on port %d. "
+ "Dropping request.\n", ntohs(tcp_hdr(skb)->dest));
+}
+
static void tcp_v6_reqsk_destructor(struct request_sock *req)
{
if (inet6_rsk(req)->pktopts)
@@ -923,7 +937,7 @@ done_opts:
}
#endif

-static struct request_sock_ops tcp6_request_sock_ops __read_mostly = {
+struct request_sock_ops tcp6_request_sock_ops __read_mostly = {
.family = AF_INET6,
.obj_size = sizeof(struct tcp6_request_sock),
.rtx_syn_ack = tcp_v6_send_synack,
@@ -1221,9 +1235,9 @@ static struct sock *tcp_v6_hnd_req(struct sock *sk,struct sk_buff *skb)
return NULL;
}

-#if 0 /*def CONFIG_SYN_COOKIES*/
+#ifdef CONFIG_SYN_COOKIES
if (!th->rst && !th->syn && th->ack)
- sk = cookie_v6_check(sk, skb, &(IPCB(skb)->opt));
+ sk = cookie_v6_check(sk, skb);
#endif
return sk;
}
@@ -1239,6 +1253,11 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
struct tcp_sock *tp = tcp_sk(sk);
struct request_sock *req = NULL;
__u32 isn = TCP_SKB_CB(skb)->when;
+#ifdef CONFIG_SYN_COOKIES
+ int want_cookie = 0;
+#else
+#define want_cookie 0
+#endif

if (skb->protocol == htons(ETH_P_IP))
return tcp_v4_conn_request(sk, skb);
@@ -1246,12 +1265,14 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
if (!ipv6_unicast_destination(skb))
goto drop;

- /*
- * There are no SYN attacks on IPv6, yet...
- */
if (inet_csk_reqsk_queue_is_full(sk) && !isn) {
if (net_ratelimit())
- printk(KERN_INFO "TCPv6: dropping request, synflood is possible\n");
+ syn_flood_warning(skb);
+#ifdef CONFIG_SYN_COOKIES
+ if (sysctl_tcp_syncookies)
+ want_cookie = 1;
+ else
+#endif
goto drop;
}

@@ -1272,29 +1293,39 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)

tcp_parse_options(skb, &tmp_opt, 0);

+ if (want_cookie) {
+ tcp_clear_options(&tmp_opt);
+ tmp_opt.saw_tstamp = 0;
+ }
+
tmp_opt.tstamp_ok = tmp_opt.saw_tstamp;
tcp_openreq_init(req, &tmp_opt, skb);

treq = inet6_rsk(req);
ipv6_addr_copy(&treq->rmt_addr, &ipv6_hdr(skb)->saddr);
ipv6_addr_copy(&treq->loc_addr, &ipv6_hdr(skb)->daddr);
- TCP_ECN_create_request(req, tcp_hdr(skb));
treq->pktopts = NULL;
- if (ipv6_opt_accepted(sk, skb) ||
- np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo ||
- np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim) {
- atomic_inc(&skb->users);
- treq->pktopts = skb;
- }
- treq->iif = sk->sk_bound_dev_if;
+ if (!want_cookie)
+ TCP_ECN_create_request(req, tcp_hdr(skb));
+
+ if (want_cookie) {
+ isn = cookie_v6_init_sequence(sk, skb, &req->mss);
+ } else if (!isn) {
+ if (ipv6_opt_accepted(sk, skb) ||
+ np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo ||
+ np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim) {
+ atomic_inc(&skb->users);
+ treq->pktopts = skb;
+ }
+ treq->iif = sk->sk_bound_dev_if;

- /* So that link locals have meaning */
- if (!sk->sk_bound_dev_if &&
- ipv6_addr_type(&treq->rmt_addr) & IPV6_ADDR_LINKLOCAL)
- treq->iif = inet6_iif(skb);
+ /* So that link locals have meaning */
+ if (!sk->sk_bound_dev_if &&
+ ipv6_addr_type(&treq->rmt_addr) & IPV6_ADDR_LINKLOCAL)
+ treq->iif = inet6_iif(skb);

- if (isn == 0)
isn = tcp_v6_init_sequence(skb);
+ }

tcp_rsk(req)->snt_isn = isn;

@@ -1303,8 +1334,10 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
if (tcp_v6_send_synack(sk, req, NULL))
goto drop;

- inet6_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT);
- return 0;
+ if (!want_cookie) {
+ inet6_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT);
+ return 0;
+ }

drop:
if (req)
--
1.5.3.4


2008-02-05 15:21:20

by Andi Kleen

[permalink] [raw]
Subject: Re: [PATCH] Add IPv6 support to TCP SYN cookies

On Mon, Feb 04, 2008 at 03:01:01PM -0800, Glenn Griffin wrote:
> Add IPv6 support to TCP SYN cookies. This is written and tested against
> 2.6.24, and applies cleanly to linus' current HEAD (d2fc0b). Unfortunately
> linus' HEAD breaks my sky2 card at the moment, so I'm unable to test against
> that. I see no reason why it would be affected though. Comments/suggestions
> are welcome.

Syncookies are discouraged these days. They disable too many
valuable TCP features (window scaling, SACK) and even without them
the kernel is usually strong enough to defend against syn floods
and systems have much more memory than they used to be.

So I don't think it makes much sense to add more code to it, sorry.

Besides you should really move it to the ipv6 module, right now the code
would be always compiled in even for ipv4 only kernels.

-Andi

2008-02-05 15:48:17

by Alan

[permalink] [raw]
Subject: Re: [PATCH] Add IPv6 support to TCP SYN cookies

> Syncookies are discouraged these days. They disable too many
> valuable TCP features (window scaling, SACK) and even without them
> the kernel is usually strong enough to defend against syn floods
> and systems have much more memory than they used to be.

Somewhat untrue. Network speeds have risen dramatically, the number of
appliances running Linux that are not PC class means memory has fallen
not risen and CPU has been pretty level.

> So I don't think it makes much sense to add more code to it, sorry.

I think it makes a lot of sense - providing it defaults off for the PC
world where as you say its use is limited.

Alan

2008-02-05 16:04:30

by Andi Kleen

[permalink] [raw]
Subject: Re: [PATCH] Add IPv6 support to TCP SYN cookies

On Tue, Feb 05, 2008 at 03:42:13PM +0000, Alan Cox wrote:
> > Syncookies are discouraged these days. They disable too many
> > valuable TCP features (window scaling, SACK) and even without them
> > the kernel is usually strong enough to defend against syn floods
> > and systems have much more memory than they used to be.
>
> Somewhat untrue. Network speeds have risen dramatically, the number of

With strong I meant Linux has much better algorithms to handle the standard
syn queue (syncookies was originally added when it had only dumb head drop)
and there are minisocks which also require significantly less overhead
to manage than full sockets (less memory etc.)

When I wrote syncookies originally that all was not true.

> appliances running Linux that are not PC class means memory has fallen
> not risen and CPU has been pretty level.
>
> > So I don't think it makes much sense to add more code to it, sorry.
>
> I think it makes a lot of sense

I have my doubts. It would be probably better to recheck everything
and then remove syncookies.

Also your sub PC class appliances rarely run LISTEN servers anyways
that are open to the world.

-Andi

2008-02-05 16:08:56

by Alan

[permalink] [raw]
Subject: Re: [PATCH] Add IPv6 support to TCP SYN cookies

> Also your sub PC class appliances rarely run LISTEN servers anyways
> that are open to the world.

Really. The ones that first come to mind often have exposed ports
including PDA devices and phones. (Ditto low end PC boxes - portscan an
EEPC some day ;))

Is the other stuff enough - good question, and can be measured easily
enough on a little dlink router or similar.

Alan

2008-02-05 16:13:56

by Andi Kleen

[permalink] [raw]
Subject: Re: [PATCH] Add IPv6 support to TCP SYN cookies

On Tue, Feb 05, 2008 at 04:03:01PM +0000, Alan Cox wrote:
> > Also your sub PC class appliances rarely run LISTEN servers anyways
> > that are open to the world.
>
> Really. The ones that first come to mind often have exposed ports
> including PDA devices and phones. (Ditto low end PC boxes - portscan an
> EEPC some day ;))

What kind of LISTEN ports? And does it matter if they're DoS'ed?

The only one I can think of right now would be ident and frankly nobody
will really care if that one works or not.

If it's just the management interface etc. (which should really be firewalled)
then likely not.


> Is the other stuff enough - good question, and can be measured easily
> enough on a little dlink router or similar.

My guess would be that it is.

If it's not it would be probably better to look at improving the standard queue
management again; e.g.readd RED.

-Andi

2008-02-05 16:20:47

by Alan

[permalink] [raw]
Subject: Re: [PATCH] Add IPv6 support to TCP SYN cookies

> What kind of LISTEN ports? And does it matter if they're DoS'ed?

I guess that depends on the opinion of the owner

- Push based mobile services
- Email delivery
- VoIP
- Management ports
- Peer to peer data transfer
- Instant messaging direct user/user connections

Some of that can also be hardened using IP filter rules

> > Is the other stuff enough - good question, and can be measured easily
> > enough on a little dlink router or similar.
>
> My guess would be that it is.

Alan

2008-02-05 18:29:42

by Glenn Griffin

[permalink] [raw]
Subject: Re: [PATCH] Add IPv6 support to TCP SYN cookies

> Syncookies are discouraged these days. They disable too many
> valuable TCP features (window scaling, SACK) and even without them
> the kernel is usually strong enough to defend against syn floods
> and systems have much more memory than they used to be.
>
> So I don't think it makes much sense to add more code to it, sorry.

As you say the kernel is usually strong enough to defend against syn flood
attacks, but what about the situations where it isn't? As valuable as the TCP
features are I would give them up if it means I'm able to connect to my sshd
port when I otherwise would be unable to. While increased synq sizes, better
dropping algorithms, and minisocks are a great way to mitigate the attacks and
in most cases are enough, there are situations where syncookies are nice.

Regardless, I would say as long as ipv4 has syncookie support it will
accurately be viewed as a deficiency of ipv6 if it lacks support. So perhaps
the discussion should be we whether all the other defenses are enough to
warrant the removal of syncookie support from ipv4. That topic may bring in
more opinions.

> Besides you should really move it to the ipv6 module, right now the code
> would be always compiled in even for ipv4 only kernels.

That is correct. I will gladly move it into it's own section within net/ipv6/.
Do you have any problem using the same CONFIG and sysctl variables as the ipv4
implementation?

Thanks

--Glenn

2008-02-05 19:27:30

by Andi Kleen

[permalink] [raw]
Subject: Re: [PATCH] Add IPv6 support to TCP SYN cookies

On Tue, Feb 05, 2008 at 10:29:28AM -0800, Glenn Griffin wrote:
> > Syncookies are discouraged these days. They disable too many
> > valuable TCP features (window scaling, SACK) and even without them
> > the kernel is usually strong enough to defend against syn floods
> > and systems have much more memory than they used to be.
> >
> > So I don't think it makes much sense to add more code to it, sorry.
>
> As you say the kernel is usually strong enough to defend against syn flood
> attacks, but what about the situations where it isn't? As valuable as the TCP
> features are I would give them up if it means I'm able to connect to my sshd
> port when I otherwise would be unable to. While increased synq sizes, better
> dropping algorithms, and minisocks are a great way to mitigate the attacks and
> in most cases are enough, there are situations where syncookies are nice.

Have you seen such a case in practice with a modern kernel?

They also cause problems unfortunately; e.g. there is no real flow control for connections
anymore in the non DOS case.

> Regardless, I would say as long as ipv4 has syncookie support it will
> accurately be viewed as a deficiency of ipv6 if it lacks support. So perhaps
> the discussion should be we whether all the other defenses are enough to
> warrant the removal of syncookie support from ipv4. That topic may bring in
> more opinions.

That is essentially what I and Alan were discussing.
>
> > Besides you should really move it to the ipv6 module, right now the code
> > would be always compiled in even for ipv4 only kernels.
>
> That is correct. I will gladly move it into it's own section within net/ipv6/.
> Do you have any problem using the same CONFIG and sysctl variables as the ipv4
> implementation?

No.

-Andi

2008-02-05 19:36:24

by Andi Kleen

[permalink] [raw]
Subject: Re: [PATCH] Add IPv6 support to TCP SYN cookies

> The problem is that any reasonably recent PC can generate enough
> forged SYN packets to overwhelm reasonable SYN queues on a much more
> powerful server.

Have you actually seen this with a recent kernel in the wild or are
you just talking theoretically?

Linux uses some heuristics to manage the syn queue that should
still ensure reasonable service even without cookies under attack.
Also SYN-RECV sockets are stored in a special data structure optimized
to use minimal resources.

It is far from the classical head drop method that was so vunerable
to syn flooding.

-Andi

2008-02-05 19:42:55

by Ross Vandegrift

[permalink] [raw]
Subject: Re: [PATCH] Add IPv6 support to TCP SYN cookies

On Tue, Feb 05, 2008 at 10:29:28AM -0800, Glenn Griffin wrote:
> > Syncookies are discouraged these days. They disable too many
> > valuable TCP features (window scaling, SACK) and even without them
> > the kernel is usually strong enough to defend against syn floods
> > and systems have much more memory than they used to be.
> >
> > So I don't think it makes much sense to add more code to it, sorry.
>
> As you say the kernel is usually strong enough to defend against syn flood
> attacks, but what about the situations where it isn't? As valuable as the TCP
> features are I would give them up if it means I'm able to connect to my sshd
> port when I otherwise would be unable to. While increased synq sizes, better
> dropping algorithms, and minisocks are a great way to mitigate the attacks and
> in most cases are enough, there are situations where syncookies are nice.
>
> Regardless, I would say as long as ipv4 has syncookie support it will
> accurately be viewed as a deficiency of ipv6 if it lacks support. So perhaps
> the discussion should be we whether all the other defenses are enough to
> warrant the removal of syncookie support from ipv4. That topic may bring in
> more opinions.

Yes, syncookies, while presenting some tradeoffs, are a necessary tool
to have.

The problem is that any reasonably recent PC can generate enough
forged SYN packets to overwhelm reasonable SYN queues on a much more
powerful server.

Imagine a server with a few hundres Apache virtual hosts. One website
pisses off the wrong person and it impacts service for everyone.
While syncookies isn't always enough, enabling it often helps
make the server more resiliant during attacks. And for web service, most
of the connections are short-lived connections for small pieces of data -
so I'm not really convinced that window scaling and selective ACK are all
that important.


--
Ross Vandegrift
[email protected]

"The good Christian should beware of mathematicians, and all those who
make empty prophecies. The danger already exists that the mathematicians
have made a covenant with the devil to darken the spirit and to confine
man in the bonds of Hell."
--St. Augustine, De Genesi ad Litteram, Book II, xviii, 37

2008-02-05 19:58:19

by Jan Engelhardt

[permalink] [raw]
Subject: Re: [PATCH] Add IPv6 support to TCP SYN cookies


On Feb 5 2008 16:55, Andi Kleen wrote:
>On Mon, Feb 04, 2008 at 03:01:01PM -0800, Glenn Griffin wrote:
>> Add IPv6 support to TCP SYN cookies. This is written and tested against
>> 2.6.24, and applies cleanly to linus' current HEAD (d2fc0b). Unfortunately
>> linus' HEAD breaks my sky2 card at the moment, so I'm unable to test against
>> that. I see no reason why it would be affected though. Comments/suggestions
>> are welcome.
>
>Syncookies are discouraged these days. They disable too many
>valuable TCP features (window scaling, SACK) and even without them
>the kernel is usually strong enough to defend against syn floods
>and systems have much more memory than they used to be.
>
>So I don't think it makes much sense to add more code to it, sorry.

Distributions should then probably deactivate it by default.
SUSE 10.3 for example still has it enabled on default installs.

2008-02-05 20:40:17

by Evgeniy Polyakov

[permalink] [raw]
Subject: Re: [PATCH] Add IPv6 support to TCP SYN cookies

On Tue, Feb 05, 2008 at 09:02:11PM +0100, Andi Kleen ([email protected]) wrote:
> On Tue, Feb 05, 2008 at 10:29:28AM -0800, Glenn Griffin wrote:
> > > Syncookies are discouraged these days. They disable too many
> > > valuable TCP features (window scaling, SACK) and even without them
> > > the kernel is usually strong enough to defend against syn floods
> > > and systems have much more memory than they used to be.
> > >
> > > So I don't think it makes much sense to add more code to it, sorry.

How does syncookies prevent windows from growing?
Most (if not all) distributions have them enabled and window growing
works just fine. Actually I do not see any reason why connection
establishment handshake should prevent any run-time operations at all,
even if it was setup during handshake.

--
Evgeniy Polyakov

2008-02-05 20:54:19

by Andi Kleen

[permalink] [raw]
Subject: Re: [PATCH] Add IPv6 support to TCP SYN cookies

On Tue, Feb 05, 2008 at 11:39:11PM +0300, Evgeniy Polyakov wrote:
> On Tue, Feb 05, 2008 at 09:02:11PM +0100, Andi Kleen ([email protected]) wrote:
> > On Tue, Feb 05, 2008 at 10:29:28AM -0800, Glenn Griffin wrote:
> > > > Syncookies are discouraged these days. They disable too many
> > > > valuable TCP features (window scaling, SACK) and even without them
> > > > the kernel is usually strong enough to defend against syn floods
> > > > and systems have much more memory than they used to be.
> > > >
> > > > So I don't think it makes much sense to add more code to it, sorry.
>
> How does syncookies prevent windows from growing?

Syncookies do not allow window scaling so you can't have any windows >64k

> Most (if not all) distributions have them enabled and window growing
> works just fine. Actually I do not see any reason why connection
> establishment handshake should prevent any run-time operations at all,
> even if it was setup during handshake.

TCP only uses options negotiated during the hand shake and syncookies
is incapable to do this.

-Andi

2008-02-05 21:23:54

by Ross Vandegrift

[permalink] [raw]
Subject: Re: [PATCH] Add IPv6 support to TCP SYN cookies

On Tue, Feb 05, 2008 at 09:11:06PM +0100, Andi Kleen wrote:
> > The problem is that any reasonably recent PC can generate enough
> > forged SYN packets to overwhelm reasonable SYN queues on a much more
> > powerful server.
>
> Have you actually seen this with a recent kernel in the wild or are
> you just talking theoretically?
>
> Linux uses some heuristics to manage the syn queue that should
> still ensure reasonable service even without cookies under attack.
> Also SYN-RECV sockets are stored in a special data structure optimized
> to use minimal resources.
>
> It is far from the classical head drop method that was so vunerable
> to syn flooding.

I work at a hosting company and we see these kinds of issues in the
real world fairly frequently. I would guess maybe a monthly basis.
The servers where we have seen this are typically running RHEL 4 or 5
kernels, so I can't really speak to how recent the kernel is in this
specific term.

If I can find a box that we could temporary get a kernel.org kernel
on, I'll see if I can get a real comparison together. We have
collected a few of the more effective attack tools that have been left
on compromised systems, so it wouldn't be too difficult to get some
numbers.

--
Ross Vandegrift
[email protected]

"The good Christian should beware of mathematicians, and all those who
make empty prophecies. The danger already exists that the mathematicians
have made a covenant with the devil to darken the spirit and to confine
man in the bonds of Hell."
--St. Augustine, De Genesi ad Litteram, Book II, xviii, 37

2008-02-05 21:28:35

by Alan

[permalink] [raw]
Subject: Re: [PATCH] Add IPv6 support to TCP SYN cookies

> How does syncookies prevent windows from growing?

Enabling them doesn't.

> Most (if not all) distributions have them enabled and window growing
> works just fine. Actually I do not see any reason why connection
> establishment handshake should prevent any run-time operations at all,
> even if it was setup during handshake.

Syncookies are only triggered if the system is under a load where it
would begin to lose connections otherwise. So they merely turn a DoS into
a working if slightly slower setup (and > 64K windows don't matter for
most normal users, especially on mobile devices).

2008-02-05 21:31:26

by Alan

[permalink] [raw]
Subject: Re: [PATCH] Add IPv6 support to TCP SYN cookies

> >So I don't think it makes much sense to add more code to it, sorry.
>
> Distributions should then probably deactivate it by default.
> SUSE 10.3 for example still has it enabled on default installs.

Even though I work the loyal opposition to SuSE I'd say SuSE 10.3 is
correct in having it enabled in the build.

Alan

2008-02-05 21:37:44

by Willy Tarreau

[permalink] [raw]
Subject: Re: [PATCH] Add IPv6 support to TCP SYN cookies

Hi Andi, Alan,

I've run extensive tests with/without syn cookies recently.

On Tue, Feb 05, 2008 at 05:39:12PM +0100, Andi Kleen wrote:
> On Tue, Feb 05, 2008 at 03:42:13PM +0000, Alan Cox wrote:
> > > Syncookies are discouraged these days. They disable too many
> > > valuable TCP features (window scaling, SACK) and even without them
> > > the kernel is usually strong enough to defend against syn floods
> > > and systems have much more memory than they used to be.
> >
> > Somewhat untrue. Network speeds have risen dramatically, the number of
>
> With strong I meant Linux has much better algorithms to handle the standard
> syn queue (syncookies was originally added when it had only dumb head drop)
> and there are minisocks which also require significantly less overhead
> to manage than full sockets (less memory etc.)

That's true, but not enough, see below.

> When I wrote syncookies originally that all was not true.
>
> > appliances running Linux that are not PC class means memory has fallen
> > not risen and CPU has been pretty level.
> >
> > > So I don't think it makes much sense to add more code to it, sorry.
> >
> > I think it makes a lot of sense
>
> I have my doubts. It would be probably better to recheck everything
> and then remove syncookies.
>
> Also your sub PC class appliances rarely run LISTEN servers anyways
> that are open to the world.

In my tests, I discovered that in fact SYN cookies more benefit high
end machines than low-end ones. Let me explain.

I noticed that computing the cookie consumes a lot of CPU, which is a
real problem on low-end machines. But on the other end, it helps the
system continue to respond when otherwise it would not. My tests on
an AMD LX800 with max_syn_backlog at 63000 on an HTTP reverse proxy
consisted in injecting 250 hits/s of legitimate traffic with 8000 SYN/s
of noise.

Without SYN cookies, the average response time was about 1.5 second and
unstable (due to retransmits), and the CPU was set to 60%. With SYN
cookies enabled, the response time dropped to 12-15ms only, but CPU
usage jumped to 70%. The difference appears at a higher legitimate
traffic rate. At 500 hits/s + 7800 SYN/s, the CPU is just saturated
with correct response time (SYN backlog almost full but never full),
and the performance slightly goes down with SYN cookies enabled, inducing
a drop of the hit rate due to the increased CPU consumption.

Till there, one would conclude that SYN cookies are bad. BUT! this was
with tcp_synack_retries = 1, which is the optimal situation without
SYN cookies under an attack and which is pretty bad for normal usage.

The real problem without SYN cookies is that you are forced to support
a huge SYN backlog (eg: 2 million entries to sustain 100 Mbps of SYN).
And what happens with a large backlog ? You send a lot of retries for
each SYN. 5 by default, meaning 6 SYN-ACKs for 1 SYN. Thus, you become
a SYN amplifier and the guy in front of you just has to send you 20 Mbps
of traffic for you to saturate your 100 Mbps uplink.

Also, sending all those SYN-ACKs takes a huge amount of CPU time. With
tcp_synack_retries at 0, my machine received 26600 SYN/s, and returned
26600 SYN-ACK/s at 100% CPU. With tcp_synack_retries set to 4, it could
only accept 12900 SYN/s, replying with 51200 SYN-ACK/s. So the input
load was halfed and the output was doubled. I did not bother going higher.

The only solution against this is then to reduce tcp_synack_retries to
very low values (0 ideally, to match SYN cookies behaviour), but in this
case, you degrade normal traffic 100% of the time, while SYN cookies would
only trigger while you're already under attack.

My conclusions after those tests was to set tcp_synack_retries to a reasonable
value (1 to 3), and set the backlog to the number of half-open sessions your
machine can accumulate under a SYN attack without collapsing. You then enable
SYN cookies, and they will only trigger when you know that your machine will
not be able to sustain the increased load.

This solution permits you to accept normal connections when not under attack,
with an acceptable number of retransmits and with TCP options working well.
Under a moderate attack, the large backlog will still help you accept
legitimate connections with all comfort (sack, wscale, ...). Under a massive
attack, you will not send more than tcp_synack_retries*backlog packets per
tcp_synack_retries period, thus limiting the outbound traffic, plus 1 SYN-ACK
per incoming SYN, legitimate or not. At this stage, if your users have a
castrated TCP stack in front of them, that's not a problem because you know
that otherwise they would not even have been able to connect.

So in this regard, SYN cookies are really needed.

Last, I've read on DJB's page that SYN cookies do not break TCP beahaviour.
Yes they do. If the client waits for the server to talk first, you'd better
not lose the first ACK from the client because it will not get retransmitted,
and the client will see an ESTABLISHED connection while the server will have
nothing. Fortunately, most attack targets are HTTP and do not have this
problem.

For this reason, I think that SYN cookies should be activable by port or
simply by a setsockopt() from the application itself. Having them enabled
by default on the whole system with small backlogs is bad, having large
backlogs to cover attacks is bad, but having medium backlogs with SYN
cookies per application would be very useful.

Best regards,
Willy

2008-02-05 21:51:27

by Evgeniy Polyakov

[permalink] [raw]
Subject: Re: [PATCH] Add IPv6 support to TCP SYN cookies

On Tue, Feb 05, 2008 at 09:53:45PM +0100, Andi Kleen ([email protected]) wrote:
> > How does syncookies prevent windows from growing?
>
> Syncookies do not allow window scaling so you can't have any windows >64k

Then you meant not windows change, but the fact, that option is ignored
as long as sack enable one?

> > Most (if not all) distributions have them enabled and window growing
> > works just fine. Actually I do not see any reason why connection
> > establishment handshake should prevent any run-time operations at all,
> > even if it was setup during handshake.
>
> TCP only uses options negotiated during the hand shake and syncookies
> is incapable to do this.

What about fixing the implementation, so that it could get into account
different options too?

> -Andi

--
Evgeniy Polyakov

2008-02-05 21:53:17

by Evgeniy Polyakov

[permalink] [raw]
Subject: Re: [PATCH] Add IPv6 support to TCP SYN cookies

Hi Alan.

On Tue, Feb 05, 2008 at 09:20:17PM +0000, Alan Cox ([email protected]) wrote:
> > Most (if not all) distributions have them enabled and window growing
> > works just fine. Actually I do not see any reason why connection
> > establishment handshake should prevent any run-time operations at all,
> > even if it was setup during handshake.
>
> Syncookies are only triggered if the system is under a load where it
> would begin to lose connections otherwise. So they merely turn a DoS into
> a working if slightly slower setup (and > 64K windows don't matter for
> most normal users, especially on mobile devices).

SACK is actually a good idea for mobile devices, so preventing
syncookies from not getting into account some options (btw, does it work
with timestamps and PAWS?) is not a solution.

--
Evgeniy Polyakov

2008-02-05 22:08:22

by Willy Tarreau

[permalink] [raw]
Subject: Re: [PATCH] Add IPv6 support to TCP SYN cookies

On Wed, Feb 06, 2008 at 12:52:17AM +0300, Evgeniy Polyakov wrote:
> Hi Alan.
>
> On Tue, Feb 05, 2008 at 09:20:17PM +0000, Alan Cox ([email protected]) wrote:
> > > Most (if not all) distributions have them enabled and window growing
> > > works just fine. Actually I do not see any reason why connection
> > > establishment handshake should prevent any run-time operations at all,
> > > even if it was setup during handshake.
> >
> > Syncookies are only triggered if the system is under a load where it
> > would begin to lose connections otherwise. So they merely turn a DoS into
> > a working if slightly slower setup (and > 64K windows don't matter for
> > most normal users, especially on mobile devices).
>
> SACK is actually a good idea for mobile devices, so preventing
> syncookies from not getting into account some options (btw, does it work
> with timestamps and PAWS?) is not a solution.

All TCP options negociated during session setup are lost. In fact, some
bits (3) are still reserved for the best known value of the MSS, but
that's all. The principle of SYN cookies is that the server does not
create any session upon the SYN, but builds a sequence number constitued
from a hash and the values it absolutely needs to know when the client
validates the session with an ACK.

I've seen some firewalls acting as SYN gateways which send the options
from the server to the client in the first ACK packet from the server.
This is normally not allowed, but it seems to work with some TCP stacks
(at least for the MSS). One solution would be to extend TCP to officially
support this behaviour and to optionally use it along with SYN cookies,
but there will always be old clients not compatible with the extension.

Regards,
Willy

2008-02-05 22:13:23

by Alan

[permalink] [raw]
Subject: Re: [PATCH] Add IPv6 support to TCP SYN cookies

> SACK is actually a good idea for mobile devices, so preventing
> syncookies from not getting into account some options (btw, does it work
> with timestamps and PAWS?) is not a solution.

Syncookies only get used at the point where the alternative is failure.
No SACK beats a DoS situation most days

2008-02-06 01:45:46

by Glenn Griffin

[permalink] [raw]
Subject: Re: [PATCH] Add IPv6 support to TCP SYN cookies

I realized an earlier email I sent had an incorrect timestamp and wasn't
associated with the thread, so I thought it would be better to resend.
I apologize if this is duplicated for anyone.

Here is a reworked patch that moves the IPv6 syncookie support out of
the ipv4/syncookies.c file and into it's own ipv6/syncookies.c. The
same CONFIG options and sysctl variables as ipv4, but this way the code
is isolated to the ipv6 module.


Signed-off-by: Glenn Griffin <[email protected]>
---
include/net/tcp.h | 6 +
net/ipv6/Makefile | 1 +
net/ipv6/syncookies.c | 273 +++++++++++++++++++++++++++++++++++++++++++++++++
net/ipv6/tcp_ipv6.c | 77 ++++++++++----
4 files changed, 335 insertions(+), 22 deletions(-)
create mode 100644 net/ipv6/syncookies.c

diff --git a/include/net/tcp.h b/include/net/tcp.h
index cb5b033..d7f620c 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -436,6 +436,11 @@ extern struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb,
extern __u32 cookie_v4_init_sequence(struct sock *sk, struct sk_buff *skb,
__u16 *mss);

+/* From net/ipv6/syncookies.c */
+extern struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb);
+extern __u32 cookie_v6_init_sequence(struct sock *sk, struct sk_buff *skb,
+ __u16 *mss);
+
/* tcp_output.c */

extern void __tcp_push_pending_frames(struct sock *sk, unsigned int cur_mss,
@@ -1337,6 +1342,7 @@ extern int tcp_proc_register(struct tcp_seq_afinfo *afinfo);
extern void tcp_proc_unregister(struct tcp_seq_afinfo *afinfo);

extern struct request_sock_ops tcp_request_sock_ops;
+extern struct request_sock_ops tcp6_request_sock_ops;

extern int tcp_v4_destroy_sock(struct sock *sk);

diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile
index 87c23a7..d1a1056 100644
--- a/net/ipv6/Makefile
+++ b/net/ipv6/Makefile
@@ -15,6 +15,7 @@ ipv6-$(CONFIG_XFRM) += xfrm6_policy.o xfrm6_state.o xfrm6_input.o \
ipv6-$(CONFIG_NETFILTER) += netfilter.o
ipv6-$(CONFIG_IPV6_MULTIPLE_TABLES) += fib6_rules.o
ipv6-$(CONFIG_PROC_FS) += proc.o
+ipv6-$(CONFIG_SYN_COOKIES) += syncookies.o

ipv6-objs += $(ipv6-y)

diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c
new file mode 100644
index 0000000..521c9da
--- /dev/null
+++ b/net/ipv6/syncookies.c
@@ -0,0 +1,273 @@
+/*
+ * IPv6 Syncookies implementation for the Linux kernel
+ *
+ * Authors:
+ * Glenn Griffin <[email protected]>
+ *
+ * Based on IPv4 implementation by Andi Kleen
+ * linux/net/ipv4/syncookies.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <linux/tcp.h>
+#include <linux/random.h>
+#include <linux/cryptohash.h>
+#include <linux/kernel.h>
+#include <net/ipv6.h>
+#include <net/tcp.h>
+
+extern int sysctl_tcp_syncookies;
+
+static __u32 syncookie_secret[2][16-10+SHA_DIGEST_WORDS];
+
+static __init int init_syncookies(void)
+{
+ get_random_bytes(syncookie_secret, sizeof(syncookie_secret));
+ return 0;
+}
+module_init(init_syncookies);
+
+#define COOKIEBITS 24 /* Upper bits store count */
+#define COOKIEMASK (((__u32)1 << COOKIEBITS) - 1)
+
+/*
+ * This table has to be sorted and terminated with (__u16)-1.
+ * XXX generate a better table.
+ * Unresolved Issues: HIPPI with a 64k MSS is not well supported.
+ *
+ * Taken directly from ipv4 implementation.
+ * Should this list be modified for ipv6 use or is it close enough?
+ * rfc 2460 8.3 suggests mss values 20 bytes less than ipv4 counterpart
+ */
+static __u16 const msstab[] = {
+ 64 - 1,
+ 256 - 1,
+ 512 - 1,
+ 536 - 1,
+ 1024 - 1,
+ 1440 - 1,
+ 1460 - 1,
+ 4312 - 1,
+ (__u16)-1
+};
+/* The number doesn't include the -1 terminator */
+#define NUM_MSS (ARRAY_SIZE(msstab) - 1)
+
+/*
+ * This (misnamed) value is the age of syncookie which is permitted.
+ * Its ideal value should be dependent on TCP_TIMEOUT_INIT and
+ * sysctl_tcp_retries1. It's a rather complicated formula (exponential
+ * backoff) to compute at runtime so it's currently hardcoded here.
+ */
+#define COUNTER_TRIES 4
+
+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 u32 cookie_hash(struct in6_addr *saddr, struct in6_addr *daddr,
+ __be16 sport, __be16 dport, u32 count, int c)
+{
+ __u32 tmp[16 + 5 + SHA_WORKSPACE_WORDS];
+
+ /*
+ * we have 320 bits of information to hash, copy in the remaining
+ * 192 bits required for sha_transform, from the syncookie_secret
+ * and overwrite the digest with the secret
+ */
+ memcpy(tmp + 10, syncookie_secret[c], sizeof(syncookie_secret[c]));
+ memcpy(tmp, saddr, 16);
+ memcpy(tmp + 4, daddr, 16);
+ tmp[8] = ((__force u32)sport << 16) + (__force u32)dport;
+ tmp[9] = count;
+ sha_transform(tmp + 16, (__u8 *)tmp, tmp + 16 + 5);
+
+ return tmp[17];
+}
+
+static __u32 secure_tcp_syn_cookie(struct in6_addr *saddr, struct in6_addr *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));
+}
+
+static __u32 check_tcp_syn_cookie(__u32 cookie, struct in6_addr *saddr,
+ struct in6_addr *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;
+}
+
+__u32 cookie_v6_init_sequence(struct sock *sk, struct sk_buff *skb, __u16 *mssp)
+{
+ struct ipv6hdr *iph = ipv6_hdr(skb);
+ const struct tcphdr *th = tcp_hdr(skb);
+ int mssind;
+ const __u16 mss = *mssp;
+
+ tcp_sk(sk)->last_synq_overflow = jiffies;
+
+ for (mssind = 0; mss > msstab[mssind + 1]; mssind++)
+ ;
+ *mssp = msstab[mssind] + 1;
+
+ NET_INC_STATS_BH(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 int cookie_check(struct sk_buff *skb, __u32 cookie)
+{
+ struct ipv6hdr *iph = ipv6_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 < NUM_MSS ? msstab[mssind] + 1 : 0;
+}
+
+struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
+{
+ struct inet_request_sock *ireq;
+ struct inet6_request_sock *ireq6;
+ struct tcp_request_sock *treq;
+ struct ipv6_pinfo *np = inet6_sk(sk);
+ 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 dst_entry *dst;
+ __u8 rcv_wscale;
+
+ if (!sysctl_tcp_syncookies || !th->ack)
+ goto out;
+
+ if (time_after(jiffies, tp->last_synq_overflow + TCP_TIMEOUT_INIT) ||
+ (mss = cookie_check(skb, cookie)) == 0) {
+ NET_INC_STATS_BH(LINUX_MIB_SYNCOOKIESFAILED);
+ goto out;
+ }
+
+ NET_INC_STATS_BH(LINUX_MIB_SYNCOOKIESRECV);
+
+ ret = NULL;
+ req = inet6_reqsk_alloc(&tcp6_request_sock_ops);
+ if (!req)
+ goto out;
+
+ ireq = inet_rsk(req);
+ ireq6 = inet6_rsk(req);
+ treq = tcp_rsk(req);
+ ireq6->pktopts = NULL;
+
+ if (security_inet_conn_request(sk, skb, req)) {
+ reqsk_free(req);
+ goto out;
+ }
+
+ req->mss = mss;
+ ireq->rmt_port = th->source;
+ ipv6_addr_copy(&ireq6->rmt_addr, &ipv6_hdr(skb)->saddr);
+ ipv6_addr_copy(&ireq6->loc_addr, &ipv6_hdr(skb)->daddr);
+ if (ipv6_opt_accepted(sk, skb) ||
+ np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo ||
+ np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim) {
+ atomic_inc(&skb->users);
+ ireq6->pktopts = skb;
+ }
+
+ ireq6->iif = sk->sk_bound_dev_if;
+ /* So that link locals have meaning */
+ if (!sk->sk_bound_dev_if &&
+ ipv6_addr_type(&ireq6->rmt_addr) & IPV6_ADDR_LINKLOCAL)
+ ireq6->iif = inet6_iif(skb);
+
+ req->expires = 0UL;
+ req->retrans = 0;
+ ireq->snd_wscale = ireq->rcv_wscale = ireq->tstamp_ok = 0;
+ ireq->wscale_ok = ireq->sack_ok = 0;
+ treq->rcv_isn = ntohl(th->seq) - 1;
+ treq->snt_isn = cookie;
+
+ /*
+ * We need to lookup the dst_entry to get the correct window size.
+ * This is taken from tcp_v6_syn_recv_sock. Somebody please enlighten
+ * me if there is a preferred way.
+ */
+ {
+ struct in6_addr *final_p = NULL, final;
+ struct flowi fl;
+ memset(&fl, 0, sizeof(fl));
+ fl.proto = IPPROTO_TCP;
+ ipv6_addr_copy(&fl.fl6_dst, &ireq6->rmt_addr);
+ if (np->opt && np->opt->srcrt) {
+ struct rt0_hdr *rt0 = (struct rt0_hdr *) np->opt->srcrt;
+ ipv6_addr_copy(&final, &fl.fl6_dst);
+ ipv6_addr_copy(&fl.fl6_dst, rt0->addr);
+ final_p = &final;
+ }
+ ipv6_addr_copy(&fl.fl6_src, &ireq6->loc_addr);
+ fl.oif = sk->sk_bound_dev_if;
+ fl.fl_ip_dport = inet_rsk(req)->rmt_port;
+ fl.fl_ip_sport = inet_sk(sk)->sport;
+ security_req_classify_flow(req, &fl);
+ if (ip6_dst_lookup(sk, &dst, &fl)) {
+ reqsk_free(req);
+ goto out;
+ }
+ if (final_p)
+ ipv6_addr_copy(&fl.fl6_dst, final_p);
+ if ((xfrm_lookup(&dst, &fl, sk, 0)) < 0)
+ goto out;
+ }
+
+ req->window_clamp = dst_metric(dst, RTAX_WINDOW);
+ tcp_select_initial_window(tcp_full_space(sk), req->mss,
+ &req->rcv_wnd, &req->window_clamp,
+ 0, &rcv_wscale);
+
+ ireq->rcv_wscale = rcv_wscale;
+
+ ret = get_cookie_sock(sk, skb, req, dst);
+
+out: return ret;
+}
+
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 93980c3..ad39bd1 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -520,6 +520,20 @@ done:
return err;
}

+static inline void syn_flood_warning(struct sk_buff *skb)
+{
+#ifdef CONFIG_SYN_COOKIES
+ if (sysctl_tcp_syncookies)
+ printk(KERN_INFO
+ "TCPv6: Possible SYN flooding on port %d. "
+ "Sending cookies.\n", ntohs(tcp_hdr(skb)->dest));
+ else
+#endif
+ printk(KERN_INFO
+ "TCPv6: Possible SYN flooding on port %d. "
+ "Dropping request.\n", ntohs(tcp_hdr(skb)->dest));
+}
+
static void tcp_v6_reqsk_destructor(struct request_sock *req)
{
if (inet6_rsk(req)->pktopts)
@@ -923,7 +937,7 @@ done_opts:
}
#endif

-static struct request_sock_ops tcp6_request_sock_ops __read_mostly = {
+struct request_sock_ops tcp6_request_sock_ops __read_mostly = {
.family = AF_INET6,
.obj_size = sizeof(struct tcp6_request_sock),
.rtx_syn_ack = tcp_v6_send_synack,
@@ -1221,9 +1235,9 @@ static struct sock *tcp_v6_hnd_req(struct sock *sk,struct sk_buff *skb)
return NULL;
}

-#if 0 /*def CONFIG_SYN_COOKIES*/
+#ifdef CONFIG_SYN_COOKIES
if (!th->rst && !th->syn && th->ack)
- sk = cookie_v6_check(sk, skb, &(IPCB(skb)->opt));
+ sk = cookie_v6_check(sk, skb);
#endif
return sk;
}
@@ -1239,6 +1253,11 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
struct tcp_sock *tp = tcp_sk(sk);
struct request_sock *req = NULL;
__u32 isn = TCP_SKB_CB(skb)->when;
+#ifdef CONFIG_SYN_COOKIES
+ int want_cookie = 0;
+#else
+#define want_cookie 0
+#endif

if (skb->protocol == htons(ETH_P_IP))
return tcp_v4_conn_request(sk, skb);
@@ -1246,12 +1265,14 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
if (!ipv6_unicast_destination(skb))
goto drop;

- /*
- * There are no SYN attacks on IPv6, yet...
- */
if (inet_csk_reqsk_queue_is_full(sk) && !isn) {
if (net_ratelimit())
- printk(KERN_INFO "TCPv6: dropping request, synflood is possible\n");
+ syn_flood_warning(skb);
+#ifdef CONFIG_SYN_COOKIES
+ if (sysctl_tcp_syncookies)
+ want_cookie = 1;
+ else
+#endif
goto drop;
}

@@ -1272,29 +1293,39 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)

tcp_parse_options(skb, &tmp_opt, 0);

+ if (want_cookie) {
+ tcp_clear_options(&tmp_opt);
+ tmp_opt.saw_tstamp = 0;
+ }
+
tmp_opt.tstamp_ok = tmp_opt.saw_tstamp;
tcp_openreq_init(req, &tmp_opt, skb);

treq = inet6_rsk(req);
ipv6_addr_copy(&treq->rmt_addr, &ipv6_hdr(skb)->saddr);
ipv6_addr_copy(&treq->loc_addr, &ipv6_hdr(skb)->daddr);
- TCP_ECN_create_request(req, tcp_hdr(skb));
treq->pktopts = NULL;
- if (ipv6_opt_accepted(sk, skb) ||
- np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo ||
- np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim) {
- atomic_inc(&skb->users);
- treq->pktopts = skb;
- }
- treq->iif = sk->sk_bound_dev_if;
+ if (!want_cookie)
+ TCP_ECN_create_request(req, tcp_hdr(skb));
+
+ if (want_cookie) {
+ isn = cookie_v6_init_sequence(sk, skb, &req->mss);
+ } else if (!isn) {
+ if (ipv6_opt_accepted(sk, skb) ||
+ np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo ||
+ np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim) {
+ atomic_inc(&skb->users);
+ treq->pktopts = skb;
+ }
+ treq->iif = sk->sk_bound_dev_if;

- /* So that link locals have meaning */
- if (!sk->sk_bound_dev_if &&
- ipv6_addr_type(&treq->rmt_addr) & IPV6_ADDR_LINKLOCAL)
- treq->iif = inet6_iif(skb);
+ /* So that link locals have meaning */
+ if (!sk->sk_bound_dev_if &&
+ ipv6_addr_type(&treq->rmt_addr) & IPV6_ADDR_LINKLOCAL)
+ treq->iif = inet6_iif(skb);

- if (isn == 0)
isn = tcp_v6_init_sequence(skb);
+ }

tcp_rsk(req)->snt_isn = isn;

@@ -1303,8 +1334,10 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
if (tcp_v6_send_synack(sk, req, NULL))
goto drop;

- inet6_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT);
- return 0;
+ if (!want_cookie) {
+ inet6_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT);
+ return 0;
+ }

drop:
if (req)
--
1.5.3.4

2008-02-06 07:15:49

by Andi Kleen

[permalink] [raw]
Subject: Re: [PATCH] Add IPv6 support to TCP SYN cookies

> +static __init int init_syncookies(void)
> +{
> + get_random_bytes(syncookie_secret, sizeof(syncookie_secret));
> + return 0;
> +}
> +module_init(init_syncookies);

I didn't think a module could have multiple module_inits. Are you
sure that works?

-Andi

2008-02-06 08:19:20

by Andi Kleen

[permalink] [raw]
Subject: Re: [PATCH] Add IPv6 support to TCP SYN cookies

> I work at a hosting company and we see these kinds of issues in the
> real world fairly frequently. I would guess maybe a monthly basis.
> The servers where we have seen this are typically running RHEL 4 or 5
> kernels, so I can't really speak to how recent the kernel is in this
> specific term.

RHEL5 should be recent enough.

>
> If I can find a box that we could temporary get a kernel.org kernel
> on, I'll see if I can get a real comparison together. We have
> collected a few of the more effective attack tools that have been left
> on compromised systems, so it wouldn't be too difficult to get some
> numbers.

That would be useful yes -- for different bandwidths.

If the young/old heuristics do not work well enough anymore most likely we should
try readding RED to the syn queue again. That used to be pretty effective
in the early days. I don't quite remember why Linux didn't end up using it
in fact.

-Andi

2008-02-06 09:16:47

by Evgeniy Polyakov

[permalink] [raw]
Subject: Re: [PATCH] Add IPv6 support to TCP SYN cookies

On Tue, Feb 05, 2008 at 05:52:31PM -0800, Glenn Griffin ([email protected]) wrote:
> +static u32 cookie_hash(struct in6_addr *saddr, struct in6_addr *daddr,
> + __be16 sport, __be16 dport, u32 count, int c)
> +{
> + __u32 tmp[16 + 5 + SHA_WORKSPACE_WORDS];

This huge buffer should not be allocated on stack.


--
Evgeniy Polyakov

2008-02-06 17:29:22

by Glenn Griffin

[permalink] [raw]
Subject: Re: [PATCH] Add IPv6 support to TCP SYN cookies

> I didn't think a module could have multiple module_inits. Are you
> sure that works?

Indeed. That will fail whenever ipv6 is compiled as a module. It's
been removed. It snuck in from the v4 implementation, where I'm still
having trouble understanding why it's needed there.

--Glenn

2008-02-06 18:11:19

by Andi Kleen

[permalink] [raw]
Subject: Re: [PATCH] Add IPv6 support to TCP SYN cookies

On Wed, Feb 06, 2008 at 09:36:11AM -0800, Glenn Griffin wrote:
> > I didn't think a module could have multiple module_inits. Are you
> > sure that works?
>
> Indeed. That will fail whenever ipv6 is compiled as a module. It's
> been removed. It snuck in from the v4 implementation, where I'm still
> having trouble understanding why it's needed there.

s/needed/used/

ipv4 is never modular so it works.

Arguably it would be cleaner if it was __initcall()

-Andi

2008-02-06 18:23:42

by Glenn Griffin

[permalink] [raw]
Subject: Re: [PATCH] Add IPv6 support to TCP SYN cookies

> > +static u32 cookie_hash(struct in6_addr *saddr, struct in6_addr *daddr,
> > + __be16 sport, __be16 dport, u32 count, int c)
> > +{
> > + __u32 tmp[16 + 5 + SHA_WORKSPACE_WORDS];
>
> This huge buffer should not be allocated on stack.

I can replace it will a kmalloc, but for my benefit what's the practical
size we try and limit the stack to? It seemed at first glance to me
that 404 bytes plus the arguments, etc. was not such a large buffer for
a non-recursive function. Plus the alternative with a kmalloc requires
propogating the possible error status back up to tcp_ipv6.c in the event
we are unable to allocate enough memory, so it can simply drop the
connection. Not an impossible task by any means but it does
significantly complicate things and I would like to know it's worth the
effort. Also would it be worth it to provide a supplemental patch for
the ipv4 implementation as it allocates the same buffer?

--Glenn

2008-02-06 22:56:41

by Glenn Griffin

[permalink] [raw]
Subject: Re: [PATCH] Add IPv6 support to TCP SYN cookies

Okay. Round3. Took into account that it was horribly broken when ipv6
was compiled as a module. The fixes export a few more symbols, and now
the syncookie_secret is shared between the v4 and v6 code. That should
be fine as it will be initialized when the v4 code starts, and it's not
currently possible to have v6 cookie support without v4. At this point
I have not taken Evgeniy's feedback on the hash buffer being to large to
keep on the stack. I was hoping to hear some other opinions on that.
Feedback is appreciated. Thanks.

Signed-off-by: Glenn Griffin <[email protected]>
---
include/net/tcp.h | 8 ++
net/ipv4/syncookies.c | 7 +-
net/ipv4/tcp_input.c | 1 +
net/ipv4/tcp_minisocks.c | 2 +
net/ipv4/tcp_output.c | 1 +
net/ipv6/Makefile | 1 +
net/ipv6/syncookies.c | 265 ++++++++++++++++++++++++++++++++++++++++++++++
net/ipv6/tcp_ipv6.c | 77 ++++++++++----
8 files changed, 336 insertions(+), 26 deletions(-)
create mode 100644 net/ipv6/syncookies.c

diff --git a/include/net/tcp.h b/include/net/tcp.h
index cb5b033..58a2dda 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -29,6 +29,7 @@
#include <linux/skbuff.h>
#include <linux/dmaengine.h>
#include <linux/crypto.h>
+#include <linux/cryptohash.h>

#include <net/inet_connection_sock.h>
#include <net/inet_timewait_sock.h>
@@ -431,11 +432,17 @@ extern int tcp_disconnect(struct sock *sk, int flags);
extern void tcp_unhash(struct sock *sk);

/* From syncookies.c */
+extern __u32 syncookie_secret[2][16-3+SHA_DIGEST_WORDS];
extern struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb,
struct ip_options *opt);
extern __u32 cookie_v4_init_sequence(struct sock *sk, struct sk_buff *skb,
__u16 *mss);

+/* From net/ipv6/syncookies.c */
+extern struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb);
+extern __u32 cookie_v6_init_sequence(struct sock *sk, struct sk_buff *skb,
+ __u16 *mss);
+
/* tcp_output.c */

extern void __tcp_push_pending_frames(struct sock *sk, unsigned int cur_mss,
@@ -1337,6 +1344,7 @@ extern int tcp_proc_register(struct tcp_seq_afinfo *afinfo);
extern void tcp_proc_unregister(struct tcp_seq_afinfo *afinfo);

extern struct request_sock_ops tcp_request_sock_ops;
+extern struct request_sock_ops tcp6_request_sock_ops;

extern int tcp_v4_destroy_sock(struct sock *sk);

diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c
index 2da1be0..e4453fc 100644
--- a/net/ipv4/syncookies.c
+++ b/net/ipv4/syncookies.c
@@ -10,8 +10,6 @@
* 2 of the License, or (at your option) any later version.
*
* $Id: syncookies.c,v 1.18 2002/02/01 22:01:04 davem Exp $
- *
- * Missing: IPv6 support.
*/

#include <linux/tcp.h>
@@ -23,14 +21,15 @@

extern int sysctl_tcp_syncookies;

-static __u32 syncookie_secret[2][16-3+SHA_DIGEST_WORDS];
+__u32 syncookie_secret[2][16-3+SHA_DIGEST_WORDS];
+EXPORT_SYMBOL(syncookie_secret);

static __init int init_syncookies(void)
{
get_random_bytes(syncookie_secret, sizeof(syncookie_secret));
return 0;
}
-module_init(init_syncookies);
+__initcall(init_syncookies);

#define COOKIEBITS 24 /* Upper bits store count */
#define COOKIEMASK (((__u32)1 << COOKIEBITS) - 1)
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index b39f0d8..5180c7d 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -5207,6 +5207,7 @@ discard:

EXPORT_SYMBOL(sysctl_tcp_ecn);
EXPORT_SYMBOL(sysctl_tcp_reordering);
+EXPORT_SYMBOL(sysctl_tcp_adv_win_scale);
EXPORT_SYMBOL(tcp_parse_options);
EXPORT_SYMBOL(tcp_rcv_established);
EXPORT_SYMBOL(tcp_rcv_state_process);
diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
index b61b768..0f494cd 100644
--- a/net/ipv4/tcp_minisocks.c
+++ b/net/ipv4/tcp_minisocks.c
@@ -35,6 +35,8 @@
#endif

int sysctl_tcp_syncookies __read_mostly = SYNC_INIT;
+EXPORT_SYMBOL(sysctl_tcp_syncookies);
+
int sysctl_tcp_abort_on_overflow __read_mostly;

struct inet_timewait_death_row tcp_death_row = {
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index f4c1eef..613c64b 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -2591,6 +2591,7 @@ void tcp_send_probe0(struct sock *sk)
}
}

+EXPORT_SYMBOL(tcp_select_initial_window);
EXPORT_SYMBOL(tcp_connect);
EXPORT_SYMBOL(tcp_make_synack);
EXPORT_SYMBOL(tcp_simple_retransmit);
diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile
index 87c23a7..d1a1056 100644
--- a/net/ipv6/Makefile
+++ b/net/ipv6/Makefile
@@ -15,6 +15,7 @@ ipv6-$(CONFIG_XFRM) += xfrm6_policy.o xfrm6_state.o xfrm6_input.o \
ipv6-$(CONFIG_NETFILTER) += netfilter.o
ipv6-$(CONFIG_IPV6_MULTIPLE_TABLES) += fib6_rules.o
ipv6-$(CONFIG_PROC_FS) += proc.o
+ipv6-$(CONFIG_SYN_COOKIES) += syncookies.o

ipv6-objs += $(ipv6-y)

diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c
new file mode 100644
index 0000000..cef5d39
--- /dev/null
+++ b/net/ipv6/syncookies.c
@@ -0,0 +1,265 @@
+/*
+ * IPv6 Syncookies implementation for the Linux kernel
+ *
+ * Authors:
+ * Glenn Griffin <[email protected]>
+ *
+ * Based on IPv4 implementation by Andi Kleen
+ * linux/net/ipv4/syncookies.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <linux/tcp.h>
+#include <linux/random.h>
+#include <linux/cryptohash.h>
+#include <linux/kernel.h>
+#include <net/ipv6.h>
+#include <net/tcp.h>
+
+extern int sysctl_tcp_syncookies;
+extern __u32 syncookie_secret[2][16-3+SHA_DIGEST_WORDS];
+
+#define COOKIEBITS 24 /* Upper bits store count */
+#define COOKIEMASK (((__u32)1 << COOKIEBITS) - 1)
+
+/*
+ * This table has to be sorted and terminated with (__u16)-1.
+ * XXX generate a better table.
+ * Unresolved Issues: HIPPI with a 64k MSS is not well supported.
+ *
+ * Taken directly from ipv4 implementation.
+ * Should this list be modified for ipv6 use or is it close enough?
+ * rfc 2460 8.3 suggests mss values 20 bytes less than ipv4 counterpart
+ */
+static __u16 const msstab[] = {
+ 64 - 1,
+ 256 - 1,
+ 512 - 1,
+ 536 - 1,
+ 1024 - 1,
+ 1440 - 1,
+ 1460 - 1,
+ 4312 - 1,
+ (__u16)-1
+};
+/* The number doesn't include the -1 terminator */
+#define NUM_MSS (ARRAY_SIZE(msstab) - 1)
+
+/*
+ * This (misnamed) value is the age of syncookie which is permitted.
+ * Its ideal value should be dependent on TCP_TIMEOUT_INIT and
+ * sysctl_tcp_retries1. It's a rather complicated formula (exponential
+ * backoff) to compute at runtime so it's currently hardcoded here.
+ */
+#define COUNTER_TRIES 4
+
+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 u32 cookie_hash(struct in6_addr *saddr, struct in6_addr *daddr,
+ __be16 sport, __be16 dport, u32 count, int c)
+{
+ __u32 tmp[16 + 5 + SHA_WORKSPACE_WORDS];
+
+ /*
+ * we have 320 bits of information to hash, copy in the remaining
+ * 192 bits required for sha_transform, from the syncookie_secret
+ * and overwrite the digest with the secret
+ */
+ memcpy(tmp + 10, syncookie_secret[c], 44);
+ memcpy(tmp, saddr, 16);
+ memcpy(tmp + 4, daddr, 16);
+ tmp[8] = ((__force u32)sport << 16) + (__force u32)dport;
+ tmp[9] = count;
+ sha_transform(tmp + 16, (__u8 *)tmp, tmp + 16 + 5);
+
+ return tmp[17];
+}
+
+static __u32 secure_tcp_syn_cookie(struct in6_addr *saddr, struct in6_addr *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));
+}
+
+static __u32 check_tcp_syn_cookie(__u32 cookie, struct in6_addr *saddr,
+ struct in6_addr *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;
+}
+
+__u32 cookie_v6_init_sequence(struct sock *sk, struct sk_buff *skb, __u16 *mssp)
+{
+ struct ipv6hdr *iph = ipv6_hdr(skb);
+ const struct tcphdr *th = tcp_hdr(skb);
+ int mssind;
+ const __u16 mss = *mssp;
+
+ tcp_sk(sk)->last_synq_overflow = jiffies;
+
+ for (mssind = 0; mss > msstab[mssind + 1]; mssind++)
+ ;
+ *mssp = msstab[mssind] + 1;
+
+ NET_INC_STATS_BH(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 int cookie_check(struct sk_buff *skb, __u32 cookie)
+{
+ struct ipv6hdr *iph = ipv6_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 < NUM_MSS ? msstab[mssind] + 1 : 0;
+}
+
+struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
+{
+ struct inet_request_sock *ireq;
+ struct inet6_request_sock *ireq6;
+ struct tcp_request_sock *treq;
+ struct ipv6_pinfo *np = inet6_sk(sk);
+ 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 dst_entry *dst;
+ __u8 rcv_wscale;
+
+ if (!sysctl_tcp_syncookies || !th->ack)
+ goto out;
+
+ if (time_after(jiffies, tp->last_synq_overflow + TCP_TIMEOUT_INIT) ||
+ (mss = cookie_check(skb, cookie)) == 0) {
+ NET_INC_STATS_BH(LINUX_MIB_SYNCOOKIESFAILED);
+ goto out;
+ }
+
+ NET_INC_STATS_BH(LINUX_MIB_SYNCOOKIESRECV);
+
+ ret = NULL;
+ req = inet6_reqsk_alloc(&tcp6_request_sock_ops);
+ if (!req)
+ goto out;
+
+ ireq = inet_rsk(req);
+ ireq6 = inet6_rsk(req);
+ treq = tcp_rsk(req);
+ ireq6->pktopts = NULL;
+
+ if (security_inet_conn_request(sk, skb, req)) {
+ reqsk_free(req);
+ goto out;
+ }
+
+ req->mss = mss;
+ ireq->rmt_port = th->source;
+ ipv6_addr_copy(&ireq6->rmt_addr, &ipv6_hdr(skb)->saddr);
+ ipv6_addr_copy(&ireq6->loc_addr, &ipv6_hdr(skb)->daddr);
+ if (ipv6_opt_accepted(sk, skb) ||
+ np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo ||
+ np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim) {
+ atomic_inc(&skb->users);
+ ireq6->pktopts = skb;
+ }
+
+ ireq6->iif = sk->sk_bound_dev_if;
+ /* So that link locals have meaning */
+ if (!sk->sk_bound_dev_if &&
+ ipv6_addr_type(&ireq6->rmt_addr) & IPV6_ADDR_LINKLOCAL)
+ ireq6->iif = inet6_iif(skb);
+
+ req->expires = 0UL;
+ req->retrans = 0;
+ ireq->snd_wscale = ireq->rcv_wscale = ireq->tstamp_ok = 0;
+ ireq->wscale_ok = ireq->sack_ok = 0;
+ treq->rcv_isn = ntohl(th->seq) - 1;
+ treq->snt_isn = cookie;
+
+ /*
+ * We need to lookup the dst_entry to get the correct window size.
+ * This is taken from tcp_v6_syn_recv_sock. Somebody please enlighten
+ * me if there is a preferred way.
+ */
+ {
+ struct in6_addr *final_p = NULL, final;
+ struct flowi fl;
+ memset(&fl, 0, sizeof(fl));
+ fl.proto = IPPROTO_TCP;
+ ipv6_addr_copy(&fl.fl6_dst, &ireq6->rmt_addr);
+ if (np->opt && np->opt->srcrt) {
+ struct rt0_hdr *rt0 = (struct rt0_hdr *) np->opt->srcrt;
+ ipv6_addr_copy(&final, &fl.fl6_dst);
+ ipv6_addr_copy(&fl.fl6_dst, rt0->addr);
+ final_p = &final;
+ }
+ ipv6_addr_copy(&fl.fl6_src, &ireq6->loc_addr);
+ fl.oif = sk->sk_bound_dev_if;
+ fl.fl_ip_dport = inet_rsk(req)->rmt_port;
+ fl.fl_ip_sport = inet_sk(sk)->sport;
+ security_req_classify_flow(req, &fl);
+ if (ip6_dst_lookup(sk, &dst, &fl)) {
+ reqsk_free(req);
+ goto out;
+ }
+ if (final_p)
+ ipv6_addr_copy(&fl.fl6_dst, final_p);
+ if ((xfrm_lookup(&dst, &fl, sk, 0)) < 0)
+ goto out;
+ }
+
+ req->window_clamp = dst_metric(dst, RTAX_WINDOW);
+ tcp_select_initial_window(tcp_full_space(sk), req->mss,
+ &req->rcv_wnd, &req->window_clamp,
+ 0, &rcv_wscale);
+
+ ireq->rcv_wscale = rcv_wscale;
+
+ ret = get_cookie_sock(sk, skb, req, dst);
+
+out: return ret;
+}
+
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 93980c3..ad39bd1 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -520,6 +520,20 @@ done:
return err;
}

+static inline void syn_flood_warning(struct sk_buff *skb)
+{
+#ifdef CONFIG_SYN_COOKIES
+ if (sysctl_tcp_syncookies)
+ printk(KERN_INFO
+ "TCPv6: Possible SYN flooding on port %d. "
+ "Sending cookies.\n", ntohs(tcp_hdr(skb)->dest));
+ else
+#endif
+ printk(KERN_INFO
+ "TCPv6: Possible SYN flooding on port %d. "
+ "Dropping request.\n", ntohs(tcp_hdr(skb)->dest));
+}
+
static void tcp_v6_reqsk_destructor(struct request_sock *req)
{
if (inet6_rsk(req)->pktopts)
@@ -923,7 +937,7 @@ done_opts:
}
#endif

-static struct request_sock_ops tcp6_request_sock_ops __read_mostly = {
+struct request_sock_ops tcp6_request_sock_ops __read_mostly = {
.family = AF_INET6,
.obj_size = sizeof(struct tcp6_request_sock),
.rtx_syn_ack = tcp_v6_send_synack,
@@ -1221,9 +1235,9 @@ static struct sock *tcp_v6_hnd_req(struct sock *sk,struct sk_buff *skb)
return NULL;
}

-#if 0 /*def CONFIG_SYN_COOKIES*/
+#ifdef CONFIG_SYN_COOKIES
if (!th->rst && !th->syn && th->ack)
- sk = cookie_v6_check(sk, skb, &(IPCB(skb)->opt));
+ sk = cookie_v6_check(sk, skb);
#endif
return sk;
}
@@ -1239,6 +1253,11 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
struct tcp_sock *tp = tcp_sk(sk);
struct request_sock *req = NULL;
__u32 isn = TCP_SKB_CB(skb)->when;
+#ifdef CONFIG_SYN_COOKIES
+ int want_cookie = 0;
+#else
+#define want_cookie 0
+#endif

if (skb->protocol == htons(ETH_P_IP))
return tcp_v4_conn_request(sk, skb);
@@ -1246,12 +1265,14 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
if (!ipv6_unicast_destination(skb))
goto drop;

- /*
- * There are no SYN attacks on IPv6, yet...
- */
if (inet_csk_reqsk_queue_is_full(sk) && !isn) {
if (net_ratelimit())
- printk(KERN_INFO "TCPv6: dropping request, synflood is possible\n");
+ syn_flood_warning(skb);
+#ifdef CONFIG_SYN_COOKIES
+ if (sysctl_tcp_syncookies)
+ want_cookie = 1;
+ else
+#endif
goto drop;
}

@@ -1272,29 +1293,39 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)

tcp_parse_options(skb, &tmp_opt, 0);

+ if (want_cookie) {
+ tcp_clear_options(&tmp_opt);
+ tmp_opt.saw_tstamp = 0;
+ }
+
tmp_opt.tstamp_ok = tmp_opt.saw_tstamp;
tcp_openreq_init(req, &tmp_opt, skb);

treq = inet6_rsk(req);
ipv6_addr_copy(&treq->rmt_addr, &ipv6_hdr(skb)->saddr);
ipv6_addr_copy(&treq->loc_addr, &ipv6_hdr(skb)->daddr);
- TCP_ECN_create_request(req, tcp_hdr(skb));
treq->pktopts = NULL;
- if (ipv6_opt_accepted(sk, skb) ||
- np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo ||
- np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim) {
- atomic_inc(&skb->users);
- treq->pktopts = skb;
- }
- treq->iif = sk->sk_bound_dev_if;
+ if (!want_cookie)
+ TCP_ECN_create_request(req, tcp_hdr(skb));
+
+ if (want_cookie) {
+ isn = cookie_v6_init_sequence(sk, skb, &req->mss);
+ } else if (!isn) {
+ if (ipv6_opt_accepted(sk, skb) ||
+ np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo ||
+ np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim) {
+ atomic_inc(&skb->users);
+ treq->pktopts = skb;
+ }
+ treq->iif = sk->sk_bound_dev_if;

- /* So that link locals have meaning */
- if (!sk->sk_bound_dev_if &&
- ipv6_addr_type(&treq->rmt_addr) & IPV6_ADDR_LINKLOCAL)
- treq->iif = inet6_iif(skb);
+ /* So that link locals have meaning */
+ if (!sk->sk_bound_dev_if &&
+ ipv6_addr_type(&treq->rmt_addr) & IPV6_ADDR_LINKLOCAL)
+ treq->iif = inet6_iif(skb);

- if (isn == 0)
isn = tcp_v6_init_sequence(skb);
+ }

tcp_rsk(req)->snt_isn = isn;

@@ -1303,8 +1334,10 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
if (tcp_v6_send_synack(sk, req, NULL))
goto drop;

- inet6_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT);
- return 0;
+ if (!want_cookie) {
+ inet6_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT);
+ return 0;
+ }

drop:
if (req)
--
1.5.3.4

2008-02-07 07:25:15

by Evgeniy Polyakov

[permalink] [raw]
Subject: Re: [PATCH] Add IPv6 support to TCP SYN cookies

On Wed, Feb 06, 2008 at 10:30:24AM -0800, Glenn Griffin ([email protected]) wrote:
> > > +static u32 cookie_hash(struct in6_addr *saddr, struct in6_addr *daddr,
> > > + __be16 sport, __be16 dport, u32 count, int c)
> > > +{
> > > + __u32 tmp[16 + 5 + SHA_WORKSPACE_WORDS];
> >
> > This huge buffer should not be allocated on stack.
>
> I can replace it will a kmalloc, but for my benefit what's the practical
> size we try and limit the stack to? It seemed at first glance to me
> that 404 bytes plus the arguments, etc. was not such a large buffer for
> a non-recursive function. Plus the alternative with a kmalloc requires

Well, maybe for connection establishment path it is not, but it is
absolutely the case in the sending and sometimes receiving pathes for 4k
stacks. The main problem is that bugs which happen because of stack
overflow are so much obscure, that it is virtually impossible to detect
where overflow happend. 'Debug stack overflow' somehow does not help to
detect it.

Usually there is about 1-1.5 kb of free stack for each process, so this
change will cut one third of the free stack, getting into account that
something can store ipv6 addresses on stack too, this can end up badly.

> propogating the possible error status back up to tcp_ipv6.c in the event
> we are unable to allocate enough memory, so it can simply drop the
> connection. Not an impossible task by any means but it does
> significantly complicate things and I would like to know it's worth the
> effort. Also would it be worth it to provide a supplemental patch for
> the ipv4 implementation as it allocates the same buffer?

One can reorganize syncookie support to work with request hash tables
too, so that we could allocate per hash-bucket space and use it as a
scratchpad for cookies.

> --Glenn

--
Evgeniy Polyakov

2008-02-07 09:40:51

by Eric Dumazet

[permalink] [raw]
Subject: Re: [PATCH] Add IPv6 support to TCP SYN cookies

diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c
index f470fe4..177da14 100644
--- a/net/ipv4/syncookies.c
+++ b/net/ipv4/syncookies.c
@@ -35,10 +35,12 @@ module_init(init_syncookies);
#define COOKIEBITS 24 /* Upper bits store count */
#define COOKIEMASK (((__u32)1 << COOKIEBITS) - 1)

+static DEFINE_PER_CPU(__u32, cookie_scratch)[16 + 5 + SHA_WORKSPACE_WORDS];
+
static u32 cookie_hash(__be32 saddr, __be32 daddr, __be16 sport, __be16 dport,
u32 count, int c)
{
- __u32 tmp[16 + 5 + SHA_WORKSPACE_WORDS];
+ __u32 *tmp = __get_cpu_var(cookie_scratch);

memcpy(tmp + 3, syncookie_secret[c], sizeof(syncookie_secret[c]));
tmp[0] = (__force u32)saddr;


Attachments:
cookie_scratch.patch (681.00 B)

2008-02-07 19:46:20

by Ross Vandegrift

[permalink] [raw]
Subject: Re: [PATCH] Add IPv6 support to TCP SYN cookies

On Wed, Feb 06, 2008 at 09:53:57AM +0100, Andi Kleen wrote:
> That would be useful yes -- for different bandwidths.

My initial test is end-to-end 1000Mbps, but I've got a few different
packet rates.

> If the young/old heuristics do not work well enough anymore most
> likely we should try readding RED to the syn queue again. That used
> to be pretty effective in the early days. I don't quite remember why
> Linux didn't end up using it in fact.

I'm running juno-z with 2, 4, & 8 threads of syn flood to port 80.
wireshark measures 2 threads at 350pps, 4 threads at 750pps, and 8
threads at 1200pps. Under no SYN flood, the server handles 750 HTTP
requests per second, measured via httping in flood mode.

With a default tcp_max_syn_backlog of 1024, I can trivially prevent
any inbound client connections with 2 threads of syn flood.
Enabling tcp_syncookies brings the connection handling back up to 725
fetches per second.

If I raise the backlog to 16384, 4 threads gives me about 650 legit
requests per sec. Going to 8 threads makes connections very unreliable - a
handful will get through every 15 to 20 seconds. Again,
tcp_syncookies returns performance almost totally back to normal.

Cranking juno-z to the max generates me about 16kpps. Any syn backlog
is easily overwhelmed and nothing gets through. tcp_syncookies gets
me back to 650 requests per second.

At these levels the CPU impact of tcp_syncookies is nothing. I can't
measure a difference. In the real world, a 16kpps syn flood is small.
People with a distributed botnet can easily get to the hundreds of
thousands, and I have seen over million packets per second of SYN flood.


BTW, I can trigger a soft lockup BUG when I restart apache to change the
backlog during the 16kpps test-case:

BUG: soft lockup detected on CPU#1!
[<c044d1ec>] softlockup_tick+0x96/0xa4
[<c042ddb0>] update_process_times+0x39/0x5c
[<c04196f7>] smp_apic_timer_interrupt+0x5b/0x6c
[<c04059bf>] apic_timer_interrupt+0x1f/0x24
[<c045007b>] taskstats_exit_send+0x152/0x371
[<c05c007b>] netlink_kernel_create+0x5/0x11c
[<c05a7415>] reqsk_queue_alloc+0x32/0x81
[<c05a5aca>] lock_sock+0x8e/0x96
[<c05ce8c4>] inet_csk_listen_start+0x17/0x106
[<c05e720f>] inet_listen+0x3c/0x5f
[<c05a3e55>] sys_listen+0x4a/0x66
[<c05a4f4d>] sys_socketcall+0x98/0x19e
[<c0407ef7>] do_syscall_trace+0xab/0xb1
[<c0404eff>] syscall_call+0x7/0xb
=======================
BUG: soft lockup detected on CPU#3!
[<c044d1ec>] softlockup_tick+0x96/0xa4
[<c042ddb0>] update_process_times+0x39/0x5c
[<c04196f7>] smp_apic_timer_interrupt+0x5b/0x6c
[<c04059bf>] apic_timer_interrupt+0x1f/0x24
[<c045007b>] taskstats_exit_send+0x152/0x371
[<c05c007b>] netlink_kernel_create+0x5/0x11c
[<c05a7415>] reqsk_queue_alloc+0x32/0x81
[<c05a5aca>] lock_sock+0x8e/0x96
[<c05ce8c4>] inet_csk_listen_start+0x17/0x106
[<c05e720f>] inet_listen+0x3c/0x5f
[<c05a3e55>] sys_listen+0x4a/0x66
[<c05a4f4d>] sys_socketcall+0x98/0x19e
[<c0407ef7>] do_syscall_trace+0xab/0xb1
[<c0404eff>] syscall_call+0x7/0xb
=======================







--
Ross Vandegrift
[email protected]

"The good Christian should beware of mathematicians, and all those who
make empty prophecies. The danger already exists that the mathematicians
have made a covenant with the devil to darken the spirit and to confine
man in the bonds of Hell."
--St. Augustine, De Genesi ad Litteram, Book II, xviii, 37

2008-02-08 05:25:26

by Glenn Griffin

[permalink] [raw]
Subject: Re: [PATCH] Add IPv6 support to TCP SYN cookies

> Or maybe use percpu storage for that...

That seems like a good approach. I'll incorporate it into my v6 patch,
and send out an update. Thanks.

> I am not sure if cookie_hash() is always called with preemption disabled.
> (If not, we have to use get_cpu_var()/put_cpu_var())

cookie_hash is always called within NET_RX_SOFTIRQ context so I believe
preemption will always be disabled by __do_softirq(). So there
shouldn't be a need to use get_cpu_var/put_cpu_var, somebody correct me
if I'm wrong.

--Glenn

2008-02-08 05:42:22

by Glenn Griffin

[permalink] [raw]
Subject: Re: [PATCH] Add IPv6 support to TCP SYN cookies

Updated to incorporate Eric's suggestion of using a per cpu buffer
rather than allocating on the stack. Just a two line change, but will
resend in it's entirety.

Signed-off-by: Glenn Griffin <[email protected]>
---
include/net/tcp.h | 8 ++
net/ipv4/syncookies.c | 7 +-
net/ipv4/tcp_input.c | 1 +
net/ipv4/tcp_minisocks.c | 2 +
net/ipv4/tcp_output.c | 1 +
net/ipv6/Makefile | 1 +
net/ipv6/syncookies.c | 267 ++++++++++++++++++++++++++++++++++++++++++++++
net/ipv6/tcp_ipv6.c | 77 ++++++++++----
8 files changed, 338 insertions(+), 26 deletions(-)
create mode 100644 net/ipv6/syncookies.c

diff --git a/include/net/tcp.h b/include/net/tcp.h
index 7de4ea3..c428ec7 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -29,6 +29,7 @@
#include <linux/skbuff.h>
#include <linux/dmaengine.h>
#include <linux/crypto.h>
+#include <linux/cryptohash.h>

#include <net/inet_connection_sock.h>
#include <net/inet_timewait_sock.h>
@@ -434,11 +435,17 @@ extern int tcp_disconnect(struct sock *sk, int flags);
extern void tcp_unhash(struct sock *sk);

/* From syncookies.c */
+extern __u32 syncookie_secret[2][16-3+SHA_DIGEST_WORDS];
extern struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb,
struct ip_options *opt);
extern __u32 cookie_v4_init_sequence(struct sock *sk, struct sk_buff *skb,
__u16 *mss);

+/* From net/ipv6/syncookies.c */
+extern struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb);
+extern __u32 cookie_v6_init_sequence(struct sock *sk, struct sk_buff *skb,
+ __u16 *mss);
+
/* tcp_output.c */

extern void __tcp_push_pending_frames(struct sock *sk, unsigned int cur_mss,
@@ -1332,6 +1339,7 @@ extern int tcp_proc_register(struct tcp_seq_afinfo *afinfo);
extern void tcp_proc_unregister(struct tcp_seq_afinfo *afinfo);

extern struct request_sock_ops tcp_request_sock_ops;
+extern struct request_sock_ops tcp6_request_sock_ops;

extern int tcp_v4_destroy_sock(struct sock *sk);

diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c
index f470fe4..cc6637b 100644
--- a/net/ipv4/syncookies.c
+++ b/net/ipv4/syncookies.c
@@ -10,8 +10,6 @@
* 2 of the License, or (at your option) any later version.
*
* $Id: syncookies.c,v 1.18 2002/02/01 22:01:04 davem Exp $
- *
- * Missing: IPv6 support.
*/

#include <linux/tcp.h>
@@ -23,14 +21,15 @@

extern int sysctl_tcp_syncookies;

-static __u32 syncookie_secret[2][16-3+SHA_DIGEST_WORDS];
+__u32 syncookie_secret[2][16-3+SHA_DIGEST_WORDS];
+EXPORT_SYMBOL(syncookie_secret);

static __init int init_syncookies(void)
{
get_random_bytes(syncookie_secret, sizeof(syncookie_secret));
return 0;
}
-module_init(init_syncookies);
+__initcall(init_syncookies);

#define COOKIEBITS 24 /* Upper bits store count */
#define COOKIEMASK (((__u32)1 << COOKIEBITS) - 1)
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 19c449f..93e128c 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -5326,6 +5326,7 @@ discard:

EXPORT_SYMBOL(sysctl_tcp_ecn);
EXPORT_SYMBOL(sysctl_tcp_reordering);
+EXPORT_SYMBOL(sysctl_tcp_adv_win_scale);
EXPORT_SYMBOL(tcp_parse_options);
EXPORT_SYMBOL(tcp_rcv_established);
EXPORT_SYMBOL(tcp_rcv_state_process);
diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
index b61b768..0f494cd 100644
--- a/net/ipv4/tcp_minisocks.c
+++ b/net/ipv4/tcp_minisocks.c
@@ -35,6 +35,8 @@
#endif

int sysctl_tcp_syncookies __read_mostly = SYNC_INIT;
+EXPORT_SYMBOL(sysctl_tcp_syncookies);
+
int sysctl_tcp_abort_on_overflow __read_mostly;

struct inet_timewait_death_row tcp_death_row = {
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index ed750f9..cbfef8b 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -2560,6 +2560,7 @@ void tcp_send_probe0(struct sock *sk)
}
}

+EXPORT_SYMBOL(tcp_select_initial_window);
EXPORT_SYMBOL(tcp_connect);
EXPORT_SYMBOL(tcp_make_synack);
EXPORT_SYMBOL(tcp_simple_retransmit);
diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile
index 24f3aa0..ae14617 100644
--- a/net/ipv6/Makefile
+++ b/net/ipv6/Makefile
@@ -16,6 +16,7 @@ ipv6-$(CONFIG_XFRM) += xfrm6_policy.o xfrm6_state.o xfrm6_input.o \
ipv6-$(CONFIG_NETFILTER) += netfilter.o
ipv6-$(CONFIG_IPV6_MULTIPLE_TABLES) += fib6_rules.o
ipv6-$(CONFIG_PROC_FS) += proc.o
+ipv6-$(CONFIG_SYN_COOKIES) += syncookies.o

ipv6-objs += $(ipv6-y)

diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c
new file mode 100644
index 0000000..063dade
--- /dev/null
+++ b/net/ipv6/syncookies.c
@@ -0,0 +1,267 @@
+/*
+ * IPv6 Syncookies implementation for the Linux kernel
+ *
+ * Authors:
+ * Glenn Griffin <[email protected]>
+ *
+ * Based on IPv4 implementation by Andi Kleen
+ * linux/net/ipv4/syncookies.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <linux/tcp.h>
+#include <linux/random.h>
+#include <linux/cryptohash.h>
+#include <linux/kernel.h>
+#include <net/ipv6.h>
+#include <net/tcp.h>
+
+extern int sysctl_tcp_syncookies;
+extern __u32 syncookie_secret[2][16-3+SHA_DIGEST_WORDS];
+
+#define COOKIEBITS 24 /* Upper bits store count */
+#define COOKIEMASK (((__u32)1 << COOKIEBITS) - 1)
+
+/*
+ * This table has to be sorted and terminated with (__u16)-1.
+ * XXX generate a better table.
+ * Unresolved Issues: HIPPI with a 64k MSS is not well supported.
+ *
+ * Taken directly from ipv4 implementation.
+ * Should this list be modified for ipv6 use or is it close enough?
+ * rfc 2460 8.3 suggests mss values 20 bytes less than ipv4 counterpart
+ */
+static __u16 const msstab[] = {
+ 64 - 1,
+ 256 - 1,
+ 512 - 1,
+ 536 - 1,
+ 1024 - 1,
+ 1440 - 1,
+ 1460 - 1,
+ 4312 - 1,
+ (__u16)-1
+};
+/* The number doesn't include the -1 terminator */
+#define NUM_MSS (ARRAY_SIZE(msstab) - 1)
+
+/*
+ * This (misnamed) value is the age of syncookie which is permitted.
+ * Its ideal value should be dependent on TCP_TIMEOUT_INIT and
+ * sysctl_tcp_retries1. It's a rather complicated formula (exponential
+ * backoff) to compute at runtime so it's currently hardcoded here.
+ */
+#define COUNTER_TRIES 4
+
+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 DEFINE_PER_CPU(__u32, cookie_scratch)[16 + 5 + SHA_WORKSPACE_WORDS];
+
+static u32 cookie_hash(struct in6_addr *saddr, struct in6_addr *daddr,
+ __be16 sport, __be16 dport, u32 count, int c)
+{
+ __u32 *tmp = __get_cpu_var(cookie_scratch);
+
+ /*
+ * we have 320 bits of information to hash, copy in the remaining
+ * 192 bits required for sha_transform, from the syncookie_secret
+ * and overwrite the digest with the secret
+ */
+ memcpy(tmp + 10, syncookie_secret[c], 44);
+ memcpy(tmp, saddr, 16);
+ memcpy(tmp + 4, daddr, 16);
+ tmp[8] = ((__force u32)sport << 16) + (__force u32)dport;
+ tmp[9] = count;
+ sha_transform(tmp + 16, (__u8 *)tmp, tmp + 16 + 5);
+
+ return tmp[17];
+}
+
+static __u32 secure_tcp_syn_cookie(struct in6_addr *saddr, struct in6_addr *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));
+}
+
+static __u32 check_tcp_syn_cookie(__u32 cookie, struct in6_addr *saddr,
+ struct in6_addr *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;
+}
+
+__u32 cookie_v6_init_sequence(struct sock *sk, struct sk_buff *skb, __u16 *mssp)
+{
+ struct ipv6hdr *iph = ipv6_hdr(skb);
+ const struct tcphdr *th = tcp_hdr(skb);
+ int mssind;
+ const __u16 mss = *mssp;
+
+ tcp_sk(sk)->last_synq_overflow = jiffies;
+
+ for (mssind = 0; mss > msstab[mssind + 1]; mssind++)
+ ;
+ *mssp = msstab[mssind] + 1;
+
+ NET_INC_STATS_BH(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 int cookie_check(struct sk_buff *skb, __u32 cookie)
+{
+ struct ipv6hdr *iph = ipv6_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 < NUM_MSS ? msstab[mssind] + 1 : 0;
+}
+
+struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
+{
+ struct inet_request_sock *ireq;
+ struct inet6_request_sock *ireq6;
+ struct tcp_request_sock *treq;
+ struct ipv6_pinfo *np = inet6_sk(sk);
+ 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 dst_entry *dst;
+ __u8 rcv_wscale;
+
+ if (!sysctl_tcp_syncookies || !th->ack)
+ goto out;
+
+ if (time_after(jiffies, tp->last_synq_overflow + TCP_TIMEOUT_INIT) ||
+ (mss = cookie_check(skb, cookie)) == 0) {
+ NET_INC_STATS_BH(LINUX_MIB_SYNCOOKIESFAILED);
+ goto out;
+ }
+
+ NET_INC_STATS_BH(LINUX_MIB_SYNCOOKIESRECV);
+
+ ret = NULL;
+ req = inet6_reqsk_alloc(&tcp6_request_sock_ops);
+ if (!req)
+ goto out;
+
+ ireq = inet_rsk(req);
+ ireq6 = inet6_rsk(req);
+ treq = tcp_rsk(req);
+ ireq6->pktopts = NULL;
+
+ if (security_inet_conn_request(sk, skb, req)) {
+ reqsk_free(req);
+ goto out;
+ }
+
+ req->mss = mss;
+ ireq->rmt_port = th->source;
+ ipv6_addr_copy(&ireq6->rmt_addr, &ipv6_hdr(skb)->saddr);
+ ipv6_addr_copy(&ireq6->loc_addr, &ipv6_hdr(skb)->daddr);
+ if (ipv6_opt_accepted(sk, skb) ||
+ np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo ||
+ np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim) {
+ atomic_inc(&skb->users);
+ ireq6->pktopts = skb;
+ }
+
+ ireq6->iif = sk->sk_bound_dev_if;
+ /* So that link locals have meaning */
+ if (!sk->sk_bound_dev_if &&
+ ipv6_addr_type(&ireq6->rmt_addr) & IPV6_ADDR_LINKLOCAL)
+ ireq6->iif = inet6_iif(skb);
+
+ req->expires = 0UL;
+ req->retrans = 0;
+ ireq->snd_wscale = ireq->rcv_wscale = ireq->tstamp_ok = 0;
+ ireq->wscale_ok = ireq->sack_ok = 0;
+ treq->rcv_isn = ntohl(th->seq) - 1;
+ treq->snt_isn = cookie;
+
+ /*
+ * We need to lookup the dst_entry to get the correct window size.
+ * This is taken from tcp_v6_syn_recv_sock. Somebody please enlighten
+ * me if there is a preferred way.
+ */
+ {
+ struct in6_addr *final_p = NULL, final;
+ struct flowi fl;
+ memset(&fl, 0, sizeof(fl));
+ fl.proto = IPPROTO_TCP;
+ ipv6_addr_copy(&fl.fl6_dst, &ireq6->rmt_addr);
+ if (np->opt && np->opt->srcrt) {
+ struct rt0_hdr *rt0 = (struct rt0_hdr *) np->opt->srcrt;
+ ipv6_addr_copy(&final, &fl.fl6_dst);
+ ipv6_addr_copy(&fl.fl6_dst, rt0->addr);
+ final_p = &final;
+ }
+ ipv6_addr_copy(&fl.fl6_src, &ireq6->loc_addr);
+ fl.oif = sk->sk_bound_dev_if;
+ fl.fl_ip_dport = inet_rsk(req)->rmt_port;
+ fl.fl_ip_sport = inet_sk(sk)->sport;
+ security_req_classify_flow(req, &fl);
+ if (ip6_dst_lookup(sk, &dst, &fl)) {
+ reqsk_free(req);
+ goto out;
+ }
+ if (final_p)
+ ipv6_addr_copy(&fl.fl6_dst, final_p);
+ if ((xfrm_lookup(&dst, &fl, sk, 0)) < 0)
+ goto out;
+ }
+
+ req->window_clamp = dst_metric(dst, RTAX_WINDOW);
+ tcp_select_initial_window(tcp_full_space(sk), req->mss,
+ &req->rcv_wnd, &req->window_clamp,
+ 0, &rcv_wscale);
+
+ ireq->rcv_wscale = rcv_wscale;
+
+ ret = get_cookie_sock(sk, skb, req, dst);
+
+out: return ret;
+}
+
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 12750f2..1f4e544 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -514,6 +514,20 @@ done:
return err;
}

+static inline void syn_flood_warning(struct sk_buff *skb)
+{
+#ifdef CONFIG_SYN_COOKIES
+ if (sysctl_tcp_syncookies)
+ printk(KERN_INFO
+ "TCPv6: Possible SYN flooding on port %d. "
+ "Sending cookies.\n", ntohs(tcp_hdr(skb)->dest));
+ else
+#endif
+ printk(KERN_INFO
+ "TCPv6: Possible SYN flooding on port %d. "
+ "Dropping request.\n", ntohs(tcp_hdr(skb)->dest));
+}
+
static void tcp_v6_reqsk_destructor(struct request_sock *req)
{
if (inet6_rsk(req)->pktopts)
@@ -917,7 +931,7 @@ done_opts:
}
#endif

-static struct request_sock_ops tcp6_request_sock_ops __read_mostly = {
+struct request_sock_ops tcp6_request_sock_ops __read_mostly = {
.family = AF_INET6,
.obj_size = sizeof(struct tcp6_request_sock),
.rtx_syn_ack = tcp_v6_send_synack,
@@ -1215,9 +1229,9 @@ static struct sock *tcp_v6_hnd_req(struct sock *sk,struct sk_buff *skb)
return NULL;
}

-#if 0 /*def CONFIG_SYN_COOKIES*/
+#ifdef CONFIG_SYN_COOKIES
if (!th->rst && !th->syn && th->ack)
- sk = cookie_v6_check(sk, skb, &(IPCB(skb)->opt));
+ sk = cookie_v6_check(sk, skb);
#endif
return sk;
}
@@ -1233,6 +1247,11 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
struct tcp_sock *tp = tcp_sk(sk);
struct request_sock *req = NULL;
__u32 isn = TCP_SKB_CB(skb)->when;
+#ifdef CONFIG_SYN_COOKIES
+ int want_cookie = 0;
+#else
+#define want_cookie 0
+#endif

if (skb->protocol == htons(ETH_P_IP))
return tcp_v4_conn_request(sk, skb);
@@ -1240,12 +1259,14 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
if (!ipv6_unicast_destination(skb))
goto drop;

- /*
- * There are no SYN attacks on IPv6, yet...
- */
if (inet_csk_reqsk_queue_is_full(sk) && !isn) {
if (net_ratelimit())
- printk(KERN_INFO "TCPv6: dropping request, synflood is possible\n");
+ syn_flood_warning(skb);
+#ifdef CONFIG_SYN_COOKIES
+ if (sysctl_tcp_syncookies)
+ want_cookie = 1;
+ else
+#endif
goto drop;
}

@@ -1266,29 +1287,39 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)

tcp_parse_options(skb, &tmp_opt, 0);

+ if (want_cookie) {
+ tcp_clear_options(&tmp_opt);
+ tmp_opt.saw_tstamp = 0;
+ }
+
tmp_opt.tstamp_ok = tmp_opt.saw_tstamp;
tcp_openreq_init(req, &tmp_opt, skb);

treq = inet6_rsk(req);
ipv6_addr_copy(&treq->rmt_addr, &ipv6_hdr(skb)->saddr);
ipv6_addr_copy(&treq->loc_addr, &ipv6_hdr(skb)->daddr);
- TCP_ECN_create_request(req, tcp_hdr(skb));
treq->pktopts = NULL;
- if (ipv6_opt_accepted(sk, skb) ||
- np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo ||
- np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim) {
- atomic_inc(&skb->users);
- treq->pktopts = skb;
- }
- treq->iif = sk->sk_bound_dev_if;
+ if (!want_cookie)
+ TCP_ECN_create_request(req, tcp_hdr(skb));
+
+ if (want_cookie) {
+ isn = cookie_v6_init_sequence(sk, skb, &req->mss);
+ } else if (!isn) {
+ if (ipv6_opt_accepted(sk, skb) ||
+ np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo ||
+ np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim) {
+ atomic_inc(&skb->users);
+ treq->pktopts = skb;
+ }
+ treq->iif = sk->sk_bound_dev_if;

- /* So that link locals have meaning */
- if (!sk->sk_bound_dev_if &&
- ipv6_addr_type(&treq->rmt_addr) & IPV6_ADDR_LINKLOCAL)
- treq->iif = inet6_iif(skb);
+ /* So that link locals have meaning */
+ if (!sk->sk_bound_dev_if &&
+ ipv6_addr_type(&treq->rmt_addr) & IPV6_ADDR_LINKLOCAL)
+ treq->iif = inet6_iif(skb);

- if (isn == 0)
isn = tcp_v6_init_sequence(skb);
+ }

tcp_rsk(req)->snt_isn = isn;

@@ -1297,8 +1328,10 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
if (tcp_v6_send_synack(sk, req, NULL))
goto drop;

- inet6_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT);
- return 0;
+ if (!want_cookie) {
+ inet6_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT);
+ return 0;
+ }

drop:
if (req)
--
1.5.3.4

2008-02-08 11:32:55

by Andi Kleen

[permalink] [raw]
Subject: Re: [PATCH] Add IPv6 support to TCP SYN cookies

On Thu, Feb 07, 2008 at 02:44:46PM -0500, Ross Vandegrift wrote:
> On Wed, Feb 06, 2008 at 09:53:57AM +0100, Andi Kleen wrote:
> > That would be useful yes -- for different bandwidths.
>
> My initial test is end-to-end 1000Mbps, but I've got a few different
> packet rates.
>
> > If the young/old heuristics do not work well enough anymore most
> > likely we should try readding RED to the syn queue again. That used
> > to be pretty effective in the early days. I don't quite remember why
> > Linux didn't end up using it in fact.
>
> I'm running juno-z with 2, 4, & 8 threads of syn flood to port 80.
> wireshark measures 2 threads at 350pps, 4 threads at 750pps, and 8

What's the total bandwidth of the attack?

> threads at 1200pps. Under no SYN flood, the server handles 750 HTTP
> requests per second, measured via httping in flood mode.

Thanks for the tests. Could you please do an additional experiment?
Use sch_em or similar to add a jittering longer latency in the connection
(as would be realistic in a real distributed DOS). Does it make a
difference?

> With a default tcp_max_syn_backlog of 1024, I can trivially prevent
> any inbound client connections with 2 threads of syn flood.
> Enabling tcp_syncookies brings the connection handling back up to 725
> fetches per second.

Yes the defaults are probably too low. That's something that should
be fixed.

>
> At these levels the CPU impact of tcp_syncookies is nothing. I can't

CPU impact of syncookies was never a concern. The problems are rather
missing flow control and disabling of valuable TCP features.

> BUG: soft lockup detected on CPU#1!
> [<c044d1ec>] softlockup_tick+0x96/0xa4
> [<c042ddb0>] update_process_times+0x39/0x5c
> [<c04196f7>] smp_apic_timer_interrupt+0x5b/0x6c
> [<c04059bf>] apic_timer_interrupt+0x1f/0x24
> [<c045007b>] taskstats_exit_send+0x152/0x371
> [<c05c007b>] netlink_kernel_create+0x5/0x11c
> [<c05a7415>] reqsk_queue_alloc+0x32/0x81
> [<c05a5aca>] lock_sock+0x8e/0x96

I think the softirqs are starving user context through the socket
lock. Probably should be fixed too. Something like softirq should
detect when there is a user and it is looping too long and should
give up the lock for some time.


-Andi

2008-02-11 16:07:23

by YOSHIFUJI Hideaki

[permalink] [raw]
Subject: Re: [PATCH] Add IPv6 support to TCP SYN cookies

In article <[email protected]> (at Thu, 7 Feb 2008 21:49:26 -0800), Glenn Griffin <[email protected]> says:

> Updated to incorporate Eric's suggestion of using a per cpu buffer
> rather than allocating on the stack. Just a two line change, but will
> resend in it's entirety.
>
> Signed-off-by: Glenn Griffin <[email protected]>

Applied in my linux-2.6-dev tree. Thanks.

--yoshfuji

2008-02-12 20:40:27

by Ross Vandegrift

[permalink] [raw]
Subject: Re: [PATCH] Add IPv6 support to TCP SYN cookies

On Fri, Feb 08, 2008 at 01:07:46PM +0100, Andi Kleen wrote:
> On Thu, Feb 07, 2008 at 02:44:46PM -0500, Ross Vandegrift wrote:
> > On Wed, Feb 06, 2008 at 09:53:57AM +0100, Andi Kleen wrote:
> > My initial test is end-to-end 1000Mbps, but I've got a few different
> > packet rates.
> >
> > > If the young/old heuristics do not work well enough anymore most
> > > likely we should try readding RED to the syn queue again. That used
> > > to be pretty effective in the early days. I don't quite remember why
> > > Linux didn't end up using it in fact.
> >
> > I'm running juno-z with 2, 4, & 8 threads of syn flood to port 80.
> > wireshark measures 2 threads at 350pps, 4 threads at 750pps, and 8
>
> What's the total bandwidth of the attack?

Sorry for the delay in getting back to you on this Andi.

Here's a breakdown for each attack of the pps and bandwidth:

packets/s Mb/s

380 0.182
715 0.343
1193 0.572
16460 7.896

The first three tests were done with some fixed delay inbetween syn
floods. The last is done with all delays as small as possible.


> > threads at 1200pps. Under no SYN flood, the server handles 750 HTTP
> > requests per second, measured via httping in flood mode.
>
> Thanks for the tests. Could you please do an additional experiment?
> Use sch_em or similar to add a jittering longer latency in the connection
> (as would be realistic in a real distributed DOS). Does it make a
> difference?

Jitter on both ends makes it worse. Jitter only on the syn flooder
end behaves approximately the same.

If I add jitter to both the flooder and the target with:
tc qdisc add dev eth0 root netem delay 50ms 100ms distribution normal
I can kill off the host with even a single thread of syn flooding,
even with a backlog queue size of 32768.


> > With a default tcp_max_syn_backlog of 1024, I can trivially prevent
> > any inbound client connections with 2 threads of syn flood.
> > Enabling tcp_syncookies brings the connection handling back up to 725
> > fetches per second.
>
> Yes the defaults are probably too low. That's something that should
> be fixed.

Yea, with a longer queue, the server is somewhat more resiliant. But
it's still pretty easy to overwhelm it. syncookies is a night and day
difference.


> > At these levels the CPU impact of tcp_syncookies is nothing. I can't
>
> CPU impact of syncookies was never a concern. The problems are rather
> missing flow control and disabling of valuable TCP features.

Oh definitely - Willy raised the CPU issue in another mail, I was just
including my findings with a bigger CPU.

In general I agree with you for the TCP features, but in the cases where
we're enabling syncookies, it's the difference between bad connectivity and
no connectivity.

--
Ross Vandegrift
[email protected]

"The good Christian should beware of mathematicians, and all those who
make empty prophecies. The danger already exists that the mathematicians
have made a covenant with the devil to darken the spirit and to confine
man in the bonds of Hell."
--St. Augustine, De Genesi ad Litteram, Book II, xviii, 37

2008-02-13 07:30:39

by YOSHIFUJI Hideaki

[permalink] [raw]
Subject: Re: [PATCH] Add IPv6 support to TCP SYN cookies

In article <[email protected]> (at Thu, 07 Feb 2008 10:40:19 +0100), Eric Dumazet <[email protected]> says:

> [NET] IPV4: lower stack usage in cookie_hash() function
>
> 400 bytes allocated on stack might be a litle bit too much. Using a
> per_cpu var is more friendly.
>
> Signed-off-by: Eric Dumazet <[email protected]>

Applied to my inet6-2.6.26 tree. Thanks.

--yoshfuji

2008-02-18 23:37:20

by Glenn Griffin

[permalink] [raw]
Subject: Re: [PATCH] Add IPv6 support to TCP SYN cookies

I've posted a series of patches that I believe address Andi's concerns
about syncookies not supporting valuable tcp options (primarily SACK,
and window scaling). The premise being if the client support tcp
timestamps we can encode the additional tcp options in the initial
timestamp we send back to the client, and they will be echo'd back to us
in the ack. Anyone interested have a look, and provide any suggestions
you may have. The new patches are a superset of this patch, so if they
are accepted this is one obsolete.

Support arbitrary initial TCP timestamps
http://lkml.org/lkml/2008/2/15/244

Enable the use of TCP options with syncookies
http://lkml.org/lkml/2008/2/15/245

Add IPv6 Support to TCP SYN cookies
http://lkml.org/lkml/2008/2/15/246

--Glenn