forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
226 lines
5.2 KiB
226 lines
5.2 KiB
// SPDX-License-Identifier: GPL-2.0 |
|
/* |
|
* Copyright (C) 2015-2019 Jason A. Donenfeld <[email protected]>. All Rights Reserved. |
|
*/ |
|
|
|
#ifdef DEBUG |
|
|
|
#include <linux/jiffies.h> |
|
|
|
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 = NULL; |
|
struct iphdr *hdr4; |
|
struct ipv6hdr *hdr6 = NULL; |
|
|
|
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
|
|
|