1d07dcf9aSJohannes Berg // SPDX-License-Identifier: GPL-2.0 2d07dcf9aSJohannes Berg /* 3d07dcf9aSJohannes Berg * NETLINK Policy advertisement to userspace 4d07dcf9aSJohannes Berg * 5d07dcf9aSJohannes Berg * Authors: Johannes Berg <johannes@sipsolutions.net> 6d07dcf9aSJohannes Berg * 7d07dcf9aSJohannes Berg * Copyright 2019 Intel Corporation 8d07dcf9aSJohannes Berg */ 9d07dcf9aSJohannes Berg 10d07dcf9aSJohannes Berg #include <linux/kernel.h> 11d07dcf9aSJohannes Berg #include <linux/errno.h> 12d07dcf9aSJohannes Berg #include <linux/types.h> 13d07dcf9aSJohannes Berg #include <net/netlink.h> 14d07dcf9aSJohannes Berg 15d07dcf9aSJohannes Berg #define INITIAL_POLICIES_ALLOC 10 16d07dcf9aSJohannes Berg 17d07dcf9aSJohannes Berg struct nl_policy_dump { 18d07dcf9aSJohannes Berg unsigned int policy_idx; 19d07dcf9aSJohannes Berg unsigned int attr_idx; 20d07dcf9aSJohannes Berg unsigned int n_alloc; 21d07dcf9aSJohannes Berg struct { 22d07dcf9aSJohannes Berg const struct nla_policy *policy; 23d07dcf9aSJohannes Berg unsigned int maxtype; 24d07dcf9aSJohannes Berg } policies[]; 25d07dcf9aSJohannes Berg }; 26d07dcf9aSJohannes Berg 27d07dcf9aSJohannes Berg static int add_policy(struct nl_policy_dump **statep, 28d07dcf9aSJohannes Berg const struct nla_policy *policy, 29d07dcf9aSJohannes Berg unsigned int maxtype) 30d07dcf9aSJohannes Berg { 31d07dcf9aSJohannes Berg struct nl_policy_dump *state = *statep; 32d07dcf9aSJohannes Berg unsigned int n_alloc, i; 33d07dcf9aSJohannes Berg 34d07dcf9aSJohannes Berg if (!policy || !maxtype) 35d07dcf9aSJohannes Berg return 0; 36d07dcf9aSJohannes Berg 37d07dcf9aSJohannes Berg for (i = 0; i < state->n_alloc; i++) { 38d07dcf9aSJohannes Berg if (state->policies[i].policy == policy) 39d07dcf9aSJohannes Berg return 0; 40d07dcf9aSJohannes Berg 41d07dcf9aSJohannes Berg if (!state->policies[i].policy) { 42d07dcf9aSJohannes Berg state->policies[i].policy = policy; 43d07dcf9aSJohannes Berg state->policies[i].maxtype = maxtype; 44d07dcf9aSJohannes Berg return 0; 45d07dcf9aSJohannes Berg } 46d07dcf9aSJohannes Berg } 47d07dcf9aSJohannes Berg 48d07dcf9aSJohannes Berg n_alloc = state->n_alloc + INITIAL_POLICIES_ALLOC; 49d07dcf9aSJohannes Berg state = krealloc(state, struct_size(state, policies, n_alloc), 50d07dcf9aSJohannes Berg GFP_KERNEL); 51d07dcf9aSJohannes Berg if (!state) 52d07dcf9aSJohannes Berg return -ENOMEM; 53d07dcf9aSJohannes Berg 54d1fb5559SJohannes Berg memset(&state->policies[state->n_alloc], 0, 55d1fb5559SJohannes Berg flex_array_size(state, policies, n_alloc - state->n_alloc)); 56d1fb5559SJohannes Berg 57d07dcf9aSJohannes Berg state->policies[state->n_alloc].policy = policy; 58d07dcf9aSJohannes Berg state->policies[state->n_alloc].maxtype = maxtype; 59d07dcf9aSJohannes Berg state->n_alloc = n_alloc; 60d07dcf9aSJohannes Berg *statep = state; 61d07dcf9aSJohannes Berg 62d07dcf9aSJohannes Berg return 0; 63d07dcf9aSJohannes Berg } 64d07dcf9aSJohannes Berg 65d07dcf9aSJohannes Berg static unsigned int get_policy_idx(struct nl_policy_dump *state, 66d07dcf9aSJohannes Berg const struct nla_policy *policy) 67d07dcf9aSJohannes Berg { 68d07dcf9aSJohannes Berg unsigned int i; 69d07dcf9aSJohannes Berg 70d07dcf9aSJohannes Berg for (i = 0; i < state->n_alloc; i++) { 71d07dcf9aSJohannes Berg if (state->policies[i].policy == policy) 72d07dcf9aSJohannes Berg return i; 73d07dcf9aSJohannes Berg } 74d07dcf9aSJohannes Berg 75d07dcf9aSJohannes Berg WARN_ON_ONCE(1); 76d07dcf9aSJohannes Berg return -1; 77d07dcf9aSJohannes Berg } 78d07dcf9aSJohannes Berg 79d07dcf9aSJohannes Berg int netlink_policy_dump_start(const struct nla_policy *policy, 80d07dcf9aSJohannes Berg unsigned int maxtype, 81d07dcf9aSJohannes Berg unsigned long *_state) 82d07dcf9aSJohannes Berg { 83d07dcf9aSJohannes Berg struct nl_policy_dump *state; 84d07dcf9aSJohannes Berg unsigned int policy_idx; 85d07dcf9aSJohannes Berg int err; 86d07dcf9aSJohannes Berg 87d07dcf9aSJohannes Berg /* also returns 0 if "*_state" is our ERR_PTR() end marker */ 88d07dcf9aSJohannes Berg if (*_state) 89d07dcf9aSJohannes Berg return 0; 90d07dcf9aSJohannes Berg 91d07dcf9aSJohannes Berg /* 92d07dcf9aSJohannes Berg * walk the policies and nested ones first, and build 93d07dcf9aSJohannes Berg * a linear list of them. 94d07dcf9aSJohannes Berg */ 95d07dcf9aSJohannes Berg 96d07dcf9aSJohannes Berg state = kzalloc(struct_size(state, policies, INITIAL_POLICIES_ALLOC), 97d07dcf9aSJohannes Berg GFP_KERNEL); 98d07dcf9aSJohannes Berg if (!state) 99d07dcf9aSJohannes Berg return -ENOMEM; 100d07dcf9aSJohannes Berg state->n_alloc = INITIAL_POLICIES_ALLOC; 101d07dcf9aSJohannes Berg 102d07dcf9aSJohannes Berg err = add_policy(&state, policy, maxtype); 103d07dcf9aSJohannes Berg if (err) 104d07dcf9aSJohannes Berg return err; 105d07dcf9aSJohannes Berg 106d07dcf9aSJohannes Berg for (policy_idx = 0; 107d07dcf9aSJohannes Berg policy_idx < state->n_alloc && state->policies[policy_idx].policy; 108d07dcf9aSJohannes Berg policy_idx++) { 109d07dcf9aSJohannes Berg const struct nla_policy *policy; 110d07dcf9aSJohannes Berg unsigned int type; 111d07dcf9aSJohannes Berg 112d07dcf9aSJohannes Berg policy = state->policies[policy_idx].policy; 113d07dcf9aSJohannes Berg 114d07dcf9aSJohannes Berg for (type = 0; 115d07dcf9aSJohannes Berg type <= state->policies[policy_idx].maxtype; 116d07dcf9aSJohannes Berg type++) { 117d07dcf9aSJohannes Berg switch (policy[type].type) { 118d07dcf9aSJohannes Berg case NLA_NESTED: 119d07dcf9aSJohannes Berg case NLA_NESTED_ARRAY: 120d07dcf9aSJohannes Berg err = add_policy(&state, 121d07dcf9aSJohannes Berg policy[type].nested_policy, 122d07dcf9aSJohannes Berg policy[type].len); 123d07dcf9aSJohannes Berg if (err) 124d07dcf9aSJohannes Berg return err; 125d07dcf9aSJohannes Berg break; 126d07dcf9aSJohannes Berg default: 127d07dcf9aSJohannes Berg break; 128d07dcf9aSJohannes Berg } 129d07dcf9aSJohannes Berg } 130d07dcf9aSJohannes Berg } 131d07dcf9aSJohannes Berg 132d07dcf9aSJohannes Berg *_state = (unsigned long)state; 133d07dcf9aSJohannes Berg 134d07dcf9aSJohannes Berg return 0; 135d07dcf9aSJohannes Berg } 136d07dcf9aSJohannes Berg 137d07dcf9aSJohannes Berg static bool netlink_policy_dump_finished(struct nl_policy_dump *state) 138d07dcf9aSJohannes Berg { 139d07dcf9aSJohannes Berg return state->policy_idx >= state->n_alloc || 140d07dcf9aSJohannes Berg !state->policies[state->policy_idx].policy; 141d07dcf9aSJohannes Berg } 142d07dcf9aSJohannes Berg 143d07dcf9aSJohannes Berg bool netlink_policy_dump_loop(unsigned long *_state) 144d07dcf9aSJohannes Berg { 145d07dcf9aSJohannes Berg struct nl_policy_dump *state = (void *)*_state; 146d07dcf9aSJohannes Berg 147d07dcf9aSJohannes Berg if (IS_ERR(state)) 148d07dcf9aSJohannes Berg return false; 149d07dcf9aSJohannes Berg 150d07dcf9aSJohannes Berg if (netlink_policy_dump_finished(state)) { 151d07dcf9aSJohannes Berg kfree(state); 152d07dcf9aSJohannes Berg /* store end marker instead of freed state */ 153d07dcf9aSJohannes Berg *_state = (unsigned long)ERR_PTR(-ENOENT); 154d07dcf9aSJohannes Berg return false; 155d07dcf9aSJohannes Berg } 156d07dcf9aSJohannes Berg 157d07dcf9aSJohannes Berg return true; 158d07dcf9aSJohannes Berg } 159d07dcf9aSJohannes Berg 160d07dcf9aSJohannes Berg int netlink_policy_dump_write(struct sk_buff *skb, unsigned long _state) 161d07dcf9aSJohannes Berg { 162d07dcf9aSJohannes Berg struct nl_policy_dump *state = (void *)_state; 163d07dcf9aSJohannes Berg const struct nla_policy *pt; 164d07dcf9aSJohannes Berg struct nlattr *policy, *attr; 165d07dcf9aSJohannes Berg enum netlink_attribute_type type; 166d07dcf9aSJohannes Berg bool again; 167d07dcf9aSJohannes Berg 168d07dcf9aSJohannes Berg send_attribute: 169d07dcf9aSJohannes Berg again = false; 170d07dcf9aSJohannes Berg 171d07dcf9aSJohannes Berg pt = &state->policies[state->policy_idx].policy[state->attr_idx]; 172d07dcf9aSJohannes Berg 173d07dcf9aSJohannes Berg policy = nla_nest_start(skb, state->policy_idx); 174d07dcf9aSJohannes Berg if (!policy) 175d07dcf9aSJohannes Berg return -ENOBUFS; 176d07dcf9aSJohannes Berg 177d07dcf9aSJohannes Berg attr = nla_nest_start(skb, state->attr_idx); 178d07dcf9aSJohannes Berg if (!attr) 179d07dcf9aSJohannes Berg goto nla_put_failure; 180d07dcf9aSJohannes Berg 181d07dcf9aSJohannes Berg switch (pt->type) { 182d07dcf9aSJohannes Berg default: 183d07dcf9aSJohannes Berg case NLA_UNSPEC: 184d07dcf9aSJohannes Berg case NLA_REJECT: 185d07dcf9aSJohannes Berg /* skip - use NLA_MIN_LEN to advertise such */ 186d07dcf9aSJohannes Berg nla_nest_cancel(skb, policy); 187d07dcf9aSJohannes Berg again = true; 188d07dcf9aSJohannes Berg goto next; 189d07dcf9aSJohannes Berg case NLA_NESTED: 190d07dcf9aSJohannes Berg type = NL_ATTR_TYPE_NESTED; 191d07dcf9aSJohannes Berg /* fall through */ 192d07dcf9aSJohannes Berg case NLA_NESTED_ARRAY: 193d07dcf9aSJohannes Berg if (pt->type == NLA_NESTED_ARRAY) 194d07dcf9aSJohannes Berg type = NL_ATTR_TYPE_NESTED_ARRAY; 195d07dcf9aSJohannes Berg if (pt->nested_policy && pt->len && 196d07dcf9aSJohannes Berg (nla_put_u32(skb, NL_POLICY_TYPE_ATTR_POLICY_IDX, 197d07dcf9aSJohannes Berg get_policy_idx(state, pt->nested_policy)) || 198d07dcf9aSJohannes Berg nla_put_u32(skb, NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE, 199d07dcf9aSJohannes Berg pt->len))) 200d07dcf9aSJohannes Berg goto nla_put_failure; 201d07dcf9aSJohannes Berg break; 202d07dcf9aSJohannes Berg case NLA_U8: 203d07dcf9aSJohannes Berg case NLA_U16: 204d07dcf9aSJohannes Berg case NLA_U32: 205d07dcf9aSJohannes Berg case NLA_U64: 206d07dcf9aSJohannes Berg case NLA_MSECS: { 207d07dcf9aSJohannes Berg struct netlink_range_validation range; 208d07dcf9aSJohannes Berg 209d07dcf9aSJohannes Berg if (pt->type == NLA_U8) 210d07dcf9aSJohannes Berg type = NL_ATTR_TYPE_U8; 211d07dcf9aSJohannes Berg else if (pt->type == NLA_U16) 212d07dcf9aSJohannes Berg type = NL_ATTR_TYPE_U16; 213d07dcf9aSJohannes Berg else if (pt->type == NLA_U32) 214d07dcf9aSJohannes Berg type = NL_ATTR_TYPE_U32; 215d07dcf9aSJohannes Berg else 216d07dcf9aSJohannes Berg type = NL_ATTR_TYPE_U64; 217d07dcf9aSJohannes Berg 218d07dcf9aSJohannes Berg nla_get_range_unsigned(pt, &range); 219d07dcf9aSJohannes Berg 220d07dcf9aSJohannes Berg if (nla_put_u64_64bit(skb, NL_POLICY_TYPE_ATTR_MIN_VALUE_U, 221d07dcf9aSJohannes Berg range.min, NL_POLICY_TYPE_ATTR_PAD) || 222d07dcf9aSJohannes Berg nla_put_u64_64bit(skb, NL_POLICY_TYPE_ATTR_MAX_VALUE_U, 223d07dcf9aSJohannes Berg range.max, NL_POLICY_TYPE_ATTR_PAD)) 224d07dcf9aSJohannes Berg goto nla_put_failure; 225d07dcf9aSJohannes Berg break; 226d07dcf9aSJohannes Berg } 227d07dcf9aSJohannes Berg case NLA_S8: 228d07dcf9aSJohannes Berg case NLA_S16: 229d07dcf9aSJohannes Berg case NLA_S32: 230d07dcf9aSJohannes Berg case NLA_S64: { 231d07dcf9aSJohannes Berg struct netlink_range_validation_signed range; 232d07dcf9aSJohannes Berg 233d07dcf9aSJohannes Berg if (pt->type == NLA_S8) 234d07dcf9aSJohannes Berg type = NL_ATTR_TYPE_S8; 235d07dcf9aSJohannes Berg else if (pt->type == NLA_S16) 236d07dcf9aSJohannes Berg type = NL_ATTR_TYPE_S16; 237d07dcf9aSJohannes Berg else if (pt->type == NLA_S32) 238d07dcf9aSJohannes Berg type = NL_ATTR_TYPE_S32; 239d07dcf9aSJohannes Berg else 240d07dcf9aSJohannes Berg type = NL_ATTR_TYPE_S64; 241d07dcf9aSJohannes Berg 242d07dcf9aSJohannes Berg nla_get_range_signed(pt, &range); 243d07dcf9aSJohannes Berg 244d07dcf9aSJohannes Berg if (nla_put_s64(skb, NL_POLICY_TYPE_ATTR_MIN_VALUE_S, 245d07dcf9aSJohannes Berg range.min, NL_POLICY_TYPE_ATTR_PAD) || 246d07dcf9aSJohannes Berg nla_put_s64(skb, NL_POLICY_TYPE_ATTR_MAX_VALUE_S, 247d07dcf9aSJohannes Berg range.max, NL_POLICY_TYPE_ATTR_PAD)) 248d07dcf9aSJohannes Berg goto nla_put_failure; 249d07dcf9aSJohannes Berg break; 250d07dcf9aSJohannes Berg } 251d07dcf9aSJohannes Berg case NLA_BITFIELD32: 252d07dcf9aSJohannes Berg type = NL_ATTR_TYPE_BITFIELD32; 253d07dcf9aSJohannes Berg if (nla_put_u32(skb, NL_POLICY_TYPE_ATTR_BITFIELD32_MASK, 254d07dcf9aSJohannes Berg pt->bitfield32_valid)) 255d07dcf9aSJohannes Berg goto nla_put_failure; 256d07dcf9aSJohannes Berg break; 257d07dcf9aSJohannes Berg case NLA_STRING: 258d07dcf9aSJohannes Berg case NLA_NUL_STRING: 259d07dcf9aSJohannes Berg case NLA_BINARY: 260d07dcf9aSJohannes Berg if (pt->type == NLA_STRING) 261d07dcf9aSJohannes Berg type = NL_ATTR_TYPE_STRING; 262d07dcf9aSJohannes Berg else if (pt->type == NLA_NUL_STRING) 263d07dcf9aSJohannes Berg type = NL_ATTR_TYPE_NUL_STRING; 264d07dcf9aSJohannes Berg else 265d07dcf9aSJohannes Berg type = NL_ATTR_TYPE_BINARY; 2668aa26c57SJohannes Berg 267c30a3c95SJohannes Berg if (pt->validation_type == NLA_VALIDATE_RANGE || 268c30a3c95SJohannes Berg pt->validation_type == NLA_VALIDATE_RANGE_WARN_TOO_LONG) { 2698aa26c57SJohannes Berg struct netlink_range_validation range; 2708aa26c57SJohannes Berg 2718aa26c57SJohannes Berg nla_get_range_unsigned(pt, &range); 2728aa26c57SJohannes Berg 2738aa26c57SJohannes Berg if (range.min && 2748aa26c57SJohannes Berg nla_put_u32(skb, NL_POLICY_TYPE_ATTR_MIN_LENGTH, 2758aa26c57SJohannes Berg range.min)) 276d07dcf9aSJohannes Berg goto nla_put_failure; 2778aa26c57SJohannes Berg 2788aa26c57SJohannes Berg if (range.max < U16_MAX && 2798aa26c57SJohannes Berg nla_put_u32(skb, NL_POLICY_TYPE_ATTR_MAX_LENGTH, 2808aa26c57SJohannes Berg range.max)) 281d07dcf9aSJohannes Berg goto nla_put_failure; 2828aa26c57SJohannes Berg } else if (pt->len && 2838aa26c57SJohannes Berg nla_put_u32(skb, NL_POLICY_TYPE_ATTR_MAX_LENGTH, 2848aa26c57SJohannes Berg pt->len)) { 2858aa26c57SJohannes Berg goto nla_put_failure; 2868aa26c57SJohannes Berg } 287d07dcf9aSJohannes Berg break; 288d07dcf9aSJohannes Berg case NLA_FLAG: 289d07dcf9aSJohannes Berg type = NL_ATTR_TYPE_FLAG; 290d07dcf9aSJohannes Berg break; 291d07dcf9aSJohannes Berg } 292d07dcf9aSJohannes Berg 293d07dcf9aSJohannes Berg if (nla_put_u32(skb, NL_POLICY_TYPE_ATTR_TYPE, type)) 294d07dcf9aSJohannes Berg goto nla_put_failure; 295d07dcf9aSJohannes Berg 296d07dcf9aSJohannes Berg /* finish and move state to next attribute */ 297d07dcf9aSJohannes Berg nla_nest_end(skb, attr); 298d07dcf9aSJohannes Berg nla_nest_end(skb, policy); 299d07dcf9aSJohannes Berg 300d07dcf9aSJohannes Berg next: 301d07dcf9aSJohannes Berg state->attr_idx += 1; 302d07dcf9aSJohannes Berg if (state->attr_idx > state->policies[state->policy_idx].maxtype) { 303d07dcf9aSJohannes Berg state->attr_idx = 0; 304d07dcf9aSJohannes Berg state->policy_idx++; 305d07dcf9aSJohannes Berg } 306d07dcf9aSJohannes Berg 307d07dcf9aSJohannes Berg if (again) { 308d07dcf9aSJohannes Berg if (netlink_policy_dump_finished(state)) 309d07dcf9aSJohannes Berg return -ENODATA; 310d07dcf9aSJohannes Berg goto send_attribute; 311d07dcf9aSJohannes Berg } 312d07dcf9aSJohannes Berg 313d07dcf9aSJohannes Berg return 0; 314d07dcf9aSJohannes Berg 315d07dcf9aSJohannes Berg nla_put_failure: 316d07dcf9aSJohannes Berg nla_nest_cancel(skb, policy); 317d07dcf9aSJohannes Berg return -ENOBUFS; 318d07dcf9aSJohannes Berg } 319