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

I know ntoh{s,l} and hton{s,l} that work with integers 2 and 4 bytes. Now I am faced with the problem of translating an IPv6 address, whose length is 16 bytes.

Is there a ready-made function for this purpose?

TIA, Jir

+4
source share
4 answers

I am not sure if ntoh and hton are related to 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 represent in network byte order when they are in binary form (by wire, on the host, in the router, etc.). Among other places, see RFC 2553, section 3.2.

From RFC 2553 :

3.2 IPv6 Address Structure

The new in6_addr structure contains one IPv6 address and is defined as the result of inclusion:

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

This data structure contains an array of sixteen 8-bit elements that make up a single 128-bit IPv6 address. The IPv6 address is stored in network order.

The in6_addr structure is typically implemented with a built-in union with additional fields that force the desired level of alignment in a manner similar to the BSD implementations for struct in_addr. These 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 
+7
source

A warning. If the C pre-processor (for example, cpp) replaces each instance of s6_addr _S6_un._S6_u8, verbatim, be sure to use the value s6_addr in the appropriate context. For example, you should not specify a function, data type or prototype as 's6_addr', because 'void * s6_addr ();' will generate 'void * _S6_un._S6_u8 ();', which is an invalid syntax.

Please note 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 #define has some side effects to be aware of.

Is there any way to implement this without side effects?

+1
source

IPv6 requires network order for ipv6 addresses. hton and ntoh are all about converting an address from how you saved it in your code, about how it should be stored in a package (and vice versa). So the problem is how you saved it in your code.

Also, defining an IPv6 address in code can allow more ways to resolve 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 for the user are represented as 8 16-bit values. If you have an address stored as 8 16-bit values ​​in your code, then you will need to use htons for each 16-bit value when you put it in the package using the u6_addr16 [] array, and use ntohs when you retrieve each of a 16-bit value from u6_addr16 [].

These links are useful:

+1
source

When I handle IPv6 addresses in 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
source

Source: https://habr.com/ru/post/1345753/


All Articles