]>
Commit | Line | Data |
---|---|---|
d07dcf9a JB |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * NETLINK Policy advertisement to userspace | |
4 | * | |
5 | * Authors: Johannes Berg <[email protected]> | |
6 | * | |
7 | * Copyright 2019 Intel Corporation | |
8 | */ | |
9 | ||
10 | #include <linux/kernel.h> | |
11 | #include <linux/errno.h> | |
12 | #include <linux/types.h> | |
13 | #include <net/netlink.h> | |
14 | ||
15 | #define INITIAL_POLICIES_ALLOC 10 | |
16 | ||
adc84845 | 17 | struct netlink_policy_dump_state { |
d07dcf9a JB |
18 | unsigned int policy_idx; |
19 | unsigned int attr_idx; | |
20 | unsigned int n_alloc; | |
21 | struct { | |
22 | const struct nla_policy *policy; | |
23 | unsigned int maxtype; | |
24 | } policies[]; | |
25 | }; | |
26 | ||
adc84845 | 27 | static int add_policy(struct netlink_policy_dump_state **statep, |
d07dcf9a JB |
28 | const struct nla_policy *policy, |
29 | unsigned int maxtype) | |
30 | { | |
adc84845 | 31 | struct netlink_policy_dump_state *state = *statep; |
d07dcf9a JB |
32 | unsigned int n_alloc, i; |
33 | ||
34 | if (!policy || !maxtype) | |
35 | return 0; | |
36 | ||
37 | for (i = 0; i < state->n_alloc; i++) { | |
899b07c5 JB |
38 | if (state->policies[i].policy == policy && |
39 | state->policies[i].maxtype == maxtype) | |
d07dcf9a JB |
40 | return 0; |
41 | ||
42 | if (!state->policies[i].policy) { | |
43 | state->policies[i].policy = policy; | |
44 | state->policies[i].maxtype = maxtype; | |
45 | return 0; | |
46 | } | |
47 | } | |
48 | ||
49 | n_alloc = state->n_alloc + INITIAL_POLICIES_ALLOC; | |
50 | state = krealloc(state, struct_size(state, policies, n_alloc), | |
51 | GFP_KERNEL); | |
52 | if (!state) | |
53 | return -ENOMEM; | |
54 | ||
d1fb5559 JB |
55 | memset(&state->policies[state->n_alloc], 0, |
56 | flex_array_size(state, policies, n_alloc - state->n_alloc)); | |
57 | ||
d07dcf9a JB |
58 | state->policies[state->n_alloc].policy = policy; |
59 | state->policies[state->n_alloc].maxtype = maxtype; | |
60 | state->n_alloc = n_alloc; | |
61 | *statep = state; | |
62 | ||
63 | return 0; | |
64 | } | |
65 | ||
04a351a6 JB |
66 | /** |
67 | * netlink_policy_dump_get_policy_idx - retrieve policy index | |
68 | * @state: the policy dump state | |
69 | * @policy: the policy to find | |
70 | * @maxtype: the policy's maxattr | |
71 | * | |
72 | * Returns: the index of the given policy in the dump state | |
73 | * | |
74 | * Call this to find a policy index when you've added multiple and e.g. | |
75 | * need to tell userspace which command has which policy (by index). | |
76 | * | |
77 | * Note: this will WARN and return 0 if the policy isn't found, which | |
78 | * means it wasn't added in the first place, which would be an | |
79 | * internal consistency bug. | |
80 | */ | |
81 | int netlink_policy_dump_get_policy_idx(struct netlink_policy_dump_state *state, | |
82 | const struct nla_policy *policy, | |
83 | unsigned int maxtype) | |
d07dcf9a JB |
84 | { |
85 | unsigned int i; | |
86 | ||
04a351a6 JB |
87 | if (WARN_ON(!policy || !maxtype)) |
88 | return 0; | |
89 | ||
d07dcf9a | 90 | for (i = 0; i < state->n_alloc; i++) { |
899b07c5 JB |
91 | if (state->policies[i].policy == policy && |
92 | state->policies[i].maxtype == maxtype) | |
d07dcf9a JB |
93 | return i; |
94 | } | |
95 | ||
04a351a6 JB |
96 | WARN_ON(1); |
97 | return 0; | |
d07dcf9a JB |
98 | } |
99 | ||
04a351a6 | 100 | static struct netlink_policy_dump_state *alloc_state(void) |
d07dcf9a | 101 | { |
adc84845 | 102 | struct netlink_policy_dump_state *state; |
04a351a6 JB |
103 | |
104 | state = kzalloc(struct_size(state, policies, INITIAL_POLICIES_ALLOC), | |
105 | GFP_KERNEL); | |
106 | if (!state) | |
107 | return ERR_PTR(-ENOMEM); | |
108 | state->n_alloc = INITIAL_POLICIES_ALLOC; | |
109 | ||
110 | return state; | |
111 | } | |
112 | ||
113 | /** | |
114 | * netlink_policy_dump_add_policy - add a policy to the dump | |
115 | * @pstate: state to add to, may be reallocated, must be %NULL the first time | |
116 | * @policy: the new policy to add to the dump | |
117 | * @maxtype: the new policy's max attr type | |
118 | * | |
119 | * Returns: 0 on success, a negative error code otherwise. | |
120 | * | |
121 | * Call this to allocate a policy dump state, and to add policies to it. This | |
122 | * should be called from the dump start() callback. | |
123 | * | |
124 | * Note: on failures, any previously allocated state is freed. | |
125 | */ | |
126 | int netlink_policy_dump_add_policy(struct netlink_policy_dump_state **pstate, | |
127 | const struct nla_policy *policy, | |
128 | unsigned int maxtype) | |
129 | { | |
130 | struct netlink_policy_dump_state *state = *pstate; | |
d07dcf9a JB |
131 | unsigned int policy_idx; |
132 | int err; | |
133 | ||
04a351a6 JB |
134 | if (!state) { |
135 | state = alloc_state(); | |
136 | if (IS_ERR(state)) | |
137 | return PTR_ERR(state); | |
138 | } | |
d07dcf9a JB |
139 | |
140 | /* | |
141 | * walk the policies and nested ones first, and build | |
142 | * a linear list of them. | |
143 | */ | |
144 | ||
d07dcf9a JB |
145 | err = add_policy(&state, policy, maxtype); |
146 | if (err) | |
147 | return err; | |
148 | ||
149 | for (policy_idx = 0; | |
150 | policy_idx < state->n_alloc && state->policies[policy_idx].policy; | |
151 | policy_idx++) { | |
152 | const struct nla_policy *policy; | |
153 | unsigned int type; | |
154 | ||
155 | policy = state->policies[policy_idx].policy; | |
156 | ||
157 | for (type = 0; | |
158 | type <= state->policies[policy_idx].maxtype; | |
159 | type++) { | |
160 | switch (policy[type].type) { | |
161 | case NLA_NESTED: | |
162 | case NLA_NESTED_ARRAY: | |
163 | err = add_policy(&state, | |
164 | policy[type].nested_policy, | |
165 | policy[type].len); | |
166 | if (err) | |
167 | return err; | |
168 | break; | |
169 | default: | |
170 | break; | |
171 | } | |
172 | } | |
173 | } | |
174 | ||
04a351a6 | 175 | *pstate = state; |
d07dcf9a JB |
176 | return 0; |
177 | } | |
178 | ||
adc84845 JK |
179 | static bool |
180 | netlink_policy_dump_finished(struct netlink_policy_dump_state *state) | |
d07dcf9a JB |
181 | { |
182 | return state->policy_idx >= state->n_alloc || | |
183 | !state->policies[state->policy_idx].policy; | |
184 | } | |
185 | ||
04a351a6 JB |
186 | /** |
187 | * netlink_policy_dump_loop - dumping loop indicator | |
188 | * @state: the policy dump state | |
189 | * | |
190 | * Returns: %true if the dump continues, %false otherwise | |
191 | * | |
192 | * Note: this frees the dump state when finishing | |
193 | */ | |
adc84845 | 194 | bool netlink_policy_dump_loop(struct netlink_policy_dump_state *state) |
d07dcf9a | 195 | { |
949ca6b8 | 196 | return !netlink_policy_dump_finished(state); |
d07dcf9a JB |
197 | } |
198 | ||
44f3625b JB |
199 | int netlink_policy_dump_attr_size_estimate(const struct nla_policy *pt) |
200 | { | |
201 | /* nested + type */ | |
202 | int common = 2 * nla_attr_size(sizeof(u32)); | |
203 | ||
204 | switch (pt->type) { | |
205 | case NLA_UNSPEC: | |
206 | case NLA_REJECT: | |
207 | /* these actually don't need any space */ | |
208 | return 0; | |
209 | case NLA_NESTED: | |
210 | case NLA_NESTED_ARRAY: | |
211 | /* common, policy idx, policy maxattr */ | |
212 | return common + 2 * nla_attr_size(sizeof(u32)); | |
213 | case NLA_U8: | |
214 | case NLA_U16: | |
215 | case NLA_U32: | |
216 | case NLA_U64: | |
217 | case NLA_MSECS: | |
218 | case NLA_S8: | |
219 | case NLA_S16: | |
220 | case NLA_S32: | |
221 | case NLA_S64: | |
222 | /* maximum is common, u64 min/max with padding */ | |
223 | return common + | |
224 | 2 * (nla_attr_size(0) + nla_attr_size(sizeof(u64))); | |
225 | case NLA_BITFIELD32: | |
226 | return common + nla_attr_size(sizeof(u32)); | |
227 | case NLA_STRING: | |
228 | case NLA_NUL_STRING: | |
229 | case NLA_BINARY: | |
230 | /* maximum is common, u32 min-length/max-length */ | |
231 | return common + 2 * nla_attr_size(sizeof(u32)); | |
232 | case NLA_FLAG: | |
233 | return common; | |
234 | } | |
235 | ||
236 | /* this should then cause a warning later */ | |
237 | return 0; | |
238 | } | |
239 | ||
d2681e93 JB |
240 | static int |
241 | __netlink_policy_dump_write_attr(struct netlink_policy_dump_state *state, | |
242 | struct sk_buff *skb, | |
243 | const struct nla_policy *pt, | |
244 | int nestattr) | |
d07dcf9a | 245 | { |
44f3625b | 246 | int estimate = netlink_policy_dump_attr_size_estimate(pt); |
d07dcf9a | 247 | enum netlink_attribute_type type; |
d2681e93 | 248 | struct nlattr *attr; |
d07dcf9a | 249 | |
d2681e93 | 250 | attr = nla_nest_start(skb, nestattr); |
d07dcf9a | 251 | if (!attr) |
d2681e93 | 252 | return -ENOBUFS; |
d07dcf9a JB |
253 | |
254 | switch (pt->type) { | |
255 | default: | |
256 | case NLA_UNSPEC: | |
257 | case NLA_REJECT: | |
258 | /* skip - use NLA_MIN_LEN to advertise such */ | |
d2681e93 JB |
259 | nla_nest_cancel(skb, attr); |
260 | return -ENODATA; | |
d07dcf9a JB |
261 | case NLA_NESTED: |
262 | type = NL_ATTR_TYPE_NESTED; | |
df561f66 | 263 | fallthrough; |
d07dcf9a JB |
264 | case NLA_NESTED_ARRAY: |
265 | if (pt->type == NLA_NESTED_ARRAY) | |
266 | type = NL_ATTR_TYPE_NESTED_ARRAY; | |
d2681e93 | 267 | if (state && pt->nested_policy && pt->len && |
d07dcf9a | 268 | (nla_put_u32(skb, NL_POLICY_TYPE_ATTR_POLICY_IDX, |
04a351a6 JB |
269 | netlink_policy_dump_get_policy_idx(state, |
270 | pt->nested_policy, | |
271 | pt->len)) || | |
d07dcf9a JB |
272 | nla_put_u32(skb, NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE, |
273 | pt->len))) | |
274 | goto nla_put_failure; | |
275 | break; | |
276 | case NLA_U8: | |
277 | case NLA_U16: | |
278 | case NLA_U32: | |
279 | case NLA_U64: | |
280 | case NLA_MSECS: { | |
281 | struct netlink_range_validation range; | |
282 | ||
283 | if (pt->type == NLA_U8) | |
284 | type = NL_ATTR_TYPE_U8; | |
285 | else if (pt->type == NLA_U16) | |
286 | type = NL_ATTR_TYPE_U16; | |
287 | else if (pt->type == NLA_U32) | |
288 | type = NL_ATTR_TYPE_U32; | |
289 | else | |
290 | type = NL_ATTR_TYPE_U64; | |
291 | ||
bdbb4e29 JK |
292 | if (pt->validation_type == NLA_VALIDATE_MASK) { |
293 | if (nla_put_u64_64bit(skb, NL_POLICY_TYPE_ATTR_MASK, | |
294 | pt->mask, | |
295 | NL_POLICY_TYPE_ATTR_PAD)) | |
296 | goto nla_put_failure; | |
297 | break; | |
298 | } | |
299 | ||
d07dcf9a JB |
300 | nla_get_range_unsigned(pt, &range); |
301 | ||
302 | if (nla_put_u64_64bit(skb, NL_POLICY_TYPE_ATTR_MIN_VALUE_U, | |
303 | range.min, NL_POLICY_TYPE_ATTR_PAD) || | |
304 | nla_put_u64_64bit(skb, NL_POLICY_TYPE_ATTR_MAX_VALUE_U, | |
305 | range.max, NL_POLICY_TYPE_ATTR_PAD)) | |
306 | goto nla_put_failure; | |
307 | break; | |
308 | } | |
309 | case NLA_S8: | |
310 | case NLA_S16: | |
311 | case NLA_S32: | |
312 | case NLA_S64: { | |
313 | struct netlink_range_validation_signed range; | |
314 | ||
315 | if (pt->type == NLA_S8) | |
316 | type = NL_ATTR_TYPE_S8; | |
317 | else if (pt->type == NLA_S16) | |
318 | type = NL_ATTR_TYPE_S16; | |
319 | else if (pt->type == NLA_S32) | |
320 | type = NL_ATTR_TYPE_S32; | |
321 | else | |
322 | type = NL_ATTR_TYPE_S64; | |
323 | ||
324 | nla_get_range_signed(pt, &range); | |
325 | ||
326 | if (nla_put_s64(skb, NL_POLICY_TYPE_ATTR_MIN_VALUE_S, | |
327 | range.min, NL_POLICY_TYPE_ATTR_PAD) || | |
328 | nla_put_s64(skb, NL_POLICY_TYPE_ATTR_MAX_VALUE_S, | |
329 | range.max, NL_POLICY_TYPE_ATTR_PAD)) | |
330 | goto nla_put_failure; | |
331 | break; | |
332 | } | |
333 | case NLA_BITFIELD32: | |
334 | type = NL_ATTR_TYPE_BITFIELD32; | |
335 | if (nla_put_u32(skb, NL_POLICY_TYPE_ATTR_BITFIELD32_MASK, | |
336 | pt->bitfield32_valid)) | |
337 | goto nla_put_failure; | |
338 | break; | |
d07dcf9a JB |
339 | case NLA_STRING: |
340 | case NLA_NUL_STRING: | |
341 | case NLA_BINARY: | |
342 | if (pt->type == NLA_STRING) | |
343 | type = NL_ATTR_TYPE_STRING; | |
344 | else if (pt->type == NLA_NUL_STRING) | |
345 | type = NL_ATTR_TYPE_NUL_STRING; | |
346 | else | |
347 | type = NL_ATTR_TYPE_BINARY; | |
8aa26c57 | 348 | |
c30a3c95 JB |
349 | if (pt->validation_type == NLA_VALIDATE_RANGE || |
350 | pt->validation_type == NLA_VALIDATE_RANGE_WARN_TOO_LONG) { | |
8aa26c57 JB |
351 | struct netlink_range_validation range; |
352 | ||
353 | nla_get_range_unsigned(pt, &range); | |
354 | ||
355 | if (range.min && | |
356 | nla_put_u32(skb, NL_POLICY_TYPE_ATTR_MIN_LENGTH, | |
357 | range.min)) | |
358 | goto nla_put_failure; | |
359 | ||
360 | if (range.max < U16_MAX && | |
361 | nla_put_u32(skb, NL_POLICY_TYPE_ATTR_MAX_LENGTH, | |
362 | range.max)) | |
363 | goto nla_put_failure; | |
364 | } else if (pt->len && | |
365 | nla_put_u32(skb, NL_POLICY_TYPE_ATTR_MAX_LENGTH, | |
366 | pt->len)) { | |
d07dcf9a | 367 | goto nla_put_failure; |
8aa26c57 | 368 | } |
d07dcf9a JB |
369 | break; |
370 | case NLA_FLAG: | |
371 | type = NL_ATTR_TYPE_FLAG; | |
372 | break; | |
373 | } | |
374 | ||
375 | if (nla_put_u32(skb, NL_POLICY_TYPE_ATTR_TYPE, type)) | |
376 | goto nla_put_failure; | |
377 | ||
d07dcf9a | 378 | nla_nest_end(skb, attr); |
44f3625b JB |
379 | WARN_ON(attr->nla_len > estimate); |
380 | ||
d2681e93 JB |
381 | return 0; |
382 | nla_put_failure: | |
383 | nla_nest_cancel(skb, attr); | |
384 | return -ENOBUFS; | |
385 | } | |
386 | ||
44f3625b JB |
387 | /** |
388 | * netlink_policy_dump_write_attr - write a given attribute policy | |
389 | * @skb: the message skb to write to | |
390 | * @pt: the attribute's policy | |
391 | * @nestattr: the nested attribute ID to use | |
392 | * | |
393 | * Returns: 0 on success, an error code otherwise; -%ENODATA is | |
394 | * special, indicating that there's no policy data and | |
395 | * the attribute is generally rejected. | |
396 | */ | |
397 | int netlink_policy_dump_write_attr(struct sk_buff *skb, | |
398 | const struct nla_policy *pt, | |
399 | int nestattr) | |
400 | { | |
401 | return __netlink_policy_dump_write_attr(NULL, skb, pt, nestattr); | |
402 | } | |
403 | ||
d2681e93 JB |
404 | /** |
405 | * netlink_policy_dump_write - write current policy dump attributes | |
406 | * @skb: the message skb to write to | |
407 | * @state: the policy dump state | |
408 | * | |
409 | * Returns: 0 on success, an error code otherwise | |
410 | */ | |
411 | int netlink_policy_dump_write(struct sk_buff *skb, | |
412 | struct netlink_policy_dump_state *state) | |
413 | { | |
414 | const struct nla_policy *pt; | |
415 | struct nlattr *policy; | |
416 | bool again; | |
417 | int err; | |
418 | ||
419 | send_attribute: | |
420 | again = false; | |
421 | ||
422 | pt = &state->policies[state->policy_idx].policy[state->attr_idx]; | |
423 | ||
424 | policy = nla_nest_start(skb, state->policy_idx); | |
425 | if (!policy) | |
426 | return -ENOBUFS; | |
427 | ||
428 | err = __netlink_policy_dump_write_attr(state, skb, pt, state->attr_idx); | |
429 | if (err == -ENODATA) { | |
430 | nla_nest_cancel(skb, policy); | |
431 | again = true; | |
432 | goto next; | |
433 | } else if (err) { | |
434 | goto nla_put_failure; | |
435 | } | |
436 | ||
437 | /* finish and move state to next attribute */ | |
d07dcf9a JB |
438 | nla_nest_end(skb, policy); |
439 | ||
440 | next: | |
441 | state->attr_idx += 1; | |
442 | if (state->attr_idx > state->policies[state->policy_idx].maxtype) { | |
443 | state->attr_idx = 0; | |
444 | state->policy_idx++; | |
445 | } | |
446 | ||
447 | if (again) { | |
448 | if (netlink_policy_dump_finished(state)) | |
449 | return -ENODATA; | |
450 | goto send_attribute; | |
451 | } | |
452 | ||
453 | return 0; | |
454 | ||
455 | nla_put_failure: | |
456 | nla_nest_cancel(skb, policy); | |
457 | return -ENOBUFS; | |
458 | } | |
949ca6b8 | 459 | |
04a351a6 JB |
460 | /** |
461 | * netlink_policy_dump_free - free policy dump state | |
462 | * @state: the policy dump state to free | |
463 | * | |
464 | * Call this from the done() method to ensure dump state is freed. | |
465 | */ | |
adc84845 | 466 | void netlink_policy_dump_free(struct netlink_policy_dump_state *state) |
949ca6b8 | 467 | { |
949ca6b8 JB |
468 | kfree(state); |
469 | } |