18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * NETLINK Policy advertisement to userspace 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Authors: Johannes Berg <johannes@sipsolutions.net> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright 2019 Intel Corporation 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/errno.h> 128c2ecf20Sopenharmony_ci#include <linux/types.h> 138c2ecf20Sopenharmony_ci#include <net/netlink.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#define INITIAL_POLICIES_ALLOC 10 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_cistruct netlink_policy_dump_state { 188c2ecf20Sopenharmony_ci unsigned int policy_idx; 198c2ecf20Sopenharmony_ci unsigned int attr_idx; 208c2ecf20Sopenharmony_ci unsigned int n_alloc; 218c2ecf20Sopenharmony_ci struct { 228c2ecf20Sopenharmony_ci const struct nla_policy *policy; 238c2ecf20Sopenharmony_ci unsigned int maxtype; 248c2ecf20Sopenharmony_ci } policies[]; 258c2ecf20Sopenharmony_ci}; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic int add_policy(struct netlink_policy_dump_state **statep, 288c2ecf20Sopenharmony_ci const struct nla_policy *policy, 298c2ecf20Sopenharmony_ci unsigned int maxtype) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci struct netlink_policy_dump_state *state = *statep; 328c2ecf20Sopenharmony_ci unsigned int n_alloc, i; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci if (!policy || !maxtype) 358c2ecf20Sopenharmony_ci return 0; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci for (i = 0; i < state->n_alloc; i++) { 388c2ecf20Sopenharmony_ci if (state->policies[i].policy == policy && 398c2ecf20Sopenharmony_ci state->policies[i].maxtype == maxtype) 408c2ecf20Sopenharmony_ci return 0; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci if (!state->policies[i].policy) { 438c2ecf20Sopenharmony_ci state->policies[i].policy = policy; 448c2ecf20Sopenharmony_ci state->policies[i].maxtype = maxtype; 458c2ecf20Sopenharmony_ci return 0; 468c2ecf20Sopenharmony_ci } 478c2ecf20Sopenharmony_ci } 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci n_alloc = state->n_alloc + INITIAL_POLICIES_ALLOC; 508c2ecf20Sopenharmony_ci state = krealloc(state, struct_size(state, policies, n_alloc), 518c2ecf20Sopenharmony_ci GFP_KERNEL); 528c2ecf20Sopenharmony_ci if (!state) 538c2ecf20Sopenharmony_ci return -ENOMEM; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci memset(&state->policies[state->n_alloc], 0, 568c2ecf20Sopenharmony_ci flex_array_size(state, policies, n_alloc - state->n_alloc)); 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci state->policies[state->n_alloc].policy = policy; 598c2ecf20Sopenharmony_ci state->policies[state->n_alloc].maxtype = maxtype; 608c2ecf20Sopenharmony_ci state->n_alloc = n_alloc; 618c2ecf20Sopenharmony_ci *statep = state; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci return 0; 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci/** 678c2ecf20Sopenharmony_ci * netlink_policy_dump_get_policy_idx - retrieve policy index 688c2ecf20Sopenharmony_ci * @state: the policy dump state 698c2ecf20Sopenharmony_ci * @policy: the policy to find 708c2ecf20Sopenharmony_ci * @maxtype: the policy's maxattr 718c2ecf20Sopenharmony_ci * 728c2ecf20Sopenharmony_ci * Returns: the index of the given policy in the dump state 738c2ecf20Sopenharmony_ci * 748c2ecf20Sopenharmony_ci * Call this to find a policy index when you've added multiple and e.g. 758c2ecf20Sopenharmony_ci * need to tell userspace which command has which policy (by index). 768c2ecf20Sopenharmony_ci * 778c2ecf20Sopenharmony_ci * Note: this will WARN and return 0 if the policy isn't found, which 788c2ecf20Sopenharmony_ci * means it wasn't added in the first place, which would be an 798c2ecf20Sopenharmony_ci * internal consistency bug. 808c2ecf20Sopenharmony_ci */ 818c2ecf20Sopenharmony_ciint netlink_policy_dump_get_policy_idx(struct netlink_policy_dump_state *state, 828c2ecf20Sopenharmony_ci const struct nla_policy *policy, 838c2ecf20Sopenharmony_ci unsigned int maxtype) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci unsigned int i; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci if (WARN_ON(!policy || !maxtype)) 888c2ecf20Sopenharmony_ci return 0; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci for (i = 0; i < state->n_alloc; i++) { 918c2ecf20Sopenharmony_ci if (state->policies[i].policy == policy && 928c2ecf20Sopenharmony_ci state->policies[i].maxtype == maxtype) 938c2ecf20Sopenharmony_ci return i; 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci WARN_ON(1); 978c2ecf20Sopenharmony_ci return 0; 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic struct netlink_policy_dump_state *alloc_state(void) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci struct netlink_policy_dump_state *state; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci state = kzalloc(struct_size(state, policies, INITIAL_POLICIES_ALLOC), 1058c2ecf20Sopenharmony_ci GFP_KERNEL); 1068c2ecf20Sopenharmony_ci if (!state) 1078c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 1088c2ecf20Sopenharmony_ci state->n_alloc = INITIAL_POLICIES_ALLOC; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci return state; 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci/** 1148c2ecf20Sopenharmony_ci * netlink_policy_dump_add_policy - add a policy to the dump 1158c2ecf20Sopenharmony_ci * @pstate: state to add to, may be reallocated, must be %NULL the first time 1168c2ecf20Sopenharmony_ci * @policy: the new policy to add to the dump 1178c2ecf20Sopenharmony_ci * @maxtype: the new policy's max attr type 1188c2ecf20Sopenharmony_ci * 1198c2ecf20Sopenharmony_ci * Returns: 0 on success, a negative error code otherwise. 1208c2ecf20Sopenharmony_ci * 1218c2ecf20Sopenharmony_ci * Call this to allocate a policy dump state, and to add policies to it. This 1228c2ecf20Sopenharmony_ci * should be called from the dump start() callback. 1238c2ecf20Sopenharmony_ci * 1248c2ecf20Sopenharmony_ci * Note: on failures, any previously allocated state is freed. 1258c2ecf20Sopenharmony_ci */ 1268c2ecf20Sopenharmony_ciint netlink_policy_dump_add_policy(struct netlink_policy_dump_state **pstate, 1278c2ecf20Sopenharmony_ci const struct nla_policy *policy, 1288c2ecf20Sopenharmony_ci unsigned int maxtype) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci struct netlink_policy_dump_state *state = *pstate; 1318c2ecf20Sopenharmony_ci unsigned int policy_idx; 1328c2ecf20Sopenharmony_ci int err; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci if (!state) { 1358c2ecf20Sopenharmony_ci state = alloc_state(); 1368c2ecf20Sopenharmony_ci if (IS_ERR(state)) 1378c2ecf20Sopenharmony_ci return PTR_ERR(state); 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci /* 1418c2ecf20Sopenharmony_ci * walk the policies and nested ones first, and build 1428c2ecf20Sopenharmony_ci * a linear list of them. 1438c2ecf20Sopenharmony_ci */ 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci err = add_policy(&state, policy, maxtype); 1468c2ecf20Sopenharmony_ci if (err) 1478c2ecf20Sopenharmony_ci goto err_try_undo; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci for (policy_idx = 0; 1508c2ecf20Sopenharmony_ci policy_idx < state->n_alloc && state->policies[policy_idx].policy; 1518c2ecf20Sopenharmony_ci policy_idx++) { 1528c2ecf20Sopenharmony_ci const struct nla_policy *policy; 1538c2ecf20Sopenharmony_ci unsigned int type; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci policy = state->policies[policy_idx].policy; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci for (type = 0; 1588c2ecf20Sopenharmony_ci type <= state->policies[policy_idx].maxtype; 1598c2ecf20Sopenharmony_ci type++) { 1608c2ecf20Sopenharmony_ci switch (policy[type].type) { 1618c2ecf20Sopenharmony_ci case NLA_NESTED: 1628c2ecf20Sopenharmony_ci case NLA_NESTED_ARRAY: 1638c2ecf20Sopenharmony_ci err = add_policy(&state, 1648c2ecf20Sopenharmony_ci policy[type].nested_policy, 1658c2ecf20Sopenharmony_ci policy[type].len); 1668c2ecf20Sopenharmony_ci if (err) 1678c2ecf20Sopenharmony_ci goto err_try_undo; 1688c2ecf20Sopenharmony_ci break; 1698c2ecf20Sopenharmony_ci default: 1708c2ecf20Sopenharmony_ci break; 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci *pstate = state; 1768c2ecf20Sopenharmony_ci return 0; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cierr_try_undo: 1798c2ecf20Sopenharmony_ci /* Try to preserve reasonable unwind semantics - if we're starting from 1808c2ecf20Sopenharmony_ci * scratch clean up fully, otherwise record what we got and caller will. 1818c2ecf20Sopenharmony_ci */ 1828c2ecf20Sopenharmony_ci if (!*pstate) 1838c2ecf20Sopenharmony_ci netlink_policy_dump_free(state); 1848c2ecf20Sopenharmony_ci else 1858c2ecf20Sopenharmony_ci *pstate = state; 1868c2ecf20Sopenharmony_ci return err; 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic bool 1908c2ecf20Sopenharmony_cinetlink_policy_dump_finished(struct netlink_policy_dump_state *state) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci return state->policy_idx >= state->n_alloc || 1938c2ecf20Sopenharmony_ci !state->policies[state->policy_idx].policy; 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci/** 1978c2ecf20Sopenharmony_ci * netlink_policy_dump_loop - dumping loop indicator 1988c2ecf20Sopenharmony_ci * @state: the policy dump state 1998c2ecf20Sopenharmony_ci * 2008c2ecf20Sopenharmony_ci * Returns: %true if the dump continues, %false otherwise 2018c2ecf20Sopenharmony_ci * 2028c2ecf20Sopenharmony_ci * Note: this frees the dump state when finishing 2038c2ecf20Sopenharmony_ci */ 2048c2ecf20Sopenharmony_cibool netlink_policy_dump_loop(struct netlink_policy_dump_state *state) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci return !netlink_policy_dump_finished(state); 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ciint netlink_policy_dump_attr_size_estimate(const struct nla_policy *pt) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci /* nested + type */ 2128c2ecf20Sopenharmony_ci int common = 2 * nla_attr_size(sizeof(u32)); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci switch (pt->type) { 2158c2ecf20Sopenharmony_ci case NLA_UNSPEC: 2168c2ecf20Sopenharmony_ci case NLA_REJECT: 2178c2ecf20Sopenharmony_ci /* these actually don't need any space */ 2188c2ecf20Sopenharmony_ci return 0; 2198c2ecf20Sopenharmony_ci case NLA_NESTED: 2208c2ecf20Sopenharmony_ci case NLA_NESTED_ARRAY: 2218c2ecf20Sopenharmony_ci /* common, policy idx, policy maxattr */ 2228c2ecf20Sopenharmony_ci return common + 2 * nla_attr_size(sizeof(u32)); 2238c2ecf20Sopenharmony_ci case NLA_U8: 2248c2ecf20Sopenharmony_ci case NLA_U16: 2258c2ecf20Sopenharmony_ci case NLA_U32: 2268c2ecf20Sopenharmony_ci case NLA_U64: 2278c2ecf20Sopenharmony_ci case NLA_MSECS: 2288c2ecf20Sopenharmony_ci case NLA_S8: 2298c2ecf20Sopenharmony_ci case NLA_S16: 2308c2ecf20Sopenharmony_ci case NLA_S32: 2318c2ecf20Sopenharmony_ci case NLA_S64: 2328c2ecf20Sopenharmony_ci /* maximum is common, u64 min/max with padding */ 2338c2ecf20Sopenharmony_ci return common + 2348c2ecf20Sopenharmony_ci 2 * (nla_attr_size(0) + nla_attr_size(sizeof(u64))); 2358c2ecf20Sopenharmony_ci case NLA_BITFIELD32: 2368c2ecf20Sopenharmony_ci return common + nla_attr_size(sizeof(u32)); 2378c2ecf20Sopenharmony_ci case NLA_STRING: 2388c2ecf20Sopenharmony_ci case NLA_NUL_STRING: 2398c2ecf20Sopenharmony_ci case NLA_BINARY: 2408c2ecf20Sopenharmony_ci /* maximum is common, u32 min-length/max-length */ 2418c2ecf20Sopenharmony_ci return common + 2 * nla_attr_size(sizeof(u32)); 2428c2ecf20Sopenharmony_ci case NLA_FLAG: 2438c2ecf20Sopenharmony_ci return common; 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci /* this should then cause a warning later */ 2478c2ecf20Sopenharmony_ci return 0; 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cistatic int 2518c2ecf20Sopenharmony_ci__netlink_policy_dump_write_attr(struct netlink_policy_dump_state *state, 2528c2ecf20Sopenharmony_ci struct sk_buff *skb, 2538c2ecf20Sopenharmony_ci const struct nla_policy *pt, 2548c2ecf20Sopenharmony_ci int nestattr) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci int estimate = netlink_policy_dump_attr_size_estimate(pt); 2578c2ecf20Sopenharmony_ci enum netlink_attribute_type type; 2588c2ecf20Sopenharmony_ci struct nlattr *attr; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci attr = nla_nest_start(skb, nestattr); 2618c2ecf20Sopenharmony_ci if (!attr) 2628c2ecf20Sopenharmony_ci return -ENOBUFS; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci switch (pt->type) { 2658c2ecf20Sopenharmony_ci default: 2668c2ecf20Sopenharmony_ci case NLA_UNSPEC: 2678c2ecf20Sopenharmony_ci case NLA_REJECT: 2688c2ecf20Sopenharmony_ci /* skip - use NLA_MIN_LEN to advertise such */ 2698c2ecf20Sopenharmony_ci nla_nest_cancel(skb, attr); 2708c2ecf20Sopenharmony_ci return -ENODATA; 2718c2ecf20Sopenharmony_ci case NLA_NESTED: 2728c2ecf20Sopenharmony_ci type = NL_ATTR_TYPE_NESTED; 2738c2ecf20Sopenharmony_ci fallthrough; 2748c2ecf20Sopenharmony_ci case NLA_NESTED_ARRAY: 2758c2ecf20Sopenharmony_ci if (pt->type == NLA_NESTED_ARRAY) 2768c2ecf20Sopenharmony_ci type = NL_ATTR_TYPE_NESTED_ARRAY; 2778c2ecf20Sopenharmony_ci if (state && pt->nested_policy && pt->len && 2788c2ecf20Sopenharmony_ci (nla_put_u32(skb, NL_POLICY_TYPE_ATTR_POLICY_IDX, 2798c2ecf20Sopenharmony_ci netlink_policy_dump_get_policy_idx(state, 2808c2ecf20Sopenharmony_ci pt->nested_policy, 2818c2ecf20Sopenharmony_ci pt->len)) || 2828c2ecf20Sopenharmony_ci nla_put_u32(skb, NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE, 2838c2ecf20Sopenharmony_ci pt->len))) 2848c2ecf20Sopenharmony_ci goto nla_put_failure; 2858c2ecf20Sopenharmony_ci break; 2868c2ecf20Sopenharmony_ci case NLA_U8: 2878c2ecf20Sopenharmony_ci case NLA_U16: 2888c2ecf20Sopenharmony_ci case NLA_U32: 2898c2ecf20Sopenharmony_ci case NLA_U64: 2908c2ecf20Sopenharmony_ci case NLA_MSECS: { 2918c2ecf20Sopenharmony_ci struct netlink_range_validation range; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci if (pt->type == NLA_U8) 2948c2ecf20Sopenharmony_ci type = NL_ATTR_TYPE_U8; 2958c2ecf20Sopenharmony_ci else if (pt->type == NLA_U16) 2968c2ecf20Sopenharmony_ci type = NL_ATTR_TYPE_U16; 2978c2ecf20Sopenharmony_ci else if (pt->type == NLA_U32) 2988c2ecf20Sopenharmony_ci type = NL_ATTR_TYPE_U32; 2998c2ecf20Sopenharmony_ci else 3008c2ecf20Sopenharmony_ci type = NL_ATTR_TYPE_U64; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci if (pt->validation_type == NLA_VALIDATE_MASK) { 3038c2ecf20Sopenharmony_ci if (nla_put_u64_64bit(skb, NL_POLICY_TYPE_ATTR_MASK, 3048c2ecf20Sopenharmony_ci pt->mask, 3058c2ecf20Sopenharmony_ci NL_POLICY_TYPE_ATTR_PAD)) 3068c2ecf20Sopenharmony_ci goto nla_put_failure; 3078c2ecf20Sopenharmony_ci break; 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci nla_get_range_unsigned(pt, &range); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci if (nla_put_u64_64bit(skb, NL_POLICY_TYPE_ATTR_MIN_VALUE_U, 3138c2ecf20Sopenharmony_ci range.min, NL_POLICY_TYPE_ATTR_PAD) || 3148c2ecf20Sopenharmony_ci nla_put_u64_64bit(skb, NL_POLICY_TYPE_ATTR_MAX_VALUE_U, 3158c2ecf20Sopenharmony_ci range.max, NL_POLICY_TYPE_ATTR_PAD)) 3168c2ecf20Sopenharmony_ci goto nla_put_failure; 3178c2ecf20Sopenharmony_ci break; 3188c2ecf20Sopenharmony_ci } 3198c2ecf20Sopenharmony_ci case NLA_S8: 3208c2ecf20Sopenharmony_ci case NLA_S16: 3218c2ecf20Sopenharmony_ci case NLA_S32: 3228c2ecf20Sopenharmony_ci case NLA_S64: { 3238c2ecf20Sopenharmony_ci struct netlink_range_validation_signed range; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci if (pt->type == NLA_S8) 3268c2ecf20Sopenharmony_ci type = NL_ATTR_TYPE_S8; 3278c2ecf20Sopenharmony_ci else if (pt->type == NLA_S16) 3288c2ecf20Sopenharmony_ci type = NL_ATTR_TYPE_S16; 3298c2ecf20Sopenharmony_ci else if (pt->type == NLA_S32) 3308c2ecf20Sopenharmony_ci type = NL_ATTR_TYPE_S32; 3318c2ecf20Sopenharmony_ci else 3328c2ecf20Sopenharmony_ci type = NL_ATTR_TYPE_S64; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci nla_get_range_signed(pt, &range); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci if (nla_put_s64(skb, NL_POLICY_TYPE_ATTR_MIN_VALUE_S, 3378c2ecf20Sopenharmony_ci range.min, NL_POLICY_TYPE_ATTR_PAD) || 3388c2ecf20Sopenharmony_ci nla_put_s64(skb, NL_POLICY_TYPE_ATTR_MAX_VALUE_S, 3398c2ecf20Sopenharmony_ci range.max, NL_POLICY_TYPE_ATTR_PAD)) 3408c2ecf20Sopenharmony_ci goto nla_put_failure; 3418c2ecf20Sopenharmony_ci break; 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci case NLA_BITFIELD32: 3448c2ecf20Sopenharmony_ci type = NL_ATTR_TYPE_BITFIELD32; 3458c2ecf20Sopenharmony_ci if (nla_put_u32(skb, NL_POLICY_TYPE_ATTR_BITFIELD32_MASK, 3468c2ecf20Sopenharmony_ci pt->bitfield32_valid)) 3478c2ecf20Sopenharmony_ci goto nla_put_failure; 3488c2ecf20Sopenharmony_ci break; 3498c2ecf20Sopenharmony_ci case NLA_STRING: 3508c2ecf20Sopenharmony_ci case NLA_NUL_STRING: 3518c2ecf20Sopenharmony_ci case NLA_BINARY: 3528c2ecf20Sopenharmony_ci if (pt->type == NLA_STRING) 3538c2ecf20Sopenharmony_ci type = NL_ATTR_TYPE_STRING; 3548c2ecf20Sopenharmony_ci else if (pt->type == NLA_NUL_STRING) 3558c2ecf20Sopenharmony_ci type = NL_ATTR_TYPE_NUL_STRING; 3568c2ecf20Sopenharmony_ci else 3578c2ecf20Sopenharmony_ci type = NL_ATTR_TYPE_BINARY; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci if (pt->validation_type == NLA_VALIDATE_RANGE || 3608c2ecf20Sopenharmony_ci pt->validation_type == NLA_VALIDATE_RANGE_WARN_TOO_LONG) { 3618c2ecf20Sopenharmony_ci struct netlink_range_validation range; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci nla_get_range_unsigned(pt, &range); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci if (range.min && 3668c2ecf20Sopenharmony_ci nla_put_u32(skb, NL_POLICY_TYPE_ATTR_MIN_LENGTH, 3678c2ecf20Sopenharmony_ci range.min)) 3688c2ecf20Sopenharmony_ci goto nla_put_failure; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci if (range.max < U16_MAX && 3718c2ecf20Sopenharmony_ci nla_put_u32(skb, NL_POLICY_TYPE_ATTR_MAX_LENGTH, 3728c2ecf20Sopenharmony_ci range.max)) 3738c2ecf20Sopenharmony_ci goto nla_put_failure; 3748c2ecf20Sopenharmony_ci } else if (pt->len && 3758c2ecf20Sopenharmony_ci nla_put_u32(skb, NL_POLICY_TYPE_ATTR_MAX_LENGTH, 3768c2ecf20Sopenharmony_ci pt->len)) { 3778c2ecf20Sopenharmony_ci goto nla_put_failure; 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci break; 3808c2ecf20Sopenharmony_ci case NLA_FLAG: 3818c2ecf20Sopenharmony_ci type = NL_ATTR_TYPE_FLAG; 3828c2ecf20Sopenharmony_ci break; 3838c2ecf20Sopenharmony_ci } 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci if (nla_put_u32(skb, NL_POLICY_TYPE_ATTR_TYPE, type)) 3868c2ecf20Sopenharmony_ci goto nla_put_failure; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci nla_nest_end(skb, attr); 3898c2ecf20Sopenharmony_ci WARN_ON(attr->nla_len > estimate); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci return 0; 3928c2ecf20Sopenharmony_cinla_put_failure: 3938c2ecf20Sopenharmony_ci nla_nest_cancel(skb, attr); 3948c2ecf20Sopenharmony_ci return -ENOBUFS; 3958c2ecf20Sopenharmony_ci} 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci/** 3988c2ecf20Sopenharmony_ci * netlink_policy_dump_write_attr - write a given attribute policy 3998c2ecf20Sopenharmony_ci * @skb: the message skb to write to 4008c2ecf20Sopenharmony_ci * @pt: the attribute's policy 4018c2ecf20Sopenharmony_ci * @nestattr: the nested attribute ID to use 4028c2ecf20Sopenharmony_ci * 4038c2ecf20Sopenharmony_ci * Returns: 0 on success, an error code otherwise; -%ENODATA is 4048c2ecf20Sopenharmony_ci * special, indicating that there's no policy data and 4058c2ecf20Sopenharmony_ci * the attribute is generally rejected. 4068c2ecf20Sopenharmony_ci */ 4078c2ecf20Sopenharmony_ciint netlink_policy_dump_write_attr(struct sk_buff *skb, 4088c2ecf20Sopenharmony_ci const struct nla_policy *pt, 4098c2ecf20Sopenharmony_ci int nestattr) 4108c2ecf20Sopenharmony_ci{ 4118c2ecf20Sopenharmony_ci return __netlink_policy_dump_write_attr(NULL, skb, pt, nestattr); 4128c2ecf20Sopenharmony_ci} 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci/** 4158c2ecf20Sopenharmony_ci * netlink_policy_dump_write - write current policy dump attributes 4168c2ecf20Sopenharmony_ci * @skb: the message skb to write to 4178c2ecf20Sopenharmony_ci * @state: the policy dump state 4188c2ecf20Sopenharmony_ci * 4198c2ecf20Sopenharmony_ci * Returns: 0 on success, an error code otherwise 4208c2ecf20Sopenharmony_ci */ 4218c2ecf20Sopenharmony_ciint netlink_policy_dump_write(struct sk_buff *skb, 4228c2ecf20Sopenharmony_ci struct netlink_policy_dump_state *state) 4238c2ecf20Sopenharmony_ci{ 4248c2ecf20Sopenharmony_ci const struct nla_policy *pt; 4258c2ecf20Sopenharmony_ci struct nlattr *policy; 4268c2ecf20Sopenharmony_ci bool again; 4278c2ecf20Sopenharmony_ci int err; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_cisend_attribute: 4308c2ecf20Sopenharmony_ci again = false; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci pt = &state->policies[state->policy_idx].policy[state->attr_idx]; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci policy = nla_nest_start(skb, state->policy_idx); 4358c2ecf20Sopenharmony_ci if (!policy) 4368c2ecf20Sopenharmony_ci return -ENOBUFS; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci err = __netlink_policy_dump_write_attr(state, skb, pt, state->attr_idx); 4398c2ecf20Sopenharmony_ci if (err == -ENODATA) { 4408c2ecf20Sopenharmony_ci nla_nest_cancel(skb, policy); 4418c2ecf20Sopenharmony_ci again = true; 4428c2ecf20Sopenharmony_ci goto next; 4438c2ecf20Sopenharmony_ci } else if (err) { 4448c2ecf20Sopenharmony_ci goto nla_put_failure; 4458c2ecf20Sopenharmony_ci } 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci /* finish and move state to next attribute */ 4488c2ecf20Sopenharmony_ci nla_nest_end(skb, policy); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_cinext: 4518c2ecf20Sopenharmony_ci state->attr_idx += 1; 4528c2ecf20Sopenharmony_ci if (state->attr_idx > state->policies[state->policy_idx].maxtype) { 4538c2ecf20Sopenharmony_ci state->attr_idx = 0; 4548c2ecf20Sopenharmony_ci state->policy_idx++; 4558c2ecf20Sopenharmony_ci } 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci if (again) { 4588c2ecf20Sopenharmony_ci if (netlink_policy_dump_finished(state)) 4598c2ecf20Sopenharmony_ci return -ENODATA; 4608c2ecf20Sopenharmony_ci goto send_attribute; 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci return 0; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_cinla_put_failure: 4668c2ecf20Sopenharmony_ci nla_nest_cancel(skb, policy); 4678c2ecf20Sopenharmony_ci return -ENOBUFS; 4688c2ecf20Sopenharmony_ci} 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci/** 4718c2ecf20Sopenharmony_ci * netlink_policy_dump_free - free policy dump state 4728c2ecf20Sopenharmony_ci * @state: the policy dump state to free 4738c2ecf20Sopenharmony_ci * 4748c2ecf20Sopenharmony_ci * Call this from the done() method to ensure dump state is freed. 4758c2ecf20Sopenharmony_ci */ 4768c2ecf20Sopenharmony_civoid netlink_policy_dump_free(struct netlink_policy_dump_state *state) 4778c2ecf20Sopenharmony_ci{ 4788c2ecf20Sopenharmony_ci kfree(state); 4798c2ecf20Sopenharmony_ci} 480