چھوٹے بچوں کے لیے بی پی ایف، حصہ صفر: کلاسک بی پی ایف

Berkeley Packet Filters (BPF) ایک لینکس کرنل ٹیکنالوجی ہے جو کئی سالوں سے انگریزی زبان کی تکنیکی اشاعتوں کے صفحہ اول پر ہے۔ کانفرنسیں بی پی ایف کے استعمال اور ترقی پر رپورٹوں سے بھری پڑی ہیں۔ ڈیوڈ ملر، لینکس نیٹ ورک سب سسٹم مینٹینر، لینکس پلمبرز 2018 میں اپنی گفتگو کہتے ہیں۔ "یہ گفتگو XDP کے بارے میں نہیں ہے" (XDP BPF کے لیے استعمال کا ایک کیس ہے)۔ برینڈن گریگ نے عنوان سے گفتگو کی۔ لینکس بی پی ایف سپر پاورز. Toke Høiland-Jørgensen ہنستا ہےکہ دانا اب ایک مائکروکرنل ہے۔ تھامس گراف اس خیال کو فروغ دیتے ہیں۔ بی پی ایف کرنل کے لیے جاوا اسکرپٹ ہے۔.

Habré پر BPF کی ابھی تک کوئی منظم وضاحت نہیں ہے، اور اس لیے مضامین کی ایک سیریز میں میں کوشش کروں گا کہ ٹیکنالوجی کی تاریخ کے بارے میں بات کروں، فن تعمیر اور ترقی کے آلات کی وضاحت کروں، اور BPF کو استعمال کرنے کے اطلاق اور مشق کے شعبوں کا خاکہ پیش کروں۔ یہ مضمون، صفر، سیریز میں، کلاسک BPF کی تاریخ اور فن تعمیر کو بتاتا ہے، اور اس کے آپریٹنگ اصولوں کے راز کو بھی ظاہر کرتا ہے۔ tcpdump, seccomp, strace، اور بہت کچھ.

بی پی ایف کی ترقی کو لینکس نیٹ ورکنگ کمیونٹی کے ذریعے کنٹرول کیا جاتا ہے، بی پی ایف کی اہم موجودہ ایپلی کیشنز نیٹ ورکس سے متعلق ہیں اور اس لیے اجازت کے ساتھ @eucariotمیں نے سیریز کو "چھوٹوں کے لیے BPF"، عظیم سیریز کے اعزاز میں کہا "چھوٹوں کے لیے نیٹ ورکس".

بی پی ایف کی تاریخ کا ایک مختصر کورسc)

جدید BPF ٹیکنالوجی اسی نام کے ساتھ پرانی ٹیکنالوجی کا ایک بہتر اور توسیع شدہ ورژن ہے، جسے اب کلاسک BPF کہا جاتا ہے تاکہ الجھنوں سے بچا جا سکے۔ کلاسک بی پی ایف کی بنیاد پر ایک معروف افادیت بنائی گئی تھی۔ tcpdump، میکانزم seccomp، نیز کم معروف ماڈیولز xt_bpf لیے iptables اور درجہ بندی کرنے والا cls_bpf. جدید لینکس میں، کلاسک بی پی ایف پروگرام خود بخود نئی شکل میں ترجمہ ہو جاتے ہیں، تاہم، صارف کے نقطہ نظر سے، API اپنی جگہ پر برقرار ہے اور کلاسک بی پی ایف کے لیے نئے استعمالات، جیسا کہ ہم اس مضمون میں دیکھیں گے، اب بھی پائے جا رہے ہیں۔ اس وجہ سے، اور یہ بھی کہ لینکس میں کلاسیکی بی پی ایف کی ترقی کی تاریخ کے بعد، یہ واضح ہو جائے گا کہ یہ اپنی جدید شکل میں کیسے اور کیوں تیار ہوا، میں نے کلاسیکی بی پی ایف کے بارے میں ایک مضمون سے آغاز کرنے کا فیصلہ کیا۔

پچھلی صدی کے اسی کی دہائی کے آخر میں، لارنس برکلے لیبارٹری کے انجینئرز اس سوال میں دلچسپی لینے لگے کہ ہارڈ ویئر پر نیٹ ورک کے پیکٹ کو کس طرح درست طریقے سے فلٹر کیا جائے جو پچھلی صدی کے اسی کی دہائی کے آخر میں جدید تھا۔ فلٹرنگ کا بنیادی خیال، اصل میں CSPF (CMU/Stanford Packet Filter) ٹیکنالوجی میں لاگو کیا گیا، غیر ضروری پیکٹوں کو جلد از جلد فلٹر کرنا تھا، یعنی کرنل اسپیس میں، کیونکہ یہ غیر ضروری ڈیٹا کو صارف کی جگہ میں کاپی کرنے سے گریز کرتا ہے۔ کرنل اسپیس میں یوزر کوڈ چلانے کے لیے رن ٹائم سیکیورٹی فراہم کرنے کے لیے، ایک سینڈ باکس والی ورچوئل مشین استعمال کی گئی تھی۔

تاہم، موجودہ فلٹرز کے لیے ورچوئل مشینیں اسٹیک پر مبنی مشینوں پر چلانے کے لیے ڈیزائن کی گئی تھیں اور نئی RISC مشینوں پر اتنی مؤثر طریقے سے نہیں چلتی تھیں۔ نتیجے کے طور پر، برکلے لیبز کے انجینئرز کی کوششوں کے ذریعے، ایک نئی بی پی ایف (برکلے پیکٹ فلٹرز) ٹیکنالوجی تیار کی گئی، جس کا ورچوئل مشین فن تعمیر Motorola 6502 پروسیسر کی بنیاد پر ڈیزائن کیا گیا تھا - ایسی معروف مصنوعات کا ورک ہارس۔ ایپل II یا NES. نئی ورچوئل مشین نے موجودہ حل کے مقابلے فلٹر کی کارکردگی میں دس گنا اضافہ کیا۔

بی پی ایف مشین فن تعمیر

ہم مثالوں کا تجزیہ کرتے ہوئے کام کرنے والے انداز میں فن تعمیر سے واقف ہوں گے۔ تاہم، شروع کرنے کے لیے، ہم یہ کہتے ہیں کہ مشین میں دو 32 بٹ رجسٹر تھے جو صارف کے لیے قابل رسائی تھے، ایک جمع کرنے والا A اور انڈیکس رجسٹر X، 64 بائٹس میموری (16 الفاظ)، لکھنے اور بعد میں پڑھنے کے لیے دستیاب ہے، اور ان اشیاء کے ساتھ کام کرنے کے لیے کمانڈز کا ایک چھوٹا نظام۔ پروگراموں میں مشروط اظہار کو لاگو کرنے کے لیے چھلانگ کی ہدایات بھی دستیاب تھیں، لیکن پروگرام کی بروقت تکمیل کی ضمانت کے لیے، چھلانگیں صرف آگے کی جا سکتی تھیں، یعنی خاص طور پر، لوپس بنانا منع تھا۔

مشین کو شروع کرنے کی عمومی اسکیم مندرجہ ذیل ہے۔ صارف BPF فن تعمیر کے لیے ایک پروگرام بناتا ہے اور استعمال کرتا ہے۔ کچھ کرنل میکانزم (جیسے سسٹم کال)، پروگرام کو لوڈ اور جوڑتا ہے۔ کچھ کو کرنل میں ایونٹ جنریٹر پر (مثال کے طور پر، ایک ایونٹ نیٹ ورک کارڈ پر اگلے پیکٹ کی آمد ہے)۔ جب کوئی واقعہ پیش آتا ہے تو، دانا پروگرام چلاتا ہے (مثال کے طور پر، ایک ترجمان میں)، اور مشین کی میموری اس سے مطابقت رکھتی ہے کچھ کو کرنل میموری ریجن (مثال کے طور پر آنے والے پیکٹ کا ڈیٹا)۔

مندرجہ بالا ہمارے لیے مثالوں کو دیکھنا شروع کرنے کے لیے کافی ہو گا: ہم ضرورت کے مطابق سسٹم اور کمانڈ فارمیٹ سے واقف ہو جائیں گے۔ اگر آپ ورچوئل مشین کے کمانڈ سسٹم کا فوری مطالعہ کرنا چاہتے ہیں اور اس کی تمام صلاحیتوں کے بارے میں جاننا چاہتے ہیں تو آپ اصل مضمون پڑھ سکتے ہیں۔ بی ایس ڈی پیکٹ فلٹر اور/یا فائل کا پہلا نصف حصہ Documentation/networking/filter.txt کرنل دستاویزات سے۔ اس کے علاوہ، آپ پریزنٹیشن کا مطالعہ کر سکتے ہیں libpcap: پیکٹ کیپچر کے لیے ایک فن تعمیر اور اصلاح کا طریقہ کار، جس میں مک کین، BPF کے مصنفین میں سے ایک، تخلیق کی تاریخ کے بارے میں بات کرتا ہے۔ libpcap.

ہم لینکس پر کلاسک بی پی ایف کے استعمال کی تمام اہم مثالوں پر غور کرتے ہیں: tcpdump (libpcap)، seccomp، xt_bpf, cls_bpf.

tcpdomp

بی پی ایف کی ترقی پیکٹ فلٹرنگ کے لیے فرنٹ اینڈ کی ترقی کے متوازی طور پر کی گئی تھی - ایک معروف افادیت tcpdump. اور، چونکہ یہ کلاسک بی پی ایف کے استعمال کی سب سے قدیم اور مشہور مثال ہے، جو بہت سے آپریٹنگ سسٹمز پر دستیاب ہے، اس لیے ہم اس کے ساتھ ٹیکنالوجی کا اپنا مطالعہ شروع کریں گے۔

(میں نے لینکس پر اس مضمون میں تمام مثالیں چلائیں۔ 5.6.0-rc6. بہتر پڑھنے کی اہلیت کے لیے کچھ کمانڈز کے آؤٹ پٹ میں ترمیم کی گئی ہے۔)

مثال: IPv6 پیکٹ کا مشاہدہ

آئیے تصور کریں کہ ہم ایک انٹرفیس پر تمام IPv6 پیکٹوں کو دیکھنا چاہتے ہیں۔ eth0. ایسا کرنے کے لیے ہم پروگرام چلا سکتے ہیں۔ tcpdump ایک سادہ فلٹر کے ساتھ ip6:

$ sudo tcpdump -i eth0 ip6

اس صورت میں، tcpdump فلٹر مرتب کرتا ہے۔ ip6 بی پی ایف آرکیٹیکچر بائیک کوڈ میں اور اسے کرنل کو بھیجیں (سیکشن میں تفصیلات دیکھیں Tcpdump: لوڈ ہو رہا ہے۔)۔ بھری ہوئی فلٹر انٹرفیس سے گزرنے والے ہر پیکٹ کے لیے چلائی جائے گی۔ eth0. اگر فلٹر غیر صفر قدر لوٹاتا ہے۔ n، پھر تک n پیکٹ کے بائٹس کو صارف کی جگہ پر کاپی کیا جائے گا اور ہم اسے آؤٹ پٹ میں دیکھیں گے۔ tcpdump.

چھوٹے بچوں کے لیے بی پی ایف، حصہ صفر: کلاسک بی پی ایف

یہ پتہ چلتا ہے کہ ہم آسانی سے معلوم کر سکتے ہیں کہ کون سا بائی کوڈ کرنل کو بھیجا گیا تھا۔ tcpdump کی مدد سے tcpdump، اگر ہم اسے آپشن کے ساتھ چلاتے ہیں۔ -d:

$ sudo tcpdump -i eth0 -d ip6
(000) ldh      [12]
(001) jeq      #0x86dd          jt 2    jf 3
(002) ret      #262144
(003) ret      #0

لائن صفر پر ہم کمانڈ چلاتے ہیں۔ ldh [12]، جس کا مطلب ہے "لوڈ ان رجسٹر A آدھا لفظ (16 بٹس) ایڈریس 12 پر واقع ہے اور صرف سوال یہ ہے کہ ہم کس قسم کی میموری کو ایڈریس کر رہے ہیں؟ جواب یہ ہے کہ پر x شروع ہوتا ہے (x+1)تجزیہ کردہ نیٹ ورک پیکٹ کا واں بائٹ۔ ہم ایتھرنیٹ انٹرفیس سے پیکٹ پڑھتے ہیں۔ eth0، اور یہ کا مطلب ہے کہکہ پیکٹ ایسا لگتا ہے (سادگی کے لیے، ہم فرض کرتے ہیں کہ پیکٹ میں کوئی VLAN ٹیگ نہیں ہیں):

       6              6          2
|Destination MAC|Source MAC|Ether Type|...|

تو حکم پر عمل کرنے کے بعد ldh [12] رجسٹر میں A ایک میدان ہو گا Ether Type - اس ایتھرنیٹ فریم میں منتقل ہونے والے پیکٹ کی قسم۔ لائن 1 پر ہم رجسٹر کے مواد کا موازنہ کرتے ہیں۔ A (پیکیج کی قسم) c 0x86dd، اور یہ اور ہے ہم جس قسم میں دلچسپی رکھتے ہیں وہ IPv6 ہے۔ لائن 1 پر، موازنہ کمانڈ کے علاوہ، دو اور کالم ہیں - jt 2 и jf 3 - وہ نشانات جن پر آپ کو جانے کی ضرورت ہے اگر موازنہ کامیاب ہو جائے (A == 0x86dd) اور ناکام۔ لہذا، ایک کامیاب کیس (IPv6) میں ہم لائن 2 پر جاتے ہیں، اور ایک ناکام کیس میں - لائن 3 پر۔ لائن 3 پر پروگرام کوڈ 0 کے ساتھ ختم ہوتا ہے (پیکٹ کو کاپی نہ کریں)، لائن 2 پر پروگرام کوڈ کے ساتھ ختم ہوتا ہے۔ 262144 (مجھے زیادہ سے زیادہ 256 کلو بائٹ پیکیج کاپی کریں)۔

ایک زیادہ پیچیدہ مثال: ہم ٹی سی پی پیکٹ کو منزل کی بندرگاہ سے دیکھتے ہیں۔

آئیے دیکھتے ہیں کہ فلٹر کیسا لگتا ہے جو تمام TCP پیکٹوں کو ڈیسٹینیشن پورٹ 666 کے ساتھ کاپی کرتا ہے۔ ہم IPv4 کیس پر غور کریں گے، کیونکہ IPv6 کیس آسان ہے۔ اس مثال کا مطالعہ کرنے کے بعد، آپ خود کو ایک مشق کے طور پر IPv6 فلٹر کو تلاش کر سکتے ہیں (ip6 and tcp dst port 666) اور عام کیس کے لیے ایک فلٹر (tcp dst port 666)۔ لہذا، جس فلٹر میں ہماری دلچسپی ہے وہ اس طرح لگتا ہے:

$ sudo tcpdump -i eth0 -d ip and tcp dst port 666
(000) ldh      [12]
(001) jeq      #0x800           jt 2    jf 10
(002) ldb      [23]
(003) jeq      #0x6             jt 4    jf 10
(004) ldh      [20]
(005) jset     #0x1fff          jt 10   jf 6
(006) ldxb     4*([14]&0xf)
(007) ldh      [x + 16]
(008) jeq      #0x29a           jt 9    jf 10
(009) ret      #262144
(010) ret      #0

ہم پہلے ہی جانتے ہیں کہ لائنیں 0 اور 1 کیا کرتی ہیں۔ لائن 2 پر ہم پہلے ہی چیک کر چکے ہیں کہ یہ ایک IPv4 پیکٹ ہے (ایتھر ٹائپ = 0x800) اور اسے رجسٹر میں لوڈ کریں۔ A پیکٹ کا 24 واں بائٹ۔ ہمارا پیکیج ایسا لگتا ہے۔

       14            8      1     1
|ethernet header|ip fields|ttl|protocol|...|

جس کا مطلب ہے کہ ہم رجسٹر میں لوڈ کرتے ہیں۔ A IP ہیڈر کا پروٹوکول فیلڈ، جو کہ منطقی ہے، کیونکہ ہم صرف TCP پیکٹ کاپی کرنا چاہتے ہیں۔ ہم پروٹوکول کا موازنہ کرتے ہیں۔ 0x6 (IPPROTO_TCPلائن 3 پر۔

لائنز 4 اور 5 پر ہم ایڈریس 20 پر موجود آدھے الفاظ کو لوڈ کرتے ہیں اور کمانڈ استعمال کرتے ہیں۔ jset چیک کریں کہ آیا تینوں میں سے ایک سیٹ ہے۔ جھنڈے - جاری کردہ ماسک پہننا jset تین سب سے اہم بٹس صاف کر دیے گئے ہیں۔ تین میں سے دو بٹس ہمیں بتاتے ہیں کہ آیا پیکٹ بکھرے ہوئے IP پیکٹ کا حصہ ہے، اور اگر ایسا ہے تو، آیا یہ آخری ٹکڑا ہے۔ تیسرا بٹ محفوظ ہے اور صفر ہونا چاہیے۔ ہم نامکمل یا ٹوٹے ہوئے پیکٹوں کو چیک نہیں کرنا چاہتے، اس لیے ہم تینوں بٹس کو چیک کرتے ہیں۔

لائن 6 اس فہرست میں سب سے زیادہ دلچسپ ہے۔ اظہار ldxb 4*([14]&0xf) یعنی ہم رجسٹر میں لوڈ کرتے ہیں۔ X پیکٹ کے پندرہویں بائٹ کے سب سے کم اہم چار بٹس کو 4 سے ضرب کیا گیا ہے۔ پندرہویں بائٹ کے سب سے کم اہم چار بٹس فیلڈ ہے انٹرنیٹ ہیڈر کی لمبائی IPv4 ہیڈر، جو الفاظ میں ہیڈر کی لمبائی کو محفوظ کرتا ہے، لہذا آپ کو پھر 4 سے ضرب کرنے کی ضرورت ہے۔ دلچسپ بات یہ ہے کہ اظہار 4*([14]&0xf) ایک خاص ایڈریسنگ اسکیم کے لیے ایک عہدہ ہے جو صرف اس فارم میں اور صرف ایک رجسٹر کے لیے استعمال کیا جا سکتا ہے۔ X، یعنی ہم بھی نہیں کہہ سکتے ldb 4*([14]&0xf) نہ ہی ldxb 5*([14]&0xf) (ہم صرف ایک مختلف آفسیٹ کی وضاحت کر سکتے ہیں، مثال کے طور پر، ldxb 4*([16]&0xf))۔ یہ واضح ہے کہ اس ایڈریسنگ اسکیم کو BPF میں درست طور پر وصول کرنے کے لیے شامل کیا گیا تھا۔ X (انڈیکس رجسٹر) IPv4 ہیڈر کی لمبائی۔

تو لائن 7 پر ہم آدھا لفظ لوڈ کرنے کی کوشش کرتے ہیں۔ (X+16). یاد رہے کہ 14 بائٹس ایتھرنیٹ ہیڈر کے زیر قبضہ ہیں، اور X IPv4 ہیڈر کی لمبائی پر مشتمل ہے، ہم سمجھتے ہیں کہ میں A TCP منزل کی بندرگاہ بھری ہوئی ہے:

       14           X           2             2
|ethernet header|ip header|source port|destination port|

آخر میں، لائن 8 پر ہم مطلوبہ قیمت کے ساتھ منزل کی بندرگاہ کا موازنہ کرتے ہیں اور لائن 9 یا 10 پر ہم نتیجہ واپس کرتے ہیں - چاہے پیکٹ کو کاپی کرنا ہے یا نہیں۔

Tcpdump: لوڈ ہو رہا ہے۔

پچھلی مثالوں میں، ہم نے خاص طور پر اس بات پر تفصیل سے غور نہیں کیا کہ پیکٹ فلٹرنگ کے لیے ہم بی پی ایف بائیک کوڈ کو دانا میں کیسے لوڈ کرتے ہیں۔ عام طور پر، tcpdump بہت سے سسٹمز پر پورٹ کیا گیا اور فلٹرز کے ساتھ کام کرنے کے لیے tcpdump لائبریری کا استعمال کرتا ہے۔ libpcap. مختصراً، استعمال کرتے ہوئے ایک انٹرفیس پر فلٹر لگانے کے لیے libpcap، آپ کو درج ذیل کام کرنے کی ضرورت ہے:

یہ دیکھنے کے لیے کہ کیسے فنکشن ہوتا ہے۔ pcap_setfilter لینکس میں لاگو کیا جاتا ہے، ہم استعمال کرتے ہیں strace (کچھ لائنیں ہٹا دی گئی ہیں):

$ sudo strace -f -e trace=%network tcpdump -p -i eth0 ip
socket(AF_PACKET, SOCK_RAW, 768)        = 3
bind(3, {sa_family=AF_PACKET, sll_protocol=htons(ETH_P_ALL), sll_ifindex=if_nametoindex("eth0"), sll_hatype=ARPHRD_NETROM, sll_pkttype=PACKET_HOST, sll_halen=0}, 20) = 0
setsockopt(3, SOL_SOCKET, SO_ATTACH_FILTER, {len=4, filter=0xb00bb00bb00b}, 16) = 0
...

آؤٹ پٹ کی پہلی دو لائنوں پر ہم تخلیق کرتے ہیں۔ خام ساکٹ تمام ایتھرنیٹ فریموں کو پڑھنے اور اسے انٹرفیس سے منسلک کرنے کے لیے eth0. کے ہماری پہلی مثال ہم جانتے ہیں کہ فلٹر ip چار BPF ہدایات پر مشتمل ہوگا، اور تیسری لائن پر ہم دیکھتے ہیں کہ آپشن کا استعمال کیسے کیا جاتا ہے۔ SO_ATTACH_FILTER سسٹم کال setsockopt ہم لمبائی 4 کے فلٹر کو لوڈ اور جوڑتے ہیں۔ یہ ہمارا فلٹر ہے۔

یہ بات قابل غور ہے کہ کلاسک بی پی ایف میں، فلٹر کو لوڈ کرنا اور جوڑنا ہمیشہ ایٹم آپریشن کے طور پر ہوتا ہے، اور بی پی ایف کے نئے ورژن میں، پروگرام کو لوڈ کرنا اور اسے ایونٹ جنریٹر سے بائنڈنگ کرنا وقت کے ساتھ الگ ہو جاتا ہے۔

پوشیدہ حقیقت

آؤٹ پٹ کا تھوڑا سا مکمل ورژن اس طرح لگتا ہے:

$ sudo strace -f -e trace=%network tcpdump -p -i eth0 ip
socket(AF_PACKET, SOCK_RAW, 768)        = 3
bind(3, {sa_family=AF_PACKET, sll_protocol=htons(ETH_P_ALL), sll_ifindex=if_nametoindex("eth0"), sll_hatype=ARPHRD_NETROM, sll_pkttype=PACKET_HOST, sll_halen=0}, 20) = 0
setsockopt(3, SOL_SOCKET, SO_ATTACH_FILTER, {len=1, filter=0xbeefbeefbeef}, 16) = 0
recvfrom(3, 0x7ffcad394257, 1, MSG_TRUNC, NULL, NULL) = -1 EAGAIN (Resource temporarily unavailable)
setsockopt(3, SOL_SOCKET, SO_ATTACH_FILTER, {len=4, filter=0xb00bb00bb00b}, 16) = 0
...

جیسا کہ اوپر بتایا گیا ہے، ہم اپنے فلٹر کو لائن 5 پر ساکٹ سے لوڈ اور منسلک کرتے ہیں، لیکن لائن 3 اور 4 پر کیا ہوتا ہے؟ یہ پتہ چلتا ہے کہ یہ libpcap ہمارا خیال رکھتا ہے - تاکہ ہمارے فلٹر کے آؤٹ پٹ میں ایسے پیکٹ شامل نہ ہوں جو اسے مطمئن نہیں کرتے، لائبریری جوڑتا ہے ڈمی فلٹر ret #0 (تمام پیکٹوں کو چھوڑ دیں)، ساکٹ کو نان بلاکنگ موڈ میں تبدیل کرتا ہے اور ان تمام پیکٹوں کو گھٹانے کی کوشش کرتا ہے جو پچھلے فلٹرز سے رہ سکتے ہیں۔

مجموعی طور پر، کلاسک بی پی ایف کا استعمال کرتے ہوئے لینکس پر پیکجوں کو فلٹر کرنے کے لیے، آپ کو ایک ڈھانچے کی شکل میں فلٹر کی ضرورت ہے جیسے struct sock_fprog اور ایک کھلا ساکٹ، جس کے بعد فلٹر کو سسٹم کال کا استعمال کرتے ہوئے ساکٹ سے منسلک کیا جا سکتا ہے۔ setsockopt.

دلچسپ بات یہ ہے کہ فلٹر کو کسی بھی ساکٹ سے منسلک کیا جا سکتا ہے، نہ صرف خام۔ یہاں مثال کے طور پر ایک ایسا پروگرام جو آنے والے UDP ڈیٹاگرامس سے پہلے دو بائٹس کے علاوہ باقی سب کو کاٹ دیتا ہے۔ (میں نے کوڈ میں تبصرے شامل کیے ہیں تاکہ مضمون میں بے ترتیبی نہ ہو۔)

استعمال کے بارے میں مزید تفصیلات setsockopt فلٹرز کو جوڑنے کے لیے، دیکھیں ساکٹ(7)، لیکن اپنے فلٹرز لکھنے کے بارے میں جیسے struct sock_fprog مدد کے بغیر tcpdump ہم سیکشن میں بات کریں گے بی پی ایف کو اپنے ہاتھوں سے پروگرام کرنا.

کلاسیکی بی پی ایف اور اکیسویں صدی

بی پی ایف کو 1997 میں لینکس میں شامل کیا گیا تھا اور یہ ایک طویل عرصے سے ایک ورک ہارس رہا ہے۔ libpcap بغیر کسی خاص تبدیلی کے (Linux کے لیے مخصوص تبدیلیاں، یقیناً، تھے، لیکن انہوں نے عالمی تصویر کو تبدیل نہیں کیا)۔ پہلی سنگین نشانیاں جو BPF تیار ہوں گی 2011 میں سامنے آئیں، جب ایرک ڈومازیٹ نے تجویز پیش کی۔ پیچ، جو کرنل میں Just In Time Compiler کا اضافہ کرتا ہے - BPF bytecode کو مقامی میں تبدیل کرنے کے لیے ایک مترجم x86_64 کوڈ

تبدیلیوں کے سلسلے میں جے آئی ٹی مرتب کرنے والا پہلا تھا: 2012 میں نمودار ہوا فلٹرز لکھنے کی صلاحیت سیکومکومپBPF کا استعمال کرتے ہوئے، جنوری 2013 میں وہاں تھا شامل کیا ماڈیول xt_bpf، جو آپ کو قواعد لکھنے کی اجازت دیتا ہے۔ iptables BPF کی مدد سے، اور اکتوبر 2013 میں تھا۔ شامل کیا ایک ماڈیول بھی cls_bpf، جو آپ کو BPF کا استعمال کرتے ہوئے ٹریفک درجہ بندی لکھنے کی اجازت دیتا ہے۔

ہم جلد ہی ان تمام مثالوں کو مزید تفصیل سے دیکھیں گے، لیکن سب سے پہلے یہ ہمارے لیے مفید ہو گا کہ بی پی ایف کے لیے صوابدیدی پروگراموں کو کیسے لکھنا اور مرتب کرنا ہے، کیونکہ لائبریری کی طرف سے فراہم کردہ صلاحیتیں libpcap محدود (سادہ مثال: فلٹر جنریٹڈ libpcap صرف دو اقدار واپس کر سکتے ہیں - 0 یا 0x40000) یا عام طور پر، جیسا کہ seccomp کے معاملے میں، لاگو نہیں ہوتے ہیں۔

بی پی ایف کو اپنے ہاتھوں سے پروگرام کرنا

آئیے BPF ہدایات کے بائنری فارمیٹ سے واقف ہوں، یہ بہت آسان ہے:

   16    8    8     32
| code | jt | jf |  k  |

ہر انسٹرکشن میں 64 بٹس ہوتے ہیں، جس میں پہلے 16 بٹس انسٹرکشن کوڈ ہوتے ہیں، پھر دو آٹھ بٹ انڈینٹ ہوتے ہیں، jt и jf، اور دلیل کے لیے 32 بٹس K، جس کا مقصد کمانڈ سے کمانڈ میں مختلف ہوتا ہے۔ مثال کے طور پر، حکم ret، جو پروگرام کو ختم کرتا ہے اس کا کوڈ ہوتا ہے۔ 6، اور واپسی کی قیمت مستقل سے لی جاتی ہے۔ K. C میں، ایک واحد BPF ہدایات کو ڈھانچے کے طور پر پیش کیا جاتا ہے۔

struct sock_filter {
        __u16   code;
        __u8    jt;
        __u8    jf;
        __u32   k;
}

اور پورا پروگرام ایک ڈھانچے کی شکل میں ہے۔

struct sock_fprog {
        unsigned short len;
        struct sock_filter *filter;
}

اس طرح، ہم پہلے سے ہی پروگرام لکھ سکتے ہیں (مثال کے طور پر، ہم انسٹرکشن کوڈز سے جانتے ہیں۔ ہے [1])۔ یہ فلٹر کی طرح نظر آئے گا ip6 کی ہماری پہلی مثال:

struct sock_filter code[] = {
        { 0x28, 0, 0, 0x0000000c },
        { 0x15, 0, 1, 0x000086dd },
        { 0x06, 0, 0, 0x00040000 },
        { 0x06, 0, 0, 0x00000000 },
};
struct sock_fprog prog = {
        .len = ARRAY_SIZE(code),
        .filter = code,
};

پروگرام prog ہم قانونی طور پر کال میں استعمال کر سکتے ہیں۔

setsockopt(sk, SOL_SOCKET, SO_ATTACH_FILTER, &prog, sizeof(prog))

مشین کوڈز کی شکل میں پروگرام لکھنا زیادہ آسان نہیں ہے، لیکن بعض اوقات یہ ضروری ہو جاتا ہے (مثال کے طور پر، ڈیبگنگ، یونٹ ٹیسٹ بنانے، Habré پر مضامین لکھنے وغیرہ)۔ سہولت کے لیے، فائل میں <linux/filter.h> مددگار میکرو کی تعریف کی گئی ہے - اوپر کی طرح وہی مثال دوبارہ لکھی جا سکتی ہے۔

struct sock_filter code[] = {
        BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 12),
        BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, ETH_P_IPV6, 0, 1),
        BPF_STMT(BPF_RET|BPF_K, 0x00040000),
        BPF_STMT(BPF_RET|BPF_K, 0),
}

تاہم، یہ اختیار بہت آسان نہیں ہے. یہ وہی ہے جو لینکس کرنل پروگرامرز نے استدلال کیا، اور اس وجہ سے ڈائریکٹری میں tools/bpf کلاسک بی پی ایف کے ساتھ کام کرنے کے لیے آپ کو ایک اسمبلر اور ڈیبگر مل سکتا ہے۔

اسمبلی کی زبان ڈیبگ آؤٹ پٹ سے بہت ملتی جلتی ہے۔ tcpdump، لیکن اس کے علاوہ ہم علامتی لیبل کی وضاحت کر سکتے ہیں۔ مثال کے طور پر، یہاں ایک پروگرام ہے جو TCP/IPv4 کے علاوہ تمام پیکٹوں کو چھوڑ دیتا ہے:

$ cat /tmp/tcp-over-ipv4.bpf
ldh [12]
jne #0x800, drop
ldb [23]
jneq #6, drop
ret #-1
drop: ret #0

پہلے سے طے شدہ طور پر، اسمبلر فارمیٹ میں کوڈ تیار کرتا ہے۔ <количество инструкций>,<code1> <jt1> <jf1> <k1>,...، TCP کے ساتھ ہماری مثال کے طور پر یہ ہوگا۔

$ tools/bpf/bpf_asm /tmp/tcp-over-ipv4.bpf
6,40 0 0 12,21 0 3 2048,48 0 0 23,21 0 1 6,6 0 0 4294967295,6 0 0 0,

سی پروگرامرز کی سہولت کے لیے، ایک مختلف آؤٹ پٹ فارمیٹ استعمال کیا جا سکتا ہے:

$ tools/bpf/bpf_asm -c /tmp/tcp-over-ipv4.bpf
{ 0x28,  0,  0, 0x0000000c },
{ 0x15,  0,  3, 0x00000800 },
{ 0x30,  0,  0, 0x00000017 },
{ 0x15,  0,  1, 0x00000006 },
{ 0x06,  0,  0, 0xffffffff },
{ 0x06,  0,  0, 0000000000 },

اس متن کو قسم کی ساخت کی تعریف میں نقل کیا جا سکتا ہے۔ struct sock_filterجیسا کہ ہم نے اس سیکشن کے آغاز میں کیا تھا۔

لینکس اور netsniff-ng ایکسٹینشنز

معیاری بی پی ایف کے علاوہ، لینکس اور tools/bpf/bpf_asm حمایت اور غیر معیاری سیٹ. بنیادی طور پر، ہدایات کا استعمال ڈھانچے کے شعبوں تک رسائی کے لیے کیا جاتا ہے۔ struct sk_buff، جو دانا میں نیٹ ورک پیکٹ کی وضاحت کرتا ہے۔ تاہم، مددگار ہدایات کی دوسری قسمیں بھی ہیں، مثال کے طور پر ldw cpu رجسٹر میں لوڈ ہو جائے گا A کرنل فنکشن چلانے کا نتیجہ raw_smp_processor_id(). (بی پی ایف کے نئے ورژن میں، ان غیر معیاری ایکسٹینشنز کو پروگراموں کو میموری، ڈھانچے، اور واقعات پیدا کرنے تک رسائی کے لیے کرنل ہیلپرز کے ایک سیٹ کے ساتھ فراہم کرنے کے لیے بڑھایا گیا ہے۔) یہاں فلٹر کی ایک دلچسپ مثال ہے جس میں ہم صرف کاپی کرتے ہیں۔ ایکسٹینشن کا استعمال کرتے ہوئے پیکٹ ہیڈر کو صارف کی جگہ میں داخل کریں۔ poff، پے لوڈ آفسیٹ:

ld poff
ret a

بی پی ایف ایکسٹینشنز میں استعمال نہیں کیا جا سکتا tcpdump، لیکن یہ یوٹیلیٹی پیکیج سے واقف ہونے کی ایک اچھی وجہ ہے۔ netsniff-ng، جس میں، دوسری چیزوں کے علاوہ، ایک اعلی درجے کا پروگرام شامل ہے۔ netsniff-ng، جس میں BPF کا استعمال کرتے ہوئے فلٹرنگ کے علاوہ، ایک موثر ٹریفک جنریٹر بھی ہے، اور اس سے زیادہ جدید tools/bpf/bpf_asm، ایک بی پی ایف اسمبلر کو بلایا گیا۔ bpfc. پیکیج کافی تفصیلی دستاویزات پر مشتمل ہے، مضمون کے آخر میں لنکس بھی دیکھیں۔

سیکومکومپ

لہذا، ہم پہلے سے ہی جانتے ہیں کہ صوابدیدی پیچیدگی کے BPF پروگراموں کو کیسے لکھنا ہے اور نئی مثالوں کو دیکھنے کے لیے تیار ہیں، جن میں سے پہلی seccomp ٹیکنالوجی ہے، جو BPF فلٹرز کا استعمال کرتے ہوئے، دستیاب سسٹم کال آرگیومنٹس کے سیٹ اور سیٹ کو منظم کرنے کی اجازت دیتی ہے۔ ایک دیا ہوا عمل اور اس کی اولاد۔

seccomp کا پہلا ورژن 2005 میں کرنل میں شامل کیا گیا تھا اور یہ زیادہ مقبول نہیں تھا، کیونکہ اس نے صرف ایک آپشن فراہم کیا تھا - کسی عمل کے لیے دستیاب سسٹم کالز کے سیٹ کو درج ذیل تک محدود کرنے کے لیے: read, write, exit и sigreturn، اور قواعد کی خلاف ورزی کرنے والے عمل کو استعمال کرتے ہوئے مارا گیا۔ SIGKILL. تاہم، 2012 میں، seccomp نے BPF فلٹرز استعمال کرنے کی صلاحیت کو شامل کیا، جس سے آپ کو اجازت دی گئی سسٹم کالز کے سیٹ کی وضاحت کرنے اور ان کے دلائل کی جانچ پڑتال کرنے کی اجازت دی گئی۔ (دلچسپ بات یہ ہے کہ کروم اس فعالیت کے پہلے صارفین میں سے ایک تھا، اور کروم کے لوگ فی الحال BPF کے نئے ورژن پر مبنی KRSI میکانزم تیار کر رہے ہیں اور لینکس سیکیورٹی ماڈیولز کو حسب ضرورت بنانے کی اجازت دے رہے ہیں۔) اضافی دستاویزات کے لنکس آخر میں مل سکتے ہیں۔ مضمون کے.

نوٹ کریں کہ seccomp کے استعمال کے بارے میں حب پر پہلے ہی مضامین موجود ہیں، ہو سکتا ہے کہ کوئی ان کو مندرجہ ذیل ذیلی حصوں کو پڑھنے سے پہلے (یا اس کے بجائے) پڑھنا چاہے۔ مضمون میں کنٹینرز اور سیکورٹی: seccomp seccomp کے استعمال کی مثالیں فراہم کرتا ہے، دونوں 2007 ورژن اور BPF کا استعمال کرتے ہوئے ورژن (فلٹر libseccomp کا استعمال کرتے ہوئے تیار کیے جاتے ہیں)، Docker کے ساتھ seccomp کے کنکشن کے بارے میں بات کرتا ہے، اور بہت سے مفید لنکس بھی فراہم کرتا ہے۔ مضمون میں سسٹمڈ کے ساتھ ڈیمن کو الگ کرنا یا "آپ کو اس کے لئے ڈوکر کی ضرورت نہیں ہے!" اس میں خاص طور پر سسٹمڈ چلانے والے ڈیمونز کے لیے سسٹم کالز کی بلیک لسٹ یا وائٹ لسٹ شامل کرنے کا طریقہ شامل ہے۔

اگلا ہم دیکھیں گے کہ فلٹرز کیسے لکھیں اور لوڈ کریں۔ seccomp ننگی سی میں اور لائبریری کا استعمال کرتے ہوئے libseccomp اور ہر آپشن کے فائدے اور نقصانات کیا ہیں، اور آخر میں، آئیے دیکھتے ہیں کہ پروگرام کے ذریعے seccomp کو کس طرح استعمال کیا جاتا ہے۔ strace.

seccomp کے لیے فلٹرز لکھنا اور لوڈ کرنا

ہم پہلے ہی جانتے ہیں کہ BPF پروگرام کیسے لکھتے ہیں، تو آئیے پہلے seccomp پروگرامنگ انٹرفیس کو دیکھتے ہیں۔ آپ عمل کی سطح پر ایک فلٹر ترتیب دے سکتے ہیں، اور تمام چائلڈ پروسیسز پابندیوں کے وارث ہوں گے۔ یہ سسٹم کال کا استعمال کرتے ہوئے کیا جاتا ہے۔ seccomp(2):

seccomp(SECCOMP_SET_MODE_FILTER, flags, &filter)

جہاں &filter - یہ ایک ایسے ڈھانچے کی طرف اشارہ ہے جو ہمارے لیے پہلے سے واقف ہے۔ struct sock_fprog، یعنی بی پی ایف پروگرام۔

seccomp کے پروگرام ساکٹ کے پروگراموں سے کیسے مختلف ہیں؟ منتقل شدہ سیاق و سباق۔ ساکٹ کے معاملے میں، ہمیں ایک میموری ایریا دیا گیا تھا جس میں پیکٹ تھا، اور seccomp کے معاملے میں ہمیں ایک ڈھانچہ دیا گیا تھا جیسے

struct seccomp_data {
    int   nr;
    __u32 arch;
    __u64 instruction_pointer;
    __u64 args[6];
};

یہاں nr شروع ہونے والی سسٹم کال کا نمبر ہے، arch - موجودہ فن تعمیر (نیچے اس پر مزید)، args - چھ تک سسٹم کال آرگیومنٹس، اور instruction_pointer یوزر اسپیس انسٹرکشن کی طرف اشارہ کرتا ہے جس نے سسٹم کال کی۔ اس طرح، مثال کے طور پر، رجسٹر میں سسٹم کال نمبر لوڈ کرنے کے لیے A ہمیں کہنا ہے

ldw [0]

seccomp پروگراموں کے لیے دیگر خصوصیات ہیں، مثال کے طور پر، سیاق و سباق تک صرف 32 بٹ الائنمنٹ کے ذریعے رسائی حاصل کی جا سکتی ہے اور آپ فلٹر لوڈ کرنے کی کوشش کرتے وقت آدھا لفظ یا بائٹ لوڈ نہیں کر سکتے۔ ldh [0] سسٹم کال seccomp لوٹے گا EINVAL. فنکشن بھری ہوئی فلٹرز کو چیک کرتا ہے۔ seccomp_check_filter() دانا (مزے کی بات یہ ہے کہ اصل کمٹ میں جس نے seccomp کی فعالیت کو شامل کیا، وہ اس فنکشن میں ہدایات کو استعمال کرنے کی اجازت شامل کرنا بھول گئے mod (بقیہ ڈویژن) اور اس کے اضافے کے بعد سے اب seccomp BPF پروگراموں کے لیے دستیاب نہیں ہے۔ ٹوٹ جائے گا ABI۔)

بنیادی طور پر، ہم seccomp پروگراموں کو لکھنے اور پڑھنے کے لیے پہلے سے ہی سب کچھ جانتے ہیں۔ عام طور پر پروگرام کی منطق کو نظام کالوں کی سفید یا سیاہ فہرست کے طور پر ترتیب دیا جاتا ہے، مثال کے طور پر پروگرام

ld [0]
jeq #304, bad
jeq #176, bad
jeq #239, bad
jeq #279, bad
good: ret #0x7fff0000 /* SECCOMP_RET_ALLOW */
bad: ret #0

304، 176، 239، 279 نمبر والی چار سسٹم کالز کی بلیک لسٹ چیک کرتا ہے۔ یہ سسٹم کالز کیا ہیں؟ ہم یقین سے نہیں کہہ سکتے، کیونکہ ہم نہیں جانتے کہ یہ پروگرام کس فن تعمیر کے لیے لکھا گیا تھا۔ لہذا، seccomp کے مصنفین پیشکش تمام پروگراموں کو آرکیٹیکچر چیک کے ساتھ شروع کریں (موجودہ فن تعمیر کو ایک فیلڈ کے طور پر سیاق و سباق میں اشارہ کیا گیا ہے arch ساخت struct seccomp_data)۔ فن تعمیر کی جانچ پڑتال کے ساتھ، مثال کا آغاز اس طرح نظر آئے گا:

ld [4]
jne #0xc000003e, bad_arch ; SCMP_ARCH_X86_64

اور پھر ہمارے سسٹم کال نمبرز کو کچھ قدریں ملیں گی۔

ہم seccomp استعمال کرنے کے لیے فلٹرز لکھتے اور لوڈ کرتے ہیں۔ libseccomp

مقامی کوڈ میں یا BPF اسمبلی میں فلٹرز لکھنا آپ کو نتیجہ پر مکمل کنٹرول حاصل کرنے کی اجازت دیتا ہے، لیکن ساتھ ہی، بعض اوقات پورٹیبل اور/یا پڑھنے کے قابل کوڈ کا ہونا بھی بہتر ہوتا ہے۔ لائبریری اس میں ہماری مدد کرے گی۔ libseccomp، جو سیاہ یا سفید فلٹرز لکھنے کے لیے ایک معیاری انٹرفیس فراہم کرتا ہے۔

آئیے، مثال کے طور پر، ایک ایسا پروگرام لکھتے ہیں جو صارف کی پسند کی بائنری فائل چلاتا ہے، جس نے پہلے سسٹم کالز کی بلیک لسٹ انسٹال کر رکھی تھی۔ مندرجہ بالا مضمون (پروگرام کو زیادہ پڑھنے کی اہلیت کے لیے آسان بنایا گیا ہے، مکمل ورژن مل سکتا ہے۔ یہاں):

#include <seccomp.h>
#include <unistd.h>
#include <err.h>

static int sys_numbers[] = {
        __NR_mount,
        __NR_umount2,
       // ... еще 40 системных вызовов ...
        __NR_vmsplice,
        __NR_perf_event_open,
};

int main(int argc, char **argv)
{
        scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_ALLOW);

        for (size_t i = 0; i < sizeof(sys_numbers)/sizeof(sys_numbers[0]); i++)
                seccomp_rule_add(ctx, SCMP_ACT_TRAP, sys_numbers[i], 0);

        seccomp_load(ctx);

        execvp(argv[1], &argv[1]);
        err(1, "execlp: %s", argv[1]);
}

پہلے ہم ایک صف کی وضاحت کرتے ہیں۔ sys_numbers بلاک کرنے کے لیے 40+ سسٹم کال نمبرز۔ پھر، سیاق و سباق کو شروع کریں۔ ctx اور لائبریری کو بتائیں کہ ہم کیا اجازت دینا چاہتے ہیں (SCMP_ACT_ALLOW) تمام سسٹم کالز بطور ڈیفالٹ (بلیک لسٹ بنانا آسان ہے)۔ پھر، ایک ایک کرکے، ہم بلیک لسٹ سے تمام سسٹم کالز کو شامل کرتے ہیں۔ فہرست سے سسٹم کال کے جواب میں، ہم درخواست کرتے ہیں۔ SCMP_ACT_TRAP، اس صورت میں seccomp عمل کو ایک سگنل بھیجے گا۔ SIGSYS اس کی وضاحت کے ساتھ کہ کس سسٹم کال نے قواعد کی خلاف ورزی کی۔ آخر میں، ہم پروگرام کو استعمال کرتے ہوئے کرنل میں لوڈ کرتے ہیں۔ seccomp_load، جو پروگرام کو مرتب کرے گا اور اسے سسٹم کال کا استعمال کرتے ہوئے عمل سے منسلک کرے گا۔ seccomp(2).

کامیاب تالیف کے لیے، پروگرام کا لائبریری سے منسلک ہونا ضروری ہے۔ libseccompمثال کے طور پر:

cc -std=c17 -Wall -Wextra -c -o seccomp_lib.o seccomp_lib.c
cc -o seccomp_lib seccomp_lib.o -lseccomp

کامیاب لانچ کی مثال:

$ ./seccomp_lib echo ok
ok

بلاک شدہ سسٹم کال کی مثال:

$ sudo ./seccomp_lib mount -t bpf bpf /tmp
Bad system call

ہم استعمال کرتے ہیں straceتفصیلات کے لیے:

$ sudo strace -e seccomp ./seccomp_lib mount -t bpf bpf /tmp
seccomp(SECCOMP_SET_MODE_FILTER, 0, {len=50, filter=0x55d8e78428e0}) = 0
--- SIGSYS {si_signo=SIGSYS, si_code=SYS_SECCOMP, si_call_addr=0xboobdeadbeef, si_syscall=__NR_mount, si_arch=AUDIT_ARCH_X86_64} ---
+++ killed by SIGSYS (core dumped) +++
Bad system call

ہم کیسے جان سکتے ہیں کہ پروگرام کو غیر قانونی سسٹم کال کے استعمال کی وجہ سے ختم کر دیا گیا تھا۔ mount(2).

لہذا، ہم نے لائبریری کا استعمال کرتے ہوئے ایک فلٹر لکھا libseccomp، غیر معمولی کوڈ کو چار لائنوں میں فٹ کرنا۔ اوپر کی مثال میں، اگر سسٹم کالز کی ایک بڑی تعداد موجود ہے، تو عمل درآمد کا وقت نمایاں طور پر کم کیا جا سکتا ہے، کیونکہ چیک صرف موازنہ کی فہرست ہے۔ اصلاح کے لیے، libseccomp نے حال ہی میں کیا تھا۔ پیچ شامل، جو فلٹر انتساب کے لیے تعاون کا اضافہ کرتا ہے۔ SCMP_FLTATR_CTL_OPTIMIZE. اس وصف کو 2 پر سیٹ کرنے سے فلٹر بائنری سرچ پروگرام میں تبدیل ہو جائے گا۔

اگر آپ یہ دیکھنا چاہتے ہیں کہ بائنری سرچ فلٹرز کیسے کام کرتے ہیں، تو اس پر ایک نظر ڈالیں۔ سادہ سکرپٹ، جو سسٹم کال نمبرز ڈائل کرکے BPF اسمبلر میں ایسے پروگرام تیار کرتا ہے، مثال کے طور پر:

$ echo 1 3 6 8 13 | ./generate_bin_search_bpf.py
ld [0]
jeq #6, bad
jgt #6, check8
jeq #1, bad
jeq #3, bad
ret #0x7fff0000
check8:
jeq #8, bad
jeq #13, bad
ret #0x7fff0000
bad: ret #0

کچھ بھی تیزی سے لکھنا ممکن نہیں ہوگا، کیونکہ BPF پروگرام انڈینٹیشن جمپ نہیں کر سکتے ہیں (مثال کے طور پر، ہم نہیں کر سکتے، jmp A یا jmp [label+X]) اور اس لیے تمام ٹرانزیشن جامد ہیں۔

seccomp اور strace

افادیت سب کو معلوم ہے۔ strace لینکس پر عمل کے رویے کا مطالعہ کرنے کے لیے ایک ناگزیر ٹول ہے۔ تاہم، بہت سے لوگوں نے بھی سنا ہے کارکردگی کے مسائل اس افادیت کا استعمال کرتے وقت. حقیقت یہ ہے کہ strace کا استعمال کرتے ہوئے لاگو کیا ptrace(2)، اور اس طریقہ کار میں ہم اس بات کی وضاحت نہیں کر سکتے کہ سسٹم کالز کے کس سیٹ پر ہمیں عمل کو روکنے کی ضرورت ہے، یعنی مثال کے طور پر، کمانڈز

$ time strace du /usr/share/ >/dev/null 2>&1

real    0m3.081s
user    0m0.531s
sys     0m2.073s

и

$ time strace -e open du /usr/share/ >/dev/null 2>&1

real    0m2.404s
user    0m0.193s
sys     0m1.800s

تقریباً ایک ہی وقت میں کارروائی کی جاتی ہے، حالانکہ دوسری صورت میں ہم صرف ایک سسٹم کال کو ٹریس کرنا چاہتے ہیں۔

نیا آپشن --seccomp-bpf، میں اضافہ ہوا strace ورژن 5.3، آپ کو کئی بار عمل کو تیز کرنے کی اجازت دیتا ہے اور ایک سسٹم کال کے ٹریس کے تحت اسٹارٹ اپ کا وقت پہلے سے ہی باقاعدہ اسٹارٹ اپ کے وقت سے موازنہ کیا جا سکتا ہے:

$ time strace --seccomp-bpf -e open du /usr/share/ >/dev/null 2>&1

real    0m0.148s
user    0m0.017s
sys     0m0.131s

$ time du /usr/share/ >/dev/null 2>&1

real    0m0.140s
user    0m0.024s
sys     0m0.116s

(یہاں، یقینا، اس میں تھوڑا سا دھوکہ ہے کہ ہم اس کمانڈ کے مین سسٹم کال کو ٹریس نہیں کر رہے ہیں۔ اگر ہم ٹریس کر رہے تھے، مثال کے طور پر، newfsstatپھر strace بغیر اتنی ہی سختی سے بریک لگائیں گے۔ --seccomp-bpf.)

یہ آپشن کیسے کام کرتا ہے؟ اس کے بغیر strace عمل سے جڑتا ہے اور اسے استعمال کرنا شروع کرتا ہے۔ PTRACE_SYSCALL. جب کوئی منظم عمل (کوئی) سسٹم کال جاری کرتا ہے تو کنٹرول کو منتقل کر دیا جاتا ہے۔ strace، جو سسٹم کال آرگیومینٹس کو دیکھتا ہے اور اسے چلاتا ہے۔ PTRACE_SYSCALL. کچھ وقت کے بعد، عمل سسٹم کال کو مکمل کرتا ہے اور جب اس سے باہر نکلتا ہے، کنٹرول دوبارہ منتقل ہوجاتا ہے۔ strace، جو واپسی کی قدروں کو دیکھتا ہے اور اس کا استعمال کرتے ہوئے عمل شروع کرتا ہے۔ PTRACE_SYSCALL، اور اسی طرح.

چھوٹے بچوں کے لیے بی پی ایف، حصہ صفر: کلاسک بی پی ایف

تاہم، seccomp کے ساتھ، اس عمل کو بالکل اسی طرح بہتر بنایا جا سکتا ہے جیسا کہ ہم چاہتے ہیں۔ یعنی، اگر ہم صرف سسٹم کال کو دیکھنا چاہتے ہیں۔ X، پھر ہم اس کے لیے BPF فلٹر لکھ سکتے ہیں۔ X قیمت واپس کرتا ہے SECCOMP_RET_TRACE، اور ان کالوں کے لیے جو ہمارے لیے دلچسپی سے خالی نہیں ہیں - SECCOMP_RET_ALLOW:

ld [0]
jneq #X, ignore
trace: ret #0x7ff00000
ignore: ret #0x7fff0000

اس معاملے میں strace ابتدائی طور پر عمل شروع ہوتا ہے PTRACE_CONTہمارے فلٹر پر ہر سسٹم کال کے لیے کارروائی کی جاتی ہے، اگر سسٹم کال نہیں ہے۔ X، پھر یہ عمل جاری رہتا ہے، لیکن اگر یہ X، پھر seccomp کنٹرول کو منتقل کرے گا۔ straceجو دلائل کو دیکھے گا اور جیسا عمل شروع کرے گا۔ PTRACE_SYSCALL (چونکہ seccomp میں سسٹم کال سے باہر نکلنے پر پروگرام چلانے کی صلاحیت نہیں ہے)۔ جب سسٹم کال واپس آتی ہے، strace کا استعمال کرتے ہوئے عمل کو دوبارہ شروع کرے گا PTRACE_CONT اور seccomp سے نئے پیغامات کا انتظار کریں گے۔

چھوٹے بچوں کے لیے بی پی ایف، حصہ صفر: کلاسک بی پی ایف

اختیار کا استعمال کرتے وقت --seccomp-bpf دو پابندیاں ہیں. سب سے پہلے، پہلے سے موجود عمل میں شامل ہونا ممکن نہیں ہوگا (آپشن -p پروگرامز strace)، چونکہ یہ seccomp کے ذریعے تعاون یافتہ نہیں ہے۔ دوسری بات یہ کہ اس کا کوئی امکان نہیں۔ کوئی چائلڈ پروسیسز کو دیکھیں، کیونکہ seccomp فلٹرز تمام چائلڈ پروسیسز سے وراثت میں ملتے ہیں بغیر اس کو غیر فعال کرنے کی صلاحیت کے۔

بالکل کس طرح پر تھوڑا سا مزید تفصیل strace کے ساتھ کام کرتا ہے seccomp سے پایا جا سکتا ہے۔ حالیہ رپورٹ. ہمارے لیے، سب سے دلچسپ حقیقت یہ ہے کہ کلاسک BPF جس کی نمائندگی seccomp کرتی ہے آج بھی استعمال ہوتی ہے۔

xt_bpf

آئیے اب نیٹ ورکس کی دنیا میں واپس چلتے ہیں۔

پس منظر: ایک طویل وقت پہلے، 2007 میں، بنیادی تھا شامل کیا ماڈیول xt_u32 نیٹ فلٹر کے لیے۔ یہ ایک اور بھی قدیم ٹریفک درجہ بندی کے ساتھ مشابہت کے ساتھ لکھا گیا تھا۔ cls_u32 اور آپ کو مندرجہ ذیل آسان آپریشنز کا استعمال کرتے ہوئے iptables کے لیے صوابدیدی بائنری قواعد لکھنے کی اجازت دی: پیکیج سے 32 بٹس لوڈ کریں اور ان پر ریاضی کی کارروائیوں کا ایک سیٹ انجام دیں۔ مثال کے طور پر،

sudo iptables -A INPUT -m u32 --u32 "6&0xFF=1" -j LOG --log-prefix "seen-by-xt_u32"

پیڈنگ 32 سے شروع ہونے والے IP ہیڈر کے 6 بٹس لوڈ کرتا ہے، اور ان پر ماسک لگاتا ہے۔ 0xFF (کم بائٹ لیں)۔ یہ میدان protocol IP ہیڈر اور ہم اس کا موازنہ 1 (ICMP) سے کرتے ہیں۔ ایک اصول میں آپ بہت سے چیکوں کو اکٹھا کر سکتے ہیں، اور آپ آپریٹر کو بھی پھانسی دے سکتے ہیں۔ @ - X بائٹس کو دائیں طرف منتقل کریں۔ مثال کے طور پر، قاعدہ

iptables -m u32 --u32 "6&0xFF=0x6 && 0>>22&0x3C@4=0x29"

چیک کرتا ہے کہ آیا TCP ترتیب نمبر برابر نہیں ہے۔ 0x29. میں مزید تفصیلات میں نہیں جاؤں گا، کیونکہ یہ پہلے ہی واضح ہے کہ اس طرح کے قواعد کو ہاتھ سے لکھنا زیادہ آسان نہیں ہے۔ مضمون میں BPF - بھولا ہوا بائی کوڈکے لیے استعمال اور اصول کی تخلیق کی مثالوں کے ساتھ کئی لنکس موجود ہیں۔ xt_u32. اس مضمون کے آخر میں لنکس بھی دیکھیں۔

2013 سے ماڈیول کی بجائے ماڈیول xt_u32 آپ بی پی ایف پر مبنی ماڈیول استعمال کر سکتے ہیں۔ xt_bpf. جس نے بھی اسے پڑھا ہے اسے اس کے آپریشن کے اصول کے بارے میں پہلے ہی واضح ہونا چاہئے: BPF bytecode کو iptables کے اصولوں کے طور پر چلائیں۔ آپ ایک نیا اصول بنا سکتے ہیں، مثال کے طور پر، اس طرح:

iptables -A INPUT -m bpf --bytecode <байткод> -j LOG

یہاں <байткод> - یہ اسمبلر آؤٹ پٹ فارمیٹ میں کوڈ ہے۔ bpf_asm بطور ڈیفالٹ، مثال کے طور پر،

$ cat /tmp/test.bpf
ldb [9]
jneq #17, ignore
ret #1
ignore: ret #0

$ bpf_asm /tmp/test.bpf
4,48 0 0 9,21 0 1 17,6 0 0 1,6 0 0 0,

# iptables -A INPUT -m bpf --bytecode "$(bpf_asm /tmp/test.bpf)" -j LOG

اس مثال میں ہم تمام UDP پیکٹوں کو فلٹر کر رہے ہیں۔ ماڈیول میں بی پی ایف پروگرام کا سیاق و سباق xt_bpfیقیناً، پیکٹ ڈیٹا کی طرف اشارہ کرتا ہے، iptables کے معاملے میں، IPv4 ہیڈر کے آغاز کی طرف۔ بی پی ایف پروگرام سے واپسی کی قیمت بولینجہاں false اس کا مطلب ہے کہ پیکٹ مماثل نہیں ہے۔

یہ واضح ہے کہ ماڈیول xt_bpf اوپر دی گئی مثال سے زیادہ پیچیدہ فلٹرز کو سپورٹ کرتا ہے۔ آئیے Cloudfare سے حقیقی مثالوں کو دیکھتے ہیں۔ کچھ عرصہ پہلے تک وہ ماڈیول استعمال کرتے تھے۔ xt_bpf DDoS حملوں سے بچانے کے لیے۔ مضمون میں بی پی ایف ٹولز کا تعارف وہ بتاتے ہیں کہ وہ کس طرح (اور کیوں) BPF فلٹرز تیار کرتے ہیں اور ایسے فلٹرز بنانے کے لیے یوٹیلیٹیز کے سیٹ کے لنکس شائع کرتے ہیں۔ مثال کے طور پر، افادیت کا استعمال کرتے ہوئے bpfgen آپ ایک BPF پروگرام بنا سکتے ہیں جو کسی نام کے لیے DNS استفسار سے مماثل ہو۔ habr.com:

$ ./bpfgen --assembly dns -- habr.com
ldx 4*([0]&0xf)
ld #20
add x
tax

lb_0:
    ld [x + 0]
    jneq #0x04686162, lb_1
    ld [x + 4]
    jneq #0x7203636f, lb_1
    ldh [x + 8]
    jneq #0x6d00, lb_1
    ret #65535

lb_1:
    ret #0

پروگرام میں ہم سب سے پہلے رجسٹر میں لوڈ کرتے ہیں۔ X لائن ایڈریس کا آغاز x04habrx03comx00 UDP ڈیٹاگرام کے اندر اور پھر درخواست کو چیک کریں: 0x04686162 <-> "x04hab" وغیرہ

تھوڑی دیر بعد، Cloudfare نے p0f -> BPF کمپائلر کوڈ شائع کیا۔ مضمون میں پیش ہے p0f BPF کمپائلر وہ اس بارے میں بات کرتے ہیں کہ p0f کیا ہے اور p0f دستخطوں کو BPF میں کیسے تبدیل کیا جائے:

$ ./bpfgen p0f -- 4:64:0:0:*,0::ack+:0
39,0 0 0 0,48 0 0 8,37 35 0 64,37 0 34 29,48 0 0 0,
84 0 0 15,21 0 31 5,48 0 0 9,21 0 29 6,40 0 0 6,
...

فی الحال Cloudfare استعمال نہیں کر رہا ہے۔ xt_bpf، چونکہ وہ XDP میں چلے گئے ہیں - BPF کا نیا ورژن استعمال کرنے کے اختیارات میں سے ایک، دیکھیں۔ L4Drop: XDP DDoS تخفیف.

cls_bpf

دانا میں کلاسک BPF استعمال کرنے کی آخری مثال درجہ بندی ہے۔ cls_bpf لینکس میں ٹریفک کنٹرول سب سسٹم کے لیے، 2013 کے آخر میں لینکس میں شامل کیا گیا اور تصوراتی طور پر قدیم کی جگہ لے کر cls_u32.

تاہم، ہم ابھی کام کی وضاحت نہیں کریں گے۔ cls_bpfچونکہ کلاسک BPF کے بارے میں علم کے نقطہ نظر سے یہ ہمیں کچھ نہیں دے گا - ہم پہلے ہی تمام فعالیت سے واقف ہو چکے ہیں۔ مزید برآں، بعد کے مضامین میں توسیع شدہ BPF کے بارے میں بات کرتے ہوئے، ہم اس درجہ بندی سے ایک سے زیادہ بار ملیں گے۔

کلاسک BPF استعمال کرنے کے بارے میں بات نہ کرنے کی ایک اور وجہ c cls_bpf مسئلہ یہ ہے کہ، توسیع شدہ بی پی ایف کے مقابلے میں، اس معاملے میں قابل اطلاق ہونے کا دائرہ یکسر محدود ہے: کلاسیکل پروگرام پیکیجز کے مواد کو تبدیل نہیں کر سکتے اور کالوں کے درمیان حالت کو محفوظ نہیں کر سکتے۔

لہذا اب وقت آگیا ہے کہ کلاسک بی پی ایف کو الوداع کہا جائے اور مستقبل پر نظر ڈالی جائے۔

کلاسک بی پی ایف کو الوداع

ہم نے دیکھا کہ کس طرح BPF ٹیکنالوجی، جو نوے کی دہائی کے اوائل میں تیار ہوئی، ایک چوتھائی صدی تک کامیابی کے ساتھ زندہ رہی اور آخر تک نئی ایپلی کیشنز ملیں۔ تاہم، اسٹیک مشینوں سے RISC میں منتقلی کی طرح، جس نے کلاسک BPF کی ترقی کے لیے ایک محرک کے طور پر کام کیا، 32 کی دہائی میں 64-bit سے XNUMX-bit مشینوں میں منتقلی ہوئی اور کلاسک BPF متروک ہونا شروع ہوا۔ اس کے علاوہ، کلاسک بی پی ایف کی صلاحیتیں بہت محدود ہیں، اور فرسودہ فن تعمیر کے علاوہ - ہمارے پاس بی پی ایف پروگراموں کی کالوں کے درمیان ریاست کو بچانے کی صلاحیت نہیں ہے، صارف کے براہ راست تعامل کا کوئی امکان نہیں ہے، بات چیت کا کوئی امکان نہیں ہے۔ کرنل کے ساتھ، ساختی فیلڈز کی محدود تعداد کو پڑھنے کے علاوہ sk_buff اور آسان ترین مددگار افعال شروع کرتے ہوئے، آپ پیکٹ کے مواد کو تبدیل نہیں کر سکتے اور انہیں ری ڈائریکٹ نہیں کر سکتے۔

درحقیقت، فی الحال لینکس میں کلاسک BPF کی باقیات API انٹرفیس ہے، اور کرنل کے اندر تمام کلاسک پروگرامز، چاہے وہ ساکٹ فلٹرز ہوں یا seccomp فلٹرز، خود بخود ایک نئے فارمیٹ، Extended BPF میں ترجمہ ہو جاتے ہیں۔ (ہم اگلے مضمون میں اس کے بارے میں بات کریں گے کہ یہ کیسے ہوتا ہے۔)

نئے فن تعمیر میں منتقلی 2013 میں شروع ہوئی، جب Alexey Starovoitov نے BPF اپ ڈیٹ سکیم کی تجویز پیش کی۔ 2014 میں اسی پیچ ظاہر ہونے لگے کور میں جہاں تک میں سمجھتا ہوں، اصل منصوبہ صرف 64 بٹ مشینوں پر زیادہ مؤثر طریقے سے چلانے کے لیے آرکیٹیکچر اور جے آئی ٹی کمپائلر کو بہتر بنانا تھا، لیکن اس کے بجائے ان اصلاحات نے لینکس کی ترقی میں ایک نئے باب کا آغاز کیا۔

اس سلسلے کے مزید مضامین نئی ٹیکنالوجی کے فن تعمیر اور ایپلی کیشنز کا احاطہ کریں گے، جسے ابتدائی طور پر اندرونی BPF، پھر توسیع شدہ BPF، اور اب صرف BPF کہا جاتا ہے۔

حوالہ جات

  1. اسٹیون میک کین اور وان جیکبسن، "بی ایس ڈی پیکٹ فلٹر: یوزر لیول پیکٹ کیپچر کے لیے ایک نیا فن تعمیر"، https://www.tcpdump.org/papers/bpf-usenix93.pdf
  2. اسٹیون میک کین، "libpcap: ایک آرکیٹیکچر اینڈ آپٹیمائزیشن میتھڈولوجی فار پیکٹ کیپچر" https://sharkfestus.wireshark.org/sharkfest.11/presentations/McCanne-Sharkfest'11_Keynote_Address.pdf
  3. tcpdump, libpcap: https://www.tcpdump.org/
  4. IPtable U32 میچ ٹیوٹوریل.
  5. BPF - بھولا ہوا بائی کوڈ: https://blog.cloudflare.com/bpf-the-forgotten-bytecode/
  6. BPF ٹول کا تعارف: https://blog.cloudflare.com/introducing-the-bpf-tools/
  7. bpf_cls: http://man7.org/linux/man-pages/man8/tc-bpf.8.html
  8. ایک سیکنڈ کا جائزہ: https://lwn.net/Articles/656307/
  9. https://github.com/torvalds/linux/blob/master/Documentation/userspace-api/seccomp_filter.rst
  10. habr: کنٹینرز اور سیکیورٹی: seccomp
  11. ہیبر: سسٹمڈ کے ساتھ ڈیمن کو الگ کرنا یا "آپ کو اس کے لئے ڈوکر کی ضرورت نہیں ہے!"
  12. پال چیگنن، "strace --seccomp-bpf: a look under the hood" https://fosdem.org/2020/schedule/event/debugging_strace_bpf/
  13. netsniff-ng: http://netsniff-ng.org/

ماخذ: www.habr.com

نیا تبصرہ شامل کریں