mirror of
https://github.com/containers/netavark.git
synced 2026-02-05 15:45:47 +01:00
Code and documentation changes to remove the support of iptables as a firewall driver. Signed-off-by: Mario Loriedo <mario.loriedo@gmail.com>
729 lines
22 KiB
Bash
729 lines
22 KiB
Bash
# -*- bash -*-
|
|
|
|
# Netavark binary to run
|
|
NETAVARK=${NETAVARK:-./bin/netavark}
|
|
NETAVARK_CONNECTION_TESTER=${NETAVARK_CONNECTION_TESTER:-./bin/netavark-connection-tester}
|
|
|
|
TESTSDIR=${TESTSDIR:-$(dirname ${BASH_SOURCE})}
|
|
|
|
# export RUST_BACKTRACE so that we get a helpful stack trace
|
|
export RUST_BACKTRACE=full
|
|
|
|
# this will cause tests to fail because stdou/stderr are not separate
|
|
# export RUST_LOG=netavark=debug
|
|
|
|
HOST_NS_PID=
|
|
CONTAINER_NS_PIDS=()
|
|
|
|
function create_container_ns() {
|
|
CONTAINER_NS_PIDS+=("$(create_netns)")
|
|
}
|
|
|
|
function basic_setup() {
|
|
HOST_NS_PID=$(create_netns)
|
|
create_container_ns
|
|
|
|
# make sure to set DBUS_SYSTEM_BUS_ADDRESS to an empty value
|
|
# netavark will try to use firewalld connection when possible
|
|
# because we run in a separate netns we cannot use firewalld
|
|
# firewalld run in the host netns and not our custom netns
|
|
# thus the firewall rules end up in the wrong netns
|
|
# unsetting does not work, it would use the default address
|
|
export DBUS_SYSTEM_BUS_ADDRESS=
|
|
|
|
NETAVARK_TMPDIR=$(mktemp -d --tmpdir=${BATS_TMPDIR:-/tmp} netavark_bats.XXXXXX)
|
|
|
|
# hack to make aardvark-dns run when really root or when running as user with
|
|
# podman unshare --rootless-netns; since netavark runs aardvark with systemd-run
|
|
# it needs to know if it should use systemd user instance or not.
|
|
rootless=false
|
|
if [[ ! -e "/run/dbus/system_bus_socket" ]]; then
|
|
rootless=true
|
|
fi
|
|
|
|
mkdir -p "$NETAVARK_TMPDIR/config"
|
|
|
|
run_in_host_netns ip link set lo up
|
|
}
|
|
|
|
function basic_teardown() {
|
|
teardown_firewalld
|
|
kill -9 $HOST_NS_PID
|
|
for i in "${!CONTAINER_NS_PIDS[@]}"; do
|
|
kill -9 "${CONTAINER_NS_PIDS[$i]}"
|
|
done
|
|
rm -rf "$NETAVARK_TMPDIR"
|
|
}
|
|
|
|
function setup_firewalld() {
|
|
# first, create a new dbus session
|
|
DBUS_SYSTEM_BUS_ADDRESS=unix:path=$NETAVARK_TMPDIR/netavark-firewalld
|
|
run_in_host_netns dbus-daemon --address="$DBUS_SYSTEM_BUS_ADDRESS" --print-pid --config-file="${TESTSDIR}/testfiles/firewalld-dbus.conf"
|
|
DBUS_PID="$output"
|
|
# export DBUS_SYSTEM_BUS_ADDRESS so firewalld and netavark will use the correct socket
|
|
export DBUS_SYSTEM_BUS_ADDRESS
|
|
|
|
# second, start firewalld in the netns with the dbus socket
|
|
# do not use run_in_host_netns because we want to run this in background
|
|
# use --nopid (we cannot change the pid file location), --nofork do not run as daemon so we can kill it by pid
|
|
# change --system-config to make sure that we do not write any config files to the host location
|
|
nsenter -n -t $HOST_NS_PID firewalld --nopid --nofork --system-config "$NETAVARK_TMPDIR" &>"$NETAVARK_TMPDIR/firewalld.log" &
|
|
FIREWALLD_PID=$!
|
|
echo "firewalld pid: $FIREWALLD_PID"
|
|
|
|
# wait for firewalld to become ready
|
|
timeout=5
|
|
while [ $timeout -gt 0 ]; do
|
|
# query firewalld with firewall-cmd
|
|
expected_rc="?" run_in_host_netns firewall-cmd --state
|
|
if [ "$status" -eq 0 ]; then
|
|
break
|
|
fi
|
|
sleep 1
|
|
timeout=$(($timeout - 1))
|
|
if [ $timeout -eq 0 ]; then
|
|
cat "$NETAVARK_TMPDIR/firewalld.log"
|
|
die "failed to start firewalld - timeout"
|
|
fi
|
|
done
|
|
}
|
|
|
|
function teardown_firewalld() {
|
|
if [ -n "${NETAVARK_FIREWALLD_RELOAD_PID}" ]; then
|
|
kill -9 $NETAVARK_FIREWALLD_RELOAD_PID
|
|
fi
|
|
if [ -n "${FIREWALLD_PID}" ]; then
|
|
kill -9 $FIREWALLD_PID
|
|
fi
|
|
if [ -n "${DBUS_PID}" ]; then
|
|
kill -9 $DBUS_PID
|
|
fi
|
|
unset DBUS_SYSTEM_BUS_ADDRESS
|
|
}
|
|
|
|
# Provide the above as default methods.
|
|
function setup() {
|
|
basic_setup
|
|
}
|
|
|
|
function teardown() {
|
|
basic_teardown
|
|
}
|
|
|
|
function create_netns() {
|
|
# create a new netns and mountns and run a sleep process to keep it alive
|
|
# we have to redirect stdout/err to /dev/null otherwise bats will hang
|
|
unshare -nm --propagation private sleep inf &>/dev/null &
|
|
# netavark writes to /run/sysctl.d, mount a tmpfs to not leak stuff on the host
|
|
mkdir -p /run/sysctl.d
|
|
nsenter -n -m -w -t $! mount -t tmpfs none /run/sysctl.d
|
|
echo $!
|
|
}
|
|
|
|
function get_container_netns_path() {
|
|
local which="0"
|
|
if [[ $# -eq 1 ]]; then
|
|
which=$1
|
|
fi
|
|
echo /proc/"${CONTAINER_NS_PIDS[$which]}"/ns/net
|
|
}
|
|
|
|
################
|
|
# run_netavark # Invoke $NETAVARK, with timeout, using BATS 'run'
|
|
################
|
|
#
|
|
# This is the preferred mechanism for invoking netavark: first, it
|
|
# it joins the test network namespace before it invokes $NETAVARK,
|
|
# which may be 'netavark' or '/some/path/netavark'.
|
|
function run_netavark() {
|
|
run_in_host_netns $NETAVARK --rootless "$rootless" \
|
|
--config "$NETAVARK_TMPDIR/config" "$@"
|
|
}
|
|
|
|
function run_netavark_firewalld_reload() {
|
|
# need to use nsetner as this will be run in the background
|
|
nsenter -n -t $HOST_NS_PID $NETAVARK --config "$NETAVARK_TMPDIR/config" firewalld-reload &
|
|
NETAVARK_FIREWALLD_RELOAD_PID=$!
|
|
}
|
|
|
|
|
|
################
|
|
# run_in_container_netns # Run args in container netns
|
|
################
|
|
#
|
|
function run_in_container_netns() {
|
|
local i="0"
|
|
isnum='^[0-9]+$'
|
|
if [[ $1 =~ $isnum ]]; then
|
|
i=$1
|
|
shift 1
|
|
fi
|
|
run_helper nsenter -n -m -w -t "${CONTAINER_NS_PIDS[$i]}" "$@"
|
|
|
|
}
|
|
|
|
################
|
|
# run_in_host_netns # Run args in host netns
|
|
################
|
|
function run_in_host_netns() {
|
|
run_helper nsenter -n -m -w -t $HOST_NS_PID "$@"
|
|
}
|
|
|
|
#### Functions below are taken from podman and buildah and adapted to netavark.
|
|
|
|
################
|
|
# run_helper # Invoke args, with timeout, using BATS 'run'
|
|
################
|
|
#
|
|
# Second, we use 'timeout' to abort (with a diagnostic) if something
|
|
# takes too long; this is preferable to a CI hang.
|
|
#
|
|
# Third, we log the command run and its output. This doesn't normally
|
|
# appear in BATS output, but it will if there's an error.
|
|
#
|
|
# Next, we check exit status. Since the normal desired code is 0,
|
|
# that's the default; but the expected_rc var can override:
|
|
#
|
|
# expected_rc=125 run_helper nonexistent-subcommand
|
|
# expected_rc=? run_helper some-other-command # let our caller check status
|
|
#
|
|
# Since we use the BATS 'run' mechanism, $output and $status will be
|
|
# defined for our caller.
|
|
#
|
|
function run_helper() {
|
|
# expected_rc if unset set default to 0
|
|
expected_rc="${expected_rc-0}"
|
|
if [ "$expected_rc" == "?" ]; then
|
|
expected_rc=
|
|
fi
|
|
# Remember command args, for possible use in later diagnostic messages
|
|
MOST_RECENT_COMMAND="$*"
|
|
|
|
# stdout is only emitted upon error; this echo is to help a debugger
|
|
echo "$_LOG_PROMPT $*"
|
|
|
|
# BATS hangs if a subprocess remains and keeps FD 3 open; this happens
|
|
# if a process crashes unexpectedly without cleaning up subprocesses.
|
|
run timeout --foreground -v --kill=10 10 "$@" 3>/dev/null
|
|
# without "quotes", multiple lines are glommed together into one
|
|
if [ -n "$output" ]; then
|
|
echo "$output"
|
|
fi
|
|
if [ "$status" -ne 0 ]; then
|
|
echo -n "[ rc=$status "
|
|
if [ -n "$expected_rc" ]; then
|
|
if [ "$status" -eq "$expected_rc" ]; then
|
|
echo -n "(expected) "
|
|
else
|
|
echo -n "(** EXPECTED $expected_rc **) "
|
|
fi
|
|
fi
|
|
echo "]"
|
|
fi
|
|
|
|
if [ "$status" -eq 124 ]; then
|
|
if expr "$output" : ".*timeout: sending" >/dev/null; then
|
|
# It's possible for a subtest to _want_ a timeout
|
|
if [[ "$expected_rc" != "124" ]]; then
|
|
echo "*** TIMED OUT ***"
|
|
false
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
if [ -n "$expected_rc" ]; then
|
|
if [ "$status" -ne "$expected_rc" ]; then
|
|
die "exit code is $status; expected $expected_rc"
|
|
fi
|
|
fi
|
|
|
|
# unset
|
|
unset expected_rc
|
|
}
|
|
|
|
#########
|
|
# die # Abort with helpful message
|
|
#########
|
|
function die() {
|
|
# FIXME: handle multi-line output
|
|
echo "#/vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv" >&2
|
|
echo "#| FAIL: $*" >&2
|
|
echo "#\\^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^" >&2
|
|
false
|
|
}
|
|
|
|
############
|
|
# assert # Compare actual vs expected string; fail if mismatch
|
|
############
|
|
#
|
|
# Compares string (default: $output) against the given string argument.
|
|
# By default we do an exact-match comparison against $output, but there
|
|
# are two different ways to invoke us, each with an optional description:
|
|
#
|
|
# xpect "EXPECT" [DESCRIPTION]
|
|
# xpect "RESULT" "OP" "EXPECT" [DESCRIPTION]
|
|
#
|
|
# The first form (one or two arguments) does an exact-match comparison
|
|
# of "$output" against "EXPECT". The second (three or four args) compares
|
|
# the first parameter against EXPECT, using the given OPerator. If present,
|
|
# DESCRIPTION will be displayed on test failure.
|
|
#
|
|
# Examples:
|
|
#
|
|
# xpect "this is exactly what we expect"
|
|
# xpect "${lines[0]}" =~ "^abc" "first line begins with abc"
|
|
#
|
|
function assert() {
|
|
local actual_string="$output"
|
|
local operator='=='
|
|
local expect_string="$1"
|
|
local testname="$2"
|
|
|
|
case "${#*}" in
|
|
0) die "Internal error: 'assert' requires one or more arguments" ;;
|
|
1 | 2) ;;
|
|
3 | 4)
|
|
actual_string="$1"
|
|
operator="$2"
|
|
expect_string="$3"
|
|
testname="$4"
|
|
;;
|
|
*) die "Internal error: too many arguments to 'assert'" ;;
|
|
esac
|
|
|
|
# Comparisons.
|
|
# Special case: there is no !~ operator, so fake it via '! x =~ y'
|
|
local not=
|
|
local actual_op="$operator"
|
|
if [[ $operator == '!~' ]]; then
|
|
not='!'
|
|
actual_op='=~'
|
|
fi
|
|
if [[ $operator == '=' || $operator == '==' ]]; then
|
|
# Special case: we can't use '=' or '==' inside [[ ... ]] because
|
|
# the right-hand side is treated as a pattern... and '[xy]' will
|
|
# not compare literally. There seems to be no way to turn that off.
|
|
if [ "$actual_string" = "$expect_string" ]; then
|
|
return
|
|
fi
|
|
elif [[ $operator == '!=' ]]; then
|
|
# Same special case as above
|
|
if [ "$actual_string" != "$expect_string" ]; then
|
|
return
|
|
fi
|
|
else
|
|
if eval "[[ $not \$actual_string $actual_op \$expect_string ]]"; then
|
|
return
|
|
elif [ $? -gt 1 ]; then
|
|
die "Internal error: could not process 'actual' $operator 'expect'"
|
|
fi
|
|
fi
|
|
|
|
# Test has failed. Get a descriptive test name.
|
|
if [ -z "$testname" ]; then
|
|
testname="${MOST_RECENT_BUILDAH_COMMAND:-[no test name given]}"
|
|
fi
|
|
|
|
# Display optimization: the typical case for 'expect' is an
|
|
# exact match ('='), but there are also '=~' or '!~' or '-ge'
|
|
# and the like. Omit the '=' but show the others; and always
|
|
# align subsequent output lines for ease of comparison.
|
|
local op=''
|
|
local ws=''
|
|
if [ "$operator" != '==' ]; then
|
|
op="$operator "
|
|
ws=$(printf "%*s" ${#op} "")
|
|
fi
|
|
|
|
# This is a multi-line message, which may in turn contain multi-line
|
|
# output, so let's format it ourself, readably
|
|
local actual_split
|
|
IFS=$'\n' read -rd '' -a actual_split <<<"$actual_string" || true
|
|
printf "#/vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv\n" >&2
|
|
printf "#| FAIL: %s\n" "$testname" >&2
|
|
printf "#| expected: %s'%s'\n" "$op" "$expect_string" >&2
|
|
printf "#| actual: %s'%s'\n" "$ws" "${actual_split[0]}" >&2
|
|
local line
|
|
for line in "${actual_split[@]:1}"; do
|
|
printf "#| > %s'%s'\n" "$ws" "$line" >&2
|
|
done
|
|
printf "#\\^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" >&2
|
|
false
|
|
}
|
|
|
|
#################
|
|
# assert_json # Compare actual json vs expected string; fail if mismatch
|
|
#################
|
|
# assert_json works like assert except that it accepts one extra parameter,
|
|
# the jq query string.
|
|
# There are two different ways to invoke us, each with an optional description:
|
|
#
|
|
# xpect "JQ_QUERY" "EXPECT" [DESCRIPTION]
|
|
# xpect "JSON_STRING" "JQ_QUERY" "OP" "EXPECT" [DESCRIPTION]
|
|
# Important this function will overwrite $output, so if you need to use the value
|
|
# more than once you need to safe it in another variable.
|
|
function assert_json() {
|
|
local actual_json="$output"
|
|
local operator='=='
|
|
local jq_query="$1"
|
|
local expect_string="$2"
|
|
local testname="$3"
|
|
|
|
case "${#*}" in
|
|
0 | 1) die "Internal error: 'assert_json' requires two or more arguments" ;;
|
|
2 | 3) ;;
|
|
4 | 5)
|
|
actual_json="$1"
|
|
jq_query="$2"
|
|
operator="$3"
|
|
expect_string="$4"
|
|
testname="$5"
|
|
;;
|
|
*) die "Internal error: too many arguments to 'assert_json'" ;;
|
|
esac
|
|
run_helper jq -r "$jq_query" <<<"$actual_json"
|
|
assert "$output" "$operator" "$expect_string" "$testname"
|
|
}
|
|
|
|
##################
|
|
# test_port_fw # test port forwarding
|
|
##################
|
|
# test port forwarding
|
|
# by default this will create a ipv4 config with tcp as protocol
|
|
#
|
|
# The following arguments are supported, the order does not matter:
|
|
# ip={4, 6, dual}
|
|
# proto={tcp,udp,sctp} or some comma separated list of the protocols
|
|
# hostip=$ip the ip which is used for binding on the host
|
|
# hostport=$port the port which is binded on the host
|
|
# containerport=$port the port which is binded in the container
|
|
# range=$num >=1 specify a port range which will forward hostport+range ports
|
|
# connectip=$ip the ip which is used to connect to in the connection test
|
|
# firewalld_reload={false,true} call firewall-cmd --reload to check for port rules
|
|
#
|
|
function test_port_fw() {
|
|
local ipv4=true
|
|
local ipv6=false
|
|
local proto=tcp
|
|
local host_ip=""
|
|
local host_port=""
|
|
local container_port=""
|
|
local range=1
|
|
local connect_ip=""
|
|
local firewalld_reload=false
|
|
|
|
# parse arguments
|
|
while [[ "$#" -gt 0 ]]; do
|
|
IFS='=' read -r arg value <<<"$1"
|
|
case "$arg" in
|
|
ip)
|
|
case "$value" in
|
|
4) ipv4=true ;;
|
|
6)
|
|
ipv6=true
|
|
ipv4=false
|
|
;;
|
|
dual) ipv6=true ;;
|
|
*) die "unknown argument '$value' for ip=" ;;
|
|
esac
|
|
;;
|
|
proto)
|
|
proto="$value"
|
|
;;
|
|
hostip)
|
|
host_ip="$value"
|
|
;;
|
|
connectip)
|
|
connect_ip="$value"
|
|
;;
|
|
hostport)
|
|
host_port="$value"
|
|
;;
|
|
containerport)
|
|
container_port="$value"
|
|
;;
|
|
range)
|
|
range="$value"
|
|
;;
|
|
firewalld_reload)
|
|
firewalld_reload="$value"
|
|
;;
|
|
*) die "unknown argument for '$arg' test_port_fw" ;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
if [ -z "$host_port" ]; then
|
|
host_port=$(random_port)
|
|
fi
|
|
|
|
if [ -z "$container_port" ]; then
|
|
container_port=$(random_port)
|
|
fi
|
|
|
|
local container_id=$(random_string 64)
|
|
local container_name="name-$(random_string 10)"
|
|
|
|
local static_ips=""
|
|
local subnets=""
|
|
|
|
if [ $ipv4 = true ]; then
|
|
ipv4_subnet=$(random_subnet)
|
|
ipv4_gateway=$(gateway_from_subnet $ipv4_subnet)
|
|
ipv4_container_ip=$(random_ip_in_subnet $ipv4_subnet)
|
|
|
|
static_ips="\"$ipv4_container_ip\""
|
|
subnets="{\"subnet\":\"$ipv4_subnet\",\"gateway\":\"$ipv4_gateway\"}"
|
|
fi
|
|
|
|
if [ $ipv6 = true ]; then
|
|
ipv6_subnet=$(random_subnet 6)
|
|
ipv6_gateway=$(gateway_from_subnet $ipv6_subnet)
|
|
ipv6_container_ip=$(random_ip_in_subnet $ipv6_subnet)
|
|
|
|
if [ $ipv4 = true ]; then
|
|
# add comma for the json
|
|
static_ips="$static_ips, "
|
|
subnets="$subnets, "
|
|
fi
|
|
static_ips="$static_ips\"$ipv6_container_ip\""
|
|
subnets="$subnets {\"subnet\":\"$ipv6_subnet\",\"gateway\":\"$ipv6_gateway\"}"
|
|
fi
|
|
|
|
read -r -d '\0' config <<EOF
|
|
{
|
|
"container_id": "$container_id",
|
|
"container_name": "$container_name",
|
|
"port_mappings": [
|
|
{
|
|
"host_ip": "$host_ip",
|
|
"container_port": $container_port,
|
|
"host_port": $host_port,
|
|
"range": $range,
|
|
"protocol": "$proto"
|
|
}
|
|
],
|
|
"networks": {
|
|
"podman1": {
|
|
"static_ips": [
|
|
$static_ips
|
|
],
|
|
"interface_name": "eth0"
|
|
}
|
|
},
|
|
"network_info": {
|
|
"podman1": {
|
|
"name": "podman1",
|
|
"id": "ed82e3a703682a9c09629d3cf45c1f1e7da5b32aeff3faf82837ef4d005356e6",
|
|
"driver": "bridge",
|
|
"network_interface": "podman1",
|
|
"subnets": [
|
|
$subnets
|
|
],
|
|
"ipv6_enabled": true,
|
|
"internal": false,
|
|
"dns_enabled": false,
|
|
"ipam_options": {
|
|
"driver": "host-local"
|
|
}
|
|
}
|
|
}
|
|
}\0
|
|
EOF
|
|
|
|
# echo the config here this is useful for debugging in case a test fails
|
|
echo "$config"
|
|
|
|
if [ $firewalld_reload = true ]; then
|
|
setup_firewalld
|
|
run_netavark_firewalld_reload
|
|
fi
|
|
|
|
run_netavark setup $(get_container_netns_path) <<<"$config"
|
|
result="$output"
|
|
|
|
if [ $firewalld_reload = true ]; then
|
|
run_in_host_netns firewall-cmd --reload
|
|
sleep 1
|
|
fi
|
|
|
|
# protocol can be a comma separated list of protocols names
|
|
# split it into an array
|
|
IFS=',' read -ra protocols <<<"$proto"
|
|
|
|
for proto in "${protocols[@]}"; do
|
|
|
|
# ports can be a range, we have to check the full range
|
|
i=0
|
|
while [ $i -lt $range ]; do
|
|
((cport = container_port + i))
|
|
((hport = host_port + i))
|
|
|
|
if [ $ipv4 = true ]; then
|
|
if [[ -z "$connect_ip" ]]; then
|
|
connect_ip=$ipv4_gateway
|
|
if [[ -n "$host_ip" ]]; then
|
|
connect_ip=$host_ip
|
|
fi
|
|
fi
|
|
|
|
run_connection_test "0" "$proto" $cport $connect_ip $hport
|
|
fi
|
|
|
|
|
|
if [ $ipv6 = true ]; then
|
|
if [[ -z "$connect_ip" ]]; then
|
|
connect_ip=$ipv6_gateway
|
|
if [[ -n "$host_ip" ]]; then
|
|
connect_ip=$host_ip
|
|
fi
|
|
fi
|
|
|
|
run_connection_test "0" "$proto" $cport $connect_ip $hport
|
|
fi
|
|
|
|
((i = i + 1))
|
|
|
|
done
|
|
|
|
done
|
|
|
|
run_netavark teardown $(get_container_netns_path) <<<"$config"
|
|
}
|
|
|
|
#################
|
|
# is_ipv6 # check if the given arg is a ipv6 address
|
|
#################
|
|
# Note that this only checks for a colon so it does not validate the ip address.
|
|
# This should only be used if you have a valid ip but do not know if it is ipv4 or ipv6.
|
|
function is_ipv6() {
|
|
[[ "$1" == *":"* ]]
|
|
}
|
|
|
|
#################
|
|
# is_ipv4 # check if the given arg is a ipv4 address
|
|
#################
|
|
# Note that this only checks for a dot so it does not validate the ip address.
|
|
# This should only be used if you have a valid ip but do not know if it is ipv4 or ipv6.
|
|
function is_ipv4() {
|
|
[[ "$1" == *"."* ]]
|
|
}
|
|
|
|
#################
|
|
# run_connection_test # run connection test between the namespaces with our test helper
|
|
#################
|
|
# $1 == contianer ns number
|
|
# $2 == protocol (tcp, udp, sctp)
|
|
# $3 == container port, the server will listen on it in the container ns
|
|
# $4 == connection ip, the ip address which is used by the client to connect to the server
|
|
# $5 == host port, the client will connect to this port
|
|
function run_connection_test() {
|
|
local container_ns=$1
|
|
local proto=$2
|
|
local container_port=$3
|
|
local connect_ip=$4
|
|
local host_port=$5
|
|
|
|
if is_ipv6 "$connect_ip"; then
|
|
connect_ip="[$connect_ip]"
|
|
fi
|
|
|
|
data=$(random_string)
|
|
run_in_host_netns $NETAVARK_CONNECTION_TESTER --$proto "$(get_container_netns_path $container_ns)" "$connect_ip:$host_port" $container_port <<<"$data"
|
|
assert "${lines[1]}" == "Message: $data" "Logged message"
|
|
}
|
|
|
|
#################
|
|
# random_port # get a random port number between 1-32768
|
|
#################
|
|
function random_port() {
|
|
printf $(($RANDOM + 1))
|
|
}
|
|
|
|
###################
|
|
# random_string # Pseudorandom alphanumeric string of given length
|
|
###################
|
|
function random_string() {
|
|
local length=${1:-10}
|
|
head /dev/urandom | tr -dc a-zA-Z0-9 | head -c$length
|
|
}
|
|
|
|
###################
|
|
# random_subnet # generate a random private subnet
|
|
###################
|
|
#
|
|
# by default it will return a 10.x.x.0/24 ipv4 subnet
|
|
# if "6" is given as first argument it will return a "fdx:x:x:x::/64" ipv6 subnet
|
|
function random_subnet() {
|
|
if [[ "$1" == "6" ]]; then
|
|
printf "fd%x:%x:%x:%x::/64" $((RANDOM % 256)) $((RANDOM % 65535)) $((RANDOM % 65535)) $((RANDOM % 65535))
|
|
else
|
|
printf "10.%d.%d.0/24" $((RANDOM % 256)) $((RANDOM % 256))
|
|
fi
|
|
}
|
|
|
|
#########################
|
|
# random_ip_in_subnet # get a random from a given subnet
|
|
#########################
|
|
# the first arg must be an subnet created by random_subnet
|
|
# otherwise this function might return an invalid ip
|
|
function random_ip_in_subnet() {
|
|
# first trim subnet
|
|
local net_ip=${1%/*}
|
|
local num=
|
|
# if ip has colon it is ipv6
|
|
if [[ "$net_ip" == *":"* ]]; then
|
|
# make sure to not get 0 or 1
|
|
num=$(printf "%x" $((RANDOM % 65533 + 2)))
|
|
else
|
|
# if ipv4 we have to trim the final 0
|
|
net_ip=${net_ip%0}
|
|
# make sure to not get 0, 1 or 255
|
|
num=$(printf "%d" $((RANDOM % 252 + 2)))
|
|
fi
|
|
printf "$net_ip%s" $num
|
|
}
|
|
|
|
#########################
|
|
# random_ip_in_subnet # get the first ip from a given subnet
|
|
#########################
|
|
# the first arg must be an subnet created by random_subnet
|
|
# otherwise this function might return an invalid ip
|
|
function gateway_from_subnet() {
|
|
# first trim subnet
|
|
local net_ip=${1%/*}
|
|
# set first ip in network as gateway
|
|
local num=1
|
|
# if ip has dor it is ipv4
|
|
if [[ "$net_ip" == *"."* ]]; then
|
|
# if ipv4 we have to trim the final 0
|
|
net_ip=${net_ip%0}
|
|
fi
|
|
printf "$net_ip%s" $num
|
|
}
|
|
|
|
##############################
|
|
# setup_sctp_kernel_module #
|
|
##############################
|
|
# tries to load the sctp kernel module if possible
|
|
# otherwise it will skip the test
|
|
function setup_sctp_kernel_module() {
|
|
modprobe sctp || skip "cannot load sctp kernel module"
|
|
}
|
|
|
|
#################################
|
|
# add_dummy_interface_on_host #
|
|
#################################
|
|
# create a dummy interface with the given name and subnet
|
|
# the first arg is the name
|
|
# the second arg is the subnet (optional)
|
|
function add_dummy_interface_on_host() {
|
|
name="$1"
|
|
ipaddr="$2"
|
|
run_in_host_netns ip link add "$name" type dummy
|
|
if [ -n "$ipaddr" ]; then
|
|
run_in_host_netns ip addr add "$ipaddr" dev "$name"
|
|
fi
|
|
run_in_host_netns ip link set "$name" up
|
|
}
|