Προσοχή στα τρωτά σημεία που φέρνουν τον κύκλο εργασίας. Μέρος 1: FragmentSmack/SegmentSmack

Προσοχή στα τρωτά σημεία που φέρνουν τον κύκλο εργασίας. Μέρος 1: FragmentSmack/SegmentSmack

Γεια σε όλους! Ονομάζομαι Dmitry Samsonov, εργάζομαι ως κορυφαίος διαχειριστής συστήματος στην Odnoklassniki. Έχουμε περισσότερους από 7 χιλιάδες φυσικούς διακομιστές, 11 χιλιάδες κοντέινερ στο cloud μας και 200 ​​εφαρμογές, οι οποίες σε διάφορες διαμορφώσεις σχηματίζουν 700 διαφορετικά συμπλέγματα. Η συντριπτική πλειοψηφία των διακομιστών τρέχει το CentOS 7.
Στις 14 Αυγούστου 2018, δημοσιεύθηκαν πληροφορίες σχετικά με την ευπάθεια FragmentSmack
(CVE-2018-5391) και SegmentSmack (CVE-2018-5390). Πρόκειται για τρωτά σημεία με διάνυσμα επίθεσης δικτύου και αρκετά υψηλή βαθμολογία (7.5), που απειλεί την άρνηση υπηρεσίας (DoS) λόγω εξάντλησης πόρων (CPU). Μια διόρθωση πυρήνα για το FragmentSmack δεν προτάθηκε εκείνη τη στιγμή· επιπλέον, εμφανίστηκε πολύ αργότερα από τη δημοσίευση πληροφοριών σχετικά με την ευπάθεια. Για την εξάλειψη του SegmentSmack, προτάθηκε η ενημέρωση του πυρήνα. Το ίδιο το πακέτο ενημέρωσης κυκλοφόρησε την ίδια μέρα, το μόνο που έμεινε ήταν να το εγκαταστήσετε.
Όχι, δεν είμαστε καθόλου κατά της ενημέρωσης του πυρήνα! Ωστόσο, υπάρχουν αποχρώσεις...

Πώς ενημερώνουμε τον πυρήνα κατά την παραγωγή

Γενικά, τίποτα περίπλοκο:

  1. Λήψη πακέτων.
  2. Εγκαταστήστε τα σε έναν αριθμό διακομιστών (συμπεριλαμβανομένων των διακομιστών που φιλοξενούν το cloud μας).
  3. Βεβαιωθείτε ότι τίποτα δεν έχει σπάσει.
  4. Βεβαιωθείτε ότι όλες οι τυπικές ρυθμίσεις του πυρήνα εφαρμόζονται χωρίς σφάλματα.
  5. Περιμένετε μερικές μέρες.
  6. Ελέγξτε την απόδοση του διακομιστή.
  7. Αλλαγή ανάπτυξης νέων διακομιστών στον νέο πυρήνα.
  8. Ενημερώστε όλους τους διακομιστές ανά κέντρο δεδομένων (ένα κέντρο δεδομένων τη φορά για να ελαχιστοποιήσετε τις επιπτώσεις στους χρήστες σε περίπτωση προβλημάτων).
  9. Επανεκκινήστε όλους τους διακομιστές.

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

  • Stock CentOS 7 3.10 - για τους περισσότερους κανονικούς διακομιστές.
  • Βανίλια 4.19 - για τη δική μας μονόνεφα σύννεφα, επειδή χρειαζόμαστε BFQ, BBR κ.λπ.
  • Elrepo kernel-ml 5.2 - για διανομείς με μεγάλο φορτίο, γιατί το 4.19 συνήθιζε να συμπεριφέρεται ασταθές, αλλά χρειάζονται τα ίδια χαρακτηριστικά.

Όπως ίσως έχετε μαντέψει, η επανεκκίνηση χιλιάδων διακομιστών διαρκεί περισσότερο. Δεδομένου ότι δεν είναι όλα τα τρωτά σημεία κρίσιμα για όλους τους διακομιστές, κάνουμε επανεκκίνηση μόνο εκείνων που είναι άμεσα προσβάσιμοι από το Διαδίκτυο. Στο cloud, για να μην περιοριστεί η ευελιξία, δεν συνδέουμε εξωτερικά προσβάσιμα κοντέινερ σε μεμονωμένους διακομιστές με νέο πυρήνα, αλλά επανεκκινούμε όλους τους κεντρικούς υπολογιστές χωρίς εξαίρεση. Ευτυχώς, η διαδικασία εκεί είναι πιο απλή από ό,τι με τους κανονικούς διακομιστές. Για παράδειγμα, τα κοντέινερ χωρίς κατάσταση μπορούν απλώς να μετακινηθούν σε άλλο διακομιστή κατά την επανεκκίνηση.

Ωστόσο, υπάρχει ακόμα πολλή δουλειά, και μπορεί να διαρκέσει αρκετές εβδομάδες, και αν υπάρχουν προβλήματα με τη νέα έκδοση, έως και αρκετούς μήνες. Οι επιτιθέμενοι το καταλαβαίνουν πολύ καλά, επομένως χρειάζονται ένα σχέδιο Β.

FragmentSmack/SegmentSmack. Λύση

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

Στην περίπτωση FragmentSmack/SegmentSmack προτάθηκε αυτή η λύση:

«Μπορείτε να αλλάξετε τις προεπιλεγμένες τιμές των 4MB και 3MB στα net.ipv4.ipfrag_high_thresh και net.ipv4.ipfrag_low_thresh (και τις αντίστοιχες τιμές για ipv6 net.ipv6.ipfrag_high_thresh και net.ipv6.ipfrag_low_thresh) σε 256 k192 ή B262144 αντίστοιχα πιο χαμηλα. Οι δοκιμές δείχνουν μικρές έως σημαντικές μειώσεις στη χρήση της CPU κατά τη διάρκεια μιας επίθεσης ανάλογα με το υλικό, τις ρυθμίσεις και τις συνθήκες. Ωστόσο, μπορεί να υπάρχει κάποιο αντίκτυπο στην απόδοση λόγω ipfrag_high_thresh=64 byte, καθώς μόνο δύο τμήματα XNUMXK μπορούν να χωρέσουν στην ουρά επανασυναρμολόγησης κάθε φορά. Για παράδειγμα, υπάρχει κίνδυνος να σπάσουν οι εφαρμογές που λειτουργούν με μεγάλα πακέτα UDP».

Οι ίδιες οι παράμετροι στην τεκμηρίωση του πυρήνα περιγράφεται ως εξής:

ipfrag_high_thresh - LONG INTEGER
    Maximum memory used to reassemble IP fragments.

ipfrag_low_thresh - LONG INTEGER
    Maximum memory used to reassemble IP fragments before the kernel
    begins to remove incomplete fragment queues to free up resources.
    The kernel still accepts new fragments for defragmentation.

Δεν έχουμε μεγάλα UDP για υπηρεσίες παραγωγής. Δεν υπάρχει κατακερματισμένη κίνηση στο LAN, υπάρχει κατακερματισμένη κίνηση στο WAN, αλλά όχι σημαντική. Δεν υπάρχουν πινακίδες - μπορείτε να εφαρμόσετε Λύση!

FragmentSmack/SegmentSmack. Πρώτο αίμα

Το πρώτο πρόβλημα που αντιμετωπίσαμε ήταν ότι τα κοντέινερ cloud μερικές φορές εφάρμοζαν τις νέες ρυθμίσεις μόνο εν μέρει (μόνο ipfrag_low_thresh) και μερικές φορές δεν τις εφάρμοζαν καθόλου - απλώς κολλούσαν στην αρχή. Δεν ήταν δυνατή η σταθερή αναπαραγωγή του προβλήματος (όλες οι ρυθμίσεις εφαρμόστηκαν χειροκίνητα χωρίς δυσκολίες). Η κατανόηση του γιατί το κοντέινερ συντρίβεται στην αρχή δεν είναι επίσης τόσο εύκολο: δεν βρέθηκαν σφάλματα. Ένα πράγμα ήταν σίγουρο: η επαναφορά των ρυθμίσεων λύνει το πρόβλημα με τα σφάλματα κοντέινερ.

Γιατί δεν αρκεί η εφαρμογή του Sysctl στον κεντρικό υπολογιστή; Το κοντέινερ ζει στον δικό του αποκλειστικό χώρο ονομάτων δικτύου, τουλάχιστον έτσι μέρος των παραμέτρων Sysctl του δικτύου στο δοχείο μπορεί να διαφέρει από τον ξενιστή.

Πώς ακριβώς εφαρμόζονται οι ρυθμίσεις Sysctl στο κοντέινερ; Δεδομένου ότι τα κοντέινερ μας δεν είναι προνομιούχα, δεν θα μπορείτε να αλλάξετε καμία ρύθμιση Sysctl μεταβαίνοντας στο ίδιο το κοντέινερ - απλά δεν έχετε αρκετά δικαιώματα. Για την εκτέλεση κοντέινερ, το σύννεφο μας εκείνη την εποχή χρησιμοποιούσε το Docker (τώρα Πόντμαν). Οι παράμετροι του νέου κοντέινερ μεταβιβάστηκαν στο Docker μέσω του API, συμπεριλαμβανομένων των απαραίτητων ρυθμίσεων Sysctl.
Κατά την αναζήτηση στις εκδόσεις, αποδείχθηκε ότι το Docker API δεν επέστρεψε όλα τα σφάλματα (τουλάχιστον στην έκδοση 1.10). Όταν προσπαθήσαμε να ξεκινήσουμε το κοντέινερ μέσω "docker run", τελικά είδαμε τουλάχιστον κάτι:

write /proc/sys/net/ipv4/ipfrag_high_thresh: invalid argument docker: Error response from daemon: Cannot start container <...>: [9] System error: could not synchronise with container process.

Η τιμή της παραμέτρου δεν είναι έγκυρη. Μα γιατί? Και γιατί δεν ισχύει μόνο μερικές φορές; Αποδείχθηκε ότι το Docker δεν εγγυάται τη σειρά με την οποία εφαρμόζονται οι παράμετροι Sysctl (η τελευταία δοκιμασμένη έκδοση είναι η 1.13.1), επομένως μερικές φορές το ipfrag_high_thresh προσπάθησε να οριστεί στα 256K όταν το ipfrag_low_thresh ήταν ακόμα 3M, δηλαδή το ανώτερο όριο ήταν χαμηλότερο από το κατώτερο όριο, το οποίο οδήγησε στο σφάλμα.

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

FragmentSmack/SegmentSmack. Πρώτο αίμα 2

Πριν προλάβουμε να κατανοήσουμε τη χρήση του Workaround στο cloud, άρχισαν να καταφθάνουν τα πρώτα σπάνια παράπονα από χρήστες. Εκείνη την εποχή, είχαν περάσει αρκετές εβδομάδες από την έναρξη χρήσης του Workaround στους πρώτους διακομιστές. Η αρχική έρευνα έδειξε ότι ελήφθησαν καταγγελίες κατά μεμονωμένων υπηρεσιών και όχι όλων των διακομιστών αυτών των υπηρεσιών. Το πρόβλημα έχει γίνει και πάλι εξαιρετικά αβέβαιο.

Πρώτα απ 'όλα, προσπαθήσαμε, φυσικά, να επαναφέρουμε τις ρυθμίσεις Sysctl, αλλά αυτό δεν είχε κανένα αποτέλεσμα. Οι διάφοροι χειρισμοί με τις ρυθμίσεις του διακομιστή και της εφαρμογής δεν βοήθησαν επίσης. Η επανεκκίνηση βοήθησε. Η επανεκκίνηση του Linux είναι τόσο αφύσικη όσο ήταν φυσιολογική για τα Windows παλιά. Ωστόσο, βοήθησε, και το αναφέραμε ως «πρόβλημα πυρήνα» κατά την εφαρμογή των νέων ρυθμίσεων στο Sysctl. Πόσο επιπόλαιο ήταν...

Τρεις εβδομάδες αργότερα το πρόβλημα επανεμφανίστηκε. Η διαμόρφωση αυτών των διακομιστών ήταν αρκετά απλή: Nginx σε λειτουργία μεσολάβησης/εξισορροπητή. Όχι πολλή κίνηση. Νέα εισαγωγική σημείωση: ο αριθμός των 504 σφαλμάτων στους πελάτες αυξάνεται καθημερινά (Λήξη χρονικού ορίου πύλης). Το γράφημα δείχνει τον αριθμό των 504 σφαλμάτων ανά ημέρα για αυτήν την υπηρεσία:

Προσοχή στα τρωτά σημεία που φέρνουν τον κύκλο εργασίας. Μέρος 1: FragmentSmack/SegmentSmack

Όλα τα σφάλματα είναι περίπου το ίδιο backend - για αυτό που βρίσκεται στο cloud. Το γράφημα κατανάλωσης μνήμης για θραύσματα πακέτων σε αυτό το backend έμοιαζε με αυτό:

Προσοχή στα τρωτά σημεία που φέρνουν τον κύκλο εργασίας. Μέρος 1: FragmentSmack/SegmentSmack

Αυτή είναι μια από τις πιο εμφανείς εκδηλώσεις του προβλήματος στα γραφήματα του λειτουργικού συστήματος. Στο cloud, την ίδια στιγμή, διορθώθηκε ένα άλλο πρόβλημα δικτύου με ρυθμίσεις QoS (Traffic Control). Στο γράφημα της κατανάλωσης μνήμης για θραύσματα πακέτων, φαινόταν ακριβώς το ίδιο:

Προσοχή στα τρωτά σημεία που φέρνουν τον κύκλο εργασίας. Μέρος 1: FragmentSmack/SegmentSmack

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

Η ουσία του επιδιορθωμένου προβλήματος ήταν ότι χρησιμοποιήσαμε τον προγραμματιστή πακέτων fq με προεπιλεγμένες ρυθμίσεις στο QoS. Από προεπιλογή, για μία σύνδεση, σας επιτρέπει να προσθέσετε 100 πακέτα στην ουρά και ορισμένες συνδέσεις, σε περιπτώσεις έλλειψης καναλιού, άρχισαν να φράζουν τη χωρητικότητα της ουράς. Σε αυτήν την περίπτωση, τα πακέτα απορρίπτονται. Στα στατιστικά tc (tc -s qdisc) αυτό μπορεί να φανεί ως εξής:

qdisc fq 2c6c: parent 1:2c6c limit 10000p flow_limit 100p buckets 1024 orphan_mask 1023 quantum 3028 initial_quantum 15140 refill_delay 40.0ms
 Sent 454701676345 bytes 491683359 pkt (dropped 464545, overlimits 0 requeues 0)
 backlog 0b 0p requeues 0
  1024 flows (1021 inactive, 0 throttled)
  0 gc, 0 highprio, 0 throttled, 464545 flows_plimit

Το "464545 flows_plimit" είναι τα πακέτα που απορρίφθηκαν λόγω υπέρβασης του ορίου ουράς μιας σύνδεσης και το "dropped 464545" είναι το άθροισμα όλων των πακέτων που απορρίφθηκαν αυτού του χρονοπρογραμματιστή. Μετά την αύξηση του μήκους της ουράς σε 1 και την επανεκκίνηση των κοντέινερ, το πρόβλημα σταμάτησε να εμφανίζεται. Μπορείτε να καθίσετε και να πιείτε ένα smoothie.

FragmentSmack/SegmentSmack. Τελευταίο αίμα

Πρώτον, αρκετούς μήνες μετά την ανακοίνωση των τρωτών σημείων στον πυρήνα, εμφανίστηκε τελικά μια επιδιόρθωση για το FragmentSmack (να σας θυμίσω ότι μαζί με την ανακοίνωση τον Αύγουστο κυκλοφόρησε μια επιδιόρθωση μόνο για το SegmentSmack), η οποία μας έδωσε την ευκαιρία να εγκαταλείψουμε το Workaround, που μας προκάλεσε αρκετά προβλήματα. Σε αυτό το διάστημα, είχαμε ήδη καταφέρει να μεταφέρουμε μερικούς από τους διακομιστές στον νέο πυρήνα και τώρα έπρεπε να ξεκινήσουμε από την αρχή. Γιατί ενημερώσαμε τον πυρήνα χωρίς να περιμένουμε την επιδιόρθωση FragmentSmack; Γεγονός είναι ότι η διαδικασία προστασίας από αυτά τα τρωτά σημεία συνέπεσε (και συγχωνεύθηκε) με τη διαδικασία ενημέρωσης του ίδιου του CentOS (η οποία απαιτεί ακόμη περισσότερο χρόνο από την ενημέρωση μόνο του πυρήνα). Επιπλέον, το SegmentSmack είναι μια πιο επικίνδυνη ευπάθεια και μια επιδιόρθωση για αυτό εμφανίστηκε αμέσως, οπότε ήταν λογικό ούτως ή άλλως. Ωστόσο, δεν μπορούσαμε απλώς να ενημερώσουμε τον πυρήνα στο CentOS επειδή η ευπάθεια FragmentSmack, η οποία εμφανίστηκε κατά τη διάρκεια του CentOS 7.5, διορθώθηκε μόνο στην έκδοση 7.6, οπότε έπρεπε να σταματήσουμε την ενημέρωση στην 7.5 και να ξεκινήσουμε από την αρχή με την ενημέρωση στην 7.6. Και αυτό συμβαίνει επίσης.

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

Όπως θυμόμαστε από την παραπάνω ιστορία, η επαναφορά του Sysctl δεν βοήθησε. Η επανεκκίνηση βοήθησε, αλλά προσωρινά.
Οι υποψίες σχετικά με το Sysctl δεν αφαιρέθηκαν, αλλά αυτή τη φορά ήταν απαραίτητο να συλλεχθούν όσο το δυνατόν περισσότερες πληροφορίες. Υπήρχε επίσης μια τεράστια έλλειψη ικανότητας αναπαραγωγής του προβλήματος μεταφόρτωσης στον πελάτη, προκειμένου να μελετηθεί με μεγαλύτερη ακρίβεια τι συνέβαινε.

Η ανάλυση όλων των διαθέσιμων στατιστικών στοιχείων και αρχείων καταγραφής δεν μας έφερε πιο κοντά στην κατανόηση του τι συνέβαινε. Υπήρχε μια οξεία έλλειψη ικανότητας αναπαραγωγής του προβλήματος προκειμένου να «αισθανθεί» μια συγκεκριμένη σύνδεση. Τέλος, οι προγραμματιστές, χρησιμοποιώντας μια ειδική έκδοση της εφαρμογής, κατάφεραν να επιτύχουν σταθερή αναπαραγωγή προβλημάτων σε μια δοκιμαστική συσκευή όταν συνδεθούν μέσω Wi-Fi. Αυτό ήταν μια σημαντική ανακάλυψη στην έρευνα. Ο υπολογιστής-πελάτης συνδέθηκε στο Nginx, το οποίο έκανε μεσολάβηση στο backend, το οποίο ήταν η εφαρμογή Java μας.

Προσοχή στα τρωτά σημεία που φέρνουν τον κύκλο εργασίας. Μέρος 1: FragmentSmack/SegmentSmack

Ο διάλογος για τα προβλήματα ήταν κάπως έτσι (διορθώθηκε στην πλευρά του διακομιστή μεσολάβησης Nginx):

  1. Πελάτης: αίτημα για λήψη πληροφοριών σχετικά με τη λήψη ενός αρχείου.
  2. Διακομιστής Java: απόκριση.
  3. Πελάτης: POST με αρχείο.
  4. Διακομιστής Java: σφάλμα.

Ταυτόχρονα, ο διακομιστής Java γράφει στο αρχείο καταγραφής ότι ελήφθησαν 0 byte δεδομένων από τον υπολογιστή-πελάτη και ο διακομιστής μεσολάβησης Nginx γράφει ότι το αίτημα πήρε περισσότερα από 30 δευτερόλεπτα (30 δευτερόλεπτα είναι το χρονικό όριο λήξης της εφαρμογής πελάτη). Γιατί το χρονικό όριο και γιατί 0 byte; Από την άποψη του HTTP, όλα λειτουργούν όπως θα έπρεπε, αλλά το POST με το αρχείο φαίνεται να εξαφανίζεται από το δίκτυο. Επιπλέον, εξαφανίζεται μεταξύ του πελάτη και του Nginx. Ήρθε η ώρα να οπλιστείτε με το Tcpdump! Αλλά πρώτα πρέπει να κατανοήσετε τη διαμόρφωση του δικτύου. Ο διακομιστής μεσολάβησης Nginx βρίσκεται πίσω από τον εξισορροπητή L3 NFware. Το Tunneling χρησιμοποιείται για την παράδοση πακέτων από τον εξισορροπητή L3 στον διακομιστή, ο οποίος προσθέτει τις κεφαλίδες του στα πακέτα:

Προσοχή στα τρωτά σημεία που φέρνουν τον κύκλο εργασίας. Μέρος 1: FragmentSmack/SegmentSmack

Σε αυτήν την περίπτωση, το δίκτυο έρχεται σε αυτόν τον διακομιστή με τη μορφή κίνησης με ετικέτα Vlan, η οποία προσθέτει επίσης τα δικά του πεδία στα πακέτα:

Προσοχή στα τρωτά σημεία που φέρνουν τον κύκλο εργασίας. Μέρος 1: FragmentSmack/SegmentSmack

Και αυτή η επισκεψιμότητα μπορεί επίσης να κατακερματιστεί (αυτό το ίδιο μικρό ποσοστό εισερχόμενης κατακερματισμένης επισκεψιμότητας για το οποίο μιλήσαμε κατά την αξιολόγηση των κινδύνων από το Workaround), το οποίο αλλάζει επίσης το περιεχόμενο των κεφαλίδων:

Προσοχή στα τρωτά σημεία που φέρνουν τον κύκλο εργασίας. Μέρος 1: FragmentSmack/SegmentSmack

Για άλλη μια φορά: τα πακέτα ενθυλακώνονται με μια ετικέτα Vlan, ενθυλακώνονται με ένα τούνελ, κατακερματίζονται. Για να κατανοήσουμε καλύτερα πώς συμβαίνει αυτό, ας εντοπίσουμε τη διαδρομή του πακέτου από τον πελάτη στον διακομιστή μεσολάβησης Nginx.

  1. Το πακέτο φτάνει στον εξισορροπητή L3. Για σωστή δρομολόγηση εντός του κέντρου δεδομένων, το πακέτο ενθυλακώνεται σε μια σήραγγα και αποστέλλεται στην κάρτα δικτύου.
  2. Δεδομένου ότι οι κεφαλίδες πακέτου + σήραγγας δεν χωρούν στο MTU, το πακέτο κόβεται σε θραύσματα και αποστέλλεται στο δίκτυο.
  3. Ο διακόπτης μετά τον εξισορροπητή L3, όταν λαμβάνει ένα πακέτο, προσθέτει μια ετικέτα Vlan σε αυτό και το στέλνει.
  4. Ο διακόπτης μπροστά από τον διακομιστή μεσολάβησης Nginx βλέπει (βάσει των ρυθμίσεων της θύρας) ότι ο διακομιστής αναμένει ένα πακέτο ενθυλακωμένο με Vlan, επομένως το στέλνει ως έχει, χωρίς να αφαιρέσει την ετικέτα Vlan.
  5. Το Linux παίρνει κομμάτια μεμονωμένων πακέτων και τα συγχωνεύει σε ένα μεγάλο πακέτο.
  6. Στη συνέχεια, το πακέτο φτάνει στη διεπαφή Vlan, όπου αφαιρείται το πρώτο στρώμα από αυτό - η ενθυλάκωση Vlan.
  7. Το Linux στη συνέχεια το στέλνει στη διεπαφή Tunnel, όπου αφαιρείται ένα άλλο επίπεδο από αυτό - η ενθυλάκωση σήραγγας.

Η δυσκολία είναι να περάσετε όλα αυτά ως παραμέτρους στο tcpdump.
Ας ξεκινήσουμε από το τέλος: υπάρχουν καθαρά (χωρίς περιττές κεφαλίδες) πακέτα IP από πελάτες, με αφαιρέθηκε το vlan και το tunnel encapsulation;

tcpdump host <ip клиента>

Όχι, δεν υπήρχαν τέτοια πακέτα στον διακομιστή. Άρα το πρόβλημα πρέπει να υπάρχει νωρίτερα. Υπάρχουν πακέτα που έχουν αφαιρεθεί μόνο η ενθυλάκωση Vlan;

tcpdump ip[32:4]=0xx390x2xx

0xx390x2xx είναι η διεύθυνση IP του πελάτη σε μορφή hex.
32:4 — διεύθυνση και μήκος του πεδίου στο οποίο είναι γραμμένο το SCR IP στο πακέτο Tunnel.

Η διεύθυνση πεδίου έπρεπε να επιλεγεί με ωμή βία, αφού στο Διαδίκτυο γράφουν περίπου 40, 44, 50, 54, αλλά δεν υπήρχε διεύθυνση IP εκεί. Μπορείτε επίσης να δείτε ένα από τα πακέτα σε hex (την παράμετρο -xx ή -XX στο tcpdump) και να υπολογίσετε τη διεύθυνση IP που γνωρίζετε.

Υπάρχουν θραύσματα πακέτων χωρίς την ενθυλάκωση Vlan και Tunnel;

tcpdump ((ip[6:2] > 0) and (not ip[6] = 64))

Αυτή η μαγεία θα μας δείξει όλα τα θραύσματα, συμπεριλαμβανομένου του τελευταίου. Πιθανώς, το ίδιο πράγμα μπορεί να φιλτραριστεί με IP, αλλά δεν προσπάθησα, επειδή δεν υπάρχουν πολλά τέτοια πακέτα και αυτά που χρειαζόμουν βρέθηκαν εύκολα στη γενική ροή. Εδώ είναι:

14:02:58.471063 In 00:de:ff:1a:94:11 ethertype IPv4 (0x0800), length 1516: (tos 0x0, ttl 63, id 53652, offset 0, flags [+], proto IPIP (4), length 1500)
    11.11.11.11 > 22.22.22.22: truncated-ip - 20 bytes missing! (tos 0x0, ttl 50, id 57750, offset 0, flags [DF], proto TCP (6), length 1500)
    33.33.33.33.33333 > 44.44.44.44.80: Flags [.], seq 0:1448, ack 1, win 343, options [nop,nop,TS val 11660691 ecr 2998165860], length 1448
        0x0000: 0000 0001 0006 00de fb1a 9441 0000 0800 ...........A....
        0x0010: 4500 05dc d194 2000 3f09 d5fb 0a66 387d E.......?....f8}
        0x0020: 1x67 7899 4500 06xx e198 4000 3206 6xx4 [email protected].
        0x0030: b291 x9xx x345 2541 83b9 0050 9740 0x04 .......A...P.@..
        0x0040: 6444 4939 8010 0257 8c3c 0000 0101 080x dDI9...W.......
        0x0050: 00b1 ed93 b2b4 6964 xxd8 ffe1 006a 4578 ......ad.....jEx
        0x0060: 6966 0000 4x4d 002a 0500 0008 0004 0100 if..MM.*........

14:02:58.471103 In 00:de:ff:1a:94:11 ethertype IPv4 (0x0800), length 62: (tos 0x0, ttl 63, id 53652, offset 1480, flags [none], proto IPIP (4), length 40)
    11.11.11.11 > 22.22.22.22: ip-proto-4
        0x0000: 0000 0001 0006 00de fb1a 9441 0000 0800 ...........A....
        0x0010: 4500 0028 d194 00b9 3f04 faf6 2x76 385x E..(....?....f8}
        0x0020: 1x76 6545 xxxx 1x11 2d2c 0c21 8016 8e43 .faE...D-,.!...C
        0x0030: x978 e91d x9b0 d608 0000 0000 0000 7c31 .x............|Q
        0x0040: 881d c4b6 0000 0000 0000 0000 0000 ..............

Πρόκειται για δύο θραύσματα ενός πακέτου (ίδιο ID 53652) με φωτογραφία (η λέξη Exif είναι ορατή στην πρώτη συσκευασία). Λόγω του ότι υπάρχουν πακέτα σε αυτό το επίπεδο, αλλά όχι σε συγχωνευμένη μορφή στα dumps, το πρόβλημα είναι ξεκάθαρα στη συναρμολόγηση. Επιτέλους, υπάρχουν τεκμηριωμένες αποδείξεις για αυτό!

Ο αποκωδικοποιητής πακέτων δεν αποκάλυψε κανένα πρόβλημα που θα εμπόδιζε την κατασκευή. Δοκίμασε το εδώ: hpd.gasmi.net. Στην αρχή, όταν προσπαθείτε να γεμίσετε κάτι εκεί, στον αποκωδικοποιητή δεν αρέσει η μορφή του πακέτου. Αποδείχθηκε ότι υπήρχαν μερικές επιπλέον δύο οκτάδες μεταξύ Srcmac και Ethertype (δεν σχετίζονται με πληροφορίες θραυσμάτων). Μετά την αφαίρεσή τους, ο αποκωδικοποιητής άρχισε να λειτουργεί. Ωστόσο, δεν παρουσίασε προβλήματα.
Ό,τι και να πει κανείς, δεν βρέθηκε τίποτα άλλο εκτός από αυτά τα Sysctl. Το μόνο που απέμενε ήταν να βρεθεί ένας τρόπος για τον εντοπισμό προβληματικών διακομιστών, προκειμένου να κατανοήσουμε την κλίμακα και να αποφασίσουμε για περαιτέρω ενέργειες. Ο απαιτούμενος μετρητής βρέθηκε αρκετά γρήγορα:

netstat -s | grep "packet reassembles failed”

Είναι επίσης σε snmpd υπό OID=1.3.6.1.2.1.4.31.1.1.16.1 (ipSystemStatsReasmFails).

"Ο αριθμός των αστοχιών που εντοπίστηκαν από τον αλγόριθμο επανασυναρμολόγησης IP (για οποιονδήποτε λόγο: λήξη χρόνου, σφάλματα κ.λπ.)."

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

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

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

Οι πιο σημαντικές ερωτήσεις

Γιατί είναι κατακερματισμένα τα πακέτα στον εξισορροπητή L3; Τα περισσότερα από τα πακέτα που φτάνουν από τους χρήστες στους εξισορροπητές είναι SYN και ACK. Τα μεγέθη αυτών των συσκευασιών είναι μικρά. Επειδή όμως το μερίδιο τέτοιων πακέτων είναι πολύ μεγάλο, στο φόντο τους δεν παρατηρήσαμε την παρουσία μεγάλων πακέτων που άρχισαν να κατακερματίζονται.

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

Γιατί η επαναφορά Sysctl δεν βοήθησε, αλλά η επανεκκίνηση βοήθησε; Η επαναφορά του Sysctl άλλαξε την ποσότητα της διαθέσιμης μνήμης για τη συγχώνευση πακέτων. Ταυτόχρονα, προφανώς το ίδιο το γεγονός της υπερχείλισης μνήμης για θραύσματα οδήγησε σε επιβράδυνση των συνδέσεων, γεγονός που οδήγησε στην καθυστέρηση των θραυσμάτων για μεγάλο χρονικό διάστημα στην ουρά. Δηλαδή, η διαδικασία έκανε κύκλους.
Η επανεκκίνηση καθάρισε τη μνήμη και όλα επανήλθαν στη σειρά.

Ήταν δυνατό να γίνει χωρίς Λύση; Ναι, αλλά υπάρχει μεγάλος κίνδυνος να αφήσετε τους χρήστες χωρίς υπηρεσία σε περίπτωση επίθεσης. Φυσικά, η χρήση του Workaround είχε ως αποτέλεσμα διάφορα προβλήματα, συμπεριλαμβανομένης της επιβράδυνσης μιας από τις υπηρεσίες για τους χρήστες, αλλά παρόλα αυτά πιστεύουμε ότι οι ενέργειες ήταν δικαιολογημένες.

Ευχαριστώ πολύ τον Andrey Timofeev (atimofeyev) για βοήθεια στη διεξαγωγή της έρευνας, καθώς και τον Alexey Krenev (συσκευήx) - για το τιτάνιο έργο της ενημέρωσης Centos και πυρήνων σε διακομιστές. Μια διαδικασία που σε αυτή την περίπτωση έπρεπε να ξεκινήσει από την αρχή αρκετές φορές, γι' αυτό και κράτησε πολλούς μήνες.

Πηγή: www.habr.com

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