#include "poll-loop.h"
#include "rconn.h"
#include "shash.h"
-#include "status.h"
#include "stream-ssl.h"
#include "svec.h"
#include "tag.h"
COVERAGE_DEFINE(ofproto_uninstallable);
COVERAGE_DEFINE(ofproto_update_port);
-#include "sflow_api.h"
+/* Maximum depth of flow table recursion (due to NXAST_RESUBMIT actions) in a
+ * flow translation. */
+#define MAX_RESUBMIT_RECURSION 16
struct rule;
*
* This is normally null so the client has to set it manually after
* calling action_xlate_ctx_init(). */
- void (*resubmit_hook)(struct action_xlate_ctx *, const struct rule *);
+ void (*resubmit_hook)(struct action_xlate_ctx *, struct rule *);
/* If true, the speciality of 'flow' should be checked before executing
* its actions. If special_cb returns false on 'flow' rendered
uint64_t dp_packet_count; /* Last known packet count in the datapath. */
uint64_t dp_byte_count; /* Last known byte count in the datapath. */
+ uint64_t rs_packet_count; /* Packets pushed to resubmit children. */
+ uint64_t rs_byte_count; /* Bytes pushed to resubmit children. */
+ long long int rs_used; /* Used time pushed to resubmit children. */
+
/* Number of bytes passed to account_cb. This may include bytes that can
* currently obtained from the datapath (thus, it can be greater than
* byte_count). */
const struct ofpbuf *packet);
static void facet_update_stats(struct ofproto *, struct facet *,
const struct dpif_flow_stats *);
+static void facet_push_stats(struct ofproto *, struct facet *);
/* ofproto supports two kinds of OpenFlow connections:
*
enum nx_role role; /* Role. */
struct hmap_node hmap_node; /* In struct ofproto's "controllers" map. */
struct discovery *discovery; /* Controller discovery object, if enabled. */
- struct status_category *ss; /* Switch status category. */
enum ofproto_band band; /* In-band or out-of-band? */
};
uint32_t max_ports;
/* Configuration. */
- struct switch_status *switch_status;
struct fail_open *fail_open;
struct netflow *netflow;
struct ofproto_sflow *sflow;
static uint64_t pick_fallback_dpid(void);
static int ofproto_expire(struct ofproto *);
+static void flow_push_stats(struct ofproto *, const struct rule *,
+ struct flow *, uint64_t packets, uint64_t bytes,
+ long long int used);
static void handle_upcall(struct ofproto *, struct dpif_upcall *);
p->max_ports = dpif_get_max_ports(dpif);
/* Initialize submodules. */
- p->switch_status = switch_status_create(p);
p->fail_open = NULL;
p->netflow = NULL;
p->sflow = NULL;
if (is_discovery_controller(c)) {
int error = discovery_create(c->accept_re, c->update_resolv_conf,
- ofproto->dpif, ofproto->switch_status,
- &discovery);
+ ofproto->dpif, &discovery);
if (error) {
return;
}
* even before we know any remote addresses. */
if (n_addrs || discovery) {
if (!ofproto->in_band) {
- in_band_create(ofproto, ofproto->dpif, ofproto->switch_status,
- &ofproto->in_band);
+ in_band_create(ofproto, ofproto->dpif, &ofproto->in_band);
}
if (ofproto->in_band) {
in_band_set_remotes(ofproto->in_band, addrs, n_addrs);
size_t n;
if (!p->fail_open) {
- p->fail_open = fail_open_create(p, p->switch_status);
+ p->fail_open = fail_open_create(p);
}
n = 0;
struct shash new_controllers;
struct ofconn *ofconn, *next_ofconn;
struct ofservice *ofservice, *next_ofservice;
- bool ss_exists;
size_t i;
/* Create newly configured controllers and services.
/* Delete controllers that are no longer configured.
* Update configuration of all now-existing controllers. */
- ss_exists = false;
HMAP_FOR_EACH_SAFE (ofconn, next_ofconn, hmap_node, &p->controllers) {
struct ofproto_controller *c;
ofconn_destroy(ofconn);
} else {
update_controller(ofconn, c);
- if (ofconn->ss) {
- ss_exists = true;
- }
}
}
update_in_band_remotes(p);
update_fail_open(p);
-
- if (!hmap_is_empty(&p->controllers) && !ss_exists) {
- ofconn = CONTAINER_OF(hmap_first(&p->controllers),
- struct ofconn, hmap_node);
- ofconn->ss = switch_status_register(p->switch_status, "remote",
- rconn_status_cb, ofconn->rconn);
- }
}
void
}
shash_destroy(&p->port_by_name);
- switch_status_destroy(p->switch_status);
netflow_destroy(p->netflow);
ofproto_sflow_destroy(p->sflow);
}
void
-ofproto_get_ofproto_controller_info(const struct ofproto * ofproto,
+ofproto_get_ofproto_controller_info(const struct ofproto *ofproto,
struct shash *info)
{
const struct ofconn *ofconn;
HMAP_FOR_EACH (ofconn, hmap_node, &ofproto->controllers) {
const struct rconn *rconn = ofconn->rconn;
+ time_t now = time_now();
+ time_t last_connection = rconn_get_last_connection(rconn);
+ time_t last_disconnect = rconn_get_last_disconnect(rconn);
const int last_error = rconn_get_last_error(rconn);
struct ofproto_controller_info *cinfo = xmalloc(sizeof *cinfo);
cinfo->pairs.values[cinfo->pairs.n++] =
xstrdup(rconn_get_state(rconn));
- cinfo->pairs.keys[cinfo->pairs.n] = "time_in_state";
- cinfo->pairs.values[cinfo->pairs.n++] =
- xasprintf("%u", rconn_get_state_elapsed(rconn));
+ if (last_connection != TIME_MIN) {
+ cinfo->pairs.keys[cinfo->pairs.n] = "sec_since_connect";
+ cinfo->pairs.values[cinfo->pairs.n++]
+ = xasprintf("%ld", (long int) (now - last_connection));
+ }
+
+ if (last_disconnect != TIME_MIN) {
+ cinfo->pairs.keys[cinfo->pairs.n] = "sec_since_disconnect";
+ cinfo->pairs.values[cinfo->pairs.n++]
+ = xasprintf("%ld", (long int) (now - last_disconnect));
+ }
}
}
* individually since we are about to blow away all the facets with
* dpif_flow_flush(). */
facet->installed = false;
+ facet->dp_packet_count = 0;
+ facet->dp_byte_count = 0;
facet_remove(ofproto, facet);
}
discovery_destroy(ofconn->discovery);
list_remove(&ofconn->node);
- switch_status_unregister(ofconn->ss);
rconn_destroy(ofconn->rconn);
rconn_packet_counter_destroy(ofconn->packet_in_counter);
rconn_packet_counter_destroy(ofconn->reply_counter);
char *ofconn_name = ofconn_make_name(p, controller_name);
rconn_connect(ofconn->rconn, controller_name, ofconn_name);
free(ofconn_name);
+ free(controller_name);
} else {
rconn_disconnect(ofconn->rconn);
}
if (rate > 0) {
if (!*s) {
- *s = pinsched_create(rate, burst,
- ofconn->ofproto->switch_status);
+ *s = pinsched_create(rate, burst);
} else {
pinsched_set_limits(*s, rate, burst);
}
assert(ofpbuf_headroom(packet) >= sizeof(struct ofp_packet_in));
flow_extract_stats(&facet->flow, packet, &stats);
+ stats.used = time_msec();
if (execute_odp_actions(ofproto, &facet->flow,
facet->actions, facet->actions_len, packet)) {
facet_update_stats(ofproto, facet, &stats);
- facet->used = time_msec();
- netflow_flow_update_time(ofproto->netflow,
- &facet->nf_flow, facet->used);
}
}
rule->used = time_msec();
rule->packet_count++;
rule->byte_count += size;
+ flow_push_stats(ofproto, rule, &flow, 1, size, rule->used);
}
ofpbuf_delete(odp_actions);
}
facet->installed = false;
facet->dp_packet_count = 0;
facet->dp_byte_count = 0;
+ } else {
+ assert(facet->dp_packet_count == 0);
+ assert(facet->dp_byte_count == 0);
}
}
assert(!facet->dp_byte_count);
assert(!facet->dp_packet_count);
+ facet_push_stats(ofproto, facet);
facet_account(ofproto, facet, 0);
if (ofproto->netflow && !facet_is_controller_flow(facet)) {
* reinstalled. */
facet->packet_count = 0;
facet->byte_count = 0;
+ facet->rs_packet_count = 0;
+ facet->rs_byte_count = 0;
facet->accounted_bytes = 0;
netflow_flow_clear(&facet->nf_flow);
list_push_back(&new_rule->facets, &facet->list_node);
facet->rule = new_rule;
facet->used = new_rule->created;
+ facet->rs_used = facet->used;
}
ofpbuf_delete(odp_actions);
return 0;
}
-/* Maximum depth of flow table recursion (due to NXAST_RESUBMIT actions) in a
- * flow translation. */
-#define MAX_RESUBMIT_RECURSION 16
-
static void do_xlate_actions(const union ofp_action *in, size_t n_in,
struct action_xlate_ctx *ctx);
static bool
is_valid_table(uint8_t table_id)
{
- return table_id == 0 || table_id == 0xff;
+ if (table_id == 0 || table_id == 0xff) {
+ return true;
+ } else {
+ /* It would probably be better to reply with an error but there doesn't
+ * seem to be any appropriate value, so that might just be
+ * confusing. */
+ VLOG_WARN_RL(&rl, "controller asked for invalid table %"PRIu8,
+ table_id);
+ return false;
+ }
}
static int
}
/* Adds a pretty-printed description of all flows to 'results', including
- * those marked hidden by secchan (e.g., by in-band control). */
+ * hidden flows (e.g., set up by in-band control). */
void
ofproto_get_all_flows(struct ofproto *p, struct ds *results)
{
return 0;
}
+/* Updates 'facet''s used time. Caller is responsible for calling
+ * facet_push_stats() to update the flows which 'facet' resubmits into. */
static void
facet_update_time(struct ofproto *ofproto, struct facet *facet,
long long int used)
facet_update_stats(struct ofproto *ofproto, struct facet *facet,
const struct dpif_flow_stats *stats)
{
- if (stats->n_packets) {
+ if (stats->n_packets || stats->used > facet->used) {
facet_update_time(ofproto, facet, stats->used);
facet->packet_count += stats->n_packets;
facet->byte_count += stats->n_bytes;
+ facet_push_stats(ofproto, facet);
netflow_flow_update_flags(&facet->nf_flow, stats->tcp_flags);
}
}
+static void
+facet_push_stats(struct ofproto *ofproto, struct facet *facet)
+{
+ uint64_t rs_packets, rs_bytes;
+
+ assert(facet->packet_count >= facet->rs_packet_count);
+ assert(facet->byte_count >= facet->rs_byte_count);
+ assert(facet->used >= facet->rs_used);
+
+ rs_packets = facet->packet_count - facet->rs_packet_count;
+ rs_bytes = facet->byte_count - facet->rs_byte_count;
+
+ if (rs_packets || rs_bytes || facet->used > facet->rs_used) {
+ facet->rs_packet_count = facet->packet_count;
+ facet->rs_byte_count = facet->byte_count;
+ facet->rs_used = facet->used;
+
+ flow_push_stats(ofproto, facet->rule, &facet->flow,
+ rs_packets, rs_bytes, facet->used);
+ }
+}
+
+struct ofproto_push {
+ struct action_xlate_ctx ctx;
+ uint64_t packets;
+ uint64_t bytes;
+ long long int used;
+};
+
+static void
+push_resubmit(struct action_xlate_ctx *ctx, struct rule *rule)
+{
+ struct ofproto_push *push = CONTAINER_OF(ctx, struct ofproto_push, ctx);
+
+ if (rule) {
+ rule->packet_count += push->packets;
+ rule->byte_count += push->bytes;
+ rule->used = MAX(push->used, rule->used);
+ }
+}
+
+/* Pushes flow statistics to the rules which 'flow' resubmits into given
+ * 'rule''s actions. */
+static void
+flow_push_stats(struct ofproto *ofproto, const struct rule *rule,
+ struct flow *flow, uint64_t packets, uint64_t bytes,
+ long long int used)
+{
+ struct ofproto_push push;
+
+ push.packets = packets;
+ push.bytes = bytes;
+ push.used = used;
+
+ action_xlate_ctx_init(&push.ctx, ofproto, flow, NULL);
+ push.ctx.resubmit_hook = push_resubmit;
+ ofpbuf_delete(xlate_actions(&push.ctx, rule->actions, rule->n_actions));
+}
+
/* Implements OFPFC_ADD and the cases for OFPFC_MODIFY and OFPFC_MODIFY_STRICT
* in which no matching flow already exists in the flow table.
*
return 0;
/* Nicira extension requests. */
- case OFPUTIL_NXT_STATUS_REQUEST:
- return switch_status_handle_request(
- ofconn->ofproto->switch_status, ofconn->rconn, oh);
-
case OFPUTIL_NXT_TUN_ID_FROM_COOKIE:
return handle_tun_id_from_cookie(ofconn, oh);
case OFPUTIL_OFPST_PORT_REPLY:
case OFPUTIL_OFPST_TABLE_REPLY:
case OFPUTIL_OFPST_AGGREGATE_REPLY:
- case OFPUTIL_NXT_STATUS_REPLY:
case OFPUTIL_NXT_ROLE_REPLY:
case OFPUTIL_NXT_FLOW_REMOVED:
case OFPUTIL_NXST_FLOW_REPLY:
}
/* Update 'packet_count', 'byte_count', and 'used' members of installed facets.
+ *
+ * This function also pushes statistics updates to rules which each facet
+ * resubmits into. Generally these statistics will be accurate. However, if a
+ * facet changes the rule it resubmits into at some time in between
+ * ofproto_update_stats() runs, it is possible that statistics accrued to the
+ * old rule will be incorrectly attributed to the new rule. This could be
+ * avoided by calling ofproto_update_stats() whenever rules are created or
+ * deleted. However, the performance impact of making so many calls to the
+ * datapath do not justify the benefit of having perfectly accurate statistics.
*/
static void
ofproto_update_stats(struct ofproto *p)
facet_update_time(p, facet, stats->used);
facet_account(p, facet, stats->n_bytes);
+ facet_push_stats(p, facet);
} else {
/* There's a flow in the datapath that we know nothing about.
* Delete it. */
* OFPT_PACKET_IN message to each OpenFlow controller as necessary according to
* their individual configurations.
*
- * Takes ownership of 'packet'. */
+ * If 'clone' is true, the caller retains ownership of 'upcall->packet'.
+ * Otherwise, ownership is transferred to this function. */
static void
send_packet_in(struct ofproto *ofproto, struct dpif_upcall *upcall,
const struct flow *flow, bool clone)
}
static void
-trace_resubmit(struct action_xlate_ctx *ctx, const struct rule *rule)
+trace_resubmit(struct action_xlate_ctx *ctx, struct rule *rule)
{
struct ofproto_trace *trace = CONTAINER_OF(ctx, struct ofproto_trace, ctx);
struct ds *result = trace->result;