1 // SPDX-License-Identifier: GPL-2.0
3 * NETLINK Policy advertisement to userspace
5 * Authors: Johannes Berg <johannes@sipsolutions.net>
7 * Copyright 2019 Intel Corporation
10 #include <linux/kernel.h>
11 #include <linux/errno.h>
12 #include <linux/types.h>
13 #include <net/netlink.h>
15 #define INITIAL_POLICIES_ALLOC 10
17 struct nl_policy_dump
{
18 unsigned int policy_idx
;
19 unsigned int attr_idx
;
22 const struct nla_policy
*policy
;
27 static int add_policy(struct nl_policy_dump
**statep
,
28 const struct nla_policy
*policy
,
31 struct nl_policy_dump
*state
= *statep
;
32 unsigned int n_alloc
, i
;
34 if (!policy
|| !maxtype
)
37 for (i
= 0; i
< state
->n_alloc
; i
++) {
38 if (state
->policies
[i
].policy
== policy
)
41 if (!state
->policies
[i
].policy
) {
42 state
->policies
[i
].policy
= policy
;
43 state
->policies
[i
].maxtype
= maxtype
;
48 n_alloc
= state
->n_alloc
+ INITIAL_POLICIES_ALLOC
;
49 state
= krealloc(state
, struct_size(state
, policies
, n_alloc
),
54 state
->policies
[state
->n_alloc
].policy
= policy
;
55 state
->policies
[state
->n_alloc
].maxtype
= maxtype
;
56 state
->n_alloc
= n_alloc
;
62 static unsigned int get_policy_idx(struct nl_policy_dump
*state
,
63 const struct nla_policy
*policy
)
67 for (i
= 0; i
< state
->n_alloc
; i
++) {
68 if (state
->policies
[i
].policy
== policy
)
76 int netlink_policy_dump_start(const struct nla_policy
*policy
,
78 unsigned long *_state
)
80 struct nl_policy_dump
*state
;
81 unsigned int policy_idx
;
84 /* also returns 0 if "*_state" is our ERR_PTR() end marker */
89 * walk the policies and nested ones first, and build
90 * a linear list of them.
93 state
= kzalloc(struct_size(state
, policies
, INITIAL_POLICIES_ALLOC
),
97 state
->n_alloc
= INITIAL_POLICIES_ALLOC
;
99 err
= add_policy(&state
, policy
, maxtype
);
104 policy_idx
< state
->n_alloc
&& state
->policies
[policy_idx
].policy
;
106 const struct nla_policy
*policy
;
109 policy
= state
->policies
[policy_idx
].policy
;
112 type
<= state
->policies
[policy_idx
].maxtype
;
114 switch (policy
[type
].type
) {
116 case NLA_NESTED_ARRAY
:
117 err
= add_policy(&state
,
118 policy
[type
].nested_policy
,
129 *_state
= (unsigned long)state
;
134 static bool netlink_policy_dump_finished(struct nl_policy_dump
*state
)
136 return state
->policy_idx
>= state
->n_alloc
||
137 !state
->policies
[state
->policy_idx
].policy
;
140 bool netlink_policy_dump_loop(unsigned long *_state
)
142 struct nl_policy_dump
*state
= (void *)*_state
;
147 if (netlink_policy_dump_finished(state
)) {
149 /* store end marker instead of freed state */
150 *_state
= (unsigned long)ERR_PTR(-ENOENT
);
157 int netlink_policy_dump_write(struct sk_buff
*skb
, unsigned long _state
)
159 struct nl_policy_dump
*state
= (void *)_state
;
160 const struct nla_policy
*pt
;
161 struct nlattr
*policy
, *attr
;
162 enum netlink_attribute_type type
;
168 pt
= &state
->policies
[state
->policy_idx
].policy
[state
->attr_idx
];
170 policy
= nla_nest_start(skb
, state
->policy_idx
);
174 attr
= nla_nest_start(skb
, state
->attr_idx
);
176 goto nla_put_failure
;
182 /* skip - use NLA_MIN_LEN to advertise such */
183 nla_nest_cancel(skb
, policy
);
187 type
= NL_ATTR_TYPE_NESTED
;
189 case NLA_NESTED_ARRAY
:
190 if (pt
->type
== NLA_NESTED_ARRAY
)
191 type
= NL_ATTR_TYPE_NESTED_ARRAY
;
192 if (pt
->nested_policy
&& pt
->len
&&
193 (nla_put_u32(skb
, NL_POLICY_TYPE_ATTR_POLICY_IDX
,
194 get_policy_idx(state
, pt
->nested_policy
)) ||
195 nla_put_u32(skb
, NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE
,
197 goto nla_put_failure
;
204 struct netlink_range_validation range
;
206 if (pt
->type
== NLA_U8
)
207 type
= NL_ATTR_TYPE_U8
;
208 else if (pt
->type
== NLA_U16
)
209 type
= NL_ATTR_TYPE_U16
;
210 else if (pt
->type
== NLA_U32
)
211 type
= NL_ATTR_TYPE_U32
;
213 type
= NL_ATTR_TYPE_U64
;
215 nla_get_range_unsigned(pt
, &range
);
217 if (nla_put_u64_64bit(skb
, NL_POLICY_TYPE_ATTR_MIN_VALUE_U
,
218 range
.min
, NL_POLICY_TYPE_ATTR_PAD
) ||
219 nla_put_u64_64bit(skb
, NL_POLICY_TYPE_ATTR_MAX_VALUE_U
,
220 range
.max
, NL_POLICY_TYPE_ATTR_PAD
))
221 goto nla_put_failure
;
228 struct netlink_range_validation_signed range
;
230 if (pt
->type
== NLA_S8
)
231 type
= NL_ATTR_TYPE_S8
;
232 else if (pt
->type
== NLA_S16
)
233 type
= NL_ATTR_TYPE_S16
;
234 else if (pt
->type
== NLA_S32
)
235 type
= NL_ATTR_TYPE_S32
;
237 type
= NL_ATTR_TYPE_S64
;
239 nla_get_range_signed(pt
, &range
);
241 if (nla_put_s64(skb
, NL_POLICY_TYPE_ATTR_MIN_VALUE_S
,
242 range
.min
, NL_POLICY_TYPE_ATTR_PAD
) ||
243 nla_put_s64(skb
, NL_POLICY_TYPE_ATTR_MAX_VALUE_S
,
244 range
.max
, NL_POLICY_TYPE_ATTR_PAD
))
245 goto nla_put_failure
;
249 type
= NL_ATTR_TYPE_BITFIELD32
;
250 if (nla_put_u32(skb
, NL_POLICY_TYPE_ATTR_BITFIELD32_MASK
,
251 pt
->bitfield32_valid
))
252 goto nla_put_failure
;
255 type
= NL_ATTR_TYPE_BINARY
;
256 if (nla_put_u32(skb
, NL_POLICY_TYPE_ATTR_MIN_LENGTH
, pt
->len
) ||
257 nla_put_u32(skb
, NL_POLICY_TYPE_ATTR_MAX_LENGTH
, pt
->len
))
258 goto nla_put_failure
;
263 if (pt
->type
== NLA_STRING
)
264 type
= NL_ATTR_TYPE_STRING
;
265 else if (pt
->type
== NLA_NUL_STRING
)
266 type
= NL_ATTR_TYPE_NUL_STRING
;
268 type
= NL_ATTR_TYPE_BINARY
;
269 if (pt
->len
&& nla_put_u32(skb
, NL_POLICY_TYPE_ATTR_MAX_LENGTH
,
271 goto nla_put_failure
;
274 type
= NL_ATTR_TYPE_BINARY
;
275 if (nla_put_u32(skb
, NL_POLICY_TYPE_ATTR_MIN_LENGTH
, pt
->len
))
276 goto nla_put_failure
;
279 type
= NL_ATTR_TYPE_FLAG
;
283 if (nla_put_u32(skb
, NL_POLICY_TYPE_ATTR_TYPE
, type
))
284 goto nla_put_failure
;
286 /* finish and move state to next attribute */
287 nla_nest_end(skb
, attr
);
288 nla_nest_end(skb
, policy
);
291 state
->attr_idx
+= 1;
292 if (state
->attr_idx
> state
->policies
[state
->policy_idx
].maxtype
) {
298 if (netlink_policy_dump_finished(state
))
306 nla_nest_cancel(skb
, policy
);