2014-12-04 128 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

相关问题