blob: 4853f6ab54a97d9a83d78d942f5d07fc744b91c4 [file] [log] [blame]
Alexey Ivanovcc01a9c2019-01-16 09:50:46 -08001#!/usr/bin/python
Brendan Gregg4f13d882018-05-30 17:09:04 -04002# @lint-avoid-python-3-compatibility-imports
3#
4# tcpdrop Trace TCP kernel-dropped packets/segments.
5# For Linux, uses BCC, eBPF. Embedded C.
6#
7# This provides information such as packet details, socket state, and kernel
8# stack trace for packets/segments that were dropped via tcp_drop().
9#
Hariharan Ananthakrishnan04893e32021-08-12 05:55:21 -070010# USAGE: tcpdrop [-4 | -6] [-h]
Brendan Gregg4f13d882018-05-30 17:09:04 -040011#
12# This uses dynamic tracing of kernel functions, and will need to be updated
13# to match kernel changes.
14#
15# Copyright 2018 Netflix, Inc.
16# Licensed under the Apache License, Version 2.0 (the "License")
17#
18# 30-May-2018 Brendan Gregg Created this.
19
20from __future__ import print_function
21from bcc import BPF
22import argparse
23from time import strftime
24from socket import inet_ntop, AF_INET, AF_INET6
25from struct import pack
Brendan Gregg4f13d882018-05-30 17:09:04 -040026from time import sleep
27from bcc import tcp
28
29# arguments
30examples = """examples:
31 ./tcpdrop # trace kernel TCP drops
Hariharan Ananthakrishnan04893e32021-08-12 05:55:21 -070032 ./tcpdrop -4 # trace IPv4 family only
33 ./tcpdrop -6 # trace IPv6 family only
Brendan Gregg4f13d882018-05-30 17:09:04 -040034"""
35parser = argparse.ArgumentParser(
36 description="Trace TCP drops by the kernel",
37 formatter_class=argparse.RawDescriptionHelpFormatter,
38 epilog=examples)
Hariharan Ananthakrishnan04893e32021-08-12 05:55:21 -070039group = parser.add_mutually_exclusive_group()
40group.add_argument("-4", "--ipv4", action="store_true",
41 help="trace IPv4 family only")
42group.add_argument("-6", "--ipv6", action="store_true",
43 help="trace IPv6 family only")
Brendan Gregg4f13d882018-05-30 17:09:04 -040044parser.add_argument("--ebpf", action="store_true",
45 help=argparse.SUPPRESS)
46args = parser.parse_args()
47debug = 0
48
49# define BPF program
50bpf_text = """
51#include <uapi/linux/ptrace.h>
52#include <uapi/linux/tcp.h>
53#include <uapi/linux/ip.h>
54#include <net/sock.h>
55#include <bcc/proto.h>
56
57BPF_STACK_TRACE(stack_traces, 1024);
58
59// separate data structs for ipv4 and ipv6
60struct ipv4_data_t {
Brendan Gregg1a5090a2018-06-08 14:21:31 -070061 u32 pid;
Brendan Gregg4f13d882018-05-30 17:09:04 -040062 u64 ip;
Joe Yin36ce1122018-08-17 06:04:00 +080063 u32 saddr;
64 u32 daddr;
Brendan Gregg1a5090a2018-06-08 14:21:31 -070065 u16 sport;
66 u16 dport;
67 u8 state;
68 u8 tcpflags;
69 u32 stack_id;
Brendan Gregg4f13d882018-05-30 17:09:04 -040070};
71BPF_PERF_OUTPUT(ipv4_events);
72
73struct ipv6_data_t {
Brendan Gregg1a5090a2018-06-08 14:21:31 -070074 u32 pid;
Brendan Gregg4f13d882018-05-30 17:09:04 -040075 u64 ip;
76 unsigned __int128 saddr;
77 unsigned __int128 daddr;
Brendan Gregg1a5090a2018-06-08 14:21:31 -070078 u16 sport;
79 u16 dport;
80 u8 state;
81 u8 tcpflags;
82 u32 stack_id;
Brendan Gregg4f13d882018-05-30 17:09:04 -040083};
84BPF_PERF_OUTPUT(ipv6_events);
85
86static struct tcphdr *skb_to_tcphdr(const struct sk_buff *skb)
87{
88 // unstable API. verify logic in tcp_hdr() -> skb_transport_header().
89 return (struct tcphdr *)(skb->head + skb->transport_header);
90}
91
92static inline struct iphdr *skb_to_iphdr(const struct sk_buff *skb)
93{
94 // unstable API. verify logic in ip_hdr() -> skb_network_header().
95 return (struct iphdr *)(skb->head + skb->network_header);
96}
97
98// from include/net/tcp.h:
99#ifndef tcp_flag_byte
100#define tcp_flag_byte(th) (((u_int8_t *)th)[13])
101#endif
102
103int trace_tcp_drop(struct pt_regs *ctx, struct sock *sk, struct sk_buff *skb)
104{
105 if (sk == NULL)
106 return 0;
Hengqi Chen151fe192021-05-16 17:18:27 +0800107 u32 pid = bpf_get_current_pid_tgid() >> 32;
Brendan Gregg4f13d882018-05-30 17:09:04 -0400108
109 // pull in details from the packet headers and the sock struct
110 u16 family = sk->__sk_common.skc_family;
111 char state = sk->__sk_common.skc_state;
112 u16 sport = 0, dport = 0;
Brendan Gregg4f13d882018-05-30 17:09:04 -0400113 struct tcphdr *tcp = skb_to_tcphdr(skb);
114 struct iphdr *ip = skb_to_iphdr(skb);
Paul Chaignon4ba5c092018-06-28 23:11:20 +0200115 u8 tcpflags = ((u_int8_t *)tcp)[13];
Paul Chaignonfe779f32018-06-14 03:51:55 +0200116 sport = tcp->source;
117 dport = tcp->dest;
Brendan Gregg4f13d882018-05-30 17:09:04 -0400118 sport = ntohs(sport);
119 dport = ntohs(dport);
120
Hariharan Ananthakrishnan04893e32021-08-12 05:55:21 -0700121 FILTER_FAMILY
122
Brendan Gregg4f13d882018-05-30 17:09:04 -0400123 if (family == AF_INET) {
Joe Yin36ce1122018-08-17 06:04:00 +0800124 struct ipv4_data_t data4 = {};
125 data4.pid = pid;
126 data4.ip = 4;
Paul Chaignonfe779f32018-06-14 03:51:55 +0200127 data4.saddr = ip->saddr;
128 data4.daddr = ip->daddr;
Brendan Gregg4f13d882018-05-30 17:09:04 -0400129 data4.dport = dport;
130 data4.sport = sport;
Brendan Gregg4f13d882018-05-30 17:09:04 -0400131 data4.state = state;
Brendan Greggb2b98712018-06-01 01:28:08 -0400132 data4.tcpflags = tcpflags;
Brendan Gregg4f13d882018-05-30 17:09:04 -0400133 data4.stack_id = stack_traces.get_stackid(ctx, 0);
134 ipv4_events.perf_submit(ctx, &data4, sizeof(data4));
135
136 } else if (family == AF_INET6) {
Marko Myllynenbfbf17e2018-09-11 21:49:58 +0300137 struct ipv6_data_t data6 = {};
138 data6.pid = pid;
139 data6.ip = 6;
Neil Spring7e3f0c02020-09-28 14:29:24 -0700140 // The remote address (skc_v6_daddr) was the source
Sumanth Korikkar7f6066d2020-05-20 10:49:56 -0500141 bpf_probe_read_kernel(&data6.saddr, sizeof(data6.saddr),
Brendan Gregg4f13d882018-05-30 17:09:04 -0400142 sk->__sk_common.skc_v6_daddr.in6_u.u6_addr32);
Neil Spring7e3f0c02020-09-28 14:29:24 -0700143 // The local address (skc_v6_rcv_saddr) was the destination
144 bpf_probe_read_kernel(&data6.daddr, sizeof(data6.daddr),
145 sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32);
Brendan Gregg4f13d882018-05-30 17:09:04 -0400146 data6.dport = dport;
147 data6.sport = sport;
Brendan Gregg4f13d882018-05-30 17:09:04 -0400148 data6.state = state;
Brendan Greggb2b98712018-06-01 01:28:08 -0400149 data6.tcpflags = tcpflags;
Brendan Gregg4f13d882018-05-30 17:09:04 -0400150 data6.stack_id = stack_traces.get_stackid(ctx, 0);
151 ipv6_events.perf_submit(ctx, &data6, sizeof(data6));
152 }
153 // else drop
154
155 return 0;
156}
157"""
158
159if debug or args.ebpf:
160 print(bpf_text)
161 if args.ebpf:
162 exit()
Hariharan Ananthakrishnan04893e32021-08-12 05:55:21 -0700163if args.ipv4:
164 bpf_text = bpf_text.replace('FILTER_FAMILY',
165 'if (family != AF_INET) { return 0; }')
166elif args.ipv6:
167 bpf_text = bpf_text.replace('FILTER_FAMILY',
168 'if (family != AF_INET6) { return 0; }')
169else:
170 bpf_text = bpf_text.replace('FILTER_FAMILY', '')
Brendan Gregg4f13d882018-05-30 17:09:04 -0400171
Brendan Gregg4f13d882018-05-30 17:09:04 -0400172# process event
173def print_ipv4_event(cpu, data, size):
Xiaozhou Liu51d62d32019-02-15 13:03:05 +0800174 event = b["ipv4_events"].event(data)
Hengqi Chen151fe192021-05-16 17:18:27 +0800175 print("%-8s %-7d %-2d %-20s > %-20s %s (%s)" % (
Brendan Gregg4f13d882018-05-30 17:09:04 -0400176 strftime("%H:%M:%S"), event.pid, event.ip,
177 "%s:%d" % (inet_ntop(AF_INET, pack('I', event.saddr)), event.sport),
178 "%s:%s" % (inet_ntop(AF_INET, pack('I', event.daddr)), event.dport),
179 tcp.tcpstate[event.state], tcp.flags2str(event.tcpflags)))
180 for addr in stack_traces.walk(event.stack_id):
181 sym = b.ksym(addr, show_offset=True)
182 print("\t%s" % sym)
183 print("")
184
185def print_ipv6_event(cpu, data, size):
Xiaozhou Liu51d62d32019-02-15 13:03:05 +0800186 event = b["ipv6_events"].event(data)
Hengqi Chen151fe192021-05-16 17:18:27 +0800187 print("%-8s %-7d %-2d %-20s > %-20s %s (%s)" % (
Brendan Gregg4f13d882018-05-30 17:09:04 -0400188 strftime("%H:%M:%S"), event.pid, event.ip,
189 "%s:%d" % (inet_ntop(AF_INET6, event.saddr), event.sport),
190 "%s:%d" % (inet_ntop(AF_INET6, event.daddr), event.dport),
191 tcp.tcpstate[event.state], tcp.flags2str(event.tcpflags)))
192 for addr in stack_traces.walk(event.stack_id):
193 sym = b.ksym(addr, show_offset=True)
194 print("\t%s" % sym)
195 print("")
196
197# initialize BPF
198b = BPF(text=bpf_text)
yonghong-song3b86b562018-06-13 06:12:43 -0700199if b.get_kprobe_functions(b"tcp_drop"):
Brendan Gregg4f13d882018-05-30 17:09:04 -0400200 b.attach_kprobe(event="tcp_drop", fn_name="trace_tcp_drop")
201else:
202 print("ERROR: tcp_drop() kernel function not found or traceable. "
203 "Older kernel versions not supported.")
204 exit()
205stack_traces = b.get_table("stack_traces")
206
207# header
Hengqi Chen151fe192021-05-16 17:18:27 +0800208print("%-8s %-7s %-2s %-20s > %-20s %s (%s)" % ("TIME", "PID", "IP",
Brendan Gregg4f13d882018-05-30 17:09:04 -0400209 "SADDR:SPORT", "DADDR:DPORT", "STATE", "FLAGS"))
210
211# read events
212b["ipv4_events"].open_perf_buffer(print_ipv4_event)
213b["ipv6_events"].open_perf_buffer(print_ipv6_event)
214while 1:
Jerome Marchand51671272018-12-19 01:57:24 +0100215 try:
216 b.perf_buffer_poll()
217 except KeyboardInterrupt:
218 exit()