struct ofbundle *out; /* Output port or NULL. */
int out_vlan; /* Output VLAN or -1. */
mirror_mask_t dup_mirrors; /* Bitmap of mirrors with the same output. */
+
+ /* Counters. */
+ int64_t packet_count; /* Number of packets sent. */
+ int64_t byte_count; /* Number of bytes sent. */
};
static void mirror_destroy(struct ofmirror *);
+static void update_mirror_stats(struct ofproto_dpif *ofproto,
+ mirror_mask_t mirrors,
+ uint64_t packets, uint64_t bytes);
-/* A group of one or more OpenFlow ports. */
-#define OFBUNDLE_FLOOD ((struct ofbundle *) 1)
struct ofbundle {
struct ofproto_dpif *ofproto; /* Owning ofproto. */
struct hmap_node hmap_node; /* In struct ofproto's "bundles" hmap. */
static void bundle_del_port(struct ofport_dpif *);
static void bundle_run(struct ofbundle *);
static void bundle_wait(struct ofbundle *);
+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);
bool has_learn; /* Actions include NXAST_LEARN? */
bool has_normal; /* Actions output to OFPP_NORMAL? */
uint16_t nf_output_iface; /* Output interface index for NetFlow. */
+ mirror_mask_t mirrors; /* Bitmap of associated mirrors. */
/* xlate_actions() initializes and uses these members, but the client has no
* reason to look at them. */
uint64_t byte_count; /* Number of bytes received. */
/* Resubmit statistics. */
- uint64_t rs_packet_count; /* Packets pushed to resubmit children. */
- uint64_t rs_byte_count; /* Bytes pushed to resubmit children. */
- long long int rs_used; /* Used time pushed to resubmit children. */
+ uint64_t prev_packet_count; /* Number of packets from last stats push. */
+ uint64_t prev_byte_count; /* Number of bytes from last stats push. */
+ long long int prev_used; /* Used time from last stats push. */
/* Accounting. */
uint64_t accounted_bytes; /* Bytes processed by facet_account(). */
bool has_learn; /* Actions include NXAST_LEARN? */
bool has_normal; /* Actions output to OFPP_NORMAL? */
tag_type tags; /* Tags that would require revalidation. */
+ mirror_mask_t mirrors; /* Bitmap of dependent mirrors. */
};
static struct facet *facet_create(struct rule_dpif *, const struct flow *);
const struct flow *,
const struct nlattr *odp_actions,
size_t actions_len,
- struct ofpbuf *packet);
+ struct ofpbuf *packet, bool clone);
static void facet_flush_stats(struct ofproto_dpif *, struct facet *);
const struct nlattr *key,
size_t key_len, ovs_be16 initial_tci);
static struct subfacet *subfacet_find(struct ofproto_dpif *,
- const struct nlattr *key, size_t key_len,
- const struct flow *);
+ const struct nlattr *key, size_t key_len);
static void subfacet_destroy(struct ofproto_dpif *, struct subfacet *);
static void subfacet_destroy__(struct ofproto_dpif *, struct subfacet *);
static void subfacet_reset_dp_stats(struct subfacet *,
};
struct ofproto_dpif {
+ struct hmap_node all_ofproto_dpifs_node; /* In 'all_ofproto_dpifs'. */
struct ofproto up;
struct dpif *dpif;
int max_ports;
* for debugging the asynchronous flow_mod implementation.) */
static bool clogged;
+/* All existing ofproto_dpif instances, indexed by ->up.name. */
+static struct hmap all_ofproto_dpifs = HMAP_INITIALIZER(&all_ofproto_dpifs);
+
static void ofproto_dpif_unixctl_init(void);
static struct ofproto_dpif *
struct ofbundle *);
/* Upcalls. */
#define FLOW_MISS_MAX_BATCH 50
-
-static void handle_upcall(struct ofproto_dpif *, struct dpif_upcall *);
-static void handle_miss_upcalls(struct ofproto_dpif *,
- struct dpif_upcall *, size_t n);
+static int handle_upcalls(struct ofproto_dpif *, unsigned int max_batch);
/* Flow expiration. */
static int expire(struct ofproto_dpif *);
static size_t
compose_sflow_action(const struct ofproto_dpif *, struct ofpbuf *odp_actions,
const struct flow *, uint32_t odp_port);
+static void add_mirror_actions(struct action_xlate_ctx *ctx,
+ const struct flow *flow);
/* Global variables. */
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
\f
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;
}
hmap_init(&ofproto->vlandev_map);
hmap_init(&ofproto->realdev_vid_map);
+ hmap_insert(&all_ofproto_dpifs, &ofproto->all_ofproto_dpifs_node,
+ hash_string(ofproto->up.name, 0));
+
*n_tablesp = N_TABLES;
return 0;
}
struct classifier *table;
int i;
+ hmap_remove(&all_ofproto_dpifs, &ofproto->all_ofproto_dpifs_node);
complete_operations(ofproto);
OFPROTO_FOR_EACH_TABLE (table, &ofproto->up) {
dpif_close(ofproto->dpif);
}
+static int
+run_fast(struct ofproto *ofproto_)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+ unsigned int work;
+
+ /* Handle one or more batches of upcalls, until there's nothing left to do
+ * or until we do a fixed total amount of work.
+ *
+ * We do work in batches because it can be much cheaper to set up a number
+ * of flows and fire off their patches all at once. We do multiple batches
+ * because in some cases handling a packet can cause another packet to be
+ * queued almost immediately as part of the return flow. Both
+ * optimizations can make major improvements on some benchmarks and
+ * presumably for real traffic as well. */
+ work = 0;
+ while (work < FLOW_MISS_MAX_BATCH) {
+ int retval = handle_upcalls(ofproto, FLOW_MISS_MAX_BATCH - work);
+ if (retval <= 0) {
+ return -retval;
+ }
+ work += retval;
+ }
+ return 0;
+}
+
static int
run(struct ofproto *ofproto_)
{
struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
- struct dpif_upcall misses[FLOW_MISS_MAX_BATCH];
struct ofport_dpif *ofport;
struct ofbundle *bundle;
- size_t n_misses;
- int i;
+ int error;
if (!clogged) {
complete_operations(ofproto);
}
dpif_run(ofproto->dpif);
- n_misses = 0;
- for (i = 0; i < FLOW_MISS_MAX_BATCH; i++) {
- struct dpif_upcall *upcall = &misses[n_misses];
- int error;
-
- error = dpif_recv(ofproto->dpif, upcall);
- if (error) {
- if (error == ENODEV && n_misses == 0) {
- return error;
- }
- break;
- }
-
- if (upcall->type == DPIF_UC_MISS) {
- /* Handle it later. */
- n_misses++;
- } else {
- handle_upcall(ofproto, upcall);
- }
+ error = run_fast(ofproto_);
+ if (error) {
+ return error;
}
- handle_miss_upcalls(ofproto, misses, n_misses);
-
if (timer_expired(&ofproto->next_expiration)) {
int delay = expire(ofproto);
timer_set_duration(&ofproto->next_expiration, delay);
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;
}
\f
/* Bundles. */
-/* Expires all MAC learning entries associated with 'port' and forces ofproto
- * to revalidate every flow. */
+/* Expires all MAC learning entries associated with 'bundle' and forces its
+ * ofproto to revalidate every flow.
+ *
+ * Normally MAC learning entries are removed only from the ofproto associated
+ * with 'bundle', but if 'all_ofprotos' is true, then the MAC learning entries
+ * are removed from every ofproto. When patch ports and SLB bonds are in use
+ * and a VM migration happens and the gratuitous ARPs are somehow lost, this
+ * avoids a MAC_ENTRY_IDLE_TIME delay before the migrated VM can communicate
+ * with the host from which it migrated. */
static void
-bundle_flush_macs(struct ofbundle *bundle)
+bundle_flush_macs(struct ofbundle *bundle, bool all_ofprotos)
{
struct ofproto_dpif *ofproto = bundle->ofproto;
struct mac_learning *ml = ofproto->ml;
ofproto->need_revalidate = true;
LIST_FOR_EACH_SAFE (mac, next_mac, lru_node, &ml->lrus) {
if (mac->port.p == bundle) {
+ if (all_ofprotos) {
+ struct ofproto_dpif *o;
+
+ HMAP_FOR_EACH (o, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
+ if (o != ofproto) {
+ struct mac_entry *e;
+
+ e = mac_learning_lookup(o->ml, mac->mac, mac->vlan,
+ NULL);
+ if (e) {
+ tag_set_add(&o->revalidate_set, e->tag);
+ mac_learning_expire(o->ml, e);
+ }
+ }
+ }
+ }
+
mac_learning_expire(ml, mac);
}
}
bundle_del_port(port);
}
- bundle_flush_macs(bundle);
+ bundle_flush_macs(bundle, true);
hmap_remove(&ofproto->bundles, &bundle->hmap_node);
free(bundle->name);
free(bundle->trunks);
/* If we changed something that would affect MAC learning, un-learn
* everything on this port and force flow revalidation. */
if (need_flush) {
- bundle_flush_macs(bundle);
+ bundle_flush_macs(bundle, false);
}
return 0;
mirror_update_dups(ofproto);
}
+static int
+mirror_get_stats(struct ofproto *ofproto_, void *aux,
+ uint64_t *packets, uint64_t *bytes)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+ struct ofmirror *mirror = mirror_lookup(ofproto, aux);
+
+ if (!mirror) {
+ *packets = *bytes = UINT64_MAX;
+ return 0;
+ }
+
+ *packets = mirror->packet_count;
+ *bytes = mirror->byte_count;
+
+ return 0;
+}
+
static int
set_flood_vlans(struct ofproto *ofproto_, unsigned long *flood_vlans)
{
/* 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. */
miss->initial_tci);
LIST_FOR_EACH_SAFE (packet, next_packet, list_node, &miss->packets) {
+ struct dpif_flow_stats stats;
+
list_remove(&packet->list_node);
ofproto->n_matches++;
if (!facet->may_install || !subfacet->actions) {
subfacet_make_actions(ofproto, subfacet, packet);
}
+
+ /* Credit statistics to subfacet for this packet. We must do this now
+ * because execute_controller_action() below may destroy 'packet'. */
+ dpif_flow_stats_extract(&facet->flow, packet, &stats);
+ subfacet_update_stats(ofproto, subfacet, &stats);
+
if (!execute_controller_action(ofproto, &facet->flow,
subfacet->actions,
- subfacet->actions_len, packet)) {
+ subfacet->actions_len, packet, true)) {
struct flow_miss_op *op = &ops[(*n_ops)++];
struct dpif_execute *execute = &op->dpif_op.execute;
+ if (flow->vlan_tci != subfacet->initial_tci) {
+ /* This packet was received on a VLAN splinter port. We added
+ * a VLAN to the packet to make the packet resemble the flow,
+ * but the actions were composed assuming that the packet
+ * contained no VLAN. So, we must remove the VLAN header from
+ * the packet before trying to execute the actions. */
+ eth_pop_vlan(packet);
+ }
+
op->subfacet = subfacet;
execute->type = DPIF_OP_EXECUTE;
execute->key = miss->key;
}
}
+/* Like odp_flow_key_to_flow(), this function converts the 'key_len' bytes of
+ * OVS_KEY_ATTR_* attributes in 'key' to a flow structure in 'flow' and returns
+ * an ODP_FIT_* value that indicates how well 'key' fits our expectations for
+ * what a flow key should contain.
+ *
+ * This function also includes some logic to help make VLAN splinters
+ * transparent to the rest of the upcall processing logic. In particular, if
+ * the extracted in_port is a VLAN splinter port, it replaces flow->in_port by
+ * the "real" port, sets flow->vlan_tci correctly for the VLAN of the VLAN
+ * splinter port, and pushes a VLAN header onto 'packet' (if it is nonnull).
+ *
+ * Sets '*initial_tci' to the VLAN TCI with which the packet was really
+ * received, that is, the actual VLAN TCI extracted by odp_flow_key_to_flow().
+ * (This differs from the value returned in flow->vlan_tci only for packets
+ * received on VLAN splinters.)
+ */
static enum odp_key_fitness
ofproto_dpif_extract_flow_key(const struct ofproto_dpif *ofproto,
const struct nlattr *key, size_t key_len,
- struct flow *flow, ovs_be16 *initial_tci)
+ struct flow *flow, ovs_be16 *initial_tci,
+ struct ofpbuf *packet)
{
enum odp_key_fitness fitness;
uint16_t realdev;
* with the VLAN device's VLAN ID. */
flow->in_port = realdev;
flow->vlan_tci = htons((vid & VLAN_VID_MASK) | VLAN_CFI);
+ if (packet) {
+ /* Make the packet resemble the flow, so that it gets sent to an
+ * OpenFlow controller properly, so that it looks correct for
+ * sFlow, and so that flow_extract() will get the correct vlan_tci
+ * if it is called on 'packet'.
+ *
+ * The allocated space inside 'packet' probably also contains
+ * 'key', that is, both 'packet' and 'key' are probably part of a
+ * struct dpif_upcall (see the large comment on that structure
+ * definition), so pushing data on 'packet' is in general not a
+ * good idea since it could overwrite 'key' or free it as a side
+ * effect. However, it's OK in this special case because we know
+ * that 'packet' is inside a Netlink attribute: pushing 4 bytes
+ * will just overwrite the 4-byte "struct nlattr", which is fine
+ * since we don't need that header anymore. */
+ eth_push_vlan(packet, flow->vlan_tci);
+ }
/* Let the caller know that we can't reproduce 'key' from 'flow'. */
if (fitness == ODP_FIT_PERFECT) {
* then set 'flow''s header pointers. */
fitness = ofproto_dpif_extract_flow_key(ofproto,
upcall->key, upcall->key_len,
- &flow, &initial_tci);
+ &flow, &initial_tci,
+ upcall->packet);
if (fitness == ODP_FIT_ERROR) {
+ ofpbuf_delete(upcall->packet);
continue;
}
flow_extract(upcall->packet, flow.priority, flow.tun_id,
fitness = ofproto_dpif_extract_flow_key(ofproto, upcall->key,
upcall->key_len, &flow,
- &initial_tci);
+ &initial_tci, upcall->packet);
if (fitness == ODP_FIT_ERROR) {
+ ofpbuf_delete(upcall->packet);
return;
}
&flow, false);
} else {
VLOG_WARN_RL(&rl, "invalid user cookie : 0x%"PRIx64, upcall->userdata);
+ ofpbuf_delete(upcall->packet);
}
}
-static void
-handle_upcall(struct ofproto_dpif *ofproto, struct dpif_upcall *upcall)
+static int
+handle_upcalls(struct ofproto_dpif *ofproto, unsigned int max_batch)
{
- switch (upcall->type) {
- case DPIF_UC_ACTION:
- handle_userspace_upcall(ofproto, upcall);
- break;
+ struct dpif_upcall misses[FLOW_MISS_MAX_BATCH];
+ int n_misses;
+ int i;
- case DPIF_UC_MISS:
- /* The caller handles these. */
- NOT_REACHED();
+ assert (max_batch <= FLOW_MISS_MAX_BATCH);
- case DPIF_N_UC_TYPES:
- default:
- VLOG_WARN_RL(&rl, "upcall has unexpected type %"PRIu32, upcall->type);
- break;
+ n_misses = 0;
+ for (i = 0; i < max_batch; i++) {
+ struct dpif_upcall *upcall = &misses[n_misses];
+ int error;
+
+ error = dpif_recv(ofproto->dpif, upcall);
+ if (error) {
+ break;
+ }
+
+ switch (upcall->type) {
+ case DPIF_UC_ACTION:
+ handle_userspace_upcall(ofproto, upcall);
+ break;
+
+ case DPIF_UC_MISS:
+ /* Handle it later. */
+ n_misses++;
+ break;
+
+ case DPIF_N_UC_TYPES:
+ default:
+ VLOG_WARN_RL(&rl, "upcall has unexpected type %"PRIu32,
+ upcall->type);
+ break;
+ }
}
+
+ handle_miss_upcalls(ofproto, misses, n_misses);
+
+ return i;
}
\f
/* Flow expiration. */
dpif_flow_dump_start(&dump, p->dpif);
while (dpif_flow_dump_next(&dump, &key, &key_len, NULL, NULL, &stats)) {
- enum odp_key_fitness fitness;
struct subfacet *subfacet;
- struct flow flow;
-
- fitness = odp_flow_key_to_flow(key, key_len, &flow);
- if (fitness == ODP_FIT_ERROR) {
- continue;
- }
- subfacet = subfacet_find(p, key, key_len, &flow);
+ subfacet = subfacet_find(p, key, key_len);
if (subfacet && subfacet->installed) {
struct facet *facet = subfacet->facet;
facet_account(p, facet);
facet_push_stats(facet);
} else {
+ if (!VLOG_DROP_WARN(&rl)) {
+ struct ds s;
+
+ ds_init(&s);
+ odp_flow_key_format(key, key_len, &s);
+ VLOG_WARN("unexpected flow from datapath %s", ds_cstr(&s));
+ ds_destroy(&s);
+ }
+
+ COVERAGE_INC(facet_unexpected);
/* There's a flow in the datapath that we know nothing about, or a
* flow that shouldn't be installed but was anyway. Delete it. */
- COVERAGE_INC(facet_unexpected);
dpif_flow_del(p->dpif, key, key_len, NULL);
}
}
free(facet);
}
+/* If the 'actions_len' bytes of actions in 'odp_actions' are just a single
+ * OVS_ACTION_ATTR_USERSPACE action, executes it internally and returns true.
+ * Otherwise, returns false without doing anything.
+ *
+ * If 'clone' is true, the caller always retains ownership of 'packet'.
+ * Otherwise, ownership is transferred to this function if it returns true. */
static bool
execute_controller_action(struct ofproto_dpif *ofproto,
const struct flow *flow,
const struct nlattr *odp_actions, size_t actions_len,
- struct ofpbuf *packet)
+ struct ofpbuf *packet, bool clone)
{
if (actions_len
&& odp_actions->nla_type == OVS_ACTION_ATTR_USERSPACE
nla = nl_attr_find_nested(odp_actions, OVS_USERSPACE_ATTR_USERDATA);
send_packet_in_action(ofproto, packet, nl_attr_get_u64(nla), flow,
- false);
+ clone);
return true;
} else {
return false;
int error;
if (execute_controller_action(ofproto, flow, odp_actions, actions_len,
- packet)) {
+ packet, false)) {
return true;
}
{
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);
facet->may_install = ctx.may_set_up_flow;
facet->has_learn = ctx.has_learn;
facet->has_normal = ctx.has_normal;
+ facet->mirrors = ctx.mirrors;
if (new_actions) {
i = 0;
LIST_FOR_EACH (subfacet, list_node, &facet->subfacets) {
list_push_back(&new_rule->facets, &facet->list_node);
facet->rule = new_rule;
facet->used = new_rule->up.created;
- facet->rs_used = facet->used;
+ facet->prev_used = facet->used;
}
return true;
{
facet->packet_count = 0;
facet->byte_count = 0;
- facet->rs_packet_count = 0;
- facet->rs_byte_count = 0;
+ facet->prev_packet_count = 0;
+ facet->prev_byte_count = 0;
facet->accounted_bytes = 0;
}
static void
facet_push_stats(struct facet *facet)
{
- uint64_t rs_packets, rs_bytes;
+ uint64_t new_packets, new_bytes;
- assert(facet->packet_count >= facet->rs_packet_count);
- assert(facet->byte_count >= facet->rs_byte_count);
- assert(facet->used >= facet->rs_used);
+ assert(facet->packet_count >= facet->prev_packet_count);
+ assert(facet->byte_count >= facet->prev_byte_count);
+ assert(facet->used >= facet->prev_used);
- rs_packets = facet->packet_count - facet->rs_packet_count;
- rs_bytes = facet->byte_count - facet->rs_byte_count;
+ new_packets = facet->packet_count - facet->prev_packet_count;
+ new_bytes = facet->byte_count - facet->prev_byte_count;
- if (rs_packets || rs_bytes || facet->used > facet->rs_used) {
- facet->rs_packet_count = facet->packet_count;
- facet->rs_byte_count = facet->byte_count;
- facet->rs_used = facet->used;
+ if (new_packets || new_bytes || facet->used > facet->prev_used) {
+ facet->prev_packet_count = facet->packet_count;
+ facet->prev_byte_count = facet->byte_count;
+ facet->prev_used = facet->used;
flow_push_stats(facet->rule, &facet->flow,
- rs_packets, rs_bytes, facet->used);
+ new_packets, new_bytes, facet->used);
+
+ update_mirror_stats(ofproto_dpif_cast(facet->rule->up.ofproto),
+ facet->mirrors, new_packets, new_bytes);
}
}
}
/* Pushes flow statistics to the rules which 'flow' resubmits into given
- * 'rule''s actions. */
+ * 'rule''s actions and mirrors. */
static void
flow_push_stats(const struct rule_dpif *rule,
const struct flow *flow, uint64_t packets, uint64_t bytes,
* 'flow'. Returns the subfacet if one exists, otherwise NULL. */
static struct subfacet *
subfacet_find(struct ofproto_dpif *ofproto,
- const struct nlattr *key, size_t key_len,
- const struct flow *flow)
+ const struct nlattr *key, size_t key_len)
{
uint32_t key_hash = odp_flow_key_hash(key, key_len);
+ enum odp_key_fitness fitness;
+ struct flow flow;
+
+ fitness = odp_flow_key_to_flow(key, key_len, &flow);
+ if (fitness == ODP_FIT_ERROR) {
+ return NULL;
+ }
- return subfacet_find__(ofproto, key, key_len, key_hash, flow);
+ return subfacet_find__(ofproto, key, key_len, key_hash, &flow);
}
/* Uninstalls 'subfacet' from the datapath, if it is installed, removes it from
{
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);
}
}
facet->has_learn = ctx.has_learn;
facet->has_normal = ctx.has_normal;
facet->nf_flow.output_iface = ctx.nf_output_iface;
+ facet->mirrors = ctx.mirrors;
if (subfacet->actions_len != odp_actions->size
|| memcmp(subfacet->actions, odp_actions->data, odp_actions->size)) {
cookie->vlan_tci = base->vlan_tci;
}
-static void
-commit_set_action(struct ofpbuf *odp_actions, enum ovs_key_attr key_type,
- const void *key, size_t key_size)
-{
- size_t offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_SET);
- nl_msg_put_unspec(odp_actions, key_type, key, key_size);
- nl_msg_end_nested(odp_actions, offset);
-}
-
-static void
-commit_set_tun_id_action(const struct flow *flow, struct flow *base,
- struct ofpbuf *odp_actions)
-{
- if (base->tun_id == flow->tun_id) {
- return;
- }
- base->tun_id = flow->tun_id;
-
- commit_set_action(odp_actions, OVS_KEY_ATTR_TUN_ID,
- &base->tun_id, sizeof(base->tun_id));
-}
-
-static void
-commit_set_ether_addr_action(const struct flow *flow, struct flow *base,
- struct ofpbuf *odp_actions)
-{
- struct ovs_key_ethernet eth_key;
-
- if (eth_addr_equals(base->dl_src, flow->dl_src) &&
- eth_addr_equals(base->dl_dst, flow->dl_dst)) {
- return;
- }
-
- memcpy(base->dl_src, flow->dl_src, ETH_ADDR_LEN);
- memcpy(base->dl_dst, flow->dl_dst, ETH_ADDR_LEN);
-
- memcpy(eth_key.eth_src, base->dl_src, ETH_ADDR_LEN);
- memcpy(eth_key.eth_dst, base->dl_dst, ETH_ADDR_LEN);
-
- commit_set_action(odp_actions, OVS_KEY_ATTR_ETHERNET,
- ð_key, sizeof(eth_key));
-}
-
-static void
-commit_vlan_action(const struct flow *flow, struct flow *base,
- struct ofpbuf *odp_actions)
-{
- if (base->vlan_tci == flow->vlan_tci) {
- return;
- }
-
- if (base->vlan_tci & htons(VLAN_CFI)) {
- nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_POP_VLAN);
- }
-
- if (flow->vlan_tci & htons(VLAN_CFI)) {
- struct ovs_action_push_vlan vlan;
-
- vlan.vlan_tpid = htons(ETH_TYPE_VLAN);
- vlan.vlan_tci = flow->vlan_tci;
- nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_PUSH_VLAN,
- &vlan, sizeof vlan);
- }
- base->vlan_tci = flow->vlan_tci;
-}
-
-static void
-commit_set_nw_action(const struct flow *flow, struct flow *base,
- struct ofpbuf *odp_actions)
-{
- struct ovs_key_ipv4 ipv4_key;
-
- if (base->dl_type != htons(ETH_TYPE_IP) ||
- !base->nw_src || !base->nw_dst) {
- return;
- }
-
- if (base->nw_src == flow->nw_src &&
- base->nw_dst == flow->nw_dst &&
- base->nw_tos == flow->nw_tos &&
- base->nw_ttl == flow->nw_ttl &&
- base->nw_frag == flow->nw_frag) {
- return;
- }
-
- ipv4_key.ipv4_src = base->nw_src = flow->nw_src;
- ipv4_key.ipv4_dst = base->nw_dst = flow->nw_dst;
- ipv4_key.ipv4_tos = base->nw_tos = flow->nw_tos;
- ipv4_key.ipv4_ttl = base->nw_ttl = flow->nw_ttl;
- ipv4_key.ipv4_proto = base->nw_proto;
- ipv4_key.ipv4_frag = (base->nw_frag == 0 ? OVS_FRAG_TYPE_NONE
- : base->nw_frag == FLOW_NW_FRAG_ANY
- ? OVS_FRAG_TYPE_FIRST : OVS_FRAG_TYPE_LATER);
-
- commit_set_action(odp_actions, OVS_KEY_ATTR_IPV4,
- &ipv4_key, sizeof(ipv4_key));
-}
-
-static void
-commit_set_port_action(const struct flow *flow, struct flow *base,
- struct ofpbuf *odp_actions)
-{
- if (!base->tp_src || !base->tp_dst) {
- return;
- }
-
- if (base->tp_src == flow->tp_src &&
- base->tp_dst == flow->tp_dst) {
- return;
- }
-
- if (flow->nw_proto == IPPROTO_TCP) {
- struct ovs_key_tcp port_key;
-
- port_key.tcp_src = base->tp_src = flow->tp_src;
- port_key.tcp_dst = base->tp_dst = flow->tp_dst;
-
- commit_set_action(odp_actions, OVS_KEY_ATTR_TCP,
- &port_key, sizeof(port_key));
-
- } else if (flow->nw_proto == IPPROTO_UDP) {
- struct ovs_key_udp port_key;
-
- port_key.udp_src = base->tp_src = flow->tp_src;
- port_key.udp_dst = base->tp_dst = flow->tp_dst;
-
- commit_set_action(odp_actions, OVS_KEY_ATTR_UDP,
- &port_key, sizeof(port_key));
- }
-}
-
-static void
-commit_set_priority_action(const struct flow *flow, struct flow *base,
- struct ofpbuf *odp_actions)
-{
- if (base->priority == flow->priority) {
- return;
- }
- base->priority = flow->priority;
-
- commit_set_action(odp_actions, OVS_KEY_ATTR_PRIORITY,
- &base->priority, sizeof(base->priority));
-}
-
-static void
-commit_odp_actions(struct action_xlate_ctx *ctx)
-{
- const struct flow *flow = &ctx->flow;
- struct flow *base = &ctx->base_flow;
- struct ofpbuf *odp_actions = ctx->odp_actions;
-
- commit_set_tun_id_action(flow, base, odp_actions);
- commit_set_ether_addr_action(flow, base, odp_actions);
- commit_vlan_action(flow, base, odp_actions);
- commit_set_nw_action(flow, base, odp_actions);
- commit_set_port_action(flow, base, odp_actions);
- commit_set_priority_action(flow, base, odp_actions);
-}
-
static void
compose_output_action__(struct action_xlate_ctx *ctx, uint16_t ofp_port,
bool check_stp)
if (out_port != odp_port) {
ctx->flow.vlan_tci = htons(0);
}
- commit_odp_actions(ctx);
+ commit_odp_actions(&ctx->flow, &ctx->base_flow, ctx->odp_actions);
nl_msg_put_u32(ctx->odp_actions, OVS_ACTION_ATTR_OUTPUT, out_port);
ctx->sflow_odp_port = odp_port;
{
struct ofport_dpif *ofport;
- commit_odp_actions(ctx);
HMAP_FOR_EACH (ofport, up.hmap_node, &ctx->ofproto->up.ports) {
uint16_t ofp_port = ofport->up.ofp_port;
{
struct user_action_cookie cookie;
- commit_odp_actions(ctx);
+ commit_odp_actions(&ctx->flow, &ctx->base_flow, ctx->odp_actions);
cookie.type = USER_ACTION_COOKIE_CONTROLLER;
cookie.data = len;
cookie.n_output = 0;
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);
xlate_actions(struct action_xlate_ctx *ctx,
const union ofp_action *in, size_t n_in)
{
+ struct flow orig_flow = ctx->flow;
+
COVERAGE_INC(ofproto_dpif_xlate);
ctx->odp_actions = ofpbuf_new(512);
ctx->has_learn = false;
ctx->has_normal = false;
ctx->nf_output_iface = NF_OUT_DROP;
+ ctx->mirrors = 0;
ctx->recurse = 0;
ctx->original_priority = ctx->flow.priority;
ctx->table_id = 0;
compose_output_action(ctx, OFPP_LOCAL);
}
}
+ add_mirror_actions(ctx, &orig_flow);
fix_sflow_action(ctx);
}
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 ofport_dpif, bundle_node);
}
-static mirror_mask_t
-compose_dsts(struct action_xlate_ctx *ctx, uint16_t vlan,
- const struct ofbundle *in_bundle,
- const struct ofbundle *out_bundle)
-{
- mirror_mask_t dst_mirrors = 0;
-
- if (out_bundle == OFBUNDLE_FLOOD) {
- struct ofbundle *bundle;
-
- HMAP_FOR_EACH (bundle, hmap_node, &ctx->ofproto->bundles) {
- if (bundle != in_bundle
- && ofbundle_includes_vlan(bundle, vlan)
- && bundle->floodable
- && !bundle->mirror_out) {
- output_normal(ctx, bundle, vlan);
- dst_mirrors |= bundle->dst_mirrors;
- }
- }
- ctx->nf_output_iface = NF_OUT_FLOOD;
- } else if (out_bundle) {
- output_normal(ctx, out_bundle, vlan);
- dst_mirrors = out_bundle->dst_mirrors;
- }
-
- return dst_mirrors;
-}
-
static bool
vlan_is_mirrored(const struct ofmirror *m, int vlan)
{
}
static void
-output_mirrors(struct action_xlate_ctx *ctx,
- uint16_t vlan, const struct ofbundle *in_bundle,
- mirror_mask_t dst_mirrors)
+add_mirror_actions(struct action_xlate_ctx *ctx, const struct flow *orig_flow)
{
struct ofproto_dpif *ofproto = ctx->ofproto;
mirror_mask_t mirrors;
+ struct ofbundle *in_bundle;
+ uint16_t vlan;
+ uint16_t vid;
+ const struct nlattr *a;
+ size_t left;
+
+ in_bundle = lookup_input_bundle(ctx->ofproto, orig_flow->in_port,
+ ctx->packet != NULL);
+ if (!in_bundle) {
+ return;
+ }
+ mirrors = in_bundle->src_mirrors;
+
+ /* Drop frames on bundles reserved for mirroring. */
+ if (in_bundle->mirror_out) {
+ if (ctx->packet != NULL) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+ VLOG_WARN_RL(&rl, "bridge %s: dropping packet received on port "
+ "%s, which is reserved exclusively for mirroring",
+ ctx->ofproto->up.name, in_bundle->name);
+ }
+ return;
+ }
+
+ /* Check VLAN. */
+ vid = vlan_tci_to_vid(orig_flow->vlan_tci);
+ if (!input_vid_is_valid(vid, in_bundle, ctx->packet != NULL)) {
+ return;
+ }
+ vlan = input_vid_to_vlan(in_bundle, vid);
+
+ /* Look at the output ports to check for destination selections. */
+
+ NL_ATTR_FOR_EACH (a, left, ctx->odp_actions->data,
+ ctx->odp_actions->size) {
+ enum ovs_action_attr type = nl_attr_type(a);
+ struct ofport_dpif *ofport;
+
+ if (type != OVS_ACTION_ATTR_OUTPUT) {
+ continue;
+ }
+
+ ofport = get_odp_port(ofproto, nl_attr_get_u32(a));
+ if (ofport && ofport->bundle) {
+ mirrors |= ofport->bundle->dst_mirrors;
+ }
+ }
- mirrors = in_bundle->src_mirrors | dst_mirrors;
if (!mirrors) {
return;
}
+ /* Restore the original packet before adding the mirror actions. */
+ ctx->flow = *orig_flow;
+
while (mirrors) {
struct ofmirror *m;
}
mirrors &= ~m->dup_mirrors;
+ ctx->mirrors |= m->dup_mirrors;
if (m->out) {
output_normal(ctx, m->out, vlan);
- } else if (eth_dst_may_rspan(ctx->flow.dl_dst)
+ } else if (eth_dst_may_rspan(orig_flow->dl_dst)
&& vlan != m->out_vlan) {
struct ofbundle *bundle;
}
}
+static void
+update_mirror_stats(struct ofproto_dpif *ofproto, mirror_mask_t mirrors,
+ uint64_t packets, uint64_t bytes)
+{
+ if (!mirrors) {
+ return;
+ }
+
+ for (; mirrors; mirrors &= mirrors - 1) {
+ struct ofmirror *m;
+
+ m = ofproto->mirrors[mirror_mask_ffs(mirrors) - 1];
+
+ if (!m) {
+ /* In normal circumstances 'm' will not be NULL. However,
+ * if mirrors are reconfigured, we can temporarily get out
+ * of sync in facet_revalidate(). We could "correct" the
+ * mirror list before reaching here, but doing that would
+ * not properly account the traffic stats we've currently
+ * accumulated for previous mirror configuration. */
+ continue;
+ }
+
+ m->packet_count += packets;
+ m->byte_count += bytes;
+ }
+}
+
/* A VM broadcasts a gratuitous ARP to indicate that it has resumed after
* migration. Older Citrix-patched Linux DomU used gratuitous ARP replies to
* indicate this; newer upstream kernels use gratuitous ARP requests. */
{
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;
}
}
}
-static struct ofport_dpif *
+static struct ofbundle *
lookup_input_bundle(struct ofproto_dpif *ofproto, uint16_t in_port, bool warn)
{
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 ofport;
+ return ofport->bundle;
}
/* Odd. A few possible reasons here:
static void
xlate_normal(struct action_xlate_ctx *ctx)
{
- mirror_mask_t dst_mirrors = 0;
struct ofport_dpif *in_port;
struct ofbundle *in_bundle;
- struct ofbundle *out_bundle;
struct mac_entry *mac;
uint16_t vlan;
uint16_t vid;
ctx->has_normal = true;
- /* Obtain in_port from ctx->flow.in_port.
- *
- * lookup_input_bundle() also ensures that in_port belongs to a bundle. */
- in_port = lookup_input_bundle(ctx->ofproto, ctx->flow.in_port,
+ in_bundle = lookup_input_bundle(ctx->ofproto, ctx->flow.in_port,
ctx->packet != NULL);
- if (!in_port) {
+ if (!in_bundle) {
return;
}
- in_bundle = in_port->bundle;
+
+ /* 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. */
if (ctx->flow.dl_type == htons(ETH_TYPE_VLAN) &&
vlan = input_vid_to_vlan(in_bundle, vid);
/* Check other admissibility requirements. */
- if (!is_admissible(ctx->ofproto, &ctx->flow, in_port, vlan, &ctx->tags)) {
- output_mirrors(ctx, vlan, in_bundle, 0);
+ if (in_port &&
+ !is_admissible(ctx->ofproto, &ctx->flow, in_port, vlan, &ctx->tags)) {
return;
}
mac = mac_learning_lookup(ctx->ofproto->ml, ctx->flow.dl_dst, vlan,
&ctx->tags);
if (mac) {
- out_bundle = mac->port.p;
+ if (mac->port.p != in_bundle) {
+ output_normal(ctx, mac->port.p, vlan);
+ }
} else if (!ctx->packet && !eth_addr_is_multicast(ctx->flow.dl_dst)) {
/* If we are revalidating but don't have a learning entry then eject
* the flow. Installing a flow that floods packets opens up a window
ctx->may_set_up_flow = false;
return;
} else {
- out_bundle = OFBUNDLE_FLOOD;
- }
+ struct ofbundle *bundle;
- /* Don't send packets out their input bundles. */
- if (in_bundle != out_bundle) {
- dst_mirrors = compose_dsts(ctx, vlan, in_bundle, out_bundle);
+ HMAP_FOR_EACH (bundle, hmap_node, &ctx->ofproto->bundles) {
+ if (bundle != in_bundle
+ && ofbundle_includes_vlan(bundle, vlan)
+ && bundle->floodable
+ && !bundle->mirror_out) {
+ output_normal(ctx, bundle, vlan);
+ }
+ }
+ ctx->nf_output_iface = NF_OUT_FLOOD;
}
- output_mirrors(ctx, vlan, in_bundle, dst_mirrors);
}
\f
/* Optimized flow revalidation.
static struct ofproto_dpif *
ofproto_dpif_lookup(const char *name)
{
- struct ofproto *ofproto = ofproto_lookup(name);
- return (ofproto && ofproto->ofproto_class == &ofproto_dpif_class
- ? ofproto_dpif_cast(ofproto)
- : NULL);
+ struct ofproto_dpif *ofproto;
+
+ HMAP_FOR_EACH_WITH_HASH (ofproto, all_ofproto_dpifs_node,
+ hash_string(name, 0), &all_ofproto_dpifs) {
+ if (!strcmp(ofproto->up.name, name)) {
+ return ofproto;
+ }
+ }
+ return NULL;
}
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);
/* Convert odp_key to flow. */
error = ofproto_dpif_extract_flow_key(ofproto, odp_key.data,
odp_key.size, &flow,
- &initial_tci);
+ &initial_tci, NULL);
if (error == ODP_FIT_ERROR) {
unixctl_command_reply(conn, 501, "Invalid flow");
goto exit;
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);
destruct,
dealloc,
run,
+ run_fast,
wait,
flush,
get_features,
bundle_set,
bundle_remove,
mirror_set,
+ mirror_get_stats,
set_flood_vlans,
is_mirror_output_bundle,
forward_bpdu_changed,
+ set_mac_idle_time,
set_realdev,
};