#!/bin/bash # CIDR membership test: is $ip inside $cidr? # ipInSubnet 10.123.154.5 10.123.154.0/24 -> 0 (in subnet) # ipInSubnet 10.100.0.5 10.123.154.0/24 -> 1 (out of subnet) # # Pure-bash 32-bit integer masking that HONOURS the real prefix length, so a /16 # genuinely contains its /24s. That matters here: the network is created with a # /24 ip-range but its declared subnet can be /16 (configs/network/network_docker # default), and a healthy "/16 subnet + /24 ip-range" install must NOT read as # drift. Compare stored IPs against docker's actual subnet CIDR, not a forced /24. # Returns 0 (in-subnet) or 1 (out-of-subnet / malformed input). ipInSubnet() { local ip="$1" cidr="$2" [[ "$cidr" == */* ]] || return 1 local base="${cidr%/*}" prefix="${cidr#*/}" [[ "$prefix" =~ ^[0-9]+$ ]] && (( prefix >= 0 && prefix <= 32 )) || return 1 local re='^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$' [[ "$ip" =~ $re ]] || return 1 local i1=$((10#${BASH_REMATCH[1]})) i2=$((10#${BASH_REMATCH[2]})) i3=$((10#${BASH_REMATCH[3]})) i4=$((10#${BASH_REMATCH[4]})) [[ "$base" =~ $re ]] || return 1 local b1=$((10#${BASH_REMATCH[1]})) b2=$((10#${BASH_REMATCH[2]})) b3=$((10#${BASH_REMATCH[3]})) b4=$((10#${BASH_REMATCH[4]})) local ip_int=$(( (i1<<24) | (i2<<16) | (i3<<8) | i4 )) local base_int=$(( (b1<<24) | (b2<<16) | (b3<<8) | b4 )) local mask=0 (( prefix > 0 )) && mask=$(( (0xFFFFFFFF << (32 - prefix)) & 0xFFFFFFFF )) (( (ip_int & mask) == (base_int & mask) )) }