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")] 47 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")] 95 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 /// ``` 120 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)] 146 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` 191 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)] 210 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")] 259 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")] 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