Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

netlink: add infrastructure to expose policies to userspace

Add, and use in generic netlink, helpers to dump out a netlink
policy to userspace, including all the range validation data,
nested policies etc.

This lets userspace discover what the kernel understands.

For families/commands other than generic netlink, the helpers
need to be used directly in an appropriate command, or we can
add some infrastructure (a new netlink family) that those can
register their policies with for introspection. I'm not that
familiar with non-generic netlink, so that's left out for now.

The data exposed to userspace also includes min and max length
for binary/string data, I've done that instead of letting the
userspace tools figure out whether min/max is intended based
on the type so that we can extend this later in the kernel, we
might want to just use the range data for example.

Because of this, I opted to not directly expose the NLA_*
values, even if some of them are already exposed via BPF, as
with min/max length we don't need to have different types here
for NLA_BINARY/NLA_MIN_LEN/NLA_EXACT_LEN, we just make them
all NL_ATTR_TYPE_BINARY with min/max length optionally set.

Similarly, we don't really need NLA_MSECS, and perhaps can
remove it in the future - but not if we encode it into the
userspace API now. It gets mapped to NL_ATTR_TYPE_U64 here.

Note that the exposing here corresponds to the strict policy
interpretation, and NLA_UNSPEC items are omitted entirely.
To get those, change them to NLA_MIN_LEN which behaves in
exactly the same way, but is exposed.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Johannes Berg and committed by
David S. Miller
d07dcf9a 2c28ae48

+498 -1
+6
include/net/netlink.h
··· 1933 1933 void nla_get_range_signed(const struct nla_policy *pt, 1934 1934 struct netlink_range_validation_signed *range); 1935 1935 1936 + int netlink_policy_dump_start(const struct nla_policy *policy, 1937 + unsigned int maxtype, 1938 + unsigned long *state); 1939 + bool netlink_policy_dump_loop(unsigned long *state); 1940 + int netlink_policy_dump_write(struct sk_buff *skb, unsigned long state); 1941 + 1936 1942 #endif
+2
include/uapi/linux/genetlink.h
··· 48 48 CTRL_CMD_NEWMCAST_GRP, 49 49 CTRL_CMD_DELMCAST_GRP, 50 50 CTRL_CMD_GETMCAST_GRP, /* unused */ 51 + CTRL_CMD_GETPOLICY, 51 52 __CTRL_CMD_MAX, 52 53 }; 53 54 ··· 63 62 CTRL_ATTR_MAXATTR, 64 63 CTRL_ATTR_OPS, 65 64 CTRL_ATTR_MCAST_GROUPS, 65 + CTRL_ATTR_POLICY, 66 66 __CTRL_ATTR_MAX, 67 67 }; 68 68
+103
include/uapi/linux/netlink.h
··· 249 249 __u32 selector; 250 250 }; 251 251 252 + /* 253 + * policy descriptions - it's specific to each family how this is used 254 + * Normally, it should be retrieved via a dump inside another attribute 255 + * specifying where it applies. 256 + */ 257 + 258 + /** 259 + * enum netlink_attribute_type - type of an attribute 260 + * @NL_ATTR_TYPE_INVALID: unused 261 + * @NL_ATTR_TYPE_FLAG: flag attribute (present/not present) 262 + * @NL_ATTR_TYPE_U8: 8-bit unsigned attribute 263 + * @NL_ATTR_TYPE_U16: 16-bit unsigned attribute 264 + * @NL_ATTR_TYPE_U32: 32-bit unsigned attribute 265 + * @NL_ATTR_TYPE_U64: 64-bit unsigned attribute 266 + * @NL_ATTR_TYPE_S8: 8-bit signed attribute 267 + * @NL_ATTR_TYPE_S16: 16-bit signed attribute 268 + * @NL_ATTR_TYPE_S32: 32-bit signed attribute 269 + * @NL_ATTR_TYPE_S64: 64-bit signed attribute 270 + * @NL_ATTR_TYPE_BINARY: binary data, min/max length may be specified 271 + * @NL_ATTR_TYPE_STRING: string, min/max length may be specified 272 + * @NL_ATTR_TYPE_NUL_STRING: NUL-terminated string, 273 + * min/max length may be specified 274 + * @NL_ATTR_TYPE_NESTED: nested, i.e. the content of this attribute 275 + * consists of sub-attributes. The nested policy and maxtype 276 + * inside may be specified. 277 + * @NL_ATTR_TYPE_NESTED_ARRAY: nested array, i.e. the content of this 278 + * attribute contains sub-attributes whose type is irrelevant 279 + * (just used to separate the array entries) and each such array 280 + * entry has attributes again, the policy for those inner ones 281 + * and the corresponding maxtype may be specified. 282 + * @NL_ATTR_TYPE_BITFIELD32: &struct nla_bitfield32 attribute 283 + */ 284 + enum netlink_attribute_type { 285 + NL_ATTR_TYPE_INVALID, 286 + 287 + NL_ATTR_TYPE_FLAG, 288 + 289 + NL_ATTR_TYPE_U8, 290 + NL_ATTR_TYPE_U16, 291 + NL_ATTR_TYPE_U32, 292 + NL_ATTR_TYPE_U64, 293 + 294 + NL_ATTR_TYPE_S8, 295 + NL_ATTR_TYPE_S16, 296 + NL_ATTR_TYPE_S32, 297 + NL_ATTR_TYPE_S64, 298 + 299 + NL_ATTR_TYPE_BINARY, 300 + NL_ATTR_TYPE_STRING, 301 + NL_ATTR_TYPE_NUL_STRING, 302 + 303 + NL_ATTR_TYPE_NESTED, 304 + NL_ATTR_TYPE_NESTED_ARRAY, 305 + 306 + NL_ATTR_TYPE_BITFIELD32, 307 + }; 308 + 309 + /** 310 + * enum netlink_policy_type_attr - policy type attributes 311 + * @NL_POLICY_TYPE_ATTR_UNSPEC: unused 312 + * @NL_POLICY_TYPE_ATTR_TYPE: type of the attribute, 313 + * &enum netlink_attribute_type (U32) 314 + * @NL_POLICY_TYPE_ATTR_MIN_VALUE_S: minimum value for signed 315 + * integers (S64) 316 + * @NL_POLICY_TYPE_ATTR_MAX_VALUE_S: maximum value for signed 317 + * integers (S64) 318 + * @NL_POLICY_TYPE_ATTR_MIN_VALUE_U: minimum value for unsigned 319 + * integers (U64) 320 + * @NL_POLICY_TYPE_ATTR_MAX_VALUE_U: maximum value for unsigned 321 + * integers (U64) 322 + * @NL_POLICY_TYPE_ATTR_MIN_LENGTH: minimum length for binary 323 + * attributes, no minimum if not given (U32) 324 + * @NL_POLICY_TYPE_ATTR_MAX_LENGTH: maximum length for binary 325 + * attributes, no maximum if not given (U32) 326 + * @NL_POLICY_TYPE_ATTR_POLICY_IDX: sub policy for nested and 327 + * nested array types (U32) 328 + * @NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE: maximum sub policy 329 + * attribute for nested and nested array types, this can 330 + * in theory be < the size of the policy pointed to by 331 + * the index, if limited inside the nesting (U32) 332 + * @NL_POLICY_TYPE_ATTR_BITFIELD32_MASK: valid mask for the 333 + * bitfield32 type (U32) 334 + * @NL_POLICY_TYPE_ATTR_PAD: pad attribute for 64-bit alignment 335 + */ 336 + enum netlink_policy_type_attr { 337 + NL_POLICY_TYPE_ATTR_UNSPEC, 338 + NL_POLICY_TYPE_ATTR_TYPE, 339 + NL_POLICY_TYPE_ATTR_MIN_VALUE_S, 340 + NL_POLICY_TYPE_ATTR_MAX_VALUE_S, 341 + NL_POLICY_TYPE_ATTR_MIN_VALUE_U, 342 + NL_POLICY_TYPE_ATTR_MAX_VALUE_U, 343 + NL_POLICY_TYPE_ATTR_MIN_LENGTH, 344 + NL_POLICY_TYPE_ATTR_MAX_LENGTH, 345 + NL_POLICY_TYPE_ATTR_POLICY_IDX, 346 + NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE, 347 + NL_POLICY_TYPE_ATTR_BITFIELD32_MASK, 348 + NL_POLICY_TYPE_ATTR_PAD, 349 + 350 + /* keep last */ 351 + __NL_POLICY_TYPE_ATTR_MAX, 352 + NL_POLICY_TYPE_ATTR_MAX = __NL_POLICY_TYPE_ATTR_MAX - 1 353 + }; 354 + 252 355 #endif /* _UAPI__LINUX_NETLINK_H */
+1 -1
net/netlink/Makefile
··· 3 3 # Makefile for the netlink driver. 4 4 # 5 5 6 - obj-y := af_netlink.o genetlink.o 6 + obj-y := af_netlink.o genetlink.o policy.o 7 7 8 8 obj-$(CONFIG_NETLINK_DIAG) += netlink_diag.o 9 9 netlink_diag-y := diag.o
+78
net/netlink/genetlink.c
··· 1043 1043 return 0; 1044 1044 } 1045 1045 1046 + static int ctrl_dumppolicy(struct sk_buff *skb, struct netlink_callback *cb) 1047 + { 1048 + const struct genl_family *rt; 1049 + unsigned int fam_id = cb->args[0]; 1050 + int err; 1051 + 1052 + if (!fam_id) { 1053 + struct nlattr *tb[CTRL_ATTR_MAX + 1]; 1054 + 1055 + err = genlmsg_parse(cb->nlh, &genl_ctrl, tb, 1056 + genl_ctrl.maxattr, 1057 + genl_ctrl.policy, cb->extack); 1058 + if (err) 1059 + return err; 1060 + 1061 + if (!tb[CTRL_ATTR_FAMILY_ID] && !tb[CTRL_ATTR_FAMILY_NAME]) 1062 + return -EINVAL; 1063 + 1064 + if (tb[CTRL_ATTR_FAMILY_ID]) { 1065 + fam_id = nla_get_u16(tb[CTRL_ATTR_FAMILY_ID]); 1066 + } else { 1067 + rt = genl_family_find_byname( 1068 + nla_data(tb[CTRL_ATTR_FAMILY_NAME])); 1069 + if (!rt) 1070 + return -ENOENT; 1071 + fam_id = rt->id; 1072 + } 1073 + } 1074 + 1075 + rt = genl_family_find_byid(fam_id); 1076 + if (!rt) 1077 + return -ENOENT; 1078 + 1079 + if (!rt->policy) 1080 + return -ENODATA; 1081 + 1082 + err = netlink_policy_dump_start(rt->policy, rt->maxattr, &cb->args[1]); 1083 + if (err) 1084 + return err; 1085 + 1086 + while (netlink_policy_dump_loop(&cb->args[1])) { 1087 + void *hdr; 1088 + struct nlattr *nest; 1089 + 1090 + hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, 1091 + cb->nlh->nlmsg_seq, &genl_ctrl, 1092 + NLM_F_MULTI, CTRL_CMD_GETPOLICY); 1093 + if (!hdr) 1094 + goto nla_put_failure; 1095 + 1096 + if (nla_put_u16(skb, CTRL_ATTR_FAMILY_ID, rt->id)) 1097 + goto nla_put_failure; 1098 + 1099 + nest = nla_nest_start(skb, CTRL_ATTR_POLICY); 1100 + if (!nest) 1101 + goto nla_put_failure; 1102 + 1103 + if (netlink_policy_dump_write(skb, cb->args[1])) 1104 + goto nla_put_failure; 1105 + 1106 + nla_nest_end(skb, nest); 1107 + 1108 + genlmsg_end(skb, hdr); 1109 + continue; 1110 + 1111 + nla_put_failure: 1112 + genlmsg_cancel(skb, hdr); 1113 + break; 1114 + } 1115 + 1116 + cb->args[0] = fam_id; 1117 + return skb->len; 1118 + } 1119 + 1046 1120 static const struct genl_ops genl_ctrl_ops[] = { 1047 1121 { 1048 1122 .cmd = CTRL_CMD_GETFAMILY, 1049 1123 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 1050 1124 .doit = ctrl_getfamily, 1051 1125 .dumpit = ctrl_dumpfamily, 1126 + }, 1127 + { 1128 + .cmd = CTRL_CMD_GETPOLICY, 1129 + .dumpit = ctrl_dumppolicy, 1052 1130 }, 1053 1131 }; 1054 1132
+308
net/netlink/policy.c
··· 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 + 17 + struct nl_policy_dump { 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 + 27 + static int add_policy(struct nl_policy_dump **statep, 28 + const struct nla_policy *policy, 29 + unsigned int maxtype) 30 + { 31 + struct nl_policy_dump *state = *statep; 32 + unsigned int n_alloc, i; 33 + 34 + if (!policy || !maxtype) 35 + return 0; 36 + 37 + for (i = 0; i < state->n_alloc; i++) { 38 + if (state->policies[i].policy == policy) 39 + return 0; 40 + 41 + if (!state->policies[i].policy) { 42 + state->policies[i].policy = policy; 43 + state->policies[i].maxtype = maxtype; 44 + return 0; 45 + } 46 + } 47 + 48 + n_alloc = state->n_alloc + INITIAL_POLICIES_ALLOC; 49 + state = krealloc(state, struct_size(state, policies, n_alloc), 50 + GFP_KERNEL); 51 + if (!state) 52 + return -ENOMEM; 53 + 54 + state->policies[state->n_alloc].policy = policy; 55 + state->policies[state->n_alloc].maxtype = maxtype; 56 + state->n_alloc = n_alloc; 57 + *statep = state; 58 + 59 + return 0; 60 + } 61 + 62 + static unsigned int get_policy_idx(struct nl_policy_dump *state, 63 + const struct nla_policy *policy) 64 + { 65 + unsigned int i; 66 + 67 + for (i = 0; i < state->n_alloc; i++) { 68 + if (state->policies[i].policy == policy) 69 + return i; 70 + } 71 + 72 + WARN_ON_ONCE(1); 73 + return -1; 74 + } 75 + 76 + int netlink_policy_dump_start(const struct nla_policy *policy, 77 + unsigned int maxtype, 78 + unsigned long *_state) 79 + { 80 + struct nl_policy_dump *state; 81 + unsigned int policy_idx; 82 + int err; 83 + 84 + /* also returns 0 if "*_state" is our ERR_PTR() end marker */ 85 + if (*_state) 86 + return 0; 87 + 88 + /* 89 + * walk the policies and nested ones first, and build 90 + * a linear list of them. 91 + */ 92 + 93 + state = kzalloc(struct_size(state, policies, INITIAL_POLICIES_ALLOC), 94 + GFP_KERNEL); 95 + if (!state) 96 + return -ENOMEM; 97 + state->n_alloc = INITIAL_POLICIES_ALLOC; 98 + 99 + err = add_policy(&state, policy, maxtype); 100 + if (err) 101 + return err; 102 + 103 + for (policy_idx = 0; 104 + policy_idx < state->n_alloc && state->policies[policy_idx].policy; 105 + policy_idx++) { 106 + const struct nla_policy *policy; 107 + unsigned int type; 108 + 109 + policy = state->policies[policy_idx].policy; 110 + 111 + for (type = 0; 112 + type <= state->policies[policy_idx].maxtype; 113 + type++) { 114 + switch (policy[type].type) { 115 + case NLA_NESTED: 116 + case NLA_NESTED_ARRAY: 117 + err = add_policy(&state, 118 + policy[type].nested_policy, 119 + policy[type].len); 120 + if (err) 121 + return err; 122 + break; 123 + default: 124 + break; 125 + } 126 + } 127 + } 128 + 129 + *_state = (unsigned long)state; 130 + 131 + return 0; 132 + } 133 + 134 + static bool netlink_policy_dump_finished(struct nl_policy_dump *state) 135 + { 136 + return state->policy_idx >= state->n_alloc || 137 + !state->policies[state->policy_idx].policy; 138 + } 139 + 140 + bool netlink_policy_dump_loop(unsigned long *_state) 141 + { 142 + struct nl_policy_dump *state = (void *)*_state; 143 + 144 + if (IS_ERR(state)) 145 + return false; 146 + 147 + if (netlink_policy_dump_finished(state)) { 148 + kfree(state); 149 + /* store end marker instead of freed state */ 150 + *_state = (unsigned long)ERR_PTR(-ENOENT); 151 + return false; 152 + } 153 + 154 + return true; 155 + } 156 + 157 + int netlink_policy_dump_write(struct sk_buff *skb, unsigned long _state) 158 + { 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; 163 + bool again; 164 + 165 + send_attribute: 166 + again = false; 167 + 168 + pt = &state->policies[state->policy_idx].policy[state->attr_idx]; 169 + 170 + policy = nla_nest_start(skb, state->policy_idx); 171 + if (!policy) 172 + return -ENOBUFS; 173 + 174 + attr = nla_nest_start(skb, state->attr_idx); 175 + if (!attr) 176 + goto nla_put_failure; 177 + 178 + switch (pt->type) { 179 + default: 180 + case NLA_UNSPEC: 181 + case NLA_REJECT: 182 + /* skip - use NLA_MIN_LEN to advertise such */ 183 + nla_nest_cancel(skb, policy); 184 + again = true; 185 + goto next; 186 + case NLA_NESTED: 187 + type = NL_ATTR_TYPE_NESTED; 188 + /* fall through */ 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, 196 + pt->len))) 197 + goto nla_put_failure; 198 + break; 199 + case NLA_U8: 200 + case NLA_U16: 201 + case NLA_U32: 202 + case NLA_U64: 203 + case NLA_MSECS: { 204 + struct netlink_range_validation range; 205 + 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; 212 + else 213 + type = NL_ATTR_TYPE_U64; 214 + 215 + nla_get_range_unsigned(pt, &range); 216 + 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; 222 + break; 223 + } 224 + case NLA_S8: 225 + case NLA_S16: 226 + case NLA_S32: 227 + case NLA_S64: { 228 + struct netlink_range_validation_signed range; 229 + 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; 236 + else 237 + type = NL_ATTR_TYPE_S64; 238 + 239 + nla_get_range_signed(pt, &range); 240 + 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; 246 + break; 247 + } 248 + case NLA_BITFIELD32: 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; 253 + break; 254 + case NLA_EXACT_LEN: 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; 259 + break; 260 + case NLA_STRING: 261 + case NLA_NUL_STRING: 262 + case NLA_BINARY: 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; 267 + else 268 + type = NL_ATTR_TYPE_BINARY; 269 + if (pt->len && nla_put_u32(skb, NL_POLICY_TYPE_ATTR_MAX_LENGTH, 270 + pt->len)) 271 + goto nla_put_failure; 272 + break; 273 + case NLA_MIN_LEN: 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; 277 + break; 278 + case NLA_FLAG: 279 + type = NL_ATTR_TYPE_FLAG; 280 + break; 281 + } 282 + 283 + if (nla_put_u32(skb, NL_POLICY_TYPE_ATTR_TYPE, type)) 284 + goto nla_put_failure; 285 + 286 + /* finish and move state to next attribute */ 287 + nla_nest_end(skb, attr); 288 + nla_nest_end(skb, policy); 289 + 290 + next: 291 + state->attr_idx += 1; 292 + if (state->attr_idx > state->policies[state->policy_idx].maxtype) { 293 + state->attr_idx = 0; 294 + state->policy_idx++; 295 + } 296 + 297 + if (again) { 298 + if (netlink_policy_dump_finished(state)) 299 + return -ENODATA; 300 + goto send_attribute; 301 + } 302 + 303 + return 0; 304 + 305 + nla_put_failure: 306 + nla_nest_cancel(skb, policy); 307 + return -ENOBUFS; 308 + }