Linux Audio

Check our new training course

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