+struct flow_stats_cb_state {
+ int dp_idx;
+ int table_idx;
+ struct sw_table_position position;
+ struct ofp_flow_stats_request *rq;
+ int sent_terminator;
+
+ struct ofp_flow_stats *flows;
+ int n_flows, max_flows;
+};
+
+static int muster_callback(struct sw_flow *flow, void *private)
+{
+ struct flow_stats_cb_state *s = private;
+
+ fill_flow_stats(&s->flows[s->n_flows], flow, s->table_idx);
+ return ++s->n_flows >= s->max_flows;
+}
+
+int
+muster_flow_stats(struct datapath *dp, struct flow_stats_cb_state *s,
+ const struct sender *sender, struct sk_buff *skb)
+{
+ struct ofp_flow_stats_reply *fsr;
+ size_t header_size, flow_size;
+ struct sw_flow_key match_key;
+ int max_openflow_len;
+ size_t size;
+
+ fsr = put_openflow_headers(dp, skb, OFPT_FLOW_STATS_REPLY, sender,
+ &max_openflow_len);
+ if (IS_ERR(fsr))
+ return PTR_ERR(fsr);
+ resize_openflow_skb(skb, &fsr->header, max_openflow_len);
+
+ header_size = offsetof(struct ofp_flow_stats_reply, flows);
+ flow_size = sizeof fsr->flows[0];
+ s->max_flows = (max_openflow_len - header_size) / flow_size;
+ if (s->max_flows <= 0)
+ return -ENOMEM;
+ s->flows = fsr->flows;
+
+ flow_extract_match(&match_key, &s->rq->match);
+ s->n_flows = 0;
+ while (s->table_idx < dp->chain->n_tables
+ && (s->rq->table_id == 0xff || s->rq->table_id == s->table_idx))
+ {
+ struct sw_table *table = dp->chain->tables[s->table_idx];
+
+ if (table->iterate(table, &match_key, &s->position,
+ muster_callback, s))
+ break;
+
+ s->table_idx++;
+ memset(&s->position, 0, sizeof s->position);
+ }
+ if (!s->n_flows) {
+ /* Signal dump completion. */
+ if (s->sent_terminator) {
+ return 0;
+ }
+ s->sent_terminator = 1;
+ }
+ size = header_size + flow_size * s->n_flows;
+ resize_openflow_skb(skb, &fsr->header, size);
+ return skb->len;
+}
+
+static int
+dp_genl_openflow_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ struct datapath *dp;
+ struct sender sender;
+ struct flow_stats_cb_state *state;
+ int err;
+
+ if (!cb->args[0]) {
+ struct nlattr *attrs[DP_GENL_A_MAX + 1];
+ struct ofp_flow_stats_request *rq;
+ struct nlattr *va;
+
+ err = nlmsg_parse(cb->nlh, GENL_HDRLEN, attrs, DP_GENL_A_MAX,
+ dp_genl_openflow_policy);
+ if (err < 0)
+ return err;
+
+ if (!attrs[DP_GENL_A_DP_IDX])
+ return -EINVAL;
+
+ va = attrs[DP_GENL_A_OPENFLOW];
+ if (!va || nla_len(va) != sizeof *state->rq)
+ return -EINVAL;
+
+ rq = nla_data(va);
+ if (rq->header.version != OFP_VERSION
+ || rq->header.type != OFPT_FLOW_STATS_REQUEST
+ || ntohs(rq->header.length) != sizeof *rq)
+ return -EINVAL;
+
+ state = kmalloc(sizeof *state, GFP_KERNEL);
+ if (!state)
+ return -ENOMEM;
+ state->dp_idx = nla_get_u32(attrs[DP_GENL_A_DP_IDX]);
+ state->table_idx = rq->table_id == 0xff ? 0 : rq->table_id;
+ memset(&state->position, 0, sizeof state->position);
+ state->rq = rq;
+ state->sent_terminator = 0;
+
+ cb->args[0] = (long) state;
+ } else {
+ state = (struct flow_stats_cb_state *) cb->args[0];
+ }
+
+ if (state->rq->type != OFPFS_INDIV) {
+ return -ENOTSUPP;
+ }
+
+ rcu_read_lock();
+ dp = dp_get(state->dp_idx);
+ if (!dp) {
+ err = -ENOENT;
+ goto out;
+ }
+
+ sender.xid = state->rq->header.xid;
+ sender.pid = NETLINK_CB(cb->skb).pid;
+ sender.seq = cb->nlh->nlmsg_seq;
+ err = muster_flow_stats(dp, state, &sender, skb);
+
+out:
+ rcu_read_unlock();
+ return err;
+}
+
+static int
+dp_genl_openflow_done(struct netlink_callback *cb)
+{
+ struct flow_stats_cb_state *state;
+ state = (struct flow_stats_cb_state *) cb->args[0];
+ kfree(state);
+ return 0;
+}
+