Как я могу изменить порядок байтов (от сети к хосту и наоборот) адреса IPV6?

Я знаю ntoh{s,l} и hton{s,l}, которые работают с целыми числами размером 2 и 4 байта. Теперь я столкнулся с проблемой перевода IPv6-адреса длиной 16 байт.

Есть ли готовая функция для этой цели?

ТИА, Жир


person Jir    schedule 29.03.2011    source источник


Ответы (4)


Я не уверен, что 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
person M. Dudley    schedule 29.03.2011

Предупреждение: Если препроцессор 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 имеет некоторые побочные эффекты, о которых нужно знать.

Есть ли способ реализовать это без побочных эффектов?

person Eric Wheeler    schedule 06.03.2012
comment
Однако это верно для всех #define; Вы всегда должны быть очень осторожны с побочными эффектами. Если вам нужно имя типа в имени вашей функции или вы не используете #define в точном предполагаемом контексте, всегда помните о последствиях. - person Pyrocater; 05.06.2014

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[].

Эти ссылки полезны:

person mrbultitude    schedule 13.01.2013

Когда я имею дело с адресами 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
person Frederico Pissarra    schedule 09.05.2017