When to use ntohs and ntohl in C?

I am very confused when to use ntohs and ntohl. I know when you use ntohs for uint16_t and ntohl uint32_t. But what about those who have unsigned int or those where a certain number of bits are indicated (for example, u_int16_t doff: 4;).

Here is my working code to demonstrate the problem:

// Utility/Debugging method for dumping raw packet data void dump(const unsigned char *data, int length) { unsigned int i; static unsigned long pcount = 0; // Decode Packet Header struct ether_header *eth_header = (struct ether_header *) data; printf("\n\n === PACKET %ld HEADER ===\n", pcount); printf("\nSource MAC: "); for (i = 0; i < 6; ++i) { printf("%02x", eth_header->ether_shost[i]); if (i < 5) { printf(":"); } } printf("\nDestination MAC: "); unsigned short ethernet_type = ntohs(eth_header->ether_type); printf("\nType: %hu\n", ethernet_type); //Why not nthos? if (ethernet_type == ETHERTYPE_IP) { //IP Header printf("\n == IP HEADER ==\n"); struct ip *ip_hdr = (struct ip*) (data + sizeof(struct ether_header)); unsigned int size_ip = ip_hdr->ip_hl * 4; //why no nthos or nthol printf("\nip_hdr->ip_hl: %u", ip_hdr->ip_hl); //why no nthos or nthol printf("\nIP Version: %u", ip_hdr->ip_v); //why no nthos or nthol printf("\nHeader Length: %u", ip_hdr->ip_hl); //why no nthos or nthol printf("\nTotal Length: %hu", ntohs(ip_hdr->ip_len)); //?is this right? // TCP Header printf("\n== TCP HEADER ==\n"); struct tcphdr *tcp_hdr = (struct tcphdr*) (data + sizeof(struct ether_header) + size_ip); unsigned int size_tcp = tcp_hdr->doff * 4; //why no nthos or nthol printf("\n Source Port: %" PRIu16, ntohs(tcp_hdr->th_sport)); printf("\n Destination Port: %" PRIu16, ntohs(tcp_hdr->th_dport)); printf("\n fin: %" PRIu16, tcp_hdr->fin ); //As this is 1 bit, both nthos or nthol will work printf("\n urg: %" PRIu16, tcp_hdr->urg ); //As this is 1 bit, both nthos or nthol will work printf("\n ack_seq: %" PRIu32, ntohl(tcp_hdr->ack_seq)); u_int16_t sourcePort = ntohs(tcp_hdr->th_sport); u_int16_t destinationPort = ntohs(tcp_hdr->th_sport); if (sourcePort == 80 || destinationPort == 80){ printf("\n\nPORT 80!!!\n"); //Transport payload! printf("\n\ === TCP PAYLOAD DATA == \n"); // Decode Packet Data (Skipping over the header) unsigned int headers_size = ETH_HLEN + size_ip + size_tcp; unsigned int data_bytes = length - headers_size; const unsigned char *payload = data + headers_size; const static int output_sz = 500; // Output this many bytes at a time while (data_bytes > 0) { int output_bytes = data_bytes < output_sz ? data_bytes : output_sz; // Print data in raw hexadecimal form printf("| "); // Print data in ascii form for (i = 0; i < output_bytes; ++i) { char byte = payload[i]; if ( (byte > 31 && byte < 127) || byte == '\n') { // Byte is in printable ascii range printf("%c", byte); //why no nthos or nthol } else { printf("."); } } payload += output_bytes; data_bytes -= output_bytes; } } } pcount++; } 

As you can see, sometimes I use ntohs / ntohl, and sometimes I do not use any of them. I do not understand when to use.

+3
source share
2 answers

But what about those with unsigned int

Basically, as noted, C does not guarantee the size of an unsigned int ; there were platforms on which int and unsigned int were 16-bit, such as PDP-11, and Motorola 68k processors with some compilers (other compilers made them 32-bit), and it could still be some 16-bit microprocessors.

So, if you send data by wire, it is better to use the types defined in <stdint.h> , if available.

In practice, the machines you use will almost certainly have a 32-bit unsigned int , although some Cray machines have a 64-bit int and even short ! But it’s better to use the types defined in <stdint.h> .

or those where a certain number of bits are indicated (for example, u_int16_t doff: 4;).

If the value is shorter than the byte, as it would be for a 4-bit field, the byte order does not matter.

However, note that the order of the bit fields in a sequence of 1, 2, or 4 bytes is also not specified by C, so you should not use bit fields in the data sent over the wire. (Yes, some UN * Xes use them in structures for IPv4 and TCP headers, but this only works if the compilers that the provider uses for architectures that support all the set bit fields in the same order, and if third-party compilers, such as GCC do the same.)

So the right way to handle the IPv4 header is to do something like

 struct ip { uint8_t ip_vhl; /* header length, version */ #define IP_V(ip) (((ip)->ip_vhl & 0xf0) >> 4) #define IP_HL(ip) ((ip)->ip_vhl & 0x0f) uint8_t ip_tos; /* type of service */ uint16_t ip_len; /* total length */ uint16_t ip_id; /* identification */ uint16_t ip_off; /* fragment offset field */ #define IP_DF 0x4000 /* dont fragment flag */ #define IP_MF 0x2000 /* more fragments flag */ #define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ uint8_t ip_ttl; /* time to live */ uint8_t ip_p; /* protocol */ uint16_t ip_sum; /* checksum */ struct in_addr ip_src,ip_dst; /* source and dest address */ }; 

use this structure to declare the ip_hdr pointer for the IP header and:

  • to extract the version, use IP_V(ip_hdr) ;
  • to retrieve the length of the header, use IP_HL(ip_hdr) .

If your ip.h provider ip.h uses bit fields, do not use your provider ip.h header; use your own caption. In fact, even if the header of your ip.h provider ip.h not use bit fields, do not use the ip.h header of your provider; use your own caption. This is not the case if the definition of the IP header is OS dependent, because ....

(What tcpdump has done for several releases now, the above is taken from his ip.h )

+4
source

The ntohX and htonX are designed to create device-independent protocols, presumably for network communications, but other purposes are possible. Such protocols must be accurate with respect to the layout of the packet, including the size of each element sent or received in a device-independent manner.

Since the C standard does not specify the size of an unsigned int , this type cannot be used for data exchange in a device-independent protocol. All items you exchange must have a certain size. Instead, use the types defined in <stdint.h> .

Bit fields, on the other hand, should be considered in a completely different way. Your code must convert them to one of the defined standard sizes, and then place this type on the wire in a device-independent way (i.e. with the htonX function). When the bit field size is less than eight, translate it to uint8_t and install the wire without conversion.

+2
source

Source: https://habr.com/ru/post/1208358/


All Articles