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

4

我知道ntoh{s,l}hton{s,l},它们适用于2和4字节的整数。现在,我遇到了一个问题,需要翻译一个长度为16字节的IPv6地址。

是否有一个现成的函数可以解决这个问题?

谢谢, Jir

4个回答

7
我不确定在IPv6中是否有关于ntohhton的相关内容。你没有本地的128位类型,对吧?
根据http://www.mail-archive.com/users@ipv6.org/msg00195.html的说法:
IPv6地址预计在二进制形式下(在网络上、主机上、路由器上等)以网络字节顺序表示。请参见RFC 2553第3.2节。
来自RFC 2553

3.2 IPv6 Address Structure

A new in6_addr structure holds a single IPv6 address and is defined as a result of including :

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

This data structure contains an array of sixteen 8-bit elements, which make up one 128-bit IPv6 address. The IPv6 address is stored in network byte order.

The structure in6_addr above is usually implemented with an embedded union with extra fields that force the desired alignment level in a manner similar to BSD implementations of "struct in_addr". Those additional implementation details are omitted here for simplicity.

An example is as follows:

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

警告:如果C预处理器(例如cpp)逐字替换每个s6_addr实例为_S6_un._S6_u8,则务必仅在正确的上下文中使用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 有一些需要注意的副作用。

是否有一种方法可以实现这个功能而不产生副作用?


这对所有的 #define 都是适用的;你应该始终非常谨慎地处理副作用。如果你需要在函数名中使用类型名称,或者你在其他情况下没有在完全预期的上下文中使用 #define,则必须了解后果。 - Pyrocater

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,并在从u6_addr16[]检索每个16位值时使用ntohs。

以下链接可能会有所帮助:


1
当我处理纯二进制形式的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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接