podman/test/system/505-networking-pasta.bats

863 lines
27 KiB
Bash

#!/usr/bin/env bats -*- bats -*-
#
# SPDX-License-Identifier: Apache-2.0
#
# Networking with pasta(1)
#
# Copyright (c) 2022 Red Hat GmbH
# Author: Stefano Brivio <sbrivio@redhat.com>
load helpers
load helpers.network
# All tests in this file must be able to run in parallel
# bats file_tags=ci:parallel
function setup() {
basic_setup
skip_if_not_rootless "pasta networking only available in rootless mode"
skip_if_no_pasta "pasta not found: install pasta(1) to run these tests"
XFER_FILE="${PODMAN_TMPDIR}/pasta.bin"
}
# _set_opt() - meta-helper for pasta_test_do.
#
# Sets an option, but panics if option is already set (e.g. UDP+TCP, IPv4/v6)
function _set_opt() {
local opt_name=$1
local -n opt_ref=$1
local newval=$2
if [[ -n "$opt_ref" ]]; then
# $kw sneakily inherited from caller
die "'$kw' in test name sets $opt_name='$newval', but $opt_name has already been set to '$opt_ref'"
fi
opt_ref=$newval
}
# pasta_test_do() - Run tests involving clients and servers
#
# This helper function is invoked without arguments; it determines what to do
# based on the @test name.
function pasta_test_do() {
local ip_ver iftype proto range delta bind_type bytes
# Normalize test name back to human-readable form. BATS gives us a
# sanitized string with non-alnum converted to '-XX' (dash-hexbyte)
# and spaces converted to underscores. Convert all of those to spaces.
# This then gives us only the important (mutable) part of the test:
#
# test_TCP_translated_..._forwarding-2c_IPv4-2c_loopback
# -> TCP translated ... forwarding IPv4 loopback
# -> TCP translated forwarding IPv4 loopback
local test_name=$(printf "$(sed \
-e 's/^test_//' \
-e 's/-\([0-9a-f]\{2\}\)/ /gI' \
-e 's/_/ /g' \
<<<"${BATS_TEST_NAME}")")
# We now have the @test name as specified in the script, minus punctuation.
# From each of the name components, determine an action.
#
# TCP translated port range forwarding IPv4 loopback
# | | | | | | \__ iftype=loopback
# | | | | | \________ ip_ver=4
# | | | | \____________________ bytes=1
# | | | \__________________________ range=3
# | | \_______________________________ (ignored)
# | \__________________________________________ delta=1
# \______________________________________________ proto=tcp
#
# Each keyword maps to one option. Conflicts ("TCP ... UDP") are fatal
# errors, as are unknown keywords.
for kw in $test_name; do
case $kw in
TCP|UDP) _set_opt proto ${kw,,} ;;
IPv*) _set_opt ip_ver $(expr "$kw" : "IPv\(.\)") ;;
Single) _set_opt range 1 ;;
range) _set_opt range 3 ;;
Address|Interface) _set_opt bind_type ${kw,,} ;;
bound) assert "$bind_type" != "" "WHAT-bound???" ;;
[Tt]ranslated) _set_opt delta 1 ;;
loopback|tap) _set_opt iftype $kw ;;
port) ;; # always occurs with 'forwarding'; ignore
forwarding) _set_opt bytes 1 ;;
large|small) _set_opt bytes $kw ;;
transfer) assert "$bytes" != "" "'transfer' must be preceded by 'large' or 'small'" ;;
*) die "cannot grok '$kw' in test name" ;;
esac
done
# Sanity checks: all test names must include IPv4/6 and TCP/UDP
test -n "$ip_ver" || die "Test name must include IPv4 or IPv6"
test -n "$proto" || die "Test name must include TCP or UDP"
test -n "$bytes" || die "Test name must include 'forwarding' or 'large/small transfer'"
# Major decision point: simple forwarding test, or multi-byte transfer?
if [[ $bytes -eq 1 ]]; then
# Simple forwarding check
# We can't always determine these from the test name. Use sane defaults.
range=${range:-1}
delta=${delta:-0}
bind_type=${bind_type:-port}
else
# Data transfer. Translate small/large to dd-recognizable sizes
case "$bytes" in
small) bytes="2k" ;;
large) case "$proto" in
tcp) bytes="10M" ;;
udp) bytes=$(($(cat /proc/sys/net/core/wmem_default) / 4)) ;;
*) die "Internal error: unknown proto '$proto'" ;;
esac
;;
*) die "Internal error: unknown transfer size '$bytes'" ;;
esac
# On data transfers, no other input args can be set in test name.
# Confirm that they are not defined, and set to a suitable default.
kw="something"
_set_opt range 1
_set_opt delta 0
_set_opt bind_type port
fi
# Dup check: make sure we haven't already run this combination of settings.
# This serves two purposes:
# 1) prevent developer from accidentally copy/pasting the same test
# 2) make sure our test-name-parsing code isn't missing anything important
local tests_run=${BATS_FILE_TMPDIR}/tests_run
touch ${tests_run}
local testid="IPv${ip_ver} $proto $iftype $bind_type range=$range delta=$delta bytes=$bytes"
if grep -q -F -x -- "$testid" ${tests_run}; then
die "Duplicate test! Have already run $testid"
fi
echo "$testid" >>${tests_run}
# Done figuring out test params. Now do the real work.
# Calculate and set addresses,
if [ ${ip_ver} -eq 4 ]; then
skip_if_no_ipv4 "IPv4 not routable on the host"
elif [ ${ip_ver} -eq 6 ]; then
skip_if_no_ipv6 "IPv6 not routable on the host"
else
skip "Unsupported IP version"
fi
if [ ${iftype} = "loopback" ]; then
local ifname="lo"
else
local ifname="$(default_ifname "${ip_ver}")"
fi
local addr="$(default_addr "${ip_ver}" "${ifname}")"
# ports,
if [ ${range} -gt 1 ]; then
local port="$(random_free_port_range ${range} ${addr} ${proto})"
local xport="$((${port%-*} + delta))-$((${port#*-} + delta))"
local seq="$(echo ${port} | tr '-' ' ')"
local xseq="$(echo ${xport} | tr '-' ' ')"
else
local port=$(random_free_port "" ${addr} ${proto})
local xport="$((port + delta))"
local seq="${port} ${port}"
local xseq="${xport} ${xport}"
fi
local proto_upper="$(echo "${proto}" | tr [:lower:] [:upper:])"
# socat options for first <address> in server ("LISTEN" address types),
local bind="${proto_upper}${ip_ver}-LISTEN:\${port}"
if [ "${proto}" = "udp" ]; then
bind="${bind},null-eof"
fi
# socat options for second <address> in server ("STDOUT" or "EXEC"),
local recvhelper=
if [ "${bytes}" = "1" ]; then
recv="STDOUT"
else
# To ease debugging in case of problems, use a helper that
# gives us byte count, hash, and first/last few bytes
recvhelper=/home/podman/bytecheck
recv="EXEC:$recvhelper"
fi
# and port forwarding configuration for Podman and pasta.
#
# TODO: Use Podman options once/if
# https://github.com/containers/podman/issues/14425 is solved
case ${bind_type} in
"interface")
local pasta_spec=":--${proto}-ports,${addr}%${ifname}/${port}:${xport}"
local podman_spec=
;;
"address")
local pasta_spec=
local podman_spec="[${addr}]:${port}:${xport}/${proto}"
;;
*)
local pasta_spec=
local podman_spec="[${addr}]:${port}:${xport}/${proto}"
;;
esac
# Fill in file for data transfer tests, and expected output strings
if [ "${bytes}" != "1" ]; then
dd if=/dev/urandom bs=${bytes} count=1 of="${XFER_FILE}"
run_podman run -i --rm $IMAGE $recvhelper < ${XFER_FILE}
local expect="$output"
else
printf "x" > "${XFER_FILE}"
local expect="$(for i in $(seq ${seq}); do printf "x"; done)"
fi
# Start server
local cname="c-socat-$(safename)"
run_podman run -d --name="$cname" --net=pasta"${pasta_spec}" -p "${podman_spec}" "${IMAGE}" \
sh -c 'for port in $(seq '"${xseq}"'); do '\
' socat -u '"${bind}"' '"${recv}"' & '\
' done; wait'
# Make sure all ports in the container are bound.
for cport in $(seq ${xseq}); do
retries=50
while [[ $retries -gt 0 ]]; do
run_podman exec $cname ss -Hln -$ip_ver --$proto sport = "$cport"
if [[ "$output" =~ "$cport" ]]; then
break
fi
retries=$((retries - 1))
sleep 0.1
done
assert $retries -gt 0 "Timed out waiting for bound port $cport in container"
done
if [ ${ip_ver} -eq 6 ] && [ ${iftype} = "tap" ]; then
# For IPv6 tests via tap interface, we use pairs of link-local
# addresses to communicate. While we disable DAD on all the
# (global unicast) addresses we copy from the host, or
# otherwise set, link-local addresses are automatically added
# by the kernel with DAD, so we need to wait until it's done.
sleep 2
fi
for hport in $(seq ${seq}); do
local connect="${proto_upper}${ip_ver}:[${addr}]:${hport}"
[ "${proto}" = "udp" ] && connect="${connect},shut-null"
# Ports are bound: we can start the client in the background.
timeout --foreground -v --kill=5 90 socat -u "OPEN:${XFER_FILE}" "${connect}" &
done
# Wait for the client children to finish.
wait
# Get server output, --follow is used to wait for the container to exit,
run_podman logs --follow $cname
# which should give us the expected output back.
# ...except, sigh, #23482: seems to be a bug in socat, issues spurious warning
if [[ "$recv" =~ EXEC ]]; then
output=$(grep -vE 'socat.*waitpid.*No child process' <<<"$output")
fi
assert "${output}" = "${expect}" "Mismatch between data sent and received"
run_podman rm $cname
}
### Addresses ##################################################################
@test "IPv4 default address assignment" {
skip_if_no_ipv4 "IPv4 not routable on the host"
run_podman run --rm --net=pasta $IMAGE ip -j -4 address show
local container_address="$(ipv4_get_addr_global "${output}")"
local host_address="$(default_addr 4)"
assert "${container_address}" = "${host_address}" \
"Container address not matching host"
}
@test "IPv4 address assignment" {
skip_if_no_ipv4 "IPv4 not routable on the host"
run_podman run --rm --net=pasta:-a,192.0.2.1 $IMAGE ip -j -4 address show
local container_address="$(ipv4_get_addr_global "${output}")"
assert "${container_address}" = "192.0.2.1" \
"Container address not matching configured value"
}
@test "No IPv4" {
skip_if_no_ipv4 "IPv4 not routable on the host"
skip_if_no_ipv6 "IPv6 not routable on the host"
run_podman run --rm --net=pasta:-6 $IMAGE ip -j -4 address show
local container_address="$(ipv4_get_addr_global "${output}")"
assert "${container_address}" = "null" \
"Container has IPv4 global address with IPv4 disabled"
}
@test "IPv6 default address assignment" {
skip_if_no_ipv6 "IPv6 not routable on the host"
run_podman run --rm --net=pasta $IMAGE ip -j -6 address show
local container_address="$(ipv6_get_addr_global "${output}")"
local host_address="$(default_addr 6)"
assert "${container_address}" = "${host_address}" \
"Container address not matching host"
}
@test "IPv6 address assignment" {
skip_if_no_ipv6 "IPv6 not routable on the host"
run_podman run --rm --net=pasta:-a,2001:db8::1 $IMAGE ip -j -6 address show
local container_address="$(ipv6_get_addr_global "${output}")"
assert "${container_address}" = "2001:db8::1" \
"Container address not matching configured value"
}
@test "No IPv6" {
skip_if_no_ipv6 "IPv6 not routable on the host"
skip_if_no_ipv4 "IPv4 not routable on the host"
run_podman run --rm --net=pasta:-4 $IMAGE ip -j -6 address show
local container_address="$(ipv6_get_addr_global "${output}")"
assert "${container_address}" = "null" \
"Container has IPv6 global address with IPv6 disabled"
}
@test "podman puts pasta IP in /etc/hosts" {
skip_if_no_ipv4 "IPv4 not routable on the host"
pname="p-$(safename)"
ip="$(default_addr 4)"
run_podman pod create --net=pasta --name "${pname}"
run_podman run --pod="${pname}" "${IMAGE}" getent hosts "${pname}"
assert "$(echo ${output} | cut -f1 -d' ')" = "${ip}" "Correct /etc/hosts entry missing"
run_podman pod rm "${pname}"
}
### Routes #####################################################################
@test "IPv4 default route" {
skip_if_no_ipv4 "IPv4 not routable on the host"
run_podman run --rm --net=pasta $IMAGE ip -j -4 route show
local container_route="$(ipv4_get_route_default "${output}")"
local host_route="$(ipv4_get_route_default)"
assert "${container_route}" = "${host_route}" \
"Container route not matching host"
}
@test "IPv4 default route assignment" {
skip_if_no_ipv4 "IPv4 not routable on the host"
run_podman run --rm --net=pasta:-a,192.0.2.2,-g,192.0.2.1 $IMAGE \
ip -j -4 route show
local container_route="$(ipv4_get_route_default "${output}")"
assert "${container_route}" = "192.0.2.1" \
"Container route not matching configured value"
}
@test "IPv6 default route" {
skip_if_no_ipv6 "IPv6 not routable on the host"
run_podman run --rm --net=pasta $IMAGE ip -j -6 route show
local container_route="$(ipv6_get_route_default "${output}")"
local host_route="$(ipv6_get_route_default)"
assert "${container_route}" = "${host_route}" \
"Container route not matching host"
}
@test "IPv6 default route assignment" {
skip_if_no_ipv6 "IPv6 not routable on the host"
run_podman run --rm --net=pasta:-a,2001:db8::2,-g,2001:db8::1 $IMAGE \
ip -j -6 route show
local container_route="$(ipv6_get_route_default "${output}")"
assert "${container_route}" = "2001:db8::1" \
"Container route not matching configured value"
}
### Interfaces #################################################################
@test "Default MTU" {
run_podman run --rm --net=pasta $IMAGE ip -j link show
container_tap_mtu="$(ether_get_mtu "${output}")"
assert "${container_tap_mtu}" = "65520" \
"Container's default MTU not 65220 bytes by default"
}
@test "MTU assignment" {
run_podman run --rm --net=pasta:-m,1280 $IMAGE ip -j link show
container_tap_mtu="$(ether_get_mtu "${output}")"
assert "${container_tap_mtu}" = "1280" \
"Container's default MTU not matching configured 1280 bytes"
}
@test "Loopback interface state" {
run_podman run --rm --net=pasta $IMAGE ip -j link show
local jq_expr='.[] | select(.link_type == "loopback").flags | '\
' contains(["UP"])'
container_loopback_up="$(printf '%s' "${output}" | jq -rM "${jq_expr}")"
assert "${container_loopback_up}" = "true" \
"Container's loopback interface not up"
}
### DNS ########################################################################
@test "Basic nameserver lookup" {
run_podman run --rm --net=pasta $IMAGE nslookup l.root-servers.net
}
@test "Default nameserver forwarding" {
skip_if_no_ipv4 "IPv4 not routable on the host"
# pasta is the default now so no need to set it
run_podman run --rm $IMAGE grep nameserver /etc/resolv.conf
assert "${lines[0]}" == "nameserver 169.254.1.1" "default dns forward server"
}
@test "Custom DNS forward address, IPv4" {
skip_if_no_ipv4 "IPv4 not routable on the host"
local addr=198.51.100.1
run_podman run --rm --net=pasta:--dns-forward,$addr \
$IMAGE grep nameserver /etc/resolv.conf
assert "${lines[0]}" == "nameserver $addr" "custom dns forward server"
run_podman run --rm --net=pasta:--dns-forward,$addr \
$IMAGE nslookup l.root-servers.net $addr
}
@test "Custom DNS forward address, IPv6" {
skip_if_no_ipv6 "IPv6 not routable on the host"
# TODO: In fact, this requires not just IPv6 connectivity on the
# host, but an IPv6 reachable nameserver which is harder to
# test for. We could remove that requirement if pasta could
# forward between IPv4 and IPv6 addresses but as of
# 2024_09_06.6b38f07 that's unsupported. Skip the test for
# now.
skip "Currently unsupported"
# local addr=2001:db8::1
#
# run_podman run --rm --net=pasta:--dns-forward,$addr \
# $IMAGE grep nameserver /etc/resolv.conf
# assert "${lines[0]}" == "nameserver $addr" "custom dns forward server"
# run_podman run --rm --net=pasta:--dns-forward,$addr \
# $IMAGE nslookup l.root-servers.net $addr
#
# TODO: In addition to the IPv6 nameserver requirement above,
# there seem to be two problems running this test. It's
# unclear if those are in busybox, musl or pasta.
#
# 1. With this, Podman writes "nameserver 2001:db8::1" to
# /etc/resolv.conf, without zone, and the query originates from ::1.
# Passing:
# --dns "2001:db8::2%eth0"
# results in:
# Error: 2001:db8::2%eth0 is not an ip address
# Fix the issue in Podman once we figure out 2. below.
#
#
# run_podman run --dns 2001:db8::1 \
# --net=pasta:--dns-forward,2001:db8::1 $IMAGE \
# sh -c 'echo 2001:db8::1%eth0 >/etc/resolv.conf; nslookup ::1'
#
# 2. This fixes the source address of the query, but the answer is
# discarded. Figure out if it's an issue in Busybox, in musl, if we
# should just include a full-fledged resolver in the test image, etc.
}
### TCP/IPv4 Port Forwarding ###################################################
@test "Single TCP port forwarding, IPv4, tap" {
pasta_test_do
}
@test "Single TCP port forwarding, IPv4, loopback" {
pasta_test_do
}
@test "TCP port range forwarding, IPv4, tap" {
pasta_test_do
}
@test "TCP port range forwarding, IPv4, loopback" {
pasta_test_do
}
@test "Translated TCP port forwarding, IPv4, tap" {
pasta_test_do
}
@test "Translated TCP port forwarding, IPv4, loopback" {
pasta_test_do
}
@test "TCP translated port range forwarding, IPv4, tap" {
pasta_test_do
}
@test "TCP translated port range forwarding, IPv4, loopback" {
pasta_test_do
}
@test "Address-bound TCP port forwarding, IPv4, tap" {
pasta_test_do
}
@test "Address-bound TCP port forwarding, IPv4, loopback" {
pasta_test_do
}
@test "Interface-bound TCP port forwarding, IPv4, tap" {
pasta_test_do
}
@test "Interface-bound TCP port forwarding, IPv4, loopback" {
pasta_test_do
}
### TCP/IPv6 Port Forwarding ###################################################
@test "Single TCP port forwarding, IPv6, tap" {
pasta_test_do
}
@test "Single TCP port forwarding, IPv6, loopback" {
pasta_test_do
}
@test "TCP port range forwarding, IPv6, tap" {
pasta_test_do
}
@test "TCP port range forwarding, IPv6, loopback" {
pasta_test_do
}
@test "Translated TCP port forwarding, IPv6, tap" {
pasta_test_do
}
@test "Translated TCP port forwarding, IPv6, loopback" {
pasta_test_do
}
@test "TCP translated port range forwarding, IPv6, tap" {
pasta_test_do
}
@test "TCP translated port range forwarding, IPv6, loopback" {
pasta_test_do
}
@test "Address-bound TCP port forwarding, IPv6, tap" {
pasta_test_do
}
@test "Address-bound TCP port forwarding, IPv6, loopback" {
pasta_test_do
}
@test "Interface-bound TCP port forwarding, IPv6, tap" {
pasta_test_do
}
@test "Interface-bound TCP port forwarding, IPv6, loopback" {
pasta_test_do
}
### UDP/IPv4 Port Forwarding ###################################################
@test "Single UDP port forwarding, IPv4, tap" {
pasta_test_do
}
@test "Single UDP port forwarding, IPv4, loopback" {
pasta_test_do
}
@test "UDP port range forwarding, IPv4, tap" {
pasta_test_do
}
@test "UDP port range forwarding, IPv4, loopback" {
pasta_test_do
}
@test "Translated UDP port forwarding, IPv4, tap" {
pasta_test_do
}
@test "Translated UDP port forwarding, IPv4, loopback" {
pasta_test_do
}
@test "UDP translated port range forwarding, IPv4, tap" {
pasta_test_do
}
@test "UDP translated port range forwarding, IPv4, loopback" {
pasta_test_do
}
@test "Address-bound UDP port forwarding, IPv4, tap" {
pasta_test_do
}
@test "Address-bound UDP port forwarding, IPv4, loopback" {
pasta_test_do
}
@test "Interface-bound UDP port forwarding, IPv4, tap" {
pasta_test_do
}
@test "Interface-bound UDP port forwarding, IPv4, loopback" {
pasta_test_do
}
### UDP/IPv6 Port Forwarding ###################################################
@test "Single UDP port forwarding, IPv6, tap" {
pasta_test_do
}
@test "Single UDP port forwarding, IPv6, loopback" {
pasta_test_do
}
@test "UDP port range forwarding, IPv6, tap" {
pasta_test_do
}
@test "UDP port range forwarding, IPv6, loopback" {
pasta_test_do
}
@test "Translated UDP port forwarding, IPv6, tap" {
pasta_test_do
}
@test "Translated UDP port forwarding, IPv6, loopback" {
pasta_test_do
}
@test "UDP translated port range forwarding, IPv6, tap" {
pasta_test_do
}
@test "UDP translated port range forwarding, IPv6, loopback" {
pasta_test_do
}
@test "Address-bound UDP port forwarding, IPv6, tap" {
pasta_test_do
}
@test "Address-bound UDP port forwarding, IPv6, loopback" {
pasta_test_do
}
@test "Interface-bound UDP port forwarding, IPv6, tap" {
pasta_test_do
}
@test "Interface-bound UDP port forwarding, IPv6, loopback" {
pasta_test_do
}
### TCP/IPv4 transfer ##########################################################
@test "TCP/IPv4 small transfer, tap" {
pasta_test_do
}
@test "TCP/IPv4 small transfer, loopback" {
pasta_test_do
}
@test "TCP/IPv4 large transfer, tap" {
pasta_test_do
}
@test "TCP/IPv4 large transfer, loopback" {
pasta_test_do
}
### TCP/IPv6 transfer ##########################################################
@test "TCP/IPv6 small transfer, tap" {
pasta_test_do
}
@test "TCP/IPv6 small transfer, loopback" {
pasta_test_do
}
@test "TCP/IPv6 large transfer, tap" {
pasta_test_do
}
@test "TCP/IPv6 large transfer, loopback" {
pasta_test_do
}
### UDP/IPv4 transfer ##########################################################
@test "UDP/IPv4 small transfer, tap" {
pasta_test_do
}
@test "UDP/IPv4 small transfer, loopback" {
pasta_test_do
}
@test "UDP/IPv4 large transfer, tap" {
pasta_test_do
}
@test "UDP/IPv4 large transfer, loopback" {
pasta_test_do
}
### UDP/IPv6 transfer ##########################################################
@test "UDP/IPv6 small transfer, tap" {
pasta_test_do
}
@test "UDP/IPv6 small transfer, loopback" {
pasta_test_do
}
@test "UDP/IPv6 large transfer, tap" {
pasta_test_do
}
@test "UDP/IPv6 large transfer, loopback" {
pasta_test_do
}
### Lifecycle ##################################################################
@test "pasta(1) quits when the namespace is gone" {
local pidfile="${PODMAN_TMPDIR}/pasta.pid"
run_podman run --rm "--net=pasta:--pid,${pidfile}" $IMAGE true
# Allow time for process to vanish, in case there's high load
local pid=$(< $pidfile)
local timeout=5
while [[ $timeout -gt 0 ]]; do
if ! ps -p $pid; then
return
fi
# Still alive. Wait and retry
sleep 1
timeout=$((timeout - 1))
done
die "Timed out waiting for pid $pid to terminate"
}
### Options ####################################################################
@test "Unsupported protocol in port forwarding" {
local port=$(random_free_port "" "" tcp)
run_podman 126 run --rm --net=pasta -p "${port}:${port}/sctp" $IMAGE true
is "$output" "Error: .*can't forward protocol: sctp"
}
@test "Use options from containers.conf" {
skip_if_remote "containers.conf must be set for the server"
containersconf=$PODMAN_TMPDIR/containers.conf
mac="9a:dd:31:ea:92:98"
cat >$containersconf <<EOF
[network]
default_rootless_network_cmd = "pasta"
pasta_options = ["-I", "myname", "--ns-mac-addr", "$mac"]
EOF
# 2023-06-29 DO NOT INCLUDE "--net=pasta" on this line!
# This tests containers.conf:default_rootless_network_cmd (pr #19032)
CONTAINERS_CONF_OVERRIDE=$containersconf run_podman run --rm $IMAGE ip link show myname
assert "$output" =~ "$mac" "mac address is set on custom interface"
# now, again but this time overwrite a option on the cli.
mac2="aa:bb:cc:dd:ee:ff"
CONTAINERS_CONF_OVERRIDE=$containersconf run_podman run --rm \
--net=pasta:--ns-mac-addr,"$mac2" $IMAGE ip link show myname
assert "$output" =~ "$mac2" "mac address from cli is set on custom interface"
}
# https://github.com/containers/podman/issues/22653
@test "pasta/bridge and host.containers.internal" {
skip_if_no_ipv4 "IPv4 not routable on the host"
pasta_ip="$(default_addr 4)"
host_ips=$(ip -4 -j addr | jq -r '.[] | select(.ifname != "lo") | .addr_info[].local')
netname=n_$(safename)
run_podman network create $netname
for network in "pasta" "$netname"; do
# special exit code logic needed here, it is possible that there is no host.containers.internal
# when there is only one ip one the host and that one is used by pasta.
# As such we have to deal with both cases.
run_podman '?' run --rm --network=$network $IMAGE grep host.containers.internal /etc/hosts
if [ "$status" -eq 0 ]; then
assert "$output" !~ "$pasta_ip" "pasta host ip must not be assigned ($network)"
# even more special we use a new --map-guest-addr pasta option and
# to map 169.254.1.2 to the host, https://github.com/containers/common/pull/2136
assert "$host_ips 169.254.1.2" =~ "$(cut -f1 <<<$output)" "ip is one of the host ips ($network)"
elif [ "$status" -eq 1 ]; then
# if only pasta ip then we cannot have a host.containers.internal entry
# make sure this fact is actually the case
assert "$pasta_ip" == "$host_ips" "pasta ip must the only one one the host ($network)"
else
die "unexpected exit code '$status' from grep or podman ($network)"
fi
done
run_podman network rm $netname
first_host_ip=$(head -n 1 <<<"$host_ips")
run_podman run --rm --network=pasta:-a,192.168.0.2,-g,192.168.0.1,-n,24 $IMAGE grep host.containers.internal /etc/hosts
assert "$output" =~ "^($first_host_ip|169.254.1.2)" "uses first host ip or special 169.254.1.2 --map-guest-addr"
}