Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+11
View File
@@ -0,0 +1,11 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# Netlink Sockets
#
config NETLINK_DIAG
tristate "NETLINK: socket monitoring interface"
default n
help
Support for NETLINK socket monitoring interface used by the ss tool.
If unsure, say Y.
+9
View File
@@ -0,0 +1,9 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for the netlink driver.
#
obj-y := af_netlink.o genetlink.o policy.o
obj-$(CONFIG_NETLINK_DIAG) += netlink_diag.o
netlink_diag-y := diag.o
File diff suppressed because it is too large Load Diff
+79
View File
@@ -0,0 +1,79 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _AF_NETLINK_H
#define _AF_NETLINK_H
#include <linux/rhashtable.h>
#include <linux/atomic.h>
#include <net/sock.h>
/* flags */
enum {
NETLINK_F_KERNEL_SOCKET,
NETLINK_F_RECV_PKTINFO,
NETLINK_F_BROADCAST_SEND_ERROR,
NETLINK_F_RECV_NO_ENOBUFS,
NETLINK_F_LISTEN_ALL_NSID,
NETLINK_F_CAP_ACK,
NETLINK_F_EXT_ACK,
NETLINK_F_STRICT_CHK,
};
#define NLGRPSZ(x) (ALIGN(x, sizeof(unsigned long) * 8) / 8)
#define NLGRPLONGS(x) (NLGRPSZ(x)/sizeof(unsigned long))
struct netlink_sock {
/* struct sock has to be the first member of netlink_sock */
struct sock sk;
unsigned long flags;
u32 portid;
u32 dst_portid;
u32 dst_group;
u32 subscriptions;
u32 ngroups;
unsigned long *groups;
unsigned long state;
size_t max_recvmsg_len;
wait_queue_head_t wait;
bool bound;
bool cb_running;
int dump_done_errno;
struct netlink_callback cb;
struct mutex nl_cb_mutex;
void (*netlink_rcv)(struct sk_buff *skb);
int (*netlink_bind)(struct net *net, int group);
void (*netlink_unbind)(struct net *net, int group);
void (*netlink_release)(struct sock *sk,
unsigned long *groups);
struct module *module;
struct rhash_head node;
struct rcu_head rcu;
};
static inline struct netlink_sock *nlk_sk(struct sock *sk)
{
return container_of(sk, struct netlink_sock, sk);
}
#define nlk_test_bit(nr, sk) test_bit(NETLINK_F_##nr, &nlk_sk(sk)->flags)
struct netlink_table {
struct rhashtable hash;
struct hlist_head mc_list;
struct listeners __rcu *listeners;
unsigned int flags;
unsigned int groups;
struct mutex *cb_mutex;
struct module *module;
int (*bind)(struct net *net, int group);
void (*unbind)(struct net *net, int group);
void (*release)(struct sock *sk,
unsigned long *groups);
int registered;
};
extern struct netlink_table *nl_table;
extern rwlock_t nl_table_lock;
#endif
+263
View File
@@ -0,0 +1,263 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/module.h>
#include <net/sock.h>
#include <linux/netlink.h>
#include <linux/sock_diag.h>
#include <linux/netlink_diag.h>
#include <linux/rhashtable.h>
#include "af_netlink.h"
static int sk_diag_dump_groups(struct sock *sk, struct sk_buff *nlskb)
{
struct netlink_sock *nlk = nlk_sk(sk);
if (nlk->groups == NULL)
return 0;
return nla_put(nlskb, NETLINK_DIAG_GROUPS, NLGRPSZ(nlk->ngroups),
nlk->groups);
}
static int sk_diag_put_flags(struct sock *sk, struct sk_buff *skb)
{
struct netlink_sock *nlk = nlk_sk(sk);
u32 flags = 0;
if (nlk->cb_running)
flags |= NDIAG_FLAG_CB_RUNNING;
if (nlk_test_bit(RECV_PKTINFO, sk))
flags |= NDIAG_FLAG_PKTINFO;
if (nlk_test_bit(BROADCAST_SEND_ERROR, sk))
flags |= NDIAG_FLAG_BROADCAST_ERROR;
if (nlk_test_bit(RECV_NO_ENOBUFS, sk))
flags |= NDIAG_FLAG_NO_ENOBUFS;
if (nlk_test_bit(LISTEN_ALL_NSID, sk))
flags |= NDIAG_FLAG_LISTEN_ALL_NSID;
if (nlk_test_bit(CAP_ACK, sk))
flags |= NDIAG_FLAG_CAP_ACK;
return nla_put_u32(skb, NETLINK_DIAG_FLAGS, flags);
}
static int sk_diag_fill(struct sock *sk, struct sk_buff *skb,
struct netlink_diag_req *req,
u32 portid, u32 seq, u32 flags, u64 sk_ino)
{
struct nlmsghdr *nlh;
struct netlink_diag_msg *rep;
struct netlink_sock *nlk = nlk_sk(sk);
nlh = nlmsg_put(skb, portid, seq, SOCK_DIAG_BY_FAMILY, sizeof(*rep),
flags);
if (!nlh)
return -EMSGSIZE;
rep = nlmsg_data(nlh);
rep->ndiag_family = AF_NETLINK;
rep->ndiag_type = sk->sk_type;
rep->ndiag_protocol = sk->sk_protocol;
rep->ndiag_state = sk->sk_state;
rep->ndiag_ino = sk_ino;
rep->ndiag_portid = nlk->portid;
rep->ndiag_dst_portid = nlk->dst_portid;
rep->ndiag_dst_group = nlk->dst_group;
sock_diag_save_cookie(sk, rep->ndiag_cookie);
if ((req->ndiag_show & NDIAG_SHOW_GROUPS) &&
sk_diag_dump_groups(sk, skb))
goto out_nlmsg_trim;
if ((req->ndiag_show & NDIAG_SHOW_MEMINFO) &&
sock_diag_put_meminfo(sk, skb, NETLINK_DIAG_MEMINFO))
goto out_nlmsg_trim;
if ((req->ndiag_show & NDIAG_SHOW_FLAGS) &&
sk_diag_put_flags(sk, skb))
goto out_nlmsg_trim;
nlmsg_end(skb, nlh);
return 0;
out_nlmsg_trim:
nlmsg_cancel(skb, nlh);
return -EMSGSIZE;
}
static int __netlink_diag_dump(struct sk_buff *skb, struct netlink_callback *cb,
int protocol, int s_num)
{
struct rhashtable_iter *hti = (void *)cb->args[2];
struct netlink_table *tbl = &nl_table[protocol];
struct net *net = sock_net(skb->sk);
struct netlink_diag_req *req;
struct netlink_sock *nlsk;
unsigned long flags;
struct sock *sk;
int num = 2;
int ret = 0;
req = nlmsg_data(cb->nlh);
if (s_num > 1)
goto mc_list;
num--;
if (!hti) {
hti = kmalloc_obj(*hti);
if (!hti)
return -ENOMEM;
cb->args[2] = (long)hti;
}
if (!s_num)
rhashtable_walk_enter(&tbl->hash, hti);
rhashtable_walk_start(hti);
while ((nlsk = rhashtable_walk_next(hti))) {
if (IS_ERR(nlsk)) {
ret = PTR_ERR(nlsk);
if (ret == -EAGAIN) {
ret = 0;
continue;
}
break;
}
sk = (struct sock *)nlsk;
if (!net_eq(sock_net(sk), net))
continue;
if (sk_diag_fill(sk, skb, req,
NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq,
NLM_F_MULTI,
sock_i_ino(sk)) < 0) {
ret = 1;
break;
}
}
rhashtable_walk_stop(hti);
if (ret)
goto done;
rhashtable_walk_exit(hti);
num++;
mc_list:
read_lock_irqsave(&nl_table_lock, flags);
sk_for_each_bound(sk, &tbl->mc_list) {
if (sk_hashed(sk))
continue;
if (!net_eq(sock_net(sk), net))
continue;
if (num < s_num) {
num++;
continue;
}
if (sk_diag_fill(sk, skb, req,
NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq,
NLM_F_MULTI,
sock_i_ino(sk)) < 0) {
ret = 1;
break;
}
num++;
}
read_unlock_irqrestore(&nl_table_lock, flags);
done:
cb->args[0] = num;
return ret;
}
static int netlink_diag_dump(struct sk_buff *skb, struct netlink_callback *cb)
{
struct netlink_diag_req *req;
int s_num = cb->args[0];
int err = 0;
req = nlmsg_data(cb->nlh);
if (req->sdiag_protocol == NDIAG_PROTO_ALL) {
int i;
for (i = cb->args[1]; i < MAX_LINKS; i++) {
err = __netlink_diag_dump(skb, cb, i, s_num);
if (err)
break;
s_num = 0;
}
cb->args[1] = i;
} else {
if (req->sdiag_protocol >= MAX_LINKS)
return -ENOENT;
err = __netlink_diag_dump(skb, cb, req->sdiag_protocol, s_num);
}
return err <= 0 ? err : skb->len;
}
static int netlink_diag_dump_done(struct netlink_callback *cb)
{
struct rhashtable_iter *hti = (void *)cb->args[2];
if (cb->args[0] == 1)
rhashtable_walk_exit(hti);
kfree(hti);
return 0;
}
static int netlink_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h)
{
int hdrlen = sizeof(struct netlink_diag_req);
struct net *net = sock_net(skb->sk);
if (nlmsg_len(h) < hdrlen)
return -EINVAL;
if (h->nlmsg_flags & NLM_F_DUMP) {
struct netlink_dump_control c = {
.dump = netlink_diag_dump,
.done = netlink_diag_dump_done,
};
return netlink_dump_start(net->diag_nlsk, skb, h, &c);
} else
return -EOPNOTSUPP;
}
static const struct sock_diag_handler netlink_diag_handler = {
.owner = THIS_MODULE,
.family = AF_NETLINK,
.dump = netlink_diag_handler_dump,
};
static int __init netlink_diag_init(void)
{
return sock_diag_register(&netlink_diag_handler);
}
static void __exit netlink_diag_exit(void)
{
sock_diag_unregister(&netlink_diag_handler);
}
module_init(netlink_diag_init);
module_exit(netlink_diag_exit);
MODULE_DESCRIPTION("Netlink-based socket monitoring/diagnostic interface (sock_diag)");
MODULE_LICENSE("GPL");
MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 16 /* AF_NETLINK */);
File diff suppressed because it is too large Load Diff
+11
View File
@@ -0,0 +1,11 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __NET_GENETLINK_H
#define __NET_GENETLINK_H
#include <linux/wait.h>
/* for synchronisation between af_netlink and genetlink */
extern atomic_t genl_sk_destructing_cnt;
extern wait_queue_head_t genl_sk_destructing_waitq;
#endif /* __LINUX_GENERIC_NETLINK_H */
+492
View File
@@ -0,0 +1,492 @@
// SPDX-License-Identifier: GPL-2.0
/*
* NETLINK Policy advertisement to userspace
*
* Authors: Johannes Berg <johannes@sipsolutions.net>
*
* Copyright 2019 Intel Corporation
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <net/netlink.h>
#define INITIAL_POLICIES_ALLOC 10
struct netlink_policy_dump_state {
unsigned int policy_idx;
unsigned int attr_idx;
unsigned int n_alloc;
struct {
const struct nla_policy *policy;
unsigned int maxtype;
} policies[] __counted_by(n_alloc);
};
static int add_policy(struct netlink_policy_dump_state **statep,
const struct nla_policy *policy,
unsigned int maxtype)
{
struct netlink_policy_dump_state *state = *statep;
unsigned int old_n_alloc, n_alloc, i;
if (!policy)
return 0;
for (i = 0; i < state->n_alloc; i++) {
if (state->policies[i].policy == policy &&
state->policies[i].maxtype == maxtype)
return 0;
if (!state->policies[i].policy) {
state->policies[i].policy = policy;
state->policies[i].maxtype = maxtype;
return 0;
}
}
n_alloc = state->n_alloc + INITIAL_POLICIES_ALLOC;
state = krealloc(state, struct_size(state, policies, n_alloc),
GFP_KERNEL);
if (!state)
return -ENOMEM;
old_n_alloc = state->n_alloc;
state->n_alloc = n_alloc;
memset(&state->policies[old_n_alloc], 0,
flex_array_size(state, policies, n_alloc - old_n_alloc));
state->policies[old_n_alloc].policy = policy;
state->policies[old_n_alloc].maxtype = maxtype;
*statep = state;
return 0;
}
/**
* netlink_policy_dump_get_policy_idx - retrieve policy index
* @state: the policy dump state
* @policy: the policy to find
* @maxtype: the policy's maxattr
*
* Returns: the index of the given policy in the dump state
*
* Call this to find a policy index when you've added multiple and e.g.
* need to tell userspace which command has which policy (by index).
*
* Note: this will WARN and return 0 if the policy isn't found, which
* means it wasn't added in the first place, which would be an
* internal consistency bug.
*/
int netlink_policy_dump_get_policy_idx(struct netlink_policy_dump_state *state,
const struct nla_policy *policy,
unsigned int maxtype)
{
unsigned int i;
if (WARN_ON(!policy))
return 0;
for (i = 0; i < state->n_alloc; i++) {
if (state->policies[i].policy == policy &&
state->policies[i].maxtype == maxtype)
return i;
}
WARN_ON(1);
return 0;
}
static struct netlink_policy_dump_state *alloc_state(void)
{
struct netlink_policy_dump_state *state;
state = kzalloc_flex(*state, policies, INITIAL_POLICIES_ALLOC);
if (!state)
return ERR_PTR(-ENOMEM);
state->n_alloc = INITIAL_POLICIES_ALLOC;
return state;
}
/**
* netlink_policy_dump_add_policy - add a policy to the dump
* @pstate: state to add to, may be reallocated, must be %NULL the first time
* @policy: the new policy to add to the dump
* @maxtype: the new policy's max attr type
*
* Returns: 0 on success, a negative error code otherwise.
*
* Call this to allocate a policy dump state, and to add policies to it. This
* should be called from the dump start() callback.
*
* Note: on failures, any previously allocated state is freed.
*/
int netlink_policy_dump_add_policy(struct netlink_policy_dump_state **pstate,
const struct nla_policy *policy,
unsigned int maxtype)
{
struct netlink_policy_dump_state *state = *pstate;
unsigned int policy_idx;
int err;
if (!state) {
state = alloc_state();
if (IS_ERR(state))
return PTR_ERR(state);
}
/*
* walk the policies and nested ones first, and build
* a linear list of them.
*/
err = add_policy(&state, policy, maxtype);
if (err)
goto err_try_undo;
for (policy_idx = 0;
policy_idx < state->n_alloc && state->policies[policy_idx].policy;
policy_idx++) {
const struct nla_policy *policy;
unsigned int type;
policy = state->policies[policy_idx].policy;
for (type = 0;
type <= state->policies[policy_idx].maxtype;
type++) {
switch (policy[type].type) {
case NLA_NESTED:
case NLA_NESTED_ARRAY:
err = add_policy(&state,
policy[type].nested_policy,
policy[type].len);
if (err)
goto err_try_undo;
break;
default:
break;
}
}
}
*pstate = state;
return 0;
err_try_undo:
/* Try to preserve reasonable unwind semantics - if we're starting from
* scratch clean up fully, otherwise record what we got and caller will.
*/
if (!*pstate)
netlink_policy_dump_free(state);
else
*pstate = state;
return err;
}
static bool
netlink_policy_dump_finished(struct netlink_policy_dump_state *state)
{
return state->policy_idx >= state->n_alloc ||
!state->policies[state->policy_idx].policy;
}
/**
* netlink_policy_dump_loop - dumping loop indicator
* @state: the policy dump state
*
* Returns: %true if the dump continues, %false otherwise
*
* Note: this frees the dump state when finishing
*/
bool netlink_policy_dump_loop(struct netlink_policy_dump_state *state)
{
return !netlink_policy_dump_finished(state);
}
int netlink_policy_dump_attr_size_estimate(const struct nla_policy *pt)
{
/* nested + type */
int common = 2 * nla_attr_size(sizeof(u32));
switch (pt->type) {
case NLA_UNSPEC:
case NLA_REJECT:
/* these actually don't need any space */
return 0;
case NLA_NESTED:
case NLA_NESTED_ARRAY:
/* common, policy idx, policy maxattr */
return common + 2 * nla_attr_size(sizeof(u32));
case NLA_U8:
case NLA_U16:
case NLA_U32:
case NLA_U64:
case NLA_MSECS:
case NLA_S8:
case NLA_S16:
case NLA_S32:
case NLA_S64:
case NLA_SINT:
case NLA_UINT:
/* maximum is common, u64 min/max with padding */
return common +
2 * (nla_attr_size(0) + nla_attr_size(sizeof(u64)));
case NLA_BITFIELD32:
return common + nla_attr_size(sizeof(u32));
case NLA_STRING:
case NLA_NUL_STRING:
case NLA_BINARY:
/* maximum is common, u32 min-length/max-length */
return common + 2 * nla_attr_size(sizeof(u32));
case NLA_FLAG:
return common;
}
/* this should then cause a warning later */
return 0;
}
static int
__netlink_policy_dump_write_attr(struct netlink_policy_dump_state *state,
struct sk_buff *skb,
const struct nla_policy *pt,
int nestattr)
{
int estimate = netlink_policy_dump_attr_size_estimate(pt);
enum netlink_attribute_type type;
struct nlattr *attr;
attr = nla_nest_start(skb, nestattr);
if (!attr)
return -ENOBUFS;
switch (pt->type) {
default:
case NLA_UNSPEC:
case NLA_REJECT:
/* skip - use NLA_MIN_LEN to advertise such */
nla_nest_cancel(skb, attr);
return -ENODATA;
case NLA_NESTED:
type = NL_ATTR_TYPE_NESTED;
fallthrough;
case NLA_NESTED_ARRAY:
if (pt->type == NLA_NESTED_ARRAY)
type = NL_ATTR_TYPE_NESTED_ARRAY;
if (state && pt->nested_policy && pt->len &&
(nla_put_u32(skb, NL_POLICY_TYPE_ATTR_POLICY_IDX,
netlink_policy_dump_get_policy_idx(state,
pt->nested_policy,
pt->len)) ||
nla_put_u32(skb, NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE,
pt->len)))
goto nla_put_failure;
break;
case NLA_U8:
case NLA_U16:
case NLA_U32:
case NLA_U64:
case NLA_UINT:
case NLA_MSECS: {
struct netlink_range_validation range;
if (pt->type == NLA_U8)
type = NL_ATTR_TYPE_U8;
else if (pt->type == NLA_U16)
type = NL_ATTR_TYPE_U16;
else if (pt->type == NLA_U32)
type = NL_ATTR_TYPE_U32;
else if (pt->type == NLA_U64)
type = NL_ATTR_TYPE_U64;
else
type = NL_ATTR_TYPE_UINT;
if (pt->validation_type == NLA_VALIDATE_MASK) {
if (nla_put_u64_64bit(skb, NL_POLICY_TYPE_ATTR_MASK,
pt->mask,
NL_POLICY_TYPE_ATTR_PAD))
goto nla_put_failure;
break;
} else if (pt->validation_type == NLA_VALIDATE_FUNCTION) {
break;
}
nla_get_range_unsigned(pt, &range);
if (nla_put_u64_64bit(skb, NL_POLICY_TYPE_ATTR_MIN_VALUE_U,
range.min, NL_POLICY_TYPE_ATTR_PAD) ||
nla_put_u64_64bit(skb, NL_POLICY_TYPE_ATTR_MAX_VALUE_U,
range.max, NL_POLICY_TYPE_ATTR_PAD))
goto nla_put_failure;
break;
}
case NLA_S8:
case NLA_S16:
case NLA_S32:
case NLA_S64:
case NLA_SINT: {
struct netlink_range_validation_signed range;
if (pt->type == NLA_S8)
type = NL_ATTR_TYPE_S8;
else if (pt->type == NLA_S16)
type = NL_ATTR_TYPE_S16;
else if (pt->type == NLA_S32)
type = NL_ATTR_TYPE_S32;
else if (pt->type == NLA_S64)
type = NL_ATTR_TYPE_S64;
else
type = NL_ATTR_TYPE_SINT;
if (pt->validation_type == NLA_VALIDATE_FUNCTION)
break;
nla_get_range_signed(pt, &range);
if (nla_put_s64(skb, NL_POLICY_TYPE_ATTR_MIN_VALUE_S,
range.min, NL_POLICY_TYPE_ATTR_PAD) ||
nla_put_s64(skb, NL_POLICY_TYPE_ATTR_MAX_VALUE_S,
range.max, NL_POLICY_TYPE_ATTR_PAD))
goto nla_put_failure;
break;
}
case NLA_BITFIELD32:
type = NL_ATTR_TYPE_BITFIELD32;
if (nla_put_u32(skb, NL_POLICY_TYPE_ATTR_BITFIELD32_MASK,
pt->bitfield32_valid))
goto nla_put_failure;
break;
case NLA_STRING:
case NLA_NUL_STRING:
case NLA_BINARY:
if (pt->type == NLA_STRING)
type = NL_ATTR_TYPE_STRING;
else if (pt->type == NLA_NUL_STRING)
type = NL_ATTR_TYPE_NUL_STRING;
else
type = NL_ATTR_TYPE_BINARY;
if (pt->validation_type == NLA_VALIDATE_RANGE ||
pt->validation_type == NLA_VALIDATE_RANGE_WARN_TOO_LONG) {
struct netlink_range_validation range;
nla_get_range_unsigned(pt, &range);
if (range.min &&
nla_put_u32(skb, NL_POLICY_TYPE_ATTR_MIN_LENGTH,
range.min))
goto nla_put_failure;
if (range.max < U16_MAX &&
nla_put_u32(skb, NL_POLICY_TYPE_ATTR_MAX_LENGTH,
range.max))
goto nla_put_failure;
} else if (pt->len &&
nla_put_u32(skb, NL_POLICY_TYPE_ATTR_MAX_LENGTH,
pt->len)) {
goto nla_put_failure;
}
break;
case NLA_FLAG:
type = NL_ATTR_TYPE_FLAG;
break;
}
if (nla_put_u32(skb, NL_POLICY_TYPE_ATTR_TYPE, type))
goto nla_put_failure;
nla_nest_end(skb, attr);
WARN_ON(attr->nla_len > estimate);
return 0;
nla_put_failure:
nla_nest_cancel(skb, attr);
return -ENOBUFS;
}
/**
* netlink_policy_dump_write_attr - write a given attribute policy
* @skb: the message skb to write to
* @pt: the attribute's policy
* @nestattr: the nested attribute ID to use
*
* Returns: 0 on success, an error code otherwise; -%ENODATA is
* special, indicating that there's no policy data and
* the attribute is generally rejected.
*/
int netlink_policy_dump_write_attr(struct sk_buff *skb,
const struct nla_policy *pt,
int nestattr)
{
return __netlink_policy_dump_write_attr(NULL, skb, pt, nestattr);
}
/**
* netlink_policy_dump_write - write current policy dump attributes
* @skb: the message skb to write to
* @state: the policy dump state
*
* Returns: 0 on success, an error code otherwise
*/
int netlink_policy_dump_write(struct sk_buff *skb,
struct netlink_policy_dump_state *state)
{
const struct nla_policy *pt;
struct nlattr *policy;
bool again;
int err;
send_attribute:
again = false;
pt = &state->policies[state->policy_idx].policy[state->attr_idx];
policy = nla_nest_start(skb, state->policy_idx);
if (!policy)
return -ENOBUFS;
err = __netlink_policy_dump_write_attr(state, skb, pt, state->attr_idx);
if (err == -ENODATA) {
nla_nest_cancel(skb, policy);
again = true;
goto next;
} else if (err) {
goto nla_put_failure;
}
/* finish and move state to next attribute */
nla_nest_end(skb, policy);
next:
state->attr_idx += 1;
if (state->attr_idx > state->policies[state->policy_idx].maxtype) {
state->attr_idx = 0;
state->policy_idx++;
}
if (again) {
if (netlink_policy_dump_finished(state))
return -ENODATA;
goto send_attribute;
}
return 0;
nla_put_failure:
nla_nest_cancel(skb, policy);
return -ENOBUFS;
}
/**
* netlink_policy_dump_free - free policy dump state
* @state: the policy dump state to free
*
* Call this from the done() method to ensure dump state is freed.
*/
void netlink_policy_dump_free(struct netlink_policy_dump_state *state)
{
kfree(state);
}