enhance tools/tcpaccept (#2254)
- add option `-T': include time column on output (HH:MM:SS)
- add option `-P PORT': only trace port(s) specified
- add RPORT colume on output
diff --git a/man/man8/tcpaccept.8 b/man/man8/tcpaccept.8
index 837717b..6e340bd 100644
--- a/man/man8/tcpaccept.8
+++ b/man/man8/tcpaccept.8
@@ -1,8 +1,8 @@
-.TH tcpaccept 8 "2015-08-25" "USER COMMANDS"
+.TH tcpaccept 8 "2019-03-08" "USER COMMANDS"
.SH NAME
tcpaccept \- Trace TCP passive connections (accept()). Uses Linux eBPF/bcc.
.SH SYNOPSIS
-.B tcpaccept [\-h] [\-t] [\-x] [\-p PID]
+.B tcpaccept [\-h] [\-T] [\-t] [\-p PID] [\-P PORTS]
.SH DESCRIPTION
This tool traces passive TCP connections (eg, via an accept() syscall;
connect() are active connections). This can be useful for general
@@ -22,11 +22,17 @@
\-h
Print usage message.
.TP
+\-T
+Include a time column on output (HH:MM:SS).
+.TP
\-t
Include a timestamp column.
.TP
\-p PID
Trace this process ID only (filtered in-kernel).
+.TP
+\-P PORTS
+Comma-separated list of local ports to trace (filtered in-kernel).
.SH EXAMPLES
.TP
Trace all passive TCP connections (accept()s):
@@ -37,11 +43,18 @@
#
.B tcpaccept \-t
.TP
+Trace connections to local ports 80 and 81 only:
+#
+.B tcpaccept \-P 80,81
+.TP
Trace PID 181 only:
#
.B tcpaccept \-p 181
.SH FIELDS
.TP
+TIME
+Time of the event, in HH:MM:SS format.
+.TP
TIME(s)
Time of the event, in seconds.
.TP
@@ -57,6 +70,9 @@
RADDR
Remote IP address.
.TP
+RPORT
+Remote port
+.TP
LADDR
Local IP address.
.TP
diff --git a/tools/tcpaccept.py b/tools/tcpaccept.py
index f606b73..169b0f3 100755
--- a/tools/tcpaccept.py
+++ b/tools/tcpaccept.py
@@ -4,7 +4,7 @@
# tcpaccept Trace TCP accept()s.
# For Linux, uses BCC, eBPF. Embedded C.
#
-# USAGE: tcpaccept [-h] [-t] [-p PID]
+# USAGE: tcpaccept [-h] [-T] [-t] [-p PID] [-P PORTS]
#
# This uses dynamic tracing of the kernel inet_csk_accept() socket function
# (from tcp_prot.accept), and will need to be modified to match kernel changes.
@@ -21,21 +21,27 @@
from struct import pack
import argparse
from bcc.utils import printb
+from time import strftime
# arguments
examples = """examples:
./tcpaccept # trace all TCP accept()s
./tcpaccept -t # include timestamps
+ ./tcpaccept -P 80,81 # only trace port 80 and 81
./tcpaccept -p 181 # only trace PID 181
"""
parser = argparse.ArgumentParser(
description="Trace TCP accepts",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=examples)
+parser.add_argument("-T", "--time", action="store_true",
+ help="include time column on output (HH:MM:SS)")
parser.add_argument("-t", "--timestamp", action="store_true",
help="include timestamp on output")
parser.add_argument("-p", "--pid",
help="trace this PID only")
+parser.add_argument("-P", "--port",
+ help="comma-separated list of local ports to trace")
parser.add_argument("--ebpf", action="store_true",
help=argparse.SUPPRESS)
args = parser.parse_args()
@@ -55,6 +61,7 @@
u32 daddr;
u64 ip;
u16 lport;
+ u16 dport;
char task[TASK_COMM_LEN];
};
BPF_PERF_OUTPUT(ipv4_events);
@@ -66,6 +73,7 @@
unsigned __int128 daddr;
u64 ip;
u16 lport;
+ u16 dport;
char task[TASK_COMM_LEN];
};
BPF_PERF_OUTPUT(ipv6_events);
@@ -126,9 +134,13 @@
return 0;
// pull in details
- u16 family = 0, lport = 0;
+ u16 family = 0, lport = 0, dport;
family = newsk->__sk_common.skc_family;
lport = newsk->__sk_common.skc_num;
+ dport = newsk->__sk_common.skc_dport;
+ dport = ntohs(dport);
+
+ ##FILTER_PORT##
if (family == AF_INET) {
struct ipv4_data_t data4 = {.pid = pid, .ip = 4};
@@ -136,6 +148,7 @@
data4.saddr = newsk->__sk_common.skc_rcv_saddr;
data4.daddr = newsk->__sk_common.skc_daddr;
data4.lport = lport;
+ data4.dport = dport;
bpf_get_current_comm(&data4.task, sizeof(data4.task));
ipv4_events.perf_submit(ctx, &data4, sizeof(data4));
@@ -147,6 +160,7 @@
bpf_probe_read(&data6.daddr, sizeof(data6.daddr),
&newsk->__sk_common.skc_v6_daddr.in6_u.u6_addr32);
data6.lport = lport;
+ data6.dport = dport;
bpf_get_current_comm(&data6.task, sizeof(data6.task));
ipv6_events.perf_submit(ctx, &data6, sizeof(data6));
}
@@ -168,9 +182,12 @@
##FILTER_PID##
// pull in details
- u16 family = 0, lport = 0;
+ u16 family = 0, lport = 0, dport;
family = args->family;
lport = args->sport;
+ dport = args->dport;
+
+ ##FILTER_PORT##
if (family == AF_INET) {
struct ipv4_data_t data4 = {.pid = pid, .ip = 4};
@@ -178,6 +195,7 @@
__builtin_memcpy(&data4.saddr, args->saddr, sizeof(data4.saddr));
__builtin_memcpy(&data4.daddr, args->daddr, sizeof(data4.daddr));
data4.lport = lport;
+ data4.dport = dport;
bpf_get_current_comm(&data4.task, sizeof(data4.task));
ipv4_events.perf_submit(args, &data4, sizeof(data4));
} else if (family == AF_INET6) {
@@ -186,6 +204,7 @@
__builtin_memcpy(&data6.saddr, args->saddr, sizeof(data6.saddr));
__builtin_memcpy(&data6.daddr, args->daddr, sizeof(data6.daddr));
data6.lport = lport;
+ data6.dport = dport;
bpf_get_current_comm(&data6.task, sizeof(data6.task));
ipv6_events.perf_submit(args, &data6, sizeof(data6));
}
@@ -207,35 +226,48 @@
'if (pid != %s) { return 0; }' % args.pid)
else:
bpf_text = bpf_text.replace('##FILTER_PID##', '')
+if args.port:
+ lports = [int(lport) for lport in args.port.split(',')]
+ lports_if = ' && '.join(['lport != %d' % lport for lport in lports])
+ bpf_text = bpf_text.replace('##FILTER_PORT##',
+ 'if (%s) { return 0; }' % lports_if)
if debug or args.ebpf:
print(bpf_text)
if args.ebpf:
exit()
+bpf_text = bpf_text.replace('##FILTER_PORT##', '')
+
# process event
def print_ipv4_event(cpu, data, size):
event = b["ipv4_events"].event(data)
global start_ts
+ if args.time:
+ print("%-9s" % strftime("%H:%M:%S"), end="")
if args.timestamp:
if start_ts == 0:
start_ts = event.ts_us
print("%-9.3f" % ((float(event.ts_us) - start_ts) / 1000000), end="")
- printb(b"%-6d %-12.12s %-2d %-16s %-16s %-4d" % (event.pid,
+ printb(b"%-7d %-12.12s %-2d %-16s %-5d %-16s %-5d" % (event.pid,
event.task, event.ip,
inet_ntop(AF_INET, pack("I", event.daddr)).encode(),
+ event.dport,
inet_ntop(AF_INET, pack("I", event.saddr)).encode(),
event.lport))
def print_ipv6_event(cpu, data, size):
event = b["ipv6_events"].event(data)
global start_ts
+ if args.time:
+ print("%-9s" % strftime("%H:%M:%S"), end="")
if args.timestamp:
if start_ts == 0:
start_ts = event.ts_us
print("%-9.3f" % ((float(event.ts_us) - start_ts) / 1000000), end="")
- printb(b"%-6d %-12.12s %-2d %-16s %-16s %-4d" % (event.pid,
+ printb(b"%-7d %-12.12s %-2d %-16s %-5d %-16s %-5d" % (event.pid,
event.task, event.ip,
inet_ntop(AF_INET6, event.daddr).encode(),
+ event.dport,
inet_ntop(AF_INET6, event.saddr).encode(),
event.lport))
@@ -243,10 +275,12 @@
b = BPF(text=bpf_text)
# header
+if args.time:
+ print("%-9s" % ("TIME"), end="")
if args.timestamp:
print("%-9s" % ("TIME(s)"), end="")
-print("%-6s %-12s %-2s %-16s %-16s %-4s" % ("PID", "COMM", "IP", "RADDR",
- "LADDR", "LPORT"))
+print("%-7s %-12s %-2s %-16s %-5s %-16s %-5s" % ("PID", "COMM", "IP", "RADDR",
+ "RPORT", "LADDR", "LPORT"))
start_ts = 0
diff --git a/tools/tcpaccept_example.txt b/tools/tcpaccept_example.txt
index f86c439..2adee45 100644
--- a/tools/tcpaccept_example.txt
+++ b/tools/tcpaccept_example.txt
@@ -6,10 +6,10 @@
addresses changed to protect the innocent):
# ./tcpaccept
-PID COMM IP RADDR LADDR LPORT
-907 sshd 4 192.168.56.1 192.168.56.102 22
-907 sshd 4 127.0.0.1 127.0.0.1 22
-5389 perl 6 1234:ab12:2040:5020:2299:0:5:0 1234:ab12:2040:5020:2299:0:5:0 7001
+PID COMM IP RADDR RPORT LADDR LPORT
+907 sshd 4 192.168.56.1 32324 192.168.56.102 22
+907 sshd 4 127.0.0.1 39866 127.0.0.1 22
+5389 perl 6 1234:ab12:2040:5020:2299:0:5:0 52352 1234:ab12:2040:5020:2299:0:5:0 7001
This output shows three connections, two IPv4 connections to PID 907, an "sshd"
process listening on port 22, and one IPv6 connection to a "perl" process
@@ -26,26 +26,29 @@
The -t option prints a timestamp column:
# ./tcpaccept -t
-TIME(s) PID COMM IP RADDR LADDR LPORT
-0.000 907 sshd 4 127.0.0.1 127.0.0.1 22
-0.010 5389 perl 6 1234:ab12:2040:5020:2299:0:5:0 1234:ab12:2040:5020:2299:0:5:0 7001
-0.992 907 sshd 4 127.0.0.1 127.0.0.1 22
-1.984 907 sshd 4 127.0.0.1 127.0.0.1 22
+TIME(s) PID COMM IP RADDR RPORT LADDR LPORT
+0.000 907 sshd 4 127.0.0.1 53700 127.0.0.1 22
+0.010 5389 perl 6 1234:ab12:2040:5020:2299:0:5:0 40614 1234:ab12:2040:5020:2299:0:5:0 7001
+0.992 907 sshd 4 127.0.0.1 32548 127.0.0.1 22
+1.984 907 sshd 4 127.0.0.1 51250 127.0.0.1 22
USAGE message:
# ./tcpaccept -h
-usage: tcpaccept [-h] [-t] [-p PID]
+usage: tcpaccept [-h] [-T] [-t] [-p PID] [-P PORTS]
Trace TCP accepts
optional arguments:
- -h, --help show this help message and exit
- -t, --timestamp include timestamp on output
- -p PID, --pid PID trace this PID only
+ -h, --help show this help message and exit
+ -T, --time include time column on output (HH:MM:SS)
+ -t, --timestamp include timestamp on output
+ -p PID, --pid PID trace this PID only
+ -P PORTS, --port PORTS comma-separated list of local ports to trace
examples:
./tcpaccept # trace all TCP accept()s
./tcpaccept -t # include timestamps
+ ./tcpaccept -P 80,81 # only trace port 80 and 81
./tcpaccept -p 181 # only trace PID 181