xref: /DragonOS/kernel/crates/rbpf/src/helpers.rs (revision fae6e9ade46a52976ad5d099643d51cc20876448)
1 // SPDX-License-Identifier: (Apache-2.0 OR MIT)
2 // Copyright 2015 Big Switch Networks, Inc
3 //      (Algorithms for uBPF helpers, originally in C)
4 // Copyright 2016 6WIND S.A. <quentin.monnet@6wind.com>
5 //      (Translation to Rust, other helpers)
6 
7 //! This module implements some built-in helpers that can be called from within an eBPF program.
8 //!
9 //! These helpers may originate from several places:
10 //!
11 //! * Some of them mimic the helpers available in the Linux kernel.
12 //! * Some of them were proposed as example helpers in uBPF and they were adapted here.
13 //! * Other helpers may be specific to rbpf.
14 //!
15 //! The prototype for helpers is always the same: five `u64` as arguments, and a `u64` as a return
16 //! value. Hence some helpers have unused arguments, or return a 0 value in all cases, in order to
17 //! respect this convention.
18 
19 // Helpers associated to kernel helpers
20 // See also linux/include/uapi/linux/bpf.h in Linux kernel sources.
21 
22 // bpf_ktime_getns()
23 
24 /// Index of helper `bpf_ktime_getns()`, equivalent to `bpf_time_getns()`, in Linux kernel, see
25 /// <https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/include/uapi/linux/bpf.h>.
26 pub const BPF_KTIME_GETNS_IDX: u32 = 5;
27 
28 /// Get monotonic time (since boot time) in nanoseconds. All arguments are unused.
29 ///
30 /// # Examples
31 ///
32 /// ```
33 /// use rbpf::helpers;
34 ///
35 /// let t = helpers::bpf_time_getns(0, 0, 0, 0, 0);
36 /// let d =  t / 10u64.pow(9)  / 60   / 60  / 24;
37 /// let h = (t / 10u64.pow(9)  / 60   / 60) % 24;
38 /// let m = (t / 10u64.pow(9)  / 60 ) % 60;
39 /// let s = (t / 10u64.pow(9)) % 60;
40 /// let ns = t % 10u64.pow(9);
41 /// println!("Uptime: {:#x} == {} days {}:{}:{}, {} ns", t, d, h, m, s, ns);
42 /// ```
43 #[allow(dead_code)]
44 #[allow(unused_variables)]
45 #[allow(deprecated)]
46 #[cfg(feature = "std")]
bpf_time_getns(unused1: u64, unused2: u64, unused3: u64, unused4: u64, unused5: u64) -> u6447 pub fn bpf_time_getns(unused1: u64, unused2: u64, unused3: u64, unused4: u64, unused5: u64) -> u64 {
48     time::precise_time_ns()
49 }
50 
51 // bpf_trace_printk()
52 
53 /// Index of helper `bpf_trace_printk()`, equivalent to `bpf_trace_printf()`, in Linux kernel, see
54 /// <https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/include/uapi/linux/bpf.h>.
55 pub const BPF_TRACE_PRINTK_IDX: u32 = 6;
56 
57 /// Prints its **last three** arguments to standard output. The **first two** arguments are
58 /// **unused**. Returns the number of bytes written.
59 ///
60 /// By ignoring the first two arguments, it creates a helper that will have a behavior similar to
61 /// the one of the equivalent helper `bpf_trace_printk()` from Linux kernel.
62 ///
63 /// # Examples
64 ///
65 /// ```
66 /// use rbpf::helpers;
67 ///
68 /// let res = helpers::bpf_trace_printf(0, 0, 1, 15, 32);
69 /// assert_eq!(res as usize, "bpf_trace_printf: 0x1, 0xf, 0x20\n".len());
70 /// ```
71 ///
72 /// This will print `bpf_trace_printf: 0x1, 0xf, 0x20`.
73 ///
74 /// The eBPF code needed to perform the call in this example would be nearly identical to the code
75 /// obtained by compiling the following code from C to eBPF with clang:
76 ///
77 /// ```c
78 /// #include <linux/bpf.h>
79 /// #include "path/to/linux/samples/bpf/bpf_helpers.h"
80 ///
81 /// int main(struct __sk_buff *skb)
82 /// {
83 ///     // Only %d %u %x %ld %lu %lx %lld %llu %llx %p %s conversion specifiers allowed.
84 ///     // See <https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/kernel/trace/bpf_trace.c>.
85 ///     char *fmt = "bpf_trace_printk %llx, %llx, %llx\n";
86 ///     return bpf_trace_printk(fmt, sizeof(fmt), 1, 15, 32);
87 /// }
88 /// ```
89 ///
90 /// This would equally print the three numbers in `/sys/kernel/debug/tracing` file each time the
91 /// program is run.
92 #[allow(dead_code)]
93 #[allow(unused_variables)]
94 #[cfg(feature = "std")]
bpf_trace_printf(unused1: u64, unused2: u64, arg3: u64, arg4: u64, arg5: u64) -> u6495 pub fn bpf_trace_printf(unused1: u64, unused2: u64, arg3: u64, arg4: u64, arg5: u64) -> u64 {
96     println!("bpf_trace_printf: {arg3:#x}, {arg4:#x}, {arg5:#x}");
97     let size_arg = |x| {
98         if x == 0 {
99             1
100         } else {
101             (x as f64).log(16.0).floor() as u64 + 1
102         }
103     };
104     "bpf_trace_printf: 0x, 0x, 0x\n".len() as u64 + size_arg(arg3) + size_arg(arg4) + size_arg(arg5)
105 }
106 
107 // Helpers coming from uBPF <https://github.com/iovisor/ubpf/blob/master/vm/test.c>
108 
109 /// The idea is to assemble five bytes into a single `u64`. For compatibility with the helpers API,
110 /// each argument must be a `u64`.
111 ///
112 /// # Examples
113 ///
114 /// ```
115 /// use rbpf::helpers;
116 ///
117 /// let gathered = helpers::gather_bytes(0x11, 0x22, 0x33, 0x44, 0x55);
118 /// assert_eq!(gathered, 0x1122334455);
119 /// ```
gather_bytes(arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64) -> u64120 pub fn gather_bytes(arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64) -> u64 {
121     arg1.wrapping_shl(32)
122         | arg2.wrapping_shl(24)
123         | arg3.wrapping_shl(16)
124         | arg4.wrapping_shl(8)
125         | arg5
126 }
127 
128 /// Same as `void *memfrob(void *s, size_t n);` in `string.h` in C. See the GNU manual page (in
129 /// section 3) for `memfrob`. The memory is directly modified, and the helper returns 0 in all
130 /// cases. Arguments 3 to 5 are unused.
131 ///
132 /// # Examples
133 ///
134 /// ```
135 /// use rbpf::helpers;
136 ///
137 /// let val: u64 = 0x112233;
138 /// let val_ptr = &val as *const u64;
139 ///
140 /// helpers::memfrob(val_ptr as u64, 8, 0, 0, 0);
141 /// assert_eq!(val, 0x2a2a2a2a2a3b0819);
142 /// helpers::memfrob(val_ptr as u64, 8, 0, 0, 0);
143 /// assert_eq!(val, 0x112233);
144 /// ```
145 #[allow(unused_variables)]
memfrob(ptr: u64, len: u64, unused3: u64, unused4: u64, unused5: u64) -> u64146 pub fn memfrob(ptr: u64, len: u64, unused3: u64, unused4: u64, unused5: u64) -> u64 {
147     for i in 0..len {
148         unsafe {
149             let mut p = (ptr + i) as *mut u8;
150             *p ^= 0b101010;
151         }
152     }
153     0
154 }
155 
156 // TODO: Try again when asm!() is available in stable Rust.
157 // #![feature(asm)]
158 // #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
159 // #[allow(unused_variables)]
160 // pub fn memfrob (ptr: u64, len: u64, arg3: u64, arg4: u64, arg5: u64) -> u64 {
161 //     unsafe {
162 //         asm!(
163 //                 "mov $0xf0, %rax"
164 //             ::: "mov $0xf1, %rcx"
165 //             ::: "mov $0xf2, %rdx"
166 //             ::: "mov $0xf3, %rsi"
167 //             ::: "mov $0xf4, %rdi"
168 //             ::: "mov $0xf5, %r8"
169 //             ::: "mov $0xf6, %r9"
170 //             ::: "mov $0xf7, %r10"
171 //             ::: "mov $0xf8, %r11"
172 //         );
173 //     }
174 //     0
175 // }
176 
177 /// Compute and return the square root of argument 1, cast as a float. Arguments 2 to 5 are
178 /// unused.
179 ///
180 /// # Examples
181 ///
182 /// ```
183 /// use rbpf::helpers;
184 ///
185 /// let x = helpers::sqrti(9, 0, 0, 0, 0);
186 /// assert_eq!(x, 3);
187 /// ```
188 #[allow(dead_code)]
189 #[allow(unused_variables)]
190 #[cfg(feature = "std")] // sqrt is only available when using `std`
sqrti(arg1: u64, unused2: u64, unused3: u64, unused4: u64, unused5: u64) -> u64191 pub fn sqrti(arg1: u64, unused2: u64, unused3: u64, unused4: u64, unused5: u64) -> u64 {
192     (arg1 as f64).sqrt() as u64
193 }
194 
195 /// C-like `strcmp`, return 0 if the strings are equal, and a non-null value otherwise.
196 ///
197 /// # Examples
198 ///
199 /// ```
200 /// use rbpf::helpers;
201 ///
202 /// let foo = "This is a string.\0".as_ptr() as u64;
203 /// let bar = "This is another sting.\0".as_ptr() as u64;
204 ///
205 /// assert!(helpers::strcmp(foo, foo, 0, 0, 0) == 0);
206 /// assert!(helpers::strcmp(foo, bar, 0, 0, 0) != 0);
207 /// ```
208 #[allow(dead_code)]
209 #[allow(unused_variables)]
strcmp(arg1: u64, arg2: u64, arg3: u64, unused4: u64, unused5: u64) -> u64210 pub fn strcmp(arg1: u64, arg2: u64, arg3: u64, unused4: u64, unused5: u64) -> u64 {
211     // C-like strcmp, maybe shorter than converting the bytes to string and comparing?
212     if arg1 == 0 || arg2 == 0 {
213         return u64::MAX;
214     }
215     let mut a = arg1;
216     let mut b = arg2;
217     unsafe {
218         let mut a_val = *(a as *const u8);
219         let mut b_val = *(b as *const u8);
220         while a_val == b_val && a_val != 0 && b_val != 0 {
221             a += 1;
222             b += 1;
223             a_val = *(a as *const u8);
224             b_val = *(b as *const u8);
225         }
226         if a_val >= b_val {
227             (a_val - b_val) as u64
228         } else {
229             (b_val - a_val) as u64
230         }
231     }
232 }
233 
234 // Some additional helpers
235 
236 /// Returns a random u64 value comprised between `min` and `max` values (inclusive). Arguments 3 to
237 /// 5 are unused.
238 ///
239 /// Relies on `rand()` function from libc, so `libc::srand()` should be called once before this
240 /// helper is used.
241 ///
242 /// # Examples
243 ///
244 /// ```
245 /// extern crate libc;
246 /// extern crate rbpf;
247 /// extern crate time;
248 ///
249 /// unsafe {
250 ///     libc::srand(time::precise_time_ns() as u32)
251 /// }
252 ///
253 /// let n = rbpf::helpers::rand(3, 6, 0, 0, 0);
254 /// assert!(3 <= n && n <= 6);
255 /// ```
256 #[allow(dead_code)]
257 #[allow(unused_variables)]
258 #[cfg(feature = "std")]
rand(min: u64, max: u64, unused3: u64, unused4: u64, unused5: u64) -> u64259 pub fn rand(min: u64, max: u64, unused3: u64, unused4: u64, unused5: u64) -> u64 {
260     let mut n = unsafe { (libc::rand() as u64).wrapping_shl(32) + libc::rand() as u64 };
261     if min < max {
262         n = n % (max + 1 - min) + min;
263     };
264     n
265 }
266 /// Prints the helper functions name and it's index.
267 #[cfg(feature = "std")]
show_helper()268 pub fn show_helper() {
269     for (index, name) in BPF_FUNC_MAPPER.iter().enumerate() {
270         println!("{}:{}", index, name);
271     }
272 }
273 
274 /// See https://github.com/torvalds/linux/blob/master/include/uapi/linux/bpf.h
275 pub const BPF_FUNC_MAPPER: &[&str] = &[
276     "unspec",
277     "map_lookup_elem",
278     "map_update_elem",
279     "map_delete_elem",
280     "probe_read",
281     "ktime_get_ns",
282     "trace_printk",
283     "get_prandom_u32",
284     "get_smp_processor_id",
285     "skb_store_bytes",
286     "l3_csum_replace",
287     "l4_csum_replace",
288     "tail_call",
289     "clone_redirect",
290     "get_current_pid_tgid",
291     "get_current_uid_gid",
292     "get_current_comm",
293     "get_cgroup_classid",
294     "skb_vlan_push",
295     "skb_vlan_pop",
296     "skb_get_tunnel_key",
297     "skb_set_tunnel_key",
298     "perf_event_read",
299     "redirect",
300     "get_route_realm",
301     "perf_event_output",
302     "skb_load_bytes",
303     "get_stackid",
304     "csum_diff",
305     "skb_get_tunnel_opt",
306     "skb_set_tunnel_opt",
307     "skb_change_proto",
308     "skb_change_type",
309     "skb_under_cgroup",
310     "get_hash_recalc",
311     "get_current_task",
312     "probe_write_user",
313     "current_task_under_cgroup",
314     "skb_change_tail",
315     "skb_pull_data",
316     "csum_update",
317     "set_hash_invalid",
318     "get_numa_node_id",
319     "skb_change_head",
320     "xdp_adjust_head",
321     "probe_read_str",
322     "get_socket_cookie",
323     "get_socket_uid",
324     "set_hash",
325     "setsockopt",
326     "skb_adjust_room",
327     "redirect_map",
328     "sk_redirect_map",
329     "sock_map_update",
330     "xdp_adjust_meta",
331     "perf_event_read_value",
332     "perf_prog_read_value",
333     "getsockopt",
334     "override_return",
335     "sock_ops_cb_flags_set",
336     "msg_redirect_map",
337     "msg_apply_bytes",
338     "msg_cork_bytes",
339     "msg_pull_data",
340     "bind",
341     "xdp_adjust_tail",
342     "skb_get_xfrm_state",
343     "get_stack",
344     "skb_load_bytes_relative",
345     "fib_lookup",
346     "sock_hash_update",
347     "msg_redirect_hash",
348     "sk_redirect_hash",
349     "lwt_push_encap",
350     "lwt_seg6_store_bytes",
351     "lwt_seg6_adjust_srh",
352     "lwt_seg6_action",
353     "rc_repeat",
354     "rc_keydown",
355     "skb_cgroup_id",
356     "get_current_cgroup_id",
357     "get_local_storage",
358     "sk_select_reuseport",
359     "skb_ancestor_cgroup_id",
360     "sk_lookup_tcp",
361     "sk_lookup_udp",
362     "sk_release",
363     "map_push_elem",
364     "map_pop_elem",
365     "map_peek_elem",
366     "msg_push_data",
367     "msg_pop_data",
368     "rc_pointer_rel",
369     "spin_lock",
370     "spin_unlock",
371     "sk_fullsock",
372     "tcp_sock",
373     "skb_ecn_set_ce",
374     "get_listener_sock",
375     "skc_lookup_tcp",
376     "tcp_check_syncookie",
377     "sysctl_get_name",
378     "sysctl_get_current_value",
379     "sysctl_get_new_value",
380     "sysctl_set_new_value",
381     "strtol",
382     "strtoul",
383     "sk_storage_get",
384     "sk_storage_delete",
385     "send_signal",
386     "tcp_gen_syncookie",
387     "skb_output",
388     "probe_read_user",
389     "probe_read_kernel",
390     "probe_read_user_str",
391     "probe_read_kernel_str",
392     "tcp_send_ack",
393     "send_signal_thread",
394     "jiffies64",
395     "read_branch_records",
396     "get_ns_current_pid_tgid",
397     "xdp_output",
398     "get_netns_cookie",
399     "get_current_ancestor_cgroup_id",
400     "sk_assign",
401     "ktime_get_boot_ns",
402     "seq_printf",
403     "seq_write",
404     "sk_cgroup_id",
405     "sk_ancestor_cgroup_id",
406     "ringbuf_output",
407     "ringbuf_reserve",
408     "ringbuf_submit",
409     "ringbuf_discard",
410     "ringbuf_query",
411     "csum_level",
412     "skc_to_tcp6_sock",
413     "skc_to_tcp_sock",
414     "skc_to_tcp_timewait_sock",
415     "skc_to_tcp_request_sock",
416     "skc_to_udp6_sock",
417     "get_task_stack",
418     "load_hdr_opt",
419     "store_hdr_opt",
420     "reserve_hdr_opt",
421     "inode_storage_get",
422     "inode_storage_delete",
423     "d_path",
424     "copy_from_user",
425     "snprintf_btf",
426     "seq_printf_btf",
427     "skb_cgroup_classid",
428     "redirect_neigh",
429     "per_cpu_ptr",
430     "this_cpu_ptr",
431     "redirect_peer",
432     "task_storage_get",
433     "task_storage_delete",
434     "get_current_task_btf",
435     "bprm_opts_set",
436     "ktime_get_coarse_ns",
437     "ima_inode_hash",
438     "sock_from_file",
439     "check_mtu",
440     "for_each_map_elem",
441     "snprintf",
442     "sys_bpf",
443     "btf_find_by_name_kind",
444     "sys_close",
445     "timer_init",
446     "timer_set_callback",
447     "timer_start",
448     "timer_cancel",
449     "get_func_ip",
450     "get_attach_cookie",
451     "task_pt_regs",
452     "get_branch_snapshot",
453     "trace_vprintk",
454     "skc_to_unix_sock",
455     "kallsyms_lookup_name",
456     "find_vma",
457     "loop",
458     "strncmp",
459     "get_func_arg",
460     "get_func_ret",
461     "get_func_arg_cnt",
462     "get_retval",
463     "set_retval",
464     "xdp_get_buff_len",
465     "xdp_load_bytes",
466     "xdp_store_bytes",
467     "copy_from_user_task",
468     "skb_set_tstamp",
469     "ima_file_hash",
470     "kptr_xchg",
471     "map_lookup_percpu_elem",
472     "skc_to_mptcp_sock",
473     "dynptr_from_mem",
474     "ringbuf_reserve_dynptr",
475     "ringbuf_submit_dynptr",
476     "ringbuf_discard_dynptr",
477     "dynptr_read",
478     "dynptr_write",
479     "dynptr_data",
480     "tcp_raw_gen_syncookie_ipv4",
481     "tcp_raw_gen_syncookie_ipv6",
482     "tcp_raw_check_syncookie_ipv4",
483     "tcp_raw_check_syncookie_ipv6",
484     "ktime_get_tai_ns",
485     "user_ringbuf_drain",
486     "cgrp_storage_get",
487     "cgrp_storage_delete",
488 ];
489