【问题标题】:When to use ntohs and ntohl in C?何时在 C 中使用 ntohs 和 ntohl?
【发布时间】:2015-02-02 15:55:41
【问题描述】:

我很困惑何时使用 ntohs 和 ntohl。我知道您何时将 ntohs 用于 uint16_t 和 ntohl uint32_t。但是那些具有 unsigned int 或指定了特定位数的那些(例如 u_int16_t doff:4;)呢?

这是我演示该问题的工作代码:

// 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,有时我都不使用。我不明白什么时候使用。

【问题讨论】:

    标签: c packet packet-capture packet-sniffers


    【解决方案1】:

    但是那些 unsigned int 的呢

    原则上,如前所述,C 不保证unsigned int 的大小;在某些平台上,intunsigned int 是 16 位的,例如 PDP-11,以及带有一些编译器的摩托罗拉 68k 处理器(其他编译器使它们成为 32 位),对于一些 16 位微处理器。

    因此,如果您通过网络发送数据,最好使用&lt;stdint.h&gt; 中定义的类型(如果可用)。

    实际上,您使用的机器几乎肯定会有 32 位的unsigned intalthough some Cray machines have 64-bit int and even short!,但最好还是使用&lt;stdint.h&gt; 中定义的类型。

    或那些指定了特定数量的位(例如 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 的大小,因此该类型不能用于硬件无关协议中的数据交换。您交换的所有元素都需要具有特定的大小。请改用&lt;stdint.h&gt; 标头中定义的类型。

      另一方面,位字段应该以完全不同的方式处理。您的代码需要将它们转换为特定标准尺寸之一,然后以与硬件无关的方式将该类型放在线路上(即使用htonX 函数)。当位域的大小小于8时,将其强制转换为uint8_t,无需转换即可上线。

      【讨论】:

      • 那么在知道变量类型是“unsigned int ip_v:4;”的情况下,读取 ip_hdr->ip_v 的“正确”方式是什么。我没有使用 ntohs 或 ntohl。我只是让它保持原样并且它起作用了。 (注意:我只是在读取数据。不发送)
      • @YahyaUddin 如果包含为ip_v 指定的四位字节的高四位设置为零,它将按原样工作。如果不确定,请使用0x0F 屏蔽该值以切断高位:ip_hdr-&gt;ip_v = 0x0F &amp; ip_v_byte;
      猜你喜欢
      • 2011-01-26
      • 2011-11-22
      • 1970-01-01
      • 2019-07-23
      • 2010-10-23
      • 2014-07-20
      • 2017-02-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多