1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (c) 2021 Red Hat GmbH
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>
18 #include <linux/netfilter.h>
20 #include <linux/netfilter/nfnetlink.h>
21 #include <linux/netfilter/nfnetlink_hook.h>
23 #include <net/netfilter/nf_tables.h>
26 static 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, },
38 static int nf_netlink_dump_start_rcu(struct sock *nlsk, struct sk_buff *skb,
39 const struct nlmsghdr *nlh,
40 struct netlink_dump_control *c)
44 if (!try_module_get(THIS_MODULE))
48 err = netlink_dump_start(nlsk, skb, nlh, c);
50 module_put(THIS_MODULE);
55 struct nfnl_dump_hook_data {
56 char devname[IFNAMSIZ];
61 static struct nlattr *nfnl_start_info_type(struct sk_buff *nlskb, enum nfnl_hook_chaintype t)
63 struct nlattr *nest = nla_nest_start(nlskb, NFNLA_HOOK_CHAIN_INFO);
69 ret = nla_put_be32(nlskb, NFNLA_HOOK_INFO_TYPE, htonl(t));
73 nla_nest_cancel(nlskb, nest);
77 static int nfnl_hook_put_bpf_prog_info(struct sk_buff *nlskb,
78 const struct nfnl_dump_hook_data *ctx,
80 const struct bpf_prog *prog)
82 struct nlattr *nest, *nest2;
85 if (!IS_ENABLED(CONFIG_NETFILTER_BPF_LINK))
88 if (WARN_ON_ONCE(!prog))
91 nest = nfnl_start_info_type(nlskb, NFNL_HOOK_TYPE_BPF);
95 nest2 = nla_nest_start(nlskb, NFNLA_HOOK_INFO_DESC);
99 ret = nla_put_be32(nlskb, NFNLA_HOOK_BPF_ID, htonl(prog->aux->id));
103 nla_nest_end(nlskb, nest2);
104 nla_nest_end(nlskb, nest);
108 nla_nest_cancel(nlskb, nest);
112 static int nfnl_hook_put_nft_chain_info(struct sk_buff *nlskb,
113 const struct nfnl_dump_hook_data *ctx,
115 struct nft_chain *chain)
117 struct net *net = sock_net(nlskb->sk);
118 struct nlattr *nest, *nest2;
121 if (WARN_ON_ONCE(!chain))
124 if (!nft_is_active(net, chain))
127 nest = nfnl_start_info_type(nlskb, NFNL_HOOK_TYPE_NFTABLES);
131 nest2 = nla_nest_start(nlskb, NFNLA_HOOK_INFO_DESC);
135 ret = nla_put_string(nlskb, NFNLA_CHAIN_TABLE, chain->table->name);
139 ret = nla_put_string(nlskb, NFNLA_CHAIN_NAME, chain->name);
143 ret = nla_put_u8(nlskb, NFNLA_CHAIN_FAMILY, chain->table->family);
147 nla_nest_end(nlskb, nest2);
148 nla_nest_end(nlskb, nest);
152 nla_nest_cancel(nlskb, nest);
156 static 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)
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;
166 #ifdef CONFIG_KALLSYMS
167 char sym[KSYM_SYMBOL_LEN];
170 nlh = nfnl_msg_put(nlskb, portid, seq, event,
171 NLM_F_MULTI, family, NFNETLINK_V0, 0);
173 goto nla_put_failure;
175 #ifdef CONFIG_KALLSYMS
176 ret = snprintf(sym, sizeof(sym), "%ps", ops->hook);
177 if (ret >= sizeof(sym)) {
179 goto nla_put_failure;
182 module_name = strstr(sym, " [");
188 end = strchr(module_name, ']');
192 ret = nla_put_string(nlskb, NFNLA_HOOK_MODULE_NAME, module_name);
194 goto nla_put_failure;
198 ret = nla_put_string(nlskb, NFNLA_HOOK_FUNCTION_NAME, sym);
200 goto nla_put_failure;
203 if (ops->pf == NFPROTO_INET && ops->hooknum == NF_INET_INGRESS)
204 hooknum = NF_NETDEV_INGRESS;
206 hooknum = ops->hooknum;
208 ret = nla_put_be32(nlskb, NFNLA_HOOK_HOOKNUM, htonl(hooknum));
210 goto nla_put_failure;
212 ret = nla_put_be32(nlskb, NFNLA_HOOK_PRIORITY, htonl(ops->priority));
214 goto nla_put_failure;
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);
221 ret = nfnl_hook_put_bpf_prog_info(nlskb, ctx, seq, ops->priv);
223 case NF_HOOK_OP_UNDEFINED:
231 goto nla_put_failure;
233 nlmsg_end(nlskb, nlh);
236 nlmsg_trim(nlskb, nlh);
240 static const struct nf_hook_entries *
241 nfnl_hook_entries_head(u8 pf, unsigned int hook, struct net *net, const char *dev)
243 const struct nf_hook_entries *hook_head = NULL;
244 #if defined(CONFIG_NETFILTER_INGRESS) || defined(CONFIG_NETFILTER_EGRESS)
245 struct net_device *netdev;
250 if (hook >= ARRAY_SIZE(net->nf.hooks_ipv4))
251 return ERR_PTR(-EINVAL);
252 hook_head = rcu_dereference(net->nf.hooks_ipv4[hook]);
255 if (hook >= ARRAY_SIZE(net->nf.hooks_ipv6))
256 return ERR_PTR(-EINVAL);
257 hook_head = rcu_dereference(net->nf.hooks_ipv6[hook]);
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]);
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]);
273 #if defined(CONFIG_NETFILTER_INGRESS) || defined(CONFIG_NETFILTER_EGRESS)
275 if (hook >= NF_NETDEV_NUMHOOKS)
276 return ERR_PTR(-EOPNOTSUPP);
279 return ERR_PTR(-ENODEV);
281 netdev = dev_get_by_name_rcu(net, dev);
283 return ERR_PTR(-ENODEV);
285 #ifdef CONFIG_NETFILTER_INGRESS
286 if (hook == NF_NETDEV_INGRESS)
287 return rcu_dereference(netdev->nf_hooks_ingress);
289 #ifdef CONFIG_NETFILTER_EGRESS
290 if (hook == NF_NETDEV_EGRESS)
291 return rcu_dereference(netdev->nf_hooks_egress);
296 return ERR_PTR(-EPROTONOSUPPORT);
302 static int nfnl_hook_dump(struct sk_buff *nlskb,
303 struct netlink_callback *cb)
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];
315 e = nfnl_hook_entries_head(family, ctx->hook, net, ctx->devname);
324 if ((unsigned long)e != ctx->headv || i >= e->num_hook_entries)
327 ops = nf_hook_entries_get_hook_ops(e);
329 for (; i < e->num_hook_entries; i++) {
330 err = nfnl_hook_dump_one(nlskb, ctx, ops[i], family,
337 nl_dump_check_consistent(cb, nlmsg_hdr(nlskb));
343 static int nfnl_hook_dump_start(struct netlink_callback *cb)
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] = "";
354 hooknum = ntohl(nla_get_be32(nla[NFNLA_HOOK_HOOKNUM]));
358 if (family == NFPROTO_NETDEV) {
359 if (!nla[NFNLA_HOOK_DEV])
362 nla_strscpy(name, nla[NFNLA_HOOK_DEV], sizeof(name));
366 /* Not dereferenced; for consistency check only */
367 head = nfnl_hook_entries_head(family, hooknum, net, name);
370 if (head && IS_ERR(head))
371 return PTR_ERR(head);
373 ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
377 strscpy(ctx->devname, name, sizeof(ctx->devname));
378 ctx->headv = (unsigned long)head;
387 static int nfnl_hook_dump_stop(struct netlink_callback *cb)
393 static int nfnl_hook_get(struct sk_buff *skb,
394 const struct nfnl_info *info,
395 const struct nlattr * const nla[])
397 if (!nla[NFNLA_HOOK_HOOKNUM])
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,
409 return nf_netlink_dump_start_rcu(info->sk, skb, info->nlh, &c);
415 static const struct nfnl_callback nfnl_hook_cb[NFNL_MSG_HOOK_MAX] = {
416 [NFNL_MSG_HOOK_GET] = {
417 .call = nfnl_hook_get,
419 .attr_count = NFNLA_HOOK_MAX,
420 .policy = nfnl_hook_nla_policy
424 static const struct nfnetlink_subsystem nfhook_subsys = {
426 .subsys_id = NFNL_SUBSYS_HOOK,
427 .cb_count = NFNL_MSG_HOOK_MAX,
431 MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_HOOK);
433 static int __init nfnetlink_hook_init(void)
435 return nfnetlink_subsys_register(&nfhook_subsys);
438 static void __exit nfnetlink_hook_exit(void)
440 nfnetlink_subsys_unregister(&nfhook_subsys);
443 module_init(nfnetlink_hook_init);
444 module_exit(nfnetlink_hook_exit);
446 MODULE_LICENSE("GPL");
448 MODULE_DESCRIPTION("nfnetlink_hook: list registered netfilter hooks");