aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv6')
-rw-r--r--net/ipv6/ip6_output.c71
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
148int ip6_output(struct sk_buff *skb) 148int 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}
772inline 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
772int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, 832int 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;