Googler | 9398cc3 | 2022-12-02 17:21:52 +0800 | [diff] [blame^] | 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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 |