datapath: Fix tunnel source port selection for mega flow
[sliver-openvswitch.git] / datapath / tunnel.c
index 0cc0607..ef46a69 100644 (file)
 #include "vlan.h"
 #include "vport.h"
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)
-#define rt_dst(rt) (rt->dst)
-#else
-#define rt_dst(rt) (rt->u.dst)
-#endif
-
 /**
  *     ovs_tnl_rcv - ingress point for generic tunnel code
  *
@@ -58,7 +52,8 @@
  * - skb->csum does not include the inner Ethernet header.
  * - The layer pointers are undefined.
  */
-void ovs_tnl_rcv(struct vport *vport, struct sk_buff *skb)
+void ovs_tnl_rcv(struct vport *vport, struct sk_buff *skb,
+                struct ovs_key_ipv4_tunnel *tun_key)
 {
        struct ethhdr *eh;
 
@@ -81,12 +76,12 @@ void ovs_tnl_rcv(struct vport *vport, struct sk_buff *skb)
                return;
        }
 
-       ovs_vport_receive(vport, skb);
+       ovs_vport_receive(vport, skb, tun_key);
 }
 
-static struct rtable *find_route(struct net *net,
-               __be32 *saddr, __be32 daddr, u8 ipproto,
-               u8 tos, u32 skb_mark)
+struct rtable *find_route(struct net *net,
+                         __be32 *saddr, __be32 daddr, u8 ipproto,
+                         u8 tos, u32 skb_mark)
 {
        struct rtable *rt;
        /* Tunnel configuration keeps DSCP part of TOS bits, But Linux
@@ -152,7 +147,6 @@ static struct sk_buff *handle_offloads(struct sk_buff *skb)
 
                nskb = __skb_gso_segment(skb, 0, false);
                if (IS_ERR(nskb)) {
-                       kfree_skb(skb);
                        err = PTR_ERR(nskb);
                        goto error;
                }
@@ -168,20 +162,18 @@ static struct sk_buff *handle_offloads(struct sk_buff *skb)
                if (unlikely(need_linearize(skb))) {
                        err = __skb_linearize(skb);
                        if (unlikely(err))
-                               goto error_free;
+                               goto error;
                }
 
                err = skb_checksum_help(skb);
                if (unlikely(err))
-                       goto error_free;
+                       goto error;
        }
 
        set_ip_summed(skb, OVS_CSUM_NONE);
 
        return skb;
 
-error_free:
-       kfree_skb(skb);
 error:
        return ERR_PTR(err);
 }
@@ -194,7 +186,9 @@ u16 ovs_tnl_get_src_port(struct sk_buff *skb)
        int low;
        int high;
        unsigned int range;
-       u32 hash = OVS_CB(skb)->flow->hash;
+       struct sw_flow_key *pkt_key = OVS_CB(skb)->pkt_key;
+       u32 hash = jhash2((const u32 *)pkt_key,
+                         sizeof(*pkt_key) / sizeof(u32), 0);
 
        inet_get_local_port_range(&low, &high);
        range = (high - low) + 1;
@@ -211,6 +205,8 @@ int ovs_tnl_send(struct vport *vport, struct sk_buff *skb,
        struct rtable *rt;
        __be32 saddr;
        int sent_len = 0;
+       int err;
+       struct sk_buff *nskb;
 
        /* Route lookup */
        saddr = OVS_CB(skb)->tun_key->ipv4_src;
@@ -220,8 +216,10 @@ int ovs_tnl_send(struct vport *vport, struct sk_buff *skb,
                        ipproto,
                        OVS_CB(skb)->tun_key->ipv4_tos,
                        skb_get_mark(skb));
-       if (IS_ERR(rt))
-               goto error_free;
+       if (IS_ERR(rt)) {
+               err = PTR_ERR(rt);
+               goto error;
+       }
 
        tunnel_hlen += sizeof(struct iphdr);
 
@@ -230,7 +228,6 @@ int ovs_tnl_send(struct vport *vport, struct sk_buff *skb,
                        + (vlan_tx_tag_present(skb) ? VLAN_HLEN : 0);
 
        if (skb_headroom(skb) < min_headroom || skb_header_cloned(skb)) {
-               int err;
                int head_delta = SKB_DATA_ALIGN(min_headroom -
                                                skb_headroom(skb) +
                                                16);
@@ -242,11 +239,12 @@ int ovs_tnl_send(struct vport *vport, struct sk_buff *skb,
        }
 
        /* Offloading */
-       skb = handle_offloads(skb);
-       if (IS_ERR(skb)) {
-               skb = NULL;
+       nskb = handle_offloads(skb);
+       if (IS_ERR(nskb)) {
+               err = PTR_ERR(nskb);
                goto err_free_rt;
        }
+       skb = nskb;
 
        /* Reset SKB */
        nf_reset(skb);
@@ -258,7 +256,6 @@ int ovs_tnl_send(struct vport *vport, struct sk_buff *skb,
                struct sk_buff *next_skb = skb->next;
                struct iphdr *iph;
                int frag_len;
-               int err;
 
                skb->next = NULL;
 
@@ -288,7 +285,7 @@ int ovs_tnl_send(struct vport *vport, struct sk_buff *skb,
                iph->tos        = OVS_CB(skb)->tun_key->ipv4_tos;
                iph->ttl        = OVS_CB(skb)->tun_key->ipv4_ttl;
                iph->frag_off   = OVS_CB(skb)->tun_key->tun_flags &
-                                 OVS_TNL_F_DONT_FRAGMENT ?  htons(IP_DF) : 0;
+                                 TUNNEL_DONT_FRAGMENT ?  htons(IP_DF) : 0;
                /*
                 * Allow our local IP stack to fragment the outer packet even
                 * if the DF bit is set as a last resort.  We also need to
@@ -311,15 +308,10 @@ next:
                skb = next_skb;
        }
 
-       if (unlikely(sent_len == 0))
-               ovs_vport_record_error(vport, VPORT_E_TX_DROPPED);
-
        return sent_len;
 
 err_free_rt:
        ip_rt_put(rt);
-error_free:
-       kfree_skb(skb);
-       ovs_vport_record_error(vport, VPORT_E_TX_ERROR);
-       return sent_len;
+error:
+       return err;
 }