تخطَّ إلى المحتوى
netrewind
الصندوق الأسود لشبكتك

كيف يعمل

سجل موحّد يربط كل حدث بدليله

يمثّل NetRewind كل رصد في صورة حدث موحّد: تنتجه المجمّعات، ويحفظه المخزن، ويحلله محرّك الارتباط، ثم تعرضه أداة سطر الأوامر. ولهذه البنية إصدار محدد، وهو حاليًا schema v1.

بُني هذا التصميم على خمسة مبادئ تطبّقها الشيفرة وتختبرها.

  1. 01

    الحدث تغيّر حالة أو قياس، وليس حزمة

    يسمح تسجيل التغيّرات بدل حركة المرور بالاحتفاظ بأيام من التاريخ في مساحة تُقاس بالجيجابايت بدل التيرابايت.

    كما يجعل معظم البيانات المحفوظة مفيدة مباشرة أثناء التحقيق، بدل أن تكون مادة خام تحتاج إلى تحليل طويل.

  2. 02

    كل حدث قابل للربط

    يحمل كل حدث هويات يمكن مطابقتها بين المصادر المختلفة. من دون ذلك لن نحصل إلا على سجلّات متفرقة تشترك في التوقيت.

    تسمح هذه الهوية المشتركة بربط الأعراض التي ترصدها أدوات متعددة داخل حادثة واحدة.

  3. 03

    كل حدث يحمل دليله

    يحفظ الحدث قدرًا كافيًا من الرصد الخام لشرح سبب تسجيله، من دون الحاجة إلى الرجوع إلى أداة أخرى.

    أي استنتاج لا يمكن التحقق من دليله قد يضيّع وقت التحقيق، خصوصًا تحت ضغط الأعطال.

  4. 04

    مفهوم واحد صارم للزمن

    يحمل كل حدث توقيت الساعة المدنية وتوقيت الساعة المطّردة بدقة النانوثانية، مع استخدام UTC للساعة المدنية.

    يضمن الجمع بينهما معرفة وقت الحدث وترتيبه حتى عند تصحيح ساعة النظام.

  5. 05

    المسجّل يصرّح بفترات العمى

    إذا لم تُسجّل فجوة الرصد، فلن يمكن تمييزها عن فترة لم يقع فيها أي حدث.

    يسجّل system.gap مدة انقطاع الرصد اعتمادًا على نبضة كل 10 ثوانٍ، بينما يسجّل system.drop عدد الأحداث التي أسقطتها النواة عند امتلاء الحلقة العازلة في eBPF.

    إظهار هذه الفجوات جزء أساسي من موثوقية الدليل؛ فالسجل الهادئ قد يعني استقرار الشبكة، وقد يعني أن المسجّل لم يكن يرى شيئًا.

الظرف المشترك

تحمل جميع الأحداث هذه الحقول، مهما كان مصدرها.

الحقلالنوعالمعنى
event_idULIDمعرّف فريد يمكن ترتيبه معجميًا بحسب وقت الإنشاء
schema_vuint16نسخة الظرف
ts_wallint64نانوثوانٍ منذ مبدأ يونكس، بتوقيت UTC
ts_monoint64نانوثوانٍ على الساعة المطّردة للراصد
observer_idstringالمسجّل الذي رصد الحدث
sourceenumnetlink · ebpf · conntrack · nftables · dhcp · dns · lldp · snmp · probe · config · internal
kindstringصيغة family.action، مثل l2.arp_binding_changed
severityenuminfo · notice · warn · error
confidenceuint8100 للرصد المباشر، وقيمة أقل للاستنتاجات
subjectEntityRefالكيان الذي يدور الحدث حوله
relatedEntityRef[]الكيانات الأخرى المرتبطة بالحدث
attrsobjectحقول خاصة بهذا النوع من الأحداث
evidenceobjectالرصد الخام بعد إزالة التفاصيل غير اللازمة
dedup_keystringمفتاح يجمع تكرارات الحدث نفسه في سجل واحد
countuint32عدد التكرارات المجمّعة

لماذا ساعتان

يحتاج الخط الزمني إلى الإجابة عن سؤالين مختلفين: متى وقع الحدث، وما ترتيبه الدقيق بين الأحداث الأخرى.

ts_wall

تجيب عن سؤال: متى وقع الحدث؟ وهي التي يستخدمها المستخدم في الاستعلام.

قد تقفز عند تصحيح NTP أو استئناف جهاز افتراضي أو تعديل الوقت يدويًا.

ts_mono

تحدد ترتيب الأحداث ولا تتأثر بتعديل ساعة النظام.

تُعاد من الصفر بعد الإقلاع، ولا يمكن مقارنتها بين جهازين.

يُبقي تسجيل الساعتين ترتيب الخط الزمني صحيحًا حتى عند تعديل ساعة النظام، ويمنع ظهور تسلسل منطقي ظاهريًا لكنه خاطئ.

كما يسمح باكتشاف system.clock_step عندما تتغيّر الساعة المدنية من دون تغيّر مماثل في الساعة المطّردة.

عنوان الشبكة ليس هوية ثابتة

كيف نتتبّع الجهاز نفسه على مدى أسبوع؟

لا يوجد معرّف شبكي بسيط يصلح لهذه المهمة. فعنوان IP قد ينتقل، وعنوان MAC يمكن تزويره أو تغييره عشوائيًا، وقد لا يتوفر اسم مضيف أصلًا.

لذلك لا يعامل NetRewind العنوان على أنه هوية. تشير الأحداث إلى host_id ثابت يُحدّد عند الاستقبال بالرجوع إلى جدول هويات زمني يسجّل الصفة والفترة التي ارتبطت فيها بالجهاز.

مخرجات مسجّلة
identity_binding
host_id ULID ثابت
attr_type mac | ipv4 | ipv6 | hostname | switch_port | dhcp_client_id
attr_value نصّ
valid_from طابع زمني
valid_to طابع زمني | NULL -- NULL تعني أنها ما زالت سارية
confidence uint8
جدول الهويات الزمني
  • يحمل EntityRef.ID المعرّف بعد حسم الهوية، ويبقى فارغًا إذا تعذّر ذلك.
  • يعرض EntityRef.Label الاسم المناسب للمستخدم.
  • ويحتفظ EntityRef.Attrs بالقيم الخام، مثل mac وip وifindex، حتى يظل الحدث قابلًا للفهم إذا تبيّن لاحقًا أن حسم الهوية كان خاطئًا.

مضيفواجهةجهازشبكة فرعية / VLANخدمةتدفّقراصد

بهذا التصميم يمكن البحث عن عنوان في وقت سابق واسترجاع الجهاز الذي كان يستخدمه حينها، من دون خلطه بالجهاز الذي يستخدم العنوان اليوم.

ويجري حسم الهوية عند استقبال الحدث، لذلك لا تتغيّر النتيجة باختلاف وقت تنفيذ الاستعلام.

قيد معلوم

تغيير عناوين MAC عشوائيًا يحدّ من القدرة على تتبّع الأجهزة المحمولة على المدى الطويل. يساعد dhcp client-id والبصمة السلوكية في تقليل المشكلة، لكنهما لا يحلانها بالكامل.

العائلات التسع

ينتمي كل حدث إلى إحدى هذه العائلات. يحدد الجزء السابق للنقطة طبقة الشبكة، ويصف الجزء اللاحق تغيّر الحالة الذي وقع.

توجد سبعة أنواع موزعة على أربع عائلات معرّفة في المخطط لكنها غير منتجة حاليًا، كما لا يوجد مصدر لعائلة change.* حتى الآن. نعرضها بوضوح حتى يعكس الفهرس حدود التنفيذ الفعلية.

المصادر أدناه خاصة بـLinux. وعلى Windows تتوفر مصادر لأحداث link.* ومداخل الجيران ضمن l2.* والمسارات والعناوين ضمن l3.* عبر IP Helper API بدل netlink. ولا تتوفر في هذا الإصدار مصادر Windows لعائلات flow.* وpolicy.* وdhcp.* وdns.* وmetric.*. يوضح تقرير قدرات المسجّل ما يراقبه كل بناء بدقة.

link.*

الطبقة الأولى · netlink

  • link.up
  • link.down
  • link.flap
  • link.mtu_changed
  • link.error_rate_high

يميّز attrs.cause بين الإيقاف الإداري للمنفذ وسقوط الرابط فعليًا، لأن لكل حالة سببًا ومعالجة مختلفين. ويحسب link.flap تكرار سقوط الرابط فقط، ولا يعتبر الإيقاف الإداري تذبذبًا. أما link.error_rate_high فيعتمد على عدّادات أخطاء الواجهة، ويظهر عندما تتجاوز الأخطاء نسبة محددة من إجمالي الحزم.

l2.*

الطبقة الثانية · جدول الجيران في netlink

  • l2.arp_binding_new
  • l2.arp_binding_changed
  • l2.mac_moved
  • l2.duplicate_ip
  • l2.neighbor_failed

يُعد l2.arp_binding_changed من أهم أحداث هذه الطبقة؛ فتغيّر ربط ARP في شبكة عاملة قد يشير إلى اختطاف أو تحويل عند العطل أو إدخال ثابت غير صحيح. وإذا كان العنوان هو البوابة الافتراضية، ترتفع الخطورة من warn إلى error لأن الأثر المحتمل أكبر. أما l2.lldp_neighbor_changed وl2.vlan_seen فهما معرّفان في المخطط، لكن مجمّع LLDP لم يُنفذ بعد، لذلك لا يرى المسجّل طوبولوجيا المبدّلات حاليًا.

l3.*

الطبقة الثالثة · مسارات netlink وعناوينه، وICMP من مقبس الحزم

  • l3.route_added
  • l3.route_removed
  • l3.route_changed
  • l3.default_route_changed
  • l3.addr_added
  • l3.addr_removed
  • l3.icmp_unreachable
  • l3.mtu_blackhole

لا يُسجّل تغيّر المسار إلا عندما يتبدّل المسار الفائز لوجهة معينة؛ فإضافة مسار احتياطي لا تعني أن حركة المرور انتقلت إليه. ويأتي آخر حدثين من قراءة ICMP لا من netlink. من أبرزها l3.mtu_blackhole، لأنه يكشف مسارًا يمرر الحزم الصغيرة ويسقط الكبيرة بصمت، وهو عطل يُنسب كثيرًا إلى التطبيق بالخطأ.

flow.*

الطبقة الرابعة · eBPF على sock/inet_sock_set_state وtcp/tcp_receive_reset

  • flow.first_failure_for_pair
  • flow.handshake_fail
  • flow.reset
  • flow.timeout_no_close
  • flow.rollup

يشير flow.first_failure_for_pair إلى فشل اتصال بين جهازين نجح اتصالهما سابقًا، ولذلك فهو أقوى من فشل محاولة اتصال عشوائية. لا يسجّل NetRewind كل اتصال منفردًا؛ بل يجمع النشاط الطبيعي في flow.rollup كل 10 ثوانٍ، ويسجّل الحالات الشاذة فقط. وتفصل نقطة تتبّع أخرى بين الرفض الصريح والاتصال الذي توقف عن الرد، فهما حالتان مختلفتان رغم تشابه العَرَض. ولهذا لا توجد أحداث flow.open أو flow.close. أما flow.retransmit_spike فهو معرّف في المخطط لكنه غير منتج حاليًا.

dhcp.* · dns.*

التسمية والعنونة · مقبس حزم مُرشَّح

  • dhcp.server_seen
  • dhcp.offer
  • dhcp.ack
  • dhcp.nak
  • dhcp.lease_changed
  • dns.resolver_changed
  • dns.query_fail
  • dns.latency_spike

يجمع هذا المصدر بيانات وصفية فقط ولا يقرأ محتوى الحزم. يسمح مرشّح cBPF المرتبط بالمقبس بمرور ICMP على IPv4 وUDP على ثلاثة منافذ، ولا تُنسخ الحزم الأخرى إلى فضاء المستخدم. المرشّح مكتوب كتعليمات قابلة للمراجعة في المستودع بدل توليده وقت التشغيل من تعبير tcpdump، لأنه يحدد حدود البيانات التي تصل إلى العملية. ويكشف dhcp.server_seen وجود خادم DHCP ثانٍ يستجيب على القطاع، وهو عطل يصعب اكتشافه يدويًا لأن الأجهزة المتضررة ليست هي الجهاز الذي يسبب المشكلة.

policy.*

قرارات الترشيح · nftables، باستطلاع دوري

  • policy.rule_changed

يفحص المسجّل قواعد nftables كل 5 ثوانٍ ويقارن بصمتها، فيظهر تغيّر سياسة جدار الحماية على الخط الزمني نفسه مع نتائجه. ويحتفظ كل حدث باسم الجدول والسلسلة، مثل table inet filter / chain input :: tcp dport 9300 drop، لأن نقل القاعدة بين سلسلتين قد يغيّر أثرها حتى إذا بقي نصها كما هو. لا تسجل هذه العائلة نتيجة تغيير الترشيح؛ ففشل اتصال قائم يُسجّل في flow.first_failure_for_pair سواء سببه قاعدة محلية أو ACL على مبدّل أو جدار حماية بعيد. أما policy.drop_burst فهو معرّف لكنه يحتاج إلى عدّادات لا يجمعها المسجّل بعد.

metric.*

سلاسل مقيسة · مجسّات نشطة

  • metric.anomaly

هذا هو المجمّع الوحيد الذي يرسل حزمًا إلى الشبكة؛ فالرصد السلبي وحده لا يكشف غياب الرد. يرسل عددًا قليلًا من المجسّات كل دقيقة إلى أهداف محددة أو مستخرجة من جدول التوجيه. ويطلق metric.anomaly عندما تتوقف الردود أو يرتفع زمنها بوضوح مقارنة بخط الأساس الخاص بالهدف، لا بعتبة ثابتة. أما عدّادات الواجهات وحجم جدول conntrack فهما معرّفان كسلاسل، لكنهما غير مجمّعين بعد.

change.*

تغييرات مقصودة · تغذية خارجية

  • change.config_applied
  • change.device_reboot
  • change.admin_action

هذه العائلة غير مرتبطة بمجمّع حاليًا. من المخطط أن تأتي أحداثها من NetIntent أو من مقارنة إعدادات الأجهزة عبر SSH أو SNMP. وهدفها تسجيل التغييرات المقصودة التي كانت تُنفذ وقت العطل. تطابق قاعدة change-broke-a-path هذه الأحداث بالفعل، وستستفيد منها فور توفر مصدر يغذيها.

system.*

المسجّل على نفسه · داخلي

  • system.gap
  • system.drop
  • system.clock_step
  • system.start
  • system.stop

تسجّل هذه العائلة صحة المسجّل نفسه. يقيس system.gap مدة انقطاع الرصد اعتمادًا على نبضة كل 10 ثوانٍ، بينما يسجّل system.drop عدد الأحداث التي أُسقطت بسبب وصولها أسرع من قدرة القراءة. تُنتج الأنواع الخمسة حاليًا، وتوجد قاعدتان ضمن القواعد المشحونة لإظهار فجوة في السجل أو توقف مجمّع واحد بينما تستمر بقية المجمّعات. الحالة الثانية أخطر لأنها قد تترك الخط الزمني يبدو طبيعيًا رغم نقص مصدر كامل.

محرّك الإسناد

استُلهم الاسم من منهج الإسناد في علوم الحديث، حيث تتكوّن سلسلة نقل معروفة الحلقات ويمكن التحقق من كل واحدة منها. وبالمبدأ نفسه يميّز المحرّك بين السببية والتزامن والترتيب الزمني، ولا يدّعي علاقة سببية من دون دليل.

ينشئ المحرّك الحوادث، بينما تقتصر مهمة المجمّعات على تسجيل ما رصدته. وحده محرّك الإسناد يفسّر العلاقة بين الأحداث.

مخرجات مسجّلة
incident_id, opened_at, closed_at, status
title, severity, confidence
root_cause { kind, entity_ref, confidence }
chain[] { seq, event_id, relation, why, evidence_ref }
victims[] host_id[]
rule_id
بنية الحادثة

وصل العَرَض؛ فما الذي تغيّر قبله؟

تتكوّن القاعدة من تسلسل مرتّب من البنود. أول بند إلزامي هو المرساة: الحدث الذي يدفع المحرّك إلى تقييم القاعدة.

يطابق المحرّك البنود التالية للمرساة إلى الأمام زمنيًا، ويبحث عن البنود السابقة إلى الخلف بدءًا من الأقرب. بهذه الآلية تستطيع القاعدة البحث عما تغيّر قبل ظهور العَرَض.

فعندما يتوقف اتصال كان يعمل أو يختفي مسار، يكون السؤال الأهم: ما التغيّر الذي سبق ذلك مباشرة؟

مخرجات مسجّلة
match:
- as: change # اختياري، ويُبحث عنه إلى الوراء
kinds: [l2.arp_binding_changed, l3.route_changed, link.down]
optional: true
why: هذا آخر ما تغيّر على المسار قبل أن ينقطع.
 
- as: breakage # لازم — وهو المرساة
kinds: [flow.first_failure_for_pair]
relation: causes
why: جهازان كانا يتصلان لم يعودا يُتِمّان المصافحة.
rules/change-broke-a-path.yaml

إذا لم يوجد تغيّر سابق داخل النافذة الزمنية، تُنشئ القاعدة حادثة من حلقة واحدة تعرض العَرَض فقط، من دون إضافة سبب متخيّل.

يختار المحرّك أقرب تغيّر زمنيًا. لذلك توضّح نصيحة القاعدة أن هذا التغيّر هو الأقرب في الوقت، وليس سببًا مثبتًا.

مفاتيح الربط تمنع خلط الحوادث المنفصلة

يحدد correlate_on الحقول التي يجب أن تتطابق في جميع الأحداث. من دونه قد يدمج المحرّك عطلين متزامنين على جهازين مختلفين في حادثة واحدة. ومع correlate_on: [subject] لا يمكن ربط تذبذب منفذ على مبدّل بسقوط جار على مبدّل آخر.

ثقة القاعدة حد أعلى

يمثّل الرقم في القاعدة أقصى ثقة ممكنة في استنتاجها. ثم يخفض المحرّك القيمة إلى مستوى أقل حدث ثقة في السلسلة، لأن الحادثة لا يمكن أن تكون أوثق من أضعف أدلتها.

تتحدّث الحادثة بدل أن تتكرر

إذا وصل بند اختياري بعد إنشاء الحادثة، يحدّث المحرّك الحادثة القائمة بالمعلومات الجديدة بدل إنشاء حادثة مشابهة.

يحافظ المحرّك على الترتيب الزمني

البنود تسلسل مرتب. يبحث المحرّك عن كل بند بعد سابقه داخل النافذة الزمنية، ولا يقبل سلسلة تعكس الترتيب الذي تصفه القاعدة.

الإشارة التي تبدأ منها السلسلة

flow.first_failure_for_pair

فشل اتصال كان يعمل قبل وقت قصير

هذه من أقوى الإشارات المنفردة في النظام، ومنها تبدأ السلسلة السببية المعروضة أعلاه.

فشل محاولة اتصال واحدة ليس دليلًا كافيًا؛ فقد يكون المنفذ مغلقًا أصلًا. لكن فشل اتصال بين جهازين نجح اتصالهما قبل وقت قصير يعني أن شيئًا ما تغيّر، مثل قاعدة ترشيح أو ACL أو مسار أو خدمة.

عاديّ
فشل اتصال بجهاز لم يسبق نجاح الاتصال به. لا يمكن استنتاج وجود تغيّر.
دليل
فشل اتصال بجهاز نجح الاتصال به قبل دقائق. توجد هنا إشارة واضحة إلى تغيّر ما بين الجهازين.
  • يتكوّن الزوج من المصدر والوجهة ومنفذ الوجهة. أما منفذ المصدر المؤقت فليس جزءًا من العلاقة.
  • يُحفظ آخر نجاح لمدة 24 ساعة؛ لأن نجاح خدمة قبل شهر لا يثبت أن حالتها تغيّرت للتو.
  • يُبلَّغ عن الانقطاع مرة واحدة. وإذا نجح الاتصال مجددًا، يصبح الزوج جاهزًا للإبلاغ عن أي انقطاع لاحق.

قيد معروف تُحفظ ذاكرة الاتصالات الناجحة داخل العملية فقط. بعد إعادة تشغيل المسجّل لا يتوفر خط أساس سابق، لذلك يُسجّل handshake_fail عاديًا إلى أن يرى اتصالًا ناجحًا للزوج نفسه.

الانضباط

السببية ليست هي التزامن

توضّح كل وصلة في السلسلة نوع العلاقة. عبارة «سبّب» تفترض وجود آلية معروفة، بينما تعني «حدث في الوقت نفسه» أن الحدثين متزامنان فحسب. يستخدم المحرّك العلاقة الثانية عندما لا يملك دليلًا على السببية.

هذا التفريق ضروري لبناء خط زمني يمكن الوثوق به، ويمنع تحويل المصادفة الزمنية إلى استنتاج قاطع.

causes
which caused
علاقة سببية: هذا الحدث أدّى إلى الحدث التالي. تُستخدم فقط عندما تصف القاعدة آلية سببية معروفة، لا مجرد تقارب زمني.
correlates
and at the same time
وقع الحدثان في الوقت نفسه، من دون إثبات علاقة سببية. تعبّر بوضوح عن حدود ما يعرفه المحرّك، بدل أن تملأ الفراغ بالتخمين.
precedes
and then, without a known link
ترتيب زمني فقط. تُستخدم عندما لا توجد أدلة كافية على علاقة أقوى.

لا يمكن أن تتجاوز ثقة الحادثة ثقة الأدلة التي بُنيت عليها؛ لذلك يخفض المحرّك درجة ثقة القاعدة إلى مستوى أقل حدث مطابق.

الطريق الذي يسلكه الرصد

تحوّل المجمّعات ما ترصده إلى الظرف المشترك. ثم تضيف مرحلة حسم الهوية قيمة host_id ثابتة، ويحفظ المخزن الحدث مع تجميع تكراراته. بعد ذلك يقرأ محرّك الارتباط الأحداث ويُنشئ حوادث ذات سلسلة سببية واضحة.

لا يجوز لأي مرحلة أن ترفع درجة الثقة فوق ثقة المدخلات؛ لذلك قد تنخفض الثقة عبر المسار، لكنها لا ترتفع.

الطريق الذي يسلكه الرصدترسل المصادر بياناتها إلى مجمّعات الأحداث، فتحوّلها إلى الظرف المشترك. تضيف مرحلة حسم الهوية معرّف مضيف ثابتًا، ثم يحفظ المخزن الحدث. يقرأ محرّك الإسناد الأحداث ليُنشئ حوادث ذات سلسلة سببية واضحة، وتعرضها أداة سطر الأوامر وواجهة الويب ومصدّرات المقاييس.netlink · eBPF · nftables · مقبس حزم · مجسّات · IP Helperمجمّعات الأحداثinternal/collectالظرف المشتركinternal/eventحسم الهويّةinternal/identityمخزن الأحداثinternal/storeمحرّك الإسناد للترابطinternal/correlateحوادث بسلسلة سببية صريحةinternal/incidentCLIويبPrometheus / OTLP

يخدم المخزن ثلاثة مخارج: أداة سطر الأوامر، وواجهة ويب للقراءة فقط تعمل محليًا عبر netrewind serve، وتصدير المقاييس إلى Prometheus وOpenTelemetry.