/*
- * Copyright (c) 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
uint64_t packets, uint64_t bytes,
long long int used);
-static uint32_t rule_calculate_tag(const struct flow *,
+static tag_type rule_calculate_tag(const struct flow *,
const struct flow_wildcards *,
uint32_t basis);
static void rule_invalidate(const struct rule_dpif *);
static struct ofbundle *lookup_input_bundle(struct ofproto_dpif *,
uint16_t in_port, bool warn);
+/* A controller may use OFPP_NONE as the ingress port to indicate that
+ * it did not arrive on a "real" port. 'ofpp_none_bundle' exists for
+ * when an input bundle is needed for validation (e.g., mirroring or
+ * OFPP_NORMAL processing). It is not connected to an 'ofproto' or have
+ * any 'port' structs, so care must be taken when dealing with it. */
+static struct ofbundle ofpp_none_bundle = {
+ .name = "OFPP_NONE",
+ .vlan_mode = PORT_VLAN_TRUNK
+};
+
static void stp_run(struct ofproto_dpif *ofproto);
static void stp_wait(struct ofproto_dpif *ofproto);
+static int set_stp_port(struct ofport *,
+ const struct ofproto_port_stp_settings *);
static bool ofbundle_includes_vlan(const struct ofbundle *, uint16_t vlan);
* revalidating without a packet to refer to. */
const struct ofpbuf *packet;
- /* Should OFPP_NORMAL MAC learning and NXAST_LEARN actions execute? We
- * want to execute them if we are actually processing a packet, or if we
- * are accounting for packets that the datapath has processed, but not if
- * we are just revalidating. */
- bool may_learn;
+ /* Should OFPP_NORMAL update the MAC learning table? We want to update it
+ * if we are actually processing a packet, or if we are accounting for
+ * packets that the datapath has processed, but not if we are just
+ * revalidating. */
+ bool may_learn_macs;
+
+ /* Should "learn" actions update the flow table? We want to update it if
+ * we are actually processing a packet, or in most cases if we are
+ * accounting for packets that the datapath has processed, but not if we
+ * are just revalidating. */
+ bool may_flow_mod;
/* If nonnull, called just before executing a resubmit action.
*
long long int used);
static void facet_reset_counters(struct facet *);
static void facet_push_stats(struct facet *);
-static void facet_account(struct ofproto_dpif *, struct facet *);
+static void facet_account(struct ofproto_dpif *, struct facet *,
+ bool may_flow_mod);
static bool facet_is_controller_flow(struct facet *);
ofproto->sflow = NULL;
ofproto->stp = NULL;
hmap_init(&ofproto->bundles);
- ofproto->ml = mac_learning_create();
+ ofproto->ml = mac_learning_create(MAC_ENTRY_DEFAULT_IDLE_TIME);
for (i = 0; i < MAX_MIRRORS; i++) {
ofproto->mirrors[i] = NULL;
}
stp_set_max_age(ofproto->stp, s->max_age);
stp_set_forward_delay(ofproto->stp, s->fwd_delay);
} else {
+ struct ofport *ofport;
+
+ HMAP_FOR_EACH (ofport, hmap_node, &ofproto->up.ports) {
+ set_stp_port(ofport, NULL);
+ }
+
stp_destroy(ofproto->stp);
ofproto->stp = NULL;
}
/* Revalidate cached flows whenever forward_bpdu option changes. */
ofproto->need_revalidate = true;
}
+
+static void
+set_mac_idle_time(struct ofproto *ofproto_, unsigned int idle_time)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+ mac_learning_set_idle_time(ofproto->ml, idle_time);
+}
\f
/* Ports. */
subfacet->dp_byte_count = stats->n_bytes;
subfacet_update_time(p, subfacet, stats->used);
- facet_account(p, facet);
+ facet_account(p, facet, true);
facet_push_stats(facet);
} else {
if (!VLOG_DROP_WARN(&rl)) {
{
struct subfacet *subfacet, *next_subfacet;
+ assert(!list_is_empty(&facet->subfacets));
+
+ /* First uninstall all of the subfacets to get final statistics. */
+ LIST_FOR_EACH (subfacet, list_node, &facet->subfacets) {
+ subfacet_uninstall(ofproto, subfacet);
+ }
+
+ /* Flush the final stats to the rule.
+ *
+ * This might require us to have at least one subfacet around so that we
+ * can use its actions for accounting in facet_account(), which is why we
+ * have uninstalled but not yet destroyed the subfacets. */
+ facet_flush_stats(ofproto, facet);
+
+ /* Now we're really all done so destroy everything. */
LIST_FOR_EACH_SAFE (subfacet, next_subfacet, list_node,
&facet->subfacets) {
subfacet_destroy__(ofproto, subfacet);
}
-
- facet_flush_stats(ofproto, facet);
hmap_remove(&ofproto->facets, &facet->hmap_node);
list_remove(&facet->list_node);
facet_free(facet);
}
static void
-facet_account(struct ofproto_dpif *ofproto, struct facet *facet)
+facet_account(struct ofproto_dpif *ofproto, struct facet *facet,
+ bool may_flow_mod)
{
uint64_t n_bytes;
struct subfacet *subfacet;
action_xlate_ctx_init(&ctx, ofproto, &facet->flow,
facet->flow.vlan_tci, NULL);
- ctx.may_learn = true;
+ ctx.may_learn_macs = true;
+ ctx.may_flow_mod = may_flow_mod;
ofpbuf_delete(xlate_actions(&ctx, facet->rule->up.actions,
facet->rule->up.n_actions));
}
}
facet_push_stats(facet);
- facet_account(ofproto, facet);
+ facet_account(ofproto, facet, false);
if (ofproto->netflow && !facet_is_controller_flow(facet)) {
struct ofexpired expired;
{
struct facet *facet = subfacet->facet;
- subfacet_destroy__(ofproto, subfacet);
- if (list_is_empty(&facet->subfacets)) {
+ if (list_is_singleton(&facet->subfacets)) {
+ /* facet_remove() needs at least one subfacet (it will remove it). */
facet_remove(ofproto, facet);
+ } else {
+ subfacet_destroy__(ofproto, subfacet);
}
}
if (table_id > 0 && table_id < N_TABLES) {
struct table_dpif *table = &ofproto->tables[table_id];
if (table->other_table) {
- ctx->tags |= (rule
+ ctx->tags |= (rule && rule->tag
? rule->tag
: rule_calculate_tag(&ctx->flow,
&table->other_table->wc,
case OFPP_CONTROLLER:
compose_controller_action(ctx, max_len);
break;
- case OFPP_LOCAL:
- compose_output_action(ctx, OFPP_LOCAL);
- break;
case OFPP_NONE:
break;
+ case OFPP_LOCAL:
default:
if (port != ctx->flow.in_port) {
compose_output_action(ctx, port);
case OFPUTIL_NXAST_LEARN:
ctx->has_learn = true;
- if (ctx->may_learn) {
+ if (ctx->may_flow_mod) {
xlate_learn_action(ctx, (const struct nx_action_learn *) ia);
}
break;
ctx->base_flow.tun_id = 0;
ctx->base_flow.vlan_tci = initial_tci;
ctx->packet = packet;
- ctx->may_learn = packet != NULL;
+ ctx->may_learn_macs = packet != NULL;
+ ctx->may_flow_mod = packet != NULL;
ctx->resubmit_hook = NULL;
}
static bool
input_vid_is_valid(uint16_t vid, struct ofbundle *in_bundle, bool warn)
{
+ /* Allow any VID on the OFPP_NONE port. */
+ if (in_bundle == &ofpp_none_bundle) {
+ return true;
+ }
+
switch (in_bundle->vlan_mode) {
case PORT_VLAN_ACCESS:
if (vid) {
{
struct mac_entry *mac;
+ /* Don't learn the OFPP_NONE port. */
+ if (in_bundle == &ofpp_none_bundle) {
+ return;
+ }
+
if (!mac_learning_may_learn(ofproto->ml, flow->dl_src, vlan)) {
return;
}
{
struct ofport_dpif *ofport;
+ /* Special-case OFPP_NONE, which a controller may use as the ingress
+ * port for traffic that it is sourcing. */
+ if (in_port == OFPP_NONE) {
+ return &ofpp_none_bundle;
+ }
+
/* Find the port and bundle for the received packet. */
ofport = get_ofp_port(ofproto, in_port);
if (ofport && ofport->bundle) {
return;
}
- /* We know 'in_port' exists, since lookup_input_bundle() succeeded. */
+ /* We know 'in_port' exists unless it is "ofpp_none_bundle",
+ * since lookup_input_bundle() succeeded. */
in_port = get_ofp_port(ctx->ofproto, ctx->flow.in_port);
/* Drop malformed frames. */
vlan = input_vid_to_vlan(in_bundle, vid);
/* Check other admissibility requirements. */
- if (!is_admissible(ctx->ofproto, &ctx->flow, in_port, vlan, &ctx->tags)) {
+ if (in_port &&
+ !is_admissible(ctx->ofproto, &ctx->flow, in_port, vlan, &ctx->tags)) {
return;
}
/* Learn source MAC. */
- if (ctx->may_learn) {
+ if (ctx->may_learn_macs) {
update_learning_table(ctx->ofproto, &ctx->flow, vlan, in_bundle);
}
/* Calculates the tag to use for 'flow' and wildcards 'wc' when it is inserted
* into an OpenFlow table with the given 'basis'. */
-static uint32_t
+static tag_type
rule_calculate_tag(const struct flow *flow, const struct flow_wildcards *wc,
uint32_t secret)
{
static void
ofproto_unixctl_fdb_flush(struct unixctl_conn *conn,
- const char *args, void *aux OVS_UNUSED)
+ const char *args, void *aux OVS_UNUSED)
{
- const struct ofproto_dpif *ofproto;
+ struct ofproto_dpif *ofproto;
- ofproto = ofproto_dpif_lookup(args);
- if (!ofproto) {
- unixctl_command_reply(conn, 501, "no such bridge");
- return;
+ if (args[0] != '\0') {
+ ofproto = ofproto_dpif_lookup(args);
+ if (!ofproto) {
+ unixctl_command_reply(conn, 501, "no such bridge");
+ return;
+ }
+ mac_learning_flush(ofproto->ml);
+ ofproto->need_revalidate = true;
+ } else {
+ HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
+ mac_learning_flush(ofproto->ml);
+ ofproto->need_revalidate = true;
+ }
}
- mac_learning_flush(ofproto->ml);
unixctl_command_reply(conn, 200, "table successfully flushed");
}
struct ofbundle *bundle = e->port.p;
ds_put_format(&ds, "%5d %4d "ETH_ADDR_FMT" %3d\n",
ofbundle_get_a_port(bundle)->odp_port,
- e->vlan, ETH_ADDR_ARGS(e->mac), mac_entry_age(e));
+ e->vlan, ETH_ADDR_ARGS(e->mac),
+ mac_entry_age(ofproto->ml, e));
}
unixctl_command_reply(conn, 200, ds_cstr(&ds));
ds_destroy(&ds);
unixctl_command_register("ofproto/trace",
"bridge {tun_id in_port packet | odp_flow [-generate]}",
ofproto_unixctl_trace, NULL);
- unixctl_command_register("fdb/flush", "bridge", ofproto_unixctl_fdb_flush,
- NULL);
+ unixctl_command_register("fdb/flush", "[bridge]",
+ ofproto_unixctl_fdb_flush, NULL);
unixctl_command_register("fdb/show", "bridge", ofproto_unixctl_fdb_show,
NULL);
unixctl_command_register("ofproto/clog", "", ofproto_dpif_clog, NULL);
set_flood_vlans,
is_mirror_output_bundle,
forward_bpdu_changed,
+ set_mac_idle_time,
set_realdev,
};