-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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 <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
- Loading branch information
Florian Westphal
authored and
Pablo Neira Ayuso
committed
Sep 26, 2024
1 parent
a4e6a10
commit a57856c
Showing
3 changed files
with
178 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
125 changes: 125 additions & 0 deletions
125
tools/testing/selftests/net/netfilter/conntrack_reverse_clash.c
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
// SPDX-License-Identifier: GPL-2.0 | ||
/* | ||
* Needs something like: | ||
* | ||
* iptables -t nat -A POSTROUTING -o nomatch -j MASQUERADE | ||
* | ||
* so NAT engine attaches a NAT null-binding to each connection. | ||
* | ||
* With unmodified kernels, child or parent will exit with | ||
* "Port number changed" error, even though no port translation | ||
* was requested. | ||
*/ | ||
|
||
#include <errno.h> | ||
#include <stdbool.h> | ||
#include <stdint.h> | ||
#include <stdio.h> | ||
#include <string.h> | ||
#include <stdlib.h> | ||
#include <time.h> | ||
#include <unistd.h> | ||
#include <arpa/inet.h> | ||
#include <sys/socket.h> | ||
#include <sys/wait.h> | ||
|
||
#define LEN 512 | ||
#define PORT 56789 | ||
#define TEST_TIME 5 | ||
|
||
static void die(const char *e) | ||
{ | ||
perror(e); | ||
exit(111); | ||
} | ||
|
||
static void die_port(uint16_t got, uint16_t want) | ||
{ | ||
fprintf(stderr, "Port number changed, wanted %d got %d\n", want, ntohs(got)); | ||
exit(1); | ||
} | ||
|
||
static int udp_socket(void) | ||
{ | ||
static const struct timeval tv = { | ||
.tv_sec = 1, | ||
}; | ||
int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); | ||
|
||
if (fd < 0) | ||
die("socket"); | ||
|
||
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); | ||
return fd; | ||
} | ||
|
||
int main(int argc, char *argv[]) | ||
{ | ||
struct sockaddr_in sa1 = { | ||
.sin_family = AF_INET, | ||
}; | ||
struct sockaddr_in sa2 = { | ||
.sin_family = AF_INET, | ||
}; | ||
int s1, s2, status; | ||
time_t end, now; | ||
socklen_t plen; | ||
char buf[LEN]; | ||
bool child; | ||
|
||
sa1.sin_port = htons(PORT); | ||
sa2.sin_port = htons(PORT + 1); | ||
|
||
s1 = udp_socket(); | ||
s2 = udp_socket(); | ||
|
||
inet_pton(AF_INET, "127.0.0.11", &sa1.sin_addr); | ||
inet_pton(AF_INET, "127.0.0.12", &sa2.sin_addr); | ||
|
||
if (bind(s1, (struct sockaddr *)&sa1, sizeof(sa1)) < 0) | ||
die("bind 1"); | ||
if (bind(s2, (struct sockaddr *)&sa2, sizeof(sa2)) < 0) | ||
die("bind 2"); | ||
|
||
child = fork() == 0; | ||
|
||
now = time(NULL); | ||
end = now + TEST_TIME; | ||
|
||
while (now < end) { | ||
struct sockaddr_in peer; | ||
socklen_t plen = sizeof(peer); | ||
|
||
now = time(NULL); | ||
|
||
if (child) { | ||
if (sendto(s1, buf, LEN, 0, (struct sockaddr *)&sa2, sizeof(sa2)) != LEN) | ||
continue; | ||
|
||
if (recvfrom(s2, buf, LEN, 0, (struct sockaddr *)&peer, &plen) < 0) | ||
die("child recvfrom"); | ||
|
||
if (peer.sin_port != htons(PORT)) | ||
die_port(peer.sin_port, PORT); | ||
} else { | ||
if (sendto(s2, buf, LEN, 0, (struct sockaddr *)&sa1, sizeof(sa1)) != LEN) | ||
continue; | ||
|
||
if (recvfrom(s1, buf, LEN, 0, (struct sockaddr *)&peer, &plen) < 0) | ||
die("parent recvfrom"); | ||
|
||
if (peer.sin_port != htons((PORT + 1))) | ||
die_port(peer.sin_port, PORT + 1); | ||
} | ||
} | ||
|
||
if (child) | ||
return 0; | ||
|
||
wait(&status); | ||
|
||
if (WIFEXITED(status)) | ||
return WEXITSTATUS(status); | ||
|
||
return 1; | ||
} |
51 changes: 51 additions & 0 deletions
51
tools/testing/selftests/net/netfilter/conntrack_reverse_clash.sh
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
#!/bin/bash | ||
# SPDX-License-Identifier: GPL-2.0 | ||
|
||
source lib.sh | ||
|
||
cleanup() | ||
{ | ||
cleanup_all_ns | ||
} | ||
|
||
checktool "nft --version" "run test without nft" | ||
checktool "conntrack --version" "run test without conntrack" | ||
|
||
trap cleanup EXIT | ||
|
||
setup_ns ns0 | ||
|
||
# make loopback connections get nat null bindings assigned | ||
ip netns exec "$ns0" nft -f - <<EOF | ||
table ip nat { | ||
chain POSTROUTING { | ||
type nat hook postrouting priority srcnat; policy accept; | ||
oifname "nomatch" counter packets 0 bytes 0 masquerade | ||
} | ||
} | ||
EOF | ||
|
||
do_flush() | ||
{ | ||
local end | ||
local now | ||
|
||
now=$(date +%s) | ||
end=$((now + 5)) | ||
|
||
while [ $now -lt $end ];do | ||
ip netns exec "$ns0" conntrack -F 2>/dev/null | ||
now=$(date +%s) | ||
done | ||
} | ||
|
||
do_flush & | ||
|
||
if ip netns exec "$ns0" ./conntrack_reverse_clash; then | ||
echo "PASS: No SNAT performed for null bindings" | ||
else | ||
echo "ERROR: SNAT performed without any matching snat rule" | ||
exit 1 | ||
fi | ||
|
||
exit 0 |