Я знаю ntoh{s,l}
и hton{s,l}
, которые работают с целыми числами размером 2 и 4 байта. Теперь я столкнулся с проблемой перевода IPv6-адреса длиной 16 байт.
Есть ли готовая функция для этой цели?
ТИА, Жир
Я знаю ntoh{s,l}
и hton{s,l}
, которые работают с целыми числами размером 2 и 4 байта. Теперь я столкнулся с проблемой перевода IPv6-адреса длиной 16 байт.
Есть ли готовая функция для этой цели?
ТИА, Жир
Я не уверен, что ntoh
и hton
актуальны для IPv6. У вас нет собственного 128-битного типа, не так ли?
Согласно http://www.mail-archive.com/[email protected]/msg00195. HTMLа>:
Ожидается, что адреса IPv6 будут представлены в сетевом порядке байтов всякий раз, когда они представлены в двоичной форме (в проводной сети, на хосте, в маршрутизаторе и т. д.). Среди прочего, см. RFC 2553, раздел 3.2.
Из RFC 2553:
3.2 Структура адреса IPv6
Новая структура in6_addr содержит один адрес IPv6 и определяется в результате включения:
struct in6_addr { uint8_t s6_addr[16]; /* IPv6 address */ };
Эта структура данных содержит массив из шестнадцати 8-битных элементов, составляющих один 128-битный IPv6-адрес. Адрес IPv6 хранится в сетевом порядке байтов.
Вышеупомянутая структура in6_addr обычно реализуется с помощью встроенного объединения с дополнительными полями, которые задают желаемый уровень выравнивания, аналогично реализациям BSD "struct in_addr". Эти дополнительные детали реализации здесь опущены для простоты.
Пример выглядит следующим образом:
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
Предупреждение: Если препроцессор 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 имеет некоторые побочные эффекты, о которых нужно знать.
Есть ли способ реализовать это без побочных эффектов?
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-битных значений, вам нужно будет использовать htons для каждого 16-битного значения, когда вы помещаете его в пакет с помощью массива u6_addr16[], и использовать ntohs для извлечения каждого 16-битного значения. -битное значение из u6_addr16[].
Эти ссылки полезны:
Когда я имею дело с адресами 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