Skip to content

Commit

Permalink
enhance tools/tcpaccept (iovisor#2254)
Browse files Browse the repository at this point in the history
- 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
  • Loading branch information
boat0 authored and yonghong-song committed Mar 8, 2019
1 parent 1b76d6c commit 701bd73
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 22 deletions.
20 changes: 18 additions & 2 deletions man/man8/tcpaccept.8
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -22,11 +22,17 @@ CONFIG_BPF and bcc.
\-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):
Expand All @@ -37,11 +43,18 @@ Trace all TCP accepts, and include timestamps:
#
.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
Expand All @@ -57,6 +70,9 @@ IP address family (4 or 6)
RADDR
Remote IP address.
.TP
RPORT
Remote port
.TP
LADDR
Local IP address.
.TP
Expand Down
48 changes: 41 additions & 7 deletions tools/tcpaccept.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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()
Expand All @@ -55,6 +61,7 @@
u32 daddr;
u64 ip;
u16 lport;
u16 dport;
char task[TASK_COMM_LEN];
};
BPF_PERF_OUTPUT(ipv4_events);
Expand All @@ -66,6 +73,7 @@
unsigned __int128 daddr;
u64 ip;
u16 lport;
u16 dport;
char task[TASK_COMM_LEN];
};
BPF_PERF_OUTPUT(ipv6_events);
Expand Down Expand Up @@ -126,16 +134,21 @@
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};
data4.ts_us = bpf_ktime_get_ns() / 1000;
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));
Expand All @@ -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));
}
Expand All @@ -168,16 +182,20 @@
##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};
data4.ts_us = bpf_ktime_get_ns() / 1000;
__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) {
Expand All @@ -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));
}
Expand All @@ -207,46 +226,61 @@
'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))

# initialize BPF
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

Expand Down
29 changes: 16 additions & 13 deletions tools/tcpaccept_example.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ passive connection via accept(); not connect()). Some example output (IP
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
Expand All @@ -26,26 +26,29 @@ ports will not be shown (those can be traced via other functions).
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

0 comments on commit 701bd73

Please sign in to comment.