blob: f007d142531e3247424cca314e7ca6ba48b98488 [file] [log] [blame]
#!/usr/bin/python
# @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()