+
+ if (vlan_tx_tag_present(skb) && !dev_supports_vlan_tx(skb->dev)) {
+ int err;
+ int features = 0;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
+ features = skb->dev->features & skb->dev->vlan_features;
+#endif
+
+ err = vswitch_skb_checksum_setup(skb);
+ if (unlikely(err)) {
+ kfree_skb(skb);
+ return 0;
+ }
+
+ if (!vlan_tso)
+ features &= ~(NETIF_F_TSO | NETIF_F_TSO6 |
+ NETIF_F_UFO | NETIF_F_FSO);
+
+ if (skb_is_gso(skb) &&
+ (!skb_gso_ok(skb, features) ||
+ unlikely(skb->ip_summed != CHECKSUM_PARTIAL))) {
+ struct sk_buff *nskb;
+
+ nskb = skb_gso_segment(skb, features);
+ if (!nskb) {
+ if (unlikely(skb_cloned(skb) &&
+ pskb_expand_head(skb, 0, 0, GFP_ATOMIC))) {
+ kfree_skb(skb);
+ return 0;
+ }
+
+ skb_shinfo(skb)->gso_type &= ~SKB_GSO_DODGY;
+ goto tag;
+ }
+
+ kfree_skb(skb);
+ skb = nskb;
+ if (IS_ERR(skb))
+ return 0;
+
+ len = 0;
+ do {
+ nskb = skb->next;
+ skb->next = NULL;
+
+ skb = __vlan_put_tag(skb, vlan_tx_tag_get(skb));
+ if (likely(skb)) {
+ len += skb->len;
+ vlan_set_tci(skb, 0);
+ dev_queue_xmit(skb);
+ }
+
+ skb = nskb;
+ } while (skb);
+
+ return len;
+ }
+
+tag:
+ skb = __vlan_put_tag(skb, vlan_tx_tag_get(skb));
+ if (unlikely(!skb))
+ return 0;
+ vlan_set_tci(skb, 0);
+ }
+
+ len = skb->len;