From: Ben Pfaff Date: Fri, 2 May 2008 00:02:13 +0000 (-0700) Subject: Implement new statistics format in kernel and userspace switches. X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3b9c97ee0177af0937b7f351791565c90d221412;p=openvswitch Implement new statistics format in kernel and userspace switches. This change really cuts the muster! --- diff --git a/datapath/datapath.c b/datapath/datapath.c index 83be3112..d5cd1dcd 100644 --- a/datapath/datapath.c +++ b/datapath/datapath.c @@ -856,78 +856,6 @@ fill_flow_stats(struct ofp_flow_stats *ofs, struct sw_flow *flow, memset(ofs->pad, 0, sizeof ofs->pad); } -static int -fill_port_stats_reply(struct datapath *dp, struct ofp_port_stats_reply *psr) -{ - struct net_bridge_port *p; - int port_count = 0; - - list_for_each_entry_rcu (p, &dp->port_list, node) { - struct ofp_port_stats *ps = &psr->ports[port_count++]; - struct net_device_stats *stats = p->dev->get_stats(p->dev); - ps->port_no = htons(p->port_no); - memset(ps->pad, 0, sizeof ps->pad); - ps->rx_count = cpu_to_be64(stats->rx_packets); - ps->tx_count = cpu_to_be64(stats->tx_packets); - ps->drop_count = cpu_to_be64(stats->rx_dropped - + stats->tx_dropped); - } - - return port_count; -} - -int -dp_send_port_stats(struct datapath *dp, const struct sender *sender) -{ - struct sk_buff *skb; - struct ofp_port_stats_reply *psr; - size_t psr_len, port_max_len; - int port_count; - - /* Overallocate. */ - port_max_len = sizeof(struct ofp_port_stats) * OFPP_MAX; - psr = alloc_openflow_skb(dp, sizeof *psr + port_max_len, - OFPT_PORT_STATS_REPLY, sender, &skb); - if (!psr) - return -ENOMEM; - - /* Fill. */ - port_count = fill_port_stats_reply(dp, psr); - - /* Shrink to fit. */ - psr_len = sizeof *psr + sizeof(struct ofp_port_stats) * port_count; - resize_openflow_skb(skb, &psr->header, psr_len); - return send_openflow_skb(skb, sender); -} - -int -dp_send_table_stats(struct datapath *dp, const struct sender *sender) -{ - struct sk_buff *skb; - struct ofp_table_stats_reply *tsr; - int i, n_tables; - - n_tables = dp->chain->n_tables; - tsr = alloc_openflow_skb(dp, (offsetof(struct ofp_table_stats_reply, - tables) - + sizeof tsr->tables[0] * n_tables), - OFPT_TABLE_STATS_REPLY, sender, &skb); - if (!tsr) - return -ENOMEM; - for (i = 0; i < n_tables; i++) { - struct ofp_table_stats *ots = &tsr->tables[i]; - struct sw_table_stats stats; - dp->chain->tables[i]->stats(dp->chain->tables[i], &stats); - strncpy(ots->name, stats.name, sizeof ots->name); - ots->table_id = i; - ots->pad[0] = ots->pad[1] = 0; - ots->max_entries = htonl(stats.max_flows); - ots->active_count = htonl(stats.n_flows); - ots->matched_count = cpu_to_be64(0); /* FIXME */ - } - return send_openflow_skb(skb, sender); -} - /* Generic Netlink interface. * * See netlink(7) for an introduction to netlink. See @@ -1160,47 +1088,48 @@ static struct nla_policy dp_genl_openflow_policy[DP_GENL_A_MAX + 1] = { [DP_GENL_A_DP_IDX] = { .type = NLA_U32 }, }; -struct flow_stats_cb_state { - int dp_idx; +struct flow_stats_state { int table_idx; struct sw_table_position position; - struct ofp_flow_stats_request *rq; - int sent_terminator; + const struct ofp_flow_stats_request *rq; struct ofp_flow_stats *flows; int n_flows, max_flows; }; -static int muster_callback(struct sw_flow *flow, void *private) +static int flow_stats_init(struct datapath *dp, const void *body, int body_len, + void **state) { - struct flow_stats_cb_state *s = private; + const struct ofp_flow_stats_request *fsr = body; + struct flow_stats_state *s = kmalloc(sizeof *s, GFP_ATOMIC); + if (!s) + return -ENOMEM; + s->table_idx = fsr->table_id == 0xff ? 0 : fsr->table_id; + memset(&s->position, 0, sizeof s->position); + s->rq = fsr; + *state = s; + return 0; +} + +static int flow_stats_dump_callback(struct sw_flow *flow, void *private) +{ + struct flow_stats_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) +static int flow_stats_dump(struct datapath *dp, void *state, + void *body, int *body_len) { - struct ofp_flow_stats_reply *fsr; - size_t header_size, flow_size; + struct flow_stats_state *s = state; + struct ofp_flow_stats *ofs; 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) + s->max_flows = *body_len / sizeof *ofs; + if (!s->max_flows) return -ENOMEM; - s->flows = fsr->flows; + s->flows = body; flow_extract_match(&match_key, &s->rq->match); s->n_flows = 0; @@ -1210,84 +1139,253 @@ muster_flow_stats(struct datapath *dp, struct flow_stats_cb_state *s, struct sw_table *table = dp->chain->tables[s->table_idx]; if (table->iterate(table, &match_key, &s->position, - muster_callback, s)) + flow_stats_dump_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; + *body_len = sizeof *ofs * s->n_flows; + return s->n_flows >= s->max_flows; +} + +static void flow_stats_done(void *state) +{ + kfree(state); +} + +static int table_stats_dump(struct datapath *dp, void *state, + void *body, int *body_len) +{ + struct ofp_table_stats *ots; + int nbytes = dp->chain->n_tables * sizeof *ots; + int i; + if (nbytes > *body_len) + return -ENOBUFS; + *body_len = nbytes; + for (i = 0, ots = body; i < dp->chain->n_tables; i++, ots++) { + struct sw_table_stats stats; + dp->chain->tables[i]->stats(dp->chain->tables[i], &stats); + strncpy(ots->name, stats.name, sizeof ots->name); + ots->table_id = i; + memset(ots->pad, 0, sizeof ots->pad); + ots->max_entries = htonl(stats.max_flows); + ots->active_count = htonl(stats.n_flows); + ots->matched_count = cpu_to_be64(0); /* FIXME */ } - size = header_size + flow_size * s->n_flows; - resize_openflow_skb(skb, &fsr->header, size); - return skb->len; + return 0; +} + +struct port_stats_state { + int port; +}; + +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_count = cpu_to_be64(stats->rx_packets); + ops->tx_count = cpu_to_be64(stats->tx_packets); + ops->drop_count = cpu_to_be64(stats->rx_dropped + + stats->tx_dropped); + 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_FLOW] = { + sizeof(struct ofp_flow_stats_request), + sizeof(struct ofp_flow_stats_request), + flow_stats_init, + flow_stats_dump, + flow_stats_done + }, + [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; - struct flow_stats_cb_state *state; + const struct stats_type *s; + struct ofp_stats_reply *osr; + int dp_idx; + int max_openflow_len, body_len; + void *body; int err; + rcu_read_lock(); if (!cb->args[0]) { struct nlattr *attrs[DP_GENL_A_MAX + 1]; - struct ofp_flow_stats_request *rq; + 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; + err = -EINVAL; + if (!attrs[DP_GENL_A_DP_IDX]) - return -EINVAL; + goto out; + dp_idx = nla_get_u16(attrs[DP_GENL_A_DP_IDX]); + dp = dp_get(dp_idx); + if (!dp) { + err = -ENOENT; + goto out; + } va = attrs[DP_GENL_A_OPENFLOW]; - if (!va || nla_len(va) != sizeof *state->rq) - return -EINVAL; + len = nla_len(va); + if (!va || len < sizeof *rq) + goto out; rq = nla_data(va); + type = ntohs(rq->type); 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; - } + || rq->header.type != OFPT_STATS_REQUEST + || ntohs(rq->header.length) != len + || type >= ARRAY_SIZE(stats) + || !stats[type].dump) + goto out; + + s = &stats[type]; + body_len = len - offsetof(struct ofp_stats_request, body); + if (body_len < s->min_body || body_len > s->max_body) + goto out; + + 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) + goto out; + cb->args[4] = (long) state; + } + } else if (cb->args[0] == 1) { + dp_idx = cb->args[1]; + s = &stats[cb->args[2]]; - rcu_read_lock(); - dp = dp_get(state->dp_idx); - if (!dp) { - err = -ENOENT; + dp = dp_get(dp_idx); + if (!dp) { + err = -ENOENT; + goto out; + } + } else { + err = 0; goto out; } - sender.xid = state->rq->header.xid; + sender.xid = cb->args[3]; sender.pid = NETLINK_CB(cb->skb).pid; sender.seq = cb->nlh->nlmsg_seq; - err = muster_flow_stats(dp, state, &sender, skb); + + osr = put_openflow_headers(dp, skb, OFPT_STATS_REPLY, &sender, + &max_openflow_len); + if (IS_ERR(osr)) { + err = PTR_ERR(osr); + goto out; + } + 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; + } out: rcu_read_unlock(); @@ -1297,9 +1395,11 @@ out: 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); + if (cb->args[0]) { + const struct stats_type *s = &stats[cb->args[2]]; + if (s->done) + s->done((void *) cb->args[4]); + } return 0; } diff --git a/datapath/datapath.h b/datapath/datapath.h index 76798ef8..1a884f83 100644 --- a/datapath/datapath.h +++ b/datapath/datapath.h @@ -71,10 +71,6 @@ int dp_set_origin(struct datapath *, uint16_t, struct sk_buff *); int dp_send_features_reply(struct datapath *, const struct sender *); int dp_send_config_reply(struct datapath *, const struct sender *); int dp_send_flow_expired(struct datapath *, struct sw_flow *); -int dp_send_flow_stats(struct datapath *, const struct sender *, - const struct ofp_match *); -int dp_send_table_stats(struct datapath *, const struct sender *); -int dp_send_port_stats(struct datapath *, const struct sender *); int dp_send_error_msg(struct datapath *, const struct sender *, uint16_t, uint16_t, const uint8_t *, size_t); int dp_update_port_flags(struct datapath *dp, const struct ofp_phy_port *opp); diff --git a/datapath/forward.c b/datapath/forward.c index 7ab28278..6553c947 100644 --- a/datapath/forward.c +++ b/datapath/forward.c @@ -437,20 +437,6 @@ recv_flow(struct sw_chain *chain, const struct sender *sender, const void *msg) } } -static int -recv_port_stats_request(struct sw_chain *chain, const struct sender *sender, - const void *msg) -{ - return dp_send_port_stats(chain->dp, sender); -} - -static int -recv_table_stats_request(struct sw_chain *chain, const struct sender *sender, - const void *msg) -{ - return dp_send_table_stats(chain->dp, sender); -} - /* 'msg', which is 'length' bytes long, was received across Netlink from * 'sender'. Apply it to 'chain'. */ int @@ -489,14 +475,6 @@ fwd_control_input(struct sw_chain *chain, const struct sender *sender, sizeof (struct ofp_port_mod), recv_port_mod, }, - [OFPT_PORT_STATS_REQUEST] = { - sizeof (struct ofp_port_stats_request), - recv_port_stats_request, - }, - [OFPT_TABLE_STATS_REQUEST] = { - sizeof (struct ofp_table_stats_request), - recv_table_stats_request, - }, }; const struct openflow_packet *pkt; diff --git a/include/openflow.h b/include/openflow.h index c8253e30..776dafdc 100644 --- a/include/openflow.h +++ b/include/openflow.h @@ -93,12 +93,8 @@ enum ofp_type { OFPT_PORT_MOD, /* 10 Controller/switch message */ OFPT_PORT_STATUS, /* 11 Async message */ OFPT_ERROR_MSG, /* 12 Async message */ - OFPT_FLOW_STATS_REQUEST, /* 13 Controller/switch message */ - OFPT_FLOW_STATS_REPLY, /* 14 Controller/switch message */ - OFPT_TABLE_STATS_REQUEST, /* 15 Controller/switch message */ - OFPT_TABLE_STATS_REPLY, /* 16 Controller/switch message */ - OFPT_PORT_STATS_REQUEST, /* 17 Controller/switch message */ - OFPT_PORT_STATS_REPLY /* 18 Controller/switch message */ + OFPT_STATS_REQUEST, /* 13 Controller/switch message */ + OFPT_STATS_REPLY /* 14 Controller/switch message */ }; /* Header on all OpenFlow packets. */ @@ -369,17 +365,39 @@ struct ofp_error_msg { on the type and code. */ }; -/* Statistics about flows that match the "match" field */ -struct ofp_flow_stats { - struct ofp_match match; /* Description of fields */ - uint32_t duration; /* Time flow has been alive in seconds. Only - used for non-aggregated results. */ - uint64_t packet_count; /* Number of packets in flow. */ - uint64_t byte_count; /* Number of bytes in flow. */ - uint16_t priority; /* Priority of the entry. Only meaningful - when this is not an exact-match entry. */ - uint8_t table_id; /* ID of table flow came from. */ - uint8_t pad[5]; /* Align to 64-bits. */ +enum ofp_stats_types { + /* Flow statistics. + * The request body is struct ofp_flow_stats_request. + * The reply body is an array of struct ofp_flow_stats. */ + OFPST_FLOW, + + /* Flow table statistics. + * The request body is empty. + * The reply body is an array of struct ofp_table_stats. */ + OFPST_TABLE, + + /* Physical port statistics. + * The request body is empty. + * The reply body is an array of struct ofp_port_stats. */ + OFPST_PORT +}; + +struct ofp_stats_request { + struct ofp_header header; + uint16_t type; /* One of the OFPST_* constants. */ + uint16_t flags; /* OFPSF_REQ_* flags (none yet defined). */ + uint8_t body[0]; /* Body of the request. */ +}; + +enum ofp_stats_reply_flags { + OFPSF_REPLY_MORE = 1 << 0, /* More replies to follow */ +}; + +struct ofp_stats_reply { + struct ofp_header header; + uint16_t type; /* One of the OFPST_* constants. */ + uint16_t flags; /* OFPSF_REPLY_* flags. */ + uint8_t body[0]; /* Body of the reply. */ }; enum ofp_stats_type { @@ -387,9 +405,8 @@ enum ofp_stats_type { OFPFS_AGGREGATE /* Aggregate matching flows */ }; -/* Current flow statistics request */ +/* Body for ofp_stats_request of type OFPST_FLOW. */ struct ofp_flow_stats_request { - struct ofp_header header; struct ofp_match match; /* Fields to match */ uint8_t table_id; /* ID of table to read (from ofp_table_stats) or 0xff for all tables. */ @@ -397,26 +414,20 @@ struct ofp_flow_stats_request { uint16_t pad; /* Align to 32-bits */ }; -/* Current flow statistics reply */ -struct ofp_flow_stats_reply { - struct ofp_header header; - - /* If request was of type OFPFS_INDIV, this will contain an array of - * flow statistic entries. The number of matching flows is likely - * much larger than can fit in a single OpenFlow message, so a - * a response with no flows included is sent to indicate the end. - * If it was a OFPFS_AGGREGATE request, only a single flow stats - * entry will be contained in the response. - */ - struct ofp_flow_stats flows[0]; -}; - -/* Current table statistics request */ -struct ofp_table_stats_request { - struct ofp_header header; +/* Body of reply to OFPST_FLOW request. */ +struct ofp_flow_stats { + struct ofp_match match; /* Description of fields */ + uint32_t duration; /* Time flow has been alive in seconds. Only + used for non-aggregated results. */ + uint64_t packet_count; /* Number of packets in flow. */ + uint64_t byte_count; /* Number of bytes in flow. */ + uint16_t priority; /* Priority of the entry. Only meaningful + when this is not an exact-match entry. */ + uint8_t table_id; /* ID of table flow came from. */ + uint8_t pad[5]; /* Align to 64-bits. */ }; -/* Statistics about a particular table */ +/* Body of reply to OFPST_TABLE request. */ struct ofp_table_stats { uint8_t table_id; uint8_t pad[3]; /* Align to 32-bits */ @@ -426,13 +437,6 @@ struct ofp_table_stats { uint64_t matched_count; /* Number of packets that hit table */ }; -/* Current table statistics reply */ -struct ofp_table_stats_reply { - struct ofp_header header; - struct ofp_table_stats tables[0]; /* The number of entries is inferred from - the length field in the header. */ -}; - /* Statistics about a particular port */ struct ofp_port_stats { uint16_t port_no; @@ -442,16 +446,4 @@ struct ofp_port_stats { uint64_t drop_count; /* Number of packets dropped by interface */ }; -/* Current port statistics request */ -struct ofp_port_stats_request { - struct ofp_header header; -}; - -/* Current port statistics reply */ -struct ofp_port_stats_reply { - struct ofp_header header; - struct ofp_port_stats ports[0]; /* The number of entries is inferred from - the length field in the header. */ -}; - #endif /* openflow.h */ diff --git a/include/rconn.h b/include/rconn.h index 064be01a..3e72da03 100644 --- a/include/rconn.h +++ b/include/rconn.h @@ -62,6 +62,7 @@ void rconn_run_wait(struct rconn *); struct buffer *rconn_recv(struct rconn *); void rconn_recv_wait(struct rconn *); int rconn_send(struct rconn *, struct buffer *); +bool rconn_is_full(const struct rconn *); const char *rconn_get_name(const struct rconn *); bool rconn_is_alive(const struct rconn *); diff --git a/lib/dpif.c b/lib/dpif.c index 34268993..e5919856 100644 --- a/lib/dpif.c +++ b/lib/dpif.c @@ -202,10 +202,10 @@ dpif_send_openflow(struct dpif *dp, struct buffer *buffer, bool wait) int n_iov; int retval; - /* The reply to OFPT_FLOW_STATS_REQUEST may be multiple segments long, so - * we need to specify NLM_F_DUMP in the request. */ + /* The reply to OFPT_STATS_REQUEST may be multiple segments long, so we + * need to specify NLM_F_DUMP in the request. */ oh = buffer_at_assert(buffer, 0, sizeof *oh); - dump_flag = oh->type == OFPT_FLOW_STATS_REQUEST ? NLM_F_DUMP : 0; + dump_flag = oh->type == OFPT_STATS_REQUEST ? NLM_F_DUMP : 0; buffer_use(&hdr, fixed_buffer, sizeof fixed_buffer); nl_msg_put_genlmsghdr(&hdr, dp->sock, 32, openflow_family, diff --git a/lib/ofp-print.c b/lib/ofp-print.c index 8b2da7e0..4c19bb95 100644 --- a/lib/ofp-print.c +++ b/lib/ofp-print.c @@ -466,20 +466,17 @@ ofp_flow_stats_request(struct ds *string, const void *oh, size_t len, } static void -ofp_flow_stats_reply(struct ds *string, const void *oh, size_t len, +ofp_flow_stats_reply(struct ds *string, const void *body, size_t len, int verbosity) { - const struct ofp_flow_stats_reply *fsr = oh; - const struct ofp_flow_stats *fs; - size_t n; - - n = (len - offsetof(struct ofp_flow_stats_reply, flows)) / sizeof *fs; + const struct ofp_flow_stats *fs = body; + size_t n = len / sizeof *fs; ds_put_format(string, " %zu flows\n", n); if (verbosity < 1) { return; } - for (fs = &fsr->flows[0]; fs < &fsr->flows[n]; fs++) { + for (; n--; fs++) { ds_put_format(string, " duration=%"PRIu32" s, ", ntohl(fs->duration)); ds_put_format(string, "table_id=%"PRIu8", ", fs->table_id); ds_put_format(string, "priority=%"PRIu16", ", @@ -492,20 +489,17 @@ ofp_flow_stats_reply(struct ds *string, const void *oh, size_t len, } static void -ofp_port_stats_reply(struct ds *string, const void *oh, size_t len, - int verbosity) +ofp_port_stats_reply(struct ds *string, const void *body, size_t len, + int verbosity) { - const struct ofp_port_stats_reply *psr = oh; - const struct ofp_port_stats *ps; - size_t n; - - n = (len - offsetof(struct ofp_port_stats_reply, ports)) / sizeof *ps; + const struct ofp_port_stats *ps = body; + size_t n = len / sizeof *ps; ds_put_format(string, " %zu ports\n", n); if (verbosity < 1) { return; } - for (ps = &psr->ports[0]; ps < &psr->ports[n]; ps++) { + for (; n--; ps++) { ds_put_format(string, " port %"PRIu16": ", ntohs(ps->port_no)); ds_put_format(string, "rx %"PRIu64", ", ntohll(ps->rx_count)); ds_put_format(string, "tx %"PRIu64", ", ntohll(ps->tx_count)); @@ -514,20 +508,17 @@ ofp_port_stats_reply(struct ds *string, const void *oh, size_t len, } static void -ofp_table_stats_reply(struct ds *string, const void *oh, size_t len, +ofp_table_stats_reply(struct ds *string, const void *body, size_t len, int verbosity) { - const struct ofp_table_stats_reply *tsr = oh; - const struct ofp_table_stats *ts; - size_t n; - - n = (len - offsetof(struct ofp_table_stats_reply, tables)) / sizeof *ts; + const struct ofp_table_stats *ts = body; + size_t n = len / sizeof *ts; ds_put_format(string, " %zu tables\n", n); if (verbosity < 1) { return; } - for (ts = &tsr->tables[0]; ts < &tsr->tables[n]; ts++) { + for (; n--; ts++) { char name[OFP_MAX_TABLE_NAME_LEN + 1]; strncpy(name, ts->name, sizeof name); name[OFP_MAX_TABLE_NAME_LEN] = '\0'; @@ -541,6 +532,106 @@ ofp_table_stats_reply(struct ds *string, const void *oh, size_t len, } } +enum stats_direction { + REQUEST, + REPLY +}; + +static void +print_stats(struct ds *string, int type, const void *body, size_t body_len, + int verbosity, enum stats_direction direction) +{ + struct stats_msg { + size_t min_body, max_body; + void (*printer)(struct ds *, const void *, size_t len, int verbosity); + }; + + struct stats_type { + const char *name; + struct stats_msg request; + struct stats_msg reply; + }; + + static const struct stats_type stats_types[] = { + [OFPST_FLOW] = { + "flow", + { sizeof(struct ofp_flow_stats_request), + sizeof(struct ofp_flow_stats_request), + ofp_flow_stats_request }, + { 0, SIZE_MAX, ofp_flow_stats_reply }, + }, + [OFPST_TABLE] = { + "table", + { 0, 0, NULL }, + { 0, SIZE_MAX, ofp_table_stats_reply }, + }, + [OFPST_PORT] = { + "port", + { 0, 0, NULL, }, + { 0, SIZE_MAX, ofp_port_stats_reply }, + }, + }; + + const struct stats_type *s; + const struct stats_msg *m; + + if (type >= ARRAY_SIZE(stats_types) || !stats_types[type].name) { + ds_put_format(string, " ***unknown type %d***", type); + return; + } + s = &stats_types[type]; + ds_put_format(string, " type=%d(%s)", type, s->name); + + m = direction == REQUEST ? &s->request : &s->reply; + if (body_len < m->min_body || body_len > m->max_body) { + ds_put_format(string, " ***body_len=%zu not in %zu...%zu***", + body_len, m->min_body, m->max_body); + return; + } + if (m->printer) { + m->printer(string, body, body_len, verbosity); + } +} + +static void +ofp_stats_request(struct ds *string, const void *oh, size_t len, int verbosity) +{ + const struct ofp_stats_request *srq = oh; + + if (srq->flags) { + ds_put_format(string, " ***unknown flags %04"PRIx16"***", + ntohs(srq->flags)); + } + + print_stats(string, ntohs(srq->type), srq->body, + len - offsetof(struct ofp_stats_request, body), + verbosity, REQUEST); +} + +static void +ofp_stats_reply(struct ds *string, const void *oh, size_t len, int verbosity) +{ + const struct ofp_stats_reply *srp = oh; + + ds_put_cstr(string, " flags="); + if (!srp->flags) { + ds_put_cstr(string, "none"); + } else { + uint16_t flags = ntohs(srp->flags); + if (flags & OFPSF_REPLY_MORE) { + ds_put_cstr(string, "[more]"); + flags &= ~OFPSF_REPLY_MORE; + } + if (flags) { + ds_put_format(string, "[***unknown%04"PRIx16"***]", flags); + } + } + + print_stats(string, ntohs(srp->type), srp->body, + len - offsetof(struct ofp_stats_reply, body), + verbosity, REPLY); +} + struct openflow_packet { const char *name; size_t min_size; @@ -608,35 +699,15 @@ static const struct openflow_packet packets[] = { sizeof (struct ofp_error_msg), ofp_print_error_msg, }, - [OFPT_FLOW_STATS_REQUEST] = { - "flow_stats_request", - sizeof (struct ofp_flow_stats_request), - ofp_flow_stats_request, - }, - [OFPT_FLOW_STATS_REPLY] = { - "flow_stats_reply", - sizeof (struct ofp_flow_stats_reply), - ofp_flow_stats_reply, - }, - [OFPT_PORT_STATS_REQUEST] = { - "port_stats_request", - sizeof (struct ofp_port_stats_request), - NULL, - }, - [OFPT_PORT_STATS_REPLY] = { - "port_stats_reply", - sizeof (struct ofp_port_stats_reply), - ofp_port_stats_reply, + [OFPT_STATS_REQUEST] = { + "stats_request", + sizeof (struct ofp_stats_request), + ofp_stats_request, }, - [OFPT_TABLE_STATS_REQUEST] = { - "table_stats_request", - sizeof (struct ofp_table_stats_request), - NULL, - }, - [OFPT_TABLE_STATS_REPLY] = { - "table_stats_reply", - sizeof (struct ofp_table_stats_reply), - ofp_table_stats_reply, + [OFPT_STATS_REPLY] = { + "stats_reply", + sizeof (struct ofp_stats_reply), + ofp_stats_reply, }, }; @@ -692,6 +763,9 @@ ofp_to_string(const void *oh_, size_t len, int verbosity) if (verbosity >= 3) { ds_put_hex_dump(&string, oh, len, 0, true); } + if (string.string[string.length - 1] != '\n') { + ds_put_char(&string, '\n'); + } return ds_cstr(&string); } diff --git a/lib/rconn.c b/lib/rconn.c index 73d42bb9..7fd44052 100644 --- a/lib/rconn.c +++ b/lib/rconn.c @@ -207,6 +207,14 @@ rconn_send(struct rconn *rc, struct buffer *b) } } +/* Returns true if 'rc''s send buffer is full, + * false if it has room for at least one more packet. */ +bool +rconn_is_full(const struct rconn *rc) +{ + return rc->txq.n >= rc->txq_limit; +} + /* Returns 'rc''s name (the 'name' argument passed to rconn_new()). */ const char * rconn_get_name(const struct rconn *rc) diff --git a/switch/Makefile.am b/switch/Makefile.am index eb9931b0..7488e072 100644 --- a/switch/Makefile.am +++ b/switch/Makefile.am @@ -16,7 +16,6 @@ switch_SOURCES = \ switch-flow.h \ table.h \ table-hash.c \ - table-linear.c \ - table-mac.c + table-linear.c switch_LDADD = ../lib/libopenflow.la -ldl diff --git a/switch/chain.c b/switch/chain.c index dab4dd33..a85a6501 100644 --- a/switch/chain.c +++ b/switch/chain.c @@ -65,9 +65,7 @@ struct sw_chain *chain_create(void) if (chain == NULL) return NULL; - if (add_table(chain, table_mac_create(TABLE_MAC_NUM_BUCKETS, - TABLE_MAC_MAX_FLOWS)) - || add_table(chain, table_hash2_create(0x1EDC6F41, TABLE_HASH_MAX_FLOWS, + if (add_table(chain, table_hash2_create(0x1EDC6F41, TABLE_HASH_MAX_FLOWS, 0x741B8CD7, TABLE_HASH_MAX_FLOWS)) || add_table(chain, table_linear_create(TABLE_LINEAR_MAX_FLOWS))) { chain_destroy(chain); diff --git a/switch/datapath.c b/switch/datapath.c index 1332a4c6..521150ba 100644 --- a/switch/datapath.c +++ b/switch/datapath.c @@ -74,16 +74,25 @@ struct sw_port { unsigned long long int rx_count, tx_count, drop_count; }; +/* The origin of a received OpenFlow message, to enable sending a reply. */ +struct sender { + struct remote *remote; /* The device that sent the message. */ + uint32_t xid; /* The OpenFlow transaction ID. */ +}; + /* A connection to a controller or a management device. */ struct remote { struct list node; struct rconn *rconn; -}; -/* The origin of a received OpenFlow message, to enable sending a reply. */ -struct sender { - struct remote *remote; /* The device that sent the message. */ - uint32_t xid; /* The OpenFlow transaction ID. */ + /* Support for reliable, multi-message replies to requests. + * + * If an incoming request needs to have a reliable reply that might + * require multiple messages, it can use remote_start_dump() to set up + * a callback that will be called as buffer space for replies. */ + int (*cb_dump)(struct datapath *, void *aux); + void (*cb_done)(void *aux); + void *cb_aux; }; struct datapath { @@ -244,7 +253,7 @@ dp_add_listen_vconn(struct datapath *dp, struct vconn *listen_vconn) } void -dp_run(struct datapath *dp) +dp_run(struct datapath *dp) { time_t now = time(0); struct sw_port *p, *pn; @@ -319,28 +328,43 @@ remote_run(struct datapath *dp, struct remote *r) rconn_run(r->rconn); - /* Process a number of commands from the remote, but cap them at a - * reasonable number so that other processing doesn't starve. */ + /* Do some remote processing, but cap it at a reasonable amount so that + * other processing doesn't starve. */ for (i = 0; i < 50; i++) { - struct buffer *buffer; - struct ofp_header *oh; + if (!r->cb_dump) { + struct buffer *buffer; + struct ofp_header *oh; - buffer = rconn_recv(r->rconn); - if (!buffer) { - break; - } + buffer = rconn_recv(r->rconn); + if (!buffer) { + break; + } - if (buffer->size >= sizeof *oh) { - struct sender sender; + if (buffer->size >= sizeof *oh) { + struct sender sender; - oh = buffer->data; - sender.remote = r; - sender.xid = oh->xid; - fwd_control_input(dp, &sender, buffer->data, buffer->size); + oh = buffer->data; + sender.remote = r; + sender.xid = oh->xid; + fwd_control_input(dp, &sender, buffer->data, buffer->size); + } else { + VLOG_WARN("received too-short OpenFlow message"); + } + buffer_delete(buffer); } else { - VLOG_WARN("received too-short OpenFlow message"); + if (!rconn_is_full(r->rconn)) { + int error = r->cb_dump(dp, r->cb_aux); + if (error <= 0) { + if (error) { + VLOG_WARN("dump callback error: %s", strerror(-error)); + } + r->cb_done(r->cb_aux); + r->cb_dump = NULL; + } + } else { + break; + } } - buffer_delete(buffer); } if (!rconn_is_alive(r->rconn)) { @@ -359,6 +383,9 @@ static void remote_destroy(struct remote *r) { if (r) { + if (r->cb_dump && r->cb_done) { + r->cb_done(r->cb_aux); + } list_remove(&r->node); rconn_destroy(r->rconn); free(r); @@ -371,9 +398,36 @@ remote_create(struct datapath *dp, struct rconn *rconn) struct remote *remote = xmalloc(sizeof *remote); list_push_back(&dp->remotes, &remote->node); remote->rconn = rconn; + remote->cb_dump = NULL; return remote; } +/* Starts a callback-based, reliable, possibly multi-message reply to a + * request made by 'remote'. + * + * 'dump' designates a function that will be called when the 'remote' send + * queue has an empty slot. It should compose a message and send it on + * 'remote'. On success, it should return 1 if it should be called again when + * another send queue slot opens up, 0 if its transmissions are complete, or a + * negative errno value on failure. + * + * 'done' designates a function to clean up any resources allocated for the + * dump. It must handle being called before the dump is complete (which will + * happen if 'remote' is closed unexpectedly). + * + * 'aux' is passed to 'dump' and 'done'. */ +static void +remote_start_dump(struct remote *remote, + int (*dump)(struct datapath *, void *), + void (*done)(void *), + void *aux) +{ + assert(!remote->cb_dump); + remote->cb_dump = dump; + remote->cb_done = done; + remote->cb_aux = aux; +} + void dp_wait(struct datapath *dp) { @@ -677,99 +731,6 @@ fill_flow_stats(struct ofp_flow_stats *ofs, struct sw_flow *flow, memset(ofs->pad, 0, sizeof ofs->pad); } -int -dp_send_flow_stats(struct datapath *dp, const struct sender *sender, - const struct ofp_match *match) -{ - struct buffer *buffer; - struct ofp_flow_stats_reply *fsr; - size_t header_size, fudge, flow_size; - struct sw_flow_key match_key; - int table_idx, n_flows, max_flows; - time_t now; - - header_size = offsetof(struct ofp_flow_stats_reply, flows); - fudge = 128; - flow_size = sizeof fsr->flows[0]; - max_flows = (65536 - header_size - fudge) / flow_size; - fsr = alloc_openflow_buffer(dp, header_size, - OFPT_FLOW_STATS_REPLY, sender, &buffer); - - n_flows = 0; - flow_extract_match(&match_key, match); - now = time(0); - for (table_idx = 0; table_idx < dp->chain->n_tables; table_idx++) { - struct sw_table *table = dp->chain->tables[table_idx]; - struct swt_iterator iter; - - if (n_flows >= max_flows) { - break; - } - - if (!table->iterator(table, &iter)) { - printf("iterator failed for table %d\n", table_idx); - continue; - } - - for (; iter.flow; table->iterator_next(&iter)) { - if (flow_matches(&match_key, &iter.flow->key)) { - struct ofp_flow_stats *ofs = buffer_put_uninit(buffer, - sizeof *ofs); - fill_flow_stats(ofs, iter.flow, table_idx, now); - if (++n_flows >= max_flows) { - break; - } - } - } - table->iterator_destroy(&iter); - } - return send_openflow_buffer(dp, buffer, sender); -} - -int -dp_send_port_stats(struct datapath *dp, const struct sender *sender) -{ - struct buffer *buffer; - struct ofp_port_stats_reply *psr; - struct sw_port *p; - - psr = alloc_openflow_buffer(dp, offsetof(struct ofp_port_stats_reply, - ports), - OFPT_PORT_STATS_REPLY, sender, &buffer); - LIST_FOR_EACH (p, struct sw_port, node, &dp->port_list) { - struct ofp_port_stats *ps = buffer_put_uninit(buffer, sizeof *ps); - ps->port_no = htons(port_no(dp, p)); - memset(ps->pad, 0, sizeof ps->pad); - ps->rx_count = htonll(p->rx_count); - ps->tx_count = htonll(p->tx_count); - ps->drop_count = htonll(p->drop_count); - } - return send_openflow_buffer(dp, buffer, sender); -} - -int -dp_send_table_stats(struct datapath *dp, const struct sender *sender) -{ - struct buffer *buffer; - struct ofp_table_stats_reply *tsr; - int i; - - tsr = alloc_openflow_buffer(dp, offsetof(struct ofp_table_stats_reply, - tables), - OFPT_TABLE_STATS_REPLY, sender, &buffer); - for (i = 0; i < dp->chain->n_tables; i++) { - struct ofp_table_stats *ots = buffer_put_uninit(buffer, sizeof *ots); - struct sw_table_stats stats; - dp->chain->tables[i]->stats(dp->chain->tables[i], &stats); - strncpy(ots->name, stats.name, sizeof ots->name); - ots->table_id = i; - ots->pad[0] = ots->pad[1] = 0; - ots->max_entries = htonl(stats.max_flows); - ots->active_count = htonl(stats.n_flows); - ots->matched_count = htonll(0); /* FIXME */ - } - return send_openflow_buffer(dp, buffer, sender); -} /* 'buffer' was received on 'in_port', a physical switch port between 0 and * OFPP_MAX. Process it according to 'chain'. */ @@ -1161,31 +1122,282 @@ recv_flow(struct datapath *dp, const struct sender *sender UNUSED, } } -static int -recv_flow_stats_request(struct datapath *dp, const struct sender *sender, - const void *msg) +struct flow_stats_state { + int table_idx; + struct sw_table_position position; + struct ofp_flow_stats_request rq; + time_t now; + + struct buffer *buffer; + int n_flows, max_flows; +}; + +static int flow_stats_init(struct datapath *dp, const void *body, int body_len, + void **state) +{ + const struct ofp_flow_stats_request *fsr = body; + struct flow_stats_state *s = xmalloc(sizeof *s); + s->table_idx = fsr->table_id == 0xff ? 0 : fsr->table_id; + memset(&s->position, 0, sizeof s->position); + s->rq = *fsr; + *state = s; + return 0; +} + +static int flow_stats_dump_callback(struct sw_flow *flow, void *private) +{ + struct flow_stats_state *s = private; + struct ofp_flow_stats *ofs = buffer_put_uninit(s->buffer, sizeof *ofs); + fill_flow_stats(ofs, flow, s->table_idx, s->now); + return ++s->n_flows >= s->max_flows; +} + +static int flow_stats_dump(struct datapath *dp, void *state, + struct buffer *buffer) +{ + struct flow_stats_state *s = state; + struct ofp_flow_stats *ofs; + struct sw_flow_key match_key; + + s->max_flows = 4096 / sizeof *ofs; + if (!s->max_flows) + return -ENOMEM; + + flow_extract_match(&match_key, &s->rq.match); + s->buffer = buffer; + s->n_flows = 0; + s->now = time(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, + flow_stats_dump_callback, s)) + break; + + s->table_idx++; + memset(&s->position, 0, sizeof s->position); + } + return s->n_flows >= s->max_flows; +} + +static void flow_stats_done(void *state) +{ + free(state); +} + +static int table_stats_dump(struct datapath *dp, void *state, + struct buffer *buffer) +{ + int i; + for (i = 0; i < dp->chain->n_tables; i++) { + struct ofp_table_stats *ots = buffer_put_uninit(buffer, sizeof *ots); + struct sw_table_stats stats; + dp->chain->tables[i]->stats(dp->chain->tables[i], &stats); + strncpy(ots->name, stats.name, sizeof ots->name); + ots->table_id = i; + memset(ots->pad, 0, sizeof ots->pad); + ots->max_entries = htonl(stats.max_flows); + ots->active_count = htonl(stats.n_flows); + ots->matched_count = htonll(0); /* FIXME */ + } + return 0; +} + +struct port_stats_state { + int port; +}; + +static int port_stats_init(struct datapath *dp, const void *body, int body_len, + void **state) { - const struct ofp_flow_stats_request *fsr = msg; - if (fsr->type == OFPFS_INDIV) { - return dp_send_flow_stats(dp, sender, &fsr->match); - } else { - /* FIXME */ - return -ENOSYS; + struct port_stats_state *s = xmalloc(sizeof *s); + s->port = 0; + *state = s; + return 0; +} + +static int port_stats_dump(struct datapath *dp, void *state, + struct buffer *buffer) +{ + struct port_stats_state *s = state; + int i; + + for (i = s->port; i < OFPP_MAX; i++) { + struct sw_port *p = &dp->ports[i]; + struct ofp_port_stats *ops; + if (!p->netdev) { + continue; + } + ops = buffer_put_uninit(buffer, sizeof *ops); + ops->port_no = htons(port_no(dp, p)); + memset(ops->pad, 0, sizeof ops->pad); + ops->rx_count = htonll(p->rx_count); + ops->tx_count = htonll(p->tx_count); + ops->drop_count = htonll(p->drop_count); + ops++; } + s->port = i; + return 0; +} + +static void port_stats_done(void *state) +{ + free(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); + + /* Appends statistics for 'dp' to 'buffer', which initially contains a + * struct ofp_stats_reply. On success, it should return 1 if it should be + * called again later with another buffer, 0 if it is done, or a negative + * errno value on failure. */ + int (*dump)(struct datapath *dp, void *state, struct buffer *buffer); + + /* 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_FLOW] = { + sizeof(struct ofp_flow_stats_request), + sizeof(struct ofp_flow_stats_request), + flow_stats_init, + flow_stats_dump, + flow_stats_done + }, + [OFPST_TABLE] = { + 0, + 0, + NULL, + table_stats_dump, + NULL + }, + [OFPST_PORT] = { + 0, + 0, + port_stats_init, + port_stats_dump, + port_stats_done + }, +}; + +struct stats_dump_cb { + bool done; + struct ofp_stats_request *rq; + struct sender sender; + const struct stats_type *s; + void *state; +}; + static int -recv_port_stats_request(struct datapath *dp, const struct sender *sender, - const void *msg) +stats_dump(struct datapath *dp, void *cb_) +{ + struct stats_dump_cb *cb = cb_; + struct ofp_stats_reply *osr; + struct buffer *buffer; + int err; + + if (cb->done) { + return 0; + } + + osr = alloc_openflow_buffer(dp, sizeof *osr, OFPT_STATS_REPLY, &cb->sender, + &buffer); + osr->type = htons(cb->s - stats); + osr->flags = 0; + + err = cb->s->dump(dp, cb->state, buffer); + if (err >= 0) { + int err2; + if (!err) { + cb->done = true; + } else { + /* Buffer might have been reallocated, so find our data again. */ + osr = buffer_at_assert(buffer, 0, sizeof *osr); + osr->flags = ntohs(OFPSF_REPLY_MORE); + } + err2 = send_openflow_buffer(dp, buffer, &cb->sender); + if (err2) { + err = err2; + } + } + + return err; +} + +static void +stats_done(void *cb_) { - return dp_send_port_stats(dp, sender); + struct stats_dump_cb *cb = cb_; + if (cb) { + if (cb->s->done) { + cb->s->done(cb->state); + } + free(cb); + } } static int -recv_table_stats_request(struct datapath *dp, const struct sender *sender, - const void *msg) +recv_stats_request(struct datapath *dp, const struct sender *sender, + const void *oh) { - return dp_send_table_stats(dp, sender); + const struct ofp_stats_request *rq = oh; + size_t rq_len = ntohs(rq->header.length); + struct stats_dump_cb *cb; + int type, body_len; + int err; + + type = ntohs(rq->type); + if (type >= ARRAY_SIZE(stats) || !stats[type].dump) { + VLOG_WARN("received stats request of unknown type %d", type); + return -EINVAL; + } + + cb = xmalloc(sizeof *cb); + cb->done = false; + cb->rq = xmemdup(rq, rq_len); + cb->sender = *sender; + cb->s = &stats[type]; + cb->state = NULL; + + body_len = rq_len - offsetof(struct ofp_stats_request, body); + if (body_len < cb->s->min_body || body_len > cb->s->max_body) { + VLOG_WARN("stats request type %d with bad body length %d", + type, body_len); + err = -EINVAL; + goto error; + } + + if (cb->s->init) { + err = cb->s->init(dp, rq->body, body_len, &cb->state); + if (err) { + VLOG_WARN("failed initialization of stats request type %d: %s", + type, strerror(-err)); + goto error; + } + } + + remote_start_dump(sender->remote, stats_dump, stats_done, cb); + return 0; + +error: + free(cb->rq); + free(cb); + return err; } /* 'msg', which is 'length' bytes long, was received from the control path. @@ -1224,17 +1436,9 @@ fwd_control_input(struct datapath *dp, const struct sender *sender, sizeof (struct ofp_port_mod), recv_port_mod, }, - [OFPT_FLOW_STATS_REQUEST] = { - sizeof (struct ofp_flow_stats_request), - recv_flow_stats_request, - }, - [OFPT_PORT_STATS_REQUEST] = { - sizeof (struct ofp_port_stats_request), - recv_port_stats_request, - }, - [OFPT_TABLE_STATS_REQUEST] = { - sizeof (struct ofp_table_stats_request), - recv_table_stats_request, + [OFPT_STATS_REQUEST] = { + sizeof (struct ofp_stats_request), + recv_stats_request, }, }; diff --git a/switch/switch-flow.h b/switch/switch-flow.h index 40d94dd2..8de25bef 100644 --- a/switch/switch-flow.h +++ b/switch/switch-flow.h @@ -58,7 +58,11 @@ struct sw_flow { time_t timeout; /* When the flow expires (if idle). */ uint64_t packet_count; /* Number of packets seen. */ uint64_t byte_count; /* Number of bytes seen. */ + + /* Private to table implementations. */ struct list node; + struct list iter_node; + unsigned long int serial; /* Actions (XXX probably most flows have only a single action). */ unsigned int n_actions; diff --git a/switch/table-hash.c b/switch/table-hash.c index 47bbe4a9..aaec5039 100644 --- a/switch/table-hash.c +++ b/switch/table-hash.c @@ -159,56 +159,36 @@ static void table_hash_destroy(struct sw_table *swt) free(th); } -struct swt_iterator_hash { - struct sw_table_hash *th; - unsigned int bucket_i; -}; - -static struct sw_flow *next_flow(struct swt_iterator_hash *ih) -{ - for (;ih->bucket_i <= ih->th->bucket_mask; ih->bucket_i++) { - struct sw_flow *f = ih->th->buckets[ih->bucket_i]; - if (f != NULL) - return f; - } - - return NULL; -} - -static int table_hash_iterator(struct sw_table *swt, - struct swt_iterator *swt_iter) +static int table_hash_iterate(struct sw_table *swt, + const struct sw_flow_key *key, + struct sw_table_position *position, + int (*callback)(struct sw_flow *, void *private), + void *private) { - struct swt_iterator_hash *ih; - - swt_iter->private = ih = malloc(sizeof *ih); + struct sw_table_hash *th = (struct sw_table_hash *) swt; - if (ih == NULL) + if (position->private[0] > th->bucket_mask) return 0; - ih->th = (struct sw_table_hash *) swt; - - ih->bucket_i = 0; - swt_iter->flow = next_flow(ih); - - return 1; -} - -static void table_hash_next(struct swt_iterator *swt_iter) -{ - struct swt_iterator_hash *ih; - - if (swt_iter->flow == NULL) - return; - - ih = (struct swt_iterator_hash *) swt_iter->private; - - ih->bucket_i++; - swt_iter->flow = next_flow(ih); -} - -static void table_hash_iterator_destroy(struct swt_iterator *swt_iter) -{ - free(swt_iter->private); + if (key->wildcards == 0) { + struct sw_flow *flow = table_hash_lookup(swt, key); + position->private[0] = -1; + return flow ? callback(flow, private) : 0; + } else { + int i; + + for (i = position->private[0]; i <= th->bucket_mask; i++) { + struct sw_flow *flow = th->buckets[i]; + if (flow && flow_matches(key, &flow->key)) { + int error = callback(flow, private); + if (error) { + position->private[0] = i + 1; + return error; + } + } + } + return 0; + } } static void table_hash_stats(struct sw_table *swt, @@ -246,9 +226,7 @@ struct sw_table *table_hash_create(unsigned int polynomial, swt->delete = table_hash_delete; swt->timeout = table_hash_timeout; swt->destroy = table_hash_destroy; - swt->iterator = table_hash_iterator; - swt->iterator_next = table_hash_next; - swt->iterator_destroy = table_hash_iterator_destroy; + swt->iterate = table_hash_iterate; swt->stats = table_hash_stats; crc32_init(&th->crc32, polynomial); @@ -310,79 +288,25 @@ static void table_hash2_destroy(struct sw_table *swt) free(t2); } -struct swt_iterator_hash2 { - struct sw_table_hash2 *th2; - struct swt_iterator ih; - uint8_t table_i; -}; - -static int table_hash2_iterator(struct sw_table *swt, - struct swt_iterator *swt_iter) +static int table_hash2_iterate(struct sw_table *swt, + const struct sw_flow_key *key, + struct sw_table_position *position, + int (*callback)(struct sw_flow *, void *), + void *private) { - struct swt_iterator_hash2 *ih2; - - swt_iter->private = ih2 = malloc(sizeof *ih2); - if (ih2 == NULL) - return 0; - - ih2->th2 = (struct sw_table_hash2 *) swt; - if (!table_hash_iterator(ih2->th2->subtable[0], &ih2->ih)) { - free(ih2); - return 0; - } - - if (ih2->ih.flow != NULL) { - swt_iter->flow = ih2->ih.flow; - ih2->table_i = 0; - } else { - table_hash_iterator_destroy(&ih2->ih); - ih2->table_i = 1; - if (!table_hash_iterator(ih2->th2->subtable[1], &ih2->ih)) { - free(ih2); - return 0; - } - swt_iter->flow = ih2->ih.flow; - } - - return 1; -} - -static void table_hash2_next(struct swt_iterator *swt_iter) -{ - struct swt_iterator_hash2 *ih2; - - if (swt_iter->flow == NULL) - return; - - ih2 = (struct swt_iterator_hash2 *) swt_iter->private; - table_hash_next(&ih2->ih); + struct sw_table_hash2 *t2 = (struct sw_table_hash2 *) swt; + int i; - if (ih2->ih.flow != NULL) { - swt_iter->flow = ih2->ih.flow; - } else { - if (ih2->table_i == 0) { - table_hash_iterator_destroy(&ih2->ih); - ih2->table_i = 1; - if (!table_hash_iterator(ih2->th2->subtable[1], &ih2->ih)) { - ih2->ih.private = NULL; - swt_iter->flow = NULL; - } else { - swt_iter->flow = ih2->ih.flow; - } - } else { - swt_iter->flow = NULL; + for (i = position->private[1]; i < 2; i++) { + int error = table_hash_iterate(t2->subtable[i], key, position, + callback, private); + if (error) { + return error; } + position->private[0] = 0; + position->private[1]++; } -} - -static void table_hash2_iterator_destroy(struct swt_iterator *swt_iter) -{ - struct swt_iterator_hash2 *ih2; - - ih2 = (struct swt_iterator_hash2 *) swt_iter->private; - if (ih2->ih.private != NULL) - table_hash_iterator_destroy(&ih2->ih); - free(ih2); + return 0; } static void table_hash2_stats(struct sw_table *swt, @@ -424,12 +348,9 @@ struct sw_table *table_hash2_create(unsigned int poly0, unsigned int buckets0, swt->delete = table_hash2_delete; swt->timeout = table_hash2_timeout; swt->destroy = table_hash2_destroy; + swt->iterate = table_hash2_iterate; swt->stats = table_hash2_stats; - swt->iterator = table_hash2_iterator; - swt->iterator_next = table_hash2_next; - swt->iterator_destroy = table_hash2_iterator_destroy; - return swt; out_free_subtable0: diff --git a/switch/table-linear.c b/switch/table-linear.c index e6c6a2ff..e6b8aed6 100644 --- a/switch/table-linear.c +++ b/switch/table-linear.c @@ -44,6 +44,8 @@ struct sw_table_linear { unsigned int max_flows; unsigned int n_flows; struct list flows; + struct list iter_flows; + unsigned long int next_serial; }; static struct sw_flow *table_linear_lookup(struct sw_table *swt, @@ -71,7 +73,9 @@ static int table_linear_insert(struct sw_table *swt, struct sw_flow *flow) if (f->priority == flow->priority && f->key.wildcards == flow->key.wildcards && flow_matches(&f->key, &flow->key)) { + flow->serial = f->serial; list_replace(&flow->node, &f->node); + list_replace(&flow->iter_node, &f->iter_node); flow_free(f); return 1; } @@ -87,7 +91,8 @@ static int table_linear_insert(struct sw_table *swt, struct sw_flow *flow) tl->n_flows++; /* Insert the entry immediately in front of where we're pointing. */ - list_push_back(&f->node, &flow->node); + list_insert(&f->node, &flow->node); + list_push_front(&tl->iter_flows, &flow->iter_node); return 1; } @@ -96,6 +101,7 @@ static void do_delete(struct sw_flow *flow) { list_remove(&flow->node); + list_remove(&flow->iter_node); flow_free(flow); } @@ -126,6 +132,7 @@ static void table_linear_timeout(struct sw_table *swt, struct list *deleted) LIST_FOR_EACH_SAFE (flow, n, struct sw_flow, node, &tl->flows) { if (flow_timeout(flow)) { list_remove(&flow->node); + list_remove(&flow->iter_node); list_push_back(deleted, &flow->node); tl->n_flows--; } @@ -145,43 +152,29 @@ static void table_linear_destroy(struct sw_table *swt) free(tl); } -/* Linear table's private data is just a pointer to the table */ - -static int table_linear_iterator(struct sw_table *swt, - struct swt_iterator *swt_iter) +static int table_linear_iterate(struct sw_table *swt, + const struct sw_flow_key *key, + struct sw_table_position *position, + int (*callback)(struct sw_flow *, void *), + void *private) { struct sw_table_linear *tl = (struct sw_table_linear *) swt; - - swt_iter->private = tl; - - if (!tl->n_flows) - swt_iter->flow = NULL; - else - swt_iter->flow = CONTAINER_OF(list_front(&tl->flows), struct sw_flow, node); - - return 1; -} - -static void table_linear_next(struct swt_iterator *swt_iter) -{ - struct sw_table_linear *tl; - struct list *next; - - if (swt_iter->flow == NULL) - return; - - tl = (struct sw_table_linear *) swt_iter->private; - - next = swt_iter->flow->node.next; - if (next == &tl->flows) - swt_iter->flow = NULL; - else - swt_iter->flow = CONTAINER_OF(next, struct sw_flow, node); + struct sw_flow *flow; + unsigned long start; + + start = ~position->private[0]; + LIST_FOR_EACH (flow, struct sw_flow, iter_node, &tl->iter_flows) { + if (flow->serial <= start && flow_matches(key, &flow->key)) { + int error = callback(flow, private); + if (error) { + position->private[0] = ~(flow->serial - 1); + return error; + } + } + } + return 0; } -static void table_linear_iterator_destroy(struct swt_iterator *swt_iter) -{} - static void table_linear_stats(struct sw_table *swt, struct sw_table_stats *stats) { @@ -207,15 +200,14 @@ struct sw_table *table_linear_create(unsigned int max_flows) swt->delete = table_linear_delete; swt->timeout = table_linear_timeout; swt->destroy = table_linear_destroy; + swt->iterate = table_linear_iterate; swt->stats = table_linear_stats; - swt->iterator = table_linear_iterator; - swt->iterator_next = table_linear_next; - swt->iterator_destroy = table_linear_iterator_destroy; - tl->max_flows = max_flows; tl->n_flows = 0; list_init(&tl->flows); + list_init(&tl->iter_flows); + tl->next_serial = 0; return swt; } diff --git a/switch/table-mac.c b/switch/table-mac.c deleted file mode 100644 index 5d2d0e98..00000000 --- a/switch/table-mac.c +++ /dev/null @@ -1,287 +0,0 @@ -/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford - * Junior University - * - * We are making the OpenFlow specification and associated documentation - * (Software) available for public use and benefit with the expectation - * that others will use, modify and enhance the Software and contribute - * those enhancements back to the community. However, since we would - * like to make the Software available for broadest use, with as few - * restrictions as possible permission is hereby granted, free of - * charge, to any person obtaining a copy of this Software to deal in - * the Software under the copyrights without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * The name and trademarks of copyright holder(s) may NOT be used in - * advertising or publicity pertaining to the Software or any - * derivatives without specific, written prior permission. - */ - -#include "table.h" -#include -#include -#include -#include "crc32.h" -#include "switch-flow.h" -#include "openflow.h" -#include "datapath.h" - -struct sw_table_mac { - struct sw_table swt; - struct crc32 crc32; - unsigned int n_flows; - unsigned int max_flows; - unsigned int bucket_mask; /* Number of buckets minus 1. */ - struct list *buckets; -}; - -static struct list *find_bucket(struct sw_table *swt, - const struct sw_flow_key *key) -{ - struct sw_table_mac *tm = (struct sw_table_mac *) swt; - unsigned int crc = crc32_calculate(&tm->crc32, key, sizeof *key); - return &tm->buckets[crc & tm->bucket_mask]; -} - -static struct sw_flow *table_mac_lookup(struct sw_table *swt, - const struct sw_flow_key *key) -{ - struct list *bucket = find_bucket(swt, key); - struct sw_flow *flow; - LIST_FOR_EACH (flow, struct sw_flow, node, bucket) { - if (!memcmp(key->flow.dl_src, flow->key.flow.dl_src, 6)) { - return flow; - } - } - return NULL; -} - -static int table_mac_insert(struct sw_table *swt, struct sw_flow *flow) -{ - struct sw_table_mac *tm = (struct sw_table_mac *) swt; - struct list *bucket; - struct sw_flow *f; - - /* MAC table only handles flows that match on Ethernet - source address and wildcard everything else. */ - if (flow->key.wildcards != (OFPFW_ALL & ~OFPFW_DL_SRC)) - return 0; - bucket = find_bucket(swt, &flow->key); - - LIST_FOR_EACH (f, struct sw_flow, node, bucket) { - if (!memcmp(f->key.flow.dl_src, flow->key.flow.dl_src, 6)) { - list_replace(&flow->node, &f->node); - flow_free(f); - return 1; - } - } - - /* Table overflow? */ - if (tm->n_flows >= tm->max_flows) { - return 0; - } - tm->n_flows++; - - list_push_front(bucket, &flow->node); - return 1; -} - -static void -do_delete(struct sw_flow *flow) -{ - list_remove(&flow->node); - flow_free(flow); -} - -/* Returns number of deleted flows. */ -static int table_mac_delete(struct sw_table *swt, - const struct sw_flow_key *key, int strict) -{ - struct sw_table_mac *tm = (struct sw_table_mac *) swt; - - if (key->wildcards == (OFPFW_ALL & ~OFPFW_DL_SRC)) { - struct sw_flow *flow = table_mac_lookup(swt, key); - if (flow) { - do_delete(flow); - tm->n_flows--; - return 1; - } - return 0; - } else { - unsigned int i; - int count = 0; - for (i = 0; i <= tm->bucket_mask; i++) { - struct list *bucket = &tm->buckets[i]; - struct sw_flow *flow, *next; - LIST_FOR_EACH_SAFE (flow, next, struct sw_flow, node, bucket) { - if (flow_del_matches(&flow->key, key, strict)) { - do_delete(flow); - count++; - } - } - } - tm->n_flows -= count; - return count; - } -} - -static void table_mac_timeout(struct sw_table *swt, struct list *deleted) -{ - struct sw_table_mac *tm = (struct sw_table_mac *) swt; - unsigned int i; - - for (i = 0; i <= tm->bucket_mask; i++) { - struct list *bucket = &tm->buckets[i]; - struct sw_flow *flow, *next; - LIST_FOR_EACH_SAFE (flow, next, struct sw_flow, node, bucket) { - if (flow_timeout(flow)) { - list_remove(&flow->node); - list_push_back(deleted, &flow->node); - tm->n_flows--; - } - } - } -} - -static void table_mac_destroy(struct sw_table *swt) -{ - struct sw_table_mac *tm = (struct sw_table_mac *) swt; - unsigned int i; - for (i = 0; i <= tm->bucket_mask; i++) { - struct list *list = &tm->buckets[i]; - while (!list_is_empty(list)) { - struct sw_flow *flow = CONTAINER_OF(list_front(list), - struct sw_flow, node); - list_remove(&flow->node); - flow_free(flow); - } - } - free(tm->buckets); - free(tm); -} - -struct swt_iterator_mac { - struct sw_table_mac *tm; - unsigned int bucket_i; -}; - -static struct sw_flow *next_head_flow(struct swt_iterator_mac *im) -{ - for (; im->bucket_i <= im->tm->bucket_mask; im->bucket_i++) { - struct list *bucket = &im->tm->buckets[im->bucket_i]; - if (!list_is_empty(bucket)) { - return CONTAINER_OF(bucket, struct sw_flow, node); - } - } - return NULL; -} - -static int table_mac_iterator(struct sw_table *swt, - struct swt_iterator *swt_iter) -{ - struct swt_iterator_mac *im; - - swt_iter->private = im = malloc(sizeof *im); - if (im == NULL) - return 0; - - im->tm = (struct sw_table_mac *) swt; - - if (!im->tm->n_flows) - swt_iter->flow = NULL; - else { - im->bucket_i = 0; - swt_iter->flow = next_head_flow(im); - } - - return 1; -} - -static void table_mac_next(struct swt_iterator *swt_iter) -{ - struct swt_iterator_mac *im; - struct list *next; - - if (swt_iter->flow == NULL) - return; - - im = (struct swt_iterator_mac *) swt_iter->private; - - next = swt_iter->flow->node.next; - if (next != NULL) { - swt_iter->flow = CONTAINER_OF(next, struct sw_flow, node); - } else { - im->bucket_i++; - swt_iter->flow = next_head_flow(im); - } -} - -static void table_mac_iterator_destroy(struct swt_iterator *swt_iter) -{ - free(swt_iter->private); -} - -static void table_mac_stats(struct sw_table *swt, struct sw_table_stats *stats) -{ - struct sw_table_mac *tm = (struct sw_table_mac *) swt; - stats->name = "mac"; - stats->n_flows = tm->n_flows; - stats->max_flows = tm->max_flows; -} - -struct sw_table *table_mac_create(unsigned int n_buckets, - unsigned int max_flows) -{ - struct sw_table_mac *tm; - struct sw_table *swt; - unsigned int i; - - tm = calloc(1, sizeof *tm); - if (tm == NULL) - return NULL; - - assert(!(n_buckets & (n_buckets - 1))); - - tm->buckets = malloc(n_buckets * sizeof *tm->buckets); - if (tm->buckets == NULL) { - printf("failed to allocate %u buckets\n", n_buckets); - free(tm); - return NULL; - } - for (i = 0; i < n_buckets; i++) { - list_init(&tm->buckets[i]); - } - tm->bucket_mask = n_buckets - 1; - - swt = &tm->swt; - swt->lookup = table_mac_lookup; - swt->insert = table_mac_insert; - swt->delete = table_mac_delete; - swt->timeout = table_mac_timeout; - swt->destroy = table_mac_destroy; - swt->stats = table_mac_stats; - - swt->iterator = table_mac_iterator; - swt->iterator_next = table_mac_next; - swt->iterator_destroy = table_mac_iterator_destroy; - - crc32_init(&tm->crc32, 0x04C11DB7); /* Ethernet CRC. */ - tm->n_flows = 0; - tm->max_flows = max_flows; - - return swt; -} diff --git a/switch/table.h b/switch/table.h index 15743387..c86380a0 100644 --- a/switch/table.h +++ b/switch/table.h @@ -43,12 +43,6 @@ struct sw_flow; struct sw_flow_key; struct list; -/* Iterator through the flows stored in a table. */ -struct swt_iterator { - struct sw_flow *flow; /* Current flow, for use by client. */ - void *private; -}; - /* Table statistics. */ struct sw_table_stats { const char *name; /* Human-readable name. */ @@ -56,6 +50,14 @@ struct sw_table_stats { unsigned long int max_flows; /* Flow capacity. */ }; +/* Position within an iteration of a sw_table. + * + * The contents are private to the table implementation, except that a position + * initialized to all-zero-bits represents the start of a table. */ +struct sw_table_position { + unsigned long private[4]; +}; + /* A single table of flows. */ struct sw_table { /* Searches 'table' for a flow matching 'key', which must not have any @@ -87,16 +89,28 @@ struct sw_table { /* Destroys 'table', which must not have any users. */ void (*destroy)(struct sw_table *table); - int (*iterator)(struct sw_table *, struct swt_iterator *); - void (*iterator_next)(struct swt_iterator *); - void (*iterator_destroy)(struct swt_iterator *); + /* Iterates through the flow entries in 'table', passing each one + * matches 'key' to 'callback'. The callback function should return 0 + * to continue iteration or a nonzero error code to stop. The iterator + * function returns either 0 if the table iteration completed or the + * value returned by the callback function otherwise. + * + * The iteration starts at 'position', which may be initialized to + * all-zero-bits to iterate from the beginning of the table. If the + * iteration terminates due to an error from the callback function, + * 'position' is updated to a value that can be passed back to the + * iterator function to resume iteration later with the following + * flow. */ + int (*iterate)(struct sw_table *table, + const struct sw_flow_key *key, + struct sw_table_position *position, + int (*callback)(struct sw_flow *flow, void *private), + void *private); /* Dumps statistics for 'table' into 'stats'. */ void (*stats)(struct sw_table *table, struct sw_table_stats *stats); }; -struct sw_table *table_mac_create(unsigned int n_buckets, - unsigned int max_flows); struct sw_table *table_hash_create(unsigned int polynomial, unsigned int n_buckets); struct sw_table *table_hash2_create(unsigned int poly0, unsigned int buckets0, diff --git a/utilities/dpctl.c b/utilities/dpctl.c index f6d851d3..a66975a0 100644 --- a/utilities/dpctl.c +++ b/utilities/dpctl.c @@ -344,6 +344,17 @@ alloc_openflow_buffer(size_t openflow_len, uint8_t type, return oh; } +static void * +alloc_stats_request(size_t body_len, uint16_t type, struct buffer **bufferp) +{ + struct ofp_stats_request *rq; + rq = alloc_openflow_buffer((offsetof(struct ofp_stats_request, body) + + body_len), OFPT_STATS_REQUEST, bufferp); + rq->type = htons(type); + rq->flags = htons(0); + return rq->body; +} + static void send_openflow_buffer(struct vconn *vconn, struct buffer *buffer) { @@ -378,30 +389,76 @@ transact_openflow(struct vconn *vconn, struct buffer *request) } static void -dump_transaction(const char *vconn_name, uint8_t request_type) +dump_transaction(const char *vconn_name, struct buffer *request) { struct vconn *vconn; - struct buffer *request, *reply; + struct buffer *reply; run(vconn_open_block(vconn_name, &vconn), "connecting to %s", vconn_name); - alloc_openflow_buffer(sizeof(struct ofp_header), request_type, &request); reply = transact_openflow(vconn, request); ofp_print(stdout, reply->data, reply->size, 1); vconn_close(vconn); } +static void +dump_trivial_transaction(const char *vconn_name, uint8_t request_type) +{ + struct buffer *request; + alloc_openflow_buffer(sizeof(struct ofp_header), request_type, &request); + dump_transaction(vconn_name, request); +} + +static void +dump_stats_transaction(const char *vconn_name, struct buffer *request) +{ + uint32_t send_xid = ((struct ofp_header *) request->data)->xid; + struct vconn *vconn; + bool done = false; + + run(vconn_open_block(vconn_name, &vconn), "connecting to %s", vconn_name); + send_openflow_buffer(vconn, request); + while (!done) { + uint32_t recv_xid; + struct buffer *reply; + + run(vconn_recv_block(vconn, &reply), "OpenFlow packet receive failed"); + recv_xid = ((struct ofp_header *) reply->data)->xid; + if (send_xid == recv_xid) { + struct ofp_stats_reply *osr; + + ofp_print(stdout, reply->data, reply->size, 1); + + osr = buffer_at(reply, 0, sizeof *osr); + done = !osr || !(ntohs(osr->flags) & OFPSF_REPLY_MORE); + } else { + VLOG_DBG("received reply with xid %08"PRIx32" " + "!= expected %08"PRIx32, recv_xid, send_xid); + } + buffer_delete(reply); + } + vconn_close(vconn); +} + +static void +dump_trivial_stats_transaction(const char *vconn_name, uint8_t stats_type) +{ + struct buffer *request; + alloc_stats_request(0, stats_type, &request); + dump_stats_transaction(vconn_name, request); +} + static void do_show(int argc UNUSED, char *argv[]) { - dump_transaction(argv[1], OFPT_FEATURES_REQUEST); - dump_transaction(argv[1], OFPT_GET_CONFIG_REQUEST); + dump_trivial_transaction(argv[1], OFPT_FEATURES_REQUEST); + dump_trivial_transaction(argv[1], OFPT_GET_CONFIG_REQUEST); } static void do_dump_tables(int argc, char *argv[]) { - dump_transaction(argv[1], OFPT_TABLE_STATS_REQUEST); + dump_trivial_stats_transaction(argv[1], OFPST_TABLE); } @@ -561,46 +618,16 @@ str_to_flow(char *string, struct ofp_match *match, struct ofp_action *action, static void do_dump_flows(int argc, char *argv[]) { - struct vconn *vconn; - struct buffer *request; struct ofp_flow_stats_request *req; - uint32_t send_xid; - bool done = false; + struct buffer *request; - run(vconn_open_block(argv[1], &vconn), "connecting to %s", argv[1]); - req = alloc_openflow_buffer(sizeof *req, OFPT_FLOW_STATS_REQUEST, - &request); + req = alloc_stats_request(sizeof *req, OFPST_FLOW, &request); str_to_flow(argc > 2 ? argv[2] : "", &req->match, NULL, &req->table_id, NULL); req->type = OFPFS_INDIV; req->pad = 0; - send_xid = ((struct ofp_header *) request->data)->xid; - send_openflow_buffer(vconn, request); - while (!done) { - uint32_t recv_xid; - struct buffer *reply; - - run(vconn_recv_block(vconn, &reply), "OpenFlow packet receive failed"); - recv_xid = ((struct ofp_header *) reply->data)->xid; - if (send_xid == recv_xid) { - struct ofp_flow_stats_reply *rpy; - - ofp_print(stdout, reply->data, reply->size, 1); - - rpy = buffer_at(reply, 0, sizeof *rpy); - done = (!rpy - || rpy->header.version != OFP_VERSION - || rpy->header.type != OFPT_FLOW_STATS_REPLY - || (ntohs(rpy->header.length) - < sizeof rpy->header + sizeof *rpy->flows)); - } else { - VLOG_DBG("received reply with xid %08"PRIx32" " - "!= expected %08"PRIx32, recv_xid, send_xid); - } - buffer_delete(reply); - } - vconn_close(vconn); + dump_stats_transaction(argv[1], request); } static void do_add_flows(int argc, char *argv[]) @@ -679,7 +706,7 @@ static void do_del_flows(int argc, char *argv[]) static void do_dump_ports(int argc, char *argv[]) { - dump_transaction(argv[1], OFPT_PORT_STATS_REQUEST); + dump_trivial_stats_transaction(argv[1], OFPST_PORT); } static void do_help(int argc UNUSED, char *argv[] UNUSED)