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