xref: /freebsd/sys/dev/wg/wg_cookie.c (revision b6a0ed7c78dd45937e404706620467bef61c308d)
1744bfb21SJohn Baldwin /* SPDX-License-Identifier: ISC
2744bfb21SJohn Baldwin  *
3744bfb21SJohn Baldwin  * Copyright (C) 2015-2021 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
4744bfb21SJohn Baldwin  * Copyright (C) 2019-2021 Matt Dunwoodie <ncon@noconroy.net>
5744bfb21SJohn Baldwin  */
6744bfb21SJohn Baldwin 
7744bfb21SJohn Baldwin #include "opt_inet.h"
8744bfb21SJohn Baldwin #include "opt_inet6.h"
9744bfb21SJohn Baldwin 
10744bfb21SJohn Baldwin #include <sys/param.h>
11744bfb21SJohn Baldwin #include <sys/systm.h>
12744bfb21SJohn Baldwin #include <sys/kernel.h>
13744bfb21SJohn Baldwin #include <sys/lock.h>
14744bfb21SJohn Baldwin #include <sys/mutex.h>
15744bfb21SJohn Baldwin #include <sys/rwlock.h>
16744bfb21SJohn Baldwin #include <sys/socket.h>
17744bfb21SJohn Baldwin #include <crypto/siphash/siphash.h>
18744bfb21SJohn Baldwin #include <netinet/in.h>
19744bfb21SJohn Baldwin #include <vm/uma.h>
20744bfb21SJohn Baldwin 
21744bfb21SJohn Baldwin #include "wg_cookie.h"
22744bfb21SJohn Baldwin 
23744bfb21SJohn Baldwin #define COOKIE_MAC1_KEY_LABEL	"mac1----"
24744bfb21SJohn Baldwin #define COOKIE_COOKIE_KEY_LABEL	"cookie--"
25744bfb21SJohn Baldwin #define COOKIE_SECRET_MAX_AGE	120
26744bfb21SJohn Baldwin #define COOKIE_SECRET_LATENCY	5
27744bfb21SJohn Baldwin 
28744bfb21SJohn Baldwin /* Constants for initiation rate limiting */
29744bfb21SJohn Baldwin #define RATELIMIT_SIZE		(1 << 13)
30744bfb21SJohn Baldwin #define RATELIMIT_MASK		(RATELIMIT_SIZE - 1)
31744bfb21SJohn Baldwin #define RATELIMIT_SIZE_MAX	(RATELIMIT_SIZE * 8)
32744bfb21SJohn Baldwin #define INITIATIONS_PER_SECOND	20
33744bfb21SJohn Baldwin #define INITIATIONS_BURSTABLE	5
34744bfb21SJohn Baldwin #define INITIATION_COST		(SBT_1S / INITIATIONS_PER_SECOND)
35744bfb21SJohn Baldwin #define TOKEN_MAX		(INITIATION_COST * INITIATIONS_BURSTABLE)
36744bfb21SJohn Baldwin #define ELEMENT_TIMEOUT		1
37744bfb21SJohn Baldwin #define IPV4_MASK_SIZE		4 /* Use all 4 bytes of IPv4 address */
38744bfb21SJohn Baldwin #define IPV6_MASK_SIZE		8 /* Use top 8 bytes (/64) of IPv6 address */
39744bfb21SJohn Baldwin 
40744bfb21SJohn Baldwin struct ratelimit_key {
41744bfb21SJohn Baldwin 	struct vnet *vnet;
42744bfb21SJohn Baldwin 	uint8_t ip[IPV6_MASK_SIZE];
43744bfb21SJohn Baldwin };
44744bfb21SJohn Baldwin 
45744bfb21SJohn Baldwin struct ratelimit_entry {
46744bfb21SJohn Baldwin 	LIST_ENTRY(ratelimit_entry)	r_entry;
47744bfb21SJohn Baldwin 	struct ratelimit_key		r_key;
48744bfb21SJohn Baldwin 	sbintime_t			r_last_time;	/* sbinuptime */
49744bfb21SJohn Baldwin 	uint64_t			r_tokens;
50744bfb21SJohn Baldwin };
51744bfb21SJohn Baldwin 
52744bfb21SJohn Baldwin struct ratelimit {
53744bfb21SJohn Baldwin 	uint8_t				rl_secret[SIPHASH_KEY_LENGTH];
54744bfb21SJohn Baldwin 	struct mtx			rl_mtx;
55744bfb21SJohn Baldwin 	struct callout			rl_gc;
56744bfb21SJohn Baldwin 	LIST_HEAD(, ratelimit_entry)	rl_table[RATELIMIT_SIZE];
57744bfb21SJohn Baldwin 	size_t				rl_table_num;
58b08ee10cSKyle Evans 	bool				rl_initialized;
59744bfb21SJohn Baldwin };
60744bfb21SJohn Baldwin 
61744bfb21SJohn Baldwin static void	precompute_key(uint8_t *,
62744bfb21SJohn Baldwin 			const uint8_t[COOKIE_INPUT_SIZE], const char *);
63744bfb21SJohn Baldwin static void	macs_mac1(struct cookie_macs *, const void *, size_t,
64744bfb21SJohn Baldwin 			const uint8_t[COOKIE_KEY_SIZE]);
65744bfb21SJohn Baldwin static void	macs_mac2(struct cookie_macs *, const void *, size_t,
66744bfb21SJohn Baldwin 			const uint8_t[COOKIE_COOKIE_SIZE]);
67744bfb21SJohn Baldwin static int	timer_expired(sbintime_t, uint32_t, uint32_t);
68744bfb21SJohn Baldwin static void	make_cookie(struct cookie_checker *,
69744bfb21SJohn Baldwin 			uint8_t[COOKIE_COOKIE_SIZE], struct sockaddr *);
70744bfb21SJohn Baldwin static void	ratelimit_init(struct ratelimit *);
71744bfb21SJohn Baldwin static void	ratelimit_deinit(struct ratelimit *);
72744bfb21SJohn Baldwin static void	ratelimit_gc_callout(void *);
73744bfb21SJohn Baldwin static void	ratelimit_gc_schedule(struct ratelimit *);
74744bfb21SJohn Baldwin static void	ratelimit_gc(struct ratelimit *, bool);
75744bfb21SJohn Baldwin static int	ratelimit_allow(struct ratelimit *, struct sockaddr *, struct vnet *);
76744bfb21SJohn Baldwin static uint64_t siphash13(const uint8_t [SIPHASH_KEY_LENGTH], const void *, size_t);
77744bfb21SJohn Baldwin 
78744bfb21SJohn Baldwin static struct ratelimit ratelimit_v4;
79744bfb21SJohn Baldwin #ifdef INET6
80744bfb21SJohn Baldwin static struct ratelimit ratelimit_v6;
81744bfb21SJohn Baldwin #endif
82744bfb21SJohn Baldwin static uma_zone_t ratelimit_zone;
83744bfb21SJohn Baldwin 
84744bfb21SJohn Baldwin /* Public Functions */
85744bfb21SJohn Baldwin int
86744bfb21SJohn Baldwin cookie_init(void)
87744bfb21SJohn Baldwin {
88*b6a0ed7cSMark Johnston 	ratelimit_zone = uma_zcreate("wg ratelimit",
89*b6a0ed7cSMark Johnston 	    sizeof(struct ratelimit_entry), NULL, NULL, NULL, NULL, 0, 0);
90744bfb21SJohn Baldwin 
91744bfb21SJohn Baldwin 	ratelimit_init(&ratelimit_v4);
92744bfb21SJohn Baldwin #ifdef INET6
93744bfb21SJohn Baldwin 	ratelimit_init(&ratelimit_v6);
94744bfb21SJohn Baldwin #endif
95744bfb21SJohn Baldwin 	return (0);
96744bfb21SJohn Baldwin }
97744bfb21SJohn Baldwin 
98744bfb21SJohn Baldwin void
99744bfb21SJohn Baldwin cookie_deinit(void)
100744bfb21SJohn Baldwin {
101744bfb21SJohn Baldwin 	ratelimit_deinit(&ratelimit_v4);
102744bfb21SJohn Baldwin #ifdef INET6
103744bfb21SJohn Baldwin 	ratelimit_deinit(&ratelimit_v6);
104744bfb21SJohn Baldwin #endif
105b08ee10cSKyle Evans 	if (ratelimit_zone != NULL)
106744bfb21SJohn Baldwin 		uma_zdestroy(ratelimit_zone);
107744bfb21SJohn Baldwin }
108744bfb21SJohn Baldwin 
109744bfb21SJohn Baldwin void
110744bfb21SJohn Baldwin cookie_checker_init(struct cookie_checker *cc)
111744bfb21SJohn Baldwin {
112744bfb21SJohn Baldwin 	bzero(cc, sizeof(*cc));
113744bfb21SJohn Baldwin 
114744bfb21SJohn Baldwin 	rw_init(&cc->cc_key_lock, "cookie_checker_key");
115744bfb21SJohn Baldwin 	mtx_init(&cc->cc_secret_mtx, "cookie_checker_secret", NULL, MTX_DEF);
116744bfb21SJohn Baldwin }
117744bfb21SJohn Baldwin 
118744bfb21SJohn Baldwin void
119744bfb21SJohn Baldwin cookie_checker_free(struct cookie_checker *cc)
120744bfb21SJohn Baldwin {
121744bfb21SJohn Baldwin 	rw_destroy(&cc->cc_key_lock);
122744bfb21SJohn Baldwin 	mtx_destroy(&cc->cc_secret_mtx);
123744bfb21SJohn Baldwin 	explicit_bzero(cc, sizeof(*cc));
124744bfb21SJohn Baldwin }
125744bfb21SJohn Baldwin 
126744bfb21SJohn Baldwin void
127744bfb21SJohn Baldwin cookie_checker_update(struct cookie_checker *cc,
128744bfb21SJohn Baldwin     const uint8_t key[COOKIE_INPUT_SIZE])
129744bfb21SJohn Baldwin {
130744bfb21SJohn Baldwin 	rw_wlock(&cc->cc_key_lock);
131744bfb21SJohn Baldwin 	if (key) {
132744bfb21SJohn Baldwin 		precompute_key(cc->cc_mac1_key, key, COOKIE_MAC1_KEY_LABEL);
133744bfb21SJohn Baldwin 		precompute_key(cc->cc_cookie_key, key, COOKIE_COOKIE_KEY_LABEL);
134744bfb21SJohn Baldwin 	} else {
135744bfb21SJohn Baldwin 		bzero(cc->cc_mac1_key, sizeof(cc->cc_mac1_key));
136744bfb21SJohn Baldwin 		bzero(cc->cc_cookie_key, sizeof(cc->cc_cookie_key));
137744bfb21SJohn Baldwin 	}
138744bfb21SJohn Baldwin 	rw_wunlock(&cc->cc_key_lock);
139744bfb21SJohn Baldwin }
140744bfb21SJohn Baldwin 
141744bfb21SJohn Baldwin void
142744bfb21SJohn Baldwin cookie_checker_create_payload(struct cookie_checker *cc,
143744bfb21SJohn Baldwin     struct cookie_macs *macs, uint8_t nonce[COOKIE_NONCE_SIZE],
144744bfb21SJohn Baldwin     uint8_t ecookie[COOKIE_ENCRYPTED_SIZE], struct sockaddr *sa)
145744bfb21SJohn Baldwin {
146744bfb21SJohn Baldwin 	uint8_t cookie[COOKIE_COOKIE_SIZE];
147744bfb21SJohn Baldwin 
148744bfb21SJohn Baldwin 	make_cookie(cc, cookie, sa);
149744bfb21SJohn Baldwin 	arc4random_buf(nonce, COOKIE_NONCE_SIZE);
150744bfb21SJohn Baldwin 
151744bfb21SJohn Baldwin 	rw_rlock(&cc->cc_key_lock);
152744bfb21SJohn Baldwin 	xchacha20poly1305_encrypt(ecookie, cookie, COOKIE_COOKIE_SIZE,
153744bfb21SJohn Baldwin 	    macs->mac1, COOKIE_MAC_SIZE, nonce, cc->cc_cookie_key);
154744bfb21SJohn Baldwin 	rw_runlock(&cc->cc_key_lock);
155744bfb21SJohn Baldwin 
156744bfb21SJohn Baldwin 	explicit_bzero(cookie, sizeof(cookie));
157744bfb21SJohn Baldwin }
158744bfb21SJohn Baldwin 
159744bfb21SJohn Baldwin void
160744bfb21SJohn Baldwin cookie_maker_init(struct cookie_maker *cm, const uint8_t key[COOKIE_INPUT_SIZE])
161744bfb21SJohn Baldwin {
162744bfb21SJohn Baldwin 	bzero(cm, sizeof(*cm));
163744bfb21SJohn Baldwin 	precompute_key(cm->cm_mac1_key, key, COOKIE_MAC1_KEY_LABEL);
164744bfb21SJohn Baldwin 	precompute_key(cm->cm_cookie_key, key, COOKIE_COOKIE_KEY_LABEL);
165744bfb21SJohn Baldwin 	rw_init(&cm->cm_lock, "cookie_maker");
166744bfb21SJohn Baldwin }
167744bfb21SJohn Baldwin 
168744bfb21SJohn Baldwin void
169744bfb21SJohn Baldwin cookie_maker_free(struct cookie_maker *cm)
170744bfb21SJohn Baldwin {
171744bfb21SJohn Baldwin 	rw_destroy(&cm->cm_lock);
172744bfb21SJohn Baldwin 	explicit_bzero(cm, sizeof(*cm));
173744bfb21SJohn Baldwin }
174744bfb21SJohn Baldwin 
175744bfb21SJohn Baldwin int
176744bfb21SJohn Baldwin cookie_maker_consume_payload(struct cookie_maker *cm,
177744bfb21SJohn Baldwin     uint8_t nonce[COOKIE_NONCE_SIZE], uint8_t ecookie[COOKIE_ENCRYPTED_SIZE])
178744bfb21SJohn Baldwin {
179744bfb21SJohn Baldwin 	uint8_t cookie[COOKIE_COOKIE_SIZE];
180744bfb21SJohn Baldwin 	int ret;
181744bfb21SJohn Baldwin 
182744bfb21SJohn Baldwin 	rw_rlock(&cm->cm_lock);
183744bfb21SJohn Baldwin 	if (!cm->cm_mac1_sent) {
184744bfb21SJohn Baldwin 		ret = ETIMEDOUT;
185744bfb21SJohn Baldwin 		goto error;
186744bfb21SJohn Baldwin 	}
187744bfb21SJohn Baldwin 
188744bfb21SJohn Baldwin 	if (!xchacha20poly1305_decrypt(cookie, ecookie, COOKIE_ENCRYPTED_SIZE,
189744bfb21SJohn Baldwin 	    cm->cm_mac1_last, COOKIE_MAC_SIZE, nonce, cm->cm_cookie_key)) {
190744bfb21SJohn Baldwin 		ret = EINVAL;
191744bfb21SJohn Baldwin 		goto error;
192744bfb21SJohn Baldwin 	}
193744bfb21SJohn Baldwin 	rw_runlock(&cm->cm_lock);
194744bfb21SJohn Baldwin 
195744bfb21SJohn Baldwin 	rw_wlock(&cm->cm_lock);
196744bfb21SJohn Baldwin 	memcpy(cm->cm_cookie, cookie, COOKIE_COOKIE_SIZE);
197744bfb21SJohn Baldwin 	cm->cm_cookie_birthdate = getsbinuptime();
198744bfb21SJohn Baldwin 	cm->cm_cookie_valid = true;
199744bfb21SJohn Baldwin 	cm->cm_mac1_sent = false;
200744bfb21SJohn Baldwin 	rw_wunlock(&cm->cm_lock);
201744bfb21SJohn Baldwin 
202744bfb21SJohn Baldwin 	return 0;
203744bfb21SJohn Baldwin error:
204744bfb21SJohn Baldwin 	rw_runlock(&cm->cm_lock);
205744bfb21SJohn Baldwin 	return ret;
206744bfb21SJohn Baldwin }
207744bfb21SJohn Baldwin 
208744bfb21SJohn Baldwin void
209744bfb21SJohn Baldwin cookie_maker_mac(struct cookie_maker *cm, struct cookie_macs *macs, void *buf,
210744bfb21SJohn Baldwin     size_t len)
211744bfb21SJohn Baldwin {
212744bfb21SJohn Baldwin 	rw_wlock(&cm->cm_lock);
213744bfb21SJohn Baldwin 	macs_mac1(macs, buf, len, cm->cm_mac1_key);
214744bfb21SJohn Baldwin 	memcpy(cm->cm_mac1_last, macs->mac1, COOKIE_MAC_SIZE);
215744bfb21SJohn Baldwin 	cm->cm_mac1_sent = true;
216744bfb21SJohn Baldwin 
217744bfb21SJohn Baldwin 	if (cm->cm_cookie_valid &&
218744bfb21SJohn Baldwin 	    !timer_expired(cm->cm_cookie_birthdate,
219744bfb21SJohn Baldwin 	    COOKIE_SECRET_MAX_AGE - COOKIE_SECRET_LATENCY, 0)) {
220744bfb21SJohn Baldwin 		macs_mac2(macs, buf, len, cm->cm_cookie);
221744bfb21SJohn Baldwin 	} else {
222744bfb21SJohn Baldwin 		bzero(macs->mac2, COOKIE_MAC_SIZE);
223744bfb21SJohn Baldwin 		cm->cm_cookie_valid = false;
224744bfb21SJohn Baldwin 	}
225744bfb21SJohn Baldwin 	rw_wunlock(&cm->cm_lock);
226744bfb21SJohn Baldwin }
227744bfb21SJohn Baldwin 
228744bfb21SJohn Baldwin int
229744bfb21SJohn Baldwin cookie_checker_validate_macs(struct cookie_checker *cc, struct cookie_macs *macs,
230744bfb21SJohn Baldwin     void *buf, size_t len, bool check_cookie, struct sockaddr *sa, struct vnet *vnet)
231744bfb21SJohn Baldwin {
232744bfb21SJohn Baldwin 	struct cookie_macs our_macs;
233744bfb21SJohn Baldwin 	uint8_t cookie[COOKIE_COOKIE_SIZE];
234744bfb21SJohn Baldwin 
235744bfb21SJohn Baldwin 	/* Validate incoming MACs */
236744bfb21SJohn Baldwin 	rw_rlock(&cc->cc_key_lock);
237744bfb21SJohn Baldwin 	macs_mac1(&our_macs, buf, len, cc->cc_mac1_key);
238744bfb21SJohn Baldwin 	rw_runlock(&cc->cc_key_lock);
239744bfb21SJohn Baldwin 
240744bfb21SJohn Baldwin 	/* If mac1 is invald, we want to drop the packet */
241744bfb21SJohn Baldwin 	if (timingsafe_bcmp(our_macs.mac1, macs->mac1, COOKIE_MAC_SIZE) != 0)
242744bfb21SJohn Baldwin 		return EINVAL;
243744bfb21SJohn Baldwin 
244744bfb21SJohn Baldwin 	if (check_cookie) {
245744bfb21SJohn Baldwin 		make_cookie(cc, cookie, sa);
246744bfb21SJohn Baldwin 		macs_mac2(&our_macs, buf, len, cookie);
247744bfb21SJohn Baldwin 
248744bfb21SJohn Baldwin 		/* If the mac2 is invalid, we want to send a cookie response */
249744bfb21SJohn Baldwin 		if (timingsafe_bcmp(our_macs.mac2, macs->mac2, COOKIE_MAC_SIZE) != 0)
250744bfb21SJohn Baldwin 			return EAGAIN;
251744bfb21SJohn Baldwin 
252744bfb21SJohn Baldwin 		/* If the mac2 is valid, we may want rate limit the peer.
253744bfb21SJohn Baldwin 		 * ratelimit_allow will return either 0 or ECONNREFUSED,
254744bfb21SJohn Baldwin 		 * implying there is no ratelimiting, or we should ratelimit
255744bfb21SJohn Baldwin 		 * (refuse) respectively. */
256744bfb21SJohn Baldwin 		if (sa->sa_family == AF_INET)
257744bfb21SJohn Baldwin 			return ratelimit_allow(&ratelimit_v4, sa, vnet);
258744bfb21SJohn Baldwin #ifdef INET6
259744bfb21SJohn Baldwin 		else if (sa->sa_family == AF_INET6)
260744bfb21SJohn Baldwin 			return ratelimit_allow(&ratelimit_v6, sa, vnet);
261744bfb21SJohn Baldwin #endif
262744bfb21SJohn Baldwin 		else
263744bfb21SJohn Baldwin 			return EAFNOSUPPORT;
264744bfb21SJohn Baldwin 	}
265744bfb21SJohn Baldwin 
266744bfb21SJohn Baldwin 	return 0;
267744bfb21SJohn Baldwin }
268744bfb21SJohn Baldwin 
269744bfb21SJohn Baldwin /* Private functions */
270744bfb21SJohn Baldwin static void
271744bfb21SJohn Baldwin precompute_key(uint8_t *key, const uint8_t input[COOKIE_INPUT_SIZE],
272744bfb21SJohn Baldwin     const char *label)
273744bfb21SJohn Baldwin {
274744bfb21SJohn Baldwin 	struct blake2s_state blake;
275744bfb21SJohn Baldwin 	blake2s_init(&blake, COOKIE_KEY_SIZE);
276744bfb21SJohn Baldwin 	blake2s_update(&blake, label, strlen(label));
277744bfb21SJohn Baldwin 	blake2s_update(&blake, input, COOKIE_INPUT_SIZE);
278744bfb21SJohn Baldwin 	blake2s_final(&blake, key);
279744bfb21SJohn Baldwin }
280744bfb21SJohn Baldwin 
281744bfb21SJohn Baldwin static void
282744bfb21SJohn Baldwin macs_mac1(struct cookie_macs *macs, const void *buf, size_t len,
283744bfb21SJohn Baldwin     const uint8_t key[COOKIE_KEY_SIZE])
284744bfb21SJohn Baldwin {
285744bfb21SJohn Baldwin 	struct blake2s_state state;
286744bfb21SJohn Baldwin 	blake2s_init_key(&state, COOKIE_MAC_SIZE, key, COOKIE_KEY_SIZE);
287744bfb21SJohn Baldwin 	blake2s_update(&state, buf, len);
288744bfb21SJohn Baldwin 	blake2s_final(&state, macs->mac1);
289744bfb21SJohn Baldwin }
290744bfb21SJohn Baldwin 
291744bfb21SJohn Baldwin static void
292744bfb21SJohn Baldwin macs_mac2(struct cookie_macs *macs, const void *buf, size_t len,
293744bfb21SJohn Baldwin     const uint8_t key[COOKIE_COOKIE_SIZE])
294744bfb21SJohn Baldwin {
295744bfb21SJohn Baldwin 	struct blake2s_state state;
296744bfb21SJohn Baldwin 	blake2s_init_key(&state, COOKIE_MAC_SIZE, key, COOKIE_COOKIE_SIZE);
297744bfb21SJohn Baldwin 	blake2s_update(&state, buf, len);
298744bfb21SJohn Baldwin 	blake2s_update(&state, macs->mac1, COOKIE_MAC_SIZE);
299744bfb21SJohn Baldwin 	blake2s_final(&state, macs->mac2);
300744bfb21SJohn Baldwin }
301744bfb21SJohn Baldwin 
302744bfb21SJohn Baldwin static __inline int
303744bfb21SJohn Baldwin timer_expired(sbintime_t timer, uint32_t sec, uint32_t nsec)
304744bfb21SJohn Baldwin {
305744bfb21SJohn Baldwin 	sbintime_t now = getsbinuptime();
306744bfb21SJohn Baldwin 	return (now > (timer + sec * SBT_1S + nstosbt(nsec))) ? ETIMEDOUT : 0;
307744bfb21SJohn Baldwin }
308744bfb21SJohn Baldwin 
309744bfb21SJohn Baldwin static void
310744bfb21SJohn Baldwin make_cookie(struct cookie_checker *cc, uint8_t cookie[COOKIE_COOKIE_SIZE],
311744bfb21SJohn Baldwin     struct sockaddr *sa)
312744bfb21SJohn Baldwin {
313744bfb21SJohn Baldwin 	struct blake2s_state state;
314744bfb21SJohn Baldwin 
315744bfb21SJohn Baldwin 	mtx_lock(&cc->cc_secret_mtx);
316744bfb21SJohn Baldwin 	if (timer_expired(cc->cc_secret_birthdate,
317744bfb21SJohn Baldwin 	    COOKIE_SECRET_MAX_AGE, 0)) {
318744bfb21SJohn Baldwin 		arc4random_buf(cc->cc_secret, COOKIE_SECRET_SIZE);
319744bfb21SJohn Baldwin 		cc->cc_secret_birthdate = getsbinuptime();
320744bfb21SJohn Baldwin 	}
321744bfb21SJohn Baldwin 	blake2s_init_key(&state, COOKIE_COOKIE_SIZE, cc->cc_secret,
322744bfb21SJohn Baldwin 	    COOKIE_SECRET_SIZE);
323744bfb21SJohn Baldwin 	mtx_unlock(&cc->cc_secret_mtx);
324744bfb21SJohn Baldwin 
325744bfb21SJohn Baldwin 	if (sa->sa_family == AF_INET) {
326744bfb21SJohn Baldwin 		blake2s_update(&state, (uint8_t *)&satosin(sa)->sin_addr,
327744bfb21SJohn Baldwin 				sizeof(struct in_addr));
328744bfb21SJohn Baldwin 		blake2s_update(&state, (uint8_t *)&satosin(sa)->sin_port,
329744bfb21SJohn Baldwin 				sizeof(in_port_t));
330744bfb21SJohn Baldwin 		blake2s_final(&state, cookie);
331744bfb21SJohn Baldwin #ifdef INET6
332744bfb21SJohn Baldwin 	} else if (sa->sa_family == AF_INET6) {
333744bfb21SJohn Baldwin 		blake2s_update(&state, (uint8_t *)&satosin6(sa)->sin6_addr,
334744bfb21SJohn Baldwin 				sizeof(struct in6_addr));
335744bfb21SJohn Baldwin 		blake2s_update(&state, (uint8_t *)&satosin6(sa)->sin6_port,
336744bfb21SJohn Baldwin 				sizeof(in_port_t));
337744bfb21SJohn Baldwin 		blake2s_final(&state, cookie);
338744bfb21SJohn Baldwin #endif
339744bfb21SJohn Baldwin 	} else {
340744bfb21SJohn Baldwin 		arc4random_buf(cookie, COOKIE_COOKIE_SIZE);
341744bfb21SJohn Baldwin 	}
342744bfb21SJohn Baldwin }
343744bfb21SJohn Baldwin 
344744bfb21SJohn Baldwin static void
345744bfb21SJohn Baldwin ratelimit_init(struct ratelimit *rl)
346744bfb21SJohn Baldwin {
347744bfb21SJohn Baldwin 	size_t i;
348744bfb21SJohn Baldwin 	mtx_init(&rl->rl_mtx, "ratelimit_lock", NULL, MTX_DEF);
349744bfb21SJohn Baldwin 	callout_init_mtx(&rl->rl_gc, &rl->rl_mtx, 0);
350744bfb21SJohn Baldwin 	arc4random_buf(rl->rl_secret, sizeof(rl->rl_secret));
351744bfb21SJohn Baldwin 	for (i = 0; i < RATELIMIT_SIZE; i++)
352744bfb21SJohn Baldwin 		LIST_INIT(&rl->rl_table[i]);
353744bfb21SJohn Baldwin 	rl->rl_table_num = 0;
354b08ee10cSKyle Evans 	rl->rl_initialized = true;
355744bfb21SJohn Baldwin }
356744bfb21SJohn Baldwin 
357744bfb21SJohn Baldwin static void
358744bfb21SJohn Baldwin ratelimit_deinit(struct ratelimit *rl)
359744bfb21SJohn Baldwin {
360b08ee10cSKyle Evans 	if (!rl->rl_initialized)
361b08ee10cSKyle Evans 		return;
362744bfb21SJohn Baldwin 	mtx_lock(&rl->rl_mtx);
363744bfb21SJohn Baldwin 	callout_stop(&rl->rl_gc);
364744bfb21SJohn Baldwin 	ratelimit_gc(rl, true);
365744bfb21SJohn Baldwin 	mtx_unlock(&rl->rl_mtx);
366744bfb21SJohn Baldwin 	mtx_destroy(&rl->rl_mtx);
367b08ee10cSKyle Evans 
368b08ee10cSKyle Evans 	rl->rl_initialized = false;
369744bfb21SJohn Baldwin }
370744bfb21SJohn Baldwin 
371744bfb21SJohn Baldwin static void
372744bfb21SJohn Baldwin ratelimit_gc_callout(void *_rl)
373744bfb21SJohn Baldwin {
374744bfb21SJohn Baldwin 	/* callout will lock rl_mtx for us */
375744bfb21SJohn Baldwin 	ratelimit_gc(_rl, false);
376744bfb21SJohn Baldwin }
377744bfb21SJohn Baldwin 
378744bfb21SJohn Baldwin static void
379744bfb21SJohn Baldwin ratelimit_gc_schedule(struct ratelimit *rl)
380744bfb21SJohn Baldwin {
381744bfb21SJohn Baldwin 	/* Trigger another GC if needed. There is no point calling GC if there
382744bfb21SJohn Baldwin 	 * are no entries in the table. We also want to ensure that GC occurs
383744bfb21SJohn Baldwin 	 * on a regular interval, so don't override a currently pending GC.
384744bfb21SJohn Baldwin 	 *
385744bfb21SJohn Baldwin 	 * In the case of a forced ratelimit_gc, there will be no entries left
386744bfb21SJohn Baldwin 	 * so we will will not schedule another GC. */
387744bfb21SJohn Baldwin 	if (rl->rl_table_num > 0 && !callout_pending(&rl->rl_gc))
388744bfb21SJohn Baldwin 		callout_reset(&rl->rl_gc, ELEMENT_TIMEOUT * hz,
389744bfb21SJohn Baldwin 		    ratelimit_gc_callout, rl);
390744bfb21SJohn Baldwin }
391744bfb21SJohn Baldwin 
392744bfb21SJohn Baldwin static void
393744bfb21SJohn Baldwin ratelimit_gc(struct ratelimit *rl, bool force)
394744bfb21SJohn Baldwin {
395744bfb21SJohn Baldwin 	size_t i;
396744bfb21SJohn Baldwin 	struct ratelimit_entry *r, *tr;
397744bfb21SJohn Baldwin 	sbintime_t expiry;
398744bfb21SJohn Baldwin 
399744bfb21SJohn Baldwin 	mtx_assert(&rl->rl_mtx, MA_OWNED);
400744bfb21SJohn Baldwin 
401744bfb21SJohn Baldwin 	if (rl->rl_table_num == 0)
402744bfb21SJohn Baldwin 		return;
403744bfb21SJohn Baldwin 
404744bfb21SJohn Baldwin 	expiry = getsbinuptime() - ELEMENT_TIMEOUT * SBT_1S;
405744bfb21SJohn Baldwin 
406744bfb21SJohn Baldwin 	for (i = 0; i < RATELIMIT_SIZE; i++) {
407744bfb21SJohn Baldwin 		LIST_FOREACH_SAFE(r, &rl->rl_table[i], r_entry, tr) {
408744bfb21SJohn Baldwin 			if (r->r_last_time < expiry || force) {
409744bfb21SJohn Baldwin 				rl->rl_table_num--;
410744bfb21SJohn Baldwin 				LIST_REMOVE(r, r_entry);
411744bfb21SJohn Baldwin 				uma_zfree(ratelimit_zone, r);
412744bfb21SJohn Baldwin 			}
413744bfb21SJohn Baldwin 		}
414744bfb21SJohn Baldwin 	}
415744bfb21SJohn Baldwin 
416744bfb21SJohn Baldwin 	ratelimit_gc_schedule(rl);
417744bfb21SJohn Baldwin }
418744bfb21SJohn Baldwin 
419744bfb21SJohn Baldwin static int
420744bfb21SJohn Baldwin ratelimit_allow(struct ratelimit *rl, struct sockaddr *sa, struct vnet *vnet)
421744bfb21SJohn Baldwin {
422744bfb21SJohn Baldwin 	uint64_t bucket, tokens;
423744bfb21SJohn Baldwin 	sbintime_t diff, now;
424744bfb21SJohn Baldwin 	struct ratelimit_entry *r;
425744bfb21SJohn Baldwin 	int ret = ECONNREFUSED;
426744bfb21SJohn Baldwin 	struct ratelimit_key key = { .vnet = vnet };
427744bfb21SJohn Baldwin 	size_t len = sizeof(key);
428744bfb21SJohn Baldwin 
429744bfb21SJohn Baldwin 	if (sa->sa_family == AF_INET) {
430744bfb21SJohn Baldwin 		memcpy(key.ip, &satosin(sa)->sin_addr, IPV4_MASK_SIZE);
431744bfb21SJohn Baldwin 		len -= IPV6_MASK_SIZE - IPV4_MASK_SIZE;
432744bfb21SJohn Baldwin 	}
433744bfb21SJohn Baldwin #ifdef INET6
434744bfb21SJohn Baldwin 	else if (sa->sa_family == AF_INET6)
435744bfb21SJohn Baldwin 		memcpy(key.ip, &satosin6(sa)->sin6_addr, IPV6_MASK_SIZE);
436744bfb21SJohn Baldwin #endif
437744bfb21SJohn Baldwin 	else
438744bfb21SJohn Baldwin 		return ret;
439744bfb21SJohn Baldwin 
440744bfb21SJohn Baldwin 	bucket = siphash13(rl->rl_secret, &key, len) & RATELIMIT_MASK;
441744bfb21SJohn Baldwin 	mtx_lock(&rl->rl_mtx);
442744bfb21SJohn Baldwin 
443744bfb21SJohn Baldwin 	LIST_FOREACH(r, &rl->rl_table[bucket], r_entry) {
444744bfb21SJohn Baldwin 		if (bcmp(&r->r_key, &key, len) != 0)
445744bfb21SJohn Baldwin 			continue;
446744bfb21SJohn Baldwin 
447744bfb21SJohn Baldwin 		/* If we get to here, we've found an entry for the endpoint.
448744bfb21SJohn Baldwin 		 * We apply standard token bucket, by calculating the time
449744bfb21SJohn Baldwin 		 * lapsed since our last_time, adding that, ensuring that we
450744bfb21SJohn Baldwin 		 * cap the tokens at TOKEN_MAX. If the endpoint has no tokens
451744bfb21SJohn Baldwin 		 * left (that is tokens <= INITIATION_COST) then we block the
452744bfb21SJohn Baldwin 		 * request, otherwise we subtract the INITITIATION_COST and
453744bfb21SJohn Baldwin 		 * return OK. */
454744bfb21SJohn Baldwin 		now = getsbinuptime();
455744bfb21SJohn Baldwin 		diff = now - r->r_last_time;
456744bfb21SJohn Baldwin 		r->r_last_time = now;
457744bfb21SJohn Baldwin 
458744bfb21SJohn Baldwin 		tokens = r->r_tokens + diff;
459744bfb21SJohn Baldwin 
460744bfb21SJohn Baldwin 		if (tokens > TOKEN_MAX)
461744bfb21SJohn Baldwin 			tokens = TOKEN_MAX;
462744bfb21SJohn Baldwin 
463744bfb21SJohn Baldwin 		if (tokens >= INITIATION_COST) {
464744bfb21SJohn Baldwin 			r->r_tokens = tokens - INITIATION_COST;
465744bfb21SJohn Baldwin 			goto ok;
466744bfb21SJohn Baldwin 		} else {
467744bfb21SJohn Baldwin 			r->r_tokens = tokens;
468744bfb21SJohn Baldwin 			goto error;
469744bfb21SJohn Baldwin 		}
470744bfb21SJohn Baldwin 	}
471744bfb21SJohn Baldwin 
472744bfb21SJohn Baldwin 	/* If we get to here, we didn't have an entry for the endpoint, let's
473744bfb21SJohn Baldwin 	 * add one if we have space. */
474744bfb21SJohn Baldwin 	if (rl->rl_table_num >= RATELIMIT_SIZE_MAX)
475744bfb21SJohn Baldwin 		goto error;
476744bfb21SJohn Baldwin 
477744bfb21SJohn Baldwin 	/* Goto error if out of memory */
478744bfb21SJohn Baldwin 	if ((r = uma_zalloc(ratelimit_zone, M_NOWAIT | M_ZERO)) == NULL)
479744bfb21SJohn Baldwin 		goto error;
480744bfb21SJohn Baldwin 
481744bfb21SJohn Baldwin 	rl->rl_table_num++;
482744bfb21SJohn Baldwin 
483744bfb21SJohn Baldwin 	/* Insert entry into the hashtable and ensure it's initialised */
484744bfb21SJohn Baldwin 	LIST_INSERT_HEAD(&rl->rl_table[bucket], r, r_entry);
485744bfb21SJohn Baldwin 	r->r_key = key;
486744bfb21SJohn Baldwin 	r->r_last_time = getsbinuptime();
487744bfb21SJohn Baldwin 	r->r_tokens = TOKEN_MAX - INITIATION_COST;
488744bfb21SJohn Baldwin 
489744bfb21SJohn Baldwin 	/* If we've added a new entry, let's trigger GC. */
490744bfb21SJohn Baldwin 	ratelimit_gc_schedule(rl);
491744bfb21SJohn Baldwin ok:
492744bfb21SJohn Baldwin 	ret = 0;
493744bfb21SJohn Baldwin error:
494744bfb21SJohn Baldwin 	mtx_unlock(&rl->rl_mtx);
495744bfb21SJohn Baldwin 	return ret;
496744bfb21SJohn Baldwin }
497744bfb21SJohn Baldwin 
498744bfb21SJohn Baldwin static uint64_t siphash13(const uint8_t key[SIPHASH_KEY_LENGTH], const void *src, size_t len)
499744bfb21SJohn Baldwin {
500744bfb21SJohn Baldwin 	SIPHASH_CTX ctx;
501744bfb21SJohn Baldwin 	return (SipHashX(&ctx, 1, 3, key, src, len));
502744bfb21SJohn Baldwin }
503744bfb21SJohn Baldwin 
504744bfb21SJohn Baldwin #ifdef SELFTESTS
505744bfb21SJohn Baldwin #include "selftest/cookie.c"
506744bfb21SJohn Baldwin #endif /* SELFTESTS */
507