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> 14be332237SAaron LI #include <sys/objcache.h> 15*1fdfe5a9SAaron LI #include <sys/queue.h> 16a6bca3d2SAaron LI #include <sys/socket.h> 17a6bca3d2SAaron LI #include <crypto/siphash/siphash.h> 18a6bca3d2SAaron LI #include <netinet/in.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]; 53139109cdSAaron LI struct lock 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 81be332237SAaron LI 82be332237SAaron LI static struct objcache *ratelimit_zone; 83be332237SAaron LI MALLOC_DEFINE(M_WG_RATELIMIT, "WG ratelimit", "wireguard ratelimit"); 84a6bca3d2SAaron LI 85a6bca3d2SAaron LI /* Public Functions */ 86a6bca3d2SAaron LI int 87a6bca3d2SAaron LI cookie_init(void) 88a6bca3d2SAaron LI { 89be332237SAaron LI ratelimit_zone = objcache_create_simple(M_WG_RATELIMIT, 90be332237SAaron LI sizeof(struct ratelimit_entry)); 91be332237SAaron LI if (ratelimit_zone == NULL) 92a6bca3d2SAaron LI return ENOMEM; 93a6bca3d2SAaron LI 94a6bca3d2SAaron LI ratelimit_init(&ratelimit_v4); 95a6bca3d2SAaron LI #ifdef INET6 96a6bca3d2SAaron LI ratelimit_init(&ratelimit_v6); 97a6bca3d2SAaron LI #endif 98a6bca3d2SAaron LI return (0); 99a6bca3d2SAaron LI } 100a6bca3d2SAaron LI 101a6bca3d2SAaron LI void 102a6bca3d2SAaron LI cookie_deinit(void) 103a6bca3d2SAaron LI { 104a6bca3d2SAaron LI ratelimit_deinit(&ratelimit_v4); 105a6bca3d2SAaron LI #ifdef INET6 106a6bca3d2SAaron LI ratelimit_deinit(&ratelimit_v6); 107a6bca3d2SAaron LI #endif 108a6bca3d2SAaron LI if (ratelimit_zone != NULL) 109be332237SAaron LI objcache_destroy(ratelimit_zone); 110a6bca3d2SAaron LI } 111a6bca3d2SAaron LI 112a6bca3d2SAaron LI void 113a6bca3d2SAaron LI cookie_checker_init(struct cookie_checker *cc) 114a6bca3d2SAaron LI { 115a6bca3d2SAaron LI bzero(cc, sizeof(*cc)); 116a6bca3d2SAaron LI 1177ef217feSAaron LI lockinit(&cc->cc_key_lock, "cookie_checker_key", 0, 0); 118139109cdSAaron LI lockinit(&cc->cc_secret_mtx, "cookie_checker_secret", 0, 0); 119a6bca3d2SAaron LI } 120a6bca3d2SAaron LI 121a6bca3d2SAaron LI void 122a6bca3d2SAaron LI cookie_checker_free(struct cookie_checker *cc) 123a6bca3d2SAaron LI { 1247ef217feSAaron LI lockuninit(&cc->cc_key_lock); 125139109cdSAaron LI lockuninit(&cc->cc_secret_mtx); 126a6bca3d2SAaron LI explicit_bzero(cc, sizeof(*cc)); 127a6bca3d2SAaron LI } 128a6bca3d2SAaron LI 129a6bca3d2SAaron LI void 130a6bca3d2SAaron LI cookie_checker_update(struct cookie_checker *cc, 131a6bca3d2SAaron LI const uint8_t key[COOKIE_INPUT_SIZE]) 132a6bca3d2SAaron LI { 1337ef217feSAaron LI lockmgr(&cc->cc_key_lock, LK_EXCLUSIVE); 134a6bca3d2SAaron LI if (key) { 135a6bca3d2SAaron LI precompute_key(cc->cc_mac1_key, key, COOKIE_MAC1_KEY_LABEL); 136a6bca3d2SAaron LI precompute_key(cc->cc_cookie_key, key, COOKIE_COOKIE_KEY_LABEL); 137a6bca3d2SAaron LI } else { 138a6bca3d2SAaron LI bzero(cc->cc_mac1_key, sizeof(cc->cc_mac1_key)); 139a6bca3d2SAaron LI bzero(cc->cc_cookie_key, sizeof(cc->cc_cookie_key)); 140a6bca3d2SAaron LI } 1417ef217feSAaron LI lockmgr(&cc->cc_key_lock, LK_RELEASE); 142a6bca3d2SAaron LI } 143a6bca3d2SAaron LI 144a6bca3d2SAaron LI void 145a6bca3d2SAaron LI cookie_checker_create_payload(struct cookie_checker *cc, 146a6bca3d2SAaron LI struct cookie_macs *macs, uint8_t nonce[COOKIE_NONCE_SIZE], 147a6bca3d2SAaron LI uint8_t ecookie[COOKIE_ENCRYPTED_SIZE], struct sockaddr *sa) 148a6bca3d2SAaron LI { 149a6bca3d2SAaron LI uint8_t cookie[COOKIE_COOKIE_SIZE]; 150a6bca3d2SAaron LI 151a6bca3d2SAaron LI make_cookie(cc, cookie, sa); 152a6bca3d2SAaron LI arc4random_buf(nonce, COOKIE_NONCE_SIZE); 153a6bca3d2SAaron LI 1547ef217feSAaron LI lockmgr(&cc->cc_key_lock, LK_SHARED); 155a6bca3d2SAaron LI xchacha20poly1305_encrypt(ecookie, cookie, COOKIE_COOKIE_SIZE, 156a6bca3d2SAaron LI macs->mac1, COOKIE_MAC_SIZE, nonce, cc->cc_cookie_key); 1577ef217feSAaron LI lockmgr(&cc->cc_key_lock, LK_RELEASE); 158a6bca3d2SAaron LI 159a6bca3d2SAaron LI explicit_bzero(cookie, sizeof(cookie)); 160a6bca3d2SAaron LI } 161a6bca3d2SAaron LI 162a6bca3d2SAaron LI void 163a6bca3d2SAaron LI cookie_maker_init(struct cookie_maker *cm, const uint8_t key[COOKIE_INPUT_SIZE]) 164a6bca3d2SAaron LI { 165a6bca3d2SAaron LI bzero(cm, sizeof(*cm)); 166a6bca3d2SAaron LI precompute_key(cm->cm_mac1_key, key, COOKIE_MAC1_KEY_LABEL); 167a6bca3d2SAaron LI precompute_key(cm->cm_cookie_key, key, COOKIE_COOKIE_KEY_LABEL); 1687ef217feSAaron LI lockinit(&cm->cm_lock, "cookie_maker", 0, 0); 169a6bca3d2SAaron LI } 170a6bca3d2SAaron LI 171a6bca3d2SAaron LI void 172a6bca3d2SAaron LI cookie_maker_free(struct cookie_maker *cm) 173a6bca3d2SAaron LI { 1747ef217feSAaron LI lockuninit(&cm->cm_lock); 175a6bca3d2SAaron LI explicit_bzero(cm, sizeof(*cm)); 176a6bca3d2SAaron LI } 177a6bca3d2SAaron LI 178a6bca3d2SAaron LI int 179a6bca3d2SAaron LI cookie_maker_consume_payload(struct cookie_maker *cm, 180a6bca3d2SAaron LI uint8_t nonce[COOKIE_NONCE_SIZE], uint8_t ecookie[COOKIE_ENCRYPTED_SIZE]) 181a6bca3d2SAaron LI { 182a6bca3d2SAaron LI uint8_t cookie[COOKIE_COOKIE_SIZE]; 183a6bca3d2SAaron LI int ret; 184a6bca3d2SAaron LI 1857ef217feSAaron LI lockmgr(&cm->cm_lock, LK_SHARED); 186a6bca3d2SAaron LI if (!cm->cm_mac1_sent) { 187a6bca3d2SAaron LI ret = ETIMEDOUT; 188a6bca3d2SAaron LI goto error; 189a6bca3d2SAaron LI } 190a6bca3d2SAaron LI 191a6bca3d2SAaron LI if (!xchacha20poly1305_decrypt(cookie, ecookie, COOKIE_ENCRYPTED_SIZE, 192a6bca3d2SAaron LI cm->cm_mac1_last, COOKIE_MAC_SIZE, nonce, cm->cm_cookie_key)) { 193a6bca3d2SAaron LI ret = EINVAL; 194a6bca3d2SAaron LI goto error; 195a6bca3d2SAaron LI } 1967ef217feSAaron LI lockmgr(&cm->cm_lock, LK_RELEASE); 197a6bca3d2SAaron LI 1987ef217feSAaron LI lockmgr(&cm->cm_lock, LK_EXCLUSIVE); 199a6bca3d2SAaron LI memcpy(cm->cm_cookie, cookie, COOKIE_COOKIE_SIZE); 200a6bca3d2SAaron LI cm->cm_cookie_birthdate = getsbinuptime(); 201a6bca3d2SAaron LI cm->cm_cookie_valid = true; 202a6bca3d2SAaron LI cm->cm_mac1_sent = false; 2037ef217feSAaron LI lockmgr(&cm->cm_lock, LK_RELEASE); 204a6bca3d2SAaron LI 205a6bca3d2SAaron LI return 0; 206a6bca3d2SAaron LI error: 2077ef217feSAaron LI lockmgr(&cm->cm_lock, LK_RELEASE); 208a6bca3d2SAaron LI return ret; 209a6bca3d2SAaron LI } 210a6bca3d2SAaron LI 211a6bca3d2SAaron LI void 212a6bca3d2SAaron LI cookie_maker_mac(struct cookie_maker *cm, struct cookie_macs *macs, void *buf, 213a6bca3d2SAaron LI size_t len) 214a6bca3d2SAaron LI { 2157ef217feSAaron LI lockmgr(&cm->cm_lock, LK_EXCLUSIVE); 216a6bca3d2SAaron LI macs_mac1(macs, buf, len, cm->cm_mac1_key); 217a6bca3d2SAaron LI memcpy(cm->cm_mac1_last, macs->mac1, COOKIE_MAC_SIZE); 218a6bca3d2SAaron LI cm->cm_mac1_sent = true; 219a6bca3d2SAaron LI 220a6bca3d2SAaron LI if (cm->cm_cookie_valid && 221a6bca3d2SAaron LI !timer_expired(cm->cm_cookie_birthdate, 222a6bca3d2SAaron LI COOKIE_SECRET_MAX_AGE - COOKIE_SECRET_LATENCY, 0)) { 223a6bca3d2SAaron LI macs_mac2(macs, buf, len, cm->cm_cookie); 224a6bca3d2SAaron LI } else { 225a6bca3d2SAaron LI bzero(macs->mac2, COOKIE_MAC_SIZE); 226a6bca3d2SAaron LI cm->cm_cookie_valid = false; 227a6bca3d2SAaron LI } 2287ef217feSAaron LI lockmgr(&cm->cm_lock, LK_RELEASE); 229a6bca3d2SAaron LI } 230a6bca3d2SAaron LI 231a6bca3d2SAaron LI int 232a6bca3d2SAaron LI cookie_checker_validate_macs(struct cookie_checker *cc, struct cookie_macs *macs, 233a6bca3d2SAaron LI void *buf, size_t len, bool check_cookie, struct sockaddr *sa, struct vnet *vnet) 234a6bca3d2SAaron LI { 235a6bca3d2SAaron LI struct cookie_macs our_macs; 236a6bca3d2SAaron LI uint8_t cookie[COOKIE_COOKIE_SIZE]; 237a6bca3d2SAaron LI 238a6bca3d2SAaron LI /* Validate incoming MACs */ 2397ef217feSAaron LI lockmgr(&cc->cc_key_lock, LK_SHARED); 240a6bca3d2SAaron LI macs_mac1(&our_macs, buf, len, cc->cc_mac1_key); 2417ef217feSAaron LI lockmgr(&cc->cc_key_lock, LK_RELEASE); 242a6bca3d2SAaron LI 243a6bca3d2SAaron LI /* If mac1 is invald, we want to drop the packet */ 244a6bca3d2SAaron LI if (timingsafe_bcmp(our_macs.mac1, macs->mac1, COOKIE_MAC_SIZE) != 0) 245a6bca3d2SAaron LI return EINVAL; 246a6bca3d2SAaron LI 247a6bca3d2SAaron LI if (check_cookie) { 248a6bca3d2SAaron LI make_cookie(cc, cookie, sa); 249a6bca3d2SAaron LI macs_mac2(&our_macs, buf, len, cookie); 250a6bca3d2SAaron LI 251a6bca3d2SAaron LI /* If the mac2 is invalid, we want to send a cookie response */ 252a6bca3d2SAaron LI if (timingsafe_bcmp(our_macs.mac2, macs->mac2, COOKIE_MAC_SIZE) != 0) 253a6bca3d2SAaron LI return EAGAIN; 254a6bca3d2SAaron LI 255a6bca3d2SAaron LI /* If the mac2 is valid, we may want rate limit the peer. 256a6bca3d2SAaron LI * ratelimit_allow will return either 0 or ECONNREFUSED, 257a6bca3d2SAaron LI * implying there is no ratelimiting, or we should ratelimit 258a6bca3d2SAaron LI * (refuse) respectively. */ 259a6bca3d2SAaron LI if (sa->sa_family == AF_INET) 260a6bca3d2SAaron LI return ratelimit_allow(&ratelimit_v4, sa, vnet); 261a6bca3d2SAaron LI #ifdef INET6 262a6bca3d2SAaron LI else if (sa->sa_family == AF_INET6) 263a6bca3d2SAaron LI return ratelimit_allow(&ratelimit_v6, sa, vnet); 264a6bca3d2SAaron LI #endif 265a6bca3d2SAaron LI else 266a6bca3d2SAaron LI return EAFNOSUPPORT; 267a6bca3d2SAaron LI } 268a6bca3d2SAaron LI 269a6bca3d2SAaron LI return 0; 270a6bca3d2SAaron LI } 271a6bca3d2SAaron LI 272a6bca3d2SAaron LI /* Private functions */ 273a6bca3d2SAaron LI static void 274a6bca3d2SAaron LI precompute_key(uint8_t *key, const uint8_t input[COOKIE_INPUT_SIZE], 275a6bca3d2SAaron LI const char *label) 276a6bca3d2SAaron LI { 277a6bca3d2SAaron LI struct blake2s_state blake; 278a6bca3d2SAaron LI blake2s_init(&blake, COOKIE_KEY_SIZE); 279a6bca3d2SAaron LI blake2s_update(&blake, label, strlen(label)); 280a6bca3d2SAaron LI blake2s_update(&blake, input, COOKIE_INPUT_SIZE); 281a6bca3d2SAaron LI blake2s_final(&blake, key); 282a6bca3d2SAaron LI } 283a6bca3d2SAaron LI 284a6bca3d2SAaron LI static void 285a6bca3d2SAaron LI macs_mac1(struct cookie_macs *macs, const void *buf, size_t len, 286a6bca3d2SAaron LI const uint8_t key[COOKIE_KEY_SIZE]) 287a6bca3d2SAaron LI { 288a6bca3d2SAaron LI struct blake2s_state state; 289a6bca3d2SAaron LI blake2s_init_key(&state, COOKIE_MAC_SIZE, key, COOKIE_KEY_SIZE); 290a6bca3d2SAaron LI blake2s_update(&state, buf, len); 291a6bca3d2SAaron LI blake2s_final(&state, macs->mac1); 292a6bca3d2SAaron LI } 293a6bca3d2SAaron LI 294a6bca3d2SAaron LI static void 295a6bca3d2SAaron LI macs_mac2(struct cookie_macs *macs, const void *buf, size_t len, 296a6bca3d2SAaron LI const uint8_t key[COOKIE_COOKIE_SIZE]) 297a6bca3d2SAaron LI { 298a6bca3d2SAaron LI struct blake2s_state state; 299a6bca3d2SAaron LI blake2s_init_key(&state, COOKIE_MAC_SIZE, key, COOKIE_COOKIE_SIZE); 300a6bca3d2SAaron LI blake2s_update(&state, buf, len); 301a6bca3d2SAaron LI blake2s_update(&state, macs->mac1, COOKIE_MAC_SIZE); 302a6bca3d2SAaron LI blake2s_final(&state, macs->mac2); 303a6bca3d2SAaron LI } 304a6bca3d2SAaron LI 305a6bca3d2SAaron LI static __inline int 306a6bca3d2SAaron LI timer_expired(sbintime_t timer, uint32_t sec, uint32_t nsec) 307a6bca3d2SAaron LI { 308a6bca3d2SAaron LI sbintime_t now = getsbinuptime(); 309a6bca3d2SAaron LI return (now > (timer + sec * SBT_1S + nstosbt(nsec))) ? ETIMEDOUT : 0; 310a6bca3d2SAaron LI } 311a6bca3d2SAaron LI 312a6bca3d2SAaron LI static void 313a6bca3d2SAaron LI make_cookie(struct cookie_checker *cc, uint8_t cookie[COOKIE_COOKIE_SIZE], 314a6bca3d2SAaron LI struct sockaddr *sa) 315a6bca3d2SAaron LI { 316a6bca3d2SAaron LI struct blake2s_state state; 317a6bca3d2SAaron LI 318139109cdSAaron LI lockmgr(&cc->cc_secret_mtx, LK_EXCLUSIVE); 319a6bca3d2SAaron LI if (timer_expired(cc->cc_secret_birthdate, 320a6bca3d2SAaron LI COOKIE_SECRET_MAX_AGE, 0)) { 321a6bca3d2SAaron LI arc4random_buf(cc->cc_secret, COOKIE_SECRET_SIZE); 322a6bca3d2SAaron LI cc->cc_secret_birthdate = getsbinuptime(); 323a6bca3d2SAaron LI } 324a6bca3d2SAaron LI blake2s_init_key(&state, COOKIE_COOKIE_SIZE, cc->cc_secret, 325a6bca3d2SAaron LI COOKIE_SECRET_SIZE); 326139109cdSAaron LI lockmgr(&cc->cc_secret_mtx, LK_RELEASE); 327a6bca3d2SAaron LI 328a6bca3d2SAaron LI if (sa->sa_family == AF_INET) { 329a6bca3d2SAaron LI blake2s_update(&state, (uint8_t *)&satosin(sa)->sin_addr, 330a6bca3d2SAaron LI sizeof(struct in_addr)); 331a6bca3d2SAaron LI blake2s_update(&state, (uint8_t *)&satosin(sa)->sin_port, 332a6bca3d2SAaron LI sizeof(in_port_t)); 333a6bca3d2SAaron LI blake2s_final(&state, cookie); 334a6bca3d2SAaron LI #ifdef INET6 335a6bca3d2SAaron LI } else if (sa->sa_family == AF_INET6) { 336a6bca3d2SAaron LI blake2s_update(&state, (uint8_t *)&satosin6(sa)->sin6_addr, 337a6bca3d2SAaron LI sizeof(struct in6_addr)); 338a6bca3d2SAaron LI blake2s_update(&state, (uint8_t *)&satosin6(sa)->sin6_port, 339a6bca3d2SAaron LI sizeof(in_port_t)); 340a6bca3d2SAaron LI blake2s_final(&state, cookie); 341a6bca3d2SAaron LI #endif 342a6bca3d2SAaron LI } else { 343a6bca3d2SAaron LI arc4random_buf(cookie, COOKIE_COOKIE_SIZE); 344a6bca3d2SAaron LI } 345a6bca3d2SAaron LI } 346a6bca3d2SAaron LI 347a6bca3d2SAaron LI static void 348a6bca3d2SAaron LI ratelimit_init(struct ratelimit *rl) 349a6bca3d2SAaron LI { 350a6bca3d2SAaron LI size_t i; 351139109cdSAaron LI lockinit(&rl->rl_mtx, "ratelimit_lock", 0, 0); 352a6bca3d2SAaron LI callout_init_mtx(&rl->rl_gc, &rl->rl_mtx, 0); 353a6bca3d2SAaron LI arc4random_buf(rl->rl_secret, sizeof(rl->rl_secret)); 354a6bca3d2SAaron LI for (i = 0; i < RATELIMIT_SIZE; i++) 355a6bca3d2SAaron LI LIST_INIT(&rl->rl_table[i]); 356a6bca3d2SAaron LI rl->rl_table_num = 0; 357a6bca3d2SAaron LI rl->rl_initialized = true; 358a6bca3d2SAaron LI } 359a6bca3d2SAaron LI 360a6bca3d2SAaron LI static void 361a6bca3d2SAaron LI ratelimit_deinit(struct ratelimit *rl) 362a6bca3d2SAaron LI { 363a6bca3d2SAaron LI if (!rl->rl_initialized) 364a6bca3d2SAaron LI return; 365139109cdSAaron LI lockmgr(&rl->rl_mtx, LK_EXCLUSIVE); 366a6bca3d2SAaron LI callout_stop(&rl->rl_gc); 367a6bca3d2SAaron LI ratelimit_gc(rl, true); 368139109cdSAaron LI lockmgr(&rl->rl_mtx, LK_RELEASE); 369139109cdSAaron LI lockuninit(&rl->rl_mtx); 370a6bca3d2SAaron LI 371a6bca3d2SAaron LI rl->rl_initialized = false; 372a6bca3d2SAaron LI } 373a6bca3d2SAaron LI 374a6bca3d2SAaron LI static void 375a6bca3d2SAaron LI ratelimit_gc_callout(void *_rl) 376a6bca3d2SAaron LI { 377a6bca3d2SAaron LI /* callout will lock rl_mtx for us */ 378a6bca3d2SAaron LI ratelimit_gc(_rl, false); 379a6bca3d2SAaron LI } 380a6bca3d2SAaron LI 381a6bca3d2SAaron LI static void 382a6bca3d2SAaron LI ratelimit_gc_schedule(struct ratelimit *rl) 383a6bca3d2SAaron LI { 384a6bca3d2SAaron LI /* Trigger another GC if needed. There is no point calling GC if there 385a6bca3d2SAaron LI * are no entries in the table. We also want to ensure that GC occurs 386a6bca3d2SAaron LI * on a regular interval, so don't override a currently pending GC. 387a6bca3d2SAaron LI * 388a6bca3d2SAaron LI * In the case of a forced ratelimit_gc, there will be no entries left 389a6bca3d2SAaron LI * so we will will not schedule another GC. */ 390a6bca3d2SAaron LI if (rl->rl_table_num > 0 && !callout_pending(&rl->rl_gc)) 391a6bca3d2SAaron LI callout_reset(&rl->rl_gc, ELEMENT_TIMEOUT * hz, 392a6bca3d2SAaron LI ratelimit_gc_callout, rl); 393a6bca3d2SAaron LI } 394a6bca3d2SAaron LI 395a6bca3d2SAaron LI static void 396a6bca3d2SAaron LI ratelimit_gc(struct ratelimit *rl, bool force) 397a6bca3d2SAaron LI { 398a6bca3d2SAaron LI size_t i; 399a6bca3d2SAaron LI struct ratelimit_entry *r, *tr; 400a6bca3d2SAaron LI sbintime_t expiry; 401a6bca3d2SAaron LI 402139109cdSAaron LI KKASSERT(lockstatus(&rl->rl_mtx, curthread) == LK_EXCLUSIVE); 403a6bca3d2SAaron LI 404a6bca3d2SAaron LI if (rl->rl_table_num == 0) 405a6bca3d2SAaron LI return; 406a6bca3d2SAaron LI 407a6bca3d2SAaron LI expiry = getsbinuptime() - ELEMENT_TIMEOUT * SBT_1S; 408a6bca3d2SAaron LI 409a6bca3d2SAaron LI for (i = 0; i < RATELIMIT_SIZE; i++) { 410*1fdfe5a9SAaron LI LIST_FOREACH_MUTABLE(r, &rl->rl_table[i], r_entry, tr) { 411a6bca3d2SAaron LI if (r->r_last_time < expiry || force) { 412a6bca3d2SAaron LI rl->rl_table_num--; 413a6bca3d2SAaron LI LIST_REMOVE(r, r_entry); 414be332237SAaron LI objcache_put(ratelimit_zone, r); 415a6bca3d2SAaron LI } 416a6bca3d2SAaron LI } 417a6bca3d2SAaron LI } 418a6bca3d2SAaron LI 419a6bca3d2SAaron LI ratelimit_gc_schedule(rl); 420a6bca3d2SAaron LI } 421a6bca3d2SAaron LI 422a6bca3d2SAaron LI static int 423a6bca3d2SAaron LI ratelimit_allow(struct ratelimit *rl, struct sockaddr *sa, struct vnet *vnet) 424a6bca3d2SAaron LI { 425a6bca3d2SAaron LI uint64_t bucket, tokens; 426a6bca3d2SAaron LI sbintime_t diff, now; 427a6bca3d2SAaron LI struct ratelimit_entry *r; 428a6bca3d2SAaron LI int ret = ECONNREFUSED; 429a6bca3d2SAaron LI struct ratelimit_key key = { .vnet = vnet }; 430a6bca3d2SAaron LI size_t len = sizeof(key); 431a6bca3d2SAaron LI 432a6bca3d2SAaron LI if (sa->sa_family == AF_INET) { 433a6bca3d2SAaron LI memcpy(key.ip, &satosin(sa)->sin_addr, IPV4_MASK_SIZE); 434a6bca3d2SAaron LI len -= IPV6_MASK_SIZE - IPV4_MASK_SIZE; 435a6bca3d2SAaron LI } 436a6bca3d2SAaron LI #ifdef INET6 437a6bca3d2SAaron LI else if (sa->sa_family == AF_INET6) 438a6bca3d2SAaron LI memcpy(key.ip, &satosin6(sa)->sin6_addr, IPV6_MASK_SIZE); 439a6bca3d2SAaron LI #endif 440a6bca3d2SAaron LI else 441a6bca3d2SAaron LI return ret; 442a6bca3d2SAaron LI 443a6bca3d2SAaron LI bucket = siphash13(rl->rl_secret, &key, len) & RATELIMIT_MASK; 444139109cdSAaron LI lockmgr(&rl->rl_mtx, LK_EXCLUSIVE); 445a6bca3d2SAaron LI 446a6bca3d2SAaron LI LIST_FOREACH(r, &rl->rl_table[bucket], r_entry) { 447a6bca3d2SAaron LI if (bcmp(&r->r_key, &key, len) != 0) 448a6bca3d2SAaron LI continue; 449a6bca3d2SAaron LI 450a6bca3d2SAaron LI /* If we get to here, we've found an entry for the endpoint. 451a6bca3d2SAaron LI * We apply standard token bucket, by calculating the time 452a6bca3d2SAaron LI * lapsed since our last_time, adding that, ensuring that we 453a6bca3d2SAaron LI * cap the tokens at TOKEN_MAX. If the endpoint has no tokens 454a6bca3d2SAaron LI * left (that is tokens <= INITIATION_COST) then we block the 455a6bca3d2SAaron LI * request, otherwise we subtract the INITITIATION_COST and 456a6bca3d2SAaron LI * return OK. */ 457a6bca3d2SAaron LI now = getsbinuptime(); 458a6bca3d2SAaron LI diff = now - r->r_last_time; 459a6bca3d2SAaron LI r->r_last_time = now; 460a6bca3d2SAaron LI 461a6bca3d2SAaron LI tokens = r->r_tokens + diff; 462a6bca3d2SAaron LI 463a6bca3d2SAaron LI if (tokens > TOKEN_MAX) 464a6bca3d2SAaron LI tokens = TOKEN_MAX; 465a6bca3d2SAaron LI 466a6bca3d2SAaron LI if (tokens >= INITIATION_COST) { 467a6bca3d2SAaron LI r->r_tokens = tokens - INITIATION_COST; 468a6bca3d2SAaron LI goto ok; 469a6bca3d2SAaron LI } else { 470a6bca3d2SAaron LI r->r_tokens = tokens; 471a6bca3d2SAaron LI goto error; 472a6bca3d2SAaron LI } 473a6bca3d2SAaron LI } 474a6bca3d2SAaron LI 475a6bca3d2SAaron LI /* If we get to here, we didn't have an entry for the endpoint, let's 476a6bca3d2SAaron LI * add one if we have space. */ 477a6bca3d2SAaron LI if (rl->rl_table_num >= RATELIMIT_SIZE_MAX) 478a6bca3d2SAaron LI goto error; 479a6bca3d2SAaron LI 480a6bca3d2SAaron LI /* Goto error if out of memory */ 481be332237SAaron LI if ((r = objcache_get(ratelimit_zone, M_NOWAIT)) == NULL) 482a6bca3d2SAaron LI goto error; 483be332237SAaron LI bzero(r, sizeof(*r)); /* objcache_get() doesn't ensure M_ZERO. */ 484a6bca3d2SAaron LI 485a6bca3d2SAaron LI rl->rl_table_num++; 486a6bca3d2SAaron LI 487a6bca3d2SAaron LI /* Insert entry into the hashtable and ensure it's initialised */ 488a6bca3d2SAaron LI LIST_INSERT_HEAD(&rl->rl_table[bucket], r, r_entry); 489a6bca3d2SAaron LI r->r_key = key; 490a6bca3d2SAaron LI r->r_last_time = getsbinuptime(); 491a6bca3d2SAaron LI r->r_tokens = TOKEN_MAX - INITIATION_COST; 492a6bca3d2SAaron LI 493a6bca3d2SAaron LI /* If we've added a new entry, let's trigger GC. */ 494a6bca3d2SAaron LI ratelimit_gc_schedule(rl); 495a6bca3d2SAaron LI ok: 496a6bca3d2SAaron LI ret = 0; 497a6bca3d2SAaron LI error: 498139109cdSAaron LI lockmgr(&rl->rl_mtx, LK_RELEASE); 499a6bca3d2SAaron LI return ret; 500a6bca3d2SAaron LI } 501a6bca3d2SAaron LI 502a6bca3d2SAaron LI static uint64_t siphash13(const uint8_t key[SIPHASH_KEY_LENGTH], const void *src, size_t len) 503a6bca3d2SAaron LI { 504a6bca3d2SAaron LI SIPHASH_CTX ctx; 505a6bca3d2SAaron LI return (SipHashX(&ctx, 1, 3, key, src, len)); 506a6bca3d2SAaron LI } 507a6bca3d2SAaron LI 508a6bca3d2SAaron LI #ifdef SELFTESTS 509a6bca3d2SAaron LI #include "selftest/cookie.c" 510a6bca3d2SAaron LI #endif /* SELFTESTS */ 511