#include "coverage.h"
#include "discovery.h"
#include "dpif.h"
+#include "dynamic-string.h"
#include "executer.h"
#include "fail-open.h"
#include "in-band.h"
#include "svec.h"
#include "tag.h"
#include "timeval.h"
+#include "unixctl.h"
#include "vconn.h"
#include "vconn-ssl.h"
#include "xtoxll.h"
const flow_t *flow, struct ofproto *ofproto,
const struct ofpbuf *packet,
struct odp_actions *out, tag_type *tags,
- bool *may_setup_flow);
+ bool *may_set_up_flow, uint16_t *nf_output_iface);
struct rule {
struct cls_rule cr;
uint64_t packet_count; /* Number of packets received. */
uint64_t byte_count; /* Number of bytes received. */
uint64_t accounted_bytes; /* Number of bytes passed to account_cb. */
- uint8_t tcp_flags; /* Bitwise-OR of all TCP flags seen. */
- uint8_t ip_tos; /* Last-seen IP type-of-service. */
tag_type tags; /* Tags (set only by hooks). */
+ struct netflow_flow nf_flow; /* Per-flow NetFlow tracking data. */
/* If 'super' is non-NULL, this rule is a subrule, that is, it is an
* exact-match rule (having cr.wc.wildcards of 0) generated from the
return false;
}
-static struct rule *rule_create(struct rule *super, const union ofp_action *,
- size_t n_actions, uint16_t idle_timeout,
- uint16_t hard_timeout);
+static struct rule *rule_create(struct ofproto *, struct rule *super,
+ const union ofp_action *, size_t n_actions,
+ uint16_t idle_timeout, uint16_t hard_timeout);
static void rule_free(struct rule *);
static void rule_destroy(struct ofproto *, struct rule *);
static struct rule *rule_from_cls_rule(const struct cls_rule *);
static const struct ofhooks default_ofhooks;
-static uint64_t pick_datapath_id(struct dpif *, uint64_t fallback_dpid);
+static uint64_t pick_datapath_id(const struct ofproto *);
static uint64_t pick_fallback_dpid(void);
static void send_packet_in_miss(struct ofpbuf *, void *ofproto);
static void send_packet_in_action(struct ofpbuf *, void *ofproto);
static void update_used(struct ofproto *);
-static void update_stats(struct rule *, const struct odp_flow_stats *);
+static void update_stats(struct ofproto *, struct rule *,
+ const struct odp_flow_stats *);
static void expire_rule(struct cls_rule *, void *ofproto);
+static void active_timeout(struct ofproto *ofproto, struct rule *rule);
static bool revalidate_rule(struct ofproto *p, struct rule *rule);
static void revalidate_cb(struct cls_rule *rule_, void *p_);
ofproto_create(const char *datapath, const struct ofhooks *ofhooks, void *aux,
struct ofproto **ofprotop)
{
- struct netdev_monitor *netdev_monitor;
struct odp_stats stats;
struct ofproto *p;
struct dpif *dpif;
dpif_flow_flush(dpif);
dpif_recv_purge(dpif);
- /* Arrange to monitor datapath ports for status changes. */
- error = netdev_monitor_create(&netdev_monitor);
- if (error) {
- VLOG_ERR("failed to starting monitoring datapath %s: %s",
- datapath, strerror(error));
- dpif_close(dpif);
- return error;
- }
-
/* Initialize settings. */
p = xcalloc(1, sizeof *p);
p->fallback_dpid = pick_fallback_dpid();
- p->datapath_id = pick_datapath_id(dpif, p->fallback_dpid);
- VLOG_INFO("using datapath ID %012"PRIx64, p->datapath_id);
+ p->datapath_id = p->fallback_dpid;
p->manufacturer = xstrdup("Nicira Networks, Inc.");
p->hardware = xstrdup("Reference Implementation");
p->software = xstrdup(VERSION BUILDNR);
/* Initialize datapath. */
p->dpif = dpif;
- p->netdev_monitor = netdev_monitor;
+ p->netdev_monitor = netdev_monitor_create();
port_array_init(&p->ports);
shash_init(&p->port_by_name);
p->max_ports = stats.max_ports;
/* Initialize OpenFlow connections. */
list_init(&p->all_conns);
- p->controller = ofconn_create(p, rconn_create(15, 15));
+ p->controller = ofconn_create(p, rconn_create(5, 8));
p->controller->pktbuf = pktbuf_create();
p->controller->miss_send_len = OFP_DEFAULT_MISS_SEND_LEN;
p->listeners = NULL;
return error;
}
+ /* Pick final datapath ID. */
+ p->datapath_id = pick_datapath_id(p);
+ VLOG_INFO("using datapath ID %012"PRIx64, p->datapath_id);
+
*ofprotop = p;
return 0;
}
ofproto_set_datapath_id(struct ofproto *p, uint64_t datapath_id)
{
uint64_t old_dpid = p->datapath_id;
- p->datapath_id = (datapath_id
- ? datapath_id
- : pick_datapath_id(p->dpif, p->fallback_dpid));
+ p->datapath_id = datapath_id ? datapath_id : pick_datapath_id(p);
if (p->datapath_id != old_dpid) {
VLOG_INFO("datapath ID changed to %012"PRIx64, p->datapath_id);
rconn_reconnect(p->controller->rconn);
}
int
-ofproto_set_netflow(struct ofproto *ofproto, const struct svec *collectors,
- uint8_t engine_type, uint8_t engine_id, bool add_id_to_iface)
+ofproto_set_netflow(struct ofproto *ofproto,
+ const struct netflow_options *nf_options)
{
- if (collectors && collectors->n) {
+ if (nf_options->collectors.n) {
if (!ofproto->netflow) {
ofproto->netflow = netflow_create();
}
- netflow_set_engine(ofproto->netflow, engine_type, engine_id,
- add_id_to_iface);
- return netflow_set_collectors(ofproto->netflow, collectors);
+ return netflow_set_options(ofproto->netflow, nf_options);
} else {
netflow_destroy(ofproto->netflow);
ofproto->netflow = NULL;
return ofproto->datapath_id;
}
+uint64_t
+ofproto_get_mgmt_id(const struct ofproto *ofproto)
+{
+ return ofproto->mgmt_id;
+}
+
int
ofproto_get_probe_interval(const struct ofproto *ofproto)
{
}
}
}
- if (p->fail_open) {
- fail_open_run(p->fail_open);
- }
pinsched_run(p->miss_sched, send_packet_in_miss, p);
pinsched_run(p->action_sched, send_packet_in_action, p);
if (p->executer) {
ofconn_run(ofconn, p);
}
+ /* Fail-open maintenance. Do this after processing the ofconns since
+ * fail-open checks the status of the controller rconn. */
+ if (p->fail_open) {
+ fail_open_run(p->fail_open);
+ }
+
for (i = 0; i < p->n_listeners; i++) {
struct vconn *vconn;
int retval;
int error;
error = xlate_actions(actions, n_actions, flow, p, packet, &odp_actions,
- NULL, NULL);
+ NULL, NULL, NULL);
if (error) {
return error;
}
int idle_timeout)
{
struct rule *rule;
- rule = rule_create(NULL, actions, n_actions,
+ rule = rule_create(p, NULL, actions, n_actions,
idle_timeout >= 0 ? idle_timeout : 5 /* XXX */, 0);
cls_rule_from_flow(&rule->cr, flow, wildcards, priority);
rule_insert(p, rule, NULL, 0);
ofport = xmalloc(sizeof *ofport);
ofport->netdev = netdev;
ofport->opp.port_no = odp_port_to_ofp_port(odp_port->port);
- memcpy(ofport->opp.hw_addr, netdev_get_etheraddr(netdev), ETH_ALEN);
+ netdev_get_etheraddr(netdev, ofport->opp.hw_addr);
memcpy(ofport->opp.name, odp_port->devname,
MIN(sizeof ofport->opp.name, sizeof odp_port->devname));
ofport->opp.name[sizeof ofport->opp.name - 1] = '\0';
update_port(struct ofproto *p, const char *devname)
{
struct odp_port odp_port;
- struct ofport *ofport;
+ struct ofport *old_ofport;
+ struct ofport *new_ofport;
int error;
COVERAGE_INC(ofproto_update_port);
- ofport = shash_find_data(&p->port_by_name, devname);
+
+ /* Query the datapath for port information. */
error = dpif_port_query_by_name(p->dpif, devname, &odp_port);
- if (!error) {
- if (!ofport) {
- /* New port. */
- if (!ofport_conflicts(p, &odp_port)) {
- ofport = make_ofport(&odp_port);
- if (ofport) {
- ofport_install(p, ofport);
- send_port_status(p, ofport, OFPPR_ADD);
- }
- }
- } else {
- /* Modified port. */
- struct ofport *new_ofport = make_ofport(&odp_port);
- if (!new_ofport) {
- return;
- }
- new_ofport->opp.config &= OFPPC_PORT_DOWN;
- new_ofport->opp.config |= ofport->opp.config & ~OFPPC_PORT_DOWN;
- if (ofport_equal(ofport, new_ofport)) {
- /* False alarm--no change. */
- ofport_free(new_ofport);
- } else {
- ofport_remove(p, ofport);
- ofport_install(p, new_ofport);
- ofport_free(ofport);
- send_port_status(p, new_ofport, OFPPR_MODIFY);
- }
- }
- } else if (error == ENOENT || error == ENODEV) {
- /* Deleted port. */
- if (ofport) {
- send_port_status(p, ofport, OFPPR_DELETE);
- ofport_remove(p, ofport);
- ofport_free(ofport);
+ /* Find the old ofport. */
+ old_ofport = shash_find_data(&p->port_by_name, devname);
+ if (!error) {
+ if (!old_ofport) {
+ /* There's no port named 'devname' but there might be a port with
+ * the same port number. This could happen if a port is deleted
+ * and then a new one added in its place very quickly, or if a port
+ * is renamed. In the former case we want to send an OFPPR_DELETE
+ * and an OFPPR_ADD, and in the latter case we want to send a
+ * single OFPPR_MODIFY. We can distinguish the cases by comparing
+ * the old port's ifindex against the new port, or perhaps less
+ * reliably but more portably by comparing the old port's MAC
+ * against the new port's MAC. However, this code isn't that smart
+ * and always sends an OFPPR_MODIFY (XXX). */
+ old_ofport = port_array_get(&p->ports, odp_port.port);
}
- } else {
+ } else if (error != ENOENT && error != ENODEV) {
VLOG_WARN_RL(&rl, "dpif_port_query_by_name returned unexpected error "
"%s", strerror(error));
return;
}
+
+ /* Create a new ofport. */
+ new_ofport = !error ? make_ofport(&odp_port) : NULL;
+
+ /* Eliminate a few pathological cases. */
+ if (!old_ofport && !new_ofport) {
+ return;
+ } else if (old_ofport && new_ofport) {
+ /* Most of the 'config' bits are OpenFlow soft state, but
+ * OFPPC_PORT_DOWN is maintained the kernel. So transfer the OpenFlow
+ * bits from old_ofport. (make_ofport() only sets OFPPC_PORT_DOWN and
+ * leaves the other bits 0.) */
+ new_ofport->opp.config |= old_ofport->opp.config & ~OFPPC_PORT_DOWN;
+
+ if (ofport_equal(old_ofport, new_ofport)) {
+ /* False alarm--no change. */
+ ofport_free(new_ofport);
+ return;
+ }
+ }
+
+ /* Now deal with the normal cases. */
+ if (old_ofport) {
+ ofport_remove(p, old_ofport);
+ }
+ if (new_ofport) {
+ ofport_install(p, new_ofport);
+ }
+ send_port_status(p, new_ofport ? new_ofport : old_ofport,
+ (!old_ofport ? OFPPR_ADD
+ : !new_ofport ? OFPPR_DELETE
+ : OFPPR_MODIFY));
+ ofport_free(old_ofport);
+
+ /* Update port groups. */
refresh_port_groups(p);
}
if (!of_msg) {
break;
}
+ if (p->fail_open) {
+ fail_open_maybe_recover(p->fail_open);
+ }
handle_openflow(ofconn, p, of_msg);
ofpbuf_delete(of_msg);
}
/* Caller is responsible for initializing the 'cr' member of the returned
* rule. */
static struct rule *
-rule_create(struct rule *super,
+rule_create(struct ofproto *ofproto, struct rule *super,
const union ofp_action *actions, size_t n_actions,
uint16_t idle_timeout, uint16_t hard_timeout)
{
}
rule->n_actions = n_actions;
rule->actions = xmemdup(actions, n_actions * sizeof *actions);
+ netflow_flow_clear(&rule->nf_flow);
+ netflow_flow_update_time(ofproto->netflow, &rule->nf_flow, rule->created);
+
return rule;
}
if (rule->cr.wc.wildcards || !flow_equal(flow, &rule->cr.flow)) {
struct rule *super = rule->super ? rule->super : rule;
if (xlate_actions(super->actions, super->n_actions, flow, ofproto,
- packet, &a, NULL, 0)) {
+ packet, &a, NULL, 0, NULL)) {
return;
}
actions = a.actions;
actions, n_actions, packet)) {
struct odp_flow_stats stats;
flow_extract_stats(flow, packet, &stats);
- update_stats(rule, &stats);
+ update_stats(ofproto, rule, &stats);
rule->used = time_msec();
+ netflow_flow_update_time(ofproto->netflow, &rule->nf_flow, rule->used);
}
}
rule_create_subrule(struct ofproto *ofproto, struct rule *rule,
const flow_t *flow)
{
- struct rule *subrule = rule_create(rule, NULL, 0,
+ struct rule *subrule = rule_create(ofproto, rule, NULL, 0,
rule->idle_timeout, rule->hard_timeout);
COVERAGE_INC(ofproto_subrule_create);
cls_rule_from_flow(&subrule->cr, flow, 0,
super = rule->super ? rule->super : rule;
rule->tags = 0;
xlate_actions(super->actions, super->n_actions, &rule->cr.flow, p,
- packet, &a, &rule->tags, &rule->may_install);
+ packet, &a, &rule->tags, &rule->may_install,
+ &rule->nf_flow.output_iface);
actions_len = a.n_actions * sizeof *a.actions;
if (rule->n_odp_actions != a.n_actions
&put)) {
rule->installed = true;
if (displaced_rule) {
- update_stats(rule, &put.flow.stats);
+ update_stats(p, displaced_rule, &put.flow.stats);
rule_post_uninstall(p, displaced_rule);
}
}
static void
rule_update_actions(struct ofproto *ofproto, struct rule *rule)
{
- bool actions_changed = rule_make_actions(ofproto, rule, NULL);
+ bool actions_changed;
+ uint16_t new_out_iface, old_out_iface;
+
+ old_out_iface = rule->nf_flow.output_iface;
+ actions_changed = rule_make_actions(ofproto, rule, NULL);
+
if (rule->may_install) {
if (rule->installed) {
if (actions_changed) {
- /* XXX should really do rule_post_uninstall() for the *old* set
- * of actions, and distinguish the old stats from the new. */
struct odp_flow_put put;
- do_put_flow(ofproto, rule, ODPPF_CREATE | ODPPF_MODIFY, &put);
+ do_put_flow(ofproto, rule, ODPPF_CREATE | ODPPF_MODIFY
+ | ODPPF_ZERO_STATS, &put);
+ update_stats(ofproto, rule, &put.flow.stats);
+
+ /* Temporarily set the old output iface so that NetFlow
+ * messages have the correct output interface for the old
+ * stats. */
+ new_out_iface = rule->nf_flow.output_iface;
+ rule->nf_flow.output_iface = old_out_iface;
+ rule_post_uninstall(ofproto, rule);
+ rule->nf_flow.output_iface = new_out_iface;
}
} else {
rule_install(ofproto, rule, NULL);
odp_flow.actions = NULL;
odp_flow.n_actions = 0;
if (!dpif_flow_del(p->dpif, &odp_flow)) {
- update_stats(rule, &odp_flow.stats);
+ update_stats(p, rule, &odp_flow.stats);
}
rule->installed = false;
}
}
+static bool
+is_controller_rule(struct rule *rule)
+{
+ /* If the only action is send to the controller then don't report
+ * NetFlow expiration messages since it is just part of the control
+ * logic for the network and not real traffic. */
+
+ if (rule && rule->super) {
+ struct rule *super = rule->super;
+
+ return super->n_actions == 1 &&
+ super->actions[0].type == htons(OFPAT_OUTPUT) &&
+ super->actions[0].output.port == htons(OFPP_CONTROLLER);
+ }
+
+ return false;
+}
+
static void
rule_post_uninstall(struct ofproto *ofproto, struct rule *rule)
{
struct rule *super = rule->super;
rule_account(ofproto, rule, 0);
- if (ofproto->netflow) {
+
+ if (ofproto->netflow && !is_controller_rule(rule)) {
struct ofexpired expired;
expired.flow = rule->cr.flow;
expired.packet_count = rule->packet_count;
expired.byte_count = rule->byte_count;
expired.used = rule->used;
- expired.created = rule->created;
- expired.tcp_flags = rule->tcp_flags;
- expired.ip_tos = rule->ip_tos;
- netflow_expire(ofproto->netflow, &expired);
+ netflow_expire(ofproto->netflow, &rule->nf_flow, &expired);
}
if (super) {
super->packet_count += rule->packet_count;
super->byte_count += rule->byte_count;
- super->tcp_flags |= rule->tcp_flags;
- if (rule->packet_count) {
- super->ip_tos = rule->ip_tos;
- }
- }
- /* Reset counters to prevent double counting if the rule ever gets
- * reinstalled. */
- rule->packet_count = 0;
- rule->byte_count = 0;
- rule->accounted_bytes = 0;
- rule->tcp_flags = 0;
- rule->ip_tos = 0;
+ /* Reset counters to prevent double counting if the rule ever gets
+ * reinstalled. */
+ rule->packet_count = 0;
+ rule->byte_count = 0;
+ rule->accounted_bytes = 0;
+
+ netflow_flow_clear(&rule->nf_flow);
+ }
}
\f
static void
}
static void
-add_output_group_action(struct odp_actions *actions, uint16_t group)
+add_output_group_action(struct odp_actions *actions, uint16_t group,
+ uint16_t *nf_output_iface)
{
odp_actions_add(actions, ODPAT_OUTPUT_GROUP)->output_group.group = group;
+
+ if (group == DP_GROUP_ALL || group == DP_GROUP_FLOOD) {
+ *nf_output_iface = NF_OUT_FLOOD;
+ }
}
static void
/* Output. */
struct odp_actions *out; /* Datapath actions. */
tag_type *tags; /* Tags associated with OFPP_NORMAL actions. */
- bool may_setup_flow; /* True ordinarily; false if the actions must
+ bool may_set_up_flow; /* True ordinarily; false if the actions must
* be reassessed for every packet. */
+ uint16_t nf_output_iface; /* Output interface index for NetFlow. */
};
static void do_xlate_actions(const union ofp_action *in, size_t n_in,
add_output_action(struct action_xlate_ctx *ctx, uint16_t port)
{
const struct ofport *ofport = port_array_get(&ctx->ofproto->ports, port);
- if (!ofport || !(ofport->opp.config & OFPPC_NO_FWD)) {
- odp_actions_add(ctx->out, ODPAT_OUTPUT)->output.port = port;
+
+ if (ofport) {
+ if (ofport->opp.config & OFPPC_NO_FWD) {
+ /* Forwarding disabled on port. */
+ return;
+ }
+ } else {
+ /*
+ * We don't have an ofport record for this port, but it doesn't hurt to
+ * allow forwarding to it anyhow. Maybe such a port will appear later
+ * and we're pre-populating the flow table.
+ */
}
+
+ odp_actions_add(ctx->out, ODPAT_OUTPUT)->output.port = port;
+ ctx->nf_output_iface = port;
}
static struct rule *
const struct ofp_action_output *oao)
{
uint16_t odp_port;
+ uint16_t prev_nf_output_iface = ctx->nf_output_iface;
+
+ ctx->nf_output_iface = NF_OUT_DROP;
switch (ntohs(oao->port)) {
case OFPP_IN_PORT:
case OFPP_NORMAL:
if (!ctx->ofproto->ofhooks->normal_cb(ctx->flow, ctx->packet,
ctx->out, ctx->tags,
+ &ctx->nf_output_iface,
ctx->ofproto->aux)) {
COVERAGE_INC(ofproto_uninstallable);
- ctx->may_setup_flow = false;
+ ctx->may_set_up_flow = false;
}
break;
case OFPP_FLOOD:
- add_output_group_action(ctx->out, DP_GROUP_FLOOD);
+ add_output_group_action(ctx->out, DP_GROUP_FLOOD,
+ &ctx->nf_output_iface);
break;
case OFPP_ALL:
- add_output_group_action(ctx->out, DP_GROUP_ALL);
+ add_output_group_action(ctx->out, DP_GROUP_ALL, &ctx->nf_output_iface);
break;
case OFPP_CONTROLLER:
add_controller_action(ctx->out, oao);
}
break;
}
+
+ if (prev_nf_output_iface == NF_OUT_FLOOD) {
+ ctx->nf_output_iface = NF_OUT_FLOOD;
+ } else if (ctx->nf_output_iface == NF_OUT_DROP) {
+ ctx->nf_output_iface = prev_nf_output_iface;
+ } else if (prev_nf_output_iface != NF_OUT_DROP &&
+ ctx->nf_output_iface != NF_OUT_FLOOD) {
+ ctx->nf_output_iface = NF_OUT_MULTI;
+ }
}
static void
oa->nw_addr.nw_addr = ia->nw_addr.nw_addr;
break;
+ case OFPAT_SET_NW_DST:
+ oa = odp_actions_add(ctx->out, ODPAT_SET_NW_DST);
+ oa->nw_addr.nw_addr = ia->nw_addr.nw_addr;
+ break;
+
case OFPAT_SET_TP_SRC:
oa = odp_actions_add(ctx->out, ODPAT_SET_TP_SRC);
oa->tp_port.tp_port = ia->tp_port.tp_port;
break;
+ case OFPAT_SET_TP_DST:
+ oa = odp_actions_add(ctx->out, ODPAT_SET_TP_DST);
+ oa->tp_port.tp_port = ia->tp_port.tp_port;
+ break;
+
case OFPAT_VENDOR:
xlate_nicira_action(ctx, (const struct nx_action_header *) ia);
break;
xlate_actions(const union ofp_action *in, size_t n_in,
const flow_t *flow, struct ofproto *ofproto,
const struct ofpbuf *packet,
- struct odp_actions *out, tag_type *tags, bool *may_setup_flow)
+ struct odp_actions *out, tag_type *tags, bool *may_set_up_flow,
+ uint16_t *nf_output_iface)
{
tag_type no_tags = 0;
struct action_xlate_ctx ctx;
ctx.packet = packet;
ctx.out = out;
ctx.tags = tags ? tags : &no_tags;
- ctx.may_setup_flow = true;
+ ctx.may_set_up_flow = true;
+ ctx.nf_output_iface = NF_OUT_DROP;
do_xlate_actions(in, n_in, &ctx);
- if (may_setup_flow) {
- *may_setup_flow = ctx.may_setup_flow;
+
+ /* Check with in-band control to see if we're allowed to set up this
+ * flow. */
+ if (!in_band_rule_check(ofproto->in_band, flow, out)) {
+ ctx.may_set_up_flow = false;
+ }
+
+ if (may_set_up_flow) {
+ *may_set_up_flow = ctx.may_set_up_flow;
+ }
+ if (nf_output_iface) {
+ *nf_output_iface = ctx.nf_output_iface;
}
if (odp_actions_overflow(out)) {
odp_actions_init(out);
if (opo->buffer_id != htonl(UINT32_MAX)) {
error = pktbuf_retrieve(ofconn->pktbuf, ntohl(opo->buffer_id),
&buffer, &in_port);
- if (error) {
+ if (error || !buffer) {
return error;
}
payload = *buffer;
flow_extract(&payload, ofp_port_to_odp_port(ntohs(opo->in_port)), &flow);
error = xlate_actions((const union ofp_action *) opo->actions, n_actions,
- &flow, p, &payload, &actions, NULL, NULL);
+ &flow, p, &payload, &actions, NULL, NULL, NULL);
if (error) {
return error;
}
struct odp_flow *odp_flows;
size_t n_odp_flows;
+ packet_count = rule->packet_count;
+ byte_count = rule->byte_count;
+
n_odp_flows = rule->cr.wc.wildcards ? list_size(&rule->list) : 1;
odp_flows = xcalloc(1, n_odp_flows * sizeof *odp_flows);
if (rule->cr.wc.wildcards) {
size_t i = 0;
LIST_FOR_EACH (subrule, struct rule, list, &rule->list) {
odp_flows[i++].key = subrule->cr.flow;
+ packet_count += subrule->packet_count;
+ byte_count += subrule->byte_count;
}
} else {
odp_flows[0].key = rule->cr.flow;
return 0;
}
+struct flow_stats_ds_cbdata {
+ struct ofproto *ofproto;
+ struct ds *results;
+};
+
+static void
+flow_stats_ds_cb(struct cls_rule *rule_, void *cbdata_)
+{
+ struct rule *rule = rule_from_cls_rule(rule_);
+ struct flow_stats_ds_cbdata *cbdata = cbdata_;
+ struct ds *results = cbdata->results;
+ struct ofp_match match;
+ uint64_t packet_count, byte_count;
+ size_t act_len = sizeof *rule->actions * rule->n_actions;
+
+ /* Don't report on subrules. */
+ if (rule->super != NULL) {
+ return;
+ }
+
+ query_stats(cbdata->ofproto, rule, &packet_count, &byte_count);
+ flow_to_ovs_match(&rule->cr.flow, rule->cr.wc.wildcards, &match);
+
+ ds_put_format(results, "duration=%llds, ",
+ (time_msec() - rule->created) / 1000);
+ ds_put_format(results, "priority=%u, ", rule->cr.priority);
+ ds_put_format(results, "n_packets=%"PRIu64", ", packet_count);
+ ds_put_format(results, "n_bytes=%"PRIu64", ", byte_count);
+ ofp_print_match(results, &match, true);
+ ofp_print_actions(results, &rule->actions->header, act_len);
+ ds_put_cstr(results, "\n");
+}
+
+/* Adds a pretty-printed description of all flows to 'results', including
+ * those marked hidden by secchan (e.g., by in-band control). */
+void
+ofproto_get_all_flows(struct ofproto *p, struct ds *results)
+{
+ struct ofp_match match;
+ struct cls_rule target;
+ struct flow_stats_ds_cbdata cbdata;
+
+ memset(&match, 0, sizeof match);
+ match.wildcards = htonl(OFPFW_ALL);
+
+ cbdata.ofproto = p;
+ cbdata.results = results;
+
+ cls_rule_from_match(&target, &match, 0);
+ classifier_for_each_match(&p->cls, &target, CLS_INC_ALL,
+ flow_stats_ds_cb, &cbdata);
+}
+
struct aggregate_stats_cbdata {
struct ofproto *ofproto;
uint16_t out_port;
}
static void
-update_time(struct rule *rule, const struct odp_flow_stats *stats)
+update_time(struct ofproto *ofproto, struct rule *rule,
+ const struct odp_flow_stats *stats)
{
long long int used = msec_from_nsec(stats->used_sec, stats->used_nsec);
if (used > rule->used) {
rule->used = used;
+ if (rule->super && used > rule->super->used) {
+ rule->super->used = used;
+ }
+ netflow_flow_update_time(ofproto->netflow, &rule->nf_flow, used);
}
}
static void
-update_stats(struct rule *rule, const struct odp_flow_stats *stats)
+update_stats(struct ofproto *ofproto, struct rule *rule,
+ const struct odp_flow_stats *stats)
{
- update_time(rule, stats);
- rule->packet_count += stats->n_packets;
- rule->byte_count += stats->n_bytes;
- rule->tcp_flags |= stats->tcp_flags;
if (stats->n_packets) {
- rule->ip_tos = stats->ip_tos;
+ update_time(ofproto, rule, stats);
+ rule->packet_count += stats->n_packets;
+ rule->byte_count += stats->n_bytes;
+ netflow_flow_update_flags(&rule->nf_flow, stats->ip_tos,
+ stats->tcp_flags);
}
}
uint16_t in_port;
int error;
- rule = rule_create(NULL, (const union ofp_action *) ofm->actions,
+ rule = rule_create(p, NULL, (const union ofp_action *) ofm->actions,
n_actions, ntohs(ofm->idle_timeout),
ntohs(ofm->hard_timeout));
cls_rule_from_match(&rule->cr, &ofm->match, ntohs(ofm->priority));
{
size_t msg_len = ntohs(ofmph->header.header.length);
if (msg_len < sizeof(*ofmph)) {
- VLOG_WARN_RL(&rl, "dropping short managment message: %d\n", msg_len);
+ VLOG_WARN_RL(&rl, "dropping short managment message: %zu\n", msg_len);
return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LENGTH);
}
struct ofmp_capability_request *ofmpcr;
if (msg_len < sizeof(struct ofmp_capability_request)) {
- VLOG_WARN_RL(&rl, "dropping short capability request: %d\n",
+ VLOG_WARN_RL(&rl, "dropping short capability request: %zu\n",
msg_len);
return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LENGTH);
}
payload.size = msg->length - sizeof *msg;
flow_extract(&payload, msg->port, &flow);
+ /* Check with in-band control to see if this packet should be sent
+ * to the local port regardless of the flow table. */
+ if (in_band_msg_in_hook(p->in_band, &flow, &payload)) {
+ union odp_action action;
+
+ memset(&action, 0, sizeof(action));
+ action.output.type = ODPAT_OUTPUT;
+ action.output.port = ODPP_LOCAL;
+ dpif_execute(p->dpif, flow.in_port, &action, 1, &payload);
+ }
+
rule = lookup_valid_rule(p, &flow);
if (!rule) {
/* Don't send a packet-in if OFPPC_NO_PACKET_IN asserted. */
rule_execute(p, rule, &payload, &flow);
rule_reinstall(p, rule);
- ofpbuf_delete(packet);
+
+ if (rule->super && rule->super->cr.priority == FAIL_OPEN_PRIORITY
+ && rconn_is_connected(p->controller->rconn)) {
+ /*
+ * Extra-special case for fail-open mode.
+ *
+ * We are in fail-open mode and the packet matched the fail-open rule,
+ * but we are connected to a controller too. We should send the packet
+ * up to the controller in the hope that it will try to set up a flow
+ * and thereby allow us to exit fail-open.
+ *
+ * See the top-level comment in fail-open.c for more information.
+ */
+ pinsched_send(p->miss_sched, in_port, packet, send_packet_in_miss, p);
+ } else {
+ ofpbuf_delete(packet);
+ }
}
\f
static void
flow_to_match(&rule->cr.flow, rule->cr.wc.wildcards, &ofe->match);
ofe->priority = htons(rule->cr.priority);
ofe->reason = reason;
- ofe->duration = (now - rule->created) / 1000;
- ofe->packet_count = rule->packet_count;
- ofe->byte_count = rule->byte_count;
+ ofe->duration = htonl((now - rule->created) / 1000);
+ ofe->packet_count = htonll(rule->packet_count);
+ ofe->byte_count = htonll(rule->byte_count);
return buf;
}
{
struct ofconn *ofconn;
struct ofconn *prev;
- struct ofpbuf *buf;
+ struct ofpbuf *buf = NULL;
/* We limit the maximum number of queued flow expirations it by accounting
* them under the counter for replies. That works because preventing
LIST_FOR_EACH (ofconn, struct ofconn, node, &p->all_conns) {
if (ofconn->send_flow_exp && rconn_is_connected(ofconn->rconn)) {
if (prev) {
- queue_tx(ofpbuf_clone(buf), prev, ofconn->reply_counter);
+ queue_tx(ofpbuf_clone(buf), prev, prev->reply_counter);
} else {
buf = compose_flow_exp(rule, now, reason);
}
}
}
if (prev) {
- queue_tx(buf, prev, ofconn->reply_counter);
+ queue_tx(buf, prev, prev->reply_counter);
}
}
? rule->used + rule->idle_timeout * 1000
: LLONG_MAX);
expire = MIN(hard_expire, idle_expire);
- if (expire == LLONG_MAX) {
- if (rule->installed && time_msec() >= rule->used + 5000) {
- uninstall_idle_flow(p, rule);
- }
- return;
- }
now = time_msec();
if (now < expire) {
if (rule->installed && now >= rule->used + 5000) {
uninstall_idle_flow(p, rule);
+ } else if (!rule->cr.wc.wildcards) {
+ active_timeout(p, rule);
}
+
return;
}
COVERAGE_INC(ofproto_expired);
+
+ /* Update stats. This code will be a no-op if the rule expired
+ * due to an idle timeout. */
if (rule->cr.wc.wildcards) {
- /* Update stats. (This code will be a no-op if the rule expired
- * due to an idle timeout, because in that case the rule has no
- * subrules left.) */
struct rule *subrule, *next;
LIST_FOR_EACH_SAFE (subrule, next, struct rule, list, &rule->list) {
rule_remove(p, subrule);
}
+ } else {
+ rule_uninstall(p, rule);
}
- send_flow_exp(p, rule, now,
- (now >= hard_expire
- ? OFPER_HARD_TIMEOUT : OFPER_IDLE_TIMEOUT));
+ if (!rule_is_hidden(rule)) {
+ send_flow_exp(p, rule, now,
+ (now >= hard_expire
+ ? OFPER_HARD_TIMEOUT : OFPER_IDLE_TIMEOUT));
+ }
rule_remove(p, rule);
}
+static void
+active_timeout(struct ofproto *ofproto, struct rule *rule)
+{
+ if (ofproto->netflow && !is_controller_rule(rule) &&
+ netflow_active_timeout_expired(ofproto->netflow, &rule->nf_flow)) {
+ struct ofexpired expired;
+ struct odp_flow odp_flow;
+
+ /* Get updated flow stats. */
+ memset(&odp_flow, 0, sizeof odp_flow);
+ if (rule->installed) {
+ odp_flow.key = rule->cr.flow;
+ odp_flow.flags = ODPFF_ZERO_TCP_FLAGS;
+ dpif_flow_get(ofproto->dpif, &odp_flow);
+
+ if (odp_flow.stats.n_packets) {
+ update_time(ofproto, rule, &odp_flow.stats);
+ netflow_flow_update_flags(&rule->nf_flow, odp_flow.stats.ip_tos,
+ odp_flow.stats.tcp_flags);
+ }
+ }
+
+ expired.flow = rule->cr.flow;
+ expired.packet_count = rule->packet_count +
+ odp_flow.stats.n_packets;
+ expired.byte_count = rule->byte_count + odp_flow.stats.n_bytes;
+ expired.used = rule->used;
+
+ netflow_expire(ofproto->netflow, &rule->nf_flow, &expired);
+
+ /* Schedule us to send the accumulated records once we have
+ * collected all of them. */
+ poll_immediate_wake();
+ }
+}
+
static void
update_used(struct ofproto *p)
{
continue;
}
- update_time(rule, &f->stats);
+ update_time(p, rule, &f->stats);
rule_account(p, rule, f->stats.n_bytes);
}
free(flows);
do_send_packet_in(struct ofconn *ofconn, uint32_t buffer_id,
const struct ofpbuf *packet, int send_len)
{
- struct ofp_packet_in *opi;
- struct ofpbuf payload, *buf;
- struct odp_msg *msg;
+ struct odp_msg *msg = packet->data;
+ struct ofpbuf payload;
+ struct ofpbuf *opi;
+ uint8_t reason;
- msg = packet->data;
+ /* Extract packet payload from 'msg'. */
payload.data = msg + 1;
payload.size = msg->length - sizeof *msg;
- send_len = MIN(send_len, payload.size);
- buf = ofpbuf_new(sizeof *opi + send_len);
- opi = put_openflow_xid(offsetof(struct ofp_packet_in, data),
- OFPT_PACKET_IN, 0, buf);
- opi->buffer_id = htonl(buffer_id);
- opi->total_len = htons(payload.size);
- opi->in_port = htons(odp_port_to_ofp_port(msg->port));
- opi->reason = msg->type == _ODPL_ACTION_NR ? OFPR_ACTION : OFPR_NO_MATCH;
- ofpbuf_put(buf, payload.data, MIN(send_len, payload.size));
- update_openflow_length(buf);
- rconn_send_with_limit(ofconn->rconn, buf, ofconn->packet_in_counter, 100);
+ /* Construct ofp_packet_in message. */
+ reason = msg->type == _ODPL_ACTION_NR ? OFPR_ACTION : OFPR_NO_MATCH;
+ opi = make_packet_in(buffer_id, odp_port_to_ofp_port(msg->port), reason,
+ &payload, send_len);
+
+ /* Send. */
+ rconn_send_with_limit(ofconn->rconn, opi, ofconn->packet_in_counter, 100);
}
static void
send_packet_in_miss(struct ofpbuf *packet, void *p_)
{
struct ofproto *p = p_;
+ bool in_fail_open = p->fail_open && fail_open_is_active(p->fail_open);
struct ofconn *ofconn;
struct ofpbuf payload;
struct odp_msg *msg;
payload.size = msg->length - sizeof *msg;
LIST_FOR_EACH (ofconn, struct ofconn, node, &p->all_conns) {
if (ofconn->miss_send_len) {
- uint32_t buffer_id = pktbuf_save(ofconn->pktbuf, &payload,
- msg->port);
+ struct pktbuf *pb = ofconn->pktbuf;
+ uint32_t buffer_id = (in_fail_open
+ ? pktbuf_get_null()
+ : pktbuf_save(pb, &payload, msg->port));
int send_len = (buffer_id != UINT32_MAX ? ofconn->miss_send_len
: UINT32_MAX);
do_send_packet_in(ofconn, buffer_id, packet, send_len);
}
static uint64_t
-pick_datapath_id(struct dpif *dpif, uint64_t fallback_dpid)
+pick_datapath_id(const struct ofproto *ofproto)
{
- char local_name[IF_NAMESIZE];
- uint8_t ea[ETH_ADDR_LEN];
- int error;
+ const struct ofport *port;
- error = dpif_port_get_name(dpif, ODPP_LOCAL,
- local_name, sizeof local_name);
- if (!error) {
- error = netdev_nodev_get_etheraddr(local_name, ea);
+ port = port_array_get(&ofproto->ports, ODPP_LOCAL);
+ if (port) {
+ uint8_t ea[ETH_ADDR_LEN];
+ int error;
+
+ error = netdev_get_etheraddr(port->netdev, ea);
if (!error) {
return eth_addr_to_uint64(ea);
}
VLOG_WARN("could not get MAC address for %s (%s)",
- local_name, strerror(error));
+ netdev_get_name(port->netdev), strerror(error));
}
-
- return fallback_dpid;
+ return ofproto->fallback_dpid;
}
static uint64_t
static bool
default_normal_ofhook_cb(const flow_t *flow, const struct ofpbuf *packet,
struct odp_actions *actions, tag_type *tags,
- void *ofproto_)
+ uint16_t *nf_output_iface, void *ofproto_)
{
struct ofproto *ofproto = ofproto_;
int out_port;
/* Determine output port. */
out_port = mac_learning_lookup_tag(ofproto->ml, flow->dl_dst, 0, tags);
if (out_port < 0) {
- add_output_group_action(actions, DP_GROUP_FLOOD);
+ add_output_group_action(actions, DP_GROUP_FLOOD, nf_output_iface);
} else if (out_port != flow->in_port) {
odp_actions_add(actions, ODPAT_OUTPUT)->output.port = out_port;
+ *nf_output_iface = out_port;
} else {
/* Drop. */
}