LINUX.ORG.RU

Raw Ethernet Пакеты

 ,


3

3

Генерирую Ethernet пакеты со своими MAC-адресами, Ethertype'ом и данными. Пример взял со следующего сайта.

http://aschauf.landshut.org/fh/linux/udp_vs_raw/ch01s03.html

Максимальный размер пакета не превышает 1500 байт. Могу ли я как-нибудь увеличить это число? Желательно не менее 10000 байт.

И да, нужно понимать что такой пакет не везде пролезет

redixin ★★★★
()

Максимум, что возможно — это TCP пакет размером 64K. Размер идёт в шапку IP.

Вопрос в другом — хочешь ли ты этого, т.к. с большими пакетами у тебя будет фрагментация.

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

То есть фактически максимальный размер пакета ограничен возможностями железа, так я понял? А фрагментацию поддерживает только IP протокол. Следовательно, если я буду передавать Ethernet пакеты большого размера, то они просто будут отбрасываться?

IvanAble
() автор топика
Ответ на: комментарий от Deleted

Пакеты я отправляю на ПЛИС по гигабитному Ethernet. Мне нужно отправлять более 10 МБайт данных в секунду. Однако я достиг максимальной скорости только лишь 5 МБайт в секунду. Основные потери в скорости, как я понял, происходят из-за задержки между пакетами, поэтому я хочу увеличить размер этих пакетов.

IvanAble
() автор топика

«По стандартам Ethernet максимальный размер ethernet-кадра составляет 1518 байт.»

«Jumbo‑кадр — ethernet‑кадр, в котором поле «payload» может занимать от 1500 байт до 16 000 байт.»

https://ru.wikipedia.org/wiki/Jumbo-кадр

У меня больше 9000 MTU установить не дает

ip link set eth7 mtu 9000

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

Основные потери в скорости, как я понял, происходят из-за задержки между пакетами

поэтому я хочу увеличить размер этих пакетов.

Чет ничего не понятно. Так откуда именно задержки? Из-за tcp? юзай Udp. Из-за плиса? Ты уверен что на парсинг содержимого тратится времени меньше чем на парсинг заголовков и т.д.?

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

У меня нет TCP и нет UDP, у меня есть чистый Ethernet фрейм - 6 байт MAC адрес назначения, 6 байт MAC адрес отправителя, 2 байта тип протокола и дальше данные. Парсинг содержимого у меня производится в реальном времени, так что тут задержки минимальные.

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

Пакеты я отправляю на ПЛИС по гигабитному Ethernet. Мне нужно отправлять более 10 МБайт данных в секунду. Однако я достиг максимальной скорости только лишь 5 МБайт в секунду. Основные потери в скорости, как я понял, происходят из-за задержки между пакетами, поэтому я хочу увеличить размер этих пакетов.

У тебя точно что-то сильно не так в ПЛИСине. Всего 4% утилизации - простыми задержками это не объяснить

P.S. А, ты наоборот на ПЛИС отправляешь через sendto. Тогда понятно, почему тормозит. Целый сискол на каждые 1500 байт.

Kosyak ★★★★
()
Последнее исправление: Kosyak (всего исправлений: 1)

На всякий случай. При использовании raw ethernet для передачи данных часто совершается ошибка, когда предполагают, что это так же надёжно как, например, usb. На самом деле буфер может переполниться и пакеты пропасть без ошибок(!). Это одна из причин использования TCP. Т.е. даже при использовании raw ethernet необходимо нумеровать пакеты и проверять в fpga.

Однако я достиг максимальной скорости только лишь 5 МБайт в секунду.

Да, тут и 100 mbit ethernet ещё имеет 50+% запаса по прочности. А ты попробуй подключиться сначала к другому ПК с 1 gbit ethernet, переписать файлик: убедиться, что скорость >100 MB. Сделай серверную часть для своей проги для этого второго ПК. Потом погоняй свою прогу, убедись, что потребление CPU не зашкаливает до 100%. И если этот тест не пройдёт, значит будешь знать, что дело не в fpga. А если твоя прога справится, значит fpga тормозит.

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

Тоесть если я буду пытаться слать пакеты через raw сокет быстрее чем может интерфейс, то пакеты которые не успевают будут дропаться, и я не получу никаких ошибок? Если да, то похоже что человек столкнулся именно с этим.

И еще вопрос: raw сокеты работают с select/epoll? Если да, то это норм решение.

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

Тоесть если я буду пытаться слать пакеты через raw сокет быстрее чем может интерфейс, то пакеты которые не успевают будут дропаться, и я не получу никаких ошибок?

Да тут raw socket в частности не причём. Ведь можно и udp пакеты терять. Фокус в том, что ethernet в общём - это не usb и не pci-e. Целостность данных в пакете проверяется, но не целостность доставки пакетов друг за дружкой.

И еще вопрос: raw сокеты работают с select/epoll? Если да, то это норм решение.

Ну, мы в linux'е - значит нам всё равно, com-порт ли это, udp-сокет, unix domain socket, или raw-socket. Если есть файловый дескриптор (а он есть для raw сокета), то функции его использующие должны работать. Хотя, и в linux'е есть всё ещё устаревшее отличие эмулятора терминала от фреймбуфера (которое уже давно преодолели в Plan 9). Туда же отсутствие /dev/eth0. Да и список можно, к сожалению, продолжать.

gag ★★★★★
()

google://jumbo frame

умеет далеко не всё оборудование, если передавать через Интернет - упрешься в фрагментацию как минимум на пакеты по 1500 байт(как минимум - потому что всякие ADSL-и и прочие инкапсуляции типа PPPoE и GRE добавляют своего треша и MTU может быть еще меньше).

А так в IPv6, например, джамбограммы могут быть по (4 Гб-1 байт). TCP и UDP ходить там смогут(я имею ввиду с учётом большого размера а не просто впритык по 64 кб), если их реализации в ОС поддерживают RFC 2675

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

Я говорю не о контроле доставки, а о контроле отправки. Я получу ошибку сли пакет вообще не попал на провод?

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

Вот, что есть в доках:

errno == ENOBUFS
The output queue for a network interface was full. This generally indicates that the interface has stopped sending, but may be caused by transient congestion. (Normally, this does not occur in Linux. Packets are just silently dropped when a device queue overflows.)

Хотя железо, как правило, и сообщает о переполнении, хотя очередь в ядре естественно тоже сообщает о переполнении, пакеты в линуксе всё равно тихо удаляются. Интересно, почему так решили.

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

P.S. А, ты наоборот на ПЛИС отправляешь через sendto. Тогда понятно, почему тормозит. Целый сискол на каждые 1500 байт.

Я не видел, чтобы ТС упомянул sendto. Но если выяснится, что проблема в этом, то решение есть: sendmmsg: отправляет несколько пакетов за один системный вызов.

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

Получается у тс одна надежда на селект, который поможет понять когда можно отправлять очередной пакет. А может такое быть, что селект считает что в raw сокет можно отправлять всегда?

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

Если пакеты будут дропаться, то это должно быть видно тут

$ ifconfig eth7|grep drop
          RX packets:200 errors:0 dropped:0 overruns:0 frame:0
          TX packets:171 errors:0 dropped:0 overruns:0 carrier:0
alx777 ★★
()
Ответ на: комментарий от I-Love-Microsoft

У разных карточек - разный аппаратный лимит

Это понятно, в вики пишут что больше 9000 не рекомендуется из-за CRC32

Кстати, насколько Jumbo frame сейчас поддерживается современным железом ? Раньше с этим было много проблем

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

Не знаю что у него за девайс, но даже на ПЛИС сделать простой парсер UDP не сложно. Равно как и в микроконтроллере можно очень просто сделать упрощенный быстрый парсер UDP чтобы избежать сложной обработки, которая может понизить скорость...

I-Love-Microsoft ★★★★★
()
Ответ на: комментарий от gag

Попробовал подключиться к другому ПК с гигабитным Ethernet. Один ПК в цикле передаёт пакеты, второй принимает их. Загрузка сети не поменялась - 4.5% от гигабита. Загрузка зависит от длины пакета. При максимуме (1500 байт) получаю порядка 6%. Причём на принимающем ПК считаю количество пакетов, которые я получил - примерно 2% пакетов дропаются.

IvanAble
() автор топика

Двумя wireshark-ами на двух концах смотрел как сетевуха пережевывает пакеты и немного офигел от того, что пакет может застрять в сетевухе или драйвере на пол милисекунды. Помогло вот это: http://blog.serverfault.com/2011/03/23/performance-tuning-intel-nics/ Это было в оффтопике, хотя в онтопике тоже, думаю, где-то должно быть.

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

Если железка пришлет UDP пакет со своим произвольным MAC и IP - то компьютер ее не зохавает? Linux (допустим такая ОС на хосте) не отметит себе эту связь? Либо я что-то не понимаю, но у меня работает... А один пакет броадкастом?

Хотя ты прав, в своих экспериментах я сразу подсовывал в пакет MAC компьютера, я жулик.

I-Love-Microsoft ★★★★★
()
Последнее исправление: I-Love-Microsoft (всего исправлений: 2)
Ответ на: комментарий от redixin

Ага, а еще нужно сделать простую реализацию ARP

Не обязательно. Можно использовать статический ARP при отправке UDP. А если там вовсе не IP, то ARP и не нужен вовсе.

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

Может, сделать отправку пакетов из модуля ядра?

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

Чтобы забить 100 Мбит сеть (эти самые ваши 10 Мегабайт/с) было достаточно первого пня. Вы точно цикл с коде в нужном месте организовали, только вокруг sendto()?

2% пакетов дропаются

Счётчики ошибок у интерфейса растут?

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

Потери возможно связаны с медленной обработкой на принимающей стороне.

Если же хотите максимально эффективно отправлять много сырых пакетов, то можно использовать af_packet.

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

Сейчас попробовал просто подключить ethernet-кабель к другому ПК и слать пакеты:

# ./send_raw_eth 10 1500
rate: 63.769881 MB/s
# ./send_raw_eth 100 1500
rate: 70.903853 MB/s
# ./send_raw_eth 1000 1500
rate: 77.395538 MB/s
# ./send_raw_eth 10000 1500
rate: 102.559829 MB/s
# ./send_raw_eth 100000 1500
rate: 113.186102 MB/s
# ./send_raw_eth 1000000 1500
rate: 114.981848 MB/s
Тупо sendto(). Собрано gcc 5.3 без оптимизаций.

И всё чисто:

# ifconfig
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
На принимающей стороне ifconfig показывает ожидаемое кол-во принятых пакетов и никаких дропов или ошибок. Прогу на принимающей стороне пока не пробовал. Но, думаю, это уже и не нужно.

Так что нужно разбираться, что у тебя за ПК, что за glibc, что за ядро и т.д.

gag ★★★★★
()
Последнее исправление: gag (всего исправлений: 1)
Ответ на: комментарий от redixin

ARP какую задачу решает и зачем? Так вот это можно немного обойти.

I-Love-Microsoft ★★★★★
()
Ответ на: комментарий от IvanAble

Да, вроде, не в коде дело, т.к. там ничего особенного. В своё время руководствовался этим.

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>

#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <linux/if_packet.h>
#include <net/if.h>
#include <netinet/ether.h>

#define DEF_IF "eth0"
#define BUF_SIZE 1500
 
int main(int argc, char *argv[])
{
        int sockfd;
        struct ifreq if_idx;
        struct ifreq if_mac;
        int tx_len;
        unsigned char buf[BUF_SIZE];
        struct ether_header *eh = (struct ether_header *) buf;
        struct sockaddr_ll socket_address;
        char if_name[IFNAMSIZ];
        int i, j, pkt_size, count;
        ssize_t ret, sent_bytes;
        struct timespec start_time, stop_time;
        time_t sec;
        long nsec;

        if (argc != 3) {
                printf("Usage: %s <packet count> <packet size>\n", argv[0]);
                printf("network interface: %s\n", DEF_IF);
                printf("minimal packet size = 14 (ethernet header length [bytes]: 6 = dest MAC, 6 = source MAC, 2 = type)\n");
                return 1;
        }
                
        count = atoi(argv[1]);
        pkt_size = atoi(argv[2]);
        if (pkt_size < 14) {
                pkt_size = 14;
                printf("packet size forced to be minimal 14\n");
        }
        strcpy(if_name, DEF_IF);
        printf("%s: count=%d, pkt_size=%d\n", if_name, count, pkt_size);

        /* Open RAW socket to send on */
        if ((sockfd = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW)) == -1) {
                perror("socket() failed");
                return 1;
        }
 
        /* Get the index of the interface to send on */
        memset(&if_idx, 0, sizeof(struct ifreq));
        strncpy(if_idx.ifr_name, if_name, IFNAMSIZ-1);
        if (ioctl(sockfd, SIOCGIFINDEX, &if_idx) < 0) {
                perror("ioctl SIOCGIFINDEX for sockfs failed");
                return 1;
        }
        /* Get the MAC address of the interface to send on */
        memset(&if_mac, 0, sizeof(struct ifreq));
        strncpy(if_mac.ifr_name, if_name, IFNAMSIZ-1);
        if (ioctl(sockfd, SIOCGIFHWADDR, &if_mac) < 0) {
                perror("ioctl SIOCGIFHWADDR for sockfd failed");
                return 1;
        }
 
        /* Ethernet header */
        /* Destination MAC address */
        eh->ether_dhost[0] = 0x01;
        eh->ether_dhost[1] = 0x02;
        eh->ether_dhost[2] = 0x03;
        eh->ether_dhost[3] = 0x04;
        eh->ether_dhost[4] = 0x05;
        eh->ether_dhost[5] = 0x06;
        /* Source MAC address */
        eh->ether_shost[0] = ((uint8_t *)&if_mac.ifr_hwaddr.sa_data)[0];
        eh->ether_shost[1] = ((uint8_t *)&if_mac.ifr_hwaddr.sa_data)[1];
        eh->ether_shost[2] = ((uint8_t *)&if_mac.ifr_hwaddr.sa_data)[2];
        eh->ether_shost[3] = ((uint8_t *)&if_mac.ifr_hwaddr.sa_data)[3];
        eh->ether_shost[4] = ((uint8_t *)&if_mac.ifr_hwaddr.sa_data)[4];
        eh->ether_shost[5] = ((uint8_t *)&if_mac.ifr_hwaddr.sa_data)[5];
        /* Ethertype field */
        //eh->ether_type = htons(ETH_P_IP);
        eh->ether_type = 0x0D0E;

        memset(&socket_address, 0, sizeof(socket_address));
        /* Index of the network device */
        socket_address.sll_ifindex = if_idx.ifr_ifindex;
        /* Following will be ignored, the data from buf will be used instead */
        /* Address length */
        /* socket_address.sll_halen = ETH_ALEN; */
        /* Destination MAC */
        /* socket_address.sll_addr[0] = 0x07; */
        /* socket_address.sll_addr[1] = 0x08; */
        /* socket_address.sll_addr[2] = 0x09; */
        /* socket_address.sll_addr[3] = 0x10; */
        /* socket_address.sll_addr[4] = 0x1A; */
        /* socket_address.sll_addr[5] = 0x1B; */
 
        tx_len = sizeof(struct ether_header) + pkt_size-14;
        sent_bytes = 0;
        clock_gettime(CLOCK_MONOTONIC, &start_time);
        for (i = 0; i < count; i++) {
                for (j = 0; j < pkt_size-14; j++)
                        buf[14+j] = (j+i)%256;
                ret = sendto(sockfd, buf, tx_len, 0, (struct sockaddr*)&socket_address, sizeof(struct sockaddr_ll));
                /* ret = send(sockfd, buf, tx_len, 0); */ /* wouldn't work, since not clear what network interface to use */
                if (ret < 0) {
                        perror("sendto() failed");
                        return 1;
                }
                sent_bytes += ret;
        }
        clock_gettime(CLOCK_MONOTONIC, &stop_time);
        sec = stop_time.tv_sec - start_time.tv_sec;
        nsec = stop_time.tv_nsec - start_time.tv_nsec;
        if ( nsec < 0) {
                sec -= 1;
                nsec += 1e9;
        }
        printf("rate: %f MB/s\n", sent_bytes/(sec+(double)nsec/1e9)/1024/1024);
 
        return 0;
}
Ну, как: нашёл принципиальное отличие?

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

Тогда понятно, почему тормозит. Целый сискол на каждые 1500 байт.

Теперь совершенно непонятно: у меня не тормозит (где-то 74000 сисколлов в секунду), но загружает проц. Кстати, если бы тормозило по этой причине, то ни mc, ни rsync невозможно было бы пользоваться, т.к. они используют буферы уж очень маленького размера: 4-8 KB. Избыточная нагрузка на проц (по сравнению с cp) хорошо заметна в top.

gag ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.