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