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