blob: fe793ad55f73784be4bd2347c7faaf57c0273113 [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.")
Nathan Scottcf0792f2018-02-02 16:56:50 +110045parser.add_argument("--ebpf", action="store_true",
46 help=argparse.SUPPRESS)
Brendan Greggf06d3b42015-10-15 17:21:32 -070047args = parser.parse_args()
48debug = 0
49
50# define BPF program
51bpf_text = """
52#include <uapi/linux/ptrace.h>
53#include <net/sock.h>
54#include <bcc/proto.h>
55
56BPF_HASH(currsock, u32, struct sock *);
57
Brendan Gregg24825522016-02-14 16:32:29 -080058// separate data structs for ipv4 and ipv6
59struct ipv4_data_t {
60 // XXX: switch some to u32's when supported
61 u64 ts_us;
62 u64 pid;
Brendan Gregg24825522016-02-14 16:32:29 -080063 u64 saddr;
64 u64 daddr;
Mark Drayton11de2982016-06-26 21:14:44 +010065 u64 ip;
Brendan Gregg24825522016-02-14 16:32:29 -080066 u64 dport;
67 char task[TASK_COMM_LEN];
68};
69BPF_PERF_OUTPUT(ipv4_events);
70
71struct ipv6_data_t {
Brendan Gregg24825522016-02-14 16:32:29 -080072 u64 ts_us;
73 u64 pid;
Mark Drayton11de2982016-06-26 21:14:44 +010074 unsigned __int128 saddr;
75 unsigned __int128 daddr;
Brendan Gregg24825522016-02-14 16:32:29 -080076 u64 ip;
Brendan Gregg24825522016-02-14 16:32:29 -080077 u64 dport;
78 char task[TASK_COMM_LEN];
79};
80BPF_PERF_OUTPUT(ipv6_events);
81
Brendan Greggf06d3b42015-10-15 17:21:32 -070082int trace_connect_entry(struct pt_regs *ctx, struct sock *sk)
83{
Alexei Starovoitovbdf07732016-01-14 10:09:20 -080084 u32 pid = bpf_get_current_pid_tgid();
chantra52938052016-09-10 09:44:50 -070085 FILTER_PID
Brendan Greggf06d3b42015-10-15 17:21:32 -070086
Alexei Starovoitovbdf07732016-01-14 10:09:20 -080087 // stash the sock ptr for lookup on return
88 currsock.update(&pid, &sk);
Brendan Greggf06d3b42015-10-15 17:21:32 -070089
Alexei Starovoitovbdf07732016-01-14 10:09:20 -080090 return 0;
Brendan Greggf06d3b42015-10-15 17:21:32 -070091};
92
93static int trace_connect_return(struct pt_regs *ctx, short ipver)
94{
Naveen N. Rao4afa96a2016-05-03 14:54:21 +053095 int ret = PT_REGS_RC(ctx);
Alexei Starovoitovbdf07732016-01-14 10:09:20 -080096 u32 pid = bpf_get_current_pid_tgid();
Brendan Greggf06d3b42015-10-15 17:21:32 -070097
Alexei Starovoitovbdf07732016-01-14 10:09:20 -080098 struct sock **skpp;
99 skpp = currsock.lookup(&pid);
100 if (skpp == 0) {
101 return 0; // missed entry
102 }
Brendan Greggf06d3b42015-10-15 17:21:32 -0700103
Alexei Starovoitovbdf07732016-01-14 10:09:20 -0800104 if (ret != 0) {
105 // failed to send SYNC packet, may not have populated
106 // socket __sk_common.{skc_rcv_saddr, ...}
107 currsock.delete(&pid);
108 return 0;
109 }
Brendan Greggf06d3b42015-10-15 17:21:32 -0700110
Alexei Starovoitovbdf07732016-01-14 10:09:20 -0800111 // pull in details
112 struct sock *skp = *skpp;
Paul Chaignoneae0acf2017-08-05 23:04:41 +0200113 u16 dport = skp->__sk_common.skc_dport;
Brendan Greggf06d3b42015-10-15 17:21:32 -0700114
chantra52938052016-09-10 09:44:50 -0700115 FILTER_PORT
116
Brendan Gregg24825522016-02-14 16:32:29 -0800117 if (ipver == 4) {
118 struct ipv4_data_t data4 = {.pid = pid, .ip = ipver};
119 data4.ts_us = bpf_ktime_get_ns() / 1000;
Paul Chaignoneae0acf2017-08-05 23:04:41 +0200120 data4.saddr = skp->__sk_common.skc_rcv_saddr;
121 data4.daddr = skp->__sk_common.skc_daddr;
Brendan Gregg24825522016-02-14 16:32:29 -0800122 data4.dport = ntohs(dport);
123 bpf_get_current_comm(&data4.task, sizeof(data4.task));
124 ipv4_events.perf_submit(ctx, &data4, sizeof(data4));
125
Alexei Starovoitovbdf07732016-01-14 10:09:20 -0800126 } else /* 6 */ {
Brendan Gregg24825522016-02-14 16:32:29 -0800127 struct ipv6_data_t data6 = {.pid = pid, .ip = ipver};
128 data6.ts_us = bpf_ktime_get_ns() / 1000;
Mark Drayton11de2982016-06-26 21:14:44 +0100129 bpf_probe_read(&data6.saddr, sizeof(data6.saddr),
Paul Chaignoneae0acf2017-08-05 23:04:41 +0200130 skp->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32);
Mark Drayton11de2982016-06-26 21:14:44 +0100131 bpf_probe_read(&data6.daddr, sizeof(data6.daddr),
Paul Chaignoneae0acf2017-08-05 23:04:41 +0200132 skp->__sk_common.skc_v6_daddr.in6_u.u6_addr32);
Brendan Gregg24825522016-02-14 16:32:29 -0800133 data6.dport = ntohs(dport);
134 bpf_get_current_comm(&data6.task, sizeof(data6.task));
135 ipv6_events.perf_submit(ctx, &data6, sizeof(data6));
Alexei Starovoitovbdf07732016-01-14 10:09:20 -0800136 }
Brendan Greggf06d3b42015-10-15 17:21:32 -0700137
Alexei Starovoitovbdf07732016-01-14 10:09:20 -0800138 currsock.delete(&pid);
Brendan Greggf06d3b42015-10-15 17:21:32 -0700139
Alexei Starovoitovbdf07732016-01-14 10:09:20 -0800140 return 0;
Brendan Greggf06d3b42015-10-15 17:21:32 -0700141}
142
143int trace_connect_v4_return(struct pt_regs *ctx)
144{
Alexei Starovoitovbdf07732016-01-14 10:09:20 -0800145 return trace_connect_return(ctx, 4);
Brendan Greggf06d3b42015-10-15 17:21:32 -0700146}
147
148int trace_connect_v6_return(struct pt_regs *ctx)
149{
Alexei Starovoitovbdf07732016-01-14 10:09:20 -0800150 return trace_connect_return(ctx, 6);
Brendan Greggf06d3b42015-10-15 17:21:32 -0700151}
152"""
153
154# code substitutions
155if args.pid:
chantra52938052016-09-10 09:44:50 -0700156 bpf_text = bpf_text.replace('FILTER_PID',
Alexei Starovoitovbdf07732016-01-14 10:09:20 -0800157 'if (pid != %s) { return 0; }' % args.pid)
chantra52938052016-09-10 09:44:50 -0700158if args.port:
159 dports = [int(dport) for dport in args.port.split(',')]
160 dports_if = ' && '.join(['dport != %d' % ntohs(dport) for dport in dports])
161 bpf_text = bpf_text.replace('FILTER_PORT',
162 'if (%s) { currsock.delete(&pid); return 0; }' % dports_if)
163
164bpf_text = bpf_text.replace('FILTER_PID', '')
165bpf_text = bpf_text.replace('FILTER_PORT', '')
166
Nathan Scottcf0792f2018-02-02 16:56:50 +1100167if debug or args.ebpf:
Alexei Starovoitovbdf07732016-01-14 10:09:20 -0800168 print(bpf_text)
Nathan Scottcf0792f2018-02-02 16:56:50 +1100169 if args.ebpf:
170 exit()
Brendan Greggf06d3b42015-10-15 17:21:32 -0700171
Brendan Gregg24825522016-02-14 16:32:29 -0800172# event data
173TASK_COMM_LEN = 16 # linux/sched.h
Mark Drayton11de2982016-06-26 21:14:44 +0100174
Brendan Gregg24825522016-02-14 16:32:29 -0800175class Data_ipv4(ct.Structure):
176 _fields_ = [
177 ("ts_us", ct.c_ulonglong),
178 ("pid", ct.c_ulonglong),
Brendan Gregg24825522016-02-14 16:32:29 -0800179 ("saddr", ct.c_ulonglong),
180 ("daddr", ct.c_ulonglong),
Mark Drayton11de2982016-06-26 21:14:44 +0100181 ("ip", ct.c_ulonglong),
Brendan Gregg24825522016-02-14 16:32:29 -0800182 ("dport", ct.c_ulonglong),
183 ("task", ct.c_char * TASK_COMM_LEN)
184 ]
Mark Drayton11de2982016-06-26 21:14:44 +0100185
Brendan Gregg24825522016-02-14 16:32:29 -0800186class Data_ipv6(ct.Structure):
187 _fields_ = [
188 ("ts_us", ct.c_ulonglong),
189 ("pid", ct.c_ulonglong),
Mark Drayton11de2982016-06-26 21:14:44 +0100190 ("saddr", (ct.c_ulonglong * 2)),
191 ("daddr", (ct.c_ulonglong * 2)),
Brendan Gregg24825522016-02-14 16:32:29 -0800192 ("ip", ct.c_ulonglong),
Brendan Gregg24825522016-02-14 16:32:29 -0800193 ("dport", ct.c_ulonglong),
194 ("task", ct.c_char * TASK_COMM_LEN)
195 ]
196
197# process event
198def print_ipv4_event(cpu, data, size):
199 event = ct.cast(data, ct.POINTER(Data_ipv4)).contents
Mark Drayton11de2982016-06-26 21:14:44 +0100200 global start_ts
Brendan Gregg24825522016-02-14 16:32:29 -0800201 if args.timestamp:
202 if start_ts == 0:
203 start_ts = event.ts_us
Mark Drayton11de2982016-06-26 21:14:44 +0100204 print("%-9.3f" % ((float(event.ts_us) - start_ts) / 1000000), end="")
Rafael F78948e42017-03-26 14:54:25 +0200205 print("%-6d %-12.12s %-2d %-16s %-16s %-4d" % (event.pid,
206 event.task.decode(), event.ip,
207 inet_ntop(AF_INET, pack("I", event.saddr)),
Mark Drayton11de2982016-06-26 21:14:44 +0100208 inet_ntop(AF_INET, pack("I", event.daddr)), event.dport))
Brendan Gregg9e0b0872016-03-28 12:11:45 -0700209
Brendan Gregg24825522016-02-14 16:32:29 -0800210def print_ipv6_event(cpu, data, size):
211 event = ct.cast(data, ct.POINTER(Data_ipv6)).contents
Mark Drayton11de2982016-06-26 21:14:44 +0100212 global start_ts
Brendan Gregg24825522016-02-14 16:32:29 -0800213 if args.timestamp:
214 if start_ts == 0:
215 start_ts = event.ts_us
Mark Drayton11de2982016-06-26 21:14:44 +0100216 print("%-9.3f" % ((float(event.ts_us) - start_ts) / 1000000), end="")
Brendan Gregg9e0b0872016-03-28 12:11:45 -0700217 print("%-6d %-12.12s %-2d %-16s %-16s %-4d" % (event.pid,
Rafael F78948e42017-03-26 14:54:25 +0200218 event.task.decode(), event.ip, inet_ntop(AF_INET6, event.saddr),
Mark Drayton11de2982016-06-26 21:14:44 +0100219 inet_ntop(AF_INET6, event.daddr), event.dport))
Brendan Gregg24825522016-02-14 16:32:29 -0800220
Brendan Greggf06d3b42015-10-15 17:21:32 -0700221# initialize BPF
222b = BPF(text=bpf_text)
223b.attach_kprobe(event="tcp_v4_connect", fn_name="trace_connect_entry")
224b.attach_kprobe(event="tcp_v6_connect", fn_name="trace_connect_entry")
225b.attach_kretprobe(event="tcp_v4_connect", fn_name="trace_connect_v4_return")
226b.attach_kretprobe(event="tcp_v6_connect", fn_name="trace_connect_v6_return")
227
228# header
229if args.timestamp:
Alexei Starovoitovbdf07732016-01-14 10:09:20 -0800230 print("%-9s" % ("TIME(s)"), end="")
Brendan Greggf06d3b42015-10-15 17:21:32 -0700231print("%-6s %-12s %-2s %-16s %-16s %-4s" % ("PID", "COMM", "IP", "SADDR",
232 "DADDR", "DPORT"))
233
234start_ts = 0
235
Brendan Gregg24825522016-02-14 16:32:29 -0800236# read events
237b["ipv4_events"].open_perf_buffer(print_ipv4_event)
238b["ipv6_events"].open_perf_buffer(print_ipv6_event)
Brendan Greggf06d3b42015-10-15 17:21:32 -0700239while 1:
Teng Qindbf00292018-02-28 21:47:50 -0800240 b.perf_buffer_poll()