xref: /dflybsd-src/sys/net/wg/wg_cookie.c (revision 7ef217fece075cfba22984d6acc1c7b06e1cb9bb)
1a6bca3d2SAaron LI /* SPDX-License-Identifier: ISC
2a6bca3d2SAaron LI  *
3a6bca3d2SAaron LI  * Copyright (C) 2015-2021 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
4a6bca3d2SAaron LI  * Copyright (C) 2019-2021 Matt Dunwoodie <ncon@noconroy.net>
5a6bca3d2SAaron LI  */
6a6bca3d2SAaron LI 
7a6bca3d2SAaron LI #include "opt_inet.h"
8a6bca3d2SAaron LI #include "opt_inet6.h"
9a6bca3d2SAaron LI 
10a6bca3d2SAaron LI #include <sys/param.h>
11a6bca3d2SAaron LI #include <sys/systm.h>
12a6bca3d2SAaron LI #include <sys/kernel.h>
13a6bca3d2SAaron LI #include <sys/lock.h>
14a6bca3d2SAaron LI #include <sys/mutex.h>
15a6bca3d2SAaron LI #include <sys/socket.h>
16a6bca3d2SAaron LI #include <crypto/siphash/siphash.h>
17a6bca3d2SAaron LI #include <netinet/in.h>
18a6bca3d2SAaron LI #include <vm/uma.h>
19a6bca3d2SAaron LI 
20a6bca3d2SAaron LI #include "wg_cookie.h"
21a6bca3d2SAaron LI 
22a6bca3d2SAaron LI #define COOKIE_MAC1_KEY_LABEL	"mac1----"
23a6bca3d2SAaron LI #define COOKIE_COOKIE_KEY_LABEL	"cookie--"
24a6bca3d2SAaron LI #define COOKIE_SECRET_MAX_AGE	120
25a6bca3d2SAaron LI #define COOKIE_SECRET_LATENCY	5
26a6bca3d2SAaron LI 
27a6bca3d2SAaron LI /* Constants for initiation rate limiting */
28a6bca3d2SAaron LI #define RATELIMIT_SIZE		(1 << 13)
29a6bca3d2SAaron LI #define RATELIMIT_MASK		(RATELIMIT_SIZE - 1)
30a6bca3d2SAaron LI #define RATELIMIT_SIZE_MAX	(RATELIMIT_SIZE * 8)
31a6bca3d2SAaron LI #define INITIATIONS_PER_SECOND	20
32a6bca3d2SAaron LI #define INITIATIONS_BURSTABLE	5
33a6bca3d2SAaron LI #define INITIATION_COST		(SBT_1S / INITIATIONS_PER_SECOND)
34a6bca3d2SAaron LI #define TOKEN_MAX		(INITIATION_COST * INITIATIONS_BURSTABLE)
35a6bca3d2SAaron LI #define ELEMENT_TIMEOUT		1
36a6bca3d2SAaron LI #define IPV4_MASK_SIZE		4 /* Use all 4 bytes of IPv4 address */
37a6bca3d2SAaron LI #define IPV6_MASK_SIZE		8 /* Use top 8 bytes (/64) of IPv6 address */
38a6bca3d2SAaron LI 
39a6bca3d2SAaron LI struct ratelimit_key {
40a6bca3d2SAaron LI 	struct vnet *vnet;
41a6bca3d2SAaron LI 	uint8_t ip[IPV6_MASK_SIZE];
42a6bca3d2SAaron LI };
43a6bca3d2SAaron LI 
44a6bca3d2SAaron LI struct ratelimit_entry {
45a6bca3d2SAaron LI 	LIST_ENTRY(ratelimit_entry)	r_entry;
46a6bca3d2SAaron LI 	struct ratelimit_key		r_key;
47a6bca3d2SAaron LI 	sbintime_t			r_last_time;	/* sbinuptime */
48a6bca3d2SAaron LI 	uint64_t			r_tokens;
49a6bca3d2SAaron LI };
50a6bca3d2SAaron LI 
51a6bca3d2SAaron LI struct ratelimit {
52a6bca3d2SAaron LI 	uint8_t				rl_secret[SIPHASH_KEY_LENGTH];
53a6bca3d2SAaron LI 	struct mtx			rl_mtx;
54a6bca3d2SAaron LI 	struct callout			rl_gc;
55a6bca3d2SAaron LI 	LIST_HEAD(, ratelimit_entry)	rl_table[RATELIMIT_SIZE];
56a6bca3d2SAaron LI 	size_t				rl_table_num;
57a6bca3d2SAaron LI 	bool				rl_initialized;
58a6bca3d2SAaron LI };
59a6bca3d2SAaron LI 
60a6bca3d2SAaron LI static void	precompute_key(uint8_t *,
61a6bca3d2SAaron LI 			const uint8_t[COOKIE_INPUT_SIZE], const char *);
62a6bca3d2SAaron LI static void	macs_mac1(struct cookie_macs *, const void *, size_t,
63a6bca3d2SAaron LI 			const uint8_t[COOKIE_KEY_SIZE]);
64a6bca3d2SAaron LI static void	macs_mac2(struct cookie_macs *, const void *, size_t,
65a6bca3d2SAaron LI 			const uint8_t[COOKIE_COOKIE_SIZE]);
66a6bca3d2SAaron LI static int	timer_expired(sbintime_t, uint32_t, uint32_t);
67a6bca3d2SAaron LI static void	make_cookie(struct cookie_checker *,
68a6bca3d2SAaron LI 			uint8_t[COOKIE_COOKIE_SIZE], struct sockaddr *);
69a6bca3d2SAaron LI static void	ratelimit_init(struct ratelimit *);
70a6bca3d2SAaron LI static void	ratelimit_deinit(struct ratelimit *);
71a6bca3d2SAaron LI static void	ratelimit_gc_callout(void *);
72a6bca3d2SAaron LI static void	ratelimit_gc_schedule(struct ratelimit *);
73a6bca3d2SAaron LI static void	ratelimit_gc(struct ratelimit *, bool);
74a6bca3d2SAaron LI static int	ratelimit_allow(struct ratelimit *, struct sockaddr *, struct vnet *);
75a6bca3d2SAaron LI static uint64_t siphash13(const uint8_t [SIPHASH_KEY_LENGTH], const void *, size_t);
76a6bca3d2SAaron LI 
77a6bca3d2SAaron LI static struct ratelimit ratelimit_v4;
78a6bca3d2SAaron LI #ifdef INET6
79a6bca3d2SAaron LI static struct ratelimit ratelimit_v6;
80a6bca3d2SAaron LI #endif
81a6bca3d2SAaron LI static uma_zone_t ratelimit_zone;
82a6bca3d2SAaron LI 
83a6bca3d2SAaron LI /* Public Functions */
84a6bca3d2SAaron LI int
85a6bca3d2SAaron LI cookie_init(void)
86a6bca3d2SAaron LI {
87a6bca3d2SAaron LI 	if ((ratelimit_zone = uma_zcreate("wg ratelimit",
88a6bca3d2SAaron LI 	    sizeof(struct ratelimit_entry), NULL, NULL, NULL, NULL, 0, 0)) == NULL)
89a6bca3d2SAaron LI 		return ENOMEM;
90a6bca3d2SAaron LI 
91a6bca3d2SAaron LI 	ratelimit_init(&ratelimit_v4);
92a6bca3d2SAaron LI #ifdef INET6
93a6bca3d2SAaron LI 	ratelimit_init(&ratelimit_v6);
94a6bca3d2SAaron LI #endif
95a6bca3d2SAaron LI 	return (0);
96a6bca3d2SAaron LI }
97a6bca3d2SAaron LI 
98a6bca3d2SAaron LI void
99a6bca3d2SAaron LI cookie_deinit(void)
100a6bca3d2SAaron LI {
101a6bca3d2SAaron LI 	ratelimit_deinit(&ratelimit_v4);
102a6bca3d2SAaron LI #ifdef INET6
103a6bca3d2SAaron LI 	ratelimit_deinit(&ratelimit_v6);
104a6bca3d2SAaron LI #endif
105a6bca3d2SAaron LI 	if (ratelimit_zone != NULL)
106a6bca3d2SAaron LI 		uma_zdestroy(ratelimit_zone);
107a6bca3d2SAaron LI }
108a6bca3d2SAaron LI 
109a6bca3d2SAaron LI void
110a6bca3d2SAaron LI cookie_checker_init(struct cookie_checker *cc)
111a6bca3d2SAaron LI {
112a6bca3d2SAaron LI 	bzero(cc, sizeof(*cc));
113a6bca3d2SAaron LI 
114*7ef217feSAaron LI 	lockinit(&cc->cc_key_lock, "cookie_checker_key", 0, 0);
115a6bca3d2SAaron LI 	mtx_init(&cc->cc_secret_mtx, "cookie_checker_secret", NULL, MTX_DEF);
116a6bca3d2SAaron LI }
117a6bca3d2SAaron LI 
118a6bca3d2SAaron LI void
119a6bca3d2SAaron LI cookie_checker_free(struct cookie_checker *cc)
120a6bca3d2SAaron LI {
121*7ef217feSAaron LI 	lockuninit(&cc->cc_key_lock);
122a6bca3d2SAaron LI 	mtx_destroy(&cc->cc_secret_mtx);
123a6bca3d2SAaron LI 	explicit_bzero(cc, sizeof(*cc));
124a6bca3d2SAaron LI }
125a6bca3d2SAaron LI 
126a6bca3d2SAaron LI void
127a6bca3d2SAaron LI cookie_checker_update(struct cookie_checker *cc,
128a6bca3d2SAaron LI     const uint8_t key[COOKIE_INPUT_SIZE])
129a6bca3d2SAaron LI {
130*7ef217feSAaron LI 	lockmgr(&cc->cc_key_lock, LK_EXCLUSIVE);
131a6bca3d2SAaron LI 	if (key) {
132a6bca3d2SAaron LI 		precompute_key(cc->cc_mac1_key, key, COOKIE_MAC1_KEY_LABEL);
133a6bca3d2SAaron LI 		precompute_key(cc->cc_cookie_key, key, COOKIE_COOKIE_KEY_LABEL);
134a6bca3d2SAaron LI 	} else {
135a6bca3d2SAaron LI 		bzero(cc->cc_mac1_key, sizeof(cc->cc_mac1_key));
136a6bca3d2SAaron LI 		bzero(cc->cc_cookie_key, sizeof(cc->cc_cookie_key));
137a6bca3d2SAaron LI 	}
138*7ef217feSAaron LI 	lockmgr(&cc->cc_key_lock, LK_RELEASE);
139a6bca3d2SAaron LI }
140a6bca3d2SAaron LI 
141a6bca3d2SAaron LI void
142a6bca3d2SAaron LI cookie_checker_create_payload(struct cookie_checker *cc,
143a6bca3d2SAaron LI     struct cookie_macs *macs, uint8_t nonce[COOKIE_NONCE_SIZE],
144a6bca3d2SAaron LI     uint8_t ecookie[COOKIE_ENCRYPTED_SIZE], struct sockaddr *sa)
145a6bca3d2SAaron LI {
146a6bca3d2SAaron LI 	uint8_t cookie[COOKIE_COOKIE_SIZE];
147a6bca3d2SAaron LI 
148a6bca3d2SAaron LI 	make_cookie(cc, cookie, sa);
149a6bca3d2SAaron LI 	arc4random_buf(nonce, COOKIE_NONCE_SIZE);
150a6bca3d2SAaron LI 
151*7ef217feSAaron LI 	lockmgr(&cc->cc_key_lock, LK_SHARED);
152a6bca3d2SAaron LI 	xchacha20poly1305_encrypt(ecookie, cookie, COOKIE_COOKIE_SIZE,
153a6bca3d2SAaron LI 	    macs->mac1, COOKIE_MAC_SIZE, nonce, cc->cc_cookie_key);
154*7ef217feSAaron LI 	lockmgr(&cc->cc_key_lock, LK_RELEASE);
155a6bca3d2SAaron LI 
156a6bca3d2SAaron LI 	explicit_bzero(cookie, sizeof(cookie));
157a6bca3d2SAaron LI }
158a6bca3d2SAaron LI 
159a6bca3d2SAaron LI void
160a6bca3d2SAaron LI cookie_maker_init(struct cookie_maker *cm, const uint8_t key[COOKIE_INPUT_SIZE])
161a6bca3d2SAaron LI {
162a6bca3d2SAaron LI 	bzero(cm, sizeof(*cm));
163a6bca3d2SAaron LI 	precompute_key(cm->cm_mac1_key, key, COOKIE_MAC1_KEY_LABEL);
164a6bca3d2SAaron LI 	precompute_key(cm->cm_cookie_key, key, COOKIE_COOKIE_KEY_LABEL);
165*7ef217feSAaron LI 	lockinit(&cm->cm_lock, "cookie_maker", 0, 0);
166a6bca3d2SAaron LI }
167a6bca3d2SAaron LI 
168a6bca3d2SAaron LI void
169a6bca3d2SAaron LI cookie_maker_free(struct cookie_maker *cm)
170a6bca3d2SAaron LI {
171*7ef217feSAaron LI 	lockuninit(&cm->cm_lock);
172a6bca3d2SAaron LI 	explicit_bzero(cm, sizeof(*cm));
173a6bca3d2SAaron LI }
174a6bca3d2SAaron LI 
175a6bca3d2SAaron LI int
176a6bca3d2SAaron LI cookie_maker_consume_payload(struct cookie_maker *cm,
177a6bca3d2SAaron LI     uint8_t nonce[COOKIE_NONCE_SIZE], uint8_t ecookie[COOKIE_ENCRYPTED_SIZE])
178a6bca3d2SAaron LI {
179a6bca3d2SAaron LI 	uint8_t cookie[COOKIE_COOKIE_SIZE];
180a6bca3d2SAaron LI 	int ret;
181a6bca3d2SAaron LI 
182*7ef217feSAaron LI 	lockmgr(&cm->cm_lock, LK_SHARED);
183a6bca3d2SAaron LI 	if (!cm->cm_mac1_sent) {
184a6bca3d2SAaron LI 		ret = ETIMEDOUT;
185a6bca3d2SAaron LI 		goto error;
186a6bca3d2SAaron LI 	}
187a6bca3d2SAaron LI 
188a6bca3d2SAaron LI 	if (!xchacha20poly1305_decrypt(cookie, ecookie, COOKIE_ENCRYPTED_SIZE,
189a6bca3d2SAaron LI 	    cm->cm_mac1_last, COOKIE_MAC_SIZE, nonce, cm->cm_cookie_key)) {
190a6bca3d2SAaron LI 		ret = EINVAL;
191a6bca3d2SAaron LI 		goto error;
192a6bca3d2SAaron LI 	}
193*7ef217feSAaron LI 	lockmgr(&cm->cm_lock, LK_RELEASE);
194a6bca3d2SAaron LI 
195*7ef217feSAaron LI 	lockmgr(&cm->cm_lock, LK_EXCLUSIVE);
196a6bca3d2SAaron LI 	memcpy(cm->cm_cookie, cookie, COOKIE_COOKIE_SIZE);
197a6bca3d2SAaron LI 	cm->cm_cookie_birthdate = getsbinuptime();
198a6bca3d2SAaron LI 	cm->cm_cookie_valid = true;
199a6bca3d2SAaron LI 	cm->cm_mac1_sent = false;
200*7ef217feSAaron LI 	lockmgr(&cm->cm_lock, LK_RELEASE);
201a6bca3d2SAaron LI 
202a6bca3d2SAaron LI 	return 0;
203a6bca3d2SAaron LI error:
204*7ef217feSAaron LI 	lockmgr(&cm->cm_lock, LK_RELEASE);
205a6bca3d2SAaron LI 	return ret;
206a6bca3d2SAaron LI }
207a6bca3d2SAaron LI 
208a6bca3d2SAaron LI void
209a6bca3d2SAaron LI cookie_maker_mac(struct cookie_maker *cm, struct cookie_macs *macs, void *buf,
210a6bca3d2SAaron LI     size_t len)
211a6bca3d2SAaron LI {
212*7ef217feSAaron LI 	lockmgr(&cm->cm_lock, LK_EXCLUSIVE);
213a6bca3d2SAaron LI 	macs_mac1(macs, buf, len, cm->cm_mac1_key);
214a6bca3d2SAaron LI 	memcpy(cm->cm_mac1_last, macs->mac1, COOKIE_MAC_SIZE);
215a6bca3d2SAaron LI 	cm->cm_mac1_sent = true;
216a6bca3d2SAaron LI 
217a6bca3d2SAaron LI 	if (cm->cm_cookie_valid &&
218a6bca3d2SAaron LI 	    !timer_expired(cm->cm_cookie_birthdate,
219a6bca3d2SAaron LI 	    COOKIE_SECRET_MAX_AGE - COOKIE_SECRET_LATENCY, 0)) {
220a6bca3d2SAaron LI 		macs_mac2(macs, buf, len, cm->cm_cookie);
221a6bca3d2SAaron LI 	} else {
222a6bca3d2SAaron LI 		bzero(macs->mac2, COOKIE_MAC_SIZE);
223a6bca3d2SAaron LI 		cm->cm_cookie_valid = false;
224a6bca3d2SAaron LI 	}
225*7ef217feSAaron LI 	lockmgr(&cm->cm_lock, LK_RELEASE);
226a6bca3d2SAaron LI }
227a6bca3d2SAaron LI 
228a6bca3d2SAaron LI int
229a6bca3d2SAaron LI cookie_checker_validate_macs(struct cookie_checker *cc, struct cookie_macs *macs,
230a6bca3d2SAaron LI     void *buf, size_t len, bool check_cookie, struct sockaddr *sa, struct vnet *vnet)
231a6bca3d2SAaron LI {
232a6bca3d2SAaron LI 	struct cookie_macs our_macs;
233a6bca3d2SAaron LI 	uint8_t cookie[COOKIE_COOKIE_SIZE];
234a6bca3d2SAaron LI 
235a6bca3d2SAaron LI 	/* Validate incoming MACs */
236*7ef217feSAaron LI 	lockmgr(&cc->cc_key_lock, LK_SHARED);
237a6bca3d2SAaron LI 	macs_mac1(&our_macs, buf, len, cc->cc_mac1_key);
238*7ef217feSAaron LI 	lockmgr(&cc->cc_key_lock, LK_RELEASE);
239a6bca3d2SAaron LI 
240a6bca3d2SAaron LI 	/* If mac1 is invald, we want to drop the packet */
241a6bca3d2SAaron LI 	if (timingsafe_bcmp(our_macs.mac1, macs->mac1, COOKIE_MAC_SIZE) != 0)
242a6bca3d2SAaron LI 		return EINVAL;
243a6bca3d2SAaron LI 
244a6bca3d2SAaron LI 	if (check_cookie) {
245a6bca3d2SAaron LI 		make_cookie(cc, cookie, sa);
246a6bca3d2SAaron LI 		macs_mac2(&our_macs, buf, len, cookie);
247a6bca3d2SAaron LI 
248a6bca3d2SAaron LI 		/* If the mac2 is invalid, we want to send a cookie response */
249a6bca3d2SAaron LI 		if (timingsafe_bcmp(our_macs.mac2, macs->mac2, COOKIE_MAC_SIZE) != 0)
250a6bca3d2SAaron LI 			return EAGAIN;
251a6bca3d2SAaron LI 
252a6bca3d2SAaron LI 		/* If the mac2 is valid, we may want rate limit the peer.
253a6bca3d2SAaron LI 		 * ratelimit_allow will return either 0 or ECONNREFUSED,
254a6bca3d2SAaron LI 		 * implying there is no ratelimiting, or we should ratelimit
255a6bca3d2SAaron LI 		 * (refuse) respectively. */
256a6bca3d2SAaron LI 		if (sa->sa_family == AF_INET)
257a6bca3d2SAaron LI 			return ratelimit_allow(&ratelimit_v4, sa, vnet);
258a6bca3d2SAaron LI #ifdef INET6
259a6bca3d2SAaron LI 		else if (sa->sa_family == AF_INET6)
260a6bca3d2SAaron LI 			return ratelimit_allow(&ratelimit_v6, sa, vnet);
261a6bca3d2SAaron LI #endif
262a6bca3d2SAaron LI 		else
263a6bca3d2SAaron LI 			return EAFNOSUPPORT;
264a6bca3d2SAaron LI 	}
265a6bca3d2SAaron LI 
266a6bca3d2SAaron LI 	return 0;
267a6bca3d2SAaron LI }
268a6bca3d2SAaron LI 
269a6bca3d2SAaron LI /* Private functions */
270a6bca3d2SAaron LI static void
271a6bca3d2SAaron LI precompute_key(uint8_t *key, const uint8_t input[COOKIE_INPUT_SIZE],
272a6bca3d2SAaron LI     const char *label)
273a6bca3d2SAaron LI {
274a6bca3d2SAaron LI 	struct blake2s_state blake;
275a6bca3d2SAaron LI 	blake2s_init(&blake, COOKIE_KEY_SIZE);
276a6bca3d2SAaron LI 	blake2s_update(&blake, label, strlen(label));
277a6bca3d2SAaron LI 	blake2s_update(&blake, input, COOKIE_INPUT_SIZE);
278a6bca3d2SAaron LI 	blake2s_final(&blake, key);
279a6bca3d2SAaron LI }
280a6bca3d2SAaron LI 
281a6bca3d2SAaron LI static void
282a6bca3d2SAaron LI macs_mac1(struct cookie_macs *macs, const void *buf, size_t len,
283a6bca3d2SAaron LI     const uint8_t key[COOKIE_KEY_SIZE])
284a6bca3d2SAaron LI {
285a6bca3d2SAaron LI 	struct blake2s_state state;
286a6bca3d2SAaron LI 	blake2s_init_key(&state, COOKIE_MAC_SIZE, key, COOKIE_KEY_SIZE);
287a6bca3d2SAaron LI 	blake2s_update(&state, buf, len);
288a6bca3d2SAaron LI 	blake2s_final(&state, macs->mac1);
289a6bca3d2SAaron LI }
290a6bca3d2SAaron LI 
291a6bca3d2SAaron LI static void
292a6bca3d2SAaron LI macs_mac2(struct cookie_macs *macs, const void *buf, size_t len,
293a6bca3d2SAaron LI     const uint8_t key[COOKIE_COOKIE_SIZE])
294a6bca3d2SAaron LI {
295a6bca3d2SAaron LI 	struct blake2s_state state;
296a6bca3d2SAaron LI 	blake2s_init_key(&state, COOKIE_MAC_SIZE, key, COOKIE_COOKIE_SIZE);
297a6bca3d2SAaron LI 	blake2s_update(&state, buf, len);
298a6bca3d2SAaron LI 	blake2s_update(&state, macs->mac1, COOKIE_MAC_SIZE);
299a6bca3d2SAaron LI 	blake2s_final(&state, macs->mac2);
300a6bca3d2SAaron LI }
301a6bca3d2SAaron LI 
302a6bca3d2SAaron LI static __inline int
303a6bca3d2SAaron LI timer_expired(sbintime_t timer, uint32_t sec, uint32_t nsec)
304a6bca3d2SAaron LI {
305a6bca3d2SAaron LI 	sbintime_t now = getsbinuptime();
306a6bca3d2SAaron LI 	return (now > (timer + sec * SBT_1S + nstosbt(nsec))) ? ETIMEDOUT : 0;
307a6bca3d2SAaron LI }
308a6bca3d2SAaron LI 
309a6bca3d2SAaron LI static void
310a6bca3d2SAaron LI make_cookie(struct cookie_checker *cc, uint8_t cookie[COOKIE_COOKIE_SIZE],
311a6bca3d2SAaron LI     struct sockaddr *sa)
312a6bca3d2SAaron LI {
313a6bca3d2SAaron LI 	struct blake2s_state state;
314a6bca3d2SAaron LI 
315a6bca3d2SAaron LI 	mtx_lock(&cc->cc_secret_mtx);
316a6bca3d2SAaron LI 	if (timer_expired(cc->cc_secret_birthdate,
317a6bca3d2SAaron LI 	    COOKIE_SECRET_MAX_AGE, 0)) {
318a6bca3d2SAaron LI 		arc4random_buf(cc->cc_secret, COOKIE_SECRET_SIZE);
319a6bca3d2SAaron LI 		cc->cc_secret_birthdate = getsbinuptime();
320a6bca3d2SAaron LI 	}
321a6bca3d2SAaron LI 	blake2s_init_key(&state, COOKIE_COOKIE_SIZE, cc->cc_secret,
322a6bca3d2SAaron LI 	    COOKIE_SECRET_SIZE);
323a6bca3d2SAaron LI 	mtx_unlock(&cc->cc_secret_mtx);
324a6bca3d2SAaron LI 
325a6bca3d2SAaron LI 	if (sa->sa_family == AF_INET) {
326a6bca3d2SAaron LI 		blake2s_update(&state, (uint8_t *)&satosin(sa)->sin_addr,
327a6bca3d2SAaron LI 				sizeof(struct in_addr));
328a6bca3d2SAaron LI 		blake2s_update(&state, (uint8_t *)&satosin(sa)->sin_port,
329a6bca3d2SAaron LI 				sizeof(in_port_t));
330a6bca3d2SAaron LI 		blake2s_final(&state, cookie);
331a6bca3d2SAaron LI #ifdef INET6
332a6bca3d2SAaron LI 	} else if (sa->sa_family == AF_INET6) {
333a6bca3d2SAaron LI 		blake2s_update(&state, (uint8_t *)&satosin6(sa)->sin6_addr,
334a6bca3d2SAaron LI 				sizeof(struct in6_addr));
335a6bca3d2SAaron LI 		blake2s_update(&state, (uint8_t *)&satosin6(sa)->sin6_port,
336a6bca3d2SAaron LI 				sizeof(in_port_t));
337a6bca3d2SAaron LI 		blake2s_final(&state, cookie);
338a6bca3d2SAaron LI #endif
339a6bca3d2SAaron LI 	} else {
340a6bca3d2SAaron LI 		arc4random_buf(cookie, COOKIE_COOKIE_SIZE);
341a6bca3d2SAaron LI 	}
342a6bca3d2SAaron LI }
343a6bca3d2SAaron LI 
344a6bca3d2SAaron LI static void
345a6bca3d2SAaron LI ratelimit_init(struct ratelimit *rl)
346a6bca3d2SAaron LI {
347a6bca3d2SAaron LI 	size_t i;
348a6bca3d2SAaron LI 	mtx_init(&rl->rl_mtx, "ratelimit_lock", NULL, MTX_DEF);
349a6bca3d2SAaron LI 	callout_init_mtx(&rl->rl_gc, &rl->rl_mtx, 0);
350a6bca3d2SAaron LI 	arc4random_buf(rl->rl_secret, sizeof(rl->rl_secret));
351a6bca3d2SAaron LI 	for (i = 0; i < RATELIMIT_SIZE; i++)
352a6bca3d2SAaron LI 		LIST_INIT(&rl->rl_table[i]);
353a6bca3d2SAaron LI 	rl->rl_table_num = 0;
354a6bca3d2SAaron LI 	rl->rl_initialized = true;
355a6bca3d2SAaron LI }
356a6bca3d2SAaron LI 
357a6bca3d2SAaron LI static void
358a6bca3d2SAaron LI ratelimit_deinit(struct ratelimit *rl)
359a6bca3d2SAaron LI {
360a6bca3d2SAaron LI 	if (!rl->rl_initialized)
361a6bca3d2SAaron LI 		return;
362a6bca3d2SAaron LI 	mtx_lock(&rl->rl_mtx);
363a6bca3d2SAaron LI 	callout_stop(&rl->rl_gc);
364a6bca3d2SAaron LI 	ratelimit_gc(rl, true);
365a6bca3d2SAaron LI 	mtx_unlock(&rl->rl_mtx);
366a6bca3d2SAaron LI 	mtx_destroy(&rl->rl_mtx);
367a6bca3d2SAaron LI 
368a6bca3d2SAaron LI 	rl->rl_initialized = false;
369a6bca3d2SAaron LI }
370a6bca3d2SAaron LI 
371a6bca3d2SAaron LI static void
372a6bca3d2SAaron LI ratelimit_gc_callout(void *_rl)
373a6bca3d2SAaron LI {
374a6bca3d2SAaron LI 	/* callout will lock rl_mtx for us */
375a6bca3d2SAaron LI 	ratelimit_gc(_rl, false);
376a6bca3d2SAaron LI }
377a6bca3d2SAaron LI 
378a6bca3d2SAaron LI static void
379a6bca3d2SAaron LI ratelimit_gc_schedule(struct ratelimit *rl)
380a6bca3d2SAaron LI {
381a6bca3d2SAaron LI 	/* Trigger another GC if needed. There is no point calling GC if there
382a6bca3d2SAaron LI 	 * are no entries in the table. We also want to ensure that GC occurs
383a6bca3d2SAaron LI 	 * on a regular interval, so don't override a currently pending GC.
384a6bca3d2SAaron LI 	 *
385a6bca3d2SAaron LI 	 * In the case of a forced ratelimit_gc, there will be no entries left
386a6bca3d2SAaron LI 	 * so we will will not schedule another GC. */
387a6bca3d2SAaron LI 	if (rl->rl_table_num > 0 && !callout_pending(&rl->rl_gc))
388a6bca3d2SAaron LI 		callout_reset(&rl->rl_gc, ELEMENT_TIMEOUT * hz,
389a6bca3d2SAaron LI 		    ratelimit_gc_callout, rl);
390a6bca3d2SAaron LI }
391a6bca3d2SAaron LI 
392a6bca3d2SAaron LI static void
393a6bca3d2SAaron LI ratelimit_gc(struct ratelimit *rl, bool force)
394a6bca3d2SAaron LI {
395a6bca3d2SAaron LI 	size_t i;
396a6bca3d2SAaron LI 	struct ratelimit_entry *r, *tr;
397a6bca3d2SAaron LI 	sbintime_t expiry;
398a6bca3d2SAaron LI 
399a6bca3d2SAaron LI 	mtx_assert(&rl->rl_mtx, MA_OWNED);
400a6bca3d2SAaron LI 
401a6bca3d2SAaron LI 	if (rl->rl_table_num == 0)
402a6bca3d2SAaron LI 		return;
403a6bca3d2SAaron LI 
404a6bca3d2SAaron LI 	expiry = getsbinuptime() - ELEMENT_TIMEOUT * SBT_1S;
405a6bca3d2SAaron LI 
406a6bca3d2SAaron LI 	for (i = 0; i < RATELIMIT_SIZE; i++) {
407a6bca3d2SAaron LI 		LIST_FOREACH_SAFE(r, &rl->rl_table[i], r_entry, tr) {
408a6bca3d2SAaron LI 			if (r->r_last_time < expiry || force) {
409a6bca3d2SAaron LI 				rl->rl_table_num--;
410a6bca3d2SAaron LI 				LIST_REMOVE(r, r_entry);
411a6bca3d2SAaron LI 				uma_zfree(ratelimit_zone, r);
412a6bca3d2SAaron LI 			}
413a6bca3d2SAaron LI 		}
414a6bca3d2SAaron LI 	}
415a6bca3d2SAaron LI 
416a6bca3d2SAaron LI 	ratelimit_gc_schedule(rl);
417a6bca3d2SAaron LI }
418a6bca3d2SAaron LI 
419a6bca3d2SAaron LI static int
420a6bca3d2SAaron LI ratelimit_allow(struct ratelimit *rl, struct sockaddr *sa, struct vnet *vnet)
421a6bca3d2SAaron LI {
422a6bca3d2SAaron LI 	uint64_t bucket, tokens;
423a6bca3d2SAaron LI 	sbintime_t diff, now;
424a6bca3d2SAaron LI 	struct ratelimit_entry *r;
425a6bca3d2SAaron LI 	int ret = ECONNREFUSED;
426a6bca3d2SAaron LI 	struct ratelimit_key key = { .vnet = vnet };
427a6bca3d2SAaron LI 	size_t len = sizeof(key);
428a6bca3d2SAaron LI 
429a6bca3d2SAaron LI 	if (sa->sa_family == AF_INET) {
430a6bca3d2SAaron LI 		memcpy(key.ip, &satosin(sa)->sin_addr, IPV4_MASK_SIZE);
431a6bca3d2SAaron LI 		len -= IPV6_MASK_SIZE - IPV4_MASK_SIZE;
432a6bca3d2SAaron LI 	}
433a6bca3d2SAaron LI #ifdef INET6
434a6bca3d2SAaron LI 	else if (sa->sa_family == AF_INET6)
435a6bca3d2SAaron LI 		memcpy(key.ip, &satosin6(sa)->sin6_addr, IPV6_MASK_SIZE);
436a6bca3d2SAaron LI #endif
437a6bca3d2SAaron LI 	else
438a6bca3d2SAaron LI 		return ret;
439a6bca3d2SAaron LI 
440a6bca3d2SAaron LI 	bucket = siphash13(rl->rl_secret, &key, len) & RATELIMIT_MASK;
441a6bca3d2SAaron LI 	mtx_lock(&rl->rl_mtx);
442a6bca3d2SAaron LI 
443a6bca3d2SAaron LI 	LIST_FOREACH(r, &rl->rl_table[bucket], r_entry) {
444a6bca3d2SAaron LI 		if (bcmp(&r->r_key, &key, len) != 0)
445a6bca3d2SAaron LI 			continue;
446a6bca3d2SAaron LI 
447a6bca3d2SAaron LI 		/* If we get to here, we've found an entry for the endpoint.
448a6bca3d2SAaron LI 		 * We apply standard token bucket, by calculating the time
449a6bca3d2SAaron LI 		 * lapsed since our last_time, adding that, ensuring that we
450a6bca3d2SAaron LI 		 * cap the tokens at TOKEN_MAX. If the endpoint has no tokens
451a6bca3d2SAaron LI 		 * left (that is tokens <= INITIATION_COST) then we block the
452a6bca3d2SAaron LI 		 * request, otherwise we subtract the INITITIATION_COST and
453a6bca3d2SAaron LI 		 * return OK. */
454a6bca3d2SAaron LI 		now = getsbinuptime();
455a6bca3d2SAaron LI 		diff = now - r->r_last_time;
456a6bca3d2SAaron LI 		r->r_last_time = now;
457a6bca3d2SAaron LI 
458a6bca3d2SAaron LI 		tokens = r->r_tokens + diff;
459a6bca3d2SAaron LI 
460a6bca3d2SAaron LI 		if (tokens > TOKEN_MAX)
461a6bca3d2SAaron LI 			tokens = TOKEN_MAX;
462a6bca3d2SAaron LI 
463a6bca3d2SAaron LI 		if (tokens >= INITIATION_COST) {
464a6bca3d2SAaron LI 			r->r_tokens = tokens - INITIATION_COST;
465a6bca3d2SAaron LI 			goto ok;
466a6bca3d2SAaron LI 		} else {
467a6bca3d2SAaron LI 			r->r_tokens = tokens;
468a6bca3d2SAaron LI 			goto error;
469a6bca3d2SAaron LI 		}
470a6bca3d2SAaron LI 	}
471a6bca3d2SAaron LI 
472a6bca3d2SAaron LI 	/* If we get to here, we didn't have an entry for the endpoint, let's
473a6bca3d2SAaron LI 	 * add one if we have space. */
474a6bca3d2SAaron LI 	if (rl->rl_table_num >= RATELIMIT_SIZE_MAX)
475a6bca3d2SAaron LI 		goto error;
476a6bca3d2SAaron LI 
477a6bca3d2SAaron LI 	/* Goto error if out of memory */
478a6bca3d2SAaron LI 	if ((r = uma_zalloc(ratelimit_zone, M_NOWAIT | M_ZERO)) == NULL)
479a6bca3d2SAaron LI 		goto error;
480a6bca3d2SAaron LI 
481a6bca3d2SAaron LI 	rl->rl_table_num++;
482a6bca3d2SAaron LI 
483a6bca3d2SAaron LI 	/* Insert entry into the hashtable and ensure it's initialised */
484a6bca3d2SAaron LI 	LIST_INSERT_HEAD(&rl->rl_table[bucket], r, r_entry);
485a6bca3d2SAaron LI 	r->r_key = key;
486a6bca3d2SAaron LI 	r->r_last_time = getsbinuptime();
487a6bca3d2SAaron LI 	r->r_tokens = TOKEN_MAX - INITIATION_COST;
488a6bca3d2SAaron LI 
489a6bca3d2SAaron LI 	/* If we've added a new entry, let's trigger GC. */
490a6bca3d2SAaron LI 	ratelimit_gc_schedule(rl);
491a6bca3d2SAaron LI ok:
492a6bca3d2SAaron LI 	ret = 0;
493a6bca3d2SAaron LI error:
494a6bca3d2SAaron LI 	mtx_unlock(&rl->rl_mtx);
495a6bca3d2SAaron LI 	return ret;
496a6bca3d2SAaron LI }
497a6bca3d2SAaron LI 
498a6bca3d2SAaron LI static uint64_t siphash13(const uint8_t key[SIPHASH_KEY_LENGTH], const void *src, size_t len)
499a6bca3d2SAaron LI {
500a6bca3d2SAaron LI 	SIPHASH_CTX ctx;
501a6bca3d2SAaron LI 	return (SipHashX(&ctx, 1, 3, key, src, len));
502a6bca3d2SAaron LI }
503a6bca3d2SAaron LI 
504a6bca3d2SAaron LI #ifdef SELFTESTS
505a6bca3d2SAaron LI #include "selftest/cookie.c"
506a6bca3d2SAaron LI #endif /* SELFTESTS */
507