Skip to content

Commit 4ccbd9c

Browse files
Guillaume Naultgregkh
authored andcommitted
netfilter: nat: never update the UDP checksum when it's 0
commit ea64d8d 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: 5b1158e ("[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>
1 parent 634c950 commit 4ccbd9c

1 file changed

Lines changed: 1 addition & 3 deletions

File tree

net/netfilter/nf_nat_proto.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,15 +68,13 @@ static bool udp_manip_pkt(struct sk_buff *skb,
6868
enum nf_nat_manip_type maniptype)
6969
{
7070
struct udphdr *hdr;
71-
bool do_csum;
7271

7372
if (skb_ensure_writable(skb, hdroff + sizeof(*hdr)))
7473
return false;
7574

7675
hdr = (struct udphdr *)(skb->data + hdroff);
77-
do_csum = hdr->check || skb->ip_summed == CHECKSUM_PARTIAL;
76+
__udp_manip_pkt(skb, iphdroff, hdr, tuple, maniptype, !!hdr->check);
7877

79-
__udp_manip_pkt(skb, iphdroff, hdr, tuple, maniptype, do_csum);
8078
return true;
8179
}
8280

0 commit comments

Comments
 (0)