BPF ننڍڙن لاء، حصو صفر: کلاسک BPF

Berkeley Packet Filters (BPF) هڪ لينڪس ڪنيل ٽيڪنالاجي آهي جيڪا ڪيترن ئي سالن کان انگريزي-ٻولي جي ٽيڪ پبليڪيشن جي اڳين صفحن تي آهي. ڪانفرنسون بي پي ايف جي استعمال ۽ ترقي تي رپورٽن سان ڀريل آهن. ڊيوڊ ملر، لينڪس نيٽ ورڪ سب سسٽم سنڀاليندڙ، لينڪس پلمبرز 2018 تي سندس ڳالهائڻ کي سڏي ٿو "اها ڳالهه XDP بابت ناهي" (XDP BPF لاءِ ھڪڙو استعمال ڪيس آھي). برينڊن گريگ حقدار ڳالهيون ڏئي ٿو لينڪس بي پي ايف سپر پاورز. Toke Høiland-Jørgensen کلڻته ڪرنل هاڻي هڪ microkernel آهي. ٿامس گراف ان خيال کي اڳتي وڌائي ٿو BPF ڪرنل لاءِ جاوا اسڪرپٽ آهي.

بي پي ايف جي اڃا تائين ڪا به منظم وضاحت نه آهي Habré تي، ۽ تنهن ڪري آرٽيڪل جي هڪ سيريز ۾ آئون ٽيڪنالاجي جي تاريخ بابت ڳالهائڻ جي ڪوشش ڪندس، فن تعمير ۽ ترقي جي اوزار کي بيان ڪرڻ، ۽ BPF استعمال ڪرڻ جي ايپليڪيشن ۽ مشق جي علائقن کي بيان ڪرڻ جي ڪوشش ڪندس. هي مضمون، صفر، سيريز ۾، کلاسک BPF جي تاريخ ۽ فن تعمير کي ٻڌائي ٿو، ۽ ان جي آپريٽنگ اصولن جا راز پڻ ظاهر ڪري ٿو. tcpdump, seccomp, strace، ۽ گهڻو ڪجهه.

BPF جي ترقي لينڪس نيٽ ورڪنگ ڪميونٽي پاران ڪنٽرول ڪئي وئي آهي، BPF جي مکيه موجوده ايپليڪيشنون نيٽ ورڪ سان لاڳاپيل آهن ۽ تنهن ڪري، اجازت سان @eucariot، مون سيريز کي سڏيو "ننڍڙن لاءِ بي پي ايف"، وڏي سيريز جي اعزاز ۾ "ننڍڙن لاء نيٽ ورڪ".

BPF جي تاريخ ۾ هڪ مختصر ڪورس (c)

جديد BPF ٽيڪنالاجي ساڳئي نالي سان پراڻي ٽيڪنالاجي جو هڪ بهتر ۽ توسيع ٿيل نسخو آهي، جيڪو هاڻي مونجهاري کان بچڻ لاءِ کلاسڪ BPF سڏجي ٿو. ھڪڙو معروف افاديت ٺاھيو ويو آھي کلاسي بي پي ايف جي بنياد تي tcpdump، ميڪانيزم seccomp، انهي سان گڏ گهٽ ڄاڻايل ماڊلز xt_bpf لاء iptables ۽ درجه بندي ڪندڙ cls_bpf. جديد لينڪس ۾، کلاسک BPF پروگرام خودڪار طريقي سان نئين شڪل ۾ ترجمو ڪيا ويا آهن، جڏهن ته، صارف جي نقطي نظر کان، API برقرار رهي ٿي ۽ کلاسک BPF لاء نوان استعمال، جيئن اسان هن مضمون ۾ ڏسندا سين، اڃا تائين مليا آهن. انهي سبب لاء، ۽ اهو پڻ ڇاڪاڻ ته لينڪس ۾ ڪلاسيڪل BPF جي ترقي جي تاريخ جي پٺيان، اهو واضح ٿي ويندو ته اهو ڪيئن ۽ ڇو ان جي جديد شڪل ۾ ترقي ڪئي، مون فيصلو ڪيو ته هڪ مضمون سان شروع ڪرڻ جو ڪلاسيڪل BPF بابت.

گذريل صديءَ جي اٺين ڏهاڪي جي آخر ۾، مشهور لارنس برڪلي ليبارٽري جا انجنيئر ان سوال ۾ دلچسپي وٺڻ لڳا ته ڪيئن صحيح طريقي سان نيٽ ورڪ پيڪيٽ کي هارڊويئر تي فلٽر ڪجي، جيڪو گذريل صديءَ جي اٺين ڏهاڪي ۾ جديد هو. فلٽرنگ جو بنيادي خيال، اصل ۾ CSPF (CMU/Stanford Packet Filter) ٽيڪنالاجي ۾ لاڳو ڪيو ويو، غير ضروري پيڪيٽ کي جلد کان جلد فلٽر ڪرڻ هو، يعني. ڪرنل اسپيس ۾، ڇاڪاڻ ته هي غير ضروري ڊيٽا کي يوزر اسپيس ۾ نقل ڪرڻ کان پاسو ڪري ٿو. ڪنييل اسپيس ۾ يوزر ڪوڊ کي هلائڻ لاءِ رن ٽائم سيڪيورٽي مهيا ڪرڻ لاءِ، سينڊ باڪس ٿيل ورچوئل مشين استعمال ڪئي وئي.

بهرحال، موجوده فلٽرن لاءِ ورچوئل مشينون اسٽيڪ تي ٻڌل مشينن تي هلائڻ لاءِ ڊزائين ڪيون ويون هيون ۽ جديد RISC مشينن تي موثر طريقي سان نه هلنديون هيون. نتيجي طور، برڪلي ليبز جي انجنيئرن جي ڪوششن جي ذريعي، هڪ نئين بي پي ايف (برڪلي پيڪٽ فلٽر) ٽيڪنالاجي تيار ڪئي وئي، جنهن جي ورچوئل مشين آرڪيٽيڪچر موٽرولا 6502 پروسيسر جي بنياد تي ٺهيل هئي - اهڙين معروف شين جو ڪم هارس. ايپل II يا NES. نئين ورچوئل مشين موجوده حلن جي مقابلي ۾ فلٽر ڪارڪردگي کي ڏهه ڀيرا وڌايو.

BPF مشين فن تعمير

اسان فن تعمير سان ڪم ڪندڙ طريقي سان واقف ٿينداسين، مثالن جو تجزيو ڪندي. بهرحال، شروع ڪرڻ لاءِ، اچو ته چئو ته مشين وٽ ٻه 32-bit رجسٽر هئا جيڪي صارف تائين پهچن ٿا، هڪ جمع ڪندڙ A ۽ انڊيڪس رجسٽر X64 بائيٽ ميموري (16 لفظن)، لکڻ ۽ بعد ۾ پڙهڻ لاءِ دستياب آهي، ۽ انهن شين سان ڪم ڪرڻ لاءِ حڪمن جو هڪ ننڍڙو نظام. پروگرامن ۾ مشروط اظهار کي لاڳو ڪرڻ لاءِ جمپ جون هدايتون پڻ موجود هيون، پر پروگرام جي بروقت مڪمل ٿيڻ جي ضمانت ڏيڻ لاءِ، جمپ صرف اڳتي وڌائي سگهجي ٿو، يعني خاص طور تي، لوپ ٺاهڻ کان منع ڪئي وئي هئي.

مشين کي شروع ڪرڻ لاء عام اسڪيم هيٺ ڏنل آهي. استعمال ڪندڙ BPF فن تعمير لاء هڪ پروگرام ٺاهي ٿو ۽ استعمال ڪندي ڪجهه kernel ميڪانيزم (جهڙوڪ سسٽم ڪال)، لوڊ ڪري ٿو ۽ پروگرام کي ڳنڍي ٿو ڪجهه ڏانهن ڪرنل ۾ واقع جنريٽر ڏانھن (مثال طور، ھڪڙو واقعو نيٽ ورڪ ڪارڊ تي ايندڙ پيڪٽ جي آمد آھي). جڏهن ڪو واقعو ٿئي ٿو، ڪرنل پروگرام کي هلائي ٿو (مثال طور، هڪ مترجم ۾)، ۽ مشين جي ياداشت سان لاڳاپيل آهي ڪجهه ڏانهن kernel ياداشت واري علائقي (مثال طور، ايندڙ پيڪٽ جي ڊيٽا).

مٿين مثالن کي ڏسڻ شروع ڪرڻ لاءِ اسان لاءِ ڪافي هوندو: اسان ضروري طور تي سسٽم ۽ ڪمانڊ فارميٽ سان واقف ٿينداسين. جيڪڏهن توهان هڪ مجازي مشين جي ڪمانڊ سسٽم کي فوري طور تي مطالعو ڪرڻ چاهيو ٿا ۽ ان جي سڀني صلاحيتن جي باري ۾ ڄاڻڻ چاهيو ٿا، ته توهان پڙهي سگهو ٿا اصل مضمون بي ايس ڊي پيڪٽ فلٽر ۽/يا فائل جو پهريون اڌ Documentation/networking/filter.txt kernel دستاويزن مان. اضافي طور تي، توهان پيشڪش جو مطالعو ڪري سگهو ٿا libpcap: پيڪٽ ڪيپچر لاءِ هڪ آرڪيٽيڪچر ۽ اصلاح جو طريقو، جنهن ۾ ميڪن، BPF جي ليکڪن مان هڪ، تخليق جي تاريخ بابت ڳالهائيندو آهي libpcap.

اسان هاڻي لينڪس تي کلاسک BPF استعمال ڪرڻ جي سڀني اهم مثالن تي غور ڪرڻ لاء اڳتي وڌو ٿا: tcpdump (libpcap) seccomp xt_bpf, cls_bpf.

ٽي سي پيمپ

بي پي ايف جي ترقي پيڪٽ فلٽرنگ لاءِ فرنٽ اينڊ جي ترقي سان متوازي ۾ ڪئي وئي - هڪ مشهور استعمال tcpdump. ۽، ڇاڪاڻ ته هي کلاسک BPF استعمال ڪرڻ جو سڀ کان پراڻو ۽ مشهور مثال آهي، ڪيترن ئي آپريٽنگ سسٽم تي دستياب آهي، اسان ان سان ٽيڪنالاجي جو مطالعو شروع ڪنداسين.

(مون لينڪس تي هن مضمون ۾ سڀئي مثال ورتا 5.6.0-rc6. ڪجهه حڪمن جي پيداوار کي بهتر پڙهڻ جي قابليت لاء تبديل ڪيو ويو آهي.)

مثال: IPv6 پيڪٽس جو مشاهدو

اچو ته تصور ڪريون ته اسان سڀني IPv6 پيڪٽس کي ڏسڻ چاهيون ٿا انٽرفيس تي eth0. ائين ڪرڻ لاءِ اسان پروگرام هلائي سگھون ٿا tcpdump هڪ سادي فلٽر سان ip6:

$ sudo tcpdump -i eth0 ip6

اهڙيء طرح tcpdump فلٽر کي گڏ ڪري ٿو ip6 BPF آرڪيٽيڪچر بائيٽ ڪوڊ ۾ ۽ ان کي ڪرنل ڏانهن موڪليو (تفصيل ڏسو سيڪشن ۾ Tcpdump: لوڊ ڪندي). لوڊ ٿيل فلٽر انٽرفيس مان گذرڻ واري هر پيڪٽ لاءِ هلايو ويندو eth0. جيڪڏهن فلٽر هڪ غير صفر قدر واپس ڪري ٿو n، پوءِ تائين n پيڪٽ جي بائيٽ کي يوزر اسپيس تي نقل ڪيو ويندو ۽ اسان ان کي آئوٽ پٽ ۾ ڏسنداسين tcpdump.

BPF ننڍڙن لاء، حصو صفر: کلاسک BPF

اهو ظاهر ٿئي ٿو ته اسان آساني سان ڳولي سگهون ٿا ته بائيٽ ڪوڊ ڪيري ڏانهن موڪليو ويو 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)th بائيٽ جو تجزيو ڪيل نيٽ ورڪ پيڪٽ. اسان Ethernet انٽرفيس مان پيڪيٽ پڙهون ٿا eth0، ۽ هي مطلبته پيڪٽ هن طرح نظر اچي ٿو (سادگي لاءِ، اسان سمجهون ٿا ته پيڪٽ ۾ VLAN ٽيگ نه آهن):

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

تنهنڪري حڪم تي عمل ڪرڻ کان پوء ldh [12] رجسٽر ۾ A اتي هڪ ميدان هوندو Ether Type - هن ايٿرنيٽ فريم ۾ منتقل ٿيل پيڪيٽ جو قسم. لائن 1 تي اسان رجسٽر جي مواد جو مقابلو ڪريون ٿا A (پيڪيج جو قسم) ج 0x86dd، ۽ هي ۽ اتي آهي قسم جنهن ۾ اسان کي دلچسپي آهي IPv6. لائن 1 تي، مقابلي واري حڪم کان علاوه، ٻه وڌيڪ ڪالمن آهن - jt 2 и jf 3 - نشان جن ڏانھن توھان کي وڃڻو آھي جيڪڏھن مقابلو ڪامياب آھي (A == 0x86dd) ۽ ناڪام. تنهن ڪري، هڪ ڪامياب ڪيس (IPv6) ۾ اسان لڪير 2 تي وڃون ٿا، ۽ هڪ ناڪامي صورت ۾ - لائن 3 ڏانهن. لائن 3 تي پروگرام ڪوڊ 0 سان ختم ٿئي ٿو (پيڪٽ کي نقل نه ڪريو)، لائن 2 تي پروگرام ڪوڊ سان ختم ٿئي ٿو. 262144 (مون کي نقل ڪريو وڌ ۾ وڌ 256 kilobytes پيڪيج).

هڪ وڌيڪ پيچيده مثال: اسان ڏسون ٿا TCP پيڪٽس ذريعي منزل مقصود

اچو ته ڏسون ته فلٽر ڪهڙو نظر اچي ٿو جيڪو سڀني 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 پيڪٽ آهي (Ether Type = 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، i.e. اسان به نٿا چئي سگهون ldb 4*([14]&0xf) نه ldxb 5*([14]&0xf) (اسان صرف هڪ مختلف آفسيٽ بيان ڪري سگهون ٿا، مثال طور، ldxb 4*([16]&0xf)). اهو واضح آهي ته هن ايڊريسنگ اسڪيم کي BPF ۾ شامل ڪيو ويو خاص طور تي حاصل ڪرڻ لاء X (انڊيڪس رجسٽر) IPv4 هيڊر ڊگھائي.

تنهن ڪري لائن 7 تي اسان اڌ لفظ لوڊ ڪرڻ جي ڪوشش ڪندا آهيون (X+16). ياد رهي ته 14 بائيٽ Ethernet هيڊر تي قبضو ڪيو ويو آهي، ۽ X IPv4 هيڊر جي ڊيگهه تي مشتمل آهي، اسان سمجهون ٿا ته ان ۾ A TCP منزل پورٽ لوڊ ٿيل آهي:

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

آخرڪار، لائن 8 تي اسان مطلوبہ قيمت سان منزل جي بندرگاهن جو مقابلو ڪريون ٿا ۽ لائين 9 يا 10 تي اسان نتيجو واپس ڏيون ٿا - ڇا پيڪٽ کي نقل ڪرڻ يا نه.

Tcpdump: لوڊ ڪندي

پوئين مثالن ۾، اسان خاص طور تي تفصيل سان نه ٻڌندا هئاسين ته ڪيئن اسان BPF بائيٽ ڪوڊ کي پيڪٽ فلٽرنگ لاءِ ڪرنل ۾ لوڊ ڪريون ٿا. عام طور تي ڳالهائڻ، 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
...

پيداوار جي پهرين ٻن لائينن تي اسان ٺاهيندا آهيون خام ساکٽ سڀ Ethernet فريم پڙهڻ ۽ ان کي انٽرفيس سان پابند ڪرڻ لاء eth0. کان اسان جو پهريون مثال اسان ڄاڻون ٿا ته فلٽر ip چار BPF هدايتون تي مشتمل هوندي، ۽ ٽئين لائن تي اسين ڏسون ٿا ته اختيار ڪيئن استعمال ڪجي SO_ATTACH_FILTER سسٽم ڪال setsockopt اسان ڊگھائي 4 جو فلٽر لوڊ ۽ ڳنڍيندا آهيون. هي اسان جو فلٽر آهي.

اهو سمجهڻ جي قابل آهي ته کلاسک BPF ۾، فلٽر لوڊ ڪرڻ ۽ ڳنڍڻ هميشه هڪ ايٽمي آپريشن جي طور تي ٿيندي آهي، ۽ بي پي ايف جي نئين نسخي ۾، پروگرام کي لوڊ ڪندي ۽ ان کي ايونٽ جنريٽر تي پابند ڪرڻ وقت ۾ الڳ ٿي ويا آهن.

لڪيل سچ

ٻاھر نڪرڻ جو ھڪڙو وڌيڪ مڪمل نسخو ھن طرح نظر اچي ٿو:

$ 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 (سڀني پيڪيٽس کي ڇڏي ڏيو)، ساکٽ کي غير بلاڪ ڪرڻ واري موڊ ۾ تبديل ڪري ٿو ۽ سڀني پيڪٽس کي ختم ڪرڻ جي ڪوشش ڪري ٿو جيڪي اڳئين فلٽرن کان رهي سگھن ٿيون.

مجموعي طور تي، لينڪس تي پيڪيجز کي فلٽر ڪرڻ لاء کلاسک BPF استعمال ڪندي، توهان کي هڪ فلٽر هجڻ جي ضرورت آهي ساخت جي صورت ۾ جهڙوڪ struct sock_fprog ۽ هڪ کليل ساکٽ، جنهن کان پوء فلٽر سسٽم ڪال استعمال ڪندي ساکٽ سان ڳنڍيل هجي setsockopt.

دلچسپ ڳالهه اها آهي ته فلٽر ڪنهن به ساکٽ سان ڳنڍيل هجي، نه رڳو خام. هتي مثال هڪ پروگرام جيڪو سڀني ايندڙ UDP ڊيٽاگرامن مان پهرين ٻن بائيٽس کان سواءِ سڀني کي ڪٽي ٿو. (مون ڪوڊ ۾ تبصرا شامل ڪيا ته جيئن آرٽيڪل کي خراب نه ٿئي.)

استعمال بابت وڌيڪ تفصيل setsockopt فلٽر ڳنڍڻ لاءِ، ڏسو ساکٽ (7)، پر لکڻ جي باري ۾ توهان جي پنهنجي فلٽر وانگر struct sock_fprog بغير مدد جي tcpdump اسان سيڪشن ۾ ڳالهائينداسين پروگرامنگ BPF اسان جي پنهنجي هٿن سان.

کلاسک BPF ۽ XNUMX صدي

BPF 1997 ۾ لينڪس ۾ شامل ڪيو ويو ۽ هڪ ڊگهي وقت تائين ڪم هارس رهيو libpcap بغير ڪنهن خاص تبديلين جي (لينڪس مخصوص تبديليون، يقينا، اهو هو، پر انهن عالمي تصوير کي تبديل نه ڪيو). پهرين سنگين نشانيون جيڪي BPF ترقي ڪري سگهندا 2011 ۾ آيا، جڏهن ايريڪ ڊومازٽ تجويز ڪيو پيچ، جيڪو شامل ڪري ٿو Just In Time Compiler کي ڪنيل ۾ - BPF bytecode کي اصلي ۾ تبديل ڪرڻ لاءِ ترجمو ڪندڙ x86_64 ڪوڊ.

JIT مرتب ڪندڙ تبديلين جي زنجير ۾ پهريون هو: 2012 ۾ ظاهر ٿيو فلٽر لکڻ جي صلاحيت سيڪمپ, BPF استعمال ڪندي، جنوري 2013 ۾ هو شامل ڪيو ويو ماڊل xt_bpf، جيڪو توهان کي قاعدن لکڻ جي اجازت ڏئي ٿو iptables BPF جي مدد سان، ۽ آڪٽوبر 2013 ۾ هو شامل ڪيو ويو پڻ هڪ ماڊل cls_bpf, جيڪو توهان کي BPF استعمال ڪندي ٽرئفڪ جي درجه بندي لکڻ جي اجازت ڏئي ٿو.

اسان جلد ئي انهن سڀني مثالن کي وڌيڪ تفصيل سان ڏسنداسين، پر پهريان اهو اسان لاءِ ڪارآمد ثابت ٿيندو ته ڪيئن لکجي ۽ ترتيب ڏيو BPF لاءِ صوابديدي پروگرام، ڇو ته لائبريري پاران مهيا ڪيل صلاحيتون libpcap محدود (سادو مثال: فلٽر ٺاهيل libpcap صرف ٻه قدر واپس ڪري سگھن ٿا - 0 يا 0x40000) يا عام طور تي، جيئن seccomp جي صورت ۾، لاڳو نه آھن.

پروگرامنگ BPF اسان جي پنهنجي هٿن سان

اچو ته BPF هدايتن جي بائنري فارميٽ سان واقف ٿي وڃو، اهو تمام سادو آهي:

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

هر هدايت 64 بٽ تي قبضو ڪري ٿي، جنهن ۾ پهرين 16 بٽ هدايتون ڪوڊ آهن، پوء ٻه اٺ-بٽ انڊينٽ آهن، jt и jf، ۽ دليل لاءِ 32 بٽ K، جنهن جو مقصد مختلف آهي حڪم کان حڪم تائين. مثال طور، حڪم ret، جيڪو پروگرام کي ختم ڪري ٿو ڪوڊ آهي 6، ۽ واپسي جي قيمت مسلسل مان ورتو وڃي ٿو K. سي ۾، ھڪڙي بي پي ايف جي ھدايت ھڪڙي ساخت جي طور تي نمائندگي ڪئي وئي آھي

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 kernels توهان ڳولي سگهو ٿا هڪ جمع ڪندڙ ۽ ڊيبگر کلاسک 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 واڌايون

معياري BPF کان علاوه، لينڪس ۽ tools/bpf/bpf_asm حمايت ۽ غير معياري سيٽ. بنيادي طور تي، هدايتون استعمال ڪيون وينديون آھن ھڪڙي ساخت جي شعبن تائين رسائي لاءِ struct sk_buff، جيڪو بيان ڪري ٿو نيٽ ورڪ پيڪٽ ڪني ۾. جڏهن ته، مددگار هدايتون جا ٻيا قسم پڻ آهن، مثال طور ldw cpu رجسٽر ۾ لوڊ ٿيندو A kernel فنڪشن کي هلائڻ جو نتيجو raw_smp_processor_id(). (BPF جي نئين ورزن ۾، انهن غير معياري واڌارن کي وڌايو ويو آهي پروگرامن کي مهيا ڪرڻ لاءِ ڪنيل مددگارن جي هڪ سيٽ سان ميموري، ساخت ۽ پيدا ٿيندڙ واقعن تائين رسائي.) هتي هڪ فلٽر جو هڪ دلچسپ مثال آهي جنهن ۾ اسان صرف ڪاپي ڪريون ٿا. پيڪٽ هيڊرز کي استعمال ڪندي استعمال ڪندڙ اسپيس ۾ poff, payload offset:

ld poff
ret a

BPF توسيع ۾ استعمال نه ٿي ڪري سگھجي tcpdump, پر اهو هڪ سٺو سبب آهي ته يوٽيلٽي پيڪيج سان واقف ٿيڻ لاء netsniff-ng، جنهن ۾، ٻين شين مان، هڪ ترقي يافته پروگرام شامل آهي netsniff-ng، جيڪو، BPF استعمال ڪندي فلٽر ڪرڻ کان علاوه، هڪ موثر ٽرئفڪ جنريٽر تي مشتمل آهي، ۽ ان کان وڌيڪ ترقي يافته tools/bpf/bpf_asm، هڪ BPF گڏ ڪرڻ وارو سڏيو bpfc. پيڪيج ۾ ڪافي تفصيلي دستاويز شامل آهن، مضمون جي آخر ۾ لنڪ پڻ ڏسو.

سيڪمپ

تنهن ڪري، اسان اڳ ۾ ئي ڄاڻون ٿا ته بي پي ايف پروگرامن کي ترتيب ڏيڻ واري پيچيدگين کي ڪيئن لکڻو آهي ۽ نوان مثالن کي ڏسڻ لاء تيار آهيون، جن مان پهريون آهي سيڪنڊ ڪمپ ٽيڪنالاجي، جيڪا اجازت ڏئي ٿي، BPF فلٽر استعمال ڪندي، سيٽ ۽ سيٽ سسٽم ڪال دليلن کي منظم ڪرڻ لاء دستياب آهي. هڪ ڏنل عمل ۽ ان جو اولاد.

seccomp جو پهريون نسخو 2005 ۾ ڪرنل ۾ شامل ڪيو ويو ۽ تمام گهڻو مشهور نه هو، ڇاڪاڻ ته اهو صرف هڪ واحد اختيار مهيا ڪيو ويو آهي - سسٽم ڪالن جي سيٽ کي محدود ڪرڻ لاء هڪ پروسيس تائين دستياب آهي: read, write, exit и sigreturn، ۽ اهو عمل جيڪو ضابطن جي ڀڃڪڙي ڪندو استعمال ڪيو ويو SIGKILL. بهرحال، 2012 ۾، seccomp BPF فلٽر استعمال ڪرڻ جي صلاحيت شامل ڪئي، توهان کي اجازت ڏني وئي ته سسٽم ڪالن جي هڪ سيٽ کي بيان ڪرڻ ۽ انهن جي دليلن تي چيڪ پڻ انجام ڏيو. (دلچسپ ڳالهه اها آهي ته، ڪروم هن فنڪشنلٽي جي پهرين استعمال ڪندڙن مان هڪ هو، ۽ ڪروم ماڻهو هن وقت هڪ KRSI ميڪانيزم ٺاهي رهيا آهن BPF جي نئين ورزن جي بنياد تي ۽ لينڪس سيڪيورٽي ماڊلز کي ڪسٽمائيزيشن جي اجازت ڏئي رهيا آهن.) اضافي دستاويزن جا لنڪ آخر ۾ ڳولي سگهجن ٿا. آرٽيڪل جو.

نوٽ ڪريو ته هب تي اڳ ۾ ئي مضمون موجود آهن seccomp استعمال ڪرڻ بابت، ٿي سگهي ٿو ته ڪو ماڻهو انهن کي پڙهڻ کان اڳ (يا بدران) هيٺين ذيلي حصن کي پڙهڻ چاهيندو. مضمون ۾ ڪنٽينر ۽ سيڪيورٽي: seccomp seccomp استعمال ڪرڻ جا مثال مهيا ڪري ٿو، ٻئي 2007 ورجن ۽ ورجن BPF استعمال ڪندي (فلٽر libseccomp استعمال ڪندي ٺاهيا ويا آهن)، Docker سان seccomp جي ڪنيڪشن بابت ڳالهائي ٿو، ۽ پڻ مهيا ڪري ٿو ڪيترائي مفيد لنڪ. مضمون ۾ سسٽمڊ سان ڊيمن کي الڳ ڪرڻ يا ”توهان کي هن لاءِ ڊڪر جي ضرورت ناهي! اهو پکڙيل آهي، خاص طور تي، ڪيئن شامل ڪجي بليڪ لسٽن يا وائيٽ لسٽن کي سسٽم ڪالن لاءِ ڊمنن لاءِ هلندڙ سسٽم ڊي.

اڳتي اسان ڏسنداسين ته فلٽر ڪيئن لکجي ۽ لوڊ ڪجي seccomp بيئر سي ۾ ۽ لائبريري استعمال ڪندي libseccomp ۽ هر آپشن جا فائدا ۽ نقصان ڇا آهن، ۽ آخر ۾، اچو ته ڏسون ته سيڪمپ پروگرام ڪيئن استعمال ٿئي ٿو. strace.

seccomp لاءِ فلٽر لکڻ ۽ لوڊ ڪرڻ

اسان اڳ ۾ ئي ڄاڻون ٿا ته BPF پروگرام ڪيئن لکندا آهن، تنهنڪري اچو ته پهرين سيڪڪمپ پروگرامنگ انٽرفيس کي ڏسو. توھان عمل جي سطح تي فلٽر سيٽ ڪري سگھو ٿا، ۽ سڀني ٻارن جي عملن کي پابنديون ورثي ۾ ملندي. اهو هڪ سسٽم ڪال استعمال ڪندي ڪيو ويندو آهي seccomp(2):

seccomp(SECCOMP_SET_MODE_FILTER, flags, &filter)

جتي &filter - ھي ھڪڙو اشارو آھي ھڪڙي جوڙجڪ ڏانھن جيڪو اسان کي اڳ ۾ ئي واقف آھي struct sock_fprog، i.e. BPF پروگرام.

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-bit alignment ذريعي رسائي سگهجي ٿو ۽ توهان اڌ لفظ يا بائيٽ لوڊ نٿا ڪري سگهو - جڏهن فلٽر لوڊ ڪرڻ جي ڪوشش ڪري رهيا آهيو. ldh [0] سسٽم ڪال seccomp واپس ڪندو EINVAL. فنڪشن لوڊ ٿيل فلٽر کي چيڪ ڪري ٿو seccomp_check_filter() دانا. (مزاحي ڳالهه اها آهي ته، اصل ڪمٽ ۾ جيڪا سيڪنڊ ڪمپ ڪارڪردگي شامل ڪئي وئي، اهي هن فنڪشن ۾ هدايتون استعمال ڪرڻ جي اجازت شامل ڪرڻ وساري ويٺا. mod (ڊيويزن باقي) ۽ هاڻي ان جي اضافي کان وٺي، سيڪڪمپ بي پي ايف پروگرامن لاءِ دستياب ناهي ٽوڙي ڇڏيندو 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، ۽ ايئن.

BPF ننڍڙن لاء، حصو صفر: کلاسک BPF

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 کان نون پيغامن جو انتظار ڪندو.

BPF ننڍڙن لاء، حصو صفر: کلاسک BPF

جڏهن اختيار استعمال ڪندي --seccomp-bpf اتي ٻه پابنديون آهن. پهرين، اهو ممڪن نه ٿيندو ته اڳ ۾ ئي موجود عمل ۾ شامل ٿيڻ (اختيار -p پروگرام strace)، ڇاڪاڻ ته هي seccomp پاران سهڪار نه آهي. ٻيو، ڪو به امڪان نه آهي نه ٻار جي عملن کي ڏسو، ڇو ته seccomp فلٽر سڀني ٻارن جي عملن کان ورثي ۾ مليا آهن بغير ان کي غير فعال ڪرڻ جي.

ٿورڙو وڌيڪ تفصيل تي ڪيئن صحيح strace سان ڪم ڪندو آهي seccomp مان ڳولي سگھجي ٿو تازو رپورٽ. اسان لاء، سڀ کان وڌيڪ دلچسپ حقيقت اها آهي ته کلاسک بي پي ايف جي نمائندگي ڪندڙ سيڪڪمپ اڃا تائين استعمال ڪيو ويندو آهي.

xt_bpf

اچو ته هاڻي نيٽ ورڪ جي دنيا ڏانهن واپس وڃو.

پس منظر: هڪ ڊگهو وقت اڳ، 2007 ۾، بنيادي هئي شامل ڪيو ويو ماڊل xt_u32 netfilter لاء. اهو هڪ وڌيڪ قديم ٽرئفڪ جي درجه بندي سان قياس سان لکيو ويو آهي cls_u32 ۽ توھان کي ھيٺ ڏنل سادي عملن کي استعمال ڪندي iptables لاءِ صوابديدي بائنري قاعدا لکڻ جي اجازت ڏني آھي: ھڪڙي پيڪيج مان 32 بٽ لوڊ ڪريو ۽ انھن تي رياضياتي عملن جو ھڪڙو سيٽ انجام ڏيو. مثال طور،

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

IP هيڊر جي 32 بٽس کي لوڊ ڪري ٿو، پيڊنگ 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 توھان استعمال ڪري سگھوٿا BPF جي بنياد تي ماڊل xt_bpf. جيڪو به پڙهي چڪو آهي اهو اڳ ۾ ئي ان جي آپريشن جي اصول بابت واضح هجڻ گهرجي: iptables ضابطن جي طور تي BPF bytecode کي هلائڻ. توھان ھڪڙو نئون قاعدو ٺاھي سگھو ٿا، مثال طور، ھن وانگر:

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 پيڪٽس. هڪ ماڊل ۾ BPF پروگرام لاءِ حوالو xt_bpf، يقينا، پيڪٽ ڊيٽا ڏانهن اشارو ڪري ٿو، iptables جي صورت ۾، IPv4 هيڊر جي شروعات ڏانهن. BPF پروگرام مان واپسي جي قيمت بولينڪٿي false مطلب ته پيڪٽ نه ملي.

اهو واضح آهي ته ماڊل xt_bpf مٿي ڏنل مثال کان وڌيڪ پيچيده فلٽرن کي سپورٽ ڪري ٿو. اچو ته ڏسو حقيقي مثالن مان Cloudfare. تازو تائين اهي ماڊل استعمال ڪندا هئا xt_bpf DDoS حملن کان بچائڻ لاء. مضمون ۾ BPF اوزار متعارف ڪرايو اهي وضاحت ڪندا آهن ته ڪيئن (۽ ڇو) اهي 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 جي مقابلي ۾، هن معاملي ۾ لاڳو ٿيڻ جو دائرو بنيادي طور تي تنگ ڪيو ويو آهي: ڪلاسيڪل پروگرام پيڪيجز جي مواد کي تبديل نٿا ڪري سگهن ۽ ڪالن جي وچ ۾ رياست کي بچائي نٿا سگهن.

تنهن ڪري اهو وقت آهي الوداع چوڻ جو کلاسي BPF ۽ مستقبل ڏانهن ڏسو.

کلاسک BPF کي الوداع

اسان ڏٺو ته ڪيئن بي پي ايف ٽيڪنالاجي، شروعاتي نون جي ڏهاڪي ۾ ترقي ڪئي، ڪاميابيء سان هڪ صدي جي چوٿين تائين رهي ۽ آخر تائين نوان ايپليڪيشنون مليون. بهرحال، اسٽيڪ مشينن کان RISC ڏانهن منتقلي وانگر، جيڪو کلاسک BPF جي ترقي لاء هڪ محرک طور ڪم ڪيو، 32s ۾ 64-bit کان XNUMX-bit مشينن جي منتقلي هئي ۽ کلاسک BPF غير معمولي ٿيڻ شروع ڪيو. ان کان علاوه، کلاسک BPF جون صلاحيتون تمام محدود آهن، ۽ ان کان علاوه پراڻي فن تعمير - اسان وٽ BPF پروگرامن جي ڪالن جي وچ ۾ رياست کي بچائڻ جي صلاحيت نه آهي، سڌو صارف جي رابطي جو ڪو امڪان ناهي، رابطي جو ڪو به امڪان ناهي. kernel سان، سواءِ ڍانچي جي محدود تعداد کي پڙهڻ لاءِ sk_buff ۽ آسان ترين مددگار افعال کي شروع ڪندي، توھان پيڪٽ جي مواد کي تبديل نه ڪري سگھوٿا ۽ انھن کي ريڊائريڪٽ ڪري سگھو ٿا.

حقيقت ۾، في الحال لينڪس ۾ کلاسک BPF جو باقي سڀ ڪجهه API انٽرفيس آهي، ۽ ڪرنل جي اندر سڀئي کلاسک پروگرام، ساڪٽ فلٽر هجن يا سيڪمپ فلٽر، خودڪار طريقي سان هڪ نئين فارميٽ ۾ ترجمو ٿيل آهن، وڌايل BPF. (اسان انهي بابت ڳالهائينداسين ته اهو ڪيئن ٿئي ٿو ايندڙ مضمون ۾.)

نئين فن تعمير جي منتقلي 2013 ۾ شروع ٿي، جڏهن Alexey Starovoitov هڪ BPF اپڊيٽ اسڪيم پيش ڪيو. 2014 ۾ لاڳاپيل پيچ ظاهر ٿيڻ لڳو بنيادي ۾. جيستائين مان سمجهان ٿو، اصل منصوبو صرف 64-bit مشينن تي وڌيڪ موثر طريقي سان هلائڻ لاءِ آرڪيٽيڪچر ۽ JIT مرتب ڪندڙ کي بهتر ڪرڻ هو، پر ان جي بدران اهي اصلاحون لينڪس ڊولپمينٽ ۾ هڪ نئين باب جي شروعات کي نشانو بڻايو.

هن سلسلي ۾ وڌيڪ آرٽيڪل نئين ٽيڪنالاجي جي فن تعمير ۽ ايپليڪيشنن کي ڍڪيندا، شروعاتي طور تي اندروني 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. پال چاگنون، "اسٽريس -seccomp-bpf: هڪ نظر هيٺ هود"، https://fosdem.org/2020/schedule/event/debugging_strace_bpf/
  13. netsniff-ng: http://netsniff-ng.org/

جو ذريعو: www.habr.com

تبصرو شامل ڪريو