#include "netflow.h"
#include "odp-util.h"
#include "ofp-print.h"
+#include "ofp-util.h"
#include "ofproto-sflow.h"
#include "ofpbuf.h"
#include "openflow/nicira-ext.h"
#include "timeval.h"
#include "unixctl.h"
#include "vconn.h"
+#include "vlog.h"
#include "xtoxll.h"
-#define THIS_MODULE VLM_ofproto
-#include "vlog.h"
+VLOG_DEFINE_THIS_MODULE(ofproto)
#include "sflow_api.h"
static void ofconn_run(struct ofconn *, struct ofproto *);
static void ofconn_wait(struct ofconn *);
static bool ofconn_receives_async_msgs(const struct ofconn *);
+static char *ofconn_make_name(const struct ofproto *, const char *target);
static void queue_tx(struct ofpbuf *msg, const struct ofconn *ofconn,
struct rconn_packet_counter *counter);
uint64_t old_dpid = p->datapath_id;
p->datapath_id = datapath_id ? datapath_id : pick_datapath_id(p);
if (p->datapath_id != old_dpid) {
- struct ofconn *ofconn;
-
VLOG_INFO("datapath ID changed to %016"PRIx64, p->datapath_id);
/* Force all active connections to reconnect, since there is no way to
* notify a controller that the datapath ID has changed. */
- LIST_FOR_EACH (ofconn, struct ofconn, node, &p->all_conns) {
- rconn_reconnect(ofconn->rconn);
- }
+ ofproto_reconnect_controllers(p);
}
}
if (discovery) {
ofconn->discovery = discovery;
} else {
- rconn_connect(ofconn->rconn, c->target);
+ char *name = ofconn_make_name(ofproto, c->target);
+ rconn_connect(ofconn->rconn, c->target, name);
+ free(name);
}
hmap_insert(&ofproto->controllers, &ofconn->hmap_node,
hash_string(c->target, 0));
static const char *
ofconn_get_target(const struct ofconn *ofconn)
{
- return ofconn->discovery ? "discover" : rconn_get_name(ofconn->rconn);
+ return ofconn->discovery ? "discover" : rconn_get_target(ofconn->rconn);
}
static struct ofconn *
}
}
+/* Drops the connections between 'ofproto' and all of its controllers, forcing
+ * them to reconnect. */
+void
+ofproto_reconnect_controllers(struct ofproto *ofproto)
+{
+ struct ofconn *ofconn;
+
+ LIST_FOR_EACH (ofconn, struct ofconn, node, &ofproto->all_conns) {
+ rconn_reconnect(ofconn->rconn);
+ }
+}
+
static bool
any_extras_changed(const struct ofproto *ofproto,
const struct sockaddr_in *extras, size_t n)
retval = pvconn_accept(p->listeners[i], OFP_VERSION, &vconn);
if (!retval) {
- ofconn_create(p, rconn_new_from_vconn("passive", vconn),
- OFCONN_TRANSIENT);
+ struct rconn *rconn;
+ char *name;
+
+ rconn = rconn_create(60, 0);
+ name = ofconn_make_name(p, vconn_get_name(vconn));
+ rconn_connect_unreliably(rconn, vconn, name);
+ free(name);
+
+ ofconn_create(p, rconn, OFCONN_TRANSIENT);
} else if (retval != EAGAIN) {
VLOG_WARN_RL(&rl, "accept failed (%s)", strerror(retval));
}
uint16_t odp_port = ofp_port_to_odp_port(ofport->opp.port_no);
netdev_monitor_remove(p->netdev_monitor, ofport->netdev);
- port_array_set(&p->ports, odp_port, NULL);
+ port_array_delete(&p->ports, odp_port);
shash_delete(&p->port_by_name,
shash_find(&p->port_by_name, (char *) ofport->opp.name));
if (p->sflow) {
}
if (discovery_run(ofconn->discovery, &controller_name)) {
if (controller_name) {
- rconn_connect(ofconn->rconn, controller_name);
+ char *ofconn_name = ofconn_make_name(p, controller_name);
+ rconn_connect(ofconn->rconn, controller_name, ofconn_name);
+ free(ofconn_name);
} else {
rconn_disconnect(ofconn->rconn);
}
return ofconn->miss_send_len > 0;
}
}
+
+/* Returns a human-readable name for an OpenFlow connection between 'ofproto'
+ * and 'target', suitable for use in log messages for identifying the
+ * connection.
+ *
+ * The name is dynamically allocated. The caller should free it (with free())
+ * when it is no longer needed. */
+static char *
+ofconn_make_name(const struct ofproto *ofproto, const char *target)
+{
+ return xasprintf("%s<->%s", dpif_base_name(ofproto->dpif), target);
+}
\f
/* Caller is responsible for initializing the 'cr' member of the returned
* rule. */
}
for (oa = actions_first(&i, rule->actions, rule->n_actions); oa;
oa = actions_next(&i)) {
- if (oa->type == htons(OFPAT_OUTPUT) && oa->output.port == out_port) {
+ if (action_outputs_to_port(oa, out_port)) {
return true;
}
}
* 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;
+ return (rule
+ && rule->super
+ && rule->super->n_actions == 1
+ && action_outputs_to_port(&rule->super->actions[0],
+ htons(OFPP_CONTROLLER)));
}
static void
(1u << OFPAT_SET_NW_DST) |
(1u << OFPAT_SET_NW_TOS) |
(1u << OFPAT_SET_TP_SRC) |
- (1u << OFPAT_SET_TP_DST));
+ (1u << OFPAT_SET_TP_DST) |
+ (1u << OFPAT_ENQUEUE));
PORT_ARRAY_FOR_EACH (port, &p->ports, port_no) {
hton_ofp_phy_port(ofpbuf_put(buf, &port->opp, sizeof port->opp));
}
static void
-add_controller_action(struct odp_actions *actions,
- const struct ofp_action_output *oao)
+add_controller_action(struct odp_actions *actions, uint16_t max_len)
{
union odp_action *a = odp_actions_add(actions, ODPAT_CONTROLLER);
- a->controller.arg = ntohs(oao->max_len);
+ a->controller.arg = max_len;
}
struct action_xlate_ctx {
}
static void
-xlate_output_action(struct action_xlate_ctx *ctx,
- const struct ofp_action_output *oao)
+xlate_output_action__(struct action_xlate_ctx *ctx,
+ uint16_t port, uint16_t max_len)
{
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)) {
+ switch (port) {
case OFPP_IN_PORT:
add_output_action(ctx, ctx->flow.in_port);
break;
add_output_group_action(ctx->out, DP_GROUP_ALL, &ctx->nf_output_iface);
break;
case OFPP_CONTROLLER:
- add_controller_action(ctx->out, oao);
+ add_controller_action(ctx->out, max_len);
break;
case OFPP_LOCAL:
add_output_action(ctx, ODPP_LOCAL);
break;
default:
- odp_port = ofp_port_to_odp_port(ntohs(oao->port));
+ odp_port = ofp_port_to_odp_port(port);
if (odp_port != ctx->flow.in_port) {
add_output_action(ctx, odp_port);
}
}
}
+static void
+xlate_output_action(struct action_xlate_ctx *ctx,
+ const struct ofp_action_output *oao)
+{
+ xlate_output_action__(ctx, ntohs(oao->port), ntohs(oao->max_len));
+}
+
+/* If the final ODP action in 'ctx' is "pop priority", drop it, as an
+ * optimization, because we're going to add another action that sets the
+ * priority immediately after, or because there are no actions following the
+ * pop. */
+static void
+remove_pop_action(struct action_xlate_ctx *ctx)
+{
+ size_t n = ctx->out->n_actions;
+ if (n > 0 && ctx->out->actions[n - 1].type == ODPAT_POP_PRIORITY) {
+ ctx->out->n_actions--;
+ }
+}
+
+static void
+xlate_enqueue_action(struct action_xlate_ctx *ctx,
+ const struct ofp_action_enqueue *oae)
+{
+ uint16_t ofp_port, odp_port;
+ uint32_t priority;
+ int error;
+
+ error = dpif_queue_to_priority(ctx->ofproto->dpif, ntohl(oae->queue_id),
+ &priority);
+ if (error) {
+ /* Fall back to ordinary output action. */
+ xlate_output_action__(ctx, ntohs(oae->port), 0);
+ return;
+ }
+
+ /* Figure out ODP output port. */
+ ofp_port = ntohs(oae->port);
+ if (ofp_port != OFPP_IN_PORT) {
+ odp_port = ofp_port_to_odp_port(ofp_port);
+ } else {
+ odp_port = ctx->flow.in_port;
+ }
+
+ /* Add ODP actions. */
+ remove_pop_action(ctx);
+ odp_actions_add(ctx->out, ODPAT_SET_PRIORITY)->priority.priority
+ = priority;
+ add_output_action(ctx, odp_port);
+ odp_actions_add(ctx->out, ODPAT_POP_PRIORITY);
+
+ /* Update NetFlow output port. */
+ if (ctx->nf_output_iface == NF_OUT_DROP) {
+ ctx->nf_output_iface = odp_port;
+ } else if (ctx->nf_output_iface != NF_OUT_FLOOD) {
+ ctx->nf_output_iface = NF_OUT_MULTI;
+ }
+}
+
static void
xlate_nicira_action(struct action_xlate_ctx *ctx,
const struct nx_action_header *nah)
break;
/* If you add a new action here that modifies flow data, don't forget to
- * update the flow key in ctx->flow in the same key. */
+ * update the flow key in ctx->flow at the same time. */
default:
VLOG_DBG_RL(&rl, "unknown Nicira action type %"PRIu16, subtype);
case OFPAT_STRIP_VLAN:
odp_actions_add(ctx->out, ODPAT_STRIP_VLAN);
- ctx->flow.dl_vlan = OFP_VLAN_NONE;
+ ctx->flow.dl_vlan = htons(OFP_VLAN_NONE);
ctx->flow.dl_vlan_pcp = 0;
break;
xlate_nicira_action(ctx, (const struct nx_action_header *) ia);
break;
+ case OFPAT_ENQUEUE:
+ xlate_enqueue_action(ctx, (const struct ofp_action_enqueue *) ia);
+ break;
+
default:
VLOG_DBG_RL(&rl, "unknown action type %"PRIu16, type);
break;
ctx.may_set_up_flow = true;
ctx.nf_output_iface = NF_OUT_DROP;
do_xlate_actions(in, n_in, &ctx);
+ remove_pop_action(&ctx);
/* Check with in-band control to see if we're allowed to set up this
* flow. */
return 0;
}
+struct queue_stats_cbdata {
+ struct ofconn *ofconn;
+ struct ofpbuf *msg;
+ uint16_t port_no;
+};
+
+static void
+put_queue_stats(struct queue_stats_cbdata *cbdata, uint32_t queue_id,
+ const struct netdev_queue_stats *stats)
+{
+ struct ofp_queue_stats *reply;
+
+ reply = append_stats_reply(sizeof *reply, cbdata->ofconn, &cbdata->msg);
+ reply->port_no = htons(cbdata->port_no);
+ memset(reply->pad, 0, sizeof reply->pad);
+ reply->queue_id = htonl(queue_id);
+ reply->tx_bytes = htonll(stats->tx_bytes);
+ reply->tx_packets = htonll(stats->tx_packets);
+ reply->tx_errors = htonll(stats->tx_errors);
+}
+
+static void
+handle_queue_stats_dump_cb(uint32_t queue_id,
+ struct netdev_queue_stats *stats,
+ void *cbdata_)
+{
+ struct queue_stats_cbdata *cbdata = cbdata_;
+
+ put_queue_stats(cbdata, queue_id, stats);
+}
+
+static void
+handle_queue_stats_for_port(struct ofport *port, uint16_t port_no,
+ uint32_t queue_id,
+ struct queue_stats_cbdata *cbdata)
+{
+ cbdata->port_no = port_no;
+ if (queue_id == OFPQ_ALL) {
+ netdev_dump_queue_stats(port->netdev,
+ handle_queue_stats_dump_cb, cbdata);
+ } else {
+ struct netdev_queue_stats stats;
+
+ netdev_get_queue_stats(port->netdev, queue_id, &stats);
+ put_queue_stats(cbdata, queue_id, &stats);
+ }
+}
+
+static int
+handle_queue_stats_request(struct ofproto *ofproto, struct ofconn *ofconn,
+ const struct ofp_stats_request *osr,
+ size_t arg_size)
+{
+ struct ofp_queue_stats_request *qsr;
+ struct queue_stats_cbdata cbdata;
+ struct ofport *port;
+ unsigned int port_no;
+ uint32_t queue_id;
+
+ if (arg_size != sizeof *qsr) {
+ return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
+ }
+ qsr = (struct ofp_queue_stats_request *) osr->body;
+
+ COVERAGE_INC(ofproto_queue_req);
+
+ cbdata.ofconn = ofconn;
+ cbdata.msg = start_stats_reply(osr, 128);
+
+ port_no = ntohs(qsr->port_no);
+ queue_id = ntohl(qsr->queue_id);
+ if (port_no == OFPP_ALL) {
+ PORT_ARRAY_FOR_EACH (port, &ofproto->ports, port_no) {
+ handle_queue_stats_for_port(port, port_no, queue_id, &cbdata);
+ }
+ } else if (port_no < ofproto->max_ports) {
+ port = port_array_get(&ofproto->ports, port_no);
+ if (port) {
+ handle_queue_stats_for_port(port, port_no, queue_id, &cbdata);
+ }
+ } else {
+ ofpbuf_delete(cbdata.msg);
+ return ofp_mkerr(OFPET_QUEUE_OP_FAILED, OFPQOFC_BAD_PORT);
+ }
+ queue_tx(cbdata.msg, ofconn, ofconn->reply_counter);
+
+ return 0;
+}
+
static int
handle_stats_request(struct ofproto *p, struct ofconn *ofconn,
struct ofp_header *oh)
case OFPST_PORT:
return handle_port_stats_request(p, ofconn, osr, arg_size);
+ case OFPST_QUEUE:
+ return handle_queue_stats_request(p, ofconn, osr, arg_size);
+
case OFPST_VENDOR:
return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_VENDOR);
handle_flow_mod(struct ofproto *p, struct ofconn *ofconn,
struct ofp_flow_mod *ofm)
{
+ struct ofp_match orig_match;
size_t n_actions;
int error;
return ofp_mkerr(OFPET_FLOW_MOD_FAILED, OFPFMFC_ALL_TABLES_FULL);
}
+ /* Normalize ofp->match. If normalization actually changes anything, then
+ * log the differences. */
+ ofm->match.pad1[0] = ofm->match.pad2[0] = 0;
+ orig_match = ofm->match;
normalize_match(&ofm->match);
+ if (memcmp(&ofm->match, &orig_match, sizeof orig_match)) {
+ static struct vlog_rate_limit normal_rl = VLOG_RATE_LIMIT_INIT(1, 1);
+ if (!VLOG_DROP_INFO(&normal_rl)) {
+ char *old = ofp_match_to_literal_string(&orig_match);
+ char *new = ofp_match_to_literal_string(&ofm->match);
+ VLOG_INFO("%s: normalization changed ofp_match, details:",
+ rconn_get_name(ofconn->rconn));
+ VLOG_INFO(" pre: %s", old);
+ VLOG_INFO("post: %s", new);
+ free(old);
+ free(new);
+ }
+ }
+
if (!ofm->match.wildcards) {
ofm->priority = htons(UINT16_MAX);
}
/* Learn source MAC (but don't try to learn from revalidation). */
if (packet != NULL) {
tag_type rev_tag = mac_learning_learn(ofproto->ml, flow->dl_src,
- 0, flow->in_port);
+ 0, flow->in_port,
+ GRAT_ARP_LOCK_NONE);
if (rev_tag) {
/* The log messages here could actually be useful in debugging,
* so keep the rate limit relatively high. */
}
/* Determine output port. */
- out_port = mac_learning_lookup_tag(ofproto->ml, flow->dl_dst, 0, tags);
+ out_port = mac_learning_lookup_tag(ofproto->ml, flow->dl_dst, 0, tags,
+ NULL);
if (out_port < 0) {
add_output_group_action(actions, DP_GROUP_FLOOD, nf_output_iface);
} else if (out_port != flow->in_port) {