LINUX.ORG.RU

skb_header_pointer() всегда возвращает в big-endian?

 , , , ,


0

1

В ядре есть такая замечательная функция:

static inline void * __must_check
__skb_header_pointer(const struct sk_buff *skb, int offset,
             int len, void *data, int hlen, void *buffer)
{
    if (hlen - offset >= len)
        return data + offset;

    if (!skb ||
        skb_copy_bits(skb, offset, buffer, len) < 0)
        return NULL;

    return buffer;
}

static inline void * __must_check
skb_header_pointer(const struct sk_buff *skb, int offset, int len, void *buffer)
{
    return __skb_header_pointer(skb, offset, len, skb->data,
                    skb_headlen(skb), buffer);
}

То есть skb_header_pointer() позволяет получить указатель на буфер длиной len на любую часть сетевого пакета в skb.

Вопрос — всегда ли гарантируется, что любые 16- или 32-битные фрагенты пакета будут в big-endian формате?

Например, можно ли делать так:

__be32 *ptr, data;
ptr = skb_header_pointer(skb, offset, sizeof(u32), &data);

Он возвращает указатель, а что лежит в пакете, это не его дело. Поля сетевых протоколов (допустим tcp) идут в network byte order, то есть в big-endian, но если в пакете было записано число в little-endian, то оно так и останется.

mky ★★★★★ ()
Ответ на: комментарий от mky

Он возвращает указатель, а что лежит в пакете, это не его дело.

Не уверен, что это всегда так, например посмотрите вот этот код:

__be32 __skb_flow_get_ports(const struct sk_buff *skb, int thoff, u8 ip_proto,
			    void *data, int hlen)
{
	int poff = proto_ports_offset(ip_proto);

	if (!data) {
		data = skb->data;
		hlen = skb_headlen(skb);
	}

	if (poff >= 0) {
		__be32 *ports, _ports;

		ports = __skb_header_pointer(skb, thoff + poff,
					     sizeof(_ports), data, hlen, &_ports);
		if (ports)
			return *ports;
	}

	return 0;
}

Здесь явно аннотируется, что мы ожидаем 4 байта в сетевом формате представления данных.

Поля сетевых протоколов (допустим tcp) идут в network byte order, то есть в big-endian, но если в пакете было записано число в little-endian, то оно так и останется.

Наверное вопрос был в другом - весь ли пакет передается в big-endian, или только его отдельные «компоненты». Я так понимаю, что только часть полей в IP/TCP/UDP/ICMP/etc. заголовках и это явным образом «аннотировано» в соответствующих структурах, например:

struct iphdr {
#if defined(__LITTLE_ENDIAN_BITFIELD)
        __u8    ihl:4,
                version:4;
#elif defined (__BIG_ENDIAN_BITFIELD)
        __u8    version:4,
                ihl:4;
#else
#error  "Please fix <asm/byteorder.h>"
#endif
        __u8    tos;
        __be16  tot_len;
        __be16  id;
        __be16  frag_off;
        __u8    ttl;
        __u8    protocol;
        __sum16 check;
        __be32  saddr;
        __be32  daddr;
        /*The options start here. */
};

То есть, как я понимаю, нельзя ожидать что любой произволный 16-битный или 32-битный кусок пакета будет в big-endian виде.

cruz7 ()
Ответ на: комментарий от cruz7

Все поля, относящиеся к протоколу ip/tcp/udp и т.д. в big-endian (в соответствии с RFC), а внутри пакета что угодно. Допустим, на машине с little-endian приложение открыло tcp-сокет и пишет туда 4-байтовые int. В tcp-пакетах в поле данных будет le32.

mky ★★★★★ ()
Ответ на: комментарий от mky

То есть только гарантируется что протоколы сетевого и транспортного уровня передают данные в big-endian формате, все что выше уровнем, например, snmp/ftp, передает в чем захочет?

cruz7 ()
Ответ на: комментарий от cruz7

Если у протокола в спецификации описан порядок байт, то передаёт как указано. Для snmp, для чисел (счётчиков) 32/64 бит, ЕМНИП, определна передача в network byte order. ftp он вобще текстовы, а файл там передаётся как есть. Если на одной машине в файл написали двоичные данные в little indian, то они так и будут переданы и это проблемы машины с big indian, как потом этот файл прочитать правильно.

mky ★★★★★ ()