1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (c) 2021 Red Hat GmbH
5 * Author: Florian Westphal <fw@strlen.de>
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");
447 MODULE_AUTHOR("Florian Westphal <fw@strlen.de>");
448 MODULE_DESCRIPTION("nfnetlink_hook: list registered netfilter hooks");