Simulăm probleme de rețea în Linux

Bună tuturor, mă numesc Sasha, și conduc testarea backend-ului la FunCorp. La noi, ca și la mulți alții, a fost implementată arhitectura orientată pe servicii. Pe de o parte, aceasta simplifică sarcinile, deoarece fiecare serviciu este mai ușor de testat separat, dar pe de altă parte, apare necesitatea de a testa interacțiunea dintre servicii, care adesea se desfășoară prin rețea.

În acest articol, voi vorbi despre două utilitare care pot verifica scenariile de bază ce descriu funcționarea aplicației în condiții de probleme cu rețeaua.

Simulăm probleme de rețea în Linux

Simulăm probleme cu rețeaua

De obicei, software-ul este testat pe servere de testare cu o conexiune bună la internet. În condiții dure de producție, lucrurile pot să nu fie atât de simple, așa că uneori este necesar să verificăm programele în condiții de conexiune slabă. În Linux, utilitarul care ne ajută în simularea acestor condiții este tc.

tc (prescurtare de la Traffic Control) permite configurarea transmiterii pachetelor de rețea în sistem. Acest utilitar are multe funcționalități, despre care se poate citi mai în detaliu aici. Aici voi analiza doar câteva dintre ele: ne interesează programarea traficului, pentru care folosim qdisc, iar deoarece dorim să emulăm o rețea instabilă, vom folosi qdisc fără clasă netem.

Vom porni un server echo pe server (eu am folosit nmap-ncat):

ncat -l 127.0.0.1 12345 -k -c 'xargs -n1 -i echo "Responses: {}"'

Pentru a afișa detaliat toate timpii pe fiecare etapă a interacțiunii client-server, am scris un script simplu în Python, care trimite o solicitare Test către serverul nostru echo.

Codul sursă al clientului

#!/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

Îl vom rula și vom observa traficul pe interfața lo și pe portul 12345:

[user@host ~]# python client.py
[ora înainte de conexiune: 1578652979.44837]
[ora după conexiune, înainte de trimitere: 1578652979.44889]
[ora după trimitere, înainte de primire: 1578652979.44894]
[ora după primire, înainte de închidere: 1578652979.45922]
[ora după închidere: 1578652979.45928]
[durata totală: 0.01091]
Răspuns: Test

Dump de trafic

[user@host ~]# tcpdump -i lo -nn port 12345
tcpdump: ieșire detaliată suprinsă, folosiți -v sau -vv pentru decodarea completă a protocolului
ascultând pe lo, tip de legătură EN10MB (Ethernet), dimensiunea capturii 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

Totul este standard: strângerea mâinilor în trei părți, PSH/ACK și ACK ca răspuns de două ori — aceasta este schimbul de cerere și răspuns între client și server, și de două ori FIN/ACK și ACK — încheierea conexiunii.

Întârzierea pachetelor

Acum vom stabili o întârziere de 500 de milisecunde:

tc qdisc add dev lo root netem delay 500ms

Pornim clientul și vedem că acum scriptul rulează timp de 2 secunde:

[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

Ce este în trafic? Să vedem:

Dump de trafic

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

Se observă că între client și server a apărut o întârziere așteptată de o jumătate de secundă. Sistemul se comportă mult mai interesant dacă întârzierea este mai mare: nucleul începe să retransmită anumite pachete TCP. Să schimbăm întârzierea la 1 secundă și să vedem traficul (nu voi afișa ieșirea clientului, acolo avem așteptatele 4 secunde în total):

tc qdisc change dev lo root netem delay 1s

Dump de trafic

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

Se observă că clientul a trimis pachetul SYN de două ori, iar serverul a trimis SYN/ACK de două ori.

Pe lângă valoarea constantă, pentru întârziere se poate specifica o deviație, o funcție de distribuție și corelație (cu un anumit val pentru pachetul anterior). Acest lucru se face astfel:

tc qdisc change dev lo root netem delay 500ms 400ms 50 distribution normal

Aici am specificat o întârziere în intervalul de la 100 la 900 milisecunde, valorile vor fi selectate conform distribuției normale și va exista o corelație de 50% cu valoarea întârzierei pentru pachetul anterior.

Probabil ați observat că în prima comandă am folosit add, apoi change. Valoarea acestor comenzi este evidentă, așa că voi adăuga doar că mai există și del, cu care se poate elimina configurația.

Pierderea pachetelor

Să încercăm acum să realizăm pierderea pachetelor. După cum se vede din documentație, se poate face acest lucru în trei moduri: să pierdem pachete aleatoriu cu o anumită probabilitate, să folosim o serie Markov de 2, 3 sau 4 stări sau să folosim modelul Elliott-Gilbert. În articol, voi analiza prima (cea mai simplă și evidentă) metodă, iar despre celelalte se poate citi. aici.

Vom face pierderi de 50% din pachete cu o corelație de 25%:

tc qdisc add dev lo root netem loss 50% 25%

Din păcate, tcpdump nu ne va putea arăta vizibil pierderea pachetelor, vom presupune doar că funcționează cu adevărat. Și ne va ajuta să ne convingem acest lucru timpul de execuție extins și instabil al scriptului. client.py (poate fi executat instantaneu, dar poate dura și 20 de secunde), precum și creșterea numărului de pachete retransmise:

[user@host ~]# netstat -s | grep retransmited; sleep 10; netstat -s | grep retransmited
    17147 segmente retransmise
    17185 segmente retransmise

Adăugarea de zgomot în pachete

Pe lângă pierderea pachetelor, putem imita și deteriorarea acestora: într-o poziție aleatorie a pachetului va apărea zgomot. Vom face deteriorarea pachetelor cu o probabilitate de 50% și fără corelație:

tc qdisc change dev lo root netem corrupt 50%

Lansăm scriptul clientului (nu este nimic interesant, dar a fost executat timp de 2 secunde), observăm traficul:

Dump de trafic

[user@host ~]# tcpdump -i lo -nn port 12345
tcpdump: ieșire detaliată suprimată, folosiți -v sau -vv pentru o decodare completă a protocolului
ascultând pe lo, tip de legătură EN10MB (Ethernet), dimensiunea capturii 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

Se observă că unele pachete au fost trimise din nou și există un pachet cu metadate corupte: options [nop,unknown-65 0x0a3dcf62eb3d,[bad opt]>. Dar cel mai important este că, în final, totul a funcționat corect — TCP și-a îndeplinit sarcina.

Duplicarea pachetelor

Ce altceva se mai poate face cu ajutorul netem? Например, сымитировать ситуацию, обратную потере пакетов, — дубликацию пакетов. Эта команда также принимает 2 аргумента: вероятность и корреляцию.

tc qdisc change dev lo root netem duplicate 50% 25%

Modificarea ordinii pachetelor

Puteți amesteca pachetele, iar asta se poate face în două moduri.

În primul mod, o parte din pachete sunt trimise imediat, restul — cu o întârziere specificată. Exemplu din documentație:

tc qdisc change dev lo root netem delay 10ms reorder 25% 50%

Cu o probabilitate de 25% (și o corelație de 50%), pachetul va fi trimis imediat, restul vor fi trimise cu o întârziere de 10 milisecunde.

Al doilea mod constă în faptul că fiecare pachet N trimis instantaneu cu o probabilitate (și corelație) specificată, iar celelalte — cu o întârziere specificată. Exemplu din documentație:

tc qdisc change dev lo root netem delay 10ms reorder 25% 50% gap 5

Fiecare al cincilea pachet cu o probabilitate de 25% va fi trimis fără întârziere.

Modificarea lățimii de bandă

De obicei, se trimite către TBF, dar cu ajutorul netem se poate modifica și lățimea de bandă a interfeței:

tc qdisc change dev lo root netem rate 56kbit

Această comandă va face ca accesul localhost să fie la fel de chinuitor ca navigarea pe internet printr-un modem dial-up. Pe lângă setarea bitrate-ului, se poate simula și modelul protocolului de nivel de legătură: se poate specifica overhead-ul pentru pachet, dimensiunea celulei și overhead-ul pentru celulă. De exemplu, se poate simula ATM și bitrate-ul de 56 kbit/s.:

tc qdisc change dev lo root netem rate 56kbit 0 48 5

Simulăm timeout de conexiune

Un alt punct important în planul de testare la recepția software-ului sunt timeout-urile. Acest lucru este important deoarece, în sistemele distribuite, când un serviciu se deconectează, celelalte trebuie să comute la timp pe altele sau să returneze o eroare clientului; acestea nu ar trebui în niciun caz să rămână blocate în așteptarea unui răspuns sau a stabilirii unei conexiuni.

Există mai multe modalități de a face acest lucru: de exemplu, folosind un mock care nu răspunde, sau conectându-vă la proces printr-un debugger, plasând un breakpoint în locul dorit și oprind execuția procesului (acesta este, probabil, cel mai ciudat mod). Dar unul dintre cele mai evidente este să blocați porturile sau gazdele. Aici ne va ajuta iptables.

Pentru demonstrație, vom bloca portul 12345 și vom rula scriptul clientului nostru. Putem bloca pachetele de ieșire pe acest port la expeditor sau pe cele de intrare la receptor. În exemplele mele, vor fi blocate pachetele de intrare (folosim chain INPUT și opțiunea —dport). Acestora li se poate aplica DROP, REJECT sau REJECT cu flag-ul TCP RST, putem folosi ICMP host unreachable (de fapt, comportamentul implicit este icmp-port-unreachable, de asemenea, există posibilitatea de a trimite ca răspuns icmp-net-unreachable, icmp-proto-unreachable, icmp-net-prohibited și icmp-host-prohibited).

ȘTERGE

Dacă există o regulă cu DROP, pachetele vor dispărea pur și simplu.

iptables -A INPUT -p tcp --dport 12345 -j DROP

Pornim clientul și vedem că se blochează la conectarea la server. Ne uităm la trafic:
Dump de trafic

[user@host ~]# tcpdump -i lo -nn port 12345
tcpdump: output detaliat suprimat, folosiți -v sau -vv pentru decodarea completă a protocolului
ascultând pe lo, tip de legătură EN10MB (Ethernet), dimensiunea capturii 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

Se observă că clientul trimite pachete SYN cu un timeout crescător exponențial. Iată că am găsit o mică eroare în client: trebuie să folosim metoda settimeout(), pentru a limita timpul în care clientul va încerca să se conecteze la server.

Ștergem imediat regula:

iptables -D INPUT -p tcp --dport 12345 -j DROP

Putem șterge toate regulile imediat:

iptables -F

Dacă folosiți Docker și trebuie să blocați tot traficul care merge către container, o puteți face în felul următor:

iptables -I DOCKER-USER -p tcp -d CONTAINER_IP -j DROP

REJECT

Acum adăugăm o regulă similară, dar cu REJECT:

iptables -A INPUT -p tcp --dport 12345 -j REJECT

Clientul se închide după o secundă cu eroarea [Errno 111] Connection refused. Ne uităm la traficul ICMP:

[user@host ~]# tcpdump -i lo -nn icmp
tcpdump: output detaliat suprimat, folosiți -v sau -vv pentru decodarea completă a protocolului
ascultând pe lo, tip de legătură EN10MB (Ethernet), dimensiunea capturii 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

Se observă că clientul a primit de două ori port unreachable și după aceasta s-a închis cu eroare.

REJECT cu tcp-reset

Să încercăm să adăugăm opțiunea —reject-with tcp-reset:

iptables -A INPUT -p tcp --dport 12345 -j REJECT --reject-with tcp-reset

În acest caz, clientul iese imediat cu o eroare, deoarece a primit un pachet RST la prima cerere:

[user@host ~]# tcpdump -i lo -nn port 12345
tcpdump: output detaliat suprimat, folosiți -v sau -vv pentru decriptarea completă a protocolului
ascultând pe lo, tip de legătură EN10MB (Ethernet), dimensiune captură 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 cu icmp-host-unreachable

Să încercăm o altă variantă de utilizare a REJECT:

iptables -A INPUT -p tcp --dport 12345 -j REJECT --reject-with icmp-host-unreachable

Clientul se închide după o secundă cu eroarea [Errno 113] Nu există cale către gazdă, în trafic ICMP vedem ICMP gazdă 127.0.0.1 inaccesibil.

Puteți încerca, de asemenea, celelalte opțiuni REJECT, iar eu mă voi opri la acestea 🙂

Simulăm timeout-ul cererii

O altă situație este atunci când clientul a reușit să se conecteze la server, dar nu poate trimite o cerere. Cum putem filtra pachetele astfel încât filtrarea să nu înceapă imediat? Dacă ne uităm la traficul oricărei comunicări între client și server, putem observa că în timpul stabilirii conexiunii sunt folosite doar flagurile SYN și ACK, iar în cadrul schimbului de date, ultimul pachet de cerere va avea flagul PSH. Acesta este stabilit automat, pentru a evita tamponarea. Putem folosi aceste informații pentru a crea un filtru: va permite toate pachetele, cu excepția celor care conțin flagul PSH. Astfel, conexiunea se va stabili, dar clientul nu va putea trimite date serverului.

ȘTERGE

Pentru DROP, comanda va arăta astfel:

iptables -A INPUT -p tcp --tcp-flags PSH PSH --dport 12345 -j DROP

Pornim clientul și observăm traficul:

Dump de trafic

[user@host ~]# tcpdump -i lo -nn port 12345
tcpdump: output detaliat suprimat, folosiți -v sau -vv pentru decriptarea completă a protocolului
ascultând pe lo, tip de legătură EN10MB (Ethernet), dimensiune captură 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

Observăm că conexiunea este stabilită și clientul nu poate trimite date serverului.

REJECT

În acest caz, comportamentul va fi același: clientul nu va putea trimite o cerere, dar va primi ICMP 127.0.0.1 tcp port 12345 inaccesibil și va crește timpul între retransmisia cererii în mod exponențial. Comanda arată așa:

iptables -A INPUT -p tcp --tcp-flags PSH PSH --dport 12345 -j REJECT

REJECT cu tcp-reset

Comanda arată după cum urmează:

iptables -A INPUT -p tcp --tcp-flags PSH PSH --dport 12345 -j REJECT --reject-with tcp-reset

Deja știm că atunci când utilizăm —reject-with tcp-reset clientul va primi un pachet RST ca răspuns, așa că putem anticipa comportamentul: primirea unui pachet RST când conexiunea este stabilită înseamnă închiderea neașteptată a socket-ului de cealaltă parte, ceea ce înseamnă că clientul trebuie să primească Connection reset by peer. Rulăm scriptul nostru și ne asigurăm de asta. Iată cum va arăta traficul:

Dump de trafic

[user@host ~]# tcpdump -i lo -nn port 12345
tcpdump: output detaliat suprimat, folosiți -v sau -vv pentru decodificare completă a protocolului
ascultând pe lo, tip de legătură EN10MB (Ethernet), dimensiune de captură 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 cu icmp-host-unreachable

Cred că este deja evident pentru toată lumea cum va arăta comanda 🙂 Comportamentul clientului în acest caz va fi puțin diferit de cel care a fost cu un REJECT simplu: clientul nu va crește timeout-ul între încercările de retransmisie a pachetului.

[user@host ~]# tcpdump -i lo -nn icmp
tcpdump: output detaliat suprimat, folosiți -v sau -vv pentru decodificare completă a protocolului
ascultând pe lo, tip de legătură EN10MB (Ethernet), dimensiune de captură 262144 bytes
10:29:56.149202 IP 127.0.0.1 > 127.0.0.1: ICMP host 127.0.0.1 inaccesibil, length 65
10:29:56.349107 IP 127.0.0.1 > 127.0.0.1: ICMP host 127.0.0.1 inaccesibil, length 65
10:29:56.549117 IP 127.0.0.1 > 127.0.0.1: ICMP host 127.0.0.1 inaccesibil, length 65
10:29:56.750125 IP 127.0.0.1 > 127.0.0.1: ICMP host 127.0.0.1 inaccesibil, length 65
10:29:56.951130 IP 127.0.0.1 > 127.0.0.1: ICMP host 127.0.0.1 inaccesibil, length 65
10:29:57.152107 IP 127.0.0.1 > 127.0.0.1: ICMP host 127.0.0.1 inaccesibil, length 65
10:29:57.353115 IP 127.0.0.1 > 127.0.0.1: ICMP host 127.0.0.1 inaccesibil, length 65

Ieșire

Nu este necesar să scrieți un mock pentru a verifica interacțiunea dintre serviciu și un client sau server blocat, uneori este suficient să folosiți utilitățile standard disponibile în Linux.

Utilitățile discutate în articol au chiar mai multe funcții decât au fost descrise, așa că puteți veni cu propriile variante de utilizare. Personal, mi-a fost întotdeauna suficient ceea ce am scris (de fapt, chiar mai puțin). Dacă utilizați aceste sau utilități similare în testarea din compania dumneavoastră, vă rog să-mi scrieți cum anume. Dacă nu, sper că software-ul dumneavoastră va deveni mai bun dacă decideți să-l testați în condițiile problemelor de rețea propuse.

Sursa: habr.com

Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS 🔥 Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS | ProHoster