diff --git a/README.md b/README.md index f94e83769fa6..14f725fa3bd0 100644 --- a/README.md +++ b/README.md @@ -121,6 +121,7 @@ Examples: - tools/[tcpaccept](tools/tcpaccept.py): Trace TCP passive connections (accept()). [Examples](tools/tcpaccept_example.txt). - tools/[tcpconnect](tools/tcpconnect.py): Trace TCP active connections (connect()). [Examples](tools/tcpconnect_example.txt). - tools/[tcpconnlat](tools/tcpconnlat.py): Trace TCP active connection latency (connect()). [Examples](tools/tcpconnlat_example.txt). +- tools/[tcplife](tools/tcplife.py): Trace TCP sessions and summarize lifespan. [Examples](tools/tcplife_example.txt). - tools/[tcpretrans](tools/tcpretrans.py): Trace TCP retransmits and TLPs. [Examples](tools/tcpretrans_example.txt). - tools/[tcptop](tools/tcptop.py): Summarize TCP send/recv throughput by host. Top for TCP. [Examples](tools/tcptop_example.txt). - tools/[tplist](tools/tplist.py): Display kernel tracepoints or USDT probes and their formats. [Examples](tools/tplist_example.txt). diff --git a/man/man8/tcplife.8 b/man/man8/tcplife.8 new file mode 100644 index 000000000000..059caa957064 --- /dev/null +++ b/man/man8/tcplife.8 @@ -0,0 +1,130 @@ +.TH tcplife 8 "2016-10-19" "USER COMMANDS" +.SH NAME +tcplife \- Trace TCP sessions and summarize lifespan. Uses Linux eBPF/bcc. +.SH SYNOPSIS +.B tcplife [\-h] [\-T] [\-t] [\-w] [\-s] [\-p PID] [\-D PORTS] [\-L PORTS] +.SH DESCRIPTION +This tool traces TCP sessions that open and close while tracing, and prints +a line of output to summarize each one. This includes the IP addresses, ports, +duration, and throughput for the session. This is useful for workload +characterisation and flow accounting: identifying what connections are +happening, with the bytes transferred. + +This tool works by using kernel dynamic tracing, and will need to be updated +if the kernel implementation changes. Only TCP state changes are traced, so +it is expected that the overhead of this tool is much lower than typical +send/receive tracing. + +Since this uses BPF, only the root user can use this tool. +.SH REQUIREMENTS +CONFIG_BPF and bcc. +.SH OPTIONS +.TP +\-h +Print usage message. +.TP +\-s +Comma separated values output (parseable). +.TP +\-t +Include a timestamp column (seconds). +.TP +\-T +Include a time column (HH:MM:SS). +.TP +\-w +Wide column output (fits IPv6 addresses). +.TP +\-p PID +Trace this process ID only (filtered in-kernel). +.TP +\-L PORTS +Comma-separated list of local ports to trace (filtered in-kernel). +.TP +\-D PORTS +Comma-separated list of destination ports to trace (filtered in-kernel). +.SH EXAMPLES +.TP +Trace all TCP sessions, and summarize lifespan and throughput: +# +.B tcplife +.TP +Include a timestamp column, and wide column output: +# +.B tcplife \-tw +.TP +Trace PID 181 only: +# +.B tcplife \-p 181 +.TP +Trace connections to local ports 80 and 81 only: +# +.B tcplife \-L 80,81 +.TP +Trace connections to remote port 80 only: +# +.B tcplife \-D 80 +.SH FIELDS +.TP +TIME +Time of the call, in HH:MM:SS format. +.TP +TIME(s) +Time of the call, in seconds. +.TP +PID +Process ID +.TP +COMM +Process name +.TP +IP +IP address family (4 or 6) +.TP +LADDR +Local IP address. +.TP +DADDR +Remote IP address. +.TP +LPORT +Local port. +.TP +DPORT +Destination port. +.TP +TX_KB +Total transmitted Kbytes. +.TP +RX_KB +Total received Kbytes. +.TP +MS +Lifespan of the session, in milliseconds. +.SH OVERHEAD +This traces the kernel TCP set state function, which should be called much +less often than send/receive tracing, and therefore have lower overhead. The +overhead of the tool is relative to the rate of new TCP sessions: if this is +high, over 10,000 per second, then there may be noticable overhead just to +print out 10k lines of formatted output per second. + +You can find out the rate of new TCP sessions using "sar \-n TCP 1", and +adding the active/s and passive/s columns. + +As always, test and understand this tools overhead for your types of +workloads before production use. +.SH SOURCE +This is from bcc. +.IP +https://github.com/iovisor/bcc +.PP +Also look in the bcc distribution for a companion _examples.txt file containing +example usage, output, and commentary for this tool. +.SH OS +Linux +.SH STABILITY +Unstable - in development. +.SH AUTHOR +Brendan Gregg +.SH SEE ALSO +tcpaccept(8), tcpconnect(8), tcptop(8) diff --git a/tools/tcplife.py b/tools/tcplife.py new file mode 100755 index 000000000000..4bf2ca654626 --- /dev/null +++ b/tools/tcplife.py @@ -0,0 +1,356 @@ +#!/usr/bin/python +# @lint-avoid-python-3-compatibility-imports +# +# tcplife Trace the lifespan of TCP sessions and summarize. +# For Linux, uses BCC, BPF. Embedded C. +# +# USAGE: tcplife [-h] [-C] [-S] [-p PID] [interval [count]] +# +# This uses dynamic tracing of kernel functions, and will need to be updated +# to match kernel changes. +# +# While throughput counters are emitted, they are fetched in a low-overhead +# manner: reading members of the tcp_info struct on TCP close. ie, we do not +# trace send/receive. +# +# Copyright 2016 Netflix, Inc. +# Licensed under the Apache License, Version 2.0 (the "License") +# +# IDEA: Julia Evans +# +# 18-Oct-2016 Brendan Gregg Created this. + +from __future__ import print_function +from bcc import BPF +import argparse +from socket import inet_ntop, ntohs, AF_INET, AF_INET6 +from struct import pack +import ctypes as ct +from time import strftime + +# arguments +examples = """examples: + ./tcplife # trace all TCP connect()s + ./tcplife -t # include time column (HH:MM:SS) + ./tcplife -w # wider colums (fit IPv6) + ./tcplife -stT # csv output, with times & timestamps + ./tcplife -p 181 # only trace PID 181 + ./tcplife -L 80 # only trace local port 80 + ./tcplife -L 80,81 # only trace local ports 80 and 81 + ./tcplife -D 80 # only trace remote port 80 +""" +parser = argparse.ArgumentParser( + description="Trace the lifespan of TCP sessions and summarize", + 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 (seconds)") +parser.add_argument("-w", "--wide", action="store_true", + help="wide column output (fits IPv6 addresses)") +parser.add_argument("-s", "--csv", action="store_true", + help="comma seperated values output") +parser.add_argument("-p", "--pid", + help="trace this PID only") +parser.add_argument("-L", "--localport", + help="comma-separated list of local ports to trace.") +parser.add_argument("-D", "--remoteport", + help="comma-separated list of remote ports to trace.") +args = parser.parse_args() +debug = 0 + +# define BPF program +bpf_text = """ +#include +#define KBUILD_MODNAME "foo" +#include +#include +#include + +BPF_HASH(birth, struct sock *, u64); + +// separate data structs for ipv4 and ipv6 +struct ipv4_data_t { + // XXX: switch some to u32's when supported + u64 ts_us; + u64 pid; + u64 saddr; + u64 daddr; + u64 ports; + u64 rx_b; + u64 tx_b; + u64 span_us; + char task[TASK_COMM_LEN]; +}; +BPF_PERF_OUTPUT(ipv4_events); + +struct ipv6_data_t { + u64 ts_us; + u64 pid; + unsigned __int128 saddr; + unsigned __int128 daddr; + u64 ports; + u64 rx_b; + u64 tx_b; + u64 span_us; + char task[TASK_COMM_LEN]; +}; +BPF_PERF_OUTPUT(ipv6_events); + +struct id_t { + u32 pid; + char task[TASK_COMM_LEN]; +}; +BPF_HASH(whoami, struct sock *, struct id_t); + +int kprobe__tcp_set_state(struct pt_regs *ctx, struct sock *sk, int state) +{ + u32 pid = bpf_get_current_pid_tgid() >> 32; + + // lport is either used in a filter here, or later + u16 lport = sk->__sk_common.skc_num; + FILTER_LPORT + + // dport is either used in a filter here, or later + u16 dport = sk->__sk_common.skc_dport; + FILTER_DPORT + + /* + * This tool includes PID and comm context. It's best effort, and may + * be wrong in some situations. It currently works like this: + * - active connections: cache PID & comm on TCP_SYN_SENT + * - passive connections: read PID & comm on TCP_LAST_ACK + */ + + // record PID & comm on SYN_SENT + if (state == TCP_SYN_SENT) { + FILTER_PID + struct id_t me = {.pid = pid}; + bpf_get_current_comm(&me.task, sizeof(me.task)); + whoami.update(&sk, &me); + } + + // capture birth time + if (state < TCP_FIN_WAIT1) { + // matching just ESTABLISHED may be sufficient, provided no code-path + // sets ESTABLISHED without a tcp_set_state() call. Until we know + // that for sure, match all early states to increase chances a + // timestamp is set. + u64 ts = bpf_ktime_get_ns(); + birth.update(&sk, &ts); + return 0; + } + + // fetch possible cached data + struct id_t *mep; + mep = whoami.lookup(&sk); + + // passive connection closing; wait until TCP_LAST_ACK + if (mep == 0 && state != TCP_LAST_ACK) + return 0; + + if (state == TCP_LAST_ACK) { + FILTER_PID + } + + // calculate lifespan + u64 *tsp, delta_us; + tsp = birth.lookup(&sk); + if (tsp == 0) { + return 0; // missed create + } + delta_us = (bpf_ktime_get_ns() - *tsp) / 1000; + + // get throughput stats. see tcp_get_info(). + u64 rx_b = 0, tx_b = 0, sport = 0; + struct tcp_sock *tp = (struct tcp_sock *)sk; + rx_b = tp->bytes_received; + tx_b = tp->bytes_acked; + + u16 family = sk->__sk_common.skc_family; + + if (family == AF_INET) { + struct ipv4_data_t data4 = {.span_us = delta_us, + .rx_b = rx_b, .tx_b = tx_b}; + data4.ts_us = bpf_ktime_get_ns() / 1000; + data4.saddr = sk->__sk_common.skc_rcv_saddr; + data4.daddr = sk->__sk_common.skc_daddr; + // a workaround until data4 compiles with separate lport/dport + data4.ports = ntohs(dport) + ((0ULL + lport) << 32); + if (mep == 0) { + data4.pid = pid; + bpf_get_current_comm(&data4.task, sizeof(data4.task)); + bpf_trace_printk("state %d 0pid %d\\n", state, pid); + } else { + data4.pid = mep->pid; + bpf_trace_printk("state %d mpid %d\\n", state, mep->pid); + bpf_probe_read(&data4.task, sizeof(data4.task), (void *)mep->task); + } + ipv4_events.perf_submit(ctx, &data4, sizeof(data4)); + + } else /* 6 */ { + struct ipv6_data_t data6 = {.span_us = delta_us, + .rx_b = rx_b, .tx_b = tx_b}; + data6.ts_us = bpf_ktime_get_ns() / 1000; + bpf_probe_read(&data6.saddr, sizeof(data6.saddr), + sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32); + bpf_probe_read(&data6.daddr, sizeof(data6.daddr), + sk->__sk_common.skc_v6_daddr.in6_u.u6_addr32); + // a workaround until data6 compiles with separate lport/dport + data6.ports = ntohs(dport) + ((0ULL + lport) << 32); + if (mep == 0) { + data6.pid = pid; + bpf_get_current_comm(&data6.task, sizeof(data6.task)); + } else { + data6.pid = mep->pid; + bpf_probe_read(&data6.task, sizeof(data6.task), (void *)mep->task); + } + ipv6_events.perf_submit(ctx, &data6, sizeof(data6)); + } + + birth.delete(&sk); + if (mep != 0) + whoami.delete(&sk); + + return 0; +} +""" + +# code substitutions +if args.pid: + bpf_text = bpf_text.replace('FILTER_PID', + 'if (pid != %s) { return 0; }' % args.pid) +if args.remoteport: + dports = [int(dport) for dport in args.remoteport.split(',')] + dports_if = ' && '.join(['dport != %d' % ntohs(dport) for dport in dports]) + bpf_text = bpf_text.replace('FILTER_DPORT', + 'if (%s) { birth.delete(&sk); return 0; }' % dports_if) +if args.localport: + lports = [int(lport) for lport in args.localport.split(',')] + lports_if = ' && '.join(['lport != %d' % lport for lport in lports]) + bpf_text = bpf_text.replace('FILTER_LPORT', + 'if (%s) { birth.delete(&sk); return 0; }' % lports_if) +bpf_text = bpf_text.replace('FILTER_PID', '') +bpf_text = bpf_text.replace('FILTER_DPORT', '') +bpf_text = bpf_text.replace('FILTER_LPORT', '') + +if debug: + print(bpf_text) + +# event data +TASK_COMM_LEN = 16 # linux/sched.h + +class Data_ipv4(ct.Structure): + _fields_ = [ + ("ts_us", ct.c_ulonglong), + ("pid", ct.c_ulonglong), + ("saddr", ct.c_ulonglong), + ("daddr", ct.c_ulonglong), + ("ports", ct.c_ulonglong), + ("rx_b", ct.c_ulonglong), + ("tx_b", ct.c_ulonglong), + ("span_us", ct.c_ulonglong), + ("task", ct.c_char * TASK_COMM_LEN) + ] + +class Data_ipv6(ct.Structure): + _fields_ = [ + ("ts_us", ct.c_ulonglong), + ("pid", ct.c_ulonglong), + ("saddr", (ct.c_ulonglong * 2)), + ("daddr", (ct.c_ulonglong * 2)), + ("ports", ct.c_ulonglong), + ("rx_b", ct.c_ulonglong), + ("tx_b", ct.c_ulonglong), + ("span_us", ct.c_ulonglong), + ("task", ct.c_char * TASK_COMM_LEN) + ] + +# +# Setup output formats +# +# Don't change the default output (next 2 lines): this fits in 80 chars. I +# know it doesn't have NS or UIDs etc. I know. If you really, really, really +# need to add columns, columns that solve real actual problems, I'd start by +# adding an extended mode (-x) to included those columns. +# +header_string = "%-5s %-10.10s %s%-15s %-5s %-15s %-5s %5s %5s %s" +format_string = "%-5d %-10.10s %s%-15s %-5d %-15s %-5d %5d %5d %.2f" +if args.wide: + header_string = "%-5s %-16.16s %-2s %-26s %-5s %-26s %-5s %6s %6s %s" + format_string = "%-5d %-16.16s %-2s %-26s %-5s %-26s %-5d %6d %6d %.2f" +if args.csv: + header_string = "%s,%s,%s,%s,%s,%s,%s,%s,%s,%s" + format_string = "%d,%s,%s,%s,%s,%s,%d,%d,%d,%.2f" + +# process event +def print_ipv4_event(cpu, data, size): + event = ct.cast(data, ct.POINTER(Data_ipv4)).contents + global start_ts + if args.time: + if args.csv: + print("%s," % strftime("%H:%M:%S"), end="") + else: + print("%-8s " % strftime("%H:%M:%S"), end="") + if args.timestamp: + if start_ts == 0: + start_ts = event.ts_us + delta_s = (float(event.ts_us) - start_ts) / 1000000 + if args.csv: + print("%.6f," % delta_s, end="") + else: + print("%-9.6f " % delta_s, end="") + print(format_string % (event.pid, event.task, + "4" if args.wide or args.csv else "", + inet_ntop(AF_INET, pack("I", event.saddr)), event.ports >> 32, + inet_ntop(AF_INET, pack("I", event.daddr)), event.ports & 0xffffffff, + event.tx_b / 1024, event.rx_b / 1024, float(event.span_us) / 1000)) + +def print_ipv6_event(cpu, data, size): + event = ct.cast(data, ct.POINTER(Data_ipv6)).contents + global start_ts + if args.time: + if args.csv: + print("%s," % strftime("%H:%M:%S"), end="") + else: + print("%-8s " % strftime("%H:%M:%S"), end="") + if args.timestamp: + if start_ts == 0: + start_ts = event.ts_us + delta_s = (float(event.ts_us) - start_ts) / 1000000 + if args.csv: + print("%.6f," % delta_s, end="") + else: + print("%-9.6f " % delta_s, end="") + print(format_string % (event.pid, event.task, + "6" if args.wide or args.csv else "", + inet_ntop(AF_INET6, event.saddr), event.ports >> 32, + inet_ntop(AF_INET6, event.daddr), event.ports & 0xffffffff, + event.tx_b / 1024, event.rx_b / 1024, float(event.span_us) / 1000)) + +# initialize BPF +b = BPF(text=bpf_text) + +# header +if args.time: + if args.csv: + print("%s," % ("TIME"), end="") + else: + print("%-8s " % ("TIME"), end="") +if args.timestamp: + if args.csv: + print("%s," % ("TIME(s)"), end="") + else: + print("%-9s " % ("TIME(s)"), end="") +print(header_string % ("PID", "COMM", + "IP" if args.wide or args.csv else "", "LADDR", + "LPORT", "RADDR", "RPORT", "TX_KB", "RX_KB", "MS")) + +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: + b.kprobe_poll() diff --git a/tools/tcplife_example.txt b/tools/tcplife_example.txt new file mode 100644 index 000000000000..457fe787586f --- /dev/null +++ b/tools/tcplife_example.txt @@ -0,0 +1,135 @@ +Demonstrations of tcplife, the Linux BPF/bcc version. + + +tcplife summarizes TCP sessions that open and close while tracing. For example: + +# ./tcplife +PID COMM LADDR LPORT RADDR RPORT TX_KB RX_KB MS +22597 recordProg 127.0.0.1 46644 127.0.0.1 28527 0 0 0.23 +3277 redis-serv 127.0.0.1 28527 127.0.0.1 46644 0 0 0.28 +22598 curl 100.66.3.172 61620 52.205.89.26 80 0 1 91.79 +22604 curl 100.66.3.172 44400 52.204.43.121 80 0 1 121.38 +22624 recordProg 127.0.0.1 46648 127.0.0.1 28527 0 0 0.22 +3277 redis-serv 127.0.0.1 28527 127.0.0.1 46648 0 0 0.27 +22647 recordProg 127.0.0.1 46650 127.0.0.1 28527 0 0 0.21 +3277 redis-serv 127.0.0.1 28527 127.0.0.1 46650 0 0 0.26 +[...] + +This caught a program, "recordProg" making a few short-lived TCP connections +to "redis-serv", lasting about 0.25 milliseconds each connection. A couple of +"curl" sessions were also traced, connecting to port 80, and lasting 91 and 121 +milliseconds. + +This tool is useful for workload characterisation and flow accounting: +identifying what connections are happening, with the bytes transferred. + + +Process names are truncated to 10 characters. By using the wide option, -w, +the column width becomes 16 characters. The IP address columns are also wider +to fit IPv6 addresses: + +# ./tcplife -w +PID COMM IP LADDR LPORT RADDR RPORT TX_KB RX_KB MS +26315 recordProgramSt 4 127.0.0.1 44188 127.0.0.1 28527 0 0 0.21 +3277 redis-server 4 127.0.0.1 28527 127.0.0.1 44188 0 0 0.26 +26320 ssh 6 fe80::8a3:9dff:fed5:6b19 22440 fe80::8a3:9dff:fed5:6b19 22 1 1 457.52 +26321 sshd 6 fe80::8a3:9dff:fed5:6b19 22 fe80::8a3:9dff:fed5:6b19 22440 1 1 458.69 +26341 recordProgramSt 4 127.0.0.1 44192 127.0.0.1 28527 0 0 0.27 +3277 redis-server 4 127.0.0.1 28527 127.0.0.1 44192 0 0 0.32 + + +In this example, I uploaded a 10 Mbyte file to the server, and then downloaded +it again, using scp: + +# ./tcplife +PID COMM LADDR LPORT RADDR RPORT TX_KB RX_KB MS +7715 recordProg 127.0.0.1 50894 127.0.0.1 28527 0 0 0.25 +3277 redis-serv 127.0.0.1 28527 127.0.0.1 50894 0 0 0.30 +7619 sshd 100.66.3.172 22 100.127.64.230 63033 5 10255 3066.79 +7770 recordProg 127.0.0.1 50896 127.0.0.1 28527 0 0 0.20 +3277 redis-serv 127.0.0.1 28527 127.0.0.1 50896 0 0 0.24 +7793 recordProg 127.0.0.1 50898 127.0.0.1 28527 0 0 0.23 +3277 redis-serv 127.0.0.1 28527 127.0.0.1 50898 0 0 0.27 +7847 recordProg 127.0.0.1 50900 127.0.0.1 28527 0 0 0.24 +3277 redis-serv 127.0.0.1 28527 127.0.0.1 50900 0 0 0.29 +7870 recordProg 127.0.0.1 50902 127.0.0.1 28527 0 0 0.29 +3277 redis-serv 127.0.0.1 28527 127.0.0.1 50902 0 0 0.30 +7798 sshd 100.66.3.172 22 100.127.64.230 64925 10265 6 2176.15 +[...] + +You can see the 10 Mbytes received by sshd, and then later transmitted. Looks +like receive was slower (3.07 seconds) than transmit (2.18 seconds). + + +Timestamps can be added with -t: + +# ./tcplife -t +TIME(s) PID COMM LADDR LPORT RADDR RPORT TX_KB RX_KB MS +0.000000 5973 recordProg 127.0.0.1 47986 127.0.0.1 28527 0 0 0.25 +0.000059 3277 redis-serv 127.0.0.1 28527 127.0.0.1 47986 0 0 0.29 +1.022454 5996 recordProg 127.0.0.1 47988 127.0.0.1 28527 0 0 0.23 +1.022513 3277 redis-serv 127.0.0.1 28527 127.0.0.1 47988 0 0 0.27 +2.044868 6019 recordProg 127.0.0.1 47990 127.0.0.1 28527 0 0 0.24 +2.044924 3277 redis-serv 127.0.0.1 28527 127.0.0.1 47990 0 0 0.28 +3.069136 6042 recordProg 127.0.0.1 47992 127.0.0.1 28527 0 0 0.22 +3.069204 3277 redis-serv 127.0.0.1 28527 127.0.0.1 47992 0 0 0.28 + +This shows that the recordProg process was connecting once per second. + +There's also a -T for HH:MM:SS formatted times. + + +There's a comma separated values mode, -s. Here it is with both -t and -T +timestamps: + +# ./tcplife -stT +TIME,TIME(s),PID,COMM,IP,LADDR,LPORT,RADDR,RPORT,TX_KB,RX_KB,MS +23:39:38,0.000000,7335,recordProgramSt,4,127.0.0.1,48098,127.0.0.1,28527,0,0,0.26 +23:39:38,0.000064,3277,redis-server,4,127.0.0.1,28527,127.0.0.1,48098,0,0,0.32 +23:39:39,1.025078,7358,recordProgramSt,4,127.0.0.1,48100,127.0.0.1,28527,0,0,0.25 +23:39:39,1.025141,3277,redis-server,4,127.0.0.1,28527,127.0.0.1,48100,0,0,0.30 +23:39:41,2.040949,7381,recordProgramSt,4,127.0.0.1,48102,127.0.0.1,28527,0,0,0.24 +23:39:41,2.041011,3277,redis-server,4,127.0.0.1,28527,127.0.0.1,48102,0,0,0.29 +23:39:42,3.067848,7404,recordProgramSt,4,127.0.0.1,48104,127.0.0.1,28527,0,0,0.30 +23:39:42,3.067914,3277,redis-server,4,127.0.0.1,28527,127.0.0.1,48104,0,0,0.35 +[...] + + +There are options for filtering on local and remote ports. Here is filtering +on local ports 22 and 80: + +# ./tcplife.py -L 22,80 +PID COMM LADDR LPORT RADDR RPORT TX_KB RX_KB MS +8301 sshd 100.66.3.172 22 100.127.64.230 58671 3 3 1448.52 +[...] + + +USAGE: + +# ./tcplife.py -h +usage: tcplife.py [-h] [-T] [-t] [-w] [-s] [-p PID] [-L LOCALPORT] + [-D REMOTEPORT] + +Trace the lifespan of TCP sessions and summarize + +optional arguments: + -h, --help show this help message and exit + -T, --time include time column on output (HH:MM:SS) + -t, --timestamp include timestamp on output (seconds) + -w, --wide wide column output (fits IPv6 addresses) + -s, --csv comma seperated values output + -p PID, --pid PID trace this PID only + -L LOCALPORT, --localport LOCALPORT + comma-separated list of local ports to trace. + -D REMOTEPORT, --remoteport REMOTEPORT + comma-separated list of remote ports to trace. + +examples: + ./tcplife # trace all TCP connect()s + ./tcplife -t # include time column (HH:MM:SS) + ./tcplife -w # wider colums (fit IPv6) + ./tcplife -stT # csv output, with times & timestamps + ./tcplife -p 181 # only trace PID 181 + ./tcplife -L 80 # only trace local port 80 + ./tcplife -L 80,81 # only trace local ports 80 and 81 + ./tcplife -D 80 # only trace remote port 80