Вычисление списка подсетей, которые составляют данный диапазон IP

Я пытаюсь придумать и понять функцию 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 но они не равны, так что это не то, что я ожидаю), а не весь диапазон.

2

Решение

Прежде всего, это не сайт, где можно получить рабочий код, не пытаясь что-то написать самостоятельно. Однако я объясню, как я бы подошел к этой проблеме, и дам вам код, который можно легко адаптировать к вашим потребностям.

Эта проблема проста, если у вас есть диапазоны, которые определяют одну область видимости, например 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/25 теперь у вас есть 10.0.0.0/26 а также 10.0.0.64/26 и ваши два новых диапазона 10.0.0.1 - 10.0.0.63 а также 10.0.0.64 - 10.0.0.126, Вы продолжите разделять свои диапазоны до тех пор, пока:

  1. Ваш начальный IP-адрес равен адресу подсети а также Конечный IP равен адресу широковещательной рассылки
  2. Ваш диапазон настолько мал, что вы не можете разделить его дальше (/ 32).

Это код, который я собрал для этого. Все вычисления выполняются на десятичном представлении IP-адреса (не точечно-десятичного), поэтому первые две функции преобразуют string с IP в десятичном виде с точками в 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) аргументы — Start IP и End 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
3

Другие решения