blob: 35a8295e408a173489126f53f91ec2926a1bd2c6 [file] [log] [blame]
Alexey Ivanovcc01a9c2019-01-16 09:50:46 -08001#!/usr/bin/python
Jean-Tiare Le Bigota1ac2f92016-03-04 21:45:32 +01002#
Sasha Goldshteinf41ae862016-10-19 01:14:30 +03003# solisten Trace TCP listen events
4# For Linux, uses BCC, eBPF. Embedded C.
Jean-Tiare Le Bigota1ac2f92016-03-04 21:45:32 +01005#
6# USAGE: solisten.py [-h] [-p PID] [--show-netns]
7#
8# This is provided as a basic example of TCP connection & socket tracing.
Edward Bettsfdf9b082017-10-10 21:13:28 +01009# It could be useful in scenarios where load balancers needs to be updated
Jean-Tiare Le Bigota1ac2f92016-03-04 21:45:32 +010010# dynamically as application is fully initialized.
11#
Jerome Marchand33c9c572019-07-29 15:57:03 +020012# All IPv4 and IPv6 listen attempts are traced, even if they ultimately fail
13# or the the listening program is not willing to accept().
Jean-Tiare Le Bigota1ac2f92016-03-04 21:45:32 +010014#
15# Copyright (c) 2016 Jean-Tiare Le Bigot.
16# Licensed under the Apache License, Version 2.0 (the "License")
17#
18# 04-Mar-2016 Jean-Tiare Le Bigot Created this.
19
20import os
Paul Chaignon83320682017-03-25 13:40:46 +010021from socket import inet_ntop, AF_INET, AF_INET6, SOCK_STREAM, SOCK_DGRAM
22from struct import pack
Jean-Tiare Le Bigota1ac2f92016-03-04 21:45:32 +010023import argparse
24from bcc import BPF
japrocaed9b1e2019-01-04 20:21:46 +030025from bcc.utils import printb
Jean-Tiare Le Bigota1ac2f92016-03-04 21:45:32 +010026
27# Arguments
28examples = """Examples:
29 ./solisten.py # Stream socket listen
30 ./solisten.py -p 1234 # Stream socket listen for specified PID only
Sasha Goldshteinf41ae862016-10-19 01:14:30 +030031 ./solisten.py --netns 4242 # " for the specified network namespace ID only
32 ./solisten.py --show-netns # Show network ns ID (useful for containers)
Jean-Tiare Le Bigota1ac2f92016-03-04 21:45:32 +010033"""
34
35parser = argparse.ArgumentParser(
36 description="Stream sockets listen",
37 formatter_class=argparse.RawDescriptionHelpFormatter,
38 epilog=examples)
39parser.add_argument("--show-netns", action="store_true",
40 help="show network namespace")
41parser.add_argument("-p", "--pid", default=0, type=int,
42 help="trace this PID only")
43parser.add_argument("-n", "--netns", default=0, type=int,
44 help="trace this Network Namespace only")
Nathan Scottcf0792f2018-02-02 16:56:50 +110045parser.add_argument("--ebpf", action="store_true",
46 help=argparse.SUPPRESS)
Jean-Tiare Le Bigota1ac2f92016-03-04 21:45:32 +010047
48
49# BPF Program
Sasha Goldshteinf41ae862016-10-19 01:14:30 +030050bpf_text = """
Jean-Tiare Le Bigota1ac2f92016-03-04 21:45:32 +010051#include <net/net_namespace.h>
52#include <bcc/proto.h>
Jean-Tiare Le Bigoted5c3812017-11-18 20:57:37 +010053#pragma clang diagnostic push
54#pragma clang diagnostic ignored "-Wenum-conversion"
55#include <net/inet_sock.h>
56#pragma clang diagnostic pop
Jean-Tiare Le Bigota1ac2f92016-03-04 21:45:32 +010057
Jean-Tiare Le Bigota1ac2f92016-03-04 21:45:32 +010058// Common structure for UDP/TCP IPv4/IPv6
59struct listen_evt_t {
60 u64 ts_us;
Hengqi Chen151fe192021-05-16 17:18:27 +080061 u64 pid;
Jean-Tiare Le Bigota1ac2f92016-03-04 21:45:32 +010062 u64 backlog;
63 u64 netns;
64 u64 proto; // familiy << 16 | type
65 u64 lport; // use only 16 bits
66 u64 laddr[2]; // IPv4: store in laddr[0]
67 char task[TASK_COMM_LEN];
68};
69BPF_PERF_OUTPUT(listen_evt);
70
71// Send an event for each IPv4 listen with PID, bound address and port
72int kprobe__inet_listen(struct pt_regs *ctx, struct socket *sock, int backlog)
73{
74 // cast types. Intermediate cast not needed, kept for readability
75 struct sock *sk = sock->sk;
Paul Chaignon23ac4632017-08-06 11:12:26 +020076 struct inet_sock *inet = (struct inet_sock *)sk;
Jean-Tiare Le Bigota1ac2f92016-03-04 21:45:32 +010077
78 // Built event for userland
79 struct listen_evt_t evt = {
80 .ts_us = bpf_ktime_get_ns() / 1000,
81 .backlog = backlog,
82 };
83
Sasha Goldshteinf41ae862016-10-19 01:14:30 +030084 // Get process comm. Needs LLVM >= 3.7.1
85 // see https://github.com/iovisor/bcc/issues/393
Jean-Tiare Le Bigota1ac2f92016-03-04 21:45:32 +010086 bpf_get_current_comm(evt.task, TASK_COMM_LEN);
87
88 // Get socket IP family
89 u16 family = sk->__sk_common.skc_family;
90 evt.proto = family << 16 | SOCK_STREAM;
91
92 // Get PID
Hengqi Chen151fe192021-05-16 17:18:27 +080093 evt.pid = bpf_get_current_pid_tgid() >> 32;
Jean-Tiare Le Bigota1ac2f92016-03-04 21:45:32 +010094
95 ##FILTER_PID##
96
97 // Get port
Paul Chaignon23ac4632017-08-06 11:12:26 +020098 evt.lport = inet->inet_sport;
Jean-Tiare Le Bigota1ac2f92016-03-04 21:45:32 +010099 evt.lport = ntohs(evt.lport);
100
101 // Get network namespace id, if kernel supports it
102#ifdef CONFIG_NET_NS
103 evt.netns = sk->__sk_common.skc_net.net->ns.inum;
104#else
105 evt.netns = 0;
106#endif
107
108 ##FILTER_NETNS##
109
110 // Get IP
111 if (family == AF_INET) {
Paul Chaignon23ac4632017-08-06 11:12:26 +0200112 evt.laddr[0] = inet->inet_rcv_saddr;
Jean-Tiare Le Bigota1ac2f92016-03-04 21:45:32 +0100113 } else if (family == AF_INET6) {
Sumanth Korikkar7f6066d2020-05-20 10:49:56 -0500114 bpf_probe_read_kernel(evt.laddr, sizeof(evt.laddr),
Sasha Goldshteinf41ae862016-10-19 01:14:30 +0300115 sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32);
Jean-Tiare Le Bigota1ac2f92016-03-04 21:45:32 +0100116 }
117
118 // Send event to userland
119 listen_evt.perf_submit(ctx, &evt, sizeof(evt));
120
121 return 0;
122};
123"""
124
Jean-Tiare Le Bigota1ac2f92016-03-04 21:45:32 +0100125 # TODO: properties to unpack protocol / ip / pid / tgid ...
126
127# Format output
128def event_printer(show_netns):
129 def print_event(cpu, data, size):
130 # Decode event
Xiaozhou Liu51d62d32019-02-15 13:03:05 +0800131 event = b["listen_evt"].event(data)
Jean-Tiare Le Bigota1ac2f92016-03-04 21:45:32 +0100132
Hengqi Chen151fe192021-05-16 17:18:27 +0800133 pid = event.pid
Jean-Tiare Le Bigota1ac2f92016-03-04 21:45:32 +0100134 proto_family = event.proto & 0xff
135 proto_type = event.proto >> 16 & 0xff
136
Paul Chaignon83320682017-03-25 13:40:46 +0100137 if proto_family == SOCK_STREAM:
Jean-Tiare Le Bigota1ac2f92016-03-04 21:45:32 +0100138 protocol = "TCP"
Paul Chaignon83320682017-03-25 13:40:46 +0100139 elif proto_family == SOCK_DGRAM:
Jean-Tiare Le Bigota1ac2f92016-03-04 21:45:32 +0100140 protocol = "UDP"
141 else:
142 protocol = "UNK"
143
144 address = ""
Paul Chaignon83320682017-03-25 13:40:46 +0100145 if proto_type == AF_INET:
Jean-Tiare Le Bigota1ac2f92016-03-04 21:45:32 +0100146 protocol += "v4"
Paul Chaignon83320682017-03-25 13:40:46 +0100147 address = inet_ntop(AF_INET, pack("I", event.laddr[0]))
148 elif proto_type == AF_INET6:
149 address = inet_ntop(AF_INET6, event.laddr)
Jean-Tiare Le Bigota1ac2f92016-03-04 21:45:32 +0100150 protocol += "v6"
151
152 # Display
153 if show_netns:
Hengqi Chen151fe192021-05-16 17:18:27 +0800154 printb(b"%-7d %-12.12s %-12d %-6s %-8d %-5d %-39s" % (
Jerome Marchand6af649a2019-07-29 16:09:59 +0200155 pid, event.task, event.netns, protocol.encode(), event.backlog,
156 event.lport, address.encode(),
Jean-Tiare Le Bigota1ac2f92016-03-04 21:45:32 +0100157 ))
158 else:
Hengqi Chen151fe192021-05-16 17:18:27 +0800159 printb(b"%-7d %-12.12s %-6s %-8d %-5d %-39s" % (
Jerome Marchand6af649a2019-07-29 16:09:59 +0200160 pid, event.task, protocol.encode(), event.backlog,
161 event.lport, address.encode(),
Jean-Tiare Le Bigota1ac2f92016-03-04 21:45:32 +0100162 ))
163
164 return print_event
165
166if __name__ == "__main__":
167 # Parse arguments
168 args = parser.parse_args()
169
170 pid_filter = ""
171 netns_filter = ""
172
173 if args.pid:
Hengqi Chen151fe192021-05-16 17:18:27 +0800174 pid_filter = "if (evt.pid != %d) return 0;" % args.pid
Jean-Tiare Le Bigota1ac2f92016-03-04 21:45:32 +0100175 if args.netns:
176 netns_filter = "if (evt.netns != %d) return 0;" % args.netns
177
178 bpf_text = bpf_text.replace("##FILTER_PID##", pid_filter)
179 bpf_text = bpf_text.replace("##FILTER_NETNS##", netns_filter)
180
Nathan Scottcf0792f2018-02-02 16:56:50 +1100181 if args.ebpf:
182 print(bpf_text)
183 exit()
184
Jean-Tiare Le Bigota1ac2f92016-03-04 21:45:32 +0100185 # Initialize BPF
186 b = BPF(text=bpf_text)
187 b["listen_evt"].open_perf_buffer(event_printer(args.show_netns))
188
189 # Print headers
190 if args.show_netns:
Hengqi Chen151fe192021-05-16 17:18:27 +0800191 print("%-7s %-12s %-12s %-6s %-8s %-5s %-39s" %
Sasha Goldshteinf41ae862016-10-19 01:14:30 +0300192 ("PID", "COMM", "NETNS", "PROTO", "BACKLOG", "PORT", "ADDR"))
Jean-Tiare Le Bigota1ac2f92016-03-04 21:45:32 +0100193 else:
Hengqi Chen151fe192021-05-16 17:18:27 +0800194 print("%-7s %-12s %-6s %-8s %-5s %-39s" %
Sasha Goldshteinf41ae862016-10-19 01:14:30 +0300195 ("PID", "COMM", "PROTO", "BACKLOG", "PORT", "ADDR"))
Jean-Tiare Le Bigota1ac2f92016-03-04 21:45:32 +0100196
197 # Read events
198 while 1:
Jerome Marchand51671272018-12-19 01:57:24 +0100199 try:
200 b.perf_buffer_poll()
201 except KeyboardInterrupt:
202 exit()