Como medir desempenhos de conectividade em redes de computadores, com Iperf3 e OpenWRT

Intro

“O wifi tá lento.”

Qualquer pessoa que já tenha administrado uma rede de computadores sabe que essa frase costuma anteceder dores de cabeça, ranger de dentes, frustração e, com mais frequência do que deveria, tempo perdido. Isso porque, na grande maioria das vezes, essa será a única informação disponível para a pessoa responsável pela rede, que deverá então se virar do avesso para descobrir o que está acontecendo… isso quando realmente há algo acontecendo (o que nem sempre é o caso). Sendo assim, é preciso ter maneiras de medir objetivamente o desempenho da rede para poder diagnosticar problemas, de modo a evitar a eterna controvérsia de “aqui está normal”, “funciona perfeitamente na minha máquina”, “não consigo reproduzir o erro descrito” e “feche o utorrent, porra”. Para atingir esse objetivo, vamos desenvolver medidores de performance de rede utilizando OpenWRT e Iperf versão 3.

Os procedimentos demonstrados aqui funcionam em qualquer roteador com OpenWRT, tanto em versões mais recentes como mais antigas, desde que haja espaço de armazenamento disponível e seja possível instalar pacotes e alterar arquivos nos dispositivos utilizados.

Neste exemplo, o foco será em dispositivos cujo hardware é considerado bastante limitado para padrões atuais (32MB de RAM e 4MB de storage) e utilizam a versão 19.07.10 do OpenWRT. Mais especificamente, os modelos TL-WA850 e TL-MR3020, mas é possível adaptar para outros.

Conteúdo do artigo:

  • instalação do iperf3 em roteadores com OpenWRT, e suas configurações como serviço de sistema;
  • como configurar um botão do dispositivo para ligar e desligar o serviço;
  • como associar leds ao status do serviço (ligado/desligado), em conjunto com o botão acima;
  • como executar testes à partir de outros dispositivos (desktop & mobile);

Instalação & Configuração

Como tudo o mais na vida, há diversas maneiras diferentes de executar esse tipo de procedimento. Logo de saída, antecipo que é necessário utilizar linha de comando para implementar os exemplos demonstrados, pois não é possível usar a interface web (daqui em diante, referida como Luci) para criar e editar arquivos dentro do firmware instalado no roteador.

O OpenWRT conta com um gerenciador de pacotes (opkg) semelhante a qualquer outra distro Linux, portanto é necessário atualizar os repositórios-fonte antes de podermos instalar algo. Por ser um aplicativo pequeno, de poucos kilobytes, é possível instalar o iperf3 até mesmo nesses dispositivos com 4MB de espaço. Também é possível incluí-lo na lista de pacotes instalados ao utilizar o image builder: nesse caso, os arquivos complementares mencionados abaixo podem ser integrados diretamente ao sistema.

Primeiro, devemos nos conectar ao roteador e executar os seguinte comandos para instalar o iperf3:

$ ssh root@roteador-alvo
# opkg update
# opkg install iperf3

Uma porta TCP deve ser liberada explicitamente no firewall do roteador, para que o Iperf possa receber conexões vindas de outros dispositivos na rede. O teste propriamente dito acontece em um fluxo de rede específico, separado deste daemon e definido pelo lado cliente, que pode, por exemplo, invocar UDP ao invés de TCP - o que deverá ser igualmente liberado no firewall. Para evitar conflito com outros serviços de rede, será usada a porta 5005 ao longo do exercício. Pode-se escolher qualquer outra porta alta, desde que esteja configurada consistentemente em todos os lugares mencionados.

A seguinte regra deve ser adicionada ao firewall - isso pode ser feito diretamente no arquivo /etc/config/firewall ou através da Luci:

config rule
	option name 'Allow-iperf3'
	option target 'ACCEPT'
	list proto 'tcp'
	list proto 'udp'
	option dest_port '5005'
	option src 'lan'

Em ordem: define-se um nome para a regra (Allow-iperf3), a ação a ser tomada (ACCEPT, ou seja, aceitar conexões), os protocolos de rede listados individualmente (tcp + udp) e a porta de destino 5005.

A ultima opção (src) deve indicar a interface de rede a ser medida, sendo que devemos prestar atenção se é uma rede interna ou uma rede diretamente exposta à internet. Embora seja possível utilizar o iperf3 em ambas, os casos descritos aqui preveem apenas redes internas.

Para que a nova regra do firewall seja aplicada:

# /etc/init.d/firewall reload

Dois arquivos, a serem criados, definem a configuração do serviço:

  • /usr/bin/iperfd : para iniciar uma instância do iperf3 em modo servidor e como daemon (-s -D), escutando conexões TCP em todas as interfaces IPv4 disponíveis, sempre na porta 5005 (-B 0.0.0.0 -p 5005). Eventos da aplicação serão logados no arquivo designado em logfile. Endereço IP e porta podem ser modificados à vontade, desde que estejam igualmente configurados no firewall, como visto acima. Outros parâmetros podem ser adicionados à execução da instância, como por exemplo: -V para verbose e -d para debug. Há suporte ipv6 também, mas isso não será contemplado aqui.

      cat <<_EOF_ > /usr/bin/iperfd
      /usr/bin/iperf3 -s -D -B 0.0.0.0 -p 5005 --logfile /var/log/iperf3-server.log
      _EOF_
    
      chmod +x /usr/bin/iperfd
    
  • /etc/init.d/iperfd : para manejar essa instância enquanto serviço do sistema. logger faz com que o evento executado apareça no log do sistema, e a mensagem contida entre aspas pode ser modificada livremente. E as outras instruções dizem respeito ao LED que será associado ao iperf-server (ver abaixo):

      ### copiar a partir da próxima linha....
      #!/bin/sh /etc/rc.common
      # (C) 2019 - 2022 Fernao Vellozo | GPLv2
    
      START=90
    
      boot() { 
      	logger "iperf3 daemon started on boot"
      	uci set system.led_sys.trigger='default-on'
      	uci commit
      	/usr/bin/iperfd
      	/etc/init.d/led reload
      }
    
      start() {
      	logger "iperf3 daemon started" 
      	uci set system.led_sys.trigger='default-on'
      	uci commit
      	/usr/bin/iperfd
      	/etc/init.d/led reload
      }
    
      stop() { 
      	logger "iperf3 daemon stopped"
      	killall iperf3
      	uci set system.led_sys.trigger='none'
      	uci commit
      	/etc/init.d/led reload
      }
    
      restart() {
      	logger "iperf3 daemon restarting"
      	killall iperf3
      	/usr/bin/iperfd
      	logger "iperf3 daemon restarted"
      }
      ### ... até a linha de cima
    

Após criar esse arquivo, ele deve ganhar a permissão de executável:

# chmod +x /etc/init.d/iperfd

Podemos configurar o serviço para que seja iniciado no boot, se preciso.

# /etc/init.d/iperfd enable

Nesse caso, devemos levar em consideração se é desejável deixar o serviço ativo (portanto exposto) o tempo todo, pois ele pode ser manipulado e/ou explorado de maneira indevida, mesmo em uma rede interna.

Led Status

Para que o status do serviço seja devidamente associado ao led SYS quando aceso ou apagado, deve-se acrescentar esse bloco de código em /etc/config/system:

config led 'led_sys'
	option default '0'
	option sysfs 'XXX'
	option name 'iperf3'
	option trigger 'none'

Para pôr as modificações em efeito:

# /etc/init.d/system reload
# /etc/init.d/led reload

Com isso, o led “sys” deverá estar aceso enquanto o serviço estiver ativo, e apagar-se quando o serviço for desativado.

Botão ON/OFF

Por padrão no OpenWRT, apenas o botão de reset vem configurado, enquanto o botão wps não faz nada. Para atribuir funções a este botão, deve acrescentar um arquivo para interpretar as ações a serem executadas. Neste exemplo, o iperf3 será iniciado sempre que o botão for pressionado por 1 segundo ou menos. Outras funções serão descritas para que sejam executadas quando o botão for pressionado por X e Y segundos, mas nada será realmente excutado nesses casos:

# mkdir -p /etc/hotplug.d/button
# vi /etc/hotplug.d/buttons/00-button
source /lib/functions.sh

do_button () {
    local button
    local action
    local handler
    local min
    local max

    config_get button "${1}" button
    config_get action "${1}" action
    config_get handler "${1}" handler
    config_get min "${1}" min
    config_get max "${1}" max

    [ "${ACTION}" = "${action}" -a "${BUTTON}" = "${button}" -a -n "${handler}" ] && {
        [ -z "${min}" -o -z "${max}" ] && eval ${handler}
        [ -n "${min}" -a -n "${max}" ] && {
            [ "${min}" -le "${SEEN}" -a "${max}" -ge "${SEEN}" ] && eval ${handler}
        }
    }
}

config_load system
config_foreach do_button button

# chmod +x /usr/bin/iperfd-btn

O script /usr/bin/iperf-btn é executado pela ação do hardware, verificando se o serviço iperf3 está executando: se sim, o serviço será interrompido. Caso contrário, será iniciado.

### copiar daqui para baixo...
#!/bin/ash
ps | grep iperf3 | grep -v grep > /dev/null 2>&1
if [ $? = 0 ]; 
  then /etc/init.d/iperfd stop;
  else /etc/init.d/iperfd start; 
fi
###... até aqui

### para que seja executável
# chmod +x /usr/bin/iperfd-btn  

Por fim, acrescenta-se à configuração do roteador, no arquivo /etc/config/system:

config button
	option button 'wps'
	option action 'pressed'
	option handler '/usr/bin/iperf-btn'

config button
	option button 'wps'
	option action 'released'
	option handler 'logger "mais que 2s e menos que 4s"'
	option max '4'
	option min '1'

config button
	option button 'wps'
	option action 'released'
	option handler 'logger "mais que 5 segundos e menos que 10 segundos"'
	option max '10'
	option min '5' 

# /etc/init.d/system reload

A ação executada pelo hardware é definida pela opção handler. Nesse exemplo, as mensagens de tempo aparecerão no log de sistema a cada vez que o botão for pressionado e solto após X segundos.

Client

Para Android, havia apps que aceitavam iperf3, mas agora passou a ser pago. Usando termux, basta digitar o hostname/IP e a porta configurada na outra ponta. Caso dê erro, a mensagem indica como instala-lo no contexto do termux:

$ iperf3 -p 5005 -c ROUTER_ACIMA

Em uma máquina Arch Linux, deve-se instalar o pacote iperf3 e executá-lo em linha de comando.:

# pacman -S iperf3
# iperf3 -p 5005 -c ROUTER_ACIMA
  • -c : Nome ou IP ou dispositivo
  • -p : porta onde o iperf3 está executando remotamente
  • -V : exibir mais informações na tela (verbose)
  • -R : modo reverso, onde o servidor envia dados para cliente (por padrão, cliente -> servidor)
  • -u : usar UDP (por padrão, usa-se TCP)
  • -4 : usar apenas IPv4
  • -6 : usar apenas IPv6