BPF kwa watoto wadogo, sehemu ya sifuri: BPF ya kawaida

Berkeley Packet Filters (BPF) ni teknolojia ya Linux kernel ambayo imekuwa kwenye kurasa za mbele za machapisho ya teknolojia ya lugha ya Kiingereza kwa miaka kadhaa sasa. Mikutano imejaa ripoti juu ya matumizi na maendeleo ya BPF. David Miller, mtunza mfumo mdogo wa mtandao wa Linux, anaita mazungumzo yake katika Linux Plumbers 2018 "Mazungumzo haya hayahusu XDP" (XDP ni kesi moja ya utumiaji ya BPF). Brendan Gregg anatoa mazungumzo yenye kichwa Linux BPF Superpowers. Toke HΓΈiland-JΓΈrgensen anachekakwamba punje sasa ni microkernel. Thomas Graf anakuza wazo hilo BPF ni javascript kwa kernel.

Bado hakuna maelezo ya utaratibu wa BPF juu ya Habre, na kwa hiyo katika mfululizo wa makala nitajaribu kuzungumza juu ya historia ya teknolojia, kuelezea zana za usanifu na maendeleo, na kuelezea maeneo ya matumizi na mazoezi ya kutumia BPF. Makala hii, sifuri, katika mfululizo, inaelezea historia na usanifu wa BPF ya classic, na pia inaonyesha siri za kanuni zake za uendeshaji. tcpdump, seccomp, strace, na mengi zaidi.

Ukuzaji wa BPF unadhibitiwa na jumuiya ya mitandao ya Linux, programu kuu zilizopo za BPF zinahusiana na mitandao na kwa hiyo, kwa ruhusa. @eucariot, Niliita mfululizo "BPF kwa watoto wadogo", kwa heshima ya mfululizo mkubwa "Mitandao ya watoto wadogo".

Kozi fupi katika historia ya BPF (c)

Teknolojia ya kisasa ya BPF ni toleo lililoboreshwa na kupanuliwa la teknolojia ya zamani yenye jina moja, ambayo sasa inaitwa BPF ya kawaida ili kuepuka kuchanganyikiwa. Huduma inayojulikana iliundwa kulingana na BPF ya kawaida tcpdump, utaratibu seccomp, pamoja na moduli zisizojulikana sana xt_bpf kwa iptables na mainishaji cls_bpf. Katika Linux ya kisasa, programu za BPF za kawaida hutafsiriwa kiatomati kwa fomu mpya, hata hivyo, kutoka kwa mtazamo wa mtumiaji, API imebaki mahali na matumizi mapya ya BPF ya kawaida, kama tutakavyoona katika nakala hii, bado yanapatikana. Kwa sababu hii, na pia kwa sababu kufuatia historia ya maendeleo ya BPF classical katika Linux, itakuwa wazi jinsi na kwa nini tolewa katika hali yake ya kisasa, niliamua kuanza na makala kuhusu classical BPF.

Mwishoni mwa miaka ya themanini ya karne iliyopita, wahandisi kutoka Maabara maarufu ya Lawrence Berkeley walipendezwa na swali la jinsi ya kuchuja vizuri pakiti za mtandao kwenye vifaa ambavyo vilikuwa vya kisasa mwishoni mwa miaka ya themanini ya karne iliyopita. Wazo la msingi la kuchuja, lililotekelezwa awali katika teknolojia ya CSPF (CMU/Stanford Packet Filter), lilikuwa ni kuchuja pakiti zisizo za lazima mapema iwezekanavyo, i.e. kwenye nafasi ya kernel, kwani hii inaepuka kunakili data isiyo ya lazima kwenye nafasi ya mtumiaji. Ili kutoa usalama wa wakati wa kutekelezwa kwa kutumia msimbo wa mtumiaji kwenye nafasi ya kernel, mashine pepe ya kisanduku cha mchanga ilitumiwa.

Hata hivyo, mashine pepe za vichujio vilivyopo ziliundwa ili kuendeshwa kwa kutumia mashine zenye rafu na hazikufanya kazi kwa ufanisi kwenye mashine mpya zaidi za RISC. Kama matokeo, kupitia juhudi za wahandisi kutoka Berkeley Labs, teknolojia mpya ya BPF (Berkeley Packet Filters) ilitengenezwa, usanifu wa mashine halisi ambao uliundwa kwa msingi wa processor ya Motorola 6502 - kazi kubwa ya bidhaa zinazojulikana kama vile. Apple II au NES. Mashine mpya pepe iliongeza utendaji wa chujio mara kumi ikilinganishwa na suluhu zilizopo.

Usanifu wa mashine ya BPF

Tutafahamiana na usanifu kwa njia ya kufanya kazi, kuchambua mifano. Walakini, kwa kuanzia, wacha tuseme kwamba mashine hiyo ilikuwa na rejista mbili za 32-bit zinazoweza kupatikana kwa mtumiaji, kikusanyiko. A na rejista ya index X, 64 byte za kumbukumbu (maneno 16), inapatikana kwa kuandika na kusoma baadae, na mfumo mdogo wa amri za kufanya kazi na vitu hivi. Maagizo ya kuruka kwa utekelezaji wa maneno ya masharti pia yalipatikana katika programu, lakini ili kuhakikisha kukamilika kwa wakati wa programu, kuruka kunaweza tu kufanywa mbele, yaani, hasa, ilikuwa marufuku kuunda loops.

Mpango wa jumla wa kuanzisha mashine ni kama ifuatavyo. Mtumiaji huunda programu ya usanifu wa BPF na, kwa kutumia baadhi utaratibu wa kernel (kama vile simu ya mfumo), hupakia na kuunganisha programu kwa baadhi kwa jenereta ya tukio kwenye kernel (kwa mfano, tukio ni kuwasili kwa pakiti inayofuata kwenye kadi ya mtandao). Wakati tukio linatokea, kernel inaendesha programu (kwa mfano, katika mkalimani), na kumbukumbu ya mashine inalingana na kwa baadhi eneo la kumbukumbu ya kernel (kwa mfano, data ya pakiti inayoingia).

Hapo juu itakuwa ya kutosha kwetu kuanza kuangalia mifano: tutafahamiana na mfumo na umbizo la amri inapohitajika. Ikiwa unataka kusoma mara moja mfumo wa amri wa mashine halisi na ujifunze juu ya uwezo wake wote, basi unaweza kusoma nakala asili. Kichujio cha Pakiti ya BSD na/au nusu ya kwanza ya faili Documentation/networking/filter.txt kutoka kwa nyaraka za kernel. Kwa kuongeza, unaweza kusoma uwasilishaji libpcap: Mbinu ya Usanifu na Uboreshaji kwa Kukamata Pakiti, ambapo McCanne, mmoja wa waandishi wa BPF, anazungumzia kuhusu historia ya uumbaji libpcap.

Sasa tunaendelea kuzingatia mifano yote muhimu ya kutumia BPF ya kawaida kwenye Linux: tcpdump (libpcap), kwa pamoja, xt_bpf, cls_bpf.

tcpdump

Ukuzaji wa BPF ulifanyika sambamba na ukuzaji wa sehemu ya mbele ya kuchuja pakiti - matumizi yanayojulikana. tcpdump. Na, kwa kuwa huu ndio mfano wa zamani na maarufu zaidi wa kutumia BPF ya kawaida, inayopatikana kwenye mifumo mingi ya uendeshaji, tutaanza masomo yetu ya teknolojia nayo.

(Niliendesha mifano yote kwenye nakala hii kwenye Linux 5.6.0-rc6. Matokeo ya baadhi ya amri yamehaririwa kwa usomaji bora.)

Mfano: kutazama pakiti za IPv6

Hebu tufikirie kwamba tunataka kuangalia pakiti zote za IPv6 kwenye kiolesura eth0. Ili kufanya hivyo, tunaweza kuendesha programu tcpdump na kichujio rahisi ip6:

$ sudo tcpdump -i eth0 ip6

Katika kesi hiyo, tcpdump inakusanya kichujio ip6 kwenye bytecode ya usanifu wa BPF na utume kwa kernel (tazama maelezo katika sehemu Tcpdump: inapakia) Kichujio kilichopakiwa kitaendeshwa kwa kila pakiti inayopita kwenye kiolesura eth0. Ikiwa kichujio kinarudisha thamani isiyo ya sifuri n, kisha hadi n baiti za pakiti zitanakiliwa kwa nafasi ya mtumiaji na tutaiona kwenye matokeo tcpdump.

BPF kwa watoto wadogo, sehemu ya sifuri: BPF ya kawaida

Inabadilika kuwa tunaweza kujua kwa urahisi ni bytecode iliyotumwa kwa kernel tcpdump kwa msaada wa tcpdump, ikiwa tutaiendesha na chaguo -d:

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

Kwenye mstari wa sifuri tunaendesha amri ldh [12], ambayo inasimamia "pakia kwenye rejista A nusu ya neno (biti 16) iliyoko kwenye anwani 12” na swali la pekee ni ni aina gani ya kumbukumbu tunayoshughulikia? Jibu ni kwamba x huanza (x+1)baiti ya pakiti ya mtandao iliyochambuliwa. Tunasoma pakiti kutoka kwa interface ya Ethernet eth0, na hii ina maanakwamba pakiti inaonekana kama hii (kwa unyenyekevu, tunadhania kuwa hakuna vitambulisho vya VLAN kwenye pakiti):

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

Kwa hivyo baada ya kutekeleza amri ldh [12] katika rejista A kutakuwa na shamba Ether Type β€” aina ya pakiti inayotumwa katika fremu hii ya Ethaneti. Kwenye mstari wa 1 tunalinganisha yaliyomo kwenye rejista A (aina ya kifurushi) c 0x86dd, na hii na kuna Aina tunayovutiwa nayo ni IPv6. Kwenye mstari wa 1, pamoja na amri ya kulinganisha, kuna safu mbili zaidi - jt 2 ΠΈ jf 3 - alama ambazo unahitaji kwenda ikiwa ulinganisho umefanikiwa (A == 0x86dd) na bila kufanikiwa. Kwa hiyo, katika kesi ya mafanikio (IPv6) tunakwenda kwenye mstari wa 2, na katika kesi isiyofanikiwa - kwa mstari wa 3. Kwenye mstari wa 3 mpango huo unaisha na msimbo wa 0 (usiinakili pakiti), kwenye mstari wa 2 programu inaisha na kanuni. 262144 (nakili kifurushi kisichozidi kilobaiti 256).

Mfano mgumu zaidi: tunaangalia pakiti za TCP kwa bandari lengwa

Hebu tuone jinsi kichujio kinavyoonekana ambacho kinakili pakiti zote za TCP zilizo na mlango wa 666 lengwa. Tutazingatia kipochi cha IPv4, kwa kuwa kipochi cha IPv6 ni rahisi zaidi. Baada ya kusoma mfano huu, unaweza kuchunguza kichujio cha IPv6 mwenyewe kama zoezi (ip6 and tcp dst port 666) na kichungi cha kesi ya jumla (tcp dst port 666) Kwa hivyo, kichungi tunachopendezwa nacho kinaonekana kama hii:

$ 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

Tayari tunajua mistari ya 0 na 1 hufanya nini. Kwenye mstari wa 2 tayari tumeangalia kuwa hii ni pakiti ya IPv4 (Aina ya Ether = 0x800) na kuipakia kwenye rejista A Byte 24 ya pakiti. Kifurushi chetu kinaonekana kama

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

maana yake tunapakia kwenye rejista A sehemu ya Itifaki ya kichwa cha IP, ambayo ni ya kimantiki, kwa sababu tunataka kunakili pakiti za TCP pekee. Tunalinganisha Itifaki na 0x6 (IPPROTO_TCP) kwenye mstari wa 3.

Kwenye mstari wa 4 na 5 tunapakia nusu ya maneno iko kwenye anwani ya 20 na kutumia amri jset angalia ikiwa moja ya tatu imewekwa bendera - amevaa mask iliyotolewa jset sehemu tatu muhimu zaidi zimefutwa. Biti mbili kati ya tatu hutuambia ikiwa pakiti ni sehemu ya pakiti ya IP iliyogawanyika, na ikiwa ni hivyo, ikiwa ni kipande cha mwisho. Biti ya tatu imehifadhiwa na lazima iwe sifuri. Hatutaki kuangalia pakiti ambazo hazijakamilika au zilizovunjika, kwa hivyo tunaangalia biti zote tatu.

Mstari wa 6 ndio unaovutia zaidi katika tangazo hili. Kujieleza ldxb 4*([14]&0xf) ina maana tunapakia kwenye rejista X biti nne muhimu zaidi za baiti ya kumi na tano ya pakiti ikizidishwa na 4. Biti nne muhimu zaidi za baiti ya kumi na tano ni sehemu. Urefu wa Kichwa cha Mtandao Kichwa cha IPv4, ambacho huhifadhi urefu wa kichwa kwa maneno, kwa hivyo unahitaji kuzidisha kwa 4. Inashangaza, usemi huo. 4*([14]&0xf) ni jina la mpango maalum wa kushughulikia ambao unaweza kutumika tu katika fomu hii na kwa rejista pekee X, i.e. hatuwezi kusema pia ldb 4*([14]&0xf) wala ldxb 5*([14]&0xf) (tunaweza tu kutaja tofauti tofauti, kwa mfano, ldxb 4*([16]&0xf)) Ni wazi kuwa mpango huu wa kushughulikia uliongezwa kwa BPF haswa ili kupokea X (rejista ya faharasa) urefu wa kichwa cha IPv4.

Kwa hivyo kwenye mstari wa 7 tunajaribu kupakia nusu ya neno (X+16). Kumbuka kwamba byte 14 zinachukuliwa na kichwa cha Ethernet, na X ina urefu wa kichwa cha IPv4, tunaelewa kuwa in A Mlango lengwa wa TCP umepakiwa:

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

Hatimaye, kwenye mstari wa 8 tunalinganisha bandari ya marudio na thamani inayotakiwa na kwenye mstari wa 9 au 10 tunarudisha matokeo - ikiwa kunakili pakiti au la.

Tcpdump: inapakia

Katika mifano iliyotangulia, hatukukaa kwa undani juu ya jinsi tunavyopakia bytecode ya BPF kwenye kernel kwa uchujaji wa pakiti. Kwa ujumla, tcpdump kuhamishwa kwa mifumo mingi na kwa kufanya kazi na vichungi tcpdump hutumia maktaba libpcap. Kwa kifupi, kuweka kichujio kwenye kiolesura kwa kutumia libpcap, unahitaji kufanya yafuatayo:

Ili kuona jinsi kazi pcap_setfilter kutekelezwa katika Linux, sisi kutumia strace (baadhi ya mistari imeondolewa):

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

Kwenye mistari miwili ya kwanza ya pato tunaunda tundu ghafi kusoma fremu zote za Ethaneti na kuifunga kwa kiolesura eth0. Kutoka mfano wetu wa kwanza tunajua kwamba chujio ip itakuwa na maagizo manne ya BPF, na kwenye mstari wa tatu tunaona jinsi ya kutumia chaguo SO_ATTACH_FILTER simu ya mfumo setsockopt tunapakia na kuunganisha chujio cha urefu wa 4. Hii ni chujio chetu.

Inafaa kumbuka kuwa katika BPF ya kawaida, kupakia na kuunganisha kichungi kila wakati hufanyika kama operesheni ya atomiki, na katika toleo jipya la BPF, upakiaji wa programu na kuifunga kwa jenereta ya tukio hutenganishwa kwa wakati.

Ukweli Uliofichwa

Toleo kamili zaidi la pato linaonekana kama hii:

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

Kama ilivyoelezwa hapo juu, tunapakia na kuunganisha chujio chetu kwenye tundu kwenye mstari wa 5, lakini nini kinatokea kwenye mstari wa 3 na 4? Inageuka kuwa hii libpcap inatutunza - ili matokeo ya chujio yetu yasijumuishe pakiti ambazo hazikidhi, maktaba. inaunganisha chujio cha dummy ret #0 (dondosha pakiti zote), hubadilisha soketi hadi hali isiyozuia na inajaribu kutoa pakiti zote ambazo zinaweza kubaki kutoka kwa vichungi vya awali.

Kwa jumla, ili kuchuja vifurushi kwenye Linux kwa kutumia BPF ya kawaida, unahitaji kuwa na kichungi katika mfumo wa muundo kama struct sock_fprog na tundu la wazi, baada ya hapo chujio kinaweza kushikamana na tundu kwa kutumia simu ya mfumo setsockopt.

Inashangaza, chujio kinaweza kushikamana na tundu lolote, sio tu mbichi. Hapa mfano programu ambayo hukata baiti zote isipokuwa mbili za kwanza kutoka kwa datagramu zote zinazoingia za UDP. (Niliongeza maoni kwenye msimbo ili nisisumbue nakala hiyo.)

Maelezo zaidi kuhusu matumizi setsockopt kwa vichungi vya kuunganisha, ona soketi(7), lakini kuhusu kuandika vichungi vyako kama struct sock_fprog bila msaada tcpdump tutazungumza katika sehemu Kupanga BPF kwa mikono yetu wenyewe.

Classic BPF na karne ya XNUMX

BPF ilijumuishwa katika Linux mnamo 1997 na imebaki kuwa kazi kwa muda mrefu libpcap bila mabadiliko yoyote maalum (mabadiliko maalum ya Linux, kwa kweli, walikuwa, lakini hazikubadilisha picha ya ulimwengu). Dalili za kwanza kubwa kwamba BPF ingeibuka ilikuja mwaka wa 2011, wakati Eric Dumazet alipopendekeza kiraka, ambayo inaongeza Mkusanyaji wa Wakati wa Wakati kwenye kernel - mtafsiri wa kubadilisha bytecode ya BPF kuwa asili x86_64 kanuni.

Mkusanyaji wa JIT alikuwa wa kwanza katika safu ya mabadiliko: mnamo 2012 alionekana uwezo wa kuandika filters kwa seccomp, kwa kutumia BPF, Januari 2013 kulikuwa imeongezwa moduli xt_bpf, ambayo inakuwezesha kuandika sheria kwa iptables kwa msaada wa BPF, na mnamo Oktoba 2013 ilikuwa imeongezwa pia moduli cls_bpf, ambayo hukuruhusu kuandika waainishaji wa trafiki kwa kutumia BPF.

Tutaangalia mifano hii yote kwa undani zaidi hivi karibuni, lakini kwanza itakuwa muhimu kwetu kujifunza jinsi ya kuandika na kukusanya programu za kiholela za BPF, kwa kuwa uwezo unaotolewa na maktaba. libpcap mdogo (mfano rahisi: chujio kilichotolewa libpcap inaweza kurudisha maadili mawili tu - 0 au 0x40000) au kwa ujumla, kama ilivyo kwa seccomp, haitumiki.

Kupanga BPF kwa mikono yetu wenyewe

Wacha tufahamiane na muundo wa binary wa maagizo ya BPF, ni rahisi sana:

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

Kila maagizo huchukua bits 64, ambayo bits 16 za kwanza ni kanuni ya mafundisho, basi kuna indents mbili-bit nane, jt ΠΈ jf, na biti 32 za hoja K, madhumuni ambayo hutofautiana kutoka amri hadi amri. Kwa mfano, amri ret, ambayo inasitisha programu ina msimbo 6, na thamani ya kurudi inachukuliwa kutoka kwa mara kwa mara K. Katika C, maagizo moja ya BPF yanawakilishwa kama muundo

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

na mpango mzima uko katika mfumo wa muundo

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

Kwa hivyo, tunaweza tayari kuandika programu (kwa mfano, tunajua nambari za maagizo kutoka [1]) Hivi ndivyo kichujio kitakavyoonekana ip6 ya mfano wetu wa kwanza:

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

programu prog tunaweza kutumia kisheria katika simu

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

Kuandika mipango kwa namna ya kanuni za mashine si rahisi sana, lakini wakati mwingine ni muhimu (kwa mfano, kwa kufuta, kuunda vipimo vya kitengo, kuandika makala kwenye Habre, nk). Kwa urahisi, katika faili <linux/filter.h> macros msaidizi hufafanuliwa - mfano sawa na hapo juu unaweza kuandikwa tena kama

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

Hata hivyo, chaguo hili si rahisi sana. Hivi ndivyo waandaaji wa programu za Linux kernel walijadili, na kwa hivyo kwenye saraka tools/bpf kokwa unaweza kupata kikusanyaji na kitatuzi cha kufanya kazi na BPF ya kawaida.

Lugha ya mkusanyiko ni sawa na pato la utatuzi tcpdump, lakini kwa kuongeza tunaweza kutaja lebo za mfano. Kwa mfano, hapa kuna programu ambayo hutupa pakiti zote isipokuwa TCP/IPv4:

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

Kwa chaguo-msingi, mkusanyaji hutoa msimbo katika umbizo <количСство инструкций>,<code1> <jt1> <jf1> <k1>,..., kwa mfano wetu na TCP itakuwa

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

Kwa urahisi wa watengeneza programu C, umbizo tofauti la pato linaweza kutumika:

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

Maandishi haya yanaweza kunakiliwa katika ufafanuzi wa muundo wa aina struct sock_filter, kama tulivyofanya mwanzoni mwa sehemu hii.

Linux na viendelezi vya netsniff-ng

Mbali na BPF ya kawaida, Linux na tools/bpf/bpf_asm msaada na seti isiyo ya kawaida. Kimsingi, maagizo hutumiwa kufikia mashamba ya muundo struct sk_buff, ambayo inaelezea pakiti ya mtandao kwenye kernel. Hata hivyo, pia kuna aina nyingine za maelekezo ya msaidizi, kwa mfano ldw cpu itapakia kwenye rejista A matokeo ya kuendesha kazi ya kernel raw_smp_processor_id(). (Katika toleo jipya la BPF, viendelezi hivi visivyo vya kawaida vimepanuliwa ili kutoa programu na seti ya wasaidizi wa kernel kwa ajili ya kufikia kumbukumbu, miundo, na matukio ya kuzalisha.) Huu hapa ni mfano wa kuvutia wa kichujio ambacho tunakili tu pakiti vichwa kwenye nafasi ya mtumiaji kwa kutumia kiendelezi poff, upunguzaji wa upakiaji:

ld poff
ret a

Viendelezi vya BPF haviwezi kutumika katika tcpdump, lakini hii ni sababu nzuri ya kufahamiana na kifurushi cha matumizi netsniff-ng, ambayo, kati ya mambo mengine, ina programu ya juu netsniff-ng, ambayo, pamoja na kuchuja kwa kutumia BPF, pia ina jenereta ya trafiki yenye ufanisi, na ya juu zaidi kuliko tools/bpf/bpf_asm, kiunganishi cha BPF kiliita bpfc. Kifurushi kina nyaraka za kina kabisa, tazama pia viungo mwishoni mwa kifungu.

seccomp

Kwa hivyo, tayari tunajua jinsi ya kuandika programu za BPF za ugumu wa kiholela na tuko tayari kuangalia mifano mpya, ya kwanza ambayo ni teknolojia ya seccomp, ambayo inaruhusu, kwa kutumia vichungi vya BPF, kusimamia seti na seti ya hoja za simu za mfumo zinazopatikana. mchakato fulani na vizazi vyake.

Toleo la kwanza la seccomp liliongezwa kwenye kernel mnamo 2005 na haikuwa maarufu sana, kwani ilitoa chaguo moja tu - kuweka kikomo cha simu za mfumo zinazopatikana kwa mchakato kwa zifuatazo: read, write, exit ΠΈ sigreturn, na mchakato uliokiuka sheria uliuawa kwa kutumia SIGKILL. Walakini, mnamo 2012, seccomp iliongeza uwezo wa kutumia vichungi vya BPF, hukuruhusu kufafanua seti ya simu zinazoruhusiwa za mfumo na hata kufanya ukaguzi kwenye hoja zao. (Cha kufurahisha, Chrome ilikuwa mmoja wa watumiaji wa kwanza wa utendakazi huu, na watu wa Chrome kwa sasa wanatengeneza utaratibu wa KRSI kulingana na toleo jipya la BPF na kuruhusu ubinafsishaji wa Module za Usalama za Linux.) Viungo vya hati za ziada vinaweza kupatikana mwishoni. ya makala.

Kumbuka kuwa tayari kumekuwa na nakala kwenye kitovu kuhusu kutumia seccomp, labda mtu atataka kuzisoma kabla (au badala ya) kusoma vifungu vifuatavyo. Katika makala Vyombo na usalama: seccomp hutoa mifano ya kutumia seccomp, toleo la 2007 na toleo la kutumia BPF (vichungi vinatengenezwa kwa kutumia libseccomp), inazungumza juu ya unganisho la seccomp na Docker, na pia hutoa viungo vingi muhimu. Katika makala Kutenga daemoni na systemd au "hauitaji Docker kwa hili!" Inashughulikia, haswa, jinsi ya kuongeza orodha zisizoruhusiwa au orodha zilizoidhinishwa za simu za mfumo kwa daemoni zinazoendesha systemd.

Ifuatayo tutaona jinsi ya kuandika na kupakia vichungi kwa seccomp katika C tupu na kutumia maktaba libseccomp na ni nini faida na hasara za kila chaguo, na hatimaye, hebu tuone jinsi seccomp inatumiwa na programu strace.

Kuandika na kupakia vichungi vya seccomp

Tayari tunajua jinsi ya kuandika programu za BPF, kwa hivyo hebu tuangalie kwanza kiolesura cha programu cha seccomp. Unaweza kuweka kichujio katika kiwango cha mchakato, na michakato yote ya mtoto itarithi vikwazo. Hii inafanywa kwa kutumia simu ya mfumo seccomp(2):

seccomp(SECCOMP_SET_MODE_FILTER, flags, &filter)

ambapo &filter - hii ni pointer kwa muundo ambao tayari unajulikana kwetu struct sock_fprog, i.e. Mpango wa BPF.

Mipango ya seccomp inatofautianaje na programu za soketi? Muktadha unaosambazwa. Kwa upande wa soketi, tulipewa eneo la kumbukumbu lililo na pakiti, na kwa upande wa seccomp tulipewa muundo kama.

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

Hapa nr ni nambari ya simu ya mfumo itakayozinduliwa, arch - usanifu wa sasa (zaidi juu ya hii hapa chini), args - hadi hoja sita za wito wa mfumo, na instruction_pointer ni kielekezi kwa maagizo ya nafasi ya mtumiaji ambayo yalipiga simu kwenye mfumo. Kwa hivyo, kwa mfano, kupakia nambari ya simu ya mfumo kwenye rejista A inabidi tuseme

ldw [0]

Kuna vipengele vingine vya programu za seccomp, kwa mfano, muktadha unaweza kupatikana tu kwa usawazishaji wa 32-bit na huwezi kupakia nusu ya neno au byte - unapojaribu kupakia kichujio. ldh [0] simu ya mfumo seccomp itarudi EINVAL. Chaguo la kukokotoa hukagua vichujio vilivyopakiwa seccomp_check_filter() kokwa. (Jambo la kuchekesha ni kwamba, katika ahadi ya asili iliyoongeza utendakazi wa seccomp, walisahau kuongeza ruhusa ya kutumia maagizo kwa kazi hii. mod (salio la mgawanyiko) na sasa haipatikani kwa programu za BPF za seccomp, tangu kuongezwa kwake itavunjika ABI.)

Kimsingi, tayari tunajua kila kitu cha kuandika na kusoma programu za seccomp. Kawaida mantiki ya programu hupangwa kama orodha nyeupe au nyeusi ya simu za mfumo, kwa mfano programu

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

hukagua orodha nyeusi ya simu nne za mfumo zilizo na nambari 304, 176, 239, 279. Je, simu hizi za mfumo ni zipi? Hatuwezi kusema kwa uhakika, kwa kuwa hatujui ni kwa ajili ya usanifu gani mpango huo uliandikwa. Kwa hiyo, waandishi wa seccomp kutoa Anzisha programu zote na ukaguzi wa usanifu (usanifu wa sasa umeonyeshwa katika muktadha kama uwanja arch muundo struct seccomp_data) Kwa usanifu ulioangaliwa, mwanzo wa mfano ungeonekana kama:

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

na kisha nambari za simu za mfumo wetu zitapata thamani fulani.

Tunaandika na kupakia vichungi kwa kutumia seccomp libseccomp

Kuandika vichungi katika msimbo asilia au katika mkusanyiko wa BPF hukuruhusu kuwa na udhibiti kamili juu ya matokeo, lakini wakati huo huo, wakati mwingine ni vyema kuwa na msimbo unaobebeka na/au kusomeka. Maktaba itatusaidia na hili libsecomp, ambayo hutoa kiolesura cha kawaida cha kuandika vichujio vyeusi au vyeupe.

Wacha, kwa mfano, tuandike programu inayoendesha faili ya binary ya chaguo la mtumiaji, baada ya kusanikisha hapo awali orodha nyeusi ya simu za mfumo kutoka. makala hapo juu (mpango umerahisishwa kwa usomaji zaidi, toleo kamili linaweza kupatikana hapa):

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

Kwanza tunafafanua safu sys_numbers ya 40+ nambari za simu za mfumo ili kuzuia. Kisha, anzisha muktadha ctx na iambie maktaba kile tunachotaka kuruhusu (SCMP_ACT_ALLOW) simu zote za mfumo kwa chaguo-msingi (ni rahisi kuunda orodha zisizoruhusiwa). Kisha, moja kwa moja, tunaongeza simu zote za mfumo kutoka kwa orodha nyeusi. Kwa kujibu simu ya mfumo kutoka kwenye orodha, tunaomba SCMP_ACT_TRAP, katika kesi hii seccomp itatuma ishara kwa mchakato SIGSYS na maelezo ya simu ya mfumo gani ilikiuka sheria. Hatimaye, tunapakia programu kwenye kernel kwa kutumia seccomp_load, ambayo itakusanya programu na kuiunganisha kwa mchakato kwa kutumia simu ya mfumo seccomp(2).

Kwa mkusanyiko uliofanikiwa, programu lazima iunganishwe na maktaba libseccomp, kwa mfano:

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

Mfano wa uzinduzi uliofanikiwa:

$ ./seccomp_lib echo ok
ok

Mfano wa simu iliyozuiwa ya mfumo:

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

Tunatumia stracekwa maelezo:

$ 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

tunawezaje kujua kuwa programu ilikatishwa kwa sababu ya matumizi ya simu ya mfumo haramu mount(2).

Kwa hiyo, tuliandika kichujio kwa kutumia maktaba libseccomp, kufaa msimbo usio wa maana katika mistari minne. Katika mfano hapo juu, ikiwa kuna idadi kubwa ya simu za mfumo, wakati wa utekelezaji unaweza kupunguzwa sana, kwani hundi ni orodha tu ya kulinganisha. Kwa uboreshaji, libseccomp hivi majuzi kiraka pamoja, ambayo huongeza usaidizi kwa sifa ya kichungi SCMP_FLTATR_CTL_OPTIMIZE. Kuweka sifa hii kuwa 2 kutabadilisha kichujio kuwa mpango wa utafutaji wa binary.

Ikiwa unataka kuona jinsi vichujio vya utafutaji wa binary hufanya kazi, angalia hati rahisi, ambayo hutoa programu kama hizi katika mkusanyiko wa BPF kwa kupiga nambari za simu za mfumo, kwa mfano:

$ 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

Haiwezekani kuandika chochote haraka sana, kwani programu za BPF haziwezi kufanya kuruka kwa indentation (hatuwezi kufanya, kwa mfano, jmp A au jmp [label+X]) na kwa hivyo mabadiliko yote ni tuli.

seccomp na strace

Kila mtu anajua matumizi strace ni zana ya lazima ya kusoma tabia ya michakato kwenye Linux. Hata hivyo, wengi pia wamesikia kuhusu masuala ya utendaji wakati wa kutumia huduma hii. Ukweli ni kwamba strace kutekelezwa kwa kutumia ptrace(2), na katika utaratibu huu hatuwezi kutaja kwa seti gani ya simu za mfumo tunahitaji kusimamisha mchakato, yaani, kwa mfano, amri.

$ 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

huchakatwa kwa takriban wakati mmoja, ingawa katika kesi ya pili tunataka kufuatilia simu moja tu ya mfumo.

Chaguo jipya --seccomp-bpf, imeongezwa kwa strace toleo la 5.3, hukuruhusu kuharakisha mchakato mara nyingi na wakati wa kuanza chini ya ufuatiliaji wa simu moja ya mfumo tayari inalinganishwa na wakati wa kuanza kwa kawaida:

$ 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

(Hapa, bila shaka, kuna udanganyifu mdogo kwa kuwa hatufuatilii wito mkuu wa mfumo wa amri hii. Ikiwa tulikuwa tukifuatilia, kwa mfano, newfsstat, Basi strace ingevunja breki ngumu kama bila --seccomp-bpf.)

Je, chaguo hili linafanya kazi vipi? Bila yeye strace inaunganisha kwenye mchakato na kuanza kuitumia PTRACE_SYSCALL. Mchakato unaosimamiwa unapotoa simu (yoyote) ya mfumo, udhibiti huhamishiwa strace, ambayo huangalia hoja za simu ya mfumo na kuiendesha kwa kutumia PTRACE_SYSCALL. Baada ya muda fulani, mchakato unakamilisha simu ya mfumo na wakati wa kuiondoa, udhibiti huhamishwa tena strace, ambayo huangalia maadili ya kurudi na kuanza mchakato kwa kutumia PTRACE_SYSCALL, Nakadhalika.

BPF kwa watoto wadogo, sehemu ya sifuri: BPF ya kawaida

Kwa seccomp, hata hivyo, mchakato huu unaweza kuboreshwa kama tunavyotaka. Yaani, ikiwa tunataka kuangalia tu simu ya mfumo X, basi tunaweza kuandika kichujio cha BPF ambacho kwa X inarudisha thamani SECCOMP_RET_TRACE, na kwa simu ambazo hazina faida kwetu - SECCOMP_RET_ALLOW:

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

Katika kesi hii strace awali huanza mchakato kama PTRACE_CONT, kichujio chetu kinachakatwa kwa kila simu ya mfumo, ikiwa simu ya mfumo sivyo X, basi mchakato unaendelea kukimbia, lakini ikiwa hii X, kisha seccomp itahamisha udhibiti straceambayo itaangalia hoja na kuanza mchakato kama PTRACE_SYSCALL (kwa kuwa seccomp haina uwezo wa kuendesha programu wakati wa kutoka kwa simu ya mfumo). Simu ya mfumo inaporudi, strace itaanza upya mchakato kwa kutumia PTRACE_CONT na itasubiri ujumbe mpya kutoka kwa seccomp.

BPF kwa watoto wadogo, sehemu ya sifuri: BPF ya kawaida

Wakati wa kutumia chaguo --seccomp-bpf kuna vikwazo viwili. Kwanza, haitawezekana kujiunga na mchakato uliopo tayari (chaguo -p mipango strace), kwani hii haihimiliwi na seccomp. Pili, hakuna uwezekano hakuna angalia michakato ya watoto, kwani vichungi vya seccomp vinarithiwa na michakato yote ya watoto bila uwezo wa kuzima hii.

Maelezo kidogo zaidi juu ya jinsi gani hasa strace fanya kazi na seccomp inaweza kupatikana kutoka ripoti ya hivi karibuni. Kwa sisi, ukweli wa kuvutia zaidi ni kwamba BPF ya kawaida inayowakilishwa na seccomp bado inatumika leo.

xt_bpf

Hebu sasa turudi kwenye ulimwengu wa mitandao.

Asili: muda mrefu uliopita, mnamo 2007, msingi ulikuwa imeongezwa moduli xt_u32 kwa netfilter. Iliandikwa kwa mlinganisho na darasa la zamani zaidi la trafiki cls_u32 na kukuruhusu kuandika sheria za kiholela za binary kwa iptables kwa kutumia shughuli rahisi zifuatazo: pakia bits 32 kutoka kwa mfuko na ufanyie seti ya shughuli za hesabu juu yao. Kwa mfano,

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

Hupakia biti 32 za kichwa cha IP, kuanzia pedi 6, na kuvitumia barakoa. 0xFF (chukua baiti ya chini). Uwanja huu protocol Kichwa cha IP na tunakilinganisha na 1 (ICMP). Unaweza kuchanganya hundi nyingi katika sheria moja, na unaweza pia kutekeleza operator @ - sogeza baiti za X kulia. Kwa mfano, kanuni

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

huangalia kama Nambari ya Mfuatano wa TCP si sawa 0x29. Sitaingia kwa maelezo zaidi, kwa kuwa tayari ni wazi kwamba kuandika sheria hizo kwa mkono sio rahisi sana. Katika makala BPF - bytecode iliyosahaulika, kuna viungo kadhaa vilivyo na mifano ya matumizi na utengenezaji wa sheria kwa xt_u32. Tazama pia viungo mwishoni mwa nakala hii.

Tangu 2013 moduli badala ya moduli xt_u32 unaweza kutumia moduli ya msingi ya BPF xt_bpf. Mtu yeyote ambaye amesoma hadi sasa anapaswa kuwa wazi juu ya kanuni ya uendeshaji wake: endesha BPF bytecode kama sheria za iptables. Unaweza kuunda sheria mpya, kwa mfano, kama hii:

iptables -A INPUT -m bpf --bytecode <Π±Π°ΠΉΡ‚ΠΊΠΎΠ΄> -j LOG

hapa <Π±Π°ΠΉΡ‚ΠΊΠΎΠ΄> - hii ndio nambari katika umbizo la pato la mkusanyiko bpf_asm kwa chaguo-msingi, kwa mfano,

$ 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

Katika mfano huu tunachuja pakiti zote za UDP. Muktadha wa programu ya BPF katika moduli xt_bpf, bila shaka, inaelekeza kwenye data ya pakiti, katika kesi ya iptables, hadi mwanzo wa kichwa cha IPv4. Rejesha thamani kutoka kwa mpango wa BPF booleanAmbapo false ina maana pakiti haikulingana.

Ni wazi kwamba moduli xt_bpf inasaidia vichujio ngumu zaidi kuliko mfano hapo juu. Wacha tuangalie mifano halisi kutoka Cloudfare. Hadi hivi majuzi walitumia moduli xt_bpf kulinda dhidi ya mashambulizi ya DDoS. Katika makala Tunakuletea Vyombo vya BPF wanaelezea jinsi (na kwa nini) wanavyozalisha vichungi vya BPF na kuchapisha viungo kwa seti ya huduma za kuunda vichungi kama hivyo. Kwa mfano, kutumia matumizi bpfgen unaweza kuunda programu ya BPF inayolingana na hoja ya DNS ya jina 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

Katika programu tunapakia kwanza kwenye rejista X mwanzo wa anwani ya mstari x04habrx03comx00 ndani ya datagram ya UDP na kisha angalia ombi: 0x04686162 <-> "x04hab" nk

Baadaye kidogo, Cloudfare ilichapisha p0f -> msimbo wa mkusanyaji wa BPF. Katika makala Tunakuletea kikusanyaji cha p0f BPF wanazungumza juu ya p0f ni nini na jinsi ya kubadilisha saini za p0f kuwa 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,
...

Kwa sasa haitumii tena Cloudfare xt_bpf, kwa kuwa walihamia XDP - moja ya chaguzi za kutumia toleo jipya la BPF, ona. L4Drop: Mapunguzo ya XDP DDoS.

cls_bpf

Mfano wa mwisho wa kutumia BPF ya kawaida kwenye kernel ni kiainishaji cls_bpf kwa mfumo mdogo wa udhibiti wa trafiki katika Linux, ulioongezwa kwa Linux mwishoni mwa 2013 na kuchukua nafasi ya zamani. cls_u32.

Walakini, hatutaelezea kazi hiyo sasa cls_bpf, kwa kuwa kutoka kwa mtazamo wa ujuzi kuhusu BPF ya classic hii haitatupa chochote - tayari tumefahamu utendaji wote. Kwa kuongezea, katika makala zinazofuata zinazozungumzia BPF Iliyoongezwa, tutakutana na kiainishaji hiki zaidi ya mara moja.

Sababu nyingine ya kutozungumza juu ya kutumia BPF ya kawaida c cls_bpf Shida ni kwamba, ikilinganishwa na BPF Iliyoongezwa, wigo wa utumiaji katika kesi hii umepunguzwa sana: programu za classical haziwezi kubadilisha yaliyomo kwenye vifurushi na haziwezi kuokoa hali kati ya simu.

Kwa hivyo ni wakati wa kusema kwaheri kwa BPF ya kawaida na kutazama siku zijazo.

Kwaheri kwa BPF ya kawaida

Tuliangalia jinsi teknolojia ya BPF, iliyotengenezwa mapema miaka ya tisini, iliishi kwa mafanikio kwa robo ya karne na hadi mwisho ilipata maombi mapya. Walakini, sawa na mpito kutoka kwa mashine za stack hadi RISC, ambayo ilitumika kama msukumo kwa maendeleo ya BPF ya kawaida, katika miaka ya 32 kulikuwa na mpito kutoka kwa mashine 64-bit hadi XNUMX-bit na BPF ya kawaida ilianza kuwa ya kizamani. Kwa kuongeza, uwezo wa BPF ya classic ni mdogo sana, na kwa kuongeza usanifu wa kizamani - hatuna uwezo wa kuokoa hali kati ya simu kwa programu za BPF, hakuna uwezekano wa mwingiliano wa mtumiaji wa moja kwa moja, hakuna uwezekano wa kuingiliana. na kernel, isipokuwa kwa kusoma idadi ndogo ya sehemu za muundo sk_buff na kuzindua kazi rahisi zaidi za msaidizi, huwezi kubadilisha yaliyomo kwenye pakiti na kuzielekeza.

Kwa kweli, kwa sasa yote yaliyosalia ya BPF ya kawaida katika Linux ni kiolesura cha API, na ndani ya kernel programu zote za kawaida, iwe vichungi vya soketi au vichungi vya seccomp, hutafsiriwa kiotomati katika muundo mpya, BPF Iliyoongezwa. (Tutazungumza juu ya jinsi hii inavyotokea katika makala inayofuata.)

Mpito kwa usanifu mpya ulianza mwaka wa 2013, wakati Alexey Starovoitov alipendekeza mpango wa sasisho wa BPF. Mwaka 2014 patches sambamba ilianza kuonekana katika msingi. Kwa kadiri ninavyoelewa, mpango wa awali ulikuwa tu kuboresha usanifu na mkusanyaji wa JIT ili kufanya kazi kwa ufanisi zaidi kwenye mashine 64-bit, lakini badala yake uboreshaji huu uliashiria mwanzo wa sura mpya katika ukuzaji wa Linux.

Nakala zaidi katika mfululizo huu zitashughulikia usanifu na matumizi ya teknolojia mpya, ambayo hapo awali ilijulikana kama BPF ya ndani, kisha BPF iliyopanuliwa, na sasa BPF kwa urahisi.

marejeo

  1. Steven McCanne na Van Jacobson, "Kichujio cha Pakiti ya BSD: Usanifu Mpya wa Kukamata Pakiti za kiwango cha Mtumiaji", https://www.tcpdump.org/papers/bpf-usenix93.pdf
  2. Steven McCanne, "libpcap: Mbinu ya Usanifu na Uboreshaji kwa Kukamata Pakiti", https://sharkfestus.wireshark.org/sharkfest.11/presentations/McCanne-Sharkfest'11_Keynote_Address.pdf
  3. tcpdump, libpcap: https://www.tcpdump.org/
  4. Mafunzo ya Mechi ya IPtable U32.
  5. BPF - bytecode iliyosahaulika: https://blog.cloudflare.com/bpf-the-forgotten-bytecode/
  6. Kuanzisha Zana ya BPF: https://blog.cloudflare.com/introducing-the-bpf-tools/
  7. bpf_cls: http://man7.org/linux/man-pages/man8/tc-bpf.8.html
  8. Muhtasari wa sehemu ndogo: https://lwn.net/Articles/656307/
  9. https://github.com/torvalds/linux/blob/master/Documentation/userspace-api/seccomp_filter.rst
  10. habr: Vyombo na usalama: seccomp
  11. habr: Kutenga daemoni na systemd au "hauitaji Docker kwa hili!"
  12. Paul Chaignon, "strace --seccomp-bpf: kuangalia chini ya kofia", https://fosdem.org/2020/schedule/event/debugging_strace_bpf/
  13. netsniff-ng: http://netsniff-ng.org/

Chanzo: mapenzi.com

Kuongeza maoni