netfilter: nfnetlink hook: dump bpf prog id

This allows userspace ("nft list hooks") to show which bpf program
is attached to which hook.

Without this, user only knows bpf prog is attached at prio
x, y, z at INPUT and FORWARD, but can't tell which program is where.

v4: kdoc fixups (Simon Horman)

Link: https://lore.kernel.org/bpf/ZEELzpNCnYJuZyod@corigine.com/
Signed-off-by: Florian Westphal <fw@strlen.de>
Link: https://lore.kernel.org/r/20230421170300.24115-4-fw@strlen.de
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
Florian Westphal 2023-04-21 19:02:56 +02:00 committed by Alexei Starovoitov
parent fd9c663b9a
commit 506a74db7e
2 changed files with 89 additions and 16 deletions

View file

@ -32,8 +32,12 @@ enum nfnl_hook_attributes {
/**
* enum nfnl_hook_chain_info_attributes - chain description
*
* NFNLA_HOOK_INFO_DESC: nft chain and table name (enum nft_table_attributes) (NLA_NESTED)
* NFNLA_HOOK_INFO_TYPE: chain type (enum nfnl_hook_chaintype) (NLA_U32)
* @NFNLA_HOOK_INFO_DESC: nft chain and table name (NLA_NESTED)
* @NFNLA_HOOK_INFO_TYPE: chain type (enum nfnl_hook_chaintype) (NLA_U32)
*
* NFNLA_HOOK_INFO_DESC depends on NFNLA_HOOK_INFO_TYPE value:
* NFNL_HOOK_TYPE_NFTABLES: enum nft_table_attributes
* NFNL_HOOK_TYPE_BPF: enum nfnl_hook_bpf_attributes
*/
enum nfnl_hook_chain_info_attributes {
NFNLA_HOOK_INFO_UNSPEC,
@ -55,10 +59,24 @@ enum nfnl_hook_chain_desc_attributes {
/**
* enum nfnl_hook_chaintype - chain type
*
* @NFNL_HOOK_TYPE_NFTABLES nf_tables base chain
* @NFNL_HOOK_TYPE_NFTABLES: nf_tables base chain
* @NFNL_HOOK_TYPE_BPF: bpf program
*/
enum nfnl_hook_chaintype {
NFNL_HOOK_TYPE_NFTABLES = 0x1,
NFNL_HOOK_TYPE_BPF,
};
/**
* enum nfnl_hook_bpf_attributes - bpf prog description
*
* @NFNLA_HOOK_BPF_ID: bpf program id (NLA_U32)
*/
enum nfnl_hook_bpf_attributes {
NFNLA_HOOK_BPF_UNSPEC,
NFNLA_HOOK_BPF_ID,
__NFNLA_HOOK_BPF_MAX,
};
#define NFNLA_HOOK_BPF_MAX (__NFNLA_HOOK_BPF_MAX - 1)
#endif /* _NFNL_HOOK_H */

View file

@ -5,6 +5,7 @@
* Author: Florian Westphal <fw@strlen.de>
*/
#include <linux/bpf.h>
#include <linux/module.h>
#include <linux/kallsyms.h>
#include <linux/kernel.h>
@ -57,35 +58,76 @@ struct nfnl_dump_hook_data {
u8 hook;
};
static struct nlattr *nfnl_start_info_type(struct sk_buff *nlskb, enum nfnl_hook_chaintype t)
{
struct nlattr *nest = nla_nest_start(nlskb, NFNLA_HOOK_CHAIN_INFO);
int ret;
if (!nest)
return NULL;
ret = nla_put_be32(nlskb, NFNLA_HOOK_INFO_TYPE, htonl(t));
if (ret == 0)
return nest;
nla_nest_cancel(nlskb, nest);
return NULL;
}
static int 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)
{
struct nlattr *nest, *nest2;
int ret;
if (!IS_ENABLED(CONFIG_NETFILTER_BPF_LINK))
return 0;
if (WARN_ON_ONCE(!prog))
return 0;
nest = nfnl_start_info_type(nlskb, NFNL_HOOK_TYPE_BPF);
if (!nest)
return -EMSGSIZE;
nest2 = nla_nest_start(nlskb, NFNLA_HOOK_INFO_DESC);
if (!nest2)
goto cancel_nest;
ret = nla_put_be32(nlskb, NFNLA_HOOK_BPF_ID, htonl(prog->aux->id));
if (ret)
goto cancel_nest;
nla_nest_end(nlskb, nest2);
nla_nest_end(nlskb, nest);
return 0;
cancel_nest:
nla_nest_cancel(nlskb, nest);
return -EMSGSIZE;
}
static int nfnl_hook_put_nft_chain_info(struct sk_buff *nlskb,
const struct nfnl_dump_hook_data *ctx,
unsigned int seq,
const struct nf_hook_ops *ops)
struct nft_chain *chain)
{
struct net *net = sock_net(nlskb->sk);
struct nlattr *nest, *nest2;
struct nft_chain *chain;
int ret = 0;
if (ops->hook_ops_type != NF_HOOK_OP_NF_TABLES)
return 0;
chain = ops->priv;
if (WARN_ON_ONCE(!chain))
return 0;
if (!nft_is_active(net, chain))
return 0;
nest = nla_nest_start(nlskb, NFNLA_HOOK_CHAIN_INFO);
nest = nfnl_start_info_type(nlskb, NFNL_HOOK_TYPE_NFTABLES);
if (!nest)
return -EMSGSIZE;
ret = nla_put_be32(nlskb, NFNLA_HOOK_INFO_TYPE,
htonl(NFNL_HOOK_TYPE_NFTABLES));
if (ret)
goto cancel_nest;
nest2 = nla_nest_start(nlskb, NFNLA_HOOK_INFO_DESC);
if (!nest2)
goto cancel_nest;
@ -171,7 +213,20 @@ static int nfnl_hook_dump_one(struct sk_buff *nlskb,
if (ret)
goto nla_put_failure;
ret = nfnl_hook_put_nft_chain_info(nlskb, ctx, seq, ops);
switch (ops->hook_ops_type) {
case NF_HOOK_OP_NF_TABLES:
ret = nfnl_hook_put_nft_chain_info(nlskb, ctx, seq, ops->priv);
break;
case NF_HOOK_OP_BPF:
ret = nfnl_hook_put_bpf_prog_info(nlskb, ctx, seq, ops->priv);
break;
case NF_HOOK_OP_UNDEFINED:
break;
default:
WARN_ON_ONCE(1);
break;
}
if (ret)
goto nla_put_failure;