calculate IP header len - c

I am trying to frame the ICMP packet and send it through raw socket. Looking at the examples, I see that the IP packet length is calculated as :
iphdr.ip_hl = sizeof(struct ip) >> 2
Can you please explain why we need to right shift struct ip by 2 times instead of assigning a constan value ?

The 'ip_hl' field of an IP (or ICMP) packet is defined as the length of the IP header, in 32-bit words.
sizeof(struct ip) yields the length of the IP header, in 8-bit bytes. Right shifting this value twice provides the length in 32-bit words, as expected in the ip_hl field.
A good reason not to use a constant for this, is to eliminate magic numbers in source code. (The compiler will generate a constant value anyway for 'sizeof(struct ip) >> 2').

Because 4-bit header length field is the number of 32-bit words in the header, including options, so it might be longer then 20 bytes (field value 5), so it's not supposed to be a constant value. Your examples just assume no options scenario.

Related

How to get the difference between two IPv6 address in C, consedering all 128 bits?

I am trying to get the difference between 2 IPv6 address. eg. Given an address range from min_v6_address to max_v6_address. I need
"uint32 offset = max_v6_address - min_v6_address"
to get the total number of IP's between them in C language.
Thanks.

Linux raw socket - endianness in ip header

I would like to confirm that any value in a ip header bigger than one byte (short, int.. Or their alternative int16_t..) should be converted to big endian using ntohs/ntohl etc to send over the wire.
Did The kernel managed that under the hood when normal socket were used or another technic was used?
It is quite of a mess since some functions, like getting the ip address of the interface with ioctl seem to already put the data in a big endian fashion when casted to sockaddr_in*. It output my address like 36.2.168.192 (with printf's %d) but the ifreq output it like 192.168.2.36
code
int addr = ((struct sockaddr_in *)(&ifr.ifr_addr))->sin_addr.s_addr;
printf("%d %d %d %d ", (addr >> 24) & 255 , (addr >> 16) & 255,(addr >> 8) & 255, (addr) & 255);
gives me my ip address in the reverse order
whereas using
for (int _x = 0; x < 14; ++_x) {
printf("%d ", ifr.ifr_ifru.ifru_addr.sa_data[_x] );
}
will give me some zeros the ip address in the right order (192.168.2.36) followed by zeros.
Waw.. I am lost.
Quite of a jungle if you ask me.
QUESTION
what to convert to big endian and what not to ?
Best not to think of it as big-endian or little-endian, but rather host order (which may be either) and network order (which is big-endian). You are correct that in the IP standard, every field is in network order. You should use the ntohs and ntohl functions for converting network to host order, and the htons and htonl functions for converting host to network order. That way your code will compile right on a big-endian machine too.
An IP address is normally stored internally in network order, in which case it can be converted to/from presentation format using inet_pton and inet_ntop. You thus don't normally need to play around with the storage format of these addresses unless you are manually applying netmasks etc. If you are doing this, the octets (bytes to you and me) are stored in the natural order, i.e. 111.222.33.44 is stored in the order 111, 222, 33 and 44. If you think about it, that's a big-endian order.

PCAP Ethertype Return

I am attempting to identify the ether type of a packet that I am receiving. The ether type ID is 608 and has no corresponding definition in Ethertype.h(libpcap 1.2.1). The majority of the packets received have an either type of 8 which again has no corresponding definition in Ethertype.h. Does anyone have any ideas of what the cause may be behind this or should I contact TCPDump with an error report.
What is the return value of pcap_datalink() on the pcap_t on which you're capturing?
If it's not DLT_EN10MB (which has the value 1), your packets aren't Ethernet packets, and you shouldn't parse them as Ethernet packets.
If it is DLT_EN10MB, then is that hex 608 or decimal 608? If it's decimal 608, it's a length field rather than a type field. The same applies to 8, which is the same decimal or hex and would thus be a length value rather than a type value.
From manpage:
"The ntohs() function converts the unsigned short integer netshort from network byte order to host byte order.".
From my code:
if(ntohs(ethernet->ether_type)==0x0800) ...

Typecasting a char to an int (for socket)

I have probably asked this question twice since y'day, but I have still not got a favourable answer. My problem is I have an IP address which is stored in an unsigned char. Now i want to send this IP address via socket from client to server. People have advised me to use htonl() and ntohl() for network byte transfer, but I am not able to understand that the arguments for htonl() and ntohl() are integers...how can i use it in case of unsigned char?? if I can't use it, how can I make sure that if I send 130.191.166.230 in my buffer, the receiver will receive the same all the time?? Any inputs or guidance will be appreciated. Thanks in advance.
If you have an unsigned char array string (along the lines of "10.0.0.7") forming the IP address (and I'm assuming you do since there are very few 32-bit char systems around, making it rather difficult to store an IP address into a single character), you can just send that through as it is and let the other end use it (assuming you both encode characters the same way of course, such as with ASCII).
On the other hand, you may have a four byte array of chars (assuming chars are eight bits) containing the binary IP address.
The use of htonl and ntohl is to ensure that this binary data is sent through in an order that both big-endian and little-endian systems can understand.
To that end, network byte order (the order of the bytes "on the wire") is big-endian so these functions basically do nothing on big-endian systems. On little-endian systems, they swap the bytes around.
In other words, you may have the following binary data:
uint32_t ipaddress = 0x0a010203; // for 10.1.2.3
In big endian layout that would be stored as 0x0a,0x01,0x02,0x03, in little endian as 0x03,0x02,0x01,0x0a.
So, if you want to send it in network byte order (that any endian system will be able to understand), you can't just do:
write (fd, &ipaddress, 4);
since sending that from little endian system to a big endian one will end up with the bytes reversed.
What you need to do is:
uint32_t ipaddress = 0x0a010203; // for 10.1.2.3
uint32_t ip_netorder = htonl (ipaddress); // change if necessary.
write (fd, &ip_netorder, 4);
That forces it to be network byte order which any program at the other end can understand (assuming it uses ntohl to ensure it's correct for its purposes).
In fact, this scheme can handle more than just big and little endian. If you have a 32-bit integer coding scheme where ABCD (four bytes) is encoded as A,D,B,C or even where you have a bizarrely wild bit mixture forming your integers (like using even bits first then odd bits), this will still work since your local htonl and ntohl know about those formats and can convert them correctly to network byte order.
An array of chars has a defined ordering and is not endian dependent - they always operate from low to high addresses by convention.
Do you have a string or 4 bytes?
IP4 address is 4 bytes (aka chars). So you will be having 4 unsigned chars, in an array somewhere. cast that array to send it across.
e.g. unsigned char IP[4];
use ((char *)IP) as data buffer to send, and send 4 bytes from it.

Sending the array of arbitrary length through a socket. Endianness

I'm fighting with socket programming now and I've encountered a problem, which I don't know how to solve in a portable way.
The task is simple : I need to send the array of 16 bytes over the network, receive it in a client application and parse it. I know, there are functions like htonl, htons and so one to use with uint16 and uint32. But what should I do with the chunks of data greater than that?
Thank you.
You say an array of 16 bytes. That doesn't really help. Endianness only matters for things larger than a byte.
If it's really raw bytes then just send them, you will receive them just the same
If it's really a struct you want to send it
struct msg
{
int foo;
int bar;
.....
Then you need to work through the buffer pulling that values you want.
When you send you must assemble a packet into a standard order
int off = 0;
*(int*)&buff[off] = htonl(foo);
off += sizeof(int);
*(int*)&buff[off] = htonl(bar);
...
when you receive
int foo = ntohl((int)buff[off]);
off += sizeof(int);
int bar = ntohl((int)buff[off]);
....
EDIT: I see you want to send an IPv6 address, they are always in network byte order - so you can just stream it raw.
Endianness is a property of multibyte variables such as 16-bit and 32-bit integers. It has to do with whether the high-order or low-order byte goes first. If the client application is processing the array as individual bytes, it doesn't have to worry about endianness, as the order of the bits within the bytes is the same.
htons, htonl, etc., are for dealing with a single data item (e.g. an int) that's larger than one byte. An array of bytes where each one is used as a single data item itself (e.g., a string) doesn't need to be translated between host and network byte order at all.
Bytes themselves don't have endianness any more in that any single byte transmitted by a computer will have the same value in a different receiving computer. Endianness only has relevance these days to multibyte data types such as ints.
In your particular case it boils down to knowing what the receiver will do with your 16 bytes. If it will treat each of the 16 entries in the array as discrete single byte values then you can just send them without worrying about endiannes. If, on the other hand, the receiver will treat your 16 byte array as four 32 bit integers then you'll need to run each integer through hton() prior to sending.
Does that help?

Resources