xref: /dflybsd-src/sys/net/wg/wg_cookie.c (revision a364ee0447e82cb000a54bd99eebb79225dd8616)
1*a364ee04SAaron LI /*-
2*a364ee04SAaron LI  * SPDX-License-Identifier: ISC
3a6bca3d2SAaron LI  *
4a6bca3d2SAaron LI  * Copyright (C) 2015-2021 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
5a6bca3d2SAaron LI  * Copyright (C) 2019-2021 Matt Dunwoodie <ncon@noconroy.net>
6*a364ee04SAaron LI  *
7*a364ee04SAaron LI  * Permission to use, copy, modify, and distribute this software for any
8*a364ee04SAaron LI  * purpose with or without fee is hereby granted, provided that the above
9*a364ee04SAaron LI  * copyright notice and this permission notice appear in all copies.
10*a364ee04SAaron LI  *
11*a364ee04SAaron LI  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12*a364ee04SAaron LI  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13*a364ee04SAaron LI  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14*a364ee04SAaron LI  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15*a364ee04SAaron LI  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16*a364ee04SAaron LI  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17*a364ee04SAaron LI  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18a6bca3d2SAaron LI  */
19a6bca3d2SAaron LI 
20a6bca3d2SAaron LI #include "opt_inet.h"
21a6bca3d2SAaron LI #include "opt_inet6.h"
22a6bca3d2SAaron LI 
23a6bca3d2SAaron LI #include <sys/param.h>
24a6bca3d2SAaron LI #include <sys/systm.h>
2564544dd5SAaron LI #include <sys/callout.h>
26a6bca3d2SAaron LI #include <sys/kernel.h>
27a6bca3d2SAaron LI #include <sys/lock.h>
2864544dd5SAaron LI #include <sys/malloc.h>
29be332237SAaron LI #include <sys/objcache.h>
301fdfe5a9SAaron LI #include <sys/queue.h>
31a6bca3d2SAaron LI #include <sys/socket.h>
322bed72b3SAaron LI #include <sys/time.h>
33a6bca3d2SAaron LI #include <netinet/in.h>
34a6bca3d2SAaron LI 
35cfdd69bcSAaron LI #include <crypto/chachapoly.h>
36d94868ddSAaron LI #include <crypto/blake2/blake2s.h>
37d94868ddSAaron LI #include <crypto/siphash/siphash.h>
38d94868ddSAaron LI 
39a6bca3d2SAaron LI #include "wg_cookie.h"
40a6bca3d2SAaron LI 
41a6bca3d2SAaron LI #define COOKIE_MAC1_KEY_LABEL	"mac1----"
42a6bca3d2SAaron LI #define COOKIE_COOKIE_KEY_LABEL	"cookie--"
43a6bca3d2SAaron LI #define COOKIE_SECRET_MAX_AGE	120
44a6bca3d2SAaron LI #define COOKIE_SECRET_LATENCY	5
45a6bca3d2SAaron LI 
46a6bca3d2SAaron LI /* Constants for initiation rate limiting */
47a6bca3d2SAaron LI #define RATELIMIT_SIZE		(1 << 13)
48a6bca3d2SAaron LI #define RATELIMIT_MASK		(RATELIMIT_SIZE - 1)
49a6bca3d2SAaron LI #define RATELIMIT_SIZE_MAX	(RATELIMIT_SIZE * 8)
502bed72b3SAaron LI #define NSEC_PER_SEC		1000000000LL
51a6bca3d2SAaron LI #define INITIATIONS_PER_SECOND	20
52a6bca3d2SAaron LI #define INITIATIONS_BURSTABLE	5
532bed72b3SAaron LI #define INITIATION_COST		(NSEC_PER_SEC / INITIATIONS_PER_SECOND)
54a6bca3d2SAaron LI #define TOKEN_MAX		(INITIATION_COST * INITIATIONS_BURSTABLE)
551ef0d803SAaron LI #define ELEMENT_TIMEOUT		1 /* second */
56a6bca3d2SAaron LI #define IPV4_MASK_SIZE		4 /* Use all 4 bytes of IPv4 address */
57a6bca3d2SAaron LI #define IPV6_MASK_SIZE		8 /* Use top 8 bytes (/64) of IPv6 address */
58a6bca3d2SAaron LI 
59a6bca3d2SAaron LI struct ratelimit_key {
60a6bca3d2SAaron LI 	uint8_t ip[IPV6_MASK_SIZE];
61a6bca3d2SAaron LI };
62a6bca3d2SAaron LI 
63a6bca3d2SAaron LI struct ratelimit_entry {
64a6bca3d2SAaron LI 	LIST_ENTRY(ratelimit_entry)	r_entry;
65a6bca3d2SAaron LI 	struct ratelimit_key		r_key;
662bed72b3SAaron LI 	struct timespec			r_last_time;	/* nanouptime */
67a6bca3d2SAaron LI 	uint64_t			r_tokens;
68a6bca3d2SAaron LI };
69a6bca3d2SAaron LI 
70a6bca3d2SAaron LI struct ratelimit {
71a6bca3d2SAaron LI 	uint8_t				rl_secret[SIPHASH_KEY_LENGTH];
72139109cdSAaron LI 	struct lock			rl_mtx;
73a6bca3d2SAaron LI 	struct callout			rl_gc;
74a6bca3d2SAaron LI 	LIST_HEAD(, ratelimit_entry)	rl_table[RATELIMIT_SIZE];
75a6bca3d2SAaron LI 	size_t				rl_table_num;
76a6bca3d2SAaron LI 	bool				rl_initialized;
77a6bca3d2SAaron LI };
78a6bca3d2SAaron LI 
791ef0d803SAaron LI 
80a6bca3d2SAaron LI static void	macs_mac1(struct cookie_macs *, const void *, size_t,
81a6bca3d2SAaron LI 			  const uint8_t[COOKIE_KEY_SIZE]);
82a6bca3d2SAaron LI static void	macs_mac2(struct cookie_macs *, const void *, size_t,
83a6bca3d2SAaron LI 			  const uint8_t[COOKIE_COOKIE_SIZE]);
84a6bca3d2SAaron LI static void	make_cookie(struct cookie_checker *,
851ef0d803SAaron LI 			    uint8_t[COOKIE_COOKIE_SIZE],
861ef0d803SAaron LI 			    const struct sockaddr *);
871ef0d803SAaron LI static void	precompute_key(uint8_t[COOKIE_KEY_SIZE],
881ef0d803SAaron LI 			       const uint8_t[COOKIE_INPUT_SIZE], const char *);
89a6bca3d2SAaron LI static void	ratelimit_init(struct ratelimit *);
90a6bca3d2SAaron LI static void	ratelimit_deinit(struct ratelimit *);
91a6bca3d2SAaron LI static void	ratelimit_gc_callout(void *);
92a6bca3d2SAaron LI static void	ratelimit_gc_schedule(struct ratelimit *);
93a6bca3d2SAaron LI static void	ratelimit_gc(struct ratelimit *, bool);
941ef0d803SAaron LI static int	ratelimit_allow(struct ratelimit *, const struct sockaddr *);
951ef0d803SAaron LI 
96a6bca3d2SAaron LI 
97a6bca3d2SAaron LI static struct ratelimit ratelimit_v4;
98a6bca3d2SAaron LI #ifdef INET6
99a6bca3d2SAaron LI static struct ratelimit ratelimit_v6;
100a6bca3d2SAaron LI #endif
101be332237SAaron LI 
102be332237SAaron LI static struct objcache *ratelimit_zone;
1031ef0d803SAaron LI static MALLOC_DEFINE(M_WG_RATELIMIT, "WG ratelimit", "wireguard ratelimit");
104a6bca3d2SAaron LI 
1051ef0d803SAaron LI 
1061ef0d803SAaron LI static inline uint64_t
1071ef0d803SAaron LI siphash13(const uint8_t key[SIPHASH_KEY_LENGTH], const void *src, size_t len)
1081ef0d803SAaron LI {
1091ef0d803SAaron LI 	SIPHASH_CTX ctx;
1101ef0d803SAaron LI 	return SipHashX(&ctx, 1, 3, key, src, len);
1111ef0d803SAaron LI }
1121ef0d803SAaron LI 
1131ef0d803SAaron LI static inline int
1141ef0d803SAaron LI timer_expired(const struct timespec *birthdate, time_t sec, long nsec)
1151ef0d803SAaron LI {
1161ef0d803SAaron LI 	struct timespec uptime;
1171ef0d803SAaron LI 	struct timespec expire = { .tv_sec = sec, .tv_nsec = nsec };
1181ef0d803SAaron LI 
1191ef0d803SAaron LI 	if (birthdate->tv_sec == 0 && birthdate->tv_nsec == 0)
1201ef0d803SAaron LI 		return (ETIMEDOUT);
1211ef0d803SAaron LI 
1221ef0d803SAaron LI 	getnanouptime(&uptime);
1231ef0d803SAaron LI 	timespecadd(birthdate, &expire, &expire);
1241ef0d803SAaron LI 	return timespeccmp(&uptime, &expire, >) ? ETIMEDOUT : 0;
1251ef0d803SAaron LI }
1261ef0d803SAaron LI 
1271ef0d803SAaron LI /*----------------------------------------------------------------------------*/
128a6bca3d2SAaron LI /* Public Functions */
1291ef0d803SAaron LI 
130a6bca3d2SAaron LI int
131a6bca3d2SAaron LI cookie_init(void)
132a6bca3d2SAaron LI {
1331ef0d803SAaron LI 	ratelimit_zone = objcache_create_simple(
1341ef0d803SAaron LI 	    M_WG_RATELIMIT, sizeof(struct ratelimit_entry));
135be332237SAaron LI 	if (ratelimit_zone == NULL)
1361ef0d803SAaron LI 		return (ENOMEM);
137a6bca3d2SAaron LI 
138a6bca3d2SAaron LI 	ratelimit_init(&ratelimit_v4);
139a6bca3d2SAaron LI #ifdef INET6
140a6bca3d2SAaron LI 	ratelimit_init(&ratelimit_v6);
141a6bca3d2SAaron LI #endif
1421ef0d803SAaron LI 
143a6bca3d2SAaron LI 	return (0);
144a6bca3d2SAaron LI }
145a6bca3d2SAaron LI 
146a6bca3d2SAaron LI void
147a6bca3d2SAaron LI cookie_deinit(void)
148a6bca3d2SAaron LI {
149a6bca3d2SAaron LI 	ratelimit_deinit(&ratelimit_v4);
150a6bca3d2SAaron LI #ifdef INET6
151a6bca3d2SAaron LI 	ratelimit_deinit(&ratelimit_v6);
152a6bca3d2SAaron LI #endif
153a6bca3d2SAaron LI 	if (ratelimit_zone != NULL)
154be332237SAaron LI 		objcache_destroy(ratelimit_zone);
155a6bca3d2SAaron LI }
156a6bca3d2SAaron LI 
157a6bca3d2SAaron LI void
158a6bca3d2SAaron LI cookie_checker_init(struct cookie_checker *cc)
159a6bca3d2SAaron LI {
160a6bca3d2SAaron LI 	bzero(cc, sizeof(*cc));
1617ef217feSAaron LI 	lockinit(&cc->cc_key_lock, "cookie_checker_key", 0, 0);
162139109cdSAaron LI 	lockinit(&cc->cc_secret_mtx, "cookie_checker_secret", 0, 0);
163a6bca3d2SAaron LI }
164a6bca3d2SAaron LI 
165a6bca3d2SAaron LI void
166a6bca3d2SAaron LI cookie_checker_free(struct cookie_checker *cc)
167a6bca3d2SAaron LI {
1687ef217feSAaron LI 	lockuninit(&cc->cc_key_lock);
169139109cdSAaron LI 	lockuninit(&cc->cc_secret_mtx);
170a6bca3d2SAaron LI 	explicit_bzero(cc, sizeof(*cc));
171a6bca3d2SAaron LI }
172a6bca3d2SAaron LI 
173a6bca3d2SAaron LI void
174a6bca3d2SAaron LI cookie_checker_update(struct cookie_checker *cc,
175a6bca3d2SAaron LI 		      const uint8_t key[COOKIE_INPUT_SIZE])
176a6bca3d2SAaron LI {
1777ef217feSAaron LI 	lockmgr(&cc->cc_key_lock, LK_EXCLUSIVE);
1781ef0d803SAaron LI 	if (key != NULL) {
179a6bca3d2SAaron LI 		precompute_key(cc->cc_mac1_key, key, COOKIE_MAC1_KEY_LABEL);
180a6bca3d2SAaron LI 		precompute_key(cc->cc_cookie_key, key, COOKIE_COOKIE_KEY_LABEL);
181a6bca3d2SAaron LI 	} else {
182a6bca3d2SAaron LI 		bzero(cc->cc_mac1_key, sizeof(cc->cc_mac1_key));
183a6bca3d2SAaron LI 		bzero(cc->cc_cookie_key, sizeof(cc->cc_cookie_key));
184a6bca3d2SAaron LI 	}
1857ef217feSAaron LI 	lockmgr(&cc->cc_key_lock, LK_RELEASE);
186a6bca3d2SAaron LI }
187a6bca3d2SAaron LI 
188a6bca3d2SAaron LI void
189a6bca3d2SAaron LI cookie_checker_create_payload(struct cookie_checker *cc,
1901ef0d803SAaron LI 			      const struct cookie_macs *macs,
1911ef0d803SAaron LI 			      uint8_t nonce[COOKIE_NONCE_SIZE],
1921ef0d803SAaron LI 			      uint8_t ecookie[COOKIE_ENCRYPTED_SIZE],
1931ef0d803SAaron LI 			      const struct sockaddr *sa)
194a6bca3d2SAaron LI {
195a6bca3d2SAaron LI 	uint8_t cookie[COOKIE_COOKIE_SIZE];
196a6bca3d2SAaron LI 
197a6bca3d2SAaron LI 	make_cookie(cc, cookie, sa);
198a9b5f3fdSAaron LI 	karc4random_buf(nonce, COOKIE_NONCE_SIZE);
199a6bca3d2SAaron LI 
2007ef217feSAaron LI 	lockmgr(&cc->cc_key_lock, LK_SHARED);
201a6bca3d2SAaron LI 	xchacha20poly1305_encrypt(ecookie, cookie, COOKIE_COOKIE_SIZE,
2021ef0d803SAaron LI 				  macs->mac1, COOKIE_MAC_SIZE, nonce,
2031ef0d803SAaron LI 				  cc->cc_cookie_key);
2047ef217feSAaron LI 	lockmgr(&cc->cc_key_lock, LK_RELEASE);
205a6bca3d2SAaron LI 
206a6bca3d2SAaron LI 	explicit_bzero(cookie, sizeof(cookie));
207a6bca3d2SAaron LI }
208a6bca3d2SAaron LI 
2091ef0d803SAaron LI int
2101ef0d803SAaron LI cookie_checker_validate_macs(struct cookie_checker *cc,
2111ef0d803SAaron LI 			     const struct cookie_macs *macs,
2121ef0d803SAaron LI 			     const void *buf, size_t len, bool check_cookie,
2131ef0d803SAaron LI 			     const struct sockaddr *sa)
2141ef0d803SAaron LI {
2151ef0d803SAaron LI 	struct cookie_macs our_macs;
2161ef0d803SAaron LI 	uint8_t cookie[COOKIE_COOKIE_SIZE];
2171ef0d803SAaron LI 
2181ef0d803SAaron LI 	/* Validate incoming MACs */
2191ef0d803SAaron LI 	lockmgr(&cc->cc_key_lock, LK_SHARED);
2201ef0d803SAaron LI 	macs_mac1(&our_macs, buf, len, cc->cc_mac1_key);
2211ef0d803SAaron LI 	lockmgr(&cc->cc_key_lock, LK_RELEASE);
2221ef0d803SAaron LI 
2231ef0d803SAaron LI 	/* If mac1 is invald, we want to drop the packet */
2241ef0d803SAaron LI 	if (timingsafe_bcmp(our_macs.mac1, macs->mac1, COOKIE_MAC_SIZE) != 0)
2251ef0d803SAaron LI 		return (EINVAL);
2261ef0d803SAaron LI 
2271ef0d803SAaron LI 	if (check_cookie) {
2281ef0d803SAaron LI 		make_cookie(cc, cookie, sa);
2291ef0d803SAaron LI 		macs_mac2(&our_macs, buf, len, cookie);
2301ef0d803SAaron LI 
2311ef0d803SAaron LI 		/* If mac2 is invalid, we want to send a cookie response. */
2321ef0d803SAaron LI 		if (timingsafe_bcmp(our_macs.mac2, macs->mac2, COOKIE_MAC_SIZE)
2331ef0d803SAaron LI 		    != 0)
2341ef0d803SAaron LI 			return (EAGAIN);
2351ef0d803SAaron LI 
2361ef0d803SAaron LI 		/*
2371ef0d803SAaron LI 		 * If the mac2 is valid, we may want to rate limit the peer.
2381ef0d803SAaron LI 		 * ratelimit_allow() will return either 0 or ECONNREFUSED,
2391ef0d803SAaron LI 		 * implying there is no ratelimiting, or we should ratelimit
2401ef0d803SAaron LI 		 * (refuse), respectively.
2411ef0d803SAaron LI 		 */
2421ef0d803SAaron LI 		if (sa->sa_family == AF_INET)
2431ef0d803SAaron LI 			return ratelimit_allow(&ratelimit_v4, sa);
2441ef0d803SAaron LI #ifdef INET6
2451ef0d803SAaron LI 		else if (sa->sa_family == AF_INET6)
2461ef0d803SAaron LI 			return ratelimit_allow(&ratelimit_v6, sa);
2471ef0d803SAaron LI #endif
2481ef0d803SAaron LI 		else
2491ef0d803SAaron LI 			return (EAFNOSUPPORT);
2501ef0d803SAaron LI 	}
2511ef0d803SAaron LI 
2521ef0d803SAaron LI 	return (0);
2531ef0d803SAaron LI }
2541ef0d803SAaron LI 
255a6bca3d2SAaron LI void
256a6bca3d2SAaron LI cookie_maker_init(struct cookie_maker *cm, const uint8_t key[COOKIE_INPUT_SIZE])
257a6bca3d2SAaron LI {
258a6bca3d2SAaron LI 	bzero(cm, sizeof(*cm));
259a6bca3d2SAaron LI 	precompute_key(cm->cm_mac1_key, key, COOKIE_MAC1_KEY_LABEL);
260a6bca3d2SAaron LI 	precompute_key(cm->cm_cookie_key, key, COOKIE_COOKIE_KEY_LABEL);
2617ef217feSAaron LI 	lockinit(&cm->cm_lock, "cookie_maker", 0, 0);
262a6bca3d2SAaron LI }
263a6bca3d2SAaron LI 
264a6bca3d2SAaron LI void
265a6bca3d2SAaron LI cookie_maker_free(struct cookie_maker *cm)
266a6bca3d2SAaron LI {
2677ef217feSAaron LI 	lockuninit(&cm->cm_lock);
268a6bca3d2SAaron LI 	explicit_bzero(cm, sizeof(*cm));
269a6bca3d2SAaron LI }
270a6bca3d2SAaron LI 
271a6bca3d2SAaron LI int
272a6bca3d2SAaron LI cookie_maker_consume_payload(struct cookie_maker *cm,
2731ef0d803SAaron LI 			     const uint8_t nonce[COOKIE_NONCE_SIZE],
2741ef0d803SAaron LI 			     const uint8_t ecookie[COOKIE_ENCRYPTED_SIZE])
275a6bca3d2SAaron LI {
276a6bca3d2SAaron LI 	uint8_t cookie[COOKIE_COOKIE_SIZE];
2771ef0d803SAaron LI 	int ret = 0;
278a6bca3d2SAaron LI 
2797ef217feSAaron LI 	lockmgr(&cm->cm_lock, LK_SHARED);
2801ef0d803SAaron LI 
281a6bca3d2SAaron LI 	if (!cm->cm_mac1_sent) {
282a6bca3d2SAaron LI 		ret = ETIMEDOUT;
2831ef0d803SAaron LI 		goto out;
284a6bca3d2SAaron LI 	}
285a6bca3d2SAaron LI 
286a6bca3d2SAaron LI 	if (!xchacha20poly1305_decrypt(cookie, ecookie, COOKIE_ENCRYPTED_SIZE,
2871ef0d803SAaron LI 				       cm->cm_mac1_last, COOKIE_MAC_SIZE,
2881ef0d803SAaron LI 				       nonce, cm->cm_cookie_key)) {
289a6bca3d2SAaron LI 		ret = EINVAL;
2901ef0d803SAaron LI 		goto out;
291a6bca3d2SAaron LI 	}
292a6bca3d2SAaron LI 
2931ef0d803SAaron LI 	lockmgr(&cm->cm_lock, LK_RELEASE);
2947ef217feSAaron LI 	lockmgr(&cm->cm_lock, LK_EXCLUSIVE);
2951ef0d803SAaron LI 
296a6bca3d2SAaron LI 	memcpy(cm->cm_cookie, cookie, COOKIE_COOKIE_SIZE);
2972bed72b3SAaron LI 	getnanouptime(&cm->cm_cookie_birthdate);
298a6bca3d2SAaron LI 	cm->cm_cookie_valid = true;
299a6bca3d2SAaron LI 	cm->cm_mac1_sent = false;
300a6bca3d2SAaron LI 
3011ef0d803SAaron LI out:
3027ef217feSAaron LI 	lockmgr(&cm->cm_lock, LK_RELEASE);
3031ef0d803SAaron LI 	return (ret);
304a6bca3d2SAaron LI }
305a6bca3d2SAaron LI 
306a6bca3d2SAaron LI void
3071ef0d803SAaron LI cookie_maker_mac(struct cookie_maker *cm, struct cookie_macs *macs,
3081ef0d803SAaron LI 		 const void *buf, size_t len)
309a6bca3d2SAaron LI {
3107ef217feSAaron LI 	lockmgr(&cm->cm_lock, LK_EXCLUSIVE);
3111ef0d803SAaron LI 
312a6bca3d2SAaron LI 	macs_mac1(macs, buf, len, cm->cm_mac1_key);
313a6bca3d2SAaron LI 	memcpy(cm->cm_mac1_last, macs->mac1, COOKIE_MAC_SIZE);
314a6bca3d2SAaron LI 	cm->cm_mac1_sent = true;
315a6bca3d2SAaron LI 
316a6bca3d2SAaron LI 	if (cm->cm_cookie_valid &&
3172bed72b3SAaron LI 	    !timer_expired(&cm->cm_cookie_birthdate,
318a6bca3d2SAaron LI 			   COOKIE_SECRET_MAX_AGE - COOKIE_SECRET_LATENCY, 0)) {
319a6bca3d2SAaron LI 		macs_mac2(macs, buf, len, cm->cm_cookie);
320a6bca3d2SAaron LI 	} else {
321a6bca3d2SAaron LI 		bzero(macs->mac2, COOKIE_MAC_SIZE);
322a6bca3d2SAaron LI 		cm->cm_cookie_valid = false;
323a6bca3d2SAaron LI 	}
3241ef0d803SAaron LI 
3257ef217feSAaron LI 	lockmgr(&cm->cm_lock, LK_RELEASE);
326a6bca3d2SAaron LI }
327a6bca3d2SAaron LI 
3281ef0d803SAaron LI /*----------------------------------------------------------------------------*/
329a6bca3d2SAaron LI /* Private functions */
3301ef0d803SAaron LI 
331a6bca3d2SAaron LI static void
3321ef0d803SAaron LI precompute_key(uint8_t key[COOKIE_KEY_SIZE],
3331ef0d803SAaron LI 	       const uint8_t input[COOKIE_INPUT_SIZE], const char *label)
334a6bca3d2SAaron LI {
335a6bca3d2SAaron LI 	struct blake2s_state blake;
3361ef0d803SAaron LI 
337a6bca3d2SAaron LI 	blake2s_init(&blake, COOKIE_KEY_SIZE);
338a6bca3d2SAaron LI 	blake2s_update(&blake, label, strlen(label));
339a6bca3d2SAaron LI 	blake2s_update(&blake, input, COOKIE_INPUT_SIZE);
340a6bca3d2SAaron LI 	blake2s_final(&blake, key);
341a6bca3d2SAaron LI }
342a6bca3d2SAaron LI 
343a6bca3d2SAaron LI static void
344a6bca3d2SAaron LI macs_mac1(struct cookie_macs *macs, const void *buf, size_t len,
345a6bca3d2SAaron LI 	  const uint8_t key[COOKIE_KEY_SIZE])
346a6bca3d2SAaron LI {
347a6bca3d2SAaron LI 	struct blake2s_state state;
3481ef0d803SAaron LI 
349a6bca3d2SAaron LI 	blake2s_init_key(&state, COOKIE_MAC_SIZE, key, COOKIE_KEY_SIZE);
350a6bca3d2SAaron LI 	blake2s_update(&state, buf, len);
351a6bca3d2SAaron LI 	blake2s_final(&state, macs->mac1);
352a6bca3d2SAaron LI }
353a6bca3d2SAaron LI 
354a6bca3d2SAaron LI static void
355a6bca3d2SAaron LI macs_mac2(struct cookie_macs *macs, const void *buf, size_t len,
356a6bca3d2SAaron LI 	  const uint8_t key[COOKIE_COOKIE_SIZE])
357a6bca3d2SAaron LI {
358a6bca3d2SAaron LI 	struct blake2s_state state;
3591ef0d803SAaron LI 
360a6bca3d2SAaron LI 	blake2s_init_key(&state, COOKIE_MAC_SIZE, key, COOKIE_COOKIE_SIZE);
361a6bca3d2SAaron LI 	blake2s_update(&state, buf, len);
362a6bca3d2SAaron LI 	blake2s_update(&state, macs->mac1, COOKIE_MAC_SIZE);
363a6bca3d2SAaron LI 	blake2s_final(&state, macs->mac2);
364a6bca3d2SAaron LI }
365a6bca3d2SAaron LI 
366a6bca3d2SAaron LI static void
367a6bca3d2SAaron LI make_cookie(struct cookie_checker *cc, uint8_t cookie[COOKIE_COOKIE_SIZE],
3681ef0d803SAaron LI 	    const struct sockaddr *sa)
369a6bca3d2SAaron LI {
370a6bca3d2SAaron LI 	struct blake2s_state state;
371a6bca3d2SAaron LI 
372139109cdSAaron LI 	lockmgr(&cc->cc_secret_mtx, LK_EXCLUSIVE);
3732bed72b3SAaron LI 	if (timer_expired(&cc->cc_secret_birthdate,
374a6bca3d2SAaron LI 			  COOKIE_SECRET_MAX_AGE, 0)) {
375a9b5f3fdSAaron LI 		karc4random_buf(cc->cc_secret, COOKIE_SECRET_SIZE);
3762bed72b3SAaron LI 		getnanouptime(&cc->cc_secret_birthdate);
377a6bca3d2SAaron LI 	}
378a6bca3d2SAaron LI 	blake2s_init_key(&state, COOKIE_COOKIE_SIZE, cc->cc_secret,
379a6bca3d2SAaron LI 			 COOKIE_SECRET_SIZE);
380139109cdSAaron LI 	lockmgr(&cc->cc_secret_mtx, LK_RELEASE);
381a6bca3d2SAaron LI 
382a6bca3d2SAaron LI 	if (sa->sa_family == AF_INET) {
3831ef0d803SAaron LI 		const struct sockaddr_in *sin = (const void *)sa;
3841ef0d803SAaron LI 		blake2s_update(&state, (const uint8_t *)&sin->sin_addr,
3851ef0d803SAaron LI 			       sizeof(sin->sin_addr));
3861ef0d803SAaron LI 		blake2s_update(&state, (const uint8_t *)&sin->sin_port,
3871ef0d803SAaron LI 			       sizeof(sin->sin_port));
388a6bca3d2SAaron LI 		blake2s_final(&state, cookie);
389a6bca3d2SAaron LI #ifdef INET6
390a6bca3d2SAaron LI 	} else if (sa->sa_family == AF_INET6) {
3911ef0d803SAaron LI 		const struct sockaddr_in6 *sin6 = (const void *)sa;
3921ef0d803SAaron LI 		blake2s_update(&state, (const uint8_t *)&sin6->sin6_addr,
3931ef0d803SAaron LI 			       sizeof(sin6->sin6_addr));
3941ef0d803SAaron LI 		blake2s_update(&state, (const uint8_t *)&sin6->sin6_port,
3951ef0d803SAaron LI 			       sizeof(sin6->sin6_port));
396a6bca3d2SAaron LI 		blake2s_final(&state, cookie);
397a6bca3d2SAaron LI #endif
398a6bca3d2SAaron LI 	} else {
399a9b5f3fdSAaron LI 		karc4random_buf(cookie, COOKIE_COOKIE_SIZE);
400a6bca3d2SAaron LI 	}
401a6bca3d2SAaron LI }
402a6bca3d2SAaron LI 
4031ef0d803SAaron LI 
404a6bca3d2SAaron LI static void
405a6bca3d2SAaron LI ratelimit_init(struct ratelimit *rl)
406a6bca3d2SAaron LI {
407a6bca3d2SAaron LI 	size_t i;
4081ef0d803SAaron LI 
4091ef0d803SAaron LI 	bzero(rl, sizeof(*rl));
410139109cdSAaron LI 	lockinit(&rl->rl_mtx, "ratelimit_lock", 0, 0);
411b1d93564SAaron LI 	callout_init_lk(&rl->rl_gc, &rl->rl_mtx);
412a9b5f3fdSAaron LI 	karc4random_buf(rl->rl_secret, sizeof(rl->rl_secret));
413a6bca3d2SAaron LI 	for (i = 0; i < RATELIMIT_SIZE; i++)
414a6bca3d2SAaron LI 		LIST_INIT(&rl->rl_table[i]);
415a6bca3d2SAaron LI 	rl->rl_table_num = 0;
4161ef0d803SAaron LI 
417a6bca3d2SAaron LI 	rl->rl_initialized = true;
418a6bca3d2SAaron LI }
419a6bca3d2SAaron LI 
420a6bca3d2SAaron LI static void
421a6bca3d2SAaron LI ratelimit_deinit(struct ratelimit *rl)
422a6bca3d2SAaron LI {
423a6bca3d2SAaron LI 	if (!rl->rl_initialized)
424a6bca3d2SAaron LI 		return;
4251ef0d803SAaron LI 
426139109cdSAaron LI 	lockmgr(&rl->rl_mtx, LK_EXCLUSIVE);
427a6bca3d2SAaron LI 	callout_stop(&rl->rl_gc);
428a6bca3d2SAaron LI 	ratelimit_gc(rl, true);
429139109cdSAaron LI 	lockmgr(&rl->rl_mtx, LK_RELEASE);
430139109cdSAaron LI 	lockuninit(&rl->rl_mtx);
431a6bca3d2SAaron LI 
432a6bca3d2SAaron LI 	rl->rl_initialized = false;
433a6bca3d2SAaron LI }
434a6bca3d2SAaron LI 
435a6bca3d2SAaron LI static void
436a6bca3d2SAaron LI ratelimit_gc_callout(void *_rl)
437a6bca3d2SAaron LI {
4381ef0d803SAaron LI 	/* callout will lock for us */
439a6bca3d2SAaron LI 	ratelimit_gc(_rl, false);
440a6bca3d2SAaron LI }
441a6bca3d2SAaron LI 
442a6bca3d2SAaron LI static void
443a6bca3d2SAaron LI ratelimit_gc_schedule(struct ratelimit *rl)
444a6bca3d2SAaron LI {
4451ef0d803SAaron LI 	/*
4461ef0d803SAaron LI 	 * Trigger another GC if needed.  There is no point calling GC if
4471ef0d803SAaron LI 	 * there are no entries in the table.  We also want to ensure that
4481ef0d803SAaron LI 	 * GC occurs on a regular interval, so don't override a currently
4491ef0d803SAaron LI 	 * pending GC.
450a6bca3d2SAaron LI 	 *
4511ef0d803SAaron LI 	 * In the case of a forced ratelimit_gc(), there will be no entries
4521ef0d803SAaron LI 	 * left so we will not schedule another GC.
4531ef0d803SAaron LI 	 */
454a6bca3d2SAaron LI 	if (rl->rl_table_num > 0 && !callout_pending(&rl->rl_gc))
455a6bca3d2SAaron LI 		callout_reset(&rl->rl_gc, ELEMENT_TIMEOUT * hz,
456a6bca3d2SAaron LI 			      ratelimit_gc_callout, rl);
457a6bca3d2SAaron LI }
458a6bca3d2SAaron LI 
459a6bca3d2SAaron LI static void
460a6bca3d2SAaron LI ratelimit_gc(struct ratelimit *rl, bool force)
461a6bca3d2SAaron LI {
462a6bca3d2SAaron LI 	struct ratelimit_entry *r, *tr;
4632bed72b3SAaron LI 	struct timespec expiry;
4641ef0d803SAaron LI 	size_t i;
465a6bca3d2SAaron LI 
466139109cdSAaron LI 	KKASSERT(lockstatus(&rl->rl_mtx, curthread) == LK_EXCLUSIVE);
467a6bca3d2SAaron LI 
468a6bca3d2SAaron LI 	if (rl->rl_table_num == 0)
469a6bca3d2SAaron LI 		return;
470a6bca3d2SAaron LI 
4712bed72b3SAaron LI 	getnanouptime(&expiry);
4722bed72b3SAaron LI 	expiry.tv_sec -= ELEMENT_TIMEOUT;
473a6bca3d2SAaron LI 
474a6bca3d2SAaron LI 	for (i = 0; i < RATELIMIT_SIZE; i++) {
4751fdfe5a9SAaron LI 		LIST_FOREACH_MUTABLE(r, &rl->rl_table[i], r_entry, tr) {
4762bed72b3SAaron LI 			if (force ||
4772bed72b3SAaron LI 			    timespeccmp(&r->r_last_time, &expiry, <)) {
478a6bca3d2SAaron LI 				rl->rl_table_num--;
479a6bca3d2SAaron LI 				LIST_REMOVE(r, r_entry);
480be332237SAaron LI 				objcache_put(ratelimit_zone, r);
481a6bca3d2SAaron LI 			}
482a6bca3d2SAaron LI 		}
483a6bca3d2SAaron LI 	}
484a6bca3d2SAaron LI 
485a6bca3d2SAaron LI 	ratelimit_gc_schedule(rl);
486a6bca3d2SAaron LI }
487a6bca3d2SAaron LI 
488a6bca3d2SAaron LI static int
4891ef0d803SAaron LI ratelimit_allow(struct ratelimit *rl, const struct sockaddr *sa)
490a6bca3d2SAaron LI {
4912bed72b3SAaron LI 	struct timespec diff;
492a6bca3d2SAaron LI 	struct ratelimit_entry *r;
4933b7831f6SAaron LI 	struct ratelimit_key key = { 0 };
4941ef0d803SAaron LI 	uint64_t bucket, tokens;
4951ef0d803SAaron LI 	size_t len;
4961ef0d803SAaron LI 	int ret = ECONNREFUSED;
497a6bca3d2SAaron LI 
498a6bca3d2SAaron LI 	if (sa->sa_family == AF_INET) {
4991ef0d803SAaron LI 		len = IPV4_MASK_SIZE;
5001ef0d803SAaron LI 		memcpy(key.ip, &((const struct sockaddr_in *)sa)->sin_addr,
5011ef0d803SAaron LI 		       len);
502a6bca3d2SAaron LI 	}
503a6bca3d2SAaron LI #ifdef INET6
5041ef0d803SAaron LI 	else if (sa->sa_family == AF_INET6) {
5051ef0d803SAaron LI 		len = IPV6_MASK_SIZE;
5061ef0d803SAaron LI 		memcpy(key.ip, &((const struct sockaddr_in6 *)sa)->sin6_addr,
5071ef0d803SAaron LI 		       len);
5081ef0d803SAaron LI 	}
509a6bca3d2SAaron LI #endif
5101ef0d803SAaron LI 	else {
5111ef0d803SAaron LI 		return (ret);
5121ef0d803SAaron LI 	}
513a6bca3d2SAaron LI 
514a6bca3d2SAaron LI 	bucket = siphash13(rl->rl_secret, &key, len) & RATELIMIT_MASK;
515139109cdSAaron LI 	lockmgr(&rl->rl_mtx, LK_EXCLUSIVE);
516a6bca3d2SAaron LI 
517a6bca3d2SAaron LI 	LIST_FOREACH(r, &rl->rl_table[bucket], r_entry) {
5181ef0d803SAaron LI 		if (memcmp(&r->r_key, &key, len) != 0)
519a6bca3d2SAaron LI 			continue;
520a6bca3d2SAaron LI 
5211ef0d803SAaron LI 		/*
5221ef0d803SAaron LI 		 * Found an entry for the endpoint.  We apply standard token
5231ef0d803SAaron LI 		 * bucket, by calculating the time lapsed since last_time,
5241ef0d803SAaron LI 		 * adding that, ensuring that we cap the tokens at TOKEN_MAX.
5251ef0d803SAaron LI 		 * If the endpoint has no tokens left (i.e., tokens <
5261ef0d803SAaron LI 		 * INITIATION_COST) then we block the request.  Otherwise, we
5271ef0d803SAaron LI 		 * subtract the INITITIATION_COST and return OK.
5281ef0d803SAaron LI 		 */
5292bed72b3SAaron LI 		diff = r->r_last_time;
5302bed72b3SAaron LI 		getnanouptime(&r->r_last_time);
5312bed72b3SAaron LI 		timespecsub(&r->r_last_time, &diff, &diff);
532a6bca3d2SAaron LI 
5332bed72b3SAaron LI 		tokens = r->r_tokens;
5342bed72b3SAaron LI 		tokens += diff.tv_sec * NSEC_PER_SEC + diff.tv_nsec;
535a6bca3d2SAaron LI 		if (tokens > TOKEN_MAX)
536a6bca3d2SAaron LI 			tokens = TOKEN_MAX;
537a6bca3d2SAaron LI 
538a6bca3d2SAaron LI 		if (tokens >= INITIATION_COST) {
539a6bca3d2SAaron LI 			r->r_tokens = tokens - INITIATION_COST;
540a6bca3d2SAaron LI 			goto ok;
541a6bca3d2SAaron LI 		} else {
542a6bca3d2SAaron LI 			r->r_tokens = tokens;
543a6bca3d2SAaron LI 			goto error;
544a6bca3d2SAaron LI 		}
545a6bca3d2SAaron LI 	}
546a6bca3d2SAaron LI 
5471ef0d803SAaron LI 	/*
5481ef0d803SAaron LI 	 * Didn't have an entry for the endpoint, so let's add one if we
5491ef0d803SAaron LI 	 * have space.
5501ef0d803SAaron LI 	 */
551a6bca3d2SAaron LI 	if (rl->rl_table_num >= RATELIMIT_SIZE_MAX)
552a6bca3d2SAaron LI 		goto error;
553a6bca3d2SAaron LI 
554be332237SAaron LI 	if ((r = objcache_get(ratelimit_zone, M_NOWAIT)) == NULL)
555a6bca3d2SAaron LI 		goto error;
556be332237SAaron LI 	bzero(r, sizeof(*r)); /* objcache_get() doesn't ensure M_ZERO. */
557a6bca3d2SAaron LI 
558a6bca3d2SAaron LI 	rl->rl_table_num++;
559a6bca3d2SAaron LI 
5601ef0d803SAaron LI 	/* Insert the new entry and initialize it. */
561a6bca3d2SAaron LI 	LIST_INSERT_HEAD(&rl->rl_table[bucket], r, r_entry);
562a6bca3d2SAaron LI 	r->r_key = key;
563a6bca3d2SAaron LI 	r->r_tokens = TOKEN_MAX - INITIATION_COST;
5642bed72b3SAaron LI 	getnanouptime(&r->r_last_time);
565a6bca3d2SAaron LI 
5661ef0d803SAaron LI 	/* We've added a new entry; let's trigger GC. */
567a6bca3d2SAaron LI 	ratelimit_gc_schedule(rl);
5681ef0d803SAaron LI 
569a6bca3d2SAaron LI ok:
570a6bca3d2SAaron LI 	ret = 0;
571a6bca3d2SAaron LI error:
572139109cdSAaron LI 	lockmgr(&rl->rl_mtx, LK_RELEASE);
5731ef0d803SAaron LI 	return (ret);
574a6bca3d2SAaron LI }
575a6bca3d2SAaron LI 
576a6bca3d2SAaron LI 
577e6c44b2eSAaron LI #ifdef WG_SELFTESTS
578a6bca3d2SAaron LI #include "selftest/cookie.c"
579e6c44b2eSAaron LI #endif /* WG_SELFTESTS */
580