]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - include/linux/if_vlan.h
linux/bitmap.h: fix type of nbits in bitmap_shift_right()
[mirror_ubuntu-bionic-kernel.git] / include / linux / if_vlan.h
index 5e6a2d4dc366033772027940b5c4b742e17d0173..87b8c20d5b27cf1b3f01ade2b0ae27fd2a9a61aa 100644 (file)
@@ -300,32 +300,47 @@ static inline bool vlan_hw_offload_capable(netdev_features_t features,
 }
 
 /**
- * __vlan_insert_tag - regular VLAN tag inserting
+ * __vlan_insert_inner_tag - inner VLAN tag inserting
  * @skb: skbuff to tag
  * @vlan_proto: VLAN encapsulation protocol
  * @vlan_tci: VLAN TCI to insert
+ * @mac_len: MAC header length including outer vlan headers
  *
- * Inserts the VLAN tag into @skb as part of the payload
+ * Inserts the VLAN tag into @skb as part of the payload at offset mac_len
  * Returns error if skb_cow_head failes.
  *
  * Does not change skb->protocol so this function can be used during receive.
  */
-static inline int __vlan_insert_tag(struct sk_buff *skb,
-                                   __be16 vlan_proto, u16 vlan_tci)
+static inline int __vlan_insert_inner_tag(struct sk_buff *skb,
+                                         __be16 vlan_proto, u16 vlan_tci,
+                                         unsigned int mac_len)
 {
        struct vlan_ethhdr *veth;
 
        if (skb_cow_head(skb, VLAN_HLEN) < 0)
                return -ENOMEM;
 
-       veth = skb_push(skb, VLAN_HLEN);
+       skb_push(skb, VLAN_HLEN);
 
-       /* Move the mac addresses to the beginning of the new header. */
-       memmove(skb->data, skb->data + VLAN_HLEN, 2 * ETH_ALEN);
+       /* Move the mac header sans proto to the beginning of the new header. */
+       if (likely(mac_len > ETH_TLEN))
+               memmove(skb->data, skb->data + VLAN_HLEN, mac_len - ETH_TLEN);
        skb->mac_header -= VLAN_HLEN;
 
+       veth = (struct vlan_ethhdr *)(skb->data + mac_len - ETH_HLEN);
+
        /* first, the ethernet type */
-       veth->h_vlan_proto = vlan_proto;
+       if (likely(mac_len >= ETH_TLEN)) {
+               /* h_vlan_encapsulated_proto should already be populated, and
+                * skb->data has space for h_vlan_proto
+                */
+               veth->h_vlan_proto = vlan_proto;
+       } else {
+               /* h_vlan_encapsulated_proto should not be populated, and
+                * skb->data has no space for h_vlan_proto
+                */
+               veth->h_vlan_encapsulated_proto = skb->protocol;
+       }
 
        /* now, the TCI */
        veth->h_vlan_TCI = htons(vlan_tci);
@@ -334,12 +349,30 @@ static inline int __vlan_insert_tag(struct sk_buff *skb,
 }
 
 /**
- * vlan_insert_tag - regular VLAN tag inserting
+ * __vlan_insert_tag - regular VLAN tag inserting
  * @skb: skbuff to tag
  * @vlan_proto: VLAN encapsulation protocol
  * @vlan_tci: VLAN TCI to insert
  *
  * Inserts the VLAN tag into @skb as part of the payload
+ * Returns error if skb_cow_head failes.
+ *
+ * Does not change skb->protocol so this function can be used during receive.
+ */
+static inline int __vlan_insert_tag(struct sk_buff *skb,
+                                   __be16 vlan_proto, u16 vlan_tci)
+{
+       return __vlan_insert_inner_tag(skb, vlan_proto, vlan_tci, ETH_HLEN);
+}
+
+/**
+ * vlan_insert_inner_tag - inner VLAN tag inserting
+ * @skb: skbuff to tag
+ * @vlan_proto: VLAN encapsulation protocol
+ * @vlan_tci: VLAN TCI to insert
+ * @mac_len: MAC header length including outer vlan headers
+ *
+ * Inserts the VLAN tag into @skb as part of the payload at offset mac_len
  * Returns a VLAN tagged skb. If a new skb is created, @skb is freed.
  *
  * Following the skb_unshare() example, in case of error, the calling function
@@ -347,12 +380,14 @@ static inline int __vlan_insert_tag(struct sk_buff *skb,
  *
  * Does not change skb->protocol so this function can be used during receive.
  */
-static inline struct sk_buff *vlan_insert_tag(struct sk_buff *skb,
-                                             __be16 vlan_proto, u16 vlan_tci)
+static inline struct sk_buff *vlan_insert_inner_tag(struct sk_buff *skb,
+                                                   __be16 vlan_proto,
+                                                   u16 vlan_tci,
+                                                   unsigned int mac_len)
 {
        int err;
 
-       err = __vlan_insert_tag(skb, vlan_proto, vlan_tci);
+       err = __vlan_insert_inner_tag(skb, vlan_proto, vlan_tci, mac_len);
        if (err) {
                dev_kfree_skb_any(skb);
                return NULL;
@@ -360,6 +395,26 @@ static inline struct sk_buff *vlan_insert_tag(struct sk_buff *skb,
        return skb;
 }
 
+/**
+ * vlan_insert_tag - regular VLAN tag inserting
+ * @skb: skbuff to tag
+ * @vlan_proto: VLAN encapsulation protocol
+ * @vlan_tci: VLAN TCI to insert
+ *
+ * Inserts the VLAN tag into @skb as part of the payload
+ * Returns a VLAN tagged skb. If a new skb is created, @skb is freed.
+ *
+ * Following the skb_unshare() example, in case of error, the calling function
+ * doesn't have to worry about freeing the original skb.
+ *
+ * Does not change skb->protocol so this function can be used during receive.
+ */
+static inline struct sk_buff *vlan_insert_tag(struct sk_buff *skb,
+                                             __be16 vlan_proto, u16 vlan_tci)
+{
+       return vlan_insert_inner_tag(skb, vlan_proto, vlan_tci, ETH_HLEN);
+}
+
 /**
  * vlan_insert_tag_set_proto - regular VLAN tag inserting
  * @skb: skbuff to tag
@@ -584,7 +639,7 @@ static inline bool skb_vlan_tagged(const struct sk_buff *skb)
  * Returns true if the skb is tagged with multiple vlan headers, regardless
  * of whether it is hardware accelerated or not.
  */
-static inline bool skb_vlan_tagged_multi(const struct sk_buff *skb)
+static inline bool skb_vlan_tagged_multi(struct sk_buff *skb)
 {
        __be16 protocol = skb->protocol;
 
@@ -594,6 +649,9 @@ static inline bool skb_vlan_tagged_multi(const struct sk_buff *skb)
                if (likely(!eth_type_vlan(protocol)))
                        return false;
 
+               if (unlikely(!pskb_may_pull(skb, VLAN_ETH_HLEN)))
+                       return false;
+
                veh = (struct vlan_ethhdr *)skb->data;
                protocol = veh->h_vlan_encapsulated_proto;
        }
@@ -611,7 +669,7 @@ static inline bool skb_vlan_tagged_multi(const struct sk_buff *skb)
  *
  * Returns features without unsafe ones if the skb has multiple tags.
  */
-static inline netdev_features_t vlan_features_check(const struct sk_buff *skb,
+static inline netdev_features_t vlan_features_check(struct sk_buff *skb,
                                                    netdev_features_t features)
 {
        if (skb_vlan_tagged_multi(skb)) {