Skip to content

Commit a57856c

Browse files
Florian Westphalummakynes
authored andcommitted
selftests: netfilter: add reverse-clash resolution test case
Add test program that is sending UDP packets in both directions and check that packets arrive without source port modification. Signed-off-by: Florian Westphal <[email protected]> Signed-off-by: Pablo Neira Ayuso <[email protected]>
1 parent a4e6a10 commit a57856c

File tree

3 files changed

+178
-0
lines changed

3 files changed

+178
-0
lines changed

tools/testing/selftests/net/netfilter/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ TEST_PROGS += conntrack_ipip_mtu.sh
1313
TEST_PROGS += conntrack_tcp_unreplied.sh
1414
TEST_PROGS += conntrack_sctp_collision.sh
1515
TEST_PROGS += conntrack_vrf.sh
16+
TEST_PROGS += conntrack_reverse_clash.sh
1617
TEST_PROGS += ipvs.sh
1718
TEST_PROGS += nf_conntrack_packetdrill.sh
1819
TEST_PROGS += nf_nat_edemux.sh
@@ -36,6 +37,7 @@ TEST_GEN_PROGS = conntrack_dump_flush
3637

3738
TEST_GEN_FILES = audit_logread
3839
TEST_GEN_FILES += connect_close nf_queue
40+
TEST_GEN_FILES += conntrack_reverse_clash
3941
TEST_GEN_FILES += sctp_collision
4042

4143
include ../../lib.mk
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
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+
30+
static void die(const char *e)
31+
{
32+
perror(e);
33+
exit(111);
34+
}
35+
36+
static void die_port(uint16_t got, uint16_t want)
37+
{
38+
fprintf(stderr, "Port number changed, wanted %d got %d\n", want, ntohs(got));
39+
exit(1);
40+
}
41+
42+
static int udp_socket(void)
43+
{
44+
static const struct timeval tv = {
45+
.tv_sec = 1,
46+
};
47+
int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
48+
49+
if (fd < 0)
50+
die("socket");
51+
52+
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
53+
return fd;
54+
}
55+
56+
int main(int argc, char *argv[])
57+
{
58+
struct sockaddr_in sa1 = {
59+
.sin_family = AF_INET,
60+
};
61+
struct sockaddr_in sa2 = {
62+
.sin_family = AF_INET,
63+
};
64+
int s1, s2, status;
65+
time_t end, now;
66+
socklen_t plen;
67+
char buf[LEN];
68+
bool child;
69+
70+
sa1.sin_port = htons(PORT);
71+
sa2.sin_port = htons(PORT + 1);
72+
73+
s1 = udp_socket();
74+
s2 = udp_socket();
75+
76+
inet_pton(AF_INET, "127.0.0.11", &sa1.sin_addr);
77+
inet_pton(AF_INET, "127.0.0.12", &sa2.sin_addr);
78+
79+
if (bind(s1, (struct sockaddr *)&sa1, sizeof(sa1)) < 0)
80+
die("bind 1");
81+
if (bind(s2, (struct sockaddr *)&sa2, sizeof(sa2)) < 0)
82+
die("bind 2");
83+
84+
child = fork() == 0;
85+
86+
now = time(NULL);
87+
end = now + TEST_TIME;
88+
89+
while (now < end) {
90+
struct sockaddr_in peer;
91+
socklen_t plen = sizeof(peer);
92+
93+
now = time(NULL);
94+
95+
if (child) {
96+
if (sendto(s1, buf, LEN, 0, (struct sockaddr *)&sa2, sizeof(sa2)) != LEN)
97+
continue;
98+
99+
if (recvfrom(s2, buf, LEN, 0, (struct sockaddr *)&peer, &plen) < 0)
100+
die("child recvfrom");
101+
102+
if (peer.sin_port != htons(PORT))
103+
die_port(peer.sin_port, PORT);
104+
} else {
105+
if (sendto(s2, buf, LEN, 0, (struct sockaddr *)&sa1, sizeof(sa1)) != LEN)
106+
continue;
107+
108+
if (recvfrom(s1, buf, LEN, 0, (struct sockaddr *)&peer, &plen) < 0)
109+
die("parent recvfrom");
110+
111+
if (peer.sin_port != htons((PORT + 1)))
112+
die_port(peer.sin_port, PORT + 1);
113+
}
114+
}
115+
116+
if (child)
117+
return 0;
118+
119+
wait(&status);
120+
121+
if (WIFEXITED(status))
122+
return WEXITSTATUS(status);
123+
124+
return 1;
125+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#!/bin/bash
2+
# SPDX-License-Identifier: GPL-2.0
3+
4+
source lib.sh
5+
6+
cleanup()
7+
{
8+
cleanup_all_ns
9+
}
10+
11+
checktool "nft --version" "run test without nft"
12+
checktool "conntrack --version" "run test without conntrack"
13+
14+
trap cleanup EXIT
15+
16+
setup_ns ns0
17+
18+
# make loopback connections get nat null bindings assigned
19+
ip netns exec "$ns0" nft -f - <<EOF
20+
table ip nat {
21+
chain POSTROUTING {
22+
type nat hook postrouting priority srcnat; policy accept;
23+
oifname "nomatch" counter packets 0 bytes 0 masquerade
24+
}
25+
}
26+
EOF
27+
28+
do_flush()
29+
{
30+
local end
31+
local now
32+
33+
now=$(date +%s)
34+
end=$((now + 5))
35+
36+
while [ $now -lt $end ];do
37+
ip netns exec "$ns0" conntrack -F 2>/dev/null
38+
now=$(date +%s)
39+
done
40+
}
41+
42+
do_flush &
43+
44+
if ip netns exec "$ns0" ./conntrack_reverse_clash; then
45+
echo "PASS: No SNAT performed for null bindings"
46+
else
47+
echo "ERROR: SNAT performed without any matching snat rule"
48+
exit 1
49+
fi
50+
51+
exit 0

0 commit comments

Comments
 (0)