xref: /linux/drivers/net/wireguard/selftest/ratelimiter.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
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] = { true, 0 },
15 	[PACKETS_BURSTABLE] = { false, 0 },
16 	[PACKETS_BURSTABLE + 1] = { 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 
maximum_jiffies_at_index(int index)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(total_msecs);
31 }
32 
timings_test(struct sk_buff * skb4,struct iphdr * hdr4,struct sk_buff * skb6,struct ipv6hdr * hdr6,int * test)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(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(skb4, &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(skb4, &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(skb6, &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(skb6, &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 
capacity_test(struct sk_buff * skb4,struct iphdr * hdr4,int * test)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(&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(skb4, &init_net) != (i != max_entries))
112 			return -EXFULL;
113 		++(*test);
114 	}
115 	return 0;
116 }
117 
wg_ratelimiter_selftest(void)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(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(skb4, sizeof(*hdr4));
152 	hdr4->saddr = htonl(8182);
153 	skb_reset_network_header(skb4);
154 	++test;
155 
156 #if IS_ENABLED(CONFIG_IPV6)
157 	skb6 = alloc_skb(sizeof(struct ipv6hdr), GFP_KERNEL);
158 	if (unlikely(!skb6)) {
159 		kfree_skb(skb4);
160 		goto err_nofree;
161 	}
162 	skb6->protocol = htons(ETH_P_IPV6);
163 	hdr6 = (struct ipv6hdr *)skb_put(skb6, 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(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_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_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(skb4);
207 #if IS_ENABLED(CONFIG_IPV6)
208 	kfree_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