开发者

How can I change the byte order (from network to host, and vice versa) of an IPV6 address?

开发者 https://www.devze.com 2023-02-20 09:18 出处:网络
I am aware of ntoh{s,l} and hton{s,l}, which work on integers of 2 and 4 bytes. Now, I am facing the problem to translate an IPv6 address, which is 16 bytes long.

I am aware of ntoh{s,l} and hton{s,l}, which work on integers of 2 and 4 bytes. Now, I am facing the problem to translate an IPv6 address, which is 16 bytes long.

Is there a ready-made function for that pur开发者_开发知识库pose?

TIA, Jir


I'm not sure that ntoh and hton are relevant in IPv6. You don't have a native 128-bit type, do you?

According to http://www.mail-archive.com/users@ipv6.org/msg00195.html:

IPv6 addresses are expected to be representd in network byte order whenever they are in binary form (on the wire, on a host, in a router, etc). Among other places, see RFC 2553, section 3.2.

From 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


Warning: If the C pre-processor (eg, cpp) is replacing every instance of s6_addr with _S6_un._S6_u8, verbatim, then be sure to only use the value of s6_addr in proper context. For example, you mustn't name a function, datatype, or prototype as 's6_addr' because 'void *s6_addr();' would produce 'void *_S6_un._S6_u8();', which is invalid syntax.

Notice that this code does not compile:

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();

So that #define has some side effects to be aware of.

Is there a way to implement this without the side effects?


IPv6 does require network order for ipv6 addresses. hton and ntoh are all about converting the address from how you have it stored in your code, to how it needs to be stored in the packet (and vice-versa). So the issue becomes how you have it stored in your code.

Also the definition of an IPv6 address in code can allow more ways to address it than just an array of bytes:

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 addresses, to the user, are represented as 8 16-bit values. If you have the address stored as 8 16-bit values in your code, then you will need to use htons on each 16 bit value as you place it into the packet using the u6_addr16[] array, and use ntohs as you retrieve each 16-bit value from u6_addr16[].

These links are helpful:

  • http://msdn.microsoft.com/en-us/library/ee175867.aspx
  • http://en.wikipedia.org/wiki/IPv6_address (especially the diagram at top right)


When I am dealing with IPv6 addresses in a pure binary form I use something like this:

// 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
0

精彩评论

暂无评论...
验证码 换一张
取 消