Skip to content

Commit

Permalink
selftests: netfilter: add reverse-clash resolution test case
Browse files Browse the repository at this point in the history
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
Show file tree
Hide file tree
Showing 3 changed files with 178 additions and 0 deletions.
2 changes: 2 additions & 0 deletions tools/testing/selftests/net/netfilter/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ TEST_PROGS += conntrack_ipip_mtu.sh
TEST_PROGS += conntrack_tcp_unreplied.sh
TEST_PROGS += conntrack_sctp_collision.sh
TEST_PROGS += conntrack_vrf.sh
TEST_PROGS += conntrack_reverse_clash.sh
TEST_PROGS += ipvs.sh
TEST_PROGS += nf_conntrack_packetdrill.sh
TEST_PROGS += nf_nat_edemux.sh
Expand All @@ -36,6 +37,7 @@ TEST_GEN_PROGS = conntrack_dump_flush

TEST_GEN_FILES = audit_logread
TEST_GEN_FILES += connect_close nf_queue
TEST_GEN_FILES += conntrack_reverse_clash
TEST_GEN_FILES += sctp_collision

include ../../lib.mk
Expand Down
125 changes: 125 additions & 0 deletions tools/testing/selftests/net/netfilter/conntrack_reverse_clash.c
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 tools/testing/selftests/net/netfilter/conntrack_reverse_clash.sh
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

0 comments on commit a57856c

Please sign in to comment.