1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (c) 2021 Red Hat GmbH
5 * Author: Florian Westphal <fw@strlen.de>
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>
16 #include <linux/netfilter.h>
18 #include <linux/netfilter/nfnetlink.h>
19 #include <linux/netfilter/nfnetlink_hook.h>
21 #include <net/netfilter/nf_tables.h>
24 static 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, },
36 static int nf_netlink_dump_start_rcu(struct sock *nlsk, struct sk_buff *skb,
37 const struct nlmsghdr *nlh,
38 struct netlink_dump_control *c)
42 if (!try_module_get(THIS_MODULE))
46 err = netlink_dump_start(nlsk, skb, nlh, c);
48 module_put(THIS_MODULE);
53 struct nfnl_dump_hook_data {
54 char devname[IFNAMSIZ];
59 static int nfnl_hook_put_nft_chain_info(struct sk_buff *nlskb,
60 const struct nfnl_dump_hook_data *ctx,
62 const struct nf_hook_ops *ops)
64 struct net *net = sock_net(nlskb->sk);
65 struct nlattr *nest, *nest2;
66 struct nft_chain *chain;
69 if (ops->hook_ops_type != NF_HOOK_OP_NF_TABLES)
73 if (WARN_ON_ONCE(!chain))
76 if (!nft_is_active(net, chain))
79 nest = nla_nest_start(nlskb, NFNLA_HOOK_CHAIN_INFO);
83 ret = nla_put_be32(nlskb, NFNLA_HOOK_INFO_TYPE,
84 htonl(NFNL_HOOK_TYPE_NFTABLES));
88 nest2 = nla_nest_start(nlskb, NFNLA_HOOK_INFO_DESC);
92 ret = nla_put_string(nlskb, NFTA_CHAIN_TABLE, chain->table->name);
96 ret = nla_put_string(nlskb, NFTA_CHAIN_NAME, chain->name);
100 nla_nest_end(nlskb, nest2);
101 nla_nest_end(nlskb, nest);
105 nla_nest_cancel(nlskb, nest);
109 static int nfnl_hook_dump_one(struct sk_buff *nlskb,
110 const struct nfnl_dump_hook_data *ctx,
111 const struct nf_hook_ops *ops,
114 u16 event = nfnl_msg_type(NFNL_SUBSYS_HOOK, NFNL_MSG_HOOK_GET);
115 unsigned int portid = NETLINK_CB(nlskb).portid;
116 struct nlmsghdr *nlh;
118 #ifdef CONFIG_KALLSYMS
119 char sym[KSYM_SYMBOL_LEN];
122 nlh = nfnl_msg_put(nlskb, portid, seq, event,
123 NLM_F_MULTI, ops->pf, NFNETLINK_V0, 0);
125 goto nla_put_failure;
127 #ifdef CONFIG_KALLSYMS
128 ret = snprintf(sym, sizeof(sym), "%ps", ops->hook);
129 if (ret >= sizeof(sym)) {
131 goto nla_put_failure;
134 module_name = strstr(sym, " [");
139 end = strchr(module_name, ']');
143 ret = nla_put_string(nlskb, NFNLA_HOOK_MODULE_NAME, module_name);
145 goto nla_put_failure;
149 ret = nla_put_string(nlskb, NFNLA_HOOK_FUNCTION_NAME, sym);
151 goto nla_put_failure;
154 ret = nla_put_be32(nlskb, NFNLA_HOOK_HOOKNUM, htonl(ops->hooknum));
156 goto nla_put_failure;
158 ret = nla_put_be32(nlskb, NFNLA_HOOK_PRIORITY, htonl(ops->priority));
160 goto nla_put_failure;
162 ret = nfnl_hook_put_nft_chain_info(nlskb, ctx, seq, ops);
164 goto nla_put_failure;
166 nlmsg_end(nlskb, nlh);
169 nlmsg_trim(nlskb, nlh);
173 static const struct nf_hook_entries *
174 nfnl_hook_entries_head(u8 pf, unsigned int hook, struct net *net, const char *dev)
176 const struct nf_hook_entries *hook_head = NULL;
177 struct net_device *netdev;
181 if (hook >= ARRAY_SIZE(net->nf.hooks_ipv4))
182 return ERR_PTR(-EINVAL);
183 hook_head = rcu_dereference(net->nf.hooks_ipv4[hook]);
186 if (hook >= ARRAY_SIZE(net->nf.hooks_ipv6))
187 return ERR_PTR(-EINVAL);
188 hook_head = rcu_dereference(net->nf.hooks_ipv6[hook]);
191 #ifdef CONFIG_NETFILTER_FAMILY_ARP
192 if (hook >= ARRAY_SIZE(net->nf.hooks_arp))
193 return ERR_PTR(-EINVAL);
194 hook_head = rcu_dereference(net->nf.hooks_arp[hook]);
198 #ifdef CONFIG_NETFILTER_FAMILY_BRIDGE
199 if (hook >= ARRAY_SIZE(net->nf.hooks_bridge))
200 return ERR_PTR(-EINVAL);
201 hook_head = rcu_dereference(net->nf.hooks_bridge[hook]);
204 #if IS_ENABLED(CONFIG_DECNET)
206 if (hook >= ARRAY_SIZE(net->nf.hooks_decnet))
207 return ERR_PTR(-EINVAL);
208 hook_head = rcu_dereference(net->nf.hooks_decnet[hook]);
211 #ifdef CONFIG_NETFILTER_INGRESS
213 if (hook != NF_NETDEV_INGRESS)
214 return ERR_PTR(-EOPNOTSUPP);
217 return ERR_PTR(-ENODEV);
219 netdev = dev_get_by_name_rcu(net, dev);
221 return ERR_PTR(-ENODEV);
223 return rcu_dereference(netdev->nf_hooks_ingress);
226 return ERR_PTR(-EPROTONOSUPPORT);
232 static int nfnl_hook_dump(struct sk_buff *nlskb,
233 struct netlink_callback *cb)
235 struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
236 struct nfnl_dump_hook_data *ctx = cb->data;
237 int err, family = nfmsg->nfgen_family;
238 struct net *net = sock_net(nlskb->sk);
239 struct nf_hook_ops * const *ops;
240 const struct nf_hook_entries *e;
241 unsigned int i = cb->args[0];
245 e = nfnl_hook_entries_head(family, ctx->hook, net, ctx->devname);
254 if ((unsigned long)e != ctx->headv || i >= e->num_hook_entries)
257 ops = nf_hook_entries_get_hook_ops(e);
259 for (; i < e->num_hook_entries; i++) {
260 err = nfnl_hook_dump_one(nlskb, ctx, ops[i], cb->seq);
266 nl_dump_check_consistent(cb, nlmsg_hdr(nlskb));
272 static int nfnl_hook_dump_start(struct netlink_callback *cb)
274 const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
275 const struct nlattr * const *nla = cb->data;
276 struct nfnl_dump_hook_data *ctx = NULL;
277 struct net *net = sock_net(cb->skb->sk);
278 u8 family = nfmsg->nfgen_family;
279 char name[IFNAMSIZ] = "";
283 hooknum = ntohl(nla_get_be32(nla[NFNLA_HOOK_HOOKNUM]));
287 if (family == NFPROTO_NETDEV) {
288 if (!nla[NFNLA_HOOK_DEV])
291 nla_strscpy(name, nla[NFNLA_HOOK_DEV], sizeof(name));
295 /* Not dereferenced; for consistency check only */
296 head = nfnl_hook_entries_head(family, hooknum, net, name);
299 if (head && IS_ERR(head))
300 return PTR_ERR(head);
302 ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
306 strscpy(ctx->devname, name, sizeof(ctx->devname));
307 ctx->headv = (unsigned long)head;
316 static int nfnl_hook_dump_stop(struct netlink_callback *cb)
322 static int nfnl_hook_get(struct sk_buff *skb,
323 const struct nfnl_info *info,
324 const struct nlattr * const nla[])
326 if (!nla[NFNLA_HOOK_HOOKNUM])
329 if (info->nlh->nlmsg_flags & NLM_F_DUMP) {
330 struct netlink_dump_control c = {
331 .start = nfnl_hook_dump_start,
332 .done = nfnl_hook_dump_stop,
333 .dump = nfnl_hook_dump,
334 .module = THIS_MODULE,
338 return nf_netlink_dump_start_rcu(info->sk, skb, info->nlh, &c);
344 static const struct nfnl_callback nfnl_hook_cb[NFNL_MSG_HOOK_MAX] = {
345 [NFNL_MSG_HOOK_GET] = {
346 .call = nfnl_hook_get,
348 .attr_count = NFNLA_HOOK_MAX,
349 .policy = nfnl_hook_nla_policy
353 static const struct nfnetlink_subsystem nfhook_subsys = {
355 .subsys_id = NFNL_SUBSYS_HOOK,
356 .cb_count = NFNL_MSG_HOOK_MAX,
360 MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_HOOK);
362 static int __init nfnetlink_hook_init(void)
364 return nfnetlink_subsys_register(&nfhook_subsys);
367 static void __exit nfnetlink_hook_exit(void)
369 nfnetlink_subsys_unregister(&nfhook_subsys);
372 module_init(nfnetlink_hook_init);
373 module_exit(nfnetlink_hook_exit);
375 MODULE_LICENSE("GPL");
376 MODULE_AUTHOR("Florian Westphal <fw@strlen.de>");
377 MODULE_DESCRIPTION("nfnetlink_hook: list registered netfilter hooks");