1f08c3bdfSopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
2f08c3bdfSopenharmony_ci/*
3f08c3bdfSopenharmony_ci * Copyright (c) 2023 FUJITSU LIMITED. All rights reserved.
4f08c3bdfSopenharmony_ci * Copyright (c) International Business Machines  Corp., 2001
5f08c3bdfSopenharmony_ci * Author: David L Stevens
6f08c3bdfSopenharmony_ci */
7f08c3bdfSopenharmony_ci
8f08c3bdfSopenharmony_ci/*\
9f08c3bdfSopenharmony_ci * [Description]
10f08c3bdfSopenharmony_ci *
11f08c3bdfSopenharmony_ci * Basic test for ICMP6_FILTER.
12f08c3bdfSopenharmony_ci *
13f08c3bdfSopenharmony_ci * For ICMP6_FILTER usage, refer to: https://man.openbsd.org/icmp6.
14f08c3bdfSopenharmony_ci *
15f08c3bdfSopenharmony_ci * Because of the extra functionality of ICMPv6 in comparison to ICMPv4, a
16f08c3bdfSopenharmony_ci * larger number of messages may be potentially received on an ICMPv6 socket.
17f08c3bdfSopenharmony_ci * Input filters may therefore be used to restrict input to a subset of the
18f08c3bdfSopenharmony_ci * incoming ICMPv6 messages so only interesting messages are returned by the
19f08c3bdfSopenharmony_ci * recv(2) family of calls to an application.
20f08c3bdfSopenharmony_ci
21f08c3bdfSopenharmony_ci * The icmp6_filter structure may be used to refine the input message set
22f08c3bdfSopenharmony_ci * according to the ICMPv6 type. By default, all messages types are allowed
23f08c3bdfSopenharmony_ci * on newly created raw ICMPv6 sockets. The following macros may be used to
24f08c3bdfSopenharmony_ci * refine the input set, thus being tested:
25f08c3bdfSopenharmony_ci *
26f08c3bdfSopenharmony_ci * void ICMP6_FILTER_SETPASSALL(struct icmp6_filter *filterp)
27f08c3bdfSopenharmony_ci * – Allow all incoming messages. filterp is modified to allow all message types.
28f08c3bdfSopenharmony_ci *
29f08c3bdfSopenharmony_ci * void ICMP6_FILTER_SETBLOCKALL(struct icmp6_filter *filterp)
30f08c3bdfSopenharmony_ci * – Ignore all incoming messages. filterp is modified to ignore all message types.
31f08c3bdfSopenharmony_ci *
32f08c3bdfSopenharmony_ci * void ICMP6_FILTER_SETPASS(int, struct icmp6_filter *filterp)
33f08c3bdfSopenharmony_ci * – Allow ICMPv6 messages with the given type. filterp is modified to allow such
34f08c3bdfSopenharmony_ci * messages.
35f08c3bdfSopenharmony_ci *
36f08c3bdfSopenharmony_ci * void ICMP6_FILTER_SETBLOCK(int, struct icmp6_filter *filterp)
37f08c3bdfSopenharmony_ci * – Ignore ICMPv6 messages with the given type. filterp is modified to ignore
38f08c3bdfSopenharmony_ci * such messages.
39f08c3bdfSopenharmony_ci *
40f08c3bdfSopenharmony_ci * int ICMP6_FILTER_WILLPASS(int, const struct icmp6_filter *filterp)
41f08c3bdfSopenharmony_ci * – Determine if the given filter will allow an ICMPv6 message of the given type.
42f08c3bdfSopenharmony_ci *
43f08c3bdfSopenharmony_ci * int ICMP6_FILTER_WILLBLOCK(int, const struct icmp6_filter *)
44f08c3bdfSopenharmony_ci * – Determine if the given filter will ignore an ICMPv6 message of the given type.
45f08c3bdfSopenharmony_ci *
46f08c3bdfSopenharmony_ci * The getsockopt(2) and setsockopt(2) calls may be used to obtain and install
47f08c3bdfSopenharmony_ci * the filter on ICMPv6 sockets at option level IPPROTO_ICMPV6 and name ICMP6_FILTER
48f08c3bdfSopenharmony_ci * with a pointer to the icmp6_filter structure as the option value.
49f08c3bdfSopenharmony_ci */
50f08c3bdfSopenharmony_ci
51f08c3bdfSopenharmony_ci#include <netinet/icmp6.h>
52f08c3bdfSopenharmony_ci#include "tst_test.h"
53f08c3bdfSopenharmony_ci
54f08c3bdfSopenharmony_cistatic int sall = -1, sf = -1;
55f08c3bdfSopenharmony_ci
56f08c3bdfSopenharmony_cienum filter_macro {
57f08c3bdfSopenharmony_ci	FILTER_SETPASS,
58f08c3bdfSopenharmony_ci	FILTER_SETBLOCK,
59f08c3bdfSopenharmony_ci	FILTER_PASSALL,
60f08c3bdfSopenharmony_ci	FILTER_BLOCKALL,
61f08c3bdfSopenharmony_ci	FILTER_WILLBLOCK,
62f08c3bdfSopenharmony_ci	FILTER_WILLPASS
63f08c3bdfSopenharmony_ci};
64f08c3bdfSopenharmony_ci
65f08c3bdfSopenharmony_ci#define DESC(x, y, z) .tname = "ICMP6_" #x ", send type: " #y ", filter type: " \
66f08c3bdfSopenharmony_ci	#z, .send_type = y, .filter_type = z, .test_macro = x
67f08c3bdfSopenharmony_ci
68f08c3bdfSopenharmony_cistatic struct tcase {
69f08c3bdfSopenharmony_ci	char *tname;
70f08c3bdfSopenharmony_ci	unsigned char send_type;
71f08c3bdfSopenharmony_ci	unsigned char filter_type;
72f08c3bdfSopenharmony_ci	enum filter_macro test_macro;
73f08c3bdfSopenharmony_ci	int pass_packet;
74f08c3bdfSopenharmony_ci} tcases[] = {
75f08c3bdfSopenharmony_ci	{DESC(FILTER_SETPASS, 20, 20), .pass_packet = 1},
76f08c3bdfSopenharmony_ci	{DESC(FILTER_SETPASS, 20, 21)},
77f08c3bdfSopenharmony_ci	{DESC(FILTER_SETBLOCK, 20, 20)},
78f08c3bdfSopenharmony_ci	{DESC(FILTER_SETBLOCK, 20, 21), .pass_packet = 1},
79f08c3bdfSopenharmony_ci	{DESC(FILTER_PASSALL, 20, 20), .pass_packet = 1},
80f08c3bdfSopenharmony_ci	{DESC(FILTER_PASSALL, 21, 0), .pass_packet = 1},
81f08c3bdfSopenharmony_ci	{DESC(FILTER_BLOCKALL, 20, 0)},
82f08c3bdfSopenharmony_ci	{DESC(FILTER_BLOCKALL, 21, 0)},
83f08c3bdfSopenharmony_ci	{DESC(FILTER_WILLBLOCK, 20, 21)},
84f08c3bdfSopenharmony_ci	{DESC(FILTER_WILLBLOCK, 20, 20), .pass_packet = 1},
85f08c3bdfSopenharmony_ci	{DESC(FILTER_WILLPASS, 20, 21)},
86f08c3bdfSopenharmony_ci	{DESC(FILTER_WILLPASS, 22, 22), .pass_packet = 1},
87f08c3bdfSopenharmony_ci};
88f08c3bdfSopenharmony_ci
89f08c3bdfSopenharmony_cistatic void ic6_send(unsigned char type)
90f08c3bdfSopenharmony_ci{
91f08c3bdfSopenharmony_ci	struct sockaddr_in6 sin6;
92f08c3bdfSopenharmony_ci	struct icmp6_hdr ic6;
93f08c3bdfSopenharmony_ci	int s;
94f08c3bdfSopenharmony_ci
95f08c3bdfSopenharmony_ci	s = SAFE_SOCKET(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
96f08c3bdfSopenharmony_ci
97f08c3bdfSopenharmony_ci	memset(&ic6, 0, sizeof(ic6));
98f08c3bdfSopenharmony_ci	ic6.icmp6_type = type;
99f08c3bdfSopenharmony_ci	ic6.icmp6_data32[0] = htonl(getpid());
100f08c3bdfSopenharmony_ci
101f08c3bdfSopenharmony_ci	memset(&sin6, 0, sizeof(sin6));
102f08c3bdfSopenharmony_ci	sin6.sin6_family = AF_INET6;
103f08c3bdfSopenharmony_ci	sin6.sin6_addr = in6addr_loopback;
104f08c3bdfSopenharmony_ci	SAFE_SENDTO(0, s, &ic6, sizeof(ic6), 0, (struct sockaddr *)&sin6, sizeof(sin6));
105f08c3bdfSopenharmony_ci}
106f08c3bdfSopenharmony_ci
107f08c3bdfSopenharmony_cistatic int ic6_recv(void)
108f08c3bdfSopenharmony_ci{
109f08c3bdfSopenharmony_ci	fd_set readfds, readfds_saved;
110f08c3bdfSopenharmony_ci	struct timeval tv;
111f08c3bdfSopenharmony_ci	int maxfd, nfds, gotall, gotone;
112f08c3bdfSopenharmony_ci	unsigned char rbuf[2048];
113f08c3bdfSopenharmony_ci
114f08c3bdfSopenharmony_ci	tv.tv_sec = 0;
115f08c3bdfSopenharmony_ci	tv.tv_usec = 250000;
116f08c3bdfSopenharmony_ci
117f08c3bdfSopenharmony_ci	FD_ZERO(&readfds_saved);
118f08c3bdfSopenharmony_ci	FD_SET(sall, &readfds_saved);
119f08c3bdfSopenharmony_ci	FD_SET(sf, &readfds_saved);
120f08c3bdfSopenharmony_ci	maxfd = MAX(sall, sf);
121f08c3bdfSopenharmony_ci
122f08c3bdfSopenharmony_ci	memcpy(&readfds, &readfds_saved, sizeof(readfds));
123f08c3bdfSopenharmony_ci
124f08c3bdfSopenharmony_ci	gotall = gotone = 0;
125f08c3bdfSopenharmony_ci
126f08c3bdfSopenharmony_ci	while (!gotall || !gotone) {
127f08c3bdfSopenharmony_ci		struct icmp6_hdr *pic6 = (struct icmp6_hdr *)rbuf;
128f08c3bdfSopenharmony_ci
129f08c3bdfSopenharmony_ci		nfds = select(maxfd + 1, &readfds, 0, 0, &tv);
130f08c3bdfSopenharmony_ci		if (nfds == 0)
131f08c3bdfSopenharmony_ci			break;
132f08c3bdfSopenharmony_ci
133f08c3bdfSopenharmony_ci		if (nfds < 0) {
134f08c3bdfSopenharmony_ci			if (errno == EINTR)
135f08c3bdfSopenharmony_ci				continue;
136f08c3bdfSopenharmony_ci			tst_brk(TBROK | TERRNO, "select failed");
137f08c3bdfSopenharmony_ci		}
138f08c3bdfSopenharmony_ci
139f08c3bdfSopenharmony_ci		if (FD_ISSET(sall, &readfds)) {
140f08c3bdfSopenharmony_ci			SAFE_RECV(0, sall, rbuf, sizeof(rbuf), 0);
141f08c3bdfSopenharmony_ci			if (htonl(pic6->icmp6_data32[0]) == (uint32_t)getpid())
142f08c3bdfSopenharmony_ci				gotall = 1;
143f08c3bdfSopenharmony_ci		}
144f08c3bdfSopenharmony_ci
145f08c3bdfSopenharmony_ci		if (FD_ISSET(sf, &readfds)) {
146f08c3bdfSopenharmony_ci			SAFE_RECV(0, sf, rbuf, sizeof(rbuf), 0);
147f08c3bdfSopenharmony_ci			if (htonl(pic6->icmp6_data32[0]) == (uint32_t)getpid())
148f08c3bdfSopenharmony_ci				gotone = 1;
149f08c3bdfSopenharmony_ci		}
150f08c3bdfSopenharmony_ci		memcpy(&readfds, &readfds_saved, sizeof(readfds));
151f08c3bdfSopenharmony_ci	}
152f08c3bdfSopenharmony_ci
153f08c3bdfSopenharmony_ci	if (!gotall) {
154f08c3bdfSopenharmony_ci		tst_res(TFAIL, "recv all time out");
155f08c3bdfSopenharmony_ci		return -1;
156f08c3bdfSopenharmony_ci	}
157f08c3bdfSopenharmony_ci
158f08c3bdfSopenharmony_ci	if (gotone)
159f08c3bdfSopenharmony_ci		return 1;
160f08c3bdfSopenharmony_ci
161f08c3bdfSopenharmony_ci	return 0;
162f08c3bdfSopenharmony_ci}
163f08c3bdfSopenharmony_ci
164f08c3bdfSopenharmony_cistatic void verify_icmp6_filter(unsigned int n)
165f08c3bdfSopenharmony_ci{
166f08c3bdfSopenharmony_ci	struct tcase *tc = &tcases[n];
167f08c3bdfSopenharmony_ci	struct icmp6_filter i6f;
168f08c3bdfSopenharmony_ci	int rc;
169f08c3bdfSopenharmony_ci
170f08c3bdfSopenharmony_ci	tst_res(TINFO, "Testing %s", tc->tname);
171f08c3bdfSopenharmony_ci
172f08c3bdfSopenharmony_ci	switch (tc->test_macro) {
173f08c3bdfSopenharmony_ci	case FILTER_SETPASS:
174f08c3bdfSopenharmony_ci		ICMP6_FILTER_SETBLOCKALL(&i6f);
175f08c3bdfSopenharmony_ci		ICMP6_FILTER_SETPASS(tc->filter_type, &i6f);
176f08c3bdfSopenharmony_ci		break;
177f08c3bdfSopenharmony_ci	case FILTER_PASSALL:
178f08c3bdfSopenharmony_ci		ICMP6_FILTER_SETPASSALL(&i6f);
179f08c3bdfSopenharmony_ci		break;
180f08c3bdfSopenharmony_ci	case FILTER_SETBLOCK:
181f08c3bdfSopenharmony_ci		ICMP6_FILTER_SETPASSALL(&i6f);
182f08c3bdfSopenharmony_ci		ICMP6_FILTER_SETBLOCK(tc->filter_type, &i6f);
183f08c3bdfSopenharmony_ci		break;
184f08c3bdfSopenharmony_ci	case FILTER_BLOCKALL:
185f08c3bdfSopenharmony_ci		ICMP6_FILTER_SETBLOCKALL(&i6f);
186f08c3bdfSopenharmony_ci		break;
187f08c3bdfSopenharmony_ci	case FILTER_WILLBLOCK:
188f08c3bdfSopenharmony_ci		ICMP6_FILTER_SETPASSALL(&i6f);
189f08c3bdfSopenharmony_ci		ICMP6_FILTER_SETBLOCK(tc->filter_type, &i6f);
190f08c3bdfSopenharmony_ci		rc = ICMP6_FILTER_WILLBLOCK(tc->send_type, &i6f);
191f08c3bdfSopenharmony_ci		TST_EXP_EXPR(rc == tc->pass_packet, "%d (%d)", tc->pass_packet, rc);
192f08c3bdfSopenharmony_ci		return;
193f08c3bdfSopenharmony_ci	case FILTER_WILLPASS:
194f08c3bdfSopenharmony_ci		ICMP6_FILTER_SETBLOCKALL(&i6f);
195f08c3bdfSopenharmony_ci		ICMP6_FILTER_SETPASS(tc->filter_type, &i6f);
196f08c3bdfSopenharmony_ci		rc = ICMP6_FILTER_WILLPASS(tc->send_type, &i6f);
197f08c3bdfSopenharmony_ci		TST_EXP_EXPR(rc == tc->pass_packet, "%d (%d)", tc->pass_packet, rc);
198f08c3bdfSopenharmony_ci		return;
199f08c3bdfSopenharmony_ci	default:
200f08c3bdfSopenharmony_ci		tst_brk(TBROK, "unknown test type %d", tc->filter_type);
201f08c3bdfSopenharmony_ci		break;
202f08c3bdfSopenharmony_ci	}
203f08c3bdfSopenharmony_ci
204f08c3bdfSopenharmony_ci	SAFE_SETSOCKOPT(sf, IPPROTO_ICMPV6, ICMP6_FILTER, &i6f, sizeof(i6f));
205f08c3bdfSopenharmony_ci	ic6_send(tc->send_type);
206f08c3bdfSopenharmony_ci
207f08c3bdfSopenharmony_ci	rc = ic6_recv();
208f08c3bdfSopenharmony_ci	if (rc < 0)
209f08c3bdfSopenharmony_ci		return;
210f08c3bdfSopenharmony_ci
211f08c3bdfSopenharmony_ci	TST_EXP_EXPR(rc == tc->pass_packet, "%s packet type %d",
212f08c3bdfSopenharmony_ci				 rc ? "pass" : "block", tc->send_type);
213f08c3bdfSopenharmony_ci}
214f08c3bdfSopenharmony_ci
215f08c3bdfSopenharmony_cistatic void setup(void)
216f08c3bdfSopenharmony_ci{
217f08c3bdfSopenharmony_ci	struct icmp6_filter i6f;
218f08c3bdfSopenharmony_ci
219f08c3bdfSopenharmony_ci	sall = SAFE_SOCKET(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
220f08c3bdfSopenharmony_ci	ICMP6_FILTER_SETPASSALL(&i6f);
221f08c3bdfSopenharmony_ci	SAFE_SETSOCKOPT(sall, IPPROTO_ICMPV6, ICMP6_FILTER, &i6f, sizeof(i6f));
222f08c3bdfSopenharmony_ci
223f08c3bdfSopenharmony_ci	sf = SAFE_SOCKET(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
224f08c3bdfSopenharmony_ci}
225f08c3bdfSopenharmony_ci
226f08c3bdfSopenharmony_cistatic void cleanup(void)
227f08c3bdfSopenharmony_ci{
228f08c3bdfSopenharmony_ci	if (sall > -1)
229f08c3bdfSopenharmony_ci		SAFE_CLOSE(sall);
230f08c3bdfSopenharmony_ci
231f08c3bdfSopenharmony_ci	if (sf > -1)
232f08c3bdfSopenharmony_ci		SAFE_CLOSE(sf);
233f08c3bdfSopenharmony_ci}
234f08c3bdfSopenharmony_ci
235f08c3bdfSopenharmony_cistatic struct tst_test test = {
236f08c3bdfSopenharmony_ci	.needs_root = 1,
237f08c3bdfSopenharmony_ci	.setup = setup,
238f08c3bdfSopenharmony_ci	.cleanup = cleanup,
239f08c3bdfSopenharmony_ci	.test = verify_icmp6_filter,
240f08c3bdfSopenharmony_ci	.tcnt = ARRAY_SIZE(tcases)
241f08c3bdfSopenharmony_ci};
242