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

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


net:channel_performance_measuring

Это старая версия документа!


Измерение пропускной способности канала: флудим по полной

Дано: есть арендованный оптический канал со скоростью 2 гигабита по договору. С одной стороны этот канал можно воткнуть в сервер, другая сторона находится далеко и разместить там измерительное оборудование не представляется возможным. С удаленной стороны канал подключен к коммутатору с которого можно снять значения приходящего из канала трафика.

Требуется: проверить что обещанные 2 гигабита предоставляются арендованным каналом.

Решение:

Если бы можно было разместить с удаленной стороны второй сервер, то можно было бы воспользоваться программами iperf3, netperf и другими подобными. И задача была бы решена. Поскольку такое невозможно по условию задачи (указанные программы требуют обязательной ответной части, которую разместить негде), то будем как-то изголяться.

Можно загрузить канал по максимуму с ближней стороны исходящим трафиком с сервера и замерять приходящий трафик на коммутаторе.

Загрузить канал можно например UDP-трафиком. Тоесть нужно найти решение предоставляющее возможность нафлудить в канал по максимуму. Данная методика не является идеальной, поскольку мы сможем загрузить канал только в одном направлении. Однако за неимением лучшего и принимая во внимание, что ассиметричный шейпинг тестируемого канала маловероятен будем довольствоваться тем что можем сделать.

Итак, начинаем изыскания:

Предварительная подготовка

Настроем тестируемую сеть. На интерфейсе eth1, который подключен к тестируемому каналу выставим какой-нибудь ip-адрес. Будем использовать 192.168.10.1/24. Отправлять пакеты будем на адрес 192.168.10.25

С противоположной стороны будут сниматься показания со счетчиков порта на коммутаторе и никакой настройки не требуется.

Для того, чтоб удостовериться в достаточной загрузке канала со стороны сервера, будем смотреть загрузку интерфейса eth1. Для этого вполне подойдет прогамма bmon, которую будем запускать следующим образом

bmon -p eth1 -o curses:ngraph=2,curses:details,curses:info

Кроме всего прочего надо иметь в виду, что ip-трафик не пойдет в интерфейс, если операционная система не будет знать, что с другой стороны канала есть устройство, способное принимать данные. Будут отправляться arp-запросы, на которые некому отвечать и больше никакого трафика. Чтобы эту проблему решить "отравим" arp-кэш - подставим в него липовые данные (любой мак-адрес для ip-адреса 192.168.10.25)

arp -s 192.168.10.25 78:e3:b5:f5:7a:DD

После окончания экспериментов следует удалить из арп-кэша фиктивное значение.

arp -d 192.168.10.25

Эксперимент №1 - пользуемся средствами bash

netflood1.sh
#!/bin/bash
 
# Будем делать отправку большими блоками данных по 64КБ 
 buflen=65535
# Формируем блок данных, заполняя его случайными цифробуквенными данными
 pkt="$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w $buflen| head -n 1)"
 
# отправляем в бесконечном цикле UDP-трафик на ip 192.168.10.25 и 
# порт 5555 (можно выбрать любой - не принципиально)
 while true; do echo "$pkt"; done >/dev/udp/192.168.10.25/5555

Смотрим в bmon и видим такие результаты 29.41MiB/сек 20.69K пакетов в секунду.

Ну даже в мегабиты переводить не имеет смысла - такой загрузки недостаточно для тестирования.

Эксперимент №2 - пользуемся программой netcat

В скрипте меняем только последнюю строку с циклом

netflood2.sh
#!/bin/sh
 
while true; do echo "$pkt"; done |nc -4u 192.168.10.25 5555

Результаты ну совсем мало отличаются от предыдущих: 29.93MiB 22.41K…

Тоже слабовато для тестирования.

Эксперимент №3 - пользуемся программой udpblast

Сначала укажем программе самой генерировать передаваемый трафик (ключ -a)

netflood3.sh
#!/bin/sh
 buflen=65535
 pkt="$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w $buflen| head -n 1)"
 
udpblast -4qa -s 32kb -c1m 10.48.1.25:5555

Результаты: 61.66MiB 44.31K

А теперь будем отправлять заранее подготовленные пакеты

netflood3-1.sh
#!/bin/sh
 
buflen=65535
pkt="$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w $buflen| head -n 1)"
 
echo "$pkt" |udpblast -4q -s 32kb -c1m 10.48.1.25:5555

Результаты: 550.48MiB 395.61K … хм-м-м-м… а вот это уже что-то. 550*8/1024=4,296875 гигабита/сек.

Можно запустить команду генерирующую трафик в несколько потоков и еще улучшить результат.

В принципе поставленная задача решена (нам надо проверить полосу в 2 гигабита), но настоящие джедаи никогда не остановятся на достигнутом, если можно улучшить результат.

Эксперимент №4 - делаем собственную программу генерации UDP-трафика

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

Нажмите, чтобы отобразить

Нажмите, чтобы скрыть

udpsender.c
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include <time.h>
#include <unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>
 
char* server="192.168.10.25";
unsigned port=8888;
size_t buflen=1400;
 
 
void die(char *s) { perror(s); exit(1); }
 
char* MakeBuf(size_t s)
{
 int i;
 char* buf =  (char*) malloc(s);
 if (!buf) die("Can't allocate memory");
 for (i = 0; i < s - 1; i++)
      buf[i] = 'a' + rand()%26;
   buf[i] = '\0';
 return buf;
}
 
void Usage()
{
 printf("Usage: udpsender [OPTIONS]\n"
 "OPTIONS:\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:")) != -1)
  {
   switch(opt)
    {
     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[])
{
 struct sockaddr_in si;
 int s, i;
 
 ParceArgs(argc,argv);
 
 char* testmsg = MakeBuf(buflen);
 
 if ( (s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
  { die("Error: socket()"); }
 
 memset((char *) &si, 0, sizeof(si));
 si.sin_family = AF_INET;
 si.sin_port = htons(port);
 
 if (inet_aton(server , &si.sin_addr) == 0).
  { fprintf(stderr, "inet_aton() failed\n");  exit(1); }
 
 while(1)
 {
  if ( sendto(s, testmsg, strlen(testmsg) , 0 , (struct sockaddr *) &si, sizeof(si)) == -1 )
   { die("Error: sendto()"); }
 }
 
 close(s);
 return 0;
}

Компилируем исходный код

gcc -O3 -static udpsender.c -o udpsender

Запускаем тестирование (шлем пакеты на 192.168.10.25 на порт 5555 размер пакетов максимальный - 65508):

./udpsender -d 192.168.10.25 -p 5555 -s 65508

Результаты: 485.53MiB 341.71K - немного ниже чем у udpblast.

Эксперимент №5 - делаем программу отправки трафика напрямую в сетевой интерфейс

Будем делать отправку данных не по ip, а генерировать ethernet-кадры и слать напрямую в сетевой интерфейс минуя обработку ip-пакетов системой.

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