Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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 <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

authored by

Florian Westphal and committed by
Pablo Neira Ayuso
9971a514 1cd472bf

+230 -287
+1 -4
include/net/netfilter/nf_nat_core.h
··· 13 13 14 14 unsigned int 15 15 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); 20 17 21 18 int nf_xfrm_me_harder(struct net *net, struct sk_buff *skb, unsigned int family); 22 19
+4 -48
include/net/netfilter/nf_nat_l3proto.h
··· 44 44 enum ip_conntrack_info ctinfo, 45 45 unsigned int hooknum); 46 46 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 - 72 47 int nf_nat_icmpv6_reply_translation(struct sk_buff *skb, struct nf_conn *ct, 73 48 enum ip_conntrack_info ctinfo, 74 49 unsigned int hooknum, unsigned int hdrlen); 75 50 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)); 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); 81 53 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)); 94 - 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); 100 56 101 57 #endif /* _NF_NAT_L3PROTO_H */
+43 -42
net/ipv4/netfilter/iptable_nat.c
··· 38 38 return ipt_do_table(skb, state, state->net->ipv4.nat_table); 39 39 } 40 40 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 - 69 41 static const struct nf_hook_ops nf_nat_ipv4_ops[] = { 70 - /* Before packet filtering, change destination */ 71 42 { 72 - .hook = iptable_nat_ipv4_in, 43 + .hook = iptable_nat_do_chain, 73 44 .pf = NFPROTO_IPV4, 74 - .nat_hook = true, 75 45 .hooknum = NF_INET_PRE_ROUTING, 76 46 .priority = NF_IP_PRI_NAT_DST, 77 47 }, 78 - /* After packet filtering, change source */ 79 48 { 80 - .hook = iptable_nat_ipv4_out, 49 + .hook = iptable_nat_do_chain, 81 50 .pf = NFPROTO_IPV4, 82 - .nat_hook = true, 83 51 .hooknum = NF_INET_POST_ROUTING, 84 52 .priority = NF_IP_PRI_NAT_SRC, 85 53 }, 86 - /* Before packet filtering, change destination */ 87 54 { 88 - .hook = iptable_nat_ipv4_local_fn, 55 + .hook = iptable_nat_do_chain, 89 56 .pf = NFPROTO_IPV4, 90 - .nat_hook = true, 91 57 .hooknum = NF_INET_LOCAL_OUT, 92 58 .priority = NF_IP_PRI_NAT_DST, 93 59 }, 94 - /* After packet filtering, change source */ 95 60 { 96 - .hook = iptable_nat_ipv4_fn, 61 + .hook = iptable_nat_do_chain, 97 62 .pf = NFPROTO_IPV4, 98 - .nat_hook = true, 99 63 .hooknum = NF_INET_LOCAL_IN, 100 64 .priority = NF_IP_PRI_NAT_SRC, 101 65 }, 102 66 }; 67 + 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 + } 103 92 104 93 static int __net_init iptable_nat_table_init(struct net *net) 105 94 { ··· 102 113 if (repl == NULL) 103 114 return -ENOMEM; 104 115 ret = ipt_register_table(net, &nf_nat_ipv4_table, repl, 105 - nf_nat_ipv4_ops, &net->ipv4.nat_table); 116 + NULL, &net->ipv4.nat_table); 117 + if (ret < 0) { 118 + kfree(repl); 119 + return ret; 120 + } 121 + 122 + ret = ipt_nat_register_lookups(net); 123 + if (ret < 0) { 124 + ipt_unregister_table(net, net->ipv4.nat_table, NULL); 125 + net->ipv4.nat_table = NULL; 126 + } 127 + 106 128 kfree(repl); 107 129 return ret; 108 130 } ··· 122 122 { 123 123 if (!net->ipv4.nat_table) 124 124 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); 126 127 net->ipv4.nat_table = NULL; 127 128 } 128 129
+55 -27
net/ipv4/netfilter/nf_nat_l3proto_ipv4.c
··· 241 241 } 242 242 EXPORT_SYMBOL_GPL(nf_nat_icmp_reply_translation); 243 243 244 - unsigned int 244 + static unsigned int 245 245 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) 250 247 { 251 248 struct nf_conn *ct; 252 249 enum ip_conntrack_info ctinfo; ··· 262 265 } 263 266 } 264 267 265 - return nf_nat_inet_fn(priv, skb, state, do_chain); 268 + return nf_nat_inet_fn(priv, skb, state); 266 269 } 267 270 EXPORT_SYMBOL_GPL(nf_nat_ipv4_fn); 268 271 269 - unsigned int 272 + static unsigned int 270 273 nf_nat_ipv4_in(void *priv, struct sk_buff *skb, 271 - const struct nf_hook_state *state, 272 - unsigned int (*do_chain)(void *priv, 273 - struct sk_buff *skb, 274 - const struct nf_hook_state *state)) 274 + const struct nf_hook_state *state) 275 275 { 276 276 unsigned int ret; 277 277 __be32 daddr = ip_hdr(skb)->daddr; 278 278 279 - ret = nf_nat_ipv4_fn(priv, skb, state, do_chain); 279 + ret = nf_nat_ipv4_fn(priv, skb, state); 280 280 if (ret != NF_DROP && ret != NF_STOLEN && 281 281 daddr != ip_hdr(skb)->daddr) 282 282 skb_dst_drop(skb); 283 283 284 284 return ret; 285 285 } 286 - EXPORT_SYMBOL_GPL(nf_nat_ipv4_in); 287 286 288 - unsigned int 287 + static unsigned int 289 288 nf_nat_ipv4_out(void *priv, struct sk_buff *skb, 290 - const struct nf_hook_state *state, 291 - unsigned int (*do_chain)(void *priv, 292 - struct sk_buff *skb, 293 - const struct nf_hook_state *state)) 289 + const struct nf_hook_state *state) 294 290 { 295 291 #ifdef CONFIG_XFRM 296 292 const struct nf_conn *ct; ··· 292 302 #endif 293 303 unsigned int ret; 294 304 295 - ret = nf_nat_ipv4_fn(priv, skb, state, do_chain); 305 + ret = nf_nat_ipv4_fn(priv, skb, state); 296 306 #ifdef CONFIG_XFRM 297 307 if (ret != NF_DROP && ret != NF_STOLEN && 298 308 !(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) && ··· 312 322 #endif 313 323 return ret; 314 324 } 315 - EXPORT_SYMBOL_GPL(nf_nat_ipv4_out); 316 325 317 - unsigned int 326 + static unsigned int 318 327 nf_nat_ipv4_local_fn(void *priv, struct sk_buff *skb, 319 - const struct nf_hook_state *state, 320 - unsigned int (*do_chain)(void *priv, 321 - struct sk_buff *skb, 322 - const struct nf_hook_state *state)) 328 + const struct nf_hook_state *state) 323 329 { 324 330 const struct nf_conn *ct; 325 331 enum ip_conntrack_info ctinfo; 326 332 unsigned int ret; 327 333 int err; 328 334 329 - ret = nf_nat_ipv4_fn(priv, skb, state, do_chain); 335 + ret = nf_nat_ipv4_fn(priv, skb, state); 330 336 if (ret != NF_DROP && ret != NF_STOLEN && 331 337 (ct = nf_ct_get(skb, &ctinfo)) != NULL) { 332 338 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); ··· 346 360 } 347 361 return ret; 348 362 } 349 - EXPORT_SYMBOL_GPL(nf_nat_ipv4_local_fn); 363 + 364 + static const struct nf_hook_ops nf_nat_ipv4_ops[] = { 365 + /* Before packet filtering, change destination */ 366 + { 367 + .hook = nf_nat_ipv4_in, 368 + .pf = NFPROTO_IPV4, 369 + .hooknum = NF_INET_PRE_ROUTING, 370 + .priority = NF_IP_PRI_NAT_DST, 371 + }, 372 + /* After packet filtering, change source */ 373 + { 374 + .hook = nf_nat_ipv4_out, 375 + .pf = NFPROTO_IPV4, 376 + .hooknum = NF_INET_POST_ROUTING, 377 + .priority = NF_IP_PRI_NAT_SRC, 378 + }, 379 + /* Before packet filtering, change destination */ 380 + { 381 + .hook = nf_nat_ipv4_local_fn, 382 + .pf = NFPROTO_IPV4, 383 + .hooknum = NF_INET_LOCAL_OUT, 384 + .priority = NF_IP_PRI_NAT_DST, 385 + }, 386 + /* After packet filtering, change source */ 387 + { 388 + .hook = nf_nat_ipv4_fn, 389 + .pf = NFPROTO_IPV4, 390 + .hooknum = NF_INET_LOCAL_IN, 391 + .priority = NF_IP_PRI_NAT_SRC, 392 + }, 393 + }; 394 + 395 + int nf_nat_l3proto_ipv4_register_fn(struct net *net, const struct nf_hook_ops *ops) 396 + { 397 + return nf_nat_register_fn(net, ops, nf_nat_ipv4_ops, ARRAY_SIZE(nf_nat_ipv4_ops)); 398 + } 399 + EXPORT_SYMBOL_GPL(nf_nat_l3proto_ipv4_register_fn); 400 + 401 + void nf_nat_l3proto_ipv4_unregister_fn(struct net *net, const struct nf_hook_ops *ops) 402 + { 403 + nf_nat_unregister_fn(net, ops, ARRAY_SIZE(nf_nat_ipv4_ops)); 404 + } 405 + EXPORT_SYMBOL_GPL(nf_nat_l3proto_ipv4_unregister_fn); 350 406 351 407 static int __init nf_nat_l3proto_ipv4_init(void) 352 408 {
+8 -43
net/ipv4/netfilter/nft_chain_nat_ipv4.c
··· 27 27 #include <net/ip.h> 28 28 29 29 static unsigned int nft_nat_do_chain(void *priv, 30 - struct sk_buff *skb, 31 - const struct nf_hook_state *state) 30 + struct sk_buff *skb, 31 + const struct nf_hook_state *state) 32 32 { 33 33 struct nft_pktinfo pkt; 34 34 ··· 38 38 return nft_do_chain(&pkt, priv); 39 39 } 40 40 41 - static unsigned int nft_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, nft_nat_do_chain); 46 - } 47 - 48 - static unsigned int nft_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, nft_nat_do_chain); 53 - } 54 - 55 - static unsigned int nft_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, nft_nat_do_chain); 60 - } 61 - 62 - static unsigned int nft_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, nft_nat_do_chain); 67 - } 68 - 69 41 static int nft_nat_ipv4_reg(struct net *net, const struct nf_hook_ops *ops) 70 42 { 71 - int ret = nf_register_net_hook(net, ops); 72 - if (ret == 0) { 73 - ret = nf_ct_netns_get(net, NFPROTO_IPV4); 74 - if (ret) 75 - nf_unregister_net_hook(net, ops); 76 - } 77 - return ret; 43 + return nf_nat_l3proto_ipv4_register_fn(net, ops); 78 44 } 79 45 80 46 static void nft_nat_ipv4_unreg(struct net *net, const struct nf_hook_ops *ops) 81 47 { 82 - nf_unregister_net_hook(net, ops); 83 - nf_ct_netns_put(net, NFPROTO_IPV4); 48 + nf_nat_l3proto_ipv4_unregister_fn(net, ops); 84 49 } 85 50 86 51 static const struct nft_chain_type nft_chain_nat_ipv4 = { ··· 58 93 (1 << NF_INET_LOCAL_OUT) | 59 94 (1 << NF_INET_LOCAL_IN), 60 95 .hooks = { 61 - [NF_INET_PRE_ROUTING] = nft_nat_ipv4_in, 62 - [NF_INET_POST_ROUTING] = nft_nat_ipv4_out, 63 - [NF_INET_LOCAL_OUT] = nft_nat_ipv4_local_fn, 64 - [NF_INET_LOCAL_IN] = nft_nat_ipv4_fn, 96 + [NF_INET_PRE_ROUTING] = nft_nat_do_chain, 97 + [NF_INET_POST_ROUTING] = nft_nat_do_chain, 98 + [NF_INET_LOCAL_OUT] = nft_nat_do_chain, 99 + [NF_INET_LOCAL_IN] = nft_nat_do_chain, 65 100 }, 66 101 .ops_register = nft_nat_ipv4_reg, 67 102 .ops_unregister = nft_nat_ipv4_unreg,
+42 -42
net/ipv6/netfilter/ip6table_nat.c
··· 40 40 return ip6t_do_table(skb, state, state->net->ipv6.ip6table_nat); 41 41 } 42 42 43 - static unsigned int ip6table_nat_fn(void *priv, 44 - struct sk_buff *skb, 45 - const struct nf_hook_state *state) 46 - { 47 - return nf_nat_ipv6_fn(priv, skb, state, ip6table_nat_do_chain); 48 - } 49 - 50 - static unsigned int ip6table_nat_in(void *priv, 51 - struct sk_buff *skb, 52 - const struct nf_hook_state *state) 53 - { 54 - return nf_nat_ipv6_in(priv, skb, state, ip6table_nat_do_chain); 55 - } 56 - 57 - static unsigned int ip6table_nat_out(void *priv, 58 - struct sk_buff *skb, 59 - const struct nf_hook_state *state) 60 - { 61 - return nf_nat_ipv6_out(priv, skb, state, ip6table_nat_do_chain); 62 - } 63 - 64 - static unsigned int ip6table_nat_local_fn(void *priv, 65 - struct sk_buff *skb, 66 - const struct nf_hook_state *state) 67 - { 68 - return nf_nat_ipv6_local_fn(priv, skb, state, ip6table_nat_do_chain); 69 - } 70 - 71 43 static const struct nf_hook_ops nf_nat_ipv6_ops[] = { 72 - /* Before packet filtering, change destination */ 73 44 { 74 - .hook = ip6table_nat_in, 45 + .hook = ip6table_nat_do_chain, 75 46 .pf = NFPROTO_IPV6, 76 - .nat_hook = true, 77 47 .hooknum = NF_INET_PRE_ROUTING, 78 48 .priority = NF_IP6_PRI_NAT_DST, 79 49 }, 80 - /* After packet filtering, change source */ 81 50 { 82 - .hook = ip6table_nat_out, 51 + .hook = ip6table_nat_do_chain, 83 52 .pf = NFPROTO_IPV6, 84 - .nat_hook = true, 85 53 .hooknum = NF_INET_POST_ROUTING, 86 54 .priority = NF_IP6_PRI_NAT_SRC, 87 55 }, 88 - /* Before packet filtering, change destination */ 89 56 { 90 - .hook = ip6table_nat_local_fn, 57 + .hook = ip6table_nat_do_chain, 91 58 .pf = NFPROTO_IPV6, 92 - .nat_hook = true, 93 59 .hooknum = NF_INET_LOCAL_OUT, 94 60 .priority = NF_IP6_PRI_NAT_DST, 95 61 }, 96 - /* After packet filtering, change source */ 97 62 { 98 - .hook = ip6table_nat_fn, 99 - .nat_hook = true, 63 + .hook = ip6table_nat_do_chain, 100 64 .pf = NFPROTO_IPV6, 101 65 .hooknum = NF_INET_LOCAL_IN, 102 66 .priority = NF_IP6_PRI_NAT_SRC, 103 67 }, 104 68 }; 69 + 70 + static int ip6t_nat_register_lookups(struct net *net) 71 + { 72 + int i, ret; 73 + 74 + for (i = 0; i < ARRAY_SIZE(nf_nat_ipv6_ops); i++) { 75 + ret = nf_nat_l3proto_ipv6_register_fn(net, &nf_nat_ipv6_ops[i]); 76 + if (ret) { 77 + while (i) 78 + nf_nat_l3proto_ipv6_unregister_fn(net, &nf_nat_ipv6_ops[--i]); 79 + 80 + return ret; 81 + } 82 + } 83 + 84 + return 0; 85 + } 86 + 87 + static void ip6t_nat_unregister_lookups(struct net *net) 88 + { 89 + int i; 90 + 91 + for (i = 0; i < ARRAY_SIZE(nf_nat_ipv6_ops); i++) 92 + nf_nat_l3proto_ipv6_unregister_fn(net, &nf_nat_ipv6_ops[i]); 93 + } 105 94 106 95 static int __net_init ip6table_nat_table_init(struct net *net) 107 96 { ··· 104 115 if (repl == NULL) 105 116 return -ENOMEM; 106 117 ret = ip6t_register_table(net, &nf_nat_ipv6_table, repl, 107 - nf_nat_ipv6_ops, &net->ipv6.ip6table_nat); 118 + NULL, &net->ipv6.ip6table_nat); 119 + if (ret < 0) { 120 + kfree(repl); 121 + return ret; 122 + } 123 + 124 + ret = ip6t_nat_register_lookups(net); 125 + if (ret < 0) { 126 + ip6t_unregister_table(net, net->ipv6.ip6table_nat, NULL); 127 + net->ipv6.ip6table_nat = NULL; 128 + } 108 129 kfree(repl); 109 130 return ret; 110 131 } ··· 123 124 { 124 125 if (!net->ipv6.ip6table_nat) 125 126 return; 126 - ip6t_unregister_table(net, net->ipv6.ip6table_nat, nf_nat_ipv6_ops); 127 + ip6t_nat_unregister_lookups(net); 128 + ip6t_unregister_table(net, net->ipv6.ip6table_nat, NULL); 127 129 net->ipv6.ip6table_nat = NULL; 128 130 } 129 131
+55 -28
net/ipv6/netfilter/nf_nat_l3proto_ipv6.c
··· 252 252 } 253 253 EXPORT_SYMBOL_GPL(nf_nat_icmpv6_reply_translation); 254 254 255 - unsigned int 255 + static unsigned int 256 256 nf_nat_ipv6_fn(void *priv, struct sk_buff *skb, 257 - const struct nf_hook_state *state, 258 - unsigned int (*do_chain)(void *priv, 259 - struct sk_buff *skb, 260 - const struct nf_hook_state *state)) 257 + const struct nf_hook_state *state) 261 258 { 262 259 struct nf_conn *ct; 263 260 enum ip_conntrack_info ctinfo; ··· 286 289 } 287 290 } 288 291 289 - return nf_nat_inet_fn(priv, skb, state, do_chain); 292 + return nf_nat_inet_fn(priv, skb, state); 290 293 } 291 - EXPORT_SYMBOL_GPL(nf_nat_ipv6_fn); 292 294 293 - unsigned int 295 + static unsigned int 294 296 nf_nat_ipv6_in(void *priv, struct sk_buff *skb, 295 - const struct nf_hook_state *state, 296 - unsigned int (*do_chain)(void *priv, 297 - struct sk_buff *skb, 298 - const struct nf_hook_state *state)) 297 + const struct nf_hook_state *state) 299 298 { 300 299 unsigned int ret; 301 300 struct in6_addr daddr = ipv6_hdr(skb)->daddr; 302 301 303 - ret = nf_nat_ipv6_fn(priv, skb, state, do_chain); 302 + ret = nf_nat_ipv6_fn(priv, skb, state); 304 303 if (ret != NF_DROP && ret != NF_STOLEN && 305 304 ipv6_addr_cmp(&daddr, &ipv6_hdr(skb)->daddr)) 306 305 skb_dst_drop(skb); 307 306 308 307 return ret; 309 308 } 310 - EXPORT_SYMBOL_GPL(nf_nat_ipv6_in); 311 309 312 - unsigned int 310 + static unsigned int 313 311 nf_nat_ipv6_out(void *priv, struct sk_buff *skb, 314 - const struct nf_hook_state *state, 315 - unsigned int (*do_chain)(void *priv, 316 - struct sk_buff *skb, 317 - const struct nf_hook_state *state)) 312 + const struct nf_hook_state *state) 318 313 { 319 314 #ifdef CONFIG_XFRM 320 315 const struct nf_conn *ct; ··· 315 326 #endif 316 327 unsigned int ret; 317 328 318 - ret = nf_nat_ipv6_fn(priv, skb, state, do_chain); 329 + ret = nf_nat_ipv6_fn(priv, skb, state); 319 330 #ifdef CONFIG_XFRM 320 331 if (ret != NF_DROP && ret != NF_STOLEN && 321 332 !(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) && ··· 335 346 #endif 336 347 return ret; 337 348 } 338 - EXPORT_SYMBOL_GPL(nf_nat_ipv6_out); 339 349 340 - unsigned int 350 + static unsigned int 341 351 nf_nat_ipv6_local_fn(void *priv, struct sk_buff *skb, 342 - const struct nf_hook_state *state, 343 - unsigned int (*do_chain)(void *priv, 344 - struct sk_buff *skb, 345 - const struct nf_hook_state *state)) 352 + const struct nf_hook_state *state) 346 353 { 347 354 const struct nf_conn *ct; 348 355 enum ip_conntrack_info ctinfo; 349 356 unsigned int ret; 350 357 int err; 351 358 352 - ret = nf_nat_ipv6_fn(priv, skb, state, do_chain); 359 + ret = nf_nat_ipv6_fn(priv, skb, state); 353 360 if (ret != NF_DROP && ret != NF_STOLEN && 354 361 (ct = nf_ct_get(skb, &ctinfo)) != NULL) { 355 362 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); ··· 369 384 } 370 385 return ret; 371 386 } 372 - EXPORT_SYMBOL_GPL(nf_nat_ipv6_local_fn); 387 + 388 + static const struct nf_hook_ops nf_nat_ipv6_ops[] = { 389 + /* Before packet filtering, change destination */ 390 + { 391 + .hook = nf_nat_ipv6_in, 392 + .pf = NFPROTO_IPV6, 393 + .hooknum = NF_INET_PRE_ROUTING, 394 + .priority = NF_IP6_PRI_NAT_DST, 395 + }, 396 + /* After packet filtering, change source */ 397 + { 398 + .hook = nf_nat_ipv6_out, 399 + .pf = NFPROTO_IPV6, 400 + .hooknum = NF_INET_POST_ROUTING, 401 + .priority = NF_IP6_PRI_NAT_SRC, 402 + }, 403 + /* Before packet filtering, change destination */ 404 + { 405 + .hook = nf_nat_ipv6_local_fn, 406 + .pf = NFPROTO_IPV6, 407 + .hooknum = NF_INET_LOCAL_OUT, 408 + .priority = NF_IP6_PRI_NAT_DST, 409 + }, 410 + /* After packet filtering, change source */ 411 + { 412 + .hook = nf_nat_ipv6_fn, 413 + .pf = NFPROTO_IPV6, 414 + .hooknum = NF_INET_LOCAL_IN, 415 + .priority = NF_IP6_PRI_NAT_SRC, 416 + }, 417 + }; 418 + 419 + int nf_nat_l3proto_ipv6_register_fn(struct net *net, const struct nf_hook_ops *ops) 420 + { 421 + return nf_nat_register_fn(net, ops, nf_nat_ipv6_ops, ARRAY_SIZE(nf_nat_ipv6_ops)); 422 + } 423 + EXPORT_SYMBOL_GPL(nf_nat_l3proto_ipv6_register_fn); 424 + 425 + void nf_nat_l3proto_ipv6_unregister_fn(struct net *net, const struct nf_hook_ops *ops) 426 + { 427 + nf_nat_unregister_fn(net, ops, ARRAY_SIZE(nf_nat_ipv6_ops)); 428 + } 429 + EXPORT_SYMBOL_GPL(nf_nat_l3proto_ipv6_unregister_fn); 373 430 374 431 static int __init nf_nat_l3proto_ipv6_init(void) 375 432 {
+6 -42
net/ipv6/netfilter/nft_chain_nat_ipv6.c
··· 36 36 return nft_do_chain(&pkt, priv); 37 37 } 38 38 39 - static unsigned int nft_nat_ipv6_fn(void *priv, 40 - struct sk_buff *skb, 41 - const struct nf_hook_state *state) 42 - { 43 - return nf_nat_ipv6_fn(priv, skb, state, nft_nat_do_chain); 44 - } 45 - 46 - static unsigned int nft_nat_ipv6_in(void *priv, 47 - struct sk_buff *skb, 48 - const struct nf_hook_state *state) 49 - { 50 - return nf_nat_ipv6_in(priv, skb, state, nft_nat_do_chain); 51 - } 52 - 53 - static unsigned int nft_nat_ipv6_out(void *priv, 54 - struct sk_buff *skb, 55 - const struct nf_hook_state *state) 56 - { 57 - return nf_nat_ipv6_out(priv, skb, state, nft_nat_do_chain); 58 - } 59 - 60 - static unsigned int nft_nat_ipv6_local_fn(void *priv, 61 - struct sk_buff *skb, 62 - const struct nf_hook_state *state) 63 - { 64 - return nf_nat_ipv6_local_fn(priv, skb, state, nft_nat_do_chain); 65 - } 66 - 67 39 static int nft_nat_ipv6_reg(struct net *net, const struct nf_hook_ops *ops) 68 40 { 69 - int ret = nf_register_net_hook(net, ops); 70 - if (ret == 0) { 71 - ret = nf_ct_netns_get(net, NFPROTO_IPV6); 72 - if (ret) 73 - nf_unregister_net_hook(net, ops); 74 - } 75 - 76 - return ret; 41 + return nf_nat_l3proto_ipv6_register_fn(net, ops); 77 42 } 78 43 79 44 static void nft_nat_ipv6_unreg(struct net *net, const struct nf_hook_ops *ops) 80 45 { 81 - nf_unregister_net_hook(net, ops); 82 - nf_ct_netns_put(net, NFPROTO_IPV6); 46 + nf_nat_l3proto_ipv6_unregister_fn(net, ops); 83 47 } 84 48 85 49 static const struct nft_chain_type nft_chain_nat_ipv6 = { ··· 56 92 (1 << NF_INET_LOCAL_OUT) | 57 93 (1 << NF_INET_LOCAL_IN), 58 94 .hooks = { 59 - [NF_INET_PRE_ROUTING] = nft_nat_ipv6_in, 60 - [NF_INET_POST_ROUTING] = nft_nat_ipv6_out, 61 - [NF_INET_LOCAL_OUT] = nft_nat_ipv6_local_fn, 62 - [NF_INET_LOCAL_IN] = nft_nat_ipv6_fn, 95 + [NF_INET_PRE_ROUTING] = nft_nat_do_chain, 96 + [NF_INET_POST_ROUTING] = nft_nat_do_chain, 97 + [NF_INET_LOCAL_OUT] = nft_nat_do_chain, 98 + [NF_INET_LOCAL_IN] = nft_nat_do_chain, 63 99 }, 64 100 .ops_register = nft_nat_ipv6_reg, 65 101 .ops_unregister = nft_nat_ipv6_unreg,
+16 -11
net/netfilter/nf_nat_core.c
··· 533 533 534 534 unsigned int 535 535 nf_nat_inet_fn(void *priv, struct sk_buff *skb, 536 - const struct nf_hook_state *state, 537 - unsigned int (*do_chain)(void *priv, 538 - struct sk_buff *skb, 539 - const struct nf_hook_state *state)) 536 + const struct nf_hook_state *state) 540 537 { 541 538 struct nf_conn *ct; 542 539 enum ip_conntrack_info ctinfo; ··· 561 564 * or local packets. 562 565 */ 563 566 if (!nf_nat_initialized(ct, maniptype)) { 567 + struct nf_nat_lookup_hook_priv *lpriv = priv; 568 + struct nf_hook_entries *e = rcu_dereference(lpriv->entries); 564 569 unsigned int ret; 570 + int i; 565 571 566 - ret = do_chain(priv, skb, state); 567 - if (ret != NF_ACCEPT) 568 - return ret; 572 + if (!e) 573 + goto null_bind; 569 574 570 - if (nf_nat_initialized(ct, HOOK2MANIP(state->hook))) 571 - break; 572 - 575 + for (i = 0; i < e->num_hook_entries; i++) { 576 + ret = e->hooks[i].hook(e->hooks[i].priv, skb, 577 + state); 578 + if (ret != NF_ACCEPT) 579 + return ret; 580 + if (nf_nat_initialized(ct, maniptype)) 581 + goto do_nat; 582 + } 583 + null_bind: 573 584 ret = nf_nat_alloc_null_binding(ct, state->hook); 574 585 if (ret != NF_ACCEPT) 575 586 return ret; ··· 597 592 if (nf_nat_oif_changed(state->hook, ctinfo, nat, state->out)) 598 593 goto oif_changed; 599 594 } 600 - 595 + do_nat: 601 596 return nf_nat_packet(ct, ctinfo, state->hook, skb); 602 597 603 598 oif_changed: