Skip to content

Commit 5e8018f

Browse files
borkmannummakynes
authored andcommitted
netfilter: nf_conntrack: add efficient mark to zone mapping
This work adds the possibility of deriving the zone id from the skb->mark field in a scalable manner. This allows for having only a single template serving hundreds/thousands of different zones, for example, instead of the need to have one match for each zone as an extra CT jump target. Note that we'd need to have this information attached to the template as at the time when we're trying to lookup a possible ct object, we already need to know zone information for a possible match when going into __nf_conntrack_find_get(). This work provides a minimal implementation for a possible mapping. In order to not add/expose an extra ct->status bit, the zone structure has been extended to carry a flag for deriving the mark. Signed-off-by: Daniel Borkmann <[email protected]> Signed-off-by: Pablo Neira Ayuso <[email protected]>
1 parent deedb59 commit 5e8018f

File tree

7 files changed

+72
-44
lines changed

7 files changed

+72
-44
lines changed

include/net/netfilter/nf_conntrack_zones.h

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@
1010

1111
#define NF_CT_DEFAULT_ZONE_DIR (NF_CT_ZONE_DIR_ORIG | NF_CT_ZONE_DIR_REPL)
1212

13+
#define NF_CT_FLAG_MARK 1
14+
1315
struct nf_conntrack_zone {
1416
u16 id;
15-
u16 dir;
17+
u8 flags;
18+
u8 dir;
1619
};
1720

1821
extern const struct nf_conntrack_zone nf_ct_zone_dflt;
@@ -32,9 +35,45 @@ nf_ct_zone(const struct nf_conn *ct)
3235
}
3336

3437
static inline const struct nf_conntrack_zone *
35-
nf_ct_zone_tmpl(const struct nf_conn *tmpl)
38+
nf_ct_zone_init(struct nf_conntrack_zone *zone, u16 id, u8 dir, u8 flags)
39+
{
40+
zone->id = id;
41+
zone->flags = flags;
42+
zone->dir = dir;
43+
44+
return zone;
45+
}
46+
47+
static inline const struct nf_conntrack_zone *
48+
nf_ct_zone_tmpl(const struct nf_conn *tmpl, const struct sk_buff *skb,
49+
struct nf_conntrack_zone *tmp)
50+
{
51+
const struct nf_conntrack_zone *zone;
52+
53+
if (!tmpl)
54+
return &nf_ct_zone_dflt;
55+
56+
zone = nf_ct_zone(tmpl);
57+
if (zone->flags & NF_CT_FLAG_MARK)
58+
zone = nf_ct_zone_init(tmp, skb->mark, zone->dir, 0);
59+
60+
return zone;
61+
}
62+
63+
static inline int nf_ct_zone_add(struct nf_conn *ct, gfp_t flags,
64+
const struct nf_conntrack_zone *info)
3665
{
37-
return tmpl ? nf_ct_zone(tmpl) : &nf_ct_zone_dflt;
66+
#ifdef CONFIG_NF_CONNTRACK_ZONES
67+
struct nf_conntrack_zone *nf_ct_zone;
68+
69+
nf_ct_zone = nf_ct_ext_add(ct, NF_CT_EXT_ZONE, flags);
70+
if (!nf_ct_zone)
71+
return -ENOMEM;
72+
73+
nf_ct_zone_init(nf_ct_zone, info->id, info->dir,
74+
info->flags);
75+
#endif
76+
return 0;
3877
}
3978

4079
static inline bool nf_ct_zone_matches_dir(const struct nf_conntrack_zone *zone,

include/uapi/linux/netfilter/xt_CT.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@ enum {
88
XT_CT_NOTRACK_ALIAS = 1 << 1,
99
XT_CT_ZONE_DIR_ORIG = 1 << 2,
1010
XT_CT_ZONE_DIR_REPL = 1 << 3,
11+
XT_CT_ZONE_MARK = 1 << 4,
1112

1213
XT_CT_MASK = XT_CT_NOTRACK | XT_CT_NOTRACK_ALIAS |
13-
XT_CT_ZONE_DIR_ORIG | XT_CT_ZONE_DIR_REPL,
14+
XT_CT_ZONE_DIR_ORIG | XT_CT_ZONE_DIR_REPL |
15+
XT_CT_ZONE_MARK,
1416
};
1517

1618
struct xt_ct_target_info {

net/ipv4/netfilter/nf_conntrack_proto_icmp.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,9 +135,10 @@ icmp_error_message(struct net *net, struct nf_conn *tmpl, struct sk_buff *skb,
135135
const struct nf_conntrack_l4proto *innerproto;
136136
const struct nf_conntrack_tuple_hash *h;
137137
const struct nf_conntrack_zone *zone;
138+
struct nf_conntrack_zone tmp;
138139

139140
NF_CT_ASSERT(skb->nfct == NULL);
140-
zone = nf_ct_zone_tmpl(tmpl);
141+
zone = nf_ct_zone_tmpl(tmpl, skb, &tmp);
141142

142143
/* Are they talking about one of our connections? */
143144
if (!nf_ct_get_tuplepr(skb,

net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ icmpv6_error_message(struct net *net, struct nf_conn *tmpl,
150150
struct nf_conntrack_tuple intuple, origtuple;
151151
const struct nf_conntrack_tuple_hash *h;
152152
const struct nf_conntrack_l4proto *inproto;
153+
struct nf_conntrack_zone tmp;
153154

154155
NF_CT_ASSERT(skb->nfct == NULL);
155156

@@ -176,7 +177,8 @@ icmpv6_error_message(struct net *net, struct nf_conn *tmpl,
176177

177178
*ctinfo = IP_CT_RELATED;
178179

179-
h = nf_conntrack_find_get(net, nf_ct_zone_tmpl(tmpl), &intuple);
180+
h = nf_conntrack_find_get(net, nf_ct_zone_tmpl(tmpl, skb, &tmp),
181+
&intuple);
180182
if (!h) {
181183
pr_debug("icmpv6_error: no match\n");
182184
return -NF_ACCEPT;

net/netfilter/nf_conntrack_core.c

Lines changed: 16 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -301,25 +301,15 @@ struct nf_conn *nf_ct_tmpl_alloc(struct net *net,
301301
tmpl->status = IPS_TEMPLATE;
302302
write_pnet(&tmpl->ct_net, net);
303303

304-
#ifdef CONFIG_NF_CONNTRACK_ZONES
305-
if (zone) {
306-
struct nf_conntrack_zone *nf_ct_zone;
307-
308-
nf_ct_zone = nf_ct_ext_add(tmpl, NF_CT_EXT_ZONE, GFP_ATOMIC);
309-
if (!nf_ct_zone)
310-
goto out_free;
311-
nf_ct_zone->id = zone->id;
312-
nf_ct_zone->dir = zone->dir;
313-
}
314-
#endif
304+
if (nf_ct_zone_add(tmpl, flags, zone) < 0)
305+
goto out_free;
306+
315307
atomic_set(&tmpl->ct_general.use, 0);
316308

317309
return tmpl;
318-
#ifdef CONFIG_NF_CONNTRACK_ZONES
319310
out_free:
320311
kfree(tmpl);
321312
return NULL;
322-
#endif
323313
}
324314
EXPORT_SYMBOL_GPL(nf_ct_tmpl_alloc);
325315

@@ -850,10 +840,9 @@ __nf_conntrack_alloc(struct net *net,
850840
* SLAB_DESTROY_BY_RCU.
851841
*/
852842
ct = kmem_cache_alloc(net->ct.nf_conntrack_cachep, gfp);
853-
if (ct == NULL) {
854-
atomic_dec(&net->ct.count);
855-
return ERR_PTR(-ENOMEM);
856-
}
843+
if (ct == NULL)
844+
goto out;
845+
857846
spin_lock_init(&ct->lock);
858847
ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple = *orig;
859848
ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode.pprev = NULL;
@@ -867,29 +856,20 @@ __nf_conntrack_alloc(struct net *net,
867856
memset(&ct->__nfct_init_offset[0], 0,
868857
offsetof(struct nf_conn, proto) -
869858
offsetof(struct nf_conn, __nfct_init_offset[0]));
870-
#ifdef CONFIG_NF_CONNTRACK_ZONES
871-
if (zone) {
872-
struct nf_conntrack_zone *nf_ct_zone;
873-
874-
nf_ct_zone = nf_ct_ext_add(ct, NF_CT_EXT_ZONE, GFP_ATOMIC);
875-
if (!nf_ct_zone)
876-
goto out_free;
877-
nf_ct_zone->id = zone->id;
878-
nf_ct_zone->dir = zone->dir;
879-
}
880-
#endif
859+
860+
if (zone && nf_ct_zone_add(ct, GFP_ATOMIC, zone) < 0)
861+
goto out_free;
862+
881863
/* Because we use RCU lookups, we set ct_general.use to zero before
882864
* this is inserted in any list.
883865
*/
884866
atomic_set(&ct->ct_general.use, 0);
885867
return ct;
886-
887-
#ifdef CONFIG_NF_CONNTRACK_ZONES
888868
out_free:
889-
atomic_dec(&net->ct.count);
890869
kmem_cache_free(net->ct.nf_conntrack_cachep, ct);
870+
out:
871+
atomic_dec(&net->ct.count);
891872
return ERR_PTR(-ENOMEM);
892-
#endif
893873
}
894874

895875
struct nf_conn *nf_conntrack_alloc(struct net *net,
@@ -937,14 +917,15 @@ init_conntrack(struct net *net, struct nf_conn *tmpl,
937917
struct nf_conntrack_expect *exp = NULL;
938918
const struct nf_conntrack_zone *zone;
939919
struct nf_conn_timeout *timeout_ext;
920+
struct nf_conntrack_zone tmp;
940921
unsigned int *timeouts;
941922

942923
if (!nf_ct_invert_tuple(&repl_tuple, tuple, l3proto, l4proto)) {
943924
pr_debug("Can't invert tuple.\n");
944925
return NULL;
945926
}
946927

947-
zone = nf_ct_zone_tmpl(tmpl);
928+
zone = nf_ct_zone_tmpl(tmpl, skb, &tmp);
948929
ct = __nf_conntrack_alloc(net, zone, tuple, &repl_tuple, GFP_ATOMIC,
949930
hash);
950931
if (IS_ERR(ct))
@@ -1042,6 +1023,7 @@ resolve_normal_ct(struct net *net, struct nf_conn *tmpl,
10421023
const struct nf_conntrack_zone *zone;
10431024
struct nf_conntrack_tuple tuple;
10441025
struct nf_conntrack_tuple_hash *h;
1026+
struct nf_conntrack_zone tmp;
10451027
struct nf_conn *ct;
10461028
u32 hash;
10471029

@@ -1053,7 +1035,7 @@ resolve_normal_ct(struct net *net, struct nf_conn *tmpl,
10531035
}
10541036

10551037
/* look for tuple match */
1056-
zone = nf_ct_zone_tmpl(tmpl);
1038+
zone = nf_ct_zone_tmpl(tmpl, skb, &tmp);
10571039
hash = hash_conntrack_raw(&tuple);
10581040
h = __nf_conntrack_find_get(net, zone, &tuple, hash);
10591041
if (!h) {

net/netfilter/nf_conntrack_netlink.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -956,9 +956,8 @@ static int
956956
ctnetlink_parse_zone(const struct nlattr *attr,
957957
struct nf_conntrack_zone *zone)
958958
{
959-
zone->id = NF_CT_DEFAULT_ZONE_ID;
960-
zone->dir = NF_CT_DEFAULT_ZONE_DIR;
961-
959+
nf_ct_zone_init(zone, NF_CT_DEFAULT_ZONE_ID,
960+
NF_CT_DEFAULT_ZONE_DIR, 0);
962961
#ifdef CONFIG_NF_CONNTRACK_ZONES
963962
if (attr)
964963
zone->id = ntohs(nla_get_be16(attr));

net/netfilter/xt_CT.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,8 @@ static int xt_ct_tg_check(const struct xt_tgchk_param *par,
208208

209209
#ifndef CONFIG_NF_CONNTRACK_ZONES
210210
if (info->zone || info->flags & (XT_CT_ZONE_DIR_ORIG |
211-
XT_CT_ZONE_DIR_REPL))
211+
XT_CT_ZONE_DIR_REPL |
212+
XT_CT_ZONE_MARK))
212213
goto err1;
213214
#endif
214215

@@ -219,6 +220,8 @@ static int xt_ct_tg_check(const struct xt_tgchk_param *par,
219220
memset(&zone, 0, sizeof(zone));
220221
zone.id = info->zone;
221222
zone.dir = xt_ct_flags_to_dir(info);
223+
if (info->flags & XT_CT_ZONE_MARK)
224+
zone.flags |= NF_CT_FLAG_MARK;
222225

223226
ct = nf_ct_tmpl_alloc(par->net, &zone, GFP_KERNEL);
224227
ret = PTR_ERR(ct);

0 commit comments

Comments
 (0)