xref: /linux/tools/testing/selftests/net/tcp_ao/icmps-discard.c (revision eed4edda910fe34dfae8c6bfbcf57f4593a54295)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Selftest that verifies that incomping ICMPs are ignored,
4  * the TCP connection stays alive, no hard or soft errors get reported
5  * to the usespace and the counter for ignored ICMPs is updated.
6  *
7  * RFC5925, 7.8:
8  * >> A TCP-AO implementation MUST default to ignore incoming ICMPv4
9  * messages of Type 3 (destination unreachable), Codes 2-4 (protocol
10  * unreachable, port unreachable, and fragmentation needed -- ’hard
11  * errors’), and ICMPv6 Type 1 (destination unreachable), Code 1
12  * (administratively prohibited) and Code 4 (port unreachable) intended
13  * for connections in synchronized states (ESTABLISHED, FIN-WAIT-1, FIN-
14  * WAIT-2, CLOSE-WAIT, CLOSING, LAST-ACK, TIME-WAIT) that match MKTs.
15  *
16  * Author: Dmitry Safonov <dima@arista.com>
17  */
18 #include <inttypes.h>
19 #include <linux/icmp.h>
20 #include <linux/icmpv6.h>
21 #include <linux/ipv6.h>
22 #include <netinet/in.h>
23 #include <netinet/ip.h>
24 #include <sys/socket.h>
25 #include "aolib.h"
26 #include "../../../../include/linux/compiler.h"
27 
28 const size_t packets_nr = 20;
29 const size_t packet_size = 100;
30 const char *tcpao_icmps	= "TCPAODroppedIcmps";
31 
32 #ifdef IPV6_TEST
33 const char *dst_unreach	= "Icmp6InDestUnreachs";
34 const int sk_ip_level	= SOL_IPV6;
35 const int sk_recverr	= IPV6_RECVERR;
36 #else
37 const char *dst_unreach	= "InDestUnreachs";
38 const int sk_ip_level	= SOL_IP;
39 const int sk_recverr	= IP_RECVERR;
40 #endif
41 
42 /* Server is expected to fail with hard error if ::accept_icmp is set */
43 #ifdef TEST_ICMPS_ACCEPT
44 # define test_icmps_fail test_ok
45 # define test_icmps_ok test_fail
46 #else
47 # define test_icmps_fail test_fail
48 # define test_icmps_ok test_ok
49 #endif
50 
51 static void serve_interfered(int sk)
52 {
53 	ssize_t test_quota = packet_size * packets_nr * 10;
54 	uint64_t dest_unreach_a, dest_unreach_b;
55 	uint64_t icmp_ignored_a, icmp_ignored_b;
56 	struct tcp_ao_counters ao_cnt1, ao_cnt2;
57 	bool counter_not_found;
58 	struct netstat *ns_after, *ns_before;
59 	ssize_t bytes;
60 
61 	ns_before = netstat_read();
62 	dest_unreach_a = netstat_get(ns_before, dst_unreach, NULL);
63 	icmp_ignored_a = netstat_get(ns_before, tcpao_icmps, NULL);
64 	if (test_get_tcp_ao_counters(sk, &ao_cnt1))
65 		test_error("test_get_tcp_ao_counters()");
66 	bytes = test_server_run(sk, test_quota, 0);
67 	ns_after = netstat_read();
68 	netstat_print_diff(ns_before, ns_after);
69 	dest_unreach_b = netstat_get(ns_after, dst_unreach, NULL);
70 	icmp_ignored_b = netstat_get(ns_after, tcpao_icmps,
71 					&counter_not_found);
72 	if (test_get_tcp_ao_counters(sk, &ao_cnt2))
73 		test_error("test_get_tcp_ao_counters()");
74 
75 	netstat_free(ns_before);
76 	netstat_free(ns_after);
77 
78 	if (dest_unreach_a >= dest_unreach_b) {
79 		test_fail("%s counter didn't change: %" PRIu64 " >= %" PRIu64,
80 				dst_unreach, dest_unreach_a, dest_unreach_b);
81 		return;
82 	}
83 	test_ok("%s delivered %" PRIu64,
84 		dst_unreach, dest_unreach_b - dest_unreach_a);
85 	if (bytes < 0)
86 		test_icmps_fail("Server failed with %zd: %s", bytes, strerrordesc_np(-bytes));
87 	else
88 		test_icmps_ok("Server survived %zd bytes of traffic", test_quota);
89 	if (counter_not_found) {
90 		test_fail("Not found %s counter", tcpao_icmps);
91 		return;
92 	}
93 #ifdef TEST_ICMPS_ACCEPT
94 	test_tcp_ao_counters_cmp(NULL, &ao_cnt1, &ao_cnt2, TEST_CNT_GOOD);
95 #else
96 	test_tcp_ao_counters_cmp(NULL, &ao_cnt1, &ao_cnt2, TEST_CNT_GOOD | TEST_CNT_AO_DROPPED_ICMP);
97 #endif
98 	if (icmp_ignored_a >= icmp_ignored_b) {
99 		test_icmps_fail("%s counter didn't change: %" PRIu64 " >= %" PRIu64,
100 				tcpao_icmps, icmp_ignored_a, icmp_ignored_b);
101 		return;
102 	}
103 	test_icmps_ok("ICMPs ignored %" PRIu64, icmp_ignored_b - icmp_ignored_a);
104 }
105 
106 static void *server_fn(void *arg)
107 {
108 	int val, sk, lsk;
109 	bool accept_icmps = false;
110 
111 	lsk = test_listen_socket(this_ip_addr, test_server_port, 1);
112 
113 #ifdef TEST_ICMPS_ACCEPT
114 	accept_icmps = true;
115 #endif
116 
117 	if (test_set_ao_flags(lsk, false, accept_icmps))
118 		test_error("setsockopt(TCP_AO_INFO)");
119 
120 	if (test_add_key(lsk, DEFAULT_TEST_PASSWORD, this_ip_dest, -1, 100, 100))
121 		test_error("setsockopt(TCP_AO_ADD_KEY)");
122 	synchronize_threads();
123 
124 	if (test_wait_fd(lsk, TEST_TIMEOUT_SEC, 0))
125 		test_error("test_wait_fd()");
126 
127 	sk = accept(lsk, NULL, NULL);
128 	if (sk < 0)
129 		test_error("accept()");
130 
131 	/* Fail on hard ip errors, such as dest unreachable (RFC1122) */
132 	val = 1;
133 	if (setsockopt(sk, sk_ip_level, sk_recverr, &val, sizeof(val)))
134 		test_error("setsockopt()");
135 
136 	synchronize_threads();
137 
138 	serve_interfered(sk);
139 	return NULL;
140 }
141 
142 static size_t packets_sent;
143 static size_t icmps_sent;
144 
145 static uint32_t checksum4_nofold(void *data, size_t len, uint32_t sum)
146 {
147 	uint16_t *words = data;
148 	size_t i;
149 
150 	for (i = 0; i < len / sizeof(uint16_t); i++)
151 		sum += words[i];
152 	if (len & 1)
153 		sum += ((char *)data)[len - 1];
154 	return sum;
155 }
156 
157 static uint16_t checksum4_fold(void *data, size_t len, uint32_t sum)
158 {
159 	sum = checksum4_nofold(data, len, sum);
160 	while (sum > 0xFFFF)
161 		sum = (sum & 0xFFFF) + (sum >> 16);
162 	return ~sum;
163 }
164 
165 static void set_ip4hdr(struct iphdr *iph, size_t packet_len, int proto,
166 		struct sockaddr_in *src, struct sockaddr_in *dst)
167 {
168 	iph->version	= 4;
169 	iph->ihl	= 5;
170 	iph->tos	= 0;
171 	iph->tot_len	= htons(packet_len);
172 	iph->ttl	= 2;
173 	iph->protocol	= proto;
174 	iph->saddr	= src->sin_addr.s_addr;
175 	iph->daddr	= dst->sin_addr.s_addr;
176 	iph->check	= checksum4_fold((void *)iph, iph->ihl << 1, 0);
177 }
178 
179 static void icmp_interfere4(uint8_t type, uint8_t code, uint32_t rcv_nxt,
180 		struct sockaddr_in *src, struct sockaddr_in *dst)
181 {
182 	int sk = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
183 	struct {
184 		struct iphdr iph;
185 		struct icmphdr icmph;
186 		struct iphdr iphe;
187 		struct {
188 			uint16_t sport;
189 			uint16_t dport;
190 			uint32_t seq;
191 		} tcph;
192 	} packet = {};
193 	size_t packet_len;
194 	ssize_t bytes;
195 
196 	if (sk < 0)
197 		test_error("socket(AF_INET, SOCK_RAW, IPPROTO_RAW)");
198 
199 	packet_len = sizeof(packet);
200 	set_ip4hdr(&packet.iph, packet_len, IPPROTO_ICMP, src, dst);
201 
202 	packet.icmph.type = type;
203 	packet.icmph.code = code;
204 	if (code == ICMP_FRAG_NEEDED) {
205 		randomize_buffer(&packet.icmph.un.frag.mtu,
206 				sizeof(packet.icmph.un.frag.mtu));
207 	}
208 
209 	packet_len = sizeof(packet.iphe) + sizeof(packet.tcph);
210 	set_ip4hdr(&packet.iphe, packet_len, IPPROTO_TCP, dst, src);
211 
212 	packet.tcph.sport = dst->sin_port;
213 	packet.tcph.dport = src->sin_port;
214 	packet.tcph.seq = htonl(rcv_nxt);
215 
216 	packet_len = sizeof(packet) - sizeof(packet.iph);
217 	packet.icmph.checksum = checksum4_fold((void *)&packet.icmph,
218 						packet_len, 0);
219 
220 	bytes = sendto(sk, &packet, sizeof(packet), 0,
221 		       (struct sockaddr *)dst, sizeof(*dst));
222 	if (bytes != sizeof(packet))
223 		test_error("send(): %zd", bytes);
224 	icmps_sent++;
225 
226 	close(sk);
227 }
228 
229 static void set_ip6hdr(struct ipv6hdr *iph, size_t packet_len, int proto,
230 		struct sockaddr_in6 *src, struct sockaddr_in6 *dst)
231 {
232 	iph->version		= 6;
233 	iph->payload_len	= htons(packet_len);
234 	iph->nexthdr		= proto;
235 	iph->hop_limit		= 2;
236 	iph->saddr		= src->sin6_addr;
237 	iph->daddr		= dst->sin6_addr;
238 }
239 
240 static inline uint16_t csum_fold(uint32_t csum)
241 {
242 	uint32_t sum = csum;
243 
244 	sum = (sum & 0xffff) + (sum >> 16);
245 	sum = (sum & 0xffff) + (sum >> 16);
246 	return (uint16_t)~sum;
247 }
248 
249 static inline uint32_t csum_add(uint32_t csum, uint32_t addend)
250 {
251 	uint32_t res = csum;
252 
253 	res += addend;
254 	return res + (res < addend);
255 }
256 
257 noinline uint32_t checksum6_nofold(void *data, size_t len, uint32_t sum)
258 {
259 	uint16_t *words = data;
260 	size_t i;
261 
262 	for (i = 0; i < len / sizeof(uint16_t); i++)
263 		sum = csum_add(sum, words[i]);
264 	if (len & 1)
265 		sum = csum_add(sum, ((char *)data)[len - 1]);
266 	return sum;
267 }
268 
269 noinline uint16_t icmp6_checksum(struct sockaddr_in6 *src,
270 				 struct sockaddr_in6 *dst,
271 				 void *ptr, size_t len, uint8_t proto)
272 {
273 	struct {
274 		struct in6_addr saddr;
275 		struct in6_addr daddr;
276 		uint32_t payload_len;
277 		uint8_t zero[3];
278 		uint8_t nexthdr;
279 	} pseudo_header = {};
280 	uint32_t sum;
281 
282 	pseudo_header.saddr		= src->sin6_addr;
283 	pseudo_header.daddr		= dst->sin6_addr;
284 	pseudo_header.payload_len	= htonl(len);
285 	pseudo_header.nexthdr		= proto;
286 
287 	sum = checksum6_nofold(&pseudo_header, sizeof(pseudo_header), 0);
288 	sum = checksum6_nofold(ptr, len, sum);
289 
290 	return csum_fold(sum);
291 }
292 
293 static void icmp6_interfere(int type, int code, uint32_t rcv_nxt,
294 		struct sockaddr_in6 *src, struct sockaddr_in6 *dst)
295 {
296 	int sk = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW);
297 	struct sockaddr_in6 dst_raw = *dst;
298 	struct {
299 		struct ipv6hdr iph;
300 		struct icmp6hdr icmph;
301 		struct ipv6hdr iphe;
302 		struct {
303 			uint16_t sport;
304 			uint16_t dport;
305 			uint32_t seq;
306 		} tcph;
307 	} packet = {};
308 	size_t packet_len;
309 	ssize_t bytes;
310 
311 
312 	if (sk < 0)
313 		test_error("socket(AF_INET6, SOCK_RAW, IPPROTO_RAW)");
314 
315 	packet_len = sizeof(packet) - sizeof(packet.iph);
316 	set_ip6hdr(&packet.iph, packet_len, IPPROTO_ICMPV6, src, dst);
317 
318 	packet.icmph.icmp6_type = type;
319 	packet.icmph.icmp6_code = code;
320 
321 	packet_len = sizeof(packet.iphe) + sizeof(packet.tcph);
322 	set_ip6hdr(&packet.iphe, packet_len, IPPROTO_TCP, dst, src);
323 
324 	packet.tcph.sport = dst->sin6_port;
325 	packet.tcph.dport = src->sin6_port;
326 	packet.tcph.seq = htonl(rcv_nxt);
327 
328 	packet_len = sizeof(packet) - sizeof(packet.iph);
329 
330 	packet.icmph.icmp6_cksum = icmp6_checksum(src, dst,
331 			(void *)&packet.icmph, packet_len, IPPROTO_ICMPV6);
332 
333 	dst_raw.sin6_port = htons(IPPROTO_RAW);
334 	bytes = sendto(sk, &packet, sizeof(packet), 0,
335 		       (struct sockaddr *)&dst_raw, sizeof(dst_raw));
336 	if (bytes != sizeof(packet))
337 		test_error("send(): %zd", bytes);
338 	icmps_sent++;
339 
340 	close(sk);
341 }
342 
343 static uint32_t get_rcv_nxt(int sk)
344 {
345 	int val = TCP_REPAIR_ON;
346 	uint32_t ret;
347 	socklen_t sz = sizeof(ret);
348 
349 	if (setsockopt(sk, SOL_TCP, TCP_REPAIR, &val, sizeof(val)))
350 		test_error("setsockopt(TCP_REPAIR)");
351 	val = TCP_RECV_QUEUE;
352 	if (setsockopt(sk, SOL_TCP, TCP_REPAIR_QUEUE, &val, sizeof(val)))
353 		test_error("setsockopt(TCP_REPAIR_QUEUE)");
354 	if (getsockopt(sk, SOL_TCP, TCP_QUEUE_SEQ, &ret, &sz))
355 		test_error("getsockopt(TCP_QUEUE_SEQ)");
356 	val = TCP_REPAIR_OFF_NO_WP;
357 	if (setsockopt(sk, SOL_TCP, TCP_REPAIR, &val, sizeof(val)))
358 		test_error("setsockopt(TCP_REPAIR)");
359 	return ret;
360 }
361 
362 static void icmp_interfere(const size_t nr, uint32_t rcv_nxt, void *src, void *dst)
363 {
364 	struct sockaddr_in *saddr4 = src;
365 	struct sockaddr_in *daddr4 = dst;
366 	struct sockaddr_in6 *saddr6 = src;
367 	struct sockaddr_in6 *daddr6 = dst;
368 	size_t i;
369 
370 	if (saddr4->sin_family != daddr4->sin_family)
371 		test_error("Different address families");
372 
373 	for (i = 0; i < nr; i++) {
374 		if (saddr4->sin_family == AF_INET) {
375 			icmp_interfere4(ICMP_DEST_UNREACH, ICMP_PROT_UNREACH,
376 					rcv_nxt, saddr4, daddr4);
377 			icmp_interfere4(ICMP_DEST_UNREACH, ICMP_PORT_UNREACH,
378 					rcv_nxt, saddr4, daddr4);
379 			icmp_interfere4(ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
380 					rcv_nxt, saddr4, daddr4);
381 			icmps_sent += 3;
382 		} else if (saddr4->sin_family == AF_INET6) {
383 			icmp6_interfere(ICMPV6_DEST_UNREACH,
384 					ICMPV6_ADM_PROHIBITED,
385 					rcv_nxt, saddr6, daddr6);
386 			icmp6_interfere(ICMPV6_DEST_UNREACH,
387 					ICMPV6_PORT_UNREACH,
388 					rcv_nxt, saddr6, daddr6);
389 			icmps_sent += 2;
390 		} else {
391 			test_error("Not ip address family");
392 		}
393 	}
394 }
395 
396 static void send_interfered(int sk)
397 {
398 	const unsigned int timeout = TEST_TIMEOUT_SEC;
399 	struct sockaddr_in6 src, dst;
400 	socklen_t addr_sz;
401 
402 	addr_sz = sizeof(src);
403 	if (getsockname(sk, &src, &addr_sz))
404 		test_error("getsockname()");
405 	addr_sz = sizeof(dst);
406 	if (getpeername(sk, &dst, &addr_sz))
407 		test_error("getpeername()");
408 
409 	while (1) {
410 		uint32_t rcv_nxt;
411 
412 		if (test_client_verify(sk, packet_size, packets_nr, timeout)) {
413 			test_fail("client: connection is broken");
414 			return;
415 		}
416 		packets_sent += packets_nr;
417 		rcv_nxt = get_rcv_nxt(sk);
418 		icmp_interfere(packets_nr, rcv_nxt, (void *)&src, (void *)&dst);
419 	}
420 }
421 
422 static void *client_fn(void *arg)
423 {
424 	int sk = socket(test_family, SOCK_STREAM, IPPROTO_TCP);
425 
426 	if (sk < 0)
427 		test_error("socket()");
428 
429 	if (test_add_key(sk, DEFAULT_TEST_PASSWORD, this_ip_dest, -1, 100, 100))
430 		test_error("setsockopt(TCP_AO_ADD_KEY)");
431 
432 	synchronize_threads();
433 	if (test_connect_socket(sk, this_ip_dest, test_server_port) <= 0)
434 		test_error("failed to connect()");
435 	synchronize_threads();
436 
437 	send_interfered(sk);
438 
439 	/* Not expecting client to quit */
440 	test_fail("client disconnected");
441 
442 	return NULL;
443 }
444 
445 int main(int argc, char *argv[])
446 {
447 	test_init(3, server_fn, client_fn);
448 	return 0;
449 }
450