1#!/usr/bin/python 2# @lint-avoid-python-3-compatibility-imports 3# 4# undump Dump UNIX socket packets. 5# For Linux, uses BCC, eBPF. Embedded C. 6# USAGE: undump [-h] [-t] [-p PID] 7# 8# This uses dynamic tracing of kernel functions, and will need to be updated 9# to match kernel changes. 10# 11# Copyright (c) 2021 Rong Tao. 12# Licensed under the GPL License, Version 2.0 13# 14# 27-Aug-2021 Rong Tao Created this. 15# 17-Sep-2021 Rong Tao Simplify according to chenhengqi's suggestion 16# https://github.com/iovisor/bcc/pull/3615 17# 18from __future__ import print_function 19from bcc import BPF 20from bcc.containers import filter_by_containers 21from bcc.utils import printb 22import argparse 23from socket import inet_ntop, ntohs, AF_INET, AF_INET6 24from struct import pack 25from time import sleep 26from datetime import datetime 27import sys 28 29# arguments 30examples = """examples: 31 ./undump # trace/dump all UNIX packets 32 ./undump -p 181 # only trace/dump PID 181 33""" 34parser = argparse.ArgumentParser( 35 description="Dump UNIX socket packets", 36 formatter_class=argparse.RawDescriptionHelpFormatter, 37 epilog=examples) 38 39parser.add_argument("-p", "--pid", 40 help="trace this PID only") 41args = parser.parse_args() 42 43# define BPF program 44bpf_text = """ 45#include <uapi/linux/ptrace.h> 46#include <net/sock.h> 47#include <bcc/proto.h> 48#include <linux/aio.h> 49#include <linux/socket.h> 50#include <linux/net.h> 51#include <linux/fs.h> 52#include <linux/mount.h> 53#include <linux/module.h> 54#include <net/sock.h> 55#include <net/af_unix.h> 56 57#define MAX_PKT 512 58struct recv_data_t { 59 u32 recv_len; 60 u8 pkt[MAX_PKT]; 61}; 62 63// single element per-cpu array to hold the current event off the stack 64BPF_PERCPU_ARRAY(unix_data, struct recv_data_t, 1); 65 66BPF_PERF_OUTPUT(unix_recv_events); 67 68int trace_unix_stream_read_actor(struct pt_regs *ctx) 69{ 70 u32 zero = 0; 71 int ret = PT_REGS_RC(ctx); 72 u64 pid_tgid = bpf_get_current_pid_tgid(); 73 u32 pid = pid_tgid >> 32; 74 u32 tid = pid_tgid; 75 76 FILTER_PID 77 78 struct sk_buff *skb = (struct sk_buff *)PT_REGS_PARM1(ctx); 79 80 struct recv_data_t *data = unix_data.lookup(&zero); 81 if (!data) 82 return 0; 83 84 unsigned int data_len = skb->len; 85 if(data_len > MAX_PKT) 86 return 0; 87 88 void *iodata = (void *)skb->data; 89 data->recv_len = data_len; 90 91 bpf_probe_read(data->pkt, data_len, iodata); 92 unix_recv_events.perf_submit(ctx, data, data_len+sizeof(u32)); 93 94 return 0; 95} 96""" 97 98if args.pid: 99 bpf_text = bpf_text.replace('FILTER_PID', 100 'if (pid != %s) { return 0; }' % args.pid) 101 102bpf_text = bpf_text.replace('FILTER_PID', '') 103 104# process event 105def print_recv_pkg(cpu, data, size): 106 event = b["unix_recv_events"].event(data) 107 if args.pid: 108 print("PID \033[1;31m%s\033[m " % args.pid, end="") 109 print("Recv \033[1;31m%d\033[m bytes" % event.recv_len) 110 111 print(" ", end="") 112 for i in range(0, event.recv_len): 113 print("%02x " % event.pkt[i], end="") 114 sys.stdout.flush() 115 if (i+1)%16 == 0: 116 print("") 117 print(" ", end="") 118 print("") 119 120# initialize BPF 121b = BPF(text=bpf_text) 122b.attach_kprobe(event="unix_stream_read_actor", fn_name="trace_unix_stream_read_actor") 123 124if args.pid: 125 print("Tracing \033[1;31mPID=%s\033[m UNIX socket packets ... Hit Ctrl-C to end" % args.pid) 126else: 127 print("Tracing UNIX socket packets ... Hit Ctrl-C to end") 128 129start_ts = 0 130 131# read events 132b["unix_recv_events"].open_perf_buffer(print_recv_pkg) 133 134while True: 135 try: 136 b.perf_buffer_poll() 137 except KeyboardInterrupt: 138 exit() 139