mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-11 13:04:03 +08:00
netfilter: ctnetlink: allow userspace to modify labels
Add the ability to set/clear labels assigned to a conntrack via ctnetlink. To allow userspace to only alter specific bits, Pablo suggested to add a new CTA_LABELS_MASK attribute: The new set of active labels is then determined via active = (active & ~mask) ^ changeset i.e., the mask selects those bits in the existing set that should be changed. This follows the same method already used by MARK and CONNMARK targets. Omitting CTA_LABELS_MASK is the same as setting all bits in CTA_LABELS_MASK to 1: The existing set is replaced by the one from userspace. Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
This commit is contained in:
parent
0ceabd8387
commit
9b21f6a909
@ -46,6 +46,9 @@ static inline struct nf_conn_labels *nf_ct_labels_ext_add(struct nf_conn *ct)
|
||||
bool nf_connlabel_match(const struct nf_conn *ct, u16 bit);
|
||||
int nf_connlabel_set(struct nf_conn *ct, u16 bit);
|
||||
|
||||
int nf_connlabels_replace(struct nf_conn *ct,
|
||||
const u32 *data, const u32 *mask, unsigned int words);
|
||||
|
||||
#ifdef CONFIG_NF_CONNTRACK_LABELS
|
||||
int nf_conntrack_labels_init(struct net *net);
|
||||
void nf_conntrack_labels_fini(struct net *net);
|
||||
|
@ -50,6 +50,7 @@ enum ctattr_type {
|
||||
CTA_TIMESTAMP,
|
||||
CTA_MARK_MASK,
|
||||
CTA_LABELS,
|
||||
CTA_LABELS_MASK,
|
||||
__CTA_MAX
|
||||
};
|
||||
#define CTA_MAX (__CTA_MAX - 1)
|
||||
|
@ -52,6 +52,49 @@ int nf_connlabel_set(struct nf_conn *ct, u16 bit)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_connlabel_set);
|
||||
|
||||
#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
|
||||
static void replace_u32(u32 *address, u32 mask, u32 new)
|
||||
{
|
||||
u32 old, tmp;
|
||||
|
||||
do {
|
||||
old = *address;
|
||||
tmp = (old & mask) ^ new;
|
||||
} while (cmpxchg(address, old, tmp) != old);
|
||||
}
|
||||
|
||||
int nf_connlabels_replace(struct nf_conn *ct,
|
||||
const u32 *data,
|
||||
const u32 *mask, unsigned int words32)
|
||||
{
|
||||
struct nf_conn_labels *labels;
|
||||
unsigned int size, i;
|
||||
u32 *dst;
|
||||
|
||||
labels = nf_ct_labels_find(ct);
|
||||
if (!labels)
|
||||
return -ENOSPC;
|
||||
|
||||
size = labels->words * sizeof(long);
|
||||
if (size < (words32 * sizeof(u32)))
|
||||
words32 = size / sizeof(u32);
|
||||
|
||||
dst = (u32 *) labels->bits;
|
||||
if (words32) {
|
||||
for (i = 0; i < words32; i++)
|
||||
replace_u32(&dst[i], mask ? ~mask[i] : 0, data[i]);
|
||||
}
|
||||
|
||||
size /= sizeof(u32);
|
||||
for (i = words32; i < size; i++) /* pad */
|
||||
replace_u32(&dst[i], 0, 0);
|
||||
|
||||
nf_conntrack_event_cache(IPCT_LABEL, ct);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_connlabels_replace);
|
||||
#endif
|
||||
|
||||
static struct nf_ct_ext_type labels_extend __read_mostly = {
|
||||
.len = sizeof(struct nf_conn_labels),
|
||||
.align = __alignof__(struct nf_conn_labels),
|
||||
|
@ -961,6 +961,7 @@ ctnetlink_parse_help(const struct nlattr *attr, char **helper_name,
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define __CTA_LABELS_MAX_LENGTH ((XT_CONNLABEL_MAXBIT + 1) / BITS_PER_BYTE)
|
||||
static const struct nla_policy ct_nla_policy[CTA_MAX+1] = {
|
||||
[CTA_TUPLE_ORIG] = { .type = NLA_NESTED },
|
||||
[CTA_TUPLE_REPLY] = { .type = NLA_NESTED },
|
||||
@ -977,6 +978,10 @@ static const struct nla_policy ct_nla_policy[CTA_MAX+1] = {
|
||||
[CTA_NAT_SEQ_ADJ_REPLY] = { .type = NLA_NESTED },
|
||||
[CTA_ZONE] = { .type = NLA_U16 },
|
||||
[CTA_MARK_MASK] = { .type = NLA_U32 },
|
||||
[CTA_LABELS] = { .type = NLA_BINARY,
|
||||
.len = __CTA_LABELS_MAX_LENGTH },
|
||||
[CTA_LABELS_MASK] = { .type = NLA_BINARY,
|
||||
.len = __CTA_LABELS_MAX_LENGTH },
|
||||
};
|
||||
|
||||
static int
|
||||
@ -1504,6 +1509,31 @@ ctnetlink_change_nat_seq_adj(struct nf_conn *ct,
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
ctnetlink_attach_labels(struct nf_conn *ct, const struct nlattr * const cda[])
|
||||
{
|
||||
#ifdef CONFIG_NF_CONNTRACK_LABELS
|
||||
size_t len = nla_len(cda[CTA_LABELS]);
|
||||
const void *mask = cda[CTA_LABELS_MASK];
|
||||
|
||||
if (len & (sizeof(u32)-1)) /* must be multiple of u32 */
|
||||
return -EINVAL;
|
||||
|
||||
if (mask) {
|
||||
if (nla_len(cda[CTA_LABELS_MASK]) == 0 ||
|
||||
nla_len(cda[CTA_LABELS_MASK]) != len)
|
||||
return -EINVAL;
|
||||
mask = nla_data(cda[CTA_LABELS_MASK]);
|
||||
}
|
||||
|
||||
len /= sizeof(u32);
|
||||
|
||||
return nf_connlabels_replace(ct, nla_data(cda[CTA_LABELS]), mask, len);
|
||||
#else
|
||||
return -EOPNOTSUPP;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int
|
||||
ctnetlink_change_conntrack(struct nf_conn *ct,
|
||||
const struct nlattr * const cda[])
|
||||
@ -1550,6 +1580,11 @@ ctnetlink_change_conntrack(struct nf_conn *ct,
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
if (cda[CTA_LABELS]) {
|
||||
err = ctnetlink_attach_labels(ct, cda);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1758,6 +1793,10 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
|
||||
else
|
||||
events = IPCT_NEW;
|
||||
|
||||
if (cda[CTA_LABELS] &&
|
||||
ctnetlink_attach_labels(ct, cda) == 0)
|
||||
events |= (1 << IPCT_LABEL);
|
||||
|
||||
nf_conntrack_eventmask_report((1 << IPCT_REPLY) |
|
||||
(1 << IPCT_ASSURED) |
|
||||
(1 << IPCT_HELPER) |
|
||||
@ -2055,6 +2094,11 @@ ctnetlink_nfqueue_parse_ct(const struct nlattr *cda[], struct nf_conn *ct)
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
if (cda[CTA_LABELS]) {
|
||||
err = ctnetlink_attach_labels(ct, cda);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
#if defined(CONFIG_NF_CONNTRACK_MARK)
|
||||
if (cda[CTA_MARK])
|
||||
ct->mark = ntohl(nla_get_be32(cda[CTA_MARK]));
|
||||
|
Loading…
Reference in New Issue
Block a user