blob: 6779adfbac5f1cfe43562f8baeac587a2f03c987 [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;
Paul Chaignoneae0acf2017-08-05 23:04:41 +0200111 u16 dport = skp->__sk_common.skc_dport;
Brendan Greggf06d3b42015-10-15 17:21:32 -0700112
chantra52938052016-09-10 09:44:50 -0700113 FILTER_PORT
114
Brendan Gregg24825522016-02-14 16:32:29 -0800115 if (ipver == 4) {
116 struct ipv4_data_t data4 = {.pid = pid, .ip = ipver};
117 data4.ts_us = bpf_ktime_get_ns() / 1000;
Paul Chaignoneae0acf2017-08-05 23:04:41 +0200118 data4.saddr = skp->__sk_common.skc_rcv_saddr;
119 data4.daddr = skp->__sk_common.skc_daddr;
Brendan Gregg24825522016-02-14 16:32:29 -0800120 data4.dport = ntohs(dport);
121 bpf_get_current_comm(&data4.task, sizeof(data4.task));
122 ipv4_events.perf_submit(ctx, &data4, sizeof(data4));
123
Alexei Starovoitovbdf07732016-01-14 10:09:20 -0800124 } else /* 6 */ {
Brendan Gregg24825522016-02-14 16:32:29 -0800125 struct ipv6_data_t data6 = {.pid = pid, .ip = ipver};
126 data6.ts_us = bpf_ktime_get_ns() / 1000;
Mark Drayton11de2982016-06-26 21:14:44 +0100127 bpf_probe_read(&data6.saddr, sizeof(data6.saddr),
Paul Chaignoneae0acf2017-08-05 23:04:41 +0200128 skp->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32);
Mark Drayton11de2982016-06-26 21:14:44 +0100129 bpf_probe_read(&data6.daddr, sizeof(data6.daddr),
Paul Chaignoneae0acf2017-08-05 23:04:41 +0200130 skp->__sk_common.skc_v6_daddr.in6_u.u6_addr32);
Brendan Gregg24825522016-02-14 16:32:29 -0800131 data6.dport = ntohs(dport);
132 bpf_get_current_comm(&data6.task, sizeof(data6.task));
133 ipv6_events.perf_submit(ctx, &data6, sizeof(data6));
Alexei Starovoitovbdf07732016-01-14 10:09:20 -0800134 }
Brendan Greggf06d3b42015-10-15 17:21:32 -0700135
Alexei Starovoitovbdf07732016-01-14 10:09:20 -0800136 currsock.delete(&pid);
Brendan Greggf06d3b42015-10-15 17:21:32 -0700137
Alexei Starovoitovbdf07732016-01-14 10:09:20 -0800138 return 0;
Brendan Greggf06d3b42015-10-15 17:21:32 -0700139}
140
141int trace_connect_v4_return(struct pt_regs *ctx)
142{
Alexei Starovoitovbdf07732016-01-14 10:09:20 -0800143 return trace_connect_return(ctx, 4);
Brendan Greggf06d3b42015-10-15 17:21:32 -0700144}
145
146int trace_connect_v6_return(struct pt_regs *ctx)
147{
Alexei Starovoitovbdf07732016-01-14 10:09:20 -0800148 return trace_connect_return(ctx, 6);
Brendan Greggf06d3b42015-10-15 17:21:32 -0700149}
150"""
151
152# code substitutions
153if args.pid:
chantra52938052016-09-10 09:44:50 -0700154 bpf_text = bpf_text.replace('FILTER_PID',
Alexei Starovoitovbdf07732016-01-14 10:09:20 -0800155 'if (pid != %s) { return 0; }' % args.pid)
chantra52938052016-09-10 09:44:50 -0700156if args.port:
157 dports = [int(dport) for dport in args.port.split(',')]
158 dports_if = ' && '.join(['dport != %d' % ntohs(dport) for dport in dports])
159 bpf_text = bpf_text.replace('FILTER_PORT',
160 'if (%s) { currsock.delete(&pid); return 0; }' % dports_if)
161
162bpf_text = bpf_text.replace('FILTER_PID', '')
163bpf_text = bpf_text.replace('FILTER_PORT', '')
164
Brendan Greggf06d3b42015-10-15 17:21:32 -0700165if debug:
Alexei Starovoitovbdf07732016-01-14 10:09:20 -0800166 print(bpf_text)
Brendan Greggf06d3b42015-10-15 17:21:32 -0700167
Brendan Gregg24825522016-02-14 16:32:29 -0800168# event data
169TASK_COMM_LEN = 16 # linux/sched.h
Mark Drayton11de2982016-06-26 21:14:44 +0100170
Brendan Gregg24825522016-02-14 16:32:29 -0800171class Data_ipv4(ct.Structure):
172 _fields_ = [
173 ("ts_us", ct.c_ulonglong),
174 ("pid", ct.c_ulonglong),
Brendan Gregg24825522016-02-14 16:32:29 -0800175 ("saddr", ct.c_ulonglong),
176 ("daddr", ct.c_ulonglong),
Mark Drayton11de2982016-06-26 21:14:44 +0100177 ("ip", ct.c_ulonglong),
Brendan Gregg24825522016-02-14 16:32:29 -0800178 ("dport", ct.c_ulonglong),
179 ("task", ct.c_char * TASK_COMM_LEN)
180 ]
Mark Drayton11de2982016-06-26 21:14:44 +0100181
Brendan Gregg24825522016-02-14 16:32:29 -0800182class Data_ipv6(ct.Structure):
183 _fields_ = [
184 ("ts_us", ct.c_ulonglong),
185 ("pid", ct.c_ulonglong),
Mark Drayton11de2982016-06-26 21:14:44 +0100186 ("saddr", (ct.c_ulonglong * 2)),
187 ("daddr", (ct.c_ulonglong * 2)),
Brendan Gregg24825522016-02-14 16:32:29 -0800188 ("ip", ct.c_ulonglong),
Brendan Gregg24825522016-02-14 16:32:29 -0800189 ("dport", ct.c_ulonglong),
190 ("task", ct.c_char * TASK_COMM_LEN)
191 ]
192
193# process event
194def print_ipv4_event(cpu, data, size):
195 event = ct.cast(data, ct.POINTER(Data_ipv4)).contents
Mark Drayton11de2982016-06-26 21:14:44 +0100196 global start_ts
Brendan Gregg24825522016-02-14 16:32:29 -0800197 if args.timestamp:
198 if start_ts == 0:
199 start_ts = event.ts_us
Mark Drayton11de2982016-06-26 21:14:44 +0100200 print("%-9.3f" % ((float(event.ts_us) - start_ts) / 1000000), end="")
Rafael F78948e42017-03-26 14:54:25 +0200201 print("%-6d %-12.12s %-2d %-16s %-16s %-4d" % (event.pid,
202 event.task.decode(), event.ip,
203 inet_ntop(AF_INET, pack("I", event.saddr)),
Mark Drayton11de2982016-06-26 21:14:44 +0100204 inet_ntop(AF_INET, pack("I", event.daddr)), event.dport))
Brendan Gregg9e0b0872016-03-28 12:11:45 -0700205
Brendan Gregg24825522016-02-14 16:32:29 -0800206def print_ipv6_event(cpu, data, size):
207 event = ct.cast(data, ct.POINTER(Data_ipv6)).contents
Mark Drayton11de2982016-06-26 21:14:44 +0100208 global start_ts
Brendan Gregg24825522016-02-14 16:32:29 -0800209 if args.timestamp:
210 if start_ts == 0:
211 start_ts = event.ts_us
Mark Drayton11de2982016-06-26 21:14:44 +0100212 print("%-9.3f" % ((float(event.ts_us) - start_ts) / 1000000), end="")
Brendan Gregg9e0b0872016-03-28 12:11:45 -0700213 print("%-6d %-12.12s %-2d %-16s %-16s %-4d" % (event.pid,
Rafael F78948e42017-03-26 14:54:25 +0200214 event.task.decode(), event.ip, inet_ntop(AF_INET6, event.saddr),
Mark Drayton11de2982016-06-26 21:14:44 +0100215 inet_ntop(AF_INET6, event.daddr), event.dport))
Brendan Gregg24825522016-02-14 16:32:29 -0800216
Brendan Greggf06d3b42015-10-15 17:21:32 -0700217# initialize BPF
218b = BPF(text=bpf_text)
219b.attach_kprobe(event="tcp_v4_connect", fn_name="trace_connect_entry")
220b.attach_kprobe(event="tcp_v6_connect", fn_name="trace_connect_entry")
221b.attach_kretprobe(event="tcp_v4_connect", fn_name="trace_connect_v4_return")
222b.attach_kretprobe(event="tcp_v6_connect", fn_name="trace_connect_v6_return")
223
224# header
225if args.timestamp:
Alexei Starovoitovbdf07732016-01-14 10:09:20 -0800226 print("%-9s" % ("TIME(s)"), end="")
Brendan Greggf06d3b42015-10-15 17:21:32 -0700227print("%-6s %-12s %-2s %-16s %-16s %-4s" % ("PID", "COMM", "IP", "SADDR",
228 "DADDR", "DPORT"))
229
230start_ts = 0
231
Brendan Gregg24825522016-02-14 16:32:29 -0800232# read events
233b["ipv4_events"].open_perf_buffer(print_ipv4_event)
234b["ipv6_events"].open_perf_buffer(print_ipv6_event)
Brendan Greggf06d3b42015-10-15 17:21:32 -0700235while 1:
Brendan Gregg24825522016-02-14 16:32:29 -0800236 b.kprobe_poll()