From 68bdd82fbe0d11bdc613288f9b8cbe13ae56fca2 Mon Sep 17 00:00:00 2001 From: bluhm Date: Wed, 20 Jul 2016 09:15:28 +0000 Subject: [PATCH] Make the size for the syn cache hash array tunable. As we are swapping between two syn caches for random reseeding anyway, this feature can be added easily. When the cache is empty, there is an opportunity to change the hash size. This allows an admin under SYN flood attack to defend his machine. Suggested by claudio@; OK jung@ claudio@ jmc@ --- lib/libc/gen/sysctl.3 | 9 +++++++-- sbin/sysctl/sysctl.8 | 10 ++++++---- sys/netinet/tcp_input.c | 39 +++++++++++++++++++++++++++++++-------- sys/netinet/tcp_usrreq.c | 23 ++++++++++++++++++++++- sys/netinet/tcp_var.h | 17 +++++++++++------ sys/sys/malloc.h | 6 +++--- 6 files changed, 80 insertions(+), 24 deletions(-) diff --git a/lib/libc/gen/sysctl.3 b/lib/libc/gen/sysctl.3 index d2e3c98862b..0c6312c52dc 100644 --- a/lib/libc/gen/sysctl.3 +++ b/lib/libc/gen/sysctl.3 @@ -1,4 +1,4 @@ -.\" $OpenBSD: sysctl.3,v 1.266 2016/07/14 17:34:06 jmc Exp $ +.\" $OpenBSD: sysctl.3,v 1.267 2016/07/20 09:15:28 bluhm Exp $ .\" .\" Copyright (c) 1993 .\" The Regents of the University of California. All rights reserved. @@ -27,7 +27,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd $Mdocdate: July 14 2016 $ +.Dd $Mdocdate: July 20 2016 $ .Dt SYSCTL 3 .Os .Sh NAME @@ -1188,6 +1188,7 @@ The currently defined protocols and names are: .It tcp Ta stats Ta structure Ta no .It tcp Ta synbucketlimit Ta integer Ta yes .It tcp Ta syncachelimit Ta integer Ta yes +.It tcp Ta synhashsize Ta integer Ta yes .It tcp Ta synuselimit Ta integer Ta yes .It udp Ta baddynamic Ta array Ta yes .It udp Ta checksum Ta integer Ta yes @@ -1617,6 +1618,10 @@ Returns the TCP statistics in a struct tcpstat. The maximum number of entries allowed per hash bucket in the TCP SYN cache. .It Li tcp.syncachelimit The maximum number of entries allowed in the TCP SYN cache. +.It Li tcp.synhashsize +The number of buckets in the TCP SYN cache hash array. +After the value is set, the actual size changes when the alternative +SYN cache becomes empty and both SYN caches are swapped. .It Li tcp.synuselimit The minimum number of times the hash function for the TCP SYN cache is used before it is reseeded. diff --git a/sbin/sysctl/sysctl.8 b/sbin/sysctl/sysctl.8 index 645de2be80b..0666b751dee 100644 --- a/sbin/sysctl/sysctl.8 +++ b/sbin/sysctl/sysctl.8 @@ -1,4 +1,4 @@ -.\" $OpenBSD: sysctl.8,v 1.202 2016/07/05 17:41:59 tim Exp $ +.\" $OpenBSD: sysctl.8,v 1.203 2016/07/20 09:15:28 bluhm Exp $ .\" $NetBSD: sysctl.8,v 1.4 1995/09/30 07:12:49 thorpej Exp $ .\" .\" Copyright (c) 1993 @@ -30,7 +30,7 @@ .\" .\" @(#)sysctl.8 8.2 (Berkeley) 5/9/95 .\" -.Dd $Mdocdate: July 5 2016 $ +.Dd $Mdocdate: July 20 2016 $ .Dt SYSCTL 8 .Os .Sh NAME @@ -256,7 +256,6 @@ and a few require a kernel compiled with non-standard .It net.inet.tcp.keepinittime Ta integer Ta yes .It net.inet.tcp.keepidle Ta integer Ta yes .It net.inet.tcp.keepintvl Ta integer Ta yes -.It net.inet.tcp.always_keepalive Ta integer Ta yes .It net.inet.tcp.slowhz Ta integer Ta no .It net.inet.tcp.baddynamic Ta array Ta yes .It net.inet.tcp.sack Ta integer Ta yes @@ -266,10 +265,13 @@ and a few require a kernel compiled with non-standard .It net.inet.tcp.ecn Ta integer Ta yes .It net.inet.tcp.syncachelimit Ta integer Ta yes .It net.inet.tcp.synbucketlimit Ta integer Ta yes -.It net.inet.tcp.synuselimit Ta integer Ta yes .It net.inet.tcp.rfc3390 Ta integer Ta yes .It net.inet.tcp.reasslimit Ta integer Ta yes +.It net.inet.tcp.sackholelimit Ta integer Ta yes +.It net.inet.tcp.always_keepalive Ta integer Ta yes +.It net.inet.tcp.synuselimit Ta integer Ta yes .It net.inet.tcp.rootonly Ta array Ta yes +.It net.inet.tcp.synhashsize Ta integer Ta yes .It net.inet.udp.checksum Ta integer Ta yes .It net.inet.udp.baddynamic Ta array Ta yes .It net.inet.udp.recvspace Ta integer Ta yes diff --git a/sys/netinet/tcp_input.c b/sys/netinet/tcp_input.c index 7807968b380..99539691c3f 100644 --- a/sys/netinet/tcp_input.c +++ b/sys/netinet/tcp_input.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tcp_input.c,v 1.324 2016/07/01 18:37:15 jca Exp $ */ +/* $OpenBSD: tcp_input.c,v 1.325 2016/07/20 09:15:28 bluhm Exp $ */ /* $NetBSD: tcp_input.c,v 1.23 1996/02/13 23:43:44 christos Exp $ */ /* @@ -3266,7 +3266,7 @@ tcp_mss_adv(struct mbuf *m, int af) */ /* syn hash parameters */ -int tcp_syn_cache_size = TCP_SYN_HASH_SIZE; +int tcp_syn_hash_size = TCP_SYN_HASH_SIZE; int tcp_syn_cache_limit = TCP_SYN_HASH_SIZE*TCP_SYN_BUCKET_SIZE; int tcp_syn_bucket_limit = 3*TCP_SYN_BUCKET_SIZE; int tcp_syn_use_limit = 100000; @@ -3360,7 +3360,13 @@ syn_cache_init(void) int i; /* Initialize the hash buckets. */ - for (i = 0; i < tcp_syn_cache_size; i++) { + tcp_syn_cache[0].scs_buckethead = mallocarray(tcp_syn_hash_size, + sizeof(struct syn_cache_head), M_SYNCACHE, M_WAITOK|M_ZERO); + tcp_syn_cache[1].scs_buckethead = mallocarray(tcp_syn_hash_size, + sizeof(struct syn_cache_head), M_SYNCACHE, M_WAITOK|M_ZERO); + tcp_syn_cache[0].scs_size = tcp_syn_hash_size; + tcp_syn_cache[1].scs_size = tcp_syn_hash_size; + for (i = 0; i < tcp_syn_hash_size; i++) { TAILQ_INIT(&tcp_syn_cache[0].scs_buckethead[i].sch_bucket); TAILQ_INIT(&tcp_syn_cache[1].scs_buckethead[i].sch_bucket); } @@ -3377,7 +3383,7 @@ syn_cache_insert(struct syn_cache *sc, struct tcpcb *tp) struct syn_cache_set *set = &tcp_syn_cache[tcp_syn_cache_active]; struct syn_cache_head *scp; struct syn_cache *sc2; - int s; + int i, s; s = splsoftnet(); @@ -3385,16 +3391,33 @@ syn_cache_insert(struct syn_cache *sc, struct tcpcb *tp) * If there are no entries in the hash table, reinitialize * the hash secrets. To avoid useless cache swaps and * reinitialization, use it until the limit is reached. + * An emtpy cache is also the oportunity to resize the hash. */ if (set->scs_count == 0 && set->scs_use <= 0) { - arc4random_buf(set->scs_random, sizeof(set->scs_random)); set->scs_use = tcp_syn_use_limit; + if (set->scs_size != tcp_syn_hash_size) { + scp = mallocarray(tcp_syn_hash_size, sizeof(struct + syn_cache_head), M_SYNCACHE, M_NOWAIT|M_ZERO); + if (scp == NULL) { + /* Try again next time. */ + set->scs_use = 0; + } else { + free(set->scs_buckethead, M_SYNCACHE, + set->scs_size * + sizeof(struct syn_cache_head)); + set->scs_buckethead = scp; + set->scs_size = tcp_syn_hash_size; + for (i = 0; i < tcp_syn_hash_size; i++) + TAILQ_INIT(&scp[i].sch_bucket); + } + } + arc4random_buf(set->scs_random, sizeof(set->scs_random)); tcpstat.tcps_sc_seedrandom++; } SYN_HASHALL(sc->sc_hash, &sc->sc_src.sa, &sc->sc_dst.sa, set->scs_random); - scp = &set->scs_buckethead[sc->sc_hash % tcp_syn_cache_size]; + scp = &set->scs_buckethead[sc->sc_hash % set->scs_size]; sc->sc_buckethead = scp; /* @@ -3437,7 +3460,7 @@ syn_cache_insert(struct syn_cache *sc, struct tcpcb *tp) */ scp2 = scp; if (TAILQ_EMPTY(&scp2->sch_bucket)) { - sce = &set->scs_buckethead[tcp_syn_cache_size]; + sce = &set->scs_buckethead[set->scs_size]; for (++scp2; scp2 != scp; scp2++) { if (scp2 >= sce) scp2 = &set->scs_buckethead[0]; @@ -3595,7 +3618,7 @@ syn_cache_lookup(struct sockaddr *src, struct sockaddr *dst, if (sets[i]->scs_count == 0) continue; SYN_HASHALL(hash, src, dst, sets[i]->scs_random); - scp = &sets[i]->scs_buckethead[hash % tcp_syn_cache_size]; + scp = &sets[i]->scs_buckethead[hash % sets[i]->scs_size]; *headp = scp; TAILQ_FOREACH(sc, &scp->sch_bucket, sc_bucketq) { if (sc->sc_hash != hash) diff --git a/sys/netinet/tcp_usrreq.c b/sys/netinet/tcp_usrreq.c index 6eb6efb8f7a..79e16cf1737 100644 --- a/sys/netinet/tcp_usrreq.c +++ b/sys/netinet/tcp_usrreq.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tcp_usrreq.c,v 1.132 2016/07/11 10:35:43 mpi Exp $ */ +/* $OpenBSD: tcp_usrreq.c,v 1.133 2016/07/20 09:15:28 bluhm Exp $ */ /* $NetBSD: tcp_usrreq.c,v 1.20 1996/02/13 23:44:16 christos Exp $ */ /* @@ -956,6 +956,27 @@ tcp_sysctl(name, namelen, oldp, oldlenp, newp, newlen) } return (0); + case TCPCTL_SYN_HASH_SIZE: + nval = tcp_syn_hash_size; + error = sysctl_int(oldp, oldlenp, newp, newlen, &nval); + if (error) + return (error); + if (nval != tcp_syn_hash_size) { + if (nval < 1 || nval > 100000) + return (EINVAL); + /* + * If global hash size has been changed, switch sets as + * soon as possible. Then the actual hash array will + * be reallocated. + */ + if (tcp_syn_cache[0].scs_size != nval) + tcp_syn_cache[0].scs_use = 0; + if (tcp_syn_cache[1].scs_size != nval) + tcp_syn_cache[1].scs_use = 0; + tcp_syn_hash_size = nval; + } + return (0); + default: if (name[0] < TCPCTL_MAXID) return (sysctl_int_arr(tcpctl_vars, name, namelen, diff --git a/sys/netinet/tcp_var.h b/sys/netinet/tcp_var.h index e057c084437..22619571321 100644 --- a/sys/netinet/tcp_var.h +++ b/sys/netinet/tcp_var.h @@ -1,4 +1,4 @@ -/* $OpenBSD: tcp_var.h,v 1.113 2016/06/18 10:36:13 vgross Exp $ */ +/* $OpenBSD: tcp_var.h,v 1.114 2016/07/20 09:15:28 bluhm Exp $ */ /* $NetBSD: tcp_var.h,v 1.17 1996/02/13 23:44:24 christos Exp $ */ /* @@ -316,10 +316,11 @@ struct syn_cache_head { }; struct syn_cache_set { - struct syn_cache_head scs_buckethead[TCP_SYN_HASH_SIZE]; - int scs_count; - int scs_use; - u_int32_t scs_random[5]; + struct syn_cache_head *scs_buckethead; + int scs_size; + int scs_count; + int scs_use; + u_int32_t scs_random[5]; }; #endif /* _KERNEL */ @@ -491,7 +492,8 @@ struct tcpstat { #define TCPCTL_ALWAYS_KEEPALIVE 22 /* assume SO_KEEPALIVE is always set */ #define TCPCTL_SYN_USE_LIMIT 23 /* number of uses before reseeding hash */ #define TCPCTL_ROOTONLY 24 /* return root only port bitmap */ -#define TCPCTL_MAXID 25 +#define TCPCTL_SYN_HASH_SIZE 25 /* number of buckets in the hash */ +#define TCPCTL_MAXID 26 #define TCPCTL_NAMES { \ { 0, 0 }, \ @@ -519,6 +521,7 @@ struct tcpstat { { "always_keepalive", CTLTYPE_INT }, \ { "synuselimit", CTLTYPE_INT }, \ { "rootonly", CTLTYPE_STRUCT }, \ + { "synhashsize", CTLTYPE_INT }, \ } #define TCPCTL_VARS { \ @@ -546,6 +549,7 @@ struct tcpstat { NULL, \ NULL, \ NULL, \ + NULL, \ NULL \ } @@ -575,6 +579,7 @@ extern int tcp_do_rfc3390; /* RFC3390 Increasing TCP's Initial Window */ extern struct pool tcpqe_pool; extern int tcp_reass_limit; /* max entries for tcp reass queues */ +extern int tcp_syn_hash_size; /* adjustable size of the hash array */ extern int tcp_syn_cache_limit; /* max entries for compressed state engine */ extern int tcp_syn_bucket_limit;/* max entries per hash bucket */ extern int tcp_syn_use_limit; /* number of uses before reseeding hash */ diff --git a/sys/sys/malloc.h b/sys/sys/malloc.h index 1690bff2842..aba3094a356 100644 --- a/sys/sys/malloc.h +++ b/sys/sys/malloc.h @@ -1,4 +1,4 @@ -/* $OpenBSD: malloc.h,v 1.112 2015/08/24 15:33:49 mpi Exp $ */ +/* $OpenBSD: malloc.h,v 1.113 2016/07/20 09:15:28 bluhm Exp $ */ /* $NetBSD: malloc.h,v 1.39 1998/07/12 19:52:01 augustss Exp $ */ /* @@ -169,7 +169,7 @@ #define M_KEVENT 137 /* kqueue related */ /* 138 free */ - /* 139 free */ +#define M_SYNCACHE 139 /* syn cache hash array */ #define M_UDFMOUNT 140 /* UDF mount */ #define M_UDFFENTRY 141 /* UDF file entry */ @@ -307,7 +307,7 @@ "NTFS vrun", /* 136 M_NTFSRUN */ \ "kqueue", /* 137 M_KEVENT */ \ NULL, /* 138 free */ \ - "NULL", \ + "SYN cache", /* 139 M_SYNCACHE */ \ "UDF mount", /* 140 M_UDFMOUNT */ \ "UDF file entry", /* 141 M_UDFFENTRY */ \ "UDF file id", /* 142 M_UDFFID */ \ -- 2.20.1