Upstream commit:
    commit f2a4d086ed4c588d32fe9b7aa67fead7280e7bf1
    Author: William Tu <u9012...@gmail.com>
    Date:   Fri Jun 10 11:49:33 2016 -0700

    openvswitch: Add packet truncation support.

    The patch adds a new OVS action, OVS_ACTION_ATTR_TRUNC, in order to
    truncate packets. A 'max_len' is added for setting up the maximum
    packet size, and a 'cutlen' field is to record the number of bytes
    to trim the packet when the packet is outputting to a port, or when
    the packet is sent to userspace.

    Signed-off-by: William Tu <u9012...@gmail.com>
    Cc: Pravin Shelar <pshe...@nicira.com>
    Acked-by: Pravin B Shelar <pshe...@ovn.org>
    Signed-off-by: David S. Miller <da...@davemloft.net>

Signed-off-by: William Tu <u9012...@gmail.com>
Acked-by: Pravin B Shelar <pshe...@ovn.org>
---
 datapath/actions.c                                | 40 ++++++++++++++++++++---
 datapath/datapath.c                               | 29 +++++++++-------
 datapath/datapath.h                               |  5 ++-
 datapath/flow_netlink.c                           |  9 +++++
 datapath/linux/compat/include/linux/openvswitch.h |  6 ++++
 datapath/vport.c                                  |  1 +
 6 files changed, 73 insertions(+), 17 deletions(-)

diff --git a/datapath/actions.c b/datapath/actions.c
index 117bdae..0421410 100644
--- a/datapath/actions.c
+++ b/datapath/actions.c
@@ -744,6 +744,14 @@ static void do_output(struct datapath *dp, struct sk_buff 
*skb, int out_port,
 
        if (likely(vport)) {
                u16 mru = OVS_CB(skb)->mru;
+               u32 cutlen = OVS_CB(skb)->cutlen;
+
+               if (unlikely(cutlen > 0)) {
+                       if (skb->len - cutlen > ETH_HLEN)
+                               pskb_trim(skb, skb->len - cutlen);
+                       else
+                               pskb_trim(skb, ETH_HLEN);
+               }
 
                if (likely(!mru || (skb->len <= mru + ETH_HLEN))) {
                        ovs_vport_send(vport, skb);
@@ -768,7 +776,8 @@ 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,
-                           const struct nlattr *actions, int actions_len)
+                           const struct nlattr *actions, int actions_len,
+                           uint32_t cutlen)
 {
        struct ip_tunnel_info info;
        struct dp_upcall_info upcall;
@@ -818,7 +827,7 @@ static int output_userspace(struct datapath *dp, struct 
sk_buff *skb,
                } /* End of switch. */
        }
 
-       return ovs_dp_upcall(dp, skb, key, &upcall);
+       return ovs_dp_upcall(dp, skb, key, &upcall, cutlen);
 }
 
 static int sample(struct datapath *dp, struct sk_buff *skb,
@@ -828,6 +837,7 @@ static int sample(struct datapath *dp, struct sk_buff *skb,
        const struct nlattr *acts_list = NULL;
        const struct nlattr *a;
        int rem;
+       u32 cutlen = 0;
 
        for (a = nla_data(attr), rem = nla_len(attr); rem > 0;
                 a = nla_next(a, &rem)) {
@@ -854,13 +864,24 @@ static int sample(struct datapath *dp, struct sk_buff 
*skb,
                return 0;
 
        /* The only known usage of sample action is having a single user-space
+        * action, or having a truncate action followed by a single user-space
         * action. Treat this usage as a special case.
         * The output_userspace() should clone the skb to be sent to the
         * user space. This skb will be consumed by its caller.
         */
+       if (unlikely(nla_type(a) == OVS_ACTION_ATTR_TRUNC)) {
+               struct ovs_action_trunc *trunc = nla_data(a);
+
+               if (skb->len > trunc->max_len)
+                       cutlen = skb->len - trunc->max_len;
+
+               a = nla_next(a, &rem);
+       }
+
        if (likely(nla_type(a) == OVS_ACTION_ATTR_USERSPACE &&
                   nla_is_last(a, rem)))
-               return output_userspace(dp, skb, key, a, actions, actions_len);
+               return output_userspace(dp, skb, key, a, actions,
+                                       actions_len, cutlen);
 
        skb = skb_clone(skb, GFP_ATOMIC);
        if (!skb)
@@ -1047,6 +1068,7 @@ static int do_execute_actions(struct datapath *dp, struct 
sk_buff *skb,
                        if (out_skb)
                                do_output(dp, out_skb, prev_port, key);
 
+                       OVS_CB(skb)->cutlen = 0;
                        prev_port = -1;
                }
 
@@ -1055,8 +1077,18 @@ static int do_execute_actions(struct datapath *dp, 
struct sk_buff *skb,
                        prev_port = nla_get_u32(a);
                        break;
 
+               case OVS_ACTION_ATTR_TRUNC: {
+                       struct ovs_action_trunc *trunc = nla_data(a);
+
+                       if (skb->len > trunc->max_len)
+                               OVS_CB(skb)->cutlen = skb->len - trunc->max_len;
+                       break;
+               }
+
                case OVS_ACTION_ATTR_USERSPACE:
-                       output_userspace(dp, skb, key, a, attr, len);
+                       output_userspace(dp, skb, key, a, attr,
+                                                    len, OVS_CB(skb)->cutlen);
+                       OVS_CB(skb)->cutlen = 0;
                        break;
 
                case OVS_ACTION_ATTR_HASH:
diff --git a/datapath/datapath.c b/datapath/datapath.c
index 5bec072..169bbbb 100644
--- a/datapath/datapath.c
+++ b/datapath/datapath.c
@@ -141,10 +141,12 @@ EXPORT_SYMBOL_GPL(lockdep_ovsl_is_held);
 
 static int queue_gso_packets(struct datapath *dp, struct sk_buff *,
                             const struct sw_flow_key *,
-                            const struct dp_upcall_info *);
+                            const struct dp_upcall_info *,
+                            uint32_t cutlen);
 static int queue_userspace_packet(struct datapath *dp, struct sk_buff *,
                                  const struct sw_flow_key *,
-                                 const struct dp_upcall_info *);
+                                 const struct dp_upcall_info *,
+                                 uint32_t cutlen);
 
 /* Must be called with rcu_read_lock. */
 static struct datapath *get_dp_rcu(struct net *net, int dp_ifindex)
@@ -280,7 +282,7 @@ void ovs_dp_process_packet(struct sk_buff *skb, struct 
sw_flow_key *key)
                upcall.cmd = OVS_PACKET_CMD_MISS;
                upcall.portid = ovs_vport_find_upcall_portid(p, skb);
                upcall.mru = OVS_CB(skb)->mru;
-               error = ovs_dp_upcall(dp, skb, key, &upcall);
+               error = ovs_dp_upcall(dp, skb, key, &upcall, 0);
                if (unlikely(error))
                        kfree_skb(skb);
                else
@@ -305,7 +307,8 @@ out:
 
 int ovs_dp_upcall(struct datapath *dp, struct sk_buff *skb,
                  const struct sw_flow_key *key,
-                 const struct dp_upcall_info *upcall_info)
+                 const struct dp_upcall_info *upcall_info,
+                 uint32_t cutlen)
 {
        struct dp_stats_percpu *stats;
        int err;
@@ -316,9 +319,9 @@ int ovs_dp_upcall(struct datapath *dp, struct sk_buff *skb,
        }
 
        if (!skb_is_gso(skb))
-               err = queue_userspace_packet(dp, skb, key, upcall_info);
+               err = queue_userspace_packet(dp, skb, key, upcall_info, cutlen);
        else
-               err = queue_gso_packets(dp, skb, key, upcall_info);
+               err = queue_gso_packets(dp, skb, key, upcall_info, cutlen);
        if (err)
                goto err;
 
@@ -336,7 +339,8 @@ err:
 
 static int queue_gso_packets(struct datapath *dp, struct sk_buff *skb,
                             const struct sw_flow_key *key,
-                            const struct dp_upcall_info *upcall_info)
+                            const struct dp_upcall_info *upcall_info,
+                                uint32_t cutlen)
 {
        unsigned short gso_type = skb_shinfo(skb)->gso_type;
        struct sw_flow_key later_key;
@@ -368,7 +372,7 @@ static int queue_gso_packets(struct datapath *dp, struct 
sk_buff *skb,
                if (gso_type & SKB_GSO_UDP && skb != segs)
                        key = &later_key;
 
-               err = queue_userspace_packet(dp, skb, key, upcall_info);
+               err = queue_userspace_packet(dp, skb, key, upcall_info, cutlen);
                if (err)
                        break;
 
@@ -424,7 +428,8 @@ static void pad_packet(struct datapath *dp, struct sk_buff 
*skb)
 
 static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb,
                                  const struct sw_flow_key *key,
-                                 const struct dp_upcall_info *upcall_info)
+                                 const struct dp_upcall_info *upcall_info,
+                                 uint32_t cutlen)
 {
        struct ovs_header *upcall;
        struct sk_buff *nskb = NULL;
@@ -475,7 +480,7 @@ static int queue_userspace_packet(struct datapath *dp, 
struct sk_buff *skb,
        else
                hlen = skb->len;
 
-       len = upcall_msg_size(upcall_info, hlen);
+       len = upcall_msg_size(upcall_info, hlen - cutlen);
        user_skb = genlmsg_new_unicast(len, &info, GFP_ATOMIC);
        if (!user_skb) {
                err = -ENOMEM;
@@ -532,9 +537,9 @@ static int queue_userspace_packet(struct datapath *dp, 
struct sk_buff *skb,
                err = -ENOBUFS;
                goto out;
        }
-       nla->nla_len = nla_attr_size(skb->len);
+       nla->nla_len = nla_attr_size(skb->len - cutlen);
 
-       err = skb_zerocopy(user_skb, skb, skb->len, hlen);
+       err = skb_zerocopy(user_skb, skb, skb->len - cutlen, hlen);
        if (err)
                goto out;
 
diff --git a/datapath/datapath.h b/datapath/datapath.h
index ceb3372..a70123c 100644
--- a/datapath/datapath.h
+++ b/datapath/datapath.h
@@ -97,11 +97,13 @@ struct datapath {
  * @input_vport: The original vport packet came in on. This value is cached
  * when a packet is received by OVS.
  * @mru: The maximum received fragement size; 0 if the packet is not
+ * @cutlen: The number of bytes from the packet end to be removed.
  * fragmented.
  */
 struct ovs_skb_cb {
        struct vport            *input_vport;
        u16                     mru;
+       u32                     cutlen;
 };
 #define OVS_CB(skb) ((struct ovs_skb_cb *)(skb)->cb)
 
@@ -193,7 +195,8 @@ extern struct genl_multicast_group 
ovs_dp_vport_multicast_group;
 void ovs_dp_process_packet(struct sk_buff *skb, struct sw_flow_key *key);
 void ovs_dp_detach_port(struct vport *);
 int ovs_dp_upcall(struct datapath *, struct sk_buff *,
-                 const struct sw_flow_key *, const struct dp_upcall_info *);
+                 const struct sw_flow_key *, const struct dp_upcall_info *,
+                 uint32_t cutlen);
 
 const char *ovs_dp_name(const struct datapath *dp);
 struct sk_buff *ovs_vport_cmd_build_info(struct vport *, u32 pid, u32 seq,
diff --git a/datapath/flow_netlink.c b/datapath/flow_netlink.c
index 6ffcc53..1defb11 100644
--- a/datapath/flow_netlink.c
+++ b/datapath/flow_netlink.c
@@ -2181,6 +2181,7 @@ static int __ovs_nla_copy_actions(struct net *net, const 
struct nlattr *attr,
                        [OVS_ACTION_ATTR_SAMPLE] = (u32)-1,
                        [OVS_ACTION_ATTR_HASH] = sizeof(struct ovs_action_hash),
                        [OVS_ACTION_ATTR_CT] = (u32)-1,
+                       [OVS_ACTION_ATTR_TRUNC] = sizeof(struct 
ovs_action_trunc),
                };
                const struct ovs_action_push_vlan *vlan;
                int type = nla_type(a);
@@ -2207,6 +2208,14 @@ static int __ovs_nla_copy_actions(struct net *net, const 
struct nlattr *attr,
                                return -EINVAL;
                        break;
 
+               case OVS_ACTION_ATTR_TRUNC: {
+                       const struct ovs_action_trunc *trunc = nla_data(a);
+
+                       if (trunc->max_len < ETH_HLEN)
+                               return -EINVAL;
+                       break;
+               }
+
                case OVS_ACTION_ATTR_HASH: {
                        const struct ovs_action_hash *act_hash = nla_data(a);
 
diff --git a/datapath/linux/compat/include/linux/openvswitch.h 
b/datapath/linux/compat/include/linux/openvswitch.h
index 3b39ebb..b92d9ed 100644
--- a/datapath/linux/compat/include/linux/openvswitch.h
+++ b/datapath/linux/compat/include/linux/openvswitch.h
@@ -600,6 +600,10 @@ enum ovs_userspace_attr {
 
 #define OVS_USERSPACE_ATTR_MAX (__OVS_USERSPACE_ATTR_MAX - 1)
 
+struct ovs_action_trunc {
+       uint32_t max_len; /* Max packet size in bytes. */
+};
+
 /**
  * struct ovs_action_push_mpls - %OVS_ACTION_ATTR_PUSH_MPLS action argument.
  * @mpls_lse: MPLS label stack entry to push.
@@ -742,6 +746,7 @@ enum ovs_nat_attr {
  * enum ovs_action_attr - Action types.
  *
  * @OVS_ACTION_ATTR_OUTPUT: Output packet to port.
+ * @OVS_ACTION_ATTR_TRUNC: Output packet to port with truncated packet size.
  * @OVS_ACTION_ATTR_USERSPACE: Send packet to userspace according to nested
  * %OVS_USERSPACE_ATTR_* attributes.
  * @OVS_ACTION_ATTR_PUSH_VLAN: Push a new outermost 802.1Q header onto the
@@ -802,6 +807,7 @@ enum ovs_action_attr {
                                       * The data must be zero for the unmasked
                                       * bits. */
        OVS_ACTION_ATTR_CT,           /* Nested OVS_CT_ATTR_* . */
+       OVS_ACTION_ATTR_TRUNC,        /* u32 struct ovs_action_trunc. */
 
 #ifndef __KERNEL__
        OVS_ACTION_ATTR_TUNNEL_PUSH,   /* struct ovs_action_push_tnl*/
diff --git a/datapath/vport.c b/datapath/vport.c
index 44b9dfb..3ba4c01 100644
--- a/datapath/vport.c
+++ b/datapath/vport.c
@@ -487,6 +487,7 @@ int ovs_vport_receive(struct vport *vport, struct sk_buff 
*skb,
 
        OVS_CB(skb)->input_vport = vport;
        OVS_CB(skb)->mru = 0;
+       OVS_CB(skb)->cutlen = 0;
        if (unlikely(dev_net(skb->dev) != ovs_dp_get_net(vport->dp))) {
                u32 mark;
 
-- 
2.5.0

_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to