From patchwork Mon Feb 4 17:27:54 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Borkmann X-Patchwork-Id: 218001 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id E55AB2C0092 for ; Tue, 5 Feb 2013 04:28:48 +1100 (EST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754019Ab3BDR2q (ORCPT ); Mon, 4 Feb 2013 12:28:46 -0500 Received: from mx1.redhat.com ([209.132.183.28]:44344 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753917Ab3BDR2q (ORCPT ); Mon, 4 Feb 2013 12:28:46 -0500 Received: from int-mx11.intmail.prod.int.phx2.redhat.com (int-mx11.intmail.prod.int.phx2.redhat.com [10.5.11.24]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id r14HRu6c002075 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Mon, 4 Feb 2013 12:27:56 -0500 Received: from localhost (vpn1-5-6.ams2.redhat.com [10.36.5.6]) by int-mx11.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id r14HRsJ0015685; Mon, 4 Feb 2013 12:27:55 -0500 From: Daniel Borkmann To: horms@verge.net.au Cc: mohanreddykv@gmail.com, pablo@netfilter.org, lvs-devel@vger.kernel.org, netfilter-devel@vger.kernel.org, linux-sctp@vger.kernel.org, root Daniel Borkmann Subject: [PATCH net] ipvs: sctp: fix checksumming by adding hardware offload support Date: Mon, 4 Feb 2013 18:27:54 +0100 Message-Id: <916047bfad2e27385c15954a71e9462bb2f828f2.1359997089.git.dborkman@redhat.com> In-Reply-To: References: X-Scanned-By: MIMEDefang 2.68 on 10.5.11.24 Sender: netfilter-devel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netfilter-devel@vger.kernel.org From: root Daniel Borkmann In our test lab, we have a simple SCTP client connecting to a SCTP server via an IPVS load balancer. On some machines, load balancing works, but on others the initial handshake just fails, thus no SCTP connection whatsoever can be established. We observed that the SCTP INIT-ACK handshake reply from the IPVS machine to the client had a correct IP checksum, but corrupt SCTP checksum when forwarded, thus on the client-side the packet was dropped and an intial handshake retriggered until all attempts run into the void. This patch fixes the problem by taking hardware offload features of the NIC into account (which was just ignored here), similar as done in the SCTP checksumming code itself. Also, while at it, the checksum is in little-endian format (as fixed in commit 458f04c: sctp: Clean up sctp checksumming code). Tested by myself. Cc: Venkata Mohan Reddy Cc: Simon Horman Signed-off-by: Daniel Borkmann --- net/netfilter/ipvs/ip_vs_proto_sctp.c | 41 ++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c index 746048b..dc41622 100644 --- a/net/netfilter/ipvs/ip_vs_proto_sctp.c +++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c @@ -61,14 +61,33 @@ sctp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd, return 1; } +static void sctp_nat_csum(struct sk_buff *skb, sctp_sctphdr_t *sctph, + unsigned int sctphoff) +{ + struct sk_buff *iter; + + /* Calculate the checksum */ + if (!(skb->dev->features & NETIF_F_SCTP_CSUM)) { + __u32 crc32 = sctp_start_cksum((__u8 *)sctph, + skb_headlen(skb) - sctphoff); + skb_walk_frags(skb, iter) + crc32 = sctp_update_cksum((u8 *) iter->data, + skb_headlen(iter), crc32); + sctph->checksum = sctp_end_cksum(crc32); + } else { + /* no need to seed pseudo checksum for SCTP */ + skb->ip_summed = CHECKSUM_PARTIAL; + skb->csum_start = (skb_transport_header(skb) - skb->head); + skb->csum_offset = offsetof(struct sctphdr, checksum); + } +} + static int sctp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, struct ip_vs_conn *cp, struct ip_vs_iphdr *iph) { sctp_sctphdr_t *sctph; unsigned int sctphoff = iph->len; - struct sk_buff *iter; - __be32 crc32; #ifdef CONFIG_IP_VS_IPV6 if (cp->af == AF_INET6 && iph->fragoffs) @@ -92,13 +111,7 @@ sctp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, sctph = (void *) skb_network_header(skb) + sctphoff; sctph->source = cp->vport; - /* Calculate the checksum */ - crc32 = sctp_start_cksum((u8 *) sctph, skb_headlen(skb) - sctphoff); - skb_walk_frags(skb, iter) - crc32 = sctp_update_cksum((u8 *) iter->data, skb_headlen(iter), - crc32); - crc32 = sctp_end_cksum(crc32); - sctph->checksum = crc32; + sctp_nat_csum(skb, sctph, sctphoff); return 1; } @@ -109,8 +122,6 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, { sctp_sctphdr_t *sctph; unsigned int sctphoff = iph->len; - struct sk_buff *iter; - __be32 crc32; #ifdef CONFIG_IP_VS_IPV6 if (cp->af == AF_INET6 && iph->fragoffs) @@ -134,13 +145,7 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, sctph = (void *) skb_network_header(skb) + sctphoff; sctph->dest = cp->dport; - /* Calculate the checksum */ - crc32 = sctp_start_cksum((u8 *) sctph, skb_headlen(skb) - sctphoff); - skb_walk_frags(skb, iter) - crc32 = sctp_update_cksum((u8 *) iter->data, skb_headlen(iter), - crc32); - crc32 = sctp_end_cksum(crc32); - sctph->checksum = crc32; + sctp_nat_csum(skb, sctph, sctphoff); return 1; }