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; |
