BPF ar gyfer y rhai bach, rhan sero: BPF clasurol

Mae Berkeley Packet Filters (BPF) yn dechnoleg cnewyllyn Linux sydd wedi bod ar dudalennau blaen cyhoeddiadau technoleg Saesneg ers sawl blwyddyn bellach. Mae cynadleddau'n cael eu llenwi ag adroddiadau ar ddefnyddio a datblygu BPF. Mae David Miller, cynhaliwr is-system rhwydwaith Linux, yn galw ei sgwrs yn Linux Plumbers 2018 “Nid yw’r sgwrs hon am XDP” (Mae XDP yn un achos defnydd ar gyfer BPF). Mae Brendan Gregg yn rhoi sgyrsiau o dan y teitl Archbwerau BPF Linux. Toke Høiland-Jørgensen chwerthinbod y cnewyllyn yn awr yn microkernel. Mae Thomas Graf yn hyrwyddo'r syniad bod Mae BPF yn javascript ar gyfer y cnewyllyn.

Nid oes disgrifiad systematig o BPF ar Habré o hyd, ac felly mewn cyfres o erthyglau byddaf yn ceisio siarad am hanes y dechnoleg, disgrifio'r offer pensaernïaeth a datblygu, ac amlinellu meysydd cymhwyso ac ymarfer defnyddio BPF. Mae'r erthygl hon, sero, yn y gyfres, yn adrodd hanes a phensaernïaeth BPF clasurol, a hefyd yn datgelu cyfrinachau ei egwyddorion gweithredu. tcpdump, seccomp, strace, a llawer mwy.

Mae datblygiad BPF yn cael ei reoli gan y gymuned rwydweithio Linux, mae prif gymwysiadau presennol BPF yn gysylltiedig â rhwydweithiau ac felly, gyda chaniatâd @eucariot, Galwais y gyfres yn “BPF i’r rhai bach”, er anrhydedd i’r gyfres wych "Rhwydweithiau ar gyfer y rhai bach".

Cwrs byr yn hanes BPF(c)

Mae technoleg BPF fodern yn fersiwn well ac estynedig o'r hen dechnoleg gyda'r un enw, a elwir bellach yn BPF clasurol i osgoi dryswch. Crëwyd cyfleustodau adnabyddus yn seiliedig ar y BPF clasurol tcpdump, mecanwaith seccomp, yn ogystal â modiwlau llai adnabyddus xt_bpf gyfer iptables a dosbarthwr cls_bpf. Yn Linux modern, mae rhaglenni BPF clasurol yn cael eu trosi'n awtomatig i'r ffurf newydd, fodd bynnag, o safbwynt defnyddiwr, mae'r API wedi aros yn ei le ac mae defnyddiau newydd ar gyfer BPF clasurol, fel y gwelwn yn yr erthygl hon, yn dal i gael eu canfod. Am y rheswm hwn, a hefyd oherwydd yn dilyn hanes datblygiad BPF clasurol yn Linux, bydd yn dod yn gliriach sut a pham y datblygodd i'w ffurf fodern, penderfynais ddechrau gydag erthygl am BPF clasurol.

Ar ddiwedd wythdegau'r ganrif ddiwethaf, daeth peirianwyr o'r Labordy Lawrence Berkeley enwog â diddordeb yn y cwestiwn o sut i hidlo pecynnau rhwydwaith yn gywir ar galedwedd a oedd yn fodern ar ddiwedd wythdegau'r ganrif ddiwethaf. Y syniad sylfaenol o hidlo, a weithredwyd yn wreiddiol mewn technoleg CSPF (CMU/Stanford Packet Filter), oedd hidlo pecynnau diangen cyn gynted â phosibl, h.y. yn y gofod cnewyllyn, gan fod hyn yn osgoi copïo data diangen i ofod defnyddwyr. Er mwyn darparu diogelwch amser rhedeg ar gyfer rhedeg cod defnyddiwr yn y gofod cnewyllyn, defnyddiwyd peiriant rhithwir blwch tywod.

Fodd bynnag, cynlluniwyd y peiriannau rhithwir ar gyfer hidlwyr presennol i redeg ar beiriannau stac ac nid oeddent yn rhedeg mor effeithlon ar beiriannau RISC mwy newydd. O ganlyniad, trwy ymdrechion peirianwyr o Berkeley Labs, datblygwyd technoleg BPF (Berkeley Packet Filters) newydd, y dyluniwyd pensaernïaeth y peiriant rhithwir yn seiliedig ar brosesydd Motorola 6502 - ceffyl gwaith cynhyrchion mor adnabyddus â Apple II neu NES. Cynyddodd y peiriant rhithwir newydd berfformiad hidlo ddegau o weithiau o'i gymharu ag atebion presennol.

Pensaernïaeth peiriant BPF

Byddwn yn dod yn gyfarwydd â phensaernïaeth mewn ffordd weithiol, gan ddadansoddi enghreifftiau. Fodd bynnag, i ddechrau, gadewch i ni ddweud bod gan y peiriant ddwy gofrestr 32-did yn hygyrch i'r defnyddiwr, sef cronadur A a chofrestr fynegai X, 64 beit o gof (16 gair), ar gael i'w hysgrifennu a'u darllen wedyn, a system fach o orchmynion ar gyfer gweithio gyda'r gwrthrychau hyn. Roedd cyfarwyddiadau naid ar gyfer gweithredu mynegiadau amodol hefyd ar gael yn y rhaglenni, ond i warantu cwblhau’r rhaglen yn amserol, dim ond neidiau ymlaen y gellid eu gwneud, h.y., yn benodol, gwaharddwyd creu dolenni.

Mae'r cynllun cyffredinol ar gyfer cychwyn y peiriant fel a ganlyn. Mae'r defnyddiwr yn creu rhaglen ar gyfer pensaernïaeth BPF a, gan ddefnyddio rhai mecanwaith cnewyllyn (fel galwad system), yn llwytho ac yn cysylltu'r rhaglen i i rai i'r generadur digwyddiad yn y cnewyllyn (er enghraifft, digwyddiad yw dyfodiad y pecyn nesaf ar y cerdyn rhwydwaith). Pan fydd digwyddiad yn digwydd, mae'r cnewyllyn yn rhedeg y rhaglen (er enghraifft, mewn cyfieithydd), ac mae cof y peiriant yn cyfateb i i rai rhanbarth cof cnewyllyn (er enghraifft, data pecyn sy'n dod i mewn).

Bydd yr uchod yn ddigon i ni ddechrau edrych ar enghreifftiau: byddwn yn dod yn gyfarwydd â'r fformat system a gorchymyn yn ôl yr angen. Os ydych chi am astudio system orchymyn peiriant rhithwir ar unwaith a dysgu am ei holl alluoedd, yna gallwch chi ddarllen yr erthygl wreiddiol Yr Hidlydd Pecyn BSD a/neu hanner cyntaf y ffeil Dogfennaeth/rhwydweithio/filter.txt o'r ddogfennaeth cnewyllyn. Yn ogystal, gallwch astudio'r cyflwyniad libpcap: Methodoleg Pensaernïaeth ac Optimeiddio ar gyfer Dal Pecyn, lle mae McCanne, un o awduron BPF, yn sôn am hanes y creu libpcap.

Symudwn ymlaen nawr i ystyried yr holl enghreifftiau arwyddocaol o ddefnyddio BPF clasurol ar Linux: tcpdump (libpcap), seccomp, xt_bpf, cls_bpf.

tcpdump

Cynhaliwyd datblygiad BPF ochr yn ochr â datblygiad y blaen ar gyfer hidlo pecynnau - cyfleustodau adnabyddus tcpdump. A chan mai dyma'r enghraifft hynaf ac enwocaf o ddefnyddio BPF clasurol, sydd ar gael ar lawer o systemau gweithredu, byddwn yn dechrau ein hastudiaeth o'r dechnoleg ag ef.

(Rhedais yr holl enghreifftiau yn yr erthygl hon ar Linux 5.6.0-rc6. Mae allbwn rhai gorchmynion wedi'i olygu er mwyn ei gwneud yn haws ei ddarllen.)

Enghraifft: arsylwi pecynnau IPv6

Gadewch i ni ddychmygu ein bod am edrych ar bob pecyn IPv6 ar ryngwyneb eth0. I wneud hyn gallwn redeg y rhaglen tcpdump gyda hidlydd syml ip6:

$ sudo tcpdump -i eth0 ip6

Yn yr achos hwn, tcpdump yn llunio'r hidlydd ip6 i mewn i god byte pensaernïaeth BPF a'i anfon at y cnewyllyn (gweler y manylion yn yr adran Tcpdump: llwytho). Bydd yr hidlydd wedi'i lwytho yn cael ei redeg ar gyfer pob pecyn sy'n mynd trwy'r rhyngwyneb eth0. Os yw'r hidlydd yn dychwelyd gwerth nad yw'n sero n, yna hyd at n bydd beit o'r pecyn yn cael ei gopïo i ofod y defnyddiwr a byddwn yn ei weld yn yr allbwn tcpdump.

BPF ar gyfer y rhai bach, rhan sero: BPF clasurol

Mae'n ymddangos y gallwn ddarganfod yn hawdd pa bytecode a anfonwyd at y cnewyllyn tcpdump gyda chymorth y tcpdump, os ydym yn ei redeg gyda'r opsiwn -d:

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

Ar-lein sero rydym yn rhedeg y gorchymyn ldh [12], sy'n sefyll am “load into register A hanner gair (16 did) wedi’i leoli yng nghyfeiriad 12” a’r unig gwestiwn yw pa fath o atgof yr ydym yn mynd i’r afael ag ef? Yr ateb yw bod yn x yn dechrau (x+1)fed beit o'r pecyn rhwydwaith a ddadansoddwyd. Rydym yn darllen pecynnau o'r rhyngwyneb Ethernet eth0, a hwn golygubod y pecyn yn edrych fel hyn (er mwyn symlrwydd, rydym yn cymryd yn ganiataol nad oes tagiau VLAN yn y pecyn):

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

Felly ar ôl gweithredu'r gorchymyn ldh [12] yn y gofrestr A bydd cae Ether Type - y math o becyn a drosglwyddir yn y ffrâm Ethernet hwn. Ar lein 1 rydym yn cymharu cynnwys y gofrestr A (math o becyn) c 0x86dd, a hwn ac y mae Y math y mae gennym ddiddordeb ynddo yw IPv6. Ar linell 1, yn ogystal â'r gorchymyn cymharu, mae dwy golofn arall - jt 2 и jf 3 — marciau y mae angen i chi fynd atynt os yw'r gymhariaeth yn llwyddiannus (A == 0x86dd) ac yn aflwyddiannus. Felly, mewn achos llwyddiannus (IPv6) rydyn ni'n mynd i linell 2, ac mewn achos aflwyddiannus - i linell 3. Ar linell 3 mae'r rhaglen yn dod i ben gyda chod 0 (peidiwch â chopïo'r pecyn), ar linell 2 mae'r rhaglen yn dod i ben gyda chod 262144 (copïwch uchafswm o becyn 256 kilobytes i mi).

Enghraifft fwy cymhleth: edrychwn ar becynnau TCP fesul porthladd cyrchfan

Gadewch i ni weld sut olwg sydd ar hidlydd sy'n copïo'r holl becynnau TCP gyda phorthladd cyrchfan 666. Byddwn yn ystyried yr achos IPv4, gan fod yr achos IPv6 yn symlach. Ar ôl astudio'r enghraifft hon, gallwch chi archwilio'r hidlydd IPv6 eich hun fel ymarfer (ip6 and tcp dst port 666) a hidlydd ar gyfer yr achos cyffredinol (tcp dst port 666). Felly, mae'r hidlydd y mae gennym ddiddordeb ynddo yn edrych fel hyn:

$ 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

Rydym eisoes yn gwybod beth mae llinellau 0 ac 1 yn ei wneud. Ar linell 2 rydym eisoes wedi gwirio mai pecyn IPv4 yw hwn (Ether Math = 0x800) a'i lwytho i'r gofrestr A 24ain beit o'r pecyn. Mae ein pecyn yn edrych fel

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

sy'n golygu ein bod yn llwytho i mewn i'r gofrestr A maes Protocol y pennawd IP, sy'n rhesymegol, oherwydd rydym am gopïo pecynnau TCP yn unig. Rydym yn cymharu Protocol â 0x6 (IPPROTO_TCP) ar-lein 3.

Ar linellau 4 a 5 rydym yn llwytho'r hanner geiriau yng nghyfeiriad 20 ac yn defnyddio'r gorchymyn jset gwiriwch a yw un o'r tri wedi'i osod baneri - gwisgo'r mwgwd a roddwyd jset mae'r tri rhan mwyaf arwyddocaol yn cael eu clirio. Mae dau o'r tri rhan yn dweud wrthym a yw'r pecyn yn rhan o becyn IP tameidiog, ac os felly, ai dyma'r darn olaf. Mae'r trydydd did wedi'i gadw a rhaid iddo fod yn sero. Nid ydym am wirio pecynnau anghyflawn neu wedi torri, felly rydym yn gwirio pob un o'r tri did.

Llinell 6 yw'r mwyaf diddorol yn y rhestr hon. Mynegiant ldxb 4*([14]&0xf) yn golygu ein bod yn llwytho i mewn i'r gofrestr X y pedwar did lleiaf arwyddocaol o bymthegfed beit y pecyn wedi'i luosi â 4. Pedwar did lleiaf arwyddocaol y pymthegfed beit yw'r maes Hyd Pennawd Rhyngrwyd Pennawd IPv4, sy'n storio hyd y pennawd mewn geiriau, felly mae angen i chi wedyn luosi â 4. Yn ddiddorol, mae'r mynegiant 4*([14]&0xf) yn ddynodiad ar gyfer cynllun cyfeiriadau arbennig y gellir ei ddefnyddio yn y ffurflen hon yn unig ac ar gyfer cofrestr yn unig X, h.y. ni allwn ddweud ychwaith ldb 4*([14]&0xf) nac yn ldxb 5*([14]&0xf) (ni allwn ond nodi gwrthbwyso gwahanol, er enghraifft, ldxb 4*([16]&0xf)). Mae'n amlwg bod y cynllun cyfarch hwn wedi'i ychwanegu at BPF yn union er mwyn ei dderbyn X (cofrestr mynegai) IPv4 hyd pennawd.

Felly ar lein 7 rydym yn ceisio llwytho hanner gair yn (X+16). Cofio bod 14 bytes yn cael eu meddiannu gan y pennawd Ethernet, a X yn cynnwys hyd y pennawd IPv4, rydym yn deall hynny yn A Mae porthladd cyrchfan TCP wedi'i lwytho:

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

Yn olaf, ar linell 8 rydym yn cymharu'r porthladd cyrchfan gyda'r gwerth dymunol ac ar linellau 9 neu 10 rydym yn dychwelyd y canlyniad - p'un ai i gopïo'r pecyn ai peidio.

Tcpdump: llwytho

Yn yr enghreifftiau blaenorol, ni wnaethom yn benodol ystyried yn fanwl sut yn union yr ydym yn llwytho cod byte BPF i'r cnewyllyn ar gyfer hidlo pecynnau. Yn gyffredinol, tcpdump cludo i lawer o systemau ac ar gyfer gweithio gyda ffilterau tcpdump yn defnyddio'r llyfrgell libpcap. Yn fyr, i osod hidlydd ar ryngwyneb gan ddefnyddio libpcap, mae angen i chi wneud y canlynol:

I weld sut mae'r swyddogaeth pcap_setfilter gweithredu yn Linux, rydym yn defnyddio strace (mae rhai llinellau wedi'u tynnu):

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

Ar y ddwy linell gyntaf o allbwn rydym yn creu soced amrwd i ddarllen yr holl fframiau Ethernet a'i rwymo i'r rhyngwyneb eth0... Oddiwrth ein hesiampl gyntaf rydym yn gwybod bod yr hidlydd ip yn cynnwys pedwar cyfarwyddyd BPF, ac ar y drydedd linell gwelwn sut i ddefnyddio'r opsiwn SO_ATTACH_FILTER galwad system setsockopt rydym yn llwytho ac yn cysylltu hidlydd o hyd 4. Dyma ein hidlydd.

Mae'n werth nodi, yn BPF clasurol, bod llwytho a chysylltu hidlydd bob amser yn digwydd fel gweithrediad atomig, ac yn y fersiwn newydd o BPF, mae llwytho'r rhaglen a'i rwymo i'r generadur digwyddiad yn cael eu gwahanu mewn amser.

Gwirionedd Cudd

Mae fersiwn ychydig yn fwy cyflawn o'r allbwn yn edrych fel hyn:

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

Fel y soniwyd uchod, rydym yn llwytho ac yn cysylltu ein hidlydd i'r soced ar linell 5, ond beth sy'n digwydd ar linellau 3 a 4? Mae'n troi allan bod hyn libpcap yn gofalu amdanom - fel nad yw allbwn ein hidl yn cynnwys pecynnau nad ydynt yn ei fodloni, y llyfrgell yn cysylltu ffilter dymi ret #0 (gollyngwch bob pecyn), yn newid y soced i'r modd di-flocio ac yn ceisio tynnu'r holl becynnau a allai aros o'r hidlwyr blaenorol.

Yn gyfan gwbl, i hidlo pecynnau ar Linux gan ddefnyddio BPF clasurol, mae angen i chi gael hidlydd ar ffurf strwythur fel struct sock_fprog a soced agored, ac ar ôl hynny gellir cysylltu'r hidlydd â'r soced gan ddefnyddio galwad system setsockopt.

Yn ddiddorol, gellir cysylltu'r hidlydd ag unrhyw soced, nid dim ond amrwd. Yma enghraifft rhaglen sy'n torri i ffwrdd pob un heblaw'r ddau beit cyntaf o'r holl ddatagramau CDU sy'n dod i mewn. (Ychwanegais sylwadau yn y cod er mwyn peidio ag annibendod yr erthygl.)

Mwy o fanylion am ddefnydd setsockopt am cysylltu ffilter, gw soced(7), ond am ysgrifennu eich hun hidlyddion fel struct sock_fprog heb gymorth tcpdump byddwn yn siarad yn yr adran Rhaglennu BPF gyda'n dwylo ein hunain.

BPF clasurol a'r XNUMXain ganrif

Cafodd BPF ei gynnwys yn Linux yn 1997 ac mae wedi parhau'n geffyl gwaith ers amser maith libpcap heb unrhyw newidiadau arbennig (newidiadau Linux-benodol, wrth gwrs, Roedd, ond ni wnaethant newid y darlun byd-eang). Daeth yr arwyddion difrifol cyntaf y byddai BPF yn esblygu yn 2011, pan gynigiodd Eric Dumazet clwt, sy'n ychwanegu Just In Time Compiler i'r cnewyllyn - cyfieithydd ar gyfer trosi bytecode BPF i frodorol x86_64 côd.

Casglwr JIT oedd y cyntaf yn y gadwyn o newidiadau: yn 2012 ymddangos y gallu i ysgrifennu hidlwyr ar gyfer seccomp, gan ddefnyddio BPF, ym mis Ionawr 2013 roedd wedi adio y modiwl xt_bpf, sy'n eich galluogi i ysgrifennu rheolau ar gyfer iptables gyda chymorth BPF, ac ym mis Hydref 2013 roedd wedi adio hefyd modwl cls_bpf, sy'n eich galluogi i ysgrifennu dosbarthwyr traffig gan ddefnyddio BPF.

Byddwn yn edrych ar yr holl enghreifftiau hyn yn fanylach yn fuan, ond yn gyntaf bydd yn ddefnyddiol i ni ddysgu sut i ysgrifennu a llunio rhaglenni mympwyol ar gyfer BPF, gan fod y galluoedd a ddarperir gan y llyfrgell libpcap cyfyngedig (enghraifft syml: hidlydd wedi'i gynhyrchu libpcap yn gallu dychwelyd dim ond dau werth - 0 neu 0x40000) neu yn gyffredinol, fel yn achos seccomp, nid ydynt yn berthnasol.

Rhaglennu BPF gyda'n dwylo ein hunain

Gadewch i ni ddod yn gyfarwydd â fformat deuaidd cyfarwyddiadau BPF, mae'n syml iawn:

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

Mae pob cyfarwyddyd yn meddiannu 64 did, a'r 16 did cyntaf yw'r cod cyfarwyddyd, yna mae dau fewnoliad wyth did, jt и jf, a 32 did ar gyfer y ddadl K, y mae ei ddiben yn amrywio o orchymyn i orchymyn. Er enghraifft, y gorchymyn ret, sy'n terfynu y rhaglen wedi y cod 6, a chymerir y gwerth dychwelyd o'r cysonyn K. Yn C, cynrychiolir un cyfarwyddyd BPF fel strwythur

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

ac mae'r rhaglen gyfan ar ffurf strwythur

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

Felly, gallwn eisoes ysgrifennu rhaglenni (er enghraifft, rydym yn gwybod y codau cyfarwyddyd gan [1]). Dyma sut olwg fydd ar yr hidlydd ip6 o ein hesiampl gyntaf:

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

rhaglen prog gallwn ei ddefnyddio'n gyfreithiol mewn galwad

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

Nid yw ysgrifennu rhaglenni ar ffurf codau peiriant yn gyfleus iawn, ond weithiau mae'n angenrheidiol (er enghraifft, ar gyfer dadfygio, creu profion uned, ysgrifennu erthyglau ar Habré, ac ati). Er hwylustod, yn y ffeil <linux/filter.h> diffinnir macros helpwr - gellid ailysgrifennu'r un enghraifft ag uchod

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

Fodd bynnag, nid yw'r opsiwn hwn yn gyfleus iawn. Dyma beth oedd y rhaglenwyr cnewyllyn Linux yn ei resymu, ac felly yn y cyfeiriadur tools/bpf cnewyllyn gallwch ddod o hyd i gydosodwr a dadfygiwr ar gyfer gweithio gyda BPF clasurol.

Mae iaith y cynulliad yn debyg iawn i allbwn dadfygio tcpdump, ond yn ogystal gallwn nodi labeli symbolaidd. Er enghraifft, dyma raglen sy'n gollwng pob pecyn ac eithrio TCP/IPv4:

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

Yn ddiofyn, mae'r cydosodwr yn cynhyrchu cod yn y fformat <количество инструкций>,<code1> <jt1> <jf1> <k1>,..., er enghraifft gyda TCP y bydd

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

Er hwylustod rhaglenwyr C, gellir defnyddio fformat allbwn gwahanol:

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

Gellir copïo'r testun hwn i'r diffiniad o strwythur math struct sock_filter, fel y gwnaethom ar ddechrau'r adran hon.

Estyniadau Linux a netsniff-ng

Yn ogystal â BPF safonol, Linux a tools/bpf/bpf_asm cefnogaeth a set ansafonol. Yn y bôn, defnyddir cyfarwyddiadau i gael mynediad i feysydd strwythur struct sk_buff, sy'n disgrifio pecyn rhwydwaith yn y cnewyllyn. Fodd bynnag, mae mathau eraill o gyfarwyddiadau cynorthwywyr hefyd, er enghraifft ldw cpu yn llwytho i mewn i'r gofrestr A canlyniad rhedeg swyddogaeth cnewyllyn raw_smp_processor_id(). (Yn y fersiwn newydd o BPF, mae'r estyniadau ansafonol hyn wedi'u hymestyn i ddarparu set o gynorthwywyr cnewyllyn i raglenni ar gyfer cyrchu cof, strwythurau, a digwyddiadau cynhyrchu.) Dyma enghraifft ddiddorol o hidlydd lle rydym yn copïo'r dim ond y penawdau pecyn i'r gofod defnyddiwr gan ddefnyddio'r estyniad poff, gwrthbwyso llwyth tâl:

ld poff
ret a

Ni ellir defnyddio estyniadau BPF yn tcpdump, ond mae hwn yn rheswm da i ddod yn gyfarwydd â'r pecyn cyfleustodau netsniff-ng, sydd, ymhlith pethau eraill, yn cynnwys rhaglen uwch netsniff-ng, sydd, yn ychwanegol at hidlo gan ddefnyddio BPF, hefyd yn cynnwys generadur traffig effeithiol, ac yn fwy datblygedig na tools/bpf/bpf_asm, galwodd cydosodwr BPF bpfc. Mae'r pecyn yn cynnwys dogfennaeth eithaf manwl, gweler hefyd y dolenni ar ddiwedd yr erthygl.

seccomp

Felly, rydym eisoes yn gwybod sut i ysgrifennu rhaglenni BPF o gymhlethdod mympwyol ac yn barod i edrych ar enghreifftiau newydd, y cyntaf ohonynt yw'r dechnoleg seccomp, sy'n caniatáu, gan ddefnyddio hidlwyr BPF, i reoli'r set a set o ddadleuon galwadau system sydd ar gael i proses benodol a'i ddisgynyddion.

Ychwanegwyd y fersiwn gyntaf o seccomp at y cnewyllyn yn 2005 ac nid oedd yn boblogaidd iawn, gan ei fod yn darparu un opsiwn yn unig - cyfyngu'r set o alwadau system sydd ar gael i broses i'r canlynol: read, write, exit и sigreturn, a lladdwyd y broses a oedd yn torri'r rheolau gan ddefnyddio SIGKILL. Fodd bynnag, yn 2012, ychwanegodd seccomp y gallu i ddefnyddio hidlwyr BPF, sy'n eich galluogi i ddiffinio set o alwadau system a ganiateir a hyd yn oed cynnal gwiriadau ar eu dadleuon. (Yn ddiddorol, Chrome oedd un o ddefnyddwyr cyntaf y swyddogaeth hon, ac mae pobl Chrome ar hyn o bryd yn datblygu mecanwaith KRSI yn seiliedig ar fersiwn newydd o BPF ac yn caniatáu addasu Modiwlau Diogelwch Linux.) Gellir dod o hyd i ddolenni i ddogfennaeth ychwanegol ar y diwedd o'r erthygl.

Sylwch fod erthyglau wedi bod ar y canolbwynt eisoes am ddefnyddio seccomp, efallai y bydd rhywun eisiau eu darllen cyn (neu yn lle) darllen yr is-adrannau canlynol. Yn yr erthygl Cynhwysyddion a diogelwch: seccomp yn darparu enghreifftiau o ddefnyddio seccomp, fersiwn 2007 a'r fersiwn gan ddefnyddio BPF (mae hidlwyr yn cael eu cynhyrchu gan ddefnyddio libseccomp), yn sôn am gysylltiad seccomp â Docker, a hefyd yn darparu llawer o ddolenni defnyddiol. Yn yr erthygl Ynysu daemonau gyda systemd neu “does dim angen Docker ar gyfer hyn!” Mae'n ymdrin, yn benodol, â sut i ychwanegu rhestrau du galwadau system a rhestrau gwyn ar gyfer daemons sy'n rhedeg systemd.

Nesaf byddwn yn gweld sut i ysgrifennu a llwytho hidlwyr ar gyfer seccomp yn C noeth ac yn defnyddio'r llyfrgell libseccomp a beth yw manteision ac anfanteision pob opsiwn, ac yn olaf, gadewch i ni weld sut mae seccomp yn cael ei ddefnyddio gan y rhaglen strace.

Ysgrifennu a llwytho hidlwyr ar gyfer seccomp

Rydym eisoes yn gwybod sut i ysgrifennu rhaglenni BPF, felly gadewch i ni edrych yn gyntaf ar y rhyngwyneb rhaglennu seccomp. Gallwch chi osod hidlydd ar lefel y broses, a bydd pob proses plentyn yn etifeddu'r cyfyngiadau. Gwneir hyn gan ddefnyddio galwad system seccomp(2):

seccomp(SECCOMP_SET_MODE_FILTER, flags, &filter)

lle &filter - mae hwn yn arwydd o strwythur sydd eisoes yn gyfarwydd i ni struct sock_fprog, h.y. rhaglen BPF.

Sut mae rhaglenni ar gyfer seccomp yn wahanol i raglenni ar gyfer socedi? Cyd-destun a drosglwyddir. Yn achos socedi, cawsom ardal cof yn cynnwys y pecyn, ac yn achos seccomp rhoddwyd strwythur fel

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

Yma nr yw rhif yr alwad system i'w lansio, arch - pensaernïaeth gyfredol (mwy am hyn isod), args - hyd at chwe dadl galwad system, a instruction_pointer yn bwyntydd i'r cyfarwyddyd gofod defnyddiwr a wnaeth y system alwad. Felly, er enghraifft, i lwytho rhif galwad y system i'r gofrestr A rhaid i ni ddweud

ldw [0]

Mae nodweddion eraill ar gyfer rhaglenni seccomp, er enghraifft, dim ond trwy aliniad 32-bit y gellir cyrchu'r cyd-destun ac ni allwch lwytho hanner gair neu beit - wrth geisio llwytho hidlydd ldh [0] galwad system seccomp bydd yn dychwelyd EINVAL. Mae'r swyddogaeth yn gwirio'r hidlwyr sydd wedi'u llwytho seccomp_check_filter() cnewyllyn. (Y peth doniol yw, yn yr ymrwymiad gwreiddiol a ychwanegodd y swyddogaeth seccomp, maent wedi anghofio ychwanegu caniatâd i ddefnyddio'r cyfarwyddyd i'r swyddogaeth hon mod (gweddill yr is-adran) ac nid yw bellach ar gael ar gyfer rhaglenni BPF seccomp, ers ei ychwanegu bydd torri ABI.)

Yn y bôn, rydyn ni eisoes yn gwybod popeth i ysgrifennu a darllen rhaglenni seccomp. Fel arfer trefnir rhesymeg y rhaglen fel rhestr wen neu ddu o alwadau system, er enghraifft y rhaglen

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

yn gwirio rhestr ddu o bedwar galwad system wedi'u rhifo 304, 176, 239, 279. Beth yw'r galwadau system hyn? Ni allwn ddweud yn sicr, gan na wyddom ar gyfer pa bensaernïaeth yr ysgrifennwyd y rhaglen. Felly, mae awduron seccomp cynnig cychwyn pob rhaglen gyda gwiriad pensaernïaeth (nodir y bensaernïaeth gyfredol yn y cyd-destun fel maes arch strwythurau struct seccomp_data). Gyda'r bensaernïaeth wedi'i gwirio, byddai dechrau'r enghraifft yn edrych fel:

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

ac yna byddai ein rhifau galwadau system yn cael gwerthoedd penodol.

Rydym yn ysgrifennu ac yn llwytho hidlwyr i'w defnyddio seccomp libseccomp

Mae ysgrifennu hidlwyr mewn cod brodorol neu yn y cynulliad BPF yn caniatáu ichi gael rheolaeth lawn dros y canlyniad, ond ar yr un pryd, weithiau mae'n well cael cod cludadwy a / neu ddarllenadwy. Bydd y llyfrgell yn ein helpu gyda hyn libseccomp, sy'n darparu rhyngwyneb safonol ar gyfer ysgrifennu hidlwyr du neu wyn.

Gadewch i ni, er enghraifft, ysgrifennu rhaglen sy'n rhedeg ffeil ddeuaidd o ddewis y defnyddiwr, ar ôl gosod rhestr ddu o alwadau system yn flaenorol o yr erthygl uchod (mae'r rhaglen wedi'i symleiddio er mwyn ei darllen yn well, mae'r fersiwn lawn i'w chael yma):

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

Yn gyntaf rydym yn diffinio arae sys_numbers o 40+ o rifau galwadau system i'w rhwystro. Yna, dechreuwch y cyd-destun ctx a dweud wrth y llyfrgell beth rydyn ni am ei ganiatáu (SCMP_ACT_ALLOW) pob galwad system yn ddiofyn (mae'n haws adeiladu rhestrau gwahardd). Yna, fesul un, rydym yn ychwanegu'r holl alwadau system o'r rhestr ddu. Mewn ymateb i alwad system o'r rhestr, rydym yn gofyn SCMP_ACT_TRAP, yn yr achos hwn bydd seccomp yn anfon signal i'r broses SIGSYS gyda disgrifiad o ba system alwad sy'n torri'r rheolau. Yn olaf, rydym yn llwytho'r rhaglen i mewn i'r cnewyllyn gan ddefnyddio seccomp_load, a fydd yn llunio'r rhaglen a'i hatodi i'r broses gan ddefnyddio galwad system seccomp(2).

Er mwyn llunio casgliad llwyddiannus, rhaid i'r rhaglen fod yn gysylltiedig â'r llyfrgell libseccomp, er enghraifft:

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

Enghraifft o lansiad llwyddiannus:

$ ./seccomp_lib echo ok
ok

Enghraifft o alwad system wedi'i rhwystro:

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

Rydym yn defnyddio straceam fanylion:

$ 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

sut y gallwn wybod bod y rhaglen wedi'i therfynu oherwydd y defnydd o alwad system anghyfreithlon mount(2).

Felly, fe wnaethon ni ysgrifennu hidlydd gan ddefnyddio'r llyfrgell libseccomp, gosod cod nad yw'n ddibwys yn bedair llinell. Yn yr enghraifft uchod, os oes nifer fawr o alwadau system, gellir lleihau'r amser gweithredu yn amlwg, gan mai dim ond rhestr o gymariaethau yw'r siec. Ar gyfer optimeiddio, roedd gan libseccomp yn ddiweddar clwt wedi'i gynnwys, sy'n ychwanegu cefnogaeth i'r priodoledd hidlo SCMP_FLTATR_CTL_OPTIMIZE. Bydd gosod y briodwedd hon i 2 yn trosi'r hidlydd yn rhaglen chwilio ddeuaidd.

Os ydych chi eisiau gweld sut mae hidlwyr chwilio deuaidd yn gweithio, edrychwch ar sgript syml, sy'n cynhyrchu rhaglenni o'r fath yn y cydosodwr BPF trwy ddeialu rhifau galwadau'r system, er enghraifft:

$ 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

Mae'n amhosibl ysgrifennu unrhyw beth yn sylweddol gyflymach, gan na all rhaglenni BPF berfformio neidiau mewnoliad (ni allwn wneud, er enghraifft, jmp A neu jmp [label+X]) ac felly mae pob trawsnewidiad yn statig.

seccomp a strace

Mae pawb yn gwybod y cyfleustodau strace yn arf anhepgor ar gyfer astudio ymddygiad prosesau ar Linux. Fodd bynnag, mae llawer hefyd wedi clywed am materion perfformiad wrth ddefnyddio'r cyfleustodau hwn. Y ffaith yw bod strace gweithredu gan ddefnyddio ptrace(2), ac yn y mecanwaith hwn ni allwn nodi pa set o alwadau system sydd eu hangen arnom i atal y broses, h.y., er enghraifft, gorchmynion

$ 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

yn cael eu prosesu tua'r un amser, er mai dim ond un alwad system yr ydym am ei holrhain yn yr ail achos.

Opsiwn newydd --seccomp-bpf, ychwanegwyd at strace fersiwn 5.3, yn caniatáu ichi gyflymu'r broses lawer gwaith ac mae'r amser cychwyn o dan olrhain un alwad system eisoes yn debyg i amser cychwyn rheolaidd:

$ 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

(Yma, wrth gwrs, mae yna ychydig o dwyll gan nad ydym yn olrhain prif alwad system y gorchymyn hwn. Pe baem yn olrhain, er enghraifft, newfsstat, Yna strace byddai brêc yr un mor galed â hebddo --seccomp-bpf.)

Sut mae'r opsiwn hwn yn gweithio? Hebddi hi strace yn cysylltu â'r broses ac yn dechrau ei ddefnyddio PTRACE_SYSCALL. Pan fydd proses a reolir yn rhoi galwad system (unrhyw un), trosglwyddir rheolaeth i strace, sy'n edrych ar y dadleuon o'r alwad system ac yn rhedeg gyda hi PTRACE_SYSCALL. Ar ôl peth amser, mae'r broses yn cwblhau'r alwad system ac wrth ei gadael, trosglwyddir rheolaeth eto strace, sy'n edrych ar y gwerthoedd dychwelyd ac yn dechrau'r broses gan ddefnyddio PTRACE_SYSCALL, ac yn y blaen.

BPF ar gyfer y rhai bach, rhan sero: BPF clasurol

Gyda seccomp, fodd bynnag, gellir optimeiddio'r broses hon yn union fel yr hoffem. Sef, os ydym am edrych ar y galwad system yn unig X, yna gallwn ysgrifennu hidlydd BPF hynny ar gyfer X yn dychwelyd gwerth SECCOMP_RET_TRACE, ac ar gyfer galwadau nad ydynt o ddiddordeb i ni - SECCOMP_RET_ALLOW:

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

Yn yr achos hwn, strace dechrau'r broses i ddechrau fel PTRACE_CONT, mae ein hidlydd yn cael ei brosesu ar gyfer pob galwad system, os nad yw'r alwad system X, yna mae'r broses yn parhau i redeg, ond os yw hyn X, yna bydd seccomp yn trosglwyddo rheolaeth stracea fydd yn edrych ar y dadleuon ac yn dechrau'r broses fel PTRACE_SYSCALL (gan nad oes gan seccomp y gallu i redeg rhaglen wrth adael galwad system). Pan fydd galwad y system yn dychwelyd, strace bydd yn ailgychwyn y broses gan ddefnyddio PTRACE_CONT a bydd yn aros am negeseuon newydd gan seccomp.

BPF ar gyfer y rhai bach, rhan sero: BPF clasurol

Wrth ddefnyddio'r opsiwn --seccomp-bpf mae dau gyfyngiad. Yn gyntaf, ni fydd yn bosibl ymuno â phroses sydd eisoes yn bodoli (opsiwn -p rhaglenni strace), gan na chefnogir hyn gan seccomp. Yn ail, nid oes unrhyw bosibilrwydd dim edrych ar brosesau plentyn, gan fod hidlwyr seccomp yn cael eu hetifeddu gan bob proses plentyn heb y gallu i analluogi hyn.

Ychydig mwy o fanylion ar sut yn union strace yn gweithio gyda seccomp i'w gael o adroddiad diweddar. I ni, y ffaith fwyaf diddorol yw bod y BPF clasurol a gynrychiolir gan seccomp yn dal i gael ei ddefnyddio heddiw.

xt_bpf

Gadewch i ni nawr fynd yn ôl i fyd rhwydweithiau.

Cefndir: amser maith yn ôl, yn 2007, roedd y craidd wedi adio y modiwl xt_u32 ar gyfer netfilter. Fe'i hysgrifennwyd trwy gyfatebiaeth â dosbarthwr traffig hyd yn oed yn fwy hynafol cls_u32 ac yn caniatáu ichi ysgrifennu rheolau deuaidd mympwyol ar gyfer iptables gan ddefnyddio'r gweithrediadau syml canlynol: llwytho 32 did o becyn a pherfformio set o weithrediadau rhifyddol arnynt. Er enghraifft,

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

Yn llwytho 32 did y pennyn IP, gan ddechrau ar badin 6, ac yn gosod mwgwd arnynt 0xFF (cymerwch y beit isel). Y maes hwn protocol Pennawd IP ac rydym yn ei gymharu ag 1 (ICMP). Gallwch gyfuno llawer o wiriadau mewn un rheol, a gallwch hefyd weithredu'r gweithredwr @ — symudwch X beit i'r dde. Er enghraifft, y rheol

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

gwirio os nad yw Rhif Sequence TCP yn gyfartal 0x29. Nid af i fanylion pellach, gan ei bod eisoes yn amlwg nad yw ysgrifennu rheolau o'r fath â llaw yn gyfleus iawn. Yn yr erthygl BPF - y bytecode anghofiedig, mae sawl cysylltiad ag enghreifftiau o ddefnydd a chynhyrchu rheolau ar gyfer xt_u32. Gweler hefyd y dolenni ar ddiwedd yr erthygl hon.

Ers 2013 modiwl yn lle modiwl xt_u32 gallwch ddefnyddio modiwl seiliedig ar BPF xt_bpf. Dylai unrhyw un sydd wedi darllen cyn belled fod yn glir eisoes ynghylch egwyddor ei weithrediad: rhedeg BPF bytecode fel rheolau iptables. Gallwch greu rheol newydd, er enghraifft, fel hyn:

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

yma <байткод> - dyma'r cod mewn fformat allbwn cyfosodwr bpf_asm yn ddiofyn, er enghraifft,

$ 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

Yn yr enghraifft hon rydym yn hidlo pob pecyn CDU. Cyd-destun ar gyfer rhaglen BPF mewn modiwl xt_bpf, wrth gwrs, yn cyfeirio at ddata'r pecyn, yn achos iptables, i ddechrau'r pennawd IPv4. Gwerth dychwelyd o'r rhaglen BPF boolaiddlle false yn golygu nad oedd y pecyn yn cyfateb.

Mae'n amlwg bod y modiwl xt_bpf yn cefnogi hidlwyr mwy cymhleth na'r enghraifft uchod. Gadewch i ni edrych ar enghreifftiau go iawn o Cloudfare. Tan yn ddiweddar buont yn defnyddio'r modiwl xt_bpf i amddiffyn rhag ymosodiadau DDoS. Yn yr erthygl Cyflwyno Offer BPF maent yn esbonio sut (a pham) y maent yn cynhyrchu hidlwyr BPF ac yn cyhoeddi dolenni i set o gyfleustodau ar gyfer creu ffilterau o'r fath. Er enghraifft, defnyddio'r cyfleustodau bpfgen gallwch greu rhaglen BPF sy'n cyfateb i ymholiad DNS am enw 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

Yn y rhaglen rydym yn llwytho i mewn i'r gofrestr yn gyntaf X cyfeiriad dechrau'r llinell x04habrx03comx00 y tu mewn i ddatagram CDU ac yna gwiriwch y cais: 0x04686162 <-> "x04hab" ac ati

Ychydig yn ddiweddarach, cyhoeddodd Cloudfare y cod casglwr p0f -> BPF. Yn yr erthygl Cyflwyno'r casglwr BPF p0f maen nhw'n siarad am beth yw p0f a sut i drosi llofnodion p0f i 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,
...

Ar hyn o bryd ddim yn defnyddio Cloudfare mwyach xt_bpf, ers iddynt symud i XDP - un o'r opsiynau ar gyfer defnyddio'r fersiwn newydd o BPF, gweler. L4Drop: Lliniaru XDP DDoS.

cls_bpf

Yr enghraifft olaf o ddefnyddio BPF clasurol yn y cnewyllyn yw'r dosbarthwr cls_bpf ar gyfer yr is-system rheoli traffig yn Linux, wedi'i ychwanegu at Linux ar ddiwedd 2013 ac yn disodli'r hynafol yn gysyniadol cls_u32.

Fodd bynnag, ni fyddwn yn awr yn disgrifio'r gwaith cls_bpf, oherwydd o safbwynt gwybodaeth am BPF clasurol ni fydd hyn yn rhoi unrhyw beth i ni - rydym eisoes wedi dod yn gyfarwydd â'r holl ymarferoldeb. Yn ogystal, mewn erthyglau dilynol yn sôn am BPF Estynedig, byddwn yn cwrdd â'r dosbarthwr hwn fwy nag unwaith.

Rheswm arall i beidio â siarad am ddefnyddio BPF clasurol c cls_bpf Y broblem yw, o'i gymharu â BPF Estynedig, bod cwmpas y cymhwysedd yn yr achos hwn wedi'i gulhau'n sylweddol: ni all rhaglenni clasurol newid cynnwys pecynnau ac ni allant arbed cyflwr rhwng galwadau.

Felly mae'n bryd ffarwelio â BPF clasurol ac edrych i'r dyfodol.

Ffarwelio â BPF clasurol

Buom yn edrych ar sut y datblygodd technoleg BPF yn y nawdegau cynnar, yn llwyddiannus am chwarter canrif a hyd y diwedd wedi dod o hyd i gymwysiadau newydd. Fodd bynnag, yn debyg i'r newid o beiriannau stac i RISC, a oedd yn ysgogiad ar gyfer datblygu BPF clasurol, yn y 32au bu newid o beiriannau 64-bit i XNUMX-bit a dechreuodd BPF clasurol ddod yn anarferedig. Yn ogystal, mae galluoedd BPF clasurol yn gyfyngedig iawn, ac yn ychwanegol at y bensaernïaeth hen ffasiwn - nid oes gennym y gallu i arbed cyflwr rhwng galwadau i raglenni BPF, nid oes unrhyw bosibilrwydd o ryngweithio uniongyrchol â defnyddwyr, nid oes unrhyw bosibilrwydd o ryngweithio. gyda'r cnewyllyn, ac eithrio ar gyfer darllen nifer cyfyngedig o feysydd strwythur sk_buff a lansio'r swyddogaethau helpwr symlaf, ni allwch newid cynnwys pecynnau a'u hailgyfeirio.

Mewn gwirionedd, ar hyn o bryd y cyfan sy'n weddill o'r BPF clasurol yn Linux yw'r rhyngwyneb API, ac y tu mewn i'r cnewyllyn mae'r holl raglenni clasurol, boed yn hidlwyr soced neu hidlwyr seccomp, yn cael eu trosi'n awtomatig i fformat newydd, Estynedig BPF. (Byddwn yn siarad am yn union sut mae hyn yn digwydd yn yr erthygl nesaf.)

Dechreuodd y newid i bensaernïaeth newydd yn 2013, pan gynigiodd Alexey Starovoytov gynllun diweddaru BPF. Yn 2014 y clytiau cyfatebol dechreuodd ymddangos yn y craidd. Cyn belled ag y deallaf, y cynllun cychwynnol oedd gwneud y gorau o'r bensaernïaeth a'r casglwr JIT i redeg yn fwy effeithlon ar beiriannau 64-bit, ond yn hytrach roedd yr optimeiddiadau hyn yn nodi dechrau pennod newydd yn natblygiad Linux.

Bydd erthyglau pellach yn y gyfres hon yn ymdrin â phensaernïaeth a chymwysiadau'r dechnoleg newydd, a adwaenir i ddechrau fel BPF mewnol, yna BPF estynedig, ac yn awr yn syml BPF.

cyfeiriadau

  1. Steven McCanne a Van Jacobson, "Yr Hidlo Pecyn BSD: Pensaernïaeth Newydd ar gyfer Dal Pecyn ar Lefel Defnyddiwr", https://www.tcpdump.org/papers/bpf-usenix93.pdf
  2. Steven McCanne, "libpcap: Pensaernïaeth a Methodoleg Optimeiddio ar gyfer Dal Pecyn", https://sharkfestus.wireshark.org/sharkfest.11/presentations/McCanne-Sharkfest'11_Keynote_Address.pdf
  3. tcpdump, libpcap: https://www.tcpdump.org/
  4. Tiwtorial Cyfateb IPtable U32.
  5. BPF - y côd byte anghofiedig: https://blog.cloudflare.com/bpf-the-forgotten-bytecode/
  6. Cyflwyno'r Offeryn BPF: https://blog.cloudflare.com/introducing-the-bpf-tools/
  7. bpf_cls: http://man7.org/linux/man-pages/man8/tc-bpf.8.html
  8. Trosolwg seccomp: https://lwn.net/Articles/656307/
  9. https://github.com/torvalds/linux/blob/master/Documentation/userspace-api/seccomp_filter.rst
  10. habr: Cynwysyddion a diogelwch: seccomp
  11. habr: Ynysu daemonau gyda systemd neu “does dim angen Docker ar gyfer hyn!”
  12. Paul Chaignon, "strae --seccomp-bpf: golwg o dan y cwfl", https://fosdem.org/2020/schedule/event/debugging_strace_bpf/
  13. netsniff-ng: http://netsniff-ng.org/

Ffynhonnell: hab.com

Ychwanegu sylw