linux-brain/net/netfilter/nf_nat_proto.c

1066 lines
27 KiB
C
Raw Permalink Normal View History

// SPDX-License-Identifier: GPL-2.0-only
/* (C) 1999-2001 Paul `Rusty' Russell
* (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
*/
#include <linux/types.h>
#include <linux/export.h>
#include <linux/init.h>
#include <linux/udp.h>
#include <linux/tcp.h>
#include <linux/icmp.h>
#include <linux/icmpv6.h>
#include <linux/dccp.h>
#include <linux/sctp.h>
#include <net/sctp/checksum.h>
#include <linux/netfilter.h>
#include <net/netfilter/nf_nat.h>
netfilter: nat: merge nf_nat_ipv4,6 into nat core before: text data bss dec hex filename 16566 1576 4136 22278 5706 nf_nat.ko 3598 844 0 4442 115a nf_nat_ipv6.ko 3187 844 0 4031 fbf nf_nat_ipv4.ko after: text data bss dec hex filename 22948 1612 4136 28696 7018 nf_nat.ko ... with ipv4/v6 nat now provided directly via nf_nat.ko. Also changes: ret = nf_nat_ipv4_fn(priv, skb, state); if (ret != NF_DROP && ret != NF_STOLEN && into if (ret != NF_ACCEPT) return ret; everywhere. The nat hooks never should return anything other than ACCEPT or DROP (and the latter only in rare error cases). The original code uses multi-line ANDing including assignment-in-if: if (ret != NF_DROP && ret != NF_STOLEN && !(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) && (ct = nf_ct_get(skb, &ctinfo)) != NULL) { I removed this while moving, breaking those in separate conditionals and moving the assignments into extra lines. checkpatch still generates some warnings: 1. Overly long lines (of moved code). Breaking them is even more ugly. so I kept this as-is. 2. use of extern function declarations in a .c file. This is necessary evil, we must call nf_nat_l3proto_register() from the nat core now. All l3proto related functions are removed later in this series, those prototypes are then removed as well. v2: keep empty nf_nat_ipv6_csum_update stub for CONFIG_IPV6=n case. v3: remove IS_ENABLED(NF_NAT_IPV4/6) tests, NF_NAT_IPVx toggles are removed here. v4: also get rid of the assignments in conditionals. Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
2019-02-20 01:38:21 +09:00
#include <linux/ipv6.h>
#include <linux/netfilter_ipv6.h>
#include <net/checksum.h>
#include <net/ip6_checksum.h>
#include <net/ip6_route.h>
#include <net/xfrm.h>
#include <net/ipv6.h>
#include <net/netfilter/nf_conntrack_core.h>
#include <net/netfilter/nf_conntrack.h>
#include <linux/netfilter/nfnetlink_conntrack.h>
netfilter: nat: merge nf_nat_ipv4,6 into nat core before: text data bss dec hex filename 16566 1576 4136 22278 5706 nf_nat.ko 3598 844 0 4442 115a nf_nat_ipv6.ko 3187 844 0 4031 fbf nf_nat_ipv4.ko after: text data bss dec hex filename 22948 1612 4136 28696 7018 nf_nat.ko ... with ipv4/v6 nat now provided directly via nf_nat.ko. Also changes: ret = nf_nat_ipv4_fn(priv, skb, state); if (ret != NF_DROP && ret != NF_STOLEN && into if (ret != NF_ACCEPT) return ret; everywhere. The nat hooks never should return anything other than ACCEPT or DROP (and the latter only in rare error cases). The original code uses multi-line ANDing including assignment-in-if: if (ret != NF_DROP && ret != NF_STOLEN && !(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) && (ct = nf_ct_get(skb, &ctinfo)) != NULL) { I removed this while moving, breaking those in separate conditionals and moving the assignments into extra lines. checkpatch still generates some warnings: 1. Overly long lines (of moved code). Breaking them is even more ugly. so I kept this as-is. 2. use of extern function declarations in a .c file. This is necessary evil, we must call nf_nat_l3proto_register() from the nat core now. All l3proto related functions are removed later in this series, those prototypes are then removed as well. v2: keep empty nf_nat_ipv6_csum_update stub for CONFIG_IPV6=n case. v3: remove IS_ENABLED(NF_NAT_IPV4/6) tests, NF_NAT_IPVx toggles are removed here. v4: also get rid of the assignments in conditionals. Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
2019-02-20 01:38:21 +09:00
static void nf_csum_update(struct sk_buff *skb,
unsigned int iphdroff, __sum16 *check,
const struct nf_conntrack_tuple *t,
enum nf_nat_manip_type maniptype);
static void
__udp_manip_pkt(struct sk_buff *skb,
unsigned int iphdroff, struct udphdr *hdr,
const struct nf_conntrack_tuple *tuple,
enum nf_nat_manip_type maniptype, bool do_csum)
{
__be16 *portptr, newport;
if (maniptype == NF_NAT_MANIP_SRC) {
/* Get rid of src port */
newport = tuple->src.u.udp.port;
portptr = &hdr->source;
} else {
/* Get rid of dst port */
newport = tuple->dst.u.udp.port;
portptr = &hdr->dest;
}
if (do_csum) {
nf_csum_update(skb, iphdroff, &hdr->check, tuple, maniptype);
inet_proto_csum_replace2(&hdr->check, skb, *portptr, newport,
false);
if (!hdr->check)
hdr->check = CSUM_MANGLED_0;
}
*portptr = newport;
}
static bool udp_manip_pkt(struct sk_buff *skb,
unsigned int iphdroff, unsigned int hdroff,
const struct nf_conntrack_tuple *tuple,
enum nf_nat_manip_type maniptype)
{
struct udphdr *hdr;
if (skb_ensure_writable(skb, hdroff + sizeof(*hdr)))
return false;
hdr = (struct udphdr *)(skb->data + hdroff);
netfilter: nat: never update the UDP checksum when it's 0 commit ea64d8d6c675c0bb712689b13810301de9d8f77a upstream. If the UDP header of a local VXLAN endpoint is NAT-ed, and the VXLAN device has disabled UDP checksums and enabled Tx checksum offloading, then the skb passed to udp_manip_pkt() has hdr->check == 0 (outer checksum disabled) and skb->ip_summed == CHECKSUM_PARTIAL (inner packet checksum offloaded). Because of the ->ip_summed value, udp_manip_pkt() tries to update the outer checksum with the new address and port, leading to an invalid checksum sent on the wire, as the original null checksum obviously didn't take the old address and port into account. So, we can't take ->ip_summed into account in udp_manip_pkt(), as it might not refer to the checksum we're acting on. Instead, we can base the decision to update the UDP checksum entirely on the value of hdr->check, because it's null if and only if checksum is disabled: * A fully computed checksum can't be 0, since a 0 checksum is represented by the CSUM_MANGLED_0 value instead. * A partial checksum can't be 0, since the pseudo-header always adds at least one non-zero value (the UDP protocol type 0x11) and adding more values to the sum can't make it wrap to 0 as the carry is then added to the wrapped number. * A disabled checksum uses the special value 0. The problem seems to be there from day one, although it was probably not visible before UDP tunnels were implemented. Fixes: 5b1158e909ec ("[NETFILTER]: Add NAT support for nf_conntrack") Signed-off-by: Guillaume Nault <gnault@redhat.com> Reviewed-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2020-04-21 09:42:19 +09:00
__udp_manip_pkt(skb, iphdroff, hdr, tuple, maniptype, !!hdr->check);
return true;
}
static bool udplite_manip_pkt(struct sk_buff *skb,
unsigned int iphdroff, unsigned int hdroff,
const struct nf_conntrack_tuple *tuple,
enum nf_nat_manip_type maniptype)
{
#ifdef CONFIG_NF_CT_PROTO_UDPLITE
struct udphdr *hdr;
if (skb_ensure_writable(skb, hdroff + sizeof(*hdr)))
return false;
hdr = (struct udphdr *)(skb->data + hdroff);
__udp_manip_pkt(skb, iphdroff, hdr, tuple, maniptype, true);
#endif
return true;
}
static bool
sctp_manip_pkt(struct sk_buff *skb,
unsigned int iphdroff, unsigned int hdroff,
const struct nf_conntrack_tuple *tuple,
enum nf_nat_manip_type maniptype)
{
#ifdef CONFIG_NF_CT_PROTO_SCTP
struct sctphdr *hdr;
int hdrsize = 8;
/* This could be an inner header returned in imcp packet; in such
* cases we cannot update the checksum field since it is outside
* of the 8 bytes of transport layer headers we are guaranteed.
*/
if (skb->len >= hdroff + sizeof(*hdr))
hdrsize = sizeof(*hdr);
if (skb_ensure_writable(skb, hdroff + hdrsize))
return false;
hdr = (struct sctphdr *)(skb->data + hdroff);
if (maniptype == NF_NAT_MANIP_SRC) {
/* Get rid of src port */
hdr->source = tuple->src.u.sctp.port;
} else {
/* Get rid of dst port */
hdr->dest = tuple->dst.u.sctp.port;
}
if (hdrsize < sizeof(*hdr))
return true;
if (skb->ip_summed != CHECKSUM_PARTIAL) {
hdr->checksum = sctp_compute_cksum(skb, hdroff);
skb->ip_summed = CHECKSUM_NONE;
}
#endif
return true;
}
static bool
tcp_manip_pkt(struct sk_buff *skb,
unsigned int iphdroff, unsigned int hdroff,
const struct nf_conntrack_tuple *tuple,
enum nf_nat_manip_type maniptype)
{
struct tcphdr *hdr;
__be16 *portptr, newport, oldport;
int hdrsize = 8; /* TCP connection tracking guarantees this much */
/* this could be a inner header returned in icmp packet; in such
cases we cannot update the checksum field since it is outside of
the 8 bytes of transport layer headers we are guaranteed */
if (skb->len >= hdroff + sizeof(struct tcphdr))
hdrsize = sizeof(struct tcphdr);
if (skb_ensure_writable(skb, hdroff + hdrsize))
return false;
hdr = (struct tcphdr *)(skb->data + hdroff);
if (maniptype == NF_NAT_MANIP_SRC) {
/* Get rid of src port */
newport = tuple->src.u.tcp.port;
portptr = &hdr->source;
} else {
/* Get rid of dst port */
newport = tuple->dst.u.tcp.port;
portptr = &hdr->dest;
}
oldport = *portptr;
*portptr = newport;
if (hdrsize < sizeof(*hdr))
return true;
nf_csum_update(skb, iphdroff, &hdr->check, tuple, maniptype);
inet_proto_csum_replace2(&hdr->check, skb, oldport, newport, false);
return true;
}
static bool
dccp_manip_pkt(struct sk_buff *skb,
unsigned int iphdroff, unsigned int hdroff,
const struct nf_conntrack_tuple *tuple,
enum nf_nat_manip_type maniptype)
{
#ifdef CONFIG_NF_CT_PROTO_DCCP
struct dccp_hdr *hdr;
__be16 *portptr, oldport, newport;
int hdrsize = 8; /* DCCP connection tracking guarantees this much */
if (skb->len >= hdroff + sizeof(struct dccp_hdr))
hdrsize = sizeof(struct dccp_hdr);
if (skb_ensure_writable(skb, hdroff + hdrsize))
return false;
hdr = (struct dccp_hdr *)(skb->data + hdroff);
if (maniptype == NF_NAT_MANIP_SRC) {
newport = tuple->src.u.dccp.port;
portptr = &hdr->dccph_sport;
} else {
newport = tuple->dst.u.dccp.port;
portptr = &hdr->dccph_dport;
}
oldport = *portptr;
*portptr = newport;
if (hdrsize < sizeof(*hdr))
return true;
nf_csum_update(skb, iphdroff, &hdr->dccph_checksum, tuple, maniptype);
inet_proto_csum_replace2(&hdr->dccph_checksum, skb, oldport, newport,
false);
#endif
return true;
}
static bool
icmp_manip_pkt(struct sk_buff *skb,
unsigned int iphdroff, unsigned int hdroff,
const struct nf_conntrack_tuple *tuple,
enum nf_nat_manip_type maniptype)
{
struct icmphdr *hdr;
if (skb_ensure_writable(skb, hdroff + sizeof(*hdr)))
return false;
hdr = (struct icmphdr *)(skb->data + hdroff);
switch (hdr->type) {
case ICMP_ECHO:
case ICMP_ECHOREPLY:
case ICMP_TIMESTAMP:
case ICMP_TIMESTAMPREPLY:
case ICMP_INFO_REQUEST:
case ICMP_INFO_REPLY:
case ICMP_ADDRESS:
case ICMP_ADDRESSREPLY:
break;
default:
return true;
}
inet_proto_csum_replace2(&hdr->checksum, skb,
hdr->un.echo.id, tuple->src.u.icmp.id, false);
hdr->un.echo.id = tuple->src.u.icmp.id;
return true;
}
static bool
icmpv6_manip_pkt(struct sk_buff *skb,
unsigned int iphdroff, unsigned int hdroff,
const struct nf_conntrack_tuple *tuple,
enum nf_nat_manip_type maniptype)
{
struct icmp6hdr *hdr;
if (skb_ensure_writable(skb, hdroff + sizeof(*hdr)))
return false;
hdr = (struct icmp6hdr *)(skb->data + hdroff);
nf_csum_update(skb, iphdroff, &hdr->icmp6_cksum, tuple, maniptype);
if (hdr->icmp6_type == ICMPV6_ECHO_REQUEST ||
hdr->icmp6_type == ICMPV6_ECHO_REPLY) {
inet_proto_csum_replace2(&hdr->icmp6_cksum, skb,
hdr->icmp6_identifier,
tuple->src.u.icmp.id, false);
hdr->icmp6_identifier = tuple->src.u.icmp.id;
}
return true;
}
/* manipulate a GRE packet according to maniptype */
static bool
gre_manip_pkt(struct sk_buff *skb,
unsigned int iphdroff, unsigned int hdroff,
const struct nf_conntrack_tuple *tuple,
enum nf_nat_manip_type maniptype)
{
#if IS_ENABLED(CONFIG_NF_CT_PROTO_GRE)
const struct gre_base_hdr *greh;
struct pptp_gre_header *pgreh;
/* pgreh includes two optional 32bit fields which are not required
* to be there. That's where the magic '8' comes from */
if (skb_ensure_writable(skb, hdroff + sizeof(*pgreh) - 8))
return false;
greh = (void *)skb->data + hdroff;
pgreh = (struct pptp_gre_header *)greh;
/* we only have destination manip of a packet, since 'source key'
* is not present in the packet itself */
if (maniptype != NF_NAT_MANIP_DST)
return true;
switch (greh->flags & GRE_VERSION) {
case GRE_VERSION_0:
/* We do not currently NAT any GREv0 packets.
* Try to behave like "nf_nat_proto_unknown" */
break;
case GRE_VERSION_1:
pr_debug("call_id -> 0x%04x\n", ntohs(tuple->dst.u.gre.key));
pgreh->call_id = tuple->dst.u.gre.key;
break;
default:
pr_debug("can't nat unknown GRE version\n");
return false;
}
#endif
return true;
}
static bool l4proto_manip_pkt(struct sk_buff *skb,
unsigned int iphdroff, unsigned int hdroff,
const struct nf_conntrack_tuple *tuple,
enum nf_nat_manip_type maniptype)
{
switch (tuple->dst.protonum) {
case IPPROTO_TCP:
return tcp_manip_pkt(skb, iphdroff, hdroff,
tuple, maniptype);
case IPPROTO_UDP:
return udp_manip_pkt(skb, iphdroff, hdroff,
tuple, maniptype);
case IPPROTO_UDPLITE:
return udplite_manip_pkt(skb, iphdroff, hdroff,
tuple, maniptype);
case IPPROTO_SCTP:
return sctp_manip_pkt(skb, iphdroff, hdroff,
tuple, maniptype);
case IPPROTO_ICMP:
return icmp_manip_pkt(skb, iphdroff, hdroff,
tuple, maniptype);
case IPPROTO_ICMPV6:
return icmpv6_manip_pkt(skb, iphdroff, hdroff,
tuple, maniptype);
case IPPROTO_DCCP:
return dccp_manip_pkt(skb, iphdroff, hdroff,
tuple, maniptype);
case IPPROTO_GRE:
return gre_manip_pkt(skb, iphdroff, hdroff,
tuple, maniptype);
}
/* If we don't know protocol -- no error, pass it unmodified. */
return true;
}
netfilter: nat: merge nf_nat_ipv4,6 into nat core before: text data bss dec hex filename 16566 1576 4136 22278 5706 nf_nat.ko 3598 844 0 4442 115a nf_nat_ipv6.ko 3187 844 0 4031 fbf nf_nat_ipv4.ko after: text data bss dec hex filename 22948 1612 4136 28696 7018 nf_nat.ko ... with ipv4/v6 nat now provided directly via nf_nat.ko. Also changes: ret = nf_nat_ipv4_fn(priv, skb, state); if (ret != NF_DROP && ret != NF_STOLEN && into if (ret != NF_ACCEPT) return ret; everywhere. The nat hooks never should return anything other than ACCEPT or DROP (and the latter only in rare error cases). The original code uses multi-line ANDing including assignment-in-if: if (ret != NF_DROP && ret != NF_STOLEN && !(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) && (ct = nf_ct_get(skb, &ctinfo)) != NULL) { I removed this while moving, breaking those in separate conditionals and moving the assignments into extra lines. checkpatch still generates some warnings: 1. Overly long lines (of moved code). Breaking them is even more ugly. so I kept this as-is. 2. use of extern function declarations in a .c file. This is necessary evil, we must call nf_nat_l3proto_register() from the nat core now. All l3proto related functions are removed later in this series, those prototypes are then removed as well. v2: keep empty nf_nat_ipv6_csum_update stub for CONFIG_IPV6=n case. v3: remove IS_ENABLED(NF_NAT_IPV4/6) tests, NF_NAT_IPVx toggles are removed here. v4: also get rid of the assignments in conditionals. Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
2019-02-20 01:38:21 +09:00
static bool nf_nat_ipv4_manip_pkt(struct sk_buff *skb,
unsigned int iphdroff,
const struct nf_conntrack_tuple *target,
enum nf_nat_manip_type maniptype)
{
struct iphdr *iph;
unsigned int hdroff;
if (skb_ensure_writable(skb, iphdroff + sizeof(*iph)))
netfilter: nat: merge nf_nat_ipv4,6 into nat core before: text data bss dec hex filename 16566 1576 4136 22278 5706 nf_nat.ko 3598 844 0 4442 115a nf_nat_ipv6.ko 3187 844 0 4031 fbf nf_nat_ipv4.ko after: text data bss dec hex filename 22948 1612 4136 28696 7018 nf_nat.ko ... with ipv4/v6 nat now provided directly via nf_nat.ko. Also changes: ret = nf_nat_ipv4_fn(priv, skb, state); if (ret != NF_DROP && ret != NF_STOLEN && into if (ret != NF_ACCEPT) return ret; everywhere. The nat hooks never should return anything other than ACCEPT or DROP (and the latter only in rare error cases). The original code uses multi-line ANDing including assignment-in-if: if (ret != NF_DROP && ret != NF_STOLEN && !(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) && (ct = nf_ct_get(skb, &ctinfo)) != NULL) { I removed this while moving, breaking those in separate conditionals and moving the assignments into extra lines. checkpatch still generates some warnings: 1. Overly long lines (of moved code). Breaking them is even more ugly. so I kept this as-is. 2. use of extern function declarations in a .c file. This is necessary evil, we must call nf_nat_l3proto_register() from the nat core now. All l3proto related functions are removed later in this series, those prototypes are then removed as well. v2: keep empty nf_nat_ipv6_csum_update stub for CONFIG_IPV6=n case. v3: remove IS_ENABLED(NF_NAT_IPV4/6) tests, NF_NAT_IPVx toggles are removed here. v4: also get rid of the assignments in conditionals. Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
2019-02-20 01:38:21 +09:00
return false;
iph = (void *)skb->data + iphdroff;
hdroff = iphdroff + iph->ihl * 4;
if (!l4proto_manip_pkt(skb, iphdroff, hdroff, target, maniptype))
netfilter: nat: merge nf_nat_ipv4,6 into nat core before: text data bss dec hex filename 16566 1576 4136 22278 5706 nf_nat.ko 3598 844 0 4442 115a nf_nat_ipv6.ko 3187 844 0 4031 fbf nf_nat_ipv4.ko after: text data bss dec hex filename 22948 1612 4136 28696 7018 nf_nat.ko ... with ipv4/v6 nat now provided directly via nf_nat.ko. Also changes: ret = nf_nat_ipv4_fn(priv, skb, state); if (ret != NF_DROP && ret != NF_STOLEN && into if (ret != NF_ACCEPT) return ret; everywhere. The nat hooks never should return anything other than ACCEPT or DROP (and the latter only in rare error cases). The original code uses multi-line ANDing including assignment-in-if: if (ret != NF_DROP && ret != NF_STOLEN && !(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) && (ct = nf_ct_get(skb, &ctinfo)) != NULL) { I removed this while moving, breaking those in separate conditionals and moving the assignments into extra lines. checkpatch still generates some warnings: 1. Overly long lines (of moved code). Breaking them is even more ugly. so I kept this as-is. 2. use of extern function declarations in a .c file. This is necessary evil, we must call nf_nat_l3proto_register() from the nat core now. All l3proto related functions are removed later in this series, those prototypes are then removed as well. v2: keep empty nf_nat_ipv6_csum_update stub for CONFIG_IPV6=n case. v3: remove IS_ENABLED(NF_NAT_IPV4/6) tests, NF_NAT_IPVx toggles are removed here. v4: also get rid of the assignments in conditionals. Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
2019-02-20 01:38:21 +09:00
return false;
iph = (void *)skb->data + iphdroff;
if (maniptype == NF_NAT_MANIP_SRC) {
csum_replace4(&iph->check, iph->saddr, target->src.u3.ip);
iph->saddr = target->src.u3.ip;
} else {
csum_replace4(&iph->check, iph->daddr, target->dst.u3.ip);
iph->daddr = target->dst.u3.ip;
}
return true;
}
static bool nf_nat_ipv6_manip_pkt(struct sk_buff *skb,
unsigned int iphdroff,
const struct nf_conntrack_tuple *target,
enum nf_nat_manip_type maniptype)
{
#if IS_ENABLED(CONFIG_IPV6)
struct ipv6hdr *ipv6h;
__be16 frag_off;
int hdroff;
u8 nexthdr;
if (skb_ensure_writable(skb, iphdroff + sizeof(*ipv6h)))
netfilter: nat: merge nf_nat_ipv4,6 into nat core before: text data bss dec hex filename 16566 1576 4136 22278 5706 nf_nat.ko 3598 844 0 4442 115a nf_nat_ipv6.ko 3187 844 0 4031 fbf nf_nat_ipv4.ko after: text data bss dec hex filename 22948 1612 4136 28696 7018 nf_nat.ko ... with ipv4/v6 nat now provided directly via nf_nat.ko. Also changes: ret = nf_nat_ipv4_fn(priv, skb, state); if (ret != NF_DROP && ret != NF_STOLEN && into if (ret != NF_ACCEPT) return ret; everywhere. The nat hooks never should return anything other than ACCEPT or DROP (and the latter only in rare error cases). The original code uses multi-line ANDing including assignment-in-if: if (ret != NF_DROP && ret != NF_STOLEN && !(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) && (ct = nf_ct_get(skb, &ctinfo)) != NULL) { I removed this while moving, breaking those in separate conditionals and moving the assignments into extra lines. checkpatch still generates some warnings: 1. Overly long lines (of moved code). Breaking them is even more ugly. so I kept this as-is. 2. use of extern function declarations in a .c file. This is necessary evil, we must call nf_nat_l3proto_register() from the nat core now. All l3proto related functions are removed later in this series, those prototypes are then removed as well. v2: keep empty nf_nat_ipv6_csum_update stub for CONFIG_IPV6=n case. v3: remove IS_ENABLED(NF_NAT_IPV4/6) tests, NF_NAT_IPVx toggles are removed here. v4: also get rid of the assignments in conditionals. Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
2019-02-20 01:38:21 +09:00
return false;
ipv6h = (void *)skb->data + iphdroff;
nexthdr = ipv6h->nexthdr;
hdroff = ipv6_skip_exthdr(skb, iphdroff + sizeof(*ipv6h),
&nexthdr, &frag_off);
if (hdroff < 0)
goto manip_addr;
if ((frag_off & htons(~0x7)) == 0 &&
!l4proto_manip_pkt(skb, iphdroff, hdroff, target, maniptype))
netfilter: nat: merge nf_nat_ipv4,6 into nat core before: text data bss dec hex filename 16566 1576 4136 22278 5706 nf_nat.ko 3598 844 0 4442 115a nf_nat_ipv6.ko 3187 844 0 4031 fbf nf_nat_ipv4.ko after: text data bss dec hex filename 22948 1612 4136 28696 7018 nf_nat.ko ... with ipv4/v6 nat now provided directly via nf_nat.ko. Also changes: ret = nf_nat_ipv4_fn(priv, skb, state); if (ret != NF_DROP && ret != NF_STOLEN && into if (ret != NF_ACCEPT) return ret; everywhere. The nat hooks never should return anything other than ACCEPT or DROP (and the latter only in rare error cases). The original code uses multi-line ANDing including assignment-in-if: if (ret != NF_DROP && ret != NF_STOLEN && !(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) && (ct = nf_ct_get(skb, &ctinfo)) != NULL) { I removed this while moving, breaking those in separate conditionals and moving the assignments into extra lines. checkpatch still generates some warnings: 1. Overly long lines (of moved code). Breaking them is even more ugly. so I kept this as-is. 2. use of extern function declarations in a .c file. This is necessary evil, we must call nf_nat_l3proto_register() from the nat core now. All l3proto related functions are removed later in this series, those prototypes are then removed as well. v2: keep empty nf_nat_ipv6_csum_update stub for CONFIG_IPV6=n case. v3: remove IS_ENABLED(NF_NAT_IPV4/6) tests, NF_NAT_IPVx toggles are removed here. v4: also get rid of the assignments in conditionals. Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
2019-02-20 01:38:21 +09:00
return false;
/* must reload, offset might have changed */
ipv6h = (void *)skb->data + iphdroff;
manip_addr:
if (maniptype == NF_NAT_MANIP_SRC)
ipv6h->saddr = target->src.u3.in6;
else
ipv6h->daddr = target->dst.u3.in6;
#endif
return true;
}
unsigned int nf_nat_manip_pkt(struct sk_buff *skb, struct nf_conn *ct,
enum nf_nat_manip_type mtype,
enum ip_conntrack_dir dir)
{
struct nf_conntrack_tuple target;
/* We are aiming to look like inverse of other direction. */
nf_ct_invert_tuple(&target, &ct->tuplehash[!dir].tuple);
switch (target.src.l3num) {
case NFPROTO_IPV6:
if (nf_nat_ipv6_manip_pkt(skb, 0, &target, mtype))
return NF_ACCEPT;
break;
case NFPROTO_IPV4:
if (nf_nat_ipv4_manip_pkt(skb, 0, &target, mtype))
return NF_ACCEPT;
break;
default:
WARN_ON_ONCE(1);
break;
}
return NF_DROP;
}
netfilter: nat: merge nf_nat_ipv4,6 into nat core before: text data bss dec hex filename 16566 1576 4136 22278 5706 nf_nat.ko 3598 844 0 4442 115a nf_nat_ipv6.ko 3187 844 0 4031 fbf nf_nat_ipv4.ko after: text data bss dec hex filename 22948 1612 4136 28696 7018 nf_nat.ko ... with ipv4/v6 nat now provided directly via nf_nat.ko. Also changes: ret = nf_nat_ipv4_fn(priv, skb, state); if (ret != NF_DROP && ret != NF_STOLEN && into if (ret != NF_ACCEPT) return ret; everywhere. The nat hooks never should return anything other than ACCEPT or DROP (and the latter only in rare error cases). The original code uses multi-line ANDing including assignment-in-if: if (ret != NF_DROP && ret != NF_STOLEN && !(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) && (ct = nf_ct_get(skb, &ctinfo)) != NULL) { I removed this while moving, breaking those in separate conditionals and moving the assignments into extra lines. checkpatch still generates some warnings: 1. Overly long lines (of moved code). Breaking them is even more ugly. so I kept this as-is. 2. use of extern function declarations in a .c file. This is necessary evil, we must call nf_nat_l3proto_register() from the nat core now. All l3proto related functions are removed later in this series, those prototypes are then removed as well. v2: keep empty nf_nat_ipv6_csum_update stub for CONFIG_IPV6=n case. v3: remove IS_ENABLED(NF_NAT_IPV4/6) tests, NF_NAT_IPVx toggles are removed here. v4: also get rid of the assignments in conditionals. Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
2019-02-20 01:38:21 +09:00
static void nf_nat_ipv4_csum_update(struct sk_buff *skb,
unsigned int iphdroff, __sum16 *check,
const struct nf_conntrack_tuple *t,
enum nf_nat_manip_type maniptype)
{
struct iphdr *iph = (struct iphdr *)(skb->data + iphdroff);
__be32 oldip, newip;
if (maniptype == NF_NAT_MANIP_SRC) {
oldip = iph->saddr;
newip = t->src.u3.ip;
} else {
oldip = iph->daddr;
newip = t->dst.u3.ip;
}
inet_proto_csum_replace4(check, skb, oldip, newip, true);
}
static void nf_nat_ipv6_csum_update(struct sk_buff *skb,
unsigned int iphdroff, __sum16 *check,
const struct nf_conntrack_tuple *t,
enum nf_nat_manip_type maniptype)
{
#if IS_ENABLED(CONFIG_IPV6)
const struct ipv6hdr *ipv6h = (struct ipv6hdr *)(skb->data + iphdroff);
const struct in6_addr *oldip, *newip;
if (maniptype == NF_NAT_MANIP_SRC) {
oldip = &ipv6h->saddr;
newip = &t->src.u3.in6;
} else {
oldip = &ipv6h->daddr;
newip = &t->dst.u3.in6;
}
inet_proto_csum_replace16(check, skb, oldip->s6_addr32,
newip->s6_addr32, true);
#endif
}
static void nf_csum_update(struct sk_buff *skb,
unsigned int iphdroff, __sum16 *check,
const struct nf_conntrack_tuple *t,
enum nf_nat_manip_type maniptype)
{
switch (t->src.l3num) {
case NFPROTO_IPV4:
nf_nat_ipv4_csum_update(skb, iphdroff, check, t, maniptype);
return;
case NFPROTO_IPV6:
nf_nat_ipv6_csum_update(skb, iphdroff, check, t, maniptype);
return;
}
}
netfilter: nat: merge nf_nat_ipv4,6 into nat core before: text data bss dec hex filename 16566 1576 4136 22278 5706 nf_nat.ko 3598 844 0 4442 115a nf_nat_ipv6.ko 3187 844 0 4031 fbf nf_nat_ipv4.ko after: text data bss dec hex filename 22948 1612 4136 28696 7018 nf_nat.ko ... with ipv4/v6 nat now provided directly via nf_nat.ko. Also changes: ret = nf_nat_ipv4_fn(priv, skb, state); if (ret != NF_DROP && ret != NF_STOLEN && into if (ret != NF_ACCEPT) return ret; everywhere. The nat hooks never should return anything other than ACCEPT or DROP (and the latter only in rare error cases). The original code uses multi-line ANDing including assignment-in-if: if (ret != NF_DROP && ret != NF_STOLEN && !(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) && (ct = nf_ct_get(skb, &ctinfo)) != NULL) { I removed this while moving, breaking those in separate conditionals and moving the assignments into extra lines. checkpatch still generates some warnings: 1. Overly long lines (of moved code). Breaking them is even more ugly. so I kept this as-is. 2. use of extern function declarations in a .c file. This is necessary evil, we must call nf_nat_l3proto_register() from the nat core now. All l3proto related functions are removed later in this series, those prototypes are then removed as well. v2: keep empty nf_nat_ipv6_csum_update stub for CONFIG_IPV6=n case. v3: remove IS_ENABLED(NF_NAT_IPV4/6) tests, NF_NAT_IPVx toggles are removed here. v4: also get rid of the assignments in conditionals. Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
2019-02-20 01:38:21 +09:00
static void nf_nat_ipv4_csum_recalc(struct sk_buff *skb,
u8 proto, void *data, __sum16 *check,
int datalen, int oldlen)
{
if (skb->ip_summed != CHECKSUM_PARTIAL) {
const struct iphdr *iph = ip_hdr(skb);
skb->ip_summed = CHECKSUM_PARTIAL;
skb->csum_start = skb_headroom(skb) + skb_network_offset(skb) +
ip_hdrlen(skb);
skb->csum_offset = (void *)check - data;
*check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, datalen,
proto, 0);
} else {
inet_proto_csum_replace2(check, skb,
htons(oldlen), htons(datalen), true);
}
}
#if IS_ENABLED(CONFIG_IPV6)
static void nf_nat_ipv6_csum_recalc(struct sk_buff *skb,
u8 proto, void *data, __sum16 *check,
int datalen, int oldlen)
{
if (skb->ip_summed != CHECKSUM_PARTIAL) {
const struct ipv6hdr *ipv6h = ipv6_hdr(skb);
skb->ip_summed = CHECKSUM_PARTIAL;
skb->csum_start = skb_headroom(skb) + skb_network_offset(skb) +
(data - (void *)skb->data);
skb->csum_offset = (void *)check - data;
*check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr,
datalen, proto, 0);
} else {
inet_proto_csum_replace2(check, skb,
htons(oldlen), htons(datalen), true);
}
}
#endif
void nf_nat_csum_recalc(struct sk_buff *skb,
u8 nfproto, u8 proto, void *data, __sum16 *check,
int datalen, int oldlen)
{
switch (nfproto) {
case NFPROTO_IPV4:
nf_nat_ipv4_csum_recalc(skb, proto, data, check,
datalen, oldlen);
return;
#if IS_ENABLED(CONFIG_IPV6)
case NFPROTO_IPV6:
nf_nat_ipv6_csum_recalc(skb, proto, data, check,
datalen, oldlen);
return;
#endif
}
WARN_ON_ONCE(1);
}
netfilter: nat: merge nf_nat_ipv4,6 into nat core before: text data bss dec hex filename 16566 1576 4136 22278 5706 nf_nat.ko 3598 844 0 4442 115a nf_nat_ipv6.ko 3187 844 0 4031 fbf nf_nat_ipv4.ko after: text data bss dec hex filename 22948 1612 4136 28696 7018 nf_nat.ko ... with ipv4/v6 nat now provided directly via nf_nat.ko. Also changes: ret = nf_nat_ipv4_fn(priv, skb, state); if (ret != NF_DROP && ret != NF_STOLEN && into if (ret != NF_ACCEPT) return ret; everywhere. The nat hooks never should return anything other than ACCEPT or DROP (and the latter only in rare error cases). The original code uses multi-line ANDing including assignment-in-if: if (ret != NF_DROP && ret != NF_STOLEN && !(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) && (ct = nf_ct_get(skb, &ctinfo)) != NULL) { I removed this while moving, breaking those in separate conditionals and moving the assignments into extra lines. checkpatch still generates some warnings: 1. Overly long lines (of moved code). Breaking them is even more ugly. so I kept this as-is. 2. use of extern function declarations in a .c file. This is necessary evil, we must call nf_nat_l3proto_register() from the nat core now. All l3proto related functions are removed later in this series, those prototypes are then removed as well. v2: keep empty nf_nat_ipv6_csum_update stub for CONFIG_IPV6=n case. v3: remove IS_ENABLED(NF_NAT_IPV4/6) tests, NF_NAT_IPVx toggles are removed here. v4: also get rid of the assignments in conditionals. Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
2019-02-20 01:38:21 +09:00
int nf_nat_icmp_reply_translation(struct sk_buff *skb,
struct nf_conn *ct,
enum ip_conntrack_info ctinfo,
unsigned int hooknum)
{
struct {
struct icmphdr icmp;
struct iphdr ip;
} *inside;
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
enum nf_nat_manip_type manip = HOOK2MANIP(hooknum);
unsigned int hdrlen = ip_hdrlen(skb);
struct nf_conntrack_tuple target;
unsigned long statusbit;
WARN_ON(ctinfo != IP_CT_RELATED && ctinfo != IP_CT_RELATED_REPLY);
if (skb_ensure_writable(skb, hdrlen + sizeof(*inside)))
netfilter: nat: merge nf_nat_ipv4,6 into nat core before: text data bss dec hex filename 16566 1576 4136 22278 5706 nf_nat.ko 3598 844 0 4442 115a nf_nat_ipv6.ko 3187 844 0 4031 fbf nf_nat_ipv4.ko after: text data bss dec hex filename 22948 1612 4136 28696 7018 nf_nat.ko ... with ipv4/v6 nat now provided directly via nf_nat.ko. Also changes: ret = nf_nat_ipv4_fn(priv, skb, state); if (ret != NF_DROP && ret != NF_STOLEN && into if (ret != NF_ACCEPT) return ret; everywhere. The nat hooks never should return anything other than ACCEPT or DROP (and the latter only in rare error cases). The original code uses multi-line ANDing including assignment-in-if: if (ret != NF_DROP && ret != NF_STOLEN && !(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) && (ct = nf_ct_get(skb, &ctinfo)) != NULL) { I removed this while moving, breaking those in separate conditionals and moving the assignments into extra lines. checkpatch still generates some warnings: 1. Overly long lines (of moved code). Breaking them is even more ugly. so I kept this as-is. 2. use of extern function declarations in a .c file. This is necessary evil, we must call nf_nat_l3proto_register() from the nat core now. All l3proto related functions are removed later in this series, those prototypes are then removed as well. v2: keep empty nf_nat_ipv6_csum_update stub for CONFIG_IPV6=n case. v3: remove IS_ENABLED(NF_NAT_IPV4/6) tests, NF_NAT_IPVx toggles are removed here. v4: also get rid of the assignments in conditionals. Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
2019-02-20 01:38:21 +09:00
return 0;
if (nf_ip_checksum(skb, hooknum, hdrlen, IPPROTO_ICMP))
netfilter: nat: merge nf_nat_ipv4,6 into nat core before: text data bss dec hex filename 16566 1576 4136 22278 5706 nf_nat.ko 3598 844 0 4442 115a nf_nat_ipv6.ko 3187 844 0 4031 fbf nf_nat_ipv4.ko after: text data bss dec hex filename 22948 1612 4136 28696 7018 nf_nat.ko ... with ipv4/v6 nat now provided directly via nf_nat.ko. Also changes: ret = nf_nat_ipv4_fn(priv, skb, state); if (ret != NF_DROP && ret != NF_STOLEN && into if (ret != NF_ACCEPT) return ret; everywhere. The nat hooks never should return anything other than ACCEPT or DROP (and the latter only in rare error cases). The original code uses multi-line ANDing including assignment-in-if: if (ret != NF_DROP && ret != NF_STOLEN && !(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) && (ct = nf_ct_get(skb, &ctinfo)) != NULL) { I removed this while moving, breaking those in separate conditionals and moving the assignments into extra lines. checkpatch still generates some warnings: 1. Overly long lines (of moved code). Breaking them is even more ugly. so I kept this as-is. 2. use of extern function declarations in a .c file. This is necessary evil, we must call nf_nat_l3proto_register() from the nat core now. All l3proto related functions are removed later in this series, those prototypes are then removed as well. v2: keep empty nf_nat_ipv6_csum_update stub for CONFIG_IPV6=n case. v3: remove IS_ENABLED(NF_NAT_IPV4/6) tests, NF_NAT_IPVx toggles are removed here. v4: also get rid of the assignments in conditionals. Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
2019-02-20 01:38:21 +09:00
return 0;
inside = (void *)skb->data + hdrlen;
if (inside->icmp.type == ICMP_REDIRECT) {
if ((ct->status & IPS_NAT_DONE_MASK) != IPS_NAT_DONE_MASK)
return 0;
if (ct->status & IPS_NAT_MASK)
return 0;
}
if (manip == NF_NAT_MANIP_SRC)
statusbit = IPS_SRC_NAT;
else
statusbit = IPS_DST_NAT;
/* Invert if this is reply direction */
if (dir == IP_CT_DIR_REPLY)
statusbit ^= IPS_NAT_MASK;
if (!(ct->status & statusbit))
return 1;
if (!nf_nat_ipv4_manip_pkt(skb, hdrlen + sizeof(inside->icmp),
&ct->tuplehash[!dir].tuple, !manip))
return 0;
if (skb->ip_summed != CHECKSUM_PARTIAL) {
/* Reloading "inside" here since manip_pkt may reallocate */
inside = (void *)skb->data + hdrlen;
inside->icmp.checksum = 0;
inside->icmp.checksum =
csum_fold(skb_checksum(skb, hdrlen,
skb->len - hdrlen, 0));
}
/* Change outer to look like the reply to an incoming packet */
nf_ct_invert_tuple(&target, &ct->tuplehash[!dir].tuple);
target.dst.protonum = IPPROTO_ICMP;
if (!nf_nat_ipv4_manip_pkt(skb, 0, &target, manip))
return 0;
return 1;
}
EXPORT_SYMBOL_GPL(nf_nat_icmp_reply_translation);
static unsigned int
nf_nat_ipv4_fn(void *priv, struct sk_buff *skb,
const struct nf_hook_state *state)
{
struct nf_conn *ct;
enum ip_conntrack_info ctinfo;
ct = nf_ct_get(skb, &ctinfo);
if (!ct)
return NF_ACCEPT;
if (ctinfo == IP_CT_RELATED || ctinfo == IP_CT_RELATED_REPLY) {
if (ip_hdr(skb)->protocol == IPPROTO_ICMP) {
if (!nf_nat_icmp_reply_translation(skb, ct, ctinfo,
state->hook))
return NF_DROP;
else
return NF_ACCEPT;
}
}
return nf_nat_inet_fn(priv, skb, state);
}
static unsigned int
netfilter: nf_nat: undo erroneous tcp edemux lookup commit 03a3ca37e4c6478e3a84f04c8429dd5889e107fd upstream. Under extremely rare conditions TCP early demux will retrieve the wrong socket. 1. local machine establishes a connection to a remote server, S, on port p. This gives: laddr:lport -> S:p ... both in tcp and conntrack. 2. local machine establishes a connection to host H, on port p2. 2a. TCP stack choses same laddr:lport, so we have laddr:lport -> H:p2 from TCP point of view. 2b). There is a destination NAT rewrite in place, translating H:p2 to S:p. This results in following conntrack entries: I) laddr:lport -> S:p (origin) S:p -> laddr:lport (reply) II) laddr:lport -> H:p2 (origin) S:p -> laddr:lport2 (reply) NAT engine has rewritten laddr:lport to laddr:lport2 to map the reply packet to the correct origin. When server sends SYN/ACK to laddr:lport2, the PREROUTING hook will undo-the SNAT transformation, rewriting IP header to S:p -> laddr:lport This causes TCP early demux to associate the skb with the TCP socket of the first connection. The INPUT hook will then reverse the DNAT transformation, rewriting the IP header to H:p2 -> laddr:lport. Because packet ends up with the wrong socket, the new connection never completes: originator stays in SYN_SENT and conntrack entry remains in SYN_RECV until timeout, and responder retransmits SYN/ACK until it gives up. To resolve this, orphan the skb after the input rewrite: Because the source IP address changed, the socket must be incorrect. We can't move the DNAT undo to prerouting due to backwards compatibility, doing so will make iptables/nftables rules to no longer match the way they did. After orphan, the packet will be handed to the next protocol layer (tcp, udp, ...) and that will repeat the socket lookup just like as if early demux was disabled. Fixes: 41063e9dd1195 ("ipv4: Early TCP socket demux.") Closes: https://bugzilla.netfilter.org/show_bug.cgi?id=1427 Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2021-02-25 01:23:19 +09:00
nf_nat_ipv4_pre_routing(void *priv, struct sk_buff *skb,
const struct nf_hook_state *state)
netfilter: nat: merge nf_nat_ipv4,6 into nat core before: text data bss dec hex filename 16566 1576 4136 22278 5706 nf_nat.ko 3598 844 0 4442 115a nf_nat_ipv6.ko 3187 844 0 4031 fbf nf_nat_ipv4.ko after: text data bss dec hex filename 22948 1612 4136 28696 7018 nf_nat.ko ... with ipv4/v6 nat now provided directly via nf_nat.ko. Also changes: ret = nf_nat_ipv4_fn(priv, skb, state); if (ret != NF_DROP && ret != NF_STOLEN && into if (ret != NF_ACCEPT) return ret; everywhere. The nat hooks never should return anything other than ACCEPT or DROP (and the latter only in rare error cases). The original code uses multi-line ANDing including assignment-in-if: if (ret != NF_DROP && ret != NF_STOLEN && !(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) && (ct = nf_ct_get(skb, &ctinfo)) != NULL) { I removed this while moving, breaking those in separate conditionals and moving the assignments into extra lines. checkpatch still generates some warnings: 1. Overly long lines (of moved code). Breaking them is even more ugly. so I kept this as-is. 2. use of extern function declarations in a .c file. This is necessary evil, we must call nf_nat_l3proto_register() from the nat core now. All l3proto related functions are removed later in this series, those prototypes are then removed as well. v2: keep empty nf_nat_ipv6_csum_update stub for CONFIG_IPV6=n case. v3: remove IS_ENABLED(NF_NAT_IPV4/6) tests, NF_NAT_IPVx toggles are removed here. v4: also get rid of the assignments in conditionals. Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
2019-02-20 01:38:21 +09:00
{
unsigned int ret;
__be32 daddr = ip_hdr(skb)->daddr;
ret = nf_nat_ipv4_fn(priv, skb, state);
if (ret == NF_ACCEPT && daddr != ip_hdr(skb)->daddr)
skb_dst_drop(skb);
return ret;
}
netfilter: nf_nat: undo erroneous tcp edemux lookup commit 03a3ca37e4c6478e3a84f04c8429dd5889e107fd upstream. Under extremely rare conditions TCP early demux will retrieve the wrong socket. 1. local machine establishes a connection to a remote server, S, on port p. This gives: laddr:lport -> S:p ... both in tcp and conntrack. 2. local machine establishes a connection to host H, on port p2. 2a. TCP stack choses same laddr:lport, so we have laddr:lport -> H:p2 from TCP point of view. 2b). There is a destination NAT rewrite in place, translating H:p2 to S:p. This results in following conntrack entries: I) laddr:lport -> S:p (origin) S:p -> laddr:lport (reply) II) laddr:lport -> H:p2 (origin) S:p -> laddr:lport2 (reply) NAT engine has rewritten laddr:lport to laddr:lport2 to map the reply packet to the correct origin. When server sends SYN/ACK to laddr:lport2, the PREROUTING hook will undo-the SNAT transformation, rewriting IP header to S:p -> laddr:lport This causes TCP early demux to associate the skb with the TCP socket of the first connection. The INPUT hook will then reverse the DNAT transformation, rewriting the IP header to H:p2 -> laddr:lport. Because packet ends up with the wrong socket, the new connection never completes: originator stays in SYN_SENT and conntrack entry remains in SYN_RECV until timeout, and responder retransmits SYN/ACK until it gives up. To resolve this, orphan the skb after the input rewrite: Because the source IP address changed, the socket must be incorrect. We can't move the DNAT undo to prerouting due to backwards compatibility, doing so will make iptables/nftables rules to no longer match the way they did. After orphan, the packet will be handed to the next protocol layer (tcp, udp, ...) and that will repeat the socket lookup just like as if early demux was disabled. Fixes: 41063e9dd1195 ("ipv4: Early TCP socket demux.") Closes: https://bugzilla.netfilter.org/show_bug.cgi?id=1427 Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2021-02-25 01:23:19 +09:00
static unsigned int
nf_nat_ipv4_local_in(void *priv, struct sk_buff *skb,
const struct nf_hook_state *state)
{
__be32 saddr = ip_hdr(skb)->saddr;
struct sock *sk = skb->sk;
unsigned int ret;
ret = nf_nat_ipv4_fn(priv, skb, state);
if (ret == NF_ACCEPT && sk && saddr != ip_hdr(skb)->saddr &&
!inet_sk_transparent(sk))
skb_orphan(skb); /* TCP edemux obtained wrong socket */
return ret;
}
netfilter: nat: merge nf_nat_ipv4,6 into nat core before: text data bss dec hex filename 16566 1576 4136 22278 5706 nf_nat.ko 3598 844 0 4442 115a nf_nat_ipv6.ko 3187 844 0 4031 fbf nf_nat_ipv4.ko after: text data bss dec hex filename 22948 1612 4136 28696 7018 nf_nat.ko ... with ipv4/v6 nat now provided directly via nf_nat.ko. Also changes: ret = nf_nat_ipv4_fn(priv, skb, state); if (ret != NF_DROP && ret != NF_STOLEN && into if (ret != NF_ACCEPT) return ret; everywhere. The nat hooks never should return anything other than ACCEPT or DROP (and the latter only in rare error cases). The original code uses multi-line ANDing including assignment-in-if: if (ret != NF_DROP && ret != NF_STOLEN && !(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) && (ct = nf_ct_get(skb, &ctinfo)) != NULL) { I removed this while moving, breaking those in separate conditionals and moving the assignments into extra lines. checkpatch still generates some warnings: 1. Overly long lines (of moved code). Breaking them is even more ugly. so I kept this as-is. 2. use of extern function declarations in a .c file. This is necessary evil, we must call nf_nat_l3proto_register() from the nat core now. All l3proto related functions are removed later in this series, those prototypes are then removed as well. v2: keep empty nf_nat_ipv6_csum_update stub for CONFIG_IPV6=n case. v3: remove IS_ENABLED(NF_NAT_IPV4/6) tests, NF_NAT_IPVx toggles are removed here. v4: also get rid of the assignments in conditionals. Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
2019-02-20 01:38:21 +09:00
static unsigned int
nf_nat_ipv4_out(void *priv, struct sk_buff *skb,
const struct nf_hook_state *state)
{
#ifdef CONFIG_XFRM
const struct nf_conn *ct;
enum ip_conntrack_info ctinfo;
int err;
#endif
unsigned int ret;
ret = nf_nat_ipv4_fn(priv, skb, state);
#ifdef CONFIG_XFRM
if (ret != NF_ACCEPT)
return ret;
if (IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED)
return ret;
ct = nf_ct_get(skb, &ctinfo);
if (ct) {
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
if (ct->tuplehash[dir].tuple.src.u3.ip !=
ct->tuplehash[!dir].tuple.dst.u3.ip ||
(ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMP &&
ct->tuplehash[dir].tuple.src.u.all !=
ct->tuplehash[!dir].tuple.dst.u.all)) {
err = nf_xfrm_me_harder(state->net, skb, AF_INET);
if (err < 0)
ret = NF_DROP_ERR(err);
}
}
#endif
return ret;
}
static unsigned int
nf_nat_ipv4_local_fn(void *priv, struct sk_buff *skb,
const struct nf_hook_state *state)
{
const struct nf_conn *ct;
enum ip_conntrack_info ctinfo;
unsigned int ret;
int err;
ret = nf_nat_ipv4_fn(priv, skb, state);
if (ret != NF_ACCEPT)
return ret;
ct = nf_ct_get(skb, &ctinfo);
if (ct) {
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
if (ct->tuplehash[dir].tuple.dst.u3.ip !=
ct->tuplehash[!dir].tuple.src.u3.ip) {
netfilter: use actual socket sk rather than skb sk when routing harder [ Upstream commit 46d6c5ae953cc0be38efd0e469284df7c4328cf8 ] If netfilter changes the packet mark when mangling, the packet is rerouted using the route_me_harder set of functions. Prior to this commit, there's one big difference between route_me_harder and the ordinary initial routing functions, described in the comment above __ip_queue_xmit(): /* Note: skb->sk can be different from sk, in case of tunnels */ int __ip_queue_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl, That function goes on to correctly make use of sk->sk_bound_dev_if, rather than skb->sk->sk_bound_dev_if. And indeed the comment is true: a tunnel will receive a packet in ndo_start_xmit with an initial skb->sk. It will make some transformations to that packet, and then it will send the encapsulated packet out of a *new* socket. That new socket will basically always have a different sk_bound_dev_if (otherwise there'd be a routing loop). So for the purposes of routing the encapsulated packet, the routing information as it pertains to the socket should come from that socket's sk, rather than the packet's original skb->sk. For that reason __ip_queue_xmit() and related functions all do the right thing. One might argue that all tunnels should just call skb_orphan(skb) before transmitting the encapsulated packet into the new socket. But tunnels do *not* do this -- and this is wisely avoided in skb_scrub_packet() too -- because features like TSQ rely on skb->destructor() being called when that buffer space is truely available again. Calling skb_orphan(skb) too early would result in buffers filling up unnecessarily and accounting info being all wrong. Instead, additional routing must take into account the new sk, just as __ip_queue_xmit() notes. So, this commit addresses the problem by fishing the correct sk out of state->sk -- it's already set properly in the call to nf_hook() in __ip_local_out(), which receives the sk as part of its normal functionality. So we make sure to plumb state->sk through the various route_me_harder functions, and then make correct use of it following the example of __ip_queue_xmit(). Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com> Reviewed-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org> Signed-off-by: Sasha Levin <sashal@kernel.org>
2020-10-29 11:56:06 +09:00
err = ip_route_me_harder(state->net, state->sk, skb, RTN_UNSPEC);
netfilter: nat: merge nf_nat_ipv4,6 into nat core before: text data bss dec hex filename 16566 1576 4136 22278 5706 nf_nat.ko 3598 844 0 4442 115a nf_nat_ipv6.ko 3187 844 0 4031 fbf nf_nat_ipv4.ko after: text data bss dec hex filename 22948 1612 4136 28696 7018 nf_nat.ko ... with ipv4/v6 nat now provided directly via nf_nat.ko. Also changes: ret = nf_nat_ipv4_fn(priv, skb, state); if (ret != NF_DROP && ret != NF_STOLEN && into if (ret != NF_ACCEPT) return ret; everywhere. The nat hooks never should return anything other than ACCEPT or DROP (and the latter only in rare error cases). The original code uses multi-line ANDing including assignment-in-if: if (ret != NF_DROP && ret != NF_STOLEN && !(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) && (ct = nf_ct_get(skb, &ctinfo)) != NULL) { I removed this while moving, breaking those in separate conditionals and moving the assignments into extra lines. checkpatch still generates some warnings: 1. Overly long lines (of moved code). Breaking them is even more ugly. so I kept this as-is. 2. use of extern function declarations in a .c file. This is necessary evil, we must call nf_nat_l3proto_register() from the nat core now. All l3proto related functions are removed later in this series, those prototypes are then removed as well. v2: keep empty nf_nat_ipv6_csum_update stub for CONFIG_IPV6=n case. v3: remove IS_ENABLED(NF_NAT_IPV4/6) tests, NF_NAT_IPVx toggles are removed here. v4: also get rid of the assignments in conditionals. Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
2019-02-20 01:38:21 +09:00
if (err < 0)
ret = NF_DROP_ERR(err);
}
#ifdef CONFIG_XFRM
else if (!(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) &&
ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMP &&
ct->tuplehash[dir].tuple.dst.u.all !=
ct->tuplehash[!dir].tuple.src.u.all) {
err = nf_xfrm_me_harder(state->net, skb, AF_INET);
if (err < 0)
ret = NF_DROP_ERR(err);
}
#endif
}
return ret;
}
static const struct nf_hook_ops nf_nat_ipv4_ops[] = {
netfilter: nat: merge nf_nat_ipv4,6 into nat core before: text data bss dec hex filename 16566 1576 4136 22278 5706 nf_nat.ko 3598 844 0 4442 115a nf_nat_ipv6.ko 3187 844 0 4031 fbf nf_nat_ipv4.ko after: text data bss dec hex filename 22948 1612 4136 28696 7018 nf_nat.ko ... with ipv4/v6 nat now provided directly via nf_nat.ko. Also changes: ret = nf_nat_ipv4_fn(priv, skb, state); if (ret != NF_DROP && ret != NF_STOLEN && into if (ret != NF_ACCEPT) return ret; everywhere. The nat hooks never should return anything other than ACCEPT or DROP (and the latter only in rare error cases). The original code uses multi-line ANDing including assignment-in-if: if (ret != NF_DROP && ret != NF_STOLEN && !(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) && (ct = nf_ct_get(skb, &ctinfo)) != NULL) { I removed this while moving, breaking those in separate conditionals and moving the assignments into extra lines. checkpatch still generates some warnings: 1. Overly long lines (of moved code). Breaking them is even more ugly. so I kept this as-is. 2. use of extern function declarations in a .c file. This is necessary evil, we must call nf_nat_l3proto_register() from the nat core now. All l3proto related functions are removed later in this series, those prototypes are then removed as well. v2: keep empty nf_nat_ipv6_csum_update stub for CONFIG_IPV6=n case. v3: remove IS_ENABLED(NF_NAT_IPV4/6) tests, NF_NAT_IPVx toggles are removed here. v4: also get rid of the assignments in conditionals. Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
2019-02-20 01:38:21 +09:00
/* Before packet filtering, change destination */
{
netfilter: nf_nat: undo erroneous tcp edemux lookup commit 03a3ca37e4c6478e3a84f04c8429dd5889e107fd upstream. Under extremely rare conditions TCP early demux will retrieve the wrong socket. 1. local machine establishes a connection to a remote server, S, on port p. This gives: laddr:lport -> S:p ... both in tcp and conntrack. 2. local machine establishes a connection to host H, on port p2. 2a. TCP stack choses same laddr:lport, so we have laddr:lport -> H:p2 from TCP point of view. 2b). There is a destination NAT rewrite in place, translating H:p2 to S:p. This results in following conntrack entries: I) laddr:lport -> S:p (origin) S:p -> laddr:lport (reply) II) laddr:lport -> H:p2 (origin) S:p -> laddr:lport2 (reply) NAT engine has rewritten laddr:lport to laddr:lport2 to map the reply packet to the correct origin. When server sends SYN/ACK to laddr:lport2, the PREROUTING hook will undo-the SNAT transformation, rewriting IP header to S:p -> laddr:lport This causes TCP early demux to associate the skb with the TCP socket of the first connection. The INPUT hook will then reverse the DNAT transformation, rewriting the IP header to H:p2 -> laddr:lport. Because packet ends up with the wrong socket, the new connection never completes: originator stays in SYN_SENT and conntrack entry remains in SYN_RECV until timeout, and responder retransmits SYN/ACK until it gives up. To resolve this, orphan the skb after the input rewrite: Because the source IP address changed, the socket must be incorrect. We can't move the DNAT undo to prerouting due to backwards compatibility, doing so will make iptables/nftables rules to no longer match the way they did. After orphan, the packet will be handed to the next protocol layer (tcp, udp, ...) and that will repeat the socket lookup just like as if early demux was disabled. Fixes: 41063e9dd1195 ("ipv4: Early TCP socket demux.") Closes: https://bugzilla.netfilter.org/show_bug.cgi?id=1427 Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2021-02-25 01:23:19 +09:00
.hook = nf_nat_ipv4_pre_routing,
netfilter: nat: merge nf_nat_ipv4,6 into nat core before: text data bss dec hex filename 16566 1576 4136 22278 5706 nf_nat.ko 3598 844 0 4442 115a nf_nat_ipv6.ko 3187 844 0 4031 fbf nf_nat_ipv4.ko after: text data bss dec hex filename 22948 1612 4136 28696 7018 nf_nat.ko ... with ipv4/v6 nat now provided directly via nf_nat.ko. Also changes: ret = nf_nat_ipv4_fn(priv, skb, state); if (ret != NF_DROP && ret != NF_STOLEN && into if (ret != NF_ACCEPT) return ret; everywhere. The nat hooks never should return anything other than ACCEPT or DROP (and the latter only in rare error cases). The original code uses multi-line ANDing including assignment-in-if: if (ret != NF_DROP && ret != NF_STOLEN && !(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) && (ct = nf_ct_get(skb, &ctinfo)) != NULL) { I removed this while moving, breaking those in separate conditionals and moving the assignments into extra lines. checkpatch still generates some warnings: 1. Overly long lines (of moved code). Breaking them is even more ugly. so I kept this as-is. 2. use of extern function declarations in a .c file. This is necessary evil, we must call nf_nat_l3proto_register() from the nat core now. All l3proto related functions are removed later in this series, those prototypes are then removed as well. v2: keep empty nf_nat_ipv6_csum_update stub for CONFIG_IPV6=n case. v3: remove IS_ENABLED(NF_NAT_IPV4/6) tests, NF_NAT_IPVx toggles are removed here. v4: also get rid of the assignments in conditionals. Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
2019-02-20 01:38:21 +09:00
.pf = NFPROTO_IPV4,
.hooknum = NF_INET_PRE_ROUTING,
.priority = NF_IP_PRI_NAT_DST,
},
/* After packet filtering, change source */
{
.hook = nf_nat_ipv4_out,
.pf = NFPROTO_IPV4,
.hooknum = NF_INET_POST_ROUTING,
.priority = NF_IP_PRI_NAT_SRC,
},
/* Before packet filtering, change destination */
{
.hook = nf_nat_ipv4_local_fn,
.pf = NFPROTO_IPV4,
.hooknum = NF_INET_LOCAL_OUT,
.priority = NF_IP_PRI_NAT_DST,
},
/* After packet filtering, change source */
{
netfilter: nf_nat: undo erroneous tcp edemux lookup commit 03a3ca37e4c6478e3a84f04c8429dd5889e107fd upstream. Under extremely rare conditions TCP early demux will retrieve the wrong socket. 1. local machine establishes a connection to a remote server, S, on port p. This gives: laddr:lport -> S:p ... both in tcp and conntrack. 2. local machine establishes a connection to host H, on port p2. 2a. TCP stack choses same laddr:lport, so we have laddr:lport -> H:p2 from TCP point of view. 2b). There is a destination NAT rewrite in place, translating H:p2 to S:p. This results in following conntrack entries: I) laddr:lport -> S:p (origin) S:p -> laddr:lport (reply) II) laddr:lport -> H:p2 (origin) S:p -> laddr:lport2 (reply) NAT engine has rewritten laddr:lport to laddr:lport2 to map the reply packet to the correct origin. When server sends SYN/ACK to laddr:lport2, the PREROUTING hook will undo-the SNAT transformation, rewriting IP header to S:p -> laddr:lport This causes TCP early demux to associate the skb with the TCP socket of the first connection. The INPUT hook will then reverse the DNAT transformation, rewriting the IP header to H:p2 -> laddr:lport. Because packet ends up with the wrong socket, the new connection never completes: originator stays in SYN_SENT and conntrack entry remains in SYN_RECV until timeout, and responder retransmits SYN/ACK until it gives up. To resolve this, orphan the skb after the input rewrite: Because the source IP address changed, the socket must be incorrect. We can't move the DNAT undo to prerouting due to backwards compatibility, doing so will make iptables/nftables rules to no longer match the way they did. After orphan, the packet will be handed to the next protocol layer (tcp, udp, ...) and that will repeat the socket lookup just like as if early demux was disabled. Fixes: 41063e9dd1195 ("ipv4: Early TCP socket demux.") Closes: https://bugzilla.netfilter.org/show_bug.cgi?id=1427 Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2021-02-25 01:23:19 +09:00
.hook = nf_nat_ipv4_local_in,
netfilter: nat: merge nf_nat_ipv4,6 into nat core before: text data bss dec hex filename 16566 1576 4136 22278 5706 nf_nat.ko 3598 844 0 4442 115a nf_nat_ipv6.ko 3187 844 0 4031 fbf nf_nat_ipv4.ko after: text data bss dec hex filename 22948 1612 4136 28696 7018 nf_nat.ko ... with ipv4/v6 nat now provided directly via nf_nat.ko. Also changes: ret = nf_nat_ipv4_fn(priv, skb, state); if (ret != NF_DROP && ret != NF_STOLEN && into if (ret != NF_ACCEPT) return ret; everywhere. The nat hooks never should return anything other than ACCEPT or DROP (and the latter only in rare error cases). The original code uses multi-line ANDing including assignment-in-if: if (ret != NF_DROP && ret != NF_STOLEN && !(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) && (ct = nf_ct_get(skb, &ctinfo)) != NULL) { I removed this while moving, breaking those in separate conditionals and moving the assignments into extra lines. checkpatch still generates some warnings: 1. Overly long lines (of moved code). Breaking them is even more ugly. so I kept this as-is. 2. use of extern function declarations in a .c file. This is necessary evil, we must call nf_nat_l3proto_register() from the nat core now. All l3proto related functions are removed later in this series, those prototypes are then removed as well. v2: keep empty nf_nat_ipv6_csum_update stub for CONFIG_IPV6=n case. v3: remove IS_ENABLED(NF_NAT_IPV4/6) tests, NF_NAT_IPVx toggles are removed here. v4: also get rid of the assignments in conditionals. Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
2019-02-20 01:38:21 +09:00
.pf = NFPROTO_IPV4,
.hooknum = NF_INET_LOCAL_IN,
.priority = NF_IP_PRI_NAT_SRC,
},
};
int nf_nat_ipv4_register_fn(struct net *net, const struct nf_hook_ops *ops)
netfilter: nat: merge nf_nat_ipv4,6 into nat core before: text data bss dec hex filename 16566 1576 4136 22278 5706 nf_nat.ko 3598 844 0 4442 115a nf_nat_ipv6.ko 3187 844 0 4031 fbf nf_nat_ipv4.ko after: text data bss dec hex filename 22948 1612 4136 28696 7018 nf_nat.ko ... with ipv4/v6 nat now provided directly via nf_nat.ko. Also changes: ret = nf_nat_ipv4_fn(priv, skb, state); if (ret != NF_DROP && ret != NF_STOLEN && into if (ret != NF_ACCEPT) return ret; everywhere. The nat hooks never should return anything other than ACCEPT or DROP (and the latter only in rare error cases). The original code uses multi-line ANDing including assignment-in-if: if (ret != NF_DROP && ret != NF_STOLEN && !(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) && (ct = nf_ct_get(skb, &ctinfo)) != NULL) { I removed this while moving, breaking those in separate conditionals and moving the assignments into extra lines. checkpatch still generates some warnings: 1. Overly long lines (of moved code). Breaking them is even more ugly. so I kept this as-is. 2. use of extern function declarations in a .c file. This is necessary evil, we must call nf_nat_l3proto_register() from the nat core now. All l3proto related functions are removed later in this series, those prototypes are then removed as well. v2: keep empty nf_nat_ipv6_csum_update stub for CONFIG_IPV6=n case. v3: remove IS_ENABLED(NF_NAT_IPV4/6) tests, NF_NAT_IPVx toggles are removed here. v4: also get rid of the assignments in conditionals. Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
2019-02-20 01:38:21 +09:00
{
return nf_nat_register_fn(net, ops->pf, ops, nf_nat_ipv4_ops,
ARRAY_SIZE(nf_nat_ipv4_ops));
netfilter: nat: merge nf_nat_ipv4,6 into nat core before: text data bss dec hex filename 16566 1576 4136 22278 5706 nf_nat.ko 3598 844 0 4442 115a nf_nat_ipv6.ko 3187 844 0 4031 fbf nf_nat_ipv4.ko after: text data bss dec hex filename 22948 1612 4136 28696 7018 nf_nat.ko ... with ipv4/v6 nat now provided directly via nf_nat.ko. Also changes: ret = nf_nat_ipv4_fn(priv, skb, state); if (ret != NF_DROP && ret != NF_STOLEN && into if (ret != NF_ACCEPT) return ret; everywhere. The nat hooks never should return anything other than ACCEPT or DROP (and the latter only in rare error cases). The original code uses multi-line ANDing including assignment-in-if: if (ret != NF_DROP && ret != NF_STOLEN && !(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) && (ct = nf_ct_get(skb, &ctinfo)) != NULL) { I removed this while moving, breaking those in separate conditionals and moving the assignments into extra lines. checkpatch still generates some warnings: 1. Overly long lines (of moved code). Breaking them is even more ugly. so I kept this as-is. 2. use of extern function declarations in a .c file. This is necessary evil, we must call nf_nat_l3proto_register() from the nat core now. All l3proto related functions are removed later in this series, those prototypes are then removed as well. v2: keep empty nf_nat_ipv6_csum_update stub for CONFIG_IPV6=n case. v3: remove IS_ENABLED(NF_NAT_IPV4/6) tests, NF_NAT_IPVx toggles are removed here. v4: also get rid of the assignments in conditionals. Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
2019-02-20 01:38:21 +09:00
}
EXPORT_SYMBOL_GPL(nf_nat_ipv4_register_fn);
netfilter: nat: merge nf_nat_ipv4,6 into nat core before: text data bss dec hex filename 16566 1576 4136 22278 5706 nf_nat.ko 3598 844 0 4442 115a nf_nat_ipv6.ko 3187 844 0 4031 fbf nf_nat_ipv4.ko after: text data bss dec hex filename 22948 1612 4136 28696 7018 nf_nat.ko ... with ipv4/v6 nat now provided directly via nf_nat.ko. Also changes: ret = nf_nat_ipv4_fn(priv, skb, state); if (ret != NF_DROP && ret != NF_STOLEN && into if (ret != NF_ACCEPT) return ret; everywhere. The nat hooks never should return anything other than ACCEPT or DROP (and the latter only in rare error cases). The original code uses multi-line ANDing including assignment-in-if: if (ret != NF_DROP && ret != NF_STOLEN && !(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) && (ct = nf_ct_get(skb, &ctinfo)) != NULL) { I removed this while moving, breaking those in separate conditionals and moving the assignments into extra lines. checkpatch still generates some warnings: 1. Overly long lines (of moved code). Breaking them is even more ugly. so I kept this as-is. 2. use of extern function declarations in a .c file. This is necessary evil, we must call nf_nat_l3proto_register() from the nat core now. All l3proto related functions are removed later in this series, those prototypes are then removed as well. v2: keep empty nf_nat_ipv6_csum_update stub for CONFIG_IPV6=n case. v3: remove IS_ENABLED(NF_NAT_IPV4/6) tests, NF_NAT_IPVx toggles are removed here. v4: also get rid of the assignments in conditionals. Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
2019-02-20 01:38:21 +09:00
void nf_nat_ipv4_unregister_fn(struct net *net, const struct nf_hook_ops *ops)
netfilter: nat: merge nf_nat_ipv4,6 into nat core before: text data bss dec hex filename 16566 1576 4136 22278 5706 nf_nat.ko 3598 844 0 4442 115a nf_nat_ipv6.ko 3187 844 0 4031 fbf nf_nat_ipv4.ko after: text data bss dec hex filename 22948 1612 4136 28696 7018 nf_nat.ko ... with ipv4/v6 nat now provided directly via nf_nat.ko. Also changes: ret = nf_nat_ipv4_fn(priv, skb, state); if (ret != NF_DROP && ret != NF_STOLEN && into if (ret != NF_ACCEPT) return ret; everywhere. The nat hooks never should return anything other than ACCEPT or DROP (and the latter only in rare error cases). The original code uses multi-line ANDing including assignment-in-if: if (ret != NF_DROP && ret != NF_STOLEN && !(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) && (ct = nf_ct_get(skb, &ctinfo)) != NULL) { I removed this while moving, breaking those in separate conditionals and moving the assignments into extra lines. checkpatch still generates some warnings: 1. Overly long lines (of moved code). Breaking them is even more ugly. so I kept this as-is. 2. use of extern function declarations in a .c file. This is necessary evil, we must call nf_nat_l3proto_register() from the nat core now. All l3proto related functions are removed later in this series, those prototypes are then removed as well. v2: keep empty nf_nat_ipv6_csum_update stub for CONFIG_IPV6=n case. v3: remove IS_ENABLED(NF_NAT_IPV4/6) tests, NF_NAT_IPVx toggles are removed here. v4: also get rid of the assignments in conditionals. Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
2019-02-20 01:38:21 +09:00
{
nf_nat_unregister_fn(net, ops->pf, ops, ARRAY_SIZE(nf_nat_ipv4_ops));
netfilter: nat: merge nf_nat_ipv4,6 into nat core before: text data bss dec hex filename 16566 1576 4136 22278 5706 nf_nat.ko 3598 844 0 4442 115a nf_nat_ipv6.ko 3187 844 0 4031 fbf nf_nat_ipv4.ko after: text data bss dec hex filename 22948 1612 4136 28696 7018 nf_nat.ko ... with ipv4/v6 nat now provided directly via nf_nat.ko. Also changes: ret = nf_nat_ipv4_fn(priv, skb, state); if (ret != NF_DROP && ret != NF_STOLEN && into if (ret != NF_ACCEPT) return ret; everywhere. The nat hooks never should return anything other than ACCEPT or DROP (and the latter only in rare error cases). The original code uses multi-line ANDing including assignment-in-if: if (ret != NF_DROP && ret != NF_STOLEN && !(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) && (ct = nf_ct_get(skb, &ctinfo)) != NULL) { I removed this while moving, breaking those in separate conditionals and moving the assignments into extra lines. checkpatch still generates some warnings: 1. Overly long lines (of moved code). Breaking them is even more ugly. so I kept this as-is. 2. use of extern function declarations in a .c file. This is necessary evil, we must call nf_nat_l3proto_register() from the nat core now. All l3proto related functions are removed later in this series, those prototypes are then removed as well. v2: keep empty nf_nat_ipv6_csum_update stub for CONFIG_IPV6=n case. v3: remove IS_ENABLED(NF_NAT_IPV4/6) tests, NF_NAT_IPVx toggles are removed here. v4: also get rid of the assignments in conditionals. Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
2019-02-20 01:38:21 +09:00
}
EXPORT_SYMBOL_GPL(nf_nat_ipv4_unregister_fn);
netfilter: nat: merge nf_nat_ipv4,6 into nat core before: text data bss dec hex filename 16566 1576 4136 22278 5706 nf_nat.ko 3598 844 0 4442 115a nf_nat_ipv6.ko 3187 844 0 4031 fbf nf_nat_ipv4.ko after: text data bss dec hex filename 22948 1612 4136 28696 7018 nf_nat.ko ... with ipv4/v6 nat now provided directly via nf_nat.ko. Also changes: ret = nf_nat_ipv4_fn(priv, skb, state); if (ret != NF_DROP && ret != NF_STOLEN && into if (ret != NF_ACCEPT) return ret; everywhere. The nat hooks never should return anything other than ACCEPT or DROP (and the latter only in rare error cases). The original code uses multi-line ANDing including assignment-in-if: if (ret != NF_DROP && ret != NF_STOLEN && !(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) && (ct = nf_ct_get(skb, &ctinfo)) != NULL) { I removed this while moving, breaking those in separate conditionals and moving the assignments into extra lines. checkpatch still generates some warnings: 1. Overly long lines (of moved code). Breaking them is even more ugly. so I kept this as-is. 2. use of extern function declarations in a .c file. This is necessary evil, we must call nf_nat_l3proto_register() from the nat core now. All l3proto related functions are removed later in this series, those prototypes are then removed as well. v2: keep empty nf_nat_ipv6_csum_update stub for CONFIG_IPV6=n case. v3: remove IS_ENABLED(NF_NAT_IPV4/6) tests, NF_NAT_IPVx toggles are removed here. v4: also get rid of the assignments in conditionals. Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
2019-02-20 01:38:21 +09:00
#if IS_ENABLED(CONFIG_IPV6)
int nf_nat_icmpv6_reply_translation(struct sk_buff *skb,
struct nf_conn *ct,
enum ip_conntrack_info ctinfo,
unsigned int hooknum,
unsigned int hdrlen)
{
struct {
struct icmp6hdr icmp6;
struct ipv6hdr ip6;
} *inside;
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
enum nf_nat_manip_type manip = HOOK2MANIP(hooknum);
struct nf_conntrack_tuple target;
unsigned long statusbit;
WARN_ON(ctinfo != IP_CT_RELATED && ctinfo != IP_CT_RELATED_REPLY);
if (skb_ensure_writable(skb, hdrlen + sizeof(*inside)))
netfilter: nat: merge nf_nat_ipv4,6 into nat core before: text data bss dec hex filename 16566 1576 4136 22278 5706 nf_nat.ko 3598 844 0 4442 115a nf_nat_ipv6.ko 3187 844 0 4031 fbf nf_nat_ipv4.ko after: text data bss dec hex filename 22948 1612 4136 28696 7018 nf_nat.ko ... with ipv4/v6 nat now provided directly via nf_nat.ko. Also changes: ret = nf_nat_ipv4_fn(priv, skb, state); if (ret != NF_DROP && ret != NF_STOLEN && into if (ret != NF_ACCEPT) return ret; everywhere. The nat hooks never should return anything other than ACCEPT or DROP (and the latter only in rare error cases). The original code uses multi-line ANDing including assignment-in-if: if (ret != NF_DROP && ret != NF_STOLEN && !(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) && (ct = nf_ct_get(skb, &ctinfo)) != NULL) { I removed this while moving, breaking those in separate conditionals and moving the assignments into extra lines. checkpatch still generates some warnings: 1. Overly long lines (of moved code). Breaking them is even more ugly. so I kept this as-is. 2. use of extern function declarations in a .c file. This is necessary evil, we must call nf_nat_l3proto_register() from the nat core now. All l3proto related functions are removed later in this series, those prototypes are then removed as well. v2: keep empty nf_nat_ipv6_csum_update stub for CONFIG_IPV6=n case. v3: remove IS_ENABLED(NF_NAT_IPV4/6) tests, NF_NAT_IPVx toggles are removed here. v4: also get rid of the assignments in conditionals. Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
2019-02-20 01:38:21 +09:00
return 0;
if (nf_ip6_checksum(skb, hooknum, hdrlen, IPPROTO_ICMPV6))
return 0;
inside = (void *)skb->data + hdrlen;
if (inside->icmp6.icmp6_type == NDISC_REDIRECT) {
if ((ct->status & IPS_NAT_DONE_MASK) != IPS_NAT_DONE_MASK)
return 0;
if (ct->status & IPS_NAT_MASK)
return 0;
}
if (manip == NF_NAT_MANIP_SRC)
statusbit = IPS_SRC_NAT;
else
statusbit = IPS_DST_NAT;
/* Invert if this is reply direction */
if (dir == IP_CT_DIR_REPLY)
statusbit ^= IPS_NAT_MASK;
if (!(ct->status & statusbit))
return 1;
if (!nf_nat_ipv6_manip_pkt(skb, hdrlen + sizeof(inside->icmp6),
&ct->tuplehash[!dir].tuple, !manip))
return 0;
if (skb->ip_summed != CHECKSUM_PARTIAL) {
struct ipv6hdr *ipv6h = ipv6_hdr(skb);
inside = (void *)skb->data + hdrlen;
inside->icmp6.icmp6_cksum = 0;
inside->icmp6.icmp6_cksum =
csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr,
skb->len - hdrlen, IPPROTO_ICMPV6,
skb_checksum(skb, hdrlen,
skb->len - hdrlen, 0));
}
nf_ct_invert_tuple(&target, &ct->tuplehash[!dir].tuple);
target.dst.protonum = IPPROTO_ICMPV6;
if (!nf_nat_ipv6_manip_pkt(skb, 0, &target, manip))
return 0;
return 1;
}
EXPORT_SYMBOL_GPL(nf_nat_icmpv6_reply_translation);
static unsigned int
nf_nat_ipv6_fn(void *priv, struct sk_buff *skb,
const struct nf_hook_state *state)
{
struct nf_conn *ct;
enum ip_conntrack_info ctinfo;
__be16 frag_off;
int hdrlen;
u8 nexthdr;
ct = nf_ct_get(skb, &ctinfo);
/* Can't track? It's not due to stress, or conntrack would
* have dropped it. Hence it's the user's responsibilty to
* packet filter it out, or implement conntrack/NAT for that
* protocol. 8) --RR
*/
if (!ct)
return NF_ACCEPT;
if (ctinfo == IP_CT_RELATED || ctinfo == IP_CT_RELATED_REPLY) {
nexthdr = ipv6_hdr(skb)->nexthdr;
hdrlen = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr),
&nexthdr, &frag_off);
if (hdrlen >= 0 && nexthdr == IPPROTO_ICMPV6) {
if (!nf_nat_icmpv6_reply_translation(skb, ct, ctinfo,
state->hook,
hdrlen))
return NF_DROP;
else
return NF_ACCEPT;
}
}
return nf_nat_inet_fn(priv, skb, state);
}
static unsigned int
nf_nat_ipv6_in(void *priv, struct sk_buff *skb,
const struct nf_hook_state *state)
{
unsigned int ret;
struct in6_addr daddr = ipv6_hdr(skb)->daddr;
ret = nf_nat_ipv6_fn(priv, skb, state);
if (ret != NF_DROP && ret != NF_STOLEN &&
ipv6_addr_cmp(&daddr, &ipv6_hdr(skb)->daddr))
skb_dst_drop(skb);
return ret;
}
static unsigned int
nf_nat_ipv6_out(void *priv, struct sk_buff *skb,
const struct nf_hook_state *state)
{
#ifdef CONFIG_XFRM
const struct nf_conn *ct;
enum ip_conntrack_info ctinfo;
int err;
#endif
unsigned int ret;
ret = nf_nat_ipv6_fn(priv, skb, state);
#ifdef CONFIG_XFRM
if (ret != NF_ACCEPT)
return ret;
if (IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED)
return ret;
ct = nf_ct_get(skb, &ctinfo);
if (ct) {
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.src.u3,
&ct->tuplehash[!dir].tuple.dst.u3) ||
(ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMPV6 &&
ct->tuplehash[dir].tuple.src.u.all !=
ct->tuplehash[!dir].tuple.dst.u.all)) {
err = nf_xfrm_me_harder(state->net, skb, AF_INET6);
if (err < 0)
ret = NF_DROP_ERR(err);
}
}
#endif
return ret;
}
static unsigned int
nf_nat_ipv6_local_fn(void *priv, struct sk_buff *skb,
const struct nf_hook_state *state)
{
const struct nf_conn *ct;
enum ip_conntrack_info ctinfo;
unsigned int ret;
int err;
ret = nf_nat_ipv6_fn(priv, skb, state);
if (ret != NF_ACCEPT)
return ret;
ct = nf_ct_get(skb, &ctinfo);
if (ct) {
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.dst.u3,
&ct->tuplehash[!dir].tuple.src.u3)) {
netfilter: use actual socket sk rather than skb sk when routing harder [ Upstream commit 46d6c5ae953cc0be38efd0e469284df7c4328cf8 ] If netfilter changes the packet mark when mangling, the packet is rerouted using the route_me_harder set of functions. Prior to this commit, there's one big difference between route_me_harder and the ordinary initial routing functions, described in the comment above __ip_queue_xmit(): /* Note: skb->sk can be different from sk, in case of tunnels */ int __ip_queue_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl, That function goes on to correctly make use of sk->sk_bound_dev_if, rather than skb->sk->sk_bound_dev_if. And indeed the comment is true: a tunnel will receive a packet in ndo_start_xmit with an initial skb->sk. It will make some transformations to that packet, and then it will send the encapsulated packet out of a *new* socket. That new socket will basically always have a different sk_bound_dev_if (otherwise there'd be a routing loop). So for the purposes of routing the encapsulated packet, the routing information as it pertains to the socket should come from that socket's sk, rather than the packet's original skb->sk. For that reason __ip_queue_xmit() and related functions all do the right thing. One might argue that all tunnels should just call skb_orphan(skb) before transmitting the encapsulated packet into the new socket. But tunnels do *not* do this -- and this is wisely avoided in skb_scrub_packet() too -- because features like TSQ rely on skb->destructor() being called when that buffer space is truely available again. Calling skb_orphan(skb) too early would result in buffers filling up unnecessarily and accounting info being all wrong. Instead, additional routing must take into account the new sk, just as __ip_queue_xmit() notes. So, this commit addresses the problem by fishing the correct sk out of state->sk -- it's already set properly in the call to nf_hook() in __ip_local_out(), which receives the sk as part of its normal functionality. So we make sure to plumb state->sk through the various route_me_harder functions, and then make correct use of it following the example of __ip_queue_xmit(). Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com> Reviewed-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org> Signed-off-by: Sasha Levin <sashal@kernel.org>
2020-10-29 11:56:06 +09:00
err = nf_ip6_route_me_harder(state->net, state->sk, skb);
netfilter: nat: merge nf_nat_ipv4,6 into nat core before: text data bss dec hex filename 16566 1576 4136 22278 5706 nf_nat.ko 3598 844 0 4442 115a nf_nat_ipv6.ko 3187 844 0 4031 fbf nf_nat_ipv4.ko after: text data bss dec hex filename 22948 1612 4136 28696 7018 nf_nat.ko ... with ipv4/v6 nat now provided directly via nf_nat.ko. Also changes: ret = nf_nat_ipv4_fn(priv, skb, state); if (ret != NF_DROP && ret != NF_STOLEN && into if (ret != NF_ACCEPT) return ret; everywhere. The nat hooks never should return anything other than ACCEPT or DROP (and the latter only in rare error cases). The original code uses multi-line ANDing including assignment-in-if: if (ret != NF_DROP && ret != NF_STOLEN && !(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) && (ct = nf_ct_get(skb, &ctinfo)) != NULL) { I removed this while moving, breaking those in separate conditionals and moving the assignments into extra lines. checkpatch still generates some warnings: 1. Overly long lines (of moved code). Breaking them is even more ugly. so I kept this as-is. 2. use of extern function declarations in a .c file. This is necessary evil, we must call nf_nat_l3proto_register() from the nat core now. All l3proto related functions are removed later in this series, those prototypes are then removed as well. v2: keep empty nf_nat_ipv6_csum_update stub for CONFIG_IPV6=n case. v3: remove IS_ENABLED(NF_NAT_IPV4/6) tests, NF_NAT_IPVx toggles are removed here. v4: also get rid of the assignments in conditionals. Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
2019-02-20 01:38:21 +09:00
if (err < 0)
ret = NF_DROP_ERR(err);
}
#ifdef CONFIG_XFRM
else if (!(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) &&
ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMPV6 &&
ct->tuplehash[dir].tuple.dst.u.all !=
ct->tuplehash[!dir].tuple.src.u.all) {
err = nf_xfrm_me_harder(state->net, skb, AF_INET6);
if (err < 0)
ret = NF_DROP_ERR(err);
}
#endif
}
return ret;
}
static const struct nf_hook_ops nf_nat_ipv6_ops[] = {
netfilter: nat: merge nf_nat_ipv4,6 into nat core before: text data bss dec hex filename 16566 1576 4136 22278 5706 nf_nat.ko 3598 844 0 4442 115a nf_nat_ipv6.ko 3187 844 0 4031 fbf nf_nat_ipv4.ko after: text data bss dec hex filename 22948 1612 4136 28696 7018 nf_nat.ko ... with ipv4/v6 nat now provided directly via nf_nat.ko. Also changes: ret = nf_nat_ipv4_fn(priv, skb, state); if (ret != NF_DROP && ret != NF_STOLEN && into if (ret != NF_ACCEPT) return ret; everywhere. The nat hooks never should return anything other than ACCEPT or DROP (and the latter only in rare error cases). The original code uses multi-line ANDing including assignment-in-if: if (ret != NF_DROP && ret != NF_STOLEN && !(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) && (ct = nf_ct_get(skb, &ctinfo)) != NULL) { I removed this while moving, breaking those in separate conditionals and moving the assignments into extra lines. checkpatch still generates some warnings: 1. Overly long lines (of moved code). Breaking them is even more ugly. so I kept this as-is. 2. use of extern function declarations in a .c file. This is necessary evil, we must call nf_nat_l3proto_register() from the nat core now. All l3proto related functions are removed later in this series, those prototypes are then removed as well. v2: keep empty nf_nat_ipv6_csum_update stub for CONFIG_IPV6=n case. v3: remove IS_ENABLED(NF_NAT_IPV4/6) tests, NF_NAT_IPVx toggles are removed here. v4: also get rid of the assignments in conditionals. Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
2019-02-20 01:38:21 +09:00
/* Before packet filtering, change destination */
{
.hook = nf_nat_ipv6_in,
.pf = NFPROTO_IPV6,
.hooknum = NF_INET_PRE_ROUTING,
.priority = NF_IP6_PRI_NAT_DST,
},
/* After packet filtering, change source */
{
.hook = nf_nat_ipv6_out,
.pf = NFPROTO_IPV6,
.hooknum = NF_INET_POST_ROUTING,
.priority = NF_IP6_PRI_NAT_SRC,
},
/* Before packet filtering, change destination */
{
.hook = nf_nat_ipv6_local_fn,
.pf = NFPROTO_IPV6,
.hooknum = NF_INET_LOCAL_OUT,
.priority = NF_IP6_PRI_NAT_DST,
},
/* After packet filtering, change source */
{
.hook = nf_nat_ipv6_fn,
.pf = NFPROTO_IPV6,
.hooknum = NF_INET_LOCAL_IN,
.priority = NF_IP6_PRI_NAT_SRC,
},
};
int nf_nat_ipv6_register_fn(struct net *net, const struct nf_hook_ops *ops)
netfilter: nat: merge nf_nat_ipv4,6 into nat core before: text data bss dec hex filename 16566 1576 4136 22278 5706 nf_nat.ko 3598 844 0 4442 115a nf_nat_ipv6.ko 3187 844 0 4031 fbf nf_nat_ipv4.ko after: text data bss dec hex filename 22948 1612 4136 28696 7018 nf_nat.ko ... with ipv4/v6 nat now provided directly via nf_nat.ko. Also changes: ret = nf_nat_ipv4_fn(priv, skb, state); if (ret != NF_DROP && ret != NF_STOLEN && into if (ret != NF_ACCEPT) return ret; everywhere. The nat hooks never should return anything other than ACCEPT or DROP (and the latter only in rare error cases). The original code uses multi-line ANDing including assignment-in-if: if (ret != NF_DROP && ret != NF_STOLEN && !(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) && (ct = nf_ct_get(skb, &ctinfo)) != NULL) { I removed this while moving, breaking those in separate conditionals and moving the assignments into extra lines. checkpatch still generates some warnings: 1. Overly long lines (of moved code). Breaking them is even more ugly. so I kept this as-is. 2. use of extern function declarations in a .c file. This is necessary evil, we must call nf_nat_l3proto_register() from the nat core now. All l3proto related functions are removed later in this series, those prototypes are then removed as well. v2: keep empty nf_nat_ipv6_csum_update stub for CONFIG_IPV6=n case. v3: remove IS_ENABLED(NF_NAT_IPV4/6) tests, NF_NAT_IPVx toggles are removed here. v4: also get rid of the assignments in conditionals. Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
2019-02-20 01:38:21 +09:00
{
return nf_nat_register_fn(net, ops->pf, ops, nf_nat_ipv6_ops,
netfilter: nat: merge nf_nat_ipv4,6 into nat core before: text data bss dec hex filename 16566 1576 4136 22278 5706 nf_nat.ko 3598 844 0 4442 115a nf_nat_ipv6.ko 3187 844 0 4031 fbf nf_nat_ipv4.ko after: text data bss dec hex filename 22948 1612 4136 28696 7018 nf_nat.ko ... with ipv4/v6 nat now provided directly via nf_nat.ko. Also changes: ret = nf_nat_ipv4_fn(priv, skb, state); if (ret != NF_DROP && ret != NF_STOLEN && into if (ret != NF_ACCEPT) return ret; everywhere. The nat hooks never should return anything other than ACCEPT or DROP (and the latter only in rare error cases). The original code uses multi-line ANDing including assignment-in-if: if (ret != NF_DROP && ret != NF_STOLEN && !(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) && (ct = nf_ct_get(skb, &ctinfo)) != NULL) { I removed this while moving, breaking those in separate conditionals and moving the assignments into extra lines. checkpatch still generates some warnings: 1. Overly long lines (of moved code). Breaking them is even more ugly. so I kept this as-is. 2. use of extern function declarations in a .c file. This is necessary evil, we must call nf_nat_l3proto_register() from the nat core now. All l3proto related functions are removed later in this series, those prototypes are then removed as well. v2: keep empty nf_nat_ipv6_csum_update stub for CONFIG_IPV6=n case. v3: remove IS_ENABLED(NF_NAT_IPV4/6) tests, NF_NAT_IPVx toggles are removed here. v4: also get rid of the assignments in conditionals. Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
2019-02-20 01:38:21 +09:00
ARRAY_SIZE(nf_nat_ipv6_ops));
}
EXPORT_SYMBOL_GPL(nf_nat_ipv6_register_fn);
netfilter: nat: merge nf_nat_ipv4,6 into nat core before: text data bss dec hex filename 16566 1576 4136 22278 5706 nf_nat.ko 3598 844 0 4442 115a nf_nat_ipv6.ko 3187 844 0 4031 fbf nf_nat_ipv4.ko after: text data bss dec hex filename 22948 1612 4136 28696 7018 nf_nat.ko ... with ipv4/v6 nat now provided directly via nf_nat.ko. Also changes: ret = nf_nat_ipv4_fn(priv, skb, state); if (ret != NF_DROP && ret != NF_STOLEN && into if (ret != NF_ACCEPT) return ret; everywhere. The nat hooks never should return anything other than ACCEPT or DROP (and the latter only in rare error cases). The original code uses multi-line ANDing including assignment-in-if: if (ret != NF_DROP && ret != NF_STOLEN && !(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) && (ct = nf_ct_get(skb, &ctinfo)) != NULL) { I removed this while moving, breaking those in separate conditionals and moving the assignments into extra lines. checkpatch still generates some warnings: 1. Overly long lines (of moved code). Breaking them is even more ugly. so I kept this as-is. 2. use of extern function declarations in a .c file. This is necessary evil, we must call nf_nat_l3proto_register() from the nat core now. All l3proto related functions are removed later in this series, those prototypes are then removed as well. v2: keep empty nf_nat_ipv6_csum_update stub for CONFIG_IPV6=n case. v3: remove IS_ENABLED(NF_NAT_IPV4/6) tests, NF_NAT_IPVx toggles are removed here. v4: also get rid of the assignments in conditionals. Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
2019-02-20 01:38:21 +09:00
void nf_nat_ipv6_unregister_fn(struct net *net, const struct nf_hook_ops *ops)
netfilter: nat: merge nf_nat_ipv4,6 into nat core before: text data bss dec hex filename 16566 1576 4136 22278 5706 nf_nat.ko 3598 844 0 4442 115a nf_nat_ipv6.ko 3187 844 0 4031 fbf nf_nat_ipv4.ko after: text data bss dec hex filename 22948 1612 4136 28696 7018 nf_nat.ko ... with ipv4/v6 nat now provided directly via nf_nat.ko. Also changes: ret = nf_nat_ipv4_fn(priv, skb, state); if (ret != NF_DROP && ret != NF_STOLEN && into if (ret != NF_ACCEPT) return ret; everywhere. The nat hooks never should return anything other than ACCEPT or DROP (and the latter only in rare error cases). The original code uses multi-line ANDing including assignment-in-if: if (ret != NF_DROP && ret != NF_STOLEN && !(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) && (ct = nf_ct_get(skb, &ctinfo)) != NULL) { I removed this while moving, breaking those in separate conditionals and moving the assignments into extra lines. checkpatch still generates some warnings: 1. Overly long lines (of moved code). Breaking them is even more ugly. so I kept this as-is. 2. use of extern function declarations in a .c file. This is necessary evil, we must call nf_nat_l3proto_register() from the nat core now. All l3proto related functions are removed later in this series, those prototypes are then removed as well. v2: keep empty nf_nat_ipv6_csum_update stub for CONFIG_IPV6=n case. v3: remove IS_ENABLED(NF_NAT_IPV4/6) tests, NF_NAT_IPVx toggles are removed here. v4: also get rid of the assignments in conditionals. Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
2019-02-20 01:38:21 +09:00
{
nf_nat_unregister_fn(net, ops->pf, ops, ARRAY_SIZE(nf_nat_ipv6_ops));
netfilter: nat: merge nf_nat_ipv4,6 into nat core before: text data bss dec hex filename 16566 1576 4136 22278 5706 nf_nat.ko 3598 844 0 4442 115a nf_nat_ipv6.ko 3187 844 0 4031 fbf nf_nat_ipv4.ko after: text data bss dec hex filename 22948 1612 4136 28696 7018 nf_nat.ko ... with ipv4/v6 nat now provided directly via nf_nat.ko. Also changes: ret = nf_nat_ipv4_fn(priv, skb, state); if (ret != NF_DROP && ret != NF_STOLEN && into if (ret != NF_ACCEPT) return ret; everywhere. The nat hooks never should return anything other than ACCEPT or DROP (and the latter only in rare error cases). The original code uses multi-line ANDing including assignment-in-if: if (ret != NF_DROP && ret != NF_STOLEN && !(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) && (ct = nf_ct_get(skb, &ctinfo)) != NULL) { I removed this while moving, breaking those in separate conditionals and moving the assignments into extra lines. checkpatch still generates some warnings: 1. Overly long lines (of moved code). Breaking them is even more ugly. so I kept this as-is. 2. use of extern function declarations in a .c file. This is necessary evil, we must call nf_nat_l3proto_register() from the nat core now. All l3proto related functions are removed later in this series, those prototypes are then removed as well. v2: keep empty nf_nat_ipv6_csum_update stub for CONFIG_IPV6=n case. v3: remove IS_ENABLED(NF_NAT_IPV4/6) tests, NF_NAT_IPVx toggles are removed here. v4: also get rid of the assignments in conditionals. Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
2019-02-20 01:38:21 +09:00
}
EXPORT_SYMBOL_GPL(nf_nat_ipv6_unregister_fn);
netfilter: nat: merge nf_nat_ipv4,6 into nat core before: text data bss dec hex filename 16566 1576 4136 22278 5706 nf_nat.ko 3598 844 0 4442 115a nf_nat_ipv6.ko 3187 844 0 4031 fbf nf_nat_ipv4.ko after: text data bss dec hex filename 22948 1612 4136 28696 7018 nf_nat.ko ... with ipv4/v6 nat now provided directly via nf_nat.ko. Also changes: ret = nf_nat_ipv4_fn(priv, skb, state); if (ret != NF_DROP && ret != NF_STOLEN && into if (ret != NF_ACCEPT) return ret; everywhere. The nat hooks never should return anything other than ACCEPT or DROP (and the latter only in rare error cases). The original code uses multi-line ANDing including assignment-in-if: if (ret != NF_DROP && ret != NF_STOLEN && !(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) && (ct = nf_ct_get(skb, &ctinfo)) != NULL) { I removed this while moving, breaking those in separate conditionals and moving the assignments into extra lines. checkpatch still generates some warnings: 1. Overly long lines (of moved code). Breaking them is even more ugly. so I kept this as-is. 2. use of extern function declarations in a .c file. This is necessary evil, we must call nf_nat_l3proto_register() from the nat core now. All l3proto related functions are removed later in this series, those prototypes are then removed as well. v2: keep empty nf_nat_ipv6_csum_update stub for CONFIG_IPV6=n case. v3: remove IS_ENABLED(NF_NAT_IPV4/6) tests, NF_NAT_IPVx toggles are removed here. v4: also get rid of the assignments in conditionals. Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
2019-02-20 01:38:21 +09:00
#endif /* CONFIG_IPV6 */
#if defined(CONFIG_NF_TABLES_INET) && IS_ENABLED(CONFIG_NFT_NAT)
int nf_nat_inet_register_fn(struct net *net, const struct nf_hook_ops *ops)
{
int ret;
if (WARN_ON_ONCE(ops->pf != NFPROTO_INET))
return -EINVAL;
ret = nf_nat_register_fn(net, NFPROTO_IPV6, ops, nf_nat_ipv6_ops,
ARRAY_SIZE(nf_nat_ipv6_ops));
if (ret)
return ret;
ret = nf_nat_register_fn(net, NFPROTO_IPV4, ops, nf_nat_ipv4_ops,
ARRAY_SIZE(nf_nat_ipv4_ops));
if (ret)
netfilter: nat: fix error handling upon registering inet hook commit b4faef1739dd1f3b3981b8bf173a2266ea86b1eb upstream. A case of warning was reported by syzbot. ------------[ cut here ]------------ WARNING: CPU: 0 PID: 19934 at net/netfilter/nf_nat_core.c:1106 nf_nat_unregister_fn+0x532/0x5c0 net/netfilter/nf_nat_core.c:1106 Kernel panic - not syncing: panic_on_warn set ... CPU: 0 PID: 19934 Comm: syz-executor.5 Not tainted 5.6.0-syzkaller #0 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011 Call Trace: __dump_stack lib/dump_stack.c:77 [inline] dump_stack+0x188/0x20d lib/dump_stack.c:118 panic+0x2e3/0x75c kernel/panic.c:221 __warn.cold+0x2f/0x35 kernel/panic.c:582 report_bug+0x27b/0x2f0 lib/bug.c:195 fixup_bug arch/x86/kernel/traps.c:175 [inline] fixup_bug arch/x86/kernel/traps.c:170 [inline] do_error_trap+0x12b/0x220 arch/x86/kernel/traps.c:267 do_invalid_op+0x32/0x40 arch/x86/kernel/traps.c:286 invalid_op+0x23/0x30 arch/x86/entry/entry_64.S:1027 RIP: 0010:nf_nat_unregister_fn+0x532/0x5c0 net/netfilter/nf_nat_core.c:1106 Code: ff df 48 c1 ea 03 80 3c 02 00 75 75 48 8b 44 24 10 4c 89 ef 48 c7 00 00 00 00 00 e8 e8 f8 53 fb e9 4d fe ff ff e8 ee 9c 16 fb <0f> 0b e9 41 fe ff ff e8 e2 45 54 fb e9 b5 fd ff ff 48 8b 7c 24 20 RSP: 0018:ffffc90005487208 EFLAGS: 00010246 RAX: 0000000000040000 RBX: 0000000000000004 RCX: ffffc9001444a000 RDX: 0000000000040000 RSI: ffffffff865c94a2 RDI: 0000000000000005 RBP: ffff88808b5cf000 R08: ffff8880a2620140 R09: fffffbfff14bcd79 R10: ffffc90005487208 R11: fffffbfff14bcd78 R12: 0000000000000000 R13: 0000000000000001 R14: 0000000000000001 R15: 0000000000000000 nf_nat_ipv6_unregister_fn net/netfilter/nf_nat_proto.c:1017 [inline] nf_nat_inet_register_fn net/netfilter/nf_nat_proto.c:1038 [inline] nf_nat_inet_register_fn+0xfc/0x140 net/netfilter/nf_nat_proto.c:1023 nf_tables_register_hook net/netfilter/nf_tables_api.c:224 [inline] nf_tables_addchain.constprop.0+0x82e/0x13c0 net/netfilter/nf_tables_api.c:1981 nf_tables_newchain+0xf68/0x16a0 net/netfilter/nf_tables_api.c:2235 nfnetlink_rcv_batch+0x83a/0x1610 net/netfilter/nfnetlink.c:433 nfnetlink_rcv_skb_batch net/netfilter/nfnetlink.c:543 [inline] nfnetlink_rcv+0x3af/0x420 net/netfilter/nfnetlink.c:561 netlink_unicast_kernel net/netlink/af_netlink.c:1303 [inline] netlink_unicast+0x537/0x740 net/netlink/af_netlink.c:1329 netlink_sendmsg+0x882/0xe10 net/netlink/af_netlink.c:1918 sock_sendmsg_nosec net/socket.c:652 [inline] sock_sendmsg+0xcf/0x120 net/socket.c:672 ____sys_sendmsg+0x6bf/0x7e0 net/socket.c:2362 ___sys_sendmsg+0x100/0x170 net/socket.c:2416 __sys_sendmsg+0xec/0x1b0 net/socket.c:2449 do_syscall_64+0xf6/0x7d0 arch/x86/entry/common.c:295 entry_SYSCALL_64_after_hwframe+0x49/0xb3 and to quiesce it, unregister NFPROTO_IPV6 hook instead of NFPROTO_INET in case of failing to register NFPROTO_IPV4 hook. Reported-by: syzbot <syzbot+33e06702fd6cffc24c40@syzkaller.appspotmail.com> Fixes: d164385ec572 ("netfilter: nat: add inet family nat support") Cc: Florian Westphal <fw@strlen.de> Cc: Stefano Brivio <sbrivio@redhat.com> Signed-off-by: Hillf Danton <hdanton@sina.com> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2020-04-18 17:28:32 +09:00
nf_nat_unregister_fn(net, NFPROTO_IPV6, ops,
ARRAY_SIZE(nf_nat_ipv6_ops));
return ret;
}
EXPORT_SYMBOL_GPL(nf_nat_inet_register_fn);
void nf_nat_inet_unregister_fn(struct net *net, const struct nf_hook_ops *ops)
{
nf_nat_unregister_fn(net, NFPROTO_IPV4, ops, ARRAY_SIZE(nf_nat_ipv4_ops));
nf_nat_unregister_fn(net, NFPROTO_IPV6, ops, ARRAY_SIZE(nf_nat_ipv6_ops));
}
EXPORT_SYMBOL_GPL(nf_nat_inet_unregister_fn);
#endif /* NFT INET NAT */