162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * NETLINK Policy advertisement to userspace 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Authors: Johannes Berg <johannes@sipsolutions.net> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright 2019 Intel Corporation 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/errno.h> 1262306a36Sopenharmony_ci#include <linux/types.h> 1362306a36Sopenharmony_ci#include <net/netlink.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#define INITIAL_POLICIES_ALLOC 10 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cistruct netlink_policy_dump_state { 1862306a36Sopenharmony_ci unsigned int policy_idx; 1962306a36Sopenharmony_ci unsigned int attr_idx; 2062306a36Sopenharmony_ci unsigned int n_alloc; 2162306a36Sopenharmony_ci struct { 2262306a36Sopenharmony_ci const struct nla_policy *policy; 2362306a36Sopenharmony_ci unsigned int maxtype; 2462306a36Sopenharmony_ci } policies[]; 2562306a36Sopenharmony_ci}; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic int add_policy(struct netlink_policy_dump_state **statep, 2862306a36Sopenharmony_ci const struct nla_policy *policy, 2962306a36Sopenharmony_ci unsigned int maxtype) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci struct netlink_policy_dump_state *state = *statep; 3262306a36Sopenharmony_ci unsigned int n_alloc, i; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci if (!policy || !maxtype) 3562306a36Sopenharmony_ci return 0; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci for (i = 0; i < state->n_alloc; i++) { 3862306a36Sopenharmony_ci if (state->policies[i].policy == policy && 3962306a36Sopenharmony_ci state->policies[i].maxtype == maxtype) 4062306a36Sopenharmony_ci return 0; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci if (!state->policies[i].policy) { 4362306a36Sopenharmony_ci state->policies[i].policy = policy; 4462306a36Sopenharmony_ci state->policies[i].maxtype = maxtype; 4562306a36Sopenharmony_ci return 0; 4662306a36Sopenharmony_ci } 4762306a36Sopenharmony_ci } 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci n_alloc = state->n_alloc + INITIAL_POLICIES_ALLOC; 5062306a36Sopenharmony_ci state = krealloc(state, struct_size(state, policies, n_alloc), 5162306a36Sopenharmony_ci GFP_KERNEL); 5262306a36Sopenharmony_ci if (!state) 5362306a36Sopenharmony_ci return -ENOMEM; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci memset(&state->policies[state->n_alloc], 0, 5662306a36Sopenharmony_ci flex_array_size(state, policies, n_alloc - state->n_alloc)); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci state->policies[state->n_alloc].policy = policy; 5962306a36Sopenharmony_ci state->policies[state->n_alloc].maxtype = maxtype; 6062306a36Sopenharmony_ci state->n_alloc = n_alloc; 6162306a36Sopenharmony_ci *statep = state; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci return 0; 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci/** 6762306a36Sopenharmony_ci * netlink_policy_dump_get_policy_idx - retrieve policy index 6862306a36Sopenharmony_ci * @state: the policy dump state 6962306a36Sopenharmony_ci * @policy: the policy to find 7062306a36Sopenharmony_ci * @maxtype: the policy's maxattr 7162306a36Sopenharmony_ci * 7262306a36Sopenharmony_ci * Returns: the index of the given policy in the dump state 7362306a36Sopenharmony_ci * 7462306a36Sopenharmony_ci * Call this to find a policy index when you've added multiple and e.g. 7562306a36Sopenharmony_ci * need to tell userspace which command has which policy (by index). 7662306a36Sopenharmony_ci * 7762306a36Sopenharmony_ci * Note: this will WARN and return 0 if the policy isn't found, which 7862306a36Sopenharmony_ci * means it wasn't added in the first place, which would be an 7962306a36Sopenharmony_ci * internal consistency bug. 8062306a36Sopenharmony_ci */ 8162306a36Sopenharmony_ciint netlink_policy_dump_get_policy_idx(struct netlink_policy_dump_state *state, 8262306a36Sopenharmony_ci const struct nla_policy *policy, 8362306a36Sopenharmony_ci unsigned int maxtype) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci unsigned int i; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci if (WARN_ON(!policy || !maxtype)) 8862306a36Sopenharmony_ci return 0; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci for (i = 0; i < state->n_alloc; i++) { 9162306a36Sopenharmony_ci if (state->policies[i].policy == policy && 9262306a36Sopenharmony_ci state->policies[i].maxtype == maxtype) 9362306a36Sopenharmony_ci return i; 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci WARN_ON(1); 9762306a36Sopenharmony_ci return 0; 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic struct netlink_policy_dump_state *alloc_state(void) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci struct netlink_policy_dump_state *state; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci state = kzalloc(struct_size(state, policies, INITIAL_POLICIES_ALLOC), 10562306a36Sopenharmony_ci GFP_KERNEL); 10662306a36Sopenharmony_ci if (!state) 10762306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 10862306a36Sopenharmony_ci state->n_alloc = INITIAL_POLICIES_ALLOC; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci return state; 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci/** 11462306a36Sopenharmony_ci * netlink_policy_dump_add_policy - add a policy to the dump 11562306a36Sopenharmony_ci * @pstate: state to add to, may be reallocated, must be %NULL the first time 11662306a36Sopenharmony_ci * @policy: the new policy to add to the dump 11762306a36Sopenharmony_ci * @maxtype: the new policy's max attr type 11862306a36Sopenharmony_ci * 11962306a36Sopenharmony_ci * Returns: 0 on success, a negative error code otherwise. 12062306a36Sopenharmony_ci * 12162306a36Sopenharmony_ci * Call this to allocate a policy dump state, and to add policies to it. This 12262306a36Sopenharmony_ci * should be called from the dump start() callback. 12362306a36Sopenharmony_ci * 12462306a36Sopenharmony_ci * Note: on failures, any previously allocated state is freed. 12562306a36Sopenharmony_ci */ 12662306a36Sopenharmony_ciint netlink_policy_dump_add_policy(struct netlink_policy_dump_state **pstate, 12762306a36Sopenharmony_ci const struct nla_policy *policy, 12862306a36Sopenharmony_ci unsigned int maxtype) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci struct netlink_policy_dump_state *state = *pstate; 13162306a36Sopenharmony_ci unsigned int policy_idx; 13262306a36Sopenharmony_ci int err; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci if (!state) { 13562306a36Sopenharmony_ci state = alloc_state(); 13662306a36Sopenharmony_ci if (IS_ERR(state)) 13762306a36Sopenharmony_ci return PTR_ERR(state); 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci /* 14162306a36Sopenharmony_ci * walk the policies and nested ones first, and build 14262306a36Sopenharmony_ci * a linear list of them. 14362306a36Sopenharmony_ci */ 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci err = add_policy(&state, policy, maxtype); 14662306a36Sopenharmony_ci if (err) 14762306a36Sopenharmony_ci goto err_try_undo; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci for (policy_idx = 0; 15062306a36Sopenharmony_ci policy_idx < state->n_alloc && state->policies[policy_idx].policy; 15162306a36Sopenharmony_ci policy_idx++) { 15262306a36Sopenharmony_ci const struct nla_policy *policy; 15362306a36Sopenharmony_ci unsigned int type; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci policy = state->policies[policy_idx].policy; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci for (type = 0; 15862306a36Sopenharmony_ci type <= state->policies[policy_idx].maxtype; 15962306a36Sopenharmony_ci type++) { 16062306a36Sopenharmony_ci switch (policy[type].type) { 16162306a36Sopenharmony_ci case NLA_NESTED: 16262306a36Sopenharmony_ci case NLA_NESTED_ARRAY: 16362306a36Sopenharmony_ci err = add_policy(&state, 16462306a36Sopenharmony_ci policy[type].nested_policy, 16562306a36Sopenharmony_ci policy[type].len); 16662306a36Sopenharmony_ci if (err) 16762306a36Sopenharmony_ci goto err_try_undo; 16862306a36Sopenharmony_ci break; 16962306a36Sopenharmony_ci default: 17062306a36Sopenharmony_ci break; 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci *pstate = state; 17662306a36Sopenharmony_ci return 0; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cierr_try_undo: 17962306a36Sopenharmony_ci /* Try to preserve reasonable unwind semantics - if we're starting from 18062306a36Sopenharmony_ci * scratch clean up fully, otherwise record what we got and caller will. 18162306a36Sopenharmony_ci */ 18262306a36Sopenharmony_ci if (!*pstate) 18362306a36Sopenharmony_ci netlink_policy_dump_free(state); 18462306a36Sopenharmony_ci else 18562306a36Sopenharmony_ci *pstate = state; 18662306a36Sopenharmony_ci return err; 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic bool 19062306a36Sopenharmony_cinetlink_policy_dump_finished(struct netlink_policy_dump_state *state) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci return state->policy_idx >= state->n_alloc || 19362306a36Sopenharmony_ci !state->policies[state->policy_idx].policy; 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci/** 19762306a36Sopenharmony_ci * netlink_policy_dump_loop - dumping loop indicator 19862306a36Sopenharmony_ci * @state: the policy dump state 19962306a36Sopenharmony_ci * 20062306a36Sopenharmony_ci * Returns: %true if the dump continues, %false otherwise 20162306a36Sopenharmony_ci * 20262306a36Sopenharmony_ci * Note: this frees the dump state when finishing 20362306a36Sopenharmony_ci */ 20462306a36Sopenharmony_cibool netlink_policy_dump_loop(struct netlink_policy_dump_state *state) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci return !netlink_policy_dump_finished(state); 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ciint netlink_policy_dump_attr_size_estimate(const struct nla_policy *pt) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci /* nested + type */ 21262306a36Sopenharmony_ci int common = 2 * nla_attr_size(sizeof(u32)); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci switch (pt->type) { 21562306a36Sopenharmony_ci case NLA_UNSPEC: 21662306a36Sopenharmony_ci case NLA_REJECT: 21762306a36Sopenharmony_ci /* these actually don't need any space */ 21862306a36Sopenharmony_ci return 0; 21962306a36Sopenharmony_ci case NLA_NESTED: 22062306a36Sopenharmony_ci case NLA_NESTED_ARRAY: 22162306a36Sopenharmony_ci /* common, policy idx, policy maxattr */ 22262306a36Sopenharmony_ci return common + 2 * nla_attr_size(sizeof(u32)); 22362306a36Sopenharmony_ci case NLA_U8: 22462306a36Sopenharmony_ci case NLA_U16: 22562306a36Sopenharmony_ci case NLA_U32: 22662306a36Sopenharmony_ci case NLA_U64: 22762306a36Sopenharmony_ci case NLA_MSECS: 22862306a36Sopenharmony_ci case NLA_S8: 22962306a36Sopenharmony_ci case NLA_S16: 23062306a36Sopenharmony_ci case NLA_S32: 23162306a36Sopenharmony_ci case NLA_S64: 23262306a36Sopenharmony_ci /* maximum is common, u64 min/max with padding */ 23362306a36Sopenharmony_ci return common + 23462306a36Sopenharmony_ci 2 * (nla_attr_size(0) + nla_attr_size(sizeof(u64))); 23562306a36Sopenharmony_ci case NLA_BITFIELD32: 23662306a36Sopenharmony_ci return common + nla_attr_size(sizeof(u32)); 23762306a36Sopenharmony_ci case NLA_STRING: 23862306a36Sopenharmony_ci case NLA_NUL_STRING: 23962306a36Sopenharmony_ci case NLA_BINARY: 24062306a36Sopenharmony_ci /* maximum is common, u32 min-length/max-length */ 24162306a36Sopenharmony_ci return common + 2 * nla_attr_size(sizeof(u32)); 24262306a36Sopenharmony_ci case NLA_FLAG: 24362306a36Sopenharmony_ci return common; 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci /* this should then cause a warning later */ 24762306a36Sopenharmony_ci return 0; 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic int 25162306a36Sopenharmony_ci__netlink_policy_dump_write_attr(struct netlink_policy_dump_state *state, 25262306a36Sopenharmony_ci struct sk_buff *skb, 25362306a36Sopenharmony_ci const struct nla_policy *pt, 25462306a36Sopenharmony_ci int nestattr) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci int estimate = netlink_policy_dump_attr_size_estimate(pt); 25762306a36Sopenharmony_ci enum netlink_attribute_type type; 25862306a36Sopenharmony_ci struct nlattr *attr; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci attr = nla_nest_start(skb, nestattr); 26162306a36Sopenharmony_ci if (!attr) 26262306a36Sopenharmony_ci return -ENOBUFS; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci switch (pt->type) { 26562306a36Sopenharmony_ci default: 26662306a36Sopenharmony_ci case NLA_UNSPEC: 26762306a36Sopenharmony_ci case NLA_REJECT: 26862306a36Sopenharmony_ci /* skip - use NLA_MIN_LEN to advertise such */ 26962306a36Sopenharmony_ci nla_nest_cancel(skb, attr); 27062306a36Sopenharmony_ci return -ENODATA; 27162306a36Sopenharmony_ci case NLA_NESTED: 27262306a36Sopenharmony_ci type = NL_ATTR_TYPE_NESTED; 27362306a36Sopenharmony_ci fallthrough; 27462306a36Sopenharmony_ci case NLA_NESTED_ARRAY: 27562306a36Sopenharmony_ci if (pt->type == NLA_NESTED_ARRAY) 27662306a36Sopenharmony_ci type = NL_ATTR_TYPE_NESTED_ARRAY; 27762306a36Sopenharmony_ci if (state && pt->nested_policy && pt->len && 27862306a36Sopenharmony_ci (nla_put_u32(skb, NL_POLICY_TYPE_ATTR_POLICY_IDX, 27962306a36Sopenharmony_ci netlink_policy_dump_get_policy_idx(state, 28062306a36Sopenharmony_ci pt->nested_policy, 28162306a36Sopenharmony_ci pt->len)) || 28262306a36Sopenharmony_ci nla_put_u32(skb, NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE, 28362306a36Sopenharmony_ci pt->len))) 28462306a36Sopenharmony_ci goto nla_put_failure; 28562306a36Sopenharmony_ci break; 28662306a36Sopenharmony_ci case NLA_U8: 28762306a36Sopenharmony_ci case NLA_U16: 28862306a36Sopenharmony_ci case NLA_U32: 28962306a36Sopenharmony_ci case NLA_U64: 29062306a36Sopenharmony_ci case NLA_MSECS: { 29162306a36Sopenharmony_ci struct netlink_range_validation range; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci if (pt->type == NLA_U8) 29462306a36Sopenharmony_ci type = NL_ATTR_TYPE_U8; 29562306a36Sopenharmony_ci else if (pt->type == NLA_U16) 29662306a36Sopenharmony_ci type = NL_ATTR_TYPE_U16; 29762306a36Sopenharmony_ci else if (pt->type == NLA_U32) 29862306a36Sopenharmony_ci type = NL_ATTR_TYPE_U32; 29962306a36Sopenharmony_ci else 30062306a36Sopenharmony_ci type = NL_ATTR_TYPE_U64; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci if (pt->validation_type == NLA_VALIDATE_MASK) { 30362306a36Sopenharmony_ci if (nla_put_u64_64bit(skb, NL_POLICY_TYPE_ATTR_MASK, 30462306a36Sopenharmony_ci pt->mask, 30562306a36Sopenharmony_ci NL_POLICY_TYPE_ATTR_PAD)) 30662306a36Sopenharmony_ci goto nla_put_failure; 30762306a36Sopenharmony_ci break; 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci nla_get_range_unsigned(pt, &range); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci if (nla_put_u64_64bit(skb, NL_POLICY_TYPE_ATTR_MIN_VALUE_U, 31362306a36Sopenharmony_ci range.min, NL_POLICY_TYPE_ATTR_PAD) || 31462306a36Sopenharmony_ci nla_put_u64_64bit(skb, NL_POLICY_TYPE_ATTR_MAX_VALUE_U, 31562306a36Sopenharmony_ci range.max, NL_POLICY_TYPE_ATTR_PAD)) 31662306a36Sopenharmony_ci goto nla_put_failure; 31762306a36Sopenharmony_ci break; 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci case NLA_S8: 32062306a36Sopenharmony_ci case NLA_S16: 32162306a36Sopenharmony_ci case NLA_S32: 32262306a36Sopenharmony_ci case NLA_S64: { 32362306a36Sopenharmony_ci struct netlink_range_validation_signed range; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci if (pt->type == NLA_S8) 32662306a36Sopenharmony_ci type = NL_ATTR_TYPE_S8; 32762306a36Sopenharmony_ci else if (pt->type == NLA_S16) 32862306a36Sopenharmony_ci type = NL_ATTR_TYPE_S16; 32962306a36Sopenharmony_ci else if (pt->type == NLA_S32) 33062306a36Sopenharmony_ci type = NL_ATTR_TYPE_S32; 33162306a36Sopenharmony_ci else 33262306a36Sopenharmony_ci type = NL_ATTR_TYPE_S64; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci nla_get_range_signed(pt, &range); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci if (nla_put_s64(skb, NL_POLICY_TYPE_ATTR_MIN_VALUE_S, 33762306a36Sopenharmony_ci range.min, NL_POLICY_TYPE_ATTR_PAD) || 33862306a36Sopenharmony_ci nla_put_s64(skb, NL_POLICY_TYPE_ATTR_MAX_VALUE_S, 33962306a36Sopenharmony_ci range.max, NL_POLICY_TYPE_ATTR_PAD)) 34062306a36Sopenharmony_ci goto nla_put_failure; 34162306a36Sopenharmony_ci break; 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci case NLA_BITFIELD32: 34462306a36Sopenharmony_ci type = NL_ATTR_TYPE_BITFIELD32; 34562306a36Sopenharmony_ci if (nla_put_u32(skb, NL_POLICY_TYPE_ATTR_BITFIELD32_MASK, 34662306a36Sopenharmony_ci pt->bitfield32_valid)) 34762306a36Sopenharmony_ci goto nla_put_failure; 34862306a36Sopenharmony_ci break; 34962306a36Sopenharmony_ci case NLA_STRING: 35062306a36Sopenharmony_ci case NLA_NUL_STRING: 35162306a36Sopenharmony_ci case NLA_BINARY: 35262306a36Sopenharmony_ci if (pt->type == NLA_STRING) 35362306a36Sopenharmony_ci type = NL_ATTR_TYPE_STRING; 35462306a36Sopenharmony_ci else if (pt->type == NLA_NUL_STRING) 35562306a36Sopenharmony_ci type = NL_ATTR_TYPE_NUL_STRING; 35662306a36Sopenharmony_ci else 35762306a36Sopenharmony_ci type = NL_ATTR_TYPE_BINARY; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci if (pt->validation_type == NLA_VALIDATE_RANGE || 36062306a36Sopenharmony_ci pt->validation_type == NLA_VALIDATE_RANGE_WARN_TOO_LONG) { 36162306a36Sopenharmony_ci struct netlink_range_validation range; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci nla_get_range_unsigned(pt, &range); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci if (range.min && 36662306a36Sopenharmony_ci nla_put_u32(skb, NL_POLICY_TYPE_ATTR_MIN_LENGTH, 36762306a36Sopenharmony_ci range.min)) 36862306a36Sopenharmony_ci goto nla_put_failure; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci if (range.max < U16_MAX && 37162306a36Sopenharmony_ci nla_put_u32(skb, NL_POLICY_TYPE_ATTR_MAX_LENGTH, 37262306a36Sopenharmony_ci range.max)) 37362306a36Sopenharmony_ci goto nla_put_failure; 37462306a36Sopenharmony_ci } else if (pt->len && 37562306a36Sopenharmony_ci nla_put_u32(skb, NL_POLICY_TYPE_ATTR_MAX_LENGTH, 37662306a36Sopenharmony_ci pt->len)) { 37762306a36Sopenharmony_ci goto nla_put_failure; 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci break; 38062306a36Sopenharmony_ci case NLA_FLAG: 38162306a36Sopenharmony_ci type = NL_ATTR_TYPE_FLAG; 38262306a36Sopenharmony_ci break; 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci if (nla_put_u32(skb, NL_POLICY_TYPE_ATTR_TYPE, type)) 38662306a36Sopenharmony_ci goto nla_put_failure; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci nla_nest_end(skb, attr); 38962306a36Sopenharmony_ci WARN_ON(attr->nla_len > estimate); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci return 0; 39262306a36Sopenharmony_cinla_put_failure: 39362306a36Sopenharmony_ci nla_nest_cancel(skb, attr); 39462306a36Sopenharmony_ci return -ENOBUFS; 39562306a36Sopenharmony_ci} 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci/** 39862306a36Sopenharmony_ci * netlink_policy_dump_write_attr - write a given attribute policy 39962306a36Sopenharmony_ci * @skb: the message skb to write to 40062306a36Sopenharmony_ci * @pt: the attribute's policy 40162306a36Sopenharmony_ci * @nestattr: the nested attribute ID to use 40262306a36Sopenharmony_ci * 40362306a36Sopenharmony_ci * Returns: 0 on success, an error code otherwise; -%ENODATA is 40462306a36Sopenharmony_ci * special, indicating that there's no policy data and 40562306a36Sopenharmony_ci * the attribute is generally rejected. 40662306a36Sopenharmony_ci */ 40762306a36Sopenharmony_ciint netlink_policy_dump_write_attr(struct sk_buff *skb, 40862306a36Sopenharmony_ci const struct nla_policy *pt, 40962306a36Sopenharmony_ci int nestattr) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci return __netlink_policy_dump_write_attr(NULL, skb, pt, nestattr); 41262306a36Sopenharmony_ci} 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci/** 41562306a36Sopenharmony_ci * netlink_policy_dump_write - write current policy dump attributes 41662306a36Sopenharmony_ci * @skb: the message skb to write to 41762306a36Sopenharmony_ci * @state: the policy dump state 41862306a36Sopenharmony_ci * 41962306a36Sopenharmony_ci * Returns: 0 on success, an error code otherwise 42062306a36Sopenharmony_ci */ 42162306a36Sopenharmony_ciint netlink_policy_dump_write(struct sk_buff *skb, 42262306a36Sopenharmony_ci struct netlink_policy_dump_state *state) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci const struct nla_policy *pt; 42562306a36Sopenharmony_ci struct nlattr *policy; 42662306a36Sopenharmony_ci bool again; 42762306a36Sopenharmony_ci int err; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_cisend_attribute: 43062306a36Sopenharmony_ci again = false; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci pt = &state->policies[state->policy_idx].policy[state->attr_idx]; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci policy = nla_nest_start(skb, state->policy_idx); 43562306a36Sopenharmony_ci if (!policy) 43662306a36Sopenharmony_ci return -ENOBUFS; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci err = __netlink_policy_dump_write_attr(state, skb, pt, state->attr_idx); 43962306a36Sopenharmony_ci if (err == -ENODATA) { 44062306a36Sopenharmony_ci nla_nest_cancel(skb, policy); 44162306a36Sopenharmony_ci again = true; 44262306a36Sopenharmony_ci goto next; 44362306a36Sopenharmony_ci } else if (err) { 44462306a36Sopenharmony_ci goto nla_put_failure; 44562306a36Sopenharmony_ci } 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci /* finish and move state to next attribute */ 44862306a36Sopenharmony_ci nla_nest_end(skb, policy); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_cinext: 45162306a36Sopenharmony_ci state->attr_idx += 1; 45262306a36Sopenharmony_ci if (state->attr_idx > state->policies[state->policy_idx].maxtype) { 45362306a36Sopenharmony_ci state->attr_idx = 0; 45462306a36Sopenharmony_ci state->policy_idx++; 45562306a36Sopenharmony_ci } 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci if (again) { 45862306a36Sopenharmony_ci if (netlink_policy_dump_finished(state)) 45962306a36Sopenharmony_ci return -ENODATA; 46062306a36Sopenharmony_ci goto send_attribute; 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci return 0; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_cinla_put_failure: 46662306a36Sopenharmony_ci nla_nest_cancel(skb, policy); 46762306a36Sopenharmony_ci return -ENOBUFS; 46862306a36Sopenharmony_ci} 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci/** 47162306a36Sopenharmony_ci * netlink_policy_dump_free - free policy dump state 47262306a36Sopenharmony_ci * @state: the policy dump state to free 47362306a36Sopenharmony_ci * 47462306a36Sopenharmony_ci * Call this from the done() method to ensure dump state is freed. 47562306a36Sopenharmony_ci */ 47662306a36Sopenharmony_civoid netlink_policy_dump_free(struct netlink_policy_dump_state *state) 47762306a36Sopenharmony_ci{ 47862306a36Sopenharmony_ci kfree(state); 47962306a36Sopenharmony_ci} 480