正在计算组成给定IP范围的子网列表

Calculating list of subnets that make a given IP range

本文关键字:范围 IP 子网 列表 计算      更新时间:2023-10-16

我正试图想出并理解一个函数IPv4Range (startIPAddr, endIPAddr),它将返回CIDR范围的列表。

例如:

  10.0.0.0 - 10.0.0.3  ->  10.0.0.0/30
  10.0.0.0 - 10.0.0.6  ->  10.0.0.0/30, 10.0.0.4/31, 10.0.0.6/32

以及更复杂的情况。

我之前在网上找到了很多这个代码的例子,但其中一些根本不起作用,其余的都返回了最小的公共子网(比如10.0.0.0/29包含10.0.0.0-10.0.0.4,但它们不相等,所以这不是我所期望的),而不是整个范围。

首先,这不是一个在不尝试自己编写代码的情况下获得工作代码的网站。然而,我将解释我将如何处理这个问题,并为您提供一个可以根据您的需求轻松采用的代码。

如果您有定义单个作用域(如10.0.0.0 - 10.0.0.255)的范围,那么这个问题就很容易了,这样我们就可以得到更复杂的内容,例如10.0.0.1 - 10.0.0.126。这个范围非常接近10.0.0.0/25,但两端都缺少/32,这保证了您需要12个作用域来填充这个范围。

这个问题可以这样表示:

               Start IP                         End IP
                  v                                v
-------|----------+----------------+---------------+-----------|------
       ^          .                                .           ^
  Subnet address  .                                .    Broadcast address
       .          .                                .           .
       .          ________________________________/           .
       .                     Provided Range                    .
       .                                                       .
       _______________________________________________________/
                              Scope Range

你可以用分而治之的方法最容易地解决这些问题。在这种情况下(考虑到子网掩码总是2的幂),我们可以将这个问题(范围10.0.0.1 - 10.0.0.126和/25掩码)分为两个较小的问题。

               Start IP                         End IP
                  v                                v
-------|----------+---------------++---------------+-----------|------
       ^                          ||                           ^
  Subnet address                  ||                    Broadcast address
       .                          ||                           .
       __________________________/___________________________/
       .          /X+1                        /X+1             .
       .                                                       .
       _______________________________________________________/
                       Scope prefix length: /X

当增加前缀长度时,基本上将作用域一分为二。因此,您现在有了10.0.0.0/2610.0.0.64/26,而不是10.0.0.0/25,并且您的两个新范围是10.0.0.1 - 10.0.0.6310.0.0.64 - 10.0.0.126。您将继续以这种方式划分您的范围,直到:

  1. 您的起始IP等于子网地址结束IP等于广播地址
  2. 你的射程太小了,不能再分开了(/32)

这是我为此编写的代码。所有计算都是在IP地址的十进制表示上完成的(不是点式十进制),因此前两个函数将带点式十进制表示的IP的string转换为long,反之亦然:

#include <sstream>
long str_to_long(string ip){
    stringstream s(ip);
    int o1, o2, o3, o4;
    char ch;
    s >> o1 >> ch >> o2 >> ch >> o3 >> ch >> o4;
    long ip_long = 0;
    ip_long = 0 | (o1 << 24) | (o2 << 16) | (o3 << 8) | o4;
    return ip_long;
}
string long_to_str(long ip){
    stringstream tmp;
    tmp << to_string((long long) ip >> 24 & 0xFF).c_str() << '.';
    tmp << to_string((long long) ip >> 16 & 0xFF).c_str() << '.';
    tmp << to_string((long long) ip >> 8 & 0xFF).c_str() << '.';
    tmp << to_string((long long) ip & 0xFF).c_str();
    return tmp.str();
}

主函数接受两个(long)参数——起始IP和结束IP——并打印所需的子网。

void subnets(long start_ip, long end_ip){
    int host_bits = 0, host_mask = 0;
    long tmp = start_ip ^ end_ip;
    while(tmp != 0){
        tmp = tmp >> 1;
        host_bits++;
    }
    host_mask = (unsigned long)-1 >> (32 - host_bits);
    long network_addr = start_ip & (-1 ^ host_mask);
    long broadcast_addr = start_ip | host_mask;
    if(host_bits > 1){
        long split_low = (network_addr | host_mask >> 1);
        long split_high =(broadcast_addr & (-1 ^ host_mask >> 1));
        if(start_ip != network_addr || end_ip != broadcast_addr){
            subnets(start_ip, split_low);
            subnets(split_high, end_ip);
        }else{
            cout << long_to_str(start_ip) << "/" << 32-host_bits << endl;
        }
    }else{
        cout << long_to_str(start_ip) << "/" << 32-host_bits << endl;
    }
}

您可以采用这种方式将子网放入向量(或任何您想要的)中,而不是将其打印到cout。因此,当我们用我在subnets(str_to_long("10.0.0.1"), str_to_long("10.0.0.126"))开头提到的范围运行时,你会得到构成该范围的12个子网的确切列表。

10.0.0.1/32
10.0.0.2/31
10.0.0.4/30
10.0.0.8/29
10.0.0.16/28
10.0.0.32/27
10.0.0.64/27
10.0.0.96/28
10.0.0.112/29
10.0.0.120/30
10.0.0.124/31
10.0.0.126/32