Olá a todos, meu nome é Sasha, eu lidero os testes de backend na FunCorp. Temos, assim como muitos, uma arquitetura orientada a serviços. Por um lado, isso facilita o trabalho, pois cada serviço é mais fácil de testar separadamente, mas por outro, surge a necessidade de testar a interação entre os serviços, que muitas vezes acontece pela rede.
Neste artigo, falarei sobre duas ferramentas que permitem verificar cenários básicos que descrevem o funcionamento do aplicativo em caso de problemas com a rede.

Simulando problemas com a rede
Normalmente, o software é testado em servidores de teste com uma boa conexão de internet. Nas condições rigorosas do ambiente de produção, tudo pode não ser tão suave, portanto, às vezes é necessário testar programas em condições de conexão ruim. No Linux, a ferramenta que ajuda a simular essas condições é tc.
tc (abreviação de Traffic Control) permite configurar a transmissão de pacotes de rede no sistema. Esta ferramenta possui grandes capacidades, que podem ser lidas em mais detalhes . Aqui, vou abordar apenas algumas delas: estamos interessados em agendar o tráfego, para o que usamos qdisc, e já que precisamos emular uma rede instável, usaremos a qdisc sem classes .
Vamos iniciar um servidor echo no servidor (usei ):
ncat -l 127.0.0.1 12345 -k -c 'xargs -n1 -i echo "Resposta: {}"'
Para detalhar os timestamps em cada etapa da interação do cliente com o servidor, escrevi um script simples em Python que envia uma solicitação Test para o nosso servidor echo.
O código fonte do cliente
#!/bin/python
import socket
import time
HOST = '127.0.0.1'
PORT = 12345
BUFFER_SIZE = 1024
MESSAGE = "Testn"
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
t1 = time.time()
print "[time before connection: %.5f]" % t1
s.connect((HOST, PORT))
print "[time after connection, before sending: %.5f]" % time.time()
s.send(MESSAGE)
print "[time after sending, before receiving: %.5f]" % time.time()
data = s.recv(BUFFER_SIZE)
print "[time after receiving, before closing: %.5f]" % time.time()
s.close()
t2 = time.time()
print "[time after closing: %.5f]" % t2
print "[total duration: %.5f]" % (t2 - t1)
print data
Vamos executá-lo e observar o tráfego na interface lo e na porta 12345:
[user@host ~]# python client.py
[tempo antes da conexão: 1578652979.44837]
[tempo após a conexão, antes do envio: 1578652979.44889]
[tempo após o envio, antes do recebimento: 1578652979.44894]
[tempo após o recebimento, antes do fechamento: 1578652979.45922]
[tempo após o fechamento: 1578652979.45928]
[duração total: 0.01091]
Resposta: Test
Captura de tráfego
[user@host ~]# tcpdump -i lo -nn port 12345
tcpdump: saída detalhada suprimida, use -v ou -vv para decodificação completa do protocolo
ouvindo em lo, tipo de link EN10MB (Ethernet), tamanho de captura 262144 bytes
10:42:59.448601 IP 127.0.0.1.54054 > 127.0.0.1.12345: Flags [S], seq 3383332866, win 43690, options [mss 65495,sackOK,TS val 606325685 ecr 0,nop,wscale 7], length 0
10:42:59.448612 IP 127.0.0.1.12345 > 127.0.0.1.54054: Flags [S.], seq 2584700178, ack 3383332867, win 43690, options [mss 65495,sackOK,TS val 606325685 ecr 606325685,nop,wscale 7], length 0
10:42:59.448622 IP 127.0.0.1.54054 > 127.0.0.1.12345: Flags [.], ack 1, win 342, options [nop,nop,TS val 606325685 ecr 606325685], length 0
10:42:59.448923 IP 127.0.0.1.54054 > 127.0.0.1.12345: Flags [P.], seq 1:6, ack 1, win 342, options [nop,nop,TS val 606325685 ecr 606325685], length 5
10:42:59.448930 IP 127.0.0.1.12345 > 127.0.0.1.54054: Flags [.], ack 6, win 342, options [nop,nop,TS val 606325685 ecr 606325685], length 0
10:42:59.459118 IP 127.0.0.1.12345 > 127.0.0.1.54054: Flags [P.], seq 1:15, ack 6, win 342, options [nop,nop,TS val 606325696 ecr 606325685], length 14
10:42:59.459213 IP 127.0.0.1.54054 > 127.0.0.1.12345: Flags [.], ack 15, win 342, options [nop,nop,TS val 606325696 ecr 606325696], length 0
10:42:59.459268 IP 127.0.0.1.54054 > 127.0.0.1.12345: Flags [F.], seq 6, ack 15, win 342, options [nop,nop,TS val 606325696 ecr 606325696], length 0
10:42:59.460184 IP 127.0.0.1.12345 > 127.0.0.1.54054: Flags [F.], seq 15, ack 7, win 342, options [nop,nop,TS val 606325697 ecr 606325696], length 0
10:42:59.460196 IP 127.0.0.1.54054 > 127.0.0.1.12345: Flags [.], ack 16, win 342, options [nop,nop,TS val 606325697 ecr 606325697], length 0
Tudo padrão: o handshake triplo, PSH/ACK e ACK em resposta duas vezes — é a troca de solicitação e resposta entre cliente e servidor, e duas vezes FIN/ACK e ACK — finalizando a conexão.
Atraso dos pacotes
Agora vamos definir um atraso de 500 milissegundos:
tc qdisc add dev lo root netem delay 500ms
Iniciamos o cliente e vemos que agora o script leva 2 segundos para executar:
[user@host ~]# ./client.py
[time before connection: 1578662612.71044]
[time after connection, before sending: 1578662613.71059]
[time after sending, before receiving: 1578662613.71065]
[time after receiving, before closing: 1578662614.72011]
[time after closing: 1578662614.72019]
[total duration: 2.00974]
Response: Test
E quanto ao tráfego? Vamos conferir:
Captura de tráfego
13:23:33.210520 IP 127.0.0.1.58694 > 127.0.0.1.12345: Flags [S], seq 1720950927, win 43690, options [mss 65495,sackOK,TS val 615958947 ecr 0,nop,wscale 7], length 0
13:23:33.710554 IP 127.0.0.1.12345 > 127.0.0.1.58694: Flags [S.], seq 1801168125, ack 1720950928, win 43690, options [mss 65495,sackOK,TS val 615959447 ecr 615958947,nop,wscale 7], length 0
13:23:34.210590 IP 127.0.0.1.58694 > 127.0.0.1.12345: Flags [.], ack 1, win 342, options [nop,nop,TS val 615959947 ecr 615959447], length 0
13:23:34.210657 IP 127.0.0.1.58694 > 127.0.0.1.12345: Flags [P.], seq 1:6, ack 1, win 342, options [nop,nop,TS val 615959947 ecr 615959447], length 5
13:23:34.710680 IP 127.0.0.1.12345 > 127.0.0.1.58694: Flags [.], ack 6, win 342, options [nop,nop,TS val 615960447 ecr 615959947], length 0
13:23:34.719371 IP 127.0.0.1.12345 > 127.0.0.1.58694: Flags [P.], seq 1:15, ack 6, win 342, options [nop,nop,TS val 615960456 ecr 615959947], length 14
13:23:35.220106 IP 127.0.0.1.58694 > 127.0.0.1.12345: Flags [.], ack 15, win 342, options [nop,nop,TS val 615960957 ecr 615960456], length 0
13:23:35.220188 IP 127.0.0.1.58694 > 127.0.0.1.12345: Flags [F.], seq 6, ack 15, win 342, options [nop,nop,TS val 615960957 ecr 615960456], length 0
13:23:35.720994 IP 127.0.0.1.12345 > 127.0.0.1.58694: Flags [F.], seq 15, ack 7, win 342, options [nop,nop,TS val 615961457 ecr 615960957], length 0
13:23:36.221025 IP 127.0.0.1.58694 > 127.0.0.1.12345: Flags [.], ack 16, win 342, options [nop,nop,TS val 615961957 ecr 615961457], length 0
É possível ver que, na interação entre o cliente e o servidor, ocorreu a latência esperada de meio segundo. O comportamento do sistema se torna muito mais interessante se a latência for maior: o núcleo começa a reenviar alguns pacotes TCP. Vamos alterar o atraso para 1 segundo e observar o tráfego (não mostrarei a saída do cliente, pois o total esperado é de 4 segundos de duração):
tc qdisc change dev lo root netem delay 1s
Captura de tráfego
13:29:07.709981 IP 127.0.0.1.39306 > 127.0.0.1.12345: Flags [S], seq 283338334, win 43690, options [mss 65495,sackOK,TS val 616292946 ecr 0,nop,wscale 7], length 0
13:29:08.710018 IP 127.0.0.1.12345 > 127.0.0.1.39306: Flags [S.], seq 3514208179, ack 283338335, win 43690, options [mss 65495,sackOK,TS val 616293946 ecr 616292946,nop,wscale 7], length 0
13:29:08.711094 IP 127.0.0.1.39306 > 127.0.0.1.12345: Flags [S], seq 283338334, win 43690, options [mss 65495,sackOK,TS val 616293948 ecr 0,nop,wscale 7], length 0
13:29:09.710048 IP 127.0.0.1.39306 > 127.0.0.1.12345: Flags [.], ack 1, win 342, options [nop,nop,TS val 616294946 ecr 616293946], length 0
13:29:09.710152 IP 127.0.0.1.39306 > 127.0.0.1.12345: Flags [P.], seq 1:6, ack 1, win 342, options [nop,nop,TS val 616294947 ecr 616293946], length 5
13:29:09.711120 IP 127.0.0.1.12345 > 127.0.0.1.39306: Flags [S.], seq 3514208179, ack 283338335, win 43690, options [mss 65495,sackOK,TS val 616294948 ecr 616292946,nop,wscale 7], length 0
13:29:10.710173 IP 127.0.0.1.12345 > 127.0.0.1.39306: Flags [.], ack 6, win 342, options [nop,nop,TS val 616295947 ecr 616294947], length 0
13:29:10.711140 IP 127.0.0.1.39306 > 127.0.0.1.12345: Flags [.], ack 1, win 342, options [nop,nop,TS val 616295948 ecr 616293946], length 0
13:29:10.714782 IP 127.0.0.1.12345 > 127.0.0.1.39306: Flags [P.], seq 1:15, ack 6, win 342, options [nop,nop,TS val 616295951 ecr 616294947], length 14
13:29:11.714819 IP 127.0.0.1.39306 > 127.0.0.1.12345: Flags [.], ack 15, win 342, options [nop,nop,TS val 616296951 ecr 616295951], length 0
13:29:11.714893 IP 127.0.0.1.39306 > 127.0.0.1.12345: Flags [F.], seq 6, ack 15, win 342, options [nop,nop,TS val 616296951 ecr 616295951], length 0
13:29:12.715562 IP 127.0.0.1.12345 > 127.0.0.1.39306: Flags [F.], seq 15, ack 7, win 342, options [nop,nop,TS val 616297952 ecr 616296951], length 0
13:29:13.715596 IP 127.0.0.1.39306 > 127.0.0.1.12345: Flags [.], ack 16, win 342, options [nop,nop,TS val 616298952 ecr 616297952], length 0
É evidente que o cliente enviou o pacote SYN duas vezes, enquanto o servidor enviou o SYN/ACK duas vezes.
Além do valor constante, para a latência é possível definir uma variação, uma função de distribuição e correlação (com o valor do pacote anterior). Isso é feito da seguinte maneira:
tc qdisc change dev lo root netem delay 500ms 400ms 50 distribution normal
Aqui configuramos a latência em um intervalo de 100 a 900 milissegundos. Os valores serão escolhidos de acordo com a distribuição normal e haverá uma correlação de 50% com o valor da latência do pacote anterior.
Você pode ter notado que no primeiro comando eu usei add, e então change. O significado desses comandos é claro, portanto só adicionarei que também existe del, que pode ser usado para remover a configuração.
Perda de pacotes
Vamos agora tentar causar perda de pacotes. Como pode ser visto na documentação, isso pode ser feito de três maneiras: perder pacotes aleatoriamente com uma certa probabilidade, usar uma cadeia de Markov de 2, 3 ou 4 estados para calcular a perda de pacotes, ou utilizar o modelo de Elliott-Gilbert. Neste artigo, vou abordar a primeira (a mais simples e óbvia) abordagem, enquanto as outras podem ser lidas. .
Vamos simular a perda de 50% dos pacotes com uma correlação de 25%:
tc qdisc add dev lo root netem loss 50% 25%
Infelizmente, tcpdump não conseguirá nos mostrar claramente a perda de pacotes, teremos que apenas supor que está funcionando. E para confirmar isso, precisaremos observar um aumento e uma instabilidade no tempo de execução do script client.py (pode ser executado instantaneamente ou até levar 20 segundos), além do aumento na quantidade de pacotes retransmitidos:
[user@host ~]# netstat -s | grep retransmited; sleep 10; netstat -s | grep retransmited
17147 segmentos retransmitidos
17185 segmentos retransmitidos
Adicionando ruído aos pacotes
Além da perda de pacotes, podemos simular sua corrupção: uma aleatoriedade surgirá em uma posição do pacote. Vamos corromper os pacotes com uma probabilidade de 50% e sem correlação:
tc qdisc change dev lo root netem corrupt 50%
Iniciamos o script do cliente (não tem nada interessante, mas foi executado por 2 segundos), observamos o tráfego:
Captura de tráfego
[user@host ~]# tcpdump -i lo -nn port 12345
tcpdump: saída detalhada suprimida, use -v ou -vv para uma decodificação completa do protocolo
ouvindo em lo, tipo de link EN10MB (Ethernet), tamanho da captura 262144 bytes
10:20:54.812434 IP 127.0.0.1.43666 > 127.0.0.1.12345: Flags [S], seq 2023663770, win 43690, options [mss 65495,sackOK,TS val 1037001049 ecr 0,nop,wscale 7], length 0
10:20:54.812449 IP 127.0.0.1.12345 > 127.0.0.1.43666: Flags [S.], seq 2104268044, ack 2023663771, win 43690, options [mss 65495,sackOK,TS val 1037001049 ecr 1037001049,nop,wscale 7], length 0
10:20:54.812458 IP 127.0.0.1.43666 > 127.0.0.1.12345: Flags [.], ack 1, win 342, options [nop,nop,TS val 1037001049 ecr 1037001049], length 0
10:20:54.812509 IP 127.0.0.1.43666 > 127.0.0.1.12345: Flags [P.], seq 1:6, ack 1, win 342, options [nop,nop,TS val 1037001049 ecr 1037001049], length 5
10:20:55.013093 IP 127.0.0.1.43666 > 127.0.0.1.12345: Flags [P.], seq 1:6, ack 1, win 342, options [nop,nop,TS val 1037001250 ecr 1037001049], length 5
10:20:55.013122 IP 127.0.0.1.12345 > 127.0.0.1.43666: Flags [.], ack 6, win 342, options [nop,nop,TS val 1037001250 ecr 1037001250], length 0
10:20:55.014681 IP 127.0.0.1.12345 > 127.0.0.1.43666: Flags [P.], seq 1:15, ack 6, win 342, options [nop,nop,TS val 1037001251 ecr 1037001250], length 14
10:20:55.014745 IP 127.0.0.1.43666 > 127.0.0.1.12345: Flags [.], ack 15, win 340, options [nop,nop,TS val 1037001251 ecr 1037001251], length 0
10:20:55.014823 IP 127.0.0.1.43666 > 127.0.0.5.12345: Flags [F.], seq 2023663776, ack 2104268059, win 342, options [nop,nop,TS val 1037001251 ecr 1037001251], length 0
10:20:55.214088 IP 127.0.0.1.12345 > 127.0.0.1.43666: Flags [P.], seq 1:15, ack 6, win 342, options [nop,unknown-65 0x0a3dcf62eb3d,[bad opt]>
10:20:55.416087 IP 127.0.0.1.43666 > 127.0.0.1.12345: Flags [F.], seq 6, ack 15, win 342, options [nop,nop,TS val 1037001653 ecr 1037001251], length 0
10:20:55.416804 IP 127.0.0.1.12345 > 127.0.0.1.43666: Flags [F.], seq 15, ack 7, win 342, options [nop,nop,TS val 1037001653 ecr 1037001653], length 0
10:20:55.416818 IP 127.0.0.1.43666 > 127.0.0.1.12345: Flags [.], ack 16, win 343, options [nop,nop,TS val 1037001653 ecr 1037001653], length 0
10:20:56.147086 IP 127.0.0.1.12345 > 127.0.0.1.43666: Flags [F.], seq 15, ack 7, win 342, options [nop,nop,TS val 1037002384 ecr 1037001653], length 0
10:20:56.147101 IP 127.0.0.1.43666 > 127.0.0.1.12345: Flags [.], ack 16, win 342, options [nop,nop,TS val 1037002384 ecr 1037001653], length 0
É evidente que alguns pacotes foram enviados novamente e há um pacote com metadados corrompidos: options [nop,unknown-65 0x0a3dcf62eb3d,[bad opt]>. Mas o principal é que, no final, tudo funcionou corretamente — o TCP cumpriu sua tarefa.
Duplicação de pacotes
O que mais pode ser feito com netem? Например, сымитировать ситуацию, обратную потере пакетов, — дубликацию пакетов. Эта команда также принимает 2 аргумента: вероятность и корреляцию.
tc qdisc change dev lo root netem duplicate 50% 25%
Alteração da ordem dos pacotes
É possível misturar os pacotes, de duas maneiras.
Na primeira, uma parte dos pacotes é enviada imediatamente, os outros — com um atraso definido. Exemplo da documentação:
tc qdisc change dev lo root netem delay 10ms reorder 25% 50%
Com uma probabilidade de 25% (e correlação de 50%), o pacote será enviado imediatamente, os outros serão enviados com um atraso de 10 milissegundos.
A segunda maneira é quando a cada N-ésimo pacote é enviado instantaneamente com uma probabilidade (e correlação) definida, enquanto os demais — com um atraso definido. Exemplo da documentação:
tc qdisc change dev lo root netem delay 10ms reorder 25% 50% gap 5
A cada quinto pacote, com uma probabilidade de 25%, será enviado sem atraso.
Alteração da largura de banda
Normalmente, em todo lugar, é utilizado , mas com a ajuda de netem também é possível modificar a largura de banda da interface:
tc qdisc change dev lo root netem rate 56kbit
Este comando tornará as navegações localhost tão penosas quanto navegar na internet por meio de um modem dial-up. Além de estabelecer a taxa de bits, também é possível simular um modelo de protocolo de camada de enlace: definir overhead para o pacote, o tamanho da célula e overhead para a célula. Por exemplo, assim é possível simular e taxa de bits de 56 kbit/s.:
tc qdisc change dev lo root netem rate 56kbit 0 48 5
Simulando o tempo limite da conexão
Outro ponto importante no plano de teste para a aceitação de software — os timeouts. Isso é importante porque, em sistemas distribuídos, ao desligar um dos serviços, os outros devem fazer failover para outros ou retornar um erro ao cliente, e de forma alguma devem simplesmente travar, aguardando uma resposta ou o estabelecimento da conexão.
Existem várias maneiras de fazer isso: por exemplo, usar um mock que não responde nada, ou conectar-se ao processo usando um depurador, colocar um breakpoint no local desejado e interromper a execução do processo (este, provavelmente, é o jeito mais extravagante). Mas uma das maneiras mais óbvias é bloquear as portas ou hosts. Para isso, vamos usar .
Para demonstração, vamos bloquear a porta 12345 e executar nosso script cliente. Podemos bloquear os pacotes de saída nessa porta do remetente ou de entrada no receptor. Nos meus exemplos, os pacotes de entrada serão bloqueados (usamos a cadeia INPUT e a opção —dport). Para esses pacotes, podemos fazer DROP, REJECT ou REJECT com a flag TCP RST, ou podemos usar ICMP host unreachable (na verdade, o comportamento padrão é icmp-port-unreachable, e ainda há a possibilidade de enviar como resposta icmp-net-unreachable, icmp-proto-unreachable, icmp-net-prohibited e icmp-host-prohibited).
DROP
Quando há uma regra com DROP, os pacotes simplesmente "desaparecem".
iptables -A INPUT -p tcp --dport 12345 -j DROP
Iniciamos o cliente e vemos que ele trava na etapa de conexão com o servidor. Analisamos o tráfego:
Captura de tráfego
[user@host ~]# tcpdump -i lo -nn port 12345
tcpdump: saída detalhada suprimida, use -v ou -vv para decodificação completa do protocolo
ouvindo em lo, tipo de link EN10MB (Ethernet), tamanho da captura 262144 bytes
08:28:20.213506 IP 127.0.0.1.32856 > 127.0.0.1.12345: Flags [S], seq 3019694933, win 43690, options [mss 65495,sackOK,TS val 1203046450 ecr 0,nop,wscale 7], length 0
08:28:21.215086 IP 127.0.0.1.32856 > 127.0.0.1.12345: Flags [S], seq 3019694933, win 43690, options [mss 65495,sackOK,TS val 1203047452 ecr 0,nop,wscale 7], length 0
08:28:23.219092 IP 127.0.0.1.32856 > 127.0.0.1.12345: Flags [S], seq 3019694933, win 43690, options [mss 65495,sackOK,TS val 1203049456 ecr 0,nop,wscale 7], length 0
08:28:27.227087 IP 127.0.0.1.32856 > 127.0.0.1.12345: Flags [S], seq 3019694933, win 43690, options [mss 65495,sackOK,TS val 1203053464 ecr 0,nop,wscale 7], length 0
08:28:35.235102 IP 127.0.0.1.32856 > 127.0.0.1.12345: Flags [S], seq 3019694933, win 43690, options [mss 65495,sackOK,TS val 1203061472 ecr 0,nop,wscale 7], length 0
Note que o cliente está enviando pacotes SYN com um tempo limite exponencial crescente. Aqui encontramos um pequeno bug no cliente: deve-se usar o método settimeout(), para limitar o tempo que o cliente tentará se conectar ao servidor.
Imediatamente removemos a regra:
iptables -D INPUT -p tcp --dport 12345 -j DROPÉ possível remover todas as regras de uma vez:
iptables -F
Se você estiver usando o Docker e precisar bloquear todo o tráfego que vai para o contêiner, você pode fazer isso da seguinte maneira:
iptables -I DOCKER-USER -p tcp -d CONTAINER_IP -j DROP
REJECT
Agora vamos adicionar uma regra semelhante, mas com REJECT:
iptables -A INPUT -p tcp --dport 12345 -j REJECT
O cliente termina após um segundo com o erro [Errno 111] Connection refused. Analisamos o tráfego ICMP:
[user@host ~]# tcpdump -i lo -nn icmp
tcpdump: saída detalhada suprimida, use -v ou -vv para decodificação completa do protocolo
ouvindo em lo, tipo de link EN10MB (Ethernet), tamanho da captura 262144 bytes
08:45:32.871414 IP 127.0.0.1 > 127.0.0.1: ICMP 127.0.0.1 tcp port 12345 unreachable, length 68
08:45:33.873097 IP 127.0.0.1 > 127.0.0.1: ICMP 127.0.0.1 tcp port 12345 unreachable, length 68
Podemos ver que o cliente recebeu duas vezes port unreachable e após isso terminou com um erro.
REJECT com tcp-reset
Vamos tentar adicionar a opção —reject-with tcp-reset:
iptables -A INPUT -p tcp --dport 12345 -j REJECT --reject-with tcp-reset
Nesse caso, o cliente sai imediatamente com um erro, pois na primeira solicitação recebeu um pacote RST:
[user@host ~]# tcpdump -i lo -nn port 12345
tcpdump: saída detalhada suprimida, use -v ou -vv para decodificação completa do protocolo
ouvindo em lo, tipo de link EN10MB (Ethernet), tamanho da captura 262144 bytes
09:02:52.766175 IP 127.0.0.1.60658 > 127.0.0.1.12345: Flags [S], seq 1889460883, win 43690, options [mss 65495,sackOK,TS val 1205119003 ecr 0,nop,wscale 7], length 0
09:02:52.766184 IP 127.0.0.1.12345 > 127.0.0.1.60658: Flags [R.], seq 0, ack 1889460884, win 0, length 0
REJECT com icmp-host-unreachable
Vamos tentar mais uma opção de uso do REJECT:
iptables -A INPUT -p tcp --dport 12345 -j REJECT --reject-with icmp-host-unreachable
O cliente termina após um segundo com o erro [Errno 113] Sem rota para o host, no tráfego ICMP vemos ICMP host 127.0.0.1 inalcançável.
Você também pode tentar os outros parâmetros REJECT, mas eu vou parar nesses 🙂
Simulando timeout de requisição
Outra situação é quando o cliente conseguiu se conectar ao servidor, mas não consegue enviar uma solicitação. Como filtrar os pacotes para que a filtragem comece, digamos, não imediatamente? Se olharmos o tráfego de qualquer comunicação entre cliente e servidor, podemos notar que ao estabelecer a conexão, apenas as flags SYN e ACK são usadas, mas na troca de dados, no último pacote da solicitação, haverá a flag PSH. Isso é definido automaticamente para evitar o buffer. Podemos usar essa informação para criar um filtro: ele irá permitir todos os pacotes, exceto aqueles que contêm a flag PSH. Assim, a conexão será estabelecida, mas o cliente não conseguirá enviar dados ao servidor.
DROP
Para DROP, o comando será assim:
iptables -A INPUT -p tcp --tcp-flags PSH PSH --dport 12345 -j DROP
Iniciamos o cliente e observamos o tráfego:
Captura de tráfego
[user@host ~]# tcpdump -i lo -nn port 12345
tcpdump: saída detalhada suprimida, use -v ou -vv para decodificação completa do protocolo
ouvindo em lo, tipo de link EN10MB (Ethernet), tamanho da captura 262144 bytes
10:02:47.549498 IP 127.0.0.1.49594 > 127.0.0.1.12345: Flags [S], seq 2166014137, win 43690, options [mss 65495,sackOK,TS val 1208713786 ecr 0,nop,wscale 7], length 0
10:02:47.549510 IP 127.0.0.1.12345 > 127.0.0.1.49594: Flags [S.], seq 2341799088, ack 2166014138, win 43690, options [mss 65495,sackOK,TS val 1208713786 ecr 1208713786,nop,wscale 7], length 0
10:02:47.549520 IP 127.0.0.1.49594 > 127.0.0.1.12345: Flags [.], ack 1, win 342, options [nop,nop,TS val 1208713786 ecr 1208713786], length 0
10:02:47.549568 IP 127.0.0.1.49594 > 127.0.0.1.12345: Flags [P.], seq 1:6, ack 1, win 342, options [nop,nop,TS val 1208713786 ecr 1208713786], length 5
10:02:47.750084 IP 127.0.0.1.49594 > 127.0.0.1.12345: Flags [P.], seq 1:6, ack 1, win 342, options [nop,nop,TS val 1208713987 ecr 1208713786], length 5
10:02:47.951088 IP 127.0.0.1.49594 > 127.0.0.1.12345: Flags [P.], seq 1:6, ack 1, win 342, options [nop,nop,TS val 1208714188 ecr 1208713786], length 5
10:02:48.354089 IP 127.0.0.1.49594 > 127.0.0.1.12345: Flags [P.], seq 1:6, ack 1, win 342, options [nop,nop,TS val 1208714591 ecr 1208713786], length 5
Vemos que a conexão foi estabelecida, mas o cliente não consegue enviar dados ao servidor.
REJECT
Nesse caso, o comportamento será o mesmo: o cliente não conseguirá enviar a solicitação, mas receberá ICMP 127.0.0.1 tcp port 12345 unreachable e aumentará o tempo entre as tentativas de reenvio da solicitação de forma exponencial. O comando é assim:
iptables -A INPUT -p tcp --tcp-flags PSH PSH --dport 12345 -j REJECT
REJECT com tcp-reset
O comando é o seguinte:
iptables -A INPUT -p tcp --tcp-flags PSH PSH --dport 12345 -j REJECT --reject-with tcp-reset
Já sabemos que, ao usar —reject-with tcp-reset o cliente receberá um pacote RST em resposta, então podemos prever o comportamento: receber um pacote RST com a conexão estabelecida significa que o socket foi fechado inesperadamente do outro lado, logo, o cliente deve receber Connection reset by peer. Vamos executar nosso script e verificar isso. Veja como será o tráfego:
Captura de tráfego
[user@host ~]# tcpdump -i lo -nn port 12345
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on lo, link-type EN10MB (Ethernet), capture size 262144 bytes
10:22:14.186269 IP 127.0.0.1.52536 > 127.0.0.1.12345: Flags [S], seq 2615137531, win 43690, options [mss 65495,sackOK,TS val 1209880423 ecr 0,nop,wscale 7], length 0
10:22:14.186284 IP 127.0.0.1.12345 > 127.0.0.1.52536: Flags [S.], seq 3999904809, ack 2615137532, win 43690, options [mss 65495,sackOK,TS val 1209880423 ecr 1209880423,nop,wscale 7], length 0
10:22:14.186293 IP 127.0.0.1.52536 > 127.0.0.1.12345: Flags [.], ack 1, win 342, options [nop,nop,TS val 1209880423 ecr 1209880423], length 0
10:22:14.186338 IP 127.0.0.1.52536 > 127.0.0.1.12345: Flags [P.], seq 1:6, ack 1, win 342, options [nop,nop,TS val 1209880423 ecr 1209880423], length 5
10:22:14.186344 IP 127.0.0.1.12345 > 127.0.0.1.52536: Flags [R], seq 3999904810, win 0, length 0
REJECT com icmp-host-unreachable
Acho que já está claro para todos como será o comando 🙂 O comportamento do cliente, nesse caso, será um pouco diferente do que foi com um REJECT simples: o cliente não aumentará o tempo limite entre as tentativas de reenvio do pacote.
[user@host ~]# tcpdump -i lo -nn icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on lo, link-type EN10MB (Ethernet), capture size 262144 bytes
10:29:56.149202 IP 127.0.0.1 > 127.0.0.1: ICMP host 127.0.0.1 unreachable, length 65
10:29:56.349107 IP 127.0.0.1 > 127.0.0.1: ICMP host 127.0.0.1 unreachable, length 65
10:29:56.549117 IP 127.0.0.1 > 127.0.0.1: ICMP host 127.0.0.1 unreachable, length 65
10:29:56.750125 IP 127.0.0.1 > 127.0.0.1: ICMP host 127.0.0.1 unreachable, length 65
10:29:56.951130 IP 127.0.0.1 > 127.0.0.1: ICMP host 127.0.0.1 unreachable, length 65
10:29:57.152107 IP 127.0.0.1 > 127.0.0.1: ICMP host 127.0.0.1 unreachable, length 65
10:29:57.353115 IP 127.0.0.1 > 127.0.0.1: ICMP host 127.0.0.1 unreachable, length 65
Saída
Não é necessário escrever um mock para verificar a interação do serviço com um cliente ou servidor travado; às vezes, é suficiente usar as ferramentas padrão disponíveis no Linux.
As ferramentas discutidas no artigo possuem ainda mais recursos do que os descritos, portanto você pode inventar suas próprias formas de utilizá-las. Para mim, sempre é suficiente o que escrevi (na verdade, até menos). Se você utiliza essas ou ferramentas similares em testes em sua empresa, por favor, compartilhe como você as usa. Se não, espero que seu software se torne mais eficiente ao verificar em condições de problemas de rede com os métodos que sugeri.
Fonte: habr.com
