vswitchd: Make the MAC entry aging time configurable.
[sliver-openvswitch.git] / datapath / tunnel.c
index 7bf46e0..a7d4943 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"
@@ -378,18 +379,14 @@ found:
        return tnl_vport_to_vport(tnl_vport_table_cast(tbl_node));
 }
 
-static inline void ecn_decapsulate(struct sk_buff *skb)
+static void ecn_decapsulate(struct sk_buff *skb, u8 tos)
 {
-       /* This is accessing the outer IP header of the tunnel, which we've
-        * already validated to be OK.  skb->data is currently set to the start
-        * of the inner Ethernet header, and we've validated ETH_HLEN.
-        */
-       if (unlikely(INET_ECN_is_ce(ip_hdr(skb)->tos))) {
+       if (unlikely(INET_ECN_is_ce(tos))) {
                __be16 protocol = skb->protocol;
 
                skb_set_network_header(skb, ETH_HLEN);
 
-               if (skb->protocol == htons(ETH_P_8021Q)) {
+               if (protocol == htons(ETH_P_8021Q)) {
                        if (unlikely(!pskb_may_pull(skb, VLAN_ETH_HLEN)))
                                return;
 
@@ -416,17 +413,27 @@ static inline void ecn_decapsulate(struct sk_buff *skb)
        }
 }
 
-/* Called with rcu_read_lock. */
-void tnl_rcv(struct vport *vport, struct sk_buff *skb)
+/**
+ *     tnl_rcv - ingress point for generic tunnel code
+ *
+ * @vport: port this packet was received on
+ * @skb: received packet
+ * @tos: ToS from encapsulating IP packet, used to copy ECN bits
+ *
+ * Must be called with rcu_read_lock.
+ *
+ * Packets received by this function are in the following state:
+ * - skb->data points to the inner Ethernet header.
+ * - The inner Ethernet header is in the linear data area.
+ * - skb->csum does not include the inner Ethernet header.
+ * - The layer pointers are undefined.
+ */
+void tnl_rcv(struct vport *vport, struct sk_buff *skb, u8 tos)
 {
-       /* Packets received by this function are in the following state:
-        * - skb->data points to the inner Ethernet header.
-        * - The inner Ethernet header is in the linear data area.
-        * - skb->csum does not include the inner Ethernet header.
-        * - The layer pointers point at the outer headers.
-        */
+       struct ethhdr *eh;
 
-       struct ethhdr *eh = (struct ethhdr *)skb->data;
+       skb_reset_mac_header(skb);
+       eh = eth_hdr(skb);
 
        if (likely(ntohs(eh->h_proto) >= 1536))
                skb->protocol = eh->h_proto;
@@ -435,10 +442,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);
+       ecn_decapsulate(skb, tos);
        compute_ip_summed(skb, false);
+       vlan_set_tci(skb, 0);
 
        vport_receive(vport, skb);
 }
@@ -682,7 +691,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,15 +728,29 @@ static bool check_mtu(struct sk_buff *skb,
        bool pmtud = mutable->flags & TNL_F_PMTUD;
        __be16 frag_off = 0;
        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)) {
@@ -737,7 +761,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;
@@ -745,10 +769,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);
@@ -1021,7 +1041,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;
@@ -1041,28 +1061,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)) {
@@ -1071,32 +1081,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;
 
@@ -1110,25 +1111,21 @@ static int send_frags(struct sk_buff *skb,
                      const struct tnl_mutable_config *mutable)
 {
        int sent_len;
-       int err;
 
        sent_len = 0;
        while (skb) {
                struct sk_buff *next = skb->next;
                int frag_len = skb->len - mutable->tunnel_hlen;
+               int err;
 
                skb->next = NULL;
                memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
 
                err = ip_local_out(skb);
-               if (likely(net_xmit_eval(err) == 0))
-                       sent_len += frag_len;
-               else {
-                       skb = next;
-                       goto free_frags;
-               }
-
                skb = next;
+               if (unlikely(net_xmit_eval(err)))
+                       goto free_frags;
+               sent_len += frag_len;
        }
 
        return sent_len;
@@ -1159,7 +1156,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;
 
@@ -1208,6 +1206,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);
@@ -1250,6 +1249,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);
@@ -1318,9 +1320,9 @@ next:
 error_free:
        tnl_free_linked_skbs(skb);
 error:
-       dst_release(unattached_dst);
        vport_record_error(vport, err);
 out:
+       dst_release(unattached_dst);
        return sent_len;
 }
 
@@ -1369,12 +1371,6 @@ static int tnl_set_config(struct nlattr *options, const struct tnl_ops *tnl_ops,
        if (a[ODP_TUNNEL_ATTR_TTL])
                mutable->ttl = nla_get_u8(a[ODP_TUNNEL_ATTR_TTL]);
 
-       mutable->tunnel_hlen = tnl_ops->hdr_len(mutable);
-       if (mutable->tunnel_hlen < 0)
-               return mutable->tunnel_hlen;
-
-       mutable->tunnel_hlen += sizeof(struct iphdr);
-
        mutable->tunnel_type = tnl_ops->tunnel_type;
        if (!a[ODP_TUNNEL_ATTR_IN_KEY]) {
                mutable->tunnel_type |= TNL_T_KEY_MATCH;
@@ -1389,6 +1385,12 @@ static int tnl_set_config(struct nlattr *options, const struct tnl_ops *tnl_ops,
        else
                mutable->out_key = nla_get_be64(a[ODP_TUNNEL_ATTR_OUT_KEY]);
 
+       mutable->tunnel_hlen = tnl_ops->hdr_len(mutable);
+       if (mutable->tunnel_hlen < 0)
+               return mutable->tunnel_hlen;
+
+       mutable->tunnel_hlen += sizeof(struct iphdr);
+
        old_vport = tnl_find_port(mutable->saddr, mutable->daddr,
                                  mutable->in_key, mutable->tunnel_type,
                                  &old_mutable);
@@ -1575,9 +1577,6 @@ const unsigned char *tnl_get_addr(const struct vport *vport)
 
 void tnl_free_linked_skbs(struct sk_buff *skb)
 {
-       if (unlikely(!skb))
-               return;
-
        while (skb) {
                struct sk_buff *next = skb->next;
                kfree_skb(skb);