Simulation von Netzwerkproblemen unter Linux

Hallo zusammen, mein Name ist Sasha, ich leite Backend-Tests bei FunCorp. Wir haben, wie viele andere auch, eine serviceorientierte Architektur implementiert. Das vereinfacht einerseits die Arbeit, denn Es ist einfacher, jeden Dienst einzeln zu testen, andererseits besteht jedoch die Notwendigkeit, die Interaktion der Dienste untereinander zu testen, die häufig über das Netzwerk erfolgt.

In diesem Artikel werde ich über zwei Dienstprogramme sprechen, mit denen grundlegende Szenarien überprüft werden können, die den Betrieb einer Anwendung bei Netzwerkproblemen beschreiben.

Simulation von Netzwerkproblemen unter Linux

Simulation von Netzwerkproblemen

Typischerweise wird Software auf Testservern mit einer guten Internetverbindung getestet. In rauen Produktionsumgebungen laufen die Dinge möglicherweise nicht so reibungslos, sodass Sie manchmal Programme unter schlechten Verbindungsbedingungen testen müssen. Unter Linux hilft das Dienstprogramm bei der Simulation solcher Bedingungen tc.

tc(Abk. von Traffic Control) ermöglicht Ihnen, die Übertragung von Netzwerkpaketen im System zu konfigurieren. Dieses Dienstprogramm verfügt über großartige Funktionen. Sie können mehr darüber lesen hier. Hier werde ich nur einige davon betrachten: Wir interessieren uns für die Verkehrsplanung, für die wir verwenden qdisc, und da wir ein instabiles Netzwerk emulieren müssen, werden wir klassenlose qdisc verwenden Netz.

Lassen Sie uns einen Echo-Server auf dem Server starten (ich habe verwendet). nmap-ncat):

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

Um alle Zeitstempel bei jedem Schritt der Interaktion zwischen Client und Server detailliert anzuzeigen, habe ich ein einfaches Python-Skript geschrieben, das eine Anfrage sendet Test an unseren Echo-Server.

Client-Quellcode

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

Starten wir es und schauen wir uns den Datenverkehr auf der Schnittstelle an lo und Port 12345:

[user@host ~]# python client.py
[time before connection: 1578652979.44837]
[time after connection, before sending: 1578652979.44889]
[time after sending, before receiving: 1578652979.44894]
[time after receiving, before closing: 1578652979.45922]
[time after closing: 1578652979.45928]
[total duration: 0.01091]
Response: Test

Verkehrsdeponie

[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: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

Alles ist Standard: ein Drei-Wege-Handshake, zweimal PSH/ACK und ACK als Antwort – das ist der Austausch von Anfrage und Antwort zwischen Client und Server, und zweimal FIN/ACK und ACK – Abschluss der Verbindung.

Paketverzögerung

Jetzt stellen wir die Verzögerung auf 500 Millisekunden ein:

tc qdisc add dev lo root netem delay 500ms

Wir starten den Client und sehen, dass das Skript nun 2 Sekunden lang läuft:

[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

Was ist im Verkehr? Lass uns nachsehen:

Verkehrsdeponie

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

Sie können sehen, dass bei der Interaktion zwischen Client und Server die erwartete Verzögerung von einer halben Sekunde aufgetreten ist. Das System verhält sich deutlich interessanter, wenn die Verzögerung größer ist: Der Kernel beginnt, einige TCP-Pakete erneut zu senden. Ändern wir die Verzögerung auf 1 Sekunde und schauen uns den Datenverkehr an (die Ausgabe des Clients zeige ich nicht, die Gesamtdauer beträgt die erwarteten 4 Sekunden):

tc qdisc change dev lo root netem delay 1s

Verkehrsdeponie

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

Es ist ersichtlich, dass der Client zweimal ein SYN-Paket gesendet hat und der Server zweimal ein SYN/ACK gesendet hat.

Zusätzlich zu einem konstanten Wert kann die Verzögerung auf eine Abweichung, eine Verteilungsfunktion und eine Korrelation (mit dem Wert für das vorherige Paket) eingestellt werden. Dies geschieht wie folgt:

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

Hier haben wir die Verzögerung zwischen 100 und 900 Millisekunden eingestellt, die Werte werden nach einer Normalverteilung ausgewählt und es besteht eine 50-prozentige Korrelation mit dem Verzögerungswert für das vorherige Paket.

Möglicherweise ist Ihnen das beim ersten Befehl aufgefallen, den ich verwendet habe hinzufügenund Drop Übernehmen . Die Bedeutung dieser Befehle ist offensichtlich, daher möchte ich nur hinzufügen, dass es noch mehr gibt Restaurants, mit dem die Konfiguration entfernt werden kann.

Paketverlust

Versuchen wir nun, den Paketverlust zu beheben. Wie aus der Dokumentation hervorgeht, kann dies auf drei Arten erfolgen: zufälliger Verlust von Paketen mit einer gewissen Wahrscheinlichkeit, Verwendung einer Markov-Kette aus 2, 3 oder 4 Zuständen zur Berechnung des Paketverlusts oder Verwendung des Elliott-Gilbert-Modells. In dem Artikel werde ich die erste (einfachste und offensichtlichste) Methode betrachten, und Sie können über andere lesen hier.

Lassen Sie uns den Verlust von 50 % der Pakete mit einer Korrelation von 25 % berechnen:

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

Leider ist die tcpdump wird uns den Verlust von Paketen nicht eindeutig anzeigen können, wir gehen nur davon aus, dass es wirklich funktioniert. Und die erhöhte und instabile Laufzeit des Skripts wird uns dabei helfen, dies zu überprüfen. client.py (kann sofort oder vielleicht in 20 Sekunden abgeschlossen werden) sowie eine erhöhte Anzahl erneut übertragener Pakete:

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

Hinzufügen von Rauschen zu Paketen

Zusätzlich zum Paketverlust können Sie Paketschäden simulieren: Rauschen tritt an einer zufälligen Paketposition auf. Lassen Sie uns Paketschäden mit einer Wahrscheinlichkeit von 50 % und ohne Korrelation verursachen:

tc qdisc change dev lo root netem corrupt 50%

Wir führen das Client-Skript aus (dort ist nichts Interessantes zu sehen, aber die Fertigstellung dauerte 2 Sekunden) und sehen uns den Datenverkehr an:

Verkehrsdeponie

[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: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

Es ist ersichtlich, dass einige Pakete wiederholt gesendet wurden und es ein Paket mit fehlerhaften Metadaten gibt: Optionen [nop,unknown-65 0x0a3dcf62eb3d,[bad opt]>. Aber die Hauptsache ist, dass am Ende alles richtig funktioniert hat – TCP hat seine Aufgabe gemeistert.

Paketduplizierung

Was kann man sonst noch damit machen? Netz? Simulieren Sie beispielsweise die umgekehrte Situation eines Paketverlusts – Paketduplizierung. Dieser Befehl benötigt außerdem zwei Argumente: Wahrscheinlichkeit und Korrelation.

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

Ändern der Paketreihenfolge

Sie können die Beutel auf zwei Arten mischen.

Im ersten Fall werden einige Pakete sofort gesendet, der Rest mit einer festgelegten Verzögerung. Beispiel aus der Dokumentation:

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

Mit einer Wahrscheinlichkeit von 25 % (und einer Korrelation von 50 %) wird das Paket sofort gesendet, der Rest wird mit einer Verzögerung von 10 Millisekunden gesendet.

Bei der zweiten Methode wird jedes N-te Paket sofort mit einer bestimmten Wahrscheinlichkeit (und Korrelation) und der Rest mit einer bestimmten Verzögerung gesendet. Beispiel aus der Dokumentation:

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

Bei jedem fünften Paket besteht eine Chance von 25 %, dass es ohne Verzögerung versendet wird.

Bandbreite ändern

Normalerweise überall dort, wo sie sich darauf beziehen TBF, aber mit der Hilfe Netz Sie können auch die Schnittstellenbandbreite ändern:

tc qdisc change dev lo root netem rate 56kbit

Dieses Team wird Wanderungen unternehmen localhost genauso schmerzhaft wie das Surfen im Internet über ein DFÜ-Modem. Zusätzlich zum Festlegen der Bitrate können Sie auch das Link-Layer-Protokollmodell emulieren: Legen Sie den Overhead für das Paket, die Zellengröße und den Overhead für die Zelle fest. Dies kann beispielsweise simuliert werden Geldautomat und Bitrate 56 kbit/s:

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

Verbindungszeitüberschreitung simulieren

Ein weiterer wichtiger Punkt im Testplan bei der Softwareabnahme sind Timeouts. Dies ist wichtig, denn wenn in verteilten Systemen einer der Dienste deaktiviert ist, müssen die anderen rechtzeitig auf die anderen zurückgreifen oder einen Fehler an den Client zurückgeben, und auf keinen Fall sollten sie einfach hängen bleiben und auf eine Antwort oder eine Verbindung warten gegründet werden.

Es gibt mehrere Möglichkeiten, dies zu tun: Verwenden Sie beispielsweise einen Mock, der nicht antwortet, oder stellen Sie über einen Debugger eine Verbindung zum Prozess her, setzen Sie einen Haltepunkt an der richtigen Stelle und stoppen Sie den Prozess (dies ist wahrscheinlich der perverseste Weg). Aber eine der offensichtlichsten ist die Firewall von Ports oder Hosts. Es wird uns dabei helfen iptables.

Zur Demonstration werden wir Port 12345 mit einer Firewall versehen und unser Client-Skript ausführen. Sie können ausgehende Pakete an diesen Port beim Sender oder eingehende Pakete beim Empfänger durch eine Firewall blockieren. In meinen Beispielen werden eingehende Pakete durch eine Firewall blockiert (wir verwenden die Kette INPUT und die Option --dport). Solche Pakete können DROP, REJECT oder REJECT mit dem TCP-Flag RST oder mit nicht erreichbarem ICMP-Host sein (tatsächlich ist das Standardverhalten). ICMP-Port-nicht erreichbar, und es besteht auch die Möglichkeit, eine Antwort zu senden icmp-net-unreachable, icmp-proto-unreachable, icmp-net-verboten и icmp-host-verboten).

DROP

Wenn es eine Regel mit DROP gibt, werden Pakete einfach „verschwinden“.

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

Wir starten den Client und stellen fest, dass er beim Herstellen einer Verbindung zum Server einfriert. Schauen wir uns den Verkehr an:
Verkehrsdeponie

[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
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

Es ist zu erkennen, dass der Client SYN-Pakete mit einem exponentiell steigenden Timeout sendet. Wir haben also einen kleinen Fehler im Client gefunden: Sie müssen die Methode verwenden settimeout()um die Zeit zu begrenzen, in der der Client versucht, eine Verbindung zum Server herzustellen.

Wir entfernen die Regel sofort:

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

Sie können alle Regeln auf einmal löschen:

iptables -F

Wenn Sie Docker verwenden und den gesamten Datenverkehr zum Container mit einer Firewall schützen müssen, können Sie dies wie folgt tun:

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

ABLEHNEN

Fügen wir nun eine ähnliche Regel hinzu, jedoch mit REJECT:

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

Der Client wird nach einer Sekunde mit einem Fehler beendet [Errno 111] Verbindung abgelehnt. Schauen wir uns den ICMP-Verkehr an:

[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
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

Es ist ersichtlich, dass der Kunde zweimal erhalten hat Port nicht erreichbar und endete dann mit einem Fehler.

ABLEHNEN mit TCP-Reset

Versuchen wir, die Option hinzuzufügen --reject-with tcp-reset:

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

In diesem Fall bricht der Client sofort mit einem Fehler ab, da bei der ersten Anfrage ein RST-Paket empfangen wurde:

[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
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

ABLEHNEN mit icmp-host-unreachable

Versuchen wir eine andere Option zur Verwendung von REJECT:

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

Der Client wird nach einer Sekunde mit einem Fehler beendet [Errno 113] Keine Route zum Host, sehen wir im ICMP-Verkehr ICMP-Host 127.0.0.1 nicht erreichbar.

Sie können auch die anderen REJECT-Parameter ausprobieren, und ich werde mich auf diese konzentrieren :)

Anforderungszeitüberschreitung simulieren

Eine andere Situation liegt vor, wenn der Client eine Verbindung zum Server herstellen konnte, aber keine Anfrage an ihn senden konnte. Wie filtere ich Pakete, damit die Filterung nicht sofort beginnt? Wenn Sie sich den Datenverkehr jeglicher Kommunikation zwischen dem Client und dem Server ansehen, werden Sie feststellen, dass beim Verbindungsaufbau nur die SYN- und ACK-Flags verwendet werden, beim Datenaustausch jedoch das letzte Anforderungspaket das PSH-Flag enthält. Die Installation erfolgt automatisch, um Pufferung zu vermeiden. Mit diesen Informationen können Sie einen Filter erstellen: Er lässt alle Pakete zu, außer denen, die das PSH-Flag enthalten. Dadurch wird die Verbindung hergestellt, der Client kann jedoch keine Daten an den Server senden.

DROP

Für DROP würde der Befehl so aussehen:

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

Starten Sie den Client und beobachten Sie den Verkehr:

Verkehrsdeponie

[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: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

Wir sehen, dass die Verbindung hergestellt ist und der Client keine Daten an den Server senden kann.

ABLEHNEN

In diesem Fall ist das Verhalten dasselbe: Der Client kann die Anfrage nicht senden, sondern empfangen ICMP 127.0.0.1 TCP-Port 12345 nicht erreichbar und die Zeit zwischen erneuten Anforderungsübermittlungen exponentiell verlängern. Der Befehl sieht so aus:

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

ABLEHNEN mit TCP-Reset

Der Befehl sieht so aus:

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

Das wissen wir bereits bei der Nutzung --reject-with tcp-reset Der Client erhält als Antwort ein RST-Paket, sodass das Verhalten vorhergesagt werden kann: Der Empfang eines RST-Pakets während der Verbindung bedeutet, dass der Socket auf der anderen Seite unerwartet geschlossen wird, was bedeutet, dass der Client empfangen sollte Verbindung von Peer zurückgesetzt. Lassen Sie uns unser Skript ausführen und dies sicherstellen. Und so wird der Verkehr aussehen:

Verkehrsdeponie

[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

ABLEHNEN mit icmp-host-unreachable

Ich denke, es ist bereits für jeden klar, wie der Befehl aussehen wird :) Das Verhalten des Clients wird sich in diesem Fall etwas von dem bei einem einfachen REJECT unterscheiden: Der Client erhöht die Zeitüberschreitung zwischen den Versuchen, das Paket erneut zu senden, nicht.

[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

Abschluss

Es ist nicht notwendig, einen Mock zu schreiben, um die Interaktion eines Dienstes mit einem hängenden Client oder Server zu testen; manchmal reicht es aus, Standarddienstprogramme von Linux zu verwenden.

Die im Artikel besprochenen Dienstprogramme verfügen über noch mehr Funktionen als beschrieben, sodass Sie sich einige eigene Optionen für deren Verwendung ausdenken können. Persönlich habe ich immer genug von dem, worüber ich geschrieben habe (eigentlich sogar noch weniger). Wenn Sie diese oder ähnliche Hilfsmittel bei Tests in Ihrem Unternehmen verwenden, schreiben Sie bitte genau, wie. Wenn nicht, hoffe ich, dass Ihre Software besser wird, wenn Sie sie mit den vorgeschlagenen Methoden unter Bedingungen von Netzwerkproblemen testen.

Source: habr.com

Kommentar hinzufügen