xref: /linux/tools/testing/selftests/net/netfilter/conntrack_reverse_clash.c (revision f2161d5f1aae21a42b0a64d87e10cb31db423f42)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Needs something like:
4  *
5  * iptables -t nat -A POSTROUTING -o nomatch -j MASQUERADE
6  *
7  * so NAT engine attaches a NAT null-binding to each connection.
8  *
9  * With unmodified kernels, child or parent will exit with
10  * "Port number changed" error, even though no port translation
11  * was requested.
12  */
13 
14 #include <errno.h>
15 #include <stdbool.h>
16 #include <stdint.h>
17 #include <stdio.h>
18 #include <string.h>
19 #include <stdlib.h>
20 #include <time.h>
21 #include <unistd.h>
22 #include <arpa/inet.h>
23 #include <sys/socket.h>
24 #include <sys/wait.h>
25 
26 #define LEN 512
27 #define PORT 56789
28 #define TEST_TIME 5
29 
die(const char * e)30 static void die(const char *e)
31 {
32 	perror(e);
33 	exit(111);
34 }
35 
die_port(const struct sockaddr_in * sin,uint16_t want)36 static void die_port(const struct sockaddr_in *sin, uint16_t want)
37 {
38 	uint16_t got = ntohs(sin->sin_port);
39 	char str[INET_ADDRSTRLEN];
40 
41 	inet_ntop(AF_INET, &sin->sin_addr, str, sizeof(str));
42 
43 	fprintf(stderr, "Port number changed, wanted %d got %d from %s\n", want, got, str);
44 	exit(1);
45 }
46 
udp_socket(void)47 static int udp_socket(void)
48 {
49 	static const struct timeval tv = {
50 		.tv_sec = 1,
51 	};
52 	int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
53 
54 	if (fd < 0)
55 		die("socket");
56 
57 	setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
58 	return fd;
59 }
60 
main(int argc,char * argv[])61 int main(int argc, char *argv[])
62 {
63 	struct sockaddr_in sa1 = {
64 		.sin_family = AF_INET,
65 	};
66 	struct sockaddr_in sa2 = {
67 		.sin_family = AF_INET,
68 	};
69 	int s1, s2, status;
70 	time_t end, now;
71 	socklen_t plen;
72 	char buf[LEN];
73 	bool child;
74 
75 	sa1.sin_port = htons(PORT);
76 	sa2.sin_port = htons(PORT + 1);
77 
78 	s1 = udp_socket();
79 	s2 = udp_socket();
80 
81 	inet_pton(AF_INET, "127.0.0.11", &sa1.sin_addr);
82 	inet_pton(AF_INET, "127.0.0.12", &sa2.sin_addr);
83 
84 	if (bind(s1, (struct sockaddr *)&sa1, sizeof(sa1)) < 0)
85 		die("bind 1");
86 	if (bind(s2, (struct sockaddr *)&sa2, sizeof(sa2)) < 0)
87 		die("bind 2");
88 
89 	child = fork() == 0;
90 
91 	now = time(NULL);
92 	end = now + TEST_TIME;
93 
94 	while (now < end) {
95 		struct sockaddr_in peer;
96 		socklen_t plen = sizeof(peer);
97 
98 		now = time(NULL);
99 
100 		if (child) {
101 			if (sendto(s1, buf, LEN, 0, (struct sockaddr *)&sa2, sizeof(sa2)) != LEN)
102 				continue;
103 
104 			if (recvfrom(s2, buf, LEN, 0, (struct sockaddr *)&peer, &plen) < 0)
105 				die("child recvfrom");
106 
107 			if (peer.sin_port != htons(PORT))
108 				die_port(&peer, PORT);
109 		} else {
110 			if (sendto(s2, buf, LEN, 0, (struct sockaddr *)&sa1, sizeof(sa1)) != LEN)
111 				continue;
112 
113 			if (recvfrom(s1, buf, LEN, 0, (struct sockaddr *)&peer, &plen) < 0)
114 				die("parent recvfrom");
115 
116 			if (peer.sin_port != htons((PORT + 1)))
117 				die_port(&peer, PORT + 1);
118 		}
119 	}
120 
121 	if (child)
122 		return 0;
123 
124 	wait(&status);
125 
126 	if (WIFEXITED(status))
127 		return WEXITSTATUS(status);
128 
129 	return 1;
130 }
131