1const std = @import("../../std.zig");
2const errno = linux.errno;
3const unexpectedErrno = std.posix.unexpectedErrno;
4const expectEqual = std.testing.expectEqual;
5const expectError = std.testing.expectError;
6const expect = std.testing.expect;
7
8const linux = std.os.linux;
9const fd_t = linux.fd_t;
10const pid_t = linux.pid_t;
11
12pub const btf = @import("bpf/btf.zig");
13pub const kern = @import("bpf/kern.zig");
14
15// instruction classes
16pub const LD = 0x00;
17pub const LDX = 0x01;
18pub const ST = 0x02;
19pub const STX = 0x03;
20pub const ALU = 0x04;
21pub const JMP = 0x05;
22pub const RET = 0x06;
23pub const MISC = 0x07;
24
25/// 32-bit
26pub const W = 0x00;
27/// 16-bit
28pub const H = 0x08;
29/// 8-bit
30pub const B = 0x10;
31/// 64-bit
32pub const DW = 0x18;
33
34pub const IMM = 0x00;
35pub const ABS = 0x20;
36pub const IND = 0x40;
37pub const MEM = 0x60;
38pub const LEN = 0x80;
39pub const MSH = 0xa0;
40
41// alu fields
42pub const ADD = 0x00;
43pub const SUB = 0x10;
44pub const MUL = 0x20;
45pub const DIV = 0x30;
46pub const OR = 0x40;
47pub const AND = 0x50;
48pub const LSH = 0x60;
49pub const RSH = 0x70;
50pub const NEG = 0x80;
51pub const MOD = 0x90;
52pub const XOR = 0xa0;
53
54// jmp fields
55pub const JA = 0x00;
56pub const JEQ = 0x10;
57pub const JGT = 0x20;
58pub const JGE = 0x30;
59pub const JSET = 0x40;
60
61// misc fields
62/// copy A into X
63pub const TAX = 0x00;
64/// copy X into A
65pub const TXA = 0x80;
66
67//#define BPF_SRC(code) ((code) & 0x08)
68pub const K = 0x00;
69pub const X = 0x08;
70
71pub const MAXINSNS = 4096;
72
73// instruction classes
74/// jmp mode in word width
75pub const JMP32 = 0x06;
76
77/// alu mode in double word width
78pub const ALU64 = 0x07;
79
80// ld/ldx fields
81/// exclusive add
82pub const XADD = 0xc0;
83
84// alu/jmp fields
85/// mov reg to reg
86pub const MOV = 0xb0;
87
88/// sign extending arithmetic shift right */
89pub const ARSH = 0xc0;
90
91// change endianness of a register
92/// flags for endianness conversion:
93pub const END = 0xd0;
94
95/// convert to little-endian */
96pub const TO_LE = 0x00;
97
98/// convert to big-endian
99pub const TO_BE = 0x08;
100pub const FROM_LE = TO_LE;
101pub const FROM_BE = TO_BE;
102
103// jmp encodings
104/// jump != *
105pub const JNE = 0x50;
106
107/// LT is unsigned, '<'
108pub const JLT = 0xa0;
109
110/// LE is unsigned, '<=' *
111pub const JLE = 0xb0;
112
113/// SGT is signed '>', GT in x86
114pub const JSGT = 0x60;
115
116/// SGE is signed '>=', GE in x86
117pub const JSGE = 0x70;
118
119/// SLT is signed, '<'
120pub const JSLT = 0xc0;
121
122/// SLE is signed, '<='
123pub const JSLE = 0xd0;
124
125/// function call
126pub const CALL = 0x80;
127
128/// function return
129pub const EXIT = 0x90;
130
131/// Flag for prog_attach command. If a sub-cgroup installs some bpf program, the
132/// program in this cgroup yields to sub-cgroup program.
133pub const F_ALLOW_OVERRIDE = 0x1;
134
135/// Flag for prog_attach command. If a sub-cgroup installs some bpf program,
136/// that cgroup program gets run in addition to the program in this cgroup.
137pub const F_ALLOW_MULTI = 0x2;
138
139/// Flag for prog_attach command.
140pub const F_REPLACE = 0x4;
141
142/// If BPF_F_STRICT_ALIGNMENT is used in BPF_PROG_LOAD command, the verifier
143/// will perform strict alignment checking as if the kernel has been built with
144/// CONFIG_EFFICIENT_UNALIGNED_ACCESS not set, and NET_IP_ALIGN defined to 2.
145pub const F_STRICT_ALIGNMENT = 0x1;
146
147/// If BPF_F_ANY_ALIGNMENT is used in BPF_PROF_LOAD command, the verifier will
148/// allow any alignment whatsoever. On platforms with strict alignment
149/// requirements for loads and stores (such as sparc and mips) the verifier
150/// validates that all loads and stores provably follow this requirement. This
151/// flag turns that checking and enforcement off.
152///
153/// It is mostly used for testing when we want to validate the context and
154/// memory access aspects of the verifier, but because of an unaligned access
155/// the alignment check would trigger before the one we are interested in.
156pub const F_ANY_ALIGNMENT = 0x2;
157
158/// BPF_F_TEST_RND_HI32 is used in BPF_PROG_LOAD command for testing purpose.
159/// Verifier does sub-register def/use analysis and identifies instructions
160/// whose def only matters for low 32-bit, high 32-bit is never referenced later
161/// through implicit zero extension. Therefore verifier notifies JIT back-ends
162/// that it is safe to ignore clearing high 32-bit for these instructions. This
163/// saves some back-ends a lot of code-gen. However such optimization is not
164/// necessary on some arches, for example x86_64, arm64 etc, whose JIT back-ends
165/// hence hasn't used verifier's analysis result. But, we really want to have a
166/// way to be able to verify the correctness of the described optimization on
167/// x86_64 on which testsuites are frequently exercised.
168///
169/// So, this flag is introduced. Once it is set, verifier will randomize high
170/// 32-bit for those instructions who has been identified as safe to ignore
171/// them. Then, if verifier is not doing correct analysis, such randomization
172/// will regress tests to expose bugs.
173pub const F_TEST_RND_HI32 = 0x4;
174
175/// If BPF_F_SLEEPABLE is used in BPF_PROG_LOAD command, the verifier will
176/// restrict map and helper usage for such programs. Sleepable BPF programs can
177/// only be attached to hooks where kernel execution context allows sleeping.
178/// Such programs are allowed to use helpers that may sleep like
179/// bpf_copy_from_user().
180pub const F_SLEEPABLE = 0x10;
181
182/// When BPF ldimm64's insn[0].src_reg != 0 then this can have two extensions:
183/// insn[0].src_reg: BPF_PSEUDO_MAP_FD BPF_PSEUDO_MAP_VALUE
184/// insn[0].imm: map fd map fd
185/// insn[1].imm: 0 offset into value
186/// insn[0].off: 0 0
187/// insn[1].off: 0 0
188/// ldimm64 rewrite: address of map address of map[0]+offset
189/// verifier type: CONST_PTR_TO_MAP PTR_TO_MAP_VALUE
190pub const PSEUDO_MAP_FD = 1;
191pub const PSEUDO_MAP_VALUE = 2;
192
193/// when bpf_call->src_reg == BPF_PSEUDO_CALL, bpf_call->imm == pc-relative
194/// offset to another bpf function
195pub const PSEUDO_CALL = 1;
196
197/// flag for BPF_MAP_UPDATE_ELEM command. create new element or update existing
198pub const ANY = 0;
199
200/// flag for BPF_MAP_UPDATE_ELEM command. create new element if it didn't exist
201pub const NOEXIST = 1;
202
203/// flag for BPF_MAP_UPDATE_ELEM command. update existing element
204pub const EXIST = 2;
205
206/// flag for BPF_MAP_UPDATE_ELEM command. spin_lock-ed map_lookup/map_update
207pub const F_LOCK = 4;
208
209/// flag for BPF_MAP_CREATE command */
210pub const BPF_F_NO_PREALLOC = 0x1;
211
212/// flag for BPF_MAP_CREATE command. Instead of having one common LRU list in
213/// the BPF_MAP_TYPE_LRU_[PERCPU_]HASH map, use a percpu LRU list which can
214/// scale and perform better. Note, the LRU nodes (including free nodes) cannot
215/// be moved across different LRU lists.
216pub const BPF_F_NO_COMMON_LRU = 0x2;
217
218/// flag for BPF_MAP_CREATE command. Specify numa node during map creation
219pub const BPF_F_NUMA_NODE = 0x4;
220
221/// flag for BPF_MAP_CREATE command. Flags for BPF object read access from
222/// syscall side
223pub const BPF_F_RDONLY = 0x8;
224
225/// flag for BPF_MAP_CREATE command. Flags for BPF object write access from
226/// syscall side
227pub const BPF_F_WRONLY = 0x10;
228
229/// flag for BPF_MAP_CREATE command. Flag for stack_map, store build_id+offset
230/// instead of pointer
231pub const BPF_F_STACK_BUILD_ID = 0x20;
232
233/// flag for BPF_MAP_CREATE command. Zero-initialize hash function seed. This
234/// should only be used for testing.
235pub const BPF_F_ZERO_SEED = 0x40;
236
237/// flag for BPF_MAP_CREATE command Flags for accessing BPF object from program
238/// side.
239pub const BPF_F_RDONLY_PROG = 0x80;
240
241/// flag for BPF_MAP_CREATE command. Flags for accessing BPF object from program
242/// side.
243pub const BPF_F_WRONLY_PROG = 0x100;
244
245/// flag for BPF_MAP_CREATE command. Clone map from listener for newly accepted
246/// socket
247pub const BPF_F_CLONE = 0x200;
248
249/// flag for BPF_MAP_CREATE command. Enable memory-mapping BPF map
250pub const BPF_F_MMAPABLE = 0x400;
251
252/// These values correspond to "syscalls" within the BPF program's environment,
253/// each one is documented in std.os.linux.BPF.kern
254pub const Helper = enum(i32) {
255 unspec,
256 map_lookup_elem,
257 map_update_elem,
258 map_delete_elem,
259 probe_read,
260 ktime_get_ns,
261 trace_printk,
262 get_prandom_u32,
263 get_smp_processor_id,
264 skb_store_bytes,
265 l3_csum_replace,
266 l4_csum_replace,
267 tail_call,
268 clone_redirect,
269 get_current_pid_tgid,
270 get_current_uid_gid,
271 get_current_comm,
272 get_cgroup_classid,
273 skb_vlan_push,
274 skb_vlan_pop,
275 skb_get_tunnel_key,
276 skb_set_tunnel_key,
277 perf_event_read,
278 redirect,
279 get_route_realm,
280 perf_event_output,
281 skb_load_bytes,
282 get_stackid,
283 csum_diff,
284 skb_get_tunnel_opt,
285 skb_set_tunnel_opt,
286 skb_change_proto,
287 skb_change_type,
288 skb_under_cgroup,
289 get_hash_recalc,
290 get_current_task,
291 probe_write_user,
292 current_task_under_cgroup,
293 skb_change_tail,
294 skb_pull_data,
295 csum_update,
296 set_hash_invalid,
297 get_numa_node_id,
298 skb_change_head,
299 xdp_adjust_head,
300 probe_read_str,
301 get_socket_cookie,
302 get_socket_uid,
303 set_hash,
304 setsockopt,
305 skb_adjust_room,
306 redirect_map,
307 sk_redirect_map,
308 sock_map_update,
309 xdp_adjust_meta,
310 perf_event_read_value,
311 perf_prog_read_value,
312 getsockopt,
313 override_return,
314 sock_ops_cb_flags_set,
315 msg_redirect_map,
316 msg_apply_bytes,
317 msg_cork_bytes,
318 msg_pull_data,
319 bind,
320 xdp_adjust_tail,
321 skb_get_xfrm_state,
322 get_stack,
323 skb_load_bytes_relative,
324 fib_lookup,
325 sock_hash_update,
326 msg_redirect_hash,
327 sk_redirect_hash,
328 lwt_push_encap,
329 lwt_seg6_store_bytes,
330 lwt_seg6_adjust_srh,
331 lwt_seg6_action,
332 rc_repeat,
333 rc_keydown,
334 skb_cgroup_id,
335 get_current_cgroup_id,
336 get_local_storage,
337 sk_select_reuseport,
338 skb_ancestor_cgroup_id,
339 sk_lookup_tcp,
340 sk_lookup_udp,
341 sk_release,
342 map_push_elem,
343 map_pop_elem,
344 map_peek_elem,
345 msg_push_data,
346 msg_pop_data,
347 rc_pointer_rel,
348 spin_lock,
349 spin_unlock,
350 sk_fullsock,
351 tcp_sock,
352 skb_ecn_set_ce,
353 get_listener_sock,
354 skc_lookup_tcp,
355 tcp_check_syncookie,
356 sysctl_get_name,
357 sysctl_get_current_value,
358 sysctl_get_new_value,
359 sysctl_set_new_value,
360 strtol,
361 strtoul,
362 sk_storage_get,
363 sk_storage_delete,
364 send_signal,
365 tcp_gen_syncookie,
366 skb_output,
367 probe_read_user,
368 probe_read_kernel,
369 probe_read_user_str,
370 probe_read_kernel_str,
371 tcp_send_ack,
372 send_signal_thread,
373 jiffies64,
374 read_branch_records,
375 get_ns_current_pid_tgid,
376 xdp_output,
377 get_netns_cookie,
378 get_current_ancestor_cgroup_id,
379 sk_assign,
380 ktime_get_boot_ns,
381 seq_printf,
382 seq_write,
383 sk_cgroup_id,
384 sk_ancestor_cgroup_id,
385 ringbuf_output,
386 ringbuf_reserve,
387 ringbuf_submit,
388 ringbuf_discard,
389 ringbuf_query,
390 csum_level,
391 skc_to_tcp6_sock,
392 skc_to_tcp_sock,
393 skc_to_tcp_timewait_sock,
394 skc_to_tcp_request_sock,
395 skc_to_udp6_sock,
396 get_task_stack,
397 load_hdr_opt,
398 store_hdr_opt,
399 reserve_hdr_opt,
400 inode_storage_get,
401 inode_storage_delete,
402 d_path,
403 copy_from_user,
404 snprintf_btf,
405 seq_printf_btf,
406 skb_cgroup_classid,
407 redirect_neigh,
408 per_cpu_ptr,
409 this_cpu_ptr,
410 redirect_peer,
411 task_storage_get,
412 task_storage_delete,
413 get_current_task_btf,
414 bprm_opts_set,
415 ktime_get_coarse_ns,
416 ima_inode_hash,
417 sock_from_file,
418 check_mtu,
419 for_each_map_elem,
420 snprintf,
421 sys_bpf,
422 btf_find_by_name_kind,
423 sys_close,
424 timer_init,
425 timer_set_callback,
426 timer_start,
427 timer_cancel,
428 get_func_ip,
429 get_attach_cookie,
430 task_pt_regs,
431 get_branch_snapshot,
432 trace_vprintk,
433 skc_to_unix_sock,
434 kallsyms_lookup_name,
435 find_vma,
436 loop,
437 strncmp,
438 get_func_arg,
439 get_func_ret,
440 get_func_arg_cnt,
441 get_retval,
442 set_retval,
443 xdp_get_buff_len,
444 xdp_load_bytes,
445 xdp_store_bytes,
446 copy_from_user_task,
447 skb_set_tstamp,
448 ima_file_hash,
449 kptr_xchg,
450 map_lookup_percpu_elem,
451 skc_to_mptcp_sock,
452 dynptr_from_mem,
453 ringbuf_reserve_dynptr,
454 ringbuf_submit_dynptr,
455 ringbuf_discard_dynptr,
456 dynptr_read,
457 dynptr_write,
458 dynptr_data,
459 tcp_raw_gen_syncookie_ipv4,
460 tcp_raw_gen_syncookie_ipv6,
461 tcp_raw_check_syncookie_ipv4,
462 tcp_raw_check_syncookie_ipv6,
463 ktime_get_tai_ns,
464 user_ringbuf_drain,
465 cgrp_storage_get,
466 cgrp_storage_delete,
467 _,
468};
469
470// TODO: determine that this is the expected bit layout for both little and big
471// endian systems
472/// a single BPF instruction
473pub const Insn = packed struct {
474 code: u8,
475 dst: u4,
476 src: u4,
477 off: i16,
478 imm: i32,
479
480 /// r0 - r9 are general purpose 64-bit registers, r10 points to the stack
481 /// frame
482 pub const Reg = enum(u4) { r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10 };
483 const Source = enum(u1) { reg, imm };
484
485 const Mode = enum(u8) {
486 imm = IMM,
487 abs = ABS,
488 ind = IND,
489 mem = MEM,
490 len = LEN,
491 msh = MSH,
492 };
493
494 pub const AluOp = enum(u8) {
495 add = ADD,
496 sub = SUB,
497 mul = MUL,
498 div = DIV,
499 alu_or = OR,
500 alu_and = AND,
501 lsh = LSH,
502 rsh = RSH,
503 neg = NEG,
504 mod = MOD,
505 xor = XOR,
506 mov = MOV,
507 arsh = ARSH,
508 };
509
510 pub const Size = enum(u8) {
511 byte = B,
512 half_word = H,
513 word = W,
514 double_word = DW,
515 };
516
517 pub const JmpOp = enum(u8) {
518 ja = JA,
519 jeq = JEQ,
520 jgt = JGT,
521 jge = JGE,
522 jset = JSET,
523 jlt = JLT,
524 jle = JLE,
525 jne = JNE,
526 jsgt = JSGT,
527 jsge = JSGE,
528 jslt = JSLT,
529 jsle = JSLE,
530 };
531
532 const ImmOrReg = union(Source) {
533 reg: Reg,
534 imm: i32,
535 };
536
537 fn imm_reg(code: u8, dst: Reg, src: anytype, off: i16) Insn {
538 const imm_or_reg = if (@TypeOf(src) == Reg or @typeInfo(@TypeOf(src)) == .enum_literal)
539 ImmOrReg{ .reg = @as(Reg, src) }
540 else
541 ImmOrReg{ .imm = src };
542
543 const src_type: u8 = switch (imm_or_reg) {
544 .imm => K,
545 .reg => X,
546 };
547
548 return Insn{
549 .code = code | src_type,
550 .dst = @backingInt(dst),
551 .src = switch (imm_or_reg) {
552 .imm => 0,
553 .reg => |r| @backingInt(r),
554 },
555 .off = off,
556 .imm = switch (imm_or_reg) {
557 .imm => |i| i,
558 .reg => 0,
559 },
560 };
561 }
562
563 pub fn alu(comptime width: comptime_int, op: AluOp, dst: Reg, src: anytype) Insn {
564 const width_bitfield = switch (width) {
565 32 => ALU,
566 64 => ALU64,
567 else => @compileError("width must be 32 or 64"),
568 };
569
570 return imm_reg(width_bitfield | @backingInt(op), dst, src, 0);
571 }
572
573 pub fn mov(dst: Reg, src: anytype) Insn {
574 return alu(64, .mov, dst, src);
575 }
576
577 pub fn add(dst: Reg, src: anytype) Insn {
578 return alu(64, .add, dst, src);
579 }
580
581 pub fn sub(dst: Reg, src: anytype) Insn {
582 return alu(64, .sub, dst, src);
583 }
584
585 pub fn mul(dst: Reg, src: anytype) Insn {
586 return alu(64, .mul, dst, src);
587 }
588
589 pub fn div(dst: Reg, src: anytype) Insn {
590 return alu(64, .div, dst, src);
591 }
592
593 pub fn alu_or(dst: Reg, src: anytype) Insn {
594 return alu(64, .alu_or, dst, src);
595 }
596
597 pub fn alu_and(dst: Reg, src: anytype) Insn {
598 return alu(64, .alu_and, dst, src);
599 }
600
601 pub fn lsh(dst: Reg, src: anytype) Insn {
602 return alu(64, .lsh, dst, src);
603 }
604
605 pub fn rsh(dst: Reg, src: anytype) Insn {
606 return alu(64, .rsh, dst, src);
607 }
608
609 pub fn neg(dst: Reg) Insn {
610 return alu(64, .neg, dst, 0);
611 }
612
613 pub fn mod(dst: Reg, src: anytype) Insn {
614 return alu(64, .mod, dst, src);
615 }
616
617 pub fn xor(dst: Reg, src: anytype) Insn {
618 return alu(64, .xor, dst, src);
619 }
620
621 pub fn arsh(dst: Reg, src: anytype) Insn {
622 return alu(64, .arsh, dst, src);
623 }
624
625 pub fn jmp(op: JmpOp, dst: Reg, src: anytype, off: i16) Insn {
626 return imm_reg(JMP | @backingInt(op), dst, src, off);
627 }
628
629 pub fn ja(off: i16) Insn {
630 return jmp(.ja, .r0, 0, off);
631 }
632
633 pub fn jeq(dst: Reg, src: anytype, off: i16) Insn {
634 return jmp(.jeq, dst, src, off);
635 }
636
637 pub fn jgt(dst: Reg, src: anytype, off: i16) Insn {
638 return jmp(.jgt, dst, src, off);
639 }
640
641 pub fn jge(dst: Reg, src: anytype, off: i16) Insn {
642 return jmp(.jge, dst, src, off);
643 }
644
645 pub fn jlt(dst: Reg, src: anytype, off: i16) Insn {
646 return jmp(.jlt, dst, src, off);
647 }
648
649 pub fn jle(dst: Reg, src: anytype, off: i16) Insn {
650 return jmp(.jle, dst, src, off);
651 }
652
653 pub fn jset(dst: Reg, src: anytype, off: i16) Insn {
654 return jmp(.jset, dst, src, off);
655 }
656
657 pub fn jne(dst: Reg, src: anytype, off: i16) Insn {
658 return jmp(.jne, dst, src, off);
659 }
660
661 pub fn jsgt(dst: Reg, src: anytype, off: i16) Insn {
662 return jmp(.jsgt, dst, src, off);
663 }
664
665 pub fn jsge(dst: Reg, src: anytype, off: i16) Insn {
666 return jmp(.jsge, dst, src, off);
667 }
668
669 pub fn jslt(dst: Reg, src: anytype, off: i16) Insn {
670 return jmp(.jslt, dst, src, off);
671 }
672
673 pub fn jsle(dst: Reg, src: anytype, off: i16) Insn {
674 return jmp(.jsle, dst, src, off);
675 }
676
677 pub fn xadd(dst: Reg, src: Reg) Insn {
678 return Insn{
679 .code = STX | XADD | DW,
680 .dst = @backingInt(dst),
681 .src = @backingInt(src),
682 .off = 0,
683 .imm = 0,
684 };
685 }
686
687 fn ld(mode: Mode, size: Size, dst: Reg, src: Reg, imm: i32) Insn {
688 return Insn{
689 .code = @backingInt(mode) | @backingInt(size) | LD,
690 .dst = @backingInt(dst),
691 .src = @backingInt(src),
692 .off = 0,
693 .imm = imm,
694 };
695 }
696
697 pub fn ld_abs(size: Size, dst: Reg, src: Reg, imm: i32) Insn {
698 return ld(.abs, size, dst, src, imm);
699 }
700
701 pub fn ld_ind(size: Size, dst: Reg, src: Reg, imm: i32) Insn {
702 return ld(.ind, size, dst, src, imm);
703 }
704
705 pub fn ldx(size: Size, dst: Reg, src: Reg, off: i16) Insn {
706 return Insn{
707 .code = MEM | @backingInt(size) | LDX,
708 .dst = @backingInt(dst),
709 .src = @backingInt(src),
710 .off = off,
711 .imm = 0,
712 };
713 }
714
715 fn ld_imm_impl1(dst: Reg, src: Reg, imm: u64) Insn {
716 return Insn{
717 .code = LD | DW | IMM,
718 .dst = @backingInt(dst),
719 .src = @backingInt(src),
720 .off = 0,
721 .imm = @as(i32, @bitCast(@as(u32, @truncate(imm)))),
722 };
723 }
724
725 fn ld_imm_impl2(imm: u64) Insn {
726 return Insn{
727 .code = 0,
728 .dst = 0,
729 .src = 0,
730 .off = 0,
731 .imm = @as(i32, @bitCast(@as(u32, @truncate(imm >> 32)))),
732 };
733 }
734
735 pub fn ld_dw1(dst: Reg, imm: u64) Insn {
736 return ld_imm_impl1(dst, .r0, imm);
737 }
738
739 pub fn ld_dw2(imm: u64) Insn {
740 return ld_imm_impl2(imm);
741 }
742
743 pub fn ld_map_fd1(dst: Reg, map_fd: fd_t) Insn {
744 return ld_imm_impl1(dst, @as(Reg, @fromBackingInt(@intCast(PSEUDO_MAP_FD))), @as(u64, @intCast(map_fd)));
745 }
746
747 pub fn ld_map_fd2(map_fd: fd_t) Insn {
748 return ld_imm_impl2(@as(u64, @intCast(map_fd)));
749 }
750
751 pub fn st(size: Size, dst: Reg, off: i16, imm: i32) Insn {
752 return Insn{
753 .code = MEM | @backingInt(size) | ST,
754 .dst = @backingInt(dst),
755 .src = 0,
756 .off = off,
757 .imm = imm,
758 };
759 }
760
761 pub fn stx(size: Size, dst: Reg, off: i16, src: Reg) Insn {
762 return Insn{
763 .code = MEM | @backingInt(size) | STX,
764 .dst = @backingInt(dst),
765 .src = @backingInt(src),
766 .off = off,
767 .imm = 0,
768 };
769 }
770
771 fn endian_swap(endian: std.builtin.Endian, comptime size: Size, dst: Reg) Insn {
772 return Insn{
773 .code = switch (endian) {
774 .big => 0xdc,
775 .little => 0xd4,
776 },
777 .dst = @backingInt(dst),
778 .src = 0,
779 .off = 0,
780 .imm = switch (size) {
781 .byte => @compileError("can't swap a single byte"),
782 .half_word => 16,
783 .word => 32,
784 .double_word => 64,
785 },
786 };
787 }
788
789 pub fn le(comptime size: Size, dst: Reg) Insn {
790 return endian_swap(.little, size, dst);
791 }
792
793 pub fn be(comptime size: Size, dst: Reg) Insn {
794 return endian_swap(.big, size, dst);
795 }
796
797 pub fn call(helper: Helper) Insn {
798 return Insn{
799 .code = JMP | CALL,
800 .dst = 0,
801 .src = 0,
802 .off = 0,
803 .imm = @backingInt(helper),
804 };
805 }
806
807 /// exit BPF program
808 pub fn exit() Insn {
809 return Insn{
810 .code = JMP | EXIT,
811 .dst = 0,
812 .src = 0,
813 .off = 0,
814 .imm = 0,
815 };
816 }
817};
818
819test "insn bitsize" {
820 try expectEqual(@bitSizeOf(Insn), 64);
821}
822
823fn expect_opcode(code: u8, insn: Insn) !void {
824 try expectEqual(code, insn.code);
825}
826
827// The opcodes were grabbed from https://github.com/iovisor/bpf-docs/blob/master/eBPF.md
828test "opcodes" {
829 // instructions that have a name that end with 1 or 2 are consecutive for
830 // loading 64-bit immediates (imm is only 32 bits wide)
831
832 // alu instructions
833 try expect_opcode(0x07, Insn.add(.r1, 0));
834 try expect_opcode(0x0f, Insn.add(.r1, .r2));
835 try expect_opcode(0x17, Insn.sub(.r1, 0));
836 try expect_opcode(0x1f, Insn.sub(.r1, .r2));
837 try expect_opcode(0x27, Insn.mul(.r1, 0));
838 try expect_opcode(0x2f, Insn.mul(.r1, .r2));
839 try expect_opcode(0x37, Insn.div(.r1, 0));
840 try expect_opcode(0x3f, Insn.div(.r1, .r2));
841 try expect_opcode(0x47, Insn.alu_or(.r1, 0));
842 try expect_opcode(0x4f, Insn.alu_or(.r1, .r2));
843 try expect_opcode(0x57, Insn.alu_and(.r1, 0));
844 try expect_opcode(0x5f, Insn.alu_and(.r1, .r2));
845 try expect_opcode(0x67, Insn.lsh(.r1, 0));
846 try expect_opcode(0x6f, Insn.lsh(.r1, .r2));
847 try expect_opcode(0x77, Insn.rsh(.r1, 0));
848 try expect_opcode(0x7f, Insn.rsh(.r1, .r2));
849 try expect_opcode(0x87, Insn.neg(.r1));
850 try expect_opcode(0x97, Insn.mod(.r1, 0));
851 try expect_opcode(0x9f, Insn.mod(.r1, .r2));
852 try expect_opcode(0xa7, Insn.xor(.r1, 0));
853 try expect_opcode(0xaf, Insn.xor(.r1, .r2));
854 try expect_opcode(0xb7, Insn.mov(.r1, 0));
855 try expect_opcode(0xbf, Insn.mov(.r1, .r2));
856 try expect_opcode(0xc7, Insn.arsh(.r1, 0));
857 try expect_opcode(0xcf, Insn.arsh(.r1, .r2));
858
859 // atomic instructions: might be more of these not documented in the wild
860 try expect_opcode(0xdb, Insn.xadd(.r1, .r2));
861
862 // TODO: byteswap instructions
863 try expect_opcode(0xd4, Insn.le(.half_word, .r1));
864 try expectEqual(@as(i32, @intCast(16)), Insn.le(.half_word, .r1).imm);
865 try expect_opcode(0xd4, Insn.le(.word, .r1));
866 try expectEqual(@as(i32, @intCast(32)), Insn.le(.word, .r1).imm);
867 try expect_opcode(0xd4, Insn.le(.double_word, .r1));
868 try expectEqual(@as(i32, @intCast(64)), Insn.le(.double_word, .r1).imm);
869 try expect_opcode(0xdc, Insn.be(.half_word, .r1));
870 try expectEqual(@as(i32, @intCast(16)), Insn.be(.half_word, .r1).imm);
871 try expect_opcode(0xdc, Insn.be(.word, .r1));
872 try expectEqual(@as(i32, @intCast(32)), Insn.be(.word, .r1).imm);
873 try expect_opcode(0xdc, Insn.be(.double_word, .r1));
874 try expectEqual(@as(i32, @intCast(64)), Insn.be(.double_word, .r1).imm);
875
876 // memory instructions
877 try expect_opcode(0x18, Insn.ld_dw1(.r1, 0));
878 try expect_opcode(0x00, Insn.ld_dw2(0));
879
880 // loading a map fd
881 try expect_opcode(0x18, Insn.ld_map_fd1(.r1, 0));
882 try expectEqual(@as(u4, @intCast(PSEUDO_MAP_FD)), Insn.ld_map_fd1(.r1, 0).src);
883 try expect_opcode(0x00, Insn.ld_map_fd2(0));
884
885 try expect_opcode(0x38, Insn.ld_abs(.double_word, .r1, .r2, 0));
886 try expect_opcode(0x20, Insn.ld_abs(.word, .r1, .r2, 0));
887 try expect_opcode(0x28, Insn.ld_abs(.half_word, .r1, .r2, 0));
888 try expect_opcode(0x30, Insn.ld_abs(.byte, .r1, .r2, 0));
889
890 try expect_opcode(0x58, Insn.ld_ind(.double_word, .r1, .r2, 0));
891 try expect_opcode(0x40, Insn.ld_ind(.word, .r1, .r2, 0));
892 try expect_opcode(0x48, Insn.ld_ind(.half_word, .r1, .r2, 0));
893 try expect_opcode(0x50, Insn.ld_ind(.byte, .r1, .r2, 0));
894
895 try expect_opcode(0x79, Insn.ldx(.double_word, .r1, .r2, 0));
896 try expect_opcode(0x61, Insn.ldx(.word, .r1, .r2, 0));
897 try expect_opcode(0x69, Insn.ldx(.half_word, .r1, .r2, 0));
898 try expect_opcode(0x71, Insn.ldx(.byte, .r1, .r2, 0));
899
900 try expect_opcode(0x62, Insn.st(.word, .r1, 0, 0));
901 try expect_opcode(0x6a, Insn.st(.half_word, .r1, 0, 0));
902 try expect_opcode(0x72, Insn.st(.byte, .r1, 0, 0));
903
904 try expect_opcode(0x63, Insn.stx(.word, .r1, 0, .r2));
905 try expect_opcode(0x6b, Insn.stx(.half_word, .r1, 0, .r2));
906 try expect_opcode(0x73, Insn.stx(.byte, .r1, 0, .r2));
907 try expect_opcode(0x7b, Insn.stx(.double_word, .r1, 0, .r2));
908
909 // branch instructions
910 try expect_opcode(0x05, Insn.ja(0));
911 try expect_opcode(0x15, Insn.jeq(.r1, 0, 0));
912 try expect_opcode(0x1d, Insn.jeq(.r1, .r2, 0));
913 try expect_opcode(0x25, Insn.jgt(.r1, 0, 0));
914 try expect_opcode(0x2d, Insn.jgt(.r1, .r2, 0));
915 try expect_opcode(0x35, Insn.jge(.r1, 0, 0));
916 try expect_opcode(0x3d, Insn.jge(.r1, .r2, 0));
917 try expect_opcode(0xa5, Insn.jlt(.r1, 0, 0));
918 try expect_opcode(0xad, Insn.jlt(.r1, .r2, 0));
919 try expect_opcode(0xb5, Insn.jle(.r1, 0, 0));
920 try expect_opcode(0xbd, Insn.jle(.r1, .r2, 0));
921 try expect_opcode(0x45, Insn.jset(.r1, 0, 0));
922 try expect_opcode(0x4d, Insn.jset(.r1, .r2, 0));
923 try expect_opcode(0x55, Insn.jne(.r1, 0, 0));
924 try expect_opcode(0x5d, Insn.jne(.r1, .r2, 0));
925 try expect_opcode(0x65, Insn.jsgt(.r1, 0, 0));
926 try expect_opcode(0x6d, Insn.jsgt(.r1, .r2, 0));
927 try expect_opcode(0x75, Insn.jsge(.r1, 0, 0));
928 try expect_opcode(0x7d, Insn.jsge(.r1, .r2, 0));
929 try expect_opcode(0xc5, Insn.jslt(.r1, 0, 0));
930 try expect_opcode(0xcd, Insn.jslt(.r1, .r2, 0));
931 try expect_opcode(0xd5, Insn.jsle(.r1, 0, 0));
932 try expect_opcode(0xdd, Insn.jsle(.r1, .r2, 0));
933 try expect_opcode(0x85, Insn.call(.unspec));
934 try expect_opcode(0x95, Insn.exit());
935}
936
937pub const Cmd = enum(usize) {
938 /// Create a map and return a file descriptor that refers to the map. The
939 /// close-on-exec file descriptor flag is automatically enabled for the new
940 /// file descriptor.
941 ///
942 /// uses MapCreateAttr
943 map_create,
944
945 /// Look up an element by key in a specified map and return its value.
946 ///
947 /// uses MapElemAttr
948 map_lookup_elem,
949
950 /// Create or update an element (key/value pair) in a specified map.
951 ///
952 /// uses MapElemAttr
953 map_update_elem,
954
955 /// Look up and delete an element by key in a specified map.
956 ///
957 /// uses MapElemAttr
958 map_delete_elem,
959
960 /// Look up an element by key in a specified map and return the key of the
961 /// next element.
962 map_get_next_key,
963
964 /// Verify and load an eBPF program, returning a new file descriptor
965 /// associated with the program. The close-on-exec file descriptor flag
966 /// is automatically enabled for the new file descriptor.
967 ///
968 /// uses ProgLoadAttr
969 prog_load,
970
971 /// Pin a map or eBPF program to a path within the minimal BPF filesystem
972 ///
973 /// uses ObjAttr
974 obj_pin,
975
976 /// Get the file descriptor of a BPF object pinned to a certain path
977 ///
978 /// uses ObjAttr
979 obj_get,
980
981 /// uses ProgAttachAttr
982 prog_attach,
983
984 /// uses ProgAttachAttr
985 prog_detach,
986
987 /// uses TestRunAttr
988 prog_test_run,
989
990 /// uses GetIdAttr
991 prog_get_next_id,
992
993 /// uses GetIdAttr
994 map_get_next_id,
995
996 /// uses GetIdAttr
997 prog_get_fd_by_id,
998
999 /// uses GetIdAttr
1000 map_get_fd_by_id,
1001
1002 /// uses InfoAttr
1003 obj_get_info_by_fd,
1004
1005 /// uses QueryAttr
1006 prog_query,
1007
1008 /// uses RawTracepointAttr
1009 raw_tracepoint_open,
1010
1011 /// uses BtfLoadAttr
1012 btf_load,
1013
1014 /// uses GetIdAttr
1015 btf_get_fd_by_id,
1016
1017 /// uses TaskFdQueryAttr
1018 task_fd_query,
1019
1020 /// uses MapElemAttr
1021 map_lookup_and_delete_elem,
1022 map_freeze,
1023
1024 /// uses GetIdAttr
1025 btf_get_next_id,
1026
1027 /// uses MapBatchAttr
1028 map_lookup_batch,
1029
1030 /// uses MapBatchAttr
1031 map_lookup_and_delete_batch,
1032
1033 /// uses MapBatchAttr
1034 map_update_batch,
1035
1036 /// uses MapBatchAttr
1037 map_delete_batch,
1038
1039 /// uses LinkCreateAttr
1040 link_create,
1041
1042 /// uses LinkUpdateAttr
1043 link_update,
1044
1045 /// uses GetIdAttr
1046 link_get_fd_by_id,
1047
1048 /// uses GetIdAttr
1049 link_get_next_id,
1050
1051 /// uses EnableStatsAttr
1052 enable_stats,
1053
1054 /// uses IterCreateAttr
1055 iter_create,
1056 link_detach,
1057 _,
1058};
1059
1060pub const MapType = enum(u32) {
1061 unspec,
1062 hash,
1063 array,
1064 prog_array,
1065 perf_event_array,
1066 percpu_hash,
1067 percpu_array,
1068 stack_trace,
1069 cgroup_array,
1070 lru_hash,
1071 lru_percpu_hash,
1072 lpm_trie,
1073 array_of_maps,
1074 hash_of_maps,
1075 devmap,
1076 sockmap,
1077 cpumap,
1078 xskmap,
1079 sockhash,
1080 cgroup_storage_deprecated,
1081 reuseport_sockarray,
1082 percpu_cgroup_storage,
1083 queue,
1084 stack,
1085 sk_storage,
1086 devmap_hash,
1087 struct_ops,
1088
1089 /// An ordered and shared CPU version of perf_event_array. They have
1090 /// similar semantics:
1091 /// - variable length records
1092 /// - no blocking: when full, reservation fails
1093 /// - memory mappable for ease and speed
1094 /// - epoll notifications for new data, but can busy poll
1095 ///
1096 /// Ringbufs give BPF programs two sets of APIs:
1097 /// - ringbuf_output() allows copy data from one place to a ring
1098 /// buffer, similar to bpf_perf_event_output()
1099 /// - ringbuf_reserve()/ringbuf_commit()/ringbuf_discard() split the
1100 /// process into two steps. First a fixed amount of space is reserved,
1101 /// if that is successful then the program gets a pointer to a chunk of
1102 /// memory and can be submitted with commit() or discarded with
1103 /// discard()
1104 ///
1105 /// ringbuf_output() will incur an extra memory copy, but allows to submit
1106 /// records of the length that's not known beforehand, and is an easy
1107 /// replacement for perf_event_output().
1108 ///
1109 /// ringbuf_reserve() avoids the extra memory copy but requires a known size
1110 /// of memory beforehand.
1111 ///
1112 /// ringbuf_query() allows to query properties of the map, 4 are currently
1113 /// supported:
1114 /// - BPF_RB_AVAIL_DATA: amount of unconsumed data in ringbuf
1115 /// - BPF_RB_RING_SIZE: returns size of ringbuf
1116 /// - BPF_RB_CONS_POS/BPF_RB_PROD_POS returns current logical position
1117 /// of consumer and producer respectively
1118 ///
1119 /// key size: 0
1120 /// value size: 0
1121 /// max entries: size of ringbuf, must be power of 2
1122 ringbuf,
1123 inode_storage,
1124 task_storage,
1125 bloom_filter,
1126 user_ringbuf,
1127 cgroup_storage,
1128 arena,
1129
1130 _,
1131};
1132
1133pub const ProgType = enum(u32) {
1134 unspec,
1135
1136 /// context type: __sk_buff
1137 socket_filter,
1138
1139 /// context type: bpf_user_pt_regs_t
1140 kprobe,
1141
1142 /// context type: __sk_buff
1143 sched_cls,
1144
1145 /// context type: __sk_buff
1146 sched_act,
1147
1148 /// context type: u64
1149 tracepoint,
1150
1151 /// context type: xdp_md
1152 xdp,
1153
1154 /// context type: bpf_perf_event_data
1155 perf_event,
1156
1157 /// context type: __sk_buff
1158 cgroup_skb,
1159
1160 /// context type: bpf_sock
1161 cgroup_sock,
1162
1163 /// context type: __sk_buff
1164 lwt_in,
1165
1166 /// context type: __sk_buff
1167 lwt_out,
1168
1169 /// context type: __sk_buff
1170 lwt_xmit,
1171
1172 /// context type: bpf_sock_ops
1173 sock_ops,
1174
1175 /// context type: __sk_buff
1176 sk_skb,
1177
1178 /// context type: bpf_cgroup_dev_ctx
1179 cgroup_device,
1180
1181 /// context type: sk_msg_md
1182 sk_msg,
1183
1184 /// context type: bpf_raw_tracepoint_args
1185 raw_tracepoint,
1186
1187 /// context type: bpf_sock_addr
1188 cgroup_sock_addr,
1189
1190 /// context type: __sk_buff
1191 lwt_seg6local,
1192
1193 /// context type: u32
1194 lirc_mode2,
1195
1196 /// context type: sk_reuseport_md
1197 sk_reuseport,
1198
1199 /// context type: __sk_buff
1200 flow_dissector,
1201
1202 /// context type: bpf_sysctl
1203 cgroup_sysctl,
1204
1205 /// context type: bpf_raw_tracepoint_args
1206 raw_tracepoint_writable,
1207
1208 /// context type: bpf_sockopt
1209 cgroup_sockopt,
1210
1211 /// context type: void *
1212 tracing,
1213
1214 /// context type: void *
1215 struct_ops,
1216
1217 /// context type: void *
1218 ext,
1219
1220 /// context type: void *
1221 lsm,
1222
1223 /// context type: bpf_sk_lookup
1224 sk_lookup,
1225
1226 /// context type: void *
1227 syscall,
1228
1229 /// context type: bpf_nf_ctx
1230 netfilter,
1231
1232 _,
1233};
1234
1235pub const AttachType = enum(u32) {
1236 cgroup_inet_ingress,
1237 cgroup_inet_egress,
1238 cgroup_inet_sock_create,
1239 cgroup_sock_ops,
1240 sk_skb_stream_parser,
1241 sk_skb_stream_verdict,
1242 cgroup_device,
1243 sk_msg_verdict,
1244 cgroup_inet4_bind,
1245 cgroup_inet6_bind,
1246 cgroup_inet4_connect,
1247 cgroup_inet6_connect,
1248 cgroup_inet4_post_bind,
1249 cgroup_inet6_post_bind,
1250 cgroup_udp4_sendmsg,
1251 cgroup_udp6_sendmsg,
1252 lirc_mode2,
1253 flow_dissector,
1254 cgroup_sysctl,
1255 cgroup_udp4_recvmsg,
1256 cgroup_udp6_recvmsg,
1257 cgroup_getsockopt,
1258 cgroup_setsockopt,
1259 trace_raw_tp,
1260 trace_fentry,
1261 trace_fexit,
1262 modify_return,
1263 lsm_mac,
1264 trace_iter,
1265 cgroup_inet4_getpeername,
1266 cgroup_inet6_getpeername,
1267 cgroup_inet4_getsockname,
1268 cgroup_inet6_getsockname,
1269 xdp_devmap,
1270 cgroup_inet_sock_release,
1271 xdp_cpumap,
1272 sk_lookup,
1273 xdp,
1274 sk_skb_verdict,
1275 sk_reuseport_select,
1276 sk_reuseport_select_or_migrate,
1277 perf_event,
1278 trace_kprobe_multi,
1279 lsm_cgroup,
1280 struct_ops,
1281 netfilter,
1282 tcx_ingress,
1283 tcx_egress,
1284 trace_uprobe_multi,
1285 cgroup_unix_connect,
1286 cgroup_unix_sendmsg,
1287 cgroup_unix_recvmsg,
1288 cgroup_unix_getpeername,
1289 cgroup_unix_getsockname,
1290 netkit_primary,
1291 netkit_peer,
1292 trace_kprobe_session,
1293 _,
1294};
1295
1296const obj_name_len = 16;
1297/// struct used by Cmd.map_create command
1298pub const MapCreateAttr = extern struct {
1299 /// one of MapType
1300 map_type: u32,
1301
1302 /// size of key in bytes
1303 key_size: u32,
1304
1305 /// size of value in bytes
1306 value_size: u32,
1307
1308 /// max number of entries in a map
1309 max_entries: u32,
1310
1311 /// .map_create related flags
1312 map_flags: u32,
1313
1314 /// fd pointing to the inner map
1315 inner_map_fd: fd_t,
1316
1317 /// numa node (effective only if MapCreateFlags.numa_node is set)
1318 numa_node: u32,
1319 map_name: [obj_name_len]u8,
1320
1321 /// ifindex of netdev to create on
1322 map_ifindex: u32,
1323
1324 /// fd pointing to a BTF type data
1325 btf_fd: fd_t,
1326
1327 /// BTF type_id of the key
1328 btf_key_type_id: u32,
1329
1330 /// BTF type_id of the value
1331 bpf_value_type_id: u32,
1332
1333 /// BTF type_id of a kernel struct stored as the map value
1334 btf_vmlinux_value_type_id: u32,
1335};
1336
1337/// struct used by Cmd.map_*_elem commands
1338pub const MapElemAttr = extern struct {
1339 map_fd: fd_t,
1340 key: u64,
1341 result: extern union {
1342 value: u64,
1343 next_key: u64,
1344 },
1345 flags: u64,
1346};
1347
1348/// struct used by Cmd.map_*_batch commands
1349pub const MapBatchAttr = extern struct {
1350 /// start batch, NULL to start from beginning
1351 in_batch: u64,
1352
1353 /// output: next start batch
1354 out_batch: u64,
1355 keys: u64,
1356 values: u64,
1357
1358 /// input/output:
1359 /// input: # of key/value elements
1360 /// output: # of filled elements
1361 count: u32,
1362 map_fd: fd_t,
1363 elem_flags: u64,
1364 flags: u64,
1365};
1366
1367/// struct used by Cmd.prog_load command
1368pub const ProgLoadAttr = extern struct {
1369 /// one of ProgType
1370 prog_type: u32,
1371 insn_cnt: u32,
1372 insns: u64,
1373 license: u64,
1374
1375 /// verbosity level of verifier
1376 log_level: u32,
1377
1378 /// size of user buffer
1379 log_size: u32,
1380
1381 /// user supplied buffer
1382 log_buf: u64,
1383
1384 /// not used
1385 kern_version: u32,
1386 prog_flags: u32,
1387 prog_name: [obj_name_len]u8,
1388
1389 /// ifindex of netdev to prep for.
1390 prog_ifindex: u32,
1391
1392 /// For some prog types expected attach type must be known at load time to
1393 /// verify attach type specific parts of prog (context accesses, allowed
1394 /// helpers, etc).
1395 expected_attach_type: u32,
1396
1397 /// fd pointing to BTF type data
1398 prog_btf_fd: fd_t,
1399
1400 /// userspace bpf_func_info size
1401 func_info_rec_size: u32,
1402 func_info: u64,
1403
1404 /// number of bpf_func_info records
1405 func_info_cnt: u32,
1406
1407 /// userspace bpf_line_info size
1408 line_info_rec_size: u32,
1409 line_info: u64,
1410
1411 /// number of bpf_line_info records
1412 line_info_cnt: u32,
1413
1414 /// in-kernel BTF type id to attach to
1415 attact_btf_id: u32,
1416
1417 /// 0 to attach to vmlinux
1418 attach_prog_id: u32,
1419};
1420
1421/// struct used by Cmd.obj_* commands
1422pub const ObjAttr = extern struct {
1423 pathname: u64,
1424 bpf_fd: fd_t,
1425 file_flags: u32,
1426};
1427
1428/// struct used by Cmd.prog_attach/detach commands
1429pub const ProgAttachAttr = extern struct {
1430 /// container object to attach to
1431 target_fd: fd_t,
1432
1433 /// eBPF program to attach
1434 attach_bpf_fd: fd_t,
1435
1436 attach_type: u32,
1437 attach_flags: u32,
1438
1439 // TODO: BPF_F_REPLACE flags
1440 /// previously attached eBPF program to replace if .replace is used
1441 replace_bpf_fd: fd_t,
1442};
1443
1444/// struct used by Cmd.prog_test_run command
1445pub const TestRunAttr = extern struct {
1446 prog_fd: fd_t,
1447 retval: u32,
1448
1449 /// input: len of data_in
1450 data_size_in: u32,
1451
1452 /// input/output: len of data_out. returns ENOSPC if data_out is too small.
1453 data_size_out: u32,
1454 data_in: u64,
1455 data_out: u64,
1456 repeat: u32,
1457 duration: u32,
1458
1459 /// input: len of ctx_in
1460 ctx_size_in: u32,
1461
1462 /// input/output: len of ctx_out. returns ENOSPC if ctx_out is too small.
1463 ctx_size_out: u32,
1464 ctx_in: u64,
1465 ctx_out: u64,
1466};
1467
1468/// struct used by Cmd.*_get_*_id commands
1469pub const GetIdAttr = extern struct {
1470 id: extern union {
1471 start_id: u32,
1472 prog_id: u32,
1473 map_id: u32,
1474 btf_id: u32,
1475 link_id: u32,
1476 },
1477 next_id: u32,
1478 open_flags: u32,
1479};
1480
1481/// struct used by Cmd.obj_get_info_by_fd command
1482pub const InfoAttr = extern struct {
1483 bpf_fd: fd_t,
1484 info_len: u32,
1485 info: u64,
1486};
1487
1488/// struct used by Cmd.prog_query command
1489pub const QueryAttr = extern struct {
1490 /// container object to query
1491 target_fd: fd_t,
1492 attach_type: u32,
1493 query_flags: u32,
1494 attach_flags: u32,
1495 prog_ids: u64,
1496 prog_cnt: u32,
1497};
1498
1499/// struct used by Cmd.raw_tracepoint_open command
1500pub const RawTracepointAttr = extern struct {
1501 name: u64,
1502 prog_fd: fd_t,
1503};
1504
1505/// struct used by Cmd.btf_load command
1506pub const BtfLoadAttr = extern struct {
1507 btf: u64,
1508 btf_log_buf: u64,
1509 btf_size: u32,
1510 btf_log_size: u32,
1511 btf_log_level: u32,
1512};
1513
1514/// struct used by Cmd.task_fd_query
1515pub const TaskFdQueryAttr = extern struct {
1516 /// input: pid
1517 pid: pid_t,
1518
1519 /// input: fd
1520 fd: fd_t,
1521
1522 /// input: flags
1523 flags: u32,
1524
1525 /// input/output: buf len
1526 buf_len: u32,
1527
1528 /// input/output:
1529 /// tp_name for tracepoint
1530 /// symbol for kprobe
1531 /// filename for uprobe
1532 buf: u64,
1533
1534 /// output: prod_id
1535 prog_id: u32,
1536
1537 /// output: BPF_FD_TYPE
1538 fd_type: u32,
1539
1540 /// output: probe_offset
1541 probe_offset: u64,
1542
1543 /// output: probe_addr
1544 probe_addr: u64,
1545};
1546
1547/// struct used by Cmd.link_create command
1548pub const LinkCreateAttr = extern struct {
1549 /// eBPF program to attach
1550 prog_fd: fd_t,
1551
1552 /// object to attach to
1553 target_fd: fd_t,
1554 attach_type: u32,
1555
1556 /// extra flags
1557 flags: u32,
1558};
1559
1560/// struct used by Cmd.link_update command
1561pub const LinkUpdateAttr = extern struct {
1562 link_fd: fd_t,
1563
1564 /// new program to update link with
1565 new_prog_fd: fd_t,
1566
1567 /// extra flags
1568 flags: u32,
1569
1570 /// expected link's program fd, it is specified only if BPF_F_REPLACE is
1571 /// set in flags
1572 old_prog_fd: fd_t,
1573};
1574
1575/// struct used by Cmd.enable_stats command
1576pub const EnableStatsAttr = extern struct {
1577 type: u32,
1578};
1579
1580/// struct used by Cmd.iter_create command
1581pub const IterCreateAttr = extern struct {
1582 link_fd: fd_t,
1583 flags: u32,
1584};
1585
1586/// Mega struct that is passed to the bpf() syscall
1587pub const Attr = extern union {
1588 map_create: MapCreateAttr,
1589 map_elem: MapElemAttr,
1590 map_batch: MapBatchAttr,
1591 prog_load: ProgLoadAttr,
1592 obj: ObjAttr,
1593 prog_attach: ProgAttachAttr,
1594 test_run: TestRunAttr,
1595 get_id: GetIdAttr,
1596 info: InfoAttr,
1597 query: QueryAttr,
1598 raw_tracepoint: RawTracepointAttr,
1599 btf_load: BtfLoadAttr,
1600 task_fd_query: TaskFdQueryAttr,
1601 link_create: LinkCreateAttr,
1602 link_update: LinkUpdateAttr,
1603 enable_stats: EnableStatsAttr,
1604 iter_create: IterCreateAttr,
1605};
1606
1607pub const Log = struct {
1608 level: u32,
1609 buf: []u8,
1610};
1611
1612pub fn map_create(map_type: MapType, key_size: u32, value_size: u32, max_entries: u32) !fd_t {
1613 var attr = Attr{
1614 .map_create = std.mem.zeroes(MapCreateAttr),
1615 };
1616
1617 attr.map_create.map_type = @backingInt(map_type);
1618 attr.map_create.key_size = key_size;
1619 attr.map_create.value_size = value_size;
1620 attr.map_create.max_entries = max_entries;
1621
1622 const rc = linux.bpf(.map_create, &attr, @sizeOf(MapCreateAttr));
1623 switch (errno(rc)) {
1624 .SUCCESS => return @as(fd_t, @intCast(rc)),
1625 .INVAL => return error.MapTypeOrAttrInvalid,
1626 .NOMEM => return error.SystemResources,
1627 .PERM => return error.PermissionDenied,
1628 else => |err| return unexpectedErrno(err),
1629 }
1630}
1631
1632test "map_create" {
1633 const map = try map_create(.hash, 4, 4, 32);
1634 defer _ = std.os.linux.close(map);
1635}
1636
1637pub fn map_lookup_elem(fd: fd_t, key: []const u8, value: []u8) !void {
1638 var attr = Attr{
1639 .map_elem = std.mem.zeroes(MapElemAttr),
1640 };
1641
1642 attr.map_elem.map_fd = fd;
1643 attr.map_elem.key = @intFromPtr(key.ptr);
1644 attr.map_elem.result.value = @intFromPtr(value.ptr);
1645
1646 const rc = linux.bpf(.map_lookup_elem, &attr, @sizeOf(MapElemAttr));
1647 switch (errno(rc)) {
1648 .SUCCESS => return,
1649 .BADF => return error.BadFd,
1650 .FAULT => unreachable,
1651 .INVAL => return error.FieldInAttrNeedsZeroing,
1652 .NOENT => return error.NotFound,
1653 .PERM => return error.PermissionDenied,
1654 else => |err| return unexpectedErrno(err),
1655 }
1656}
1657
1658pub fn map_update_elem(fd: fd_t, key: []const u8, value: []const u8, flags: u64) !void {
1659 var attr = Attr{
1660 .map_elem = std.mem.zeroes(MapElemAttr),
1661 };
1662
1663 attr.map_elem.map_fd = fd;
1664 attr.map_elem.key = @intFromPtr(key.ptr);
1665 attr.map_elem.result = .{ .value = @intFromPtr(value.ptr) };
1666 attr.map_elem.flags = flags;
1667
1668 const rc = linux.bpf(.map_update_elem, &attr, @sizeOf(MapElemAttr));
1669 switch (errno(rc)) {
1670 .SUCCESS => return,
1671 .@"2BIG" => return error.ReachedMaxEntries,
1672 .BADF => return error.BadFd,
1673 .FAULT => unreachable,
1674 .INVAL => return error.FieldInAttrNeedsZeroing,
1675 .NOMEM => return error.SystemResources,
1676 .PERM => return error.PermissionDenied,
1677 else => |err| return unexpectedErrno(err),
1678 }
1679}
1680
1681pub fn map_delete_elem(fd: fd_t, key: []const u8) !void {
1682 var attr = Attr{
1683 .map_elem = std.mem.zeroes(MapElemAttr),
1684 };
1685
1686 attr.map_elem.map_fd = fd;
1687 attr.map_elem.key = @intFromPtr(key.ptr);
1688
1689 const rc = linux.bpf(.map_delete_elem, &attr, @sizeOf(MapElemAttr));
1690 switch (errno(rc)) {
1691 .SUCCESS => return,
1692 .BADF => return error.BadFd,
1693 .FAULT => unreachable,
1694 .INVAL => return error.FieldInAttrNeedsZeroing,
1695 .NOENT => return error.NotFound,
1696 .PERM => return error.PermissionDenied,
1697 else => |err| return unexpectedErrno(err),
1698 }
1699}
1700
1701pub fn map_get_next_key(fd: fd_t, key: []const u8, next_key: []u8) !bool {
1702 var attr = Attr{
1703 .map_elem = std.mem.zeroes(MapElemAttr),
1704 };
1705
1706 attr.map_elem.map_fd = fd;
1707 attr.map_elem.key = @intFromPtr(key.ptr);
1708 attr.map_elem.result.next_key = @intFromPtr(next_key.ptr);
1709
1710 const rc = linux.bpf(.map_get_next_key, &attr, @sizeOf(MapElemAttr));
1711 switch (errno(rc)) {
1712 .SUCCESS => return true,
1713 .BADF => return error.BadFd,
1714 .FAULT => unreachable,
1715 .INVAL => return error.FieldInAttrNeedsZeroing,
1716 .NOENT => return false,
1717 .PERM => return error.PermissionDenied,
1718 else => |err| return unexpectedErrno(err),
1719 }
1720}
1721
1722test "map lookup, update, and delete" {
1723 const key_size = 4;
1724 const value_size = 4;
1725 const map = try map_create(.hash, key_size, value_size, 1);
1726 defer _ = std.os.linux.close(map);
1727
1728 const key = std.mem.zeroes([key_size]u8);
1729 var value = std.mem.zeroes([value_size]u8);
1730
1731 // fails looking up value that doesn't exist
1732 try expectError(error.NotFound, map_lookup_elem(map, &key, &value));
1733
1734 // succeed at updating and looking up element
1735 try map_update_elem(map, &key, &value, 0);
1736 try map_lookup_elem(map, &key, &value);
1737
1738 // fails inserting more than max entries
1739 const second_key = [key_size]u8{ 0, 0, 0, 1 };
1740 try expectError(error.ReachedMaxEntries, map_update_elem(map, &second_key, &value, 0));
1741
1742 // succeed at iterating all keys of map
1743 var lookup_key = [_]u8{ 1, 0, 0, 0 };
1744 var next_key = [_]u8{ 2, 3, 4, 5 }; // garbage value
1745 const status = try map_get_next_key(map, &lookup_key, &next_key);
1746 try expectEqual(status, true);
1747 try expectEqual(next_key, key);
1748 lookup_key = next_key;
1749 const status2 = try map_get_next_key(map, &lookup_key, &next_key);
1750 try expectEqual(status2, false);
1751
1752 // succeed at deleting an existing elem
1753 try map_delete_elem(map, &key);
1754 try expectError(error.NotFound, map_lookup_elem(map, &key, &value));
1755
1756 // fail at deleting a non-existing elem
1757 try expectError(error.NotFound, map_delete_elem(map, &key));
1758}
1759
1760pub fn prog_load(
1761 prog_type: ProgType,
1762 insns: []const Insn,
1763 log: ?*Log,
1764 license: []const u8,
1765 kern_version: u32,
1766 flags: u32,
1767) !fd_t {
1768 var attr = Attr{
1769 .prog_load = std.mem.zeroes(ProgLoadAttr),
1770 };
1771
1772 attr.prog_load.prog_type = @backingInt(prog_type);
1773 attr.prog_load.insns = @intFromPtr(insns.ptr);
1774 attr.prog_load.insn_cnt = @as(u32, @intCast(insns.len));
1775 attr.prog_load.license = @intFromPtr(license.ptr);
1776 attr.prog_load.kern_version = kern_version;
1777 attr.prog_load.prog_flags = flags;
1778
1779 if (log) |l| {
1780 attr.prog_load.log_buf = @intFromPtr(l.buf.ptr);
1781 attr.prog_load.log_size = @as(u32, @intCast(l.buf.len));
1782 attr.prog_load.log_level = l.level;
1783 }
1784
1785 const rc = linux.bpf(.prog_load, &attr, @sizeOf(ProgLoadAttr));
1786 return switch (errno(rc)) {
1787 .SUCCESS => @as(fd_t, @intCast(rc)),
1788 .ACCES => error.UnsafeProgram,
1789 .FAULT => unreachable,
1790 .INVAL => error.InvalidProgram,
1791 .PERM => error.PermissionDenied,
1792 else => |err| unexpectedErrno(err),
1793 };
1794}
1795
1796test "prog_load" {
1797 // this should fail because it does not set r0 before exiting
1798 const bad_prog = [_]Insn{
1799 Insn.exit(),
1800 };
1801
1802 const good_prog = [_]Insn{
1803 Insn.mov(.r0, 0),
1804 Insn.exit(),
1805 };
1806
1807 const prog = try prog_load(.socket_filter, &good_prog, null, "MIT", 0, 0);
1808 defer _ = std.os.linux.close(prog);
1809
1810 try expectError(error.UnsafeProgram, prog_load(.socket_filter, &bad_prog, null, "MIT", 0, 0));
1811}