]> Pileus Git - ~andy/linux/blobdiff - net/core/skbuff.c
tunneling: Capture inner mac header during encapsulation.
[~andy/linux] / net / core / skbuff.c
index 33245ef54c3bd6982abed459cc6c0ef7935cb21f..31c6737d3189ccd07ff9924126f3a5b7292c27a3 100644 (file)
@@ -673,6 +673,7 @@ static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
        new->mac_header         = old->mac_header;
        new->inner_transport_header = old->inner_transport_header;
        new->inner_network_header = old->inner_network_header;
+       new->inner_mac_header = old->inner_mac_header;
        skb_dst_copy(new, old);
        new->rxhash             = old->rxhash;
        new->ooo_okay           = old->ooo_okay;
@@ -867,6 +868,18 @@ struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t gfp_mask)
 }
 EXPORT_SYMBOL(skb_clone);
 
+static void skb_headers_offset_update(struct sk_buff *skb, int off)
+{
+       /* {transport,network,mac}_header and tail are relative to skb->head */
+       skb->transport_header += off;
+       skb->network_header   += off;
+       if (skb_mac_header_was_set(skb))
+               skb->mac_header += off;
+       skb->inner_transport_header += off;
+       skb->inner_network_header += off;
+       skb->inner_mac_header += off;
+}
+
 static void copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
 {
 #ifndef NET_SKBUFF_DATA_USES_OFFSET
@@ -879,13 +892,7 @@ static void copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
        __copy_skb_header(new, old);
 
 #ifndef NET_SKBUFF_DATA_USES_OFFSET
-       /* {transport,network,mac}_header are relative to skb->head */
-       new->transport_header += offset;
-       new->network_header   += offset;
-       if (skb_mac_header_was_set(new))
-               new->mac_header       += offset;
-       new->inner_transport_header += offset;
-       new->inner_network_header   += offset;
+       skb_headers_offset_update(new, offset);
 #endif
        skb_shinfo(new)->gso_size = skb_shinfo(old)->gso_size;
        skb_shinfo(new)->gso_segs = skb_shinfo(old)->gso_segs;
@@ -1077,14 +1084,8 @@ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail,
 #else
        skb->end      = skb->head + size;
 #endif
-       /* {transport,network,mac}_header and tail are relative to skb->head */
        skb->tail             += off;
-       skb->transport_header += off;
-       skb->network_header   += off;
-       if (skb_mac_header_was_set(skb))
-               skb->mac_header += off;
-       skb->inner_transport_header += off;
-       skb->inner_network_header += off;
+       skb_headers_offset_update(skb, off);
        /* Only adjust this if it actually is csum_start rather than csum */
        if (skb->ip_summed == CHECKSUM_PARTIAL)
                skb->csum_start += nhead;
@@ -1180,12 +1181,7 @@ struct sk_buff *skb_copy_expand(const struct sk_buff *skb,
        if (n->ip_summed == CHECKSUM_PARTIAL)
                n->csum_start += off;
 #ifdef NET_SKBUFF_DATA_USES_OFFSET
-       n->transport_header += off;
-       n->network_header   += off;
-       if (skb_mac_header_was_set(skb))
-               n->mac_header += off;
-       n->inner_transport_header += off;
-       n->inner_network_header    += off;
+       skb_headers_offset_update(n, off);
 #endif
 
        return n;
@@ -2741,12 +2737,19 @@ struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features)
        unsigned int tnl_hlen = skb_tnl_header_len(skb);
        unsigned int headroom;
        unsigned int len;
+       __be16 proto;
+       bool csum;
        int sg = !!(features & NETIF_F_SG);
        int nfrags = skb_shinfo(skb)->nr_frags;
        int err = -ENOMEM;
        int i = 0;
        int pos;
 
+       proto = skb_network_protocol(skb);
+       if (unlikely(!proto))
+               return ERR_PTR(-EINVAL);
+
+       csum = !!can_checksum_protocol(features, proto);
        __skb_push(skb, doffset);
        headroom = skb_headroom(skb);
        pos = skb_headlen(skb);
@@ -2884,6 +2887,12 @@ skip_fraglist:
                nskb->data_len = len - hsize;
                nskb->len += nskb->data_len;
                nskb->truesize += nskb->data_len;
+
+               if (!csum) {
+                       nskb->csum = skb_checksum(nskb, doffset,
+                                                 nskb->len - doffset, 0);
+                       nskb->ip_summed = CHECKSUM_NONE;
+               }
        } while ((offset += len) < skb->len);
 
        return segs;