80fd3678af8aa4f4a8d5e9dbfef90c0b45e62aa3
[librecmc/librecmc.git] /
1 From: Florian Westphal <fw@strlen.de>
2 Date: Fri, 8 Dec 2017 17:01:54 +0100
3 Subject: [PATCH] netfilter: core: only allow one nat hook per hook point
4
5 The netfilter NAT core cannot deal with more than one NAT hook per hook
6 location (prerouting, input ...), because the NAT hooks install a NAT null
7 binding in case the iptables nat table (iptable_nat hooks) or the
8 corresponding nftables chain (nft nat hooks) doesn't specify a nat
9 transformation.
10
11 Null bindings are needed to detect port collsisions between NAT-ed and
12 non-NAT-ed connections.
13
14 This causes nftables NAT rules to not work when iptable_nat module is
15 loaded, and vice versa because nat binding has already been attached
16 when the second nat hook is consulted.
17
18 The netfilter core is not really the correct location to handle this
19 (hooks are just hooks, the core has no notion of what kinds of side
20  effects a hook implements), but its the only place where we can check
21 for conflicts between both iptables hooks and nftables hooks without
22 adding dependencies.
23
24 So add nat annotation to hook_ops to describe those hooks that will
25 add NAT bindings and then make core reject if such a hook already exists.
26 The annotation fills a padding hole, in case further restrictions appar
27 we might change this to a 'u8 type' instead of bool.
28
29 iptables error if nft nat hook active:
30 iptables -t nat -A POSTROUTING -j MASQUERADE
31 iptables v1.4.21: can't initialize iptables table `nat': File exists
32 Perhaps iptables or your kernel needs to be upgraded.
33
34 nftables error if iptables nat table present:
35 nft -f /etc/nftables/ipv4-nat
36 /usr/etc/nftables/ipv4-nat:3:1-2: Error: Could not process rule: File exists
37 table nat {
38 ^^
39
40 Signed-off-by: Florian Westphal <fw@strlen.de>
41 Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
42 ---
43
44 --- a/include/linux/netfilter.h
45 +++ b/include/linux/netfilter.h
46 @@ -67,6 +67,7 @@ struct nf_hook_ops {
47         struct net_device       *dev;
48         void                    *priv;
49         u_int8_t                pf;
50 +       bool                    nat_hook;
51         unsigned int            hooknum;
52         /* Hooks are ordered in ascending priority. */
53         int                     priority;
54 --- a/net/ipv4/netfilter/iptable_nat.c
55 +++ b/net/ipv4/netfilter/iptable_nat.c
56 @@ -72,6 +72,7 @@ static const struct nf_hook_ops nf_nat_i
57         {
58                 .hook           = iptable_nat_ipv4_in,
59                 .pf             = NFPROTO_IPV4,
60 +               .nat_hook       = true,
61                 .hooknum        = NF_INET_PRE_ROUTING,
62                 .priority       = NF_IP_PRI_NAT_DST,
63         },
64 @@ -79,6 +80,7 @@ static const struct nf_hook_ops nf_nat_i
65         {
66                 .hook           = iptable_nat_ipv4_out,
67                 .pf             = NFPROTO_IPV4,
68 +               .nat_hook       = true,
69                 .hooknum        = NF_INET_POST_ROUTING,
70                 .priority       = NF_IP_PRI_NAT_SRC,
71         },
72 @@ -86,6 +88,7 @@ static const struct nf_hook_ops nf_nat_i
73         {
74                 .hook           = iptable_nat_ipv4_local_fn,
75                 .pf             = NFPROTO_IPV4,
76 +               .nat_hook       = true,
77                 .hooknum        = NF_INET_LOCAL_OUT,
78                 .priority       = NF_IP_PRI_NAT_DST,
79         },
80 @@ -93,6 +96,7 @@ static const struct nf_hook_ops nf_nat_i
81         {
82                 .hook           = iptable_nat_ipv4_fn,
83                 .pf             = NFPROTO_IPV4,
84 +               .nat_hook       = true,
85                 .hooknum        = NF_INET_LOCAL_IN,
86                 .priority       = NF_IP_PRI_NAT_SRC,
87         },
88 --- a/net/ipv6/netfilter/ip6table_nat.c
89 +++ b/net/ipv6/netfilter/ip6table_nat.c
90 @@ -74,6 +74,7 @@ static const struct nf_hook_ops nf_nat_i
91         {
92                 .hook           = ip6table_nat_in,
93                 .pf             = NFPROTO_IPV6,
94 +               .nat_hook       = true,
95                 .hooknum        = NF_INET_PRE_ROUTING,
96                 .priority       = NF_IP6_PRI_NAT_DST,
97         },
98 @@ -81,6 +82,7 @@ static const struct nf_hook_ops nf_nat_i
99         {
100                 .hook           = ip6table_nat_out,
101                 .pf             = NFPROTO_IPV6,
102 +               .nat_hook       = true,
103                 .hooknum        = NF_INET_POST_ROUTING,
104                 .priority       = NF_IP6_PRI_NAT_SRC,
105         },
106 @@ -88,12 +90,14 @@ static const struct nf_hook_ops nf_nat_i
107         {
108                 .hook           = ip6table_nat_local_fn,
109                 .pf             = NFPROTO_IPV6,
110 +               .nat_hook       = true,
111                 .hooknum        = NF_INET_LOCAL_OUT,
112                 .priority       = NF_IP6_PRI_NAT_DST,
113         },
114         /* After packet filtering, change source */
115         {
116                 .hook           = ip6table_nat_fn,
117 +               .nat_hook       = true,
118                 .pf             = NFPROTO_IPV6,
119                 .hooknum        = NF_INET_LOCAL_IN,
120                 .priority       = NF_IP6_PRI_NAT_SRC,
121 --- a/net/netfilter/core.c
122 +++ b/net/netfilter/core.c
123 @@ -160,6 +160,12 @@ nf_hook_entries_grow(const struct nf_hoo
124                         ++i;
125                         continue;
126                 }
127 +
128 +               if (reg->nat_hook && orig_ops[i]->nat_hook) {
129 +                       kvfree(new);
130 +                       return ERR_PTR(-EEXIST);
131 +               }
132 +
133                 if (inserted || reg->priority > orig_ops[i]->priority) {
134                         new_ops[nhooks] = (void *)orig_ops[i];
135                         new->hooks[nhooks] = old->hooks[i];
136 --- a/net/netfilter/nf_tables_api.c
137 +++ b/net/netfilter/nf_tables_api.c
138 @@ -1446,6 +1446,8 @@ static int nf_tables_addchain(struct nft
139                                 ops->hook = hookfn;
140                         if (afi->hook_ops_init)
141                                 afi->hook_ops_init(ops, i);
142 +                       if (basechain->type->type == NFT_CHAIN_T_NAT)
143 +                               ops->nat_hook = true;
144                 }
145  
146                 chain->flags |= NFT_BASE_CHAIN;