المستوى المطلوب: محترف (Professional) — هذا المقال موجّه لمهندسي DevOps ومسؤولي الأنظمة اللي بيديروا سيرفرات لينكس تحت حِمل شبكي عالي. الفرضية إن عندك خدمة بتستقبل آلاف الاتصالات في الثانية على جدار حماية أو Load Balancer أو Kubernetes node.
لو سيرفرك فجأة بيرفض اتصالات جديدة، والـ CPU والرام مرتاحين، والـ ping شغّال بس الـ TCP بيعلّق، غالبًا مالوش علاقة بالتطبيق. الجاني الحقيقي سطر واحد في الـ kernel log: nf_conntrack: table full, dropping packet. هنا هتعرف تشخّصها في أقل من دقيقة وتصلّحها صح من غير ما تفتح ثغرة ذاكرة.
nf_conntrack: table full — لماذا يتوقف سيرفرك عن قبول الاتصالات فجأة
المشكلة باختصار
لينكس بيتتبّع كل اتصال شبكي ماشي عبر الـ firewall في جدول اسمه nf_conntrack. الجدول ده له سقف. لمّا يمتلي، الـ kernel بيبدأ يرمي أي حزمة SYN جديدة على الأرض، فالمستخدم بيشوف الموقع "واقف" مع إن السيرفر مرتاح. المشكلة دي بتضرب البوابات (gateways)، الـ reverse proxies، وعُقد Kubernetes تحت ترافيك كثيف أو أثناء هجوم SYN flood.
يعني إيه conntrack أصلًا؟ (مثال بسيط الأول)
تخيّل موظف استقبال في فندق معاه دفتر. كل ضيف بيدخل، بيكتب اسمه ورقم غرفته في سطر. لمّا الضيف يمشي، بيشطب السطر. الدفتر ده بيخلّي الموظف يعرف مين جوّه ومين بيرجع لأي غرفة. المشكلة: الدفتر عدد صفحاته محدود. لو دخل ضيوف أكتر من عدد السطور، الموظف مش هيقدر يسجّل ضيف جديد، فبيقفله على الباب حتى لو الفندق نفسه فاضي.
ده بالظبط اللي بيعمله nf_conntrack. الـ conntrack (connection tracking) هو النظام اللي بيخلّي الـ Netfilter/iptables "stateful": بيربط الحزمة الراجعة بالاتصال اللي طلبها، وبيسمح بقواعد زي --ctstate ESTABLISHED,RELATED. كل اتصال (زوج IP/port في الاتجاهين + البروتوكول) بياخد سطر في جدول hash في ذاكرة الـ kernel. لمّا عدد الأسطر nf_conntrack_count يوصل للسقف nf_conntrack_max، أي حزمة تبدأ اتصال جديد بتتساب invalid وتتدروب، والـ kernel بيسجّل السطر الشهير في dmesg.
إزاي تكتشفها في أقل من دقيقة
أول حاجة، دوّر على السطر في الـ kernel log:
# لو لقيت السطر ده، المشكلة اتأكدت
dmesg | grep -i "nf_conntrack: table full"
# قارن العدد الحالي بالسقف
sysctl net.netfilter.nf_conntrack_count
sysctl net.netfilter.nf_conntrack_max
# مثال ناتج:
# net.netfilter.nf_conntrack_count = 262144
# net.netfilter.nf_conntrack_max = 262144 <-- count == max يعني ممتلئلو count بيلامس max باستمرار، انت في المنطقة الخطر. اعرف كمان مين ماخد أكبر عدد أسطر عشان تفرّق بين حِمل حقيقي وهجوم أو تسريب اتصالات:
# أعلى 10 وجهات ماخدة أسطر في الجدول
cat /proc/net/nf_conntrack \
| awk '{print $7}' | sort | uniq -c | sort -rn | headالحل خطوة بخطوة
فيه ثلاث روافع، رتّبهم بالأولوية. الأولوية الأعلى مش دايمًا "ارفع السقف".
- ارفع السقف (حل فوري): ده بيوقف النزيف لكن بياكل ذاكرة. كل سطر بياخد تقريبًا 288–376 بايت في الـ kernel. يعني رفع السقف لمليون سطر تكلفته حوالي 288 ميجابايت رام مقيمة (non-swappable).
# زوّد السقف + كبّر جدول الـ hash بالتناسب (القاعدة: max / 4) sysctl -w net.netfilter.nf_conntrack_max=1048576 echo 262144 > /sys/module/nf_conntrack/parameters/hashsize # ثبّتها بعد الريبوت echo "net.netfilter.nf_conntrack_max = 1048576" >> /etc/sysctl.d/99-conntrack.conf sysctl --system - قلّل مهلة الاتصالات الميتة (أعلى ROI غالبًا): الإعداد الافتراضي لمهلة اتصال TCP مكتمل هو
432000ثانية — يعني 5 أيام. ده بيخلّي أسطر لاتصالات خلصت من زمان قاعدة في الجدول بلا داعي. تنزيلها بيحرّر أسطر بسرعة من غير ما تزوّد ذاكرة:# من 5 أيام إلى ساعة واحدة للاتصالات المكتملة sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=3600 # ونصف مهلة انتظار SYN المعلّق (افتراضيًا 60 ثانية) sysctl -w net.netfilter.nf_conntrack_tcp_timeout_syn_sent=30 - وقّف التتبّع لِما لا يحتاجه (الحل الجذري لحِمل ضخم): لو عندك ترافيك مالوش لازمة تتتبّعه (مثلًا خدمة HTTP عامة على بورت 80/443 على بوابة مش محتاجة NAT له)، اعمله
NOTRACKفمش بياخد سطر أصلًا:# استثنِ بورت 80 من التتبّع تمامًا (raw table) iptables -t raw -A PREROUTING -p tcp --dport 80 -j NOTRACK iptables -t raw -A OUTPUT -p tcp --sport 80 -j NOTRACK
المقايضات (trade-offs)
كل رافعة ليها ثمن، اذكره بوضوح قبل ما تطبّق:
- رفع
nf_conntrack_max: بتكسب سعة فورية، بتخسر ذاكرة kernel مقيمة (~300 ميجا لكل مليون سطر) + احتمال إبطاء بسيط في البحث لو نسيت تكبّرhashsizeبالتناسب. - تقليل الـ timeouts: بتكسب أسطر متحرّرة بسرعة بلا ذاكرة زيادة، بتخسر إن اتصالات طويلة صامتة فعليًا (زي WebSocket خامل أو long-poll) ممكن تتقطع لو نزّلت المهلة تحت مدة الخمول بتاعتها. لو عندك WebSockets، خليها فوق مدة الـ keepalive.
- NOTRACK: بتكسب صفر استهلاك للجدول على الترافيك ده، بتخسر إمكانية عمل stateful matching أو NAT عليه. ممنوع تعمله على ترافيك محتاج
ESTABLISHED,RELATEDأو DNAT.
متى لا تستخدم هذه الطريقة
لو السيرفر مش بيعمل firewalling ولا NAT أصلًا (مثلًا host تطبيقي بسيط ورا Load Balancer)، فالأفضل تفريغ الموديول بدل ما تظبط سقفه. تحميل nf_conntrack على host مش محتاجه هو نفسه المشكلة. في الحالة دي:
# تأكد إن مفيش قواعد iptables محتاجة conntrack، بعدين
modprobe -r nf_conntrack # لو مفيش اعتماد عليهوكمان: متزوّدش السقف لأرقام خيالية على جهاز ذاكرته صغيرة. على node بـ 2 جيجا رام، سقف 4 مليون سطر ممكن ياكل أكتر من نصف الذاكرة ويجيب لك OOM بدل الدروب — استبدلت مشكلة بأسوأ منها.
الخطوة التالية
افتح سيرفرك دلوقتي ونفّذ الأمرين دول: sysctl net.netfilter.nf_conntrack_count و nf_conntrack_max. لو النسبة بينهم فوق 80%، ابدأ بتقليل nf_conntrack_tcp_timeout_established لـ 3600 (أرخص حل) وراقب الـ count لمدة ساعة. لو لسه بيلامس السقف، ساعتها بس ارفع الـ max وكبّر الـ hashsize معاه.
المصادر
- توثيق الـ kernel لمعاملات netfilter conntrack (nf_conntrack sysctl): kernel.org — nf_conntrack-sysctl
- مشروع Netfilter الرسمي (conntrack-tools ومفاهيم تتبّع الاتصال): netfilter.org — conntrack-tools
- Red Hat — حل مشكلة "nf_conntrack: table full, dropping packet": access.redhat.com/solutions/8721