datapath: Fix vxlan udp csum of gso packet
[cascardo/ovs.git] / datapath / actions.c
index 52d7213..c529bbb 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/in6.h>
 #include <linux/if_arp.h>
 #include <linux/if_vlan.h>
+
 #include <net/ip.h>
 #include <net/ipv6.h>
 #include <net/checksum.h>
@@ -167,10 +168,7 @@ static int pop_mpls(struct sk_buff *skb, struct sw_flow_key *key,
        if (unlikely(err))
                return err;
 
-       if (skb->ip_summed == CHECKSUM_COMPLETE)
-               skb->csum = csum_sub(skb->csum,
-                                    csum_partial(skb_mpls_header(skb),
-                                                 MPLS_HLEN, 0));
+       skb_postpull_rcsum(skb, skb_mpls_header(skb), MPLS_HLEN);
 
        memmove(skb_mac_header(skb) + MPLS_HLEN, skb_mac_header(skb),
                skb->mac_len);
@@ -228,7 +226,6 @@ static int pop_vlan(struct sk_buff *skb, struct sw_flow_key *key)
                invalidate_flow_key(key);
        else
                key->eth.tci = 0;
-
        return err;
 }
 
@@ -239,7 +236,6 @@ static int push_vlan(struct sk_buff *skb, struct sw_flow_key *key,
                invalidate_flow_key(key);
        else
                key->eth.tci = vlan->vlan_tci;
-
        return skb_vlan_push(skb, vlan->vlan_tpid,
                             ntohs(vlan->vlan_tci) & ~VLAN_TAG_PRESENT);
 }
@@ -280,28 +276,37 @@ static int set_eth_addr(struct sk_buff *skb, struct sw_flow_key *flow_key,
        return 0;
 }
 
-static void set_ip_addr(struct sk_buff *skb, struct iphdr *nh,
-                       __be32 *addr, __be32 new_addr)
+static void update_ip_l4_checksum(struct sk_buff *skb, struct iphdr *nh,
+                                 __be32 addr, __be32 new_addr)
 {
        int transport_len = skb->len - skb_transport_offset(skb);
 
+       if (nh->frag_off & htons(IP_OFFSET))
+               return;
+
        if (nh->protocol == IPPROTO_TCP) {
                if (likely(transport_len >= sizeof(struct tcphdr)))
                        inet_proto_csum_replace4(&tcp_hdr(skb)->check, skb,
-                                                *addr, new_addr, 1);
+                                                addr, new_addr, 1);
        } else if (nh->protocol == IPPROTO_UDP) {
                if (likely(transport_len >= sizeof(struct udphdr))) {
                        struct udphdr *uh = udp_hdr(skb);
 
                        if (uh->check || skb->ip_summed == CHECKSUM_PARTIAL) {
                                inet_proto_csum_replace4(&uh->check, skb,
-                                                        *addr, new_addr, 1);
+                                                        addr, new_addr, 1);
                                if (!uh->check)
                                        uh->check = CSUM_MANGLED_0;
                        }
                }
        }
 
+}
+
+static void set_ip_addr(struct sk_buff *skb, struct iphdr *nh,
+                       __be32 *addr, __be32 new_addr)
+{
+       update_ip_l4_checksum(skb, nh, *addr, new_addr);
        csum_replace4(&nh->check, *addr, new_addr);
        skb_clear_hash(skb);
        *addr = new_addr;
@@ -557,7 +562,6 @@ static int set_tcp(struct sk_buff *skb, struct sw_flow_key *flow_key,
                return err;
 
        th = tcp_hdr(skb);
-
        src = MASKED(th->source, key->tcp_src, mask->tcp_src);
        if (likely(src != th->source)) {
                set_tp_port(skb, &th->source, src, &th->check);
@@ -587,7 +591,6 @@ static int set_sctp(struct sk_buff *skb, struct sw_flow_key *flow_key,
                return err;
 
        sh = sctp_hdr(skb);
-
        old_csum = sh->checksum;
        old_correct_csum = sctp_compute_cksum(skb, sctphoff);
 
@@ -617,17 +620,16 @@ static void do_output(struct datapath *dp, struct sk_buff *skb, int out_port)
 }
 
 static int output_userspace(struct datapath *dp, struct sk_buff *skb,
-                           struct sw_flow_key *key, const struct nlattr *attr)
+                           struct sw_flow_key *key, const struct nlattr *attr,
+                           const struct nlattr *actions, int actions_len)
 {
        struct ovs_tunnel_info info;
        struct dp_upcall_info upcall;
        const struct nlattr *a;
        int rem;
 
+       memset(&upcall, 0, sizeof(upcall));
        upcall.cmd = OVS_PACKET_CMD_ACTION;
-       upcall.userdata = NULL;
-       upcall.portid = 0;
-       upcall.egress_tun_info = NULL;
 
        for (a = nla_data(attr), rem = nla_len(attr); rem > 0;
                 a = nla_next(a, &rem)) {
@@ -656,6 +658,13 @@ static int output_userspace(struct datapath *dp, struct sk_buff *skb,
                        break;
                }
 
+               case OVS_USERSPACE_ATTR_ACTIONS: {
+                       /* Include actions. */
+                       upcall.actions = actions;
+                       upcall.actions_len = actions_len;
+                       break;
+               }
+
                } /* End of switch. */
        }
 
@@ -663,7 +672,8 @@ static int output_userspace(struct datapath *dp, struct sk_buff *skb,
 }
 
 static int sample(struct datapath *dp, struct sk_buff *skb,
-                 struct sw_flow_key *key, const struct nlattr *attr)
+                 struct sw_flow_key *key, const struct nlattr *attr,
+                 const struct nlattr *actions, int actions_len)
 {
        const struct nlattr *acts_list = NULL;
        const struct nlattr *a;
@@ -671,9 +681,12 @@ static int sample(struct datapath *dp, struct sk_buff *skb,
 
        for (a = nla_data(attr), rem = nla_len(attr); rem > 0;
                 a = nla_next(a, &rem)) {
+               u32 probability;
+
                switch (nla_type(a)) {
                case OVS_SAMPLE_ATTR_PROBABILITY:
-                       if (prandom_u32() >= nla_get_u32(a))
+                       probability = nla_get_u32(a);
+                       if (!probability || prandom_u32() > probability)
                                return 0;
                        break;
 
@@ -697,7 +710,7 @@ static int sample(struct datapath *dp, struct sk_buff *skb,
         */
        if (likely(nla_type(a) == OVS_ACTION_ATTR_USERSPACE &&
                   nla_is_last(a, rem)))
-               return output_userspace(dp, skb, key, a);
+               return output_userspace(dp, skb, key, a, actions, actions_len);
 
        skb = skb_clone(skb, GFP_ATOMIC);
        if (!skb)
@@ -740,7 +753,6 @@ static int execute_set_action(struct sk_buff *skb,
        }
 
        return -EINVAL;
-
 }
 
 /* Mask is at the midpoint of the data. */
@@ -882,7 +894,7 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
                        break;
 
                case OVS_ACTION_ATTR_USERSPACE:
-                       output_userspace(dp, skb, key, a);
+                       output_userspace(dp, skb, key, a, attr, len);
                        break;
 
                case OVS_ACTION_ATTR_HASH:
@@ -926,7 +938,7 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
                        break;
 
                case OVS_ACTION_ATTR_SAMPLE:
-                       err = sample(dp, skb, key, a);
+                       err = sample(dp, skb, key, a, attr, len);
                        break;
                }