xref: /dragonfly/sys/net/wg/selftest/cookie.c (revision 03c3b87e)
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