Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions test_suite/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,23 @@

In this file, the test suite features that have been made possible thanks to [funding from NLnet](./README.md#funding) are documented.

## Double NAT(Oct 21, 2025)
### Added
- Explanation of Double NAT and how it is implemented in the test suite in the [system test documentation](./README.md#double-nat).
- The `-2` flag to [`system_tests.sh`](system_tests.sh) and [`nat_simulation/setup_networks.sh`](nat_simulation/setup_networks.sh), which enables Double NAT.
- New logic in [`system_tests.sh`](system_tests.sh) which decides the expected test result if the peers are behind Double NAT. This logic assumes the NAT behaviour is limited to the 4 NATs described in RFC 3489
- Functionality in [`nat_simulation/setup_router.sh`](nat_simulation/setup_router.sh) to perform different actions depending on which of the two NATs is being set up.
- Report on the system tests results with Double NAT for each combination of two RFC 3489 NATs in the [system test results](./README.md#effect-of-double-nat-on-system-test-results).

### Changed
- The syntax of the NAT and network namespace configurations which are passed as parameters to [`system_test.sh`](system_test.sh), such that a second NAT layer can be specified.

### Fixed
- Small miscellaneous improvements, such as:
- Less duplicated code for regex validation by placing regular expressions which are used multiple times in [util.sh](util.sh).
- Fix incorrect abbreviation ADPF -> APDF across documentation and code comments.
- Add missing command necessary before running parallel system tests in the [system test requirements](./README.md#system-test-specific-requirements).

## NAT IP pooling (June 20, 2025)
### Added
- Explanation of NAT IP pooling and how it is implemented in the test suite in the [system test documentation](./README.md#ip-address-pooling).
Expand Down
139 changes: 112 additions & 27 deletions test_suite/README.md

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions test_suite/nat_simulation/setup_nat_filtering_hairpinning.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ fi
# Configure NAT filtering type with nftables
nft add table inet filter
nft add chain inet filter input { type filter hook input priority 0\; policy drop\; }
nft add rule inet filter input ct state related,established counter accept # This rule is sufficient to simulate ADPF
nft add rule inet filter input ct state related,established counter accept # This rule is sufficient to simulate APDF

# This pattern captures the following info from a conntrack event:
# 1) the source IP
Expand All @@ -36,7 +36,7 @@ pattern=".*src=(\S+).*sport=(\S+).*src=(\S+).*dst=(\S+).*dport=(\S+).*$"
hairpin_rule1="nat prerouting iif $priv_nat_iface ip saddr $priv_subnet ip daddr \4 meta l4proto {tcp, udp} th dport \5 counter dnat to \1:\2" # All traffic from the private network destined to \4:\5 should be hairpinned pack to \1:\2
hairpin_rule2="nat postrouting iif $priv_nat_iface ip saddr \1 ip daddr $priv_subnet meta l4proto {tcp, udp} th sport \2 counter snat to \4:\5" # For all hairpinned packets from \1:\2, the source becomes \4:\5

# Filtering (not necessary for ADPF because of filter rule above)
# Filtering (not necessary for APDF because of filter rule above)
case $nat_filter in
0)
# If a mapping is created with source IP \1, source port \2 and translated source port \5, all traffic destined to \5 should be DNATed to \1:\2
Expand All @@ -48,7 +48,7 @@ esac

# Only monitor new source NAT connections that are created by the nftables NAT mapping rules
if [[ $nat_filter -eq 2 ]]; then
conntrack -En -s $priv_subnet -e NEW | sed -rn -e "s#$pattern#nft add rule $hairpin_rule1; nft add rule $hairpin_rule2#e" # No filter rule for ADPF NAT
conntrack -En -s $priv_subnet -e NEW | sed -rn -e "s#$pattern#nft add rule $hairpin_rule1; nft add rule $hairpin_rule2#e" # No filter rule for APDF NAT
else
conntrack -En -s $priv_subnet -e NEW | sed -rn -e "s#$pattern#nft add rule $hairpin_rule1; nft add rule $hairpin_rule2; nft add rule $filter_rule#e"
fi
74 changes: 57 additions & 17 deletions test_suite/nat_simulation/setup_networks.sh
Original file line number Diff line number Diff line change
@@ -1,23 +1,43 @@
#!/usr/bin/env bash

usage_str="""
Usage: ${0} <AMOUNT OF IPS>

<AMOUNT OF IPS> specifies the number of IP addresses assigned to each NAT; setting this argument >1 allows NAT IP pooling to be simulated
usage_str="""Usage: ${0} [OPTIONAL ARGUMENTS]

Simulates a network setup containing two private networks connected via the public network. Each private network contains two peers using eduP2P
To allow traffic to flow between the public and private networks, the scripts setup_nat_mapping.sh should also be executed
To allow traffic to flow between peers in the same private network, the scripts setup_nat_filtering_hairpinning.sh should also be executed

This script must be run with root permissions"""
By default, a single NAT with one IP address facilitates the communication between a private and public network. With the -2 flag, Double NAT is simulated by adding another level of private networks on top of the existing ones. With the -n flag, the amount of IPs on the NAT can be increased up to 9; if Double NAT is enabled, the additional NAT always has 1 IP address.

if [[ $1 = "-h" || $# -ne 1 ]]; then
echo $usage_str
exit 1
fi
To allow traffic to flow between the public and private networks, the script setup_nat_mapping.sh should also be executed. To allow traffic to flow between peers in the same private network, the script setup_nat_filtering_hairpinning.sh should also be executed

# Number of IPs per router to test NAT IP pooling
n_pooling_ips=$1
This script must be run with root permissions"""

# Use functions and constants from util.sh
. ../util.sh

# Default arguments
n_pooling_ips=1

# Validate optional arguments
while getopts ":2n:h" opt; do
case $opt in
2)
double_nat=true
;;
n)
n_pooling_ips=$OPTARG

# Make sure n_pooling_ips is an integer between 1 and 9
n_pooling_ips_regex="^[1-9]$"
validate_str $n_pooling_ips $n_pooling_ips_regex
;;
h)
echo "$usage_str"
exit 0
;;
*)
exit_with_error "invalid option"
;;
esac
done

# Enable IP forwarding to allow for routing between namespaces
sysctl -w net.ipv4.ip_forward=1 &> /dev/null
Expand Down Expand Up @@ -73,11 +93,31 @@ for ((i=1; i<=n_priv_nets; i++)); do
# Add router's public subnet to list created earlier
adm_ips+=($pub_subnet)

# Setup router
ip netns exec $router_name ./setup_router.sh $router_name $priv_name $priv_subnet $router_priv_ip $pub_prefix $n_pooling_ips $switch_ip
if [[ -z $double_nat ]]; then
# Setup router
ip netns exec $router_name ./setup_router.sh $router_name $priv_name public $priv_subnet $router_priv_ip $pub_prefix $n_pooling_ips $switch_ip 0

# Setup private network
ip netns exec $priv_name ./setup_private.sh $router_name $router_pub_ip $priv_subnet
else
# Create namespace for the additional private network
double_name="double${i}"
./create_namespace.sh $double_name

# Variables related to the additional private network and its router
double_prefix="172.16.${i}"
double_subnet="${priv_prefix}.0/24"
double_ip="${double_prefix}.254"

# Setup first router
ip netns exec $router_name ./setup_router.sh $router_name $double_name public $double_subnet $double_ip $pub_prefix $n_pooling_ips $switch_ip 0

# Setup additional router
ip netns exec $double_name ./setup_router.sh $double_name $priv_name $router_name $priv_subnet $router_priv_ip $double_prefix $n_pooling_ips $router_pub_ip 1

# Setup private network
ip netns exec $priv_name ./setup_private.sh $router_name $router_pub_ip $priv_subnet
# Setup private network
ip netns exec $priv_name ./setup_private.sh $double_name $double_ip $priv_subnet
fi

# Setup peers in each private network
for ((j=1; j<=n_peers; j++)); do
Expand Down
59 changes: 34 additions & 25 deletions test_suite/nat_simulation/setup_router.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@

router_name=$1
priv_name=$2
priv_subnet=$3
priv_ip=$4
pub_subnet_prefix=$5
n_ips=$6
switch_ip=$7

if [[ $# -ne 7 ]]; then
public_name=$3
priv_subnet=$4
priv_ip=$5
pub_subnet_prefix=$6
n_ips=$7
switch_ip=$8
router_index=$9

if [[ $# -ne 9 ]]; then
echo """
Usage: ${0} <ROUTER NAME> <PRIVATE NETWORK NAME> <PRIVATE SUBNET> <ROUTER PRIVATE IP> <PUBLIC /24 NETWORK PREFIX> <NUMBER OF IPS> <SWITCH IP>
Usage: ${0} <ROUTER NAME> <PRIVATE NETWORK NAME> <PUBLIC NETWORK NAME> <PRIVATE SUBNET> <ROUTER PRIVATE IP> <ROUTER PUBLIC IP> <NUMBER OF IPS> <SWITCH IP> <ROUTER INDEX>

This script must be run with root permissions"""
exit 1
Expand All @@ -25,25 +27,32 @@ ip netns exec $priv_name ip addr add "${priv_ip}/24" dev $router_name
ip link set $router_priv up
ip netns exec $priv_name ip link set $router_name up

# Create veth pair to place the router's public interface in the public and router namespaces
router_pub="${router_name}_pub"
ip link add $router_pub type veth peer $router_name netns public

for host in $(seq $((254 - $n_ips + 1)) 254); do
ip="$pub_subnet_prefix.$host/24"
ip addr add $ip dev $router_pub
done
# Add route for traffic to router's private network
ip route add $priv_ip dev $router_priv
ip route add $priv_subnet via $priv_ip dev $router_priv

ip link set $router_pub up
ip netns exec public ip link set $router_name up
# $router_index is equal to 0 for first router, 1 for additional router
if [[ $router_index -eq 0 ]]; then
# Create veth pair to place the router's public interface in the public and router namespaces
router_pub="${router_name}_pub"
ip link add $router_pub type veth peer $router_name netns public

for host in $(seq $((254 - $n_ips + 1)) 254); do
ip="$pub_subnet_prefix.$host/24"
ip addr add $ip dev $router_pub
done

ip link set $router_pub up
ip netns exec public ip link set $router_name up

# Create route to first router in the public network
ip netns exec $public_name ip route add $pub_subnet dev $router_name
else
# Veth pair already created by first router
router_pub=$public_name
fi

# Add switch as default gateway
# Show switch is routable via router as default gateway
ip route add $switch_ip dev $router_pub
ip route add default via $switch_ip dev $router_pub

# Add route for traffic to router's private network
ip route add $priv_ip dev $router_priv
ip route add $priv_subnet via $priv_ip dev $router_priv

# Create route to router in the public network
ip netns exec public ip route add $pub_subnet dev $router_name
2 changes: 1 addition & 1 deletion test_suite/performance_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ while getopts ":b:h" opt; do
case $opt in
b)
baseline=$OPTARG
validate_str $baseline "^direct|wireguard|both$"
validate_str "$baseline" "^(direct|wireguard|both)$"

case $baseline in
"direct")
Expand Down
4 changes: 2 additions & 2 deletions test_suite/set_delay.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ Usage: ${0} <DELAY>

delay=$1

# Make sure delay is an integer
int_regex="^[0-9]+$"
. ./util.sh

# Make sure delay is an integer
if [[ $# -ne 1 || ! ( $delay =~ $int_regex) ]]; then
echo $usage_str
exit 1
Expand Down
6 changes: 3 additions & 3 deletions test_suite/set_packet_loss.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ Usage: ${0} <PACKET LOSS PERCENTAGE>

packet_loss=$1

# Make sure packet_loss is a real number, and get the amount of decimal digits
real_regex="^[0-9]+[.]?([0-9]+)?$"
. ./util.sh

if [[ $# -ne 1 || ! ( $packet_loss =~ $real_regex) ]]; then
# Make sure packet_loss is a real number, and get the amount of decimal digits
if [[ $# -ne 1 || ! ( $packet_loss =~ ^$real_regex$ ) ]]; then
echo $usage_str
exit 1
fi
Expand Down
Loading