xref: /linux/tools/testing/selftests/net/netfilter/udpclash.c (revision 6832a9317eee280117cd695fa885b2b7a7a38daf)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 /* Usage: ./udpclash <IP> <PORT>
4  *
5  * Emit THREAD_COUNT UDP packets sharing the same saddr:daddr pair.
6  *
7  * This mimics DNS resolver libraries that emit A and AAAA requests
8  * in parallel.
9  *
10  * This exercises conntrack clash resolution logic added and later
11  * refined in
12  *
13  *  71d8c47fc653 ("netfilter: conntrack: introduce clash resolution on insertion race")
14  *  ed07d9a021df ("netfilter: nf_conntrack: resolve clash for matching conntracks")
15  *  6a757c07e51f ("netfilter: conntrack: allow insertion of clashing entries")
16  */
17 #include <stdio.h>
18 #include <string.h>
19 #include <stdlib.h>
20 #include <unistd.h>
21 #include <arpa/inet.h>
22 #include <sys/socket.h>
23 #include <pthread.h>
24 
25 #define THREAD_COUNT 128
26 
27 struct thread_args {
28 	const struct sockaddr_in *si_remote;
29 	int sockfd;
30 };
31 
32 static int wait = 1;
33 
thread_main(void * varg)34 static void *thread_main(void *varg)
35 {
36 	const struct sockaddr_in *si_remote;
37 	const struct thread_args *args = varg;
38 	static const char msg[] = "foo";
39 
40 	si_remote = args->si_remote;
41 
42 	while (wait == 1)
43 		;
44 
45 	if (sendto(args->sockfd, msg, strlen(msg), MSG_NOSIGNAL,
46 		   (struct sockaddr *)si_remote, sizeof(*si_remote)) < 0)
47 		exit(111);
48 
49 	return varg;
50 }
51 
run_test(int fd,const struct sockaddr_in * si_remote)52 static int run_test(int fd, const struct sockaddr_in *si_remote)
53 {
54 	struct thread_args thread_args = {
55 		.si_remote = si_remote,
56 		.sockfd = fd,
57 	};
58 	pthread_t *tid = calloc(THREAD_COUNT, sizeof(pthread_t));
59 	unsigned int repl_count = 0, timeout = 0;
60 	int i;
61 
62 	if (!tid) {
63 		perror("calloc");
64 		return 1;
65 	}
66 
67 	for (i = 0; i < THREAD_COUNT; i++) {
68 		int err = pthread_create(&tid[i], NULL, &thread_main, &thread_args);
69 
70 		if (err != 0) {
71 			perror("pthread_create");
72 			exit(1);
73 		}
74 	}
75 
76 	wait = 0;
77 
78 	for (i = 0; i < THREAD_COUNT; i++)
79 		pthread_join(tid[i], NULL);
80 
81 	while (repl_count < THREAD_COUNT) {
82 		struct sockaddr_in si_repl;
83 		socklen_t si_repl_len = sizeof(si_repl);
84 		char repl[512];
85 		ssize_t ret;
86 
87 		ret = recvfrom(fd, repl, sizeof(repl), MSG_NOSIGNAL,
88 			       (struct sockaddr *) &si_repl, &si_repl_len);
89 		if (ret < 0) {
90 			if (timeout++ > 5000) {
91 				fputs("timed out while waiting for reply from thread\n", stderr);
92 				break;
93 			}
94 
95 			/* give reply time to pass though the stack */
96 			usleep(1000);
97 			continue;
98 		}
99 
100 		if (si_repl_len != sizeof(*si_remote)) {
101 			fprintf(stderr, "warning: reply has unexpected repl_len %d vs %d\n",
102 				(int)si_repl_len, (int)sizeof(si_repl));
103 		} else if (si_remote->sin_addr.s_addr != si_repl.sin_addr.s_addr ||
104 			si_remote->sin_port != si_repl.sin_port) {
105 			char a[64], b[64];
106 
107 			inet_ntop(AF_INET, &si_remote->sin_addr, a, sizeof(a));
108 			inet_ntop(AF_INET, &si_repl.sin_addr, b, sizeof(b));
109 
110 			fprintf(stderr, "reply from wrong source: want %s:%d got %s:%d\n",
111 				a, ntohs(si_remote->sin_port), b, ntohs(si_repl.sin_port));
112 		}
113 
114 		repl_count++;
115 	}
116 
117 	printf("got %d of %d replies\n", repl_count, THREAD_COUNT);
118 
119 	free(tid);
120 
121 	return repl_count == THREAD_COUNT ? 0 : 1;
122 }
123 
main(int argc,char * argv[])124 int main(int argc, char *argv[])
125 {
126 	struct sockaddr_in si_local = {
127 		.sin_family = AF_INET,
128 	};
129 	struct sockaddr_in si_remote = {
130 		.sin_family = AF_INET,
131 	};
132 	int fd, ret;
133 
134 	if (argc < 3) {
135 		fputs("Usage: send_udp <daddr> <dport>\n", stderr);
136 		return 1;
137 	}
138 
139 	si_remote.sin_port = htons(atoi(argv[2]));
140 	si_remote.sin_addr.s_addr = inet_addr(argv[1]);
141 
142 	fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, IPPROTO_UDP);
143 	if (fd < 0) {
144 		perror("socket");
145 		return 1;
146 	}
147 
148 	if (bind(fd, (struct sockaddr *)&si_local, sizeof(si_local)) < 0) {
149 		perror("bind");
150 		return 1;
151 	}
152 
153 	ret = run_test(fd, &si_remote);
154 
155 	close(fd);
156 
157 	return ret;
158 }
159