BPF airson an fheadhainn bheaga, pàirt neoni: BPF clasaigeach

Tha Berkeley Packet Filters (BPF) na theicneòlas kernel Linux a tha air a bhith air duilleagan aghaidh foillseachaidhean teignigeach Beurla airson grunn bhliadhnaichean a-nis. Tha co-labhairtean air an lìonadh le aithisgean air cleachdadh agus leasachadh BPF. Bidh Dàibhidh Mac a’ Mhuilleir, neach-gleidhidh fo-shiostam lìonra Linux, a’ gairm an òraid aige aig Linux Plumbers 2018 “Chan ann mu dheidhinn XDP a tha an òraid seo” (Is e XDP aon chùis cleachdaidh airson BPF). Bheir Brendan Gregg seachad òraidean leis an tiotal Sàr-chumhachdan Linux BPF. Toke Høiland-Jørgensen gàiregu bheil an kernel a-nis na microkernel. Tha Tòmas Graf a’ brosnachadh a’ bheachd gu bheil Tha BPF na javascript airson an kernel.

Chan eil cunntas eagarach ann fhathast air BPF air Habré, agus mar sin ann an sreath de artaigilean feuchaidh mi ri bruidhinn mu eachdraidh an teicneòlais, cunntas a thoirt air na h-innealan ailtireachd agus leasachaidh, agus cunntas a thoirt air raointean tagraidh agus cleachdaidh cleachdadh BPF. Tha an artaigil seo, neoni, san t-sreath, ag innse eachdraidh agus ailtireachd clasaigeach BPF, agus cuideachd a 'nochdadh dìomhaireachd a phrionnsabalan obrachaidh. tcpdump, seccomp, strace, agus mòran a bharrachd.

Tha leasachadh BPF fo smachd coimhearsnachd lìonraidh Linux, tha na prìomh thagraidhean BPF a th’ ann mar-thà co-cheangailte ri lìonraidhean agus mar sin, le cead @eucariot, Dh'ainmich mi an t-sreath "BPF airson an fheadhainn bheaga", mar urram don t-sreath sgoinneil "Lìonra airson an fheadhainn bheaga".

Cùrsa goirid ann an eachdraidh BPFc)

Tha teicneòlas BPF ùr-nodha na dhreach leasaichte agus leudaichte den t-seann theicneòlas leis an aon ainm, ris an canar a-nis clasaigeach BPF gus troimh-chèile a sheachnadh. Chaidh goireas ainmeil a chruthachadh stèidhichte air an BPF clasaigeach tcpdump, innleachd seccomp, a bharrachd air modalan nach eil cho aithnichte xt_bpf airson iptables agus seòrsaiche cls_bpf. Ann an Linux an latha an-diugh, bidh prògraman clasaigeach BPF air an eadar-theangachadh gu fèin-ghluasadach chun fhoirm ùr, ge-tà, bho shealladh neach-cleachdaidh, tha an API air fuireach na àite agus tha cleachdaidhean ùra airson BPF clasaigeach, mar a chì sinn san artaigil seo, fhathast gan lorg. Air an adhbhar seo, agus cuideachd air sgàth an dèidh eachdraidh leasachadh BPF clasaigeach ann an Linux, bidh e nas soilleire ciamar agus carson a thàinig e gu bhith na chruth ùr-nodha, chuir mi romham tòiseachadh le artaigil mu BPF clasaigeach.

Aig deireadh na h-ochdadan den linn mu dheireadh, dh'fhàs innleadairean bhon obair-lann ainmeil Lawrence Berkeley ùidh anns a 'cheist mu mar a bu chòir pacaidean lìonra a shìoladh gu ceart air bathar-cruaidh a bha ùr-nodha aig deireadh na h-ochdadan den linn mu dheireadh. B’ e am beachd bunaiteach airson sìoladh, a chaidh a chuir an gnìomh an toiseach ann an teicneòlas CSPF (CMU / Stanford Packet Filter), pacaidean neo-riatanach a shìoladh cho tràth ‘s a ghabhas, i.e. ann an àite kernel, leis gu bheil seo a’ seachnadh copaidh de dhàta neo-riatanach a-steach don àite luchd-cleachdaidh. Gus tèarainteachd ùine ruith a thoirt seachad airson còd cleachdaiche a ruith ann an àite kernel, chaidh inneal brìgheil bogsa gainmhich a chleachdadh.

Ach, chaidh na h-innealan brìgheil airson sìoltachain gnàthaichte a dhealbhadh gus ruith air innealan stèidhichte air stac agus cha do ruith iad cho èifeachdach air innealan RISC nas ùire. Mar thoradh air an sin, tro oidhirpean innleadairean bho Berkeley Labs, chaidh teicneòlas ùr BPF (Berkeley Packet Filters) a leasachadh, agus chaidh an ailtireachd inneal brìgheil a dhealbhadh stèidhichte air pròiseasar Motorola 6502 - an t-each-obrach de thoraidhean ainmeil mar Apple II no NES. Mheudaich an inneal brìgheil ùr coileanadh sìoltachain deichean thursan an taca ris na fuasglaidhean a th ’ann.

BPF ailtireachd inneal

Gheibh sinn eòlas air ailtireachd ann an dòigh obrach, a’ mion-sgrùdadh eisimpleirean. Ach, an toiseach, canaidh sinn gu robh dà chlàr 32-bit aig an inneal a bha ruigsinneach don neach-cleachdaidh, neach-cruinneachaidh A agus clàr-amais X, 64 bytes cuimhne (16 faclan), ri fhaighinn airson sgrìobhadh agus leughadh às deidh sin, agus siostam beag òrdughan airson obrachadh leis na nithean sin. Bha stiùireadh leum airson abairtean cumhach a chuir an gnìomh rim faighinn anns na prògraman cuideachd, ach gus dèanamh cinnteach gum biodh am prògram air a chrìochnachadh ann an deagh àm, cha b’ urrainnear ach geansaidhean a thoirt air adhart, ie, gu sònraichte, bha e toirmisgte lùban a chruthachadh.

Tha an sgeama coitcheann airson an inneal a thòiseachadh mar a leanas. Bidh an neach-cleachdaidh a 'cruthachadh prògram airson ailtireachd BPF agus, a' cleachdadh cuid inneal kernel (leithid gairm siostam), a’ luchdachadh agus a’ ceangal a’ phrògraim ri ri cuid gu gineadair an tachartais anns an kernel (mar eisimpleir, is e tachartas teachd an ath phacaid air a’ chairt lìonra). Nuair a thachras tachartas, bidh an kernel a’ ruith a’ phrògram (mar eisimpleir, ann an eadar-theangair), agus tha cuimhne an inneil a’ freagairt ri ri cuid sgìre cuimhne kernel (mar eisimpleir, dàta pacaid a tha a’ tighinn a-steach).

Bidh na tha gu h-àrd gu leòr airson gun tòisich sinn a’ coimhead air eisimpleirean: gheibh sinn eòlas air an t-siostam agus cruth àithne mar a dh’ fheumar. Ma tha thu airson sgrùdadh a dhèanamh sa bhad air siostam àithne inneal brìgheil agus ionnsachadh mu na comasan aige, faodaidh tu an artaigil tùsail a leughadh An criathrag pacaid BSD agus/no a’ chiad leth dhen fhaidhle Sgrìobhainnean/lìonradh/filter.txt bho na sgrìobhainnean kernel. A bharrachd air an sin, faodaidh tu sgrùdadh a dhèanamh air an taisbeanadh libpcap: Modh Ailtireachd agus Optimization airson Glacadh Pacaid, anns a bheil McCanne, aon de na h-ùghdaran aig BPF, a 'bruidhinn air eachdraidh a' chruthachaidh libpcap.

Bidh sinn a-nis a’ gluasad air adhart gus beachdachadh air na h-eisimpleirean cudromach uile de bhith a’ cleachdadh BPF clasaigeach air Linux: tcpdump (libpcap), seccomp, xt_bpf, cls_bpf.

tcpdump

Chaidh leasachadh BPF a dhèanamh aig an aon àm ri leasachadh an aghaidh airson sìoladh pacaidean - goireas ainmeil tcpdump. Agus, leis gur e seo an eisimpleir as sine agus as ainmeil de chleachdadh clasaigeach BPF, a tha ri fhaighinn air iomadh siostam obrachaidh, tòisichidh sinn ar sgrùdadh air an teicneòlas leis.

(Ruith mi na h-eisimpleirean gu lèir san artaigil seo air Linux 5.6.0-rc6. Chaidh toradh cuid de dh’ àitheantan a dheasachadh airson a leughadh nas fheàrr.)

Eisimpleir: a 'coimhead air pacaidean IPv6

Smaoinich gu bheil sinn airson coimhead air a h-uile pasgan IPv6 air eadar-aghaidh eth0. Gus seo a dhèanamh is urrainn dhuinn am prògram a ruith tcpdump le sìoltachan sìmplidh ip6:

$ sudo tcpdump -i eth0 ip6

Mar sin, tcpdump a’ cur ri chèile an criathrag ip6 a-steach do bytecode ailtireachd BPF agus cuir chun kernel e (faic mion-fhiosrachadh san roinn Tcpdump: luchdachadh). Thèid an criathrag luchdaichte a ruith airson gach pacaid a thèid tron ​​​​eadar-aghaidh eth0. Ma thilleas an criathrag luach neo-neoni n, an uairsin suas gu n thèid bytes den phacaid a chopaigeadh gu àite luchd-cleachdaidh agus chì sinn e san toradh tcpdump.

BPF airson an fheadhainn bheaga, pàirt neoni: BPF clasaigeach

Tha e a ’tionndadh a-mach gun urrainn dhuinn faighinn a-mach gu furasta dè an còd byte a chaidh a chuir chun kernel tcpdump le cuideachadh bho na tcpdump, ma ruitheas sinn e leis an roghainn -d:

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

Air loidhne neoni bidh sinn a’ ruith an àithne ldh [12], a tha a’ ciallachadh “load into register A leth-fhacal (16 pìosan) suidhichte aig seòladh 12” agus is e an aon cheist dè an seòrsa cuimhne air a bheil sinn a’ dèiligeadh? Is e am freagairt gu bheil aig x a ’tòiseachadh (x+1)th byte den phacaid lìonra sgrùdaichte. Leugh sinn pacaidean bhon eadar-aghaidh Ethernet eth0, agus seo ciallachadhgu bheil coltas mar seo air a’ phacaid (airson sìmplidh, tha sinn a’ gabhail ris nach eil tagaichean VLAN anns a’ phacaid):

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

Mar sin às deidh an àithne a chuir an gnìomh ldh [12] anns a’ chlàr A bidh achadh ann Ether Type - an seòrsa pacaid a thèid a ghluasad san fhrèam Ethernet seo. Air loidhne 1 bidh sinn a 'dèanamh coimeas eadar susbaint a' chlàir A (seòrsa pacaid) c 0x86dd, agus seo agus tha Is e IPv6 an seòrsa anns a bheil ùidh againn. Air loidhne 1, a bharrachd air an àithne coimeas, tha dà cholbh eile ann - jt 2 и jf 3 - comharran air am feum thu a dhol ma tha an coimeas soirbheachail (A == 0x86dd) agus neo-shoirbheachail. Mar sin, ann an cùis shoirbheachail (IPv6) thèid sinn gu loidhne 2, agus ann an cùis neo-shoirbheachail - gu loidhne 3. Air loidhne 3 thig am prògram gu crìch le còd 0 (na dèan lethbhreac den phacaid), air loidhne 2 thig am prògram gu crìch le còd 262144 (lethbhreac dhomh pasgan 256 kilobytes aig a’ char as àirde).

Eisimpleir nas iom-fhillte: bidh sinn a 'coimhead air pacaidean TCP a rèir port ceann-uidhe

Feuch gum faic sinn cò ris a tha sìoltachan coltach a bhios a’ dèanamh lethbhreac de na pacaidean TCP gu lèir le port ceann-uidhe 666. Beachdaichidh sinn air a’ chùis IPv4, leis gu bheil a’ chùis IPv6 nas sìmplidhe. Às deidh dhut an eisimpleir seo a sgrùdadh, faodaidh tu sgrùdadh a dhèanamh air sìoltachan IPv6 thu fhèin mar eacarsaich (ip6 and tcp dst port 666) agus criathrag airson a’ chùis choitcheann (tcp dst port 666). Mar sin, tha an sìoltachan anns a bheil ùidh againn a’ coimhead mar seo:

$ 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

Tha fios againn mu thràth dè a bhios loidhnichean 0 agus 1 a’ dèanamh. Air loidhne 2 tha sinn air dearbhadh mar-thà gur e pacaid IPv4 a tha seo (Ether Type = 0x800) agus luchdaich e a-steach don chlàr A 24mh byte den phacaid. Tha am pasgan againn coltach

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

Tha sin a’ ciallachadh gun luchdaich sinn a-steach don chlàr A an raon Protocol den cheann-cinn IP, a tha loidsigeach, oir tha sinn airson dìreach pacaidean TCP a chopaigeadh. Bidh sinn a’ dèanamh coimeas eadar Protocol le 0x6 (IPPROTO_TCP) air loidhne 3.

Air loidhnichean 4 agus 5 bidh sinn a’ luchdachadh na leth-fhaclan a tha suidhichte aig seòladh 20 agus a’ cleachdadh an àithne jset thoir sùil a bheil aon de na trì air a shuidheachadh brataichean - a’ caitheamh am masg a chaidh a thoirt seachad jset tha na trì pìosan as cudromaiche air am fuadach. Tha dhà de na trì pìosan ag innse dhuinn a bheil am pacaid mar phàirt de phasgan IP sgapte, agus ma tha, an e am criomag mu dheireadh a th’ ann. Tha an treas pìos glèidhte agus feumaidh e a bhith neoni. Chan eil sinn airson sùil a thoirt air pacaidean neo-choileanta no briste, agus mar sin bheir sinn sùil air na trì pìosan.

Is e Loidhne 6 an fheadhainn as inntinniche san liostadh seo. Briseadh ldxb 4*([14]&0xf) a 'ciallachadh gu bheil sinn a' luchdachadh a-steach don chlàr X na ceithir pìosan as lugha cudromach den chòigeamh byte deug den phacaid air iomadachadh le 4. 'S e an raon na ceithir pìosan as lugha dhen chòigeamh byte deug san raon Fad bann-cinn eadar-lìn Ceann-cinn IPv4, a bhios a 'stòradh fad a' chinn ann am faclan, agus mar sin feumaidh tu iomadachadh le 4. Gu inntinneach, tha an abairt 4*([14]&0xf) a tha na shònrachadh airson sgeama seòlaidh sònraichte nach gabh a chleachdadh ach san fhoirm seo agus dìreach airson clàr X, i.e. chan urrainn dhuinn a ràdh idir ldb 4*([14]&0xf) chan eil ldxb 5*([14]&0xf) (chan urrainn dhuinn ach co-chothromachadh eadar-dhealaichte a shònrachadh, mar eisimpleir, ldxb 4*([16]&0xf)). Tha e soilleir gun deach an sgeama seòlaidh seo a chur ri BPF dìreach airson faighinn X (clàr clàr-amais) Fad bann IPv4.

Mar sin air loidhne 7 bidh sinn a’ feuchainn ri leth facal a luchdachadh aig (X+16). A’ cuimhneachadh gu bheil bann-cinn Ethernet ann an 14 bytes, agus X anns a bheil fad bann-cinn IPv4, tha sinn a’ tuigsinn sin ann an A Tha port ceann-uidhe TCP air a luchdachadh:

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

Mu dheireadh, air loidhne 8 bidh sinn a 'dèanamh coimeas eadar a' phort ceann-uidhe leis an luach a tha a dhìth agus air loidhnichean 9 no 10 bidh sinn a 'tilleadh an toradh - am bu chòir dhuinn lethbhreac a dhèanamh den phacaid no nach eil.

Tcpdump: luchdachadh

Anns na h-eisimpleirean roimhe, cha robh sinn gu sònraichte a’ gabhail còmhnaidh gu mionaideach air mar a bhios sinn a’ luchdachadh bytecode BPF a-steach don kernel airson sìoladh pacaidean. San fharsaingeachd, tcpdump air a ghluasad gu iomadh siostam agus airson obrachadh le sìoltachain tcpdump cleachdadh an leabharlainn libpcap. Ann an ùine ghoirid, gus criathrag a chuir air eadar-aghaidh a ’cleachdadh libpcap, feumaidh tu na leanas a dhèanamh:

Gus faicinn mar a tha an gnìomh pcap_setfilter air a chuir an gnìomh ann an Linux, bidh sinn a’ cleachdadh strace (Chaidh cuid de loidhnichean a thoirt air falbh):

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

Air a 'chiad dà shreath de thoraidhean bidh sinn a' cruthachadh socaid amh gus a h-uile frèam Ethernet a leughadh agus a cheangal ris an eadar-aghaidh eth0S an Iar- De na a’ chiad eisimpleir againn tha fios againn gu bheil an criathar ip bidh ceithir stiùiridhean BPF ann, agus air an treas loidhne chì sinn mar a chleachdas tu an roghainn SO_ATTACH_FILTER call siostam setsockopt bidh sinn a' luchdachadh agus a' ceangal criathrag de dh'fhaid 4. Seo an criathar againn.

Is fhiach a bhith mothachail, ann am BPF clasaigeach, gu bheil luchdachadh agus ceangal sìoltachan an-còmhnaidh a ’tachairt mar ghnìomhachd atamach, agus anns an dreach ùr de BPF, bidh am prògram a’ luchdachadh agus ga cheangal ri gineadair an tachartais air an sgaradh ann an ùine.

Fìrinn Falaichte

Tha dreach beagan nas coileanta den toradh a’ coimhead mar seo:

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

Mar a chaidh ainmeachadh gu h-àrd, bidh sinn a’ luchdachadh agus a’ ceangal ar criathrag ris an t-socaid air loidhne 5, ach dè thachras air loidhnichean 3 agus 4? Tha e a 'tionndadh a-mach gu bheil seo libpcap a’ toirt aire dhuinn - gus nach bi toradh ar criathrag a’ toirt a-steach pacaidean nach eil ga shàsachadh, an leabharlann a' ceangal criathrag dummy ret #0 (leig às a h-uile pacaid), ag atharrachadh an t-socaid gu modh neo-bacadh agus a’ feuchainn ris a h-uile pacaid a dh’ fhaodadh fuireach bho na sìoltachain a bh’ ann roimhe a thoirt air falbh.

Gu h-iomlan, gus pacaidean a shìoladh air Linux a’ cleachdadh BPF clasaigeach, feumaidh criathrag a bhith agad ann an cruth structar mar struct sock_fprog agus socaid fosgailte, às deidh sin faodar an sìoltachan a cheangal ris an t-socaid a’ cleachdadh gairm siostaim setsockopt.

Gu inntinneach, faodar an sìoltachan a cheangal ri socaid sam bith, chan e dìreach amh. Seo eisimpleir prògram a ghearras dheth a h-uile càil ach a’ chiad dà byte bho gach datagram UDP a tha a’ tighinn a-steach. (Chuir mi beachdan a-steach sa chòd gus nach cuir thu dragh air an artaigil.)

Tuilleadh fiosrachaidh mu chleachdadh setsockopt airson sìoltachain a cheangal, faic socaid(7), ach mu bhith a’ sgrìobhadh na sìoltachain agad fhèin mar struct sock_fprog gun chuideachadh tcpdump labhraidh sinn anns an earrainn Prògramachadh BPF le ar làmhan fhèin.

BPF clasaigeach agus an XNUMXmh linn

Chaidh BPF a thoirt a-steach do Linux ann an 1997 agus tha e air a bhith na neach-obrach airson ùine mhòr libpcap gun atharrachaidhean sònraichte sam bith (atharrachaidhean sònraichte Linux, gu dearbh, bha e, ach cha do dh'atharraich iad an dealbh cruinne). Thàinig a’ chiad droch shoidhnichean gun atharraicheadh ​​BPF ann an 2011, nuair a mhol Eric Dumazet paiste, a chuireas Just In Time Compiler ris an kernel - eadar-theangair airson còd byte BPF a thionndadh gu dùthchasach x86_64 còd.

B’ e compiler JIT a’ chiad fhear anns an t-sreath atharrachaidhean: ann an 2012 nochdadh comas sìoltachain a sgrìobhadh airson seccomp, a’ cleachdadh BPF, san Fhaoilleach 2013 bha air a chur ris modal xt_bpf, a leigeas leat riaghailtean a sgrìobhadh airson iptables le cuideachadh bho BPF, agus san Dàmhair 2013 bha air a chur ris cuideachd modal cls_bpf, a leigeas leat luchd-seòrsachaidh trafaic a sgrìobhadh a’ cleachdadh BPF.

Bheir sinn sùil nas mionaidiche air na h-eisimpleirean sin uile a dh’ aithghearr, ach an toiseach bidh e feumail dhuinn ionnsachadh mar a sgrìobhas sinn agus a chuireas ri chèile prògraman neo-riaghailteach airson BPF, leis gu bheil na comasan a thug an leabharlann seachad libpcap cuibhrichte (eisimpleir shìmplidh: sìoltachan air a chruthachadh libpcap chan urrainn dhaibh ach dà luach a thilleadh - 0 no 0x40000) no san fharsaingeachd, mar ann an cùis seccomp, chan eil iad iomchaidh.

Prògramachadh BPF le ar làmhan fhèin

Leig leinn eòlas fhaighinn air cruth binary stiùireadh BPF, tha e gu math sìmplidh:

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

Tha 64 pìosan anns gach stiùireadh, anns a bheil a’ chiad 16 pìosan mar an còd stiùiridh, agus an uairsin tha dà indent ochd-bit, jt и jf, agus 32 pìosan airson na h-argamaid K, aig a bheil an adhbhar ag atharrachadh bho àithne gu àithne. Mar eisimpleir, an àithne ret, a tha a 'crìochnachadh a' phrògram tha an còd 6, agus tha an luach tilleadh air a thoirt bhon sheasmhach K. Ann an C, tha aon stiùireadh BPF air a riochdachadh mar structar

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

agus tha am prògram gu lèir ann an cruth structair

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

Mar sin, is urrainn dhuinn prògraman a sgrìobhadh mu thràth (mar eisimpleir, tha fios againn air na còdan stiùiridh bho [1]). Seo cò ris a bhios an criathrag coltach ip6 bho a’ chiad eisimpleir againn:

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

prògram prog faodaidh sinn a chleachdadh gu laghail ann an gairm

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

Chan eil e gu math goireasach prògraman a sgrìobhadh ann an cruth còdan inneal, ach uaireannan tha feum air (mar eisimpleir, airson debugging, cruthachadh deuchainnean aonad, sgrìobhadh artaigilean air Habré, msaa). Airson goireasachd, anns an fhaidhle <linux/filter.h> tha macros cuideachaidh air am mìneachadh - dh’ fhaodadh an aon eisimpleir gu h-àrd a bhith air ath-sgrìobhadh mar

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

Ach, chan eil an roghainn seo gu math goireasach. Is e seo a bha na prògramadairean kernel Linux a’ reusanachadh, agus mar sin san eòlaire tools/bpf kernels gheibh thu assembler agus debugger airson a bhith ag obair le clasaigeach BPF.

Tha cànan cruinneachaidh glè choltach ri toradh deasbaid tcpdump, ach a bharrachd air sin is urrainn dhuinn bileagan samhlachail a shònrachadh. Mar eisimpleir, seo prògram a tha a’ fàgail a h-uile pacaid ach a-mhàin TCP/IPv4:

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

Gu gnàthach, bidh an assembler a’ gineadh còd san cruth <количество инструкций>,<code1> <jt1> <jf1> <k1>,..., mar eisimpleir againn le TCP bidh e

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

Airson goireasachd luchd-prògramaidh C, faodar cruth toraidh eadar-dhealaichte a chleachdadh:

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

Faodar an teacsa seo a chopaigeadh a-steach don mhìneachadh seòrsa structar struct sock_filter, mar a rinn sinn aig toiseach na h-earrainn so.

Leudachain Linux agus netsniff-ng

A bharrachd air BPF àbhaisteach, Linux agus tools/bpf/bpf_asm taic agus seata neo-àbhaisteach. Gu bunaiteach, thathas a’ cleachdadh stiùireadh gus faighinn gu raointean structar struct sk_buff, a tha a 'toirt cunntas air pasgan lìonra anns an kernel. Ach, tha seòrsachan eile de stiùiridhean cuideachaidh ann cuideachd, mar eisimpleir ldw cpu thèid a luchdachadh a-steach don chlàr A mar thoradh air a bhith a’ ruith gnìomh kernel raw_smp_processor_id(). (Anns an dreach ùr de BPF, chaidh na leudachaidhean neo-àbhaisteach seo a leudachadh gus prògraman a sholarachadh le seata de luchd-cuideachaidh kernel airson faighinn gu cuimhne, structaran, agus tachartasan gineadh.) Seo eisimpleir inntinneach de shìoltachan anns nach bi sinn a’ dèanamh lethbhreac ach den cinn pacaid a-steach don àite luchd-cleachdaidh a’ cleachdadh an leudachadh poff, cosgais pàighidh:

ld poff
ret a

Chan urrainnear leudachaidhean BPF a chleachdadh ann an tcpdump, ach is e adhbhar math a tha seo airson eòlas fhaighinn air a’ phasgan goireis netsniff-ng, anns a bheil, am measg rudan eile, prògram adhartach netsniff-ng, anns a bheil, a bharrachd air sìoladh le bhith a’ cleachdadh BPF, gineadair trafaic èifeachdach, agus nas adhartaiche na tools/bpf/bpf_asm, neach-cruinneachaidh BPF ris an canar bpfc. Tha sgrìobhainnean gu math mionaideach anns a’ phacaid, faic cuideachd na ceanglaichean aig deireadh an artaigil.

seccomp

Mar sin, tha fios againn mu thràth mar a sgrìobhas sinn prògraman BPF de iom-fhillteachd neo-riaghailteach agus tha sinn deiseil airson coimhead air eisimpleirean ùra, is e a’ chiad fhear an teicneòlas seccomp, a leigeas le bhith a ’cleachdadh sìoltachain BPF, an seata agus an seata de argamaidean gairm siostam a tha rim faighinn a riaghladh. pròiseas sònraichte agus a shliochd.

Chaidh a’ chiad dreach de seccomp a chur ris an kernel ann an 2005 agus cha robh fèill mhòr air, leis nach tug e seachad ach aon roghainn - gus an t-seata de ghairmean siostam a tha rim faighinn don phròiseas a chuingealachadh gu na leanas: read, write, exit и sigreturn, agus chaidh am pròiseas a bhriseas na riaghailtean a mharbhadh a’ cleachdadh SIGKILL. Ach, ann an 2012, chuir seccomp ris a’ chomas sìoltachain BPF a chleachdadh, a’ toirt cothrom dhut seata de ghlaidhean siostam ceadaichte a mhìneachadh agus eadhon sgrùdaidhean a dhèanamh air na h-argamaidean aca. (Gu h-inntinneach, b ’e Chrome aon de na ciad luchd-cleachdaidh a’ ghnìomhachd seo, agus tha muinntir Chrome an-dràsta a ’leasachadh uidheamachd KRSI stèidhichte air dreach ùr de BPF agus a’ ceadachadh Modalan Tèarainteachd Linux a ghnàthachadh.) Gheibhear ceanglaichean gu sgrìobhainnean a bharrachd aig an deireadh den artaigil.

Thoir an aire gu bheil artaigilean air a bhith air a’ mheadhan mu thràth mu bhith a’ cleachdadh seccomp, is dòcha gum bi cuideigin airson an leughadh mus (no an àite) na fo-roinnean a leanas a leughadh. Anns an artaigil Soithichean agus tèarainteachd: seccomp a’ toirt seachad eisimpleirean de bhith a’ cleachdadh seccomp, an dà chuid an dreach 2007 agus an dreach a’ cleachdadh BPF (tha sìoltachain air an gineadh a’ cleachdadh libseccomp), a’ bruidhinn air ceangal seccomp ri Docker, agus cuideachd a’ toirt seachad mòran cheanglaichean feumail. Anns an artaigil A’ dealachadh deamhan le systemd no “chan fheum thu Docker airson seo!” Tha e a’ còmhdach, gu sònraichte, mar a chuireas tu liostaichean dubha no liostaichean geala de ghairmean siostaim airson daemons a’ ruith systemd.

An uairsin chì sinn mar a sgrìobhas tu agus a luchdaicheas tu sìoltachain airson seccomp ann an lom C agus a’ cleachdadh an leabharlainn libseccomp agus dè na buannachdan agus na mì-bhuannachdan a tha aig gach roghainn, agus mu dheireadh, chì sinn mar a bhios am prògram a’ cleachdadh seccomp strace.

A 'sgrìobhadh agus a' luchdachadh sìoltachain airson seccomp

Tha fios againn mu thràth mar a sgrìobhas sinn prògraman BPF, mar sin leig dhuinn sùil an-toiseach air an eadar-aghaidh prògramadh seccomp. Faodaidh tu criathrag a shuidheachadh aig ìre a 'phròiseis, agus sealbhaichidh a h-uile pròiseas pàiste na cuingeadan. Tha seo air a dhèanamh le bhith a 'cleachdadh siostam gairm seccomp(2):

seccomp(SECCOMP_SET_MODE_FILTER, flags, &filter)

far a bheil &filter - tha seo na chomharradh air structar air a bheil sinn eòlach mu thràth struct sock_fprog, i.e. Prògram BPF saor an asgaidh.

Ciamar a tha prògraman airson seccomp eadar-dhealaichte bho phrògraman airson socaidean? Co-theacsa air a ghluasad. A thaobh socaidean, chaidh àite cuimhne a thoirt dhuinn anns an robh am pacaid, agus a thaobh seccomp fhuair sinn structar mar

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

tha e nr an e àireamh gairm an t-siostaim a thèid a chuir air bhog, arch - ailtireachd gnàthach (barrachd air seo gu h-ìosal), args - suas ri sia argamaidean gairm siostam, agus instruction_pointer na chomharra air stiùireadh àite an neach-cleachdaidh a thug air an t-siostam gairm. Mar sin, mar eisimpleir, gus àireamh gairm an t-siostaim a luchdachadh a-steach don chlàr A feumaidh sinn a ràdh

ldw [0]

Tha feartan eile ann airson prògraman seccomp, mar eisimpleir, chan fhaighear a-steach don cho-theacsa ach le co-thaobhadh 32-bit agus chan urrainn dhut leth-fhacal no byte a luchdachadh - nuair a tha thu a’ feuchainn ri criathrag a luchdachadh ldh [0] call siostam seccomp tillidh EINVAL. Bidh an gnìomh a’ sgrùdadh na sìoltachain luchdaichte seccomp_check_filter() kernels. (Is e an rud èibhinn, anns a’ ghealladh thùsail a chuir ris a’ ghnìomhachd seccomp, dhìochuimhnich iad cead a chuir ris an stiùireadh a chleachdadh don ghnìomh seo mod (an còrr den roinn) agus chan eil e a-nis ri fhaighinn airson prògraman seccomp BPF, bho chaidh a chur ris brisidh ABI.)

Gu bunaiteach, tha fios againn mu thràth air a h-uile dad airson prògraman seccomp a sgrìobhadh agus a leughadh. Mar as trice tha loidsig a’ phrògraim air a rèiteachadh mar liosta geal no dubh de ghlaodhan siostaim, mar eisimpleir am prògram

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

a' sgrùdadh liosta dhubh de cheithir fiosan siostam leis an àireamh 304, 176, 239, 279. Dè na gairmean siostam seo? Chan urrainn dhuinn a ràdh le cinnt, leis nach eil fios againn dè an ailtireachd a chaidh am prògram a sgrìobhadh. Mar sin, tha ùghdaran seccomp tairgse tòisich a h-uile prògram le sgrùdadh ailtireachd (tha an ailtireachd gnàthach air a chomharrachadh sa cho-theacsa mar raon arch an structar struct seccomp_data). Le sgrùdadh air an ailtireachd, bhiodh toiseach an eisimpleir coltach:

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

agus an uairsin gheibheadh ​​​​na h-àireamhan gairm siostam againn luachan sònraichte.

Bidh sinn a’ sgrìobhadh agus a’ luchdachadh sìoltachain airson cleachdadh seccomp libseccomp

Le bhith a’ sgrìobhadh sìoltachain ann an còd dùthchasach no ann an co-chruinneachadh BPF leigidh sin dhut làn smachd a bhith agad air an toradh, ach aig an aon àm, uaireannan tha e nas fheàrr còd so-ghiùlain agus / no leughaidh a bhith agad. Cuidichidh an leabharlann sinn le seo libseccomp, a bheir seachad eadar-aghaidh àbhaisteach airson sìoltachain dubh no geal a sgrìobhadh.

Sgrìobhamaid, mar eisimpleir, prògram a ruitheas faidhle binary de roghainn an neach-cleachdaidh, às deidh dhuinn liosta dhubh de ghlaodhan siostam a chuir a-steach roimhe seo bho an artaigil gu h-àrd (tha am prògram air a dhèanamh nas sìmplidhe airson barrachd leughaidh, gheibhear an dreach slàn an seo):

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

An toiseach tha sinn a 'mìneachadh an t-sreath sys_numbers de 40+ àireamhan gairm siostam ri bhacadh. An uairsin, tòisich a 'cho-theacsa ctx agus innis don leabharlann na tha sinn airson a cheadachadh (SCMP_ACT_ALLOW) a h-uile gairm siostam gu bunaiteach (tha e nas fhasa liostaichean dubha a thogail). An uairsin, aon às deidh aon, cuiridh sinn a h-uile fios siostam bhon liosta dhubh. Mar fhreagairt do ghairm siostam bhon liosta, bidh sinn ag iarraidh SCMP_ACT_TRAP, anns a 'chùis seo cuiridh seccomp comharra chun a' phròiseas SIGSYS le tuairisgeul air an t-siostam gairm a bhris na riaghailtean. Mu dheireadh, bidh sinn a 'luchdachadh a' phrògram a-steach don kernel a 'cleachdadh seccomp_load, a chuireas ri chèile am prògram agus a cheanglas e ris a 'phròiseas a' cleachdadh gairm siostam seccomp(2).

Airson cruinneachadh soirbheachail, feumaidh am prògram a bhith ceangailte ris an leabharlann libseccompmar eisimpleir:

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

Eisimpleir de chur air bhog soirbheachail:

$ ./seccomp_lib echo ok
ok

Eisimpleir de ghairm siostam dùinte:

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

Cleachd straceairson mion-fhiosrachadh:

$ 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

ciamar a bhios fios againn gun deach am prògram a thoirt gu crìch air sgàth cleachdadh gairm siostam mì-laghail mount(2).

Mar sin, sgrìobh sinn criathrag a’ cleachdadh an leabharlainn libseccomp, a’ cur còd neo-thrioblaideach ann an ceithir loidhnichean. Anns an eisimpleir gu h-àrd, ma tha àireamh mhòr de shiostaman fiosan, faodar an ùine cur gu bàs a lùghdachadh gu mòr, oir chan eil anns an t-seic ach liosta de choimeasan. Airson optimization, bha aig libseccomp o chionn ghoirid paiste air a thoirt a-steach, a chuireas taic ris a’ bhuadh sìoltachain SCMP_FLTATR_CTL_OPTIMIZE. Ma chuireas tu am feart seo gu 2 tionndaidhidh sin an criathrag gu bhith na phrògram sgrùdaidh dà-chànanach.

Ma tha thu airson faicinn mar a tha sìoltachain sgrùdaidh dà-chànanach ag obair, thoir sùil air sgriobt sìmplidh, a bhios a’ gineadh a leithid de phrògraman ann an cruinniche BPF le bhith a’ dial àireamhan gairm siostam, mar eisimpleir:

$ 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

Tha e do-dhèanta dad a sgrìobhadh gu math nas luaithe, leis nach urrainn dha prògraman BPF leuman indentation a dhèanamh (chan urrainn dhuinn, mar eisimpleir, a dhèanamh. jmp A no jmp [label+X]) agus mar sin tha na h-eadar-ghluasadan uile seasmhach.

seccomp agus strì

Tha fios aig a h-uile duine air a’ ghoireas strace na inneal riatanach airson sgrùdadh a dhèanamh air giùlan phròiseasan air Linux. Ach, tha mòran air cluinntinn mu dheidhinn cuideachd cùisean coileanaidh nuair a bhios tu a’ cleachdadh a’ ghoireas seo. Is e an fhìrinn gu bheil strace cur an gnìomh a’ cleachdadh ptrace(2), agus anns an uidheamachd seo chan urrainn dhuinn sònrachadh aig an t-seata de shiostaman a dh’ fheumas sinn stad a chuir air a’ phròiseas, i.e., mar eisimpleir, òrdughan

$ 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

air an giullachd timcheall air an aon àm, ged anns an dàrna cùis tha sinn airson dìreach aon ghairm siostam a lorg.

Roghainn ùr --seccomp-bpf, air a chur ri strace dreach 5.3, a’ leigeil leat am pròiseas a luathachadh iomadh uair agus tha an ùine tòiseachaidh fo lorg aon ghairm siostam mar-thà an coimeas ri àm tòiseachaidh cunbhalach:

$ 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

(An seo, gu dearbh, tha beagan mealladh ann leis nach eil sinn a’ lorg prìomh ghairm siostam na h-àithne seo. Nam biodh sinn a’ lorg, mar eisimpleir, newfsstat, an uairsin strace bhiodh e a' briseadh a cheart cho cruaidh 's as aonais --seccomp-bpf.)

Ciamar a tha an roghainn seo ag obair? Sin i strace ceangail ris a’ phròiseas agus tòisichidh e ga chleachdadh PTRACE_SYSCALL. Nuair a bhios pròiseas fo stiùir a’ toirt a-mach gairm siostam (sam bith), thèid smachd a ghluasad gu strace, a tha a 'coimhead air argamaidean a' ghairm siostam agus ga ruith le PTRACE_SYSCALL. Às deidh beagan ùine, bidh am pròiseas a ’crìochnachadh gairm an t-siostaim agus nuair a dh’ fhàgas e e, thèid smachd a ghluasad a-rithist strace, a tha a 'coimhead air na luachan tilleadh agus a' tòiseachadh air a 'phròiseas a' cleachdadh PTRACE_SYSCALL, Agus mar sin air adhart.

BPF airson an fheadhainn bheaga, pàirt neoni: BPF clasaigeach

Le seccomp, ge-tà, faodar am pròiseas seo a mheudachadh dìreach mar a bu mhath leinn. Is e sin, ma tha sinn airson coimhead a-mhàin air gairm an t-siostaim X, an uairsin is urrainn dhuinn sìoltachan BPF a sgrìobhadh airson sin X a’ tilleadh luach SECCOMP_RET_TRACE, agus air son ghairmean nach 'eil suim againn- SECCOMP_RET_ALLOW:

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

Anns a ’chùis seo strace an toiseach a 'tòiseachadh air a' phròiseas mar PTRACE_CONT, tha an sìoltachan againn air a phròiseasadh airson gach gairm siostam, mura h-eil gairm an t-siostaim X, an uairsin tha am pròiseas a 'leantainn air adhart, ach ma tha seo X, an uairsin gluaisidh seccomp smachd stracea bheir sùil air na h-argamaidean agus a thòisicheas am pròiseas mar PTRACE_SYSCALL (leis nach eil comas aig seccomp prògram a ruith nuair a thig thu a-mach à gairm siostaim). Nuair a thilleas gairm an t-siostaim, strace ath-thòisichear am pròiseas a’ cleachdadh PTRACE_CONT agus feitheamh ri teachdaireachdan ùra bho seccomp.

BPF airson an fheadhainn bheaga, pàirt neoni: BPF clasaigeach

Nuair a bhios tu a 'cleachdadh an roghainn --seccomp-bpf tha dà bhacadh ann. An toiseach, cha bhith e comasach a dhol còmhla ri pròiseas a tha ann mu thràth (roghainn -p prògraman strace), leis nach eil seo a’ faighinn taic bho seccomp. San dàrna àite, chan eil comas ann chan eil thoir sùil air pròiseasan cloinne, leis gu bheil sìoltachain seccomp air an sealbhachadh leis a h-uile pròiseas cloinne gun chomas seo a chuir dheth.

Beagan nas mionaidiche air mar a tha e ceart strace ag obair le seccomp gheibhear bho aithisg o chionn ghoirid. Dhuinne, is e an fhìrinn as inntinniche gu bheil am BPF clasaigeach air a riochdachadh le seccomp fhathast air a chleachdadh an-diugh.

xt_bpf

Rachamaid a-nis air ais gu saoghal nan lìonraidhean.

Cùl-fhiosrachadh: o chionn fhada, ann an 2007, bha an cridhe air a chur ris modal xt_u32 Air sgàth airson netfilter. Chaidh a sgrìobhadh le samhlachas le seòrsaiche trafaic eadhon nas sine cls_u32 agus leig e leat riaghailtean binary neo-riaghailteach a sgrìobhadh airson iptables a’ cleachdadh na h-obraichean sìmplidh a leanas: luchdaich 32 pìosan bho phasgan agus dèan seata de ghnìomhachd àireamhachd orra. Mar eisimpleir,

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

A’ luchdachadh na pìosan 32 den cheann-cinn IP, a’ tòiseachadh aig pleadhag 6, agus a’ cur masg orra 0xFF (gabh am byte ìosal). An raon seo protocol Ceann-cinn IP agus bidh sinn ga choimeas le 1 (ICMP). Faodaidh tu iomadh seic a chur còmhla ann an aon riaghailt, agus faodaidh tu cuideachd an gnìomhaiche a chuir gu bàs @ — gluais X bytes air an taobh dheas. Mar eisimpleir, an riaghailt

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

thoir sùil mura h-eil Àireamh Seicheamh TCP co-ionann 0x29. Cha tèid mi a-steach gu mion-fhiosrachadh nas fhaide, oir tha e soilleir mar-thà nach eil e glè ghoireasach na riaghailtean sin a sgrìobhadh le làimh. Anns an artaigil BPF - am bytecode a dhìochuimhnich, tha grunn cheanglaichean ann le eisimpleirean de chleachdadh agus gineadh riaghailtean airson xt_u32. Faic cuideachd na ceanglaichean aig deireadh an artaigil seo.

Bho 2013 modal an àite modal xt_u32 faodaidh tu modal stèidhichte air BPF a chleachdadh xt_bpf. Bu chòir do dhuine sam bith a leugh cho fada seo a bhith soilleir mu phrionnsapal na h-obrach aige: ruith BPF bytecode mar riaghailtean iptables. Faodaidh tu riaghailt ùr a chruthachadh, mar eisimpleir, mar seo:

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

an seo <байткод> - is e seo an còd ann an cruth toraidh assembler bpf_asm gu bunaiteach, mar eisimpleir,

$ 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

San eisimpleir seo tha sinn a’ sìoladh a h-uile pacaid UDP. Co-theacsa airson prògram BPF ann am modal xt_bpf, gu dearbh, a’ comharrachadh dàta a’ phacaid, a thaobh iptables, gu toiseach bann-cinn IPv4. Thoir air ais luach bho phrògram BPF booleancàite false a’ ciallachadh nach robh am paca a’ freagairt.

Tha e soilleir gu bheil am modal xt_bpf a’ toirt taic do shìoltachain nas iom-fhillte na an eisimpleir gu h-àrd. Bheir sinn sùil air fìor eisimpleirean bho Cloudfare. Gu ruige o chionn ghoirid chleachd iad am modal xt_bpf gus dìon an aghaidh ionnsaighean DDoS. Anns an artaigil A’ toirt a-steach Innealan BPF bidh iad a’ mìneachadh ciamar (agus carson) a bhios iad a’ gineadh sìoltachain BPF agus a’ foillseachadh cheanglaichean gu seata de ghoireasan airson a leithid de shìoltachain a chruthachadh. Mar eisimpleir, a 'cleachdadh an goireas bpfgen faodaidh tu prògram BPF a chruthachadh a fhreagras ri ceist DNS airson ainm 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

Anns a 'phrògram bidh sinn an toiseach a' luchdachadh a-steach don chlàr X seòladh tòiseachaidh loidhne x04habrx03comx00 taobh a-staigh datagram UDP agus an uairsin thoir sùil air an iarrtas: 0x04686162 <-> "x04hab" agus mar sin air adhart.

Beagan nas fhaide air adhart, dh'fhoillsich Cloudfare an còd cruinneachaidh p0f -> BPF. Anns an artaigil A’ toirt a-steach an compiler p0f BPF bidh iad a’ bruidhinn air dè a th’ ann am p0f agus mar as urrainn dhut ainmean-sgrìobhte p0f a thionndadh gu 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,
...

Chan eil an-dràsta a’ cleachdadh Cloudfare tuilleadh xt_bpf, bhon a ghluais iad gu XDP - aon de na roghainnean airson a bhith a 'cleachdadh an dreach ùr de BPF, faic. L4Drop: lasachaidhean XDP DDoS.

cls_bpf

Is e an eisimpleir mu dheireadh de bhith a’ cleachdadh BPF clasaigeach anns an kernel an seòrsaiche cls_bpf airson an fho-shiostam smachd trafaic ann an Linux, air a chur ri Linux aig deireadh 2013 agus a’ dol an àite an t-seann cls_u32.

Ach, cha toir sinn cunntas air an obair a-nis cls_bpf, oir bho thaobh an eòlais air BPF clasaigeach cha toir seo dad dhuinn - tha sinn mu thràth air fàs eòlach air a h-uile gnìomh. A bharrachd air an sin, ann an artaigilean às deidh sin a ’bruidhinn mu dheidhinn BPF Leudaichte, coinnichidh sinn ris an seòrsaiche seo barrachd air aon uair.

Adhbhar eile gun a bhith a’ bruidhinn mu bhith a’ cleachdadh clasaigeach BPF c cls_bpf Is e an duilgheadas a th’ ann, an taca ri BPF Leudaichte, gu bheil farsaingeachd iomchaidheachd sa chùis seo air a lughdachadh gu mòr: chan urrainn dha prògraman clasaigeach na tha ann am pasganan atharrachadh agus chan urrainn dhaibh staid eadar fiosan a shàbhaladh.

Mar sin tha an t-àm ann soraidh slàn le BPF clasaigeach agus coimhead ris an àm ri teachd.

Soraidh le BPF clasaigeach

Choimhead sinn air mar a bha teicneòlas BPF, a chaidh a leasachadh tràth anns na naochadan, beò gu soirbheachail airson cairteal linn agus gus an deireadh lorg sinn tagraidhean ùra. Ach, coltach ris a’ ghluasad bho innealan stac gu RISC, a bha na bhrosnachadh airson leasachadh BPF clasaigeach, anns na 32n bha gluasad bho innealan 64-bit gu XNUMX-bit agus thòisich BPF clasaigeach a’ dol à bith. A bharrachd air an sin, tha comasan BPF clasaigeach glè chuingealaichte, agus a bharrachd air an ailtireachd seann-fhasanta - chan eil comas againn staid a shàbhaladh eadar fiosan gu prògraman BPF, chan eil comas ann eadar-obrachadh dìreach le luchd-cleachdaidh, chan eil comas ann eadar-obrachadh. leis an kernel, ach a-mhàin leughadh àireamh chuingealaichte de raointean structar sk_buff agus a’ cur air bhog na gnìomhan cuideachaidh as sìmplidh, chan urrainn dhut susbaint pacaidean atharrachadh agus an ath-stiùireadh.

Gu dearbh, an-dràsta chan eil air fhàgail den BPF clasaigeach ann an Linux an eadar-aghaidh API, agus taobh a-staigh an kernel tha a h-uile prògram clasaigeach, ge bith an e sìoltachain socaid no sìoltachain seccomp, air an eadar-theangachadh gu fèin-ghluasadach gu cruth ùr, Extended BPF. (Bruidhnidh sinn mu dheidhinn dìreach mar a thachras seo san ath artaigil.)

Thòisich an gluasad gu ailtireachd ùr ann an 2013, nuair a mhol Alexei Starovoytov sgeama ùrachaidh BPF. Ann an 2014 na pìosan co-fhreagarrach thòisich e ri nochdadh anns a' chridhe. Cho fad ‘s a tha mi a’ tuigsinn, cha robh anns a ’chiad phlana ach an ailtireachd agus an inneal-cruinneachaidh JIT a bharrachadh gus ruith nas èifeachdaiche air innealan 64-bit, ach an àite sin chomharraich na optimizations sin toiseach caibideil ùr ann an leasachadh Linux.

Còmhdaichidh artaigilean eile san t-sreath seo ailtireachd agus cleachdadh an teicneòlais ùr, ris an canar an toiseach BPF a-staigh, an uairsin leudaich BPF, agus a-nis dìreach BPF.

iomraidhean

  1. Steven McCanne agus Van Jacobson, "The BSD Packet Filter: Ailtireachd Ùr airson Glacadh Pacaid aig ìre cleachdaiche", https://www.tcpdump.org/papers/bpf-usenix93.pdf
  2. Steven McCanne, "libpcap: Ailtireachd agus Dòigh-obrach Optimization airson Glacadh Pacaid", https://sharkfestus.wireshark.org/sharkfest.11/presentations/McCanne-Sharkfest'11_Keynote_Address.pdf
  3. tcpdump, libpcap: https://www.tcpdump.org/
  4. Oideachadh maidsidh IPtable U32.
  5. BPF - an còd byte a dhìochuimhnich: https://blog.cloudflare.com/bpf-the-forgotten-bytecode/
  6. A’ toirt a-steach an inneal BPF: https://blog.cloudflare.com/introducing-the-bpf-tools/
  7. bpf_cls: http://man7.org/linux/man-pages/man8/tc-bpf.8.html
  8. Sealladh farsaing air seccomp: https://lwn.net/Articles/656307/
  9. https://github.com/torvalds/linux/blob/master/Documentation/userspace-api/seccomp_filter.rst
  10. habr: Gabhadairean agus tèarainteachd: seccomp
  11. habr: A’ dealachadh deamhan le systemd no “chan fheum thu Docker airson seo!”
  12. Paul Chaignon, "strace --seccomp-bpf: sùil fon chochall", https://fosdem.org/2020/schedule/event/debugging_strace_bpf/
  13. netsniff-ng: http://netsniff-ng.org/

Source: www.habr.com

Cuir beachd ann