1#!/usr/bin/python
2#
3# tcpv4connect	Trace TCP IPv4 connect()s.
4#		For Linux, uses BCC, eBPF. Embedded C.
5#
6# USAGE: tcpv4connect [-h] [-t] [-p PID]
7#
8# This is provided as a basic example of TCP connection & socket tracing.
9#
10# All IPv4 connection attempts are traced, even if they ultimately fail.
11#
12# Copyright (c) 2015 Brendan Gregg.
13# Licensed under the Apache License, Version 2.0 (the "License")
14#
15# 15-Oct-2015	Brendan Gregg	Created this.
16
17from __future__ import print_function
18from bcc import BPF
19
20# define BPF program
21bpf_text = """
22#include <uapi/linux/ptrace.h>
23#include <net/sock.h>
24#include <bcc/proto.h>
25
26BPF_HASH(currsock, u32, struct sock *);
27
28int kprobe__tcp_v4_connect(struct pt_regs *ctx, struct sock *sk)
29{
30	u32 pid = bpf_get_current_pid_tgid();
31
32	// stash the sock ptr for lookup on return
33	currsock.update(&pid, &sk);
34
35	return 0;
36};
37
38int kretprobe__tcp_v4_connect(struct pt_regs *ctx)
39{
40	int ret = PT_REGS_RC(ctx);
41	u32 pid = bpf_get_current_pid_tgid();
42
43	struct sock **skpp;
44	skpp = currsock.lookup(&pid);
45	if (skpp == 0) {
46		return 0;	// missed entry
47	}
48
49	if (ret != 0) {
50		// failed to send SYNC packet, may not have populated
51		// socket __sk_common.{skc_rcv_saddr, ...}
52		currsock.delete(&pid);
53		return 0;
54	}
55
56	// pull in details
57	struct sock *skp = *skpp;
58	u32 saddr = skp->__sk_common.skc_rcv_saddr;
59	u32 daddr = skp->__sk_common.skc_daddr;
60	u16 dport = skp->__sk_common.skc_dport;
61
62	// output
63	bpf_trace_printk("trace_tcp4connect %x %x %d\\n", saddr, daddr, ntohs(dport));
64
65	currsock.delete(&pid);
66
67	return 0;
68}
69"""
70
71# initialize BPF
72b = BPF(text=bpf_text)
73
74# header
75print("%-6s %-12s %-16s %-16s %-4s" % ("PID", "COMM", "SADDR", "DADDR",
76    "DPORT"))
77
78def inet_ntoa(addr):
79	dq = ''
80	for i in range(0, 4):
81		dq = dq + str(addr & 0xff)
82		if (i != 3):
83			dq = dq + '.'
84		addr = addr >> 8
85	return dq
86
87# filter and format output
88while 1:
89	# Read messages from kernel pipe
90	try:
91	    (task, pid, cpu, flags, ts, msg) = b.trace_fields()
92	    (_tag, saddr_hs, daddr_hs, dport_s) = msg.split(" ")
93	except ValueError:
94	    # Ignore messages from other tracers
95	    continue
96
97	# Ignore messages from other tracers
98	if _tag != "trace_tcp4connect":
99	    continue
100
101	print("%-6d %-12.12s %-16s %-16s %-4s" % (pid, task,
102	    inet_ntoa(int(saddr_hs, 16)),
103	    inet_ntoa(int(daddr_hs, 16)),
104	    dport_s))
105