Line data Source code
1 : // SPDX-License-Identifier: GPL-2.0-only
2 : /*
3 : * Copyright (c) 2023 Meta Platforms, Inc. and affiliates.
4 : */
5 :
6 : #include "bpfilter/btf.h"
7 :
8 : #include <linux/btf.h>
9 :
10 : #include <bpf/btf.h>
11 : #include <errno.h>
12 : #include <limits.h>
13 : #include <stddef.h>
14 : #include <stdlib.h>
15 :
16 : #include "bpfilter/helper.h"
17 : #include "bpfilter/logger.h"
18 :
19 : static struct btf *_bf_btf = NULL;
20 :
21 3 : int bf_btf_setup(void)
22 : {
23 3 : _bf_btf = btf__load_vmlinux_btf();
24 3 : if (!_bf_btf)
25 1 : return bf_err_r(errno, "failed to load vmlinux BTF");
26 :
27 : return 0;
28 : }
29 :
30 2 : void bf_btf_teardown(void)
31 : {
32 2 : btf__free(_bf_btf);
33 2 : _bf_btf = NULL;
34 2 : }
35 :
36 0 : int bf_btf_get_id(const char *name)
37 : {
38 : int id;
39 :
40 0 : bf_assert(name);
41 :
42 0 : id = btf__find_by_name(_bf_btf, name);
43 0 : if (id < 0)
44 0 : return bf_err_r(errno, "failed to find BTF type for \"%s\"", name);
45 :
46 : return id;
47 : }
48 :
49 0 : const char *bf_btf_get_name(int id)
50 : {
51 : const struct btf_type *type;
52 :
53 0 : type = btf__type_by_id(_bf_btf, id);
54 0 : if (!type) {
55 0 : bf_warn("can't find BTF type ID %d", id);
56 0 : return NULL;
57 : }
58 :
59 0 : return btf__name_by_offset(_bf_btf, type->name_off);
60 : }
61 :
62 0 : int bf_btf_kernel_has_token(void)
63 : {
64 : int bpf_attr_id;
65 : const struct btf_type *bpf_attr_type;
66 : const struct btf_member *bpf_attr_members;
67 :
68 0 : bpf_attr_id = btf__find_by_name_kind(_bf_btf, "bpf_attr", BTF_KIND_UNION);
69 0 : if (bpf_attr_id < 0) {
70 0 : return bf_err_r(bpf_attr_id,
71 : "can't find structure 'bpf_attr' in kernel BTF");
72 : }
73 :
74 0 : bpf_attr_type = btf__type_by_id(_bf_btf, bpf_attr_id);
75 0 : if (!bpf_attr_type)
76 0 : return bf_err_r(-EINVAL, "failed to request 'bpf_attr' BTF type");
77 :
78 : bpf_attr_members = btf_members(bpf_attr_type);
79 : // Iterate through union members
80 0 : for (unsigned short i = 0; i < btf_vlen(bpf_attr_type); i++) {
81 : const struct btf_type *member_type =
82 0 : btf__type_by_id(_bf_btf, bpf_attr_members[i].type);
83 : const struct btf_member *m_members = btf_members(member_type);
84 :
85 : // We are looking for an anonymous structure
86 0 : if (!btf_is_struct(member_type) || bpf_attr_members[i].name_off != 0)
87 0 : continue;
88 :
89 0 : for (int j = 0; j < btf_vlen(member_type); j++) {
90 : const char *member_name =
91 0 : btf__name_by_offset(_bf_btf, m_members[j].name_off);
92 :
93 0 : if (member_name && bf_streq(member_name, "prog_token_fd"))
94 : return 0;
95 : }
96 : }
97 :
98 : return -ENOENT;
99 : }
100 :
101 : #define _bf_btf_type_is_compound(type) \
102 : (BTF_INFO_KIND((type)->info) == BTF_KIND_UNION || \
103 : BTF_INFO_KIND((type)->info) == BTF_KIND_STRUCT)
104 :
105 : /**
106 : * @brief Find the offset of `field_name` in BTF type ID `compound_id`.
107 : *
108 : * @param compound_id Compound BTF type id.
109 : * @param field_name Name of the field to find the offset of. Can't be NULL.
110 : * @return Offset (in bits) of `field_name` in `compound_id` BTF type.
111 : */
112 6 : static ssize_t _bf_btf_offset_in_compound(uint32_t compound_id,
113 : const char *field_name)
114 : {
115 : ssize_t offset;
116 : const struct btf_type *compound_type, *member_type;
117 : const struct btf_member *member;
118 :
119 6 : bf_assert(field_name);
120 :
121 6 : compound_type = btf__type_by_id(_bf_btf, compound_id);
122 6 : if (!compound_type)
123 0 : return -errno;
124 :
125 6 : if (!_bf_btf_type_is_compound(compound_type))
126 : return -ENOTSUP;
127 :
128 6 : member = (const struct btf_member *)(compound_type + 1);
129 28 : for (size_t i = 0; i < BTF_INFO_VLEN(compound_type->info); ++i, ++member) {
130 25 : const char *name = btf__name_by_offset(_bf_btf, member->name_off);
131 :
132 25 : member_type = btf__type_by_id(_bf_btf, member->type);
133 25 : if (!member_type)
134 0 : return -errno;
135 :
136 : // Member is an anonymous compound type? Check its members
137 25 : if (*name == '\0' && _bf_btf_type_is_compound(member_type)) {
138 2 : offset = _bf_btf_offset_in_compound(member->type, field_name);
139 2 : if (offset < 0)
140 2 : continue;
141 :
142 0 : if (BTF_MEMBER_BIT_OFFSET(member->offset) % 8) {
143 : // Assuming this is not possible, but who knows
144 0 : return bf_err_r(-ENOTSUP,
145 : "anonymous compound parent is a bitfield");
146 : }
147 :
148 0 : if (BTF_INFO_KFLAG(compound_type->info))
149 0 : offset += BTF_MEMBER_BIT_OFFSET(member->offset);
150 : else
151 0 : offset += member->offset;
152 :
153 0 : return offset;
154 : }
155 :
156 23 : if (!bf_streq(name, field_name))
157 20 : continue;
158 :
159 3 : offset = (ssize_t)member->offset;
160 3 : if (BTF_INFO_KFLAG(compound_type->info))
161 2 : offset = BTF_MEMBER_BIT_OFFSET(member->offset);
162 :
163 : return offset;
164 : }
165 :
166 : return -EINVAL;
167 : }
168 :
169 6 : static int _bf_btf_get_compound_type_id(const char *name)
170 : {
171 : int compound_id;
172 :
173 6 : compound_id = btf__find_by_name_kind(_bf_btf, name, BTF_KIND_STRUCT);
174 6 : if (compound_id < 0 && compound_id != -ENOENT)
175 : return compound_id;
176 6 : if (compound_id >= 0)
177 : return compound_id;
178 :
179 2 : compound_id = btf__find_by_name_kind(_bf_btf, name, BTF_KIND_UNION);
180 : if (compound_id < 0 && compound_id != -ENOENT)
181 : return compound_id;
182 :
183 : return compound_id;
184 : }
185 :
186 6 : int bf_btf_get_field_off(const char *struct_name, const char *field_name)
187 : {
188 : int id;
189 : ssize_t offset;
190 :
191 6 : bf_assert(struct_name);
192 6 : bf_assert(field_name);
193 :
194 6 : id = _bf_btf_get_compound_type_id(struct_name);
195 6 : if (id < 0)
196 2 : return bf_err_r(id, "failed to find BTF type ID for '%s'", struct_name);
197 :
198 4 : offset = _bf_btf_offset_in_compound(id, field_name);
199 4 : if (offset < 0) {
200 1 : return bf_err_r((int)offset, "failed to find offset of %s.%s",
201 : struct_name, field_name);
202 : }
203 3 : if (offset % 8) {
204 0 : return bf_err_r(-EINVAL, "%s.%s has a bit offset", struct_name,
205 : field_name);
206 : }
207 3 : if (offset / 8 > INT_MAX) {
208 0 : return bf_err_r(-E2BIG, "%s.%s has an offset bigger than %d",
209 : struct_name, field_name, INT_MAX);
210 : }
211 :
212 3 : return (int)(offset / 8);
213 : }
|