1#!/usr/bin/env 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 21from __future__ import print_function 22from bcc import BPF 23import argparse 24 25# arguments 26examples = """examples: 27 ./tcpaccept # trace all TCP accept()s 28 ./tcpaccept -t # include timestamps 29 ./tcpaccept -p 181 # only trace PID 181 30""" 31parser = argparse.ArgumentParser( 32 description="Trace TCP accepts", 33 formatter_class=argparse.RawDescriptionHelpFormatter, 34 epilog=examples) 35parser.add_argument("-t", "--timestamp", action="store_true", 36 help="include timestamp on output") 37parser.add_argument("-p", "--pid", 38 help="trace this PID only") 39args = parser.parse_args() 40debug = 0 41 42# define BPF program 43bpf_text = """ 44#include <uapi/linux/ptrace.h> 45#include <net/sock.h> 46#include <bcc/proto.h> 47 48int 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 94if args.pid: 95 bpf_text = bpf_text.replace('FILTER', 96 'if (pid != %s) { return 0; }' % args.pid) 97else: 98 bpf_text = bpf_text.replace('FILTER', '') 99if debug: 100 print(bpf_text) 101 102# initialize BPF 103b = BPF(text=bpf_text) 104 105# header 106if args.timestamp: 107 print("%-9s" % ("TIME(s)"), end="") 108print("%-6s %-12s %-2s %-16s %-16s %-4s" % ("PID", "COMM", "IP", "RADDR", 109 "LADDR", "LPORT")) 110 111start_ts = 0 112 113def 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 123while 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