diff mbox

[ovs-dev,PATCHv2] ofproto-dpif-sflow: Add snaplen for sample action and sFlow.

Message ID 1467402182-18093-1-git-send-email-u9012063@gmail.com
State Changes Requested
Headers show

Commit Message

William Tu July 1, 2016, 7:43 p.m. UTC
This patch adds a 'snaplen' field in sample action (nx_action_sample2).
Currently, sample action is used by sFlow and IPFIX. For IPFIX, nothing
is changed.  For sFlow configuration, the patch translates header=N to
a sample action with snaplen=N, then the snaplen=N translates to trunc(N)
in kernel datapath.  Thus, only N bytes instead of full-packet size will
be copied from kernel to userspace, saving the copying overhead.

Also, the patch parses OVS_PACKET_ATTR_LEN from nlattr to upcall related
structures so the sFlow receiver knows the original packet size before it
is truncated.

Tested-at: https://travis-ci.org/williamtu/ovs-travis/builds/141704344
Signed-off-by: William Tu <u9012063@gmail.com>
---
v1->v2
http://openvswitch.org/pipermail/dev/2016-June/072404.html
- Add snaplen to nx_action_sample2
- Since truncate then userspace action is supported in userspace datapath,
  ,commit aaca4fe0ce9e (ofp-actions: Add truncate action.), remove
  datapath-specific implementation and testcase.
---
 include/openvswitch/ofp-actions.h |  1 +
 lib/dpif-netlink.c                |  4 +++-
 lib/dpif.h                        |  2 +-
 lib/ofp-actions.c                 | 12 +++++++++++-
 ofproto/ofproto-dpif-sflow.c      | 16 +++++++++++++---
 ofproto/ofproto-dpif-sflow.h      |  2 ++
 ofproto/ofproto-dpif-upcall.c     | 21 ++++++++++++++++-----
 ofproto/ofproto-dpif-xlate.c      | 22 ++++++++++++++++++++--
 tests/ofp-actions.at              |  4 ++--
 tests/ovs-ofctl.at                | 24 ++++++++++++------------
 tests/system-traffic.at           | 31 +++++++++++++++++++++++++++++++
 11 files changed, 112 insertions(+), 27 deletions(-)

Comments

Ben Pfaff July 2, 2016, 4:28 a.m. UTC | #1
On Fri, Jul 01, 2016 at 12:43:02PM -0700, William Tu wrote:
> This patch adds a 'snaplen' field in sample action (nx_action_sample2).
> Currently, sample action is used by sFlow and IPFIX. For IPFIX, nothing
> is changed.  For sFlow configuration, the patch translates header=N to
> a sample action with snaplen=N, then the snaplen=N translates to trunc(N)
> in kernel datapath.  Thus, only N bytes instead of full-packet size will
> be copied from kernel to userspace, saving the copying overhead.
> 
> Also, the patch parses OVS_PACKET_ATTR_LEN from nlattr to upcall related
> structures so the sFlow receiver knows the original packet size before it
> is truncated.
> 
> Tested-at: https://travis-ci.org/williamtu/ovs-travis/builds/141704344
> Signed-off-by: William Tu <u9012063@gmail.com>
> ---
> v1->v2
> http://openvswitch.org/pipermail/dev/2016-June/072404.html
> - Add snaplen to nx_action_sample2
> - Since truncate then userspace action is supported in userspace datapath,
>   ,commit aaca4fe0ce9e (ofp-actions: Add truncate action.), remove
>   datapath-specific implementation and testcase.

This changes the OpenFlow sample action but it doesn't document the new
feature in ovs-ofctl(8).

The new features should be mentioned in NEWS.

In encode_SAMPLE(), NXAST_RAW_SAMPLE2 should also be selected if
nas->snaplen != 0.

In format_SAMPLE(), I would only output snaplen if it is nonzero (and
not UINT16_MAX?).

The datapath will reject a snaplen less than 14, but I don't see
anything here that prevents userspace from trying such a snaplen.

Thanks,

Ben.
William Tu July 5, 2016, 10:10 p.m. UTC | #2
Hi Ben,

Thanks, I've fixed it and submitted new version.

Regards,
William


On Fri, Jul 1, 2016 at 9:28 PM, Ben Pfaff <blp@ovn.org> wrote:
> On Fri, Jul 01, 2016 at 12:43:02PM -0700, William Tu wrote:
>> This patch adds a 'snaplen' field in sample action (nx_action_sample2).
>> Currently, sample action is used by sFlow and IPFIX. For IPFIX, nothing
>> is changed.  For sFlow configuration, the patch translates header=N to
>> a sample action with snaplen=N, then the snaplen=N translates to trunc(N)
>> in kernel datapath.  Thus, only N bytes instead of full-packet size will
>> be copied from kernel to userspace, saving the copying overhead.
>>
>> Also, the patch parses OVS_PACKET_ATTR_LEN from nlattr to upcall related
>> structures so the sFlow receiver knows the original packet size before it
>> is truncated.
>>
>> Tested-at: https://travis-ci.org/williamtu/ovs-travis/builds/141704344
>> Signed-off-by: William Tu <u9012063@gmail.com>
>> ---
>> v1->v2
>> http://openvswitch.org/pipermail/dev/2016-June/072404.html
>> - Add snaplen to nx_action_sample2
>> - Since truncate then userspace action is supported in userspace datapath,
>>   ,commit aaca4fe0ce9e (ofp-actions: Add truncate action.), remove
>>   datapath-specific implementation and testcase.
>
> This changes the OpenFlow sample action but it doesn't document the new
> feature in ovs-ofctl(8).
>
> The new features should be mentioned in NEWS.
>
> In encode_SAMPLE(), NXAST_RAW_SAMPLE2 should also be selected if
> nas->snaplen != 0.
>
> In format_SAMPLE(), I would only output snaplen if it is nonzero (and
> not UINT16_MAX?).
>
> The datapath will reject a snaplen less than 14, but I don't see
> anything here that prevents userspace from trying such a snaplen.
>
> Thanks,
>
> Ben.
diff mbox

Patch

diff --git a/include/openvswitch/ofp-actions.h b/include/openvswitch/ofp-actions.h
index 0b8ccbb..cfecc25 100644
--- a/include/openvswitch/ofp-actions.h
+++ b/include/openvswitch/ofp-actions.h
@@ -789,6 +789,7 @@  struct ofpact_note {
 struct ofpact_sample {
     struct ofpact ofpact;
     uint16_t probability;  /* Always positive. */
+    uint16_t snaplen;
     uint32_t collector_set_id;
     uint32_t obs_domain_id;
     uint32_t obs_point_id;
diff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c
index 9bff3a8..8455bc4 100644
--- a/lib/dpif-netlink.c
+++ b/lib/dpif-netlink.c
@@ -1969,7 +1969,8 @@  parse_odp_packet(const struct dpif_netlink *dpif, struct ofpbuf *buf,
         [OVS_PACKET_ATTR_USERDATA] = { .type = NL_A_UNSPEC, .optional = true },
         [OVS_PACKET_ATTR_EGRESS_TUN_KEY] = { .type = NL_A_NESTED, .optional = true },
         [OVS_PACKET_ATTR_ACTIONS] = { .type = NL_A_NESTED, .optional = true },
-        [OVS_PACKET_ATTR_MRU] = { .type = NL_A_U16, .optional = true }
+        [OVS_PACKET_ATTR_MRU] = { .type = NL_A_U16, .optional = true },
+        [OVS_PACKET_ATTR_LEN] = { .type = NL_A_U32, .optional = true },
     };
 
     struct ofpbuf b = ofpbuf_const_initializer(buf->data, buf->size);
@@ -2002,6 +2003,7 @@  parse_odp_packet(const struct dpif_netlink *dpif, struct ofpbuf *buf,
     upcall->out_tun_key = a[OVS_PACKET_ATTR_EGRESS_TUN_KEY];
     upcall->actions = a[OVS_PACKET_ATTR_ACTIONS];
     upcall->mru = a[OVS_PACKET_ATTR_MRU];
+    upcall->len = a[OVS_PACKET_ATTR_LEN];
 
     /* Allow overwriting the netlink attribute header without reallocating. */
     dp_packet_use_stub(&upcall->packet,
diff --git a/lib/dpif.h b/lib/dpif.h
index 981868c..070d2f9 100644
--- a/lib/dpif.h
+++ b/lib/dpif.h
@@ -784,7 +784,7 @@  struct dpif_upcall {
     size_t key_len;             /* Length of 'key' in bytes. */
     ovs_u128 ufid;              /* Unique flow identifier for 'key'. */
     struct nlattr *mru;         /* Maximum receive unit. */
-    struct nlattr *cutlen;      /* Number of bytes shrink from the end. */
+    struct nlattr *len;         /* Original packet length. */
 
     /* DPIF_UC_ACTION only. */
     struct nlattr *userdata;    /* Argument to OVS_ACTION_ATTR_USERSPACE. */
diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
index 997cc15..5b4bb0d 100644
--- a/lib/ofp-actions.c
+++ b/lib/ofp-actions.c
@@ -4756,7 +4756,8 @@  struct nx_action_sample2 {
     ovs_be32 obs_domain_id;         /* ID of sampling observation domain. */
     ovs_be32 obs_point_id;          /* ID of sampling observation point. */
     ovs_be16 sampling_port;         /* Sampling port. */
-    uint8_t  pad[6];                /* Pad to a multiple of 8 bytes */
+    ovs_be16 snaplen;               /* Max sampled packet size in byte. */
+    uint8_t  pad[4];                /* Pad to a multiple of 8 bytes */
  };
  OFP_ASSERT(sizeof(struct nx_action_sample2) == 32);
 
@@ -4797,6 +4798,7 @@  decode_NXAST_RAW_SAMPLE2(const struct nx_action_sample2 *nas,
     sample->obs_domain_id = ntohl(nas->obs_domain_id);
     sample->obs_point_id = ntohl(nas->obs_point_id);
     sample->sampling_port = u16_to_ofp(ntohs(nas->sampling_port));
+    sample->snaplen = ntohs(nas->snaplen);
 
     if (sample->probability == 0) {
         return OFPERR_OFPBAC_BAD_ARGUMENT;
@@ -4813,6 +4815,7 @@  encode_SAMPLE(const struct ofpact_sample *sample,
         || sample->sampling_port != OFPP_NONE) {
         struct nx_action_sample2 *nas = put_NXAST_SAMPLE2(out);
         nas->probability = htons(sample->probability);
+        nas->snaplen = htons(sample->snaplen);
         nas->collector_set_id = htonl(sample->collector_set_id);
         nas->obs_domain_id = htonl(sample->obs_domain_id);
         nas->obs_point_id = htonl(sample->obs_point_id);
@@ -4857,6 +4860,8 @@  parse_SAMPLE(char *arg, struct ofpbuf *ofpacts,
             if (!ofputil_port_from_string(value, &os->sampling_port)) {
                 error = xasprintf("%s: unknown port", value);
             }
+        } else if (!strcmp(key, "snaplen")) {
+            error = str_to_u16(value, "snaplen", &os->snaplen);
         } else {
             error = xasprintf("invalid key \"%s\" in \"sample\" argument",
                               key);
@@ -4884,6 +4889,11 @@  format_SAMPLE(const struct ofpact_sample *a, struct ds *s)
                   colors.param, colors.end, a->collector_set_id,
                   colors.param, colors.end, a->obs_domain_id,
                   colors.param, colors.end, a->obs_point_id);
+
+    if (a->ofpact.raw == NXAST_RAW_SAMPLE2) {
+        ds_put_format(s, ",%ssnaplen=%s%"PRIu16,
+                      colors.param, colors.end, a->snaplen);
+    }
     if (a->sampling_port != OFPP_NONE) {
         ds_put_format(s, ",%ssampling_port=%s%"PRIu16,
                       colors.param, colors.end, a->sampling_port);
diff --git a/ofproto/ofproto-dpif-sflow.c b/ofproto/ofproto-dpif-sflow.c
index 7d0aa36..f545f9b 100644
--- a/ofproto/ofproto-dpif-sflow.c
+++ b/ofproto/ofproto-dpif-sflow.c
@@ -556,6 +556,16 @@  dpif_sflow_get_probability(const struct dpif_sflow *ds) OVS_EXCLUDED(mutex)
     return probability;
 }
 
+uint16_t
+dpif_sflow_get_header_len(const struct dpif_sflow *ds) OVS_EXCLUDED(mutex)
+{
+    uint16_t header_len;
+    ovs_mutex_lock(&mutex);
+    header_len = ds->options->header_len;
+    ovs_mutex_unlock(&mutex);
+    return header_len;
+}
+
 void
 dpif_sflow_unref(struct dpif_sflow *ds) OVS_EXCLUDED(mutex)
 {
@@ -1223,6 +1233,7 @@  dpif_sflow_cookie_num_outputs(const union user_action_cookie *cookie)
 void
 dpif_sflow_received(struct dpif_sflow *ds, const struct dp_packet *packet,
 		    const struct flow *flow, odp_port_t odp_in_port,
+		    const uint32_t len,
 		    const union user_action_cookie *cookie,
 		    const struct dpif_sflow_actions *sflow_actions)
     OVS_EXCLUDED(mutex)
@@ -1269,11 +1280,10 @@  dpif_sflow_received(struct dpif_sflow *ds, const struct dp_packet *packet,
     header->header_protocol = SFLHEADER_ETHERNET_ISO8023;
     /* The frame_length should include the Ethernet FCS (4 bytes),
      * but it has already been stripped,  so we need to add 4 here. */
-    header->frame_length = dp_packet_size(packet) + 4;
+    header->frame_length = len + 4;
     /* Ethernet FCS stripped off. */
     header->stripped = 4;
-    header->header_length = MIN(dp_packet_size(packet),
-                                sampler->sFlowFsMaximumHeaderSize);
+    header->header_length = MIN(len, sampler->sFlowFsMaximumHeaderSize);
     header->header_bytes = dp_packet_data(packet);
 
     /* Add extended switch element. */
diff --git a/ofproto/ofproto-dpif-sflow.h b/ofproto/ofproto-dpif-sflow.h
index 014e6cc..6b59b84 100644
--- a/ofproto/ofproto-dpif-sflow.h
+++ b/ofproto/ofproto-dpif-sflow.h
@@ -56,6 +56,7 @@  struct dpif_sflow *dpif_sflow_ref(const struct dpif_sflow *);
 void dpif_sflow_unref(struct dpif_sflow *);
 
 uint32_t dpif_sflow_get_probability(const struct dpif_sflow *);
+uint16_t dpif_sflow_get_header_len(const struct dpif_sflow *);
 
 void dpif_sflow_set_options(struct dpif_sflow *,
                             const struct ofproto_sflow_options *);
@@ -75,6 +76,7 @@  void dpif_sflow_read_actions(const struct flow *,
 
 void dpif_sflow_received(struct dpif_sflow *, const struct dp_packet *,
                          const struct flow *, odp_port_t odp_port,
+                         const uint32_t cutlen,
                          const union user_action_cookie *,
 			 const struct dpif_sflow_actions *);
 
diff --git a/ofproto/ofproto-dpif-upcall.c b/ofproto/ofproto-dpif-upcall.c
index c83df9e..493291b 100644
--- a/ofproto/ofproto-dpif-upcall.c
+++ b/ofproto/ofproto-dpif-upcall.c
@@ -207,6 +207,7 @@  struct upcall {
     ofp_port_t in_port;            /* OpenFlow in port, or OFPP_NONE. */
     uint16_t mru;                  /* If !0, Maximum receive unit of
                                       fragmented IP packet */
+    uint32_t len;                  /* Original packet length. */
 
     enum dpif_upcall_type type;    /* Datapath type of the upcall. */
     const struct nlattr *userdata; /* Userdata for DPIF_UC_ACTION Upcalls. */
@@ -350,7 +351,7 @@  static enum upcall_type classify_upcall(enum dpif_upcall_type type,
 static int upcall_receive(struct upcall *, const struct dpif_backer *,
                           const struct dp_packet *packet, enum dpif_upcall_type,
                           const struct nlattr *userdata, const struct flow *,
-                          const unsigned int mru,
+                          const unsigned int mru, const uint32_t cutlen,
                           const ovs_u128 *ufid, const unsigned pmd_id);
 static void upcall_uninit(struct upcall *);
 
@@ -746,6 +747,7 @@  recv_upcalls(struct handler *handler)
         struct upcall *upcall = &upcalls[n_upcalls];
         struct flow *flow = &flows[n_upcalls];
         unsigned int mru;
+        uint32_t len;
         int error;
 
         ofpbuf_use_stub(recv_buf, recv_stubs[n_upcalls],
@@ -766,9 +768,14 @@  recv_upcalls(struct handler *handler)
             mru = 0;
         }
 
+        if (dupcall->len) {
+            len = nl_attr_get_u32(dupcall->len);
+        } else {
+            len = 0;
+        }
         error = upcall_receive(upcall, udpif->backer, &dupcall->packet,
                                dupcall->type, dupcall->userdata, flow, mru,
-                               &dupcall->ufid, PMD_ID_NULL);
+                               len, &dupcall->ufid, PMD_ID_NULL);
         if (error) {
             if (error == ENODEV) {
                 /* Received packet on datapath port for which we couldn't
@@ -1009,7 +1016,7 @@  static int
 upcall_receive(struct upcall *upcall, const struct dpif_backer *backer,
                const struct dp_packet *packet, enum dpif_upcall_type type,
                const struct nlattr *userdata, const struct flow *flow,
-               const unsigned int mru,
+               const unsigned int mru, const unsigned int len,
                const ovs_u128 *ufid, const unsigned pmd_id)
 {
     int error;
@@ -1039,6 +1046,7 @@  upcall_receive(struct upcall *upcall, const struct dpif_backer *backer,
     upcall->key = NULL;
     upcall->key_len = 0;
     upcall->mru = mru;
+    upcall->len = len;
 
     upcall->out_tun_key = NULL;
     upcall->actions = NULL;
@@ -1150,7 +1158,8 @@  upcall_cb(const struct dp_packet *packet, const struct flow *flow, ovs_u128 *ufi
     atomic_read_relaxed(&udpif->flow_limit, &flow_limit);
 
     error = upcall_receive(&upcall, udpif->backer, packet, type, userdata,
-                           flow, 0, ufid, pmd_id);
+                           flow, 0, dp_packet_size(packet) + packet->cutlen,
+                           ufid, pmd_id);
     if (error) {
         return error;
     }
@@ -1237,7 +1246,9 @@  process_upcall(struct udpif *udpif, struct upcall *upcall,
                 }
             }
             dpif_sflow_received(upcall->sflow, packet, flow,
-                                flow->in_port.odp_port, &cookie,
+                                flow->in_port.odp_port,
+                                upcall->len,
+                                &cookie,
                                 actions_len > 0 ? &sflow_actions : NULL);
         }
         break;
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index d46a52c..4f8411b 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -2598,7 +2598,8 @@  xlate_normal(struct xlate_ctx *ctx)
 
 /* Appends a "sample" action for sFlow or IPFIX to 'ctx->odp_actions'.  The
  * 'probability' is the number of packets out of UINT32_MAX to sample.  The
- * 'cookie' (of length 'cookie_size' bytes) is passed back in the callback for
+ * 'snaplen' is the maximum sampled packet size in bytes.  The 'cookie'
+ * (of length 'cookie_size' bytes) is passed back in the callback for
  * each sampled packet.  'tunnel_out_port', if not ODPP_NONE, is added as the
  * OVS_USERSPACE_ATTR_EGRESS_TUN_PORT attribute.  If 'include_actions', an
  * OVS_USERSPACE_ATTR_ACTIONS attribute is added.  If 'emit_set_tunnel',
@@ -2609,6 +2610,7 @@  xlate_normal(struct xlate_ctx *ctx)
 static size_t
 compose_sample_action(struct xlate_ctx *ctx,
                       const uint32_t probability,
+                      const uint16_t snaplen,
                       const union user_action_cookie *cookie,
                       const size_t cookie_size,
                       const odp_port_t tunnel_out_port,
@@ -2622,6 +2624,18 @@  compose_sample_action(struct xlate_ctx *ctx,
     size_t actions_offset = nl_msg_start_nested(ctx->odp_actions,
                                                 OVS_SAMPLE_ATTR_ACTIONS);
 
+    bool support_trunc = ctx->xbridge->support.trunc;
+    if (support_trunc) {
+        if (snaplen > 0 && snaplen < UINT16_MAX) {
+            struct ovs_action_trunc *trunc;
+
+            trunc = nl_msg_put_unspec_uninit(ctx->odp_actions,
+                            OVS_ACTION_ATTR_TRUNC,
+                            sizeof *trunc);
+            trunc->max_len = snaplen;
+        }
+    }
+
     odp_port_t odp_port = ofp_port_to_odp_port(
         ctx->xbridge, ctx->xin->flow.in_port.ofp_port);
     uint32_t pid = dpif_port_get_pid(ctx->xbridge->dpif, odp_port,
@@ -2654,6 +2668,7 @@  compose_sflow_action(struct xlate_ctx *ctx)
 
     union user_action_cookie cookie = { .type = USER_ACTION_COOKIE_SFLOW };
     return compose_sample_action(ctx, dpif_sflow_get_probability(sflow),
+                                 dpif_sflow_get_header_len(sflow),
                                  &cookie, sizeof cookie.sflow, ODPP_NONE,
                                  true);
 }
@@ -2701,6 +2716,7 @@  compose_ipfix_action(struct xlate_ctx *ctx, odp_port_t output_odp_port)
     };
     compose_sample_action(ctx,
                           dpif_ipfix_get_bridge_exporter_probability(ipfix),
+                          0, /* snaplen = 0 means UINT16_MAX. */
                           &cookie, sizeof cookie.ipfix, tunnel_out_port,
                           false);
 }
@@ -4212,6 +4228,7 @@  xlate_sample_action(struct xlate_ctx *ctx,
     /* Scale the probability from 16-bit to 32-bit while representing
      * the same percentage. */
     uint32_t probability = (os->probability << 16) | os->probability;
+    uint16_t snaplen = os->snaplen;
 
     if (!ctx->xbridge->support.variable_length_userdata) {
         static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
@@ -4278,7 +4295,8 @@  xlate_sample_action(struct xlate_ctx *ctx,
             .output_odp_port = output_odp_port,
         }
     };
-    compose_sample_action(ctx, probability, &cookie, sizeof cookie.flow_sample,
+    compose_sample_action(ctx, probability, snaplen,
+                          &cookie, sizeof cookie.flow_sample,
                           tunnel_out_port, false);
 }
 
diff --git a/tests/ofp-actions.at b/tests/ofp-actions.at
index ca4d1ba..a0f8ee2 100644
--- a/tests/ofp-actions.at
+++ b/tests/ofp-actions.at
@@ -127,8 +127,8 @@  ffff 0020 00002320 0015 000500000000 80003039005A02fd 0400000000000000
 # actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
 ffff 0018 00002320 001d 3039 00005BA0 00008707 0000B26E
 
-# actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789)
-ffff 0020 00002320 0026 3039 00005BA0 00008707 0000B26E DDD50000 00000000
+# actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,snaplen=100,sampling_port=56789)
+ffff 0020 00002320 0026 3039 00005BA0 00008707 0000B26E DDD50064 00000000
 
 # bad OpenFlow10 actions: OFPBAC_BAD_LEN
 & ofp_actions|WARN|OpenFlow action OFPAT_OUTPUT length 240 exceeds action buffer length 8
diff --git a/tests/ovs-ofctl.at b/tests/ovs-ofctl.at
index 71c4aab..83be273 100644
--- a/tests/ovs-ofctl.at
+++ b/tests/ovs-ofctl.at
@@ -160,7 +160,7 @@  sctp actions=drop
 sctp actions=drop
 in_port=0 actions=resubmit:0
 actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
-actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789)
+actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,snaplen=100,sampling_port=56789)
 actions=ct(nat)
 actions=ct(commit,nat(dst))
 actions=ct(commit,nat(src))
@@ -191,7 +191,7 @@  OFPT_FLOW_MOD: ADD sctp actions=drop
 OFPT_FLOW_MOD: ADD sctp actions=drop
 OFPT_FLOW_MOD: ADD in_port=0 actions=resubmit:0
 OFPT_FLOW_MOD: ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
-OFPT_FLOW_MOD: ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789)
+OFPT_FLOW_MOD: ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,snaplen=100,sampling_port=56789)
 OFPT_FLOW_MOD: ADD actions=ct(nat)
 OFPT_FLOW_MOD: ADD actions=ct(commit,nat(dst))
 OFPT_FLOW_MOD: ADD actions=ct(commit,nat(src))
@@ -221,7 +221,7 @@  sctp actions=drop
 sctp actions=drop
 in_port=0 actions=resubmit:0
 actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
-actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789)
+actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,snaplen=100,sampling_port=56789)
 ]])
 
 AT_CHECK([ovs-ofctl --protocols OpenFlow11 parse-flows flows.txt
@@ -241,7 +241,7 @@  OFPT_FLOW_MOD (OF1.1): ADD sctp actions=drop
 OFPT_FLOW_MOD (OF1.1): ADD sctp actions=drop
 OFPT_FLOW_MOD (OF1.1): ADD in_port=0 actions=resubmit:0
 OFPT_FLOW_MOD (OF1.1): ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
-OFPT_FLOW_MOD (OF1.1): ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789)
+OFPT_FLOW_MOD (OF1.1): ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,snaplen=100,sampling_port=56789)
 ]])
 AT_CLEANUP
 
@@ -264,7 +264,7 @@  ip actions=mod_nw_src:10.1.1.2,mod_nw_dst:192.168.10.1,mod_nw_ttl:1,mod_nw_tos:1
 in_port=0 actions=mod_dl_src:11:22:33:44:55:66,mod_dl_dst:10:20:30:40:50:60
 in_port=0 actions=resubmit:0
 actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
-actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789)
+actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,snaplen=100,sampling_port=56789)
 ]])
 
 AT_CHECK([ovs-ofctl --protocols OpenFlow12 parse-flows flows.txt
@@ -288,7 +288,7 @@  OFPT_FLOW_MOD (OF1.2): ADD ip actions=set_field:10.1.1.2->ip_src,set_field:192.1
 OFPT_FLOW_MOD (OF1.2): ADD in_port=0 actions=set_field:11:22:33:44:55:66->eth_src,set_field:10:20:30:40:50:60->eth_dst
 OFPT_FLOW_MOD (OF1.2): ADD in_port=0 actions=resubmit:0
 OFPT_FLOW_MOD (OF1.2): ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
-OFPT_FLOW_MOD (OF1.2): ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789)
+OFPT_FLOW_MOD (OF1.2): ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,snaplen=100,sampling_port=56789)
 ]])
 AT_CLEANUP
 
@@ -381,7 +381,7 @@  check_overlap,actions=output:1,exit,output:2
 tcp,actions=fin_timeout(idle_timeout=5,hard_timeout=15)
 actions=controller(max_len=123,reason=invalid_ttl,id=555)
 actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
-actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789)
+actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,snaplen=100,sampling_port=56789)
 ip,actions=ct(commit,zone=5)
 ip,actions=ct(commit,exec(load(1->NXM_NX_CT_MARK[])))
 ip,actions=ct(commit,exec(load(0x1->NXM_NX_CT_LABEL[])))
@@ -428,7 +428,7 @@  NXT_FLOW_MOD: ADD table:255 check_overlap actions=output:1,exit,output:2
 NXT_FLOW_MOD: ADD table:255 tcp actions=fin_timeout(idle_timeout=5,hard_timeout=15)
 NXT_FLOW_MOD: ADD table:255 actions=controller(reason=invalid_ttl,max_len=123,id=555)
 NXT_FLOW_MOD: ADD table:255 actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
-NXT_FLOW_MOD: ADD table:255 actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789)
+NXT_FLOW_MOD: ADD table:255 actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,snaplen=100,sampling_port=56789)
 NXT_FLOW_MOD: ADD table:255 ip actions=ct(commit,zone=5)
 NXT_FLOW_MOD: ADD table:255 ip actions=ct(commit,exec(load:0x1->NXM_NX_CT_MARK[]))
 NXT_FLOW_MOD: ADD table:255 ip actions=ct(commit,exec(load:0x1->NXM_NX_CT_LABEL[0..63],load:0->NXM_NX_CT_LABEL[64..127]))
@@ -473,7 +473,7 @@  dl_dst=00:00:00:00:00:00/01:00:00:00:00:00,actions=drop
 dl_dst=aa:bb:cc:dd:ee:ff/fe:ff:ff:ff:ff:ff,actions=drop
 dl_dst=aa:bb:cc:dd:ee:ff/00:00:00:00:00:00,actions=drop
 actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
-actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789)
+actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,snaplen=100,sampling_port=56789)
 ip,actions=ct(commit,zone=5)
 ip,actions=ct(commit,exec(load(1->NXM_NX_CT_MARK[[]])))
 ip,actions=ct(commit,exec(load(0x1->NXM_NX_CT_LABEL[[]])))
@@ -510,7 +510,7 @@  NXT_FLOW_MOD: ADD dl_dst=00:00:00:00:00:00/01:00:00:00:00:00 actions=drop
 NXT_FLOW_MOD: ADD dl_dst=aa:bb:cc:dd:ee:ff/fe:ff:ff:ff:ff:ff actions=drop
 NXT_FLOW_MOD: ADD actions=drop
 NXT_FLOW_MOD: ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
-NXT_FLOW_MOD: ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789)
+NXT_FLOW_MOD: ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,snaplen=100,sampling_port=56789)
 NXT_FLOW_MOD: ADD ip actions=ct(commit,zone=5)
 NXT_FLOW_MOD: ADD ip actions=ct(commit,exec(load:0x1->NXM_NX_CT_MARK[[]]))
 NXT_FLOW_MOD: ADD ip actions=ct(commit,exec(load:0x1->NXM_NX_CT_LABEL[[0..63]],load:0->NXM_NX_CT_LABEL[[64..127]]))
@@ -547,7 +547,7 @@  actions=move:OXM_OF_ETH_DST[]->OXM_OF_ETH_SRC[]
 actions=push:NXM_NX_REG0[0..31],pop:NXM_NX_REG0[]
 vlan_tci=0x1123/0x1fff,actions=drop
 actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
-actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789)
+actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,snaplen=100,sampling_port=56789)
 ip,actions=ct(commit,zone=5)
 ip,actions=ct(commit,exec(load(1->NXM_NX_CT_MARK[])))
 ip,actions=ct(commit,exec(load(1->NXM_NX_CT_LABEL[])))
@@ -584,7 +584,7 @@  NXT_FLOW_MOD: ADD <any> actions=move:NXM_OF_ETH_DST[]->NXM_OF_ETH_SRC[]
 NXT_FLOW_MOD: ADD <any> actions=push:NXM_NX_REG0[],pop:NXM_NX_REG0[]
 NXT_FLOW_MOD: ADD NXM_OF_VLAN_TCI_W(1123/1fff) actions=drop
 NXT_FLOW_MOD: ADD <any> actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
-NXT_FLOW_MOD: ADD <any> actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789)
+NXT_FLOW_MOD: ADD <any> actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,snaplen=100,sampling_port=56789)
 NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(0800) actions=ct(commit,zone=5)
 NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(0800) actions=ct(commit,exec(load:0x1->NXM_NX_CT_MARK[]))
 NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(0800) actions=ct(commit,exec(load:0x1->NXM_NX_CT_LABEL[0..63],load:0->NXM_NX_CT_LABEL[64..127]))
diff --git a/tests/system-traffic.at b/tests/system-traffic.at
index 252ed20..90ac3d3 100644
--- a/tests/system-traffic.at
+++ b/tests/system-traffic.at
@@ -497,6 +497,37 @@  n_bytes=100
 OVS_TRAFFIC_VSWITCHD_STOP
 AT_CLEANUP
 
+AT_SETUP([datapath - truncate and userspace action])
+dnl Demonstrate that when truncate happens at kernel datapath, the
+dnl upcall netlink packet size is no longer the original size.
+OVS_TRAFFIC_VSWITCHD_START()
+
+dnl skip if it is check-userspace
+AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"])
+ADD_NAMESPACES(at_ns0, at_ns1)
+
+ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24")
+ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24")
+
+NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -w 2 10.1.1.2 | FORMAT_PING], [0], [dnl
+3 packets transmitted, 3 received, 0% packet loss, time 0ms
+])
+
+dnl setup sflow
+AT_CHECK([ovs-vsctl -- \
+    --id=@s create sFlow agent=br0 target=\"127.0.0.1:6344\" \
+    header=64 sampling=1 polling=1 -- set bridge br0 sflow=@s
+], [0], [stdout])
+
+dnl ofproto/trace
+AT_CHECK([ovs-appctl ofproto/trace br0 "in_port=2,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=6,tp_src=8,tp_dst=9"], [0], [stdout])
+AT_CHECK([tail -1 stdout | sed 's/pid=[[0-9]]*/pid=/g'], [0],
+    [Datapath actions: sample(sample=100.0%,actions(trunc(64),userspace(pid=,sFlow(vid=0,pcp=0,output=2147483650),actions))),1,2
+])
+
+OVS_TRAFFIC_VSWITCHD_STOP
+AT_CLEANUP
+
 AT_SETUP([conntrack - controller])
 CHECK_CONNTRACK()
 OVS_TRAFFIC_VSWITCHD_START()