BPF untuk anak kecil, bagian nol: BPF klasik

Berkeley Packet Filters (BPF) adalah teknologi kernel Linux yang telah ada di halaman depan publikasi teknis berbahasa Inggris selama beberapa tahun sekarang. Konferensi dipenuhi dengan laporan tentang penggunaan dan pengembangan BPF. David Miller, manajer subsistem jaringan Linux, menyebut ceramahnya di Linux Plumbers 2018 β€œPembicaraan ini bukan tentang XDP” (XDP adalah salah satu cara untuk menggunakan BPF). Brendan Gregg membaca laporan yang disebut Linux BPF Superpowers . Toke HΓΈiland-JΓΈrgensen tertawa bahwa intinya sekarang adalah microkernel. Thomas Graf memuji gagasan bahwa BPF adalah javascript untuk kernel .


Masih belum ada deskripsi sistematis BPF tentang Habr, dan dalam serangkaian artikel saya akan mencoba menceritakan tentang sejarah teknologi, menjelaskan arsitektur dan alat pengembangan, menguraikan bidang aplikasi dan praktik menggunakan BPF. Dalam satu ini, nol, seri artikel ini membahas sejarah dan arsitektur BPF klasik, dan mengungkapkan rahasia prinsip-prinsip operasi tcpdump, seccomp, strace, dan banyak lagi.


Pengembangan BPF dipantau oleh komunitas jaringan Linux, aplikasi BPF utama yang ada terkait dengan jaringan dan oleh karena itu, dengan izin @ eucariot , saya telah menamai BPF untuk seri terkecil untuk menghormati seri hebat Networks for the Youngest .


Sejarah Singkat BPF ( c )


Teknologi BPF modern adalah versi yang ditingkatkan dan ditambah dari teknologi lama dengan nama yang sama, yang sekarang disebut, untuk menghindari kebingungan, BPF klasik. Berdasarkan BPF klasik, utilitas terkenal tcpdump, mekanisme seccomp, serta modul yang kurang terkenal xt_bpfuntuk iptablesdan classifier dibuat cls_bpf. Di Linux modern, program BPF klasik secara otomatis diterjemahkan ke dalam bentuk baru, namun, dari sudut pandang pengguna, API tetap ada dan aplikasi baru BPF klasik, seperti yang akan kita lihat dalam artikel ini, masih ada. Untuk alasan ini, dan juga karena mengikuti sejarah perkembangan BPF klasik di Linux, akan menjadi lebih jelas bagaimana dan mengapa itu berkembang menjadi bentuk modern, saya memutuskan untuk memulai dengan sebuah artikel tentang BPF klasik.


Lawrence Berkeley Laboratory , . , CSPF (CMU/Stanford Packet Filter), , , .. , . , β€” .


RISC . Berkeley Labs BPF (Berkeley Packet Filters), Motorola 6502 β€” Apple II NES. .


BPF


-, . , 32- , A X, 64 (16 ), , . , , .., , .


. BPF , - (, ), - (, β€” ). (, ), - (, ).


, : . , The BSD Packet Filter / Documentation/networking/filter.txt . , libpcap: An Architecture and Optimization Methodology for Packet Capture, McCanne, BPF, libpcap.


BPF Linux: tcpdump (libpcap), seccomp, xt_bpf, cls_bpf.


tcpdump


BPF β€” tcpdump. , BPF, , .


( Linux 5.6.0-rc6. .)


: IPv6


, IPv6 eth0. tcpdump ip6:


$ sudo tcpdump -i eth0 ip6

tcpdump ip6 - BPF (. Tcpdump: ). , eth0. n, n tcpdump.



, tcpdump tcpdump, -d:


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

ldh [12], Β« A - (16 ), 12Β» β€” ? , x (x+1)- . Ethernet eth0, , ( , VLAN ):


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

ldh [12] A Ether Type β€” Ethernet- . 1 A ( ) c 0x86dd, IPv6. 1 β€” jt 2 jf 3 β€” , (A == 0x86dd) . , (IPv6) 2, β€” 3. 3 0 ( ), 2 262144 ( 256 ).


: TCP


, TCP 666. IPv4, IPv6 . , IPv6 (ip6 and tcp dst port 666) (tcp dst port 666). , :


$ sudo tcpdump -i eth0 -d ip and tcp dst port 666
(000) ldh      [12]
(001) jeq      #0x800           jt 2    jf 10
(002) ldb      [23]
(003) jeq      #0x6             jt 4    jf 10
(004) ldh      [20]
(005) jset     #0x1fff          jt 10   jf 6
(006) ldxb     4*([14]&0xf)
(007) ldh      [x + 16]
(008) jeq      #0x29a           jt 9    jf 10
(009) ret      #262144
(010) ret      #0

0 1 . 2 , IPv4 (Ether Type = 0x800) A 24- .


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

A Protocol IP, , TCP . Protocol 0x6 (IPPROTO_TCP) 3.


4 5 , 20, jset , β€” jset . , IP , , . . , .


6 β€” . ldxb 4*([14]&0xf) , X , 4. β€” Internet Header Length IPv4, , 4. , 4*([14]&0xf) β€” , X, .. ldb 4*([14]&0xf) ldxb 5*([14]&0xf) ( offset, , ldxb 4*([16]&0xf)). , BPF , X ( ) IPv4.


, 7 -, (X+16). , 14 Ethernet, X IPv4, , A TCP:


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

, 8 9 10 β€” .


Tcpdump:


, BPF . , tcpdump tcpdump libpcap. , libpcap, :



, pcap_setfilter Linux, strace ( ):


$ sudo strace -f -e trace=%network tcpdump -p -i eth0 ip
socket(AF_PACKET, SOCK_RAW, 768)        = 3
bind(3, {sa_family=AF_PACKET, sll_protocol=htons(ETH_P_ALL), sll_ifindex=if_nametoindex("eth0"), sll_hatype=ARPHRD_NETROM, sll_pkttype=PACKET_HOST, sll_halen=0}, 20) = 0
setsockopt(3, SOL_SOCKET, SO_ATTACH_FILTER, {len=4, filter=0xb00bb00bb00b}, 16) = 0
...

raw Ethernet eth0. , ip BPF , , SO_ATTACH_FILTER setsockopt 4. .


, BPF , BPF .


Versi output yang sedikit lebih lengkap terlihat seperti ini:


$ sudo strace -f -e trace=%network tcpdump -p -i eth0 ip
socket(AF_PACKET, SOCK_RAW, 768)        = 3
bind(3, {sa_family=AF_PACKET, sll_protocol=htons(ETH_P_ALL), sll_ifindex=if_nametoindex("eth0"), sll_hatype=ARPHRD_NETROM, sll_pkttype=PACKET_HOST, sll_halen=0}, 20) = 0
setsockopt(3, SOL_SOCKET, SO_ATTACH_FILTER, {len=1, filter=0xbeefbeefbeef}, 16) = 0
recvfrom(3, 0x7ffcad394257, 1, MSG_TRUNC, NULL, NULL) = -1 EAGAIN (Resource temporarily unavailable)
setsockopt(3, SOL_SOCKET, SO_ATTACH_FILTER, {len=4, filter=0xb00bb00bb00b}, 16) = 0
...

, 5, 3 4? , libpcap β€” , , , ret #0 ( ), , .


, Linux BPF, struct sock_fprog , setsockopt.


, , raw. , , UDP . ( , .)


setsockopt . socket(7), struct sock_fprog tcpdump BPF .


BPF XXI


BPF Linux 1997 libpcap (Linux- , , , ). , BPF 2011 , Eric Dumazet , Just In Time Compiler β€” BPF x86_64 .


JIT compiler : 2012 seccomp, BPF, 2013 xt_bpf, iptables BPF, 2013 cls_bpf, BPF .


, BPF, , libpcap ( : , libpcap β€” 0 0x40000) , seccomp, .


BPF


BPF, :


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

64 , 16 β€” , , jt jf, 32 K, . , ret, 6, K. C BPF


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

β€”


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

, ( , , [1]). ip6 :


struct sock_filter code[] = {
        { 0x28, 0, 0, 0x0000000c },
        { 0x15, 0, 1, 0x000086dd },
        { 0x06, 0, 0, 0x00040000 },
        { 0x06, 0, 0, 0x00000000 },
};
struct sock_fprog prog = {
        .len = ARRAY_SIZE(code),
        .filter = code,
};

prog


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

, (, , -, ..). <linux/filter.h> - β€” , ,


struct sock_filter code[] = {
        BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 12),
        BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, ETH_P_IPV6, 0, 1),
        BPF_STMT(BPF_RET|BPF_K, 0x00040000),
        BPF_STMT(BPF_RET|BPF_K, 0),
}

, - . Linux tools/bpf BPF.


tcpdump, . , , , TCP/IPv4:


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

< >,<code1> <jt1> <jf1> <k1>,..., TCP


$ tools/bpf/bpf_asm /tmp/tcp-over-ipv4.bpf
6,40 0 0 12,21 0 3 2048,48 0 0 23,21 0 1 6,6 0 0 4294967295,6 0 0 0,

C :


$ tools/bpf/bpf_asm -c /tmp/tcp-over-ipv4.bpf
{ 0x28,  0,  0, 0x0000000c },
{ 0x15,  0,  3, 0x00000800 },
{ 0x30,  0,  0, 0x00000017 },
{ 0x15,  0,  1, 0x00000006 },
{ 0x06,  0,  0, 0xffffffff },
{ 0x06,  0,  0, 0000000000 },

struct sock_filter, .


Linux netsniff-ng


BPF, Linux tools/bpf/bpf_asm . , struct sk_buff, . , - , ldw cpu A raw_smp_processor_id(). ( BPF kernel helpers , , .) , , poff, payload offset:


ld poff
ret a

BPF tcpdump, netsniff-ng, , , netsniff-ng, , BPF , , tools/bpf/bpf_asm, BPF bpfc. , . .


seccomp


, BPF , β€” seccomp, BPF , .


seccomp 2005 , β€” , , : read, write, exit sigreturn, , SIGKILL. 2012 seccomp BPF , . (, Chrome, Chrome KRSI, BPF Linux Security Modules.) .


, seccomp, - ( ) . : seccomp seccomp, 2007 , BPF ( libseccomp), seccomp Docker, . systemd Β« Docker !Β» , , , systemd.


seccomp C libseccomp , seccomp strace.


seccomp


BPF seccomp. , . seccomp(2):


seccomp(SECCOMP_SET_MODE_FILTER, flags, &filter)

&filter β€” struct sock_fprog, .. BPF.


seccomp ? . , , , seccomp


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

nr β€” , arch β€” ( ), args β€” , instruction_pointer β€” , . , , A


ldw [0]

seccomp , , 32- - β€” ldh [0] seccomp EINVAL. seccomp_check_filter() . ( , , seccomp, mod ( ) seccomp BPF , ABI.)


, , seccomp . ,


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

304, 176, 239, 279. ? , . seccomp ( arch struct seccomp_data). :


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

.


seccomp libseccomp


BPF , / . libseccomp, .


, , , , , , ( , ):


#include <seccomp.h>
#include <unistd.h>
#include <err.h>

static int sys_numbers[] = {
        __NR_mount,
        __NR_umount2,
       // ...  40   ...
        __NR_vmsplice,
        __NR_perf_event_open,
};

int main(int argc, char **argv)
{
        scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_ALLOW);

        for (size_t i = 0; i < sizeof(sys_numbers)/sizeof(sys_numbers[0]); i++)
                seccomp_rule_add(ctx, SCMP_ACT_TRAP, sys_numbers[i], 0);

        seccomp_load(ctx);

        execvp(argv[1], &argv[1]);
        err(1, "execlp: %s", argv[1]);
}

sys_numbers 40+ . , ctx , (SCMP_ACT_ALLOW) - ( ). , , . SCMP_ACT_TRAP, seccomp SIGSYS , . , seccomp_load, seccomp(2).


libseccomp, :


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

:


$ ./seccomp_lib echo ok
ok

:


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

strace, :


$ sudo strace -e seccomp ./seccomp_lib mount -t bpf bpf /tmp
seccomp(SECCOMP_SET_MODE_FILTER, 0, {len=50, filter=0x55d8e78428e0}) = 0
--- SIGSYS {si_signo=SIGSYS, si_code=SYS_SECCOMP, si_call_addr=0xboobdeadbeef, si_syscall=__NR_mount, si_arch=AUDIT_ARCH_X86_64} ---
+++ killed by SIGSYS (core dumped) +++
Bad system call

, - mount(2).


, libseccomp, . , β€” . libseccomp , SCMP_FLTATR_CTL_OPTIMIZE. 2, .


, , BPF , :


$ echo 1 3 6 8 13 | ./generate_bin_search_bpf.py
ld [0]
jeq #6, bad
jgt #6, check8
jeq #1, bad
jeq #3, bad
ret #0x7fff0000
check8:
jeq #8, bad
jeq #13, bad
ret #0x7fff0000
bad: ret #0

, BPF ( , , jmp A jmp [label+X]) .


seccomp strace


strace β€” Linux. , . , strace ptrace(2), , , .., ,


$ time strace du /usr/share/ >/dev/null 2>&1

real    0m3.081s
user    0m0.531s
sys     0m2.073s


$ time strace -e open du /usr/share/ >/dev/null 2>&1

real    0m2.404s
user    0m0.193s
sys     0m1.800s

, .


--seccomp-bpf, strace 5.3, :


$ time strace --seccomp-bpf -e open du /usr/share/ >/dev/null 2>&1

real    0m0.148s
user    0m0.017s
sys     0m0.131s

$ time du /usr/share/ >/dev/null 2>&1

real    0m0.140s
user    0m0.024s
sys     0m0.116s

(, , , . , , newfsstat, strace , --seccomp-bpf.)


? strace PTRACE_SYSCALL. () , strace, PTRACE_SYSCALL. strace, PTRACE_SYSCALL, ..



seccomp, , , . , X, BPF , X SECCOMP_RET_TRACE, β€” SECCOMP_RET_ALLOW:


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

strace PTRACE_CONT, , X, , X, seccomp strace, PTRACE_SYSCALL ( seccomp ). , strace PTRACE_CONT seccomp.



--seccomp-bpf . -, ( -p strace), seccomp. -, , seccomp .


strace seccomp . , BPF seccomp .


xt_bpf


.


: -, 2007 , xt_u32 netfilter. cls_u32 iptables : 32 . ,


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

32 IP, 6, 0xFF ( ). β€” protocol IP 1 (ICMP). , @ β€” X . ,


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

, TCP Sequence Number 0x29. , , . BPF β€” the forgotten bytecode, xt_u32. . .


2013 xt_u32 BPF xt_bpf. : BPF iptables. , , :


iptables -A INPUT -m bpf --bytecode <> -j LOG

<> β€” bpf_asm -, ,


$ cat /tmp/test.bpf
ldb [9]
jneq #17, ignore
ret #1
ignore: ret #0

$ bpf_asm /tmp/test.bpf
4,48 0 0 9,21 0 1 17,6 0 0 1,6 0 0 0,

# iptables -A INPUT -m bpf --bytecode "$(bpf_asm /tmp/test.bpf)" -j LOG

UDP . BPF xt_bpf, , , iptables β€” IPv4. BPF , false , .


, xt_bpf , . Cloudfare. xt_bpf DDoS . Introducing the BPF Tools ( ) BPF . , bpfgen BPF , DNS- habr.com:


$ ./bpfgen --assembly dns -- habr.com
ldx 4*([0]&0xf)
ld #20
add x
tax

lb_0:
    ld [x + 0]
    jneq #0x04686162, lb_1
    ld [x + 4]
    jneq #0x7203636f, lb_1
    ldh [x + 8]
    jneq #0x6d00, lb_1
    ret #65535

lb_1:
    ret #0

X \x04habr\x03com\x00 UDP- : 0x04686162 <-> "\x04hab" ..


Cloudfare p0f -> BPF. Introducing the p0f BPF compiler , p0f p0f BPF:


$ ./bpfgen p0f -- 4:64:0:0:*,0::ack+:0
39,0 0 0 0,48 0 0 8,37 35 0 64,37 0 34 29,48 0 0 0,
84 0 0 15,21 0 31 5,48 0 0 9,21 0 29 6,40 0 0 6,
...

Cloudfare xt_bpf, XDP β€” BPF, . L4Drop: XDP DDoS Mitigations.


cls_bpf


BPF β€” cls_bpf Linux, Linux 2013 cls_u32.


, , cls_bpf, BPF β€” . , , Extended BPF, .


BPF c cls_bpf , Extended BPF : .


BPF .


classic BPF


, BPF, . , RISC, BPF, 32- 64- BPF . , BPF β€” BPF , , , sk_buff -, .


, BPF Linux API , , seccomp, , Extended BPF. ( .)


2013 , BPF. 2014 . , JIT-compiler 64- , Linux.


Artikel lebih lanjut dalam seri ini akan berbicara tentang arsitektur dan aplikasi teknologi baru, awalnya dikenal sebagai BPF internal, kemudian diperpanjang BPF, dan sekarang hanya BPF.


Referensi
  1. Steven McCanne and Van Jacobson, "The BSD Packet Filter: A New Architecture for User-level Packet Capture", https://www.tcpdump.org/papers/bpf-usenix93.pdf
  2. Steven McCanne, "libpcap: An Architecture and Optimization Methodology for Packet Capture", https://sharkfestus.wireshark.org/sharkfest.11/presentations/McCanne-Sharkfest'11_Keynote_Address.pdf
  3. tcpdump, libpcap: https://www.tcpdump.org/
  4. IPtable U32 Match Tutorial.
  5. BPF β€” the forgotten bytecode: https://blog.cloudflare.com/bpf-the-forgotten-bytecode/
  6. Introducing the BPF Tool: https://blog.cloudflare.com/introducing-the-bpf-tools/
  7. bpf_cls: http://man7.org/linux/man-pages/man8/tc-bpf.8.html
  8. A seccomp overview: https://lwn.net/Articles/656307/
  9. https://github.com/torvalds/linux/blob/master/Documentation/userspace-api/seccomp_filter.rst
  10. habr: : seccomp
  11. habr: systemd Β« Docker !Β»
  12. Paul Chaignon, "strace --seccomp-bpf: a look under the hood", https://fosdem.org/2020/schedule/event/debugging_strace_bpf/
  13. netsniff-ng: http://netsniff-ng.org/

All Articles