diff options
author | Kostya B <bkostya@hotmail.com> | 2008-04-30 01:36:30 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-04-30 01:36:30 -0400 |
commit | be9164e769d57aa10b2bbe15d103edc041b9e7de (patch) | |
tree | 35f8c540da31cb8cafa1e6948ae682fd3c8d6bfa /net | |
parent | 3a8209d19dd791aaac3668be2fa51a9b42113efd (diff) |
[IPv4] UFO: prevent generation of chained skb destined to UFO device
Problem: ip_append_data() could wrongly generate a chained skb for
devices which support UFO. When sk_write_queue is not empty
(e.g. MSG_MORE), __instead__ of appending data into the next nr_frag
of the queued skb, a new chained skb is created.
I would normally assume UFO device should get data in nr_frags and not
in frag_list. Later the udp4_hwcsum_outgoing() resets csum to NONE
and skb_gso_segment() has oops.
Proposal:
1. Even length is less than mtu, employ ip_ufo_append_data()
and append data to the __existed__ skb in the sk_write_queue.
2. ip_ufo_append_data() is fixed due to a wrong manipulation of
peek-ing and later enqueue-ing of the same skb. Now, enqueuing is
always performed, because on error the further
ip_flush_pending_frames() would release the queued skb.
Signed-off-by: Kostya B <bkostya@hotmail.com>
Acked-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/ipv4/ip_output.c | 22 |
1 files changed, 7 insertions, 15 deletions
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 08349267ceb4..e527628f56cf 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c | |||
@@ -753,23 +753,15 @@ static inline int ip_ufo_append_data(struct sock *sk, | |||
753 | skb->ip_summed = CHECKSUM_PARTIAL; | 753 | skb->ip_summed = CHECKSUM_PARTIAL; |
754 | skb->csum = 0; | 754 | skb->csum = 0; |
755 | sk->sk_sndmsg_off = 0; | 755 | sk->sk_sndmsg_off = 0; |
756 | } | ||
757 | 756 | ||
758 | err = skb_append_datato_frags(sk,skb, getfrag, from, | 757 | /* specify the length of each IP datagram fragment */ |
759 | (length - transhdrlen)); | ||
760 | if (!err) { | ||
761 | /* specify the length of each IP datagram fragment*/ | ||
762 | skb_shinfo(skb)->gso_size = mtu - fragheaderlen; | 758 | skb_shinfo(skb)->gso_size = mtu - fragheaderlen; |
763 | skb_shinfo(skb)->gso_type = SKB_GSO_UDP; | 759 | skb_shinfo(skb)->gso_type = SKB_GSO_UDP; |
764 | __skb_queue_tail(&sk->sk_write_queue, skb); | 760 | __skb_queue_tail(&sk->sk_write_queue, skb); |
765 | |||
766 | return 0; | ||
767 | } | 761 | } |
768 | /* There is not enough support do UFO , | 762 | |
769 | * so follow normal path | 763 | return skb_append_datato_frags(sk, skb, getfrag, from, |
770 | */ | 764 | (length - transhdrlen)); |
771 | kfree_skb(skb); | ||
772 | return err; | ||
773 | } | 765 | } |
774 | 766 | ||
775 | /* | 767 | /* |
@@ -863,9 +855,9 @@ int ip_append_data(struct sock *sk, | |||
863 | csummode = CHECKSUM_PARTIAL; | 855 | csummode = CHECKSUM_PARTIAL; |
864 | 856 | ||
865 | inet->cork.length += length; | 857 | inet->cork.length += length; |
866 | if (((length > mtu) && (sk->sk_protocol == IPPROTO_UDP)) && | 858 | if (((length> mtu) || !skb_queue_empty(&sk->sk_write_queue)) && |
867 | (rt->u.dst.dev->features & NETIF_F_UFO)) { | 859 | (sk->sk_protocol == IPPROTO_UDP) && |
868 | 860 | (rt->u.dst.dev->features & NETIF_F_UFO)) { | |
869 | err = ip_ufo_append_data(sk, getfrag, from, length, hh_len, | 861 | err = ip_ufo_append_data(sk, getfrag, from, length, hh_len, |
870 | fragheaderlen, transhdrlen, mtu, | 862 | fragheaderlen, transhdrlen, mtu, |
871 | flags); | 863 | flags); |