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