Commit b397f999 authored by David S. Miller's avatar David S. Miller

Merge branch 'sctp_csum'

Daniel Borkmann says:

====================
SCTP fix/updates

Please see patch 5 for the main description/motivation, the rest just
brings in the needed functionality for that. Although this is actually
a fix, I've based it against net-next as some additional work for
fixing it was needed.
====================
Acked-by: default avatarNeil Horman <nhorman@tuxdriver.com>
Acked-by: default avatarVlad Yasevich <vyasevich@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 296c1063 e6d8b64b
...@@ -11,8 +11,48 @@ ...@@ -11,8 +11,48 @@
extern u32 crc32_le(u32 crc, unsigned char const *p, size_t len); extern u32 crc32_le(u32 crc, unsigned char const *p, size_t len);
extern u32 crc32_be(u32 crc, unsigned char const *p, size_t len); extern u32 crc32_be(u32 crc, unsigned char const *p, size_t len);
/**
* crc32_le_combine - Combine two crc32 check values into one. For two
* sequences of bytes, seq1 and seq2 with lengths len1
* and len2, crc32_le() check values were calculated
* for each, crc1 and crc2.
*
* @crc1: crc32 of the first block
* @crc2: crc32 of the second block
* @len2: length of the second block
*
* Return: The crc32_le() check value of seq1 and seq2 concatenated,
* requiring only crc1, crc2, and len2. Note: If seq_full denotes
* the concatenated memory area of seq1 with seq2, and crc_full
* the crc32_le() value of seq_full, then crc_full ==
* crc32_le_combine(crc1, crc2, len2) when crc_full was seeded
* with the same initializer as crc1, and crc2 seed was 0. See
* also crc32_combine_test().
*/
extern u32 crc32_le_combine(u32 crc1, u32 crc2, size_t len2);
extern u32 __crc32c_le(u32 crc, unsigned char const *p, size_t len); extern u32 __crc32c_le(u32 crc, unsigned char const *p, size_t len);
/**
* __crc32c_le_combine - Combine two crc32c check values into one. For two
* sequences of bytes, seq1 and seq2 with lengths len1
* and len2, __crc32c_le() check values were calculated
* for each, crc1 and crc2.
*
* @crc1: crc32c of the first block
* @crc2: crc32c of the second block
* @len2: length of the second block
*
* Return: The __crc32c_le() check value of seq1 and seq2 concatenated,
* requiring only crc1, crc2, and len2. Note: If seq_full denotes
* the concatenated memory area of seq1 with seq2, and crc_full
* the __crc32c_le() value of seq_full, then crc_full ==
* __crc32c_le_combine(crc1, crc2, len2) when crc_full was
* seeded with the same initializer as crc1, and crc2 seed
* was 0. See also crc32c_combine_test().
*/
extern u32 __crc32c_le_combine(u32 crc1, u32 crc2, size_t len2);
#define crc32(seed, data, length) crc32_le(seed, (unsigned char const *)(data), length) #define crc32(seed, data, length) crc32_le(seed, (unsigned char const *)(data), length)
/* /*
......
...@@ -2360,8 +2360,6 @@ int skb_copy_datagram_const_iovec(const struct sk_buff *from, int offset, ...@@ -2360,8 +2360,6 @@ int skb_copy_datagram_const_iovec(const struct sk_buff *from, int offset,
void skb_free_datagram(struct sock *sk, struct sk_buff *skb); void skb_free_datagram(struct sock *sk, struct sk_buff *skb);
void skb_free_datagram_locked(struct sock *sk, struct sk_buff *skb); void skb_free_datagram_locked(struct sock *sk, struct sk_buff *skb);
int skb_kill_datagram(struct sock *sk, struct sk_buff *skb, unsigned int flags); int skb_kill_datagram(struct sock *sk, struct sk_buff *skb, unsigned int flags);
__wsum skb_checksum(const struct sk_buff *skb, int offset, int len,
__wsum csum);
int skb_copy_bits(const struct sk_buff *skb, int offset, void *to, int len); int skb_copy_bits(const struct sk_buff *skb, int offset, void *to, int len);
int skb_store_bits(struct sk_buff *skb, int offset, const void *from, int len); int skb_store_bits(struct sk_buff *skb, int offset, const void *from, int len);
__wsum skb_copy_and_csum_bits(const struct sk_buff *skb, int offset, u8 *to, __wsum skb_copy_and_csum_bits(const struct sk_buff *skb, int offset, u8 *to,
...@@ -2373,9 +2371,18 @@ void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to); ...@@ -2373,9 +2371,18 @@ void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to);
void skb_split(struct sk_buff *skb, struct sk_buff *skb1, const u32 len); void skb_split(struct sk_buff *skb, struct sk_buff *skb1, const u32 len);
int skb_shift(struct sk_buff *tgt, struct sk_buff *skb, int shiftlen); int skb_shift(struct sk_buff *tgt, struct sk_buff *skb, int shiftlen);
void skb_scrub_packet(struct sk_buff *skb, bool xnet); void skb_scrub_packet(struct sk_buff *skb, bool xnet);
struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features); struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features);
struct skb_checksum_ops {
__wsum (*update)(const void *mem, int len, __wsum wsum);
__wsum (*combine)(__wsum csum, __wsum csum2, int offset, int len);
};
__wsum __skb_checksum(const struct sk_buff *skb, int offset, int len,
__wsum csum, const struct skb_checksum_ops *ops);
__wsum skb_checksum(const struct sk_buff *skb, int offset, int len,
__wsum csum);
static inline void *skb_header_pointer(const struct sk_buff *skb, int offset, static inline void *skb_header_pointer(const struct sk_buff *skb, int offset,
int len, void *buffer) int len, void *buffer)
{ {
......
...@@ -78,6 +78,12 @@ csum_block_add(__wsum csum, __wsum csum2, int offset) ...@@ -78,6 +78,12 @@ csum_block_add(__wsum csum, __wsum csum2, int offset)
return csum_add(csum, (__force __wsum)sum); return csum_add(csum, (__force __wsum)sum);
} }
static inline __wsum
csum_block_add_ext(__wsum csum, __wsum csum2, int offset, int len)
{
return csum_block_add(csum, csum2, offset);
}
static inline __wsum static inline __wsum
csum_block_sub(__wsum csum, __wsum csum2, int offset) csum_block_sub(__wsum csum, __wsum csum2, int offset)
{ {
......
...@@ -42,56 +42,38 @@ ...@@ -42,56 +42,38 @@
#include <linux/types.h> #include <linux/types.h>
#include <net/sctp/sctp.h> #include <net/sctp/sctp.h>
#include <linux/crc32c.h> #include <linux/crc32c.h>
#include <linux/crc32.h>
static inline __u32 sctp_crc32c(__u32 crc, u8 *buffer, u16 length) static inline __wsum sctp_csum_update(const void *buff, int len, __wsum sum)
{ {
return crc32c(crc, buffer, length); /* This uses the crypto implementation of crc32c, which is either
} * implemented w/ hardware support or resolves to __crc32c_le().
static inline __u32 sctp_start_cksum(__u8 *buffer, __u16 length)
{
__u32 crc = ~(__u32)0;
__u8 zero[sizeof(__u32)] = {0};
/* Optimize this routine to be SCTP specific, knowing how
* to skip the checksum field of the SCTP header.
*/ */
return crc32c(sum, buff, len);
/* Calculate CRC up to the checksum. */
crc = sctp_crc32c(crc, buffer, sizeof(struct sctphdr) - sizeof(__u32));
/* Skip checksum field of the header. */
crc = sctp_crc32c(crc, zero, sizeof(__u32));
/* Calculate the rest of the CRC. */
crc = sctp_crc32c(crc, &buffer[sizeof(struct sctphdr)],
length - sizeof(struct sctphdr));
return crc;
}
static inline __u32 sctp_update_cksum(__u8 *buffer, __u16 length, __u32 crc32)
{
return sctp_crc32c(crc32, buffer, length);
} }
static inline __le32 sctp_end_cksum(__u32 crc32) static inline __wsum sctp_csum_combine(__wsum csum, __wsum csum2,
int offset, int len)
{ {
return cpu_to_le32(~crc32); return __crc32c_le_combine(csum, csum2, len);
} }
/* Calculate the CRC32C checksum of an SCTP packet. */
static inline __le32 sctp_compute_cksum(const struct sk_buff *skb, static inline __le32 sctp_compute_cksum(const struct sk_buff *skb,
unsigned int offset) unsigned int offset)
{ {
const struct sk_buff *iter; struct sctphdr *sh = sctp_hdr(skb);
__le32 ret, old = sh->checksum;
const struct skb_checksum_ops ops = {
.update = sctp_csum_update,
.combine = sctp_csum_combine,
};
__u32 crc32 = sctp_start_cksum(skb->data + offset, sh->checksum = 0;
skb_headlen(skb) - offset); ret = cpu_to_le32(~__skb_checksum(skb, offset, skb->len - offset,
skb_walk_frags(skb, iter) ~(__u32)0, &ops));
crc32 = sctp_update_cksum((__u8 *) iter->data, sh->checksum = old;
skb_headlen(iter), crc32);
return sctp_end_cksum(crc32); return ret;
} }
#endif /* __sctp_checksum_h__ */ #endif /* __sctp_checksum_h__ */
This diff is collapsed.
...@@ -1928,9 +1928,8 @@ int skb_store_bits(struct sk_buff *skb, int offset, const void *from, int len) ...@@ -1928,9 +1928,8 @@ int skb_store_bits(struct sk_buff *skb, int offset, const void *from, int len)
EXPORT_SYMBOL(skb_store_bits); EXPORT_SYMBOL(skb_store_bits);
/* Checksum skb data. */ /* Checksum skb data. */
__wsum __skb_checksum(const struct sk_buff *skb, int offset, int len,
__wsum skb_checksum(const struct sk_buff *skb, int offset, __wsum csum, const struct skb_checksum_ops *ops)
int len, __wsum csum)
{ {
int start = skb_headlen(skb); int start = skb_headlen(skb);
int i, copy = start - offset; int i, copy = start - offset;
...@@ -1941,7 +1940,7 @@ __wsum skb_checksum(const struct sk_buff *skb, int offset, ...@@ -1941,7 +1940,7 @@ __wsum skb_checksum(const struct sk_buff *skb, int offset,
if (copy > 0) { if (copy > 0) {
if (copy > len) if (copy > len)
copy = len; copy = len;
csum = csum_partial(skb->data + offset, copy, csum); csum = ops->update(skb->data + offset, copy, csum);
if ((len -= copy) == 0) if ((len -= copy) == 0)
return csum; return csum;
offset += copy; offset += copy;
...@@ -1962,10 +1961,10 @@ __wsum skb_checksum(const struct sk_buff *skb, int offset, ...@@ -1962,10 +1961,10 @@ __wsum skb_checksum(const struct sk_buff *skb, int offset,
if (copy > len) if (copy > len)
copy = len; copy = len;
vaddr = kmap_atomic(skb_frag_page(frag)); vaddr = kmap_atomic(skb_frag_page(frag));
csum2 = csum_partial(vaddr + frag->page_offset + csum2 = ops->update(vaddr + frag->page_offset +
offset - start, copy, 0); offset - start, copy, 0);
kunmap_atomic(vaddr); kunmap_atomic(vaddr);
csum = csum_block_add(csum, csum2, pos); csum = ops->combine(csum, csum2, pos, copy);
if (!(len -= copy)) if (!(len -= copy))
return csum; return csum;
offset += copy; offset += copy;
...@@ -1984,9 +1983,9 @@ __wsum skb_checksum(const struct sk_buff *skb, int offset, ...@@ -1984,9 +1983,9 @@ __wsum skb_checksum(const struct sk_buff *skb, int offset,
__wsum csum2; __wsum csum2;
if (copy > len) if (copy > len)
copy = len; copy = len;
csum2 = skb_checksum(frag_iter, offset - start, csum2 = __skb_checksum(frag_iter, offset - start,
copy, 0); copy, 0, ops);
csum = csum_block_add(csum, csum2, pos); csum = ops->combine(csum, csum2, pos, copy);
if ((len -= copy) == 0) if ((len -= copy) == 0)
return csum; return csum;
offset += copy; offset += copy;
...@@ -1998,6 +1997,18 @@ __wsum skb_checksum(const struct sk_buff *skb, int offset, ...@@ -1998,6 +1997,18 @@ __wsum skb_checksum(const struct sk_buff *skb, int offset,
return csum; return csum;
} }
EXPORT_SYMBOL(__skb_checksum);
__wsum skb_checksum(const struct sk_buff *skb, int offset,
int len, __wsum csum)
{
const struct skb_checksum_ops ops = {
.update = csum_partial,
.combine = csum_block_add_ext,
};
return __skb_checksum(skb, offset, len, csum, &ops);
}
EXPORT_SYMBOL(skb_checksum); EXPORT_SYMBOL(skb_checksum);
/* Both of above in one bottle. */ /* Both of above in one bottle. */
......
...@@ -390,7 +390,6 @@ int sctp_packet_transmit(struct sctp_packet *packet) ...@@ -390,7 +390,6 @@ int sctp_packet_transmit(struct sctp_packet *packet)
__u8 has_data = 0; __u8 has_data = 0;
struct dst_entry *dst = tp->dst; struct dst_entry *dst = tp->dst;
unsigned char *auth = NULL; /* pointer to auth in skb data */ unsigned char *auth = NULL; /* pointer to auth in skb data */
__u32 cksum_buf_len = sizeof(struct sctphdr);
pr_debug("%s: packet:%p\n", __func__, packet); pr_debug("%s: packet:%p\n", __func__, packet);
...@@ -493,7 +492,6 @@ int sctp_packet_transmit(struct sctp_packet *packet) ...@@ -493,7 +492,6 @@ int sctp_packet_transmit(struct sctp_packet *packet)
if (chunk == packet->auth) if (chunk == packet->auth)
auth = skb_tail_pointer(nskb); auth = skb_tail_pointer(nskb);
cksum_buf_len += chunk->skb->len;
memcpy(skb_put(nskb, chunk->skb->len), memcpy(skb_put(nskb, chunk->skb->len),
chunk->skb->data, chunk->skb->len); chunk->skb->data, chunk->skb->len);
...@@ -538,12 +536,7 @@ int sctp_packet_transmit(struct sctp_packet *packet) ...@@ -538,12 +536,7 @@ int sctp_packet_transmit(struct sctp_packet *packet)
if (!sctp_checksum_disable) { if (!sctp_checksum_disable) {
if (!(dst->dev->features & NETIF_F_SCTP_CSUM) || if (!(dst->dev->features & NETIF_F_SCTP_CSUM) ||
(dst_xfrm(dst) != NULL) || packet->ipfragok) { (dst_xfrm(dst) != NULL) || packet->ipfragok) {
__u32 crc32 = sctp_start_cksum((__u8 *)sh, cksum_buf_len); sh->checksum = sctp_compute_cksum(nskb, 0);
/* 3) Put the resultant value into the checksum field in the
* common header, and leave the rest of the bits unchanged.
*/
sh->checksum = sctp_end_cksum(crc32);
} else { } else {
/* no need to seed pseudo checksum for SCTP */ /* no need to seed pseudo checksum for SCTP */
nskb->ip_summed = CHECKSUM_PARTIAL; nskb->ip_summed = CHECKSUM_PARTIAL;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment