Инструменты пользователя

Инструменты сайта


net:channel_performance_measuring

Различия

Показаны различия между двумя версиями страницы.

Ссылка на это сравнение

Предыдущая версия справа и слеваПредыдущая версия
Следующая версия
Предыдущая версия
net:channel_performance_measuring [2022/02/28 18:19] – [Эксперимент №5 - делаем программу отправки трафика напрямую в сетевой интерфейс] sannet:channel_performance_measuring [2022/03/01 12:15] (текущий) – [Эксперимент №5 - делаем программу отправки трафика напрямую в сетевой интерфейс] san
Строка 1: Строка 1:
 ====== Измерение пропускной способности канала: флудим по полной ====== ====== Измерение пропускной способности канала: флудим по полной ======
  
-**Дано:** есть арендованный оптический канал со скоростью 2 гигабита по договору. С одной стороны этот канал можно воткнуть в сервер, другая сторона находится далеко и разместить там измерительное оборудование не представляется возможным. С удаленной стороны канал подключен к коммутатору с которого можно снять значения приходящего из канала трафика.+**Дано:** есть арендованный оптический канал со скоростью 2 гигабита по договору. С одной стороны этот канал можно воткнуть в сервер в котором есть 10G оптическая сетевуха, другая сторона находится далеко и разместить там измерительное оборудование не представляется возможным. С удаленной стороны канал подключен к коммутатору с которого можно снять значения приходящего из канала трафика.
  
 **Требуется:** проверить что обещанные 2 гигабита предоставляются арендованным каналом. **Требуется:** проверить что обещанные 2 гигабита предоставляются арендованным каналом.
Строка 22: Строка 22:
 <file bash> <file bash>
 bmon -p eth1 -o curses:ngraph=2,curses:details,curses:info bmon -p eth1 -o curses:ngraph=2,curses:details,curses:info
 +</file>
 +
 +<file console>
 + eth1                                                                                                                                                                                                      bmon 4.0 
 +Interfaces                     │ RX bps       pps     %│ TX bps       pps     %
 + >eth1                         │      0              │   1.03GiB 733.75K
 +───────────────────────────────┴───────────────────────┴────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
 +                            (RX Bytes/second)                                                                MiB                      (TX Bytes/second)
 +  133.00 ..................|.........................................                                      1090.03 |||||||||||||||||||||.......................................
 +  110.83 ..................|.........................................                                       908.36 |||||||||||||||||||||.......................................
 +   88.67 ..................|.........................................                                       726.68 |||||||||||||||||||||.......................................
 +   66.50 ..................|.........................................                                       545.01 ||||||||||||||||||||||......................................
 +   44.33 ..................|.........................................                                       363.34 ||||||||||||||||||||||......................................
 +   22.17 ..................|.........................................                                       181.67 ||||||||||||||||||||||......................................
 +             10   15   20   25   30   35   40   45   50   55   60                                              1     10   15   20   25   30   35   40   45   50   55   60
 +                             (RX Packets/second)                                                                                     (TX Packets/second)
 +    1.00 ..................|.........................................                                       761.92 |||||||||||||||||||||.......................................
 +    0.83 ..................|.........................................                                       634.93 |||||||||||||||||||||.......................................
 +    0.67 ..................|.........................................                                       507.94 |||||||||||||||||||||.......................................
 +    0.50 ..................|.........................................                                       380.96 ||||||||||||||||||||||......................................
 +    0.33 ..................|.........................................                                       253.97 ||||||||||||||||||||||......................................
 +    0.17 ..................|.........................................                                       126.99 ||||||||||||||||||||||......................................
 +             10   15   20   25   30   35   40   45   50   55   60                                              1     10   15   20   25   30   35   40   45   50   55   60
 </file> </file>
  
Строка 203: Строка 226:
 Будем делать отправку данных не по ip, а генерировать ethernet-кадры и слать напрямую в сетевой интерфейс минуя обработку ip-пакетов системой. Будем делать отправку данных не по ip, а генерировать ethernet-кадры и слать напрямую в сетевой интерфейс минуя обработку ip-пакетов системой.
  
 +В качестве документации берем статью [[https://austinmarton.wordpress.com/2011/09/14/sending-raw-ethernet-packets-from-a-specific-interface-in-c-on-linux/|Sending raw Ethernet packets from a specific interface in C on Linux]] и создаем такую программу
 +Поскольку программа пихает пакеты в интерфейс не определяя MAC-адреса получателя, то для данной программы **не требуется "отравлять" arp-кэш**.
 +
 +<hidden>
 +<file C rawudpsender.c>
 +#include <arpa/inet.h>
 +#include <linux/if_packet.h>
 +#include <stdio.h>
 +#include <unistd.h>
 +#include <string.h>
 +#include <stdlib.h>
 +#include <sys/ioctl.h>
 +#include <sys/socket.h>
 +#include <net/if.h>
 +#include <netinet/ether.h>
 +#include <netinet/in.h>
 +#include <netinet/ip.h>
 +#include <netinet/udp.h>
 +
 +#define MY_DEST_MAC0 0x00
 +#define MY_DEST_MAC1 0x00
 +#define MY_DEST_MAC2 0x00
 +#define MY_DEST_MAC3 0x00
 +#define MY_DEST_MAC4 0x00
 +#define MY_DEST_MAC5 0x00
 +
 +char* server=(char*)"192.168.10.25";
 +unsigned port=8888;
 +size_t buflen=1500;
 +char ifName[IFNAMSIZ]="eth1";
 +
 +void die(const char *s) { perror(s); exit(1); }
 +
 +char* FillBuf(char* buf, size_t s)
 +{
 + int i;
 + for (i = 0; i < s - 1; i++)
 +      buf[i] = 'a' + rand()%26;
 + buf[i] = '\0';
 + return buf;
 +}
 +
 +void Usage()
 +{
 + printf("Usage: udprawsender [OPTIONS]\n"
 + "OPTIONS:\n"
 + "-i int - Network interface name\n"
 + "-d host - Destination host\n"
 + "-p port - Destination port\n"
 + "-s size - Packet size\n"
 + );
 +}
 + 
 +int ParceArgs(int argc, char *argv[])
 +{
 + int opt;
 + while((opt = getopt(argc, argv, "hp:d:s:i:")) != -1)
 +  {
 +   switch(opt)
 +    {
 +     case 'i':
 +            strcpy(ifName, optarg);
 +            break;
 +     case 'p':
 +            port = atoi(optarg);
 +            break;
 +     case 's':
 +            buflen = atoi(optarg);
 +            break;
 +     case 'd':
 +            server = strdup(optarg);
 +            break;
 +     default:
 +            Usage();
 +            exit(1);
 +    }
 +  }
 + return 1;
 +}
 +int main(int argc, char *argv[])
 +{
 +
 + ParceArgs(argc,argv);
 +
 + char* sendbuf =  (char*) malloc(buflen);
 + if (!sendbuf) die("Can't allocate memory");
 + memset(sendbuf,0,buflen);
 +
 + int sockfd;
 + struct ifreq if_idx;
 + struct ifreq if_mac;
 + struct ether_header *eh = (struct ether_header *) sendbuf;
 + struct iphdr *iph = (struct iphdr *) (sendbuf + sizeof(struct ether_header));
 + struct udphdr *udph = (struct udphdr *) (sendbuf + sizeof(struct ether_header) + sizeof(struct iphdr));
 + char* data = (char *) (sendbuf + sizeof(struct ether_header) + sizeof(struct iphdr) + sizeof(struct udphdr));
 + struct ifreq if_ip;
 + struct sockaddr_ll socket_address;
 + int ttl=250;
 +
 + /* Open RAW socket to send on */
 + if ((sockfd = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW)) == -1) { perror("socket"); }
 + 
 + memset(&if_ip, 0, sizeof(struct ifreq));
 + strncpy(if_ip.ifr_name, ifName, IFNAMSIZ-1);
 + if (ioctl(sockfd, SIOCGIFADDR, &if_ip) < 0)
 +    perror("SIOCGIFADDR");
 +
 + /* Get the index of the interface to send on */
 + memset(&if_idx, 0, sizeof(struct ifreq));
 + strncpy(if_idx.ifr_name, ifName, IFNAMSIZ-1);
 + if (ioctl(sockfd, SIOCGIFINDEX, &if_idx) < 0) perror("SIOCGIFINDEX");
 + /* Get the MAC address of the interface to send on */
 + memset(&if_mac, 0, sizeof(struct ifreq));
 + strncpy(if_mac.ifr_name, ifName, IFNAMSIZ-1);
 + if (ioctl(sockfd, SIOCGIFHWADDR, &if_mac) < 0) perror("SIOCGIFHWADDR");
 +
 + /* Construct the Ethernet header */
 + /* Ethernet header */
 + 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];
 + eh->ether_dhost[0] = MY_DEST_MAC0;
 + eh->ether_dhost[1] = MY_DEST_MAC1;
 + eh->ether_dhost[2] = MY_DEST_MAC2;
 + eh->ether_dhost[3] = MY_DEST_MAC3;
 + eh->ether_dhost[4] = MY_DEST_MAC4;
 + eh->ether_dhost[5] = MY_DEST_MAC5;
 + /* Ethertype field */
 + eh->ether_type = htons(ETH_P_IP);
 +
 + /* IP Header */
 + iph->ihl = 5; /* header length */
 + iph->version = 4;
 + iph->tos = 16; // Low delay
 + iph->tot_len = buflen;
 + iph->id = htons(54321);
 + iph->frag_off = 0x00; //16 bit field = [0:2] flags + [3:15] offset = 0x0
 + iph->ttl = ttl; //16 bit time to live (or maximal number of hops)
 + iph->protocol = 17; // UDP
 + iph->check = 0; //16 bit checksum of IP header. Can't calculate at this point
 + /* Source IP address, can be spoofed */
 + iph->saddr = inet_addr(inet_ntoa(((struct sockaddr_in *)&if_ip.ifr_addr)->sin_addr));
 + /* Destination IP address */
 + iph->daddr = inet_addr(server);
 +
 + udph->source=htons(55555);
 + udph->dest=htons(port);
 +// udph->len
 +// udph->check
 +
 + /* Packet data */
 + FillBuf(data, buflen - sizeof(struct ether_header) - sizeof(struct iphdr) - sizeof(struct udphdr));
 +
 + /* Index of the network device */
 + socket_address.sll_ifindex = if_idx.ifr_ifindex;
 + /* Address length*/
 + socket_address.sll_halen = ETH_ALEN;
 + /* Destination MAC */
 + socket_address.sll_addr[0] = MY_DEST_MAC0;
 + socket_address.sll_addr[1] = MY_DEST_MAC1;
 + socket_address.sll_addr[2] = MY_DEST_MAC2;
 + socket_address.sll_addr[3] = MY_DEST_MAC3;
 + socket_address.sll_addr[4] = MY_DEST_MAC4;
 + socket_address.sll_addr[5] = MY_DEST_MAC5;
 +
 + while (1)
 + {
 +  /* Send packet */
 +  if (sendto(sockfd, sendbuf, buflen, 0, (struct sockaddr*)&socket_address, sizeof(struct sockaddr_ll)) < 0)
 +     die("Send failed\n");
 + }
 +
 + return 0;
 +}
 +
 +</file>
 +</hidden>
 +Компилируем:
 +<file>
 +gcc -O3 -static rawudpsender.c -o rawudpsender
 +</file>
 +Запускаем:
 +<file>
 +./udprawsender -d 192.168.10.25 -p 5555 -s 1500 -i eth1
 +</file>
 +В качестве параметров указываем ip-адрес и порт (это все как раньше). Указываем имя интерфейса в который будем отправлять пакеты. И что важно - размер пакета следует задать равным MTU для данного сетевого интерфейса (меньше будет непродуктивно, больше не войдет в интерфейс).
 +
 +Результаты: **551.42MiB** 385.45K - максимально приближено к результатам udpblast.
 +
 +Улучшим результат запустив флудилку в многопоточном режиме
 +
 +<file bash netflood.sh>
 +#!/bin/bash
 +
 +threads=32 # будем запускать в 32 потока
 +cmd="udprawsender"
 +param="-d 192.168.10.25 -p 5555 -s 1500 -i eth1"
 +
 +for i in $(seq 1 $threads)
 +do
 + "./$cmd" $param &
 + pid[$i]=$!
 + echo "Run thread $i - pid: ${pid[$i]}"
 + sleep 0.1
 +done
 +
 +read -p "Flood Started! Press enter to terminate flood"
 +
 +for i in $(seq 1 $threads); do  kill -9 ${pid[$i]}; done
 +</file>
 +После запуска всех потоков флудилки скрипт ожидает нажатия Enter, после которого завершает запущенные потоки. Поэтому скрипт не рекомендуется прерывать по Ctrl+C, если так сделать, то запущенная в много потоков флудилка не будет завершена и останется флудить после прерывания скрипта.
 +Если скрипт был неправильно завершен, то придется прибивать запущенные флудилки с помощью "killall -9".
 +
 +Число потоков флудилки 32 было подобрано опытным путем. Начиная с 8 увеличивал, пока результаты не перестали заметно увеличиваться.
 +
 +Результаты: 1.07GiB 766.91K 
 +О! 1,07×8=8,56 гигабит в секунду, 766К пакетов в секунду. Пока что это максимум чего удалось достигнуть.
 +
 +Я пробовал запускать udpsender в много потоков. Результаты получались почти такие же как у отправлялки сырых пакетов, однако udpsender при этом заметно сильнее нагружает систему. Load Average подскакивает очень заметно пропорционально числу потоков флудилки.
 +
 +===== Послесловие =====
 +
 +При выполнении задачи описанной в статье важно озаботиться тюнингом сетевой карты. Как минимум увеличить размер очереди отправки:
 +<file bash>
 +ip link set dev eth1 txqueuelen 10000
 +</file>
 +Если на сетевой карте выставить MTU в 9000, то можно увеличить загрузку однопоточным зафлуживанием канала. В многопоточном варианте все-равно все упирается в потолок 1.08GiB/s, но зато можно снизить число запускаемых потоков для достижения этого "потолка".
 +
 +Результатов превышающих абсолютно все описанные удалось добиться при использовании udpblast в многопоточном режиме **1.16GiB 150.92K** **1,16*8=9,28 гигабит в секунду**, что близко к максимальной полосе испытываемого 10гигабитного интерфейса, однако все остальные эксперименты были тоже интересны.
 +
 +Вот скрипт многопоточного флуда с помощью tcpblast
 +<file bash netflood-max.sh>
 +#!/bin/bash
 +
 +threads=32
 +buflen=65535
 +pkt="$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w $buflen| head -n 1)"
 +
 +for i in $(seq 1 $threads)
 +do
 + (echo "$pkt" |udpblast -4q -s 32kb -c1m 192.168.10.25:5555) &
 + echo "Run thread $i"
 + sleep 0.1
 +done
 +read -p "Flood Started! Press enter to terminate flood"
 +killall -9 udpblast
 +</file>
 +
 +
 +{{tag>linux shell Си network}}
net/channel_performance_measuring.1646061586.txt.gz · Последнее изменение: 2022/02/28 18:19 — san

Если не указано иное, содержимое этой вики предоставляется на условиях следующей лицензии: Public Domain
Public Domain Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki