blob: 81e82e0413fcf8b85861bf684ff95ef403c64325 [file] [log] [blame]
Jean-Tiare Le Bigota1ac2f92016-03-04 21:45:32 +01001#!/usr/bin/env python
2#
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#
12# All IPv4 listen attempts are traced, even if they ultimately fail or the
13# the listening program is not willing to accept().
14#
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
25import ctypes as ct
japrocaed9b1e2019-01-04 20:21:46 +030026from bcc.utils import printb
Jean-Tiare Le Bigota1ac2f92016-03-04 21:45:32 +010027
28# Arguments
29examples = """Examples:
30 ./solisten.py # Stream socket listen
31 ./solisten.py -p 1234 # Stream socket listen for specified PID only
Sasha Goldshteinf41ae862016-10-19 01:14:30 +030032 ./solisten.py --netns 4242 # " for the specified network namespace ID only
33 ./solisten.py --show-netns # Show network ns ID (useful for containers)
Jean-Tiare Le Bigota1ac2f92016-03-04 21:45:32 +010034"""
35
36parser = argparse.ArgumentParser(
37 description="Stream sockets listen",
38 formatter_class=argparse.RawDescriptionHelpFormatter,
39 epilog=examples)
40parser.add_argument("--show-netns", action="store_true",
41 help="show network namespace")
42parser.add_argument("-p", "--pid", default=0, type=int,
43 help="trace this PID only")
44parser.add_argument("-n", "--netns", default=0, type=int,
45 help="trace this Network Namespace only")
Nathan Scottcf0792f2018-02-02 16:56:50 +110046parser.add_argument("--ebpf", action="store_true",
47 help=argparse.SUPPRESS)
Jean-Tiare Le Bigota1ac2f92016-03-04 21:45:32 +010048
49
50# BPF Program
Sasha Goldshteinf41ae862016-10-19 01:14:30 +030051bpf_text = """
Jean-Tiare Le Bigota1ac2f92016-03-04 21:45:32 +010052#include <net/net_namespace.h>
53#include <bcc/proto.h>
Jean-Tiare Le Bigoted5c3812017-11-18 20:57:37 +010054#pragma clang diagnostic push
55#pragma clang diagnostic ignored "-Wenum-conversion"
56#include <net/inet_sock.h>
57#pragma clang diagnostic pop
Jean-Tiare Le Bigota1ac2f92016-03-04 21:45:32 +010058
Jean-Tiare Le Bigota1ac2f92016-03-04 21:45:32 +010059// Common structure for UDP/TCP IPv4/IPv6
60struct listen_evt_t {
61 u64 ts_us;
62 u64 pid_tgid;
63 u64 backlog;
64 u64 netns;
65 u64 proto; // familiy << 16 | type
66 u64 lport; // use only 16 bits
67 u64 laddr[2]; // IPv4: store in laddr[0]
68 char task[TASK_COMM_LEN];
69};
70BPF_PERF_OUTPUT(listen_evt);
71
72// Send an event for each IPv4 listen with PID, bound address and port
73int kprobe__inet_listen(struct pt_regs *ctx, struct socket *sock, int backlog)
74{
75 // cast types. Intermediate cast not needed, kept for readability
76 struct sock *sk = sock->sk;
Paul Chaignon23ac4632017-08-06 11:12:26 +020077 struct inet_sock *inet = (struct inet_sock *)sk;
Jean-Tiare Le Bigota1ac2f92016-03-04 21:45:32 +010078
79 // Built event for userland
80 struct listen_evt_t evt = {
81 .ts_us = bpf_ktime_get_ns() / 1000,
82 .backlog = backlog,
83 };
84
Sasha Goldshteinf41ae862016-10-19 01:14:30 +030085 // Get process comm. Needs LLVM >= 3.7.1
86 // see https://github.com/iovisor/bcc/issues/393
Jean-Tiare Le Bigota1ac2f92016-03-04 21:45:32 +010087 bpf_get_current_comm(evt.task, TASK_COMM_LEN);
88
89 // Get socket IP family
90 u16 family = sk->__sk_common.skc_family;
91 evt.proto = family << 16 | SOCK_STREAM;
92
93 // Get PID
94 evt.pid_tgid = bpf_get_current_pid_tgid();
95
96 ##FILTER_PID##
97
98 // Get port
Paul Chaignon23ac4632017-08-06 11:12:26 +020099 evt.lport = inet->inet_sport;
Jean-Tiare Le Bigota1ac2f92016-03-04 21:45:32 +0100100 evt.lport = ntohs(evt.lport);
101
102 // Get network namespace id, if kernel supports it
103#ifdef CONFIG_NET_NS
104 evt.netns = sk->__sk_common.skc_net.net->ns.inum;
105#else
106 evt.netns = 0;
107#endif
108
109 ##FILTER_NETNS##
110
111 // Get IP
112 if (family == AF_INET) {
Paul Chaignon23ac4632017-08-06 11:12:26 +0200113 evt.laddr[0] = inet->inet_rcv_saddr;
Jean-Tiare Le Bigota1ac2f92016-03-04 21:45:32 +0100114 } else if (family == AF_INET6) {
Sasha Goldshteinf41ae862016-10-19 01:14:30 +0300115 bpf_probe_read(evt.laddr, sizeof(evt.laddr),
116 sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32);
Jean-Tiare Le Bigota1ac2f92016-03-04 21:45:32 +0100117 }
118
119 // Send event to userland
120 listen_evt.perf_submit(ctx, &evt, sizeof(evt));
121
122 return 0;
123};
124"""
125
126# event data
127TASK_COMM_LEN = 16 # linux/sched.h
128class ListenEvt(ct.Structure):
129 _fields_ = [
130 ("ts_us", ct.c_ulonglong),
131 ("pid_tgid", ct.c_ulonglong),
132 ("backlog", ct.c_ulonglong),
133 ("netns", ct.c_ulonglong),
134 ("proto", ct.c_ulonglong),
135 ("lport", ct.c_ulonglong),
136 ("laddr", ct.c_ulonglong * 2),
137 ("task", ct.c_char * TASK_COMM_LEN)
138 ]
139
140 # TODO: properties to unpack protocol / ip / pid / tgid ...
141
142# Format output
143def event_printer(show_netns):
144 def print_event(cpu, data, size):
145 # Decode event
146 event = ct.cast(data, ct.POINTER(ListenEvt)).contents
147
148 pid = event.pid_tgid & 0xffffffff
149 proto_family = event.proto & 0xff
150 proto_type = event.proto >> 16 & 0xff
151
Paul Chaignon83320682017-03-25 13:40:46 +0100152 if proto_family == SOCK_STREAM:
Jean-Tiare Le Bigota1ac2f92016-03-04 21:45:32 +0100153 protocol = "TCP"
Paul Chaignon83320682017-03-25 13:40:46 +0100154 elif proto_family == SOCK_DGRAM:
Jean-Tiare Le Bigota1ac2f92016-03-04 21:45:32 +0100155 protocol = "UDP"
156 else:
157 protocol = "UNK"
158
159 address = ""
Paul Chaignon83320682017-03-25 13:40:46 +0100160 if proto_type == AF_INET:
Jean-Tiare Le Bigota1ac2f92016-03-04 21:45:32 +0100161 protocol += "v4"
Paul Chaignon83320682017-03-25 13:40:46 +0100162 address = inet_ntop(AF_INET, pack("I", event.laddr[0]))
163 elif proto_type == AF_INET6:
164 address = inet_ntop(AF_INET6, event.laddr)
Jean-Tiare Le Bigota1ac2f92016-03-04 21:45:32 +0100165 protocol += "v6"
166
167 # Display
168 if show_netns:
japrocaed9b1e2019-01-04 20:21:46 +0300169 printb(b"%-6d %-12.12s %-12s %-6s %-8s %-5s %-39s" % (
Jean-Tiare Le Bigota1ac2f92016-03-04 21:45:32 +0100170 pid, event.task, event.netns, protocol, event.backlog,
171 event.lport, address,
172 ))
173 else:
japrocaed9b1e2019-01-04 20:21:46 +0300174 printb(b"%-6d %-12.12s %-6s %-8s %-5s %-39s" % (
Jean-Tiare Le Bigota1ac2f92016-03-04 21:45:32 +0100175 pid, event.task, protocol, event.backlog,
176 event.lport, address,
177 ))
178
179 return print_event
180
181if __name__ == "__main__":
182 # Parse arguments
183 args = parser.parse_args()
184
185 pid_filter = ""
186 netns_filter = ""
187
188 if args.pid:
189 pid_filter = "if (evt.pid_tgid != %d) return 0;" % args.pid
190 if args.netns:
191 netns_filter = "if (evt.netns != %d) return 0;" % args.netns
192
193 bpf_text = bpf_text.replace("##FILTER_PID##", pid_filter)
194 bpf_text = bpf_text.replace("##FILTER_NETNS##", netns_filter)
195
Nathan Scottcf0792f2018-02-02 16:56:50 +1100196 if args.ebpf:
197 print(bpf_text)
198 exit()
199
Jean-Tiare Le Bigota1ac2f92016-03-04 21:45:32 +0100200 # Initialize BPF
201 b = BPF(text=bpf_text)
202 b["listen_evt"].open_perf_buffer(event_printer(args.show_netns))
203
204 # Print headers
205 if args.show_netns:
Sasha Goldshteinf41ae862016-10-19 01:14:30 +0300206 print("%-6s %-12s %-12s %-6s %-8s %-5s %-39s" %
207 ("PID", "COMM", "NETNS", "PROTO", "BACKLOG", "PORT", "ADDR"))
Jean-Tiare Le Bigota1ac2f92016-03-04 21:45:32 +0100208 else:
Sasha Goldshteinf41ae862016-10-19 01:14:30 +0300209 print("%-6s %-12s %-6s %-8s %-5s %-39s" %
210 ("PID", "COMM", "PROTO", "BACKLOG", "PORT", "ADDR"))
Jean-Tiare Le Bigota1ac2f92016-03-04 21:45:32 +0100211
212 # Read events
213 while 1:
Jerome Marchand51671272018-12-19 01:57:24 +0100214 try:
215 b.perf_buffer_poll()
216 except KeyboardInterrupt:
217 exit()