Дар ибтидо технология вуҷуд дошт ва он BPF ном дошт. Мо ба вай нигаристем
Тақрибан гӯем, BPF ба шумо имкон медиҳад, ки рамзи худсаронаи корбарро дар фазои ядрои Linux иҷро кунед ва меъмории нав он қадар муваффақ шуд, ки барои тавсифи ҳамаи замимаҳои он ба мо даҳҳо мақолаи дигар лозим мешавад. (Ягона чизе, ки таҳиягарон хуб кор накарданд, тавре ки шумо дар рамзи иҷрои зер мебинед, эҷоди логотипи арзанда буд.)
Дар ин мақола сохтори мошини виртуалии BPF, интерфейсҳои ядрои кор бо BPF, асбобҳои таҳия, инчунин шарҳи мухтасар ва хеле мухтасари қобилиятҳои мавҷуда, яъне. ҳама чизеро, ки ба мо дар оянда барои амиқтар омӯхтани татбиқи амалии BPF лозим аст.
Хулосаи мақола
bpf(2)
.
Пишем программы BPF с помощью libbpf
.libbpf
. Мо як скелети асосии барномаи BPF эҷод мекунем, ки мо дар мисолҳои минбаъда истифода хоҳем кард.
Муқаддима ба меъмории BPF
Пеш аз он ки мо ба баррасии меъмории BPF шурӯъ кунем, мо бори охир ба (оҳ) муроҷиат мекунем
BPF нав ҳамчун вокуниш ба паҳншавии мошинҳои 64-битӣ, хидматҳои абрӣ ва эҳтиёҷоти афзоянда ба асбобҳо барои эҷоди SDN таҳия шудааст (Sзуд-dмуайян карда шудааст nкор кардан). Аз ҷониби муҳандисони шабакаи ядро ҳамчун ивазкунандаи мукаммали классикии BPF таҳия шудааст, BPF-и нав аслан пас аз шаш моҳ барномаҳоро дар вазифаи душвори пайгирии системаҳои Linux пайдо кард ва ҳоло, пас аз шаш соли пайдоиши он, ба мо як мақолаи пурраи навбатӣ лозим мешавад. намудҳои гуногуни барномаҳоро номбар кунед.
Суратхои хандаовар
Дар асл, BPF як мошини маҷозии қуттии қум мебошад, ки ба шумо имкон медиҳад, ки рамзи "худсарона" -ро дар фазои ядро бидуни осеб ба амният иҷро кунед. Барномаҳои BPF дар фазои корбар эҷод карда, ба ядро бор карда мешаванд ва ба баъзе манбаи рӯйдодҳо пайваст мешаванд. Ҳодиса метавонад, масалан, интиқоли баста ба интерфейси шабака, ба кор андохтани баъзе функсияҳои ядро ва ғайра. Дар ҳолати баста, барномаи BPF ба маълумот ва метамаълумоти баста дастрасӣ дорад (барои хондан ва эҳтимолан навиштан вобаста ба намуди барнома); дар ҳолати иҷро кардани функсияи ядро аргументҳои функсия, аз ҷумла нишондиҳандаҳои хотираи ядро ва ғайра.
Биёед ин равандро муфассалтар дида бароем. Барои оғоз, биёед дар бораи фарқияти аввал аз классикии BPF сӯҳбат кунем, ки барномаҳо барои онҳо дар ассемблер навишта шудаанд. Дар версияи нав, меъморӣ васеъ карда шуд, то ки барномаҳо бо забонҳои сатҳи баланд, пеш аз ҳама, албатта, дар C навишта шаванд. Барои ин, backend барои llvm таҳия карда шуд, ки ба шумо имкон медиҳад байткод барои меъмории BPF тавлид карда шавад.
Меъмории BPF қисман тарҳрезӣ шудааст, ки дар мошинҳои муосир самаранок кор кунад. Барои амалӣ кардани ин кор, байткоди BPF, ки як бор ба ядро бор карда мешавад, бо истифода аз ҷузъе, ки компилятори JIT ном дорад, ба коди аслӣ тарҷума карда мешавад (JИСТ In Tим). Баъд, агар шумо дар хотир доред, дар BPF-и классикӣ барнома ба ядро бор карда шуда, ба манбаи ҳодиса ба таври атомӣ - дар контексти занги ягонаи система замима карда мешуд. Дар меъмории нав, ин дар ду марҳила сурат мегирад - аввал, код бо истифода аз занги система ба ядро бор карда мешавад. bpf(2)
ва баъдтар, тавассути механизмҳои дигар, ки вобаста ба намуди барнома фарқ мекунанд, барнома ба манбаи ҳодиса пайваст мешавад.
Дар ин ҷо шояд хонанда саволе дошта бошад: оё ин имконпазир аст? Бехатарии иҷрои чунин код чӣ гуна кафолат дода мешавад? Бехатарии иҷро ба мо бо марҳилаи боркунии барномаҳои BPF бо номи verifier кафолат дода мешавад (дар забони англисӣ ин марҳила verifier номида мешавад ва ман калимаи англисиро истифода мекунам):
Verifier як таҳлилгари статикӣ мебошад, ки кафолат медиҳад, ки барнома кори муқаррарии ядроро халалдор накунад. Дар омади гап, ин маънои онро надорад, ки барнома наметавонад ба кори система халал расонад - барномаҳои BPF, вобаста ба намуд, метавонанд қисмҳои хотираи ядро хонда ва аз нав сабт кунанд, арзишҳои функсияҳоро баргардонанд, бурида, замима, аз нав сабт кунанд. ва ҳатто бастаҳои шабакавӣ. Verifier кафолат медиҳад, ки иҷрои барномаи BPF ядроро вайрон намекунад ва барномае, ки тибқи қоидаҳо дастрасии навиштан дорад, масалан, маълумоти бастаи содиротӣ, наметавонад хотираи ядроро берун аз баста баргардонад. Мо пас аз шиносоӣ бо ҳамаи ҷузъҳои дигари BPF, мо verifierро каме муфассалтар дар фасли мувофиқ дида мебароем.
Пас, мо то ҳол чӣ омӯхтаем? Истифодабаранда дар C барнома менависад, онро бо истифода аз занги система ба ядро бор мекунад bpf(2)
, ки он аз ҷониби тафтишкунанда тафтиш карда мешавад ва ба байткоди аслӣ тарҷума карда мешавад. Сипас, ҳамон ё дигар корбар барномаро ба манбаи ҳодиса пайваст мекунад ва он ба иҷроиш оғоз мекунад. Ҷудо кардани боркунӣ ва пайвастшавӣ барои якчанд сабаб зарур аст. Аввалан, иҷро кардани санҷиш нисбатан гарон аст ва бо зеркашии як барнома чанд маротиба мо вақти компютерро беҳуда сарф мекунем. Сониян, маҳз чӣ гуна пайваст шудани барнома аз намуди он вобаста аст ва як интерфейси "универсалӣ", ки як сол пеш таҳия шуда буд, метавонад барои намудҳои нави барномаҳо мувофиқ набошад. (Гарчанде ки ҳоло меъморӣ баркамол шуда истодааст, идеяи муттаҳид кардани ин интерфейс дар сатҳ вуҷуд дорад. libbpf
.)
Хонандаи бодиққат шояд пайхас кунад, ки мо ҳанӯз бо тасвирҳо ба итмом нарасидем. Дар ҳақиқат, ҳама чизҳои дар боло зикршуда шарҳ намедиҳанд, ки чаро BPF тасвирро дар муқоиса бо BPF классикӣ ба таври куллӣ тағир медиҳад. Ду навоварие, ки доираи татбиқро ба таври назаррас васеъ мекунанд, қобилияти истифодаи хотираи муштарак ва функсияҳои ёрирасони ядро мебошанд. Дар BPF, хотираи муштарак бо истифода аз харитаҳо - сохторҳои муштараки додаҳо бо API мушаххас амалӣ карда мешавад. Эҳтимол онҳо ин номро гирифтаанд, зеро аввалин намуди харитае, ки пайдо шуд, ҷадвали ҳаш буд. Пас аз он массивҳо пайдо шуданд, мизҳои хэши маҳаллӣ (ба як CPU) ва массивҳои маҳаллӣ, дарахтони ҷустуҷӯ, харитаҳое, ки нишондодҳои барномаҳои BPF доранд ва ғайра. Ҳоло барои мо ҷолиб он аст, ки барномаҳои BPF ҳоло қобилияти нигоҳ доштани ҳолати байни зангҳо ва мубодилаи онро бо барномаҳои дигар ва фазои корбар доранд.
Харитаҳо аз равандҳои корбар бо истифода аз занги система дастрас карда мешаванд bpf(2)
, ва аз барномаҳои BPF, ки дар ядро бо истифода аз функсияҳои ёрирасон кор мекунанд. Гузашта аз ин, ёрдамчиён на танҳо барои кор бо харитаҳо, балки барои дастрасӣ ба дигар қобилиятҳои ядро низ мавҷуданд. Масалан, барномаҳои BPF метавонанд функсияҳои ёрирасонро барои интиқоли бастаҳо ба интерфейсҳои дигар, тавлиди рӯйдодҳои perf, дастрасӣ ба сохторҳои ядро ва ғайра истифода баранд.
Хулоса, BPF қобилияти бор кардани рамзи худсарона, яъне коди аз ҷониби тафтишкунанда санҷидашударо дар фазои ядро таъмин мекунад. Ин код метавонад ҳолати байни зангҳоро захира кунад ва маълумотро бо фазои корбар мубодила кунад ва инчунин ба зерсистемаҳои ядрои аз ҷониби ин намуди барнома иҷозатдодашуда дастрасӣ дорад.
Ин аллакай ба қобилиятҳои пешниҳодкардаи модулҳои ядро шабеҳ аст, ки дар муқоиса бо онҳо BPF баъзе бартариҳо дорад (албатта, шумо метавонед танҳо замимаҳои шабеҳро муқоиса кунед, масалан, пайгирии система - шумо наметавонед драйвери худсаронаро бо BPF нависед). Шумо метавонед ҳадди ақали вурудро қайд кунед (баъзе утилитҳое, ки BPF-ро истифода мебаранд, аз корбар малакаҳои барномасозии ядро ё умуман малакаҳои барномасозиро талаб намекунанд), бехатарии вақти корӣ (дасти худро дар шарҳҳо барои онҳое, ки ҳангоми навиштан системаро вайрон накардаанд, баланд кунед. ё модулҳои санҷишӣ), атомӣ - ҳангоми боркунии модулҳо вақти бекорӣ вуҷуд дорад ва зерсистемаи BPF кафолат медиҳад, ки ягон ҳодиса аз даст надиҳад (одилона гуфтан, ин барои ҳама намудҳои барномаҳои BPF дуруст нест).
Мавҷудияти чунин қобилиятҳо BPF-ро як воситаи универсалии васеъ кардани ядро мегардонад, ки дар амал тасдиқ карда шудааст: ба BPF ҳарчи бештар намудҳои нави барномаҳо илова карда мешаванд, шумораи бештари ширкатҳои калон BPF-ро дар серверҳои ҷангии 24×7 истифода мебаранд, бештар ва бештар. стартапҳо тиҷорати худро дар асоси қарорҳое бунёд мекунанд, ки ба BPF асос ёфтаанд. BPF дар ҳама ҷо истифода мешавад: ҳангоми муҳофизат аз ҳамлаҳои DDoS, эҷоди SDN (масалан, татбиқи шабакаҳо барои кубернетҳо), ҳамчун воситаи асосии пайгирии система ва коллектори омор, дар системаҳои ошкоркунии ҳамлаҳо ва системаҳои қуттии қум ва ғайра.
Биёед қисми шарҳи мақоларо дар ин ҷо ба анҷом расонем ва ба мошини виртуалӣ ва экосистемаи BPF муфассалтар назар андозем.
Дигрессия: коммуналӣ
Барои он ки мисолҳоро дар бахшҳои зерин иҷро карда тавонед, шумо метавонед ҳадди аққал як қатор утилитҳоро талаб кунед. llvm
/clang
бо дастгирии bpf ва bpftool
... Дар боб
Реестрҳои мошини виртуалии BPF ва системаи дастур
Системаи меъморӣ ва фармондиҳии BPF бо назардошти он таҳия шудааст, ки барномаҳо бо забони Си навишта мешаванд ва пас аз бор кардан ба ядро ба коди модарӣ тарҷума карда мешаванд. Аз ин рӯ, шумораи регистрҳо ва маҷмӯи фармонҳо бо назардошти чорроҳа, ба маънои математикӣ, имкониятҳои мошинҳои муосир интихоб карда шуданд. Гайр аз ин, барои барномахо махдудиятхои гуногун гузошта мешуданд, масалан, то вактхои охир навиштани циклхо ва зерпрограммахо имкон надошт ва шумораи дастурхо то 4096 адад махдуд мешуд (холо программахои имтиёзнок метавонанд то як миллион дастурро бор кунанд).
BPF дорои ёздаҳ регистрҳои 64-бит, ки барои корбар дастрас аст r0
-r10
ва ҳисобкунаки барнома. Бақайдгирӣ r10
дорои нишоннамои чаҳорчӯба аст ва танҳо барои хондан аст. Барномаҳо ба стеки 512-байтӣ дар вақти корӣ ва миқдори номаҳдуди хотираи муштарак дар шакли харитаҳо дастрасӣ доранд.
Ба барномаҳои BPF иҷозат дода мешавад, ки маҷмӯи мушаххаси ёварҳои навъи барномаро иҷро кунанд ва ба наздикӣ, функсияҳои муқаррарӣ. Ҳар як функсияи даъватшуда метавонад то панҷ аргумент гирад, ки дар регистрҳо интиқол дода мешаванд r1
-r5
, ва арзиши бозгашт ба интиқол дода мешавад r0
. Кафолат дода мешавад, ки пас аз бозгашт аз функсия, мундариҷаи реестрҳо r6
-r9
Дигар намешавад.
Барои тарҷумаи самараноки барнома, ба қайд мегирад r0
-r11
барои ҳама меъмориҳои дастгирӣшаванда ба таври беназир ба регистрҳои воқеӣ бо назардошти хусусиятҳои ABI-и меъмории ҷорӣ харита карда мешаванд. Масалан, барои x86_64
кайд мекунад r1
-r5
, ки барои гузаштани параметрҳои функсия истифода мешавад, нишон дода мешаванд rdi
, rsi
, rdx
, rcx
, r8
, ки барои интиқоли параметрҳо ба функсияҳо истифода мешаванд x86_64
. Масалан, рамзи дар тарафи чап буда ба рамзи тарафи рост чунин тарҷума мешавад:
1: (b7) r1 = 1 mov $0x1,%rdi
2: (b7) r2 = 2 mov $0x2,%rsi
3: (b7) r3 = 3 mov $0x3,%rdx
4: (b7) r4 = 4 mov $0x4,%rcx
5: (b7) r5 = 5 mov $0x5,%r8
6: (85) call pc+1 callq 0x0000000000001ee8
Сабти ном r0
инчунин барои баргардонидани натиҷаи иҷрои барнома ва дар реестр истифода мешавад r1
ба барнома ишоракунанда ба контекст интиқол дода мешавад - вобаста ба намуди барнома, ин метавонад, масалан, сохтор бошад struct xdp_md
struct __sk_buff
struct pt_regs
Ҳамин тавр, мо маҷмӯи регистрҳо, ёрирасонҳои ядро , стек, нишондиҳандаи контекстӣ ва хотираи муштарак дар шакли харитаҳо доштем. На ин ки ҳамаи ин дар сафар комилан зарур аст, аммо...
Биёед тавсифро идома дода, дар бораи системаи фармондиҳии кор бо ин объектҳо сӯҳбат кунем. Ҳама (
Ин аст, Code
- ин рамзгузории дастур аст, Dst
/Src
мутаносибан рамзгузории қабулкунанда ва манбаъ мебошанд, Off
- 16-бита абзор имзо, ва Imm
адади бутуни имзошудаи 32-бит аст, ки дар баъзе дастурҳо истифода мешавад (монанд ба доимии cBPF К). Рамзгузорӣ Code
яке аз ду намуд дорад:
Синфҳои дастурҳои 0, 1, 2, 3 фармонҳоро барои кор бо хотира муайян мекунанд. Онхо BPF_LD
, BPF_LDX
, BPF_ST
, BPF_STX
, мутаносибан. Синфҳои 4, 7 (BPF_ALU
, BPF_ALU64
) маҷмӯи дастурҳои ALU-ро ташкил медиҳанд. Синфҳои 5, 6 (BPF_JMP
, BPF_JMP32
) дорои дастурҳои ҷаҳиш аст.
Нақшаи минбаъдаи омӯзиши системаи дастури BPF чунин аст: ба ҷои бодиққат номбар кардани ҳамаи дастурҳо ва параметрҳои онҳо, мо дар ин бахш якчанд мисолҳоро дида мебароем ва аз онҳо маълум мешавад, ки дастурҳо воқеан чӣ гуна кор мекунанд ва чӣ гуна бояд ҳама гуна файли бинариро барои BPF дастӣ ҷудо кунед. Барои муттаҳид кардани мавод дертар дар мақола, мо инчунин бо дастурҳои инфиродӣ дар бахшҳо дар бораи Verifier, compiler JIT, тарҷумаи классикии BPF, инчунин ҳангоми омӯзиши харитаҳо, занги функсияҳо ва ғайра вохӯрем.
Вақте ки мо дар бораи дастурҳои инфиродӣ гап мезанем, мо ба файлҳои аслӣ муроҷиат мекунем bpf.h
bpf_common.h
Мисол: ҷудо кардани BPF дар сари шумо
Биёед мисолеро дида бароем, ки дар он мо барнома тартиб медиҳем readelf-example.c
ва ба дуӣ натиҷа нигаред. Мо мундариҷаи аслиро ошкор хоҳем кард readelf-example.c
дар зер, пас аз барқарор кардани мантиқи он аз рамзҳои дуӣ:
$ clang -target bpf -c readelf-example.c -o readelf-example.o -O2
$ llvm-readelf -x .text readelf-example.o
Hex dump of section '.text':
0x00000000 b7000000 01000000 15010100 00000000 ................
0x00000010 b7000000 02000000 95000000 00000000 ................
Сутуни якум дар баромад readelf
як банд аст ва барномаи мо аз ин рӯ аз чаҳор фармон иборат аст:
Code Dst Src Off Imm
b7 0 0 0000 01000000
15 0 1 0100 00000000
b7 0 0 0000 02000000
95 0 0 0000 00000000
Рамзҳои фармон баробаранд b7
, 15
, b7
и 95
. Ба ёд оред, ки се битҳои камтарин синфи дастур мебошанд. Дар ҳолати мо, бит чоруми ҳамаи дастурҳо холӣ аст, бинобар ин синфҳои дастурҳо мутаносибан 7, 5, 7, 5 мебошанд. BPF_ALU64
, ва 5 аст BPF_JMP
. Барои ҳарду синф формати дастур якхела аст (ниг.
Op S Class Dst Src Off Imm
b 0 ALU64 0 0 0 1
1 0 JMP 0 1 1 0
b 0 ALU64 0 0 0 2
9 0 JMP 0 0 0 0
Амалиёт b
дараҷа ALU64
Оё s
(сарчашма), он гоҳ кимат аз реестри манбаъ гирифта мешавад ва агар он тавре ки дар ҳолати мо муқаррар карда нашуда бошад, пас арзиш аз майдон гирифта мешавад. Imm
. Ҳамин тавр, дар дастурҳои якум ва сеюм мо амалиётро иҷро мекунем r0 = Imm
. Ғайр аз он, амалиёти JMP синфи 1 аст S
сифр аст, он арзиши реестри манбаъро бо майдон муқоиса мекунад Imm
. Агар арзишҳо мувофиқат кунанд, пас гузариш ба амал меояд PC + Off
ки дар PC
, чун маъмул, суроғаи дастури навбатӣ дорад. Дар ниҳоят, амалиёти JMP Синфи 9 аст BPF_EXIT
r0
. Биёед ба ҷадвали худ сутуни нав илова кунем:
Op S Class Dst Src Off Imm Disassm
MOV 0 ALU64 0 0 0 1 r0 = 1
JEQ 0 JMP 0 1 1 0 if (r1 == 0) goto pc+1
MOV 0 ALU64 0 0 0 2 r0 = 2
EXIT 0 JMP 0 0 0 0 exit
Мо метавонем инро дар шакли қулай аз нав нависем:
r0 = 1
if (r1 == 0) goto END
r0 = 2
END:
exit
Агар дар хотир дошта бошем, ки дар реестр он чиро r1
барнома як ишора ба контекст аз ядро, ва дар реестр гузаронида мешавад r0
арзиш ба ядро баргардонида мешавад, пас мо мебинем, ки агар нишондиханда ба контекст сифр бошад, пас мо 1-ро бармегардонем ва дар акси ҳол - 2. Биёед тафтиш кунем, ки дурустии мо ба манбаъ нигоҳ карда мешавад:
$ cat readelf-example.c
int foo(void *ctx)
{
return ctx ? 2 : 1;
}
Бале, ин як барномаи бемаънист, аммо он танҳо ба чаҳор дастури оддӣ тарҷума мешавад.
Мисоли истисно: дастури 16-байтӣ
Мо қаблан гуфта будем, ки баъзе дастурҳо зиёда аз 64 битро мегиранд. Ин, масалан, ба дастурҳо дахл дорад lddw
(Рамз = 0x18
= BPF_LD
BPF_DW
BPF_IMM
Imm
. Гап дар сари он аст Imm
дорои андозаи 32 ва калимаи дугона 64 бит аст, аз ин рӯ бор кардани арзиши фаврии 64-бит ба реестр дар як дастури 64-битӣ кор намекунад. Барои ин ду дастури ҳамшафат барои нигоҳ доштани қисми дуюми арзиши 64-бит дар майдон истифода мешаванд Imm
. Мисол:
$ cat x64.c
long foo(void *ctx)
{
return 0x11223344aabbccdd;
}
$ clang -target bpf -c x64.c -o x64.o -O2
$ llvm-readelf -x .text x64.o
Hex dump of section '.text':
0x00000000 18000000 ddccbbaa 00000000 44332211 ............D3".
0x00000010 95000000 00000000 ........
Дар барномаи дуӣ танҳо ду дастур мавҷуд аст:
Binary Disassm
18000000 ddccbbaa 00000000 44332211 r0 = Imm[0]|Imm[1]
95000000 00000000 exit
Мо боз бо дастурҳо вомехӯрем lddw
, вакте ки мо дар бораи кучидан ва кор кардан бо харитахо сухан меронем.
Мисол: ҷудо кардани BPF бо истифода аз асбобҳои стандартӣ
Ҳамин тавр, мо хондани рамзҳои дуии BPF-ро ёд гирифтем ва дар ҳолати зарурӣ омодаем ҳама дастурҳоро таҳлил кунем. Бо вуҷуди ин, бояд гуфт, ки дар амал тақсим кардани барномаҳо бо истифода аз асбобҳои стандартӣ қулайтар ва тезтар аст, масалан:
$ llvm-objdump -d x64.o
Disassembly of section .text:
0000000000000000 <foo>:
0: 18 00 00 00 dd cc bb aa 00 00 00 00 44 33 22 11 r0 = 1234605617868164317 ll
2: 95 00 00 00 00 00 00 00 exit
Давраи ҳаёти объектҳои BPF, системаи файлии bpffs
(Ман аввал баъзе тафсилоти дар ин зербоб тавсифшударо аз
Объектҳои BPF - барномаҳо ва харитаҳо - аз фазои корбар бо истифода аз фармонҳо сохта мешаванд BPF_PROG_LOAD
и BPF_MAP_CREATE
занги система bpf(2)
, мо дар бораи маҳз чӣ гуна ин воқеа дар боби оянда сӯҳбат хоҳем кард. Ин сохторҳои додаҳои ядроиро ва барои ҳар яки онҳо эҷод мекунад refcount
(шумораи истинод) ба як муқаррар карда мешавад ва тасвири файле, ки ба объект ишора мекунад, ба корбар баргардонида мешавад. Пас аз баста шудани даста refcount
объект як кам мешавад ва ба сифр мерасад, объект нобуд мешавад.
Агар барнома харитаҳоро истифода барад, пас refcount
ин харитаҳо пас аз боркунии барнома як маротиба зиёд мешаванд, яъне. тавсифкунандагони файли онҳо метавонанд аз раванди корбар баста шаванд ва ҳоло ҳам refcount
сифр нахоҳад шуд:
Пас аз бомуваффақият бор кардани барнома, мо одатан онро ба як навъ генератори ҳодиса замима мекунем. Масалан, мо метавонем онро дар интерфейси шабакавӣ барои коркарди бастаҳои воридотӣ ё пайваст кардани он ба баъзеҳо ҷойгир кунем tracepoint
дар асл. Дар ин лаҳза ҳисобкунаки истинод низ як маротиба зиёд мешавад ва мо метавонем дескриптори файлро дар барномаи боркунак пӯшем.
Агар мо ҳоло боркунакро хомӯш кунем, чӣ мешавад? Он аз намуди генератори ҳодиса (қалмоқ) вобаста аст. Ҳама қалмоқҳои шабакавӣ пас аз ба итмом расидани боркунак вуҷуд хоҳанд дошт, инҳо қалмоқҳои ба истилоҳ глобалӣ мебошанд. Ва, масалан, барномаҳои пайгирӣ пас аз қатъ шудани раванде, ки онҳоро эҷод кардааст (ва аз ин рӯ, аз "маҳаллӣ ба раванд" маҳаллӣ номида мешаванд) бароварда мешаванд. Аз ҷиҳати техникӣ, қалмоқҳои маҳаллӣ ҳамеша дар фазои корбар тавсифкунандаи файли мувофиқ доранд ва аз ин рӯ, ҳангоми баста шудани раванд пӯшида мешаванд, аммо қалмоқҳои глобалӣ не. Дар расми зерин, бо истифода аз салибҳои сурх, ман кӯшиш мекунам нишон диҳам, ки чӣ гуна қатъи барномаи боркунак ба мӯҳлати истифодаи объектҳо дар ҳолати қалмоқҳои маҳаллӣ ва глобалӣ таъсир мерасонад.
Чаро байни қалмоқҳои маҳаллӣ ва ҷаҳонӣ фарқият вуҷуд дорад? Иҷрои баъзе намудҳои барномаҳои шабакавӣ бе фазои корбарон маъно дорад, масалан, тасаввур кунед, ки муҳофизати DDoS - боркунак қоидаҳоро менависад ва барномаи BPF-ро ба интерфейси шабака пайваст мекунад, ки пас аз он пурборкунанда метавонад рафта, худро бикушад. Аз тарафи дигар, тасаввур кунед, ки барномаи пайгирии ислоҳи он, ки шумо дар даҳ дақиқа ба зонуҳои худ навиштед - вақте ки он ба итмом мерасад, шумо мехоҳед, ки дар система партов намонад ва қалмоқҳои маҳаллӣ инро таъмин мекунанд.
Аз тарафи дигар, тасаввур кунед, ки шумо мехоҳед ба нуқтаи пайгирӣ дар ядро пайваст шавед ва дар тӯли солҳои зиёд омор ҷамъ кунед. Дар ин ҳолат, шумо мехоҳед қисми корбарро анҷом диҳед ва гоҳ-гоҳ ба омор баргардед. Системаи файлии bpf ин имкониятро фароҳам меорад. Ин як системаи псевдофайлӣ дар хотира мебошад, ки имкон медиҳад файлҳоеро эҷод кунад, ки ба объектҳои BPF истинод мекунанд ва ба ин васила зиёд мешаванд. refcount
объектхо. Пас аз ин, боркунак метавонад берун шавад ва объектҳои офаридааш зинда боқӣ мемонанд.
Эҷоди файлҳо дар bpffs, ки ба объектҳои BPF истинод мекунанд, "пайвасткунӣ" номида мешавад (чунон ки дар ибораи зерин: "раванд метавонад барномаи BPF ё харитаро пайваст кунад"). Эҷоди объектҳои файлӣ барои объектҳои BPF на танҳо барои дароз кардани ҳаёти объектҳои маҳаллӣ, балки инчунин барои корношоямии объектҳои глобалӣ маъно дорад - ба мисол бо барномаи глобалии муҳофизати DDoS бармегардем, мо мехоҳем омада, ба омор нигоҳ кунем. дам ба дам.
Системаи файлии BPF одатан дар васл карда мешавад /sys/fs/bpf
, аммо он инчунин метавонад ба таври маҳаллӣ насб карда шавад, масалан, ба ин монанд:
$ mkdir bpf-mountpoint
$ sudo mount -t bpf none bpf-mountpoint
Номҳои системаи файлӣ бо истифода аз фармон сохта мешаванд BPF_OBJ_PIN
Занг ба системаи BPF. Барои мисол, биёед барномаеро гирем, онро тартиб диҳем, онро бор кунем ва ба он пайваст кунем bpffs
. Барномаи мо ҳеҷ чизи муфиде намекунад, мо танҳо кодро пешниҳод мекунем, то шумо мисолро дубора тавлид кунед:
$ cat test.c
__attribute__((section("xdp"), used))
int test(void *ctx)
{
return 0;
}
char _license[] __attribute__((section("license"), used)) = "GPL";
Биёед ин барномаро тартиб дода, нусхаи локалии системаи файлиро созем bpffs
:
$ clang -target bpf -c test.c -o test.o
$ mkdir bpf-mountpoint
$ sudo mount -t bpf none bpf-mountpoint
Акнун биёед барномаи худро бо истифода аз утилита зеркашӣ кунем bpftool
ва ба зангҳои системаи ҳамроҳ назар кунед bpf(2)
(баъзе сатрҳои номатлуб аз баромади strace хориҷ карда шудаанд):
$ sudo strace -e bpf bpftool prog load ./test.o bpf-mountpoint/test
bpf(BPF_PROG_LOAD, {prog_type=BPF_PROG_TYPE_XDP, prog_name="test", ...}, 120) = 3
bpf(BPF_OBJ_PIN, {pathname="bpf-mountpoint/test", bpf_fd=3}, 120) = 0
Дар ин ҷо мо бо истифода аз барнома бор кардем BPF_PROG_LOAD
, дескриптори файлро аз ядро гирифт 3
ва бо истифода аз фармон BPF_OBJ_PIN
ин дескриптори файлро ҳамчун файл пайваст кард "bpf-mountpoint/test"
. Пас аз ин, барномаи пурборкунанда bpftool
кор ба охир расид, аммо барномаи мо дар ядро монд, гарчанде ки мо онро ба ягон интерфейси шабака пайваст накардаем:
$ sudo bpftool prog | tail -3
783: xdp name test tag 5c8ba0cf164cb46c gpl
loaded_at 2020-05-05T13:27:08+0000 uid 0
xlated 24B jited 41B memlock 4096B
Мо метавонем объекти файлро ба таври муқаррарӣ нест кунем unlink(2)
ва баъд аз он барномаи мувофиқ нест карда мешавад:
$ sudo rm ./bpf-mountpoint/test
$ sudo bpftool prog show id 783
Error: get by id (783): No such file or directory
Нест кардани объектҳо
Дар бораи нест кардани объектҳо сухан ронда, бояд фаҳмонем, ки пас аз он ки мо барномаро аз қалмоқ (генератори ҳодисаҳо) ҷудо кардем, ягон ҳодисаи нав ба оғози он роҳ намедиҳад, аммо ҳама намунаҳои ҷории барнома бо тартиби муқаррарӣ анҷом дода мешаванд. .
Баъзе намудҳои барномаҳои BPF ба шумо имкон медиҳанд, ки барномаро дар парвоз иваз кунед, яъне. атомии пайдарпайро таъмин мекунанд replace = detach old program, attach new program
. Дар ин ҳолат, ҳамаи инстансияҳои фаъоли версияи кӯҳнаи барнома кори худро анҷом медиҳанд ва коркардкунандагони нави ҳодисаҳо аз барномаи нав сохта мешаванд ва "атомӣ" дар ин ҷо маънои онро дорад, ки ягон ҳодиса аз даст нахоҳанд рафт.
Замимаи барномаҳо ба манбаъҳои рӯйдодҳо
Дар ин мақола мо пайваст кардани барномаҳоро ба манбаъҳои рӯйдодҳо алоҳида тавсиф намекунем, зеро омӯзиши ин дар заминаи як намуди мушаххаси барнома маъно дорад. См.
Идоракунии объектҳо бо истифода аз занги системаи bpf
Барномаҳои BPF
Ҳама объектҳои BPF аз фазои корбар бо истифода аз занги система сохта ва идора карда мешаванд bpf
, дорои прототипи зерин:
#include <linux/bpf.h>
int bpf(int cmd, union bpf_attr *attr, unsigned int size);
Ана команда cmd
яке аз арзишҳои намуд мебошад enum bpf_cmd
attr
— ишора ба параметрҳои барномаи мушаххас ва size
— андозаи объект аз руи нишондиханда, яъне. одатан ин sizeof(*attr)
. Дар ядрои 5.8 занги система bpf
дастгирӣ 34 фармонҳои гуногун, ва union bpf_attr
200 сатрро ишгол мекунад. Аммо мо набояд аз ин тарсонем, зеро дар тӯли якчанд мақола мо бо фармонҳо ва параметрҳо шинос мешавем.
Биёед аз даста оғоз кунем BPF_PROG_LOAD
, ки барномаҳои BPF-ро эҷод мекунад - маҷмӯи дастурҳои BPF-ро гирифта, онро ба ядро бор мекунад. Дар лахзаи боркуни верификатор ба кор андохта мешавад ва баъд компилятори JIT ва пас аз ичрои бомуваффакият дескриптори файли барнома ба корбар баргардонида мешавад. Мо дар фасли қаблӣ дидем, ки бо ӯ чӣ мешавад
Мо ҳоло як барномаи фармоишӣ менависем, ки барномаи оддии BPF-ро бор мекунад, аммо аввал мо бояд муайян кунем, ки кадом барномаро бор кардан мехоҳем - мо бояд интихоб кунем BPF_PROG_TYPE_XDP
, ки арзишро бармегардонад XDP_PASS
(ҳама бастаҳоро гузаред). Дар assembler BPF он хеле содда менамояд:
r0 = 2
exit
Пас аз он ки мо тасмим гирифтем он мо боргузорӣ мекунем, мо метавонем ба шумо бигӯем, ки чӣ тавр ин корро мекунем:
#define _GNU_SOURCE
#include <string.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <linux/bpf.h>
static inline __u64 ptr_to_u64(const void *ptr)
{
return (__u64) (unsigned long) ptr;
}
int main(void)
{
struct bpf_insn insns[] = {
{
.code = BPF_ALU64 | BPF_MOV | BPF_K,
.dst_reg = BPF_REG_0,
.imm = XDP_PASS
},
{
.code = BPF_JMP | BPF_EXIT
},
};
union bpf_attr attr = {
.prog_type = BPF_PROG_TYPE_XDP,
.insns = ptr_to_u64(insns),
.insn_cnt = sizeof(insns)/sizeof(insns[0]),
.license = ptr_to_u64("GPL"),
};
strncpy(attr.prog_name, "woo", sizeof(attr.prog_name));
syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr));
for ( ;; )
pause();
}
Ҳодисаҳои ҷолиб дар барнома аз таърифи массив оғоз мешаванд insns
- барномаи BPF мо дар рамзи мошин. Дар ин ҳолат, ҳар як дастури барномаи BPF дар сохтор баста мешавад bpf_insn
insns
ба дастурҳо мувофиқат мекунад r0 = 2
, дуюм - exit
.
Акибнишинӣ. Ядро макросҳои мувофиқтарро барои навиштани рамзҳои мошин ва истифодаи файли сарлавҳаи ядро муайян мекунад tools/include/linux/filter.h
навишта метавонистем
struct bpf_insn insns[] = {
BPF_MOV64_IMM(BPF_REG_0, XDP_PASS),
BPF_EXIT_INSN()
};
Аммо азбаски навиштани барномаҳои BPF бо коди аслӣ танҳо барои навиштани тестҳо дар ядро ва мақолаҳо дар бораи BPF зарур аст, мавҷуд набудани ин макросҳо ҳаёти таҳиягарро аслан мушкил намекунад.
Пас аз муайян кардани барномаи BPF, мо ба боркунии он ба ядро мегузарем. Маҷмӯи параметрҳои минималистии мо attr
навъи барнома, маҷмӯа ва шумораи дастурҳо, иҷозатномаи зарурӣ ва номро дар бар мегирад "woo"
, ки мо барои дарёфти барномаи худ дар система пас аз зеркашӣ истифода мебарем. Барнома, тавре ки ваъда шудааст, бо истифода аз занги система ба система бор карда мешавад bpf
.
Дар охири барнома мо ба як ҳалқаи беохир мерасем, ки сарбориро симулятсия мекунад. Бе он, вақте ки дескриптори файле, ки занги система ба мо бармегардонад, барнома аз ҷониби ядро кушта мешавад. bpf
, ва мо онро дар система намебинем.
Хуб, мо барои санҷиш омодаем. Биёед барномаро дар зери он ҷамъ кунем ва иҷро кунем strace
санҷед, ки ҳама чиз тавре ки лозим аст, кор мекунад:
$ clang -g -O2 simple-prog.c -o simple-prog
$ sudo strace ./simple-prog
execve("./simple-prog", ["./simple-prog"], 0x7ffc7b553480 /* 13 vars */) = 0
...
bpf(BPF_PROG_LOAD, {prog_type=BPF_PROG_TYPE_XDP, insn_cnt=2, insns=0x7ffe03c4ed50, license="GPL", log_level=0, log_size=0, log_buf=NULL, kern_version=KERNEL_V
ERSION(0, 0, 0), prog_flags=0, prog_name="woo", prog_ifindex=0, expected_attach_type=BPF_CGROUP_INET_INGRESS}, 72) = 3
pause(
Ҳама чиз хуб аст, bpf(2)
баргашт дастаки 3 ба мо ва мо ба як ҳалқаи беохир бо рафт pause()
. Биёед кӯшиш кунем, ки барномаи худро дар система пайдо кунем. Барои ин мо ба терминали дигар меравем ва утилитаро истифода мебарем bpftool
:
# bpftool prog | grep -A3 woo
390: xdp name woo tag 3b185187f1855c4c gpl
loaded_at 2020-08-31T24:66:44+0000 uid 0
xlated 16B jited 40B memlock 4096B
pids simple-prog(10381)
Мо мебинем, ки дар система як барномаи пурбор мавҷуд аст woo
ки ID-и глобалии он 390 аст ва дар ҳоли ҳозир идома дорад simple-prog
Дескриптори файли кушода мавҷуд аст, ки ба барнома ишора мекунад (ва агар simple-prog
корро ба охир мерасонад, пас woo
нопадид мешавад). Чунон ки пешбинй шуда буд, программа woo
16 байт - ду дастур - рамзҳои дуӣ дар меъмории BPF мегирад, аммо дар шакли аслии худ (x86_64) он аллакай 40 байт аст. Биёед барномаи худро дар шакли аслии он бубинем:
# bpftool prog dump xlated id 390
0: (b7) r0 = 2
1: (95) exit
сюрприз нест. Акнун биёед ба коди аз ҷониби компилятори JIT тавлидшуда назар андозем:
# bpftool prog dump jited id 390
bpf_prog_3b185187f1855c4c_woo:
0: nopl 0x0(%rax,%rax,1)
5: push %rbp
6: mov %rsp,%rbp
9: sub $0x0,%rsp
10: push %rbx
11: push %r13
13: push %r14
15: push %r15
17: pushq $0x0
19: mov $0x2,%eax
1e: pop %rbx
1f: pop %r15
21: pop %r14
23: pop %r13
25: pop %rbx
26: leaveq
27: retq
барои он чандон самаранок нест exit(2)
, аммо аз рӯи адолат, барномаи мо хеле содда аст ва барои барномаҳои ғайриоддӣ муқаддима ва эпилоги иловакардаи мураттиби JIT, албатта, лозим аст.
Харитаҳо
Барномаҳои BPF метавонанд минтақаҳои хотираи сохториро истифода баранд, ки ҳам барои дигар барномаҳои BPF ва ҳам барои барномаҳо дар фазои корбар дастрасанд. Ин объектҳоро харитаҳо меноманд ва дар ин бахш мо нишон медиҳем, ки чӣ тавр онҳоро бо истифода аз занги система идора кардан мумкин аст bpf
.
Дарҳол бигӯем, ки имкониятҳои харитаҳо танҳо бо дастрасӣ ба хотираи муштарак маҳдуд нестанд. Харитаҳои таъиноти махсус мавҷуданд, ки масалан, нишондиҳандаҳои барномаҳои BPF ё ишоракунакҳо ба интерфейсҳои шабакавӣ, харитаҳо барои кор бо рӯйдодҳои perf ва ғайра доранд. Мо дар ин ҷо дар бораи онҳо сухан намегӯем, то хонандаро ба иштибоҳ наандозад. Ба ғайр аз ин, мо масъалаҳои синхронизатсияро нодида мегирем, зеро ин барои мисолҳои мо муҳим нест. Рӯйхати пурраи намудҳои харитаи дастрасро дар ин ҷо пайдо кардан мумкин аст <linux/bpf.h>
BPF_MAP_TYPE_HASH
.
Агар шумо дар C++ ҷадвали ҳаш эҷод кунед, шумо мегӯед unordered_map<int,long> woo
, ки ба забони русй маънои «Ба ман миз лозим аст woo
андозаи номаҳдуд, ки калидҳои онҳо навъи мебошанд int
, ва арзишҳо навъи мебошанд long
" Барои сохтани ҷадвали hash BPF, мо бояд ҳамон корро кунем, ба истиснои он ки мо бояд андозаи максималии ҷадвалро муайян кунем ва ба ҷои муайян кардани намудҳои калидҳо ва арзишҳо, мо бояд андозаи онҳоро дар байт муайян кунем . Барои сохтани харитаҳо фармонро истифода баред BPF_MAP_CREATE
занги система bpf
. Биёед як барномаи кам ё камтарини минималиро дида бароем, ки харитаро месозад. Пас аз барномаи қаблӣ, ки барномаҳои BPF-ро бор мекунад, ин барнома бояд барои шумо оддӣ бошад:
$ cat simple-map.c
#define _GNU_SOURCE
#include <string.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <linux/bpf.h>
int main(void)
{
union bpf_attr attr = {
.map_type = BPF_MAP_TYPE_HASH,
.key_size = sizeof(int),
.value_size = sizeof(int),
.max_entries = 4,
};
strncpy(attr.map_name, "woo", sizeof(attr.map_name));
syscall(__NR_bpf, BPF_MAP_CREATE, &attr, sizeof(attr));
for ( ;; )
pause();
}
Дар ин ҷо мо маҷмӯи параметрҳоро муайян мекунем attr
, ки дар он мо мегӯем "Ба ман ҷадвали ҳаш бо калидҳо ва арзишҳои андоза лозим аст sizeof(int)
, ки дар он ман метавонам ҳадди аксар чор элементро ҷойгир кунам." Ҳангоми сохтани харитаҳои BPF, шумо метавонед параметрҳои дигарро нишон диҳед, масалан, ҳамон тавре ки дар мисоли барнома, мо номи объектро ҳамчун "woo"
.
Биёед барномаро тартиб ва иҷро кунем:
$ clang -g -O2 simple-map.c -o simple-map
$ sudo strace ./simple-map
execve("./simple-map", ["./simple-map"], 0x7ffd40a27070 /* 14 vars */) = 0
...
bpf(BPF_MAP_CREATE, {map_type=BPF_MAP_TYPE_HASH, key_size=4, value_size=4, max_entries=4, map_name="woo", ...}, 72) = 3
pause(
Ин аст занги система bpf(2)
рақами харитаи дескрипторро ба мо баргардонд 3
ва он гоҳ барнома, тавре интизор меравад, дастурҳои минбаъдаро дар занги система интизор аст pause(2)
.
Акнун биёед барномаи худро ба замина фиристем ё терминали дигарро кушоем ва бо истифода аз утилита объекти худро бубинем bpftool
(мо метавонем харитаи худро аз дигарон бо номаш фарқ кунем):
$ sudo bpftool map
...
114: hash name woo flags 0x0
key 4B value 4B max_entries 4 memlock 4096B
...
Рақами 114 ID-и глобалии объекти мост. Ҳар як барномаи система метавонад ин ID-ро барои кушодани харитаи мавҷуда бо истифода аз фармон истифода барад BPF_MAP_GET_FD_BY_ID
занги система bpf
.
Акнун мо метавонем бо мизи hash худ бозӣ кунем. Биёед ба мундариҷаи он назар андозем:
$ sudo bpftool map dump id 114
Found 0 elements
холӣ. Биёед ба он арзише гузорем hash[1] = 1
:
$ sudo bpftool map update id 114 key 1 0 0 0 value 1 0 0 0
Биёед бори дигар ба ҷадвал назар андозем:
$ sudo bpftool map dump id 114
key: 01 00 00 00 value: 01 00 00 00
Found 1 element
Ура! Мо тавонистем як элементро илова кунем. Аҳамият диҳед, ки мо бояд дар сатҳи байт кор кунем, зеро ин корро анҷом диҳем bptftool
намедонад, ки арзишҳо дар ҷадвали hash кадом навъианд. (Ин донишро бо истифода аз BTF ба ӯ интиқол додан мумкин аст, аммо бештар дар ин бора ҳоло.)
Чӣ тавр маҳз bpftool унсурҳоро мехонад ва илова мекунад? Биёед ба зери сарпӯш назар андозем:
$ sudo strace -e bpf bpftool map dump id 114
bpf(BPF_MAP_GET_FD_BY_ID, {map_id=114, next_id=0, open_flags=0}, 120) = 3
bpf(BPF_MAP_GET_NEXT_KEY, {map_fd=3, key=NULL, next_key=0x55856ab65280}, 120) = 0
bpf(BPF_MAP_LOOKUP_ELEM, {map_fd=3, key=0x55856ab65280, value=0x55856ab652a0}, 120) = 0
key: 01 00 00 00 value: 01 00 00 00
bpf(BPF_MAP_GET_NEXT_KEY, {map_fd=3, key=0x55856ab65280, next_key=0x55856ab65280}, 120) = -1 ENOENT
Аввалан мо бо истифода аз фармон харитаро бо ID глобалии он кушодем BPF_MAP_GET_FD_BY_ID
и bpf(2)
дескриптори 3-ро ба мо баргардонд.. Минбаъд бо истифода аз фармон BPF_MAP_GET_NEXT_KEY
мо калиди якумро дар чадвал бо рохи гузаш-тан ёфтем NULL
ҳамчун ишора ба калиди "пештара". Агар мо калид дошта бошем, мо метавонем BPF_MAP_LOOKUP_ELEM
ки арзишро ба нишондиханда бармегардонад value
. Қадами навбатӣ ин аст, ки мо кӯшиш мекунем, ки элементи навбатиро тавассути гузариш ба калиди ҷорӣ пайдо кунем, аммо ҷадвали мо танҳо як элемент ва фармонро дар бар мегирад. BPF_MAP_GET_NEXT_KEY
бармегардад ENOENT
.
Хуб, биёед арзишро бо калиди 1 иваз кунем, бигӯем, ки мантиқи тиҷоратии мо сабти номро талаб мекунад hash[1] = 2
:
$ sudo strace -e bpf bpftool map update id 114 key 1 0 0 0 value 2 0 0 0
bpf(BPF_MAP_GET_FD_BY_ID, {map_id=114, next_id=0, open_flags=0}, 120) = 3
bpf(BPF_MAP_UPDATE_ELEM, {map_fd=3, key=0x55dcd72be260, value=0x55dcd72be280, flags=BPF_ANY}, 120) = 0
Тавре ки интизор мерафт, ин хеле содда аст: фармон BPF_MAP_GET_FD_BY_ID
харитаи моро бо ID мекушояд, ва фармон BPF_MAP_UPDATE_ELEM
элементро аз нав менависад.
Ҳамин тариқ, пас аз сохтани ҷадвали ҳаш аз як барнома, мо метавонем мундариҷаи онро аз барномаи дигар хонем ва нависем. Дар хотир доред, ки агар мо инро аз сатри фармон иҷро карда тавонистем, он гоҳ ягон барномаи дигари система метавонад ин корро кунад. Илова ба фармонҳои дар боло тавсифшуда, барои кор бо харитаҳо аз фазои корбар,
BPF_MAP_LOOKUP_ELEM
: пайдо кардани арзиш аз рӯи калидBPF_MAP_UPDATE_ELEM
: навсозӣ/эҷоди арзишBPF_MAP_DELETE_ELEM
: калидро хориҷ кунедBPF_MAP_GET_NEXT_KEY
: калиди навбатӣ (ё аввал)-ро пайдо кунедBPF_MAP_GET_NEXT_ID
: ба шумо имкон медиҳад, ки аз тамоми харитаҳои мавҷуда гузаред, ҳамин тавр кор мекунадbpftool map
BPF_MAP_GET_FD_BY_ID
: харитаи мавҷударо аз рӯи ID глобалии он кушоедBPF_MAP_LOOKUP_AND_DELETE_ELEM
: арзиши объектро ба таври атомӣ навсозӣ кунед ва объекти кӯҳнаро баргардонедBPF_MAP_FREEZE
: харитаро аз фазои корбарон тағйирнопазир гардонед (ин амалро бекор кардан мумкин нест)BPF_MAP_LOOKUP_BATCH
,BPF_MAP_LOOKUP_AND_DELETE_BATCH
,BPF_MAP_UPDATE_BATCH
,BPF_MAP_DELETE_BATCH
: амалиёти оммавй. Барои намуна,BPF_MAP_LOOKUP_AND_DELETE_BATCH
- ин ягона роҳи боэътимоди хондан ва барқарор кардани ҳама арзишҳо аз харита аст
На ҳамаи ин фармонҳо барои ҳама намудҳои харитаҳо кор мекунанд, аммо дар маҷмӯъ кор бо дигар намудҳои харитаҳо аз фазои корбар маҳз ба кор бо ҷадвалҳои ҳаш монанд ба назар мерасад.
Барои тартиб, биёед таҷрибаҳои ҷадвали ҳашро ба анҷом расонем. Дар хотир доред, ки мо ҷадвалеро офаридаем, ки то чор калидро дар бар гирад? Биёед якчанд унсурҳои дигар илова кунем:
$ sudo bpftool map update id 114 key 2 0 0 0 value 1 0 0 0
$ sudo bpftool map update id 114 key 3 0 0 0 value 1 0 0 0
$ sudo bpftool map update id 114 key 4 0 0 0 value 1 0 0 0
То ҳоло ҳамааш хуб:
$ sudo bpftool map dump id 114
key: 01 00 00 00 value: 01 00 00 00
key: 02 00 00 00 value: 01 00 00 00
key: 04 00 00 00 value: 01 00 00 00
key: 03 00 00 00 value: 01 00 00 00
Found 4 elements
Биёед кӯшиш кунем, ки як чизи дигарро илова кунем:
$ sudo bpftool map update id 114 key 5 0 0 0 value 1 0 0 0
Error: update failed: Argument list too long
Чунон ки интизор будем, ба мо муяссар нашуд. Биёед ба хатогиҳо муфассалтар назар андозем:
$ sudo strace -e bpf bpftool map update id 114 key 5 0 0 0 value 1 0 0 0
bpf(BPF_MAP_GET_FD_BY_ID, {map_id=114, next_id=0, open_flags=0}, 120) = 3
bpf(BPF_OBJ_GET_INFO_BY_FD, {info={bpf_fd=3, info_len=80, info=0x7ffe6c626da0}}, 120) = 0
bpf(BPF_MAP_UPDATE_ELEM, {map_fd=3, key=0x56049ded5260, value=0x56049ded5280, flags=BPF_ANY}, 120) = -1 E2BIG (Argument list too long)
Error: update failed: Argument list too long
+++ exited with 255 +++
Ҳама чиз хуб аст: тавре ки интизор мерафт, даста BPF_MAP_UPDATE_ELEM
кӯшиш мекунад, ки калиди нави панҷумро эҷод кунад, аммо ба садама дучор мешавад E2BIG
.
Ҳамин тавр, мо метавонем барномаҳои BPF эҷод ва бор кунем, инчунин аз фазои корбар харитаҳоро эҷод ва идора кунем. Ҳоло мантиқист, ки мо метавонем харитаҳоро аз худи барномаҳои BPF истифода барем. Мо метавонем дар ин бора бо забони барномаҳои хонданашон душвор дар кодҳои макросимҳои мошин сӯҳбат кунем, аммо дар асл вақти нишон додани он расидааст, ки барномаҳои BPF воқеан чӣ гуна навишта ва нигоҳ дошта мешаванд - бо истифода аз libbpf
.
(Барои хонандагоне, ки аз набудани мисоли сатҳи паст норозианд: мо барномаҳоеро муфассал таҳлил хоҳем кард, ки харитаҳо ва функсияҳои ёрирасонро истифода мебаранд, ки бо истифода аз онҳо сохта шудаанд. libbpf
ва ба шумо бигӯед, ки дар сатҳи дастур чӣ рӯй медиҳад. Барои хонандагоне, ки норозианд хеле зиёд, илова кардем
Навиштани барномаҳои BPF бо истифода аз libbpf
Навиштани барномаҳои BPF бо истифода аз рамзҳои мошинӣ метавонад танҳо бори аввал ҷолиб бошад ва баъд серӣ ба вуҷуд меояд. Дар ин лаҳза шумо бояд диққати худро ба он равона кунед llvm
, ки дорои пуштибони барои тавлиди код барои меъмории BPF, инчунин китобхона libbpf
, ки ба шумо имкон медиҳад тарафи корбарии замимаҳои BPF нависед ва рамзи барномаҳои BPF-ро бо истифода аз тавлидшуда бор кунед llvm
/clang
.
Дарвоқеъ, чунон ки мо дар ин мақола ва мақолаҳои минбаъда мебинем. libbpf
бе он кори зиёдеро анҷом медиҳад (ё асбобҳои шабеҳ - iproute2
, libbcc
, libbpf-go
ва гайра) зистан мумкин нест. Яке аз хусусиятҳои қотилони лоиҳа libbpf
аст BPF CO-RE (Compile Once, Run Everywhere) - лоиҳае, ки ба шумо имкон медиҳад, ки барномаҳои BPF-ро нависед, ки аз як ядро ба ядрои дигар интиқолшаванда бо қобилияти кор кардан дар API-ҳои гуногун (масалан, вақте ки сохтори ядро аз версия тағир меёбад) ба версия). Барои он ки бо CO-RE кор карда тавонед, ядрои шумо бояд бо дастгирии BTF тартиб дода шавад (мо чӣ гуна ин корро дар боб тавсиф мекунем.
$ ls -lh /sys/kernel/btf/vmlinux
-r--r--r-- 1 root root 2.6M Jul 29 15:30 /sys/kernel/btf/vmlinux
Ин файл маълумотро дар бораи ҳама намудҳои додаҳои дар ядро истифодашуда нигоҳ медорад ва дар ҳама мисолҳои мо истифода мешавад libbpf
. Мо дар бораи CO-RE муфассал дар мақолаи навбатӣ сӯҳбат хоҳем кард, аммо дар ин мақола - танҳо худатон як ядро созед. CONFIG_DEBUG_INFO_BTF
.
китобхона libbpf
рост дар директория зиндагӣ мекунад tools/lib/bpf
ядро ва таҳияи он тавассути феҳристи почта анҷом дода мешавад [email protected]
. Аммо, барои эҳтиёҷоти барномаҳое, ки берун аз ядро зиндагӣ мекунанд, як анбори алоҳида нигоҳ дошта мешавад
Дар ин бахш мо дида мебароем, ки чӣ тавр шумо метавонед лоиҳаеро эҷод кунед, ки истифода мебарад libbpf
, биёед якчанд барномаи санҷиширо (каму беш бемаънӣ) нависем ва ба таври муфассал таҳлил кунем, ки ин ҳама чӣ гуна кор мекунад. Ин ба мо имкон медиҳад, ки дар бахшҳои зерин дақиқтар фаҳмонем, ки чӣ гуна барномаҳои BPF бо харитаҳо, ёварони ядро, BTF ва ғайра ҳамкорӣ мекунанд.
Одатан лоиҳаҳоеро истифода мебаранд libbpf
анбори GitHub-ро ҳамчун зермодули git илова кунед, мо ҳамин корро мекунем:
$ mkdir /tmp/libbpf-example
$ cd /tmp/libbpf-example/
$ git init-db
Initialized empty Git repository in /tmp/libbpf-example/.git/
$ git submodule add https://github.com/libbpf/libbpf.git
Cloning into '/tmp/libbpf-example/libbpf'...
remote: Enumerating objects: 200, done.
remote: Counting objects: 100% (200/200), done.
remote: Compressing objects: 100% (103/103), done.
remote: Total 3354 (delta 101), reused 118 (delta 79), pack-reused 3154
Receiving objects: 100% (3354/3354), 2.05 MiB | 10.22 MiB/s, done.
Resolving deltas: 100% (2176/2176), done.
Гузаштан ба libbpf
хеле содда:
$ cd libbpf/src
$ mkdir build
$ OBJDIR=build DESTDIR=root make -s install
$ find root
root
root/usr
root/usr/include
root/usr/include/bpf
root/usr/include/bpf/bpf_tracing.h
root/usr/include/bpf/xsk.h
root/usr/include/bpf/libbpf_common.h
root/usr/include/bpf/bpf_endian.h
root/usr/include/bpf/bpf_helpers.h
root/usr/include/bpf/btf.h
root/usr/include/bpf/bpf_helper_defs.h
root/usr/include/bpf/bpf.h
root/usr/include/bpf/libbpf_util.h
root/usr/include/bpf/libbpf.h
root/usr/include/bpf/bpf_core_read.h
root/usr/lib64
root/usr/lib64/libbpf.so.0.1.0
root/usr/lib64/libbpf.so.0
root/usr/lib64/libbpf.a
root/usr/lib64/libbpf.so
root/usr/lib64/pkgconfig
root/usr/lib64/pkgconfig/libbpf.pc
Нақшаи навбатии мо дар ин бахш чунин аст: мо як барномаи BPF менависем BPF_PROG_TYPE_XDP
, ҳамон тавре ки дар мисоли қаблӣ буд, аммо дар C, мо онро бо истифода аз тартиб медиҳем clang
, ва барномаи ёваре нависед, ки онро ба ядро бор мекунад. Дар бахшҳои минбаъда мо имкониятҳои ҳам барномаи BPF ва ҳам барномаи ассистентро васеъ хоҳем кард.
Мисол: сохтани барномаи мукаммал бо истифода аз libbpf
Барои оғоз, мо файлро истифода мебарем /sys/kernel/btf/vmlinux
, ки дар боло зикр шуда буд, ва муодили онро дар шакли файли сарлавҳа эҷод кунед:
$ bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h
Ин файл ҳамаи сохторҳои додаҳои дар ядрои мо мавҷудбударо нигоҳ медорад, масалан, сарлавҳаи IPv4 дар ядро ҳамин тавр муайян карда мешавад:
$ grep -A 12 'struct iphdr {' vmlinux.h
struct iphdr {
__u8 ihl: 4;
__u8 version: 4;
__u8 tos;
__be16 tot_len;
__be16 id;
__be16 frag_off;
__u8 ttl;
__u8 protocol;
__sum16 check;
__be32 saddr;
__be32 daddr;
};
Акнун мо барномаи BPF-и худро дар C нависед:
$ cat xdp-simple.bpf.c
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
SEC("xdp/simple")
int simple(void *ctx)
{
return XDP_PASS;
}
char LICENSE[] SEC("license") = "GPL";
Гарчанде ки барномаи мо хеле содда баромад, мо бояд ба бисёр ҷузъиёт диққат диҳем. Аввалан, файли аввалини сарлавҳае, ки мо дохил мекунем vmlinux.h
, ки мо танҳо бо истифода аз он тавлид кардем bpftool btf dump
- акнун ба мо лозим нест, ки бастаи kernel-headers насб кунем, то бидонем, ки сохторҳои ядро чӣ гунаанд. Файли сарлавҳаи зерин аз китобхона ба мо меояд libbpf
. Ҳоло ба мо танҳо барои муайян кардани макрос лозим аст SEC
, ки аломатро ба бахши мувофиқи файли объекти ELF мефиристад. Барномаи мо дар қисмат мавҷуд аст xdp/simple
, ки дар он пеш аз слэш навъи барномаро муайян мекунем BPF - ин конвенсияест, ки дар он истифода мешавад libbpf
, дар асоси номи бахш он навъи дурустро ҳангоми оғозёбӣ иваз мекунад bpf(2)
. Худи барномаи BPF ин аст C
- хеле содда ва аз як сатр иборат аст return XDP_PASS
. Дар охир, як қисми алоҳида "license"
дорои номи иҷозатнома.
Мо метавонем барномаи худро бо истифода аз llvm/clang, версияи >= 10.0.0, ё беҳтараш, бузургтар тартиб диҳем (ба қисм нигаред.
$ clang --version
clang version 11.0.0 (https://github.com/llvm/llvm-project.git afc287e0abec710398465ee1f86237513f2b5091)
...
$ clang -O2 -g -c -target bpf -I libbpf/src/root/usr/include xdp-simple.bpf.c -o xdp-simple.bpf.o
Дар байни хусусиятҳои ҷолиб: мо меъмории ҳадафро нишон медиҳем -target bpf
ва роҳ ба сарлавҳаҳо libbpf
, ки мо ба наздикй насб кардем. Ҳамчунин, фаромӯш накунед -O2
, бе ин хосият шумо метавонед дар оянда ба сюрпризҳо дучор шавед. Биёед ба коди худ назар андозем, оё мо барномаи дилхоҳамонро навишта тавонистем?
$ llvm-objdump --section=xdp/simple --no-show-raw-insn -D xdp-simple.bpf.o
xdp-simple.bpf.o: file format elf64-bpf
Disassembly of section xdp/simple:
0000000000000000 <simple>:
0: r0 = 2
1: exit
Бале, кор кард! Ҳоло, мо як файли дуӣ бо барнома дорем ва мо мехоҳем барномае созем, ки онро ба ядро бор кунад. Бо ин максад китобхона libbpf
ба мо ду интихобро пешниҳод мекунад - API-и сатҳи поёнтар ё API-и сатҳи олиро истифода баред. Мо бо роҳи дуюм меравем, зеро мо мехоҳем, ки чӣ гуна навиштан, бор кардан ва пайваст кардани барномаҳои BPF-ро бо кӯшиши ҳадди ақал барои омӯзиши минбаъдаи онҳо ёд гирем.
Аввалан, мо бояд "скелет" -и барномаи худро аз бинарии он бо истифода аз ҳамон утилита тавлид кунем bpftool
- корди швейтсарии ҷаҳони BPF (ки онро айнан метавон гирифт, зеро Даниел Боркман, яке аз созандагон ва нигоҳдорони BPF, швейтсарӣ аст):
$ bpftool gen skeleton xdp-simple.bpf.o > xdp-simple.skel.h
Дар файл xdp-simple.skel.h
дорои рамзи дуии барномаи мо ва функсияҳои идоракунӣ - бор кардан, замима кардан, нест кардани объекти мо. Дар ҳолати оддии мо ин аз ҳад зиёд ба назар мерасад, аммо он инчунин дар ҳолате кор мекунад, ки файли объект бисёр барномаҳо ва харитаҳои BPF дорад ва барои бор кардани ин ELF азим ба мо танҳо лозим аст, ки скелет тавлид кунем ва аз барномаи фармоишӣ як ё ду функсияро даъват кунем. менависанд Биёед ҳоло идома диҳем.
Ба таври қатъӣ, барномаи боркунаки мо ночиз аст:
#include <err.h>
#include <unistd.h>
#include "xdp-simple.skel.h"
int main(int argc, char **argv)
{
struct xdp_simple_bpf *obj;
obj = xdp_simple_bpf__open_and_load();
if (!obj)
err(1, "failed to open and/or load BPF objectn");
pause();
xdp_simple_bpf__destroy(obj);
}
Ин аст, struct xdp_simple_bpf
дар файл муайян карда шудааст xdp-simple.skel.h
ва файли объекти моро тавсиф мекунад:
struct xdp_simple_bpf {
struct bpf_object_skeleton *skeleton;
struct bpf_object *obj;
struct {
struct bpf_program *simple;
} progs;
struct {
struct bpf_link *simple;
} links;
};
Мо метавонем нишонаҳои API-и сатҳи пастро дар ин ҷо бубинем: сохтор struct bpf_program *simple
и struct bpf_link *simple
. Сохтори аввал барномаи моро, ки дар бахш навишта шудааст, махсусан тавсиф мекунад xdp/simple
, ва дуюм тасвир мекунад, ки чӣ гуна барнома ба манбаи ҳодиса пайваст мешавад.
функсия xdp_simple_bpf__open_and_load
, объекти ELF-ро мекушояд, онро таҳлил мекунад, тамоми сохторҳо ва зерсохторҳоро месозад (ғайр аз барнома, ELF инчунин қисмҳои дигарро дар бар мегирад - маълумот, маълумоти танҳо барои хондан, иттилооти ислоҳи ислоҳ, иҷозатнома ва ғайра) ва сипас бо истифода аз система онро ба ядро бор мекунад. занг занед bpf
, ки мо метавонем онро тавассути тартиб додан ва иҷро кардани барнома тафтиш кунем:
$ clang -O2 -I ./libbpf/src/root/usr/include/ xdp-simple.c -o xdp-simple ./libbpf/src/root/usr/lib64/libbpf.a -lelf -lz
$ sudo strace -e bpf ./xdp-simple
...
bpf(BPF_BTF_LOAD, 0x7ffdb8fd9670, 120) = 3
bpf(BPF_PROG_LOAD, {prog_type=BPF_PROG_TYPE_XDP, insn_cnt=2, insns=0xdfd580, license="GPL", log_level=0, log_size=0, log_buf=NULL, kern_version=KERNEL_VERSION(5, 8, 0), prog_flags=0, prog_name="simple", prog_ifindex=0, expected_attach_type=0x25 /* BPF_??? */, ...}, 120) = 4
Биёед ҳоло бо истифода аз барномаи худ назар кунем bpftool
. Биёед шахсияти ӯро пайдо кунем:
# bpftool p | grep -A4 simple
463: xdp name simple tag 3b185187f1855c4c gpl
loaded_at 2020-08-01T01:59:49+0000 uid 0
xlated 16B jited 40B memlock 4096B
btf_id 185
pids xdp-simple(16498)
ва партов (мо шакли кӯтоҳшудаи фармонро истифода мебарем bpftool prog dump xlated
):
# bpftool p d x id 463
int simple(void *ctx):
; return XDP_PASS;
0: (b7) r0 = 2
1: (95) exit
Чизи нав! Барнома қисмҳои файли манбаи C-и моро чоп кард.Ин аз ҷониби китобхона анҷом дода шудааст libbpf
, ки бахши дебагро дар дуӣ пайдо кард, онро ба объекти BTF тартиб дод ва бо истифода аз он ба ядро бор кард. BPF_BTF_LOAD
, ва сипас ҳангоми боркунии барнома бо фармон дескриптори файли ҳосилшударо муайян кунед BPG_PROG_LOAD
.
Ёрдамчиёни ядро
Барномаҳои BPF метавонанд функсияҳои "берунӣ" - ёварҳои ядроро иҷро кунанд. Ин функсияҳои ёрирасон ба барномаҳои BPF имкон медиҳанд, ки ба сохторҳои ядроӣ дастрасӣ пайдо кунанд, харитаҳоро идора кунанд ва инчунин бо "ҷаҳони воқеӣ" муошират кунанд - рӯйдодҳои perf эҷод кунанд, сахтафзорро идора кунанд (масалан, бастаҳои масир) ва ғайра.
Мисол: bpf_get_smp_processor_id
Дар доираи парадигмаи "омӯзиш аз рӯи намуна", биёед яке аз вазифаҳои ёрирасонро баррасӣ кунем, bpf_get_smp_processor_id()
, kernel/bpf/helpers.c
. Он рақами протсессоро бар мегардонад, ки дар он барномаи BPF, ки онро даъват кардааст, кор мекунад. Аммо мо ба семантикаи он чандон таваҷҷӯҳ надорем, зеро татбиқи он як сатрро мегирад:
BPF_CALL_0(bpf_get_smp_processor_id)
{
return smp_processor_id();
}
Таърифҳои функсияи ёрирасони BPF ба таърифҳои занги системаи Linux монанданд. Дар ин ҷо, масалан, функсия муайян карда шудааст, ки далел надорад. (Функсияе, ки масалан се аргументро мегирад, бо истифода аз макрос муайян карда мешавад BPF_CALL_3
. Шумораи максималии далелҳо панҷ аст.) Аммо, ин танҳо қисми аввали таъриф аст. Қисми дуюм муайян кардани сохтори намуд мебошад struct bpf_func_proto
, ки тавсифи функсияи ёрирасонро дар бар мегирад, ки тафтишкунанда мефаҳмад:
const struct bpf_func_proto bpf_get_smp_processor_id_proto = {
.func = bpf_get_smp_processor_id,
.gpl_only = false,
.ret_type = RET_INTEGER,
};
Бақайдгирии Функсияҳои Ёрдам
Барои он ки барномаҳои BPF-и як навъи мушаххас ин функсияро истифода баранд, онҳо бояд онро сабт кунанд, масалан барои навъи BPF_PROG_TYPE_XDP
функсия дар ядро муайян карда шудааст xdp_func_proto
, ки аз ID функсияи ёрирасон муайян мекунад, ки оё XDP ин функсияро дастгирӣ мекунад ё не. Функсияи мо ин аст
static const struct bpf_func_proto *
xdp_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
{
switch (func_id) {
...
case BPF_FUNC_get_smp_processor_id:
return &bpf_get_smp_processor_id_proto;
...
}
}
Навъҳои нави барномаи BPF дар файл "муайян карда шудаанд" include/linux/bpf_types.h
BPF_PROG_TYPE
. Дар нохунак муайян карда шудааст, зеро он таърифи мантиқӣ аст ва дар истилоҳҳои забони Си таърифи маҷмӯи тамоми сохторҳои мушаххас дар дигар ҷойҳо рух медиҳад. Аз чумла, дар файл kernel/bpf/verifier.c
ҳама таърифҳо аз файл bpf_types.h
барои эҷоди як қатор сохторҳо истифода мешаванд bpf_verifier_ops[]
:
static const struct bpf_verifier_ops *const bpf_verifier_ops[] = {
#define BPF_PROG_TYPE(_id, _name, prog_ctx_type, kern_ctx_type)
[_id] = & _name ## _verifier_ops,
#include <linux/bpf_types.h>
#undef BPF_PROG_TYPE
};
Яъне, барои ҳар як намуди барномаи BPF, нишондиҳанда ба сохтори додаҳои навъи он муайян карда мешавад struct bpf_verifier_ops
, ки бо арзиш оғоз карда мешавад _name ## _verifier_ops
, яъне, xdp_verifier_ops
барои xdp
. Сохтор xdp_verifier_ops
net/core/filter.c
чунин аст:
const struct bpf_verifier_ops xdp_verifier_ops = {
.get_func_proto = xdp_func_proto,
.is_valid_access = xdp_is_valid_access,
.convert_ctx_access = xdp_convert_ctx_access,
.gen_prologue = bpf_noop_prologue,
};
Дар ин ҷо мо вазифаи шиносамонро мебинем xdp_func_proto
, ки тафтишкунандаро ҳар дафъае, ки бо мушкилот дучор мешавад, иҷро мекунад баъзеҳо функсияҳо дар дохили барномаи BPF, нигаред verifier.c
Биёед бубинем, ки чӣ тавр барномаи гипотетикии BPF ин функсияро истифода мебарад bpf_get_smp_processor_id
. Барои ин, мо барномаро аз бахши қаблии худ ба таври зерин аз нав менависем:
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
SEC("xdp/simple")
int simple(void *ctx)
{
if (bpf_get_smp_processor_id() != 0)
return XDP_DROP;
return XDP_PASS;
}
char LICENSE[] SEC("license") = "GPL";
Символ bpf_get_smp_processor_id
<bpf/bpf_helper_defs.h>
китобхонаҳо libbpf
чи тавр
static u32 (*bpf_get_smp_processor_id)(void) = (void *) 8;
яъне, bpf_get_smp_processor_id
нишондиҳандаи функсияест, ки арзиши он 8 аст, дар он ҷо 8 арзиш аст BPF_FUNC_get_smp_processor_id
намуд enum bpf_fun_id
, ки барои мо дар файл муайян карда шудааст vmlinux.h
(файл bpf_helper_defs.h
дар ядро бо скрипт тавлид мешавад, аз ин рӯ рақамҳои "ҷодугарӣ" хубанд). Ин функсия ягон далел қабул намекунад ва арзиши навъи бар мегардонад __u32
. Вақте ки мо онро дар барномаи худ иҷро мекунем, clang
дастур тавлид мекунад BPF_CALL
"навъи дуруст" Биёед барномаро тартиб дода, ба қисмат назар кунем xdp/simple
:
$ clang -O2 -g -c -target bpf -I libbpf/src/root/usr/include xdp-simple.bpf.c -o xdp-simple.bpf.o
$ llvm-objdump -D --section=xdp/simple xdp-simple.bpf.o
xdp-simple.bpf.o: file format elf64-bpf
Disassembly of section xdp/simple:
0000000000000000 <simple>:
0: 85 00 00 00 08 00 00 00 call 8
1: bf 01 00 00 00 00 00 00 r1 = r0
2: 67 01 00 00 20 00 00 00 r1 <<= 32
3: 77 01 00 00 20 00 00 00 r1 >>= 32
4: b7 00 00 00 02 00 00 00 r0 = 2
5: 15 01 01 00 00 00 00 00 if r1 == 0 goto +1 <LBB0_2>
6: b7 00 00 00 01 00 00 00 r0 = 1
0000000000000038 <LBB0_2>:
7: 95 00 00 00 00 00 00 00 exit
Дар сатри аввал мо дастурҳоро мебинем call
, параметр IMM
ки ба 8 баробар аст, ва SRC_REG
- сифр. Мувофиқи созишномаи ABI, ки аз ҷониби тафтишкунанда истифода мешавад, ин занг ба функсияи ёрирасони рақами ҳашт мебошад. Вақте ки он оғоз мешавад, мантиқ оддӣ аст. Баргардонидани арзиш аз феҳрист r0
нусхабардорӣ шудааст r1
ва дар сатрҳои 2,3 он ба намуди табдил дода мешавад u32
— 32 битҳои боло тоза карда мешаванд. Дар сатрҳои 4,5,6,7 мо 2-ро бармегардонем (XDP_PASS
) ё 1 (XDP_DROP
) вобаста ба он, ки оё функсияи ёрирасон аз сатри 0 арзиши сифр ё ғайрисифрро баргардонд.
Биёед худро санҷем: барномаро бор кунед ва ба натиҷа назар кунед bpftool prog dump xlated
:
$ bpftool gen skeleton xdp-simple.bpf.o > xdp-simple.skel.h
$ clang -O2 -g -I ./libbpf/src/root/usr/include/ -o xdp-simple xdp-simple.c ./libbpf/src/root/usr/lib64/libbpf.a -lelf -lz
$ sudo ./xdp-simple &
[2] 10914
$ sudo bpftool p | grep simple
523: xdp name simple tag 44c38a10c657e1b0 gpl
pids xdp-simple(10915)
$ sudo bpftool p d x id 523
int simple(void *ctx):
; if (bpf_get_smp_processor_id() != 0)
0: (85) call bpf_get_smp_processor_id#114128
1: (bf) r1 = r0
2: (67) r1 <<= 32
3: (77) r1 >>= 32
4: (b7) r0 = 2
; }
5: (15) if r1 == 0x0 goto pc+1
6: (b7) r0 = 1
7: (95) exit
Хуб, тафтишкунанда ёрдамчии дурусти ядро ёфт.
Мисол: додани далелҳо ва ниҳоят иҷро кардани барнома!
Ҳама функсияҳои ёрирасони сатҳи иҷро прототип доранд
u64 fn(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5)
Параметрҳо ба функсияҳои ёрирасон дар регистрҳо интиқол дода мешаванд r1
-r5
, ва арзиш дар реестр баргардонида мешавад r0
. Функсияҳое вуҷуд надоранд, ки зиёда аз панҷ далелро дар бар гиранд ва дар оянда дастгирии онҳо илова карда намешавад.
Биёед ба ёрдамчии нави ядро ва бубинем, ки BPF параметрҳоро чӣ гуна мегузарад. Биёед аз нав нависем xdp-simple.bpf.c
зайл (боқимондаи сатрҳо тағйир наёфтаанд):
SEC("xdp/simple")
int simple(void *ctx)
{
bpf_printk("running on CPU%un", bpf_get_smp_processor_id());
return XDP_PASS;
}
Барномаи мо рақами CPU-ро, ки дар он кор мекунад, чоп мекунад. Биёед онро тартиб дода, кодро бубинем:
$ llvm-objdump -D --section=xdp/simple --no-show-raw-insn xdp-simple.bpf.o
0000000000000000 <simple>:
0: r1 = 10
1: *(u16 *)(r10 - 8) = r1
2: r1 = 8441246879787806319 ll
4: *(u64 *)(r10 - 16) = r1
5: r1 = 2334956330918245746 ll
7: *(u64 *)(r10 - 24) = r1
8: call 8
9: r1 = r10
10: r1 += -24
11: r2 = 18
12: r3 = r0
13: call 6
14: r0 = 2
15: exit
Дар сатрҳои 0-7 мо сатрро менависем running on CPU%un
, ва он гоҳ дар хати 8 мо хати шиносро иҷро мекунем bpf_get_smp_processor_id
. Дар сатрҳои 9-12 мо далелҳои ёрирасонро омода мекунем bpf_printk
- ба қайд мегирад r1
, r2
, r3
. Чаро онҳо се нафаранду ду не? Зеро bpf_printk
- bpf_trace_printk
, ки бояд андозаи сатри форматро гузаронад.
Биёед ҳоло ба он якчанд сатр илова кунем xdp-simple.c
то ки барномаи мо ба интерфейс пайваст шавад lo
ва дар ҳақиқат оғоз шуд!
$ cat xdp-simple.c
#include <linux/if_link.h>
#include <err.h>
#include <unistd.h>
#include "xdp-simple.skel.h"
int main(int argc, char **argv)
{
__u32 flags = XDP_FLAGS_SKB_MODE;
struct xdp_simple_bpf *obj;
obj = xdp_simple_bpf__open_and_load();
if (!obj)
err(1, "failed to open and/or load BPF objectn");
bpf_set_link_xdp_fd(1, -1, flags);
bpf_set_link_xdp_fd(1, bpf_program__fd(obj->progs.simple), flags);
cleanup:
xdp_simple_bpf__destroy(obj);
}
Дар ин ҷо мо функсияро истифода мебарем bpf_set_link_xdp_fd
, ки барномаҳои XDP-навъи BPF-ро ба интерфейсҳои шабакавӣ мепайвандад. Мо рақами интерфейсро сахт код кардем lo
, ки ҳамеша 1 аст. Мо функсияро ду маротиба иҷро мекунем, то аввал барномаи кӯҳнаро, агар замима шуда бошад, ҷудо кунем. Аҳамият диҳед, ки ҳоло мо ба мушкилот ниёз надорем pause
ё ҳалқаи беохир: барномаи боркунаки мо хориҷ мешавад, аммо барномаи BPF кушта намешавад, зеро он ба манбаи ҳодиса пайваст аст. Пас аз бомуваффақият зеркашӣ ва пайвастшавӣ, барнома барои ҳар як бастаи шабакавӣ, ки ба он ворид мешавад, оғоз карда мешавад lo
.
Биёед барномаро зеркашӣ кунем ва интерфейсро бубинем lo
:
$ sudo ./xdp-simple
$ sudo bpftool p | grep simple
669: xdp name simple tag 4fca62e77ccb43d6 gpl
$ ip l show dev lo
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 xdpgeneric qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
prog/xdp id 669
Барномае, ки мо зеркашӣ кардем, дорои ID 669 мебошад ва мо дар интерфейс ҳамон ID-ро мебинем lo
. Мо якчанд бастаро ба он мефиристем 127.0.0.1
(дархост + ҷавоб):
$ ping -c1 localhost
ва ҳоло биёед ба мундариҷаи файли виртуалии debug назар кунем /sys/kernel/debug/tracing/trace_pipe
, ки дар он bpf_printk
паёмҳои худро менависад:
# cat /sys/kernel/debug/tracing/trace_pipe
ping-13937 [000] d.s1 442015.377014: bpf_trace_printk: running on CPU0
ping-13937 [000] d.s1 442015.377027: bpf_trace_printk: running on CPU0
Дар ду баста дида шуд lo
ва дар CPU0 коркард шудааст - аввалин барномаи мукаммали бемаънии BPF мо кор кард!
Қобили зикр аст, ки bpf_printk
Бесабаб нест, ки он ба файли дебаг менависад: ин ёвари муваффақтарин барои истифода дар истеҳсолот нест, аммо ҳадафи мо нишон додани чизи оддӣ буд.
Дастрасӣ ба харитаҳо аз барномаҳои BPF
Мисол: истифодаи харита аз барномаи BPF
Дар бахшҳои қаблӣ мо тарзи сохтан ва истифода бурдани харитаҳоро аз фазои корбар омӯхта будем ва ҳоло биёед ба қисми ядро нигарем. Биёед, чун маъмул, бо як мисол оғоз кунем. Биёед барномаи худро аз нав нависем xdp-simple.bpf.c
чунин аст:
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__uint(max_entries, 8);
__type(key, u32);
__type(value, u64);
} woo SEC(".maps");
SEC("xdp/simple")
int simple(void *ctx)
{
u32 key = bpf_get_smp_processor_id();
u32 *val;
val = bpf_map_lookup_elem(&woo, &key);
if (!val)
return XDP_ABORTED;
*val += 1;
return XDP_PASS;
}
char LICENSE[] SEC("license") = "GPL";
Дар оғози барнома мо таърифи харитаро илова кардем woo
: Ин массиви 8-элемент аст, ки арзишҳои монанди u64
(дар C мо чунин массивро муайян мекунем u64 woo[8]
). Дар як барнома "xdp/simple"
мо рақами протсессори ҷорӣро ба тағирёбанда мегирем key
ва сипас бо истифода аз функсияи ёрирасон bpf_map_lookup_element
мо нишондиҳандаеро ба вуруди мувофиқ дар массив мегирем, ки онро як маротиба зиёд мекунем. Ба забони русӣ тарҷума шудааст: мо омореро ҳисоб мекунем, ки CPU бастаҳои воридшударо коркард кардааст. Биёед кӯшиш кунем, ки барномаро иҷро кунем:
$ clang -O2 -g -c -target bpf -I libbpf/src/root/usr/include xdp-simple.bpf.c -o xdp-simple.bpf.o
$ bpftool gen skeleton xdp-simple.bpf.o > xdp-simple.skel.h
$ clang -O2 -g -I ./libbpf/src/root/usr/include/ -o xdp-simple xdp-simple.c ./libbpf/src/root/usr/lib64/libbpf.a -lelf -lz
$ sudo ./xdp-simple
Биёед тафтиш кунем, ки вай ба ӯ пайваст аст lo
ва якчанд бастаҳо фиристед:
$ ip l show dev lo
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 xdpgeneric qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
prog/xdp id 108
$ for s in `seq 234`; do sudo ping -f -c 100 127.0.0.1 >/dev/null 2>&1; done
Акнун биёед ба мундариҷаи массив назар андозем:
$ sudo bpftool map dump name woo
[
{ "key": 0, "value": 0 },
{ "key": 1, "value": 400 },
{ "key": 2, "value": 0 },
{ "key": 3, "value": 0 },
{ "key": 4, "value": 0 },
{ "key": 5, "value": 0 },
{ "key": 6, "value": 0 },
{ "key": 7, "value": 46400 }
]
Қариб ҳамаи равандҳо дар CPU7 коркард карда шуданд. Ин барои мо муҳим нест, чизи асосӣ ин аст, ки барнома кор мекунад ва мо мефаҳмем, ки чӣ тавр ба харитаҳо аз барномаҳои BPF дастрасӣ пайдо кардан мумкин аст - бо истифода аз хелперов bpf_mp_*
Индекси мистикӣ
Ҳамин тавр, мо метавонем ба харита аз барномаи BPF бо истифода аз зангҳои монанди
val = bpf_map_lookup_elem(&woo, &key);
ки дар он вазифаи ёрирасон ба назар мерасад
void *bpf_map_lookup_elem(struct bpf_map *map, const void *key)
вале мо аз нишондиханда гузашта истодаем &woo
ба сохтори номаълум struct { ... }
...
Агар мо ба assembler барнома назар кунем, мебинем, ки арзиши &woo
аслан муайян нашудааст (сатри 4):
llvm-objdump -D --section xdp/simple xdp-simple.bpf.o
xdp-simple.bpf.o: file format elf64-bpf
Disassembly of section xdp/simple:
0000000000000000 <simple>:
0: 85 00 00 00 08 00 00 00 call 8
1: 63 0a fc ff 00 00 00 00 *(u32 *)(r10 - 4) = r0
2: bf a2 00 00 00 00 00 00 r2 = r10
3: 07 02 00 00 fc ff ff ff r2 += -4
4: 18 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 r1 = 0 ll
6: 85 00 00 00 01 00 00 00 call 1
...
ва дар кӯчонидан мавҷуд аст:
$ llvm-readelf -r xdp-simple.bpf.o | head -4
Relocation section '.relxdp/simple' at offset 0xe18 contains 1 entries:
Offset Info Type Symbol's Value Symbol's Name
0000000000000020 0000002700000001 R_BPF_64_64 0000000000000000 woo
Аммо агар мо ба барномаи аллакай боршуда назар кунем, мо нишондодеро ба харитаи дуруст мебинем (хати 4):
$ sudo bpftool prog dump x name simple
int simple(void *ctx):
0: (85) call bpf_get_smp_processor_id#114128
1: (63) *(u32 *)(r10 -4) = r0
2: (bf) r2 = r10
3: (07) r2 += -4
4: (18) r1 = map[id:64]
...
Ҳамин тариқ, мо метавонем хулоса барорем, ки ҳангоми оғоз кардани барномаи боркунаки мо, истиноди ба &woo
бо чизе бо китобхона иваз карда шуд libbpf
. Аввалан, мо ба натиҷа назар мекунем strace
:
$ sudo strace -e bpf ./xdp-simple
...
bpf(BPF_MAP_CREATE, {map_type=BPF_MAP_TYPE_ARRAY, key_size=4, value_size=8, max_entries=8, map_name="woo", ...}, 120) = 4
bpf(BPF_PROG_LOAD, {prog_type=BPF_PROG_TYPE_XDP, prog_name="simple", ...}, 120) = 5
Мо инро мебинем libbpf
харита сохтанд woo
ва он гоҳ барномаи моро зеркашӣ кард simple
. Биёед бубинем, ки чӣ тавр мо барномаро бор мекунем:
- занг занед
xdp_simple_bpf__open_and_load
аз файлxdp-simple.skel.h
- ки сабаб мешавад
xdp_simple_bpf__load
аз файлxdp-simple.skel.h
- ки сабаб мешавад
bpf_object__load_skeleton
аз файлlibbpf/src/libbpf.c
- ки сабаб мешавад
bpf_object__load_xattr
аз онlibbpf/src/libbpf.c
Функсияи охирин, дар байни чизҳои дигар, занг хоҳад зад bpf_object__create_maps
, ки харитаҳои мавҷударо эҷод мекунад ё мекушояд, онҳоро ба тавсифи файл табдил медиҳад. (Ин ҷоест, ки мо мебинем BPF_MAP_CREATE
дар баромад strace
.) Минбаъд функсия даъват карда мешавад bpf_object__relocate
ва маҳз вай ба мо таваҷҷӯҳ мекунад, зеро мо он чизеро, ки дидаем, дар хотир дорем woo
дар ҷадвали интиқол. Омӯзиши он, мо дар ниҳоят худро дар функсия мебинем bpf_program__relocate
, ки
case RELO_LD64:
insn[0].src_reg = BPF_PSEUDO_MAP_FD;
insn[0].imm = obj->maps[relo->map_idx].fd;
break;
Пас, мо дастурҳои худро мегирем
18 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 r1 = 0 ll
ва сабти манбаъро дар он иваз кунед BPF_PSEUDO_MAP_FD
, ва аввалин IMM ба тавсифи файли харитаи мо ва агар он ба он баробар бошад, масалан, 0xdeadbeef
, пас дар натича мо дастур мегирем
18 11 00 00 ef eb ad de 00 00 00 00 00 00 00 00 r1 = 0 ll
Ин аст, ки чӣ тавр иттилооти харита ба барномаи мушаххаси боргузори BPF интиқол дода мешавад. Дар ин ҳолат, харита метавонад бо истифода аз он сохта шавад BPF_MAP_CREATE
, ва бо истифода аз ID кушода мешавад BPF_MAP_GET_FD_BY_ID
.
Ҳамагӣ, ҳангоми истифода libbpf
алгоритми зерин аст:
- ҳангоми тартиб додан, сабтҳо дар ҷадвали кӯчонидан барои истинод ба харитаҳо сохта мешаванд
libbpf
китоби объекти ELF -ро мекушояд, ҳама харитаҳои истифодашударо пайдо мекунад ва барои онҳо тавсифи файлҳо эҷод мекунад- Дескрипторҳои файл ҳамчун қисми дастур ба ядро бор карда мешаванд
LD64
Тавре ки шумо тасаввур карда метавонед, боз чизҳои дигар дар пешанд ва мо бояд ба асли худ назар кунем. Хушбахтона, мо як фаҳмиш дорем - мо маънои онро навиштаем BPF_PSEUDO_MAP_FD
ба феҳристи сарчашмаҳо ворид кунед ва мо метавонем онро дафн кунем, ки моро ба муқаддасоти ҳамаи муқаддасон мебарад - kernel/bpf/verifier.c
, ки дар он функсия бо номи фарқкунанда тавсифкунандаи файлро бо суроғаи сохтори намуд иваз мекунад struct bpf_map
:
static int replace_map_fd_with_map_ptr(struct bpf_verifier_env *env) {
...
f = fdget(insn[0].imm);
map = __bpf_map_get(f);
if (insn->src_reg == BPF_PSEUDO_MAP_FD) {
addr = (unsigned long)map;
}
insn[0].imm = (u32)addr;
insn[1].imm = addr >> 32;
(рамзи пурра пайдо кардан мумкин аст
- Ҳангоми боркунии барнома, верфикатор истифодаи дурусти харитаро тафтиш мекунад ва суроғаи сохтори мувофиқро менависад
struct bpf_map
Ҳангоми зеркашии дуӣ ELF бо истифода аз libbpf
Бисёр чизҳои дигар ҳастанд, аммо мо инро дар мақолаҳои дигар муҳокима хоҳем кард.
Боркунии барномаҳо ва харитаҳо бе libbpf
Тавре ваъда дода шудааст, дар ин ҷо як мисол барои хонандагоне мебошад, ки мехоҳанд бидонанд, ки чӣ гуна барномаеро, ки харитаҳоро истифода мебарад, бидуни кӯмак эҷод ва бор кунанд. libbpf
. Ин метавонад вақте муфид бошад, ки шумо дар муҳите кор мекунед, ки барои он шумо вобастагӣ эҷод карда наметавонед ё ҳар як битро захира кунед ё барномаи монанди навиштан ply
Барои осон кардани мантиқ пайравӣ кардан, мо намунаи худро бо ин мақсадҳо дубора менависем xdp-simple
. Рамзи пурра ва каме васеъшудаи барномаро, ки дар ин мисол баррасӣ мешавад, дар ин ҷо пайдо кардан мумкин аст
Мантиқи барномаи мо чунин аст:
- харитаи тип эҷод кунед
BPF_MAP_TYPE_ARRAY
бо истифода аз фармонBPF_MAP_CREATE
, - эҷод кардани барномае, ки ин харитаро истифода мебарад,
- барномаро ба интерфейс пайваст кунед
lo
,
ки ба одам чун тарчума мешавад
int main(void)
{
int map_fd, prog_fd;
map_fd = map_create();
if (map_fd < 0)
err(1, "bpf: BPF_MAP_CREATE");
prog_fd = prog_load(map_fd);
if (prog_fd < 0)
err(1, "bpf: BPF_PROG_LOAD");
xdp_attach(1, prog_fd);
}
Ин аст, map_create
ҳамон тавре ки мо дар мисоли аввал дар бораи занги система кардем, харитаро месозад bpf
- "ядро, лутфан ба ман харитаи навро дар шакли массиви 8 элемент созед __u64
ва тасвири файлро ба ман баргардонед":
static int map_create()
{
union bpf_attr attr;
memset(&attr, 0, sizeof(attr));
attr.map_type = BPF_MAP_TYPE_ARRAY,
attr.key_size = sizeof(__u32),
attr.value_size = sizeof(__u64),
attr.max_entries = 8,
strncpy(attr.map_name, "woo", sizeof(attr.map_name));
return syscall(__NR_bpf, BPF_MAP_CREATE, &attr, sizeof(attr));
}
Барномаро инчунин бор кардан осон аст:
static int prog_load(int map_fd)
{
union bpf_attr attr;
struct bpf_insn insns[] = {
...
};
memset(&attr, 0, sizeof(attr));
attr.prog_type = BPF_PROG_TYPE_XDP;
attr.insns = ptr_to_u64(insns);
attr.insn_cnt = sizeof(insns)/sizeof(insns[0]);
attr.license = ptr_to_u64("GPL");
strncpy(attr.prog_name, "woo", sizeof(attr.prog_name));
return syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr));
}
Қисми душвор prog_load
таърифи барномаи BPF-и мо ҳамчун як қатор сохторҳо мебошад struct bpf_insn insns[]
. Аммо азбаски мо барномаеро истифода мебарем, ки дар C мавҷуд аст, мо метавонем каме фиреб диҳем:
$ llvm-objdump -D --section xdp/simple xdp-simple.bpf.o
0000000000000000 <simple>:
0: 85 00 00 00 08 00 00 00 call 8
1: 63 0a fc ff 00 00 00 00 *(u32 *)(r10 - 4) = r0
2: bf a2 00 00 00 00 00 00 r2 = r10
3: 07 02 00 00 fc ff ff ff r2 += -4
4: 18 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 r1 = 0 ll
6: 85 00 00 00 01 00 00 00 call 1
7: b7 01 00 00 00 00 00 00 r1 = 0
8: 15 00 04 00 00 00 00 00 if r0 == 0 goto +4 <LBB0_2>
9: 61 01 00 00 00 00 00 00 r1 = *(u32 *)(r0 + 0)
10: 07 01 00 00 01 00 00 00 r1 += 1
11: 63 10 00 00 00 00 00 00 *(u32 *)(r0 + 0) = r1
12: b7 01 00 00 02 00 00 00 r1 = 2
0000000000000068 <LBB0_2>:
13: bf 10 00 00 00 00 00 00 r0 = r1
14: 95 00 00 00 00 00 00 00 exit
Дар маҷмӯъ, мо бояд 14 дастурро дар шакли сохторҳо нависед struct bpf_insn
(маслиҳат: партовгоҳро аз боло гирифта, қисмати дастурҳоро дубора хонед, кушоед linux/bpf.h
linux/bpf_common.h
struct bpf_insn insns[]
ба худ):
struct bpf_insn insns[] = {
/* 85 00 00 00 08 00 00 00 call 8 */
{
.code = BPF_JMP | BPF_CALL,
.imm = 8,
},
/* 63 0a fc ff 00 00 00 00 *(u32 *)(r10 - 4) = r0 */
{
.code = BPF_MEM | BPF_STX,
.off = -4,
.src_reg = BPF_REG_0,
.dst_reg = BPF_REG_10,
},
/* bf a2 00 00 00 00 00 00 r2 = r10 */
{
.code = BPF_ALU64 | BPF_MOV | BPF_X,
.src_reg = BPF_REG_10,
.dst_reg = BPF_REG_2,
},
/* 07 02 00 00 fc ff ff ff r2 += -4 */
{
.code = BPF_ALU64 | BPF_ADD | BPF_K,
.dst_reg = BPF_REG_2,
.imm = -4,
},
/* 18 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 r1 = 0 ll */
{
.code = BPF_LD | BPF_DW | BPF_IMM,
.src_reg = BPF_PSEUDO_MAP_FD,
.dst_reg = BPF_REG_1,
.imm = map_fd,
},
{ }, /* placeholder */
/* 85 00 00 00 01 00 00 00 call 1 */
{
.code = BPF_JMP | BPF_CALL,
.imm = 1,
},
/* b7 01 00 00 00 00 00 00 r1 = 0 */
{
.code = BPF_ALU64 | BPF_MOV | BPF_K,
.dst_reg = BPF_REG_1,
.imm = 0,
},
/* 15 00 04 00 00 00 00 00 if r0 == 0 goto +4 <LBB0_2> */
{
.code = BPF_JMP | BPF_JEQ | BPF_K,
.off = 4,
.src_reg = BPF_REG_0,
.imm = 0,
},
/* 61 01 00 00 00 00 00 00 r1 = *(u32 *)(r0 + 0) */
{
.code = BPF_MEM | BPF_LDX,
.off = 0,
.src_reg = BPF_REG_0,
.dst_reg = BPF_REG_1,
},
/* 07 01 00 00 01 00 00 00 r1 += 1 */
{
.code = BPF_ALU64 | BPF_ADD | BPF_K,
.dst_reg = BPF_REG_1,
.imm = 1,
},
/* 63 10 00 00 00 00 00 00 *(u32 *)(r0 + 0) = r1 */
{
.code = BPF_MEM | BPF_STX,
.src_reg = BPF_REG_1,
.dst_reg = BPF_REG_0,
},
/* b7 01 00 00 02 00 00 00 r1 = 2 */
{
.code = BPF_ALU64 | BPF_MOV | BPF_K,
.dst_reg = BPF_REG_1,
.imm = 2,
},
/* <LBB0_2>: bf 10 00 00 00 00 00 00 r0 = r1 */
{
.code = BPF_ALU64 | BPF_MOV | BPF_X,
.src_reg = BPF_REG_1,
.dst_reg = BPF_REG_0,
},
/* 95 00 00 00 00 00 00 00 exit */
{
.code = BPF_JMP | BPF_EXIT
},
};
Машқ барои онҳое, ки инро худашон нанавиштаанд - пайдо кунед map_fd
.
Дар барномаи мо боз як қисми ифшонашуда боқӣ мондааст - xdp_attach
. Мутаассифона, барномаҳои монанди XDP наметавонанд бо истифода аз занги система пайваст шаванд bpf
. Одамоне, ки BPF ва XDP-ро офаридаанд, аз ҷомеаи онлайни Linux буданд, яъне онҳо яке аз маъмултаринеро, ки ба онҳо наздиктаранд (аммо на барои муқаррарӣ одамон) интерфейс барои ҳамкорӣ бо ядро: xdp_attach
кодро аз он нусхабардорӣ мекунад libbpf
, яъне аз файл netlink.c
Хуш омадед ба ҷаҳони розеткаҳои netlink
Навъи васлаки netlink кушоед NETLINK_ROUTE
:
int netlink_open(__u32 *nl_pid)
{
struct sockaddr_nl sa;
socklen_t addrlen;
int one = 1, ret;
int sock;
memset(&sa, 0, sizeof(sa));
sa.nl_family = AF_NETLINK;
sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (sock < 0)
err(1, "socket");
if (setsockopt(sock, SOL_NETLINK, NETLINK_EXT_ACK, &one, sizeof(one)) < 0)
warnx("netlink error reporting not supported");
if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0)
err(1, "bind");
addrlen = sizeof(sa);
if (getsockname(sock, (struct sockaddr *)&sa, &addrlen) < 0)
err(1, "getsockname");
*nl_pid = sa.nl_pid;
return sock;
}
Мо аз ин розетка мехонем:
static int bpf_netlink_recv(int sock, __u32 nl_pid, int seq)
{
bool multipart = true;
struct nlmsgerr *errm;
struct nlmsghdr *nh;
char buf[4096];
int len, ret;
while (multipart) {
multipart = false;
len = recv(sock, buf, sizeof(buf), 0);
if (len < 0)
err(1, "recv");
if (len == 0)
break;
for (nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, len);
nh = NLMSG_NEXT(nh, len)) {
if (nh->nlmsg_pid != nl_pid)
errx(1, "wrong pid");
if (nh->nlmsg_seq != seq)
errx(1, "INVSEQ");
if (nh->nlmsg_flags & NLM_F_MULTI)
multipart = true;
switch (nh->nlmsg_type) {
case NLMSG_ERROR:
errm = (struct nlmsgerr *)NLMSG_DATA(nh);
if (!errm->error)
continue;
ret = errm->error;
// libbpf_nla_dump_errormsg(nh); too many code to copy...
goto done;
case NLMSG_DONE:
return 0;
default:
break;
}
}
}
ret = 0;
done:
return ret;
}
Дар ниҳоят, ин вазифаи мост, ки розеткаро мекушояд ва ба он паёми махсусе мефиристад, ки дорои тавсифи файл аст:
static int xdp_attach(int ifindex, int prog_fd)
{
int sock, seq = 0, ret;
struct nlattr *nla, *nla_xdp;
struct {
struct nlmsghdr nh;
struct ifinfomsg ifinfo;
char attrbuf[64];
} req;
__u32 nl_pid = 0;
sock = netlink_open(&nl_pid);
if (sock < 0)
return sock;
memset(&req, 0, sizeof(req));
req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
req.nh.nlmsg_type = RTM_SETLINK;
req.nh.nlmsg_pid = 0;
req.nh.nlmsg_seq = ++seq;
req.ifinfo.ifi_family = AF_UNSPEC;
req.ifinfo.ifi_index = ifindex;
/* started nested attribute for XDP */
nla = (struct nlattr *)(((char *)&req)
+ NLMSG_ALIGN(req.nh.nlmsg_len));
nla->nla_type = NLA_F_NESTED | IFLA_XDP;
nla->nla_len = NLA_HDRLEN;
/* add XDP fd */
nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len);
nla_xdp->nla_type = IFLA_XDP_FD;
nla_xdp->nla_len = NLA_HDRLEN + sizeof(int);
memcpy((char *)nla_xdp + NLA_HDRLEN, &prog_fd, sizeof(prog_fd));
nla->nla_len += nla_xdp->nla_len;
/* if user passed in any flags, add those too */
__u32 flags = XDP_FLAGS_SKB_MODE;
nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len);
nla_xdp->nla_type = IFLA_XDP_FLAGS;
nla_xdp->nla_len = NLA_HDRLEN + sizeof(flags);
memcpy((char *)nla_xdp + NLA_HDRLEN, &flags, sizeof(flags));
nla->nla_len += nla_xdp->nla_len;
req.nh.nlmsg_len += NLA_ALIGN(nla->nla_len);
if (send(sock, &req, req.nh.nlmsg_len, 0) < 0)
err(1, "send");
ret = bpf_netlink_recv(sock, nl_pid, seq);
cleanup:
close(sock);
return ret;
}
Пас, ҳама чиз барои санҷиш омода аст:
$ cc nolibbpf.c -o nolibbpf
$ sudo strace -e bpf ./nolibbpf
bpf(BPF_MAP_CREATE, {map_type=BPF_MAP_TYPE_ARRAY, map_name="woo", ...}, 72) = 3
bpf(BPF_PROG_LOAD, {prog_type=BPF_PROG_TYPE_XDP, insn_cnt=15, prog_name="woo", ...}, 72) = 4
+++ exited with 0 +++
Биёед бубинем, ки оё барномаи мо ба он пайваст шудааст lo
:
$ ip l show dev lo
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 xdpgeneric qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
prog/xdp id 160
Биёед пингҳо фиристем ва ба харита назар андозем:
$ for s in `seq 234`; do sudo ping -f -c 100 127.0.0.1 >/dev/null 2>&1; done
$ sudo bpftool m dump name woo
key: 00 00 00 00 value: 90 01 00 00 00 00 00 00
key: 01 00 00 00 value: 00 00 00 00 00 00 00 00
key: 02 00 00 00 value: 00 00 00 00 00 00 00 00
key: 03 00 00 00 value: 00 00 00 00 00 00 00 00
key: 04 00 00 00 value: 00 00 00 00 00 00 00 00
key: 05 00 00 00 value: 00 00 00 00 00 00 00 00
key: 06 00 00 00 value: 40 b5 00 00 00 00 00 00
key: 07 00 00 00 value: 00 00 00 00 00 00 00 00
Found 8 elements
Хуб, ҳама чиз кор мекунад. Дар омади гап, дар хотир доред, ки харитаи мо боз дар шакли байт намоиш дода мешавад. Ин ба он вобаста аст, ки бар хилофи libbpf
мо маълумоти навъи (BTF) бор накардем. Аммо мо дафъаи оянда дар ин бора бештар сӯҳбат хоҳем кард.
Воситаҳои рушд
Дар ин бахш, мо ҳадди ақали абзори таҳиягари BPF-ро дида мебароем.
Умуман, барои таҳияи барномаҳои BPF ба шумо ягон чизи махсус лозим нест - BPF дар ҳама гуна ядрои тақсимоти мувофиқ кор мекунад ва барномаҳо бо истифода аз clang
, ки онро аз баста таъмин кардан мумкин аст. Аммо, аз сабаби он, ки BPF дар ҳоли таҳия аст, ядро ва асбобҳо доимо тағир меёбанд, агар шумо нахоҳед, ки барномаҳои BPF-ро бо усулҳои кӯҳна аз соли 2019 нависед, пас шумо бояд тартиб диҳед
llvm
/clang
pahole
- асосии он
bpftool
(Барои истинод, ин бахш ва ҳамаи мисолҳои мақола дар Debian 10 иҷро шудаанд.)
llvm/clang
BPF бо LLVM дӯстона аст ва гарчанде ки ба наздикӣ барномаҳои BPF метавонанд бо истифода аз gcc тартиб дода шаванд, тамоми таҳияи ҷорӣ барои LLVM анҷом дода мешавад. Бинобар ин, пеш аз ҳама, мо версияи кунуниро месозем clang
аз git:
$ sudo apt install ninja-build
$ git clone --depth 1 https://github.com/llvm/llvm-project.git
$ mkdir -p llvm-project/llvm/build/install
$ cd llvm-project/llvm/build
$ cmake .. -G "Ninja" -DLLVM_TARGETS_TO_BUILD="BPF;X86"
-DLLVM_ENABLE_PROJECTS="clang"
-DBUILD_SHARED_LIBS=OFF
-DCMAKE_BUILD_TYPE=Release
-DLLVM_BUILD_RUNTIME=OFF
$ time ninja
... много времени спустя
$
Акнун мо метавонем тафтиш кунем, ки оё ҳама чиз дуруст аст:
$ ./bin/llc --version
LLVM (http://llvm.org/):
LLVM version 11.0.0git
Optimized build.
Default target: x86_64-unknown-linux-gnu
Host CPU: znver1
Registered Targets:
bpf - BPF (host endian)
bpfeb - BPF (big endian)
bpfel - BPF (little endian)
x86 - 32-bit X86: Pentium-Pro and above
x86-64 - 64-bit X86: EM64T and AMD64
(Дастур оид ба васлкунӣ clang
аз ҷониби ман гирифта шудааст
Мо барномаҳоеро, ки нав сохтаем, насб намекунем, балки ба ҷои он танҳо ба онҳо илова кунед PATH
, масалан:
export PATH="`pwd`/bin:$PATH"
(Инро метавон илова кард .bashrc
ё ба файли алоҳида. Шахсан ман ба ин чизҳо илова мекунам ~/bin/activate-llvm.sh
ва дар мавриди зарурат ин корро мекунам . activate-llvm.sh
.)
Пахоле ва BTF
Коммуналӣ pahole
ҳангоми сохтани ядро барои эҷоди маълумоти ислоҳкунӣ дар формати BTF истифода мешавад. Мо дар ин мақола дар бораи ҷузъиёти технологияи BTF ба таври муфассал маълумот намедиҳем, ба истиснои он, ки он қулай аст ва мо мехоҳем онро истифода барем. Пас, агар шумо ядрои худро бунёд карданӣ бошед, аввал созед pahole
(бе pahole
шумо наметавонед ядроро бо интихоб созед CONFIG_DEBUG_INFO_BTF
:
$ git clone https://git.kernel.org/pub/scm/devel/pahole/pahole.git
$ cd pahole/
$ sudo apt install cmake
$ mkdir build
$ cd build/
$ cmake -D__LIB=lib ..
$ make
$ sudo make install
$ which pahole
/usr/local/bin/pahole
Ядроҳо барои озмоиш бо BPF
Ҳангоми омӯхтани имкониятҳои BPF, ман мехоҳам ядрои худро ҷамъ кунам. Умуман, ин шарт нест, зеро шумо метавонед барномаҳои BPF-ро дар ядрои тақсимот тартиб диҳед ва бор кунед, аммо доштани ядрои худ ба шумо имкон медиҳад, ки хусусиятҳои навтарини BPF-ро истифода баред, ки дар тақсимоти шумо дар тӯли моҳҳои беҳтарин пайдо мешаванд. , ё, чунон ки дар мавриди баъзе асбобҳои ислоҳкунӣ дар ояндаи наздик тамоман баста карда намешаванд. Инчунин, асосии худаш озмоишро бо код муҳим ҳис мекунад.
Барои сохтани ядро, аввал, худи ядро ва дуюм, файли конфигуратсияи ядро лозим аст. Барои озмоиш бо BPF мо метавонем муқаррариро истифода барем net
net-next
bpf
bpf-next
*-next
ядроҳо ноустувортарин дар рӯйхат мебошанд).
Дар бораи чӣ гуна идора кардани файлҳои конфигуратсияи ядро сухан кардан аз доираи ин мақола берун нест - гумон меравад, ки шумо ё ин корро аллакай медонед, ё
Яке аз ядроҳои болоро зеркашӣ кунед:
$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git
$ cd bpf-next
Сохтани конфигуратсияи ҳадди ақали ядрои корӣ:
$ cp /boot/config-`uname -r` .config
$ make localmodconfig
Имконоти BPF-ро дар файл фаъол созед .config
аз интихоби худ (эҳтимоли зиёд CONFIG_BPF
аллакай фаъол карда мешавад, зеро systemd онро истифода мебарад). Дар ин ҷо рӯйхати вариантҳо аз ядрое, ки барои ин мақола истифода мешаванд, оварда шудаанд:
CONFIG_CGROUP_BPF=y
CONFIG_BPF=y
CONFIG_BPF_LSM=y
CONFIG_BPF_SYSCALL=y
CONFIG_ARCH_WANT_DEFAULT_BPF_JIT=y
CONFIG_BPF_JIT_ALWAYS_ON=y
CONFIG_BPF_JIT_DEFAULT_ON=y
CONFIG_IPV6_SEG6_BPF=y
# CONFIG_NETFILTER_XT_MATCH_BPF is not set
# CONFIG_BPFILTER is not set
CONFIG_NET_CLS_BPF=y
CONFIG_NET_ACT_BPF=y
CONFIG_BPF_JIT=y
CONFIG_BPF_STREAM_PARSER=y
CONFIG_LWTUNNEL_BPF=y
CONFIG_HAVE_EBPF_JIT=y
CONFIG_BPF_EVENTS=y
CONFIG_BPF_KPROBE_OVERRIDE=y
CONFIG_DEBUG_INFO_BTF=y
Пас мо метавонем модулҳо ва ядроро ба осонӣ ҷамъ ва насб кунем (дар омади гап, шумо метавонед ядроро бо истифода аз навҷамъшуда ҷамъ кунед. clang
бо илова кардан CC=clang
):
$ make -s -j $(getconf _NPROCESSORS_ONLN)
$ sudo make modules_install
$ sudo make install
ва бо ядрои нав аз нав оғоз кунед (ман барои ин kexec
аз баста kexec-tools
):
v=5.8.0-rc6+ # если вы пересобираете текущее ядро, то можно делать v=`uname -r`
sudo kexec -l -t bzImage /boot/vmlinuz-$v --initrd=/boot/initrd.img-$v --reuse-cmdline &&
sudo kexec -e
bpftool
Утилите, ки дар мақола маъмултарин истифода мешавад, утилита хоҳад буд bpftool
, ҳамчун як қисми ядрои Linux таъмин карда мешавад. Он аз ҷониби таҳиягарони BPF барои таҳиягарони BPF навишта ва нигоҳ дошта мешавад ва метавонад барои идоракунии ҳама намуди объектҳои BPF - бор кардани барномаҳо, эҷод ва таҳрир кардани харитаҳо, омӯхтани ҳаёти экосистемаи BPF ва ғайра истифода шавад. Ҳуҷҷатҳоро дар шакли кодҳои сарчашма барои саҳифаҳои одам пайдо кардан мумкин аст
Дар замони навиштани ин bpftool
танҳо барои RHEL, Fedora ва Ubuntu омода аст (ниг. масалан, bpftool
дар Debian). Аммо агар шумо аллакай ядрои худро сохта бошед, пас созед bpftool
мисли пирог осон:
$ cd ${linux}/tools/bpf/bpftool
# ... пропишите пути к последнему clang, как рассказано выше
$ make -s
Auto-detecting system features:
... libbfd: [ on ]
... disassembler-four-args: [ on ]
... zlib: [ on ]
... libcap: [ on ]
... clang-bpf-co-re: [ on ]
Auto-detecting system features:
... libelf: [ on ]
... zlib: [ on ]
... bpf: [ on ]
$
(Ин ҷо ${linux}
- ин директорияи ядрои шумост.) Пас аз иҷрои ин фармонҳо bpftool
дар феҳрист ҷамъ карда мешавад ${linux}/tools/bpf/bpftool
ва он метавонад ба роҳ илова карда шавад (пеш аз ҳама ба корбар root
) ё танҳо ба он нусхабардорӣ кунед /usr/local/sbin
.
Ҷамъоварӣ кунед bpftool
беҳтар аст, ки охиринро истифода баред clang
, ҷамъ тавре ки дар боло тавсиф, ва санҷед, ки оё он дуруст васл шудааст - бо истифода аз, масалан, фармон
$ sudo bpftool feature probe kernel
Scanning system configuration...
bpf() syscall for unprivileged users is enabled
JIT compiler is enabled
JIT compiler hardening is disabled
JIT compiler kallsyms exports are enabled for root
...
ки нишон медиҳад, ки кадом хусусиятҳои BPF дар ядрои шумо фаъоланд.
Дар омади гап, фармони қаблиро метавон ҳамчун иҷро кард
# bpftool f p k
Ин аз рӯи қиёс бо коммуналӣ аз баста анҷом дода мешавад iproute2
, ки мо, масалан, гуфта метавонем ip a s eth0
ба ҷои ip addr show dev eth0
.
хулоса
BPF ба шумо имкон медиҳад, ки бӯрро пойафзол кунед, то ба таври муассир чен кунед ва функсияи ядроро тағир диҳед. Система аз рӯи анъанаҳои беҳтарини UNIX хеле муваффақ шуд: механизми оддӣ, ки ба шумо имкон медиҳад (аз нав) барномарезӣ кунед ядро ба шумораи зиёди одамон ва созмонҳо имкон дод, ки озмоиш кунанд. Ва, гарчанде ки таҷрибаҳо ва инчунин рушди худи инфрасохтори BPF ба анҷом нарасидаанд, система аллакай як ABI-и устувор дорад, ки ба шумо имкон медиҳад мантиқи боэътимод ва муҳимтар аз ҳама самараноки тиҷоратро эҷод кунед.
Мехостам кайд намоям, ки ба назари ман, технология ин кадар маъмул гаштааст, зеро аз як тараф, метавонад бозӣ кунед (меъмории мошинро дар як бегох каму беш фахмидан мумкин аст), аз тарафи дигар барои халли масъалахое, ки пеш аз пайдоиши он (зебо) хал карда наметавонистанд. Ин ду ҷузъ дар якҷоягӣ одамонро маҷбур мекунанд, ки таҷриба ва орзу кунанд, ки боиси пайдоиши ҳалли бештари инноватсионӣ мегардад.
Ин мақола, ҳарчанд он қадар кӯтоҳ нест, танҳо як муқаддима ба ҷаҳони BPF аст ва хусусиятҳои "пешрафта" ва қисмҳои муҳими меъмориро тавсиф намекунад. Нақшаи оянда чунин аст: мақолаи навбатӣ шарҳи намудҳои барномаи BPF хоҳад буд (дар ядрои 5.8 30 намуди барнома мавҷуд аст), пас мо дар ниҳоят мебинем, ки чӣ гуна барномаҳои воқеии BPF-ро бо истифода аз барномаҳои пайгирии ядроҳо нависед ҳамчун мисол, пас вақти он расидааст, ки як курси амиқтар оид ба меъмории BPF, пас аз он мисолҳои шабакавии BPF ва барномаҳои амниятӣ.
Мақолаҳои қаблӣ дар ин силсила
Пайвандҳо
-
Дастури истинодҳои BPF ва XDP - ҳуҷҷатҳо дар бораи BPF аз cilium, ё аниқтараш аз Даниел Боркман, яке аз созандагон ва нигоҳдорони BPF. Ин яке аз аввалин тавсифҳои ҷиддӣ аст, ки аз дигарон бо он фарқ мекунад, ки Дониёл дақиқ медонад, ки дар бораи чӣ менависад ва дар он ҳеҷ хатогӣ вуҷуд надорад. Аз ҷумла, ин ҳуҷҷат тарзи кор бо барномаҳои BPF-и навъи XDP ва TC-ро бо истифода аз утилитаи маъруф тавсиф мекунад.ip
аз бастаiproute2
. -
Documentation/networking/filter.txt - файли аслӣ бо ҳуҷҷатҳо барои BPF классикӣ ва сипас васеъ. Хониши хуб, агар шумо хоҳед, ки ба забони маҷлис ва ҷузъиёти меъмории техникӣ омӯзед. -
Блог дар бораи BPF аз Facebook . Он хеле кам, аммо ба таври мувофиқ нав карда мешавад, тавре ки Алексей Старовойтов (муаллифи eBPF) ва Андрей Накрйико - (нигоҳдор) дар он ҷо менависандlibbpf
). -
Асрори bpftool . Риштаи ҷолиби твиттер аз Квентин Монет бо мисолҳо ва асрори истифодаи bpftool. -
Мубтало ба BPF: рӯйхати маводи хониш . Рӯйхати азим (ва то ҳол нигоҳ дошта мешавад) аз истинод ба ҳуҷҷатҳои BPF аз Квентин Моннет.
Манбаъ: will.com