ofproto-dpif: Make ofport_dpif_cast() internally consistent.
[sliver-openvswitch.git] / ofproto / ofproto-dpif.c
index 33b09c6..0d17dbf 100644 (file)
@@ -20,6 +20,7 @@
 
 #include <errno.h>
 
+#include "bfd.h"
 #include "bond.h"
 #include "bundle.h"
 #include "byte-order.h"
@@ -46,6 +47,7 @@
 #include "ofp-parse.h"
 #include "ofp-print.h"
 #include "ofproto-dpif-governor.h"
+#include "ofproto-dpif-ipfix.h"
 #include "ofproto-dpif-sflow.h"
 #include "poll-loop.h"
 #include "simap.h"
@@ -303,9 +305,6 @@ struct initial_vals {
     * This member should be removed when the VLAN splinters feature is no
     * longer needed. */
     ovs_be16 vlan_tci;
-
-    /* If received on a tunnel, the IP TOS value of the tunnel. */
-    uint8_t tunnel_ip_tos;
 };
 
 static void action_xlate_ctx_init(struct action_xlate_ctx *,
@@ -325,7 +324,8 @@ static void xlate_table_action(struct action_xlate_ctx *, uint16_t in_port,
 static size_t put_userspace_action(const struct ofproto_dpif *,
                                    struct ofpbuf *odp_actions,
                                    const struct flow *,
-                                   const union user_action_cookie *);
+                                   const union user_action_cookie *,
+                                   const size_t);
 
 static void compose_slow_path(const struct ofproto_dpif *, const struct flow *,
                               enum slow_path_reason,
@@ -529,6 +529,7 @@ struct ofport_dpif {
     struct ofbundle *bundle;    /* Bundle that contains this port, if any. */
     struct list bundle_node;    /* In struct ofbundle's "ports" list. */
     struct cfm *cfm;            /* Connectivity Fault Management, if any. */
+    struct bfd *bfd;            /* BFD, if any. */
     tag_type tag;               /* Tag associated with this port. */
     bool may_enable;            /* May be enabled in bonds. */
     long long int carrier_seq;  /* Carrier status changes. */
@@ -589,7 +590,6 @@ static uint16_t odp_port_to_ofp_port(const struct ofproto_dpif *,
 static struct ofport_dpif *
 ofport_dpif_cast(const struct ofport *ofport)
 {
-    ovs_assert(ofport->ofproto->ofproto_class == &ofproto_dpif_class);
     return ofport ? CONTAINER_OF(ofport, struct ofport_dpif, up) : NULL;
 }
 
@@ -695,6 +695,7 @@ struct ofproto_dpif {
     /* Bridging. */
     struct netflow *netflow;
     struct dpif_sflow *sflow;
+    struct dpif_ipfix *ipfix;
     struct hmap bundles;        /* Contains "struct ofbundle"s. */
     struct mac_learning *ml;
     struct ofmirror *mirrors[MAX_MIRRORS];
@@ -820,6 +821,9 @@ static int send_packet(const struct ofport_dpif *, struct ofpbuf *packet);
 static size_t compose_sflow_action(const struct ofproto_dpif *,
                                    struct ofpbuf *odp_actions,
                                    const struct flow *, uint32_t odp_port);
+static void compose_ipfix_action(const struct ofproto_dpif *,
+                                 struct ofpbuf *odp_actions,
+                                 const struct flow *);
 static void add_mirror_actions(struct action_xlate_ctx *ctx,
                                const struct flow *flow);
 /* Global variables. */
@@ -1349,6 +1353,7 @@ construct(struct ofproto *ofproto_)
 
     ofproto->netflow = NULL;
     ofproto->sflow = NULL;
+    ofproto->ipfix = NULL;
     ofproto->stp = NULL;
     hmap_init(&ofproto->bundles);
     ofproto->ml = mac_learning_create(MAC_ENTRY_DEFAULT_IDLE_TIME);
@@ -1771,6 +1776,7 @@ port_construct(struct ofport *port_)
     ofproto->backer->need_revalidate = REV_RECONFIGURE;
     port->bundle = NULL;
     port->cfm = NULL;
+    port->bfd = NULL;
     port->tag = tag_create_random();
     port->may_enable = true;
     port->stp_port = NULL;
@@ -1782,7 +1788,11 @@ port_construct(struct ofport *port_)
     port->carrier_seq = netdev_get_carrier_resets(netdev);
 
     if (netdev_vport_is_patch(netdev)) {
-        /* XXX By bailing out here, we don't do required sFlow work. */
+        /* By bailing out here, we don't submit the port to the sFlow module
+        * to be considered for counter polling export.  This is correct
+        * because the patch port represents an interface that sFlow considers
+        * to be "internal" to the switch as a whole, and therefore not an
+        * candidate for counter polling. */
         port->odp_port = OVSP_NONE;
         return 0;
     }
@@ -1913,6 +1923,32 @@ set_sflow(struct ofproto *ofproto_,
     return 0;
 }
 
+static int
+set_ipfix(
+    struct ofproto *ofproto_,
+    const struct ofproto_ipfix_bridge_exporter_options *bridge_exporter_options,
+    const struct ofproto_ipfix_flow_exporter_options *flow_exporters_options,
+    size_t n_flow_exporters_options)
+{
+    struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+    struct dpif_ipfix *di = ofproto->ipfix;
+
+    if (bridge_exporter_options || flow_exporters_options) {
+        if (!di) {
+            di = ofproto->ipfix = dpif_ipfix_create();
+        }
+        dpif_ipfix_set_options(
+            di, bridge_exporter_options, flow_exporters_options,
+            n_flow_exporters_options);
+    } else {
+        if (di) {
+            dpif_ipfix_destroy(di);
+            ofproto->ipfix = NULL;
+        }
+    }
+    return 0;
+}
+
 static int
 set_cfm(struct ofport *ofport_, const struct cfm_settings *s)
 {
@@ -1957,6 +1993,35 @@ get_cfm_status(const struct ofport *ofport_,
         return false;
     }
 }
+
+static int
+set_bfd(struct ofport *ofport_, const struct smap *cfg)
+{
+    struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport_->ofproto);
+    struct ofport_dpif *ofport = ofport_dpif_cast(ofport_);
+    struct bfd *old;
+
+    old = ofport->bfd;
+    ofport->bfd = bfd_configure(old, netdev_get_name(ofport->up.netdev), cfg);
+    if (ofport->bfd != old) {
+        ofproto->backer->need_revalidate = REV_RECONFIGURE;
+    }
+
+    return 0;
+}
+
+static int
+get_bfd_status(struct ofport *ofport_, struct smap *smap)
+{
+    struct ofport_dpif *ofport = ofport_dpif_cast(ofport_);
+
+    if (ofport->bfd) {
+        bfd_get_status(ofport->bfd, smap);
+        return 0;
+    } else {
+        return ENOENT;
+    }
+}
 \f
 /* Spanning Tree. */
 
@@ -3096,6 +3161,15 @@ port_run_fast(struct ofport_dpif *ofport)
         send_packet(ofport, &packet);
         ofpbuf_uninit(&packet);
     }
+
+    if (ofport->bfd && bfd_should_send_packet(ofport->bfd)) {
+        struct ofpbuf packet;
+
+        ofpbuf_init(&packet, 0);
+        bfd_put_packet(ofport->bfd, &packet, ofport->up.pp.hw_addr);
+        send_packet(ofport, &packet);
+        ofpbuf_uninit(&packet);
+    }
 }
 
 static void
@@ -3126,6 +3200,11 @@ port_run(struct ofport_dpif *ofport)
         }
     }
 
+    if (ofport->bfd) {
+        bfd_run(ofport->bfd);
+        enable = enable && bfd_forwarding(ofport->bfd);
+    }
+
     if (ofport->bundle) {
         enable = enable && lacp_slave_may_enable(ofport->bundle->lacp, ofport);
         if (carrier_changed) {
@@ -3150,6 +3229,10 @@ port_wait(struct ofport_dpif *ofport)
     if (ofport->cfm) {
         cfm_wait(ofport->cfm);
     }
+
+    if (ofport->bfd) {
+        bfd_wait(ofport->bfd);
+    }
 }
 
 static int
@@ -3473,6 +3556,11 @@ process_special(struct ofproto_dpif *ofproto, const struct flow *flow,
             cfm_process_heartbeat(ofport->cfm, packet);
         }
         return SLOW_CFM;
+    } else if (ofport->bfd && bfd_should_process_flow(flow)) {
+        if (packet) {
+            bfd_process_packet(ofport->bfd, flow, packet);
+        }
+        return SLOW_BFD;
     } else if (ofport->bundle && ofport->bundle->lacp
                && flow->dl_type == htons(ETH_TYPE_LACP)) {
         if (packet) {
@@ -3800,8 +3888,7 @@ drop_key_clear(struct dpif_backer *backer)
  * to the VLAN TCI with which the packet was really received, that is, the
  * actual VLAN TCI extracted by odp_flow_key_to_flow().  (This differs from
  * the value returned in flow->vlan_tci only for packets received on
- * VLAN splinters.)  Also, if received on an IP tunnel, sets
- * 'initial_vals->tunnel_ip_tos' to the tunnel's IP TOS.
+ * VLAN splinters.)
  *
  * Similarly, this function also includes some logic to help with tunnels.  It
  * may modify 'flow' as necessary to make the tunneling implementation
@@ -3828,7 +3915,6 @@ ofproto_receive(const struct dpif_backer *backer, struct ofpbuf *packet,
 
     if (initial_vals) {
         initial_vals->vlan_tci = flow->vlan_tci;
-        initial_vals->tunnel_ip_tos = flow->tunnel.ip_tos;
     }
 
     if (odp_in_port) {
@@ -3843,9 +3929,6 @@ ofproto_receive(const struct dpif_backer *backer, struct ofpbuf *packet,
         }
         port = ofport_dpif_cast(ofport);
 
-        /* We can't reproduce 'key' from 'flow'. */
-        fitness = fitness == ODP_FIT_PERFECT ? ODP_FIT_TOO_MUCH : fitness;
-
         /* XXX: Since the tunnel module is not scoped per backer, it's
          * theoretically possible that we'll receive an ofport belonging to an
          * entirely different datapath.  In practice, this can't happen because
@@ -4006,9 +4089,11 @@ handle_miss_upcalls(struct dpif_backer *backer, struct dpif_upcall *upcalls,
     hmap_destroy(&todo);
 }
 
-static enum { SFLOW_UPCALL, MISS_UPCALL, BAD_UPCALL }
+static enum { SFLOW_UPCALL, MISS_UPCALL, BAD_UPCALL, FLOW_SAMPLE_UPCALL,
+              IPFIX_UPCALL }
 classify_upcall(const struct dpif_upcall *upcall)
 {
+    size_t userdata_len;
     union user_action_cookie cookie;
 
     /* First look at the upcall type. */
@@ -4030,23 +4115,30 @@ classify_upcall(const struct dpif_upcall *upcall)
         VLOG_WARN_RL(&rl, "action upcall missing cookie");
         return BAD_UPCALL;
     }
-    if (nl_attr_get_size(upcall->userdata) != sizeof(cookie)) {
+    userdata_len = nl_attr_get_size(upcall->userdata);
+    if (userdata_len < sizeof cookie.type
+        || userdata_len > sizeof cookie) {
         VLOG_WARN_RL(&rl, "action upcall cookie has unexpected size %zu",
-                     nl_attr_get_size(upcall->userdata));
+                     userdata_len);
         return BAD_UPCALL;
     }
-    memcpy(&cookie, nl_attr_get(upcall->userdata), sizeof(cookie));
-    switch (cookie.type) {
-    case USER_ACTION_COOKIE_SFLOW:
+    memset(&cookie, 0, sizeof cookie);
+    memcpy(&cookie, nl_attr_get(upcall->userdata), userdata_len);
+    if (userdata_len == sizeof cookie.sflow
+        && cookie.type == USER_ACTION_COOKIE_SFLOW) {
         return SFLOW_UPCALL;
-
-    case USER_ACTION_COOKIE_SLOW_PATH:
+    } else if (userdata_len == sizeof cookie.slow_path
+               && cookie.type == USER_ACTION_COOKIE_SLOW_PATH) {
         return MISS_UPCALL;
-
-    case USER_ACTION_COOKIE_UNSPEC:
-    default:
-        VLOG_WARN_RL(&rl, "invalid user cookie : 0x%"PRIx64,
-                     nl_attr_get_u64(upcall->userdata));
+    } else if (userdata_len == sizeof cookie.flow_sample
+               && cookie.type == USER_ACTION_COOKIE_FLOW_SAMPLE) {
+        return FLOW_SAMPLE_UPCALL;
+    } else if (userdata_len == sizeof cookie.ipfix
+               && cookie.type == USER_ACTION_COOKIE_IPFIX) {
+        return IPFIX_UPCALL;
+    } else {
+        VLOG_WARN_RL(&rl, "invalid user cookie of type %"PRIu16
+                     " and size %zu", cookie.type, userdata_len);
         return BAD_UPCALL;
     }
 }
@@ -4066,11 +4158,56 @@ handle_sflow_upcall(struct dpif_backer *backer,
         return;
     }
 
-    memcpy(&cookie, nl_attr_get(upcall->userdata), sizeof(cookie));
+    memset(&cookie, 0, sizeof cookie);
+    memcpy(&cookie, nl_attr_get(upcall->userdata), sizeof cookie.sflow);
     dpif_sflow_received(ofproto->sflow, upcall->packet, &flow,
                         odp_in_port, &cookie);
 }
 
+static void
+handle_flow_sample_upcall(struct dpif_backer *backer,
+                          const struct dpif_upcall *upcall)
+{
+    struct ofproto_dpif *ofproto;
+    union user_action_cookie cookie;
+    struct flow flow;
+
+    if (ofproto_receive(backer, upcall->packet, upcall->key, upcall->key_len,
+                        &flow, NULL, &ofproto, NULL, NULL)
+        || !ofproto->ipfix) {
+        return;
+    }
+
+    memset(&cookie, 0, sizeof cookie);
+    memcpy(&cookie, nl_attr_get(upcall->userdata), sizeof cookie.flow_sample);
+
+    /* The flow reflects exactly the contents of the packet.  Sample
+     * the packet using it. */
+    dpif_ipfix_flow_sample(ofproto->ipfix, upcall->packet, &flow,
+                           cookie.flow_sample.collector_set_id,
+                           cookie.flow_sample.probability,
+                           cookie.flow_sample.obs_domain_id,
+                           cookie.flow_sample.obs_point_id);
+}
+
+static void
+handle_ipfix_upcall(struct dpif_backer *backer,
+                    const struct dpif_upcall *upcall)
+{
+    struct ofproto_dpif *ofproto;
+    struct flow flow;
+
+    if (ofproto_receive(backer, upcall->packet, upcall->key, upcall->key_len,
+                        &flow, NULL, &ofproto, NULL, NULL)
+        || !ofproto->ipfix) {
+        return;
+    }
+
+    /* The flow reflects exactly the contents of the packet.  Sample
+     * the packet using it. */
+    dpif_ipfix_bridge_sample(ofproto->ipfix, upcall->packet, &flow);
+}
+
 static int
 handle_upcalls(struct dpif_backer *backer, unsigned int max_batch)
 {
@@ -4108,6 +4245,16 @@ handle_upcalls(struct dpif_backer *backer, unsigned int max_batch)
             ofpbuf_uninit(buf);
             break;
 
+        case FLOW_SAMPLE_UPCALL:
+            handle_flow_sample_upcall(backer, upcall);
+            ofpbuf_uninit(buf);
+            break;
+
+        case IPFIX_UPCALL:
+            handle_ipfix_upcall(backer, upcall);
+            ofpbuf_uninit(buf);
+            break;
+
         case BAD_UPCALL:
             ofpbuf_uninit(buf);
             break;
@@ -4426,7 +4573,7 @@ expire_subfacets(struct ofproto_dpif *ofproto, int dp_max_idle)
                         &ofproto->subfacets) {
         long long int cutoff;
 
-        cutoff = (subfacet->slow & (SLOW_CFM | SLOW_LACP | SLOW_STP)
+        cutoff = (subfacet->slow & (SLOW_CFM | SLOW_BFD | SLOW_LACP | SLOW_STP)
                   ? special_cutoff
                   : normal_cutoff);
         if (subfacet->used < cutoff) {
@@ -5651,7 +5798,6 @@ rule_dpif_execute(struct rule_dpif *rule, const struct flow *flow,
     rule_credit_stats(rule, &stats);
 
     initial_vals.vlan_tci = flow->vlan_tci;
-    initial_vals.tunnel_ip_tos = flow->tunnel.ip_tos;
     ofpbuf_use_stub(&odp_actions, odp_actions_stub, sizeof odp_actions_stub);
     action_xlate_ctx_init(&ctx, ofproto, flow, &initial_vals,
                           rule, stats.tcp_flags, packet);
@@ -5747,6 +5893,7 @@ send_packet(const struct ofport_dpif *ofport, struct ofpbuf *packet)
                            ofp_port_to_odp_port(ofproto, flow.in_port));
 
     compose_sflow_action(ofproto, &odp_actions, &flow, odp_port);
+    compose_ipfix_action(ofproto, &odp_actions, &flow);
 
     nl_msg_put_u32(&odp_actions, OVS_ACTION_ATTR_OUTPUT, odp_port);
     error = dpif_execute(ofproto->backer->dpif,
@@ -5794,11 +5941,12 @@ compose_slow_path(const struct ofproto_dpif *ofproto, const struct flow *flow,
     cookie.slow_path.reason = slow;
 
     ofpbuf_use_stack(&buf, stub, stub_size);
-    if (slow & (SLOW_CFM | SLOW_LACP | SLOW_STP)) {
+    if (slow & (SLOW_CFM | SLOW_BFD | SLOW_LACP | SLOW_STP)) {
         uint32_t pid = dpif_port_get_pid(ofproto->backer->dpif, UINT32_MAX);
-        odp_put_userspace_action(pid, &cookie, sizeof cookie, &buf);
+        odp_put_userspace_action(pid, &cookie, sizeof cookie.slow_path, &buf);
     } else {
-        put_userspace_action(ofproto, &buf, flow, &cookie);
+        put_userspace_action(ofproto, &buf, flow, &cookie,
+                             sizeof cookie.slow_path);
     }
     *actionsp = buf.data;
     *actions_lenp = buf.size;
@@ -5808,14 +5956,43 @@ static size_t
 put_userspace_action(const struct ofproto_dpif *ofproto,
                      struct ofpbuf *odp_actions,
                      const struct flow *flow,
-                     const union user_action_cookie *cookie)
+                     const union user_action_cookie *cookie,
+                     const size_t cookie_size)
 {
     uint32_t pid;
 
     pid = dpif_port_get_pid(ofproto->backer->dpif,
                             ofp_port_to_odp_port(ofproto, flow->in_port));
 
-    return odp_put_userspace_action(pid, cookie, sizeof *cookie, odp_actions);
+    return odp_put_userspace_action(pid, cookie, cookie_size, odp_actions);
+}
+
+/* Compose SAMPLE action for sFlow or IPFIX.  The given probability is
+ * the number of packets out of UINT32_MAX to sample.  The given
+ * cookie is passed back in the callback for each sampled packet.
+ */
+static size_t
+compose_sample_action(const struct ofproto_dpif *ofproto,
+                      struct ofpbuf *odp_actions,
+                      const struct flow *flow,
+                      const uint32_t probability,
+                      const union user_action_cookie *cookie,
+                      const size_t cookie_size)
+{
+    size_t sample_offset, actions_offset;
+    int cookie_offset;
+
+    sample_offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_SAMPLE);
+
+    nl_msg_put_u32(odp_actions, OVS_SAMPLE_ATTR_PROBABILITY, probability);
+
+    actions_offset = nl_msg_start_nested(odp_actions, OVS_SAMPLE_ATTR_ACTIONS);
+    cookie_offset = put_userspace_action(ofproto, odp_actions, flow, cookie,
+                                         cookie_size);
+
+    nl_msg_end_nested(odp_actions, actions_offset);
+    nl_msg_end_nested(odp_actions, sample_offset);
+    return cookie_offset;
 }
 
 static void
@@ -5850,7 +6027,7 @@ compose_sflow_cookie(const struct ofproto_dpif *ofproto,
     }
 }
 
-/* Compose SAMPLE action for sFlow. */
+/* Compose SAMPLE action for sFlow bridge sampling. */
 static size_t
 compose_sflow_action(const struct ofproto_dpif *ofproto,
                      struct ofpbuf *odp_actions,
@@ -5859,32 +6036,60 @@ compose_sflow_action(const struct ofproto_dpif *ofproto,
 {
     uint32_t probability;
     union user_action_cookie cookie;
-    size_t sample_offset, actions_offset;
-    int cookie_offset;
 
     if (!ofproto->sflow || flow->in_port == OFPP_NONE) {
         return 0;
     }
 
-    sample_offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_SAMPLE);
-
-    /* Number of packets out of UINT_MAX to sample. */
     probability = dpif_sflow_get_probability(ofproto->sflow);
-    nl_msg_put_u32(odp_actions, OVS_SAMPLE_ATTR_PROBABILITY, probability);
-
-    actions_offset = nl_msg_start_nested(odp_actions, OVS_SAMPLE_ATTR_ACTIONS);
     compose_sflow_cookie(ofproto, htons(0), odp_port,
                          odp_port == OVSP_NONE ? 0 : 1, &cookie);
-    cookie_offset = put_userspace_action(ofproto, odp_actions, flow, &cookie);
 
-    nl_msg_end_nested(odp_actions, actions_offset);
-    nl_msg_end_nested(odp_actions, sample_offset);
-    return cookie_offset;
+    return compose_sample_action(ofproto, odp_actions, flow,  probability,
+                                 &cookie, sizeof cookie.sflow);
+}
+
+static void
+compose_flow_sample_cookie(uint16_t probability, uint32_t collector_set_id,
+                           uint32_t obs_domain_id, uint32_t obs_point_id,
+                           union user_action_cookie *cookie)
+{
+    cookie->type = USER_ACTION_COOKIE_FLOW_SAMPLE;
+    cookie->flow_sample.probability = probability;
+    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;
+}
+
+static void
+compose_ipfix_cookie(union user_action_cookie *cookie)
+{
+    cookie->type = USER_ACTION_COOKIE_IPFIX;
 }
 
-/* SAMPLE action must be first action in any given list of actions.
- * At this point we do not have all information required to build it. So try to
- * build sample action as complete as possible. */
+/* Compose SAMPLE action for IPFIX bridge sampling. */
+static void
+compose_ipfix_action(const struct ofproto_dpif *ofproto,
+                     struct ofpbuf *odp_actions,
+                     const struct flow *flow)
+{
+    uint32_t probability;
+    union user_action_cookie cookie;
+
+    if (!ofproto->ipfix || flow->in_port == OFPP_NONE) {
+        return;
+    }
+
+    probability = dpif_ipfix_get_bridge_exporter_probability(ofproto->ipfix);
+    compose_ipfix_cookie(&cookie);
+
+    compose_sample_action(ofproto, odp_actions, flow,  probability,
+                          &cookie, sizeof cookie.ipfix);
+}
+
+/* SAMPLE action for sFlow must be first action in any given list of
+ * actions.  At this point we do not have all information required to
+ * build it. So try to build sample action as complete as possible. */
 static void
 add_sflow_action(struct action_xlate_ctx *ctx)
 {
@@ -5895,6 +6100,14 @@ add_sflow_action(struct action_xlate_ctx *ctx)
     ctx->sflow_n_outputs = 0;
 }
 
+/* SAMPLE action for IPFIX must be 1st or 2nd action in any given list
+ * of actions, eventually after the SAMPLE action for sFlow. */
+static void
+add_ipfix_action(struct action_xlate_ctx *ctx)
+{
+    compose_ipfix_action(ctx->ofproto, ctx->odp_actions, &ctx->flow);
+}
+
 /* Fix SAMPLE action according to data collected while composing ODP actions.
  * We need to fix SAMPLE actions OVS_SAMPLE_ATTR_ACTIONS attribute, i.e. nested
  * USERSPACE action's user-cookie which is required for sflow. */
@@ -5909,7 +6122,7 @@ fix_sflow_action(struct action_xlate_ctx *ctx)
     }
 
     cookie = ofpbuf_at(ctx->odp_actions, ctx->user_cookie_offset,
-                       sizeof(*cookie));
+                       sizeof cookie->sflow);
     ovs_assert(cookie->type == USER_ACTION_COOKIE_SFLOW);
 
     compose_sflow_cookie(ctx->ofproto, base->vlan_tci,
@@ -6540,6 +6753,23 @@ xlate_fin_timeout(struct action_xlate_ctx *ctx,
     }
 }
 
+static void
+xlate_sample_action(struct action_xlate_ctx *ctx,
+                    const struct ofpact_sample *os)
+{
+  union user_action_cookie cookie;
+  /* Scale the probability from 16-bit to 32-bit while representing
+   * the same percentage. */
+  uint32_t probability = (os->probability << 16) | os->probability;
+
+  commit_odp_actions(&ctx->flow, &ctx->base_flow, ctx->odp_actions);
+
+  compose_flow_sample_cookie(os->probability, os->collector_set_id,
+                             os->obs_domain_id, os->obs_point_id, &cookie);
+  compose_sample_action(ctx->ofproto, ctx->odp_actions, &ctx->flow,
+                        probability, &cookie, sizeof cookie.flow_sample);
+}
+
 static bool
 may_receive(const struct ofport_dpif *port, struct action_xlate_ctx *ctx)
 {
@@ -6565,7 +6795,7 @@ static bool
 tunnel_ecn_ok(struct action_xlate_ctx *ctx)
 {
     if (is_ip_any(&ctx->base_flow)
-        && (ctx->base_flow.tunnel.ip_tos & IP_ECN_MASK) == IP_ECN_CE) {
+        && (ctx->flow.tunnel.ip_tos & IP_ECN_MASK) == IP_ECN_CE) {
         if ((ctx->base_flow.nw_tos & IP_ECN_MASK) == IP_ECN_NOT_ECT) {
             VLOG_WARN_RL(&rl, "dropping tunnel packet marked ECN CE"
                          " but is not ECN capable");
@@ -6820,6 +7050,10 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
             }
             break;
         }
+
+        case OFPACT_SAMPLE:
+            xlate_sample_action(ctx, ofpact_get_SAMPLE(a));
+            break;
         }
     }
 
@@ -6836,8 +7070,6 @@ action_xlate_ctx_init(struct action_xlate_ctx *ctx,
                       struct rule_dpif *rule,
                       uint8_t tcp_flags, const struct ofpbuf *packet)
 {
-    ovs_be64 initial_tun_id = flow->tunnel.tun_id;
-
     /* Flow initialization rules:
      * - 'base_flow' must match the kernel's view of the packet at the
      *   time that action processing starts.  'flow' represents any
@@ -6849,23 +7081,21 @@ action_xlate_ctx_init(struct action_xlate_ctx *ctx,
      *   to another device without any modifications this will cause us to
      *   insert a new tag since the original one was stripped off by the
      *   VLAN device.
-     * - Tunnel 'flow' is largely cleared when transitioning between
-     *   the input and output stages since it does not make sense to output
-     *   a packet with the exact headers that it was received with (i.e.
-     *   the destination IP is us).  The one exception is the tun_id, which
-     *   is preserved to allow use in later resubmit lookups and loads into
-     *   registers.
+     * - Tunnel metadata as received is retained in 'flow'. This allows
+     *   tunnel metadata matching also in later tables.
+     *   Since a kernel action for setting the tunnel metadata will only be
+     *   generated with actual tunnel output, changing the tunnel metadata
+     *   values in 'flow' (such as tun_id) will only have effect with a later
+     *   tunnel output action.
      * - Tunnel 'base_flow' is completely cleared since that is what the
      *   kernel does.  If we wish to maintain the original values an action
      *   needs to be generated. */
 
     ctx->ofproto = ofproto;
     ctx->flow = *flow;
-    memset(&ctx->flow.tunnel, 0, sizeof ctx->flow.tunnel);
     ctx->base_flow = ctx->flow;
+    memset(&ctx->base_flow.tunnel, 0, sizeof ctx->base_flow.tunnel);
     ctx->base_flow.vlan_tci = initial_vals->vlan_tci;
-    ctx->base_flow.tunnel.ip_tos = initial_vals->tunnel_ip_tos;
-    ctx->flow.tunnel.tun_id = initial_tun_id;
     ctx->rule = rule;
     ctx->packet = packet;
     ctx->may_learn = packet != NULL;
@@ -6948,12 +7178,14 @@ xlate_actions(struct action_xlate_ctx *ctx,
     } else {
         static struct vlog_rate_limit trace_rl = VLOG_RATE_LIMIT_INIT(1, 1);
         struct initial_vals initial_vals;
+        size_t sample_actions_len;
         uint32_t local_odp_port;
 
         initial_vals.vlan_tci = ctx->base_flow.vlan_tci;
-        initial_vals.tunnel_ip_tos = ctx->base_flow.tunnel.ip_tos;
 
         add_sflow_action(ctx);
+        add_ipfix_action(ctx);
+        sample_actions_len = ctx->odp_actions->size;
 
         if (tunnel_ecn_ok(ctx) && (!in_port || may_receive(in_port, ctx))) {
             do_xlate_actions(ofpacts, ofpacts_len, ctx);
@@ -6961,8 +7193,7 @@ xlate_actions(struct action_xlate_ctx *ctx,
             /* We've let OFPP_NORMAL and the learning action look at the
              * packet, so drop it now if forwarding is disabled. */
             if (in_port && !stp_forward_in_state(in_port->stp_state)) {
-                ofpbuf_clear(ctx->odp_actions);
-                add_sflow_action(ctx);
+                ctx->odp_actions->size = sample_actions_len;
             }
         }
 
@@ -7714,7 +7945,6 @@ packet_out(struct ofproto *ofproto_, struct ofpbuf *packet,
     dpif_flow_stats_extract(flow, packet, time_msec(), &stats);
 
     initial_vals.vlan_tci = flow->vlan_tci;
-    initial_vals.tunnel_ip_tos = 0;
     action_xlate_ctx_init(&ctx, ofproto, flow, &initial_vals, NULL,
                           packet_get_tcp_flags(packet, flow), packet);
     ctx.resubmit_stats = &stats;
@@ -8017,7 +8247,6 @@ ofproto_unixctl_trace(struct unixctl_conn *conn, int argc, const char *argv[],
             }
 
             initial_vals.vlan_tci = flow.vlan_tci;
-            initial_vals.tunnel_ip_tos = flow.tunnel.ip_tos;
         }
 
         /* Generate a packet, if requested. */
@@ -8052,7 +8281,6 @@ ofproto_unixctl_trace(struct unixctl_conn *conn, int argc, const char *argv[],
         flow_extract(packet, priority, mark, NULL, in_port, &flow);
         flow.tunnel.tun_id = tun_id;
         initial_vals.vlan_tci = flow.vlan_tci;
-        initial_vals.tunnel_ip_tos = flow.tunnel.ip_tos;
     } else {
         unixctl_command_reply_error(conn, "Bad command syntax");
         goto exit;
@@ -8131,6 +8359,9 @@ ofproto_trace(struct ofproto_dpif *ofproto, const struct flow *flow,
                 case SLOW_STP:
                     ds_put_cstr(ds, "\n\t- Consists of STP packets.");
                     break;
+                case SLOW_BFD:
+                    ds_put_cstr(ds, "\n\t- Consists of BFD packets.");
+                    break;
                 case SLOW_IN_BAND:
                     ds_put_cstr(ds, "\n\t- Needs in-band special case "
                                 "processing.");
@@ -8852,8 +9083,11 @@ const struct ofproto_class ofproto_dpif_class = {
     set_netflow,
     get_netflow_ids,
     set_sflow,
+    set_ipfix,
     set_cfm,
     get_cfm_status,
+    set_bfd,
+    get_bfd_status,
     set_stp,
     get_stp_status,
     set_stp_port,