From ab2e6d14ea141080cbb5c3d4cace12f97511d714 Mon Sep 17 00:00:00 2001 From: Arseny Maslennikov Date: Fri, 22 Aug 2025 17:00:00 +0300 Subject: [PATCH] ip-address: Add tests for ipv4_prefix2mask Add some tests that cover all the possible octet values in the mask, as well as invalid prefix lengths. While running the tests, it was discovered the function did not work on mksh at all: - that shell stores integers as int32_t in arith context, so numbers like 0x80000000 are < 0, and bit shift to the right has been observed to sign-extend; - that shell cannot left-shift an integer by 32 bits (and evaluate to a multiple of 4294967296), since the shift amount is considered modulo 32. We have to replace the algorithm to not rely on left shift by 32, or any right shift. We move it to a separate function to be used from more places in subsequent changes. Signed-off-by: Arseny Maslennikov --- shell-ip-address | 29 ++++++- tests/ip_address_mask | 189 ++++++++++++++++++++++++++++++++++++++++++ tests/runtests | 2 +- 3 files changed, 215 insertions(+), 5 deletions(-) create mode 100644 tests/ip_address_mask diff --git a/shell-ip-address b/shell-ip-address index 46d5c27..7a6a1e7 100644 --- a/shell-ip-address +++ b/shell-ip-address @@ -88,6 +88,26 @@ __ipv4_hex() printf '%02x' "$@" } +__len2mask_32() +{ + local len + len="${1-}" + + [ -n "$len" ] || + return 2 + + local mask + local pos=0 n nbits + while [ "$len" -lt 32 ]; do + n=$(( ($len | 7) + 1 - $len )) + nbits=$(((1 << $n) - 1)) + len=$(($len + $n)) + pos=$(( ($pos << $n) | $nbits )) + done + mask=$((0xFFFFFFFF - $pos)) + printf '%s' "$mask" +} + ### Checks that IP address is in subnet ### Usage example: ### ipv4_ip_subnet 172.16.1.2 172.16.1.0/24; echo res=$? @@ -154,14 +174,15 @@ ipv4_mask2prefix() ### 255.255.255.0 ipv4_prefix2mask() { - local len + local len mask len="${1-}" - [ "$len" -ge 0 ] && [ "$len" -le 32 ] 2>/dev/null || + [ "$len" = 0 ] || shell_var_is_number "$len" || + return 1 + [ "$len" -ge 0 ] && [ "$len" -le 32 ] || return 1 - local position=$((0xFFFFFFFF)) - local mask=$(($position - ($position >> $len))) + mask="$(__len2mask_32 "$len")" printf '%s.%s.%s.%s\n' \ "$(($mask >> 24 & 0xFF))" \ diff --git a/tests/ip_address_mask b/tests/ip_address_mask new file mode 100644 index 0000000..88cac82 --- /dev/null +++ b/tests/ip_address_mask @@ -0,0 +1,189 @@ +#!/bin/ash -efu + +ip_address_mask_test400() { # UnitTest + . ../shell-ip-address + + local rc=0 out= + local plen=0 + ipv4_prefix2mask "$plen" || rc=1 + out="$(ipv4_prefix2mask "$plen")" + + assertTrue "/$plen" $rc + assertEquals "0.0.0.0" "$out" +} + +ip_address_mask_test401() { # UnitTest + . ../shell-ip-address + + local rc=0 out= + local plen=1 + ipv4_prefix2mask "$plen" || rc=1 + out="$(ipv4_prefix2mask "$plen")" + + assertTrue "/$plen" $rc + assertEquals "128.0.0.0" "$out" +} + +ip_address_mask_test402() { # UnitTest + . ../shell-ip-address + + local rc=0 out= + local plen=4 + ipv4_prefix2mask "$plen" || rc=1 + out="$(ipv4_prefix2mask "$plen")" + + assertTrue "/$plen" $rc + assertEquals "240.0.0.0" "$out" +} + +ip_address_mask_test403() { # UnitTest + . ../shell-ip-address + + local rc=0 out= + local plen=8 + ipv4_prefix2mask "$plen" || rc=1 + out="$(ipv4_prefix2mask "$plen")" + + assertTrue "/$plen" $rc + assertEquals "255.0.0.0" "$out" +} + +ip_address_mask_test404() { # UnitTest + . ../shell-ip-address + + local rc=0 out= + local plen=10 + ipv4_prefix2mask "$plen" || rc=1 + out="$(ipv4_prefix2mask "$plen")" + + assertTrue "/$plen" $rc + assertEquals "255.192.0.0" "$out" +} + +ip_address_mask_test405() { # UnitTest + . ../shell-ip-address + + local rc=0 out= + local plen=11 + ipv4_prefix2mask "$plen" || rc=1 + out="$(ipv4_prefix2mask "$plen")" + + assertTrue "/$plen" $rc + assertEquals "255.224.0.0" "$out" +} + +ip_address_mask_test406() { # UnitTest + . ../shell-ip-address + + local rc=0 out= + local plen=13 + ipv4_prefix2mask "$plen" || rc=1 + out="$(ipv4_prefix2mask "$plen")" + + assertTrue "/$plen" $rc + assertEquals "255.248.0.0" "$out" +} + +ip_address_mask_test407() { # UnitTest + . ../shell-ip-address + + local rc=0 out= + local plen=15 + ipv4_prefix2mask "$plen" || rc=1 + out="$(ipv4_prefix2mask "$plen")" + + assertTrue "/$plen" $rc + assertEquals "255.254.0.0" "$out" +} + +ip_address_mask_test408() { # UnitTest + . ../shell-ip-address + + local rc=0 out= + local plen=16 + ipv4_prefix2mask "$plen" || rc=1 + out="$(ipv4_prefix2mask "$plen")" + + assertTrue "/$plen" $rc + assertEquals "255.255.0.0" "$out" +} + +ip_address_mask_test409() { # UnitTest + . ../shell-ip-address + + local rc=0 out= + local plen=24 + ipv4_prefix2mask "$plen" || rc=1 + out="$(ipv4_prefix2mask "$plen")" + + assertTrue "/$plen" $rc + assertEquals "255.255.255.0" "$out" +} + +ip_address_mask_test410() { # UnitTest + . ../shell-ip-address + + local rc=0 out= + local plen=30 + ipv4_prefix2mask "$plen" || rc=1 + out="$(ipv4_prefix2mask "$plen")" + + assertTrue "/$plen" $rc + assertEquals "255.255.255.252" "$out" +} + +ip_address_mask_test411() { # UnitTest + . ../shell-ip-address + + local rc=0 out= + local plen=32 + ipv4_prefix2mask "$plen" || rc=1 + out="$(ipv4_prefix2mask "$plen")" + + assertTrue "/$plen" $rc + assertEquals "255.255.255.255" "$out" +} + +ip_address_mask_test412() { # UnitTest + . ../shell-ip-address + + local rc=0 + local plen=33 + ipv4_prefix2mask "$plen" || rc=1 + + assertFalse "prefix length too large: $plen" $rc + assertNull "$(ipv4_prefix2mask "$plen")" +} + +ip_address_mask_test413() { # UnitTest + . ../shell-ip-address + + local rc=0 + local plen='' + ipv4_prefix2mask "$plen" || rc=1 + + assertFalse "empty prefix length" $rc + assertNull "$(ipv4_prefix2mask "$plen")" +} + +ip_address_mask_test414() { # UnitTest + . ../shell-ip-address + + local rc=0 + local plen=xyz + ipv4_prefix2mask "$plen" || rc=1 + + assertFalse "garbage prefix length: $plen" $rc + assertNull "$(ipv4_prefix2mask "$plen")" +} + +ip_address_mask_test416() { # UnitTest + . ../shell-ip-address + + local rc=0 + local plen=255.255.224.0 + ipv4_prefix2mask "$plen" || rc=1 + + assertFalse "passed netmask: $plen" $rc + assertNull "$(ipv4_prefix2mask "$plen")" +} diff --git a/tests/runtests b/tests/runtests index 5ac4212..9b8fc52 100755 --- a/tests/runtests +++ b/tests/runtests @@ -36,7 +36,7 @@ for s in \ git_config_foreach git_config_get git_config_count git_config_list git_config_set git_config_unset git_config_append \ shell_var_unquote shell_var_trim \ fill_mask \ - ip_address \ + ip_address ip_address_mask \ cmdline_foreach \ cmdline_get \ run_scripts \