blob: 11170304fccfd3c43c58ff36121fd97d8a56ad93 [file] [log] [blame]
Brendan Greggf06d3b42015-10-15 17:21:32 -07001#!/usr/bin/python
Alexei Starovoitovbdf07732016-01-14 10:09:20 -08002# @lint-avoid-python-3-compatibility-imports
Brendan Greggf06d3b42015-10-15 17:21:32 -07003#
Alexei Starovoitovbdf07732016-01-14 10:09:20 -08004# tcpconnect Trace TCP connect()s.
5# For Linux, uses BCC, eBPF. Embedded C.
Brendan Greggf06d3b42015-10-15 17:21:32 -07006#
chantra52938052016-09-10 09:44:50 -07007# USAGE: tcpconnect [-h] [-t] [-p PID] [-P PORT [PORT ...]]
Brendan Greggf06d3b42015-10-15 17:21:32 -07008#
9# All connection attempts are traced, even if they ultimately fail.
10#
Brendan Gregg24825522016-02-14 16:32:29 -080011# This uses dynamic tracing of kernel functions, and will need to be updated
12# to match kernel changes.
13#
Brendan Greggf06d3b42015-10-15 17:21:32 -070014# Copyright (c) 2015 Brendan Gregg.
15# Licensed under the Apache License, Version 2.0 (the "License")
16#
Alexei Starovoitovbdf07732016-01-14 10:09:20 -080017# 25-Sep-2015 Brendan Gregg Created this.
Brendan Gregg24825522016-02-14 16:32:29 -080018# 14-Feb-2016 " " Switch to bpf_perf_output.
Brendan Greggf06d3b42015-10-15 17:21:32 -070019
20from __future__ import print_function
21from bcc import BPF
22import argparse
chantra52938052016-09-10 09:44:50 -070023from socket import inet_ntop, ntohs, AF_INET, AF_INET6
Mark Drayton11de2982016-06-26 21:14:44 +010024from struct import pack
Brendan Gregg24825522016-02-14 16:32:29 -080025import ctypes as ct
Brendan Greggf06d3b42015-10-15 17:21:32 -070026
27# arguments
28examples = """examples:
29 ./tcpconnect # trace all TCP connect()s
30 ./tcpconnect -t # include timestamps
31 ./tcpconnect -p 181 # only trace PID 181
chantra52938052016-09-10 09:44:50 -070032 ./tcpconnect -P 80 # only trace port 80
33 ./tcpconnect -P 80,81 # only trace port 80 and 81
Brendan Greggf06d3b42015-10-15 17:21:32 -070034"""
35parser = argparse.ArgumentParser(
Alexei Starovoitovbdf07732016-01-14 10:09:20 -080036 description="Trace TCP connects",
37 formatter_class=argparse.RawDescriptionHelpFormatter,
38 epilog=examples)
Brendan Greggf06d3b42015-10-15 17:21:32 -070039parser.add_argument("-t", "--timestamp", action="store_true",
Alexei Starovoitovbdf07732016-01-14 10:09:20 -080040 help="include timestamp on output")
Brendan Greggf06d3b42015-10-15 17:21:32 -070041parser.add_argument("-p", "--pid",
Alexei Starovoitovbdf07732016-01-14 10:09:20 -080042 help="trace this PID only")
chantra52938052016-09-10 09:44:50 -070043parser.add_argument("-P", "--port",
44 help="comma-separated list of destination ports to trace.")
Brendan Greggf06d3b42015-10-15 17:21:32 -070045args = parser.parse_args()
46debug = 0
47
48# define BPF program
49bpf_text = """
50#include <uapi/linux/ptrace.h>
51#include <net/sock.h>
52#include <bcc/proto.h>
53
54BPF_HASH(currsock, u32, struct sock *);
55
Brendan Gregg24825522016-02-14 16:32:29 -080056// separate data structs for ipv4 and ipv6
57struct ipv4_data_t {
58 // XXX: switch some to u32's when supported
59 u64 ts_us;
60 u64 pid;
Brendan Gregg24825522016-02-14 16:32:29 -080061 u64 saddr;
62 u64 daddr;
Mark Drayton11de2982016-06-26 21:14:44 +010063 u64 ip;
Brendan Gregg24825522016-02-14 16:32:29 -080064 u64 dport;
65 char task[TASK_COMM_LEN];
66};
67BPF_PERF_OUTPUT(ipv4_events);
68
69struct ipv6_data_t {
Brendan Gregg24825522016-02-14 16:32:29 -080070 u64 ts_us;
71 u64 pid;
Mark Drayton11de2982016-06-26 21:14:44 +010072 unsigned __int128 saddr;
73 unsigned __int128 daddr;
Brendan Gregg24825522016-02-14 16:32:29 -080074 u64 ip;
Brendan Gregg24825522016-02-14 16:32:29 -080075 u64 dport;
76 char task[TASK_COMM_LEN];
77};
78BPF_PERF_OUTPUT(ipv6_events);
79
Brendan Greggf06d3b42015-10-15 17:21:32 -070080int trace_connect_entry(struct pt_regs *ctx, struct sock *sk)
81{
Alexei Starovoitovbdf07732016-01-14 10:09:20 -080082 u32 pid = bpf_get_current_pid_tgid();
chantra52938052016-09-10 09:44:50 -070083 FILTER_PID
Brendan Greggf06d3b42015-10-15 17:21:32 -070084
Alexei Starovoitovbdf07732016-01-14 10:09:20 -080085 // stash the sock ptr for lookup on return
86 currsock.update(&pid, &sk);
Brendan Greggf06d3b42015-10-15 17:21:32 -070087
Alexei Starovoitovbdf07732016-01-14 10:09:20 -080088 return 0;
Brendan Greggf06d3b42015-10-15 17:21:32 -070089};
90
91static int trace_connect_return(struct pt_regs *ctx, short ipver)
92{
Naveen N. Rao4afa96a2016-05-03 14:54:21 +053093 int ret = PT_REGS_RC(ctx);
Alexei Starovoitovbdf07732016-01-14 10:09:20 -080094 u32 pid = bpf_get_current_pid_tgid();
Brendan Greggf06d3b42015-10-15 17:21:32 -070095
Alexei Starovoitovbdf07732016-01-14 10:09:20 -080096 struct sock **skpp;
97 skpp = currsock.lookup(&pid);
98 if (skpp == 0) {
99 return 0; // missed entry
100 }
Brendan Greggf06d3b42015-10-15 17:21:32 -0700101
Alexei Starovoitovbdf07732016-01-14 10:09:20 -0800102 if (ret != 0) {
103 // failed to send SYNC packet, may not have populated
104 // socket __sk_common.{skc_rcv_saddr, ...}
105 currsock.delete(&pid);
106 return 0;
107 }
Brendan Greggf06d3b42015-10-15 17:21:32 -0700108
Alexei Starovoitovbdf07732016-01-14 10:09:20 -0800109 // pull in details
110 struct sock *skp = *skpp;
Alexei Starovoitovbdf07732016-01-14 10:09:20 -0800111 u16 dport = 0;
112 bpf_probe_read(&dport, sizeof(dport), &skp->__sk_common.skc_dport);
Brendan Greggf06d3b42015-10-15 17:21:32 -0700113
chantra52938052016-09-10 09:44:50 -0700114 FILTER_PORT
115
Brendan Gregg24825522016-02-14 16:32:29 -0800116 if (ipver == 4) {
117 struct ipv4_data_t data4 = {.pid = pid, .ip = ipver};
118 data4.ts_us = bpf_ktime_get_ns() / 1000;
119 bpf_probe_read(&data4.saddr, sizeof(u32),
120 &skp->__sk_common.skc_rcv_saddr);
121 bpf_probe_read(&data4.daddr, sizeof(u32),
122 &skp->__sk_common.skc_daddr);
123 data4.dport = ntohs(dport);
124 bpf_get_current_comm(&data4.task, sizeof(data4.task));
125 ipv4_events.perf_submit(ctx, &data4, sizeof(data4));
126
Alexei Starovoitovbdf07732016-01-14 10:09:20 -0800127 } else /* 6 */ {
Brendan Gregg24825522016-02-14 16:32:29 -0800128 struct ipv6_data_t data6 = {.pid = pid, .ip = ipver};
129 data6.ts_us = bpf_ktime_get_ns() / 1000;
Mark Drayton11de2982016-06-26 21:14:44 +0100130 bpf_probe_read(&data6.saddr, sizeof(data6.saddr),
131 &skp->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32);
132 bpf_probe_read(&data6.daddr, sizeof(data6.daddr),
133 &skp->__sk_common.skc_v6_daddr.in6_u.u6_addr32);
Brendan Gregg24825522016-02-14 16:32:29 -0800134 data6.dport = ntohs(dport);
135 bpf_get_current_comm(&data6.task, sizeof(data6.task));
136 ipv6_events.perf_submit(ctx, &data6, sizeof(data6));
Alexei Starovoitovbdf07732016-01-14 10:09:20 -0800137 }
Brendan Greggf06d3b42015-10-15 17:21:32 -0700138
Alexei Starovoitovbdf07732016-01-14 10:09:20 -0800139 currsock.delete(&pid);
Brendan Greggf06d3b42015-10-15 17:21:32 -0700140
Alexei Starovoitovbdf07732016-01-14 10:09:20 -0800141 return 0;
Brendan Greggf06d3b42015-10-15 17:21:32 -0700142}
143
144int trace_connect_v4_return(struct pt_regs *ctx)
145{
Alexei Starovoitovbdf07732016-01-14 10:09:20 -0800146 return trace_connect_return(ctx, 4);
Brendan Greggf06d3b42015-10-15 17:21:32 -0700147}
148
149int trace_connect_v6_return(struct pt_regs *ctx)
150{
Alexei Starovoitovbdf07732016-01-14 10:09:20 -0800151 return trace_connect_return(ctx, 6);
Brendan Greggf06d3b42015-10-15 17:21:32 -0700152}
153"""
154
155# code substitutions
156if args.pid:
chantra52938052016-09-10 09:44:50 -0700157 bpf_text = bpf_text.replace('FILTER_PID',
Alexei Starovoitovbdf07732016-01-14 10:09:20 -0800158 'if (pid != %s) { return 0; }' % args.pid)
chantra52938052016-09-10 09:44:50 -0700159if args.port:
160 dports = [int(dport) for dport in args.port.split(',')]
161 dports_if = ' && '.join(['dport != %d' % ntohs(dport) for dport in dports])
162 bpf_text = bpf_text.replace('FILTER_PORT',
163 'if (%s) { currsock.delete(&pid); return 0; }' % dports_if)
164
165bpf_text = bpf_text.replace('FILTER_PID', '')
166bpf_text = bpf_text.replace('FILTER_PORT', '')
167
Brendan Greggf06d3b42015-10-15 17:21:32 -0700168if debug:
Alexei Starovoitovbdf07732016-01-14 10:09:20 -0800169 print(bpf_text)
Brendan Greggf06d3b42015-10-15 17:21:32 -0700170
Brendan Gregg24825522016-02-14 16:32:29 -0800171# event data
172TASK_COMM_LEN = 16 # linux/sched.h
Mark Drayton11de2982016-06-26 21:14:44 +0100173
Brendan Gregg24825522016-02-14 16:32:29 -0800174class Data_ipv4(ct.Structure):
175 _fields_ = [
176 ("ts_us", ct.c_ulonglong),
177 ("pid", ct.c_ulonglong),
Brendan Gregg24825522016-02-14 16:32:29 -0800178 ("saddr", ct.c_ulonglong),
179 ("daddr", ct.c_ulonglong),
Mark Drayton11de2982016-06-26 21:14:44 +0100180 ("ip", ct.c_ulonglong),
Brendan Gregg24825522016-02-14 16:32:29 -0800181 ("dport", ct.c_ulonglong),
182 ("task", ct.c_char * TASK_COMM_LEN)
183 ]
Mark Drayton11de2982016-06-26 21:14:44 +0100184
Brendan Gregg24825522016-02-14 16:32:29 -0800185class Data_ipv6(ct.Structure):
186 _fields_ = [
187 ("ts_us", ct.c_ulonglong),
188 ("pid", ct.c_ulonglong),
Mark Drayton11de2982016-06-26 21:14:44 +0100189 ("saddr", (ct.c_ulonglong * 2)),
190 ("daddr", (ct.c_ulonglong * 2)),
Brendan Gregg24825522016-02-14 16:32:29 -0800191 ("ip", ct.c_ulonglong),
Brendan Gregg24825522016-02-14 16:32:29 -0800192 ("dport", ct.c_ulonglong),
193 ("task", ct.c_char * TASK_COMM_LEN)
194 ]
195
196# process event
197def print_ipv4_event(cpu, data, size):
198 event = ct.cast(data, ct.POINTER(Data_ipv4)).contents
Mark Drayton11de2982016-06-26 21:14:44 +0100199 global start_ts
Brendan Gregg24825522016-02-14 16:32:29 -0800200 if args.timestamp:
201 if start_ts == 0:
202 start_ts = event.ts_us
Mark Drayton11de2982016-06-26 21:14:44 +0100203 print("%-9.3f" % ((float(event.ts_us) - start_ts) / 1000000), end="")
Rafael F78948e42017-03-26 14:54:25 +0200204 print("%-6d %-12.12s %-2d %-16s %-16s %-4d" % (event.pid,
205 event.task.decode(), event.ip,
206 inet_ntop(AF_INET, pack("I", event.saddr)),
Mark Drayton11de2982016-06-26 21:14:44 +0100207 inet_ntop(AF_INET, pack("I", event.daddr)), event.dport))
Brendan Gregg9e0b0872016-03-28 12:11:45 -0700208
Brendan Gregg24825522016-02-14 16:32:29 -0800209def print_ipv6_event(cpu, data, size):
210 event = ct.cast(data, ct.POINTER(Data_ipv6)).contents
Mark Drayton11de2982016-06-26 21:14:44 +0100211 global start_ts
Brendan Gregg24825522016-02-14 16:32:29 -0800212 if args.timestamp:
213 if start_ts == 0:
214 start_ts = event.ts_us
Mark Drayton11de2982016-06-26 21:14:44 +0100215 print("%-9.3f" % ((float(event.ts_us) - start_ts) / 1000000), end="")
Brendan Gregg9e0b0872016-03-28 12:11:45 -0700216 print("%-6d %-12.12s %-2d %-16s %-16s %-4d" % (event.pid,
Rafael F78948e42017-03-26 14:54:25 +0200217 event.task.decode(), event.ip, inet_ntop(AF_INET6, event.saddr),
Mark Drayton11de2982016-06-26 21:14:44 +0100218 inet_ntop(AF_INET6, event.daddr), event.dport))
Brendan Gregg24825522016-02-14 16:32:29 -0800219
Brendan Greggf06d3b42015-10-15 17:21:32 -0700220# initialize BPF
221b = BPF(text=bpf_text)
222b.attach_kprobe(event="tcp_v4_connect", fn_name="trace_connect_entry")
223b.attach_kprobe(event="tcp_v6_connect", fn_name="trace_connect_entry")
224b.attach_kretprobe(event="tcp_v4_connect", fn_name="trace_connect_v4_return")
225b.attach_kretprobe(event="tcp_v6_connect", fn_name="trace_connect_v6_return")
226
227# header
228if args.timestamp:
Alexei Starovoitovbdf07732016-01-14 10:09:20 -0800229 print("%-9s" % ("TIME(s)"), end="")
Brendan Greggf06d3b42015-10-15 17:21:32 -0700230print("%-6s %-12s %-2s %-16s %-16s %-4s" % ("PID", "COMM", "IP", "SADDR",
231 "DADDR", "DPORT"))
232
233start_ts = 0
234
Brendan Gregg24825522016-02-14 16:32:29 -0800235# read events
236b["ipv4_events"].open_perf_buffer(print_ipv4_event)
237b["ipv6_events"].open_perf_buffer(print_ipv6_event)
Brendan Greggf06d3b42015-10-15 17:21:32 -0700238while 1:
Brendan Gregg24825522016-02-14 16:32:29 -0800239 b.kprobe_poll()