diff --git a/test/system/helpers.network.bash b/test/system/helpers.network.bash index 831d973afe..c8366ee419 100644 --- a/test/system/helpers.network.bash +++ b/test/system/helpers.network.bash @@ -126,11 +126,13 @@ function random_rfc1918_subnet() { local retries=1024 while [ "$retries" -gt 0 ];do - local cidr=172.$(( 16 + $RANDOM % 16 )).$(( $RANDOM & 255 )) + # 172.16.0.0 -> 172.31.255.255 + local n1=172 + local n2=$(( 16 + $RANDOM & 15 )) + local n3=$(( $RANDOM & 255 )) - in_use=$(ip route list | grep -F $cidr) - if [ -z "$in_use" ]; then - echo "$cidr" + if ! subnet_in_use $n1 $n2 $n3; then + echo "$n1.$n2.$n3" return fi @@ -140,6 +142,69 @@ function random_rfc1918_subnet() { die "Could not find a random not-in-use rfc1918 subnet" } +# subnet_in_use() - true if subnet already routed on host +function subnet_in_use() { + local subnet_script=${PODMAN_TMPDIR-/var/tmp}/subnet-in-use + rm -f $subnet_script + + # This would be a nightmare to do in bash. ipcalc, ipcalc-ng, sipcalc + # would be nice but are unavailable some environments (cough RHEL). + # Likewise python/perl netmask modules. So, use bare-bones perl. + cat >$subnet_script <<"EOF" +#!/usr/bin/env perl + +use strict; +use warnings; + +# 3 octets, in binary: 172.16.x -> 1010 1100 0000 1000 xxxx xxxx ... +my $subnet_to_check = sprintf("%08b%08b%08b", @ARGV); + +my $found = 0; + +# Input is "ip route list", one or more lines like '10.0.0.0/8 via ...' +while () { + # Only interested in x.x.x.x/n lines + if (m!^([\d.]+)/(\d+)!) { + my ($ip, $bits) = ($1, $2); + + # Our caller has /24 granularity, so treat /30 on host as /24. + $bits = 24 if $bits > 24; + + # Temporary: entire subnet as binary string. 4 octets, split, + # then represented as a 32-bit binary string. + my $net = sprintf("%08b%08b%08b%08b", split(/\./, $ip)); + + # Now truncate those 32 bits down to the route's netmask size. + # This is the actual subnet range in use on the host. + my $net_truncated = sprintf("%.*s", $bits, $net); + + # Desired subnet is in use if it matches a host route prefix +# print STDERR "--- $subnet_to_check in $net_truncated (@ARGV in $ip/$bits)\n"; + $found = 1 if $subnet_to_check =~ /^$net_truncated/; + } +} + +# Convert to shell exit status (0 = success) +exit !$found; +EOF + + chmod 755 $subnet_script + + # This runs 'ip route list', converts x.x.x.x/n to its binary prefix, + # then checks if our desired subnet matches that prefix (i.e. is in + # that range). Existing routes with size greater than 24 are + # normalized to /24 because that's the granularity of our + # random_rfc1918_subnet code. + # + # Contrived examples: + # 127.0.0.0/1 -> 0 + # 128.0.0.0/1 -> 1 + # 10.0.0.0/8 -> 00001010 + # + # I'm so sorry for the ugliness. + ip route list | $subnet_script $* +} + # 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() { diff --git a/test/system/helpers.t b/test/system/helpers.t index 3ae544f4f6..d767750622 100755 --- a/test/system/helpers.t +++ b/test/system/helpers.t @@ -18,6 +18,10 @@ die() { testnum=0 rc=0 +# Possibly used by the code we're testing +PODMAN_TMPDIR=$(mktemp -d --tmpdir=${TMPDIR:-/tmp} podman_helper_tests.XXXXXX) +trap 'rm -rf $PODMAN_TMPDIR' 0 + ############################################################################### # BEGIN test the parse_table helper @@ -242,6 +246,42 @@ done < <(parse_table "$table") # END ipv6_to_procfs ############################################################################### +# BEGIN subnet_in_use ... because that's complicated + +# Override ip command +function ip() { + echo "default foo" + echo "192.168.0.0/16" + echo "172.17.2.3/30" + echo "172.128.0.0/9" +} + +# x.y.z | result (1 = in use, 0 = not in use - opposite of exit code) +table=" +172 | 0 | 0 | 0 +172 | 0 | 255 | 0 +172 | 1 | 1 | 0 +172 | 1 | 2 | 0 +172 | 1 | 3 | 0 +172 | 17 | 1 | 0 +172 | 17 | 2 | 1 +172 | 17 | 3 | 0 +172 | 127 | 0 | 0 +172 | 128 | 0 | 1 +172 | 255 | 2 | 1 +192 | 168 | 1 | 1 +" + +while read n1 n2 n3 expect; do + subnet_in_use $n1 $n2 $n3 + actual=$? + check_result "$((1 - $actual))" "$expect" "subnet_in_use $n1.$n2.$n3" +done < <(parse_table "$table") + +unset -f ip + +# END subnet_in_use +############################################################################### # BEGIN check_assert # # This is way, way more complicated than it should be. The purpose is