BPF pro parvulis, pars nulla: classic BPF

Berkeley Packet Filtra (BPF) est technologiae nuclei Linux quae in anterioribus paginis technicae linguae Anglicae divulgationis iam aliquot annos fuit. Conferentiae plenae sunt relationibus de usu et progressu BPF. David Miller, Linux retis subsystem assertorem, sermonem suum vocat apud Linux Plumbers 2018 "Haec Disputatio non est de XDP" (XDP unus usus est causa BPF). Brendan Gregg dat loquitur inscribitur Linux BPF Superpowers. Toke HΓΈiland-JΓΈrgensen ridetnucleus nunc microkernel. Thomas Graf promovet opinionem BPF est JavaScript pro nucleo.

Nulla adhuc descriptio systematica BPF in HabrΓ©, ideoque in serie articulorum loqui conabor de historia technicae artis, de architectura et evolutione instrumentorum describere, ac ambitus applicationis et praxi utendi BPF. Articulus hic, nulla, in serie, historiam et architecturam classicarum BPF narrat, ac etiam secreta principiorum operantium eius manifestat. tcpdump, seccomp, straceac multo magis.

Progressio BPF regitur a communitate retis Linux, principales applicationes BPF existentes ad retiacula referuntur ideoque, cum venia. @eucariotSeriem pro parvulis BPF in honorem magnae seriei vocavi "Retia pro parvulis".

Brevis cursus in historia BPF.c)

Technologia moderna BPF est emendatior et amplificata versio veteris technologiae eodem nomine, nunc classico BPF ad vitandam confusionem. Nota utilitas creata fundatur in classic BPF tcpdump, mechanism seccomp, tum modulorum minorum notarum xt_bpf ad iptables et classifier cls_bpf. In recentioribus Linux, programmata classica BPF automatice in novam formam transferuntur, tamen, ex parte utentis, API loco mansit et novos usus classicorum BPF, sicut in hoc articulo videbimus, adhuc inveniuntur. Quam ob rem, tum etiam quia historiam progressionem classicarum BPF in Linux secutus, clarius fiet quomodo et cur in formam recentiorem evolvatur, articulum de classicis BPF statui incipere.

In fine octoginta proximi saeculi, fabrum clarissimi Laurentii Berkeley Laboratorium studiosi facti sunt in quaestionem quomodo ad fasciculos retis recte eliquandum in ferramentis quae recenti octoginta saeculo proxime praeterito fuit. Praecipua notio eliquationis, quam primum in CSPF (CMU/Stanford Packet Filter) technologiae effecta est, erat ut primo quoque tempore eliquare fasciculos superfluos, i.e. in spatio nucleo, quia hoc describens notitias non necessarias in spatium usoris vitat. Ad securitatem currentis temporis in kernel spatii usoris codicem currit, apparatus virtualis sandboxed adhibitus est.

Sed virtualis machinis ad filtras exsistentes destinata sunt ut in machinis acervosis fundatis currerent nec tam efficaciter in machinis RISC recentioribus currerent. Quam ob rem, per machinarum machinarum conatus a Berkeley Labs, nova technologia BPF (Berkeley Packet Filters) elaborata est, cuius architectura virtualis apparatus erat cuius processus Motorola 6502 processor in fundamento erat - opificii talium notarum productorum ut Apple II aut nit,. Novus apparatus virtualis augebatur colum perficiendi decies vicibus comparatis solutionibus existentibus.

BPF machina architectura

Nos architecturam in operatione cognoscemus, exempla analysis. Sed imprimis, dicamus machinam duas 32-bit tabulas pervias utenti utenti, accumulatori. A et index mandare X64 bytes of memory (16 words), promptus scribendi et sequens lectio, et parva ratio mandatorum ad operandum cum hiis. Salire instructiones ad expressiones conditionales exsequendas etiam in programmatis praesto erant, sed ut opportune complerentur programmatis, salit modo progredi posset, i.e., speciatim vetitum erat loramenta creare.

Ratio generalis machinae incipiendi talis est. Usor programmata architecturae BPF creat et, utens quidam nucleus mechanismus (ut systema vocant), onerat et connectit programma to ut quidam ad eventum generantis in nucleo (exempli gratia, eventus est adventus proximi fasciculi in card retiacula). Cum res incidit, nucleus programmatis (exempli gratia in interpretem decurrit), et machina memoriae correspondet ut quidam regionis memoria nucleus (exempli gratia, notitia fasciculi advenientis).

Praedicta sufficiant nobis ad exempla inspicere: cognoscemus ordinem ac formas imperandi necessarias. Si vis statim systema imperativum machinae virtualis studere et de omnibus eius facultatibus cognoscere, tunc legere potes articulum originalem. BSD fasciculum Filter et / vel primum dimidium tabella Documentation/networking/filter.txt ex nucleo documentorum. Insuper studere potes libpcap: Architecture and Optimization Methodology for Packet Capturein quo McCanne, unus ex auctoribus BPF, loquitur de historia creationis libpcap.

Movemus omnia exempla significantia utendi classic BPF in Linux: tcpdump (libpcap), seccomp, xt_bpf, cls_bpf.

tcpdump

Explicatio BPF peracta est parallela cum evolutione frontis ad fasciculum eliquandi - notae utilitatis. tcpdump. Et, cum hoc vetustissimum et celeberrimum exemplum sit utendi classic BPF, praesto multis systematibus operantibus, studium technologiarum cum eo incipiemus.

Exempla omnia in hoc articulo de Linux cucurri 5.6.0-rc6. Praeceptorum quorundam output pro meliori readability editum est.)

Exempli gratia: servatis IPv6 facis

Cogitemus nos velle inspicere omnia IPv6 facis in interface eth0. Ad hoc facere possumus currere programmata tcpdump cum simplex filter ip6:

$ sudo tcpdump -i eth0 ip6

haec tcpdump conscribit colum ip6 in BPF architecturae bytecode et mitte ad nucleum (vide singula in sectione Tcpdump: loading). Colamentum onustum pro quolibet fasciculo per interfacem transiens mittetur eth0. Si filter refert a non-nulla pretii nErgo usque ad n bytes fasciculi exscriptus erit ad spatium usoris et in output videbimus tcpdump.

BPF pro parvulis, pars nulla: classic BPF

Evenit ut facile invenire possimus quid bytecode ad nucleum missum sit tcpdump ope tcpdumpsi currimus cum optione -d:

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

In linea nulla currimus imperium ldh [12], quod stat pro "onere in registro" A dimidium verbi (16 bits) in inscriptione 12 positum" et sola quaestio est qualem nos alloquimur memoriam? Responsum est quod in x incipit (x+1)th byte de retis fasciculum resolvitur. Legimus facis ab Aer interface eth0Et illud modoquod fasciculus huic similis videtur (pro simplicitate ponimus nullas VLAN tags in fasciculo esse);

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

Itaque mandatum ldh [12] in actis mandare A erunt agri Ether Type β€” genus fasciculi in hoc artubus Ethernet transmissum. In linea 1 contenta registri comparamus A (Sarcina genus) c * 0x86ddEt illud atque ibi Typum quem interest, IPv6 est. In linea 1, praeter praeceptum comparationis, duae sunt columnae. jt 2 ΠΈ jf 3 - notas ad quas ire debes si succedit comparatio (A == 0x86dd) et infaustus. Ita, in felici casu (IPv6) ad lineam 2 itur, et in incassum casu ad lineam 3. In linea 3 ratio terminatur cum codice 0 (do not copy the packet), in linea 2 the programmatis terminat with code. 262144 (exemplum mihi maximum est 256 involucrum chiliobytarum).

Exemplum magis complicatum: spectamus TCP facis per destinationem portum

Videamus quid colum spectet ut codices omnes TCP facis cum portu destinatum 666. Causam IPv4 consideremus, cum casus IPv6 simplicior sit. Hoc exemplo studens, IPv6 eliquare te ut exercitatio explorare potes.ip6 and tcp dst port 666) et filtrum pro casu generali (tcp dst port 666). Ita, colum quaerimus in specie huius modi:

$ 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

Iam scimus quid lineae 0 et 1 agamus. In linea 2 iam repressimus hanc esse fasciculum IPv4 (Ether Type = 0x800) et in actis mandare A 24* byte of the packet. Nostra sarcina similis

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

id onerant in actis mandare A Protocollum campi capitis IP, quod logicum est, quia solum TCP facis imitari volumus. Comparamus Protocollum cum 0x6 (IPPROTO_TCPin linea III.

In lineis 4 et 5 dimidiatis verbis ad orationem 20 collocatis oneratis et praecepto utimur jset reprehendo si trium sit vexilla - persona edita jset tres maxime significantes frena tolluntur. Duo ex tribus bits nobis indica an fasciculus pars IP fasciculi redacta sit, et si ita est, an fragmentum novissimum est. Tertium frenum reservatum est et nulla esse debet. Nolumus enim fasciculos incompletos vel fractos cohibere, sic omnes tres frusta compescimus.

Linea 6 maxime interesting in hac serie est. Expressio ldxb 4*([14]&0xf) significat nos load in actis mandare X quattuor minutae minimae decimae byte significans quattuor frusta ducta per 4. Minima quattuor obolis notabile decimum byte est ager. Internet Header Longitudo IPv4 header, qui longitudinem capitis in verbis reponit, ergo necesse est ut multiplicetur 4. Interestingly, the expression. 4*([14]&0xf) designatio peculiaris adlocutione quae modo in hac forma adhiberi potest et tantum pro registro X, i.e. non possumus dicere vel ldb 4*([14]&0xf) uel ldxb 5*([14]&0xf) (solum denotare possumus alium offset, v. gr. ldxb 4*([16]&0xf)). Perspicuum est hanc inscriptionis rationem in BPF additam esse praecise ad recipiendum X (index registri) IPv4 header longitudo.

Itaque in linea 7 conamur dimidium verbi onerare (X+16). Recordantes quod 14 bytes ab Ethernet heade occupantur X continet longitudinem IPv4 capitis, intelligimus in . A TCP destination portum oneratur:

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

Denique in linea 8 portum destinatum cum valore desiderato comparamus et in lineis 9 vel 10 eventum reddimus, sive fasciculum imitari sive non.

Tcpdump: loading

In exemplis praecedentibus specialiter non exacte habitamus quomodo BPF bytecode oneremus in nucleum pro eliquatione fasciculi. Fere, tcpdump tibus multis rationibus et operandi cum Filtra tcpdump utitur bibliotheca libpcap. Breviter, filtrum ponere in interface utens libpcapdebes facere quae sequuntur;

Ad videndum quomodo munus pcap_setfilter implemented in Linux, utimur strace (lineae quaedam remotae);

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

In duabus primis lineis output nos creamus rudis ostium tabernaculi omnes tabulas ethernet legere et eam ligare ad interface eth0... Ex primum exemplum nostrum Scimus quod filter ip constabit ex quatuor mandatorum BPF, et in tertia linea quomodo utens optione videmus SO_ATTACH_FILTER ratio vocationis setsockopt filtrum longitudinis oneramus et coniungimus 4. Colum nostrum haec est.

Notatu dignum est quod in classic BPF, loading et filtrum connectens ut operatio atomica semper occurrat, et in nova versione BPF, programmata onerans et ligans ad eventum generantis in tempore separatum.

Veritas abscondita

Paulo plenior versio e output similis est:

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

Ut supra, filtrum nostrum ad nervum in linea 5, oneramus et coniungimus, sed quid fit in lineis 3 et 4? Evenit ut hoc libpcap nos curat, ut colum fasciculi non includant quae ei non satisfaciunt, bibliotheca. annectit phantasma filter ret #0 (omissis omnibus fasciculis), nervum permutat ad modum non-obstructionem et omnes fasciculos quae ex priorum filtra remanere potuerunt minuere conatur.

In summa, fasciculos spargere in Linux utens classic BPF, debes habere filtrum in forma structurae sicut struct sock_fprog et nervus patens, post quem colum adjungi potest nervum systematis calli utens setsockopt.

Interestingly colum cuilibet nervum adiungi potest, non solum crudum. Hic exempli gratia programma, quod omnia intercludit, sed primum duos bytes ab omnibus advenientibus UDP datagrams. (Addidi commentarios in codice ne articulum clutter).

More details de usu setsockopt for connectens odio, see nervum (7), sed de scriptis tuis similibus struct sock_fprog sine auxilio tcpdump nos in sectione loqui Programmatio BPF manibus nostris.

Classic BPF ac XNUMX saeculo

BPF in Linux anno 1997 inclusa est et in ergastulis diu permansit libpcap sine ullis specialibus mutationibus (Linux-certis mutationibus, utique; eratsed picturam globalem non mutaverunt). Prima signa gravia quae BPF essent evolvenda anno 2011 venerunt, cum Ericus Dumazet proposuit lacus, qui addit Interpres Just Temporis Compilator ad nucleum convertendi BPF bytecode in patria x86_64 codicem.

JIT compilator primus in vinculo mutationum fuit: in MMXII apparuit facultatem scribere Filtra for seccompadhibitis BPF, mense Ianuario MMXIII erat additae modulus xt_bpf, quod tibi permittit scribere regulas pro iptables ope BPF, et mense Octobri MMXIII erat additae et modulus cls_bpf, quod permittit tibi scribere negotiatores classiarios utentes BPF.

Haec omnia mox accuratius inspiciemus, sed primum nobis utile erit scribere et compilare libellos arbitrarios BPF, cum facultates a librario praebeantur. libpcap limited (simplex exemplum: filter generatae libpcap Duo tantum valores referre possunt - 0 vel 0x40000) vel generaliter, sicut in casu seccomp, non teneri.

Programmatio BPF manibus nostris

De forma binaria instructionum BPF cognoscamus, valde simplex est;

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

Quaelibet institutio 64 bits obtinet, in quibus primum 16 frena sunt codicis instructionis, deinde duae sunt octo- bit indentae; jt ΠΈ jf, et 32 ​​bits pro argumento Kcuius quidem est ex praecepto ad imperandum. Exempli gratia, mandatum retquae programmata habet codicem 6& reditus pretii ex constanti K. In C, una instructio BPF repraesentatur ut structura

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

et tota progressio in forma structurae

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

Ita programmata scribere iam possumus (exempli gratia ex codicibus disciplinam scimus [1]). Hoc est quod colum videbunt ip6 ex primum exemplum nostrum:

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

Program prog utimur iure possumus in vocatio

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

Programmata scripturae in forma machinae codicibus non valde commoda est, sed interdum est necessaria (exempli gratia, ad debugging, creandos probationes unitatis, articulos scribendos in Habre, etc.). Nam commodo in tabella <linux/filter.h> adiutorium macros definiuntur - idem exemplum quod supra revocetur

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

Sed hic optio commodi non. Hoc est quod programmatores nuclei Linux ratiocinati sunt, ideoque in indicem tools/bpf nuclei invenire potes congregatorem et debuggerem ad operandum cum classic BPF.

Lingua conventus valde similis est debug output tcpdumpsed praeterea titulus symbolica denotare possumus. Exempli gratia, hic programma est quod omnes facis praeter TCP/IPv4 remittit;

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

Defalta, Congregator codicem in forma generat <количСство инструкций>,<code1> <jt1> <jf1> <k1>,...ad exemplum nostrum TCP erit

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

Ad commodum programmatum C, forma alia output adhiberi potest:

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

Hic locus exscribi potest in structuram speciei definitionis struct sock_filterut in principio hujus sectionis fecimus.

Linux and netsniff-ng extensiones

Praeter vexillum BPF, Linux et tools/bpf/bpf_asm support et set non-vexillum. Basically, instructiones ad agros structurae accedere solebant struct sk_buffquae in nucleo retis describitur. Sed alia quoque genera instructionum adiutorium sunt, e.g ldw cpu onerare in actis mandare A consequuntur currit ad nucleum munus raw_smp_processor_id(). (In nova versione BPF, hae extensiones non normae extensae sunt ut programmata nucleorum auxiliatorum ad memoriam, structurarum ac eventuum generandam accessionem praebeant.) Hic est exemplum interesting filtri in quo solum imitamur. packet capitis in user spatium usus est extensio poff, payload offset:

ld poff
ret a

BPF extensiones non possunt in tcpdump, sed hoc rationabiliter noscendum est utilitatis sarcina netsniff-ngquae, inter alia, progressum continet netsniff-ng, quae, praeter eliquationem utendi BPF, etiam generans negotiationem effectivam continet et plusquam antecedens tools/bpf/bpf_asm, a BPF vocatus bpfc. Involucrum documenta satis accurata continet, vide etiam nexus in fine articuli.

seccomp

Itaque iam novimus programmata arbitrariae complexionis BPF scribere et parata ad nova exempla spectare, quorum primum est technologia seccomp, quae, utens BPF filtra, disponere et disponere rationum vocationis argumenta praesto sunt. dato processu posterisque suis.

Prima versio seccomp nucleo anno 2005 addita est et non valde popularis, cum unicam tantum optionem praeberet - ut modum institutum systematis vocat processum ad hunc modum: read, write, exit ΠΈ sigreturnet processus qui praecepta violare utens occisus est SIGKILL. Nihilominus, anno 2012, seccomp columellae facultatem utendi BPF addidit, permittens te definitum systematis vocatum definire atque etiam in eorum argumentis compescere facere. (Interestingly, Chrome unus e primis usoribus huius functionis fuit, et Chrome populus nunc enucleat mechanismum KRSI innixam in nova versione BPF et customizationem Moduli Securitatis Linux permittens.) Vincula ad ulteriora documenta in fine inveniri possunt. articulum.

Nota articulos iam fuisse in centrum circa utentes seccomp, fortasse aliquis eas legere volet ante (vel pro) sequentes ordines legere. In articulum Vasa et securitas: seccomp exempla praebet utendi seccomp, tam in versione 2007 quam in versione utendi BPF (filters generantur utens libseccomp), loquitur de nexu seccomp cum Docker, et etiam multos nexus utiles praebet. In articulum Daemones segregandi cum systematico vel "hoc Docker non eges!" Tegit, praesertim, quomodo adiciendi blacklists vel album systematis daemones systematos currendo vocat.

Deinde videbimus quomodo scribenda et onera columellae pro seccomp in nudo C et per bibliothecam libseccomp et quaenam sint cuiusque optionis pros et cons, et denique videamus quomodo per programmata seccomp adhibetur strace.

Scribere et loading Filtra seccomp

Iam novimus programmata BPF scribere, ut inspiciamus primum interfaciendi programmationis seccomp. Filtrum in gradu processus potes ponere, et omnes processus pueri restrictiones hereditabunt. Hoc fit utens ratio vocationis seccomp(2):

seccomp(SECCOMP_SET_MODE_FILTER, flags, &filter)

quibus &filter - Regula est haec structurae iam nota nobis struct sock_fprog, i.e. Programma BPF.

Quomodo programmata seccomp differunt ab programmatibus basibus? Traditur contextus. In basibus, area memoriae cum fasciculo datae sumus, et in casu seccomp structurae sicut nobis datae sunt.

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

est nr vocationis ratio deducenda est numerus; arch β€” Architectura monetae (de hoc infra plura); args - usque ad sex rationes vocationis ratio, et instruction_pointer monstrator est ad instructionem spatii usoris, quod ratio vocationis fecit. Sic, exempli gratia, numerum vocationis systematis onerare in actis mandare A debemus dicere

ldw [0]

Aliae notae sunt pro programmatibus seccomp, exempli gratia: contextus solum potest accessi per 32-dam noctis et dimidium verbi vel byte onerare non potes - cum filtrum onerare conatur ldh [0] ratio vocationis seccomp reddet EINVAL. Munus impedit onusta Filtra seccomp_check_filter() nucleis. (Ridiculam res est, in originali committo quod seccomp functionality additam, obliti sunt licentiam addendi utendi instructionem ad hoc munus utendi. mod (residuum divisio) et nunc perpendat pro programmatibus seccomp BPF, cum eius additione et conteram ABI.)

Basically, iam omnia scire programmata seccomp scribere et legere. Solet programmata logica ita disposita ut album vel nigrum systematis vocat, exempli gratia programmatis

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

ratio sistit notatione qua- tuor numeranda 304, 176, 239, 279. Quaenam haec ratio vocat? Pro certo dicere non possumus, quia nescimus cuius programmatis architectura scripta sit. Auctores igitur seccomp offer satus omnes programmata cum architectura reprehendo (exprimatur current architectura in contextu sicut in agro arch structure struct seccomp_data). Cum architecturae inhibeatur, principium exempli speciem praebet;

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

et tunc systema vocationis numeri certa bona obtinerent.

Scribimus et odio seccomp utendo ad Filtra libseccomp

Filtra scribens in codice indigena vel in BPF conventu permittit te plenam potestatem habere de eventu, sed eodem tempore, interdum potior est ut codicem portatilem et/vel lectabilem habeas. Bibliotheca nos adiuvabit hac libseccompquod praebet signum instrumenti ad scribendum Filtra nigra vel alba.

Sit scriptor, exempli gratia, scribere programma quod binarium fasciculi eligentis usoris decurrit, antea album systematis vocatum ex nigro inauguratum. supra articulum (programma facilior ad maiorem promptitudinem, plena litera inueniri potest" hic):

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

Primum definimus aciem sys_numbers de 40+ ratio vocationis numeros ad scandalum. Deinde initialize contextum ctx et dic bibliothecae quod volumus permittere .SCMP_ACT_ALLOW) omnis ratio vocat defaltam (facilius est blacklists aedificare). Deinde singillatim addimus omnem rationem vocati a notatione. Ad rationem vocationis ex indice rogamus SCMP_ACT_TRAPsignum processui mittet in hoc casu seccomp SIGSYS cum descriptione, quae systema vocant, praecepta violarit. Denique progressio in nucleo utendo oneremus seccomp_load, quae programmata componet et eam ad processum vocationis systematis adhibens seccomp(2).

Ad bene compilationem, propositum cum bibliotheca coniungendum est libseccompFor example:

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

Exemplum felicis deducunt:

$ ./seccomp_lib echo ok
ok

Exemplum de ratione vocatus clausus:

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

Utimur stracead singula:

$ 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

quomodo scire possumus programmata esse terminata propter usum vocationis iniustum systema mount(2).

Ita nos scripsimus filtrum bibliothecae utendo libseccompin quatuor lineis codice non-triviali aptans. In exemplo superiore, si plures sunt systematis vocatorum, tempus exsecutionis notabiliter reduci potest, cum perscriptio iusta collationum enumeratio est. Pro ipsum, libseccomp nuper habuit repeciare includi, quae subsidium colum attributum addit SCMP_FLTATR_CTL_OPTIMIZE. Ponens hoc attributum ad 2 colum in programmata inquisitionis binarii convertet.

Si vis videre quomodo investigationis columellae binarii laborant, inspice simplex scriptorqui tales programmata in BPF convocat, systema dialing vocant numeros, exempli gratia:

$ 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

Nihil signanter ocius scribere impossibile est, cum programmata BPF salit incisum praestare non possunt (exempli gratia facere non possumus, jmp A aut jmp [label+X]) ideoque omnes transitus sunt statice.

seccomp et strace

Quisque novit utilitatem strace instrumentum necessarium est ad investigandum mores processuum in Linux. Sed multi etiam audiverunt perficientur quaestiones cum hac utilitate utens. Factum est strace per implemented ptrace(2)et in hac mechanismo definire non possumus in quo statuta ratio vocat, processum prohibere necesse est, i.e., exempli gratia, mandatorum.

$ 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

eodem fere tempore discursum est, quamvis in secundo casu unam tantum vocationis rationem investigare velimus.

Nova optio --seccomp-bpfAdditum strace versio 5.3, sino te pluries accelerare processum et temporis initium sub vestigio unius vocationis systematis iam comparabile cum tempore cuiusdam satus regularis;

$ 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

(Hic, sane, levis est deceptio in eo quod nos principalem huius praecepti rationem non repetimus. Si repetamus, e.g. newfsstattum strace non fregit tantum ut difficile ac sine --seccomp-bpf.)

Quomodo haec optio opus facit? Sine eius strace annectit processum et incipit illud utens PTRACE_SYSCALL. Cum processum administratum negotium (aliquem) ratio vocat, imperium transfertur strace, quod argumenta vocationis systematis spectat et currit cum PTRACE_SYSCALL. Post aliquod tempus, processus vocationem systema complet et cum exeunt, imperium iterum transfertur strace, qui reditus bonorum spectat et processum utens incipit PTRACE_SYSCALL, et sic porro.

BPF pro parvulis, pars nulla: classic BPF

Cum seccomp, tamen, hic processus optimized prorsus sicut velimus. Nempe si solum ad vocationem systematis spectare velimus X, tunc scribere possumus BPF colum quod for X refert valorem SECCOMP_RET_TRACEet vocat quae non sunt ad nos commoda. SECCOMP_RET_ALLOW:

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

P 'SЌS, RΡ•Rј SΠƒR "SΡ“S ‑ RΞΌ Β° F strace initio incipit processus ut PTRACE_CONT, colum nostrum ad singulas vocationes systematis discursum est, si ratio vocationis non est Xtum processus currit, si id X, seccomp transferet imperium stracequae argumenta spectabimus et processum incipiemus sicut PTRACE_SYSCALL (quia seccomp non habet facultatem ad progressionem in exitus ex vocatione systematis currere). Cum ratio vocationis redit; strace et sileo processus usura PTRACE_CONT et exspectabo novas e seccomp.

BPF pro parvulis, pars nulla: classic BPF

Cum per optionem ad --seccomp-bpf sunt duo restrictiones. Uno modo, non erit processus iam exsistentis coniungi -p programs strace) , cum hoc non fulciatur per seccomp. Alio modo, non est possibilitas non ecce processus infantis, quia seccomp filamenta hereditaria ab omnibus processibus infantis sine facultate disable hoc.

Paulo accuratius quam exacte strace operatus est seccomp inveniri potest ex * recens fama. Nobis, maxime interesting res est quod classic BPF, quae per seccomp repraesentata est, hodie adhuc adhibetur.

xt_bpf

Nunc ad mundi ligula.

Background: olim in 2007, core erat additae modulus xt_u32 for netfilter. Similitudine scripta est cum classificio negotiationis vel antiquioris cls_u32 et permisit tibi scribere normas arbitrarias binarias pro iptables utentes operationibus simplicibus sequentibus: onerare 32 frenos e sarcinis et in eis arithmeticam copiam facere. Exempli gratia

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

32 frenos onerat capitis IP, incipiendo a additamento 6 et larvam illis applicans 0xFF (byte accipere humilis). Hic ager protocol IP header and we compare it with 1 (ICMP). Plures compescmenta in una regula coniungere potes, operatorem quoque exequi potes @ - X bytes ad dextram move. Exempli gratia

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

checks si TCP Sequentia non est aequalis 0x29. Plura non persequar, cum iam perspicuum sit eiusmodi regulas manu scribere non satis commode. In articulum BPF - oblitus bytecode, plures nexus sunt cum exemplis usui et regulae generationis xt_u32. Vide etiam nexus in fine huius articuli.

Cum MMXIII moduli pro moduli xt_u32 vos can utor a moduli secundum BPF xt_bpf. Qui hoc longe legerit, iam de principio suae operationis clarescere debet: currunt BPF bytecode sicut regulae iptables. Novam regulam creare potes, verbi gratia, sic:

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

hic <Π±Π°ΠΉΡ‚ΠΊΠΎΠ΄> - Hoc signum est in forma coadunatoris output bpf_asm per defaltam, v.gr.

$ 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

In hoc exemplo percolamus omnia UDP facis. Contextus pro BPF progressio in moduli xt_bpfsane notat fasciculum datae, in casu iptables, ad initium capitis IPv4. Redi valorem ex progressio BPF Booleanquibus false significat packet congruit.

Patet modulus xt_bpf plures Filtra complexus sustinet quam exemplum supra. Intueamur vera exempla e Cloudfare. Usque ad modulum nuper utebantur xt_bpf contra DDoS munire impetus. In articulum Introducendis BPF Tools explicant quomodo (quare) Filtra BPF gignant et nexus edant statuto utilitatum ad tales filteras creandas. Verbi gratia utens utilitate bpfgen BPF programma creare potes quod quaestioni nomen DNS congruit 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

In programmate primum pondus in tabulario dabimus X initium linea oratio x04habrx03comx00 intra UDP datagram et deinde instantiam; 0x04686162 <-> "x04hab" etc.

Paulo post, Cloudfare ediderunt p0f -> BPF compilator codicem. In articulum Introducendis p0f BPF compilator loquuntur quid p0f sit et quomodo signaturas p0f convertendi ad 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,
...

Currently non per Cloudfare xt_bpf, cum ad XDP - unam ex optionibus ad novam versionem BPF utendam, vide. L4Drop: XDP DDoS Mitigationes.

cls_bpf

Ultimum exemplum utendi classic BPF in nucleo est classificans cls_bpf ad subsystem subsystem negotiationis in Linux, Linux in fine 2013 additae et antiqua ratione reposuit. cls_u32.

Sed opus non nunc describere cls_bpfcum ex parte cognitionis de classicis BPF id nihil nobis tribuet β€” iam omnibus functionibus familiares facti sumus. Praeterea in articulis subsequentibus de BPF extenso loquentibus, hoc classificio plus semel occurremus.

Alia ratio non est loqui de utens classic BPF c cls_bpf Problema est quod, respectu BPF extenso, scopus applicabilitatis in hoc casu funditus coarctatur: programmata classica contenta fasciculorum mutare non possunt et statum inter vocat servare non possunt.

Tempus est igitur ut classic BPF vale dicere et futura prospicere.

Vale ut classic BPF

Inspeximus quomodo technologiam BPF, in primis nonaginta primis, feliciter vixerit in quarta parte saeculi et usque ad finem novae applicationes invenit. Attamen, similis transitus a machinis acervis ad RISC, quae pro BPF classicorum evolutionis impetu functus est, in 32s transitus ab 64-bit ad XNUMX frenum machinis et classicis BPF obsolescere coepit. Praeterea facultates classicorum BPF valde limitatae sunt, et praeter architecturam iamiam - non habemus facultatem conservandi status inter vocationum ad programmata BPF, nulla possibilitas directi usoris commercii, nulla possibilitas mutuandi. cum nucleo, praeter paucas structurae agris legere sk_buff et functiones simplicissimas adiutrices deducis, contenta fasciculorum mutare non potes et ea redigere.

Re vera, nunc omnia quae reliquiae classicae BPF in Linux sunt API interfacies, et intra nucleum omnium programmatum classicorum, si filtra vel filtra seccomp, automatice in novam formam, BPF extensa, transferuntur. (Prorsus loquemur quomodo hoc in sequenti articulo fiat.)

Transitus ad novam architecturam anno 2013 incepit, cum Alexey Starovoitov rationem renovationis BPF proposuit. In MMXIV correspondentes inaequaliter coepit apparere in core. Quantum intelligo, initiale consilium solum ad optimize architecturae et JIT compilator erat ut efficacius in 64 minutas machinas curreret, sed pro his optimizationibus notatum est initium novi capituli in evolutione Linux.

Articuli praeterea in hac serie operient architecturam et applicationes novae technologiae, initio notae internae BPF, deinde BPF, nunc simpliciter BPF.

References

  1. Steven McCanne et Van Jacobson, "The BSD fasciculum filtrum: Novum Architecture pro Usor-gradu Captura fasciculum", https://www.tcpdump.org/papers/bpf-usenix93.pdf
  2. Steven McCanne, "libpcap: Architecturae et Optimizationis Methodologia pro fasciculo Captura", https://sharkfestus.wireshark.org/sharkfest.11/presentations/McCanne-Sharkfest'11_Keynote_Address.pdf
  3. tcpdump, libpcap: https://www.tcpdump.org/
  4. IPtable U32 Compositus Roma.
  5. BPF - oblitus bytecode: https://blog.cloudflare.com/bpf-the-forgotten-bytecode/
  6. Introducendis BPF Tool: https://blog.cloudflare.com/introducing-the-bpf-tools/
  7. bpf_cls: http://man7.org/linux/man-pages/man8/tc-bpf.8.html
  8. A seccomp Overview: https://lwn.net/Articles/656307/
  9. https://github.com/torvalds/linux/blob/master/Documentation/userspace-api/seccomp_filter.rst
  10. habr: Containers et securitas: seccomp
  11. habr: daemones segregare cum systemd vel "non opus est tibi Docker pro hoc!"
  12. Paulus Chaignon, "strace--seccomp-bpf: vultus sub cucullo"; https://fosdem.org/2020/schedule/event/debugging_strace_bpf/
  13. netsniff-ng: http://netsniff-ng.org/

Source: www.habr.com