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

Reply via email to