Skip to content

Commit 663ead3

Browse files
herbertxDavid S. Miller
authored andcommitted
[NET]: Use csum_start offset instead of skb_transport_header
The skb transport pointer is currently used to specify the start of the checksum region for transmit checksum offload. Unfortunately, the same pointer is also used during receive side processing. This creates a problem when we want to retransmit a received packet with partial checksums since the skb transport pointer would be overwritten. This patch solves this problem by creating a new 16-bit csum_start offset value to replace the skb transport header for the purpose of checksums. This offset is calculated from skb->head so that it does not have to change when skb->data changes. No extra space is required since csum_offset itself fits within a 16-bit word so we can use the other 16 bits for csum_start. For backwards compatibility, just before we push a packet with partial checksums off into the device driver, we set the skb transport header to what it would have been under the old scheme. Signed-off-by: Herbert Xu <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent ac758e3 commit 663ead3

File tree

6 files changed

+28
-13
lines changed

6 files changed

+28
-13
lines changed

include/linux/skbuff.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,9 @@ typedef unsigned char *sk_buff_data_t;
205205
* @len: Length of actual data
206206
* @data_len: Data length
207207
* @mac_len: Length of link layer header
208-
* @csum: Checksum
208+
* @csum: Checksum (must include start/offset pair)
209+
* @csum_start: Offset from skb->head where checksumming should start
210+
* @csum_offset: Offset from csum_start where checksum should be stored
209211
* @local_df: allow local fragmentation
210212
* @cloned: Head may be cloned (check refcnt to be sure)
211213
* @nohdr: Payload reference only, must not modify header
@@ -261,7 +263,10 @@ struct sk_buff {
261263
mac_len;
262264
union {
263265
__wsum csum;
264-
__u32 csum_offset;
266+
struct {
267+
__u16 csum_start;
268+
__u16 csum_offset;
269+
};
265270
};
266271
__u32 priority;
267272
__u8 local_df:1,

net/core/dev.c

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1155,7 +1155,7 @@ EXPORT_SYMBOL(netif_device_attach);
11551155
int skb_checksum_help(struct sk_buff *skb)
11561156
{
11571157
__wsum csum;
1158-
int ret = 0, offset = skb_transport_offset(skb);
1158+
int ret = 0, offset;
11591159

11601160
if (skb->ip_summed == CHECKSUM_COMPLETE)
11611161
goto out_set_summed;
@@ -1171,15 +1171,16 @@ int skb_checksum_help(struct sk_buff *skb)
11711171
goto out;
11721172
}
11731173

1174+
offset = skb->csum_start - skb_headroom(skb);
11741175
BUG_ON(offset > (int)skb->len);
11751176
csum = skb_checksum(skb, offset, skb->len-offset, 0);
11761177

1177-
offset = skb->tail - skb->transport_header;
1178+
offset = skb_headlen(skb) - offset;
11781179
BUG_ON(offset <= 0);
11791180
BUG_ON(skb->csum_offset + 2 > offset);
11801181

1181-
*(__sum16 *)(skb_transport_header(skb) +
1182-
skb->csum_offset) = csum_fold(csum);
1182+
*(__sum16 *)(skb->head + skb->csum_start + skb->csum_offset) =
1183+
csum_fold(csum);
11831184
out_set_summed:
11841185
skb->ip_summed = CHECKSUM_NONE;
11851186
out:
@@ -1431,12 +1432,16 @@ int dev_queue_xmit(struct sk_buff *skb)
14311432
/* If packet is not checksummed and device does not support
14321433
* checksumming for this protocol, complete checksumming here.
14331434
*/
1434-
if (skb->ip_summed == CHECKSUM_PARTIAL &&
1435-
(!(dev->features & NETIF_F_GEN_CSUM) &&
1436-
(!(dev->features & NETIF_F_IP_CSUM) ||
1437-
skb->protocol != htons(ETH_P_IP))))
1438-
if (skb_checksum_help(skb))
1439-
goto out_kfree_skb;
1435+
if (skb->ip_summed == CHECKSUM_PARTIAL) {
1436+
skb_set_transport_header(skb, skb->csum_start -
1437+
skb_headroom(skb));
1438+
1439+
if (!(dev->features & NETIF_F_GEN_CSUM) &&
1440+
(!(dev->features & NETIF_F_IP_CSUM) ||
1441+
skb->protocol != htons(ETH_P_IP)))
1442+
if (skb_checksum_help(skb))
1443+
goto out_kfree_skb;
1444+
}
14401445

14411446
gso:
14421447
spin_lock_prefetch(&dev->queue_lock);

net/core/skbuff.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1358,7 +1358,7 @@ void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to)
13581358
long csstart;
13591359

13601360
if (skb->ip_summed == CHECKSUM_PARTIAL)
1361-
csstart = skb_transport_offset(skb);
1361+
csstart = skb->csum_start - skb_headroom(skb);
13621362
else
13631363
csstart = skb_headlen(skb);
13641364

net/ipv4/tcp_ipv4.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,7 @@ void tcp_v4_send_check(struct sock *sk, int len, struct sk_buff *skb)
504504
if (skb->ip_summed == CHECKSUM_PARTIAL) {
505505
th->check = ~tcp_v4_check(len, inet->saddr,
506506
inet->daddr, 0);
507+
skb->csum_start = skb_transport_header(skb) - skb->head;
507508
skb->csum_offset = offsetof(struct tcphdr, check);
508509
} else {
509510
th->check = tcp_v4_check(len, inet->saddr, inet->daddr,
@@ -526,6 +527,7 @@ int tcp_v4_gso_send_check(struct sk_buff *skb)
526527

527528
th->check = 0;
528529
th->check = ~tcp_v4_check(skb->len, iph->saddr, iph->daddr, 0);
530+
skb->csum_start = skb_transport_header(skb) - skb->head;
529531
skb->csum_offset = offsetof(struct tcphdr, check);
530532
skb->ip_summed = CHECKSUM_PARTIAL;
531533
return 0;

net/ipv4/udp.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,7 @@ static void udp4_hwcsum_outgoing(struct sock *sk, struct sk_buff *skb,
427427
/*
428428
* Only one fragment on the socket.
429429
*/
430+
skb->csum_start = skb_transport_header(skb) - skb->head;
430431
skb->csum_offset = offsetof(struct udphdr, check);
431432
uh->check = ~csum_tcpudp_magic(src, dst, len, IPPROTO_UDP, 0);
432433
} else {

net/ipv6/tcp_ipv6.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -950,6 +950,7 @@ static void tcp_v6_send_check(struct sock *sk, int len, struct sk_buff *skb)
950950

951951
if (skb->ip_summed == CHECKSUM_PARTIAL) {
952952
th->check = ~csum_ipv6_magic(&np->saddr, &np->daddr, len, IPPROTO_TCP, 0);
953+
skb->csum_start = skb_transport_header(skb) - skb->head;
953954
skb->csum_offset = offsetof(struct tcphdr, check);
954955
} else {
955956
th->check = csum_ipv6_magic(&np->saddr, &np->daddr, len, IPPROTO_TCP,
@@ -972,6 +973,7 @@ static int tcp_v6_gso_send_check(struct sk_buff *skb)
972973
th->check = 0;
973974
th->check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr, skb->len,
974975
IPPROTO_TCP, 0);
976+
skb->csum_start = skb_transport_header(skb) - skb->head;
975977
skb->csum_offset = offsetof(struct tcphdr, check);
976978
skb->ip_summed = CHECKSUM_PARTIAL;
977979
return 0;

0 commit comments

Comments
 (0)