]> Pileus Git - ~andy/linux/blobdiff - net/ipv6/ip6_offload.c
ipv6: sit: add GSO/TSO support
[~andy/linux] / net / ipv6 / ip6_offload.c
index 5c2fc1d0419638967a7a5a8a29dd8696c93df505..4b851692b1f6bed3fbe476c65672c45e28570b3e 100644 (file)
@@ -90,6 +90,7 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb,
        u8 *prevhdr;
        int offset = 0;
        bool tunnel;
+       int nhoff;
 
        if (unlikely(skb_shinfo(skb)->gso_type &
                     ~(SKB_GSO_UDP |
@@ -97,16 +98,23 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb,
                       SKB_GSO_TCP_ECN |
                       SKB_GSO_GRE |
                       SKB_GSO_IPIP |
+                      SKB_GSO_SIT |
                       SKB_GSO_UDP_TUNNEL |
                       SKB_GSO_MPLS |
                       SKB_GSO_TCPV6 |
                       0)))
                goto out;
 
+       skb_reset_network_header(skb);
+       nhoff = skb_network_header(skb) - skb_mac_header(skb);
        if (unlikely(!pskb_may_pull(skb, sizeof(*ipv6h))))
                goto out;
 
-       tunnel = skb->encapsulation;
+       tunnel = SKB_GSO_CB(skb)->encap_level > 0;
+       if (tunnel)
+               features = skb->dev->hw_enc_features & netif_skb_features(skb);
+       SKB_GSO_CB(skb)->encap_level += sizeof(*ipv6h);
+
        ipv6h = ipv6_hdr(skb);
        __skb_pull(skb, sizeof(*ipv6h));
        segs = ERR_PTR(-EPROTONOSUPPORT);
@@ -123,13 +131,17 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb,
                goto out;
 
        for (skb = segs; skb; skb = skb->next) {
-               ipv6h = ipv6_hdr(skb);
-               ipv6h->payload_len = htons(skb->len - skb->mac_len -
-                                          sizeof(*ipv6h));
+               ipv6h = (struct ipv6hdr *)(skb_mac_header(skb) + nhoff);
+               ipv6h->payload_len = htons(skb->len - nhoff - sizeof(*ipv6h));
+               if (tunnel) {
+                       skb_reset_inner_headers(skb);
+                       skb->encapsulation = 1;
+               }
+               skb->network_header = (u8 *)ipv6h - skb->head;
+
                if (!tunnel && proto == IPPROTO_UDP) {
                        unfrag_ip6hlen = ip6_find_1stfragopt(skb, &prevhdr);
-                       fptr = (struct frag_hdr *)(skb_network_header(skb) +
-                               unfrag_ip6hlen);
+                       fptr = (struct frag_hdr *)((u8 *)ipv6h + unfrag_ip6hlen);
                        fptr->frag_off = htons(offset);
                        if (skb->next != NULL)
                                fptr->frag_off |= htons(IP6_MF);
@@ -265,6 +277,13 @@ static struct packet_offload ipv6_packet_offload __read_mostly = {
        },
 };
 
+static const struct net_offload sit_offload = {
+       .callbacks = {
+               .gso_send_check = ipv6_gso_send_check,
+               .gso_segment    = ipv6_gso_segment,
+       },
+};
+
 static int __init ipv6_offload_init(void)
 {
 
@@ -276,6 +295,9 @@ static int __init ipv6_offload_init(void)
                pr_crit("%s: Cannot add EXTHDRS protocol offload\n", __func__);
 
        dev_add_offload(&ipv6_packet_offload);
+
+       inet_add_offload(&sit_offload, IPPROTO_IPV6);
+
        return 0;
 }