1 #!/usr/bin/python 2 # @lint-avoid-python-3-compatibility-imports 3 # 4 # tcpaccept Trace TCP accept()s. 5 # For Linux, uses BCC, eBPF. Embedded C. 6 # 7 # USAGE: tcpaccept [-h] [-t] [-p PID] 8 # 9 # This uses dynamic tracing of the kernel inet_csk_accept() socket function 10 # (from tcp_prot.accept), and will need to be modified to match kernel changes. 11 # 12 # IPv4 addresses are printed as dotted quads. For IPv6 addresses, the last four 13 # bytes are printed after "..."; check for future versions with better IPv6 14 # support. 15 # 16 # Copyright (c) 2015 Brendan Gregg. 17 # Licensed under the Apache License, Version 2.0 (the "License") 18 # 19 # 13-Oct-2015 Brendan Gregg Created this. 20 21 from __future__ import print_function 22 from bcc import BPF 23 import argparse 24 25 # arguments 26 examples = """examples: 27 ./tcpaccept # trace all TCP accept()s 28 ./tcpaccept -t # include timestamps 29 ./tcpaccept -p 181 # only trace PID 181 30 """ 31 parser = argparse.ArgumentParser( 32 description="Trace TCP accepts", 33 formatter_class=argparse.RawDescriptionHelpFormatter, 34 epilog=examples) 35 parser.add_argument("-t", "--timestamp", action="store_true", 36 help="include timestamp on output") 37 parser.add_argument("-p", "--pid", 38 help="trace this PID only") 39 args = parser.parse_args() 40 debug = 0 41 42 # define BPF program 43 bpf_text = """ 44 #include <uapi/linux/ptrace.h> 45 #include <net/sock.h> 46 #include <bcc/proto.h> 47 48 int kretprobe__inet_csk_accept(struct pt_regs *ctx) 49 { 50 struct sock *newsk = (struct sock *)PT_REGS_RC(ctx); 51 u32 pid = bpf_get_current_pid_tgid(); 52 53 if (newsk == NULL) 54 return 0; 55 56 // check this is TCP 57 u8 protocol = 0; 58 // workaround for reading the sk_protocol bitfield: 59 bpf_probe_read(&protocol, 1, (void *)((long)&newsk->sk_wmem_queued) - 3); 60 if (protocol != IPPROTO_TCP) 61 return 0; 62 63 // pull in details 64 u16 family = 0, lport = 0; 65 u32 saddr = 0, daddr = 0; 66 bpf_probe_read(&family, sizeof(family), &newsk->__sk_common.skc_family); 67 bpf_probe_read(&lport, sizeof(lport), &newsk->__sk_common.skc_num); 68 if (family == AF_INET) { 69 bpf_probe_read(&saddr, sizeof(saddr), 70 &newsk->__sk_common.skc_rcv_saddr); 71 bpf_probe_read(&daddr, sizeof(daddr), 72 &newsk->__sk_common.skc_daddr); 73 74 // output 75 bpf_trace_printk("4 %x %x %d\\n", daddr, saddr, lport); 76 } else if (family == AF_INET6) { 77 // just grab the last 4 bytes for now 78 bpf_probe_read(&saddr, sizeof(saddr), 79 &newsk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32[3]); 80 bpf_probe_read(&daddr, sizeof(daddr), 81 &newsk->__sk_common.skc_v6_daddr.in6_u.u6_addr32[3]); 82 83 // output and flip byte order of addresses 84 bpf_trace_printk("6 %x %x %d\\n", bpf_ntohl(daddr), 85 bpf_ntohl(saddr), lport); 86 } 87 // else drop 88 89 return 0; 90 } 91 """ 92 93 # code substitutions 94 if args.pid: 95 bpf_text = bpf_text.replace('FILTER', 96 'if (pid != %s) { return 0; }' % args.pid) 97 else: 98 bpf_text = bpf_text.replace('FILTER', '') 99 if debug: 100 print(bpf_text) 101 102 # initialize BPF 103 b = BPF(text=bpf_text) 104 105 # header 106 if args.timestamp: 107 print("%-9s" % ("TIME(s)"), end="") 108 print("%-6s %-12s %-2s %-16s %-16s %-4s" % ("PID", "COMM", "IP", "RADDR", 109 "LADDR", "LPORT")) 110 111 start_ts = 0 112 113 def inet_ntoa(addr): 114 dq = '' 115 for i in range(0, 4): 116 dq = dq + str(addr & 0xff) 117 if (i != 3): 118 dq = dq + '.' 119 addr = addr >> 8 120 return dq 121 122 # format output 123 while 1: 124 (task, pid, cpu, flags, ts, msg) = b.trace_fields() 125 (ip_s, raddr_hs, laddr_hs, lport_s) = msg.split(" ") 126 127 if args.timestamp: 128 if start_ts == 0: 129 start_ts = ts 130 print("%-9.3f" % (ts - start_ts), end="") 131 print("%-6d %-12.12s %-2s %-16s %-16s %-4s" % (pid, task, ip_s, 132 inet_ntoa(int(raddr_hs, 16)) if ip_s == "4" else "..." + raddr_hs, 133 inet_ntoa(int(laddr_hs, 16)) if ip_s == "4" else "..." + laddr_hs, 134 lport_s)) 135