Avoid printf type modifiers not supported by MSVC C runtime library.
[cascardo/ovs.git] / ofproto / ofproto-dpif.c
index 4f3e473..7df0450 100644 (file)
@@ -110,6 +110,28 @@ struct rule_dpif {
 static void rule_get_stats(struct rule *, uint64_t *packets, uint64_t *bytes);
 static struct rule_dpif *rule_dpif_cast(const struct rule *);
 
+struct group_dpif {
+    struct ofgroup up;
+
+    /* These statistics:
+     *
+     *   - Do include packets and bytes from facets that have been deleted or
+     *     whose own statistics have been folded into the rule.
+     *
+     *   - Do include packets and bytes sent "by hand" that were accounted to
+     *     the rule without any facet being involved (this is a rare corner
+     *     case in rule_execute()).
+     *
+     *   - Do not include packet or bytes that can be obtained from any facet's
+     *     packet_count or byte_count member or that can be obtained from the
+     *     datapath by, e.g., dpif_flow_get() for any subfacet.
+     */
+    struct ovs_mutex stats_mutex;
+    uint64_t packet_count OVS_GUARDED;  /* Number of packets received. */
+    uint64_t byte_count OVS_GUARDED;    /* Number of bytes received. */
+    struct bucket_counter *bucket_stats OVS_GUARDED;  /* Bucket statistics. */
+};
+
 struct ofbundle {
     struct hmap_node hmap_node; /* In struct ofproto's "bundles" hmap. */
     struct ofproto_dpif *ofproto; /* Owning ofproto. */
@@ -263,7 +285,7 @@ struct facet {
     /* Accounting. */
     uint64_t accounted_bytes;    /* Bytes processed by facet_account(). */
     struct netflow_flow nf_flow; /* Per-flow NetFlow tracking data. */
-    uint8_t tcp_flags;           /* TCP flags seen for this 'rule'. */
+    uint16_t tcp_flags;          /* TCP flags seen for this 'rule'. */
 
     struct xlate_out xout;
 
@@ -310,6 +332,7 @@ struct ofport_dpif {
     struct bfd *bfd;            /* BFD, if any. */
     bool may_enable;            /* May be enabled in bonds. */
     bool is_tunnel;             /* This port is a tunnel. */
+    bool is_layer3;             /* This is a layer 3 port. */
     long long int carrier_seq;  /* Carrier status changes. */
     struct ofport_dpif *peer;   /* Peer if patch port. */
 
@@ -515,7 +538,9 @@ ofproto_dpif_cast(const struct ofproto *ofproto)
 static struct ofport_dpif *get_ofp_port(const struct ofproto_dpif *ofproto,
                                         ofp_port_t ofp_port);
 static void ofproto_trace(struct ofproto_dpif *, const struct flow *,
-                          const struct ofpbuf *packet, struct ds *);
+                          const struct ofpbuf *packet,
+                          const struct ofpact[], size_t ofpacts_len,
+                          struct ds *);
 
 /* Upcalls. */
 static void handle_upcalls(struct dpif_backer *);
@@ -545,11 +570,11 @@ ofproto_dpif_flow_mod(struct ofproto_dpif *ofproto,
  * Takes ownership of 'pin' and pin->packet. */
 void
 ofproto_dpif_send_packet_in(struct ofproto_dpif *ofproto,
-                            struct ofputil_packet_in *pin)
+                            struct ofproto_packet_in *pin)
 {
     if (!guarded_list_push_back(&ofproto->pins, &pin->list_node, 1024)) {
         COVERAGE_INC(packet_in_overflow);
-        free(CONST_CAST(void *, pin->packet));
+        free(CONST_CAST(void *, pin->up.packet));
         free(pin);
     }
 }
@@ -802,8 +827,8 @@ type_run(const char *type)
                                  ofport->up.netdev, ofport->cfm,
                                  ofport->bfd, ofport->peer, stp_port,
                                  ofport->qdscp, ofport->n_qdscp,
-                                 ofport->up.pp.config, ofport->is_tunnel,
-                                 ofport->may_enable);
+                                 ofport->up.pp.config, ofport->up.pp.state,
+                                 ofport->is_tunnel, ofport->may_enable);
             }
             ovs_rwlock_unlock(&xlate_rwlock);
 
@@ -1211,7 +1236,6 @@ construct(struct ofproto *ofproto_)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
     struct shash_node *node, *next;
-    uint32_t max_ports;
     int error;
 
     error = open_dpif_backer(ofproto->up.type, &ofproto->backer);
@@ -1219,9 +1243,6 @@ construct(struct ofproto *ofproto_)
         return error;
     }
 
-    max_ports = dpif_get_max_ports(ofproto->backer->dpif);
-    ofproto_init_max_ports(ofproto_, MIN(max_ports, ofp_to_u16(OFPP_MAX)));
-
     ofproto->netflow = NULL;
     ofproto->sflow = NULL;
     ofproto->ipfix = NULL;
@@ -1233,7 +1254,7 @@ construct(struct ofproto *ofproto_)
     ovs_mutex_init(&ofproto->stats_mutex);
     ovs_mutex_init(&ofproto->vsp_mutex);
 
-    classifier_init(&ofproto->facets);
+    classifier_init(&ofproto->facets, NULL);
     ofproto->consistency_rl = LLONG_MIN;
 
     guarded_list_init(&ofproto->pins);
@@ -1359,7 +1380,7 @@ destruct(struct ofproto *ofproto_)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
     struct rule_dpif *rule, *next_rule;
-    struct ofputil_packet_in *pin, *next_pin;
+    struct ofproto_packet_in *pin, *next_pin;
     struct facet *facet, *next_facet;
     struct cls_cursor cursor;
     struct oftable *table;
@@ -1397,7 +1418,7 @@ destruct(struct ofproto *ofproto_)
     guarded_list_pop_all(&ofproto->pins, &pins);
     LIST_FOR_EACH_SAFE (pin, next_pin, list_node, &pins) {
         list_remove(&pin->list_node);
-        free(CONST_CAST(void *, pin->packet));
+        free(CONST_CAST(void *, pin->up.packet));
         free(pin);
     }
     guarded_list_destroy(&ofproto->pins);
@@ -1428,7 +1449,7 @@ static int
 run_fast(struct ofproto *ofproto_)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
-    struct ofputil_packet_in *pin, *next_pin;
+    struct ofproto_packet_in *pin, *next_pin;
     struct list pins;
 
     /* Do not perform any periodic activity required by 'ofproto' while
@@ -1441,11 +1462,10 @@ run_fast(struct ofproto *ofproto_)
     LIST_FOR_EACH_SAFE (pin, next_pin, list_node, &pins) {
         connmgr_send_packet_in(ofproto->up.connmgr, pin);
         list_remove(&pin->list_node);
-        free(CONST_CAST(void *, pin->packet));
+        free(CONST_CAST(void *, pin->up.packet));
         free(pin);
     }
 
-    ofproto_dpif_monitor_run_fast();
     return 0;
 }
 
@@ -1487,9 +1507,6 @@ run(struct ofproto *ofproto_)
         dpif_ipfix_run(ofproto->ipfix);
     }
 
-    ofproto_dpif_monitor_run_fast();
-    ofproto_dpif_monitor_run();
-
     HMAP_FOR_EACH (ofport, up.hmap_node, &ofproto->up.ports) {
         port_run(ofport);
     }
@@ -1509,14 +1526,14 @@ run(struct ofproto *ofproto_)
     if (time_msec() >= ofproto->consistency_rl
         && !classifier_is_empty(&ofproto->facets)
         && !ofproto->backer->need_revalidate) {
-        struct cls_table *table;
+        struct cls_subtable *table;
         struct cls_rule *cr;
         struct facet *facet;
 
         ofproto->consistency_rl = time_msec() + 250;
 
-        table = CONTAINER_OF(hmap_random_node(&ofproto->facets.tables),
-                             struct cls_table, hmap_node);
+        table = CONTAINER_OF(hmap_random_node(&ofproto->facets.subtables),
+                             struct cls_subtable, hmap_node);
         cr = CONTAINER_OF(hmap_random_node(&table->rules), struct cls_rule,
                           hmap_node);
         facet = CONTAINER_OF(cr, struct facet, cr);
@@ -1546,7 +1563,6 @@ wait(struct ofproto *ofproto_)
     if (ofproto->ipfix) {
         dpif_ipfix_wait(ofproto->ipfix);
     }
-    ofproto_dpif_monitor_wait();
     HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) {
         bundle_wait(bundle);
     }
@@ -1693,6 +1709,7 @@ port_construct(struct ofport *port_)
     port->realdev_ofp_port = 0;
     port->vlandev_vid = 0;
     port->carrier_seq = netdev_get_carrier_resets(netdev);
+    port->is_layer3 = netdev_vport_is_layer3(netdev);
 
     if (netdev_vport_is_patch(netdev)) {
         /* By bailing out here, we don't submit the port to the sFlow module
@@ -1936,6 +1953,7 @@ get_cfm_status(const struct ofport *ofport_,
 
     if (ofport->cfm) {
         status->faults = cfm_get_fault(ofport->cfm);
+        status->flap_count = cfm_get_flap_count(ofport->cfm);
         status->remote_opstate = cfm_get_opup(ofport->cfm);
         status->health = cfm_get_health(ofport->cfm);
         cfm_get_remote_mpids(ofport->cfm, &status->rmps, &status->n_rmps);
@@ -2289,6 +2307,7 @@ bundle_update(struct ofbundle *bundle)
     bundle->floodable = true;
     LIST_FOR_EACH (port, bundle_node, &bundle->ports) {
         if (port->up.pp.config & OFPUTIL_PC_NO_FLOOD
+            || port->is_layer3
             || !stp_forward_in_state(port->stp_state)) {
             bundle->floodable = false;
             break;
@@ -2336,6 +2355,7 @@ bundle_add_port(struct ofbundle *bundle, ofp_port_t ofp_port,
         port->bundle = bundle;
         list_push_back(&bundle->ports, &port->bundle_node);
         if (port->up.pp.config & OFPUTIL_PC_NO_FLOOD
+            || port->is_layer3
             || !stp_forward_in_state(port->stp_state)) {
             bundle->floodable = false;
         }
@@ -3801,22 +3821,49 @@ facet_free(struct facet *facet)
     }
 }
 
-/* Executes, within 'ofproto', the 'n_actions' actions in 'actions' on
- * 'packet', which arrived on 'in_port'. */
-static int
-execute_odp_actions(struct ofproto_dpif *ofproto, const struct flow *flow,
-                    const struct nlattr *odp_actions, size_t actions_len,
-                    struct ofpbuf *packet)
+/* Executes, within 'ofproto', the actions in 'rule' or 'ofpacts' on 'packet'.
+ * 'flow' must reflect the data in 'packet'. */
+int
+ofproto_dpif_execute_actions(struct ofproto_dpif *ofproto,
+                             const struct flow *flow,
+                             struct rule_dpif *rule,
+                             const struct ofpact *ofpacts, size_t ofpacts_len,
+                             struct ofpbuf *packet)
 {
     struct odputil_keybuf keybuf;
+    struct dpif_flow_stats stats;
+    struct xlate_out xout;
+    struct xlate_in xin;
+    ofp_port_t in_port;
     struct ofpbuf key;
+    int error;
+
+    ovs_assert((rule != NULL) != (ofpacts != NULL));
+
+    dpif_flow_stats_extract(flow, packet, time_msec(), &stats);
+    if (rule) {
+        rule_dpif_credit_stats(rule, &stats);
+    }
+
+    xlate_in_init(&xin, ofproto, flow, rule, stats.tcp_flags, packet);
+    xin.ofpacts = ofpacts;
+    xin.ofpacts_len = ofpacts_len;
+    xin.resubmit_stats = &stats;
+    xlate_actions(&xin, &xout);
 
     ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
-    odp_flow_key_from_flow(&key, flow,
-                           ofp_port_to_odp_port(ofproto, flow->in_port.ofp_port));
+    in_port = flow->in_port.ofp_port;
+    if (in_port == OFPP_NONE) {
+        in_port = OFPP_LOCAL;
+    }
+    odp_flow_key_from_flow(&key, flow, ofp_port_to_odp_port(ofproto, in_port));
 
-    return dpif_execute(ofproto->backer->dpif, key.data, key.size,
-                        odp_actions, actions_len, packet);
+    error = dpif_execute(ofproto->backer->dpif, key.data, key.size,
+                         xout.odp_actions.data, xout.odp_actions.size, packet,
+                         (xout.slow & SLOW_ACTION) != 0);
+    xlate_out_uninit(&xout);
+
+    return error;
 }
 
 /* Remove 'facet' from its ofproto and free up the associated memory:
@@ -4282,9 +4329,15 @@ rule_dpif_credit_stats(struct rule_dpif *rule,
 }
 
 bool
-rule_dpif_fail_open(const struct rule_dpif *rule)
+rule_dpif_is_fail_open(const struct rule_dpif *rule)
 {
-    return rule->up.cr.priority == FAIL_OPEN_PRIORITY;
+    return is_fail_open_rule(&rule->up);
+}
+
+bool
+rule_dpif_is_table_miss(const struct rule_dpif *rule)
+{
+    return rule_is_table_miss(&rule->up);
 }
 
 ovs_be64
@@ -4606,9 +4659,7 @@ rule_dpif_lookup_in_table(struct ofproto_dpif *ofproto,
         cls_rule = classifier_lookup(cls, &ofpc_normal_flow, wc);
     } else if (frag && ofproto->up.frag_handling == OFPC_FRAG_DROP) {
         cls_rule = &ofproto->drop_frags_rule->up.cr;
-        if (wc) {
-            flow_wildcards_init_exact(wc);
-        }
+        /* Frag mask in wc already set above. */
     } else {
         cls_rule = classifier_lookup(cls, flow, wc);
     }
@@ -4735,21 +4786,8 @@ rule_dpif_execute(struct rule_dpif *rule, const struct flow *flow,
                   struct ofpbuf *packet)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
-    struct dpif_flow_stats stats;
-    struct xlate_out xout;
-    struct xlate_in xin;
-
-    dpif_flow_stats_extract(flow, packet, time_msec(), &stats);
-    rule_dpif_credit_stats(rule, &stats);
-
-    xlate_in_init(&xin, ofproto, flow, rule, stats.tcp_flags, packet);
-    xin.resubmit_stats = &stats;
-    xlate_actions(&xin, &xout);
-
-    execute_odp_actions(ofproto, flow, xout.odp_actions.data,
-                        xout.odp_actions.size, packet);
 
-    xlate_out_uninit(&xout);
+    ofproto_dpif_execute_actions(ofproto, flow, rule, NULL, 0, packet);
 }
 
 static enum ofperr
@@ -4776,6 +4814,137 @@ rule_modify_actions(struct rule *rule_, bool reset_counters)
 
     complete_operation(rule);
 }
+
+static struct group_dpif *group_dpif_cast(const struct ofgroup *group)
+{
+    return group ? CONTAINER_OF(group, struct group_dpif, up) : NULL;
+}
+
+static struct ofgroup *
+group_alloc(void)
+{
+    struct group_dpif *group = xzalloc(sizeof *group);
+    return &group->up;
+}
+
+static void
+group_dealloc(struct ofgroup *group_)
+{
+    struct group_dpif *group = group_dpif_cast(group_);
+    free(group);
+}
+
+static void
+group_construct_stats(struct group_dpif *group)
+    OVS_REQUIRES(group->stats_mutex)
+{
+    group->packet_count = 0;
+    group->byte_count = 0;
+    if (!group->bucket_stats) {
+        group->bucket_stats = xcalloc(group->up.n_buckets,
+                                      sizeof *group->bucket_stats);
+    } else {
+        memset(group->bucket_stats, 0, group->up.n_buckets *
+               sizeof *group->bucket_stats);
+    }
+}
+
+static enum ofperr
+group_construct(struct ofgroup *group_)
+{
+    struct group_dpif *group = group_dpif_cast(group_);
+    ovs_mutex_init(&group->stats_mutex);
+    ovs_mutex_lock(&group->stats_mutex);
+    group_construct_stats(group);
+    ovs_mutex_unlock(&group->stats_mutex);
+    return 0;
+}
+
+static void
+group_destruct__(struct group_dpif *group)
+    OVS_REQUIRES(group->stats_mutex)
+{
+    free(group->bucket_stats);
+    group->bucket_stats = NULL;
+}
+
+static void
+group_destruct(struct ofgroup *group_)
+{
+    struct group_dpif *group = group_dpif_cast(group_);
+    ovs_mutex_lock(&group->stats_mutex);
+    group_destruct__(group);
+    ovs_mutex_unlock(&group->stats_mutex);
+    ovs_mutex_destroy(&group->stats_mutex);
+}
+
+static enum ofperr
+group_modify(struct ofgroup *group_, struct ofgroup *victim_)
+{
+    struct group_dpif *group = group_dpif_cast(group_);
+    struct group_dpif *victim = group_dpif_cast(victim_);
+
+    ovs_mutex_lock(&group->stats_mutex);
+    if (victim->up.n_buckets < group->up.n_buckets) {
+        group_destruct__(group);
+    }
+    group_construct_stats(group);
+    ovs_mutex_unlock(&group->stats_mutex);
+
+    return 0;
+}
+
+static enum ofperr
+group_get_stats(const struct ofgroup *group_, struct ofputil_group_stats *ogs)
+{
+    struct group_dpif *group = group_dpif_cast(group_);
+
+    /* Start from historical data for 'group' itself that are no longer tracked
+     * in facets.  This counts, for example, facets that have expired. */
+    ovs_mutex_lock(&group->stats_mutex);
+    ogs->packet_count = group->packet_count;
+    ogs->byte_count = group->byte_count;
+    memcpy(ogs->bucket_stats, group->bucket_stats,
+           group->up.n_buckets * sizeof *group->bucket_stats);
+    ovs_mutex_unlock(&group->stats_mutex);
+
+    return 0;
+}
+
+bool
+group_dpif_lookup(struct ofproto_dpif *ofproto, uint32_t group_id,
+                  struct group_dpif **group)
+    OVS_TRY_RDLOCK(true, (*group)->up.rwlock)
+{
+    struct ofgroup *ofgroup;
+    bool found;
+
+    *group = NULL;
+    found = ofproto_group_lookup(&ofproto->up, group_id, &ofgroup);
+    *group = found ?  group_dpif_cast(ofgroup) : NULL;
+
+    return found;
+}
+
+void
+group_dpif_release(struct group_dpif *group)
+    OVS_RELEASES(group->up.rwlock)
+{
+    ofproto_group_release(&group->up);
+}
+
+void
+group_dpif_get_buckets(const struct group_dpif *group,
+                       const struct list **buckets)
+{
+    *buckets = &group->up.buckets;
+}
+
+enum ofp11_group_type
+group_dpif_get_type(const struct group_dpif *group)
+{
+    return group->up.type;
+}
 \f
 /* Sends 'packet' out 'ofport'.
  * May modify 'packet'.
@@ -4854,22 +5023,9 @@ packet_out(struct ofproto *ofproto_, struct ofpbuf *packet,
            const struct ofpact *ofpacts, size_t ofpacts_len)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
-    struct dpif_flow_stats stats;
-    struct xlate_out xout;
-    struct xlate_in xin;
-
-    dpif_flow_stats_extract(flow, packet, time_msec(), &stats);
-
-    xlate_in_init(&xin, ofproto, flow, NULL, stats.tcp_flags, packet);
-    xin.resubmit_stats = &stats;
-    xin.ofpacts_len = ofpacts_len;
-    xin.ofpacts = ofpacts;
-
-    xlate_actions(&xin, &xout);
-    execute_odp_actions(ofproto, flow,
-                        xout.odp_actions.data, xout.odp_actions.size, packet);
-    xlate_out_uninit(&xout);
 
+    ofproto_dpif_execute_actions(ofproto, flow, NULL, ofpacts,
+                                 ofpacts_len, packet);
     return 0;
 }
 \f
@@ -5054,7 +5210,7 @@ trace_format_rule(struct ds *result, int level, const struct rule_dpif *rule)
     actions = rule_dpif_get_actions(rule);
 
     ds_put_char_multiple(result, '\t', level);
-    ds_put_cstr(result, "OpenFlow ");
+    ds_put_cstr(result, "OpenFlow actions=");
     ofpacts_format(actions->ofpacts, actions->ofpacts_len, result);
     ds_put_char(result, '\n');
 
@@ -5085,7 +5241,7 @@ trace_format_regs(struct ds *result, int level, const char *title,
     ds_put_char_multiple(result, '\t', level);
     ds_put_format(result, "%s:", title);
     for (i = 0; i < FLOW_N_REGS; i++) {
-        ds_put_format(result, " reg%zu=0x%"PRIx32, i, trace->flow.regs[i]);
+        ds_put_format(result, " reg%"PRIuSIZE"=0x%"PRIx32, i, trace->flow.regs[i]);
     }
     ds_put_char(result, '\n');
 }
@@ -5126,37 +5282,39 @@ trace_report(struct xlate_in *xin, const char *s, int recurse)
     ds_put_char(result, '\n');
 }
 
-static void
-ofproto_unixctl_trace(struct unixctl_conn *conn, int argc, const char *argv[],
-                      void *aux OVS_UNUSED)
+/* Parses the 'argc' elements of 'argv', ignoring argv[0].  The following
+ * forms are supported:
+ *
+ *     - [dpname] odp_flow [-generate | packet]
+ *     - bridge br_flow [-generate | packet]
+ *
+ * On success, initializes '*ofprotop' and 'flow' and returns NULL.  On failure
+ * returns a nonnull error message. */
+static const char *
+parse_flow_and_packet(int argc, const char *argv[],
+                      struct ofproto_dpif **ofprotop, struct flow *flow,
+                      struct ofpbuf **packetp)
 {
     const struct dpif_backer *backer = NULL;
-    struct ofproto_dpif *ofproto;
-    struct ofpbuf odp_key, odp_mask;
+    const char *error = NULL;
+    struct simap port_names = SIMAP_INITIALIZER(&port_names);
     struct ofpbuf *packet;
-    struct ds result;
-    struct flow flow;
-    struct simap port_names;
-    char *s;
+    struct ofpbuf odp_key;
+    struct ofpbuf odp_mask;
 
-    packet = NULL;
-    backer = NULL;
-    ds_init(&result);
     ofpbuf_init(&odp_key, 0);
     ofpbuf_init(&odp_mask, 0);
-    simap_init(&port_names);
 
     /* Handle "-generate" or a hex string as the last argument. */
     if (!strcmp(argv[argc - 1], "-generate")) {
         packet = ofpbuf_new(0);
         argc--;
     } else {
-        const char *error = eth_from_hex(argv[argc - 1], &packet);
+        error = eth_from_hex(argv[argc - 1], &packet);
         if (!error) {
             argc--;
         } else if (argc == 4) {
             /* The 3-argument form must end in "-generate' or a hex string. */
-            unixctl_command_reply_error(conn, error);
             goto exit;
         }
     }
@@ -5173,12 +5331,15 @@ ofproto_unixctl_trace(struct unixctl_conn *conn, int argc, const char *argv[],
             dp_type = argv[1];
         }
         backer = shash_find_data(&all_dpif_backers, dp_type);
-    } else {
+    } else if (argc == 2) {
         struct shash_node *node;
         if (shash_count(&all_dpif_backers) == 1) {
             node = shash_first(&all_dpif_backers);
             backer = node->data;
         }
+    } else {
+        error = "Syntax error";
+        goto exit;
     }
     if (backer && backer->dpif) {
         struct dpif_port dpif_port;
@@ -5193,96 +5354,220 @@ ofproto_unixctl_trace(struct unixctl_conn *conn, int argc, const char *argv[],
      * bridge is specified. If function odp_flow_key_from_string()
      * returns 0, the flow is a odp_flow. If function
      * parse_ofp_exact_flow() returns 0, the flow is a br_flow. */
-    if (!odp_flow_from_string(argv[argc - 1], &port_names, &odp_key, &odp_mask)) {
+    if (!odp_flow_from_string(argv[argc - 1], &port_names,
+                              &odp_key, &odp_mask)) {
         if (!backer) {
-            unixctl_command_reply_error(conn, "Cannot find the datapath");
+            error = "Cannot find the datapath";
             goto exit;
         }
 
-        if (xlate_receive(backer, NULL, odp_key.data, odp_key.size, &flow,
-                          NULL, &ofproto, NULL)) {
-            unixctl_command_reply_error(conn, "Invalid datapath flow");
+        if (xlate_receive(backer, NULL, odp_key.data, odp_key.size, flow,
+                          NULL, ofprotop, NULL)) {
+            error = "Invalid datapath flow";
             goto exit;
         }
-        ds_put_format(&result, "Bridge: %s\n", ofproto->up.name);
-    } else if (!parse_ofp_exact_flow(&flow, argv[argc - 1])) {
+    } else if (!parse_ofp_exact_flow(flow, NULL, argv[argc - 1], NULL)) {
         if (argc != 3) {
-            unixctl_command_reply_error(conn, "Must specify bridge name");
+            error = "Must specify bridge name";
             goto exit;
         }
 
-        ofproto = ofproto_dpif_lookup(argv[1]);
-        if (!ofproto) {
-            unixctl_command_reply_error(conn, "Unknown bridge name");
+        *ofprotop = ofproto_dpif_lookup(argv[1]);
+        if (!*ofprotop) {
+            error = "Unknown bridge name";
             goto exit;
         }
     } else {
-        unixctl_command_reply_error(conn, "Bad flow syntax");
+        error = "Bad flow syntax";
         goto exit;
     }
 
     /* Generate a packet, if requested. */
     if (packet) {
         if (!packet->size) {
-            flow_compose(packet, &flow);
+            flow_compose(packet, flow);
         } else {
-            union flow_in_port in_port_;
-
-            in_port_ = flow.in_port;
-            ds_put_cstr(&result, "Packet: ");
-            s = ofp_packet_to_string(packet->data, packet->size);
-            ds_put_cstr(&result, s);
-            free(s);
+            union flow_in_port in_port = flow->in_port;
 
             /* Use the metadata from the flow and the packet argument
              * to reconstruct the flow. */
-            flow_extract(packet, flow.skb_priority, flow.pkt_mark, NULL,
-                         &in_port_, &flow);
+            flow_extract(packet, flow->skb_priority, flow->pkt_mark, NULL,
+                         &in_portflow);
         }
     }
 
-    ofproto_trace(ofproto, &flow, packet, &result);
-    unixctl_command_reply(conn, ds_cstr(&result));
+    error = NULL;
 
 exit:
-    ds_destroy(&result);
-    ofpbuf_delete(packet);
+    if (error) {
+        ofpbuf_delete(packet);
+        packet = NULL;
+    }
+    *packetp = packet;
     ofpbuf_uninit(&odp_key);
     ofpbuf_uninit(&odp_mask);
     simap_destroy(&port_names);
+    return error;
 }
 
+static void
+ofproto_unixctl_trace(struct unixctl_conn *conn, int argc, const char *argv[],
+                      void *aux OVS_UNUSED)
+{
+    struct ofproto_dpif *ofproto;
+    struct ofpbuf *packet;
+    const char *error;
+    struct flow flow;
+
+    error = parse_flow_and_packet(argc, argv, &ofproto, &flow, &packet);
+    if (!error) {
+        struct ds result;
+
+        ds_init(&result);
+        ofproto_trace(ofproto, &flow, packet, NULL, 0, &result);
+        unixctl_command_reply(conn, ds_cstr(&result));
+        ds_destroy(&result);
+        ofpbuf_delete(packet);
+    } else {
+        unixctl_command_reply_error(conn, error);
+    }
+}
+
+static void
+ofproto_unixctl_trace_actions(struct unixctl_conn *conn, int argc,
+                              const char *argv[], void *aux OVS_UNUSED)
+{
+    enum ofputil_protocol usable_protocols;
+    struct ofproto_dpif *ofproto;
+    bool enforce_consistency;
+    struct ofpbuf ofpacts;
+    struct ofpbuf *packet;
+    struct ds result;
+    struct flow flow;
+    uint16_t in_port;
+
+    /* Three kinds of error return values! */
+    enum ofperr retval;
+    const char *error;
+    char *rw_error;
+
+    packet = NULL;
+    ds_init(&result);
+    ofpbuf_init(&ofpacts, 0);
+
+    /* Parse actions. */
+    rw_error = parse_ofpacts(argv[--argc], &ofpacts, &usable_protocols);
+    if (rw_error) {
+        unixctl_command_reply_error(conn, rw_error);
+        free(rw_error);
+        goto exit;
+    }
+
+    /* OpenFlow 1.1 and later suggest that the switch enforces certain forms of
+     * consistency between the flow and the actions.  With -consistent, we
+     * enforce consistency even for a flow supported in OpenFlow 1.0. */
+    if (!strcmp(argv[1], "-consistent")) {
+        enforce_consistency = true;
+        argv++;
+        argc--;
+    } else {
+        enforce_consistency = false;
+    }
+
+    error = parse_flow_and_packet(argc, argv, &ofproto, &flow, &packet);
+    if (error) {
+        unixctl_command_reply_error(conn, error);
+        goto exit;
+    }
+
+    /* Do the same checks as handle_packet_out() in ofproto.c.
+     *
+     * We pass a 'table_id' of 0 to ofproto_check_ofpacts(), which isn't
+     * strictly correct because these actions aren't in any table, but it's OK
+     * because it 'table_id' is used only to check goto_table instructions, but
+     * packet-outs take a list of actions and therefore it can't include
+     * instructions.
+     *
+     * We skip the "meter" check here because meter is an instruction, not an
+     * action, and thus cannot appear in ofpacts. */
+    in_port = ofp_to_u16(flow.in_port.ofp_port);
+    if (in_port >= ofproto->up.max_ports && in_port < ofp_to_u16(OFPP_MAX)) {
+        unixctl_command_reply_error(conn, "invalid in_port");
+        goto exit;
+    }
+    if (enforce_consistency) {
+        retval = ofpacts_check_consistency(ofpacts.data, ofpacts.size, &flow,
+                                           u16_to_ofp(ofproto->up.max_ports),
+                                           0, 0, usable_protocols);
+    } else {
+        retval = ofpacts_check(ofpacts.data, ofpacts.size, &flow,
+                               u16_to_ofp(ofproto->up.max_ports), 0, 0,
+                               &usable_protocols);
+    }
+
+    if (retval) {
+        ds_clear(&result);
+        ds_put_format(&result, "Bad actions: %s", ofperr_to_string(retval));
+        unixctl_command_reply_error(conn, ds_cstr(&result));
+        goto exit;
+    }
+
+    ofproto_trace(ofproto, &flow, packet, ofpacts.data, ofpacts.size, &result);
+    unixctl_command_reply(conn, ds_cstr(&result));
+
+exit:
+    ds_destroy(&result);
+    ofpbuf_delete(packet);
+    ofpbuf_uninit(&ofpacts);
+}
+
+/* Implements a "trace" through 'ofproto''s flow table, appending a textual
+ * description of the results to 'ds'.
+ *
+ * The trace follows a packet with the specified 'flow' through the flow
+ * table.  'packet' may be nonnull to trace an actual packet, with consequent
+ * side effects (if it is nonnull then its flow must be 'flow').
+ *
+ * If 'ofpacts' is nonnull then its 'ofpacts_len' bytes specify the actions to
+ * trace, otherwise the actions are determined by a flow table lookup. */
 static void
 ofproto_trace(struct ofproto_dpif *ofproto, const struct flow *flow,
-              const struct ofpbuf *packet, struct ds *ds)
+              const struct ofpbuf *packet,
+              const struct ofpact ofpacts[], size_t ofpacts_len,
+              struct ds *ds)
 {
     struct rule_dpif *rule;
     struct flow_wildcards wc;
 
+    ds_put_format(ds, "Bridge: %s\n", ofproto->up.name);
     ds_put_cstr(ds, "Flow: ");
     flow_format(ds, flow);
     ds_put_char(ds, '\n');
 
     flow_wildcards_init_catchall(&wc);
-    rule_dpif_lookup(ofproto, flow, &wc, &rule);
+    if (ofpacts) {
+        rule = NULL;
+    } else {
+        rule_dpif_lookup(ofproto, flow, &wc, &rule);
 
-    trace_format_rule(ds, 0, rule);
-    if (rule == ofproto->miss_rule) {
-        ds_put_cstr(ds, "\nNo match, flow generates \"packet in\"s.\n");
-    } else if (rule == ofproto->no_packet_in_rule) {
-        ds_put_cstr(ds, "\nNo match, packets dropped because "
-                    "OFPPC_NO_PACKET_IN is set on in_port.\n");
-    } else if (rule == ofproto->drop_frags_rule) {
-        ds_put_cstr(ds, "\nPackets dropped because they are IP fragments "
-                    "and the fragment handling mode is \"drop\".\n");
+        trace_format_rule(ds, 0, rule);
+        if (rule == ofproto->miss_rule) {
+            ds_put_cstr(ds, "\nNo match, flow generates \"packet in\"s.\n");
+        } else if (rule == ofproto->no_packet_in_rule) {
+            ds_put_cstr(ds, "\nNo match, packets dropped because "
+                        "OFPPC_NO_PACKET_IN is set on in_port.\n");
+        } else if (rule == ofproto->drop_frags_rule) {
+            ds_put_cstr(ds, "\nPackets dropped because they are IP fragments "
+                        "and the fragment handling mode is \"drop\".\n");
+        }
     }
 
-    if (rule) {
+    if (rule || ofpacts) {
         uint64_t odp_actions_stub[1024 / 8];
         struct ofpbuf odp_actions;
         struct trace_ctx trace;
         struct match match;
-        uint8_t tcp_flags;
+        uint16_t tcp_flags;
 
         tcp_flags = packet ? packet_get_tcp_flags(packet, flow) : 0;
         trace.result = ds;
@@ -5290,6 +5575,10 @@ ofproto_trace(struct ofproto_dpif *ofproto, const struct flow *flow,
         ofpbuf_use_stub(&odp_actions,
                         odp_actions_stub, sizeof odp_actions_stub);
         xlate_in_init(&trace.xin, ofproto, flow, rule, tcp_flags, packet);
+        if (ofpacts) {
+            trace.xin.ofpacts = ofpacts;
+            trace.xin.ofpacts_len = ofpacts_len;
+        }
         trace.xin.resubmit_hook = trace_resubmit;
         trace.xin.report_hook = trace_report;
 
@@ -5309,27 +5598,19 @@ ofproto_trace(struct ofproto_dpif *ofproto, const struct flow *flow,
                            trace.xout.odp_actions.size);
 
         if (trace.xout.slow) {
+            enum slow_path_reason slow;
+
             ds_put_cstr(ds, "\nThis flow is handled by the userspace "
                         "slow path because it:");
-            switch (trace.xout.slow) {
-            case SLOW_CFM:
-                ds_put_cstr(ds, "\n\t- Consists of CFM packets.");
-                break;
-            case SLOW_LACP:
-                ds_put_cstr(ds, "\n\t- Consists of LACP packets.");
-                break;
-            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_CONTROLLER:
-                ds_put_cstr(ds, "\n\t- Sends \"packet-in\" messages "
-                            "to the OpenFlow controller.");
-                break;
-            case __SLOW_MAX:
-                NOT_REACHED();
+
+            slow = trace.xout.slow;
+            while (slow) {
+                enum slow_path_reason bit = rightmost_1bit(slow);
+
+                ds_put_format(ds, "\n\t- %s.",
+                              slow_path_reason_to_explanation(bit));
+
+                slow &= ~bit;
             }
         }
 
@@ -5453,7 +5734,7 @@ dpif_show_backer(const struct dpif_backer *backer, struct ds *ds)
 
     ds_put_format(ds, "%s: hit:%"PRIu64" missed:%"PRIu64"\n",
                   dpif_name(backer->dpif), n_hit, n_missed);
-    ds_put_format(ds, "\tflows: cur: %zu, avg: %u, max: %u,"
+    ds_put_format(ds, "\tflows: cur: %"PRIuSIZE", avg: %u, max: %u,"
                   " life span: %lldms\n", hmap_count(&backer->subfacets),
                   backer->avg_n_subfacet, backer->max_n_subfacet,
                   backer->avg_subfacet_life);
@@ -5564,7 +5845,7 @@ ofproto_unixctl_dpif_dump_megaflows(struct unixctl_conn *conn,
     CLS_CURSOR_FOR_EACH (facet, cr, &cursor) {
         cls_rule_format(&facet->cr, &ds);
         ds_put_cstr(&ds, ", ");
-        ds_put_format(&ds, "n_subfacets:%zu, ", list_size(&facet->subfacets));
+        ds_put_format(&ds, "n_subfacets:%"PRIuSIZE", ", list_size(&facet->subfacets));
         ds_put_format(&ds, "used:%.3fs, ", (now - facet->used) / 1000.0);
         ds_put_cstr(&ds, "Datapath actions: ");
         if (facet->xout.slow) {
@@ -5731,8 +6012,12 @@ ofproto_dpif_unixctl_init(void)
 
     unixctl_command_register(
         "ofproto/trace",
-        "[dp_name]|bridge odp_flow|br_flow [-generate|packet]",
+        "{[dp_name] odp_flow | bridge br_flow} [-generate|packet]",
         1, 3, ofproto_unixctl_trace, NULL);
+    unixctl_command_register(
+        "ofproto/trace-packet-out",
+        "[-consistent] {[dp_name] odp_flow | bridge br_flow} [-generate|packet] actions",
+        2, 6, ofproto_unixctl_trace_actions, NULL);
     unixctl_command_register("fdb/flush", "[bridge]", 0, 1,
                              ofproto_unixctl_fdb_flush, NULL);
     unixctl_command_register("fdb/show", "bridge", 1, 1,
@@ -6084,10 +6369,10 @@ const struct ofproto_class ofproto_dpif_class = {
     NULL,                       /* meter_set */
     NULL,                       /* meter_get */
     NULL,                       /* meter_del */
-    NULL,                       /* group_alloc */
-    NULL,                       /* group_construct */
-    NULL,                       /* group_destruct */
-    NULL,                       /* group_dealloc */
-    NULL,                       /* group_modify */
-    NULL,                       /* group_get_stats */
+    group_alloc,                /* group_alloc */
+    group_construct,            /* group_construct */
+    group_destruct,             /* group_destruct */
+    group_dealloc,              /* group_dealloc */
+    group_modify,               /* group_modify */
+    group_get_stats,            /* group_get_stats */
 };