return send_openflow_skb(skb, sender);
}
-static void
+static int
fill_flow_stats(struct ofp_flow_stats *ofs, struct sw_flow *flow,
int table_idx)
{
+ int length = sizeof *ofs + sizeof *ofs->actions * flow->n_actions;
+ ofs->length = htons(length);
+ ofs->table_id = table_idx;
+ ofs->pad = 0;
ofs->match.wildcards = htons(flow->key.wildcards);
ofs->match.in_port = flow->key.in_port;
memcpy(ofs->match.dl_src, flow->key.dl_src, ETH_ALEN);
ofs->byte_count = cpu_to_be64(flow->byte_count);
ofs->priority = htons(flow->priority);
ofs->max_idle = htons(flow->max_idle);
- ofs->table_id = table_idx;
- memset(ofs->pad, 0, sizeof ofs->pad);
+ memcpy(ofs->actions, flow->actions,
+ sizeof *ofs->actions * flow->n_actions);
+ return length;
}
/* Generic Netlink interface.
struct sw_table_position position;
const struct ofp_flow_stats_request *rq;
- struct ofp_flow_stats *flows;
- int n_flows, max_flows;
+ void *body;
+ int bytes_used, bytes_allocated;
};
+#define MAX_FLOW_STATS_SIZE (sizeof(struct ofp_flow_stats) \
+ + MAX_ACTIONS * sizeof(struct ofp_action))
+
static int flow_stats_init(struct datapath *dp, const void *body, int body_len,
void **state)
{
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;
+ s->bytes_used += fill_flow_stats(s->body + s->bytes_used, flow,
+ s->table_idx);
+ return s->bytes_used + MAX_FLOW_STATS_SIZE > s->bytes_allocated;
}
static int flow_stats_dump(struct datapath *dp, void *state,
void *body, int *body_len)
{
struct flow_stats_state *s = state;
- struct ofp_flow_stats *ofs;
struct sw_flow_key match_key;
- s->max_flows = *body_len / sizeof *ofs;
- if (!s->max_flows)
+ s->bytes_used = 0;
+ s->bytes_allocated = *body_len;
+ if (s->bytes_allocated < MAX_FLOW_STATS_SIZE)
return -ENOMEM;
- s->flows = body;
+ s->body = body;
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))
{
s->table_idx++;
memset(&s->position, 0, sizeof s->position);
}
- *body_len = sizeof *ofs * s->n_flows;
- return s->n_flows >= s->max_flows;
+ *body_len = s->bytes_used;
+ return s->bytes_used + MAX_FLOW_STATS_SIZE > s->bytes_allocated;
}
static void flow_stats_done(void *state)
/* Body of reply to OFPST_FLOW request. */
struct ofp_flow_stats {
+ uint16_t length; /* Length of this entry */
+ uint8_t table_id; /* ID of table flow came from. 0nly used for
+ non-aggregated results */
+ uint8_t pad;
struct ofp_match match; /* Description of fields */
- uint32_t duration; /* Time flow has been alive in seconds. Only
+ 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
+ uint16_t priority; /* Priority of the entry. Only meaningful
when this is not an exact-match entry. */
uint16_t max_idle; /* Only used for non-aggregated results. */
- uint8_t table_id; /* ID of table flow came from. */
- uint8_t pad[5]; /* Align to 64-bits. */
+ struct ofp_action actions[0]; /* Only used for non-aggregated results. */
};
/* Body of reply to OFPST_TABLE request. */
{
uint16_t w = ntohs(om->wildcards);
- print_wild(f, "inport", w & OFPFW_IN_PORT, "%d", ntohs(om->in_port));
+ print_wild(f, " inport", w & OFPFW_IN_PORT, "%d", ntohs(om->in_port));
print_wild(f, ":vlan", w & OFPFW_DL_VLAN, "%04x", ntohs(om->dl_vlan));
print_wild(f, " mac[", w & OFPFW_DL_SRC,
ETH_ADDR_FMT, ETH_ADDR_ARGS(om->dl_src));
print_wild(f, "] proto", w & OFPFW_NW_PROTO, "%u", om->nw_proto);
print_wild(f, " tport[", w & OFPFW_TP_SRC, "%d", ntohs(om->tp_src));
print_wild(f, "->", w & OFPFW_TP_DST, "%d", ntohs(om->tp_dst));
- ds_put_cstr(f, "]\n");
+ ds_put_cstr(f, "]");
}
/* Pretty-print the OFPT_FLOW_MOD packet of 'len' bytes at 'oh' to 'string'
}
static void
-ofp_flow_stats_reply(struct ds *string, const void *body, size_t len,
+ofp_flow_stats_reply(struct ds *string, const void *body_, size_t len,
int verbosity)
{
- 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;
- }
+ const char *body = body_;
+ const char *pos = body;
+ for (;;) {
+ const struct ofp_flow_stats *fs;
+ ptrdiff_t bytes_left = body + len - pos;
+ size_t length;
+
+ if (bytes_left < sizeof *fs) {
+ if (bytes_left != 0) {
+ ds_put_format(string, " ***%td leftover bytes at end***",
+ bytes_left);
+ }
+ break;
+ }
+
+ fs = (const void *) pos;
+ length = ntohs(fs->length);
+ if (length < sizeof *fs) {
+ ds_put_format(string, " ***length=%zu shorter than minimum %zu***",
+ length, sizeof *fs);
+ break;
+ } else if (length > bytes_left) {
+ ds_put_format(string,
+ " ***length=%zu but only %td bytes left***",
+ length, bytes_left);
+ break;
+ } else if ((length - sizeof *fs) % sizeof fs->actions[0]) {
+ ds_put_format(string,
+ " ***length=%zu has %zu bytes leftover in "
+ "final action***",
+ length,
+ (length - sizeof *fs) % sizeof fs->actions[0]);
+ break;
+ }
- 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", ",
ds_put_format(string, "n_bytes=%"PRIu64", ", ntohll(fs->byte_count));
ds_put_format(string, "max_idle=%"PRIu16", ", ntohs(fs->max_idle));
ofp_print_match(string, &fs->match);
+ ofp_print_actions(string, fs->actions, length - sizeof *fs);
+ ds_put_char(string, '\n');
+
+ pos += length;
}
}
}
static void
-fill_flow_stats(struct ofp_flow_stats *ofs, struct sw_flow *flow,
+fill_flow_stats(struct buffer *buffer, struct sw_flow *flow,
int table_idx, time_t now)
{
+ struct ofp_flow_stats *ofs;
+ int length = sizeof *ofs + sizeof *ofs->actions * flow->n_actions;
+ ofs = buffer_put_uninit(buffer, length);
+ ofs->length = htons(length);
+ ofs->table_id = table_idx;
+ ofs->pad = 0;
ofs->match.wildcards = htons(flow->key.wildcards);
ofs->match.in_port = flow->key.flow.in_port;
memcpy(ofs->match.dl_src, flow->key.flow.dl_src, ETH_ADDR_LEN);
ofs->byte_count = htonll(flow->byte_count);
ofs->priority = htons(flow->priority);
ofs->max_idle = htons(flow->max_idle);
- ofs->table_id = table_idx;
- memset(ofs->pad, 0, sizeof ofs->pad);
+ memcpy(ofs->actions, flow->actions,
+ sizeof *ofs->actions * flow->n_actions);
}
\f
time_t now;
struct buffer *buffer;
- int n_flows, max_flows;
};
+#define MAX_FLOW_STATS_BYTES 4096
+
static int flow_stats_init(struct datapath *dp, const void *body, int body_len,
void **state)
{
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;
+ fill_flow_stats(s->buffer, flow, s->table_idx, s->now);
+ return s->buffer->size >= MAX_FLOW_STATS_BYTES;
}
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))
s->table_idx++;
memset(&s->position, 0, sizeof s->position);
}
- return s->n_flows >= s->max_flows;
+ return s->buffer->size >= MAX_FLOW_STATS_BYTES;
}
static void flow_stats_done(void *state)