0

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!

SteveJ
  • 290
  • 2
  • 10
  • did you see http://stackoverflow.com/questions/15429420/given-the-ip-and-netmask-how-can-i-calculate-the-network-address-using-bash/32690695#32690695 ? I think it is ipv4, but you might find something that will help you. Good luck. – shellter Dec 03 '15 at 00:35
  • Thx, @shellter. The method in the question you referenced is almost identical to the method I use for IPv4 (see my included example). My problem is that IPv6 is far more complex, and I'm not sure where to start with the calculations for validity. :( – SteveJ Dec 03 '15 at 19:57

0 Answers0