text: #!/usr/bin/env python
  #
  # solisten      Trace TCP listen events
  #               For Linux, uses BCC, eBPF. Embedded C.
  #
  # USAGE: solisten.py [-h] [-p PID] [--show-netns]
  #
  # This is provided as a basic example of TCP connection & socket tracing.
  # It could be useful in scenarios where load balancers needs to be updated
  # dynamically as application is fully initialized.
  #
  # All IPv4 listen attempts are traced, even if they ultimately fail or the
  # the listening program is not willing to accept().
  #
  # Copyright (c) 2016 Jean-Tiare Le Bigot.
  # Licensed under the Apache License, Version 2.0 (the "License")
  #
  # 04-Mar-2016	Jean-Tiare Le Bigot	Created this.
  
  import os
  from socket import inet_ntop, AF_INET, AF_INET6, SOCK_STREAM, SOCK_DGRAM
  from struct import pack
  import argparse
  from bcc import BPF
  import ctypes as ct
  
  # Arguments
  examples = """Examples:
      ./solisten.py              # Stream socket listen
      ./solisten.py -p 1234      # Stream socket listen for specified PID only
      ./solisten.py --netns 4242 # " for the specified network namespace ID only
      ./solisten.py --show-netns # Show network ns ID (useful for containers)
  """
  
  parser = argparse.ArgumentParser(
      description="Stream sockets listen",
      formatter_class=argparse.RawDescriptionHelpFormatter,
      epilog=examples)
  parser.add_argument("--show-netns", action="store_true",
      help="show network namespace")
  parser.add_argument("-p", "--pid", default=0, type=int,
      help="trace this PID only")
  parser.add_argument("-n", "--netns", default=0, type=int,
      help="trace this Network Namespace only")
  parser.add_argument("--ebpf", action="store_true",
      help=argparse.SUPPRESS)
  
  
  # BPF Program
  bpf_text = """
  #include <net/net_namespace.h>
  #include <bcc/proto.h>
  #pragma clang diagnostic push
  #pragma clang diagnostic ignored "-Wenum-conversion"
  #include <net/inet_sock.h>
  #pragma clang diagnostic pop
  
  // Common structure for UDP/TCP IPv4/IPv6
  struct listen_evt_t {
      u64 ts_us;
      u64 pid_tgid;
      u64 backlog;
      u64 netns;
      u64 proto;    // familiy << 16 | type
      u64 lport;    // use only 16 bits
      u64 laddr[2]; // IPv4: store in laddr[0]
      char task[TASK_COMM_LEN];
  };
  BPF_PERF_OUTPUT(listen_evt);
  
  // Send an event for each IPv4 listen with PID, bound address and port
  int kprobe__inet_listen(struct pt_regs *ctx, struct socket *sock, int backlog)
  {
          // cast types. Intermediate cast not needed, kept for readability
          struct sock *sk = sock->sk;
          struct inet_sock *inet = (struct inet_sock *)sk;
  
          // Built event for userland
          struct listen_evt_t evt = {
              .ts_us = bpf_ktime_get_ns() / 1000,
              .backlog = backlog,
          };
  
          // Get process comm. Needs LLVM >= 3.7.1
          // see https://github.com/iovisor/bcc/issues/393
          bpf_get_current_comm(evt.task, TASK_COMM_LEN);
  
          // Get socket IP family
          u16 family = sk->__sk_common.skc_family;
          evt.proto = family << 16 | SOCK_STREAM;
  
          // Get PID
          evt.pid_tgid = bpf_get_current_pid_tgid();
  
          ##FILTER_PID##
  
          // Get port
          evt.lport = inet->inet_sport;
          evt.lport = ntohs(evt.lport);
  
          // Get network namespace id, if kernel supports it
  #ifdef CONFIG_NET_NS
          evt.netns = sk->__sk_common.skc_net.net->ns.inum;
  #else
          evt.netns = 0;
  #endif
  
          ##FILTER_NETNS##
  
          // Get IP
          if (family == AF_INET) {
              evt.laddr[0] = inet->inet_rcv_saddr;
          } else if (family == AF_INET6) {
              bpf_probe_read(evt.laddr, sizeof(evt.laddr),
                             sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32);
          }
  
          // Send event to userland
          listen_evt.perf_submit(ctx, &evt, sizeof(evt));
  
          return 0;
  };
  """
  
  # event data
  TASK_COMM_LEN = 16      # linux/sched.h
  class ListenEvt(ct.Structure):
      _fields_ = [
          ("ts_us", ct.c_ulonglong),
          ("pid_tgid", ct.c_ulonglong),
          ("backlog", ct.c_ulonglong),
          ("netns", ct.c_ulonglong),
          ("proto", ct.c_ulonglong),
          ("lport", ct.c_ulonglong),
          ("laddr", ct.c_ulonglong * 2),
          ("task", ct.c_char * TASK_COMM_LEN)
      ]
  
      # TODO: properties to unpack protocol / ip / pid / tgid ...
  
  # Format output
  def event_printer(show_netns):
      def print_event(cpu, data, size):
          # Decode event
          event = ct.cast(data, ct.POINTER(ListenEvt)).contents
  
          pid = event.pid_tgid & 0xffffffff
          proto_family = event.proto & 0xff
          proto_type = event.proto >> 16 & 0xff
  
          if proto_family == SOCK_STREAM:
              protocol = "TCP"
          elif proto_family == SOCK_DGRAM:
              protocol = "UDP"
          else:
              protocol = "UNK"
  
          address = ""
          if proto_type == AF_INET:
              protocol += "v4"
              address = inet_ntop(AF_INET, pack("I", event.laddr[0]))
          elif proto_type == AF_INET6:
              address = inet_ntop(AF_INET6, event.laddr)
              protocol += "v6"
  
          # Display
          if show_netns:
              print("%-6d %-12.12s %-12s %-6s %-8s %-5s %-39s" % (
                  pid, event.task, event.netns, protocol, event.backlog,
                  event.lport, address,
              ))
          else:
              print("%-6d %-12.12s %-6s %-8s %-5s %-39s" % (
                  pid, event.task, protocol, event.backlog,
                  event.lport, address,
              ))
  
      return print_event
  
  if __name__ == "__main__":
      # Parse arguments
      args = parser.parse_args()
  
      pid_filter = ""
      netns_filter = ""
  
      if args.pid:
          pid_filter = "if (evt.pid_tgid != %d) return 0;" % args.pid
      if args.netns:
          netns_filter = "if (evt.netns != %d) return 0;" % args.netns
  
      bpf_text = bpf_text.replace("##FILTER_PID##", pid_filter)
      bpf_text = bpf_text.replace("##FILTER_NETNS##", netns_filter)
  
      if args.ebpf:
          print(bpf_text)
          exit()
  
      # Initialize BPF
      b = BPF(text=bpf_text)
      b["listen_evt"].open_perf_buffer(event_printer(args.show_netns))
  
      # Print headers
      if args.show_netns:
          print("%-6s %-12s %-12s %-6s %-8s %-5s %-39s" %
                ("PID", "COMM", "NETNS", "PROTO", "BACKLOG", "PORT", "ADDR"))
      else:
          print("%-6s %-12s %-6s %-8s %-5s %-39s" %
                ("PID", "COMM", "PROTO", "BACKLOG", "PORT", "ADDR"))
  
      # Read events
      while 1:
          b.kprobe_poll()
  