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 
8*506a74dbSFlorian Westphal #include <linux/bpf.h>
9e2cf17d3SFlorian Westphal #include <linux/module.h>
10b6459415SJakub Kicinski #include <linux/kallsyms.h>
11e2cf17d3SFlorian Westphal #include <linux/kernel.h>
12e2cf17d3SFlorian Westphal #include <linux/types.h>
13e2cf17d3SFlorian Westphal #include <linux/skbuff.h>
14e2cf17d3SFlorian Westphal #include <linux/errno.h>
15e2cf17d3SFlorian Westphal #include <linux/netlink.h>
16e2cf17d3SFlorian Westphal #include <linux/slab.h>
17e2cf17d3SFlorian Westphal 
18e2cf17d3SFlorian Westphal #include <linux/netfilter.h>
19e2cf17d3SFlorian Westphal 
20e2cf17d3SFlorian Westphal #include <linux/netfilter/nfnetlink.h>
21e2cf17d3SFlorian Westphal #include <linux/netfilter/nfnetlink_hook.h>
22e2cf17d3SFlorian Westphal 
23e2cf17d3SFlorian Westphal #include <net/netfilter/nf_tables.h>
24e2cf17d3SFlorian Westphal #include <net/sock.h>
25e2cf17d3SFlorian Westphal 
26e2cf17d3SFlorian Westphal static const struct nla_policy nfnl_hook_nla_policy[NFNLA_HOOK_MAX + 1] = {
27e2cf17d3SFlorian Westphal 	[NFNLA_HOOK_HOOKNUM]	= { .type = NLA_U32 },
28e2cf17d3SFlorian Westphal 	[NFNLA_HOOK_PRIORITY]	= { .type = NLA_U32 },
29e2cf17d3SFlorian Westphal 	[NFNLA_HOOK_DEV]	= { .type = NLA_STRING,
30e2cf17d3SFlorian Westphal 				    .len = IFNAMSIZ - 1 },
31e2cf17d3SFlorian Westphal 	[NFNLA_HOOK_FUNCTION_NAME] = { .type = NLA_NUL_STRING,
32e2cf17d3SFlorian Westphal 				       .len = KSYM_NAME_LEN, },
33e2cf17d3SFlorian Westphal 	[NFNLA_HOOK_MODULE_NAME] = { .type = NLA_NUL_STRING,
34e2cf17d3SFlorian Westphal 				     .len = MODULE_NAME_LEN, },
35e2cf17d3SFlorian Westphal 	[NFNLA_HOOK_CHAIN_INFO] = { .type = NLA_NESTED, },
36e2cf17d3SFlorian Westphal };
37e2cf17d3SFlorian Westphal 
nf_netlink_dump_start_rcu(struct sock * nlsk,struct sk_buff * skb,const struct nlmsghdr * nlh,struct netlink_dump_control * c)38e2cf17d3SFlorian Westphal static int nf_netlink_dump_start_rcu(struct sock *nlsk, struct sk_buff *skb,
39e2cf17d3SFlorian Westphal 				     const struct nlmsghdr *nlh,
40e2cf17d3SFlorian Westphal 				     struct netlink_dump_control *c)
41e2cf17d3SFlorian Westphal {
42e2cf17d3SFlorian Westphal 	int err;
43e2cf17d3SFlorian Westphal 
44e2cf17d3SFlorian Westphal 	if (!try_module_get(THIS_MODULE))
45e2cf17d3SFlorian Westphal 		return -EINVAL;
46e2cf17d3SFlorian Westphal 
47e2cf17d3SFlorian Westphal 	rcu_read_unlock();
48e2cf17d3SFlorian Westphal 	err = netlink_dump_start(nlsk, skb, nlh, c);
49e2cf17d3SFlorian Westphal 	rcu_read_lock();
50e2cf17d3SFlorian Westphal 	module_put(THIS_MODULE);
51e2cf17d3SFlorian Westphal 
52e2cf17d3SFlorian Westphal 	return err;
53e2cf17d3SFlorian Westphal }
54e2cf17d3SFlorian Westphal 
55e2cf17d3SFlorian Westphal struct nfnl_dump_hook_data {
56e2cf17d3SFlorian Westphal 	char devname[IFNAMSIZ];
57e2cf17d3SFlorian Westphal 	unsigned long headv;
58e2cf17d3SFlorian Westphal 	u8 hook;
59e2cf17d3SFlorian Westphal };
60e2cf17d3SFlorian Westphal 
nfnl_start_info_type(struct sk_buff * nlskb,enum nfnl_hook_chaintype t)61*506a74dbSFlorian Westphal static struct nlattr *nfnl_start_info_type(struct sk_buff *nlskb, enum nfnl_hook_chaintype t)
62*506a74dbSFlorian Westphal {
63*506a74dbSFlorian Westphal 	struct nlattr *nest = nla_nest_start(nlskb, NFNLA_HOOK_CHAIN_INFO);
64*506a74dbSFlorian Westphal 	int ret;
65*506a74dbSFlorian Westphal 
66*506a74dbSFlorian Westphal 	if (!nest)
67*506a74dbSFlorian Westphal 		return NULL;
68*506a74dbSFlorian Westphal 
69*506a74dbSFlorian Westphal 	ret = nla_put_be32(nlskb, NFNLA_HOOK_INFO_TYPE, htonl(t));
70*506a74dbSFlorian Westphal 	if (ret == 0)
71*506a74dbSFlorian Westphal 		return nest;
72*506a74dbSFlorian Westphal 
73*506a74dbSFlorian Westphal 	nla_nest_cancel(nlskb, nest);
74*506a74dbSFlorian Westphal 	return NULL;
75*506a74dbSFlorian Westphal }
76*506a74dbSFlorian Westphal 
nfnl_hook_put_bpf_prog_info(struct sk_buff * nlskb,const struct nfnl_dump_hook_data * ctx,unsigned int seq,const struct bpf_prog * prog)77*506a74dbSFlorian Westphal static int nfnl_hook_put_bpf_prog_info(struct sk_buff *nlskb,
78*506a74dbSFlorian Westphal 				       const struct nfnl_dump_hook_data *ctx,
79*506a74dbSFlorian Westphal 				       unsigned int seq,
80*506a74dbSFlorian Westphal 				       const struct bpf_prog *prog)
81*506a74dbSFlorian Westphal {
82*506a74dbSFlorian Westphal 	struct nlattr *nest, *nest2;
83*506a74dbSFlorian Westphal 	int ret;
84*506a74dbSFlorian Westphal 
85*506a74dbSFlorian Westphal 	if (!IS_ENABLED(CONFIG_NETFILTER_BPF_LINK))
86*506a74dbSFlorian Westphal 		return 0;
87*506a74dbSFlorian Westphal 
88*506a74dbSFlorian Westphal 	if (WARN_ON_ONCE(!prog))
89*506a74dbSFlorian Westphal 		return 0;
90*506a74dbSFlorian Westphal 
91*506a74dbSFlorian Westphal 	nest = nfnl_start_info_type(nlskb, NFNL_HOOK_TYPE_BPF);
92*506a74dbSFlorian Westphal 	if (!nest)
93*506a74dbSFlorian Westphal 		return -EMSGSIZE;
94*506a74dbSFlorian Westphal 
95*506a74dbSFlorian Westphal 	nest2 = nla_nest_start(nlskb, NFNLA_HOOK_INFO_DESC);
96*506a74dbSFlorian Westphal 	if (!nest2)
97*506a74dbSFlorian Westphal 		goto cancel_nest;
98*506a74dbSFlorian Westphal 
99*506a74dbSFlorian Westphal 	ret = nla_put_be32(nlskb, NFNLA_HOOK_BPF_ID, htonl(prog->aux->id));
100*506a74dbSFlorian Westphal 	if (ret)
101*506a74dbSFlorian Westphal 		goto cancel_nest;
102*506a74dbSFlorian Westphal 
103*506a74dbSFlorian Westphal 	nla_nest_end(nlskb, nest2);
104*506a74dbSFlorian Westphal 	nla_nest_end(nlskb, nest);
105*506a74dbSFlorian Westphal 	return 0;
106*506a74dbSFlorian Westphal 
107*506a74dbSFlorian Westphal cancel_nest:
108*506a74dbSFlorian Westphal 	nla_nest_cancel(nlskb, nest);
109*506a74dbSFlorian Westphal 	return -EMSGSIZE;
110*506a74dbSFlorian Westphal }
111*506a74dbSFlorian Westphal 
nfnl_hook_put_nft_chain_info(struct sk_buff * nlskb,const struct nfnl_dump_hook_data * ctx,unsigned int seq,struct nft_chain * chain)112e2cf17d3SFlorian Westphal static int nfnl_hook_put_nft_chain_info(struct sk_buff *nlskb,
113e2cf17d3SFlorian Westphal 					const struct nfnl_dump_hook_data *ctx,
114e2cf17d3SFlorian Westphal 					unsigned int seq,
115*506a74dbSFlorian Westphal 					struct nft_chain *chain)
116e2cf17d3SFlorian Westphal {
117e2cf17d3SFlorian Westphal 	struct net *net = sock_net(nlskb->sk);
118e2cf17d3SFlorian Westphal 	struct nlattr *nest, *nest2;
119e2cf17d3SFlorian Westphal 	int ret = 0;
120e2cf17d3SFlorian Westphal 
121e2cf17d3SFlorian Westphal 	if (WARN_ON_ONCE(!chain))
122e2cf17d3SFlorian Westphal 		return 0;
123e2cf17d3SFlorian Westphal 
124e2cf17d3SFlorian Westphal 	if (!nft_is_active(net, chain))
125e2cf17d3SFlorian Westphal 		return 0;
126e2cf17d3SFlorian Westphal 
127*506a74dbSFlorian Westphal 	nest = nfnl_start_info_type(nlskb, NFNL_HOOK_TYPE_NFTABLES);
128e2cf17d3SFlorian Westphal 	if (!nest)
129e2cf17d3SFlorian Westphal 		return -EMSGSIZE;
130e2cf17d3SFlorian Westphal 
131e2cf17d3SFlorian Westphal 	nest2 = nla_nest_start(nlskb, NFNLA_HOOK_INFO_DESC);
132e2cf17d3SFlorian Westphal 	if (!nest2)
133e2cf17d3SFlorian Westphal 		goto cancel_nest;
134e2cf17d3SFlorian Westphal 
135a6e57c4aSPablo Neira Ayuso 	ret = nla_put_string(nlskb, NFNLA_CHAIN_TABLE, chain->table->name);
136e2cf17d3SFlorian Westphal 	if (ret)
137e2cf17d3SFlorian Westphal 		goto cancel_nest;
138e2cf17d3SFlorian Westphal 
139a6e57c4aSPablo Neira Ayuso 	ret = nla_put_string(nlskb, NFNLA_CHAIN_NAME, chain->name);
140a6e57c4aSPablo Neira Ayuso 	if (ret)
141a6e57c4aSPablo Neira Ayuso 		goto cancel_nest;
142a6e57c4aSPablo Neira Ayuso 
143a6e57c4aSPablo Neira Ayuso 	ret = nla_put_u8(nlskb, NFNLA_CHAIN_FAMILY, chain->table->family);
144e2cf17d3SFlorian Westphal 	if (ret)
145e2cf17d3SFlorian Westphal 		goto cancel_nest;
146e2cf17d3SFlorian Westphal 
147e2cf17d3SFlorian Westphal 	nla_nest_end(nlskb, nest2);
148e2cf17d3SFlorian Westphal 	nla_nest_end(nlskb, nest);
149e2cf17d3SFlorian Westphal 	return ret;
150e2cf17d3SFlorian Westphal 
151e2cf17d3SFlorian Westphal cancel_nest:
152e2cf17d3SFlorian Westphal 	nla_nest_cancel(nlskb, nest);
153e2cf17d3SFlorian Westphal 	return -EMSGSIZE;
154e2cf17d3SFlorian Westphal }
155e2cf17d3SFlorian Westphal 
nfnl_hook_dump_one(struct sk_buff * nlskb,const struct nfnl_dump_hook_data * ctx,const struct nf_hook_ops * ops,int family,unsigned int seq)156e2cf17d3SFlorian Westphal static int nfnl_hook_dump_one(struct sk_buff *nlskb,
157e2cf17d3SFlorian Westphal 			      const struct nfnl_dump_hook_data *ctx,
158e2cf17d3SFlorian Westphal 			      const struct nf_hook_ops *ops,
15969311e7cSPablo Neira Ayuso 			      int family, unsigned int seq)
160e2cf17d3SFlorian Westphal {
161e2cf17d3SFlorian Westphal 	u16 event = nfnl_msg_type(NFNL_SUBSYS_HOOK, NFNL_MSG_HOOK_GET);
162e2cf17d3SFlorian Westphal 	unsigned int portid = NETLINK_CB(nlskb).portid;
163e2cf17d3SFlorian Westphal 	struct nlmsghdr *nlh;
164e2cf17d3SFlorian Westphal 	int ret = -EMSGSIZE;
165269fc695SPablo Neira Ayuso 	u32 hooknum;
166e2cf17d3SFlorian Westphal #ifdef CONFIG_KALLSYMS
167e2cf17d3SFlorian Westphal 	char sym[KSYM_SYMBOL_LEN];
168e2cf17d3SFlorian Westphal 	char *module_name;
169e2cf17d3SFlorian Westphal #endif
170e2cf17d3SFlorian Westphal 	nlh = nfnl_msg_put(nlskb, portid, seq, event,
17169311e7cSPablo Neira Ayuso 			   NLM_F_MULTI, family, NFNETLINK_V0, 0);
172e2cf17d3SFlorian Westphal 	if (!nlh)
173e2cf17d3SFlorian Westphal 		goto nla_put_failure;
174e2cf17d3SFlorian Westphal 
175e2cf17d3SFlorian Westphal #ifdef CONFIG_KALLSYMS
176e2cf17d3SFlorian Westphal 	ret = snprintf(sym, sizeof(sym), "%ps", ops->hook);
17724610ed8SDan Carpenter 	if (ret >= sizeof(sym)) {
17824610ed8SDan Carpenter 		ret = -EINVAL;
179e2cf17d3SFlorian Westphal 		goto nla_put_failure;
18024610ed8SDan Carpenter 	}
181e2cf17d3SFlorian Westphal 
182e2cf17d3SFlorian Westphal 	module_name = strstr(sym, " [");
183e2cf17d3SFlorian Westphal 	if (module_name) {
184e2cf17d3SFlorian Westphal 		char *end;
185e2cf17d3SFlorian Westphal 
18661e0c2bcSPablo Neira Ayuso 		*module_name = '\0';
187e2cf17d3SFlorian Westphal 		module_name += 2;
188e2cf17d3SFlorian Westphal 		end = strchr(module_name, ']');
189e2cf17d3SFlorian Westphal 		if (end) {
190e2cf17d3SFlorian Westphal 			*end = 0;
191e2cf17d3SFlorian Westphal 
192e2cf17d3SFlorian Westphal 			ret = nla_put_string(nlskb, NFNLA_HOOK_MODULE_NAME, module_name);
193e2cf17d3SFlorian Westphal 			if (ret)
194e2cf17d3SFlorian Westphal 				goto nla_put_failure;
195e2cf17d3SFlorian Westphal 		}
196e2cf17d3SFlorian Westphal 	}
197e2cf17d3SFlorian Westphal 
198e2cf17d3SFlorian Westphal 	ret = nla_put_string(nlskb, NFNLA_HOOK_FUNCTION_NAME, sym);
199e2cf17d3SFlorian Westphal 	if (ret)
200e2cf17d3SFlorian Westphal 		goto nla_put_failure;
201e2cf17d3SFlorian Westphal #endif
202e2cf17d3SFlorian Westphal 
203269fc695SPablo Neira Ayuso 	if (ops->pf == NFPROTO_INET && ops->hooknum == NF_INET_INGRESS)
204269fc695SPablo Neira Ayuso 		hooknum = NF_NETDEV_INGRESS;
205269fc695SPablo Neira Ayuso 	else
206269fc695SPablo Neira Ayuso 		hooknum = ops->hooknum;
207269fc695SPablo Neira Ayuso 
208269fc695SPablo Neira Ayuso 	ret = nla_put_be32(nlskb, NFNLA_HOOK_HOOKNUM, htonl(hooknum));
209e2cf17d3SFlorian Westphal 	if (ret)
210e2cf17d3SFlorian Westphal 		goto nla_put_failure;
211e2cf17d3SFlorian Westphal 
212e2cf17d3SFlorian Westphal 	ret = nla_put_be32(nlskb, NFNLA_HOOK_PRIORITY, htonl(ops->priority));
213e2cf17d3SFlorian Westphal 	if (ret)
214e2cf17d3SFlorian Westphal 		goto nla_put_failure;
215e2cf17d3SFlorian Westphal 
216*506a74dbSFlorian Westphal 	switch (ops->hook_ops_type) {
217*506a74dbSFlorian Westphal 	case NF_HOOK_OP_NF_TABLES:
218*506a74dbSFlorian Westphal 		ret = nfnl_hook_put_nft_chain_info(nlskb, ctx, seq, ops->priv);
219*506a74dbSFlorian Westphal 		break;
220*506a74dbSFlorian Westphal 	case NF_HOOK_OP_BPF:
221*506a74dbSFlorian Westphal 		ret = nfnl_hook_put_bpf_prog_info(nlskb, ctx, seq, ops->priv);
222*506a74dbSFlorian Westphal 		break;
223*506a74dbSFlorian Westphal 	case NF_HOOK_OP_UNDEFINED:
224*506a74dbSFlorian Westphal 		break;
225*506a74dbSFlorian Westphal 	default:
226*506a74dbSFlorian Westphal 		WARN_ON_ONCE(1);
227*506a74dbSFlorian Westphal 		break;
228*506a74dbSFlorian Westphal 	}
229*506a74dbSFlorian Westphal 
230e2cf17d3SFlorian Westphal 	if (ret)
231e2cf17d3SFlorian Westphal 		goto nla_put_failure;
232e2cf17d3SFlorian Westphal 
233e2cf17d3SFlorian Westphal 	nlmsg_end(nlskb, nlh);
234e2cf17d3SFlorian Westphal 	return 0;
235e2cf17d3SFlorian Westphal nla_put_failure:
236e2cf17d3SFlorian Westphal 	nlmsg_trim(nlskb, nlh);
237e2cf17d3SFlorian Westphal 	return ret;
238e2cf17d3SFlorian Westphal }
239e2cf17d3SFlorian Westphal 
240e2cf17d3SFlorian Westphal static const struct nf_hook_entries *
nfnl_hook_entries_head(u8 pf,unsigned int hook,struct net * net,const char * dev)241e2cf17d3SFlorian Westphal nfnl_hook_entries_head(u8 pf, unsigned int hook, struct net *net, const char *dev)
242e2cf17d3SFlorian Westphal {
243e2cf17d3SFlorian Westphal 	const struct nf_hook_entries *hook_head = NULL;
24442df6e1dSLukas Wunner #if defined(CONFIG_NETFILTER_INGRESS) || defined(CONFIG_NETFILTER_EGRESS)
245e2cf17d3SFlorian Westphal 	struct net_device *netdev;
246217e26bdSArnd Bergmann #endif
247e2cf17d3SFlorian Westphal 
248e2cf17d3SFlorian Westphal 	switch (pf) {
249e2cf17d3SFlorian Westphal 	case NFPROTO_IPV4:
250e2cf17d3SFlorian Westphal 		if (hook >= ARRAY_SIZE(net->nf.hooks_ipv4))
251e2cf17d3SFlorian Westphal 			return ERR_PTR(-EINVAL);
252e2cf17d3SFlorian Westphal 		hook_head = rcu_dereference(net->nf.hooks_ipv4[hook]);
253e2cf17d3SFlorian Westphal 		break;
254e2cf17d3SFlorian Westphal 	case NFPROTO_IPV6:
255e2cf17d3SFlorian Westphal 		if (hook >= ARRAY_SIZE(net->nf.hooks_ipv6))
256e2cf17d3SFlorian Westphal 			return ERR_PTR(-EINVAL);
2575302560bSColin Ian King 		hook_head = rcu_dereference(net->nf.hooks_ipv6[hook]);
258e2cf17d3SFlorian Westphal 		break;
259e2cf17d3SFlorian Westphal 	case NFPROTO_ARP:
260e2cf17d3SFlorian Westphal #ifdef CONFIG_NETFILTER_FAMILY_ARP
261e2cf17d3SFlorian Westphal 		if (hook >= ARRAY_SIZE(net->nf.hooks_arp))
262e2cf17d3SFlorian Westphal 			return ERR_PTR(-EINVAL);
263e2cf17d3SFlorian Westphal 		hook_head = rcu_dereference(net->nf.hooks_arp[hook]);
264e2cf17d3SFlorian Westphal #endif
265e2cf17d3SFlorian Westphal 		break;
266e2cf17d3SFlorian Westphal 	case NFPROTO_BRIDGE:
267e2cf17d3SFlorian Westphal #ifdef CONFIG_NETFILTER_FAMILY_BRIDGE
268e2cf17d3SFlorian Westphal 		if (hook >= ARRAY_SIZE(net->nf.hooks_bridge))
269e2cf17d3SFlorian Westphal 			return ERR_PTR(-EINVAL);
270e2cf17d3SFlorian Westphal 		hook_head = rcu_dereference(net->nf.hooks_bridge[hook]);
271e2cf17d3SFlorian Westphal #endif
272e2cf17d3SFlorian Westphal 		break;
27342df6e1dSLukas Wunner #if defined(CONFIG_NETFILTER_INGRESS) || defined(CONFIG_NETFILTER_EGRESS)
274e2cf17d3SFlorian Westphal 	case NFPROTO_NETDEV:
27542df6e1dSLukas Wunner 		if (hook >= NF_NETDEV_NUMHOOKS)
276e2cf17d3SFlorian Westphal 			return ERR_PTR(-EOPNOTSUPP);
277e2cf17d3SFlorian Westphal 
278e2cf17d3SFlorian Westphal 		if (!dev)
279e2cf17d3SFlorian Westphal 			return ERR_PTR(-ENODEV);
280e2cf17d3SFlorian Westphal 
281e2cf17d3SFlorian Westphal 		netdev = dev_get_by_name_rcu(net, dev);
282e2cf17d3SFlorian Westphal 		if (!netdev)
283e2cf17d3SFlorian Westphal 			return ERR_PTR(-ENODEV);
284e2cf17d3SFlorian Westphal 
28542df6e1dSLukas Wunner #ifdef CONFIG_NETFILTER_INGRESS
28642df6e1dSLukas Wunner 		if (hook == NF_NETDEV_INGRESS)
287e2cf17d3SFlorian Westphal 			return rcu_dereference(netdev->nf_hooks_ingress);
288e2cf17d3SFlorian Westphal #endif
28942df6e1dSLukas Wunner #ifdef CONFIG_NETFILTER_EGRESS
29042df6e1dSLukas Wunner 		if (hook == NF_NETDEV_EGRESS)
29142df6e1dSLukas Wunner 			return rcu_dereference(netdev->nf_hooks_egress);
29242df6e1dSLukas Wunner #endif
29342df6e1dSLukas Wunner 		fallthrough;
29442df6e1dSLukas Wunner #endif
295e2cf17d3SFlorian Westphal 	default:
296e2cf17d3SFlorian Westphal 		return ERR_PTR(-EPROTONOSUPPORT);
297e2cf17d3SFlorian Westphal 	}
298e2cf17d3SFlorian Westphal 
299e2cf17d3SFlorian Westphal 	return hook_head;
300e2cf17d3SFlorian Westphal }
301e2cf17d3SFlorian Westphal 
nfnl_hook_dump(struct sk_buff * nlskb,struct netlink_callback * cb)302e2cf17d3SFlorian Westphal static int nfnl_hook_dump(struct sk_buff *nlskb,
303e2cf17d3SFlorian Westphal 			  struct netlink_callback *cb)
304e2cf17d3SFlorian Westphal {
305e2cf17d3SFlorian Westphal 	struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
306e2cf17d3SFlorian Westphal 	struct nfnl_dump_hook_data *ctx = cb->data;
307e2cf17d3SFlorian Westphal 	int err, family = nfmsg->nfgen_family;
308e2cf17d3SFlorian Westphal 	struct net *net = sock_net(nlskb->sk);
309e2cf17d3SFlorian Westphal 	struct nf_hook_ops * const *ops;
310e2cf17d3SFlorian Westphal 	const struct nf_hook_entries *e;
311e2cf17d3SFlorian Westphal 	unsigned int i = cb->args[0];
312e2cf17d3SFlorian Westphal 
313e2cf17d3SFlorian Westphal 	rcu_read_lock();
314e2cf17d3SFlorian Westphal 
315e2cf17d3SFlorian Westphal 	e = nfnl_hook_entries_head(family, ctx->hook, net, ctx->devname);
316e2cf17d3SFlorian Westphal 	if (!e)
317e2cf17d3SFlorian Westphal 		goto done;
318e2cf17d3SFlorian Westphal 
319e2cf17d3SFlorian Westphal 	if (IS_ERR(e)) {
320e2cf17d3SFlorian Westphal 		cb->seq++;
321e2cf17d3SFlorian Westphal 		goto done;
322e2cf17d3SFlorian Westphal 	}
323e2cf17d3SFlorian Westphal 
324e2cf17d3SFlorian Westphal 	if ((unsigned long)e != ctx->headv || i >= e->num_hook_entries)
325e2cf17d3SFlorian Westphal 		cb->seq++;
326e2cf17d3SFlorian Westphal 
327e2cf17d3SFlorian Westphal 	ops = nf_hook_entries_get_hook_ops(e);
328e2cf17d3SFlorian Westphal 
329e2cf17d3SFlorian Westphal 	for (; i < e->num_hook_entries; i++) {
33069311e7cSPablo Neira Ayuso 		err = nfnl_hook_dump_one(nlskb, ctx, ops[i], family,
3313d9bbaf6SPablo Neira Ayuso 					 cb->nlh->nlmsg_seq);
332e2cf17d3SFlorian Westphal 		if (err)
333e2cf17d3SFlorian Westphal 			break;
334e2cf17d3SFlorian Westphal 	}
335e2cf17d3SFlorian Westphal 
336e2cf17d3SFlorian Westphal done:
337e2cf17d3SFlorian Westphal 	nl_dump_check_consistent(cb, nlmsg_hdr(nlskb));
338e2cf17d3SFlorian Westphal 	rcu_read_unlock();
339e2cf17d3SFlorian Westphal 	cb->args[0] = i;
340e2cf17d3SFlorian Westphal 	return nlskb->len;
341e2cf17d3SFlorian Westphal }
342e2cf17d3SFlorian Westphal 
nfnl_hook_dump_start(struct netlink_callback * cb)343e2cf17d3SFlorian Westphal static int nfnl_hook_dump_start(struct netlink_callback *cb)
344e2cf17d3SFlorian Westphal {
345e2cf17d3SFlorian Westphal 	const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
346e2cf17d3SFlorian Westphal 	const struct nlattr * const *nla = cb->data;
347e2cf17d3SFlorian Westphal 	struct nfnl_dump_hook_data *ctx = NULL;
348e2cf17d3SFlorian Westphal 	struct net *net = sock_net(cb->skb->sk);
349e2cf17d3SFlorian Westphal 	u8 family = nfmsg->nfgen_family;
350e2cf17d3SFlorian Westphal 	char name[IFNAMSIZ] = "";
351e2cf17d3SFlorian Westphal 	const void *head;
352e2cf17d3SFlorian Westphal 	u32 hooknum;
353e2cf17d3SFlorian Westphal 
354e2cf17d3SFlorian Westphal 	hooknum = ntohl(nla_get_be32(nla[NFNLA_HOOK_HOOKNUM]));
355e2cf17d3SFlorian Westphal 	if (hooknum > 255)
356e2cf17d3SFlorian Westphal 		return -EINVAL;
357e2cf17d3SFlorian Westphal 
358e2cf17d3SFlorian Westphal 	if (family == NFPROTO_NETDEV) {
359e2cf17d3SFlorian Westphal 		if (!nla[NFNLA_HOOK_DEV])
360e2cf17d3SFlorian Westphal 			return -EINVAL;
361e2cf17d3SFlorian Westphal 
362e2cf17d3SFlorian Westphal 		nla_strscpy(name, nla[NFNLA_HOOK_DEV], sizeof(name));
363e2cf17d3SFlorian Westphal 	}
364e2cf17d3SFlorian Westphal 
365e2cf17d3SFlorian Westphal 	rcu_read_lock();
366e2cf17d3SFlorian Westphal 	/* Not dereferenced; for consistency check only */
367e2cf17d3SFlorian Westphal 	head = nfnl_hook_entries_head(family, hooknum, net, name);
368e2cf17d3SFlorian Westphal 	rcu_read_unlock();
369e2cf17d3SFlorian Westphal 
370e2cf17d3SFlorian Westphal 	if (head && IS_ERR(head))
371e2cf17d3SFlorian Westphal 		return PTR_ERR(head);
372e2cf17d3SFlorian Westphal 
373e2cf17d3SFlorian Westphal 	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
374e2cf17d3SFlorian Westphal 	if (!ctx)
375e2cf17d3SFlorian Westphal 		return -ENOMEM;
376e2cf17d3SFlorian Westphal 
377e2cf17d3SFlorian Westphal 	strscpy(ctx->devname, name, sizeof(ctx->devname));
378e2cf17d3SFlorian Westphal 	ctx->headv = (unsigned long)head;
379e2cf17d3SFlorian Westphal 	ctx->hook = hooknum;
380e2cf17d3SFlorian Westphal 
381e2cf17d3SFlorian Westphal 	cb->seq = 1;
382e2cf17d3SFlorian Westphal 	cb->data = ctx;
383e2cf17d3SFlorian Westphal 
384e2cf17d3SFlorian Westphal 	return 0;
385e2cf17d3SFlorian Westphal }
386e2cf17d3SFlorian Westphal 
nfnl_hook_dump_stop(struct netlink_callback * cb)387e2cf17d3SFlorian Westphal static int nfnl_hook_dump_stop(struct netlink_callback *cb)
388e2cf17d3SFlorian Westphal {
389e2cf17d3SFlorian Westphal 	kfree(cb->data);
390e2cf17d3SFlorian Westphal 	return 0;
391e2cf17d3SFlorian Westphal }
392e2cf17d3SFlorian Westphal 
nfnl_hook_get(struct sk_buff * skb,const struct nfnl_info * info,const struct nlattr * const nla[])393e2cf17d3SFlorian Westphal static int nfnl_hook_get(struct sk_buff *skb,
394e2cf17d3SFlorian Westphal 			 const struct nfnl_info *info,
395e2cf17d3SFlorian Westphal 			 const struct nlattr * const nla[])
396e2cf17d3SFlorian Westphal {
397e2cf17d3SFlorian Westphal 	if (!nla[NFNLA_HOOK_HOOKNUM])
398e2cf17d3SFlorian Westphal 		return -EINVAL;
399e2cf17d3SFlorian Westphal 
400e2cf17d3SFlorian Westphal 	if (info->nlh->nlmsg_flags & NLM_F_DUMP) {
401e2cf17d3SFlorian Westphal 		struct netlink_dump_control c = {
402e2cf17d3SFlorian Westphal 			.start = nfnl_hook_dump_start,
403e2cf17d3SFlorian Westphal 			.done = nfnl_hook_dump_stop,
404e2cf17d3SFlorian Westphal 			.dump = nfnl_hook_dump,
405e2cf17d3SFlorian Westphal 			.module = THIS_MODULE,
406e2cf17d3SFlorian Westphal 			.data = (void *)nla,
407e2cf17d3SFlorian Westphal 		};
408e2cf17d3SFlorian Westphal 
409e2cf17d3SFlorian Westphal 		return nf_netlink_dump_start_rcu(info->sk, skb, info->nlh, &c);
410e2cf17d3SFlorian Westphal 	}
411e2cf17d3SFlorian Westphal 
412e2cf17d3SFlorian Westphal 	return -EOPNOTSUPP;
413e2cf17d3SFlorian Westphal }
414e2cf17d3SFlorian Westphal 
415e2cf17d3SFlorian Westphal static const struct nfnl_callback nfnl_hook_cb[NFNL_MSG_HOOK_MAX] = {
416e2cf17d3SFlorian Westphal 	[NFNL_MSG_HOOK_GET] = {
417e2cf17d3SFlorian Westphal 		.call		= nfnl_hook_get,
418e2cf17d3SFlorian Westphal 		.type		= NFNL_CB_RCU,
419e2cf17d3SFlorian Westphal 		.attr_count	= NFNLA_HOOK_MAX,
420e2cf17d3SFlorian Westphal 		.policy		= nfnl_hook_nla_policy
421e2cf17d3SFlorian Westphal 	},
422e2cf17d3SFlorian Westphal };
423e2cf17d3SFlorian Westphal 
424e2cf17d3SFlorian Westphal static const struct nfnetlink_subsystem nfhook_subsys = {
425e2cf17d3SFlorian Westphal 	.name				= "nfhook",
426e2cf17d3SFlorian Westphal 	.subsys_id			= NFNL_SUBSYS_HOOK,
427e2cf17d3SFlorian Westphal 	.cb_count			= NFNL_MSG_HOOK_MAX,
428e2cf17d3SFlorian Westphal 	.cb				= nfnl_hook_cb,
429e2cf17d3SFlorian Westphal };
430e2cf17d3SFlorian Westphal 
431e2cf17d3SFlorian Westphal MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_HOOK);
432e2cf17d3SFlorian Westphal 
nfnetlink_hook_init(void)433e2cf17d3SFlorian Westphal static int __init nfnetlink_hook_init(void)
434e2cf17d3SFlorian Westphal {
435e2cf17d3SFlorian Westphal 	return nfnetlink_subsys_register(&nfhook_subsys);
436e2cf17d3SFlorian Westphal }
437e2cf17d3SFlorian Westphal 
nfnetlink_hook_exit(void)438e2cf17d3SFlorian Westphal static void __exit nfnetlink_hook_exit(void)
439e2cf17d3SFlorian Westphal {
440e2cf17d3SFlorian Westphal 	nfnetlink_subsys_unregister(&nfhook_subsys);
441e2cf17d3SFlorian Westphal }
442e2cf17d3SFlorian Westphal 
443e2cf17d3SFlorian Westphal module_init(nfnetlink_hook_init);
444e2cf17d3SFlorian Westphal module_exit(nfnetlink_hook_exit);
445e2cf17d3SFlorian Westphal 
446e2cf17d3SFlorian Westphal MODULE_LICENSE("GPL");
447e2cf17d3SFlorian Westphal MODULE_AUTHOR("Florian Westphal <fw@strlen.de>");
448e2cf17d3SFlorian Westphal MODULE_DESCRIPTION("nfnetlink_hook: list registered netfilter hooks");
449