blob: 5ca6851dad7542da04749676d9169aeab7e5a4e1 [file] [log] [blame]
Alexey Ivanov777e8022019-01-03 13:46:38 -08001#!/usr/bin/env 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.
Takuma Kumeb181a8e2019-01-10 05:49:59 +090019# 09-Jan-2019 Takuma Kume Support filtering by UID
Brendan Greggf06d3b42015-10-15 17:21:32 -070020
21from __future__ import print_function
22from bcc import BPF
japrocaed9b1e2019-01-04 20:21:46 +030023from bcc.utils import printb
Brendan Greggf06d3b42015-10-15 17:21:32 -070024import argparse
chantra52938052016-09-10 09:44:50 -070025from socket import inet_ntop, ntohs, AF_INET, AF_INET6
Mark Drayton11de2982016-06-26 21:14:44 +010026from struct import pack
Brendan Gregg24825522016-02-14 16:32:29 -080027import ctypes as ct
Brendan Greggf06d3b42015-10-15 17:21:32 -070028
29# arguments
30examples = """examples:
31 ./tcpconnect # trace all TCP connect()s
32 ./tcpconnect -t # include timestamps
33 ./tcpconnect -p 181 # only trace PID 181
chantra52938052016-09-10 09:44:50 -070034 ./tcpconnect -P 80 # only trace port 80
35 ./tcpconnect -P 80,81 # only trace port 80 and 81
Takuma Kumeb181a8e2019-01-10 05:49:59 +090036 ./tcpconnect -U # include UID
37 ./tcpconnect -u 1000 # only trace UID 1000
Brendan Greggf06d3b42015-10-15 17:21:32 -070038"""
39parser = argparse.ArgumentParser(
Alexei Starovoitovbdf07732016-01-14 10:09:20 -080040 description="Trace TCP connects",
41 formatter_class=argparse.RawDescriptionHelpFormatter,
42 epilog=examples)
Brendan Greggf06d3b42015-10-15 17:21:32 -070043parser.add_argument("-t", "--timestamp", action="store_true",
Alexei Starovoitovbdf07732016-01-14 10:09:20 -080044 help="include timestamp on output")
Brendan Greggf06d3b42015-10-15 17:21:32 -070045parser.add_argument("-p", "--pid",
Alexei Starovoitovbdf07732016-01-14 10:09:20 -080046 help="trace this PID only")
chantra52938052016-09-10 09:44:50 -070047parser.add_argument("-P", "--port",
48 help="comma-separated list of destination ports to trace.")
Takuma Kumeb181a8e2019-01-10 05:49:59 +090049parser.add_argument("-U", "--print-uid", action="store_true",
50 help="include UID on output")
51parser.add_argument("-u", "--uid",
52 help="trace this UID only")
Nathan Scottcf0792f2018-02-02 16:56:50 +110053parser.add_argument("--ebpf", action="store_true",
54 help=argparse.SUPPRESS)
Brendan Greggf06d3b42015-10-15 17:21:32 -070055args = parser.parse_args()
56debug = 0
57
58# define BPF program
59bpf_text = """
60#include <uapi/linux/ptrace.h>
61#include <net/sock.h>
62#include <bcc/proto.h>
63
64BPF_HASH(currsock, u32, struct sock *);
65
Brendan Gregg24825522016-02-14 16:32:29 -080066// separate data structs for ipv4 and ipv6
67struct ipv4_data_t {
Brendan Gregg24825522016-02-14 16:32:29 -080068 u64 ts_us;
Joe Yin36ce1122018-08-17 06:04:00 +080069 u32 pid;
Takuma Kumeb181a8e2019-01-10 05:49:59 +090070 u32 uid;
Joe Yin36ce1122018-08-17 06:04:00 +080071 u32 saddr;
72 u32 daddr;
Mark Drayton11de2982016-06-26 21:14:44 +010073 u64 ip;
Joe Yin36ce1122018-08-17 06:04:00 +080074 u16 dport;
Brendan Gregg24825522016-02-14 16:32:29 -080075 char task[TASK_COMM_LEN];
76};
77BPF_PERF_OUTPUT(ipv4_events);
78
79struct ipv6_data_t {
Brendan Gregg24825522016-02-14 16:32:29 -080080 u64 ts_us;
Joe Yin36ce1122018-08-17 06:04:00 +080081 u32 pid;
Takuma Kumeb181a8e2019-01-10 05:49:59 +090082 u32 uid;
Mark Drayton11de2982016-06-26 21:14:44 +010083 unsigned __int128 saddr;
84 unsigned __int128 daddr;
Brendan Gregg24825522016-02-14 16:32:29 -080085 u64 ip;
Joe Yin36ce1122018-08-17 06:04:00 +080086 u16 dport;
Brendan Gregg24825522016-02-14 16:32:29 -080087 char task[TASK_COMM_LEN];
88};
89BPF_PERF_OUTPUT(ipv6_events);
90
Brendan Greggf06d3b42015-10-15 17:21:32 -070091int trace_connect_entry(struct pt_regs *ctx, struct sock *sk)
92{
Alexei Starovoitovbdf07732016-01-14 10:09:20 -080093 u32 pid = bpf_get_current_pid_tgid();
chantra52938052016-09-10 09:44:50 -070094 FILTER_PID
Brendan Greggf06d3b42015-10-15 17:21:32 -070095
Takuma Kumeb181a8e2019-01-10 05:49:59 +090096 u32 uid = bpf_get_current_uid_gid();
97 FILTER_UID
98
Alexei Starovoitovbdf07732016-01-14 10:09:20 -080099 // stash the sock ptr for lookup on return
100 currsock.update(&pid, &sk);
Brendan Greggf06d3b42015-10-15 17:21:32 -0700101
Alexei Starovoitovbdf07732016-01-14 10:09:20 -0800102 return 0;
Brendan Greggf06d3b42015-10-15 17:21:32 -0700103};
104
105static int trace_connect_return(struct pt_regs *ctx, short ipver)
106{
Naveen N. Rao4afa96a2016-05-03 14:54:21 +0530107 int ret = PT_REGS_RC(ctx);
Alexei Starovoitovbdf07732016-01-14 10:09:20 -0800108 u32 pid = bpf_get_current_pid_tgid();
Brendan Greggf06d3b42015-10-15 17:21:32 -0700109
Alexei Starovoitovbdf07732016-01-14 10:09:20 -0800110 struct sock **skpp;
111 skpp = currsock.lookup(&pid);
112 if (skpp == 0) {
113 return 0; // missed entry
114 }
Brendan Greggf06d3b42015-10-15 17:21:32 -0700115
Alexei Starovoitovbdf07732016-01-14 10:09:20 -0800116 if (ret != 0) {
117 // failed to send SYNC packet, may not have populated
118 // socket __sk_common.{skc_rcv_saddr, ...}
119 currsock.delete(&pid);
120 return 0;
121 }
Brendan Greggf06d3b42015-10-15 17:21:32 -0700122
Alexei Starovoitovbdf07732016-01-14 10:09:20 -0800123 // pull in details
124 struct sock *skp = *skpp;
Paul Chaignoneae0acf2017-08-05 23:04:41 +0200125 u16 dport = skp->__sk_common.skc_dport;
Brendan Greggf06d3b42015-10-15 17:21:32 -0700126
chantra52938052016-09-10 09:44:50 -0700127 FILTER_PORT
128
Brendan Gregg24825522016-02-14 16:32:29 -0800129 if (ipver == 4) {
130 struct ipv4_data_t data4 = {.pid = pid, .ip = ipver};
Takuma Kumeb181a8e2019-01-10 05:49:59 +0900131 data4.uid = bpf_get_current_uid_gid();
Brendan Gregg24825522016-02-14 16:32:29 -0800132 data4.ts_us = bpf_ktime_get_ns() / 1000;
Paul Chaignoneae0acf2017-08-05 23:04:41 +0200133 data4.saddr = skp->__sk_common.skc_rcv_saddr;
134 data4.daddr = skp->__sk_common.skc_daddr;
Brendan Gregg24825522016-02-14 16:32:29 -0800135 data4.dport = ntohs(dport);
136 bpf_get_current_comm(&data4.task, sizeof(data4.task));
137 ipv4_events.perf_submit(ctx, &data4, sizeof(data4));
138
Alexei Starovoitovbdf07732016-01-14 10:09:20 -0800139 } else /* 6 */ {
Brendan Gregg24825522016-02-14 16:32:29 -0800140 struct ipv6_data_t data6 = {.pid = pid, .ip = ipver};
Takuma Kumeb181a8e2019-01-10 05:49:59 +0900141 data6.uid = bpf_get_current_uid_gid();
Brendan Gregg24825522016-02-14 16:32:29 -0800142 data6.ts_us = bpf_ktime_get_ns() / 1000;
Mark Drayton11de2982016-06-26 21:14:44 +0100143 bpf_probe_read(&data6.saddr, sizeof(data6.saddr),
Paul Chaignoneae0acf2017-08-05 23:04:41 +0200144 skp->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32);
Mark Drayton11de2982016-06-26 21:14:44 +0100145 bpf_probe_read(&data6.daddr, sizeof(data6.daddr),
Paul Chaignoneae0acf2017-08-05 23:04:41 +0200146 skp->__sk_common.skc_v6_daddr.in6_u.u6_addr32);
Brendan Gregg24825522016-02-14 16:32:29 -0800147 data6.dport = ntohs(dport);
148 bpf_get_current_comm(&data6.task, sizeof(data6.task));
149 ipv6_events.perf_submit(ctx, &data6, sizeof(data6));
Alexei Starovoitovbdf07732016-01-14 10:09:20 -0800150 }
Brendan Greggf06d3b42015-10-15 17:21:32 -0700151
Alexei Starovoitovbdf07732016-01-14 10:09:20 -0800152 currsock.delete(&pid);
Brendan Greggf06d3b42015-10-15 17:21:32 -0700153
Alexei Starovoitovbdf07732016-01-14 10:09:20 -0800154 return 0;
Brendan Greggf06d3b42015-10-15 17:21:32 -0700155}
156
157int trace_connect_v4_return(struct pt_regs *ctx)
158{
Alexei Starovoitovbdf07732016-01-14 10:09:20 -0800159 return trace_connect_return(ctx, 4);
Brendan Greggf06d3b42015-10-15 17:21:32 -0700160}
161
162int trace_connect_v6_return(struct pt_regs *ctx)
163{
Alexei Starovoitovbdf07732016-01-14 10:09:20 -0800164 return trace_connect_return(ctx, 6);
Brendan Greggf06d3b42015-10-15 17:21:32 -0700165}
166"""
167
168# code substitutions
169if args.pid:
chantra52938052016-09-10 09:44:50 -0700170 bpf_text = bpf_text.replace('FILTER_PID',
Alexei Starovoitovbdf07732016-01-14 10:09:20 -0800171 'if (pid != %s) { return 0; }' % args.pid)
chantra52938052016-09-10 09:44:50 -0700172if args.port:
173 dports = [int(dport) for dport in args.port.split(',')]
174 dports_if = ' && '.join(['dport != %d' % ntohs(dport) for dport in dports])
175 bpf_text = bpf_text.replace('FILTER_PORT',
176 'if (%s) { currsock.delete(&pid); return 0; }' % dports_if)
Takuma Kumeb181a8e2019-01-10 05:49:59 +0900177if args.uid:
178 bpf_text = bpf_text.replace('FILTER_UID',
179 'if (uid != %s) { return 0; }' % args.uid)
chantra52938052016-09-10 09:44:50 -0700180
181bpf_text = bpf_text.replace('FILTER_PID', '')
182bpf_text = bpf_text.replace('FILTER_PORT', '')
Takuma Kumeb181a8e2019-01-10 05:49:59 +0900183bpf_text = bpf_text.replace('FILTER_UID', '')
chantra52938052016-09-10 09:44:50 -0700184
Nathan Scottcf0792f2018-02-02 16:56:50 +1100185if debug or args.ebpf:
Alexei Starovoitovbdf07732016-01-14 10:09:20 -0800186 print(bpf_text)
Nathan Scottcf0792f2018-02-02 16:56:50 +1100187 if args.ebpf:
188 exit()
Brendan Greggf06d3b42015-10-15 17:21:32 -0700189
Brendan Gregg24825522016-02-14 16:32:29 -0800190# event data
191TASK_COMM_LEN = 16 # linux/sched.h
Mark Drayton11de2982016-06-26 21:14:44 +0100192
Brendan Gregg24825522016-02-14 16:32:29 -0800193class Data_ipv4(ct.Structure):
194 _fields_ = [
195 ("ts_us", ct.c_ulonglong),
Joe Yin36ce1122018-08-17 06:04:00 +0800196 ("pid", ct.c_uint),
Takuma Kumeb181a8e2019-01-10 05:49:59 +0900197 ("uid", ct.c_uint),
Joe Yin36ce1122018-08-17 06:04:00 +0800198 ("saddr", ct.c_uint),
199 ("daddr", ct.c_uint),
Mark Drayton11de2982016-06-26 21:14:44 +0100200 ("ip", ct.c_ulonglong),
Joe Yin36ce1122018-08-17 06:04:00 +0800201 ("dport", ct.c_ushort),
Brendan Gregg24825522016-02-14 16:32:29 -0800202 ("task", ct.c_char * TASK_COMM_LEN)
203 ]
Mark Drayton11de2982016-06-26 21:14:44 +0100204
Brendan Gregg24825522016-02-14 16:32:29 -0800205class Data_ipv6(ct.Structure):
206 _fields_ = [
207 ("ts_us", ct.c_ulonglong),
Joe Yin36ce1122018-08-17 06:04:00 +0800208 ("pid", ct.c_uint),
Takuma Kumeb181a8e2019-01-10 05:49:59 +0900209 ("uid", ct.c_uint),
Mark Drayton11de2982016-06-26 21:14:44 +0100210 ("saddr", (ct.c_ulonglong * 2)),
211 ("daddr", (ct.c_ulonglong * 2)),
Brendan Gregg24825522016-02-14 16:32:29 -0800212 ("ip", ct.c_ulonglong),
Joe Yin36ce1122018-08-17 06:04:00 +0800213 ("dport", ct.c_ushort),
Brendan Gregg24825522016-02-14 16:32:29 -0800214 ("task", ct.c_char * TASK_COMM_LEN)
215 ]
216
217# process event
218def print_ipv4_event(cpu, data, size):
219 event = ct.cast(data, ct.POINTER(Data_ipv4)).contents
Mark Drayton11de2982016-06-26 21:14:44 +0100220 global start_ts
Brendan Gregg24825522016-02-14 16:32:29 -0800221 if args.timestamp:
222 if start_ts == 0:
223 start_ts = event.ts_us
Mark Drayton11de2982016-06-26 21:14:44 +0100224 print("%-9.3f" % ((float(event.ts_us) - start_ts) / 1000000), end="")
Takuma Kumeb181a8e2019-01-10 05:49:59 +0900225 if args.print_uid:
226 print("%-6d" % event.uid, end="")
japrocaed9b1e2019-01-04 20:21:46 +0300227 printb(b"%-6d %-12.12s %-2d %-16s %-16s %-4d" % (event.pid,
Yonghong Songebe19512019-01-10 14:54:16 -0800228 event.task, event.ip,
229 inet_ntop(AF_INET, pack("I", event.saddr)).encode(),
230 inet_ntop(AF_INET, pack("I", event.daddr)).encode(), event.dport))
Brendan Gregg9e0b0872016-03-28 12:11:45 -0700231
Brendan Gregg24825522016-02-14 16:32:29 -0800232def print_ipv6_event(cpu, data, size):
233 event = ct.cast(data, ct.POINTER(Data_ipv6)).contents
Mark Drayton11de2982016-06-26 21:14:44 +0100234 global start_ts
Brendan Gregg24825522016-02-14 16:32:29 -0800235 if args.timestamp:
236 if start_ts == 0:
237 start_ts = event.ts_us
Mark Drayton11de2982016-06-26 21:14:44 +0100238 print("%-9.3f" % ((float(event.ts_us) - start_ts) / 1000000), end="")
Takuma Kumeb181a8e2019-01-10 05:49:59 +0900239 if args.print_uid:
240 print("%-6d" % event.uid, end="")
japrocaed9b1e2019-01-04 20:21:46 +0300241 printb(b"%-6d %-12.12s %-2d %-16s %-16s %-4d" % (event.pid,
Yonghong Songebe19512019-01-10 14:54:16 -0800242 event.task, event.ip,
243 inet_ntop(AF_INET6, event.saddr).encode(), inet_ntop(AF_INET6, event.daddr).encode(),
jeromemarchandb96ebcd2018-10-10 01:58:15 +0200244 event.dport))
Brendan Gregg24825522016-02-14 16:32:29 -0800245
Brendan Greggf06d3b42015-10-15 17:21:32 -0700246# initialize BPF
247b = BPF(text=bpf_text)
248b.attach_kprobe(event="tcp_v4_connect", fn_name="trace_connect_entry")
249b.attach_kprobe(event="tcp_v6_connect", fn_name="trace_connect_entry")
250b.attach_kretprobe(event="tcp_v4_connect", fn_name="trace_connect_v4_return")
251b.attach_kretprobe(event="tcp_v6_connect", fn_name="trace_connect_v6_return")
252
253# header
254if args.timestamp:
Alexei Starovoitovbdf07732016-01-14 10:09:20 -0800255 print("%-9s" % ("TIME(s)"), end="")
Takuma Kumeb181a8e2019-01-10 05:49:59 +0900256if args.print_uid:
257 print("%-6s" % ("UID"), end="")
Brendan Greggf06d3b42015-10-15 17:21:32 -0700258print("%-6s %-12s %-2s %-16s %-16s %-4s" % ("PID", "COMM", "IP", "SADDR",
259 "DADDR", "DPORT"))
260
261start_ts = 0
262
Brendan Gregg24825522016-02-14 16:32:29 -0800263# read events
264b["ipv4_events"].open_perf_buffer(print_ipv4_event)
265b["ipv6_events"].open_perf_buffer(print_ipv6_event)
Brendan Greggf06d3b42015-10-15 17:21:32 -0700266while 1:
Jerome Marchand51671272018-12-19 01:57:24 +0100267 try:
268 b.perf_buffer_poll()
269 except KeyboardInterrupt:
270 exit()