1af6d6403SAaron LI /*-
2af6d6403SAaron LI * SPDX-License-Identifier: MIT
382c705f0SAaron LI *
482c705f0SAaron LI * Copyright (C) 2015-2021 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
582c705f0SAaron LI * Copyright (C) 2019-2021 Matt Dunwoodie <ncon@noconroy.net>
6af6d6403SAaron LI *
7af6d6403SAaron LI * Permission is hereby granted, free of charge, to any person obtaining
8af6d6403SAaron LI * a copy of this software and associated documentation files (the
9af6d6403SAaron LI * "Software"), to deal in the Software without restriction, including
10af6d6403SAaron LI * without limitation the rights to use, copy, modify, merge, publish,
11af6d6403SAaron LI * distribute, sublicense, and/or sell copies of the Software, and to
12af6d6403SAaron LI * permit persons to whom the Software is furnished to do so, subject
13af6d6403SAaron LI * to the following conditions:
14af6d6403SAaron LI *
15af6d6403SAaron LI * The above copyright notice and this permission notice shall be
16af6d6403SAaron LI * included in all copies or substantial portions of the Software.
17af6d6403SAaron LI *
18af6d6403SAaron LI * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19af6d6403SAaron LI * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20af6d6403SAaron LI * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21af6d6403SAaron LI * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
22af6d6403SAaron LI * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23af6d6403SAaron LI * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24af6d6403SAaron LI * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2582c705f0SAaron LI */
2682c705f0SAaron LI
27af6d6403SAaron LI #define T_MESSAGE_LEN 64
2882c705f0SAaron LI #define T_FAILED_ITER(test) do { \
299de0ba69SAaron LI kprintf("%s %s: FAIL, iter: %d\n", __func__, test, i); \
3082c705f0SAaron LI goto cleanup; \
3182c705f0SAaron LI } while (0)
3282c705f0SAaron LI #define T_FAILED(test) do { \
339de0ba69SAaron LI kprintf("%s %s: FAIL\n", __func__, test); \
3482c705f0SAaron LI goto cleanup; \
3582c705f0SAaron LI } while (0)
369de0ba69SAaron LI #define T_PASSED kprintf("%s: pass\n", __func__)
3782c705f0SAaron LI
3882c705f0SAaron LI static const struct expected_results {
3982c705f0SAaron LI int result;
409de0ba69SAaron LI uint64_t sleep_time; /* nanoseconds */
4182c705f0SAaron LI } rl_expected[] = {
42af6d6403SAaron LI /* [0 ... INITIATIONS_BURSTABLE-1] entries are implied zero. */
4382c705f0SAaron LI [INITIATIONS_BURSTABLE] = { ECONNREFUSED, 0 },
4482c705f0SAaron LI [INITIATIONS_BURSTABLE + 1] = { 0, INITIATION_COST },
4582c705f0SAaron LI [INITIATIONS_BURSTABLE + 2] = { ECONNREFUSED, 0 },
4682c705f0SAaron LI [INITIATIONS_BURSTABLE + 3] = { 0, INITIATION_COST * 2 },
4782c705f0SAaron LI [INITIATIONS_BURSTABLE + 4] = { 0, 0 },
48af6d6403SAaron LI [INITIATIONS_BURSTABLE + 5] = { ECONNREFUSED, 0 },
4982c705f0SAaron LI };
5082c705f0SAaron LI
51af6d6403SAaron LI static struct ratelimit rl_test;
5282c705f0SAaron LI
5382c705f0SAaron LI static bool
cookie_ratelimit_timings_test(void)5482c705f0SAaron LI cookie_ratelimit_timings_test(void)
5582c705f0SAaron LI {
56af6d6403SAaron LI struct sockaddr_in sin = { .sin_family = AF_INET };
5782c705f0SAaron LI #ifdef INET6
58af6d6403SAaron LI struct sockaddr_in6 sin6 = { .sin6_family = AF_INET6 };
5982c705f0SAaron LI #endif
609de0ba69SAaron LI uint64_t t;
6182c705f0SAaron LI int i;
62af6d6403SAaron LI bool ret = false;
6382c705f0SAaron LI
64af6d6403SAaron LI ratelimit_init(&rl_test);
6582c705f0SAaron LI
66af6d6403SAaron LI for (i = 0; i < nitems(rl_expected); i++) {
67af6d6403SAaron LI if ((t = rl_expected[i].sleep_time) != 0) {
68af6d6403SAaron LI tsleep(&rl_test, 0, "rl_test",
69af6d6403SAaron LI (int)(t * hz / NSEC_PER_SEC));
70af6d6403SAaron LI }
7182c705f0SAaron LI
72af6d6403SAaron LI /*
73af6d6403SAaron LI * The first v4 ratelimit_allow is against a constant address,
74af6d6403SAaron LI * and should be indifferent to the port.
75af6d6403SAaron LI */
7682c705f0SAaron LI sin.sin_addr.s_addr = 0x01020304;
779de0ba69SAaron LI sin.sin_port = karc4random();
7882c705f0SAaron LI
79af6d6403SAaron LI if (ratelimit_allow(&rl_test, sintosa(&sin))
80af6d6403SAaron LI != rl_expected[i].result)
8182c705f0SAaron LI T_FAILED_ITER("malicious v4");
8282c705f0SAaron LI
83af6d6403SAaron LI /*
84af6d6403SAaron LI * The second ratelimit_allow is to test that an arbitrary
85af6d6403SAaron LI * address is still allowed.
86af6d6403SAaron LI */
8782c705f0SAaron LI sin.sin_addr.s_addr += i + 1;
889de0ba69SAaron LI sin.sin_port = karc4random();
8982c705f0SAaron LI
90af6d6403SAaron LI if (ratelimit_allow(&rl_test, sintosa(&sin)) != 0)
9182c705f0SAaron LI T_FAILED_ITER("non-malicious v4");
9282c705f0SAaron LI
9382c705f0SAaron LI #ifdef INET6
94af6d6403SAaron LI /*
95af6d6403SAaron LI * The first v6 ratelimit_allow is against a constant address,
9682c705f0SAaron LI * and should be indifferent to the port. We also mutate the
9782c705f0SAaron LI * lower 64 bits of the address as we want to ensure ratelimit
98af6d6403SAaron LI * occurs against the higher 64 bits (/64 network).
99af6d6403SAaron LI */
10082c705f0SAaron LI sin6.sin6_addr.s6_addr32[0] = 0x01020304;
10182c705f0SAaron LI sin6.sin6_addr.s6_addr32[1] = 0x05060708;
10282c705f0SAaron LI sin6.sin6_addr.s6_addr32[2] = i;
10382c705f0SAaron LI sin6.sin6_addr.s6_addr32[3] = i;
1049de0ba69SAaron LI sin6.sin6_port = karc4random();
10582c705f0SAaron LI
106af6d6403SAaron LI if (ratelimit_allow(&rl_test, sin6tosa(&sin6))
107af6d6403SAaron LI != rl_expected[i].result)
10882c705f0SAaron LI T_FAILED_ITER("malicious v6");
10982c705f0SAaron LI
110af6d6403SAaron LI /*
111af6d6403SAaron LI * Again, test that an address different to above is still
112af6d6403SAaron LI * allowed.
113af6d6403SAaron LI */
11482c705f0SAaron LI sin6.sin6_addr.s6_addr32[0] += i + 1;
1159de0ba69SAaron LI sin6.sin6_port = karc4random();
11682c705f0SAaron LI
117af6d6403SAaron LI if (ratelimit_allow(&rl_test, sintosa(&sin)) != 0)
11882c705f0SAaron LI T_FAILED_ITER("non-malicious v6");
11982c705f0SAaron LI #endif
12082c705f0SAaron LI }
12182c705f0SAaron LI T_PASSED;
12282c705f0SAaron LI ret = true;
123af6d6403SAaron LI
12482c705f0SAaron LI cleanup:
125af6d6403SAaron LI ratelimit_deinit(&rl_test);
126af6d6403SAaron LI return (ret);
12782c705f0SAaron LI }
12882c705f0SAaron LI
12982c705f0SAaron LI static bool
cookie_ratelimit_capacity_test(void)13082c705f0SAaron LI cookie_ratelimit_capacity_test(void)
13182c705f0SAaron LI {
13282c705f0SAaron LI struct sockaddr_in sin;
13382c705f0SAaron LI int i;
13482c705f0SAaron LI bool ret = false;
13582c705f0SAaron LI
136af6d6403SAaron LI ratelimit_init(&rl_test);
13782c705f0SAaron LI
13882c705f0SAaron LI sin.sin_family = AF_INET;
13982c705f0SAaron LI sin.sin_port = 1234;
14082c705f0SAaron LI
141af6d6403SAaron LI /*
142af6d6403SAaron LI * Test that the ratelimiter has an upper bound on the number of
143af6d6403SAaron LI * addresses to be limited.
144af6d6403SAaron LI */
14582c705f0SAaron LI for (i = 0; i <= RATELIMIT_SIZE_MAX; i++) {
14682c705f0SAaron LI sin.sin_addr.s_addr = i;
14782c705f0SAaron LI if (i == RATELIMIT_SIZE_MAX) {
148af6d6403SAaron LI if (ratelimit_allow(&rl_test, sintosa(&sin))
149af6d6403SAaron LI != ECONNREFUSED)
15082c705f0SAaron LI T_FAILED_ITER("reject");
15182c705f0SAaron LI } else {
152af6d6403SAaron LI if (ratelimit_allow(&rl_test, sintosa(&sin)) != 0)
15382c705f0SAaron LI T_FAILED_ITER("allow");
15482c705f0SAaron LI }
15582c705f0SAaron LI }
15682c705f0SAaron LI T_PASSED;
15782c705f0SAaron LI ret = true;
158af6d6403SAaron LI
15982c705f0SAaron LI cleanup:
160af6d6403SAaron LI ratelimit_deinit(&rl_test);
161af6d6403SAaron LI return (ret);
16282c705f0SAaron LI }
16382c705f0SAaron LI
16482c705f0SAaron LI static bool
cookie_ratelimit_gc_test(void)16582c705f0SAaron LI cookie_ratelimit_gc_test(void)
16682c705f0SAaron LI {
16782c705f0SAaron LI struct sockaddr_in sin;
16882c705f0SAaron LI int i;
16982c705f0SAaron LI bool ret = false;
17082c705f0SAaron LI
171af6d6403SAaron LI ratelimit_init(&rl_test);
17282c705f0SAaron LI
17382c705f0SAaron LI sin.sin_family = AF_INET;
17482c705f0SAaron LI sin.sin_port = 1234;
17582c705f0SAaron LI
176af6d6403SAaron LI /* Test that the garbage collect routine will run. */
177af6d6403SAaron LI if (rl_test.rl_table_num != 0)
17882c705f0SAaron LI T_FAILED("init not empty");
17982c705f0SAaron LI
18082c705f0SAaron LI for (i = 0; i < RATELIMIT_SIZE_MAX / 2; i++) {
18182c705f0SAaron LI sin.sin_addr.s_addr = i;
182af6d6403SAaron LI if (ratelimit_allow(&rl_test, sintosa(&sin)) != 0)
18382c705f0SAaron LI T_FAILED_ITER("insert");
18482c705f0SAaron LI }
18582c705f0SAaron LI
186af6d6403SAaron LI if (rl_test.rl_table_num != RATELIMIT_SIZE_MAX / 2)
18782c705f0SAaron LI T_FAILED("insert 1 not full");
18882c705f0SAaron LI
189af6d6403SAaron LI tsleep(&rl_test, 0, "rl_test", ELEMENT_TIMEOUT * hz / 2);
19082c705f0SAaron LI
19182c705f0SAaron LI for (i = 0; i < RATELIMIT_SIZE_MAX / 2; i++) {
19282c705f0SAaron LI sin.sin_addr.s_addr = i;
193af6d6403SAaron LI if (ratelimit_allow(&rl_test, sintosa(&sin)) != 0)
19482c705f0SAaron LI T_FAILED_ITER("insert");
19582c705f0SAaron LI }
19682c705f0SAaron LI
197af6d6403SAaron LI if (rl_test.rl_table_num != RATELIMIT_SIZE_MAX / 2)
19882c705f0SAaron LI T_FAILED("insert 2 not full");
19982c705f0SAaron LI
200af6d6403SAaron LI tsleep(&rl_test, 0, "rl_test", ELEMENT_TIMEOUT * hz * 2);
20182c705f0SAaron LI
202af6d6403SAaron LI if (rl_test.rl_table_num != 0)
20382c705f0SAaron LI T_FAILED("gc");
204af6d6403SAaron LI
20582c705f0SAaron LI T_PASSED;
20682c705f0SAaron LI ret = true;
207af6d6403SAaron LI
20882c705f0SAaron LI cleanup:
209af6d6403SAaron LI ratelimit_deinit(&rl_test);
210af6d6403SAaron LI return (ret);
21182c705f0SAaron LI }
21282c705f0SAaron LI
21382c705f0SAaron LI static bool
cookie_mac_test(void)21482c705f0SAaron LI cookie_mac_test(void)
21582c705f0SAaron LI {
216*03c3b87eSAaron LI struct cookie_checker *checker;
217*03c3b87eSAaron LI struct cookie_maker *maker;
21882c705f0SAaron LI struct cookie_macs cm;
21982c705f0SAaron LI struct sockaddr_in sin;
22082c705f0SAaron LI uint8_t nonce[COOKIE_NONCE_SIZE];
22182c705f0SAaron LI uint8_t cookie[COOKIE_ENCRYPTED_SIZE];
22282c705f0SAaron LI uint8_t shared[COOKIE_INPUT_SIZE];
223af6d6403SAaron LI uint8_t message[T_MESSAGE_LEN];
224af6d6403SAaron LI int res, i;
225af6d6403SAaron LI bool ret = false;
22682c705f0SAaron LI
2279de0ba69SAaron LI karc4random_buf(shared, COOKIE_INPUT_SIZE);
228af6d6403SAaron LI karc4random_buf(message, T_MESSAGE_LEN);
22982c705f0SAaron LI
23082c705f0SAaron LI /* Init cookie_maker. */
231*03c3b87eSAaron LI maker = cookie_maker_alloc(shared);
23282c705f0SAaron LI
233*03c3b87eSAaron LI checker = cookie_checker_alloc();
234*03c3b87eSAaron LI cookie_checker_update(checker, shared);
23582c705f0SAaron LI
236af6d6403SAaron LI /* Create dummy sockaddr. */
23782c705f0SAaron LI sin.sin_family = AF_INET;
23882c705f0SAaron LI sin.sin_len = sizeof(sin);
23982c705f0SAaron LI sin.sin_addr.s_addr = 1;
24082c705f0SAaron LI sin.sin_port = 51820;
24182c705f0SAaron LI
242af6d6403SAaron LI /* MAC message. */
243*03c3b87eSAaron LI cookie_maker_mac(maker, &cm, message, T_MESSAGE_LEN);
24482c705f0SAaron LI
245af6d6403SAaron LI /* Check we have a null mac2. */
246af6d6403SAaron LI for (i = 0; i < sizeof(cm.mac2); i++) {
24782c705f0SAaron LI if (cm.mac2[i] != 0)
24882c705f0SAaron LI T_FAILED("validate_macs_noload_mac2_zeroed");
249af6d6403SAaron LI }
25082c705f0SAaron LI
251af6d6403SAaron LI /* Validate all bytes are checked in mac1. */
25282c705f0SAaron LI for (i = 0; i < sizeof(cm.mac1); i++) {
25382c705f0SAaron LI cm.mac1[i] = ~cm.mac1[i];
254*03c3b87eSAaron LI if (cookie_checker_validate_macs(checker, &cm, message,
255af6d6403SAaron LI T_MESSAGE_LEN, 0,
256af6d6403SAaron LI sintosa(&sin)) != EINVAL)
25782c705f0SAaron LI T_FAILED("validate_macs_noload_munge");
25882c705f0SAaron LI cm.mac1[i] = ~cm.mac1[i];
25982c705f0SAaron LI }
26082c705f0SAaron LI
261af6d6403SAaron LI /* Check mac2 is zeroed. */
26282c705f0SAaron LI res = 0;
26382c705f0SAaron LI for (i = 0; i < sizeof(cm.mac2); i++)
26482c705f0SAaron LI res |= cm.mac2[i];
26582c705f0SAaron LI if (res != 0)
26682c705f0SAaron LI T_FAILED("validate_macs_mac2_checkzero");
26782c705f0SAaron LI
26882c705f0SAaron LI
269af6d6403SAaron LI /* Check we can successfully validate the MAC. */
270*03c3b87eSAaron LI if (cookie_checker_validate_macs(checker, &cm, message, T_MESSAGE_LEN,
271af6d6403SAaron LI 0, sintosa(&sin)) != 0)
27282c705f0SAaron LI T_FAILED("validate_macs_noload_normal");
27382c705f0SAaron LI
274af6d6403SAaron LI /* Check we get a EAGAIN if no mac2 and under load. */
275*03c3b87eSAaron LI if (cookie_checker_validate_macs(checker, &cm, message, T_MESSAGE_LEN,
276af6d6403SAaron LI 1, sintosa(&sin)) != EAGAIN)
27782c705f0SAaron LI T_FAILED("validate_macs_load_normal");
27882c705f0SAaron LI
279af6d6403SAaron LI /* Simulate a cookie message. */
280*03c3b87eSAaron LI cookie_checker_create_payload(checker, &cm, nonce, cookie,
281af6d6403SAaron LI sintosa(&sin));
28282c705f0SAaron LI
283af6d6403SAaron LI /* Validate all bytes are checked in cookie. */
28482c705f0SAaron LI for (i = 0; i < sizeof(cookie); i++) {
28582c705f0SAaron LI cookie[i] = ~cookie[i];
286*03c3b87eSAaron LI if (cookie_maker_consume_payload(maker, nonce, cookie)
287af6d6403SAaron LI != EINVAL)
28882c705f0SAaron LI T_FAILED("consume_payload_munge");
28982c705f0SAaron LI cookie[i] = ~cookie[i];
29082c705f0SAaron LI }
29182c705f0SAaron LI
292af6d6403SAaron LI /* Check we can actually consume the payload. */
293*03c3b87eSAaron LI if (cookie_maker_consume_payload(maker, nonce, cookie) != 0)
29482c705f0SAaron LI T_FAILED("consume_payload_normal");
29582c705f0SAaron LI
296af6d6403SAaron LI /* Check replay isn't allowed. */
297*03c3b87eSAaron LI if (cookie_maker_consume_payload(maker, nonce, cookie) != ETIMEDOUT)
29882c705f0SAaron LI T_FAILED("consume_payload_normal_replay");
29982c705f0SAaron LI
300af6d6403SAaron LI /* MAC message again, with MAC2. */
301*03c3b87eSAaron LI cookie_maker_mac(maker, &cm, message, T_MESSAGE_LEN);
30282c705f0SAaron LI
303af6d6403SAaron LI /* Check we added a mac2. */
30482c705f0SAaron LI res = 0;
30582c705f0SAaron LI for (i = 0; i < sizeof(cm.mac2); i++)
30682c705f0SAaron LI res |= cm.mac2[i];
30782c705f0SAaron LI if (res == 0)
30882c705f0SAaron LI T_FAILED("validate_macs_make_mac2");
30982c705f0SAaron LI
31082c705f0SAaron LI /* Check we get OK if mac2 and under load */
311*03c3b87eSAaron LI if (cookie_checker_validate_macs(checker, &cm, message, T_MESSAGE_LEN,
312af6d6403SAaron LI 1, sintosa(&sin)) != 0)
31382c705f0SAaron LI T_FAILED("validate_macs_load_normal_mac2");
31482c705f0SAaron LI
315af6d6403SAaron LI /* Check we get EAGAIN if we munge the source IP. */
31682c705f0SAaron LI sin.sin_addr.s_addr = ~sin.sin_addr.s_addr;
317*03c3b87eSAaron LI if (cookie_checker_validate_macs(checker, &cm, message, T_MESSAGE_LEN,
318af6d6403SAaron LI 1, sintosa(&sin)) != EAGAIN)
31982c705f0SAaron LI T_FAILED("validate_macs_load_spoofip_mac2");
32082c705f0SAaron LI sin.sin_addr.s_addr = ~sin.sin_addr.s_addr;
32182c705f0SAaron LI
32282c705f0SAaron LI /* Check we get OK if mac2 and under load */
323*03c3b87eSAaron LI if (cookie_checker_validate_macs(checker, &cm, message, T_MESSAGE_LEN,
324af6d6403SAaron LI 1, sintosa(&sin)) != 0)
32582c705f0SAaron LI T_FAILED("validate_macs_load_normal_mac2_retry");
32682c705f0SAaron LI
32782c705f0SAaron LI T_PASSED;
32882c705f0SAaron LI ret = true;
329af6d6403SAaron LI
33082c705f0SAaron LI cleanup:
331*03c3b87eSAaron LI cookie_checker_free(checker);
332*03c3b87eSAaron LI cookie_maker_free(maker);
333af6d6403SAaron LI return (ret);
33482c705f0SAaron LI }
33582c705f0SAaron LI
33682c705f0SAaron LI bool
cookie_selftest(void)33782c705f0SAaron LI cookie_selftest(void)
33882c705f0SAaron LI {
33982c705f0SAaron LI bool ret = true;
340af6d6403SAaron LI
34182c705f0SAaron LI ret &= cookie_ratelimit_timings_test();
34282c705f0SAaron LI ret &= cookie_ratelimit_capacity_test();
34382c705f0SAaron LI ret &= cookie_ratelimit_gc_test();
34482c705f0SAaron LI ret &= cookie_mac_test();
345af6d6403SAaron LI
346af6d6403SAaron LI kprintf("%s: %s\n", __func__, ret ? "pass" : "FAIL");
347af6d6403SAaron LI return (ret);
34882c705f0SAaron LI }
349af6d6403SAaron LI
350af6d6403SAaron LI #undef T_MESSAGE_LEN
351af6d6403SAaron LI #undef T_FAILED_ITER
352af6d6403SAaron LI #undef T_FAILED
353af6d6403SAaron LI #undef T_PASSED
354