1#!/usr/bin/python 2# @lint-avoid-python-3-compatibility-imports 3# 4# tcpconnect Trace TCP connect()s. 5# For Linux, uses BCC, eBPF. Embedded C. 6# 7# USAGE: tcpconnect [-h] [-t] [-p PID] 8# 9# All connection attempts are traced, even if they ultimately fail. 10# 11# Copyright (c) 2015 Brendan Gregg. 12# Licensed under the Apache License, Version 2.0 (the "License") 13# 14# 25-Sep-2015 Brendan Gregg Created this. 15 16from __future__ import print_function 17from bcc import BPF 18import argparse 19 20# arguments 21examples = """examples: 22 ./tcpconnect # trace all TCP connect()s 23 ./tcpconnect -t # include timestamps 24 ./tcpconnect -p 181 # only trace PID 181 25""" 26parser = argparse.ArgumentParser( 27 description="Trace TCP connects", 28 formatter_class=argparse.RawDescriptionHelpFormatter, 29 epilog=examples) 30parser.add_argument("-t", "--timestamp", action="store_true", 31 help="include timestamp on output") 32parser.add_argument("-p", "--pid", 33 help="trace this PID only") 34args = parser.parse_args() 35debug = 0 36 37# define BPF program 38bpf_text = """ 39#include <uapi/linux/ptrace.h> 40#include <net/sock.h> 41#include <bcc/proto.h> 42 43BPF_HASH(currsock, u32, struct sock *); 44 45int trace_connect_entry(struct pt_regs *ctx, struct sock *sk) 46{ 47 u32 pid = bpf_get_current_pid_tgid(); 48 FILTER 49 50 // stash the sock ptr for lookup on return 51 currsock.update(&pid, &sk); 52 53 return 0; 54}; 55 56static int trace_connect_return(struct pt_regs *ctx, short ipver) 57{ 58 int ret = PT_REGS_RC(ctx); 59 u32 pid = bpf_get_current_pid_tgid(); 60 61 struct sock **skpp; 62 skpp = currsock.lookup(&pid); 63 if (skpp == 0) { 64 return 0; // missed entry 65 } 66 67 if (ret != 0) { 68 // failed to send SYNC packet, may not have populated 69 // socket __sk_common.{skc_rcv_saddr, ...} 70 currsock.delete(&pid); 71 return 0; 72 } 73 74 // pull in details 75 struct sock *skp = *skpp; 76 u32 saddr = 0, daddr = 0; 77 u16 dport = 0; 78 dport = skp->__sk_common.skc_dport; 79 if (ipver == 4) { 80 saddr = skp->__sk_common.skc_rcv_saddr; 81 daddr = skp->__sk_common.skc_daddr; 82 83 // output 84 bpf_trace_printk("4 %x %x %d\\n", saddr, daddr, ntohs(dport)); 85 } else /* 6 */ { 86 // just grab the last 4 bytes for now 87 bpf_probe_read(&saddr, sizeof(saddr), 88 &skp->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32[3]); 89 bpf_probe_read(&daddr, sizeof(daddr), 90 &skp->__sk_common.skc_v6_daddr.in6_u.u6_addr32[3]); 91 92 // output and flip byte order of addresses 93 bpf_trace_printk("6 %x %x %d\\n", bpf_ntohl(saddr), 94 bpf_ntohl(daddr), ntohs(dport)); 95 } 96 97 currsock.delete(&pid); 98 99 return 0; 100} 101 102int trace_connect_v4_return(struct pt_regs *ctx) 103{ 104 return trace_connect_return(ctx, 4); 105} 106 107int trace_connect_v6_return(struct pt_regs *ctx) 108{ 109 return trace_connect_return(ctx, 6); 110} 111""" 112 113# code substitutions 114if args.pid: 115 bpf_text = bpf_text.replace('FILTER', 116 'if (pid != %s) { return 0; }' % args.pid) 117else: 118 bpf_text = bpf_text.replace('FILTER', '') 119if debug: 120 print(bpf_text) 121 122# initialize BPF 123b = BPF(text=bpf_text) 124b.attach_kprobe(event="tcp_v4_connect", fn_name="trace_connect_entry") 125b.attach_kprobe(event="tcp_v6_connect", fn_name="trace_connect_entry") 126b.attach_kretprobe(event="tcp_v4_connect", fn_name="trace_connect_v4_return") 127b.attach_kretprobe(event="tcp_v6_connect", fn_name="trace_connect_v6_return") 128 129# header 130if args.timestamp: 131 print("%-9s" % ("TIME(s)"), end="") 132print("%-6s %-12s %-2s %-16s %-16s %-4s" % ("PID", "COMM", "IP", "SADDR", 133 "DADDR", "DPORT")) 134 135start_ts = 0 136 137def inet_ntoa(addr): 138 dq = '' 139 for i in range(0, 4): 140 dq = dq + str(addr & 0xff) 141 if (i != 3): 142 dq = dq + '.' 143 addr = addr >> 8 144 return dq 145 146# format output 147while 1: 148 (task, pid, cpu, flags, ts, msg) = b.trace_fields() 149 (ip_s, saddr_hs, daddr_hs, dport_s) = msg.split(" ") 150 151 if args.timestamp: 152 if start_ts == 0: 153 start_ts = ts 154 print("%-9.3f" % (ts - start_ts), end="") 155 print("%-6d %-12.12s %-2s %-16s %-16s %-4s" % (pid, task, ip_s, 156 inet_ntoa(int(saddr_hs, 16)) if ip_s == "4" else "..." + saddr_hs, 157 inet_ntoa(int(daddr_hs, 16)) if ip_s == "4" else "..." + daddr_hs, 158 dport_s)) 159