netfilter: optimize nf_inet_addr_cmp
[linux-block.git] / net / netfilter / nf_nat_proto.c
CommitLineData
faec18db
FW
1/* (C) 1999-2001 Paul `Rusty' Russell
2 * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 */
8
9#include <linux/types.h>
10#include <linux/export.h>
11#include <linux/init.h>
12#include <linux/udp.h>
13#include <linux/tcp.h>
14#include <linux/icmp.h>
15#include <linux/icmpv6.h>
16
17#include <linux/dccp.h>
18#include <linux/sctp.h>
19#include <net/sctp/checksum.h>
20
21#include <linux/netfilter.h>
22#include <net/netfilter/nf_nat.h>
faec18db 23
3bf195ae
FW
24#include <linux/ipv6.h>
25#include <linux/netfilter_ipv6.h>
26#include <net/checksum.h>
27#include <net/ip6_checksum.h>
28#include <net/ip6_route.h>
29#include <net/xfrm.h>
30#include <net/ipv6.h>
31
32#include <net/netfilter/nf_conntrack_core.h>
33#include <net/netfilter/nf_conntrack.h>
14cb1a6e 34#include <linux/netfilter/nfnetlink_conntrack.h>
3bf195ae 35
03fe5efc
FW
36static void nf_csum_update(struct sk_buff *skb,
37 unsigned int iphdroff, __sum16 *check,
38 const struct nf_conntrack_tuple *t,
39 enum nf_nat_manip_type maniptype);
40
faec18db
FW
41static void
42__udp_manip_pkt(struct sk_buff *skb,
faec18db
FW
43 unsigned int iphdroff, struct udphdr *hdr,
44 const struct nf_conntrack_tuple *tuple,
45 enum nf_nat_manip_type maniptype, bool do_csum)
46{
47 __be16 *portptr, newport;
48
49 if (maniptype == NF_NAT_MANIP_SRC) {
50 /* Get rid of src port */
51 newport = tuple->src.u.udp.port;
52 portptr = &hdr->source;
53 } else {
54 /* Get rid of dst port */
55 newport = tuple->dst.u.udp.port;
56 portptr = &hdr->dest;
57 }
58 if (do_csum) {
03fe5efc 59 nf_csum_update(skb, iphdroff, &hdr->check, tuple, maniptype);
faec18db
FW
60 inet_proto_csum_replace2(&hdr->check, skb, *portptr, newport,
61 false);
62 if (!hdr->check)
63 hdr->check = CSUM_MANGLED_0;
64 }
65 *portptr = newport;
66}
67
68static bool udp_manip_pkt(struct sk_buff *skb,
faec18db
FW
69 unsigned int iphdroff, unsigned int hdroff,
70 const struct nf_conntrack_tuple *tuple,
71 enum nf_nat_manip_type maniptype)
72{
73 struct udphdr *hdr;
74 bool do_csum;
75
76 if (!skb_make_writable(skb, hdroff + sizeof(*hdr)))
77 return false;
78
79 hdr = (struct udphdr *)(skb->data + hdroff);
80 do_csum = hdr->check || skb->ip_summed == CHECKSUM_PARTIAL;
81
03fe5efc 82 __udp_manip_pkt(skb, iphdroff, hdr, tuple, maniptype, do_csum);
faec18db
FW
83 return true;
84}
85
86static bool udplite_manip_pkt(struct sk_buff *skb,
faec18db
FW
87 unsigned int iphdroff, unsigned int hdroff,
88 const struct nf_conntrack_tuple *tuple,
89 enum nf_nat_manip_type maniptype)
90{
91#ifdef CONFIG_NF_CT_PROTO_UDPLITE
92 struct udphdr *hdr;
93
94 if (!skb_make_writable(skb, hdroff + sizeof(*hdr)))
95 return false;
96
97 hdr = (struct udphdr *)(skb->data + hdroff);
03fe5efc 98 __udp_manip_pkt(skb, iphdroff, hdr, tuple, maniptype, true);
faec18db
FW
99#endif
100 return true;
101}
102
103static bool
104sctp_manip_pkt(struct sk_buff *skb,
faec18db
FW
105 unsigned int iphdroff, unsigned int hdroff,
106 const struct nf_conntrack_tuple *tuple,
107 enum nf_nat_manip_type maniptype)
108{
109#ifdef CONFIG_NF_CT_PROTO_SCTP
110 struct sctphdr *hdr;
111 int hdrsize = 8;
112
113 /* This could be an inner header returned in imcp packet; in such
114 * cases we cannot update the checksum field since it is outside
115 * of the 8 bytes of transport layer headers we are guaranteed.
116 */
117 if (skb->len >= hdroff + sizeof(*hdr))
118 hdrsize = sizeof(*hdr);
119
120 if (!skb_make_writable(skb, hdroff + hdrsize))
121 return false;
122
123 hdr = (struct sctphdr *)(skb->data + hdroff);
124
125 if (maniptype == NF_NAT_MANIP_SRC) {
126 /* Get rid of src port */
127 hdr->source = tuple->src.u.sctp.port;
128 } else {
129 /* Get rid of dst port */
130 hdr->dest = tuple->dst.u.sctp.port;
131 }
132
133 if (hdrsize < sizeof(*hdr))
134 return true;
135
136 if (skb->ip_summed != CHECKSUM_PARTIAL) {
137 hdr->checksum = sctp_compute_cksum(skb, hdroff);
138 skb->ip_summed = CHECKSUM_NONE;
139 }
140
141#endif
142 return true;
143}
144
145static bool
146tcp_manip_pkt(struct sk_buff *skb,
faec18db
FW
147 unsigned int iphdroff, unsigned int hdroff,
148 const struct nf_conntrack_tuple *tuple,
149 enum nf_nat_manip_type maniptype)
150{
151 struct tcphdr *hdr;
152 __be16 *portptr, newport, oldport;
153 int hdrsize = 8; /* TCP connection tracking guarantees this much */
154
155 /* this could be a inner header returned in icmp packet; in such
156 cases we cannot update the checksum field since it is outside of
157 the 8 bytes of transport layer headers we are guaranteed */
158 if (skb->len >= hdroff + sizeof(struct tcphdr))
159 hdrsize = sizeof(struct tcphdr);
160
161 if (!skb_make_writable(skb, hdroff + hdrsize))
162 return false;
163
164 hdr = (struct tcphdr *)(skb->data + hdroff);
165
166 if (maniptype == NF_NAT_MANIP_SRC) {
167 /* Get rid of src port */
168 newport = tuple->src.u.tcp.port;
169 portptr = &hdr->source;
170 } else {
171 /* Get rid of dst port */
172 newport = tuple->dst.u.tcp.port;
173 portptr = &hdr->dest;
174 }
175
176 oldport = *portptr;
177 *portptr = newport;
178
179 if (hdrsize < sizeof(*hdr))
180 return true;
181
03fe5efc 182 nf_csum_update(skb, iphdroff, &hdr->check, tuple, maniptype);
faec18db
FW
183 inet_proto_csum_replace2(&hdr->check, skb, oldport, newport, false);
184 return true;
185}
186
187static bool
188dccp_manip_pkt(struct sk_buff *skb,
faec18db
FW
189 unsigned int iphdroff, unsigned int hdroff,
190 const struct nf_conntrack_tuple *tuple,
191 enum nf_nat_manip_type maniptype)
192{
193#ifdef CONFIG_NF_CT_PROTO_DCCP
194 struct dccp_hdr *hdr;
195 __be16 *portptr, oldport, newport;
196 int hdrsize = 8; /* DCCP connection tracking guarantees this much */
197
198 if (skb->len >= hdroff + sizeof(struct dccp_hdr))
199 hdrsize = sizeof(struct dccp_hdr);
200
201 if (!skb_make_writable(skb, hdroff + hdrsize))
202 return false;
203
204 hdr = (struct dccp_hdr *)(skb->data + hdroff);
205
206 if (maniptype == NF_NAT_MANIP_SRC) {
207 newport = tuple->src.u.dccp.port;
208 portptr = &hdr->dccph_sport;
209 } else {
210 newport = tuple->dst.u.dccp.port;
211 portptr = &hdr->dccph_dport;
212 }
213
214 oldport = *portptr;
215 *portptr = newport;
216
217 if (hdrsize < sizeof(*hdr))
218 return true;
219
03fe5efc 220 nf_csum_update(skb, iphdroff, &hdr->dccph_checksum, tuple, maniptype);
faec18db
FW
221 inet_proto_csum_replace2(&hdr->dccph_checksum, skb, oldport, newport,
222 false);
223#endif
224 return true;
225}
226
227static bool
228icmp_manip_pkt(struct sk_buff *skb,
faec18db
FW
229 unsigned int iphdroff, unsigned int hdroff,
230 const struct nf_conntrack_tuple *tuple,
231 enum nf_nat_manip_type maniptype)
232{
233 struct icmphdr *hdr;
234
235 if (!skb_make_writable(skb, hdroff + sizeof(*hdr)))
236 return false;
237
238 hdr = (struct icmphdr *)(skb->data + hdroff);
239 inet_proto_csum_replace2(&hdr->checksum, skb,
240 hdr->un.echo.id, tuple->src.u.icmp.id, false);
241 hdr->un.echo.id = tuple->src.u.icmp.id;
242 return true;
243}
244
245static bool
246icmpv6_manip_pkt(struct sk_buff *skb,
faec18db
FW
247 unsigned int iphdroff, unsigned int hdroff,
248 const struct nf_conntrack_tuple *tuple,
249 enum nf_nat_manip_type maniptype)
250{
251 struct icmp6hdr *hdr;
252
253 if (!skb_make_writable(skb, hdroff + sizeof(*hdr)))
254 return false;
255
256 hdr = (struct icmp6hdr *)(skb->data + hdroff);
03fe5efc 257 nf_csum_update(skb, iphdroff, &hdr->icmp6_cksum, tuple, maniptype);
faec18db
FW
258 if (hdr->icmp6_type == ICMPV6_ECHO_REQUEST ||
259 hdr->icmp6_type == ICMPV6_ECHO_REPLY) {
260 inet_proto_csum_replace2(&hdr->icmp6_cksum, skb,
261 hdr->icmp6_identifier,
262 tuple->src.u.icmp.id, false);
263 hdr->icmp6_identifier = tuple->src.u.icmp.id;
264 }
265 return true;
266}
267
268/* manipulate a GRE packet according to maniptype */
269static bool
270gre_manip_pkt(struct sk_buff *skb,
faec18db
FW
271 unsigned int iphdroff, unsigned int hdroff,
272 const struct nf_conntrack_tuple *tuple,
273 enum nf_nat_manip_type maniptype)
274{
275#if IS_ENABLED(CONFIG_NF_CT_PROTO_GRE)
276 const struct gre_base_hdr *greh;
277 struct pptp_gre_header *pgreh;
278
279 /* pgreh includes two optional 32bit fields which are not required
280 * to be there. That's where the magic '8' comes from */
281 if (!skb_make_writable(skb, hdroff + sizeof(*pgreh) - 8))
282 return false;
283
284 greh = (void *)skb->data + hdroff;
285 pgreh = (struct pptp_gre_header *)greh;
286
287 /* we only have destination manip of a packet, since 'source key'
288 * is not present in the packet itself */
289 if (maniptype != NF_NAT_MANIP_DST)
290 return true;
291
292 switch (greh->flags & GRE_VERSION) {
293 case GRE_VERSION_0:
294 /* We do not currently NAT any GREv0 packets.
295 * Try to behave like "nf_nat_proto_unknown" */
296 break;
297 case GRE_VERSION_1:
298 pr_debug("call_id -> 0x%04x\n", ntohs(tuple->dst.u.gre.key));
299 pgreh->call_id = tuple->dst.u.gre.key;
300 break;
301 default:
302 pr_debug("can't nat unknown GRE version\n");
303 return false;
304 }
305#endif
306 return true;
307}
308
14cb1a6e 309static bool l4proto_manip_pkt(struct sk_buff *skb,
faec18db
FW
310 unsigned int iphdroff, unsigned int hdroff,
311 const struct nf_conntrack_tuple *tuple,
312 enum nf_nat_manip_type maniptype)
313{
314 switch (tuple->dst.protonum) {
315 case IPPROTO_TCP:
03fe5efc 316 return tcp_manip_pkt(skb, iphdroff, hdroff,
faec18db
FW
317 tuple, maniptype);
318 case IPPROTO_UDP:
03fe5efc 319 return udp_manip_pkt(skb, iphdroff, hdroff,
faec18db
FW
320 tuple, maniptype);
321 case IPPROTO_UDPLITE:
03fe5efc 322 return udplite_manip_pkt(skb, iphdroff, hdroff,
faec18db
FW
323 tuple, maniptype);
324 case IPPROTO_SCTP:
03fe5efc 325 return sctp_manip_pkt(skb, iphdroff, hdroff,
faec18db
FW
326 tuple, maniptype);
327 case IPPROTO_ICMP:
03fe5efc 328 return icmp_manip_pkt(skb, iphdroff, hdroff,
faec18db
FW
329 tuple, maniptype);
330 case IPPROTO_ICMPV6:
03fe5efc 331 return icmpv6_manip_pkt(skb, iphdroff, hdroff,
faec18db
FW
332 tuple, maniptype);
333 case IPPROTO_DCCP:
03fe5efc 334 return dccp_manip_pkt(skb, iphdroff, hdroff,
faec18db
FW
335 tuple, maniptype);
336 case IPPROTO_GRE:
03fe5efc 337 return gre_manip_pkt(skb, iphdroff, hdroff,
faec18db
FW
338 tuple, maniptype);
339 }
340
341 /* If we don't know protocol -- no error, pass it unmodified. */
342 return true;
343}
3bf195ae
FW
344
345static bool nf_nat_ipv4_manip_pkt(struct sk_buff *skb,
346 unsigned int iphdroff,
347 const struct nf_conntrack_tuple *target,
348 enum nf_nat_manip_type maniptype)
349{
350 struct iphdr *iph;
351 unsigned int hdroff;
352
353 if (!skb_make_writable(skb, iphdroff + sizeof(*iph)))
354 return false;
355
356 iph = (void *)skb->data + iphdroff;
357 hdroff = iphdroff + iph->ihl * 4;
358
03fe5efc 359 if (!l4proto_manip_pkt(skb, iphdroff, hdroff, target, maniptype))
3bf195ae
FW
360 return false;
361 iph = (void *)skb->data + iphdroff;
362
363 if (maniptype == NF_NAT_MANIP_SRC) {
364 csum_replace4(&iph->check, iph->saddr, target->src.u3.ip);
365 iph->saddr = target->src.u3.ip;
366 } else {
367 csum_replace4(&iph->check, iph->daddr, target->dst.u3.ip);
368 iph->daddr = target->dst.u3.ip;
369 }
370 return true;
371}
372
373static bool nf_nat_ipv6_manip_pkt(struct sk_buff *skb,
374 unsigned int iphdroff,
375 const struct nf_conntrack_tuple *target,
376 enum nf_nat_manip_type maniptype)
377{
378#if IS_ENABLED(CONFIG_IPV6)
379 struct ipv6hdr *ipv6h;
380 __be16 frag_off;
381 int hdroff;
382 u8 nexthdr;
383
384 if (!skb_make_writable(skb, iphdroff + sizeof(*ipv6h)))
385 return false;
386
387 ipv6h = (void *)skb->data + iphdroff;
388 nexthdr = ipv6h->nexthdr;
389 hdroff = ipv6_skip_exthdr(skb, iphdroff + sizeof(*ipv6h),
390 &nexthdr, &frag_off);
391 if (hdroff < 0)
392 goto manip_addr;
393
394 if ((frag_off & htons(~0x7)) == 0 &&
03fe5efc 395 !l4proto_manip_pkt(skb, iphdroff, hdroff, target, maniptype))
3bf195ae
FW
396 return false;
397
398 /* must reload, offset might have changed */
399 ipv6h = (void *)skb->data + iphdroff;
400
401manip_addr:
402 if (maniptype == NF_NAT_MANIP_SRC)
403 ipv6h->saddr = target->src.u3.in6;
404 else
405 ipv6h->daddr = target->dst.u3.in6;
406
407#endif
408 return true;
409}
410
2e666b22
FW
411unsigned int nf_nat_manip_pkt(struct sk_buff *skb, struct nf_conn *ct,
412 enum nf_nat_manip_type mtype,
413 enum ip_conntrack_dir dir)
414{
415 struct nf_conntrack_tuple target;
416
417 /* We are aiming to look like inverse of other direction. */
418 nf_ct_invert_tuple(&target, &ct->tuplehash[!dir].tuple);
419
420 switch (target.src.l3num) {
421 case NFPROTO_IPV6:
422 if (nf_nat_ipv6_manip_pkt(skb, 0, &target, mtype))
423 return NF_ACCEPT;
424 break;
425 case NFPROTO_IPV4:
426 if (nf_nat_ipv4_manip_pkt(skb, 0, &target, mtype))
427 return NF_ACCEPT;
428 break;
429 default:
430 WARN_ON_ONCE(1);
431 break;
432 }
433
434 return NF_DROP;
435}
436
3bf195ae
FW
437static void nf_nat_ipv4_csum_update(struct sk_buff *skb,
438 unsigned int iphdroff, __sum16 *check,
439 const struct nf_conntrack_tuple *t,
440 enum nf_nat_manip_type maniptype)
441{
442 struct iphdr *iph = (struct iphdr *)(skb->data + iphdroff);
443 __be32 oldip, newip;
444
445 if (maniptype == NF_NAT_MANIP_SRC) {
446 oldip = iph->saddr;
447 newip = t->src.u3.ip;
448 } else {
449 oldip = iph->daddr;
450 newip = t->dst.u3.ip;
451 }
452 inet_proto_csum_replace4(check, skb, oldip, newip, true);
453}
454
455static void nf_nat_ipv6_csum_update(struct sk_buff *skb,
456 unsigned int iphdroff, __sum16 *check,
457 const struct nf_conntrack_tuple *t,
458 enum nf_nat_manip_type maniptype)
459{
460#if IS_ENABLED(CONFIG_IPV6)
461 const struct ipv6hdr *ipv6h = (struct ipv6hdr *)(skb->data + iphdroff);
462 const struct in6_addr *oldip, *newip;
463
464 if (maniptype == NF_NAT_MANIP_SRC) {
465 oldip = &ipv6h->saddr;
466 newip = &t->src.u3.in6;
467 } else {
468 oldip = &ipv6h->daddr;
469 newip = &t->dst.u3.in6;
470 }
471 inet_proto_csum_replace16(check, skb, oldip->s6_addr32,
472 newip->s6_addr32, true);
473#endif
474}
475
03fe5efc
FW
476static void nf_csum_update(struct sk_buff *skb,
477 unsigned int iphdroff, __sum16 *check,
478 const struct nf_conntrack_tuple *t,
479 enum nf_nat_manip_type maniptype)
480{
481 switch (t->src.l3num) {
482 case NFPROTO_IPV4:
483 nf_nat_ipv4_csum_update(skb, iphdroff, check, t, maniptype);
484 return;
485 case NFPROTO_IPV6:
486 nf_nat_ipv6_csum_update(skb, iphdroff, check, t, maniptype);
487 return;
488 }
489}
490
3bf195ae
FW
491static void nf_nat_ipv4_csum_recalc(struct sk_buff *skb,
492 u8 proto, void *data, __sum16 *check,
493 int datalen, int oldlen)
494{
495 if (skb->ip_summed != CHECKSUM_PARTIAL) {
496 const struct iphdr *iph = ip_hdr(skb);
497
498 skb->ip_summed = CHECKSUM_PARTIAL;
499 skb->csum_start = skb_headroom(skb) + skb_network_offset(skb) +
500 ip_hdrlen(skb);
501 skb->csum_offset = (void *)check - data;
502 *check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, datalen,
503 proto, 0);
504 } else {
505 inet_proto_csum_replace2(check, skb,
506 htons(oldlen), htons(datalen), true);
507 }
508}
509
510#if IS_ENABLED(CONFIG_IPV6)
511static void nf_nat_ipv6_csum_recalc(struct sk_buff *skb,
512 u8 proto, void *data, __sum16 *check,
513 int datalen, int oldlen)
514{
515 if (skb->ip_summed != CHECKSUM_PARTIAL) {
516 const struct ipv6hdr *ipv6h = ipv6_hdr(skb);
517
518 skb->ip_summed = CHECKSUM_PARTIAL;
519 skb->csum_start = skb_headroom(skb) + skb_network_offset(skb) +
520 (data - (void *)skb->data);
521 skb->csum_offset = (void *)check - data;
522 *check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr,
523 datalen, proto, 0);
524 } else {
525 inet_proto_csum_replace2(check, skb,
526 htons(oldlen), htons(datalen), true);
527 }
528}
529#endif
530
dac3fe72
FW
531void nf_nat_csum_recalc(struct sk_buff *skb,
532 u8 nfproto, u8 proto, void *data, __sum16 *check,
533 int datalen, int oldlen)
534{
535 switch (nfproto) {
536 case NFPROTO_IPV4:
537 nf_nat_ipv4_csum_recalc(skb, proto, data, check,
538 datalen, oldlen);
539 return;
540#if IS_ENABLED(CONFIG_IPV6)
541 case NFPROTO_IPV6:
542 nf_nat_ipv6_csum_recalc(skb, proto, data, check,
543 datalen, oldlen);
544 return;
545#endif
546 }
547
548 WARN_ON_ONCE(1);
549}
550
3bf195ae
FW
551int nf_nat_icmp_reply_translation(struct sk_buff *skb,
552 struct nf_conn *ct,
553 enum ip_conntrack_info ctinfo,
554 unsigned int hooknum)
555{
556 struct {
557 struct icmphdr icmp;
558 struct iphdr ip;
559 } *inside;
560 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
561 enum nf_nat_manip_type manip = HOOK2MANIP(hooknum);
562 unsigned int hdrlen = ip_hdrlen(skb);
563 struct nf_conntrack_tuple target;
564 unsigned long statusbit;
565
566 WARN_ON(ctinfo != IP_CT_RELATED && ctinfo != IP_CT_RELATED_REPLY);
567
568 if (!skb_make_writable(skb, hdrlen + sizeof(*inside)))
569 return 0;
570 if (nf_ip_checksum(skb, hooknum, hdrlen, 0))
571 return 0;
572
573 inside = (void *)skb->data + hdrlen;
574 if (inside->icmp.type == ICMP_REDIRECT) {
575 if ((ct->status & IPS_NAT_DONE_MASK) != IPS_NAT_DONE_MASK)
576 return 0;
577 if (ct->status & IPS_NAT_MASK)
578 return 0;
579 }
580
581 if (manip == NF_NAT_MANIP_SRC)
582 statusbit = IPS_SRC_NAT;
583 else
584 statusbit = IPS_DST_NAT;
585
586 /* Invert if this is reply direction */
587 if (dir == IP_CT_DIR_REPLY)
588 statusbit ^= IPS_NAT_MASK;
589
590 if (!(ct->status & statusbit))
591 return 1;
592
593 if (!nf_nat_ipv4_manip_pkt(skb, hdrlen + sizeof(inside->icmp),
594 &ct->tuplehash[!dir].tuple, !manip))
595 return 0;
596
597 if (skb->ip_summed != CHECKSUM_PARTIAL) {
598 /* Reloading "inside" here since manip_pkt may reallocate */
599 inside = (void *)skb->data + hdrlen;
600 inside->icmp.checksum = 0;
601 inside->icmp.checksum =
602 csum_fold(skb_checksum(skb, hdrlen,
603 skb->len - hdrlen, 0));
604 }
605
606 /* Change outer to look like the reply to an incoming packet */
607 nf_ct_invert_tuple(&target, &ct->tuplehash[!dir].tuple);
608 target.dst.protonum = IPPROTO_ICMP;
609 if (!nf_nat_ipv4_manip_pkt(skb, 0, &target, manip))
610 return 0;
611
612 return 1;
613}
614EXPORT_SYMBOL_GPL(nf_nat_icmp_reply_translation);
615
616static unsigned int
617nf_nat_ipv4_fn(void *priv, struct sk_buff *skb,
618 const struct nf_hook_state *state)
619{
620 struct nf_conn *ct;
621 enum ip_conntrack_info ctinfo;
622
623 ct = nf_ct_get(skb, &ctinfo);
624 if (!ct)
625 return NF_ACCEPT;
626
627 if (ctinfo == IP_CT_RELATED || ctinfo == IP_CT_RELATED_REPLY) {
628 if (ip_hdr(skb)->protocol == IPPROTO_ICMP) {
629 if (!nf_nat_icmp_reply_translation(skb, ct, ctinfo,
630 state->hook))
631 return NF_DROP;
632 else
633 return NF_ACCEPT;
634 }
635 }
636
637 return nf_nat_inet_fn(priv, skb, state);
638}
639
640static unsigned int
641nf_nat_ipv4_in(void *priv, struct sk_buff *skb,
642 const struct nf_hook_state *state)
643{
644 unsigned int ret;
645 __be32 daddr = ip_hdr(skb)->daddr;
646
647 ret = nf_nat_ipv4_fn(priv, skb, state);
648 if (ret == NF_ACCEPT && daddr != ip_hdr(skb)->daddr)
649 skb_dst_drop(skb);
650
651 return ret;
652}
653
654static unsigned int
655nf_nat_ipv4_out(void *priv, struct sk_buff *skb,
656 const struct nf_hook_state *state)
657{
658#ifdef CONFIG_XFRM
659 const struct nf_conn *ct;
660 enum ip_conntrack_info ctinfo;
661 int err;
662#endif
663 unsigned int ret;
664
665 ret = nf_nat_ipv4_fn(priv, skb, state);
666#ifdef CONFIG_XFRM
667 if (ret != NF_ACCEPT)
668 return ret;
669
670 if (IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED)
671 return ret;
672
673 ct = nf_ct_get(skb, &ctinfo);
674 if (ct) {
675 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
676
677 if (ct->tuplehash[dir].tuple.src.u3.ip !=
678 ct->tuplehash[!dir].tuple.dst.u3.ip ||
679 (ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMP &&
680 ct->tuplehash[dir].tuple.src.u.all !=
681 ct->tuplehash[!dir].tuple.dst.u.all)) {
682 err = nf_xfrm_me_harder(state->net, skb, AF_INET);
683 if (err < 0)
684 ret = NF_DROP_ERR(err);
685 }
686 }
687#endif
688 return ret;
689}
690
691static unsigned int
692nf_nat_ipv4_local_fn(void *priv, struct sk_buff *skb,
693 const struct nf_hook_state *state)
694{
695 const struct nf_conn *ct;
696 enum ip_conntrack_info ctinfo;
697 unsigned int ret;
698 int err;
699
700 ret = nf_nat_ipv4_fn(priv, skb, state);
701 if (ret != NF_ACCEPT)
702 return ret;
703
704 ct = nf_ct_get(skb, &ctinfo);
705 if (ct) {
706 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
707
708 if (ct->tuplehash[dir].tuple.dst.u3.ip !=
709 ct->tuplehash[!dir].tuple.src.u3.ip) {
710 err = ip_route_me_harder(state->net, skb, RTN_UNSPEC);
711 if (err < 0)
712 ret = NF_DROP_ERR(err);
713 }
714#ifdef CONFIG_XFRM
715 else if (!(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) &&
716 ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMP &&
717 ct->tuplehash[dir].tuple.dst.u.all !=
718 ct->tuplehash[!dir].tuple.src.u.all) {
719 err = nf_xfrm_me_harder(state->net, skb, AF_INET);
720 if (err < 0)
721 ret = NF_DROP_ERR(err);
722 }
723#endif
724 }
725 return ret;
726}
727
728static const struct nf_hook_ops nf_nat_ipv4_ops[] = {
729 /* Before packet filtering, change destination */
730 {
731 .hook = nf_nat_ipv4_in,
732 .pf = NFPROTO_IPV4,
733 .hooknum = NF_INET_PRE_ROUTING,
734 .priority = NF_IP_PRI_NAT_DST,
735 },
736 /* After packet filtering, change source */
737 {
738 .hook = nf_nat_ipv4_out,
739 .pf = NFPROTO_IPV4,
740 .hooknum = NF_INET_POST_ROUTING,
741 .priority = NF_IP_PRI_NAT_SRC,
742 },
743 /* Before packet filtering, change destination */
744 {
745 .hook = nf_nat_ipv4_local_fn,
746 .pf = NFPROTO_IPV4,
747 .hooknum = NF_INET_LOCAL_OUT,
748 .priority = NF_IP_PRI_NAT_DST,
749 },
750 /* After packet filtering, change source */
751 {
752 .hook = nf_nat_ipv4_fn,
753 .pf = NFPROTO_IPV4,
754 .hooknum = NF_INET_LOCAL_IN,
755 .priority = NF_IP_PRI_NAT_SRC,
756 },
757};
758
d2c5c103 759int nf_nat_ipv4_register_fn(struct net *net, const struct nf_hook_ops *ops)
3bf195ae
FW
760{
761 return nf_nat_register_fn(net, ops, nf_nat_ipv4_ops, ARRAY_SIZE(nf_nat_ipv4_ops));
762}
d2c5c103 763EXPORT_SYMBOL_GPL(nf_nat_ipv4_register_fn);
3bf195ae 764
d2c5c103 765void nf_nat_ipv4_unregister_fn(struct net *net, const struct nf_hook_ops *ops)
3bf195ae
FW
766{
767 nf_nat_unregister_fn(net, ops, ARRAY_SIZE(nf_nat_ipv4_ops));
768}
d2c5c103 769EXPORT_SYMBOL_GPL(nf_nat_ipv4_unregister_fn);
3bf195ae 770
3bf195ae 771#if IS_ENABLED(CONFIG_IPV6)
3bf195ae
FW
772int nf_nat_icmpv6_reply_translation(struct sk_buff *skb,
773 struct nf_conn *ct,
774 enum ip_conntrack_info ctinfo,
775 unsigned int hooknum,
776 unsigned int hdrlen)
777{
778 struct {
779 struct icmp6hdr icmp6;
780 struct ipv6hdr ip6;
781 } *inside;
782 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
783 enum nf_nat_manip_type manip = HOOK2MANIP(hooknum);
784 struct nf_conntrack_tuple target;
785 unsigned long statusbit;
786
787 WARN_ON(ctinfo != IP_CT_RELATED && ctinfo != IP_CT_RELATED_REPLY);
788
789 if (!skb_make_writable(skb, hdrlen + sizeof(*inside)))
790 return 0;
791 if (nf_ip6_checksum(skb, hooknum, hdrlen, IPPROTO_ICMPV6))
792 return 0;
793
794 inside = (void *)skb->data + hdrlen;
795 if (inside->icmp6.icmp6_type == NDISC_REDIRECT) {
796 if ((ct->status & IPS_NAT_DONE_MASK) != IPS_NAT_DONE_MASK)
797 return 0;
798 if (ct->status & IPS_NAT_MASK)
799 return 0;
800 }
801
802 if (manip == NF_NAT_MANIP_SRC)
803 statusbit = IPS_SRC_NAT;
804 else
805 statusbit = IPS_DST_NAT;
806
807 /* Invert if this is reply direction */
808 if (dir == IP_CT_DIR_REPLY)
809 statusbit ^= IPS_NAT_MASK;
810
811 if (!(ct->status & statusbit))
812 return 1;
813
814 if (!nf_nat_ipv6_manip_pkt(skb, hdrlen + sizeof(inside->icmp6),
815 &ct->tuplehash[!dir].tuple, !manip))
816 return 0;
817
818 if (skb->ip_summed != CHECKSUM_PARTIAL) {
819 struct ipv6hdr *ipv6h = ipv6_hdr(skb);
820
821 inside = (void *)skb->data + hdrlen;
822 inside->icmp6.icmp6_cksum = 0;
823 inside->icmp6.icmp6_cksum =
824 csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr,
825 skb->len - hdrlen, IPPROTO_ICMPV6,
826 skb_checksum(skb, hdrlen,
827 skb->len - hdrlen, 0));
828 }
829
830 nf_ct_invert_tuple(&target, &ct->tuplehash[!dir].tuple);
831 target.dst.protonum = IPPROTO_ICMPV6;
832 if (!nf_nat_ipv6_manip_pkt(skb, 0, &target, manip))
833 return 0;
834
835 return 1;
836}
837EXPORT_SYMBOL_GPL(nf_nat_icmpv6_reply_translation);
838
839static unsigned int
840nf_nat_ipv6_fn(void *priv, struct sk_buff *skb,
841 const struct nf_hook_state *state)
842{
843 struct nf_conn *ct;
844 enum ip_conntrack_info ctinfo;
845 __be16 frag_off;
846 int hdrlen;
847 u8 nexthdr;
848
849 ct = nf_ct_get(skb, &ctinfo);
850 /* Can't track? It's not due to stress, or conntrack would
851 * have dropped it. Hence it's the user's responsibilty to
852 * packet filter it out, or implement conntrack/NAT for that
853 * protocol. 8) --RR
854 */
855 if (!ct)
856 return NF_ACCEPT;
857
858 if (ctinfo == IP_CT_RELATED || ctinfo == IP_CT_RELATED_REPLY) {
859 nexthdr = ipv6_hdr(skb)->nexthdr;
860 hdrlen = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr),
861 &nexthdr, &frag_off);
862
863 if (hdrlen >= 0 && nexthdr == IPPROTO_ICMPV6) {
864 if (!nf_nat_icmpv6_reply_translation(skb, ct, ctinfo,
865 state->hook,
866 hdrlen))
867 return NF_DROP;
868 else
869 return NF_ACCEPT;
870 }
871 }
872
873 return nf_nat_inet_fn(priv, skb, state);
874}
875
876static unsigned int
877nf_nat_ipv6_in(void *priv, struct sk_buff *skb,
878 const struct nf_hook_state *state)
879{
880 unsigned int ret;
881 struct in6_addr daddr = ipv6_hdr(skb)->daddr;
882
883 ret = nf_nat_ipv6_fn(priv, skb, state);
884 if (ret != NF_DROP && ret != NF_STOLEN &&
885 ipv6_addr_cmp(&daddr, &ipv6_hdr(skb)->daddr))
886 skb_dst_drop(skb);
887
888 return ret;
889}
890
891static unsigned int
892nf_nat_ipv6_out(void *priv, struct sk_buff *skb,
893 const struct nf_hook_state *state)
894{
895#ifdef CONFIG_XFRM
896 const struct nf_conn *ct;
897 enum ip_conntrack_info ctinfo;
898 int err;
899#endif
900 unsigned int ret;
901
902 ret = nf_nat_ipv6_fn(priv, skb, state);
903#ifdef CONFIG_XFRM
904 if (ret != NF_ACCEPT)
905 return ret;
906
907 if (IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED)
908 return ret;
909 ct = nf_ct_get(skb, &ctinfo);
910 if (ct) {
911 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
912
913 if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.src.u3,
914 &ct->tuplehash[!dir].tuple.dst.u3) ||
915 (ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMPV6 &&
916 ct->tuplehash[dir].tuple.src.u.all !=
917 ct->tuplehash[!dir].tuple.dst.u.all)) {
918 err = nf_xfrm_me_harder(state->net, skb, AF_INET6);
919 if (err < 0)
920 ret = NF_DROP_ERR(err);
921 }
922 }
923#endif
924
925 return ret;
926}
927
928static int nat_route_me_harder(struct net *net, struct sk_buff *skb)
929{
930#ifdef CONFIG_IPV6_MODULE
931 const struct nf_ipv6_ops *v6_ops = nf_get_ipv6_ops();
932
933 if (!v6_ops)
934 return -EHOSTUNREACH;
935
936 return v6_ops->route_me_harder(net, skb);
937#else
938 return ip6_route_me_harder(net, skb);
939#endif
940}
941
942static unsigned int
943nf_nat_ipv6_local_fn(void *priv, struct sk_buff *skb,
944 const struct nf_hook_state *state)
945{
946 const struct nf_conn *ct;
947 enum ip_conntrack_info ctinfo;
948 unsigned int ret;
949 int err;
950
951 ret = nf_nat_ipv6_fn(priv, skb, state);
952 if (ret != NF_ACCEPT)
953 return ret;
954
955 ct = nf_ct_get(skb, &ctinfo);
956 if (ct) {
957 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
958
959 if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.dst.u3,
960 &ct->tuplehash[!dir].tuple.src.u3)) {
961 err = nat_route_me_harder(state->net, skb);
962 if (err < 0)
963 ret = NF_DROP_ERR(err);
964 }
965#ifdef CONFIG_XFRM
966 else if (!(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) &&
967 ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMPV6 &&
968 ct->tuplehash[dir].tuple.dst.u.all !=
969 ct->tuplehash[!dir].tuple.src.u.all) {
970 err = nf_xfrm_me_harder(state->net, skb, AF_INET6);
971 if (err < 0)
972 ret = NF_DROP_ERR(err);
973 }
974#endif
975 }
976
977 return ret;
978}
979
980static const struct nf_hook_ops nf_nat_ipv6_ops[] = {
981 /* Before packet filtering, change destination */
982 {
983 .hook = nf_nat_ipv6_in,
984 .pf = NFPROTO_IPV6,
985 .hooknum = NF_INET_PRE_ROUTING,
986 .priority = NF_IP6_PRI_NAT_DST,
987 },
988 /* After packet filtering, change source */
989 {
990 .hook = nf_nat_ipv6_out,
991 .pf = NFPROTO_IPV6,
992 .hooknum = NF_INET_POST_ROUTING,
993 .priority = NF_IP6_PRI_NAT_SRC,
994 },
995 /* Before packet filtering, change destination */
996 {
997 .hook = nf_nat_ipv6_local_fn,
998 .pf = NFPROTO_IPV6,
999 .hooknum = NF_INET_LOCAL_OUT,
1000 .priority = NF_IP6_PRI_NAT_DST,
1001 },
1002 /* After packet filtering, change source */
1003 {
1004 .hook = nf_nat_ipv6_fn,
1005 .pf = NFPROTO_IPV6,
1006 .hooknum = NF_INET_LOCAL_IN,
1007 .priority = NF_IP6_PRI_NAT_SRC,
1008 },
1009};
1010
d2c5c103 1011int nf_nat_ipv6_register_fn(struct net *net, const struct nf_hook_ops *ops)
3bf195ae
FW
1012{
1013 return nf_nat_register_fn(net, ops, nf_nat_ipv6_ops,
1014 ARRAY_SIZE(nf_nat_ipv6_ops));
1015}
d2c5c103 1016EXPORT_SYMBOL_GPL(nf_nat_ipv6_register_fn);
3bf195ae 1017
d2c5c103 1018void nf_nat_ipv6_unregister_fn(struct net *net, const struct nf_hook_ops *ops)
3bf195ae
FW
1019{
1020 nf_nat_unregister_fn(net, ops, ARRAY_SIZE(nf_nat_ipv6_ops));
1021}
d2c5c103 1022EXPORT_SYMBOL_GPL(nf_nat_ipv6_unregister_fn);
3bf195ae 1023#endif /* CONFIG_IPV6 */