diff options
author | Sridhar Samudrala <sri@us.ibm.com> | 2009-07-09 08:10:04 +0000 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-07-12 14:29:29 -0700 |
commit | ba73542585a4a3c8a708f502e62e6e63dd74b66c (patch) | |
tree | 5dbb997e5f2f0eeb1cbc03000f8121b322b254cd /net/ipv6/af_inet6.c | |
parent | 7ea2f2c5a66e4e9a8d96296ac47ad895c467ee1d (diff) |
udpv6: Handle large incoming UDP/IPv6 packets and support software UFO
- validate and forward GSO UDP/IPv6 packets from untrusted sources.
- do software UFO if the outgoing device doesn't support UFO.
Signed-off-by: Sridhar Samudrala <sri@us.ibm.com>
Acked-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6/af_inet6.c')
-rw-r--r-- | net/ipv6/af_inet6.c | 20 |
1 files changed, 17 insertions, 3 deletions
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index caa0278d30a..bf85d5f9703 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -772,6 +772,11 @@ 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; + struct frag_hdr *fptr; + unsigned int unfrag_ip6hlen; + u8 *prevhdr; + int offset = 0; if (!(features & NETIF_F_V6_CSUM)) features &= ~NETIF_F_SG; @@ -791,10 +796,9 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, int features) __skb_pull(skb, sizeof(*ipv6h)); segs = ERR_PTR(-EPROTONOSUPPORT); + proto = ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr); rcu_read_lock(); - ops = rcu_dereference(inet6_protos[ - ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr)]); - + ops = rcu_dereference(inet6_protos[proto]); if (likely(ops && ops->gso_segment)) { skb_reset_transport_header(skb); segs = ops->gso_segment(skb, features); @@ -808,6 +812,16 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, int features) ipv6h = ipv6_hdr(skb); ipv6h->payload_len = htons(skb->len - skb->mac_len - sizeof(*ipv6h)); + if (proto == IPPROTO_UDP) { + unfrag_ip6hlen = ip6_find_1stfragopt(skb, &prevhdr); + fptr = (struct frag_hdr *)(skb_network_header(skb) + + unfrag_ip6hlen); + fptr->frag_off = htons(offset); + if (skb->next != NULL) + fptr->frag_off |= htons(IP6_MF); + offset += (ntohs(ipv6h->payload_len) - + sizeof(struct frag_hdr)); + } } out: |