coverage: Make the coverage counters catalog program-specific.
[sliver-openvswitch.git] / ofproto / ofproto.c
index 9d6ebfe..c3214c5 100644 (file)
 
 VLOG_DEFINE_THIS_MODULE(ofproto);
 
+COVERAGE_DEFINE(odp_overflow);
+COVERAGE_DEFINE(ofproto_add_wc_flow);
+COVERAGE_DEFINE(ofproto_agg_request);
+COVERAGE_DEFINE(ofproto_costly_flags);
+COVERAGE_DEFINE(ofproto_ctlr_action);
+COVERAGE_DEFINE(ofproto_del_wc_flow);
+COVERAGE_DEFINE(ofproto_dp_missed);
+COVERAGE_DEFINE(ofproto_error);
+COVERAGE_DEFINE(ofproto_expiration);
+COVERAGE_DEFINE(ofproto_expired);
+COVERAGE_DEFINE(ofproto_flows_req);
+COVERAGE_DEFINE(ofproto_flush);
+COVERAGE_DEFINE(ofproto_invalidated);
+COVERAGE_DEFINE(ofproto_mod_wc_flow);
+COVERAGE_DEFINE(ofproto_no_packet_in);
+COVERAGE_DEFINE(ofproto_odp_unchanged);
+COVERAGE_DEFINE(ofproto_ofconn_stuck);
+COVERAGE_DEFINE(ofproto_ofp2odp);
+COVERAGE_DEFINE(ofproto_packet_in);
+COVERAGE_DEFINE(ofproto_packet_out);
+COVERAGE_DEFINE(ofproto_queue_req);
+COVERAGE_DEFINE(ofproto_recv_openflow);
+COVERAGE_DEFINE(ofproto_reinit_ports);
+COVERAGE_DEFINE(ofproto_revalidate);
+COVERAGE_DEFINE(ofproto_revalidate_moved);
+COVERAGE_DEFINE(ofproto_revalidate_rule);
+COVERAGE_DEFINE(ofproto_subrule_create);
+COVERAGE_DEFINE(ofproto_unexpected_rule);
+COVERAGE_DEFINE(ofproto_uninstallable);
+COVERAGE_DEFINE(ofproto_update_port);
+
 #include "sflow_api.h"
 
 struct ofport {
@@ -124,8 +155,7 @@ static void rule_destroy(struct ofproto *, struct rule *);
 static void rule_free(struct rule *);
 
 static struct rule *rule_lookup(struct ofproto *, const struct flow *);
-static void rule_insert(struct ofproto *, struct rule *,
-                        struct ofpbuf *packet, uint16_t in_port);
+static void rule_insert(struct ofproto *, struct rule *);
 static void rule_remove(struct ofproto *, struct rule *);
 
 static void rule_send_removed(struct ofproto *, struct rule *, uint8_t reason);
@@ -178,10 +208,10 @@ static bool facet_revalidate(struct ofproto *, struct facet *);
 
 static void facet_install(struct ofproto *, struct facet *, bool zero_stats);
 static void facet_uninstall(struct ofproto *, struct facet *);
-static void facet_post_uninstall(struct ofproto *, struct facet *);
+static void facet_flush_stats(struct ofproto *, struct facet *);
 
-static bool facet_make_actions(struct ofproto *, struct facet *,
-                              const struct ofpbuf *packet);
+static void facet_make_actions(struct ofproto *, struct facet *,
+                               const struct ofpbuf *packet);
 static void facet_update_stats(struct ofproto *, struct facet *,
                                const struct odp_flow_stats *);
 
@@ -306,6 +336,7 @@ struct ofproto {
     long long int next_in_band_update;
     struct sockaddr_in *extra_in_band_remotes;
     size_t n_extra_remotes;
+    int in_band_queue;
 
     /* Flow table. */
     struct classifier cls;
@@ -406,11 +437,14 @@ ofproto_create(const char *datapath, const char *datapath_type,
 
     /* Initialize submodules. */
     p->switch_status = switch_status_create(p);
-    p->in_band = NULL;
     p->fail_open = NULL;
     p->netflow = NULL;
     p->sflow = NULL;
 
+    /* Initialize in-band control. */
+    p->in_band = NULL;
+    p->in_band_queue = -1;
+
     /* Initialize flow table. */
     classifier_init(&p->cls);
     p->next_expiration = time_msec() + 1000;
@@ -601,6 +635,7 @@ update_in_band_remotes(struct ofproto *ofproto)
         if (ofproto->in_band) {
             in_band_set_remotes(ofproto->in_band, addrs, n_addrs);
         }
+        in_band_set_queue(ofproto->in_band, ofproto->in_band_queue);
         ofproto->next_in_band_update = time_msec() + 1000;
     } else {
         in_band_destroy(ofproto->in_band);
@@ -777,6 +812,18 @@ ofproto_set_extra_in_band_remotes(struct ofproto *ofproto,
     update_in_band_remotes(ofproto);
 }
 
+/* Sets the OpenFlow queue used by flows set up by in-band control on
+ * 'ofproto' to 'queue_id'.  If 'queue_id' is negative, then in-band control
+ * flows will use the default queue. */
+void
+ofproto_set_in_band_queue(struct ofproto *ofproto, int queue_id)
+{
+    if (queue_id != ofproto->in_band_queue) {
+        ofproto->in_band_queue = queue_id;
+        update_in_band_remotes(ofproto);
+    }
+}
+
 void
 ofproto_set_desc(struct ofproto *p,
                  const char *mfr_desc, const char *hw_desc,
@@ -1338,7 +1385,7 @@ ofproto_add_flow(struct ofproto *p, const struct cls_rule *cls_rule,
 {
     struct rule *rule;
     rule = rule_create(cls_rule, actions, n_actions, 0, 0, 0, false);
-    rule_insert(p, rule, NULL, 0);
+    rule_insert(p, rule);
 }
 
 void
@@ -1353,19 +1400,12 @@ ofproto_delete_flow(struct ofproto *ofproto, const struct cls_rule *target)
     }
 }
 
-static void
-destroy_rule(struct cls_rule *rule_, void *ofproto_)
-{
-    struct rule *rule = rule_from_cls_rule(rule_);
-    struct ofproto *ofproto = ofproto_;
-
-    rule_remove(ofproto, rule);
-}
-
 void
 ofproto_flush_flows(struct ofproto *ofproto)
 {
     struct facet *facet, *next_facet;
+    struct rule *rule, *next_rule;
+    struct cls_cursor cursor;
 
     COVERAGE_INC(ofproto_flush);
 
@@ -1377,7 +1417,12 @@ ofproto_flush_flows(struct ofproto *ofproto)
         facet->installed = false;
         facet_remove(ofproto, facet);
     }
-    classifier_for_each(&ofproto->cls, CLS_INC_ALL, destroy_rule, ofproto);
+
+    cls_cursor_init(&cursor, &ofproto->cls, NULL);
+    CLS_CURSOR_FOR_EACH_SAFE (rule, next_rule, cr, &cursor) {
+        rule_remove(ofproto, rule);
+    }
+
     dpif_flow_flush(ofproto->dpif);
     if (ofproto->in_band) {
         in_band_flushed(ofproto->in_band);
@@ -1501,7 +1546,10 @@ send_port_status(struct ofproto *p, const struct ofport *ofport,
         struct ofp_port_status *ops;
         struct ofpbuf *b;
 
-        if (!ofconn_receives_async_msgs(ofconn)) {
+        /* Primary controllers, even slaves, should always get port status
+           updates.  Otherwise obey ofconn_receives_async_msgs(). */
+        if (ofconn->type != OFCONN_PRIMARY
+            && !ofconn_receives_async_msgs(ofconn)) {
             continue;
         }
 
@@ -1604,9 +1652,9 @@ update_port(struct ofproto *p, const char *devname)
         return;
     } else if (old_ofport && new_ofport) {
         /* Most of the 'config' bits are OpenFlow soft state, but
-         * OFPPC_PORT_DOWN is maintained the kernel.  So transfer the OpenFlow
-         * bits from old_ofport.  (make_ofport() only sets OFPPC_PORT_DOWN and
-         * leaves the other bits 0.)  */
+         * OFPPC_PORT_DOWN is maintained by the kernel.  So transfer the
+         * OpenFlow bits from old_ofport.  (make_ofport() only sets
+         * OFPPC_PORT_DOWN and leaves the other bits 0.)  */
         new_ofport->opp.config |= old_ofport->opp.config & ~OFPPC_PORT_DOWN;
 
         if (ofport_equal(old_ofport, new_ofport)) {
@@ -2067,15 +2115,9 @@ rule_execute(struct ofproto *ofproto, struct rule *rule, uint16_t in_port,
     }
 }
 
-/* Inserts 'rule' into 'p''s flow table.
- *
- * If 'packet' is nonnull, takes ownership of 'packet', executes 'rule''s
- * actions on it and credits the statistics for sending the packet to 'rule'.
- * 'packet' must have at least sizeof(struct ofp_packet_in) bytes of
- * headroom. */
+/* Inserts 'rule' into 'p''s flow table. */
 static void
-rule_insert(struct ofproto *p, struct rule *rule, struct ofpbuf *packet,
-            uint16_t in_port)
+rule_insert(struct ofproto *p, struct rule *rule)
 {
     struct rule *displaced_rule;
 
@@ -2084,10 +2126,6 @@ rule_insert(struct ofproto *p, struct rule *rule, struct ofpbuf *packet,
         rule_destroy(p, displaced_rule);
     }
     p->need_revalidate = true;
-
-    if (packet) {
-        rule_execute(p, rule, in_port, packet);
-    }
 }
 
 /* Creates and returns a new facet within 'ofproto' owned by 'rule', given a
@@ -2150,14 +2188,14 @@ static void
 facet_remove(struct ofproto *ofproto, struct facet *facet)
 {
     facet_uninstall(ofproto, facet);
+    facet_flush_stats(ofproto, facet);
     hmap_remove(&ofproto->facets, &facet->hmap_node);
     list_remove(&facet->list_node);
     facet_free(facet);
 }
 
-/* Composes the ODP actions for 'facet' based on its rule's actions.
- * Returns true if the actions changed, false otherwise. */
-static bool
+/* Composes the ODP actions for 'facet' based on its rule's actions. */
+static void
 facet_make_actions(struct ofproto *p, struct facet *facet,
                    const struct ofpbuf *packet)
 {
@@ -2170,15 +2208,12 @@ facet_make_actions(struct ofproto *p, struct facet *facet,
                   &facet->nf_flow.output_iface);
 
     actions_len = a.n_actions * sizeof *a.actions;
-    if (facet->n_actions == a.n_actions
-        && !memcmp(facet->actions, a.actions, actions_len)) {
-        return false;
+    if (facet->n_actions != a.n_actions
+        || memcmp(facet->actions, a.actions, actions_len)) {
+        free(facet->actions);
+        facet->n_actions = a.n_actions;
+        facet->actions = xmemdup(a.actions, actions_len);
     }
-
-    free(facet->actions);
-    facet->n_actions = a.n_actions;
-    facet->actions = xmemdup(a.actions, actions_len);
-    return true;
 }
 
 static int
@@ -2214,41 +2249,6 @@ facet_install(struct ofproto *p, struct facet *facet, bool zero_stats)
     }
 }
 
-/* Recomposes the ODP actions for 'facet' and installs or uninstalls or
- * reinstalls them as necessary. */
-static void
-facet_update_actions(struct ofproto *ofproto, struct facet *facet)
-{
-    bool actions_changed;
-    uint16_t new_out_iface, old_out_iface;
-
-    old_out_iface = facet->nf_flow.output_iface;
-    actions_changed = facet_make_actions(ofproto, facet, NULL);
-
-    if (facet->may_install) {
-        if (facet->installed) {
-            if (actions_changed) {
-                struct odp_flow_put put;
-                facet_put__(ofproto, facet, ODPPF_CREATE | ODPPF_MODIFY
-                                           | ODPPF_ZERO_STATS, &put);
-                facet_update_stats(ofproto, facet, &put.flow.stats);
-
-                /* Temporarily set the old output iface so that NetFlow
-                 * messages have the correct output interface for the old
-                 * stats. */
-                new_out_iface = facet->nf_flow.output_iface;
-                facet->nf_flow.output_iface = old_out_iface;
-                facet_post_uninstall(ofproto, facet);
-                facet->nf_flow.output_iface = new_out_iface;
-            }
-        } else {
-            facet_install(ofproto, facet, true);
-        }
-    } else {
-        facet_uninstall(ofproto, facet);
-    }
-}
-
 /* Ensures that the bytes in 'facet', plus 'extra_bytes', have been passed up
  * to the accounting hook function in the ofhooks structure. */
 static void
@@ -2267,10 +2267,7 @@ facet_account(struct ofproto *ofproto,
     }
 }
 
-/* If 'rule' is installed in the datapath, uninstalls it and updates its
- * rule's statistics.
- *
- * If 'rule' is not installed, this function has no effect. */
+/* If 'rule' is installed in the datapath, uninstalls it. */
 static void
 facet_uninstall(struct ofproto *p, struct facet *facet)
 {
@@ -2285,8 +2282,6 @@ facet_uninstall(struct ofproto *p, struct facet *facet)
             facet_update_stats(p, facet, &odp_flow.stats);
         }
         facet->installed = false;
-
-        facet_post_uninstall(p, facet);
     }
 }
 
@@ -2305,7 +2300,7 @@ facet_is_controller_flow(struct facet *facet)
 /* Folds all of 'facet''s statistics into its rule.  Also updates the
  * accounting ofhook and emits a NetFlow expiration if appropriate.  */
 static void
-facet_post_uninstall(struct ofproto *ofproto, struct facet *facet)
+facet_flush_stats(struct ofproto *ofproto, struct facet *facet)
 {
     facet_account(ofproto, facet, 0);
 
@@ -2381,29 +2376,77 @@ facet_lookup_valid(struct ofproto *ofproto, const struct flow *flow)
  *
  *   - If there is none, destroys 'facet'.
  *
- * Returns true if 'facet' still exists, false if it has been destroyed.
- */
+ * Returns true if 'facet' still exists, false if it has been destroyed. */
 static bool
 facet_revalidate(struct ofproto *ofproto, struct facet *facet)
 {
-    struct rule *rule;
+    struct rule *new_rule;
+    struct odp_actions a;
+    size_t actions_len;
+    uint16_t new_nf_output_iface;
+    bool actions_changed;
 
     COVERAGE_INC(facet_revalidate);
-    rule = rule_lookup(ofproto, &facet->flow);
-    if (!rule) {
+
+    /* Determine the new rule. */
+    new_rule = rule_lookup(ofproto, &facet->flow);
+    if (!new_rule) {
+        /* No new rule, so delete the facet. */
         facet_remove(ofproto, facet);
         return false;
     }
 
-    if (rule != facet->rule) {
+    /* Calculate new ODP actions.
+     *
+     * We are very cautious about actually modifying 'facet' state at this
+     * point, because we might need to, e.g., emit a NetFlow expiration and, if
+     * so, we need to have the old state around to properly compose it. */
+    xlate_actions(new_rule->actions, new_rule->n_actions, &facet->flow,
+                  ofproto, NULL, &a, &facet->tags, &facet->may_install,
+                  &new_nf_output_iface);
+    actions_len = a.n_actions * sizeof *a.actions;
+    actions_changed = (facet->n_actions != a.n_actions
+                       || memcmp(facet->actions, a.actions, actions_len));
+
+    /* If the ODP actions changed or the installability changed, then we need
+     * to talk to the datapath. */
+    if (actions_changed || facet->may_install != facet->installed) {
+        if (facet->may_install) {
+            struct odp_flow_put put;
+
+            memset(&put.flow.stats, 0, sizeof put.flow.stats);
+            odp_flow_key_from_flow(&put.flow.key, &facet->flow);
+            put.flow.actions = a.actions;
+            put.flow.n_actions = a.n_actions;
+            put.flow.flags = 0;
+            put.flags = ODPPF_CREATE | ODPPF_MODIFY | ODPPF_ZERO_STATS;
+            dpif_flow_put(ofproto->dpif, &put);
+
+            facet_update_stats(ofproto, facet, &put.flow.stats);
+        } else {
+            facet_uninstall(ofproto, facet);
+        }
+
+        /* The datapath flow is gone or has zeroed stats, so push stats out of
+         * 'facet' into 'rule'. */
+        facet_flush_stats(ofproto, facet);
+    }
+
+    /* Update 'facet' now that we've taken care of all the old state. */
+    facet->nf_flow.output_iface = new_nf_output_iface;
+    if (actions_changed) {
+        free(facet->actions);
+        facet->n_actions = a.n_actions;
+        facet->actions = xmemdup(a.actions, actions_len);
+    }
+    if (facet->rule != new_rule) {
         COVERAGE_INC(facet_changed_rule);
         list_remove(&facet->list_node);
-        list_push_back(&rule->facets, &facet->list_node);
-        facet->rule = rule;
-        facet->used = rule->created;
+        list_push_back(&new_rule->facets, &facet->list_node);
+        facet->rule = new_rule;
+        facet->used = new_rule->created;
     }
 
-    facet_update_actions(ofproto, facet);
     return true;
 }
 \f
@@ -2591,8 +2634,7 @@ add_output_action(struct action_xlate_ctx *ctx, uint16_t port)
 static struct rule *
 rule_lookup(struct ofproto *ofproto, const struct flow *flow)
 {
-    return rule_from_cls_rule(classifier_lookup(&ofproto->cls, flow,
-                                                CLS_INC_ALL));
+    return rule_from_cls_rule(classifier_lookup(&ofproto->cls, flow));
 }
 
 static void
@@ -2777,16 +2819,12 @@ xlate_set_queue_action(struct action_xlate_ctx *ctx,
 static void
 xlate_set_dl_tci(struct action_xlate_ctx *ctx)
 {
-    ovs_be16 dl_vlan = ctx->flow.dl_vlan;
-    uint8_t dl_vlan_pcp = ctx->flow.dl_vlan_pcp;
-
-    if (dl_vlan == htons(OFP_VLAN_NONE)) {
+    ovs_be16 tci = ctx->flow.vlan_tci;
+    if (!(tci & htons(VLAN_CFI))) {
         odp_actions_add(ctx->out, ODPAT_STRIP_VLAN);
     } else {
         union odp_action *oa = odp_actions_add(ctx->out, ODPAT_SET_DL_TCI);
-        oa->dl_tci.tci = htons(ntohs(dl_vlan & htons(VLAN_VID_MASK))
-                               | (dl_vlan_pcp << VLAN_PCP_SHIFT)
-                               | VLAN_CFI);
+        oa->dl_tci.tci = tci & ~htons(VLAN_CFI);
     }
 }
 
@@ -2794,12 +2832,11 @@ static void
 xlate_reg_move_action(struct action_xlate_ctx *ctx,
                       const struct nx_action_reg_move *narm)
 {
-    ovs_be16 old_vlan = ctx->flow.dl_vlan;
-    uint8_t old_pcp = ctx->flow.dl_vlan_pcp;
+    ovs_be16 old_tci = ctx->flow.vlan_tci;
 
     nxm_execute_reg_move(narm, &ctx->flow);
 
-    if (ctx->flow.dl_vlan != old_vlan || ctx->flow.dl_vlan_pcp != old_pcp) {
+    if (ctx->flow.vlan_tci != old_tci) {
         xlate_set_dl_tci(ctx);
     }
 }
@@ -2849,6 +2886,9 @@ xlate_nicira_action(struct action_xlate_ctx *ctx,
     case NXAST_REG_LOAD:
         nxm_execute_reg_load((const struct nx_action_reg_load *) nah,
                              &ctx->flow);
+
+    case NXAST_NOTE:
+        /* Nothing to do. */
         break;
 
     /* If you add a new action here that modifies flow data, don't forget to
@@ -2886,18 +2926,20 @@ do_xlate_actions(const union ofp_action *in, size_t n_in,
             break;
 
         case OFPAT_SET_VLAN_VID:
-            ctx->flow.dl_vlan = ia->vlan_vid.vlan_vid;
+            ctx->flow.vlan_tci &= ~htons(VLAN_VID_MASK);
+            ctx->flow.vlan_tci |= ia->vlan_vid.vlan_vid | htons(VLAN_CFI);
             xlate_set_dl_tci(ctx);
             break;
 
         case OFPAT_SET_VLAN_PCP:
-            ctx->flow.dl_vlan_pcp = ia->vlan_pcp.vlan_pcp;
+            ctx->flow.vlan_tci &= ~htons(VLAN_PCP_MASK);
+            ctx->flow.vlan_tci |= htons(
+                (ia->vlan_pcp.vlan_pcp << VLAN_PCP_SHIFT) | VLAN_CFI);
             xlate_set_dl_tci(ctx);
             break;
 
         case OFPAT_STRIP_VLAN:
-            ctx->flow.dl_vlan = htons(OFP_VLAN_NONE);
-            ctx->flow.dl_vlan_pcp = 0;
+            ctx->flow.vlan_tci = htons(0);
             xlate_set_dl_tci(ctx);
             break;
 
@@ -3324,12 +3366,6 @@ handle_port_stats_request(struct ofconn *ofconn, struct ofp_stats_request *osr,
     return 0;
 }
 
-struct flow_stats_cbdata {
-    struct ofconn *ofconn;
-    ovs_be16 out_port;
-    struct ofpbuf *msg;
-};
-
 /* Obtains statistic counters for 'rule' within 'p' and stores them into
  * '*packet_countp' and '*byte_countp'.  The returned statistics include
  * statistics for all of 'rule''s facets. */
@@ -3388,29 +3424,27 @@ calc_flow_duration(long long int start, ovs_be32 *sec, ovs_be32 *nsec)
 }
 
 static void
-flow_stats_cb(struct cls_rule *rule_, void *cbdata_)
+put_ofp_flow_stats(struct ofconn *ofconn, struct rule *rule,
+                   ovs_be16 out_port, struct ofpbuf **replyp)
 {
-    struct rule *rule = rule_from_cls_rule(rule_);
-    struct flow_stats_cbdata *cbdata = cbdata_;
     struct ofp_flow_stats *ofs;
     uint64_t packet_count, byte_count;
     size_t act_len, len;
 
-    if (rule_is_hidden(rule) || !rule_has_out_port(rule, cbdata->out_port)) {
+    if (rule_is_hidden(rule) || !rule_has_out_port(rule, out_port)) {
         return;
     }
 
     act_len = sizeof *rule->actions * rule->n_actions;
     len = offsetof(struct ofp_flow_stats, actions) + act_len;
 
-    query_stats(cbdata->ofconn->ofproto, rule, &packet_count, &byte_count);
+    query_stats(ofconn->ofproto, rule, &packet_count, &byte_count);
 
-    ofs = append_ofp_stats_reply(len, cbdata->ofconn, &cbdata->msg);
+    ofs = append_ofp_stats_reply(len, ofconn, replyp);
     ofs->length = htons(len);
     ofs->table_id = 0;
     ofs->pad = 0;
-    flow_to_match(&rule->cr.flow, rule->cr.wc.wildcards,
-                  cbdata->ofconn->flow_format, &ofs->match);
+    ofputil_cls_rule_to_match(&rule->cr, ofconn->flow_format, &ofs->match);
     calc_flow_duration(rule->created, &ofs->duration_sec, &ofs->duration_nsec);
     ofs->cookie = rule->flow_cookie;
     ofs->priority = htons(rule->cr.priority);
@@ -3424,10 +3458,10 @@ flow_stats_cb(struct cls_rule *rule_, void *cbdata_)
     }
 }
 
-static int
-table_id_to_include(uint8_t table_id)
+static bool
+is_valid_table(uint8_t table_id)
 {
-    return table_id == 0 || table_id == 0xff ? CLS_INC_ALL : 0;
+    return table_id == 0 || table_id == 0xff;
 }
 
 static int
@@ -3435,8 +3469,7 @@ handle_flow_stats_request(struct ofconn *ofconn,
                           const struct ofp_stats_request *osr, size_t arg_size)
 {
     struct ofp_flow_stats_request *fsr;
-    struct flow_stats_cbdata cbdata;
-    struct cls_rule target;
+    struct ofpbuf *reply;
 
     if (arg_size != sizeof *fsr) {
         return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
@@ -3444,38 +3477,46 @@ handle_flow_stats_request(struct ofconn *ofconn,
     fsr = (struct ofp_flow_stats_request *) osr->body;
 
     COVERAGE_INC(ofproto_flows_req);
-    cbdata.ofconn = ofconn;
-    cbdata.out_port = fsr->out_port;
-    cbdata.msg = start_ofp_stats_reply(osr, 1024);
-    cls_rule_from_match(&fsr->match, 0, NXFF_OPENFLOW10, 0, &target);
-    classifier_for_each_match(&ofconn->ofproto->cls, &target,
-                              table_id_to_include(fsr->table_id),
-                              flow_stats_cb, &cbdata);
-    queue_tx(cbdata.msg, ofconn, ofconn->reply_counter);
+    reply = start_ofp_stats_reply(osr, 1024);
+    if (is_valid_table(fsr->table_id)) {
+        struct cls_cursor cursor;
+        struct cls_rule target;
+        struct rule *rule;
+
+        ofputil_cls_rule_from_match(&fsr->match, 0, NXFF_OPENFLOW10, 0,
+                                    &target);
+        cls_cursor_init(&cursor, &ofconn->ofproto->cls, &target);
+        CLS_CURSOR_FOR_EACH (rule, cr, &cursor) {
+            put_ofp_flow_stats(ofconn, rule, fsr->out_port, &reply);
+        }
+    }
+    queue_tx(reply, ofconn, ofconn->reply_counter);
+
     return 0;
 }
 
 static void
-nx_flow_stats_cb(struct cls_rule *rule_, void *cbdata_)
+put_nx_flow_stats(struct ofconn *ofconn, struct rule *rule,
+                  ovs_be16 out_port, struct ofpbuf **replyp)
 {
-    struct rule *rule = rule_from_cls_rule(rule_);
-    struct flow_stats_cbdata *cbdata = cbdata_;
     struct nx_flow_stats *nfs;
     uint64_t packet_count, byte_count;
     size_t act_len, start_len;
+    struct ofpbuf *reply;
 
-    if (rule_is_hidden(rule) || !rule_has_out_port(rule, cbdata->out_port)) {
+    if (rule_is_hidden(rule) || !rule_has_out_port(rule, out_port)) {
         return;
     }
 
-    query_stats(cbdata->ofconn->ofproto, rule, &packet_count, &byte_count);
+    query_stats(ofconn->ofproto, rule, &packet_count, &byte_count);
 
     act_len = sizeof *rule->actions * rule->n_actions;
 
-    start_len = cbdata->msg->size;
-    append_nxstats_reply(sizeof *nfs + NXM_MAX_LEN + act_len,
-                         cbdata->ofconn, &cbdata->msg);
-    nfs = ofpbuf_put_uninit(cbdata->msg, sizeof *nfs);
+    start_len = (*replyp)->size;
+    append_nxstats_reply(sizeof *nfs + NXM_MAX_LEN + act_len, ofconn, replyp);
+    reply = *replyp;
+
+    nfs = ofpbuf_put_uninit(reply, sizeof *nfs);
     nfs->table_id = 0;
     nfs->pad = 0;
     calc_flow_duration(rule->created, &nfs->duration_sec, &nfs->duration_nsec);
@@ -3483,22 +3524,22 @@ nx_flow_stats_cb(struct cls_rule *rule_, void *cbdata_)
     nfs->priority = htons(rule->cr.priority);
     nfs->idle_timeout = htons(rule->idle_timeout);
     nfs->hard_timeout = htons(rule->hard_timeout);
-    nfs->match_len = htons(nx_put_match(cbdata->msg, &rule->cr));
+    nfs->match_len = htons(nx_put_match(reply, &rule->cr));
     memset(nfs->pad2, 0, sizeof nfs->pad2);
     nfs->packet_count = htonll(packet_count);
     nfs->byte_count = htonll(byte_count);
     if (rule->n_actions > 0) {
-        ofpbuf_put(cbdata->msg, rule->actions, act_len);
+        ofpbuf_put(reply, rule->actions, act_len);
     }
-    nfs->length = htons(cbdata->msg->size - start_len);
+    nfs->length = htons(reply->size - start_len);
 }
 
 static int
 handle_nxst_flow(struct ofconn *ofconn, struct ofpbuf *b)
 {
     struct nx_flow_stats_request *nfsr;
-    struct flow_stats_cbdata cbdata;
     struct cls_rule target;
+    struct ofpbuf *reply;
     int error;
 
     /* Dissect the message. */
@@ -3512,34 +3553,30 @@ handle_nxst_flow(struct ofconn *ofconn, struct ofpbuf *b)
     }
 
     COVERAGE_INC(ofproto_flows_req);
-    cbdata.ofconn = ofconn;
-    cbdata.out_port = nfsr->out_port;
-    cbdata.msg = start_nxstats_reply(&nfsr->nsm, 1024);
-    classifier_for_each_match(&ofconn->ofproto->cls, &target,
-                              table_id_to_include(nfsr->table_id),
-                              nx_flow_stats_cb, &cbdata);
-    queue_tx(cbdata.msg, ofconn, ofconn->reply_counter);
+    reply = start_nxstats_reply(&nfsr->nsm, 1024);
+    if (is_valid_table(nfsr->table_id)) {
+        struct cls_cursor cursor;
+        struct rule *rule;
+
+        cls_cursor_init(&cursor, &ofconn->ofproto->cls, &target);
+        CLS_CURSOR_FOR_EACH (rule, cr, &cursor) {
+            put_nx_flow_stats(ofconn, rule, nfsr->out_port, &reply);
+        }
+    }
+    queue_tx(reply, ofconn, ofconn->reply_counter);
+
     return 0;
 }
 
-struct flow_stats_ds_cbdata {
-    struct ofproto *ofproto;
-    struct ds *results;
-};
-
 static void
-flow_stats_ds_cb(struct cls_rule *rule_, void *cbdata_)
+flow_stats_ds(struct ofproto *ofproto, struct rule *rule, struct ds *results)
 {
-    struct rule *rule = rule_from_cls_rule(rule_);
-    struct flow_stats_ds_cbdata *cbdata = cbdata_;
-    struct ds *results = cbdata->results;
     struct ofp_match match;
     uint64_t packet_count, byte_count;
     size_t act_len = sizeof *rule->actions * rule->n_actions;
 
-    query_stats(cbdata->ofproto, rule, &packet_count, &byte_count);
-    flow_to_match(&rule->cr.flow, rule->cr.wc.wildcards,
-                  NXFF_OPENFLOW10, &match);
+    query_stats(ofproto, rule, &packet_count, &byte_count);
+    ofputil_cls_rule_to_match(&rule->cr, NXFF_OPENFLOW10, &match);
 
     ds_put_format(results, "duration=%llds, ",
                   (time_msec() - rule->created) / 1000);
@@ -3560,45 +3597,13 @@ flow_stats_ds_cb(struct cls_rule *rule_, void *cbdata_)
 void
 ofproto_get_all_flows(struct ofproto *p, struct ds *results)
 {
-    struct ofp_match match;
-    struct cls_rule target;
-    struct flow_stats_ds_cbdata cbdata;
-
-    memset(&match, 0, sizeof match);
-    match.wildcards = htonl(OVSFW_ALL);
-
-    cbdata.ofproto = p;
-    cbdata.results = results;
-
-    cls_rule_from_match(&match, 0, NXFF_OPENFLOW10, 0, &target);
-    classifier_for_each_match(&p->cls, &target, CLS_INC_ALL,
-                              flow_stats_ds_cb, &cbdata);
-}
-
-struct aggregate_stats_cbdata {
-    struct ofproto *ofproto;
-    ovs_be16 out_port;
-    uint64_t packet_count;
-    uint64_t byte_count;
-    uint32_t n_flows;
-};
-
-static void
-aggregate_stats_cb(struct cls_rule *rule_, void *cbdata_)
-{
-    struct rule *rule = rule_from_cls_rule(rule_);
-    struct aggregate_stats_cbdata *cbdata = cbdata_;
-    uint64_t packet_count, byte_count;
+    struct cls_cursor cursor;
+    struct rule *rule;
 
-    if (rule_is_hidden(rule) || !rule_has_out_port(rule, cbdata->out_port)) {
-        return;
+    cls_cursor_init(&cursor, &p->cls, NULL);
+    CLS_CURSOR_FOR_EACH (rule, cr, &cursor) {
+        flow_stats_ds(p, rule, results);
     }
-
-    query_stats(cbdata->ofproto, rule, &packet_count, &byte_count);
-
-    cbdata->packet_count += packet_count;
-    cbdata->byte_count += byte_count;
-    cbdata->n_flows++;
 }
 
 static void
@@ -3606,21 +3611,34 @@ query_aggregate_stats(struct ofproto *ofproto, struct cls_rule *target,
                       ovs_be16 out_port, uint8_t table_id,
                       struct ofp_aggregate_stats_reply *oasr)
 {
-    struct aggregate_stats_cbdata cbdata;
+    uint64_t total_packets = 0;
+    uint64_t total_bytes = 0;
+    int n_flows = 0;
 
     COVERAGE_INC(ofproto_agg_request);
-    cbdata.ofproto = ofproto;
-    cbdata.out_port = out_port;
-    cbdata.packet_count = 0;
-    cbdata.byte_count = 0;
-    cbdata.n_flows = 0;
-    classifier_for_each_match(&ofproto->cls, target,
-                              table_id_to_include(table_id),
-                              aggregate_stats_cb, &cbdata);
-
-    oasr->flow_count = htonl(cbdata.n_flows);
-    oasr->packet_count = htonll(cbdata.packet_count);
-    oasr->byte_count = htonll(cbdata.byte_count);
+
+    if (is_valid_table(table_id)) {
+        struct cls_cursor cursor;
+        struct rule *rule;
+
+        cls_cursor_init(&cursor, &ofproto->cls, target);
+        CLS_CURSOR_FOR_EACH (rule, cr, &cursor) {
+            if (!rule_is_hidden(rule) && rule_has_out_port(rule, out_port)) {
+                uint64_t packet_count;
+                uint64_t byte_count;
+
+                query_stats(ofproto, rule, &packet_count, &byte_count);
+
+                total_packets += packet_count;
+                total_bytes += byte_count;
+                n_flows++;
+            }
+        }
+    }
+
+    oasr->flow_count = htonl(n_flows);
+    oasr->packet_count = htonll(total_packets);
+    oasr->byte_count = htonll(total_bytes);
     memset(oasr->pad, 0, sizeof oasr->pad);
 }
 
@@ -3639,7 +3657,8 @@ handle_aggregate_stats_request(struct ofconn *ofconn,
     }
     request = (struct ofp_aggregate_stats_request *) osr->body;
 
-    cls_rule_from_match(&request->match, 0, NXFF_OPENFLOW10, 0, &target);
+    ofputil_cls_rule_from_match(&request->match, 0, NXFF_OPENFLOW10, 0,
+                                &target);
 
     msg = start_ofp_stats_reply(osr, sizeof *reply);
     reply = append_ofp_stats_reply(sizeof *reply, ofconn, &msg);
@@ -3934,7 +3953,10 @@ add_flow(struct ofconn *ofconn, struct flow_mod *fm)
     rule = rule_create(&fm->cr, fm->actions, fm->n_actions,
                        fm->idle_timeout, fm->hard_timeout, fm->cookie,
                        fm->flags & OFPFF_SEND_FLOW_REM);
-    rule_insert(p, rule, packet, in_port);
+    rule_insert(p, rule);
+    if (packet) {
+        rule_execute(p, rule, in_port, packet);
+    }
     return error;
 }
 
@@ -3976,7 +3998,6 @@ struct modify_flows_cbdata {
 
 static int modify_flow(struct ofproto *, const struct flow_mod *,
                        struct rule *);
-static void modify_flows_cb(struct cls_rule *, void *cbdata_);
 
 /* Implements OFPFC_MODIFY.  Returns 0 on success or an OpenFlow error code as
  * encoded by ofp_mkerr() on failure.
@@ -3986,19 +4007,24 @@ static void modify_flows_cb(struct cls_rule *, void *cbdata_);
 static int
 modify_flows_loose(struct ofconn *ofconn, struct flow_mod *fm)
 {
-    struct modify_flows_cbdata cbdata;
+    struct ofproto *p = ofconn->ofproto;
+    struct rule *match = NULL;
+    struct cls_cursor cursor;
+    struct rule *rule;
 
-    cbdata.ofproto = ofconn->ofproto;
-    cbdata.fm = fm;
-    cbdata.match = NULL;
+    cls_cursor_init(&cursor, &p->cls, &fm->cr);
+    CLS_CURSOR_FOR_EACH (rule, cr, &cursor) {
+        if (!rule_is_hidden(rule)) {
+            match = rule;
+            modify_flow(p, fm, rule);
+        }
+    }
 
-    classifier_for_each_match(&ofconn->ofproto->cls, &fm->cr, CLS_INC_ALL,
-                              modify_flows_cb, &cbdata);
-    if (cbdata.match) {
-        /* This credits the packet to whichever flow happened to happened to
-         * match last.  That's weird.  Maybe we should do a lookup for the
-         * flow that actually matches the packet?  Who knows. */
-        send_buffered_packet(ofconn, cbdata.match, fm->buffer_id);
+    if (match) {
+        /* This credits the packet to whichever flow happened to match last.
+         * That's weird.  Maybe we should do a lookup for the flow that
+         * actually matches the packet?  Who knows. */
+        send_buffered_packet(ofconn, match, fm->buffer_id);
         return 0;
     } else {
         return add_flow(ofconn, fm);
@@ -4023,19 +4049,6 @@ modify_flow_strict(struct ofconn *ofconn, struct flow_mod *fm)
     }
 }
 
-/* Callback for modify_flows_loose(). */
-static void
-modify_flows_cb(struct cls_rule *rule_, void *cbdata_)
-{
-    struct rule *rule = rule_from_cls_rule(rule_);
-    struct modify_flows_cbdata *cbdata = cbdata_;
-
-    if (!rule_is_hidden(rule)) {
-        cbdata->match = rule;
-        modify_flow(cbdata->ofproto, cbdata->fm, rule);
-    }
-}
-
 /* Implements core of OFPFC_MODIFY and OFPFC_MODIFY_STRICT where 'rule' has
  * been identified as a flow in 'p''s flow table to be modified, by changing
  * the rule's actions to match those in 'ofm' (which is followed by 'n_actions'
@@ -4066,25 +4079,19 @@ modify_flow(struct ofproto *p, const struct flow_mod *fm, struct rule *rule)
 \f
 /* OFPFC_DELETE implementation. */
 
-struct delete_flows_cbdata {
-    struct ofproto *ofproto;
-    ovs_be16 out_port;
-};
-
-static void delete_flows_cb(struct cls_rule *, void *cbdata_);
 static void delete_flow(struct ofproto *, struct rule *, ovs_be16 out_port);
 
 /* Implements OFPFC_DELETE. */
 static void
 delete_flows_loose(struct ofproto *p, const struct flow_mod *fm)
 {
-    struct delete_flows_cbdata cbdata;
+    struct rule *rule, *next_rule;
+    struct cls_cursor cursor;
 
-    cbdata.ofproto = p;
-    cbdata.out_port = htons(fm->out_port);
-
-    classifier_for_each_match(&p->cls, &fm->cr, CLS_INC_ALL,
-                              delete_flows_cb, &cbdata);
+    cls_cursor_init(&cursor, &p->cls, &fm->cr);
+    CLS_CURSOR_FOR_EACH_SAFE (rule, next_rule, cr, &cursor) {
+        delete_flow(p, rule, htons(fm->out_port));
+    }
 }
 
 /* Implements OFPFC_DELETE_STRICT. */
@@ -4097,16 +4104,6 @@ delete_flow_strict(struct ofproto *p, struct flow_mod *fm)
     }
 }
 
-/* Callback for delete_flows_loose(). */
-static void
-delete_flows_cb(struct cls_rule *rule_, void *cbdata_)
-{
-    struct rule *rule = rule_from_cls_rule(rule_);
-    struct delete_flows_cbdata *cbdata = cbdata_;
-
-    delete_flow(cbdata->ofproto, rule, cbdata->out_port);
-}
-
 /* Implements core of OFPFC_DELETE and OFPFC_DELETE_STRICT where 'rule' has
  * been identified as a flow to delete from 'p''s flow table, by deleting the
  * flow and sending out a OFPT_FLOW_REMOVED message to any interested
@@ -4220,8 +4217,8 @@ handle_ofpt_flow_mod(struct ofconn *ofconn, struct ofp_header *oh)
     }
 
     /* Translate the message. */
-    cls_rule_from_match(&ofm->match, ntohs(ofm->priority), ofconn->flow_format,
-                        ofm->cookie, &fm.cr);
+    ofputil_cls_rule_from_match(&ofm->match, ntohs(ofm->priority),
+                                ofconn->flow_format, ofm->cookie, &fm.cr);
     fm.cookie = ofm->cookie;
     fm.command = ntohs(ofm->command);
     fm.idle_timeout = ntohs(ofm->idle_timeout);
@@ -4496,6 +4493,11 @@ handle_odp_miss_msg(struct ofproto *p, struct ofpbuf *packet)
     payload.size = msg->length - sizeof *msg;
     flow_extract(&payload, msg->arg, msg->port, &flow);
 
+    packet->l2 = payload.l2;
+    packet->l3 = payload.l3;
+    packet->l4 = payload.l4;
+    packet->l7 = payload.l7;
+
     /* Check with in-band control to see if this packet should be sent
      * to the local port regardless of the flow table. */
     if (in_band_msg_in_hook(p->in_band, &flow, &payload)) {
@@ -4588,14 +4590,9 @@ handle_odp_msg(struct ofproto *p, struct ofpbuf *packet)
 \f
 /* Flow expiration. */
 
-struct expire_cbdata {
-    struct ofproto *ofproto;
-    int dp_max_idle;
-};
-
 static int ofproto_dp_max_idle(const struct ofproto *);
 static void ofproto_update_used(struct ofproto *);
-static void rule_expire(struct cls_rule *, void *cbdata);
+static void rule_expire(struct ofproto *, struct rule *);
 static void ofproto_expire_facets(struct ofproto *, int dp_max_idle);
 
 /* This function is called periodically by ofproto_run().  Its job is to
@@ -4607,18 +4604,22 @@ static void ofproto_expire_facets(struct ofproto *, int dp_max_idle);
 static int
 ofproto_expire(struct ofproto *ofproto)
 {
-    struct expire_cbdata cbdata;
+    struct rule *rule, *next_rule;
+    struct cls_cursor cursor;
+    int dp_max_idle;
 
     /* Update 'used' for each flow in the datapath. */
     ofproto_update_used(ofproto);
 
     /* Expire facets that have been idle too long. */
-    cbdata.dp_max_idle = ofproto_dp_max_idle(ofproto);
-    ofproto_expire_facets(ofproto, cbdata.dp_max_idle);
+    dp_max_idle = ofproto_dp_max_idle(ofproto);
+    ofproto_expire_facets(ofproto, dp_max_idle);
 
     /* Expire OpenFlow flows whose idle_timeout or hard_timeout has passed. */
-    cbdata.ofproto = ofproto;
-    classifier_for_each(&ofproto->cls, CLS_INC_ALL, rule_expire, &cbdata);
+    cls_cursor_init(&cursor, &ofproto->cls, NULL);
+    CLS_CURSOR_FOR_EACH_SAFE (rule, next_rule, cr, &cursor) {
+        rule_expire(ofproto, rule);
+    }
 
     /* Let the hook know that we're at a stable point: all outstanding data
      * in existing flows has been accounted to the account_cb.  Thus, the
@@ -4628,7 +4629,7 @@ ofproto_expire(struct ofproto *ofproto)
         ofproto->ofhooks->account_checkpoint_cb(ofproto->aux);
     }
 
-    return MIN(cbdata.dp_max_idle, 1000);
+    return MIN(dp_max_idle, 1000);
 }
 
 /* Update 'used' member of installed facets. */
@@ -4807,15 +4808,11 @@ ofproto_expire_facets(struct ofproto *ofproto, int dp_max_idle)
     }
 }
 
-/* If 'cls_rule' is an OpenFlow rule, that has expired according to OpenFlow
- * rules, then delete it entirely.
- *
- * (This is a callback function for classifier_for_each().) */
+/* If 'rule' is an OpenFlow rule, that has expired according to OpenFlow rules,
+ * then delete it entirely. */
 static void
-rule_expire(struct cls_rule *cls_rule, void *cbdata_)
+rule_expire(struct ofproto *ofproto, struct rule *rule)
 {
-    struct expire_cbdata *cbdata = cbdata_;
-    struct rule *rule = rule_from_cls_rule(cls_rule);
     struct facet *facet, *next_facet;
     long long int now;
     uint8_t reason;
@@ -4837,14 +4834,14 @@ rule_expire(struct cls_rule *cls_rule, void *cbdata_)
     /* Update stats.  (This is a no-op if the rule expired due to an idle
      * timeout, because that only happens when the rule has no facets left.) */
     LIST_FOR_EACH_SAFE (facet, next_facet, list_node, &rule->facets) {
-        facet_remove(cbdata->ofproto, facet);
+        facet_remove(ofproto, facet);
     }
 
     /* Get rid of the rule. */
     if (!rule_is_hidden(rule)) {
-        rule_send_removed(cbdata->ofproto, rule, reason);
+        rule_send_removed(ofproto, rule, reason);
     }
-    rule_remove(cbdata->ofproto, rule);
+    rule_remove(ofproto, rule);
 }
 \f
 static struct ofpbuf *
@@ -4855,8 +4852,7 @@ compose_ofp_flow_removed(struct ofconn *ofconn, const struct rule *rule,
     struct ofpbuf *buf;
 
     ofr = make_openflow(sizeof *ofr, OFPT_FLOW_REMOVED, &buf);
-    flow_to_match(&rule->cr.flow, rule->cr.wc.wildcards, ofconn->flow_format,
-                  &ofr->match);
+    ofputil_cls_rule_to_match(&rule->cr, ofconn->flow_format, &ofr->match);
     ofr->cookie = rule->flow_cookie;
     ofr->priority = htons(rule->cr.priority);
     ofr->reason = reason;