如何更改IPV6地址的字节顺序(从网络到主机,反之亦然)?

时间:2011-03-29 16:17:43

标签: ipv6 endianness

我知道ntoh{s,l}hton{s,l},它们对2和4字节的整数有效。 现在,我面临着翻译IPv6地址的问题,该地址长度为16个字节。

是否有为此目的的现成功能?

TIA, JIR

4 个答案:

答案 0 :(得分:7)

我不确定ntohhton是否与IPv6相关。您没有本机128位类型,是吗?

根据http://www.mail-archive.com/users@ipv6.org/msg00195.html

  

预计会有IPv6地址   以网络字节顺序表示   无论何时它们都是二进制形式(on   电线,主机,路由器,   等等)。在其他地方,请参阅RFC   2553,第3.2节。

来自RFC 2553

  

3.2 IPv6地址结构

     

新的in6_addr结构包含单个IPv6地址,并且定义为包含以下内容的结果:

struct in6_addr {
    uint8_t  s6_addr[16];      /* IPv6 address */
};
     

此数据结构包含16个8位元素的数组,这些元素组成一个128位IPv6地址。 IPv6地址以网络字节顺序存储。

     

上面的结构in6_addr通常使用带有额外字段的嵌入式并集来实现,这些字段以类似于“struct in_addr”的BSD实现的方式强制所需的对齐级别。为简单起见,这里省略了这些额外的实现细节。

     

一个例子如下:

struct in6_addr {
    union {
        uint8_t  _S6_u8[16];
        uint32_t _S6_u32[4];
        uint64_t _S6_u64[2];
    } _S6_un;
};
#define s6_addr _S6_un._S6_u8

答案 1 :(得分:1)

警告:如果C预处理器(例如,cpp)正在用_S6_un._S6_u8逐字替换s6_addr的每个实例,那么请确保仅在适当的上下文中使用s6_addr的值。例如,您不能将函数,数据类型或原型命名为's6_addr',因为'void * s6_addr();'会产生'void * _S6_un._S6_u8();',这是无效的语法。

请注意,此代码无法编译:

struct in6_addr {
    union {
        unsigned char _S6_u8[16];
        unsigned long _S6_u32[4];
        unsigned long long _S6_u64[2];
    } _S6_un;
};
#define s6_addr _S6_un._S6_u8

void *s6_addr();

因此#define有一些副作用需要注意。

有没有办法在没有副作用的情况下实现这个?

答案 2 :(得分:1)

IPv6确实需要ipv6地址的网络顺序。 hton和ntoh都是关于将地址从存储在代码中的地址转换为如何将其存储在数据包中(反之亦然)。因此,问题就变成了如何将其存储在代码中。

此外,代码中IPv6地址的定义可以允许更多的方式来解决它而不仅仅是一个字节数组:

struct in6_addr
{
    union 
    {
        __u8 u6_addr8[16];
        __u16 u6_addr16[8];
        __u32 u6_addr32[4];
    } in6_u;
#define s6_addr in6_u.u6_addr8
#define s6_addr16 in6_u.u6_addr16
#define s6_addr32 in6_u.u6_addr32
};

用户的IPv6地址表示为8个16位值。如果在代码中将地址存储为8个16位值,则在使用u6_addr16 []数组将数据放入数据包时,需要在每个16位值上使用htons,并在检索每个数据时使用ntohs 16来自u6_addr16 []的位值。

这些链接很有帮助:

答案 3 :(得分:0)

当我以纯二进制形式处理IPv6地址时,我使用类似这样的东西:

// Compile with gcc
#include <x86intrin.h>
#include <stdint.h>
#include <arpa/inet.h>

// C99 supports __int128!
typedef unsigned __int128 uint128_t;

#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__

# define htonll(v) __builtin_bswap64((v))

  uint128_t hton128(uint128_t val)
  {
  // SSE2 is defined if SSSE3.
# if __SSSE3__
    // This routine is 100 cycles faster than the routine below.
    __m128i m;
    __m128i mask;

    m = _mm_loadu_si128((__m128i *)&val);

    // mask: 0x0f0e0d0c0b0a09080706050403020100
    mask = _mm_set_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);

    _mm_store_si128((__m128i *)&val, _mm_shuffle_epi8(m, mask));
# else
    // No SSSE3.. Slowest approach: use pointers to shuffle.

    uint64_t *p, *q;

    p = (uint64_t *)&val;
    q = p + 1;

    *p = htonll(*p);
    *q = htonll(*q);

    {
      uint64_t t;

      t = *p;
      *p = *q;
      *q = t;
    }
# endif

    return val;
  }
#endif