Skip to content

Commit 9971a51

Browse files
Florian Westphalummakynes
authored andcommitted
netfilter: nf_nat: add nat type hooks to nat core
Currently the packet rewrite and instantiation of nat NULL bindings happens from the protocol specific nat backend. Invocation occurs either via ip(6)table_nat or the nf_tables nat chain type. Invocation looks like this (simplified): NF_HOOK() | `---iptable_nat | `---> nf_nat_l3proto_ipv4 -> nf_nat_packet | new packet? pass skb though iptables nat chain | `---> iptable_nat: ipt_do_table In nft case, this looks the same (nft_chain_nat_ipv4 instead of iptable_nat). This is a problem for two reasons: 1. Can't use iptables nat and nf_tables nat at the same time, as the first user adds a nat binding (nf_nat_l3proto_ipv4 adds a NULL binding if do_table() did not find a matching nat rule so we can detect post-nat tuple collisions). 2. If you use e.g. nft_masq, snat, redir, etc. uses must also register an empty base chain so that the nat core gets called fro NF_HOOK() to do the reverse translation, which is neither obvious nor user friendly. After this change, the base hook gets registered not from iptable_nat or nftables nat hooks, but from the l3 nat core. iptables/nft nat base hooks get registered with the nat core instead: NF_HOOK() | `---> nf_nat_l3proto_ipv4 -> nf_nat_packet | new packet? pass skb through iptables/nftables nat chains | +-> iptables_nat: ipt_do_table +-> nft nat chain x `-> nft nat chain y The nat core deals with null bindings and reverse translation. When no mapping exists, it calls the registered nat lookup hooks until one creates a new mapping. If both iptables and nftables nat hooks exist, the first matching one is used (i.e., higher priority wins). Also, nft users do not need to create empty nat hooks anymore, nat core always registers the base hooks that take care of reverse/reply translation. Signed-off-by: Florian Westphal <[email protected]> Signed-off-by: Pablo Neira Ayuso <[email protected]>
1 parent 1cd472b commit 9971a51

File tree

9 files changed

+232
-289
lines changed

9 files changed

+232
-289
lines changed

include/net/netfilter/nf_nat_core.h

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,7 @@ unsigned int nf_nat_packet(struct nf_conn *ct, enum ip_conntrack_info ctinfo,
1313

1414
unsigned int
1515
nf_nat_inet_fn(void *priv, struct sk_buff *skb,
16-
const struct nf_hook_state *state,
17-
unsigned int (*do_chain)(void *priv,
18-
struct sk_buff *skb,
19-
const struct nf_hook_state *state));
16+
const struct nf_hook_state *state);
2017

2118
int nf_xfrm_me_harder(struct net *net, struct sk_buff *skb, unsigned int family);
2219

include/net/netfilter/nf_nat_l3proto.h

Lines changed: 4 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -44,58 +44,14 @@ int nf_nat_icmp_reply_translation(struct sk_buff *skb, struct nf_conn *ct,
4444
enum ip_conntrack_info ctinfo,
4545
unsigned int hooknum);
4646

47-
unsigned int nf_nat_ipv4_in(void *priv, struct sk_buff *skb,
48-
const struct nf_hook_state *state,
49-
unsigned int (*do_chain)(void *priv,
50-
struct sk_buff *skb,
51-
const struct nf_hook_state *state));
52-
53-
unsigned int nf_nat_ipv4_out(void *priv, struct sk_buff *skb,
54-
const struct nf_hook_state *state,
55-
unsigned int (*do_chain)(void *priv,
56-
struct sk_buff *skb,
57-
const struct nf_hook_state *state));
58-
59-
unsigned int nf_nat_ipv4_local_fn(void *priv,
60-
struct sk_buff *skb,
61-
const struct nf_hook_state *state,
62-
unsigned int (*do_chain)(void *priv,
63-
struct sk_buff *skb,
64-
const struct nf_hook_state *state));
65-
66-
unsigned int nf_nat_ipv4_fn(void *priv, struct sk_buff *skb,
67-
const struct nf_hook_state *state,
68-
unsigned int (*do_chain)(void *priv,
69-
struct sk_buff *skb,
70-
const struct nf_hook_state *state));
71-
7247
int nf_nat_icmpv6_reply_translation(struct sk_buff *skb, struct nf_conn *ct,
7348
enum ip_conntrack_info ctinfo,
7449
unsigned int hooknum, unsigned int hdrlen);
7550

76-
unsigned int nf_nat_ipv6_in(void *priv, struct sk_buff *skb,
77-
const struct nf_hook_state *state,
78-
unsigned int (*do_chain)(void *priv,
79-
struct sk_buff *skb,
80-
const struct nf_hook_state *state));
81-
82-
unsigned int nf_nat_ipv6_out(void *priv, struct sk_buff *skb,
83-
const struct nf_hook_state *state,
84-
unsigned int (*do_chain)(void *priv,
85-
struct sk_buff *skb,
86-
const struct nf_hook_state *state));
87-
88-
unsigned int nf_nat_ipv6_local_fn(void *priv,
89-
struct sk_buff *skb,
90-
const struct nf_hook_state *state,
91-
unsigned int (*do_chain)(void *priv,
92-
struct sk_buff *skb,
93-
const struct nf_hook_state *state));
51+
int nf_nat_l3proto_ipv4_register_fn(struct net *net, const struct nf_hook_ops *ops);
52+
void nf_nat_l3proto_ipv4_unregister_fn(struct net *net, const struct nf_hook_ops *ops);
9453

95-
unsigned int nf_nat_ipv6_fn(void *priv, struct sk_buff *skb,
96-
const struct nf_hook_state *state,
97-
unsigned int (*do_chain)(void *priv,
98-
struct sk_buff *skb,
99-
const struct nf_hook_state *state));
54+
int nf_nat_l3proto_ipv6_register_fn(struct net *net, const struct nf_hook_ops *ops);
55+
void nf_nat_l3proto_ipv6_unregister_fn(struct net *net, const struct nf_hook_ops *ops);
10056

10157
#endif /* _NF_NAT_L3PROTO_H */

net/ipv4/netfilter/iptable_nat.c

Lines changed: 43 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -38,69 +38,58 @@ static unsigned int iptable_nat_do_chain(void *priv,
3838
return ipt_do_table(skb, state, state->net->ipv4.nat_table);
3939
}
4040

41-
static unsigned int iptable_nat_ipv4_fn(void *priv,
42-
struct sk_buff *skb,
43-
const struct nf_hook_state *state)
44-
{
45-
return nf_nat_ipv4_fn(priv, skb, state, iptable_nat_do_chain);
46-
}
47-
48-
static unsigned int iptable_nat_ipv4_in(void *priv,
49-
struct sk_buff *skb,
50-
const struct nf_hook_state *state)
51-
{
52-
return nf_nat_ipv4_in(priv, skb, state, iptable_nat_do_chain);
53-
}
54-
55-
static unsigned int iptable_nat_ipv4_out(void *priv,
56-
struct sk_buff *skb,
57-
const struct nf_hook_state *state)
58-
{
59-
return nf_nat_ipv4_out(priv, skb, state, iptable_nat_do_chain);
60-
}
61-
62-
static unsigned int iptable_nat_ipv4_local_fn(void *priv,
63-
struct sk_buff *skb,
64-
const struct nf_hook_state *state)
65-
{
66-
return nf_nat_ipv4_local_fn(priv, skb, state, iptable_nat_do_chain);
67-
}
68-
6941
static const struct nf_hook_ops nf_nat_ipv4_ops[] = {
70-
/* Before packet filtering, change destination */
7142
{
72-
.hook = iptable_nat_ipv4_in,
43+
.hook = iptable_nat_do_chain,
7344
.pf = NFPROTO_IPV4,
74-
.nat_hook = true,
7545
.hooknum = NF_INET_PRE_ROUTING,
7646
.priority = NF_IP_PRI_NAT_DST,
7747
},
78-
/* After packet filtering, change source */
7948
{
80-
.hook = iptable_nat_ipv4_out,
49+
.hook = iptable_nat_do_chain,
8150
.pf = NFPROTO_IPV4,
82-
.nat_hook = true,
8351
.hooknum = NF_INET_POST_ROUTING,
8452
.priority = NF_IP_PRI_NAT_SRC,
8553
},
86-
/* Before packet filtering, change destination */
8754
{
88-
.hook = iptable_nat_ipv4_local_fn,
55+
.hook = iptable_nat_do_chain,
8956
.pf = NFPROTO_IPV4,
90-
.nat_hook = true,
9157
.hooknum = NF_INET_LOCAL_OUT,
9258
.priority = NF_IP_PRI_NAT_DST,
9359
},
94-
/* After packet filtering, change source */
9560
{
96-
.hook = iptable_nat_ipv4_fn,
61+
.hook = iptable_nat_do_chain,
9762
.pf = NFPROTO_IPV4,
98-
.nat_hook = true,
9963
.hooknum = NF_INET_LOCAL_IN,
10064
.priority = NF_IP_PRI_NAT_SRC,
10165
},
10266
};
10367

68+
static int ipt_nat_register_lookups(struct net *net)
69+
{
70+
int i, ret;
71+
72+
for (i = 0; i < ARRAY_SIZE(nf_nat_ipv4_ops); i++) {
73+
ret = nf_nat_l3proto_ipv4_register_fn(net, &nf_nat_ipv4_ops[i]);
74+
if (ret) {
75+
while (i)
76+
nf_nat_l3proto_ipv4_unregister_fn(net, &nf_nat_ipv4_ops[--i]);
77+
78+
return ret;
79+
}
80+
}
81+
82+
return 0;
83+
}
84+
85+
static void ipt_nat_unregister_lookups(struct net *net)
86+
{
87+
int i;
88+
89+
for (i = 0; i < ARRAY_SIZE(nf_nat_ipv4_ops); i++)
90+
nf_nat_l3proto_ipv4_unregister_fn(net, &nf_nat_ipv4_ops[i]);
91+
}
92+
10493
static int __net_init iptable_nat_table_init(struct net *net)
10594
{
10695
struct ipt_replace *repl;
@@ -113,7 +102,18 @@ static int __net_init iptable_nat_table_init(struct net *net)
113102
if (repl == NULL)
114103
return -ENOMEM;
115104
ret = ipt_register_table(net, &nf_nat_ipv4_table, repl,
116-
nf_nat_ipv4_ops, &net->ipv4.nat_table);
105+
NULL, &net->ipv4.nat_table);
106+
if (ret < 0) {
107+
kfree(repl);
108+
return ret;
109+
}
110+
111+
ret = ipt_nat_register_lookups(net);
112+
if (ret < 0) {
113+
ipt_unregister_table(net, net->ipv4.nat_table, NULL);
114+
net->ipv4.nat_table = NULL;
115+
}
116+
117117
kfree(repl);
118118
return ret;
119119
}
@@ -122,7 +122,8 @@ static void __net_exit iptable_nat_net_exit(struct net *net)
122122
{
123123
if (!net->ipv4.nat_table)
124124
return;
125-
ipt_unregister_table(net, net->ipv4.nat_table, nf_nat_ipv4_ops);
125+
ipt_nat_unregister_lookups(net);
126+
ipt_unregister_table(net, net->ipv4.nat_table, NULL);
126127
net->ipv4.nat_table = NULL;
127128
}
128129

net/ipv4/netfilter/nf_nat_l3proto_ipv4.c

Lines changed: 55 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -241,12 +241,9 @@ int nf_nat_icmp_reply_translation(struct sk_buff *skb,
241241
}
242242
EXPORT_SYMBOL_GPL(nf_nat_icmp_reply_translation);
243243

244-
unsigned int
244+
static unsigned int
245245
nf_nat_ipv4_fn(void *priv, struct sk_buff *skb,
246-
const struct nf_hook_state *state,
247-
unsigned int (*do_chain)(void *priv,
248-
struct sk_buff *skb,
249-
const struct nf_hook_state *state))
246+
const struct nf_hook_state *state)
250247
{
251248
struct nf_conn *ct;
252249
enum ip_conntrack_info ctinfo;
@@ -265,35 +262,28 @@ nf_nat_ipv4_fn(void *priv, struct sk_buff *skb,
265262
}
266263
}
267264

268-
return nf_nat_inet_fn(priv, skb, state, do_chain);
265+
return nf_nat_inet_fn(priv, skb, state);
269266
}
270267
EXPORT_SYMBOL_GPL(nf_nat_ipv4_fn);
271268

272-
unsigned int
269+
static unsigned int
273270
nf_nat_ipv4_in(void *priv, struct sk_buff *skb,
274-
const struct nf_hook_state *state,
275-
unsigned int (*do_chain)(void *priv,
276-
struct sk_buff *skb,
277-
const struct nf_hook_state *state))
271+
const struct nf_hook_state *state)
278272
{
279273
unsigned int ret;
280274
__be32 daddr = ip_hdr(skb)->daddr;
281275

282-
ret = nf_nat_ipv4_fn(priv, skb, state, do_chain);
276+
ret = nf_nat_ipv4_fn(priv, skb, state);
283277
if (ret != NF_DROP && ret != NF_STOLEN &&
284278
daddr != ip_hdr(skb)->daddr)
285279
skb_dst_drop(skb);
286280

287281
return ret;
288282
}
289-
EXPORT_SYMBOL_GPL(nf_nat_ipv4_in);
290283

291-
unsigned int
284+
static unsigned int
292285
nf_nat_ipv4_out(void *priv, struct sk_buff *skb,
293-
const struct nf_hook_state *state,
294-
unsigned int (*do_chain)(void *priv,
295-
struct sk_buff *skb,
296-
const struct nf_hook_state *state))
286+
const struct nf_hook_state *state)
297287
{
298288
#ifdef CONFIG_XFRM
299289
const struct nf_conn *ct;
@@ -302,7 +292,7 @@ nf_nat_ipv4_out(void *priv, struct sk_buff *skb,
302292
#endif
303293
unsigned int ret;
304294

305-
ret = nf_nat_ipv4_fn(priv, skb, state, do_chain);
295+
ret = nf_nat_ipv4_fn(priv, skb, state);
306296
#ifdef CONFIG_XFRM
307297
if (ret != NF_DROP && ret != NF_STOLEN &&
308298
!(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) &&
@@ -322,21 +312,17 @@ nf_nat_ipv4_out(void *priv, struct sk_buff *skb,
322312
#endif
323313
return ret;
324314
}
325-
EXPORT_SYMBOL_GPL(nf_nat_ipv4_out);
326315

327-
unsigned int
316+
static unsigned int
328317
nf_nat_ipv4_local_fn(void *priv, struct sk_buff *skb,
329-
const struct nf_hook_state *state,
330-
unsigned int (*do_chain)(void *priv,
331-
struct sk_buff *skb,
332-
const struct nf_hook_state *state))
318+
const struct nf_hook_state *state)
333319
{
334320
const struct nf_conn *ct;
335321
enum ip_conntrack_info ctinfo;
336322
unsigned int ret;
337323
int err;
338324

339-
ret = nf_nat_ipv4_fn(priv, skb, state, do_chain);
325+
ret = nf_nat_ipv4_fn(priv, skb, state);
340326
if (ret != NF_DROP && ret != NF_STOLEN &&
341327
(ct = nf_ct_get(skb, &ctinfo)) != NULL) {
342328
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
@@ -360,7 +346,49 @@ nf_nat_ipv4_local_fn(void *priv, struct sk_buff *skb,
360346
}
361347
return ret;
362348
}
363-
EXPORT_SYMBOL_GPL(nf_nat_ipv4_local_fn);
349+
350+
static const struct nf_hook_ops nf_nat_ipv4_ops[] = {
351+
/* Before packet filtering, change destination */
352+
{
353+
.hook = nf_nat_ipv4_in,
354+
.pf = NFPROTO_IPV4,
355+
.hooknum = NF_INET_PRE_ROUTING,
356+
.priority = NF_IP_PRI_NAT_DST,
357+
},
358+
/* After packet filtering, change source */
359+
{
360+
.hook = nf_nat_ipv4_out,
361+
.pf = NFPROTO_IPV4,
362+
.hooknum = NF_INET_POST_ROUTING,
363+
.priority = NF_IP_PRI_NAT_SRC,
364+
},
365+
/* Before packet filtering, change destination */
366+
{
367+
.hook = nf_nat_ipv4_local_fn,
368+
.pf = NFPROTO_IPV4,
369+
.hooknum = NF_INET_LOCAL_OUT,
370+
.priority = NF_IP_PRI_NAT_DST,
371+
},
372+
/* After packet filtering, change source */
373+
{
374+
.hook = nf_nat_ipv4_fn,
375+
.pf = NFPROTO_IPV4,
376+
.hooknum = NF_INET_LOCAL_IN,
377+
.priority = NF_IP_PRI_NAT_SRC,
378+
},
379+
};
380+
381+
int nf_nat_l3proto_ipv4_register_fn(struct net *net, const struct nf_hook_ops *ops)
382+
{
383+
return nf_nat_register_fn(net, ops, nf_nat_ipv4_ops, ARRAY_SIZE(nf_nat_ipv4_ops));
384+
}
385+
EXPORT_SYMBOL_GPL(nf_nat_l3proto_ipv4_register_fn);
386+
387+
void nf_nat_l3proto_ipv4_unregister_fn(struct net *net, const struct nf_hook_ops *ops)
388+
{
389+
nf_nat_unregister_fn(net, ops, ARRAY_SIZE(nf_nat_ipv4_ops));
390+
}
391+
EXPORT_SYMBOL_GPL(nf_nat_l3proto_ipv4_unregister_fn);
364392

365393
static int __init nf_nat_l3proto_ipv4_init(void)
366394
{

0 commit comments

Comments
 (0)