Commit 23014011 authored by Florian Westphal's avatar Florian Westphal Committed by Pablo Neira Ayuso

netfilter: conntrack: support a fixed size of 128 distinct labels

The conntrack label extension is currently variable-sized, e.g. if
only 2 labels are used by iptables rules then the labels->bits[] array
will only contain one element.

We track size of each label storage area in the 'words' member.

But in nftables and openvswitch we always have to ask for worst-case
since we don't know what bit will be used at configuration time.

As most arches are 64bit we need to allocate 24 bytes in this case:

struct nf_conn_labels {
    u8            words;   /*     0     1 */
    /* XXX 7 bytes hole, try to pack */
    long unsigned bits[2]; /*     8     24 */

Make bits a fixed size and drop the words member, it simplifies
the code and only increases memory requirements on x86 when
less than 64bit labels are required.

We still only allocate the extension if its needed.
Signed-off-by: default avatarFlorian Westphal <fw@strlen.de>
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent 6e1f760e
...@@ -10,8 +10,7 @@ ...@@ -10,8 +10,7 @@
#define NF_CT_LABELS_MAX_SIZE ((XT_CONNLABEL_MAXBIT + 1) / BITS_PER_BYTE) #define NF_CT_LABELS_MAX_SIZE ((XT_CONNLABEL_MAXBIT + 1) / BITS_PER_BYTE)
struct nf_conn_labels { struct nf_conn_labels {
u8 words; unsigned long bits[NF_CT_LABELS_MAX_SIZE / sizeof(long)];
unsigned long bits[];
}; };
static inline struct nf_conn_labels *nf_ct_labels_find(const struct nf_conn *ct) static inline struct nf_conn_labels *nf_ct_labels_find(const struct nf_conn *ct)
...@@ -26,20 +25,13 @@ static inline struct nf_conn_labels *nf_ct_labels_find(const struct nf_conn *ct) ...@@ -26,20 +25,13 @@ static inline struct nf_conn_labels *nf_ct_labels_find(const struct nf_conn *ct)
static inline struct nf_conn_labels *nf_ct_labels_ext_add(struct nf_conn *ct) static inline struct nf_conn_labels *nf_ct_labels_ext_add(struct nf_conn *ct)
{ {
#ifdef CONFIG_NF_CONNTRACK_LABELS #ifdef CONFIG_NF_CONNTRACK_LABELS
struct nf_conn_labels *cl_ext;
struct net *net = nf_ct_net(ct); struct net *net = nf_ct_net(ct);
u8 words;
words = ACCESS_ONCE(net->ct.label_words); if (net->ct.labels_used == 0)
if (words == 0)
return NULL; return NULL;
cl_ext = nf_ct_ext_add_length(ct, NF_CT_EXT_LABELS, return nf_ct_ext_add_length(ct, NF_CT_EXT_LABELS,
words * sizeof(long), GFP_ATOMIC); sizeof(struct nf_conn_labels), GFP_ATOMIC);
if (cl_ext != NULL)
cl_ext->words = words;
return cl_ext;
#else #else
return NULL; return NULL;
#endif #endif
......
...@@ -20,7 +20,7 @@ int nf_connlabel_set(struct nf_conn *ct, u16 bit) ...@@ -20,7 +20,7 @@ int nf_connlabel_set(struct nf_conn *ct, u16 bit)
{ {
struct nf_conn_labels *labels = nf_ct_labels_find(ct); struct nf_conn_labels *labels = nf_ct_labels_find(ct);
if (!labels || BIT_WORD(bit) >= labels->words) if (!labels)
return -ENOSPC; return -ENOSPC;
if (test_bit(bit, labels->bits)) if (test_bit(bit, labels->bits))
...@@ -60,7 +60,7 @@ int nf_connlabels_replace(struct nf_conn *ct, ...@@ -60,7 +60,7 @@ int nf_connlabels_replace(struct nf_conn *ct,
if (!labels) if (!labels)
return -ENOSPC; return -ENOSPC;
size = labels->words * sizeof(long); size = sizeof(labels->bits);
if (size < (words32 * sizeof(u32))) if (size < (words32 * sizeof(u32)))
words32 = size / sizeof(u32); words32 = size / sizeof(u32);
...@@ -80,16 +80,11 @@ EXPORT_SYMBOL_GPL(nf_connlabels_replace); ...@@ -80,16 +80,11 @@ EXPORT_SYMBOL_GPL(nf_connlabels_replace);
int nf_connlabels_get(struct net *net, unsigned int bits) int nf_connlabels_get(struct net *net, unsigned int bits)
{ {
size_t words; if (BIT_WORD(bits) >= NF_CT_LABELS_MAX_SIZE / sizeof(long))
words = BIT_WORD(bits) + 1;
if (words > NF_CT_LABELS_MAX_SIZE / sizeof(long))
return -ERANGE; return -ERANGE;
spin_lock(&nf_connlabels_lock); spin_lock(&nf_connlabels_lock);
net->ct.labels_used++; net->ct.labels_used++;
if (words > net->ct.label_words)
net->ct.label_words = words;
spin_unlock(&nf_connlabels_lock); spin_unlock(&nf_connlabels_lock);
return 0; return 0;
...@@ -100,8 +95,6 @@ void nf_connlabels_put(struct net *net) ...@@ -100,8 +95,6 @@ void nf_connlabels_put(struct net *net)
{ {
spin_lock(&nf_connlabels_lock); spin_lock(&nf_connlabels_lock);
net->ct.labels_used--; net->ct.labels_used--;
if (net->ct.labels_used == 0)
net->ct.label_words = 0;
spin_unlock(&nf_connlabels_lock); spin_unlock(&nf_connlabels_lock);
} }
EXPORT_SYMBOL_GPL(nf_connlabels_put); EXPORT_SYMBOL_GPL(nf_connlabels_put);
......
...@@ -346,25 +346,25 @@ static inline int ctnetlink_label_size(const struct nf_conn *ct) ...@@ -346,25 +346,25 @@ static inline int ctnetlink_label_size(const struct nf_conn *ct)
if (!labels) if (!labels)
return 0; return 0;
return nla_total_size(labels->words * sizeof(long)); return nla_total_size(sizeof(labels->bits));
} }
static int static int
ctnetlink_dump_labels(struct sk_buff *skb, const struct nf_conn *ct) ctnetlink_dump_labels(struct sk_buff *skb, const struct nf_conn *ct)
{ {
struct nf_conn_labels *labels = nf_ct_labels_find(ct); struct nf_conn_labels *labels = nf_ct_labels_find(ct);
unsigned int len, i; unsigned int i;
if (!labels) if (!labels)
return 0; return 0;
len = labels->words * sizeof(long);
i = 0; i = 0;
do { do {
if (labels->bits[i] != 0) if (labels->bits[i] != 0)
return nla_put(skb, CTA_LABELS, len, labels->bits); return nla_put(skb, CTA_LABELS, sizeof(labels->bits),
labels->bits);
i++; i++;
} while (i < labels->words); } while (i < ARRAY_SIZE(labels->bits));
return 0; return 0;
} }
......
...@@ -113,20 +113,13 @@ static void nft_ct_get_eval(const struct nft_expr *expr, ...@@ -113,20 +113,13 @@ static void nft_ct_get_eval(const struct nft_expr *expr,
#ifdef CONFIG_NF_CONNTRACK_LABELS #ifdef CONFIG_NF_CONNTRACK_LABELS
case NFT_CT_LABELS: { case NFT_CT_LABELS: {
struct nf_conn_labels *labels = nf_ct_labels_find(ct); struct nf_conn_labels *labels = nf_ct_labels_find(ct);
unsigned int size;
if (!labels) { if (labels)
memcpy(dest, labels->bits, NF_CT_LABELS_MAX_SIZE);
else
memset(dest, 0, NF_CT_LABELS_MAX_SIZE); memset(dest, 0, NF_CT_LABELS_MAX_SIZE);
return; return;
} }
size = labels->words * sizeof(long);
memcpy(dest, labels->bits, size);
if (size < NF_CT_LABELS_MAX_SIZE)
memset(((char *) dest) + size, 0,
NF_CT_LABELS_MAX_SIZE - size);
return;
}
#endif #endif
case NFT_CT_BYTES: /* fallthrough */ case NFT_CT_BYTES: /* fallthrough */
case NFT_CT_PKTS: { case NFT_CT_PKTS: {
......
...@@ -25,7 +25,7 @@ static bool connlabel_match(const struct nf_conn *ct, u16 bit) ...@@ -25,7 +25,7 @@ static bool connlabel_match(const struct nf_conn *ct, u16 bit)
if (!labels) if (!labels)
return false; return false;
return BIT_WORD(bit) < labels->words && test_bit(bit, labels->bits); return test_bit(bit, labels->bits);
} }
static bool static bool
......
...@@ -135,7 +135,7 @@ static void ovs_ct_get_labels(const struct nf_conn *ct, ...@@ -135,7 +135,7 @@ static void ovs_ct_get_labels(const struct nf_conn *ct,
struct nf_conn_labels *cl = ct ? nf_ct_labels_find(ct) : NULL; struct nf_conn_labels *cl = ct ? nf_ct_labels_find(ct) : NULL;
if (cl) { if (cl) {
size_t len = cl->words * sizeof(long); size_t len = sizeof(cl->bits);
if (len > OVS_CT_LABELS_LEN) if (len > OVS_CT_LABELS_LEN)
len = OVS_CT_LABELS_LEN; len = OVS_CT_LABELS_LEN;
...@@ -274,7 +274,7 @@ static int ovs_ct_set_labels(struct sk_buff *skb, struct sw_flow_key *key, ...@@ -274,7 +274,7 @@ static int ovs_ct_set_labels(struct sk_buff *skb, struct sw_flow_key *key,
nf_ct_labels_ext_add(ct); nf_ct_labels_ext_add(ct);
cl = nf_ct_labels_find(ct); cl = nf_ct_labels_find(ct);
} }
if (!cl || cl->words * sizeof(long) < OVS_CT_LABELS_LEN) if (!cl || sizeof(cl->bits) < OVS_CT_LABELS_LEN)
return -ENOSPC; return -ENOSPC;
err = nf_connlabels_replace(ct, (u32 *)labels, (u32 *)mask, err = nf_connlabels_replace(ct, (u32 *)labels, (u32 *)mask,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment