168 lines
5.0 KiB
Python
Executable File
168 lines
5.0 KiB
Python
Executable File
#! /usr/bin/python3
|
|
# @lint-avoid-python-3-compatibility-imports
|
|
#
|
|
# rdmaucma: Trace RDMA Userspace Connection Manager Access Event.
|
|
# For Linux, uses BCC, eBPF.
|
|
#
|
|
# USAGE: rdmaucma [-h]
|
|
#
|
|
# Copyright (c) 2023 zhenwei pi
|
|
# Licensed under the Apache License, Version 2.0 (the "License")
|
|
#
|
|
# 29-MAY-2023 zhenwei pi Created this.
|
|
|
|
from __future__ import print_function
|
|
from bcc import BPF
|
|
from socket import inet_ntop, AF_INET, AF_INET6
|
|
import socket, struct
|
|
import argparse
|
|
import ctypes
|
|
from time import strftime
|
|
|
|
# arguments
|
|
examples = """examples:
|
|
./rdmaucma # Trace all RDMA Userspace Connection Manager Access Event
|
|
"""
|
|
parser = argparse.ArgumentParser(
|
|
description="Trace RDMA Userspace Connection Manager Access Event",
|
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
epilog=examples)
|
|
parser.add_argument("-D", "--debug", action="store_true",
|
|
help="print BPF program before starting (for debugging purposes)")
|
|
parser.add_argument("--ebpf", action="store_true",
|
|
help=argparse.SUPPRESS)
|
|
args = parser.parse_args()
|
|
|
|
# define BPF program
|
|
bpf_text = """
|
|
#include <linux/bpf.h>
|
|
#include <uapi/linux/ptrace.h>
|
|
#include <rdma/rdma_cm.h>
|
|
|
|
struct ipv4_data_t {
|
|
u32 saddr;
|
|
u32 daddr;
|
|
u16 sport;
|
|
u16 dport;
|
|
int event;
|
|
};
|
|
|
|
BPF_PERF_OUTPUT(ipv4_events);
|
|
|
|
struct ipv6_data_t {
|
|
unsigned __int128 saddr;
|
|
unsigned __int128 daddr;
|
|
u16 sport;
|
|
u16 dport;
|
|
int event;
|
|
};
|
|
|
|
BPF_PERF_OUTPUT(ipv6_events);
|
|
|
|
int trace_ucma_event_handler(struct pt_regs *ctx,
|
|
struct rdma_cm_id *cm_id,
|
|
struct rdma_cm_event *event)
|
|
{
|
|
struct sockaddr_storage *ss = &cm_id->route.addr.src_addr;
|
|
|
|
if (ss->ss_family == AF_INET) {
|
|
struct ipv4_data_t ipv4_data = { 0 };
|
|
struct sockaddr_in *addr4 = (struct sockaddr_in *)ss;
|
|
ipv4_data.sport = addr4->sin_port;
|
|
ipv4_data.saddr = addr4->sin_addr.s_addr;
|
|
|
|
addr4 = (struct sockaddr_in *)&cm_id->route.addr.dst_addr;
|
|
ipv4_data.dport = addr4->sin_port;
|
|
ipv4_data.daddr = addr4->sin_addr.s_addr;
|
|
|
|
ipv4_data.event = event->event;
|
|
ipv4_events.perf_submit(ctx, &ipv4_data, sizeof(ipv4_data));
|
|
} else if (ss->ss_family == AF_INET6) {
|
|
struct ipv6_data_t ipv6_data = { 0 };
|
|
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)ss;
|
|
ipv6_data.sport = addr6->sin6_port;
|
|
bpf_probe_read_kernel(&ipv6_data.saddr, sizeof(ipv6_data.saddr), addr6->sin6_addr.in6_u.u6_addr32);
|
|
|
|
addr6 = (struct sockaddr_in6 *)&cm_id->route.addr.dst_addr;
|
|
ipv6_data.dport = addr6->sin6_port;
|
|
bpf_probe_read_kernel(&ipv6_data.daddr, sizeof(ipv6_data.daddr), addr6->sin6_addr.in6_u.u6_addr32);
|
|
|
|
ipv6_data.event = event->event;
|
|
ipv6_events.perf_submit(ctx, &ipv6_data, sizeof(ipv6_data));
|
|
} else {
|
|
return -EPROTONOSUPPORT;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
"""
|
|
|
|
# debug/dump ebpf enable or not
|
|
if args.debug or args.ebpf:
|
|
print(bpf_text)
|
|
if args.ebpf:
|
|
exit()
|
|
|
|
# load BPF program
|
|
b = BPF(text=bpf_text)
|
|
b.attach_kprobe(event="ucma_event_handler", fn_name="trace_ucma_event_handler")
|
|
|
|
# see linux/include/rdma/rdma_cm.h
|
|
rdma_cm_event = [
|
|
"address resolved",
|
|
"address error",
|
|
"route resolved ",
|
|
"route error",
|
|
"connect request",
|
|
"connect response",
|
|
"connect error",
|
|
"unreachable",
|
|
"rejected",
|
|
"established",
|
|
"disconnected",
|
|
"device removal",
|
|
"multicast join",
|
|
"multicast error",
|
|
"address change",
|
|
"timewait exit" ]
|
|
|
|
def print_ipv4_event(cpu, data, size):
|
|
event = b["ipv4_events"].event(data)
|
|
|
|
cm_event = "unknown event"
|
|
if event.event < len(rdma_cm_event):
|
|
cm_event = rdma_cm_event[event.event]
|
|
|
|
print("%-9s %-16s %-6s %-45s %-45s" % (strftime("%H:%M:%S").encode('ascii'),
|
|
cm_event, "IPv4",
|
|
inet_ntop(AF_INET, struct.pack("I", event.saddr)) + ":" + str(socket.ntohs(event.sport)),
|
|
inet_ntop(AF_INET, struct.pack("I", event.daddr)) + ":" + str(socket.ntohs(event.dport))))
|
|
|
|
def print_ipv6_event(cpu, data, size):
|
|
event = b["ipv6_events"].event(data)
|
|
|
|
cm_event = "unknown event"
|
|
if event.event < len(rdma_cm_event):
|
|
cm_event = rdma_cm_event[event.event]
|
|
|
|
print("%-9s %-16s %-6s %-45s %-45s" % (strftime("%H:%M:%S").encode('ascii'),
|
|
cm_event, "IPv6",
|
|
inet_ntop(AF_INET6, event.saddr) + ":" + str(socket.ntohs(event.sport)),
|
|
inet_ntop(AF_INET6, event.daddr) + ":" + str(socket.ntohs(event.dport))))
|
|
|
|
|
|
b["ipv4_events"].open_perf_buffer(print_ipv4_event)
|
|
b["ipv6_events"].open_perf_buffer(print_ipv6_event)
|
|
|
|
# output
|
|
print("Tracing RDMA Userspace Connection Manager Access event... Hit Ctrl-C to end.")
|
|
|
|
# address length 39 = max("2001:0db8:3c4d:0015:0000:0000:1a2f:1a2b", "255.255.255.255")
|
|
print("%-9s %-16s %-4s %-45s %-45s" % ("Timestamp", "Event", "Family", "Local", "Remote"))
|
|
|
|
while (1):
|
|
try:
|
|
b.perf_buffer_poll()
|
|
except KeyboardInterrupt:
|
|
exit()
|