This is a note to let you know that I've just added the patch titled net: account for current skb length when deciding about UFO to the 4.4-stable tree which can be found at: http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary The filename of the patch is: net-account-for-current-skb-length-when-deciding-about-ufo.patch and it can be found in the queue-4.4 subdirectory. If you, or anyone else, feels it should not be added to the stable tree, please let <stable@xxxxxxxxxxxxxxx> know about it. >From a5cb659bbc1c8644efa0c3138a757a1e432a4880 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Kube=C4=8Dek?= <mkubecek@xxxxxxx> Date: Mon, 19 Jun 2017 13:03:43 +0200 Subject: net: account for current skb length when deciding about UFO From: Michal Kubeček <mkubecek@xxxxxxx> commit a5cb659bbc1c8644efa0c3138a757a1e432a4880 upstream. Our customer encountered stuck NFS writes for blocks starting at specific offsets w.r.t. page boundary caused by networking stack sending packets via UFO enabled device with wrong checksum. The problem can be reproduced by composing a long UDP datagram from multiple parts using MSG_MORE flag: sendto(sd, buff, 1000, MSG_MORE, ...); sendto(sd, buff, 1000, MSG_MORE, ...); sendto(sd, buff, 3000, 0, ...); Assume this packet is to be routed via a device with MTU 1500 and NETIF_F_UFO enabled. When second sendto() gets into __ip_append_data(), this condition is tested (among others) to decide whether to call ip_ufo_append_data(): ((length + fragheaderlen) > mtu) || (skb && skb_is_gso(skb)) At the moment, we already have skb with 1028 bytes of data which is not marked for GSO so that the test is false (fragheaderlen is usually 20). Thus we append second 1000 bytes to this skb without invoking UFO. Third sendto(), however, has sufficient length to trigger the UFO path so that we end up with non-UFO skb followed by a UFO one. Later on, udp_send_skb() uses udp_csum() to calculate the checksum but that assumes all fragments have correct checksum in skb->csum which is not true for UFO fragments. When checking against MTU, we need to add skb->len to length of new segment if we already have a partially filled skb and fragheaderlen only if there isn't one. In the IPv6 case, skb can only be null if this is the first segment so that we have to use headersize (length of the first IPv6 header) rather than fragheaderlen (length of IPv6 header of further fragments) for skb == NULL. Fixes: e89e9cf539a2 ("[IPv4/IPv6]: UFO Scatter-gather approach") Fixes: e4c5e13aa45c ("ipv6: Should use consistent conditional judgement for ip6 fragment between __ip6_append_data and ip6_finish_output") Signed-off-by: Michal Kubecek <mkubecek@xxxxxxx> Acked-by: Vlad Yasevich <vyasevic@xxxxxxxxxx> Signed-off-by: David S. Miller <davem@xxxxxxxxxxxxx> Signed-off-by: Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx> --- net/ipv4/ip_output.c | 2 +- net/ipv6/ip6_output.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -923,7 +923,7 @@ static int __ip_append_data(struct sock cork->length += length; if ((skb && skb_is_gso(skb)) || - (((length + fragheaderlen) > mtu) && + (((length + (skb ? skb->len : fragheaderlen)) > mtu) && (skb_queue_len(queue) <= 1) && (sk->sk_protocol == IPPROTO_UDP) && (rt->dst.dev->features & NETIF_F_UFO) && !rt->dst.header_len && --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -1358,7 +1358,7 @@ emsgsize: cork->length += length; if ((skb && skb_is_gso(skb)) || - (((length + fragheaderlen) > mtu) && + (((length + (skb ? skb->len : headersize)) > mtu) && (skb_queue_len(queue) <= 1) && (sk->sk_protocol == IPPROTO_UDP) && (rt->dst.dev->features & NETIF_F_UFO) && Patches currently in stable-queue which might be from mkubecek@xxxxxxx are queue-4.4/revert-net-account-for-current-skb-length-when-deciding-about-ufo.patch queue-4.4/net-account-for-current-skb-length-when-deciding-about-ufo.patch