blob: 47857debaef9832b640d5163a35af218ddc78448 [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);
Mark Drayton8ba998e2016-07-31 17:51:52 +010095 // lport is host order
Brendan Gregg553f2aa2016-02-14 18:15:24 -080096 data4.lport = lport;
Mark Drayton8ba998e2016-07-31 17:51:52 +010097 data4.dport = ntohs(dport);
Brendan Gregg553f2aa2016-02-14 18:15:24 -080098 data4.state = state;
99 ipv4_events.perf_submit(ctx, &data4, sizeof(data4));
100
101 } else if (family == AF_INET6) {
102 struct ipv6_data_t data6 = {.pid = pid, .ip = 6, .type = type};
Mark Drayton11de2982016-06-26 21:14:44 +0100103 bpf_probe_read(&data6.saddr, sizeof(data6.saddr),
104 &skp->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32);
105 bpf_probe_read(&data6.daddr, sizeof(data6.daddr),
106 &skp->__sk_common.skc_v6_daddr.in6_u.u6_addr32);
Mark Drayton8ba998e2016-07-31 17:51:52 +0100107 // lport is host order
Brendan Gregg553f2aa2016-02-14 18:15:24 -0800108 data6.lport = lport;
Mark Drayton8ba998e2016-07-31 17:51:52 +0100109 data6.dport = ntohs(dport);
Brendan Gregg553f2aa2016-02-14 18:15:24 -0800110 data6.state = state;
111 ipv6_events.perf_submit(ctx, &data6, sizeof(data6));
112 }
113 // else drop
114
115 return 0;
116}
117
118int trace_retransmit(struct pt_regs *ctx, struct sock *sk)
119{
120 trace_event(ctx, sk, RETRANSMIT);
121 return 0;
122}
123
124int trace_tlp(struct pt_regs *ctx, struct sock *sk)
125{
126 trace_event(ctx, sk, TLP);
127 return 0;
128}
129"""
130
131# event data
Brendan Gregg553f2aa2016-02-14 18:15:24 -0800132class Data_ipv4(ct.Structure):
133 _fields_ = [
134 ("pid", ct.c_ulonglong),
135 ("ip", ct.c_ulonglong),
136 ("saddr", ct.c_ulonglong),
137 ("daddr", ct.c_ulonglong),
138 ("lport", ct.c_ulonglong),
139 ("dport", ct.c_ulonglong),
140 ("state", ct.c_ulonglong),
141 ("type", ct.c_ulonglong)
142 ]
Mark Drayton11de2982016-06-26 21:14:44 +0100143
Brendan Gregg553f2aa2016-02-14 18:15:24 -0800144class Data_ipv6(ct.Structure):
145 _fields_ = [
146 ("pid", ct.c_ulonglong),
147 ("ip", ct.c_ulonglong),
Mark Drayton11de2982016-06-26 21:14:44 +0100148 ("saddr", (ct.c_ulonglong * 2)),
149 ("daddr", (ct.c_ulonglong * 2)),
Brendan Gregg553f2aa2016-02-14 18:15:24 -0800150 ("lport", ct.c_ulonglong),
151 ("dport", ct.c_ulonglong),
152 ("state", ct.c_ulonglong),
153 ("type", ct.c_ulonglong)
154 ]
155
156# from bpf_text:
157type = {}
158type[1] = 'R'
159type[2] = 'L'
160
Brendan Gregg553f2aa2016-02-14 18:15:24 -0800161# from include/net/tcp_states.h:
162tcpstate = {}
163tcpstate[1] = 'ESTABLISHED'
164tcpstate[2] = 'SYN_SENT'
165tcpstate[3] = 'SYN_RECV'
166tcpstate[4] = 'FIN_WAIT1'
167tcpstate[5] = 'FIN_WAIT2'
168tcpstate[6] = 'TIME_WAIT'
169tcpstate[7] = 'CLOSE'
170tcpstate[8] = 'CLOSE_WAIT'
171tcpstate[9] = 'LAST_ACK'
172tcpstate[10] = 'LISTEN'
173tcpstate[11] = 'CLOSING'
174tcpstate[12] = 'NEW_SYN_RECV'
175
176# process event
177def print_ipv4_event(cpu, data, size):
178 event = ct.cast(data, ct.POINTER(Data_ipv4)).contents
179 print("%-8s %-6d %-2d %-20s %1s> %-20s %s" % (
180 strftime("%H:%M:%S"), event.pid, event.ip,
Mark Drayton11de2982016-06-26 21:14:44 +0100181 "%s:%d" % (inet_ntop(AF_INET, pack('I', event.saddr)), event.lport),
Brendan Gregg553f2aa2016-02-14 18:15:24 -0800182 type[event.type],
Mark Drayton11de2982016-06-26 21:14:44 +0100183 "%s:%s" % (inet_ntop(AF_INET, pack('I', event.daddr)), event.dport),
Brendan Gregg553f2aa2016-02-14 18:15:24 -0800184 tcpstate[event.state]))
Mark Drayton11de2982016-06-26 21:14:44 +0100185
Brendan Gregg553f2aa2016-02-14 18:15:24 -0800186def print_ipv6_event(cpu, data, size):
187 event = ct.cast(data, ct.POINTER(Data_ipv6)).contents
Ivan Babrouc862e312016-06-23 18:11:25 +0100188 print("%-8s %-6d %-2d %-20s %1s> %-20s %s" % (
Brendan Gregg553f2aa2016-02-14 18:15:24 -0800189 strftime("%H:%M:%S"), event.pid, event.ip,
Mark Drayton11de2982016-06-26 21:14:44 +0100190 "%s:%d" % (inet_ntop(AF_INET6, event.saddr), event.lport),
Brendan Gregg553f2aa2016-02-14 18:15:24 -0800191 type[event.type],
Mark Drayton11de2982016-06-26 21:14:44 +0100192 "%s:%d" % (inet_ntop(AF_INET6, event.daddr), event.dport),
Brendan Gregg553f2aa2016-02-14 18:15:24 -0800193 tcpstate[event.state]))
194
195# initialize BPF
196b = BPF(text=bpf_text)
197b.attach_kprobe(event="tcp_retransmit_skb", fn_name="trace_retransmit")
Mark Drayton11de2982016-06-26 21:14:44 +0100198if args.lossprobe:
199 b.attach_kprobe(event="tcp_send_loss_probe", fn_name="trace_tlp")
Brendan Gregg553f2aa2016-02-14 18:15:24 -0800200
201# header
202print("%-8s %-6s %-2s %-20s %1s> %-20s %-4s" % ("TIME", "PID", "IP",
203 "LADDR:LPORT", "T", "RADDR:RPORT", "STATE"))
204
205# read events
206b["ipv4_events"].open_perf_buffer(print_ipv4_event)
207b["ipv6_events"].open_perf_buffer(print_ipv6_event)
208while 1:
209 b.kprobe_poll()