John Doe John Doe - 15 days ago 6
C Question

All IP's in a subnet (C)

Does someone have a good example of a way I could take a IP address with a CIDR such as

192.168.1.1/24
and return all the ip addresses inside of that range such as
192.168.1.1, 192.168.1.2, 192.168.1.3 ...


I'm fine with it being returned in a array of
unsigned long
, a
char
or just something like

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


As long as I can understand it its fine.

Feel free to comment a link to a post if you think it could help me.

Edit: Probably worth mentioning I've found lots of stuff that calculated the broadcast address ect I'm just not sure how to link all those functions together.

Answer

First, pack your IPv4 address into an uint32_t (defined in <stdint.h>), putting the leftmost octet in dotted-decimal notation into the most significat bits. For example,

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));
}

and its inverse,

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;
}

An address like 128.64.32.16 is packed into 0x80402010 (128 == 8016, 64 == 4016, 32 == 2016, and 16 == 1016).

You need to also convert the CIDR prefix size (1 to 32) into a binary mask of that many highest bits set:

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;
}

Prefix 24 corresponds to a mask of 11111111111111111111111100000000 in binary, and 0xFFFFFF00 in hexadecimal.

Prefix 28 corresponds to a mask of 11111111111111111111111111110000 in binary, and 0xFFFFFFF0 in hexadecimal.

For address addr1.addr2.addr3.addr4/prefix, the first address in a range (typically the gateway address for said range) is

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

and the last address (typically the broadcast address for said range) is

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

In all cases, first <= last, and iterating from first to last, inclusive, and calling ipv4_unpack() to unpack the value into dotted-decimal notation), yields all IPv4 addresses within the range.


The same would work for IPv6, but requires something like an uint128_t type. (It can be emulated with smaller unsigned integer types, of course, taking a few more instructions, but the logic stays the same.)