Как я могу изменить порядок байтов (от сети к хосту и наоборот) адреса IPV6? - PullRequest
4 голосов
/ 29 марта 2011

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

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

ТИА, Jir

Ответы [ 4 ]

7 голосов
/ 29 марта 2011

Я не уверен, что ntoh и hton актуальны в IPv6. У вас нет собственного 128-битного типа?

По данным http://www.mail-archive.com/users@ipv6.org/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
1 голос
/ 13 января 2013

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

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

1 голос
/ 06 марта 2012

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

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

0 голосов
/ 09 мая 2017

Когда я имею дело с адресами 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
...