#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;
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). */
+ uint16_t nf_output_iface; /* Output interface index for NetFlow. */
/* 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
/* 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) {
- return in_band_create(p, &p->dpif, p->switch_status,
- p->controller->rconn, &p->in_band);
+ in_band_create(p, &p->dpif, p->switch_status,
+ p->controller->rconn, &p->in_band);
+ return 0;
} else {
ofproto_set_discovery(p, false, NULL, true);
in_band_destroy(p->in_band);
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;
}
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);
}
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;
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_output_iface);
actions_len = a.n_actions * sizeof *a.actions;
if (rule->n_odp_actions != a.n_actions
rule_post_uninstall(struct ofproto *ofproto, struct rule *rule)
{
struct rule *super = rule->super;
+ bool controller_action;
rule_account(ofproto, rule, 0);
- if (ofproto->netflow) {
+
+ /* 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. */
+ controller_action = rule->n_odp_actions == 1 &&
+ rule->odp_actions[0].type == ODPAT_CONTROLLER;
+
+ if (ofproto->netflow && rule->byte_count && !controller_action) {
struct ofexpired expired;
expired.flow = rule->cr.flow;
expired.packet_count = rule->packet_count;
expired.created = rule->created;
expired.tcp_flags = rule->tcp_flags;
expired.ip_tos = rule->ip_tos;
+ expired.output_iface = rule->nf_output_iface;
netflow_expire(ofproto->netflow, &expired);
}
if (super) {
}
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
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;
}
- packet_count = rule->packet_count;
- byte_count = rule->byte_count;
if (!dpif_flow_get_multiple(&p->dpif, odp_flows, n_odp_flows)) {
size_t i;
for (i = 0; i < n_odp_flows; i++) {
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
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);
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. */
}