
Rex هو إطار عمل امتدادات النواة آمن وقابل للاستخدام يسمح بتحميل وتنفيذ برامج امتداد النواة بلغة رست بدلاً من eBPF.
____ _______ __ _____ _ _
| _ \| ____\ \/ / | ____|_ _| |_ ___ _ __ ___(_) ___ _ __ ___
| |_) | _| \ / | _| \ \/ / __/ _ \ '_ \/ __| |/ _ \| '_ \/ __|
| _ <| |___ / \ | |___ > <| || __/ | | \__ \ | (_) | | | \__ \
|_| \_\_____/_/\_\ |_____/_/\_\\__\___|_| |_|___/_|\___/|_| |_|___|
ريكس (Rex) هو إطار عمل آمن وقابل للاستخدام لإضافات النواة يسمح بتحميل وتنفيذ برامج إضافات النواة المكتوبة بلغة رست (Rust) بدلاً من eBPF. على عكس الأطر القائمة على eBPF مثل Aya، لا تمر برامج ريكس عبر المدقق داخل النواة (in-kernel verifier)، بل يتم تنفيذ البرامج في المجموعة الفرعية الآمنة من لغة رست، حيث يقوم مترجم رست بإجراء فحوصات الأمان اللازمة ويولد كودًا أصليًا مباشرة. يتجنب هذا الأسلوب متطلبات التحقق المقيدة بشكل مفرط (مثل قيود تعقيد البرنامج) والأخطاء الغامضة الناتجة عن التحقق، مع توفير فرصة أفضل للتحسين في الواجهة الخلفية للمترجم الأصلي (أي LLVM) مقارنة بأسلوب الواجهة الخلفية لـ eBPF + JIT داخل النواة.
يدعم ريكس حاليًا الميزات التالية:
kprobe و perf_event و tracepoint و xdp و tc.ينفذ المثال التالي برنامج kprobe يتعلق باستدعاء نظام محدد ويحقن خطأ (محدد بواسطة errno) في استدعاء النظام على عملية (محددة بواسطة pid). يمكن العثور على المثال الكامل، بما في ذلك برنامج التحميل، في samples/error_injector.
#![no_std]
#![no_main]
use rex::kprobe::kprobe;
use rex::map::RexHashMap;
use rex::pt_regs::PtRegs;
use rex::rex_kprobe;
use rex::rex_map;
use rex::Result;
#[allow(non_upper_case_globals)]
#[rex_map]
static pid_to_errno: RexHashMap<i32, u64> = RexHashMap::new(1, 0);
#[rex_kprobe]
pub fn err_injector(obj: &kprobe, ctx: &mut PtRegs) -> Result {
obj.bpf_get_current_task()
.map(|t| t.get_pid())
.and_then(|p| obj.bpf_map_lookup_elem(&pid_to_errno, &p).cloned())
.map(|e| obj.bpf_override_return(ctx, e))
.ok_or(0)
}
يمكن العثور على المزيد من البرامج النموذجية في samples.
يمكنك العثور على الدليل المفصل هنا.
يمكن العثور على مستندات تصميم إضافية تحت docs.
تعتمد إضافات eBPF الحالية على مدقق eBPF داخل النواة لتوفير ضمانات الأمان. وهذا يؤدي للأسف إلى مشكلات في سهولة الاستخدام حيث يتم رفض البرامج الآمنة من قبل المدقق، بما في ذلك على سبيل المثال لا الحصر:
يهدف ريكس إلى معالجة هذه المشكلات من خلال الاستفادة مباشرة من ضمان الأمان من Rust الآمن. يمكن للمطورين تنفيذ برامجهم بأي طريقة يمكن كتابتها بلغة Rust الآمنة مع قيود قليلة، ولم يعودوا بحاجة للقلق بشأن تعقيد البرنامج، أو مولد الكود، أو إيجاد طريقة (غالبًا ما تكون غير بديهية) للتعبير عن نفس المنطق لإرضاء المدقق.
نوضح ذلك مع تنفيذ BPF Memcached Cache (BMC)، وهو برنامج إضافة متطور لتسريع Memcached. كبرنامج eBPF معقد، تم إجبار BMC على التقسيم إلى عدة مكونات متصلة بواسطة tail-calls من BPF واستخدام تطبيقات حلقات/فروع غير ملائمة لإرضاء المدقق، وهي غير ضرورية تمامًا في تنفيذه في ريكس.
على سبيل المثال، نعرض الكود في منطق إبطال التخزين المؤقت لـ BPF-BMC الذي يبحث عن أمر SET في حمولة الحزمة:
// Searches for SET command in payload
for (unsigned int off = 0;
off < BMC_MAX_PACKET_LENGTH && payload + off + 1 <= data_end;
off++) {
if (set_found == 0 && payload[off] == 's' &&
payload + off + 3 <= data_end && payload[off + 1] == 'e' &&
payload[off + 2] == 't') {
off += 3;
set_found = 1;
}
...
}
لا يقدم الكود قيدًا إضافيًا في الحلقة (off < BMC_MAX_PACKET_LENGTH) فقط من أجل اجتياز المدقق، بل يستخدم أيضًا كودًا نموذجيًا متكررًا للتحقق من نهايات الحزمة (data_end) ومنطقًا معقدًا لمطابقة السلسلة "set" في الحزمة.
لا شيء من هذه الأعباء مطلوب بقوة Rust الآمنة في ريكس، الذي ليس لديه حدود تعقيد ويوفر حرية أكبر في التنفيذ:
let set_iter = payload.windows(4).enumerate().filter_map(|(i, v)| {
if v == b"set " {
Some(i)
} else {
None
}
});
يمكن العثور على التنفيذ الكامل لـ BMC في ريكس في samples/bmc.
ريكس مرخص بموجب ترخيص GPLv2. الوحدات الفرعية (Linux و Rust و LLVM) في هذا المستودع مرخصة بموجب شروطها الخاصة. يرجى الاطلاع على ملفات الترخيص المقابلة لمزيد من التفاصيل. بالإضافة إلى ذلك، فإن معيار memcached مرخص بموجب ترخيص MIT.