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

Bash 在两个IPv4地址之间以CIDR表示法查找IPv4网络的算法,bash,networking,subnet,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块?我只想提出一个建议,

我想找出这两个网络之间所有采用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块?我只想提出一个建议,我希望哪一个方向就足够了:)

如果你想要最短的掩码(最大的网络),从最低的地址(10.11.3.128)开始,然后放上可能最小的掩码,从下一个地址开始,然后放上可能最小的掩码,等等。只是不要超过范围内的最大地址:

  • 10.11.3.128/25(10.11.3.128至10.11.3.255)任何较小的值均无效
  • 10.11.4.0/22(10.11.4.0至10.11.7.255)任何较小值均无效
  • 10.11.8.0/21(10.11.8.0至10.11.15.255)任何较小值均无效
  • 10.11.16.0/20(10.11.16.0至10.11.31.255)任何较小值均无效
  • 10.11.32.0/20(10.11.32.0至10.11.47.255)/19是有效的,但会走得太远
  • 10.11.48.0/22(10.11.48.0至10.11.51.255)/20和/21是有效的,但会走得太远
  • 从二进制来看,这是显而易见的。掩码与子网进行and运算(子网或掩码中的任何位置都将变为零;一个位置必须在子网和掩码中都有一个才能有一个)。如果您和一个子网和一个掩码不相等,则该子网无效


    所有IP地址的计算都需要以二进制进行。点十进制表示法对于人类可读性来说很好,但不应该用于尝试IP地址计算。

    我非常喜欢这个问题,昨晚我看了一下,决定试一试。在这一点上,我有一个概念验证shell脚本正在工作

    免责声明:

  • 这只是一个概念证明
  • 我在这里重新发明了轮子,因为我没有使用任何TCP/IP库
  • 我没有实现输入验证
  • 如果用编程语言而不是bash编写此代码,则速度可能会快得多,尽管对于此特定对象,网络范围并不是很慢
  • 另外值得一提的是,我对以下方面的理解:

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

    我们应该尝试从为网络保留的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
    
    #/bin/bash
    函数分裂八位组{
    
    sed-re“s/\.//g”解释
    IPv4网络应具有尽可能短的子网掩码。
    @hek2mgl这意味着IPv4前缀应尽可能大。例如,网络范围
    10.11.3.128-10.11.51.255
    可以用CIDR表示法表示为
    10.11.3.128/25
    ,然后从
    10.11.4.0/24
    10.11.51.0/24
    使用/24前缀,而实际上那些
    /24
    可以聚合。或者从
    10.11.3.128/0
    10.11.51.255/0
    使用32位前缀,这实际上是IPV4最大的前缀。这就是为什么我在问。@hek2mgl在我看来
    IPV4网络应该有尽可能短的子网掩码。
    句子是单音的。这意味着网络掩码部分应该尽可能短,即包含尽可能小的部分。好的,明白了。对不起,我的错。现在这是一个有趣的问题。。。