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