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