ovsdb-idl: JSONRPC_REPLY message always has nonnull 'id'.
[sliver-openvswitch.git] / datapath / tunnel.c
index bd3a9e0..a0d9fd9 100644 (file)
@@ -33,6 +33,7 @@
 #include "datapath.h"
 #include "table.h"
 #include "tunnel.h"
+#include "vlan.h"
 #include "vport.h"
 #include "vport-generic.h"
 #include "vport-internal_dev.h"
@@ -435,10 +436,12 @@ void tnl_rcv(struct vport *vport, struct sk_buff *skb)
 
        skb_dst_drop(skb);
        nf_reset(skb);
+       skb_clear_rxhash(skb);
        secpath_reset(skb);
 
        ecn_decapsulate(skb);
        compute_ip_summed(skb, false);
+       vlan_set_tci(skb, 0);
 
        vport_receive(vport, skb);
 }
@@ -663,7 +666,6 @@ bool tnl_frag_needed(struct vport *vport, const struct tnl_mutable_config *mutab
        }
 #endif
 
-       total_length = min(total_length, mutable->mtu);
        payload_length = total_length - header_length;
 
        nskb = dev_alloc_skb(NET_IP_ALIGN + eth_hdr_len + header_length +
@@ -683,7 +685,8 @@ bool tnl_frag_needed(struct vport *vport, const struct tnl_mutable_config *mutab
 
                vh->h_vlan_TCI = vlan_eth_hdr(skb)->h_vlan_TCI;
                vh->h_vlan_encapsulated_proto = skb->protocol;
-       }
+       } else
+               vlan_set_tci(nskb, vlan_get_tci(skb));
        skb_reset_mac_header(nskb);
 
        /* Protocol */
@@ -718,16 +721,30 @@ static bool check_mtu(struct sk_buff *skb,
 {
        bool pmtud = mutable->flags & TNL_F_PMTUD;
        __be16 frag_off = 0;
-       int mtu;
+       int mtu = 0;
+       unsigned int packet_length = skb->len - ETH_HLEN;
+
+       /* Allow for one level of tagging in the packet length. */
+       if (!vlan_tx_tag_present(skb) &&
+           eth_hdr(skb)->h_proto == htons(ETH_P_8021Q))
+               packet_length -= VLAN_HLEN;
 
        if (pmtud) {
+               int vlan_header = 0;
+
                frag_off = htons(IP_DF);
 
+               /* The tag needs to go in packet regardless of where it
+                * currently is, so subtract it from the MTU.
+                */
+               if (vlan_tx_tag_present(skb) ||
+                   eth_hdr(skb)->h_proto == htons(ETH_P_8021Q))
+                       vlan_header = VLAN_HLEN;
+
                mtu = dst_mtu(&rt_dst(rt))
                        - ETH_HLEN
                        - mutable->tunnel_hlen
-                       - (eth_hdr(skb)->h_proto == htons(ETH_P_8021Q) ?
-                               VLAN_HLEN : 0);
+                       - vlan_header;
        }
 
        if (skb->protocol == htons(ETH_P_IP)) {
@@ -738,7 +755,7 @@ static bool check_mtu(struct sk_buff *skb,
                if (pmtud && iph->frag_off & htons(IP_DF)) {
                        mtu = max(mtu, IP_MIN_MTU);
 
-                       if (ntohs(iph->tot_len) > mtu &&
+                       if (packet_length > mtu &&
                            tnl_frag_needed(vport, mutable, skb, mtu,
                                            OVS_CB(skb)->tun_id))
                                return false;
@@ -746,10 +763,6 @@ static bool check_mtu(struct sk_buff *skb,
        }
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
        else if (skb->protocol == htons(ETH_P_IPV6)) {
-               unsigned int packet_length = skb->len - ETH_HLEN
-                       - (eth_hdr(skb)->h_proto == htons(ETH_P_8021Q) ?
-                               VLAN_HLEN : 0);
-
                /* IPv6 requires PMTUD if the packet is above the minimum MTU. */
                if (packet_length > IPV6_MIN_MTU)
                        frag_off = htons(IP_DF);
@@ -1022,7 +1035,7 @@ static inline bool need_linearize(const struct sk_buff *skb)
         * change them from underneath us and we can skip the linearization.
         */
        for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
-               if (unlikely(page_count(skb_shinfo(skb)->frags[0].page) > 1))
+               if (unlikely(page_count(skb_shinfo(skb)->frags[i].page) > 1))
                        return true;
 
        return false;
@@ -1042,28 +1055,18 @@ static struct sk_buff *handle_offloads(struct sk_buff *skb,
                goto error_free;
 
        min_headroom = LL_RESERVED_SPACE(rt_dst(rt).dev) + rt_dst(rt).header_len
-                       + mutable->tunnel_hlen;
+                       + mutable->tunnel_hlen
+                       + (vlan_tx_tag_present(skb) ? VLAN_HLEN : 0);
+
+       skb = check_headroom(skb, min_headroom);
+       if (IS_ERR(skb)) {
+               err = PTR_ERR(skb);
+               goto error;
+       }
 
        if (skb_is_gso(skb)) {
                struct sk_buff *nskb;
 
-               /*
-                * If we are doing GSO on a pskb it is better to make sure that
-                * the headroom is correct now.  We will only have to copy the
-                * portion in the linear data area and GSO will preserve
-                * headroom when it creates the segments.  This is particularly
-                * beneficial on Xen where we get a lot of GSO pskbs.
-                * Conversely, we avoid copying if it is just to get our own
-                * writable clone because GSO will do the copy for us.
-                */
-               if (skb_headroom(skb) < min_headroom) {
-                       skb = check_headroom(skb, min_headroom);
-                       if (IS_ERR(skb)) {
-                               err = PTR_ERR(skb);
-                               goto error;
-                       }
-               }
-
                nskb = skb_gso_segment(skb, 0);
                kfree_skb(skb);
                if (IS_ERR(nskb)) {
@@ -1072,32 +1075,23 @@ static struct sk_buff *handle_offloads(struct sk_buff *skb,
                }
 
                skb = nskb;
-       } else {
-               skb = check_headroom(skb, min_headroom);
-               if (IS_ERR(skb)) {
-                       err = PTR_ERR(skb);
-                       goto error;
-               }
-
-               if (skb->ip_summed == CHECKSUM_PARTIAL) {
-                       /*
-                        * Pages aren't locked and could change at any time.
-                        * If this happens after we compute the checksum, the
-                        * checksum will be wrong.  We linearize now to avoid
-                        * this problem.
-                        */
-                       if (unlikely(need_linearize(skb))) {
-                               err = __skb_linearize(skb);
-                               if (unlikely(err))
-                                       goto error_free;
-                       }
-
-                       err = skb_checksum_help(skb);
+       } else if (skb->ip_summed == CHECKSUM_PARTIAL) {
+               /* Pages aren't locked and could change at any time.
+                * If this happens after we compute the checksum, the
+                * checksum will be wrong.  We linearize now to avoid
+                * this problem.
+                */
+               if (unlikely(need_linearize(skb))) {
+                       err = __skb_linearize(skb);
                        if (unlikely(err))
                                goto error_free;
-               } else if (skb->ip_summed == CHECKSUM_COMPLETE)
-                       skb->ip_summed = CHECKSUM_NONE;
-       }
+               }
+
+               err = skb_checksum_help(skb);
+               if (unlikely(err))
+                       goto error_free;
+       } else if (skb->ip_summed == CHECKSUM_COMPLETE)
+               skb->ip_summed = CHECKSUM_NONE;
 
        return skb;
 
@@ -1160,7 +1154,8 @@ int tnl_send(struct vport *vport, struct sk_buff *skb)
        u8 tos;
 
        /* Validate the protocol headers before we try to use them. */
-       if (skb->protocol == htons(ETH_P_8021Q)) {
+       if (skb->protocol == htons(ETH_P_8021Q) &&
+           !vlan_tx_tag_present(skb)) {
                if (unlikely(!pskb_may_pull(skb, VLAN_ETH_HLEN)))
                        goto error_free;
 
@@ -1209,6 +1204,7 @@ int tnl_send(struct vport *vport, struct sk_buff *skb)
        nf_reset(skb);
        secpath_reset(skb);
        skb_dst_drop(skb);
+       skb_clear_rxhash(skb);
 
        /* Offloading */
        skb = handle_offloads(skb, mutable, rt);
@@ -1251,6 +1247,9 @@ int tnl_send(struct vport *vport, struct sk_buff *skb)
                struct sk_buff *next_skb = skb->next;
                skb->next = NULL;
 
+               if (unlikely(vlan_deaccel_tag(skb)))
+                       goto next;
+
                if (likely(cache)) {
                        skb_push(skb, cache->len);
                        memcpy(skb->data, get_cached_header(cache), cache->len);
@@ -1428,7 +1427,6 @@ struct vport *tnl_create(const struct vport_parms *parms,
        }
 
        vport_gen_rand_ether_addr(mutable->eth_addr);
-       mutable->mtu = ETH_DATA_LEN;
 
        get_random_bytes(&initial_frag_id, sizeof(int));
        atomic_set(&tnl_vport->frag_id, initial_frag_id);
@@ -1477,7 +1475,6 @@ int tnl_set_options(struct vport *vport, struct nlattr *options)
        old_mutable = rtnl_dereference(tnl_vport->mutable);
        mutable->seq = old_mutable->seq + 1;
        memcpy(mutable->eth_addr, old_mutable->eth_addr, ETH_ALEN);
-       mutable->mtu = old_mutable->mtu;
 
        /* Parse the others configured by userspace. */
        err = tnl_set_config(options, tnl_vport->tnl_ops, vport, mutable);
@@ -1548,22 +1545,6 @@ int tnl_destroy(struct vport *vport)
        return 0;
 }
 
-int tnl_set_mtu(struct vport *vport, int mtu)
-{
-       struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
-       struct tnl_mutable_config *mutable;
-
-       mutable = kmemdup(rtnl_dereference(tnl_vport->mutable),
-                         sizeof(struct tnl_mutable_config), GFP_KERNEL);
-       if (!mutable)
-               return -ENOMEM;
-
-       mutable->mtu = mtu;
-       assign_config_rcu(vport, mutable);
-
-       return 0;
-}
-
 int tnl_set_addr(struct vport *vport, const unsigned char *addr)
 {
        struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
@@ -1592,12 +1573,6 @@ const unsigned char *tnl_get_addr(const struct vport *vport)
        return rcu_dereference_rtnl(tnl_vport->mutable)->eth_addr;
 }
 
-int tnl_get_mtu(const struct vport *vport)
-{
-       const struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
-       return rcu_dereference_rtnl(tnl_vport->mutable)->mtu;
-}
-
 void tnl_free_linked_skbs(struct sk_buff *skb)
 {
        if (unlikely(!skb))