mirror of https://github.com/containers/podman.git
test/system: Use procfs to find bound ports, with optional address and protocol
Using bash /dev/tcp/ pseudo-device files to probe for bound ports has indeed the advantage of simplicity, but comes with a few drawbacks: - it will actually send data to unsuspecting services that might be running in the same network namespace as the tests, possibly causing unwanted interactions - it doesn't allow for UDP probing - it makes it impossible to clearly distinguish between different address bindings Replace that approach with a new helper, port_is_bound(), that uses procfs entries at /proc/net to detect bound ports, without the need for active probing. We can now implement optional parameters in callers, to check if a port if free for binding to a given address, including any IPv4 (0.0.0.0) or any IPv6 (::0) address, and for a given protocol, TCP or UDP. Extend random_free_port() and random_free_port_range() to support that. The implementation of one function in the file test/system/helpers.bash, namely ipv6_to_procfs(), and the implementation of the corresponding own test, delimited by the markers "# BEGIN ipv6_to_procfs" and "# END ipv6_to_procfs" in the file test/system/helpers.c was provided, on the public forum at: https://github.com/containers/podman/pull/16141 by Ed Santiago <santiago@redhat.com>, who expressly invited me to include them in this code submission. Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
This commit is contained in:
parent
7e3d04fbc6
commit
ea4f168b3a
|
|
@ -272,15 +272,18 @@ function wait_for_ready {
|
|||
wait_for_output 'READY' "$@"
|
||||
}
|
||||
|
||||
######################
|
||||
# random_free_port # Pick an available port within a specified range
|
||||
######################
|
||||
# random_free_port() - Get unbound port with pseudorandom number
|
||||
# $1: Optional, dash-separated interval, [5000, 5999] by default
|
||||
# $2: Optional binding address, any IPv4 address by default
|
||||
# $3: Optional protocol, tcp or udp
|
||||
function random_free_port() {
|
||||
local range=${1:-5000-5999}
|
||||
local address=${2:-0.0.0.0}
|
||||
local protocol=${3:-tcp}
|
||||
|
||||
local port
|
||||
for port in $(shuf -i ${range}); do
|
||||
if port_is_free $port; then
|
||||
if port_is_free $port $address $protocol; then
|
||||
echo $port
|
||||
return
|
||||
fi
|
||||
|
|
@ -289,8 +292,14 @@ function random_free_port() {
|
|||
die "Could not find open port in range $range"
|
||||
}
|
||||
|
||||
# random_free_port_range() - Get range of unbound ports with pseudorandom start
|
||||
# $1: Size of range (i.e. number of ports)
|
||||
# $2: Optional binding address, any IPv4 address by default
|
||||
# $3: Optional protocol, tcp or udp
|
||||
function random_free_port_range() {
|
||||
local size=${1?Usage: random_free_port_range SIZE (as in, number of ports)}
|
||||
local size=${1?Usage: random_free_port_range SIZE [ADDRESS [tcp|udp]]}
|
||||
local address=${2:-0.0.0.0}
|
||||
local protocol=${3:-tcp}
|
||||
|
||||
local maxtries=10
|
||||
while [[ $maxtries -gt 0 ]]; do
|
||||
|
|
@ -298,7 +307,7 @@ function random_free_port_range() {
|
|||
local lastport=
|
||||
for i in $(seq 1 $((size - 1))); do
|
||||
lastport=$((firstport + i))
|
||||
if ! port_is_free $lastport; then
|
||||
if ! port_is_free $lastport $address $protocol; then
|
||||
echo "# port $lastport is in use; trying another." >&3
|
||||
lastport=
|
||||
break
|
||||
|
|
@ -315,11 +324,98 @@ function random_free_port_range() {
|
|||
die "Could not find free port range with size $size"
|
||||
}
|
||||
|
||||
function port_is_free() {
|
||||
local port=${1?Usage: port_is_free PORT [ADDRESS]}
|
||||
local address="${2:-127.0.0.1}"
|
||||
# ipv6_to_procfs() - RFC 5952 IPv6 address text representation to procfs format
|
||||
# $1: Address in any notation described by RFC 5952
|
||||
function ipv6_to_procfs() {
|
||||
local addr="${1}"
|
||||
|
||||
! { exec {unused_fd}<> /dev/tcp/"${address}"/$port; } &>/dev/null
|
||||
# Add leading zero if missing
|
||||
case ${addr} in
|
||||
"::"*) addr=0"${addr}" ;;
|
||||
esac
|
||||
|
||||
# Double colon can mean any number of all-zero fields. Expand to fill
|
||||
# as many colons as are missing. (This will not be a valid IPv6 form,
|
||||
# but we don't need it for long). E.g., 0::1 -> 0:::::::1
|
||||
case ${addr} in
|
||||
*"::"*)
|
||||
# All the colons in the address
|
||||
local colons
|
||||
colons=$(tr -dc : <<<$addr)
|
||||
# subtract those from a string of eight colons; this gives us
|
||||
# a string of two to six colons...
|
||||
local pad
|
||||
pad=$(sed -e "s/$colons//" <<<":::::::")
|
||||
# ...which we then inject in place of the double colon.
|
||||
addr=$(sed -e "s/::/::$pad/" <<<$addr)
|
||||
;;
|
||||
esac
|
||||
|
||||
# Print as a contiguous string of zero-filled 16-bit words
|
||||
# (The additional ":" below is needed because 'read -d x' actually
|
||||
# means "x is a TERMINATOR, not a delimiter")
|
||||
local group
|
||||
while read -d : group; do
|
||||
printf "%04X" "0x${group:-0}"
|
||||
done <<<"${addr}:"
|
||||
}
|
||||
|
||||
# __ipv4_to_procfs() - Print bytes in hexadecimal notation reversing arguments
|
||||
# $@: IPv4 address as separate bytes
|
||||
function __ipv4_to_procfs() {
|
||||
printf "%02X%02X%02X%02X" ${4} ${3} ${2} ${1}
|
||||
}
|
||||
|
||||
# ipv4_to_procfs() - IPv4 address representation to big-endian procfs format
|
||||
# $1: Text representation of IPv4 address
|
||||
function ipv4_to_procfs() {
|
||||
IFS='.' __ipv4_to_procfs ${1}
|
||||
}
|
||||
|
||||
# port_is_bound() - Check if TCP or UDP port is bound for a given address
|
||||
# $1: Port number
|
||||
# $2: Optional protocol, or optional IPv4 or IPv6 address, default: tcp
|
||||
# $3: Optional IPv4 or IPv6 address, or optional protocol, default: any
|
||||
function port_is_bound() {
|
||||
local port=${1?Usage: port_is_bound PORT [tcp|udp] [ADDRESS]}
|
||||
|
||||
if [ "${2}" = "tcp" ] || [ "${2}" = "udp" ]; then
|
||||
local address="${3}"
|
||||
local proto="${2}"
|
||||
elif [ "${3}" = "tcp" ] || [ "${3}" = "udp" ]; then
|
||||
local address="${2}"
|
||||
local proto="${3}"
|
||||
else
|
||||
local address="${2}" # Might be empty
|
||||
local proto="tcp"
|
||||
fi
|
||||
|
||||
port=$(printf %04X ${port})
|
||||
case "${address}" in
|
||||
*":"*)
|
||||
grep -e "^[^:]*: $(ipv6_to_procfs "${address}"):${port} .*" \
|
||||
-e "^[^:]*: $(ipv6_to_procfs "::0"):${port} .*" \
|
||||
-q "/proc/net/${proto}6"
|
||||
;;
|
||||
*"."*)
|
||||
grep -e "^[^:]*: $(ipv4_to_procfs "${address}"):${port}" \
|
||||
-e "^[^:]*: $(ipv4_to_procfs "0.0.0.0"):${port}" \
|
||||
-q "/proc/net/${proto}"
|
||||
;;
|
||||
*)
|
||||
# No address: check both IPv4 and IPv6, for any bound address
|
||||
grep "^[^:]*: [^:]*:${port} .*" -q "/proc/net/${proto}6" || \
|
||||
grep "^[^:]*: [^:]*:${port} .*" -q "/proc/net/${proto}"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# port_is_free() - Check if TCP or UDP port is free to bind for a given address
|
||||
# $1: Port number
|
||||
# $2: Optional protocol, or optional IPv4 or IPv6 address, default: tcp
|
||||
# $3: Optional IPv4 or IPv6 address, or optional protocol, default: any
|
||||
function port_is_free() {
|
||||
! port_is_bound ${@}
|
||||
}
|
||||
|
||||
###################
|
||||
|
|
|
|||
|
|
@ -224,5 +224,22 @@ check_result "$found" "16700" "random_free_port"
|
|||
|
||||
# END random_free_port
|
||||
###############################################################################
|
||||
# BEGIN ipv6_to_procfs
|
||||
|
||||
# Table of IPv6 short forms and their procfs equivalents. For readability,
|
||||
# spaces separate each 16-bit word. Spaces are removed when testing.
|
||||
table="
|
||||
2b06::1 | 2B06 0000 0000 0000 0000 0000 0000 0001
|
||||
::1 | 0000 0000 0000 0000 0000 0000 0000 0001
|
||||
0::1 | 0000 0000 0000 0000 0000 0000 0000 0001
|
||||
"
|
||||
|
||||
while read shortform expect; do
|
||||
actual=$(ipv6_to_procfs $shortform)
|
||||
check_result "$actual" "${expect// }" "ipv6_to_procfs $shortform"
|
||||
done < <(parse_table "$table")
|
||||
|
||||
# END ipv6_to_procfs
|
||||
###############################################################################
|
||||
|
||||
exit $rc
|
||||
|
|
|
|||
Loading…
Reference in New Issue