diff options
Diffstat (limited to 'net/ipv6/ip6_output.c')
-rw-r--r-- | net/ipv6/ip6_output.c | 71 |
1 files changed, 70 insertions, 1 deletions
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 563b442ffab8..614296a920c6 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c | |||
@@ -147,7 +147,8 @@ static int ip6_output2(struct sk_buff *skb) | |||
147 | 147 | ||
148 | int ip6_output(struct sk_buff *skb) | 148 | int ip6_output(struct sk_buff *skb) |
149 | { | 149 | { |
150 | if (skb->len > dst_mtu(skb->dst) || dst_allfrag(skb->dst)) | 150 | if ((skb->len > dst_mtu(skb->dst) && !skb_shinfo(skb)->ufo_size) || |
151 | dst_allfrag(skb->dst)) | ||
151 | return ip6_fragment(skb, ip6_output2); | 152 | return ip6_fragment(skb, ip6_output2); |
152 | else | 153 | else |
153 | return ip6_output2(skb); | 154 | return ip6_output2(skb); |
@@ -768,6 +769,65 @@ out_err_release: | |||
768 | *dst = NULL; | 769 | *dst = NULL; |
769 | return err; | 770 | return err; |
770 | } | 771 | } |
772 | inline int ip6_ufo_append_data(struct sock *sk, | ||
773 | int getfrag(void *from, char *to, int offset, int len, | ||
774 | int odd, struct sk_buff *skb), | ||
775 | void *from, int length, int hh_len, int fragheaderlen, | ||
776 | int transhdrlen, int mtu,unsigned int flags) | ||
777 | |||
778 | { | ||
779 | struct sk_buff *skb; | ||
780 | int err; | ||
781 | |||
782 | /* There is support for UDP large send offload by network | ||
783 | * device, so create one single skb packet containing complete | ||
784 | * udp datagram | ||
785 | */ | ||
786 | if ((skb = skb_peek_tail(&sk->sk_write_queue)) == NULL) { | ||
787 | skb = sock_alloc_send_skb(sk, | ||
788 | hh_len + fragheaderlen + transhdrlen + 20, | ||
789 | (flags & MSG_DONTWAIT), &err); | ||
790 | if (skb == NULL) | ||
791 | return -ENOMEM; | ||
792 | |||
793 | /* reserve space for Hardware header */ | ||
794 | skb_reserve(skb, hh_len); | ||
795 | |||
796 | /* create space for UDP/IP header */ | ||
797 | skb_put(skb,fragheaderlen + transhdrlen); | ||
798 | |||
799 | /* initialize network header pointer */ | ||
800 | skb->nh.raw = skb->data; | ||
801 | |||
802 | /* initialize protocol header pointer */ | ||
803 | skb->h.raw = skb->data + fragheaderlen; | ||
804 | |||
805 | skb->ip_summed = CHECKSUM_HW; | ||
806 | skb->csum = 0; | ||
807 | sk->sk_sndmsg_off = 0; | ||
808 | } | ||
809 | |||
810 | err = skb_append_datato_frags(sk,skb, getfrag, from, | ||
811 | (length - transhdrlen)); | ||
812 | if (!err) { | ||
813 | struct frag_hdr fhdr; | ||
814 | |||
815 | /* specify the length of each IP datagram fragment*/ | ||
816 | skb_shinfo(skb)->ufo_size = (mtu - fragheaderlen) - | ||
817 | sizeof(struct frag_hdr); | ||
818 | ipv6_select_ident(skb, &fhdr); | ||
819 | skb_shinfo(skb)->ip6_frag_id = fhdr.identification; | ||
820 | __skb_queue_tail(&sk->sk_write_queue, skb); | ||
821 | |||
822 | return 0; | ||
823 | } | ||
824 | /* There is not enough support do UPD LSO, | ||
825 | * so follow normal path | ||
826 | */ | ||
827 | kfree_skb(skb); | ||
828 | |||
829 | return err; | ||
830 | } | ||
771 | 831 | ||
772 | int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, | 832 | int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, |
773 | int offset, int len, int odd, struct sk_buff *skb), | 833 | int offset, int len, int odd, struct sk_buff *skb), |
@@ -860,6 +920,15 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, | |||
860 | */ | 920 | */ |
861 | 921 | ||
862 | inet->cork.length += length; | 922 | inet->cork.length += length; |
923 | if (((length > mtu) && (sk->sk_protocol == IPPROTO_UDP)) && | ||
924 | (rt->u.dst.dev->features & NETIF_F_UFO)) { | ||
925 | |||
926 | if(ip6_ufo_append_data(sk, getfrag, from, length, hh_len, | ||
927 | fragheaderlen, transhdrlen, mtu, flags)) | ||
928 | goto error; | ||
929 | |||
930 | return 0; | ||
931 | } | ||
863 | 932 | ||
864 | if ((skb = skb_peek_tail(&sk->sk_write_queue)) == NULL) | 933 | if ((skb = skb_peek_tail(&sk->sk_write_queue)) == NULL) |
865 | goto alloc_new_skb; | 934 | goto alloc_new_skb; |