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/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