【问题标题】:algorithm to find IPv4 networks in CIDR notation between two IPv4 addresses在两个 IPv4 地址之间以 CIDR 表示法查找 IPv4 网络的算法
【发布时间】:2014-11-14 03:32:58
【问题描述】:

我想找出这两个网络之间以 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 块?只是我认为应该朝哪个方向提出建议就足够了:)

【问题讨论】:

  • 解释IPv4 networks should have as short subnet-mask as possible.
  • @hek2mgl 这意味着 IPv4 前缀应该尽可能大。例如,网络范围10.11.3.128-10.11.51.255 可以用10.11.3.128/25 以CIDR 表示法表示,然后使用/24 前缀从10.11.4.0/2410.11.51.0/24,而实际上这些/24 可以聚合。
  • 或从 10.11.3.128/010.11.51.255/0 使用 32 位前缀,这实际上是 IPV4 的最大可能前缀..这就是我问的原因..
  • @hek2mgl 在我看来,IPv4 networks should have as short subnet-mask as possible 句子是单义的。这意味着网络掩码部分应尽可能短,即包含尽可能少的部分。
  • 好的,知道了。对不起,我的错。现在这是一个有趣的问题...

标签: bash networking subnet


【解决方案1】:

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

免责声明:

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

还有一点值得一提的是我的理解:

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: 开始广播ip
  6. ebc_ip: 结束广播ip
  7. sbc_dec: 开始广播ip
  8. ebc_dec: 结束广播 ip

无论你在哪里看到 tnet 或 tbc 都是 temp net、temp broadcast、temp,因为它在循环内。

【讨论】:

    【解决方案2】:

    如果您想要可用的最短掩码(最大的网络),请从最低地址 (10.11.3.128) 开始并尽可能使用最小的掩码,从下一个地址开始并尽可能使用最小的掩码,等等。只要不要'不超过范围的最大地址:

    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 有效,但太过分了

    看这个二进制,就很明显了。掩码与子网进行“与”运算(子网或掩码中具有零的任何位置都变为零;一个位置必须在子网和掩码中都具有 1 才能具有 1)。如果你 AND 一个子网和一个掩码,它不等于子网,它是无效的。

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

    【讨论】:

      猜你喜欢
      • 2013-07-15
      • 1970-01-01
      • 2012-12-01
      • 2013-05-13
      • 2015-04-05
      • 2017-06-13
      • 2014-12-16
      • 2019-07-13
      • 2015-02-19
      相关资源
      最近更新 更多