blob: 21fcbc7a6b3f5155fdd1e79ecb78ac2a80d11f4f [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
Paul Chaignon25212ee2017-08-06 11:15:11 +020073static int trace_event(struct pt_regs *ctx, struct sock *skp, int type)
Brendan Gregg553f2aa2016-02-14 18:15:24 -080074{
Paul Chaignon25212ee2017-08-06 11:15:11 +020075 if (skp == NULL)
Brendan Gregg553f2aa2016-02-14 18:15:24 -080076 return 0;
77 u32 pid = bpf_get_current_pid_tgid();
Brendan Gregg553f2aa2016-02-14 18:15:24 -080078
79 // pull in details
Paul Chaignon25212ee2017-08-06 11:15:11 +020080 u16 family = skp->__sk_common.skc_family;
81 u16 lport = skp->__sk_common.skc_num;
82 u16 dport = skp->__sk_common.skc_dport;
83 char state = skp->__sk_common.skc_state;
Brendan Gregg553f2aa2016-02-14 18:15:24 -080084
85 if (family == AF_INET) {
86 struct ipv4_data_t data4 = {.pid = pid, .ip = 4, .type = type};
Paul Chaignon25212ee2017-08-06 11:15:11 +020087 data4.saddr = skp->__sk_common.skc_rcv_saddr;
88 data4.daddr = skp->__sk_common.skc_daddr;
Mark Drayton8ba998e2016-07-31 17:51:52 +010089 // lport is host order
Brendan Gregg553f2aa2016-02-14 18:15:24 -080090 data4.lport = lport;
Mark Drayton8ba998e2016-07-31 17:51:52 +010091 data4.dport = ntohs(dport);
Brendan Gregg553f2aa2016-02-14 18:15:24 -080092 data4.state = state;
93 ipv4_events.perf_submit(ctx, &data4, sizeof(data4));
94
95 } else if (family == AF_INET6) {
96 struct ipv6_data_t data6 = {.pid = pid, .ip = 6, .type = type};
Mark Drayton11de2982016-06-26 21:14:44 +010097 bpf_probe_read(&data6.saddr, sizeof(data6.saddr),
Paul Chaignon25212ee2017-08-06 11:15:11 +020098 skp->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32);
Mark Drayton11de2982016-06-26 21:14:44 +010099 bpf_probe_read(&data6.daddr, sizeof(data6.daddr),
Paul Chaignon25212ee2017-08-06 11:15:11 +0200100 skp->__sk_common.skc_v6_daddr.in6_u.u6_addr32);
Mark Drayton8ba998e2016-07-31 17:51:52 +0100101 // lport is host order
Brendan Gregg553f2aa2016-02-14 18:15:24 -0800102 data6.lport = lport;
Mark Drayton8ba998e2016-07-31 17:51:52 +0100103 data6.dport = ntohs(dport);
Brendan Gregg553f2aa2016-02-14 18:15:24 -0800104 data6.state = state;
105 ipv6_events.perf_submit(ctx, &data6, sizeof(data6));
106 }
107 // else drop
108
109 return 0;
110}
111
112int trace_retransmit(struct pt_regs *ctx, struct sock *sk)
113{
114 trace_event(ctx, sk, RETRANSMIT);
115 return 0;
116}
117
118int trace_tlp(struct pt_regs *ctx, struct sock *sk)
119{
120 trace_event(ctx, sk, TLP);
121 return 0;
122}
123"""
124
125# event data
Brendan Gregg553f2aa2016-02-14 18:15:24 -0800126class Data_ipv4(ct.Structure):
127 _fields_ = [
128 ("pid", ct.c_ulonglong),
129 ("ip", ct.c_ulonglong),
130 ("saddr", ct.c_ulonglong),
131 ("daddr", ct.c_ulonglong),
132 ("lport", ct.c_ulonglong),
133 ("dport", ct.c_ulonglong),
134 ("state", ct.c_ulonglong),
135 ("type", ct.c_ulonglong)
136 ]
Mark Drayton11de2982016-06-26 21:14:44 +0100137
Brendan Gregg553f2aa2016-02-14 18:15:24 -0800138class Data_ipv6(ct.Structure):
139 _fields_ = [
140 ("pid", ct.c_ulonglong),
141 ("ip", ct.c_ulonglong),
Mark Drayton11de2982016-06-26 21:14:44 +0100142 ("saddr", (ct.c_ulonglong * 2)),
143 ("daddr", (ct.c_ulonglong * 2)),
Brendan Gregg553f2aa2016-02-14 18:15:24 -0800144 ("lport", ct.c_ulonglong),
145 ("dport", ct.c_ulonglong),
146 ("state", ct.c_ulonglong),
147 ("type", ct.c_ulonglong)
148 ]
149
150# from bpf_text:
151type = {}
152type[1] = 'R'
153type[2] = 'L'
154
Brendan Gregg553f2aa2016-02-14 18:15:24 -0800155# from include/net/tcp_states.h:
156tcpstate = {}
157tcpstate[1] = 'ESTABLISHED'
158tcpstate[2] = 'SYN_SENT'
159tcpstate[3] = 'SYN_RECV'
160tcpstate[4] = 'FIN_WAIT1'
161tcpstate[5] = 'FIN_WAIT2'
162tcpstate[6] = 'TIME_WAIT'
163tcpstate[7] = 'CLOSE'
164tcpstate[8] = 'CLOSE_WAIT'
165tcpstate[9] = 'LAST_ACK'
166tcpstate[10] = 'LISTEN'
167tcpstate[11] = 'CLOSING'
168tcpstate[12] = 'NEW_SYN_RECV'
169
170# process event
171def print_ipv4_event(cpu, data, size):
172 event = ct.cast(data, ct.POINTER(Data_ipv4)).contents
173 print("%-8s %-6d %-2d %-20s %1s> %-20s %s" % (
174 strftime("%H:%M:%S"), event.pid, event.ip,
Mark Drayton11de2982016-06-26 21:14:44 +0100175 "%s:%d" % (inet_ntop(AF_INET, pack('I', event.saddr)), event.lport),
Brendan Gregg553f2aa2016-02-14 18:15:24 -0800176 type[event.type],
Mark Drayton11de2982016-06-26 21:14:44 +0100177 "%s:%s" % (inet_ntop(AF_INET, pack('I', event.daddr)), event.dport),
Brendan Gregg553f2aa2016-02-14 18:15:24 -0800178 tcpstate[event.state]))
Mark Drayton11de2982016-06-26 21:14:44 +0100179
Brendan Gregg553f2aa2016-02-14 18:15:24 -0800180def print_ipv6_event(cpu, data, size):
181 event = ct.cast(data, ct.POINTER(Data_ipv6)).contents
Ivan Babrouc862e312016-06-23 18:11:25 +0100182 print("%-8s %-6d %-2d %-20s %1s> %-20s %s" % (
Brendan Gregg553f2aa2016-02-14 18:15:24 -0800183 strftime("%H:%M:%S"), event.pid, event.ip,
Mark Drayton11de2982016-06-26 21:14:44 +0100184 "%s:%d" % (inet_ntop(AF_INET6, event.saddr), event.lport),
Brendan Gregg553f2aa2016-02-14 18:15:24 -0800185 type[event.type],
Mark Drayton11de2982016-06-26 21:14:44 +0100186 "%s:%d" % (inet_ntop(AF_INET6, event.daddr), event.dport),
Brendan Gregg553f2aa2016-02-14 18:15:24 -0800187 tcpstate[event.state]))
188
189# initialize BPF
190b = BPF(text=bpf_text)
191b.attach_kprobe(event="tcp_retransmit_skb", fn_name="trace_retransmit")
Mark Drayton11de2982016-06-26 21:14:44 +0100192if args.lossprobe:
193 b.attach_kprobe(event="tcp_send_loss_probe", fn_name="trace_tlp")
Brendan Gregg553f2aa2016-02-14 18:15:24 -0800194
195# header
196print("%-8s %-6s %-2s %-20s %1s> %-20s %-4s" % ("TIME", "PID", "IP",
197 "LADDR:LPORT", "T", "RADDR:RPORT", "STATE"))
198
199# read events
200b["ipv4_events"].open_perf_buffer(print_ipv4_event)
201b["ipv6_events"].open_perf_buffer(print_ipv6_event)
202while 1:
203 b.kprobe_poll()