Προσομοίωση προβλημάτων δικτύου στο Linux

Γεια σε όλους, με λένε Sasha, ηγούμαι δοκιμών backend στο FunCorp. Εμείς, όπως πολλοί άλλοι, έχουμε εφαρμόσει μια αρχιτεκτονική προσανατολισμένη στις υπηρεσίες. Από τη μία, αυτό απλοποιεί τη δουλειά, γιατί... Είναι πιο εύκολο να δοκιμάσετε κάθε υπηρεσία ξεχωριστά, αλλά από την άλλη πλευρά, υπάρχει η ανάγκη δοκιμής της αλληλεπίδρασης των υπηρεσιών μεταξύ τους, η οποία συμβαίνει συχνά μέσω του δικτύου.

Σε αυτό το άρθρο, θα μιλήσω για δύο βοηθητικά προγράμματα που μπορούν να χρησιμοποιηθούν για τον έλεγχο βασικών σεναρίων που περιγράφουν τη λειτουργία μιας εφαρμογής παρουσία προβλημάτων δικτύου.

Προσομοίωση προβλημάτων δικτύου στο Linux

Προσομοίωση προβλημάτων δικτύου

Συνήθως, το λογισμικό δοκιμάζεται σε δοκιμαστικούς διακομιστές με καλή σύνδεση στο Διαδίκτυο. Σε σκληρά περιβάλλοντα παραγωγής, τα πράγματα μπορεί να μην είναι τόσο ομαλά, επομένως μερικές φορές χρειάζεται να δοκιμάσετε προγράμματα σε κακές συνθήκες σύνδεσης. Στο Linux, το βοηθητικό πρόγραμμα θα βοηθήσει στην προσομοίωση τέτοιων συνθηκών tc.

tc(συντομ. από τον Έλεγχο Κυκλοφορίας) σας επιτρέπει να διαμορφώσετε τη μετάδοση πακέτων δικτύου στο σύστημα. Αυτό το βοηθητικό πρόγραμμα έχει εξαιρετικές δυνατότητες, μπορείτε να διαβάσετε περισσότερα για αυτές εδώ. Εδώ θα εξετάσω μόνο μερικά από αυτά: μας ενδιαφέρει ο προγραμματισμός της κυκλοφορίας, για τον οποίο χρησιμοποιούμε qdisc, και δεδομένου ότι πρέπει να προσομοιώσουμε ένα ασταθές δίκτυο, θα χρησιμοποιήσουμε qdisc χωρίς κλάση netem.

Ας ξεκινήσουμε έναν διακομιστή echo στον διακομιστή (χρησιμοποίησα nmap-ncat):

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

Προκειμένου να εμφανιστούν λεπτομερώς όλες οι χρονικές σημάνσεις σε κάθε βήμα αλληλεπίδρασης μεταξύ του πελάτη και του διακομιστή, έγραψα ένα απλό σενάριο Python που στέλνει ένα αίτημα Δοκιμή στον διακομιστή μας echo.

Πηγαίος κώδικας πελάτη

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

Ας το εκκινήσουμε και ας δούμε την κίνηση στη διεπαφή lo και θύρα 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

Χωματερή κυκλοφορίας

[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

Όλα είναι στάνταρ: μια τριπλή χειραψία, PSH/ACK και ACK σε απόκριση δύο φορές - αυτή είναι η ανταλλαγή αιτήματος και απόκρισης μεταξύ του πελάτη και του διακομιστή και FIN/ACK και ACK δύο φορές - ολοκληρώνοντας τη σύνδεση.

Καθυστέρηση πακέτων

Τώρα ας ορίσουμε την καθυστέρηση στα 500 χιλιοστά του δευτερολέπτου:

tc qdisc add dev lo root netem delay 500ms

Εκκινούμε τον πελάτη και βλέπουμε ότι το σενάριο εκτελείται τώρα για 2 δευτερόλεπτα:

[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

Τι υπάρχει στην κίνηση; Ας κοιτάξουμε:

Χωματερή κυκλοφορίας

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

Μπορείτε να δείτε ότι η αναμενόμενη καθυστέρηση μισού δευτερολέπτου εμφανίστηκε στην αλληλεπίδραση μεταξύ του πελάτη και του διακομιστή. Το σύστημα συμπεριφέρεται πολύ πιο ενδιαφέροντα εάν η καθυστέρηση είναι μεγαλύτερη: ο πυρήνας αρχίζει να στέλνει ξανά ορισμένα πακέτα TCP. Ας αλλάξουμε την καθυστέρηση σε 1 δευτερόλεπτο και ας δούμε την κίνηση (δεν θα δείξω την έξοδο του πελάτη, υπάρχουν τα αναμενόμενα 4 δευτερόλεπτα σε συνολική διάρκεια):

tc qdisc change dev lo root netem delay 1s

Χωματερή κυκλοφορίας

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

Μπορεί να φανεί ότι ο πελάτης έστειλε ένα πακέτο SYN δύο φορές και ο διακομιστής έστειλε ένα SYN/ACK δύο φορές.

Εκτός από μια σταθερή τιμή, η καθυστέρηση μπορεί να οριστεί σε μια απόκλιση, μια συνάρτηση διανομής και μια συσχέτιση (με την τιμή για το προηγούμενο πακέτο). Αυτό γίνεται ως εξής:

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

Εδώ έχουμε ορίσει την καθυστέρηση μεταξύ 100 και 900 χιλιοστών του δευτερολέπτου, οι τιμές θα επιλεγούν σύμφωνα με μια κανονική κατανομή και θα υπάρχει 50% συσχέτιση με την τιμή καθυστέρησης για το προηγούμενο πακέτο.

Ίσως έχετε παρατηρήσει ότι στην πρώτη εντολή που χρησιμοποίησα προσθέτωκαι στη συνέχεια αλλαγή. Το νόημα αυτών των εντολών είναι προφανές, οπότε θα προσθέσω απλώς ότι υπάρχουν περισσότερες del, το οποίο μπορεί να χρησιμοποιηθεί για την κατάργηση της διαμόρφωσης.

Απώλεια πακέτων

Ας προσπαθήσουμε τώρα να κάνουμε απώλεια πακέτων. Όπως φαίνεται από την τεκμηρίωση, αυτό μπορεί να γίνει με τρεις τρόπους: χάνοντας τυχαία πακέτα με κάποια πιθανότητα, χρησιμοποιώντας μια αλυσίδα Markov 2, 3 ή 4 καταστάσεων για τον υπολογισμό της απώλειας πακέτων ή χρησιμοποιώντας το μοντέλο Elliott-Gilbert. Στο άρθρο θα εξετάσω την πρώτη (απλότερη και πιο προφανή) μέθοδο και μπορείτε να διαβάσετε για άλλες εδώ.

Ας κάνουμε την απώλεια του 50% των πακέτων με συσχέτιση 25%:

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

Δυστυχώς, tcpdump δεν θα είναι σε θέση να μας δείξει ξεκάθαρα την απώλεια πακέτων, θα υποθέσουμε μόνο ότι λειτουργεί πραγματικά. Και ο αυξημένος και ασταθής χρόνος εκτέλεσης του σεναρίου θα μας βοηθήσει να το επαληθεύσουμε. client.py (μπορεί να ολοκληρωθεί άμεσα, ή ίσως σε 20 δευτερόλεπτα), καθώς και αυξημένος αριθμός πακέτων που αναμεταδίδονται:

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

Προσθήκη θορύβου στα πακέτα

Εκτός από την απώλεια πακέτων, μπορείτε να προσομοιώσετε τη ζημιά πακέτων: θα εμφανιστεί θόρυβος σε μια τυχαία θέση πακέτου. Ας κάνουμε ζημιά πακέτων με πιθανότητα 50% και χωρίς συσχέτιση:

tc qdisc change dev lo root netem corrupt 50%

Εκτελούμε το σενάριο πελάτη (τίποτα ενδιαφέρον εκεί, αλλά χρειάστηκαν 2 δευτερόλεπτα για να ολοκληρωθεί), κοιτάξτε την κίνηση:

Χωματερή κυκλοφορίας

[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

Μπορεί να φανεί ότι ορισμένα πακέτα στάλθηκαν επανειλημμένα και υπάρχει ένα πακέτο με σπασμένα μεταδεδομένα: επιλογές [nop,unknown-65 0x0a3dcf62eb3d,[bad opt]>. Αλλά το κύριο πράγμα είναι ότι στο τέλος όλα λειτούργησαν σωστά - το TCP αντιμετώπισε το έργο του.

Αντιγραφή πακέτων

Με τι άλλο μπορείτε να κάνετε netem? Για παράδειγμα, προσομοιώστε την αντίστροφη κατάσταση απώλειας πακέτων—διπλασιασμός πακέτων. Αυτή η εντολή παίρνει επίσης 2 ορίσματα: πιθανότητα και συσχέτιση.

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

Αλλαγή σειράς πακέτων

Μπορείτε να αναμίξετε τις σακούλες με δύο τρόπους.

Στην πρώτη, ορισμένα πακέτα αποστέλλονται αμέσως, τα υπόλοιπα με καθορισμένη καθυστέρηση. Παράδειγμα από την τεκμηρίωση:

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

Με πιθανότητα 25% (και συσχέτιση 50%) το πακέτο θα σταλεί αμέσως, το υπόλοιπο θα σταλεί με καθυστέρηση 10 χιλιοστών του δευτερολέπτου.

Η δεύτερη μέθοδος είναι όταν κάθε Nο πακέτο αποστέλλεται αμέσως με δεδομένη πιθανότητα (και συσχέτιση) και το υπόλοιπο με δεδομένη καθυστέρηση. Παράδειγμα από την τεκμηρίωση:

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

Κάθε πέμπτο πακέτο έχει 25% πιθανότητα να σταλεί χωρίς καθυστέρηση.

Αλλαγή εύρους ζώνης

Συνήθως παντού αναφέρονται ΔΕΒΘ, αλλά με τη βοήθεια netem Μπορείτε επίσης να αλλάξετε το εύρος ζώνης διεπαφής:

tc qdisc change dev lo root netem rate 56kbit

Αυτή η ομάδα θα κάνει πεζοπορίες localhost τόσο επώδυνο όσο το σερφάρισμα στο Διαδίκτυο μέσω dial-up modem. Εκτός από τη ρύθμιση του bitrate, μπορείτε επίσης να προσομοιώσετε το μοντέλο πρωτοκόλλου επιπέδου σύνδεσης: ορίστε την επιβάρυνση για το πακέτο, το μέγεθος κελιού και την επιβάρυνση για το κελί. Για παράδειγμα, αυτό μπορεί να προσομοιωθεί ΑΤΜ και bitrate 56 kbit/sec:

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

Προσομοίωση χρονικού ορίου σύνδεσης

Ένα άλλο σημαντικό σημείο στο σχέδιο δοκιμής κατά την αποδοχή λογισμικού είναι τα χρονικά όρια. Αυτό είναι σημαντικό γιατί στα κατανεμημένα συστήματα, όταν μια από τις υπηρεσίες είναι απενεργοποιημένη, οι άλλες πρέπει να επιστρέψουν στις άλλες εγκαίρως ή να επιστρέψουν ένα σφάλμα στον πελάτη και σε καμία περίπτωση δεν πρέπει απλώς να κολλήσουν, περιμένοντας απάντηση ή σύνδεση να καθιερωθεί.

Υπάρχουν διάφοροι τρόποι για να το κάνετε αυτό: για παράδειγμα, χρησιμοποιήστε ένα mock που δεν ανταποκρίνεται ή συνδεθείτε στη διαδικασία χρησιμοποιώντας ένα πρόγραμμα εντοπισμού σφαλμάτων, βάλτε ένα σημείο διακοπής στη σωστή θέση και σταματήστε τη διαδικασία (αυτός είναι ίσως ο πιο διαστρεβλωμένος τρόπος). Αλλά ένα από τα πιο προφανή είναι να τείχος προστασίας θύρες ή κεντρικούς υπολογιστές. Θα μας βοηθήσει σε αυτό iptables.

Για επίδειξη, θα τείχος προστασίας τη θύρα 12345 και θα εκτελέσουμε το σενάριο πελάτη μας. Μπορείτε να τείχος προστασίας εξερχόμενα πακέτα σε αυτή τη θύρα στον αποστολέα ή εισερχόμενα πακέτα στον δέκτη. Στα παραδείγματά μου, τα εισερχόμενα πακέτα θα έχουν τείχος προστασίας (χρησιμοποιούμε Chain INPUT και την επιλογή --dport). Τέτοια πακέτα μπορεί να είναι DROP, REJECT ή REJECT με τη σημαία TCP RST ή με μη προσβάσιμο κεντρικό υπολογιστή ICMP (στην πραγματικότητα, η προεπιλεγμένη συμπεριφορά είναι icmp-port-μη προσβάσιμο, και υπάρχει επίσης η ευκαιρία να στείλετε μια απάντηση icmp-net-απρόσιτο, icmp-proto-απρόσιτο, icmp-net-απαγορεύεται и icmp-host-απαγορεύεται).

DROP

Εάν υπάρχει κανόνας με το DROP, τα πακέτα απλά θα "εξαφανιστούν".

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

Εκκινούμε τον πελάτη και βλέπουμε ότι παγώνει στο στάδιο της σύνδεσης με τον διακομιστή. Ας δούμε την κίνηση:
Χωματερή κυκλοφορίας

[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

Μπορεί να φανεί ότι ο πελάτης στέλνει πακέτα SYN με εκθετικά αυξανόμενο χρονικό όριο. Βρήκαμε λοιπόν ένα μικρό σφάλμα στον πελάτη: πρέπει να χρησιμοποιήσετε τη μέθοδο settimeout()για να περιορίσετε το χρόνο κατά τον οποίο ο πελάτης θα προσπαθήσει να συνδεθεί στον διακομιστή.

Καταργούμε αμέσως τον κανόνα:

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

Μπορείτε να διαγράψετε όλους τους κανόνες ταυτόχρονα:

iptables -F

Εάν χρησιμοποιείτε το Docker και πρέπει να τείχος προστασίας όλη η κίνηση που πηγαίνει στο κοντέινερ, τότε μπορείτε να το κάνετε ως εξής:

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

ΑΠΟΡΡΙΠΤΩ

Τώρα ας προσθέσουμε έναν παρόμοιο κανόνα, αλλά με ΑΠΟΡΡΙΨΗ:

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

Ο πελάτης εξέρχεται μετά από ένα δευτερόλεπτο με σφάλμα [Errno 111] Η σύνδεση απορρίφθηκε. Ας δούμε την κίνηση του ICMP:

[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

Μπορεί να φανεί ότι ο πελάτης έλαβε δύο φορές λιμάνι απρόσιτο και μετά τελείωσε με λάθος.

ΑΠΟΡΡΙΨΗ με tcp-reset

Ας προσπαθήσουμε να προσθέσουμε την επιλογή --απόρριψη-με tcp-reset:

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

Σε αυτήν την περίπτωση, ο πελάτης εξέρχεται αμέσως με ένα σφάλμα, επειδή το πρώτο αίτημα έλαβε ένα πακέτο RST:

[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

ΑΠΟΡΡΙΨΗ με icmp-host-unreachable

Ας δοκιμάσουμε μια άλλη επιλογή για τη χρήση του REJECT:

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

Ο πελάτης εξέρχεται μετά από ένα δευτερόλεπτο με σφάλμα [Errno 113] Δεν υπάρχει διαδρομή προς φιλοξενία, βλέπουμε στην κίνηση ICMP Ο κεντρικός υπολογιστής ICMP 127.0.0.1 δεν είναι προσβάσιμος.

Μπορείτε επίσης να δοκιμάσετε τις υπόλοιπες παραμέτρους REJECT και θα επικεντρωθώ σε αυτές :)

Προσομοίωση χρονικού ορίου λήξης αιτήματος

Μια άλλη κατάσταση είναι όταν ο πελάτης μπόρεσε να συνδεθεί στον διακομιστή, αλλά δεν μπορεί να στείλει αίτημα σε αυτόν. Πώς να φιλτράρετε τα πακέτα έτσι ώστε το φιλτράρισμα να μην ξεκινά αμέσως; Εάν κοιτάξετε την κίνηση οποιασδήποτε επικοινωνίας μεταξύ του πελάτη και του διακομιστή, θα παρατηρήσετε ότι κατά τη δημιουργία μιας σύνδεσης, χρησιμοποιούνται μόνο οι σημαίες SYN και ACK, αλλά κατά την ανταλλαγή δεδομένων, το τελευταίο πακέτο αιτήματος θα περιέχει τη σημαία PSH. Εγκαθίσταται αυτόματα για να αποφευχθεί το buffer. Μπορείτε να χρησιμοποιήσετε αυτές τις πληροφορίες για να δημιουργήσετε ένα φίλτρο: θα επιτρέψει όλα τα πακέτα εκτός από αυτά που περιέχουν τη σημαία PSH. Έτσι, η σύνδεση θα δημιουργηθεί, αλλά ο πελάτης δεν θα μπορεί να στείλει δεδομένα στον διακομιστή.

DROP

Για το DROP η εντολή θα μοιάζει με αυτό:

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

Εκκινήστε τον πελάτη και παρακολουθήστε την κίνηση:

Χωματερή κυκλοφορίας

[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

Βλέπουμε ότι η σύνδεση έχει δημιουργηθεί και ο πελάτης δεν μπορεί να στείλει δεδομένα στον διακομιστή.

ΑΠΟΡΡΙΠΤΩ

Σε αυτήν την περίπτωση η συμπεριφορά θα είναι η ίδια: ο πελάτης δεν θα μπορεί να στείλει το αίτημα, αλλά θα λάβει Η θύρα ICMP 127.0.0.1 tcp 12345 δεν είναι προσβάσιμη και αυξήστε εκθετικά το χρόνο μεταξύ των επαναυποβολών αιτημάτων. Η εντολή μοιάζει με αυτό:

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

ΑΠΟΡΡΙΨΗ με tcp-reset

Η εντολή μοιάζει με αυτό:

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

Το γνωρίζουμε ήδη κατά τη χρήση --απόρριψη-με tcp-reset ο πελάτης θα λάβει ένα πακέτο RST ως απόκριση, επομένως η συμπεριφορά μπορεί να προβλεφθεί: η λήψη ενός πακέτου RST ενώ η σύνδεση είναι εγκατεστημένη σημαίνει ότι η υποδοχή είναι απροσδόκητα κλειστή στην άλλη πλευρά, πράγμα που σημαίνει ότι ο πελάτης θα πρέπει να λάβει Επαναφορά σύνδεσης από ομότιμο. Ας τρέξουμε το σενάριό μας και ας βεβαιωθούμε για αυτό. Και έτσι θα είναι η κίνηση:

Χωματερή κυκλοφορίας

[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

ΑΠΟΡΡΙΨΗ με icmp-host-unreachable

Νομίζω ότι είναι ήδη προφανές σε όλους πώς θα μοιάζει η εντολή :) Η συμπεριφορά του πελάτη σε αυτήν την περίπτωση θα είναι ελαφρώς διαφορετική από αυτήν με μια απλή ΑΠΟΡΡΙΨΗ: ο πελάτης δεν θα αυξήσει το χρονικό όριο μεταξύ των προσπαθειών για εκ νέου αποστολή του πακέτου.

[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

Παραγωγή

Δεν είναι απαραίτητο να γράψετε μια παρωδία για να δοκιμάσετε την αλληλεπίδραση μιας υπηρεσίας με έναν αναρτημένο πελάτη ή διακομιστή, μερικές φορές αρκεί να χρησιμοποιήσετε τυπικά βοηθητικά προγράμματα που βρίσκονται στο Linux.

Τα βοηθητικά προγράμματα που συζητούνται στο άρθρο έχουν ακόμη περισσότερες δυνατότητες από αυτές που περιγράφηκαν, επομένως μπορείτε να βρείτε μερικές από τις δικές σας επιλογές για τη χρήση τους. Προσωπικά έχω πάντα αρκετά από αυτά που έγραψα (μάλιστα και λιγότερα). Εάν χρησιμοποιείτε αυτά ή παρόμοια βοηθητικά προγράμματα σε δοκιμές στην εταιρεία σας, γράψτε πώς ακριβώς. Εάν όχι, τότε ελπίζω το λογισμικό σας να γίνει καλύτερο εάν αποφασίσετε να το δοκιμάσετε σε συνθήκες προβλημάτων δικτύου χρησιμοποιώντας τις προτεινόμενες μεθόδους.

Πηγή: www.habr.com

Προσθέστε ένα σχόλιο