2 * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.net>
3 * Copyright (c) 2012 Pablo Neira Ayuso <pablo@netfilter.org>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
9 * Development of this code funded by Astaro AG (http://www.astaro.com/)
12 #include <linux/module.h>
13 #include <linux/init.h>
14 #include <linux/list.h>
15 #include <linux/skbuff.h>
17 #include <linux/netlink.h>
18 #include <linux/netfilter.h>
19 #include <linux/netfilter_ipv4.h>
20 #include <linux/netfilter/nfnetlink.h>
21 #include <linux/netfilter/nf_tables.h>
22 #include <net/netfilter/nf_conntrack.h>
23 #include <net/netfilter/nf_nat.h>
24 #include <net/netfilter/nf_nat_core.h>
25 #include <net/netfilter/nf_tables.h>
26 #include <net/netfilter/nf_nat_l3proto.h>
30 enum nft_registers sreg_addr_min
:8;
31 enum nft_registers sreg_addr_max
:8;
32 enum nft_registers sreg_proto_min
:8;
33 enum nft_registers sreg_proto_max
:8;
34 enum nf_nat_manip_type type
;
37 static void nft_nat_eval(const struct nft_expr
*expr
,
38 struct nft_data data
[NFT_REG_MAX
+ 1],
39 const struct nft_pktinfo
*pkt
)
41 const struct nft_nat
*priv
= nft_expr_priv(expr
);
42 enum ip_conntrack_info ctinfo
;
43 struct nf_conn
*ct
= nf_ct_get(pkt
->skb
, &ctinfo
);
44 struct nf_nat_range range
;
46 memset(&range
, 0, sizeof(range
));
47 if (priv
->sreg_addr_min
) {
48 range
.min_addr
.ip
= data
[priv
->sreg_addr_min
].data
[0];
49 range
.max_addr
.ip
= data
[priv
->sreg_addr_max
].data
[0];
50 range
.flags
|= NF_NAT_RANGE_MAP_IPS
;
53 if (priv
->sreg_proto_min
) {
54 range
.min_proto
.all
= data
[priv
->sreg_proto_min
].data
[0];
55 range
.max_proto
.all
= data
[priv
->sreg_proto_max
].data
[0];
56 range
.flags
|= NF_NAT_RANGE_PROTO_SPECIFIED
;
59 data
[NFT_REG_VERDICT
].verdict
=
60 nf_nat_setup_info(ct
, &range
, priv
->type
);
63 static const struct nla_policy nft_nat_policy
[NFTA_NAT_MAX
+ 1] = {
64 [NFTA_NAT_ADDR_MIN
] = { .type
= NLA_U32
},
65 [NFTA_NAT_ADDR_MAX
] = { .type
= NLA_U32
},
66 [NFTA_NAT_PROTO_MIN
] = { .type
= NLA_U32
},
67 [NFTA_NAT_PROTO_MAX
] = { .type
= NLA_U32
},
68 [NFTA_NAT_TYPE
] = { .type
= NLA_U32
},
71 static int nft_nat_init(const struct nft_ctx
*ctx
, const struct nft_expr
*expr
,
72 const struct nlattr
* const tb
[])
74 struct nft_nat
*priv
= nft_expr_priv(expr
);
77 if (tb
[NFTA_NAT_TYPE
] == NULL
)
80 switch (ntohl(nla_get_be32(tb
[NFTA_NAT_TYPE
]))) {
82 priv
->type
= NF_NAT_MANIP_SRC
;
85 priv
->type
= NF_NAT_MANIP_DST
;
91 if (tb
[NFTA_NAT_ADDR_MIN
]) {
92 priv
->sreg_addr_min
= ntohl(nla_get_be32(tb
[NFTA_NAT_ADDR_MIN
]));
93 err
= nft_validate_input_register(priv
->sreg_addr_min
);
98 if (tb
[NFTA_NAT_ADDR_MAX
]) {
99 priv
->sreg_addr_max
= ntohl(nla_get_be32(tb
[NFTA_NAT_ADDR_MAX
]));
100 err
= nft_validate_input_register(priv
->sreg_addr_max
);
104 priv
->sreg_addr_max
= priv
->sreg_addr_min
;
106 if (tb
[NFTA_NAT_PROTO_MIN
]) {
107 priv
->sreg_proto_min
= ntohl(nla_get_be32(tb
[NFTA_NAT_PROTO_MIN
]));
108 err
= nft_validate_input_register(priv
->sreg_proto_min
);
113 if (tb
[NFTA_NAT_PROTO_MAX
]) {
114 priv
->sreg_proto_max
= ntohl(nla_get_be32(tb
[NFTA_NAT_PROTO_MAX
]));
115 err
= nft_validate_input_register(priv
->sreg_proto_max
);
119 priv
->sreg_proto_max
= priv
->sreg_proto_min
;
124 static int nft_nat_dump(struct sk_buff
*skb
, const struct nft_expr
*expr
)
126 const struct nft_nat
*priv
= nft_expr_priv(expr
);
128 switch (priv
->type
) {
129 case NF_NAT_MANIP_SRC
:
130 if (nla_put_be32(skb
, NFTA_NAT_TYPE
, htonl(NFT_NAT_SNAT
)))
131 goto nla_put_failure
;
133 case NF_NAT_MANIP_DST
:
134 if (nla_put_be32(skb
, NFTA_NAT_TYPE
, htonl(NFT_NAT_DNAT
)))
135 goto nla_put_failure
;
139 if (nla_put_be32(skb
, NFTA_NAT_ADDR_MIN
, htonl(priv
->sreg_addr_min
)))
140 goto nla_put_failure
;
141 if (nla_put_be32(skb
, NFTA_NAT_ADDR_MAX
, htonl(priv
->sreg_addr_max
)))
142 goto nla_put_failure
;
143 if (nla_put_be32(skb
, NFTA_NAT_PROTO_MIN
, htonl(priv
->sreg_proto_min
)))
144 goto nla_put_failure
;
145 if (nla_put_be32(skb
, NFTA_NAT_PROTO_MAX
, htonl(priv
->sreg_proto_max
)))
146 goto nla_put_failure
;
153 static struct nft_expr_type nft_nat_type
;
154 static const struct nft_expr_ops nft_nat_ops
= {
155 .type
= &nft_nat_type
,
156 .size
= NFT_EXPR_SIZE(sizeof(struct nft_nat
)),
157 .eval
= nft_nat_eval
,
158 .init
= nft_nat_init
,
159 .dump
= nft_nat_dump
,
162 static struct nft_expr_type nft_nat_type __read_mostly
= {
165 .policy
= nft_nat_policy
,
166 .maxattr
= NFTA_NAT_MAX
,
167 .owner
= THIS_MODULE
,
174 static unsigned int nf_nat_fn(const struct nf_hook_ops
*ops
,
176 const struct net_device
*in
,
177 const struct net_device
*out
,
178 int (*okfn
)(struct sk_buff
*))
180 enum ip_conntrack_info ctinfo
;
181 struct nf_conn
*ct
= nf_ct_get(skb
, &ctinfo
);
182 struct nf_conn_nat
*nat
;
183 enum nf_nat_manip_type maniptype
= HOOK2MANIP(ops
->hooknum
);
186 if (ct
== NULL
|| nf_ct_is_untracked(ct
))
189 NF_CT_ASSERT(!(ip_hdr(skb
)->frag_off
& htons(IP_MF
| IP_OFFSET
)));
193 /* Conntrack module was loaded late, can't add extension. */
194 if (nf_ct_is_confirmed(ct
))
196 nat
= nf_ct_ext_add(ct
, NF_CT_EXT_NAT
, GFP_ATOMIC
);
203 case IP_CT_RELATED
+ IP_CT_IS_REPLY
:
204 if (ip_hdr(skb
)->protocol
== IPPROTO_ICMP
) {
205 if (!nf_nat_icmp_reply_translation(skb
, ct
, ctinfo
,
213 if (nf_nat_initialized(ct
, maniptype
))
216 ret
= nft_do_chain(ops
, skb
, in
, out
, okfn
);
217 if (ret
!= NF_ACCEPT
)
219 if (!nf_nat_initialized(ct
, maniptype
)) {
220 ret
= nf_nat_alloc_null_binding(ct
, ops
->hooknum
);
221 if (ret
!= NF_ACCEPT
)
228 return nf_nat_packet(ct
, ctinfo
, ops
->hooknum
, skb
);
231 static unsigned int nf_nat_prerouting(const struct nf_hook_ops
*ops
,
233 const struct net_device
*in
,
234 const struct net_device
*out
,
235 int (*okfn
)(struct sk_buff
*))
237 __be32 daddr
= ip_hdr(skb
)->daddr
;
240 ret
= nf_nat_fn(ops
, skb
, in
, out
, okfn
);
241 if (ret
!= NF_DROP
&& ret
!= NF_STOLEN
&&
242 ip_hdr(skb
)->daddr
!= daddr
) {
248 static unsigned int nf_nat_postrouting(const struct nf_hook_ops
*ops
,
250 const struct net_device
*in
,
251 const struct net_device
*out
,
252 int (*okfn
)(struct sk_buff
*))
254 enum ip_conntrack_info ctinfo __maybe_unused
;
255 const struct nf_conn
*ct __maybe_unused
;
258 ret
= nf_nat_fn(ops
, skb
, in
, out
, okfn
);
260 if (ret
!= NF_DROP
&& ret
!= NF_STOLEN
&&
261 (ct
= nf_ct_get(skb
, &ctinfo
)) != NULL
) {
262 enum ip_conntrack_dir dir
= CTINFO2DIR(ctinfo
);
264 if (ct
->tuplehash
[dir
].tuple
.src
.u3
.ip
!=
265 ct
->tuplehash
[!dir
].tuple
.dst
.u3
.ip
||
266 ct
->tuplehash
[dir
].tuple
.src
.u
.all
!=
267 ct
->tuplehash
[!dir
].tuple
.dst
.u
.all
)
268 return nf_xfrm_me_harder(skb
, AF_INET
) == 0 ?
275 static unsigned int nf_nat_output(const struct nf_hook_ops
*ops
,
277 const struct net_device
*in
,
278 const struct net_device
*out
,
279 int (*okfn
)(struct sk_buff
*))
281 enum ip_conntrack_info ctinfo
;
282 const struct nf_conn
*ct
;
285 ret
= nf_nat_fn(ops
, skb
, in
, out
, okfn
);
286 if (ret
!= NF_DROP
&& ret
!= NF_STOLEN
&&
287 (ct
= nf_ct_get(skb
, &ctinfo
)) != NULL
) {
288 enum ip_conntrack_dir dir
= CTINFO2DIR(ctinfo
);
290 if (ct
->tuplehash
[dir
].tuple
.dst
.u3
.ip
!=
291 ct
->tuplehash
[!dir
].tuple
.src
.u3
.ip
) {
292 if (ip_route_me_harder(skb
, RTN_UNSPEC
))
296 else if (ct
->tuplehash
[dir
].tuple
.dst
.u
.all
!=
297 ct
->tuplehash
[!dir
].tuple
.src
.u
.all
)
298 if (nf_xfrm_me_harder(skb
, AF_INET
))
305 struct nf_chain_type nft_chain_nat_ipv4
= {
306 .family
= NFPROTO_IPV4
,
308 .type
= NFT_CHAIN_T_NAT
,
309 .hook_mask
= (1 << NF_INET_PRE_ROUTING
) |
310 (1 << NF_INET_POST_ROUTING
) |
311 (1 << NF_INET_LOCAL_OUT
) |
312 (1 << NF_INET_LOCAL_IN
),
314 [NF_INET_PRE_ROUTING
] = nf_nat_prerouting
,
315 [NF_INET_POST_ROUTING
] = nf_nat_postrouting
,
316 [NF_INET_LOCAL_OUT
] = nf_nat_output
,
317 [NF_INET_LOCAL_IN
] = nf_nat_fn
,
322 static int __init
nft_chain_nat_init(void)
326 err
= nft_register_chain_type(&nft_chain_nat_ipv4
);
330 err
= nft_register_expr(&nft_nat_type
);
337 nft_unregister_chain_type(&nft_chain_nat_ipv4
);
341 static void __exit
nft_chain_nat_exit(void)
343 nft_unregister_expr(&nft_nat_type
);
344 nft_unregister_chain_type(&nft_chain_nat_ipv4
);
347 module_init(nft_chain_nat_init
);
348 module_exit(nft_chain_nat_exit
);
350 MODULE_LICENSE("GPL");
351 MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
352 MODULE_ALIAS_NFT_CHAIN(AF_INET
, "nat");
353 MODULE_ALIAS_NFT_EXPR("nat");