tools/tcpconnect: add option -c to count connects
Add -c to count all active connections per dest ip/port so we can
easily spot the heavy outbound connection attempts.
# ./tcpconnect.py -c
Tracing connect ... Hit Ctrl-C to end
^C
LADDR RADDR RPORT CONNECTS
192.168.10.50 172.217.21.194 443 70
192.168.10.50 172.213.11.195 443 34
192.168.10.50 172.212.22.194 443 21
[...]
diff --git a/tools/tcpconnect.py b/tools/tcpconnect.py
index cb3e83b..eb12667 100755
--- a/tools/tcpconnect.py
+++ b/tools/tcpconnect.py
@@ -4,7 +4,7 @@
# tcpconnect Trace TCP connect()s.
# For Linux, uses BCC, eBPF. Embedded C.
#
-# USAGE: tcpconnect [-h] [-t] [-p PID] [-P PORT [PORT ...]]
+# USAGE: tcpconnect [-h] [-c] [-t] [-p PID] [-P PORT [PORT ...]]
#
# All connection attempts are traced, even if they ultimately fail.
#
@@ -17,6 +17,7 @@
# 25-Sep-2015 Brendan Gregg Created this.
# 14-Feb-2016 " " Switch to bpf_perf_output.
# 09-Jan-2019 Takuma Kume Support filtering by UID
+# 30-Jul-2019 Xiaozhou Liu Count connects.
from __future__ import print_function
from bcc import BPF
@@ -24,6 +25,7 @@
import argparse
from socket import inet_ntop, ntohs, AF_INET, AF_INET6
from struct import pack
+from time import sleep
# arguments
examples = """examples:
@@ -34,6 +36,7 @@
./tcpconnect -P 80,81 # only trace port 80 and 81
./tcpconnect -U # include UID
./tcpconnect -u 1000 # only trace UID 1000
+ ./tcpconnect -c # count connects per src ip and dest ip/port
"""
parser = argparse.ArgumentParser(
description="Trace TCP connects",
@@ -49,6 +52,8 @@
help="include UID on output")
parser.add_argument("-u", "--uid",
help="trace this UID only")
+parser.add_argument("-c", "--count", action="store_true",
+ help="count connects per src ip and dest ip/port")
parser.add_argument("--ebpf", action="store_true",
help=argparse.SUPPRESS)
args = parser.parse_args()
@@ -87,6 +92,21 @@
};
BPF_PERF_OUTPUT(ipv6_events);
+// separate flow keys per address family
+struct ipv4_flow_key_t {
+ u32 saddr;
+ u32 daddr;
+ u16 dport;
+};
+BPF_HASH(ipv4_count, struct ipv4_flow_key_t);
+
+struct ipv6_flow_key_t {
+ unsigned __int128 saddr;
+ unsigned __int128 daddr;
+ u16 dport;
+};
+BPF_HASH(ipv6_count, struct ipv6_flow_key_t);
+
int trace_connect_entry(struct pt_regs *ctx, struct sock *sk)
{
u64 pid_tgid = bpf_get_current_pid_tgid();
@@ -130,26 +150,9 @@
FILTER_PORT
if (ipver == 4) {
- struct ipv4_data_t data4 = {.pid = pid, .ip = ipver};
- data4.uid = bpf_get_current_uid_gid();
- data4.ts_us = bpf_ktime_get_ns() / 1000;
- data4.saddr = skp->__sk_common.skc_rcv_saddr;
- data4.daddr = skp->__sk_common.skc_daddr;
- data4.dport = ntohs(dport);
- bpf_get_current_comm(&data4.task, sizeof(data4.task));
- ipv4_events.perf_submit(ctx, &data4, sizeof(data4));
-
+ IPV4_CODE
} else /* 6 */ {
- struct ipv6_data_t data6 = {.pid = pid, .ip = ipver};
- data6.uid = bpf_get_current_uid_gid();
- data6.ts_us = bpf_ktime_get_ns() / 1000;
- bpf_probe_read(&data6.saddr, sizeof(data6.saddr),
- skp->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32);
- bpf_probe_read(&data6.daddr, sizeof(data6.daddr),
- skp->__sk_common.skc_v6_daddr.in6_u.u6_addr32);
- data6.dport = ntohs(dport);
- bpf_get_current_comm(&data6.task, sizeof(data6.task));
- ipv6_events.perf_submit(ctx, &data6, sizeof(data6));
+ IPV6_CODE
}
currsock.delete(&tid);
@@ -168,7 +171,58 @@
}
"""
+struct_init = { 'ipv4':
+ { 'count' :
+ """
+ struct ipv4_flow_key_t flow_key = {};
+ flow_key.saddr = skp->__sk_common.skc_rcv_saddr;
+ flow_key.daddr = skp->__sk_common.skc_daddr;
+ flow_key.dport = ntohs(dport);
+ ipv4_count.increment(flow_key);""",
+ 'trace' :
+ """
+ struct ipv4_data_t data4 = {.pid = pid, .ip = ipver};
+ data4.uid = bpf_get_current_uid_gid();
+ data4.ts_us = bpf_ktime_get_ns() / 1000;
+ data4.saddr = skp->__sk_common.skc_rcv_saddr;
+ data4.daddr = skp->__sk_common.skc_daddr;
+ data4.dport = ntohs(dport);
+ bpf_get_current_comm(&data4.task, sizeof(data4.task));
+ ipv4_events.perf_submit(ctx, &data4, sizeof(data4));"""
+ },
+ 'ipv6':
+ { 'count' :
+ """
+ struct ipv6_flow_key_t flow_key = {};
+ bpf_probe_read(&flow_key.saddr, sizeof(flow_key.saddr),
+ skp->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32);
+ bpf_probe_read(&flow_key.daddr, sizeof(flow_key.daddr),
+ skp->__sk_common.skc_v6_daddr.in6_u.u6_addr32);
+ flow_key.dport = ntohs(dport);
+ ipv6_count.increment(flow_key);""",
+ 'trace' :
+ """
+ struct ipv6_data_t data6 = {.pid = pid, .ip = ipver};
+ data6.uid = bpf_get_current_uid_gid();
+ data6.ts_us = bpf_ktime_get_ns() / 1000;
+ bpf_probe_read(&data6.saddr, sizeof(data6.saddr),
+ skp->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32);
+ bpf_probe_read(&data6.daddr, sizeof(data6.daddr),
+ skp->__sk_common.skc_v6_daddr.in6_u.u6_addr32);
+ data6.dport = ntohs(dport);
+ bpf_get_current_comm(&data6.task, sizeof(data6.task));
+ ipv6_events.perf_submit(ctx, &data6, sizeof(data6));"""
+ }
+ }
+
# code substitutions
+if args.count:
+ bpf_text = bpf_text.replace("IPV4_CODE", struct_init['ipv4']['count'])
+ bpf_text = bpf_text.replace("IPV6_CODE", struct_init['ipv6']['count'])
+else:
+ bpf_text = bpf_text.replace("IPV4_CODE", struct_init['ipv4']['trace'])
+ bpf_text = bpf_text.replace("IPV6_CODE", struct_init['ipv6']['trace'])
+
if args.pid:
bpf_text = bpf_text.replace('FILTER_PID',
'if (pid != %s) { return 0; }' % args.pid)
@@ -219,6 +273,18 @@
inet_ntop(AF_INET6, event.saddr).encode(), inet_ntop(AF_INET6, event.daddr).encode(),
event.dport))
+def depict_cnt(counts_tab, l3prot='ipv4'):
+ for k, v in sorted(counts_tab.items(), key=lambda counts: counts[1].value, reverse=True):
+ depict_key = ""
+ if l3prot == 'ipv4':
+ depict_key = "%-25s %-25s %-20s" % ((inet_ntop(AF_INET, pack('I', k.saddr))),
+ inet_ntop(AF_INET, pack('I', k.daddr)), k.dport)
+ else:
+ depict_key = "%-25s %-25s %-20s" % ((inet_ntop(AF_INET6, k.saddr)),
+ inet_ntop(AF_INET6, k.daddr), k.dport)
+
+ print ("%s %-10d" % (depict_key, v.value))
+
# initialize BPF
b = BPF(text=bpf_text)
b.attach_kprobe(event="tcp_v4_connect", fn_name="trace_connect_entry")
@@ -226,21 +292,36 @@
b.attach_kretprobe(event="tcp_v4_connect", fn_name="trace_connect_v4_return")
b.attach_kretprobe(event="tcp_v6_connect", fn_name="trace_connect_v6_return")
-# header
-if args.timestamp:
- print("%-9s" % ("TIME(s)"), end="")
-if args.print_uid:
- print("%-6s" % ("UID"), end="")
-print("%-6s %-12s %-2s %-16s %-16s %-4s" % ("PID", "COMM", "IP", "SADDR",
- "DADDR", "DPORT"))
-
-start_ts = 0
-
-# read events
-b["ipv4_events"].open_perf_buffer(print_ipv4_event)
-b["ipv6_events"].open_perf_buffer(print_ipv6_event)
-while 1:
+print("Tracing connect ... Hit Ctrl-C to end")
+if args.count:
try:
- b.perf_buffer_poll()
+ while 1:
+ sleep(99999999)
except KeyboardInterrupt:
- exit()
+ pass
+
+ # header
+ print("\n%-25s %-25s %-20s %-10s" % (
+ "LADDR", "RADDR", "RPORT", "CONNECTS"))
+ depict_cnt(b["ipv4_count"])
+ depict_cnt(b["ipv6_count"], l3prot='ipv6')
+# read events
+else:
+ # header
+ if args.timestamp:
+ print("%-9s" % ("TIME(s)"), end="")
+ if args.print_uid:
+ print("%-6s" % ("UID"), end="")
+ print("%-6s %-12s %-2s %-16s %-16s %-4s" % ("PID", "COMM", "IP", "SADDR",
+ "DADDR", "DPORT"))
+
+ start_ts = 0
+
+ # read events
+ b["ipv4_events"].open_perf_buffer(print_ipv4_event)
+ b["ipv6_events"].open_perf_buffer(print_ipv6_event)
+ while 1:
+ try:
+ b.perf_buffer_poll()
+ except KeyboardInterrupt:
+ exit()