diff --git a/test/system/505-networking-pasta.bats b/test/system/505-networking-pasta.bats new file mode 100644 index 0000000000..ab68e1d9d2 --- /dev/null +++ b/test/system/505-networking-pasta.bats @@ -0,0 +1,683 @@ +#!/usr/bin/env bats -*- bats -*- +# +# SPDX-License-Identifier: Apache-2.0 +# +# Networking with pasta(1) +# +# Copyright (c) 2022 Red Hat GmbH +# Author: Stefano Brivio + +load helpers +load helpers.network + +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" +} + +# pasta_test_do() - Run tests involving clients and servers +# $1: IP version: 4 or 6 +# $2: Interface type: "tap" or "loopback" +# $3: Protocol: "tcp" or "udp" +# $4: Size of port range, 1 for tests over a single port +# $5: Delta for remapped destination ports in guest +# $6: How specifically pasta will bind: "port", "address", or "interface" +# $7: Bytes to transfer, with multiplicative suffixes as supported by dd(1) +function pasta_test_do() { + local ip_ver="${1}" + local iftype="${2}" + local proto="${3}" + local range="${4}" + local delta="${5}" + local bind_type="${6}" + local bytes="${7}" + + # Calculate and set addresses, + if [ ${ip_ver} -eq 4 ]; then + skip_if_no_ipv4 "IPv4 not routable on the host" + if [ ${iftype} = "loopback" ]; then + local addr="127.0.0.1" + else + local addr="$(ipv4_get_addr_global)" + fi + elif [ ${ip_ver} -eq 6 ]; then + skip_if_no_ipv6 "IPv6 not routable on the host" + if [ ${iftype} = "loopback" ]; then + local addr="::1" + else + local addr="$(ipv6_get_addr_global)" + fi + else + skip "Unsupported IP version" + fi + + # interface names, + if [ ${iftype} = "loopback" ]; then + local ifname="lo" + else + local ifname="$(ether_get_name)" + fi + + # 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 "" ${address} ${proto}) + local xport="$((port + port_delta))" + local seq="${port} ${port}" + local xseq="${xport} ${xport}" + fi + + local proto_upper="$(echo "${proto}" | tr [:lower:] [:upper:])" + + # socat options for first
in server ("LISTEN" address types), + local bind="${proto_upper}${ip_ver}-LISTEN:\${port}" + # For IPv6 via tap, we can pick either link-local or global unicast + if [ ${ip_ver} -eq 4 ] || [ ${iftype} = "loopback" ]; then + bind="${bind},bind=[${addr}]" + fi + if [ "${proto}" = "udp" ]; then + bind="${bind},null-eof" + fi + + # socat options for second
in server ("STDOUT" or "EXEC"), + if [ "${bytes}" = "1" ]; then + recv="STDOUT" + else + recv="EXEC:md5sum" + fi + + # socat options for first
in client ("OPEN" or "EXEC"), + if [ "${bytes}" = "1" ]; then + send="EXEC:printf x" + else + send="OPEN:${XFER_FILE}" + 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}" + local expect="$(cat "${XFER_FILE}" | md5sum)" + else + local expect="$(for i in $(seq ${seq}); do printf "x"; done)" + fi + + # Set retry/initial delay for client to connect + local delay=1 + if [ ${ip_ver} -eq 6 ] && [ "${addr}" != "::1" ]; then + # Duplicate Address Detection on link-local + delay=3 + elif [ "${proto}" = "udp" ]; then + # With Podman up, and socat still starting, UDP clients send to nowhere + delay=2 + fi + + # Now actually run the test: client, + for one_port in $(seq ${seq}); do + local connect="${proto_upper}${ip_ver}:[${addr}]:${one_port}" + [ "${proto}" = "udp" ] && connect="${connect},shut-null" + + (while sleep ${delay} && ! socat -u "${send}" "${connect}"; do : + done) & + done + + # and server, + run_podman run --net=pasta"${pasta_spec}" -p "${podman_spec}" "${IMAGE}" \ + sh -c 'for port in $(seq '"${xseq}"'); do '\ +' socat -u '"${bind}"' '"${recv}"' & '\ +' done; wait' + + # which should give us the expected output back. + assert "${output}" = "${expect}" "Mismatch between data sent and received" +} + +function teardown() { + rm -f "${XFER_FILE}" +} + +### Addresses ################################################################## + +@test "podman networking with pasta(1) - IPv4 default address assignment" { + skip_if_no_ipv4 "IPv4 not routable on the host" + + run_podman run --net=pasta $IMAGE ip -j -4 address show + + local container_address="$(ipv4_get_addr_global "${output}")" + local host_address="$(ipv4_get_addr_global)" + + assert "${container_address}" = "${host_address}" \ + "Container address not matching host" +} + +@test "podman networking with pasta(1) - IPv4 address assignment" { + skip_if_no_ipv4 "IPv4 not routable on the host" + + run_podman run --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 "podman networking with pasta(1) - 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 --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 "podman networking with pasta(1) - IPv6 default address assignment" { + skip_if_no_ipv6 "IPv6 not routable on the host" + + run_podman run --net=pasta $IMAGE ip -j -6 address show + + local container_address="$(ipv6_get_addr_global "${output}")" + local host_address="$(ipv6_get_addr_global)" + + assert "${container_address}" = "${host_address}" \ + "Container address not matching host" +} + +@test "podman networking with pasta(1) - IPv6 address assignment" { + skip_if_no_ipv6 "IPv6 not routable on the host" + + run_podman run --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 "podman networking with pasta(1) - 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 --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" +} + +### Routes ##################################################################### + +@test "podman networking with pasta(1) - IPv4 default route" { + skip_if_no_ipv4 "IPv4 not routable on the host" + + run_podman run --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 "podman networking with pasta(1) - IPv4 default route assignment" { + skip_if_no_ipv4 "IPv4 not routable on the host" + + run_podman run --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 "podman networking with pasta(1) - IPv6 default route" { + skip_if_no_ipv6 "IPv6 not routable on the host" + + run_podman run --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 "podman networking with pasta(1) - IPv6 default route assignment" { + skip_if_no_ipv6 "IPv6 not routable on the host" + + run_podman run --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 "podman networking with pasta(1) - Default MTU" { + run_podman run --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 "podman networking with pasta(1) - MTU assignment" { + run_podman run --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 "podman networking with pasta(1) - Loopback interface state" { + run_podman run --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 "podman networking with pasta(1) - External resolver, IPv4" { + skip_if_no_ipv4 "IPv4 not routable on the host" + + run_podman run --net=pasta $IMAGE nslookup 127.0.0.1 || : + + assert "$output" =~ "1.0.0.127.in-addr.arpa" \ + "127.0.0.1 not resolved" +} + +@test "podman networking with pasta(1) - External resolver, IPv6" { + skip_if_no_ipv6 "IPv6 not routable on the host" + + run_podman run --net=pasta $IMAGE nslookup ::1 || : + + assert "$output" =~ "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa" \ + "::1 not resolved" +} + +@test "podman networking with pasta(1) - Local forwarder, IPv4" { + skip_if_no_ipv4 "IPv4 not routable on the host" + + run_podman run --dns 198.51.100.1 \ + --net=pasta:--dns-forward,198.51.100.1 $IMAGE nslookup 127.0.0.1 + + assert "$output" =~ "1.0.0.127.in-addr.arpa" \ + "127.0.0.1 not resolved to 1.0.0.127.in-addr.arpa" +} + +@test "podman networking with pasta(1) - Local forwarder, IPv6" { + skip_if_no_ipv6 "IPv6 not routable on the host" + + # TODO: Two issues here: + skip "Currently unsupported" + # run_podman run --dns 2001:db8::1 \ + # --net=pasta:--dns-forward,2001:db8::1 $IMAGE nslookup ::1 + # + # 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 "podman networking with pasta(1) - Single TCP port forwarding, IPv4, tap" { + pasta_test_do 4 tap tcp 1 0 "port" 1 +} + +@test "podman networking with pasta(1) - Single TCP port forwarding, IPv4, loopback" { + pasta_test_do 4 loopback tcp 1 0 "port" 1 +} + +@test "podman networking with pasta(1) - TCP port range forwarding, IPv4, tap" { + pasta_test_do 4 tap tcp 3 0 "port" 1 +} + +@test "podman networking with pasta(1) - TCP port range forwarding, IPv4, loopback" { + pasta_test_do 4 loopback tcp 3 0 "port" 1 +} + +@test "podman networking with pasta(1) - Translated TCP port forwarding, IPv4, tap" { + pasta_test_do 4 tap tcp 1 1 "port" 1 +} + +@test "podman networking with pasta(1) - Translated TCP port forwarding, IPv4, loopback" { + pasta_test_do 4 loopback tcp 1 1 "port" 1 +} + +@test "podman networking with pasta(1) - TCP translated port range forwarding, IPv4, tap" { + pasta_test_do 4 tap tcp 3 1 "port" 1 +} + +@test "podman networking with pasta(1) - TCP translated port range forwarding, IPv4, loopback" { + pasta_test_do 4 loopback tcp 3 1 "port" 1 +} + +@test "podman networking with pasta(1) - Address-bound TCP port forwarding, IPv4, tap" { + pasta_test_do 4 tap tcp 1 0 "address" 1 +} + +@test "podman networking with pasta(1) - Address-bound TCP port forwarding, IPv4, loopback" { + pasta_test_do 4 loopback tcp 1 0 "address" 1 +} + +@test "podman networking with pasta(1) - Interface-bound TCP port forwarding, IPv4, tap" { + pasta_test_do 4 tap tcp 1 0 "interface" 1 +} + +@test "podman networking with pasta(1) - Interface-bound TCP port forwarding, IPv4, loopback" { + pasta_test_do 4 loopback tcp 1 0 "interface" 1 +} + +### TCP/IPv6 Port Forwarding ################################################### + +@test "podman networking with pasta(1) - Single TCP port forwarding, IPv6, tap" { + pasta_test_do 6 tap tcp 1 0 "port" 1 +} + +@test "podman networking with pasta(1) - Single TCP port forwarding, IPv6, loopback" { + pasta_test_do 6 loopback tcp 1 0 "port" 1 +} + +@test "podman networking with pasta(1) - TCP port range forwarding, IPv6, tap" { + pasta_test_do 6 tap tcp 3 0 "port" 1 +} + +@test "podman networking with pasta(1) - TCP port range forwarding, IPv6, loopback" { + pasta_test_do 6 loopback tcp 3 0 "port" 1 +} + +@test "podman networking with pasta(1) - Translated TCP port forwarding, IPv6, tap" { + pasta_test_do 6 tap tcp 1 1 "port" 1 +} + +@test "podman networking with pasta(1) - Translated TCP port forwarding, IPv6, loopback" { + pasta_test_do 6 loopback tcp 1 1 "port" 1 +} + +@test "podman networking with pasta(1) - TCP translated port range forwarding, IPv6, tap" { + pasta_test_do 6 tap tcp 3 1 "port" 1 +} + +@test "podman networking with pasta(1) - TCP translated port range forwarding, IPv6, loopback" { + pasta_test_do 6 loopback tcp 3 1 "port" 1 +} + +@test "podman networking with pasta(1) - Address-bound TCP port forwarding, IPv6, tap" { + pasta_test_do 6 tap tcp 1 0 "address" 1 +} + +@test "podman networking with pasta(1) - Address-bound TCP port forwarding, IPv6, loopback" { + pasta_test_do 6 loopback tcp 1 0 "address" 1 +} + +@test "podman networking with pasta(1) - Interface-bound TCP port forwarding, IPv6, tap" { + pasta_test_do 6 tap tcp 1 0 "interface" 1 +} + +@test "podman networking with pasta(1) - Interface-bound TCP port forwarding, IPv6, loopback" { + pasta_test_do 6 loopback tcp 1 0 "interface" 1 +} + +### UDP/IPv4 Port Forwarding ################################################### + +@test "podman networking with pasta(1) - Single UDP port forwarding, IPv4, tap" { + pasta_test_do 4 tap udp 1 0 "port" 1 +} + +@test "podman networking with pasta(1) - Single UDP port forwarding, IPv4, loopback" { + pasta_test_do 4 loopback udp 1 0 "port" 1 +} + +@test "podman networking with pasta(1) - UDP port range forwarding, IPv4, tap" { + pasta_test_do 4 tap udp 3 0 "port" 1 +} + +@test "podman networking with pasta(1) - UDP port range forwarding, IPv4, loopback" { + pasta_test_do 4 loopback udp 3 0 "port" 1 +} + +@test "podman networking with pasta(1) - Translated UDP port forwarding, IPv4, tap" { + pasta_test_do 4 tap udp 1 1 "port" 1 +} + +@test "podman networking with pasta(1) - Translated UDP port forwarding, IPv4, loopback" { + pasta_test_do 4 loopback udp 1 1 "port" 1 +} + +@test "podman networking with pasta(1) - UDP translated port range forwarding, IPv4, tap" { + pasta_test_do 4 tap udp 3 1 "port" 1 +} + +@test "podman networking with pasta(1) - UDP translated port range forwarding, IPv4, loopback" { + pasta_test_do 4 loopback udp 3 1 "port" 1 +} + +@test "podman networking with pasta(1) - Address-bound UDP port forwarding, IPv4, tap" { + pasta_test_do 4 tap udp 1 0 "address" 1 +} + +@test "podman networking with pasta(1) - Address-bound UDP port forwarding, IPv4, loopback" { + pasta_test_do 4 loopback udp 1 0 "address" 1 +} + +@test "podman networking with pasta(1) - Interface-bound UDP port forwarding, IPv4, tap" { + pasta_test_do 4 tap udp 1 0 "interface" 1 +} + +@test "podman networking with pasta(1) - Interface-bound UDP port forwarding, IPv4, loopback" { + pasta_test_do 4 loopback udp 1 0 "interface" 1 +} + +### UDP/IPv6 Port Forwarding ################################################### + +@test "podman networking with pasta(1) - Single UDP port forwarding, IPv6, tap" { + pasta_test_do 6 tap udp 1 0 "port" 1 +} + +@test "podman networking with pasta(1) - Single UDP port forwarding, IPv6, loopback" { + pasta_test_do 6 loopback udp 1 0 "port" 1 +} + +@test "podman networking with pasta(1) - UDP port range forwarding, IPv6, tap" { + pasta_test_do 6 tap udp 3 0 "port" 1 +} + +@test "podman networking with pasta(1) - UDP port range forwarding, IPv6, loopback" { + pasta_test_do 6 loopback udp 3 0 "port" 1 +} + +@test "podman networking with pasta(1) - Translated UDP port forwarding, IPv6, tap" { + pasta_test_do 6 tap udp 1 1 "port" 1 +} + +@test "podman networking with pasta(1) - Translated UDP port forwarding, IPv6, loopback" { + pasta_test_do 6 loopback udp 1 1 "port" 1 +} + +@test "podman networking with pasta(1) - UDP translated port range forwarding, IPv6, tap" { + pasta_test_do 6 tap udp 3 1 "port" 1 +} + +@test "podman networking with pasta(1) - UDP translated port range forwarding, IPv6, loopback" { + pasta_test_do 6 loopback udp 3 1 "port" 1 +} + +@test "podman networking with pasta(1) - Address-bound UDP port forwarding, IPv6, tap" { + pasta_test_do 6 tap udp 1 0 "address" 1 +} + +@test "podman networking with pasta(1) - Address-bound UDP port forwarding, IPv6, loopback" { + pasta_test_do 6 loopback udp 1 0 "address" 1 +} + +@test "podman networking with pasta(1) - Interface-bound UDP port forwarding, IPv6, tap" { + pasta_test_do 6 tap udp 1 0 "interface" 1 +} + +@test "podman networking with pasta(1) - Interface-bound UDP port forwarding, IPv6, loopback" { + pasta_test_do 6 loopback udp 1 0 "interface" 1 +} + +### TCP/IPv4 transfer ########################################################## + +@test "podman networking with pasta(1) - TCP/IPv4 small transfer, tap" { + pasta_test_do 4 tap tcp 1 0 "port" 2k +} + +@test "podman networking with pasta(1) - TCP/IPv4 small transfer, loopback" { + pasta_test_do 4 loopback tcp 1 0 "port" 2k +} + +@test "podman networking with pasta(1) - TCP/IPv4 large transfer, tap" { + pasta_test_do 4 tap tcp 1 0 "port" 10M +} + +@test "podman networking with pasta(1) - TCP/IPv4 large transfer, loopback" { + pasta_test_do 4 loopback tcp 1 0 "port" 10M +} + +### TCP/IPv6 transfer ########################################################## + +@test "podman networking with pasta(1) - TCP/IPv6 small transfer, tap" { + pasta_test_do 6 tap tcp 1 0 "port" 2k +} + +@test "podman networking with pasta(1) - TCP/IPv6 small transfer, loopback" { + pasta_test_do 6 loopback tcp 1 0 "port" 2k +} + +@test "podman networking with pasta(1) - TCP/IPv6 large transfer, tap" { + pasta_test_do 6 tap tcp 1 0 "port" 10M +} + +@test "podman networking with pasta(1) - TCP/IPv6 large transfer, loopback" { + pasta_test_do 6 loopback tcp 1 0 "port" 10M +} + +### UDP/IPv4 transfer ########################################################## + +@test "podman networking with pasta(1) - UDP/IPv4 small transfer, tap" { + pasta_test_do 4 tap udp 1 0 "port" 2k +} + +@test "podman networking with pasta(1) - UDP/IPv4 small transfer, loopback" { + pasta_test_do 4 loopback udp 1 0 "port" 2k +} + +@test "podman networking with pasta(1) - UDP/IPv4 large transfer, tap" { + pasta_test_do 4 tap udp 1 0 "port" $(($(cat /proc/sys/net/core/wmem_default) / 4)) +} + +@test "podman networking with pasta(1) - UDP/IPv4 large transfer, loopback" { + pasta_test_do 4 loopback udp 1 0 "port" $(($(cat /proc/sys/net/core/wmem_default) / 4)) +} + +### UDP/IPv6 transfer ########################################################## + +@test "podman networking with pasta(1) - UDP/IPv6 small transfer, tap" { + pasta_test_do 6 tap udp 1 0 "port" 2k +} + +@test "podman networking with pasta(1) - UDP/IPv6 small transfer, loopback" { + pasta_test_do 6 loopback udp 1 0 "port" 2k +} + +@test "podman networking with pasta(1) - UDP/IPv6 large transfer, tap" { + pasta_test_do 6 tap udp 1 0 "port" $(($(cat /proc/sys/net/core/wmem_default) / 4)) +} + +@test "podman networking with pasta(1) - UDP/IPv6 large transfer, loopback" { + pasta_test_do 6 loopback udp 1 0 "port" $(($(cat /proc/sys/net/core/wmem_default) / 4)) +} + +### ICMP, ICMPv6 ############################################################### + +@test "podman networking with pasta(1) - ICMP echo request" { + skip_if_no_ipv4 "IPv6 not routable on the host" + + local minuid=$(cut -f1 /proc/sys/net/ipv4/ping_group_range) + local maxuid=$(cut -f2 /proc/sys/net/ipv4/ping_group_range) + + if [ $(id -u) -lt ${minuid} ] || [ $(id -u) -gt ${maxuid} ]; then + skip "ICMP echo sockets not available for this UID" + fi + + run_podman run --net=pasta $IMAGE \ + sh -c 'ping -c3 -W1 sed -nr "s/^nameserver[ ]{1,}([^.]*).(.*)/\1.\2/p" /etc/resolv.conf | head -1' +} + +@test "podman networking with pasta(1) - ICMPv6 echo request" { + skip_if_no_ipv6 "IPv6 not routable on the host" + + local minuid=$(cut -f1 /proc/sys/net/ipv4/ping_group_range) + local maxuid=$(cut -f2 /proc/sys/net/ipv4/ping_group_range) + + if [ $(id -u) -lt ${minuid} ] || [ $(id -u) -gt ${maxuid} ]; then + skip "ICMPv6 echo sockets not available for this UID" + fi + + run_podman run --net=pasta $IMAGE ping -6 -c3 -W1 \ + sh -c 'ping -c3 -W1 sed -nr "s/^nameserver[ ]{1,}([^:]*):(.*)/\1:\2/p" /etc/resolv.conf | head -1' +} + +### Lifecycle ################################################################## + +@test "podman networking with pasta(1) - pasta(1) quits when the namespace is gone" { + local pidfile="${PODMAN_TMPDIR}/pasta.pid" + + run_podman run "--net=pasta:--pid,${pidfile}" $IMAGE true + sleep 1 + ! ps -p $(cat "${pidfile}") && rm "${pidfile}" +} diff --git a/test/system/helpers.network.bash b/test/system/helpers.network.bash index 11286e13fb..52927b8c72 100644 --- a/test/system/helpers.network.bash +++ b/test/system/helpers.network.bash @@ -1,6 +1,51 @@ # -*- bash -*- +### Feature Checks ############################################################# + +# has_ipv4() - Check if one default route is available for IPv4 +function has_ipv4() { + [ -n "$(ip -j -4 route show | jq -rM '.[] | select(.dst == "default")')" ] +} + +# has_ipv6() - Check if one default route is available for IPv6 +function has_ipv6() { + [ -n "$(ip -j -6 route show | jq -rM '.[] | select(.dst == "default")')" ] +} + +# skip_if_no_ipv4() - Skip current test if IPv4 traffic can't be routed +# $1: Optional message to display +function skip_if_no_ipv4() { + if ! has_ipv4; then + local msg=$(_add_label_if_missing "$1" "IPv4") + skip "${msg:-not applicable with no routable IPv4}" + fi +} + +# skip_if_no_ipv6() - Skip current test if IPv6 traffic can't be routed +# $1: Optional message to display +function skip_if_no_ipv6() { + if ! has_ipv6; then + local msg=$(_add_label_if_missing "$1" "IPv6") + skip "${msg:-not applicable with no routable IPv6}" + fi +} + +# has_pasta() - Check if the pasta(1) command is available +function has_pasta() { + command -v pasta >/dev/null +} + +# skip_if_no_pasta() - Skip current test if pasta(1) is not available +# $1: Optional message to display +function skip_if_no_pasta() { + if ! has_pasta; then + local msg=$(_add_label_if_missing "$1" "pasta") + skip "${msg:-not applicable with no pasta binary}" + fi +} + + ### procfs access ############################################################## # ipv6_to_procfs() - RFC 5952 IPv6 address text representation to procfs format @@ -52,7 +97,21 @@ function ipv4_to_procfs() { } -### Addresses ################################################################## +### Addresses, Routes, Links ################################################### + +# ipv4_get_addr_global() - Print first global IPv4 address reported by netlink +# $1: Optional output of 'ip -j -4 address show' from a different context +function ipv4_get_addr_global() { + local expr='[.[].addr_info[] | select(.scope=="global")] | .[0].local' + echo "${1:-$(ip -j -4 address show)}" | jq -rM "${expr}" +} + +# ipv6_get_addr_global() - Print first global IPv6 address reported by netlink +# $1: Optional output of 'ip -j -6 address show' from a different context +function ipv6_get_addr_global() { + local expr='[.[].addr_info[] | select(.scope=="global")] | .[0].local' + echo "${1:-$(ip -j -6 address show)}" | jq -rM "${expr}" +} # random_rfc1918_subnet() - Pseudorandom unused subnet in 172.16/12 prefix # @@ -80,6 +139,34 @@ function random_rfc1918_subnet() { die "Could not find a random not-in-use rfc1918 subnet" } +# ipv4_get_route_default() - Print first default IPv4 route reported by netlink +# $1: Optional output of 'ip -j -4 route show' from a different context +function ipv4_get_route_default() { + local jq_expr='[.[] | select(.dst == "default").gateway] | .[0]' + echo "${1:-$(ip -j -4 route show)}" | jq -rM "${jq_expr}" +} + +# ipv6_get_route_default() - Print first default IPv6 route reported by netlink +# $1: Optional output of 'ip -j -6 route show' from a different context +function ipv6_get_route_default() { + local jq_expr='[.[] | select(.dst == "default").gateway] | .[0]' + echo "${1:-$(ip -j -6 route show)}" | jq -rM "${jq_expr}" +} + +# ether_get_mtu() - Get MTU of first Ethernet-like link +# $1: Optional output of 'ip -j link show' from a different context +function ether_get_mtu() { + local jq_expr='[.[] | select(.link_type == "ether").mtu] | .[0]' + echo "${1:-$(ip -j link show)}" | jq -rM "${jq_expr}" +} + +# ether_get_name() - Get name of first Ethernet-like interface +# $1: Optional output of 'ip -j link show' from a different context +function ether_get_name() { + local jq_expr='[.[] | select(.link_type == "ether").ifname] | .[0]' + echo "${1:-$(ip -j link show)}" | jq -rM "${jq_expr}" +} + ### Ports and Ranges ###########################################################