الخلاصة

  • تحدد RFC 9669 ترميز تعليمات BPF ومجموعات توافق مسماة، وتجعل base32 فقط الحد الأدنى الإلزامي؛ أما مجموعة packet القديمة فحالتها Historical.
  • بقاء تعليمة قابلة للتنفيذ في runtime بعينه لا يثبت قابليتها للنقل، كما أن قبول البرنامج لا يثبت ربطه أو استدعاءه أو أثره الخارجي.

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

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

الفرق بين «معرّف في السجل» و«قدرة على هذه الآلة» هو الفرق بين التنسيق والواقع.

لماذا توجد مجموعات التوافق

تصف RFC 9669 تعليمة أساسية بعرض 64 بت، وتعليمة واسعة تضيف كلمة ثانية بعرض 64 بت. تتوزع الحقول بين opcode والسجلات وoffset والقيمة الفورية، ويحدد صنف التعليمة كيفية تفسيرها. يتيح ذلك للمترجم وruntime الاتفاق على معنى البايتات.

لكن الوثيقة لا تشترط دعم كل التعليميات. يجب دعم base32، ويمكن دعم مجموعات أخرى. تشمل base64 مجموعة base32، وتشمل atomic64 مجموعة atomic32، وتشمل divmul64 مجموعة divmul32. ومن يعلن مجموعة يلتزم بكل تعليماتها.

أما تعليمات الوصول القديمة إلى الحزم فتنتمي إلى مجموعة packet ويصفها RFC بأنها deprecated وينصح بعدم استخدامها. سجل IANA الأولي يجعل المجموعة Historical. هذا لا يمحو deployments موجودة ولا يمنع runtime محلياً من الاحتفاظ بها. لكنه يمنع تحويل إرث منصة بعينها إلى افتراض محمول غير معلن.

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

حالة Permanent أو Historical هي حقيقة حوكمة للتسجيل. ليست أمراً للنواة، ولا قياساً لانتشار التنفيذ، ولا وعداً من بطاقة offload. لذلك يجب أن يكون اكتشاف القدرات مرتبطاً بالهدف والإصدار والمعمارية والوقت.

فك التعليمة لا يثبت صلاحية البرنامج

قد يعرف runtime كل opcode في الملف، ومع ذلك يكون البرنامج غير صالح. يحسب القفز offset بوحدات 64 بت. وإذا وقع على الكلمة الثانية لتعليمة واسعة من 128 بت، يكون السلوك undefined. المعرفة بالقطع لا تكفي إذا كانت بنية control flow غير صحيحة.

وتضيف maps والوظائف قيوداً محلية. يصف ISA وظائف مجردة لحل العناوين والكائنات، لكن المنصة تحدد الموجود فعلاً. ويمكن لنوع برنامج أن يرى context وhelpers لا يراها نوع آخر. لهذا لا يساوي التوافق مع جدول التعليمات التوافق مع عقد API للهدف.

تقول RFC 9669 إن المتحققين يفحصون الانتهاء، وسلامة الذاكرة، وعقود API للمنصة، وتجنب السلوك غير المحدد، ثم تضع تفاصيل التحقق خارج نطاقها. المعنى واضح: المعيار يدير اللغة المشتركة، أما الدخول إلى سياق مميز فيظل قرار البيئة.

توثيق Linux يعرض مثالاً عملياً. يفحص المتحقق control flow أولاً، ثم يحاكي المسارات ويتابع أنواع السجلات وحالة stack. تتحكم callbacks الخاصة بنوع البرنامج في الحقول المسموحة ونماذج الوظائف. يمكن لعنوان أن يبدو ضمن مجال عددي صحيح، لكنه يُرفض لأن نوع المؤشر أو المحاذاة أو الحدود غير صحيحة.

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

التحميل والربط والاستدعاء لا يحدث بعضها تلقائياً

بعد قبول المتحقق قد ينشئ loader كائن برنامج ويحصل على ID. هذا إيصال وجود، لا إيصال تشغيل. يجب أن يربط link الجيل الصحيح بالـ hook الصحيح. وبعد الربط ينبغي أن تمر events من ذلك الموضع كي يحدث invocation.

قد يبقى العداد صفراً لأن hook غير صحيح، أو لأن traffic لم يمر، أو لأن selector يشير إلى interface أو cgroup آخر. وقد يرتفع العداد من دون تحقق النتيجة الخارجية. map يكتبها البرنامج دليل من داخل الآلية، ولا يستطيع وحده إثبات أن packet لم يسلك مساراً موازياً أو أن transaction تغيرت بصورة durable.

التوقيع لا يدمج هذه المراحل. يوضح توثيق Linux الحالي أن التوقيع لا يستبدل permissions أو المتحقق. BPF_SIG_VERIFIED في نقطة قبول مبكرة يعني أن التوقيع صحيح وفق تغطيته، لا أن البرنامج loaded. يمكن للفحص اللاحق أن يرفضه. هذا مثال خاص بـ Linux وليس التزاماً في RFC 9669، لكنه يبيّن قيمة اللغة الدقيقة.

كل انتقال يحتاج هوية جيل

السجل التشغيلي الجيد يبدأ بمصدر وبناء معروفين، ثم hash للكائن، واختيار compiler للميزات، ومجموعات target، وruntime والمعمارية ونوع البرنامج، ونتائج relocation، وقرار المتحقق وhash للسجل، وprogram ID، وlink وhook، وجيل maps، وأوقات attach وdetach، وعدد invocations، وقرار البرنامج، ثم ملاحظة مستقلة للنتيجة.

الإخفاقات ليست حالة واحدة: مجموعة غير مدعومة، تعليمة تاريخية، فشل relocation، رفض verifier، فشل load، فشل attach، غياب الاستدعاء، وعدم تطابق النتيجة. الاحتفاظ بها يحدد صاحب القرار الذي يجب أن يتدخل.

وفي replacement قد يُحمَّل كائن جديد ويبقى link القديم. وقد تتشارك أجيال متعددة map واحدة. إذا جمع dashboard السجلات باسم policy فقط، يمكنه أن يعرض metadata الجديدة فوق تنفيذ قديم. rollback يحتاج تعداد كل الروابط والكائنات، لا حذف أحدث ملف فحسب.

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

المصادر