1e7096c13SJason A. Donenfeld // SPDX-License-Identifier: GPL-2.0
2e7096c13SJason A. Donenfeld /*
3e7096c13SJason A. Donenfeld * Copyright (C) 2015-2019 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
4e7096c13SJason A. Donenfeld */
5e7096c13SJason A. Donenfeld
6e7096c13SJason A. Donenfeld #ifdef DEBUG
7e7096c13SJason A. Donenfeld
8e7096c13SJason A. Donenfeld #include <linux/jiffies.h>
9e7096c13SJason A. Donenfeld
10e7096c13SJason A. Donenfeld static const struct {
11e7096c13SJason A. Donenfeld bool result;
12*684dec3cSJason A. Donenfeld unsigned int msec_to_sleep_before;
13e7096c13SJason A. Donenfeld } expected_results[] __initconst = {
14e7096c13SJason A. Donenfeld [0 ... PACKETS_BURSTABLE - 1] = { true, 0 },
15e7096c13SJason A. Donenfeld [PACKETS_BURSTABLE] = { false, 0 },
16*684dec3cSJason A. Donenfeld [PACKETS_BURSTABLE + 1] = { true, MSEC_PER_SEC / PACKETS_PER_SECOND },
17e7096c13SJason A. Donenfeld [PACKETS_BURSTABLE + 2] = { false, 0 },
18*684dec3cSJason A. Donenfeld [PACKETS_BURSTABLE + 3] = { true, (MSEC_PER_SEC / PACKETS_PER_SECOND) * 2 },
19e7096c13SJason A. Donenfeld [PACKETS_BURSTABLE + 4] = { true, 0 },
20e7096c13SJason A. Donenfeld [PACKETS_BURSTABLE + 5] = { false, 0 }
21e7096c13SJason A. Donenfeld };
22e7096c13SJason A. Donenfeld
maximum_jiffies_at_index(int index)23e7096c13SJason A. Donenfeld static __init unsigned int maximum_jiffies_at_index(int index)
24e7096c13SJason A. Donenfeld {
25*684dec3cSJason A. Donenfeld unsigned int total_msecs = 2 * MSEC_PER_SEC / PACKETS_PER_SECOND / 3;
26e7096c13SJason A. Donenfeld int i;
27e7096c13SJason A. Donenfeld
28e7096c13SJason A. Donenfeld for (i = 0; i <= index; ++i)
29*684dec3cSJason A. Donenfeld total_msecs += expected_results[i].msec_to_sleep_before;
30*684dec3cSJason A. Donenfeld return msecs_to_jiffies(total_msecs);
31e7096c13SJason A. Donenfeld }
32e7096c13SJason A. Donenfeld
timings_test(struct sk_buff * skb4,struct iphdr * hdr4,struct sk_buff * skb6,struct ipv6hdr * hdr6,int * test)33e7096c13SJason A. Donenfeld static __init int timings_test(struct sk_buff *skb4, struct iphdr *hdr4,
34e7096c13SJason A. Donenfeld struct sk_buff *skb6, struct ipv6hdr *hdr6,
35e7096c13SJason A. Donenfeld int *test)
36e7096c13SJason A. Donenfeld {
37e7096c13SJason A. Donenfeld unsigned long loop_start_time;
38e7096c13SJason A. Donenfeld int i;
39e7096c13SJason A. Donenfeld
40e7096c13SJason A. Donenfeld wg_ratelimiter_gc_entries(NULL);
41e7096c13SJason A. Donenfeld rcu_barrier();
42e7096c13SJason A. Donenfeld loop_start_time = jiffies;
43e7096c13SJason A. Donenfeld
44e7096c13SJason A. Donenfeld for (i = 0; i < ARRAY_SIZE(expected_results); ++i) {
45*684dec3cSJason A. Donenfeld if (expected_results[i].msec_to_sleep_before)
46*684dec3cSJason A. Donenfeld msleep(expected_results[i].msec_to_sleep_before);
47e7096c13SJason A. Donenfeld
48e7096c13SJason A. Donenfeld if (time_is_before_jiffies(loop_start_time +
49e7096c13SJason A. Donenfeld maximum_jiffies_at_index(i)))
50e7096c13SJason A. Donenfeld return -ETIMEDOUT;
51e7096c13SJason A. Donenfeld if (wg_ratelimiter_allow(skb4, &init_net) !=
52e7096c13SJason A. Donenfeld expected_results[i].result)
53e7096c13SJason A. Donenfeld return -EXFULL;
54e7096c13SJason A. Donenfeld ++(*test);
55e7096c13SJason A. Donenfeld
56e7096c13SJason A. Donenfeld hdr4->saddr = htonl(ntohl(hdr4->saddr) + i + 1);
57e7096c13SJason A. Donenfeld if (time_is_before_jiffies(loop_start_time +
58e7096c13SJason A. Donenfeld maximum_jiffies_at_index(i)))
59e7096c13SJason A. Donenfeld return -ETIMEDOUT;
60e7096c13SJason A. Donenfeld if (!wg_ratelimiter_allow(skb4, &init_net))
61e7096c13SJason A. Donenfeld return -EXFULL;
62e7096c13SJason A. Donenfeld ++(*test);
63e7096c13SJason A. Donenfeld
64e7096c13SJason A. Donenfeld hdr4->saddr = htonl(ntohl(hdr4->saddr) - i - 1);
65e7096c13SJason A. Donenfeld
66e7096c13SJason A. Donenfeld #if IS_ENABLED(CONFIG_IPV6)
67e7096c13SJason A. Donenfeld hdr6->saddr.in6_u.u6_addr32[2] = htonl(i);
68e7096c13SJason A. Donenfeld hdr6->saddr.in6_u.u6_addr32[3] = htonl(i);
69e7096c13SJason A. Donenfeld if (time_is_before_jiffies(loop_start_time +
70e7096c13SJason A. Donenfeld maximum_jiffies_at_index(i)))
71e7096c13SJason A. Donenfeld return -ETIMEDOUT;
72e7096c13SJason A. Donenfeld if (wg_ratelimiter_allow(skb6, &init_net) !=
73e7096c13SJason A. Donenfeld expected_results[i].result)
74e7096c13SJason A. Donenfeld return -EXFULL;
75e7096c13SJason A. Donenfeld ++(*test);
76e7096c13SJason A. Donenfeld
77e7096c13SJason A. Donenfeld hdr6->saddr.in6_u.u6_addr32[0] =
78e7096c13SJason A. Donenfeld htonl(ntohl(hdr6->saddr.in6_u.u6_addr32[0]) + i + 1);
79e7096c13SJason A. Donenfeld if (time_is_before_jiffies(loop_start_time +
80e7096c13SJason A. Donenfeld maximum_jiffies_at_index(i)))
81e7096c13SJason A. Donenfeld return -ETIMEDOUT;
82e7096c13SJason A. Donenfeld if (!wg_ratelimiter_allow(skb6, &init_net))
83e7096c13SJason A. Donenfeld return -EXFULL;
84e7096c13SJason A. Donenfeld ++(*test);
85e7096c13SJason A. Donenfeld
86e7096c13SJason A. Donenfeld hdr6->saddr.in6_u.u6_addr32[0] =
87e7096c13SJason A. Donenfeld htonl(ntohl(hdr6->saddr.in6_u.u6_addr32[0]) - i - 1);
88e7096c13SJason A. Donenfeld
89e7096c13SJason A. Donenfeld if (time_is_before_jiffies(loop_start_time +
90e7096c13SJason A. Donenfeld maximum_jiffies_at_index(i)))
91e7096c13SJason A. Donenfeld return -ETIMEDOUT;
92e7096c13SJason A. Donenfeld #endif
93e7096c13SJason A. Donenfeld }
94e7096c13SJason A. Donenfeld return 0;
95e7096c13SJason A. Donenfeld }
96e7096c13SJason A. Donenfeld
capacity_test(struct sk_buff * skb4,struct iphdr * hdr4,int * test)97e7096c13SJason A. Donenfeld static __init int capacity_test(struct sk_buff *skb4, struct iphdr *hdr4,
98e7096c13SJason A. Donenfeld int *test)
99e7096c13SJason A. Donenfeld {
100e7096c13SJason A. Donenfeld int i;
101e7096c13SJason A. Donenfeld
102e7096c13SJason A. Donenfeld wg_ratelimiter_gc_entries(NULL);
103e7096c13SJason A. Donenfeld rcu_barrier();
104e7096c13SJason A. Donenfeld
105e7096c13SJason A. Donenfeld if (atomic_read(&total_entries))
106e7096c13SJason A. Donenfeld return -EXFULL;
107e7096c13SJason A. Donenfeld ++(*test);
108e7096c13SJason A. Donenfeld
109e7096c13SJason A. Donenfeld for (i = 0; i <= max_entries; ++i) {
110e7096c13SJason A. Donenfeld hdr4->saddr = htonl(i);
111e7096c13SJason A. Donenfeld if (wg_ratelimiter_allow(skb4, &init_net) != (i != max_entries))
112e7096c13SJason A. Donenfeld return -EXFULL;
113e7096c13SJason A. Donenfeld ++(*test);
114e7096c13SJason A. Donenfeld }
115e7096c13SJason A. Donenfeld return 0;
116e7096c13SJason A. Donenfeld }
117e7096c13SJason A. Donenfeld
wg_ratelimiter_selftest(void)118e7096c13SJason A. Donenfeld bool __init wg_ratelimiter_selftest(void)
119e7096c13SJason A. Donenfeld {
120e7096c13SJason A. Donenfeld enum { TRIALS_BEFORE_GIVING_UP = 5000 };
121e7096c13SJason A. Donenfeld bool success = false;
122e7096c13SJason A. Donenfeld int test = 0, trials;
1234fed818eSJason A. Donenfeld struct sk_buff *skb4, *skb6 = NULL;
124e7096c13SJason A. Donenfeld struct iphdr *hdr4;
1254fed818eSJason A. Donenfeld struct ipv6hdr *hdr6 = NULL;
126e7096c13SJason A. Donenfeld
127e7096c13SJason A. Donenfeld if (IS_ENABLED(CONFIG_KASAN) || IS_ENABLED(CONFIG_UBSAN))
128e7096c13SJason A. Donenfeld return true;
129e7096c13SJason A. Donenfeld
130*684dec3cSJason A. Donenfeld BUILD_BUG_ON(MSEC_PER_SEC % PACKETS_PER_SECOND != 0);
131e7096c13SJason A. Donenfeld
132e7096c13SJason A. Donenfeld if (wg_ratelimiter_init())
133e7096c13SJason A. Donenfeld goto out;
134e7096c13SJason A. Donenfeld ++test;
135e7096c13SJason A. Donenfeld if (wg_ratelimiter_init()) {
136e7096c13SJason A. Donenfeld wg_ratelimiter_uninit();
137e7096c13SJason A. Donenfeld goto out;
138e7096c13SJason A. Donenfeld }
139e7096c13SJason A. Donenfeld ++test;
140e7096c13SJason A. Donenfeld if (wg_ratelimiter_init()) {
141e7096c13SJason A. Donenfeld wg_ratelimiter_uninit();
142e7096c13SJason A. Donenfeld wg_ratelimiter_uninit();
143e7096c13SJason A. Donenfeld goto out;
144e7096c13SJason A. Donenfeld }
145e7096c13SJason A. Donenfeld ++test;
146e7096c13SJason A. Donenfeld
147e7096c13SJason A. Donenfeld skb4 = alloc_skb(sizeof(struct iphdr), GFP_KERNEL);
148e7096c13SJason A. Donenfeld if (unlikely(!skb4))
149e7096c13SJason A. Donenfeld goto err_nofree;
150e7096c13SJason A. Donenfeld skb4->protocol = htons(ETH_P_IP);
151e7096c13SJason A. Donenfeld hdr4 = (struct iphdr *)skb_put(skb4, sizeof(*hdr4));
152e7096c13SJason A. Donenfeld hdr4->saddr = htonl(8182);
153e7096c13SJason A. Donenfeld skb_reset_network_header(skb4);
154e7096c13SJason A. Donenfeld ++test;
155e7096c13SJason A. Donenfeld
156e7096c13SJason A. Donenfeld #if IS_ENABLED(CONFIG_IPV6)
157e7096c13SJason A. Donenfeld skb6 = alloc_skb(sizeof(struct ipv6hdr), GFP_KERNEL);
158e7096c13SJason A. Donenfeld if (unlikely(!skb6)) {
159e7096c13SJason A. Donenfeld kfree_skb(skb4);
160e7096c13SJason A. Donenfeld goto err_nofree;
161e7096c13SJason A. Donenfeld }
162e7096c13SJason A. Donenfeld skb6->protocol = htons(ETH_P_IPV6);
163e7096c13SJason A. Donenfeld hdr6 = (struct ipv6hdr *)skb_put(skb6, sizeof(*hdr6));
164e7096c13SJason A. Donenfeld hdr6->saddr.in6_u.u6_addr32[0] = htonl(1212);
165e7096c13SJason A. Donenfeld hdr6->saddr.in6_u.u6_addr32[1] = htonl(289188);
166e7096c13SJason A. Donenfeld skb_reset_network_header(skb6);
167e7096c13SJason A. Donenfeld ++test;
168e7096c13SJason A. Donenfeld #endif
169e7096c13SJason A. Donenfeld
170*684dec3cSJason A. Donenfeld for (trials = TRIALS_BEFORE_GIVING_UP; IS_ENABLED(DEBUG_RATELIMITER_TIMINGS);) {
171e7096c13SJason A. Donenfeld int test_count = 0, ret;
172e7096c13SJason A. Donenfeld
173e7096c13SJason A. Donenfeld ret = timings_test(skb4, hdr4, skb6, hdr6, &test_count);
174e7096c13SJason A. Donenfeld if (ret == -ETIMEDOUT) {
175e7096c13SJason A. Donenfeld if (!trials--) {
176e7096c13SJason A. Donenfeld test += test_count;
177e7096c13SJason A. Donenfeld goto err;
178e7096c13SJason A. Donenfeld }
179e7096c13SJason A. Donenfeld continue;
180e7096c13SJason A. Donenfeld } else if (ret < 0) {
181e7096c13SJason A. Donenfeld test += test_count;
182e7096c13SJason A. Donenfeld goto err;
183e7096c13SJason A. Donenfeld } else {
184e7096c13SJason A. Donenfeld test += test_count;
185e7096c13SJason A. Donenfeld break;
186e7096c13SJason A. Donenfeld }
187e7096c13SJason A. Donenfeld }
188e7096c13SJason A. Donenfeld
189e7096c13SJason A. Donenfeld for (trials = TRIALS_BEFORE_GIVING_UP;;) {
190e7096c13SJason A. Donenfeld int test_count = 0;
191e7096c13SJason A. Donenfeld
192e7096c13SJason A. Donenfeld if (capacity_test(skb4, hdr4, &test_count) < 0) {
193e7096c13SJason A. Donenfeld if (!trials--) {
194e7096c13SJason A. Donenfeld test += test_count;
195e7096c13SJason A. Donenfeld goto err;
196e7096c13SJason A. Donenfeld }
197e7096c13SJason A. Donenfeld continue;
198e7096c13SJason A. Donenfeld }
199e7096c13SJason A. Donenfeld test += test_count;
200e7096c13SJason A. Donenfeld break;
201e7096c13SJason A. Donenfeld }
202e7096c13SJason A. Donenfeld
203e7096c13SJason A. Donenfeld success = true;
204e7096c13SJason A. Donenfeld
205e7096c13SJason A. Donenfeld err:
206e7096c13SJason A. Donenfeld kfree_skb(skb4);
207e7096c13SJason A. Donenfeld #if IS_ENABLED(CONFIG_IPV6)
208e7096c13SJason A. Donenfeld kfree_skb(skb6);
209e7096c13SJason A. Donenfeld #endif
210e7096c13SJason A. Donenfeld err_nofree:
211e7096c13SJason A. Donenfeld wg_ratelimiter_uninit();
212e7096c13SJason A. Donenfeld wg_ratelimiter_uninit();
213e7096c13SJason A. Donenfeld wg_ratelimiter_uninit();
214e7096c13SJason A. Donenfeld /* Uninit one extra time to check underflow detection. */
215e7096c13SJason A. Donenfeld wg_ratelimiter_uninit();
216e7096c13SJason A. Donenfeld out:
217e7096c13SJason A. Donenfeld if (success)
218e7096c13SJason A. Donenfeld pr_info("ratelimiter self-tests: pass\n");
219e7096c13SJason A. Donenfeld else
220e7096c13SJason A. Donenfeld pr_err("ratelimiter self-test %d: FAIL\n", test);
221e7096c13SJason A. Donenfeld
222e7096c13SJason A. Donenfeld return success;
223e7096c13SJason A. Donenfeld }
224e7096c13SJason A. Donenfeld #endif
225