2006-12-03 06:07:13 +00:00
|
|
|
/* (C) 1999-2001 Paul `Rusty' Russell
|
|
|
|
* (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
|
|
* published by the Free Software Foundation.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/types.h>
|
2011-07-15 15:47:34 +00:00
|
|
|
#include <linux/export.h>
|
2006-12-03 06:07:13 +00:00
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/udp.h>
|
|
|
|
|
|
|
|
#include <linux/netfilter.h>
|
|
|
|
#include <net/netfilter/nf_nat.h>
|
|
|
|
#include <net/netfilter/nf_nat_core.h>
|
2012-08-26 17:14:06 +00:00
|
|
|
#include <net/netfilter/nf_nat_l3proto.h>
|
|
|
|
#include <net/netfilter/nf_nat_l4proto.h>
|
2006-12-03 06:07:13 +00:00
|
|
|
|
2010-08-02 15:20:54 +00:00
|
|
|
static void
|
2012-08-26 17:14:06 +00:00
|
|
|
udp_unique_tuple(const struct nf_nat_l3proto *l3proto,
|
|
|
|
struct nf_conntrack_tuple *tuple,
|
2018-04-04 13:38:22 +00:00
|
|
|
const struct nf_nat_range2 *range,
|
2006-12-03 06:07:13 +00:00
|
|
|
enum nf_nat_manip_type maniptype,
|
|
|
|
const struct nf_conn *ct)
|
|
|
|
{
|
2018-11-15 09:22:59 +00:00
|
|
|
nf_nat_l4proto_unique_tuple(l3proto, tuple, range, maniptype, ct);
|
2006-12-03 06:07:13 +00:00
|
|
|
}
|
|
|
|
|
2016-12-20 20:57:03 +00:00
|
|
|
static void
|
|
|
|
__udp_manip_pkt(struct sk_buff *skb,
|
|
|
|
const struct nf_nat_l3proto *l3proto,
|
|
|
|
unsigned int iphdroff, struct udphdr *hdr,
|
|
|
|
const struct nf_conntrack_tuple *tuple,
|
|
|
|
enum nf_nat_manip_type maniptype, bool do_csum)
|
2006-12-03 06:07:13 +00:00
|
|
|
{
|
|
|
|
__be16 *portptr, newport;
|
|
|
|
|
2011-12-23 12:59:49 +00:00
|
|
|
if (maniptype == NF_NAT_MANIP_SRC) {
|
2012-08-26 17:14:06 +00:00
|
|
|
/* Get rid of src port */
|
2006-12-03 06:07:13 +00:00
|
|
|
newport = tuple->src.u.udp.port;
|
|
|
|
portptr = &hdr->source;
|
|
|
|
} else {
|
2012-08-26 17:14:06 +00:00
|
|
|
/* Get rid of dst port */
|
2006-12-03 06:07:13 +00:00
|
|
|
newport = tuple->dst.u.udp.port;
|
|
|
|
portptr = &hdr->dest;
|
|
|
|
}
|
2016-12-20 20:57:03 +00:00
|
|
|
if (do_csum) {
|
2012-08-26 17:14:06 +00:00
|
|
|
l3proto->csum_update(skb, iphdroff, &hdr->check,
|
|
|
|
tuple, maniptype);
|
2007-11-29 14:17:11 +00:00
|
|
|
inet_proto_csum_replace2(&hdr->check, skb, *portptr, newport,
|
2015-08-17 20:42:25 +00:00
|
|
|
false);
|
2006-12-03 06:07:13 +00:00
|
|
|
if (!hdr->check)
|
|
|
|
hdr->check = CSUM_MANGLED_0;
|
|
|
|
}
|
|
|
|
*portptr = newport;
|
2016-12-20 20:57:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static bool udp_manip_pkt(struct sk_buff *skb,
|
|
|
|
const struct nf_nat_l3proto *l3proto,
|
|
|
|
unsigned int iphdroff, unsigned int hdroff,
|
|
|
|
const struct nf_conntrack_tuple *tuple,
|
|
|
|
enum nf_nat_manip_type maniptype)
|
|
|
|
{
|
|
|
|
struct udphdr *hdr;
|
|
|
|
|
|
|
|
if (!skb_make_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 00:42:19 +00:00
|
|
|
__udp_manip_pkt(skb, l3proto, iphdroff, hdr, tuple, maniptype,
|
|
|
|
!!hdr->check);
|
2016-12-20 20:57:03 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_NF_NAT_PROTO_UDPLITE
|
|
|
|
static bool udplite_manip_pkt(struct sk_buff *skb,
|
|
|
|
const struct nf_nat_l3proto *l3proto,
|
|
|
|
unsigned int iphdroff, unsigned int hdroff,
|
|
|
|
const struct nf_conntrack_tuple *tuple,
|
|
|
|
enum nf_nat_manip_type maniptype)
|
|
|
|
{
|
|
|
|
struct udphdr *hdr;
|
|
|
|
|
|
|
|
if (!skb_make_writable(skb, hdroff + sizeof(*hdr)))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
hdr = (struct udphdr *)(skb->data + hdroff);
|
|
|
|
__udp_manip_pkt(skb, l3proto, iphdroff, hdr, tuple, maniptype, true);
|
2008-04-14 09:15:53 +00:00
|
|
|
return true;
|
2006-12-03 06:07:13 +00:00
|
|
|
}
|
|
|
|
|
2016-12-20 20:57:03 +00:00
|
|
|
static void
|
|
|
|
udplite_unique_tuple(const struct nf_nat_l3proto *l3proto,
|
|
|
|
struct nf_conntrack_tuple *tuple,
|
2018-04-04 13:38:22 +00:00
|
|
|
const struct nf_nat_range2 *range,
|
2016-12-20 20:57:03 +00:00
|
|
|
enum nf_nat_manip_type maniptype,
|
|
|
|
const struct nf_conn *ct)
|
|
|
|
{
|
2018-11-15 09:22:59 +00:00
|
|
|
nf_nat_l4proto_unique_tuple(l3proto, tuple, range, maniptype, ct);
|
2016-12-20 20:57:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const struct nf_nat_l4proto nf_nat_l4proto_udplite = {
|
|
|
|
.l4proto = IPPROTO_UDPLITE,
|
|
|
|
.manip_pkt = udplite_manip_pkt,
|
|
|
|
.in_range = nf_nat_l4proto_in_range,
|
|
|
|
.unique_tuple = udplite_unique_tuple,
|
|
|
|
#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
|
|
|
|
.nlattr_to_range = nf_nat_l4proto_nlattr_to_range,
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
#endif /* CONFIG_NF_NAT_PROTO_UDPLITE */
|
|
|
|
|
2012-08-26 17:14:06 +00:00
|
|
|
const struct nf_nat_l4proto nf_nat_l4proto_udp = {
|
|
|
|
.l4proto = IPPROTO_UDP,
|
2006-12-03 06:07:13 +00:00
|
|
|
.manip_pkt = udp_manip_pkt,
|
2012-08-26 17:14:06 +00:00
|
|
|
.in_range = nf_nat_l4proto_in_range,
|
2006-12-03 06:07:13 +00:00
|
|
|
.unique_tuple = udp_unique_tuple,
|
2014-06-30 01:19:32 +00:00
|
|
|
#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
|
2012-08-26 17:14:06 +00:00
|
|
|
.nlattr_to_range = nf_nat_l4proto_nlattr_to_range,
|
2006-12-03 06:07:13 +00:00
|
|
|
#endif
|
|
|
|
};
|