BPF барои хурдсолон, қисми якум: BPF васеъ

Дар ибтидо технология вуҷуд дошт ва он BPF ном дошт. Мо ба вай нигаристем қаблӣ, Мақолаи Аҳди Қадим аз ин силсила. Дар соли 2013 бо кӯшишҳои Алексей Старовойтов ва Даниел Боркман версияи такмилёфтаи он, ки барои мошинҳои муосири 64-бит оптимизатсия шудааст, таҳия ва ба ядрои Linux дохил карда шуд. Ин технологияи нав ба таври мухтасар Internal BPF номида шуд, сипас Extended BPF номида шуд ва ҳоло, пас аз чанд сол, ҳама онро танҳо BPF меноманд.

Тақрибан гӯем, BPF ба шумо имкон медиҳад, ки рамзи худсаронаи корбарро дар фазои ядрои Linux иҷро кунед ва меъмории нав он қадар муваффақ шуд, ки барои тавсифи ҳамаи замимаҳои он ба мо даҳҳо мақолаи дигар лозим мешавад. (Ягона чизе, ки таҳиягарон хуб кор накарданд, тавре ки шумо дар рамзи иҷрои зер мебинед, эҷоди логотипи арзанда буд.)

Дар ин мақола сохтори мошини виртуалии BPF, интерфейсҳои ядрои кор бо BPF, асбобҳои таҳия, инчунин шарҳи мухтасар ва хеле мухтасари қобилиятҳои мавҷуда, яъне. ҳама чизеро, ки ба мо дар оянда барои амиқтар омӯхтани татбиқи амалии BPF лозим аст.
BPF барои хурдсолон, қисми якум: BPF васеъ

Хулосаи мақола

Муқаддима ба меъмории BPF. Аввалан, мо меъмории BPF-ро бо чашми парранда нигоҳ медорем ва ҷузъҳои асосиро нишон медиҳем.

Реестрҳо ва системаи фармондиҳии мошини виртуалии BPF. Аллакай дар бораи меъморӣ дар маҷмӯъ тасаввурот дорем, мо сохтори мошини виртуалии BPF-ро тавсиф хоҳем кард.

Давраи ҳаёти объектҳои BPF, системаи файлии bpffs. Дар ин бахш мо ба давраи зиндагии объектҳои BPF - барномаҳо ва харитаҳо муфассалтар назар мекунем.

Идоракунии объектҳо бо истифода аз занги системаи bpf. Бо баъзе фаҳмиши система, ки аллакай мавҷуд аст, мо ниҳоят дида мебароем, ки чӣ гуна объектҳоро аз фазои корбар бо истифода аз занги махсуси система эҷод кардан ва идора кардан мумкин аст - bpf(2).

Пишем программы BPF с помощью libbpf. Албатта, шумо метавонед бо истифода аз занги система барнома нависед. Аммо мушкил аст. Барои як сенарияи воқеӣ, барномасозони ҳастаӣ китобхона таҳия карданд libbpf. Мо як скелети асосии барномаи BPF эҷод мекунем, ки мо дар мисолҳои минбаъда истифода хоҳем кард.

Ёрдамчиёни ядро ​​​​. Дар ин ҷо мо мефаҳмем, ки чӣ тавр барномаҳои BPF метавонанд ба функсияҳои ёрирасони ядро ​​дастрасӣ пайдо кунанд - асбобе, ки дар баробари харитаҳо қобилиятҳои BPF-и навро дар муқоиса бо барномаи классикӣ ба таври куллӣ васеъ мекунад.

Дастрасӣ ба харитаҳо аз барномаҳои BPF. Дар ин лаҳза, мо ба қадри кофӣ медонем, то бифаҳмем, ки чӣ гуна мо метавонем барномаҳоеро эҷод кунем, ки харитаҳоро истифода мебаранд. Ва биёед ҳатто ба тафтишгари бузург ва тавоно назар андозем.

Воситаҳои рушд. Бахши кӯмак дар бораи чӣ гуна ҷамъ кардани утилитаҳо ва ядрои зарурӣ барои таҷрибаҳо.

Хулоса. Дар охири мақола онҳое, ки то ин вақт хондаанд, дар мақолаҳои минбаъда калимаҳои ҳавасмандкунанда ва тавсифи мухтасари он чизеро пайдо мекунанд. Мо инчунин як қатор истинодҳоро барои омӯзиши мустақилона барои онҳое, ки хоҳиш ва қобилияти интизории идомаро надоранд, номбар мекунем.

Муқаддима ба меъмории BPF

Пеш аз он ки мо ба баррасии меъмории BPF шурӯъ кунем, мо бори охир ба (оҳ) муроҷиат мекунем BPF классикӣ, ки ҳамчун посух ба пайдоиши мошинҳои RISC таҳия шудааст ва масъалаи филтркунии самараноки бастаҳоро ҳал кардааст. Меъморӣ чунон муваффақ шуд, ки дар солҳои навадум дар Беркли UNIX таваллуд шуда, ба аксари системаҳои оператсионии мавҷуда интиқол дода шуд, дар солҳои бистуми девона зинда монд ва ҳоло ҳам замимаҳои нав пайдо мекунад.

BPF нав ҳамчун вокуниш ба паҳншавии мошинҳои 64-битӣ, хидматҳои абрӣ ва эҳтиёҷоти афзоянда ба асбобҳо барои эҷоди SDN таҳия шудааст (Sзуд-dмуайян карда шудааст nкор кардан). Аз ҷониби муҳандисони шабакаи ядро ​​​​ҳамчун ивазкунандаи мукаммали классикии BPF таҳия шудааст, BPF-и нав аслан пас аз шаш моҳ барномаҳоро дар вазифаи душвори пайгирии системаҳои Linux пайдо кард ва ҳоло, пас аз шаш соли пайдоиши он, ба мо як мақолаи пурраи навбатӣ лозим мешавад. намудҳои гуногуни барномаҳоро номбар кунед.

Суратхои хандаовар

Дар асл, BPF як мошини маҷозии қуттии қум мебошад, ки ба шумо имкон медиҳад, ки рамзи "худсарона" -ро дар фазои ядро ​​бидуни осеб ба амният иҷро кунед. Барномаҳои BPF дар фазои корбар эҷод карда, ба ядро ​​бор карда мешаванд ва ба баъзе манбаи рӯйдодҳо пайваст мешаванд. Ҳодиса метавонад, масалан, интиқоли баста ба интерфейси шабака, ба кор андохтани баъзе функсияҳои ядро ​​ва ғайра. Дар ҳолати баста, барномаи BPF ба маълумот ва метамаълумоти баста дастрасӣ дорад (барои хондан ва эҳтимолан навиштан вобаста ба намуди барнома); дар ҳолати иҷро кардани функсияи ядро ​​​​аргументҳои функсия, аз ҷумла нишондиҳандаҳои хотираи ядро ​​ва ғайра.

Биёед ин равандро муфассалтар дида бароем. Барои оғоз, биёед дар бораи фарқияти аввал аз классикии BPF сӯҳбат кунем, ки барномаҳо барои онҳо дар ассемблер навишта шудаанд. Дар версияи нав, меъморӣ васеъ карда шуд, то ки барномаҳо бо забонҳои сатҳи баланд, пеш аз ҳама, албатта, дар C навишта шаванд. Барои ин, backend барои llvm таҳия карда шуд, ки ба шумо имкон медиҳад байткод барои меъмории BPF тавлид карда шавад.

BPF барои хурдсолон, қисми якум: BPF васеъ

Меъмории BPF қисман тарҳрезӣ шудааст, ки дар мошинҳои муосир самаранок кор кунад. Барои амалӣ кардани ин кор, байткоди BPF, ки як бор ба ядро ​​бор карда мешавад, бо истифода аз ҷузъе, ки компилятори JIT ном дорад, ба коди аслӣ тарҷума карда мешавад (JИСТ In Tим). Баъд, агар шумо дар хотир доред, дар BPF-и классикӣ барнома ба ядро ​​бор карда шуда, ба манбаи ҳодиса ба таври атомӣ - дар контексти занги ягонаи система замима карда мешуд. Дар меъмории нав, ин дар ду марҳила сурат мегирад - аввал, код бо истифода аз занги система ба ядро ​​бор карда мешавад. bpf(2)ва баъдтар, тавассути механизмҳои дигар, ки вобаста ба намуди барнома фарқ мекунанд, барнома ба манбаи ҳодиса пайваст мешавад.

Дар ин ҷо шояд хонанда саволе дошта бошад: оё ин имконпазир аст? Бехатарии иҷрои чунин код чӣ гуна кафолат дода мешавад? Бехатарии иҷро ба мо бо марҳилаи боркунии барномаҳои BPF бо номи verifier кафолат дода мешавад (дар забони англисӣ ин марҳила verifier номида мешавад ва ман калимаи англисиро истифода мекунам):

BPF барои хурдсолон, қисми якум: BPF васеъ

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-ро як воситаи универсалии васеъ кардани ядро ​​​​мегардонад, ки дар амал тасдиқ карда шудааст: ба 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 (барои XDP) ё сохтор struct __sk_buff (барои барномаҳои шабакавии гуногун) ё сохтор struct pt_regs (барои намудҳои гуногуни барномаҳои пайгирӣ) ва ғайра.

Ҳамин тавр, мо маҷмӯи регистрҳо, ёрирасонҳои ядро ​​​​, стек, нишондиҳандаи контекстӣ ва хотираи муштарак дар шакли харитаҳо доштем. На ин ки ҳамаи ин дар сафар комилан зарур аст, аммо...

Биёед тавсифро идома дода, дар бораи системаи фармондиҳии кор бо ин объектҳо сӯҳбат кунем. Ҳама (қариб ҳама) Дастурҳои BPF дорои андозаи собит 64-бит мебошанд. Агар шумо ба як дастур оид ба мошини 64-битаи Big Endian нигаред, шумо хоҳед дид

BPF барои хурдсолон, қисми якум: BPF васеъ

Ин аст, Code - ин рамзгузории дастур аст, Dst/Src мутаносибан рамзгузории қабулкунанда ва манбаъ мебошанд, Off - 16-бита абзор имзо, ва Imm адади бутуни имзошудаи 32-бит аст, ки дар баъзе дастурҳо истифода мешавад (монанд ба доимии cBPF К). Рамзгузорӣ Code яке аз ду намуд дорад:

BPF барои хурдсолон, қисми якум: BPF васеъ

Синфҳои дастурҳои 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-ро муайян мекунанд. Ҳангоми омӯзиши меъморӣ аз рӯи худ ва/ё таҳлили бинарҳо, шумо метавонед семантикаро дар манбаъҳои зерин пайдо кунед, ки бо тартиби мураккабӣ мураттаб шудаанд: Мушаххасоти ғайрирасмии eBPF, Дастури истинод BPF ва XDP, маҷмӯи дастурҳо, Documentation/networking/filter.txt ва, албатта, дар коди сарчашмаи Linux - verifier, JIT, BPF тарҷумон.

Мисол: ҷудо кардани 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 Оё BPF_MOV. Он ба реестри таъинот арзиш таъин мекунад. Агар бит муқаррар карда шавад s (сарчашма), он гоҳ кимат аз реестри манбаъ гирифта мешавад ва агар он тавре ки дар ҳолати мо муқаррар карда нашуда бошад, пас арзиш аз майдон гирифта мешавад. Imm. Ҳамин тавр, дар дастурҳои якум ва сеюм мо амалиётро иҷро мекунем r0 = Imm. Ғайр аз он, амалиёти JMP синфи 1 аст BPF_JEQ (агар баробар ҷаҳед). Дар мавриди мо, аз бит 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 - барномаҳо ва харитаҳо - аз фазои корбар бо истифода аз фармонҳо сохта мешаванд BPF_PROG_LOAD и BPF_MAP_CREATE занги система bpf(2), мо дар бораи маҳз чӣ гуна ин воқеа дар боби оянда сӯҳбат хоҳем кард. Ин сохторҳои додаҳои ядроиро ва барои ҳар яки онҳо эҷод мекунад refcount (шумораи истинод) ба як муқаррар карда мешавад ва тасвири файле, ки ба объект ишора мекунад, ба корбар баргардонида мешавад. Пас аз баста шудани даста refcount объект як кам мешавад ва ба сифр мерасад, объект нобуд мешавад.

Агар барнома харитаҳоро истифода барад, пас refcount ин харитаҳо пас аз боркунии барнома як маротиба зиёд мешаванд, яъне. тавсифкунандагони файли онҳо метавонанд аз раванди корбар баста шаванд ва ҳоло ҳам refcount сифр нахоҳад шуд:

BPF барои хурдсолон, қисми якум: BPF васеъ

Пас аз бомуваффақият бор кардани барнома, мо одатан онро ба як навъ генератори ҳодиса замима мекунем. Масалан, мо метавонем онро дар интерфейси шабакавӣ барои коркарди бастаҳои воридотӣ ё пайваст кардани он ба баъзеҳо ҷойгир кунем tracepoint дар асл. Дар ин лаҳза ҳисобкунаки истинод низ як маротиба зиёд мешавад ва мо метавонем дескриптори файлро дар барномаи боркунак пӯшем.

Агар мо ҳоло боркунакро хомӯш кунем, чӣ мешавад? Он аз намуди генератори ҳодиса (қалмоқ) вобаста аст. Ҳама қалмоқҳои шабакавӣ пас аз ба итмом расидани боркунак вуҷуд хоҳанд дошт, инҳо қалмоқҳои ба истилоҳ глобалӣ мебошанд. Ва, масалан, барномаҳои пайгирӣ пас аз қатъ шудани раванде, ки онҳоро эҷод кардааст (ва аз ин рӯ, аз "маҳаллӣ ба раванд" маҳаллӣ номида мешаванд) бароварда мешаванд. Аз ҷиҳати техникӣ, қалмоқҳои маҳаллӣ ҳамеша дар фазои корбар тавсифкунандаи файли мувофиқ доранд ва аз ин рӯ, ҳангоми баста шудани раванд пӯшида мешаванд, аммо қалмоқҳои глобалӣ не. Дар расми зерин, бо истифода аз салибҳои сурх, ман кӯшиш мекунам нишон диҳам, ки чӣ гуна қатъи барномаи боркунак ба мӯҳлати истифодаи объектҳо дар ҳолати қалмоқҳои маҳаллӣ ва глобалӣ таъсир мерасонад.

BPF барои хурдсолон, қисми якум: BPF васеъ

Чаро байни қалмоқҳои маҳаллӣ ва ҷаҳонӣ фарқият вуҷуд дорад? Иҷрои баъзе намудҳои барномаҳои шабакавӣ бе фазои корбарон маъно дорад, масалан, тасаввур кунед, ки муҳофизати DDoS - боркунак қоидаҳоро менависад ва барномаи BPF-ро ба интерфейси шабака пайваст мекунад, ки пас аз он пурборкунанда метавонад рафта, худро бикушад. Аз тарафи дигар, тасаввур кунед, ки барномаи пайгирии ислоҳи он, ки шумо дар даҳ дақиқа ба зонуҳои худ навиштед - вақте ки он ба итмом мерасад, шумо мехоҳед, ки дар система партов намонад ва қалмоқҳои маҳаллӣ инро таъмин мекунанд.

Аз тарафи дигар, тасаввур кунед, ки шумо мехоҳед ба нуқтаи пайгирӣ дар ядро ​​пайваст шавед ва дар тӯли солҳои зиёд омор ҷамъ кунед. Дар ин ҳолат, шумо мехоҳед қисми корбарро анҷом диҳед ва гоҳ-гоҳ ба омор баргардед. Системаи файлии bpf ин имкониятро фароҳам меорад. Ин як системаи псевдофайлӣ дар хотира мебошад, ки имкон медиҳад файлҳоеро эҷод кунад, ки ба объектҳои BPF истинод мекунанд ва ба ин васила зиёд мешаванд. refcount объектхо. Пас аз ин, боркунак метавонад берун шавад ва объектҳои офаридааш зинда боқӣ мемонанд.

BPF барои хурдсолон, қисми якум: BPF васеъ

Эҷоди файлҳо дар 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. Дар ин ҳолат, ҳамаи инстансияҳои фаъоли версияи кӯҳнаи барнома кори худро анҷом медиҳанд ва коркардкунандагони нави ҳодисаҳо аз барномаи нав сохта мешаванд ва "атомӣ" дар ин ҷо маънои онро дорад, ки ягон ҳодиса аз даст нахоҳанд рафт.

Замимаи барномаҳо ба манбаъҳои рӯйдодҳо

Дар ин мақола мо пайваст кардани барномаҳоро ба манбаъҳои рӯйдодҳо алоҳида тавсиф намекунем, зеро омӯзиши ин дар заминаи як намуди мушаххаси барнома маъно дорад. См. намуна дар зер, ки дар он мо нишон медиҳем, ки чӣ гуна барномаҳо ба монанди XDP пайваст мешаванд.

Идоракунии объектҳо бо истифода аз занги системаи 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-ро бор мекунад, аммо аввал мо бояд муайян кунем, ки кадом барномаро бор кардан мехоҳем - мо бояд интихоб кунем намуди ва дар доираи ин намуд, барномае нависед, ки аз санҷиши verifier мегузарад. Аммо, барои он ки ин равандро мушкил накунад, дар ин ҷо як ҳалли омода аст: мо як барномаеро ба мисли 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 тартиб дода шавад (мо чӣ гуна ин корро дар боб тавсиф мекунем. Воситаҳои рушд. Шумо метавонед тафтиш кунед, ки ядрои шумо бо 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]. Аммо, барои эҳтиёҷоти барномаҳое, ки берун аз ядро ​​зиндагӣ мекунанд, як анбори алоҳида нигоҳ дошта мешавад https://github.com/libbpf/libbpf ки дар он китобхонаи ядро ​​барои дастрасии хониш бештар ё камтар инъикос шудааст.

Дар ин бахш мо дида мебароем, ки чӣ тавр шумо метавонед лоиҳаеро эҷод кунед, ки истифода мебарад 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, ки рамзи дуии BPF-ро дар парвоз тавлид мекунад.

Барои осон кардани мантиқ пайравӣ кардан, мо намунаи худро бо ин мақсадҳо дубора менависем 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 буданд, яъне онҳо яке аз маъмултаринеро, ки ба онҳо наздиктаранд (аммо на барои муқаррарӣ одамон) интерфейс барои ҳамкорӣ бо ядро: розеткаҳои netlink, инчунин нигаред RFC3549. Роҳи соддатарин барои татбиқ 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 аз ҷониби ман гирифта шудааст bpf_devel_QA.)

Мо барномаҳоеро, ки нав сохтаем, насб намекунем, балки ба ҷои он танҳо ба онҳо илова кунед 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 мо метавонем муқаррариро истифода барем Бахром ядро ё яке аз ядроҳои рушд. Таърихан, рушди BPF дар дохили ҷомеаи шабакавии Linux сурат мегирад ва аз ин рӯ, ҳама тағйирот дер ё зуд тавассути Дэвид Миллер, нигоҳдории шабакаи Linux мегузарад. Вобаста аз табиати онҳо - вироишҳо ё хусусиятҳои нав - тағйироти шабакавӣ ба яке аз ду аслӣ дохил мешаванд - net ё net-next. Тағирот барои BPF ба ҳамон тарз байни тақсим карда мешавад bpf и bpf-next, ки баъд мутаносибан ба net ва net-next ҷамъ карда мешаванд. Барои тафсилоти бештар нигаред bpf_devel_QA и netdev-Саволҳои Савол. Пас, ядроро дар асоси завқи худ ва эҳтиёҷоти устувории системае, ки шумо дар он озмоиш мекунед, интихоб кунед (*-next ядроҳо ноустувортарин дар рӯйхат мебошанд).

Дар бораи чӣ гуна идора кардани файлҳои конфигуратсияи ядро ​​​​сухан кардан аз доираи ин мақола берун нест - гумон меравад, ки шумо ё ин корро аллакай медонед, ё омода барои омӯхтан ба худ. Бо вуҷуди ин, дастурҳои зерин бояд бештар ё камтар кофӣ бошанд, то ба шумо системаи кории BPF-ро фароҳам оранд.

Яке аз ядроҳои болоро зеркашӣ кунед:

$ 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 ва барномаҳои амниятӣ.

Мақолаҳои қаблӣ дар ин силсила

  1. BPF барои хурдсолон, қисми сифр: BPF классикӣ

Пайвандҳо

  1. Дастури истинодҳои BPF ва XDP - ҳуҷҷатҳо дар бораи BPF аз cilium, ё аниқтараш аз Даниел Боркман, яке аз созандагон ва нигоҳдорони BPF. Ин яке аз аввалин тавсифҳои ҷиддӣ аст, ки аз дигарон бо он фарқ мекунад, ки Дониёл дақиқ медонад, ки дар бораи чӣ менависад ва дар он ҳеҷ хатогӣ вуҷуд надорад. Аз ҷумла, ин ҳуҷҷат тарзи кор бо барномаҳои BPF-и навъи XDP ва TC-ро бо истифода аз утилитаи маъруф тавсиф мекунад. ip аз баста iproute2.

  2. Documentation/networking/filter.txt - файли аслӣ бо ҳуҷҷатҳо барои BPF классикӣ ва сипас васеъ. Хониши хуб, агар шумо хоҳед, ки ба забони маҷлис ва ҷузъиёти меъмории техникӣ омӯзед.

  3. Блог дар бораи BPF аз Facebook. Он хеле кам, аммо ба таври мувофиқ нав карда мешавад, тавре ки Алексей Старовойтов (муаллифи eBPF) ва Андрей Накрйико - (нигоҳдор) дар он ҷо менависанд libbpf).

  4. Асрори bpftool. Риштаи ҷолиби твиттер аз Квентин Монет бо мисолҳо ва асрори истифодаи bpftool.

  5. Мубтало ба BPF: рӯйхати маводи хониш. Рӯйхати азим (ва то ҳол нигоҳ дошта мешавад) аз истинод ба ҳуҷҷатҳои BPF аз Квентин Моннет.

Манбаъ: will.com

Илова Эзоҳ