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 */ \