Все IP-адреса в подсети (C)

У кого-нибудь есть хороший пример того, как я мог бы взять IP-адрес с CIDR, например 192.168.1.1/24, и вернуть все IP-адреса внутри этого диапазона, например 192.168.1.1, 192.168.1.2, 192.168.1.3 ...

Я согласен с тем, что он возвращается в виде массива unsigned long, char или просто что-то вроде

/* Pseudocode */
while(currnetip <= finalip) { 
    print(currnetip); 
    currnetip++; 
}

Пока я могу это понять, все в порядке.

Не стесняйтесь комментировать ссылку на сообщение, если вы думаете, что это может помочь мне.

Редактировать: Вероятно, стоит упомянуть, что я нашел много вещей, которые рассчитали широковещательный адрес и т. Д. Я просто не уверен, как связать все эти функции вместе.


person John Doe    schedule 23.11.2016    source источник
comment
Есть только два текущих IP: IPv4 и IPv6. Я думаю, вы имеете в виду IP-адреса, а не IP-адреса (интернет-протоколы).   -  person Ron Maupin    schedule 24.11.2016
comment
@ Рон Мопен, да, это то, что я имел в виду. Отредактировано для ясности   -  person John Doe    schedule 24.11.2016


Ответы (1)


Во-первых, упакуйте свой IPv4-адрес в uint32_t (определено в <stdint.h>), поместив крайний левый октет в точечно-десятичной нотации в наиболее значащие биты. Например,

uint32_t ipv4_pack(const uint8_t octet1,
                   const uint8_t octet2,
                   const uint8_t octet3,
                   const uint8_t octet4)
{
    return (((uint32_t)octet1) << 24)
         | (((uint32_t)octet2) << 16)
         | (((uint32_t)octet3) <<  8)
         |  ((uint32_t)octet4);
}

и обратное,

unsigned char *ipv4_unpack(unsigned char *addr, const uint32_t packed)
{
    addr[3] = (uint8_t)(packed);
    addr[2] = (uint8_t)(packed >> 8);
    addr[1] = (uint8_t)(packed >> 16);
    addr[0] = (uint8_t)(packed >> 24);
    return addr;
}

Адрес типа 128.64.32.16 упаковывается в 0x80402010 (128 == 8016, 64 == 4016, 32 == 2016 и 16). == 1016).


Вам также необходимо преобразовать размер префикса CIDR (от 1 до 32) в двоичную маску. из этого множества старших битов:

uint32_t ipv4_mask(const int prefix_size)
{
    if (prefix_size > 31)
        return (uint32_t)0xFFFFFFFFU;
    else
    if (prefix_size > 0)
        return ((uint32_t)0xFFFFFFFFU) << (32 - prefix_size);
    else
        return (uint32_t)0U;
}

Префикс 24 соответствует маске 1111111111111111111111100000000 в двоичном формате и 0xFFFFFF00 в шестнадцатеричном.

Префикс 28 соответствует маске 1111111111111111111111111110000 в двоичном формате и 0xFFFFFFFF0 в шестнадцатеричном.

Для адреса addr1.addr2.addr3.addr4/prefix первым адресом в диапазоне (обычно это адрес шлюза для указанного диапазона) является

uint32_t first = ipv4_pack(addr1, addr2, addr3, addr4) & ipv4_mask(prefix);

и последний адрес (обычно широковещательный адрес для указанного диапазона)

uint32_t last = ipv4_pack(addr1, addr2, addr3, addr4) | (~ipv4_mask(prefix));

Во всех случаях first <= last и итерация от first до last включительно, а также вызов ipv4_unpack() для распаковки значения в десятичную запись с точками) дает все адреса IPv4 в пределах диапазона.


То же самое будет работать для IPv6, но требует что-то вроде типа uint128_t. (Конечно, его можно эмулировать с меньшими целочисленными типами без знака, выполнив несколько дополнительных инструкций, но логика останется прежней.)

person Nominal Animal    schedule 23.11.2016