Hi,

claudio@ suggested to have a tunable size for the syn cache hash
array.  As we are swapping between two syn caches for random reseeding
anyway, this feature can be added easily.  When the cache is empty,
we can change the hash size.

This allows an admin under SYN flood attack to tune his machine.
sysctl net.inet.tcp.synhashsize=10000

ok?

bluhm

Index: netinet/tcp_input.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/tcp_input.c,v
retrieving revision 1.324
diff -u -p -r1.324 tcp_input.c
--- netinet/tcp_input.c 1 Jul 2016 18:37:15 -0000       1.324
+++ netinet/tcp_input.c 19 Jul 2016 15:02:35 -0000
@@ -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, s
        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, s
         * 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, s
                 */
                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, s
                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: netinet/tcp_usrreq.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/tcp_usrreq.c,v
retrieving revision 1.132
diff -u -p -r1.132 tcp_usrreq.c
--- netinet/tcp_usrreq.c        11 Jul 2016 10:35:43 -0000      1.132
+++ netinet/tcp_usrreq.c        19 Jul 2016 15:44:59 -0000
@@ -956,6 +956,27 @@ tcp_sysctl(name, namelen, oldp, oldlenp,
                }
                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: netinet/tcp_var.h
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/tcp_var.h,v
retrieving revision 1.113
diff -u -p -r1.113 tcp_var.h
--- netinet/tcp_var.h   18 Jun 2016 10:36:13 -0000      1.113
+++ netinet/tcp_var.h   19 Jul 2016 15:44:59 -0000
@@ -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 In
 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: sys/malloc.h
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/sys/malloc.h,v
retrieving revision 1.112
diff -u -p -r1.112 malloc.h
--- sys/malloc.h        24 Aug 2015 15:33:49 -0000      1.112
+++ sys/malloc.h        19 Jul 2016 15:02:35 -0000
@@ -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 */ \

Reply via email to