2014-12-04 67 views
3

我很困惑什麼時候使用ntohs和ntohl。我知道你什麼時候用unt16_t和ntohl uint32_t使用ntohs。但那些unsigned int或那些指定了特定位數的函數(例如u_int16_t doff:4;)呢?什麼時候在C中使用ntohs和ntohl?

這是我工作的代碼在於向世人證明問題:

// 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++; 
} 

正如你可以看到有次我用ntohs和/和再用ntohl有次我都不使用。我不明白何時使用哪個。

回答

4

但對於那些與unsigned int類型

原則,如上所述,C是沒有的unsigned int大小的保障;有一些平臺,其中intunsigned int是16位的,例如PDP-11,以及帶有一些編譯器(其他編譯器使它們爲32位)的摩托羅拉68k處理器,並且對於某些16位微處理器。

因此,如果您通過線路發送數據,最好使用<stdint.h>中定義的類型(如果有)。

實際上,您使用的機器幾乎肯定會有32位unsigned int,although some Cray machines have 64-bit int and even short!但最好使用<stdint.h>中定義的類型。

或其中指定了特定位數的那些位(例如u_int16_t doff:4;)。

如果一個值比一個字節短,就像4位字段的情況那樣,字節順序是不相關的。

然而,請注意,比特字段的順序的1,2或4個字節的序列是不被C指定的,因此不應該在發送的數據用位字段線。 (是的,一些UN * Xes碰巧在IPv4和TCP頭結構中使用它們,但只有在供應商使用它們支持的體系結構的編譯器纔會將位字段按相同順序放置時纔有效,如果第三方GCC等編譯器也做同樣的事情。)

所以處理IPv4報頭的正確方法是做一些諸如

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 */ 
}; 

使用結構的ip_hdr指針聲明IP報頭,並且:

  • 提取版本,使用IP_V(ip_hdr);
  • 提取標題長度,使用IP_HL(ip_hdr)

如果您的供應商的ip.h標頭使用位域,請勿使用供應商的ip.h標頭;使用您自己的標題。實際上,即使您的供應商的ip.h標頭沒有使用位域,也不要使用您的供應商的ip.h標頭;使用您自己的標題。這並不是因爲如果IP報頭的定義是依賴於操作系統的,畢竟....

(這就是tcpdump的有現在幾個版本完成;上述從其ip.h拍攝。)

2

ntohXhtonX函數旨在幫助您構建與硬件無關的協議,可能用於通過網絡進行通信,但其他用途也是可以的。這些協議應該精確地關於數據包的佈局,包括以硬件無關的方式發送或接收的每個元素的大小。

由於C標準未指定unsigned int的大小,因此該類型不能用於硬件無關協議中的數據交換。您交換的所有元素都需要具有特定的大小。改用<stdint.h>標題中定義的類型。

另一方面,位字段應該以完全不同的方式處理。您的代碼需要將它們轉換爲特定標準尺寸之一,然後以硬件無關的方式將該類型置於電線上(即使用htonX函數)。當位字段的大小小於8時,將其轉換爲uint8_t,並將其轉換爲未經轉換的電線。

+0

等什麼將知道變量類型是「unsigned int ip_v:4;」將是讀取ip_hdr-> ip_v的「正確」方式。我沒有使用ntohs或ntohl。我只是把它保留下來,並且工作。 (注意:我只讀數據,不發送) – 2014-12-04 17:43:25

+1

@YahyaUddin如果包含爲ip_v指定的四位的字節將高四位設置爲零,它將按原樣工作。如果你不確定,用'0x0F'來屏蔽這個值來切斷高位:'ip_hdr-> ip_v = 0x0F&ip_v_byte;' – dasblinkenlight 2014-12-04 17:50:39

相關問題