1292706Spkelsey/*- 2292706Spkelsey * Copyright (c) 2015 Patrick Kelsey 3292706Spkelsey * All rights reserved. 4292706Spkelsey * 5292706Spkelsey * Redistribution and use in source and binary forms, with or without 6292706Spkelsey * modification, are permitted provided that the following conditions 7292706Spkelsey * are met: 8292706Spkelsey * 1. Redistributions of source code must retain the above copyright 9292706Spkelsey * notice, this list of conditions and the following disclaimer. 10292706Spkelsey * 2. Redistributions in binary form must reproduce the above copyright 11292706Spkelsey * notice, this list of conditions and the following disclaimer in the 12292706Spkelsey * documentation and/or other materials provided with the distribution. 13292706Spkelsey * 14292706Spkelsey * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15292706Spkelsey * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16292706Spkelsey * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17292706Spkelsey * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18292706Spkelsey * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19292706Spkelsey * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20292706Spkelsey * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21292706Spkelsey * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22292706Spkelsey * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23292706Spkelsey * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24292706Spkelsey * SUCH DAMAGE. 25292706Spkelsey */ 26292706Spkelsey 27292706Spkelsey/* 28292706Spkelsey * This is a server-side implementation of TCP Fast Open (TFO) [RFC7413]. 29292706Spkelsey * 30292706Spkelsey * This implementation is currently considered to be experimental and is not 31292706Spkelsey * included in kernel builds by default. To include this code, add the 32292706Spkelsey * following line to your kernel config: 33292706Spkelsey * 34292706Spkelsey * options TCP_RFC7413 35292706Spkelsey * 36292706Spkelsey * The generated TFO cookies are the 64-bit output of 37292706Spkelsey * SipHash24(<16-byte-key><client-ip>). Multiple concurrent valid keys are 38292706Spkelsey * supported so that time-based rolling cookie invalidation policies can be 39292706Spkelsey * implemented in the system. The default number of concurrent keys is 2. 40292706Spkelsey * This can be adjusted in the kernel config as follows: 41292706Spkelsey * 42292706Spkelsey * options TCP_RFC7413_MAX_KEYS=<num-keys> 43292706Spkelsey * 44292706Spkelsey * 45292706Spkelsey * The following TFO-specific sysctls are defined: 46292706Spkelsey * 47292706Spkelsey * net.inet.tcp.fastopen.acceptany (RW, default 0) 48292706Spkelsey * When non-zero, all client-supplied TFO cookies will be considered to 49292706Spkelsey * be valid. 50292706Spkelsey * 51292706Spkelsey * net.inet.tcp.fastopen.autokey (RW, default 120) 52292706Spkelsey * When this and net.inet.tcp.fastopen.enabled are non-zero, a new key 53292706Spkelsey * will be automatically generated after this many seconds. 54292706Spkelsey * 55292706Spkelsey * net.inet.tcp.fastopen.enabled (RW, default 0) 56292706Spkelsey * When zero, no new TFO connections can be created. On the transition 57292706Spkelsey * from enabled to disabled, all installed keys are removed. On the 58292706Spkelsey * transition from disabled to enabled, if net.inet.tcp.fastopen.autokey 59292706Spkelsey * is non-zero and there are no keys installed, a new key will be 60292706Spkelsey * generated immediately. The transition from enabled to disabled does 61292706Spkelsey * not affect any TFO connections in progress; it only prevents new ones 62292706Spkelsey * from being made. 63292706Spkelsey * 64292706Spkelsey * net.inet.tcp.fastopen.keylen (RO) 65292706Spkelsey * The key length in bytes. 66292706Spkelsey * 67292706Spkelsey * net.inet.tcp.fastopen.maxkeys (RO) 68292706Spkelsey * The maximum number of keys supported. 69292706Spkelsey * 70292706Spkelsey * net.inet.tcp.fastopen.numkeys (RO) 71292706Spkelsey * The current number of keys installed. 72292706Spkelsey * 73292706Spkelsey * net.inet.tcp.fastopen.setkey (WO) 74292706Spkelsey * Install a new key by writing net.inet.tcp.fastopen.keylen bytes to this 75292706Spkelsey * sysctl. 76292706Spkelsey * 77292706Spkelsey * 78292706Spkelsey * In order for TFO connections to be created via a listen socket, that 79292706Spkelsey * socket must have the TCP_FASTOPEN socket option set on it. This option 80292706Spkelsey * can be set on the socket either before or after the listen() is invoked. 81292706Spkelsey * Clearing this option on a listen socket after it has been set has no 82292706Spkelsey * effect on existing TFO connections or TFO connections in progress; it 83292706Spkelsey * only prevents new TFO connections from being made. 84292706Spkelsey * 85292706Spkelsey * For passively-created sockets, the TCP_FASTOPEN socket option can be 86292706Spkelsey * queried to determine whether the connection was established using TFO. 87292706Spkelsey * Note that connections that are established via a TFO SYN, but that fall 88292706Spkelsey * back to using a non-TFO SYN|ACK will have the TCP_FASTOPEN socket option 89292706Spkelsey * set. 90292706Spkelsey * 91292706Spkelsey * Per the RFC, this implementation limits the number of TFO connections 92292706Spkelsey * that can be in the SYN_RECEIVED state on a per listen-socket basis. 93292706Spkelsey * Whenever this limit is exceeded, requests for new TFO connections are 94292706Spkelsey * serviced as non-TFO requests. Without such a limit, given a valid TFO 95292706Spkelsey * cookie, an attacker could keep the listen queue in an overflow condition 96292706Spkelsey * using a TFO SYN flood. This implementation sets the limit at half the 97292706Spkelsey * configured listen backlog. 98292706Spkelsey * 99292706Spkelsey */ 100292706Spkelsey 101292706Spkelsey#include <sys/cdefs.h> 102292706Spkelsey__FBSDID("$FreeBSD: stable/11/sys/netinet/tcp_fastopen.c 339037 2018-10-01 09:40:41Z ae $"); 103292706Spkelsey 104292706Spkelsey#include "opt_inet.h" 105292706Spkelsey 106292706Spkelsey#include <sys/param.h> 107292706Spkelsey#include <sys/kernel.h> 108292706Spkelsey#include <sys/limits.h> 109292706Spkelsey#include <sys/lock.h> 110292706Spkelsey#include <sys/rmlock.h> 111304086Skarels#include <sys/socket.h> 112292706Spkelsey#include <sys/socketvar.h> 113292706Spkelsey#include <sys/sysctl.h> 114292706Spkelsey#include <sys/systm.h> 115292706Spkelsey 116292706Spkelsey#include <crypto/siphash/siphash.h> 117292706Spkelsey 118292706Spkelsey#include <net/vnet.h> 119292706Spkelsey 120292706Spkelsey#include <netinet/in.h> 121292706Spkelsey#include <netinet/in_pcb.h> 122292706Spkelsey#include <netinet/tcp_fastopen.h> 123292706Spkelsey#include <netinet/tcp_var.h> 124292706Spkelsey 125292706Spkelsey 126292706Spkelsey#define TCP_FASTOPEN_KEY_LEN SIPHASH_KEY_LENGTH 127292706Spkelsey 128292706Spkelsey#if !defined(TCP_RFC7413_MAX_KEYS) || (TCP_RFC7413_MAX_KEYS < 1) 129292706Spkelsey#define TCP_FASTOPEN_MAX_KEYS 2 130292706Spkelsey#else 131292706Spkelsey#define TCP_FASTOPEN_MAX_KEYS TCP_RFC7413_MAX_KEYS 132292706Spkelsey#endif 133292706Spkelsey 134292706Spkelseystruct tcp_fastopen_keylist { 135292706Spkelsey unsigned int newest; 136292706Spkelsey uint8_t key[TCP_FASTOPEN_MAX_KEYS][TCP_FASTOPEN_KEY_LEN]; 137292706Spkelsey}; 138292706Spkelsey 139292706Spkelseystruct tcp_fastopen_callout { 140292706Spkelsey struct callout c; 141292706Spkelsey struct vnet *v; 142292706Spkelsey}; 143292706Spkelsey 144292706SpkelseySYSCTL_NODE(_net_inet_tcp, OID_AUTO, fastopen, CTLFLAG_RW, 0, "TCP Fast Open"); 145292706Spkelsey 146292706Spkelseystatic VNET_DEFINE(int, tcp_fastopen_acceptany) = 0; 147292706Spkelsey#define V_tcp_fastopen_acceptany VNET(tcp_fastopen_acceptany) 148292706SpkelseySYSCTL_INT(_net_inet_tcp_fastopen, OID_AUTO, acceptany, 149292706Spkelsey CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(tcp_fastopen_acceptany), 0, 150292706Spkelsey "Accept any non-empty cookie"); 151292706Spkelsey 152292706Spkelseystatic VNET_DEFINE(unsigned int, tcp_fastopen_autokey) = 120; 153292706Spkelsey#define V_tcp_fastopen_autokey VNET(tcp_fastopen_autokey) 154292706Spkelseystatic int sysctl_net_inet_tcp_fastopen_autokey(SYSCTL_HANDLER_ARGS); 155292706SpkelseySYSCTL_PROC(_net_inet_tcp_fastopen, OID_AUTO, autokey, 156292706Spkelsey CTLFLAG_VNET | CTLTYPE_UINT | CTLFLAG_RW, NULL, 0, 157292706Spkelsey &sysctl_net_inet_tcp_fastopen_autokey, "IU", 158292706Spkelsey "Number of seconds between auto-generation of a new key; zero disables"); 159292706Spkelsey 160292706SpkelseyVNET_DEFINE(unsigned int, tcp_fastopen_enabled) = 0; 161292706Spkelseystatic int sysctl_net_inet_tcp_fastopen_enabled(SYSCTL_HANDLER_ARGS); 162292706SpkelseySYSCTL_PROC(_net_inet_tcp_fastopen, OID_AUTO, enabled, 163292706Spkelsey CTLFLAG_VNET | CTLTYPE_UINT | CTLFLAG_RW, NULL, 0, 164292706Spkelsey &sysctl_net_inet_tcp_fastopen_enabled, "IU", 165292706Spkelsey "Enable/disable TCP Fast Open processing"); 166292706Spkelsey 167292706SpkelseySYSCTL_INT(_net_inet_tcp_fastopen, OID_AUTO, keylen, 168292706Spkelsey CTLFLAG_RD, SYSCTL_NULL_INT_PTR, TCP_FASTOPEN_KEY_LEN, 169292706Spkelsey "Key length in bytes"); 170292706Spkelsey 171292706SpkelseySYSCTL_INT(_net_inet_tcp_fastopen, OID_AUTO, maxkeys, 172292706Spkelsey CTLFLAG_RD, SYSCTL_NULL_INT_PTR, TCP_FASTOPEN_MAX_KEYS, 173292706Spkelsey "Maximum number of keys supported"); 174292706Spkelsey 175292706Spkelseystatic VNET_DEFINE(unsigned int, tcp_fastopen_numkeys) = 0; 176292706Spkelsey#define V_tcp_fastopen_numkeys VNET(tcp_fastopen_numkeys) 177292706SpkelseySYSCTL_UINT(_net_inet_tcp_fastopen, OID_AUTO, numkeys, 178292706Spkelsey CTLFLAG_VNET | CTLFLAG_RD, &VNET_NAME(tcp_fastopen_numkeys), 0, 179292706Spkelsey "Number of keys installed"); 180292706Spkelsey 181292706Spkelseystatic int sysctl_net_inet_tcp_fastopen_setkey(SYSCTL_HANDLER_ARGS); 182292706SpkelseySYSCTL_PROC(_net_inet_tcp_fastopen, OID_AUTO, setkey, 183292706Spkelsey CTLFLAG_VNET | CTLTYPE_OPAQUE | CTLFLAG_WR, NULL, 0, 184292706Spkelsey &sysctl_net_inet_tcp_fastopen_setkey, "", 185292706Spkelsey "Install a new key"); 186292706Spkelsey 187292706Spkelseystatic VNET_DEFINE(struct rmlock, tcp_fastopen_keylock); 188292706Spkelsey#define V_tcp_fastopen_keylock VNET(tcp_fastopen_keylock) 189292706Spkelsey 190292706Spkelsey#define TCP_FASTOPEN_KEYS_RLOCK(t) rm_rlock(&V_tcp_fastopen_keylock, (t)) 191292706Spkelsey#define TCP_FASTOPEN_KEYS_RUNLOCK(t) rm_runlock(&V_tcp_fastopen_keylock, (t)) 192292706Spkelsey#define TCP_FASTOPEN_KEYS_WLOCK() rm_wlock(&V_tcp_fastopen_keylock) 193292706Spkelsey#define TCP_FASTOPEN_KEYS_WUNLOCK() rm_wunlock(&V_tcp_fastopen_keylock) 194292706Spkelsey 195292706Spkelseystatic VNET_DEFINE(struct tcp_fastopen_keylist, tcp_fastopen_keys); 196292706Spkelsey#define V_tcp_fastopen_keys VNET(tcp_fastopen_keys) 197292706Spkelsey 198292706Spkelseystatic VNET_DEFINE(struct tcp_fastopen_callout, tcp_fastopen_autokey_ctx); 199292706Spkelsey#define V_tcp_fastopen_autokey_ctx VNET(tcp_fastopen_autokey_ctx) 200292706Spkelsey 201292706Spkelseystatic VNET_DEFINE(uma_zone_t, counter_zone); 202292706Spkelsey#define V_counter_zone VNET(counter_zone) 203292706Spkelsey 204292706Spkelseyvoid 205292706Spkelseytcp_fastopen_init(void) 206292706Spkelsey{ 207292706Spkelsey V_counter_zone = uma_zcreate("tfo", sizeof(unsigned int), 208297738Sbz NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); 209292706Spkelsey rm_init(&V_tcp_fastopen_keylock, "tfo_keylock"); 210292706Spkelsey callout_init_rm(&V_tcp_fastopen_autokey_ctx.c, 211292706Spkelsey &V_tcp_fastopen_keylock, 0); 212339037Sae V_tcp_fastopen_autokey_ctx.v = curvnet; 213292706Spkelsey V_tcp_fastopen_keys.newest = TCP_FASTOPEN_MAX_KEYS - 1; 214292706Spkelsey} 215292706Spkelsey 216292706Spkelseyvoid 217292706Spkelseytcp_fastopen_destroy(void) 218292706Spkelsey{ 219292706Spkelsey callout_drain(&V_tcp_fastopen_autokey_ctx.c); 220292706Spkelsey rm_destroy(&V_tcp_fastopen_keylock); 221292706Spkelsey uma_zdestroy(V_counter_zone); 222292706Spkelsey} 223292706Spkelsey 224292706Spkelseyunsigned int * 225292706Spkelseytcp_fastopen_alloc_counter(void) 226292706Spkelsey{ 227292706Spkelsey unsigned int *counter; 228292706Spkelsey counter = uma_zalloc(V_counter_zone, M_NOWAIT); 229292706Spkelsey if (counter) 230292706Spkelsey *counter = 1; 231292706Spkelsey return (counter); 232292706Spkelsey} 233292706Spkelsey 234292706Spkelseyvoid 235292706Spkelseytcp_fastopen_decrement_counter(unsigned int *counter) 236292706Spkelsey{ 237292706Spkelsey if (*counter == 1) 238292706Spkelsey uma_zfree(V_counter_zone, counter); 239292706Spkelsey else 240292706Spkelsey atomic_subtract_int(counter, 1); 241292706Spkelsey} 242292706Spkelsey 243292706Spkelseystatic void 244292706Spkelseytcp_fastopen_addkey_locked(uint8_t *key) 245292706Spkelsey{ 246292706Spkelsey 247292706Spkelsey V_tcp_fastopen_keys.newest++; 248292706Spkelsey if (V_tcp_fastopen_keys.newest == TCP_FASTOPEN_MAX_KEYS) 249292706Spkelsey V_tcp_fastopen_keys.newest = 0; 250292706Spkelsey memcpy(V_tcp_fastopen_keys.key[V_tcp_fastopen_keys.newest], key, 251292706Spkelsey TCP_FASTOPEN_KEY_LEN); 252292706Spkelsey if (V_tcp_fastopen_numkeys < TCP_FASTOPEN_MAX_KEYS) 253292706Spkelsey V_tcp_fastopen_numkeys++; 254292706Spkelsey} 255292706Spkelsey 256292706Spkelseystatic void 257292706Spkelseytcp_fastopen_autokey_locked(void) 258292706Spkelsey{ 259292706Spkelsey uint8_t newkey[TCP_FASTOPEN_KEY_LEN]; 260292706Spkelsey 261292706Spkelsey arc4rand(newkey, TCP_FASTOPEN_KEY_LEN, 0); 262292706Spkelsey tcp_fastopen_addkey_locked(newkey); 263292706Spkelsey} 264292706Spkelsey 265292706Spkelseystatic void 266292706Spkelseytcp_fastopen_autokey_callout(void *arg) 267292706Spkelsey{ 268292706Spkelsey struct tcp_fastopen_callout *ctx = arg; 269292706Spkelsey 270292706Spkelsey CURVNET_SET(ctx->v); 271292706Spkelsey tcp_fastopen_autokey_locked(); 272292706Spkelsey callout_reset(&ctx->c, V_tcp_fastopen_autokey * hz, 273292706Spkelsey tcp_fastopen_autokey_callout, ctx); 274292706Spkelsey CURVNET_RESTORE(); 275292706Spkelsey} 276292706Spkelsey 277292706Spkelsey 278292706Spkelseystatic uint64_t 279292706Spkelseytcp_fastopen_make_cookie(uint8_t key[SIPHASH_KEY_LENGTH], struct in_conninfo *inc) 280292706Spkelsey{ 281292706Spkelsey SIPHASH_CTX ctx; 282292706Spkelsey uint64_t siphash; 283292706Spkelsey 284292706Spkelsey SipHash24_Init(&ctx); 285292706Spkelsey SipHash_SetKey(&ctx, key); 286292706Spkelsey switch (inc->inc_flags & INC_ISIPV6) { 287292706Spkelsey#ifdef INET 288292706Spkelsey case 0: 289292706Spkelsey SipHash_Update(&ctx, &inc->inc_faddr, sizeof(inc->inc_faddr)); 290292706Spkelsey break; 291292706Spkelsey#endif 292292706Spkelsey#ifdef INET6 293292706Spkelsey case INC_ISIPV6: 294292706Spkelsey SipHash_Update(&ctx, &inc->inc6_faddr, sizeof(inc->inc6_faddr)); 295292706Spkelsey break; 296292706Spkelsey#endif 297292706Spkelsey } 298292706Spkelsey SipHash_Final((u_int8_t *)&siphash, &ctx); 299292706Spkelsey 300292706Spkelsey return (siphash); 301292706Spkelsey} 302292706Spkelsey 303292706Spkelsey 304292706Spkelsey/* 305292706Spkelsey * Return values: 306292706Spkelsey * -1 the cookie is invalid and no valid cookie is available 307292706Spkelsey * 0 the cookie is invalid and the latest cookie has been returned 308292706Spkelsey * 1 the cookie is valid and the latest cookie has been returned 309292706Spkelsey */ 310292706Spkelseyint 311292706Spkelseytcp_fastopen_check_cookie(struct in_conninfo *inc, uint8_t *cookie, 312292706Spkelsey unsigned int len, uint64_t *latest_cookie) 313292706Spkelsey{ 314292706Spkelsey struct rm_priotracker tracker; 315292706Spkelsey unsigned int i, key_index; 316292706Spkelsey uint64_t cur_cookie; 317292706Spkelsey 318292706Spkelsey if (V_tcp_fastopen_acceptany) { 319292706Spkelsey *latest_cookie = 0; 320292706Spkelsey return (1); 321292706Spkelsey } 322292706Spkelsey 323292706Spkelsey if (len != TCP_FASTOPEN_COOKIE_LEN) { 324292706Spkelsey if (V_tcp_fastopen_numkeys > 0) { 325292706Spkelsey *latest_cookie = 326292706Spkelsey tcp_fastopen_make_cookie( 327292706Spkelsey V_tcp_fastopen_keys.key[V_tcp_fastopen_keys.newest], 328292706Spkelsey inc); 329292706Spkelsey return (0); 330292706Spkelsey } 331292706Spkelsey return (-1); 332292706Spkelsey } 333292706Spkelsey 334292706Spkelsey /* 335292706Spkelsey * Check against each available key, from newest to oldest. 336292706Spkelsey */ 337292706Spkelsey TCP_FASTOPEN_KEYS_RLOCK(&tracker); 338292706Spkelsey key_index = V_tcp_fastopen_keys.newest; 339292706Spkelsey for (i = 0; i < V_tcp_fastopen_numkeys; i++) { 340292706Spkelsey cur_cookie = 341292706Spkelsey tcp_fastopen_make_cookie(V_tcp_fastopen_keys.key[key_index], 342292706Spkelsey inc); 343292706Spkelsey if (i == 0) 344292706Spkelsey *latest_cookie = cur_cookie; 345292706Spkelsey if (memcmp(cookie, &cur_cookie, TCP_FASTOPEN_COOKIE_LEN) == 0) { 346292706Spkelsey TCP_FASTOPEN_KEYS_RUNLOCK(&tracker); 347292706Spkelsey return (1); 348292706Spkelsey } 349292706Spkelsey if (key_index == 0) 350292706Spkelsey key_index = TCP_FASTOPEN_MAX_KEYS - 1; 351292706Spkelsey else 352292706Spkelsey key_index--; 353292706Spkelsey } 354292706Spkelsey TCP_FASTOPEN_KEYS_RUNLOCK(&tracker); 355292706Spkelsey 356292706Spkelsey return (0); 357292706Spkelsey} 358292706Spkelsey 359292706Spkelseystatic int 360292706Spkelseysysctl_net_inet_tcp_fastopen_autokey(SYSCTL_HANDLER_ARGS) 361292706Spkelsey{ 362292706Spkelsey int error; 363292706Spkelsey unsigned int new; 364292706Spkelsey 365292706Spkelsey new = V_tcp_fastopen_autokey; 366292706Spkelsey error = sysctl_handle_int(oidp, &new, 0, req); 367292706Spkelsey if (error == 0 && req->newptr) { 368292706Spkelsey if (new > (INT_MAX / hz)) 369292706Spkelsey return (EINVAL); 370292706Spkelsey 371292706Spkelsey TCP_FASTOPEN_KEYS_WLOCK(); 372292706Spkelsey if (V_tcp_fastopen_enabled) { 373292706Spkelsey if (V_tcp_fastopen_autokey && !new) 374292706Spkelsey callout_stop(&V_tcp_fastopen_autokey_ctx.c); 375292706Spkelsey else if (new) 376292706Spkelsey callout_reset(&V_tcp_fastopen_autokey_ctx.c, 377292706Spkelsey new * hz, tcp_fastopen_autokey_callout, 378292706Spkelsey &V_tcp_fastopen_autokey_ctx); 379292706Spkelsey } 380292706Spkelsey V_tcp_fastopen_autokey = new; 381292706Spkelsey TCP_FASTOPEN_KEYS_WUNLOCK(); 382292706Spkelsey } 383292706Spkelsey 384292706Spkelsey return (error); 385292706Spkelsey} 386292706Spkelsey 387292706Spkelseystatic int 388292706Spkelseysysctl_net_inet_tcp_fastopen_enabled(SYSCTL_HANDLER_ARGS) 389292706Spkelsey{ 390292706Spkelsey int error; 391292706Spkelsey unsigned int new; 392292706Spkelsey 393292706Spkelsey new = V_tcp_fastopen_enabled; 394292706Spkelsey error = sysctl_handle_int(oidp, &new, 0, req); 395292706Spkelsey if (error == 0 && req->newptr) { 396292706Spkelsey if (V_tcp_fastopen_enabled && !new) { 397292706Spkelsey /* enabled -> disabled */ 398292706Spkelsey TCP_FASTOPEN_KEYS_WLOCK(); 399292706Spkelsey V_tcp_fastopen_numkeys = 0; 400292706Spkelsey V_tcp_fastopen_keys.newest = TCP_FASTOPEN_MAX_KEYS - 1; 401292706Spkelsey if (V_tcp_fastopen_autokey) 402292706Spkelsey callout_stop(&V_tcp_fastopen_autokey_ctx.c); 403292706Spkelsey V_tcp_fastopen_enabled = 0; 404292706Spkelsey TCP_FASTOPEN_KEYS_WUNLOCK(); 405292706Spkelsey } else if (!V_tcp_fastopen_enabled && new) { 406292706Spkelsey /* disabled -> enabled */ 407292706Spkelsey TCP_FASTOPEN_KEYS_WLOCK(); 408292706Spkelsey if (V_tcp_fastopen_autokey && 409292706Spkelsey (V_tcp_fastopen_numkeys == 0)) { 410292706Spkelsey tcp_fastopen_autokey_locked(); 411292706Spkelsey callout_reset(&V_tcp_fastopen_autokey_ctx.c, 412292706Spkelsey V_tcp_fastopen_autokey * hz, 413292706Spkelsey tcp_fastopen_autokey_callout, 414292706Spkelsey &V_tcp_fastopen_autokey_ctx); 415292706Spkelsey } 416292706Spkelsey V_tcp_fastopen_enabled = 1; 417292706Spkelsey TCP_FASTOPEN_KEYS_WUNLOCK(); 418292706Spkelsey } 419292706Spkelsey } 420292706Spkelsey return (error); 421292706Spkelsey} 422292706Spkelsey 423292706Spkelseystatic int 424292706Spkelseysysctl_net_inet_tcp_fastopen_setkey(SYSCTL_HANDLER_ARGS) 425292706Spkelsey{ 426292706Spkelsey int error; 427292706Spkelsey uint8_t newkey[TCP_FASTOPEN_KEY_LEN]; 428292706Spkelsey 429292706Spkelsey if (req->oldptr != NULL || req->oldlen != 0) 430292706Spkelsey return (EINVAL); 431292706Spkelsey if (req->newptr == NULL) 432292706Spkelsey return (EPERM); 433292706Spkelsey if (req->newlen != sizeof(newkey)) 434292706Spkelsey return (EINVAL); 435292706Spkelsey error = SYSCTL_IN(req, newkey, sizeof(newkey)); 436292706Spkelsey if (error) 437292706Spkelsey return (error); 438292706Spkelsey 439292706Spkelsey TCP_FASTOPEN_KEYS_WLOCK(); 440292706Spkelsey tcp_fastopen_addkey_locked(newkey); 441292706Spkelsey TCP_FASTOPEN_KEYS_WUNLOCK(); 442292706Spkelsey 443292706Spkelsey return (0); 444292706Spkelsey} 445