I've got a bash script that builds a whitelist based on the IP addresses in known senders' SPF records (https://github.com/stevejenkins/postwhite). In that script, I've got a series of functions that "normalize" the retrieved IPv4 CIDRs:
# Create IPv4 normalize function
ip2int() {
local a b c d
{ IFS=. read a b c d; } <<< $1
printf $(((((((a << 8) | b) << 8) | c) << 8) | d))
}
int2ip() {
local ui32=$1; shift
local ip n
for n in 1 2 3 4; do
ip=$((ui32 & 0xff))${ip:+.}$ip
ui32=$((ui32 >> 8))
done
printf $ip
}
network() {
local ip netmask;
{ IFS="/" read ip netmask; } <<< $1
local addr=$(ip2int $ip);
local mask=$((0xffffffff << (32 -$netmask)));
printf $(int2ip $((addr & mask)))/$netmask
}
function normalize_ipv4() {
# split by ":"
local array=(${ip/:/ });
if [ "x${array[0]}" = "xip4" ] ; then
# check if is a CIDR
if [[ ${array[1]} == *"/"32 ]] ; then
IP=${array[1]}
elif [[ ${array[1]} == *"/"* ]] ; then
IP=$(network ${array[1]});
else
IP=${array[1]}
fi
else
IP=${array[1]}
fi
printf "$IP"
}
I can determine if the normalized IPv4 CIDR matches the IPv4 CIDR from the query, then I can "fix," "remove," or "keep" invalid IPv4 CIDRs in the whitelist:
# If enabled, fix invalid CIDRs
if [ "$invalidip4" == "fix" ] ; then
printf "\nFixing invalid IPv4 CIDRs..."
for ip in $(cat "${tmp2}") ; do
ip=$(normalize_ipv4 "$ip");
if [ -n "$ip" ] ; then
printf "$ip\tpermit\n"
fi
done >> "${tmp3}" &
show_dots $!
# If enabled, remove invalid CIDRs
elif [ "$invalidip4" == "remove" ] ; then
printf "\nRemoving invalid IPv4 CIDRs..."
for ip in $(cat "${tmp2}") ; do
iptype=$( printf "$ip" | cut -d\: -f1 )
origip=$( printf "$ip" | cut -d\: -f2 )
ip=$(normalize_ipv4 "$ip");
if [ "$origip" == "$ip" ] ; then
printf "$ip\tpermit\n"
elif [ "$iptype" == "ip6" ] ; then
printf "$ip\tpermit\n"
fi
done >> "${tmp3}" &
show_dots $!
# If enabled, keep invalid CIDRs
elif [ "$invalidip4" == "keep" ] ; then
printf "\nKeeping invalid IPv4 CIDRs...\n"
printf "%s\n" | grep "^ip" "${tmp2}" | cut -c5- | sed s/$/' permit'/ > "${tmp3}"
fi
I'm now looking to create a normalize function for IPv6 addresses and CIDRs, so I can do a similar comparison and allow the user to choose how to treat them. I have no idea where to start, and my regex skillz are non-existent.
Here are some examples of how the IPv6 query results are formatted prior to normalization:
ip6:2620:109:c003:104::/64
ip6:2620:109:c006:104::/64
ip6:2620:109:c00d:104::/64
ip6:2001:4860:4000::/36
ip6:2404:6800:4000::/36
ip6:2607:f8b0:4000::/36
ip6:2800:3f0:4000::/36
ip6:2a00:1450:4000::/36
ip6:2a04:35c0::/29
ip6:2607:f8b0:4001:c13::1b
ip6:2c0f:fb50:4000::/36
The script needs to run on Linux, BSD, OSX, and as many systems as possible, so I'm trying keep the script as generic as possible.
Thanks in advance!