xref: /aosp_15_r20/external/bcc/examples/tracing/nflatency.py (revision 387f9dfdfa2baef462e92476d413c7bc2470293e)
1*387f9dfdSAndroid Build Coastguard Worker#!/usr/bin/python3
2*387f9dfdSAndroid Build Coastguard Worker#
3*387f9dfdSAndroid Build Coastguard Worker# nflatency     Trace netfilter hook latency.
4*387f9dfdSAndroid Build Coastguard Worker#
5*387f9dfdSAndroid Build Coastguard Worker# This attaches a kprobe and kretprobe to nf_hook_slow.
6*387f9dfdSAndroid Build Coastguard Worker# 2020-04-03 Casey Callendrello / <[email protected]>
7*387f9dfdSAndroid Build Coastguard Worker
8*387f9dfdSAndroid Build Coastguard Workerimport argparse
9*387f9dfdSAndroid Build Coastguard Workerimport sys
10*387f9dfdSAndroid Build Coastguard Workerimport time
11*387f9dfdSAndroid Build Coastguard Worker
12*387f9dfdSAndroid Build Coastguard Workerfrom bcc import BPF
13*387f9dfdSAndroid Build Coastguard Worker
14*387f9dfdSAndroid Build Coastguard WorkerBPF_SRC = """
15*387f9dfdSAndroid Build Coastguard Worker#include <linux/ip.h>
16*387f9dfdSAndroid Build Coastguard Worker#include <linux/ipv6.h>
17*387f9dfdSAndroid Build Coastguard Worker#include <linux/tcp.h>
18*387f9dfdSAndroid Build Coastguard Worker#include <linux/netfilter.h>
19*387f9dfdSAndroid Build Coastguard Worker#include <net/ip.h>
20*387f9dfdSAndroid Build Coastguard Worker#include <uapi/linux/bpf.h>
21*387f9dfdSAndroid Build Coastguard Worker
22*387f9dfdSAndroid Build Coastguard Workerstatic struct tcphdr *skb_to_tcphdr(const struct sk_buff *skb)
23*387f9dfdSAndroid Build Coastguard Worker{
24*387f9dfdSAndroid Build Coastguard Worker    // unstable API. verify logic in tcp_hdr() -> skb_transport_header().
25*387f9dfdSAndroid Build Coastguard Worker    return (struct tcphdr *)(skb->head + skb->transport_header);
26*387f9dfdSAndroid Build Coastguard Worker}
27*387f9dfdSAndroid Build Coastguard Worker
28*387f9dfdSAndroid Build Coastguard Workerstatic inline struct iphdr *skb_to_iphdr(const struct sk_buff *skb)
29*387f9dfdSAndroid Build Coastguard Worker{
30*387f9dfdSAndroid Build Coastguard Worker    // unstable API. verify logic in ip_hdr() -> skb_network_header().
31*387f9dfdSAndroid Build Coastguard Worker    return (struct iphdr *)(skb->head + skb->network_header);
32*387f9dfdSAndroid Build Coastguard Worker}
33*387f9dfdSAndroid Build Coastguard Worker
34*387f9dfdSAndroid Build Coastguard Workerstatic inline struct ipv6hdr *skb_to_ip6hdr(const struct sk_buff *skb)
35*387f9dfdSAndroid Build Coastguard Worker{
36*387f9dfdSAndroid Build Coastguard Worker    // unstable API. verify logic in ip_hdr() -> skb_network_header().
37*387f9dfdSAndroid Build Coastguard Worker    return (struct ipv6hdr *)(skb->head + skb->network_header);
38*387f9dfdSAndroid Build Coastguard Worker}
39*387f9dfdSAndroid Build Coastguard Worker
40*387f9dfdSAndroid Build Coastguard Worker// for correlating between kprobe and kretprobe
41*387f9dfdSAndroid Build Coastguard Workerstruct start_data {
42*387f9dfdSAndroid Build Coastguard Worker    u8 hook;
43*387f9dfdSAndroid Build Coastguard Worker    u8 pf; // netfilter protocol
44*387f9dfdSAndroid Build Coastguard Worker    u8 tcp_state;
45*387f9dfdSAndroid Build Coastguard Worker    u64 ts;
46*387f9dfdSAndroid Build Coastguard Worker};
47*387f9dfdSAndroid Build Coastguard WorkerBPF_PERCPU_ARRAY(sts, struct start_data, 1);
48*387f9dfdSAndroid Build Coastguard Worker
49*387f9dfdSAndroid Build Coastguard Worker// the histogram keys
50*387f9dfdSAndroid Build Coastguard Workertypedef struct nf_lat_key {
51*387f9dfdSAndroid Build Coastguard Worker    u8 proto; // see netfilter.h
52*387f9dfdSAndroid Build Coastguard Worker    u8 hook;
53*387f9dfdSAndroid Build Coastguard Worker    u8 tcp_state;
54*387f9dfdSAndroid Build Coastguard Worker} nf_lat_key_t;
55*387f9dfdSAndroid Build Coastguard Worker
56*387f9dfdSAndroid Build Coastguard Workertypedef struct hist_key {
57*387f9dfdSAndroid Build Coastguard Worker    nf_lat_key_t key;
58*387f9dfdSAndroid Build Coastguard Worker    u64 slot;
59*387f9dfdSAndroid Build Coastguard Worker} hist_key_t;
60*387f9dfdSAndroid Build Coastguard WorkerBPF_HISTOGRAM(dist, hist_key_t);
61*387f9dfdSAndroid Build Coastguard Worker
62*387f9dfdSAndroid Build Coastguard Worker
63*387f9dfdSAndroid Build Coastguard Workerint kprobe__nf_hook_slow(struct pt_regs *ctx, struct sk_buff *skb, struct nf_hook_state *state) {
64*387f9dfdSAndroid Build Coastguard Worker    struct start_data data = {};
65*387f9dfdSAndroid Build Coastguard Worker    data.ts = bpf_ktime_get_ns();
66*387f9dfdSAndroid Build Coastguard Worker    data.hook = state->hook;
67*387f9dfdSAndroid Build Coastguard Worker    data.pf = state->pf;
68*387f9dfdSAndroid Build Coastguard Worker
69*387f9dfdSAndroid Build Coastguard Worker    COND
70*387f9dfdSAndroid Build Coastguard Worker
71*387f9dfdSAndroid Build Coastguard Worker    u8 ip_proto;
72*387f9dfdSAndroid Build Coastguard Worker    if (skb->protocol == htons(ETH_P_IP)) {
73*387f9dfdSAndroid Build Coastguard Worker        struct iphdr *ip = skb_to_iphdr(skb);
74*387f9dfdSAndroid Build Coastguard Worker        ip_proto = ip->protocol;
75*387f9dfdSAndroid Build Coastguard Worker
76*387f9dfdSAndroid Build Coastguard Worker    } else if (skb->protocol == htons(ETH_P_IPV6)) {
77*387f9dfdSAndroid Build Coastguard Worker        struct ipv6hdr *ip = skb_to_ip6hdr(skb);
78*387f9dfdSAndroid Build Coastguard Worker        ip_proto = ip->nexthdr;
79*387f9dfdSAndroid Build Coastguard Worker    }
80*387f9dfdSAndroid Build Coastguard Worker
81*387f9dfdSAndroid Build Coastguard Worker    data.tcp_state = 0;
82*387f9dfdSAndroid Build Coastguard Worker    if (ip_proto == 0x06) { //tcp
83*387f9dfdSAndroid Build Coastguard Worker        struct tcphdr *tcp = skb_to_tcphdr(skb);
84*387f9dfdSAndroid Build Coastguard Worker        u8 tcpflags = ((u_int8_t *)tcp)[13];
85*387f9dfdSAndroid Build Coastguard Worker
86*387f9dfdSAndroid Build Coastguard Worker        // FIN or RST
87*387f9dfdSAndroid Build Coastguard Worker        if (((tcpflags & 1) + (tcpflags & 4)) > 0) {
88*387f9dfdSAndroid Build Coastguard Worker            data.tcp_state = 3;
89*387f9dfdSAndroid Build Coastguard Worker        }
90*387f9dfdSAndroid Build Coastguard Worker        // SYN / SACK
91*387f9dfdSAndroid Build Coastguard Worker        else if ((tcpflags & 0x02) > 0) {
92*387f9dfdSAndroid Build Coastguard Worker            data.tcp_state = 1;
93*387f9dfdSAndroid Build Coastguard Worker            if ((tcpflags & 16) > 0) { // ACK
94*387f9dfdSAndroid Build Coastguard Worker                data.tcp_state = 2;
95*387f9dfdSAndroid Build Coastguard Worker            }
96*387f9dfdSAndroid Build Coastguard Worker        }
97*387f9dfdSAndroid Build Coastguard Worker    }
98*387f9dfdSAndroid Build Coastguard Worker
99*387f9dfdSAndroid Build Coastguard Worker    u32 idx = 0;
100*387f9dfdSAndroid Build Coastguard Worker    sts.update(&idx, &data);
101*387f9dfdSAndroid Build Coastguard Worker    return 0;
102*387f9dfdSAndroid Build Coastguard Worker}
103*387f9dfdSAndroid Build Coastguard Worker
104*387f9dfdSAndroid Build Coastguard Workerint kretprobe__nf_hook_slow(struct pt_regs *ctx) {
105*387f9dfdSAndroid Build Coastguard Worker    u32 idx = 0;
106*387f9dfdSAndroid Build Coastguard Worker    struct start_data *s;
107*387f9dfdSAndroid Build Coastguard Worker    s = sts.lookup(&idx);
108*387f9dfdSAndroid Build Coastguard Worker    if (!s || s->ts == 0) {
109*387f9dfdSAndroid Build Coastguard Worker        return 0;
110*387f9dfdSAndroid Build Coastguard Worker    }
111*387f9dfdSAndroid Build Coastguard Worker
112*387f9dfdSAndroid Build Coastguard Worker    s->ts = bpf_ktime_get_ns() - s->ts;
113*387f9dfdSAndroid Build Coastguard Worker
114*387f9dfdSAndroid Build Coastguard Worker    hist_key_t key = {};
115*387f9dfdSAndroid Build Coastguard Worker    key.key.hook = s->hook;
116*387f9dfdSAndroid Build Coastguard Worker    key.key.proto = s->pf;
117*387f9dfdSAndroid Build Coastguard Worker    key.key.tcp_state = s->tcp_state;
118*387f9dfdSAndroid Build Coastguard Worker    key.slot = bpf_log2l(s->ts / FACTOR );
119*387f9dfdSAndroid Build Coastguard Worker    dist.increment(key);
120*387f9dfdSAndroid Build Coastguard Worker
121*387f9dfdSAndroid Build Coastguard Worker    s->ts = 0;
122*387f9dfdSAndroid Build Coastguard Worker    sts.update(&idx, s);
123*387f9dfdSAndroid Build Coastguard Worker
124*387f9dfdSAndroid Build Coastguard Worker    return 0;
125*387f9dfdSAndroid Build Coastguard Worker}
126*387f9dfdSAndroid Build Coastguard Worker"""
127*387f9dfdSAndroid Build Coastguard Worker
128*387f9dfdSAndroid Build Coastguard Worker# constants from netfilter.h
129*387f9dfdSAndroid Build Coastguard WorkerNF_HOOKS = {
130*387f9dfdSAndroid Build Coastguard Worker    0: "PRE_ROUTING",
131*387f9dfdSAndroid Build Coastguard Worker    1: "LOCAL_IN",
132*387f9dfdSAndroid Build Coastguard Worker    2: "FORWARD",
133*387f9dfdSAndroid Build Coastguard Worker    3: "LOCAL_OUT",
134*387f9dfdSAndroid Build Coastguard Worker    4: "POST_ROUTING",
135*387f9dfdSAndroid Build Coastguard Worker}
136*387f9dfdSAndroid Build Coastguard Worker
137*387f9dfdSAndroid Build Coastguard WorkerNF_PROTOS = {
138*387f9dfdSAndroid Build Coastguard Worker    0: "UNSPEC",
139*387f9dfdSAndroid Build Coastguard Worker    1: "INET",
140*387f9dfdSAndroid Build Coastguard Worker    2: "IPV4",
141*387f9dfdSAndroid Build Coastguard Worker    3: "ARP",
142*387f9dfdSAndroid Build Coastguard Worker    5: "NETDEV",
143*387f9dfdSAndroid Build Coastguard Worker    7: "BRIDGE",
144*387f9dfdSAndroid Build Coastguard Worker    10: "IPV6",
145*387f9dfdSAndroid Build Coastguard Worker    12: "DECNET",
146*387f9dfdSAndroid Build Coastguard Worker}
147*387f9dfdSAndroid Build Coastguard Worker
148*387f9dfdSAndroid Build Coastguard WorkerTCP_FLAGS = {
149*387f9dfdSAndroid Build Coastguard Worker    0: "other",
150*387f9dfdSAndroid Build Coastguard Worker    1: "SYN",
151*387f9dfdSAndroid Build Coastguard Worker    2: "SACK",
152*387f9dfdSAndroid Build Coastguard Worker    3: "FIN",
153*387f9dfdSAndroid Build Coastguard Worker}
154*387f9dfdSAndroid Build Coastguard Worker
155*387f9dfdSAndroid Build Coastguard WorkerEXAMPLES = """examples:
156*387f9dfdSAndroid Build Coastguard Worker    nflatency                   # print netfilter latency histograms, 1 second refresh
157*387f9dfdSAndroid Build Coastguard Worker    nflatency -p IPV4 -p IPV6   # print only for ipv4 and ipv6
158*387f9dfdSAndroid Build Coastguard Worker    nflatency -k PRE_ROUTING    # only record the PRE_ROUTING hook
159*387f9dfdSAndroid Build Coastguard Worker    nflatency -i 5 -d 10        # run for 10 seconds, printing every 5
160*387f9dfdSAndroid Build Coastguard Worker"""
161*387f9dfdSAndroid Build Coastguard Worker
162*387f9dfdSAndroid Build Coastguard Worker
163*387f9dfdSAndroid Build Coastguard Workerparser = argparse.ArgumentParser(
164*387f9dfdSAndroid Build Coastguard Worker    description="Track latency added by netfilter hooks. Where possible, interesting TCP flags are included",
165*387f9dfdSAndroid Build Coastguard Worker    formatter_class=argparse.RawDescriptionHelpFormatter,
166*387f9dfdSAndroid Build Coastguard Worker    epilog=EXAMPLES)
167*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-p", "--proto",
168*387f9dfdSAndroid Build Coastguard Worker                    action='append',
169*387f9dfdSAndroid Build Coastguard Worker                    help="record this protocol only (multiple parameters allowed)",
170*387f9dfdSAndroid Build Coastguard Worker                    choices=NF_PROTOS.values())
171*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-k", "--hook",
172*387f9dfdSAndroid Build Coastguard Worker                    action='append',
173*387f9dfdSAndroid Build Coastguard Worker                    help="record this netfilter hook only (multiple parameters allowed)",
174*387f9dfdSAndroid Build Coastguard Worker                    choices=NF_HOOKS.values())
175*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-i", "--interval", type=int,
176*387f9dfdSAndroid Build Coastguard Worker                    help="summary interval, in seconds. Default is 10, unless --duration is supplied")
177*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-d", "--duration", type=int,
178*387f9dfdSAndroid Build Coastguard Worker                    help="total duration of trace, in seconds")
179*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("--nano", action="store_true",
180*387f9dfdSAndroid Build Coastguard Worker                    help="bucket by nanoseconds instead of milliseconds")
181*387f9dfdSAndroid Build Coastguard Worker
182*387f9dfdSAndroid Build Coastguard Workerdef main():
183*387f9dfdSAndroid Build Coastguard Worker    args = parser.parse_args()
184*387f9dfdSAndroid Build Coastguard Worker
185*387f9dfdSAndroid Build Coastguard Worker    src = build_src(args)
186*387f9dfdSAndroid Build Coastguard Worker    b = BPF(text=src)
187*387f9dfdSAndroid Build Coastguard Worker    dist = b.get_table("dist")
188*387f9dfdSAndroid Build Coastguard Worker
189*387f9dfdSAndroid Build Coastguard Worker    seconds = 0
190*387f9dfdSAndroid Build Coastguard Worker    interval = 0
191*387f9dfdSAndroid Build Coastguard Worker    if not args.interval:
192*387f9dfdSAndroid Build Coastguard Worker        interval = 1
193*387f9dfdSAndroid Build Coastguard Worker        if args.duration:
194*387f9dfdSAndroid Build Coastguard Worker            interval = args.duration
195*387f9dfdSAndroid Build Coastguard Worker    else:
196*387f9dfdSAndroid Build Coastguard Worker        interval = args.interval
197*387f9dfdSAndroid Build Coastguard Worker
198*387f9dfdSAndroid Build Coastguard Worker    sys.stderr.write("Tracing netfilter hooks... Hit Ctrl-C to end.\n")
199*387f9dfdSAndroid Build Coastguard Worker    while 1:
200*387f9dfdSAndroid Build Coastguard Worker        try:
201*387f9dfdSAndroid Build Coastguard Worker            dist.print_log2_hist(
202*387f9dfdSAndroid Build Coastguard Worker                section_header="Bucket",
203*387f9dfdSAndroid Build Coastguard Worker                bucket_fn=lambda k: (k.proto, k.hook, k.tcp_state),
204*387f9dfdSAndroid Build Coastguard Worker                section_print_fn=bucket_desc)
205*387f9dfdSAndroid Build Coastguard Worker            if args.duration and seconds >= args.duration:
206*387f9dfdSAndroid Build Coastguard Worker                sys.exit(0)
207*387f9dfdSAndroid Build Coastguard Worker            seconds += interval
208*387f9dfdSAndroid Build Coastguard Worker            time.sleep(interval)
209*387f9dfdSAndroid Build Coastguard Worker        except KeyboardInterrupt:
210*387f9dfdSAndroid Build Coastguard Worker            sys.exit(1)
211*387f9dfdSAndroid Build Coastguard Worker
212*387f9dfdSAndroid Build Coastguard Worker
213*387f9dfdSAndroid Build Coastguard Workerdef build_src(args):
214*387f9dfdSAndroid Build Coastguard Worker    cond_src = ""
215*387f9dfdSAndroid Build Coastguard Worker    if args.proto:
216*387f9dfdSAndroid Build Coastguard Worker        predicate = " || ".join(map(lambda x: "data.pf == NFPROTO_%s" % x, args.proto))
217*387f9dfdSAndroid Build Coastguard Worker        cond_src = "if (!(%s)) { return 0; }\n" % predicate
218*387f9dfdSAndroid Build Coastguard Worker    if args.hook:
219*387f9dfdSAndroid Build Coastguard Worker        predicate = " || ".join(map(lambda x: "data.hook == NF_INET_%s" % x, args.hook))
220*387f9dfdSAndroid Build Coastguard Worker        cond_src = "%s    if (!(%s)) { return 0;}\n" % (cond_src, predicate)
221*387f9dfdSAndroid Build Coastguard Worker
222*387f9dfdSAndroid Build Coastguard Worker    factor = "1000"
223*387f9dfdSAndroid Build Coastguard Worker    if args.nano:
224*387f9dfdSAndroid Build Coastguard Worker        factor = "1"
225*387f9dfdSAndroid Build Coastguard Worker
226*387f9dfdSAndroid Build Coastguard Worker    return BPF_SRC.replace('COND', cond_src).replace('FACTOR', factor)
227*387f9dfdSAndroid Build Coastguard Worker
228*387f9dfdSAndroid Build Coastguard Worker
229*387f9dfdSAndroid Build Coastguard Workerdef bucket_desc(bucket):
230*387f9dfdSAndroid Build Coastguard Worker    return "%s %s (tcp %s)" % (
231*387f9dfdSAndroid Build Coastguard Worker        NF_PROTOS[bucket[0]],
232*387f9dfdSAndroid Build Coastguard Worker        NF_HOOKS[bucket[1]],
233*387f9dfdSAndroid Build Coastguard Worker        TCP_FLAGS[bucket[2]])
234*387f9dfdSAndroid Build Coastguard Worker
235*387f9dfdSAndroid Build Coastguard Worker
236*387f9dfdSAndroid Build Coastguard Workerif __name__ == "__main__":
237*387f9dfdSAndroid Build Coastguard Worker    main()
238