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