1 // SPDX-License-Identifier: GPL-2.0-only
2 /* (C) 1999-2001 Paul `Rusty' Russell
3 * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
6 #include <linux/types.h>
7 #include <linux/export.h>
8 #include <linux/init.h>
10 #include <linux/tcp.h>
11 #include <linux/icmp.h>
12 #include <linux/icmpv6.h>
14 #include <linux/dccp.h>
15 #include <linux/sctp.h>
16 #include <net/sctp/checksum.h>
18 #include <linux/netfilter.h>
19 #include <net/netfilter/nf_nat.h>
21 #include <linux/ipv6.h>
22 #include <linux/netfilter_ipv6.h>
23 #include <net/checksum.h>
24 #include <net/ip6_checksum.h>
25 #include <net/ip6_route.h>
29 #include <net/netfilter/nf_conntrack_core.h>
30 #include <net/netfilter/nf_conntrack.h>
31 #include <linux/netfilter/nfnetlink_conntrack.h>
33 static void nf_csum_update(struct sk_buff
*skb
,
34 unsigned int iphdroff
, __sum16
*check
,
35 const struct nf_conntrack_tuple
*t
,
36 enum nf_nat_manip_type maniptype
);
39 __udp_manip_pkt(struct sk_buff
*skb
,
40 unsigned int iphdroff
, struct udphdr
*hdr
,
41 const struct nf_conntrack_tuple
*tuple
,
42 enum nf_nat_manip_type maniptype
, bool do_csum
)
44 __be16
*portptr
, newport
;
46 if (maniptype
== NF_NAT_MANIP_SRC
) {
47 /* Get rid of src port */
48 newport
= tuple
->src
.u
.udp
.port
;
49 portptr
= &hdr
->source
;
51 /* Get rid of dst port */
52 newport
= tuple
->dst
.u
.udp
.port
;
56 nf_csum_update(skb
, iphdroff
, &hdr
->check
, tuple
, maniptype
);
57 inet_proto_csum_replace2(&hdr
->check
, skb
, *portptr
, newport
,
60 hdr
->check
= CSUM_MANGLED_0
;
65 static bool udp_manip_pkt(struct sk_buff
*skb
,
66 unsigned int iphdroff
, unsigned int hdroff
,
67 const struct nf_conntrack_tuple
*tuple
,
68 enum nf_nat_manip_type maniptype
)
72 if (skb_ensure_writable(skb
, hdroff
+ sizeof(*hdr
)))
75 hdr
= (struct udphdr
*)(skb
->data
+ hdroff
);
76 __udp_manip_pkt(skb
, iphdroff
, hdr
, tuple
, maniptype
, !!hdr
->check
);
81 static bool udplite_manip_pkt(struct sk_buff
*skb
,
82 unsigned int iphdroff
, unsigned int hdroff
,
83 const struct nf_conntrack_tuple
*tuple
,
84 enum nf_nat_manip_type maniptype
)
86 #ifdef CONFIG_NF_CT_PROTO_UDPLITE
89 if (skb_ensure_writable(skb
, hdroff
+ sizeof(*hdr
)))
92 hdr
= (struct udphdr
*)(skb
->data
+ hdroff
);
93 __udp_manip_pkt(skb
, iphdroff
, hdr
, tuple
, maniptype
, true);
99 sctp_manip_pkt(struct sk_buff
*skb
,
100 unsigned int iphdroff
, unsigned int hdroff
,
101 const struct nf_conntrack_tuple
*tuple
,
102 enum nf_nat_manip_type maniptype
)
104 #ifdef CONFIG_NF_CT_PROTO_SCTP
108 /* This could be an inner header returned in imcp packet; in such
109 * cases we cannot update the checksum field since it is outside
110 * of the 8 bytes of transport layer headers we are guaranteed.
112 if (skb
->len
>= hdroff
+ sizeof(*hdr
))
113 hdrsize
= sizeof(*hdr
);
115 if (skb_ensure_writable(skb
, hdroff
+ hdrsize
))
118 hdr
= (struct sctphdr
*)(skb
->data
+ hdroff
);
120 if (maniptype
== NF_NAT_MANIP_SRC
) {
121 /* Get rid of src port */
122 hdr
->source
= tuple
->src
.u
.sctp
.port
;
124 /* Get rid of dst port */
125 hdr
->dest
= tuple
->dst
.u
.sctp
.port
;
128 if (hdrsize
< sizeof(*hdr
))
131 if (skb
->ip_summed
!= CHECKSUM_PARTIAL
) {
132 hdr
->checksum
= sctp_compute_cksum(skb
, hdroff
);
133 skb
->ip_summed
= CHECKSUM_NONE
;
141 tcp_manip_pkt(struct sk_buff
*skb
,
142 unsigned int iphdroff
, unsigned int hdroff
,
143 const struct nf_conntrack_tuple
*tuple
,
144 enum nf_nat_manip_type maniptype
)
147 __be16
*portptr
, newport
, oldport
;
148 int hdrsize
= 8; /* TCP connection tracking guarantees this much */
150 /* this could be a inner header returned in icmp packet; in such
151 cases we cannot update the checksum field since it is outside of
152 the 8 bytes of transport layer headers we are guaranteed */
153 if (skb
->len
>= hdroff
+ sizeof(struct tcphdr
))
154 hdrsize
= sizeof(struct tcphdr
);
156 if (skb_ensure_writable(skb
, hdroff
+ hdrsize
))
159 hdr
= (struct tcphdr
*)(skb
->data
+ hdroff
);
161 if (maniptype
== NF_NAT_MANIP_SRC
) {
162 /* Get rid of src port */
163 newport
= tuple
->src
.u
.tcp
.port
;
164 portptr
= &hdr
->source
;
166 /* Get rid of dst port */
167 newport
= tuple
->dst
.u
.tcp
.port
;
168 portptr
= &hdr
->dest
;
174 if (hdrsize
< sizeof(*hdr
))
177 nf_csum_update(skb
, iphdroff
, &hdr
->check
, tuple
, maniptype
);
178 inet_proto_csum_replace2(&hdr
->check
, skb
, oldport
, newport
, false);
183 dccp_manip_pkt(struct sk_buff
*skb
,
184 unsigned int iphdroff
, unsigned int hdroff
,
185 const struct nf_conntrack_tuple
*tuple
,
186 enum nf_nat_manip_type maniptype
)
188 #ifdef CONFIG_NF_CT_PROTO_DCCP
189 struct dccp_hdr
*hdr
;
190 __be16
*portptr
, oldport
, newport
;
191 int hdrsize
= 8; /* DCCP connection tracking guarantees this much */
193 if (skb
->len
>= hdroff
+ sizeof(struct dccp_hdr
))
194 hdrsize
= sizeof(struct dccp_hdr
);
196 if (skb_ensure_writable(skb
, hdroff
+ hdrsize
))
199 hdr
= (struct dccp_hdr
*)(skb
->data
+ hdroff
);
201 if (maniptype
== NF_NAT_MANIP_SRC
) {
202 newport
= tuple
->src
.u
.dccp
.port
;
203 portptr
= &hdr
->dccph_sport
;
205 newport
= tuple
->dst
.u
.dccp
.port
;
206 portptr
= &hdr
->dccph_dport
;
212 if (hdrsize
< sizeof(*hdr
))
215 nf_csum_update(skb
, iphdroff
, &hdr
->dccph_checksum
, tuple
, maniptype
);
216 inet_proto_csum_replace2(&hdr
->dccph_checksum
, skb
, oldport
, newport
,
223 icmp_manip_pkt(struct sk_buff
*skb
,
224 unsigned int iphdroff
, unsigned int hdroff
,
225 const struct nf_conntrack_tuple
*tuple
,
226 enum nf_nat_manip_type maniptype
)
230 if (skb_ensure_writable(skb
, hdroff
+ sizeof(*hdr
)))
233 hdr
= (struct icmphdr
*)(skb
->data
+ hdroff
);
238 case ICMP_TIMESTAMPREPLY
:
239 case ICMP_INFO_REQUEST
:
240 case ICMP_INFO_REPLY
:
242 case ICMP_ADDRESSREPLY
:
247 inet_proto_csum_replace2(&hdr
->checksum
, skb
,
248 hdr
->un
.echo
.id
, tuple
->src
.u
.icmp
.id
, false);
249 hdr
->un
.echo
.id
= tuple
->src
.u
.icmp
.id
;
254 icmpv6_manip_pkt(struct sk_buff
*skb
,
255 unsigned int iphdroff
, unsigned int hdroff
,
256 const struct nf_conntrack_tuple
*tuple
,
257 enum nf_nat_manip_type maniptype
)
259 struct icmp6hdr
*hdr
;
261 if (skb_ensure_writable(skb
, hdroff
+ sizeof(*hdr
)))
264 hdr
= (struct icmp6hdr
*)(skb
->data
+ hdroff
);
265 nf_csum_update(skb
, iphdroff
, &hdr
->icmp6_cksum
, tuple
, maniptype
);
266 if (hdr
->icmp6_type
== ICMPV6_ECHO_REQUEST
||
267 hdr
->icmp6_type
== ICMPV6_ECHO_REPLY
) {
268 inet_proto_csum_replace2(&hdr
->icmp6_cksum
, skb
,
269 hdr
->icmp6_identifier
,
270 tuple
->src
.u
.icmp
.id
, false);
271 hdr
->icmp6_identifier
= tuple
->src
.u
.icmp
.id
;
276 /* manipulate a GRE packet according to maniptype */
278 gre_manip_pkt(struct sk_buff
*skb
,
279 unsigned int iphdroff
, unsigned int hdroff
,
280 const struct nf_conntrack_tuple
*tuple
,
281 enum nf_nat_manip_type maniptype
)
283 #if IS_ENABLED(CONFIG_NF_CT_PROTO_GRE)
284 const struct gre_base_hdr
*greh
;
285 struct pptp_gre_header
*pgreh
;
287 /* pgreh includes two optional 32bit fields which are not required
288 * to be there. That's where the magic '8' comes from */
289 if (skb_ensure_writable(skb
, hdroff
+ sizeof(*pgreh
) - 8))
292 greh
= (void *)skb
->data
+ hdroff
;
293 pgreh
= (struct pptp_gre_header
*)greh
;
295 /* we only have destination manip of a packet, since 'source key'
296 * is not present in the packet itself */
297 if (maniptype
!= NF_NAT_MANIP_DST
)
300 switch (greh
->flags
& GRE_VERSION
) {
302 /* We do not currently NAT any GREv0 packets.
303 * Try to behave like "nf_nat_proto_unknown" */
306 pr_debug("call_id -> 0x%04x\n", ntohs(tuple
->dst
.u
.gre
.key
));
307 pgreh
->call_id
= tuple
->dst
.u
.gre
.key
;
310 pr_debug("can't nat unknown GRE version\n");
317 static bool l4proto_manip_pkt(struct sk_buff
*skb
,
318 unsigned int iphdroff
, unsigned int hdroff
,
319 const struct nf_conntrack_tuple
*tuple
,
320 enum nf_nat_manip_type maniptype
)
322 switch (tuple
->dst
.protonum
) {
324 return tcp_manip_pkt(skb
, iphdroff
, hdroff
,
327 return udp_manip_pkt(skb
, iphdroff
, hdroff
,
329 case IPPROTO_UDPLITE
:
330 return udplite_manip_pkt(skb
, iphdroff
, hdroff
,
333 return sctp_manip_pkt(skb
, iphdroff
, hdroff
,
336 return icmp_manip_pkt(skb
, iphdroff
, hdroff
,
339 return icmpv6_manip_pkt(skb
, iphdroff
, hdroff
,
342 return dccp_manip_pkt(skb
, iphdroff
, hdroff
,
345 return gre_manip_pkt(skb
, iphdroff
, hdroff
,
349 /* If we don't know protocol -- no error, pass it unmodified. */
353 static bool nf_nat_ipv4_manip_pkt(struct sk_buff
*skb
,
354 unsigned int iphdroff
,
355 const struct nf_conntrack_tuple
*target
,
356 enum nf_nat_manip_type maniptype
)
361 if (skb_ensure_writable(skb
, iphdroff
+ sizeof(*iph
)))
364 iph
= (void *)skb
->data
+ iphdroff
;
365 hdroff
= iphdroff
+ iph
->ihl
* 4;
367 if (!l4proto_manip_pkt(skb
, iphdroff
, hdroff
, target
, maniptype
))
369 iph
= (void *)skb
->data
+ iphdroff
;
371 if (maniptype
== NF_NAT_MANIP_SRC
) {
372 csum_replace4(&iph
->check
, iph
->saddr
, target
->src
.u3
.ip
);
373 iph
->saddr
= target
->src
.u3
.ip
;
375 csum_replace4(&iph
->check
, iph
->daddr
, target
->dst
.u3
.ip
);
376 iph
->daddr
= target
->dst
.u3
.ip
;
381 static bool nf_nat_ipv6_manip_pkt(struct sk_buff
*skb
,
382 unsigned int iphdroff
,
383 const struct nf_conntrack_tuple
*target
,
384 enum nf_nat_manip_type maniptype
)
386 #if IS_ENABLED(CONFIG_IPV6)
387 struct ipv6hdr
*ipv6h
;
392 if (skb_ensure_writable(skb
, iphdroff
+ sizeof(*ipv6h
)))
395 ipv6h
= (void *)skb
->data
+ iphdroff
;
396 nexthdr
= ipv6h
->nexthdr
;
397 hdroff
= ipv6_skip_exthdr(skb
, iphdroff
+ sizeof(*ipv6h
),
398 &nexthdr
, &frag_off
);
402 if ((frag_off
& htons(~0x7)) == 0 &&
403 !l4proto_manip_pkt(skb
, iphdroff
, hdroff
, target
, maniptype
))
406 /* must reload, offset might have changed */
407 ipv6h
= (void *)skb
->data
+ iphdroff
;
410 if (maniptype
== NF_NAT_MANIP_SRC
)
411 ipv6h
->saddr
= target
->src
.u3
.in6
;
413 ipv6h
->daddr
= target
->dst
.u3
.in6
;
419 unsigned int nf_nat_manip_pkt(struct sk_buff
*skb
, struct nf_conn
*ct
,
420 enum nf_nat_manip_type mtype
,
421 enum ip_conntrack_dir dir
)
423 struct nf_conntrack_tuple target
;
425 /* We are aiming to look like inverse of other direction. */
426 nf_ct_invert_tuple(&target
, &ct
->tuplehash
[!dir
].tuple
);
428 switch (target
.src
.l3num
) {
430 if (nf_nat_ipv6_manip_pkt(skb
, 0, &target
, mtype
))
434 if (nf_nat_ipv4_manip_pkt(skb
, 0, &target
, mtype
))
445 static void nf_nat_ipv4_csum_update(struct sk_buff
*skb
,
446 unsigned int iphdroff
, __sum16
*check
,
447 const struct nf_conntrack_tuple
*t
,
448 enum nf_nat_manip_type maniptype
)
450 struct iphdr
*iph
= (struct iphdr
*)(skb
->data
+ iphdroff
);
453 if (maniptype
== NF_NAT_MANIP_SRC
) {
455 newip
= t
->src
.u3
.ip
;
458 newip
= t
->dst
.u3
.ip
;
460 inet_proto_csum_replace4(check
, skb
, oldip
, newip
, true);
463 static void nf_nat_ipv6_csum_update(struct sk_buff
*skb
,
464 unsigned int iphdroff
, __sum16
*check
,
465 const struct nf_conntrack_tuple
*t
,
466 enum nf_nat_manip_type maniptype
)
468 #if IS_ENABLED(CONFIG_IPV6)
469 const struct ipv6hdr
*ipv6h
= (struct ipv6hdr
*)(skb
->data
+ iphdroff
);
470 const struct in6_addr
*oldip
, *newip
;
472 if (maniptype
== NF_NAT_MANIP_SRC
) {
473 oldip
= &ipv6h
->saddr
;
474 newip
= &t
->src
.u3
.in6
;
476 oldip
= &ipv6h
->daddr
;
477 newip
= &t
->dst
.u3
.in6
;
479 inet_proto_csum_replace16(check
, skb
, oldip
->s6_addr32
,
480 newip
->s6_addr32
, true);
484 static void nf_csum_update(struct sk_buff
*skb
,
485 unsigned int iphdroff
, __sum16
*check
,
486 const struct nf_conntrack_tuple
*t
,
487 enum nf_nat_manip_type maniptype
)
489 switch (t
->src
.l3num
) {
491 nf_nat_ipv4_csum_update(skb
, iphdroff
, check
, t
, maniptype
);
494 nf_nat_ipv6_csum_update(skb
, iphdroff
, check
, t
, maniptype
);
499 static void nf_nat_ipv4_csum_recalc(struct sk_buff
*skb
,
500 u8 proto
, void *data
, __sum16
*check
,
501 int datalen
, int oldlen
)
503 if (skb
->ip_summed
!= CHECKSUM_PARTIAL
) {
504 const struct iphdr
*iph
= ip_hdr(skb
);
506 skb
->ip_summed
= CHECKSUM_PARTIAL
;
507 skb
->csum_start
= skb_headroom(skb
) + skb_network_offset(skb
) +
509 skb
->csum_offset
= (void *)check
- data
;
510 *check
= ~csum_tcpudp_magic(iph
->saddr
, iph
->daddr
, datalen
,
513 inet_proto_csum_replace2(check
, skb
,
514 htons(oldlen
), htons(datalen
), true);
518 #if IS_ENABLED(CONFIG_IPV6)
519 static void nf_nat_ipv6_csum_recalc(struct sk_buff
*skb
,
520 u8 proto
, void *data
, __sum16
*check
,
521 int datalen
, int oldlen
)
523 if (skb
->ip_summed
!= CHECKSUM_PARTIAL
) {
524 const struct ipv6hdr
*ipv6h
= ipv6_hdr(skb
);
526 skb
->ip_summed
= CHECKSUM_PARTIAL
;
527 skb
->csum_start
= skb_headroom(skb
) + skb_network_offset(skb
) +
528 (data
- (void *)skb
->data
);
529 skb
->csum_offset
= (void *)check
- data
;
530 *check
= ~csum_ipv6_magic(&ipv6h
->saddr
, &ipv6h
->daddr
,
533 inet_proto_csum_replace2(check
, skb
,
534 htons(oldlen
), htons(datalen
), true);
539 void nf_nat_csum_recalc(struct sk_buff
*skb
,
540 u8 nfproto
, u8 proto
, void *data
, __sum16
*check
,
541 int datalen
, int oldlen
)
545 nf_nat_ipv4_csum_recalc(skb
, proto
, data
, check
,
548 #if IS_ENABLED(CONFIG_IPV6)
550 nf_nat_ipv6_csum_recalc(skb
, proto
, data
, check
,
559 int nf_nat_icmp_reply_translation(struct sk_buff
*skb
,
561 enum ip_conntrack_info ctinfo
,
562 unsigned int hooknum
)
568 enum ip_conntrack_dir dir
= CTINFO2DIR(ctinfo
);
569 enum nf_nat_manip_type manip
= HOOK2MANIP(hooknum
);
570 unsigned int hdrlen
= ip_hdrlen(skb
);
571 struct nf_conntrack_tuple target
;
572 unsigned long statusbit
;
574 WARN_ON(ctinfo
!= IP_CT_RELATED
&& ctinfo
!= IP_CT_RELATED_REPLY
);
576 if (skb_ensure_writable(skb
, hdrlen
+ sizeof(*inside
)))
578 if (nf_ip_checksum(skb
, hooknum
, hdrlen
, IPPROTO_ICMP
))
581 inside
= (void *)skb
->data
+ hdrlen
;
582 if (inside
->icmp
.type
== ICMP_REDIRECT
) {
583 if ((ct
->status
& IPS_NAT_DONE_MASK
) != IPS_NAT_DONE_MASK
)
585 if (ct
->status
& IPS_NAT_MASK
)
589 if (manip
== NF_NAT_MANIP_SRC
)
590 statusbit
= IPS_SRC_NAT
;
592 statusbit
= IPS_DST_NAT
;
594 /* Invert if this is reply direction */
595 if (dir
== IP_CT_DIR_REPLY
)
596 statusbit
^= IPS_NAT_MASK
;
598 if (!(ct
->status
& statusbit
))
601 if (!nf_nat_ipv4_manip_pkt(skb
, hdrlen
+ sizeof(inside
->icmp
),
602 &ct
->tuplehash
[!dir
].tuple
, !manip
))
605 if (skb
->ip_summed
!= CHECKSUM_PARTIAL
) {
606 /* Reloading "inside" here since manip_pkt may reallocate */
607 inside
= (void *)skb
->data
+ hdrlen
;
608 inside
->icmp
.checksum
= 0;
609 inside
->icmp
.checksum
=
610 csum_fold(skb_checksum(skb
, hdrlen
,
611 skb
->len
- hdrlen
, 0));
614 /* Change outer to look like the reply to an incoming packet */
615 nf_ct_invert_tuple(&target
, &ct
->tuplehash
[!dir
].tuple
);
616 target
.dst
.protonum
= IPPROTO_ICMP
;
617 if (!nf_nat_ipv4_manip_pkt(skb
, 0, &target
, manip
))
622 EXPORT_SYMBOL_GPL(nf_nat_icmp_reply_translation
);
625 nf_nat_ipv4_fn(void *priv
, struct sk_buff
*skb
,
626 const struct nf_hook_state
*state
)
629 enum ip_conntrack_info ctinfo
;
631 ct
= nf_ct_get(skb
, &ctinfo
);
635 if (ctinfo
== IP_CT_RELATED
|| ctinfo
== IP_CT_RELATED_REPLY
) {
636 if (ip_hdr(skb
)->protocol
== IPPROTO_ICMP
) {
637 if (!nf_nat_icmp_reply_translation(skb
, ct
, ctinfo
,
645 return nf_nat_inet_fn(priv
, skb
, state
);
649 nf_nat_ipv4_in(void *priv
, struct sk_buff
*skb
,
650 const struct nf_hook_state
*state
)
653 __be32 daddr
= ip_hdr(skb
)->daddr
;
655 ret
= nf_nat_ipv4_fn(priv
, skb
, state
);
656 if (ret
== NF_ACCEPT
&& daddr
!= ip_hdr(skb
)->daddr
)
663 nf_nat_ipv4_out(void *priv
, struct sk_buff
*skb
,
664 const struct nf_hook_state
*state
)
667 const struct nf_conn
*ct
;
668 enum ip_conntrack_info ctinfo
;
673 ret
= nf_nat_ipv4_fn(priv
, skb
, state
);
675 if (ret
!= NF_ACCEPT
)
678 if (IPCB(skb
)->flags
& IPSKB_XFRM_TRANSFORMED
)
681 ct
= nf_ct_get(skb
, &ctinfo
);
683 enum ip_conntrack_dir dir
= CTINFO2DIR(ctinfo
);
685 if (ct
->tuplehash
[dir
].tuple
.src
.u3
.ip
!=
686 ct
->tuplehash
[!dir
].tuple
.dst
.u3
.ip
||
687 (ct
->tuplehash
[dir
].tuple
.dst
.protonum
!= IPPROTO_ICMP
&&
688 ct
->tuplehash
[dir
].tuple
.src
.u
.all
!=
689 ct
->tuplehash
[!dir
].tuple
.dst
.u
.all
)) {
690 err
= nf_xfrm_me_harder(state
->net
, skb
, AF_INET
);
692 ret
= NF_DROP_ERR(err
);
700 nf_nat_ipv4_local_fn(void *priv
, struct sk_buff
*skb
,
701 const struct nf_hook_state
*state
)
703 const struct nf_conn
*ct
;
704 enum ip_conntrack_info ctinfo
;
708 ret
= nf_nat_ipv4_fn(priv
, skb
, state
);
709 if (ret
!= NF_ACCEPT
)
712 ct
= nf_ct_get(skb
, &ctinfo
);
714 enum ip_conntrack_dir dir
= CTINFO2DIR(ctinfo
);
716 if (ct
->tuplehash
[dir
].tuple
.dst
.u3
.ip
!=
717 ct
->tuplehash
[!dir
].tuple
.src
.u3
.ip
) {
718 err
= ip_route_me_harder(state
->net
, skb
, RTN_UNSPEC
);
720 ret
= NF_DROP_ERR(err
);
723 else if (!(IPCB(skb
)->flags
& IPSKB_XFRM_TRANSFORMED
) &&
724 ct
->tuplehash
[dir
].tuple
.dst
.protonum
!= IPPROTO_ICMP
&&
725 ct
->tuplehash
[dir
].tuple
.dst
.u
.all
!=
726 ct
->tuplehash
[!dir
].tuple
.src
.u
.all
) {
727 err
= nf_xfrm_me_harder(state
->net
, skb
, AF_INET
);
729 ret
= NF_DROP_ERR(err
);
736 static const struct nf_hook_ops nf_nat_ipv4_ops
[] = {
737 /* Before packet filtering, change destination */
739 .hook
= nf_nat_ipv4_in
,
741 .hooknum
= NF_INET_PRE_ROUTING
,
742 .priority
= NF_IP_PRI_NAT_DST
,
744 /* After packet filtering, change source */
746 .hook
= nf_nat_ipv4_out
,
748 .hooknum
= NF_INET_POST_ROUTING
,
749 .priority
= NF_IP_PRI_NAT_SRC
,
751 /* Before packet filtering, change destination */
753 .hook
= nf_nat_ipv4_local_fn
,
755 .hooknum
= NF_INET_LOCAL_OUT
,
756 .priority
= NF_IP_PRI_NAT_DST
,
758 /* After packet filtering, change source */
760 .hook
= nf_nat_ipv4_fn
,
762 .hooknum
= NF_INET_LOCAL_IN
,
763 .priority
= NF_IP_PRI_NAT_SRC
,
767 int nf_nat_ipv4_register_fn(struct net
*net
, const struct nf_hook_ops
*ops
)
769 return nf_nat_register_fn(net
, ops
->pf
, ops
, nf_nat_ipv4_ops
,
770 ARRAY_SIZE(nf_nat_ipv4_ops
));
772 EXPORT_SYMBOL_GPL(nf_nat_ipv4_register_fn
);
774 void nf_nat_ipv4_unregister_fn(struct net
*net
, const struct nf_hook_ops
*ops
)
776 nf_nat_unregister_fn(net
, ops
->pf
, ops
, ARRAY_SIZE(nf_nat_ipv4_ops
));
778 EXPORT_SYMBOL_GPL(nf_nat_ipv4_unregister_fn
);
780 #if IS_ENABLED(CONFIG_IPV6)
781 int nf_nat_icmpv6_reply_translation(struct sk_buff
*skb
,
783 enum ip_conntrack_info ctinfo
,
784 unsigned int hooknum
,
788 struct icmp6hdr icmp6
;
791 enum ip_conntrack_dir dir
= CTINFO2DIR(ctinfo
);
792 enum nf_nat_manip_type manip
= HOOK2MANIP(hooknum
);
793 struct nf_conntrack_tuple target
;
794 unsigned long statusbit
;
796 WARN_ON(ctinfo
!= IP_CT_RELATED
&& ctinfo
!= IP_CT_RELATED_REPLY
);
798 if (skb_ensure_writable(skb
, hdrlen
+ sizeof(*inside
)))
800 if (nf_ip6_checksum(skb
, hooknum
, hdrlen
, IPPROTO_ICMPV6
))
803 inside
= (void *)skb
->data
+ hdrlen
;
804 if (inside
->icmp6
.icmp6_type
== NDISC_REDIRECT
) {
805 if ((ct
->status
& IPS_NAT_DONE_MASK
) != IPS_NAT_DONE_MASK
)
807 if (ct
->status
& IPS_NAT_MASK
)
811 if (manip
== NF_NAT_MANIP_SRC
)
812 statusbit
= IPS_SRC_NAT
;
814 statusbit
= IPS_DST_NAT
;
816 /* Invert if this is reply direction */
817 if (dir
== IP_CT_DIR_REPLY
)
818 statusbit
^= IPS_NAT_MASK
;
820 if (!(ct
->status
& statusbit
))
823 if (!nf_nat_ipv6_manip_pkt(skb
, hdrlen
+ sizeof(inside
->icmp6
),
824 &ct
->tuplehash
[!dir
].tuple
, !manip
))
827 if (skb
->ip_summed
!= CHECKSUM_PARTIAL
) {
828 struct ipv6hdr
*ipv6h
= ipv6_hdr(skb
);
830 inside
= (void *)skb
->data
+ hdrlen
;
831 inside
->icmp6
.icmp6_cksum
= 0;
832 inside
->icmp6
.icmp6_cksum
=
833 csum_ipv6_magic(&ipv6h
->saddr
, &ipv6h
->daddr
,
834 skb
->len
- hdrlen
, IPPROTO_ICMPV6
,
835 skb_checksum(skb
, hdrlen
,
836 skb
->len
- hdrlen
, 0));
839 nf_ct_invert_tuple(&target
, &ct
->tuplehash
[!dir
].tuple
);
840 target
.dst
.protonum
= IPPROTO_ICMPV6
;
841 if (!nf_nat_ipv6_manip_pkt(skb
, 0, &target
, manip
))
846 EXPORT_SYMBOL_GPL(nf_nat_icmpv6_reply_translation
);
849 nf_nat_ipv6_fn(void *priv
, struct sk_buff
*skb
,
850 const struct nf_hook_state
*state
)
853 enum ip_conntrack_info ctinfo
;
858 ct
= nf_ct_get(skb
, &ctinfo
);
859 /* Can't track? It's not due to stress, or conntrack would
860 * have dropped it. Hence it's the user's responsibilty to
861 * packet filter it out, or implement conntrack/NAT for that
867 if (ctinfo
== IP_CT_RELATED
|| ctinfo
== IP_CT_RELATED_REPLY
) {
868 nexthdr
= ipv6_hdr(skb
)->nexthdr
;
869 hdrlen
= ipv6_skip_exthdr(skb
, sizeof(struct ipv6hdr
),
870 &nexthdr
, &frag_off
);
872 if (hdrlen
>= 0 && nexthdr
== IPPROTO_ICMPV6
) {
873 if (!nf_nat_icmpv6_reply_translation(skb
, ct
, ctinfo
,
882 return nf_nat_inet_fn(priv
, skb
, state
);
886 nf_nat_ipv6_in(void *priv
, struct sk_buff
*skb
,
887 const struct nf_hook_state
*state
)
890 struct in6_addr daddr
= ipv6_hdr(skb
)->daddr
;
892 ret
= nf_nat_ipv6_fn(priv
, skb
, state
);
893 if (ret
!= NF_DROP
&& ret
!= NF_STOLEN
&&
894 ipv6_addr_cmp(&daddr
, &ipv6_hdr(skb
)->daddr
))
901 nf_nat_ipv6_out(void *priv
, struct sk_buff
*skb
,
902 const struct nf_hook_state
*state
)
905 const struct nf_conn
*ct
;
906 enum ip_conntrack_info ctinfo
;
911 ret
= nf_nat_ipv6_fn(priv
, skb
, state
);
913 if (ret
!= NF_ACCEPT
)
916 if (IP6CB(skb
)->flags
& IP6SKB_XFRM_TRANSFORMED
)
918 ct
= nf_ct_get(skb
, &ctinfo
);
920 enum ip_conntrack_dir dir
= CTINFO2DIR(ctinfo
);
922 if (!nf_inet_addr_cmp(&ct
->tuplehash
[dir
].tuple
.src
.u3
,
923 &ct
->tuplehash
[!dir
].tuple
.dst
.u3
) ||
924 (ct
->tuplehash
[dir
].tuple
.dst
.protonum
!= IPPROTO_ICMPV6
&&
925 ct
->tuplehash
[dir
].tuple
.src
.u
.all
!=
926 ct
->tuplehash
[!dir
].tuple
.dst
.u
.all
)) {
927 err
= nf_xfrm_me_harder(state
->net
, skb
, AF_INET6
);
929 ret
= NF_DROP_ERR(err
);
938 nf_nat_ipv6_local_fn(void *priv
, struct sk_buff
*skb
,
939 const struct nf_hook_state
*state
)
941 const struct nf_conn
*ct
;
942 enum ip_conntrack_info ctinfo
;
946 ret
= nf_nat_ipv6_fn(priv
, skb
, state
);
947 if (ret
!= NF_ACCEPT
)
950 ct
= nf_ct_get(skb
, &ctinfo
);
952 enum ip_conntrack_dir dir
= CTINFO2DIR(ctinfo
);
954 if (!nf_inet_addr_cmp(&ct
->tuplehash
[dir
].tuple
.dst
.u3
,
955 &ct
->tuplehash
[!dir
].tuple
.src
.u3
)) {
956 err
= nf_ip6_route_me_harder(state
->net
, skb
);
958 ret
= NF_DROP_ERR(err
);
961 else if (!(IP6CB(skb
)->flags
& IP6SKB_XFRM_TRANSFORMED
) &&
962 ct
->tuplehash
[dir
].tuple
.dst
.protonum
!= IPPROTO_ICMPV6
&&
963 ct
->tuplehash
[dir
].tuple
.dst
.u
.all
!=
964 ct
->tuplehash
[!dir
].tuple
.src
.u
.all
) {
965 err
= nf_xfrm_me_harder(state
->net
, skb
, AF_INET6
);
967 ret
= NF_DROP_ERR(err
);
975 static const struct nf_hook_ops nf_nat_ipv6_ops
[] = {
976 /* Before packet filtering, change destination */
978 .hook
= nf_nat_ipv6_in
,
980 .hooknum
= NF_INET_PRE_ROUTING
,
981 .priority
= NF_IP6_PRI_NAT_DST
,
983 /* After packet filtering, change source */
985 .hook
= nf_nat_ipv6_out
,
987 .hooknum
= NF_INET_POST_ROUTING
,
988 .priority
= NF_IP6_PRI_NAT_SRC
,
990 /* Before packet filtering, change destination */
992 .hook
= nf_nat_ipv6_local_fn
,
994 .hooknum
= NF_INET_LOCAL_OUT
,
995 .priority
= NF_IP6_PRI_NAT_DST
,
997 /* After packet filtering, change source */
999 .hook
= nf_nat_ipv6_fn
,
1001 .hooknum
= NF_INET_LOCAL_IN
,
1002 .priority
= NF_IP6_PRI_NAT_SRC
,
1006 int nf_nat_ipv6_register_fn(struct net
*net
, const struct nf_hook_ops
*ops
)
1008 return nf_nat_register_fn(net
, ops
->pf
, ops
, nf_nat_ipv6_ops
,
1009 ARRAY_SIZE(nf_nat_ipv6_ops
));
1011 EXPORT_SYMBOL_GPL(nf_nat_ipv6_register_fn
);
1013 void nf_nat_ipv6_unregister_fn(struct net
*net
, const struct nf_hook_ops
*ops
)
1015 nf_nat_unregister_fn(net
, ops
->pf
, ops
, ARRAY_SIZE(nf_nat_ipv6_ops
));
1017 EXPORT_SYMBOL_GPL(nf_nat_ipv6_unregister_fn
);
1018 #endif /* CONFIG_IPV6 */
1020 #if defined(CONFIG_NF_TABLES_INET) && IS_ENABLED(CONFIG_NFT_NAT)
1021 int nf_nat_inet_register_fn(struct net
*net
, const struct nf_hook_ops
*ops
)
1025 if (WARN_ON_ONCE(ops
->pf
!= NFPROTO_INET
))
1028 ret
= nf_nat_register_fn(net
, NFPROTO_IPV6
, ops
, nf_nat_ipv6_ops
,
1029 ARRAY_SIZE(nf_nat_ipv6_ops
));
1033 ret
= nf_nat_register_fn(net
, NFPROTO_IPV4
, ops
, nf_nat_ipv4_ops
,
1034 ARRAY_SIZE(nf_nat_ipv4_ops
));
1036 nf_nat_unregister_fn(net
, NFPROTO_IPV6
, ops
,
1037 ARRAY_SIZE(nf_nat_ipv6_ops
));
1040 EXPORT_SYMBOL_GPL(nf_nat_inet_register_fn
);
1042 void nf_nat_inet_unregister_fn(struct net
*net
, const struct nf_hook_ops
*ops
)
1044 nf_nat_unregister_fn(net
, NFPROTO_IPV4
, ops
, ARRAY_SIZE(nf_nat_ipv4_ops
));
1045 nf_nat_unregister_fn(net
, NFPROTO_IPV6
, ops
, ARRAY_SIZE(nf_nat_ipv6_ops
));
1047 EXPORT_SYMBOL_GPL(nf_nat_inet_unregister_fn
);
1048 #endif /* NFT INET NAT */