mirror of
https://kernel.googlesource.com/pub/scm/linux/kernel/git/torvalds/linux
synced 2025-09-26 02:20:25 +10:00
netfilter pull request 23-09-13
-----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEN9lkrMBJgcdVAPub1V2XiooUIOQFAmUCKboACgkQ1V2XiooU IOQ1CxAAqKwyeROJ7+qLvIBbwRFIQr70pPCjfY/GskP9aqljhth+e5TsKurWA12X wwbVhQ9xblvxarekR4B8lwGhvenYHk3l6R/3wuTMYPHFTXkE+mluGgljffaMwV+D YywK5hOkLenBZmxdjUfdJ87DJwAadcbLOABmEiSQ3hDxj3/xTBf7gToqlSwHtjCC JDC7vhxjosQHQSLhjqetfrUauz0OZAqldZ2is/FELYg56oCGKddGAZxnC4fQBnXx DzvRroP8f8bkqGjKwkt945bKiQ4Cz1frQE+YP1+pRk0rOkv70hhzH0JXIELQ5q9L RYLFfgkemp2HfBJ+y2PK8lBDailre4MdGdsAI5eWjBXgrl3jRBybioafhhUbJVIq Q3zIzXVgLQqXwSONBF2sfVssVZzhfjAzZQzzgw3wayhWj1WgwqsCb0EChvA4FJZ7 HW4xyROeOV7GHoUAWCPcoeBiNJYKmGNWjkWwlT4q5LtYMyWWP9oYx2kOn9/JQ9QI Tth8QobntRr8Gw/f0awGULM2pcecCLyYhIoJtWctegFSN2ejrKiV9XItbxZ3G1in 3pYSVgpyve9ZAvHmTSyvh+mjZ71X2ZebLyMADrWbsHrCXgIUSUkoksQd97XsffeZ noRVlLj0MlfRlUoorDQG3A+QxdQb+ZaHkBKTOEzouKOYEj6vylY= =TgRd -----END PGP SIGNATURE----- Merge tag 'nf-23-09-13' of git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf netfilter pull request 23-09-13 ==================== The following patchset contains Netfilter fixes for net: 1) Do not permit to remove rules from chain binding, otherwise double rule release is possible, triggering UaF. This rule deletion support does not make sense and userspace does not use this. Problem exists since the introduction of chain binding support. 2) rbtree GC worker only collects the elements that have expired. This operation is not destructive, therefore, turn write into read spinlock to avoid datapath contention due to GC worker run. This was not fixed in the recent GC fix batch in the 6.5 cycle. 3) pipapo set backend performs sync GC, therefore, catchall elements must use sync GC queue variant. This bug was introduced in the 6.5 cycle with the recent GC fixes. 4) Stop GC run if memory allocation fails in pipapo set backend, otherwise access to NULL pointer to GC transaction object might occur. This bug was introduced in the 6.5 cycle with the recent GC fixes. 5) rhash GC run uses an iterator that might hit EAGAIN to rewind, triggering double-collection of the same element. This bug was introduced in the 6.5 cycle with the recent GC fixes. 6) Do not permit to remove elements in anonymous sets, this type of sets are populated once and then bound to rules. This fix is similar to the chain binding patch coming first in this batch. API permits since the very beginning but it has no use case from userspace. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
615efed8b6
@ -1700,8 +1700,9 @@ void nft_trans_gc_queue_sync_done(struct nft_trans_gc *trans);
|
|||||||
|
|
||||||
void nft_trans_gc_elem_add(struct nft_trans_gc *gc, void *priv);
|
void nft_trans_gc_elem_add(struct nft_trans_gc *gc, void *priv);
|
||||||
|
|
||||||
struct nft_trans_gc *nft_trans_gc_catchall(struct nft_trans_gc *gc,
|
struct nft_trans_gc *nft_trans_gc_catchall_async(struct nft_trans_gc *gc,
|
||||||
unsigned int gc_seq);
|
unsigned int gc_seq);
|
||||||
|
struct nft_trans_gc *nft_trans_gc_catchall_sync(struct nft_trans_gc *gc);
|
||||||
|
|
||||||
void nft_setelem_data_deactivate(const struct net *net,
|
void nft_setelem_data_deactivate(const struct net *net,
|
||||||
const struct nft_set *set,
|
const struct nft_set *set,
|
||||||
|
@ -40,10 +40,10 @@ static const u8 nf_ct_ext_type_len[NF_CT_EXT_NUM] = {
|
|||||||
[NF_CT_EXT_ECACHE] = sizeof(struct nf_conntrack_ecache),
|
[NF_CT_EXT_ECACHE] = sizeof(struct nf_conntrack_ecache),
|
||||||
#endif
|
#endif
|
||||||
#ifdef CONFIG_NF_CONNTRACK_TIMESTAMP
|
#ifdef CONFIG_NF_CONNTRACK_TIMESTAMP
|
||||||
[NF_CT_EXT_TSTAMP] = sizeof(struct nf_conn_acct),
|
[NF_CT_EXT_TSTAMP] = sizeof(struct nf_conn_tstamp),
|
||||||
#endif
|
#endif
|
||||||
#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
|
#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
|
||||||
[NF_CT_EXT_TIMEOUT] = sizeof(struct nf_conn_tstamp),
|
[NF_CT_EXT_TIMEOUT] = sizeof(struct nf_conn_timeout),
|
||||||
#endif
|
#endif
|
||||||
#ifdef CONFIG_NF_CONNTRACK_LABELS
|
#ifdef CONFIG_NF_CONNTRACK_LABELS
|
||||||
[NF_CT_EXT_LABELS] = sizeof(struct nf_conn_labels),
|
[NF_CT_EXT_LABELS] = sizeof(struct nf_conn_labels),
|
||||||
|
@ -1432,7 +1432,7 @@ static int nft_flush_table(struct nft_ctx *ctx)
|
|||||||
if (!nft_is_active_next(ctx->net, chain))
|
if (!nft_is_active_next(ctx->net, chain))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (nft_chain_is_bound(chain))
|
if (nft_chain_binding(chain))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
ctx->chain = chain;
|
ctx->chain = chain;
|
||||||
@ -1446,8 +1446,7 @@ static int nft_flush_table(struct nft_ctx *ctx)
|
|||||||
if (!nft_is_active_next(ctx->net, set))
|
if (!nft_is_active_next(ctx->net, set))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (nft_set_is_anonymous(set) &&
|
if (nft_set_is_anonymous(set))
|
||||||
!list_empty(&set->bindings))
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
err = nft_delset(ctx, set);
|
err = nft_delset(ctx, set);
|
||||||
@ -1477,7 +1476,7 @@ static int nft_flush_table(struct nft_ctx *ctx)
|
|||||||
if (!nft_is_active_next(ctx->net, chain))
|
if (!nft_is_active_next(ctx->net, chain))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (nft_chain_is_bound(chain))
|
if (nft_chain_binding(chain))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
ctx->chain = chain;
|
ctx->chain = chain;
|
||||||
@ -2910,6 +2909,9 @@ static int nf_tables_delchain(struct sk_buff *skb, const struct nfnl_info *info,
|
|||||||
return PTR_ERR(chain);
|
return PTR_ERR(chain);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (nft_chain_binding(chain))
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
nft_ctx_init(&ctx, net, skb, info->nlh, family, table, chain, nla);
|
nft_ctx_init(&ctx, net, skb, info->nlh, family, table, chain, nla);
|
||||||
|
|
||||||
if (nla[NFTA_CHAIN_HOOK]) {
|
if (nla[NFTA_CHAIN_HOOK]) {
|
||||||
@ -3449,6 +3451,8 @@ static int __nf_tables_dump_rules(struct sk_buff *skb,
|
|||||||
struct net *net = sock_net(skb->sk);
|
struct net *net = sock_net(skb->sk);
|
||||||
const struct nft_rule *rule, *prule;
|
const struct nft_rule *rule, *prule;
|
||||||
unsigned int s_idx = cb->args[0];
|
unsigned int s_idx = cb->args[0];
|
||||||
|
unsigned int entries = 0;
|
||||||
|
int ret = 0;
|
||||||
u64 handle;
|
u64 handle;
|
||||||
|
|
||||||
prule = NULL;
|
prule = NULL;
|
||||||
@ -3471,9 +3475,11 @@ static int __nf_tables_dump_rules(struct sk_buff *skb,
|
|||||||
NFT_MSG_NEWRULE,
|
NFT_MSG_NEWRULE,
|
||||||
NLM_F_MULTI | NLM_F_APPEND,
|
NLM_F_MULTI | NLM_F_APPEND,
|
||||||
table->family,
|
table->family,
|
||||||
table, chain, rule, handle, reset) < 0)
|
table, chain, rule, handle, reset) < 0) {
|
||||||
return 1;
|
ret = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
entries++;
|
||||||
nl_dump_check_consistent(cb, nlmsg_hdr(skb));
|
nl_dump_check_consistent(cb, nlmsg_hdr(skb));
|
||||||
cont:
|
cont:
|
||||||
prule = rule;
|
prule = rule;
|
||||||
@ -3481,10 +3487,10 @@ cont_skip:
|
|||||||
(*idx)++;
|
(*idx)++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reset && *idx)
|
if (reset && entries)
|
||||||
audit_log_rule_reset(table, cb->seq, *idx);
|
audit_log_rule_reset(table, cb->seq, entries);
|
||||||
|
|
||||||
return 0;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nf_tables_dump_rules(struct sk_buff *skb,
|
static int nf_tables_dump_rules(struct sk_buff *skb,
|
||||||
@ -3971,6 +3977,11 @@ static int nf_tables_newrule(struct sk_buff *skb, const struct nfnl_info *info,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (info->nlh->nlmsg_flags & NLM_F_REPLACE) {
|
if (info->nlh->nlmsg_flags & NLM_F_REPLACE) {
|
||||||
|
if (nft_chain_binding(chain)) {
|
||||||
|
err = -EOPNOTSUPP;
|
||||||
|
goto err_destroy_flow_rule;
|
||||||
|
}
|
||||||
|
|
||||||
err = nft_delrule(&ctx, old_rule);
|
err = nft_delrule(&ctx, old_rule);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto err_destroy_flow_rule;
|
goto err_destroy_flow_rule;
|
||||||
@ -4078,7 +4089,7 @@ static int nf_tables_delrule(struct sk_buff *skb, const struct nfnl_info *info,
|
|||||||
NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_CHAIN]);
|
NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_CHAIN]);
|
||||||
return PTR_ERR(chain);
|
return PTR_ERR(chain);
|
||||||
}
|
}
|
||||||
if (nft_chain_is_bound(chain))
|
if (nft_chain_binding(chain))
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4112,7 +4123,7 @@ static int nf_tables_delrule(struct sk_buff *skb, const struct nfnl_info *info,
|
|||||||
list_for_each_entry(chain, &table->chains, list) {
|
list_for_each_entry(chain, &table->chains, list) {
|
||||||
if (!nft_is_active_next(net, chain))
|
if (!nft_is_active_next(net, chain))
|
||||||
continue;
|
continue;
|
||||||
if (nft_chain_is_bound(chain))
|
if (nft_chain_binding(chain))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
ctx.chain = chain;
|
ctx.chain = chain;
|
||||||
@ -7183,8 +7194,10 @@ static int nf_tables_delsetelem(struct sk_buff *skb,
|
|||||||
if (IS_ERR(set))
|
if (IS_ERR(set))
|
||||||
return PTR_ERR(set);
|
return PTR_ERR(set);
|
||||||
|
|
||||||
if (!list_empty(&set->bindings) &&
|
if (nft_set_is_anonymous(set))
|
||||||
(set->flags & (NFT_SET_CONSTANT | NFT_SET_ANONYMOUS)))
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
if (!list_empty(&set->bindings) && (set->flags & NFT_SET_CONSTANT))
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
|
|
||||||
nft_ctx_init(&ctx, net, skb, info->nlh, family, table, NULL, nla);
|
nft_ctx_init(&ctx, net, skb, info->nlh, family, table, NULL, nla);
|
||||||
@ -9605,8 +9618,9 @@ void nft_trans_gc_queue_sync_done(struct nft_trans_gc *trans)
|
|||||||
call_rcu(&trans->rcu, nft_trans_gc_trans_free);
|
call_rcu(&trans->rcu, nft_trans_gc_trans_free);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct nft_trans_gc *nft_trans_gc_catchall(struct nft_trans_gc *gc,
|
static struct nft_trans_gc *nft_trans_gc_catchall(struct nft_trans_gc *gc,
|
||||||
unsigned int gc_seq)
|
unsigned int gc_seq,
|
||||||
|
bool sync)
|
||||||
{
|
{
|
||||||
struct nft_set_elem_catchall *catchall;
|
struct nft_set_elem_catchall *catchall;
|
||||||
const struct nft_set *set = gc->set;
|
const struct nft_set *set = gc->set;
|
||||||
@ -9622,7 +9636,11 @@ struct nft_trans_gc *nft_trans_gc_catchall(struct nft_trans_gc *gc,
|
|||||||
|
|
||||||
nft_set_elem_dead(ext);
|
nft_set_elem_dead(ext);
|
||||||
dead_elem:
|
dead_elem:
|
||||||
gc = nft_trans_gc_queue_async(gc, gc_seq, GFP_ATOMIC);
|
if (sync)
|
||||||
|
gc = nft_trans_gc_queue_sync(gc, GFP_ATOMIC);
|
||||||
|
else
|
||||||
|
gc = nft_trans_gc_queue_async(gc, gc_seq, GFP_ATOMIC);
|
||||||
|
|
||||||
if (!gc)
|
if (!gc)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
@ -9632,6 +9650,17 @@ dead_elem:
|
|||||||
return gc;
|
return gc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct nft_trans_gc *nft_trans_gc_catchall_async(struct nft_trans_gc *gc,
|
||||||
|
unsigned int gc_seq)
|
||||||
|
{
|
||||||
|
return nft_trans_gc_catchall(gc, gc_seq, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct nft_trans_gc *nft_trans_gc_catchall_sync(struct nft_trans_gc *gc)
|
||||||
|
{
|
||||||
|
return nft_trans_gc_catchall(gc, 0, true);
|
||||||
|
}
|
||||||
|
|
||||||
static void nf_tables_module_autoload_cleanup(struct net *net)
|
static void nf_tables_module_autoload_cleanup(struct net *net)
|
||||||
{
|
{
|
||||||
struct nftables_pernet *nft_net = nft_pernet(net);
|
struct nftables_pernet *nft_net = nft_pernet(net);
|
||||||
@ -11054,7 +11083,7 @@ static void __nft_release_table(struct net *net, struct nft_table *table)
|
|||||||
ctx.family = table->family;
|
ctx.family = table->family;
|
||||||
ctx.table = table;
|
ctx.table = table;
|
||||||
list_for_each_entry(chain, &table->chains, list) {
|
list_for_each_entry(chain, &table->chains, list) {
|
||||||
if (nft_chain_is_bound(chain))
|
if (nft_chain_binding(chain))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
ctx.chain = chain;
|
ctx.chain = chain;
|
||||||
|
@ -338,12 +338,9 @@ static void nft_rhash_gc(struct work_struct *work)
|
|||||||
|
|
||||||
while ((he = rhashtable_walk_next(&hti))) {
|
while ((he = rhashtable_walk_next(&hti))) {
|
||||||
if (IS_ERR(he)) {
|
if (IS_ERR(he)) {
|
||||||
if (PTR_ERR(he) != -EAGAIN) {
|
nft_trans_gc_destroy(gc);
|
||||||
nft_trans_gc_destroy(gc);
|
gc = NULL;
|
||||||
gc = NULL;
|
goto try_later;
|
||||||
goto try_later;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Ruleset has been updated, try later. */
|
/* Ruleset has been updated, try later. */
|
||||||
@ -372,7 +369,7 @@ dead_elem:
|
|||||||
nft_trans_gc_elem_add(gc, he);
|
nft_trans_gc_elem_add(gc, he);
|
||||||
}
|
}
|
||||||
|
|
||||||
gc = nft_trans_gc_catchall(gc, gc_seq);
|
gc = nft_trans_gc_catchall_async(gc, gc_seq);
|
||||||
|
|
||||||
try_later:
|
try_later:
|
||||||
/* catchall list iteration requires rcu read side lock. */
|
/* catchall list iteration requires rcu read side lock. */
|
||||||
|
@ -1596,7 +1596,7 @@ static void pipapo_gc(const struct nft_set *_set, struct nft_pipapo_match *m)
|
|||||||
|
|
||||||
gc = nft_trans_gc_queue_sync(gc, GFP_ATOMIC);
|
gc = nft_trans_gc_queue_sync(gc, GFP_ATOMIC);
|
||||||
if (!gc)
|
if (!gc)
|
||||||
break;
|
return;
|
||||||
|
|
||||||
nft_pipapo_gc_deactivate(net, set, e);
|
nft_pipapo_gc_deactivate(net, set, e);
|
||||||
pipapo_drop(m, rulemap);
|
pipapo_drop(m, rulemap);
|
||||||
@ -1610,7 +1610,7 @@ static void pipapo_gc(const struct nft_set *_set, struct nft_pipapo_match *m)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gc = nft_trans_gc_catchall(gc, 0);
|
gc = nft_trans_gc_catchall_sync(gc);
|
||||||
if (gc) {
|
if (gc) {
|
||||||
nft_trans_gc_queue_sync_done(gc);
|
nft_trans_gc_queue_sync_done(gc);
|
||||||
priv->last_gc = jiffies;
|
priv->last_gc = jiffies;
|
||||||
|
@ -622,8 +622,7 @@ static void nft_rbtree_gc(struct work_struct *work)
|
|||||||
if (!gc)
|
if (!gc)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
write_lock_bh(&priv->lock);
|
read_lock_bh(&priv->lock);
|
||||||
write_seqcount_begin(&priv->count);
|
|
||||||
for (node = rb_first(&priv->root); node != NULL; node = rb_next(node)) {
|
for (node = rb_first(&priv->root); node != NULL; node = rb_next(node)) {
|
||||||
|
|
||||||
/* Ruleset has been updated, try later. */
|
/* Ruleset has been updated, try later. */
|
||||||
@ -670,11 +669,10 @@ dead_elem:
|
|||||||
nft_trans_gc_elem_add(gc, rbe);
|
nft_trans_gc_elem_add(gc, rbe);
|
||||||
}
|
}
|
||||||
|
|
||||||
gc = nft_trans_gc_catchall(gc, gc_seq);
|
gc = nft_trans_gc_catchall_async(gc, gc_seq);
|
||||||
|
|
||||||
try_later:
|
try_later:
|
||||||
write_seqcount_end(&priv->count);
|
read_unlock_bh(&priv->lock);
|
||||||
write_unlock_bh(&priv->lock);
|
|
||||||
|
|
||||||
if (gc)
|
if (gc)
|
||||||
nft_trans_gc_queue_async_done(gc);
|
nft_trans_gc_queue_async_done(gc);
|
||||||
|
1
tools/testing/selftests/netfilter/.gitignore
vendored
1
tools/testing/selftests/netfilter/.gitignore
vendored
@ -1,3 +1,4 @@
|
|||||||
# SPDX-License-Identifier: GPL-2.0-only
|
# SPDX-License-Identifier: GPL-2.0-only
|
||||||
nf-queue
|
nf-queue
|
||||||
connect_close
|
connect_close
|
||||||
|
audit_logread
|
||||||
|
@ -6,13 +6,13 @@ TEST_PROGS := nft_trans_stress.sh nft_fib.sh nft_nat.sh bridge_brouter.sh \
|
|||||||
nft_concat_range.sh nft_conntrack_helper.sh \
|
nft_concat_range.sh nft_conntrack_helper.sh \
|
||||||
nft_queue.sh nft_meta.sh nf_nat_edemux.sh \
|
nft_queue.sh nft_meta.sh nf_nat_edemux.sh \
|
||||||
ipip-conntrack-mtu.sh conntrack_tcp_unreplied.sh \
|
ipip-conntrack-mtu.sh conntrack_tcp_unreplied.sh \
|
||||||
conntrack_vrf.sh nft_synproxy.sh rpath.sh
|
conntrack_vrf.sh nft_synproxy.sh rpath.sh nft_audit.sh
|
||||||
|
|
||||||
HOSTPKG_CONFIG := pkg-config
|
HOSTPKG_CONFIG := pkg-config
|
||||||
|
|
||||||
CFLAGS += $(shell $(HOSTPKG_CONFIG) --cflags libmnl 2>/dev/null)
|
CFLAGS += $(shell $(HOSTPKG_CONFIG) --cflags libmnl 2>/dev/null)
|
||||||
LDLIBS += $(shell $(HOSTPKG_CONFIG) --libs libmnl 2>/dev/null || echo -lmnl)
|
LDLIBS += $(shell $(HOSTPKG_CONFIG) --libs libmnl 2>/dev/null || echo -lmnl)
|
||||||
|
|
||||||
TEST_GEN_FILES = nf-queue connect_close
|
TEST_GEN_FILES = nf-queue connect_close audit_logread
|
||||||
|
|
||||||
include ../lib.mk
|
include ../lib.mk
|
||||||
|
165
tools/testing/selftests/netfilter/audit_logread.c
Normal file
165
tools/testing/selftests/netfilter/audit_logread.c
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <linux/audit.h>
|
||||||
|
#include <linux/netlink.h>
|
||||||
|
|
||||||
|
static int fd;
|
||||||
|
|
||||||
|
#define MAX_AUDIT_MESSAGE_LENGTH 8970
|
||||||
|
struct audit_message {
|
||||||
|
struct nlmsghdr nlh;
|
||||||
|
union {
|
||||||
|
struct audit_status s;
|
||||||
|
char data[MAX_AUDIT_MESSAGE_LENGTH];
|
||||||
|
} u;
|
||||||
|
};
|
||||||
|
|
||||||
|
int audit_recv(int fd, struct audit_message *rep)
|
||||||
|
{
|
||||||
|
struct sockaddr_nl addr;
|
||||||
|
socklen_t addrlen = sizeof(addr);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
do {
|
||||||
|
ret = recvfrom(fd, rep, sizeof(*rep), 0,
|
||||||
|
(struct sockaddr *)&addr, &addrlen);
|
||||||
|
} while (ret < 0 && errno == EINTR);
|
||||||
|
|
||||||
|
if (ret < 0 ||
|
||||||
|
addrlen != sizeof(addr) ||
|
||||||
|
addr.nl_pid != 0 ||
|
||||||
|
rep->nlh.nlmsg_type == NLMSG_ERROR) /* short-cut for now */
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int audit_send(int fd, uint16_t type, uint32_t key, uint32_t val)
|
||||||
|
{
|
||||||
|
static int seq = 0;
|
||||||
|
struct audit_message msg = {
|
||||||
|
.nlh = {
|
||||||
|
.nlmsg_len = NLMSG_SPACE(sizeof(msg.u.s)),
|
||||||
|
.nlmsg_type = type,
|
||||||
|
.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK,
|
||||||
|
.nlmsg_seq = ++seq,
|
||||||
|
},
|
||||||
|
.u.s = {
|
||||||
|
.mask = key,
|
||||||
|
.enabled = key == AUDIT_STATUS_ENABLED ? val : 0,
|
||||||
|
.pid = key == AUDIT_STATUS_PID ? val : 0,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
struct sockaddr_nl addr = {
|
||||||
|
.nl_family = AF_NETLINK,
|
||||||
|
};
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
do {
|
||||||
|
ret = sendto(fd, &msg, msg.nlh.nlmsg_len, 0,
|
||||||
|
(struct sockaddr *)&addr, sizeof(addr));
|
||||||
|
} while (ret < 0 && errno == EINTR);
|
||||||
|
|
||||||
|
if (ret != (int)msg.nlh.nlmsg_len)
|
||||||
|
return -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int audit_set(int fd, uint32_t key, uint32_t val)
|
||||||
|
{
|
||||||
|
struct audit_message rep = { 0 };
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = audit_send(fd, AUDIT_SET, key, val);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = audit_recv(fd, &rep);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int readlog(int fd)
|
||||||
|
{
|
||||||
|
struct audit_message rep = { 0 };
|
||||||
|
int ret = audit_recv(fd, &rep);
|
||||||
|
const char *sep = "";
|
||||||
|
char *k, *v;
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (rep.nlh.nlmsg_type != AUDIT_NETFILTER_CFG)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* skip the initial "audit(...): " part */
|
||||||
|
strtok(rep.u.data, " ");
|
||||||
|
|
||||||
|
while ((k = strtok(NULL, "="))) {
|
||||||
|
v = strtok(NULL, " ");
|
||||||
|
|
||||||
|
/* these vary and/or are uninteresting, ignore */
|
||||||
|
if (!strcmp(k, "pid") ||
|
||||||
|
!strcmp(k, "comm") ||
|
||||||
|
!strcmp(k, "subj"))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* strip the varying sequence number */
|
||||||
|
if (!strcmp(k, "table"))
|
||||||
|
*strchrnul(v, ':') = '\0';
|
||||||
|
|
||||||
|
printf("%s%s=%s", sep, k, v);
|
||||||
|
sep = " ";
|
||||||
|
}
|
||||||
|
if (*sep) {
|
||||||
|
printf("\n");
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cleanup(int sig)
|
||||||
|
{
|
||||||
|
audit_set(fd, AUDIT_STATUS_ENABLED, 0);
|
||||||
|
close(fd);
|
||||||
|
if (sig)
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
struct sigaction act = {
|
||||||
|
.sa_handler = cleanup,
|
||||||
|
};
|
||||||
|
|
||||||
|
fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_AUDIT);
|
||||||
|
if (fd < 0) {
|
||||||
|
perror("Can't open netlink socket");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sigaction(SIGTERM, &act, NULL) < 0 ||
|
||||||
|
sigaction(SIGINT, &act, NULL) < 0) {
|
||||||
|
perror("Can't set signal handler");
|
||||||
|
close(fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
audit_set(fd, AUDIT_STATUS_ENABLED, 1);
|
||||||
|
audit_set(fd, AUDIT_STATUS_PID, getpid());
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
readlog(fd);
|
||||||
|
}
|
@ -6,3 +6,4 @@ CONFIG_NFT_REDIR=m
|
|||||||
CONFIG_NFT_MASQ=m
|
CONFIG_NFT_MASQ=m
|
||||||
CONFIG_NFT_FLOW_OFFLOAD=m
|
CONFIG_NFT_FLOW_OFFLOAD=m
|
||||||
CONFIG_NF_CT_NETLINK=m
|
CONFIG_NF_CT_NETLINK=m
|
||||||
|
CONFIG_AUDIT=y
|
||||||
|
108
tools/testing/selftests/netfilter/nft_audit.sh
Executable file
108
tools/testing/selftests/netfilter/nft_audit.sh
Executable file
@ -0,0 +1,108 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
|
#
|
||||||
|
# Check that audit logs generated for nft commands are as expected.
|
||||||
|
|
||||||
|
SKIP_RC=4
|
||||||
|
RC=0
|
||||||
|
|
||||||
|
nft --version >/dev/null 2>&1 || {
|
||||||
|
echo "SKIP: missing nft tool"
|
||||||
|
exit $SKIP_RC
|
||||||
|
}
|
||||||
|
|
||||||
|
logfile=$(mktemp)
|
||||||
|
echo "logging into $logfile"
|
||||||
|
./audit_logread >"$logfile" &
|
||||||
|
logread_pid=$!
|
||||||
|
trap 'kill $logread_pid; rm -f $logfile' EXIT
|
||||||
|
exec 3<"$logfile"
|
||||||
|
|
||||||
|
do_test() { # (cmd, log)
|
||||||
|
echo -n "testing for cmd: $1 ... "
|
||||||
|
cat <&3 >/dev/null
|
||||||
|
$1 >/dev/null || exit 1
|
||||||
|
sleep 0.1
|
||||||
|
res=$(diff -a -u <(echo "$2") - <&3)
|
||||||
|
[ $? -eq 0 ] && { echo "OK"; return; }
|
||||||
|
echo "FAIL"
|
||||||
|
echo "$res"
|
||||||
|
((RC++))
|
||||||
|
}
|
||||||
|
|
||||||
|
nft flush ruleset
|
||||||
|
|
||||||
|
for table in t1 t2; do
|
||||||
|
do_test "nft add table $table" \
|
||||||
|
"table=$table family=2 entries=1 op=nft_register_table"
|
||||||
|
|
||||||
|
do_test "nft add chain $table c1" \
|
||||||
|
"table=$table family=2 entries=1 op=nft_register_chain"
|
||||||
|
|
||||||
|
do_test "nft add chain $table c2; add chain $table c3" \
|
||||||
|
"table=$table family=2 entries=2 op=nft_register_chain"
|
||||||
|
|
||||||
|
cmd="add rule $table c1 counter"
|
||||||
|
|
||||||
|
do_test "nft $cmd" \
|
||||||
|
"table=$table family=2 entries=1 op=nft_register_rule"
|
||||||
|
|
||||||
|
do_test "nft $cmd; $cmd" \
|
||||||
|
"table=$table family=2 entries=2 op=nft_register_rule"
|
||||||
|
|
||||||
|
cmd=""
|
||||||
|
sep=""
|
||||||
|
for chain in c2 c3; do
|
||||||
|
for i in {1..3}; do
|
||||||
|
cmd+="$sep add rule $table $chain counter"
|
||||||
|
sep=";"
|
||||||
|
done
|
||||||
|
done
|
||||||
|
do_test "nft $cmd" \
|
||||||
|
"table=$table family=2 entries=6 op=nft_register_rule"
|
||||||
|
done
|
||||||
|
|
||||||
|
do_test 'nft reset rules t1 c2' \
|
||||||
|
'table=t1 family=2 entries=3 op=nft_reset_rule'
|
||||||
|
|
||||||
|
do_test 'nft reset rules table t1' \
|
||||||
|
'table=t1 family=2 entries=3 op=nft_reset_rule
|
||||||
|
table=t1 family=2 entries=3 op=nft_reset_rule
|
||||||
|
table=t1 family=2 entries=3 op=nft_reset_rule'
|
||||||
|
|
||||||
|
do_test 'nft reset rules' \
|
||||||
|
'table=t1 family=2 entries=3 op=nft_reset_rule
|
||||||
|
table=t1 family=2 entries=3 op=nft_reset_rule
|
||||||
|
table=t1 family=2 entries=3 op=nft_reset_rule
|
||||||
|
table=t2 family=2 entries=3 op=nft_reset_rule
|
||||||
|
table=t2 family=2 entries=3 op=nft_reset_rule
|
||||||
|
table=t2 family=2 entries=3 op=nft_reset_rule'
|
||||||
|
|
||||||
|
for ((i = 0; i < 500; i++)); do
|
||||||
|
echo "add rule t2 c3 counter accept comment \"rule $i\""
|
||||||
|
done | do_test 'nft -f -' \
|
||||||
|
'table=t2 family=2 entries=500 op=nft_register_rule'
|
||||||
|
|
||||||
|
do_test 'nft reset rules t2 c3' \
|
||||||
|
'table=t2 family=2 entries=189 op=nft_reset_rule
|
||||||
|
table=t2 family=2 entries=188 op=nft_reset_rule
|
||||||
|
table=t2 family=2 entries=126 op=nft_reset_rule'
|
||||||
|
|
||||||
|
do_test 'nft reset rules t2' \
|
||||||
|
'table=t2 family=2 entries=3 op=nft_reset_rule
|
||||||
|
table=t2 family=2 entries=3 op=nft_reset_rule
|
||||||
|
table=t2 family=2 entries=186 op=nft_reset_rule
|
||||||
|
table=t2 family=2 entries=188 op=nft_reset_rule
|
||||||
|
table=t2 family=2 entries=129 op=nft_reset_rule'
|
||||||
|
|
||||||
|
do_test 'nft reset rules' \
|
||||||
|
'table=t1 family=2 entries=3 op=nft_reset_rule
|
||||||
|
table=t1 family=2 entries=3 op=nft_reset_rule
|
||||||
|
table=t1 family=2 entries=3 op=nft_reset_rule
|
||||||
|
table=t2 family=2 entries=3 op=nft_reset_rule
|
||||||
|
table=t2 family=2 entries=3 op=nft_reset_rule
|
||||||
|
table=t2 family=2 entries=180 op=nft_reset_rule
|
||||||
|
table=t2 family=2 entries=188 op=nft_reset_rule
|
||||||
|
table=t2 family=2 entries=135 op=nft_reset_rule'
|
||||||
|
|
||||||
|
exit $RC
|
Loading…
Reference in New Issue
Block a user