1 // SPDX-License-Identifier: GPL-2.0
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.
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.
16 * Author: Dmitry Safonov <dima@arista.com>
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>
26 #include "../../../../include/linux/compiler.h"
28 const size_t packets_nr
= 20;
29 const size_t packet_size
= 100;
30 const char *tcpao_icmps
= "TCPAODroppedIcmps";
33 const char *dst_unreach
= "Icmp6InDestUnreachs";
34 const int sk_ip_level
= SOL_IPV6
;
35 const int sk_recverr
= IPV6_RECVERR
;
37 const char *dst_unreach
= "InDestUnreachs";
38 const int sk_ip_level
= SOL_IP
;
39 const int sk_recverr
= IP_RECVERR
;
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
47 # define test_icmps_fail test_fail
48 # define test_icmps_ok test_ok
51 static void serve_interfered(int sk
)
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
;
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
,
72 if (test_get_tcp_ao_counters(sk
, &ao_cnt2
))
73 test_error("test_get_tcp_ao_counters()");
75 netstat_free(ns_before
);
76 netstat_free(ns_after
);
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
);
83 test_ok("%s delivered %" PRIu64
,
84 dst_unreach
, dest_unreach_b
- dest_unreach_a
);
86 test_icmps_fail("Server failed with %zd: %s", bytes
, strerrordesc_np(-bytes
));
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
);
93 #ifdef TEST_ICMPS_ACCEPT
94 test_tcp_ao_counters_cmp(NULL
, &ao_cnt1
, &ao_cnt2
, TEST_CNT_GOOD
);
96 test_tcp_ao_counters_cmp(NULL
, &ao_cnt1
, &ao_cnt2
, TEST_CNT_GOOD
| TEST_CNT_AO_DROPPED_ICMP
);
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
);
103 test_icmps_ok("ICMPs ignored %" PRIu64
, icmp_ignored_b
- icmp_ignored_a
);
106 static void *server_fn(void *arg
)
109 bool accept_icmps
= false;
111 lsk
= test_listen_socket(this_ip_addr
, test_server_port
, 1);
113 #ifdef TEST_ICMPS_ACCEPT
117 if (test_set_ao_flags(lsk
, false, accept_icmps
))
118 test_error("setsockopt(TCP_AO_INFO)");
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();
124 if (test_wait_fd(lsk
, TEST_TIMEOUT_SEC
, 0))
125 test_error("test_wait_fd()");
127 sk
= accept(lsk
, NULL
, NULL
);
129 test_error("accept()");
131 /* Fail on hard ip errors, such as dest unreachable (RFC1122) */
133 if (setsockopt(sk
, sk_ip_level
, sk_recverr
, &val
, sizeof(val
)))
134 test_error("setsockopt()");
136 synchronize_threads();
138 serve_interfered(sk
);
142 static size_t packets_sent
;
143 static size_t icmps_sent
;
145 static uint32_t checksum4_nofold(void *data
, size_t len
, uint32_t sum
)
147 uint16_t *words
= data
;
150 for (i
= 0; i
< len
/ sizeof(uint16_t); i
++)
153 sum
+= ((char *)data
)[len
- 1];
157 static uint16_t checksum4_fold(void *data
, size_t len
, uint32_t sum
)
159 sum
= checksum4_nofold(data
, len
, sum
);
161 sum
= (sum
& 0xFFFF) + (sum
>> 16);
165 static void set_ip4hdr(struct iphdr
*iph
, size_t packet_len
, int proto
,
166 struct sockaddr_in
*src
, struct sockaddr_in
*dst
)
171 iph
->tot_len
= htons(packet_len
);
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);
179 static void icmp_interfere4(uint8_t type
, uint8_t code
, uint32_t rcv_nxt
,
180 struct sockaddr_in
*src
, struct sockaddr_in
*dst
)
182 int sk
= socket(AF_INET
, SOCK_RAW
, IPPROTO_RAW
);
185 struct icmphdr icmph
;
197 test_error("socket(AF_INET, SOCK_RAW, IPPROTO_RAW)");
199 packet_len
= sizeof(packet
);
200 set_ip4hdr(&packet
.iph
, packet_len
, IPPROTO_ICMP
, src
, dst
);
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
));
209 packet_len
= sizeof(packet
.iphe
) + sizeof(packet
.tcph
);
210 set_ip4hdr(&packet
.iphe
, packet_len
, IPPROTO_TCP
, dst
, src
);
212 packet
.tcph
.sport
= dst
->sin_port
;
213 packet
.tcph
.dport
= src
->sin_port
;
214 packet
.tcph
.seq
= htonl(rcv_nxt
);
216 packet_len
= sizeof(packet
) - sizeof(packet
.iph
);
217 packet
.icmph
.checksum
= checksum4_fold((void *)&packet
.icmph
,
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
);
229 static void set_ip6hdr(struct ipv6hdr
*iph
, size_t packet_len
, int proto
,
230 struct sockaddr_in6
*src
, struct sockaddr_in6
*dst
)
233 iph
->payload_len
= htons(packet_len
);
234 iph
->nexthdr
= proto
;
236 iph
->saddr
= src
->sin6_addr
;
237 iph
->daddr
= dst
->sin6_addr
;
240 static inline uint16_t csum_fold(uint32_t csum
)
244 sum
= (sum
& 0xffff) + (sum
>> 16);
245 sum
= (sum
& 0xffff) + (sum
>> 16);
246 return (uint16_t)~sum
;
249 static inline uint32_t csum_add(uint32_t csum
, uint32_t addend
)
254 return res
+ (res
< addend
);
257 noinline
uint32_t checksum6_nofold(void *data
, size_t len
, uint32_t sum
)
259 uint16_t *words
= data
;
262 for (i
= 0; i
< len
/ sizeof(uint16_t); i
++)
263 sum
= csum_add(sum
, words
[i
]);
265 sum
= csum_add(sum
, ((char *)data
)[len
- 1]);
269 noinline
uint16_t icmp6_checksum(struct sockaddr_in6
*src
,
270 struct sockaddr_in6
*dst
,
271 void *ptr
, size_t len
, uint8_t proto
)
274 struct in6_addr saddr
;
275 struct in6_addr daddr
;
276 uint32_t payload_len
;
279 } pseudo_header
= {};
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
;
287 sum
= checksum6_nofold(&pseudo_header
, sizeof(pseudo_header
), 0);
288 sum
= checksum6_nofold(ptr
, len
, sum
);
290 return csum_fold(sum
);
293 static void icmp6_interfere(int type
, int code
, uint32_t rcv_nxt
,
294 struct sockaddr_in6
*src
, struct sockaddr_in6
*dst
)
296 int sk
= socket(AF_INET6
, SOCK_RAW
, IPPROTO_RAW
);
297 struct sockaddr_in6 dst_raw
= *dst
;
300 struct icmp6hdr icmph
;
313 test_error("socket(AF_INET6, SOCK_RAW, IPPROTO_RAW)");
315 packet_len
= sizeof(packet
) - sizeof(packet
.iph
);
316 set_ip6hdr(&packet
.iph
, packet_len
, IPPROTO_ICMPV6
, src
, dst
);
318 packet
.icmph
.icmp6_type
= type
;
319 packet
.icmph
.icmp6_code
= code
;
321 packet_len
= sizeof(packet
.iphe
) + sizeof(packet
.tcph
);
322 set_ip6hdr(&packet
.iphe
, packet_len
, IPPROTO_TCP
, dst
, src
);
324 packet
.tcph
.sport
= dst
->sin6_port
;
325 packet
.tcph
.dport
= src
->sin6_port
;
326 packet
.tcph
.seq
= htonl(rcv_nxt
);
328 packet_len
= sizeof(packet
) - sizeof(packet
.iph
);
330 packet
.icmph
.icmp6_cksum
= icmp6_checksum(src
, dst
,
331 (void *)&packet
.icmph
, packet_len
, IPPROTO_ICMPV6
);
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
);
343 static uint32_t get_rcv_nxt(int sk
)
345 int val
= TCP_REPAIR_ON
;
347 socklen_t sz
= sizeof(ret
);
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)");
362 static void icmp_interfere(const size_t nr
, uint32_t rcv_nxt
, void *src
, void *dst
)
364 struct sockaddr_in
*saddr4
= src
;
365 struct sockaddr_in
*daddr4
= dst
;
366 struct sockaddr_in6
*saddr6
= src
;
367 struct sockaddr_in6
*daddr6
= dst
;
370 if (saddr4
->sin_family
!= daddr4
->sin_family
)
371 test_error("Different address families");
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
);
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
,
388 rcv_nxt
, saddr6
, daddr6
);
391 test_error("Not ip address family");
396 static void send_interfered(int sk
)
398 const unsigned int timeout
= TEST_TIMEOUT_SEC
;
399 struct sockaddr_in6 src
, dst
;
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()");
412 if (test_client_verify(sk
, packet_size
, packets_nr
, timeout
)) {
413 test_fail("client: connection is broken");
416 packets_sent
+= packets_nr
;
417 rcv_nxt
= get_rcv_nxt(sk
);
418 icmp_interfere(packets_nr
, rcv_nxt
, (void *)&src
, (void *)&dst
);
422 static void *client_fn(void *arg
)
424 int sk
= socket(test_family
, SOCK_STREAM
, IPPROTO_TCP
);
427 test_error("socket()");
429 if (test_add_key(sk
, DEFAULT_TEST_PASSWORD
, this_ip_dest
, -1, 100, 100))
430 test_error("setsockopt(TCP_AO_ADD_KEY)");
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();
439 /* Not expecting client to quit */
440 test_fail("client disconnected");
445 int main(int argc
, char *argv
[])
447 test_init(4, server_fn
, client_fn
);