Simulation von Netzwerkproblemen in Linux.

Hallo zusammen, mein Name ist Sasha und ich leite die Backend-Tests bei FunCorp. Wie viele andere Unternehmen haben wir eine serviceorientierte Architektur implementiert. Einerseits erleichtert dies die Arbeit, da jeder Dienst einfacher einzeln getestet werden kann. Andererseits entsteht die Notwendigkeit, die Interaktion zwischen den Diensten zu testen, die häufig über das Netzwerk erfolgt.

In diesem Artikel werde ich über zwei Tools berichten, mit denen grundlegende Szenarien überprüft werden können, die die Funktionsweise der Anwendung bei Netzwerkproblemen beschreiben.

Simulation von Netzwerkproblemen in Linux.

Netzwerkprobleme simulieren

Normalerweise wird Software auf Testservern mit guter Internetverbindung getestet. Unter realen Produktionsbedingungen kann jedoch alles anders aussehen, weshalb es manchmal notwendig ist, Programme unter schlechten Verbindungsbedingungen zu überprüfen. In Linux hilft das Tool tc.

tc (kurz für Traffic Control) ermöglicht es, die Übertragung von Netzwerkpaketen im System zu konfigurieren. Dieses Tool bietet umfangreiche Funktionen, über die man mehr erfahren kann hier. Hier werde ich nur einige davon betrachten: uns interessiert das Traffic Scheduling, wofür wir qdisc, und da wir ein instabiles Netzwerk emulieren müssen, verwenden wir classless qdisc netem.

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

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

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

Der Quellcode des Clients

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

Lassen Sie uns ihn ausführen und den Datenverkehr am Interface lo und Port 12345 beobachten:

[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

Datenverkehrs-Dump

[user@host ~]# tcpdump -i lo -nn port 12345
tcpdump: Ausführliche Ausgabe unterdrückt, verwenden Sie -v oder -vv für die volle Protokollanalyse
höre auf lo, Linktyp EN10MB (Ethernet), Erfassungsgröße 262144 Bytes
10:42:59.448601 IP 127.0.0.1.54054 > 127.0.0.1.12345: Flags [S], seq 3383332866, win 43690, Optionen [mss 65495,sackOK,TS val 606325685 ecr 0,nop,wscale 7], Länge 0
10:42:59.448612 IP 127.0.0.1.12345 > 127.0.0.1.54054: Flags [S.], seq 2584700178, ack 3383332867, win 43690, Optionen [mss 65495,sackOK,TS val 606325685 ecr 606325685,nop,wscale 7], Länge 0
10:42:59.448622 IP 127.0.0.1.54054 > 127.0.0.1.12345: Flags [.], ack 1, win 342, Optionen [nop,nop,TS val 606325685 ecr 606325685], Länge 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, Optionen [nop,nop,TS val 606325685 ecr 606325685], Länge 5
10:42:59.448930 IP 127.0.0.1.12345 > 127.0.0.1.54054: Flags [.], ack 6, win 342, Optionen [nop,nop,TS val 606325685 ecr 606325685], Länge 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, Optionen [nop,nop,TS val 606325696 ecr 606325685], Länge 14
10:42:59.459213 IP 127.0.0.1.54054 > 127.0.0.1.12345: Flags [.], ack 15, win 342, Optionen [nop,nop,TS val 606325696 ecr 606325696], Länge 0
10:42:59.459268 IP 127.0.0.1.54054 > 127.0.0.1.12345: Flags [F.], seq 6, ack 15, win 342, Optionen [nop,nop,TS val 606325696 ecr 606325696], Länge 0
10:42:59.460184 IP 127.0.0.1.12345 > 127.0.0.1.54054: Flags [F.], seq 15, ack 7, win 342, Optionen [nop,nop,TS val 606325697 ecr 606325696], Länge 0
10:42:59.460196 IP 127.0.0.1.54054 > 127.0.0.1.12345: Flags [.], ack 16, win 342, Optionen [nop,nop,TS val 606325697 ecr 606325697], Länge 0

Alles standardmäßig: ein Drei-Wege-Handschlag, PSH/ACK und ACK als Antwort zweimal – das ist der Austausch von Anfrage und Antwort zwischen Client und Server, sowie zweimal FIN/ACK und ACK – Abschluss der Verbindung.

Paketverzögerung

Jetzt setzen wir eine Verzögerung von 500 Millisekunden ein:

tc qdisc add dev lo root netem delay 500ms

Wir starten den Client und sehen, dass das Skript jetzt 2 Sekunden lang ausgeführt wird:

[user@host ~]# ./client.py
[Zeit vor der Verbindung: 1578662612.71044]
[Zeit nach der Verbindung, vor dem Senden: 1578662613.71059]
[Zeit nach dem Senden, vor dem Empfangen: 1578662613.71065]
[Zeit nach dem Empfangen, vor dem Schließen: 1578662614.72011]
[Zeit nach dem Schließen: 1578662614.72019]
[Gesamtdauer: 2.00974]
Antwort: Test

Was ist im Verkehr? Schauen wir mal:

Datenverkehrs-Dump

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

Es ist zu erkennen, dass bei der Interaktion zwischen Client und Server eine erwartete Verzögerung von einer halben Sekunde aufgetreten ist. Viel interessanter verhält sich das System, wenn die Verzögerung größer ist: Der Kernel beginnt, einige TCP-Pakete erneut zu senden. Lassen Sie uns die Verzögerung auf 1 Sekunde ändern und den Verkehr beobachten (ich werde die Ausgabe des Clients nicht zeigen, dort sind erwartete 4 Sekunden in der Gesamtdauer):

tc qdisc change dev lo root netem delay 1s

Datenverkehrs-Dump

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 zu erkennen, dass der Client das SYN-Paket zweimal gesendet hat, während der Server zweimal SYN/ACK gesendet hat.

Neben dem konstanten Wert kann für die Verzögerung auch eine Abweichung, eine Verteilungsfunktion und eine Korrelation (mit dem Wert des vorherigen Pakets) festgelegt werden. Dies geschieht folgendermaßen:

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

Hier haben wir eine Verzögerung im Bereich von 100 bis 900 Millisekunden festgelegt. Die Werte werden entsprechend der Normalverteilung ausgewählt und es wird eine 50-prozentige Korrelation mit dem Verzögerungswert des vorherigen Pakets bestehen.

Sie haben vielleicht bemerkt, dass ich im ersten Befehl das add, und dann auf changeverwendet habe. Der Wert dieser Befehle ist offensichtlich, deshalb füge ich nur hinzu, dass es auch noch das delgibt, mit dem Sie die Konfiguration entfernen können.

Paketverlust

Lassen Sie uns nun versuchen, Paketverluste zu erzeugen. Wie aus der Dokumentation ersichtlich, kann dies auf drei Arten erfolgen: Pakete zufällig mit einer bestimmten Wahrscheinlichkeit zu verlieren, eine Markow-Kette aus 2, 3 oder 4 Zuständen zur Berechnung des Paketverlusts zu verwenden oder das Elliott-Gilbert-Modell zu nutzen. In diesem Artikel werde ich die erste (einfachste und offensichtlichste) Methode behandeln; über die anderen können Sie lesen. hier.

Wir simulieren einen Paketverlust von 50% mit einer Korrelation von 25%:

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

Leider, tcpdump kann uns nicht direkt den Paketverlust zeigen; wir werden nur annehmen, dass es wirklich funktioniert. Eine Bestätigung liefert uns die erhöhte und instabile Ausführungszeit des Skripts client.py (es kann sofort ausgeführt werden oder bis zu 20 Sekunden dauern), sowie die gestiegene Anzahl an retransmierten Paketen:

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

Hinzufügen von Rauschen zu Paketen

Neben Paketverlust kann auch deren Beschädigung simuliert werden: An einer zufälligen Stelle im Paket tritt Rauschen auf. Wir erzeugen eine Beschädigung der Pakete mit einer Wahrscheinlichkeit von 50% und ohne Korrelation:

tc qdisc change dev lo root netem corrupt 50%

Wir starten das Client-Skript (nichts Interessantes, aber es wurde 2 Sekunden ausgeführt) und schauen uns den Traffic an:

Datenverkehrs-Dump

[user@host ~]# tcpdump -i lo -nn port 12345
tcpdump: ausführliche Ausgabe unterdrückt, verwenden Sie -v oder -vv für die vollständige Protokollanalyse
lauscht auf lo, Linktyp EN10MB (Ethernet), Erfassungsgröße 262144 Bytes
10:20:54.812434 IP 127.0.0.1.43666 > 127.0.0.1.12345: Flags [S], seq 2023663770, win 43690, Optionen [mss 65495,sackOK,TS val 1037001049 ecr 0,nop,wscale 7], Länge 0
10:20:54.812449 IP 127.0.0.1.12345 > 127.0.0.1.43666: Flags [S.], seq 2104268044, ack 2023663771, win 43690, Optionen [mss 65495,sackOK,TS val 1037001049 ecr 1037001049,nop,wscale 7], Länge 0
10:20:54.812458 IP 127.0.0.1.43666 > 127.0.0.1.12345: Flags [.], ack 1, win 342, Optionen [nop,nop,TS val 1037001049 ecr 1037001049], Länge 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, Optionen [nop,nop,TS val 1037001049 ecr 1037001049], Länge 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, Optionen [nop,nop,TS val 1037001250 ecr 1037001049], Länge 5
10:20:55.013122 IP 127.0.0.1.12345 > 127.0.0.1.43666: Flags [.], ack 6, win 342, Optionen [nop,nop,TS val 1037001250 ecr 1037001250], Länge 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, Optionen [nop,nop,TS val 1037001251 ecr 1037001250], Länge 14
10:20:55.014745 IP 127.0.0.1.43666 > 127.0.0.1.12345: Flags [.], ack 15, win 340, Optionen [nop,nop,TS val 1037001251 ecr 1037001251], Länge 0
10:20:55.014823 IP 127.0.0.1.43666 > 127.0.0.5.12345: Flags [F.], seq 2023663776, ack 2104268059, win 342, Optionen [nop,nop,TS val 1037001251 ecr 1037001251], Länge 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, Optionen [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, Optionen [nop,nop,TS val 1037001653 ecr 1037001251], Länge 0
10:20:55.416804 IP 127.0.0.1.12345 > 127.0.0.1.43666: Flags [F.], seq 15, ack 7, win 342, Optionen [nop,nop,TS val 1037001653 ecr 1037001653], Länge 0
10:20:55.416818 IP 127.0.0.1.43666 > 127.0.0.1.12345: Flags [.], ack 16, win 343, Optionen [nop,nop,TS val 1037001653 ecr 1037001653], Länge 0
10:20:56.147086 IP 127.0.0.1.12345 > 127.0.0.1.43666: Flags [F.], seq 15, ack 7, win 342, Optionen [nop,nop,TS val 1037002384 ecr 1037001653], Länge 0
10:20:56.147101 IP 127.0.0.1.43666 > 127.0.0.1.12345: Flags [.], ack 16, win 342, Optionen [nop,nop,TS val 1037002384 ecr 1037001653], Länge 0

Es ist zu sehen, dass einige Pakete wiederholt gesendet wurden und es ein Paket mit beschädigten Metadaten gibt: options [nop,unknown-65 0x0a3dcf62eb3d,[bad opt]>. Aber das Wichtigste ist, dass am Ende alles korrekt funktioniert hat – TCP hat seine Aufgabe erfüllt.

Paketdublikation

Was kann man noch mit netem? Например, сымитировать ситуацию, обратную потере пакетов, — дубликацию пакетов. Эта команда также принимает 2 аргумента: вероятность и корреляцию.

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

Änderung der Paketreihenfolge

Es ist möglich, Pakete auf zwei Arten zu mischen.

Beim ersten Teil werden einige Pakete sofort gesendet, die restlichen mit einer bestimmten 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, die anderen werden mit einer Verzögerung von 10 Millisekunden gesendet.

Bei der zweiten Methode wird jedes N-te Paket sofort mit einer bestimmten Wahrscheinlichkeit (und Korrelation) gesendet, während die anderen mit einer bestimmten Verzögerung gesendet werden. Beispiel aus der Dokumentation:

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

Jedes fünfte Paket wird mit einer Wahrscheinlichkeit von 25 % ohne Verzögerung gesendet.

Änderung der Bandbreite

Normalerweise wird überall auf TBF, verwiesen, aber auch mit netem kann die Bandbreite der Schnittstelle geändert werden:

tc qdisc change dev lo root netem rate 56kbit

Dieser Befehl wird die Zugriffe auf localhost genauso qualvoll wie das Surfen im Internet über ein Dial-up-Modem. Neben der Einstellung der Bitrate können Sie auch das Protokollmodell der Netzwerkschicht emulieren: die Overhead-Größe für das Paket, die Zellgröße und den Overhead für die Zelle festlegen. So könnten Sie zum Beispiel simulieren ATM und eine Bitrate von 56 kbit/s.:

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

Wir simulieren einen Connection-Timeout

Ein weiterer wichtiger Punkt im Testplan bei der Abnahme von Software sind die Timeouts. Dies ist entscheidend, denn in verteilten Systemen müssen die anderen Dienste rechtzeitig auf alternative Dienste umschalten oder eine Fehlermeldung an den Kunden zurückgeben, wenn einer der Dienste ausfällt. Sie dürfen dabei auf keinen Fall einfach festhängen und auf eine Antwort oder eine Verbindung warten.

Es gibt verschiedene Möglichkeiten, dies zu erreichen: Sie können beispielsweise ein Mock-Objekt verwenden, das nicht antwortet, oder sich mit einem Debugger in den Prozess einklinken, an der gewünschten Stelle einen Breakpoint setzen und die Ausführung des Prozesses anhalten (das ist wohl die extremste Methode). Eine der offensichtlichsten Methoden ist jedoch, Ports oder Hosts zu blockieren. Dabei können wir auf die Hilfe von iptables.

Um eine Demonstration zu geben, werden wir den Port 12345 firewallen und unser Client-Skript ausführen. Man kann die ausgehenden Pakete an diesen Port beim Sender oder die eingehenden am Empfänger firewallen. In meinen Beispielen werden die eingehenden Pakete gefirewallt (wir verwenden die Chain INPUT und die Option —dport). Solchen Paketen kann man DROP, REJECT oder REJECT mit dem TCP-Flag RST zuweisen; man kann auch ICMP-Host-unreachable verwenden (das Standardverhalten ist tatsächlich icmp-port-unreachable, und zudem gibt es die Möglichkeit, als Antwort zu senden icmp-net-unreachable, icmp-proto-unreachable, icmp-net-prohibited und icmp-host-prohibited).

LÖSCHEN

Wenn eine Regel mit DROP vorhanden ist, verschwinden die Pakete einfach.

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

Wir starten den Client und sehen, dass er beim Verbindungsaufbau zum Server hängen bleibt. Lass uns den Verkehr betrachten:
Datenverkehrs-Dump

[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 exponentiell zunehmenden Timeouts sendet. Hier haben wir einen kleinen Fehler im Client gefunden: Es sollte die Methode settimeout(), um die Zeit einzuschränken, innerhalb der der Client versucht, eine Verbindung zum Server herzustellen.

Das Regel sofort löschen:

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

Man kann alle Regeln sofort löschen:

iptables -F

Wenn Sie Docker verwenden und den gesamten Traffic, der an den Container geht, sperren möchten, können Sie dies wie folgt tun:

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

REJECT

Jetzt fügen wir eine ähnliche Regel hinzu, aber mit REJECT:

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

Der Client beendet sich nach einer Sekunde mit einem Fehler [Errno 111] Verbindung abgelehnt. Betrachten wir den ICMP-Verkehr:

[user@host ~]# tcpdump -i lo -nn icmp
tcpdump: detaillierte Ausgabe unterdrückt, verwenden Sie -v oder -vv für die vollständige Protokolldekodierung
hört auf lo, Linktyp EN10MB (Ethernet), Erfassungsgröße 262144 Bytes
08:45:32.871414 IP 127.0.0.1 > 127.0.0.1: ICMP 127.0.0.1 tcp-Port 12345 unerreichbar, Länge 68
08:45:33.873097 IP 127.0.0.1 > 127.0.0.1: ICMP 127.0.0.1 tcp-Port 12345 unerreichbar, Länge 68

Es ist zu sehen, dass der Client zweimal erhalten hat port unerreichbar und sich danach mit einem Fehler beendet hat.

REJECT mit tcp-reset

Lassen Sie uns die Option hinzufügen —reject-with tcp-reset:

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

In diesem Fall beendet sich der Client sofort mit einem Fehler, weil er bei der ersten Anfrage ein RST-Paket erhalten hat:

[user@host ~]# tcpdump -i lo -nn port 12345
tcpdump: detaillierte Ausgabe unterdrückt, verwenden Sie -v oder -vv für die vollständige Protokolldekodierung
hört auf lo, Linktyp EN10MB (Ethernet), Erfassungsgröße 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], Länge 0
09:02:52.766184 IP 127.0.0.1.12345 > 127.0.0.1.60658: Flags [R.], seq 0, ack 1889460884, win 0, Länge 0

REJECT mit icmp-host-unreachable

Probieren wir eine weitere Variante der Verwendung von REJECT:

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

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

Sie können auch die anderen REJECT-Parameter ausprobieren, aber ich werde bei diesen bleiben 🙂

Simulieren wir einen Anfrage-Timeout

Eine weitere Situation ist, wenn der Kunde sich mit dem Server verbinden kann, jedoch keine Anfrage senden kann. Wie kann man Pakete filtern, sodass die Filterung nicht sofort beginnt? Wenn man den Datenverkehr zwischen dem Client und dem Server betrachtet, kann man bemerken, dass bei der Verbindungsherstellung nur die SYN- und ACK-Flags verwendet werden. Im letzten Paket der Anfrage wird jedoch das PSH-Flag gesetzt. Dieses wird automatisch gesetzt, um Pufferung zu vermeiden. Diese Information kann genutzt werden, um einen Filter zu erstellen: er lässt alle Pakete durch, außer denjenigen, die das PSH-Flag enthalten. Auf diese Weise wird die Verbindung hergestellt, aber der Client kann keine Daten an den Server senden.

LÖSCHEN

Für DROP sieht der Befehl wie folgt aus:

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

Wir starten den Client und beobachten den Datenverkehr:

Datenverkehrs-Dump

[user@host ~]# tcpdump -i lo -nn port 12345
tcpdump: Ausführliche Ausgabe unterdrückt, verwenden Sie -v oder -vv für vollständige Protokoll-Dekodierung
lauscht auf lo, Linktyp EN10MB (Ethernet), Erfassungsgröße 262144 Bytes
10:02:47.549498 IP 127.0.0.1.49594 > 127.0.0.1.12345: Flags [S], seq 2166014137, win 43690, Optionen [mss 65495,sackOK,TS val 1208713786 ecr 0,nop,wscale 7], Länge 0
10:02:47.549510 IP 127.0.0.1.12345 > 127.0.0.1.49594: Flags [S.], seq 2341799088, ack 2166014138, win 43690, Optionen [mss 65495,sackOK,TS val 1208713786 ecr 1208713786,nop,wscale 7], Länge 0
10:02:47.549520 IP 127.0.0.1.49594 > 127.0.0.1.12345: Flags [.], ack 1, win 342, Optionen [nop,nop,TS val 1208713786 ecr 1208713786], Länge 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, Optionen [nop,nop,TS val 1208713786 ecr 1208713786], Länge 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, Optionen [nop,nop,TS val 1208713987 ecr 1208713786], Länge 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, Optionen [nop,nop,TS val 1208714188 ecr 1208713786], Länge 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, Optionen [nop,nop,TS val 1208714591 ecr 1208713786], Länge 5

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

REJECT

In diesem Fall wird das Verhalten dasselbe sein: Der Client kann keine Anfrage senden, erhält aber ICMP 127.0.0.1 tcp port 12345 unerreichbar und erhöht die Zeit zwischen den Wiederholungsanfragen exponentiell. Der Befehl sieht folgendermaßen aus:

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

REJECT mit tcp-reset

Der Befehl sieht wie folgt aus:

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

Wir wissen bereits, dass bei Verwendung von —reject-with tcp-reset der Client ein RST-Paket als Antwort erhält, wodurch man das Verhalten vorhersagen kann: Der Erhalt eines RST-Pakets bei einer etablierten Verbindung bedeutet, dass die Verbindung von der anderen Seite unerwartet geschlossen wurde, was bedeutet, dass der Client erhalten sollte Verbindung zurückgesetzt durch Peer. Wir führen unser Skript aus und vergewissern uns davon. So könnte der Datenverkehr aussehen:

Datenverkehrs-Dump

[user@host ~]# tcpdump -i lo -nn port 12345
tcpdump: Ausführliche Ausgabe unterdrückt, verwenden Sie -v oder -vv für die vollständige Protokolldekodierung
hörend auf lo, Linktyp EN10MB (Ethernet), Erfassungsgröße 262144 Bytes
10:22:14.186269 IP 127.0.0.1.52536 > 127.0.0.1.12345: Flags [S], seq 2615137531, win 43690, Optionen [mss 65495,sackOK,TS val 1209880423 ecr 0,nop,wscale 7], Länge 0
10:22:14.186284 IP 127.0.0.1.12345 > 127.0.0.1.52536: Flags [S.], seq 3999904809, ack 2615137532, win 43690, Optionen [mss 65495,sackOK,TS val 1209880423 ecr 1209880423,nop,wscale 7], Länge 0
10:22:14.186293 IP 127.0.0.1.52536 > 127.0.0.1.12345: Flags [.], ack 1, win 342, Optionen [nop,nop,TS val 1209880423 ecr 1209880423], Länge 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, Optionen [nop,nop,TS val 1209880423 ecr 1209880423], Länge 5
10:22:14.186344 IP 127.0.0.1.12345 > 127.0.0.1.52536: Flags [R], seq 3999904810, win 0, Länge 0

REJECT mit icmp-host-unreachable

Es ist wahrscheinlich bereits für alle offensichtlich, wie das Team aussehen wird 🙂 Das Verhalten des Clients wird in diesem Fall etwas anders sein als bei einem einfachen REJECT: Der Client wird die Timeout-Zeit zwischen den erneuten Versuchen, das Paket zu senden, nicht erhöhen.

[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

Fazit

Es ist nicht notwendig, ein Mock zu schreiben, um die Interaktion des Dienstes mit einem hängenden Client oder Server zu überprüfen, manchmal reicht es aus, die Standardutilities zu verwenden, die in Linux verfügbar sind.

Die in diesem Artikel behandelten Tools bieten noch mehr Funktionen, als beschrieben, sodass Sie Ihre eigenen Verwendungsmöglichkeiten entwickeln können. Persöliche Erfahrungen zeigen mir, dass das, worüber ich geschrieben habe, oft bereits ausreichend ist. Wenn Sie diese oder ähnliche Tools in Ihren Unternehmensprüfungen einsetzen, lassen Sie es mich bitte wissen, wie genau. Wenn nicht, hoffe ich, dass Ihre Software qualitativ besser wird, wenn Sie beschließen, sie mit den vorgeschlagenen Methoden unter Netzwerkproblemen zu überprüfen.

Quelle: habr.com

Zuverlässiges Webhosting mit DDoS-Schutz, VPS- und VDS-Server kaufen 🔥 Zuverlässiges Webhosting mit DDoS-Schutz, VPS- und VDS-Server kaufen | ProHoster