aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRusty Russell <rusty@rustcorp.com.au>2008-02-04 23:49:54 -0500
committerRusty Russell <rusty@rustcorp.com.au>2008-02-04 07:49:56 -0500
commitf35d9d8aae08940b7fdd1bb8110619da2ece6b28 (patch)
tree562d8d7a6583d0a0750ec8d996026b73e8315421
parent9135f1901ee6449dfe338adf6e40e9c2025b8150 (diff)
virtio: Implement skb_partial_csum_set, for setting partial csums on untrusted packets.
Use it in virtio_net (replacing buggy version there), it's also going to be used by TAP for partial csum support. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au> Acked-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/virtio_net.c11
-rw-r--r--include/linux/skbuff.h1
-rw-r--r--net/core/skbuff.c29
3 files changed, 31 insertions, 10 deletions
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index 5413dbf3d4ac..a60505c8f82a 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -83,17 +83,8 @@ static void receive_skb(struct net_device *dev, struct sk_buff *skb,
83 83
84 if (hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) { 84 if (hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) {
85 pr_debug("Needs csum!\n"); 85 pr_debug("Needs csum!\n");
86 skb->ip_summed = CHECKSUM_PARTIAL; 86 if (!skb_partial_csum_set(skb,hdr->csum_start,hdr->csum_offset))
87 skb->csum_start = hdr->csum_start;
88 skb->csum_offset = hdr->csum_offset;
89 if (skb->csum_start > skb->len - 2
90 || skb->csum_offset > skb->len - 2) {
91 if (net_ratelimit())
92 printk(KERN_WARNING "%s: csum=%u/%u len=%u\n",
93 dev->name, skb->csum_start,
94 skb->csum_offset, skb->len);
95 goto frame_err; 87 goto frame_err;
96 }
97 } 88 }
98 89
99 if (hdr->gso_type != VIRTIO_NET_HDR_GSO_NONE) { 90 if (hdr->gso_type != VIRTIO_NET_HDR_GSO_NONE) {
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index dfe975a9967e..412672a79e8a 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -1810,5 +1810,6 @@ static inline void skb_forward_csum(struct sk_buff *skb)
1810 skb->ip_summed = CHECKSUM_NONE; 1810 skb->ip_summed = CHECKSUM_NONE;
1811} 1811}
1812 1812
1813bool skb_partial_csum_set(struct sk_buff *skb, u16 start, u16 off);
1813#endif /* __KERNEL__ */ 1814#endif /* __KERNEL__ */
1814#endif /* _LINUX_SKBUFF_H */ 1815#endif /* _LINUX_SKBUFF_H */
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 98420f9c4b6d..4e354221ec23 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -2461,6 +2461,34 @@ int skb_cow_data(struct sk_buff *skb, int tailbits, struct sk_buff **trailer)
2461 return elt; 2461 return elt;
2462} 2462}
2463 2463
2464/**
2465 * skb_partial_csum_set - set up and verify partial csum values for packet
2466 * @skb: the skb to set
2467 * @start: the number of bytes after skb->data to start checksumming.
2468 * @off: the offset from start to place the checksum.
2469 *
2470 * For untrusted partially-checksummed packets, we need to make sure the values
2471 * for skb->csum_start and skb->csum_offset are valid so we don't oops.
2472 *
2473 * This function checks and sets those values and skb->ip_summed: if this
2474 * returns false you should drop the packet.
2475 */
2476bool skb_partial_csum_set(struct sk_buff *skb, u16 start, u16 off)
2477{
2478 if (unlikely(start > skb->len - 2) ||
2479 unlikely((int)start + off > skb->len - 2)) {
2480 if (net_ratelimit())
2481 printk(KERN_WARNING
2482 "bad partial csum: csum=%u/%u len=%u\n",
2483 start, off, skb->len);
2484 return false;
2485 }
2486 skb->ip_summed = CHECKSUM_PARTIAL;
2487 skb->csum_start = skb_headroom(skb) + start;
2488 skb->csum_offset = off;
2489 return true;
2490}
2491
2464EXPORT_SYMBOL(___pskb_trim); 2492EXPORT_SYMBOL(___pskb_trim);
2465EXPORT_SYMBOL(__kfree_skb); 2493EXPORT_SYMBOL(__kfree_skb);
2466EXPORT_SYMBOL(kfree_skb); 2494EXPORT_SYMBOL(kfree_skb);
@@ -2497,3 +2525,4 @@ EXPORT_SYMBOL(skb_append_datato_frags);
2497 2525
2498EXPORT_SYMBOL_GPL(skb_to_sgvec); 2526EXPORT_SYMBOL_GPL(skb_to_sgvec);
2499EXPORT_SYMBOL_GPL(skb_cow_data); 2527EXPORT_SYMBOL_GPL(skb_cow_data);
2528EXPORT_SYMBOL_GPL(skb_partial_csum_set);