I've just started on some raw network programming in C++ and have been compiling on my Raspberry Pi itself (no cross-compiling). That makes everything little endian.
After constructing my IP header, I calculate the IP checksum, but it was always coming out incorrect (based on an example here http://www.thegeekstuff.com/2012/05/ip-header-checksum/).
Revving up gdb, I've worked my issue down to the ordering of the first 32 bits in the IP header. The example uses 0x4500003C
, which means version 4 (0x4
), IHL 5 (0x5
), TOS 0 (0x00
), and tot_length 60 (0x003C
). So I set my packet up the same.
struct iphdr* ip; // Also some mallocing
ip->version = 4;
ip->ihl = 5;
ip->tos = 0;
ip->tot_len = 60;
Now in gdb, I examined the first 32 bits, expecting 0x3C000045
because of endianness, but instead I get this:
(gdb) print ip
$1 = (iphdr *) 0x11018
(gdb) x/1xw 0x11018
0x11018: 0x003c0045
The first 16 bits are in little endian (0x0045
) but the second, containing decimal 60, seem to be in big endian (0x003C
)!
What is giving this? Am I crazy? Am I completely wrong about byte order inside structs? (It's a definite possibility)
The Endianness is a property of the CPU ... and on the Raspberry it is Little-Endian. It is not possible to boot into "Big-Endian"-Mode, because the CPU won't understand one single instruction.
The times when you need to worry about endianess: you are sending binary data between machines or processes (using a network or file). If the machines may have different byte order or the protocol used specifies a particular byte order (which it should), you'll need to deal with endianess.
So knowledge of endianness is important when you are reading and writing the data across the network from one system to another. If the sender and receiver computer have different endianness, then the receiver system would not receive the actual data transmitted by the sender.
For example, Intel processors have traditionally been little-endian. Motorola processors have always been big-endian. Big-endian is an order in which the "big end" (the most-significant byte) is stored first. Little-endian is an order in which the "little end" (the least-significant byte) is stored first.
There's the order of fields within the struct, and then there's the order of bytes within a multibyte field.
0x003C
isn't endian at all, it's the hex value for 60. Sure, it's stored in memory with some endianness, but the order you used to write the field and the order you used to read it back out are the same -- both are the native byte order of the Raspberry Pi, and they cancel out.
Typically you will want to write:
ip->tot_len = htons(60);
when storing a 16-bit field into a packet. There's also htonl
for 32-bit fields, and ntohs
and ntohl
for reading fields from network packets.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With