Slide 1

Slide 1 text

Story of Rucy: “Compile” a Ruby Script into BPF

Slide 2

Slide 2 text

Senior-Principal Engineer@GMO Pepabo DataOps & Dev Productivity Engineering Team RubyKaigi Speaker (’16, ’18 && ’19) RubyKaigi Organizer (’19 @ Fukuoka) Hacker Supporter @ Fukuoka City Engineer’s Café Ruby, mruby, Rust, Containers, Kernel, Duolingo Uchio Kondo:

Slide 3

Slide 3 text

ToC • 1) What is BPF? Has it something to do with us? • 2) What is Rucy for, and Why? • 3) What is going on behind Rucy • Overview, BPF Opcode and mruby VM Opcode, Transpilation • 4) Conclusion, demo aiming at the future ࠓ೔࿩͢಺༰Ͱ͢ɻ 3VDZͷ࿩Λ͢Δલʹલఏ஌͕ࣝͨ͘͞Μ͋ΔͷͰɺॱ࣍આ໌͍͖ͯ͠·͢ɻ

Slide 4

Slide 4 text

What is BPF? Has it something to do with us?

Slide 5

Slide 5 text

BPF is: • One of the latest Linux SOTAs • Used, for example, in the following areas: • Networking • Server Tracing (Observability) • Do you know DTrace? What BPF can do is almost like that. • Device Access Auditing for containers #1'͸-JOVYͷ࠷ઌ୺ٕज़ͷҰͭͰ͢ɻωοτϫʔΫɺύϑΥʔϚϯεɾτϨʔεɺ ίϯςφ಺෦ͷσόΠεΞΫηε੍ޚͷ಺෦ͳͲͰ࢖ΘΕ͍ͯ·͢ɻ

Slide 6

Slide 6 text

How BPF works: Observability Userspace Kernel BPF binary BPF binary BPF VM Userspace Program (libbpf) BPF Map kprobe, tracepoint... Load&Verify Filter/Aggregate Receive Informations Load&Verify Filter/Aggregate Receive Informations Visualize τϨʔεͷ৔߹ɺʮ#1'όΠφϦʯΛΧʔωϧʹϩʔυ͠ɺ Χʔωϧͷ৘ใΛूΊͯɺϢʔβεϖʔεʹฦ͠·͢ɻ

Slide 7

Slide 7 text

How BPF works: Device access auditing cgroup Kernel Devices Process BPF 👍 ❌ Access filter Try to access devices Given access context /dev/urandom /dev/null /dev/zero /dev/tty /dev/sdaXX /dev/sdbXX /dev/sdcXX ...... σόΠεΞΫηε੍ޚͷ࣌͸ɺDHSPVQʹ#1'όΠφϦϓϩάϥϜΛಡΈࠐ·ͤ ΞΫηε࣌ʹ౉͞ΕΔίϯςΫετΛར༻͠ϑΟϧλʔ͠·͢ɻ

Slide 8

Slide 8 text

What is Rucy for? And... why?

Slide 9

Slide 9 text

To begin with: Can BPF be used via Ruby? • The answer is Yes. • I created RbBCC • BCC is a BPF SDK for Python, and Lua... • I need Ruby’s one. So created. • BCC uses FFI(ctypes) to access libbcc, then RbBCC uses Fiddle. • RbBCC can do basic traces as BCC do. • RbBCC is one of the results of a Ruby Association Grant in 2019. (*) https:/ /github.com/udzura/rbbcc Thanks, ko1-san #1'͸3VCZ͔Β࢖͑·͔͢ʁ౴͑͸ʮ:FTʯͰ͢ɻ 3C#$$ͱ͍͏΋ͷΛ࢖͍·͢ɻ3VCZΞιγΤʔγϣϯͷάϥϯτର৅Ͱ΋͋Γ·͢ɻ

Slide 10

Slide 10 text

BCC’s pitfalls • Tools with (Rb)BCC do the whole BPF compilation process • When it starts tracing - Just In Time • It causes: • 1) overhead • 2) large number of files required to build • The compilation process needs LLVM, clang and kernel headers. ͔͠͠ɺ#$$ʢ3C#$$ʣ͸ɺτϨʔεͷ࠷ॳʹ#1'ϓϩάϥϜΛͦͷ৔ͰίϯύΠϧ͠·͢ɻ Φʔόʔϔου΍ɺίϯύΠϧʹඞཁͳ؀ڥͷංେԽͳͲ໰୊͕͋Γ·͢ɻ

Slide 11

Slide 11 text

Brand-new BPF tool for Ruby • Linux community recommends to precompile the "BPF binary” • As tools using on-the-fly build are such problematic • Tools should be served as “One Binary” ✴ There are also mechanisms to absorb differences in execution environments such as the kernel version. ✴ These portable one-binary commands are called BPF CO-RE (Compile Once, Run Everywhere). BPF Tracing Program Userspace Part (C, Rust, Go...) BPF Binary (Bytecodes) + ͳͷͰࣄલʹίϯύΠϧͨ͠ʮ#1'όΠφϦʯͷར༻͕ਪ঑͞Ε·͢ɻ ϓϩάϥϜຊମʹ#1'όΠφϦΛ݁߹ͯ͠ɺϫϯόΠφϦͰఏڙՄೳͰ͢ɻ

Slide 12

Slide 12 text

“One Binary” in Ruby • mruby seems to be the best way • to achieve the "one binary for everything" goal. • But - "BPF binary” can only be created in C, effectively NSVCZΛ࢖͑͹ϫϯόΠφϦΛ࡞Δ͜ͱ͕Ͱ͖ͦ͏Ͱ͢ɻ Ͱ͕͢ɺʮ#1'όΠφϦʯ΋༻ҙ͠ͳ͍ͱ͍͚·ͤΜɻ͜Ε͸࣮࣭తʹ$ݴޠͰ͔͠ॻ͚·ͤΜɻ

Slide 13

Slide 13 text

Making a BPF binary • We need to pass the C code to the clang command: • I decided to make a tool that enable Rubyists - • To make "BPF binaries" without writing any C code! ✴ In fact, Rust can create BPF binaries using RedBPF crate. But - this is RubyKaigi. 🆕 ͡Ό͋ɺશ෦3VCZͰॻ͚ΔΑ͏ʹ͠·͠ΐ͏ɻ

Slide 14

Slide 14 text

Summary so far: • BPF is an emerging Linux technology • used in fast firewalls, lightweight tracers, and containers security. • You can use BPF technology from Ruby via BCC. • But BCC is problematic because it compiles on the fly. • Precompiled "One binary" is recommended. • You need to write C language for the "BPF binary" part. • Writing every part of BPF program in Ruby is a desire of Rubyists. ͜͜·Ͱͷ·ͱΊͰ͢ɻ ͱʹ͔͘ɺ#1'ʹؔΘΔ͢΂ͯͷύʔτΛ3VCZͰॻ͖ͨ͋͘Γ·ͤΜ͔ʁ

Slide 15

Slide 15 text

Only with the Rucy. ͦ͏ɺ3VDZͳΒͶɻ

Slide 16

Slide 16 text

What is going on? Technologies behind Rucy

Slide 17

Slide 17 text

Reprise: what is Rucy • Rucy is a “Ruby Compiler” to BPF target. • In other words: • Rucy generates “BPF binary” directly from a plain-old Ruby script. ✴ Rucy is named after “Ruby Compiler” (Ru-C -> Rucy) 3VDZʢ3V$ʣ͸ʮ3VCZ$PNQJMFSʯͰ͢ɻ ͩ͘Μͷʮ#1'όΠφϦʯΛɺ3VCZεΫϦϓτ͔Β௚઀ίϯύΠϧͯ͠ੜ੒͠·͢ɻ

Slide 18

Slide 18 text

Architecture overview Ruby Script mruby OpCodes BPF OpCodes BPF Object (ELF format) mruby Rucy transpiler ✴ Rucy is built upon Rust , with mruby embedded. ΞʔΩςΫνϟશମͰ͢ɻ3VCZεΫϦϓτΛNSVCZΦϖίʔυʹίϯύΠϧ͠ɺ ͦΕΛ͞Βʹ#1'Φϖίʔυʹม׵ɺ࠷ޙʹ&-'ϑΝΠϧʹ·ͱΊ·͢ɻ

Slide 19

Slide 19 text

BPF OpCode Overview • BPF has its own VM and OpCode set. • The VM can use 10 registers • BPF instructions are basically 64bit fixed length • Instruction classes are: LD, LDX, ST, STX, ALU, JMP, and ALU64 • Layout: ASCII C Struct #1'͸ɺಠࣗͷϨδελϚγϯ7.Λ࣋ͪ·͢ɻͷϨδελ͕͋Γɺ ໋ྩ͸͓͓ΉͶCJUͷݻఆ௕Ͱ͢ɻόΠφϦϨΠΞ΢τ͸εϥΠυͷ௨ΓͰ͢ɻ

Slide 20

Slide 20 text

BPF instruction examples Instruction Pseudo code #1'໋ྩͷྫɻ-%9ɺ"-6ɺ+.1ͳͲ͕͋Γ·͢ɻ

Slide 21

Slide 21 text

Mruby OpCode Overview • Like CRuby, mruby has its VM and OpCodes • can use 65536 registers, recommended to use only 256. • Instructions are in variable length • It depends on what kind of operands it will take. • The sizes of the operand variable ... • B (8 bits), S (16 bits), W (24 bits), and Z (no operand) NSVCZ΋ϨδελϚγϯͷ7.͕͋Γ·͢ɻϨδελ͸ͨ͘͞Μ࢖͑·͢ɻ ໋ྩ͸Մม௕Ͱ͢ɻ#ɺ4ɺ8ɺ;ͷΦϖϥϯυ͕͋Γ·͢ɻ

Slide 22

Slide 22 text

mruby instruction examples Bytes OpCode&Operand # Pseudo code NSVCZͷ໋ྩͰ͢ɻͨͱ͑͹01@-0"%*͸CJUɺ01@&/5&3͸CJUͷ໋ྩ௕Ͱ͢ɻ

Slide 23

Slide 23 text

Transpile OpCodes

Slide 24

Slide 24 text

Bytecode transpilation process • Here is a sample Ruby code. ͯ͞ɺόΠτίʔυΛτϥϯεύΠϧ͢Δํ๏Λઆ໌͠·͢ɻ ͪ͜Β͕ࠓճͷ3VCZͷίʔυͰ͢ɻ

Slide 25

Slide 25 text

This code outputs mruby OpCodes like: ͜ͷ3VCZίʔυ͸͜͏͍͏NSVCZΦϖίʔυʹͳΓ·͢ɻ

Slide 26

Slide 26 text

The goal of transpilation mruby opcode BPF opcode ࠷ऴతʹɺ͜͏͍͏#1'ͷΦϖίʔυʹม׵͞Ε·͢ɻ

Slide 27

Slide 27 text

First: ignore some instructions ·ͣɺ͍͔ͭ͘ͷ#1'ͷରԠ͢Δ໋ྩ͕ͳ͍Φϖίʔυ͸ແࢹ͠·͢ɻ

Slide 28

Slide 28 text

Second: direct translation (e.g. OP_LOADI) Traspilation Code: ͦͷ··ʮ຋༁ʯͰ͖Δ΋ͷ͸ͦ͏͠·͢ɻ 01@-0"%*͸#1'ͷํͰ΋ͨͩͷ୅ೖʹͳΓ·͢ɻ

Slide 29

Slide 29 text

Then: object access to structure access / / remember local variable name Traspilation Code: ࣍͸ʮΦϒδΣΫτ΁ͷଐੑతϝιουʯΛʮߏ଄ମͷϝϯόʯʹ຋༁͠·͢ɻ ࠷ॳʹɺ3ʹDUYม਺͕ೖ͍ͬͯΔ͜ͱΛอଘ͓͖ͯ͠·͢ɻ

Slide 30

Slide 30 text

Then: object access to structure access / / calculate offset / / with varname, symname / / c.f. ctx.send(:minor) ม਺໊ͱɺγϯϘϧ໊͕෼͔Ε͹ ߏ଄ମͱͯ͠ͷϝϯό΁ͷΦϑηοτ͕ܭࢉͰ͖ɺ#1'ʹ຋༁Ͱ͖·͢ɻ

Slide 31

Slide 31 text

More: if state to JUDGE/GOTO “R3: bool = R3 == R4” Traspilation Code: ࣍͸+.1໋ྩͰ͕͢ɺNSVCZ͸໋ྩඞཁͳͷʹɺ#1'͸໋ྩʹͳΓ·͢ɻ ࠩΛຒΊΔͨΊɺ·ͣɺ01@&2Λݮࢉૢ࡞ʹม׵ͯ͠͠·͍·͢ɻ

Slide 32

Slide 32 text

More: if state to JUDGE/GOTO / / save GOTO label info / / Check r3 as “u32” Traspilation Code: ࣍ͷ໋ྩͰʮ3͕θϩ͔൱͔ʯΛݕࠪͯ͠(050͢Ε͹ɺ શମͱͯ͠01@&201@+.1/05Λ຋༁͢Δ͜ͱ͕Ͱ͖·ͨ͠ɻ

Slide 33

Slide 33 text

Finally: return a register value / / R0 = R3 / / exit (return R0) Traspilation Code: ࠷ޙʹSFUVSOॲཧͰ͢ɻNSVCZ͸೚ҙͷϨδελΛฦͤ·͕͢ɺ#1'͸3͚ͩͰ͢ɻ NSVCZ͸3Λ࢖Θͳ͍ͷͰɺܾΊଧͪͰ୅ೖͰ͖·͢ɻ

Slide 34

Slide 34 text

Again: Generated BPF opcodes ͱ͍͏͜ͱͰɺ#1'ͷΦϖίʔυΛੜ੒Ͱ͖·ͨ͠ɻ

Slide 35

Slide 35 text

Again: Generated BPF opcodes օ͞ΜΛஔ͍ͯߦ͔ͳ͍Α͏ɺ৭Λ෇͚ͯΈ·ͨ͠ɻόΠφϦ৭෇͚܎Ͱ͢ɻ

Slide 36

Slide 36 text

Conclusion, demo and future You’ll see the magic!

Slide 37

Slide 37 text

Conclusion • We can create a BPF binary object from Ruby script directly. • This BPF object + mruby + libbpf = whole command binary • Less context switch for programers • Shared data structures (in the future? Not yet implemented...) 3VCZΛίϯύΠϧͯ͠#1'Λ࡞Γ·ͨ͠ɻશ෦3VCZͰॻ͚Δͱศརͩͱࢥ͍·͢ɻ কདྷ͸3VCZͷΫϥεͰɺΧʔωϧͱϢʔβϥϯυͷσʔλߏ଄Λڞ༗Ͱ͖Ε͹ͱɻকདྷ͸ɻ

Slide 38

Slide 38 text

Demo #1: cgroup device filter cf. counterpart C σϞͦͷɻDHSPVQͷσόΠεΞΫηεϑΟϧλΛ࣮૷͠·͢ɻ

Slide 39

Slide 39 text

Compile it with rucy command • Then, check the load program works properly

Slide 40

Slide 40 text

Demo #2: Tracing kernel function • Working demo: • https://github.com/udzura/mruby-rubykaigi-rucy-sample σϞͦͷɻΧʔωϧͷUDQ@DPOOFDUؔ਺ͷݺͼग़͠ΛɺLQSPCFܦ༝ͰτϨʔε͠·͢ɻ

Slide 41

Slide 41 text

Tracing TCP’s connect on the host • By mruby cli tool embedded with Rucy BPF object

Slide 42

Slide 42 text

Restrictions and aiming at the future • Only implemented the really minimum BPF functionality. • 🙆: Generate very basic BPF opcodes • 🙆: Call BPF-specific helper functions (e.g. bpf_trace_printk()) • 🙅: Handle a BPF Map data structure • 🙅: Inject parameters via rodata, ... • Gradually implement all of them. See you soon! ݱࡏɺຊ౰ʹ࠷௿ݶͷػೳ͔࣮͠૷͍ͯ͠·ͤΜ͕ɺͨͱ͑͹#1'.BQͷར༻ͳͲɺ ॱ࣍ඞཁͦ͏ͳػೳΛ࣮૷͍͖͍ͯͨ͠ؾ࣋ͪ͸͋Γ·͢ɻ

Slide 43

Slide 43 text

Enjoy binary hack! See you in 2022