BPF pou ti piti yo, pati zewo: BPF klasik

Berkeley Packet Filters (BPF) se yon teknoloji nwayo Linux ki te nan premye paj piblikasyon teknoloji ann angle depi plizyè ane kounye a. Konferans yo plen ak rapò sou itilizasyon ak devlopman BPF. David Miller, mentè subsistèm rezo Linux, rele diskou li nan Linux Plumbers 2018 "Diskou sa a pa sou XDP" (XDP se youn ka itilize pou BPF). Brendan Gregg bay diskou ki gen dwa Linux BPF gwo pouvwa. Toke Høiland-Jørgensen rike nwayo a se kounye a yon mikrokernel. Thomas Graf ankouraje lide ke BPF la vle di javascript pou nwayo a.

Pa gen toujou yon deskripsyon sistematik nan BPF sou Habré, ak Se poutèt sa nan yon seri de atik mwen pral eseye pale sou istwa a nan teknoloji a, dekri achitekti yo ak zouti devlopman, ak deskripsyon zòn yo nan aplikasyon ak pratik nan itilize BPF. Atik sa a, zewo, nan seri a, rakonte istwa a ak achitekti nan BPF klasik, epi tou li revele sekrè yo nan prensip fonksyònman li yo. tcpdump, seccomp, strace, ak plis ankò.

Devlopman BPF kontwole pa kominote rezo Linux la, aplikasyon prensipal BPF ki egziste deja yo gen rapò ak rezo e se poutèt sa, avèk pèmisyon. @eukariot, Mwen te rele seri a "BPF pou ti moun yo", nan onè nan seri a gwo "Rezo pou timoun piti yo".

Yon kou kout nan istwa BPF(c)

Teknoloji modèn BPF se yon vèsyon amelyore ak elaji nan ansyen teknoloji a ak menm non an, kounye a yo rele klasik BPF pou fè pou evite konfizyon. Yon sèvis piblik byen koni te kreye ki baze sou BPF klasik la tcpdump, mekanis seccomp, osi byen ke pi piti modil li te ye xt_bpf pou iptables ak klasifikasyon cls_bpf. Nan Linux modèn, pwogram BPF klasik yo otomatikman tradui nan nouvo fòm lan, sepandan, nan yon pwen de vi itilizatè, API a te rete an plas ak nouvo itilizasyon pou BPF klasik, jan nou pral wè nan atik sa a, yo toujou jwenn. Pou rezon sa a, epi tou paske apre istwa a nan devlopman nan BPF klasik nan Linux, li pral vin pi klè ki jan ak poukisa li evolye nan fòm modèn li yo, mwen deside kòmanse ak yon atik sou BPF klasik.

Nan fen katreventèn yo nan dènye syèk la, enjenyè ki soti nan pi popilè Lawrence Berkeley Laboratory te vin enterese nan kesyon an sou ki jan yo byen filtre pake rezo sou pyès ki nan konpitè ki te modèn nan fen katreventèn yo nan dènye syèk la. Lide debaz nan filtraj, orijinal aplike nan teknoloji CSPF (CMU/Stanford Packet Filter), se te filtre pake ki pa nesesè pi bonè posib, sa vle di. nan espas nwayo, depi sa a evite kopye done ki pa nesesè nan espas itilizatè. Pou bay sekirite ègzekutabl pou kouri kòd itilizatè nan espas nwayo, yo te itilize yon machin vityèl sandboxed.

Sepandan, machin vityèl yo pou filtè ki egziste deja yo te fèt pou yo kouri sou machin ki baze sou pile epi yo pa t 'kouri avèk efikasite sou nouvo machin RISC. Kòm yon rezilta, grasa efò enjenyè Berkeley Labs yo, yo te devlope yon nouvo teknoloji BPF (Berkeley Packet Filters), achitekti machin vityèl ki te fèt ki baze sou processeur Motorola 6502 - chwal de travay nan pwodwi byen li te ye tankou Apple II oswa n. Nouvo machin vityèl la ogmante pèfòmans filtre dè dizèn de fwa konpare ak solisyon ki egziste deja.

BPF machin achitekti

Nou pral fè konesans ak achitekti nan yon fason k ap travay, analize egzanp. Sepandan, pou kòmanse, ann di ke machin nan te gen de rejis 32-bit aksesib a itilizatè a, yon akimilatè. A ak enskri endèks X, 64 byte memwa (16 mo), ki disponib pou ekri ak lekti ki vin apre, ak yon ti sistèm kòmandman pou travay ak objè sa yo. Enstriksyon so pou mete ann aplikasyon ekspresyon kondisyonèl yo te disponib tou nan pwogram yo, men pou garanti fini pwogram nan alè, so yo te kapab fèt sèlman pi devan, sa vle di, an patikilye, li te entèdi yo kreye bouk.

Konplo jeneral pou kòmanse machin nan se jan sa a. Itilizatè a kreye yon pwogram pou achitekti BPF ak, lè l sèvi avèk kèk mekanis nwayo (tankou yon apèl sistèm), chaje ak konekte pwogram nan pou kèk nan dèlko evènman an nan nwayo a (pa egzanp, yon evènman se rive nan pwochen pake a sou kat rezo a). Lè yon evènman rive, nwayo a kouri pwogram nan (pa egzanp, nan yon entèprèt), epi memwa machin nan koresponn ak pou kèk rejyon memwa nwayo (pa egzanp, done yon pake k ap rantre).

Sa ki pi wo a pral ase pou nou kòmanse gade egzanp: nou pral fè konesans ak sistèm nan ak fòma lòd jan sa nesesè. Si ou vle imedyatman etidye sistèm nan lòd nan yon machin vityèl epi aprann sou tout kapasite li yo, Lè sa a, ou ka li atik orijinal la. Filtè Pake BSD la ak/oswa premye mwatye dosye a Dokimantasyon/rezo/filter.txt soti nan dokiman nwayo a. Anplis de sa, ou ka etidye prezantasyon an libpcap: Yon Achitekti ak Metodoloji Optimizasyon pou Capture Pake, nan ki McCanne, youn nan otè yo nan BPF, pale sou istwa a nan kreyasyon libpcap.

Koulye a, nou ale nan konsidere tout egzanp enpòtan yo nan itilize BPF klasik sou Linux: tcpdump (libpcap), seccomp, xt_bpf, cls_bpf.

tcpdump

Devlopman nan BPF te fèt an paralèl ak devlopman nan entèfas pou filtraj pake - yon sèvis piblik byen li te ye. tcpdump. Epi, kòm sa a se egzanp ki pi ansyen ak pi popilè nan itilize BPF klasik, ki disponib sou anpil sistèm opere, nou pral kòmanse etid nou an sou teknoloji a avèk li.

(Mwen te kouri tout egzanp yo nan atik sa a sou Linux 5.6.0-rc6. Pwodiksyon kèk kòmandman te modifye pou pi bon lizibilite.)

Egzanp: obsève pake IPv6

Ann imajine ke nou vle gade tout pake IPv6 sou yon koòdone eth0. Pou fè sa nou ka kouri pwogram nan tcpdump ak yon filtè senp ip6:

$ sudo tcpdump -i eth0 ip6

Nan ka sa a, tcpdump konpile filtè a ip6 nan bytecode achitekti BPF epi voye li nan nwayo a (gade detay nan seksyon an Tcpdump: chaje). Filtè chaje a pral kouri pou chak pake ki pase nan koòdone a eth0. Si filtè a retounen yon valè ki pa zewo n, Lè sa a, jiska n octets nan pake a pral kopye nan espas itilizatè epi nou pral wè li nan pwodiksyon an tcpdump.

BPF pou ti piti yo, pati zewo: BPF klasik

Li sanble ke nou ka fasilman chèche konnen ki bytecode te voye nan nwayo a tcpdump avèk èd nan tcpdump, si nou kouri li ak opsyon an -d:

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

Sou liy zewo nou kouri lòd la ldh [12], ki vle di "chaje nan anrejistre A mwatye yon mo (16 bits) ki sitiye nan adrès 12” ak kesyon an sèlman se ki kalite memwa nou adrese? Repons lan se ke nan x kòmanse (x+1)th byte nan pake rezo analiz la. Nou li pake ki soti nan koòdone Ethernet la eth0e sa vle dike pake a sanble sa a (pou senplisite, nou sipoze ke pa gen okenn tag VLAN nan pake a):

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

Se konsa, apre egzekite lòd la ldh [12] nan rejis la A pral gen yon jaden Ether Type — ki kalite pake transmèt nan ankadreman Ethernet sa a. Sou liy 1 nou konpare sa ki nan rejis la A (kalite pake) c 0x86dde sa e gen Kalite nou enterese se IPv6. Sou liy 1, anplis de kòmandman an konparezon, gen de plis kolòn - jt 2 и jf 3 — mak kote ou bezwen ale si konparezon an reyisi (A == 0x86dd) ak san siksè. Se konsa, nan yon ka siksè (IPv6) nou ale nan liy 2, ak nan yon ka san siksè - nan liy 3. Sou liy 3 pwogram nan fini ak kòd 0 (pa kopye pake a), sou liy 2 pwogram nan fini ak kòd. 262144 (kopi m 'yon maksimòm 256 kilookte pake).

Yon egzanp ki pi konplike: nou gade pake TCP pa pò destinasyon

Ann wè ki jan yon filtè sanble ki kopye tout pake TCP ak pò destinasyon 666. Nou pral konsidere ka IPv4 la, paske ka IPv6 la pi senp. Apre w fin etidye egzanp sa a, ou ka eksplore filtè IPv6 tèt ou kòm yon egzèsis (ip6 and tcp dst port 666) ak yon filtè pou ka jeneral la (tcp dst port 666). Se konsa, filtè nou enterese nan sanble sa a:

$ 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

Nou deja konnen kisa liy 0 ak 1 fè. Sou liy 2 nou te deja tcheke ke sa a se yon pake IPv4 (Etè Kalite = 0x800) epi chaje li nan rejis la A 24yèm octet nan pake a. Pake nou an sanble

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

ki vle di nou chaje nan rejis la A jaden an Pwotokòl nan header IP la, ki se lojik, paske nou vle kopye sèlman TCP pake. Nou konpar Protokol avek 0x6 (IPPROTO_TCP) sou liy 3.

Sou liy 4 ak 5 nou chaje mwatye mo yo ki sitiye nan adrès 20 epi sèvi ak kòmandman an jset tcheke si youn nan twa yo mete drapo - mete mask yo bay la jset twa moso ki pi enpòtan yo otorize. De nan twa Bits yo di nou si pake a se yon pati nan yon pake IP fragmenté, epi si se konsa, si li se dènye fragman an. Twazyèm ti jan an rezève epi li dwe zewo. Nou pa vle tcheke swa pake ki pa konplè oswa ki kase, kidonk nou tcheke tout twa bit.

Liy 6 se pi enteresan nan lis sa a. Ekspresyon ldxb 4*([14]&0xf) vle di nou chaje nan rejis la X kat bit yo pi piti enpòtan nan kenzyèm octet nan pake a miltipliye pa 4. Kat pi piti enpòtan nan byte kenzyèm lan se jaden an. Entènèt Header Length IPv4 header, ki estoke longè header a nan mo, kidonk ou bezwen miltipliye pa 4. Enteresan, ekspresyon an. 4*([14]&0xf) se yon deziyasyon pou yon plan adrès espesyal ki ka itilize sèlman nan fòm sa a epi sèlman pou yon rejis X, i.e. nou pa ka di tou ldb 4*([14]&0xf) ni ldxb 5*([14]&0xf) (nou ka sèlman presize yon konpanse diferan, pou egzanp, ldxb 4*([16]&0xf)). Li klè ke konplo adrès sa a te ajoute nan BPF jisteman yo nan lòd yo resevwa X (enskri endèks) longè header IPv4.

Se konsa, sou liy 7 nou eseye chaje mwatye yon mo nan (X+16). Sonje ke 14 bytes yo okipe pa header Ethernet la, ak X gen longè header IPv4 la, nou konprann ke nan A Pò destinasyon TCP chaje:

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

Finalman, sou liy 8 nou konpare pò destinasyon an ak valè a vle ak sou liy 9 oswa 10 nou retounen rezilta a - si yo kopye pake a oswa ou pa.

Tcpdump: chaje

Nan egzanp anvan yo, nou espesyalman pa t rete an detay sou egzakteman ki jan nou chaje BPF bytecode nan nwayo a pou filtraj pake. An jeneral, tcpdump Port nan anpil sistèm ak pou travay ak filtè tcpdump sèvi ak bibliyotèk la libpcap. Yon ti tan, yo mete yon filtè sou yon koòdone lè l sèvi avèk libpcap, ou bezwen fè bagay sa yo:

Pou wè ki jan fonksyon an pcap_setfilter aplike nan Linux, nou itilize strace (kèk liy yo te retire):

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

Sou de premye liy pwodiksyon nou kreye priz kri li tout ankadreman Ethernet epi mare li nan koòdone a eth0... Nan premye egzanp nou an nou konnen ke filtre a ip pral konpoze de kat enstriksyon BPF, ak sou liy lan twazyèm nou wè ki jan w ap itilize opsyon an SO_ATTACH_FILTER apèl sistèm setsockopt nou chaje ak konekte yon filtè ki gen longè 4. Sa a se filtè nou an.

Li se vo anyen ke nan BPF klasik, chaje ak konekte yon filtè toujou rive kòm yon operasyon atomik, ak nan nouvo vèsyon an nan BPF, chaje pwogram nan ak obligatwa li nan dèlko evènman an yo separe nan tan.

Verite kache

Yon vèsyon yon ti kras pi konplè nan pwodiksyon an sanble sa a:

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

Kòm mansyone pi wo a, nou chaje ak tache filtè nou an nan priz la sou liy 5, men sa k ap pase sou liy 3 ak 4? Li sanble ke sa a libpcap pran swen nou - pou pwodiksyon filtè nou an pa gen ladan pake ki pa satisfè li, bibliyotèk la. konekte enbesil filtre ret #0 (depoze tout pake), chanje priz la nan mòd ki pa bloke epi eseye soustraksyon tout pake ki ta ka rete nan filtè anvan yo.

An total, pou filtre pakè sou Linux lè l sèvi avèk BPF klasik, ou bezwen gen yon filtè nan fòm yon estrikti tankou struct sock_fprog ak yon priz louvri, apre sa ka filtè a dwe tache ak priz la lè l sèvi avèk yon apèl sistèm setsockopt.

Enteresan, filtè a ka tache nan nenpòt priz, pa sèlman anvan tout koreksyon. Isit la egzanp yon pwogram ki koupe tout, men de premye byte yo nan tout datagram UDP fèk ap rantre. (Mwen te ajoute kòmantè nan kòd la pou m pa ankonbre atik la.)

Plis detay sou itilizasyon setsockopt pou konekte filtè, gade priz (7), men sou ekri pwòp filtè ou tankou struct sock_fprog san èd tcpdump nou pral pale nan seksyon an Programming BPF ak pwòp men nou.

BPF klasik ak XNUMXyèm syèk la

BPF te enkli nan Linux an 1997 e li te rete yon chwal de travay pou yon tan long libpcap san okenn chanjman espesyal (chanjman espesifik pou Linux, nan kou, Nou te, men yo pa t chanje foto mondyal la). Premye siy grav ke BPF ta evolye te vini an 2011, lè Eric Dumazet te pwopoze patch, ki ajoute Just In Time Compiler nan nwayo a - yon tradiktè pou konvèti BPF bytecode nan natif natal. x86_64 kòd.

Compilateur JIT te premye nan chèn chanjman an: nan 2012 parèt kapasite yo ekri filtè pou seccomp, lè l sèvi avèk BPF, nan mwa janvye 2013 te gen te ajoute modil xt_bpf, ki pèmèt ou ekri règ pou iptables avèk èd nan BPF, ak nan mwa Oktòb 2013 te te ajoute tou yon modil cls_bpf, ki pèmèt ou ekri klasifikasyon trafik lè l sèvi avèk BPF.

Nou pral gade tout egzanp sa yo an plis detay byento, men anvan li pral itil pou nou aprann kijan pou ekri ak konpile pwogram abitrè pou BPF, depi kapasite bibliyotèk la bay. libpcap limite (egzanp senp: filtre pwodwi libpcap ka retounen sèlman de valè - 0 oswa 0x40000) oswa jeneralman, tankou nan ka a nan seccomp, yo pa aplikab.

Programming BPF ak pwòp men nou

Ann fè konesans ak fòma binè enstriksyon BPF, li trè senp:

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

Chak enstriksyon okipe 64 bit, kote premye 16 bit yo se kòd enstriksyon an, Lè sa a, gen de ti kras uit bit, jt и jf, ak 32 Bits pou agiman an K, objektif ki varye de kòmand pou kòmand. Pou egzanp, lòd la ret, ki mete fen nan pwogram nan gen kòd la 6, epi yo pran valè retounen nan konstan an K. Nan C, yon sèl enstriksyon BPF reprezante kòm yon estrikti

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

ak tout pwogram nan se nan fòm lan nan yon estrikti

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

Kidonk, nou ka deja ekri pwogram (pa egzanp, nou konnen kòd enstriksyon yo nan [1]). Sa a se sa filtè a pral sanble ip6 nan premye egzanp nou an:

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

pwogram prog nou ka itilize legalman nan yon apèl

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

Ekri pwogram nan fòm kòd machin pa trè pratik, men pafwa li nesesè (pa egzanp, pou debogaj, kreye tès inite, ekri atik sou Habré, elatriye). Pou konvenyans, nan dosye a <linux/filter.h> makro asistan yo defini - menm egzanp ki anwo a ta ka reekri kòm

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

Sepandan, opsyon sa a pa trè pratik. Sa a se sa pwogramasyon yo nwayo Linux rezone, ak Se poutèt sa nan anyè a tools/bpf nwayo ou ka jwenn yon asanblaj ak debogaj pou travay ak BPF klasik.

Lang asanble sanble anpil ak pwodiksyon debug tcpdump, men anplis nou ka presize etikèt senbolik. Pa egzanp, isit la se yon pwogram ki jete tout pake eksepte TCP/IPv4:

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

Pa default, asanble a jenere kòd nan fòma a <количество инструкций>,<code1> <jt1> <jf1> <k1>,..., pou egzanp nou an ak TCP li pral

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

Pou konvenyans pwogramè C yo, yo ka itilize yon fòma pwodiksyon diferan:

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

Tèks sa a ka kopye nan definisyon estrikti kalite a struct sock_filter, jan nou te fè nan kòmansman seksyon sa a.

Linux ak netsniff-ng ekstansyon

Anplis BPF estanda, Linux ak tools/bpf/bpf_asm sipò ak seri ki pa estanda. Fondamantalman, enstriksyon yo itilize pou jwenn aksè nan jaden yo nan yon estrikti struct sk_buff, ki dekri yon pake rezo nan nwayo a. Sepandan, genyen tou lòt kalite asistan enstriksyon, pou egzanp ldw cpu pral chaje nan rejis la A rezilta nan kouri yon fonksyon nwayo raw_smp_processor_id(). (Nan nouvo vèsyon BPF, ekstansyon ki pa estanda sa yo te pwolonje pou bay pwogram yo yon seri asistans nwayo pou jwenn aksè nan memwa, estrikti, ak jenere evènman yo.) Men yon egzanp enteresan nan yon filtè kote nou kopye sèlman a. Tèt pake nan espas itilizatè lè l sèvi avèk ekstansyon an poff, konpanse chaj:

ld poff
ret a

Ekstansyon BPF pa ka itilize nan tcpdump, men sa a se yon bon rezon pou fè konesans ak pake sèvis piblik la netsniff-ng, ki, pami lòt bagay, gen yon pwogram avanse netsniff-ng, ki, anplis filtraj lè l sèvi avèk BPF, tou gen yon dèlko trafik efikas, ak pi avanse pase tools/bpf/bpf_asm, yon asanble BPF rele bpfc. Pake a gen dokiman byen detaye, gade tou lyen yo nan fen atik la.

seccomp

Kidonk, nou deja konnen ki jan yo ekri pwogram BPF ki gen konpleksite abitrè epi nou pare pou gade nouvo egzanp, premye nan yo se teknoloji seccomp, ki pèmèt, lè l sèvi avèk filtè BPF, jere seri ak seri diskisyon apèl sistèm ki disponib pou. yon pwosesis bay ak desandan li yo.

Premye vèsyon seccomp te ajoute nan nwayo a an 2005 e li pa t trè popilè, paske li te bay sèlman yon sèl opsyon - pou limite seri apèl sistèm ki disponib nan yon pwosesis pou sa ki annapre yo: read, write, exit и sigreturn, ak pwosesis la ki vyole règ yo te touye lè l sèvi avèk SIGKILL. Sepandan, nan 2012, seccomp te ajoute kapasite pou itilize filtè BPF, ki pèmèt ou defini yon seri apèl sistèm pèmèt e menm fè chèk sou agiman yo. (Enteresan, Chrome se te youn nan premye itilizatè yo nan fonksyonalite sa a, ak moun yo Chrome yo kounye a ap devlope yon mekanis KRSI ki baze sou yon nouvo vèsyon BPF epi ki pèmèt personnalisation nan modil sekirite Linux.) Ou ka jwenn lyen ki mennen nan dokiman adisyonèl nan fen a. nan atik la.

Remake byen ke te deja gen atik sou mwaye a sou itilizasyon seccomp, petèt yon moun ap vle li yo anvan (oswa olye pou yo) li sou-seksyon sa yo. Nan atik la Kontenè ak sekirite: seccomp bay egzanp lè l sèvi avèk seccomp, tou de vèsyon an 2007 ak vèsyon an lè l sèvi avèk BPF (filtè yo pwodwi lè l sèvi avèk libseccomp), pale sou koneksyon an nan seccomp ak Docker, epi tou li bay anpil lyen itil. Nan atik la Izole demon ak systemd oswa "ou pa bezwen Docker pou sa!" Li kouvri, an patikilye, ki jan yo ajoute lis nwa oswa lis blan nan apèl sistèm pou demon kap kouri systemd.

Apre sa nou pral wè ki jan yo ekri ak chaje filtè pou seccomp nan toutouni C ak lè l sèvi avèk bibliyotèk la libseccomp ak ki avantaj ak dezavantaj chak opsyon, epi finalman, ann wè kijan pwogram nan itilize seccomp. strace.

Ekri ak chaje filtè pou seccomp

Nou deja konnen ki jan yo ekri pwogram BPF, kidonk ann premye gade nan koòdone nan pwogram seccomp. Ou ka mete yon filtè nan nivo pwosesis la, epi tout pwosesis timoun yo pral eritye restriksyon yo. Sa a se fè lè l sèvi avèk yon apèl sistèm seccomp(2):

seccomp(SECCOMP_SET_MODE_FILTER, flags, &filter)

kote &filter - sa a se yon konsèy sou yon estrikti deja abitye pou nou struct sock_fprog, i.e. Pwogram BPF.

Ki jan pwogram pou seccomp diferan de pwogram pou sockets? Kontèks transmèt. Nan ka sipò yo, yo te ba nou yon zòn memwa ki gen pake a, ak nan ka a nan seccomp yo te ba nou yon estrikti tankou

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

Isit la nr se nimewo apèl sistèm lan pou lanse, arch - Achitekti aktyèl (plis sou sa anba a), args - jiska sis sistèm apèl agiman, ak instruction_pointer se yon konsèy sou enstriksyon espas itilizatè a ki te fè apèl sistèm lan. Kidonk, pou egzanp, chaje nimewo apèl sistèm lan nan rejis la A nou dwe di

ldw [0]

Gen lòt karakteristik pou pwogram seccomp, pou egzanp, kontèks la ka sèlman jwenn aksè nan aliyman 32-bit epi ou pa ka chaje mwatye yon mo oswa yon byte - lè w ap eseye chaje yon filtè. ldh [0] apèl sistèm seccomp pral retounen EINVAL. Fonksyon an tcheke filtè ki chaje yo seccomp_check_filter() nwayo. (Bagay komik se, nan komèt orijinal la ki te ajoute fonksyonalite seccomp la, yo bliye ajoute pèmisyon pou itilize enstriksyon nan fonksyon sa a. mod (rès divizyon) epi kounye a li pa disponib pou pwogram seccomp BPF, depi adisyon li yo pral kraze ABI.)

Fondamantalman, nou deja konnen tout bagay pou ekri ak li pwogram seccomp. Anjeneral lojik pwogram nan ranje kòm yon lis blan oswa nwa nan apèl sistèm, pou egzanp pwogram nan

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

tcheke yon lis nwa kat apèl sistèm ki nimewote 304, 176, 239, 279. Kisa apèl sistèm sa yo ye? Nou pa ka di pou asire w, paske nou pa konnen pou ki achitekti pwogram nan te ekri. Se poutèt sa, otè yo nan seccomp òf kòmanse tout pwogram yo ak yon chèk achitekti (achitekti aktyèl la endike nan kontèks la kòm yon jaden arch estrikti struct seccomp_data). Avèk achitekti a tcheke, kòmansman egzanp lan ta sanble:

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

ak Lè sa a, nimewo apèl sistèm nou an ta jwenn sèten valè.

Nou ekri ak chaje filtè pou seccomp lè l sèvi avèk libseccomp

Ekri filtè nan kòd natif natal oswa nan asanble BPF pèmèt ou gen tout kontwòl sou rezilta a, men an menm tan an, li se pafwa pi preferab gen kòd pòtab ak / oswa lizib. Bibliyotèk la ap ede nou ak sa libseccomp, ki bay yon koòdone estanda pou ekri filtè nwa oswa blan.

Ann, pou egzanp, ekri yon pwogram ki kouri yon dosye binè nan chwazi itilizatè a, li te deja enstale yon lis nwa nan apèl sistèm soti nan. atik ki anwo a (yo te senplifye pwogram nan pou pi gwo lizibilite, ou ka jwenn vèsyon konplè a isit la):

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

Premye nou defini yon etalaj sys_numbers nan 40+ nimewo apèl sistèm yo bloke. Lè sa a, inisyalize kontèks la ctx epi di bibliyotèk la sa nou vle pèmèt (SCMP_ACT_ALLOW) tout apèl sistèm pa default (li pi fasil pou konstwi lis nwa). Lè sa a, youn pa youn, nou ajoute tout apèl sistèm nan lis nwa a. An repons a yon apèl sistèm nan lis la, nou mande SCMP_ACT_TRAP, nan ka sa a seccomp pral voye yon siyal nan pwosesis la SIGSYS ak yon deskripsyon ki apèl sistèm vyole règ yo. Finalman, nou chaje pwogram nan nan nwayo a lè l sèvi avèk seccomp_load, ki pral konpile pwogram nan epi tache li nan pwosesis la lè l sèvi avèk yon apèl sistèm seccomp(2).

Pou konpilasyon siksè, pwogram nan dwe lye ak bibliyotèk la libseccomppa egzanp:

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

Egzanp yon lansman siksè:

$ ./seccomp_lib echo ok
ok

Egzanp yon apèl sistèm bloke:

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

Nou itilize stracepou detay:

$ 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

ki jan nou ka konnen ke pwogram nan te sispann akòz itilizasyon yon apèl sistèm ilegal mount(2).

Se konsa, nou te ekri yon filtè lè l sèvi avèk bibliyotèk la libseccomp, Fitting kòd ki pa trivial nan kat liy. Nan egzanp ki anwo a, si gen yon gwo kantite apèl sistèm, tan an ekzekisyon ka notables redwi, depi chèk la se jis yon lis konparezon. Pou optimize, libseccomp dènyèman te genyen patch enkli, ki ajoute sipò pou atribi filtre a SCMP_FLTATR_CTL_OPTIMIZE. Mete atribi sa a nan 2 pral konvèti filtè a nan yon pwogram rechèch binè.

Si ou vle wè ki jan filtè rechèch binè travay, pran yon gade nan script senp, ki jenere pwogram sa yo nan BPF assembler lè w konpoze nimewo apèl sistèm yo, pou egzanp:

$ 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

Li enposib pou ekri anyen siyifikativman pi vit, paske pwogram BPF pa ka fè so indentation (nou pa ka fè, pou egzanp, jmp A oswa jmp [label+X]) ak Se poutèt sa tout tranzisyon yo estatik.

seccomp ak strace

Tout moun konnen sèvis piblik la strace se yon zouti endispansab pou etidye konpòtman pwosesis sou Linux. Sepandan, anpil moun te tande pale tou pwoblèm pèfòmans lè w ap itilize sèvis piblik sa a. Reyalite a se ke strace aplike lè l sèvi avèk ptrace(2), ak nan mekanis sa a nou pa ka presize nan ki seri apèl sistèm nou bezwen sispann pwosesis la, sa vle di, pou egzanp, kòmandman.

$ 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

yo trete nan apeprè menm tan an, byenke nan dezyèm ka a nou vle trase sèlman yon apèl sistèm.

Nouvo opsyon --seccomp-bpf, ajoute nan strace vèsyon 5.3, pèmèt ou akselere pwosesis la anpil fwa ak tan an demaraj anba tras la nan yon apèl sistèm deja konparab ak tan an nan yon demaraj regilye:

$ 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

(Isit la, nan kou, gen yon desepsyon ti tay nan ke nou pa ap trase apèl sistèm prensipal la nan lòd sa a. Si nou te trase, pou egzanp, newfsstat, Lè sa a, strace ta frennen menm jan ak san yo pa --seccomp-bpf.)

Ki jan opsyon sa a travay? San li strace konekte ak pwosesis la epi li kòmanse itilize li PTRACE_SYSCALL. Lè yon pwosesis jere bay yon (nenpòt) apèl sistèm, kontwòl yo transfere nan strace, ki gade agiman yo nan apèl la sistèm ak kouri li lè l sèvi avèk PTRACE_SYSCALL. Apre kèk tan, pwosesis la konplete apèl sistèm lan epi lè w ap sòti nan li, kontwòl transfere ankò strace, ki gade valè retounen yo epi kòmanse pwosesis la lè l sèvi avèk PTRACE_SYSCALL, ak sou sa.

BPF pou ti piti yo, pati zewo: BPF klasik

Avèk seccomp, sepandan, pwosesis sa a ka optimize egzakteman jan nou ta renmen. Savwa, si nou vle gade sèlman nan apèl la sistèm X, Lè sa a, nou ka ekri yon filtè BPF ki pou X retounen yon valè SECCOMP_RET_TRACE, ak pou apèl ki pa enterese nou - SECCOMP_RET_ALLOW:

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

Nan ka sa a strace okòmansman kòmanse pwosesis la kòm PTRACE_CONT, filtè nou an trete pou chak apèl sistèm, si apèl sistèm lan pa X, Lè sa a, pwosesis la ap kontinye kouri, men si sa a X, Lè sa a, seccomp pral transfere kontwòl straceki pral gade nan agiman yo epi kòmanse pwosesis la tankou PTRACE_SYSCALL (depi seccomp pa gen kapasite pou kouri yon pwogram sou sòti nan yon apèl sistèm). Lè apèl sistèm lan retounen, strace pral rekòmanse pwosesis la lè l sèvi avèk PTRACE_CONT epi yo pral tann nouvo mesaj soti nan seccomp.

BPF pou ti piti yo, pati zewo: BPF klasik

Lè w ap itilize opsyon an --seccomp-bpf gen de restriksyon. Premyèman, li pa pral posib pou rantre nan yon pwosesis ki deja egziste (opsyon -p pwogram strace), depi sa a pa sipòte pa seccomp. Dezyèmman, pa gen okenn posibilite pa gen okenn gade nan pwosesis timoun yo, depi filtè seccomp yo eritye pa tout pwosesis timoun san yo pa kapasite nan enfim sa a.

Yon ti kras plis detay sou ki jan egzakteman strace travay avèk seccomp ka jwenn nan dènye rapò. Pou nou, reyalite ki pi enteresan an se ke BPF klasik reprezante pa seccomp toujou itilize jodi a.

xt_bpf

Ann tounen kounye a nan mond lan nan rezo.

Background: yon bon bout tan de sa, an 2007, nwayo a te te ajoute modil xt_u32 pou netfilter. Li te ekri pa analoji ak yon klasifikasyon trafik menm plis ansyen cls_u32 epi pèmèt ou ekri règ binè abitrè pou iptables lè l sèvi avèk operasyon senp sa yo: chaje 32 bit nan yon pake epi fè yon seri operasyon aritmetik sou yo. Pa egzanp,

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

Chaje 32 bit yo nan header IP la, kòmanse nan padding 6, epi aplike yon mask sou yo. 0xFF (pran byte ki ba). Jaden sa a protocol IP header epi nou konpare li ak 1 (ICMP). Ou ka konbine anpil chèk nan yon sèl règ, epi ou ka tou egzekite operatè a @ — deplase X bytes sou bò dwat la. Pou egzanp, règ la

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

tcheke si Nimewo Sekans TCP a pa egal 0x29. Mwen pa pral antre nan detay pi lwen, paske li deja klè ke ekri règ sa yo alamen pa trè pratik. Nan atik la BPF - bytecode a bliye, gen plizyè lyen ak egzanp itilizasyon ak jenerasyon règ pou xt_u32. Gade tou lyen yo nan fen atik sa a.

Depi 2013 modil olye de modil xt_u32 ou ka itilize yon modil ki baze sou BPF xt_bpf. Nenpòt moun ki te li byen lwen sa a ta dwe deja klè sou prensip la nan operasyon li yo: kouri BPF bytecode kòm règ iptables. Ou ka kreye yon nouvo règ, pou egzanp, tankou sa a:

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

isit la <байткод> - sa a se kòd la nan fòma pwodiksyon assembler bpf_asm pa default, pou egzanp,

$ 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

Nan egzanp sa a nou ap filtre tout pake UDP. Kontèks pou yon pwogram BPF nan yon modil xt_bpf, nan kou, pwen nan done yo pake, nan ka a nan iptables, nan kòmansman an nan header la IPv4. Valè retounen nan pwogram BPF booleyenkote false vle di pake a pa t matche.

Li klè ke modil la xt_bpf sipòte filtè pi konplèks pase egzanp ki anwo a. Ann gade egzanp reyèl ki soti nan Cloudfare. Jiska dènyèman yo te itilize modil la xt_bpf pou pwoteje kont atak DDoS. Nan atik la Entwodwi Zouti BPF yo yo eksplike kijan (ak poukisa) yo jenere filtè BPF epi pibliye lyen ki mennen nan yon seri sèvis piblik pou kreye filtè sa yo. Pou egzanp, lè l sèvi avèk sèvis piblik la bpfgen ou ka kreye yon pwogram BPF ki matche ak yon demann DNS pou yon non 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

Nan pwogram nan nou premye chaje nan rejis la X kòmansman adrès liy x04habrx03comx00 andedan yon datagram UDP epi tcheke demann lan: 0x04686162 <-> "x04hab" elatriye

Yon ti kras pita, Cloudfare pibliye p0f -> BPF du kòd. Nan atik la Prezante p0f BPF du yo pale sou ki sa p0f ye ak ki jan yo konvèti siyati p0f nan 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,
...

Kounye a pa sèvi ak Cloudfare ankò xt_bpf, depi yo demenaje ale rete nan XDP - youn nan opsyon yo pou itilize nouvo vèsyon an nan BPF, gade. L4Drop: XDP DDoS mitigasyon.

cls_bpf

Dènye egzanp lè l sèvi avèk BPF klasik nan nwayo a se klasifikasyon an cls_bpf pou subsistèm kontwòl trafik nan Linux, ajoute nan Linux nan fen 2013 ak konseptyèlman ranplase ansyen an. cls_u32.

Sepandan, nou pa pral kounye a dekri travay la cls_bpf, depi nan pwen de vi konesans sou BPF klasik sa a pa pral ba nou anyen - nou te deja vin abitye ak tout fonksyonalite yo. Anplis de sa, nan atik ki vin apre ap pale de BPF pwolonje, nou pral rankontre klasifikasyon sa a plis pase yon fwa.

Yon lòt rezon pou pa pale sou itilize klasik BPF c cls_bpf Pwoblèm lan se ke, konpare ak BPF pwolonje, sijè ki abòde lan aplikab nan ka sa a se radikalman etwat: pwogram klasik pa ka chanje sa ki nan pakè epi yo pa ka sove eta ant apèl yo.

Se konsa, li lè yo di orevwa nan BPF klasik ak gade nan lavni an.

Adye BPF klasik

Nou te gade kijan teknoloji BPF, ki te devlope nan kòmansman ane 32 yo, te viv avèk siksè pou yon ka de syèk e jiska lafen jwenn nouvo aplikasyon. Sepandan, menm jan ak tranzisyon an soti nan machin pile nan RISC, ki te sèvi kòm yon UN pou devlopman nan BPF klasik, nan ane 64 yo te gen yon tranzisyon soti nan machin XNUMX-ti jan XNUMX-ti jan ak BPF klasik yo te kòmanse vin demode. Anplis de sa, kapasite yo nan BPF klasik yo trè limite, ak nan adisyon a achitekti a demode - nou pa gen kapasite pou konsève pou eta ant apèl nan pwogram BPF, pa gen okenn posibilite pou entèraksyon itilizatè dirèk, pa gen okenn posibilite pou kominike. ak nwayo a, eksepte pou li yon kantite limite nan jaden estrikti sk_buff epi lanse fonksyon asistan ki pi senp yo, ou pa ka chanje sa ki nan pake epi redireksyon yo.

An reyalite, kounye a tout sa ki rete nan BPF klasik nan Linux se koòdone API a, ak andedan nwayo a tout pwogram klasik, kit se filtè priz oswa filtè seccomp, yo otomatikman tradui nan yon nouvo fòma, BPF pwolonje. (Nou pral pale sou egzakteman ki jan sa rive nan pwochen atik la.)

Tranzisyon an nan yon nouvo achitekti te kòmanse nan 2013, lè Alexey Starovoitov te pwopoze yon konplo aktyalizasyon BPF. Nan 2014 plak korespondan yo te kòmanse parèt nan nwayo a. Jan mwen konprann, plan inisyal la te sèlman optimize achitekti a ak konpilateur JIT pou kouri pi efikasman sou machin 64-bit, men olye de sa yo optimize te make kòmansman yon nouvo chapit nan devlopman Linux.

Plis atik nan seri sa a pral kouvri achitekti ak aplikasyon nouvo teknoloji a, okòmansman ke yo rekonèt kòm BPF entèn, Lè sa a, pwolonje BPF, epi kounye a tou senpleman BPF.

Referans

  1. Steven McCanne ak Van Jacobson, "BSD Pake Filter: Yon Nouvo Achitekti pou Capture Pake Itilizatè-nivo", https://www.tcpdump.org/papers/bpf-usenix93.pdf
  2. Steven McCanne, "libpcap: Yon Achitekti ak Metodoloji Optimizasyon pou Capture Pake", https://sharkfestus.wireshark.org/sharkfest.11/presentations/McCanne-Sharkfest'11_Keynote_Address.pdf
  3. tcpdump, libpcap: https://www.tcpdump.org/
  4. IPtable U32 Match Tutorial.
  5. BPF - bytecode a bliye: https://blog.cloudflare.com/bpf-the-forgotten-bytecode/
  6. Prezante zouti BPF: https://blog.cloudflare.com/introducing-the-bpf-tools/
  7. bpf_cls: http://man7.org/linux/man-pages/man8/tc-bpf.8.html
  8. Yon apèsi sou seccomp: https://lwn.net/Articles/656307/
  9. https://github.com/torvalds/linux/blob/master/Documentation/userspace-api/seccomp_filter.rst
  10. habr: Kontenè ak sekirite: seccomp
  11. habr: Izole demon ak systemd oswa "ou pa bezwen Docker pou sa!"
  12. Paul Chaignon, "strace --seccomp-bpf: yon gade anba kapo a", https://fosdem.org/2020/schedule/event/debugging_strace_bpf/
  13. netsniff-ng: http://netsniff-ng.org/

Sous: www.habr.com

Add nouvo kòmantè