summaryrefslogtreecommitdiffstats
path: root/net/ipv6/ipv6_sockglue.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@g5.osdl.org>2006-06-30 15:40:17 -0700
committerLinus Torvalds <torvalds@g5.osdl.org>2006-06-30 15:40:17 -0700
commite37a72de84d27ee8bc0e7dbb5c2f1774ed306dbb (patch)
treef9da35cbd79b52a5bd08d4a0f960bde6af741da0 /net/ipv6/ipv6_sockglue.c
parent93fdf10d4c28edaa1b9f80e7f9c3002359186d00 (diff)
parentf83ef8c0b58dac17211a4c0b6df0e2b1bd6637b1 (diff)
downloadlinux-e37a72de84d27ee8bc0e7dbb5c2f1774ed306dbb.tar.gz
linux-e37a72de84d27ee8bc0e7dbb5c2f1774ed306dbb.tar.bz2
linux-e37a72de84d27ee8bc0e7dbb5c2f1774ed306dbb.zip
Merge master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6
* master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6: [IPV6]: Added GSO support for TCPv6 [NET]: Generalise TSO-specific bits from skb_setup_caps [IPV6]: Added GSO support for TCPv6 [IPV6]: Remove redundant length check on input [NETFILTER]: SCTP conntrack: fix crash triggered by packet without chunks [TG3]: Update version and reldate [TG3]: Add TSO workaround using GSO [TG3]: Turn on hw fix for ASF problems [TG3]: Add rx BD workaround [TG3]: Add tg3_netif_stop() in vlan functions [TCP]: Reset gso_segs if packet is dodgy
Diffstat (limited to 'net/ipv6/ipv6_sockglue.c')
-rw-r--r--net/ipv6/ipv6_sockglue.c62
1 files changed, 62 insertions, 0 deletions
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index 97199d6ec79f..c28e5c287447 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -57,9 +57,71 @@
DEFINE_SNMP_STAT(struct ipstats_mib, ipv6_statistics) __read_mostly;
+static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, int features)
+{
+ struct sk_buff *segs = ERR_PTR(-EINVAL);
+ struct ipv6hdr *ipv6h;
+ struct inet6_protocol *ops;
+ int proto;
+
+ if (unlikely(!pskb_may_pull(skb, sizeof(*ipv6h))))
+ goto out;
+
+ ipv6h = skb->nh.ipv6h;
+ proto = ipv6h->nexthdr;
+ __skb_pull(skb, sizeof(*ipv6h));
+
+ rcu_read_lock();
+ for (;;) {
+ struct ipv6_opt_hdr *opth;
+ int len;
+
+ if (proto != NEXTHDR_HOP) {
+ ops = rcu_dereference(inet6_protos[proto]);
+
+ if (unlikely(!ops))
+ goto unlock;
+
+ if (!(ops->flags & INET6_PROTO_GSO_EXTHDR))
+ break;
+ }
+
+ if (unlikely(!pskb_may_pull(skb, 8)))
+ goto unlock;
+
+ opth = (void *)skb->data;
+ len = opth->hdrlen * 8 + 8;
+
+ if (unlikely(!pskb_may_pull(skb, len)))
+ goto unlock;
+
+ proto = opth->nexthdr;
+ __skb_pull(skb, len);
+ }
+
+ skb->h.raw = skb->data;
+ if (likely(ops->gso_segment))
+ segs = ops->gso_segment(skb, features);
+
+unlock:
+ rcu_read_unlock();
+
+ if (unlikely(IS_ERR(segs)))
+ goto out;
+
+ for (skb = segs; skb; skb = skb->next) {
+ ipv6h = skb->nh.ipv6h;
+ ipv6h->payload_len = htons(skb->len - skb->mac_len);
+ }
+
+out:
+ return segs;
+}
+
static struct packet_type ipv6_packet_type = {
.type = __constant_htons(ETH_P_IPV6),
.func = ipv6_rcv,
+ .gso_segment = ipv6_gso_segment,
};
struct ip6_ra_chain *ip6_ra_chain;