From 9e0b087fceda18641eadbd567d3823c69d5f89a8 Mon Sep 17 00:00:00 2001 From: Brendan Gregg Date: Mon, 28 Mar 2016 12:11:45 -0700 Subject: [PATCH] IPv6 strings with an inet6_ntoa --- man/man8/tcpconnect.8 | 6 ++-- tools/tcpconnect.py | 63 ++++++++++++++++++++++++++++-------- tools/tcpconnect_example.txt | 6 ++-- 3 files changed, 53 insertions(+), 22 deletions(-) diff --git a/man/man8/tcpconnect.8 b/man/man8/tcpconnect.8 index 198ac50d8d2f..27780aed8611 100644 --- a/man/man8/tcpconnect.8 +++ b/man/man8/tcpconnect.8 @@ -55,12 +55,10 @@ IP IP address family (4 or 6) .TP SADDR -Source IP address. IPv4 as a dotted quad, IPv6 shows "..." then the last 4 -bytes (check for newer versions of this tool for the full address). +Source IP address. .TP DADDR -Destination IP address. IPv4 as a dotted quad, IPv6 shows "..." then the last 4 -bytes (check for newer versions of this tool for the full address). +Destination IP address. .TP DPORT Destination port diff --git a/tools/tcpconnect.py b/tools/tcpconnect.py index 5a46197f2d00..12f683e9a6a9 100755 --- a/tools/tcpconnect.py +++ b/tools/tcpconnect.py @@ -24,6 +24,7 @@ from __future__ import print_function from bcc import BPF import argparse +import re import ctypes as ct # arguments @@ -65,12 +66,11 @@ BPF_PERF_OUTPUT(ipv4_events); struct ipv6_data_t { - // XXX: update to transfer full ipv6 addrs u64 ts_us; u64 pid; u64 ip; - u64 saddr; - u64 daddr; + u64 saddr[2]; + u64 daddr[2]; u64 dport; char task[TASK_COMM_LEN]; }; @@ -125,13 +125,14 @@ struct ipv6_data_t data6 = {.pid = pid, .ip = ipver}; data6.ts_us = bpf_ktime_get_ns() / 1000; // just grab the last 4 bytes for now - u32 saddr = 0, daddr = 0; - bpf_probe_read(&saddr, sizeof(saddr), - &skp->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32[3]); - bpf_probe_read(&daddr, sizeof(daddr), - &skp->__sk_common.skc_v6_daddr.in6_u.u6_addr32[3]); - data6.saddr = bpf_ntohl(saddr); - data6.daddr = bpf_ntohl(daddr); + bpf_probe_read(&data6.saddr[0], sizeof(data6.saddr[0]), + &skp->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32[0]); + bpf_probe_read(&data6.saddr[1], sizeof(data6.saddr[1]), + &skp->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32[2]); + bpf_probe_read(&data6.daddr[0], sizeof(data6.daddr[0]), + &skp->__sk_common.skc_v6_daddr.in6_u.u6_addr32[0]); + bpf_probe_read(&data6.daddr[1], sizeof(data6.daddr[1]), + &skp->__sk_common.skc_v6_daddr.in6_u.u6_addr32[2]); data6.dport = ntohs(dport); bpf_get_current_comm(&data6.task, sizeof(data6.task)); ipv6_events.perf_submit(ctx, &data6, sizeof(data6)); @@ -179,8 +180,8 @@ class Data_ipv6(ct.Structure): ("ts_us", ct.c_ulonglong), ("pid", ct.c_ulonglong), ("ip", ct.c_ulonglong), - ("saddr", ct.c_ulonglong), - ("daddr", ct.c_ulonglong), + ("saddr", ct.c_ulonglong * 2), + ("daddr", ct.c_ulonglong * 2), ("dport", ct.c_ulonglong), ("task", ct.c_char * TASK_COMM_LEN) ] @@ -195,14 +196,18 @@ def print_ipv4_event(cpu, data, size): print("%-6d %-12.12s %-2d %-16s %-16s %-4d" % (event.pid, event.task, event.ip, inet_ntoa(event.saddr), inet_ntoa(event.daddr), event.dport)) + def print_ipv6_event(cpu, data, size): event = ct.cast(data, ct.POINTER(Data_ipv6)).contents if args.timestamp: if start_ts == 0: start_ts = event.ts_us print("%-9.3f" % ((event.ts_us - start_ts) / 100000), end="") - print("%-6d %-12.12s %-2d ...%-13x ...%-13x %-4d" % (event.pid, - event.task, event.ip, event.saddr, event.daddr, event.dport)) + print("%-6d %-12.12s %-2d %-16s %-16s %-4d" % (event.pid, + event.task, event.ip, + inet6_ntoa(event.saddr[1] << 64 | event.saddr[0]), + inet6_ntoa(event.daddr[1] << 64 | event.daddr[0]), + event.dport)) # initialize BPF b = BPF(text=bpf_text) @@ -220,6 +225,7 @@ def print_ipv6_event(cpu, data, size): start_ts = 0 def inet_ntoa(addr): + # u32 to dotted quad string dq = '' for i in range(0, 4): dq = dq + str(addr & 0xff) @@ -228,6 +234,35 @@ def inet_ntoa(addr): addr = addr >> 8 return dq +def inet6_ntoa(addr): + # see RFC4291 summary in RFC5952 section 2 + s = '' + for i in range(0, 16): + if ((i % 2) == 0): + zerorun = 1 + if (i != 0): + s = s + ':' + + v = addr & 0xff + if ((i % 2) == 0): + # if first byte in field is zero, skip it: + if v != 0: + # don't zero pad first byte in field: + s = s + "%x" % v + zerorun = 0 + else: + if zerorun: + # previous byte was zero, don't zero pad this one: + s = s + "%x" % v + else: + # previous byte was non-zero, need a zero pad: + s = s + "%02x" % v + addr = addr >> 8 + + # compress left-most zero run only (change to most for RFC5952): + s = re.sub(r'(^|:)0:(0:)+', r'::', s, 1) + return s + # read events b["ipv4_events"].open_perf_buffer(print_ipv4_event) b["ipv6_events"].open_perf_buffer(print_ipv6_event) diff --git a/tools/tcpconnect_example.txt b/tools/tcpconnect_example.txt index 1089a581285a..4e6cf41fae30 100644 --- a/tools/tcpconnect_example.txt +++ b/tools/tcpconnect_example.txt @@ -10,16 +10,14 @@ PID COMM IP SADDR DADDR DPORT 1479 telnet 4 127.0.0.1 127.0.0.1 23 1469 curl 4 10.201.219.236 54.245.105.25 80 1469 curl 4 10.201.219.236 54.67.101.145 80 -11072 ssh 6 ...fe8203ac ...fe82abcd 22 +1991 telnet 6 ::1 ::1 23 +2015 ssh 6 fe80::2000:bff:fe82:3ac fe80::2000:bff:fe82:3ac 22 This output shows four connections, one from a "telnet" process, two from "curl", and one from "ssh". The output details shows the IP version, source address, destination address, and destination port. This traces attempted connections: these may have failed. -IPv4 addresses are printed as dotted quads. Only the last 4 bytes of IPv6 -addresses are printed for now (check for updated versions of this tool). - The overhead of this tool should be negligible, since it is only tracing the kernel functions performing connect. It is not tracing every packet and then filtering.