Skip to content

Commit 4905649

Browse files
feat:non struct field values can be cast
1 parent 7b7b00d commit 4905649

File tree

5 files changed

+103
-10
lines changed

5 files changed

+103
-10
lines changed

BCC-Examples/disksnoop.ipynb

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3025,19 +3025,20 @@
30253025
],
30263026
"source": [
30273027
"from vmlinux import struct_request, struct_pt_regs\n",
3028-
"from pythonbpf import bpf, section, bpfglobal, compile_to_ir, compile, map, BPF\n",
3028+
"from pythonbpf import bpf, section, bpfglobal, map, BPF\n",
30293029
"from pythonbpf.helper import ktime\n",
30303030
"from pythonbpf.maps import HashMap\n",
3031-
"import logging\n",
30323031
"from ctypes import c_int64, c_uint64, c_int32\n",
30333032
"\n",
30343033
"REQ_WRITE = 1\n",
30353034
"\n",
3035+
"\n",
30363036
"@bpf\n",
30373037
"@map\n",
30383038
"def start() -> HashMap:\n",
30393039
" return HashMap(key=c_uint64, value=c_uint64, max_entries=10240)\n",
30403040
"\n",
3041+
"\n",
30413042
"@bpf\n",
30423043
"@section(\"kprobe/blk_mq_end_request\")\n",
30433044
"def trace_completion(ctx: struct_pt_regs) -> c_int64:\n",
@@ -3063,6 +3064,7 @@
30633064
"\n",
30643065
" return c_int64(0)\n",
30653066
"\n",
3067+
"\n",
30663068
"@bpf\n",
30673069
"@section(\"kprobe/blk_mq_start_request\")\n",
30683070
"def trace_start(ctx1: struct_pt_regs) -> c_int32:\n",
@@ -3071,11 +3073,13 @@
30713073
" start.update(req, ts)\n",
30723074
" return c_int32(0)\n",
30733075
"\n",
3076+
"\n",
30743077
"@bpf\n",
30753078
"@bpfglobal\n",
30763079
"def LICENSE() -> str:\n",
30773080
" return \"GPL\"\n",
30783081
"\n",
3082+
"\n",
30793083
"b = BPF()"
30803084
]
30813085
},

pythonbpf/expr/expr_pass.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -666,13 +666,17 @@ def _handle_vmlinux_cast(
666666
# Cast the integer/value to a pointer to the struct
667667
# If arg_val is an integer type, we need to inttoptr it
668668
ptr_type = ir.PointerType()
669-
# TODO: add a integer check here later
670-
if ctypes_to_ir(arg_type.type.__name__):
671-
# Cast integer to pointer
672-
casted_ptr = builder.inttoptr(arg_val, ptr_type)
669+
# TODO: add a field value type check here
670+
print(arg_type)
671+
if isinstance(arg_type, Field):
672+
if ctypes_to_ir(arg_type.type.__name__):
673+
# Cast integer to pointer
674+
casted_ptr = builder.inttoptr(arg_val, ptr_type)
675+
else:
676+
logger.error(f"Unsupported type for vmlinux cast: {arg_type}")
677+
return None
673678
else:
674-
logger.error(f"Unsupported type for vmlinux cast: {arg_type}")
675-
return None
679+
casted_ptr = builder.inttoptr(arg_val, ptr_type)
676680

677681
return casted_ptr, vmlinux_struct_type
678682

pythonbpf/type_deducer.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
"c_longlong": ir.IntType(64),
1919
"c_uint": ir.IntType(32),
2020
"c_int": ir.IntType(32),
21+
"c_ushort": ir.IntType(16),
22+
"c_short": ir.IntType(16),
2123
# Not so sure about this one
2224
"str": ir.PointerType(ir.IntType(8)),
2325
}

pythonbpf/vmlinux_parser/vmlinux_exports_handler.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,12 @@ def handle_vmlinux_struct_field(
121121

122122
# Use bpf_probe_read_kernel for non-context struct field access
123123
field_value = self.load_struct_field(
124-
builder, struct_ptr, globvar_ir, field_data, struct_name
124+
builder,
125+
struct_ptr,
126+
globvar_ir,
127+
field_data,
128+
struct_name,
129+
local_sym_tab,
125130
)
126131
# Return field value and field type
127132
return field_value, field_data
@@ -130,7 +135,12 @@ def handle_vmlinux_struct_field(
130135

131136
@staticmethod
132137
def load_struct_field(
133-
builder, struct_ptr_int, offset_global, field_data, struct_name=None
138+
builder,
139+
struct_ptr_int,
140+
offset_global,
141+
field_data,
142+
struct_name=None,
143+
local_sym_tab=None,
134144
):
135145
"""
136146
Generate LLVM IR to load a field from a regular (non-context) struct using bpf_probe_read_kernel.
@@ -204,6 +214,7 @@ def load_struct_field(
204214
logger.warning("Complex vmlinux field type, using default 64 bits")
205215

206216
# Allocate local storage for the field value
217+
# TODO: CRITICAL BUG. alloca cannot be used anywhere other than the basic block
207218
local_storage = builder.alloca(ir.IntType(int_width))
208219
local_storage_i8_ptr = builder.bitcast(local_storage, i8_ptr_type)
209220

tests/c-form/xdp_test.bpf.c

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// xdp_ip_map.c
2+
#include <linux/bpf.h>
3+
#include <bpf/bpf_helpers.h>
4+
#include <bpf/bpf_endian.h>
5+
#include <linux/if_ether.h>
6+
#include <linux/ip.h>
7+
8+
struct ip_key {
9+
__u8 family; // 4 = IPv4
10+
__u8 pad[3]; // padding for alignment
11+
__u8 addr[16]; // IPv4 uses first 4 bytes
12+
};
13+
14+
// key → packet count
15+
struct {
16+
__uint(type, BPF_MAP_TYPE_HASH);
17+
__uint(max_entries, 16384);
18+
__type(key, struct ip_key);
19+
__type(value, __u64);
20+
} ip_count_map SEC(".maps");
21+
22+
SEC("xdp")
23+
int xdp_ip_map(struct xdp_md *ctx)
24+
{
25+
void *data_end = (void *)(long)ctx->data_end;
26+
void *data = (void *)(long)ctx->data;
27+
struct ethhdr *eth = data;
28+
29+
if (eth + 1 > (struct ethhdr *)data_end)
30+
return XDP_PASS;
31+
32+
__u16 h_proto = eth->h_proto;
33+
void *nh = data + sizeof(*eth);
34+
35+
// VLAN handling: single tag
36+
if (h_proto == bpf_htons(ETH_P_8021Q) ||
37+
h_proto == bpf_htons(ETH_P_8021AD)) {
38+
39+
if (nh + 4 > data_end)
40+
return XDP_PASS;
41+
42+
h_proto = *(__u16 *)(nh + 2);
43+
nh += 4;
44+
}
45+
46+
struct ip_key key = {};
47+
48+
// IPv4
49+
if (h_proto == bpf_htons(ETH_P_IP)) {
50+
struct iphdr *iph = nh;
51+
if (iph + 1 > (struct iphdr *)data_end)
52+
return XDP_PASS;
53+
54+
key.family = 4;
55+
// Copy 4 bytes of IPv4 address
56+
__builtin_memcpy(key.addr, &iph->saddr, 4);
57+
58+
__u64 *val = bpf_map_lookup_elem(&ip_count_map, &key);
59+
if (val)
60+
(*val)++;
61+
else {
62+
__u64 init = 1;
63+
bpf_map_update_elem(&ip_count_map, &key, &init, BPF_ANY);
64+
}
65+
66+
return XDP_PASS;
67+
}
68+
69+
return XDP_PASS;
70+
}
71+
72+
char _license[] SEC("license") = "GPL";

0 commit comments

Comments
 (0)