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