Add support to export tunnel information for flow-based IPFIX.
The original steps to configure flow level IPFIX:
    1) Create a new record in Flow_Sample_Collector_Set table:
       'ovs-vsctl -- create Flow_Sample_Collector_Set id=1 bridge="Bridge UUID"'
    2) Add IPFIX configuration which is referred by corresponding
       row in Flow_Sample_Collector_Set table:
       'ovs-vsctl -- set Flow_Sample_Collector_Set
       "Flow_Sample_Collector_Set UUID" ipfix=@i -- --id=@i create IPFIX
       targets=\"IP:4739\" obs_domain_id=123 obs_point_id=456
       cache_active_timeout=60 cache_max_flows=13'
    3) Add sample action to the flows:
       'ovs-ofctl add-flow mybridge in_port=1,
       actions=sample'('probability=65535,collector_set_id=1,
       obs_domain_id=123,obs_point_id=456')',output:3'
NXAST_SAMPLE action was used in step 3. In order to support exporting tunnel
information, the NXAST_SAMPLE2 action was added. With NXAST_SAMPLE2 action in
this patch, the step 3 should be configured like below:
       'ovs-ofctl add-flow mybridge in_port=1,
       
actions=sample'('probability=4294967295,collector_set_id=1,obs_domain_id=123,
       obs_point_id=456,sampling_port=3')',output:3'
'sampling_port' can be equal to ingress port or one of egress ports. If sampling
port is equal to output port and the output port is a tunnel port,
OVS_USERSPACE_ATTR_EGRESS_TUN_PORT will be set in the datapath flow sample 
action.
When flow sample action upcall happens, tunnel information will be retrieved 
from
the datapath and then IPFIX can export egress tunnel port information. If
samping_port=65535 (OFPP_NONE), flow-based IPFIX will keep the same behavior
as before.

This patch mainly do three tasks:
    1) Add a new flow sample action NXAST_SAMPLE2 to support exporting
       tunnel information. NXAST_SAMPLE2 action has a new added field
       'sampling_port' and 32bit probability instead if 16bit probability.
       NXAST_SAMPLE2 Supports 32bit probability, as datapath actually
       uses 32bit usigned interger as probability and bridge IPFIX and
       sFlow are both using 32bit probability.
    2) Use 'other_configure: enable-tunnel-sampling' to enable or disable
       exporting tunnel information.
    3) Flow sample action for egress tunnel port is moved to the point that
       is just before output action. It makes sure that flow sample action
       for egress tunnel port is always behind corresponding set_tunnel
       action.
    4) Add a test of flow-based IPFIX for tunnel set.

How to test flow-based IPFIX:
    1) Setup a test environment with two Linux host with Docker supported
    2) Create a Docker container and a GRE tunnel port on each host
    3) Use ovs-docker to add the container on the bridge
    4) Listen on port 4739 on the collector machine and use wireshark to filter
       'cflow' packets.
    5) Configure flow-based IPFIX:
       - 'ovs-vsctl -- create Flow_Sample_Collector_Set id=1 bridge="Bridge 
UUID"'
       - 'ovs-vsctl -- set Flow_Sample_Collector_Set
          "Flow_Sample_Collector_Set UUID" ipfix=@i -- --id=@i create IPFIX \
          targets=\"IP:4739\" cache_active_timeout=60 cache_max_flows=13 \
          other_config:enable-tunnel-sampling=true'
       - 'ovs-ofctl add-flow mybridge in_port=1,
          
actions=sample'('probability=65535,collector_set_id=1,obs_domain_id=123,
          obs_point_id=456,sampling_port=3')',output:3'
       Note: The in-port is container port. The output port and sampling_port
             are both open flow port and the output port is a GRE tunnel port.
    6) Ping from the container whose host enabled flow-based IPFIX.
    7) Get the IPFIX template pakcets and IPFIX information packets.

Signed-off-by: Benli Ye <dani...@vmware.com>
---
 include/openvswitch/ofp-actions.h |   5 +-
 lib/odp-util.c                    |  15 ++--
 lib/odp-util.h                    |   5 +-
 lib/ofp-actions.c                 |  88 +++++++++++++++++---
 ofproto/ofproto-dpif-ipfix.c      |  66 +++++++++++++--
 ofproto/ofproto-dpif-ipfix.h      |   7 +-
 ofproto/ofproto-dpif-upcall.c     |  13 ++-
 ofproto/ofproto-dpif-xlate.c      | 131 ++++++++++++++++++++++++++---
 ofproto/ofproto.h                 |   1 +
 tests/odp.at                      |   4 +-
 tests/ofp-actions.at              |   3 +
 tests/ofproto-dpif.at             |  76 +++++++++++++++++
 tests/ovs-ofctl.at                |  12 +++
 utilities/ovs-ofctl.8.in          |   8 ++
 vswitchd/bridge.c                 |   3 +
 vswitchd/vswitch.xml              | 168 ++++++++++++++++++++------------------
 16 files changed, 478 insertions(+), 127 deletions(-)

diff --git a/include/openvswitch/ofp-actions.h 
b/include/openvswitch/ofp-actions.h
index 038ef87..b060997 100644
--- a/include/openvswitch/ofp-actions.h
+++ b/include/openvswitch/ofp-actions.h
@@ -775,13 +775,14 @@ struct ofpact_note {
 
 /* OFPACT_SAMPLE.
  *
- * Used for NXAST_SAMPLE. */
+ * Used for NXAST_SAMPLE and NXAST_SAMPLE2. */
 struct ofpact_sample {
     struct ofpact ofpact;
-    uint16_t probability;  // Always >0.
+    uint32_t probability;  /* Always positive. */
     uint32_t collector_set_id;
     uint32_t obs_domain_id;
     uint32_t obs_point_id;
+    ofp_port_t sampling_port;
 };
 
 /* OFPACT_DEC_TTL.
diff --git a/lib/odp-util.c b/lib/odp-util.c
index d9ace90..42432e6 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -308,14 +308,16 @@ format_odp_userspace_action(struct ds *ds, const struct 
nlattr *attr)
                 ds_put_format(ds, ")");
             } else if (userdata_len == sizeof cookie.flow_sample
                        && cookie.type == USER_ACTION_COOKIE_FLOW_SAMPLE) {
-                ds_put_format(ds, ",flow_sample(probability=%"PRIu16
+                ds_put_format(ds, ",flow_sample(probability=%"PRIu32
                               ",collector_set_id=%"PRIu32
                               ",obs_domain_id=%"PRIu32
-                              ",obs_point_id=%"PRIu32")",
+                              ",obs_point_id=%"PRIu32
+                              ",output_port=%"PRIu32")",
                               cookie.flow_sample.probability,
                               cookie.flow_sample.collector_set_id,
                               cookie.flow_sample.obs_domain_id,
-                              cookie.flow_sample.obs_point_id);
+                              cookie.flow_sample.obs_point_id,
+                              cookie.flow_sample.output_odp_port);
             } else if (userdata_len >= sizeof cookie.ipfix
                        && cookie.type == USER_ACTION_COOKIE_IPFIX) {
                 ds_put_format(ds, ",ipfix(output_port=%"PRIu32")",
@@ -951,9 +953,11 @@ parse_odp_userspace_action(const char *s, struct ofpbuf 
*actions)
         } else if (ovs_scan(&s[n], ",flow_sample(probability=%"SCNi32","
                             "collector_set_id=%"SCNi32","
                             "obs_domain_id=%"SCNi32","
-                            "obs_point_id=%"SCNi32")%n",
+                            "obs_point_id=%"SCNi32","
+                            "output_port=%"SCNi32")%n",
                             &probability, &collector_set_id,
-                            &obs_domain_id, &obs_point_id, &n1)) {
+                            &obs_domain_id, &obs_point_id,
+                            &output, &n1)) {
             n += n1;
 
             cookie.type = USER_ACTION_COOKIE_FLOW_SAMPLE;
@@ -961,6 +965,7 @@ parse_odp_userspace_action(const char *s, struct ofpbuf 
*actions)
             cookie.flow_sample.collector_set_id = collector_set_id;
             cookie.flow_sample.obs_domain_id = obs_domain_id;
             cookie.flow_sample.obs_point_id = obs_point_id;
+            cookie.flow_sample.output_odp_port = u32_to_odp(output);
             user_data = &cookie;
             user_data_size = sizeof cookie.flow_sample;
         } else if (ovs_scan(&s[n], ",ipfix(output_port=%"SCNi32")%n",
diff --git a/lib/odp-util.h b/lib/odp-util.h
index 51cf5c3..dbf8e59 100644
--- a/lib/odp-util.h
+++ b/lib/odp-util.h
@@ -299,10 +299,11 @@ union user_action_cookie {
 
     struct {
         uint16_t type;          /* USER_ACTION_COOKIE_FLOW_SAMPLE. */
-        uint16_t probability;   /* Sampling probability. */
+        uint32_t probability;   /* Sampling probability. */
         uint32_t collector_set_id; /* ID of IPFIX collector set. */
         uint32_t obs_domain_id; /* Observation Domain ID. */
         uint32_t obs_point_id;  /* Observation Point ID. */
+        odp_port_t output_odp_port; /* The output odp port. */
     } flow_sample;
 
     struct {
@@ -310,7 +311,7 @@ union user_action_cookie {
         odp_port_t output_odp_port; /* The output odp port. */
     } ipfix;
 };
-BUILD_ASSERT_DECL(sizeof(union user_action_cookie) == 16);
+BUILD_ASSERT_DECL(sizeof(union user_action_cookie) == 24);
 
 size_t odp_put_userspace_action(uint32_t pid,
                                 const void *userdata, size_t userdata_size,
diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
index 7ddadb8..3f6e4ee 100644
--- a/lib/ofp-actions.c
+++ b/lib/ofp-actions.c
@@ -289,6 +289,8 @@ enum ofp_raw_action_type {
 
     /* NX1.0+(29): struct nx_action_sample. */
     NXAST_RAW_SAMPLE,
+    /* NX1.0+(38): struct nx_action_sample2. */
+    NXAST_RAW_SAMPLE2,
 
     /* NX1.0+(34): struct nx_action_conjunction. */
     NXAST_RAW_CONJUNCTION,
@@ -4697,6 +4699,24 @@ struct nx_action_sample {
 };
 OFP_ASSERT(sizeof(struct nx_action_sample) == 24);
 
+/* Action structure for NXAST_SAMPLE2.
+ *
+ * This replacement for NXAST_SAMPLE makes it support exporting
+ * egress tunnel information. */
+struct nx_action_sample2 {
+    ovs_be16 type;                  /* OFPAT_VENDOR. */
+    ovs_be16 len;                   /* Length is 32. */
+    ovs_be32 vendor;                /* NX_VENDOR_ID. */
+    ovs_be16 subtype;               /* NXAST_SAMPLE2. */
+    ovs_be16 sampling_port;         /* Sampling port. */
+    ovs_be32 probability;           /* Fraction of packets to sample. */
+    ovs_be32 collector_set_id;      /* ID of collector set in OVSDB. */
+    ovs_be32 obs_domain_id;         /* ID of sampling observation domain. */
+    ovs_be32 obs_point_id;          /* ID of sampling observation point. */
+    uint8_t  pad[4];                /* Pad to a multiple of 8 bytes */
+ };
+ OFP_ASSERT(sizeof(struct nx_action_sample2) == 32);
+
 static enum ofperr
 decode_NXAST_RAW_SAMPLE(const struct nx_action_sample *nas,
                         enum ofp_version ofp_version OVS_UNUSED,
@@ -4705,10 +4725,35 @@ decode_NXAST_RAW_SAMPLE(const struct nx_action_sample 
*nas,
     struct ofpact_sample *sample;
 
     sample = ofpact_put_SAMPLE(out);
+    sample->ofpact.raw = NXAST_RAW_SAMPLE;
     sample->probability = ntohs(nas->probability);
     sample->collector_set_id = ntohl(nas->collector_set_id);
     sample->obs_domain_id = ntohl(nas->obs_domain_id);
     sample->obs_point_id = ntohl(nas->obs_point_id);
+    /* Default value for sampling port is OFPP_NONE */
+    sample->sampling_port = OFPP_NONE;
+
+    if (sample->probability == 0) {
+        return OFPERR_OFPBAC_BAD_ARGUMENT;
+    }
+
+    return 0;
+}
+
+static enum ofperr
+decode_NXAST_RAW_SAMPLE2(const struct nx_action_sample2 *nas,
+                         enum ofp_version ofp_version OVS_UNUSED,
+                         struct ofpbuf *out)
+{
+    struct ofpact_sample *sample;
+
+    sample = ofpact_put_SAMPLE(out);
+    sample->ofpact.raw = NXAST_RAW_SAMPLE2;
+    sample->probability = ntohl(nas->probability);
+    sample->collector_set_id = ntohl(nas->collector_set_id);
+    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));
 
     if (sample->probability == 0) {
         return OFPERR_OFPBAC_BAD_ARGUMENT;
@@ -4721,13 +4766,22 @@ static void
 encode_SAMPLE(const struct ofpact_sample *sample,
               enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out)
 {
-    struct nx_action_sample *nas;
-
-    nas = put_NXAST_SAMPLE(out);
-    nas->probability = htons(sample->probability);
-    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);
+    if (sample->ofpact.raw == NXAST_RAW_SAMPLE2
+        || sample->sampling_port != OFPP_NONE) {
+        struct nx_action_sample2 *nas = put_NXAST_SAMPLE2(out);
+        nas->probability = htonl(sample->probability);
+        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);
+        nas->sampling_port = htons(ofp_to_u16(sample->sampling_port));
+    } else {
+        struct nx_action_sample *nas = put_NXAST_SAMPLE(out);
+        uint16_t probability =  sample->probability & 0xffff;
+        nas->probability = htons(probability);
+        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);
+    }
 }
 
 /* Parses 'arg' as the argument to a "sample" action, and appends such an
@@ -4740,13 +4794,14 @@ parse_SAMPLE(char *arg, struct ofpbuf *ofpacts,
              enum ofputil_protocol *usable_protocols OVS_UNUSED)
 {
     struct ofpact_sample *os = ofpact_put_SAMPLE(ofpacts);
-    char *key, *value;
+    os->sampling_port = OFPP_NONE;
 
+    char *key, *value;
     while (ofputil_parse_key_value(&arg, &key, &value)) {
         char *error = NULL;
 
         if (!strcmp(key, "probability")) {
-            error = str_to_u16(value, "probability", &os->probability);
+            error = str_to_u32(value, &os->probability);
             if (!error && os->probability == 0) {
                 error = xasprintf("invalid probability value \"%s\"", value);
             }
@@ -4756,6 +4811,10 @@ parse_SAMPLE(char *arg, struct ofpbuf *ofpacts,
             error = str_to_u32(value, &os->obs_domain_id);
         } else if (!strcmp(key, "obs_point_id")) {
             error = str_to_u32(value, &os->obs_point_id);
+        } else if (!strcmp(key, "sampling_port")) {
+            if (!ofputil_port_from_string(value, &os->sampling_port)) {
+                error = xasprintf("%s: unknown port", value);
+            }
         } else {
             error = xasprintf("invalid key \"%s\" in \"sample\" argument",
                               key);
@@ -4767,6 +4826,7 @@ parse_SAMPLE(char *arg, struct ofpbuf *ofpacts,
     if (os->probability == 0) {
         return xstrdup("non-zero \"probability\" must be specified on sample");
     }
+
     return NULL;
 }
 
@@ -4776,13 +4836,17 @@ format_SAMPLE(const struct ofpact_sample *a, struct ds 
*s)
     ds_put_format(s, "%ssample(%s%sprobability=%s%"PRIu16
                   ",%scollector_set_id=%s%"PRIu32
                   ",%sobs_domain_id=%s%"PRIu32
-                  ",%sobs_point_id=%s%"PRIu32"%s)%s",
+                  ",%sobs_point_id=%s%"PRIu32,
                   colors.paren, colors.end,
                   colors.param, colors.end, a->probability,
                   colors.param, colors.end, a->collector_set_id,
                   colors.param, colors.end, a->obs_domain_id,
-                  colors.param, colors.end, a->obs_point_id,
-                  colors.paren, colors.end);
+                  colors.param, colors.end, a->obs_point_id);
+    if (a->sampling_port != OFPP_NONE) {
+        ds_put_format(s, ",%ssampling_port=%s%"PRIu16,
+                      colors.param, colors.end, a->sampling_port);
+    }
+    ds_put_format(s, "%s)%s", colors.paren, colors.end);
 }
 
 /* debug_recirc instruction. */
diff --git a/ofproto/ofproto-dpif-ipfix.c b/ofproto/ofproto-dpif-ipfix.c
index 79ba234..25ab17b 100644
--- a/ofproto/ofproto-dpif-ipfix.c
+++ b/ofproto/ofproto-dpif-ipfix.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2013, 2014, 2015 Nicira, Inc.
+ * Copyright (c) 2012, 2013, 2014, 2015, 2016 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -25,6 +25,7 @@
 #include "netdev.h"
 #include "openvswitch/list.h"
 #include "openvswitch/ofpbuf.h"
+#include "openvswitch/ofp-actions.h"
 #include "ofproto.h"
 #include "ofproto-dpif.h"
 #include "dp-packet.h"
@@ -467,6 +468,7 @@ ofproto_ipfix_flow_exporter_options_equal(
     return (a->collector_set_id == b->collector_set_id
             && a->cache_active_timeout == b->cache_active_timeout
             && a->cache_max_flows == b->cache_max_flows
+            && a->enable_tunnel_sampling == b->enable_tunnel_sampling
             && sset_equals(&a->targets, &b->targets));
 }
 
@@ -938,6 +940,33 @@ dpif_ipfix_get_bridge_exporter_tunnel_sampling(const 
struct dpif_ipfix *di)
     return ret;
 }
 
+bool
+dpif_ipfix_flow_exporter_enable(const struct dpif_ipfix *di)
+    OVS_EXCLUDED(mutex)
+{
+    ovs_mutex_lock(&mutex);
+    bool ret = hmap_count(&di->flow_exporter_map) > 0;
+    ovs_mutex_unlock(&mutex);
+
+    return ret;
+}
+
+bool
+dpif_ipfix_get_flow_exporter_tunnel_sampling(const struct dpif_ipfix *di,
+                                             const uint32_t collector_set_id)
+    OVS_EXCLUDED(mutex)
+{
+    ovs_mutex_lock(&mutex);
+    struct dpif_ipfix_flow_exporter_map_node *node
+        = dpif_ipfix_find_flow_exporter_map_node(di, collector_set_id);
+    bool ret = (node
+                && node->exporter.options
+                && node->exporter.options->enable_tunnel_sampling);
+    ovs_mutex_unlock(&mutex);
+
+    return ret;
+}
+
 static void
 dpif_ipfix_clear(struct dpif_ipfix *di) OVS_REQUIRES(mutex)
 {
@@ -1743,21 +1772,44 @@ dpif_ipfix_bridge_sample(struct dpif_ipfix *di, const 
struct dp_packet *packet,
 
 void
 dpif_ipfix_flow_sample(struct dpif_ipfix *di, const struct dp_packet *packet,
-                       const struct flow *flow, uint32_t collector_set_id,
-                       uint16_t probability, uint32_t obs_domain_id,
-                       uint32_t obs_point_id) OVS_EXCLUDED(mutex)
+                       const struct flow *flow,
+                       const union user_action_cookie *cookie,
+                       odp_port_t input_odp_port,
+                       const struct flow_tnl *output_tunnel_key)
+    OVS_EXCLUDED(mutex)
 {
     struct dpif_ipfix_flow_exporter_map_node *node;
+    const struct flow_tnl *tunnel_key = NULL;
+    struct dpif_ipfix_port * tunnel_port = NULL;
+    odp_port_t output_odp_port = cookie->flow_sample.output_odp_port;
+    uint32_t collector_set_id = cookie->flow_sample.collector_set_id;
+    uint32_t probability = cookie->flow_sample.probability;
+
     /* Use the sampling probability as an approximation of the number
      * of matched packets. */
-    uint64_t packet_delta_count = USHRT_MAX / probability;
+    uint64_t packet_delta_count = UINT32_MAX / probability;
 
     ovs_mutex_lock(&mutex);
     node = dpif_ipfix_find_flow_exporter_map_node(di, collector_set_id);
     if (node) {
+        if (node->exporter.options->enable_tunnel_sampling) {
+            if (output_odp_port == ODPP_NONE && flow->tunnel.ip_dst) {
+                /* Input tunnel. */
+                tunnel_key = &flow->tunnel;
+                tunnel_port = dpif_ipfix_find_port(di, input_odp_port);
+            }
+            if (output_odp_port != ODPP_NONE && output_tunnel_key) {
+                /* Output tunnel, output_tunnel_key must be valid. */
+                tunnel_key = output_tunnel_key;
+                tunnel_port = dpif_ipfix_find_port(di, output_odp_port);
+            }
+        }
+
         dpif_ipfix_sample(&node->exporter.exporter, packet, flow,
-                          packet_delta_count, obs_domain_id, obs_point_id,
-                          ODPP_NONE, NULL, NULL);
+                          packet_delta_count,
+                          cookie->flow_sample.obs_domain_id,
+                          cookie->flow_sample.obs_point_id,
+                          output_odp_port, tunnel_port, tunnel_key);
     }
     ovs_mutex_unlock(&mutex);
 }
diff --git a/ofproto/ofproto-dpif-ipfix.h b/ofproto/ofproto-dpif-ipfix.h
index 2bb0e43..915d05c 100644
--- a/ofproto/ofproto-dpif-ipfix.h
+++ b/ofproto/ofproto-dpif-ipfix.h
@@ -40,6 +40,9 @@ uint32_t dpif_ipfix_get_bridge_exporter_probability(const 
struct dpif_ipfix *);
 bool dpif_ipfix_get_bridge_exporter_tunnel_sampling(const struct dpif_ipfix *);
 bool dpif_ipfix_get_bridge_exporter_input_sampling(const struct dpif_ipfix *);
 bool dpif_ipfix_get_bridge_exporter_output_sampling(const struct dpif_ipfix *);
+bool dpif_ipfix_flow_exporter_enable(const struct dpif_ipfix *);
+bool dpif_ipfix_get_flow_exporter_tunnel_sampling(const struct dpif_ipfix *,
+                                                  const uint32_t);
 bool dpif_ipfix_get_tunnel_port(const struct dpif_ipfix *, odp_port_t);
 void dpif_ipfix_set_options(
     struct dpif_ipfix *,
@@ -50,8 +53,8 @@ void dpif_ipfix_bridge_sample(struct dpif_ipfix *, const 
struct dp_packet *,
                               const struct flow *,
                               odp_port_t, odp_port_t, const struct flow_tnl *);
 void dpif_ipfix_flow_sample(struct dpif_ipfix *, const struct dp_packet *,
-                            const struct flow *, uint32_t, uint16_t, uint32_t,
-                            uint32_t);
+                            const struct flow *, const union 
user_action_cookie *,
+                            odp_port_t, const struct flow_tnl *);
 
 void dpif_ipfix_run(struct dpif_ipfix *);
 void dpif_ipfix_wait(struct dpif_ipfix *);
diff --git a/ofproto/ofproto-dpif-upcall.c b/ofproto/ofproto-dpif-upcall.c
index a18fc5a..feed9b1 100644
--- a/ofproto/ofproto-dpif-upcall.c
+++ b/ofproto/ofproto-dpif-upcall.c
@@ -1256,17 +1256,22 @@ process_upcall(struct udpif *udpif, struct upcall 
*upcall,
     case FLOW_SAMPLE_UPCALL:
         if (upcall->ipfix) {
             union user_action_cookie cookie;
+            struct flow_tnl output_tunnel_key;
 
             memset(&cookie, 0, sizeof cookie);
             memcpy(&cookie, nl_attr_get(userdata), sizeof cookie.flow_sample);
 
+            if (upcall->out_tun_key) {
+                odp_tun_key_from_attr(upcall->out_tun_key, false,
+                                      &output_tunnel_key);
+            }
+
             /* The flow reflects exactly the contents of the packet.
              * Sample the packet using it. */
             dpif_ipfix_flow_sample(upcall->ipfix, packet, flow,
-                                   cookie.flow_sample.collector_set_id,
-                                   cookie.flow_sample.probability,
-                                   cookie.flow_sample.obs_domain_id,
-                                   cookie.flow_sample.obs_point_id);
+                                   &cookie, flow->in_port.odp_port,
+                                   upcall->out_tun_key ?
+                                       &output_tunnel_key : NULL);
         }
         break;
 
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index cca5c5c..a11dc66 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -2655,10 +2655,81 @@ compose_sflow_action(struct xlate_ctx *ctx)
                                  true);
 }
 
-/* If IPFIX is enabled, this appends a "sample" action to implement IPFIX to
- * 'ctx->odp_actions'. */
+/* This is used for making flow sample action which contains
+ * OVS_USERSPACE_ATTR_EGRESS_TUN_PORT attribute is just in front of
+ * output action. By doing this, it makes sure that set_tunnel action
+ * will be in front of flow sample action at egress point of tunnel
+ * port. So we can export egress tunnel information of the packets */
 static void
-compose_ipfix_action(struct xlate_ctx *ctx, odp_port_t output_odp_port)
+adjust_sample_action(struct xlate_ctx *ctx, odp_port_t output_odp_port)
+{
+    const struct nlattr *nla, *nla_nested_lay1, *nla_nested_lay2;
+    const struct nlattr *tunnel_out_port_attr;
+    size_t step_length = 0;
+    struct ofpbuf *odp_actions;
+
+    if (output_odp_port == ODPP_NONE) {
+        return;
+    }
+
+    odp_actions = ctx->odp_actions;
+    size_t total_length = odp_actions->size;
+
+    while (step_length < total_length) {
+        nla = nl_attr_find(odp_actions, step_length, OVS_ACTION_ATTR_SAMPLE);
+
+        if (nla) {
+            nla_nested_lay1 = nl_attr_find_nested(nla,
+                                                  OVS_SAMPLE_ATTR_ACTIONS);
+            if (nla_nested_lay1) {
+                nla_nested_lay2 = nl_attr_find_nested(nla_nested_lay1,
+                                                      
OVS_ACTION_ATTR_USERSPACE);
+            } else {
+                return;
+            }
+        } else {
+            return;
+        }
+
+        tunnel_out_port_attr = nl_attr_find_nested(nla_nested_lay2,
+                                                   
OVS_USERSPACE_ATTR_EGRESS_TUN_PORT);
+        if (tunnel_out_port_attr) {
+            size_t len = nla->nla_len;
+            if (output_odp_port !=
+                u32_to_odp(nl_attr_get_u32(tunnel_out_port_attr))) {
+                step_length = (char *) nla - (char *) odp_actions->data +
+                              nla->nla_len;
+                continue;
+            }
+
+            if (total_length == (char *) nla - (char *) odp_actions->data +
+                                len) {
+                break;
+            }
+
+            size_t remain_len = (char *) odp_actions->data +
+                                total_length - (char *) nla - len;
+            char *tmpBuffer = xmalloc(len);
+            memcpy(tmpBuffer, (char *) nla, len);
+            memmove((char *) nla, (char *) nla + len, remain_len);
+            memcpy((char *) nla + remain_len, tmpBuffer, len);
+            free(tmpBuffer);
+            break;
+        } else {
+            /* Continue to find sample action */
+            step_length = (char *) nla - ((char *) odp_actions->data) +
+                          nla->nla_len;
+        }
+    }
+}
+
+/* If flow IPFIX is enabled, make sure IPFIX flow sample action
+ * at egress point of tunnel port is just in front of corresponding
+ * output action. If bridge IPFIX is enabled, this appends an IPFIX
+ * sample action to 'ctx->odp_actions'. */
+static void
+compose_ipfix_action(struct xlate_ctx *ctx, bool egress_tunnel_port,
+                     odp_port_t output_odp_port)
 {
     struct dpif_ipfix *ipfix = ctx->xbridge->ipfix;
     odp_port_t tunnel_out_port = ODPP_NONE;
@@ -2667,6 +2738,12 @@ compose_ipfix_action(struct xlate_ctx *ctx, odp_port_t 
output_odp_port)
         return;
     }
 
+    /* For IPFIX flow sample action, egress sample action of tunnel port
+     * should be placed just in front of output action. */
+    if (egress_tunnel_port && dpif_ipfix_flow_exporter_enable(ipfix)) {
+        adjust_sample_action(ctx, output_odp_port);
+    }
+
     /* For input case, output_odp_port is ODPP_NONE, which is an invalid port
      * number. */
     if (output_odp_port == ODPP_NONE &&
@@ -2674,7 +2751,7 @@ compose_ipfix_action(struct xlate_ctx *ctx, odp_port_t 
output_odp_port)
         return;
     }
 
-    /* For output case, output_odp_port is valid*/
+    /* For output case, output_odp_port is valid. */
     if (output_odp_port != ODPP_NONE) {
         if (!dpif_ipfix_get_bridge_exporter_output_sampling(ipfix)) {
             return;
@@ -3220,7 +3297,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t 
ofp_port,
                 } else {
                     /* Tunnel push-pop action is not compatible with
                      * IPFIX action. */
-                    compose_ipfix_action(ctx, out_port);
+                    compose_ipfix_action(ctx, xport->is_tunnel, out_port);
                     nl_msg_put_odp_port(ctx->odp_actions,
                                         OVS_ACTION_ATTR_OUTPUT,
                                         out_port);
@@ -4145,9 +4222,20 @@ static void
 xlate_sample_action(struct xlate_ctx *ctx,
                     const struct ofpact_sample *os)
 {
-    /* Scale the probability from 16-bit to 32-bit while representing
-     * the same percentage. */
-    uint32_t probability = (os->probability << 16) | os->probability;
+    odp_port_t output_odp_port = ODPP_NONE;
+    odp_port_t tunnel_out_port = ODPP_NONE;
+    struct dpif_ipfix *ipfix = ctx->xbridge->ipfix;
+
+    if (!ipfix || ctx->xin->flow.in_port.ofp_port == OFPP_NONE) {
+        return;
+    }
+
+    uint32_t probability = os->probability;
+    if (os->sampling_port == OFPP_NONE) {
+        /* For NXA_SAMPLE action, scale the probability from 16-bit to
+         * 32-bit while representing the same percentage. */
+        probability = (os->probability << 16) | os->probability;
+    }
 
     if (!ctx->xbridge->support.variable_length_userdata) {
         static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
@@ -4158,19 +4246,40 @@ xlate_sample_action(struct xlate_ctx *ctx,
         return;
     }
 
+    /* If ofp_port in flow sample action is equel to ofp_port,
+     * this sample action is a input port action. */
+    if (os->sampling_port != OFPP_NONE &&
+        os->sampling_port != ctx->xin->flow.in_port.ofp_port) {
+        output_odp_port = ofp_port_to_odp_port(ctx->xbridge,
+                                               os->sampling_port);
+        if (output_odp_port == ODPP_NONE) {
+            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
+            VLOG_ERR_RL(&rl, "can't use unknown port %d in flow sample "
+                         "action", os->sampling_port);
+            return;
+        }
+
+        if (dpif_ipfix_get_flow_exporter_tunnel_sampling(ipfix,
+                                                         os->collector_set_id)
+            && dpif_ipfix_get_tunnel_port(ipfix, output_odp_port)) {
+            tunnel_out_port = output_odp_port;
+        }
+    }
+
     xlate_commit_actions(ctx);
 
     union user_action_cookie cookie = {
         .flow_sample = {
             .type = USER_ACTION_COOKIE_FLOW_SAMPLE,
-            .probability = os->probability,
+            .probability = probability,
             .collector_set_id = os->collector_set_id,
             .obs_domain_id = os->obs_domain_id,
             .obs_point_id = os->obs_point_id,
+            .output_odp_port = output_odp_port,
         }
     };
     compose_sample_action(ctx, probability, &cookie, sizeof cookie.flow_sample,
-                          ODPP_NONE, false);
+                          tunnel_out_port, false);
 }
 
 static bool
@@ -5437,7 +5546,7 @@ xlate_actions(struct xlate_in *xin, struct xlate_out 
*xout)
         unsigned int user_cookie_offset = 0;
         if (!xin->frozen_state) {
             user_cookie_offset = compose_sflow_action(&ctx);
-            compose_ipfix_action(&ctx, ODPP_NONE);
+            compose_ipfix_action(&ctx, false, ODPP_NONE);
         }
         size_t sample_actions_len = ctx.odp_actions->size;
 
diff --git a/ofproto/ofproto.h b/ofproto/ofproto.h
index 8588872..774b5b6 100644
--- a/ofproto/ofproto.h
+++ b/ofproto/ofproto.h
@@ -89,6 +89,7 @@ struct ofproto_ipfix_flow_exporter_options {
     struct sset targets;
     uint32_t cache_active_timeout;
     uint32_t cache_max_flows;
+    bool enable_tunnel_sampling;
 };
 
 struct ofproto_rstp_status {
diff --git a/tests/odp.at b/tests/odp.at
index 808a83b..7b94c92 100644
--- a/tests/odp.at
+++ b/tests/odp.at
@@ -255,8 +255,8 @@ userspace(pid=9765,slow_path(cfm))
 userspace(pid=9765,slow_path(cfm),tunnel_out_port=10)
 userspace(pid=1234567,userdata(0102030405060708090a0b0c0d0e0f),actions)
 
userspace(pid=1234567,userdata(0102030405060708090a0b0c0d0e0f),tunnel_out_port=10)
-userspace(pid=6633,flow_sample(probability=123,collector_set_id=1234,obs_domain_id=2345,obs_point_id=3456))
-userspace(pid=6633,flow_sample(probability=123,collector_set_id=1234,obs_domain_id=2345,obs_point_id=3456),tunnel_out_port=10)
+userspace(pid=6633,flow_sample(probability=123,collector_set_id=1234,obs_domain_id=2345,obs_point_id=3456,output_port=10))
+userspace(pid=6633,flow_sample(probability=123,collector_set_id=1234,obs_domain_id=2345,obs_point_id=3456,output_port=10),tunnel_out_port=10)
 userspace(pid=6633,ipfix(output_port=10))
 userspace(pid=6633,ipfix(output_port=10),tunnel_out_port=10)
 set(in_port(2))
diff --git a/tests/ofp-actions.at b/tests/ofp-actions.at
index 83a2301..d128a0f 100644
--- a/tests/ofp-actions.at
+++ b/tests/ofp-actions.at
@@ -127,6 +127,9 @@ 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 DDD5 00003039 00005BA0 00008707 0000B26E 00000000
+
 # bad OpenFlow10 actions: OFPBAC_BAD_LEN
 & ofp_actions|WARN|OpenFlow action OFPAT_OUTPUT length 240 exceeds action 
buffer length 8
 & ofp_actions|WARN|bad action at offset 0 (OFPBAC_BAD_LEN):
diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
index 638d269..415a2c4 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -5826,6 +5826,82 @@ packets:2, bytes:120, used:0.001s, actions:drop
 OVS_VSWITCHD_STOP(["/sending to collector failed/d"])
 AT_CLEANUP
 
+dnl Flow IPFIX sanity check for tunnel set
+AT_SETUP([ofproto-dpif - Flow IPFIX sanity check - tunnel set])
+AT_XFAIL_IF([test "$IS_WIN32" = "yes"])
+OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=gre \
+                    options:remote_ip=1.1.1.1 options:local_ip=2.2.2.2 \
+                    options:key=5 ofport_request=1\
+                    -- add-port br0 p2 -- set Interface p2 type=stt \
+                    options:remote_ip=1.1.1.2 options:local_ip=2.2.2.3 \
+                    options:key=6 ofport_request=2\
+                    -- add-port br0 p3 -- set Interface p3 type=dummy \
+                    ofport_request=3 \
+                    -- --id=@br0 get Bridge br0 \
+                    -- --id=@ipfix create IPFIX targets=\"127.0.0.1:4739\" \
+                    -- --id=@cs create Flow_Sample_Collector_Set id=1 
bridge=@br0 ipfix=@ipfix],
+                   [<0>
+<1>
+])
+
+OVS_VSWITCHD_DISABLE_TUNNEL_PUSH_POP
+
+dnl NXA_SAMPLE: add openflow sample action without sampling_port.
+AT_DATA([flows.txt], [dnl
+in_port=3, actions=sample(probability=65535,collector_set_id=1),output:1
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt], [0], [ignore])
+
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 
'in_port(3),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.10.10.2,dst=10.10.10.1,proto=1,tos=1,ttl=128,frag=no),icmp(type=8,code=0)'],
 [0], [stdout])
+dnl For NXA_SAMPLE action, datapath scale the probability
+dnl from 16-bit to 32-bit while representing the same percentage
+AT_CHECK([tail -1 stdout], [0], [dnl
+Datapath actions: 
sample(sample=100.0%,actions(userspace(pid=0,flow_sample(probability=4294967295,collector_set_id=1,obs_domain_id=0,obs_point_id=0,output_port=4294967295)))),set(tunnel(tun_id=0x5,src=2.2.2.2,dst=1.1.1.1,tos=0x1,ttl=64,flags(df|key))),1
+])
+
+dnl Remove the flow which contains sample action.
+AT_CHECK([ovs-ofctl del-flows br0 in_port=3], [0], [ignore])
+AT_CHECK([ovs-appctl revalidator/purge])
+
+dnl NXA_SAMPLE2: add openflow sample action with sampling_port
+dnl which is equal to output port.
+AT_DATA([flows2.txt], [dnl
+in_port=3, 
actions=sample(probability=4294967295,collector_set_id=1,sampling_port=1),output:1
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows2.txt], [0], [ignore])
+
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 
'in_port(3),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.10.10.2,dst=10.10.10.1,proto=1,tos=1,ttl=128,frag=no),icmp(type=8,code=0)'],
 [0], [stdout])
+dnl Make sure flow sample action in datapath is behind set tunnel
+dnl action at egress point of tunnel port.
+AT_CHECK([tail -1 stdout], [0], [dnl
+Datapath actions: 
set(tunnel(tun_id=0x5,src=2.2.2.2,dst=1.1.1.1,tos=0x1,ttl=64,flags(df|key))),sample(sample=100.0%,actions(userspace(pid=0,flow_sample(probability=4294967295,collector_set_id=1,obs_domain_id=0,obs_point_id=0,output_port=1),tunnel_out_port=1))),1
+])
+
+dnl Remove the flow which contains sample action.
+AT_CHECK([ovs-ofctl del-flows br0 in_port=3], [0], [ignore])
+AT_CHECK([ovs-appctl revalidator/purge])
+
+dnl NXA_SAMPLE2: add a rule with two sample actions and
+dnl each sample action has a sampling_port
+AT_DATA([flows3.txt], [dnl
+in_port=3, 
actions=sample(probability=4294967295,collector_set_id=1,sampling_port=1),sample(probability=4294967295,collector_set_id=1,sampling_port=2),output:1,2
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows3.txt], [0], [ignore])
+
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 
'in_port(3),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.10.10.2,dst=10.10.10.1,proto=1,tos=1,ttl=128,frag=no),icmp(type=8,code=0)'],
 [0], [stdout])
+dnl Make sure flow sample action in datapath is behind set tunnel
+dnl action at egress point of tunnel port.
+AT_CHECK([tail -1 stdout], [0], [dnl
+Datapath actions: 
set(tunnel(tun_id=0x5,src=2.2.2.2,dst=1.1.1.1,tos=0x1,ttl=64,flags(df|key))),sample(sample=100.0%,actions(userspace(pid=0,flow_sample(probability=4294967295,collector_set_id=1,obs_domain_id=0,obs_point_id=0,output_port=1),tunnel_out_port=1))),1,set(tunnel(tun_id=0x6,src=2.2.2.3,dst=1.1.1.2,tos=0x1,ttl=64,flags(df|key))),sample(sample=100.0%,actions(userspace(pid=0,flow_sample(probability=4294967295,collector_set_id=1,obs_domain_id=0,obs_point_id=0,output_port=7471),tunnel_out_port=7471))),7471
+])
+
+dnl Remove the flow which contains sample action.
+AT_CHECK([ovs-ofctl del-flows br0 in_port=3], [0], [ignore])
+AT_CHECK([ovs-vsctl destroy Flow_Sample_Collector_Set 1], [0], [ignore])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
 AT_SETUP([ofproto-dpif - flow stats])
 OVS_VSWITCHD_START
 AT_CHECK([ovs-ofctl add-flow br0 "ip,actions=NORMAL"])
diff --git a/tests/ovs-ofctl.at b/tests/ovs-ofctl.at
index 8287cd2..613d9ce 100644
--- a/tests/ovs-ofctl.at
+++ b/tests/ovs-ofctl.at
@@ -160,6 +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=ct(nat)
 actions=ct(commit,nat(dst))
 actions=ct(commit,nat(src))
@@ -190,6 +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=ct(nat)
 OFPT_FLOW_MOD: ADD actions=ct(commit,nat(dst))
 OFPT_FLOW_MOD: ADD actions=ct(commit,nat(src))
@@ -219,6 +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)
 ]])
 
 AT_CHECK([ovs-ofctl --protocols OpenFlow11 parse-flows flows.txt
@@ -238,6 +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)
 ]])
 AT_CLEANUP
 
@@ -260,6 +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)
 ]])
 
 AT_CHECK([ovs-ofctl --protocols OpenFlow12 parse-flows flows.txt
@@ -283,6 +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)
 ]])
 AT_CLEANUP
 
@@ -375,6 +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)
 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[])))
@@ -417,6 +424,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 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]))
@@ -457,6 +465,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)
 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[[]])))
@@ -493,6 +502,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 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]]))
@@ -529,6 +539,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)
 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[])))
@@ -565,6 +576,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 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/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in
index e2e26f7..639306d 100644
--- a/utilities/ovs-ofctl.8.in
+++ b/utilities/ovs-ofctl.8.in
@@ -2291,6 +2291,8 @@ Samples packets and sends one sample for every sampled 
packet.
 .RS
 .IP "\fBprobability=\fIpackets\fR"
 The number of sampled packets out of 65535.  Must be greater or equal to 1.
+When \fBsampling_port\fR option is used and not specified as \fBNONE\fB,
+this option means sampled packets out of 4294967295.
 .IP "\fBcollector_set_id=\fIid\fR"
 The unsigned 32-bit integer identifier of the set of sample collectors
 to send sampled packets to.  Defaults to 0.
@@ -2300,6 +2302,12 @@ Observation Domain ID sent in every IPFIX flow record.  
Defaults to 0.
 .IP "\fBobs_point_id=\fIid\fR"
 When sending samples to IPFIX collectors, the unsigned 32-bit integer
 Observation Point ID sent in every IPFIX flow record.  Defaults to 0.
+.IP "\fBsampling_port=\fIport\fR"
+Sample packets on the port.  It can be set as input port or output
+port.  When this option is omitted, or specified as \fBNONE\fB, IPFIX
+does not differentiate between ingress packets and egress packets and
+does not export egress tunnel information.  This option was added in
+Open vSwitch 2.5.90.
 .RE
 .IP
 Refer to \fBovs\-vswitchd.conf.db\fR(8) for more details on
diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c
index 41ec4ba..4273552 100644
--- a/vswitchd/bridge.c
+++ b/vswitchd/bridge.c
@@ -1225,6 +1225,9 @@ bridge_configure_ipfix(struct bridge *br)
                     ? *fe_cfg->ipfix->cache_active_timeout : 0;
                 opts->cache_max_flows = fe_cfg->ipfix->cache_max_flows
                     ? *fe_cfg->ipfix->cache_max_flows : 0;
+                opts->enable_tunnel_sampling = smap_get_bool(
+                                                   
&fe_cfg->ipfix->other_config,
+                                                  "enable-tunnel-sampling", 
true);
                 opts++;
             }
         }
diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml
index 5cf1ee1..6e0ee80 100644
--- a/vswitchd/vswitch.xml
+++ b/vswitchd/vswitch.xml
@@ -4655,6 +4655,94 @@
       disabled.
     </column>
 
+    <column name="other_config" key="enable-tunnel-sampling"
+            type='{"type": "boolean"}'>
+      <p>
+        Set to <code>true</code> to enable sampling and reporting tunnel
+        header 7-tuples in IPFIX flow records.  Tunnel sampling is enabled
+        by default.
+      </p>
+
+      <p>
+        The following enterprise entities report the sampled tunnel info:
+      </p>
+
+      <dl>
+        <dt>tunnelType:</dt>
+        <dd>
+          <p>ID: 891, and enterprise ID 6876 (VMware).</p>
+          <p>type: unsigned 8-bit integer.</p>
+          <p>data type semantics: identifier.</p>
+          <p>description: Identifier of the layer 2 network overlay network
+          encapsulation type: 0x01 VxLAN, 0x02 GRE, 0x03 LISP, 0x05 IPsec+GRE,
+          0x07 GENEVE.</p>
+        </dd>
+        <dt>tunnelKey:</dt>
+        <dd>
+          <p>ID: 892, and enterprise ID 6876 (VMware).</p>
+          <p>type: variable-length octetarray.</p>
+          <p>data type semantics: identifier.</p>
+          <p>description: Key which is used for identifying an individual
+          traffic flow within a VxLAN (24-bit VNI), GENEVE (24-bit VNI),
+          GRE (32-bit key), or LISP (24-bit instance ID) tunnel. The
+          key is encoded in this octetarray as a 3-, 4-, or 8-byte integer
+          ID in network byte order.</p>
+        </dd>
+        <dt>tunnelSourceIPv4Address:</dt>
+        <dd>
+          <p>ID: 893, and enterprise ID 6876 (VMware).</p>
+          <p>type: unsigned 32-bit integer.</p>
+          <p>data type semantics: identifier.</p>
+          <p>description: The IPv4 source address in the tunnel IP packet
+          header.</p>
+        </dd>
+        <dt>tunnelDestinationIPv4Address:</dt>
+        <dd>
+          <p>ID: 894, and enterprise ID 6876 (VMware).</p>
+          <p>type: unsigned 32-bit integer.</p>
+          <p>data type semantics: identifier.</p>
+          <p>description: The IPv4 destination address in the tunnel IP
+          packet header.</p>
+        </dd>
+        <dt>tunnelProtocolIdentifier:</dt>
+        <dd>
+          <p>ID: 895, and enterprise ID 6876 (VMware).</p>
+          <p>type: unsigned 8-bit integer.</p>
+          <p>data type semantics: identifier.</p>
+          <p>description: The value of the protocol number in the tunnel
+          IP packet header. The protocol number identifies the tunnel IP
+          packet payload type.</p>
+        </dd>
+        <dt>tunnelSourceTransportPort:</dt>
+        <dd>
+          <p>ID: 896, and enterprise ID 6876 (VMware).</p>
+          <p>type: unsigned 16-bit integer.</p>
+          <p>data type semantics: identifier.</p>
+          <p>description: The source port identifier in the tunnel transport
+          header. For the transport protocols UDP, TCP, and SCTP, this is
+          the source port number given in the respective header.</p>
+        </dd>
+        <dt>tunnelDestinationTransportPort:</dt>
+        <dd>
+          <p>ID: 897, and enterprise ID 6876 (VMware).</p>
+          <p>type: unsigned 16-bit integer.</p>
+          <p>data type semantics: identifier.</p>
+          <p>description: The destination port identifier in the tunnel
+          transport header. For the transport protocols UDP, TCP, and SCTP,
+          this is the destination port number given in the respective header.
+          </p>
+        </dd>
+      </dl>
+
+      <p>
+        Before Open vSwitch 2.5.90, <ref column="other_config"
+        key="enable-tunnel-sampling"/> was only supported with per-bridge
+        sampling, and ignored otherwise.  Open vSwitch 2.5.90 and later support
+        <ref column="other_config" key="enable-tunnel-sampling"/> for
+        per-bridge and per-flow sampling.
+      </p>
+    </column>
+
     <group title="Per-Bridge Sampling">
       <p>
         These values affect only per-bridge sampling.  See above for a
@@ -4678,86 +4766,6 @@
         specified, defaults to 0.
       </column>
 
-      <column name="other_config" key="enable-tunnel-sampling"
-              type='{"type": "boolean"}'>
-        <p>
-          Set to <code>true</code> to enable sampling and reporting tunnel
-          header 7-tuples in IPFIX flow records.  Tunnel sampling is disabled
-          by default.
-        </p>
-
-        <p>
-          The following enterprise entities report the sampled tunnel info:
-        </p>
-
-        <dl>
-          <dt>tunnelType:</dt>
-          <dd>
-            <p>ID: 891, and enterprise ID 6876 (VMware).</p>
-            <p>type: unsigned 8-bit integer.</p>
-            <p>data type semantics: identifier.</p>
-            <p>description: Identifier of the layer 2 network overlay network
-            encapsulation type: 0x01 VxLAN, 0x02 GRE, 0x03 LISP, 0x05 
IPsec+GRE,
-            0x07 GENEVE.</p>
-          </dd>
-          <dt>tunnelKey:</dt>
-          <dd>
-            <p>ID: 892, and enterprise ID 6876 (VMware).</p>
-            <p>type: variable-length octetarray.</p>
-            <p>data type semantics: identifier.</p>
-            <p>description: Key which is used for identifying an individual
-            traffic flow within a VxLAN (24-bit VNI), GENEVE (24-bit VNI),
-            GRE (32-bit key), or LISP (24-bit instance ID) tunnel. The
-            key is encoded in this octetarray as a 3-, 4-, or 8-byte integer
-            ID in network byte order.</p>
-          </dd>
-          <dt>tunnelSourceIPv4Address:</dt>
-          <dd>
-            <p>ID: 893, and enterprise ID 6876 (VMware).</p>
-            <p>type: unsigned 32-bit integer.</p>
-            <p>data type semantics: identifier.</p>
-            <p>description: The IPv4 source address in the tunnel IP packet
-            header.</p>
-          </dd>
-          <dt>tunnelDestinationIPv4Address:</dt>
-          <dd>
-            <p>ID: 894, and enterprise ID 6876 (VMware).</p>
-            <p>type: unsigned 32-bit integer.</p>
-            <p>data type semantics: identifier.</p>
-            <p>description: The IPv4 destination address in the tunnel IP
-            packet header.</p>
-          </dd>
-          <dt>tunnelProtocolIdentifier:</dt>
-          <dd>
-            <p>ID: 895, and enterprise ID 6876 (VMware).</p>
-            <p>type: unsigned 8-bit integer.</p>
-            <p>data type semantics: identifier.</p>
-            <p>description: The value of the protocol number in the tunnel
-            IP packet header. The protocol number identifies the tunnel IP
-            packet payload type.</p>
-          </dd>
-          <dt>tunnelSourceTransportPort:</dt>
-          <dd>
-            <p>ID: 896, and enterprise ID 6876 (VMware).</p>
-            <p>type: unsigned 16-bit integer.</p>
-            <p>data type semantics: identifier.</p>
-            <p>description: The source port identifier in the tunnel transport
-            header. For the transport protocols UDP, TCP, and SCTP, this is
-            the source port number given in the respective header.</p>
-          </dd>
-          <dt>tunnelDestinationTransportPort:</dt>
-          <dd>
-            <p>ID: 897, and enterprise ID 6876 (VMware).</p>
-            <p>type: unsigned 16-bit integer.</p>
-            <p>data type semantics: identifier.</p>
-            <p>description: The destination port identifier in the tunnel
-            transport header. For the transport protocols UDP, TCP, and SCTP,
-            this is the destination port number given in the respective header.
-            </p>
-          </dd>
-        </dl>
-      </column>
-
       <column name="other_config" key="enable-input-sampling"
               type='{"type": "boolean"}'>
         By default, Open vSwitch samples and reports flows at bridge port input
-- 
1.9.1

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

Reply via email to