xref: /freebsd-src/sys/netinet/tcp_fastopen.c (revision 9b035689f15fc4aec96f9c18c6c86bd615faed2f)
1281a0fd4SPatrick Kelsey /*-
2c560df6fSPatrick Kelsey  * Copyright (c) 2015-2017 Patrick Kelsey
3281a0fd4SPatrick Kelsey  * All rights reserved.
4281a0fd4SPatrick Kelsey  *
5281a0fd4SPatrick Kelsey  * Redistribution and use in source and binary forms, with or without
6281a0fd4SPatrick Kelsey  * modification, are permitted provided that the following conditions
7281a0fd4SPatrick Kelsey  * are met:
8281a0fd4SPatrick Kelsey  * 1. Redistributions of source code must retain the above copyright
9281a0fd4SPatrick Kelsey  *    notice, this list of conditions and the following disclaimer.
10281a0fd4SPatrick Kelsey  * 2. Redistributions in binary form must reproduce the above copyright
11281a0fd4SPatrick Kelsey  *    notice, this list of conditions and the following disclaimer in the
12281a0fd4SPatrick Kelsey  *    documentation and/or other materials provided with the distribution.
13281a0fd4SPatrick Kelsey  *
14281a0fd4SPatrick Kelsey  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15281a0fd4SPatrick Kelsey  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16281a0fd4SPatrick Kelsey  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17281a0fd4SPatrick Kelsey  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18281a0fd4SPatrick Kelsey  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19281a0fd4SPatrick Kelsey  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20281a0fd4SPatrick Kelsey  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21281a0fd4SPatrick Kelsey  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22281a0fd4SPatrick Kelsey  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23281a0fd4SPatrick Kelsey  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24281a0fd4SPatrick Kelsey  * SUCH DAMAGE.
25281a0fd4SPatrick Kelsey  */
26281a0fd4SPatrick Kelsey 
27281a0fd4SPatrick Kelsey /*
28c560df6fSPatrick Kelsey  * This is an implementation of TCP Fast Open (TFO) [RFC7413]. To include
29c560df6fSPatrick Kelsey  * this code, add the following line to your kernel config:
30281a0fd4SPatrick Kelsey  *
31281a0fd4SPatrick Kelsey  * options TCP_RFC7413
32281a0fd4SPatrick Kelsey  *
33c560df6fSPatrick Kelsey  *
34281a0fd4SPatrick Kelsey  * The generated TFO cookies are the 64-bit output of
35c560df6fSPatrick Kelsey  * SipHash24(key=<16-byte-key>, msg=<client-ip>).  Multiple concurrent valid
36c560df6fSPatrick Kelsey  * keys are supported so that time-based rolling cookie invalidation
37c560df6fSPatrick Kelsey  * policies can be implemented in the system.  The default number of
38c560df6fSPatrick Kelsey  * concurrent keys is 2.  This can be adjusted in the kernel config as
39c560df6fSPatrick Kelsey  * follows:
40281a0fd4SPatrick Kelsey  *
41281a0fd4SPatrick Kelsey  * options TCP_RFC7413_MAX_KEYS=<num-keys>
42281a0fd4SPatrick Kelsey  *
43281a0fd4SPatrick Kelsey  *
44c560df6fSPatrick Kelsey  * In addition to the facilities defined in RFC7413, this implementation
45c560df6fSPatrick Kelsey  * supports a pre-shared key (PSK) mode of operation in which the TFO server
46*9b035689SGordon Bergling  * requires the client to be in possession of a shared secret in order for
47c560df6fSPatrick Kelsey  * the client to be able to successfully open TFO connections with the
48c560df6fSPatrick Kelsey  * server.  This is useful, for example, in environments where TFO servers
49c560df6fSPatrick Kelsey  * are exposed to both internal and external clients and only wish to allow
50c560df6fSPatrick Kelsey  * TFO connections from internal clients.
51c560df6fSPatrick Kelsey  *
52c560df6fSPatrick Kelsey  * In the PSK mode of operation, the server generates and sends TFO cookies
53c560df6fSPatrick Kelsey  * to requesting clients as usual.  However, when validating cookies
54c560df6fSPatrick Kelsey  * received in TFO SYNs from clients, the server requires the
55c560df6fSPatrick Kelsey  * client-supplied cookie to equal SipHash24(key=<16-byte-psk>,
56c560df6fSPatrick Kelsey  * msg=<cookie-sent-to-client>).
57c560df6fSPatrick Kelsey  *
58c560df6fSPatrick Kelsey  * Multiple concurrent valid pre-shared keys are supported so that
59c560df6fSPatrick Kelsey  * time-based rolling PSK invalidation policies can be implemented in the
60c560df6fSPatrick Kelsey  * system.  The default number of concurrent pre-shared keys is 2.  This can
61c560df6fSPatrick Kelsey  * be adjusted in the kernel config as follows:
62c560df6fSPatrick Kelsey  *
63c560df6fSPatrick Kelsey  * options TCP_RFC7413_MAX_PSKS=<num-psks>
64c560df6fSPatrick Kelsey  *
65c560df6fSPatrick Kelsey  *
66281a0fd4SPatrick Kelsey  * The following TFO-specific sysctls are defined:
67281a0fd4SPatrick Kelsey  *
68281a0fd4SPatrick Kelsey  * net.inet.tcp.fastopen.acceptany (RW, default 0)
69281a0fd4SPatrick Kelsey  *     When non-zero, all client-supplied TFO cookies will be considered to
70281a0fd4SPatrick Kelsey  *     be valid.
71281a0fd4SPatrick Kelsey  *
72281a0fd4SPatrick Kelsey  * net.inet.tcp.fastopen.autokey (RW, default 120)
73c560df6fSPatrick Kelsey  *     When this and net.inet.tcp.fastopen.server_enable are non-zero, a new
74c560df6fSPatrick Kelsey  *     key will be automatically generated after this many seconds.
75281a0fd4SPatrick Kelsey  *
76c560df6fSPatrick Kelsey  * net.inet.tcp.fastopen.ccache_bucket_limit
77c560df6fSPatrick Kelsey  *                     (RWTUN, default TCP_FASTOPEN_CCACHE_BUCKET_LIMIT_DEFAULT)
78c560df6fSPatrick Kelsey  *     The maximum number of entries in a client cookie cache bucket.
79281a0fd4SPatrick Kelsey  *
80c560df6fSPatrick Kelsey  * net.inet.tcp.fastopen.ccache_buckets
81c560df6fSPatrick Kelsey  *                          (RDTUN, default TCP_FASTOPEN_CCACHE_BUCKETS_DEFAULT)
82c560df6fSPatrick Kelsey  *     The number of client cookie cache buckets.
83c560df6fSPatrick Kelsey  *
84c9da5853SMichael Tuexen  * net.inet.tcp.fastopen.ccache_list (RO)
85c9da5853SMichael Tuexen  *     Print the client cookie cache.
86c9da5853SMichael Tuexen  *
87c560df6fSPatrick Kelsey  * net.inet.tcp.fastopen.client_enable (RW, default 0)
88c560df6fSPatrick Kelsey  *     When zero, no new active (i.e., client) TFO connections can be
89c560df6fSPatrick Kelsey  *     created.  On the transition from enabled to disabled, the client
90c560df6fSPatrick Kelsey  *     cookie cache is cleared and disabled.  The transition from enabled to
91c560df6fSPatrick Kelsey  *     disabled does not affect any active TFO connections in progress; it
92c560df6fSPatrick Kelsey  *     only prevents new ones from being made.
93c560df6fSPatrick Kelsey  *
94c560df6fSPatrick Kelsey  * net.inet.tcp.fastopen.keylen (RD)
95281a0fd4SPatrick Kelsey  *     The key length in bytes.
96281a0fd4SPatrick Kelsey  *
97c560df6fSPatrick Kelsey  * net.inet.tcp.fastopen.maxkeys (RD)
98281a0fd4SPatrick Kelsey  *     The maximum number of keys supported.
99281a0fd4SPatrick Kelsey  *
100c560df6fSPatrick Kelsey  * net.inet.tcp.fastopen.maxpsks (RD)
101c560df6fSPatrick Kelsey  *     The maximum number of pre-shared keys supported.
102c560df6fSPatrick Kelsey  *
103c560df6fSPatrick Kelsey  * net.inet.tcp.fastopen.numkeys (RD)
104281a0fd4SPatrick Kelsey  *     The current number of keys installed.
105281a0fd4SPatrick Kelsey  *
106c560df6fSPatrick Kelsey  * net.inet.tcp.fastopen.numpsks (RD)
107c560df6fSPatrick Kelsey  *     The current number of pre-shared keys installed.
108281a0fd4SPatrick Kelsey  *
109c560df6fSPatrick Kelsey  * net.inet.tcp.fastopen.path_disable_time
110c560df6fSPatrick Kelsey  *                          (RW, default TCP_FASTOPEN_PATH_DISABLE_TIME_DEFAULT)
111c560df6fSPatrick Kelsey  *     When a failure occurs while trying to create a new active (i.e.,
112c560df6fSPatrick Kelsey  *     client) TFO connection, new active connections on the same path, as
113c560df6fSPatrick Kelsey  *     determined by the tuple {client_ip, server_ip, server_port}, will be
114c560df6fSPatrick Kelsey  *     forced to be non-TFO for this many seconds.  Note that the path
115c560df6fSPatrick Kelsey  *     disable mechanism relies on state stored in client cookie cache
116c560df6fSPatrick Kelsey  *     entries, so it is possible for the disable time for a given path to
117c560df6fSPatrick Kelsey  *     be reduced if the corresponding client cookie cache entry is reused
118c560df6fSPatrick Kelsey  *     due to resource pressure before the disable period has elapsed.
119c560df6fSPatrick Kelsey  *
120c560df6fSPatrick Kelsey  * net.inet.tcp.fastopen.psk_enable (RW, default 0)
121c560df6fSPatrick Kelsey  *     When non-zero, pre-shared key (PSK) mode is enabled for all TFO
122c560df6fSPatrick Kelsey  *     servers.  On the transition from enabled to disabled, all installed
123c560df6fSPatrick Kelsey  *     pre-shared keys are removed.
124c560df6fSPatrick Kelsey  *
125c560df6fSPatrick Kelsey  * net.inet.tcp.fastopen.server_enable (RW, default 0)
126c560df6fSPatrick Kelsey  *     When zero, no new passive (i.e., server) TFO connections can be
127c560df6fSPatrick Kelsey  *     created.  On the transition from enabled to disabled, all installed
128c560df6fSPatrick Kelsey  *     keys and pre-shared keys are removed.  On the transition from
129c560df6fSPatrick Kelsey  *     disabled to enabled, if net.inet.tcp.fastopen.autokey is non-zero and
130c560df6fSPatrick Kelsey  *     there are no keys installed, a new key will be generated immediately.
131c560df6fSPatrick Kelsey  *     The transition from enabled to disabled does not affect any passive
132c560df6fSPatrick Kelsey  *     TFO connections in progress; it only prevents new ones from being
133c560df6fSPatrick Kelsey  *     made.
134c560df6fSPatrick Kelsey  *
135c560df6fSPatrick Kelsey  * net.inet.tcp.fastopen.setkey (WR)
136c560df6fSPatrick Kelsey  *     Install a new key by writing net.inet.tcp.fastopen.keylen bytes to
137c560df6fSPatrick Kelsey  *     this sysctl.
138c560df6fSPatrick Kelsey  *
139c560df6fSPatrick Kelsey  * net.inet.tcp.fastopen.setpsk (WR)
140c560df6fSPatrick Kelsey  *     Install a new pre-shared key by writing net.inet.tcp.fastopen.keylen
141c560df6fSPatrick Kelsey  *     bytes to this sysctl.
142281a0fd4SPatrick Kelsey  *
143281a0fd4SPatrick Kelsey  * In order for TFO connections to be created via a listen socket, that
144281a0fd4SPatrick Kelsey  * socket must have the TCP_FASTOPEN socket option set on it.  This option
145281a0fd4SPatrick Kelsey  * can be set on the socket either before or after the listen() is invoked.
146281a0fd4SPatrick Kelsey  * Clearing this option on a listen socket after it has been set has no
147281a0fd4SPatrick Kelsey  * effect on existing TFO connections or TFO connections in progress; it
148281a0fd4SPatrick Kelsey  * only prevents new TFO connections from being made.
149281a0fd4SPatrick Kelsey  *
150281a0fd4SPatrick Kelsey  * For passively-created sockets, the TCP_FASTOPEN socket option can be
151281a0fd4SPatrick Kelsey  * queried to determine whether the connection was established using TFO.
152281a0fd4SPatrick Kelsey  * Note that connections that are established via a TFO SYN, but that fall
153281a0fd4SPatrick Kelsey  * back to using a non-TFO SYN|ACK will have the TCP_FASTOPEN socket option
154281a0fd4SPatrick Kelsey  * set.
155281a0fd4SPatrick Kelsey  *
156281a0fd4SPatrick Kelsey  * Per the RFC, this implementation limits the number of TFO connections
157281a0fd4SPatrick Kelsey  * that can be in the SYN_RECEIVED state on a per listen-socket basis.
158281a0fd4SPatrick Kelsey  * Whenever this limit is exceeded, requests for new TFO connections are
159281a0fd4SPatrick Kelsey  * serviced as non-TFO requests.  Without such a limit, given a valid TFO
160281a0fd4SPatrick Kelsey  * cookie, an attacker could keep the listen queue in an overflow condition
161281a0fd4SPatrick Kelsey  * using a TFO SYN flood.  This implementation sets the limit at half the
162281a0fd4SPatrick Kelsey  * configured listen backlog.
163281a0fd4SPatrick Kelsey  *
164281a0fd4SPatrick Kelsey  */
165281a0fd4SPatrick Kelsey 
166281a0fd4SPatrick Kelsey #include <sys/cdefs.h>
167281a0fd4SPatrick Kelsey #include "opt_inet.h"
168281a0fd4SPatrick Kelsey 
169281a0fd4SPatrick Kelsey #include <sys/param.h>
170c9da5853SMichael Tuexen #include <sys/jail.h>
171281a0fd4SPatrick Kelsey #include <sys/kernel.h>
172c560df6fSPatrick Kelsey #include <sys/hash.h>
173281a0fd4SPatrick Kelsey #include <sys/limits.h>
174281a0fd4SPatrick Kelsey #include <sys/lock.h>
175c9da5853SMichael Tuexen #include <sys/proc.h>
176281a0fd4SPatrick Kelsey #include <sys/rmlock.h>
177c9da5853SMichael Tuexen #include <sys/sbuf.h>
178bca0155fSMike Karels #include <sys/socket.h>
179281a0fd4SPatrick Kelsey #include <sys/socketvar.h>
180281a0fd4SPatrick Kelsey #include <sys/sysctl.h>
181281a0fd4SPatrick Kelsey #include <sys/systm.h>
182281a0fd4SPatrick Kelsey 
183281a0fd4SPatrick Kelsey #include <crypto/siphash/siphash.h>
184281a0fd4SPatrick Kelsey 
185281a0fd4SPatrick Kelsey #include <net/vnet.h>
186281a0fd4SPatrick Kelsey 
187281a0fd4SPatrick Kelsey #include <netinet/in.h>
188281a0fd4SPatrick Kelsey #include <netinet/in_pcb.h>
189281a0fd4SPatrick Kelsey #include <netinet/tcp_var.h>
190c560df6fSPatrick Kelsey #include <netinet/tcp_fastopen.h>
191281a0fd4SPatrick Kelsey 
192281a0fd4SPatrick Kelsey #define	TCP_FASTOPEN_KEY_LEN	SIPHASH_KEY_LENGTH
193281a0fd4SPatrick Kelsey 
194c560df6fSPatrick Kelsey #if TCP_FASTOPEN_PSK_LEN != TCP_FASTOPEN_KEY_LEN
195c560df6fSPatrick Kelsey #error TCP_FASTOPEN_PSK_LEN must be equal to TCP_FASTOPEN_KEY_LEN
196c560df6fSPatrick Kelsey #endif
197c560df6fSPatrick Kelsey 
198c560df6fSPatrick Kelsey /*
199c560df6fSPatrick Kelsey  * Because a PSK-mode setsockopt() uses tcpcb.t_tfo_cookie.client to hold
200c560df6fSPatrick Kelsey  * the PSK until the connect occurs.
201c560df6fSPatrick Kelsey  */
202c560df6fSPatrick Kelsey #if TCP_FASTOPEN_MAX_COOKIE_LEN < TCP_FASTOPEN_PSK_LEN
203c560df6fSPatrick Kelsey #error TCP_FASTOPEN_MAX_COOKIE_LEN must be >= TCP_FASTOPEN_PSK_LEN
204c560df6fSPatrick Kelsey #endif
205c560df6fSPatrick Kelsey 
206c560df6fSPatrick Kelsey #define TCP_FASTOPEN_CCACHE_BUCKET_LIMIT_DEFAULT	16
207c560df6fSPatrick Kelsey #define TCP_FASTOPEN_CCACHE_BUCKETS_DEFAULT		2048 /* must be power of 2 */
208c560df6fSPatrick Kelsey 
209c560df6fSPatrick Kelsey #define TCP_FASTOPEN_PATH_DISABLE_TIME_DEFAULT		900 /* seconds */
210c560df6fSPatrick Kelsey 
211281a0fd4SPatrick Kelsey #if !defined(TCP_RFC7413_MAX_KEYS) || (TCP_RFC7413_MAX_KEYS < 1)
212281a0fd4SPatrick Kelsey #define	TCP_FASTOPEN_MAX_KEYS	2
213281a0fd4SPatrick Kelsey #else
214281a0fd4SPatrick Kelsey #define	TCP_FASTOPEN_MAX_KEYS	TCP_RFC7413_MAX_KEYS
215281a0fd4SPatrick Kelsey #endif
216281a0fd4SPatrick Kelsey 
217c560df6fSPatrick Kelsey #if TCP_FASTOPEN_MAX_KEYS > 10
218c560df6fSPatrick Kelsey #undef TCP_FASTOPEN_MAX_KEYS
219c560df6fSPatrick Kelsey #define	TCP_FASTOPEN_MAX_KEYS	10
220c560df6fSPatrick Kelsey #endif
221c560df6fSPatrick Kelsey 
222c560df6fSPatrick Kelsey #if !defined(TCP_RFC7413_MAX_PSKS) || (TCP_RFC7413_MAX_PSKS < 1)
223c560df6fSPatrick Kelsey #define	TCP_FASTOPEN_MAX_PSKS	2
224c560df6fSPatrick Kelsey #else
225c560df6fSPatrick Kelsey #define	TCP_FASTOPEN_MAX_PSKS	TCP_RFC7413_MAX_PSKS
226c560df6fSPatrick Kelsey #endif
227c560df6fSPatrick Kelsey 
228c560df6fSPatrick Kelsey #if TCP_FASTOPEN_MAX_PSKS > 10
229c560df6fSPatrick Kelsey #undef TCP_FASTOPEN_MAX_PSKS
230c560df6fSPatrick Kelsey #define	TCP_FASTOPEN_MAX_PSKS	10
231c560df6fSPatrick Kelsey #endif
232c560df6fSPatrick Kelsey 
233281a0fd4SPatrick Kelsey struct tcp_fastopen_keylist {
234281a0fd4SPatrick Kelsey 	unsigned int newest;
235c560df6fSPatrick Kelsey 	unsigned int newest_psk;
236281a0fd4SPatrick Kelsey 	uint8_t key[TCP_FASTOPEN_MAX_KEYS][TCP_FASTOPEN_KEY_LEN];
237c560df6fSPatrick Kelsey 	uint8_t psk[TCP_FASTOPEN_MAX_PSKS][TCP_FASTOPEN_KEY_LEN];
238281a0fd4SPatrick Kelsey };
239281a0fd4SPatrick Kelsey 
240281a0fd4SPatrick Kelsey struct tcp_fastopen_callout {
241281a0fd4SPatrick Kelsey 	struct callout c;
242281a0fd4SPatrick Kelsey 	struct vnet *v;
243281a0fd4SPatrick Kelsey };
244281a0fd4SPatrick Kelsey 
245c560df6fSPatrick Kelsey static struct tcp_fastopen_ccache_entry *tcp_fastopen_ccache_lookup(
246c560df6fSPatrick Kelsey     struct in_conninfo *, struct tcp_fastopen_ccache_bucket **);
247c560df6fSPatrick Kelsey static struct tcp_fastopen_ccache_entry *tcp_fastopen_ccache_create(
248c560df6fSPatrick Kelsey     struct tcp_fastopen_ccache_bucket *, struct in_conninfo *, uint16_t, uint8_t,
249c560df6fSPatrick Kelsey     uint8_t *);
250c560df6fSPatrick Kelsey static void tcp_fastopen_ccache_bucket_trim(struct tcp_fastopen_ccache_bucket *,
251c560df6fSPatrick Kelsey     unsigned int);
252c560df6fSPatrick Kelsey static void tcp_fastopen_ccache_entry_drop(struct tcp_fastopen_ccache_entry *,
253c560df6fSPatrick Kelsey     struct tcp_fastopen_ccache_bucket *);
254c560df6fSPatrick Kelsey 
2557029da5cSPawel Biernacki SYSCTL_NODE(_net_inet_tcp, OID_AUTO, fastopen, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
2567029da5cSPawel Biernacki     "TCP Fast Open");
257281a0fd4SPatrick Kelsey 
2585f901c92SAndrew Turner VNET_DEFINE_STATIC(int, tcp_fastopen_acceptany) = 0;
259281a0fd4SPatrick Kelsey #define	V_tcp_fastopen_acceptany	VNET(tcp_fastopen_acceptany)
260281a0fd4SPatrick Kelsey SYSCTL_INT(_net_inet_tcp_fastopen, OID_AUTO, acceptany,
261281a0fd4SPatrick Kelsey     CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(tcp_fastopen_acceptany), 0,
262281a0fd4SPatrick Kelsey     "Accept any non-empty cookie");
263281a0fd4SPatrick Kelsey 
2645f901c92SAndrew Turner VNET_DEFINE_STATIC(unsigned int, tcp_fastopen_autokey) = 120;
265281a0fd4SPatrick Kelsey #define	V_tcp_fastopen_autokey	VNET(tcp_fastopen_autokey)
266281a0fd4SPatrick Kelsey static int sysctl_net_inet_tcp_fastopen_autokey(SYSCTL_HANDLER_ARGS);
267281a0fd4SPatrick Kelsey SYSCTL_PROC(_net_inet_tcp_fastopen, OID_AUTO, autokey,
2687029da5cSPawel Biernacki     CTLFLAG_VNET | CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_MPSAFE,
2697029da5cSPawel Biernacki     NULL, 0, &sysctl_net_inet_tcp_fastopen_autokey, "IU",
270281a0fd4SPatrick Kelsey     "Number of seconds between auto-generation of a new key; zero disables");
271281a0fd4SPatrick Kelsey 
272c560df6fSPatrick Kelsey static int sysctl_net_inet_tcp_fastopen_ccache_bucket_limit(SYSCTL_HANDLER_ARGS);
273c560df6fSPatrick Kelsey SYSCTL_PROC(_net_inet_tcp_fastopen, OID_AUTO, ccache_bucket_limit,
274224aec05SZhenlei Huang     CTLFLAG_VNET | CTLTYPE_UINT | CTLFLAG_RWTUN | CTLFLAG_NOFETCH | CTLFLAG_NEEDGIANT,
2757029da5cSPawel Biernacki     NULL, 0, &sysctl_net_inet_tcp_fastopen_ccache_bucket_limit, "IU",
276c560df6fSPatrick Kelsey     "Max entries per bucket in client cookie cache");
277c560df6fSPatrick Kelsey 
2785f901c92SAndrew Turner VNET_DEFINE_STATIC(unsigned int, tcp_fastopen_ccache_buckets) =
279c560df6fSPatrick Kelsey     TCP_FASTOPEN_CCACHE_BUCKETS_DEFAULT;
280c560df6fSPatrick Kelsey #define	V_tcp_fastopen_ccache_buckets VNET(tcp_fastopen_ccache_buckets)
281c560df6fSPatrick Kelsey SYSCTL_UINT(_net_inet_tcp_fastopen, OID_AUTO, ccache_buckets,
282c560df6fSPatrick Kelsey     CTLFLAG_VNET | CTLFLAG_RDTUN, &VNET_NAME(tcp_fastopen_ccache_buckets), 0,
283c560df6fSPatrick Kelsey     "Client cookie cache number of buckets (power of 2)");
284c560df6fSPatrick Kelsey 
285af4da586SSean Bruno VNET_DEFINE(unsigned int, tcp_fastopen_client_enable) = 1;
286c560df6fSPatrick Kelsey static int sysctl_net_inet_tcp_fastopen_client_enable(SYSCTL_HANDLER_ARGS);
287c560df6fSPatrick Kelsey SYSCTL_PROC(_net_inet_tcp_fastopen, OID_AUTO, client_enable,
2887029da5cSPawel Biernacki     CTLFLAG_VNET | CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
2897029da5cSPawel Biernacki     NULL, 0, &sysctl_net_inet_tcp_fastopen_client_enable, "IU",
290c560df6fSPatrick Kelsey     "Enable/disable TCP Fast Open client functionality");
291281a0fd4SPatrick Kelsey 
292281a0fd4SPatrick Kelsey SYSCTL_INT(_net_inet_tcp_fastopen, OID_AUTO, keylen,
293281a0fd4SPatrick Kelsey     CTLFLAG_RD, SYSCTL_NULL_INT_PTR, TCP_FASTOPEN_KEY_LEN,
294281a0fd4SPatrick Kelsey     "Key length in bytes");
295281a0fd4SPatrick Kelsey 
296281a0fd4SPatrick Kelsey SYSCTL_INT(_net_inet_tcp_fastopen, OID_AUTO, maxkeys,
297281a0fd4SPatrick Kelsey     CTLFLAG_RD, SYSCTL_NULL_INT_PTR, TCP_FASTOPEN_MAX_KEYS,
298281a0fd4SPatrick Kelsey     "Maximum number of keys supported");
299281a0fd4SPatrick Kelsey 
300c560df6fSPatrick Kelsey SYSCTL_INT(_net_inet_tcp_fastopen, OID_AUTO, maxpsks,
301c560df6fSPatrick Kelsey     CTLFLAG_RD, SYSCTL_NULL_INT_PTR, TCP_FASTOPEN_MAX_PSKS,
302c560df6fSPatrick Kelsey     "Maximum number of pre-shared keys supported");
303c560df6fSPatrick Kelsey 
3045f901c92SAndrew Turner VNET_DEFINE_STATIC(unsigned int, tcp_fastopen_numkeys) = 0;
305281a0fd4SPatrick Kelsey #define	V_tcp_fastopen_numkeys	VNET(tcp_fastopen_numkeys)
306281a0fd4SPatrick Kelsey SYSCTL_UINT(_net_inet_tcp_fastopen, OID_AUTO, numkeys,
307281a0fd4SPatrick Kelsey     CTLFLAG_VNET | CTLFLAG_RD, &VNET_NAME(tcp_fastopen_numkeys), 0,
308281a0fd4SPatrick Kelsey     "Number of keys installed");
309281a0fd4SPatrick Kelsey 
3105f901c92SAndrew Turner VNET_DEFINE_STATIC(unsigned int, tcp_fastopen_numpsks) = 0;
311c560df6fSPatrick Kelsey #define	V_tcp_fastopen_numpsks	VNET(tcp_fastopen_numpsks)
312c560df6fSPatrick Kelsey SYSCTL_UINT(_net_inet_tcp_fastopen, OID_AUTO, numpsks,
313c560df6fSPatrick Kelsey     CTLFLAG_VNET | CTLFLAG_RD, &VNET_NAME(tcp_fastopen_numpsks), 0,
314c560df6fSPatrick Kelsey     "Number of pre-shared keys installed");
315c560df6fSPatrick Kelsey 
3165f901c92SAndrew Turner VNET_DEFINE_STATIC(unsigned int, tcp_fastopen_path_disable_time) =
317c560df6fSPatrick Kelsey     TCP_FASTOPEN_PATH_DISABLE_TIME_DEFAULT;
318c560df6fSPatrick Kelsey #define	V_tcp_fastopen_path_disable_time VNET(tcp_fastopen_path_disable_time)
319c560df6fSPatrick Kelsey SYSCTL_UINT(_net_inet_tcp_fastopen, OID_AUTO, path_disable_time,
320c560df6fSPatrick Kelsey     CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(tcp_fastopen_path_disable_time), 0,
321c560df6fSPatrick Kelsey     "Seconds a TFO failure disables a {client_ip, server_ip, server_port} path");
322c560df6fSPatrick Kelsey 
3235f901c92SAndrew Turner VNET_DEFINE_STATIC(unsigned int, tcp_fastopen_psk_enable) = 0;
324c560df6fSPatrick Kelsey #define	V_tcp_fastopen_psk_enable	VNET(tcp_fastopen_psk_enable)
325c560df6fSPatrick Kelsey static int sysctl_net_inet_tcp_fastopen_psk_enable(SYSCTL_HANDLER_ARGS);
326c560df6fSPatrick Kelsey SYSCTL_PROC(_net_inet_tcp_fastopen, OID_AUTO, psk_enable,
3277029da5cSPawel Biernacki     CTLFLAG_VNET | CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_MPSAFE,
3287029da5cSPawel Biernacki     NULL, 0, &sysctl_net_inet_tcp_fastopen_psk_enable, "IU",
329c560df6fSPatrick Kelsey     "Enable/disable TCP Fast Open server pre-shared key mode");
330c560df6fSPatrick Kelsey 
331c560df6fSPatrick Kelsey VNET_DEFINE(unsigned int, tcp_fastopen_server_enable) = 0;
332c560df6fSPatrick Kelsey static int sysctl_net_inet_tcp_fastopen_server_enable(SYSCTL_HANDLER_ARGS);
333c560df6fSPatrick Kelsey SYSCTL_PROC(_net_inet_tcp_fastopen, OID_AUTO, server_enable,
3347029da5cSPawel Biernacki     CTLFLAG_VNET | CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_MPSAFE,
3357029da5cSPawel Biernacki     NULL, 0, &sysctl_net_inet_tcp_fastopen_server_enable, "IU",
336c560df6fSPatrick Kelsey     "Enable/disable TCP Fast Open server functionality");
337c560df6fSPatrick Kelsey 
338281a0fd4SPatrick Kelsey static int sysctl_net_inet_tcp_fastopen_setkey(SYSCTL_HANDLER_ARGS);
339281a0fd4SPatrick Kelsey SYSCTL_PROC(_net_inet_tcp_fastopen, OID_AUTO, setkey,
3407029da5cSPawel Biernacki     CTLFLAG_VNET | CTLTYPE_OPAQUE | CTLFLAG_WR | CTLFLAG_MPSAFE,
3417029da5cSPawel Biernacki     NULL, 0, &sysctl_net_inet_tcp_fastopen_setkey, "",
342281a0fd4SPatrick Kelsey     "Install a new key");
343281a0fd4SPatrick Kelsey 
344c560df6fSPatrick Kelsey static int sysctl_net_inet_tcp_fastopen_setpsk(SYSCTL_HANDLER_ARGS);
345c560df6fSPatrick Kelsey SYSCTL_PROC(_net_inet_tcp_fastopen, OID_AUTO, setpsk,
3467029da5cSPawel Biernacki     CTLFLAG_VNET | CTLTYPE_OPAQUE | CTLFLAG_WR | CTLFLAG_MPSAFE,
3477029da5cSPawel Biernacki     NULL, 0, &sysctl_net_inet_tcp_fastopen_setpsk, "",
348c560df6fSPatrick Kelsey     "Install a new pre-shared key");
349c560df6fSPatrick Kelsey 
350c9da5853SMichael Tuexen static int sysctl_net_inet_tcp_fastopen_ccache_list(SYSCTL_HANDLER_ARGS);
351c9da5853SMichael Tuexen SYSCTL_PROC(_net_inet_tcp_fastopen, OID_AUTO, ccache_list,
3527029da5cSPawel Biernacki     CTLFLAG_VNET | CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_SKIP | CTLFLAG_MPSAFE,
3537029da5cSPawel Biernacki     NULL, 0, sysctl_net_inet_tcp_fastopen_ccache_list, "A",
354c9da5853SMichael Tuexen     "List of all client cookie cache entries");
355c9da5853SMichael Tuexen 
3565f901c92SAndrew Turner VNET_DEFINE_STATIC(struct rmlock, tcp_fastopen_keylock);
357281a0fd4SPatrick Kelsey #define	V_tcp_fastopen_keylock	VNET(tcp_fastopen_keylock)
358281a0fd4SPatrick Kelsey 
359281a0fd4SPatrick Kelsey #define TCP_FASTOPEN_KEYS_RLOCK(t)	rm_rlock(&V_tcp_fastopen_keylock, (t))
360281a0fd4SPatrick Kelsey #define TCP_FASTOPEN_KEYS_RUNLOCK(t)	rm_runlock(&V_tcp_fastopen_keylock, (t))
361281a0fd4SPatrick Kelsey #define TCP_FASTOPEN_KEYS_WLOCK()	rm_wlock(&V_tcp_fastopen_keylock)
362281a0fd4SPatrick Kelsey #define TCP_FASTOPEN_KEYS_WUNLOCK()	rm_wunlock(&V_tcp_fastopen_keylock)
363281a0fd4SPatrick Kelsey 
3645f901c92SAndrew Turner VNET_DEFINE_STATIC(struct tcp_fastopen_keylist, tcp_fastopen_keys);
365281a0fd4SPatrick Kelsey #define V_tcp_fastopen_keys	VNET(tcp_fastopen_keys)
366281a0fd4SPatrick Kelsey 
3675f901c92SAndrew Turner VNET_DEFINE_STATIC(struct tcp_fastopen_callout, tcp_fastopen_autokey_ctx);
368281a0fd4SPatrick Kelsey #define V_tcp_fastopen_autokey_ctx	VNET(tcp_fastopen_autokey_ctx)
369281a0fd4SPatrick Kelsey 
3705f901c92SAndrew Turner VNET_DEFINE_STATIC(uma_zone_t, counter_zone);
371281a0fd4SPatrick Kelsey #define	V_counter_zone			VNET(counter_zone)
372281a0fd4SPatrick Kelsey 
373c560df6fSPatrick Kelsey static MALLOC_DEFINE(M_TCP_FASTOPEN_CCACHE, "tfo_ccache", "TFO client cookie cache buckets");
374c560df6fSPatrick Kelsey 
3755f901c92SAndrew Turner VNET_DEFINE_STATIC(struct tcp_fastopen_ccache, tcp_fastopen_ccache);
376c560df6fSPatrick Kelsey #define V_tcp_fastopen_ccache	VNET(tcp_fastopen_ccache)
377c560df6fSPatrick Kelsey 
378c560df6fSPatrick Kelsey #define	CCB_LOCK(ccb)		mtx_lock(&(ccb)->ccb_mtx)
379c560df6fSPatrick Kelsey #define	CCB_UNLOCK(ccb)		mtx_unlock(&(ccb)->ccb_mtx)
380c560df6fSPatrick Kelsey #define	CCB_LOCK_ASSERT(ccb)	mtx_assert(&(ccb)->ccb_mtx, MA_OWNED)
381c560df6fSPatrick Kelsey 
382281a0fd4SPatrick Kelsey void
tcp_fastopen_init(void)383281a0fd4SPatrick Kelsey tcp_fastopen_init(void)
384281a0fd4SPatrick Kelsey {
385c560df6fSPatrick Kelsey 	unsigned int i;
386c560df6fSPatrick Kelsey 
387281a0fd4SPatrick Kelsey 	V_counter_zone = uma_zcreate("tfo", sizeof(unsigned int),
388f254aedaSBjoern A. Zeeb 	    NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0);
389281a0fd4SPatrick Kelsey 	rm_init(&V_tcp_fastopen_keylock, "tfo_keylock");
390281a0fd4SPatrick Kelsey 	callout_init_rm(&V_tcp_fastopen_autokey_ctx.c,
391281a0fd4SPatrick Kelsey 	    &V_tcp_fastopen_keylock, 0);
392ec93ed8dSPatrick Kelsey 	V_tcp_fastopen_autokey_ctx.v = curvnet;
393281a0fd4SPatrick Kelsey 	V_tcp_fastopen_keys.newest = TCP_FASTOPEN_MAX_KEYS - 1;
394c560df6fSPatrick Kelsey 	V_tcp_fastopen_keys.newest_psk = TCP_FASTOPEN_MAX_PSKS - 1;
395c560df6fSPatrick Kelsey 
396224aec05SZhenlei Huang 	TUNABLE_INT_FETCH("net.inet.tcp.fastopen.ccache_bucket_limit",
397224aec05SZhenlei Huang 	    &V_tcp_fastopen_ccache.bucket_limit);
398c560df6fSPatrick Kelsey 	if (V_tcp_fastopen_ccache.bucket_limit == 0)
399c560df6fSPatrick Kelsey 		V_tcp_fastopen_ccache.bucket_limit =
400c560df6fSPatrick Kelsey 		    TCP_FASTOPEN_CCACHE_BUCKET_LIMIT_DEFAULT;
401c560df6fSPatrick Kelsey 
402c560df6fSPatrick Kelsey 	/* May already be non-zero if kernel tunable was set */
403c560df6fSPatrick Kelsey 	if ((V_tcp_fastopen_ccache_buckets == 0) ||
404c560df6fSPatrick Kelsey 	    !powerof2(V_tcp_fastopen_ccache_buckets))
405c560df6fSPatrick Kelsey 		V_tcp_fastopen_ccache.buckets =
406c560df6fSPatrick Kelsey 			TCP_FASTOPEN_CCACHE_BUCKETS_DEFAULT;
407c560df6fSPatrick Kelsey 	else
408c560df6fSPatrick Kelsey 		V_tcp_fastopen_ccache.buckets = V_tcp_fastopen_ccache_buckets;
409c560df6fSPatrick Kelsey 
410c560df6fSPatrick Kelsey 	V_tcp_fastopen_ccache.mask = V_tcp_fastopen_ccache.buckets - 1;
411c560df6fSPatrick Kelsey 	V_tcp_fastopen_ccache.secret = arc4random();
412c560df6fSPatrick Kelsey 
413c560df6fSPatrick Kelsey 	V_tcp_fastopen_ccache.base = malloc(V_tcp_fastopen_ccache.buckets *
414c560df6fSPatrick Kelsey 	    sizeof(struct tcp_fastopen_ccache_bucket), M_TCP_FASTOPEN_CCACHE,
415c560df6fSPatrick Kelsey 	    M_WAITOK | M_ZERO);
416c560df6fSPatrick Kelsey 
417c560df6fSPatrick Kelsey 	for (i = 0; i < V_tcp_fastopen_ccache.buckets; i++) {
418c560df6fSPatrick Kelsey 		TAILQ_INIT(&V_tcp_fastopen_ccache.base[i].ccb_entries);
419c560df6fSPatrick Kelsey 		mtx_init(&V_tcp_fastopen_ccache.base[i].ccb_mtx, "tfo_ccache_bucket",
420c560df6fSPatrick Kelsey 			 NULL, MTX_DEF);
421c556884fSMichael Tuexen 		if (V_tcp_fastopen_client_enable) {
422c556884fSMichael Tuexen 			/* enable bucket */
423c556884fSMichael Tuexen 			V_tcp_fastopen_ccache.base[i].ccb_num_entries = 0;
424c556884fSMichael Tuexen 		} else {
425c556884fSMichael Tuexen 			/* disable bucket */
426c556884fSMichael Tuexen 			V_tcp_fastopen_ccache.base[i].ccb_num_entries = -1;
427c556884fSMichael Tuexen 		}
428c560df6fSPatrick Kelsey 		V_tcp_fastopen_ccache.base[i].ccb_ccache = &V_tcp_fastopen_ccache;
429c560df6fSPatrick Kelsey 	}
430c560df6fSPatrick Kelsey 
431c560df6fSPatrick Kelsey 	/*
432c560df6fSPatrick Kelsey 	 * Note that while the total number of entries in the cookie cache
433c560df6fSPatrick Kelsey 	 * is limited by the table management logic to
434c560df6fSPatrick Kelsey 	 * V_tcp_fastopen_ccache.buckets *
435c560df6fSPatrick Kelsey 	 * V_tcp_fastopen_ccache.bucket_limit, the total number of items in
436c560df6fSPatrick Kelsey 	 * this zone can exceed that amount by the number of CPUs in the
437c560df6fSPatrick Kelsey 	 * system times the maximum number of unallocated items that can be
438c560df6fSPatrick Kelsey 	 * present in each UMA per-CPU cache for this zone.
439c560df6fSPatrick Kelsey 	 */
440c560df6fSPatrick Kelsey 	V_tcp_fastopen_ccache.zone = uma_zcreate("tfo_ccache_entries",
441c560df6fSPatrick Kelsey 	    sizeof(struct tcp_fastopen_ccache_entry), NULL, NULL, NULL, NULL,
442c560df6fSPatrick Kelsey 	    UMA_ALIGN_CACHE, 0);
443281a0fd4SPatrick Kelsey }
444281a0fd4SPatrick Kelsey 
445281a0fd4SPatrick Kelsey void
tcp_fastopen_destroy(void)446281a0fd4SPatrick Kelsey tcp_fastopen_destroy(void)
447281a0fd4SPatrick Kelsey {
448c560df6fSPatrick Kelsey 	struct tcp_fastopen_ccache_bucket *ccb;
449c560df6fSPatrick Kelsey 	unsigned int i;
450c560df6fSPatrick Kelsey 
451c560df6fSPatrick Kelsey 	for (i = 0; i < V_tcp_fastopen_ccache.buckets; i++) {
452c560df6fSPatrick Kelsey 		ccb = &V_tcp_fastopen_ccache.base[i];
453c560df6fSPatrick Kelsey 		tcp_fastopen_ccache_bucket_trim(ccb, 0);
454c560df6fSPatrick Kelsey 		mtx_destroy(&ccb->ccb_mtx);
455c560df6fSPatrick Kelsey 	}
456c560df6fSPatrick Kelsey 
457c560df6fSPatrick Kelsey 	KASSERT(uma_zone_get_cur(V_tcp_fastopen_ccache.zone) == 0,
458c560df6fSPatrick Kelsey 	    ("%s: TFO ccache zone allocation count not 0", __func__));
459c560df6fSPatrick Kelsey 	uma_zdestroy(V_tcp_fastopen_ccache.zone);
460c560df6fSPatrick Kelsey 	free(V_tcp_fastopen_ccache.base, M_TCP_FASTOPEN_CCACHE);
461c560df6fSPatrick Kelsey 
462281a0fd4SPatrick Kelsey 	callout_drain(&V_tcp_fastopen_autokey_ctx.c);
463281a0fd4SPatrick Kelsey 	rm_destroy(&V_tcp_fastopen_keylock);
464281a0fd4SPatrick Kelsey 	uma_zdestroy(V_counter_zone);
465281a0fd4SPatrick Kelsey }
466281a0fd4SPatrick Kelsey 
467281a0fd4SPatrick Kelsey unsigned int *
tcp_fastopen_alloc_counter(void)468281a0fd4SPatrick Kelsey tcp_fastopen_alloc_counter(void)
469281a0fd4SPatrick Kelsey {
470281a0fd4SPatrick Kelsey 	unsigned int *counter;
471281a0fd4SPatrick Kelsey 	counter = uma_zalloc(V_counter_zone, M_NOWAIT);
472281a0fd4SPatrick Kelsey 	if (counter)
473281a0fd4SPatrick Kelsey 		*counter = 1;
474281a0fd4SPatrick Kelsey 	return (counter);
475281a0fd4SPatrick Kelsey }
476281a0fd4SPatrick Kelsey 
477281a0fd4SPatrick Kelsey void
tcp_fastopen_decrement_counter(unsigned int * counter)478281a0fd4SPatrick Kelsey tcp_fastopen_decrement_counter(unsigned int *counter)
479281a0fd4SPatrick Kelsey {
480281a0fd4SPatrick Kelsey 	if (*counter == 1)
481281a0fd4SPatrick Kelsey 		uma_zfree(V_counter_zone, counter);
482281a0fd4SPatrick Kelsey 	else
483281a0fd4SPatrick Kelsey 		atomic_subtract_int(counter, 1);
484281a0fd4SPatrick Kelsey }
485281a0fd4SPatrick Kelsey 
486281a0fd4SPatrick Kelsey static void
tcp_fastopen_addkey_locked(uint8_t * key)487281a0fd4SPatrick Kelsey tcp_fastopen_addkey_locked(uint8_t *key)
488281a0fd4SPatrick Kelsey {
489281a0fd4SPatrick Kelsey 
490281a0fd4SPatrick Kelsey 	V_tcp_fastopen_keys.newest++;
491281a0fd4SPatrick Kelsey 	if (V_tcp_fastopen_keys.newest == TCP_FASTOPEN_MAX_KEYS)
492281a0fd4SPatrick Kelsey 		V_tcp_fastopen_keys.newest = 0;
493281a0fd4SPatrick Kelsey 	memcpy(V_tcp_fastopen_keys.key[V_tcp_fastopen_keys.newest], key,
494281a0fd4SPatrick Kelsey 	    TCP_FASTOPEN_KEY_LEN);
495281a0fd4SPatrick Kelsey 	if (V_tcp_fastopen_numkeys < TCP_FASTOPEN_MAX_KEYS)
496281a0fd4SPatrick Kelsey 		V_tcp_fastopen_numkeys++;
497281a0fd4SPatrick Kelsey }
498281a0fd4SPatrick Kelsey 
499281a0fd4SPatrick Kelsey static void
tcp_fastopen_addpsk_locked(uint8_t * psk)500c560df6fSPatrick Kelsey tcp_fastopen_addpsk_locked(uint8_t *psk)
501c560df6fSPatrick Kelsey {
502c560df6fSPatrick Kelsey 
503c560df6fSPatrick Kelsey 	V_tcp_fastopen_keys.newest_psk++;
504c560df6fSPatrick Kelsey 	if (V_tcp_fastopen_keys.newest_psk == TCP_FASTOPEN_MAX_PSKS)
505c560df6fSPatrick Kelsey 		V_tcp_fastopen_keys.newest_psk = 0;
506c560df6fSPatrick Kelsey 	memcpy(V_tcp_fastopen_keys.psk[V_tcp_fastopen_keys.newest_psk], psk,
507c560df6fSPatrick Kelsey 	    TCP_FASTOPEN_KEY_LEN);
508c560df6fSPatrick Kelsey 	if (V_tcp_fastopen_numpsks < TCP_FASTOPEN_MAX_PSKS)
509c560df6fSPatrick Kelsey 		V_tcp_fastopen_numpsks++;
510c560df6fSPatrick Kelsey }
511c560df6fSPatrick Kelsey 
512c560df6fSPatrick Kelsey static void
tcp_fastopen_autokey_locked(void)513281a0fd4SPatrick Kelsey tcp_fastopen_autokey_locked(void)
514281a0fd4SPatrick Kelsey {
515281a0fd4SPatrick Kelsey 	uint8_t newkey[TCP_FASTOPEN_KEY_LEN];
516281a0fd4SPatrick Kelsey 
517281a0fd4SPatrick Kelsey 	arc4rand(newkey, TCP_FASTOPEN_KEY_LEN, 0);
518281a0fd4SPatrick Kelsey 	tcp_fastopen_addkey_locked(newkey);
519281a0fd4SPatrick Kelsey }
520281a0fd4SPatrick Kelsey 
521281a0fd4SPatrick Kelsey static void
tcp_fastopen_autokey_callout(void * arg)522281a0fd4SPatrick Kelsey tcp_fastopen_autokey_callout(void *arg)
523281a0fd4SPatrick Kelsey {
524281a0fd4SPatrick Kelsey 	struct tcp_fastopen_callout *ctx = arg;
525281a0fd4SPatrick Kelsey 
526281a0fd4SPatrick Kelsey 	CURVNET_SET(ctx->v);
527281a0fd4SPatrick Kelsey 	tcp_fastopen_autokey_locked();
528281a0fd4SPatrick Kelsey 	callout_reset(&ctx->c, V_tcp_fastopen_autokey * hz,
529281a0fd4SPatrick Kelsey 		      tcp_fastopen_autokey_callout, ctx);
530281a0fd4SPatrick Kelsey 	CURVNET_RESTORE();
531281a0fd4SPatrick Kelsey }
532281a0fd4SPatrick Kelsey 
533281a0fd4SPatrick Kelsey static uint64_t
tcp_fastopen_make_cookie(uint8_t key[SIPHASH_KEY_LENGTH],struct in_conninfo * inc)534281a0fd4SPatrick Kelsey tcp_fastopen_make_cookie(uint8_t key[SIPHASH_KEY_LENGTH], struct in_conninfo *inc)
535281a0fd4SPatrick Kelsey {
536281a0fd4SPatrick Kelsey 	SIPHASH_CTX ctx;
537281a0fd4SPatrick Kelsey 	uint64_t siphash;
538281a0fd4SPatrick Kelsey 
539281a0fd4SPatrick Kelsey 	SipHash24_Init(&ctx);
540281a0fd4SPatrick Kelsey 	SipHash_SetKey(&ctx, key);
541281a0fd4SPatrick Kelsey 	switch (inc->inc_flags & INC_ISIPV6) {
542281a0fd4SPatrick Kelsey #ifdef INET
543281a0fd4SPatrick Kelsey 	case 0:
544281a0fd4SPatrick Kelsey 		SipHash_Update(&ctx, &inc->inc_faddr, sizeof(inc->inc_faddr));
545281a0fd4SPatrick Kelsey 		break;
546281a0fd4SPatrick Kelsey #endif
547281a0fd4SPatrick Kelsey #ifdef INET6
548281a0fd4SPatrick Kelsey 	case INC_ISIPV6:
549281a0fd4SPatrick Kelsey 		SipHash_Update(&ctx, &inc->inc6_faddr, sizeof(inc->inc6_faddr));
550281a0fd4SPatrick Kelsey 		break;
551281a0fd4SPatrick Kelsey #endif
552281a0fd4SPatrick Kelsey 	}
553281a0fd4SPatrick Kelsey 	SipHash_Final((u_int8_t *)&siphash, &ctx);
554281a0fd4SPatrick Kelsey 
555281a0fd4SPatrick Kelsey 	return (siphash);
556281a0fd4SPatrick Kelsey }
557281a0fd4SPatrick Kelsey 
558c560df6fSPatrick Kelsey static uint64_t
tcp_fastopen_make_psk_cookie(uint8_t * psk,uint8_t * cookie,uint8_t cookie_len)559c560df6fSPatrick Kelsey tcp_fastopen_make_psk_cookie(uint8_t *psk, uint8_t *cookie, uint8_t cookie_len)
560c560df6fSPatrick Kelsey {
561c560df6fSPatrick Kelsey 	SIPHASH_CTX ctx;
562c560df6fSPatrick Kelsey 	uint64_t psk_cookie;
563c560df6fSPatrick Kelsey 
564c560df6fSPatrick Kelsey 	SipHash24_Init(&ctx);
565c560df6fSPatrick Kelsey 	SipHash_SetKey(&ctx, psk);
566c560df6fSPatrick Kelsey 	SipHash_Update(&ctx, cookie, cookie_len);
567c560df6fSPatrick Kelsey 	SipHash_Final((u_int8_t *)&psk_cookie, &ctx);
568c560df6fSPatrick Kelsey 
569c560df6fSPatrick Kelsey 	return (psk_cookie);
570c560df6fSPatrick Kelsey }
571c560df6fSPatrick Kelsey 
572c560df6fSPatrick Kelsey static int
tcp_fastopen_find_cookie_match_locked(uint8_t * wire_cookie,uint64_t * cur_cookie)573c560df6fSPatrick Kelsey tcp_fastopen_find_cookie_match_locked(uint8_t *wire_cookie, uint64_t *cur_cookie)
574c560df6fSPatrick Kelsey {
575c560df6fSPatrick Kelsey 	unsigned int i, psk_index;
576c560df6fSPatrick Kelsey 	uint64_t psk_cookie;
577c560df6fSPatrick Kelsey 
578c560df6fSPatrick Kelsey 	if (V_tcp_fastopen_psk_enable) {
579c560df6fSPatrick Kelsey 		psk_index = V_tcp_fastopen_keys.newest_psk;
580c560df6fSPatrick Kelsey 		for (i = 0; i < V_tcp_fastopen_numpsks; i++) {
581c560df6fSPatrick Kelsey 			psk_cookie =
582c560df6fSPatrick Kelsey 			    tcp_fastopen_make_psk_cookie(
583c560df6fSPatrick Kelsey 				 V_tcp_fastopen_keys.psk[psk_index],
584c560df6fSPatrick Kelsey 				 (uint8_t *)cur_cookie,
585c560df6fSPatrick Kelsey 				 TCP_FASTOPEN_COOKIE_LEN);
586c560df6fSPatrick Kelsey 
587c560df6fSPatrick Kelsey 			if (memcmp(wire_cookie, &psk_cookie,
588c560df6fSPatrick Kelsey 				   TCP_FASTOPEN_COOKIE_LEN) == 0)
589c560df6fSPatrick Kelsey 				return (1);
590c560df6fSPatrick Kelsey 
591c560df6fSPatrick Kelsey 			if (psk_index == 0)
592c560df6fSPatrick Kelsey 				psk_index = TCP_FASTOPEN_MAX_PSKS - 1;
593c560df6fSPatrick Kelsey 			else
594c560df6fSPatrick Kelsey 				psk_index--;
595c560df6fSPatrick Kelsey 		}
596c560df6fSPatrick Kelsey 	} else if (memcmp(wire_cookie, cur_cookie, TCP_FASTOPEN_COOKIE_LEN) == 0)
597c560df6fSPatrick Kelsey 		return (1);
598c560df6fSPatrick Kelsey 
599c560df6fSPatrick Kelsey 	return (0);
600c560df6fSPatrick Kelsey }
601281a0fd4SPatrick Kelsey 
602281a0fd4SPatrick Kelsey /*
603281a0fd4SPatrick Kelsey  * Return values:
604281a0fd4SPatrick Kelsey  *	-1	the cookie is invalid and no valid cookie is available
605281a0fd4SPatrick Kelsey  *	 0	the cookie is invalid and the latest cookie has been returned
606281a0fd4SPatrick Kelsey  *	 1	the cookie is valid and the latest cookie has been returned
607281a0fd4SPatrick Kelsey  */
608281a0fd4SPatrick Kelsey int
tcp_fastopen_check_cookie(struct in_conninfo * inc,uint8_t * cookie,unsigned int len,uint64_t * latest_cookie)609281a0fd4SPatrick Kelsey tcp_fastopen_check_cookie(struct in_conninfo *inc, uint8_t *cookie,
610281a0fd4SPatrick Kelsey     unsigned int len, uint64_t *latest_cookie)
611281a0fd4SPatrick Kelsey {
612281a0fd4SPatrick Kelsey 	struct rm_priotracker tracker;
613281a0fd4SPatrick Kelsey 	unsigned int i, key_index;
614798caa2eSPatrick Kelsey 	int rv;
615281a0fd4SPatrick Kelsey 	uint64_t cur_cookie;
616281a0fd4SPatrick Kelsey 
617281a0fd4SPatrick Kelsey 	if (V_tcp_fastopen_acceptany) {
618281a0fd4SPatrick Kelsey 		*latest_cookie = 0;
619281a0fd4SPatrick Kelsey 		return (1);
620281a0fd4SPatrick Kelsey 	}
621281a0fd4SPatrick Kelsey 
622798caa2eSPatrick Kelsey 	TCP_FASTOPEN_KEYS_RLOCK(&tracker);
623281a0fd4SPatrick Kelsey 	if (len != TCP_FASTOPEN_COOKIE_LEN) {
624281a0fd4SPatrick Kelsey 		if (V_tcp_fastopen_numkeys > 0) {
625281a0fd4SPatrick Kelsey 			*latest_cookie =
626281a0fd4SPatrick Kelsey 			    tcp_fastopen_make_cookie(
627281a0fd4SPatrick Kelsey 				V_tcp_fastopen_keys.key[V_tcp_fastopen_keys.newest],
628281a0fd4SPatrick Kelsey 				inc);
629798caa2eSPatrick Kelsey 			rv = 0;
630798caa2eSPatrick Kelsey 		} else
631798caa2eSPatrick Kelsey 			rv = -1;
632798caa2eSPatrick Kelsey 		goto out;
633281a0fd4SPatrick Kelsey 	}
634281a0fd4SPatrick Kelsey 
635281a0fd4SPatrick Kelsey 	/*
636281a0fd4SPatrick Kelsey 	 * Check against each available key, from newest to oldest.
637281a0fd4SPatrick Kelsey 	 */
638281a0fd4SPatrick Kelsey 	key_index = V_tcp_fastopen_keys.newest;
639281a0fd4SPatrick Kelsey 	for (i = 0; i < V_tcp_fastopen_numkeys; i++) {
640281a0fd4SPatrick Kelsey 		cur_cookie =
641281a0fd4SPatrick Kelsey 		    tcp_fastopen_make_cookie(V_tcp_fastopen_keys.key[key_index],
642281a0fd4SPatrick Kelsey 			inc);
643281a0fd4SPatrick Kelsey 		if (i == 0)
644281a0fd4SPatrick Kelsey 			*latest_cookie = cur_cookie;
645c560df6fSPatrick Kelsey 		rv = tcp_fastopen_find_cookie_match_locked(cookie, &cur_cookie);
646c560df6fSPatrick Kelsey 		if (rv)
647798caa2eSPatrick Kelsey 			goto out;
648281a0fd4SPatrick Kelsey 		if (key_index == 0)
649281a0fd4SPatrick Kelsey 			key_index = TCP_FASTOPEN_MAX_KEYS - 1;
650281a0fd4SPatrick Kelsey 		else
651281a0fd4SPatrick Kelsey 			key_index--;
652281a0fd4SPatrick Kelsey 	}
653798caa2eSPatrick Kelsey 	rv = 0;
654281a0fd4SPatrick Kelsey 
655798caa2eSPatrick Kelsey  out:
656798caa2eSPatrick Kelsey 	TCP_FASTOPEN_KEYS_RUNLOCK(&tracker);
657798caa2eSPatrick Kelsey 	return (rv);
658281a0fd4SPatrick Kelsey }
659281a0fd4SPatrick Kelsey 
660281a0fd4SPatrick Kelsey static int
sysctl_net_inet_tcp_fastopen_autokey(SYSCTL_HANDLER_ARGS)661281a0fd4SPatrick Kelsey sysctl_net_inet_tcp_fastopen_autokey(SYSCTL_HANDLER_ARGS)
662281a0fd4SPatrick Kelsey {
663281a0fd4SPatrick Kelsey 	int error;
664281a0fd4SPatrick Kelsey 	unsigned int new;
665281a0fd4SPatrick Kelsey 
666281a0fd4SPatrick Kelsey 	new = V_tcp_fastopen_autokey;
667281a0fd4SPatrick Kelsey 	error = sysctl_handle_int(oidp, &new, 0, req);
668281a0fd4SPatrick Kelsey 	if (error == 0 && req->newptr) {
669281a0fd4SPatrick Kelsey 		if (new > (INT_MAX / hz))
670281a0fd4SPatrick Kelsey 			return (EINVAL);
671281a0fd4SPatrick Kelsey 
672281a0fd4SPatrick Kelsey 		TCP_FASTOPEN_KEYS_WLOCK();
673c560df6fSPatrick Kelsey 		if (V_tcp_fastopen_server_enable) {
674281a0fd4SPatrick Kelsey 			if (V_tcp_fastopen_autokey && !new)
675281a0fd4SPatrick Kelsey 				callout_stop(&V_tcp_fastopen_autokey_ctx.c);
676281a0fd4SPatrick Kelsey 			else if (new)
677281a0fd4SPatrick Kelsey 				callout_reset(&V_tcp_fastopen_autokey_ctx.c,
678281a0fd4SPatrick Kelsey 				    new * hz, tcp_fastopen_autokey_callout,
679281a0fd4SPatrick Kelsey 				    &V_tcp_fastopen_autokey_ctx);
680281a0fd4SPatrick Kelsey 		}
681281a0fd4SPatrick Kelsey 		V_tcp_fastopen_autokey = new;
682281a0fd4SPatrick Kelsey 		TCP_FASTOPEN_KEYS_WUNLOCK();
683281a0fd4SPatrick Kelsey 	}
684281a0fd4SPatrick Kelsey 
685281a0fd4SPatrick Kelsey 	return (error);
686281a0fd4SPatrick Kelsey }
687281a0fd4SPatrick Kelsey 
688281a0fd4SPatrick Kelsey static int
sysctl_net_inet_tcp_fastopen_psk_enable(SYSCTL_HANDLER_ARGS)689c560df6fSPatrick Kelsey sysctl_net_inet_tcp_fastopen_psk_enable(SYSCTL_HANDLER_ARGS)
690281a0fd4SPatrick Kelsey {
691281a0fd4SPatrick Kelsey 	int error;
692281a0fd4SPatrick Kelsey 	unsigned int new;
693281a0fd4SPatrick Kelsey 
694c560df6fSPatrick Kelsey 	new = V_tcp_fastopen_psk_enable;
695281a0fd4SPatrick Kelsey 	error = sysctl_handle_int(oidp, &new, 0, req);
696281a0fd4SPatrick Kelsey 	if (error == 0 && req->newptr) {
697c560df6fSPatrick Kelsey 		if (V_tcp_fastopen_psk_enable && !new) {
698c560df6fSPatrick Kelsey 			/* enabled -> disabled */
699c560df6fSPatrick Kelsey 			TCP_FASTOPEN_KEYS_WLOCK();
700c560df6fSPatrick Kelsey 			V_tcp_fastopen_numpsks = 0;
701c560df6fSPatrick Kelsey 			V_tcp_fastopen_keys.newest_psk =
702c560df6fSPatrick Kelsey 			    TCP_FASTOPEN_MAX_PSKS - 1;
703c560df6fSPatrick Kelsey 			V_tcp_fastopen_psk_enable = 0;
704c560df6fSPatrick Kelsey 			TCP_FASTOPEN_KEYS_WUNLOCK();
705c560df6fSPatrick Kelsey 		} else if (!V_tcp_fastopen_psk_enable && new) {
706c560df6fSPatrick Kelsey 			/* disabled -> enabled */
707c560df6fSPatrick Kelsey 			TCP_FASTOPEN_KEYS_WLOCK();
708c560df6fSPatrick Kelsey 			V_tcp_fastopen_psk_enable = 1;
709c560df6fSPatrick Kelsey 			TCP_FASTOPEN_KEYS_WUNLOCK();
710c560df6fSPatrick Kelsey 		}
711c560df6fSPatrick Kelsey 	}
712c560df6fSPatrick Kelsey 	return (error);
713c560df6fSPatrick Kelsey }
714c560df6fSPatrick Kelsey 
715c560df6fSPatrick Kelsey static int
sysctl_net_inet_tcp_fastopen_server_enable(SYSCTL_HANDLER_ARGS)716c560df6fSPatrick Kelsey sysctl_net_inet_tcp_fastopen_server_enable(SYSCTL_HANDLER_ARGS)
717c560df6fSPatrick Kelsey {
718c560df6fSPatrick Kelsey 	int error;
719c560df6fSPatrick Kelsey 	unsigned int new;
720c560df6fSPatrick Kelsey 
721c560df6fSPatrick Kelsey 	new = V_tcp_fastopen_server_enable;
722c560df6fSPatrick Kelsey 	error = sysctl_handle_int(oidp, &new, 0, req);
723c560df6fSPatrick Kelsey 	if (error == 0 && req->newptr) {
724c560df6fSPatrick Kelsey 		if (V_tcp_fastopen_server_enable && !new) {
725281a0fd4SPatrick Kelsey 			/* enabled -> disabled */
726281a0fd4SPatrick Kelsey 			TCP_FASTOPEN_KEYS_WLOCK();
727281a0fd4SPatrick Kelsey 			V_tcp_fastopen_numkeys = 0;
728281a0fd4SPatrick Kelsey 			V_tcp_fastopen_keys.newest = TCP_FASTOPEN_MAX_KEYS - 1;
729281a0fd4SPatrick Kelsey 			if (V_tcp_fastopen_autokey)
730281a0fd4SPatrick Kelsey 				callout_stop(&V_tcp_fastopen_autokey_ctx.c);
731c560df6fSPatrick Kelsey 			V_tcp_fastopen_numpsks = 0;
732c560df6fSPatrick Kelsey 			V_tcp_fastopen_keys.newest_psk =
733c560df6fSPatrick Kelsey 			    TCP_FASTOPEN_MAX_PSKS - 1;
734c560df6fSPatrick Kelsey 			V_tcp_fastopen_server_enable = 0;
735281a0fd4SPatrick Kelsey 			TCP_FASTOPEN_KEYS_WUNLOCK();
736c560df6fSPatrick Kelsey 		} else if (!V_tcp_fastopen_server_enable && new) {
737281a0fd4SPatrick Kelsey 			/* disabled -> enabled */
738281a0fd4SPatrick Kelsey 			TCP_FASTOPEN_KEYS_WLOCK();
739281a0fd4SPatrick Kelsey 			if (V_tcp_fastopen_autokey &&
740281a0fd4SPatrick Kelsey 			    (V_tcp_fastopen_numkeys == 0)) {
741281a0fd4SPatrick Kelsey 				tcp_fastopen_autokey_locked();
742281a0fd4SPatrick Kelsey 				callout_reset(&V_tcp_fastopen_autokey_ctx.c,
743281a0fd4SPatrick Kelsey 				    V_tcp_fastopen_autokey * hz,
744281a0fd4SPatrick Kelsey 				    tcp_fastopen_autokey_callout,
745281a0fd4SPatrick Kelsey 				    &V_tcp_fastopen_autokey_ctx);
746281a0fd4SPatrick Kelsey 			}
747c560df6fSPatrick Kelsey 			V_tcp_fastopen_server_enable = 1;
748281a0fd4SPatrick Kelsey 			TCP_FASTOPEN_KEYS_WUNLOCK();
749281a0fd4SPatrick Kelsey 		}
750281a0fd4SPatrick Kelsey 	}
751281a0fd4SPatrick Kelsey 	return (error);
752281a0fd4SPatrick Kelsey }
753281a0fd4SPatrick Kelsey 
754281a0fd4SPatrick Kelsey static int
sysctl_net_inet_tcp_fastopen_setkey(SYSCTL_HANDLER_ARGS)755281a0fd4SPatrick Kelsey sysctl_net_inet_tcp_fastopen_setkey(SYSCTL_HANDLER_ARGS)
756281a0fd4SPatrick Kelsey {
757281a0fd4SPatrick Kelsey 	int error;
758281a0fd4SPatrick Kelsey 	uint8_t newkey[TCP_FASTOPEN_KEY_LEN];
759281a0fd4SPatrick Kelsey 
760281a0fd4SPatrick Kelsey 	if (req->oldptr != NULL || req->oldlen != 0)
761281a0fd4SPatrick Kelsey 		return (EINVAL);
762281a0fd4SPatrick Kelsey 	if (req->newptr == NULL)
763281a0fd4SPatrick Kelsey 		return (EPERM);
764281a0fd4SPatrick Kelsey 	if (req->newlen != sizeof(newkey))
765281a0fd4SPatrick Kelsey 		return (EINVAL);
766281a0fd4SPatrick Kelsey 	error = SYSCTL_IN(req, newkey, sizeof(newkey));
767281a0fd4SPatrick Kelsey 	if (error)
768281a0fd4SPatrick Kelsey 		return (error);
769281a0fd4SPatrick Kelsey 
770281a0fd4SPatrick Kelsey 	TCP_FASTOPEN_KEYS_WLOCK();
771281a0fd4SPatrick Kelsey 	tcp_fastopen_addkey_locked(newkey);
772281a0fd4SPatrick Kelsey 	TCP_FASTOPEN_KEYS_WUNLOCK();
773281a0fd4SPatrick Kelsey 
774281a0fd4SPatrick Kelsey 	return (0);
775281a0fd4SPatrick Kelsey }
776c560df6fSPatrick Kelsey 
777c560df6fSPatrick Kelsey static int
sysctl_net_inet_tcp_fastopen_setpsk(SYSCTL_HANDLER_ARGS)778c560df6fSPatrick Kelsey sysctl_net_inet_tcp_fastopen_setpsk(SYSCTL_HANDLER_ARGS)
779c560df6fSPatrick Kelsey {
780c560df6fSPatrick Kelsey 	int error;
781c560df6fSPatrick Kelsey 	uint8_t newpsk[TCP_FASTOPEN_KEY_LEN];
782c560df6fSPatrick Kelsey 
783c560df6fSPatrick Kelsey 	if (req->oldptr != NULL || req->oldlen != 0)
784c560df6fSPatrick Kelsey 		return (EINVAL);
785c560df6fSPatrick Kelsey 	if (req->newptr == NULL)
786c560df6fSPatrick Kelsey 		return (EPERM);
787c560df6fSPatrick Kelsey 	if (req->newlen != sizeof(newpsk))
788c560df6fSPatrick Kelsey 		return (EINVAL);
789c560df6fSPatrick Kelsey 	error = SYSCTL_IN(req, newpsk, sizeof(newpsk));
790c560df6fSPatrick Kelsey 	if (error)
791c560df6fSPatrick Kelsey 		return (error);
792c560df6fSPatrick Kelsey 
793c560df6fSPatrick Kelsey 	TCP_FASTOPEN_KEYS_WLOCK();
794c560df6fSPatrick Kelsey 	tcp_fastopen_addpsk_locked(newpsk);
795c560df6fSPatrick Kelsey 	TCP_FASTOPEN_KEYS_WUNLOCK();
796c560df6fSPatrick Kelsey 
797c560df6fSPatrick Kelsey 	return (0);
798c560df6fSPatrick Kelsey }
799c560df6fSPatrick Kelsey 
800c560df6fSPatrick Kelsey static int
sysctl_net_inet_tcp_fastopen_ccache_bucket_limit(SYSCTL_HANDLER_ARGS)801c560df6fSPatrick Kelsey sysctl_net_inet_tcp_fastopen_ccache_bucket_limit(SYSCTL_HANDLER_ARGS)
802c560df6fSPatrick Kelsey {
803c560df6fSPatrick Kelsey 	struct tcp_fastopen_ccache_bucket *ccb;
804c560df6fSPatrick Kelsey 	int error;
805c560df6fSPatrick Kelsey 	unsigned int new;
806c560df6fSPatrick Kelsey 	unsigned int i;
807c560df6fSPatrick Kelsey 
808c560df6fSPatrick Kelsey 	new = V_tcp_fastopen_ccache.bucket_limit;
809c560df6fSPatrick Kelsey 	error = sysctl_handle_int(oidp, &new, 0, req);
810c560df6fSPatrick Kelsey 	if (error == 0 && req->newptr) {
811c560df6fSPatrick Kelsey 		if ((new == 0) || (new > INT_MAX))
812c560df6fSPatrick Kelsey 			error = EINVAL;
813c560df6fSPatrick Kelsey 		else {
814c560df6fSPatrick Kelsey 			if (new < V_tcp_fastopen_ccache.bucket_limit) {
815c560df6fSPatrick Kelsey 				for (i = 0; i < V_tcp_fastopen_ccache.buckets;
816c560df6fSPatrick Kelsey 				     i++) {
817c560df6fSPatrick Kelsey 					ccb = &V_tcp_fastopen_ccache.base[i];
818c560df6fSPatrick Kelsey 					tcp_fastopen_ccache_bucket_trim(ccb, new);
819c560df6fSPatrick Kelsey 				}
820c560df6fSPatrick Kelsey 			}
821c560df6fSPatrick Kelsey 			V_tcp_fastopen_ccache.bucket_limit = new;
822c560df6fSPatrick Kelsey 		}
823c560df6fSPatrick Kelsey 	}
824c560df6fSPatrick Kelsey 	return (error);
825c560df6fSPatrick Kelsey }
826c560df6fSPatrick Kelsey 
827c560df6fSPatrick Kelsey static int
sysctl_net_inet_tcp_fastopen_client_enable(SYSCTL_HANDLER_ARGS)828c560df6fSPatrick Kelsey sysctl_net_inet_tcp_fastopen_client_enable(SYSCTL_HANDLER_ARGS)
829c560df6fSPatrick Kelsey {
830c560df6fSPatrick Kelsey 	struct tcp_fastopen_ccache_bucket *ccb;
831c560df6fSPatrick Kelsey 	int error;
832c560df6fSPatrick Kelsey 	unsigned int new, i;
833c560df6fSPatrick Kelsey 
834c560df6fSPatrick Kelsey 	new = V_tcp_fastopen_client_enable;
835c560df6fSPatrick Kelsey 	error = sysctl_handle_int(oidp, &new, 0, req);
836c560df6fSPatrick Kelsey 	if (error == 0 && req->newptr) {
837c560df6fSPatrick Kelsey 		if (V_tcp_fastopen_client_enable && !new) {
838c560df6fSPatrick Kelsey 			/* enabled -> disabled */
839c560df6fSPatrick Kelsey 			for (i = 0; i < V_tcp_fastopen_ccache.buckets; i++) {
840c560df6fSPatrick Kelsey 				ccb = &V_tcp_fastopen_ccache.base[i];
841c556884fSMichael Tuexen 				KASSERT(ccb->ccb_num_entries > -1,
842c556884fSMichael Tuexen 				    ("%s: ccb->ccb_num_entries %d is negative",
843c556884fSMichael Tuexen 					__func__, ccb->ccb_num_entries));
844c560df6fSPatrick Kelsey 				tcp_fastopen_ccache_bucket_trim(ccb, 0);
845c560df6fSPatrick Kelsey 			}
846c560df6fSPatrick Kelsey 			V_tcp_fastopen_client_enable = 0;
847c560df6fSPatrick Kelsey 		} else if (!V_tcp_fastopen_client_enable && new) {
848c560df6fSPatrick Kelsey 			/* disabled -> enabled */
849c560df6fSPatrick Kelsey 			for (i = 0; i < V_tcp_fastopen_ccache.buckets; i++) {
850c560df6fSPatrick Kelsey 				ccb = &V_tcp_fastopen_ccache.base[i];
851c560df6fSPatrick Kelsey 				CCB_LOCK(ccb);
852c560df6fSPatrick Kelsey 				KASSERT(TAILQ_EMPTY(&ccb->ccb_entries),
853c560df6fSPatrick Kelsey 				    ("%s: ccb->ccb_entries not empty", __func__));
854c560df6fSPatrick Kelsey 				KASSERT(ccb->ccb_num_entries == -1,
855c560df6fSPatrick Kelsey 				    ("%s: ccb->ccb_num_entries %d not -1", __func__,
856c560df6fSPatrick Kelsey 					ccb->ccb_num_entries));
857c560df6fSPatrick Kelsey 				ccb->ccb_num_entries = 0; /* enable bucket */
858c560df6fSPatrick Kelsey 				CCB_UNLOCK(ccb);
859c560df6fSPatrick Kelsey 			}
860c560df6fSPatrick Kelsey 			V_tcp_fastopen_client_enable = 1;
861c560df6fSPatrick Kelsey 		}
862c560df6fSPatrick Kelsey 	}
863c560df6fSPatrick Kelsey 	return (error);
864c560df6fSPatrick Kelsey }
865c560df6fSPatrick Kelsey 
866c560df6fSPatrick Kelsey void
tcp_fastopen_connect(struct tcpcb * tp)867c560df6fSPatrick Kelsey tcp_fastopen_connect(struct tcpcb *tp)
868c560df6fSPatrick Kelsey {
8699eb0e832SGleb Smirnoff 	struct inpcb *inp = tptoinpcb(tp);
870c560df6fSPatrick Kelsey 	struct tcp_fastopen_ccache_bucket *ccb;
871c560df6fSPatrick Kelsey 	struct tcp_fastopen_ccache_entry *cce;
872c560df6fSPatrick Kelsey 	sbintime_t now;
873c560df6fSPatrick Kelsey 	uint16_t server_mss;
874c560df6fSPatrick Kelsey 	uint64_t psk_cookie;
875c560df6fSPatrick Kelsey 
87621f6fe2dSMatt Macy 	psk_cookie = 0;
877c560df6fSPatrick Kelsey 	cce = tcp_fastopen_ccache_lookup(&inp->inp_inc, &ccb);
878c560df6fSPatrick Kelsey 	if (cce) {
879c560df6fSPatrick Kelsey 		if (cce->disable_time == 0) {
880c560df6fSPatrick Kelsey 			if ((cce->cookie_len > 0) &&
881c560df6fSPatrick Kelsey 			    (tp->t_tfo_client_cookie_len ==
882c560df6fSPatrick Kelsey 			     TCP_FASTOPEN_PSK_LEN)) {
883c560df6fSPatrick Kelsey 				psk_cookie =
884c560df6fSPatrick Kelsey 				    tcp_fastopen_make_psk_cookie(
885c560df6fSPatrick Kelsey 					tp->t_tfo_cookie.client,
886c560df6fSPatrick Kelsey 					cce->cookie, cce->cookie_len);
887c560df6fSPatrick Kelsey 			} else {
888c560df6fSPatrick Kelsey 				tp->t_tfo_client_cookie_len = cce->cookie_len;
889c560df6fSPatrick Kelsey 				memcpy(tp->t_tfo_cookie.client, cce->cookie,
890c560df6fSPatrick Kelsey 				    cce->cookie_len);
891c560df6fSPatrick Kelsey 			}
892c560df6fSPatrick Kelsey 			server_mss = cce->server_mss;
893c560df6fSPatrick Kelsey 			CCB_UNLOCK(ccb);
894c560df6fSPatrick Kelsey 			if (tp->t_tfo_client_cookie_len ==
89521f6fe2dSMatt Macy 			    TCP_FASTOPEN_PSK_LEN && psk_cookie) {
896c560df6fSPatrick Kelsey 				tp->t_tfo_client_cookie_len =
897c560df6fSPatrick Kelsey 				    TCP_FASTOPEN_COOKIE_LEN;
898c560df6fSPatrick Kelsey 				memcpy(tp->t_tfo_cookie.client, &psk_cookie,
899c560df6fSPatrick Kelsey 				    TCP_FASTOPEN_COOKIE_LEN);
900c560df6fSPatrick Kelsey 			}
901c560df6fSPatrick Kelsey 			tcp_mss(tp, server_mss ? server_mss : -1);
902c560df6fSPatrick Kelsey 			tp->snd_wnd = tp->t_maxseg;
903c560df6fSPatrick Kelsey 		} else {
904c560df6fSPatrick Kelsey 			/*
905c560df6fSPatrick Kelsey 			 * The path is disabled.  Check the time and
906c560df6fSPatrick Kelsey 			 * possibly re-enable.
907c560df6fSPatrick Kelsey 			 */
908c560df6fSPatrick Kelsey 			now = getsbinuptime();
909c560df6fSPatrick Kelsey 			if (now - cce->disable_time >
910c560df6fSPatrick Kelsey 			    ((sbintime_t)V_tcp_fastopen_path_disable_time << 32)) {
911c560df6fSPatrick Kelsey 				/*
912c560df6fSPatrick Kelsey 				 * Re-enable path.  Force a TFO cookie
913c560df6fSPatrick Kelsey 				 * request.  Forget the old MSS as it may be
914c560df6fSPatrick Kelsey 				 * bogus now, and we will rediscover it in
915c560df6fSPatrick Kelsey 				 * the SYN|ACK.
916c560df6fSPatrick Kelsey 				 */
917c560df6fSPatrick Kelsey 				cce->disable_time = 0;
918c560df6fSPatrick Kelsey 				cce->server_mss = 0;
919c560df6fSPatrick Kelsey 				cce->cookie_len = 0;
920c560df6fSPatrick Kelsey 				/*
921c560df6fSPatrick Kelsey 				 * tp->t_tfo... cookie details are already
922c560df6fSPatrick Kelsey 				 * zero from the tcpcb init.
923c560df6fSPatrick Kelsey 				 */
924c560df6fSPatrick Kelsey 			} else {
925c560df6fSPatrick Kelsey 				/*
926c560df6fSPatrick Kelsey 				 * Path is disabled, so disable TFO on this
927c560df6fSPatrick Kelsey 				 * connection.
928c560df6fSPatrick Kelsey 				 */
929c560df6fSPatrick Kelsey 				tp->t_flags &= ~TF_FASTOPEN;
930c560df6fSPatrick Kelsey 			}
931c560df6fSPatrick Kelsey 			CCB_UNLOCK(ccb);
932c560df6fSPatrick Kelsey 			tcp_mss(tp, -1);
933c560df6fSPatrick Kelsey 			/*
934c560df6fSPatrick Kelsey 			 * snd_wnd is irrelevant since we are either forcing
935c560df6fSPatrick Kelsey 			 * a TFO cookie request or disabling TFO - either
936c560df6fSPatrick Kelsey 			 * way, no data with the SYN.
937c560df6fSPatrick Kelsey 			 */
938c560df6fSPatrick Kelsey 		}
939c560df6fSPatrick Kelsey 	} else {
940c560df6fSPatrick Kelsey 		/*
941c560df6fSPatrick Kelsey 		 * A new entry for this path will be created when a SYN|ACK
942c560df6fSPatrick Kelsey 		 * comes back, or the attempt otherwise fails.
943c560df6fSPatrick Kelsey 		 */
944c560df6fSPatrick Kelsey 		CCB_UNLOCK(ccb);
945c560df6fSPatrick Kelsey 		tcp_mss(tp, -1);
946c560df6fSPatrick Kelsey 		/*
947c560df6fSPatrick Kelsey 		 * snd_wnd is irrelevant since we are forcing a TFO cookie
948c560df6fSPatrick Kelsey 		 * request.
949c560df6fSPatrick Kelsey 		 */
950c560df6fSPatrick Kelsey 	}
951c560df6fSPatrick Kelsey }
952c560df6fSPatrick Kelsey 
953c560df6fSPatrick Kelsey void
tcp_fastopen_disable_path(struct tcpcb * tp)954c560df6fSPatrick Kelsey tcp_fastopen_disable_path(struct tcpcb *tp)
955c560df6fSPatrick Kelsey {
9569eb0e832SGleb Smirnoff 	struct in_conninfo *inc = &tptoinpcb(tp)->inp_inc;
957c560df6fSPatrick Kelsey 	struct tcp_fastopen_ccache_bucket *ccb;
958c560df6fSPatrick Kelsey 	struct tcp_fastopen_ccache_entry *cce;
959c560df6fSPatrick Kelsey 
960c560df6fSPatrick Kelsey 	cce = tcp_fastopen_ccache_lookup(inc, &ccb);
961c560df6fSPatrick Kelsey 	if (cce) {
962c560df6fSPatrick Kelsey 		cce->server_mss = 0;
963c560df6fSPatrick Kelsey 		cce->cookie_len = 0;
964c560df6fSPatrick Kelsey 		/*
965c560df6fSPatrick Kelsey 		 * Preserve the existing disable time if it is already
966c560df6fSPatrick Kelsey 		 * disabled.
967c560df6fSPatrick Kelsey 		 */
968c560df6fSPatrick Kelsey 		if (cce->disable_time == 0)
969c560df6fSPatrick Kelsey 			cce->disable_time = getsbinuptime();
970c560df6fSPatrick Kelsey 	} else /* use invalid cookie len to create disabled entry */
971c560df6fSPatrick Kelsey 		tcp_fastopen_ccache_create(ccb, inc, 0,
972c560df6fSPatrick Kelsey 	   	    TCP_FASTOPEN_MAX_COOKIE_LEN + 1, NULL);
973c560df6fSPatrick Kelsey 
974c560df6fSPatrick Kelsey 	CCB_UNLOCK(ccb);
975c560df6fSPatrick Kelsey 	tp->t_flags &= ~TF_FASTOPEN;
976c560df6fSPatrick Kelsey }
977c560df6fSPatrick Kelsey 
978c560df6fSPatrick Kelsey void
tcp_fastopen_update_cache(struct tcpcb * tp,uint16_t mss,uint8_t cookie_len,uint8_t * cookie)979c560df6fSPatrick Kelsey tcp_fastopen_update_cache(struct tcpcb *tp, uint16_t mss,
980c560df6fSPatrick Kelsey     uint8_t cookie_len, uint8_t *cookie)
981c560df6fSPatrick Kelsey {
9829eb0e832SGleb Smirnoff 	struct in_conninfo *inc = &tptoinpcb(tp)->inp_inc;
983c560df6fSPatrick Kelsey 	struct tcp_fastopen_ccache_bucket *ccb;
984c560df6fSPatrick Kelsey 	struct tcp_fastopen_ccache_entry *cce;
985c560df6fSPatrick Kelsey 
986c560df6fSPatrick Kelsey 	cce = tcp_fastopen_ccache_lookup(inc, &ccb);
987c560df6fSPatrick Kelsey 	if (cce) {
988c560df6fSPatrick Kelsey 		if ((cookie_len >= TCP_FASTOPEN_MIN_COOKIE_LEN) &&
989c560df6fSPatrick Kelsey 		    (cookie_len <= TCP_FASTOPEN_MAX_COOKIE_LEN) &&
990c560df6fSPatrick Kelsey 		    ((cookie_len & 0x1) == 0)) {
991c560df6fSPatrick Kelsey 			cce->server_mss = mss;
992c560df6fSPatrick Kelsey 			cce->cookie_len = cookie_len;
993c560df6fSPatrick Kelsey 			memcpy(cce->cookie, cookie, cookie_len);
994c560df6fSPatrick Kelsey 			cce->disable_time = 0;
995c560df6fSPatrick Kelsey 		} else {
996c560df6fSPatrick Kelsey 			/* invalid cookie length, disable entry */
997c560df6fSPatrick Kelsey 			cce->server_mss = 0;
998c560df6fSPatrick Kelsey 			cce->cookie_len = 0;
999c560df6fSPatrick Kelsey 			/*
1000c560df6fSPatrick Kelsey 			 * Preserve the existing disable time if it is
1001c560df6fSPatrick Kelsey 			 * already disabled.
1002c560df6fSPatrick Kelsey 			 */
1003c560df6fSPatrick Kelsey 			if (cce->disable_time == 0)
1004c560df6fSPatrick Kelsey 				cce->disable_time = getsbinuptime();
1005c560df6fSPatrick Kelsey 		}
1006c560df6fSPatrick Kelsey 	} else
1007c560df6fSPatrick Kelsey 		tcp_fastopen_ccache_create(ccb, inc, mss, cookie_len, cookie);
1008c560df6fSPatrick Kelsey 
1009c560df6fSPatrick Kelsey 	CCB_UNLOCK(ccb);
1010c560df6fSPatrick Kelsey }
1011c560df6fSPatrick Kelsey 
1012c560df6fSPatrick Kelsey static struct tcp_fastopen_ccache_entry *
tcp_fastopen_ccache_lookup(struct in_conninfo * inc,struct tcp_fastopen_ccache_bucket ** ccbp)1013c560df6fSPatrick Kelsey tcp_fastopen_ccache_lookup(struct in_conninfo *inc,
1014c560df6fSPatrick Kelsey     struct tcp_fastopen_ccache_bucket **ccbp)
1015c560df6fSPatrick Kelsey {
1016c560df6fSPatrick Kelsey 	struct tcp_fastopen_ccache_bucket *ccb;
1017c560df6fSPatrick Kelsey 	struct tcp_fastopen_ccache_entry *cce;
1018c560df6fSPatrick Kelsey 	uint32_t last_word;
1019c560df6fSPatrick Kelsey 	uint32_t hash;
1020c560df6fSPatrick Kelsey 
1021c560df6fSPatrick Kelsey 	hash = jenkins_hash32((uint32_t *)&inc->inc_ie.ie_dependladdr, 4,
1022c560df6fSPatrick Kelsey 	    V_tcp_fastopen_ccache.secret);
1023c560df6fSPatrick Kelsey 	hash = jenkins_hash32((uint32_t *)&inc->inc_ie.ie_dependfaddr, 4,
1024c560df6fSPatrick Kelsey 	    hash);
1025c560df6fSPatrick Kelsey 	last_word = inc->inc_fport;
1026c560df6fSPatrick Kelsey 	hash = jenkins_hash32(&last_word, 1, hash);
1027c560df6fSPatrick Kelsey 	ccb = &V_tcp_fastopen_ccache.base[hash & V_tcp_fastopen_ccache.mask];
1028c560df6fSPatrick Kelsey 	*ccbp = ccb;
1029c560df6fSPatrick Kelsey 	CCB_LOCK(ccb);
1030c560df6fSPatrick Kelsey 
1031c560df6fSPatrick Kelsey 	/*
1032c560df6fSPatrick Kelsey 	 * Always returns with locked bucket.
1033c560df6fSPatrick Kelsey 	 */
1034c560df6fSPatrick Kelsey 	TAILQ_FOREACH(cce, &ccb->ccb_entries, cce_link)
1035c560df6fSPatrick Kelsey 		if ((!(cce->af == AF_INET6) == !(inc->inc_flags & INC_ISIPV6)) &&
1036c560df6fSPatrick Kelsey 		    (cce->server_port == inc->inc_ie.ie_fport) &&
1037c560df6fSPatrick Kelsey 		    (((cce->af == AF_INET) &&
1038c560df6fSPatrick Kelsey 		      (cce->cce_client_ip.v4.s_addr == inc->inc_laddr.s_addr) &&
1039c560df6fSPatrick Kelsey 		      (cce->cce_server_ip.v4.s_addr == inc->inc_faddr.s_addr)) ||
1040c560df6fSPatrick Kelsey 		     ((cce->af == AF_INET6) &&
1041c560df6fSPatrick Kelsey 		      IN6_ARE_ADDR_EQUAL(&cce->cce_client_ip.v6, &inc->inc6_laddr) &&
1042c560df6fSPatrick Kelsey 		      IN6_ARE_ADDR_EQUAL(&cce->cce_server_ip.v6, &inc->inc6_faddr))))
1043c560df6fSPatrick Kelsey 			break;
1044c560df6fSPatrick Kelsey 
1045c560df6fSPatrick Kelsey 	return (cce);
1046c560df6fSPatrick Kelsey }
1047c560df6fSPatrick Kelsey 
1048c560df6fSPatrick Kelsey static struct tcp_fastopen_ccache_entry *
tcp_fastopen_ccache_create(struct tcp_fastopen_ccache_bucket * ccb,struct in_conninfo * inc,uint16_t mss,uint8_t cookie_len,uint8_t * cookie)1049c560df6fSPatrick Kelsey tcp_fastopen_ccache_create(struct tcp_fastopen_ccache_bucket *ccb,
1050c560df6fSPatrick Kelsey     struct in_conninfo *inc, uint16_t mss, uint8_t cookie_len, uint8_t *cookie)
1051c560df6fSPatrick Kelsey {
1052c560df6fSPatrick Kelsey 	struct tcp_fastopen_ccache_entry *cce;
1053c560df6fSPatrick Kelsey 
1054c560df6fSPatrick Kelsey 	/*
1055c560df6fSPatrick Kelsey 	 * 1. Create a new entry, or
1056c560df6fSPatrick Kelsey 	 * 2. Reclaim an existing entry, or
1057c560df6fSPatrick Kelsey 	 * 3. Fail
1058c560df6fSPatrick Kelsey 	 */
1059c560df6fSPatrick Kelsey 
1060c560df6fSPatrick Kelsey 	CCB_LOCK_ASSERT(ccb);
1061c560df6fSPatrick Kelsey 
1062c560df6fSPatrick Kelsey 	cce = NULL;
1063c560df6fSPatrick Kelsey 	if (ccb->ccb_num_entries < V_tcp_fastopen_ccache.bucket_limit)
1064c560df6fSPatrick Kelsey 		cce = uma_zalloc(V_tcp_fastopen_ccache.zone, M_NOWAIT);
1065c560df6fSPatrick Kelsey 
1066c560df6fSPatrick Kelsey 	if (cce == NULL) {
1067c560df6fSPatrick Kelsey 		/*
1068c560df6fSPatrick Kelsey 		 * At bucket limit, or out of memory - reclaim last
1069c560df6fSPatrick Kelsey 		 * entry in bucket.
1070c560df6fSPatrick Kelsey 		 */
1071c560df6fSPatrick Kelsey 		cce = TAILQ_LAST(&ccb->ccb_entries, bucket_entries);
1072c560df6fSPatrick Kelsey 		if (cce == NULL) {
1073c560df6fSPatrick Kelsey 			/* XXX count this event */
1074c560df6fSPatrick Kelsey 			return (NULL);
1075c560df6fSPatrick Kelsey 		}
1076c560df6fSPatrick Kelsey 
1077c560df6fSPatrick Kelsey 		TAILQ_REMOVE(&ccb->ccb_entries, cce, cce_link);
1078c560df6fSPatrick Kelsey 	} else
1079c560df6fSPatrick Kelsey 		ccb->ccb_num_entries++;
1080c560df6fSPatrick Kelsey 
1081c560df6fSPatrick Kelsey 	TAILQ_INSERT_HEAD(&ccb->ccb_entries, cce, cce_link);
1082c560df6fSPatrick Kelsey 	cce->af = (inc->inc_flags & INC_ISIPV6) ? AF_INET6 : AF_INET;
1083c560df6fSPatrick Kelsey 	if (cce->af == AF_INET) {
1084c560df6fSPatrick Kelsey 		cce->cce_client_ip.v4 = inc->inc_laddr;
1085c560df6fSPatrick Kelsey 		cce->cce_server_ip.v4 = inc->inc_faddr;
1086c560df6fSPatrick Kelsey 	} else {
1087c560df6fSPatrick Kelsey 		cce->cce_client_ip.v6 = inc->inc6_laddr;
1088c560df6fSPatrick Kelsey 		cce->cce_server_ip.v6 = inc->inc6_faddr;
1089c560df6fSPatrick Kelsey 	}
1090c560df6fSPatrick Kelsey 	cce->server_port = inc->inc_fport;
10911c714531SMichael Tuexen 	if ((cookie_len >= TCP_FASTOPEN_MIN_COOKIE_LEN) &&
10921c714531SMichael Tuexen 	    (cookie_len <= TCP_FASTOPEN_MAX_COOKIE_LEN) &&
1093c560df6fSPatrick Kelsey 	    ((cookie_len & 0x1) == 0)) {
1094c560df6fSPatrick Kelsey 		cce->server_mss = mss;
1095c560df6fSPatrick Kelsey 		cce->cookie_len = cookie_len;
1096c560df6fSPatrick Kelsey 		memcpy(cce->cookie, cookie, cookie_len);
1097c560df6fSPatrick Kelsey 		cce->disable_time = 0;
1098c560df6fSPatrick Kelsey 	} else {
1099c560df6fSPatrick Kelsey 		/* invalid cookie length, disable cce */
1100c560df6fSPatrick Kelsey 		cce->server_mss = 0;
1101c560df6fSPatrick Kelsey 		cce->cookie_len = 0;
1102c560df6fSPatrick Kelsey 		cce->disable_time = getsbinuptime();
1103c560df6fSPatrick Kelsey 	}
1104c560df6fSPatrick Kelsey 
1105c560df6fSPatrick Kelsey 	return (cce);
1106c560df6fSPatrick Kelsey }
1107c560df6fSPatrick Kelsey 
1108c560df6fSPatrick Kelsey static void
tcp_fastopen_ccache_bucket_trim(struct tcp_fastopen_ccache_bucket * ccb,unsigned int limit)1109c560df6fSPatrick Kelsey tcp_fastopen_ccache_bucket_trim(struct tcp_fastopen_ccache_bucket *ccb,
1110c560df6fSPatrick Kelsey     unsigned int limit)
1111c560df6fSPatrick Kelsey {
1112c560df6fSPatrick Kelsey 	struct tcp_fastopen_ccache_entry *cce, *cce_tmp;
1113c560df6fSPatrick Kelsey 	unsigned int entries;
1114c560df6fSPatrick Kelsey 
1115c560df6fSPatrick Kelsey 	CCB_LOCK(ccb);
1116c560df6fSPatrick Kelsey 	entries = 0;
1117c560df6fSPatrick Kelsey 	TAILQ_FOREACH_SAFE(cce, &ccb->ccb_entries, cce_link, cce_tmp) {
1118c560df6fSPatrick Kelsey 		entries++;
1119c560df6fSPatrick Kelsey 		if (entries > limit)
1120c560df6fSPatrick Kelsey 			tcp_fastopen_ccache_entry_drop(cce, ccb);
1121c560df6fSPatrick Kelsey 	}
11221f13c23fSPatrick Kelsey 	KASSERT(ccb->ccb_num_entries <= (int)limit,
1123c560df6fSPatrick Kelsey 	    ("%s: ccb->ccb_num_entries %d exceeds limit %d", __func__,
1124c560df6fSPatrick Kelsey 		ccb->ccb_num_entries, limit));
1125c560df6fSPatrick Kelsey 	if (limit == 0) {
1126c560df6fSPatrick Kelsey 		KASSERT(TAILQ_EMPTY(&ccb->ccb_entries),
1127c560df6fSPatrick Kelsey 		    ("%s: ccb->ccb_entries not empty", __func__));
1128c560df6fSPatrick Kelsey 		ccb->ccb_num_entries = -1; /* disable bucket */
1129c560df6fSPatrick Kelsey 	}
1130c560df6fSPatrick Kelsey 	CCB_UNLOCK(ccb);
1131c560df6fSPatrick Kelsey }
1132c560df6fSPatrick Kelsey 
1133c560df6fSPatrick Kelsey static void
tcp_fastopen_ccache_entry_drop(struct tcp_fastopen_ccache_entry * cce,struct tcp_fastopen_ccache_bucket * ccb)1134c560df6fSPatrick Kelsey tcp_fastopen_ccache_entry_drop(struct tcp_fastopen_ccache_entry *cce,
1135c560df6fSPatrick Kelsey     struct tcp_fastopen_ccache_bucket *ccb)
1136c560df6fSPatrick Kelsey {
1137c560df6fSPatrick Kelsey 
1138c560df6fSPatrick Kelsey 	CCB_LOCK_ASSERT(ccb);
1139c560df6fSPatrick Kelsey 
1140c560df6fSPatrick Kelsey 	TAILQ_REMOVE(&ccb->ccb_entries, cce, cce_link);
1141c560df6fSPatrick Kelsey 	ccb->ccb_num_entries--;
1142c560df6fSPatrick Kelsey 	uma_zfree(V_tcp_fastopen_ccache.zone, cce);
1143c560df6fSPatrick Kelsey }
1144c560df6fSPatrick Kelsey 
1145c9da5853SMichael Tuexen static int
sysctl_net_inet_tcp_fastopen_ccache_list(SYSCTL_HANDLER_ARGS)1146c9da5853SMichael Tuexen sysctl_net_inet_tcp_fastopen_ccache_list(SYSCTL_HANDLER_ARGS)
1147c9da5853SMichael Tuexen {
1148c9da5853SMichael Tuexen 	struct sbuf sb;
1149c9da5853SMichael Tuexen 	struct tcp_fastopen_ccache_bucket *ccb;
1150c9da5853SMichael Tuexen 	struct tcp_fastopen_ccache_entry *cce;
1151c9da5853SMichael Tuexen 	sbintime_t now, duration, limit;
1152c9da5853SMichael Tuexen 	const int linesize = 128;
1153c9da5853SMichael Tuexen 	int i, error, num_entries;
1154c9da5853SMichael Tuexen 	unsigned int j;
1155c9da5853SMichael Tuexen #ifdef INET6
1156c9da5853SMichael Tuexen 	char clt_buf[INET6_ADDRSTRLEN], srv_buf[INET6_ADDRSTRLEN];
1157c9da5853SMichael Tuexen #else
1158c9da5853SMichael Tuexen 	char clt_buf[INET_ADDRSTRLEN], srv_buf[INET_ADDRSTRLEN];
1159c9da5853SMichael Tuexen #endif
1160c9da5853SMichael Tuexen 
1161c9da5853SMichael Tuexen 	if (jailed_without_vnet(curthread->td_ucred) != 0)
1162c9da5853SMichael Tuexen 		return (EPERM);
1163c9da5853SMichael Tuexen 
1164c9da5853SMichael Tuexen 	/* Only allow root to read the client cookie cache */
1165c9da5853SMichael Tuexen 	if (curthread->td_ucred->cr_uid != 0)
1166c9da5853SMichael Tuexen 		return (EPERM);
1167c9da5853SMichael Tuexen 
1168c9da5853SMichael Tuexen 	num_entries = 0;
1169c9da5853SMichael Tuexen 	for (i = 0; i < V_tcp_fastopen_ccache.buckets; i++) {
1170c9da5853SMichael Tuexen 		ccb = &V_tcp_fastopen_ccache.base[i];
1171c9da5853SMichael Tuexen 		CCB_LOCK(ccb);
1172c9da5853SMichael Tuexen 		if (ccb->ccb_num_entries > 0)
1173c9da5853SMichael Tuexen 			num_entries += ccb->ccb_num_entries;
1174c9da5853SMichael Tuexen 		CCB_UNLOCK(ccb);
1175c9da5853SMichael Tuexen 	}
1176c9da5853SMichael Tuexen 	sbuf_new(&sb, NULL, linesize * (num_entries + 1), SBUF_INCLUDENUL);
1177c9da5853SMichael Tuexen 
1178c9da5853SMichael Tuexen 	sbuf_printf(&sb,
1179c9da5853SMichael Tuexen 	            "\nLocal IP address     Remote IP address     Port   MSS"
1180c9da5853SMichael Tuexen 	            " Disabled Cookie\n");
1181c9da5853SMichael Tuexen 
1182c9da5853SMichael Tuexen 	now = getsbinuptime();
1183c9da5853SMichael Tuexen 	limit = (sbintime_t)V_tcp_fastopen_path_disable_time << 32;
1184c9da5853SMichael Tuexen 	for (i = 0; i < V_tcp_fastopen_ccache.buckets; i++) {
1185c9da5853SMichael Tuexen 		ccb = &V_tcp_fastopen_ccache.base[i];
1186c9da5853SMichael Tuexen 		CCB_LOCK(ccb);
1187c9da5853SMichael Tuexen 		TAILQ_FOREACH(cce, &ccb->ccb_entries, cce_link) {
1188c9da5853SMichael Tuexen 			if (cce->disable_time != 0) {
1189c9da5853SMichael Tuexen 				duration = now - cce->disable_time;
1190c9da5853SMichael Tuexen 				if (limit >= duration)
1191c9da5853SMichael Tuexen 					duration = limit - duration;
1192c9da5853SMichael Tuexen 				else
1193c9da5853SMichael Tuexen 					duration = 0;
1194c9da5853SMichael Tuexen 			} else
1195c9da5853SMichael Tuexen 				duration = 0;
1196c9da5853SMichael Tuexen 			sbuf_printf(&sb,
1197c9da5853SMichael Tuexen 			            "%-20s %-20s %5u %5u ",
1198c9da5853SMichael Tuexen 			            inet_ntop(cce->af, &cce->cce_client_ip,
1199c9da5853SMichael Tuexen 			                clt_buf, sizeof(clt_buf)),
1200c9da5853SMichael Tuexen 			            inet_ntop(cce->af, &cce->cce_server_ip,
1201c9da5853SMichael Tuexen 			                srv_buf, sizeof(srv_buf)),
1202c9da5853SMichael Tuexen 			            ntohs(cce->server_port),
1203c9da5853SMichael Tuexen 			            cce->server_mss);
1204c9da5853SMichael Tuexen 			if (duration > 0)
1205c9da5853SMichael Tuexen 				sbuf_printf(&sb, "%7ds ", sbintime_getsec(duration));
1206c9da5853SMichael Tuexen 			else
1207c9da5853SMichael Tuexen 				sbuf_printf(&sb, "%8s ", "No");
1208c9da5853SMichael Tuexen 			for (j = 0; j < cce->cookie_len; j++)
1209c9da5853SMichael Tuexen 				sbuf_printf(&sb, "%02x", cce->cookie[j]);
1210c9da5853SMichael Tuexen 			sbuf_putc(&sb, '\n');
1211c9da5853SMichael Tuexen 		}
1212c9da5853SMichael Tuexen 		CCB_UNLOCK(ccb);
1213c9da5853SMichael Tuexen 	}
1214c9da5853SMichael Tuexen 	error = sbuf_finish(&sb);
1215c9da5853SMichael Tuexen 	if (error == 0)
1216c9da5853SMichael Tuexen 		error = SYSCTL_OUT(req, sbuf_data(&sb), sbuf_len(&sb));
1217c9da5853SMichael Tuexen 	sbuf_delete(&sb);
1218c9da5853SMichael Tuexen 	return (error);
1219c9da5853SMichael Tuexen }
1220