// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2015-2019 Jason A. Donenfeld . All Rights Reserved. */ #ifdef DEBUG #include static const struct { bool result; unsigned int msec_to_sleep_before; } expected_results[] __initconst = { [0 ... PACKETS_BURSTABLE - 1] = { true, 0 }, [PACKETS_BURSTABLE] = { false, 0 }, [PACKETS_BURSTABLE + 1] = { true, MSEC_PER_SEC / PACKETS_PER_SECOND }, [PACKETS_BURSTABLE + 2] = { false, 0 }, [PACKETS_BURSTABLE + 3] = { true, (MSEC_PER_SEC / PACKETS_PER_SECOND) * 2 }, [PACKETS_BURSTABLE + 4] = { true, 0 }, [PACKETS_BURSTABLE + 5] = { false, 0 } }; static __init unsigned int maximum_jiffies_at_index(int index) { unsigned int total_msecs = 2 * MSEC_PER_SEC / PACKETS_PER_SECOND / 3; int i; for (i = 0; i <= index; ++i) total_msecs += expected_results[i].msec_to_sleep_before; return msecs_to_jiffies(total_msecs); } static __init int timings_test(struct sk_buff *skb4, struct iphdr *hdr4, struct sk_buff *skb6, struct ipv6hdr *hdr6, int *test) { unsigned long loop_start_time; int i; wg_ratelimiter_gc_entries(NULL); rcu_barrier(); loop_start_time = jiffies; for (i = 0; i < ARRAY_SIZE(expected_results); ++i) { if (expected_results[i].msec_to_sleep_before) msleep(expected_results[i].msec_to_sleep_before); if (time_is_before_jiffies(loop_start_time + maximum_jiffies_at_index(i))) return -ETIMEDOUT; if (wg_ratelimiter_allow(skb4, &init_net) != expected_results[i].result) return -EXFULL; ++(*test); hdr4->saddr = htonl(ntohl(hdr4->saddr) + i + 1); if (time_is_before_jiffies(loop_start_time + maximum_jiffies_at_index(i))) return -ETIMEDOUT; if (!wg_ratelimiter_allow(skb4, &init_net)) return -EXFULL; ++(*test); hdr4->saddr = htonl(ntohl(hdr4->saddr) - i - 1); #if IS_ENABLED(CONFIG_IPV6) hdr6->saddr.in6_u.u6_addr32[2] = htonl(i); hdr6->saddr.in6_u.u6_addr32[3] = htonl(i); if (time_is_before_jiffies(loop_start_time + maximum_jiffies_at_index(i))) return -ETIMEDOUT; if (wg_ratelimiter_allow(skb6, &init_net) != expected_results[i].result) return -EXFULL; ++(*test); hdr6->saddr.in6_u.u6_addr32[0] = htonl(ntohl(hdr6->saddr.in6_u.u6_addr32[0]) + i + 1); if (time_is_before_jiffies(loop_start_time + maximum_jiffies_at_index(i))) return -ETIMEDOUT; if (!wg_ratelimiter_allow(skb6, &init_net)) return -EXFULL; ++(*test); hdr6->saddr.in6_u.u6_addr32[0] = htonl(ntohl(hdr6->saddr.in6_u.u6_addr32[0]) - i - 1); if (time_is_before_jiffies(loop_start_time + maximum_jiffies_at_index(i))) return -ETIMEDOUT; #endif } return 0; } static __init int capacity_test(struct sk_buff *skb4, struct iphdr *hdr4, int *test) { int i; wg_ratelimiter_gc_entries(NULL); rcu_barrier(); if (atomic_read(&total_entries)) return -EXFULL; ++(*test); for (i = 0; i <= max_entries; ++i) { hdr4->saddr = htonl(i); if (wg_ratelimiter_allow(skb4, &init_net) != (i != max_entries)) return -EXFULL; ++(*test); } return 0; } bool __init wg_ratelimiter_selftest(void) { enum { TRIALS_BEFORE_GIVING_UP = 5000 }; bool success = false; int test = 0, trials; struct sk_buff *skb4, *skb6; struct iphdr *hdr4; struct ipv6hdr *hdr6; if (IS_ENABLED(CONFIG_KASAN) || IS_ENABLED(CONFIG_UBSAN)) return true; BUILD_BUG_ON(MSEC_PER_SEC % PACKETS_PER_SECOND != 0); if (wg_ratelimiter_init()) goto out; ++test; if (wg_ratelimiter_init()) { wg_ratelimiter_uninit(); goto out; } ++test; if (wg_ratelimiter_init()) { wg_ratelimiter_uninit(); wg_ratelimiter_uninit(); goto out; } ++test; skb4 = alloc_skb(sizeof(struct iphdr), GFP_KERNEL); if (unlikely(!skb4)) goto err_nofree; skb4->protocol = htons(ETH_P_IP); hdr4 = (struct iphdr *)skb_put(skb4, sizeof(*hdr4)); hdr4->saddr = htonl(8182); skb_reset_network_header(skb4); ++test; #if IS_ENABLED(CONFIG_IPV6) skb6 = alloc_skb(sizeof(struct ipv6hdr), GFP_KERNEL); if (unlikely(!skb6)) { kfree_skb(skb4); goto err_nofree; } skb6->protocol = htons(ETH_P_IPV6); hdr6 = (struct ipv6hdr *)skb_put(skb6, sizeof(*hdr6)); hdr6->saddr.in6_u.u6_addr32[0] = htonl(1212); hdr6->saddr.in6_u.u6_addr32[1] = htonl(289188); skb_reset_network_header(skb6); ++test; #endif for (trials = TRIALS_BEFORE_GIVING_UP;;) { int test_count = 0, ret; ret = timings_test(skb4, hdr4, skb6, hdr6, &test_count); if (ret == -ETIMEDOUT) { if (!trials--) { test += test_count; goto err; } msleep(500); continue; } else if (ret < 0) { test += test_count; goto err; } else { test += test_count; break; } } for (trials = TRIALS_BEFORE_GIVING_UP;;) { int test_count = 0; if (capacity_test(skb4, hdr4, &test_count) < 0) { if (!trials--) { test += test_count; goto err; } msleep(50); continue; } test += test_count; break; } success = true; err: kfree_skb(skb4); #if IS_ENABLED(CONFIG_IPV6) kfree_skb(skb6); #endif err_nofree: wg_ratelimiter_uninit(); wg_ratelimiter_uninit(); wg_ratelimiter_uninit(); /* Uninit one extra time to check underflow detection. */ wg_ratelimiter_uninit(); out: if (success) pr_info("ratelimiter self-tests: pass\n"); else pr_err("ratelimiter self-test %d: FAIL\n", test); return success; } #endif