]> git.proxmox.com Git - mirror_ubuntu-eoan-kernel.git/blame - net/ipv4/netfilter/nf_nat_l3proto_ipv4.c
Merge tag 'omap-for-v5.0/fixes-rc7-signed' of git://git.kernel.org/pub/scm/linux...
[mirror_ubuntu-eoan-kernel.git] / net / ipv4 / netfilter / nf_nat_l3proto_ipv4.c
CommitLineData
c7232c99
PM
1/*
2 * (C) 1999-2001 Paul `Rusty' Russell
3 * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
4 * (C) 2011 Patrick McHardy <kaber@trash.net>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 */
10
11#include <linux/types.h>
12#include <linux/module.h>
13#include <linux/skbuff.h>
14#include <linux/ip.h>
15#include <linux/icmp.h>
16#include <linux/netfilter.h>
17#include <linux/netfilter_ipv4.h>
18#include <net/secure_seq.h>
19#include <net/checksum.h>
20#include <net/route.h>
21#include <net/ip.h>
22
23#include <net/netfilter/nf_conntrack_core.h>
24#include <net/netfilter/nf_conntrack.h>
25#include <net/netfilter/nf_nat_core.h>
26#include <net/netfilter/nf_nat_l3proto.h>
27#include <net/netfilter/nf_nat_l4proto.h>
28
29static const struct nf_nat_l3proto nf_nat_l3proto_ipv4;
30
31#ifdef CONFIG_XFRM
32static void nf_nat_ipv4_decode_session(struct sk_buff *skb,
33 const struct nf_conn *ct,
34 enum ip_conntrack_dir dir,
35 unsigned long statusbit,
36 struct flowi *fl)
37{
38 const struct nf_conntrack_tuple *t = &ct->tuplehash[dir].tuple;
39 struct flowi4 *fl4 = &fl->u.ip4;
40
41 if (ct->status & statusbit) {
42 fl4->daddr = t->dst.u3.ip;
43 if (t->dst.protonum == IPPROTO_TCP ||
44 t->dst.protonum == IPPROTO_UDP ||
45 t->dst.protonum == IPPROTO_UDPLITE ||
46 t->dst.protonum == IPPROTO_DCCP ||
47 t->dst.protonum == IPPROTO_SCTP)
48 fl4->fl4_dport = t->dst.u.all;
49 }
50
51 statusbit ^= IPS_NAT_MASK;
52
53 if (ct->status & statusbit) {
54 fl4->saddr = t->src.u3.ip;
55 if (t->dst.protonum == IPPROTO_TCP ||
56 t->dst.protonum == IPPROTO_UDP ||
57 t->dst.protonum == IPPROTO_UDPLITE ||
58 t->dst.protonum == IPPROTO_DCCP ||
59 t->dst.protonum == IPPROTO_SCTP)
60 fl4->fl4_sport = t->src.u.all;
61 }
62}
63#endif /* CONFIG_XFRM */
64
c7232c99
PM
65static bool nf_nat_ipv4_manip_pkt(struct sk_buff *skb,
66 unsigned int iphdroff,
c7232c99
PM
67 const struct nf_conntrack_tuple *target,
68 enum nf_nat_manip_type maniptype)
69{
70 struct iphdr *iph;
71 unsigned int hdroff;
72
73 if (!skb_make_writable(skb, iphdroff + sizeof(*iph)))
74 return false;
75
76 iph = (void *)skb->data + iphdroff;
77 hdroff = iphdroff + iph->ihl * 4;
78
faec18db
FW
79 if (!nf_nat_l4proto_manip_pkt(skb, &nf_nat_l3proto_ipv4, iphdroff,
80 hdroff, target, maniptype))
c7232c99
PM
81 return false;
82 iph = (void *)skb->data + iphdroff;
83
84 if (maniptype == NF_NAT_MANIP_SRC) {
85 csum_replace4(&iph->check, iph->saddr, target->src.u3.ip);
86 iph->saddr = target->src.u3.ip;
87 } else {
88 csum_replace4(&iph->check, iph->daddr, target->dst.u3.ip);
89 iph->daddr = target->dst.u3.ip;
90 }
91 return true;
92}
93
94static void nf_nat_ipv4_csum_update(struct sk_buff *skb,
95 unsigned int iphdroff, __sum16 *check,
96 const struct nf_conntrack_tuple *t,
97 enum nf_nat_manip_type maniptype)
98{
99 struct iphdr *iph = (struct iphdr *)(skb->data + iphdroff);
100 __be32 oldip, newip;
101
102 if (maniptype == NF_NAT_MANIP_SRC) {
103 oldip = iph->saddr;
104 newip = t->src.u3.ip;
105 } else {
106 oldip = iph->daddr;
107 newip = t->dst.u3.ip;
108 }
4b048d6d 109 inet_proto_csum_replace4(check, skb, oldip, newip, true);
c7232c99
PM
110}
111
112static void nf_nat_ipv4_csum_recalc(struct sk_buff *skb,
113 u8 proto, void *data, __sum16 *check,
114 int datalen, int oldlen)
115{
c7232c99 116 if (skb->ip_summed != CHECKSUM_PARTIAL) {
26461905
JR
117 const struct iphdr *iph = ip_hdr(skb);
118
119 skb->ip_summed = CHECKSUM_PARTIAL;
120 skb->csum_start = skb_headroom(skb) + skb_network_offset(skb) +
121 ip_hdrlen(skb);
122 skb->csum_offset = (void *)check - data;
123 *check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, datalen,
124 proto, 0);
c7232c99
PM
125 } else
126 inet_proto_csum_replace2(check, skb,
4b048d6d 127 htons(oldlen), htons(datalen), true);
c7232c99
PM
128}
129
24de3d37 130#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
c7232c99 131static int nf_nat_ipv4_nlattr_to_range(struct nlattr *tb[],
2eb0f624 132 struct nf_nat_range2 *range)
c7232c99
PM
133{
134 if (tb[CTA_NAT_V4_MINIP]) {
135 range->min_addr.ip = nla_get_be32(tb[CTA_NAT_V4_MINIP]);
136 range->flags |= NF_NAT_RANGE_MAP_IPS;
137 }
138
139 if (tb[CTA_NAT_V4_MAXIP])
140 range->max_addr.ip = nla_get_be32(tb[CTA_NAT_V4_MAXIP]);
141 else
142 range->max_addr.ip = range->min_addr.ip;
143
144 return 0;
145}
24de3d37 146#endif
c7232c99
PM
147
148static const struct nf_nat_l3proto nf_nat_l3proto_ipv4 = {
149 .l3proto = NFPROTO_IPV4,
c7232c99
PM
150 .manip_pkt = nf_nat_ipv4_manip_pkt,
151 .csum_update = nf_nat_ipv4_csum_update,
152 .csum_recalc = nf_nat_ipv4_csum_recalc,
24de3d37 153#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
c7232c99 154 .nlattr_to_range = nf_nat_ipv4_nlattr_to_range,
24de3d37 155#endif
c7232c99
PM
156#ifdef CONFIG_XFRM
157 .decode_session = nf_nat_ipv4_decode_session,
158#endif
159};
160
161int nf_nat_icmp_reply_translation(struct sk_buff *skb,
162 struct nf_conn *ct,
163 enum ip_conntrack_info ctinfo,
164 unsigned int hooknum)
165{
166 struct {
167 struct icmphdr icmp;
168 struct iphdr ip;
169 } *inside;
170 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
171 enum nf_nat_manip_type manip = HOOK2MANIP(hooknum);
172 unsigned int hdrlen = ip_hdrlen(skb);
c7232c99
PM
173 struct nf_conntrack_tuple target;
174 unsigned long statusbit;
175
44d6e2f2 176 WARN_ON(ctinfo != IP_CT_RELATED && ctinfo != IP_CT_RELATED_REPLY);
c7232c99
PM
177
178 if (!skb_make_writable(skb, hdrlen + sizeof(*inside)))
179 return 0;
180 if (nf_ip_checksum(skb, hooknum, hdrlen, 0))
181 return 0;
182
183 inside = (void *)skb->data + hdrlen;
184 if (inside->icmp.type == ICMP_REDIRECT) {
185 if ((ct->status & IPS_NAT_DONE_MASK) != IPS_NAT_DONE_MASK)
186 return 0;
187 if (ct->status & IPS_NAT_MASK)
188 return 0;
189 }
190
191 if (manip == NF_NAT_MANIP_SRC)
192 statusbit = IPS_SRC_NAT;
193 else
194 statusbit = IPS_DST_NAT;
195
196 /* Invert if this is reply direction */
197 if (dir == IP_CT_DIR_REPLY)
198 statusbit ^= IPS_NAT_MASK;
199
200 if (!(ct->status & statusbit))
201 return 1;
202
c7232c99 203 if (!nf_nat_ipv4_manip_pkt(skb, hdrlen + sizeof(inside->icmp),
5cbabeec 204 &ct->tuplehash[!dir].tuple, !manip))
c7232c99
PM
205 return 0;
206
207 if (skb->ip_summed != CHECKSUM_PARTIAL) {
208 /* Reloading "inside" here since manip_pkt may reallocate */
209 inside = (void *)skb->data + hdrlen;
210 inside->icmp.checksum = 0;
211 inside->icmp.checksum =
212 csum_fold(skb_checksum(skb, hdrlen,
213 skb->len - hdrlen, 0));
214 }
215
216 /* Change outer to look like the reply to an incoming packet */
217 nf_ct_invert_tuplepr(&target, &ct->tuplehash[!dir].tuple);
8303b7e8 218 target.dst.protonum = IPPROTO_ICMP;
5cbabeec 219 if (!nf_nat_ipv4_manip_pkt(skb, 0, &target, manip))
c7232c99
PM
220 return 0;
221
222 return 1;
223}
224EXPORT_SYMBOL_GPL(nf_nat_icmp_reply_translation);
225
9971a514 226static unsigned int
06198b34 227nf_nat_ipv4_fn(void *priv, struct sk_buff *skb,
9971a514 228 const struct nf_hook_state *state)
30766f4c
PNA
229{
230 struct nf_conn *ct;
231 enum ip_conntrack_info ctinfo;
30766f4c 232
30766f4c 233 ct = nf_ct_get(skb, &ctinfo);
30766f4c
PNA
234 if (!ct)
235 return NF_ACCEPT;
236
1f55236b 237 if (ctinfo == IP_CT_RELATED || ctinfo == IP_CT_RELATED_REPLY) {
30766f4c
PNA
238 if (ip_hdr(skb)->protocol == IPPROTO_ICMP) {
239 if (!nf_nat_icmp_reply_translation(skb, ct, ctinfo,
082a758f 240 state->hook))
30766f4c
PNA
241 return NF_DROP;
242 else
243 return NF_ACCEPT;
244 }
30766f4c
PNA
245 }
246
9971a514 247 return nf_nat_inet_fn(priv, skb, state);
30766f4c 248}
30766f4c 249
9971a514 250static unsigned int
06198b34 251nf_nat_ipv4_in(void *priv, struct sk_buff *skb,
9971a514 252 const struct nf_hook_state *state)
30766f4c
PNA
253{
254 unsigned int ret;
255 __be32 daddr = ip_hdr(skb)->daddr;
256
9971a514 257 ret = nf_nat_ipv4_fn(priv, skb, state);
30766f4c
PNA
258 if (ret != NF_DROP && ret != NF_STOLEN &&
259 daddr != ip_hdr(skb)->daddr)
260 skb_dst_drop(skb);
261
262 return ret;
263}
30766f4c 264
9971a514 265static unsigned int
06198b34 266nf_nat_ipv4_out(void *priv, struct sk_buff *skb,
9971a514 267 const struct nf_hook_state *state)
30766f4c
PNA
268{
269#ifdef CONFIG_XFRM
270 const struct nf_conn *ct;
271 enum ip_conntrack_info ctinfo;
272 int err;
273#endif
274 unsigned int ret;
275
9971a514 276 ret = nf_nat_ipv4_fn(priv, skb, state);
30766f4c
PNA
277#ifdef CONFIG_XFRM
278 if (ret != NF_DROP && ret != NF_STOLEN &&
279 !(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) &&
280 (ct = nf_ct_get(skb, &ctinfo)) != NULL) {
281 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
282
283 if ((ct->tuplehash[dir].tuple.src.u3.ip !=
284 ct->tuplehash[!dir].tuple.dst.u3.ip) ||
285 (ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMP &&
286 ct->tuplehash[dir].tuple.src.u.all !=
287 ct->tuplehash[!dir].tuple.dst.u.all)) {
c7af6483 288 err = nf_xfrm_me_harder(state->net, skb, AF_INET);
30766f4c
PNA
289 if (err < 0)
290 ret = NF_DROP_ERR(err);
291 }
292 }
293#endif
294 return ret;
295}
30766f4c 296
9971a514 297static unsigned int
06198b34 298nf_nat_ipv4_local_fn(void *priv, struct sk_buff *skb,
9971a514 299 const struct nf_hook_state *state)
30766f4c
PNA
300{
301 const struct nf_conn *ct;
302 enum ip_conntrack_info ctinfo;
303 unsigned int ret;
304 int err;
305
9971a514 306 ret = nf_nat_ipv4_fn(priv, skb, state);
30766f4c
PNA
307 if (ret != NF_DROP && ret != NF_STOLEN &&
308 (ct = nf_ct_get(skb, &ctinfo)) != NULL) {
309 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
310
311 if (ct->tuplehash[dir].tuple.dst.u3.ip !=
312 ct->tuplehash[!dir].tuple.src.u3.ip) {
e45f5066 313 err = ip_route_me_harder(state->net, skb, RTN_UNSPEC);
30766f4c
PNA
314 if (err < 0)
315 ret = NF_DROP_ERR(err);
316 }
317#ifdef CONFIG_XFRM
318 else if (!(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) &&
319 ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMP &&
320 ct->tuplehash[dir].tuple.dst.u.all !=
321 ct->tuplehash[!dir].tuple.src.u.all) {
c7af6483 322 err = nf_xfrm_me_harder(state->net, skb, AF_INET);
30766f4c
PNA
323 if (err < 0)
324 ret = NF_DROP_ERR(err);
325 }
326#endif
327 }
328 return ret;
329}
9971a514
FW
330
331static const struct nf_hook_ops nf_nat_ipv4_ops[] = {
332 /* Before packet filtering, change destination */
333 {
334 .hook = nf_nat_ipv4_in,
335 .pf = NFPROTO_IPV4,
336 .hooknum = NF_INET_PRE_ROUTING,
337 .priority = NF_IP_PRI_NAT_DST,
338 },
339 /* After packet filtering, change source */
340 {
341 .hook = nf_nat_ipv4_out,
342 .pf = NFPROTO_IPV4,
343 .hooknum = NF_INET_POST_ROUTING,
344 .priority = NF_IP_PRI_NAT_SRC,
345 },
346 /* Before packet filtering, change destination */
347 {
348 .hook = nf_nat_ipv4_local_fn,
349 .pf = NFPROTO_IPV4,
350 .hooknum = NF_INET_LOCAL_OUT,
351 .priority = NF_IP_PRI_NAT_DST,
352 },
353 /* After packet filtering, change source */
354 {
355 .hook = nf_nat_ipv4_fn,
356 .pf = NFPROTO_IPV4,
357 .hooknum = NF_INET_LOCAL_IN,
358 .priority = NF_IP_PRI_NAT_SRC,
359 },
360};
361
362int nf_nat_l3proto_ipv4_register_fn(struct net *net, const struct nf_hook_ops *ops)
363{
364 return nf_nat_register_fn(net, ops, nf_nat_ipv4_ops, ARRAY_SIZE(nf_nat_ipv4_ops));
365}
366EXPORT_SYMBOL_GPL(nf_nat_l3proto_ipv4_register_fn);
367
368void nf_nat_l3proto_ipv4_unregister_fn(struct net *net, const struct nf_hook_ops *ops)
369{
370 nf_nat_unregister_fn(net, ops, ARRAY_SIZE(nf_nat_ipv4_ops));
371}
372EXPORT_SYMBOL_GPL(nf_nat_l3proto_ipv4_unregister_fn);
30766f4c 373
c7232c99
PM
374static int __init nf_nat_l3proto_ipv4_init(void)
375{
5cbabeec 376 return nf_nat_l3proto_register(&nf_nat_l3proto_ipv4);
c7232c99
PM
377}
378
379static void __exit nf_nat_l3proto_ipv4_exit(void)
380{
381 nf_nat_l3proto_unregister(&nf_nat_l3proto_ipv4);
c7232c99
PM
382}
383
384MODULE_LICENSE("GPL");
385MODULE_ALIAS("nf-nat-" __stringify(AF_INET));
386
387module_init(nf_nat_l3proto_ipv4_init);
388module_exit(nf_nat_l3proto_ipv4_exit);