#!/bin/sh # # Copyright (C) 2026 Nikos Mavrogiannopoulos # # This file is part of ocserv. # # ocserv is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation; either version 2 of the License, or (at # your option) any later version. # # ocserv is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # Unit test for the mask_to_prefix / normalize_route shell functions in # ocserv-fw-nftables. The functions are extracted directly from the script # so this test always exercises the real implementation. srcdir=${srcdir:-.} FW_SCRIPT="${srcdir}/../src/ocserv-fw-nftables" if ! test -f "${FW_SCRIPT}"; then echo "cannot find ${FW_SCRIPT}" exit 1 fi echo "Testing mask_to_prefix and normalize_route from ${FW_SCRIPT}..." # Load the two functions directly from the firewall script. awk extracts # each function body (from "funcname() {" to the closing "}" at column 0). eval "$(awk '/^mask_to_prefix\(\)/,/^\}/' "${FW_SCRIPT}")" eval "$(awk '/^normalize_route\(\)/,/^\}/' "${FW_SCRIPT}")" if ! type mask_to_prefix > /dev/null 2>&1 || \ ! type normalize_route > /dev/null 2>&1; then echo "failed to load functions from ${FW_SCRIPT}" exit 1 fi # ---- test harness ----------------------------------------------------------- FAIL=0 check() { input="$1" expected="$2" got=$(normalize_route "$input") if test "$got" != "$expected"; then echo "FAIL: normalize_route '$input' -> '$got', expected '$expected'" FAIL=1 fi } check_mask() { mask="$1" expected="$2" got=$(mask_to_prefix "$mask") if test "$got" != "$expected"; then echo "FAIL: mask_to_prefix '$mask' -> '$got', expected '$expected'" FAIL=1 fi } # Expect mask_to_prefix to fail (return non-zero, produce no output) check_mask_invalid() { mask="$1" got=$(mask_to_prefix "$mask" 2>/dev/null) if test $? = 0 || test -n "$got"; then echo "FAIL: mask_to_prefix '$mask' should have failed but returned '$got'" FAIL=1 fi } # Expect normalize_route to fail (return non-zero, produce no output) check_route_invalid() { route="$1" got=$(normalize_route "$route" 2>/dev/null) if test $? = 0 || test -n "$got"; then echo "FAIL: normalize_route '$route' should have failed but returned '$got'" FAIL=1 fi } # ---- mask_to_prefix: all 33 valid prefix lengths ---------------------------- check_mask "0.0.0.0" 0 check_mask "128.0.0.0" 1 check_mask "192.0.0.0" 2 check_mask "224.0.0.0" 3 check_mask "240.0.0.0" 4 check_mask "248.0.0.0" 5 check_mask "252.0.0.0" 6 check_mask "254.0.0.0" 7 check_mask "255.0.0.0" 8 check_mask "255.128.0.0" 9 check_mask "255.192.0.0" 10 check_mask "255.224.0.0" 11 check_mask "255.240.0.0" 12 check_mask "255.248.0.0" 13 check_mask "255.252.0.0" 14 check_mask "255.254.0.0" 15 check_mask "255.255.0.0" 16 check_mask "255.255.128.0" 17 check_mask "255.255.192.0" 18 check_mask "255.255.224.0" 19 check_mask "255.255.240.0" 20 check_mask "255.255.248.0" 21 check_mask "255.255.252.0" 22 check_mask "255.255.254.0" 23 check_mask "255.255.255.0" 24 check_mask "255.255.255.128" 25 check_mask "255.255.255.192" 26 check_mask "255.255.255.224" 27 check_mask "255.255.255.240" 28 check_mask "255.255.255.248" 29 check_mask "255.255.255.252" 30 check_mask "255.255.255.254" 31 check_mask "255.255.255.255" 32 # ---- normalize_route: dotted-decimal mask conversion ------------------------ check "10.0.0.0/255.0.0.0" "10.0.0.0/8" check "172.16.0.0/255.240.0.0" "172.16.0.0/12" check "192.168.0.0/255.255.0.0" "192.168.0.0/16" check "192.168.1.0/255.255.255.0" "192.168.1.0/24" check "192.168.1.128/255.255.255.128" "192.168.1.128/25" check "192.168.1.192/255.255.255.192" "192.168.1.192/26" check "0.0.0.0/0.0.0.0" "0.0.0.0/0" check "192.168.1.1/255.255.255.255" "192.168.1.1/32" # ---- normalize_route: already-CIDR IPv4 passed through --------------------- check "10.0.0.0/8" "10.0.0.0/8" check "192.168.1.0/24" "192.168.1.0/24" check "0.0.0.0/0" "0.0.0.0/0" check "192.168.1.1/32" "192.168.1.1/32" # ---- normalize_route: IPv6 passed through unchanged ------------------------ check "fd00::/48" "fd00::/48" check "2001:db8::/32" "2001:db8::/32" check "fd91:6d14:7241:dc6a::/112" "fd91:6d14:7241:dc6a::/112" check "fc7d:b139:f7ba:5f53:c634::/80" "fc7d:b139:f7ba:5f53:c634::/80" check "::/0" "::/0" check "::1/128" "::1/128" # ---- mask_to_prefix: invalid octet values ----------------------------------- check_mask_invalid "255.300.0.0" check_mask_invalid "255.255.127.0" check_mask_invalid "255.1.0.0" # ---- mask_to_prefix: non-contiguous masks ----------------------------------- check_mask_invalid "254.192.128.0" check_mask_invalid "255.0.255.0" check_mask_invalid "0.255.0.0" check_mask_invalid "255.255.0.255" # ---- mask_to_prefix: wrong number of octets --------------------------------- check_mask_invalid "255.255.0" check_mask_invalid "255.255.255.0.0" # ---- normalize_route: propagates mask_to_prefix failure --------------------- check_route_invalid "10.0.0.0/255.0.255.0" check_route_invalid "10.0.0.0/255.255.255.0.0" # ---- result ----------------------------------------------------------------- if test "$FAIL" = "0"; then echo "All tests passed." exit 0 else exit 1 fi