Search code examples
clinuxendianness

c: increment octets of IPv4 address in network byte order


In the following code addr_min and addr_max are 32-bit unsigned values representing IPv4 address, and they're in network-byte order, i.e. big-endian format:

uint32_t addr;
for (addr = addr_min; addr <= addr_max; addr = addr + htonl(1)) {
   ...
}

So assuming I want to iterate over address 1.2.0.0 and gradually increment the third and fourth octets, thus yielding 65534 addresses in total (addr_max when printed shows feff0201). I expected the above code would work, instead it only changes the 4th octet to value 0xfe and never touches the third one.

What am I possibly doing wrong?


Solution

  • You aren't intended to do any arithmetic with values already encoded in network byte order. In this case, you're likely only adding to the highest-order byte.

    The quick answer is that:

    uint32_t val_n = htonl(init_val);
    val_n += htonl(1);
    

    is not equivalent to

    uint32_t val = init_val;
    val += 1;
    uint32_t val_n = htonl(val);
    

    Where val and init_val are regular host-order values, and val_n is network-order encoded.

    The network-order values are not useful for arithmetic directly. You don't know what value htonl(1) will add to your value, and you don't know that adding it will cause the next byte to roll over after you've gotten to 255 on the lowest byte. Don't consider network-order values to be numbers, think of them as opaque values with meaning.

    Keep everything that you work with in host order, as actual numbers:

    uint32_t addr;
    for (addr = addr_min; addr <= addr_max; addr += 1) {
        uint32_t addr_net_order = htonl(addr);
        ...
    }
    

    where addr, addr_min, and addr_max are all host order i.e. if addr_min should be 1.2.0.0, addr_min = 0x01020000.