Skip to content

Commit

Permalink
netfilter: ctnetlink: allow to filter dump by status bits
Browse files Browse the repository at this point in the history
If CTA_STATUS is present, but CTA_STATUS_MASK is not, then the
mask is automatically set to 'status', so that kernel returns those
entries that have all of the requested bits set.

This makes more sense than using a all-one mask since we'd hardly
ever find a match.

There are no other checks for status bits, so if e.g. userspace
sets impossible combinations it will get an empty dump.

If kernel would reject unknown status bits, then a program that works on
a future kernel that has IPS_FOO bit fails on old kernels.

Same for 'impossible' combinations:

Kernel never sets ASSURED without first having set SEEN_REPLY, but its
possible that a future kernel could do so.

Therefore no sanity tests other than a 0-mask.

Signed-off-by: Florian Westphal <[email protected]>
Signed-off-by: Pablo Neira Ayuso <[email protected]>
  • Loading branch information
Florian Westphal authored and ummakynes committed Aug 5, 2021
1 parent ff1199d commit 9344988
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 1 deletion.
1 change: 1 addition & 0 deletions include/uapi/linux/netfilter/nfnetlink_conntrack.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ enum ctattr_type {
CTA_LABELS_MASK,
CTA_SYNPROXY,
CTA_FILTER,
CTA_STATUS_MASK,
__CTA_MAX
};
#define CTA_MAX (__CTA_MAX - 1)
Expand Down
34 changes: 33 additions & 1 deletion net/netfilter/nf_conntrack_netlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -868,6 +868,7 @@ struct ctnetlink_filter {
struct nf_conntrack_zone zone;

struct ctnetlink_filter_u32 mark;
struct ctnetlink_filter_u32 status;
};

static const struct nla_policy cta_filter_nla_policy[CTA_FILTER_MAX + 1] = {
Expand Down Expand Up @@ -927,6 +928,28 @@ static int ctnetlink_filter_parse_mark(struct ctnetlink_filter_u32 *mark,
return 0;
}

static int ctnetlink_filter_parse_status(struct ctnetlink_filter_u32 *status,
const struct nlattr * const cda[])
{
if (cda[CTA_STATUS]) {
status->val = ntohl(nla_get_be32(cda[CTA_STATUS]));
if (cda[CTA_STATUS_MASK])
status->mask = ntohl(nla_get_be32(cda[CTA_STATUS_MASK]));
else
status->mask = status->val;

/* status->val == 0? always true, else always false. */
if (status->mask == 0)
return -EINVAL;
} else if (cda[CTA_STATUS_MASK]) {
return -EINVAL;
}

/* CTA_STATUS is NLA_U32, if this fires UAPI needs to be extended */
BUILD_BUG_ON(__IPS_MAX_BIT >= 32);
return 0;
}

static struct ctnetlink_filter *
ctnetlink_alloc_filter(const struct nlattr * const cda[], u8 family)
{
Expand All @@ -948,6 +971,10 @@ ctnetlink_alloc_filter(const struct nlattr * const cda[], u8 family)
if (err)
goto err_filter;

err = ctnetlink_filter_parse_status(&filter->status, cda);
if (err)
goto err_filter;

if (!cda[CTA_FILTER])
return filter;

Expand Down Expand Up @@ -1001,7 +1028,7 @@ ctnetlink_alloc_filter(const struct nlattr * const cda[], u8 family)

static bool ctnetlink_needs_filter(u8 family, const struct nlattr * const *cda)
{
return family || cda[CTA_MARK] || cda[CTA_FILTER];
return family || cda[CTA_MARK] || cda[CTA_FILTER] || cda[CTA_STATUS];
}

static int ctnetlink_start(struct netlink_callback *cb)
Expand Down Expand Up @@ -1094,6 +1121,7 @@ static int ctnetlink_filter_match(struct nf_conn *ct, void *data)
{
struct ctnetlink_filter *filter = data;
struct nf_conntrack_tuple *tuple;
u32 status;

if (filter == NULL)
goto out;
Expand Down Expand Up @@ -1125,6 +1153,9 @@ static int ctnetlink_filter_match(struct nf_conn *ct, void *data)
if ((ct->mark & filter->mark.mask) != filter->mark.val)
goto ignore_entry;
#endif
status = (u32)READ_ONCE(ct->status);
if ((status & filter->status.mask) != filter->status.val)
goto ignore_entry;

out:
return 1;
Expand Down Expand Up @@ -1507,6 +1538,7 @@ static const struct nla_policy ct_nla_policy[CTA_MAX+1] = {
[CTA_LABELS_MASK] = { .type = NLA_BINARY,
.len = NF_CT_LABELS_MAX_SIZE },
[CTA_FILTER] = { .type = NLA_NESTED },
[CTA_STATUS_MASK] = { .type = NLA_U32 },
};

static int ctnetlink_flush_iterate(struct nf_conn *ct, void *data)
Expand Down

0 comments on commit 9344988

Please sign in to comment.