+ cbdata.msg = start_ofp_stats_reply(osr, 1024);
+ if (is_valid_table(fsr->table_id)) {
+ struct cls_rule target;
+
+ cbdata.ofconn = ofconn;
+ cbdata.out_port = fsr->out_port;
+ cls_rule_from_match(&fsr->match, 0, NXFF_OPENFLOW10, 0, &target);
+ classifier_for_each_match(&ofconn->ofproto->cls, &target,
+ flow_stats_cb, &cbdata);
+ }
+
+ queue_tx(cbdata.msg, ofconn, ofconn->reply_counter);
+ return 0;
+}
+
+static void
+nx_flow_stats_cb(struct cls_rule *rule_, void *cbdata_)
+{
+ 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;
+
+ if (rule_is_hidden(rule) || !rule_has_out_port(rule, cbdata->out_port)) {
+ return;
+ }
+
+ query_stats(cbdata->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);
+ nfs->table_id = 0;
+ nfs->pad = 0;
+ calc_flow_duration(rule->created, &nfs->duration_sec, &nfs->duration_nsec);
+ nfs->cookie = rule->flow_cookie;
+ 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));
+ 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);
+ }
+ nfs->length = htons(cbdata->msg->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;
+ int error;
+
+ /* Dissect the message. */
+ nfsr = ofpbuf_try_pull(b, sizeof *nfsr);
+ if (!nfsr) {
+ return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
+ }
+ error = nx_pull_match(b, ntohs(nfsr->match_len), 0, &target);
+ if (error) {
+ return error;
+ }
+
+ COVERAGE_INC(ofproto_flows_req);
+ cbdata.msg = start_nxstats_reply(&nfsr->nsm, 1024);
+ if (is_valid_table(nfsr->table_id)) {
+ cbdata.ofconn = ofconn;
+ cbdata.out_port = nfsr->out_port;
+ classifier_for_each_match(&ofconn->ofproto->cls, &target,
+ nx_flow_stats_cb, &cbdata);
+ }