diff options
author | Daniel Borkmann <dborkman@redhat.com> | 2013-10-30 06:50:52 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2013-11-03 23:04:57 -0500 |
commit | e6d8b64b34aa8a9fe39609bc2db8a243b0331ceb (patch) | |
tree | de3bbb3dc4e08fd17fcde46aedb239d274e45d6b /net | |
parent | 2817a336d4d533fb8b68719723cd60ea7dd7c09e (diff) |
net: sctp: fix and consolidate SCTP checksumming code
This fixes an outstanding bug found through IPVS, where SCTP packets
with skb->data_len > 0 (non-linearized) and empty frag_list, but data
accumulated in frags[] member, are forwarded with incorrect checksum
letting SCTP initial handshake fail on some systems. Linearizing each
SCTP skb in IPVS to prevent that would not be a good solution as
this leads to an additional and unnecessary performance penalty on
the load-balancer itself for no good reason (as we actually only want
to update the checksum, and can do that in a different/better way
presented here).
The actual problem is elsewhere, namely, that SCTP's checksumming
in sctp_compute_cksum() does not take frags[] into account like
skb_checksum() does. So while we are fixing this up, we better reuse
the existing code that we have anyway in __skb_checksum() and use it
for walking through the data doing checksumming. This will not only
fix this issue, but also consolidates some SCTP code with core
sk_buff code, bringing it closer together and removing respectively
avoiding reimplementation of skb_checksum() for no good reason.
As crc32c() can use hardware implementation within the crypto layer,
we leave that intact (it wraps around / falls back to e.g. slice-by-8
algorithm in __crc32c_le() otherwise); plus use the __crc32c_le_combine()
combinator for crc32c blocks.
Also, we remove all other SCTP checksumming code, so that we only
have to use sctp_compute_cksum() from now on; for doing that, we need
to transform SCTP checkumming in output path slightly, and can leave
the rest intact.
Signed-off-by: Daniel Borkmann <dborkman@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/sctp/output.c | 9 |
1 files changed, 1 insertions, 8 deletions
diff --git a/net/sctp/output.c b/net/sctp/output.c index 319137340d15..e650978daf27 100644 --- a/net/sctp/output.c +++ b/net/sctp/output.c | |||
@@ -390,7 +390,6 @@ int sctp_packet_transmit(struct sctp_packet *packet) | |||
390 | __u8 has_data = 0; | 390 | __u8 has_data = 0; |
391 | struct dst_entry *dst = tp->dst; | 391 | struct dst_entry *dst = tp->dst; |
392 | unsigned char *auth = NULL; /* pointer to auth in skb data */ | 392 | unsigned char *auth = NULL; /* pointer to auth in skb data */ |
393 | __u32 cksum_buf_len = sizeof(struct sctphdr); | ||
394 | 393 | ||
395 | pr_debug("%s: packet:%p\n", __func__, packet); | 394 | pr_debug("%s: packet:%p\n", __func__, packet); |
396 | 395 | ||
@@ -493,7 +492,6 @@ int sctp_packet_transmit(struct sctp_packet *packet) | |||
493 | if (chunk == packet->auth) | 492 | if (chunk == packet->auth) |
494 | auth = skb_tail_pointer(nskb); | 493 | auth = skb_tail_pointer(nskb); |
495 | 494 | ||
496 | cksum_buf_len += chunk->skb->len; | ||
497 | memcpy(skb_put(nskb, chunk->skb->len), | 495 | memcpy(skb_put(nskb, chunk->skb->len), |
498 | chunk->skb->data, chunk->skb->len); | 496 | chunk->skb->data, chunk->skb->len); |
499 | 497 | ||
@@ -538,12 +536,7 @@ int sctp_packet_transmit(struct sctp_packet *packet) | |||
538 | if (!sctp_checksum_disable) { | 536 | if (!sctp_checksum_disable) { |
539 | if (!(dst->dev->features & NETIF_F_SCTP_CSUM) || | 537 | if (!(dst->dev->features & NETIF_F_SCTP_CSUM) || |
540 | (dst_xfrm(dst) != NULL) || packet->ipfragok) { | 538 | (dst_xfrm(dst) != NULL) || packet->ipfragok) { |
541 | __u32 crc32 = sctp_start_cksum((__u8 *)sh, cksum_buf_len); | 539 | sh->checksum = sctp_compute_cksum(nskb, 0); |
542 | |||
543 | /* 3) Put the resultant value into the checksum field in the | ||
544 | * common header, and leave the rest of the bits unchanged. | ||
545 | */ | ||
546 | sh->checksum = sctp_end_cksum(crc32); | ||
547 | } else { | 540 | } else { |
548 | /* no need to seed pseudo checksum for SCTP */ | 541 | /* no need to seed pseudo checksum for SCTP */ |
549 | nskb->ip_summed = CHECKSUM_PARTIAL; | 542 | nskb->ip_summed = CHECKSUM_PARTIAL; |