IPv4 to decimal different values? - c

Why is the IPv4's decimal value different with inet_pton and inet_addr (1734763876) than what you get if you use these 2 websites (1684366951) ?
struct sockaddr_in sin;
inet_pton(AF_INET, "100.101.102.103", &(sin.sin_addr));
printf("%i\n%i\n", inet_addr("100.101.102.103"), sin.sin_addr);
http://www.allredroster.com/iptodec.htm
http://www.ipaddresslocation.org/convertip.php

Endianness - they have the four bytes in opposite orders:
1734763876 = 0x67 66 65 64
1684366951 = 0x64 65 66 67
The value you'll need to use for URLs etc. is the one in 'Network' order, Most-Significant-Byte first. Use htonl() (host-to-network-long) to convert the value, i.e.
printf("%i\n%i\n", htonl(inet_addr("100.101.102.103")), htonl(sin.sin_addr));
caf points out below that I probably have this backwards: the issue is really that you need to convert the network-order data from the socket functions back into host-order for display, i.e.
printf("%i\n%i\n", ntohl(inet_addr("100.101.102.103")), ntohl(sin.sin_addr));

inet_addr gives the result in network-byte order.
1684366951 and 1734763876 are the same number ;-) if you change the endianess.

Related

Wrong size of IPv4 packets in DPDK

I have these packets saved in pcap file (shown in wireshark)
Iam parsing these packets using DPDK and for some reason i cannot use mbufs. To parse them iam using struct ipv4_hdr and struct ether_hdr as following:
eth_hdr = (struct ether_hdr *) pckt;
ip_hdr = (struct ipv4_hdr *)((unsigned char *) pckt + sizeof(struct ether_hdr));
The total_length in ipv4_hdr is size of ipv4 header + size of data. With size of etherhet header it should be the total length of packet, right?
size = rte_bswap16(ip_hdr->total_length) + sizeof(struct ether_hdr);
which gives me for these 6 packets output:
54, 54, 54, 54, 54, 54
For first, third and fifth packets its correct, but for the others its 6 bytes less than it should be.
Iam asking what these 6 bytes in packets are and how to find out the correct sizes using ipv4 and ether headers:
54, 60, 54, 60, 54, 60
In wireshark all of these packets has total_length 40 bytes and sizeof(ether_hdr) is 14 bytes -> it should be 54.
I guess the root cause is the minimum Ethernet frame length. As Wikipedia says, the minimum frame length is 64 bytes.
At the end of each Ethernet frame we add Frame Check Sequence (FCS, 4 octets), so it makes minimum 60 octets for Ethernet header and payload.
Now answering your questions:
I am asking what these 6 bytes in packets
Those are zero paddings to make ethernet frame (with FCS) at least 64 octets.
For outgoing packets, there are no paddings yet, they will be added later by the driver or NIC itself. So Wireshark shows unpadded frames, i.e. 40 bytes of IP + 14 bytes of Ethernet header (without FCS) makes 54 bytes.
For incoming packets, there are already paddings added by the sending part. So Wireshark shows frames with padding, i.e. 64 octets - 4 octets FCS = 60 octets.
how to find out the correct sizes using ipv4 and ether headers
Your method is completely correct. Those zeros at the end of the frame are just padding and should be ignored. If we really need a correct length, we should take into account the minimum frame length as described above.

struct sockaddr_in member byte order for bind()

I'm learning socket programming and am confused by what I feel is inconsistent use of htons() and family of functions in my learning material. I'm currently reading this site which has the following code segment:
001 1: struct sockaddr_in adr_inet;
002 2: int adr_len;
003 3:
004 4: memset(&adr_inet,0,sizeof adr_inet);
005 5:
006 6: adr_inet.sin_family = AF_INET;
007 7: adr_inet.sin_port = ntohs(0);
008 8: adr_inet.sin_addr.s_addr = ntohl(INADDR_ANY);
009 9: adr_len = sizeof adr_inet;
A subsequent example further down at the same noted site has the following code segment:
030 30: struct sockaddr_in adr_inet;/* AF_INET */
...
042 42: /* Create an AF_INET address */
043 43: memset(&adr_inet,0,sizeof adr_inet);
044 44:
045 45: adr_inet.sin_family = AF_INET;
046 46: adr_inet.sin_port = htons(9000);
047 47: memcpy(&adr_inet.sin_addr.s_addr,IPno,4);
048 48: len_inet = sizeof adr_inet;
049 49:
050 50: /* Now bind the address to the socket */
051 51: z = bind(sck_inet,
052 52: (struct sockaddr *)&adr_inet,
053 53: len_inet);
Question:
Why is ntohs() used on adr_inet.sin_port in the first instance, but htons() in the second?
Question:
Why is neither ntohs() nor htons() used on adr_inet.sin_family?
The noted site doesn't explain why ntohs() or htons() are used in their respective examples; it only says to "note the use of" said functions.
I understand endianness and that network byte order is big-endian order. My questions is more about when do you want a struct sockaddr_in's members in network vs. host byte order? In the second code example, .sin_port is set to network byte order before being passed to bind(). I can see the case for passing data to this function in either network or host byte order: bind() is a "network-related" function, so maybe it needs its data in network byte order; on the other hand bind() is executed on the host, so why shouldn't accept data in host byte order?
Why is ntohs() used on adr_inet.sin_port in the first instance, but htons() in the second?
The first is a mistake, but in practice works anyway.
Nowadays practically all machines use 8-bit bytes and either consistent big-endian or consistent little-endian formats. On the former both hton[sl] and ntoh[sl] are no-ops; on the latter both reverse the byte order, and thus actually do the same thing even though their intended semantics are different. Thus using the wrong one still works on all systems you're likely to run a program on.
Back when the socket API was designed this wasn't always the case; for example the then-popular PDP-11 somewhat infamously used 'middle-endian' (!) aka 'NUXI' order for 32-bit.
Why is neither ntohs() nor htons() used on adr_inet.sin_family?
Again in ancient times the Internet Protocol stack was only one of several (up to a dozen or so) competing network technologies. The family field distinguishes different types of sockaddr_* structures for these different protocols, which did not all follow the Internet 'rule' for big-endian, at least not consistently. As there was no universal network representation for family they just left it in host order -- which is usually more convenient for host software.
Nowadays in practice nobody uses any families but INET, INET6, and sometimes UNIX -- and the latter can be replaced by using named pipes in the filesystem which is usually at least as good.
Why is neither ntohs() nor htons() used on adr_inet.sin_family?
adr_inet.sin_family is initialized to the value of AF_INET. This is defined in bits/socket.h (which is called by netinet/in.h in your example) as:
#define PF_INET 2 /* IP protocol family. */
and then,
#define AF_INET PF_INET
So AF_INET is just a way for the program to identify the associated socket as a TCP/IP connection. It won't actually hold the value of an IPv4 address itself, so there's no need to perform an endian conversion on it.
Also, note that in newer iterations of C, netinet/in.h has a comment that states the following:
/* Functions to convert between host and network byte order.
Please note that these functions normally take `unsigned long int' or
`unsigned short int' values as arguments and also return them. But
this was a short-sighted decision since on different systems the types
may have different representations but the values are always the same. */
extern uint32_t ntohl (uint32_t __netlong) __THROW __attribute__ ((__const__));
extern uint16_t ntohs (uint16_t __netshort)
__THROW __attribute__ ((__const__));
extern uint32_t htonl (uint32_t __hostlong)
__THROW __attribute__ ((__const__));
extern uint16_t htons (uint16_t __hostshort)
__THROW __attribute__ ((__const__));
Whereas the website you're referencing cites the older use of unsigned long and unsigned short
datatypes for the conversion functions. So there's a chance you may encounter issues running code from that site if
you're using a newer version of C.

Print unsigned short int in hex and decimal C

I've been trying to print unsigned short int values in C with no luck. As far as I know, it's a 16 bit value, so I've tried several different methods to print this 2 bytes together, but I've only been able to print it correctly when doing it byte by byte.
Note that I want to print this 16 bits in both a decimal and hexadecimal format, for example 00 01 as 1, another example would be: 00 ff as 255.
I have this struct:
struct arphdr {
unsigned short int ar_hrd;
unsigned short int ar_pro;
unsigned char ar_hln;
unsigned char ar_pln;
unsigned short int ar_op;
// I'm commenting the following part because I won't need it now
// for the explanation
/* unsigned char __ar_sha[ETH_ALEN];
unsigned char __ar_sip[4];
unsigned char __ar_tha[ETH_ALEN];
unsigned char __ar_tip[4]; */
};
And this function:
void print_ARP_msg(struct arphdr arp_hdr) {
// I need to print the arp_hdr.ar_hrd in both decimal and hex.
printf("Format HW: %04x\n", arp_hdr.ar_hrd);
printf("Format Proto: %04x\n", arp_hdr.ar_pro);
printf("HW Len: %x\n", arp_hdr.ar_hln);
printf("Proto Len: %x\n", arp_hdr.ar_pln);
printf("Command: %04x\n", arp_hdr.ar_op);
}
The print_ARP_msg function returns me this:
Format HW: 0100
Format Proto: 0008
HW Len: 6
Proto Len: 4
Command: 0100
The hex values of the struct are "00 01 08 00 06 04 00 01", so I don't know why it's returning me 0100 in the arp_hdr.ar_hrd value.
Also, I made a function which prints the struct in hex, to make sure that I'm doing it right, and I was able to check, that all the fields where correctly assigned.
PS: before sending this question, I realized that it's printing the correct Hex values but disordered. Could it be related to the little/big endian "difference"?
Could it be related to the little/big endian "difference"?
Yes. If you're dealing with packets that have arrived over a network - and you're printing fields of an ARP packet, so that's exactly what you're doing - you may have to convert from the byte order of the fields as they are when sent over the network to the byte order on the machine on which you're running.
For example:
printf("Format HW: %04x\n", ntohs(arp_hdr.ar_hrd));
In that particular case, you can get away without the ntohs() call on a big-endian machine (SPARC, System/3x0, z/Architecture, PowerPC/Power ISA running AIX, PowerPC/Power ISA running Mac OS X, possibly PowerPC/Power ISA running Linux, etc.), but you can't get away without it on a little-endian machine (anything with an x86 processor, including x86-64 processors, probably most ARM, etc.).
You can use it on both types of processor.
The hex values of the struct are "00 01 08 00 06 04 00 01", so I don't know why it's returning me 0100 in the arp_hdr.ar_hrd value.
Looks like your platform uses the little endian system.
What appears to be 00 01 in memory is interpreted as 01 x 2^8 + 00. In other words, the hex representation of the number is 0100.
ARP packets, like all internet protocol packets, are transmitted in "network order", which is big-endian. The client must use, for example, ntohs (network to host short) to convert from network order to the machine's local byte order, and htons to go the other way.
See man byteorder for details.

FAT BPB and little endian reversal

My CPU is little endian, which documentation has told me conforms to the byte-order of the FAT specification. Why then, am I getting a valid address for the BS_jmpBoot, bytes 0-3 of first sector, but not getting a valid number for BPB_BytesPerSec, bytes 11-12 of the first sector.
116 int fd = open (diskpath, O_RDONLY, S_IROTH);
117
118 read (fd, BS_jmpBoot, 3);
119 printf("BS_jmpBoot = 0x%02x%02x%02x\n", BS_jmpBoot[0], S_jmpBoot[1], S_jmpBoot[2]);
120
121 read (fd, OEMName, 8);
122 OEMName[8] = '\0';
123 printf("OEMName = %s\n", OEMName);
124
125 read (fd, BPB_BytesPerSec, 2);
126 printf("BPB_BytesPerSec = 0x%02x%02x\n",BPB_BytesPerSec[0], BPB_BytesPerSec[1]);
Yields
BS_jmpBoot = 0xeb5890 //valid address, while 0x9058eb would not be
OEMName = MSDOS5.0
BPB_BytesPerSec = 0x0002 //Should be 0x0200
I would like figure out why BS_jmpBoot and OEMName print valid but BPB_BytesPerSec does not. If anyone could enlighten me I would be greatly appreciative.
Thanks
EDIT: Thanks for the help everyone, it was my types that were making everything go awry. I got it to work by writing the bytes to an unsigned short, as uesp suggested(kinda), but I would still like to know why this didn't work:
unsigned char BPB_BytesPerSec[2];
...
125 read (fd, BPB_BytesPerSec, 2);
126 printf("BPB_BytesPerSec = 0x%04x\n", *BPB_BytesPerSec);
yielded
BPB_BytesPerSec = 0x0000
I would like to use char arrays to allocate the space because I want to be sure of the space I'm writing to on any machine; or should I not?
Thanks again!
You are reading BPB_BytesPerSec incorrectly. The structure of the Bpb is (from here):
BYTE BS_jmpBoot[3];
BYTE BS_OEMName[8];
WORD BPB_BytesPerSec;
...
The first two fields are bytes so their endianness is irrelevant (I think). BPB_BytesPerSec is a WORD (assuming 2 bytes) so you should define/read it like:
WORD BPB_BytesPerSec; //Assuming WORD is defined on your system
read (fd, &BPB_BytesPerSec, 2);
printf("BPB_BytesPerSec = 0x%04x\n", BPB_BytesPerSec);
Since when you read the bytes directly you get 00 02, which is 0x0200 in little endian, you should correctly read BPB_BytesPerSec like this.
First of all, this line:
printf("BPB_BytesPerSec = 0x%02x%02x\n",BPB_BytesPerSec[0], BPB_BytesPerSec[1]);
is printing the value out in big endian format. If it prints 0x0002 here, the actual value would be 0x0200 in little endian.
As for the BS_jmpBoot value, according to this site:
The first three bytes EB 3C and 90 disassemble to JMP SHORT 3C NOP. (The 3C value may be different.) The reason for this is to jump over the disk format information (the BPB and EBPB). Since the first sector of the disk is loaded into ram at location 0x0000:0x7c00 and executed, without this jump, the processor would attempt to execute data that isn't code.
In other words, the first 3 bytes are opcodes which are three separate bytes, not one little endian value.

htonl printing garbage value

The variable 'value' is uint32_t
value = htonl(value);
printf("after htonl is %ld\n\n",value);
This prints -201261056
value = htons(value);
printf("after htons is %ld\n\n",value);
This prints 62465
Please suggest what could be the reason?
I guess your input is 500, isn't it?
500 is 2**8+2**7+2**6+2**5+2**4+2**2 or 0x00 0x00 0x01 0xF4 in little endian order.
TCP/IP uses big endian. So after the htonl, the sequence is 0xF4 0x01 0x00 0x00.
If you print it as signed integer, since the first digit is 1, it is negative then. Negative numbers are regarded as complement, The value is -(2**27 + 2**25+2**24+2**23+2**22+2**21+2**20+2**19+2**18+2**17+2**16) == -201261056
Host Order is the order which your machine understands the data (assuming your machine is little endian) correctly. Network Order is Big Endian, which cannot be understood by your system properly. This is the reason for your so called garbage values.
So, basically, there is nothing with the code. : )
Google "Endianness" to get all the details about Big Endian and Little Endian.
Providing some more info, In Big endian, first byte or lowest address will have the most significant byte and in little endian, at the same place, the least significant byte will be present. So, when you use htonl, your first byte will now contain the most significant byte, but your system will think it is as the least significant byte.
Considering the wikipedia example of decimal 1000 (hex 3E8) in big endian will be 03 E8 and in little endian will be E8 03. Now, if you pass 03 E8 to a little machine, it will consider to be decimal 59395.
htonl() and htons() are functions which is used to convert the data from host's endianess to networks endiness.
Network uses big-endian. so if your system is X86, then it is little-endian.
Host to Network byte order(long data) is htonl(). i.e converts 32bit value to network byte order.
Host to Network byte order(short data) is htons(). i.e converts 16bit value to network byte order.
sample program to show how htonl() works as well as effect of using 32bit value in htons() function.
#include <stdio.h>
#include <arpa/inet.h>
int main()
{
long data = 0x12345678;
printf("\n After htonl():0x%x , 0x%x\n", htonl(data), htons(data));
return 0;
}
It will print After htonl():0x78563412 , 0x7856 on X86_64.
Reference:
http://en.wikipedia.org/wiki/Endianess
http://msdn.microsoft.com/en-us/library/windows/desktop/ms738557%28v=vs.85%29.aspx
http://msdn.microsoft.com/en-us/library/windows/desktop/ms738556%28v=vs.85%29.aspx
#halfelf>I jut want to put my findings.I just tried the below program with the same
value 500.I guess you have mistakenely mentioned output of LE as BE and vice versa
Actual output i got is 0xf4 0x01 0x00 0x00 as little Endian format.My Machine is
LE
#include <stdio.h>
#include <netinet/in.h>
/* function to show bytes in memory, from location start to start+n*/
void show_mem_rep(char *start, int n)
{
int i;
for (i = 0; i < n; i++)
printf(" %.2x-%p", start[i],start+i);
printf("\n");
}
/*Main function to call above function for 0x01234567*/
int main()
{
int i = 500;//0x01234567;
int y=htonl(i); --->(1)
printf("i--%d , y---%d,ntohl(y):%d\n",i,y,ntohl(ntohl(y)));
printf("_------LITTLE ENDIAN-------\n");
show_mem_rep((char *)&i, sizeof(i));
printf("-----BIG ENDIAN-----\n");/* i just used int y=htonl(i)(-1) for reversing
500 ,so that
i can print as if am using a BE machine. */
show_mem_rep((char *)&y, sizeof(i));
getchar();
return 0;
}
output is
i--500 , y----201261056,ntohl(y):-201261056
_------LITTLE ENDIAN-------
f4-0xbfda8f9c 01-0xbfda8f9d 00-0xbfda8f9e 00-0xbfda8f9f
-----BIG ENDIAN-----
00-0xbfda8f98 00-0xbfda8f99 01-0xbfda8f9a f4-0xbfda8f9b

Resources