+static int port_stats_init(struct datapath *dp, const void *body, int body_len,
+ void **state)
+{
+ struct port_stats_state *s = kmalloc(sizeof *s, GFP_ATOMIC);
+ if (!s)
+ return -ENOMEM;
+ s->port = 0;
+ *state = s;
+ return 0;
+}
+
+static int port_stats_dump(struct datapath *dp, void *state,
+ void *body, int *body_len)
+{
+ struct port_stats_state *s = state;
+ struct ofp_port_stats *ops;
+ int n_ports, max_ports;
+ int i;
+
+ max_ports = *body_len / sizeof *ops;
+ if (!max_ports)
+ return -ENOMEM;
+ ops = body;
+
+ n_ports = 0;
+ for (i = s->port; i < OFPP_MAX && n_ports < max_ports; i++) {
+ struct net_bridge_port *p = dp->ports[i];
+ struct net_device_stats *stats;
+ if (!p)
+ continue;
+ stats = p->dev->get_stats(p->dev);
+ ops->port_no = htons(p->port_no);
+ memset(ops->pad, 0, sizeof ops->pad);
+ ops->rx_packets = cpu_to_be64(stats->rx_packets);
+ ops->tx_packets = cpu_to_be64(stats->tx_packets);
+ ops->rx_bytes = cpu_to_be64(stats->rx_bytes);
+ ops->tx_bytes = cpu_to_be64(stats->tx_bytes);
+ ops->rx_dropped = cpu_to_be64(stats->rx_dropped);
+ ops->tx_dropped = cpu_to_be64(stats->tx_dropped);
+ ops->rx_errors = cpu_to_be64(stats->rx_errors);
+ ops->tx_errors = cpu_to_be64(stats->tx_errors);
+ ops->rx_frame_err = cpu_to_be64(stats->rx_frame_errors);
+ ops->rx_over_err = cpu_to_be64(stats->rx_over_errors);
+ ops->rx_crc_err = cpu_to_be64(stats->rx_crc_errors);
+ ops->collisions = cpu_to_be64(stats->collisions);
+ n_ports++;
+ ops++;
+ }
+ s->port = i;
+ *body_len = n_ports * sizeof *ops;
+ return n_ports >= max_ports;
+}
+
+static void port_stats_done(void *state)
+{
+ kfree(state);
+}
+
+struct stats_type {
+ /* Minimum and maximum acceptable number of bytes in body member of
+ * struct ofp_stats_request. */
+ size_t min_body, max_body;
+
+ /* Prepares to dump some kind of statistics on 'dp'. 'body' and
+ * 'body_len' are the 'body' member of the struct ofp_stats_request.
+ * Returns zero if successful, otherwise a negative error code.
+ * May initialize '*state' to state information. May be null if no
+ * initialization is required.*/
+ int (*init)(struct datapath *dp, const void *body, int body_len,
+ void **state);
+
+ /* Dumps statistics for 'dp' into the '*body_len' bytes at 'body', and
+ * modifies '*body_len' to reflect the number of bytes actually used.
+ * ('body' will be transmitted as the 'body' member of struct
+ * ofp_stats_reply.) */
+ int (*dump)(struct datapath *dp, void *state,
+ void *body, int *body_len);
+
+ /* Cleans any state created by the init or dump functions. May be null
+ * if no cleanup is required. */
+ void (*done)(void *state);
+};
+
+static const struct stats_type stats[] = {
+ [OFPST_DESC] = {
+ 0,
+ 0,
+ NULL,
+ desc_stats_dump,
+ NULL
+ },
+ [OFPST_FLOW] = {
+ sizeof(struct ofp_flow_stats_request),
+ sizeof(struct ofp_flow_stats_request),
+ flow_stats_init,
+ flow_stats_dump,
+ flow_stats_done
+ },
+ [OFPST_AGGREGATE] = {
+ sizeof(struct ofp_aggregate_stats_request),
+ sizeof(struct ofp_aggregate_stats_request),
+ aggregate_stats_init,
+ aggregate_stats_dump,
+ NULL
+ },
+ [OFPST_TABLE] = {
+ 0,
+ 0,
+ NULL,
+ table_stats_dump,
+ NULL
+ },
+ [OFPST_PORT] = {
+ 0,
+ 0,
+ port_stats_init,
+ port_stats_dump,
+ port_stats_done
+ },
+};
+
+static int
+dp_genl_openflow_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ struct datapath *dp;
+ struct sender sender;
+ const struct stats_type *s;
+ struct ofp_stats_reply *osr;
+ int dp_idx;
+ int max_openflow_len, body_len;
+ void *body;
+ int err;
+
+ /* Set up the cleanup function for this dump. Linux 2.6.20 and later
+ * support setting up cleanup functions via the .doneit member of
+ * struct genl_ops. This kluge supports earlier versions also. */
+ cb->done = dp_genl_openflow_done;
+
+ sender.pid = NETLINK_CB(cb->skb).pid;
+ sender.seq = cb->nlh->nlmsg_seq;
+ if (!cb->args[0]) {
+ struct nlattr *attrs[DP_GENL_A_MAX + 1];
+ struct ofp_stats_request *rq;
+ struct nlattr *va;
+ size_t len, body_len;
+ int type;
+
+ 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;
+ dp_idx = nla_get_u16(attrs[DP_GENL_A_DP_IDX]);
+ dp = dp_get(dp_idx);
+ if (!dp)
+ return -ENOENT;
+
+ va = attrs[DP_GENL_A_OPENFLOW];
+ len = nla_len(va);
+ if (!va || len < sizeof *rq)
+ return -EINVAL;
+
+ rq = nla_data(va);
+ sender.xid = rq->header.xid;
+ type = ntohs(rq->type);
+ if (rq->header.version != OFP_VERSION) {
+ dp_send_error_msg(dp, &sender, OFPET_BAD_REQUEST,
+ OFPBRC_BAD_VERSION, rq, len);
+ return -EINVAL;
+ }
+ if (rq->header.type != OFPT_STATS_REQUEST
+ || ntohs(rq->header.length) != len)
+ return -EINVAL;
+
+ if (type >= ARRAY_SIZE(stats) || !stats[type].dump) {
+ dp_send_error_msg(dp, &sender, OFPET_BAD_REQUEST,
+ OFPBRC_BAD_STAT, rq, len);
+ return -EINVAL;
+ }
+
+ s = &stats[type];
+ body_len = len - offsetof(struct ofp_stats_request, body);
+ if (body_len < s->min_body || body_len > s->max_body)
+ return -EINVAL;
+
+ cb->args[0] = 1;
+ cb->args[1] = dp_idx;
+ cb->args[2] = type;
+ cb->args[3] = rq->header.xid;
+ if (s->init) {
+ void *state;
+ err = s->init(dp, rq->body, body_len, &state);
+ if (err)
+ return err;
+ cb->args[4] = (long) state;
+ }
+ } else if (cb->args[0] == 1) {
+ sender.xid = cb->args[3];
+ dp_idx = cb->args[1];
+ s = &stats[cb->args[2]];
+
+ dp = dp_get(dp_idx);
+ if (!dp)
+ return -ENOENT;
+ } else {
+ return 0;
+ }
+
+ osr = put_openflow_headers(dp, skb, OFPT_STATS_REPLY, &sender,
+ &max_openflow_len);
+ if (IS_ERR(osr))
+ return PTR_ERR(osr);
+ osr->type = htons(s - stats);
+ osr->flags = 0;
+ resize_openflow_skb(skb, &osr->header, max_openflow_len);
+ body = osr->body;
+ body_len = max_openflow_len - offsetof(struct ofp_stats_reply, body);
+
+ err = s->dump(dp, (void *) cb->args[4], body, &body_len);
+ if (err >= 0) {
+ if (!err)
+ cb->args[0] = 2;
+ else
+ osr->flags = ntohs(OFPSF_REPLY_MORE);
+ resize_openflow_skb(skb, &osr->header,
+ (offsetof(struct ofp_stats_reply, body)
+ + body_len));
+ err = skb->len;
+ }
+
+ return err;
+}
+
+static int
+dp_genl_openflow_done(struct netlink_callback *cb)
+{
+ if (cb->args[0]) {
+ const struct stats_type *s = &stats[cb->args[2]];
+ if (s->done)
+ s->done((void *) cb->args[4]);
+ }
+ return 0;
+}
+
+static struct genl_ops dp_genl_ops_openflow = {
+ .cmd = DP_GENL_C_OPENFLOW,