Due to accidents of history, internet protocols use big-endian numbers, while common CPU architectures such as AMD64 or the older IA-32 use little-endian numbers. There do exist functions to convert numbers between host and network byte orders, though if a number is not converted correctly, one may end up with a bad (that is, backwards) address. For some addresses (9.9.9.9 comes to mind) this is never a problem as these addresses read the same, forwards or backwards.
If you only have a little (or big) endian system it can be helpful to run a real or virtual system that uses the opposite byte order to confirm that any code you write is correct on both platforms, and that any generated numbers can be moved between any platform without an address being generated backwards. Debian may still make available a big-endian MIPS image that runs under QEMU, or I believe some of the ARM chips can also run big-endian.
To see how big- and little-endian byte orders differ, the following commands pack two different IP addresses into a 32-bit bucket, and then unpack that using either "N" for Network byte order (big) or "V" for VAX (little-endian) order.
$ perl -E 'say unpack N => pack C4 => 127, 0, 0, 1'
2130706433
$ perl -E 'say unpack V => pack C4 => 127, 0, 0, 1'
16777343
$ perl -E 'say unpack N => pack C4 => 9, 9, 9, 9'
151587081
$ perl -E 'say unpack V => pack C4 => 9, 9, 9, 9'
151587081
Building an address with the incorrect byte order will result in a backwards address.
$ make wrong && ./wrong
cc -O2 -pipe -o wrong wrong.c
1.0.0.127
In this case, the big-endian 2130706433 was used directly on a little-endian system, resulting in a backwards address.
// wrong - use a big-endian address number on a little-endian system
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdio.h>
int
main(int argc, char *argv[])
{
char string[INET_ADDRSTRLEN];
struct in_addr addr;
addr.s_addr = 2130706433;
inet_ntop(AF_INET, &addr, (char *) &string, sizeof(string));
puts(string);
}
One may wonder how much incorrect traffic 1.0.0.127 receives. Better code might emit numbers as big-endian, and then use the htonl(3) function to, if necessary, flip the bytes around.
$ make htonl && ./htonl
cc -O2 -pipe -o htonl htonl.c
127.0.0.1
root@debian-mips:~# make htonl && ./htonl
make: `htonl' is up to date.
2130706433
127.0.0.1
// portable number to address assuming big-endian inputs
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdio.h>
int
main(int argc, char *argv[])
{
char string[INET_ADDRSTRLEN];
struct in_addr addr;
addr.s_addr = htonl(2130706433);
inet_ntop(AF_INET, &addr, (char *) &string, sizeof(string));
puts(string);
}
A better interface may be to build the address up by bytes, similar to the "C4" or "four unsigned characters" pack trick used in the perl code above. This has the advantage of better documenting the address than 2130706433 does.
// buildip - use a macro to build addresses
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdio.h>
#define MKIPV4(a, b, c, d) \
( (((a) &0xFF) << 24) | \
(((b) &0xFF) << 16) | \
(((c) &0xFF) << 8) | \
((d) &0xFF) )
int
main(int argc, char *argv[])
{
char string[INET_ADDRSTRLEN];
struct in_addr addr;
addr.s_addr = htonl(MKIPV4(127,0,0,1));
inet_ntop(AF_INET, &addr, (char *) &string, sizeof(string));
puts(string);
}
$ make buildip && ./buildip
cc -O2 -pipe -o buildip buildip.c
127.0.0.1
root@debian-mips:~# make buildip && ./buildip
cc buildip.c -o buildip
127.0.0.1
Netmasks are also 32-bit numbers, and must get the bits ordered correctly. There are not many netmasks (ignoring exotic netmasks that may not even be supported by the software in question) so a table of them might be good to have.
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdio.h>
unsigned int netmasken[33] = {
0, 0x80000000, 0xc0000000, 0xe0000000, 0xf0000000, 0xf8000000,
0xfc000000, 0xfe000000, 0xff000000, 0xff800000, 0xffc00000, 0xffe00000,
0xfff00000, 0xfff80000, 0xfffc0000, 0xfffe0000, 0xffff0000, 0xffff8000,
0xffffc000, 0xffffe000, 0xfffff000, 0xfffff800, 0xfffffc00, 0xfffffe00,
0xffffff00, 0xffffff80, 0xffffffc0, 0xffffffe0, 0xfffffff0, 0xfffffff8,
0xfffffffc, 0xfffffffe, 0xffffffff};
int
main(int argc, char *argv[])
{
char string[INET_ADDRSTRLEN];
struct in_addr addr;
// build the table (fixup little-endian systems)
for (size_t i = 1; i < 32; ++i)
netmasken[i] = htonl(netmasken[i]);
// check the results
for (size_t i = 0; i < 33; ++i) {
addr.s_addr = netmasken[i];
inet_ntop(AF_INET, &addr, (char *) &string, sizeof(string));
puts(string);
}
}
netmasken.c
What's an exotic netmask? I'd define it as a 32-bit number that has non-contiguous bit groups, something like 255.0.255.0. There may be extremely rare uses for such in firewall rules, assuming the firewall supports them and where you cannot simply make two or more table entries to match what the exotic mask does.
more on netmasks