blob: 01de7041c02ff895b34eaa4c585a2f9f4fb18af3 [file] [log] [blame]
Brendan Gregg553f2aa2016-02-14 18:15:24 -08001#!/usr/bin/python
2# @lint-avoid-python-3-compatibility-imports
3#
4# tcpretrans Trace TCP retransmits and TLPs.
5# For Linux, uses BCC, eBPF. Embedded C.
6#
7# USAGE: tcpretrans [-h] [-l]
8#
9# This uses dynamic tracing of kernel functions, and will need to be updated
10# to match kernel changes.
11#
Brendan Gregg553f2aa2016-02-14 18:15:24 -080012# Copyright 2016 Netflix, Inc.
13# Licensed under the Apache License, Version 2.0 (the "License")
14#
15# 14-Feb-2016 Brendan Gregg Created this.
16
17from __future__ import print_function
18from bcc import BPF
19import argparse
20from time import strftime
Mark Drayton11de2982016-06-26 21:14:44 +010021from socket import inet_ntop, AF_INET, AF_INET6
22from struct import pack
Brendan Gregg553f2aa2016-02-14 18:15:24 -080023import ctypes as ct
24
25# arguments
26examples = """examples:
27 ./tcpretrans # trace TCP retransmits
28 ./tcpretrans -l # include TLP attempts
29"""
30parser = argparse.ArgumentParser(
31 description="Trace TCP retransmits",
32 formatter_class=argparse.RawDescriptionHelpFormatter,
33 epilog=examples)
34parser.add_argument("-l", "--lossprobe", action="store_true",
35 help="include tail loss probe attempts")
36args = parser.parse_args()
37
38# define BPF program
39bpf_text = """
40#include <uapi/linux/ptrace.h>
41#include <net/sock.h>
42#include <bcc/proto.h>
43
44#define RETRANSMIT 1
45#define TLP 2
46
47// separate data structs for ipv4 and ipv6
48struct ipv4_data_t {
49 // XXX: switch some to u32's when supported
50 u64 pid;
51 u64 ip;
52 u64 saddr;
53 u64 daddr;
54 u64 lport;
55 u64 dport;
56 u64 state;
57 u64 type;
58};
59BPF_PERF_OUTPUT(ipv4_events);
60
61struct ipv6_data_t {
Brendan Gregg553f2aa2016-02-14 18:15:24 -080062 u64 pid;
63 u64 ip;
Mark Drayton11de2982016-06-26 21:14:44 +010064 unsigned __int128 saddr;
65 unsigned __int128 daddr;
Brendan Gregg553f2aa2016-02-14 18:15:24 -080066 u64 lport;
67 u64 dport;
68 u64 state;
69 u64 type;
70};
71BPF_PERF_OUTPUT(ipv6_events);
72
73static int trace_event(struct pt_regs *ctx, struct sock *sk, int type)
74{
75 if (sk == NULL)
76 return 0;
77 u32 pid = bpf_get_current_pid_tgid();
78 struct sock *skp = NULL;
79 bpf_probe_read(&skp, sizeof(skp), &sk);
80
81 // pull in details
82 u16 family = 0, lport = 0, dport = 0;
83 char state = 0;
84 bpf_probe_read(&family, sizeof(family), &skp->__sk_common.skc_family);
85 bpf_probe_read(&lport, sizeof(lport), &skp->__sk_common.skc_num);
86 bpf_probe_read(&dport, sizeof(dport), &skp->__sk_common.skc_dport);
87 bpf_probe_read(&state, sizeof(state), (void *)&skp->__sk_common.skc_state);
88
89 if (family == AF_INET) {
90 struct ipv4_data_t data4 = {.pid = pid, .ip = 4, .type = type};
91 bpf_probe_read(&data4.saddr, sizeof(u32),
92 &skp->__sk_common.skc_rcv_saddr);
93 bpf_probe_read(&data4.daddr, sizeof(u32),
94 &skp->__sk_common.skc_daddr);
95 data4.lport = lport;
96 data4.dport = dport;
97 data4.state = state;
98 ipv4_events.perf_submit(ctx, &data4, sizeof(data4));
99
100 } else if (family == AF_INET6) {
101 struct ipv6_data_t data6 = {.pid = pid, .ip = 6, .type = type};
Mark Drayton11de2982016-06-26 21:14:44 +0100102 bpf_probe_read(&data6.saddr, sizeof(data6.saddr),
103 &skp->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32);
104 bpf_probe_read(&data6.daddr, sizeof(data6.daddr),
105 &skp->__sk_common.skc_v6_daddr.in6_u.u6_addr32);
Brendan Gregg553f2aa2016-02-14 18:15:24 -0800106 data6.lport = lport;
107 data6.dport = dport;
108 data6.state = state;
109 ipv6_events.perf_submit(ctx, &data6, sizeof(data6));
110 }
111 // else drop
112
113 return 0;
114}
115
116int trace_retransmit(struct pt_regs *ctx, struct sock *sk)
117{
118 trace_event(ctx, sk, RETRANSMIT);
119 return 0;
120}
121
122int trace_tlp(struct pt_regs *ctx, struct sock *sk)
123{
124 trace_event(ctx, sk, TLP);
125 return 0;
126}
127"""
128
129# event data
Brendan Gregg553f2aa2016-02-14 18:15:24 -0800130class Data_ipv4(ct.Structure):
131 _fields_ = [
132 ("pid", ct.c_ulonglong),
133 ("ip", ct.c_ulonglong),
134 ("saddr", ct.c_ulonglong),
135 ("daddr", ct.c_ulonglong),
136 ("lport", ct.c_ulonglong),
137 ("dport", ct.c_ulonglong),
138 ("state", ct.c_ulonglong),
139 ("type", ct.c_ulonglong)
140 ]
Mark Drayton11de2982016-06-26 21:14:44 +0100141
Brendan Gregg553f2aa2016-02-14 18:15:24 -0800142class Data_ipv6(ct.Structure):
143 _fields_ = [
144 ("pid", ct.c_ulonglong),
145 ("ip", ct.c_ulonglong),
Mark Drayton11de2982016-06-26 21:14:44 +0100146 ("saddr", (ct.c_ulonglong * 2)),
147 ("daddr", (ct.c_ulonglong * 2)),
Brendan Gregg553f2aa2016-02-14 18:15:24 -0800148 ("lport", ct.c_ulonglong),
149 ("dport", ct.c_ulonglong),
150 ("state", ct.c_ulonglong),
151 ("type", ct.c_ulonglong)
152 ]
153
154# from bpf_text:
155type = {}
156type[1] = 'R'
157type[2] = 'L'
158
Brendan Gregg553f2aa2016-02-14 18:15:24 -0800159# from include/net/tcp_states.h:
160tcpstate = {}
161tcpstate[1] = 'ESTABLISHED'
162tcpstate[2] = 'SYN_SENT'
163tcpstate[3] = 'SYN_RECV'
164tcpstate[4] = 'FIN_WAIT1'
165tcpstate[5] = 'FIN_WAIT2'
166tcpstate[6] = 'TIME_WAIT'
167tcpstate[7] = 'CLOSE'
168tcpstate[8] = 'CLOSE_WAIT'
169tcpstate[9] = 'LAST_ACK'
170tcpstate[10] = 'LISTEN'
171tcpstate[11] = 'CLOSING'
172tcpstate[12] = 'NEW_SYN_RECV'
173
174# process event
175def print_ipv4_event(cpu, data, size):
176 event = ct.cast(data, ct.POINTER(Data_ipv4)).contents
177 print("%-8s %-6d %-2d %-20s %1s> %-20s %s" % (
178 strftime("%H:%M:%S"), event.pid, event.ip,
Mark Drayton11de2982016-06-26 21:14:44 +0100179 "%s:%d" % (inet_ntop(AF_INET, pack('I', event.saddr)), event.lport),
Brendan Gregg553f2aa2016-02-14 18:15:24 -0800180 type[event.type],
Mark Drayton11de2982016-06-26 21:14:44 +0100181 "%s:%s" % (inet_ntop(AF_INET, pack('I', event.daddr)), event.dport),
Brendan Gregg553f2aa2016-02-14 18:15:24 -0800182 tcpstate[event.state]))
Mark Drayton11de2982016-06-26 21:14:44 +0100183
Brendan Gregg553f2aa2016-02-14 18:15:24 -0800184def print_ipv6_event(cpu, data, size):
185 event = ct.cast(data, ct.POINTER(Data_ipv6)).contents
Ivan Babrouc862e312016-06-23 18:11:25 +0100186 print("%-8s %-6d %-2d %-20s %1s> %-20s %s" % (
Brendan Gregg553f2aa2016-02-14 18:15:24 -0800187 strftime("%H:%M:%S"), event.pid, event.ip,
Mark Drayton11de2982016-06-26 21:14:44 +0100188 "%s:%d" % (inet_ntop(AF_INET6, event.saddr), event.lport),
Brendan Gregg553f2aa2016-02-14 18:15:24 -0800189 type[event.type],
Mark Drayton11de2982016-06-26 21:14:44 +0100190 "%s:%d" % (inet_ntop(AF_INET6, event.daddr), event.dport),
Brendan Gregg553f2aa2016-02-14 18:15:24 -0800191 tcpstate[event.state]))
192
193# initialize BPF
194b = BPF(text=bpf_text)
195b.attach_kprobe(event="tcp_retransmit_skb", fn_name="trace_retransmit")
Mark Drayton11de2982016-06-26 21:14:44 +0100196if args.lossprobe:
197 b.attach_kprobe(event="tcp_send_loss_probe", fn_name="trace_tlp")
Brendan Gregg553f2aa2016-02-14 18:15:24 -0800198
199# header
200print("%-8s %-6s %-2s %-20s %1s> %-20s %-4s" % ("TIME", "PID", "IP",
201 "LADDR:LPORT", "T", "RADDR:RPORT", "STATE"))
202
203# read events
204b["ipv4_events"].open_perf_buffer(print_ipv4_event)
205b["ipv6_events"].open_perf_buffer(print_ipv6_event)
206while 1:
207 b.kprobe_poll()