def test_simple(self): b = bcc.BPF(text=""" #include <uapi/linux/ptrace.h> struct bpf_map; BPF_STACK_TRACE_BUILDID(stack_traces, 10240); BPF_HASH(stack_entries, int, int); BPF_HASH(stub); int kprobe__sys_getuid(struct pt_regs *ctx, struct bpf_map *map, u64 *k) { int id = stack_traces.get_stackid(ctx, BPF_F_USER_STACK); if (id < 0) return 0; int key = 1; stack_entries.update(&key, &id); return 0; } """) os.getuid() stub = b["stub"] stack_traces = b["stack_traces"] stack_entries = b["stack_entries"] b.add_module(Get_libc_path()) try: x = stub[stub.Key(1)] except: pass k = stack_entries.Key(1) self.assertIn(k, stack_entries) stackid = stack_entries[k] self.assertIsNotNone(stackid) stack = stack_traces[stackid] self.assertTrue(b.sym(stack.trace[0], -1).find(b"getuid") != -1)
def start_server(ctx): # Load xdp program to fix redirection in veth. if ctx.local.xdp_mode: prog = bcc.BPF( text=b"""int dummy(struct xdp_md *ctx) { return XDP_PASS; }""") func = prog.load_func(b"dummy", bcc.BPF.XDP) prog.attach_xdp(ctx.local.iface.encode(), func, ctx.local.xdp_mode) atexit.register(prog.remove_xdp, ctx.local.iface.encode()) listener = multiprocessing.connection.Listener( (ctx.comm.inet, ctx.comm.port)) print(f"Server started: {ctx}.") while True: conn = None try: conn = listener.accept() data = conn.recv() if data[0] == utils.ServerCommand.SEND: send_packets(ctx.local.iface, data[1], conn) elif data[0] == utils.ServerCommand.WATCH: watch_traffic(ctx.local.iface, conn) elif data[0] == utils.ServerCommand.INTRODUCE: introduce_self(ctx.local, conn) finally: if conn: conn.close()
def main(): parser = argparse.ArgumentParser( description='trace mount() and umount() syscalls') parser.add_argument("--ebpf", action="store_true", help=argparse.SUPPRESS) args = parser.parse_args() mounts = {} umounts = {} if args.ebpf: print(bpf_text) exit() b = bcc.BPF(text=bpf_text) mount_fnname = b.get_syscall_fnname("mount") b.attach_kprobe(event=mount_fnname, fn_name="syscall__mount") b.attach_kretprobe(event=mount_fnname, fn_name="do_ret_sys_mount") umount_fnname = b.get_syscall_fnname("umount") b.attach_kprobe(event=umount_fnname, fn_name="syscall__umount") b.attach_kretprobe(event=umount_fnname, fn_name="do_ret_sys_umount") b['events'].open_perf_buffer( functools.partial(print_event, mounts, umounts)) print('{:16} {:<7} {:<7} {:<11} {}'.format('COMM', 'PID', 'TID', 'MNT_NS', 'CALL')) while True: try: b.perf_buffer_poll() except KeyboardInterrupt: exit()
def test_simple_library(self): text = """ #include <uapi/linux/ptrace.h> BPF_ARRAY(stats, u64, 1); static void incr(int idx) { u64 *ptr = stats.lookup(&idx); if (ptr) ++(*ptr); } int count(struct pt_regs *ctx) { bpf_trace_printk("count() uprobe fired"); u32 pid = bpf_get_current_pid_tgid(); if (pid == PID) incr(0); return 0; }""" test_pid = os.getpid() text = text.replace("PID", "%d" % test_pid) b = bcc.BPF(text=text) b.attach_uprobe(name="c", sym="malloc_stats", fn_name="count", pid=test_pid) b.attach_uretprobe(name="c", sym="malloc_stats", fn_name="count", pid=test_pid) libc = ctypes.CDLL("libc.so.6") libc.malloc_stats.restype = None libc.malloc_stats.argtypes = [] libc.malloc_stats() self.assertEqual(b["stats"][ctypes.c_int(0)].value, 2) b.detach_uretprobe(name="c", sym="malloc_stats", pid=test_pid) b.detach_uprobe(name="c", sym="malloc_stats", pid=test_pid)
def test_cycles(self): text = """ BPF_PERF_ARRAY(cnt1, NUM_CPUS); BPF_ARRAY(prev, u64, NUM_CPUS); BPF_HISTOGRAM(dist); int kprobe__sys_getuid(void *ctx) { u32 cpu = bpf_get_smp_processor_id(); u64 val = cnt1.perf_read(cpu); prev.update(&cpu, &val); return 0; } int kretprobe__sys_getuid(void *ctx) { u32 cpu = bpf_get_smp_processor_id(); u64 val = cnt1.perf_read(cpu); u64 *prevp = prev.lookup(&cpu); if (prevp) dist.increment(bpf_log2l(val - *prevp)); return 0; } """ b = bcc.BPF(text=text, debug=0, cflags=["-DNUM_CPUS=%d" % multiprocessing.cpu_count()]) cnt1 = b["cnt1"] try: cnt1.open_perf_event(cnt1.HW_CPU_CYCLES) except: if ctypes.get_errno() == 2: raise self.skipTest("hardware events unsupported") raise for i in range(0, 100): os.getuid() b["dist"].print_log2_hist()
def main(opts): debug_flags = 0 if opts.debug_source: debug_flags = bcc.DEBUG_SOURCE if opts.debug_bpf: debug_flags = bcc.DEBUG_BPF b = bcc.BPF(text=BPF_TEXT, debug=debug_flags) if opts.debug_source or opts.debug_bpf: raise SystemExit(0) print("starting child process:", opts.command) comm = subprocess.Popen(opts.command) print("forked child as:", comm.pid) b['events'].open_perf_buffer(partial(print_event, comm.pid)) while True: try: b.perf_buffer_poll(timeout=5) if comm.poll() is not None: break except KeyboardInterrupt: exit() comm.terminate() comm.wait()
def test_simple(self): b = bcc.BPF(text=""" #include <uapi/linux/ptrace.h> struct bpf_map; BPF_STACK_TRACE(stack_traces, 10240); BPF_HASH(stack_entries, int, int); BPF_HASH(stub); int kprobe__htab_map_lookup_elem(struct pt_regs *ctx, struct bpf_map *map, u64 *k) { int id = stack_traces.get_stackid(ctx, BPF_F_REUSE_STACKID); if (id < 0) return 0; int key = 1; stack_entries.update(&key, &id); return 0; } """) stub = b["stub"] stack_traces = b["stack_traces"] stack_entries = b["stack_entries"] try: x = stub[stub.Key(1)] except: pass k = stack_entries.Key(1) self.assertIn(k, stack_entries) stackid = stack_entries[k] self.assertIsNotNone(stackid) stack = stack_traces[stackid].ip self.assertEqual(b.ksym(stack[0]), b"htab_map_lookup_elem")
def main(iface): prog = bcc.BPF(src_file="prog.c") func = prog.load_func("prog", bcc.BPF.XDP) try: prog.attach_xdp(iface, func, 2) print(f"attached to {iface}, wait 30s or ^C") time.sleep(30.0) finally: prog.remove_xdp(iface) prog.trace_print()
def setUpClass(cls): super().setUpClass() cls.prog = cls.load_bpf(b"progs/return_values.c") cls.to_send = cls.generate_default_packets() cls.exception_counter = bcc.BPF(text=b""" BPF_ARRAY(counter, int, 1); int prog(void *ctx) { counter.increment(0); return 0; } """)
def main(): parser = argparse.ArgumentParser( description='trace mount() and umount() syscalls') args = parser.parse_args() mounts = {} umounts = {} b = bcc.BPF(text=bpf_text) b['events'].open_perf_buffer( functools.partial(print_event, mounts, umounts)) print('{:16} {:<7} {:<7} {:<11} {}'.format('COMM', 'PID', 'TID', 'MNT_NS', 'CALL')) while True: b.kprobe_poll()
def test_cycles(self): text = """ BPF_PERF_ARRAY(cnt1, NUM_CPUS); BPF_ARRAY(prev, u64, NUM_CPUS); BPF_HISTOGRAM(dist); int do_sys_getuid(void *ctx) { u32 cpu = bpf_get_smp_processor_id(); u64 val = cnt1.perf_read(CUR_CPU_IDENTIFIER); if (((s64)val < 0) && ((s64)val > -256)) return 0; prev.update(&cpu, &val); return 0; } int do_ret_sys_getuid(void *ctx) { u32 cpu = bpf_get_smp_processor_id(); u64 val = cnt1.perf_read(CUR_CPU_IDENTIFIER); if (((s64)val < 0) && ((s64)val > -256)) return 0; u64 *prevp = prev.lookup(&cpu); if (prevp) { dist.increment(bpf_log2l(val - *prevp)); dist.atomic_increment(bpf_log2l(val - *prevp)); } return 0; } """ b = bcc.BPF(text=text, debug=0, cflags=["-DNUM_CPUS=%d" % multiprocessing.cpu_count()]) event_name = b.get_syscall_fnname("getuid") b.attach_kprobe(event=event_name, fn_name="do_sys_getuid") b.attach_kretprobe(event=event_name, fn_name="do_ret_sys_getuid") cnt1 = b["cnt1"] try: cnt1.open_perf_event(bcc.PerfType.HARDWARE, bcc.PerfHWConfig.CPU_CYCLES) except: if ctypes.get_errno() == 2: raise self.skipTest("hardware events unsupported") raise for i in range(0, 100): os.getuid() b["dist"].print_log2_hist()
def test_tracepoint(self): text = """ BPF_HASH(switches, u32, u64); TRACEPOINT_PROBE(sched, sched_switch) { u64 val = 0; u32 pid = args->next_pid; u64 *existing = switches.lookup_or_init(&pid, &val); (*existing)++; return 0; } """ b = bcc.BPF(text=text) sleep(1) total_switches = 0 for k, v in b["switches"].items(): total_switches += v.value self.assertNotEqual(0, total_switches)
def main(): parser = argparse.ArgumentParser( description='trace mount() and umount() syscalls') parser.add_argument("--ebpf", action="store_true", help=argparse.SUPPRESS) args = parser.parse_args() mounts = {} umounts = {} if args.ebpf: print(bpf_text) exit() b = bcc.BPF(text=bpf_text) b['events'].open_perf_buffer( functools.partial(print_event, mounts, umounts)) print('{:16} {:<7} {:<7} {:<11} {}'.format('COMM', 'PID', 'TID', 'MNT_NS', 'CALL')) while True: b.kprobe_poll()
def main(): parser = argparse.ArgumentParser( description='trace mount() and umount() syscalls') parser.add_argument("--ebpf", action="store_true", help=argparse.SUPPRESS) parser.add_argument("-P", "--parent_process", action="store_true", help="also snoop the parent process") parser.add_argument("--cgroupmap", help="trace cgroups in this BPF map only") parser.add_argument("--mntnsmap", help="trace mount namespaces in this BPF map only") args = parser.parse_args() mounts = {} umounts = {} global bpf_text bpf_text = filter_by_containers(args) + bpf_text if args.ebpf: print(bpf_text) exit() b = bcc.BPF(text=bpf_text) mount_fnname = b.get_syscall_fnname("mount") b.attach_kprobe(event=mount_fnname, fn_name="syscall__mount") b.attach_kretprobe(event=mount_fnname, fn_name="do_ret_sys_mount") umount_fnname = b.get_syscall_fnname("umount") b.attach_kprobe(event=umount_fnname, fn_name="syscall__umount") b.attach_kretprobe(event=umount_fnname, fn_name="do_ret_sys_umount") b['events'].open_perf_buffer( functools.partial(print_event, mounts, umounts, args.parent_process)) if args.parent_process: print('{:16} {:<7} {:<7} {:16} {:<7} {:<11} {}'.format( 'COMM', 'PID', 'TID', 'PCOMM', 'PPID', 'MNT_NS', 'CALL')) else: print('{:16} {:<7} {:<7} {:<11} {}'.format('COMM', 'PID', 'TID', 'MNT_NS', 'CALL')) while True: try: b.perf_buffer_poll() except KeyboardInterrupt: exit()
def test_tracepoint_data_loc(self): text = """ struct value_t { char filename[64]; }; BPF_HASH(execs, u32, struct value_t); TRACEPOINT_PROBE(sched, sched_process_exec) { struct value_t val = {0}; char fn[64]; u32 pid = args->pid; struct value_t *existing = execs.lookup_or_init(&pid, &val); TP_DATA_LOC_READ_CONST(fn, filename, 64); __builtin_memcpy(existing->filename, fn, 64); return 0; } """ b = bcc.BPF(text=text) subprocess.check_output(["/bin/ls"]) sleep(1) self.assertTrue( "/bin/ls" in [v.filename.decode() for v in b["execs"].values()])
def __init__(self, args, debug=0): mounts = {} umounts = {} self.arguments = args if args.ebpf: print(self.bpf_text) exit() b = bcc.BPF(text=self.bpf_text) mount_fnname = b.get_syscall_fnname("mount") b.attach_kprobe(event=mount_fnname, fn_name="syscall__mount") b.attach_kretprobe(event=mount_fnname, fn_name="do_ret_sys_mount") umount_fnname = b.get_syscall_fnname("umount") b.attach_kprobe(event=umount_fnname, fn_name="syscall__umount") b.attach_kretprobe(event=umount_fnname, fn_name="do_ret_sys_umount") b['events'].open_perf_buffer( functools.partial(self.print_event, mounts, umounts)) if not args.json: print('{:16} {:<7} {:<7} {:<11} {}'.format( 'COMM', 'PID', 'TID', 'MNT_NS', 'CALL')) while True: b.perf_buffer_poll()
def test_simple_binary(self): text = """ #include <uapi/linux/ptrace.h> BPF_ARRAY(stats, u64, 1); static void incr(int idx) { u64 *ptr = stats.lookup(&idx); if (ptr) ++(*ptr); } int count(struct pt_regs *ctx) { u32 pid = bpf_get_current_pid_tgid(); incr(0); return 0; }""" b = bcc.BPF(text=text) b.attach_uprobe(name="/usr/bin/python", sym="main", fn_name="count") b.attach_uretprobe(name="/usr/bin/python", sym="main", fn_name="count") with os.popen("/usr/bin/python -V") as f: pass self.assertGreater(b["stats"][ctypes.c_int(0)].value, 0) b.detach_uretprobe(name="/usr/bin/python", sym="main") b.detach_uprobe(name="/usr/bin/python", sym="main")
def setUpClass(cls): super().setUpClass() cls.target_cpu = 3 cls.xdp_prog = cls.load_bpf( b"progs/helper_functions.c", cflags=["-DREDIRECT_TARGET=" + str(cls.target_cpu)]) cls.to_send = cls.generate_default_packets() cls.tp_prog = bcc.BPF(text=b""" struct format { char padding1[16]; int cpu; char padding2[8]; int to_cpu; }; BPF_ARRAY(counter); int prog(struct format *format) { int zero = 0; u64 *val; val = counter.lookup(&zero); if (val) { *val += 1; u64 one = format->cpu; counter.update((int *)val, &one); *val += 1; u64 two = format->to_cpu; counter.update((int *)val, &two); } return 0; } """)
#!/usr/bin/env python3 import time import sys import bcc if __name__ == "__main__": iface = sys.argv[1].encode() prog = bcc.BPF(text=""" int dropper(struct xdp_md *ctx) { return XDP_DROP; } """) try: print("attached") prog.attach_xdp(iface, prog.load_func("dropper", bcc.BPF.XDP)) time.sleep(120) except: print("stopped") finally: print("removing") prog.remove_xdp(iface)
def __init__(self): super(KernelInspector, self).__init__() self.bpf = bcc.BPF(EBPF_PROGRAM) self.http_request_rate_per_pid = dict() self.http_resp_code_rate_per_pid = dict() self.lock = threading.Lock()
def __init__(self): super(KernelInspector, self).__init__() self.bpf = bcc.BPF(EBPF_FILE) self.http_rate_per_pid = dict() self.lock = threading.Lock()
def main(): b = bcc.BPF(text=bpf_text) while True: b.kprobe_poll()
#!/usr/bin/python import bcc import sys pid = int(sys.argv[1]) path = sys.argv[2] bpf_text = """ #include <linux/ptrace.h> int probe_func(struct pt_regs *ctx) { return 0; } """ u = bcc.USDT(pid=pid, path=path) ### works u.enable_probe(probe="probe_noarg_0", fn_name="probe_func") u.enable_probe(probe="probe_noarg_1", fn_name="probe_func") ### doesn't work u.enable_probe(probe="probe_arg_0", fn_name="probe_func") u.enable_probe(probe="probe_arg_1", fn_name="probe_func") b = bcc.BPF(text=bpf_text, usdt_contexts=[u]) while (True): pass
bpf_probe_read(&size, sizeof(size), &PT_REGS_PARM4(ctx)); print_pid(*p, size); return 0; } """ bpf_text = headers_txt + print_pid_txt.replace('FILTERSIZE', filtersize) + functions_txt if args.debug: print(bpf_text) # initialize BPF b = bcc.BPF(text=bpf_text) b.attach_uretprobe(name=beampath, sym="erts_schedule", fn_name="set_pid", pid=tracepid) b.attach_uprobe(name=beampath, sym="garbage_collect", fn_name="gc_called", pid=tracepid) b.attach_uretprobe(name=beampath, sym="garbage_collect", fn_name="gc_return", pid=tracepid) b.attach_uprobe(name=beampath, sym="erts_alcu_alloc_thr_spec", fn_name="size_arg3", pid=tracepid) b.attach_uprobe(name=beampath, sym="erts_alcu_alloc_thr_pref", fn_name="size_arg3", pid=tracepid) b.attach_uprobe(name=beampath, sym="erts_alcu_realloc", fn_name="size_arg4", pid=tracepid) # header print("Exit with ctrl-c") # format output
# from __future__ import print_function import bcc import time text = """ #include <uapi/linux/ptrace.h> BPF_HISTOGRAM(dist); int count(struct pt_regs *ctx) { dist.increment(bpf_log2l(PT_REGS_RC(ctx))); return 0; } """ b = bcc.BPF(text=text) sym="strlen" b.attach_uretprobe(name="c", sym=sym, fn_name="count") dist = b["dist"] try: while True: time.sleep(1) print("%-8s\n" % time.strftime("%H:%M:%S"), end="") dist.print_log2_hist(sym + " return:") dist.clear() except KeyboardInterrupt: pass
def test_mount_namespace(self): text = """ #include <uapi/linux/ptrace.h> BPF_TABLE("array", int, u64, stats, 1); static void incr(int idx) { u64 *ptr = stats.lookup(&idx); if (ptr) ++(*ptr); } int count(struct pt_regs *ctx) { bpf_trace_printk("count() uprobe fired"); u32 pid = bpf_get_current_pid_tgid(); if (pid == PID) incr(0); return 0; }""" # Need to import libc from ctypes to access unshare(2) libc = ctypes.CDLL("libc.so.6", use_errno=True) # Need to find path to libz.so.1 libz_path = None p = subprocess.Popen(["ldconfig", "-p"], stdout=subprocess.PIPE) for l in p.stdout: n = l.split() if n[0] == "libz.so.1": libz_path = n[-1] p.wait() p = None self.assertIsNotNone(libz_path) # fork a child that we'll place in a separate mount namespace child_pid = os.fork() if child_pid == 0: # Unshare CLONE_NEWNS if libc.unshare(0x00020000) == -1: e = ctypes.get_errno() raise OSError(e, errno.errorcode[e]) # Remount root MS_REC|MS_PRIVATE if libc.mount(None, "/", None, (1 << 14) | (1 << 18), None) == -1: e = ctypes.get_errno() raise OSError(e, errno.errorcode[e]) if libc.mount("tmpfs", "/tmp", "tmpfs", 0, None) == -1: e = ctypes.get_errno() raise OSError(e, errno.errorcode[e]) shutil.copy(libz_path, "/tmp") libz = ctypes.CDLL("/tmp/libz.so.1") time.sleep(1) libz.zlibVersion() time.sleep(5) os._exit(0) libname = "/tmp/libz.so.1" symname = "zlibVersion" text = text.replace("PID", "%d" % child_pid) b = bcc.BPF(text=text) b.attach_uprobe(name=libname, sym=symname, fn_name="count", pid=child_pid) b.attach_uretprobe(name=libname, sym=symname, fn_name="count", pid=child_pid) time.sleep(1) self.assertEqual(b["stats"][ctypes.c_int(0)].value, 2) b.detach_uretprobe(name=libname, sym=symname, pid=child_pid) b.detach_uprobe(name=libname, sym=symname, pid=child_pid) os.wait()
maps_string = f""" BPF_ARRAY(all_features, s64, 12); BPF_ARRAY(children_left, s64, {len(children_left)}); BPF_ARRAY(children_right, s64, {len(children_right)}); BPF_ARRAY(value, s64, {len(value)}); BPF_ARRAY(feature, s64, {len(feature)}); BPF_ARRAY(threshold, s64, {len(threshold)}); """ with open("ebpf.c") as f: actual_bpf_text = f.read() ebpf_program = maps_string + actual_bpf_text # initialize BPF - load source code from http-parse-simple.c bpf = bcc.BPF(text=ebpf_program, debug=False) children_left_table = bpf.get_table("children_left") for i in range(len(children_left)): children_left_table[i] = ct.c_long(children_left[i].item()) children_right_table = bpf.get_table("children_right") for i in range(len(children_right)): children_right_table[i] = ct.c_long(children_right[i].item()) value_table = bpf.get_table("value") for i in range(len(value)): value_table[i] = ct.c_long(value[i].item()) threshold_table = bpf.get_table("threshold") for i in range(len(threshold)): threshold_table[i] = ct.c_long(threshold[i].item()) feature_table = bpf.get_table("feature") for i in range(len(feature)):
return 0; } ''' def getQuery(measurement, pid, count): return '%s,item=nginx_req_count,pid=%d value=%d' % (measurement, pid, count) ip = sys.argv[1] port = int(sys.argv[2]) measurement = sys.argv[3] interval = int(sys.argv[4]) bpf = bcc.BPF(text=bpf_code) bpf.attach_uprobe(name="/usr/sbin/nginx", sym="ngx_http_create_request", fn_name="hook_ngx_http_create_req") s = socket.socket() #直接发往traceserver,不再经过agent print("measurement: %s" % (measurement)) print("connect %s:%d" % (ip, port)) s.connect((ip, port)) while True: data = bpf["reqs"] for key, val in data.items(): #data = json.dumps(getData(key.value, val.value)) data = getQuery(measurement, key.value, val.value)
def __init__(self): import bcc self._bpf = bcc.BPF(text=BPF_PROGRAM) self._recv_bytes = self._bpf["ipv4_recv_bytes"]