On Tue, Jul 19, 2016 at 06:13:42PM +0200, Alexander Bluhm wrote: > 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
Makes sense to me and I like this. > ok? ok jung@ Please, also document it, at least in sysctl(8). > 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 */ \ >