Index: conf/files =================================================================== --- conf/files (revision 253028) +++ conf/files (working copy) @@ -547,6 +547,8 @@ netgraph_mppc_encryption | sctp crypto/sha2/sha2.c optional crypto | geom_bde | ipsec | random | \ sctp | zfs +crypto/siphash/siphash.c optional inet | inet6 +crypto/siphash/siphash_test.c optional inet | inet6 ddb/db_access.c optional ddb ddb/db_break.c optional ddb ddb/db_capture.c optional ddb Index: crypto/siphash/siphash.c =================================================================== --- crypto/siphash/siphash.c (revision 0) +++ crypto/siphash/siphash.c (working copy) @@ -0,0 +1,238 @@ +/*- + * Copyright (c) 2013 Andre Oppermann + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * SipHash is a family of PRFs SipHash-c-d where the integer parameters c and d + * are the number of compression rounds and the number of finalization rounds. + * A compression round is identical to a finalization round and this round + * function is called SipRound. Given a 128-bit key k and a (possibly empty) + * byte string m, SipHash-c-d returns a 64-bit value SipHash-c-d(k; m). + * + * Implemented from the paper "SipHash: a fast short-input PRF", 2012.09.18, + * by Jean-Philippe Aumasson and Daniel J. Bernstein, + * Permanent Document ID b9a943a805fbfc6fde808af9fc0ecdfa + * https://131002.net/siphash/siphash.pdf + * https://131002.net/siphash/ + */ + +#include +#include +#include +#include +#include + +#include + +static void SipRounds(SIPHASH_CTX *ctx, int final); + +void +SipHash_InitX(SIPHASH_CTX *ctx, int rc, int rf) +{ + + ctx->v[0] = 0x736f6d6570736575ull; + ctx->v[1] = 0x646f72616e646f6dull; + ctx->v[2] = 0x6c7967656e657261ull; + ctx->v[3] = 0x7465646279746573ull; + ctx->buf.b64 = 0; + ctx->bytes = 0; + ctx->buflen = 0; + ctx->rounds_compr = rc; + ctx->rounds_final = rf; + ctx->initialized = 1; +} + +void +SipHash_SetKey(SIPHASH_CTX *ctx, const uint8_t key[16]) +{ + uint64_t k[2]; + + KASSERT(ctx->v[0] == 0x736f6d6570736575ull && + ctx->initialized == 1, + ("%s: context %p not properly initialized", __func__, ctx)); + + k[0] = le64dec(&key[0]); + k[1] = le64dec(&key[8]); + + ctx->v[0] ^= k[0]; + ctx->v[1] ^= k[1]; + ctx->v[2] ^= k[0]; + ctx->v[3] ^= k[1]; + + ctx->initialized = 2; +} + +static size_t +SipBuf(SIPHASH_CTX *ctx, const uint8_t **src, size_t len, int final) +{ + size_t x = 0; + + KASSERT((!final && len > 0) || (final && len == 0), + ("%s: invalid parameters", __func__)); + + if (!final) { + x = MIN(len, sizeof(ctx->buf.b64) - ctx->buflen); + bcopy(*src, &ctx->buf.b8[ctx->buflen], x); + ctx->buflen += x; + *src += x; + } else + ctx->buf.b8[7] = (uint8_t)ctx->bytes; + + if (ctx->buflen == 8 || final) { + ctx->v[3] ^= le64toh(ctx->buf.b64); + SipRounds(ctx, 0); + ctx->v[0] ^= le64toh(ctx->buf.b64); + ctx->buf.b64 = 0; + ctx->buflen = 0; + } + return (x); +} + +void +SipHash_Update(SIPHASH_CTX *ctx, const void *src, size_t len) +{ + uint64_t m, *p; + const uint8_t *s; + size_t rem; + + KASSERT(ctx->initialized == 2, + ("%s: context %p not properly initialized", __func__, ctx)); + + s = src; + ctx->bytes += len; + + /* + * Push length smaller than block size into buffer or + * fill up the buffer if there is already something + * in it. + */ + if (ctx->buflen > 0 || len < 8) + len -= SipBuf(ctx, &s, len, 0); + if (len == 0) + return; + + rem = len & 0x7; + len >>= 3; + + /* Optimze for 64bit aligned/unaligned access. */ + if (((uintptr_t)s & 0x7) == 0) { + for (p = (uint64_t *)s; len > 0; len--, p++) { + m = le64toh(*p); + ctx->v[3] ^= m; + SipRounds(ctx, 0); + ctx->v[0] ^= m; + } + s = (uint8_t *)p; + } else { + for (; len > 0; len--, s += 8) { + m = le64dec(s); + ctx->v[3] ^= m; + SipRounds(ctx, 0); + ctx->v[0] ^= m; + } + } + + /* Push remainder into buffer. */ + if (rem > 0) + (void)SipBuf(ctx, &s, rem, 0); +} + +void +SipHash_Final(void *dst, SIPHASH_CTX *ctx) +{ + uint64_t r; + + KASSERT(ctx->initialized == 2, + ("%s: context %p not properly initialized", __func__, ctx)); + + r = SipHash_End(ctx); + le64enc(dst, r); +} + +uint64_t +SipHash_End(SIPHASH_CTX *ctx) +{ + uint64_t r; + + KASSERT(ctx->initialized == 2, + ("%s: context %p not properly initialized", __func__, ctx)); + + SipBuf(ctx, NULL, 0, 1); + ctx->v[2] ^= 0xff; + SipRounds(ctx, 1); + r = (ctx->v[0] ^ ctx->v[1]) ^ (ctx->v[2] ^ ctx->v[3]); + + bzero(ctx, sizeof(*ctx)); + return (r); +} + +uint64_t +SipHashX(SIPHASH_CTX *ctx, int rc, int rf, const uint8_t key[16], + const void *src, size_t len) +{ + + SipHash_InitX(ctx, rc, rf); + SipHash_SetKey(ctx, key); + SipHash_Update(ctx, src, len); + + return (SipHash_End(ctx)); +} + +#define SIP_ROTL(x, b) (uint64_t)(((x) << (b)) | ( (x) >> (64 - (b)))) + +static void +SipRounds(SIPHASH_CTX *ctx, int final) +{ + int rounds; + + if (!final) + rounds = ctx->rounds_compr; + else + rounds = ctx->rounds_final; + + while (rounds--) { + ctx->v[0] += ctx->v[1]; + ctx->v[2] += ctx->v[3]; + ctx->v[1] = SIP_ROTL(ctx->v[1], 13); + ctx->v[3] = SIP_ROTL(ctx->v[3], 16); + + ctx->v[1] ^= ctx->v[0]; + ctx->v[3] ^= ctx->v[2]; + ctx->v[0] = SIP_ROTL(ctx->v[0], 32); + + ctx->v[2] += ctx->v[1]; + ctx->v[0] += ctx->v[3]; + ctx->v[1] = SIP_ROTL(ctx->v[1], 17); + ctx->v[3] = SIP_ROTL(ctx->v[3], 21); + + ctx->v[1] ^= ctx->v[2]; + ctx->v[3] ^= ctx->v[0]; + ctx->v[2] = SIP_ROTL(ctx->v[2], 32); + } +} + Index: crypto/siphash/siphash.h =================================================================== --- crypto/siphash/siphash.h (revision 0) +++ crypto/siphash/siphash.h (working copy) @@ -0,0 +1,81 @@ +/*- + * Copyright (c) 2013 Andre Oppermann + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * SipHash is a family of pseudorandom functions (a.k.a. keyed hash functions) + * optimized for speed on short messages returning a 64bit hash/digest value. + * + * The number of rounds is defined during the initialization: + * SipHash24_Init() for the fast and resonable strong version + * SipHash48_Init() for the strong version (half as fast) + * + * struct SIPHASH_CTX ctx; + * SipHash24_Init(&ctx); + * SipHash_SetKey(&ctx, "16bytes long key"); + * SipHash_Update(&ctx, pointer_to_string, length_of_string); + * SipHash_Final(output, &ctx); + */ + +#ifndef _SIPHASH_H_ +#define _SIPHASH_H_ + +#define SIPHASH_BLOCK_LENGTH 8 +#define SIPHASH_KEY_LENGTH 16 +#define SIPHASH_DIGEST_LENGTH 8 + +typedef struct _SIPHASH_CTX { + uint64_t v[4]; + union { + uint64_t b64; + uint8_t b8[8]; + } buf; + uint64_t bytes; + uint8_t buflen; + uint8_t rounds_compr; + uint8_t rounds_final; + uint8_t initialized; +} SIPHASH_CTX; + + +#define SipHash24_Init(x) SipHash_InitX((x), 2, 4) +#define SipHash48_Init(x) SipHash_InitX((x), 4, 8) +void SipHash_InitX(SIPHASH_CTX *, int, int); +void SipHash_SetKey(SIPHASH_CTX *, const uint8_t [16]); +void SipHash_Update(SIPHASH_CTX *, const void *, size_t); +void SipHash_Final(void *, SIPHASH_CTX *); +uint64_t SipHash_End(SIPHASH_CTX *); + +#define SipHash24(x, y, z, i) SipHashX((x), 2, 4, (y), (z), (i)); +#define SipHash48(x, y, z, i) SipHashX((x), 4, 8, (y), (z), (i)); +uint64_t SipHashX(SIPHASH_CTX *, int, int, const uint8_t [16], const void *, + size_t); + +int SipHash24_TestVectors(void); + +#endif /* _SIPHASH_H_ */ Index: crypto/siphash/siphash_test.c =================================================================== --- crypto/siphash/siphash_test.c (revision 0) +++ crypto/siphash/siphash_test.c (working copy) @@ -0,0 +1,135 @@ +/*- + * Test Vectors from the SipHash reference C implementation: + * + * Written in 2012 by + * Jean-Philippe Aumasson + * Daniel J. Bernstein + * + * To the extent possible under law, the author(s) have dedicated all copyright + * and related and neighboring rights to this software to the public domain + * worldwide. This software is distributed without any warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication along with + * this software. If not, see . + * + * SipHash-2-4 output with + * k = 00 01 02 ... + * and + * in = (empty string) + * in = 00 (1 byte) + * in = 00 01 (2 bytes) + * in = 00 01 02 (3 bytes) + * ... + * in = 00 01 02 ... 3e (63 bytes) + */ + +#include +#include +#include +#include +#include + +#include + +uint8_t vectors[64][8] = +{ + { 0x31, 0x0e, 0x0e, 0xdd, 0x47, 0xdb, 0x6f, 0x72, }, + { 0xfd, 0x67, 0xdc, 0x93, 0xc5, 0x39, 0xf8, 0x74, }, + { 0x5a, 0x4f, 0xa9, 0xd9, 0x09, 0x80, 0x6c, 0x0d, }, + { 0x2d, 0x7e, 0xfb, 0xd7, 0x96, 0x66, 0x67, 0x85, }, + { 0xb7, 0x87, 0x71, 0x27, 0xe0, 0x94, 0x27, 0xcf, }, + { 0x8d, 0xa6, 0x99, 0xcd, 0x64, 0x55, 0x76, 0x18, }, + { 0xce, 0xe3, 0xfe, 0x58, 0x6e, 0x46, 0xc9, 0xcb, }, + { 0x37, 0xd1, 0x01, 0x8b, 0xf5, 0x00, 0x02, 0xab, }, + { 0x62, 0x24, 0x93, 0x9a, 0x79, 0xf5, 0xf5, 0x93, }, + { 0xb0, 0xe4, 0xa9, 0x0b, 0xdf, 0x82, 0x00, 0x9e, }, + { 0xf3, 0xb9, 0xdd, 0x94, 0xc5, 0xbb, 0x5d, 0x7a, }, + { 0xa7, 0xad, 0x6b, 0x22, 0x46, 0x2f, 0xb3, 0xf4, }, + { 0xfb, 0xe5, 0x0e, 0x86, 0xbc, 0x8f, 0x1e, 0x75, }, + { 0x90, 0x3d, 0x84, 0xc0, 0x27, 0x56, 0xea, 0x14, }, + { 0xee, 0xf2, 0x7a, 0x8e, 0x90, 0xca, 0x23, 0xf7, }, + { 0xe5, 0x45, 0xbe, 0x49, 0x61, 0xca, 0x29, 0xa1, }, + { 0xdb, 0x9b, 0xc2, 0x57, 0x7f, 0xcc, 0x2a, 0x3f, }, + { 0x94, 0x47, 0xbe, 0x2c, 0xf5, 0xe9, 0x9a, 0x69, }, + { 0x9c, 0xd3, 0x8d, 0x96, 0xf0, 0xb3, 0xc1, 0x4b, }, + { 0xbd, 0x61, 0x79, 0xa7, 0x1d, 0xc9, 0x6d, 0xbb, }, + { 0x98, 0xee, 0xa2, 0x1a, 0xf2, 0x5c, 0xd6, 0xbe, }, + { 0xc7, 0x67, 0x3b, 0x2e, 0xb0, 0xcb, 0xf2, 0xd0, }, + { 0x88, 0x3e, 0xa3, 0xe3, 0x95, 0x67, 0x53, 0x93, }, + { 0xc8, 0xce, 0x5c, 0xcd, 0x8c, 0x03, 0x0c, 0xa8, }, + { 0x94, 0xaf, 0x49, 0xf6, 0xc6, 0x50, 0xad, 0xb8, }, + { 0xea, 0xb8, 0x85, 0x8a, 0xde, 0x92, 0xe1, 0xbc, }, + { 0xf3, 0x15, 0xbb, 0x5b, 0xb8, 0x35, 0xd8, 0x17, }, + { 0xad, 0xcf, 0x6b, 0x07, 0x63, 0x61, 0x2e, 0x2f, }, + { 0xa5, 0xc9, 0x1d, 0xa7, 0xac, 0xaa, 0x4d, 0xde, }, + { 0x71, 0x65, 0x95, 0x87, 0x66, 0x50, 0xa2, 0xa6, }, + { 0x28, 0xef, 0x49, 0x5c, 0x53, 0xa3, 0x87, 0xad, }, + { 0x42, 0xc3, 0x41, 0xd8, 0xfa, 0x92, 0xd8, 0x32, }, + { 0xce, 0x7c, 0xf2, 0x72, 0x2f, 0x51, 0x27, 0x71, }, + { 0xe3, 0x78, 0x59, 0xf9, 0x46, 0x23, 0xf3, 0xa7, }, + { 0x38, 0x12, 0x05, 0xbb, 0x1a, 0xb0, 0xe0, 0x12, }, + { 0xae, 0x97, 0xa1, 0x0f, 0xd4, 0x34, 0xe0, 0x15, }, + { 0xb4, 0xa3, 0x15, 0x08, 0xbe, 0xff, 0x4d, 0x31, }, + { 0x81, 0x39, 0x62, 0x29, 0xf0, 0x90, 0x79, 0x02, }, + { 0x4d, 0x0c, 0xf4, 0x9e, 0xe5, 0xd4, 0xdc, 0xca, }, + { 0x5c, 0x73, 0x33, 0x6a, 0x76, 0xd8, 0xbf, 0x9a, }, + { 0xd0, 0xa7, 0x04, 0x53, 0x6b, 0xa9, 0x3e, 0x0e, }, + { 0x92, 0x59, 0x58, 0xfc, 0xd6, 0x42, 0x0c, 0xad, }, + { 0xa9, 0x15, 0xc2, 0x9b, 0xc8, 0x06, 0x73, 0x18, }, + { 0x95, 0x2b, 0x79, 0xf3, 0xbc, 0x0a, 0xa6, 0xd4, }, + { 0xf2, 0x1d, 0xf2, 0xe4, 0x1d, 0x45, 0x35, 0xf9, }, + { 0x87, 0x57, 0x75, 0x19, 0x04, 0x8f, 0x53, 0xa9, }, + { 0x10, 0xa5, 0x6c, 0xf5, 0xdf, 0xcd, 0x9a, 0xdb, }, + { 0xeb, 0x75, 0x09, 0x5c, 0xcd, 0x98, 0x6c, 0xd0, }, + { 0x51, 0xa9, 0xcb, 0x9e, 0xcb, 0xa3, 0x12, 0xe6, }, + { 0x96, 0xaf, 0xad, 0xfc, 0x2c, 0xe6, 0x66, 0xc7, }, + { 0x72, 0xfe, 0x52, 0x97, 0x5a, 0x43, 0x64, 0xee, }, + { 0x5a, 0x16, 0x45, 0xb2, 0x76, 0xd5, 0x92, 0xa1, }, + { 0xb2, 0x74, 0xcb, 0x8e, 0xbf, 0x87, 0x87, 0x0a, }, + { 0x6f, 0x9b, 0xb4, 0x20, 0x3d, 0xe7, 0xb3, 0x81, }, + { 0xea, 0xec, 0xb2, 0xa3, 0x0b, 0x22, 0xa8, 0x7f, }, + { 0x99, 0x24, 0xa4, 0x3c, 0xc1, 0x31, 0x57, 0x24, }, + { 0xbd, 0x83, 0x8d, 0x3a, 0xaf, 0xbf, 0x8d, 0xb7, }, + { 0x0b, 0x1a, 0x2a, 0x32, 0x65, 0xd5, 0x1a, 0xea, }, + { 0x13, 0x50, 0x79, 0xa3, 0x23, 0x1c, 0xe6, 0x60, }, + { 0x93, 0x2b, 0x28, 0x46, 0xe4, 0xd7, 0x06, 0x66, }, + { 0xe1, 0x91, 0x5f, 0x5c, 0xb1, 0xec, 0xa4, 0x6c, }, + { 0xf3, 0x25, 0x96, 0x5c, 0xa1, 0x6d, 0x62, 0x9f, }, + { 0x57, 0x5f, 0xf2, 0x8e, 0x60, 0x38, 0x1b, 0xe5, }, + { 0x72, 0x45, 0x06, 0xeb, 0x4c, 0x32, 0x8a, 0x95, } +}; + +#define MAXLEN 64 + +int +SipHash24_TestVectors(void) +{ + int i, fail = 0; + uint8_t in[MAXLEN], out[8], k[16]; + SIPHASH_CTX ctx; + + /* Initialize key. */ + for (i = 0; i < 16; ++i) + k[i] = i; + + /* Step through differnet length. */ + for (i = 0; i < MAXLEN; ++i) { + in[i] = i; + + SipHash24_Init(&ctx); + SipHash_SetKey(&ctx, k); + SipHash_Update(&ctx, in, i); + SipHash_Final(out, &ctx); + + if (memcmp(out, vectors[i], 8)) +#if 0 + printf("%i: test vector failed\n", i); + else + printf("%i: test vector correct\n", i); +#else + fail++; +#endif + } + + return ((fail == 0)); +} Index: netinet/tcp_syncache.c =================================================================== --- netinet/tcp_syncache.c (revision 253028) +++ netinet/tcp_syncache.c (working copy) @@ -1,12 +1,12 @@ /*- * Copyright (c) 2001 McAfee, Inc. - * Copyright (c) 2006 Andre Oppermann, Internet Business Solutions AG + * Copyright (c) 2006,2013 Andre Oppermann, Internet Business Solutions AG * All rights reserved. * * This software was developed for the FreeBSD Project by Jonathan Lemon * and McAfee Research, the Security Research Division of McAfee, Inc. under * DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the - * DARPA CHATS research program. + * DARPA CHATS research program. [2001 McAfee, Inc.] * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -47,7 +47,6 @@ #include #include #include -#include #include /* for proc0 declaration */ #include #include @@ -55,6 +54,9 @@ #include #include +#include +#include + #include #include @@ -127,13 +129,20 @@ static void syncache_timeout(struct syncache *sc, struct syncache_head *sch, int docallout); static void syncache_timer(void *); -static void syncookie_generate(struct syncache_head *, struct syncache *, - u_int32_t *); + +static uint32_t syncookie_mac(struct in_conninfo *, tcp_seq, uint8_t, + uint8_t *, uintptr_t); +static tcp_seq syncookie_generate(struct syncache_head *, struct syncache *); static struct syncache *syncookie_lookup(struct in_conninfo *, struct syncache_head *, - struct syncache *, struct tcpopt *, struct tcphdr *, + struct syncache *, struct tcphdr *, struct tcpopt *, struct socket *); +static void syncookie_reseed(void *); +static int syncookie_cmp(struct in_conninfo *inc, struct syncache_head *sch, + struct syncache *sc, struct tcphdr *th, struct tcpopt *to, + struct socket *lso); + /* * Transmit the SYN,ACK fewer times than TCP_MAXRXTSHIFT specifies. * 3 retransmits corresponds to a timeout of 3 * (1 + 2 + 4 + 8) == 45 seconds, @@ -252,11 +261,12 @@ V_tcp_syncache.hashbase = malloc(V_tcp_syncache.hashsize * sizeof(struct syncache_head), M_SYNCACHE, M_WAITOK | M_ZERO); +#ifdef VIMAGE + V_tcp_syncache.vnet = curvnet; +#endif + /* Initialize the hash buckets. */ for (i = 0; i < V_tcp_syncache.hashsize; i++) { -#ifdef VIMAGE - V_tcp_syncache.hashbase[i].sch_vnet = curvnet; -#endif TAILQ_INIT(&V_tcp_syncache.hashbase[i].sch_bucket); mtx_init(&V_tcp_syncache.hashbase[i].sch_mtx, "tcp_sc_head", NULL, MTX_DEF); @@ -263,6 +273,7 @@ callout_init_mtx(&V_tcp_syncache.hashbase[i].sch_timer, &V_tcp_syncache.hashbase[i].sch_mtx, 0); V_tcp_syncache.hashbase[i].sch_length = 0; + V_tcp_syncache.hashbase[i].sch_sc = &V_tcp_syncache; } /* Create the syncache entry zone. */ @@ -270,6 +281,13 @@ NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); V_tcp_syncache.cache_limit = uma_zone_set_max(V_tcp_syncache.zone, V_tcp_syncache.cache_limit); + + /* Start the SYN cookie reseeder callout. */ + callout_init(&V_tcp_syncache.secret.reseed, 1); + arc4rand(V_tcp_syncache.secret.key[0], SYNCOOKIE_SECRET_SIZE, 0); + arc4rand(V_tcp_syncache.secret.key[1], SYNCOOKIE_SECRET_SIZE, 0); + callout_reset(&V_tcp_syncache.secret.reseed, SYNCOOKIE_LIFETIME * hz, + syncookie_reseed, &V_tcp_syncache); } #ifdef VIMAGE @@ -303,6 +321,8 @@ /* Free the allocated global resources. */ uma_zdestroy(V_tcp_syncache.zone); free(V_tcp_syncache.hashbase, M_SYNCACHE); + + callout_drain(&V_tcp_syncache.secret.reseed); } #endif @@ -414,7 +434,7 @@ int tick = ticks; char *s; - CURVNET_SET(sch->sch_vnet); + CURVNET_SET(sch->sch_sc->vnet); /* NB: syncache_head has already been locked by the callout. */ SCH_LOCK_ASSERT(sch); @@ -927,6 +947,13 @@ sc = syncache_lookup(inc, &sch); /* returns locked sch */ SCH_LOCK_ASSERT(sch); + + /* + * XXXAO: Test code for syncookies. + */ + if (sc != NULL) + syncookie_cmp(inc, sch, sc, th, to, *lsop); + if (sc == NULL) { /* * There is no syncache entry, so see if this ACK is @@ -946,7 +973,7 @@ goto failed; } bzero(&scs, sizeof(scs)); - sc = syncookie_lookup(inc, sch, &scs, to, th, *lsop); + sc = syncookie_lookup(inc, sch, &scs, th, to, *lsop); SCH_UNLOCK(sch); if (sc == NULL) { if ((s = tcp_log_addrs(inc, th, NULL, NULL))) @@ -1053,7 +1080,6 @@ struct syncache *sc = NULL; struct syncache_head *sch; struct mbuf *ipopts = NULL; - u_int32_t flowtmp; u_int ltflags; int win, sb_hiwat, ip_ttl, ip_tos; char *s; @@ -1294,19 +1320,17 @@ if ((th->th_flags & (TH_ECE|TH_CWR)) && V_tcp_do_ecn) sc->sc_flags |= SCF_ECN; - if (V_tcp_syncookies) { - syncookie_generate(sch, sc, &flowtmp); + if (V_tcp_syncookies) + sc->sc_iss = syncookie_generate(sch, sc); #ifdef INET6 - if (autoflowlabel) - sc->sc_flowlabel = flowtmp; + if (autoflowlabel) { + if (V_tcp_syncookies) + sc->sc_flowlabel = sc->sc_iss; + else + sc->sc_flowlabel = ip6_randomflowlabel(); + sc->sc_flowlabel = htonl(sc->sc_flowlabel) & IPV6_FLOWLABEL_MASK; + } #endif - } else { -#ifdef INET6 - if (autoflowlabel) - sc->sc_flowlabel = - (htonl(ip6_randomflowlabel()) & IPV6_FLOWLABEL_MASK); -#endif - } SCH_UNLOCK(sch); /* @@ -1529,213 +1553,260 @@ } /* - * The purpose of SYN cookies is to avoid keeping track of all SYN's we - * receive and to be able to handle SYN floods from bogus source addresses - * (where we will never receive any reply). SYN floods try to exhaust all - * our memory and available slots in the SYN cache table to cause a denial - * of service to legitimate users of the local host. + * The purpose of syncookies is to handle spoofed SYN flooding DoS attacks + * that exceed the capacity of the syncache by avoiding the storage of any + * of the SYNs we receive. Syncookies defend against blind SYN flooding + * attacks where the attacker does not have access to our responses. * - * The idea of SYN cookies is to encode and include all necessary information - * about the connection setup state within the SYN-ACK we send back and thus - * to get along without keeping any local state until the ACK to the SYN-ACK - * arrives (if ever). Everything we need to know should be available from - * the information we encoded in the SYN-ACK. + * Syncookies encode and include all necessary information about the + * connection setup within the SYN|ACK that we send back. That way we + * can avoid keeping any local state until the ACK to our SYN|ACK returns + * (if ever). Normally the syncache and syncookies are running in parallel + * with the latter taking over when the former is exhausted. When matching + * syncache entry is found the syncookie is ignored. * - * More information about the theory behind SYN cookies and its first - * discussion and specification can be found at: - * http://cr.yp.to/syncookies.html (overview) - * http://cr.yp.to/syncookies/archive (gory details) + * The only reliable information persisting the 3WHS is our inital sequence + * number ISS of 32 bits. Syncookies embed a cryptographically sufficient + * strong hash (MAC) value and a few bits of TCP SYN options in the ISS + * of our SYN|ACK. The MAC can be recomputed when the ACK to our SYN|ACK + * returns and signifies a legitimate connection if it matches the ACK. * - * This implementation extends the orginal idea and first implementation - * of FreeBSD by using not only the initial sequence number field to store - * information but also the timestamp field if present. This way we can - * keep track of the entire state we need to know to recreate the session in - * its original form. Almost all TCP speakers implement RFC1323 timestamps - * these days. For those that do not we still have to live with the known - * shortcomings of the ISN only SYN cookies. + * The available space of 32 bits to store the hash and to encode the SYN + * option information is very tight and we should have at least 24 bits for + * the MAC to keep the number of guesses by blind spoofing reasonably high. * - * Cookie layers: + * SYN option information we have to encode to fully restore a connection: + * MSS: is imporant to chose an optimal segment size to avoid IP level + * fragmentation along the path. The common MSS values can be encoded + * in a 3-bit table. Uncommon values are captured by the next lower value + * in the table leading to a slight increase in packetization overhead. + * WSCALE: is necessary to allow large windows to be used for high delay- + * bandwidth product links. Not scaling the window when it was initially + * negotiated is bad for performance as lack of scaling further decreases + * the apparent available send window. We only need to encode the WSCALE + * we received from the remote end. Our end can be recalculated at any + * time. The common WSCALE values can be encoded in a 3-bit table. + * Uncommon values are captured by the next lower value in the table + * making us under-estimate the available window size halving our + * theoretically possible maximum throughput for that connection. + * SACK: Greatly assists in packet loss recovery and requires 1 bit. + * TIMESTAMP and SIGNATURE is not encoded because they are permanent options + * that are included in all segments on a connection. We enable them when + * the ACK has them. * - * Initial sequence number we send: - * 31|................................|0 - * DDDDDDDDDDDDDDDDDDDDDDDDDMMMRRRP - * D = MD5 Digest (first dword) - * M = MSS index - * R = Rotation of secret - * P = Odd or Even secret + * Security of syncookies and attack vectors: * - * The MD5 Digest is computed with over following parameters: - * a) randomly rotated secret - * b) struct in_conninfo containing the remote/local ip/port (IPv4&IPv6) - * c) the received initial sequence number from remote host - * d) the rotation offset and odd/even bit + * The MAC is computed over (faddr||laddr||fport||lport||irs||flags||secmod) + * together with the gloabl secret to make it unique per connection attempt. + * Thus any change of any of those parameters results in a different MAC output + * in an unpredictable way unless a collision is encountered. 24 bits of the + * MAC are embedded into the ISS. * - * Timestamp we send: - * 31|................................|0 - * DDDDDDDDDDDDDDDDDDDDDDSSSSRRRRA5 - * D = MD5 Digest (third dword) (only as filler) - * S = Requested send window scale - * R = Requested receive window scale - * A = SACK allowed - * 5 = TCP-MD5 enabled (not implemented yet) - * XORed with MD5 Digest (forth dword) + * To prevent replay attacks two rotating global secrets are updated with a + * new random value every 15 seconds. The life-time of a syncookie is thus + * 15-30 seconds. * - * The timestamp isn't cryptographically secure and doesn't need to be. - * The double use of the MD5 digest dwords ties it to a specific remote/ - * local host/port, remote initial sequence number and our local time - * limited secret. A received timestamp is reverted (XORed) and then - * the contained MD5 dword is compared to the computed one to ensure the - * timestamp belongs to the SYN-ACK we sent. The other parameters may - * have been tampered with but this isn't different from supplying bogus - * values in the SYN in the first place. + * Vector 1: Attacking the secret. This requires finding a weakness in the + * MAC itself or the way it is used here. The attacker can do a chosen plain + * text attack by varying and testing the all parameters under his control. + * The strength depends on the size and randomness of the secret, and the + * cryptographic security of the MAC function. Due to the constant updating + * of the secret the attacker has at most 29.999 seconds to find the secret + * and launch spoofed connections. After that he has to start all over again. * - * Some problems with SYN cookies remain however: - * Consider the problem of a recreated (and retransmitted) cookie. If the - * original SYN was accepted, the connection is established. The second - * SYN is inflight, and if it arrives with an ISN that falls within the - * receive window, the connection is killed. + * Vector 2: Collision attack on the MAC of a single ACK. With a 24 bit MAC + * size an average of 4,823 attempts are required for a 50% chance of success + * to spoof a single syncookie (birthday collision paradox). However the + * attacker is blind and doesn't know if one of his attempts succeeded unless + * he has a side channel to interfere success from. A single connection setup + * success average of 90% requires 8,790 packets, 99.99% requires 17,578 packets. + * This many attempts are required for each one blind spoofed connection. For + * every additional spoofed connection he has to launch another N attempts. + * Thus for a sustained rate 100 spoofed connections per second approximately + * 1,800,000 packets per second would have to be sent. * - * Notes: - * A heuristic to determine when to accept syn cookies is not necessary. - * An ACK flood would cause the syncookie verification to be attempted, - * but a SYN flood causes syncookies to be generated. Both are of equal - * cost, so there's no point in trying to optimize the ACK flood case. - * Also, if you don't process certain ACKs for some reason, then all someone - * would have to do is launch a SYN and ACK flood at the same time, which - * would stop cookie verification and defeat the entire purpose of syncookies. + * NB: The MAC function should be fast so that it doesn't become a CPU + * exhaustion attack vector itself. + * + * References: + * RFC4987 TCP SYN Flooding Attacks and Common Mitigations + * SYN cookies were first proposed by cryptographer Dan J. Bernstein in 1996 + * http://cr.yp.to/syncookies.html (overview) + * http://cr.yp.to/syncookies/archive (details) + * + * + * Schematic construction of a syncookie enabled Initial Sequence Number: + * 0 1 2 3 + * 12345678901234567890123456789012 + * |xxxxxxxxxxxxxxxxxxxxxxxxWWWMMMSP| + * + * x 24 MAC (truncated) + * W 3 Send Window Scale index + * M 3 MSS index + * S 1 SACK permitted + * P 1 Odd/even secret */ -static int tcp_sc_msstab[] = { 0, 256, 468, 536, 996, 1452, 1460, 8960 }; -static void -syncookie_generate(struct syncache_head *sch, struct syncache *sc, - u_int32_t *flowlabel) +/* + * Distribution and probability of certain MSS values. Those in between are + * rounded down to the next lower one. + * [An Analysis of TCP Maximum Segment Sizes, S. Alcock and R. Nelson, 2011] + * .2% .3% 5% 7% 7% 20% 15% 45% + */ +static int tcp_sc_msstab[] = { 216, 536, 1200, 1360, 1400, 1440, 1452, 1460 }; + +/* + * Distribution and probability of certain WSCALE values. We have to map the + * (send) window scale (shift) option with a range of 0-14 from 4 bits into 3 + * bits based on prevalence of certain values. Where we don't have an exact + * match for are rounded down to the next lower one letting us under-estimate + * the true available window. At the moment this would happen only for the + * very uncommon values 3, 5 and those above 8 (more than 16MB socket buffer + * and window size). The absence of the WSCALE option (no scaling in either + * direction) is encoded with index zero. + * [WSCALE values histograms, Allman, 2012] + * X 10 10 35 5 6 14 10% by host + * X 11 4 5 5 18 49 3% by connections + */ +static int tcp_sc_wstab[] = { 0, 0, 1, 2, 4, 6, 7, 8 }; + +/* + * Compute the MAC for the SYN cookie. SIPHASH-2-4 is chosen for its speed + * and good cryptographic properties. + */ +static uint32_t +syncookie_mac(struct in_conninfo *inc, tcp_seq irs, uint8_t flags, + uint8_t *secbits, uintptr_t secmod) { - MD5_CTX ctx; - u_int32_t md5_buffer[MD5_DIGEST_LENGTH / sizeof(u_int32_t)]; - u_int32_t data; - u_int32_t *secbits; - u_int off, pmss, mss; - int i; + SIPHASH_CTX ctx; + uint32_t siphash[2]; + SipHash24_Init(&ctx); + SipHash_SetKey(&ctx, secbits); + switch (inc->inc_flags & INC_ISIPV6) { +#ifdef INET + case 0: + SipHash_Update(&ctx, &inc->inc_faddr, sizeof(inc->inc_faddr)); + SipHash_Update(&ctx, &inc->inc_laddr, sizeof(inc->inc_laddr)); + break; +#endif +#ifdef INET6 + case 1: + SipHash_Update(&ctx, &inc->inc6_faddr, sizeof(inc->inc6_faddr)); + SipHash_Update(&ctx, &inc->inc6_laddr, sizeof(inc->inc6_laddr)); + break; +#endif + } + SipHash_Update(&ctx, &inc->inc_fport, sizeof(inc->inc_fport)); + SipHash_Update(&ctx, &inc->inc_lport, sizeof(inc->inc_lport)); + SipHash_Update(&ctx, &flags, sizeof(flags)); + SipHash_Update(&ctx, &secmod, sizeof(secmod)); + SipHash_Final((u_int8_t *)&siphash, &ctx); + + return (siphash[0] ^ siphash[1]); +} + +static tcp_seq +syncookie_generate(struct syncache_head *sch, struct syncache *sc) +{ + u_int i, mss, secbit, wscale; + uint32_t iss, hash; + uint8_t *secbits; + union syncookie cookie; + SCH_LOCK_ASSERT(sch); - /* Which of the two secrets to use. */ - secbits = sch->sch_oddeven ? - sch->sch_secbits_odd : sch->sch_secbits_even; + cookie.cookie = 0; - /* Reseed secret if too old. */ - if (sch->sch_reseed < time_uptime) { - sch->sch_oddeven = sch->sch_oddeven ? 0 : 1; /* toggle */ - secbits = sch->sch_oddeven ? - sch->sch_secbits_odd : sch->sch_secbits_even; - for (i = 0; i < SYNCOOKIE_SECRET_SIZE; i++) - secbits[i] = arc4random(); - sch->sch_reseed = time_uptime + SYNCOOKIE_LIFETIME; + /* Map our computed MSS into the 3-bit index. */ + mss = min(tcp_mssopt(&sc->sc_inc), max(sc->sc_peer_mss, V_tcp_minmss)); + for (i = sizeof(tcp_sc_msstab) / sizeof(*tcp_sc_msstab) - 1; + tcp_sc_msstab[i] > mss && i > 0; + i--) + ; + cookie.flags.mss_idx = i; + + /* + * Map the send window scale into the 3-bit index but only if + * the wscale option was received. + */ + if (sc->sc_flags & SCF_WINSCALE) { + wscale = sc->sc_requested_s_scale; + for (i = sizeof(tcp_sc_wstab) / sizeof(*tcp_sc_wstab) - 1; + tcp_sc_wstab[i] > wscale && i > 0; + i--) + ; + cookie.flags.wscale_idx = i; } - /* Secret rotation offset. */ - off = sc->sc_iss & 0x7; /* iss was randomized before */ + /* Can we do SACK? */ + if (sc->sc_flags & SCF_SACK) + cookie.flags.sack_ok = 1; - /* Maximum segment size calculation. */ - pmss = - max( min(sc->sc_peer_mss, tcp_mssopt(&sc->sc_inc)), V_tcp_minmss); - for (mss = sizeof(tcp_sc_msstab) / sizeof(int) - 1; mss > 0; mss--) - if (tcp_sc_msstab[mss] <= pmss) - break; + /* Which of the two secrets to use. */ + secbit = sch->sch_sc->secret.oddeven & 0x1; + cookie.flags.odd_even = secbit; - /* Fold parameters and MD5 digest into the ISN we will send. */ - data = sch->sch_oddeven;/* odd or even secret, 1 bit */ - data |= off << 1; /* secret offset, derived from iss, 3 bits */ - data |= mss << 4; /* mss, 3 bits */ + secbits = sch->sch_sc->secret.key[secbit]; + hash = syncookie_mac(&sc->sc_inc, sc->sc_irs, cookie.cookie, secbits, + (uintptr_t)sch); - MD5Init(&ctx); - MD5Update(&ctx, ((u_int8_t *)secbits) + off, - SYNCOOKIE_SECRET_SIZE * sizeof(*secbits) - off); - MD5Update(&ctx, secbits, off); - MD5Update(&ctx, &sc->sc_inc, sizeof(sc->sc_inc)); - MD5Update(&ctx, &sc->sc_irs, sizeof(sc->sc_irs)); - MD5Update(&ctx, &data, sizeof(data)); - MD5Final((u_int8_t *)&md5_buffer, &ctx); + /* + * Put the flags into the hash and XOR them to get better ISS number + * variance. This doesn't enhance the cryptographic strength and is + * done to prevent the 8 cookie bits from showing up directly on the + * wire. + */ + iss = hash & ~0xff; + iss |= cookie.cookie ^ (hash >> 24); - data |= (md5_buffer[0] << 7); - sc->sc_iss = data; - -#ifdef INET6 - *flowlabel = md5_buffer[1] & IPV6_FLOWLABEL_MASK; -#endif - - /* Additional parameters are stored in the timestamp if present. */ + /* Randomize the timestamp. */ if (sc->sc_flags & SCF_TIMESTAMP) { - data = ((sc->sc_flags & SCF_SIGNATURE) ? 1 : 0); /* TCP-MD5, 1 bit */ - data |= ((sc->sc_flags & SCF_SACK) ? 1 : 0) << 1; /* SACK, 1 bit */ - data |= sc->sc_requested_s_scale << 2; /* SWIN scale, 4 bits */ - data |= sc->sc_requested_r_scale << 6; /* RWIN scale, 4 bits */ - data |= md5_buffer[2] << 10; /* more digest bits */ - data ^= md5_buffer[3]; - sc->sc_ts = data; - sc->sc_tsoff = data - tcp_ts_getticks(); /* after XOR */ + sc->sc_ts = arc4random(); + sc->sc_tsoff = sc->sc_ts - tcp_ts_getticks(); } TCPSTAT_INC(tcps_sc_sendcookie); + return (iss); } static struct syncache * syncookie_lookup(struct in_conninfo *inc, struct syncache_head *sch, - struct syncache *sc, struct tcpopt *to, struct tcphdr *th, - struct socket *so) + struct syncache *sc, struct tcphdr *th, struct tcpopt *to, + struct socket *lso) { - MD5_CTX ctx; - u_int32_t md5_buffer[MD5_DIGEST_LENGTH / sizeof(u_int32_t)]; - u_int32_t data = 0; - u_int32_t *secbits; + uint32_t hash; + uint8_t *secbits; tcp_seq ack, seq; - int off, mss, wnd, flags; + int wnd, wscale = 0; + union syncookie cookie; SCH_LOCK_ASSERT(sch); /* - * Pull information out of SYN-ACK/ACK and - * revert sequence number advances. + * Pull information out of SYN-ACK/ACK and revert sequence number + * advances. */ ack = th->th_ack - 1; seq = th->th_seq - 1; - off = (ack >> 1) & 0x7; - mss = (ack >> 4) & 0x7; - flags = ack & 0x7f; - /* Which of the two secrets to use. */ - secbits = (flags & 0x1) ? sch->sch_secbits_odd : sch->sch_secbits_even; - /* - * The secret wasn't updated for the lifetime of a syncookie, - * so this SYN-ACK/ACK is either too old (replay) or totally bogus. + * Unpack the flags containing enough information to restore the + * connection. */ - if (sch->sch_reseed + SYNCOOKIE_LIFETIME < time_uptime) { - return (NULL); - } + cookie.cookie = (ack & 0xff) ^ (ack >> 24); - /* Recompute the digest so we can compare it. */ - MD5Init(&ctx); - MD5Update(&ctx, ((u_int8_t *)secbits) + off, - SYNCOOKIE_SECRET_SIZE * sizeof(*secbits) - off); - MD5Update(&ctx, secbits, off); - MD5Update(&ctx, inc, sizeof(*inc)); - MD5Update(&ctx, &seq, sizeof(seq)); - MD5Update(&ctx, &flags, sizeof(flags)); - MD5Final((u_int8_t *)&md5_buffer, &ctx); + /* Which of the two secrets to use. */ + secbits = sch->sch_sc->secret.key[cookie.flags.odd_even]; - /* Does the digest part of or ACK'ed ISS match? */ - if ((ack & (~0x7f)) != (md5_buffer[0] << 7)) + hash = syncookie_mac(inc, seq, cookie.cookie, secbits, (uintptr_t)sch); + + /* The recomputed hash matches the ACK if this was a genuine cookie. */ + if ((ack & ~0xff) != (hash & ~0xff)) return (NULL); - /* Does the digest part of our reflected timestamp match? */ - if (to->to_flags & TOF_TS) { - data = md5_buffer[3] ^ to->to_tsecr; - if ((data & (~0x3ff)) != (md5_buffer[2] << 10)) - return (NULL); - } - /* Fill in the syncache values. */ + sc->sc_flags = 0; bcopy(inc, &sc->sc_inc, sizeof(struct in_conninfo)); sc->sc_ipopts = NULL; @@ -1742,52 +1813,123 @@ sc->sc_irs = seq; sc->sc_iss = ack; + switch (inc->inc_flags & INC_ISIPV6) { #ifdef INET6 - if (inc->inc_flags & INC_ISIPV6) { - if (sotoinpcb(so)->inp_flags & IN6P_AUTOFLOWLABEL) - sc->sc_flowlabel = md5_buffer[1] & IPV6_FLOWLABEL_MASK; - } else + case 1: + if (sotoinpcb(lso)->inp_flags & IN6P_AUTOFLOWLABEL) + sc->sc_flowlabel = sc->sc_iss & IPV6_FLOWLABEL_MASK; + break; #endif - { - sc->sc_ip_ttl = sotoinpcb(so)->inp_ip_ttl; - sc->sc_ip_tos = sotoinpcb(so)->inp_ip_tos; +#ifdef INET + case 0: + sc->sc_ip_ttl = sotoinpcb(lso)->inp_ip_ttl; + sc->sc_ip_tos = sotoinpcb(lso)->inp_ip_tos; + break; +#endif } - /* Additional parameters that were encoded in the timestamp. */ - if (data) { + sc->sc_peer_mss = tcp_sc_msstab[cookie.flags.mss_idx]; + + /* We can simply recompute receive window scale we sent earlier. */ + while (wscale < TCP_MAX_WINSHIFT && (TCP_MAXWIN << wscale) < sb_max) + wscale++; + + /* Only use wscale if it was enabled in the orignal SYN. */ + if (cookie.flags.wscale_idx > 0) { + sc->sc_requested_r_scale = wscale; + sc->sc_requested_s_scale = tcp_sc_wstab[cookie.flags.wscale_idx]; + sc->sc_flags |= SCF_WINSCALE; + } + + wnd = sbspace(&lso->so_rcv); + wnd = imax(wnd, 0); + wnd = imin(wnd, TCP_MAXWIN); + sc->sc_wnd = wnd; + + if (cookie.flags.sack_ok) + sc->sc_flags |= SCF_SACK; + + if (to->to_flags & TOF_TS) { sc->sc_flags |= SCF_TIMESTAMP; sc->sc_tsreflect = to->to_tsval; sc->sc_ts = to->to_tsecr; sc->sc_tsoff = to->to_tsecr - tcp_ts_getticks(); - sc->sc_flags |= (data & 0x1) ? SCF_SIGNATURE : 0; - sc->sc_flags |= ((data >> 1) & 0x1) ? SCF_SACK : 0; - sc->sc_requested_s_scale = min((data >> 2) & 0xf, - TCP_MAX_WINSHIFT); - sc->sc_requested_r_scale = min((data >> 6) & 0xf, - TCP_MAX_WINSHIFT); - if (sc->sc_requested_s_scale || sc->sc_requested_r_scale) - sc->sc_flags |= SCF_WINSCALE; - } else - sc->sc_flags |= SCF_NOOPT; + } - wnd = sbspace(&so->so_rcv); - wnd = imax(wnd, 0); - wnd = imin(wnd, TCP_MAXWIN); - sc->sc_wnd = wnd; + if (to->to_flags & TOF_SIGNATURE) + sc->sc_flags |= SCF_SIGNATURE; sc->sc_rxmits = 0; - sc->sc_peer_mss = tcp_sc_msstab[mss]; TCPSTAT_INC(tcps_sc_recvcookie); return (sc); } +static int +syncookie_cmp(struct in_conninfo *inc, struct syncache_head *sch, + struct syncache *sc, struct tcphdr *th, struct tcpopt *to, + struct socket *lso) +{ + struct syncache scs, *scx; + char *s; + + bzero(&scs, sizeof(scs)); + scx = syncookie_lookup(inc, sch, &scs, th, to, lso); + + if ((s = tcp_log_addrs(inc, th, NULL, NULL)) == NULL) + return (0); + + if (scx != NULL) { + if (sc->sc_peer_mss != scx->sc_peer_mss) + log(LOG_DEBUG, "%s; %s: mss different %i vs %i\n", + s, __func__, sc->sc_peer_mss, scx->sc_peer_mss); + + if (sc->sc_requested_r_scale != scx->sc_requested_r_scale) + log(LOG_DEBUG, "%s; %s: rwscale different %i vs %i\n", + s, __func__, sc->sc_requested_r_scale, + scx->sc_requested_r_scale); + + if (sc->sc_requested_s_scale != scx->sc_requested_s_scale) + log(LOG_DEBUG, "%s; %s: swscale different %i vs %i\n", + s, __func__, sc->sc_requested_s_scale, + scx->sc_requested_s_scale); + + if ((sc->sc_flags & SCF_SACK) != (scx->sc_flags & SCF_SACK)) + log(LOG_DEBUG, "%s; %s: SACK different\n", s, __func__); + } + + if (s != NULL) + free(s, M_TCPLOG); + return (0); +} + +static void +syncookie_reseed(void *arg) +{ + struct tcp_syncache *sc = arg; + uint8_t *secbits; + int secbit; + + /* + * Reseeding the secret doesn't have to be protected by a lock. + * It only must be ensured that the new random values are visible + * to all CPUs in a SMP environment. The atomic with release + * semantics ensures that. + */ + secbit = (sc->secret.oddeven & 0x1) ? 0 : 1; + secbits = sc->secret.key[secbit]; + arc4rand(secbits, SYNCOOKIE_SECRET_SIZE, 0); + atomic_add_rel_int(&sc->secret.oddeven, 1); + + /* Reschedule ourself. */ + callout_schedule(&sc->secret.reseed, SYNCOOKIE_LIFETIME * hz); +} + /* * Returns the current number of syncache entries. This number * will probably change before you get around to calling * syncache_pcblist. */ - int syncache_pcbcount(void) { Index: netinet/tcp_syncache.h =================================================================== --- netinet/tcp_syncache.h (revision 253028) +++ netinet/tcp_syncache.h (working copy) @@ -90,22 +90,25 @@ #define SCF_SACK 0x80 /* send SACK option */ #define SCF_ECN 0x100 /* send ECN setup packet */ -#define SYNCOOKIE_SECRET_SIZE 8 /* dwords */ -#define SYNCOOKIE_LIFETIME 16 /* seconds */ - struct syncache_head { - struct vnet *sch_vnet; struct mtx sch_mtx; TAILQ_HEAD(sch_head, syncache) sch_bucket; struct callout sch_timer; int sch_nextc; u_int sch_length; - u_int sch_oddeven; - u_int32_t sch_secbits_odd[SYNCOOKIE_SECRET_SIZE]; - u_int32_t sch_secbits_even[SYNCOOKIE_SECRET_SIZE]; - u_int sch_reseed; /* time_uptime, seconds */ + struct tcp_syncache *sch_sc; }; +#define SYNCOOKIE_SECRET_SIZE 16 +#define SYNCOOKIE_LIFETIME 15 /* seconds */ + +struct syncookie_secret { + volatile u_int oddeven; + uint8_t key[2][SYNCOOKIE_SECRET_SIZE]; + struct callout reseed; + u_int lifetime; +}; + struct tcp_syncache { struct syncache_head *hashbase; uma_zone_t zone; @@ -115,7 +118,20 @@ u_int cache_limit; u_int rexmt_limit; u_int hash_secret; + struct vnet *sch_vnet; + struct syncookie_secret secret; }; +/* Internal use for the syncookie functions. */ +union syncookie { + uint8_t cookie; + struct { + uint8_t odd_even:1, + sack_ok:1, + wscale_idx:3, + mss_idx:3; + } flags; +}; + #endif /* _KERNEL */ #endif /* !_NETINET_TCP_SYNCACHE_H_ */