John Doe - 1 year ago 65
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.

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)
{
}
``````

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

``````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.)