ကလေးများအတွက် BPF၊ အပိုင်း သုည- classic BPF

Berkeley Packet Filters (BPF) သည် အင်္ဂလိပ်ဘာသာနည်းပညာဆိုင်ရာ ပုံနှိပ်ထုတ်ဝေမှုများ၏ ရှေ့ဆုံးစာမျက်နှာတွင် ယခု နှစ်အတော်ကြာရှိနေပြီဖြစ်သော Linux kernel နည်းပညာတစ်ခုဖြစ်သည်။ ညီလာခံများတွင် BPF အသုံးပြုမှုနှင့် ဖွံ့ဖြိုးတိုးတက်မှုဆိုင်ရာ အစီရင်ခံစာများနှင့် ပြည့်နှက်နေသည်။ Linux ကွန်ရက်စနစ်ခွဲထိန်းသိမ်းသူ David Miller သည် သူ၏ဟောပြောချက်ကို Linux Plumbers 2018 တွင်ခေါ်ဆိုခဲ့သည်။ "ဒီစကားက XDP အကြောင်းမဟုတ်ဘူး" (XDP သည် BPF အတွက် အသုံးပြုနိုင်သော ကိစ္စတစ်ခုဖြစ်သည်။ Brendan Gregg သည် ဆွေးနွေးပွဲများကို ခံစားခွင့် ပေးသည်။ Linux BPF စူပါပါဝါများ. Toke Høiland-Jørgensen ရယ်သည်။kernel သည် ယခု microkernel ဖြစ်သည် ။ Thomas Graf သည် ထိုအယူအဆကို အားပေးသည်။ BPF သည် kernel အတွက် javascript ဖြစ်သည်။.

Habre တွင် BPF ၏စနစ်တကျဖော်ပြချက်မရသေးပါ၊ ထို့ကြောင့် ဆောင်းပါးအတွဲလိုက်တွင် နည်းပညာသမိုင်းအကြောင်းပြောရန်၊ ဗိသုကာပညာနှင့် ဖွံ့ဖြိုးတိုးတက်ရေးကိရိယာများကိုဖော်ပြရန်နှင့် BPF အသုံးပြုခြင်းဆိုင်ရာ နယ်ပယ်များနှင့် လက်တွေ့အသုံးချမှုနယ်ပယ်များကို အကြမ်းဖျဉ်းဖော်ပြပါမည်။ စီးရီးတွင် သုည၊ ဤဆောင်းပါးသည် ဂန္ထဝင် BPF ၏ သမိုင်းနှင့် ဗိသုကာလက်ရာကို ပြောပြထားပြီး ၎င်း၏ လည်ပတ်မှုဆိုင်ရာ အခြေခံမူများ၏ လျှို့ဝှက်ချက်များကိုလည်း ဖော်ပြသည်။ tcpdump, seccomp, strace, ပြီးတော့နောက်ထပ်အများကြီးပဲ။

BPF ၏ဖွံ့ဖြိုးတိုးတက်မှုကို Linux ကွန်ရက်အသိုင်းအဝိုင်းမှထိန်းချုပ်ထားသည်၊ BPF ၏အဓိကတည်ရှိနေသောအပလီကေးရှင်းများသည်ကွန်ယက်များနှင့်သက်ဆိုင်သောကြောင့်ခွင့်ပြုချက်ဖြင့်၊ @eucariotစီးရီးကြီးကို "ကလေးငယ်များအတွက် BPF" စီးရီးကို ဂုဏ်ပြုတဲ့အနေနဲ့ ကျွန်တော်ခေါ်ပါတယ်။ "ကလေးငယ်များအတွက်ကွန်ရက်များ".

BPF ၏သမိုင်းတွင် ရက်တိုသင်တန်း (c)

ခေတ်မီ BPF နည်းပညာသည် ရှုပ်ထွေးမှုများကို ရှောင်ရှားရန် ယခု ဂန္ထဝင် BPF ဟုခေါ်သော နာမည်တူ နည်းပညာဟောင်း၏ မြှင့်တင်ပြီး တိုးချဲ့ထားသော ဗားရှင်းတစ်ခုဖြစ်သည်။ ဂန္ထဝင် BPF ကို အခြေခံ၍ လူသိများသော အသုံးဝင်မှုတစ်ခုကို ဖန်တီးခဲ့သည်။ tcpdumpယန္တရား seccompအပြင် လူသိနည်းသော module များ xt_bpf အတွက် iptables အမျိုးအစားခွဲသည်။ cls_bpf. ခေတ်သစ် Linux တွင်၊ ဂန္ထဝင် BPF ပရိုဂရမ်များကို ပုံစံအသစ်သို့ အလိုအလျောက် ဘာသာပြန်ဆိုသော်လည်း အသုံးပြုသူအမြင်အရ၊ API သည် နေရာ၌ရှိနေဆဲဖြစ်ပြီး၊ ဤဆောင်းပါးတွင် တွေ့ရမည်ဖြစ်သကဲ့သို့ ဂန္တဝင် BPF အတွက် အသုံးပြုမှုအသစ်များကိုလည်း တွေ့ရှိနေဆဲဖြစ်သည်။ ဤအကြောင်းကြောင့်၊ Linux တွင် classical BPF ၏ဖွံ့ဖြိုးတိုးတက်မှုသမိုင်းကိုလိုက်လျှောက်ခြင်းကြောင့်၎င်းသည်၎င်း၏ခေတ်မီပုံစံသို့မည်ကဲ့သို့ပြောင်းလဲလာသည်ကိုပိုမိုရှင်းလင်းလာစေရန်ဂန္ထဝင် BPF အကြောင်းဆောင်းပါးတစ်ပုဒ်ဖြင့်စတင်ရန်ဆုံးဖြတ်ခဲ့သည်။

လွန်ခဲ့သောရာစုနှစ် ရှစ်ဆယ်၏အဆုံးတွင်၊ ကျော်ကြားသော Lawrence Berkeley ဓာတ်ခွဲခန်းမှ အင်ဂျင်နီယာများသည် လွန်ခဲ့သည့်ရာစုနှစ် ရှစ်ဆယ်နှောင်းပိုင်းတွင် ခေတ်မီသော ဟာ့ဒ်ဝဲများပေါ်တွင် ကွန်ရက်ပက်ကတ်များကို မည်ကဲ့သို့ စစ်ထုတ်ရမည်နည်းဟူသော မေးခွန်းကို စိတ်ဝင်စားလာကြသည်။ မူလက CSPF (CMU/Stanford Packet Filter) နည်းပညာတွင် အကောင်အထည်ဖော်ခဲ့သော စစ်ထုတ်ခြင်း၏ အခြေခံအယူအဆမှာ မလိုအပ်သော ပက်ကေ့ဂျ်များကို ဖြစ်နိုင်သမျှစောစီးစွာ စစ်ထုတ်ရန်ဖြစ်သည်၊ ဆိုလိုသည်မှာ၊ kernel space တွင် မလိုအပ်သော data များကို user space ထဲသို့ ကူးယူခြင်းမှ ရှောင်ကြဉ်သောကြောင့် ဖြစ်သည်။ kernel space တွင် အသုံးပြုသူကုဒ်အား runtime လုံခြုံရေးအတွက် runtime security ပေးရန်၊ sandboxed virtual machine ကိုအသုံးပြုခဲ့သည်။

သို့သော်၊ ရှိပြီးသား filters များအတွက် virtual machines များသည် stack-based machines များပေါ်တွင်လည်ပတ်ရန်ဒီဇိုင်းထုတ်ထားပြီး RISC စက်အသစ်များပေါ်တွင်ထိရောက်စွာမလည်ပတ်နိုင်ပါ။ ရလဒ်အနေဖြင့် Berkeley Labs မှ အင်ဂျင်နီယာများ၏ ကြိုးပမ်းအားထုတ်မှုဖြင့် BPF (Berkeley Packet Filters) နည်းပညာအသစ်ကို တီထွင်ခဲ့ပြီး Motorola 6502 ပရိုဆက်ဆာကို အခြေခံ၍ ဒီဇိုင်းထုတ်ထားသည့် virtual machine ဗိသုကာလက်ရာဖြစ်သည့် လူသိများသော ထုတ်ကုန်များ၏ အလုပ်လုပ်ဆောင်မှု၊ Apple II သို့မဟုတ် NES. virtual machine အသစ်သည် ရှိပြီးသား ဖြေရှင်းချက်များနှင့် နှိုင်းယှဉ်ပါက စစ်ထုတ်မှု စွမ်းဆောင်ရည် အဆ ဆယ်ဆ တိုးမြင့်လာပါသည်။

BPF စက်ဗိသုကာ

ဥပမာများကို ခွဲခြမ်းစိတ်ဖြာခြင်းဖြင့် ဗိသုကာပညာနှင့် ရင်းနှီးပါမည်။ သို့သော်၊ စတင်ရန်၊ စက်တွင် အသုံးပြုသူမှ ဝင်ရောက်နိုင်သော 32-bit မှတ်ပုံတင်မှု နှစ်ခုရှိသည် ဆိုကြပါစို့၊ A နှင့် index register X၊ 64 bytes of memory (16 လုံး)၊ စာရေးခြင်းနှင့် နောက်ဆက်တွဲဖတ်ရှုခြင်းအတွက် ရနိုင်သော၊ နှင့် ဤအရာဝတ္ထုများနှင့် လုပ်ဆောင်ရန်အတွက် အမိန့်ပေးသည့်စနစ်ငယ်။ အခြေအနေဆိုင်ရာ အသုံးအနှုန်းများကို အကောင်အထည်ဖော်ရန်အတွက် Jump လမ်းညွှန်ချက်များကို ပရိုဂရမ်များတွင်လည်း ရနိုင်သော်လည်း ပရိုဂရမ်၏ အချိန်မီပြီးစီးမှုကို အာမခံရန်၊ jumps များကိုသာ ရှေ့သို့လုပ်ဆောင်နိုင်သည်၊ အထူးသဖြင့်၊ ကွင်းဆက်များဖန်တီးခြင်းကို တားမြစ်ထားသည်။

စက်စတင်ရန်အတွက် ယေဘူယျအစီအစဉ်မှာ အောက်ပါအတိုင်းဖြစ်သည်။ အသုံးပြုသူသည် BPF ဗိသုကာအတွက် ပရိုဂရမ်တစ်ခုကို ဖန်တီးပြီး အသုံးပြုသည်။ အချို့ kernel ယန္တရား (ဥပမာ စနစ်ခေါ်ဆိုမှု) သည် ပရိုဂရမ်အား တင်၍ ချိတ်ဆက်သည်။ အချို့ကို kernel ရှိ event generator သို့ (ဥပမာ၊ event တစ်ခုသည် network card ပေါ်ရှိ နောက်ထုပ်ပိုး၏ ရောက်ရှိလာခြင်းဖြစ်သည်)။ ဖြစ်ရပ်တစ်ခု ဖြစ်ပေါ်လာသောအခါ၊ kernel သည် ပရိုဂရမ်ကို လုပ်ဆောင်သည် (ဥပမာ၊ စကားပြန်ဖြင့်) လုပ်ဆောင်ပြီး စက်မှတ်ဉာဏ်နှင့် သက်ဆိုင်သည်။ အချို့ကို kernel မန်မိုရီဒေသ (ဥပမာ၊ အဝင်ပက်ကတ်တစ်ခု၏ဒေတာ)။

အထက်ဖော်ပြပါများသည် ဥပမာများကို စတင်ကြည့်ရှုရန် ကျွန်ုပ်တို့အတွက် လုံလောက်ပါမည်- ကျွန်ုပ်တို့သည် လိုအပ်သလို စနစ်နှင့် အမိန့်ပေးဖော်မတ်ကို သိရှိနားလည်ပါမည်။ အကယ်၍ သင်သည် virtual machine တစ်ခု၏ command system ကိုချက်ချင်းလေ့လာပြီး၎င်း၏စွမ်းဆောင်ရည်အားလုံးကိုလေ့လာလိုပါက၊ မူရင်းဆောင်းပါးကိုသင်ဖတ်ရှုနိုင်သည်။ BSD Packet Filter နှင့်/သို့မဟုတ် ဖိုင်၏ ပထမတစ်ဝက် Documentation/networking/filter.txt kernel စာရွက်စာတမ်းမှ။ ထို့အပြင် တင်ပြချက်ကို လေ့လာနိုင်သည်။ libpcap: Packet Capture အတွက် ဗိသုကာနှင့် ပိုမိုကောင်းမွန်အောင်ပြုလုပ်ခြင်းနည်းလမ်းBPF ၏စာရေးဆရာတစ်ဦးဖြစ်သည့် McCanne သည် ဖန်တီးမှု၏သမိုင်းကြောင်းကို ပြောပြသည်။ libpcap.

Linux တွင် classic BPF အသုံးပြုခြင်း၏ သိသာထင်ရှားသော ဥပမာများအားလုံးကို ဆက်လက်စဉ်းစားရန် ကျွန်ုပ်တို့ ဆက်လက်လုပ်ဆောင်သွားပါမည်။ tcpdump (libpcap), seccomp၊ xt_bpf, cls_bpf.

ချစ်သူ

BPF ၏ဖွံ့ဖြိုးတိုးတက်မှုသည် packet filtering အတွက် frontend ၏ဖွံ့ဖြိုးတိုးတက်မှုနှင့်အပြိုင်လုပ်ဆောင်ခဲ့သည် - လူသိများသောအသုံးဝင်မှု tcpdump. ထို့အပြင်၊ ဤသည်မှာ ဂန္တဝင် BPF ကို အသုံးပြုခြင်း၏ ရှေးအကျဆုံးနှင့် အကျော်ကြားဆုံး ဥပမာဖြစ်ပြီး၊ လည်ပတ်မှုစနစ်များစွာတွင်ရရှိနိုင်သောကြောင့်၊ ကျွန်ုပ်တို့သည် ၎င်းနှင့်နည်းပညာကို စတင်လေ့လာပါမည်။

(ဤဆောင်းပါးတွင် Linux တွင် နမူနာများအားလုံးကို ကျွန်ုပ်ဖော်ပြခဲ့သည်။ 5.6.0-rc6. ပိုမိုကောင်းမွန်စွာဖတ်ရှုနိုင်စေရန်အတွက် အချို့သော command များ၏ output ကို တည်းဖြတ်ထားပါသည်။)

ဥပမာ- IPv6 packets များကို စောင့်ကြည့်ခြင်း။

အင်တာဖေ့စ်တစ်ခုပေါ်ရှိ IPv6 packets အားလုံးကို ကြည့်လိုသည်ဟု စိတ်ကူးကြည့်ကြပါစို့ eth0. ဒီလိုလုပ်ဖို့ ကျွန်တော်တို့ program ကို run နိုင်ပါတယ်။ tcpdump ရိုးရှင်းသော filter နှင့် ip6:

$ sudo tcpdump -i eth0 ip6

ထိုသို့ tcpdump filter ကို compile လုပ်ပါ။ ip6 BPF ဗိသုကာ bytecode ထဲသို့ ပို့ပြီး kernel (အသေးစိတ်အချက်အလက်များကို ကဏ္ဍတွင်ကြည့်ပါ။ Tcpdump- တင်နေသည်။) အင်တာဖေ့စ်မှတဆင့် ဖြတ်သန်းသွားသော ပက်ကတ်တိုင်းအတွက် တင်ထားသော စစ်ထုတ်မှုအား လုပ်ဆောင်သွားပါမည်။ eth0. အကယ်၍ filter သည် သုညမဟုတ်သောတန်ဖိုးကို ပြန်ပေးသည်။ n, ထို့နောက်အထိ n packet ၏ bytes ကို user space သို့ ကူးယူမည်ဖြစ်ပြီး output တွင် ၎င်းကို မြင်ရပါမည်။ tcpdump.

ကလေးများအတွက် BPF၊ အပိုင်း သုည- classic BPF

မည်သည့် bytecode ကို kernel သို့ပေးပို့ခဲ့သည်ကို ကျွန်ုပ်တို့ အလွယ်တကူ ရှာဖွေတွေ့ရှိနိုင်သည် tcpdump ၏အကူအညီနှင့်အတူ tcpdumpoption နှင့်အတူ run လျှင် -d:

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

လိုင်းသုညတွင်ကျွန်ုပ်တို့သည် command ကို run သည်။ ldh [12]"စာရင်းသွင်းရန်" ၏အတိုကောက်ဖြစ်သည်။ A လိပ်စာ 16” တွင်ရှိသော စကားလုံးတစ်ဝက် (12 bits) ရှိပြီး တစ်ခုတည်းသောမေးခွန်းမှာ ကျွန်ုပ်တို့သည် မည်သည့် memory အမျိုးအစားကို ကိုင်တွယ်ဖြေရှင်းနေသနည်း။ အဖြေက အဲ့ဒီမှာ x စတင်ခဲ့သည် (x+1)ခွဲခြမ်းစိတ်ဖြာထားသော ကွန်ရက်ပက်ကတ်၏ th byte။ ကျွန်ုပ်တို့သည် Ethernet အင်တာဖေ့စ်မှ packet များကိုဖတ်သည်။ eth0နှင့်ဤ နည်းလမ်းပက်ကက်သည် ဤကဲ့သို့ပုံရသည် (ရိုးရိုးရှင်းရှင်းအတွက်၊ ပက်ကတ်တွင် VLAN တဂ်များမရှိဟု ကျွန်ုပ်တို့ယူဆသည်)

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

ဒါကြောင့် command ကို execute လုပ်ပြီးနောက် ldh [12] မှတ်ပုံတင်၌ A လယ်ကွင်းတစ်ခုရှိလိမ့်မည်။ Ether Type — ဤ Ethernet frame တွင် ပေးပို့သော packet အမျိုးအစား။ စာကြောင်း 1 တွင် မှတ်ပုံတင်ထားသော အကြောင်းအရာများကို နှိုင်းယှဉ်ပါသည်။ A (အထုပ်အမျိုးအစား) ဂ 0x86ddနှင့်ဤ ထိုအရပ်၌ ကျွန်ုပ်တို့စိတ်ဝင်စားသောအမျိုးအစားမှာ IPv6 ဖြစ်သည်။ စာကြောင်း 1 တွင် နှိုင်းယှဉ်မှုအမိန့်အပြင် နောက်ထပ် ကော်လံနှစ်ခုပါရှိသည် - jt 2 и jf 3 - နှိုင်းယှဉ်မှုအောင်မြင်ပါက သင်သွားရန်လိုအပ်သည့် အမှတ်အသားများ (A == 0x86dd) မအောင်မြင်ဘူး။ ဒီတော့ အောင်မြင်တဲ့ကိစ္စ (IPv6) မှာ လိုင်း 2 ကိုသွားပြီးတော့ မအောင်မြင်တဲ့ကိစ္စမှာ လိုင်း 3 ကိုသွားပါ။ လိုင်း 3 မှာ ပရိုဂရမ်က code 0 နဲ့ အဆုံးသတ်ပါတယ် (packet ကို ကော်ပီမလုပ်ပါနဲ့) လိုင်း 2 မှာ program က code နဲ့ အဆုံးသတ်ပါတယ်။ 262144 (ငါ့ကို အများဆုံး 256 ကီလိုဘိုက် အထုပ်ကို ကူးယူပါ)။

ပိုမိုရှုပ်ထွေးသောဥပမာတစ်ခု- ကျွန်ုပ်တို့သည် TCP ပက်ကေ့ခ်ျများကို ဦးတည်ရာပေါက်ဖြင့် ကြည့်ရှုသည်။

ပန်းတိုင် 666 ပါသော TCP ပက်ကေ့ခ်ျအားလုံးကို မိတ္တူကူးယူထားသည့် filter တစ်ခုသည် မည်သို့မည်ပုံဖြစ်သည်ကို ကြည့်ကြပါစို့။ IPv4 အမှုသည် ပိုမိုရိုးရှင်းသောကြောင့် IPv6 ကို သုံးသပ်ပါမည်။ ဤဥပမာကိုလေ့လာပြီးနောက်၊ သင်သည် လေ့ကျင့်ခန်းတစ်ခုအနေဖြင့် သင့်ကိုယ်သင် IPv6 စစ်ထုတ်မှုကို ရှာဖွေနိုင်သည် (ip6 and tcp dst port 666) နှင့် အထွေထွေကိစ္စရပ်အတွက် စစ်ထုတ်မှုတစ်ခု (tcp dst port 666) ထို့ကြောင့်၊ ကျွန်ုပ်တို့စိတ်ဝင်စားသော filter သည် ဤကဲ့သို့ဖြစ်သည်-

$ 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 အထုပ်၏ 24th byte ။ ကျွန်ုပ်တို့၏အထုပ်ကိုကြည့်ရသည်နှင့်တူသည်။

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

ဆိုလိုသည်မှာ ကျွန်ုပ်တို့သည် မှတ်ပုံတင်ထဲသို့ သွင်းခြင်းပင်ဖြစ်သည်။ A ကျွန်ုပ်တို့သည် TCP အထုပ်များကိုသာကူးယူလိုသောကြောင့် ယုတ္တိရှိသော IP ခေါင်းစီး၏ပရိုတိုကောအကွက်။ ကျွန်ုပ်တို့သည် Protocol နှင့် နှိုင်းယှဉ်ပါသည်။ 0x6 (IPPROTO_TCP) စာကြောင်း ၃။

လိုင်း 4 နှင့် 5 တွင်ကျွန်ုပ်တို့သည်လိပ်စာ 20 တွင်ရှိသော halfwords ကိုတင်ပြီး command ကိုအသုံးပြုသည်။ jset သုံးခုထဲမှ တစ်ခုကို သတ်မှတ်ခြင်းရှိမရှိ စစ်ဆေးပါ။ အလံများ - ထုတ်ထားသည့် Mask ဝတ်ဆင်ပါ။ jset အထင်ရှားဆုံးသော အပိုင်းသုံးပိုင်းကို ရှင်းလင်းထားသည်။ ဘစ်သုံးခုမှ နှစ်ခုသည် ပက်ကက်သည် အစိတ်စိတ်အမွှာမွှာကွဲအက်နေသော IP ပက်ကတ်၏ အစိတ်အပိုင်းဟုတ်မဟုတ်၊ သို့မဟုတ်ပါက၊ ၎င်းသည် နောက်ဆုံးအပိုင်းအစရှိမရှိကို ပြောပြသည်။ တတိယဘစ်ကို သီးသန့်ထားပြီး သုညဖြစ်ရမည်။ မပြည့်စုံသော သို့မဟုတ် ပျက်နေသော ပက်ကေ့ဂျ်များကို မစစ်ဆေးချင်ပါ၊ ထို့ကြောင့် ဘစ်သုံးခုလုံးကို စစ်ဆေးပါ။

လိုင်း 6 သည် ဤစာရင်းတွင် စိတ်ဝင်စားစရာအကောင်းဆုံးဖြစ်သည်။ စကားရပ် ldxb 4*([14]&0xf) ဆိုလိုတာက ကျွန်တော်တို့ register ထဲကို သွင်းပါတယ်။ X ပက်ကက်၏ဆယ့်ငါးဘိုက်၏သိသာထင်ရှားသောလေးဘစ်ကို 4 ဖြင့်မြှောက်သည်။ ဆယ့်ငါးဘိုက်၏သိသာထင်ရှားသောဘစ်လေးခုသည် အကွက်ဖြစ်သည်။ အင်တာနက် ခေါင်းစီး အရှည် ခေါင်းစီး၏အရှည်ကို စကားလုံးများဖြင့် သိမ်းဆည်းပေးသော IPv4 ခေါင်းစီး၊ ထို့ကြောင့် သင်သည် 4 နှင့် မြှောက်ရန် လိုအပ်သည်။ စိတ်ဝင်စားစရာကောင်းသည်မှာ စကားရပ် 4*([14]&0xf) ဤဖောင်တွင်သာ အသုံးပြုနိုင်ပြီး မှတ်ပုံတင်ရန်အတွက်သာ အသုံးပြုနိုင်သော အထူးလိပ်စာအစီအစဉ်တစ်ခုအတွက် သတ်မှတ်ချက်တစ်ခုဖြစ်သည်။ X, i.e. ငါတို့လည်း မပြောနိုင်ဘူး။ ldb 4*([14]&0xf) သို့မဟုတ် ldxb 5*([14]&0xf) (ဥပမာ၊ ကျွန်ုပ်တို့သည် မတူညီသော အော့ဖ်ဆက်တစ်ခုကိုသာ သတ်မှတ်နိုင်သည်။ ldxb 4*([16]&0xf)) လက်ခံရရှိရန်အတွက် ဤလိပ်စာအစီအစဉ်ကို BPF တွင် အတိအကျထည့်သွင်းထားကြောင်း ရှင်းရှင်းလင်းလင်းသိရသည်။ X (index register) IPv4 ခေါင်းစီးအရှည်။

ဒါကြောင့် စာကြောင်း 7 မှာ စာလုံးတစ်ဝက်ကို တင်ဖို့ ကြိုးစားပါတယ်။ (X+16). 14 bytes ကို Ethernet header နှင့် သိမ်းပိုက်ထားကြောင်း သတိရပါ။ X IPv4 ခေါင်းစီး၏အရှည်ပါရှိသည်၊ ကျွန်ုပ်တို့နားလည်ပါသည်။ A TCP ဦးတည်ရာဆိပ်ကမ်းကို ဖွင့်ထားသည်-

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

နောက်ဆုံးတွင်၊ လိုင်း 8 တွင် destination port ကိုလိုချင်သောတန်ဖိုးနှင့်နှိုင်းယှဉ်ပြီး 9 သို့မဟုတ် 10 တွင် packet ကိုကော်ပီကူးသည်ဖြစ်စေမကူးသည်ဖြစ်စေရလဒ်ကိုပြန်ပေးသည်။

Tcpdump- တင်နေသည်။

ယခင်နမူနာများတွင်၊ ကျွန်ုပ်တို့သည် packet filtering အတွက် kernel သို့ BPF bytecode ကိုမည်သို့မည်ပုံဖွင့်ပုံအတိအကျကို ကျွန်ုပ်တို့အတိအကျမဖော်ပြထားပါ။ ယေဘူယျအားဖြင့်၊ tcpdump များစွာသော စနစ်များသို့ သယ်ဆောင်ပြီး စစ်ထုတ်မှုများဖြင့် လုပ်ဆောင်ရန် tcpdump စာကြည့်တိုက်ကို အသုံးပြု libpcap. အတိုချုပ်အားဖြင့်၊ အသုံးပြုပြီး အင်တာဖေ့စ်တစ်ခုပေါ်တွင် filter တစ်ခုထားရှိရန် libpcap, အောက်ပါအချက်များကိုလုပ်ပါ:

  • အမျိုးအစားဖော်ပြချက်တစ်ခုဖန်တီးပါ။ pcap_t အင်တာဖေ့စ်အမည်မှ pcap_create,
  • အင်တာဖေ့စ်ကို အသက်သွင်းရန်- pcap_activate,
  • compile filter- pcap_compile,
  • စစ်ထုတ်မှုကို ချိတ်ဆက်ပါ- pcap_setfilter.

ဘယ်လို function လဲဆိုတာ သိနိုင်ပါတယ်။ pcap_setfilter Linux တွင်အသုံးပြုသော၊ ကျွန်ုပ်တို့အသုံးပြုသည်။ 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
...

output ၏ပထမနှစ်လိုင်းတွင်ကျွန်ုပ်တို့ဖန်တီးသည်။ အကြမ်းပေါက် Ethernet frames အားလုံးကိုဖတ်ပြီး interface တွင်ချိတ်ရန် eth0... ထံမှ ကျွန်ုပ်တို့၏ပထမဆုံးဥပမာ filter ကိုငါတို့သိတယ်။ ip BPF ညွှန်ကြားချက်လေးခုပါ၀င်ပြီး တတိယစာကြောင်းတွင် ရွေးချယ်ခွင့်ကို မည်သို့အသုံးပြုသည်ကို ကျွန်ုပ်တို့တွေ့မြင်ရသည်။ SO_ATTACH_FILTER စနစ်ခေါ်ဆိုမှု setsockopt ကျွန်ုပ်တို့သည် အရှည်၏ filter တစ်ခုကို တင်၍ ချိတ်ဆက်ပါသည်။

ဂန္ထဝင် BPF တွင် filter တစ်ခုအား တင်ခြင်းနှင့် ချိတ်ဆက်ခြင်းသည် အနုမြူ လုပ်ဆောင်ချက်တစ်ခုအဖြစ် အမြဲဖြစ်ပေါ်ပြီး BPF ဗားရှင်းအသစ်တွင် ပရိုဂရမ်ကိုဖွင့်ပြီး ၎င်းကို event generator တွင် ချိတ်တွဲထားသည်ကို အချိန်မီ ခွဲခြားထားကြောင်း သတိပြုသင့်ပါသည်။

ဝှက်ထားသောအမှန်တရား

Output ၏ အနည်းငယ်ပိုပြည့်စုံသောဗားရှင်းသည် ဤကဲ့သို့ဖြစ်ပုံရသည်-

$ 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
...

အထက်တွင်ဖော်ပြထားသည့်အတိုင်း၊ ကျွန်ုပ်တို့သည် ကျွန်ုပ်တို့၏ filter ကိုလိုင်း 5 ရှိ socket သို့တင်ပြီးချိတ်ဆက်သော်လည်းလိုင်း 3 နှင့် 4 တွင်ဘာဖြစ်သွားသနည်း။ ဒါဟာထွက်လှည့် libpcap ကျွန်ုပ်တို့ကို ဂရုစိုက်ပါ - ကျွန်ုပ်တို့၏ filter ၏အထွက်တွင် ၎င်းကို မကျေနပ်နိုင်သော packet များမပါဝင်စေရန်၊ စာကြည့်တိုက်၊ ချိတ်ဆက်သည်။ dummy စစ်ထုတ် ret #0 (ပက်ကေ့ဂျ်အားလုံးကိုချပါ)၊ socket ကို ပိတ်ဆို့ခြင်းမပြုသည့်မုဒ်သို့ ပြောင်းပြီး ယခင်စစ်ထုတ်မှုများမှ ကျန်ရှိနေနိုင်သည့် ပက်ကေ့ခ်ျအားလုံးကို နုတ်ရန် ကြိုးစားသည်။

စုစုပေါင်း၊ classic BPF ကို အသုံးပြု၍ Linux ပေါ်ရှိ package များကို စစ်ထုတ်ရန်၊ ကဲ့သို့သော တည်ဆောက်ပုံပုံစံဖြင့် filter တစ်ခုရှိရန် လိုအပ်ပါသည်။ struct sock_fprog နှင့် open socket တစ်ခု၊ ထို့နောက် filter ကို system call ကိုအသုံးပြုပြီး socket တွင်တွဲနိုင်သည်။ setsockopt.

စိတ်ဝင်စားစရာမှာ filter သည် အစိမ်းသက်သက်မဟုတ်ဘဲ မည်သည့် socket နှင့်မဆို တွဲနိုင်ပါသည်။ ဒီမှာ နမူနာ ဝင်လာသော UDP ဒေတာဂရမ်များအားလုံးမှ ပထမနှစ်ဘိုက်မှလွဲ၍ အားလုံးကို ဖြတ်တောက်မည့် ပရိုဂရမ်တစ်ခု။ (ဆောင်းပါးကို ရှုပ်ပွမနေစေရန် ကုဒ်တွင် မှတ်ချက်များ ထည့်ထားပါသည်။)

အသုံးပြုပုံအသေးစိတ်အချက်များ setsockopt စစ်ထုတ်မှုများကို ချိတ်ဆက်ရန်အတွက်၊ ကြည့်ပါ။ ပလပ်ပေါက်(၇)ခုဒါပေမယ့် ကိုယ့်ဘာသာကိုယ် စီစစ်ရေးရတာနဲ့ ပတ်သက်တယ်။ struct sock_fprog အကူအညီမပါဘဲ tcpdump ကဏ္ဍမှာ ဆွေးနွေးပါမယ်။ ကျွန်ုပ်တို့၏လက်ဖြင့် BPF ပရိုဂရမ်ရေးဆွဲခြင်း။.

Classic BPF နှင့် XNUMX ရာစု

BPF ကို 1997 ခုနှစ်တွင် Linux တွင် ထည့်သွင်းခဲ့ပြီး အချိန်အတော်ကြာအောင် လုပ်သားအဖြစ် ရှိနေခဲ့သည်။ libpcap အထူးပြောင်းလဲမှုများမရှိဘဲ ( Linux သီးသန့်ပြောင်းလဲမှုများ၊ ဟုတ်ပါတယ်၊ ဒါဟာခဲ့ဒါပေမယ့် သူတို့ဟာ ကမ္ဘာလုံးဆိုင်ရာ ရုပ်ပုံလွှာကို မပြောင်းလဲခဲ့ပါဘူး။) Eric Dumazet က 2011 ခုနှစ်တွင် BPF ဆင့်ကဲပြောင်းလဲလာမည့် ပြင်းထန်သော လက္ခဏာရပ်များ ထွက်ပေါ်လာခဲ့သည်။ ကွမ်းခြံကုန်းkernel သို့ Just In Time Compiler ပေါင်းထည့်သည့် - BPF bytecode ကို မူရင်းသို့ ပြောင်းရန်အတွက် ဘာသာပြန်သူ x86_64 ကုဒ်။

JIT compiler သည် 2012 ခုနှစ်တွင် ပြောင်းလဲမှုများ၏ ကွင်းဆက်တွင် ပထမဆုံးဖြစ်သည်။ သည်ထင်ရှား Filter တွေရေးပေးနိုင်ပါတယ်။ မြတ်နိုး၂၀၁၃ ခုနှစ် ဇန်နဝါရီလတွင် BPF ကို အသုံးပြုခဲ့သည်။ ထပ်ပြောသည် module တစ်ခု xt_bpfစည်းကမ်းချက်တွေကို ရေးနိုင်ရမယ်။ iptables BPF ၏အကူအညီဖြင့်၊ နှင့် 2013 အောက်တိုဘာလတွင်ဖြစ်ခဲ့သည်။ ထပ်ပြောသည် module တစ်ခုလည်းဖြစ်ပါတယ်။ cls_bpfBPF ကို အသုံးပြု၍ အသွားအလာ အမျိုးအစားခွဲခြားမှုများကို ရေးနိုင်စေမည့်၊

ဤနမူနာများအားလုံးကို မကြာမီ အသေးစိတ်ကြည့်ရှုနိုင်ပါမည်၊ သို့သော် စာကြည့်တိုက်မှ ပံ့ပိုးပေးထားသည့် စွမ်းဆောင်နိုင်မှုများကြောင့် BPF အတွက် မတရားသော ပရိုဂရမ်များ ရေးသားပုံနှင့် စုစည်းပုံတို့ကို လေ့လာရန် ဦးစွာ အသုံးဝင်ပါလိမ့်မည်။ libpcap ကန့်သတ်ချက် (ရိုးရှင်းသော ဥပမာ- စစ်ထုတ်မှု ထုတ်ပေးသည်။ libpcap တန်ဖိုးနှစ်ခုသာ ပြန်ပေးနိုင်သည် - 0 သို့မဟုတ် 0x40000) သို့မဟုတ် ယေဘူယျအားဖြင့်၊ seccomp ၏ကိစ္စရပ်တွင် အကျုံးမဝင်ပါ။

ကျွန်ုပ်တို့၏လက်ဖြင့် BPF ပရိုဂရမ်ရေးဆွဲခြင်း။

BPF ညွှန်ကြားချက်များ၏ binary format နှင့် ရင်းနှီးရအောင်၊ အလွန်ရိုးရှင်းပါသည်။

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

instruction တစ်ခုစီသည် 64 bits ရှိပြီး ပထမ 16 bits သည် instruction code ဖြစ်ပြီး၊ ထို့နောက် XNUMX-bit indent နှစ်ခုရှိသည်။ jt и jfနှင့် အငြင်းအခုံအတွက် 32 bits Kအမိန့်တစ်ခုနှင့်တစ်ခု ကွဲပြားသည့် ရည်ရွယ်ချက်၊ ဥပမာ၊ အမိန့် retပရိုဂရမ်ကို အဆုံးသတ်သည့် ကုဒ်ပါရှိသည်။ 6နှင့် return value ကို ကိန်းသေမှ ယူသည်။ 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]) ဤသည်မှာ filter ၏ပုံစံဖြစ်သည်။ 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> helper macros ကိုသတ်မှတ်ထားသည် - အထက်ဖော်ပြပါကဲ့သို့တူညီသောဥပမာကိုပြန်လည်ရေးသားနိုင်သည်။

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),
}

သို့သော်ဤရွေးချယ်မှုသည်အလွန်အဆင်ပြေသည်မဟုတ်။ ဤသည်မှာ Linux kernel ပရိုဂရမ်မာများက ကျိုးကြောင်းဆင်ခြင်ခဲ့ခြင်းဖြစ်ပြီး၊ ထို့ကြောင့် လမ်းညွှန်တွင်ဖြစ်သည်။ tools/bpf kernels သည် classic BPF နှင့်အလုပ်လုပ်ရန်အတွက် assembler နှင့် debugger ကိုရှာနိုင်သည်။

Assembly language သည် debug output နှင့် အလွန်ဆင်တူသည်။ tcpdumpထို့အပြင် ကျွန်ုပ်တို့သည် သင်္ကေတတံဆိပ်များကို သတ်မှတ်နိုင်သည်။ ဥပမာအားဖြင့်၊ ဤနေရာတွင် TCP/IPv4 မှလွဲ၍ packet အားလုံးကို ချပေးသည့် ပရိုဂရမ်တစ်ခုဖြစ်သည်။

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

ပုံမှန်အားဖြင့်၊ assembler သည် format ဖြင့် code ကိုထုတ်ပေးသည်။ <количество инструкций>,<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,

C ပရိုဂရမ်မာများ အဆင်ပြေစေရန်အတွက် မတူညီသော အထွက်ဖော်မတ်ကို အသုံးပြုနိုင်ပါသည်။

$ 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ဒီအပိုင်းအစမှာ လုပ်ခဲ့သလိုပါပဲ။

Linux နှင့် netsniff-ng တိုးချဲ့မှုများ

စံ BPF အပြင်၊ Linux နှင့် tools/bpf/bpf_asm အထောက်အပံ့နှင့် စံသတ်မှတ်ချက်မဟုတ်သော. အခြေခံအားဖြင့်၊ ဖွဲ့စည်းပုံတစ်ခု၏နယ်ပယ်များသို့ဝင်ရောက်ရန် ညွှန်ကြားချက်များကိုအသုံးပြုသည်။ struct sk_buffkernel ရှိ network packet ကိုဖော်ပြသော၊ သို့သော်၊ ဥပမာ၊ အခြားအကူအညီပေးသည့် ညွှန်ကြားချက် အမျိုးအစားများလည်း ရှိသေးသည်။ ldw cpu မှတ်ပုံတင်ထဲသို့ သွင်းလိမ့်မည်။ A kernel function ကိုလည်ပတ်ခြင်း၏ရလဒ် raw_smp_processor_id(). (BPF ဗားရှင်းအသစ်တွင်၊ မန်မိုရီ၊ တည်ဆောက်ပုံများနှင့် ဖြစ်ရပ်များကို ထုတ်ပေးရန်အတွက် ပရိုဂရမ်များကို အသုံးပြုရန်အတွက် kernel helpers အစုံဖြင့် ပံ့ပိုးပေးရန်အတွက် ဤစံမဟုတ်သော extension များကို တိုးချဲ့ထားပါသည်။) ဤသည်မှာ ကျွန်ုပ်တို့သာလျှင် ကူးယူထားသော filter တစ်ခု၏ စိတ်ဝင်စားစရာကောင်းသော ဥပမာတစ်ခုဖြစ်သည်။ အိတ်စတန်းရှင်းကို အသုံးပြု၍ အသုံးပြုသူနေရာသို့ packet ခေါင်းစီးများ poff, payload offset-

ld poff
ret a

BPF extension များကို အသုံးပြု၍မရပါ။ tcpdumpဒါပေမယ့် ဒါက utility package ကို သိဖို့ အကြောင်းပြချက်ကောင်းတစ်ခုပါ။ netsniff-ngအခြားအရာများထဲတွင် အဆင့်မြင့် ပရိုဂရမ်တစ်ခုပါရှိသည်။ netsniff-ngBPF ကို အသုံးပြု၍ စစ်ထုတ်ခြင်းအပြင် ထိရောက်သော လမ်းကြောင်းထုတ်ပေးသည့် ဂျင်နရေတာ ပါ၀င်ပြီး ထက်ပိုမိုအဆင့်မြင့်သည်။ tools/bpf/bpf_asmBPF တပ်ဆင်သူဟု ခေါ်သည်။ bpfc. ပက်ကေ့ဂျ်တွင် အတော်လေးအသေးစိတ်စာရွက်စာတမ်းများပါရှိသည်၊ ဆောင်းပါး၏အဆုံးတွင် လင့်ခ်များကို ကြည့်ပါ။

မြတ်နိုး

ထို့ကြောင့်၊ ကျွန်ုပ်တို့သည် ထင်သလို ရှုပ်ထွေးမှုရှိသော BPF ပရိုဂရမ်များကို မည်သို့ရေးရမည်ကို သိထားပြီးဖြစ်သည့်အတွက် နမူနာအသစ်များကို ကြည့်ရှုရန် အဆင်သင့်ဖြစ်နေပါပြီ၊ ယင်းတို့အနက် ပထမမှာ BPF filters များကို အသုံးပြု၍ ရရှိနိုင်သော စနစ်ခေါ်ဆိုမှုဆိုင်ရာ အကြောင်းပြချက်များကို စီမံခန့်ခွဲရန် ခွင့်ပြုသည့် seccomp နည်းပညာ၊ ပေးထားသည့် လုပ်ငန်းစဉ်နှင့် ၎င်း၏ သားစဉ်မြေးဆက်များ။

seccomp ၏ပထမဗားရှင်းကို 2005 ခုနှစ်တွင် kernel တွင်ထည့်သွင်းခဲ့ပြီး တစ်ခုတည်းသောရွေးချယ်ခွင့်ကိုပေးသောကြောင့် - လုပ်ငန်းစဉ်တစ်ခုအတွက်ရရှိနိုင်သောစနစ်ခေါ်ဆိုမှုအစုအဝေးကိုကန့်သတ်ရန်အတွက် အောက်ပါအတိုင်းလုပ်ဆောင်နိုင်သည်- read, write, exit и sigreturnစည်းကမ်းဖောက်ဖျက်သည့် ဖြစ်စဉ်ကို အသုံးပြု၍ သတ်ဖြတ်ခဲ့ခြင်း ဖြစ်သည်။ SIGKILL. သို့သော်လည်း၊ 2012 ခုနှစ်တွင်၊ seccomp သည် BPF စစ်ထုတ်မှုများကို အသုံးပြုရန် စွမ်းရည်ကို ထည့်သွင်းခဲ့ပြီး ခွင့်ပြုထားသော စနစ်ခေါ်ဆိုမှုအစုအဝေးကို သတ်မှတ်ရန်နှင့် ၎င်းတို့၏ ငြင်းခုံချက်များကိုပင် စစ်ဆေးမှုများ ပြုလုပ်နိုင်စေမည်ဖြစ်သည်။ (စိတ်ဝင်စားစရာကောင်းတာက Chrome သည် ဤလုပ်ဆောင်ချက်ကို ပထမဆုံးအသုံးပြုသူများထဲမှ တစ်ဦးဖြစ်ပြီး Chrome မှလူများသည် BPF ဗားရှင်းအသစ်အပေါ်အခြေခံ၍ KRSI ယန္တရားကို တီထွင်နေပြီး Linux Security Modules များကို စိတ်ကြိုက်ပြင်ဆင်ခွင့်ပြုထားသည်။) ထပ်လောင်းစာရွက်စာတမ်းများ၏ လင့်ခ်များကို အဆုံးတွင် တွေ့နိုင်ပါသည်။ ဆောင်းပါး၏

seccomp အသုံးပြုခြင်းနှင့်ပတ်သက်သည့် hub တွင် ဆောင်းပါးများ ရှိထားပြီးဖြစ်သည်ကို သတိပြုပါ ၊ တစ်စုံတစ်ယောက်သည် အောက်ပါ အပိုင်းများကို မဖတ်မီ (သို့မဟုတ်) မဖတ်မီ ၎င်းတို့ကို ဖတ်လိုပေမည်။ ဆောင်းပါးထဲမှာ ကွန်တိန်နာများနှင့် လုံခြုံရေး- seccomp seccomp ကိုအသုံးပြုခြင်း၏နမူနာများကို ပံ့ပိုးပေးသည်၊၊ 2007 ဗားရှင်းနှင့် BPF ကိုအသုံးပြုသည့်ဗားရှင်း (filters များကို libseccomp ဖြင့်ထုတ်ပေးသည်)၊ Docker နှင့် seccomp ၏ချိတ်ဆက်မှုအကြောင်း ဆွေးနွေးမှုများနှင့် အသုံးဝင်သောလင့်ခ်များစွာကိုလည်း ပံ့ပိုးပေးပါသည်။ ဆောင်းပါးထဲမှာ systemd ဖြင့် daemons ကိုခွဲထုတ်ခြင်း သို့မဟုတ် "ဒါအတွက် Docker မလိုအပ်ပါဘူး!" အထူးသဖြင့် ၎င်းသည် systemd လုပ်ဆောင်နေသည့် daemons အတွက် အမည်ပျက်စာရင်းများ သို့မဟုတ် စနစ်ခေါ်ဆိုမှုများ၏ အဖြူရောင်စာရင်းများကို မည်သို့ထည့်သွင်းရမည်ကို အကျုံးဝင်ပါသည်။

နောက်တစ်ခုကတော့ filters တွေကို ဘယ်လိုရေးပြီး load လုပ်ရမလဲဆိုတာ ကြည့်ပါမယ်။ seccomp ဗလာ C နှင့်စာကြည့်တိုက်ကို အသုံးပြု libseccomp ရွေးချယ်မှုတစ်ခုစီ၏ ကောင်းကျိုးဆိုးကျိုးများကား အဘယ်နည်း၊ နောက်ဆုံးတွင်၊ ပရိုဂရမ်မှ seccomp ကို မည်သို့အသုံးပြုသည်ကို ကြည့်ကြပါစို့။ strace.

seccomp အတွက် filter များကို ရေးသားခြင်းနှင့် တင်ခြင်း။

ကျွန်ုပ်တို့သည် BPF ပရိုဂရမ်များကို မည်သို့ရေးရမည်ကို သိထားပြီးဖြစ်သောကြောင့် seccomp programming interface ကို ဦးစွာကြည့်ကြပါစို့။ လုပ်ငန်းစဉ်အဆင့်တွင် စစ်ထုတ်မှုတစ်ခုကို သင်သတ်မှတ်နိုင်ပြီး ကလေးလုပ်ငန်းစဉ်များအားလုံးသည် ကန့်သတ်ချက်များကို အမွေဆက်ခံမည်ဖြစ်သည်။ စနစ်ခေါ်ဆိုမှုကို အသုံးပြု၍ လုပ်ဆောင်သည်။ seccomp(2):

seccomp(SECCOMP_SET_MODE_FILTER, flags, &filter)

ဘယ်မှာ &filter - ဤအရာသည် ကျွန်ုပ်တို့နှင့် ရင်းနှီးပြီးသား ဖွဲ့စည်းပုံကို ညွှန်ပြနေပါသည်။ struct sock_fprog, i.e. BPF အစီအစဉ်။

seccomp အတွက်ပရိုဂရမ်များသည် sockets အတွက်ပရိုဂရမ်များနှင့်မည်သို့ကွာခြားသနည်း။ ကူးယူဆက်စပ်မှု။ socket များတွင်၊ packet ပါ ၀ င်သည့် memory area ကိုပေးခဲ့ပြီး 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 ချိန်ညှိခြင်းဖြင့်သာ ဝင်ရောက်နိုင်ပြီး စစ်ထုတ်မှုတစ်ခုအား တင်ရန်ကြိုးစားသောအခါတွင် သင်သည် စကားလုံးတစ်ဝက် သို့မဟုတ် ဘိုက်တစ်ခုကို တင်၍မရပါ။ 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 ကိုအသုံးပြုရန်အတွက် filter များကိုရေးပြီးတင်ပါ။ libseccomp

မူရင်းကုဒ်တွင် သို့မဟုတ် BPF စည်းဝေးပွဲများတွင် စစ်ထုတ်မှုများရေးသားခြင်းက သင့်အား ရလဒ်အပေါ် အပြည့်အဝထိန်းချုပ်နိုင်စေသော်လည်း တစ်ချိန်တည်းတွင်၊ သယ်ဆောင်ရလွယ်ကူပြီး/သို့မဟုတ် ဖတ်နိုင်သောကုဒ်ရှိရန် တစ်ခါတစ်ရံတွင် ပိုကောင်းပါသည်။ ဒါကို စာကြည့်တိုက်က ကူညီပေးပါလိမ့်မယ်။ libseccompအနက်ရောင် သို့မဟုတ် အဖြူ စစ်ထုတ်မှုများကို ရေးသားရန်အတွက် စံအင်တာဖေ့စ်ကို ပံ့ပိုးပေးသော၊

ဥပမာအားဖြင့်၊ အသုံးပြုသူ၏ရွေးချယ်မှု၏ binary ဖိုင်ကိုလုပ်ဆောင်သည့် ပရိုဂရမ်တစ်ခု ရေးလိုက်ကြပါစို့၊ ယခင်က စနစ်ခေါ်ဆိုမှုများ၏ အမည်းရောင်စာရင်းကို ထည့်သွင်းထားပြီး၊ အထက်ပါဆောင်းပါး (ပရိုဂရမ်ကို ပိုမိုဖတ်ရှုနိုင်စေရန်အတွက် ရိုးရှင်းပြီး ဗားရှင်းအပြည့်အစုံကို တွေ့ရှိနိုင်သည်။ ဒီမှာ):

#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]);
}

ပထမဦးစွာ ကျွန်ုပ်တို့သည် array တစ်ခုကို သတ်မှတ်သည်။ sys_numbers ပိတ်ဆို့ရန် စနစ်ခေါ်ဆိုမှုနံပါတ် 40+ ၏ ထို့နောက် အကြောင်းအရာကို စတင်ပါ။ ctx စာကြည့်တိုက်ကို ကျွန်ုပ်တို့ ခွင့်ပြုလိုကြောင်း ပြောပြပါ (SCMP_ACT_ALLOW) စနစ်ခေါ်ဆိုမှုအားလုံးကို မူရင်းအတိုင်း (အမည်ပျက်စာရင်းများသွင်းရန် ပိုမိုလွယ်ကူသည်)။ ထို့နောက် တစ်ခုပြီးတစ်ခု၊ ကျွန်ုပ်တို့သည် အမည်ပျက်စာရင်းမှ စနစ်ခေါ်ဆိုမှုအားလုံးကို ပေါင်းထည့်ပါသည်။ စာရင်းမှ စနစ်ခေါ်ဆိုမှုကို တုံ့ပြန်ရန်အတွက် ကျွန်ုပ်တို့ တောင်းဆိုပါသည်။ SCMP_ACT_TRAPဤကိစ္စတွင်၊ seccomp သည် လုပ်ငန်းစဉ်ဆီသို့ အချက်ပြမှုတစ်ခု ပေးပို့မည်ဖြစ်သည်။ SIGSYS မည်သည့်စနစ်ခေါ်ဆိုမှု စည်းမျဉ်းများကို ချိုးဖောက်ခဲ့ကြောင်း ဖော်ပြချက်နှင့်အတူ။ နောက်ဆုံးတွင်၊ ကျွန်ုပ်တို့သည် အသုံးပြု၍ kernel ထဲသို့ program ကို load လုပ်သည်။ 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 မကြာသေးမီက ရှိခဲ့သည်။ patch ပါဝင်သည်။filter attribute အတွက် ပံ့ပိုးမှု ထပ်လောင်းပေးသော၊ SCMP_FLTATR_CTL_OPTIMIZE. ဤ attribute ကို 2 သို့သတ်မှတ်ခြင်းဖြင့် filter ကို binary ရှာဖွေမှုပရိုဂရမ်အဖြစ်သို့ပြောင်းလဲပေးလိမ့်မည်။

binary ရှာဖွေမှု စစ်ထုတ်မှုများ မည်သို့အလုပ်လုပ်သည်ကို သင်ကြည့်ရှုလိုပါက၊ ကြည့်ရှုပါ။ ရိုးရိုးပဲဗျ။စနစ်ခေါ်ဆိုမှုနံပါတ်များကိုခေါ်ဆိုခြင်းဖြင့် 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 ပရိုဂရမ်များသည် Indentation Jumps များကို မလုပ်ဆောင်နိုင်သောကြောင့် သိသိသာသာ မြန်မြန်ဆန်ဆန်ရေးရန် မဖြစ်နိုင်ပါ (ဥပမာ၊ ကျွန်ုပ်တို့ မလုပ်နိုင်ပါ၊ jmp A သို့မဟုတ် jmp [label+X]) ထို့ကြောင့် အသွင်ကူးပြောင်းမှုအားလုံးသည် တည်ငြိမ်သည်။

seccomp နှင့် strace

အသုံးဝင်ပုံကို လူတိုင်းသိပါတယ်။ strace Linux ရှိ လုပ်ငန်းစဉ်များ၏ အပြုအမူများကို လေ့လာရန်အတွက် မရှိမဖြစ်လိုအပ်သောကိရိယာတစ်ခုဖြစ်သည်။ ဒါပေမယ့် တော်တော်များများလည်း ကြားဖူးကြမှာပါ။ စွမ်းဆောင်ရည်ပြဿနာများ ဒီ utility ကိုသုံးတဲ့အခါ။ အမှန်က အဲဒါ 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၊ အပိုင်း သုည- classic BPF

သို့သော် seccomp ဖြင့်၊ ဤလုပ်ငန်းစဉ်ကို ကျွန်ုပ်တို့အလိုရှိသည့်အတိုင်း အတိအကျ optimize လုပ်နိုင်ပါသည်။ ပြောရရင် စံနစ်ကို ကြည့်ချင်ရင် ခေါ်ဆိုပါ။ Xထို့နောက် BPF filter ကို ရေးနိုင်သည်။ X တန်ဖိုးတစ်ခု ပြန်ပေးသည်။ SECCOMP_RET_TRACEကျွန်ုပ်တို့ စိတ်မဝင်စားသော ဖုန်းခေါ်ဆိုမှုများအတွက်၊ SECCOMP_RET_ALLOW:

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

ဤကိစ္စတွင်ခုနှစ်, strace အစပိုင်းတွင် လုပ်ငန်းစဉ်အဖြစ် စတင်သည်။ PTRACE_CONTစနစ်ခေါ်ဆိုမှုမဟုတ်ပါက၊ ကျွန်ုပ်တို့၏ filter ကို စနစ်ခေါ်ဆိုမှုတစ်ခုစီအတွက် လုပ်ဆောင်ပါသည်။ Xသို့ဆိုလျှင် လုပ်ငန်းစဉ်သည် ဆက်လက်လည်ပတ်နေမည်ဖြစ်သော်လည်း၊ Xထို့နောက် seccomp သည် ထိန်းချုပ်မှုကို လွှဲပြောင်းပေးလိမ့်မည်။ straceအငြင်းအခုံများကို ကြည့်ရှုပြီး လုပ်ငန်းစဉ်ကို စတင်မည် ဖြစ်သည်။ PTRACE_SYSCALL (seccomp သည် system call မှထွက်သည့် program တစ်ခုကို run နိုင်စွမ်းမရှိသောကြောင့်)။ စနစ်ခေါ်ဆိုမှုပြန်လာသောအခါ၊ strace အသုံးပြုပြီး လုပ်ငန်းစဉ်ကို ပြန်လည်စတင်ပါမည်။ PTRACE_CONT seccomp မှ မက်ဆေ့ခ်ျအသစ်များကို စောင့်မျှော်နေပါမည်။

ကလေးများအတွက် BPF၊ အပိုင်း သုည- classic BPF

option ကိုအသုံးပြုသောအခါ --seccomp-bpf ကန့်သတ်ချက်နှစ်ခုရှိသည်။ ပထမဦးစွာ၊ ရှိပြီးသားလုပ်ငန်းစဉ် (option) တွင် ပါဝင်ရန် မဖြစ်နိုင်ပါ။ -p အစီအစဉ်များ strace) ၎င်းကို seccomp မှ မပံ့ပိုးသောကြောင့်ဖြစ်သည်။ ဒုတိယအနေနဲ့ကတော့ ဖြစ်နိုင်ခြေမရှိပါဘူး။ မဟုတ် ကလေးလုပ်ငန်းစဉ်များကိုကြည့်ပါ၊ အဘယ်ကြောင့်ဆိုသော် ၎င်းကို disable လုပ်နိုင်စွမ်းမရှိဘဲ ကလေးလုပ်ငန်းစဉ်များအားလုံးမှ seccomp filter များကို အမွေဆက်ခံထားပါသည်။

လုပ်ပုံလုပ်နည်းအသေးစိတ်အသေးစိတ် strace အလုပ်တွေ seccomp တို့မှ တွေ့ရှိနိုင်သည်။ မကြာသေးမီကအစီရင်ခံစာ. ကျွန်ုပ်တို့အတွက်၊ စိတ်ဝင်စားစရာအကောင်းဆုံးအချက်မှာ seccomp မှကိုယ်စားပြုထားသော classic BPF ကို ယနေ့ထိအသုံးပြုနေဆဲဖြစ်သည်။

xt_bpf

အခုပဲ ကွန်ရက်လောကကို ပြန်သွားကြရအောင်။

နောက်ခံသမိုင်း- လွန်ခဲ့သော ၂၀၀၇ ခုနှစ်တွင် အူတိုင်ဖြစ်ခဲ့သည် ထပ်ပြောသည် module တစ်ခု xt_u32 netfilter အတွက် ၎င်းကို ပို၍ရှေးကျသော ယာဉ်အသွားအလာ အမျိုးအစားခွဲခြားမှုဖြင့် ယှဉ်တွဲရေးသားထားသည်။ cls_u32 အောက်ဖော်ပြပါ ရိုးရှင်းသော လုပ်ဆောင်ချက်များကို အသုံးပြု၍ iptables အတွက် မတရားသော ဒွိဒွိစည်းမျဉ်းများကို ရေးနိုင်ခွင့်ပြုသည်- ပက်ကေ့ဂျ်တစ်ခုမှ 32 bits ကို တင်ပြီး ၎င်းတို့တွင် ဂဏန်းသင်္ချာဆိုင်ရာ လုပ်ဆောင်ချက်များကို လုပ်ဆောင်ပါ။ ဥပမာအားဖြင့်,

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

padding 32 မှစတင်၍ IP header ၏ 6 bits ကိုတင်ပြီး mask တစ်ခုကို အသုံးပြုပါ 0xFF (အနိမ့်ဘိုက်ကို ယူပါ)။ ဒီလယ် protocol IP header ကို 1 (ICMP) နှင့် နှိုင်းယှဉ်ပါသည်။ စည်းမျဉ်းတစ်ခုတွင် စစ်ဆေးမှုများစွာကို ပေါင်းစပ်နိုင်ပြီး အော်ပရေတာကိုလည်း လုပ်ဆောင်နိုင်သည်။ @ - X bytes ကို ညာဘက်သို့ ရွှေ့ပါ။ ဥပမာအားဖြင့် စည်းကမ်းချက်

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

TCP Sequence Number သည် မညီခြင်းရှိမရှိ စစ်ဆေးပါ။ 0x29. ဤစည်းမျဉ်းများကို လက်ဖြင့်ရေးခြင်းသည် အလွန်အဆင်ပြေမည်မဟုတ်ကြောင်း ရှင်းရှင်းလင်းလင်းရှိပြီးသားဖြစ်သောကြောင့် အသေးစိတ်အချက်အလက်များကို ကျွန်ုပ်မပြောလိုပါ။ ဆောင်းပါးထဲမှာ BPF - မေ့သွားသော bytecodeအသုံးပြုမှုနှင့် စည်းကမ်းဖန်တီးမှုများအတွက် နမူနာများနှင့် လင့်ခ်များစွာ ရှိပါသည်။ xt_u32. ဤဆောင်းပါး၏အဆုံးတွင် လင့်ခ်များကို ကြည့်ပါ။

2013 ခုနှစ်ကတည်းက module အစား module များ xt_u32 BPF အခြေခံ module ကိုသင်သုံးနိုင်သည်။ xt_bpf. ဤအဝေးကြီးကိုဖတ်ဖူးသူတိုင်းသည် ၎င်း၏လုပ်ဆောင်မှုနိယာမနှင့်ပတ်သက်၍ ရှင်းလင်းပြီးသားဖြစ်သင့်သည်- BPF bytecodes ကို iptables စည်းမျဉ်းများအဖြစ် လုပ်ဆောင်ပါ။ ဥပမာအားဖြင့် ဤကဲ့သို့ စည်းမျဉ်းအသစ်တစ်ခုကို သင်ဖန်တီးနိုင်သည်-

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

ဒီမှာ <байткод> - ၎င်းသည် assembler output format တွင် code ဖြစ်သည်။ 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 packet အားလုံးကို စစ်ထုတ်နေပါသည်။ module တစ်ခုရှိ BPF ပရိုဂရမ်အတွက် အကြောင်းအရာ xt_bpfဟုတ်ပါတယ်၊ iptables တွေမှာတော့ packet data ကို IPv4 header ရဲ့အစကို ညွှန်ပြပါတယ်။ BPF ပရိုဂရမ်မှ တန်ဖိုးကို ပြန်ပေးပါ။ ဘူလီယံဘယ်မှာ false packet နှင့် မကိုက်ညီဟု ဆိုလိုသည်။

module ဆိုတာ ရှင်းပါတယ်။ xt_bpf အထက်ဖော်ပြပါ ဥပမာများထက် ပိုမိုရှုပ်ထွေးသော စစ်ထုတ်မှုများကို ပံ့ပိုးပေးသည်။ Cloudfare မှ တကယ့်ဥပမာများကို ကြည့်ကြပါစို့။ မကြာသေးမီအထိ သူတို့သည် module ကိုအသုံးပြုခဲ့သည်။ xt_bpf DDoS တိုက်ခိုက်မှုများကို ကာကွယ်ရန်။ ဆောင်းပါးထဲမှာ BPF Tools များကို မိတ်ဆက်ခြင်း။ ၎င်းတို့သည် BPF စစ်ထုတ်မှုများကို မည်ကဲ့သို့ (နှင့် အဘယ်ကြောင့်) ထုတ်ပေးကြောင်း ရှင်းပြပြီး ထိုကဲ့သို့သော စစ်ထုတ်မှုများကို ဖန်တီးရန်အတွက် အသုံးဝင်မှုအစုံသို့ လင့်ခ်များကို ထုတ်ဝေကြသည်။ ဥပမာအားဖြင့်၊ utility ကိုအသုံးပြုခြင်း။ bpfgen နာမည်တစ်ခုအတွက် DNS query တစ်ခုနှင့် ကိုက်ညီသော BPF ပရိုဂရမ်တစ်ခုကို သင်ဖန်တီးနိုင်သည်။ 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 compiler code ကို ထုတ်ပြန်ခဲ့သည်။ ဆောင်းပါးထဲမှာ p0f BPF compiler ကို မိတ်ဆက်ခြင်း။ p0f က ဘာလဲဆိုတာနဲ့ p0f signatures တွေကို 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_bpfBPF ဗားရှင်းအသစ်ကို အသုံးပြုရန်အတွက် ရွေးချယ်စရာများထဲမှတစ်ခုဖြစ်သည့် XDP သို့ ပြောင်းရွှေ့သွားသည်ကို ကြည့်ပါ။ L4Drop- XDP DDoS လျှော့ချမှုများ.

cls_bpf

kernel တွင် classic BPF ကိုအသုံးပြုခြင်း၏နောက်ဆုံးဥပမာမှာ classifier ဖြစ်သည်။ cls_bpf Linux ရှိ traffic control subsystem အတွက်၊ 2013 နှစ်ကုန်တွင် Linux သို့ ထည့်သွင်းပြီး ရှေးခေတ်ကို စိတ်ကူးဖြင့် အစားထိုးခြင်း၊ cls_u32.

သို့သော်၊ ကျွန်ုပ်တို့သည် ယခုအလုပ်အား ဖော်ပြမည်မဟုတ်ပါ။ cls_bpfဂန္ထဝင် BPF အကြောင်း ဗဟုသုတ ရှုထောင့်မှကြည့်လျှင် ၎င်းသည် ကျွန်ုပ်တို့အား မည်သည့်အရာမှ ပေးမည်မဟုတ် - ကျွန်ုပ်တို့သည် လုပ်ဆောင်နိုင်စွမ်းအားလုံးနှင့် ရင်းနှီးပြီးသားဖြစ်နေပါပြီ။ ထို့အပြင်၊ Extended BPF အကြောင်းပြောနေသည့် နောက်ဆောင်းပါးများတွင်၊ ဤအမျိုးအစားခွဲခြားမှုကို တစ်ကြိမ်ထက်ပို၍တွေ့ပါမည်။

classic BPF c ကိုအသုံးပြုခြင်းအကြောင်း မပြောရန်နောက်ထပ်အကြောင်းပြချက် cls_bpf ပြဿနာမှာ၊ Extended BPF နှင့် နှိုင်းယှဉ်ပါက ဤကိစ္စတွင် အသုံးချနိုင်မှု နယ်ပယ်သည် အလွန်ကျဉ်းမြောင်းသွားသည်- ရှေးရိုးပရိုဂရမ်များသည် ပက်ကေ့ဂျ်များ၏ အကြောင်းအရာများကို မပြောင်းလဲနိုင်သည့်အပြင် ဖုန်းခေါ်ဆိုမှုများကြားတွင် အခြေအနေကို မသိမ်းဆည်းနိုင်ပါ။

ဒါကြောင့် classic BPF ကို နှုတ်ဆက်ပြီး အနာဂတ်ကို မျှော်ကြည့်ဖို့ အချိန်တန်ပါပြီ။

Classic BPF ကို နှုတ်ဆက်လိုက်ပါ။

အစောပိုင်း ကိုးဆယ်ကျော်တွင် တီထွင်ခဲ့သော BPF နည်းပညာသည် ရာစုတစ်ခု၏ လေးပုံတစ်ပုံအထိ အောင်မြင်စွာနေထိုင်ခဲ့ပြီး အဆုံးတိုင်အောင် အသုံးချမှုအသစ်များကို တွေ့ရှိခဲ့သည်။ သို့သော်၊ ဂန္ထဝင် BPF ဖွံ့ဖြိုးတိုးတက်မှုအတွက် တွန်းအားတစ်ခုအဖြစ် ဆောင်ရွက်ခဲ့သည့် stack machines မှ RISC သို့ ကူးပြောင်းခြင်းကဲ့သို့ပင်၊ 32 ခုနှစ်များတွင် 64-bit မှ XNUMX-bit စက်များသို့ ကူးပြောင်းသွားပြီး classic BPF သည် အသုံးမ၀င်တော့ပါ။ ထို့အပြင်၊ ဂန္တဝင် BPF ၏စွမ်းရည်များသည် အလွန်အကန့်အသတ်ရှိပြီး ခေတ်မမီတော့သော ဗိသုကာလက်ရာများအပြင် ကျွန်ုပ်တို့သည် BPF ပရိုဂရမ်များသို့ ဖုန်းခေါ်ဆိုမှုများကြားတွင် အခြေအနေကို သိမ်းဆည်းနိုင်စွမ်းမရှိပါ၊ တိုက်ရိုက်အသုံးပြုသူ အပြန်အလှန်တုံ့ပြန်နိုင်ခြေမရှိပါ၊ အပြန်အလှန်တုံ့ပြန်နိုင်ခြေမရှိပါ။ kernel နှင့်အတူ၊ အကန့်အသတ်ရှိသောဖွဲ့စည်းပုံအကွက်များကိုဖတ်ခြင်းမှလွဲ၍ sk_buff အရိုးရှင်းဆုံး အကူအညီပေးသည့် လုပ်ဆောင်ချက်များကို စတင်ခြင်းဖြင့် သင်သည် ပက်ကတ်များ၏ အကြောင်းအရာများကို ပြောင်းလဲပြီး ၎င်းတို့ကို ပြန်ညွှန်းနိုင်မည်မဟုတ်ပေ။

တကယ်တော့၊ လက်ရှိ Linux ရှိ ဂန္တဝင် BPF ၏ ကျန်ရှိနေသည့်အရာအားလုံးသည် API အင်တာဖေ့စ်ဖြစ်ပြီး kernel အတွင်းရှိ ဂန္တဝင်ပရိုဂရမ်များအားလုံးကို socket filters သို့မဟုတ် seccomp filter များဖြစ်စေ ၊ Extended BPF သည် ဖော်မတ်အသစ်သို့ အလိုအလျောက်ပြန်ဆိုပါသည်။ (ဒါကို နောက်ဆောင်းပါးမှာ အတိအကျ ပြောပြပါမယ်။)

Alexey Starovoitov သည် BPF အဆင့်မြှင့်တင်မှုအစီအစဉ်ကို အဆိုပြုသောအခါတွင် အသစ်သောဗိသုကာအသွင်ကူးပြောင်းမှုစတင်ခဲ့သည်။ 2013 တွင် သက်ဆိုင်ရာ ဖာထေးမှုများ ပေါ်လာတော့တယ်။ core ၌။ ကျွန်ုပ်နားလည်သလောက်၊ ကနဦးအစီအစဥ်သည် 64-bit စက်များတွင် ပိုမိုထိရောက်စွာလည်ပတ်နိုင်ရန် ဗိသုကာနှင့် JIT compiler ကို ပိုကောင်းအောင်ပြုလုပ်ရန်သာဖြစ်ပြီး၊ ယင်းအစား ဤပိုမိုကောင်းမွန်အောင်ပြုလုပ်ခြင်းက Linux ဖွံ့ဖြိုးတိုးတက်မှု၏အခန်းသစ်၏အစကို အမှတ်အသားပြုပါသည်။

ဤစီးရီးရှိ နောက်ထပ်ဆောင်းပါးများသည် အတွင်းပိုင်း BPF ဟု ကနဦးသိကြသော နည်းပညာသစ်၏ တည်ဆောက်ပုံနှင့် အသုံးချမှုများ၊ ထို့နောက် တိုးချဲ့ထားသော BPF နှင့် ယခုအခါ ရိုးရိုး BPF တို့ကို ခြုံငုံမိမည်ဖြစ်သည်။

ကိုးကား

  1. Steven McCanne နှင့် Van Jacobson ၊ "BSD Packet Filter- User-level Packet Capture အတွက် ဗိသုကာအသစ်" https://www.tcpdump.org/papers/bpf-usenix93.pdf
  2. Steven McCanne၊ "libpcap- Packet Capture အတွက် ဗိသုကာနှင့် အကောင်းမွန်ဆုံးနည်းလမ်း" 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 - မေ့သွားသော bytecode https://blog.cloudflare.com/bpf-the-forgotten-bytecode/
  6. BPF Tool ကို မိတ်ဆက်ခြင်း။ 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. habr- systemd ဖြင့် daemons ကိုခွဲထုတ်ခြင်း သို့မဟုတ် "ဒါအတွက် Docker မလိုအပ်ပါဘူး!"
  12. Paul Chaignon, "strace --seccomp-bpf: hood under a look", https://fosdem.org/2020/schedule/event/debugging_strace_bpf/
  13. netsniff-ng: http://netsniff-ng.org/

source: www.habr.com

မှတ်ချက် Add