1e2cf17d3SFlorian Westphal // SPDX-License-Identifier: GPL-2.0-or-later
2e2cf17d3SFlorian Westphal /*
3e2cf17d3SFlorian Westphal  * Copyright (c) 2021 Red Hat GmbH
4e2cf17d3SFlorian Westphal  *
5e2cf17d3SFlorian Westphal  * Author: Florian Westphal <fw@strlen.de>
6e2cf17d3SFlorian Westphal  */
7e2cf17d3SFlorian Westphal 
8e2cf17d3SFlorian Westphal #include <linux/module.h>
9e2cf17d3SFlorian Westphal #include <linux/kernel.h>
10e2cf17d3SFlorian Westphal #include <linux/types.h>
11e2cf17d3SFlorian Westphal #include <linux/skbuff.h>
12e2cf17d3SFlorian Westphal #include <linux/errno.h>
13e2cf17d3SFlorian Westphal #include <linux/netlink.h>
14e2cf17d3SFlorian Westphal #include <linux/slab.h>
15e2cf17d3SFlorian Westphal 
16e2cf17d3SFlorian Westphal #include <linux/netfilter.h>
17e2cf17d3SFlorian Westphal 
18e2cf17d3SFlorian Westphal #include <linux/netfilter/nfnetlink.h>
19e2cf17d3SFlorian Westphal #include <linux/netfilter/nfnetlink_hook.h>
20e2cf17d3SFlorian Westphal 
21e2cf17d3SFlorian Westphal #include <net/netfilter/nf_tables.h>
22e2cf17d3SFlorian Westphal #include <net/sock.h>
23e2cf17d3SFlorian Westphal 
24e2cf17d3SFlorian Westphal static const struct nla_policy nfnl_hook_nla_policy[NFNLA_HOOK_MAX + 1] = {
25e2cf17d3SFlorian Westphal 	[NFNLA_HOOK_HOOKNUM]	= { .type = NLA_U32 },
26e2cf17d3SFlorian Westphal 	[NFNLA_HOOK_PRIORITY]	= { .type = NLA_U32 },
27e2cf17d3SFlorian Westphal 	[NFNLA_HOOK_DEV]	= { .type = NLA_STRING,
28e2cf17d3SFlorian Westphal 				    .len = IFNAMSIZ - 1 },
29e2cf17d3SFlorian Westphal 	[NFNLA_HOOK_FUNCTION_NAME] = { .type = NLA_NUL_STRING,
30e2cf17d3SFlorian Westphal 				       .len = KSYM_NAME_LEN, },
31e2cf17d3SFlorian Westphal 	[NFNLA_HOOK_MODULE_NAME] = { .type = NLA_NUL_STRING,
32e2cf17d3SFlorian Westphal 				     .len = MODULE_NAME_LEN, },
33e2cf17d3SFlorian Westphal 	[NFNLA_HOOK_CHAIN_INFO] = { .type = NLA_NESTED, },
34e2cf17d3SFlorian Westphal };
35e2cf17d3SFlorian Westphal 
36e2cf17d3SFlorian Westphal static int nf_netlink_dump_start_rcu(struct sock *nlsk, struct sk_buff *skb,
37e2cf17d3SFlorian Westphal 				     const struct nlmsghdr *nlh,
38e2cf17d3SFlorian Westphal 				     struct netlink_dump_control *c)
39e2cf17d3SFlorian Westphal {
40e2cf17d3SFlorian Westphal 	int err;
41e2cf17d3SFlorian Westphal 
42e2cf17d3SFlorian Westphal 	if (!try_module_get(THIS_MODULE))
43e2cf17d3SFlorian Westphal 		return -EINVAL;
44e2cf17d3SFlorian Westphal 
45e2cf17d3SFlorian Westphal 	rcu_read_unlock();
46e2cf17d3SFlorian Westphal 	err = netlink_dump_start(nlsk, skb, nlh, c);
47e2cf17d3SFlorian Westphal 	rcu_read_lock();
48e2cf17d3SFlorian Westphal 	module_put(THIS_MODULE);
49e2cf17d3SFlorian Westphal 
50e2cf17d3SFlorian Westphal 	return err;
51e2cf17d3SFlorian Westphal }
52e2cf17d3SFlorian Westphal 
53e2cf17d3SFlorian Westphal struct nfnl_dump_hook_data {
54e2cf17d3SFlorian Westphal 	char devname[IFNAMSIZ];
55e2cf17d3SFlorian Westphal 	unsigned long headv;
56e2cf17d3SFlorian Westphal 	u8 hook;
57e2cf17d3SFlorian Westphal };
58e2cf17d3SFlorian Westphal 
59e2cf17d3SFlorian Westphal static int nfnl_hook_put_nft_chain_info(struct sk_buff *nlskb,
60e2cf17d3SFlorian Westphal 					const struct nfnl_dump_hook_data *ctx,
61e2cf17d3SFlorian Westphal 					unsigned int seq,
62e2cf17d3SFlorian Westphal 					const struct nf_hook_ops *ops)
63e2cf17d3SFlorian Westphal {
64e2cf17d3SFlorian Westphal 	struct net *net = sock_net(nlskb->sk);
65e2cf17d3SFlorian Westphal 	struct nlattr *nest, *nest2;
66e2cf17d3SFlorian Westphal 	struct nft_chain *chain;
67e2cf17d3SFlorian Westphal 	int ret = 0;
68e2cf17d3SFlorian Westphal 
69e2cf17d3SFlorian Westphal 	if (ops->hook_ops_type != NF_HOOK_OP_NF_TABLES)
70e2cf17d3SFlorian Westphal 		return 0;
71e2cf17d3SFlorian Westphal 
72e2cf17d3SFlorian Westphal 	chain = ops->priv;
73e2cf17d3SFlorian Westphal 	if (WARN_ON_ONCE(!chain))
74e2cf17d3SFlorian Westphal 		return 0;
75e2cf17d3SFlorian Westphal 
76e2cf17d3SFlorian Westphal 	if (!nft_is_active(net, chain))
77e2cf17d3SFlorian Westphal 		return 0;
78e2cf17d3SFlorian Westphal 
79e2cf17d3SFlorian Westphal 	nest = nla_nest_start(nlskb, NFNLA_HOOK_CHAIN_INFO);
80e2cf17d3SFlorian Westphal 	if (!nest)
81e2cf17d3SFlorian Westphal 		return -EMSGSIZE;
82e2cf17d3SFlorian Westphal 
83e2cf17d3SFlorian Westphal 	ret = nla_put_be32(nlskb, NFNLA_HOOK_INFO_TYPE,
84e2cf17d3SFlorian Westphal 			   htonl(NFNL_HOOK_TYPE_NFTABLES));
85e2cf17d3SFlorian Westphal 	if (ret)
86e2cf17d3SFlorian Westphal 		goto cancel_nest;
87e2cf17d3SFlorian Westphal 
88e2cf17d3SFlorian Westphal 	nest2 = nla_nest_start(nlskb, NFNLA_HOOK_INFO_DESC);
89e2cf17d3SFlorian Westphal 	if (!nest2)
90e2cf17d3SFlorian Westphal 		goto cancel_nest;
91e2cf17d3SFlorian Westphal 
92a6e57c4aSPablo Neira Ayuso 	ret = nla_put_string(nlskb, NFNLA_CHAIN_TABLE, chain->table->name);
93e2cf17d3SFlorian Westphal 	if (ret)
94e2cf17d3SFlorian Westphal 		goto cancel_nest;
95e2cf17d3SFlorian Westphal 
96a6e57c4aSPablo Neira Ayuso 	ret = nla_put_string(nlskb, NFNLA_CHAIN_NAME, chain->name);
97a6e57c4aSPablo Neira Ayuso 	if (ret)
98a6e57c4aSPablo Neira Ayuso 		goto cancel_nest;
99a6e57c4aSPablo Neira Ayuso 
100a6e57c4aSPablo Neira Ayuso 	ret = nla_put_u8(nlskb, NFNLA_CHAIN_FAMILY, chain->table->family);
101e2cf17d3SFlorian Westphal 	if (ret)
102e2cf17d3SFlorian Westphal 		goto cancel_nest;
103e2cf17d3SFlorian Westphal 
104e2cf17d3SFlorian Westphal 	nla_nest_end(nlskb, nest2);
105e2cf17d3SFlorian Westphal 	nla_nest_end(nlskb, nest);
106e2cf17d3SFlorian Westphal 	return ret;
107e2cf17d3SFlorian Westphal 
108e2cf17d3SFlorian Westphal cancel_nest:
109e2cf17d3SFlorian Westphal 	nla_nest_cancel(nlskb, nest);
110e2cf17d3SFlorian Westphal 	return -EMSGSIZE;
111e2cf17d3SFlorian Westphal }
112e2cf17d3SFlorian Westphal 
113e2cf17d3SFlorian Westphal static int nfnl_hook_dump_one(struct sk_buff *nlskb,
114e2cf17d3SFlorian Westphal 			      const struct nfnl_dump_hook_data *ctx,
115e2cf17d3SFlorian Westphal 			      const struct nf_hook_ops *ops,
11669311e7cSPablo Neira Ayuso 			      int family, unsigned int seq)
117e2cf17d3SFlorian Westphal {
118e2cf17d3SFlorian Westphal 	u16 event = nfnl_msg_type(NFNL_SUBSYS_HOOK, NFNL_MSG_HOOK_GET);
119e2cf17d3SFlorian Westphal 	unsigned int portid = NETLINK_CB(nlskb).portid;
120e2cf17d3SFlorian Westphal 	struct nlmsghdr *nlh;
121e2cf17d3SFlorian Westphal 	int ret = -EMSGSIZE;
122269fc695SPablo Neira Ayuso 	u32 hooknum;
123e2cf17d3SFlorian Westphal #ifdef CONFIG_KALLSYMS
124e2cf17d3SFlorian Westphal 	char sym[KSYM_SYMBOL_LEN];
125e2cf17d3SFlorian Westphal 	char *module_name;
126e2cf17d3SFlorian Westphal #endif
127e2cf17d3SFlorian Westphal 	nlh = nfnl_msg_put(nlskb, portid, seq, event,
12869311e7cSPablo Neira Ayuso 			   NLM_F_MULTI, family, NFNETLINK_V0, 0);
129e2cf17d3SFlorian Westphal 	if (!nlh)
130e2cf17d3SFlorian Westphal 		goto nla_put_failure;
131e2cf17d3SFlorian Westphal 
132e2cf17d3SFlorian Westphal #ifdef CONFIG_KALLSYMS
133e2cf17d3SFlorian Westphal 	ret = snprintf(sym, sizeof(sym), "%ps", ops->hook);
13424610ed8SDan Carpenter 	if (ret >= sizeof(sym)) {
13524610ed8SDan Carpenter 		ret = -EINVAL;
136e2cf17d3SFlorian Westphal 		goto nla_put_failure;
13724610ed8SDan Carpenter 	}
138e2cf17d3SFlorian Westphal 
139e2cf17d3SFlorian Westphal 	module_name = strstr(sym, " [");
140e2cf17d3SFlorian Westphal 	if (module_name) {
141e2cf17d3SFlorian Westphal 		char *end;
142e2cf17d3SFlorian Westphal 
14361e0c2bcSPablo Neira Ayuso 		*module_name = '\0';
144e2cf17d3SFlorian Westphal 		module_name += 2;
145e2cf17d3SFlorian Westphal 		end = strchr(module_name, ']');
146e2cf17d3SFlorian Westphal 		if (end) {
147e2cf17d3SFlorian Westphal 			*end = 0;
148e2cf17d3SFlorian Westphal 
149e2cf17d3SFlorian Westphal 			ret = nla_put_string(nlskb, NFNLA_HOOK_MODULE_NAME, module_name);
150e2cf17d3SFlorian Westphal 			if (ret)
151e2cf17d3SFlorian Westphal 				goto nla_put_failure;
152e2cf17d3SFlorian Westphal 		}
153e2cf17d3SFlorian Westphal 	}
154e2cf17d3SFlorian Westphal 
155e2cf17d3SFlorian Westphal 	ret = nla_put_string(nlskb, NFNLA_HOOK_FUNCTION_NAME, sym);
156e2cf17d3SFlorian Westphal 	if (ret)
157e2cf17d3SFlorian Westphal 		goto nla_put_failure;
158e2cf17d3SFlorian Westphal #endif
159e2cf17d3SFlorian Westphal 
160269fc695SPablo Neira Ayuso 	if (ops->pf == NFPROTO_INET && ops->hooknum == NF_INET_INGRESS)
161269fc695SPablo Neira Ayuso 		hooknum = NF_NETDEV_INGRESS;
162269fc695SPablo Neira Ayuso 	else
163269fc695SPablo Neira Ayuso 		hooknum = ops->hooknum;
164269fc695SPablo Neira Ayuso 
165269fc695SPablo Neira Ayuso 	ret = nla_put_be32(nlskb, NFNLA_HOOK_HOOKNUM, htonl(hooknum));
166e2cf17d3SFlorian Westphal 	if (ret)
167e2cf17d3SFlorian Westphal 		goto nla_put_failure;
168e2cf17d3SFlorian Westphal 
169e2cf17d3SFlorian Westphal 	ret = nla_put_be32(nlskb, NFNLA_HOOK_PRIORITY, htonl(ops->priority));
170e2cf17d3SFlorian Westphal 	if (ret)
171e2cf17d3SFlorian Westphal 		goto nla_put_failure;
172e2cf17d3SFlorian Westphal 
173e2cf17d3SFlorian Westphal 	ret = nfnl_hook_put_nft_chain_info(nlskb, ctx, seq, ops);
174e2cf17d3SFlorian Westphal 	if (ret)
175e2cf17d3SFlorian Westphal 		goto nla_put_failure;
176e2cf17d3SFlorian Westphal 
177e2cf17d3SFlorian Westphal 	nlmsg_end(nlskb, nlh);
178e2cf17d3SFlorian Westphal 	return 0;
179e2cf17d3SFlorian Westphal nla_put_failure:
180e2cf17d3SFlorian Westphal 	nlmsg_trim(nlskb, nlh);
181e2cf17d3SFlorian Westphal 	return ret;
182e2cf17d3SFlorian Westphal }
183e2cf17d3SFlorian Westphal 
184e2cf17d3SFlorian Westphal static const struct nf_hook_entries *
185e2cf17d3SFlorian Westphal nfnl_hook_entries_head(u8 pf, unsigned int hook, struct net *net, const char *dev)
186e2cf17d3SFlorian Westphal {
187e2cf17d3SFlorian Westphal 	const struct nf_hook_entries *hook_head = NULL;
188*42df6e1dSLukas Wunner #if defined(CONFIG_NETFILTER_INGRESS) || defined(CONFIG_NETFILTER_EGRESS)
189e2cf17d3SFlorian Westphal 	struct net_device *netdev;
190217e26bdSArnd Bergmann #endif
191e2cf17d3SFlorian Westphal 
192e2cf17d3SFlorian Westphal 	switch (pf) {
193e2cf17d3SFlorian Westphal 	case NFPROTO_IPV4:
194e2cf17d3SFlorian Westphal 		if (hook >= ARRAY_SIZE(net->nf.hooks_ipv4))
195e2cf17d3SFlorian Westphal 			return ERR_PTR(-EINVAL);
196e2cf17d3SFlorian Westphal 		hook_head = rcu_dereference(net->nf.hooks_ipv4[hook]);
197e2cf17d3SFlorian Westphal 		break;
198e2cf17d3SFlorian Westphal 	case NFPROTO_IPV6:
199e2cf17d3SFlorian Westphal 		if (hook >= ARRAY_SIZE(net->nf.hooks_ipv6))
200e2cf17d3SFlorian Westphal 			return ERR_PTR(-EINVAL);
2015302560bSColin Ian King 		hook_head = rcu_dereference(net->nf.hooks_ipv6[hook]);
202e2cf17d3SFlorian Westphal 		break;
203e2cf17d3SFlorian Westphal 	case NFPROTO_ARP:
204e2cf17d3SFlorian Westphal #ifdef CONFIG_NETFILTER_FAMILY_ARP
205e2cf17d3SFlorian Westphal 		if (hook >= ARRAY_SIZE(net->nf.hooks_arp))
206e2cf17d3SFlorian Westphal 			return ERR_PTR(-EINVAL);
207e2cf17d3SFlorian Westphal 		hook_head = rcu_dereference(net->nf.hooks_arp[hook]);
208e2cf17d3SFlorian Westphal #endif
209e2cf17d3SFlorian Westphal 		break;
210e2cf17d3SFlorian Westphal 	case NFPROTO_BRIDGE:
211e2cf17d3SFlorian Westphal #ifdef CONFIG_NETFILTER_FAMILY_BRIDGE
212e2cf17d3SFlorian Westphal 		if (hook >= ARRAY_SIZE(net->nf.hooks_bridge))
213e2cf17d3SFlorian Westphal 			return ERR_PTR(-EINVAL);
214e2cf17d3SFlorian Westphal 		hook_head = rcu_dereference(net->nf.hooks_bridge[hook]);
215e2cf17d3SFlorian Westphal #endif
216e2cf17d3SFlorian Westphal 		break;
217e2cf17d3SFlorian Westphal #if IS_ENABLED(CONFIG_DECNET)
218e2cf17d3SFlorian Westphal 	case NFPROTO_DECNET:
219e2cf17d3SFlorian Westphal 		if (hook >= ARRAY_SIZE(net->nf.hooks_decnet))
220e2cf17d3SFlorian Westphal 			return ERR_PTR(-EINVAL);
221e2cf17d3SFlorian Westphal 		hook_head = rcu_dereference(net->nf.hooks_decnet[hook]);
222e2cf17d3SFlorian Westphal 		break;
223e2cf17d3SFlorian Westphal #endif
224*42df6e1dSLukas Wunner #if defined(CONFIG_NETFILTER_INGRESS) || defined(CONFIG_NETFILTER_EGRESS)
225e2cf17d3SFlorian Westphal 	case NFPROTO_NETDEV:
226*42df6e1dSLukas Wunner 		if (hook >= NF_NETDEV_NUMHOOKS)
227e2cf17d3SFlorian Westphal 			return ERR_PTR(-EOPNOTSUPP);
228e2cf17d3SFlorian Westphal 
229e2cf17d3SFlorian Westphal 		if (!dev)
230e2cf17d3SFlorian Westphal 			return ERR_PTR(-ENODEV);
231e2cf17d3SFlorian Westphal 
232e2cf17d3SFlorian Westphal 		netdev = dev_get_by_name_rcu(net, dev);
233e2cf17d3SFlorian Westphal 		if (!netdev)
234e2cf17d3SFlorian Westphal 			return ERR_PTR(-ENODEV);
235e2cf17d3SFlorian Westphal 
236*42df6e1dSLukas Wunner #ifdef CONFIG_NETFILTER_INGRESS
237*42df6e1dSLukas Wunner 		if (hook == NF_NETDEV_INGRESS)
238e2cf17d3SFlorian Westphal 			return rcu_dereference(netdev->nf_hooks_ingress);
239e2cf17d3SFlorian Westphal #endif
240*42df6e1dSLukas Wunner #ifdef CONFIG_NETFILTER_EGRESS
241*42df6e1dSLukas Wunner 		if (hook == NF_NETDEV_EGRESS)
242*42df6e1dSLukas Wunner 			return rcu_dereference(netdev->nf_hooks_egress);
243*42df6e1dSLukas Wunner #endif
244*42df6e1dSLukas Wunner 		fallthrough;
245*42df6e1dSLukas Wunner #endif
246e2cf17d3SFlorian Westphal 	default:
247e2cf17d3SFlorian Westphal 		return ERR_PTR(-EPROTONOSUPPORT);
248e2cf17d3SFlorian Westphal 	}
249e2cf17d3SFlorian Westphal 
250e2cf17d3SFlorian Westphal 	return hook_head;
251e2cf17d3SFlorian Westphal }
252e2cf17d3SFlorian Westphal 
253e2cf17d3SFlorian Westphal static int nfnl_hook_dump(struct sk_buff *nlskb,
254e2cf17d3SFlorian Westphal 			  struct netlink_callback *cb)
255e2cf17d3SFlorian Westphal {
256e2cf17d3SFlorian Westphal 	struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
257e2cf17d3SFlorian Westphal 	struct nfnl_dump_hook_data *ctx = cb->data;
258e2cf17d3SFlorian Westphal 	int err, family = nfmsg->nfgen_family;
259e2cf17d3SFlorian Westphal 	struct net *net = sock_net(nlskb->sk);
260e2cf17d3SFlorian Westphal 	struct nf_hook_ops * const *ops;
261e2cf17d3SFlorian Westphal 	const struct nf_hook_entries *e;
262e2cf17d3SFlorian Westphal 	unsigned int i = cb->args[0];
263e2cf17d3SFlorian Westphal 
264e2cf17d3SFlorian Westphal 	rcu_read_lock();
265e2cf17d3SFlorian Westphal 
266e2cf17d3SFlorian Westphal 	e = nfnl_hook_entries_head(family, ctx->hook, net, ctx->devname);
267e2cf17d3SFlorian Westphal 	if (!e)
268e2cf17d3SFlorian Westphal 		goto done;
269e2cf17d3SFlorian Westphal 
270e2cf17d3SFlorian Westphal 	if (IS_ERR(e)) {
271e2cf17d3SFlorian Westphal 		cb->seq++;
272e2cf17d3SFlorian Westphal 		goto done;
273e2cf17d3SFlorian Westphal 	}
274e2cf17d3SFlorian Westphal 
275e2cf17d3SFlorian Westphal 	if ((unsigned long)e != ctx->headv || i >= e->num_hook_entries)
276e2cf17d3SFlorian Westphal 		cb->seq++;
277e2cf17d3SFlorian Westphal 
278e2cf17d3SFlorian Westphal 	ops = nf_hook_entries_get_hook_ops(e);
279e2cf17d3SFlorian Westphal 
280e2cf17d3SFlorian Westphal 	for (; i < e->num_hook_entries; i++) {
28169311e7cSPablo Neira Ayuso 		err = nfnl_hook_dump_one(nlskb, ctx, ops[i], family,
2823d9bbaf6SPablo Neira Ayuso 					 cb->nlh->nlmsg_seq);
283e2cf17d3SFlorian Westphal 		if (err)
284e2cf17d3SFlorian Westphal 			break;
285e2cf17d3SFlorian Westphal 	}
286e2cf17d3SFlorian Westphal 
287e2cf17d3SFlorian Westphal done:
288e2cf17d3SFlorian Westphal 	nl_dump_check_consistent(cb, nlmsg_hdr(nlskb));
289e2cf17d3SFlorian Westphal 	rcu_read_unlock();
290e2cf17d3SFlorian Westphal 	cb->args[0] = i;
291e2cf17d3SFlorian Westphal 	return nlskb->len;
292e2cf17d3SFlorian Westphal }
293e2cf17d3SFlorian Westphal 
294e2cf17d3SFlorian Westphal static int nfnl_hook_dump_start(struct netlink_callback *cb)
295e2cf17d3SFlorian Westphal {
296e2cf17d3SFlorian Westphal 	const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
297e2cf17d3SFlorian Westphal 	const struct nlattr * const *nla = cb->data;
298e2cf17d3SFlorian Westphal 	struct nfnl_dump_hook_data *ctx = NULL;
299e2cf17d3SFlorian Westphal 	struct net *net = sock_net(cb->skb->sk);
300e2cf17d3SFlorian Westphal 	u8 family = nfmsg->nfgen_family;
301e2cf17d3SFlorian Westphal 	char name[IFNAMSIZ] = "";
302e2cf17d3SFlorian Westphal 	const void *head;
303e2cf17d3SFlorian Westphal 	u32 hooknum;
304e2cf17d3SFlorian Westphal 
305e2cf17d3SFlorian Westphal 	hooknum = ntohl(nla_get_be32(nla[NFNLA_HOOK_HOOKNUM]));
306e2cf17d3SFlorian Westphal 	if (hooknum > 255)
307e2cf17d3SFlorian Westphal 		return -EINVAL;
308e2cf17d3SFlorian Westphal 
309e2cf17d3SFlorian Westphal 	if (family == NFPROTO_NETDEV) {
310e2cf17d3SFlorian Westphal 		if (!nla[NFNLA_HOOK_DEV])
311e2cf17d3SFlorian Westphal 			return -EINVAL;
312e2cf17d3SFlorian Westphal 
313e2cf17d3SFlorian Westphal 		nla_strscpy(name, nla[NFNLA_HOOK_DEV], sizeof(name));
314e2cf17d3SFlorian Westphal 	}
315e2cf17d3SFlorian Westphal 
316e2cf17d3SFlorian Westphal 	rcu_read_lock();
317e2cf17d3SFlorian Westphal 	/* Not dereferenced; for consistency check only */
318e2cf17d3SFlorian Westphal 	head = nfnl_hook_entries_head(family, hooknum, net, name);
319e2cf17d3SFlorian Westphal 	rcu_read_unlock();
320e2cf17d3SFlorian Westphal 
321e2cf17d3SFlorian Westphal 	if (head && IS_ERR(head))
322e2cf17d3SFlorian Westphal 		return PTR_ERR(head);
323e2cf17d3SFlorian Westphal 
324e2cf17d3SFlorian Westphal 	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
325e2cf17d3SFlorian Westphal 	if (!ctx)
326e2cf17d3SFlorian Westphal 		return -ENOMEM;
327e2cf17d3SFlorian Westphal 
328e2cf17d3SFlorian Westphal 	strscpy(ctx->devname, name, sizeof(ctx->devname));
329e2cf17d3SFlorian Westphal 	ctx->headv = (unsigned long)head;
330e2cf17d3SFlorian Westphal 	ctx->hook = hooknum;
331e2cf17d3SFlorian Westphal 
332e2cf17d3SFlorian Westphal 	cb->seq = 1;
333e2cf17d3SFlorian Westphal 	cb->data = ctx;
334e2cf17d3SFlorian Westphal 
335e2cf17d3SFlorian Westphal 	return 0;
336e2cf17d3SFlorian Westphal }
337e2cf17d3SFlorian Westphal 
338e2cf17d3SFlorian Westphal static int nfnl_hook_dump_stop(struct netlink_callback *cb)
339e2cf17d3SFlorian Westphal {
340e2cf17d3SFlorian Westphal 	kfree(cb->data);
341e2cf17d3SFlorian Westphal 	return 0;
342e2cf17d3SFlorian Westphal }
343e2cf17d3SFlorian Westphal 
344e2cf17d3SFlorian Westphal static int nfnl_hook_get(struct sk_buff *skb,
345e2cf17d3SFlorian Westphal 			 const struct nfnl_info *info,
346e2cf17d3SFlorian Westphal 			 const struct nlattr * const nla[])
347e2cf17d3SFlorian Westphal {
348e2cf17d3SFlorian Westphal 	if (!nla[NFNLA_HOOK_HOOKNUM])
349e2cf17d3SFlorian Westphal 		return -EINVAL;
350e2cf17d3SFlorian Westphal 
351e2cf17d3SFlorian Westphal 	if (info->nlh->nlmsg_flags & NLM_F_DUMP) {
352e2cf17d3SFlorian Westphal 		struct netlink_dump_control c = {
353e2cf17d3SFlorian Westphal 			.start = nfnl_hook_dump_start,
354e2cf17d3SFlorian Westphal 			.done = nfnl_hook_dump_stop,
355e2cf17d3SFlorian Westphal 			.dump = nfnl_hook_dump,
356e2cf17d3SFlorian Westphal 			.module = THIS_MODULE,
357e2cf17d3SFlorian Westphal 			.data = (void *)nla,
358e2cf17d3SFlorian Westphal 		};
359e2cf17d3SFlorian Westphal 
360e2cf17d3SFlorian Westphal 		return nf_netlink_dump_start_rcu(info->sk, skb, info->nlh, &c);
361e2cf17d3SFlorian Westphal 	}
362e2cf17d3SFlorian Westphal 
363e2cf17d3SFlorian Westphal 	return -EOPNOTSUPP;
364e2cf17d3SFlorian Westphal }
365e2cf17d3SFlorian Westphal 
366e2cf17d3SFlorian Westphal static const struct nfnl_callback nfnl_hook_cb[NFNL_MSG_HOOK_MAX] = {
367e2cf17d3SFlorian Westphal 	[NFNL_MSG_HOOK_GET] = {
368e2cf17d3SFlorian Westphal 		.call		= nfnl_hook_get,
369e2cf17d3SFlorian Westphal 		.type		= NFNL_CB_RCU,
370e2cf17d3SFlorian Westphal 		.attr_count	= NFNLA_HOOK_MAX,
371e2cf17d3SFlorian Westphal 		.policy		= nfnl_hook_nla_policy
372e2cf17d3SFlorian Westphal 	},
373e2cf17d3SFlorian Westphal };
374e2cf17d3SFlorian Westphal 
375e2cf17d3SFlorian Westphal static const struct nfnetlink_subsystem nfhook_subsys = {
376e2cf17d3SFlorian Westphal 	.name				= "nfhook",
377e2cf17d3SFlorian Westphal 	.subsys_id			= NFNL_SUBSYS_HOOK,
378e2cf17d3SFlorian Westphal 	.cb_count			= NFNL_MSG_HOOK_MAX,
379e2cf17d3SFlorian Westphal 	.cb				= nfnl_hook_cb,
380e2cf17d3SFlorian Westphal };
381e2cf17d3SFlorian Westphal 
382e2cf17d3SFlorian Westphal MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_HOOK);
383e2cf17d3SFlorian Westphal 
384e2cf17d3SFlorian Westphal static int __init nfnetlink_hook_init(void)
385e2cf17d3SFlorian Westphal {
386e2cf17d3SFlorian Westphal 	return nfnetlink_subsys_register(&nfhook_subsys);
387e2cf17d3SFlorian Westphal }
388e2cf17d3SFlorian Westphal 
389e2cf17d3SFlorian Westphal static void __exit nfnetlink_hook_exit(void)
390e2cf17d3SFlorian Westphal {
391e2cf17d3SFlorian Westphal 	nfnetlink_subsys_unregister(&nfhook_subsys);
392e2cf17d3SFlorian Westphal }
393e2cf17d3SFlorian Westphal 
394e2cf17d3SFlorian Westphal module_init(nfnetlink_hook_init);
395e2cf17d3SFlorian Westphal module_exit(nfnetlink_hook_exit);
396e2cf17d3SFlorian Westphal 
397e2cf17d3SFlorian Westphal MODULE_LICENSE("GPL");
398e2cf17d3SFlorian Westphal MODULE_AUTHOR("Florian Westphal <fw@strlen.de>");
399e2cf17d3SFlorian Westphal MODULE_DESCRIPTION("nfnetlink_hook: list registered netfilter hooks");
400