Prepare Open vSwitch 1.1.2 release.
[sliver-openvswitch.git] / datapath / tunnel.c
index 95492f7..899d1cd 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 */
@@ -720,17 +730,27 @@ static bool check_mtu(struct sk_buff *skb,
        int mtu = 0;
        unsigned int packet_length = skb->len - ETH_HLEN;
 
-       if (eth_hdr(skb)->h_proto == htons(ETH_P_8021Q))
+       /* 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)) {
@@ -784,7 +804,7 @@ static void create_tunnel_header(const struct vport *vport,
        iph->saddr      = rt->rt_src;
        iph->ttl        = mutable->ttl;
        if (!iph->ttl)
-               iph->ttl = dst_metric(&rt_dst(rt), RTAX_HOPLIMIT);
+               iph->ttl = ip4_dst_hoplimit(&rt_dst(rt));
 
        tnl_vport->tnl_ops->build_header(vport, mutable, iph + 1);
 }
@@ -971,6 +991,7 @@ static struct rtable *find_route(struct vport *vport,
                return cur_cache->rt;
        } else {
                struct rtable *rt;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)
                struct flowi fl = { .nl_u = { .ip4_u =
                                              { .daddr = mutable->daddr,
                                                .saddr = mutable->saddr,
@@ -979,6 +1000,16 @@ static struct rtable *find_route(struct vport *vport,
 
                if (unlikely(ip_route_output_key(&init_net, &rt, &fl)))
                        return NULL;
+#else
+               struct flowi4 fl = { .daddr = mutable->daddr,
+                                    .saddr = mutable->saddr,
+                                    .flowi4_tos = tos,
+                                    .flowi4_proto = tnl_vport->tnl_ops->ipproto };
+
+               rt = ip_route_output_key(&init_net, &fl);
+               if (IS_ERR(rt))
+                       return NULL;
+#endif
 
                if (likely(tos == mutable->tos))
                        *cache = build_cache(vport, mutable, rt);
@@ -1041,28 +1072,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 +1092,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 +1122,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 +1167,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 +1217,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);
@@ -1234,7 +1244,7 @@ int tnl_send(struct vport *vport, struct sk_buff *skb)
        /* TTL */
        ttl = mutable->ttl;
        if (!ttl)
-               ttl = dst_metric(&rt_dst(rt), RTAX_HOPLIMIT);
+               ttl = ip4_dst_hoplimit(&rt_dst(rt));
 
        if (mutable->flags & TNL_F_TTL_INHERIT) {
                if (skb->protocol == htons(ETH_P_IP))
@@ -1250,6 +1260,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 +1331,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 +1382,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 +1396,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 +1588,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);