planarian_backend_netwatch/
btf.rs1const MAGIC: u16 = 0xEB9F;
11const KIND_INT: u32 = 1;
12const KIND_PTR: u32 = 2;
13const KIND_ARRAY: u32 = 3;
14const KIND_STRUCT: u32 = 4;
15const KIND_UNION: u32 = 5;
16const KIND_ENUM: u32 = 6;
17const KIND_TYPEDEF: u32 = 8;
18const KIND_VOLATILE: u32 = 9;
19const KIND_CONST: u32 = 10;
20const KIND_RESTRICT: u32 = 11;
21const KIND_FUNC_PROTO: u32 = 13;
22const KIND_VAR: u32 = 14;
23const KIND_DATASEC: u32 = 15;
24const KIND_DECL_TAG: u32 = 17;
25const KIND_TYPE_TAG: u32 = 18;
26const KIND_ENUM64: u32 = 19;
27const KIND_FLOAT: u32 = 16;
28const DEPTH: usize = 16;
30
31struct Member {
32 name: u32,
33 ty: u32,
34 bit_offset: u32,
35}
36
37struct Ty {
38 name: u32,
39 kind: u32,
40 refers: u32,
43 nelems: u32,
45 members: Vec<Member>,
46}
47
48pub struct Btf {
49 types: Vec<Ty>,
51 strings: Vec<u8>,
52}
53
54fn u32_at(b: &[u8], at: usize) -> Result<u32, String> {
55 b.get(at..at + 4).map(|s| u32::from_le_bytes([s[0], s[1], s[2], s[3]])).ok_or_else(|| format!("BTF: short at {at}"))
56}
57
58impl Btf {
59 pub fn parse(b: &[u8]) -> Result<Btf, String> {
60 if b.get(0..2) != Some(&MAGIC.to_le_bytes()[..]) {
61 return Err("BTF: bad magic (or big-endian)".into());
62 }
63 let hdr = u32_at(b, 4)? as usize;
64 let (type_off, type_len) = (u32_at(b, 8)? as usize, u32_at(b, 12)? as usize);
65 let (str_off, str_len) = (u32_at(b, 16)? as usize, u32_at(b, 20)? as usize);
66 let types_b = b.get(hdr + type_off..hdr + type_off + type_len).ok_or("BTF: type section out of range")?;
67 let strings = b.get(hdr + str_off..hdr + str_off + str_len).ok_or("BTF: string section out of range")?.to_vec();
68 let mut types = vec![Ty { name: 0, kind: 0, refers: 0, nelems: 0, members: vec![] }];
69 let mut at = 0;
70 while at < types_b.len() {
71 let (name, info, size_or_type) = (u32_at(types_b, at)?, u32_at(types_b, at + 4)?, u32_at(types_b, at + 8)?);
72 at += 12;
73 let vlen = (info & 0xffff) as usize;
74 let kind = (info >> 24) & 0x1f;
75 let kind_flag = info >> 31 == 1;
76 let mut members = Vec::new();
77 let mut refers = size_or_type;
78 let mut nelems = 0;
79 let extra = match kind {
80 KIND_INT | KIND_VAR | KIND_DECL_TAG => 4,
81 KIND_ARRAY => {
82 refers = u32_at(types_b, at)?;
84 nelems = u32_at(types_b, at + 8)?;
85 12
86 }
87 KIND_STRUCT | KIND_UNION => {
88 for i in 0..vlen {
89 let m = at + 12 * i;
90 let off = u32_at(types_b, m + 8)?;
91 let bit_offset = if kind_flag { off & 0x00ff_ffff } else { off };
92 members.push(Member { name: u32_at(types_b, m)?, ty: u32_at(types_b, m + 4)?, bit_offset });
93 }
94 12 * vlen
95 }
96 KIND_ENUM | KIND_FUNC_PROTO => 8 * vlen,
97 KIND_DATASEC | KIND_ENUM64 => 12 * vlen,
98 2 | 7..=12 | KIND_FLOAT | KIND_TYPE_TAG => 0,
101 k => return Err(format!("BTF: unknown kind {k} at type {}", types.len())),
104 };
105 at += extra;
106 types.push(Ty { name, kind, refers, nelems, members });
107 }
108 Ok(Btf { types, strings })
109 }
110
111 fn name(&self, off: u32) -> &[u8] {
112 let s = self.strings.get(off as usize..).unwrap_or(&[]);
113 &s[..s.iter().position(|&c| c == 0).unwrap_or(s.len())]
114 }
115
116 fn resolve(&self, mut id: u32) -> Option<&Ty> {
118 for _ in 0..DEPTH {
119 let t = self.types.get(id as usize)?;
120 match t.kind {
121 KIND_TYPEDEF | KIND_VOLATILE | KIND_CONST | KIND_RESTRICT | KIND_TYPE_TAG => id = t.refers,
122 _ => return Some(t),
123 }
124 }
125 None
126 }
127
128 fn member(&self, t: &Ty, name: &str, depth: usize) -> Option<(u32, u32)> {
131 if depth > DEPTH {
132 return None;
133 }
134 for m in &t.members {
135 if self.name(m.name) == name.as_bytes() {
136 return Some((m.ty, m.bit_offset));
137 }
138 if m.name == 0
139 && let Some(inner) = self.resolve(m.ty)
140 && matches!(inner.kind, KIND_STRUCT | KIND_UNION)
141 && let Some((ty, off)) = self.member(inner, name, depth + 1)
142 {
143 return Some((ty, m.bit_offset.checked_add(off)?));
144 }
145 }
146 None
147 }
148
149 fn size(&self, id: u32) -> Option<u32> {
151 self.size_at(id, 0)
152 }
153
154 fn size_at(&self, id: u32, depth: usize) -> Option<u32> {
155 if depth > DEPTH {
156 return None;
157 }
158 let t = self.resolve(id)?;
159 match t.kind {
160 KIND_PTR => Some(8),
161 KIND_ARRAY => self.size_at(t.refers, depth + 1)?.checked_mul(t.nelems),
162 KIND_INT | KIND_ENUM | KIND_ENUM64 | KIND_STRUCT | KIND_UNION | KIND_FLOAT => Some(t.refers),
163 _ => None,
164 }
165 }
166
167 pub fn offset(&self, strukt: &str, path: &[&str]) -> Result<u32, String> {
171 let what = || format!("struct {strukt}.{}", path.join("."));
172 let mut t = self
173 .types
174 .iter()
175 .find(|t| t.kind == KIND_STRUCT && self.name(t.name) == strukt.as_bytes())
176 .ok_or_else(|| format!("BTF: no {}", what()))?;
177 let mut bits: u32 = 0;
178 for (i, name) in path.iter().enumerate() {
179 let (ty, off) = self.member(t, name, 0).ok_or_else(|| format!("BTF: no member {name} in {}", what()))?;
180 let size = self.size(ty).ok_or_else(|| format!("BTF: {name} has no size in {}", what()))?;
181 if !off.is_multiple_of(8) || (off / 8).checked_add(size).is_none_or(|end| end > t.refers) {
182 return Err(format!("BTF: {name} lies outside its struct in {}", what()));
183 }
184 bits = bits.checked_add(off).ok_or_else(|| format!("BTF: offset overflow in {}", what()))?;
185 if i + 1 < path.len() {
186 t = self
187 .resolve(ty)
188 .filter(|t| matches!(t.kind, KIND_STRUCT | KIND_UNION))
189 .ok_or_else(|| format!("BTF: {name} is not a struct in {}", what()))?;
190 }
191 }
192 if !bits.is_multiple_of(8) {
193 return Err(format!("BTF: {} is a bitfield", what()));
194 }
195 Ok(bits / 8)
196 }
197}
198
199#[derive(Clone, Copy, Debug, PartialEq, Eq)]
203pub struct NetnsOffsets {
204 pub skb_sk: u32,
205 pub sk_net: u32,
206 pub net_inum: u32,
207}
208
209impl NetnsOffsets {
210 pub fn from_btf(btf: &Btf) -> Result<NetnsOffsets, String> {
211 Ok(NetnsOffsets {
212 skb_sk: btf.offset("sk_buff", &["sk"])?,
213 sk_net: btf.offset("sock", &["__sk_common", "skc_net", "net"])?,
214 net_inum: btf.offset("net", &["ns", "inum"])?,
215 })
216 }
217}
218
219#[derive(Clone, Copy, Debug, PartialEq, Eq)]
224pub struct CgroupOffsets {
225 pub task_cgroups: u32,
226 pub css_dfl: u32,
227 pub cgroup_kn: u32,
228 pub kn_name: u32,
229 pub kn_parent: u32,
230}
231
232impl CgroupOffsets {
233 pub fn from_btf(btf: &Btf) -> Result<CgroupOffsets, String> {
234 Ok(CgroupOffsets {
235 task_cgroups: btf.offset("task_struct", &["cgroups"])?,
236 css_dfl: btf.offset("css_set", &["dfl_cgrp"])?,
237 cgroup_kn: btf.offset("cgroup", &["kn"])?,
238 kn_name: btf.offset("kernfs_node", &["name"])?,
239 kn_parent: btf.offset("kernfs_node", &["__parent"]).or_else(|_| btf.offset("kernfs_node", &["parent"]))?,
240 })
241 }
242}
243
244#[derive(Clone, Copy, Debug, PartialEq, Eq)]
247pub struct NetdevOffsets {
248 pub name: u32,
249 pub net: u32,
250}
251
252impl NetdevOffsets {
253 pub fn from_btf(btf: &Btf) -> Result<NetdevOffsets, String> {
254 Ok(NetdevOffsets {
255 name: btf.offset("net_device", &["name"])?,
256 net: btf.offset("net_device", &["nd_net", "net"])?,
257 })
258 }
259}