在两个IPv4地址之间以CIDR表示法查找IPv4网络的算法

时间:2014-09-19 13:52:41

标签: bash networking subnet

我想找出这两个网络之间CIDR表示法中的所有IPv4网络:

10.11.3.64-10.11.3.127
10.11.52.0-10.11.52.255

IPv4网络应具有尽可能短的子网掩码。

10.11.3.127转换为二进制,添加1并转换回十进制以获取网络的第一个地址相当容易。然后将10.11.52.0转换为二进制,减去1并转换回十进制,以获取网络的最后一个地址。但是,为了找出10.11.3.128-10.11.51.255范围内的CIDR块,任何建议使用哪种算法都很聪明?只是建议我认为哪个方向足够了:))

2 个答案:

答案 0 :(得分:3)

我真的很喜欢这个问题,我昨晚看了一眼并决定试一试。在这一点上,我有一个概念证明shell脚本工作。

<强>声明:

  1. 这只是一个概念验证
  2. 由于我没有使用任何TCP / IP库,我在这里重新发明了轮子
  3. 我没有实现输入验证
  4. 如果用编程语言而不是bash编写,这段代码可能会快得多,尽管对于这种特定的网络范围并不是那么慢
  5. 值得一提的另一件事是我的理解:

    IPv4 networks should have as short subnet-mask as possible.

    我们应该尝试从保留的 8 位网络到提供的最大cidr,在这种情况下 25

    好的,让我们看一下正在运行的脚本:

    [root@TIAGO-TEST2 tmp]# time bash  ip.sh   10.11.3.64/25 10.11.52.0/24 
    10.11.3.128/25
    10.11.4.0/22
    10.11.8.0/21
    10.11.16.0/20
    10.11.32.0/20
    10.11.48.0/22
    
    real    0m48.376s
    user    0m6.174s
    sys     0m34.644s
    

    代码下方:

    #! /bin/bash
    
    function split_octet {
        sed -re "s/\./ /g" <<< "$1"
    }
    
    function dec2bin {
        perl -e 'printf "%0'"$1"'b\n",'"$2"';'
    }
    
    function bin2dec {
        perl -le 'print 0b'"$1"';'
    }
    
    function ip2bin {
        str=""
        for octet in $(split_octet $1); do
            str="${str}$(dec2bin 8 $octet)"
        done
        echo "$str"
    }
    
    function bin2ip {
        str=""
        for octet in $(grep -Eo '.{8}' <<< $1); do
            dec=$(bin2dec $octet)
            str="${str}.${dec}"
        done
        echo "$str" | sed -re 's/^\.|\.$//g'
    }
    
    function ip2dec {
        ip=$1
        bin2dec $(ip2bin $ip )
    }
    
    function dec2ip  {
        dec=$1
        bin2ip $(dec2bin 32 $dec )
    }
    
    function AND {
        perl -e '   $a=0b'"$1"' & 0b'"$2"';
                            printf "%032b\n",$a
                        '
    }
    
    function OR {
        perl -e '   $a=0b'"$1"' | 0b'"$2"';
                            printf "%032b\n",$a
                        '
    }
    
    function NOT {
        perl -le '  $a= (~ 0b'"$1"') & 0xFFFFFFFF; 
                                printf "%032b\n",$a
                         '
    }
    
    function get_network {
        ip=$1; mask=$2;
    
        if [ -n "$ip" -a -n "$mask" ];then
        echo $(bin2ip $(AND $(ip2bin $ip) $(ip2bin $mask)))
            return
        fi
    
        grep -qP "\d+\.\d+\.\d+.\d+/\d+" <<< "$ip"
        if [ "$?" == 0 ];then
            ip=$(get_ip_from_cidr $1 )
            mask=$(get_mask_from_cidr $1)
            echo $( bin2ip $(AND $(ip2bin $ip) $(ip2bin $mask)))
        fi
    }
    
    function get_broadcast {
        ip=$1; mask=$2;
    
        if [ -n "$ip" -a -n "$mask" ];then
            echo $( bin2ip $(OR $(ip2bin $ip) $(NOT $(ip2bin $mask) ) ))
            return
        fi
    
        grep -qP "\d+\.\d+\.\d+.\d+/\d+" <<< "$ip"
        if [ "$?" == 0 ];then
            ip=$(get_ip_from_cidr $1 )
            mask=$(get_mask_from_cidr $1)
            echo $( bin2ip $(OR $(ip2bin $ip) $(NOT $(ip2bin $mask) ) ))
        fi
    
    }
    
    function get_ip_from_cidr {
        awk -F/ '{print $1}' <<< "$1"
    }
    
    function get_mask_from_cidr {
        mask=$(awk -F/ '{print $2}' <<< "$1")
        mask=$(cidr $mask)
        mask=$(bin2ip $mask)
        echo $mask
    }
    
    function cidr {
        perl -e '
                            $n='"$1"';
                            $diff=32-$n;
                            print "1"x$n . "0"x$diff;
                        '
    }
    
    
    snet_cidr=$1
    enet_cidr=$2
    
    largest_cidr=$(echo -e "$snet_cidr\n$enet_cidr"| awk -F/ '{print $2}' | sort -rn | head -1 )
    
    snet_dec=$( ip2dec $(get_ip_from_cidr $snet_cidr))
    enet_dec=$( ip2dec $(get_ip_from_cidr $enet_cidr))
    
    sbc_ip=$(get_broadcast $snet_cidr)
    ebc_ip=$(get_broadcast $enet_cidr)
    
    sbc_dec=$(ip2dec $sbc_ip)
    ebc_dec=$(ip2dec $ebc_ip)
    
    counter=$sbc_dec
    
    while [ $counter -lt $enet_dec ];do
        tip=$(dec2ip $counter)
        for cidr in $(seq 8 $largest_cidr) ; do 
            tnet_ip=$(get_network $tip/$cidr)
            tnet_cidr=$tnet_ip/$cidr
            tbc_ip=$(get_broadcast $tnet_cidr)
            tnet_dec=$( ip2dec $(get_ip_from_cidr $tnet_cidr))
            tbc_dec=$(ip2dec $tbc_ip)
            if [ $sbc_dec -lt $tnet_dec -a $enet_dec -gt $tbc_dec ];then
                echo $tnet_cidr 
                counter=$tbc_dec
                break
            fi  
        done
        let counter++
    done
    

    编辑解释这些变量的含义可能是个好主意:

    1. snet_cidr:以cidr表示法启动网络
    2. enet_cidr:cidr中的结束网
    3. snet_dec:以十进制开始网
    4. enet_dec:以十进制结束的网络
    5. sbc_ip:start broadcast ip
    6. ebc_ip:end broadcast ip
    7. sbc_dec:start broadcast ip
    8. ebc_dec:end broadcast ip
    9. 无论你在哪里看到tnet或tbc都是temp net,temp broadcast,temp,因为它在循环中。

答案 1 :(得分:2)

如果你想要最短的面具(最大的网络),可以从最低的地址(10.11.3.128)开始,然后戴上最小的面具,从下一个地址开始,然后戴上最小的面具,等等。 #39; t超过范围的最大地址:

  1. 10.11.3.128/25(10.11.3.128至10.11.3.255)任何较小的内容无效
  2. 10.11.4.0/22(10.11.4.0至10.11.7.255)任何较小的内容无效
  3. 10.11.8.0/21(10.11.8.0至10.11.15.255)任何较小的内容无效
  4. 10.11.16.0/20(10.11.16.0至10.11.31.255)任何较小的内容无效
  5. 10.11.32.0/20(10.11.32.0至10.11.47.255)/ 19有效,但会走得太远
  6. 10.11.48.0/22(10.11.48.0至10.11.51.255)/ 20和/ 21有效,但会走得太远
  7. 用二进制看这个,很明显。掩码与子网进行AND运算(子网或掩码中零的任何位置都变为零;子网和掩码中的位置必须具有一个位置才能有一个)。如果你和一个子网和一个掩码,并且它不等于子网,它就是无效的。

    所有IP地址计算都需要以二进制方式完成。点分十进制表示法适用于人类可读性,但不应用于尝试进行IP地址计算。