#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"
/* 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;
{
if (in_band != (p->in_band != NULL)) {
if (in_band) {
- in_band_create(p, p->switch_status, p->controller->rconn,
- &p->in_band);
- return 0;
+ return in_band_create(p, p->dpif, p->switch_status,
+ p->controller->rconn, &p->in_band);
} else {
ofproto_set_discovery(p, false, NULL, true);
in_band_destroy(p->in_band);
}
}
}
- 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;
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);
}
struct rule *super = rule->super;
rule_account(ofproto, rule, 0);
- if (ofproto->netflow) {
+ if (ofproto->netflow && rule->byte_count) {
struct ofexpired expired;
expired.flow = rule->cr.flow;
expired.packet_count = rule->packet_count;
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;
}
static struct rule *
ctx.tags = tags ? tags : &no_tags;
ctx.may_setup_flow = true;
do_xlate_actions(in, n_in, &ctx);
+
+ /* Check with in-band control to see if we're allowed to setup this
+ * flow. */
+ if (!in_band_rule_check(ofproto->in_band, flow, out)) {
+ ctx.may_setup_flow = false;
+ }
+
if (may_setup_flow) {
*may_setup_flow = ctx.may_setup_flow;
}
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;
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;
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
{
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);
}
}
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);