1*387f9dfdSAndroid Build Coastguard Worker // SPDX-License-Identifier: GPL-2.0
2*387f9dfdSAndroid Build Coastguard Worker
3*387f9dfdSAndroid Build Coastguard Worker /*
4*387f9dfdSAndroid Build Coastguard Worker * tcplife Trace the lifespan of TCP sessions and summarize.
5*387f9dfdSAndroid Build Coastguard Worker *
6*387f9dfdSAndroid Build Coastguard Worker * Copyright (c) 2022 Hengqi Chen
7*387f9dfdSAndroid Build Coastguard Worker *
8*387f9dfdSAndroid Build Coastguard Worker * Based on tcplife(8) from BCC by Brendan Gregg.
9*387f9dfdSAndroid Build Coastguard Worker * 02-Jun-2022 Hengqi Chen Created this.
10*387f9dfdSAndroid Build Coastguard Worker */
11*387f9dfdSAndroid Build Coastguard Worker #include <argp.h>
12*387f9dfdSAndroid Build Coastguard Worker #include <errno.h>
13*387f9dfdSAndroid Build Coastguard Worker #include <signal.h>
14*387f9dfdSAndroid Build Coastguard Worker #include <time.h>
15*387f9dfdSAndroid Build Coastguard Worker #include <arpa/inet.h>
16*387f9dfdSAndroid Build Coastguard Worker #include <sys/socket.h>
17*387f9dfdSAndroid Build Coastguard Worker
18*387f9dfdSAndroid Build Coastguard Worker #include "btf_helpers.h"
19*387f9dfdSAndroid Build Coastguard Worker #include "tcplife.h"
20*387f9dfdSAndroid Build Coastguard Worker #include "tcplife.skel.h"
21*387f9dfdSAndroid Build Coastguard Worker
22*387f9dfdSAndroid Build Coastguard Worker #define PERF_BUFFER_PAGES 16
23*387f9dfdSAndroid Build Coastguard Worker #define PERF_POLL_TIMEOUT_MS 100
24*387f9dfdSAndroid Build Coastguard Worker
25*387f9dfdSAndroid Build Coastguard Worker static volatile sig_atomic_t exiting = 0;
26*387f9dfdSAndroid Build Coastguard Worker
27*387f9dfdSAndroid Build Coastguard Worker static pid_t target_pid = 0;
28*387f9dfdSAndroid Build Coastguard Worker static short target_family = 0;
29*387f9dfdSAndroid Build Coastguard Worker static char *target_sports = NULL;
30*387f9dfdSAndroid Build Coastguard Worker static char *target_dports = NULL;
31*387f9dfdSAndroid Build Coastguard Worker static int column_width = 15;
32*387f9dfdSAndroid Build Coastguard Worker static bool emit_timestamp = false;
33*387f9dfdSAndroid Build Coastguard Worker static bool verbose = false;
34*387f9dfdSAndroid Build Coastguard Worker
35*387f9dfdSAndroid Build Coastguard Worker const char *argp_program_version = "tcplife 0.1";
36*387f9dfdSAndroid Build Coastguard Worker const char *argp_program_bug_address =
37*387f9dfdSAndroid Build Coastguard Worker "https://github.com/iovisor/bcc/tree/master/libbpf-tools";
38*387f9dfdSAndroid Build Coastguard Worker const char argp_program_doc[] =
39*387f9dfdSAndroid Build Coastguard Worker "Trace the lifespan of TCP sessions and summarize.\n"
40*387f9dfdSAndroid Build Coastguard Worker "\n"
41*387f9dfdSAndroid Build Coastguard Worker "USAGE: tcplife [-h] [-p PID] [-4] [-6] [-L] [-D] [-T] [-w]\n"
42*387f9dfdSAndroid Build Coastguard Worker "\n"
43*387f9dfdSAndroid Build Coastguard Worker "EXAMPLES:\n"
44*387f9dfdSAndroid Build Coastguard Worker " tcplife -p 1215 # only trace PID 1215\n"
45*387f9dfdSAndroid Build Coastguard Worker " tcplife -p 1215 -4 # trace IPv4 only\n";
46*387f9dfdSAndroid Build Coastguard Worker
47*387f9dfdSAndroid Build Coastguard Worker static const struct argp_option opts[] = {
48*387f9dfdSAndroid Build Coastguard Worker { "pid", 'p', "PID", 0, "Process ID to trace" },
49*387f9dfdSAndroid Build Coastguard Worker { "ipv4", '4', NULL, 0, "Trace IPv4 only" },
50*387f9dfdSAndroid Build Coastguard Worker { "ipv6", '6', NULL, 0, "Trace IPv6 only" },
51*387f9dfdSAndroid Build Coastguard Worker { "wide", 'w', NULL, 0, "Wide column output (fits IPv6 addresses)" },
52*387f9dfdSAndroid Build Coastguard Worker { "time", 'T', NULL, 0, "Include timestamp on output" },
53*387f9dfdSAndroid Build Coastguard Worker { "localport", 'L', "LOCALPORT", 0, "Comma-separated list of local ports to trace." },
54*387f9dfdSAndroid Build Coastguard Worker { "remoteport", 'D', "REMOTEPORT", 0, "Comma-separated list of remote ports to trace." },
55*387f9dfdSAndroid Build Coastguard Worker { "verbose", 'v', NULL, 0, "Verbose debug output" },
56*387f9dfdSAndroid Build Coastguard Worker { NULL, 'h', NULL, OPTION_HIDDEN, "Show the full help" },
57*387f9dfdSAndroid Build Coastguard Worker {},
58*387f9dfdSAndroid Build Coastguard Worker };
59*387f9dfdSAndroid Build Coastguard Worker
parse_arg(int key,char * arg,struct argp_state * state)60*387f9dfdSAndroid Build Coastguard Worker static error_t parse_arg(int key, char *arg, struct argp_state *state)
61*387f9dfdSAndroid Build Coastguard Worker {
62*387f9dfdSAndroid Build Coastguard Worker long n;
63*387f9dfdSAndroid Build Coastguard Worker
64*387f9dfdSAndroid Build Coastguard Worker switch (key) {
65*387f9dfdSAndroid Build Coastguard Worker case 'p':
66*387f9dfdSAndroid Build Coastguard Worker errno = 0;
67*387f9dfdSAndroid Build Coastguard Worker n = strtol(arg, NULL, 10);
68*387f9dfdSAndroid Build Coastguard Worker if (errno || n <= 0) {
69*387f9dfdSAndroid Build Coastguard Worker fprintf(stderr, "Invalid PID: %s\n", arg);
70*387f9dfdSAndroid Build Coastguard Worker argp_usage(state);
71*387f9dfdSAndroid Build Coastguard Worker }
72*387f9dfdSAndroid Build Coastguard Worker target_pid = n;
73*387f9dfdSAndroid Build Coastguard Worker break;
74*387f9dfdSAndroid Build Coastguard Worker case '4':
75*387f9dfdSAndroid Build Coastguard Worker target_family = AF_INET;
76*387f9dfdSAndroid Build Coastguard Worker break;
77*387f9dfdSAndroid Build Coastguard Worker case '6':
78*387f9dfdSAndroid Build Coastguard Worker target_family = AF_INET6;
79*387f9dfdSAndroid Build Coastguard Worker break;
80*387f9dfdSAndroid Build Coastguard Worker case 'w':
81*387f9dfdSAndroid Build Coastguard Worker column_width = 39;
82*387f9dfdSAndroid Build Coastguard Worker break;
83*387f9dfdSAndroid Build Coastguard Worker case 'L':
84*387f9dfdSAndroid Build Coastguard Worker target_sports = strdup(arg);
85*387f9dfdSAndroid Build Coastguard Worker break;
86*387f9dfdSAndroid Build Coastguard Worker case 'D':
87*387f9dfdSAndroid Build Coastguard Worker target_dports = strdup(arg);
88*387f9dfdSAndroid Build Coastguard Worker break;
89*387f9dfdSAndroid Build Coastguard Worker case 'T':
90*387f9dfdSAndroid Build Coastguard Worker emit_timestamp = true;
91*387f9dfdSAndroid Build Coastguard Worker break;
92*387f9dfdSAndroid Build Coastguard Worker case 'v':
93*387f9dfdSAndroid Build Coastguard Worker verbose = true;
94*387f9dfdSAndroid Build Coastguard Worker break;
95*387f9dfdSAndroid Build Coastguard Worker case 'h':
96*387f9dfdSAndroid Build Coastguard Worker argp_state_help(state, stderr, ARGP_HELP_STD_HELP);
97*387f9dfdSAndroid Build Coastguard Worker break;
98*387f9dfdSAndroid Build Coastguard Worker default:
99*387f9dfdSAndroid Build Coastguard Worker return ARGP_ERR_UNKNOWN;
100*387f9dfdSAndroid Build Coastguard Worker }
101*387f9dfdSAndroid Build Coastguard Worker return 0;
102*387f9dfdSAndroid Build Coastguard Worker }
103*387f9dfdSAndroid Build Coastguard Worker
libbpf_print_fn(enum libbpf_print_level level,const char * format,va_list args)104*387f9dfdSAndroid Build Coastguard Worker static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args)
105*387f9dfdSAndroid Build Coastguard Worker {
106*387f9dfdSAndroid Build Coastguard Worker if (level == LIBBPF_DEBUG && !verbose)
107*387f9dfdSAndroid Build Coastguard Worker return 0;
108*387f9dfdSAndroid Build Coastguard Worker return vfprintf(stderr, format, args);
109*387f9dfdSAndroid Build Coastguard Worker }
110*387f9dfdSAndroid Build Coastguard Worker
sig_int(int signo)111*387f9dfdSAndroid Build Coastguard Worker static void sig_int(int signo)
112*387f9dfdSAndroid Build Coastguard Worker {
113*387f9dfdSAndroid Build Coastguard Worker exiting = 1;
114*387f9dfdSAndroid Build Coastguard Worker }
115*387f9dfdSAndroid Build Coastguard Worker
handle_event(void * ctx,int cpu,void * data,__u32 data_sz)116*387f9dfdSAndroid Build Coastguard Worker static void handle_event(void *ctx, int cpu, void *data, __u32 data_sz)
117*387f9dfdSAndroid Build Coastguard Worker {
118*387f9dfdSAndroid Build Coastguard Worker char ts[32], saddr[48], daddr[48];
119*387f9dfdSAndroid Build Coastguard Worker struct event *e = data;
120*387f9dfdSAndroid Build Coastguard Worker struct tm *tm;
121*387f9dfdSAndroid Build Coastguard Worker time_t t;
122*387f9dfdSAndroid Build Coastguard Worker
123*387f9dfdSAndroid Build Coastguard Worker if (emit_timestamp) {
124*387f9dfdSAndroid Build Coastguard Worker time(&t);
125*387f9dfdSAndroid Build Coastguard Worker tm = localtime(&t);
126*387f9dfdSAndroid Build Coastguard Worker strftime(ts, sizeof(ts), "%H:%M:%S", tm);
127*387f9dfdSAndroid Build Coastguard Worker printf("%8s ", ts);
128*387f9dfdSAndroid Build Coastguard Worker }
129*387f9dfdSAndroid Build Coastguard Worker
130*387f9dfdSAndroid Build Coastguard Worker inet_ntop(e->family, &e->saddr, saddr, sizeof(saddr));
131*387f9dfdSAndroid Build Coastguard Worker inet_ntop(e->family, &e->daddr, daddr, sizeof(daddr));
132*387f9dfdSAndroid Build Coastguard Worker
133*387f9dfdSAndroid Build Coastguard Worker printf("%-7d %-16s %-*s %-5d %-*s %-5d %-6.2f %-6.2f %-.2f\n",
134*387f9dfdSAndroid Build Coastguard Worker e->pid, e->comm, column_width, saddr, e->sport, column_width, daddr, e->dport,
135*387f9dfdSAndroid Build Coastguard Worker (double)e->tx_b / 1024, (double)e->rx_b / 1024, (double)e->span_us / 1000);
136*387f9dfdSAndroid Build Coastguard Worker }
137*387f9dfdSAndroid Build Coastguard Worker
handle_lost_events(void * ctx,int cpu,__u64 lost_cnt)138*387f9dfdSAndroid Build Coastguard Worker static void handle_lost_events(void *ctx, int cpu, __u64 lost_cnt)
139*387f9dfdSAndroid Build Coastguard Worker {
140*387f9dfdSAndroid Build Coastguard Worker fprintf(stderr, "lost %llu events on CPU #%d\n", lost_cnt, cpu);
141*387f9dfdSAndroid Build Coastguard Worker }
142*387f9dfdSAndroid Build Coastguard Worker
main(int argc,char ** argv)143*387f9dfdSAndroid Build Coastguard Worker int main(int argc, char **argv)
144*387f9dfdSAndroid Build Coastguard Worker {
145*387f9dfdSAndroid Build Coastguard Worker LIBBPF_OPTS(bpf_object_open_opts, open_opts);
146*387f9dfdSAndroid Build Coastguard Worker static const struct argp argp = {
147*387f9dfdSAndroid Build Coastguard Worker .options = opts,
148*387f9dfdSAndroid Build Coastguard Worker .parser = parse_arg,
149*387f9dfdSAndroid Build Coastguard Worker .doc = argp_program_doc,
150*387f9dfdSAndroid Build Coastguard Worker };
151*387f9dfdSAndroid Build Coastguard Worker struct tcplife_bpf *obj;
152*387f9dfdSAndroid Build Coastguard Worker struct perf_buffer *pb = NULL;
153*387f9dfdSAndroid Build Coastguard Worker short port_num;
154*387f9dfdSAndroid Build Coastguard Worker char *port;
155*387f9dfdSAndroid Build Coastguard Worker int err, i;
156*387f9dfdSAndroid Build Coastguard Worker
157*387f9dfdSAndroid Build Coastguard Worker err = argp_parse(&argp, argc, argv, 0, NULL, NULL);
158*387f9dfdSAndroid Build Coastguard Worker if (err)
159*387f9dfdSAndroid Build Coastguard Worker return err;
160*387f9dfdSAndroid Build Coastguard Worker
161*387f9dfdSAndroid Build Coastguard Worker libbpf_set_print(libbpf_print_fn);
162*387f9dfdSAndroid Build Coastguard Worker
163*387f9dfdSAndroid Build Coastguard Worker err = ensure_core_btf(&open_opts);
164*387f9dfdSAndroid Build Coastguard Worker if (err) {
165*387f9dfdSAndroid Build Coastguard Worker fprintf(stderr, "failed to fetch necessary BTF for CO-RE: %s\n", strerror(-err));
166*387f9dfdSAndroid Build Coastguard Worker return 1;
167*387f9dfdSAndroid Build Coastguard Worker }
168*387f9dfdSAndroid Build Coastguard Worker
169*387f9dfdSAndroid Build Coastguard Worker obj = tcplife_bpf__open_opts(&open_opts);
170*387f9dfdSAndroid Build Coastguard Worker if (!obj) {
171*387f9dfdSAndroid Build Coastguard Worker fprintf(stderr, "failed to open BPF object\n");
172*387f9dfdSAndroid Build Coastguard Worker return 1;
173*387f9dfdSAndroid Build Coastguard Worker }
174*387f9dfdSAndroid Build Coastguard Worker
175*387f9dfdSAndroid Build Coastguard Worker obj->rodata->target_pid = target_pid;
176*387f9dfdSAndroid Build Coastguard Worker obj->rodata->target_family = target_family;
177*387f9dfdSAndroid Build Coastguard Worker
178*387f9dfdSAndroid Build Coastguard Worker if (target_sports) {
179*387f9dfdSAndroid Build Coastguard Worker i = 0;
180*387f9dfdSAndroid Build Coastguard Worker port = strtok(target_sports, ",");
181*387f9dfdSAndroid Build Coastguard Worker while (port && i < MAX_PORTS) {
182*387f9dfdSAndroid Build Coastguard Worker port_num = strtol(port, NULL, 10);
183*387f9dfdSAndroid Build Coastguard Worker obj->rodata->target_sports[i++] = port_num;
184*387f9dfdSAndroid Build Coastguard Worker port = strtok(NULL, ",");
185*387f9dfdSAndroid Build Coastguard Worker }
186*387f9dfdSAndroid Build Coastguard Worker obj->rodata->filter_sport = true;
187*387f9dfdSAndroid Build Coastguard Worker }
188*387f9dfdSAndroid Build Coastguard Worker
189*387f9dfdSAndroid Build Coastguard Worker if (target_dports) {
190*387f9dfdSAndroid Build Coastguard Worker i = 0;
191*387f9dfdSAndroid Build Coastguard Worker port = strtok(target_dports, ",");
192*387f9dfdSAndroid Build Coastguard Worker while (port && i < MAX_PORTS) {
193*387f9dfdSAndroid Build Coastguard Worker port_num = strtol(port, NULL, 10);
194*387f9dfdSAndroid Build Coastguard Worker obj->rodata->target_dports[i++] = port_num;
195*387f9dfdSAndroid Build Coastguard Worker port = strtok(NULL, ",");
196*387f9dfdSAndroid Build Coastguard Worker }
197*387f9dfdSAndroid Build Coastguard Worker obj->rodata->filter_dport = true;
198*387f9dfdSAndroid Build Coastguard Worker }
199*387f9dfdSAndroid Build Coastguard Worker
200*387f9dfdSAndroid Build Coastguard Worker err = tcplife_bpf__load(obj);
201*387f9dfdSAndroid Build Coastguard Worker if (err) {
202*387f9dfdSAndroid Build Coastguard Worker fprintf(stderr, "failed to load BPF object: %d\n", err);
203*387f9dfdSAndroid Build Coastguard Worker goto cleanup;
204*387f9dfdSAndroid Build Coastguard Worker }
205*387f9dfdSAndroid Build Coastguard Worker
206*387f9dfdSAndroid Build Coastguard Worker err = tcplife_bpf__attach(obj);
207*387f9dfdSAndroid Build Coastguard Worker if (err) {
208*387f9dfdSAndroid Build Coastguard Worker fprintf(stderr, "failed to attach BPF object: %d\n", err);
209*387f9dfdSAndroid Build Coastguard Worker goto cleanup;
210*387f9dfdSAndroid Build Coastguard Worker }
211*387f9dfdSAndroid Build Coastguard Worker
212*387f9dfdSAndroid Build Coastguard Worker pb = perf_buffer__new(bpf_map__fd(obj->maps.events), PERF_BUFFER_PAGES,
213*387f9dfdSAndroid Build Coastguard Worker handle_event, handle_lost_events, NULL, NULL);
214*387f9dfdSAndroid Build Coastguard Worker if (!pb) {
215*387f9dfdSAndroid Build Coastguard Worker err = -errno;
216*387f9dfdSAndroid Build Coastguard Worker fprintf(stderr, "failed to open perf buffer: %d\n", err);
217*387f9dfdSAndroid Build Coastguard Worker goto cleanup;
218*387f9dfdSAndroid Build Coastguard Worker }
219*387f9dfdSAndroid Build Coastguard Worker
220*387f9dfdSAndroid Build Coastguard Worker if (signal(SIGINT, sig_int) == SIG_ERR) {
221*387f9dfdSAndroid Build Coastguard Worker fprintf(stderr, "can't set signal handler: %s\n", strerror(errno));
222*387f9dfdSAndroid Build Coastguard Worker err = 1;
223*387f9dfdSAndroid Build Coastguard Worker goto cleanup;
224*387f9dfdSAndroid Build Coastguard Worker }
225*387f9dfdSAndroid Build Coastguard Worker
226*387f9dfdSAndroid Build Coastguard Worker if (emit_timestamp)
227*387f9dfdSAndroid Build Coastguard Worker printf("%-8s ", "TIME(s)");
228*387f9dfdSAndroid Build Coastguard Worker printf("%-7s %-16s %-*s %-5s %-*s %-5s %-6s %-6s %-s\n",
229*387f9dfdSAndroid Build Coastguard Worker "PID", "COMM", column_width, "LADDR", "LPORT", column_width, "RADDR", "RPORT",
230*387f9dfdSAndroid Build Coastguard Worker "TX_KB", "RX_KB", "MS");
231*387f9dfdSAndroid Build Coastguard Worker
232*387f9dfdSAndroid Build Coastguard Worker while (!exiting) {
233*387f9dfdSAndroid Build Coastguard Worker err = perf_buffer__poll(pb, PERF_POLL_TIMEOUT_MS);
234*387f9dfdSAndroid Build Coastguard Worker if (err < 0 && err != -EINTR) {
235*387f9dfdSAndroid Build Coastguard Worker fprintf(stderr, "error polling perf buffer: %s\n", strerror(-err));
236*387f9dfdSAndroid Build Coastguard Worker goto cleanup;
237*387f9dfdSAndroid Build Coastguard Worker }
238*387f9dfdSAndroid Build Coastguard Worker /* reset err to return 0 if exiting */
239*387f9dfdSAndroid Build Coastguard Worker err = 0;
240*387f9dfdSAndroid Build Coastguard Worker }
241*387f9dfdSAndroid Build Coastguard Worker
242*387f9dfdSAndroid Build Coastguard Worker cleanup:
243*387f9dfdSAndroid Build Coastguard Worker perf_buffer__free(pb);
244*387f9dfdSAndroid Build Coastguard Worker tcplife_bpf__destroy(obj);
245*387f9dfdSAndroid Build Coastguard Worker cleanup_core_btf(&open_opts);
246*387f9dfdSAndroid Build Coastguard Worker return err != 0;
247*387f9dfdSAndroid Build Coastguard Worker }
248