ofproto: Fix wildcard masking with nw_tos.
[sliver-openvswitch.git] / ofproto / ofproto-dpif-xlate.c
index eb4931e..db27f89 100644 (file)
@@ -58,6 +58,8 @@ VLOG_DEFINE_THIS_MODULE(ofproto_dpif_xlate);
 /* Maximum depth of flow table recursion (due to resubmit actions) in a
  * flow translation. */
 #define MAX_RESUBMIT_RECURSION 64
+#define MAX_INTERNAL_RESUBMITS 1   /* Max resbmits allowed using rules in
+                                      internal table. */
 
 /* Maximum number of resubmit actions in a flow translation, whether they are
  * recursive or not. */
@@ -89,6 +91,9 @@ struct xbridge {
     bool has_in_band;             /* Bridge has in band control? */
     bool forward_bpdu;            /* Bridge forwards STP BPDUs? */
 
+    /* True if the datapath supports recirculation. */
+    bool enable_recirc;
+
     /* True if the datapath supports variable-length
      * OVS_USERSPACE_ATTR_USERDATA in OVS_ACTION_ATTR_USERSPACE actions.
      * False if the datapath supports only 8-byte (or shorter) userdata. */
@@ -201,9 +206,11 @@ struct xlate_ctx {
  * it did not arrive on a "real" port.  'ofpp_none_bundle' exists for
  * when an input bundle is needed for validation (e.g., mirroring or
  * OFPP_NORMAL processing).  It is not connected to an 'ofproto' or have
- * any 'port' structs, so care must be taken when dealing with it.
- * The bundle's name and vlan mode are initialized in lookup_input_bundle() */
-static struct xbundle ofpp_none_bundle;
+ * any 'port' structs, so care must be taken when dealing with it. */
+static struct xbundle ofpp_none_bundle = {
+    .name      = "OFPP_NONE",
+    .vlan_mode = PORT_VLAN_TRUNK
+};
 
 /* Node in 'xport''s 'skb_priorities' map.  Used to maintain a map from
  * 'priority' (the datapath's term for QoS queue) to the dscp bits which all
@@ -224,10 +231,11 @@ static void do_xlate_actions(const struct ofpact *, size_t ofpacts_len,
                              struct xlate_ctx *);
 static void xlate_actions__(struct xlate_in *, struct xlate_out *)
     OVS_REQ_RDLOCK(xlate_rwlock);
-    static void xlate_normal(struct xlate_ctx *);
-    static void xlate_report(struct xlate_ctx *, const char *);
-    static void xlate_table_action(struct xlate_ctx *, ofp_port_t in_port,
-                                   uint8_t table_id, bool may_packet_in);
+static void xlate_normal(struct xlate_ctx *);
+static void xlate_report(struct xlate_ctx *, const char *);
+static void xlate_table_action(struct xlate_ctx *, ofp_port_t in_port,
+                               uint8_t table_id, bool may_packet_in,
+                               bool honor_table_miss);
 static bool input_vid_is_valid(uint16_t vid, struct xbundle *, bool warn);
 static uint16_t input_vid_to_vlan(const struct xbundle *, uint16_t vid);
 static void output_normal(struct xlate_ctx *, const struct xbundle *,
@@ -254,6 +262,7 @@ xlate_ofproto_set(struct ofproto_dpif *ofproto, const char *name,
                   const struct dpif_ipfix *ipfix,
                   const struct netflow *netflow, enum ofp_config_flags frag,
                   bool forward_bpdu, bool has_in_band,
+                  bool enable_recirc,
                   bool variable_length_userdata,
                   size_t max_mpls_depth)
 {
@@ -307,6 +316,7 @@ xlate_ofproto_set(struct ofproto_dpif *ofproto, const char *name,
     xbridge->frag = frag;
     xbridge->miss_rule = miss_rule;
     xbridge->no_packet_in_rule = no_packet_in_rule;
+    xbridge->enable_recirc = enable_recirc;
     xbridge->variable_length_userdata = variable_length_userdata;
     xbridge->max_mpls_depth = max_mpls_depth;
 }
@@ -665,7 +675,7 @@ xport_get_stp_port(const struct xport *xport)
         : NULL;
 }
 
-static enum stp_state
+static bool
 xport_stp_learn_state(const struct xport *xport)
 {
     struct stp_port *sp = xport_get_stp_port(xport);
@@ -679,6 +689,13 @@ xport_stp_forward_state(const struct xport *xport)
     return stp_forward_in_state(sp ? stp_port_get_state(sp) : STP_DISABLED);
 }
 
+static bool
+xport_stp_listen_state(const struct xport *xport)
+{
+    struct stp_port *sp = xport_get_stp_port(xport);
+    return stp_listen_in_state(sp ? stp_port_get_state(sp) : STP_DISABLED);
+}
+
 /* Returns true if STP should process 'flow'.  Sets fields in 'wc' that
  * were used to make the determination.*/
 static bool
@@ -693,7 +710,7 @@ stp_process_packet(const struct xport *xport, const struct ofpbuf *packet)
 {
     struct stp_port *sp = xport_get_stp_port(xport);
     struct ofpbuf payload = *packet;
-    struct eth_header *eth = payload.data;
+    struct eth_header *eth = ofpbuf_data(&payload);
 
     /* Sink packets on ports that have STP disabled when the bridge has
      * STP enabled. */
@@ -702,12 +719,12 @@ stp_process_packet(const struct xport *xport, const struct ofpbuf *packet)
     }
 
     /* Trim off padding on payload. */
-    if (payload.size > ntohs(eth->eth_type) + ETH_HEADER_LEN) {
-        payload.size = ntohs(eth->eth_type) + ETH_HEADER_LEN;
+    if (ofpbuf_size(&payload) > ntohs(eth->eth_type) + ETH_HEADER_LEN) {
+        ofpbuf_set_size(&payload, ntohs(eth->eth_type) + ETH_HEADER_LEN);
     }
 
     if (ofpbuf_try_pull(&payload, ETH_HEADER_LEN + LLC_HEADER_LEN)) {
-        stp_received_bpdu(sp, payload.data, payload.size);
+        stp_received_bpdu(sp, ofpbuf_data(&payload), ofpbuf_size(&payload));
     }
 }
 
@@ -887,8 +904,6 @@ lookup_input_bundle(const struct xbridge *xbridge, ofp_port_t in_port,
     /* Special-case OFPP_NONE, which a controller may use as the ingress
      * port for traffic that it is sourcing. */
     if (in_port == OFPP_NONE) {
-        ofpp_none_bundle.name = "OFPP_NONE";
-        ofpp_none_bundle.vlan_mode = PORT_VLAN_TRUNK;
         return &ofpp_none_bundle;
     }
 
@@ -1123,13 +1138,30 @@ output_normal(struct xlate_ctx *ctx, const struct xbundle *out_xbundle,
         /* Partially configured bundle with no slaves.  Drop the packet. */
         return;
     } else if (!out_xbundle->bond) {
+        ctx->xout->use_recirc = false;
         xport = CONTAINER_OF(list_front(&out_xbundle->xports), struct xport,
                              bundle_node);
     } else {
         struct ofport_dpif *ofport;
+        struct xlate_recirc *xr = &ctx->xout->recirc;
+        struct flow_wildcards *wc = &ctx->xout->wc;
+
+        if (ctx->xbridge->enable_recirc) {
+            ctx->xout->use_recirc = bond_may_recirc(
+                out_xbundle->bond, &xr->recirc_id, &xr->hash_bias);
 
-        ofport = bond_choose_output_slave(out_xbundle->bond, &ctx->xin->flow,
-                                          &ctx->xout->wc, vid);
+            if (ctx->xout->use_recirc) {
+                /* Only TCP mode uses recirculation. */
+                xr->hash_alg = OVS_RECIRC_HASH_ALG_L4;
+                bond_update_post_recirc_rules(out_xbundle->bond, false);
+
+                /* Recirculation does not require unmasking hash fields. */
+                wc = NULL;
+            }
+        }
+
+        ofport = bond_choose_output_slave(out_xbundle->bond,
+                                          &ctx->xin->flow, wc, vid);
         xport = xport_lookup(ofport);
 
         if (!xport) {
@@ -1484,7 +1516,7 @@ compose_sample_action(const struct xbridge *xbridge,
     actions_offset = nl_msg_start_nested(odp_actions, OVS_SAMPLE_ATTR_ACTIONS);
 
     odp_port = ofp_port_to_odp_port(xbridge, flow->in_port.ofp_port);
-    pid = dpif_port_get_pid(xbridge->dpif, odp_port);
+    pid = dpif_port_get_pid(xbridge->dpif, odp_port, 0);
     cookie_offset = odp_put_userspace_action(pid, cookie, cookie_size, odp_actions);
 
     nl_msg_end_nested(odp_actions, actions_offset);
@@ -1685,7 +1717,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
 
     /* If 'struct flow' gets additional metadata, we'll need to zero it out
      * before traversing a patch port. */
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 24);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 25);
 
     if (!xport) {
         xlate_report(ctx, "Nonexistent output port");
@@ -1693,9 +1725,18 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
     } else if (xport->config & OFPUTIL_PC_NO_FWD) {
         xlate_report(ctx, "OFPPC_NO_FWD set, skipping output");
         return;
-    } else if (check_stp && !xport_stp_forward_state(xport)) {
-        xlate_report(ctx, "STP not in forwarding state, skipping output");
-        return;
+    } else if (check_stp) {
+        if (eth_addr_equals(ctx->base_flow.dl_dst, eth_addr_stp)) {
+            if (!xport_stp_listen_state(xport)) {
+                xlate_report(ctx, "STP not in listening state, "
+                             "skipping bpdu output");
+                return;
+            }
+        } else if (!xport_stp_forward_state(xport)) {
+            xlate_report(ctx, "STP not in forwarding state, "
+                         "skipping output");
+            return;
+        }
     }
 
     if (mbridge_has_mirrors(ctx->xbridge->mbridge) && xport->xbundle) {
@@ -1720,17 +1761,17 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
             ctx->xout->slow |= special;
         } else if (may_receive(peer, ctx)) {
             if (xport_stp_forward_state(peer)) {
-                xlate_table_action(ctx, flow->in_port.ofp_port, 0, true);
+                xlate_table_action(ctx, flow->in_port.ofp_port, 0, true, true);
             } else {
                 /* Forwarding is disabled by STP.  Let OFPP_NORMAL and the
                  * learning action look at the packet, then drop it. */
                 struct flow old_base_flow = ctx->base_flow;
-                size_t old_size = ctx->xout->odp_actions.size;
+                size_t old_size = ofpbuf_size(&ctx->xout->odp_actions);
                 mirror_mask_t old_mirrors = ctx->xout->mirrors;
-                xlate_table_action(ctx, flow->in_port.ofp_port, 0, true);
+                xlate_table_action(ctx, flow->in_port.ofp_port, 0, true, true);
                 ctx->xout->mirrors = old_mirrors;
                 ctx->base_flow = old_base_flow;
-                ctx->xout->odp_actions.size = old_size;
+                ofpbuf_set_size(&ctx->xout->odp_actions, old_size);
             }
         }
 
@@ -1753,7 +1794,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
     flow_nw_tos = flow->nw_tos;
 
     if (dscp_from_skb_priority(xport, flow->skb_priority, &dscp)) {
-        wc->masks.nw_tos |= IP_ECN_MASK;
+        wc->masks.nw_tos |= IP_DSCP_MASK;
         flow->nw_tos &= ~IP_DSCP_MASK;
         flow->nw_tos |= dscp;
     }
@@ -1800,8 +1841,20 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
         ctx->xout->slow |= commit_odp_actions(flow, &ctx->base_flow,
                                               &ctx->xout->odp_actions,
                                               &ctx->xout->wc);
-        nl_msg_put_odp_port(&ctx->xout->odp_actions, OVS_ACTION_ATTR_OUTPUT,
-                            out_port);
+
+        if (ctx->xout->use_recirc) {
+            struct ovs_action_recirc *act_recirc;
+            struct xlate_recirc *xr = &ctx->xout->recirc;
+
+            act_recirc = nl_msg_put_unspec_uninit(&ctx->xout->odp_actions,
+                               OVS_ACTION_ATTR_RECIRC, sizeof *act_recirc);
+            act_recirc->recirc_id = xr->recirc_id;
+            act_recirc->hash_alg = xr->hash_alg;
+            act_recirc->hash_bias = xr->hash_bias;
+        } else {
+            nl_msg_put_odp_port(&ctx->xout->odp_actions, OVS_ACTION_ATTR_OUTPUT,
+                                out_port);
+        }
 
         ctx->sflow_odp_port = odp_port;
         ctx->sflow_n_outputs++;
@@ -1836,7 +1889,6 @@ xlate_recursively(struct xlate_ctx *ctx, struct rule_dpif *rule)
     ctx->rule = rule;
     actions = rule_dpif_get_actions(rule);
     do_xlate_actions(actions->ofpacts, actions->ofpacts_len, ctx);
-    rule_actions_unref(actions);
     ctx->rule = old_rule;
     ctx->recurse--;
 }
@@ -1846,14 +1898,14 @@ xlate_resubmit_resource_check(struct xlate_ctx *ctx)
 {
     static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
 
-    if (ctx->recurse >= MAX_RESUBMIT_RECURSION) {
+    if (ctx->recurse >= MAX_RESUBMIT_RECURSION + MAX_INTERNAL_RESUBMITS) {
         VLOG_ERR_RL(&rl, "resubmit actions recursed over %d times",
                     MAX_RESUBMIT_RECURSION);
-    } else if (ctx->resubmits >= MAX_RESUBMITS) {
+    } else if (ctx->resubmits >= MAX_RESUBMITS + MAX_INTERNAL_RESUBMITS) {
         VLOG_ERR_RL(&rl, "over %d resubmit actions", MAX_RESUBMITS);
-    } else if (ctx->xout->odp_actions.size > UINT16_MAX) {
+    } else if (ofpbuf_size(&ctx->xout->odp_actions) > UINT16_MAX) {
         VLOG_ERR_RL(&rl, "resubmits yielded over 64 kB of actions");
-    } else if (ctx->stack.size >= 65536) {
+    } else if (ofpbuf_size(&ctx->stack) >= 65536) {
         VLOG_ERR_RL(&rl, "resubmits yielded over 64 kB of stack");
     } else {
         return true;
@@ -1863,14 +1915,16 @@ xlate_resubmit_resource_check(struct xlate_ctx *ctx)
 }
 
 static void
-xlate_table_action(struct xlate_ctx *ctx,
-                   ofp_port_t in_port, uint8_t table_id, bool may_packet_in)
+xlate_table_action(struct xlate_ctx *ctx, ofp_port_t in_port, uint8_t table_id,
+                   bool may_packet_in, bool honor_table_miss)
 {
     if (xlate_resubmit_resource_check(ctx)) {
         ofp_port_t old_in_port = ctx->xin->flow.in_port.ofp_port;
         bool skip_wildcards = ctx->xin->skip_wildcards;
         uint8_t old_table_id = ctx->table_id;
         struct rule_dpif *rule;
+        enum rule_dpif_lookup_verdict verdict;
+        enum ofputil_port_config config = 0;
 
         ctx->table_id = table_id;
 
@@ -1878,29 +1932,47 @@ xlate_table_action(struct xlate_ctx *ctx,
          * original input port (otherwise OFPP_NORMAL and OFPP_IN_PORT will
          * have surprising behavior). */
         ctx->xin->flow.in_port.ofp_port = in_port;
-        rule_dpif_lookup_in_table(ctx->xbridge->ofproto, &ctx->xin->flow,
-                                  !skip_wildcards ? &ctx->xout->wc : NULL,
-                                  table_id, &rule);
+        verdict = rule_dpif_lookup_from_table(ctx->xbridge->ofproto,
+                                              &ctx->xin->flow,
+                                              !skip_wildcards
+                                              ? &ctx->xout->wc : NULL,
+                                              honor_table_miss,
+                                              &ctx->table_id, &rule);
         ctx->xin->flow.in_port.ofp_port = old_in_port;
 
         if (ctx->xin->resubmit_hook) {
             ctx->xin->resubmit_hook(ctx->xin, rule, ctx->recurse);
         }
 
-        if (!rule && may_packet_in) {
-            struct xport *xport;
-
-            /* XXX
-             * check if table configuration flags
-             * OFPTC11_TABLE_MISS_CONTROLLER, default.
-             * OFPTC11_TABLE_MISS_CONTINUE,
-             * OFPTC11_TABLE_MISS_DROP
-             * When OF1.0, OFPTC11_TABLE_MISS_CONTINUE is used. What to do? */
-            xport = get_ofp_port(ctx->xbridge, ctx->xin->flow.in_port.ofp_port);
-            choose_miss_rule(xport ? xport->config : 0,
-                             ctx->xbridge->miss_rule,
-                             ctx->xbridge->no_packet_in_rule, &rule);
+        switch (verdict) {
+        case RULE_DPIF_LOOKUP_VERDICT_MATCH:
+           goto match;
+        case RULE_DPIF_LOOKUP_VERDICT_CONTROLLER:
+            if (may_packet_in) {
+                struct xport *xport;
+
+                xport = get_ofp_port(ctx->xbridge,
+                                     ctx->xin->flow.in_port.ofp_port);
+                config = xport ? xport->config : 0;
+                break;
+            }
+            /* Fall through to drop */
+        case RULE_DPIF_LOOKUP_VERDICT_DROP:
+            config = OFPUTIL_PC_NO_PACKET_IN;
+            break;
+        case RULE_DPIF_LOOKUP_VERDICT_DEFAULT:
+            if (!ofproto_dpif_wants_packet_in_on_miss(ctx->xbridge->ofproto)) {
+                config = OFPUTIL_PC_NO_PACKET_IN;
+            }
+            break;
+        default:
+            OVS_NOT_REACHED();
         }
+
+        choose_miss_rule(config, ctx->xbridge->miss_rule,
+                         ctx->xbridge->no_packet_in_rule, &rule);
+
+match:
         if (rule) {
             xlate_recursively(ctx, rule);
             rule_dpif_unref(rule);
@@ -1924,7 +1996,7 @@ xlate_group_bucket(struct xlate_ctx *ctx, const struct ofputil_bucket *bucket)
 
     ofpacts_execute_action_set(&action_list, &action_set);
     ctx->recurse++;
-    do_xlate_actions(action_list.data, action_list.size, ctx);
+    do_xlate_actions(ofpbuf_data(&action_list), ofpbuf_size(&action_list), ctx);
     ctx->recurse--;
 
     ofpbuf_uninit(&action_set);
@@ -1970,7 +2042,7 @@ xlate_select_group(struct xlate_ctx *ctx, struct group_dpif *group)
     const struct ofputil_bucket *bucket;
     uint32_t basis;
 
-    basis = hash_bytes(ctx->xin->flow.dl_dst, sizeof ctx->xin->flow.dl_dst, 0);
+    basis = hash_mac(ctx->xin->flow.dl_dst, 0, 0);
     bucket = group_best_live_bucket(ctx, group, basis);
     if (bucket) {
         memset(&wc->masks.dl_dst, 0xff, sizeof wc->masks.dl_dst);
@@ -2050,6 +2122,15 @@ xlate_ofpact_resubmit(struct xlate_ctx *ctx,
 {
     ofp_port_t in_port;
     uint8_t table_id;
+    bool may_packet_in = false;
+    bool honor_table_miss = false;
+
+    if (ctx->rule && rule_dpif_is_internal(ctx->rule)) {
+        /* Still allow missed packets to be sent to the controller
+         * if resubmitting from an internal table. */
+        may_packet_in = true;
+        honor_table_miss = true;
+    }
 
     in_port = resubmit->in_port;
     if (in_port == OFPP_IN_PORT) {
@@ -2061,7 +2142,8 @@ xlate_ofpact_resubmit(struct xlate_ctx *ctx,
         table_id = ctx->table_id;
     }
 
-    xlate_table_action(ctx, in_port, table_id, false);
+    xlate_table_action(ctx, in_port, table_id, may_packet_in,
+                       honor_table_miss);
 }
 
 static void
@@ -2104,11 +2186,12 @@ execute_controller_action(struct xlate_ctx *ctx, int len,
                                           &ctx->xout->odp_actions,
                                           &ctx->xout->wc);
 
-    odp_execute_actions(NULL, packet, &md, ctx->xout->odp_actions.data,
-                        ctx->xout->odp_actions.size, NULL);
+    odp_execute_actions(NULL, packet, false, &md,
+                        ofpbuf_data(&ctx->xout->odp_actions),
+                        ofpbuf_size(&ctx->xout->odp_actions), NULL);
 
     pin = xmalloc(sizeof *pin);
-    pin->up.packet_len = packet->size;
+    pin->up.packet_len = ofpbuf_size(packet);
     pin->up.packet = ofpbuf_steal_data(packet);
     pin->up.reason = reason;
     pin->up.table_id = ctx->table_id;
@@ -2120,8 +2203,22 @@ execute_controller_action(struct xlate_ctx *ctx, int len,
 
     pin->controller_id = controller_id;
     pin->send_len = len;
-    pin->generated_by_table_miss = (ctx->rule
-                                    && rule_dpif_is_table_miss(ctx->rule));
+    /* If a rule is a table-miss rule then this is
+     * a table-miss handled by a table-miss rule.
+     *
+     * Else, if rule is internal and has a controller action,
+     * the later being implied by the rule being processed here,
+     * then this is a table-miss handled without a table-miss rule.
+     *
+     * Otherwise this is not a table-miss. */
+    pin->miss_type = OFPROTO_PACKET_IN_NO_MISS;
+    if (ctx->rule) {
+        if (rule_dpif_is_table_miss(ctx->rule)) {
+            pin->miss_type = OFPROTO_PACKET_IN_MISS_FLOW;
+        } else if (rule_dpif_is_internal(ctx->rule)) {
+            pin->miss_type = OFPROTO_PACKET_IN_MISS_WITHOUT_FLOW;
+        }
+    }
     ofproto_dpif_send_packet_in(ctx->xbridge->ofproto, pin);
     ofpbuf_delete(packet);
 }
@@ -2269,7 +2366,7 @@ xlate_output_action(struct xlate_ctx *ctx,
         break;
     case OFPP_TABLE:
         xlate_table_action(ctx, ctx->xin->flow.in_port.ofp_port,
-                           0, may_packet_in);
+                           0, may_packet_in, true);
         break;
     case OFPP_NORMAL:
         xlate_normal(ctx);
@@ -2506,7 +2603,7 @@ xlate_action_set(struct xlate_ctx *ctx)
 
     ofpbuf_use_stub(&action_list, action_list_stub, sizeof action_list_stub);
     ofpacts_execute_action_set(&action_list, &ctx->action_set);
-    do_xlate_actions(action_list.data, action_list.size, ctx);
+    do_xlate_actions(ofpbuf_data(&action_list), ofpbuf_size(&action_list), ctx);
     ofpbuf_uninit(&action_list);
 }
 
@@ -2782,7 +2879,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
 
             ovs_assert(ctx->table_id < ogt->table_id);
             xlate_table_action(ctx, ctx->xin->flow.in_port.ofp_port,
-                               ogt->table_id, true);
+                               ogt->table_id, true, true);
             break;
         }
 
@@ -2852,8 +2949,8 @@ xlate_out_copy(struct xlate_out *dst, const struct xlate_out *src)
 
     ofpbuf_use_stub(&dst->odp_actions, dst->odp_actions_stub,
                     sizeof dst->odp_actions_stub);
-    ofpbuf_put(&dst->odp_actions, src->odp_actions.data,
-               src->odp_actions.size);
+    ofpbuf_put(&dst->odp_actions, ofpbuf_data(&src->odp_actions),
+               ofpbuf_size(&src->odp_actions));
 }
 \f
 static struct skb_priority_to_dscp *
@@ -2898,8 +2995,8 @@ actions_output_to_local_port(const struct xlate_ctx *ctx)
     const struct nlattr *a;
     unsigned int left;
 
-    NL_ATTR_FOR_EACH_UNSAFE (a, left, ctx->xout->odp_actions.data,
-                             ctx->xout->odp_actions.size) {
+    NL_ATTR_FOR_EACH_UNSAFE (a, left, ofpbuf_data(&ctx->xout->odp_actions),
+                             ofpbuf_size(&ctx->xout->odp_actions)) {
         if (nl_attr_type(a) == OVS_ACTION_ATTR_OUTPUT
             && nl_attr_get_odp_port(a) == local_odp_port) {
             return true;
@@ -3009,14 +3106,16 @@ xlate_actions__(struct xlate_in *xin, struct xlate_out *xout)
     ctx.exit = false;
 
     if (!xin->ofpacts && !ctx.rule) {
-        rule_dpif_lookup(ctx.xbridge->ofproto, flow,
-                         !xin->skip_wildcards ? wc : NULL, &rule);
+        ctx.table_id = rule_dpif_lookup(ctx.xbridge->ofproto, flow,
+                                        !xin->skip_wildcards ? wc : NULL,
+                                        &rule);
         if (ctx.xin->resubmit_stats) {
             rule_dpif_credit_stats(rule, ctx.xin->resubmit_stats);
         }
         ctx.rule = rule;
     }
     xout->fail_open = ctx.rule && rule_dpif_is_fail_open(ctx.rule);
+    xout->use_recirc = false;
 
     if (xin->ofpacts) {
         ofpacts = xin->ofpacts;
@@ -3085,7 +3184,7 @@ xlate_actions__(struct xlate_in *xin, struct xlate_out *xout)
 
         add_sflow_action(&ctx);
         add_ipfix_action(&ctx);
-        sample_actions_len = ctx.xout->odp_actions.size;
+        sample_actions_len = ofpbuf_size(&ctx.xout->odp_actions);
 
         if (tnl_may_send && (!in_port || may_receive(in_port, &ctx))) {
             do_xlate_actions(ofpacts, ofpacts_len, &ctx);
@@ -3093,11 +3192,11 @@ xlate_actions__(struct xlate_in *xin, struct xlate_out *xout)
             /* We've let OFPP_NORMAL and the learning action look at the
              * packet, so drop it now if forwarding is disabled. */
             if (in_port && !xport_stp_forward_state(in_port)) {
-                ctx.xout->odp_actions.size = sample_actions_len;
+                ofpbuf_set_size(&ctx.xout->odp_actions, sample_actions_len);
             }
         }
 
-        if (ctx.action_set.size) {
+        if (ofpbuf_size(&ctx.action_set)) {
             xlate_action_set(&ctx);
         }
 
@@ -3114,7 +3213,7 @@ xlate_actions__(struct xlate_in *xin, struct xlate_out *xout)
         }
     }
 
-    if (nl_attr_oversized(ctx.xout->odp_actions.size)) {
+    if (nl_attr_oversized(ofpbuf_size(&ctx.xout->odp_actions))) {
         /* These datapath actions are too big for a Netlink attribute, so we
          * can't hand them to the kernel directly.  dpif_execute() can execute
          * them one by one with help, so just mark the result as SLOW_ACTION to
@@ -3171,7 +3270,6 @@ xlate_actions__(struct xlate_in *xin, struct xlate_out *xout)
     }
 
 out:
-    rule_actions_unref(actions);
     rule_dpif_unref(rule);
 }