Make the size for the syn cache hash array tunable. As we are
authorbluhm <bluhm@openbsd.org>
Wed, 20 Jul 2016 09:15:28 +0000 (09:15 +0000)
committerbluhm <bluhm@openbsd.org>
Wed, 20 Jul 2016 09:15:28 +0000 (09:15 +0000)
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
sbin/sysctl/sysctl.8
sys/netinet/tcp_input.c
sys/netinet/tcp_usrreq.c
sys/netinet/tcp_var.h
sys/sys/malloc.h

index d2e3c98..0c6312c 100644 (file)
@@ -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.
index 645de2b..0666b75 100644 (file)
@@ -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
index 7807968..9953969 100644 (file)
@@ -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)
index 6eb6efb..79e16cf 100644 (file)
@@ -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,
index e057c08..2261957 100644 (file)
@@ -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 */
index 1690bff..aba3094 100644 (file)
@@ -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 $     */
 
 /*
 #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 */
        "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 */ \