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